home *** CD-ROM | disk | FTP | other *** search
- /*
- * pcal.c - generate PostScript file to print calendar for any month and year
- *
- * The original PostScript code to generate the calendars was written by
- * Patrick Wood (Copyright (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, and Andrew Rogers (who also
- * did the VMS port), and Mark Kantrowitz. Many thanks also to Joe Brownlee
- * and Eric Hammond for their suggestions.
- *
- * Revision history:
- *
- * 2.1 MK/AWR 08/27/90 support -L, -C, -R, -n options;
- * print holiday text next to date
- *
- * AWR 08/24/90 incorporate cpp-like functionality;
- * add -D and -U options; save date file
- * information in internal data structure;
- * look for PCAL_OPTS and PCAL_DIR; look
- * for ~/.calendar and ~/calendar
- *
- * 2.0 AWR 08/08/90 included revision history; replaced -r
- * flag with -l and -p; replaced -s and -S
- * flags with -b and -g; recognize flags
- * set in date file; translate ( and ) in
- * text to octal escape sequence; usage()
- * message condensed to fit 24x80 screen
- *
- * Parameters:
- *
- * pcal [opts] generate calendar for current month/year
- *
- * pcal [opts] yy generate calendar for entire year yy
- *
- * pcal [opts] mm yy generate calendar for month mm
- * (Jan = 1), year yy (19yy if yy < 100)
- *
- * pcal [opts] mm yy n as above, for n consecutive months
- *
- * Output:
- *
- * PostScript file to print calendars for all selected months.
- *
- * Options:
- *
- * -b <DAY> print specified weekday in black
- * -g <DAY> print specified weekday in gray
- * (default: print Saturdays and Sundays in gray)
- *
- * -d <FONT> specify alternate font for day names
- * (default: Times-Bold)
- *
- * -n <FONT> specify alternate font for notes in boxes
- * (default: Helvetica-Narrow)
- *
- * -t <FONT> specify alternate font for titles
- * (default: Times-Bold)
- *
- * -D <SYM> define preprocessor symbol
- * -U <SYM> un-define preprocessor symbol
- *
- * -e generate empty calendar (ignore date file)
- *
- * -f <FILE> specify alternate date file (default:
- * ~/.calendar on Un*x, SYS$LOGIN:CALENDAR.DAT
- * on VMS; if environment variable [logical
- * name on VMS] PCAL_DIR exists, looks there
- * instead)
- *
- * -o <FILE> specify alternate output file (default:
- * stdout on Un*x, CALENDAR.PS on VMS)
- *
- * -L <STRING> specify left foot string (default: "")
- * -C <STRING> specify center foot string (default: "")
- * -R <STRING> specify right foot string (default: "")
- *
- * -l generate landscape-mode calendars
- * -p generate portrait-mode calendars
- * (default: landscape-mode)
- *
- * There are many ways to specify these options in addition to using the
- * command line; this facilitates customization to the user's needs.
- *
- * If the environment variable (global symbol on VMS) PCAL_OPTS is
- * present, its value will be parsed as if it were a command line.
- * Any options specified will override the program defaults.
- *
- * All but the -e, -f, -D, and -U options may be specified in the date
- * file by the inclusion of one or more lines of the form "opt <options>".
- * Any such options override any previous values set either as program
- * defaults, via PCAL_OPTS, or in previous "opt" lines.
- *
- * Options explicitly specified on the command line in turn override all
- * of the above.
- *
- * The -b, -d, -g, -n, -o, -t, -C, -L, and -R flags may be specified at
- * any point to reset the corresponding option to its default. -D may
- * be specified without an argument in order to un-define all symbols.
- *
- * Parameters and flags may be mixed on the command line. In some cases
- * (e.g., when a parameter follows a flag without its optional argument)
- * this may lead to ambiguity; the dummy flag '-' (or '--') may be used
- * to separate them, i.e. "pcal -t - 9 90".
- *
- * Simple cpp-like functionality is provided. The date file may include
- * the following commands, which work like their cpp counterparts:
- *
- * define <sym>
- * undef <sym>
- *
- * if{n}def <sym>
- * ...
- * { else
- * ... }
- * endif
- *
- * include <file>
- *
- * Note that these do not start with '#', which is reserved as a comment
- * character.
- *
- * "define" alone deletes all the current definitions; "ifdef" alone is
- * always false; "ifndef" alone is always true. All defined symbols are
- * treated in a case-insensitive manner.
- *
- * The file name in the "include" directive may optionally be surrounded
- * by "" or <>.
- *
- */
-
-
- #include <stdio.h>
- #include <ctype.h>
- #include <time.h>
- #include <string.h>
-
- #ifdef VMS /* VMS oddities isolated here */
-
- #include <ssdef.h> /* required for trnlog() */
- #include <descrip.h>
-
- #define HOME_DIR "SYS$LOGIN"
- #define DATEFILE "calendar.dat"
- #define OUTFILE "calendar.ps"
- #define START_PATH '['
- #define END_PATH ']'
-
- #define EXIT_SUCCESS 1
- #define EXIT_FAILURE 3
-
- #else /* non-VMS - assume Un*x of some sort */
-
- #define HOME_DIR "HOME"
- #define DATEFILE ".calendar"
- #define ALT_DATEFILE "calendar" /* for backward compatibility */
- #define OUTFILE ""
- #define START_PATH '/'
- #define END_PATH '/'
-
- #define EXIT_SUCCESS 0
- #define EXIT_FAILURE 1
-
- #endif
-
- #define PCAL_OPTS "PCAL_OPTS" /* environment variables */
- #define PCAL_DIR "PCAL_DIR"
-
- #define IS_LEAP(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
- #define INIT_COLORS memcpy(color, default_color, sizeof(color))
- #define LASTCHAR(p) ((p) && *(p) ? (p) + strlen(p) - 1 : NULL)
-
- #ifdef __STDC__
- #define TOLOWER(c) tolower(c)
- #else
- #define TOLOWER(c) (isupper(c) ? tolower(c) : (c))
- #endif
-
- #define PRT (void)printf
- #define FPR (void)fprintf
-
- #define FALSE 0
- #define TRUE 1
-
- #define ALL_FLAGS "bCDdefgLlnopRtU" /* all command-line flags */
- #define DATEFILE_FLAGS "DefU" /* parsed before opening datefile */
- #define OTHER_FLAGS "bCdgLlnopRt" /* parsed inside datefile */
-
- #define DAYFONT "Times-Bold" /* default font names */
- #define TITLEFONT "Times-Bold"
- #define NOTESFONT "Helvetica-Narrow"
-
- #define LFOOT "" /* default foot strings */
- #define CFOOT ""
- #define RFOOT ""
-
- #define LANDSCAPE 90 /* degrees to rotate for landscape/portrait */
- #define PORTRAIT 0
- #define ROTATE LANDSCAPE /* default */
-
- #define BLACK 0 /* colors for dates */
- #define GRAY 1
-
- #define NO_DATEFILE 0 /* date file (if any) to use */
- #define USER_DATEFILE 1
- #define SYS_DATEFILE 2
-
- /* preprocessor token codes - must be contiguous range of integers starting
- * at 0 and ending with code for non-tokens (cf. pp_info[], pp_token())
- */
- #define PP_DEFINE 0
- #define PP_ELSE 1
- #define PP_ENDIF 2
- #define PP_IFDEF 3
- #define PP_IFNDEF 4
- #define PP_INCLUDE 5
- #define PP_UNDEF 6
- #define PP_OTHER 7 /* not pp token */
-
- #define MAX_NESTING 10 /* maximum nesting level for file inclusion */
-
- #define MAX_PP_SYMS 100 /* number of definable preprocessor symbols */
- #define PP_SYM_UNDEF -1 /* flag for undefined symbol */
-
- #define MIN_YR 1900 /* significant years (calendar limits) */
- #define MAX_YR 9999
-
- #define JAN 1 /* significant months */
- #define FEB 2
- #define DEC 12
-
- #define PARSE_OK 0 /* returns from parse(), enter_day_info() */
- #define PARSE_INVDATE 1
- #define PARSE_INVLINE 2
-
- #define STRSIZ 200 /* size of misc. strings */
-
- #define MAXARGS 3 /* numeric command-line args */
-
- #define WHITESPACE " \t" /* token delimiters in date file */
-
- /*
- * Global typedef declarations for data structure
- */
-
- typedef struct d_i {
- int is_holiday;
- char *text;
- struct d_i *next;
- } day_info;
-
- typedef struct m_i {
- unsigned long holidays;
- day_info *day[31];
- } month_info;
-
- typedef struct y_i {
- int year;
- month_info *month[12];
- struct y_i *next;
- } year_info;
-
-
- /*
- * Global variables:
- */
-
- int do_define(), do_ifdef(), do_ifndef(), do_include(), do_undef();
- char *trnlog(), *mk_path(), *mk_filespec();
-
- extern char *getenv();
-
- year_info *head = NULL; /* head of internal data structure */
- int nesting_level = 0; /* level of include file nesting */
- int init_month; /* initial month, year, number of months */
- int init_year;
- int nmonths;
- int curr_year; /* current default year for date file entries */
- char *words[100]; /* maximum number of words per date file line */
- char lbuf[512]; /* maximum date file line size */
- char *pp_sym[MAX_PP_SYMS]; /* preprocessor defined symbols */
- char progname[STRSIZ]; /* program name (for error messages) */
- char color[7]; /* colors of weekdays - cf. default_color[] */
-
- /*
- * Default values for command-line options:
- */
-
- char default_color[7] = { /* -b, -g */
- GRAY, BLACK, BLACK, BLACK, BLACK, BLACK, GRAY /* cf. COLOR_MSG */
- };
-
- int datefile_type = SYS_DATEFILE; /* -e, -f */
- char datefile[STRSIZ] = "";
- char default_dir[STRSIZ] = "";
-
- int rotate = ROTATE; /* -l, -p */
-
- char dayfont[STRSIZ] = DAYFONT; /* -d, -t, -n */
- char titlefont[STRSIZ] = TITLEFONT;
- char notesfont[STRSIZ] = NOTESFONT;
-
- char lfoot[STRSIZ] = LFOOT; /* -L, -C, -R */
- char cfoot[STRSIZ] = CFOOT;
- char rfoot[STRSIZ] = RFOOT;
-
- char outfile[STRSIZ] = OUTFILE; /* -o */
-
- /*
- * Language-dependent strings (month and day names, option file keywords,
- * preprocessor tokens):
- */
-
- static char *months[12] = {
- "January", "February", "March", "April", "May", "June",
- "July", "August", "September", "October", "November", "December"
- };
-
- static char *days[7] = {
- "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
- "Saturday"
- };
-
- /* preprocessor tokens - must be in same order as PP_XXXXX (cf. pp_token()) */
- static struct pp {
- char *token; /* name */
- int (*pfcn)(); /* dispatch routine */
- } pp_info[] = {
- { "define", do_define }, /* PP_DEFINE */
- { "else", NULL }, /* PP_ELSE */
- { "endif", NULL }, /* PP_ENDIF */
- { "ifdef", do_ifdef }, /* PP_IFDEF */
- { "ifndef", do_ifndef }, /* PP_IFNDEF */
- { "include", do_include }, /* PP_INCLUDE */
- { "undef", do_undef }, /* PP_UNDEF */
- { NULL, NULL } /* PP_OTHER */
- };
-
- #define MIN_DAY_LEN 2 /* minimum size of abbreviations */
- #define MIN_MONTH_LEN 3
- #define MIN_PPTOK_LEN 3
-
- #define ALL "all" /* command-line or date file keywords */
- #define OPT "opt"
- #define YEAR "year"
-
- #define COLOR_MSG "Sat/Sun in gray, others in black" /* cf. usage() */
-
- /*
- * PostScript boilerplate
- */
-
- #include "pcalinit.h"
-
-
- /*
- * Main program - parse and validate command-line arguments, open files,
- * generate PostScript boilerplate and code to generate calendars.
- *
- * Program structure:
- *
- * main() looks for the environment variable (global symbol on VMS) PCAL_OPTS
- * and calles get_args() to parse it. It then calls get_args() again to parse
- * the command line for the date file name, -D and -U options to be in effect
- * prior to reading the date file, and any numeric arguments (month, year,
- * number of months). It then calls read_datefile() to read and parse the
- * date file; any "opt" lines present will override the defaults for the
- * command-line flags. It then calls get_args() again to process the other
- * command-line flags, which in turn override any specified earlier.
- *
- * main() then generates the common PostScript code and then calls pmonth() to
- * print the calendars.
- *
- * read_datefile() calls getline() to read the date file, do_xxxxx() to process
- * the preprocessor tokens, and parse() to parse each date line.
- *
- * getline() reads one or more lines from the date file, stripping comments
- * (# through end-of-line) and ignoring blank lines.
- *
- * parse() parses a line from the date file and processes it. If "opt", it
- * calls loadwords() to split the line into tokens and get_args() to
- * process them. If the line contains a date, it calls enter_day_info() to
- * enter the day and related text into the data structure.
- *
- * pmonth() calls find_holidays() to generate the list of holidays to be
- * printed in gray; it then calls find_daytext() to generate the text to
- * be printed inside the calendar boxes.
- *
- */
- main(argc, argv)
- int argc;
- char **argv;
- {
- FILE *dfp = NULL; /* date file pointer */
- char *p, **ap;
- int i, month, year, ngray;
-
- #define DO_HEADER(phdr) for (ap = phdr; *ap; ap++) PRT("%s\n", *ap)
-
- INIT_COLORS; /* set up default colors */
-
- /* isolate root program name (for use in error messages) */
-
- strcpy(progname, **argv ? *argv : "pcal");
-
- if ((p = strrchr(progname, END_PATH)) != NULL)
- strcpy(progname, ++p);
- if ((p = strchr(progname, '.')) != NULL)
- *p = '\0';
-
- /*
- * Get the arguments from a) the environment variable, b) "opt" lines
- * in the date file, and c) the command line, in that order
- */
-
- /* look for environment variable for options */
-
- if ((p = getenv(PCAL_OPTS)) != NULL) {
- strcpy(lbuf, "x "); /* prepend a dummy token */
- strcat(lbuf, p);
- loadwords();
- if (! get_args(words, ALL_FLAGS, FALSE)) {
- usage();
- exit(EXIT_FAILURE);
- }
- }
-
- /* parse command-line arguments once to find name of date file, etc. */
-
- if (!get_args(argv, DATEFILE_FLAGS, TRUE)) {
- usage();
- exit(EXIT_FAILURE);
- }
-
- /* Attempt to open the date file as specified by the [-e | -f] flags */
-
- switch (datefile_type) {
- case NO_DATEFILE:
- dfp = NULL;
- break;
-
- case USER_DATEFILE:
- /* Attempt to open user-specified calendar file */
- if ((dfp = fopen(datefile, "r")) == NULL) {
- FPR(stderr, "%s: can't open file %s\n", progname,
- datefile);
- exit(EXIT_FAILURE);
- }
- mk_path(default_dir, datefile); /* extract path */
- break;
-
- case SYS_DATEFILE:
- /* Attempt to open system-specified calendar file */
- if ((p = trnlog(PCAL_DIR)) || (p = trnlog(HOME_DIR)))
- strcpy(default_dir, p);
-
- mk_filespec(datefile, default_dir, DATEFILE);
- dfp = fopen(datefile, "r"); /* no error if nonexistent */
- #ifdef ALT_DATEFILE
- if (!dfp) { /* try again with alternate file */
- mk_filespec(datefile, default_dir, ALT_DATEFILE);
- dfp = fopen(datefile, "r");
- }
- #endif
- break;
- }
-
- /* read the date file (if any) and build internal data structure */
-
- if (dfp) {
- curr_year = init_year;
- read_datefile(dfp, datefile);
- fclose(dfp);
- }
-
- /* reparse command line - flags there supersede those in date file */
-
- get_args(argv, OTHER_FLAGS, FALSE);
-
- /* done with the arguments and flags - try to open the output file */
-
- if (*outfile && freopen(outfile, "w", stdout) == (FILE *) NULL) {
- FPR(stderr, "%s: can't open file %s\n", progname, outfile);
- exit(EXIT_FAILURE);
- }
-
- /*
- * Write out PostScript prolog
- */
-
- /* font names */
-
- PRT("%%!\n");
- PRT("/titlefont /%s def\n/dayfont /%s def\n/notesfont /%s def\n", titlefont, dayfont, notesfont);
-
- /* foot strings */
-
- def_footstring(lfoot, 'L');
- def_footstring(cfoot, 'C');
- def_footstring(rfoot, 'R');
-
- /* month names */
-
- PRT("/month_names [");
- for (i = 0; i < 12; i++)
- PRT("%s(%s) ", i % 6 == 0 ? "\n\t" : "", months[i]);
- PRT("] def\n");
-
- /* day names */
-
- PRT("/day_names [");
- for (i = 0; i < 7; i++)
- PRT("%s(%s) ", i % 6 == 0 ? "\n\t" : "", days[i]);
- PRT("] def\n");
-
- /* colors (black/gray) to print weekdays and holidays */
-
- PRT("/day_gray [");
- for (ngray = i = 0; i < 7; ngray += color[i++] == GRAY)
- PRT(" %s", color[i] == GRAY ? "true" : "false");
- PRT(" ] def\n");
- PRT("/holiday_gray %s def\n", ngray <= 3 ? "true" : "false");
-
- /* PostScript boilerplate (part 1) */
-
- DO_HEADER(header_1);
-
- /* landscape or portrait mode */
-
- PRT("\t%d rotate\n", rotate);
- if (rotate == LANDSCAPE)
- PRT("\t50 -120 translate\n");
- else
- PRT("\t0.75 0.75 scale\n\t50 500 translate\n");
-
- /* PostScript boilerplate (part 2) */
-
- DO_HEADER(header_2);
-
- /*
- * Write out PostScript code to print calendars
- */
-
- month = init_month;
- year = init_year;
-
- while (nmonths--) {
- pmonth(month, year);
- if (++month > DEC) {
- month = JAN;
- year++;
- }
- }
-
- cleanup();
-
- #ifdef VMS
- FPR(stderr, "Output is in file %s\n", outfile);
- #endif
- exit(EXIT_SUCCESS);
- }
-
- /*
- * get_args - walk the argument list, parsing all arguments but processing only
- * those specified in "flags". If "do_numargs" is TRUE, processes numeric
- * arguments (month, year, number of months) as well.
- */
- int get_args(argv, flags, do_numargs)
- char **argv; /* argument list */
- char *flags; /* which flags to process */
- int do_numargs; /* process numeric arguments? */
- {
- char *p, *opt;
- int i, do_flag;
- long tmp; /* for getting current month/year */
- struct tm *p_tm;
- int badopt = FALSE; /* flag set if bad param */
- int nargs = 0; /* count of non-flag args */
- int numargs[MAXARGS]; /* non-flag (numeric) args */
-
- /* Look for the argument following flag - may be separated by spaces or
- * not (bumps argv in former case). If no non-flag argument appears, set
- * "arg" to NULL (-b, -C, -d, -g, -L, -n, -o, -R, and -t without an argument
- * reset the corresponding option to its default value).
- */
- #define GETARG(arg) arg = *(*argv + 2) ? *argv + 2 : \
- (*(argv+1) && **(argv+1) != '-' ? *++argv : NULL)
-
- /* Walk argument list, ignoring first element (program name) */
-
- while (*++argv) {
-
- /* Assume that any non-flag argument is a numeric argument */
- if (**argv != '-') {
- if (do_numargs && nargs < MAXARGS)
- numargs[nargs++] = atoi(*argv);
- continue;
- }
-
- /* Is this flag among those to be processed beyond parsing? */
-
- do_flag = strchr(flags, *(opt = *argv + 1)) != NULL;
-
- switch (*opt) {
-
- case '\0': /* take - or -- as dummy flags */
- case '-' :
- break;
-
- case 'b': /* print day in black or gray */
- case 'g':
- GETARG(p);
- if (do_flag)
- if (p)
- set_color(p, *opt == 'b' ? BLACK : GRAY);
- else
- INIT_COLORS; /* reset to defaults */
- break;
-
- case 'C': /* specify alternate center foot */
- GETARG(p);
- if (do_flag)
- strcpy(cfoot, p ? p : CFOOT);
- break;
-
- case 'd': /* specify alternate day font */
- GETARG(p);
- if (do_flag)
- strcpy(dayfont, p ? p : DAYFONT);
- break;
-
- case 'D': /* define preprocessor symbol */
- GETARG(p);
- if (do_flag)
- do_define(p);
- break;
-
- case 'e': /* generate empty calendar */
- if (do_flag) {
- datefile_type = NO_DATEFILE;
- datefile[0] = '\0';
- }
- break;
-
- case 'f': /* specify alternate date file */
- GETARG(p);
- if (p && do_flag) {
- datefile_type = USER_DATEFILE;
- strcpy(datefile, p);
- }
- break;
-
- case 'L': /* specify alternate left foot */
- GETARG(p);
- if (do_flag)
- strcpy(lfoot, p ? p : LFOOT);
- break;
-
- case 'l': /* generate landscape calendar */
- if (do_flag)
- rotate = LANDSCAPE;
- break;
-
- case 'n': /* specify alternate notes font */
- GETARG(p);
- if (do_flag)
- strcpy(notesfont, p ? p : NOTESFONT);
- break;
-
- case 'o': /* specify alternate output file */
- GETARG(p);
- if (do_flag)
- strcpy(outfile, p ? p : OUTFILE);
- break;
-
- case 'p': /* generate portrait calendar */
- if (do_flag)
- rotate = PORTRAIT;
- break;
-
- case 'R': /* specify alternate right foot */
- GETARG(p);
- if (do_flag)
- strcpy(rfoot, p ? p : RFOOT);
- break;
-
-
- case 't': /* specify alternate title font */
- GETARG(p);
- if (do_flag)
- strcpy(titlefont, p ? p : TITLEFONT);
- break;
-
- case 'U': /* undef preprocessor symbol */
- GETARG(p);
- if (do_flag)
- do_undef(p);
- break;
-
- default: /* unrecognized flag */
- FPR(stderr, "%s: illegal option -%s\n", progname, opt);
- badopt = TRUE;
- break;
- }
- }
-
- if (!do_numargs)
- return !badopt; /* return TRUE if OK, FALSE if error */
-
- /* Validate non-flag (numeric) parameters */
-
- switch (nargs) {
- case 0: /* no arguments - print current month/year */
- time(&tmp);
- p_tm = localtime(&tmp);
- init_month = p_tm->tm_mon + 1;
- init_year = p_tm->tm_year;
- nmonths = 1;
- break;
- case 1: /* one argument - print entire year */
- init_month = JAN;
- init_year = numargs[0];
- nmonths = 12;
- break;
- default: /* two or three arguments - print one or more months */
- init_month = numargs[0];
- init_year = numargs[1];
- nmonths = nargs > 2 ? numargs[2] : 1;
- break;
- }
-
- if (nmonths < 1) /* ensure at least one month */
- nmonths = 1;
-
- /* check range of month and year */
-
- if (init_month < JAN || init_month > DEC) {
- FPR(stderr, "%s: month %d not in range 1 .. 12\n", progname,
- init_month);
- badopt = TRUE;
- }
-
- if (init_year > 0 && init_year < 100) /* treat nn as 19nn */
- init_year += 1900;
-
- if (init_year < MIN_YR || init_year > MAX_YR) {
- FPR(stderr, "%s year %d not in range %d .. %d\n", progname,
- init_year, MIN_YR, MAX_YR);
- badopt = TRUE;
- }
-
- return !badopt; /* return TRUE if OK, FALSE if error */
- }
-
-
-
- /*
- * usage - print message explaining correct usage of the command-line
- * arguments and flags
- */
- usage()
- {
- FPR(stderr, "\nUsage:\t%s [-b DAY]* [-d FONT] [-n FONT] [-e | -f FILE] [-g DAY]* [-o FILE]\n", progname);
- FPR(stderr, "\t\t[-l|-p] [-L|-C|-R STRING] [-t FONT] [ [ [mm] yy ] | [mm yy n] ]\n");
- FPR(stderr, "\n");
- FPR(stderr, "\t-b DAY\t\tprint weekday DAY in black\n");
- FPR(stderr, "\t-g DAY\t\tprint weekday DAY in gray\n");
- FPR(stderr, "\t\t\t(default: %s)\n", COLOR_MSG);
- FPR(stderr, "\n");
- FPR(stderr, "\t-d FONT\t\tspecify alternate day name font (default: %s)\n",
- DAYFONT);
- FPR(stderr, "\t-t FONT\t\tspecify alternate title font (default: %s)\n",
- TITLEFONT);
- FPR(stderr, "\t-n FONT\t\tspecify alternate notes font (default: %s)\n",
- NOTESFONT);
- FPR(stderr, "\n");
- FPR(stderr, "\t-L STRING\t\tspecify left foot string (default: %s)\n",
- LFOOT);
- FPR(stderr, "\t-C STRING\t\tspecify center foot string (default: %s)\n",
- CFOOT);
- FPR(stderr, "\t-R STRING\t\tspecify right foot string (default: %s)\n",
- RFOOT);
- FPR(stderr, "\n");
- FPR(stderr, "\t-D SYM\t\tdefine preprocessor symbol\n");
- FPR(stderr, "\t-U SYM\t\tundefine preprocessor symbol\n");
- FPR(stderr, "\n");
- FPR(stderr, "\t-e\t\tgenerate empty calendar (ignore date file)\n");
- FPR(stderr, "\t-f FILE\t\tspecify alternate date file (default: %s)\n",
- DATEFILE);
- FPR(stderr, "\t-o FILE\t\tspecify alternate output file (default: %s)\n",
- OUTFILE[0] ? OUTFILE : "stdout");
- FPR(stderr, "\n");
- FPR(stderr, "\t-l\t\tgenerate landscape-style calendars");
- #if (ROTATE == LANDSCAPE)
- FPR(stderr, " (default)");
- #endif
- FPR(stderr, "\n\t-p\t\tgenerate portrait-style calendars");
- #if (ROTATE == PORTRAIT)
- FPR(stderr, " (default)");
- #endif
- FPR(stderr, "\n\n");
- FPR(stderr, "\tyy\t\tgenerate calendar for year yy (19yy if yy < 100)\n");
- FPR(stderr, "\tmm yy\t\tgenerate calendar for month mm (Jan = 1), year yy\n");
- FPR(stderr, "\tmm yy n\t\tgenerate calendars for n months, starting at mm/yy\n");
- FPR(stderr, "\t(default)\tgenerate calendar for current month/year\n");
- }
-
-
- /*
- * General-purpose utility routines
- */
-
-
- /*
- * alloc - interface to calloc(); terminates if unsuccessful
- */
- char *alloc(size)
- int size;
- {
- char *p;
- extern char *calloc();
-
- if (size == 0) /* not all calloc()s like null requests */
- size = 1;
-
- if ((p = calloc(1, size)) == NULL) {
- FPR(stderr, "%s: out of memory\n", progname);
- exit(EXIT_FAILURE);
- }
-
- return p;
- }
-
-
- /*
- * ci_str{n}cmp - case-insensitive flavors of strcmp(), strncmp()
- */
- int ci_strcmp(s1, s2)
- register char *s1, *s2;
- {
- register char c1, c2;
-
- for ( ; (c1 = TOLOWER(*s1)) == (c2 = TOLOWER(*s2)); s1++, s2++)
- if (c1 == '\0')
- return 0;
-
- return c1 - c2;
- }
-
-
- int ci_strncmp(s1, s2, n)
- register char *s1, *s2;
- int n;
- {
- register char c1, c2;
-
- for ( ; --n >= 0 && (c1 = TOLOWER(*s1)) == (c2 = TOLOWER(*s2)); s1++, s2++)
- if (c1 == '\0')
- return 0;
-
- return n < 0 ? 0 : c1 - c2;
- }
-
-
- /*
- * Preprocessor token and symbol table routines
- */
-
-
- /*
- * find_sym - look up symbol; return symbol table index if found, PP_SYM_UNDEF
- * if not found
- */
- int find_sym(sym)
- char *sym;
- {
- int i;
-
- if (!sym)
- return PP_SYM_UNDEF;
-
- for (i = 0; i < MAX_PP_SYMS; i++)
- if (pp_sym[i] && ci_strcmp(pp_sym[i], sym) == 0)
- return i;
-
- return PP_SYM_UNDEF;
- }
-
-
- /*
- * do_ifdef - return TRUE if 'sym' is currently defined; FALSE if not
- */
- int do_ifdef(sym)
- char *sym;
- {
- return find_sym(sym) != PP_SYM_UNDEF;
- }
-
-
- /*
- * do_ifndef - return FALSE if 'sym' is currently defined; TRUE if not
- */
- int do_ifndef(sym)
- char *sym;
- {
- return find_sym(sym) == PP_SYM_UNDEF;
- }
-
-
- /*
- * do_define - enter 'sym' into symbol table; if 'sym' NULL, clear symbol table
- */
- do_define(sym)
- char *sym;
- {
- int i;
-
- if (! sym) { /* null argument - clear all definitions */
- clear_syms();
- return;
- }
-
- if (do_ifdef(sym)) /* already defined? */
- return;
-
- for (i = 0; i < MAX_PP_SYMS; i++) /* find room for it */
- if (! pp_sym[i]) {
- strcpy(pp_sym[i] = alloc(strlen(sym)+1), sym);
- return;
- }
-
- FPR(stderr, "%s: no room to define %s\n", progname, sym);
- }
-
-
- /*
- * do_undef - undefine 'sym' and free its space; no error if not defined
- */
- do_undef(sym)
- char *sym;
- {
- int i;
-
- if (! sym)
- return;
-
- if ((i = find_sym(sym)) != PP_SYM_UNDEF) {
- free(pp_sym[i]);
- pp_sym[i] = NULL;
- }
- }
-
-
- /*
- * do_include - include specified file (optionally in "" or <>)
- */
- do_include(path, name)
- char *path; /* path to file */
- char *name; /* file name */
- {
- FILE *fp;
- char *p, incfile[STRSIZ], tmpnam[STRSIZ];
-
- if (! name) /* whoops, no date file */
- return;
-
- /* copy name, stripping "" or <> */
- strcpy(tmpnam, name + (*name == '"' || *name == '<'));
- if ((p = LASTCHAR(tmpnam)) && *p == '"' || *p == '>')
- *p = '\0';
-
- if ((fp = fopen(mk_filespec(incfile, path, tmpnam), "r")) == NULL) {
- FPR(stderr, "%s: can't open file %s\n", progname, incfile);
- exit(EXIT_FAILURE);
- }
-
- read_datefile(fp, incfile);
- fclose(fp);
- }
-
-
- /*
- * pp_token - look up 'token' in list of preprocessor tokens; return its
- * index if found, PP_OTHER if not (N.B.: this relies on the ordering of
- * PP_XXXXX and pp_info[]; see comments at their definitions).
- */
- int pp_token(token)
- char *token;
- {
- struct pp *p;
-
- for (p = pp_info;
- p->token && ci_strncmp(token, p->token, MIN_PPTOK_LEN);
- p++)
- ;
-
- return p - pp_info;
- }
-
- /*
- * Internal data structure routines
- */
-
-
- /*
- * find_year - find record in year list; optionally create if not present
- */
- year_info *find_year(year, insert) /* find record in year list */
- int year;
- int insert; /* insert if missing */
- {
- year_info *pyear, *plast, *p;
-
- for (plast = NULL, pyear = head; /* search linked list */
- pyear && pyear->year < year;
- plast = pyear, pyear = pyear->next)
- ;
-
- if (pyear && pyear->year == year) /* found - return it */
- return pyear;
-
- if (insert) { /* not found - insert it if requested */
- p = (year_info *) alloc(sizeof(year_info)); /* create new record */
- p->year = year;
-
- p->next = pyear; /* link it in */
- return *(plast ? &plast->next : &head) = p;
- }
- else
- return NULL;
- }
-
-
- /*
- * enter_day_info - enter text for specified day; avoid entering duplicates.
- * returns PARSE_INVDATE if date invalid, PARSE_OK if OK
- */
- int enter_day_info(m, d, y, is_holiday, pword) /* fill in information for given day */
- int m, d, y;
- int is_holiday;
- char **pword;
- {
- static year_info *pyear;
- static int prev_year = 0;
- month_info *pmonth;
- day_info *pday, *plast;
-
- if (! is_valid(m, d, y))
- return PARSE_INVDATE;
-
- if (y != prev_year) /* avoid unnecessary year lookup */
- pyear = find_year(y, 1);
-
- --m, --d; /* adjust for use as subscripts */
-
- if ((pmonth = pyear->month[m]) == NULL) /* find/create month record */
- pyear->month[m] = pmonth = (month_info *) alloc(sizeof(month_info));
-
- if (is_holiday)
- pmonth->holidays |= (1 << d);
-
- /* insert text for day at end of list (preserving the order of entry
- * for multiple lines on same day); eliminate those differing only
- * in spacing and capitalization from existing entries
- */
-
- get_text(pword); /* consolidate text into lbuf */
-
- if (*lbuf) {
- for (plast = NULL, pday = pmonth->day[d];
- pday;
- plast = pday, pday = pday->next)
- if (ci_strcmp(pday->text, lbuf) == 0) {
- pday->is_holiday |= is_holiday;
- return PARSE_OK;
- }
-
- /* unique - add to end of list */
-
- pday = (day_info *) alloc(sizeof(day_info));
- pday->is_holiday = is_holiday;
- strcpy(pday->text = (char *) alloc(strlen(lbuf)+1), lbuf);
- pday->next = NULL;
- *(plast ? &plast->next : &pmonth->day[d]) = pday;
- }
-
- return PARSE_OK;
- }
-
-
- /*
- * Housekeeping routines to free allocated data
- */
-
-
- /*
- * clear_syms - clear and deallocate the symbol table
- */
- clear_syms()
- {
- int i;
-
- for (i = 0; i < MAX_PP_SYMS; i++)
- if (pp_sym[i]) {
- free(pp_sym[i]);
- pp_sym[i] = NULL;
- }
- }
-
-
- /*
- * cleanup - free all allocated data
- */
- cleanup()
- {
- int i, j;
- year_info *py, *pny;
- month_info *pm;
- day_info *pd, *pnd;
-
- for (py = head; py; py = pny) { /* main data structure */
- pny = py->next;
- for (i = 0; i < 12; i++) {
- if ((pm = py->month[i]) == NULL)
- continue;
- for (j = 0; j < 31; j++)
- for (pd = pm->day[j]; pd; pd = pnd) {
- pnd = pd->next;
- free(pd->text);
- free(pd);
- }
- free(pm);
- }
- free(py);
- }
-
- clear_syms(); /* symbol table */
-
- }
-
-
- /*
- * Utility routines for date and text parsing and entry
- */
-
-
- /*
- * is_valid - return TRUE if m/d/y is a valid date
- */
- int is_valid(m, d, y)
- register int m, d, y;
- {
- static char len[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
-
- return m >= JAN && m <= DEC &&
- d >= 1 && d <= (len[m] + (m == FEB && IS_LEAP(y)));
- }
-
-
- /*
- * loadwords - tokenize line buffer into word array, return word count.
- * differs from old loadwords() in that it handles quoted (" or ') strings
- */
-
- int loadwords()
- {
- register char *pstr, *ptok;
- char *delim, **ap;
- register int i;
-
- pstr = lbuf;
-
- for (i = 0, ap = words; TRUE; i++, ap++) {
- delim = *pstr == '"' ? "\"" :
- *pstr == '\'' ? "'" :
- WHITESPACE;
-
- ptok = pstr += strspn(pstr, delim); /* look for next token */
-
- if (! *pstr) { /* end of lbuf? */
- *ap = NULL; /* add null ptr at end */
- return i; /* return count of non-null ptrs */
- }
-
- if (*ptok == '"' || *ptok == '\'') /* bump past quote */
- ptok++;
-
- *ap = ptok; /* save token ptr */
-
- pstr += strcspn(pstr, delim); /* skip past token */
-
- if (*pstr) /* terminate token */
- *pstr++ = '\0';
- }
-
- }
-
-
- /*
- * get_text - retrieve remaining text in lbuf and transform in place, removing
- * leading/trailing whitespace and condensing runs of whitespace to one blank
- */
- get_text(pword)
- char **pword; /* pointer to first desired word in "words" */
- {
- char *pbuf, *p;
-
- /* copy words back to lbuf in place, separating by one blank */
-
- for (pbuf = lbuf; p = *pword; *pbuf++ = *++pword ? ' ' : '\0')
- while (*p)
- *pbuf++ = *p++;
-
- if (pbuf == lbuf)
- *lbuf = '\0';
- }
-
-
- /*
- * print_text - print tokens in text (assumed separated by single blank)
- * in PostScript format; convert '(' or ')' to octal escape
- */
- print_text(p)
- char *p;
- {
- char c;
-
- PRT("(");
- for ( ; c = *p ; p++) {
- if (c == ' ')
- PRT(")\n(");
- else
- PRT(c == '(' || c == ')' ? "\\%03o" : "%c", c);
- }
- PRT(")\n");
- }
-
-
- /*
- * def_footstring - print definition for foot string, again converting '('
- * or ')' to octal escape
- */
- def_footstring(p, c)
- char *p; /* definition */
- char c; /* L, C, or R */
- {
-
- PRT("/%cfootstring (", c);
-
- while (c = *p++)
- PRT(c == '(' || c == ')' ? "\\%03o" : "%c", c);
-
- PRT(") def\n");
- }
-
-
- /*
- * set_color - set one or all weekdays to print in black or gray
- */
- set_color(day, col)
- char *day; /* weekday name (or "all") */
- int col; /* select black or gray */
- {
- int i, do_all;
-
- do_all = ci_strncmp(day, ALL, strlen(ALL)) == 0; /* set all days? */
-
- for (i = 0; i < 7; i++)
- if (do_all || ci_strncmp(day, days[i], MIN_DAY_LEN) == 0)
- color[i] = col;
- }
-
- /*
- * parse - enter date file data (in lbuf[]) into data structure
- *
- * Looks for an entry of one of the following forms:
- *
- * year <year>
- * <month_name> <day>{*} {<text>}
- * <month><sep><day>{<sep><year>}{*} {<text>}
- * opt <options>
- *
- * where
- * <month_name> := first 3+ characters of name of month (in English)
- * <sep> := one or more non-numeric, non-space, non-'*' characters
- * <options> := any command-line option except -e, -f, -D, -U
- *
- * Enters information for year, month, and day into data structure for
- * subsequent retrieval. Lines of form "opt <options>" override any
- * defaults.
- *
- * N.B.: "inc" and other cpp-like lines are handled in read_datefile().
- *
- */
- int parse()
- {
- register char *cp;
- char **pword;
- int mm, dd, yy;
- int is_holiday;
-
- /* macro to skip numeric field */
-
- #define SKIP_FIELD(p) \
- if (1) {while (isdigit(*p)) p++; while (*p && !isdigit(*p)) p++;} else
-
- /*
- * Get first field - can be either "year", "opt", a month name, or
- * a (complete) numeric date spec
- */
-
- cp = *(pword = words);
-
- /*
- * If field begins with alpha, look for one of the following:
- * "year", "opt", or month name
- */
- if (isalpha(*cp)) {
-
- /* Check for "year" line */
-
- if (ci_strcmp(cp, YEAR) == 0) {
- if ((cp = *++pword) != NULL && (yy = atoi(cp)) > 0) {
- if (yy < 100)
- yy += 1900;
- curr_year = yy;
- return PARSE_OK;
- }
- return PARSE_INVDATE; /* year missing or invalid */
- }
-
- /* Check for "opt" line */
-
- if (ci_strncmp(cp, OPT, strlen(OPT)) == 0) {
- if (!get_args(words, OTHER_FLAGS, FALSE)) {
- usage();
- exit(EXIT_FAILURE);
- }
- return PARSE_OK;
- }
-
- /* Check for month name */
-
- for (mm = JAN;
- mm <= DEC && ci_strncmp(cp, months[mm-1], MIN_MONTH_LEN);
- mm++)
- ;
-
- if (mm > DEC) /* not a month - reject line */
- return PARSE_INVLINE;
-
- /* month found, get and validate day */
-
- if ((cp = *++pword) == NULL || (dd = atoi(cp)) == 0)
- return PARSE_INVDATE;
- is_holiday = cp[strlen(cp) - 1] == '*'; /* look for holiday flag */
-
- return enter_day_info(mm, dd, curr_year, is_holiday, ++pword);
- }
-
- /*
- * Not alpha month, try numeric date (parse completely in case year
- * has changed)
- */
-
- if (isdigit(*cp)) {
-
- is_holiday = cp[strlen(cp) - 1] == '*'; /* look for holiday flag */
-
- /* extract month and day fields */
-
- mm = atoi(cp);
- SKIP_FIELD(cp);
-
- dd = atoi(cp);
- SKIP_FIELD(cp);
-
- /* Numeric dates may (or may not) have a year */
-
- if ((yy = atoi(cp)) > 0) {
- if (yy < 100)
- yy += 1900;
- curr_year = yy; /* if present, reset current year */
- }
-
- return enter_day_info(mm, dd, curr_year, is_holiday, ++pword);
- }
-
- return PARSE_INVLINE; /* line not recognized */
- }
-
-
- /*
- * getline - read next non-null line of input file into lbuf; return 0 on EOF
- */
- int getline(dfp, pline)
- FILE *dfp;
- int *pline;
- {
- register char *cp;
- register int c;
- int in_comment; /* comments: from '#' to end-of-line */
-
- cp = lbuf;
- do {
- in_comment = FALSE;
- while ((c = getc(dfp)) != '\n' && c != EOF) {
- if (c == '#')
- in_comment = TRUE;
-
- /* ignore comments and leading white space */
- if (in_comment ||
- (cp == lbuf && (c == ' ' || c == '\t')))
- continue;
- *cp++ = c;
- }
- if (c == EOF)
- return FALSE;
-
- (*pline)++; /* bump line number */
-
- } while (cp == lbuf); /* ignore empty lines */
-
- *cp = '\0';
- return TRUE;
- }
-
-
- /*
- * read_datefile - read and parse date file, handling preprocessor lines
- */
- read_datefile(fp, filename)
- FILE *fp; /* file pointer (assumed open) */
- char *filename; /* file name (for error messages) */
- {
- static int file_level = 0;
- int if_level = 0;
- int restart_level = 0;
- int line = 0;
- int processing = TRUE;
-
- int pptype, extra, ntokens, save_year;
- int (*pfcn)();
- char *ptok;
- char **pword;
- char msg[STRSIZ], incpath[STRSIZ];
-
- #define ERR(errmsg) FPR(stderr, "%s: %s in file %s, line %d\n", \
- progname, errmsg, filename, line);
-
- if (fp == NULL) /* whoops, no date file */
- return;
-
- if (++file_level > MAX_NESTING) {
- ERR("maximum file nesting level exceeded");
- exit(EXIT_FAILURE);
- }
-
- save_year = curr_year; /* save default year */
-
- /* read lines until EOF */
-
- while (getline(fp, &line)) {
-
- ntokens = loadwords(); /* split line into tokens */
- pword = words; /* point to the first */
-
- /* get token type and pointers to function and name */
-
- pptype = pp_token(*pword++);
- pfcn = pp_info[pptype].pfcn;
- ptok = pp_info[pptype].token;
-
- switch (pptype) {
-
- case PP_DEFINE:
- case PP_UNDEF:
- if (processing)
- (*pfcn)(*pword);
- extra = ntokens > 2;
- break;
-
- case PP_ELSE:
- if (if_level < 1) {
- ERR("unmatched \"else\"");
- break;
- }
-
- if (processing) {
- processing = FALSE; /* disable processing */
- restart_level = if_level;
- } else if (if_level == restart_level) {
- processing = TRUE; /* re-enable processing */
- restart_level = 0;
- }
- extra = ntokens > 1;
- break;
-
- case PP_ENDIF:
- if (if_level < 1) {
- ERR("unmatched \"end\"");
- break;
- }
-
- if (! processing && if_level == restart_level) {
- processing = TRUE; /* re-enable processing */
- restart_level = 0;
- }
- if_level--;
- extra = ntokens > 1;
- break;
-
- case PP_IFDEF:
- case PP_IFNDEF:
- if_level++;
- if (processing) {
- if (! (*pfcn)(*pword)) {
- processing = FALSE;
- restart_level = if_level;
- }
- }
- extra = ntokens > 2;
- break;
-
- case PP_INCLUDE:
- if (processing)
- do_include(mk_path(incpath, filename), *pword);
- extra = ntokens > 2;
- break;
-
- case PP_OTHER: /* none of the above - parse as date */
- if (processing) {
- switch (parse()) {
-
- case PARSE_INVDATE:
- ERR("invalid date");
- break;
-
- case PARSE_INVLINE:
- ERR("unrecognized line");
- break;
-
- }
- }
- extra = FALSE;
- break;
-
- } /* end switch */
-
- if (extra) { /* extraneous data? */
- sprintf(msg, "extraneous data on \"%s\" line", ptok);
- ERR(msg);
- }
-
- } /* end while */
-
- if (if_level > 0)
- FPR(stderr, "%s: unterminated if{n}def..{else..}endif in %s\n",
- progname, filename);
-
- file_level--;
- curr_year = save_year; /* restore default year */
- }
-
- /*
- * Routines to extract and print data
- */
-
-
- /*
- * Browse through the data structure looking for day or holiday text in
- * specified month/year
- */
- find_daytext(month, year, is_holiday)
- int month, year;
- int is_holiday;
- {
- register int day;
- year_info *py;
- month_info *pm;
- register day_info *pd;
- char *fcn = is_holiday ? "holidaytext" : "daytext";
- int first;
-
- /* if no text for this year and month, return */
-
- if ((py = find_year(year, FALSE)) == NULL ||
- (pm = py->month[month-1]) == NULL ||
- is_holiday && pm->holidays == 0)
- return;
-
- /* walk array of day text pointers and linked lists of text */
-
- for (day = 1; day <= 31; day++) {
- for (pd = pm->day[day-1], first = TRUE;
- pd;
- pd = pd->next) {
- if (pd->is_holiday != is_holiday)
- continue;
- if (first)
- PRT("%d [ \n", day); /* set up call */
- else
- PRT("(.p)\n"); /* separate text */
- print_text(pd->text);
- first = FALSE;
- }
- if (! first) /* wrap up call (if one made) */
- PRT("] %s\n", fcn);
- }
- }
-
-
- /*
- * Browse through the date file looking for holidays in specified month/year
- */
- find_holidays(month, year)
- int month, year;
- {
- register int day;
- register unsigned long holidays;
- year_info *py;
- month_info *pm;
-
- pm = (py = find_year(year, FALSE)) ? py->month[month-1] : NULL;
-
- PRT("/holidays ["); /* start definition of list */
-
- for (holidays = pm ? pm->holidays : 0, day = 1;
- holidays;
- holidays >>= 1, day++)
- if (holidays & 01)
- PRT(" %d", day);
-
- PRT(" 99 ] def\n"); /* terminate with dummy entry */
- }
-
-
- /*
- * pmonth - generate calendar for specified month/year
- */
- pmonth(month, year)
- int month, year;
- {
-
- PRT("/year %d def\n", year); /* set up year and month */
- PRT("/month %d def\n", month);
- find_holidays(month, year); /* make list of holidays */
- PRT("printmonth\n");
- find_daytext(month, year, TRUE); /* add holiday text to boxes */
- find_daytext(month, year, FALSE); /* add non-holiday text to boxes */
- PRT("showpage\n");
- }
-
-
- /*
- * Routines dealing with translation of file specifications (VMS, Un*x)
- */
-
- #ifdef VMS
- /*
- * mk_path - extract the path component from VMS file spec
- */
- char *mk_path(path, filespec)
- char *path; /* output path */
- char *filespec; /* input filespec */
- {
- char *p;
-
- strcpy(path, filespec);
- if (!(p = strchr(path, ']')) && !(p = strchr(path, ':')))
- p = path - 1; /* return null string if no path */
- *++p = '\0';
-
- return path;
- }
-
-
- /*
- * mk_filespec - merge VMS path and file names, where latter can be relative
- */
-
- char *mk_filespec(filespec, path, name)
- char *filespec; /* output filespec */
- char *path; /* input path */
- char *name; /* input file name */
- {
- char *p;
-
- *filespec = '\0';
-
- /* copy name intact if absolute; else merge path and relative name */
- if (!strchr(name, ':')) {
- strcpy(filespec, path);
- if ((p = LASTCHAR(filespec)) && *p == END_PATH &&
- name[0] == START_PATH && strchr(".-", name[1]))
- *p = *++name == '-' ? '.' : '\0';
- }
-
- return strcat(filespec, name);
- }
-
-
- /*
- * trnlog - return translation of VMS logical name (null if missing)
- */
- char *trnlog(logname) /* look up logical name */
- char *logname;
- {
- static char trnbuf[STRSIZ];
-
- $DESCRIPTOR(src, logname);
- $DESCRIPTOR(dst, trnbuf);
- short len;
- int ret;
-
- src.dsc$w_length = strlen(logname);
- ret = LIB$SYS_TRNLOG(&src, &len, &dst);
- return ret == SS$_NORMAL ? trnbuf[len] = '\0', trnbuf : 0;
- }
-
- #else
-
- /*
- * mk_path - extract the path component from a Un*x file spec
- */
- char *mk_path(path, filespec)
- char *path; /* output path */
- char *filespec; /* input filespec */
- {
- char *p;
-
- strcpy(path, filespec);
- if (! (p = strrchr(path, END_PATH)) )
- p = path - 1; /* return null string if no path */
-
- *++p = '\0';
- return path;
- }
-
-
- /*
- * mk_filespec - merge Un*x path and file names, where latter can be relative
- */
-
- char *mk_filespec(filespec, path, name)
- char *filespec; /* output filespec */
- char *path; /* input path */
- char *name; /* input file name */
- {
- char *p;
-
- *filespec = '\0';
-
- /* copy name intact if absolute; else merge path and relative name */
-
- /* if path starts with "~/", translate it for user */
- if (strncmp(name, "~/", 2) == 0 && (p = trnlog(HOME_DIR)) != NULL) {
- strcpy(filespec, p);
- if ((p = LASTCHAR(filespec)) && *p != END_PATH)
- *++p = END_PATH, *++p = '\0';
- name += 2; /* skip "~/" */
- }
- else if (*name != START_PATH) { /* relative path */
- strcpy(filespec, path);
- if ((p = LASTCHAR(filespec)) && *p != END_PATH)
- *++p = END_PATH, *++p = '\0';
- }
-
- return strcat(filespec, name);
- }
-
-
- /*
- * trnlog - return translation of Un*x environment variable
- */
- char *trnlog(logname) /* look up logical name */
- char *logname;
- {
- return getenv(logname);
- }
-
- #endif
-