/***************************************************************************** ** htmlcal.c = calendar generator for HTML pages ** Copyright (C) 1996, Mark Livingston ** Created: 26 May 96, Mark Livingston, ** Dept. of Computer Science, Univ. of North Carolina at Chapel Hill ** ** This program generates an HTML-formatted calendar and a text-formatted ** calendar, both for HTML pages, from a text description file. The HTML ** for the graphical page uses tables. This produces a calendar that is ** to other calendar-generators I have seen, including one by Daniel Macks ** of Brown University. That program, which I have seen, was written in ** Perl. I prefer to work in C, so I wrote this program instead. I have ** tried to make it somewhat customizable in the names.h header file. *****************************************************************************/ #include #include #include #include #include #include "names.h" #define ERROR -1 #define OK 0 /* Set up an array of linked lists of events */ typedef struct event_struct { char *name; char *place; char *time; char *cost; char *info; struct event_struct *next; } Event; Event *events[31]; /* maximum number of days per month, plus error cache */ static char * mName[12] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; /* Always allow Feb 29.... */ static int maxDays[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; /* Our base year is 1900. Since 1 January 1900 was a Monday, we get the ** following keys for the months. */ static int mkeys[12] = { 1, 4, 4, 0, 2, 5, 0, 3, 6, 1, 4, 6 }; /* This function converts month, date, and year to the day of the week, ** returning an integer between 0 and 6, inclusive, with 0 indicating Sunday. ** This function should work for any year from 1901 to 2099. */ static int DayOfWeek( int month, int date, int year ) { int day; day = ( year - 1900 ) + ( year - 1900 ) / 4 + mkeys[month] + date - 1; /* The above counts the leap day even if it occurs later in the year */ if( ( year % 4 == 0 ) && ( month < 2 ) ) day--; if( ( year % 4 != 0 ) && ( month == 1 ) ) maxDays[ month ]--; /* remove February 29th from non-leap years */ return day % 7; } static void StoreEvent( int day, Event *ev ) { Event *cur; if( ev->name != NULL ) { /* store in slot for given day, in order of appearance in file */ if( events[ day ] == NULL ) { /* nothing on this day yet */ events[ day ] = (Event *) malloc( sizeof( Event ) ); if( ev->name != NULL ) { events[ day ]->name = (char *) malloc( ( strlen( ev->name ) ) * sizeof( char ) ); strncpy( events[ day ]->name, ev->name, strlen( ev->name ) - 1 ); events[day]->name[ strlen( ev->name ) - 1 ] = '\0'; } else events[ day ]->name = NULL; if( ev->time != NULL ) { events[ day ]->time = (char *) malloc( ( strlen( ev->time ) ) * sizeof( char ) ); strncpy( events[ day ]->time, ev->time, strlen( ev->time ) - 1 ); events[day]->time[ strlen( ev->time ) - 1 ] = '\0'; } else events[ day ]->time = NULL; if( ev->cost != NULL ) { events[ day ]->cost = (char *) malloc( ( strlen( ev->cost ) ) * sizeof( char ) ); strncpy( events[ day ]->cost, ev->cost, strlen( ev->cost ) - 1 ); events[day]->cost[ strlen( ev->cost ) - 1 ] = '\0'; } else events[ day ]->cost = NULL; if( ev->place != NULL ) { events[ day ]->place = (char *) malloc( ( strlen( ev->place ) ) * sizeof( char ) ); strncpy( events[ day ]->place, ev->place, strlen( ev->place ) - 1 ); events[day]->place[ strlen( ev->place ) - 1 ] = '\0'; } else events[ day ]->place = NULL; if( ev->info != NULL ) { events[ day ]->info = (char *) malloc( ( strlen( ev->info ) ) * sizeof( char ) ); strncpy( events[ day ]->info, ev->info, strlen( ev->info ) - 1 ); events[ day ]->info[ strlen( ev->info ) - 1 ] = '\0'; } else events[ day ]->info = NULL; events[ day ]->next = NULL; } else { /* put at end of list for this day */ for( cur = events[ day ]; cur->next != NULL; cur = cur->next ) /* EMPTY */; cur->next = (Event *) malloc( sizeof( Event ) ); cur = cur->next; if( ev->name != NULL ) { cur->name = (char *) malloc( ( strlen( ev->name ) ) * sizeof( char ) ); strncpy( cur->name, ev->name, strlen( ev->name ) - 1 ); cur->name[ strlen( ev->name ) - 1 ] = '\0'; } else cur->name = NULL; if( ev->time != NULL ) { cur->time = (char *) malloc( ( strlen( ev->time ) ) * sizeof( char ) ); strncpy( cur->time, ev->time, strlen( ev->time ) - 1 ); cur->time[ strlen( ev->time ) - 1 ] = '\0'; } else cur->time = NULL; if( ev->cost != NULL ) { cur->cost = (char *) malloc( ( strlen( ev->cost ) ) * sizeof( char ) ); strncpy( cur->cost, ev->cost, strlen( ev->cost ) - 1 ); cur->cost[ strlen( ev->cost ) - 1 ] = '\0'; } else cur->cost = NULL; if( ev->place != NULL ) { cur->place = (char *) malloc( ( strlen( ev->place ) ) * sizeof( char ) ); strncpy( cur->place, ev->place, strlen( ev->place ) - 1 ); cur->place[ strlen( ev->place ) - 1 ] = '\0'; } else cur->place = NULL; if( ev->info != NULL ) { cur->info = (char *) malloc( ( strlen( ev->info ) ) * sizeof( char ) ); strncpy( cur->info, ev->info, strlen( ev->info ) - 1 ); cur->info[ strlen( ev->info ) - 1 ] = '\0'; } else cur->info = NULL; cur->next = NULL; } /* end else put at end of list */ } return; } /* Read in the named file, fill array of structs, return the month and year */ int ParseCalFile( char *fname, int *month, int *year ) { FILE *fp; char buf[256], mName[20]; Event curEv; int day, lineno, mm, yy, len; curEv.name = NULL; curEv.place = NULL; curEv.time = NULL; curEv.cost = NULL; curEv.info = NULL; /* Open the file and move to the beginning */ fp = fopen( fname, "r" ); if( fp == NULL ) { /* crash and burn */ fprintf( stderr, "Unable to open file '%s'\n", fname ); return ERROR; } rewind( fp ); lineno = 0; /* First thing to read is the month and year */ fgets( buf, 255, fp ); lineno++; sscanf( buf, "%s", mName ); sscanf( &buf[ strlen( mName ) + 1 ], "%d", &yy ); if( !strncmp( mName, "Jan", 3 ) || !strncmp( mName, "jan", 3 ) ) mm = 1; else if( !strncmp( mName, "Feb", 3 ) || !strncmp( mName, "feb", 3 ) ) mm = 2; else if( !strncmp( mName, "Mar", 3 ) || !strncmp( mName, "mar", 3 ) ) mm = 3; else if( !strncmp( mName, "Apr", 3 ) || !strncmp( mName, "apr", 3 ) ) mm = 4; else if( !strncmp( mName, "May", 3 ) || !strncmp( mName, "may", 3 ) ) mm = 5; else if( !strncmp( mName, "Jun", 3 ) || !strncmp( mName, "jun", 3 ) ) mm = 6; else if( !strncmp( mName, "Jul", 3 ) || !strncmp( mName, "jul", 3 ) ) mm = 7; else if( !strncmp( mName, "Aug", 3 ) || !strncmp( mName, "aug", 3 ) ) mm = 8; else if( !strncmp( mName, "Sep", 3 ) || !strncmp( mName, "sep", 3 ) ) mm = 9; else if( !strncmp( mName, "Oct", 3 ) || !strncmp( mName, "oct", 3 ) ) mm = 10; else if( !strncmp( mName, "Nov", 3 ) || !strncmp( mName, "nov", 3 ) ) mm = 11; else if( !strncmp( mName, "Dec", 3 ) || !strncmp( mName, "dec", 3 ) ) mm = 12; else /* assume month is given numerically between 1 - 12 */ sscanf( mName, "%d", &mm ); if( mm < 1 || mm > 12 ) { fprintf( stderr, "Couldn't find month in input\n" ); return ERROR; } else *month = mm; /* Allow "May 96" for "May 1996" */ if( yy < 100 ) *year = 1900 + yy; else *year = yy; /* Now read descriptions */ do { fgets( buf, 255, fp ); lineno++; } while( !strcmp( buf, "\n" ) ); while( !feof( fp ) ) { /* ** This is sort of a token parsing loop. The possible things we could ** see are the names of the fields, a blank line (or we exit the loop). ** For a field (including the pseudo-field "day", we update the current ** event record. For a blank line, we store the event as is and start ** a new one. Each of the fields has multiple tests, with varying ** amounts of whitespace surrounding the =. */ if( !strcmp( buf, "\n" ) ) { StoreEvent( day, &curEv ); if( curEv.name != NULL ) { free( curEv.name ); curEv.name = NULL; } if( curEv.place != NULL ) { free( curEv.place ); curEv.place = NULL; } if( curEv.time != NULL ) { free( curEv.time ); curEv.time = NULL; } if( curEv.cost != NULL ) { free( curEv.cost ); curEv.cost = NULL; } if( curEv.info != NULL ) { free( curEv.info ); curEv.info = NULL; } } /* NAME field ***************************************************/ else if( !strncmp( buf, "name = ", 7 ) ) { if( curEv.name != NULL ) free( curEv.name ); curEv.name = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.name, &buf[7] ); } else if( !strncmp( buf, "name =", 6 ) ) { if( curEv.name != NULL ) free( curEv.name ); curEv.name = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.name, &buf[6] ); } else if( !strncmp( buf, "name= ", 6 ) ) { if( curEv.name != NULL ) free( curEv.name ); curEv.name = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.name, &buf[6] ); } else if( !strncmp( buf, "name=", 5 ) ) { if( curEv.name != NULL ) free( curEv.name ); curEv.name = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.name, &buf[5] ); } /* TIME field ***************************************************/ else if( !strncmp( buf, "time = ", 7 ) ) { if( curEv.time != NULL ) free( curEv.time ); curEv.time = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.time, &buf[7] ); } else if( !strncmp( buf, "time =", 6 ) ) { if( curEv.time != NULL ) free( curEv.time ); curEv.time = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.time, &buf[6] ); } else if( !strncmp( buf, "time= ", 6 ) ) { if( curEv.time != NULL ) free( curEv.time ); curEv.time = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.time, &buf[6] ); } else if( !strncmp( buf, "time=", 5 ) ) { if( curEv.time != NULL ) free( curEv.time ); curEv.time = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.time, &buf[5] ); } /* COST field ***************************************************/ else if( !strncmp( buf, "cost = ", 7 ) ) { if( curEv.cost != NULL ) free( curEv.cost ); curEv.cost = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.cost, &buf[7] ); } else if( !strncmp( buf, "cost =", 6 ) ) { if( curEv.cost != NULL ) free( curEv.cost ); curEv.cost = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.cost, &buf[6] ); } else if( !strncmp( buf, "cost= ", 6 ) ) { if( curEv.cost != NULL ) free( curEv.cost ); curEv.cost = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.cost, &buf[6] ); } else if( !strncmp( buf, "cost=", 5 ) ) { if( curEv.cost != NULL ) free( curEv.cost ); curEv.cost = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.cost, &buf[5] ); } /* PLACE field ***************************************************/ else if( !strncmp( buf, "place = ", 8 ) ) { if( curEv.place != NULL ) free( curEv.place ); curEv.place = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.place, &buf[8] ); } else if( !strncmp( buf, "place =", 7 ) ) { if( curEv.place != NULL ) free( curEv.place ); curEv.place = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.place, &buf[7] ); } else if( !strncmp( buf, "place= ", 7 ) ) { if( curEv.place != NULL ) free( curEv.place ); curEv.place = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.place, &buf[7] ); } else if( !strncmp( buf, "place=", 6 ) ) { if( curEv.place != NULL ) free( curEv.place ); curEv.place = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.place, &buf[6] ); } /* DAY pseudo-field **********************************************/ else if( !strncmp( buf, "day = ", 6 ) ) { sscanf( &buf[6], "%d", &day ); if( day < 1 || day > maxDays[ mm ] ) { fprintf( stderr, "Illegal day specified on line %d\n", lineno ); return ERROR; } day--; } else if( !strncmp( buf, "day =", 5 ) ) { sscanf( &buf[5], "%d", &day ); if( day < 1 || day > maxDays[ mm ] ) { fprintf( stderr, "Illegal day specified on line %d\n", lineno ); return ERROR; } day--; } else if( !strncmp( buf, "day= ", 5 ) ) { sscanf( &buf[5], "%d", &day ); if( day < 1 || day > maxDays[ mm ] ) { fprintf( stderr, "Illegal day specified on line %d\n", lineno ); return ERROR; } day--; } else if( !strncmp( buf, "day=", 4 ) ) { sscanf( &buf[4], "%d", &day ); if( day < 1 || day > maxDays[ mm ] ) { fprintf( stderr, "Illegal day specified on line %d\n", lineno ); return ERROR; } day--; } /* INFO field ****************************************************/ else if( !strncmp( buf, "info = ", 7 ) ) { if( curEv.info != NULL ) free( curEv.info ); curEv.info = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.info, &buf[7] ); } else if( !strncmp( buf, "info =", 6 ) ) { if( curEv.info != NULL ) free( curEv.info ); curEv.info = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.info, &buf[6] ); } else if( !strncmp( buf, "info= ", 6 ) ) { if( curEv.info != NULL ) free( curEv.info ); curEv.info = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.info, &buf[6] ); } else if( !strncmp( buf, "info=", 5 ) ) { if( curEv.info != NULL ) free( curEv.info ); curEv.info = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.info, &buf[5] ); } /* extension of info field */ else if( buf[0] == '+' ) { if( curEv.info == NULL ) { curEv.info = (char *) malloc( strlen( buf ) * sizeof( char ) ); strcpy( curEv.info, &buf[1] ); } else { len = strlen( curEv.info ) + strlen( buf ) + 2; curEv.info = (char *) realloc( curEv.info, len ); strcpy( &curEv.info[ strlen( curEv.info ) ], " " ); strcpy( &curEv.info[ strlen( curEv.info ) ], &buf[1] ); } } fgets( buf, 255, fp ); lineno++; } /* end while( !EOF ) */ /* We hit the end of the input file, but we might have a record still to put in */ StoreEvent( day, &curEv ); return OK; } /* end ParseCalFile */ int WriteHTMLFiles( int month, int year, int day ) { int i, j, curDate; char tname[80], iname[80]; FILE *fpTab, *fpTxt; struct tm *curtime; time_t sectime; Event *cur; /* Get the current date */ sectime = time( NULL ); curtime = localtime( §ime ); /* Text version file */ sprintf( tname, "%s-text.html", mName[ month ] ); fpTxt = fopen( tname, "w" ); if( fpTxt == NULL ) { fprintf( stderr, "Unable to create text version HTML file\n" ); return ERROR; } /* Text version header */ fprintf( fpTxt, "\n%s Calendar\n", organization ); fprintf( fpTxt, "\n", BKGD, LINK, ALNK, VLNK ); fprintf( fpTxt, "

%s Calendar

\n\n", organization ); fprintf( fpTxt, "

%s %d

\n", mName[ month ], year ); /* The event records */ for( i = 0; i < maxDays[ month ]; i++ ) if( events[ i ] != NULL ) { fprintf( fpTxt, "\n

%d

\n", i + 1 ); for( cur = events[ i ]; cur != NULL; cur = cur->next ) { if( cur->info != NULL ) fprintf( fpTxt, "", '"', i + 1, '"' ); fprintf( fpTxt, "* %s", cur->name ); if( cur->place != NULL ) fprintf( fpTxt, ", %s", cur->place ); if( cur->time != NULL ) fprintf( fpTxt, ", %s", cur->time ); if( cur->cost != NULL ) fprintf( fpTxt, ", %s", cur->cost ); if( cur->info != NULL ) fprintf( fpTxt, ", %s", cur->info ); fprintf( fpTxt, "
\n" ); } } /* Text version footer */ fprintf( fpTxt, "
\n\nThis web page was generated automatically by " ); fprintf( fpTxt, "htmlcal by\n" ); fprintf( fpTxt, "
" ); fprintf( fpTxt, "Mark Livingston on %d %s %d.\n", curtime->tm_mday, mName[ curtime->tm_mon ], 1900 + curtime->tm_year ); fprintf( fpTxt, "

There's that\n" ); fprintf( fpTxt, "disclaimer again!\n\n\n\n" ); fclose( fpTxt ); /* Table and image version open file */ sprintf( iname, "%s.html", mName[ month ] ); fpTab = fopen( iname, "w" ); if( fpTab == NULL ) { fprintf( stderr, "Unable to create table version HTML file\n" ); return ERROR; } /* Fancy version header */ fprintf( fpTab, "\n%s Calendar\n", organization ); fprintf( fpTxt, "\n", BKGD, LINK, ALNK, VLNK ); fprintf( fpTxt, "

" ); if( strcmp( imagefile, "" ) ) { fprintf( fpTab, "%s Calendar\n", imagefile, organization ); fprintf( fpTab, "\n", imagefile ); } else { fprintf( fpTab, "%s Calendar\n", organization ); } fprintf( fpTab, "

\n\n" ); fprintf( fpTab, "
\nA text-only version of", tname ); fprintf( fpTab, " the calendar is available.\n
\n

\n\n" ); /* Begin the table */ fprintf( fpTab, "\n\n", mName[ month ], year ); fprintf( fpTab, "\n\n" ); /* First half-row, if necessary */ if( day != 0 ) { fprintf( fpTab, "\n" ); for( i = 0; i < day; i++ ) fprintf( fpTab, "\n" ); /* empty cells */ for( curDate = 0; i < 7; i++, curDate++ ) { fprintf( fpTab, "\n" ); } fprintf( fpTab, "\n" ); } /* Intermediate rows */ while( curDate + 7 < maxDays[ month ] ) { /* at least 7 days left */ fprintf( fpTab, "\n" ); for( j = 0; j < 7; j++, curDate++ ) { fprintf( fpTab, "\n" ); } /* end for( j ) */ fprintf( fpTab, "\n" ); } /* end while */ /* Last row, if necessary */ if( curDate < maxDays[ month ] ) { fprintf( fpTab, "\n" ); for( i = 0; curDate < maxDays[ month ]; curDate++, i++ ) { fprintf( fpTab, "\n" ); } /* Don't need to fill out the row with empty cells */ fprintf( fpTab, "\n" ); } /* End the table and print the footer */ fprintf( fpTab, "

" ); fprintf( fpTab, "%s %d

Sunday\nMonday\n" ); fprintf( fpTab, "Tuesday\nWednesday\n" ); fprintf( fpTab, "Thursday\nFriday\n" ); fprintf( fpTab, "Saturday\n

%d

\n", curDate + 1 ); if( events[ curDate ] != NULL ) for( cur = events[ curDate ]; cur != NULL; cur = cur->next ) { if( cur->info != NULL ) fprintf( fpTab, "\n", tname, curDate + 1 ); fprintf( fpTab, "* %s", cur->name ); if( cur->info != NULL ) fprintf( fpTab, "\n" ); if( cur->place != NULL ) fprintf( fpTab, ", %s", cur->place ); if( cur->time != NULL ) fprintf( fpTab, ", %s", cur->time ); if( cur->cost != NULL ) fprintf( fpTab, ", %s", cur->cost ); if( cur->info != NULL ) fprintf( fpTab, "
Details\n", tname, curDate + 1 ); fprintf( fpTab, "
\n" ); } /* end for and if */ fprintf( fpTab, "

%d

\n", curDate + 1 ); if( events[ curDate ] != NULL ) for( cur = events[ curDate ]; cur != NULL; cur = cur->next ) { if( cur->info != NULL ) fprintf( fpTab, "\n", tname, curDate + 1 ); fprintf( fpTab, "* %s", cur->name ); if( cur->info != NULL ) fprintf( fpTab, "\n" ); if( cur->place != NULL ) fprintf( fpTab, ", %s", cur->place ); if( cur->time != NULL ) fprintf( fpTab, ", %s", cur->time ); if( cur->cost != NULL ) fprintf( fpTab, ", %s", cur->cost ); if( cur->info != NULL ) fprintf( fpTab, "
Details\n", tname, curDate + 1 ); fprintf( fpTab, "
\n" ); } /* end for( cur ) and if */ fprintf( fpTab, "

%d

\n", curDate + 1 ); if( events[ curDate ] != NULL ) for( cur = events[ curDate ]; cur != NULL; cur = cur->next ) { if( cur->info != NULL ) fprintf( fpTab, "\n", tname, curDate + 1 ); fprintf( fpTab, "* %s", cur->name ); if( cur->info != NULL ) fprintf( fpTab, "\n" ); if( cur->place != NULL ) fprintf( fpTab, ", %s", cur->place ); if( cur->time != NULL ) fprintf( fpTab, ", %s", cur->time ); if( cur->cost != NULL ) fprintf( fpTab, ", %s", cur->cost ); if( cur->info != NULL ) fprintf( fpTab, "
Details\n", tname, curDate + 1 ); fprintf( fpTab, "
\n" ); } /* end for and if */ fprintf( fpTab, "
\n" ); fprintf( fpTab, "


\n\nThis web page was generated automatically by " ); fprintf( fpTab, "htmlcal by\n" ); fprintf( fpTab, "" ); fprintf( fpTab, "Mark Livingston on %d %s %d.\n", curtime->tm_mday, mName[ curtime->tm_mon ], 1900 + curtime->tm_year ); fprintf( fpTab, "

There's that\n" ); fprintf( fpTab, "disclaimer again!\n\n\n\n" ); fclose( fpTab ); return OK; } void usage( char *Progname ) { fprintf( stderr, "Usage: %s [ -help ] -f \n\n", Progname ); fprintf( stderr, "-help = print this help message\n" ); fprintf( stderr, "-f = read events from " ); fprintf( stderr, " **required argument**\n" ); return; } int main( int argc, char *argv[] ) { int month; /* 0 - 11 */ int year; /* if < 100, assume 19xx */ int first; /* day of week first of month falls on, 0-6 */ int i; char *fname = NULL; /* Parse arguments */ for( i = 1; i < argc; i++ ) { if( !strcmp( argv[i], "-f" ) ) fname = argv[ ++i ]; else if( !strcmp( argv[i], "-help" ) ) { usage( argv[0] ); return; } else { fprintf( stderr, "Unknown argument '%s' ignored\n", argv[i] ); usage( argv[0] ); } } /* end for( i ) */ if( fname == NULL ) { fprintf( stderr, "No input file specified\n" ); usage( argv[0] ); return; } /* Read in description file */ if( ParseCalFile( fname, &month, &year ) == OK ) { /* Figure out the calendar format for the month */ first = DayOfWeek( month - 1, 1, year ); /* Write output files */ WriteHTMLFiles( month - 1, year, first ); } return 0; }