home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume15 / lwf / lwf.c next >
Encoding:
C/C++ Source or Header  |  1988-05-24  |  26.0 KB  |  1,156 lines

  1.  
  2. /* vi: set tabstop=4 : */
  3.  
  4. /*
  5.  * lwf - Convert ASCII text to PostScript
  6.  *
  7.  * Usage:
  8.  *    lwf [-d] [-i#] [-l] [-m] [-olist] [-p[str]] [-P filename] [-s#] [-t#]
  9.  *                [-v] [file ...]
  10.  *
  11.  * Options:
  12.  *    -d            Debug mode
  13.  *    -i#            Indent each line # inches (so much for metric)
  14.  *    -l            Landscape instead of Portrait
  15.  *    -m            Use 3 hole punch margins
  16.  *    -olist        Only print pages in the specified range
  17.  *    -p[str]        Use pr to print, passing optional string
  18.  *    -P filename Copy prologue from filename instead of default
  19.  *    -r            Toggle page reversal flag (see Makefile)
  20.  *    -s#            Use point size #
  21.  *    -t#            Spaces between tab stops is # characters
  22.  *    -v            Verbose
  23.  *    -S            Standalone mode (print header page, use EOF's)
  24.  *
  25.  * If no files are specified, stdin is used.
  26.  * Form feeds handled
  27.  * Backspacing with underlining (or overprinting) works
  28.  * The output conforms to Adobe 2.0
  29.  *
  30.  * Problems:
  31.  *    - assumes fixed-width (non-proportional) font in some places
  32.  *    - can't back up (using backspaces) over tabs
  33.  *    - assumes 8.5 x 11.0 paper
  34.  *
  35.  * BJB - Jun/87
  36.  * ========================================================================
  37.  *
  38.  * Permission is given to freely copy and distribute this software provided:
  39.  *
  40.  *    1) You do not sell it,
  41.  *    2) You do not use it for commercial advantage, and
  42.  *    3) This notice accompanies the distribution
  43.  *
  44.  * Copyright (c) 1988
  45.  * Barry Brachman
  46.  * Dept. of Computer Science
  47.  * Univ. of British Columbia
  48.  * Vancouver, B.C. V6T 1W5
  49.  *
  50.  * .. {ihnp4!alberta, uw-beaver, uunet}!ubc-vision!ubc-csgrads!brachman
  51.  * brachman@grads.cs.ubc.cdn
  52.  * brachman%ubc.csnet@csnet-relay.arpa
  53.  * brachman@ubc.csnet
  54.  * ========================================================================
  55.  */
  56.  
  57. #include <sys/file.h>
  58. #include <ctype.h>
  59. #include <pwd.h>
  60. #include <stdio.h>
  61.  
  62. #define min(a, b)        ((a) < (b) ? (a) : (b))
  63.  
  64. /*
  65.  * Configurable...
  66.  * BUFOUT should be fairly large
  67.  */
  68. #define BUFIN                1024    /* maximum length of an input line */
  69. #define BUFOUT                (BUFIN * 5)
  70. #define MAXPAGES            10000    /* maximum number of pages per job */
  71. #define DEFAULT_TAB_SIZE    8
  72. #define DEFAULT_POINT_SIZE    10
  73. #ifndef PROLOGUE
  74. #define PROLOGUE            "/usr/local/lib/lwf.prologue"
  75. #endif
  76. #ifndef REVERSE
  77. #define REVERSE                0
  78. #endif
  79. #ifndef PR
  80. #define PR                    "/bin/pr"
  81. #endif
  82.  
  83. #ifdef SYSV
  84. #define rindex                strrchr
  85. #endif
  86.  
  87. /*
  88.  * As mentioned in the man page, /bin/pr doesn't handle formfeeds correctly
  89.  * when doing multicolumn formatting
  90.  * Instead of starting a new column or page it passes a formfeed through,
  91.  * causing pr and lwf to get out-of-synch with regard to the current
  92.  * location on the page
  93.  * If your pr behaves this way (4.[23] does, SYSV doesn't), define PRBUG so
  94.  * that fgetline() will filter out these bogus formfeeds while preserving
  95.  * the column structuring
  96.  */
  97. #ifndef SYSV
  98. #define PRBUG                1
  99. #endif
  100.  
  101. /*
  102.  * PostScript command strings defined in the prologue file
  103.  */
  104. #define BACKSPACE        "B"
  105. #define ENDPAGE            "EP"
  106. #define LINETO            "L"
  107. #define MOVETO            "M"
  108. #define NEWPATH            "NP"
  109. #define SHOW            "S"
  110. #define STARTPAGE        "SP"
  111. #define STARTHPAGE        "SHP"
  112. #define STARTLPAGE        "SLP"
  113. #define STROKE            "ST"
  114. #define TAB                "T"
  115.  
  116. /*
  117.  * Conformance requires that no PostScript line exceed 256 characters
  118.  */
  119. #define MAX_OUTPUT_LINE_LENGTH    256
  120.  
  121. #define TEXTFONT        "Courier"
  122. #define HEADERFONT        "Times-Roman"
  123. #define HEADERPS        18            /* header page point size */
  124.  
  125. #define PORTRAIT_START_Y    768        /* first row (Y coord) on each page */
  126. #define LANDSCAPE_START_Y    576
  127. #define START_X                25        /* position of start of each line */
  128. #define START_Y_HEADER        700        /* first row (Y coord) of header */
  129. #define THREE_HOLE_X        1.0        /* portrait x offset (inches) 3 hole */
  130. #define THREE_HOLE_Y        0.5        /* landscape y offset (inches) 3 hole */
  131.  
  132. #define MAX_X            612
  133. #define MAX_Y            792
  134.  
  135. #define SEP_CHAR        '\001'        /* pr column separator character */
  136.  
  137. #define PS_EOF            04
  138.  
  139. #define NPSIZES            6
  140. struct psize {
  141.     int size;                        /* point size */
  142.     double charsperinch;            /* approx. char width, for Courier */
  143.     int portrait_page_length;        /* page length in lines */
  144.     int portrait_cols;                /* maximum # of chars per line */
  145.     int landscape_page_length;
  146.     int landscape_cols;
  147. } psize[NPSIZES] = {
  148.      7, 17.0, 108, 135, 80, 181,
  149.      8, 15.0,  94, 118, 70, 159,
  150.      9, 14.0,  84, 105, 62, 141,
  151.     10, 12.0,  75,  94, 56, 127,
  152.     11, 11.0,  68,  86, 51, 115,
  153.     12, 10.0,  62,  79, 46, 106
  154. };
  155.  
  156. #define USAGE    \
  157. "[-d] [-i#] [-l] [-m] [-olist] [-p[str]] [-r] [-s#] [-t#] [-v] [-S] [file ...]"
  158.  
  159. long page_map[MAXPAGES];    /* offset of first byte of each page */
  160. int page_count;
  161.  
  162. int lines_per_page;
  163. int columns;
  164. int point_size;
  165. int start_x, start_y;
  166. int ncopies;
  167.  
  168. char bufin[BUFIN];            /* input buffer */
  169. char bufout[BUFOUT];        /* used for page reversal and output buffering */
  170.  
  171. char *currentdate, *username;
  172. char hostname[32];
  173.  
  174. int row;
  175. char *range;
  176. int tabstop;
  177. char *propts;
  178.  
  179. int dflag, lflag, mflag, pflag, rflag, vflag, Sflag;
  180.  
  181. char *strcpy();
  182. char *fgetline();
  183. char *sprintf();
  184.  
  185. char *prologue;
  186. char *progname;
  187.  
  188. char *version = "lwf V2.0 brachman@ubc.csnet 21-Feb-88";
  189.  
  190. main(argc, argv)
  191. int argc;
  192. char **argv;
  193. {
  194.     register int i, j, first_file;
  195.     char *pc;
  196.     struct psize *p, *get_psize();
  197.     double offset, atof();
  198.     char *rindex();
  199.     FILE *infile, *popen();
  200.     double ceil();
  201.  
  202.     if ((pc = rindex(argv[0], '/')) != (char *) NULL)
  203.         progname = pc + 1;
  204.     else
  205.         progname = argv[0];
  206.     range = ":";
  207.     propts = "";
  208.     tabstop = DEFAULT_TAB_SIZE;
  209.     page_count = 0;
  210.     ncopies = 1;
  211.     offset = 0.0;
  212.     prologue = PROLOGUE;
  213.     p = get_psize(DEFAULT_POINT_SIZE);
  214.     rflag = REVERSE;
  215.  
  216.     for (i = 1; i < argc && argv[i][0] == '-'; i++) {
  217.         switch (argv[i][1]) {
  218.         case 'c':
  219.             ncopies = atof(&argv[i][2]);
  220.             if (ncopies <= 0) {
  221.                 fatal("number of copies must be > 0");
  222.                 /*NOTREACHED*/
  223.             }
  224.             break;
  225.         case 'd':
  226.             dflag = 1;
  227.             break;
  228.         case 'i':
  229.             offset = atof(&argv[i][2]);
  230.             if (offset < 0.0 || offset >= 8.5) {
  231.                 fatal("bad indent");
  232.                 /*NOTREACHED*/
  233.             }
  234.             break;
  235.         case 'l':
  236.             lflag = 1;
  237.             break;
  238.         case 'm':
  239.             mflag = 1;
  240.             break;
  241.         case 'o':
  242.             range = &argv[i][2];
  243.             if (checkrange(range)) {
  244.                 fatal("bad range specification");
  245.                 /*NOTREACHED*/
  246.             }
  247.             break;
  248.         case 'p':
  249.             pflag = 1;
  250.             propts = &argv[i][2];
  251.             break;
  252.         case 'P':
  253.             if (++i == argc) {
  254.                 fatal("missing filename after -P");
  255.                 /*NOTREACHED*/
  256.             }
  257.             prologue = argv[i];
  258.             break;
  259.         case 'r':
  260.             rflag = !rflag;
  261.             break;
  262.         case 's':
  263.             j = atoi(&argv[i][2]);
  264.             if ((p = get_psize(j)) == (struct psize *) NULL) {
  265.                 fatal("bad point size");
  266.                 /*NOTREACHED*/
  267.             }
  268.             break;
  269.         case 't':
  270.             tabstop = atoi(&argv[i][2]);
  271.             if (tabstop < 1) {
  272.                 fatal("bad tabstop");
  273.                 /*NOTREACHED*/
  274.             }
  275.             break;
  276.         case 'v':
  277.             vflag = 1;
  278.             break;
  279.         case 'S':
  280.             Sflag = 1;
  281.             break;
  282.         default:
  283.             (void) fprintf(stderr, "Usage: %s %s\n", progname, USAGE);
  284.             exit(1);
  285.         }
  286.     }
  287.  
  288.     /*
  289.      * Check that all files are readable
  290.      * This is so that no output at all is produced if any file is not
  291.      * readable in case the output is being piped to a printer
  292.      */
  293.     for (j = i; j < argc; j++) {
  294.         if (access(argv[j], R_OK) == -1) {
  295.             fatal("cannot access %s", argv[j]);
  296.             /*NOTREACHED*/
  297.         }
  298.     }
  299.  
  300.     point_size = p->size;
  301.  
  302.     if (lflag) {
  303.         start_y = LANDSCAPE_START_Y;
  304.         start_x = START_X + (int) (offset * 72.27);
  305.         lines_per_page = p->landscape_page_length;
  306.         columns = p->landscape_cols - (int) ceil(offset * p->charsperinch);
  307.         if (mflag) {
  308.             int nlines;
  309.  
  310.             nlines = (int) ceil((THREE_HOLE_Y * 72.27) / point_size);
  311.             start_y -= (nlines * point_size);
  312.             lines_per_page -= nlines;
  313.             columns -= (int) ceil(THREE_HOLE_Y * p->charsperinch);
  314.         }
  315.     }
  316.     else {
  317.         start_y = PORTRAIT_START_Y;
  318.         lines_per_page = p->portrait_page_length;
  319.         start_x = START_X;
  320.         if (mflag)
  321.                 offset += THREE_HOLE_X;
  322.         start_x += (int) (offset * 72.27);
  323.         columns = p->portrait_cols - (int) ceil(offset * p->charsperinch);
  324.     }
  325.     if (vflag) {
  326.         (void) fprintf(stderr, "%s\n\n", version);
  327.         (void) fprintf(stderr, "Lines/page = %d\n", lines_per_page);
  328.         (void) fprintf(stderr, "Columns = %d\n", columns);
  329.         (void) fprintf(stderr, "X-offset = %5.2f inches\n", offset);
  330.     }
  331.  
  332.     setup();
  333.     preamble();
  334.  
  335.     first_file = i;
  336.  
  337.     if (!rflag && Sflag)
  338.         header(argc - first_file, argv + first_file);
  339.  
  340.     if (i == argc) {    /* no files on command line */
  341.         infile = stdin;
  342.         if (pflag) {
  343.             build_prcmd(bufin, "");
  344.             if ((infile = popen(bufin, "r")) == (FILE *) NULL) {
  345.                 fatal("popen failed");
  346.                 /*NOTREACHED*/
  347.             }
  348.         }
  349.         if (vflag)
  350.             (void) fprintf(stderr, "printing stdin\n");
  351.         print(infile);
  352.         if (pflag)
  353.             (void) pclose(infile);
  354.     }
  355.  
  356.     /*
  357.      * If page reversal is performed, process the file arguments right to left,
  358.      * oth. left to right
  359.      * If the correct flag is used for the printer the first file argument
  360.      * will be on top in the printer's output tray when the paper is removed
  361.      */
  362.     if (rflag)
  363.         j = argc - 1;
  364.     else
  365.         j = i;
  366.     while (i < argc) {
  367.         infile = stdin;
  368.         if (pflag) {
  369.             build_prcmd(bufin, argv[j]);
  370.             if ((infile = popen(bufin, "r")) == (FILE *) NULL) {
  371.                 fatal("popen failed");
  372.                 /*NOTREACHED*/
  373.             }
  374.         }
  375.         else {
  376.             if (freopen(argv[j], "r", stdin) == (FILE *) NULL) {
  377.                 fatal("can't open %s", argv[j]);
  378.                 /*NOTREACHED*/
  379.             }
  380.         }
  381.         if (vflag)
  382.             (void) fprintf(stderr, "printing %s\n", argv[j]);
  383.         print(infile);
  384.         if (pflag)
  385.             (void) pclose(infile);
  386.         if (rflag)
  387.             j--;
  388.         else
  389.             j++;
  390.         i++;
  391.     }
  392.  
  393.     if (rflag && Sflag)
  394.         header(argc - first_file, argv + first_file);
  395.  
  396.     (void) printf("%%%%Trailer\n");
  397.     (void) printf("%%%%Pages: %d\n", page_count);
  398.     if (Sflag)
  399.         (void) putc(PS_EOF, stdout);
  400.  
  401.     if (fflush(stdout) == EOF) {
  402.         fatal("write error on stdout");
  403.         /*NOTREACHED*/
  404.     }
  405.     exit(0);
  406. }
  407.  
  408. /*
  409.  * Return a pointer to the point size structure for the
  410.  * specified point size
  411.  */
  412. struct psize *
  413. get_psize(size)
  414. int size;
  415. {
  416.     register int i;
  417.  
  418.     for (i = 0; i < NPSIZES; i++)
  419.         if (psize[i].size == size)
  420.             break;
  421.     if (i == NPSIZES)
  422.         return((struct psize *) NULL);
  423.     return(&psize[i]);
  424. }
  425.  
  426. /*
  427.  * Initial lines sent to the LaserWriter
  428.  * This stuff is sent to stdout since we don't want it to be reversed
  429.  * Generates the PostScript header and includes the prologue file
  430.  * There is limited checking for I/O errors here
  431.  * When the standard prologue is being used we probably should verify
  432.  * that it is the correct version (via %%BeginProcSet)
  433.  */
  434. preamble()
  435. {
  436.     FILE *fp;
  437.  
  438.     if ((fp = fopen(prologue, "r")) == (FILE *) NULL) {
  439.         fatal("can't open prologue file `%s'", prologue);
  440.         /*NOTREACHED*/
  441.     }
  442.  
  443.     if (Sflag)
  444.         (void) putc(PS_EOF, stdout);
  445.  
  446.     (void) printf("%%!PS-Adobe-2.0\n");
  447.     (void) printf("%%%%Creator: %s on %s\n", progname, hostname);
  448.     (void) printf("%%%%CreationDate: %s\n", currentdate);
  449.     (void) printf("%%%%For: %s\n", username);
  450.     (void) printf("%%%%DocumentFonts: %s", TEXTFONT);
  451.     if (Sflag)
  452.         (void) printf(" %s\n", HEADERFONT);
  453.     else
  454.         (void) printf("\n");
  455.     (void) printf("%%%%Pages: (atend)\n");
  456.  
  457.     while (fgets(bufin, sizeof(bufin), fp) != (char *) NULL)
  458.         fputs(bufin, stdout);
  459.     (void) fclose(fp);
  460.     if (ferror(stdout) || fflush(stdout) == EOF) {
  461.         fatal("write error on stdout");
  462.         /*NOTREACHED*/
  463.     }
  464. }
  465.  
  466. /*
  467.  * Generate a command, in the specified buffer, to print the given file
  468.  * according to the options in effect
  469.  */
  470. build_prcmd(buf, file)
  471. char *buf, *file;
  472. {
  473.  
  474. #ifdef SYSV
  475.     (void) sprintf(buf, "%s -e%d -w%d -l%d -s%c %s %s",
  476.                 PR, tabstop, columns, lines_per_page, SEP_CHAR, propts, file);
  477. #else
  478.     (void) sprintf(buf, "%s -w%d -l%d -s%c %s %s",
  479.                         PR, columns, lines_per_page, SEP_CHAR, propts, file);
  480. #endif
  481.     if (vflag)
  482.         (void) fprintf(stderr, "pr cmd: %s\n", buf);
  483. }
  484.  
  485. /*
  486.  * Print a file
  487.  *
  488.  * The input stream may be stdin, a file, or a pipe
  489.  * If page reversal is being performed, the output goes to a temporary file and
  490.  * then reverse() is called to do the page reversal to stdout
  491.  */
  492. print(infile)
  493. FILE *infile;
  494. {
  495.     register int eof, pagenum, r;
  496.     register char *p;
  497.     FILE *outfile;
  498.     char *mktemp();
  499.  
  500.     if (rflag) {
  501.         static char bigbuf[BUFOUT];
  502.  
  503.         page_map[0] = 0L;
  504.         (void) sprintf(bufin, "/tmp/%sXXXXXX", progname);
  505.         if (vflag)
  506.             (void) fprintf(stderr, "temp will be: %s ... ", bufin);
  507.         p = mktemp(bufin);
  508.         if (vflag)
  509.             (void) fprintf(stderr, "%s\n", p);
  510.         if ((outfile = fopen(p, "w+")) == (FILE *) NULL) {
  511.             (void) fprintf(stderr, "%s: can't create %s\n", progname, p);
  512.             cleanup();
  513.             /*NOTREACHED*/
  514.         }
  515.         setbuffer(outfile, bigbuf, sizeof(bigbuf));
  516.         if (!dflag)
  517.             (void) unlink(p);
  518.         else
  519.             (void) fprintf(stderr, "will not unlink %s\n", p);
  520.     }
  521.     else
  522.         outfile = stdout;
  523.  
  524.     pagenum = 1;
  525.     eof = 0;
  526.     while (!eof) {
  527.         row = start_y;
  528.         if ((r = inrange(pagenum, range)) == -1) {
  529.             cleanup();
  530.             /*NOTREACHED*/
  531.         }
  532.         else if (r == 1)
  533.             eof = printpage(infile, outfile);
  534.         else if (r == 0)
  535.             eof = flushpage(infile);
  536.         else {
  537.             fatal("bad inrange result");
  538.             /*NOTREACHED*/
  539.         }
  540.         pagenum++;
  541.     }
  542.     if (row != start_y)
  543.         endpage(outfile);
  544.     if (vflag)
  545.         (void) fprintf(stderr, "\n");
  546.     if (fflush(outfile) == EOF) {
  547.         fatal("write error while flushing output");
  548.         /*NOTREACHED*/
  549.     }
  550.     if (rflag) {
  551.         reverse(outfile);
  552.         (void) fclose(outfile);
  553.     }
  554. }
  555.  
  556. /*
  557.  * Process the next page
  558.  * Return 1 on EOF, 0 oth.
  559.  */
  560. printpage(infile, outfile)
  561. FILE *infile, *outfile;
  562. {
  563.     register int lineno;
  564.  
  565.     if (ungetc(getc(infile), infile) == EOF)
  566.         return(1);
  567.  
  568.     startpage(page_count + 1, outfile);
  569.     for (lineno = 0; lineno < lines_per_page; lineno++) {
  570.         if (fgetline(bufin, sizeof(bufin), infile) == (char *) NULL)
  571.             return(1);
  572.         if (bufin[0] == '\f')
  573.             break;
  574.         if (bufin[0] != '\0') {
  575.             (void) fprintf(outfile, "%d %d %s\n", start_x, row, MOVETO);
  576.             proc(bufin, outfile);
  577.         }
  578.         row -= point_size;
  579.     }
  580.     endpage(outfile);
  581.     return(0);
  582. }
  583.  
  584. /*
  585.  * The next page will not be printed; just consume the input and discard
  586.  * Don't change xrow since we don't want an endpage()
  587.  */
  588. flushpage(infile)
  589. FILE *infile;
  590. {
  591.     register int lineno, xrow;
  592.  
  593.     xrow = row;
  594.     for (lineno = 0; lineno < lines_per_page; lineno++) {
  595.         if (fgetline(bufin, sizeof(bufin), infile) == (char *) NULL)
  596.             return(1);
  597.         if (bufin[0] == '\f')
  598.             break;
  599.         xrow -= point_size;
  600.     }
  601.     return(0);
  602. }
  603.  
  604. /*
  605.  * Start a new page
  606.  */
  607. startpage(n, outfile)
  608. int n;
  609. FILE *outfile;
  610. {
  611.  
  612.     (void) fprintf(outfile, "%%%%Page: ? %d\n", n);
  613.     (void) fprintf(outfile, "%d /%s %s\n",
  614.             point_size, TEXTFONT, lflag ? STARTLPAGE : STARTPAGE);
  615. }
  616.  
  617. /*
  618.  * A page has been written to the temp file
  619.  * Record the start of the next page
  620.  * Terminate the page and indicate the start of the next
  621.  */
  622. endpage(outfile)
  623. FILE *outfile;
  624. {
  625.     long ftell();
  626.  
  627.     if (page_count == MAXPAGES) {
  628.         fatal("pagelimit (%d) reached", MAXPAGES);
  629.         /*NOTREACHED*/
  630.     }
  631.     (void) fprintf(outfile, "%d %s\n", ncopies, ENDPAGE);
  632.     if (rflag) {
  633.         if (fflush(outfile) == EOF) {
  634.             fatal("write error while flushing page");
  635.             /*NOTREACHED*/
  636.         }
  637.         page_map[++page_count] = ftell(outfile);
  638.     }
  639.     else
  640.         page_count++;
  641.     if (vflag)
  642.         (void) fprintf(stderr, "x");
  643. }
  644.  
  645. /*
  646.  * Print the pages to stdout in reverse order
  647.  * Assumes that the number of characters per page can be contained in an int
  648.  */
  649. reverse(outfile)
  650. FILE *outfile;
  651. {
  652.     register int i;
  653.     int bytecount, nbytes;
  654.     long lseek();
  655.  
  656.     if (vflag)
  657.         (void) fprintf(stderr, "\nreversing %d page%s\n", page_count,
  658.                         page_count > 1 ? "s" : "");
  659.     if (dflag) {
  660.         for (i = 0; i <= page_count; i++)
  661.             (void) fprintf(stderr, "[%ld]\n", page_map[i]);
  662.     }
  663.     for (i = page_count - 1; i >= 0; i--) {
  664.         if (fseek(outfile, page_map[i], 0) == -1L) {
  665.             fatal("seek error");
  666.             /*NOTREACHED*/
  667.         }
  668.         nbytes = (int) (page_map[i + 1] - page_map[i]);
  669.         while (nbytes > 0) {
  670.             bytecount = min(nbytes, sizeof(bufout));
  671.             if (fread(bufout, 1, bytecount, outfile) != bytecount) {
  672.                 fatal("read error while reversing pages");
  673.                 /*NOTREACHED*/
  674.             }
  675.             if (fwrite(bufout, 1, bytecount, stdout) != bytecount) {
  676.                 fatal("write error while reversing pages");
  677.                 /*NOTREACHED*/
  678.             }
  679.             nbytes -= bytecount;
  680.         }
  681.     }
  682. }
  683.  
  684. /*
  685.  * Process a line of input, escaping characters when necessary and handling
  686.  * tabs
  687.  *
  688.  * The output is improved somewhat by coalescing consecutive tabs and
  689.  * backspaces and eliminating tabs at the end of a line
  690.  *
  691.  * Overprinting (presumably most often used in underlining) can be far from
  692.  * optimal; in particular the way nroff underlines by sequences like
  693.  * "_\ba_\bb_\bc" creates a large volume of PostScript.  This isn't too
  694.  * serious since a lot of nroff underlining is unlikely.
  695.  *
  696.  * Since a newline is generated for each call there will be more
  697.  * newlines in the output than is necessary
  698.  */
  699. proc(in, outfile)
  700. char *in;
  701. FILE *outfile;
  702. {
  703.     register int i;
  704.     register char *last, *p, *q;
  705.     int currentp, instr, tabc, tabto;
  706.     char *savep;
  707.     static int colskip, ncols;
  708.     static int seen_sep = 0;
  709.  
  710.     currentp = 0;
  711.     instr = 0;
  712.     tabto = 0;
  713.     last = bufout + MAX_OUTPUT_LINE_LENGTH - 20;    /* subtract slop factor */
  714.  
  715.     q = bufout;
  716.     *q = '\0';
  717.     for (p = in; *p != '\0'; p++) {
  718.         switch (*p) {
  719.         case SEP_CHAR:
  720.             /*
  721.              * This assumes that the input buffer contains the entire line
  722.              * oth. the column count will be off
  723.              * Also, the input stream must be formatted into a constant number
  724.              * of columns oth. it would be necessary to scan each line to
  725.              * count SEP_CHARs (which is not hard but could be slow)
  726.              */
  727.             if (!seen_sep) {            /* discern number of columns */
  728.                 seen_sep = 1;
  729.                 ncols = 2;                /* there are at least two columns... */
  730.                 savep = p++;
  731.                 while (*p != '\0') {
  732.                     if (*p++ == SEP_CHAR)
  733.                         ncols++;
  734.                 }
  735.                 p = savep;
  736.                 colskip = columns / ncols;
  737.                 if (vflag)
  738.                     (void) fprintf(stderr, "Using %d columns\n", ncols);
  739.             }
  740.             if (instr) {
  741.                 (void) sprintf(q, ")%s ", SHOW);
  742.                 q += strlen(q);
  743.                 instr = 0;
  744.             }
  745.             tabto += (colskip - currentp);
  746.             currentp = 0;
  747.             break;
  748.         case '\t':
  749.             /*
  750.              * Count the number of tabs that immediately follow the one we're
  751.              * looking at
  752.              */
  753.             tabc = 0;
  754.             while (*(p + 1) == '\t') {
  755.                 p++;
  756.                 tabc++;
  757.             }
  758.             if (currentp > 0) {        /* not beginning of line */
  759.                 i = tabstop - (currentp % tabstop) + tabc * tabstop;
  760.                 if (instr) {
  761.                     (void) sprintf(q, ")%s ", SHOW);
  762.                     q += strlen(q);
  763.                     instr = 0;
  764.                 }
  765.             }
  766.             else
  767.                 i = (tabc + 1) * tabstop;
  768.             tabto += i;
  769.             currentp += i;
  770.             break;
  771.         case '\b':
  772.             *q = '\0';
  773.             (void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
  774.             /* backspacing over tabs doesn't work... */
  775.             if (tabto != 0) {
  776.                 fatal("attempt to backspace over a tab");
  777.                 /*NOTREACHED*/
  778.             }
  779.             p++;
  780.             for (i = 1; *p == '\b'; p++)
  781.                 i++;
  782.             if (currentp - i < 0) {
  783.                 fatal("too many backspaces");
  784.                 /*NOTREACHED*/
  785.             }
  786.             if (!instr) {
  787.                 fatal("bad backspacing");
  788.                 /*NOTREACHED*/
  789.             }
  790.             if (i == 1)                /* frequent case gets special attention */
  791.                 (void) sprintf(bufout, "%s (", BACKSPACE);
  792.             else
  793.                 (void) sprintf(bufout, "-%d %s (", i, TAB);
  794.             currentp -= i;
  795.             q = bufout + strlen(bufout);
  796.             p--;
  797.             break;
  798.         case '\f':
  799.             tabto = 0;                            /* optimizes */
  800.             *q = '\0';
  801.             if (instr)
  802.                 (void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
  803.             else
  804.                 (void) fprintf(outfile, "%s\n", bufout);
  805.             endpage(outfile);
  806.             startpage(page_count + 1, outfile);
  807.             row = start_y;
  808.             (void) fprintf(outfile, "%d %d %s\n", start_x, row, MOVETO);
  809.             q = bufout;
  810.             currentp = 0;
  811.             instr = 0;
  812.             break;
  813.         case '\r':
  814.             tabto = 0;                            /* optimizes */
  815.             if (instr) {
  816.                 *q = '\0';
  817.                 (void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
  818.                 instr = 0;
  819.                 q = bufout;
  820.             }
  821.             (void) fprintf(outfile, "%d %d %s\n", start_x, row, MOVETO);
  822.             currentp = 0;
  823.             break;
  824.         case '\\':
  825.         case '(':
  826.         case ')':
  827.             if (!instr) {
  828.                 if (tabto) {
  829.                     (void) sprintf(q, "%d %s ", tabto, TAB);
  830.                     q += strlen(q);
  831.                     tabto = 0;
  832.                 }
  833.                 *q++ = '(';
  834.                 instr = 1;
  835.             }
  836.             *q++ = '\\';
  837.             *q++ = *p;
  838.             currentp++;
  839.             break;
  840.         default:
  841.             /*
  842.              * According to the PostScript Language Manual, PostScript files
  843.              * can contain only "the printable subset of the ASCII character
  844.              * set (plus the newline marker)".
  845.              */
  846.             if (!isascii(*p) || !isprint(*p)) {
  847.                 fatal("bad character in input");
  848.                 /*NOTREACHED*/
  849.             }
  850.             if (!instr) {
  851.                 if (tabto) {
  852.                     (void) sprintf(q, "%d %s ", tabto, TAB);
  853.                     q += strlen(q);
  854.                     tabto = 0;
  855.                 }
  856.                 *q++ = '(';
  857.                 instr = 1;
  858.             }
  859.             *q++ = *p;
  860.             currentp++;
  861.             break;
  862.         }
  863.         if (q >= last) {
  864.             *q = '\0';
  865.             if (instr)
  866.                 (void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
  867.             else
  868.                 (void) fprintf(outfile, "%s\n", bufout);
  869.             q = bufout;
  870.             instr = 0;
  871.         }
  872.     }
  873.     if (instr) {
  874.         (void) sprintf(q, ")%s", SHOW);
  875.         q += strlen(q);
  876.     }
  877.     else
  878.         *q = '\0';
  879.     if (q >= last) {
  880.         fatal("bufout overflow");
  881.         /*NOTREACHED*/
  882.     }
  883.     if (bufout[0] != '\0')
  884.         (void) fprintf(outfile, "%s\n", bufout);
  885. }
  886.  
  887. /*
  888.  * Find out who the user is, etc.
  889.  * Possible system dependencies here...
  890.  */
  891. setup()
  892. {
  893.     int len;
  894.     char *p;
  895.     long t, time();
  896.     int gethostname();
  897.     char *ctime(), *getlogin(), *malloc();
  898.     struct passwd *pw, *getpwuid();
  899.  
  900.     if ((p = getlogin()) == (char *) NULL) {
  901.         if ((pw = getpwuid(getuid())) == (struct passwd *) NULL)
  902.             p = "Whoknows";
  903.         else
  904.             p = pw->pw_name;
  905.         endpwent();
  906.     }
  907.     username = (char *) malloc((unsigned) (strlen(p) + 1));
  908.     (void) strcpy(username, p);
  909.  
  910. #ifdef HOSTNAME
  911.     (void) strncpy(hostname, HOSTNAME, sizeof(hostname));
  912.     hostname[sizeof(hostname) - 1] = '\0';
  913. #else
  914.     (void) gethostname(hostname, sizeof(hostname));
  915. #endif
  916.  
  917.     t = time((long *) 0);
  918.     p = ctime(&t);
  919.     len = strlen(p);
  920.     *(p + len - 1) = '\0';        /* zap the newline character */
  921.     currentdate = (char *) malloc((unsigned) len);
  922.     (void) strcpy(currentdate, p);
  923. }
  924.  
  925. /*
  926.  * Print a header page
  927.  * Assumes setup() has already been called to fill in the user, host, etc.
  928.  * Uses HEADERFONT in HEADERPS point
  929.  */
  930. header(nfiles, files)
  931. int nfiles;
  932. char **files;
  933. {
  934.     register int i;
  935.     register char *p;
  936.  
  937.     if (vflag) {
  938.         (void) fprintf(stderr, "printing header\n");
  939.         (void) fprintf(stderr, "%d file%s are:\n", nfiles,
  940.                             nfiles > 1 ? "s" : "");
  941.         if (nfiles == 0)
  942.             (void) fprintf(stderr, "\tstdin\n");
  943.         for (i = 0; i < nfiles; i++)
  944.             (void) fprintf(stderr, "\t%s\n", files[i]);
  945.     }
  946.  
  947.     (void) fprintf(stdout, "%%%%Page: ? %d\n", ++page_count);
  948.     (void) fprintf(stdout, "%d /%s %s\n", HEADERPS, HEADERFONT, STARTHPAGE);
  949.  
  950.     /*
  951.      * The header sheet looks like:
  952.      *
  953.      * ----------------------------
  954.      * ----------------------------
  955.      *
  956.      * User:
  957.      * Host:
  958.      * Date:
  959.      * Files:
  960.      *
  961.      * ----------------------------
  962.      * ----------------------------
  963.      */
  964.     row = START_Y_HEADER;
  965.     (void) printf("%s %d %d %s\n", NEWPATH, START_X, row, MOVETO);
  966.     (void) printf("%d %d %s\n", START_X + 400, row, LINETO);
  967.     row -= 6;
  968.     (void) printf("%d %d %s\n", START_X, row, MOVETO);
  969.     (void) printf("%d %d %s\n", START_X + 400, row, LINETO);
  970.     row -= 24;
  971.     (void) printf("%s\n", STROKE);
  972.  
  973.     (void) printf("%d %d %s\n", START_X, row, MOVETO);
  974.     (void) sprintf(bufin, "User: %s", username);
  975.     proc(bufin, stdout);
  976.     row -= 24;
  977.     (void) printf("%d %d %s\n", START_X, row, MOVETO);
  978.     (void) sprintf(bufin, "Host: %s", hostname);
  979.     proc(bufin, stdout);
  980.     row -= 24;
  981.     (void) printf("%d %d %s\n", START_X, row, MOVETO);
  982.     (void) sprintf(bufin, "Date: %s", currentdate);
  983.     proc(bufin, stdout);
  984.     row -= 24;
  985.  
  986.     if (nfiles == 0) {
  987.         (void) printf("%d %d %s\n", START_X, row, MOVETO);
  988.         (void) sprintf(bufin, "File: <stdin>");
  989.         proc(bufin, stdout);
  990.     }
  991.     else {
  992.         register int len, max, sum;
  993.  
  994.         /*
  995.          * If the list of files is "too long" we'll only print as many as
  996.          * possible
  997.          * Arbitrary chop off point is 50 characters
  998.          * (assume bufin is bigger than this)
  999.          */
  1000.         (void) printf("%d %d %s\n", START_X, row, MOVETO);
  1001.         (void) sprintf(bufin, "File%s: ", nfiles > 1 ? "s" : "");
  1002.         p = bufin + (sum = strlen(bufin));
  1003.         max = 50;
  1004.         for (i = 0; i < nfiles - 1; i++) {
  1005.             sum += (len = strlen(files[i]) + 1);
  1006.             if (sum >= max)
  1007.                 break;
  1008.             (void) sprintf(p, "%s,", files[i]);
  1009.             p += len;
  1010.         }
  1011.         sum += (len = strlen(files[i]) + 1);
  1012.         if (sum < max)
  1013.             (void) sprintf(p, "%s", files[i]);
  1014.         else
  1015.             (void) strcpy(p, "...");
  1016.         proc(bufin, stdout);
  1017.     }
  1018.  
  1019.     row -= 12;
  1020.     (void) printf("%s %d %d %s\n", NEWPATH, START_X, row, MOVETO);
  1021.     (void) printf("%d %d %s\n", START_X + 400, row, LINETO);
  1022.     row -= 6;
  1023.     (void) printf("%d %d %s\n", START_X, row, MOVETO);
  1024.     (void) printf("%d %d %s\n", START_X + 400, row, LINETO);
  1025.     (void) printf("%s\n", STROKE);
  1026.     (void) printf("1 %s\n", ENDPAGE);
  1027.     if (fflush(stdout) == EOF) {
  1028.         fatal("write error on stdout");
  1029.         /*NOTREACHED*/
  1030.     }
  1031. }
  1032.  
  1033. /*
  1034.  * Special version of fgets
  1035.  * Read until a formfeed, newline, or overflow
  1036.  * If a formfeed is the first character, return it immediately
  1037.  * If a formfeed is found after the first character, replace it by a newline
  1038.  * and push the formfeed back onto the input stream
  1039.  * A special case is a formfeed followed by a newline in which case the
  1040.  * newline is ignored 
  1041.  * The input buffer will be null-terminated and will *not* end with a newline
  1042.  * The buffer size n includes the null
  1043.  */
  1044. char *
  1045. fgetline(s, n, iop)
  1046. char *s;
  1047. int n;
  1048. register FILE *iop;
  1049. {
  1050.     register int ch;
  1051.     register char *cs;
  1052.  
  1053.     if (n < 2) {
  1054.         fatal("fgetline called with bad buffer size!?");
  1055.         /*NOTREACHED*/
  1056.     }
  1057.  
  1058.     cs = s;
  1059.     n--;                                /* the null */
  1060.  
  1061.     /*
  1062.      * Check out the special cases
  1063.      */
  1064.     if ((ch = getc(iop)) == EOF)
  1065.         return((char *) NULL);
  1066.     if (ch == '\f') {
  1067. #ifdef PRBUG
  1068.         if (pflag) {
  1069.             /*
  1070.              * Filter out the formfeeds
  1071.              */
  1072.             do {
  1073.                 if (ch == '\f')
  1074.                     continue;
  1075.                 if (ch == '\n')
  1076.                     break;
  1077.                 *cs++ = ch;
  1078.                 n--;
  1079.             } while (n > 0 && (ch = getc(iop)) != EOF);
  1080.             if (ch == EOF) {
  1081.                 if (ungetc(ch, iop) == EOF && !feof(iop)) {
  1082.                     /* Shouldn't happen since a getc() was just done */
  1083.                     fatal("fgetline - ungetc failed");
  1084.                     /*NOTREACHED*/
  1085.                 }
  1086.             }
  1087.             else if (ch != '\n') {
  1088.                 fatal("fgetline - input line too long");
  1089.                 /*NOTREACHED*/
  1090.             }
  1091.             *cs = '\0';
  1092.             return(s);
  1093.         }
  1094. #endif
  1095.         if ((ch = getc(iop)) != '\n') {
  1096.             /*
  1097.              * If EOF was just read it will be noticed next time through
  1098.              */
  1099.             if (ungetc(ch, iop) == EOF && !feof(iop)) {
  1100.                 /* Shouldn't happen since a getc() was just done */
  1101.                 fatal("fgetline - ungetc failed");
  1102.                 /*NOTREACHED*/
  1103.             }
  1104.         }
  1105.         *cs++ = '\f';
  1106.         *cs = '\0';
  1107.         return(s);
  1108.     }
  1109.  
  1110.     /*
  1111.      * Check for "weird" input characters is made in proc()
  1112.      */
  1113.     while (n-- > 0) {
  1114.         if (ch == '\f' || ch == '\n')
  1115.             break;
  1116.         *cs++ = ch;
  1117.         if ((ch = getc(iop)) == EOF)
  1118.             break;
  1119.     }
  1120.  
  1121.     if (ch == EOF && cs == s)        /* Nothing was read */
  1122.         return((char *) NULL);
  1123.     if (ch == '\f') {
  1124.         if (ungetc(ch, iop) == EOF)
  1125.             (void) fprintf(stderr, "fgetline - can't ungetc??\n");
  1126.     }
  1127.     else if (ch != '\n' && ch != EOF) {
  1128.         fatal("fgetline - input line too long");
  1129.         /*NOTREACHED*/
  1130.     }
  1131.     *cs = '\0';
  1132.     return(s);
  1133. }
  1134.  
  1135. /*VARARGS*/
  1136. fatal(s, a, b, c, d, e, f, g, h, i, j)
  1137. char *s;
  1138. {
  1139.  
  1140.     (void) fprintf(stderr, "%s: ", progname);
  1141.     (void) fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j);
  1142.     (void) fprintf(stderr, "\n");
  1143.     cleanup();
  1144.     /*NOTREACHED*/
  1145. }
  1146.  
  1147. /*
  1148.  * Clean up and exit after an error
  1149.  */
  1150. cleanup()
  1151. {
  1152.  
  1153.     exit(1);
  1154. }
  1155.  
  1156.