#include #include #include #include #include "pobj.h" #include "debug.h" #ifndef NULL #define NULL 0 #endif #define MAXLINELEN 255 #define SUN 0 #define MON 1 #define TUE 2 #define WED 3 #define THU 4 #define FRI 5 #define SAT 6 #define DAILY 0 #define MONTHLY 1 #define WEEKLY 2 #define ONCE 3 #define COMMENT 4 #define PSET_INDEX(x, y, z) pobj_memcpy(x, x + y, &z, sizeof(z)) #define PSET_STATIC(x, y) pobj_static_set_ref(&x, y) struct string { char *data; int len; }; struct entry { int month; int day; char dayofweek; char type; struct string *text; struct entry *next; }; int string_ref_fields[] = { member_offset(struct string, data), -1 }; int entry_ref_fields[] = { member_offset(struct entry, text), member_offset(struct entry, next), -1 }; typedef struct string String; typedef struct entry Entry; Entry *head = NULL; Entry *tail = NULL; // Assumes input is non-persistent. void trim(String *s) { int i; for (i = 0; i < s->len && s->data[i] == ' '; i++) { s->data++; } s->len -= i; for (i = s->len - 1; i >= 0 && s->data[i] == ' '; i--) { s->len--; } s->data[s->len] = '\0'; } int findWordEnd(String *s, int start) { int end; while (s->data[start] == ' ') start++; for (end = start; end < s->len && s->data[end] != ' '; end++); return end < s->len ? end : s->len; } // Assumes there is enough space in the destination. void persistentStringCopy(String *src, int off1, String *dst, int off2, int length) { /* int i; */ /* char c = '\0'; */ /* // Can't use strncpy here without some sort of pobj_update() function. */ /* for (i = 0; i < length; i++) { */ /* PSET_INDEX(dst->data, off2 + i, src->data[off1 + i]); */ /* } */ debug("in persistent string copy, src = %x, off1 = %d, dst = %x, off2 = %d, length = %d", src, off1, dst, off2, length); debug("src->len is %d, data at src->data[off1] = %s", src->len, src->data + off1); strncpy(dst->data + off2, src->data + off1, length); dst->data[off2+length] = '\0'; pobj_update(dst->data); POBJ_SET_INT(dst, len, length); debug("end of copy, dst->data = %s, dst->len = %d", dst->data, dst->len); } String *makePersistentString(String *s, int start, int len) { pobj_start(); String *p = (String *) pobj_malloc(sizeof(String)); pobj_ref_typify(p, string_ref_fields); POBJ_SET_REF(p, data, pobj_malloc(sizeof(char) * (len + 1))); persistentStringCopy(s, start, p, 0, len); pobj_end(); return p; } Entry *makeEntry(String *s, char t, int off) { Entry *e = (Entry *) pobj_malloc(sizeof(Entry)); pobj_ref_typify(e, entry_ref_fields); POBJ_SET_CHAR(e, type, t); s = makePersistentString(s, off, s->len - off); POBJ_SET_REF(e, text, s); return e; } Entry *makeCommentEntry(String *s) { pobj_start(); Entry *e = makeEntry(s, COMMENT, 0); pobj_end(); return e; } Entry *makeDayEntry(String *s, int off) { pobj_start(); Entry *e = makeEntry(s, DAILY, off); pobj_end(); return e; } Entry *makeMonthEntry(String *s, int d, int off) { pobj_start(); Entry *e = makeEntry(s, MONTHLY, off); POBJ_SET_INT(e, day, d); pobj_end(); return e; } Entry *makeWeekEntry(String *s, int start, int off) { pobj_start(); Entry *e = makeEntry(s, WEEKLY, off); switch (s->data[start]) { case 'm': POBJ_SET_CHAR(e, dayofweek, MON); break; case 't': switch (s->data[start+1]) { case 'u': POBJ_SET_CHAR(e, dayofweek, TUE); break; default: POBJ_SET_CHAR(e, dayofweek, THU); } break; case 'w': POBJ_SET_CHAR(e, dayofweek, WED); break; case 'f': POBJ_SET_CHAR(e, dayofweek, FRI); break; default: switch (s->data[start+1]) { case 'a': POBJ_SET_CHAR(e, dayofweek, SAT); break; default: POBJ_SET_CHAR(e, dayofweek, SUN); } } pobj_end(); return e; } Entry *makeOnceEntry(String *s, int m, int d, int off) { pobj_start(); Entry *e = makeEntry(s, ONCE, off); POBJ_SET_INT(e, month, m); POBJ_SET_INT(e, day, d); pobj_end(); return e; } // Parse a (non-persistent) string into a (persistent) entry. Entry *parse(String *s) { int start, end, tmp; trim(s); if (s->data[0] == '#' || s->len < 1) { return makeCommentEntry(s); } start = 0; end = findWordEnd(s, start); switch (s->data[0]) { case 'D': return makeDayEntry(s, end + 1); case 'M': start = end; end = findWordEnd(s, start); return makeMonthEntry(s, atoi(s->data + start), end + 1); case 'W': start = end; end = findWordEnd(s, start); return makeWeekEntry(s, start + 1, end + 1); case 'O': start = end; end = findWordEnd(s, start); tmp = atoi(s->data + start); start = end; end = findWordEnd(s, start); return makeOnceEntry(s, tmp, atoi(s->data + start), end + 1); default: return makeCommentEntry(s); } } // Should be rewritten to be sorted? void addEntry(String *str) { Entry *e = parse(str); pobj_start(); if (head == NULL) { PSET_STATIC(head, e); } else { POBJ_SET_REF(tail, next, e); } PSET_STATIC(tail, e); POBJ_SET_REF(e, next, NULL); pobj_end(); } void print(Entry *e) { //e->text->data[e->text->len] = '\0'; printf("%s\n", e->text->data); } void findEntries(int month, int day, int dayofweek) { Entry *tmp = head; Entry *prev = NULL; while (tmp != NULL) { switch (tmp->type) { case DAILY: print(tmp); break; case MONTHLY: if (day == tmp->day) { print(tmp); } break; case WEEKLY: if (dayofweek == tmp->dayofweek) { print(tmp); } break; case ONCE: if (day == tmp->day && month == tmp->month) { print(tmp); // Remove this entry, since it is no longer needed. pobj_start(); if (prev == NULL) { PSET_STATIC(head, tmp->next); } else { POBJ_SET_REF(prev, next, tmp->next); } if (tail == tmp) { PSET_STATIC(tail, prev); } pobj_free(tmp); pobj_end(); } break; } prev = tmp; tmp = tmp->next; } } void getEntries(String *str) { time_t timer; struct tm asdf; struct tm *stm = &asdf; debug("1"); time(&timer); debug("2"); localtime_r(&timer, stm); debug("3"); findEntries(stm->tm_mon + 1, stm->tm_mday, stm->tm_wday); } void dumpEntries(String *str) { Entry *tmp = head; while (tmp != NULL) { switch (tmp->type) { case DAILY: printf("DAILY "); break; case MONTHLY: printf("MONTHLY %d ", tmp->day); break; case WEEKLY: printf("WEEKLY "); switch (tmp->dayofweek) { case SUN: printf("sun "); break; case MON: printf("mon "); break; case TUE: printf("tue "); break; case WED: printf("wed "); break; case THU: printf("thu "); break; case FRI: printf("fri "); break; default: printf("sat "); } break; case ONCE: printf("ONCE %d %d ", tmp->month, tmp->day); break; default: printf("COMMENT "); } print(tmp); tmp = tmp->next; } } int getline(char *string, int num) { if (fgets(string, num, stdin) != string) { return -1; } num = strlen(string); if (string[num-1] == '\n') { string[num-1] = '\0'; return num - 1; } else { return num; } } void error(char *a, char *b, int len) { b[len] = '\0'; printf("%s%s\n", a, b); } void process(String *str) { int end = findWordEnd(str, 0); str->data += end; str->len -= end; switch (str->data[-end]) { case 'A': addEntry(str); break; case 'G': getEntries(str); break; case 'D': dumpEntries(str); break; default: error("Unknown command: ", str->data - end, end); } } void usage(void) { printf("Simple calendar program. Reads in commands from stdin.\n\n"); printf("Commands:\n"); printf("A -- adds the specified entry into the calendar\n"); printf(" can be of one of the following forms:\n"); printf(" DAILY -- denotes that should be printed every day\n"); printf(" WEEKLY -- denotes that should be printed on the\n"); printf(" specified day of the week\n"); printf(" must be one of sun, mon, tue, wed, thu, fri, and sat\n"); printf(" MONTHLY -- denotes that should be printed on the\n"); printf(" specified day of the month\n"); printf(" must be a number between 1 and 31, inclusive\n"); printf(" ONCE -- denotes that should be only\n"); printf(" printed on the specified date\n"); printf(" must be a number between 1 and 12, inclusive\n"); printf(" must be a number between 1 and 31, inclusive\n"); printf(" All other entries are interpreted as comments.\n"); printf("G -- displays the messages for the current day\n"); printf(" deletes ONCE type entries for current day\n"); printf("D -- dumps all entries currently in the calendar\n"); printf(" -- exits the program\n"); } int main(int argc, char **argv) { char line[MAXLINELEN+1]; String str; str.data = line; if (argc > 1 && !strncmp(argv[1], "--help", 6)) { usage(); return 0; } else if (argc > 1) { error("Illegal option: ", argv[1], strlen(argv[1])); return 1; } pobj_init(NULL); while ((str.len = getline(line, MAXLINELEN)) != -1) { process(&str); str.data = line; // Could have been moved by process(). } pobj_shutdown(); return 0; }