/* $Id: todo.c 74 2008-09-21 03:20:47Z dleigh $ vim: tabstop=3 shiftwidth=3 expandtab: This program has been superceded by my python todo program and is no longer being actively developed. Console todo reminder program. Create a .todo with your tasks, and running todo wil tell you how long before you have to do them. Based on todo.c by Emil Mikulic - http://dmr.ath.cx/stuff/code/todo.c Dylan's bloats^H^H^H^H^H^H enhancements: + Handles years + Handles expired items (instead of assuming next year...) + Optional categories (use -c to group by category) + Reversed order of sorting (can use -r to rereverse) + Support for no deadline items (use -d to not show them) + ANSI colour output (use -m for monochrome output) + Supports file argument (-f filename), default still ~/.todo /home/.todo file: [Category] yyyy mm dd Description yyyy mm dd Description can have lots of words 0000 00 00 Description of undated item e.g. [Intro to AI] 2005 05 01 Assignment 1 Due 2005 06 03 Assignment 2 Due [Mathematics] 2005 04 06 Calculus Test 0000 00 00 Get new tables book [Info Syst. ] 2005 06 03 Project Due Note: todo by default only displays items in the next 30 days. If you have longer deadlines than this you should probably be breaking things up into smaller tasks anyway. Use an integer argument to display by that many days. ----- Copyright (c) 2004-2005 Dylan Leigh. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Modified versions must be plainly marked as such, and must not be misrepresented as being the original software. 4. The names of the authors or copyright holders must not be used to endorse or promote products derived from this software without prior written permission. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----- */ #include #include #include #include #include /* these are all with black background (40) */ #define ANSI_RED "\033[0;31m" #define ANSI_GREEN "\033[0;32m" #define ANSI_YELLOW "\033[0;33m" #define ANSI_BLUE "\033[0;34m" #define ANSI_MAGENTA "\033[0;35m" #define ANSI_CYAN "\033[0;36m" #define ANSI_WHITE "\033[0;37m" #define ANSI_NORMAL "\033[0m" #define DEFAULT_DAYS 30 #define POOLSIZE 100 #define DEFAULTTODOFILE /* $HOME/ */ ".todo" static char default_editor[] = "vim"; #define LINEBUF 256 static char tempbuf[LINEBUF * 2]; static char line[LINEBUF]; static char category[LINEBUF] = ""; //default blank category static int linenum = 0; /* commandline params for behaviour */ static char reverse = 0; static char group_category = 0; static char monochrome = 0; static char deadlines_only = 0; static char edit_file = 0; static char uses_custom_file = 0; typedef struct { time_t when; // time_t starts; TODO: for sleeping char *what; } todo_entry; static todo_entry *todo_list = NULL; static int todo_pool = 0, todo_used = 0; static time_t now, today; static char *todo_fn = NULL; static int todocmp(const void *t1, const void *t2); static void showdays(const int limit_days); static void *xmalloc(const size_t s) { void *tmp = malloc(s); if (!tmp) { fprintf(stderr, "Out of memory!\n"); exit(EXIT_FAILURE); } return tmp; } static void growpool(void) { void *tmp; todo_pool += POOLSIZE; tmp = realloc(todo_list, todo_pool * sizeof(todo_entry)); if (!tmp) { fprintf(stderr, "Out of memory in growpool()\n"); exit(EXIT_FAILURE); } todo_list = (todo_entry*)tmp; } static void getline(FILE *f) { int i; char c; linenum++; for (i=0; iwhen = mktime(&t); } else e->when = 0; e->what = strdup(what); } static int parseline(int days) { int left, pos, year = 0, month = 0, day = 0; if (!strlen(line)) return 1; if (line[0] == '#') return 1; if (line[0] == '[') { if (todo_list == NULL || !group_category) { strcpy(category, line); return 1; } printf("\n \t \t %s \n", category); qsort(todo_list, todo_used, sizeof(todo_entry), todocmp); showdays(days); free(todo_list); todo_list = NULL; todo_pool = todo_used = 0; strcpy(category, line); return 1; } for (pos = 0; line[pos] == ' ' && line[pos]; pos++); /* whitespace */ if (!line[pos]) return 0; if (line[pos] == '#') return 1; for (left = pos; line[pos] != ' ' && line[pos]; pos++); /* numbers */ if (!line[pos]) return 0; line[pos] = 0; year = atoi(line+left); for (pos++; line[pos] == ' ' && line[pos]; pos++); /* whitespace */ if (!line[pos]) return 0; for (left = pos; line[pos] != ' ' && line[pos]; pos++); /* numbers */ if (!line[pos]) return 0; line[pos] = 0; month = atoi(line+left); for (pos++; line[pos] == ' ' && line[pos]; pos++); /* whitespace */ if (!line[pos]) return 0; for (left = pos; line[pos] != ' ' && line[pos]; pos++); /* numbers */ if (!line[pos]) return 0; line[pos] = 0; day = atoi(line+left); for (pos++; line[pos] == ' ' && line[pos]; pos++); /* whitespace */ if (month == 0 && day == 0 && year == 0) ; else if ( month < 1 || month > 12 || day < 1 || day > 31 || !strlen(line+pos) ) return 0; if (!group_category) { strcpy(tempbuf, category); strcat(tempbuf, " "); strcat(tempbuf, line+pos); addentry(year, month, day, tempbuf); } else addentry(year, month, day, line+pos); return 1; } static int todocmp(const void *t1, const void *t2) { todo_entry *e1 = (todo_entry *)t1; todo_entry *e2 = (todo_entry *)t2; if (reverse) { if (e1->when < e2->when) return -1; else if (e1->when > e2->when) return 1; else return 0; } else { if (e1->when > e2->when) return -1; else if (e1->when < e2->when) return 1; else return 0; } } static void loadtodo(int days) { FILE *f = fopen(todo_fn, "r"); if (!f) { fprintf(stderr, "Can't open %s\n", todo_fn); exit(EXIT_FAILURE); } while (!feof(f)) { getline(f); if (!parseline(days)) fprintf(stderr, "Malformed line in %s:%d.\n", todo_fn, linenum); } fclose(f); /* ooh */ qsort(todo_list, todo_used, sizeof(todo_entry), todocmp); } static void do_usage(const char *me) { printf( "usage: %s [options...]\n" "\n" "\t-a\tdisplay all reminders (regardless of date)\n" "\t-c\tgroup by category\n" "\t-d\tonly display items with deadlines\n" "\t-e\tedit reminder file (%s)\n" "\t-m\tmonochrome output\n" "\t-r\tdisplay in reverse order\n" "\t-f \tuse as .todo file\n" "\t\tNumber of days to look ahead\n" "\n", me, todo_fn); exit(EXIT_FAILURE); } static void showdays(const int limit_days) { int i = 0; for (; iwhen - today) / 86400; if (reverse) { if (days > limit_days && limit_days != -1) return; } else { if (days > limit_days && limit_days != -1) continue; } t = localtime(&(e->when)); if (monochrome) { if (e->when < today) { if (e->when == 0) { if (!deadlines_only) printf(" [ * ] %s\n", e->what); } else printf("*(%02d-%02d)[%02d d] %s *\n", t->tm_mon+1, t->tm_mday, days, e->what); } else printf(" (%02d-%02d)[%02d d] %s\n", t->tm_mon+1, t->tm_mday, days, e->what); } else //colour { if (e->when <= (today + 86400)) // +1 for tomorrow -> yellow { if (e->when == 0) { // undated items blue if (!deadlines_only) printf("%s [*] %s%s\n", ANSI_BLUE, e->what, ANSI_NORMAL); } else { if (days < 0) // overdue red printf("%s*(%02d-%02d)[%02d d]%s %s *\n", ANSI_RED, t->tm_mon+1, t->tm_mday, days, ANSI_NORMAL, e->what); else // due today/tomorrow yellow printf("%s*(%02d-%02d)[%02d d]%s %s *\n", ANSI_YELLOW, t->tm_mon+1, t->tm_mday, days, ANSI_NORMAL, e->what); } } else // not due yet green printf("%s (%02d-%02d)[%02d d]%s %s\n", ANSI_GREEN, t->tm_mon+1, t->tm_mday, days, ANSI_NORMAL, e->what); } } } static void do_edit(void) { char *cmd, *editor = getenv("EDITOR"); if (!editor || !strlen(editor)) editor = default_editor; cmd = (char*)xmalloc(strlen(editor) + strlen(todo_fn) + 2); sprintf(cmd, "%s %s", editor, todo_fn); system(cmd); free(cmd); } int main(int argc, char **argv) { int i; int days = DEFAULT_DAYS; char *home = getenv("HOME"); struct tm t1, *t2; now = time(NULL); t2 = localtime(&now); t1.tm_isdst = 0; t1.tm_sec = 0; t1.tm_min = 0; t1.tm_hour = 0; t1.tm_mday = t2->tm_mday; t1.tm_mon = t2->tm_mon; t1.tm_year = t2->tm_year; today = mktime(&t1); //if (argc > 4) do_usage(argv[0]); if (argc > 1) for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-c") == 0) group_category = 1; else if (strcmp(argv[i], "-r") == 0) reverse = 1; else if (strcmp(argv[i], "-m") == 0) monochrome = 1; else if (strcmp(argv[i], "-d") == 0) deadlines_only = 1; else if (strcmp(argv[i], "-e") == 0) edit_file = 1; else if (strcmp(argv[i], "-a") == 0) days = -1; else // all days if (strcmp(argv[i], "-f") == 0) //filename { i++; if (i >= argc) do_usage(argv[0]); // needs a filename arg at i uses_custom_file = 1; todo_fn = argv[i]; } else // if numbers, use as expiry, else fail and do_usage { char *tmp = NULL; days = (int)strtol(argv[i], &tmp, 10); if (tmp != argv[i] + strlen(argv[i])) do_usage(argv[0]); } } if (todo_fn == NULL) { assert(uses_custom_file == 0); todo_fn = (char*)xmalloc(strlen(home) + strlen(DEFAULTTODOFILE) + 2); sprintf(todo_fn, "%s/%s", home, DEFAULTTODOFILE); } else assert(uses_custom_file != 0); if (edit_file) do_edit(); else { loadtodo(days); if (group_category) printf("\n \t \t %s \n", category); showdays(days); if (group_category) printf("\n"); } free(todo_list); if (!uses_custom_file) free(todo_fn); return EXIT_SUCCESS; }