jbr0@cbnews.att.com (joseph.a.brownlee) (10/06/90)
This is newest version of "pcal", the Postscript calendar program. This version completely replaces both Version 2.3 and the patch to 2.4 recently posted to "alt.sources". While I have included the functionality provided by the 2.4 patch in this version of "pcal", I have done so in a different way. In this version, "pcal" supports a feature allowing the user to select which day is displyed in the left column, but as a command line option rather than as a compile time selection. Use -F<dayname> to make the given day appear as the first day of the week. You may place the -F option in your ".calendar" file. Overkill perhaps, but it was as easy as allowing only Sunday or Monday. I have also done some minor cleanup on the Postscript code and a little reformatting of the calendar. By making the day numbers slightly smaller, you can now fit 9 lines of text into a single day. One other note: several people have sent me e-mail suggesting that "pcal" be released less frequently as changes are made. I'm going to abide by that because keeping up with the many changes used to be a problem for me, too. So if you have an idea for a change you would like to make to "pcal", please send me e-mail first so that I can provide you with the most up-to-date sources before you hack up and old cut. Enjoy. - _ Joe Brownlee, Analysts International Corp. @ AT&T Network Systems /_\ @ / ` 471 E Broad St, Suite 1610, Columbus, Ohio 43215 (614) 860-7461 / \ | \_, E-mail: jbr@cblph.att.com Who pays attention to what _I_ say? "Scotty, we need warp drive in 3 minutes or we're all dead!" --- James T. Kirk ------------------------------- 8< cut here >8 ------------------------------- #!/bin/sh # This is a shell archive (produced by shar 3.49) # To extract the files from this archive, save it to a file, remove # everything above the "!/bin/sh" line above, and type "sh file_name". # # existing files will NOT be overwritten unless -c is specified # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 433 -rw-r--r-- Makefile # 1734 -rw-r--r-- ReadMe # 984 -rw-r--r-- ReadMe.orig # 1464 -rw-r--r-- calendar # 397 -rw-r--r-- make_pcal.com # 46687 -rw-r--r-- pcal.c # 8035 -rw-r--r-- pcal.hlp # 7393 -rw-r--r-- pcal.man # 1792 -rw-r--r-- pcalinit.c # 11138 -rw-r--r-- pcalinit.ps # # ============= Makefile ============== if test -f 'Makefile' -a X"$1" != X"-c"; then echo 'x - skipping Makefile (File already exists)' else echo 'x - extracting Makefile (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Makefile' && # Set the configuration variables below to taste. X CC = /bin/cc MANDIR = /usr1/jad/man X pcal: pcal.c pcalinit.h X $(CC) $(CFLAGS) $(LDFLAGS) $(COPTS) -o pcal pcal.c X @ echo "+ Compile of pcal complete!" X pcalinit: pcalinit.c X $(CC) $(CFLAGS) $(LDFLAGS) $(COPTS) -o pcalinit pcalinit.c X pcalinit.h: pcalinit pcalinit.ps X pcalinit pcalinit.ps pcalinit.h X man: pcal.man X nroff -man pcal.man > pcal.1 X pack pcal.1 # mv pcal.1.z $(MANDIR) SHAR_EOF chmod 0644 Makefile || echo 'restore of Makefile failed' Wc_c="`wc -c < 'Makefile'`" test 433 -eq "$Wc_c" || echo 'Makefile: original size 433, current size' "$Wc_c" fi # ============= ReadMe ============== if test -f 'ReadMe' -a X"$1" != X"-c"; then echo 'x - skipping ReadMe (File already exists)' else echo 'x - extracting ReadMe (Text)' sed 's/^X//' << 'SHAR_EOF' > 'ReadMe' && "pcal" Version 2.5 X This is newest version of "pcal", the Postscript calendar program. This version completely replaces both Version 2.3 and the patch to 2.4 recently posted to "alt.sources". While I have included the functionality provided by the 2.4 patch in this version of "pcal", I have done so in a different way. X In this version, "pcal" supports a feature allowing the user to select which day is displyed in the left column, but as a command line option rather than as a compile time selection. Use -F<dayname> to make the given day appear as the first day of the week. You may place the -F option in your ".calendar" file. Overkill perhaps, but it was as easy as allowing only Sunday or Monday. X I have also done some minor cleanup on the Postscript code and a little reformatting of the calendar. By making the day numbers slightly smaller, you can now fit 9 lines of text into a single day. X One other note: several people have sent me e-mail suggesting that "pcal" be released less frequently as changes are made. I'm going to abide by that because keeping up with the many changes used to be a problem for me, too. So if you have an idea for a change you would like to make to "pcal", please send me e-mail first so that I can provide you with the most up-to-date sources before you hack up and old cut. X The accompanying file called "ReadMe.orig" came with the original distribution as README and states this program is copyrighted but with permission to modify and redistribute. X Joe Brownlee jbr@cblph.att.com X Additional note: This distribution includes a VMS HELP file written by Richard Dyson. Countless other people worked on pcal long before me; see the ReadMe.orig file and topline comments in pcal.c. SHAR_EOF chmod 0644 ReadMe || echo 'restore of ReadMe failed' Wc_c="`wc -c < 'ReadMe'`" test 1734 -eq "$Wc_c" || echo 'ReadMe: original size 1734, current size' "$Wc_c" fi # ============= ReadMe.orig ============== if test -f 'ReadMe.orig' -a X"$1" != X"-c"; then echo 'x - skipping ReadMe.orig (File already exists)' else echo 'x - extracting ReadMe.orig (Text)' sed 's/^X//' << 'SHAR_EOF' > 'ReadMe.orig' && "Pcal" is a program to print PostScript calendars for any month and year. By default, it looks for a file in the home directory named "calendar" for entries with leading dates matching dates on the calendar, and prints any following text under the appropriate day. X The program may be a little System V flavored (getopt, time routines) but should be easily portable to other vintages of UNIX. X Pcal is the combined effort of several people, most notably Patrick Wood of Pipeline Associates, Inc. for the original PostScript code and Bill Vogel of AT&T for the calendar file mechanism. My part was simple translation to a "C" program, the addition of a couple options and a more generalized date searching routine (oh yes, and a manual page :-). X The original calendar PostScript was Copyright (c) 1987 by Patrick Wood and Pipeline Associates, Inc. with permission to modify and redistribute. Please retain this README file with the package. X X Ken Keirnan Pacific Bell San Ramon, CA. SHAR_EOF chmod 0644 ReadMe.orig || echo 'restore of ReadMe.orig failed' Wc_c="`wc -c < 'ReadMe.orig'`" test 984 -eq "$Wc_c" || echo 'ReadMe.orig: original size 984, current size' "$Wc_c" fi # ============= calendar ============== if test -f 'calendar' -a X"$1" != X"-c"; then echo 'x - skipping calendar (File already exists)' else echo 'x - extracting calendar (Text)' sed 's/^X//' << 'SHAR_EOF' > 'calendar' && # Sample calendar file for pcal # # This should be ~/.calendar on Unix, SYS$LOGIN:CALENDAR.DAT on VMS # # Valid entries are of the following forms: # # opt <options> # year <year> # <month_name> <day>{*} {<text>} # <month><sep><day>{<sep><year>}{*} {<text>} # note <month> {<text>} # note <month_name> {<text>} # # where: # <options> := one or more valid command-line options (except -e and -f) # <month_name> := first 3+ characters of name of month (in English) # <sep> := one or more non-numeric, non-space, non-'*' characters # <text> is the text to be printed in the calendar box # <day>, <month>, and <year> are appropriate integers # # whitespace is to be used/avoided as implied by the above productions # '*' flags the date as a holiday (to be printed in gray) # comments run from '#' through end-of-line X # A sample "opt" line to change the fonts and output file names, to print # only Sundays in gray, and to print moons on all days: # #opt -d Helvetica-Bold -t Helvetica-Bold -o myfile.ps -b all -g sun -M X year 1990 # set year explicitly X 5/28* Memorial Day (observed) # '*' prints holiday in gray 5/31 Memorial Day X 7/4/90* Independence Day # full date format X Sep 3* Labor Day # month written out X 10/8* Columbus Day (observed) 10/12 Columbus Day X 11/22* Thanksgiving 11/23* # holiday without text X 12/24* # Christmas Eve 12/25* Christmas X note Dec Some consider Christmas Eve a holiday X 1/1/91* New Year's Day # set new year implicitly SHAR_EOF chmod 0644 calendar || echo 'restore of calendar failed' Wc_c="`wc -c < 'calendar'`" test 1464 -eq "$Wc_c" || echo 'calendar: original size 1464, current size' "$Wc_c" fi # ============= make_pcal.com ============== if test -f 'make_pcal.com' -a X"$1" != X"-c"; then echo 'x - skipping make_pcal.com (File already exists)' else echo 'x - extracting make_pcal.com (Text)' sed 's/^X//' << 'SHAR_EOF' > 'make_pcal.com' && $! make_pcal.com - VMS command script to build/run pcalinit, create pcalinit.h $! from pcalinit.ps, and compile/link pcalinit.c $ $ cc pcalinit.c $ link pcalinit $ pcalinit = "$" + f$environment("DEFAULT") + "pcalinit" $ pcalinit pcalinit.ps pcalinit.h $ delete/exclude=(*.c,*.h,*.ps) pcalinit.*;* $ $ cc pcal.c $ link pcal.obj $ pcal == "$" + f$environment("DEFAULT") + "pcal" $ show symbol pcal SHAR_EOF chmod 0644 make_pcal.com || echo 'restore of make_pcal.com failed' Wc_c="`wc -c < 'make_pcal.com'`" test 397 -eq "$Wc_c" || echo 'make_pcal.com: original size 397, current size' "$Wc_c" fi # ============= pcal.c ============== if test -f 'pcal.c' -a X"$1" != X"-c"; then echo 'x - skipping pcal.c (File already exists)' else echo 'x - extracting pcal.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'pcal.c' && static char VERISON_STRING[] = "@(#)pcal v2.5 - print Postscript calendars"; /* X * pcal.c - generate PostScript file to print calendar for any month and year X * X * The original PostScript code to generate the calendars was written by X * Patrick Wood (Copyright (c) 1987 by Patrick Wood of Pipeline Associates, X * Inc.), and authorized for modification and redistribution. The calendar X * file inclusion code was originally written in "bs(1)" by Bill Vogel of X * AT&T. Patrick's original PostScript was modified and enhanced several X * times by others whose names have regrettably been lost. This C version X * was originally created by Ken Keirnan of Pacific Bell; additional X * enhancements by Joseph P. Larson, Ed Hand, and Andrew Rogers (who also X * did the VMS port), Mark Kantrowitz, Joe Brownlee. The moon routines were X * originally written by Mark Hanson, and were improved and incorporated into X * this version by Jamie Zawinski. X * X * Revision history: X * X * 2.5 JAB 10/04/90 added -F option X * X * 2.4 --- 10/01/90 * no modifications * X * X * 2.3 jwz 09/18/90 added moon routines X * X * 2.2 AWR 09/17/90 revise logic of parse(); new usage X * message X * X * JAB/AWR 09/14/90 support "note" lines in date file X * X * 2.1 MK/AWR 08/27/90 support -L, -C, -R, -n options; X * print holiday text next to date X * X * AWR 08/24/90 incorporate cpp-like functionality; X * add -D and -U options; save date file X * information in internal data structure; X * look for PCAL_OPTS and PCAL_DIR; look X * for ~/.calendar and ~/calendar X * X * 2.0 AWR 08/08/90 included revision history; replaced -r X * flag with -l and -p; replaced -s and -S X * flags with -b and -g; recognize flags X * set in date file; translate ( and ) in X * text to octal escape sequence; usage() X * message condensed to fit 24x80 screen X * X * Parameters: X * X * pcal [opts] generate calendar for current month/year X * X * pcal [opts] yy generate calendar for entire year yy X * X * pcal [opts] mm yy generate calendar for month mm X * (Jan = 1), year yy (19yy if yy < 100) X * X * pcal [opts] mm yy n as above, for n consecutive months X * X * Output: X * X * PostScript file to print calendars for all selected months. X * X * Options: X * X * -b <DAY> print specified weekday in black X * -g <DAY> print specified weekday in gray X * (default: print Saturdays and Sundays in gray) X * X * -d <FONT> specify alternate font for day names X * (default: Times-Bold) X * X * -n <FONT> specify alternate font for notes in boxes X * (default: Helvetica-Narrow) X * X * -t <FONT> specify alternate font for titles X * (default: Times-Bold) X * X * -D <SYM> define preprocessor symbol X * -U <SYM> un-define preprocessor symbol X * X * -e generate empty calendar (ignore date file) X * X * -f <FILE> specify alternate date file (default: X * ~/.calendar on Un*x, SYS$LOGIN:CALENDAR.DAT X * on VMS; if environment variable [logical X * name on VMS] PCAL_DIR exists, looks there X * instead) X * X * -o <FILE> specify alternate output file (default: X * stdout on Un*x, CALENDAR.PS on VMS) X * X * -L <STRING> specify left foot string (default: "") X * -C <STRING> specify center foot string (default: "") X * -R <STRING> specify right foot string (default: "") X * X * -l generate landscape-mode calendars X * -p generate portrait-mode calendars X * (default: landscape-mode) X * X * -m draw a small moon icon on the days of the X * full, new, and half moons. X * -M draw a small moon icon every day. X * (default: no moons). X * X * -F <DAY> select any day to be displayed as the first X * day of the weel X * X * There are many ways to specify these options in addition to using the X * command line; this facilitates customization to the user's needs. X * X * If the environment variable (global symbol on VMS) PCAL_OPTS is X * present, its value will be parsed as if it were a command line. X * Any options specified will override the program defaults. X * X * All but the -e, -f, -D, and -U options may be specified in the date X * file by the inclusion of one or more lines of the form "opt <options>". X * Any such options override any previous values set either as program X * defaults, via PCAL_OPTS, or in previous "opt" lines. X * X * Options explicitly specified on the command line in turn override all X * of the above. X * X * Any flag which normally takes an argument may also be specified without X * an argument; this resets the corresponding option to its default. -D X * alone un-defines all symbols; -U alone has no effect. X * X * Parameters and flags may be mixed on the command line. In some cases X * (e.g., when a parameter follows a flag without its optional argument) X * this may lead to ambiguity; the dummy flag '-' (or '--') may be used X * to separate them, i.e. "pcal -t - 9 90". X * X * Simple cpp-like functionality is provided. The date file may include X * the following commands, which work like their cpp counterparts: X * X * define <sym> X * undef <sym> X * X * if{n}def <sym> X * ... X * { else X * ... } X * endif X * X * include <file> X * X * Note that these do not start with '#', which is reserved as a comment X * character. X * X * "define" alone deletes all the current definitions; "ifdef" alone is X * always false; "ifndef" alone is always true. All defined symbols are X * treated in a case-insensitive manner. X * X * The file name in the "include" directive may optionally be surrounded X * by "" or <>. X * X * Additional notes may be propagated to an empty calendar box by the X * inclusion of one or more lines of the form "note <month> <text>", X * where <month> may be numeric or alphabetic. X * X */ X X #include <stdio.h> #include <ctype.h> #include <time.h> #include <string.h> X #ifdef VMS /* VMS oddities isolated here */ X #include <ssdef.h> /* required for trnlog() */ #include <descrip.h> X #define HOME_DIR "SYS$LOGIN" #define DATEFILE "calendar.dat" #define OUTFILE "calendar.ps" #define START_PATH '[' #define END_PATH ']' X #define EXIT_SUCCESS 1 #define EXIT_FAILURE 3 X #else /* non-VMS - assume Un*x of some sort */ X #define HOME_DIR "HOME" #define DATEFILE ".calendar" #define ALT_DATEFILE "calendar" /* for backward compatibility */ #define OUTFILE "" #define START_PATH '/' #define END_PATH '/' X #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1 X #endif X #define PCAL_OPTS "PCAL_OPTS" /* environment variables */ #define PCAL_DIR "PCAL_DIR" X #define IS_LEAP(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) #define INIT_COLORS memcpy(color, default_color, sizeof(color)) #define LASTCHAR(p) ((p) && *(p) ? (p) + strlen(p) - 1 : NULL) X #ifdef __STDC__ #define TOLOWER(c) tolower(c) #else #define TOLOWER(c) (isupper(c) ? tolower(c) : (c)) #endif X #define PRT (void)printf #define FPR (void)fprintf X #define FALSE 0 #define TRUE 1 X #define ALL_FLAGS "bCDdefgLlMmnopRtUF" /* all command-line flags */ #define DATEFILE_FLAGS "DefU" /* parsed before opening datefile */ #define OTHER_FLAGS "bCdgLlMmnopRtF" /* parsed inside datefile */ X #define DAYFONT "Times-Bold" /* default font names */ #define TITLEFONT "Times-Bold" #define NOTESFONT "Helvetica-Narrow" X #define LFOOT "" /* default foot strings */ #define CFOOT "" #define RFOOT "" X #define LANDSCAPE 90 /* degrees to rotate for landscape/portrait */ #define PORTRAIT 0 #define ROTATE LANDSCAPE /* default */ X #define BLACK 0 /* colors for dates */ #define GRAY 1 X #define NO_DATEFILE 0 /* date file (if any) to use */ #define USER_DATEFILE 1 #define SYS_DATEFILE 2 X /* preprocessor token codes - must be contiguous range of integers starting X * at 0 and ending with code for non-tokens (cf. pp_info[], pp_token()) X */ #define PP_DEFINE 0 #define PP_ELSE 1 #define PP_ENDIF 2 #define PP_IFDEF 3 #define PP_IFNDEF 4 #define PP_INCLUDE 5 #define PP_UNDEF 6 #define PP_OTHER 7 /* not pp token */ X #define MAX_NESTING 10 /* maximum nesting level for file inclusion */ X #define MAX_PP_SYMS 100 /* number of definable preprocessor symbols */ #define PP_SYM_UNDEF -1 /* flag for undefined symbol */ X #define MIN_YR 1900 /* significant years (calendar limits) */ #define MAX_YR 9999 X #define JAN 1 /* significant months */ #define FEB 2 #define DEC 12 X #define DAY_TEXT 0 /* types of text in data structure */ #define HOLIDAY_TEXT 1 #define NOTE_TEXT 2 X #define NOTE_DAY 32 /* dummy day for notes text */ X #define DF_YEAR 0 /* returns from date_type() */ #define DF_OPT 1 #define DF_NOTE 2 #define DF_MONTH 3 #define DF_DATE 4 #define DF_OTHER 5 X #define PARSE_OK 0 /* returns from parse(), enter_day_info() */ #define PARSE_INVDATE 1 #define PARSE_INVLINE 2 X #define STRSIZ 200 /* size of misc. strings */ X #define MAXARGS 3 /* numeric command-line args */ X #define WHITESPACE " \t" /* token delimiters in date file */ X /* X * Global typedef declarations for data structure X */ X typedef struct d_i { X int is_holiday; X char *text; X struct d_i *next; X } day_info; X typedef struct m_i { X unsigned long holidays; X day_info *day[32]; /* including NOTE_DAY */ X } month_info; X typedef struct y_i { X int year; X month_info *month[12]; X struct y_i *next; X } year_info; X X /* X * Global variables: X */ X int do_define(), do_ifdef(), do_ifndef(), do_include(), do_undef(); char *trnlog(), *mk_path(), *mk_filespec(); X extern char *getenv(); X year_info *head = NULL; /* head of internal data structure */ int nesting_level = 0; /* level of include file nesting */ int init_month; /* initial month, year, number of months */ int init_year; int nmonths; int curr_year; /* current default year for date file entries */ char *words[100]; /* maximum number of words per date file line */ char lbuf[512]; /* maximum date file line size */ char *pp_sym[MAX_PP_SYMS]; /* preprocessor defined symbols */ char progname[STRSIZ]; /* program name (for error messages) */ char color[7]; /* colors of weekdays - cf. default_color[] */ int first_day_of_week = 0; /* first day of week; 0 = Sun, 1 = Mon, etc. */ X /* X * Default values for command-line options: X */ X char default_color[7] = { /* -b, -g */ X GRAY, BLACK, BLACK, BLACK, BLACK, BLACK, GRAY /* cf. COLOR_MSG */ X }; X int datefile_type = SYS_DATEFILE; /* -e, -f */ char datefile[STRSIZ] = ""; char default_dir[STRSIZ] = ""; X int rotate = ROTATE; /* -l, -p */ X char *draw_moons = "false"; /* -m, -M */ X char dayfont[STRSIZ] = DAYFONT; /* -d, -t, -n */ char titlefont[STRSIZ] = TITLEFONT; char notesfont[STRSIZ] = NOTESFONT; X char lfoot[STRSIZ] = LFOOT; /* -L, -C, -R */ char cfoot[STRSIZ] = CFOOT; char rfoot[STRSIZ] = RFOOT; X char outfile[STRSIZ] = OUTFILE; /* -o */ X /* X * Language-dependent strings (month and day names, option file keywords, X * preprocessor tokens): X */ X static char *months[12] = { X "January", "February", "March", "April", "May", "June", X "July", "August", "September", "October", "November", "December" X }; X static char *days[7] = { X "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", X "Saturday" X }; X /* preprocessor tokens - must be in same order as PP_XXXXX (cf. pp_token()) */ static struct pp { X char *token; /* name */ X int (*pfcn)(); /* dispatch routine */ X } pp_info[] = { X { "define", do_define }, /* PP_DEFINE */ X { "else", NULL }, /* PP_ELSE */ X { "endif", NULL }, /* PP_ENDIF */ X { "ifdef", do_ifdef }, /* PP_IFDEF */ X { "ifndef", do_ifndef }, /* PP_IFNDEF */ X { "include", do_include }, /* PP_INCLUDE */ X { "undef", do_undef }, /* PP_UNDEF */ X { NULL, NULL } /* PP_OTHER */ X }; X #define MIN_DAY_LEN 2 /* minimum size of abbreviations */ #define MIN_MONTH_LEN 3 #define MIN_PPTOK_LEN 3 X #define ALL "all" /* command-line or date file keywords */ #define NOTE "note" #define OPT "opt" #define YEAR "year" X #define COLOR_MSG "Sat/Sun in gray, others in black" /* cf. usage() */ X /* X * PostScript boilerplate X */ X #include "pcalinit.h" X X /* X * Main program - parse and validate command-line arguments, open files, X * generate PostScript boilerplate and code to generate calendars. X * X * Program structure: X * X * main() looks for the environment variable (global symbol on VMS) PCAL_OPTS X * and calles get_args() to parse it. It then calls get_args() again to parse X * the command line for the date file name, -D and -U options to be in effect X * prior to reading the date file, and any numeric arguments (month, year, X * number of months). It then calls read_datefile() to read and parse the X * date file; any "opt" lines present will override the defaults for the X * command-line flags. It then calls get_args() again to process the other X * command-line flags, which in turn override any specified earlier. X * X * main() then generates the common PostScript code and then calls pmonth() to X * print the calendars. X * X * read_datefile() calls getline() to read the date file, do_xxxxx() to process X * the preprocessor tokens, and parse() to parse each date line. X * X * getline() reads one or more lines from the date file, stripping comments X * (# through end-of-line) and ignoring blank lines. X * X * parse() parses a line from the date file and processes it. If "opt", it X * calls loadwords() to split the line into tokens and get_args() to X * process them. If the line contains "note" or a date, it calls X * enter_day_info() to enter the day and related text into the data structure. X * X * pmonth() calls find_holidays() to generate the list of holidays to be X * printed in gray; it then calls find_daytext() to generate the text to X * be printed inside the calendar boxes. X * X */ main(argc, argv) X int argc; X char **argv; { X FILE *dfp = NULL; /* date file pointer */ X char *p, **ap; X int i, month, year, ngray; X #define DO_HEADER(phdr) for (ap = phdr; *ap; ap++) PRT("%s\n", *ap) X X INIT_COLORS; /* set up default colors */ X X /* isolate root program name (for use in error messages) */ X X strcpy(progname, **argv ? *argv : "pcal"); X X if ((p = strrchr(progname, END_PATH)) != NULL) X strcpy(progname, ++p); X if ((p = strchr(progname, '.')) != NULL) X *p = '\0'; X X /* X * Get the arguments from a) the environment variable, b) "opt" lines X * in the date file, and c) the command line, in that order X */ X X /* look for environment variable for options */ X X if ((p = getenv(PCAL_OPTS)) != NULL) { X strcpy(lbuf, "x "); /* prepend a dummy token */ X strcat(lbuf, p); X loadwords(); X if (! get_args(words, ALL_FLAGS, FALSE)) { X usage(); X exit(EXIT_FAILURE); X } X } X X /* parse command-line arguments once to find name of date file, etc. */ X X if (!get_args(argv, DATEFILE_FLAGS, TRUE)) { X usage(); X exit(EXIT_FAILURE); X } X X /* Attempt to open the date file as specified by the [-e | -f] flags */ X X switch (datefile_type) { X case NO_DATEFILE: X dfp = NULL; X break; X X case USER_DATEFILE: X /* Attempt to open user-specified calendar file */ X if ((dfp = fopen(datefile, "r")) == NULL) { X FPR(stderr, "%s: can't open file %s\n", progname, X datefile); X exit(EXIT_FAILURE); X } X mk_path(default_dir, datefile); /* extract path */ X break; X X case SYS_DATEFILE: X /* Attempt to open system-specified calendar file */ X if ((p = trnlog(PCAL_DIR)) || (p = trnlog(HOME_DIR))) X strcpy(default_dir, p); X X mk_filespec(datefile, default_dir, DATEFILE); X dfp = fopen(datefile, "r"); /* no error if nonexistent */ #ifdef ALT_DATEFILE X if (!dfp) { /* try again with alternate file */ X mk_filespec(datefile, default_dir, ALT_DATEFILE); X dfp = fopen(datefile, "r"); X } #endif X break; X } X X /* read the date file (if any) and build internal data structure */ X X if (dfp) { X curr_year = init_year; X read_datefile(dfp, datefile); X fclose(dfp); X } X X /* reparse command line - flags there supersede those in date file */ X X get_args(argv, OTHER_FLAGS, FALSE); X X /* reshuffle days depending on first day of week selected by user */ X X set_first_day(first_day_of_week); X X /* done with the arguments and flags - try to open the output file */ X X if (*outfile && freopen(outfile, "w", stdout) == (FILE *) NULL) { X FPR(stderr, "%s: can't open file %s\n", progname, outfile); X exit(EXIT_FAILURE); X } X X /* X * Write out PostScript prolog X */ X X /* font names */ X X PRT("%%!\n"); X PRT("/titlefont /%s def\n/dayfont /%s def\n/notesfont /%s def\n", titlefont, dayfont, notesfont); X X /* set offset from Sunday to selected first day of week */ X X PRT("/dayoffset %d def\n", first_day_of_week); X X /* foot strings */ X X def_footstring(lfoot, 'L'); X def_footstring(cfoot, 'C'); X def_footstring(rfoot, 'R'); X X /* month names */ X X PRT("/month_names ["); X for (i = 0; i < 12; i++) X PRT("%s(%s) ", i % 6 == 0 ? "\n\t" : "", months[i]); X PRT("] def\n"); X X /* day names */ X X PRT("/day_names ["); X for (i = 0; i < 7; i++) X PRT("%s(%s) ", i % 6 == 0 ? "\n\t" : "", days[i]); X PRT("] def\n"); X X /* colors (black/gray) to print weekdays and holidays */ X X PRT("/day_gray ["); X for (ngray = i = 0; i < 7; ngray += color[i++] == GRAY) X PRT(" %s", color[i] == GRAY ? "true" : "false"); X PRT(" ] def\n"); X PRT("/holiday_gray %s def\n", ngray <= 3 ? "true" : "false"); X X /* PostScript boilerplate (part 1 of 1) */ X DO_HEADER(header); X X /* landscape or portrait mode */ X X PRT("\n/landscape-p %s def\n", (rotate == LANDSCAPE) ? "true":"false"); X PRT("/draw-moons %s def\n", draw_moons); X X /* X * Write out PostScript code to print calendars X */ X X month = init_month; X year = init_year; X X while (nmonths--) { X pmonth(month, year); X if (++month > DEC) { X month = JAN; X year++; X } X } X X cleanup(); X #ifdef VMS X FPR(stderr, "Output is in file %s\n", outfile); #endif X exit(EXIT_SUCCESS); } X /* X * get_args - walk the argument list, parsing all arguments but processing only X * those specified in "flags". If "do_numargs" is TRUE, processes numeric X * arguments (month, year, number of months) as well. X */ int get_args(argv, flags, do_numargs) X char **argv; /* argument list */ X char *flags; /* which flags to process */ X int do_numargs; /* process numeric arguments? */ { X char *p, *opt; X int i, do_flag; X long tmp; /* for getting current month/year */ X struct tm *p_tm; X int badopt = FALSE; /* flag set if bad param */ X int nargs = 0; /* count of non-flag args */ X int numargs[MAXARGS]; /* non-flag (numeric) args */ X /* Look for the argument following flag - may be separated by spaces or X * not (bumps argv in former case). If no non-flag argument appears, set X * "arg" to NULL (-b, -C, -d, -g, -L, -n, -o, -R, and -t without an argument X * reset the corresponding option to its default value). X */ #define GETARG(arg) arg = *(*argv + 2) ? *argv + 2 : \ X (*(argv+1) && **(argv+1) != '-' ? *++argv : NULL) X X /* Walk argument list, ignoring first element (program name) */ X X while (*++argv) { X X /* Assume that any non-flag argument is a numeric argument */ X if (**argv != '-') { X if (do_numargs && nargs < MAXARGS) X numargs[nargs++] = atoi(*argv); X continue; X } X X /* Is this flag among those to be processed beyond parsing? */ X X do_flag = strchr(flags, *(opt = *argv + 1)) != NULL; X X switch (*opt) { X X case '\0': /* take - or -- as dummy flags */ X case '-' : X break; X X case 'b': /* print day in black or gray */ X case 'g': X GETARG(p); X if (do_flag) X if (p) X set_color(p, *opt == 'b' ? BLACK : GRAY); X else X INIT_COLORS; /* reset to defaults */ X break; X X case 'C': /* specify alternate center foot */ X GETARG(p); X if (do_flag) X strcpy(cfoot, p ? p : CFOOT); X break; X X case 'd': /* specify alternate day font */ X GETARG(p); X if (do_flag) X strcpy(dayfont, p ? p : DAYFONT); X break; X X case 'D': /* define preprocessor symbol */ X GETARG(p); X if (do_flag) X do_define(p); X break; X X case 'e': /* generate empty calendar */ X if (do_flag) { X datefile_type = NO_DATEFILE; X datefile[0] = '\0'; X } X break; X X case 'f': /* specify alternate date file */ X GETARG(p); X if (p && do_flag) { X datefile_type = USER_DATEFILE; X strcpy(datefile, p); X } X break; X X case 'L': /* specify alternate left foot */ X GETARG(p); X if (do_flag) X strcpy(lfoot, p ? p : LFOOT); X break; X X case 'l': /* generate landscape calendar */ X if (do_flag) X rotate = LANDSCAPE; X break; X X case 'n': /* specify alternate notes font */ X GETARG(p); X if (do_flag) X strcpy(notesfont, p ? p : NOTESFONT); X break; X X case 'o': /* specify alternate output file */ X GETARG(p); X if (do_flag) X strcpy(outfile, p ? p : OUTFILE); X break; X X case 'p': /* generate portrait calendar */ X if (do_flag) X rotate = PORTRAIT; X break; X X case 'R': /* specify alternate right foot */ X GETARG(p); X if (do_flag) X strcpy(rfoot, p ? p : RFOOT); X break; X X X case 't': /* specify alternate title font */ X GETARG(p); X if (do_flag) X strcpy(titlefont, p ? p : TITLEFONT); X break; X X case 'U': /* undef preprocessor symbol */ X GETARG(p); X if (do_flag) X do_undef(p); X break; X X case 'm': /* draw four moons */ X if (do_flag) X draw_moons = "4"; X break; X X case 'M': /* draw a moon for each day */ X if (do_flag) X draw_moons = "true"; X break; X X case 'F': /* select starting day of week */ X GETARG(p); X if (do_flag) { X if (p) { X for (i = 0; i < 7; i++) X if (ci_strncmp(p, days[i], X MIN_DAY_LEN) == 0) X first_day_of_week = i; X } else X first_day_of_week = 0; /* default */ X } X break; X X default: /* unrecognized flag */ X FPR(stderr, "%s: illegal option -%s\n", progname, opt); X badopt = TRUE; X break; X } X } X X if (!do_numargs) X return !badopt; /* return TRUE if OK, FALSE if error */ X X /* Validate non-flag (numeric) parameters */ X X switch (nargs) { X case 0: /* no arguments - print current month/year */ X time(&tmp); X p_tm = localtime(&tmp); X init_month = p_tm->tm_mon + 1; X init_year = p_tm->tm_year; X nmonths = 1; X break; X case 1: /* one argument - print entire year */ X init_month = JAN; X init_year = numargs[0]; X nmonths = 12; X break; X default: /* two or three arguments - print one or more months */ X init_month = numargs[0]; X init_year = numargs[1]; X nmonths = nargs > 2 ? numargs[2] : 1; X break; X } X X if (nmonths < 1) /* ensure at least one month */ X nmonths = 1; X X /* check range of month and year */ X X if (init_month < JAN || init_month > DEC) { X FPR(stderr, "%s: month %d not in range 1 .. 12\n", progname, X init_month); X badopt = TRUE; X } X X if (init_year > 0 && init_year < 100) /* treat nn as 19nn */ X init_year += 1900; X X if (init_year < MIN_YR || init_year > MAX_YR) { X FPR(stderr, "%s year %d not in range %d .. %d\n", progname, X init_year, MIN_YR, MAX_YR); X badopt = TRUE; X } X X return !badopt; /* return TRUE if OK, FALSE if error */ } X X X /* X * usage - print message explaining correct usage of the command-line X * arguments and flags X */ usage() { X FPR(stderr, "\nUsage:\t%s [-b|-g DAY]* [-d|-n|-t FONT] [-e | -f FILE] [-o FILE] [-l | -p]\n", progname); X FPR(stderr, "\t[-m|-M] [-D|-U SYM] [-L|-C|-R STRING] [ [ [mm] yy ] | [mm yy n] ]\n"); X FPR(stderr, "\n"); X FPR(stderr, "\t-b DAY\t\tprint weekday DAY in black\n"); X FPR(stderr, "\t-g DAY\t\tprint weekday DAY in gray\n"); X FPR(stderr, "\t\t\t(default: %s)\n", COLOR_MSG); X FPR(stderr, "\n"); X FPR(stderr, "\t-d FONT\t\tspecify alternate day name font (default: %s)\n", X DAYFONT); X FPR(stderr, "\t-n FONT\t\tspecify alternate notes font (default: %s)\n", X NOTESFONT); X FPR(stderr, "\t-t FONT\t\tspecify alternate title font (default: %s)\n", X TITLEFONT); X FPR(stderr, "\n"); X FPR(stderr, "\t-e\t\tgenerate empty calendar (ignore date file)\n"); X FPR(stderr, "\t-f FILE\t\tspecify alternate date file (default: %s)\n", X DATEFILE); X FPR(stderr, "\t-o FILE\t\tspecify alternate output file (default: %s)\n", X OUTFILE[0] ? OUTFILE : "stdout"); X FPR(stderr, "\n"); X FPR(stderr, "\t-l\t\tgenerate landscape-style calendars"); #if (ROTATE == LANDSCAPE) X FPR(stderr, " (default)"); #endif X FPR(stderr, "\n\t-p\t\tgenerate portrait-style calendars"); #if (ROTATE == PORTRAIT) X FPR(stderr, " (default)"); #endif X FPR(stderr, "\n\n"); X FPR(stderr, "\t-m\t\t\draw a \"moon\" icon on days of full, new, and half moons\n"); #ifdef VMS X FPR(stderr, "\t-\"M\"\t\t\draw a \"moon\" icon every day (default: no moons)\n"); X FPR(stderr, "\n"); X FPR(stderr, "\t-\"D\" SYM\tdefine preprocessor symbol\n"); X FPR(stderr, "\t-\"U\" SYM\tundefine preprocessor symbol\n"); X FPR(stderr, "\n"); X FPR(stderr, "\t-\"L\" STRING\tspecify left foot string (default: \"%s\")\n", X LFOOT); X FPR(stderr, "\t-\"C\" STRING\tspecify center foot string (default: \"%s\")\n", X CFOOT); X FPR(stderr, "\t-\"R\" STRING\tspecify right foot string (default: \"%s\")\n", X RFOOT); #else X FPR(stderr, "\t-M\t\t\draw a \"moon\" icon every day (default: no moons)\n"); X FPR(stderr, "\n"); X FPR(stderr, "\t-D SYM\t\tdefine preprocessor symbol\n"); X FPR(stderr, "\t-U SYM\t\tundefine preprocessor symbol\n"); X FPR(stderr, "\n"); X FPR(stderr, "\t-L STRING\tspecify left foot string (default: \"%s\")\n", X LFOOT); X FPR(stderr, "\t-C STRING\tspecify center foot string (default: \"%s\")\n", X CFOOT); X FPR(stderr, "\t-R STRING\tspecify right foot string (default: \"%s\")\n", X RFOOT); #endif X FPR(stderr, "\n"); X FPR(stderr, "\tyy\t\tgenerate calendar for year yy (19yy if yy < 100)\n"); X FPR(stderr, "\tmm yy\t\tgenerate calendar for month mm (Jan = 1), year yy\n"); X FPR(stderr, "\tmm yy n\t\tgenerate calendars for n months, starting at mm/yy\n"); X FPR(stderr, "\t(default)\tgenerate calendar for current month/year\n"); } X X /* X * General-purpose utility routines X */ X X /* X * alloc - interface to calloc(); terminates if unsuccessful X */ char *alloc(size) X int size; { X char *p; X extern char *calloc(); X X if (size == 0) /* not all calloc()s like null requests */ X size = 1; X X if ((p = calloc(1, size)) == NULL) { X FPR(stderr, "%s: out of memory\n", progname); X exit(EXIT_FAILURE); X } X X return p; } X X /* X * ci_str{n}cmp - case-insensitive flavors of strcmp(), strncmp() X */ int ci_strcmp(s1, s2) register char *s1, *s2; { X register char c1, c2; X X for ( ; (c1 = TOLOWER(*s1)) == (c2 = TOLOWER(*s2)); s1++, s2++) X if (c1 == '\0') X return 0; X X return c1 - c2; } X X int ci_strncmp(s1, s2, n) register char *s1, *s2; int n; { X register char c1, c2; X X for ( ; --n >= 0 && (c1 = TOLOWER(*s1)) == (c2 = TOLOWER(*s2)); s1++, s2++) X if (c1 == '\0') X return 0; X X return n < 0 ? 0 : c1 - c2; } X X /* X * Preprocessor token and symbol table routines X */ X X /* X * find_sym - look up symbol; return symbol table index if found, PP_SYM_UNDEF X * if not found X */ int find_sym(sym) X char *sym; { X int i; X X if (!sym) X return PP_SYM_UNDEF; X X for (i = 0; i < MAX_PP_SYMS; i++) X if (pp_sym[i] && ci_strcmp(pp_sym[i], sym) == 0) X return i; X X return PP_SYM_UNDEF; } X X /* X * do_ifdef - return TRUE if 'sym' is currently defined; FALSE if not X */ int do_ifdef(sym) X char *sym; { X return find_sym(sym) != PP_SYM_UNDEF; } X X /* X * do_ifndef - return FALSE if 'sym' is currently defined; TRUE if not X */ int do_ifndef(sym) X char *sym; { X return find_sym(sym) == PP_SYM_UNDEF; } X X /* X * do_define - enter 'sym' into symbol table; if 'sym' NULL, clear symbol table X */ do_define(sym) X char *sym; { X int i; X X if (! sym) { /* null argument - clear all definitions */ X clear_syms(); X return; X } X X if (do_ifdef(sym)) /* already defined? */ X return; X X for (i = 0; i < MAX_PP_SYMS; i++) /* find room for it */ X if (! pp_sym[i]) { X strcpy(pp_sym[i] = alloc(strlen(sym)+1), sym); X return; X } X X FPR(stderr, "%s: no room to define %s\n", progname, sym); } X X /* X * do_undef - undefine 'sym' and free its space; no error if not defined X */ do_undef(sym) X char *sym; { X int i; X X if (! sym) X return; X X if ((i = find_sym(sym)) != PP_SYM_UNDEF) { X free(pp_sym[i]); X pp_sym[i] = NULL; X } } X X /* X * do_include - include specified file (optionally in "" or <>) X */ do_include(path, name) X char *path; /* path to file */ X char *name; /* file name */ { X FILE *fp; X char *p, incfile[STRSIZ], tmpnam[STRSIZ]; X X if (! name) /* whoops, no date file */ X return; X X /* copy name, stripping "" or <> */ X strcpy(tmpnam, name + (*name == '"' || *name == '<')); X if ((p = LASTCHAR(tmpnam)) && *p == '"' || *p == '>') X *p = '\0'; X X if ((fp = fopen(mk_filespec(incfile, path, tmpnam), "r")) == NULL) { X FPR(stderr, "%s: can't open file %s\n", progname, incfile); X exit(EXIT_FAILURE); X } X X read_datefile(fp, incfile); X fclose(fp); } X X /* X * pp_token - look up 'token' in list of preprocessor tokens; return its X * index if found, PP_OTHER if not (N.B.: this relies on the ordering of X * PP_XXXXX and pp_info[]; see comments at their definitions). X */ int pp_token(token) X char *token; { X struct pp *p; X X for (p = pp_info; X p->token && ci_strncmp(token, p->token, MIN_PPTOK_LEN); X p++) X ; X X return p - pp_info; } X /* X * Internal data structure routines X */ X X /* X * find_year - find record in year list; optionally create if not present X */ year_info *find_year(year, insert) /* find record in year list */ X int year; X int insert; /* insert if missing */ { X year_info *pyear, *plast, *p; X X for (plast = NULL, pyear = head; /* search linked list */ X pyear && pyear->year < year; X plast = pyear, pyear = pyear->next) X ; X X if (pyear && pyear->year == year) /* found - return it */ X return pyear; X X if (insert) { /* not found - insert it if requested */ X p = (year_info *) alloc(sizeof(year_info)); /* create new record */ X p->year = year; X X p->next = pyear; /* link it in */ X return *(plast ? &plast->next : &head) = p; X } X else X return NULL; } X X /* X * enter_day_info - enter text for specified day; avoid entering duplicates. X * returns PARSE_INVDATE if date invalid, PARSE_OK if OK X */ int enter_day_info(m, d, y, text_type, pword) /* fill in information for given day */ X int m, d, y; X int text_type; X char **pword; { X static year_info *pyear; X static int prev_year = 0; X month_info *pmonth; X day_info *pday, *plast; X int is_holiday = text_type == HOLIDAY_TEXT; X X if (! is_valid(m, d == NOTE_DAY && text_type == NOTE_TEXT ? 1 : d, y)) X return PARSE_INVDATE; X X if (y != prev_year) /* avoid unnecessary year lookup */ X pyear = find_year(y, 1); X X --m, --d; /* adjust for use as subscripts */ X X if ((pmonth = pyear->month[m]) == NULL) /* find/create month record */ X pyear->month[m] = pmonth = (month_info *) alloc(sizeof(month_info)); X X if (is_holiday) X pmonth->holidays |= (1 << d); X X /* insert text for day at end of list (preserving the order of entry X * for multiple lines on same day); eliminate those differing only X * in spacing and capitalization from existing entries X */ X X get_text(pword); /* consolidate text into lbuf */ X X if (*lbuf) { X for (plast = NULL, pday = pmonth->day[d]; X pday; X plast = pday, pday = pday->next) X if (ci_strcmp(pday->text, lbuf) == 0) { X pday->is_holiday |= is_holiday; X return PARSE_OK; X } X X /* unique - add to end of list */ X X pday = (day_info *) alloc(sizeof(day_info)); X pday->is_holiday = is_holiday; X strcpy(pday->text = (char *) alloc(strlen(lbuf)+1), lbuf); X pday->next = NULL; X *(plast ? &plast->next : &pmonth->day[d]) = pday; X } X X return PARSE_OK; } X X /* X * Housekeeping routines to free allocated data X */ X X /* X * clear_syms - clear and deallocate the symbol table X */ clear_syms() { X int i; X X for (i = 0; i < MAX_PP_SYMS; i++) X if (pp_sym[i]) { X free(pp_sym[i]); X pp_sym[i] = NULL; X } } X X /* X * cleanup - free all allocated data X */ cleanup() { X int i, j; X year_info *py, *pny; X month_info *pm; X day_info *pd, *pnd; X X for (py = head; py; py = pny) { /* main data structure */ X pny = py->next; X for (i = 0; i < 12; i++) { X if ((pm = py->month[i]) == NULL) X continue; X for (j = 0; j < NOTE_DAY; j++) X for (pd = pm->day[j]; pd; pd = pnd) { X pnd = pd->next; X free(pd->text); X free(pd); X } X free(pm); X } X free(py); X } X X clear_syms(); /* symbol table */ X } X X /* X * Utility routines for date and text parsing and entry X */ X /* X * get_month - convert numeric or alpha string to month; return 1-12 if valid, X * 0 if not valid X */ int get_month(cp) X char *cp; { X int mm; X X if (! cp) X return 0; X X if (isdigit(*cp)) X mm = atoi(cp); X else X for (mm = JAN; X mm <= DEC && ci_strncmp(cp, months[mm-1], MIN_MONTH_LEN); X mm++) X ; X X return mm >= JAN && mm <= DEC ? mm : 0; } X X /* X * date_type - examine token and return date type code; if DF_MONTH, fill X * in number of month X */ int date_type(cp, pm) X char *cp; /* pointer to start of token */ X int *pm; /* month value (returned) */ { X int mm; X X if (isdigit(*cp)) X return DF_DATE; X X if (ci_strncmp(cp, YEAR, strlen(YEAR)) == 0) X return DF_YEAR; X X if (ci_strncmp(cp, OPT, strlen(OPT)) == 0) X return DF_OPT; X X if (ci_strncmp(cp, NOTE, strlen(NOTE)) == 0) X return DF_NOTE; X X return (mm = get_month(cp)) != 0 ? (*pm = mm, DF_MONTH) : DF_OTHER; } X X /* X * is_valid - return TRUE if m/d/y is a valid date X */ int is_valid(m, d, y) X register int m, d, y; { X static char len[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; X X return m >= JAN && m <= DEC && X d >= 1 && d <= (len[m] + (m == FEB && IS_LEAP(y))); } X X /* X * loadwords - tokenize line buffer into word array, return word count. X * differs from old loadwords() in that it handles quoted (" or ') strings X */ X int loadwords() { X register char *pstr, *ptok; X char *delim, **ap; X register int i; X X pstr = lbuf; X X for (i = 0, ap = words; TRUE; i++, ap++) { X delim = *pstr == '"' ? "\"" : X *pstr == '\'' ? "'" : X WHITESPACE; X X ptok = pstr += strspn(pstr, delim); /* look for next token */ X X if (! *pstr) { /* end of lbuf? */ X *ap = NULL; /* add null ptr at end */ X return i; /* return count of non-null ptrs */ X } X X if (*ptok == '"' || *ptok == '\'') /* bump past quote */ X ptok++; X X *ap = ptok; /* save token ptr */ X X pstr += strcspn(pstr, delim); /* skip past token */ X X if (*pstr) /* terminate token */ X *pstr++ = '\0'; X } X } X X /* X * get_text - retrieve remaining text in lbuf and transform in place, removing X * leading/trailing whitespace and condensing runs of whitespace to one blank X */ get_text(pword) X char **pword; /* pointer to first desired word in "words" */ { char *pbuf, *p; X /* copy words back to lbuf in place, separating by one blank */ X for (pbuf = lbuf; p = *pword; *pbuf++ = *++pword ? ' ' : '\0') X while (*p) X *pbuf++ = *p++; X if (pbuf == lbuf) X *lbuf = '\0'; } X X /* X * print_text - print tokens in text (assumed separated by single blank) X * in PostScript format; convert '(' or ')' to octal escape X */ print_text(p) X char *p; { X char c; X X PRT("("); X for ( ; c = *p ; p++) { X if (c == ' ') X PRT(")\n("); X else X PRT(c == '(' || c == ')' ? "\\%03o" : "%c", c); X } X PRT(")\n"); } X X /* X * def_footstring - print definition for foot string, again converting '(' X * or ')' to octal escape X */ def_footstring(p, c) X char *p; /* definition */ X char c; /* L, C, or R */ { X X PRT("/%cfootstring (", c); X X while (c = *p++) X PRT(c == '(' || c == ')' ? "\\%03o" : "%c", c); X X PRT(") def\n"); } X X /* X * set_color - set one or all weekdays to print in black or gray X */ set_color(day, col) X char *day; /* weekday name (or "all") */ X int col; /* select black or gray */ { X int i, do_all; X X do_all = ci_strncmp(day, ALL, strlen(ALL)) == 0; /* set all days? */ X X for (i = 0; i < 7; i++) X if (do_all || ci_strncmp(day, days[i], MIN_DAY_LEN) == 0) X color[i] = col; } X /* X * set_first_day - change the first day of the week from Sunday to any day X */ set_first_day(day) X int day; /* day of week number; 0 = Sun, 1 = Mon, etc. */ { X char temp_color[7], *temp_days[7]; X int i, from_index; X X if (day == 0) /* if Sunday, no need to change things */ X return; X X /* X * first we save off the data items in the "color" and "days" X * arrays which must be reshuffled. X */ X X for (i = 0; i < 7; i++) { X temp_color[i] = color[i]; X temp_days[i] = days[i]; X } X X /* X * now we reshuffle the copies back into the originals after X * offsetting by the apprpriate number of days X */ X X for (i = 0; i < 7; i++) { X from_index = (i + day) % 7; X color[i] = temp_color[from_index]; X days[i] = temp_days[from_index]; X } } X /* X * parse - enter date file data (in lbuf[]) into data structure X * X * Looks for an entry of one of the following forms: X * X * year <year> X * <month_name> <day>{*} {<text>} X * <month><sep><day>{<sep><year>}{*} {<text>} X * opt <options> X * note <month_name> <text> X * note <month> <text> X * X * where X * <month_name> := first 3+ characters of name of month (in English) X * <sep> := one or more non-numeric, non-space, non-'*' characters X * <options> := any command-line option except -e, -f, -D, -U X * X * Enters information for year, month, and day into data structure for X * subsequent retrieval. Lines of form "opt <options>" override any X * defaults. X * X * N.B.: "inc" and other cpp-like lines are handled in read_datefile(). X * X */ int parse() { X register char *cp; X char **pword, *p; X int mm, dd, yy; X int text_type; X /* macro to skip numeric field */ X #define SKIP_FIELD(p) \ X if (1) {while (isdigit(*p)) p++; while (*p && !isdigit(*p)) p++;} else X X /* X * Get first field - can be either "year", "opt", "note", a month X * name, or a (complete) numeric date spec X */ X X cp = *(pword = words); X X switch (date_type(cp, &mm)) { X X case DF_YEAR: X if ((cp = *++pword) != NULL && (yy = atoi(cp)) > 0) { X if (yy < 100) X yy += 1900; X curr_year = yy; X return PARSE_OK; X } X return PARSE_INVDATE; /* year missing or invalid */ X break; X X case DF_OPT: X if (!get_args(words, OTHER_FLAGS, FALSE)) { X usage(); X exit(EXIT_FAILURE); X } X return PARSE_OK; X break; X X case DF_NOTE: X mm = get_month(*++pword); X return enter_day_info(mm, NOTE_DAY, curr_year, NOTE_TEXT, X ++pword); X break; X X case DF_MONTH: X if ((cp = *++pword) == NULL || (dd = atoi(cp)) == 0) X return PARSE_INVDATE; X text_type = cp[strlen(cp) - 1] == '*' ? HOLIDAY_TEXT : DAY_TEXT; X X return enter_day_info(mm, dd, curr_year, text_type, ++pword); X break; X X case DF_DATE: X text_type = cp[strlen(cp) - 1] == '*' ? HOLIDAY_TEXT : DAY_TEXT; X X /* extract month and day fields */ X X mm = atoi(cp); X SKIP_FIELD(cp); X X dd = atoi(cp); X SKIP_FIELD(cp); X X /* Numeric dates may (or may not) have a year */ X X if ((yy = atoi(cp)) > 0) { X if (yy < 100) X yy += 1900; X curr_year = yy; /* if present, reset current year */ X } X X return enter_day_info(mm, dd, curr_year, text_type, ++pword); X break; X X case DF_OTHER: X return PARSE_INVLINE; /* line not recognized */ X break; X } } X X /* X * getline - read next non-null line of input file into lbuf; return 0 on EOF X */ int getline(dfp, pline) X FILE *dfp; X int *pline; { X register char *cp; X register int c; X int in_comment; /* comments: from '#' to end-of-line */ X X cp = lbuf; X do { X in_comment = FALSE; X while ((c = getc(dfp)) != '\n' && c != EOF) { X if (c == '#') X in_comment = TRUE; X X /* ignore comments and leading white space */ X if (in_comment || X (cp == lbuf && (c == ' ' || c == '\t'))) X continue; X *cp++ = c; X } X if (c == EOF) X return FALSE; X X (*pline)++; /* bump line number */ X X } while (cp == lbuf); /* ignore empty lines */ X X *cp = '\0'; X return TRUE; } X X /* X * read_datefile - read and parse date file, handling preprocessor lines X */ read_datefile(fp, filename) X FILE *fp; /* file pointer (assumed open) */ X char *filename; /* file name (for error messages) */ { X static int file_level = 0; X int if_level = 0; X int restart_level = 0; X int line = 0; X int processing = TRUE; X X int pptype, extra, ntokens, save_year; X int (*pfcn)(); X char *ptok; X char **pword; X char msg[STRSIZ], incpath[STRSIZ]; X #define ERR(errmsg) FPR(stderr, "%s: %s in file %s, line %d\n", \ X progname, errmsg, filename, line); X X if (fp == NULL) /* whoops, no date file */ X return; X X if (++file_level > MAX_NESTING) { X ERR("maximum file nesting level exceeded"); X exit(EXIT_FAILURE); X } X X save_year = curr_year; /* save default year */ X X /* read lines until EOF */ X X while (getline(fp, &line)) { X X ntokens = loadwords(); /* split line into tokens */ X pword = words; /* point to the first */ X X /* get token type and pointers to function and name */ X X pptype = pp_token(*pword++); X pfcn = pp_info[pptype].pfcn; X ptok = pp_info[pptype].token; X X switch (pptype) { X X case PP_DEFINE: X case PP_UNDEF: X if (processing) X (*pfcn)(*pword); X extra = ntokens > 2; X break; X X case PP_ELSE: X if (if_level < 1) { X ERR("unmatched \"else\""); X break; X } X X if (processing) { X processing = FALSE; /* disable processing */ X restart_level = if_level; X } else if (if_level == restart_level) { X processing = TRUE; /* re-enable processing */ X restart_level = 0; X } X extra = ntokens > 1; X break; X X case PP_ENDIF: X if (if_level < 1) { X ERR("unmatched \"end\""); X break; X } X X if (! processing && if_level == restart_level) { X processing = TRUE; /* re-enable processing */ X restart_level = 0; X } X if_level--; X extra = ntokens > 1; X break; X X case PP_IFDEF: X case PP_IFNDEF: X if_level++; X if (processing) { X if (! (*pfcn)(*pword)) { X processing = FALSE; X restart_level = if_level; X } X } X extra = ntokens > 2; X break; X X case PP_INCLUDE: X if (processing) X do_include(mk_path(incpath, filename), *pword); X extra = ntokens > 2; X break; X X case PP_OTHER: /* none of the above - parse as date */ X if (processing) { X switch (parse()) { X X case PARSE_INVDATE: X ERR("invalid date"); X break; X X case PARSE_INVLINE: X ERR("unrecognized line"); X break; X X } X } X extra = FALSE; X break; X X } /* end switch */ X X if (extra) { /* extraneous data? */ X sprintf(msg, "extraneous data on \"%s\" line", ptok); X ERR(msg); X } X X } /* end while */ X X if (if_level > 0) X FPR(stderr, "%s: unterminated if{n}def..{else..}endif in %s\n", X progname, filename); X X file_level--; X curr_year = save_year; /* restore default year */ } X /* X * Routines to extract and print data X */ X X /* X * Browse through the data structure looking for day, holiday, or notes text X * in the specified month/year X */ find_daytext(month, year, is_holiday) X int month, year; X int is_holiday; { X register int day; X year_info *py; X month_info *pm; X register day_info *pd; X int first; X char *fcn = is_holiday ? "holidaytext" : "daytext"; X X /* if no text for this year and month, return */ X X if ((py = find_year(year, FALSE)) == NULL || X (pm = py->month[month-1]) == NULL) X return; X X /* walk array of day text pointers and linked lists of text */ X X for (day = 1; day <= NOTE_DAY; day++) { X for (pd = pm->day[day-1], first = TRUE; X pd; X pd = pd->next) { X if (pd->is_holiday != is_holiday) X continue; X if (first) { X if (day != NOTE_DAY) /* set up call */ X PRT("%d ", day); X printf("[ \n"); X } else X PRT("(.p)\n"); /* separate text */ X print_text(pd->text); X first = FALSE; X } X if (! first) /* wrap up call (if one made) */ X PRT("] %s\n", day == NOTE_DAY ? "notetext" : fcn); X } } X X /* X * Browse through the date file looking for holidays in specified month/year X */ find_holidays(month, year) X int month, year; { X register int day; X register unsigned long holidays; X year_info *py; X month_info *pm; X X pm = (py = find_year(year, FALSE)) ? py->month[month-1] : NULL; X X PRT("/note_block %s def\n", pm && pm->day[NOTE_DAY-1] ? "true" : X "false"); /* are there notes? */ X X PRT("/holidays ["); /* start definition of list */ X X for (holidays = pm ? pm->holidays : 0, day = 1; X holidays; X holidays >>= 1, day++) X if (holidays & 01) X PRT(" %d", day); X X PRT(" 99 ] def\n"); /* terminate with dummy entry */ X } X X /* X * pmonth - generate calendar for specified month/year X */ pmonth(month, year) X int month, year; { X X PRT("/year %d def\n", year); /* set up year and month */ X PRT("/month %d def\n", month); X find_holidays(month, year); /* make list of holidays */ X PRT("printmonth\n"); X find_daytext(month, year, TRUE); /* holiday text */ X find_daytext(month, year, FALSE); /* day and note text */ X PRT("showpage\n"); } X X /* X * Routines dealing with translation of file specifications (VMS, Un*x) X */ X #ifdef VMS /* X * mk_path - extract the path component from VMS file spec X */ char *mk_path(path, filespec) X char *path; /* output path */ X char *filespec; /* input filespec */ { X char *p; X X strcpy(path, filespec); X if (!(p = strchr(path, ']')) && !(p = strchr(path, ':'))) X p = path - 1; /* return null string if no path */ X *++p = '\0'; X X return path; } X X /* X * mk_filespec - merge VMS path and file names, where latter can be relative X */ X char *mk_filespec(filespec, path, name) X char *filespec; /* output filespec */ X char *path; /* input path */ X char *name; /* input file name */ { X char *p; X X *filespec = '\0'; X X /* copy name intact if absolute; else merge path and relative name */ X if (!strchr(name, ':')) { X strcpy(filespec, path); X if ((p = LASTCHAR(filespec)) && *p == END_PATH && X name[0] == START_PATH && strchr(".-", name[1])) X *p = *++name == '-' ? '.' : '\0'; X } X X return strcat(filespec, name); } X X /* X * trnlog - return translation of VMS logical name (null if missing) X */ char *trnlog(logname) /* look up logical name */ X char *logname; { static char trnbuf[STRSIZ]; X $DESCRIPTOR(src, logname); $DESCRIPTOR(dst, trnbuf); short len; int ret; X src.dsc$w_length = strlen(logname); ret = LIB$SYS_TRNLOG(&src, &len, &dst); return ret == SS$_NORMAL ? (trnbuf[len] = '\0', trnbuf) : NULL; } X #else X /* X * mk_path - extract the path component from a Un*x file spec X */ char *mk_path(path, filespec) X char *path; /* output path */ X char *filespec; /* input filespec */ { X char *p; X X strcpy(path, filespec); X if (! (p = strrchr(path, END_PATH)) ) X p = path - 1; /* return null string if no path */ X X *++p = '\0'; X return path; } X X /* X * mk_filespec - merge Un*x path and file names, where latter can be relative X */ X char *mk_filespec(filespec, path, name) X char *filespec; /* output filespec */ X char *path; /* input path */ X char *name; /* input file name */ { X char *p; X X *filespec = '\0'; X X /* copy name intact if absolute; else merge path and relative name */ X X /* if path starts with "~/", translate it for user */ X if (strncmp(name, "~/", 2) == 0 && (p = trnlog(HOME_DIR)) != NULL) { X strcpy(filespec, p); X if ((p = LASTCHAR(filespec)) && *p != END_PATH) X *++p = END_PATH, *++p = '\0'; X name += 2; /* skip "~/" */ X } X else if (*name != START_PATH) { /* relative path */ X strcpy(filespec, path); X if ((p = LASTCHAR(filespec)) && *p != END_PATH) X *++p = END_PATH, *++p = '\0'; X } X X return strcat(filespec, name); } X X /* X * trnlog - return translation of Un*x environment variable X */ char *trnlog(logname) /* look up logical name */ X char *logname; { return getenv(logname); } X #endif SHAR_EOF chmod 0644 pcal.c || echo 'restore of pcal.c failed' Wc_c="`wc -c < 'pcal.c'`" test 46687 -eq "$Wc_c" || echo 'pcal.c: original size 46687, current size' "$Wc_c" fi true || echo 'restore of pcal.hlp failed' echo End of part 1, continue with part 2 exit 0