1 /* ---------------------------------------------------------------------------
2 
3    writefil.c
4 
5    Notes:
6 
7       This file contains routines for writing the PostScript output.
8 
9    Revision history:
10 
11 	4.11.0
12 		B.Marr		2007-12-16
13 
14 		Allow the drawing of moon phase icons ('-m' or '-M') and
15 		Julian dates ('-j' or '-J') on yearly-format calendars.
16 
17 		Perform various cosmetic cleanups (mostly by adding blank
18 		lines) to the PostScript output.
19 
20 		B.Marr		2007-12-15
21 
22 		Fix bug whereby use of '-J' option caused garbage text
23 		(PostScript commands) to appear in place of the number of days
24 		remaining in the year.
25 
26 		Add support for new '-W' option, to specify horizontal
27 		alignment of the "Month/Year" title on monthly-format
28 		calendars, thanks to a patch from Todd Foster.
29 
30 		Remove long-obsolete external 'moon file' concept.  Now, we
31 		depend solely on the algorithmic determination of moon phases.
32 
33 		Rename some variables, structures, and/or routines to be
34 		clearer about their purpose and/or to allow easier searching
35 		with fewer "false positives".
36 
37 	4.10.0
38 		B.Marr		2006-07-19
39 
40 		Use an international (as opposed to USA-centric) date format
41 		and 2 more digits of precision in the debug output for moon
42 		phases ('-ZM' option).
43 
44 		Add the 'pcal' website URL to the PostScript output.
45 
46 		Eliminate 1-use macros for EPS-like output and put the code
47 		inline for clarity.
48 
49 		Reformatted comments and code to match my standards.
50 
51 		B.Marr		2006-07-12
52 
53 		Eliminate C++ style comments ('//').
54 
55 		Provide explicit casting in several spots to avoid warnings in
56 		a "gcc 3.4.2 on Solaris 8" environment, based on a report from
57 		David Mathog <mathog at mendel.bio.caltech.edu>.
58 
59 		Get rid of all the '#ifdef PROTOS' checks, which are pretty
60 		much obsolete these days and just needlessly clutter up the
61 		code.
62 
63 	4.9.0
64 		B.Marr		2005-08-10
65 
66 		Fix a long-standing bug whereby a centered "footer"
67 		specification with 'strftime()'-like date specifiers used in
68 		an HTML yearly calendar was using/showing the correct date
69 		values for the HTML 'title' but the wrong date values for the
70 		centered header string at the start of the actual displayed
71 		(HTML) content.
72 
73 		Eliminate the hack to support Esperanto via a custom,
74 		dedicated character encoding.  Esperanto is now handled
75 		generically by the 'Latin3' (ISO 8859-3) character encoding.
76 
77 		B.Marr		2005-08-02
78 
79 		Per a user's request, change the separator character from a
80 		space to a tab when using the '-c' option (to output text
81 		lines which are compatible with the Unix 'calendar' program).
82 
83 		B.Marr		2005-01-04
84 
85 		Add support for several new character mappings (PostScript
86 		encoding vectors) in order to support a wider variety of
87 		languages.  Rename enumerations for all encodings to be more
88 		consistent (and easily searchable).
89 
90 	4.8.0
91 		B.Marr		2004-12-04
92 
93 		Support new paper sizes.  Support specification of paper size
94 		via run-time option (command-line, etc).  Create and support
95 		concept of 'input' language versus 'output' language.  Use
96 		separate variables for X/Y scaling and X/Y translation done by
97 		the program to distinguish from the X/Y scaling and X/Y
98 		translation specified by the user.
99 
100 		B.Marr		2004-11-19
101 
102 		Support KOI8U character encodings (for Ukrainian language
103 		support) and provide abbreviated day-of-week names, both based
104 		on a patch from Volodymyr M. Lisivka.  Provide support for
105 		embedded EPS images (photos, icons, etc) for monthly
106 		PostScript calendars.  Fix bug since v4.7.1 whereby use of
107 		'-q' flag required '-F 1' to prevent wrong weekday display.
108 		Remove spaces embedded within tab fields.  Remove Ctl-L (page
109 		eject) characters from source file.
110 
111 	4.7.1	SF	01/06/2003	html output with one column per month
112 					(cf. single_month_one_column_html)
113 
114 	4.7	AWR	02/09/2000	document block of code responsible for
115 					forcing Letter or A4 paper tray
116 
117 			10/27/1999	rearrange comment block - 'sdtimage'
118 					tool (possibly others) doesn't recognize
119 					%%Orientation unless it precedes %%Pages
120 
121 			04/19/1999	support #if DIVIDE_BLANK_SPACE (cf.
122 					pcaldefs.h) to print each partial
123 					row of blank space at beginning/end
124 					of calendar as N one-column boxes
125 					instead	of one N-column box
126 
127 			06/19/1998	support landscape- and portrait-mode
128 					whole-year calendars in HTML mode
129 
130 			03/08/1998	write additional definitions to
131 					PostScript output for use with
132 					alternate (just A4 at present) paper
133 					sizes
134 
135 			12/21/1997	clean up gcc warnings in -Wall mode
136 
137 			07/27/1997	revise for -H support (generate HTML
138 					table as output; cf. write_htmlfile()
139 					and print_html()); delete obsolete
140 					FPR and PRT macros
141 
142 	4.6	AWR	04/30/1996	suppress "For:" and "Routing:" comments
143 					if account name is "nobody" (typically
144 					when pcal is run from a CGI script)
145 
146 			04/22/1996	use alternate (taller) date box
147 					height for single-month calendars in
148 					portrait mode
149 
150 			12/02/1995	drop asterisks from -c output (Un*x
151 					"calendar" utility interprets them
152 					as wildcards) unless KEEP_ASTERISKS
153 					is defined
154 
155 			11/14/1995	if OUTLINE_BLACK is defined, display
156 					-O dates as black outlines (not the
157 					specified color) in RGB mode (cf.
158 					pcalinit.ps)
159 
160 			10/03/1995	use globals for initial month, year,
161 					number of months (cf. pcalglob.h,
162 					pcal.c)
163 
164 			05/09/1995	add support for font style escape
165 					sequences (.[bir]; cf. pcalinit.ps)
166 
167 		AH	02/03/1995	add multiple language support
168 
169 
170 	4.5	AWR	11/15/1994	select gray/RGB independently for dates
171 					and fill boxes (cf. pcalinit.ps)
172 
173 			04/05/1994	select real vs. dummy PostScript code
174 					within write_psfile() (cf. pcalinit.ps)
175 
176 			11/30/1993	pre-scale all fonts used (as suggested
177 					by Andrew Houghton; cf. pcalinit.ps)
178 
179 			11/24/1993	replace find_holidays() with
180 					print_colors() (cf. drawnums{} in
181 					pcalinit.ps)
182 
183 			11/16/1993	Add set_rgb() to handle red:green:blue
184 					values for date/fill colors
185 
186 			09/23/1993	Support both ROMAN-8 and LATIN-8 font
187 					mappings
188 
189 		AWR	07/09/1993	Revised PostScript comment block
190 
191 		AWR	03/01/1993	add optional mapping of 8-bit fonts
192 
193 		AWR	02/05/1993	Support -# flag (multiple copies of
194 					each output page)
195 
196 		AWR	04/22/1992	use STRSIZ for temp buffer size
197 
198 	4.4	AWR	04/07/1992	revise to use new PUTCHAR and PUTSTR
199 					macros (cf. pcaldefs.h)
200 
201 			01/20/1992	support -z and revised -[bgGO] flags
202 
203 		AWR	01/13/1992	support optional font size in -d and
204 					-t flags; move initialization of fonts
205 					and sizes here (from pcalinit.ps)
206 
207 	4.3	AWR	12/03/1991	add support for -s flag (specify
208 					alternate date/fill box shading values)
209 
210 	4.2	AWR	10/08/1991	add support for -[kK] flags (change
211 					position of small calendars)
212 
213 			10/03/1991	add find_noteboxes(); revise to print
214 					text in multiple notes boxes
215 
216 					add support for -S flag
217 
218 			10/02/1991	modify def_footstring() to handle all
219 					types of strings; use it to print notes
220 					header (-N flag)
221 
222 			09/19/1991	add write_calfile(), print_dates(),
223 					and new print_text() to generate
224 					input for Un*x "calendar" utility;
225 					renamed old print_text() as
226 					print_pstext() for clarity; revised
227 					to simplify setting working date
228 
229 	4.11	AWR	08/23/1991	revise expand_fmt() to write results
230 					to string instead of stdout; revise
231 					print_word() to avoid writing null
232 					strings
233 
234 		AWR	08/21/1991	use ABBR_DAY_LEN and ABBR_MONTH_LEN
235 					(cf. pcallang.h) to print abbreviated
236 					day/month names
237 
238 		AWR	08/21/1991	add %u and %w (calculate week number
239 					so that 1/1 is always week 1); support
240 					%[+-]<n>[DWMY] to adjust working date
241 					by +|- <n> days/weeks/months/years
242 
243 	4.1	AWR	08/16/1991	Support -G flag (outlined gray dates)
244 
245 	4.02	AWR	07/02/1991	Added "%" expansions in text strings
246 					(cf. expand_fmt())
247 
248 	4.0	AWR	01/28/1991	Support -B, -w flags and moon file
249 
250 			01/15/1991	Extracted from pcal.c
251 
252 */
253 
254 /* ---------------------------------------------------------------------------
255 
256    Header Files
257 
258 */
259 
260 #include <stdio.h>
261 #include <ctype.h>
262 #include <string.h>
263 #include <time.h>
264 
265 #include "pcaldefs.h"
266 #include "pcallang.h"
267 #include "protos.h"
268 
269 /* Some headers needed for 'user account/real name' (via 'getuid()' etc) in
270    PostScript comments */
271 #if defined (BUILD_ENV_UNIX) || defined (BUILD_ENV_DJGPP)
272 #include <pwd.h>
273 #include <unistd.h>
274 #endif
275 
276 /* ---------------------------------------------------------------------------
277 
278    Type, Struct, & Enum Declarations
279 
280 */
281 
282 /* ---------------------------------------------------------------------------
283 
284    Constant Declarations
285 
286 */
287 
288 /* suffix for new 8-bit fonts */
289 #define NEWFONT   "-8"
290 
291 /* maximum number of fonts to prescale */
292 #define MAXFONT   20
293 
294 /* ---------------------------------------------------------------------------
295 
296    Macro Definitions
297 
298 */
299 
300 /* make sure printf() doesn't round "ph" up to 1.0 when printing it */
301 #define PRT_TWEAK(ph)   ((ph) >= 0.9995 ? 0.0 : (ph))
302 
303 /* advance working date by n days */
304 #define SET_DATE(n)   do {						\
305 	MAKE_DATE(date, work_month, work_day + (n), work_year);		\
306 	normalize(&date);						\
307 	work_month = date.mm, work_day = date.dd, work_year = date.yy;	\
308 } while (0)
309 
310 /* prescale a font and add its name to list */
311 #define ADDFONT(name, size, font, isarray)   do {			\
312 	char *p = alloc(strlen(name) + strlen(size) + 2);		\
313 	sprintf(allfonts[nfonts++] = p, "%s_%s", name, size);		\
314 	printf("/%s { %sfontsize ", p, font);				\
315 	if (isarray) printf("%s get ", size);				\
316 	printf("%sfont FontFind } def\n", font);			\
317 } while (0)
318 
319 /* print PostScript string definition */
320 #define PRINT_DEF(name, value)   do {					\
321 	printf("/%s ", name);						\
322 	print_word(value);						\
323 	printf(" def\n");						\
324 } while (0)
325 
326 /* add specified number of blank lines to HTML table entry */
327 #define BLANKLINES(n)  do {						\
328 	int i;								\
329 	for (i = 0; i < n; i++)						\
330 		printf("<br>&nbsp;");					\
331 } while(0)
332 
333 #define FOOTSTRINGS()   (lfoot[0] || cfoot[0] || rfoot[0])
334 
335 /* reset working date to original date */
336 #define RESET_DATE()   \
337    work_month = this_month, work_day = this_day, work_year = this_year
338 
339 /* 'clean' output */
340 #define PUTCHAR_CLEAN(_fc, _c, _fp) \
341         fprintf((_fp), "%c", (_c) & CHAR_MSK)
342 
343 #define PUTSTR_CLEAN(_fc, _s, _fp) \
344    do { char *_p; for (_p = (_s); *_p; _p++) \
345    PUTCHAR_CLEAN(_fc, *_p, _fp); } while (0)
346 
347 /* ---------------------------------------------------------------------------
348 
349    Data Declarations (including externals)
350 
351 */
352 
353 /* order of following strings must conform to #define's in pcaldefs.h (q.v.) */
354 static char *cond[3] = {"false", "true", "(some)"};
355 
356 static int this_day, this_month, this_year;   /* current day */
357 static int work_day, work_month, work_year;   /* working day (cf. expand_fmt()) */
358 static char *kw_note, *kw_opt, *kw_year;   /* keywords for -c output */
359 
360 static int debug_text;   /* generate debug output */
361 
362 /* ---------------------------------------------------------------------------
363 
364    External Routine References & Function Prototypes
365 
366 */
367 
368 /* ---------------------------------------------------------------------------
369 
370    ps_prtday_bw
371 
372    Notes:
373 
374       none
375 
376 */
ps_prtday_bw(void)377 void ps_prtday_bw (void)
378 {
379    printf("%% print 'day' in 'color' (black, gray, outline, or outline-gray; cf. pcaldefs.h)\n");
380    printf("%% using single value in 'dategray' as gray shade\n");
381    printf("%%\n");
382    printf("/prtday {					%% black and white version\n");
383    printf("	gsave\n");
384    printf("	day 3 string cvs			%% convert day to string\n");
385    printf("	[\n");
386    printf("		{ show }				%% black (0)\n");
387    printf("		{ dategray setgray show }		%% gray (1)\n");
388    printf("		{ true charpath stroke }		%% outline (2)\n");
389    printf("		{ true charpath gsave			%% outline-gray (3)\n");
390    printf("		  dategray setgray fill\n");
391    printf("		  grestore stroke }\n");
392    printf("	] color get exec			%% execute operators for color\n");
393    printf("	grestore\n");
394    printf("} bind def\n\n");
395    return;
396 }
397 
398 /* ---------------------------------------------------------------------------
399 
400    write_psfile
401 
402    Notes:
403 
404       This routine writes the actual PostScript code.
405 
406       The actual output of the PostScript code is straightforward.  This
407       routine writes a PostScript header followed by declarations of all the
408       PostScript variables affected by command-line flags and/or language
409       dependencies.  It then generates the PostScript boilerplate and finally
410       calls 'print_month()' to generate the PostScript code for each requested
411       month.
412 
413 */
write_psfile(void)414 void write_psfile (void)
415 {
416    int i, nfonts, nfsize, copies, dfltsize, color_dates, color_fill;
417    char *p, tmp[STRSIZ], *allfonts[MAXFONT];
418    char time_str[50];
419    time_t curr_tyme;
420 
421 #if defined (BUILD_ENV_UNIX) || defined (BUILD_ENV_DJGPP)
422    struct passwd *pw;
423 #endif
424    /* default date, title, weekday font sizes (small/medium/large) */
425    static int dsize[3] = DATEFONTSIZE;
426    static int tsize[3] = TITLEFONTSIZE;
427    static int wsize[3] = WEEKDAYFONTSIZE;
428    static int fsize[3] = FOOTFONTSIZE;
429    static int dmargin[3] = DATEMARGIN;
430    static double gwidth[3] = GRIDLINEWIDTH;
431    static char *calsize[3] = CALSIZE;
432 
433    debug_text = DEBUG(DEBUG_TEXT);   /* debug text output? */
434 
435    /*
436     * Write out PostScript prolog (including version/datefile stamp)
437     */
438 
439    /* comment block at top */
440 
441    printf("%%!%s\n", PS_RELEASE);   /* PostScript release */
442 
443 
444    /* Get the current date/time so that we can write it into the output file
445       as a timestamp...  */
446    time(&curr_tyme);
447 
448    /* It seems that neither MS-DOS (Borland C) nor DOS+DJGPP support the '%P'
449       (lowercase 'am'/'pm') specifier, so we'll use '%p' (uppercase 'AM'/'PM')
450       instead. */
451 #if defined (BUILD_ENV_MSDOS) || defined (BUILD_ENV_DJGPP)
452    strftime(time_str, sizeof(time_str), "%d %b %Y (%a) %I:%M:%S%p", localtime(&curr_tyme));
453 #else
454    strftime(time_str, sizeof(time_str), "%d %b %Y (%a) %I:%M:%S%P", localtime(&curr_tyme));
455 #endif
456 
457    printf("%%%%CreationDate: %s\n", time_str);
458 
459    printf("%%%%Creator: Generated by %s %s (%s)\n", progname, version, PCAL_WEBSITE);
460 
461    /* Generate "For" and "Routing" comments if user name is known... */
462 
463 #if defined (BUILD_ENV_UNIX) || defined (BUILD_ENV_DJGPP)
464    if ((pw = getpwuid(getuid())) != NULL && strcmp(pw->pw_name, "nobody" /* anonymous account */) != 0) {
465       printf("%%%%For: %s\n", pw->pw_name);
466 #ifdef BUILD_ENV_UNIX
467       /* The 'pw->pw_gecos' element ('real' user name) is not available in
468          MS-DOS or DOS+DJGPP build environments... */
469       strcpy(tmp, pw->pw_gecos);
470       if ((p = strchr(tmp, ',')) != NULL) *p = '\0';
471       printf("%%%%Routing: %s\n", tmp);
472 #endif
473    }
474 #endif
475 
476 
477 
478 
479    /* Identify the output (month/year range and input file) */
480 
481    if (do_whole_year && init_month == JAN) {
482       printf("%%%%Title: calendar for %d", init_year);
483       if (final_year > init_year) printf(" - %d", final_year);
484    }
485    else {
486       char c = date_style == EUR_DATES ? '.' : '/';
487       printf("%%%%Title: calendar for %02d%c%02d", init_month, c, init_year);
488       if (nmonths > 1) printf(" - %02d%c%02d", final_month, c, final_year);
489    }
490 
491    /* If a configuration file was used, show the file's name... */
492    if (*datefile) printf(" (from %s)", datefile);
493 
494    printf("\n");
495 
496    /* Miscellaneous other identification */
497 
498    printf("%%%%Orientation: %s\n", rotate == LANDSCAPE ? "Landscape" : "Portrait");
499    printf("%%%%Pages: %d\n", do_whole_year ? nmonths / 12 : nmonths);
500    printf("%%%%PageOrder: Ascend\n");
501    printf("%%%%BoundingBox: 0 0 %d %d\n", page_dim_short_axis_pts, page_dim_long_axis_pts);
502    printf("%%%%DocumentPaperSizes: %s\n", paper_info[paper_size].name);
503    printf("%%%%ProofMode: NotifyMe\n");
504    printf("%%%%EndComments\n\n");
505 
506    /* number of copies (from -#<n> flag) */
507    if ((copies = ncopy) > MAXCOPY) copies = MAXCOPY;
508    if (copies > 1) printf("/#copies %d def\n", copies);
509 
510    /* paper size (force appropriate tray if known) */
511 
512    /* The following lines force the printer to select the specified paper tray
513       (Letter or A4, as selected in the Makefile).  I (AWR) added it to v4.7
514       to fix a problem with one particular HP printer configured for both
515       Letter and A4 size paper: under some conditions, previous A4 jobs would
516       leave the printer in A4 mode unless I forced it back to Letter mode.  It
517       is probably unnecessary for most users (and is known to confuse some
518       previewers), so #ifdef it out if you like.
519     */
520 #if 1
521    strcpy(tmp, paper_info[paper_size].name);
522    tmp[0] = tolower(tmp[0]);
523    printf("statusdict /%stray known { statusdict begin %stray end } if\n", tmp, tmp);
524 #endif
525 
526    /* calendar sizes: to minimize number of pre-scaled fonts, whole- year
527       calendars define 'medium' as 0 and the other sizes as -1 (not used);
528       single-month calendars define 'large' as 0, 'small' as 1, and 'medium'
529       as -1 (not used)
530     */
531 
532    for (i = SMALL; i <= LARGE; i++) {
533       printf("/%s %d def\n", calsize[i], do_whole_year ? (i == MEDIUM ? 0 : -1) :
534              (i == MEDIUM ? -1 : i == SMALL));
535    }
536 
537    /* font names and sizes */
538 
539    /* v4.4 supports user override of note and large date/title sizes */
540    nfsize = (p = strrchr(notesfont, '/')) ? *p++ = '\0', atoi(p) : atoi(strrchr(NOTESFONT, '/') + 1);
541    dsize[LARGE] = (p = strrchr(datefont, '/')) ? *p++ = '\0', atoi(p) : atoi(strrchr(DATEFONT, '/') + 1);
542    tsize[LARGE] = (p = strrchr(titlefont, '/')) ? *p++ = '\0', atoi(p) : atoi(strrchr(TITLEFONT, '/') + 1);
543 
544    /* enlarge footer strings in whole-year/portrait mode */
545    if (do_whole_year && rotate == PORTRAIT) {
546       fsize[MEDIUM] = (int)((double)fsize[MEDIUM] * 1.25);
547    }
548 
549    /*
550       if 8-bit remapping has been requested (-r flag), create new fonts with
551       desired character remapping
552    */
553 
554    if (mapfonts != ENC_NONE) {
555 
556       /* include desired mapping */
557 
558       switch (mapfonts) {
559 
560       case ENC_LATIN_1: encvec_iso8859_1(); break;
561       case ENC_LATIN_2: encvec_iso8859_2(); break;
562       case ENC_LATIN_3: encvec_iso8859_3(); break;
563       case ENC_LATIN_4: encvec_iso8859_4(); break;
564       case ENC_CYRILLIC: encvec_iso8859_5(); break;
565       case ENC_ARABIC: encvec_iso8859_6(); break;
566       case ENC_GREEK: encvec_iso8859_7(); break;
567       case ENC_HEBREW: encvec_iso8859_8(); break;
568       case ENC_LATIN_5: encvec_iso8859_9(); break;
569       case ENC_LATIN_6: encvec_iso8859_10(); break;
570       case ENC_THAI: encvec_iso8859_11(); break;
571       case ENC_LATIN_7: encvec_iso8859_13(); break;
572       case ENC_LATIN_8: encvec_iso8859_14(); break;
573       case ENC_LATIN_9: encvec_iso8859_15(); break;
574       case ENC_LATIN_10: encvec_iso8859_16(); break;
575       case ENC_KOI8_R: encvec_koi8_r(); break;
576       case ENC_KOI8_U: encvec_koi8_u(); break;
577       case ENC_ROMAN8: encvec_roman8(); break;
578 
579       default:
580          fprintf(stderr, "Unknown language encoding (%d) encountered!\n", mapfonts);
581          break;
582       }
583 
584       /* Boilerplate PostScript code to remap for 8-bit fonts... */
585 
586       printf("/alt_dict 20 dict def			%% Local storage\n");
587       printf("\n");
588       printf("%% <oldfont> <newfont> remap_font => --\n");
589       printf("%%\n");
590       printf("%% create remapped font using one of the above 8-bit character remapping tables\n");
591       printf("%%\n");
592       printf("/remap_font {\n");
593       printf("	alt_dict begin\n");
594       printf("		/newName exch def\n");
595       printf("		/oldName exch def\n");
596       printf("		/oldDict oldName findfont def\n");
597       printf("		/newDict oldDict maxlength dict def\n");
598       printf("		oldDict {\n");
599       printf("			exch dup /FID ne {\n");
600       printf("				dup /Encoding eq {\n");
601       printf("					exch dup length array copy\n");
602       printf("					newDict 3 1 roll put\n");
603       printf("				} {\n");
604       printf("					exch newDict 3 1 roll put\n");
605       printf("				} ifelse\n");
606       printf("			} {\n");
607       printf("				pop pop\n");
608       printf("			} ifelse\n");
609       printf("		} forall\n");
610       printf("		newDict /FontName newName put\n");
611       printf("		0 2 alt_mappings length 1 sub {\n");
612       printf("			dup\n");
613       printf("			alt_mappings exch get\n");
614       printf("			exch 1 add alt_mappings exch get\n");
615       printf("			newDict /Encoding get 3 1 roll put\n");
616       printf("		} for\n");
617       printf("		newName newDict definefont pop\n");
618       printf("	end\n");
619       printf("} bind def\n\n");
620 
621       /* always generate code to remap title font */
622       printf("/%s /%s%s remap_font\n", titlefont, titlefont, NEWFONT);
623       strcat(titlefont, NEWFONT);
624 
625 #if MAP_DATEFONT   /* any text printed in date font (cf. pcaldefs.h)? */
626       /* generate code to remap date font if necessary */
627       printf("FontDirectory /%s%s known not {\n", datefont, NEWFONT);
628       printf("/%s /%s%s remap_font\n", datefont, datefont, NEWFONT);
629       printf("} if\n");
630       strcat(datefont, NEWFONT);
631 #endif
632 
633       /* generate code to remap notes font if necessary */
634       printf("FontDirectory /%s%s known not {\n", notesfont, NEWFONT);
635       printf("/%s /%s%s remap_font\n", notesfont, notesfont, NEWFONT);
636       printf("} if\n");
637       strcat(notesfont, NEWFONT);
638    }
639 
640    /* define title, notes, and date fonts */
641    printf("/titlefont /%s def\n", titlefont);
642    printf("/datefont /%s def\n", datefont);
643    printf("/notesfont /%s def\n", notesfont);
644 
645    /* typically defined in terms of above fonts - must define last */
646    printf("/weekdayfont %s def\n", WEEKDAYFONT);
647    printf("/footfont %s def\n", FOOTFONT);
648    printf("/headingfont %s def\n", HEADINGFONT);
649 
650    /* print various font sizes and line/margin widths as PostScript arrays:
651       one element for whole-year calendars; two (large, small) for single-
652       month calendars
653    */
654 
655    if (do_whole_year) {
656       printf("/datemargin [ %d ] def\n", dmargin[MEDIUM]);
657       printf("/gridlinewidth [ %.1f ] def\n", gwidth[MEDIUM]);
658       printf("/titlefontsize [ %d ] def\n", tsize[MEDIUM]);
659       printf("/datefontsize [ %d ] def\n", dsize[MEDIUM]);
660    } else {
661       printf("/datemargin [ %d %d ] def\n", dmargin[LARGE], dmargin[SMALL]);
662       printf("/gridlinewidth [ %.1f %.1f ] def\n", gwidth[LARGE], gwidth[SMALL]);
663       printf("/titlefontsize [ %d %d ] def\n", tsize[LARGE], tsize[SMALL]);
664       printf("/datefontsize [ %d %d ] def\n", dsize[LARGE], dsize[SMALL]);
665    }
666 
667    dfltsize = do_whole_year ? MEDIUM : LARGE;
668 
669    printf("/weekdayfontsize %d def\n", wsize[dfltsize]);
670    printf("/footfontsize %d def\n", fsize[dfltsize]);
671 
672    /* Now that we allow Julian dates to be displayed on yearly-format
673       calendars, we must tweak the font size of the 'notes font' accordingly,
674       since that font is also used to display the Julian dates (if enabled by
675       the user)...
676    */
677    printf("/notesfontsize %d def\n", do_whole_year ? 24 : nfsize);
678 
679    printf("/headingfontsize %d def\n", HEADINGFONTSIZE);
680 
681    /* pre-scale all fonts used by PostScript code; try to be smart about
682       skipping those that we know (at this point) won't be needed (whole-year
683       calendars use either 3 or 4 fonts, while single-month calendars can use
684       anywhere from 3 to 8).  "FF" et. al. are indices into the font array (cf
685       pcalinit.ps) for the different font types.
686    */
687 
688    printf("/FontFind { findfont exch scalefont } def\n");
689 
690    nfonts = 0;
691 
692    printf("/FF %d def\n", nfonts);   /* footers */
693    if (FOOTSTRINGS()) ADDFONT("ff", calsize[dfltsize], "foot", FALSE);
694 
695    if (do_whole_year) {
696       printf("/TF %d def\n", nfonts);   /* month/year title */
697       ADDFONT("tf", calsize[MEDIUM], "title", TRUE);
698 
699       printf("/DF %d def\n", nfonts);   /* dates */
700       ADDFONT("df", calsize[MEDIUM], "date", TRUE);
701    }
702    else {
703       printf("/HF %d def\n", nfonts);   /* 'Notes' heading */
704       if (notes_hdr[0]) ADDFONT("hf", calsize[LARGE], "heading", FALSE);
705 
706       /* large/small (if used) scalings of the same font must be
707        * contiguous and appear in that order
708        */
709 
710       printf("/TF %d def\n", nfonts);   /* large/small title */
711       ADDFONT("tf", calsize[LARGE], "title", TRUE);
712       if (small_cal_pos != SC_NONE) ADDFONT("tf", calsize[SMALL], "title", TRUE);
713 
714       printf("/DF %d def\n", nfonts);   /* large/small dates */
715       ADDFONT("df", calsize[LARGE], "date", TRUE);
716       if (small_cal_pos != SC_NONE) ADDFONT("df", calsize[SMALL], "date", TRUE);
717    }
718 
719    printf("/NF %d def\n", nfonts);   /* text for note-boxes and/or Julian dates */
720    ADDFONT("nf", calsize[LARGE], "notes", FALSE);
721 
722 
723    printf("/WF %d def\n", nfonts);   /* weekdays */
724    ADDFONT("wf", calsize[dfltsize], "weekday", FALSE);
725 
726    /* generate the font array (automatically in sync with above) */
727 
728    printf("/allfonts [\n\t");
729    for (i = 0; i < nfonts; i++) {
730       printf("%s ", allfonts[i]);
731       free(allfonts[i]);
732    }
733    printf("\n] def\n");
734 
735    /*
736       Define various strings and numeric values used by Pcal
737    */
738 
739    /* month names */
740 
741    printf("/month_names [");
742    for (i = JAN; i <= DEC; i++) {
743       printf(i % 6 == 1 ? "\n\t" : " ");
744       (void) print_word(months_ml[output_language][i-1]);
745    }
746    printf(" ] def\n");
747 
748    /* day names - abbreviate if printing entire year on page */
749 
750    printf("/day_names [");
751    for (i = SUN; i <= SAT; i++) {
752       printf((i % 6 == 0 && !do_whole_year) ? "\n\t" : " ");
753       if (do_whole_year) {
754          strcpy(tmp, days_ml_short[output_language][(i + first_day_of_week) % 7]);
755       }
756       else {
757          strcpy(tmp, days_ml[output_language][(i + first_day_of_week) % 7]);
758       }
759       (void) print_word(tmp);
760    }
761    printf(" ] def\n");
762 
763    /* line separator and font change strings */
764 
765    PRINT_DEF("linesep", LINE_SEP);
766    PRINT_DEF("boldfont", BOLD_FONT);
767    PRINT_DEF("italicfont", ITALIC_FONT);
768    PRINT_DEF("romanfont", ROMAN_FONT);
769 
770    /* page width and height (always referenced to 'portrait' mode) */
771 
772    printf("/pagewidth %d def\n", page_dim_short_axis_pts);
773    printf("/pageheight %d def\n", page_dim_long_axis_pts);
774 
775    /* rotation, scaling, and translation factors */
776 
777    printf("/rval %d def\n", rotate);
778    printf("/xsval %.3f def\n/ysval %.3f def\n", xsval_pgm * xsval_user, ysval_pgm * ysval_user);
779    printf("/xtval %d def\n/ytval %d def\n", xtval_pgm + xtval_user, ytval_pgm + ytval_user);
780    printf("/ytop %d def\n", -TOP_OF_CAL_BOXES_PTS);
781 
782    /* date box dimensions */
783 
784    printf("/daywidth %d def\n", daybox_width_pts);
785    printf("/dayheight %d def\n", daybox_height_pts);
786 
787    /* moon, Julian date, and box fill flags */
788 
789    printf("/draw-moons %s def\n", cond[draw_moons]);
790    printf("/julian-dates %s def\n", cond[julian_dates]);
791    printf("/fill-boxes %s def\n", cond[! blank_boxes]);
792 
793    /* position of small calendars */
794 
795    printf("/prev_small_cal %d def\n", prev_cal_box[small_cal_pos]);
796    printf("/next_small_cal %d def\n", next_cal_box[small_cal_pos]);
797 
798    /* date and fill box shading values */
799 
800    strcpy(tmp, shading);
801    *(p = strchr(tmp, '/')) = '\0';
802    printf("/dategray %s def\n", set_rgb(tmp));
803    color_dates = strchr(tmp, RGB_CHAR) != NULL;
804    printf("/fillgray %s def\n", set_rgb(++p));
805    color_fill = strchr(p, RGB_CHAR) != NULL;
806 
807    /* PostScript boilerplate (part 1 of 1) */
808 
809    printf("/Y0 0 def				%% Y-coordinate of calendar grid origin\n");
810    printf("\n");
811    printf("/gridwidth daywidth 7 mul def\n");
812    printf("/gridheight dayheight 6 mul def\n");
813    printf("/negdaywidth daywidth neg def\n");
814    printf("/negdayheight dayheight neg def\n");
815    printf("/neggridwidth gridwidth neg def\n");
816    printf("/neggridheight gridheight neg def\n");
817    printf("\n");
818    printf("/textmargin 2 def			%% left/right margin for text\n");
819    printf("/notemargin 4 def			%% left/right margin for notes\n");
820    printf("/charlinewidth 0.1 def			%% width of outline characters\n");
821    printf("\n");
822    printf("/datewidth 2 array def			%% for aligning holiday text\n");
823    printf("\n");
824    printf("/moonlinewidth 0.1 def			%% width of moon icon line\n");
825 
826    /*
827 
828      Moon icon radius is actually larger (due to scaling, I suppose) for a
829      yearly-format calendar...
830 
831    */
832    printf("/radius %d def				%% radius of moon icon\n", do_whole_year ? 12 : 6);
833 
834    printf("/halfperiod 0.5 def			%% scale factors, etc. used by 'domoon'\n");
835    printf("/quartperiod 0.25 def\n");
836    printf("/offset radius datemargin 0 get add def\n");
837    printf("/rect radius 2 sqrt mul quartperiod div def\n");
838    printf("\n");
839    printf("/ROMAN 0 def				%% font types for text in boxes\n");
840    printf("/BOLD 1 def\n");
841    printf("/ITALIC 2 def\n");
842    printf("/currfonttype ROMAN def\n");
843    printf("/boldoffset 0.5 def			%% offset for overstriking bold text\n");
844    printf("\n");
845    printf("%% simulate Italic text by slanting Roman text - note that this is properly\n");
846    printf("%% called 'Oblique'\n");
847    printf("\n");
848    printf("/italicangle 12 def			%% angle for slanting Italic text\n");
849    printf("/italicmatrix				%% Italic font matrix\n");
850    printf(" [notesfontsize 0 notesfontsize italicangle dup sin exch cos div mul\n");
851    printf(" notesfontsize 0 0] def\n");
852    printf("\n");
853    printf("/hangingindent (   ) def		%% for indenting continued text lines\n");
854    printf("\n");
855    printf("%% disable duplex mode (if supported)\n");
856    printf("statusdict (duplexmode) known { statusdict begin false setduplexmode end } if\n");
857    printf("\n");
858    printf("%%\n");
859    printf("%% Utility functions:\n");
860    printf("%%\n");
861    printf("\n");
862    printf("%% <fontposition> FontSet => --\n");
863    printf("%%\n");
864    printf("%% set current font to element <fontposition> of 'allfonts' array\n");
865    printf("%%\n");
866    printf("/FontSet {\n");
867    printf("	allfonts exch\n");
868    printf("	userdict /CurrentFontSet 2 index put\n");
869    printf("	get setfont\n");
870    printf("} bind def\n\n");
871 
872    printf("%% <size> *FontSet => --\n");
873    printf("%%\n");
874    printf("%% fetch pre-scaled font (of desired calendar size) from 'allfonts' array\n");
875    printf("%%\n");
876    printf("/TitleFontSet {TF add FontSet} bind def\n");
877    printf("\n");
878    printf("/DateFontSet {DF add FontSet} bind def\n");
879    printf("\n");
880    printf("%% -- *FontSet => --\n");
881    printf("%%\n");
882    printf("%% fetch pre-scaled font (of constant size) from 'allfonts' array\n");
883    printf("%%\n");
884    printf("/WeekdayFontSet {WF FontSet} bind def\n");
885    printf("\n");
886    printf("/FootFontSet {FF FontSet} bind def\n");
887    printf("\n");
888    printf("/NotesFontSet {NF FontSet /currfonttype ROMAN def} bind def\n");
889    printf("\n");
890    printf("/HeadingFontSet {HF FontSet} bind def\n");
891    printf("\n");
892    printf("%% <string> <width> center => --\n");
893    printf("%%\n");
894    printf("%% display <string> centered horizontally in <width>\n");
895    printf("%%\n");
896    printf("/center {\n");
897    printf("	1 index stringwidth pop sub 2 div 0 rmoveto show\n");
898    printf("} bind def\n\n");
899 
900    /* These 2 routines ('left' and 'right') were added to support the '-W'
901       option (horizontal alignment of title on monthly-format calendar).
902    */
903    printf("%% <string> <width> left => --\n");
904    printf("%%\n");
905    printf("%% display <string> justified left in <width>\n");
906    printf("%%\n");
907    printf("/left {\n");
908    printf("   1 index show\n");
909    printf("} bind def\n\n");
910    printf("%% <string> <width> right => --\n");
911    printf("%%\n");
912    printf("%% display <string> justified right in <width>\n");
913    printf("%%\n");
914    printf("/right {\n");
915    printf("   1 index stringwidth pop sub 0 rmoveto show\n");
916    printf("} bind def\n\n");
917 
918    printf("%% <str1> <str2> strcat => <string>\n");
919    printf("%%\n");
920    printf("%% concatenate <str1> and <str2>; push result onto stack\n");
921    printf("%%\n");
922    printf("/strcat {\n");
923    printf("	2 copy\n");
924    printf("	length exch length\n");
925    printf("	dup 3 -1 roll add\n");
926    printf("	string\n");
927    printf("	dup 0 6 -1 roll putinterval\n");
928    printf("	dup 3 -1 roll 4 -1 roll putinterval\n");
929    printf("} bind def\n\n");
930 
931 
932    printf("%% -- nextbox => --\n");
933    printf("%%\n");
934    printf("%% move to same relative position within following day's box\n");
935    printf("%%\n");
936    printf("/nextbox {\n");
937    printf("	day startbox add 7 mod 0 eq			%% end of week?\n");
938    printf("		{ neggridwidth daywidth add negdayheight rmoveto }  %% next row\n");
939    printf("		{ daywidth 0 rmoveto }				    %% next col\n");
940    printf("	ifelse\n");
941    printf("} bind def\n\n");
942 
943    printf("%% <box> boxpos => <x> <y>\n");
944    printf("%%\n");
945    printf("%% calculate and push coordinates of upper-left corner of <box> (0..41)\n");
946    printf("%%\n");
947    printf("/boxpos {\n");
948    printf("	dup 7 mod daywidth mul					%% x-coord\n");
949    printf("	exch 7 idiv negdayheight mul Y0 add			%% y-coord\n");
950    printf("} bind def\n\n");
951 
952    printf("%% <day> datepos => <x> <y>\n");
953    printf("%%\n");
954    printf("%% calculate and push coordinates of upper-left corner of box for <day>\n");
955    printf("%%\n");
956    printf("/datepos {\n");
957    printf("	startbox add 1 sub dup 7 mod daywidth mul		%% x-coord\n");
958    printf("	exch 7 idiv negdayheight mul Y0 add			%% y-coord\n");
959    printf("} bind def\n\n");
960 
961    printf("%%\n");
962    printf("%% Functions for drawing components of calendar:\n");
963    printf("%%\n");
964    printf("%% The point size of a PostScript font includes the descenders on [gjpqy],\n");
965    printf("%% but the Y-origin for printing text starts above any descenders (at the\n");
966    printf("%% bottom of the upper-case characters).  The following code - and other\n");
967    printf("%% code concerned with vertical spacing - assumes that the descenders\n");
968    printf("%% occupy 1/4 of the overall point size.\n\n");
969 
970    printf("%% -- drawtitle => --\n");
971    printf("%%\n");
972    printf("%% print month/year title centered at top of calendar\n");
973    printf("%%\n");
974    printf("/drawtitle {\n");
975    printf("	/fontsize titlefontsize calsize get def\n");
976    printf("	calsize TitleFontSet\n");
977    printf("	/month_name month_names month 1 sub get def\n");
978    printf("	/yearstring year 10 string cvs def\n");
979    printf("	0 Y0 fontsize 0.25 mul add\n");
980    printf("	  calsize small eq { 4 } { weekdayfontsize } ifelse\n");
981    printf("	  1.15 mul add moveto\n");
982    printf("   month_name (  ) strcat yearstring strcat gridwidth %s\n", title_align);
983    printf("} bind def\n\n");
984 
985    printf("%% -- drawdaynames => --\n");
986    printf("%%\n");
987    printf("%% print weekday names centered above respective columns\n");
988    printf("%%\n");
989    printf("/drawdaynames {	\n");
990    printf("	WeekdayFontSet\n");
991    printf("	0 1 6 {\n");
992    printf("		/i exch def\n");
993    printf("		i daywidth mul Y0 weekdayfontsize 0.4 mul add moveto\n");
994    printf("		day_names i get\n");
995    printf("		daywidth center\n");
996    printf("	} for\n");
997    printf("} bind def\n\n");
998 
999    printf("%% -- drawgrid => --\n");
1000    printf("%%\n");
1001    printf("%% draw the grid (6 rows x 7 columns) for the calendar\n");
1002    printf("%%\n");
1003    printf("/drawgrid {\n");
1004    printf("	gridlinewidth calsize get setlinewidth\n");
1005    printf("\n");
1006    printf("	1 1 6 {					%% inner vertical lines\n");
1007    printf("		daywidth mul Y0 moveto\n");
1008    printf("		0 neggridheight rlineto\n");
1009    printf("		stroke\n");
1010    printf("	} for\n");
1011    printf("\n");
1012    printf("	1 1 5 {					%% inner horizontal lines\n");
1013    printf("		0 exch negdayheight mul Y0 add moveto\n");
1014    printf("		gridwidth 0 rlineto\n");
1015    printf("		stroke\n");
1016    printf("	} for\n");
1017    printf("\n");
1018    printf("	newpath					%% border (w/mitered corners)\n");
1019    printf("	0 Y0 moveto\n");
1020    printf("	gridwidth 0 rlineto\n");
1021    printf("	0 neggridheight rlineto\n");
1022    printf("	neggridwidth 0 rlineto\n");
1023    printf("	closepath\n");
1024    printf("	stroke\n");
1025    printf("} bind def\n\n");
1026 
1027    printf("%% -- drawnums => --\n");
1028    printf("%%\n");
1029    printf("%% print dates in appropriate boxes of calendar\n");
1030    printf("%%\n");
1031    printf("/drawnums {\n");
1032    printf("	/fontsize datefontsize calsize get def\n");
1033    printf("	/margin datemargin calsize get def\n");
1034    printf("	calsize DateFontSet\n");
1035    printf("	charlinewidth setlinewidth\n");
1036    printf("	1 datepos fontsize 0.75 mul margin add sub exch margin add exch moveto\n");
1037    printf("\n");
1038    printf("	%% calculate date widths for 'holidaytext' to use; use '2' for all\n");
1039    printf("	%% single-digit dates and '22' for all double-digit dates\n");
1040    printf("	calsize large eq {\n");
1041    printf("		datewidth 0 (2) stringwidth pop margin 2 mul add put\n");
1042    printf("		datewidth 1 (22) stringwidth pop margin 2 mul add put\n");
1043    printf("	} if\n");
1044    printf("\n");
1045    printf("	calsize small eq {\n");
1046    printf("		/color 0 def		%% small calendar dates (all black)\n");
1047    printf("		1 1 ndays {\n");
1048    printf("			/day exch def\n");
1049    printf("			prtday\n");
1050    printf("			nextbox\n");
1051    printf("		} for\n");
1052    printf("	} {\n");
1053    printf("		1 1 ndays {		%% medium/large calendar dates\n");
1054    printf("			/day exch def\n");
1055    printf("			/color date_color day get def\n");
1056    printf("			prtday\n");
1057    printf("			nextbox\n");
1058    printf("		} for\n");
1059    printf("	} ifelse\n");
1060    printf("} bind def\n\n");
1061 
1062    printf("%% -- startpage => --\n");
1063    printf("%%\n");
1064    printf("%% initialize new physical page\n");
1065    printf("%%\n");
1066    printf("/startpage {\n");
1067    printf("	rval rotate\n");
1068    printf("	xsval ysval scale\n");
1069    printf("	xtval ytval translate\n");
1070    printf("} bind def\n\n");
1071 
1072    printf("%% -- calendar => --\n");
1073    printf("%%\n");
1074    printf("%% draw calendar for 'month'/'year', with various features enabled/disabled\n");
1075    printf("%% according to 'calsize'\n");
1076    printf("%%\n");
1077    printf("/calendar {\n");
1078    printf("	drawtitle					%% month/year\n");
1079    printf("	calsize small ne { drawdaynames } if		%% weekday names\n");
1080    printf("	calsize large eq { footstrings } if		%% footer strings\n");
1081    printf("	drawnums					%% dates\n");
1082 
1083    /*
1084 
1085      On yearly-format calendars, eliminate the test 'calsize == large', so
1086      that Julian dates get drawn when enabled by user...
1087 
1088    */
1089    if (do_whole_year) {
1090       printf("	julian-dates false ne { drawjnums } if		%% Julian dates\n");
1091    }
1092    else {
1093       printf("	calsize large eq				%% Julian dates\n");
1094       printf("	  julian-dates false ne and { drawjnums } if\n");
1095    }
1096 
1097    printf("	fill-boxes { drawfill } if			%% fill boxes\n");
1098    printf("	drawgrid					%% grid\n");
1099 
1100    /*
1101 
1102      On yearly-format calendars, eliminate the test 'calsize == large', so
1103      that moon icons get drawn when enabled by user...
1104 
1105    */
1106    if (do_whole_year) {
1107       printf("	draw-moons false ne { drawmoons } if		%% moon icons\n");
1108    }
1109    else {
1110       printf("	calsize large eq				%% moon icons\n");
1111       printf("	  draw-moons false ne and { drawmoons } if   \n");
1112    }
1113 
1114    printf("	0 0 moveto\n");
1115    printf("} bind def\n\n");
1116 
1117 
1118    /* Additional PostScript code tailored to this calendar */
1119 
1120 #ifdef OUTLINE_BLACK
1121    /* if OUTLINE_BLACK is defined, include an alternate prtday{} which prints
1122       outlined dates (-O) in black instead of the specified color; otherwise
1123       use the standard prtday{}
1124    */
1125 
1126    if (color_dates) {
1127       printf("%% print 'day' in 'color' (black, gray, outline, or outline-gray; cf. pcaldefs.h)\n");
1128       printf("%% using three values in 'dategray' as red/green/blue levels; 'gray' will\n");
1129       printf("%% print as solid color, 'outline' as black outline; 'outline-gray' as black\n");
1130       printf("%% outline filled with color\n");
1131       printf("%%\n");
1132       printf("/prtday {					%% color version\n");
1133       printf("	gsave\n");
1134       printf("	day 3 string cvs			%% convert day to string\n");
1135       printf("	[\n");
1136       printf("		{ show }				%% black (0)\n");
1137       printf("		{ dategray aload pop setrgbcolor show }	%% gray (1)\n");
1138       printf("		{ true charpath stroke }		%% outline (2; black)\n");
1139       printf("		{ true charpath gsave			%% outline-gray (3)\n");
1140       printf("		  dategray aload pop setrgbcolor\n");
1141       printf("		  fill grestore stroke }\n");
1142       printf("	] color get exec			%% execute operators for color\n");
1143       printf("	grestore\n");
1144       printf("} bind def\n\n");
1145    }
1146    else {
1147       ps_prtday_bw();
1148    }
1149 
1150 #else
1151 
1152    if (color_dates) {
1153       printf("%% print 'day' in 'color' (black, gray, outline, or outline-gray; cf. pcaldefs.h)\n");
1154       printf("%% using three values in 'dategray' as red/green/blue levels; 'gray' will\n");
1155       printf("%% print as solid color, 'outline' as color outline; 'outline-gray' as black\n");
1156       printf("%% outline filled with color\n");
1157       printf("%%\n");
1158       printf("/prtday {					%% color version\n");
1159       printf("	gsave\n");
1160       printf("	day 3 string cvs			%% convert day to string\n");
1161       printf("	[\n");
1162       printf("		{ show }				%% black (0)\n");
1163       printf("		{ dategray aload pop setrgbcolor show }	%% gray (1)\n");
1164       printf("		{ dategray aload pop setrgbcolor	%% outline (2; color)\n");
1165       printf("		  true charpath stroke }\n");
1166       printf("		{ true charpath gsave			%% outline-gray (3)\n");
1167       printf("		  dategray aload pop setrgbcolor\n");
1168       printf("		  fill grestore stroke }\n");
1169       printf("	] color get exec			%% execute operators for color\n");
1170       printf("	grestore\n");
1171       printf("} bind def\n\n");
1172    }
1173    else {
1174       ps_prtday_bw();
1175    }
1176 
1177 #endif
1178 
1179    /*
1180       pcal will generate the appropriate flavor (B&W or color) of setfill{}
1181       depending on the shading values (gray scale or RGB triple) specified by
1182       the "-s" flag
1183    */
1184    if (color_fill) {
1185       printf("%% set fill box color using three values in 'fillgray' as red/green/blue levels\n");
1186       printf("%%\n");
1187       printf("/setfill {					%% color version\n");
1188       printf("	fillgray aload pop setrgbcolor\n");
1189       printf("} def\n\n");
1190    }
1191    else {
1192       printf("%% set fill box shading using single value in 'fillgray'\n");
1193       printf("%%\n");
1194       printf("/setfill {					%% black and white version\n");
1195       printf("	fillgray setgray\n");
1196       printf("} def\n\n");
1197    }
1198 
1199 
1200    if (FOOTSTRINGS()) {
1201       /* at least one foot string */
1202       printf("%% -- footstrings => --\n");
1203       printf("%%\n");
1204       printf("%% print foot strings ([LCR]footstring) at bottom of page\n");
1205       printf("%%\n");
1206       printf("/footstrings {\n");
1207       printf("	FootFontSet\n");
1208       printf("%%	/yfoot { Y0 neggridheight add footfontsize 1.25 mul sub } bind def\n");
1209       printf("	/yfoot { neggridheight 15 sub } bind def\n");
1210       printf("	0 yfoot moveto\n");
1211       printf("	Lfootstring show\n");
1212       printf("	gridwidth Rfootstring stringwidth pop sub yfoot moveto\n");
1213       printf("	Rfootstring show\n");
1214       printf("	0 yfoot moveto\n");
1215       printf("	Cfootstring gridwidth center\n");
1216       printf("} bind def\n\n");
1217    }
1218    else {
1219       /* no foot strings */
1220       printf("/footstrings {} bind def\n\n");
1221    }
1222 
1223    if (blank_boxes) {
1224       /* blank fill boxes */
1225       printf("/drawfill {} bind def\n\n");
1226    }
1227    else {
1228       /* shaded fill boxes */
1229       printf("%% <first> <last> fillboxes => --\n");
1230       printf("%%\n");
1231       printf("%% fill empty calendar boxes in range <first>..<last> (0..41)\n");
1232       printf("%%\n");
1233       printf("/fillboxes {\n");
1234       printf("	/last exch def\n");
1235       printf("	/first exch def\n");
1236       printf("\n");
1237       printf("	first 1 last {		%% loop through range of boxes\n");
1238       printf("		/box exch def\n");
1239       printf("		/fillit true def\n");
1240       printf("		calsize large eq {	%% skip note and small calendar boxes\n");
1241       printf("			noteboxes { box eq { /fillit false def } if } forall\n");
1242       printf("			box prev_small_cal eq box next_small_cal eq or {\n");
1243       printf("				/fillit false def\n");
1244       printf("			} if\n");
1245       printf("		} if\n");
1246       printf("		fillit {		%% move to position and fill the box\n");
1247       printf("			box boxpos moveto\n");
1248       printf("			gsave\n");
1249       printf("			setfill\n");
1250       printf("			daywidth 0 rlineto\n");
1251       printf("			0 negdayheight rlineto\n");
1252       printf("			negdaywidth 0 rlineto\n");
1253       printf("			closepath fill\n");
1254       printf("			grestore\n");
1255       printf("		} if\n");
1256       printf("	} for\n");
1257       printf("} bind def\n\n");
1258 
1259       printf("%% -- drawfill => --\n");
1260       printf("%%\n");
1261       printf("%% fill in unused boxes before and after calendar dates\n");
1262       printf("%%\n");
1263       printf("/drawfill {\n");
1264       printf("	0 startbox 1 sub fillboxes		%% fill boxes before calendar\n");
1265       printf("	startbox ndays add 41 fillboxes		%% fill boxes after calendar\n");
1266       printf("} bind def\n\n");
1267    }
1268 
1269    if (do_whole_year) {
1270 
1271       if (rotate == LANDSCAPE) {
1272          /* medium months (landscape) */
1273          printf("/hspace gridwidth daywidth add def	%% one date box space between calendars\n");
1274          printf("/hscale 0.2258 def			%% 1 / (4 + 3/7)\n");
1275          printf("/vspace gridheight ytop add 135 add neg def %% vertical spacing\n");
1276          printf("/vscale .25 def 			%% beats me how this was calculated\n");
1277          printf("\n");
1278          printf("/printmonth {\n");
1279          printf("	/calsize medium def\n");
1280          printf("\n");
1281          printf("	posn 0 eq {		%% assume first month printed on page is posn 0\n");
1282          printf("		startpage\n");
1283          printf("		footstrings\n");
1284          printf("	} if\n");
1285          printf("\n");
1286          printf("	gsave			%% draw medium calendar at selected position\n");
1287          printf("	hscale vscale scale	%% landscape mode - 3 rows, 4 cols\n");
1288          printf("	posn 4 mod hspace mul\n");
1289          printf("	posn 4 idiv vspace mul 45 add\n");
1290          printf("	translate\n");
1291          printf("	calendar\n");
1292          printf("	grestore\n");
1293          printf("} bind def\n\n");
1294       }
1295       else {
1296          /* medium months (portrait)  */
1297          printf("/hspace gridwidth daywidth add def	%% one date box space between calendars\n");
1298          printf("/hscale 0.3043 def			%% 1 / (3 + 2/7)\n");
1299          printf("/vspace gridheight ytop add 135 add neg def %% vertical spacing\n");
1300          printf("/vscale .194 def			%% beats me how this was calculated\n");
1301          printf("\n");
1302          printf("/printmonth {	\n");
1303          printf("	/calsize medium def\n");
1304          printf("\n");
1305          printf("	posn 0 eq {		%% assume first month printed on page is posn 0\n");
1306          printf("		gsave		%% print foot strings at original scale\n");
1307          printf("		startpage\n");
1308          printf("		0 25 translate  %% reposition footstrings slightly higher\n");
1309          printf("		footstrings\n");
1310          printf("		grestore\n");
1311          printf("\n");
1312          printf("		/sv_ysval ysval def	%% re-scale Y axis to full page height\n");
1313          printf("		/ysval xsval pageheight pagewidth div dup mul mul def\n");
1314          printf("		startpage\n");
1315          printf("		/ysval sv_ysval def\n");
1316          printf("	} if\n");
1317          printf("\n");
1318          printf("	gsave			%% draw medium calendar at selected position\n");
1319          printf("	hscale vscale scale	%% portrait mode - 4 rows, 3 cols\n");
1320          printf("	posn 3 mod hspace mul\n");
1321          printf("	posn 3 idiv vspace mul 155 add\n");
1322          printf("	translate\n");
1323          printf("	calendar\n");
1324          printf("	grestore\n");
1325          printf("} bind def\n\n");
1326       }
1327    }
1328    else {
1329       if (head) {
1330          /* date text */
1331          printf("%%\n");
1332          printf("%% Functions for printing text (and/or images) inside boxes:\n");
1333          printf("%%\n");
1334          printf("\n");
1335          printf("%% <day> <text> daytext => --\n");
1336          printf("%%\n");
1337          printf("%% print <text> in <day> box (below date)\n");
1338          printf("%%\n");
1339          printf("/daytext {\n");
1340          printf("	/mytext exch def /day exch def\n");
1341          printf("	NotesFontSet\n");
1342          printf("	day datepos datefontsize large get 0.75 mul datemargin large get\n");
1343          printf("	  2 mul add notesfontsize 0.75 mul add sub dup\n");
1344          printf("	  /ypos exch def exch textmargin add exch moveto\n");
1345          printf("	currentpoint pop /LM exch def\n");
1346          printf("	/RM LM daywidth textmargin 2 mul sub add def\n");
1347          printf("	showtext\n");
1348          printf("} bind def\n\n");
1349 
1350          printf("%% <day> <text> holidaytext => --\n");
1351          printf("%%\n");
1352          printf("%% print <text> in <day> box (to right of date)\n");
1353          printf("%%\n");
1354          printf("/holidaytext {\n");
1355          printf("	/mytext exch def /day exch def\n");
1356          printf("\n");
1357          printf("	/datesize datefontsize large get def \n");
1358          printf("	/margin datemargin large get def\n");
1359          printf("	/dwidth datewidth day 10 lt { 0 } { 1 } ifelse get def\n");
1360          printf("\n");
1361          printf("	%% display the text between the date and the moon icon (if any)\n");
1362          printf("	NotesFontSet\n");
1363          printf("	day datepos margin notesfontsize 0.75 mul add sub dup\n");
1364          printf("	/ypos exch def exch dwidth add exch moveto\n");
1365          printf("	currentpoint pop /LM exch def\n");
1366          printf("	/mwidth do-moon-p {offset radius add} {0} ifelse def	%% moon width\n");
1367          printf("	/RM LM daywidth textmargin sub dwidth mwidth add sub add def\n");
1368          printf("	showtext\n");
1369          printf("} bind def\n\n");
1370 
1371          printf("%% <box> <text> notetext => --\n");
1372          printf("%%\n");
1373          printf("%% print notes heading (if any) and <text> in <box> (0..41)\n");
1374          printf("%%\n");
1375          printf("/notetext {\n");
1376          printf("	/mytext exch def /box exch def\n");
1377          printf("\n");
1378          printf("	%% skip notes box heading if null\n");
1379          printf("	notesheading () eq {\n");
1380          printf("		box boxpos notemargin notesfontsize 0.75 mul add sub dup\n");
1381          printf("	} {\n");
1382          printf("		box boxpos notemargin headingfontsize 0.75 mul add sub exch\n");
1383          printf("		  notemargin add exch moveto\n");
1384          printf("		HeadingFontSet\n");
1385          printf("		notesheading show\n");
1386          printf("		box boxpos notemargin headingfontsize add\n");
1387          printf("		  notesfontsize add sub dup\n");
1388          printf("	} ifelse\n");
1389          printf("	/ypos exch def exch notemargin add exch moveto\n");
1390          printf("\n");
1391          printf("	%% display the notes text\n");
1392          printf("	NotesFontSet\n");
1393          printf("	/LM currentpoint pop def\n");
1394          printf("	/RM LM daywidth notemargin 2 mul sub add def\n");
1395          printf("	showtext\n");
1396          printf("} bind def\n\n");
1397 
1398          printf("%% -- crlf => --\n");
1399          printf("%%\n");
1400          printf("%% simulate carriage return/line feed sequence\n");
1401          printf("%%\n");
1402          printf("/crlf {\n");
1403          printf("	ypos notesfontsize sub /ypos exch def LM ypos moveto\n");
1404          printf("} bind def\n\n");
1405 
1406          printf("%% <string> prstr => --\n");
1407          printf("%%\n");
1408          printf("%% print <string> on current line if possible; otherwise print on next line\n");
1409          printf("%%\n");
1410          printf("/prstr {\n");
1411          printf("	gsave				%% in case rescaling is necessary\n");
1412          printf("	dup stringwidth pop currentpoint pop add RM gt {	%% too wide?\n");
1413          printf("		%% move to the next line (unless this is the initial word)\n");
1414          printf("		currentpoint pop LM ne {\n");
1415          printf("			crlf hangingindent show\n");
1416          printf("		} if\n");
1417          printf("		%% rescale in X dimension as necessary to make word fit\n");
1418          printf("		dup stringwidth pop RM currentpoint pop sub gt {\n");
1419          printf("			dup stringwidth pop RM currentpoint pop sub exch div 1\n");
1420          printf("			scale\n");
1421          printf("		} if\n");
1422          printf("	} if\n");
1423          printf("	currfonttype BOLD eq\n");
1424          printf("		{ dup currentpoint 2 index show exch 0.5 add exch moveto show }\n");
1425          printf("		{ show }\n");
1426          printf("	ifelse\n");
1427          printf("	currentpoint grestore moveto	%% grestore alone would reset position\n");
1428          printf("} bind def\n\n");
1429 
1430          printf("%% -- showtext => --\n");
1431          printf("%%\n");
1432          printf("%% print words in 'mytext', inserting line breaks where necessary (or requested)\n");
1433          printf("%%\n");
1434          printf("/showtext {\n");
1435          printf("	mytext {\n");
1436          printf("		/word exch def\n");
1437          printf("		/i 1 def\n");
1438          printf("		/n 0 def\n");
1439          printf("\n");
1440          printf("		%% look up current word in list of special formatting strings\n");
1441          printf("		[ linesep boldfont italicfont romanfont ] {\n");
1442          printf("			word eq {\n");
1443          printf("				/n i def\n");
1444          printf("			} if\n");
1445          printf("			/i i 1 add def\n");
1446          printf("		} forall\n");
1447          printf("	\n");
1448          printf("		%% n will be 0 if current word is an ordinary word, 1..4 if it\n");
1449          printf("		%% is a formatting string; execute corresponding block of code\n");
1450          printf("		[\n");
1451          printf("			{ word prstr ( ) show }		%% ordinary word\n");
1452          printf("\n");
1453          printf("			{ crlf				%% line separator (.p)\n");
1454          printf("			  NotesFontSet }		%% reset font type\n");
1455          printf("\n");
1456          printf("			{ NotesFontSet			%% font => bold (.b)\n");
1457          printf("			  /currfonttype BOLD def }\n");
1458          printf("\n");
1459          printf("			{ /currfonttype ITALIC def	%% font => italic (.i)\n");
1460          printf("			  notesfont findfont italicmatrix makefont setfont }\n");
1461          printf("\n");
1462          printf("			{ NotesFontSet }		%% font => roman (.r)\n");
1463          printf("		] n get exec\n");
1464          printf("	} forall\n");
1465          printf("} bind def\n\n");
1466 
1467          printf("%% <day> <text> <EPS-image-filename> <x-scaling> <y-scaling> <x-offset> <y-offset> epsimage => --\n");
1468          printf("%%\n");
1469          printf("%% print image <EPS-image-filename> in <day> box (below date)\n");
1470          printf("%%\n");
1471          printf("/epsimage {\n");
1472          printf("	/yoffset exch def /xoffset exch def\n");
1473          printf("	/yscale exch def /xscale exch def\n");
1474          printf("	/myimage exch def /mytext exch def /day exch def\n");
1475          printf("	NotesFontSet\n");
1476          printf("	day datepos datefontsize large get 0.75 mul datemargin large get\n");
1477          printf("	2 mul add\n");
1478          printf("	sub dup\n");
1479          printf("	/ypos exch def exch textmargin add exch\n");
1480          printf("	moveto\n");
1481          printf("	currentpoint\n");
1482          printf("	\n");
1483          printf("	gsave\n");
1484          printf("	yoffset add exch xoffset add exch  %% add the X,Y offsets\n");
1485          printf("	translate\n");
1486          printf("	xscale yscale scale\n");
1487          printf("\n");
1488          printf("	%% \n");
1489          printf("	%% Temporarily redefine the 'showpage' operator to null so that it's\n");
1490          printf("	%% ignored when the EPS image file is processed...\n");
1491          printf("	%%\n");
1492          printf("	1 dict begin /showpage { } bind def\n");
1493          printf("	myimage run\n");
1494          printf("	end\n");
1495          printf("        \n");
1496          printf("	grestore\n");
1497          printf("} bind def\n\n");
1498       }
1499 
1500 
1501       /* single month */
1502 
1503       printf("%% -- printmonth => --\n");
1504       printf("%%\n");
1505       printf("%% print full-size calendar for 'month'/'year' on single page\n");
1506       printf("%%\n");
1507       printf("/printmonth {\n");
1508       printf("	/calsize large def\n");
1509       printf("	startpage\n");
1510       printf("	calendar\n");
1511       printf("	printsmallcals				%% small calendars\n");
1512       printf("	\n");
1513       printf("} bind def\n\n");
1514 
1515       printf("%% scale factor (slightly < 1/7) and offset for printing small calendars\n");
1516       printf("/scscale .138 def\n");
1517       printf("/scoffset gridwidth gridwidth scscale 7 mul mul sub 2.0 div def\n");
1518       printf("\n");
1519       printf("%% -- printsmallcals => --\n");
1520       printf("%%\n");
1521       printf("%% print small calendars in boxes 'prev_small_cal' and 'next_small_cal'\n");
1522       printf("%%\n");
1523       printf("/printsmallcals {\n");
1524       printf("	/calsize small def\n");
1525       printf("	/sv_startbox startbox def\n");
1526       printf("\n");
1527       printf("	prev_small_cal 0 ge {			%% previous month/year\n");
1528       printf("		/year p_year def\n");
1529       printf("		/month p_month def\n");
1530       printf("		/startbox p_startbox def\n");
1531       printf("		/ndays p_ndays def\n");
1532       printf("		gsave\n");
1533       printf("		prev_small_cal boxpos translate\n");
1534       printf("		scscale dup scale\n");
1535       printf("		scoffset ytop neg translate\n");
1536       printf("		calendar\n");
1537       printf("		grestore\n");
1538       printf("	} if\n");
1539       printf("\n");
1540       printf("	next_small_cal 0 ge {			%% next month/year\n");
1541       printf("		/year n_year def\n");
1542       printf("		/month n_month def\n");
1543       printf("		/startbox n_startbox def\n");
1544       printf("		/ndays n_ndays def\n");
1545       printf("		gsave\n");
1546       printf("		next_small_cal boxpos translate\n");
1547       printf("		scscale dup scale\n");
1548       printf("		scoffset ytop neg translate\n");
1549       printf("		calendar\n");
1550       printf("		grestore\n");
1551       printf("	} if\n");
1552       printf("\n");
1553       printf("	/startbox sv_startbox def		%% required for text boxes\n");
1554       printf("} bind def\n\n");
1555    }
1556 
1557    if (julian_dates == NO_JULIANS) {
1558       /* no julian dates */
1559       printf("/drawjnums {} bind def\n\n");
1560    }
1561    else {
1562       /* some julian dates */
1563       printf("%% -- drawjnums => --\n");
1564       printf("%%\n");
1565       printf("%% print day-of-year (and, optionally, days remaining) for each date\n");
1566       printf("%%\n");
1567       printf("/drawjnums {\n");
1568       printf("	NotesFontSet\n");
1569       printf("	1 datepos dayheight 3 sub sub exch daywidth 3 sub add exch moveto\n");
1570       printf("\n");
1571       printf("	1 1 ndays {\n");
1572       printf("		/day exch def\n");
1573       printf("		/jday jdstart day add 1 sub def\n");
1574       printf("		/str jday 3 string cvs def\n");
1575       printf("		julian-dates true eq {		%% print days left in year?\n");
1576       printf("			/str str ( \\050) strcat yearlen jday sub 3 string cvs\n");
1577       printf("				strcat (\\051) strcat def\n");
1578       printf("		} if\n");
1579       printf("		gsave\n");
1580       printf("		str dup stringwidth pop 0 exch sub 0 rmoveto show\n");
1581       printf("		grestore\n");
1582       printf("		nextbox\n");
1583       printf("	} for\n");
1584       printf("} bind def\n\n");
1585    }
1586 
1587    if (draw_moons == NO_MOONS) {
1588       /* no moons */
1589       printf("/do-moon-p { false } bind def\n");
1590       printf("/drawmoons {} bind def\n\n");
1591    }
1592    else {
1593       /* moons on some or all days */
1594       printf("%%\n");
1595       printf("%% Moon-drawing functions:\n");
1596       printf("%%\n\n");
1597 
1598       printf("%% <phase> domoon => --\n");
1599       printf("%%\n");
1600       printf("%% draw icon showing moon at <phase> (0 = new; .25 = fq; .5 = full; .75 = lq)\n");
1601       printf("%%\n");
1602       printf("/domoon {\n");
1603       printf("	/phase exch def\n");
1604       printf("\n");
1605       printf("	gsave\n");
1606       printf("	currentpoint translate\n");
1607       printf("	newpath\n");
1608       printf("\n");
1609       printf("	%% if moon is full, just draw unfilled circle\n");
1610       printf("\n");
1611       printf("	phase halfperiod .01 sub ge phase halfperiod .01 add le and {\n");
1612       printf("		0 0 radius\n");
1613       printf("		0 360 arc stroke\n");
1614       printf("	}\n");
1615       printf("	{\n");
1616       printf("		%% draw the line arc now; prepare (but don't draw) the fill arc\n");
1617       printf("\n");
1618       printf("		0 0 radius			%% for line and fill arcs\n");
1619       printf("		0 0 radius \n");
1620       printf("		phase halfperiod lt {		%% phase between new and full\n");
1621       printf("			270 90 arc stroke	%% (line on right, fill on left)\n");
1622       printf("			0 radius neg moveto\n");
1623       printf("			270 90 arcn \n");
1624       printf("		}\n");
1625       printf("		{				%% phase between full and new\n");
1626       printf("			90 270 arc stroke	%% (line on left, fill on right)\n");
1627       printf("			0 radius neg moveto\n");
1628       printf("			270 90 arc \n");
1629       printf("			/phase phase halfperiod sub def\n");
1630       printf("		} ifelse\n");
1631       printf("\n");
1632       printf("		%% curveto uses (x0,y0) [current point], (x1,y1), (x2,y2),\n");
1633       printf("		%% and (x3,y3) as the control points for drawing a Bezier\n");
1634       printf("		%% cubic section, used here as the curve dividing the moon\n");
1635       printf("		%% icon into dark and light sections.  x1 is in the range\n");
1636       printf("		%% -R*sqrt(2) <= x1 <= R*sqrt(2) and y1 is in the range\n");
1637       printf("		%% 0 <= y1 <= R; note that except in the degenerate case\n");
1638       printf("		%% where x1 = y1 = x2 = y2 = 0, the curve does not actually\n");
1639       printf("		%% pass through (x1,y1) or (x2,y2).\n");
1640       printf("\n");
1641       printf("		/x1 quartperiod phase sub rect mul def\n");
1642       printf("		/y1 x1 abs 2 sqrt div def\n");
1643       printf("\n");
1644       printf("		%% push control points for curveto\n");
1645       printf("\n");
1646       printf("					%% x0 = 0   (current\n");
1647       printf("					%% y0 = R    point)\n");
1648       printf("		x1			%% x1\n");
1649       printf("		y1			%% y1\n");
1650       printf("		x1			%% x2 = x1\n");
1651       printf("		y1 neg			%% y2 = -y1\n");
1652       printf("		0			%% x3 = 0\n");
1653       printf("		radius neg		%% y3 = -R\n");
1654       printf("\n");
1655       printf("		%% draw Bezier curve; fill area between curve and fill arc\n");
1656       printf("\n");
1657       printf("		curveto\n");
1658       printf("		fill\n");
1659       printf("	} ifelse\n");
1660       printf("\n");
1661       printf("	grestore\n");
1662       printf("} bind def\n\n");
1663 
1664       printf("%% -- do-moon-p => <bool>\n");
1665       printf("%%\n");
1666       printf("%% determine whether or not moon icon is to be drawn on 'day'; push result\n");
1667       printf("%%\n");
1668       printf("/do-moon-p {\n");
1669       printf("	draw-moons (some) eq {		%% printing quarter moons?  look up day\n");
1670       printf("		/p false def\n");
1671       printf("		quarter_moons { day eq { /p true def } if } forall\n");
1672       printf("		p\n");
1673       printf("	}\n");
1674       printf("	{\n");
1675       printf("		draw-moons		%% all moons or no moons\n");
1676       printf("	} ifelse\n");
1677       printf("} bind def\n\n");
1678 
1679       printf("%% -- drawmoons => --\n");
1680       printf("%%\n");
1681       printf("%% main routine to draw moon icons on calendar\n");
1682       printf("%%\n");
1683       printf("/drawmoons {\n");
1684       printf("	gsave\n");
1685       printf("	moonlinewidth setlinewidth\n");
1686       printf("	1 datepos offset sub exch daywidth add offset sub exch moveto\n");
1687       printf("	/n 0 def			%% index into moon_phases\n");
1688       printf("	1 1 ndays {\n");
1689       printf("		/day exch def\n");
1690       printf("		do-moon-p {		%% draw a moon today?\n");
1691       printf("			moon_phases n get domoon\n");
1692       printf("			/n n 1 add def\n");
1693       printf("		} if\n");
1694       printf("		nextbox\n");
1695       printf("	} for\n");
1696       printf("	grestore\n");
1697       printf("} bind def\n\n");
1698    }
1699 
1700    /*
1701       Write out PostScript code to print calendars...
1702    */
1703 
1704    for (i = 0, this_month = init_month, this_year = init_year;
1705         i < nmonths;
1706         i++, BUMP_MONTH_AND_YEAR(this_month, this_year)) {
1707       print_month(this_month, this_year);
1708    }
1709 
1710 #ifdef EPS_DSC
1711    /* generate trailer at end of PostScript output */
1712    printf("%%%%Trailer\n");
1713    printf("clear flush\n");
1714    printf("%%%%EOF\n");
1715 #endif
1716 
1717    return;
1718 }
1719 
1720 /* ---------------------------------------------------------------------------
1721 
1722    write_calfile
1723 
1724    Notes:
1725 
1726       This routine writes dates in a format suitable for the old Unix
1727       "calendar" utility (and subsequent use by Pcal).
1728 
1729 */
write_calfile(void)1730 void write_calfile (void)
1731 {
1732    KWD *k;
1733    int i;
1734 
1735    /* look up the Pcal keywords (assumed present) for the -c output file */
1736    for (k = keywds; k->name; k++) {
1737       if (k->code == DT_NOTE) kw_note = k->name;
1738       if (k->code == DT_OPT)  kw_opt  = k->name;
1739       if (k->code == DT_YEAR) kw_year = k->name;
1740    }
1741 
1742    /* print the date style for subsequent use by Pcal */
1743    printf("%s -%c\n", kw_opt, date_style == USA_DATES ? F_USA_DATES :
1744           F_EUR_DATES);
1745 
1746    for (i = 0, this_month = init_month, this_year = init_year;
1747         i < nmonths;
1748         i++, BUMP_MONTH_AND_YEAR(this_month, this_year)) {
1749       print_dates(this_month, this_year);
1750    }
1751 
1752    return;
1753 }
1754 
1755 /* ---------------------------------------------------------------------------
1756 
1757    single_month_html
1758 
1759    Notes:
1760 
1761       This routine prints the specified month and year as an HTML table.
1762 
1763 */
single_month_html(int this_month,int this_year)1764 void single_month_html (int this_month, int this_year)
1765 {
1766    long holidays;
1767    int day, box, len;
1768    year_info *py;
1769    month_info *pm;
1770    register day_info *pd;
1771 
1772    /* table heading with month and year */
1773    printf("<a name=_%02d%02d>\n", this_month, this_year % 100);
1774    printf("<table width=100%% border=%d>\n", BORDER);
1775    printf("<tr><th colspan=7>%s", MONTHYEAR_PRE);
1776    print_html(months_ml[output_language][this_month-1]);
1777    printf(" %d%s</th></tr>\n", this_year, MONTHYEAR_POST);
1778 
1779    /* headings for weekday names */
1780    printf("<tr>");
1781    for (day = 0; day < 7; day++) {
1782       char buf[40];
1783       strcpy(buf, days_ml[output_language][(day + first_day_of_week) % 7]);
1784 
1785       /* buf[ABBR_MONTH_LEN] = '\0'; */
1786 
1787       printf("<th width=14%%>");
1788       print_html(buf);
1789       printf("</th>");
1790    }
1791    printf("</tr>\n");
1792 
1793    /* blank space at beginning (if necessary) - divide into N one-column
1794     * boxes (#if DIVIDE_BLANK_SPACE) or print as single N-column box
1795     */
1796    printf("<tr>\n");
1797    if ((box = START_BOX(this_month, this_year)) > 0) {
1798 
1799 #if DIVIDE_BLANK_SPACE
1800       int i;
1801       for (i = 0; i < box; i++) {
1802          printf("<td>");
1803          BLANKLINES(TEXTLINES);
1804          printf("</td>\n");
1805       }
1806 #else
1807       printf("<td colspan=%d>", box);
1808       BLANKLINES(TEXTLINES);
1809       printf("</td>\n");
1810 #endif
1811    }
1812 
1813    /* get pointer to text information for current month */
1814    pm = (py = find_year(this_year, FALSE)) != NULL ? py->month[this_month-1] : NULL;
1815 
1816    /* main loop for dates */
1817    len = LENGTH_OF(this_month, this_year);
1818    for (day = 1, holidays = pm ? pm->holidays : 0;
1819         day <= len;
1820         day++, box++, holidays >>= 1) {
1821       int nrows;
1822 
1823       if (box > 0 && box % 7 == 0) printf("<tr>\n");   /* start of row */
1824 
1825       printf("<td valign=top>");
1826 
1827       /* special HTML code for holidays (cf. pcaldefs.h) */
1828       if (holidays & 01) printf("%s%d%s", HOLIDAY_PRE, day, HOLIDAY_POST);
1829       else printf("%d", day);
1830 
1831       /* print associated text (count lines) */
1832       for (nrows = 0, pd = pm ? pm->day[day-1] : NULL;
1833            pd;
1834            pd = pd->next, nrows++) {
1835          /* Skip lines specifying an EPS image... */
1836          if (ci_strncmp(pd->text, "image:", 6) == 0) continue;
1837          printf("\n<br>");
1838          this_day = day;
1839          RESET_DATE();   /* reset working date */
1840          print_html(pd->text);
1841       }
1842 
1843       /* pad first box in each row with blank lines */
1844       if (box % 7 == 0 && nrows < TEXTLINES) BLANKLINES(TEXTLINES - nrows);
1845 
1846       printf("</td>\n");
1847       if (box % 7 == 6) printf("</tr>\n");   /* end of row */
1848    }
1849 
1850    /* blank space at end (if necessary) - divide into N one-column
1851     * boxes (#if DIVIDE_BLANK_SPACE) or print as single N-column box
1852     */
1853    if ((box %= 7) != 0) {
1854 #if DIVIDE_BLANK_SPACE
1855       int i;
1856       for (i = box; i < 7; i++) {
1857          printf("<td>");
1858          BLANKLINES(TEXTLINES);
1859          printf("</td>\n");
1860       }
1861 #else
1862       printf("<td colspan=%d>", 7 - box);
1863       BLANKLINES(TEXTLINES);
1864       printf("</td>\n");
1865 #endif
1866    }
1867    printf("</tr>\n");
1868    printf("</table>\n");
1869 
1870    /* links to other months on page */
1871    if (nmonths > 2) {
1872       int j, m, y;
1873       char c = date_style == EUR_DATES ? '.' : '/';
1874 
1875       printf("<p>");
1876       for (j = 0, m = init_month, y = init_year;
1877            j < nmonths;
1878            j++, BUMP_MONTH_AND_YEAR(m, y)) {
1879          printf("<a href=#_%02d%02d>%02d%c%02d</a>%s\n",
1880                 m, y % 100, m, c, y % 100,
1881                 j % 12 == 11 ? "<br>" : "&nbsp;");
1882       }
1883    }
1884 
1885    return;
1886 }
1887 
1888 
1889 /* ---------------------------------------------------------------------------
1890 
1891    single_month_one_column_html
1892 
1893    Notes:
1894 
1895       This routine prints the specified month and year as an
1896       HTML table in one column.
1897 
1898 */
single_month_one_column_html(int this_month,int this_year)1899 void single_month_one_column_html (int this_month, int this_year)
1900 {
1901    long holidays;
1902    int day, box = 0, len, i;
1903    year_info *py;
1904    month_info *pm;
1905    register day_info *pd;
1906    char *text_short;
1907 
1908    /* table heading with first 5 chars of the month */
1909    printf("<table border=%d>\n", BORDER);
1910    printf("<tr><th width=100px>%s</th></tr>\n", months_ml[output_language][this_month-1]);
1911 
1912    /* get pointer to text information for current month */
1913    pm = (py = find_year(this_year, FALSE)) != NULL ? py->month[this_month-1] : NULL;
1914 
1915    /* main loop for dates */
1916    len = LENGTH_OF(this_month, this_year);
1917    for (day = 1, holidays = pm ? pm->holidays : 0;
1918         day <= len;
1919         day++, box++, holidays >>= 1) {
1920       int nrows;
1921 
1922       /* print only one column for the month */
1923       printf("<tr>\n");
1924       printf("<td valign=top>");
1925 
1926       /* special HTML code for holidays (cf. pcaldefs.h) */
1927       if (holidays & 01) {
1928          printf("%s%d%s %c", HOLIDAY_PRE, day, HOLIDAY_POST,
1929                 days_ml[output_language][(day + START_BOX(this_month, this_year) +
1930                                           first_day_of_week - 1) % 7][0]);
1931       }
1932       else {
1933          /* print Sundays in bold */
1934          if ((day + START_BOX(this_month, this_year) + first_day_of_week - 1) % 7 == 0) {
1935             printf("<B>%d %c</B>", day,
1936                    days_ml[output_language][(day + START_BOX(this_month, this_year) +
1937                                              first_day_of_week - 1) % 7][0]);
1938          }
1939          /* print Saturdays in gray */
1940          else if ((day + START_BOX(this_month, this_year) + first_day_of_week - 1) % 7 == 6) {
1941             printf("<B><FONT color=darkgray>%d</FONT> %c</B>", day,
1942                    days_ml[output_language][(day + START_BOX(this_month, this_year) +
1943                                              first_day_of_week - 1) % 7][0]);
1944          }
1945          else {
1946             printf("%d %c", day, days_ml[output_language]
1947                    [(day + START_BOX(this_month, this_year) + first_day_of_week - 1) % 7][0]);
1948          }
1949          /* print associated text (count lines),
1950             not for holidays */
1951          for (nrows = 0, pd = pm ? pm->day[day-1] : NULL;
1952               pd;
1953               pd = pd->next, nrows++) {
1954             /* Skip lines specifying an EPS image... */
1955             if (ci_strncmp(pd->text, "image:", 6) == 0) continue;
1956             printf("\n<I>");
1957             this_day = day;
1958             RESET_DATE();   /* reset working date */
1959             text_short = pd->text;
1960             for  (i=0; i < 5; i++) {
1961                if (text_short[i] == '\0') break;
1962                /* if text starts with < it is probably an HTML tag (e.g. <I>)
1963                   -> skip three letters
1964                */
1965                if(text_short[0] == '<' && text_short[i+3] == '\0') break;
1966                if(text_short[0] == '<') printf("%c",text_short[i+3]);
1967                else printf("%c",text_short[i]);
1968             }
1969             printf("</I>\n");
1970          }
1971       }
1972 
1973       /* TR-tag to end the row */
1974       printf("</td></TR>\n");
1975       if (box % 7 == 6) printf("</tr>\n");   /* end of row */
1976    }
1977    printf("</tr>\n");
1978    printf("</table>\n");
1979 
1980    return;
1981 }
1982 
1983 /* ---------------------------------------------------------------------------
1984 
1985    multiple_months_html
1986 
1987    Notes:
1988 
1989       This routine prints an HTML table in 'ncols' (assumed to be either 3 or
1990       4) columns, one column per month starting with the specified month and
1991       year.
1992 
1993 */
multiple_months_html(int first_month,int first_year,int ncols)1994 void multiple_months_html (int first_month, int first_year, int ncols)
1995 {
1996    long holidays;
1997    int day, box, len;
1998    year_info *py;
1999    month_info *pm;
2000    int i, m, d, y, w, wd1, wd2, nw;
2001 
2002    /* width of dates and spaces between months */
2003    wd1 = 12 / ncols;
2004    wd2 = (100 - (ncols * (7 * wd1))) / (ncols - 1);
2005 
2006    /* table heading with months and year */
2007    printf("<table width=100%% border=%d>\n", 0);
2008 
2009    printf("<tr>");
2010    for (i = 0, m = first_month, y = first_year;
2011         i < ncols;
2012         i++, BUMP_MONTH_AND_YEAR(m, y)) {
2013       if (i > 0) printf("<th><br></th>");
2014       printf("<th colspan=7>%s", MONTHYEAR_W_PRE);
2015       print_html(months_ml[output_language][m-1]);
2016       printf(" %d%s</th>\n", y, MONTHYEAR_W_POST);
2017    }
2018    printf("</tr>\n");
2019 
2020    /* headings for weekday names */
2021    printf("<tr>");
2022    for (i = 0; i < ncols; i++) {
2023       if (i > 0) printf("\n<th width=%d%%><br></th>", wd2);
2024       for (day = 0; day < 7; day++) {
2025          char buf[40];
2026          strcpy(buf, days_ml[output_language][(day + first_day_of_week) % 7]);
2027 #if 0
2028          buf[ncols == 3 ? 3 : 2] = '\0';
2029 #else
2030          buf[2] = '\0';   /* truncate to two characters */
2031 #endif
2032          printf("<th width=%d%%>", wd1);
2033          print_html(buf);
2034          printf("</th>");
2035       }
2036    }
2037    printf("</tr>\n");
2038 
2039 #ifdef CONSISTENT_SPACING
2040    /* always print 6 weeks (to maintain consistent vertical spacing) */
2041    nw = 6;
2042 #else
2043    /* print 5 weeks unless at least one month requires a sixth week */
2044    nw = 5;
2045    for (i = 0, m = first_month, y = first_year;
2046         i < ncols;
2047         i++, BUMP_MONTH_AND_YEAR(m, y)) {
2048       if (START_BOX(m, y) + LENGTH_OF(m, y) > 35) nw = 6;
2049    }
2050 #endif
2051 
2052    /* main loop to print dates (one week at a time) */
2053 
2054    for (w = 0; w < nw; w++) {
2055       printf("<tr>");
2056 
2057       /* print one week of dates for each month */
2058 
2059       for (i = 0, m = first_month, y = first_year;
2060            i < ncols;
2061            i++, BUMP_MONTH_AND_YEAR(m, y)) {
2062          if (i > 0) printf("<td><br></td>");
2063 
2064          /* get starting box, month length, holiday info */
2065          box = START_BOX(m, y);
2066          len = LENGTH_OF(m, y);
2067          pm = (py = find_year(y, FALSE)) != NULL ? py->month[m-1] : NULL;
2068          holidays = pm ? pm->holidays : 0;
2069 
2070          /* loop over days in current week and month */
2071 
2072          for (d = 1; d <= 7; d++) {
2073             day = (7 * w) + d - box;
2074             if (day < 1 || day > len) printf("<td><br></td>");
2075             else {
2076                printf("<td align=center>");
2077                if (holidays & (1L << (day - 1))) {
2078                   printf("%s%d%s", HOLIDAY_PRE, day, HOLIDAY_POST);
2079                }
2080                else printf("%d", day);
2081 
2082                printf("</td>");
2083             }
2084          }
2085       }
2086       printf("</tr>\n");
2087    }
2088 
2089    /* end of table */
2090    printf("</table>\n");
2091 
2092    return;
2093 }
2094 
2095 /* ---------------------------------------------------------------------------
2096 
2097    write_htmlfile
2098 
2099    Notes:
2100 
2101       This routine generates a calendar in HTML format.
2102 
2103 */
write_htmlfile(void)2104 void write_htmlfile (void)
2105 {
2106    int i;
2107    static struct {
2108       char *attribute;   /* attribute name */
2109       char *value;   /* attribute value */
2110       int   numeric;   /* TRUE if value is numeric */
2111    } *pb, body_attributes[] = {   /* cf. pcaldefs.h */
2112       { "bgcolor",	BGCOLOR,	TRUE },
2113       { "background",	BACKGROUND,	FALSE },
2114       { "text",		TEXT,		TRUE },
2115       { "link",		LINK,		TRUE },
2116       { "alink",	ALINK,		TRUE },
2117       { "vlink",	VLINK,		TRUE },
2118       { NULL,		NULL,		FALSE }   /* must be last */
2119    };
2120 
2121    this_day = 1;   /* initialize working date */
2122    this_month = init_month;
2123    this_year = init_year;
2124    RESET_DATE();
2125 
2126    /* HTML boilerplate */
2127    printf("<html>\n");
2128    printf("<head>\n");
2129 
2130    /*
2131     * if center footstring (-C) was specified, use it as the title;
2132     * otherwise contrive title from beginning/ending month/year
2133     */
2134    printf("<title>");
2135    if (cfoot[0]) {
2136       print_html(cfoot);
2137    }
2138    else {
2139       if (do_whole_year && init_month == JAN) {
2140          printf("%d", init_year);
2141          if (final_year > init_year) printf(" - %d", final_year);
2142       }
2143       else {
2144          char c = date_style == EUR_DATES ? '.' : '/';
2145          printf("%d%c%02d", init_month, c, init_year % 100);
2146          if (nmonths > 1) {
2147             printf(" - %d%c%02d", final_month, c, final_year % 100);
2148          }
2149       }
2150    }
2151    printf("</title>\n");
2152    printf("</head>\n");
2153 
2154    /* generate <body> definition with attributes */
2155    printf("<body");
2156    for (pb = body_attributes; pb->attribute; pb++) {
2157       char *p = pb->value;
2158       if (p && *p) {
2159          printf(" %s=%s%s\"", pb->attribute,
2160                 pb->numeric && p[0] != '#' ? "\"#" : "\"",
2161                 pb->value);
2162       }
2163    }
2164    printf(">\n");
2165 
2166    /* repeat center footstring (if specified) as heading */
2167    if (cfoot[0]) {
2168       RESET_DATE();
2169       printf("%s", HEADING_PRE);
2170       print_html(cfoot);
2171       printf("%s\n", HEADING_POST);
2172    }
2173 
2174    /* loop over all specified months, creating HTML table for each */
2175    this_month = init_month;
2176    this_year = init_year;
2177 
2178    if (do_whole_year) {
2179       /* whole-year mode - generate 3- or 4-column tables */
2180       int j, nc;
2181 
2182       nc = (rotate == LANDSCAPE) ? 4 : 3;
2183       for (i = 0; i < nmonths; i += nc) {
2184          if (i > 0) printf("<p>\n");
2185          multiple_months_html(this_month, this_year, nc);
2186          for (j = 0; j < nc; j++) BUMP_MONTH_AND_YEAR(this_month, this_year);
2187       }
2188    }
2189    else {
2190       /* single-month mode - generate one month per table
2191        * either in one or several columns */
2192       if (one_column) printf("<TABLE><TR>");
2193       for (i = 0; i < nmonths; i++) {
2194          if (one_column) {
2195             printf("<TD valign=\"top\">");
2196             single_month_one_column_html(this_month, this_year);
2197             printf("</TD>");
2198          }
2199          else {
2200             printf("<TD valign=\"top\" width=\"%i%%\">",100/nmonths);
2201             if (i > 0) printf("<p><br>\n");
2202             single_month_html(this_month, this_year);
2203          }
2204          BUMP_MONTH_AND_YEAR(this_month, this_year);
2205       }
2206       printf("</TR></TABLE>");
2207    }
2208 
2209    /* boilerplate to terminate HTML file */
2210    printf("</body>\n");
2211    printf("</html>\n");
2212 
2213    return;
2214 }
2215 
2216 /*
2217  * low-level utilities for PostScript generation
2218  */
2219 
2220 /* ---------------------------------------------------------------------------
2221 
2222    set_rgb
2223 
2224    Notes:
2225 
2226       This routine converts "<r>:<g>:<b>" to [r g b] or "<gray>" to gray.
2227 
2228       It returns a pointer to a static buffer containing the converted string.
2229 
2230 */
set_rgb(char * s)2231 char *set_rgb (char *s)
2232 {
2233    static char buf[STRSIZ];
2234    char *p1, *p2;
2235    double val[3];
2236    int n;
2237 
2238    val[0] = val[1] = val[2] = 0;   /* defaults */
2239 
2240    /* extract 1 - 3 floating-point values from string */
2241    for (n = 1, p1 = s; n <= 3; n++, p1 = p2 + 1) {
2242       val[n-1] = atof(p1);
2243       if ((p2 = strchr(p1, RGB_CHAR)) == NULL) break;
2244    }
2245 
2246    /* single value is gray scale; assume anything else is [r g b] */
2247    if (n > 1) sprintf(buf, "[%.3f %.3f %.3f]", val[0], val[1], val[2]);
2248    else sprintf(buf, "%.3f", val[0]);
2249 
2250    return buf;
2251 }
2252 
2253 /* ---------------------------------------------------------------------------
2254 
2255    select_color
2256 
2257    Notes:
2258 
2259       This routine chooses a color which contrasts with the majority of
2260       weekday colors, if the holiday color has not been explicitly selected.
2261 
2262 */
select_color(void)2263 int select_color (void)
2264 {
2265    int i, min, index = BLACK;
2266    char count[NUM_COLORS];
2267 
2268    for (i = 0; i < NUM_COLORS; i++) {   /* clear counts */
2269       count[i] = 0;
2270    }
2271 
2272    for (i = SUN; i <= SAT; i++) {   /* count colors */
2273       count[(int)day_color[i]]++;
2274    }
2275 
2276    /* find smallest non-zero count; set its index and value */
2277    for (i = 0, min = 99; i < NUM_COLORS; i++) {
2278       if (count[i] && count[i] < min) min = count[index = i];
2279    }
2280 
2281    /* return least-used color (or pick one if only one color used) */
2282    return min == 7 ? index == BLACK ? GRAY : BLACK : index;
2283 }
2284 
2285 
2286 /* ---------------------------------------------------------------------------
2287 
2288    ordinal_suffix
2289 
2290    Notes:
2291 
2292       This routine translates the %o (ordinal suffix) format specifier into
2293       the appropriate string for printing.
2294 
2295       The parameter is an ordinal number, assumed positive.
2296 
2297 */
ordinal_suffix(int num)2298 char *ordinal_suffix (int num)
2299 {
2300    static char buf[10];
2301    int tens, units;
2302 
2303    /* Select suffix according to rules for target language.  In English,
2304       the rules are as follows:
2305 
2306        tens digit      units digit     suffix
2307          (any)           0, 4..9        "th"
2308            1              (any)         "th"
2309          != 1               1           "st"
2310          != 1               2           "nd"
2311          != 1               3           "rd"
2312    */
2313    tens = (num / 10) % 10;
2314    units = num % 10;
2315    strcpy(buf, ord_suffix[(units > 3 || tens == 1) ? 0 : units]);
2316 
2317    return buf;
2318 }
2319 
2320 /* ---------------------------------------------------------------------------
2321 
2322    expand_fmt
2323 
2324    Notes:
2325 
2326       This routine expands a 'strftime'-like date format specifier.
2327 
2328       Pcal supports '%[aAbBdjmUWyY]' from 'strftime()' plus '%[luwDM]' and
2329       prefixes '[0o+-]' (see below).
2330 
2331       The expanded string is placed in an output buffer and a pointer to the
2332       character following the end of format specifier is returned.
2333 
2334       Assumes working date has been initialized (via RESET_DATE() macro) prior
2335       to first call for a given text string
2336 
2337 */
expand_fmt(char * buf,char * p)2338 char *expand_fmt (char *buf,   /* output buffer (filled in) */
2339                    char *p)    /* character following percent sign */
2340 {
2341    char c;
2342    static char *prefixes = "0o+-";
2343    int firstday, wkday;
2344    int adjust = 0, print_lz = FALSE, ordinal = FALSE, prev_num = -1;
2345    int num_present = FALSE, num_value = 0;
2346    date_str date;
2347 
2348    /* For compatibility with version 4.1, still support %[+-][bBdmY] (print
2349       the next/last month-name/day/month/year).  Version 4.11 introduces
2350       %[+-]<n>[DWMY], which adjusts the working date by [+-]<n>
2351       days/weeks/months/years; this method is preferred due to its greater
2352       flexibility.
2353    */
2354 
2355    buf[0] = '\0';   /* initialize output to null string */
2356 
2357    do {   /* loop until format character found */
2358       switch (c = *p++) {
2359       case 'a':   /* %a : abbreviated weekday */
2360          wkday = calc_weekday(work_month, work_day, work_year);
2361          strcpy(buf, days_ml_short[output_language][wkday]);
2362          break;
2363 
2364       case 'A':   /* %A : full weekday */
2365          wkday = calc_weekday(work_month, work_day, work_year);
2366          strcpy(buf, days_ml[output_language][wkday]);
2367          break;
2368 
2369       case 'b':   /* %b : abbreviated month name */
2370       case 'B':   /* %B : full month name */
2371          strcpy(buf, months_ml[output_language][(work_month + adjust + 11) % 12]);
2372          if (c == 'b')
2373             buf[ABBR_MONTH_LEN] = '\0';
2374          break;
2375 
2376       case 'd':   /* %d : day of month (01-31) */
2377          prev_num = work_day;
2378          sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
2379          break;
2380 
2381       case 'D':   /* %D : adjust working date by <N> days (NEW) */
2382          if (!num_present || num_value == 0) RESET_DATE();
2383          else SET_DATE(adjust * num_value);
2384          break;
2385 
2386       case 'j':   /* %j : day of year (001-366) */
2387          prev_num = DAY_OF_YEAR(work_month, work_day, work_year);
2388          sprintf(buf, print_lz ? "%03d" : "%d", prev_num);
2389          break;
2390 
2391       case 'l':   /* %l : days left in year (000-365) (NEW) */
2392          prev_num = YEAR_LEN(work_year) - DAY_OF_YEAR(work_month, work_day, work_year);
2393          sprintf(buf, print_lz ? "%03d" : "%d", prev_num);
2394          break;
2395 
2396       case 'm':   /* %m : month (01-12) */
2397          prev_num = (work_month + adjust + 11) % 12 + 1;
2398          sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
2399          break;
2400 
2401       case 'M':   /* %M : adjust date by <N> months (NEW) */
2402          if (!num_present || num_value == 0) {
2403             RESET_DATE();
2404          }
2405          else {
2406             int len;
2407 
2408             work_month += adjust * num_value;
2409             while (work_month > DEC) {
2410                work_month -= 12;
2411                work_year++;
2412             }
2413             while (work_month < JAN) {
2414                work_month += 12;
2415                work_year--;
2416             }
2417 
2418             /* make sure day of new month is legal */
2419             len = LENGTH_OF(work_month, work_year);
2420             if (work_day > len) work_day = len;
2421          }
2422          break;
2423 
2424          /* %u considers the week containing 1/1 to be week 1 and the next
2425             "logical Sunday" (the first day of the week as printed - cf. the
2426             -F option) to be the start of week 2; %U considers the first
2427             "logical Sunday" of the year to be the start of week 1.  %w and %W
2428             behave like %u and %U respectively, but use the first "logical
2429             Monday" instead.
2430          */
2431       case 'W':   /* %W : week number (00-53)       */
2432          /* %W, if prefaced by [+-]N, adjusts the date by [+-]N weeks (resets
2433             if N == 0); check for this case first
2434          */
2435          if (num_present) {
2436             if (num_value == 0) RESET_DATE();   /* N = 0: reset date */
2437             else SET_DATE(7 * adjust * num_value);
2438             break;
2439          }
2440          /* fall through */
2441       case 'u':   /* %u : week number (01-54) (NEW) */
2442       case 'U':   /* %U : week number (00-53)       */
2443       case 'w':   /* %w : week number (01-54) (NEW) */
2444          firstday = ((tolower(c) == 'w' ? 15 : 14) -
2445                      START_BOX(JAN, work_year)) % 7 + 1;
2446          prev_num = (DAY_OF_YEAR(work_month, work_day,
2447                                  work_year) - firstday + 7) / 7;
2448          if (islower((int)c) && firstday != 1) prev_num++;
2449          sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
2450          break;
2451 
2452       case 'y':   /* %y : year w/o century (00-99) */
2453          prev_num = (work_year + adjust) % 100;
2454          sprintf(buf, "%02d", prev_num);
2455          break;
2456 
2457       case 'Y':   /* %Y : year w/century */
2458          /* %Y, if prefaced by [+-]N, adjusts the date by [+-]N years (resets
2459             if N == 0); check for this case first
2460           */
2461          if (num_present) {
2462             if (num_value == 0) { /* N = 0: reset date */
2463                RESET_DATE();
2464             }
2465             else {
2466                int len;
2467 
2468                work_year += adjust * num_value;
2469 
2470                /* make sure day is legal */
2471                len = LENGTH_OF(work_month, work_year);
2472                if (work_day > len) work_day = len;
2473             }
2474          }
2475          else {
2476             prev_num = work_year + adjust;
2477             sprintf(buf, "%d", prev_num);
2478          }
2479          break;
2480 
2481          /* prefix flags [o0+-] : set flags for next pass */
2482 
2483       case 'o':   /* %o : ordinal suffix (NEW) */
2484          ordinal = TRUE;
2485          break;
2486 
2487       case '0':   /* %0 : add leading zeroes (NEW) */
2488          print_lz = TRUE;
2489          break;
2490 
2491       case '+':   /* %+ : increment next value (NEW) */
2492       case '-':   /* %- : decrement next value (NEW) */
2493          adjust = c == '-' ? -1 : 1;
2494          if (isdigit((int)*p)) {   /* get the number */
2495             num_present = TRUE;
2496             while (isdigit((int)*p)) {
2497                num_value = num_value * 10 + (*p++ - '0');
2498             }
2499          }
2500          break;
2501 
2502       case '\0':   /* accidental end-of-string */
2503       case ' ':
2504          return p - 1;
2505 
2506       default:   /* other - just copy it to output */
2507          sprintf(buf, "%c", c);
2508          break;
2509       };
2510 
2511    } while (strchr(prefixes, c) != NULL);
2512 
2513    /* append ordinal suffix if requested */
2514    if (ordinal && prev_num >= 0) strcat(buf, ordinal_suffix(prev_num));
2515 
2516    return p;
2517 }
2518 
2519 /* ---------------------------------------------------------------------------
2520 
2521    print_word
2522 
2523    Notes:
2524 
2525       This routine prints a single word, representing punctuation and
2526       non-ASCII characters as octal literals and expanding format specifiers.
2527 
2528       It returns a pointer to the character following the word (NULL if no
2529       word follows).
2530 
2531 */
print_word(char * p)2532 char *print_word (char *p)
2533 {
2534    char c, buf[STRSIZ];
2535    int first = TRUE;   /* flag to avoid printing null strings */
2536 
2537    if (*p == '\0' || *(p += strspn(p, WHITESPACE)) == '\0') return NULL;
2538 
2539    while ((c = *p) && !isspace(c & CHAR_MSK)) {
2540       if (c == '%' && p[1] != '\0') {
2541          p = expand_fmt(buf, p + 1);
2542          if (*buf && first) {
2543             printf("(");
2544             first = FALSE;
2545          }
2546          PUTSTR(isalnum, buf, stdout);
2547       }
2548       else {
2549          if (first) printf("(");
2550          first = FALSE;
2551          PUTCHAR(isalnum, c, stdout);
2552          p++;
2553       }
2554    }
2555 
2556    if (!first) printf(")");
2557 
2558    return p;
2559 }
2560 
2561 /* ---------------------------------------------------------------------------
2562 
2563    print_db_word
2564 
2565    Notes:
2566 
2567       This routine is the debug version of 'print_word()'.
2568 
2569       It omits parentheses, does not convert punctuation to escape sequences,
2570       and writes results to stderr (not stdout).
2571 
2572 */
print_db_word(char * p)2573 char *print_db_word (char *p)
2574 {
2575    char c, buf[STRSIZ];
2576 
2577    if (*p == '\0' || *(p += strspn(p, WHITESPACE)) == '\0') return NULL;
2578 
2579    while ((c = *p) && !isspace(c & CHAR_MSK)) {
2580       if (c == '%' && p[1] != '\0') {
2581          p = expand_fmt(buf, p + 1);
2582          PUTSTR(isprint, buf, stderr);
2583       }
2584       else {
2585          PUTCHAR(isprint, c, stderr);
2586          p++;
2587       }
2588    }
2589 
2590    return p;
2591 }
2592 
2593 /* ---------------------------------------------------------------------------
2594 
2595    print_pstext
2596 
2597    Notes:
2598 
2599       This routine prints tokens in the input text (assumed separated by
2600       single blank) in PostScript format and as debugging information if
2601       requested.
2602 
2603 */
print_pstext(char * p)2604 void print_pstext (char *p)
2605 {
2606    char *s = p;   /* save for possible second pass */
2607 
2608    while ((p = print_word(p)) != NULL) printf(" ");
2609 
2610    /* repeat to generate debugging info if requested */
2611    if (debug_text) {
2612       while ((s = print_db_word(s)) != NULL) fprintf(stderr, " ");
2613    }
2614 
2615    return;
2616 }
2617 
2618 /* ---------------------------------------------------------------------------
2619 
2620    print_text
2621 
2622    Notes:
2623 
2624       This routine prints text as supplied.  It expands the format specifiers
2625       but does not tokenize into words or translate punctuation to escape
2626       sequences.
2627 
2628 */
print_text(char * p)2629 void print_text (char *p)
2630 {
2631    char c, buf[STRSIZ];
2632 
2633    while ((c = *p) != '\0') {
2634       if (c == '%' && p[1] != '\0') {
2635          p = expand_fmt(buf, p + 1);
2636          /* AH: non-ascii for -c mode should pass clean! */
2637          PUTSTR_CLEAN(isprint, buf, stdout);
2638       }
2639       else {
2640          PUTCHAR_CLEAN(isprint, c, stdout);
2641          p++;
2642       }
2643    }
2644 
2645    return;
2646 }
2647 
2648 
2649 /* ---------------------------------------------------------------------------
2650 
2651    def_footstring
2652 
2653    Notes:
2654 
2655       This routine prints the definition for a foot string, again converting
2656       all characters other than letters, digits, or space to octal escape and
2657       expanding format specifiers.
2658 
2659       The first parameter is the definition.
2660 
2661       The second parameter is the name of the string.
2662 
2663 */
def_footstring(char * p,char * str)2664 void def_footstring (char *p, char *str)
2665 {
2666    char c, buf[STRSIZ];
2667 
2668    this_day = 1;   /* set default day in foot string */
2669    RESET_DATE();   /* reset working date */
2670 
2671    printf("/%s (", str);
2672    while ((c = *p) != '\0') {
2673       if (c == '%' && p[1] != '\0') {
2674          p = expand_fmt(buf, p + 1);
2675          PUTSTR(isalnum, buf, stdout);
2676       }
2677       else {
2678          PUTCHAR(isalnum, c, stdout);
2679          p++;
2680       }
2681    }
2682    printf(") def\n");
2683 
2684    return;
2685 }
2686 
2687 
2688 /*
2689  * Routines to extract and print data
2690  */
2691 
2692 
2693 /* ---------------------------------------------------------------------------
2694 
2695    find_daytext
2696 
2697    Notes:
2698 
2699       This routine finds and prints the day (including notes) or holiday text
2700       for the specified month/year.
2701 
2702       The third parameter acts as a flag to indicate if the holiday text
2703       should be printed.
2704 
2705 */
find_daytext(int month,int year,int is_holiday)2706 void find_daytext (int month, int year, int is_holiday)
2707 {
2708    register int day;
2709    year_info *py;
2710    month_info *pm;
2711    register day_info *pd;
2712    int first;
2713    char *fcn = is_holiday ? "holidaytext" : "daytext";
2714    char hol = is_holiday ? '*' : ' ';
2715 
2716    /* if no text for this year and month, return */
2717 
2718    if ((py = find_year(year, FALSE)) == NULL || (pm = py->month[month-1]) == NULL) {
2719       return;
2720    }
2721 
2722    /* walk array of day text pointers and linked lists of text */
2723 
2724    for (day = 1; day <= LAST_NOTE_DAY; day++) {
2725       for (pd = pm->day[day-1], first = TRUE;
2726            pd;
2727            pd = pd->next) {
2728          if (pd->is_holiday != is_holiday) continue;
2729          /* Skip lines specifying an EPS image... */
2730          if (ci_strncmp(pd->text, "image:", 6) == 0) continue;
2731          if (first) {
2732             printf("%d [ \n", day >= FIRST_NOTE_DAY ?
2733                    note_box(month, day, year) : day);
2734          }
2735          else {
2736             printf("\n");
2737             print_word(LINE_SEP);   /* separate lines */
2738             printf("\n");
2739          }
2740          this_day = day >= FIRST_NOTE_DAY ? 1 : day;
2741          RESET_DATE();   /* reset working date */
2742          if (debug_text) {
2743             if (day < FIRST_NOTE_DAY) {
2744                fprintf(stderr, "%02d/%02d/%d%c ", month, day, year, hol);
2745             }
2746             else {
2747                fprintf(stderr, "%02d[%02d]%d  ", month, day - FIRST_NOTE_DAY + 1, year);
2748             }
2749          }
2750          print_pstext(pd->text);
2751          if (debug_text) fprintf(stderr, "\n");
2752          first = FALSE;
2753       }
2754       if (! first) {   /* wrap up call (if one made) */
2755          printf("\n] %s\n", day >= FIRST_NOTE_DAY ? "notetext" : fcn);
2756       }
2757    }
2758 
2759    return;
2760 }
2761 
2762 /* ---------------------------------------------------------------------------
2763 
2764    find_images
2765 
2766    Notes:
2767 
2768       This routine finds and prints the EPS images for the specified
2769       month/year.
2770 
2771 */
find_images(int month,int year)2772 void find_images (int month, int year)
2773 {
2774    register int day;
2775    year_info *py;
2776    month_info *pm;
2777    register day_info *pd;
2778 
2779    /* if no text for this year and month, return */
2780 
2781    if ((py = find_year(year, FALSE)) == NULL || (pm = py->month[month-1]) == NULL) {
2782       return;
2783    }
2784 
2785    /* walk array of day text pointers and linked lists of text */
2786 
2787    for (day = 1; day <= LAST_NOTE_DAY; day++) {
2788       for (pd = pm->day[day-1]; pd; pd = pd->next) {
2789 
2790          /* Determine if this is a 'daytext' or an 'image'... */
2791          if (ci_strncmp(pd->text, "image:", 6) == 0) {
2792 
2793             char temp_str[300];
2794             char *p;
2795 
2796             printf("%d [()] \n", day);
2797 
2798             strncpy(temp_str, pd->text, sizeof temp_str);
2799             p = strchr(pd->text, ' ');  /* Isolate the EPS image filename */
2800             strncpy(temp_str, p+1, sizeof temp_str);  /* Copy the remaining parameters */
2801             *p = '\0';
2802             printf("(%s) %s ", pd->text+6, temp_str);
2803 
2804             this_day = 1;
2805             RESET_DATE();   /* reset working date */
2806 
2807             printf("epsimage\n");
2808          }
2809       }
2810    }
2811 
2812    return;
2813 }
2814 
2815 /* ---------------------------------------------------------------------------
2816 
2817    print_colors
2818 
2819    Notes:
2820 
2821       This routine prints an array specifying the color of each date in the
2822       current month (formerly calculated on the fly in drawnums{} in
2823       pcalinit.ps).
2824 
2825 */
print_colors(int month,int year)2826 void print_colors (int month, int year)
2827 {
2828    register int day;
2829    year_info *py;
2830    month_info *pm;
2831    unsigned long holidays;
2832    int j, len;
2833    short color[32];
2834 
2835    len = LENGTH_OF(month, year);
2836 
2837    /* determine each date's color from its day of the week */
2838    for (day = 1, j = FIRST_OF(month, year);
2839         day <= len;
2840         day++, j = (j + 1) % 7) {
2841       color[day] = day_color[j];
2842    }
2843 
2844    pm = (py = find_year(year, FALSE)) ? py->month[month-1] : NULL;
2845 
2846    /* override weekday color for holidays */
2847    for (holidays = pm ? pm->holidays : 0, day = 1;
2848         holidays;
2849         holidays >>= 1, day++) {
2850       if (holidays & 01) color[day] = holiday_color;
2851    }
2852 
2853    printf("/date_color [ -1");   /* dummy value for element 0 */
2854 
2855    for (day = 1; day <= len; day++) {
2856       printf("%s %d", day % 10 == 1 ? " " : "", color[day]);
2857    }
2858    printf(" ] def\n");
2859 
2860    return;
2861 }
2862 
2863 /* ---------------------------------------------------------------------------
2864 
2865    find_noteboxes
2866 
2867    Notes:
2868 
2869       This routine finds and prints the note box numbers for the specified
2870       month/year.
2871 
2872 */
find_noteboxes(int month,int year)2873 void find_noteboxes (int month, int year)
2874 {
2875    register int day;
2876    year_info *py;
2877    month_info *pm;
2878 
2879    /* if no text for this year and month, print empty list and return */
2880 
2881    if ((py = find_year(year, FALSE)) == NULL ||
2882        (pm = py->month[month-1]) == NULL) {
2883       printf("/noteboxes [ ] def\n");
2884       return;
2885    }
2886 
2887    printf("/noteboxes [");   /* start definition of list */
2888 
2889    /* walk array of note text pointers, converting days to box numbers */
2890 
2891    for (day = FIRST_NOTE_DAY; day <= LAST_NOTE_DAY; day++) {
2892       if (pm->day[day-1]) {
2893          printf(" %d", note_box(month, day, year));
2894       }
2895    }
2896 
2897    printf(" ] def\n");
2898 
2899    return;
2900 }
2901 
2902 /* ---------------------------------------------------------------------------
2903 
2904    print_dates
2905 
2906    Notes:
2907 
2908       This routine prints the dates (in "calendar" format) for the specified
2909       month and year.
2910 
2911 */
print_dates(int month,int year)2912 void print_dates (int month, int year)
2913 {
2914    register int day;
2915    year_info *py;
2916    month_info *pm;
2917    register day_info *pd;
2918    unsigned long holidays;
2919    int has_holiday_text;
2920    static int save_year = 0;
2921 
2922    /* if no text for this year and month, return */
2923 
2924    if ((py = find_year(year, FALSE)) == NULL || (pm = py->month[month-1]) == NULL) {
2925       return;
2926    }
2927 
2928    /* print the year if it has changed */
2929 
2930    if (year != save_year) printf("%s %d\n", kw_year, save_year = year);
2931 
2932    /* walk array of day text pointers and linked lists of text */
2933 
2934    for (holidays = pm->holidays, day = 1;
2935         day < FIRST_NOTE_DAY;
2936         holidays >>= 1L, day++) {
2937       has_holiday_text = FALSE;
2938       for (pd = pm->day[day-1]; pd; pd = pd->next) {
2939          /* Bypass entries that specify an EPS image... */
2940          if (ci_strncmp(pd->text, "image:", 6) == 0) continue;
2941          if (date_style == USA_DATES) printf("%02d/%02d", month, day);
2942          else printf("%02d/%02d", day, month);
2943 #ifdef KEEP_ASTERISKS
2944          printf(pd->is_holiday ? "*\t" : "\t");
2945 #else
2946          printf("\t");
2947 #endif
2948          this_day = day;
2949          RESET_DATE();   /* reset working date */
2950          print_text(pd->text);
2951          printf("\n");
2952          has_holiday_text |= pd->is_holiday;
2953       }
2954 #ifdef KEEP_ASTERISKS
2955       /* was date flagged as holiday w/o associated text? */
2956       if ((holidays & 01) && !has_holiday_text) {
2957          if (date_style == USA_DATES) printf("%02d/%02d*\n", month, day);
2958          else printf("%02d/%02d*\n", day, month);
2959       }
2960 #endif
2961    }
2962 
2963    return;
2964 }
2965 
2966 /* ---------------------------------------------------------------------------
2967 
2968    print_moon_info
2969 
2970    Notes:
2971 
2972       This routine prints the information necessary to draw moons.
2973 
2974       If printing moons on all days, print the phase for each day.  If
2975       printing only quarter moons, tweak the phase to an exact quarter (so the
2976       icon is printed correctly) and generate a list of the quarter-moon
2977       dates.
2978 
2979 */
print_moon_info(int month,int year)2980 void print_moon_info (int month, int year)
2981 {
2982    int n, ndays, day, quarter;
2983    unsigned long qdays;
2984    double phase;
2985    static char *q[4] = {"NM", "1Q", "FM", "3Q"};
2986 
2987    if (draw_moons == NO_MOONS) return;
2988 
2989    /* print the phase of the moon for each day of the month */
2990 
2991    printf("/moon_phases [\t\t%% from algorithm ");
2992    if (atof(time_zone) != 0.0) printf(" (UTC offset = %s)", time_zone);
2993    printf("\n\t");
2994 
2995    for (n = 0, qdays = 0L, day = 1, ndays = LENGTH_OF(month, year);
2996         day <= ndays;
2997         day++) {
2998 
2999       phase = find_phase(month, day, year, &quarter);
3000 
3001       if (DEBUG(DEBUG_MOON)) {
3002          fprintf(stderr, "Moon phase on %04d-%02d-%02d: %.5f %s\n", year, month, day, phase,
3003                  quarter != MOON_OTHER ? q[quarter] : "");
3004       }
3005 
3006       /* adjust phase to exact quarter if printing only quarters */
3007       if (draw_moons == SOME_MOONS && quarter != MOON_OTHER) phase = 0.25 * quarter;
3008       if (draw_moons == ALL_MOONS || quarter != MOON_OTHER) {
3009          printf("%.3f%s", PRT_TWEAK(phase), ++n % 10 == 0 ? "\n\t" : " ");
3010       }
3011 
3012       if (quarter != MOON_OTHER) qdays |= 1L << (day - 1);
3013    }
3014    printf("] def\n");
3015 
3016    /* if drawing only quarter moons, print days when they occur */
3017 
3018    if (draw_moons == SOME_MOONS) {
3019       printf("/quarter_moons [ ");
3020       for (day = 1; qdays; day++, qdays >>= 1) {
3021          if (qdays & 01) printf("%d ", day);
3022       }
3023       printf("] def\n");
3024    }
3025 
3026    return;
3027 }
3028 
3029 /* ---------------------------------------------------------------------------
3030 
3031    print_julian_info
3032 
3033    Notes:
3034 
3035       This routine prints the information necessary to print Julian dates.
3036 
3037 */
print_julian_info(int month,int year)3038 void print_julian_info (int month, int year)
3039 {
3040    if (julian_dates != NO_JULIANS) {
3041       printf("/jdstart %d def\n", DAY_OF_YEAR(month, 1, year));
3042    }
3043    if (julian_dates == ALL_JULIANS) {
3044       printf("/yearlen %d def\n", YEAR_LEN(year));
3045    }
3046    return;
3047 }
3048 
3049 /* ---------------------------------------------------------------------------
3050 
3051    print_month
3052 
3053    Notes:
3054 
3055       This routine generates a calendar for the specified month/year.
3056 
3057 */
print_month(int month,int year)3058 void print_month (int month, int year)
3059 {
3060    static int nmonths = 0;
3061 #ifdef EPS_DSC
3062    static int page = 0;
3063 #endif
3064    int startbox;
3065 
3066 #ifdef EPS_DSC
3067    /* start of new physical page? */
3068    if (!do_whole_year || nmonths % 12 == 0) {
3069       page++;
3070       printf("%%%%Page: %d %d\n", page, page);
3071       printf("%%%%BeginPageSetup\n");
3072       printf("clear flush\n");
3073       printf("/PageNum { %d } def\n", page);
3074       printf("/PageState save def\n");
3075       printf("%%%%EndPageSetup\n\n");
3076    }
3077 #endif
3078 
3079    if (do_whole_year) {
3080       /* reset foot strings at start of each page */
3081       if (nmonths % 12 == 0) {
3082          def_footstring(lfoot, "Lfootstring");
3083          def_footstring(cfoot, "Cfootstring");
3084          def_footstring(rfoot, "Rfootstring");
3085          def_footstring(notes_hdr, "notesheading");
3086          printf("\n");
3087       }
3088    }
3089 
3090    /* set up year and month */
3091    printf("/year %d def\n", year);
3092    printf("/month %d def\n", month);
3093 
3094    /* move starting box to second row if conflict with small calendars */
3095    startbox = START_BOX(month, year);
3096    if (!do_whole_year &&
3097        (prev_cal_box[small_cal_pos] == startbox ||
3098         next_cal_box[small_cal_pos] == startbox)) {
3099       startbox += 7;
3100    }
3101 
3102    printf("/startbox %d def\n", startbox);
3103    printf("/ndays %d def\n", LENGTH_OF(month, year));
3104 
3105    find_noteboxes(month, year);   /* make list of note boxes */
3106    print_colors(month, year);   /* make list of date colors */
3107 
3108    /* Are we printing 12 months per page or only one? */
3109    if (do_whole_year) {
3110       printf("/posn %d def\n", nmonths % 12);   /* location on page */
3111 
3112       print_julian_info(month, year);   /* Julian date info */
3113       print_moon_info(month, year);   /* moon info */
3114 
3115       printf("printmonth\n\n");
3116    }
3117    else {
3118       /* reset foot strings each month (may change) */
3119       def_footstring(lfoot, "Lfootstring");
3120       def_footstring(cfoot, "Cfootstring");
3121       def_footstring(rfoot, "Rfootstring");
3122       def_footstring(notes_hdr, "notesheading");
3123 
3124       /* generate information necessary for small calendars */
3125 
3126       if (small_cal_pos != SC_NONE) {
3127          int m, y;
3128 
3129          printf("/p_year %d def\n", y = PREV_YEAR(month, year));
3130          printf("/p_month %d def\n", m = PREV_MONTH(month, year));
3131          printf("/p_startbox %d def\n", START_BOX(m, y));
3132          printf("/p_ndays %d def\n", LENGTH_OF(m, y));
3133 
3134          printf("/n_year %d def\n", y = NEXT_YEAR(month, year));
3135          printf("/n_month %d def\n", m = NEXT_MONTH(month, year));
3136          printf("/n_startbox %d def\n", START_BOX(m, y));
3137          printf("/n_ndays %d def\n", LENGTH_OF(m, y));
3138       }
3139 
3140       print_julian_info(month, year);   /* Julian date info */
3141       print_moon_info(month, year);   /* moon info */
3142 
3143       printf("printmonth\n");
3144       find_daytext(month, year, TRUE);   /* holiday text */
3145       find_daytext(month, year, FALSE);   /* day and note text */
3146       find_images(month, year);   /* EPS images */
3147    }
3148 
3149    /* end of physical page? */
3150    if (!do_whole_year || ++nmonths % 12 == 0) {
3151 #ifdef EPS_DSC
3152       printf("%%%%PageTrailer\n");
3153       printf("showpage\n");
3154       printf("clear flush\n");
3155       printf("PageState restore\n");
3156 #else
3157       printf("showpage\n");
3158 #endif
3159    }
3160 }
3161 
3162 /* ---------------------------------------------------------------------------
3163 
3164    print_html
3165 
3166    Notes:
3167 
3168       This routine prints a character string in HTML format, converting all
3169       non-printing characters to "&#NNN;" notation.
3170 
3171 */
print_html(char * p)3172 void print_html (char *p)
3173 {
3174    char c, buf[STRSIZ];
3175 
3176    while ((c = *p) != '\0') {
3177       if (c == '%' && p[1] != '\0') {
3178          p = expand_fmt(buf, p + 1);
3179          PUTSTR_CLEAN(isalnum, buf, stdout);
3180       }
3181       else if (c < ' ' || c > '\176') printf("&#%03d;", *p++ & CHAR_MSK);
3182       else printf("%c", *p++);
3183    }
3184    return;
3185 }
3186