home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1070 / a2ps.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  14.2 KB  |  571 lines

  1. /************************************************************************/
  2. /*                                    */
  3. /* Description: Ascii to PostScript printer program.            */
  4. /* File: imag:/users/local/a2ps/a2ps.c                    */
  5. /* Created: Mon Nov 28 15:22:15 1988 by miguel@imag (Miguel Santana)    */
  6. /* Version: 2.0                                */
  7. /*                                    */
  8. /* Edit history:                            */
  9. /* 1) Derived of shell program written by evan@csli (Evan Kirshenbaum).    */
  10. /*    Written in C for improve speed execution and portability. Many    */
  11. /*    improvements have been added.                    */
  12. /* Fixes by Oscar Nierstrasz @ cui.uucp:                */
  13. /* 2) Fixed incorrect handling of stdin (removed error if no file names)*/
  14. /* 3) Added start_page variable to eliminate blank pages printed for    */
  15. /*    files that are exactly multiples of 132 lines (e.g., man pages)    */
  16. /* Modified by miguel@imag:                        */
  17. /* 4) Added new options at installation : sheet format (height/width in    */
  18. /*    inches), page format (number of columns per line and of lines per    */
  19. /*    page).                                */
  20. /* Modified by miguel@imag:                        */
  21. /* 5) Added new option to print n copies of a same document.        */
  22. /* 6) Cut long filenames if don't fit in the page header.        */
  23. /* Modified by Tim Clark (T.Clark@warwick.ac.uk):            */
  24. /* 7) Two additional modes of printing (portrait and wide format modes)    */
  25. /* 8) Fixed to cope with filenames which contain a character which must    */
  26. /*    be escaped in a PostScript string.                */
  27. /* Modified by miguel@imag.fr to                    */
  28. /* 9) Add new option to suppress heading printing.            */
  29. /* 10) Add new option to suppress page surrounding border printing.    */
  30. /* 11) Add new option to change font size. Number of lines and columns    */
  31. /*     are now automatically adjusted, depending on font size and    */
  32. /*     printing mode used.                        */
  33. /* 12) Minor changes (best layout, usage message, etc).            */
  34. /* Modifid by tml@tik.vtt.fi                        */
  35. /* 13) Simplified *a lot*.  Produce conforming PostScript.        */
  36. /*     Use getopt.                              */
  37. /*                                    */
  38. /************************************************************************/
  39.  
  40. /*
  41.  * Copyright (c) 1988, Miguel Santana, miguel@imag.imag.fr
  42.  *
  43.  * Permission is granted to copy and distribute this file in modified
  44.  * or unmodified form, for noncommercial use, provided (a) this copyright
  45.  * notice is preserved, (b) no attempt is made to restrict redistribution
  46.  * of this file, and (c) this file is not distributed as part of any
  47.  * collection whose redistribution is restricted by a compilation copyright.
  48. */
  49.  
  50.  
  51. #include <stdio.h>
  52. #include <ctype.h>
  53. #include <string.h>
  54. #include <sys/types.h>
  55. #include <time.h>
  56.  
  57. #ifndef    PROLOGUE
  58. #define    PROLOGUE    "./a2ps.prologue"
  59. #endif
  60.  
  61. /* A4 paper size */
  62.  
  63. #define MM_PER_INCH 25.4
  64.  
  65. #ifndef WIDTH
  66. #define    WIDTH    (210/MM_PER_INCH)
  67. #endif
  68.  
  69. #ifndef HEIGHT
  70. #define    HEIGHT    (297/MM_PER_INCH)
  71. #endif
  72.  
  73. #ifndef MARGIN
  74. #define    MARGIN    (15/MM_PER_INCH)
  75. #endif
  76.  
  77. /* The portrait header is tuned to produce 80 columns */
  78. #define    PORTRAIT_HEADER        0.9
  79. #define    LANDSCAPE_HEADER    0.5
  80. #define    POINTS_PER_INCH        72
  81. #define MAXFILENAME        20
  82. #define    FALSE            0
  83. #define    TRUE            1
  84.  
  85. static int fold_line();
  86. static void print_file();
  87. static char cut_line();
  88. static int printchar();
  89. static void skip_page();
  90. static void header_and_prologue();
  91.  
  92. static char *titlestring;
  93. static char dictid[100];
  94.  
  95. static char *curfilename;
  96. static int column = 0;         /* Column number (in current line) */
  97. static int line = 0;         /* Line number (in current page) */
  98. static int first_page;         /* First page for a file */
  99. static int nonprinting_chars, chars; /* Number of nonprinting and total chars */
  100. static int pages = 0;         /* Number of logical pages printed */
  101. static int filepage;         /* Number of page of curent file */
  102. static int linesperpage = 0;     /* Lines per page */
  103. static int columnsperline;     /* Characters per output line */
  104.                  /*  */
  105. static double font_size = 0.0;     /* Size of a char for body font */
  106. static int folding = TRUE;     /* Line folding option */
  107. static int restart = TRUE;     /* Restart page number at each file option */
  108. static int only_printable = FALSE; /* Replace non printable char by space option */
  109. static int interpret = TRUE;     /* Interpret TAB, FF and BS chars option */
  110. static int print_binaries = FALSE; /* Force printing for binary files */ 
  111. static int landscape = FALSE;     /* Otherwise portrait format sheets */
  112. static int no_border = FALSE;     /* TRUE if user don't want the border */
  113. static int no_header = FALSE;     /* TRUE if user don't want the header */
  114. static int column_width = 8;     /* default column tab width (8) */
  115.  
  116. int
  117. main(argc, argv)
  118.      int argc;
  119.      char *argv[];
  120. {
  121.   double char_width, header_size;
  122.   double page_height, page_width;
  123.   int i;
  124.   extern double atof();
  125.   int c;
  126.   extern char *optarg;
  127.   extern int optind;
  128.   
  129.   /* Option processing */
  130.   while ((c = getopt(argc, argv, "bcf:hil:nt:vw")) != EOF)
  131.     switch (c) {
  132.     case 'b':             /* print binary files */
  133.       print_binaries = TRUE;
  134.       break;
  135.     case 'c':             /* cut long lines */
  136.       folding = FALSE;
  137.       break;
  138.     case 'f':             /* change font size */
  139.       if ((font_size = atof(optarg)) == 0.0) {
  140.     fprintf(stderr, "Invalid font size (-f option)\n");
  141.     exit(2);
  142.       }
  143.       break;
  144.     case 'h':
  145.       no_header = TRUE;
  146.       break;
  147.     case 'i':             /* don't interpret ctrl chars */
  148.       interpret = FALSE;
  149.       break;
  150.     case 'l':
  151.       if ((linesperpage = atoi(optarg)) == 0) {
  152.     fprintf(stderr, "Invalid page length (-l option)\n");
  153.     exit(2);
  154.       }
  155.       break;
  156.     case 't':             /* set tab size */
  157.       if ((column_width = atoi(optind)) <= 0) {
  158.     fprintf(stderr, "Invalid tab width (-t option)\n");
  159.     exit(2);
  160.       }
  161.       break;
  162.     case 'v':             /* print control chars */
  163.       only_printable = FALSE;
  164.       break;
  165.     case 'w':             /* landscape format */
  166.       landscape = TRUE;
  167.       break;
  168.       
  169.     default:
  170.     usage:
  171.       fprintf(stderr,"Usage: %s [options] [f1 f2 ... fn]\n", argv[0]);
  172.       fprintf(stderr,"    -f num\t(font size)\n");
  173.       fprintf(stderr,"    -h\t(print this information)\n");
  174.       fprintf(stderr,"    -i\t(don't interpret tab, bs and ff chars)\n");
  175.       fprintf(stderr,"    -n\t(don't number line files)\n");
  176.       fprintf(stderr,"    -p\t(print in portrait mode)\n");
  177.       fprintf(stderr,"    -tn\t(set tab size to n)\n");
  178.       fprintf(stderr,"    -v\t(show non-printing chars in a clear form)\n");
  179.       exit(0);
  180.     }
  181.   
  182.   sprintf(dictid, "a2ps-%d", time((time_t*)0));
  183.  
  184.   page_height = (HEIGHT - 2*MARGIN) * POINTS_PER_INCH;
  185.   page_width = (WIDTH - 2*MARGIN) * POINTS_PER_INCH;
  186.   if (landscape) {
  187.     header_size = no_header ? 0.0 : LANDSCAPE_HEADER * POINTS_PER_INCH;
  188.     if (linesperpage == 0)
  189.       linesperpage = 66;
  190.     if (font_size == 0)
  191.       font_size = ((page_width - header_size) / linesperpage);
  192.     char_width = 0.6 * font_size;
  193.     columnsperline = (page_height / char_width) - 1;
  194.   } else {
  195.     header_size = no_header ? 0.0 : PORTRAIT_HEADER * POINTS_PER_INCH;
  196.     if (linesperpage == 0)
  197.       linesperpage = 66;
  198.     if (font_size == 0)
  199.       font_size = ((page_height - header_size) / linesperpage);
  200.     char_width = 0.6 * font_size;
  201.     columnsperline = (page_width / char_width) - 1;
  202.   }
  203.   if (linesperpage * font_size - 1 /* -1 to compensate for rounding errors */
  204.       > (landscape ? page_width : page_height) - header_size) {
  205.     fprintf(stderr, "Font too big or too many lines per page\n");
  206.     exit(2);
  207.   }
  208.   
  209.   if (optind == argc - 1)
  210.     titlestring = argv[optind];
  211.   else
  212.     titlestring = "a2ps listing";
  213.  
  214.   /* Header printing (postcript prolog) */
  215.   header_and_prologue();
  216.   
  217.   /* Print files designated or standard input */
  218.   if (optind >= argc)
  219.     print_file("");
  220.   else {
  221.     for ( ; optind < argc; optind++) {
  222.       if (freopen(argv[optind], "r", stdin) == NULL)
  223.     fprintf(stderr, "Error opening %s\n", argv[optind]);
  224.       else
  225.     print_file(argv[optind]);
  226.     }
  227.   }
  228.   
  229.   printf("\n\
  230. %%%%Trailer\n\
  231. %%%%Pages: %d\n",
  232.      pages);
  233.   return 0;
  234. }
  235.  
  236. static void
  237. print_file(name)
  238.      char *name;
  239. {
  240.   register int c;
  241.   int start_line, continue_exit;
  242.   int char_width;
  243.   int start_page;
  244.   char new_name[MAXFILENAME+1];
  245.   char *p;
  246.   
  247.   /*
  248.    * Boolean to indicates that previous char is \n (or interpreted \f)
  249.    * and a new page would be started, if more text follows
  250.    */
  251.   start_page = FALSE;
  252.  
  253.   filepage = 0;
  254.  
  255.   /*
  256.    * Printing binary files is not very useful. We stop printing
  257.    * if we detect one of these files. Our heuristic to detect them:
  258.    * if 50% characters of first page are non-printing characters,
  259.    * the file is a binary file.
  260.    * Option -b force binary files impression.
  261.    */
  262.   first_page = TRUE;
  263.   nonprinting_chars = chars = 0;
  264.   
  265.   /*
  266.    * Preprocessing (before printing):
  267.    * - TABs expansion (see interpret option)
  268.    * - FF and BS interpretation
  269.    * - replace non printable characters by a backslash-octal sequence
  270.    * - prefix parents and backslash ['(', ')', '\'] by backslash
  271.    *   (escape character in postcript)
  272.    */
  273.   column = 0;
  274.   line = 0;
  275.   start_line = TRUE;
  276.   
  277.   if (strlen(name) > MAXFILENAME) {
  278.     cut_filename(name, new_name);
  279.     curfilename = name = new_name;
  280.   } else
  281.     curfilename = name;
  282.   
  283.   skip_page();
  284.  
  285.   c = getchar();
  286.   while (c != EOF) {
  287.     /* Form feed */
  288.     if (c == '\f' && interpret) {
  289.       /* Close current line */
  290.       if (!start_line) {
  291.     printf(") s\n");
  292.     start_line = TRUE;
  293.       }
  294.       /* start a new page ? */
  295.       if (start_page)
  296.     skip_page();
  297.       /* Close current page and begin another */
  298.       printf("grestore end showpage\n") ;
  299.       start_page = TRUE;
  300.       /* Verification for binary files */
  301.       if (first_page && is_binaryfile(name))
  302.     return;
  303.       line = 0;
  304.       if ((c = getchar()) == EOF)
  305.     break;
  306.     }
  307.  
  308.     /* Start a new line? */
  309.     if (start_line) {
  310.       if (start_page) {        /* only if there is something to print! */
  311.     skip_page();
  312.     start_page = FALSE ;
  313.       }
  314.       printf("gsave ( ");
  315.       start_line = FALSE;
  316.     }
  317.  
  318.     /* Interpret each character */
  319.     switch (c) {
  320.     case '\b':
  321.       if (!interpret)
  322.     goto print;
  323.       if (column)
  324.     column--;
  325.       printf(") bs (");
  326.       break;
  327.     case '\n':
  328.       column = 0;
  329.       start_line = TRUE;
  330.       printf(") s\n");
  331.       if (++line >= linesperpage) {
  332.     printf("grestore end showpage\n");
  333.     start_page = TRUE ;
  334.     if (first_page && is_binaryfile(name))
  335.       return;
  336.     line = 0;
  337.       }
  338.       break;
  339.     case '\t':
  340.       if (interpret) {
  341.     continue_exit = FALSE;
  342.     do {
  343.       if (++column >= columnsperline)
  344.         if (folding) {
  345.           if (fold_line(name) == FALSE)
  346.         return;
  347.         } else {
  348.           c = cut_line();
  349.           continue_exit = TRUE;
  350.           break;
  351.         }
  352.       putchar(' ');
  353.     } while (column % column_width);
  354.     if (continue_exit)
  355.       continue;
  356.     break;
  357.       }
  358.     default:
  359.     print:
  360.       if (only_printable)
  361.     char_width = 1;
  362.       else
  363.     char_width = (c < ' ' || c >= 0177) ? 4 : 1;
  364.       if ((column += char_width) >= columnsperline)
  365.     if (folding) {
  366.       if (fold_line(name) == FALSE)
  367.         return;
  368.     } else {
  369.       c = cut_line();
  370.       continue;
  371.     }
  372.       nonprinting_chars += printchar(c);
  373.       chars++;
  374.       break;
  375.     }
  376.     c = getchar();
  377.   }
  378.  
  379.   if (!start_line)
  380.     printf(") s\n");
  381.   if (!start_page)
  382.     printf("grestore end showpage\n");
  383. }
  384.  
  385. /*
  386.  * Cut long filenames.
  387.  */
  388. static int
  389. cut_filename(old_name, new_name)
  390.      char *old_name, *new_name;
  391. {
  392.   register char *p;
  393.   register int i;
  394.   
  395.   p = strrchr(old_name, '/');
  396.   if (!p)
  397.     p = old_name;
  398.  
  399.   for (i = 0, p++; *p != NULL && i < MAXFILENAME; i++)
  400.     *new_name++ = *p++;
  401.   *new_name = NULL;
  402. }
  403.  
  404. /*
  405.  * Fold a line too long.
  406.  */
  407. static int
  408. fold_line(name)
  409.      char *name;
  410. {
  411.   column = 0;
  412.   printf(") s\n");
  413.   if (++line >= linesperpage) {
  414.     printf("grestore end showpage\n");
  415.     if (first_page && is_binaryfile(name))
  416.       return FALSE;
  417.     skip_page();
  418.     line = 0;
  419.   }
  420.   printf("gsave ( ");
  421.  
  422.   return TRUE;
  423. }
  424.  
  425. /*
  426.  * Cut a textline too long to the size of a page line.
  427.  */
  428. static char
  429. cut_line()
  430. {
  431.   char c;
  432.  
  433.   while ((c = getchar()) != EOF && c != '\n' && c != '\f');
  434.   return c;
  435. }
  436.  
  437. /*
  438.  * Print a char in a form accept by postscript printers.
  439.  */
  440. static int
  441. printchar(c)
  442.      unsigned char c;
  443. {
  444.   if (c >= ' ' && c < 0177) {
  445.     if (c == '(' || c == ')' || c == '\\')
  446.       putchar('\\');
  447.     putchar(c);
  448.     return 0;
  449.   }
  450.   
  451.   if (only_printable) {
  452.     putchar(' ');
  453.     return 1;
  454.   }
  455.  
  456.   printf("\\134%o", c);
  457.   return 1;
  458. }
  459.  
  460. /*
  461.  * Begins a new physical page.
  462.  */
  463. static void
  464. skip_page()
  465. {
  466.   char *p;
  467.  
  468.   pages++;
  469.   filepage++;
  470.   printf("\
  471. %%%%Page: %d %d\n\
  472. %s begin\n\
  473. /pagenum %d def\n",
  474.      pages, pages, dictid, filepage);
  475.   putchar('(');
  476.   for (p = curfilename; *p;)
  477.     printchar(*p++);
  478.   printf(") setfilename gsave startpage\n");
  479. }
  480.  
  481. /*
  482.  * Test if we have a binary file.
  483.  */
  484. static int
  485. is_binaryfile(name)
  486. char *name;
  487. {
  488.   first_page = FALSE;
  489.   if (!print_binaries && (nonprinting_chars*100 / chars) >= 75) {
  490.     fprintf(stderr, "%s is a binary file: printing aborted\n", name);
  491.     return TRUE;
  492.   }
  493.   return FALSE;
  494. }
  495.  
  496. static void
  497. header_and_prologue()
  498. {
  499.   register int c;
  500.   FILE *prologue;
  501.   char *datestring;
  502.   time_t date;
  503.   
  504.   if ((prologue = fopen(PROLOGUE, "r")) == NULL) {
  505.     fprintf(stderr, "Poscript header missing\n");
  506.     exit(1);
  507.   }
  508.  
  509.   /* Retrieve date and hour */
  510.   if (time(&date) == -1) {
  511.     perror("time:");
  512.     exit(1);
  513.   }
  514.   datestring = ctime(&date);
  515.  
  516.    /* Conforming header and prologue */
  517.   printf("\
  518. %%!PS-Adobe-2.0\n\
  519. %%%%Title: %s\n\
  520. %%%%Creator: a2ps 4.0(tml)\n\
  521. %%%%CreationDate: %s\
  522. %%%%Pages: (atend)\n\
  523. %%%%BoundingBox: %d %d %d %d\n\
  524. %%%%DocumentFonts: Helvetica-Bold Helvetica Courier\n\
  525. %%%%EndComments\n\
  526. /%s 100 dict def\n\
  527. %% Initialize page description variables.\n\
  528. %s begin\n\
  529. /inch {72 mul} bind def\n\
  530. /landscape %s def\n\
  531. /sheetheight %g inch def\n\
  532. /sheetwidth %g inch def\n\
  533. /margin %g inch def\n\
  534. /noborder %s def\n",
  535.      titlestring,
  536.      datestring,
  537.      (int) (MARGIN*72), (int) (MARGIN*72),
  538.      (int) ((WIDTH-MARGIN)*72), (int) ((HEIGHT-MARGIN)*72),
  539.      dictid,
  540.      dictid,
  541.      landscape ? "true" : "false",
  542.      HEIGHT, WIDTH, MARGIN,
  543.      no_border ? "true" : "false");
  544.   if (no_header)
  545.     printf("\
  546. /noheader true def\n\
  547. /headersize 0.0 def\n");
  548.   else
  549.     printf("\
  550. /noheader false def\n\
  551. /headersize %g inch def\n", landscape ? LANDSCAPE_HEADER : PORTRAIT_HEADER);
  552.   printf("\
  553. /bodyfontsize %g def\n\
  554. /lines %d def\n\
  555. /columns %d def\n\
  556. /date (%.6s %.4s %.8s) def\n",
  557.        font_size,
  558.        linesperpage,
  559.        columnsperline,
  560.        datestring+4, datestring+20, datestring+11);
  561.   
  562.   /* Prologue */
  563.   printf("%%%%BeginFile: %s\n", PROLOGUE);
  564.   while ((c = getc(prologue)) != EOF)
  565.     putchar(c);
  566.   fclose(prologue);
  567.   printf("%%%%EndFile\n");
  568.  
  569.   printf("end\n%%%%EndProlog\n\n");
  570. }
  571.