From 5b4a10af81c60c6c4787d95f82004094f358459c Mon Sep 17 00:00:00 2001 From: gramanas Date: Wed, 27 Mar 2019 19:56:24 +0200 Subject: Initial.. --- .gitignore | 3 + Makefile | 36 +++++ qc | 178 ++++++++++++++++++++++++ qc.1 | 1 + quart.1 | 114 ++++++++++++++++ quart.1.scd | 79 +++++++++++ quart.5 | 129 ++++++++++++++++++ quart.5.scd | 90 +++++++++++++ quart.c | 419 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ quart.i3blocks | 15 +++ sample.org | 8 ++ 11 files changed, 1072 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100755 qc create mode 120000 qc.1 create mode 100644 quart.1 create mode 100644 quart.1.scd create mode 100644 quart.5 create mode 100644 quart.5.scd create mode 100644 quart.c create mode 100755 quart.i3blocks create mode 100644 sample.org diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e34fb33 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +quart +*.test +*.q \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f05117c --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +TARGET=quart +SRC=*.c +CC=gcc +CFLAGS=-D_GNU_SOURCE -std=c99 -pedantic -lm +REL_FLAGS=-O3 +DBG_FLAGS=-Wall -g3 +COMPILE=$(CC) -o $(TARGET) $(SRC) $(CFLAGS) -Ilib + +make: $(SRC) + $(CC) $(REL_FLAGS) $(CFLAGS) -o $(TARGET) $^ + +debug: $(SRC) + $(CC) $(DBG_FLAGS) -fsanitize=address $(CFLAGS) -o $(TARGET) $^ + +noasan: $(SRC) + $(CC) $(DBG_FLAGS) $(CFLAGS) -o $(TARGET) $^ + +docs: + scdoc < quart.1.scd > quart.1 + scdoc < quart.5.scd > quart.5 + ln -fs quart.1 qc.1 + +.PHONY: clean + +clean: + rm -f *.o + rm -f *.1 + rm -f *.5 + rm -f $(TARGET) + +install: $(TARGET) + @cp -v $(TARGET) /usr/local/bin/$(TARGET) + @cp -v qc /usr/local/bin/qc + +uninstall: + @rm -v /usr/local/bin/$(TARGET) diff --git a/qc b/qc new file mode 100755 index 0000000..0e32109 --- /dev/null +++ b/qc @@ -0,0 +1,178 @@ +#!/bin/bash +# +## Wrapper script for quart +# +## Takes care of ~/.quart.d +# +## .quart.d +## |-- archive +## | |-- 190706_Saturday_220700.org +## | |-- 190707_Sunday_220701.org +## |-- today.org + +# +## +## Initialize values +## +# + +ED="${EDITOR:-emacs -nw}" +QHOME="${QUART_HOME:-$HOME/.quart.d}" +ARCHIVE="$QHOME"/archive +TODAY_FILE="$QHOME"/today.org +DATE_CMD="date +%y%m%d_%A_%H%m%S" + +if [[ -z $(which quart) ]]; then + echo "quart is not installed" + exit 1 +fi + +QUART_CMD=$(which quart) + +function archive { + if [[ ! -d "$ARCHIVE" ]]; then + echo "archive directory does not exist" + exit 1 + fi + + if [[ -f "$TODAY_FILE" ]]; then + mv "$TODAY_FILE" "$ARCHIVE/$($DATE_CMD)" + return 1 + fi + + return 0 +} + +function make_today { + if [[ ! -d "$QHOME" ]]; then + echo "quart home directory does not exist" + exit 1 + fi + + cat > "$TODAY_FILE" << EOF +#+START: 9:00 + +# quart schedule created at $(date "+%A %d of %B the year of our lord %Y") + +# Avaliable task flags: +# ! Urgent, ? Maybe, @ External +# Use `=` to signal a task repeat + +EOF +} + +function edit_today { + if [[ ! -f "$TODAY_FILE" ]]; then + echo "run new first" + exit 1 + fi + + $ED "$TODAY_FILE" +} + +function summary_today { + if [[ ! -f "$TODAY_FILE" ]]; then + return 0 + fi + + "$QUART_CMD" "$TODAY_FILE" -s +} + +function make_qhome { + if [[ -d "$QHOME" ]]; then + echo "QUART_HOME already exists in $QHOME" + else + mkdir "$QHOME" + echo "QUART_HOME initialized in $QHOME" + fi + + if [[ -d "$ARCHIVE" ]]; then + echo "archive directory already exists" + else + mkdir "$ARCHIVE" + echo "archive directory created" + fi + +} + +if [ $# -lt 1 ]; then + if [[ ! -d "$QHOME" ]]; then + echo "run init first" + exit 1 + fi + + if [[ ! -f "$TODAY_FILE" ]]; then + echo "run new first" + exit 1 + fi + + echo -e "Current schedule:\n" + "$QUART_CMD" "$TODAY_FILE" -s + exit 0 +fi + +if [[ "$1" == "-" ]]; then + shift + "$QUART_CMD" "$TODAY_FILE" $@ + exit $? +fi + +case $1 in + init|i) + make_qhome + exit 0 + # create ~/.quart.d director structure + ;; + new|n) + shift + archive + make_today + edit_today + summary_today + exit 0 + # backup the old schedule and create a new one + ;; + edit|e) + shift + edit_today + summary_today + exit 0 + # edit the current schedule + ;; + remove|rm) + shift + archive + exit 0 + # rm current schedule, after backing it up + ;; + cat|c) + shift + cat "$TODAY_FILE" + exit 0 + ;; + help|h|-h|--help) + shift + cat << EOF +qc -- bash wrapper for quart + +Usage: +$0 [OPTION] +$0 - [quart option] + +OPTIONS: + init | i initialize quart home + new | n create new schedule (archives current one) + remove | rm remove current schedule (and archive it) + edit | e edit current schedule + cat | c print current schedule + help | h show this help + - pass arguments to quart +EOF + exit 0 + ;; + *) + echo "qc: '$1' is not a valid option" + shift + exit 1 + ;; +esac diff --git a/qc.1 b/qc.1 new file mode 120000 index 0000000..4ef8e08 --- /dev/null +++ b/qc.1 @@ -0,0 +1 @@ +quart.1 \ No newline at end of file diff --git a/quart.1 b/quart.1 new file mode 100644 index 0000000..37b3c34 --- /dev/null +++ b/quart.1 @@ -0,0 +1,114 @@ +.\" Generated by scdoc 1.11.1 +.\" Complete documentation for this program is not available as a GNU info page +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.nh +.ad l +.\" Begin generated content: +.TH "quart" "1" "2020-12-25" +.P +.SH NAME +.P +quart - plan your day, a quarter at a time +.P +.SH SYNOPSIS +.P +\fBquart\fR [\fIfile\fR] [\fB-schrn\fR] [\fB-l\fR \fINUM\fR] +.br +\fBqc\fR [\fICOMMAND\fR] +.br +\fBqc\fR [\fB-\fR \fIARGS\fR] +.P +.SH OPTIONS +.P +\fBquart\fR has the following options: +.P +\fIfile\fR +.RS 4 +File containing the schedule to parse.\& +.P +.RE +\fB-c\fR +.RS 4 +Print \fIcurrent\fR task.\& +.P +.RE +\fB-n\fR +.RS 4 +Print \fInext\fR task.\& +.P +.RE +\fB-l\fR \fIquarters\fR +.RS 4 +Print the task that you should do \fIquarters\fR quarters \fIlater\fR.\& +.P +.RE +\fBqc\fR has the following options: +.P +Without any arguments it pretty prints the current schedule.\& +.P +\fB-\fR +.RS 4 +Pass any argument that follows the `-` symbol to \fBquart\fR, along with +the current schedule.\& +.P +.RE +\fBinit, i\fR +.RS 4 +Create the required directory structure under \fI$QUART_HOME\fR.\& +.P +.RE +\fBnew, n\fR +.RS 4 +Create a new schedule and archive the current one.\& +.P +.RE +\fBremove, rm\fR +.RS 4 +Remove current schedule while also archiving it.\& +.P +.RE +\fBedit, e\fR +.RS 4 +Run \fI$EDITOR\fR on the current schedule.\& +.P +.RE +\fBcat, c\fR +.RS 4 +Print current schedule file in \fIstdout\fR.\& +.P +.RE +\fBhelp, h, -h\fR +.RS 4 +Show help.\& +.P +.RE +.SH DESCRIPTION +.P +\fBquart\fR parses a file in \fBquart\fR(5) format and prints the schedule according to +the given options.\& \fBqc\fR is a bash wrapper around \fBquart\fR that automates the +management of daily schedule files.\& It cat be customized by editing it.\& It is +the recommended way of managing you quart schedules.\& Both commands provide +usage information using the \fB-h\fR flag.\& +.P +.SH FILES/ENVIRONMENT +.P +\fIQUART_HOME\fR +.RS 4 +\fBqc\fR uses this location for storing current and archived schedules.\& +\fI~/.\&quart.\&d\fR by default.\& +.P +.RE +\fIQUART_HOME/archive\fR +.RS 4 +All the archived schedules end up here and are renamed.\& +.P +.RE +\fIEDITOR\fR +.RS 4 +Used when invoking the edit command.\& +.P +.RE +.SH AUTHOR +.P +Anastasis Grammenos \ No newline at end of file diff --git a/quart.1.scd b/quart.1.scd new file mode 100644 index 0000000..2a9e08e --- /dev/null +++ b/quart.1.scd @@ -0,0 +1,79 @@ +quart(1) + +; Tabs are required for indentation ( in emacs) + +# NAME + +quart - plan your day, a quarter at a time + +# SYNOPSIS + +*quart* [_file_] [*-schrn*] [*-l* _NUM_] ++ +*qc* [_COMMAND_] ++ +*qc* [*-* _ARGS_] + +# OPTIONS + +*quart* has the following options: + +_file_ + File containing the schedule to parse. + +*-c* + Print _current_ task. + +*-n* + Print _next_ task. + +*-l* _quarters_ + Print the task that you should do _quarters_ quarters _later_. + +*qc* has the following options: + +Without any arguments it pretty prints the current schedule. + +*-* + Pass any argument that follows the `-` symbol to *quart*, along with + the current schedule. + +*init, i* + Create the required directory structure under _$QUART_HOME_. + +*new, n* + Create a new schedule and archive the current one. + +*remove, rm* + Remove current schedule while also archiving it. + +*edit, e* + Run _$EDITOR_ on the current schedule. + +*cat, c* + Print current schedule file in _stdout_. + +*help, h, -h* + Show help. + +# DESCRIPTION + +*quart* parses a file in *quart*(5) format and prints the schedule according to +the given options. *qc* is a bash wrapper around *quart* that automates the +management of daily schedule files. It cat be customized by editing it. It is +the recommended way of managing you quart schedules. Both commands provide +usage information using the *-h* flag. + +# FILES/ENVIRONMENT + +_QUART_HOME_ + *qc* uses this location for storing current and archived schedules. + _~/.quart.d_ by default. + +_QUART_HOME/archive_ + All the archived schedules end up here and are renamed. + +_EDITOR_ + Used when invoking the edit command. + +# AUTHOR + +Anastasis Grammenos \ No newline at end of file diff --git a/quart.5 b/quart.5 new file mode 100644 index 0000000..b8bb2e2 --- /dev/null +++ b/quart.5 @@ -0,0 +1,129 @@ +.\" Generated by scdoc 1.11.1 +.\" Complete documentation for this program is not available as a GNU info page +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.nh +.ad l +.\" Begin generated content: +.TH "quart" "5" "2020-12-25" +.P +.P +.SH NAME +.P +quart - document format for \fBquart\fR(1) schedules +.P +.SH SYNTAX +.P +\fBquart\fR files are inspired by emacs' org-mode.\& The header contains the time +that the schedule begins and each line represents a quarter of time, or 15 +minures.\& +.P +.SS HEADER +.P +Before any task, every \fBquart\fR file must contain a header with the starting +time of the schedule.\& It looks like this: +.P +.nf +.RS 4 +#+START: 9:15 +.fi +.RE +.P +It is case insenstive.\& +.P +.SS COMMENTS +.P +Every line beginning with `#` is ignored.\& Comments must start at the beginning +of the line.\& +.P +.SS BODY +.P +The body of the document contains the tasks.\& Each task is a line starting with `* ` +(asterisk and space).\& The full definition of a task looks like this: +.P +.RS 4 +* [\fIREPETITION\fR] [\fIFLAG\fR] <\fBDESCRIPTION\fR> +.P +.RE +\fIFLAG\fR and \fIREPETION\fR are optional.\& See the respective sections below.\& +.P +The description can be any arbitrary string.\& +.P +.SS FLAGS +.P +Each task can have a \fIflag\fR set to make it pop out.\& There are three flags avaliable: +.P +.TS +c r l +c r l +c r l +c r l. +T{ +\fBChar\fR +T} T{ +\fBFlag\fR +T} T{ +\fBDescription\fR +T} +T{ +! +T} T{ +\fIUrgent\fR +T} T{ +Mark task as important.\& +T} +T{ +?\& +T} T{ +\fIMaybe\fR +T} T{ +Mark task as mayhap.\& +T} +T{ +@ +T} T{ +\fIDepends\fR +T} T{ +Task that depends on someone else.\& +T} +.TE +.sp 1 +Use them by adding the required \fIchar\fR after the `* ` in a task: +.P +.nf +.RS 4 +#+start: 9:30 +* ! Meet with Knuth\&. +.fi +.RE +.P +If used with \fIrepetition\fR it must go \fBafter\fR it.\& +.P +.SS REPETITION +.P +Allows repetition of a task for as many quarters as needed in succession.\& +.P +Use them by adding a number right after the `* ` in a task: +.P +.nf +.RS 4 +#+start: 11:30 +* 8 Boring 2h meeting\&. +.fi +.RE +.P +If used with \fIflags\fR it must go \fBbefore\fR any.\& +.P +.SS SPECIAL +.P +There are two special symbols the \fIDESCRIPTION\fR can contain: +.P +\fB=\fR +.RS 4 +Repeat the previous task.\& It will only carry over the description +so it can contain a different \fIflag\fR or \fIrepetition\fR number.\& +.P +.RE +\fB-\fR +.RS 4 +None, the most important thing.\& 15 minutes of doing nothing.\& \ No newline at end of file diff --git a/quart.5.scd b/quart.5.scd new file mode 100644 index 0000000..438dfad --- /dev/null +++ b/quart.5.scd @@ -0,0 +1,90 @@ +quart(5) + +; Tabs are required for indentation ( in emacs) + +# NAME + +quart - document format for *quart*(1) schedules + +# SYNTAX + +*quart* files are inspired by emacs' org-mode. The header contains the time +that the schedule begins and each line represents a quarter of time, or 15 +minures. + +## HEADER + +Before any task, every *quart* file must contain a header with the starting +time of the schedule. It looks like this: + +``` +#+START: 9:15 +``` + +It is case insenstive. + +## COMMENTS + +Every line beginning with `#` is ignored. Comments must start at the beginning +of the line. + +## BODY + +The body of the document contains the tasks. Each task is a line starting with `\* ` +(asterisk and space). The full definition of a task looks like this: + + \* [_REPETITION_] [_FLAG_] <*DESCRIPTION*> + +_FLAG_ and _REPETION_ are optional. See the respective sections below. + +The description can be any arbitrary string. + +## FLAGS + +Each task can have a _flag_ set to make it pop out. There are three flags avaliable: + +|- *Char* +:] *Flag* +:[ *Description* +|- \! +: _Urgent_ +:[ Mark task as important. +|- ? +: _Maybe_ +:[ Mark task as mayhap. +|- @ +: _Depends_ +:[ Task that depends on someone else. + +Use them by adding the required _char_ after the `\* ` in a task: + +``` +#+start: 9:30 +\* ! Meet with Knuth. +``` + +If used with _repetition_ it must go *after* it. + +## REPETITION + +Allows repetition of a task for as many quarters as needed in succession. + +Use them by adding a number right after the `\* ` in a task: + +``` +#+start: 11:30 +* 8 Boring 2h meeting. +``` + +If used with _flags_ it must go *before* any. + +## SPECIAL + +There are two special symbols the _DESCRIPTION_ can contain: + +*=* + Repeat the previous task. It will only carry over the description + so it can contain a different _flag_ or _repetition_ number. + +*-* + None, the most important thing. 15 minutes of doing nothing. \ No newline at end of file diff --git a/quart.c b/quart.c new file mode 100644 index 0000000..4f2ad3b --- /dev/null +++ b/quart.c @@ -0,0 +1,419 @@ +/** + * quart -- manage the day one quarter at a time + * Copyright (C) 2019 Anastasis Grammenos + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_LEN 8192 + +static char buf[BUFFER_LEN] = ""; +static char prev[BUFFER_LEN] = ""; +static uint line = 0; + +#define FERR(format, ...) do { \ + fprintf(stderr, "%s:%d error: ", ctx.file, line); \ + fprintf(stderr, format, __VA_ARGS__); \ + fprintf(stderr, "\n"); } while(0); + +typedef enum mods { + NONE = 0, + URGENT, + MAYBE, + DEPENDS, +} mods; + +typedef struct Q { + time_t start; + char **entry; + mods *m; + unsigned int n; +} Q; + +struct context { + char *file; + int later; + int current; + int next; + int remaining; +}; + +static struct context ctx = { + NULL, + 0, + 0, + 0, + 1 +}; + +void trim(char *str) +{ + int i; + int begin = 0; + int end = strlen(str) - 1; + + while (isspace((unsigned char) str[begin])) + begin++; + + while ((end >= begin) && isspace((unsigned char) str[end])) + end--; + + // Shift all characters back to the start of the string array. + for (i = begin; i <= end; i++) + str[i - begin] = str[i]; + + str[i - begin] = '\0'; // Null terminate string. +} + +static void print_help(char *argv0) +{ + fprintf(stderr, + "quart - manage the day one quarter at a time\n"); + fprintf(stderr, "Copyright (C) 2019 Anastasis Grammenos\n"); + fprintf(stderr, + "This program is licenced under GPLv2. See source tree for details.\n\n"); + fprintf(stderr, "Usage:\n~~~~~~\n"); + fprintf(stderr, "%s [file] [...]\n\n", argv0); + fprintf(stderr, "%8s %4s %10s %55s\n", "Options", "", "Mnemonic", + "Description"); + fprintf(stderr, "%8s %4s %10s %55s\n", "~~~~~~~", "", "~~~~~~~~", + "~~~~~~~~~~~"); + fprintf(stderr, "%8s %4s %10s %55s\n", "-h", "", "help", + "Print this help message"); + fprintf(stderr, "%8s %4s %10s %55s\n\n", "file", "[..]", "", + "The file to parse the daily schedule from"); + fprintf(stderr, "%8s %4s %10s %55s\n", "-c", "", "current", + "Print what you should be doing now"); + fprintf(stderr, "%8s %4s %10s %55s\n", "-n", "", "next", + "Print what you should be doing next"); + fprintf(stderr, "%8s %4s %10s %55s\n", "-l", "[..]", "later", + "Print what you should be doing `l` quarters later"); + fprintf(stderr, "%8s %4s %10s %55s\n", "", "", "", + "(Accepts numbers. -l 1 is the same as -n)"); + fprintf(stderr, "%8s %4s %10s %55s\n", "-s", "", "summary", + "Print the day's summary"); + fprintf(stderr, "%8s %4s %10s %55s\n", "-r", "", "remaining", + "Print what remains to be done (The default)"); + +} + +static int parse_cli(const int argc, char * const argv[]) +{ + if (argc < 2) + return -1; + char c; + while ((c = getopt(argc, argv, "-schrnl:")) != -1) { + switch (c) { + case 'h': + print_help(argv[0]); + return -1; + case '\1': + ctx.file = optarg; + break; + case 's': + ctx.remaining = 0; + ctx.current = 0; + ctx.next = 0; + ctx.later = 0; + break; + case 'c': + ctx.remaining = 0; + ctx.current = 1; + ctx.later = 0; + ctx.next = 0; + break; + case 'n': + ctx.remaining = 0; + ctx.next = 1; + ctx.current = 0; + ctx.later = 0; + break; + case 'l': + ctx.remaining = 0; + ctx.next = 0; + ctx.current = 0; + ctx.later = atoi(optarg); + break; + case 'r': + ctx.remaining = 1; + ctx.next = 0; + ctx.current = 0; + ctx.later = 0; + break; + default: + return -1; + } + } + return 0; +} + +void initQ(Q *q) +{ + q->n = 0; + q->start = 0; + q->entry = (char **) malloc(sizeof(char *)); + q->m = (mods *) malloc(sizeof(mods)); +} + +void freeQ(Q *q) +{ + for (uint i = 0; i < q->n; i++) + free(q->entry[i]); + free(q->entry); + free(q->m); +} + +/** + * str must be in the form of 00:00 */ +int str_to_time(time_t *t, char *str) +{ + char *c = strtok(str, ":"); + if (!c) return -1; + uint hour = atoi(c); + if (hour > 23) return -1; + c = strtok(NULL, ":"); + if (!c) return -1; + uint minute = atoi(c); + if (minute > 59) return -1; + + time_t tmp; + time(&tmp); + struct tm *tt = localtime(&tmp); + + tt->tm_hour = hour; + tt->tm_min = minute; + tt->tm_sec = 0; + + *t = mktime(tt); + if (*t == -1) return -1; + return 0; +} + +int parse_header(Q *q) +{ + char *c = strtok(buf, " "); + if (!c) return -1; + if ((strcmp(c, "#+start:") != 0) && + (strcmp(c, "#+START:") != 0)) + return -1; + c = strtok(NULL, " "); + if (!c) return -1; + if (str_to_time(&q->start, c)) { + FERR("can't convert string \"%s\" to timestamp", c); + return -1; + } + return 0; +} + +int add(Q *q) +{ + const char mod = buf[0]; + char *ret = strchr(buf, mod); + if (ret) { + switch(mod) { + case '!': + q->m[q->n] = URGENT; + break; + case '?': + q->m[q->n] = MAYBE; + break; + case '@': + q->m[q->n] = DEPENDS; + break; + default: + q->m[q->n] = NONE; + } + if (q->m[q->n] != NONE) { + ret++; + trim(ret); + } + q->entry[q->n] = strdup(ret); + q->n++; + return 0; + } + return -1; +} + +int add_entry(Q *q) +{ + char *str = NULL; + q->entry = (char **)realloc(q->entry, sizeof(char *) * (q->n + 1)); + q->m = (mods *)realloc(q->m, sizeof(mods) * (q->n + 1)); + if (buf[0] == '=') { + if (strcmp(prev,"") == 0) + FERR("\"%s\" there is no previous entry", buf); + strcpy(buf, prev); + return add(q); + } + else if (buf[0] == '-') { + q->entry[q->n] = strdup(""); + q->m[q->n] = NONE; + q->n++; + return 0; + } + else { + if (add(q)) + return -1; + } + + if (str) + strcpy(prev, str); + else + strcpy(prev, buf); + return 0; +} + +int parseQ(FILE *f, Q *q) +{ + initQ(q); + int head = 0; + int entries = 0; + + while (fgets(buf, BUFFER_LEN, f) != NULL) { + line++; + trim(buf); + if (buf[0] == '#' && buf[1] == '+') { + if (entries) { + FERR("\"%s\" can't put header information inside entry list", buf); + return -1; + } + if (parse_header(q)) { + FERR("\"%s\" wrong header", buf); + return -1; + } + head = 1; + } + else if (buf[0] == '#') { + continue; + } + else if (buf[0] == '*') { + entries = 1; + buf[0] = ' '; + trim(buf); + if (!head) { + FERR("Can't add \"%s\", missing #start header", buf) + return -1; + } + if (strchr("123456789", buf[0]) != NULL) { + int i = atoi(buf); + int digits = (int)(floor(log(i)/log(10)) + 1); + for (int j = 0; j < digits; j++) { + buf[j] = ' '; + } + trim(buf); + for (int j = 1; j < i; j++) { + add_entry(q); + } + } + if(add_entry(q)) { + FERR("\"%s\" could not add entry", buf) + return -1; + } + } + else if (buf[0] == ';' || buf[0] == '\0') { + continue; + } + else { + FERR("\"%s\" is not a valid entry", buf) + } + } + return head ? 0 : -1; +} + +const char * get_mod(const mods m) { + switch(m) { + case NONE: + return "---"; + case URGENT: + return "!!!"; + case MAYBE: + return "???"; + case DEPENDS: + return "@@@"; + default: + return ""; + } +} + +#define P(before, timestr, entry, mod) \ + printf("%s%5s %3s %s\n", before, timestr, mod, entry); + +void printQ(const Q *q) +{ + time_t now; + time(&now); + + char timestr[100]; + time_t t = q->start; + for (uint i = 0; i < q->n; i++) { + strftime(timestr, sizeof(timestr), "%R", localtime(&t)); + double d = difftime(t, now); + if (ctx.current) { + if (d > -900 && d <= 0) + P("Now: ", timestr, q->entry[i], get_mod(q->m[i])); + } + else if (ctx.next) { + if (d > 0 && d <= 900) + P("Next: ", timestr, q->entry[i], get_mod(q->m[i])); + } + else if (ctx.later) { + if (d > 900 * (ctx.later - 1) && d <= 900 * (ctx.later)) + P("Later: ", timestr, q->entry[i], get_mod(q->m[i])); + } + else if (ctx.remaining) { + if (d > -900) + P("", timestr, q->entry[i], get_mod(q->m[i])); + } + else { + P("", timestr, q->entry[i], get_mod(q->m[i])); + } + t+=(15*60); + } +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + print_help(argv[0]); + return 0; + } + + if (parse_cli(argc, argv)) + return -1; + + FILE *f = fopen(ctx.file, "r"); + if (!f) { + printf("file %s does not exist\n", ctx.file); + return -1; + } + Q q; + + if (parseQ(f, &q)) { + goto end; + } + + printQ(&q); +end: + fclose(f); + freeQ(&q); +} diff --git a/quart.i3blocks b/quart.i3blocks new file mode 100755 index 0000000..bf0e1a1 --- /dev/null +++ b/quart.i3blocks @@ -0,0 +1,15 @@ +#!/bin/sh + +case $BLOCK_BUTTON in + 1) notify-send "Current:" "$(qc - -c)" + ;; + 2) notify-send "Next:" "$(qc - -n)" + ;; + 3) notify-send "Remaining:" "$(qc - -r)" + ;; +esac + +REM=$(qc - -r | wc -l) + +echo "Q:\ +$REM ($(qc - -c | awk -F '[[:space:]][[:space:]]' '{print $3}'))" diff --git a/sample.org b/sample.org new file mode 100644 index 0000000..185fcd4 --- /dev/null +++ b/sample.org @@ -0,0 +1,8 @@ +#+START: 14:15 + +* ! Coffee +* = +* sysadmin emacs+tmux config +* = +* ssh-agent +* = -- cgit v1.2.3