/* This is a program to keep track of events occurring in tasmania. At the moment the bash script 'ev.sh' is more useful IDEAS Create a series of filters, written in c which filter the event data and transform it. Eg ev.today would only display events which are occurring today. ev.free would only display free events. ev.html would transform text data into html for display. ev.latex would trasform the text event data into LaTeX or pdf. These filters could be chained together et cat list.txt | ev.today | ev.free | ev.html would display in html all free event occuring today. HISTORY 2014 begun 2015 writing ev.sh //TODO!!! // make a 'current event' paradigm where certain commands operate // on the current event. By default the current event would be // the last event added or edited in the database. // for example the command 'pub' could make the current event // a public event. // add a command which displays all events for the comming week. // */ // something to store and print cultural and social public events #include #include #include #include #define INVALID -1 // some colours for nice printing #define RED "\x1B[31m" #define GREEN "\x1B[32m" #define YELLOW "\x1B[33m" #define BLUE "\x1B[34m" #define MAGENTA "\x1B[35m" #define CYAN "\x1B[36m" #define WHITE "\x1B[37m" #define NORMAL "\x1B[0m" void colourPrint(char *); char * Colours[] = {RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, NORMAL}; // given a month name or partial name returns a number // months in struct tm are 0 based int monthFromName(char * name) { int ii = -1; char month[50]; char * c = name; for (ii = 0; ii < 12; ii++) { strftime(month, 49, "%B", &(struct tm){0,0,0,0,ii,0,0,0,0}); //printf("name:%s\n", month); if (tolower(name[0]) == tolower(month[0]) && tolower(name[1]) == tolower(month[1]) && tolower(name[2]) == tolower(month[2])) { return ii; //printf("ii:%d", ii); } } return ii; } // construct a tm date from a free string. // could use 'strptime' but that is Posix and not // standard c. // this is a mess!!! the logic needs to be cleaned up // also need to handle 'tomorrow' 'wed 4pm' 'today 1pm' etc struct tm * dateFromString(struct tm * date, char * input) { char buffer[200]; char month[100] = "[?]"; int iMonth=0; char ampm = '?'; int daytime = -1, day = -1, year = -1; int result = -1; //printf("date: %s\n", ctime(date)); // eg 4/6/2012 = 4th of july 2012 result = sscanf(input, "%d/%d/%d", &day, &iMonth, &year); if (result < 3) // eg 4/6 = 4th of july result = sscanf(input, "%d/%d", &day, &iMonth); if (result < 2) // eg january 9, 2012 result = sscanf(input, "%20[a-zA-Z] %d%*[, ] %d", month, &day, &year); if (result < 3) // eg 9 january, 2012 result = sscanf(input, "%d %20[a-zA-Z]%*[, \t]%d", &day, month, &year); //printf("result: %d, month: %s, day: %d year: %d\n", // result, month, day, year); // if no year, use the current year if (year == INVALID) { time_t now = time(NULL); year = localtime(&now)->tm_year+1900; } if (strcmp(month, "[?]") != 0) date->tm_mon = monthFromName(month); else date->tm_mon = iMonth; date->tm_mday = day; date->tm_year = year - 1900; //strftime(buffer, 199, "%A, %B %d, %Y", date); //printf("date: %s\n", buffer); time_t tt = mktime(date); date->tm_wday = localtime(&tt)->tm_wday; printf("date: %s", asctime(date)); } // event publicity: all these names below should probably be // 'eventPublicity' etc... enum EventType { PRIVATE=0, GROUP, PUBLIC, INVALIDEVENT }; struct { enum EventType type; char * name; char * shortDesc; char * longDesc; } EventTypeInfo[] = { { PRIVATE, "private", "events to be publicised only to family", ""}, { GROUP, "group", "events to be publicised to a select group", ""}, { PUBLIC, "public", "events to be publicised publicy without restriction", ""}, { INVALIDEVENT, "invalid", "an incorrect or uninitialised event type", ""} }; void printEventTypes() { int ii; printf("The valid event types are: \n"); for (ii = PRIVATE; ii < INVALIDEVENT; ii++) { printf("%s\n", EventTypeInfo[ii].name); printf(" %s\n", EventTypeInfo[ii].shortDesc); } } // information about social events struct Event { struct tm startWhen; struct tm endWhen; enum EventType type; char title[500]; char location[500]; char link[500]; char imageFile[500]; char notes[5000]; }; // below are some functions to perform simple date arithmetic // is a particular date today? int isToday(struct tm date) { time_t now; time(&now); struct tm today = *localtime(&now); if (today.tm_year == date.tm_year && today.tm_mon == date.tm_mon && today.tm_mday == date.tm_mday) return 1; else return 0; //printf(WHITE "Today " NORMAL); } // get a struct tm representing tomorrow // need to set hour min and sec == 0 !!!! struct tm * getTomorrow(struct tm * date) { time_t now; time(&now); // add a days worth of seconds time_t tomorrow = now + 60*60*24; date = localtime(&tomorrow); return date; } // get a struct tm representing today // need to set hour min and sec == 0 !!!! struct tm * getToday(struct tm * date) { time_t now; time(&now); date = localtime(&now); return date; } // is a particular date today, tomorrow etc int isFuture(struct tm date) { time_t now; time(&now); time_t someDay; struct tm today = *localtime(&now); someDay = mktime(&date); if (today.tm_year == date.tm_year && today.tm_mon == date.tm_mon && today.tm_mday == date.tm_mday) return 1; if (someDay - now > 0) return 1; return 0; } // get the date of the next weekday name // eg today=monday weekDay=wed struct tm= today+2 struct tm nextWeekDay(char * weekDay) { time_t now; time(&now); struct tm today = *localtime(&now); } // just initialise all members of the event structure void newEvent(struct Event * event) { //memset(&event->startWhen, 0, sizeof(struct tm)); //event->startWhen = (struct tm){-1,-1,-1,-1,-1,-1,-1,-1,-1}; event->startWhen = (struct tm){0,0,0,0,0,0,0,0,0}; event->endWhen = (struct tm){-1,-1,-1,-1,-1,-1,-1,-1,-1}; event->type = PUBLIC; strcpy(event->title, ""); strcpy(event->location, ""); strcpy(event->link, ""); strcpy(event->imageFile, ""); strcpy(event->notes, ""); } // print a 2 line event summary. void printEventSummary(struct Event * event) { if (isToday(event->startWhen)) printf(WHITE "Today " NORMAL); char displayDate[200] = ""; //todo! hours and minutes if applicable strftime(displayDate, 199, "%A %d %B, %Y", &event->startWhen); printf(BLUE "%s\n" NORMAL " %s\n", displayDate, event->title); } // print detailed info about the event void printEvent(struct Event * event) { printf("%s:\n %s at %s\n", asctime(&event->startWhen), event->title, event->location); } // print completely detailed info about the event structure void dumpEvent(struct Event * event) { char startDate[200], endDate[200]; strftime(startDate, 199, "%d/%m/%Y %H:%M:%S", &event->startWhen); strftime(endDate, 199, "%d/%m/%Y %H:%M:%S", &event->endWhen); printf("type: %s \nstart date:%s \nend date:%s\n" "title:'%s' \nlocation:'%s' \nlink:'%s' \n" "image:'%s' \nnotes:'%s'\n", EventTypeInfo[event->type].name, startDate, endDate, event->title, event->location, event->link, event->imageFile, event->notes); } void parseEvent(struct Event * event, const char * input) { } struct EventList { char dataFile[200]; //where data is read from and written to //"/home/mjb/sf/htdocs/app/events.txt"; size_t size; //how many events are there size_t capacity; // if the event list is malloced, realloced int currentEvent; // event being modified int modified; // true if new or edited event struct Event listing[1000]; }; // create a new blank listing of events void newEventList(struct EventList * list) { list->size = 0; list->capacity = 1000; strcpy(list->dataFile, "/home/mjb/sf/htdocs/app/test.txt"); } // load saved events from a file void readEvents(struct EventList * list, char * file) { // fread or fscanf or strtok? FILE *fp; char word[1000]; struct Event * nextEvent = &list->listing[0]; newEvent(nextEvent); //if (file == NULL) // file = "/home/mjb/sf/htdocs/app/events.txt"; if ((fp = fopen (list->dataFile, "r")) == NULL) { fprintf(stderr, RED "couldnt open file: " NORMAL "%s\n", list->dataFile); return; } /* an example record from the data file /event public /dates 10/4/2014 11:30 - 11/4/2014 12:00 /title rally florentine /location elizabeth mall /link www.site.org /image /images/foto.jpg /notes guest speakers * */ char title[200], notes[1000]; char line[300]; char dates[500]; char * remainder; // obliterate everthing list->size = 0; while (fscanf(fp, "%999s", word) == 1) { //printf("%s \n", word); if (strcmp(word, "/event") == 0){ list->size++; newEvent(nextEvent); //printf("\n"); } if (strcmp(word, "/dates") == 0){ fscanf(fp, "%499[^\n]", dates); remainder = (char *)strptime(dates, "%d/%m/%Y %H:%M", &nextEvent->startWhen); //printf("1st parsed date: %s\n", // asctime(&nextEvent->startWhen)); //printf("remainder: %s\n", dates); remainder = (char *)strptime(remainder, " - %d/%m/%Y %H:%M", &nextEvent->endWhen); //printf("2nd parsed date: %s\n", // asctime(&nextEvent->endWhen)); } // %*[ \t\n] discards initial white space if (strcmp(word, "/type") == 0){ fscanf(fp, "%*[ \t]%199[^\n]", line); sscanf(line, "%s", word); if (strcmp(word, "private") == 0) nextEvent->type = PRIVATE; else nextEvent->type = PUBLIC; } if (strcmp(word, "/title") == 0){ fscanf(fp, "%*[ \t]%199[^\n]", nextEvent->title); } if (strcmp(word, "/location") == 0){ fscanf(fp, "%*[ \t]%199[^\n]", nextEvent->location); //printf("location: %s\n", nextEvent->location); } if (strcmp(word, "/link") == 0){ fscanf(fp, "%*[ \t]%199[^\n]", nextEvent->link); } if (strcmp(word, "/image") == 0){ fscanf(fp, "%*[ \t]%199[^\n]", nextEvent->imageFile); } if (strcmp(word, "/notes") == 0){ fscanf(fp, "%*[ \t\n]%999[^*]%*c", nextEvent->notes); nextEvent++; } /* stray *s cause spurious events if (strcmp(word, "*") == 0){ nextEvent++; } */ } printf(WHITE "%d" NORMAL " events read from " YELLOW "%s\n" NORMAL, nextEvent - &list->listing[0], list->dataFile); fclose (fp); } // save all events to file void writeEvents(struct EventList * list, char * file) { if (file == NULL || strlen(file) == 0) file = list->dataFile; if (list->size < 1) { printf("no events to write!\n"); return; } //strcpy(file, "test.txt"); FILE * fp = fopen(file, "w"); // for testing only !!! fp = stdout; if (fp == NULL) { fprintf(stderr, "Could not open file '%s' for writing\n", file); return; } int ii; struct Event * event; // write events in an easily parsable format char startDate[200], endDate[200]; char temp[200]; for (ii = 0; ii < list->size; ii++) { event = &list->listing[ii]; fprintf(fp, "/event %d\n", ii); strftime(startDate, 199, "%d/%m/%Y %H:%M", &event->startWhen); strftime(endDate, 199, "%d/%m/%Y %H:%M", &event->endWhen); fprintf(fp, "/dates %s - %s\n", startDate, endDate); fprintf(fp, "/title %s\n", event->title); fprintf(fp, "/location %s\n", event->location); fprintf(fp, "/link %s\n", event->link); if (sscanf(event->notes, "%s", temp) < 1) fprintf(fp, "/notes\n*\n"); else fprintf(fp, "/notes\n %s*\n", event->notes); } printf(WHITE "%d" NORMAL " events written to " YELLOW "%s\n" NORMAL, list->size, file); fclose(fp); } // changes where data is read from and written to void changeDataFile(struct EventList * list, char * input) { if (input == NULL || strlen(input) == 0) { printf("enter a new data file name? "); fscanf(stdin, "%200s", list->dataFile); } else strcpy(list->dataFile, input); printf("data file changed to '%s'\n", list->dataFile); } // backs up the public and private event data files void backupDataFile(struct EventList * list, char * input) { /* if (input == NULL || strlen(input) == 0) { printf("enter a new data file name? "); fscanf(stdin, "%200s", list->dataFile); } else strcpy(list->dataFile, input); */ char fileName[200]; time_t now = time(NULL); struct tm date = *localtime(&now); strftime(fileName, 199, "events.%d%b%Y.txt", &date); printf("all events NOT!! backed up to " YELLOW "%s\n" NORMAL, fileName); } // create some sample events for testing void sampleEvents(struct EventList * list) { } void printEvents(struct EventList * list, char * input) { int ii; struct Event * ee; printf("Number of Events: %d\n", list->size); printf("Capacity of list: %d\n", list->capacity); for (ii = 0; ii < list->size; ii++) { printf(RED "%d " NORMAL, ii); printEventSummary(&list->listing[ii]); } } // display all events today and afterwards void printCurrentEvents(struct EventList * list, char * input) { int ii; struct Event * ee; //printf("Number of Events: %d\n", list->size); for (ii = 0; ii < list->size; ii++) { ee = &list->listing[ii]; if (isFuture(ee->startWhen)) { printEventSummary(&list->listing[ii]); } } } // display all events for today and only today void printTodaysEvents(struct EventList * list, char * input) { int ii; struct Event * ee; int count; //printf("Number of Events: %d\n", list->size); for (ii = 0; ii < list->size; ii++) { ee = &list->listing[ii]; if (isToday(ee->startWhen)) { printEventSummary(&list->listing[ii]); count++; } } printf(BLUE "%d " NORMAL "events today\n", ii); } // this is counterpart to dumpEvent() void eventDump(struct EventList * list, char * input) { int number; struct Event * event; if (input == NULL || strlen(input) == 0) { printf("which event number? "); fscanf(stdin, "%100d", &number); } else sscanf(input, "%100d", &number); if (number > list->size) { printf("%d is too big, only %d events\n", number, list->size); return; } event = &list->listing[number]; dumpEvent(event); } // show detaild info about a particular event, but not debugging void showEvent(struct EventList * list, char * input) { int number; struct Event * event; if (input == NULL || strlen(input) == 0) { printf("which event number? "); fscanf(stdin, "%100d", &number); } else sscanf(input, "%100d", &number); event = &list->listing[number]; if (isToday(event->startWhen)) printf(WHITE "Today " NORMAL); char displayDate[200] = ""; //todo! hours and minutes if applicable strftime(displayDate, 199, "%A %d %B, %Y", &event->startWhen); printf(BLUE "%s\n" NORMAL " %s\n\n%s", displayDate, event->title, event->notes); } //add a new event to the list void addEvent(struct EventList * list, char * input) { char line[200]; char text[200]; //int result = -1; struct Event * nextEvent = &list->listing[list->size]; newEvent(nextEvent); nextEvent->type = PUBLIC; //struct tm day = {-1,-1,-1,-1,-1,-1,-1,-1,-1}; if (input == NULL || strlen(input) == 0) { colourPrint("Enter a new event...\n"); printf(YELLOW "when? " NORMAL); fgets(line, 199, stdin); dateFromString(&nextEvent->startWhen, line); printf(YELLOW "what? " NORMAL); fgets(line, 199, stdin); *(line+strlen(line)-1) = '\0'; //fscanf(stdin, "%*[ \t]%[^\n]%*c", nextEvent->title); if (sscanf(line, "%s", text) < 1) { printf(RED "event cancelled\n" NORMAL); return; } else strcpy(nextEvent->title, line); printf(YELLOW "notes: " NORMAL); fgets(line, 199, stdin); } list->size++; printEvents(list, "aa"); //printEventSummary(nextEvent); //printEventSummary(&list->listing[list->size-1]); } //add a new private event to the list void addPrivateEvent(struct EventList * list, char * input) { char line[200]; char text[200]; //int result = -1; struct Event * nextEvent = &list->listing[list->size]; newEvent(nextEvent); nextEvent->type = PRIVATE; //struct tm day = {-1,-1,-1,-1,-1,-1,-1,-1,-1}; if (input == NULL || strlen(input) == 0) { colourPrint("Enter a new event...\n"); printf(YELLOW "when? " NORMAL); fgets(line, 199, stdin); dateFromString(&nextEvent->startWhen, line); printf(YELLOW "what? " NORMAL); fgets(line, 199, stdin); *(line+strlen(line)-1) = '\0'; //fscanf(stdin, "%*[ \t]%[^\n]%*c", nextEvent->title); if (sscanf(line, "%s", text) < 1) { printf(RED "event cancelled\n" NORMAL); return; } else strcpy(nextEvent->title, line); printf(YELLOW "notes: " NORMAL); fgets(line, 199, stdin); } list->size++; printEvents(list, "aa"); //printEventSummary(nextEvent); //printEventSummary(&list->listing[list->size-1]); } // the different commands the user can enter in the // interpreter enum Command { PRINT=0, PRINTCURRENT, PRINTTODAYS, PRINTONE, DUMPONE, HELP, HELPONE, NEW, NEWPRIVATE, EDIT, READ, WRITE, DATAFILE, BACKUP, QUIT, INVALIDCOMMAND }; void invalidCommand(struct EventList *, char *); void printCommands(struct EventList *, char *); void printCommandDescription(struct EventList *, char *); void quit(struct EventList *, char *); struct { enum Command command; char * name; char * shortDesc; char * longDesc; void (*fn)(struct EventList * list, char * parameter); } CommandInfo[] = { { PRINT, "p", "show all events", "", printEvents }, { PRINTCURRENT, "c", "show all current events from today onwards", "This command displays all events from today onwards", printCurrentEvents }, { PRINTTODAYS, "t", "show all events for today", "This command displays all events taking place today", printTodaysEvents }, { PRINTONE, "P", "display a particular event", "This command displays detailed information about a\n" "particular event selected by its 'id' number", showEvent }, { DUMPONE, "D", "dumps all information about a particular event", "This command is mainly of debugging use to\n" "display all information about an event structure", eventDump }, { HELP, "h", "show all available commands", "", printCommands}, { HELPONE, "H", "show a full description for a command", "", printCommandDescription}, { NEW, "n", "record a new public event", "", addEvent}, { NEWPRIVATE, "N", "record a new private event", "This command records a new event which is not for\n" "general publication. The data is stored in a separate\n" "text data file", addPrivateEvent}, { EDIT, "edit", "edit an event", "", NULL}, { READ, "r", "read events from the data file", "This command loads all the events contained in the\n" "current data file. Events which have been added \n" "with the 'new event' command should be written to\n" "file first or they will be lost", readEvents}, { WRITE, "w", "write all events to file", "", writeEvents}, { DATAFILE, "f", "change the data-file to use", "This command changes the text data file which will\n" "be used when writing new events to file, or reading\n" "in events", changeDataFile}, { BACKUP, "b", "create a backup of the public and private event data files", "This command copies the data to a file named something\n" "like events.5april2014.txt\n", backupDataFile}, { QUIT, "x", "exits", "This command terminates the application", quit}, { INVALIDCOMMAND, "invalid", "an incorrect or uninitialized command type", "", invalidCommand} }; // print text in lots of colours! .... void colourPrint(char * text) { int ii; int length = strlen(text); for (ii = 0; ii < length; ii++) { printf("%s%c", Colours[ii%8], *text); text++; } printf(NORMAL); } // exits the event manager application void quit(struct EventList * list, char * name) { colourPrint("GoodBye!\n"); exit(0); } // get a command type given its name enum Command commandFromName(const char * name) { int ii; for (ii = PRINT; ii < INVALIDCOMMAND; ii++) { if (strcmp(name, CommandInfo[ii].name) == 0) return(CommandInfo[ii].command); //or just return ii; } return(INVALIDCOMMAND); } // print information about a particular command void printCommandInfo(enum Command command) { printf("%s(%d):\n %s\n", CommandInfo[command].name, command, CommandInfo[command].shortDesc); } // prints a detailed description of a given command // this command should provide 'interpolation' on the long // description field void printCommandDescription(struct EventList * list, char * name) { int ii; //char name[200]; enum Command c; if (name == NULL || strlen(name) == 0) { printf("which command?"); fscanf(stdin, "%100s", name); //fgets(name, 199, stdin); } c = commandFromName(name); printf("%s: " RED "%s\n" CYAN " %s\n" NORMAL, CommandInfo[c].name, CommandInfo[c].shortDesc, CommandInfo[c].longDesc); } // print all available commands void printCommands(struct EventList * list, char * input) { int ii; for (ii = PRINT; ii < INVALIDCOMMAND; ii++) { printf(BLUE "%s " NORMAL "%s\n", CommandInfo[ii].name, CommandInfo[ii].shortDesc); } } // a simple handler for invalid interpreter commands void invalidCommand(struct EventList * list, char * string) { printf( "The command entered '" CYAN "%s" NORMAL "' is invalid.\n" "Type '" CYAN "%s" NORMAL "' to see valid commands.\n", string, CommandInfo[HELP].name ); } int main(void) { char buffer[200] = "hi"; struct EventList list; newEventList(&list); char line[1000], command[100], param[900]; colourPrint("!!Tasmanian Events!!\n"); readEvents(&list, NULL); printf("Type '%s' to see valid commands\n", CommandInfo[HELP].name ); while (1) { strcpy(command, ""); strcpy(param, ""); printf(">"); fgets(line, 999, stdin); int result = sscanf(line, "%99s %99[^\n]%*s", command,param); //printf("res:%d com:%s param:%s\n", result, command, param); if (result > 0) { enum Command c = commandFromName(command); if (c == INVALIDCOMMAND) strcpy(param, command); CommandInfo[c].fn(&list, param); } } }