/* * fontfilt.c - cawf post-processing font filter * * V. Abell * Purdue University Computing Center * * Fontfilt filters output from cawf, the C version of Henry Spencer's * Amazingly Workable (text) Formatter, awf, to produce printer-specific * codes for bold and italic characters. (Cawf provides modest support * for documents formatted with nroff's man(7) and ms(7) macros.) * * Fontfilt is based on work by and suggestions from Chet Creider * . */ /* * Copyright (c) 1991 Purdue University Research Foundation, * West Lafayette, Indiana 47907. All rights reserved. * * Written by Victor A. Abell , Purdue * University Computing Center. Not derived from licensed software; * derived from work by Chet Creider . * * Permission is granted to anyone to use this software for any * purpose on any computer system, and to alter it and redistribute * it freely, subject to the following restrictions: * * 1. The author is not responsible for any consequences of use of * this software, even if they arise from flaws in it. * * 2. The origin of this software must not be misrepresented, either * by explicit claim or by omission. Credits must appear in the * documentation. * * 3. Altered versions must be plainly marked as such, and must not * be misrepresented as being the original software. Credits must * appear in the documentation. * * 4. This notice may not be removed or altered. */ /* * Usage: * * fontfilt [-c config] [-d device] [-f font] [file(s)] * * where: * * -c config specifies an alternate configuration file * (default directory = CAWFLIB definition or * CAWFLIB env. variable) * * -d device specifies the output device * * -f font specifies the font to be used on the * output device * * file(s) the path(s) to the file(s) containing cawf, * -fe format output * * Cawf's font ESCape mode must be used - e. g., * * % cawf -fe -man cawf.1 | fontfilt -dlj3 -flg12 */ /* * See fontfilt.cf for a list of supported devices and fonts, or use * the -h (help) option. The default device is the last device named * in fontfilt.cf. */ #include #ifdef STDLIB #include #endif #ifdef UNIX #ifdef USG #include #else #include #endif #else #include #endif #include #include #include #include "cawflib.h" /* * Local definitions */ #define BOLD 'B' #define CONFIG "fontfilt.cf" #define ESC 0x1b #define ITALIC 'I' #define MAXLINE 512 #define ROMAN 'R' /* * Local global variables */ static char *Besc = NULL; /* bold font escape string pointer */ static char *Conf = NULL; /* configuration file path */ static char *Defdev = NULL; /* default device name (last name of * fontfilt.cf */ static char Font = ROMAN; /* current font */ static char *Iesc = NULL; /* italic font escape string pointer */ static char *Pname; /* program name */ static char *Resc = NULL; /* roman font escape string pointer */ /* * Structure for font definitions */ struct font { char *nm; /* font name */ char *fi; /* font initialization character sequence */ struct font *next; /* next font fot this device */ }; /* * Device structure */ struct dev { char *nm; /* device name */ struct font *f; /* supported fonts */ char *b; /* bold font ESCape sequence */ char *i; /* italic font ESCape sequence */ char *r; /* Roman font ESCape sequence */ struct dev *next; /* next device */ } *Dp = NULL; /* * Externals */ extern char *optarg; /* getopt(3) argument pointer */ extern int optind; /* getopt(3) index */ /* * Function definitions */ static char *Convstr(); static int Convfont(), Readcf(); static void Bold(), Getec(), Italic(), Roman(); #ifndef STDLIB char *getenv(), *malloc(), *strchr(), *strrchr(); #endif /* * Main program */ main(argc, argv) int argc; /* argument count */ char *argv[]; /* argument pointers */ { register int c; /* character buffer */ char *cnm = NULL; /* config file name */ char *dnm = NULL; /* device name */ struct dev *dp; /* device */ char *fnm = NULL; /* font name */ int err = 0; /* argument error count */ int fc, fx; /* file count and index */ struct font *fp; /* font pointer */ FILE *fs; /* file stream */ int help = 0; /* -h status */ struct stat sbuf; /* file stat() buffer */ char *sep; /* separator pointer */ /* * Save program name. */ if ((Pname = strrchr(argv[0], '\\')) != NULL) Pname++; else if ((Pname = strrchr(argv[0], '/')) != NULL) Pname++; else Pname = argv[0]; /* * Process options. */ while ((c = getopt(argc, argv, "c:d:f:h")) != EOF) { switch (c) { /* * -c config -- specify configuration file */ case 'c': if (cnm != NULL) { (void) fprintf(stderr, "%s: duplicate config name\n", Pname); err = 1; } else cnm = optarg; break; /* * -d device -- specify device */ case 'd': if (dnm != NULL) { (void) fprintf(stderr, "%s: duplicate device name\n", Pname); err = 1; } else dnm = optarg; break; /* * -f font -- specify font */ case 'f': if (fnm != NULL) { (void) fprintf(stderr, "%s: duplicate font name\n", Pname); err = 1; } else fnm = optarg; break; /* * -h -- request help */ case 'h': help++; break; /* * unknown option */ case '?': err = 1; } } /* * Handle file arguments. */ if (optind >= argc) fc = 0; else { fc = argc; for (fx = optind; fx < fc; fx++) { if (stat(argv[fx], &sbuf) != 0) { (void) fprintf(stderr, "%s: can't find %s\n", Pname, argv[fx]); err++; } } } /* * Read configuration file. */ if (Readcf(cnm) == 0) err++; /* * Validate device name. */ if (dnm == NULL) dnm = Defdev; for (dp = Dp; dp; dp = dp->next) { if (strcmp(dnm, dp->nm) == 0) break; } if (dp) { if (fnm == NULL && dp->f) fnm = (dp->f)->nm; } else { if (dnm == NULL) (void) fprintf(stderr, "%s: no device specified\n", Pname); else (void) fprintf(stderr, "%s: unknown device: %s\n", Pname, dnm); err++; dp = NULL; } /* * Validate font name. */ if (fnm != NULL && dp) { for (fp = dp->f; fp; fp = fp->next) { if (strcmp(fnm, fp->nm) == 0) break; } if (fp == NULL) { (void) fprintf(stderr, "%s: font %s not defined for device %s\n", Pname, fnm, dnm); err++; } } else fp = NULL; /* * Go no further if errors have been detected, or help has been requested. */ if (err || help) { (void) fprintf(stderr, "%s usage: [-c config ] [-d device]", Pname); (void) fprintf(stderr, " [-f font] [file(s)]\n"); (void) fprintf(stderr, "\t-c config specify configuration file path\n"); if (Conf != NULL) { (void) fprintf(stderr, "\t (currently using %s)\n", Conf); } (void) fprintf(stderr, "\t-d device specify device name\n"); if (Defdev != NULL) { (void) fprintf(stderr, "\t (default = %s)\n", Defdev); } (void) fprintf(stderr, "\t-f font specify font name\n"); (void) fprintf(stderr, "\tfile(s) specify cawf -fe output files\n"); (void) fprintf(stderr, "\t (default = standard input)\n"); (void) fprintf(stderr, "-d -f (first font is default)\n"); for (dp = Dp; dp; dp = dp->next) { (void) fprintf(stderr, "%-8.8s ", dp->nm); if ((fp = dp->f) == NULL) (void) fprintf(stderr, "none\n"); else { sep = ""; while (fp) { (void) fprintf(stderr, "%s%s", sep, fp->nm); sep = "|"; fp = fp->next; } (void) putc('\n', stderr); } } if (err) exit(1); exit(0); } /* * Issue font selection, if any, and set up font escape string pointers. */ if (fp && fp->fi) fputs(fp->fi, stdout); Besc = dp->b; Iesc = dp->i; Resc = dp->r; /* * Loop through the input files. */ fx = optind; do { /* * If there are no files, use standard input (once). */ if (fc == 0) fs = stdin; else { /* * Open the named file. */ #ifdef UNIX if ((fs = fopen(argv[fx], "r")) == NULL) #else if ((fs = fopen(argv[fx], "rt")) == NULL) #endif { (void) fprintf(stderr, "%s: can't open %s\n", Pname, argv[fx]); exit(1); } } /* * Read and process the file. */ while ( ! feof(fs) && (c = getc(fs)) != EOF) { switch (c) { case ESC: Getec(fs); if ( ! feof(fs) && (c = getc(fs)) != EOF) putchar(c); break; default: Roman(); putchar(c); break; } } if (fc != 0) (void) fclose(fs); } while (++fx < fc); exit(0); } /* * Bold() - set bold font */ static void Bold() { if (Font == BOLD) return; if (Besc) fputs(Besc, stdout); Font = BOLD; } /* * Convfont(dp, s) - convert a font for a device * * entry: * dp = device structure pointer * s = font definition string -- "name=initialization" * * exit: * return = 0 if errors detected */ static int Convfont(dp, s) struct dev *dp; /* device structure pointer */ char *s; /* font definition string */ { char *cp; /* temporary character pointer */ char *fn; /* font name */ struct font *fp; /* font structure pointer */ struct font *tfp; /* temporary font structure pointer */ /* * Get the font name allocate space for it and allocate space for * a font structure. */ if ((cp = strchr(s, '=')) == NULL) { (void) fprintf(stderr, "%s: bad %s font line format: %s\n", Pname, dp->nm, s); return(0); } if ((fn = malloc(cp - s + 1)) == NULL) { (void) fprintf(stderr, "%s: no space for %s font name %s\n", Pname, dp->nm, s); return(0); } (void) strncpy(fn, s, cp - s); fn[cp - s] = '\0'; if ((fp = (struct font *)malloc(sizeof(struct font))) == NULL) { (void) fprintf(stderr, "%s: no space for %s font struct %s\n", Pname, dp->nm, fn); return(0); } /* * Fill in the font structure with the font name and the initialization * string. */ fp->nm = fn; fp->next = NULL; if (dp->f == NULL) dp->f = fp; else { for (tfp = dp->f; tfp; tfp = tfp->next) { if (tfp->next == NULL) { tfp->next = fp; break; } } } if ((fp->fi = Convstr(cp + 1)) == NULL) return(0); return(1); } /* * Convstr(s) - convert a string * * entry: * s = input string * * exit: * return = converted string address * NULL = error */ static char * Convstr(s) char *s; /* input string */ { int c; /* character assembly */ char *cp; /* temporary character pointer */ char *em; /* error message */ int i; /* temporary index */ int l; /* length */ char *r; /* result string */ /* * Make space for the result. */ if ((r = malloc(strlen(s) + 1)) == NULL) { (void) fprintf(stderr, "%s: out of string space at %s\n", Pname, r); return(NULL); } /* * Copy the input string to the result, processing '\\' escapes. */ for (cp = r; *s;) { switch (*s) { case '\\': s++; if (*s >= '0' && *s <= '7') { /* * '\xxx' -- octal form */ for (c = i = 0; i < 3; i++, s++) { if (*s < '0' || *s > '7') { em = "non-octal char"; bad_string: (void) fprintf(stderr, "%s: %s : %s\n", Pname, em, r); return(NULL); } c = (c << 3) + *s - '0'; } if (c > 0377) { em = "octal char > 0377"; goto bad_string; } *cp++ = c; } else if (*s == 'x') { /* * '\xyy' -- hexadecimal form */ s++; for (c = i = 0; i < 2; i++, s++) { if ( ! isascii(*s) && ! isalpha(*s) && ! isdigit(*s)) { non_hex_char: em = "non-hex char"; goto bad_string; } c = c << 4; if (*s >= '0' && *s <= '9') c += *s - '0'; else if ((*s >= 'a' && *s <= 'f') || (*s >= 'A' && *s <= 'F')) c += *s + 10 - (isupper(*s) ? 'A' : 'a'); else goto non_hex_char; } *cp++ = c; } else if (*s == 'E' || *s == 'e') { /* * '\E' or '\e' -- ESCape */ *cp++ = ESC; } else if (*s == '\0') { em = "no char after \\"; goto bad_string; } else /* * escaped character (for some reason) */ *cp++ = *s++; break; /* * Copy a "normal" character. */ default: *cp++ = *s++; } } *cp = '\0'; return(r); } /* * Getec(fs) - get ESCape follower character * * entry: * fs = file stream */ static void Getec(fs) FILE *fs; /* file stream */ { register int c; if (feof(fs) || (c = getc(fs)) == EOF) return; switch (c) { case ROMAN: Roman(); break; case ITALIC: Italic(); break; case BOLD: Bold(); break; default: (void) fprintf (stderr, "%s: unknown escape sequence ESC-0x%x\n", Pname, c); break; } } /* * Italic() - set italic font */ static void Italic() { if (Font == ITALIC) return; if (Iesc) fputs(Iesc, stdout); Font = ITALIC; } /* * Roman() - set Roman font */ static void Roman() { if (Font == ROMAN) return; if (Resc) fputs(Resc, stdout); Font = ROMAN; } /* * Readcf(p) - read configuration file * * entry: * p = configuration file path * * exit: * return = 0 if error detected */ static int Readcf(p) char *p; /* configuration file path */ { FILE *fs; /* file stream */ char *dn; /* device name */ struct dev *dp; /* device structure pointer */ int err = 0; /* errror count */ int l; /* length */ char line[MAXLINE]; /* line buffer */ char *s; /* temporary string pointer */ /* * If a path is supplied, use it. */ if (p) Conf = p; else { /* * Use the CAWFLIB environment if it is defined. */ if ((Conf = getenv("CAWFLIB")) == NULL) Conf = CAWFLIB; l = strlen(Conf) + 1 + strlen(p ? p : CONFIG) + 1; if ((s = malloc(l)) == NULL) { (void) fprintf(stderr, "%s: no space for %s name\n", Pname, p ? p : CONFIG); return(0); } (void) sprintf(s, "%s/%s", Conf, p ? p : CONFIG); Conf = s; } /* * Open the configuration file. */ #ifdef UNIX if ((fs = fopen(Conf, "r")) == NULL) #else if ((fs = fopen(Conf, "rt")) == NULL) #endif { (void) fprintf(stderr, "%s: can't open config file: %s\n", Pname, Conf); return(0); } *line = ' '; /* * Look for a device definition line -- a line that begins with a name. */ while ( ! feof(fs)) { if (isascii(*line) && (isspace(*line) || *line == '#')) { (void) fgets(line, MAXLINE, fs); continue; } if ((s = strrchr(line, '\n')) != NULL) *s = '\0'; else line[MAXLINE-1] = '\0'; /* * Allocate space for the name and the device structure. */ if ((dn = malloc(strlen(line) + 1)) == NULL) { (void) fprintf(stderr, "%s: no string space for device %s\n", Pname, line); return(0); } (void) strcpy(dn, line); if ((dp = (struct dev *)malloc(sizeof(struct dev))) == NULL) { (void) fprintf(stderr, "%s: no space for structure for device %s\n", Pname, dn); return(0); } dp->next = Dp; Dp = dp; Defdev = dp->nm = dn; dp->b = dp->i = dp->r = NULL; dp->f = NULL; /* * Read the parameter lines for the device. */ while (fgets(line, MAXLINE, fs) != NULL) { if ( ! isascii(*line) || *line != '\t') break; if ( ! isascii(line[1]) || ! isalpha(line[1]) || line[2] != '=') break; if ((s = strrchr(line, '\n')) != NULL) *s = '\0'; else line[MAXLINE-1] = '\0'; switch (line[1]) { /* * \tb= */ case 'b': if ((dp->b = Convstr(&line[3])) == NULL) err++; break; /* * \ti= */ case 'i': if ((dp->i = Convstr(&line[3])) == NULL) err++; break; /* * \tr= */ case 'r': if ((dp->r = Convstr(&line[3])) == NULL) err++; break; /* * \tf== */ case 'f': if (Convfont(dp, &line[3]) == 0) err++; break; /* * ???? */ default: (void) fprintf(stderr, "%s: unknown device %s line %s\n", Pname, dp->nm, line); err++; } } } (void) fclose(fs); if (err) return(0); return(1); }