home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / cawf407.zip / src / cawf.c < prev    next >
C/C++ Source or Header  |  1993-12-28  |  15KB  |  658 lines

  1. /*
  2.  *    cawf - a C version of Henry Spencer's awf(1), the Amazingly
  3.  *           Workable (text) Formatter
  4.  *
  5.  *    V. Abell, Purdue University Computing Center
  6.  */
  7.  
  8. /*
  9.  *    Copyright (c) 1991 Purdue University Research Foundation,
  10.  *    West Lafayette, Indiana 47907.  All rights reserved.
  11.  *
  12.  *    Written by Victor A. Abell <abe@mace.cc.purdue.edu>,  Purdue
  13.  *    University Computing Center.  Not derived from licensed software;
  14.  *    derived from awf(1) by Henry Spencer of the University of Toronto.
  15.  *
  16.  *    Permission is granted to anyone to use this software for any
  17.  *    purpose on any computer system, and to alter it and redistribute
  18.  *    it freely, subject to the following restrictions:
  19.  *
  20.  *    1. The author is not responsible for any consequences of use of
  21.  *       this software, even if they arise from flaws in it.
  22.  *
  23.  *    2. The origin of this software must not be misrepresented, either
  24.  *       by explicit claim or by omission.  Credits must appear in the
  25.  *       documentation.
  26.  *
  27.  *    3. Altered versions must be plainly marked as such, and must not
  28.  *       be misrepresented as being the original software.  Credits must
  29.  *       appear in the documentation.
  30.  *
  31.  *    4. This notice may not be removed or altered.
  32.  */
  33.  
  34. static char Version[] = "4.07";
  35.  
  36. #include "cawf.h"
  37.  
  38. #include <time.h>
  39.  
  40. #include <sys/stat.h>
  41. #ifndef    UNIX
  42. #include <io.h>
  43. #include <process.h>
  44. #include <string.h>
  45. #include <sys\types.h>
  46. #include <sys\stat.h>
  47. #endif
  48.  
  49. static char *Month[] =
  50.     { "January", "February", "March", "April", "May", "June", "July",
  51.       "August", "September", "October", "November", "December" };
  52.  
  53.  
  54. main(argc, argv)
  55.     int     argc;
  56.     char    *argv[];
  57. {
  58.     char dt[32];            /* date for td and DY strings */
  59.     char *ep;                   /* environment pointer */
  60.     int fff = 0;            /* final form feed status */
  61.     char *files[MAXFILES];      /* file names */
  62.     int help = 0;            /* help status */
  63.     int i;                           /* temporary index */
  64.     size_t l;                       /* length */
  65.     char *lib = CAWFLIB;        /* library path */
  66.     int libl;            /* library path length */
  67.     struct tm *lt;            /* local time structure pointer */
  68.     int mac = 0;            /* macro specification status */
  69.     int nf = 0;                 /* number of files */
  70.     char *np;                   /* name pointer */
  71.     int pc;                     /* prolog count */
  72.     struct stat sbuf;               /* stat buffer */
  73.     time_t tm;            /* system time of day */
  74. /*
  75.  * Save program name.
  76.  */
  77.     if ((Pname = strrchr(argv[0], '\\')) != NULL)
  78.         Pname++;
  79.     else if ((Pname = strrchr(argv[0], '/')) != NULL)
  80.         Pname++;
  81.     else
  82.         Pname = argv[0];
  83.  
  84. #ifdef    __EMX__
  85. /*
  86.  * Perform OS2 initialization:
  87.  *
  88.  *    expand response files;
  89.  *    do file name glob'bing
  90.  */
  91.     _response(&argc, &argv);
  92.     _wildcard(&argc, &argv);
  93. #endif    /* __EMX__ */
  94.  
  95. /*
  96.  * Set error file stream pointer.
  97.  */
  98.     Efs = stderr;
  99. /*
  100.  * Get library name.
  101.  */
  102.     if ((np = getenv("CAWFLIB")) != NULL)
  103.         lib = np;
  104.     libl = strlen(lib);
  105. /*
  106.  * Get device file name.
  107.  */
  108.     for (ep = getenv("TERM");; ep = NULL) {
  109.         if (ep == NULL || *ep == '\0')
  110.             ep = "dumb";
  111.         l = libl + 1 + strlen(ep) + strlen(".dev") + 1;
  112.         if ((np = malloc(l)) == NULL)
  113.             Error(FATAL, NOLINE,
  114.                 " no string space for device file: ", ep);
  115.         (void) sprintf(np, "%s/%s.dev", lib, ep);
  116.         if (stat(np, &sbuf) == 0)
  117.             break;
  118.         if (strcmp(ep, "dumb") == 0)
  119.             Error(FATAL, NOLINE, " no dumb.dev file in ", lib);
  120.         (void) free(np);
  121.     }
  122.     files[nf++] = np;
  123. /*
  124.  * Get common text file name.
  125.  */
  126.     l = libl + 1 + strlen("common") + 1;
  127.     if ((np = malloc(l)) == NULL)
  128.         Error(FATAL, NOLINE, " no string space for common file name",
  129.             NULL);
  130.     (void) sprintf(np, "%s/common", lib);
  131.     files[nf++] = np;
  132. /*
  133.  * Process options.
  134.  */
  135.     while ((i = getopt(argc, argv, "c:d:ef:hm:n:o:")) != EOF) {
  136.         switch (i) {
  137.     /*
  138.      * -c c
  139.      *
  140.      * Set the device configuration file path to c.
  141.      */
  142.         case 'c':
  143.             Devconf = optarg;
  144.             break;
  145.     /*
  146.      * -d d
  147.      *
  148.      * Set the output device name to d.
  149.      *
  150.      * The default output device name is NORMAL -- i.e., a device that
  151.      * does bold face with backspace and overprinting and italic face with
  152.      * underscore.  NORMAL is usually a terminal device.
  153.      *
  154.      * There is a built-in device, named ANSI, that does bold face with
  155.      * the ANSI shadow mode and italic face with the ANSI underscore mode.
  156.      * ANSI is normally a terminal device that supports the ANSI shadow
  157.      * and underscore modes.
  158.      *
  159.      * There is a built-in output device, named NONE, that does nothing
  160.      * at all for the bold or italic faces.  This is usually a terminal
  161.      * device.
  162.      *
  163.      * All other device names must match a stanza in the device
  164.      * configuration file.
  165.      */
  166.         case 'd':
  167.             Device = optarg;
  168.             break;
  169.     /*
  170.      * -e -- eject: issue final form feed
  171.      */
  172.         case 'e':
  173.             fff = 1;
  174.             break;
  175.     /*
  176.      * -f f
  177.      *
  178.      * Set f as the font name for the output device (from the device
  179.      * configuration file).
  180.      */
  181.         case 'f':
  182.             Devfont = optarg;
  183.             break;
  184.     /*
  185.      * -h -- display help (usage)
  186.      */
  187.         case 'h':
  188.             help = 1;
  189.             break;
  190.     /*
  191.      * -m m
  192.      *
  193.      * Set the macro file name to m.
  194.      *
  195.      *  Special support is provided for -man, -me and -ms.
  196.      */
  197.         case 'm':
  198.             if (mac) {
  199.                 Error(WARN, NOLINE,
  200.                     "multiple macro file declaration",
  201.                     NULL);
  202.                 break;
  203.             }
  204.             l = libl + 2 + strlen(optarg) + strlen(".mac") + 1;
  205.             if ((np = malloc(l)) == NULL)
  206.                 Error(FATAL, NOLINE, " no string space for ",
  207.                     argv[1]);
  208.             (void) sprintf(np, "%s/m%s.mac", lib, optarg);
  209.             files[nf++] = np;
  210.             if (strcmp(optarg, "an") == 0)
  211.                 Marg = MANMACROS;
  212.             else if (strcmp(optarg, "e") == 0)
  213.                 Marg = MEMACROS;
  214.             else if (strcmp(optarg, "s") == 0)
  215.                 Marg = MSMACROS;
  216.             mac++;
  217.             break;
  218.     /*
  219.      * -n n
  220.      *
  221.      * Set the starting page number to n.
  222.      */
  223.         case 'n':
  224.             Thispg = atoi(optarg);
  225.             break;
  226.     /*
  227.      * -o o
  228.      *
  229.      * Set the pages to print list from the string o.
  230.      *
  231.      * Entries are separated by commas.  A range is specified as n-m.
  232.      * A starting range of the form -m has one for a starting value,
  233.      * and a range of the form n- has an ending value equivalent to
  234.      * the end of the document.  For example:
  235.      *
  236.      *    -5,8,10-
  237.      *
  238.      * specifies the printing of pages 1 through 5, 8, and 10 through
  239.      * the end of the document.
  240.      */
  241.         case 'o':
  242.             if (!AsmPgRange(optarg))
  243.                 Error(WARN, NOLINE, "invalid page list: ",
  244.                     optarg);
  245.             break;
  246.     /*
  247.      * Option not recognized by getopt().
  248.      */
  249.         case '?':
  250.             Err = 1;
  251.         }
  252.     }
  253.     if (Defdev())
  254.         Err++;
  255.     if (help || Err) {
  256.       (void) fprintf(stderr,
  257.         "%s %s usage: [-c c] [-d d] [-e] [-f f] [-h] [-m m]\n",
  258.         Pname, Version);
  259.       (void) fprintf(stderr, "\t[-n n] [-o o] file...\n");
  260.       (void) fprintf(stderr,
  261.         "\t-c c      c is the device configuration file path\n");
  262.       (void) fprintf(stderr,
  263.         "\t          (default = %s/%s)\n", CAWFLIB, DEVCONFIG);
  264.       (void) fprintf(stderr,
  265.         "\t-d d      d is the output device name\n");
  266.       (void) fprintf(stderr,
  267.         "\t          (default = NORMAL, using \\b for bold and italic)\n");
  268.       (void) fprintf(stderr,
  269.         "\t          (built-ins = ANSI, NONE and NORMAL)\n");
  270.       (void) fprintf(stderr,
  271.         "\t-e        issue eject after last page\n");
  272.       (void) fprintf(stderr,
  273.         "\t-f f      f is the output device font name\n");
  274.       (void) fprintf(stderr,
  275.         "\t-h        display help (this output)\n");
  276.       (void) fprintf(stderr,
  277.         "\t-m m      m is the macro file name\n");
  278.       (void) fprintf(stderr,
  279.         "\t-n n      n is the starting page number\n");
  280.       (void) fprintf(stderr,
  281.         "\t-o o      o specifies output pages and page ranges\n");
  282.       (void) fprintf(stderr,
  283.         "\tfile ...  source file names\n");
  284.       exit(Err);
  285.     }
  286.     if (mac == 0) {
  287.  
  288.         /*
  289.          * No macros - enable Bold, Italic and Roman fonts.
  290.          */
  291.         for (i = 0; Fcode[i].nm; i++) {
  292.             switch (Fcode[i].nm) {
  293.             case 'B':
  294.             case 'I':
  295.             case 'R':
  296.                 Fcode[i].status = '1';
  297.             }
  298.         }
  299.     }
  300. /*
  301.  * Add user-supplied file names.
  302.  */
  303.     pc = nf;
  304.     if (optind >= argc) {
  305.         files[nf++] = NULL;       /* STDIN */
  306.     } else {
  307.         while (optind < argc) {
  308.             if (nf >= MAXFILES)
  309.                 Error(WARN, NOLINE, " too many files at ",
  310.                     argv[optind]);
  311.             files[nf++] = argv[optind++];
  312.         }
  313.     }
  314. /*
  315.  * Make sure all input files are accessible.
  316.  */
  317.     for (i = 0; i < nf; i++) {
  318.         if (files[i] != NULL) {
  319.             if (stat(files[i], &sbuf) != 0)
  320.                 Error(WARN, NOLINE, " can't find ", files[i]);
  321.         }
  322.     }
  323.     if (Err)
  324.         exit(1);
  325. /*
  326.  * Miscellaneous initialization.
  327.  */
  328.  
  329.     for (i = 0; ; i++) {
  330.         if (Pat[i].re == NULL)
  331.             break;
  332.         if ((Pat[i].pat = regcomp(Pat[i].re)) == NULL)
  333.             Error(WARN, NOLINE, Pat[i].re, " regcomp failure");
  334.     }
  335.     if ((i = Findscale((int)'n', 0.0, 0)) < 0)
  336.         Error(WARN, NOLINE, " can't find Scale['n']", NULL);
  337.     Scalen = Scale[i].val;
  338.     if ((i = Findscale((int)'u', 0.0, 0)) < 0)
  339.         Error(WARN, NOLINE, " can't find Scale['u']", NULL);
  340.     Scaleu = Scale[i].val;
  341.     if ((i = Findscale((int)'v', 0.0, 0)) < 0)
  342.         Error(WARN, NOLINE, " can't find Scale['v']", NULL);
  343.     Scalev = Scale[i].val;
  344.     (void) Findstr((unsigned char *)"CH", (unsigned char *)"= % -", 1);
  345.     Cont = Newstr((unsigned char *)" ");
  346.     Contlen = 1;
  347.     if ((Trtbl = (unsigned char *)malloc(256)) == NULL)
  348.         Error(WARN, NOLINE, " can't allocate translate table space",
  349.             NULL);
  350.     else {
  351.         *Trtbl = ' ';
  352.         for (i = 1; i < 256; i++)
  353.             Trtbl[i] = (unsigned char) i;
  354.     }
  355.     if (Err)
  356.         exit(1);
  357. /*
  358.  * Here begins pass1 of awf - reading input lines and expanding macros.
  359.  */
  360.  
  361. /*
  362.  * Output prolog.
  363.  */
  364.     if (Fstr.i)
  365.         Stringput(Fstr.i, Fstr.il);
  366.     Macro((unsigned char *)".^x");
  367.     Macro((unsigned char *)".^b");
  368.     Macro((unsigned char *)".^# 1 <prolog>");
  369. /*
  370.  * Put today's date in the -me td string or the -ms DY string.
  371.  */
  372.     if (Marg == MEMACROS || Marg == MSMACROS) {
  373.         tm = time(NULL);
  374.         lt = localtime(&tm);
  375.         (void) sprintf(dt, "%s %d, %d",
  376.             Month[lt->tm_mon], lt->tm_mday, lt->tm_year + 1900);
  377.         Findstr(
  378.             (Marg == MEMACROS) ? (unsigned char *)"td"
  379.                        : (unsigned char *)"DY",
  380.             (unsigned char *)dt, 1);
  381.     }
  382. /*
  383.  * Read input files.
  384.  */
  385.     for (i = 0; i < nf; i++) {
  386.         if (files[i] == NULL) {
  387.             np = "stdin";
  388.             Ifs = stdin;
  389.         } else {
  390. #ifdef    UNIX
  391.             if ((Ifs = fopen(files[i], "r")) == NULL)
  392. #else
  393.             if ((Ifs = fopen(files[i], "rt")) == NULL)
  394. #endif
  395.                 Error(FATAL, NOLINE, " can't open ", files[i]);
  396.             np = files[i];
  397.         }
  398.         if (i >= pc) {
  399.             (void) sprintf((char *)Line, ".^# 1 %s", np);
  400.             Macro(Line);
  401.             NR = 0;
  402.         }
  403.         Fsp = 0;
  404.         do {
  405.             while (fgets((char *)Line, MAXLINE, Ifs) != NULL) {
  406.                 NR++;
  407.                 if ((np = strrchr((char *)Line, '\n')) != NULL)
  408.                     *np = '\0';
  409.                 else
  410.                     Line[MAXLINE-1] = '\0';
  411.                 Macro(Line);
  412.             }
  413.             if (i >= pc)
  414.                 Macro((unsigned char *)".^e");
  415.             if (Ifs != stdin)
  416.                 (void) fclose(Ifs);
  417.             if (Fsp > 0) {
  418.                 Free(&Inname);
  419.                 Inname = Inn_stk[Fsp-1];
  420.                 NR = NR_stk[Fsp-1];
  421.                 Ifs = Ifs_stk[Fsp-1];
  422.             }
  423.         } while (Fsp-- > 0);
  424.     }
  425.     Macro(NULL);
  426.     if (fff)
  427.         Charput("\f");
  428.     return(Err);
  429. }
  430.  
  431.  
  432. /*
  433.  * AsmPg(pa, pb) - assemble an ASCII page number
  434.  */
  435.  
  436. char *
  437. AsmPg(pa, pb)
  438.     char *pa;            /* page number in ASCII */
  439.     int *pb;            /* page number in binary */
  440. {
  441.     for (*pb = 0; *pa; pa++) {
  442.         if (*pa < '0' || *pa > '9')
  443.             return(pa);
  444.         *pb = (*pb * 10) + (int)(*pa - '0');
  445.     }
  446.     return(pa);
  447. }
  448.  
  449.  
  450. /*
  451.  * AsmPgRange(s) - assemble a page range
  452.  */
  453.  
  454. int
  455. AsmPgRange(s)
  456.     char *s;            /* page range string */
  457. {
  458.     char *cp, *tbuf;
  459.     int pg, pl, pu;
  460.     struct pgrange *pr;
  461.  
  462.     if (s == NULL || (pl = strlen(s)) == 0)
  463.         return(1);
  464. /*
  465.  * Remove spaces from the range string.
  466.  */
  467.     if ((tbuf = (char *)malloc(pl + 1)) == NULL)
  468.         Error(FATAL, NOLINE, " no string space for page range", NULL);
  469.     for (cp = tbuf; *s; *s++) {
  470.         if (*s != ' ')
  471.             *cp++ = *s;
  472.     }
  473.     *cp = '\0';
  474. /*
  475.  * Parse the range string.
  476.  */
  477.     for (cp = tbuf; *cp;) {
  478.  
  479.     /*
  480.      * Get the starting value.
  481.      * A missing initial range value defaults to one.
  482.      */
  483.         if (*cp == '-')
  484.             pg = 1;
  485.         else
  486.             cp = AsmPg(cp, &pg);
  487.     /*
  488.      * Base further processing on the range terminator character.
  489.      */
  490.         switch (*cp) {
  491.         case ',':        /* single page number */
  492.         case '\0':
  493.             pl = pu = pg;
  494.             break;
  495.         case '-':        /* page number range */
  496.             cp++;
  497.             pl = pg;
  498.         /*
  499.          * Assemble the terminating page number.  A missing
  500.          * number defaults to page 1,000,000 -- the end of
  501.          * the document.
  502.          */
  503.             if (!*cp) {
  504.                 pu = 10000;
  505.                 break;
  506.             }
  507.             cp = AsmPg(cp, &pg);
  508.             pu = pg;
  509.             break;
  510.         default:
  511.             return(0);
  512.         }
  513.     /*
  514.      * Check the page numbers.
  515.      */
  516.         if (pl < 0 || pu < 0 || pl > pu)
  517.             return(0);
  518.         if (*cp) {
  519.             if (*cp != ',')
  520.                 return(0);
  521.             cp++;
  522.         }
  523.     /*
  524.      * Allocate, fill in, and connect a page range structure.
  525.      */
  526.         if ((pr = (struct pgrange *)malloc(sizeof(struct pgrange)))
  527.         == NULL) {
  528.             Error(FATAL, NOLINE, " no space for page range entry",
  529.                 NULL);
  530.         }
  531.         pr->l = pl;
  532.         pr->u = pu;
  533.         pr->next = PgRange;
  534.         PgRange = pr;
  535.     }
  536.     return(1);
  537. }
  538.  
  539.  
  540. /*
  541.  * Macro(inp) - process a possible macro statement
  542.  *        pass non-macros and macros alike to pass 2
  543.  */
  544.  
  545. void
  546. Macro(inp)
  547.     unsigned char *inp;        /* possible macro statement pointer */
  548. {
  549.     unsigned char c[2];        /* characters */
  550.     int endm;            /* end of macro status */
  551.     FILE *fs;            /* temporary file stream */
  552.     int req;            /* request character status */
  553.     unsigned char *s1, *s2;        /* temporary string pointers */
  554.  
  555.     if (inp == NULL) {
  556.         Pass2(NULL);
  557.         return;
  558.     }
  559.     req = (*inp == '.' || *inp == '\'') ? 1 : 0;
  560. /*
  561.  * Check for file name designator.
  562.  */
  563.     if (req && inp[1] == '^' && inp[2] == '#') {
  564.         Free(&Inname);
  565.         Inname = Field(3, inp, 1);
  566.         F = NULL;
  567.         Pass2(inp);
  568.         return;
  569.     }
  570. /*
  571.  * Check for source command - "^[.']so".
  572.  */
  573.     if (req && inp[1] == 's' && inp[2] == 'o') {
  574.         if ((s1 = Field(2, inp, 1)) == NULL) {
  575.             Error(WARN, LINE, " no file specified", NULL);
  576.             return;
  577.         }
  578.         if ((fs = fopen((char *)s1, "r")) == NULL) {
  579.             Error(WARN, LINE, " can't open", NULL);
  580.             return;
  581.         }
  582.         if (Fsp >= MAXFSTK) {
  583.             (void) fclose(fs);
  584.             Error(WARN, LINE, " nesting too deep", NULL);
  585.             return;
  586.         }
  587.         Ifs_stk[Fsp] = Ifs;
  588.         Ifs = fs;
  589.         Inn_stk[Fsp] = Inname;
  590.         Inname = F;
  591.         F = NULL;
  592.         NR_stk[Fsp++] = NR;
  593.         NR = 0;
  594.         return;
  595.     }
  596.  /*
  597.   * Check for start of macro definition.
  598.   */
  599.     if (req && inp[1] == 'd' && inp[2] == 'e') {
  600.         if (inp[3] != ' ' || inp[4] == '\0') {
  601.             Error(WARN, LINE, " illegal macro definition", NULL);
  602.             return;
  603.         }
  604.         c[0] = inp[4];
  605.         c[1] = inp[5];
  606.         Curmx = Findmacro(c, 1);
  607.         return;
  608.     }
  609. /*
  610.  * Check for macro text.  Remove double backslashes.
  611.  */
  612.     if (req && (inp[1] == '\0' || (inp[2] == '\0' && inp[0] == inp[1])))
  613.         endm = 1;
  614.     else
  615.         endm = 0;
  616.     if (Curmx >= 0 && !endm) {
  617.         if (Mtx >= MAXMTXT)
  618.             Error(FATAL, LINE, " out of macro text space", NULL);
  619.         if ((s1 = (unsigned char *)strchr((char *)inp, '\\')) == NULL)
  620.             Macrotxt[Mtx] = Newstr(inp);
  621.         else {
  622.             for (s1 = Pass1ln, s2 = inp;; s1++) {
  623.                 if ((*s1 = *s2++) == '\0')
  624.                     break;
  625.                 if (*s1 == '\\' && *s2 == '\\')
  626.                     s2++;
  627.             }
  628.             Macrotxt[Mtx] = Newstr(Pass1ln);
  629.         }
  630.         if (Macrotab[Curmx].bx == -1)
  631.             Macrotab[Curmx].bx = Mtx;
  632.         Mtx++;
  633.         Macrotab[Curmx].ct++;
  634.         return;
  635.     }
  636. /*
  637.  * Check for end of macro.
  638.  */
  639.     if (Curmx >= 0 && endm) {
  640.         Curmx = -1;
  641.         (void) sprintf((char *)Pass1ln, ".^# %d %s", NR, Inname);
  642.         Pass2(Pass1ln);
  643.         return;
  644.     }
  645.  /*
  646.   * Check for conditionals and macro expansions.
  647.   */
  648.     if (req
  649.     &&  (Findmacro(inp+1, 0) != -1 || regexec(Pat[0].pat, inp))) {
  650.         Expand(inp);
  651.         return;
  652.     }
  653. /*
  654.  * None of the above: forward the line.
  655.  */
  656.     Pass2(inp);
  657. }
  658.