ftp.nice.ch/pub/next/tools/postscript/psCalendar.N.bs.tar.gz#/psCalendar/SourceCode/psCalendar.c

This is psCalendar.c in view mode; [Download] [Up]

/*
 * psCalendar.c - generate PostScript file to print calendar for any month/year
 *
 * The original PostScript code to generate the calendars was written by
 * Patrick Wood (Copywrite (c) 1987 by Patrick Wood of Pipeline Associates,
 * Inc.), and authorized for modification and redistribution.  The calendar
 * file inclusion code was originally written in "bs(1)" by Bill Vogel of
 * AT&T.  Patrick's original PostScript was modified and enhanced several
 * times by others whose names have regrettably been lost.  This C version
 * was originally created by Ken Keirnan of Pacific Bell; additional
 * enhancements by Joseph P. Larson, Ed Hand, Andrew W. Rogers and Michael
 * Friendly.
 */

#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <string.h>

#define PRT	(void)printf
#define FPR	(void)fprintf

#define HOLIDAY	(1 << 6)	/* bit set to flag day as holiday */

char *words[100];	/* maximum number of words on a line */
char lbuf[512];		/* maximum line size */

char *months[] = {	/* used to match alpha months */
	"jan", "feb", "mar", "apr", "may", "jun",
	"jul", "aug", "sep", "oct", "nov", "dec",
	(char *)0,
};

/*
 * the PostScript routines for psCalendar.c
 */

/* modified by MLF to put out proper PostScript header declarations */

char *header_0[] = {
  "%!PS-Adobe-3.0",
  "%%Pages: (atend)",
  "%%Creator: psCalendar",
  "%%EndComments",
  (char *)0
  };

/* modified by AWR to skip printing days of week on small calendars */

char *header_1[] = {
  "/month_names [ (January) (February) (March) (April) (May) (June) (July)",
  "\t\t(August) (September) (October) (November) (December) ] def",
  "/prtnum { 3 string cvs show} def",
  "/drawgrid {\t\t% draw calendar boxes",
  "\tdayfont findfont 10 scalefont setfont",
  "\t0 1 6 {",
  "\t\t/i exch def",
  "\t\tsubmonth 0 eq {",
  "\t\t\ti 100 mul 40 moveto",
  "\t\t\t[ (Sunday) (Monday) (Tuesday) (Wednesday) (Thursday) (Friday) (Saturday) ] i get",
  "\t\t\t100 center",
  "\t\t} if",
  "\t\ti 100 mul 35 moveto",
  "\t\t1.0 setlinewidth",
  "\t\t0 1 5 {",
  "\t\t\tgsave",
  "\t\t\t100 0 rlineto ",
  "\t\t\t0 -80 rlineto",
  "\t\t\t-100 0 rlineto",
  "\t\t\tclosepath stroke",
  "\t\t\tgrestore",
  "\t\t\t0 -80 rmoveto",
  "\t\t pop } for",
  "\t} for",
  "} def",
  "/drawnums {\t\t% place day numbers on calendar",
  "\tdayfont findfont 30 scalefont setfont",
  "\t/start startday def",
  "\t/days ndays def",
  "\t/n 0 def",
  "\tstart 100 mul 5 add 10 rmoveto",
  "\t1 1 days {",
  "\t\t/day exch def",
  "\t\tgsave",
  "\t\tsubmonth 0 eq {",
  (char *)0
  };

/* modified by AWR: choice of black or gray Saturdays now command-line option */

char *header_2[] = {			/* set Saturdays to gray */
  "\t\t\tday start add 7 mod 0 eq {",
  "\t\t\t\t.8 setgray",
  "\t\t\t} if",
  (char *)0
  };

/* modified by AWR: calculate leap years correctly, print holidays in gray */

char *header_3[] = {
  "\t\t\tday start add 7 mod 1 eq {",
  "\t\t\t\t.8 setgray",
  "\t\t\t} if",
  "\t\t\tday holidays n get eq {",
  "\t\t\t\t.8 setgray",
  "\t\t\t\t/n n 1 add def",
  "\t\t\t} if",
  "\t\t} if",
  "\t\tday prtnum",
  "\t\tgrestore",
  "\t\tday start add 7 mod 0 eq",
  "\t\t{",
  "\t\t\tcurrentpoint exch pop 80 sub 5 exch moveto",
  "\t\t}",
  "\t\t{",
  "\t\t\t100 0 rmoveto",
  "\t\t} ifelse",
  "\t} for",
  "} def",
  "/drawfill {\t\t% place fill squares on calendar",
  "\t/start startday def",
  "\t/days ndays def",
  "\t0 35 rmoveto",
  "\t1.0 setlinewidth",
  "\t0 1 start 1 sub {",
  "\t\tgsave",
  "\t\t.9 setgray",
  "\t\t100 0 rlineto ",
  "\t\t0 -80 rlineto",
  "\t\t-100 0 rlineto",
  "\t\tclosepath fill",
  "\t\tgrestore",
  "\t\t100 0 rmoveto",
  "\tpop } for",
  "\tsubmonth 1 eq",
  "\t{",
  "\t\t/lastday 42 def",
  "\t\t600 -365 moveto",
  "\t}",
  "\t{",
  "\t\t/lastday 40 def",
  "\t\t400 -365 moveto",
  "\t} ifelse",
  "\tlastday -1 ndays start 1 add add",
  "\t{",
  "\t\t/day exch def",
  "\t\tgsave",
  "\t\t.9 setgray",
  "\t\t100 0 rlineto ",
  "\t\t0 -80 rlineto",
  "\t\t-100 0 rlineto",
  "\t\tclosepath fill",
  "\t\tgrestore",
  "\t\tday 7 mod 1 eq",
  "\t\t{",
  "\t\t\t600 -365 80 add moveto",
  "\t\t}",
  "\t\t{",
  "\t\t\t-100 0 rmoveto",
  "\t\t} ifelse",
  "\t} for",
  "} def",
  "/isleap {\t\t% is this a leap year?",
  "\tyear 4 mod 0 eq\t\t% multiple of 4",
  "\tyear 100 mod 0 ne \t% not century",
  "\tyear 400 mod 0 eq or and\t% or divisible by 400",
  "} def",
  "/days_month [ 31 28 31 30 31 30 31 31 30 31 30 31 ] def",
  "/ndays {\t\t% number of days in this month",
  "\tdays_month month 1 sub get",
  "\tmonth 2 eq\t% Feb",
  "\tisleap and",
  "\t{",
  "\t\t1 add",
  "\t} if",
  "} def",
  "/startday {\t\t% starting day-of-week for this month",
  "\t/off year 2000 sub def\t% offset from start of epoch",
  "\toff",
  "\toff 4 idiv add\t\t% number of leap years",
  "\toff 100 idiv sub\t% number of centuries",
  "\toff 400 idiv add\t% number of years divisible by 400",
  "\t6 add 7 mod 7 add \t% offset from Jan 1 2000",
  "\t/off exch def",
  "\t1 1 month 1 sub {",
  "\t\t/idx exch def",
  "\t\tdays_month idx 1 sub get",
  "\t\tidx 2 eq",
  "\t\tisleap and",
  "\t\t{",
  "\t\t\t1 add",
  "\t\t} if",
  "\t\t/off exch off add def",
  "\t} for",
  "\toff 7 mod\t\t% 0--Sunday, 1--monday, etc.",
  "} def",
  "/center {\t\t% center string in given width",
  "\t/width exch def",
  "\t/str exch def width str ",
  "\tstringwidth pop sub 2 div 0 rmoveto str show",
  "} def",
  "/calendar",
  "{",
  "\ttitlefont findfont 48 scalefont setfont",
  "\t0 60 moveto",
  "\t/month_name month_names month 1 sub get def",
  "\tmonth_name show",
  "\t/yearstring year 10 string cvs def",
  "\t700 yearstring stringwidth pop sub 60 moveto",
  "\tyearstring show",
  "\t0 0 moveto",
  "\tdrawnums",
  "\t0 0 moveto",
  "\tdrawfill",
  "\t0 0 moveto",
  "\tdrawgrid",
  "} def",
  "/daytext {",
  "\t/Helvetica-Narrow findfont 6 scalefont setfont",
  "\t/mytext\texch def /myday exch def",
  "\tstartday myday 1 sub add dup 7 mod 100 mul 5 add % gives column",
  "\texch 7 idiv -80 mul % gives row",
  "\tdup /ypos exch def moveto",
  "\t/LM currentpoint pop def /RM LM 95 add def",
  "        mytext { dup (.p) eq { crlf pop} {prstr ( ) show} ifelse } forall",
  "} def",
  "/crlf {",
  "    ypos 8 sub /ypos exch def LM ypos moveto",
  "} def",
  "/prstr {",
  "    dup stringwidth pop currentpoint pop",
  "    add RM gt {crlf} if show",
  "} def",
  "/printmonth {",
   (char *)0,
 };
 
 char *header_4[] = {
  "\t/submonth 0 def",
  "\tcalendar",
  "\tmonth 1 sub 0 eq",
  "\t{",
  "\t\t/lmonth 12 def",
  "\t\t/lyear year 1 sub def",
  "\t}",
  "\t{",
  "\t\t/lmonth month 1 sub def",
  "\t\t/lyear year def",
  "\t} ifelse",
  "\tmonth 1 add 13 eq",
  "\t{",
  "\t\t/nmonth 1 def",
  "\t\t/nyear year 1 add def",
  "\t} ",
  "\t{",
  "\t\t/nmonth month 1 add def",
  "\t\t/nyear year def",
  "\t} ifelse",
  "\t/savemonth month def",
  "\t/saveyear year def",
  "\t/submonth 1 def",
  "\t/year lyear def",
  "\t/month lmonth def",
  "\tgsave",
  "\t500 -365 translate",
  "\tgsave",
  "\t.138 .138 scale",
  "\t10 -120 translate",
  "\tcalendar",
  "\tgrestore",
  "\t/submonth 1 def",
  "\t/year nyear def",
  "\t/month nmonth def",
  "\t100 0 translate",
  "\tgsave",
  "\t.138 .138 scale",
  "\t10 -120 translate",
  "\tcalendar",
  "\tgrestore",
  "\t/month savemonth def",
  "\t/year saveyear def",
  "\t/submonth 0 def",
  "\tgrestore",
  "} def",
  (char *)0,
};

FILE *cfp = NULL;
int year;
int cyear;

void exit();
char *getenv();

main(argc, argv)
int argc;
char **argv;
{
	extern char *optarg;
	extern int optind;
	register struct tm *lt;
	register char **ap;
	register char *cp;
	char *cfile = NULL;
	char cbuf[80];
	long t, time();
	int errflg = 0;
	int nocal = 0;
	int sat = 0;
	char *titlefont = "Times-Bold";
 	char *dayfont = "Times-Bold";
 	int rotate = 90;
 	int month = 0;
	int m;
	char doyear = 0;

#define DOHEADER(phdr) for(ap = phdr; *ap; ap++) PRT("%s\n", *ap);

	while ((m = getopt(argc, argv, "d:ef:m:rst:y:")) != EOF)

		switch (m) {

		case 'd':		/* select font for day names/numbers */
			dayfont = optarg;
			break;

		case 'e':		/* print empty calendar */
			nocal++;
			cfile = NULL;
			break;

		case 'f':		/* use alternate calendar file */
			cfile = optarg;
			nocal = 0;
			break;

		case 'm':		/* select month */
			month = atoi(optarg);
			if (!month) doyear = 1;
			break;

		case 'r':		/* generate portrait calendar */
			rotate = 0;
			break;

		case 's':		/* print Saturdays in black */
			sat++;
			break;

		case 't':		/* select font for month/year */
			titlefont = optarg;
			break;

		case 'y':		/* select year */
			year = atoi(optarg);
			if (year && year < 1900) year = year % 100 + 1900;
			break;

		case '?':
			errflg = 1;
			break;
		}

	if (errflg) {
		FPR(stderr,
			"Usage: psCalendar [ -r ] [ -s ] [ -e | -f <cal> ] [ -m month|0] [ -y <year> ]\n");
		FPR(stderr, 
			"\t\t[ -t <title font> ] [ -d <day font> ]\n");
		exit(1);
	}
	t = time((long *)0);
	lt = localtime(&t);

	if (!month && !doyear)
		month = lt->tm_mon + 1;
	if (!year)
		year = lt->tm_year + 1900;

	/*
	 * In case we don't encounter any year data in the
	 * calendar file, assume the current year.
	 */
	cyear = year;

	/*
	 * Open a supplied calendar file (if any)
	 */
	if (cfile != NULL) {
		if ((cfp = fopen(cfile, "r")) == NULL) {
			FPR(stderr, "psCalendar: can't open file: %s\n", cfile);
			exit(1);
		}
	}
	/*
	 * Else see if a calendar file exists in the home directory
	 */
	else if (nocal == 0 && (cp = getenv("HOME")) != NULL) {
		(void)strcpy(cbuf, cp);
		(void)strcat(cbuf, "/calendar");
		cfp = fopen(cbuf, "r");
	}

	/*
	 * Write out PostScript prolog
	 */
 	DOHEADER(header_0);
 	PRT("/titlefont /%s def\n/dayfont /%s def\n", titlefont, dayfont);

	DOHEADER(header_1);
	if (sat == 0)
		DOHEADER(header_2);
	DOHEADER(header_3);

 	PRT("\t%d rotate\n", rotate);
 	if (rotate)
 		PRT("\t50 -120 translate\n");
 	else
 		PRT("\t0.75 0.75 scale\n\t50 460 translate\n");

	DOHEADER(header_4);

	if (month) {
		PRT("%%%%Page: 1\n");
		pmonth(month);
		PRT("%%%%Trailer:\n");
		PRT("%%%%Pages: 1\n");
		}
	else {
		for (month = 1; month <= 12; month++) {
			PRT("%%%%Page: %d\n", month);
			pmonth(month);
			}
		PRT("%%%%Trailer:\n");
		PRT("%%%%Pages: 12\n");
		}
	
	exit(0);
}

/*
 * Browse through the calendar file looking for day info in current month
 */
find_daytext(m)
int m;
{
	register char **s;
	register int oldday = -1;
	register int day;
	int reset;

	for (reset = 1; (day = getday(m, reset)) != 0; reset = 0)
		if (*words) {
			day &= ~HOLIDAY;
			if (day != oldday) {
				if (oldday == -1)
					PRT("%d [ \n", day);
				else
					PRT("] daytext\n%d [ \n", day);
				oldday = day;
			} else
				PRT("(.p)\n");
			for (s = words; *s; s++)
				PRT("(%s)\n", *s);
		}

	if (oldday != -1)		/* terminate call to daytext */
		PRT("] daytext\n");
}


/*
 * Browse through the calendar file looking for holidays in current month
 */
find_holidays(m)
int m;
{
	register int oldday = -1;
	register int day;
	int reset;

	PRT("/holidays [");	/* start definition of list */

	for (reset = 1; (day = getday(m, reset)) != 0; reset = 0)
		if (day & HOLIDAY) {
			day &= ~HOLIDAY;
			if (day != oldday) {
				PRT(" %d", day);
				oldday = day;
			}
		}

	PRT(" 99 ] def\n");	/* terminate with dummy entry */
}


/*
 * pmonth - do calendar for month "m"
 */
pmonth(m)
int m;
{

	PRT("/year %d def\n", year);	/* set up year and month */
	PRT("/month %d def\n", m);
	find_holidays(m);		/* first pass - make list of holidays */
	PRT("printmonth\n");
	find_daytext(m);		/* second pass - add text to boxes */
	PRT("showpage\n");
}


/*
 * getday - find next day entry for desired month in the calendar file
 */
int getday(m, reset)
register int m;
int reset;
{
	static eof = 0;
	register char *cp;
	register c;

	if (cfp == NULL)	/* whoops, no calendar file */
		return(0);

	if (reset) {		/* new month, rewind */
		rewind(cfp);
		eof = 0;
	}
	if (eof)
		return(0);
nextline:
	cp = lbuf;
	do {
		while ((c = getc(cfp)) != '\n' && c != EOF) {
			/* ignore leading white space */
			if (cp == lbuf && (c == ' ' || c == '\t'))
				continue;
			*cp++ = c;
		}
		if (c == EOF) {
			eof = 1;
			return(0);
		}
	} while (cp == lbuf);	/* ignore empty lines */
	*cp = 0;

	/* examine the line, see if its one we want */
	if ((c = parse(m)) == 0)
		goto nextline;

	return(c);
}

/*
 * parse - check calendar entry for desired month, break line into fields
 */
parse(m)
register m;
{
	register char *cp;
	register i;
	int is_holiday = 0;		/* '*' after date flags it as holiday */
	int valid = 1;

	cp = strtok(lbuf, " \t");	/* get first field */

	while (*cp) {
		if (isupper(*cp))
			*cp = tolower(*cp);
		cp++;
	}
	cp = lbuf;

	/*
	 * Check for "year" line
	 */
	if (strcmp(cp, "year") == 0) {
		cp = strtok((char *)0, " \t");
		if ((i = atoi(cp)) > 0) {
			if (i < 100)
				i += 1900;
			cyear = i;
		}
		return(0);
	}

	/*
	 * If field begins with alpha, try to decode month name
	 */
	if (isalpha(*cp)) {
		if (cyear != year)
			return(0);

		for (i = 0; months[i]; i++)
			if (strncmp(cp, months[i], 3) == 0) {
				if (++i != m)
					return(0);

				/* month found, get day */

				if ((cp = strtok((char *)0, " \t")) == NULL)
					return(0);
				if ((i = atoi(cp)) < 1 || i > 31)
					return(0);
				while (isdigit(*cp))	/* skip over day field */
					cp++;
				if (*cp == '*')		/* holiday? */
					is_holiday = 1;
				if (loadwords() || is_holiday)
					return(i | is_holiday * HOLIDAY);
				return(0);
			}
		return(0);
	}
	/*
	 * Not alpha month, try numeric (parse full date to see if year has changed)
	 */
	if ((i = atoi(cp)) != m)
		valid = 0;
	while (isdigit(*cp))
		cp++;
	while (*cp && !isdigit(*cp))
		cp++;

	/* now get day */

	if ((i = atoi(cp)) < 1 || i > 31)
		valid = 0;

	 /* Numeric dates may have a year */

	while (isdigit(*cp))		/* skip over day field */
		cp++;
	if (*cp == '*')			/* holiday? */
		is_holiday = 1;
	while (*cp && !isdigit(*cp))
		cp++;
	if ((m = atoi(cp)) > 0) {
		if (m < 100)
			m += 1900;
		cyear = m;
		while (isdigit(*cp))	/* skip over year field */
			cp++;
		if (*cp == '*')		/* holiday? */
			is_holiday = 1;
	}

	if (!valid || cyear != year)	/* date not applicable - return 0 */
		return(0);

	if (loadwords() || is_holiday)	/* date of some significance */
		return(i | is_holiday * HOLIDAY);
	return(0);
}


/*
 * loadwords - tokenize line buffer into word array, return word count
 */
loadwords()
{
	register char **ap = words;
	register i;

	for (i = 0; *ap = strtok((char *)0, " \t") ; ap++, i++) ;
	return(i);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.