diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 36 | ||||
-rwxr-xr-x | qc | 178 | ||||
l--------- | qc.1 | 1 | ||||
-rw-r--r-- | quart.1 | 114 | ||||
-rw-r--r-- | quart.1.scd | 79 | ||||
-rw-r--r-- | quart.5 | 129 | ||||
-rw-r--r-- | quart.5.scd | 90 | ||||
-rw-r--r-- | quart.c | 419 | ||||
-rwxr-xr-x | quart.i3blocks | 15 | ||||
-rw-r--r-- | sample.org | 8 |
11 files changed, 1072 insertions, 0 deletions
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) @@ -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 @@ -0,0 +1 @@ +quart.1
\ No newline at end of file @@ -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 (<C-q tab> 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 @@ -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 (<C-q tab> 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 @@ -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 <stdio.h> +#include <string.h> +#include <time.h> +#include <stdlib.h> +#include <ctype.h> +#include <math.h> +#include <getopt.h> + +#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 "<span color='#755a87'>Q:</span>\ +<span color='#6b8e23'>$REM ($(qc - -c | awk -F '[[:space:]][[:space:]]' '{print $3}'))</span>" 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 +* = |