1 static char  VERSION_STRING[]	= "@(#)pcal v4.11.0 - generate PostScript calendars";
2 
3 #ifdef AMIGA
4 #ifndef __AMIGADATE__
5 #define __AMIGADATE__ "("__DATE__")"
6 #endif
7 const char  VERsion_STRING[]	= "$VER: pcal 4.11.0 "__AMIGADATE__;
8 #endif
9 
10 /* ---------------------------------------------------------------------------
11 
12    pcal.c
13 
14    Notes:
15 
16       This file contains routines to generate a PostScript file to print a
17       calendar for any month and year.
18 
19       For information on this application, see the 'ReadMe.txt' file.
20 
21       The original PostScript code to generate the calendars was written by
22       Patrick Wood (Copyright (c) 1987 by Patrick Wood of Pipeline Associates,
23       Inc.), and authorized for modification and redistribution.  The calendar
24       file inclusion code was originally written in "bs(1)" by Bill Vogel of
25       AT&T.  Patrick's original PostScript was modified and enhanced several
26       times by King Ables, Tim Tessin, Joe Wood, Jeff Mogul, Mark Hanson, and
27       others whose names have regrettably been lost.  This C version was
28       originally created by Ken Keirnan of Pacific Bell; additional
29       enhancements by Joseph P. Larson, Ed Hand, Andrew Rogers, Mark
30       Kantrowitz, Joe Brownlee, Andy Fyfe, Steve Grandi, and Geoff Kuenning.
31       The moon routines were originally written by Jef Poskanzer and Craig
32       Leres, and were incorporated into Pcal by Richard Dyson.
33 
34       Note: Beginning with version 4.8.0, the 'man' page for 'pcal' is the
35       most up-to-date source of authors and contributors to 'pcal'.
36 
37    Revision history:
38 
39 	4.11.0
40 		B.Marr		2007-12-16
41 
42 		Allow the drawing of moon phase icons ('-m' or '-M') and
43 		Julian dates ('-j' or '-J') on yearly-format calendars.
44 
45 		B.Marr		2007-12-15
46 
47 		Update version number in version string.
48 
49 		Add support for new "on" preposition, thanks to a request from
50 		and in part to a patch from Erkki Petsalo.
51 
52 		Eliminate the now-needless "F13" ("Friday the 13th") special
53 		event trigger and the associated processing of it.
54 
55 		Add support for new '-W' option, to specify horizontal
56 		alignment of the "Month/Year" title on monthly-format
57 		calendars, thanks to a patch from Todd Foster.
58 
59 		Add support for building on Amiga, thanks to a patch from
60 		Stefan Haubenthal.
61 
62 	4.10.0
63 		B.Marr		2006-07-19
64 
65 		Merged 'pcalpapr.c' code into this file.
66 
67 		Reformatted comments and code to match my standards.
68 
69 		B.Marr		2006-07-12
70 
71 		Update version number in version string.
72 
73 		Clarify 'pcal -h' output generation ('display_usage()'
74 		routine) with improved comments.
75 
76 		Remove massive block of partially-obsolete comments describing
77 		'pcal' options and usage.  This information is adequately
78 		covered in other sources (e.g. 'man pcal') which are kept
79 		up-to-date.
80 
81 		Remove unneeded code to tell user where output file was
82 		written.
83 
84 		Drop support for obsolete platforms (Amiga, VMS, OS/2).
85 
86 		Get rid of all the '#ifdef PROTOS' checks, which are pretty
87 		much obsolete these days and just needlessly clutter up the
88 		code.
89 
90 	4.9.1
91 		B.Marr		2005-08-24
92 
93 		Update version number in version string.
94 
95 	4.9.0
96 		B.Marr		2005-08-08
97 
98 		Eliminate the hack to support Esperanto via a custom,
99 		dedicated character encoding.  Esperanto is now handled
100 		generically by the 'Latin3' (ISO 8859-3) character encoding.
101 
102 		B.Marr		2005-01-24
103 
104 		Update version number in version string.
105 
106 		Add support for several new character mappings (PostScript
107 		encoding vectors) in order to support a wider variety of
108 		languages.  Rename enumerations for existing encodings to be
109 		more consistent (and easily searchable).
110 
111 	4.8.0
112 		B.Marr		2004-12-04
113 
114 		Support new paper sizes.  Support specification of paper size
115 		via run-time option (command-line, etc).  Define new
116 		pre-processor symbols for paper size and page orientation.
117 		Remove spaces embedded within tab fields.  Create and support
118 		concept of 'input' language versus 'output' language.
119 
120 		Create separate variables for X/Y scaling and X/Y translation
121 		done by the program to distinguish from the X/Y scaling and
122 		X/Y translation specified by the user.  Remove spaces embedded
123 		within tab fields.
124 
125 		B.Marr		2004-11-13
126 
127 		Update version number in version string. Use new KOI8U mapping
128 		for newly-added Ukrainian language support.  Remove Ctl-L
129 		(page eject) characters from source file.
130 
131 
132 	4.7.1	SF	01/06/2003	added q-flag for 1-column output
133 
134 	4.7	AWR	01/25/2000	look for calendar file in directory
135 					where Pcal executable lives only if
136 					SEARCH_PCAL_DIR (cf. pcaldefs.h) is
137 					non-zero; this avoids conflicts if
138 					a program named 'calendar' has also
139 					been installed there
140 
141 					revised default year calculation to
142 					fix Y2K-related problems reported
143 					under some flavors of Un*x
144 
145 			12/15/1998	postpone calculation of final_month
146 					and final_year until adjusted value
147 					of nmonths is known
148 
149 			06/19/1998	allow numeric (0-6) argument to -F
150 					(first day) flag
151 
152 			03/08/1998	treat scaling/translation factors
153 					and copy count as numeric values
154 					instead of strings (cf. pcaldefs.h)
155 
156 			03/08/1998	redefine several globals as numeric
157 					instead of string (cf. pcaldefs.h)
158 
159 			12/21/1997	clean up gcc warnings in -Wall mode
160 
161 			07/27/1997	revise for -H (generate HTML output)
162 					support (cf. writefil.c); delete
163 					obsolete FPR and PRT macros
164 
165 			07/27/1997	replace calendar_out global with
166 					output_type (PostScript, HTML, or
167 					Un*x 'calendar' utility output)
168 
169 	4.6	AWR	05/14/1997	replace obsolete CENTURY macro with
170 					call to century() (cf. pcalutil.c)
171 
172 			12/11/1995	predefine version symbol "vN_M" for
173 					pcal release vN.M (cf. init_misc())
174 
175 			11/10/1995	support -T flag to select default
176 					font style (Bold/Italic/Roman)
177 
178 			10/31/1995	define "lang_XX" when language is set
179 					to XX (-a XX)
180 
181 			10/03/1995	use globals for initial and final
182 					month and year
183 
184 			10/03/1995	Add globals "init_month", "init_year",
185 					"nmonths", "final_month", "final_year"
186 
187 			09/21/1995	support "year all" syntax (calculate
188 					and save last year printed - cf.
189 					check_numargs())
190 
191 			05/09/1995	support troff-style escape sequences
192 					in text (cf. pcalutil.c, pcalinit.ps)
193 					to set font style to bold/Italic/Roman
194 
195 	4.5	AWR	11/01/1994	update top-line comments to credit
196 					additional early Pcal hackers
197 
198 			11/16/1993	support red:green:blue shading value
199 					syntax (cf. writefil.c, pcalutil.ps)
200 
201 			11/03/1993	widen flag/argument field in usage()
202 
203 			10/01/1993	use define_font() and define_shading()
204 					(cf. pcalutil.c; latter replaces old
205 					gen_shading()) for font and shading
206 					redefinition
207 
208 			09/09/1993	predefine alternate character set
209 					mapping name (cf. writefil.c,
210 					fontmaps.ps, pcaldefs.h)
211 
212 			04/28/1993	restructure function definitions so
213 					function name appears in first column
214 					(to facilitate searching for definition
215 					by name)
216 
217 			02/05/1993	support -# flag (generate multiple
218 					copies of each page)
219 
220 			11/16/1992	Use common "protos.h" file (q.v.)
221 
222 			02/11/1992	Add support for predefined holidays
223 					(cf. pcallang.h, readfile.c)
224 
225 	4.4	AWR	02/10/1992	Pipe "help" message through filter
226 					defined by environment variable
227 					PAGER_ENV (cf. pcaldefs.h)
228 
229 			01/20/1992	Add -z flag (extension of change
230 					suggested by Steve Grandi)
231 
232 			01/15/1992	Add "holiday" to -b, -g; expand -b,
233 					-g functionality to -G, -O; allow
234 					range of weekday names in all
235 
236 			01/13/1992	Support alternate date and title font
237 					sizes (single-month calendars only)
238 
239 			01/05/1992	Support "{<ordinal>} <day_name> <prep>
240 					<date_spec>" (cf. readfile.c)
241 
242 	4.3	AWR	12/06/1991	Attempted to simplify some of the
243 					mysteries surrounding command-line
244 					parsing; moved some processing from
245 					get_args() to new check_numargs()
246 
247 			12/05/1991	Search for moon file in directory
248 					where Pcal lives (cf. moonphas.c)
249 
250 			12/03/1991	Add -s flag to override default values
251 					for date/fill box shading
252 
253 			11/22/1991	Use cvt_escape() (new; cf. pcalutil.c)
254 					to convert escape sequences in command
255 					line strings
256 
257 			11/18/1991	Improve documentation; add init_misc()
258 					as catch-all for various initializations
259 
260 			10/25/1991	Support moon phases as wildcards
261 
262 			10/17/1991	Add -Z flag to generate debugging
263 					information; add a pre-pass through
264 					command line flags to detect -ZO
265 					prior to parsing PCAL_OPTS
266 
267 			10/15/1991	Revise logic of date file search
268 
269 	4.2	AWR	10/08/1991	Add -k and -K flags to control
270 					positioning of small calendars
271 
272 			10/03/1991	Add "note{/<n>}" to select box for
273 					note text (as per Geoff Kuenning)
274 
275 					Add -S flag to suppress generation
276 					of the small calendars
277 
278 			10/02/1991	Add -N flag to specify alternate
279 					heading for notes box
280 
281 					Allow user to specify alternate notes
282 					font size (-n <name>/<size>)
283 
284 			10/01/1991	Add -u flag to generate version info
285 					and parameter usage message
286 
287 			09/30/1991	Support "if" and "elif" in date file
288 
289 			09/19/1991	Add -c flag to generate input file for
290 					Un*x "calendar" utility
291 
292 	4.11	AWR	08/20/1991	Add support for "nearest" keyword
293 					(as per Andy Fyfe)
294 
295 					define "whole_year" when -w set
296 
297 			08/21/1991	Support %u, %w, %D, %M format specs
298 					and optional number following %[+-]
299 					(cf. writefil.c)
300 
301 	4.1	AWR	08/16/1991	Add -G flag to print "gray" dates as
302 					outlined, gray-filled characters
303 
304 					Fix potential bug in julday() (cf.
305 					moonphas.c)
306 
307 	4.02	AWR	07/02/1991	Add -v flag to print version info only;
308 					call find_executable() to get true
309 					program pathname (cf. pcalutil.c);
310 					add format specifiers to text strings
311 					(cf. writefil.c)
312 
313 	4.01	AWR	03/19/1991	Incorporate revised moonphas.c (q.v.)
314 
315 	4.0	AWR	02/24/1991	Add alt_fopen() to search for file
316 					in alternate path; use to look for
317 					date file in same directory as
318 					Pcal executable (as per Floyd Miller)
319 
320 					Support negative ordinals (cf.
321 					readfile.c, pcalutil.c)
322 
323 					Support expressions in preprocessor
324 					"if{n}def" lines (cf. exprpars.c)
325 
326 					Support "even", "odd" ordinals (cf.
327 					readfile.c) and ordinals > 5th
328 
329 					Support -B (leave unused boxes blank)
330 					flag
331 
332 					Separated into moonphas.c, pcal.c,
333 					pcalutil.c, readfile.c, and writefil.c;
334 					added support for moon phase file
335 
336 					Support -w (whole year) flag; fix
337 					various bugs and nonportable constructs
338 
339 	4.0	AWR	01/28/1991	Added support for -b and -w flags
340 
341 */
342 
343 /* ---------------------------------------------------------------------------
344 
345    Header Files
346 
347 */
348 
349 #include <stdio.h>
350 #include <ctype.h>
351 #include <time.h>
352 #include <string.h>
353 
354 #include "pcaldefs.h"
355 #include "pcallang.h"
356 #include "protos.h"
357 
358 /* ---------------------------------------------------------------------------
359 
360    Type, Struct, & Enum Declarations
361 
362 */
363 
364 /* ---------------------------------------------------------------------------
365 
366    Constant Declarations
367 
368 */
369 
370 /* ---------------------------------------------------------------------------
371 
372    Macro Definitions
373 
374 */
375 
376 /* ---------------------------------------------------------------------------
377 
378    Data Declarations (including externals)
379 
380 */
381 
382 static int nargs = 0;   /* count of non-flag args */
383 static int numargs[MAXARGS];   /* non-flag (numeric) args */
384 static int map_default = TRUE;   /* use default mapping */
385 static int oflag = FALSE;   /* -o flag specified */
386 
387 /*
388  * Misc. globals
389  */
390 
391 year_info *head = NULL;   /* head of internal data structure */
392 int curr_year;   /* current default year for date file entries */
393 int init_month;   /* starting month */
394 int init_year;   /* starting year */
395 int nmonths;   /* number of months */
396 int final_month;   /* ending month (calculated) */
397 int final_year;   /* ending year (calculated) */
398 double xsval_pgm;   /* X scaling value calculated by program */
399 double ysval_pgm;   /* Y scaling value calculated by program*/
400 int xtval_pgm;   /* X translation value calculated by program */
401 int ytval_pgm;   /* Y translation value calculated by program */
402 char *words[MAXWORD];   /* maximum number of words per date file line */
403 char lbuf[LINSIZ];   /* date file source line buffer */
404 char progname[STRSIZ];   /* program name (for error messages) */
405 char progpath[STRSIZ];   /* directory where executable lives */
406 char version[20];   /* program version (for info messages) */
407 
408 /* lengths and offsets of months in common year */
409 char month_len[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
410 short month_off[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
411 
412 /* dispatch table of functions for wildcard matching routines */
413 int (*pdatefcn[]) (int, int, int) = {
414    is_anyday, is_weekday, is_workday, is_holiday, not_weekday, not_workday,
415    not_holiday, is_newmoon, is_firstq, is_fullmoon, is_lastq
416 };
417 
418 /*
419  * Default values for command-line options:
420  */
421 
422 char default_color[] = DAY_COLOR;   /* -b, -g, -G, -O */
423 char day_color[7];
424 int holiday_color;
425 int weekday_color = WEEKDAY_COLOR;
426 
427 int datefile_type = SYS_DATEFILE;   /* -e, -f */
428 char datefile[STRSIZ] = "";
429 
430 int rotate = ROTATE;   /* -l, -p */
431 
432 int draw_moons = DRAW_MOONS;   /* -m, -M */
433 
434 char datefont[STRSIZ] = DATEFONT;   /* -d, -t, -n */
435 char titlefont[STRSIZ] = TITLEFONT;
436 char notesfont[STRSIZ] = NOTESFONT;
437 
438 int mapfonts = MAPFONTS;   /* -r */
439 
440 char shading[STRSIZ] = SHADING;   /* -s */
441 
442 char lfoot[STRSIZ] = LFOOT;             /* -L, -C, -R */
443 char cfoot[STRSIZ] = CFOOT;
444 char rfoot[STRSIZ] = RFOOT;
445 
446 char notes_hdr[STRSIZ];   /* -N (initialized in main()) */
447 
448 int first_day_of_week = FIRST_DAY;   /* -F */
449 
450 int date_style = DATE_STYLE;   /* -A, -E */
451 
452 char outfile[STRSIZ] = "";   /* -o */
453 
454 double xsval_user = 1.0;   /* -x, -y, -X, -Y */
455 double ysval_user = 1.0;
456 int xtval_user = 0;
457 int ytval_user = 0;
458 
459 int julian_dates = JULIAN_DATES;   /* -j */
460 
461 int do_whole_year = DO_WHOLE_YEAR;   /* -w */
462 
463 int output_type = OUTPUT_TYPE;   /* -c, -H */
464 
465 int one_column = 0;   /* -q */
466 
467 int blank_boxes = BLANK_BOXES;   /* -B */
468 
469 int ncopy = NCOPY;   /* -# */
470 
471 int small_cal_pos = SMALL_CAL_POS;   /* -k, -K, -S */
472 int prev_cal_box[4] = PREV_CAL_BOX;
473 int next_cal_box[4] = NEXT_CAL_BOX;
474 
475 char time_zone[STRSIZ] = TIMEZONE;   /* -z */
476 int tz_flag = FALSE;
477 
478 char title_align[STRSIZ] = TITLE_ALIGN;   /* -W */
479 
480 int debug_flags = 0;   /* -Z */
481 
482 
483 /* must be in same order as color definitions in pcaldefs.h; also see
484  * "prtday{}" in pcalinit.ps
485  */
486 char *color_names[] = { W_BLACK, W_GRAY, W_OUTLINE, W_OUTLINE_GRAY };
487 
488 /* Must be a 2-D array so address within may be used as an initializer;
489  * wildcard names must be in same order as symbolic names in pcaldefs.h
490  * Note that the weekday names were superseded by days_ml[][] throughout
491  * as of v4.6; only the wildcards are still used
492  */
493 char *days[] = {
494    "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",   /* weekday names */
495    "day", "weekday", "workday", HOLIDAY, "nonweekday", "nonworkday", "nonholiday"   /* wildcards */
496 };
497 
498 /* preprocessor tokens: token name, token code, dispatch routine; note that
499  * substring "if" must follow "ifdef" and "ifndef" for proper matching
500  */
501 KWD_F pp_info[] = {
502    { "define",	PP_DEFINE,	do_define },
503    { "elif",	PP_ELIF,	do_ifdef },
504    { "else",	PP_ELSE,	NULL },
505    { "endif",	PP_ENDIF,	NULL },
506    { "ifdef",	PP_IFDEF,	do_ifdef },
507    { "ifndef",	PP_IFNDEF,	do_ifndef },
508    { "if",	PP_IFDEF,	do_ifdef },   /* "ifdef" synonym */
509    { "include",	PP_INCLUDE,	NULL },   /* do_include */
510    { "undef",	PP_UNDEF,	do_undef },
511    { NULL,	PP_OTHER,	NULL }   /* must be last */
512 };
513 
514 /* ordinal numbers - e.g. "first Monday in September": ordinal name,
515  * ordinal code, ordinal value; note that "all" is parsed as a keyword
516  * and (depending on context) may be subsequently treated as an ordinal
517  */
518 
519 KWD_O ordinals[] = {
520    { "first",	ORD_POSNUM,	FIRST },
521    { "second",	ORD_POSNUM,	SECOND },
522    { "third",	ORD_POSNUM,	THIRD },
523    { "fourth",	ORD_POSNUM,	FOURTH },
524    { "fifth",	ORD_POSNUM,	FIFTH },
525    { "last",	ORD_NEGNUM,	LAST },
526    { "odd",	ORD_ODD,	0 },
527    { "even",	ORD_EVEN,	0 },
528    { NULL,	ORD_OTHER,	0 }   /* must be last */
529 };
530 
531 /* predefined events (often, but not always, holiday events): event name,
532  * definition, and dispatch function.  The definition and dispatch function
533  * are mutually exclusive; those events which can easily be redefined as an
534  * equivalent Pcal date are translated in-line by parse_date() using the
535  * definition string, while the "hard" ones - e.g., Easter - each use a
536  * dedicated dispatch function instead.  (The syntax for Christmas is awkward,
537  * but unlike the obvious "12/25" it is independent of American/European date
538  * parsing conventions.)
539  */
540 
541 KWD_H predef_events[] = {
542    { "Christmas", "25th day of December", NULL },
543    { "Thanksgiving", "Fourth Thu in November", NULL },
544    { "Easter", NULL, find_easter },
545    { "Good_Friday", "Friday before Easter", NULL },
546 #ifndef NO_ORTHODOX
547    /* Orthodox Easter related */
548    { "GEaster", NULL, find_odox_easter },
549    { "Gstgeorge", NULL, find_odox_stgeorge },
550    { "Gmarcus", NULL, find_odox_marcus },
551 #endif /* !NO_ORTHODOX  */
552    { NULL, NULL, NULL }  /* must be last */
553 };
554 
555 /* allowable suffixes for ordinal numbers - these must be in order 0, 1, 2...
556  * according to the rules of the target language; cf. ordinal_suffix() below
557  */
558 
559 char *ord_suffix[] = { "th", "st", "nd", "rd", NULL };
560 
561 /* prepositions - e.g., "Friday after fourth Thursday in November" */
562 
563 KWD preps[] = {
564    { "before",		PR_BEFORE },
565    { "preceding",	PR_BEFORE },
566    { "on_or_before",	PR_ON_BEFORE },
567    { "oob",		PR_ON_BEFORE },
568    { "after",		PR_AFTER },
569    { "following",	PR_AFTER },
570    { "on_or_after",	PR_ON_AFTER },
571    { "ooa",		PR_ON_AFTER },
572    { "nearest",		PR_NEAREST },
573    { "nearest_before",	PR_NEAREST_BEFORE },
574    { "nearest_after",	PR_NEAREST_AFTER },
575    { "on",		PR_ON },
576    { NULL,		PR_OTHER }   /* must be last */
577 };
578 
579 /* other keywords */
580 
581 KWD keywds[] = {
582    { ALL,		DT_ALL },
583    { "each",		DT_ALL },
584    { "every",		DT_ALL },
585    { "note",		DT_NOTE },
586    { "opt",		DT_OPT },
587    { "input-language",	DT_INPUT_LANGUAGE },
588    { "year",		DT_YEAR },
589    { "delete",		DT_DELETE },
590    { NULL,		DT_OTHER }   /* must be last */
591 };
592 
593 /* moon phases (for moon file) */
594 
595 KWD phases[] = {
596    { "new_moon",	MOON_NM },   /* new moon */
597    { "nm",		MOON_NM },
598    { "first_quarter",	MOON_1Q },   /* first quarter */
599    { "1Q",		MOON_1Q },
600    { "FQ",		MOON_1Q },
601    { "full_moon",	MOON_FM },   /* full moon */
602    { "FM",		MOON_FM },
603    { "last_quarter",	MOON_3Q },   /* last (third) quarter */
604    { "LQ",		MOON_3Q },
605    { "third_quarter",	MOON_3Q },
606    { "3Q",		MOON_3Q },
607    { NULL,		MOON_OTHER }   /* must be last */
608 };
609 
610 /* default notes box header */
611 
612 char default_notes_hdr[] = "Notes";
613 
614 /* default font style (must be Roman) */
615 
616 char fontstyle[] = W_ROMAN;
617 
618 
619 /*
620  * Flag usage information - not strictly language-dependent, but here anyway
621  * (N.B.: all flags must be represented by an entry in this table!)
622  *
623  * Flags may appear in any of three places: in environment variable
624  * PCAL_OPTS, on the command line, or in "opt" lines in the date file.
625  * The command line is actually parsed twice: once before reading the date
626  * file to get the flags needed in processing it (-[bcefgklpwADEKSU]), and
627  * and again after reading the date file to give the user one last chance
628  * to override any of the other flags set earlier.  (Note, however, that
629  * the only way to turn off -J|-j [Julian dates], -M|-m [moons], -w [whole
630  * year], or -G|-O [outline "gray" dates] once selected is to use -I to
631  * reinitialize all program defaults.)
632  *
633  * The table below supplies the following information about each flag:
634  *
635  *      - Its name (cf. symbolic definitions above)
636  *
637  *      - Whether or not it can take an (optional) argument
638  *
639  *      - Which passes parse it - in order, they are: P_CMD0 ("pre-pass" of
640  *        command line to find debugging flags), P_ENV (environment variable),
641  *        P_CMD1 (first command line pass), P_OPT ("opt" lines in date file),
642  *        and P_CMD2 (second command line pass).
643  *
644  *        The basic idea here is that some flags need to be in effect prior
645  *        to reading the date file (e.g., -E, -A) while the user should
646  *        be able to override other flags set in the date file (e.g., -d,
647  *        -n, -t) on the command line if desired.  Two flags - F_OUTLINE
648  *        and F_OUTLINE_GRAY - are processed in both command-line passes, for
649  *        backward compatibility with earlier versions of Pcal (cf. pcal.c).
650  */
651 FLAG_USAGE flag_tbl[] = {
652 
653 /*	flag name	arg?		     passes where parsed	*/
654 
655 	{ F_INITIALIZE,	FALSE,		 P_ENV | P_CMD1 | P_OPT | P_CMD2 },
656 
657 	{ F_BLACK_DAY,	TRUE,		 P_ENV | P_CMD1 | P_OPT		 },
658 	{ F_GRAY_DAY,	TRUE,		 P_ENV | P_CMD1 | P_OPT		 },
659 	{ F_OUTLINE,	TRUE,		 P_ENV | P_CMD1 | P_OPT | P_CMD2 },
660 	{ F_OUTLINE_GRAY, TRUE,		 P_ENV | P_CMD1 | P_OPT | P_CMD2 },
661 
662 	{ F_DAY_FONT,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
663 	{ F_NOTES_FONT,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
664 	{ F_TITLE_FONT,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
665 
666 	{ F_REMAP_FONT,	TRUE,		 P_ENV | P_CMD1 | P_OPT		 },
667 
668 	{ F_PAPERSIZE,	TRUE,		 P_ENV | P_CMD1 | P_OPT		 },
669 
670 	{ F_EMPTY_CAL,	FALSE,		 P_ENV | P_CMD1			 },
671 	{ F_DATE_FILE,	TRUE,		 P_ENV | P_CMD1			 },
672 	{ F_OUT_FILE,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
673 
674 	{ F_LANDSCAPE,	FALSE,		 P_ENV | P_CMD1 | P_OPT | P_CMD2 },
675 	{ F_PORTRAIT,	FALSE,		 P_ENV | P_CMD1 | P_OPT | P_CMD2 },
676 
677 	{ F_HELP,	FALSE,	P_CMD0					 },
678 	{ F_USAGE,	FALSE,	P_CMD0					 },
679 	{ F_VERSION,	FALSE,	P_CMD0					 },
680 
681 	{ F_MOON_4,	FALSE,		 P_ENV		| P_OPT | P_CMD2 },
682 	{ F_MOON_ALL,	FALSE,		 P_ENV		| P_OPT | P_CMD2 },
683 
684 	{ F_DEFINE,	TRUE,		 P_ENV | P_CMD1			 },
685 	{ F_UNDEF,	TRUE,		 P_ENV | P_CMD1			 },
686 
687 	{ F_L_FOOT,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
688 	{ F_C_FOOT,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
689 	{ F_R_FOOT,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
690 
691 	{ F_NOTES_HDR,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
692 
693 	{ F_FIRST_DAY,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
694 
695 	{ F_USA_DATES,	FALSE,		 P_ENV | P_CMD1 | P_OPT		 },
696 	{ F_EUR_DATES,	FALSE,		 P_ENV | P_CMD1 | P_OPT		 },
697 
698 	{ F_X_TRANS,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
699 	{ F_Y_TRANS,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
700 	{ F_X_SCALE,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
701 	{ F_Y_SCALE,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
702 
703 	{ F_JULIAN,	FALSE,		 P_ENV		| P_OPT | P_CMD2 },
704 	{ F_JULIAN_ALL,	FALSE,		 P_ENV		| P_OPT | P_CMD2 },
705 
706 	{ F_WHOLE_YEAR,	FALSE,		 P_ENV | P_CMD1 | P_OPT		 },
707 
708 	{ F_BLANK_BOXES, FALSE,		 P_ENV		| P_OPT | P_CMD2 },
709 
710 	{ F_NUM_PAGES,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
711 
712 	{ F_SC_NONE,	FALSE,		 P_ENV | P_CMD1 | P_OPT		 },
713 	{ F_SC_FIRST,	FALSE,		 P_ENV | P_CMD1 | P_OPT		 },
714 	{ F_SC_SPLIT,	FALSE,		 P_ENV | P_CMD1 | P_OPT		 },
715 
716 	{ F_SHADING,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
717 
718 	{ F_CALENDAR,	FALSE,		 P_ENV | P_CMD1			 },
719 	{ F_HTML,	FALSE,		 P_ENV | P_CMD1			 },
720 	{ F_1COLUMN,	FALSE,		 P_ENV | P_CMD1			 },
721 
722 	{ F_TIMEZONE,	TRUE,		 P_ENV		| P_OPT | P_CMD2 },
723 
724 	{ F_SETLANG,	TRUE,		 P_ENV | P_CMD1 | P_OPT		 },
725 
726 	{ F_TYPEFACE,	TRUE,		 P_ENV | P_CMD1 | P_OPT		 },
727 
728 	{ F_TITLEALIGN,	TRUE,		P_ENV | P_CMD1 | P_OPT		 },
729 
730 	{ F_DEBUG,	TRUE,	P_CMD0 | P_ENV		| P_OPT		 },
731 
732 	{ '-',		FALSE,		 P_ENV | P_CMD1 | P_OPT | P_CMD2 },
733 	{ '\0',		FALSE,		 P_ENV | P_CMD1 | P_OPT | P_CMD2 }	/* must be last */
734 };
735 
736 /* subflags for debug info flag F_DEBUG */
737 
738 DEBUG_INFO debug_info[] = {
739    { D_DATES,		DEBUG_DATES },
740    { D_FILE_PATHS,	DEBUG_PATHS },
741    { D_MOON,		DEBUG_MOON },
742    { D_OPT,		DEBUG_OPTS },
743    { D_PREPROCESSOR,	DEBUG_PP },
744    { D_TEXT,		DEBUG_TEXT },
745    { '\0',		0 }   /* must be last */
746 };
747 
748 
749 /* to be filled in by display_usage() */
750 static char Xtval[VALSIZ], Ytval[VALSIZ], Xsval[VALSIZ], Ysval[VALSIZ], Ncopy[VALSIZ];
751 
752 /*
753  * Message strings to be printed by usage() - translate as necessary
754  */
755 FLAG_MSG flag_msg[] = {
756 
757 /*	flag name	metasyntax	description						default */
758 
759 	{ F_INITIALIZE,	NULL,		"initialize all parameters to program defaults",	NULL },
760 	{ END_GROUP },
761 
762 	{ F_BLACK_DAY,	W_DAY2,		"print specified day(s) in black",			NULL },
763 	{ F_GRAY_DAY,	W_DAY2,		"print specified day(s) in day-numerics color ('-s')",	NULL },
764 	{ F_OUTLINE,	W_DAY2,		"print specified day(s) as outline",			NULL },
765 	{ F_OUTLINE_GRAY, W_DAY2,	"print specified day(s) as filled outline",		NULL },
766 	{ END_GROUP },
767 
768 	{ F_SHADING,	W_SHADING,	"specify colors for day-numerics and empty-box-fill",	NULL },
769 	{ ' ',		NULL,		" ",							SHADING },
770 	{ END_GROUP },
771 
772 	{ F_DAY_FONT,	W_FONT_SIZE,	"specify alternate date font and optional size <n>",	NULL },
773 	{ ' ',		NULL,		" ",							DATEFONT },
774 	{ F_NOTES_FONT,	W_FONT_SIZE,	"specify alternate notes font and optional size <n>",	NULL },
775 	{ ' ',		NULL,		" ",							NOTESFONT },
776 	{ F_TITLE_FONT,	W_FONT_SIZE,	"specify alternate title font and optional size <n>",	NULL },
777 	{ ' ',		NULL,		" ",							TITLEFONT },
778 	{ END_GROUP },
779 
780 	{ F_REMAP_FONT,	W_MAPPING,	"remap 8-bit characters (Latin1, KOI8-U, Roman8, etc)", NULL },
781 #if MAPFONTS == ENC_LATIN_1
782 	{ GROUP_DEFAULT,									MAPPING_LATIN_1 },
783 #elif MAPFONTS == ENC_LATIN_2
784 	{ GROUP_DEFAULT,									MAPPING_LATIN_2 },
785 #elif MAPFONTS == ENC_LATIN_3
786 	{ GROUP_DEFAULT,									MAPPING_LATIN_3 },
787 #elif MAPFONTS == ENC_LATIN_4
788 	{ GROUP_DEFAULT,									MAPPING_LATIN_4 },
789 #elif MAPFONTS == ENC_CYRILLIC
790 	{ GROUP_DEFAULT,									MAPPING_CYRILLIC },
791 /* #elif MAPFONTS == ENC_ARABIC */  /* currently unsupported */
792 /* 	{ GROUP_DEFAULT,									MAPPING_ARABIC }, */
793 #elif MAPFONTS == ENC_GREEK
794 	{ GROUP_DEFAULT,									MAPPING_GREEK },
795 /* #elif MAPFONTS == ENC_HEBREW */  /* currently unsupported */
796 /* 	{ GROUP_DEFAULT,									MAPPING_HEBREW }, */
797 #elif MAPFONTS == ENC_LATIN_5
798 	{ GROUP_DEFAULT,									MAPPING_LATIN_5 },
799 #elif MAPFONTS == ENC_LATIN_6
800 	{ GROUP_DEFAULT,									MAPPING_LATIN_6 },
801 #elif MAPFONTS == ENC_THAI
802 	{ GROUP_DEFAULT,									MAPPING_THAI },
803 #elif MAPFONTS == ENC_LATIN_7
804 	{ GROUP_DEFAULT,									MAPPING_LATIN_7 },
805 #elif MAPFONTS == ENC_LATIN_8
806 	{ GROUP_DEFAULT,									MAPPING_LATIN_8 },
807 #elif MAPFONTS == ENC_LATIN_9
808 	{ GROUP_DEFAULT,									MAPPING_LATIN_9 },
809 /* #elif MAPFONTS == ENC_LATIN_10 */  /* currently unsupported */
810 /* 	{ GROUP_DEFAULT,									MAPPING_LATIN_10 }, */
811 #elif MAPFONTS == ENC_KOI8_R
812 	{ GROUP_DEFAULT,									MAPPING_KOI8_R },
813 #elif MAPFONTS == ENC_KOI8_U
814 	{ GROUP_DEFAULT,									MAPPING_KOI8_U },
815 #elif MAPFONTS == ENC_ROMAN8
816 	{ GROUP_DEFAULT,									MAPPING_ROMAN8 },
817 #else
818 	{ GROUP_DEFAULT,									"language-specific mapping" },
819 #endif
820 	{ END_GROUP },
821 
822 	{ F_EMPTY_CAL,	NULL,		"generate empty calendar (ignore date file)",		NULL },
823 	{ END_GROUP },
824 
825 	{ F_DATE_FILE,	W_FILE,		"specify alternate date file",				DATEFILE },
826 	{ END_GROUP },
827 
828 #ifdef DEFAULT_OUTFILE
829 	{ F_OUT_FILE,	W_FILE,		"specify alternate output file",			DEFAULT_OUTFILE },
830 #else
831 	{ F_OUT_FILE,	W_FILE,		"specify alternate output file",			"stdout" },
832 #endif
833 	{ END_GROUP },
834 
835 	{ F_LANDSCAPE,	NULL,		"generate landscape-style calendar",			NULL },
836 	{ F_PORTRAIT,	NULL,		"generate portrait-style calendar",			NULL },
837 #if ROTATE == LANDSCAPE
838 	{ GROUP_DEFAULT,									"landscape" },
839 #else
840 	{ GROUP_DEFAULT,									"portrait" },
841 #endif
842 	{ END_GROUP },
843 
844 	{ F_PAPERSIZE,	W_PAPERSIZE,	"specify paper size (letter, legal, a4, tabloid)",	NULL },
845 	{ END_GROUP },
846 
847 	{ F_HELP,	NULL,		"print this help message",				NULL },
848 	{ F_USAGE,	NULL,		"print parameter usage message",			NULL },
849 	{ F_VERSION,	NULL,		"print version information",				NULL },
850 	{ END_GROUP },
851 
852 	{ F_MOON_4,	NULL,		"draw 'moon phase' icon at full/new/quarter moons",	NULL },
853 	{ F_MOON_ALL,	NULL,		"draw 'moon phase' icon every day",			NULL },
854 #if DRAW_MOONS == NO_MOONS
855 	{ GROUP_DEFAULT,									"no moons" },
856 #else
857 #if DRAW_MOONS == SOME_MOONS
858 	{ GROUP_DEFAULT,									"full/new/quarter moons" },
859 #else
860 	{ GROUP_DEFAULT,									"every day" },
861 #endif
862 #endif
863 	{ END_GROUP },
864 
865 	{ F_DEFINE,	W_SYMBOL,	"define preprocessor symbol",				NULL },
866 	{ F_UNDEF,	W_SYMBOL,	"undefine preprocessor symbol",				NULL },
867 	{ END_GROUP },
868 
869 	{ F_L_FOOT,	W_STRING,	"specify left foot string",				LFOOT },
870 	{ F_C_FOOT,	W_STRING,	"specify center foot string",				CFOOT },
871 	{ F_R_FOOT,	W_STRING,	"specify right foot string",				RFOOT },
872 	{ END_GROUP },
873 
874 	{ F_NOTES_HDR,	W_STRING,	"specify header for notes box",				default_notes_hdr },
875 	{ END_GROUP },
876 
877 	{ F_FIRST_DAY,	W_DAY,		"specify starting day of week",				days_ml[LANG_DEFAULT][FIRST_DAY] },
878 	{ END_GROUP },
879 
880 	{ F_USA_DATES,	NULL,		"parse American dates (\"mm/dd{/yy}\" and/or \"month dd\")", NULL },
881 	{ F_EUR_DATES,	NULL,		"parse European dates (\"dd/mm{/yy}\" and/or \"dd month\")", NULL },
882 #if DATE_STYLE == USA_DATES
883 	{ GROUP_DEFAULT,									"American" },
884 #else
885 	{ GROUP_DEFAULT,									"European" },
886 #endif
887 	{ END_GROUP },
888 
889 	{ F_X_TRANS,	W_VALUE,	"specify x-axis translation",				Xtval },
890 	{ F_Y_TRANS,	W_VALUE,	"specify y-axis translation",				Ytval },
891 	{ F_X_SCALE,	W_VALUE,	"specify x-axis scale factor",				Xsval },
892 	{ F_Y_SCALE,	W_VALUE,	"specify y-axis scale factor",				Ysval },
893 	{ END_GROUP },
894 
895 	{ F_JULIAN,	NULL,		"print Julian day (day of year)",			NULL },
896 	{ F_JULIAN_ALL,	NULL,		"print Julian day and days remaining in year",		NULL },
897 #if JULIAN_DATES == NO_JULIANS
898 	{ GROUP_DEFAULT,									"neither" },
899 #else
900 #if JULIAN_DATES == SOME_JULIANS
901 	{ GROUP_DEFAULT,									"Julian day" },
902 #else
903 	{ GROUP_DEFAULT,									"both" },
904 #endif
905 #endif
906 	{ END_GROUP },
907 
908 #if DO_WHOLE_YEAR == FALSE
909 	{ F_WHOLE_YEAR,	NULL,		"print whole year (12 consecutive months) per page",	NULL },
910 #else
911 	{ F_WHOLE_YEAR,	NULL,		"print individual months one per page",			NULL },
912 #endif
913 	{ END_GROUP },
914 
915 #if BLANK_BOXES == FALSE
916 	{ F_BLANK_BOXES, NULL,		"leave unused boxes blank",				NULL },
917 #else
918 	{ F_BLANK_BOXES, NULL,		"shade unused boxes",					NULL },
919 #endif
920 	{ END_GROUP },
921 
922 	{ F_NUM_PAGES,	W_N,		"print <n> copies of each output page",			Ncopy },
923 	{ END_GROUP },
924 
925 	{ F_SC_NONE,	NULL,		"suppress generation of small calendars",		NULL },
926 	{ F_SC_FIRST,	NULL,		"print small calendars in first two boxes",		NULL },
927 	{ F_SC_SPLIT,	NULL,		"print previous month in first box, next in last",	NULL },
928 #if SMALL_CAL_POS == SC_LAST
929 	{ GROUP_DEFAULT,									"last two boxes" },
930 #else
931 #if SMALL_CAL_POS == SC_FIRST
932 	{ GROUP_DEFAULT,									"first two boxes" },
933 #else
934 #if SMALL_CAL_POS == SC_SPLIT
935 	{ GROUP_DEFAULT,									"first/last boxes" },
936 #else
937 	{ GROUP_DEFAULT,									"suppress small calendars" },
938 #endif
939 #endif
940 #endif
941 	{ END_GROUP },
942 
943 	{ F_CALENDAR,	NULL,		"generate input for Unix calendar(1) utility",		NULL },
944 	{ F_HTML,	NULL,		"generate calendar as HTML table",			NULL },
945 	{ END_GROUP },
946 
947 	{ F_1COLUMN,	NULL,		"print one column per month (HTML)",			NULL },
948 	{ END_GROUP },
949 
950 	{ F_TIMEZONE,	W_VALUE,	"specify time zone in hours west of GMT/UTC",		NULL },
951 	{ GROUP_DEFAULT,									TIMEZONE },
952 	{ END_GROUP },
953 
954 	{ F_SETLANG,	W_LANG,		"specify output language for day/month names",		NULL },
955 	{ GROUP_DEFAULT,									lang_id[LANG_DEFAULT] },
956 	{ END_GROUP },
957 
958 	{ F_TYPEFACE,	W_TYPEFACE,	"specify font style (Bold | Italic | Roman)",		NULL },
959 	{ GROUP_DEFAULT,									W_ROMAN },
960 	{ END_GROUP },
961 
962 	{ F_TITLEALIGN,	W_TITLEALIGN,	"specify title alignment",				NULL },
963 	{ GROUP_DEFAULT,									"center" },
964 	{ END_GROUP },
965 
966 	{ END_LIST }   /* must be last */
967 };
968 
969 
970 
971 /* Numeric parameter descriptions and text */
972 PARAM_MSG param_msg[] = {
973 	{ "YY",			"generate calendar for year YY (20YY if YY < 100)" },
974 	{ "MM YY",		"generate calendar for month MM (Jan = 1), year YY" },
975 	{ "MM YY N",		"generate calendars for N months, starting at MM/YY" },
976 	{ "(default)",		"generate calendar for current month and year" },
977 	{ "",			"" },
978 	{ "if -w specified:",	"" },
979 	{ "",			"" },
980 	{ "YY",			"generate calendar for year YY (20YY if YY < 100)" },
981 	{ "MM YY",		"generate calendar for 12 months, starting at MM/YY" },
982 	{ "MM YY N",		"generate calendars for N months, starting at MM/YY" },
983 	{ "",			"  (N rounded up to next multiple of 12)" },
984 	{ "(default)",		"generate calendar for current year" },
985 	{ NULL,			NULL }   /* must be last */
986 };
987 
988 
989 
990 /*
991    Define the values associated with each of the supported paper sizes.
992 
993    All dimensions are provided in typographic points (72nds of an inch).
994 
995    Values defined here include the common name associated with that paper
996    size, the page dimensions (shortest first), and the dimensions (width,
997    height) of a 'day box' on a monthly-format calendar printed in landscape
998    orientation.
999  */
1000 paper_info_str_typ paper_info[] = {
1001    { "Letter",             /* name of this paper size */
1002      612, 792,             /* U.S. 'Letter' = 8.5 x 11.0 inches */
1003      100, 80               /* 'daybox' width and height */
1004    },
1005    { "Legal",              /* name of this paper size */
1006      612, 1008,            /* U.S. 'Legal' = 8.5 x 14.0 inches */
1007      130, 80               /* 'daybox' width and height */
1008    },
1009    { "A4",                 /* name of this paper size */
1010      595, 842,             /* International 'A4' = 210 x 297 mm */
1011      108, 77               /* 'daybox' width and height */
1012    },
1013    { "Tabloid",            /* name of this paper size */
1014      792, 1224,            /* 'Tabloid'/'Ledger' = 11.0 x 17.0 inches */
1015      161, 110              /* 'daybox' width and height */
1016    },
1017 };
1018 
1019 /* Define the default paper size. */
1020 int paper_size = PAPERSIZE_DEFAULT;
1021 
1022 /*
1023    These variables keep track of the page dimensions in typographic points
1024    (72nds of an inch).
1025 
1026    Note: Because of the possibility of different page orientations ('portrait'
1027    and 'landscape'), to avoid confusion, these variables are named and used
1028    independently of page orientation.  That is, they're not termed 'width' and
1029    'height' (which change depending on the orientation) but instead refer to
1030    the 'long' and 'short' axes of the paper (which don't change with
1031    orientation).
1032 */
1033 int page_dim_short_axis_pts, page_dim_long_axis_pts;
1034 
1035 /*
1036    Define the width and height of a 'day box' (i.e. a box which represents a
1037    single day within a given month) in typographic points.
1038 
1039    Note: These values change depending on whether the selected orientation is
1040    'portrait' or 'landscape'.
1041  */
1042 int daybox_width_pts, daybox_height_pts;
1043 
1044 
1045 /* ---------------------------------------------------------------------------
1046 
1047    External Routine References & Function Prototypes
1048 
1049 */
1050 
1051 /* ---------------------------------------------------------------------------
1052 
1053    init_misc
1054 
1055    Notes:
1056 
1057       This routine performs various initializations not easily handled in the
1058       header files.
1059 
1060 */
init_misc(void)1061 void init_misc (void)
1062 {
1063    char tmp[STRSIZ], *p;
1064 
1065    INIT_COLORS;   /* copy default_color to day_color */
1066    strcpy(notes_hdr, default_notes_hdr);   /* initialize notes_hdr */
1067 
1068    /* define preprocessor symbol "lang_XX" for default language XX */
1069    do_define(gen_lang_sym(LANG_DEFAULT));
1070 
1071    /* Define 'PAPERSIZE_XXX' (where XXX is the name of the selected paper
1072     * size).
1073     */
1074    sprintf(tmp, "PAPERSIZE_%s", paper_info[paper_size].name);
1075    do_define(tmp);
1076 
1077    /* Define 'ORIENTATION_XXX' (where XXX is the name of the current paper
1078     * orientation -- 'portrait' or 'landscape').
1079     */
1080    sprintf(tmp, "ORIENTATION_%s", (rotate == PORTRAIT) ? "PORTRAIT" : "LANDSCAPE");
1081    do_define(tmp);
1082 
1083    /* define preprocessor symbol vN_N for Pcal version vN.N */
1084    strcpy(tmp, version);
1085    for (p = tmp; *p; p++) {
1086       if (*p == '.') *p = '_';
1087    }
1088    do_define(tmp);
1089 
1090    return;
1091 }
1092 
1093 /* ---------------------------------------------------------------------------
1094 
1095    set_color
1096 
1097    Notes:
1098 
1099       This routine sets one or all weekdays (or all holidays) to print in the
1100       specified color.  It can also accept ranges such as "mon-fri" or
1101       "sat-sun".
1102 
1103       The first parameter is the day-of-week name (or "all" or "holiday").
1104 
1105       The second parameter is used to select black / gray / outline /
1106       outline-gray.
1107 
1108 */
set_color(char * day,int col)1109 void set_color (char *day, int col)
1110 {
1111    int i, j, max, count[NUM_COLORS];
1112    char tmp[STRSIZ], *p;
1113 
1114    if (ci_strncmp(day, ALL, strlen(ALL)) == 0) {   /* set all days */
1115       for (i = 0; i < 7; i++) day_color[i] = col;
1116    }
1117 
1118    else if (ci_strncmp(day, HOLIDAY, MIN_DAY_LEN) == 0) { /* set holidays */
1119       holiday_color = col;
1120    }
1121 
1122    else {   /* set range of days */
1123       strcpy(tmp, day);
1124       if ((p = strchr(tmp, '-')) != NULL) *p++ = '\0';
1125 
1126       /* parse single day or range of days - return if error */
1127       if ((i = get_weekday(tmp, FALSE)) == NOT_WEEKDAY ||
1128           (j = p ? get_weekday(p, FALSE) : i) == NOT_WEEKDAY) {
1129          return;
1130       }
1131 
1132       /* set day colors, wrapping around end of week */
1133       for (j = (i > j) ? j + 7 : j; i <= j; i++) day_color[i % 7] = col;
1134    }
1135 
1136    /* reset weekday_color to most prevalent color */
1137 
1138    for (i = 0; i < NUM_COLORS; i++) {   /* clear counts */
1139       count[i] = 0;
1140    }
1141 
1142    for (i = SUN; i <= SAT; i++) { /* count colors */
1143       count[(int)day_color[i]]++;
1144    }
1145 
1146    for (i = max = 0; i < NUM_COLORS; i++) { /* get most prevalent */
1147       if (count[i] > max) max = count[weekday_color = i];
1148    }
1149    return;
1150 }
1151 
1152 /* ---------------------------------------------------------------------------
1153 
1154    change_color
1155 
1156    Notes:
1157 
1158       This routine resets existing non-black colors to the specified color
1159       (for backward-compatibility with pre-v4.4 -G and -O flags).
1160 
1161 */
change_color(int col)1162 void change_color (int col)
1163 {
1164    int i;
1165 
1166    for (i = SUN; i <= SAT; i++) {
1167       if (day_color[i] != BLACK) day_color[i] = col;
1168    }
1169 
1170    if (holiday_color != BLACK && holiday_color != HOLIDAY_DEFAULT) {
1171       holiday_color = col;
1172    }
1173    return;
1174 }
1175 
1176 /* ---------------------------------------------------------------------------
1177 
1178    get_flag
1179 
1180    Notes:
1181 
1182       This routine looks up "flag" in 'flag_tbl'.  It returns a pointer to its
1183       entry (or NULL if not found).
1184 
1185 */
get_flag(char flag)1186 FLAG_USAGE * get_flag (char flag)
1187 {
1188    FLAG_USAGE *pflag;
1189 
1190    for (pflag = flag_tbl; pflag->flag; pflag++) {
1191       if (flag == pflag->flag) return pflag;
1192    }
1193 
1194    return flag ? NULL : pflag;   /* '\0' is a valid flag */
1195 }
1196 
1197 /* ---------------------------------------------------------------------------
1198 
1199    set_debug_flag
1200 
1201    Notes:
1202 
1203       This routine looks up "flag" in the debug flag table and sets selected
1204       bits in the debug flag word (clearing word if "flag" is NULL).
1205 
1206 */
set_debug_flag(char * flag)1207 void set_debug_flag (char *flag)
1208 {
1209    char f;
1210    DEBUG_INFO *pd;
1211 
1212    if (!flag) {   /* clear all if NULL */
1213       debug_flags = 0;
1214       return;
1215    }
1216 
1217    /* loop through all characters in "flag", setting corresponding
1218     * bits in debug_flags
1219     */
1220    while ((f = *flag++) != '\0') {
1221       for (pd = debug_info; pd->flag; pd++) {
1222          if (pd->flag == f) debug_flags |= pd->value;
1223       }
1224    }
1225    return;
1226 }
1227 
1228 /* ---------------------------------------------------------------------------
1229 
1230    display_usage
1231 
1232    Notes:
1233 
1234       This routine prints a message explaining correct usage of the command-line
1235       arguments and flags.
1236 
1237       If "fullmsg" is true, it print associated detailed text.
1238 
1239 */
display_usage(FILE * fp,int fullmsg)1240 void display_usage (FILE *fp,       /* destination of usage message */
1241                     int fullmsg)    /* print complete message? */
1242 {
1243    FLAG_MSG *pflag;
1244    PARAM_MSG *ppar;
1245    char buf[30], *p, flag, *meta;
1246    int nchars, first, i, indent, n;
1247 
1248    sprintf(buf, "%s: %s", W_USAGE, progname);
1249    nchars = indent = strlen(buf);
1250    first = TRUE;
1251    meta = p = NULL;
1252    fprintf(fp, "\n%s", buf);
1253 
1254    /* convert various values to strings for printing (cf. pcallang.h) */
1255    sprintf(Xtval, "%d", 0);
1256    sprintf(Ytval, "%d", 0);
1257    sprintf(Xsval, "%.3f", 1.0);
1258    sprintf(Ysval, "%.3f", 1.0);
1259 
1260    sprintf(Ncopy, "%d", NCOPY);
1261 
1262    /* Display the basic command-line syntax message (by group of flags)... */
1263 
1264    for (pflag = flag_msg; (flag = pflag->flag) != '\0'; pflag++) {
1265       if (flag == '\n') {   /* end of group? */
1266          if (p) *p = '\0';
1267          if (meta) {   /* append metavariable name */
1268             strcat(buf, " ");
1269             strcat(buf, meta);
1270          }
1271          strcat(buf, "]");
1272          n = strlen(buf);
1273          if (nchars + n > SCREENWIDTH) {   /* does it fit on line? */
1274             fprintf(fp, "\n");   /* no - start new one */
1275             for (nchars = 0; nchars < indent; nchars++) fprintf(fp, " ");
1276          }
1277          fprintf(fp, "%s", buf);
1278          nchars += n;
1279          first = TRUE;
1280       }
1281       else if (flag != ' ') {   /* accumulate flags for group */
1282          if (first) {
1283             sprintf(buf, " [");
1284             p = buf + strlen(buf);
1285          }
1286          else *p++ = '|';
1287 
1288          *p++ = '-';
1289          *p++ = flag;
1290          meta = pflag->meta;   /* save metavariable name */
1291          first = FALSE;
1292       }
1293    }
1294 
1295    /* After displaying all the possible flag options, we now loop to append a
1296       display of the various possible formats of the numeric parameter
1297       descriptions (starting year, starting month, and number of months, in
1298       various permutations)... */
1299 
1300    for (i = 0; i < PARAM_MSGS; i++) {
1301       sprintf(buf, " [%s]%s", param_msg[i].desc, i < PARAM_MSGS - 1 ? " |" : "");
1302       n = strlen(buf);
1303       if (nchars + n > SCREENWIDTH) {   /* does it fit on line? */
1304          fprintf(fp, "\n");   /* no - start new one */
1305          for (nchars = 0; nchars < indent; nchars++) fprintf(fp, " ");
1306       }
1307       fprintf(fp, "%s", buf);
1308       nchars += n;
1309    }
1310 
1311    fprintf(fp, "\n\n");
1312    if (! fullmsg) {
1313       fprintf(fp, USAGE_MSG, progname, F_HELP);
1314       return;
1315    }
1316 
1317    /* Display detailed descriptions of each of the possible command-line
1318       flags... */
1319 
1320    for (pflag = flag_msg; (flag = pflag->flag) != '\0'; pflag++) {
1321       if (flag == '\n') {   /* newline?  print and quit */
1322          fprintf(fp, "\n");
1323          continue;
1324       }
1325       p = buf;   /* copy flag and metavariable to buffer */
1326       if (flag != ' ') *p++ = '-';
1327 
1328       *p++ = flag;
1329 
1330       *p = '\0';
1331       if (pflag->meta) sprintf(p, " %s", pflag->meta);
1332 
1333       fprintf(fp, "\t%-20.20s", buf);
1334       if (pflag->text) fprintf(fp, "%s", pflag->text);
1335 
1336       /* print default value if specified */
1337       if (pflag->def) {
1338          fprintf(fp, " (%s: %s)", W_DEFAULT, pflag->def[0] ? pflag->def : "\"\"" );
1339       }
1340       fprintf(fp, "\n");
1341 
1342       /* special case - print color messages after F_OUTLINE_GRAY */
1343       if (flag == F_OUTLINE_GRAY) {
1344          fprintf(fp, "\t%20s  (%s: %s)\n", "", W_DEFAULT, color_msg());
1345       }
1346 
1347    }
1348 
1349    /* now print the information about the numeric parameters */
1350 
1351    for (ppar = param_msg; ppar->desc; ppar++) {
1352       fprintf(fp, "\t%-16.16s%s\n", ppar->desc, ppar->text);
1353    }
1354 
1355    fprintf(fp, "\n");
1356 
1357    return;
1358 }
1359 
1360 /* ---------------------------------------------------------------------------
1361 
1362    recalc_paper_parameters
1363 
1364    Notes:
1365 
1366       This routine sets up all the variables which are associated with a new
1367       setting of 'paper size'.
1368 
1369 */
recalc_paper_parameters(int paper_size_idx)1370 void recalc_paper_parameters (int paper_size_idx)
1371 {
1372    page_dim_short_axis_pts = paper_info[paper_size_idx].page_dim_short_axis_pts;
1373    page_dim_long_axis_pts = paper_info[paper_size_idx].page_dim_long_axis_pts;
1374 
1375    daybox_width_pts = paper_info[paper_size_idx].daybox_width_pts;
1376    daybox_height_pts = paper_info[paper_size_idx].daybox_height_pts;
1377 
1378    xsval_pgm = ysval_pgm = (double)page_dim_short_axis_pts / (double)page_dim_long_axis_pts;
1379    xtval_pgm = (page_dim_long_axis_pts - (7 * daybox_width_pts)) / 2;
1380 
1381    if ((rotate == PORTRAIT) && do_whole_year) {
1382       ytval_pgm = page_dim_short_axis_pts + TOP_OF_CAL_BOXES_PTS +
1383          6 * (daybox_height_pts - paper_info[paper_size_idx].daybox_height_pts);
1384    }
1385    else if ((rotate == PORTRAIT) && !do_whole_year) {
1386       /*
1387        * Copied from the 'ReadMe' file for v4.6:
1388        *
1389        *    The date box height for single-month portrait-mode calendars has
1390        *    been expanded from 80 points to 117.  If you prefer the original
1391        *    date box proportions (basically a scaled-down landscape-mode
1392        *    calendar), compile with -DORIG_BOX_HEIGHT.
1393        */
1394 #ifndef ORIG_BOX_HEIGHT
1395       daybox_height_pts = (page_dim_long_axis_pts - DEFAULT_MARGIN_BOTTOM_PTS +
1396                            TOP_OF_CAL_BOXES_PTS) / 6 / ysval_pgm;
1397 #endif
1398       ytval_pgm = (page_dim_long_axis_pts + TOP_OF_CAL_BOXES_PTS) / ysval_pgm;
1399    }
1400    else {   /* landscape mode */
1401       xsval_pgm = ysval_pgm = 1.0;
1402       ytval_pgm = TOP_OF_CAL_BOXES_PTS;
1403    }
1404    return;
1405 }
1406 
1407 /* ---------------------------------------------------------------------------
1408 
1409    get_args
1410 
1411    Notes:
1412 
1413       This routine walks the argument list, parsing all arguments but processing only
1414       those specified (in 'flag_tbl[]') to be processed this pass.
1415 
1416       It returns TRUE if OK, FALSE if invalid flag was found.
1417 
1418       The first parameter is the argument list.
1419 
1420       The second parameter is the current pas ('P_xxx').
1421 
1422       The third parameter is for error messages.
1423 
1424       The fourth parameter is a flag to indicate that this routine should
1425       parse and save the numeric arguments.
1426 
1427 */
get_args(char ** argv,int curr_pass,char * where,int get_numargs)1428 int get_args (char **argv, int  curr_pass, char *where, int  get_numargs)
1429 {
1430    char *parg, *opt, *p, *pass, tmpbuf[STRSIZ], c, sym[STRSIZ];
1431    FLAG_USAGE *pflag, *pf;
1432    int i, flag, sv_debug;
1433    int flags_ok = TRUE;   /* return value */
1434    FILE *fp = stdout;   /* for piping "help" message */
1435 
1436    /*
1437     * If argument follows flag (immediately or as next parameter), return
1438     * pointer to it (and bump argv if necessary); else return NULL
1439     */
1440 #define GETARG() (*(*argv + 2) ? *argv + 2 : \
1441                   (*(argv+1) && **(argv+1) != '-' ? *++argv : NULL))
1442 
1443    /* set up pass name to print in debug messages */
1444 
1445    pass = where ? where :
1446       curr_pass == P_CMD0 ? "CMD0" :
1447       curr_pass == P_ENV  ? "ENV"  :
1448       curr_pass == P_CMD1 ? "CMD1" :
1449       curr_pass == P_CMD2 ? "CMD2" :
1450       "OPT"  ;
1451 
1452    /* Walk argument list, ignoring first element (program name) */
1453 
1454    while ((opt = *++argv) != NULL) {
1455 
1456       /* Assume that any non-flag argument is a numeric argument */
1457       if (*opt != '-') {
1458          if (get_numargs && nargs < MAXARGS) {
1459             if (! IS_NUMERIC(opt)) goto bad_par;
1460             numargs[nargs++] = atoi(opt);
1461             if (DEBUG(DEBUG_OPTS)) fprintf(stderr, "%s: %s\n", pass, opt);
1462          }
1463          continue;
1464       }
1465 
1466       /* Check that flag is legal */
1467       if (! (pflag = get_flag(flag = opt[1])) ) goto bad_par;
1468 
1469       /* get optional argument even if flag not processed this pass */
1470       parg = pflag->has_arg ? GETARG() : NULL;
1471 
1472       /* skip to next flag if not to be processed this pass */
1473       if (! (pflag->passes & curr_pass)) {
1474          if (curr_pass == P_OPT) {
1475             fprintf(stderr, E_FLAG_IGNORED, progname, flag, DATE_FILE, where);
1476          }
1477          continue;
1478       }
1479 
1480       /* echo pass name and flag if debugging on */
1481       if (DEBUG(DEBUG_OPTS)) {
1482          fprintf(stderr, "%s: -%c%s%s\n", pass, flag, parg ? " " : "", parg ? parg : "");
1483       }
1484 
1485       /* convert escape sequences in command-line parameters */
1486       if (parg && (curr_pass == P_CMD1 || curr_pass == P_CMD2)) {
1487          cvt_escape(tmpbuf, parg);
1488          parg = tmpbuf;
1489       }
1490 
1491       switch (flag) {
1492 
1493       case F_INITIALIZE:   /* reset all flags to defaults */
1494 
1495          /* set up a command line to reset all of the flags; call get_args()
1496           * recursively to parse it (note that some of the flags must be reset
1497           * explicitly, as no command-line flags exist to reset them)
1498           */
1499 
1500          /* reset flags described above */
1501          julian_dates  = JULIAN_DATES;
1502          draw_moons    = DRAW_MOONS;
1503          do_whole_year = DO_WHOLE_YEAR;
1504          blank_boxes   = BLANK_BOXES;
1505          output_type   = OUTPUT_TYPE;
1506          small_cal_pos = SMALL_CAL_POS;
1507 
1508          /* select program default for landscape/portrait mode (must be done
1509           * first because -[xXyY] depend on it) and US/European date styles
1510           */
1511          sprintf(lbuf, "pcal -%c -%c",
1512 #if (ROTATE == LANDSCAPE)
1513                  F_LANDSCAPE,
1514 #else
1515                  F_PORTRAIT,
1516 #endif
1517 
1518 #if (DATE_STYLE == USA_DATES)
1519                  F_USA_DATES
1520 #else
1521                  F_EUR_DATES
1522 #endif
1523                  );
1524 
1525          p = lbuf + strlen(lbuf);
1526 
1527          /* all other flags take arguments and are reset by specifying the
1528           * flag without an argument
1529           */
1530          for (pf = flag_tbl; pf->flag; pf++) {
1531             if ((pf->passes & curr_pass) && pf->has_arg) {
1532                sprintf(p, " -%c", pf->flag);
1533                p += strlen(p);
1534             }
1535          }
1536 
1537          /* split new command line into words; parse it */
1538          (void) loadwords(words, lbuf);
1539          (void) get_args(words, curr_pass, NULL, FALSE);
1540 
1541          /* -G and -O without arguments change some day colors (for
1542           * backward-compatibility with pre-v4.4 versions), so initialize the
1543           * day colors explicitly
1544           */
1545          INIT_COLORS;
1546 
1547          /* also disable the time zone flag explicitly */
1548          tz_flag = FALSE;
1549 
1550          break;
1551 
1552       case F_BLACK_DAY:   /* print day in black or gray */
1553       case F_GRAY_DAY:
1554          if (parg) set_color(parg, flag == F_BLACK_DAY ? BLACK : GRAY);
1555          else INIT_COLORS;   /* reset to defaults */
1556          break;
1557 
1558       case F_OUTLINE:   /* print day in outline or outline-gray */
1559       case F_OUTLINE_GRAY:
1560          /* ignore "-Gall" etc. on second command-line pass; this is to avoid
1561           * changing the definition of "weekday", et. al. after the date file
1562           * has already been read
1563           */
1564          if (parg) {
1565             if (curr_pass != P_CMD2) {
1566                set_color(parg, flag == F_OUTLINE ? OUTLINE : OUTLINE_GRAY);
1567             }
1568          }
1569          else {
1570             /* special hack for backward compatibility with v4.3 and earlier:
1571              * -G or -O alone change all non-black days to the selected color
1572              */
1573             change_color(flag == F_OUTLINE ? OUTLINE : OUTLINE_GRAY);
1574          }
1575 
1576          break;
1577 
1578       case F_DAY_FONT:   /* specify alternate day font */
1579          define_font(datefont, parg, DATEFONT);
1580          break;
1581 
1582       case F_NOTES_FONT:   /* specify alternate notes font */
1583          define_font(notesfont, parg, NOTESFONT);
1584          break;
1585 
1586       case F_TITLE_FONT:   /* specify alternate title font */
1587          define_font(titlefont, parg, TITLEFONT);
1588          break;
1589 
1590       case F_REMAP_FONT:   /* specify 8-bit font mapping */
1591          if (parg) {
1592             if (ci_strncmp(parg, MAPPING_LATIN_1, strlen(parg)) == 0)
1593                mapfonts = ENC_LATIN_1;
1594             else if (ci_strncmp(parg, MAPPING_LATIN_2, strlen(parg)) == 0)
1595                mapfonts = ENC_LATIN_2;
1596             else if (ci_strncmp(parg, MAPPING_LATIN_3, strlen(parg)) == 0)
1597                mapfonts = ENC_LATIN_3;
1598             else if (ci_strncmp(parg, MAPPING_LATIN_4, strlen(parg)) == 0)
1599                mapfonts = ENC_LATIN_4;
1600             else if (ci_strncmp(parg, MAPPING_CYRILLIC, strlen(parg)) == 0)
1601                mapfonts = ENC_CYRILLIC;
1602             /* else if (ci_strncmp(parg, MAPPING_ARABIC, strlen(parg)) == 0) */
1603             /* mapfonts = ENC_ARABIC; */
1604             else if (ci_strncmp(parg, MAPPING_GREEK, strlen(parg)) == 0)
1605                mapfonts = ENC_GREEK;
1606             /* else if (ci_strncmp(parg, MAPPING_HEBREW, strlen(parg)) == 0) */
1607             /* mapfonts = ENC_HEBREW; */
1608             else if (ci_strncmp(parg, MAPPING_LATIN_5, strlen(parg)) == 0)
1609                mapfonts = ENC_LATIN_5;
1610             else if (ci_strncmp(parg, MAPPING_LATIN_6, strlen(parg)) == 0)
1611                mapfonts = ENC_LATIN_6;
1612             else if (ci_strncmp(parg, MAPPING_THAI, strlen(parg)) == 0)
1613                mapfonts = ENC_THAI;
1614             else if (ci_strncmp(parg, MAPPING_LATIN_7, strlen(parg)) == 0)
1615                mapfonts = ENC_LATIN_7;
1616             else if (ci_strncmp(parg, MAPPING_LATIN_8, strlen(parg)) == 0)
1617                mapfonts = ENC_LATIN_8;
1618             else if (ci_strncmp(parg, MAPPING_LATIN_9, strlen(parg)) == 0)
1619                mapfonts = ENC_LATIN_9;
1620             /* else if (ci_strncmp(parg, MAPPING_LATIN_10, strlen(parg)) == 0) */
1621             /* mapfonts = ENC_LATIN_10; */
1622             else if (ci_strncmp(parg, MAPPING_KOI8_R, strlen(parg)) == 0)
1623                mapfonts = ENC_KOI8_R;
1624             else if (ci_strncmp(parg, MAPPING_KOI8_U, strlen(parg)) == 0)
1625                mapfonts = ENC_KOI8_U;
1626             else if (ci_strncmp(parg, MAPPING_ROMAN8, strlen(parg)) == 0)
1627                mapfonts = ENC_ROMAN8;
1628             else
1629                mapfonts = ENC_NONE;
1630          } else /* Use the value defined in the 'Makefile' */
1631             mapfonts = MAPFONTS;
1632 
1633          map_default = !parg;
1634          break;
1635 
1636       case F_EMPTY_CAL:   /* generate empty calendar */
1637          datefile_type = NO_DATEFILE;
1638          strcpy(datefile, "");
1639          break;
1640 
1641       case F_DATE_FILE:   /* specify alternate date file */
1642          datefile_type = parg ? USER_DATEFILE : SYS_DATEFILE;
1643          strcpy(datefile, parg ? parg : "");
1644          break;
1645 
1646       case F_OUT_FILE:   /* specify alternate output file */
1647          oflag = TRUE;
1648          strcpy(outfile, parg ? parg : "");
1649          break;
1650 
1651       case F_LANDSCAPE:   /* generate landscape calendar */
1652          rotate = LANDSCAPE;
1653          recalc_paper_parameters(paper_size);
1654 
1655          /* Undefine any old paper orientation symbol and define a new symbol
1656           *   to match the new paper orientation.
1657           */
1658          do_undef("ORIENTATION_PORTRAIT");
1659          do_define("ORIENTATION_LANDSCAPE");
1660          break;
1661 
1662       case F_PORTRAIT:   /* generate portrait calendar */
1663          rotate = PORTRAIT;
1664          recalc_paper_parameters(paper_size);
1665 
1666          /* Undefine any old paper orientation symbol and define a new symbol
1667           *   to match the new paper orientation.
1668           */
1669          do_undef("ORIENTATION_LANDSCAPE");
1670          do_define("ORIENTATION_PORTRAIT");
1671          break;
1672 
1673       case F_PAPERSIZE:   /* specify paper size */
1674 
1675          /* Undefine 'papersize_XXX' (where XXX is the name of the
1676           * previously-selected paper size).
1677           */
1678          sprintf(sym, "papersize_%s", paper_info[paper_size].name);
1679          do_undef(sym);
1680 
1681          paper_size = PAPERSIZE_DEFAULT;
1682          if (parg) {
1683             int found = FALSE;
1684             for (i = 0; i < NUM_PAPER_SIZES; i++) {
1685                if (ci_strncmp(paper_info[i].name, parg, strlen(paper_info[i].name)) == 0) {
1686                   paper_size = i;
1687                   found = TRUE;
1688                   break;
1689                }
1690             }
1691             if (!found) {
1692                fprintf(stderr, E_ILL_PAPERSIZE, progname, parg);
1693             }
1694          }
1695          recalc_paper_parameters(paper_size);
1696 
1697          /* Define 'papersize_XXX' (where XXX is the name of the
1698           * newly-selected paper size).
1699           */
1700          sprintf(sym, "papersize_%s", paper_info[paper_size].name);
1701          do_define(sym);
1702 
1703          break;
1704 
1705       case F_HELP:   /* request version info and/or help */
1706       case F_USAGE:
1707       case F_VERSION:
1708          /* PAGER_ENV (cf. pcaldefs.h) defines the name of an environment
1709           * variable which, if set, points to the appropriate pager (e.g.,
1710           * "more", "less", "pg") for piping the "help" message (Un*x systems
1711           * only)
1712           */
1713 #ifdef PAGER_ENV
1714          if (flag == F_HELP) {
1715             FILE *pfp;
1716             char *pager, *p;
1717 
1718             pager = (p = getenv(PAGER_ENV)) ? p : PAGER_DEFAULT;
1719             /* null pointer or string: no paging */
1720             if (pager && *pager && (pfp = popen(pager, "w")) != (FILE *)NULL) {
1721                fp = pfp;
1722             }
1723          }
1724 #endif
1725          fprintf(fp, "%s\n", VERSION_STRING + 4);
1726          if (flag != F_VERSION) display_usage(fp, flag == F_HELP);
1727          fflush(fp);
1728 
1729 #ifdef PAGER_ENV
1730          if (fp != stdout) pclose(fp);
1731 #endif
1732 
1733          exit(EXIT_SUCCESS);
1734          break;
1735 
1736       case F_MOON_4:   /* draw four moons */
1737       case F_MOON_ALL:   /* draw a moon for each day */
1738          draw_moons = flag == F_MOON_ALL ? ALL_MOONS : SOME_MOONS;
1739          break;
1740 
1741       case F_DEFINE:   /* define preprocessor symbol */
1742          (void) do_define(parg);
1743          break;
1744 
1745       case F_UNDEF:   /* undef preprocessor symbol */
1746          (void) do_undef(parg);
1747          break;
1748 
1749       case F_L_FOOT:   /* specify alternate left foot */
1750          strcpy(lfoot, parg ? parg : LFOOT);
1751          break;
1752 
1753       case F_C_FOOT:   /* specify alternate center foot */
1754          strcpy(cfoot, parg ? parg : CFOOT);
1755          break;
1756 
1757       case F_R_FOOT:   /* specify alternate right foot */
1758          strcpy(rfoot, parg ? parg : RFOOT);
1759          break;
1760 
1761       case F_NOTES_HDR:   /* specify alternate notes header */
1762          strcpy(notes_hdr, parg ? parg : default_notes_hdr);
1763          break;
1764 
1765       case F_FIRST_DAY:   /* select starting day of week */
1766          if (parg) {
1767             if ((i = get_weekday(parg, FALSE)) != NOT_WEEKDAY) {
1768                first_day_of_week = i;
1769             }
1770             else if (parg[0] >= '0' && parg[0] <= '6') {
1771                first_day_of_week = parg[0] - '0';
1772             }
1773          }
1774          else first_day_of_week = FIRST_DAY;
1775          break;
1776 
1777       case F_USA_DATES:   /* select American date style */
1778       case F_EUR_DATES:   /* select European date style */
1779          date_style = flag == F_USA_DATES ? USA_DATES : EUR_DATES;
1780          break;
1781 
1782       case F_X_TRANS:   /* set x-axis translation factor */
1783          if (parg) xtval_user = atoi(parg);
1784          break;
1785 
1786       case F_Y_TRANS:   /* set y-axis translation factor */
1787          if (parg) ytval_user = atoi(parg);
1788          break;
1789 
1790       case F_X_SCALE:   /* set x-axis scaling factor */
1791          if (parg) xsval_user = atof(parg);
1792          break;
1793 
1794       case F_Y_SCALE:   /* set y-axis scaling factor */
1795          if (parg) ysval_user = atof(parg);
1796          break;
1797 
1798       case F_JULIAN:   /* display Julian dates */
1799       case F_JULIAN_ALL:   /* Julian date/days remaining */
1800          julian_dates = flag == F_JULIAN_ALL ? ALL_JULIANS : SOME_JULIANS;
1801          break;
1802 
1803       case F_WHOLE_YEAR:   /* print whole year at once */
1804          do_whole_year = !(DO_WHOLE_YEAR);  /* toggle default */
1805          if (do_whole_year) (void) do_define(DEF_WHOLE_YEAR);
1806          recalc_paper_parameters(paper_size);
1807          break;
1808 
1809       case F_CALENDAR:   /* generate "calendar(1)" input */
1810          output_type = OUTPUT_CAL;
1811          break;
1812 
1813       case F_HTML:   /* generate HTML table */
1814          output_type = OUTPUT_HTML;
1815          do_define(DEF_HTML);
1816          break;
1817 
1818       case F_1COLUMN:   /* print one column per month (HTML) */
1819          one_column = 1;
1820          break;
1821 
1822       case F_BLANK_BOXES:   /* suppress shading unused boxes */
1823          blank_boxes = !(BLANK_BOXES);
1824          break;
1825 
1826       case F_NUM_PAGES:   /* print multiple copies of each page */
1827          ncopy = parg ? atoi(parg) : NCOPY;
1828          break;
1829 
1830       case F_SC_NONE:   /* suppress small calendars */
1831          small_cal_pos = SC_NONE;
1832          break;
1833 
1834       case F_SC_FIRST:   /* reposition of small calendars */
1835       case F_SC_SPLIT:
1836          small_cal_pos = flag == F_SC_FIRST ? SC_FIRST : SC_SPLIT;
1837          break;
1838 
1839       case F_SHADING:   /* set date/fill shading levels */
1840          define_shading(shading, parg, SHADING);
1841          break;
1842 
1843       case F_TIMEZONE:   /* select alternate time zone */
1844          strcpy(time_zone, parg ? parg : TIMEZONE);
1845          tz_flag = TRUE;
1846          break;
1847 
1848       case F_SETLANG:   /* select output lang for month/days */
1849          /* undefine lang_XX (where XX is current language) */
1850          do_undef(gen_lang_sym(output_language));
1851 
1852          output_language = LANG_DEFAULT;
1853          if (parg) {
1854             for (i = 0; i < NUM_LANGUAGES; i++) {
1855                if (ci_strncmp(lang_id[i], parg,
1856                               MIN_LANG_LEN) == 0 ) {
1857                   output_language = i;
1858                   break;
1859                }
1860             }
1861          }
1862 
1863          /* define lang_XX (where XX is new current language) */
1864          do_define(gen_lang_sym(output_language));
1865          break;
1866 
1867       case F_TYPEFACE:   /* select font style (Bold/Italic) */
1868          c = parg ? toupper(parg[0]) : ROMAN;
1869          fontstyle[0] = (c == BOLD || c == ITALIC) ? c : ROMAN;
1870          fontstyle[1] = '\0';   /* just in case */
1871          break;
1872 
1873       case F_TITLEALIGN:  /* specify title alignment (left/center/right) */
1874          strcpy (title_align, parg && IS_TITLE_ALIGN(parg) ? parg : TITLE_ALIGN);
1875          break;
1876 
1877       case F_DEBUG:   /* turn on debugging (undocumented) */
1878          sv_debug = DEBUG(DEBUG_OPTS);
1879          set_debug_flag(parg);
1880 
1881          /* print -ZO flag if first time set */
1882          if (!sv_debug && DEBUG(DEBUG_OPTS)) {
1883             fprintf(stderr, "%s: -%c%s\n", pass, flag, parg ? parg : "");
1884          }
1885          break;
1886 
1887       case '-' :   /* accept - and -- as dummy flags */
1888       case '\0':
1889          break;
1890 
1891       default:   /* missing case label if reached!!! */
1892 
1893 bad_par:   /* unrecognized parameter */
1894 
1895          fprintf(stderr, E_ILL_OPT, progname, opt);
1896          if (where) {
1897             fprintf(stderr, E_ILL_OPT2,
1898                     curr_pass == P_ENV ? ENV_VAR :
1899                     curr_pass == P_OPT ? DATE_FILE : "",
1900                     where);
1901          }
1902          fprintf(stderr, "\n");
1903          flags_ok = FALSE;
1904          break;
1905       }
1906    }
1907 
1908    /* if we read the numeric arguments, validate and interpret them */
1909    if (get_numargs) flags_ok &= check_numargs();
1910 
1911    return flags_ok;
1912 }
1913 
1914 /* ---------------------------------------------------------------------------
1915 
1916    check_numargs
1917 
1918    Notes:
1919 
1920       This routine validates and interprets numeric command-line parameters.
1921 
1922       It returns 'TRUE' if all are OK.  It prints an error message and returns
1923       'FALSE' if not.
1924 
1925 */
check_numargs(void)1926 int check_numargs (void)
1927 {
1928    /* Validate non-flag (numeric) parameters */
1929 
1930    struct tm *p_tm;   /* for getting current month/year */
1931    time_t tmp;
1932    int params_ok = TRUE;   /* return value */
1933 
1934    switch (nargs) {
1935    case 0:   /* no arguments - print current month and/or year */
1936       time(&tmp);
1937       p_tm = localtime(&tmp);
1938       init_month = p_tm->tm_mon + 1;
1939       /* revised to conform to standard Un*x behavior 01/25/00 */
1940       /* init_year = century() + (p_tm->tm_year % 100); */
1941       init_year = TM_YEAR + p_tm->tm_year;
1942       nmonths = 1;
1943       break;
1944 
1945    case 1:   /* one argument - print entire year */
1946       init_month = JAN;
1947       init_year = numargs[0];
1948       nmonths = 12;
1949       break;
1950 
1951    default:   /* two or three arguments - print one or more months */
1952       init_month = numargs[0];
1953       init_year = numargs[1];
1954       nmonths = nargs > 2 ? numargs[2] : 1;
1955       break;
1956    }
1957 
1958    if (nmonths < 1) nmonths = 1;   /* ensure at least one month */
1959 
1960    /* check range of month and year */
1961 
1962    if (init_month < JAN || init_month > DEC) {
1963       fprintf(stderr, E_ILL_MONTH, progname, init_month, JAN, DEC);
1964       params_ok = FALSE;
1965    }
1966 
1967    if (init_year >= 0 && init_year < 100) {   /* treat nn as CCnn */
1968       init_year += century();
1969    }
1970 
1971    if (init_year < MIN_YR || init_year > MAX_YR) {
1972       fprintf(stderr, E_ILL_YEAR, progname, init_year, MIN_YR, MAX_YR);
1973       params_ok = FALSE;
1974    }
1975 
1976    return params_ok;
1977 }
1978 
1979 /* ---------------------------------------------------------------------------
1980 
1981    color_msg
1982 
1983    Notes:
1984 
1985       This routine returns a character string explaining the default day
1986       colors.  It assumes that defaults consist of black and at most one other
1987       color.
1988 
1989 */
color_msg(void)1990 char *color_msg (void)
1991 {
1992    int i, ngray = 0, others, gray = GRAY;
1993    static char msg[80], *alt_color;
1994 
1995    for (i = SUN; i <= SAT; i++) { /* count "logical gray" weekdays */
1996       if (default_color[i] != BLACK) {
1997          gray = default_color[i];
1998          ngray++;
1999       }
2000    }
2001 
2002    alt_color = color_names[gray];   /* map "logical gray" to its name */
2003 
2004    if (ngray == 0 || ngray == 7) {   /* all same color? */
2005       sprintf(msg, COLOR_MSG_1, ngray ? alt_color : W_BLACK);
2006       return msg;
2007    }
2008 
2009    others = ngray <= 3 ? BLACK : gray;   /* no - get predominant color */
2010    msg[0] = '\0';
2011    for (i = SUN; i <= SAT; i++) {
2012       if (default_color[i] != others) {
2013          strncat(msg, days_ml[LANG_DEFAULT][i], MIN_DAY_LEN);
2014          strcat(msg, "/");
2015       }
2016    }
2017 
2018    LASTCHAR(msg) = ' ';
2019 
2020    sprintf(msg + strlen(msg), COLOR_MSG_2,
2021            others == BLACK ? alt_color : W_BLACK,
2022            others == BLACK ? W_BLACK : alt_color);
2023    return msg;
2024 }
2025 
2026 /* ---------------------------------------------------------------------------
2027 
2028    alt_fopen
2029 
2030    Notes:
2031 
2032       This routine attempts to open a file in one of several paths in a
2033       NULL-terminated path list.  If successful, it returns the (opened) file
2034       pointer and fills in the full path name.  If unsuccessful, it returns
2035       NULL.
2036 
2037       The first parameter is the full (output) path name.
2038 
2039       The second parameter is the base name (or full path spec).
2040 
2041       The third parameter is the NULL-terminated path list.
2042 
2043       The fourth parameter is the permission level requested.
2044 
2045 */
alt_fopen(char * fullpath,char * name,char * pathlist[],char * access)2046 FILE *alt_fopen (char *fullpath, char *name, char *pathlist[], char *access)
2047 {
2048    char **path;
2049    FILE *fp;
2050 
2051    /* print complete list of paths if debugging path names */
2052    if (DEBUG(DEBUG_PATHS)) {
2053       fprintf(stderr, "Searching for %s in the following paths:\n", name);
2054       for (path = pathlist; *path; path++) {
2055          fprintf(stderr, "  %s\n", **path ? *path : ".");
2056       }
2057    }
2058 
2059    /* search path list for requested file */
2060    for (path = pathlist; *path; path++) {
2061       mk_filespec(fullpath, *path, name);
2062       if ((fp = fopen(fullpath, access)) != NULL) {
2063          if (DEBUG(DEBUG_PATHS)) fprintf(stderr, "found %s\n", fullpath);
2064          return fp;
2065       }
2066    }
2067 
2068    fullpath[0] = '\0';   /* file not found */
2069    return NULL;
2070 }
2071 
2072 /* ---------------------------------------------------------------------------
2073 
2074    gen_lang_sym
2075 
2076    Notes:
2077 
2078       This routine creates a symbol name "lang_XX" corresponding to the
2079       language code passed as an argument (e.g., LANG_ENGLISH => "lang_en").
2080 
2081 */
gen_lang_sym(int lang)2082 char *gen_lang_sym (int lang)
2083 {
2084    static char buf[sizeof(DEF_LANG) + MIN_LANG_LEN + 1];
2085    char *p;
2086    int i;
2087 
2088    strcpy(buf, DEF_LANG);
2089    p = buf + strlen(buf);
2090    for (i = 0; i < MIN_LANG_LEN; i++) p[i] = lang_id[lang][i];
2091    p[i] = '\0';
2092 
2093    return buf;
2094 }
2095 
2096 /* ---------------------------------------------------------------------------
2097 
2098    main
2099 
2100    Notes:
2101 
2102       Main program - parse and validate command-line arguments, open files,
2103       generate PostScript boilerplate and code to generate calendars.
2104 
2105       Program structure:
2106 
2107       For maximum user flexibility, Pcal gives the user several different ways
2108       to set program flags and/or override earlier choices.  This necessitates
2109       that main() call get_args() (directly or indirectly) several times:
2110 
2111            a) to parse the command line, looking only for -Z flags (which turn
2112               on debugging information) and numeric parameters
2113 
2114            b) to parse environment variable PCAL_OPTS, if defined
2115 
2116            c) to parse the command line a second time, looking for options
2117               related to finding/interpreting the date file: -[cefhuvDU]
2118 
2119            d) main() calls read_datefile() to read and parse the date file; it
2120               in turn calls get_args() once per "opt" line in the date file
2121 
2122            e) to parse the command line one final time, allowing the user to
2123               override any flags other than those listed in c) above
2124 
2125       The rest of it is straightforward: main() attempts to open the output
2126       file (if any), and, if successful, calls write_psfile() to generate the
2127       PostScript output (or write_calfile() to generate the "calendar" input).
2128       Some minor housekeeping and we're done.
2129 
2130  */
main(int argc GCC_UNUSED,char ** argv)2131 int main (int argc GCC_UNUSED, char **argv)
2132 {
2133    FILE *dfp = NULL;   /* date file pointer */
2134    char *p, *pathlist[10];
2135    char tmp[STRSIZ];
2136    int n;
2137 
2138    /* get version from VERSION_STRING (for PostScript comments and version
2139     * symbol predefined in init_misc())
2140     */
2141    strcpy(tmp, VERSION_STRING + 4);
2142    p = strchr(tmp, ' ') + 1;   /* skip program name */
2143    *strchr(p, ' ') = '\0';   /* terminate after version */
2144    strcpy(version, p);
2145 
2146    init_misc();   /* handle initialization warts */
2147 
2148    /* extract root program name and program path - note that some systems
2149     * supply the full pathname and others just the root
2150     */
2151 
2152    strcpy(progname, **argv ? *argv : "pcal");
2153 
2154    if ((p = strrchr(progname, END_PATH)) != NULL) strcpy(progname, ++p);
2155 
2156 #ifndef BUILD_ENV_UNIX
2157    if ((p = strchr(progname, '.')) != NULL) *p = '\0';   /* strip suffix if non-Un*x */
2158 #endif
2159 
2160    mk_path(progpath, find_executable(*argv));
2161 
2162    /*
2163     * Get the arguments from a) the command line (pre-pass to pick up debug
2164     * and usage flags), b) the environment variable PCAL_OPTS, c) the first
2165     * command line pass, d) "opt" lines in the date file, and e) a final
2166     * command line pass, in that order
2167     */
2168 
2169    /* Make a preliminary pass to look for the debug flags (to ensure that -ZO
2170     * will print any flags set in PCAL_OPTS) and the usage flags (-h, -u, and
2171     * -v).  If none of the latter is specified, this pass will also validate
2172     * any numeric command-line arguments.
2173     */
2174    if (!get_args(argv, P_CMD0, NULL, TRUE)) {
2175       display_usage(stderr, FALSE);   /* invalid flag or param */
2176       exit(EXIT_FAILURE);
2177    }
2178 
2179    /* parse environment variable PCAL_OPTS as a command line */
2180 
2181    if ((p = getenv(PCAL_OPTS)) != NULL) {
2182       strcpy(lbuf, "pcal ");   /* dummy program name */
2183       strcat(lbuf, p);
2184       (void) loadwords(words, lbuf);   /* split string into words */
2185       if (! get_args(words, P_ENV, PCAL_OPTS, FALSE)) {
2186          display_usage(stderr, FALSE);
2187          exit(EXIT_FAILURE);
2188       }
2189    }
2190 
2191    /* parse command-line arguments once to find name of date file, etc. */
2192 
2193    (void) get_args(argv, P_CMD1, NULL, FALSE);
2194 
2195    /* if in whole-year mode, round number of months up to full year and set
2196     * default starting month to January of current year
2197     */
2198    if (do_whole_year) {
2199       nmonths = ((nmonths + 11) / 12) * 12;
2200       if (nargs == 0) init_month = JAN;
2201    }
2202 
2203    /* recalculate final month and year (latter needed for "year all") */
2204    final_month = (init_month + (nmonths - 1) - 1) % 12 + 1;
2205    final_year = init_year + ((nmonths - 1 + init_month - 1) / 12);
2206 
2207    /* Attempt to open the date file as specified by the [-e | -f] flags */
2208 
2209    switch (datefile_type) {
2210    case NO_DATEFILE:
2211       dfp = NULL;
2212       break;
2213 
2214    case USER_DATEFILE:
2215       /* Attempt to open user-specified calendar file: search first in the
2216        * current directory, then in PCAL_DIR (if defined), and finally in the
2217        * directory where the Pcal executable lives (if SEARCH_PCAL_DIR != 0).
2218        * It is a fatal error if the user-specified date file cannot be found.
2219        */
2220       n = 0;
2221       pathlist[n++] = "";
2222       if ((p = getenv(PCAL_DIR)) != NULL) pathlist[n++] = p;
2223 
2224 #if (SEARCH_PCAL_DIR != 0)
2225       pathlist[n++] = progpath;
2226 #endif
2227 
2228       pathlist[n] = NULL;
2229 
2230       strcpy(tmp, datefile);   /* save original name for error msg */
2231 
2232       if ((dfp = alt_fopen(datefile, tmp, pathlist, "r")) == NULL) {
2233          fprintf(stderr, E_FOPEN_ERR, progname, tmp);
2234          exit(EXIT_FAILURE);
2235       }
2236       break;
2237 
2238    case SYS_DATEFILE:
2239       /* Attempt to open system-specified calendar file: search first in
2240        * PCAL_DIR, then in HOME_DIR (current directory if neither is defined)
2241        * and finally in the directory where the Pcal executable lives (if
2242        * SEARCH_PCAL_DIR != 0).  It is not an error if the system-specified
2243        * date file cannot be opened; Pcal will simply generate an empty
2244        * calendar.
2245        */
2246       n = 0;
2247       if ((p = getenv(PCAL_DIR)) != NULL) pathlist[n++] = p;
2248       if ((p = getenv(HOME_DIR)) != NULL) pathlist[n++] = p;
2249       if (n == 0) pathlist[n++] = "";
2250 
2251 #if (SEARCH_PCAL_DIR != 0)
2252       pathlist[n++] = progpath;
2253 #endif
2254 
2255       pathlist[n] = NULL;
2256 
2257       dfp = alt_fopen(datefile, DATEFILE, pathlist, "r");
2258 
2259       /* if the date file has not been found and ALT_DATEFILE is defined,
2260        * search same paths for ALT_DATEFILE before giving up
2261        */
2262 #ifdef ALT_DATEFILE
2263       if (!dfp) dfp = alt_fopen(datefile, ALT_DATEFILE, pathlist, "r");
2264 #endif
2265 
2266       break;
2267    }
2268 
2269    /* read the date file (if any) and build internal data structure */
2270 
2271    if (dfp) {
2272       curr_year = init_year;
2273       read_datefile(dfp, datefile);
2274       fclose(dfp);
2275    } else datefile[0] = '\0';   /* for PostScript comment */
2276 
2277    /* reparse command line - flags there supersede those in date file */
2278 
2279    (void) get_args(argv, P_CMD2, NULL, FALSE);
2280 
2281    /* if the user has not explicitly selected a mapping, choose one based on
2282     * the chosen language
2283     */
2284    if (map_default == TRUE) mapfonts = lang_mapping[output_language];
2285 
2286    /* fprintf(stderr, "Font mapping is %d\n", mapfonts); */
2287 
2288    /* select an appropriate color for holidays if not set explicitly */
2289    if (holiday_color == HOLIDAY_DEFAULT) holiday_color = select_color();
2290 
2291    /* done with the arguments and flags - try to open the output file */
2292 
2293    /* use default output files (cf. pcaldefs.h) if -o flag not used */
2294    if (!oflag && output_type == OUTPUT_PS) strcpy(outfile, PS_OUTFILE);
2295    if (!oflag && output_type == OUTPUT_HTML) strcpy(outfile, HTML_OUTFILE);
2296 
2297    /* reopen stdout as alternate file if one was specified */
2298    if (*outfile && freopen(outfile, "w", stdout) == (FILE *) NULL) {
2299       fprintf(stderr, E_FOPEN_ERR, progname, outfile);
2300       exit(EXIT_FAILURE);
2301    }
2302 
2303    /* Set up the initial page-related values based on the default paper
2304     * size.
2305     */
2306    recalc_paper_parameters(paper_size);
2307 
2308    /* generate the "calendar", HTML, or PostScript code (cf. writefil.c) */
2309 
2310    if (output_type == OUTPUT_CAL) write_calfile();
2311    else if (output_type == OUTPUT_HTML) write_htmlfile();
2312    else write_psfile();
2313 
2314    cleanup();   /* free allocated data */
2315 
2316    exit(EXIT_SUCCESS);
2317 }
2318