/************************************************************************/ /* */ /* Description: Ascii to PostScript printer program. */ /* File: imag:/users/local/a2ps/a2ps.c */ /* Created: Mon Nov 28 15:22:15 1988 by miguel@imag (Miguel Santana) */ /* Version: 2.0 */ /* */ /* Edit history: */ /* 1) Derived of shell program written by evan@csli (Evan Kirshenbaum). */ /* Written in C for improve speed execution and portability. Many */ /* improvements have been added. */ /* Fixes by Oscar Nierstrasz @ cui.uucp: */ /* 2) Fixed incorrect handling of stdin (removed error if no file names)*/ /* 3) Added start_page variable to eliminate blank pages printed for */ /* files that are exactly multiples of 132 lines (e.g., man pages) */ /* Modified by miguel@imag: */ /* 4) Added new options at installation : sheet format (height/width in */ /* inches), page format (number of columns per line and of lines per */ /* page). */ /* Modified by miguel@imag: */ /* 5) Added new option to print n copies of a same document. */ /* 6) Cut long filenames if don't fit in the page header. */ /* Modified by Tim Clark (T.Clark@warwick.ac.uk): */ /* 7) Two additional modes of printing (portrait and wide format modes) */ /* 8) Fixed to cope with filenames which contain a character which must */ /* be escaped in a PostScript string. */ /* Modified by miguel@imag.fr to */ /* 9) Add new option to suppress heading printing. */ /* 10) Add new option to suppress page surrounding border printing. */ /* 11) Add new option to change font size. Number of lines and columns */ /* are now automatically adjusted, depending on font size and */ /* printing mode used. */ /* 12) Minor changes (best layout, usage message, etc). */ /* */ /************************************************************************/ /* * Copyright (c) 1988, Miguel Santana, miguel@imag.imag.fr * * Permission is granted to copy and distribute this file in modified * or unmodified form, for noncommercial use, provided (a) this copyright * notice is preserved, (b) no attempt is made to restrict redistribution * of this file, and (c) this file is not distributed as part of any * collection whose redistribution is restricted by a compilation copyright. */ #define ANSIC #define HEADER_PS "\\tools\\data\\header.ps" #include #include #ifdef ANSIC #include #include #else #ifdef BSD #include #else #ifdef SYSV #include #include #include #else error ! #endif #endif #endif #ifndef HEADER_PS #define HEADER_PS "./header.ps" #endif #ifndef WIDTH #define WIDTH 8.27 #endif #ifndef HEIGHT #define HEIGHT 11.00 #endif #ifndef MARGIN #define MARGIN 1.2 #endif #ifndef DIR_SEP #define DIR_SEP '/' #endif #define PORTRAIT_HEADER 0.29 #define LANDSCAPE_HEADER 0.22 #define PIXELS_INCH 72 #define MAXFILENAME 20 #define FALSE 0 #define TRUE 1 int fold_line(); void print_file(); char cut_line(); int printchar(); void skip_page(); int column = 0; /* Column number (in current line) */ int line = 0; /* Line number (in current page) */ int line_number = 0; /* Source line number */ int first_page; /* First page for a file */ int nonprinting_chars, chars; /* Number of nonprinting and total chars */ int prefix_width; /* Width in characters for line prefix */ int pages = 0; /* Number of logical pages printed */ int sheets = 0; /* Number of physical pages printed */ int linesperpage; /* Lines per page */ int columnsperline; /* Characters per output line */ double font_size = 0.0; /* Size of a char for body font */ int numbering = TRUE; /* Line numbering option */ int folding = TRUE; /* Line folding option */ int restart = TRUE; /* Restart page number at each file option */ int only_printable = FALSE; /* Replace non printable char by space option */ int interpret = TRUE; /* Interpret TAB, FF and BS chars option */ int print_binaries = FALSE; /* Force printing for binary files */ int copies_number = 1; /* Number of copies to print */ int landscape = TRUE; /* Otherwise portrait format sheets */ int wide_pages = FALSE; /* TRUE implies landscape, not twinpage */ int twinpage = TRUE; /* 2 pages per sheet if true, 1 otherwise */ int no_border = FALSE; /* TRUE if user don't want the border */ int no_header = FALSE; /* TRUE if user don't want the header */ int column_width = 8; /* default column tab width (8) */ main(argc, argv) int argc; char *argv[]; { register int narg; register char *arg; double char_width, header_size; double page_height, page_width; int i; extern double atof(); /* Option processing */ arg = argv[narg = 1]; while (narg < argc && arg[0] == '-') { switch (arg[1]) { case '?': /* help */ goto usage; case '#': /* n copies */ copies_number = 0; arg += 2; while (*arg != NULL && isdigit(*arg)) copies_number = copies_number*10 + (*arg++ - '0'); if (*arg != NULL || copies_number <= 0) goto usage; break; case 'b': /* print binary files */ if (arg[2] != NULL) goto usage; print_binaries = TRUE; break; case 'f': /* change font size */ if (arg[2] == NULL) { folding = TRUE; break; } if ((font_size = atof(&arg[2])) == 0.0) { fprintf(stderr, "Wrong value for option -s\n"); exit(1); } break; case 'h': /* help */ goto usage; case 'i': /* interpret control chars */ if (arg[2] != NULL) goto usage; interpret = TRUE; break; case 'n': /* number file lines */ if (arg[2] == NULL) { numbering = TRUE; break; } if (arg[3] != NULL) goto usage; switch (arg[2]) { case 'b': /* don't print binaries */ print_binaries = FALSE; break; case 'f': /* cut lines too long */ folding = FALSE; break; case 'h': /* don't print header */ no_header = TRUE; break; case 'i': /* don't interpret ctrl chars */ interpret = FALSE; break; case 'n': /* don't number lines */ numbering = FALSE; break; case 'p': /* landscape format */ landscape = TRUE; break; case 'r': /* don't restart sheet number */ restart = FALSE; break; case 's': /* no surrounding border */ no_border = TRUE; break; case 'v': /* only printable chars */ only_printable = TRUE; break; case 'w': /* twin pages */ wide_pages = FALSE; break; default: goto usage; } break; case 'p': /* portrait format */ if (arg[2] != NULL) goto usage; if (wide_pages) { fprintf(stderr, "a2ps: options -p and -w are incompatible\n"); exit(1); } landscape = FALSE; break; case 'r': /* restart sheet number */ if (arg[2] != NULL) goto usage; restart = TRUE; break; case 't': /* set tab size */ if (arg[2] == NULL || (column_width = atoi(arg+2)) <= 0) goto usage; break; case 'v': /* print control chars */ if (arg[2] != NULL) goto usage; only_printable = FALSE; break; case 'w': /* wide format */ if (arg[2] != NULL) goto usage; if (!landscape) { fprintf(stderr, "a2ps: options -p and -w are incompatible\n"); exit(1); } wide_pages = TRUE; break; default: usage: fprintf(stderr,"Usage: %s [options] [f1 f2 ... fn]\n", argv[0]); fprintf(stderr,"options = -#num\t(number of copies to print)\n"); fprintf(stderr," -?\t(print this information)\n"); fprintf(stderr," -f\t(fold lines too large)\n"); fprintf(stderr," -fnum\t(font size, num is a float number)\n"); fprintf(stderr," -h\t(print this information)\n"); fprintf(stderr," -i\t(interpret tab, bs and ff chars)\n"); fprintf(stderr," -n\t(number line files)\n"); fprintf(stderr," -p\t(print in portrait mode)\n"); fprintf(stderr," -r\t(restart page number after each file)\n"); fprintf(stderr," -tn\t(set tab size to n)\n"); fprintf(stderr," -v\t(show non-printing chars in a clear form)\n"); fprintf(stderr," -w\t(print in wide format)\n"); fprintf(stderr," -nb\t(don't force printing binary files)\n"); fprintf(stderr," -nf\t(don't fold lines)\n"); fprintf(stderr," -nh\t(don't print the header)\n"); fprintf(stderr," -ni\t(don't interpret special chars)\n"); fprintf(stderr," -nn\t(don't number output lines)\n"); fprintf(stderr," -np\t(don't print in portrait format)\n"); fprintf(stderr," -nr\t(don't restart page number)\n"); fprintf(stderr," -ns\t(don't print surrounding borders)\n"); fprintf(stderr," -nv\t(replace non-printing chars by space)\n"); fprintf(stderr," -nw\t(don't print in wide format)\n"); exit(1); } arg = argv[++narg]; } if (arg != NULL && strcmp(arg, "?") == 0) goto usage; twinpage = landscape && !wide_pages; if (font_size == 0.0) font_size = landscape ? 6.8 : 9.0; page_height = (HEIGHT - MARGIN) * PIXELS_INCH; page_width = (WIDTH - MARGIN) * PIXELS_INCH; char_width = 0.6 * font_size; if (landscape) { header_size = no_header ? 0.0 : LANDSCAPE_HEADER * PIXELS_INCH; linesperpage = ((page_width - header_size) / font_size) - 1; if (wide_pages) columnsperline = (page_height / char_width) - 1; else columnsperline = ((page_height / 2) / char_width) - 1; } else { header_size = no_header ? 0.0 : PORTRAIT_HEADER * PIXELS_INCH; linesperpage = ((page_height - header_size) / font_size) - 1; columnsperline = (page_width / char_width) - 1; } if (linesperpage <= 0 || columnsperline <= 0) { fprintf(stderr, "Font %g too big !!\n", font_size); exit(1); } /* Header printing (postcript prolog) */ print_header(); /* Print files designated or standard input */ prefix_width = numbering ? 6 : 1; if (narg >= argc) print_file("stdin"); else { while (narg < argc) { if (freopen(arg, "r", stdin) == NULL) { fprintf(stderr, "Error opening %s\n", arg); printf("\n%%%%Trailer\ncleanup\ndocsave restore end\n"); exit(1); } print_file(arg); arg = argv[++narg]; } } printf("\n%%%%Trailer\ncleanup\ndocsave restore end\n"); } void print_file(name) char *name; { register int c; int start_line, continue_exit; int char_width; int start_page; char new_name[MAXFILENAME+1]; char *p; /* * Boolean to indicates that previous char is \n (or interpreted \f) * and a new page would be started, if more text follows */ start_page = FALSE; /* * Printing binary files is not very useful. We stop printing * if we detect one of these files. Our heuristic to detect them: * if 50% characters of first page are non-printing characters, * the file is a binary file. * Option -b force binary files impression. */ first_page = TRUE; nonprinting_chars = chars = 0; /* * Preprocessing (before printing): * - TABs expansion (see interpret option) * - FF and BS interpretation * - replace non printable characters by a space or a char sequence * like: * ^X for ascii codes < 0x20 (X = [@, A, B, ...]) * ^? for del char * M-c for ascii codes > 0x3f * - prefix parents and backslash ['(', ')', '\'] by backslash * (escape character in postcript) */ column = 0; line = line_number = 0; start_line = TRUE; if (strlen(name) > MAXFILENAME) { cut_filename(name, new_name); name = new_name; } putchar('('); for (p = name; *p != NULL;) printchar(*p++); printf(") newfile\n"); if (restart) printf("/sheet 1 def\n"); pages = 0; skip_page(); c = getchar(); while (c != EOF) { /* Form feed */ if (c == '\f' && interpret) { /* Close current line */ if (!start_line) { printf(") s\n"); start_line = TRUE; } /* start a new page ? */ if (start_page) skip_page(); /* Close current page and begin another */ printf("endpage\n") ; start_page = TRUE; /* Verification for binary files */ if (first_page && is_binaryfile(name)) return; line = 0; if ((c = getchar()) == EOF) break; } /* Start a new line? */ if (start_line) { if (start_page) { /* only if there is something to print! */ skip_page(); start_page = FALSE ; } if (numbering) printf("(%-5d ", ++line_number); else printf("( "); start_line = FALSE; } /* Interpret each character */ switch (c) { case '\b': if (!interpret) goto print; if (column) column--; putchar(c); break; case '\n': column = 0; start_line = TRUE; printf(") s\n"); if (++line >= linesperpage) { printf("endpage\n"); start_page = TRUE ; if (first_page && is_binaryfile(name)) return; line = 0; } break; case '\t': if (interpret) { continue_exit = FALSE; do { if (++column + prefix_width > columnsperline) if (folding) { if (fold_line(name) == FALSE) return; } else { c = cut_line(); continue_exit = TRUE; break; } putchar(' '); } while (column % column_width); if (continue_exit) continue; break; } default: print: if (only_printable) char_width = 1; else { char_width = c > 0177 ? 2 : 0; char_width += c < ' ' || c == 0177 ? 2 : 1; } if (prefix_width + (column += char_width) > columnsperline) if (folding) { if (fold_line(name) == FALSE) return; } else { c = cut_line(); continue; } nonprinting_chars += printchar(c); chars++; break; } c = getchar(); } if (!start_line) printf(") s\n"); if (!start_page) printf("endpage\n"); } /* * Cut long filenames. */ int cut_filename(old_name, new_name) char *old_name, *new_name; { register char *p; register int i; p = old_name + (strlen(old_name)-1); while (p >= old_name && *p != DIR_SEP) p--; for (i = 0, p++; *p != NULL && i < MAXFILENAME; i++) *new_name++ = *p++; *new_name = NULL; } /* * Fold a line too long. */ int fold_line(name) char *name; { column = 0; printf(") s\n"); if (++line >= linesperpage) { printf("endpage\n"); skip_page(); if (first_page && is_binaryfile(name)) return FALSE; line = 0; } if (numbering) printf("( "); else printf("( "); return TRUE; } /* * Cut a textline too long to the size of a page line. */ char cut_line() { char c; while ((c = getchar()) != EOF && c != '\n' && c != '\f'); return c; } /* * Print a char in a form accept by postscript printers. */ int printchar(c) unsigned char c; { if (c >= ' ' && c < 0177) { if (c == '(' || c == ')' || c == '\\') putchar('\\'); putchar(c); return 0; } if (only_printable) { putchar(' '); return 1; } if (c > 0177) { printf("M-"); c &= 0177; } if (c < ' ') { putchar('^'); if ((c = c + '@') == '(' || c == ')' || c == '\\') putchar('\\'); putchar(c); } else if (c == 0177) printf("^?"); else putchar(c); return 1; } /* * Begins a new physical page. */ void skip_page() { pages++; if (twinpage == FALSE || (pages & 0x1)) { sheets++; printf("%%%%Page: %d %d\n", sheets, sheets); } printf("startpage\n"); } /* * Test if we have a binary file. */ is_binaryfile(name) char *name; { first_page = FALSE; if (!print_binaries && (nonprinting_chars*100 / chars) >= 75) { fprintf(stderr, "%s is a binary file: printing aborted\n", name); return TRUE; } return FALSE; } print_header() { register int c; FILE *f; char *string; #ifdef ANSIC time_t date; #else #ifdef BSD struct timeval date; struct tm *p; #else #ifdef SYSV struct timeb date; #endif #endif #endif if ((f = fopen(HEADER_PS, "r")) == NULL) { fprintf(stderr, "Poscript header missing\n"); exit(1); } /* Initialize some postcript variables */ printf("%%! a2ps 3.0\n\n"); printf("/$a2psdict 100 dict def\n"); printf("$a2psdict begin\n"); printf("%% Initialize page description variables.\n"); printf("/inch {72 mul} bind def\n"); printf("/landscape %s def\n", landscape ? "true" : "false"); printf("/twinpage %s def\n", twinpage ? "true" : "false"); printf("/sheetheight %g inch def\n", HEIGHT); printf("/sheetwidth %g inch def\n", WIDTH); printf("/margin %g inch def\n", MARGIN); printf("/noborder %s def\n", no_border ? "true" : "false"); if (no_header) { printf("/noheader true def\n"); printf("/headersize 0.0 def\n"); } else { printf("/noheader false def\n"); printf("/headersize %g inch def\n", landscape ? LANDSCAPE_HEADER : PORTRAIT_HEADER); } printf("/bodyfontsize %g def\n", font_size); printf("/lines %d def\n", linesperpage); printf("/columns %d def\n", columnsperline); /* Retrieve date and hour */ #ifdef ANSIC if (time(&date) == -1) { fprintf(stderr, "Error calculing time\n"); exit(1); } string = ctime(&date); /* and print them */ printf("/date (%.6s %.4s %.8s) def\n", string+4, string+20, string+11); #else #ifdef BSD (void) gettimeofday(&date, (struct timezone *)0); p = localtime(&date.tv_sec); string = asctime(p); /* and print them */ printf("/date (%.6s %.4s %.8s) def\n", string+4, string+20, string+11); #else #ifdef SYSV (void)ftime(&date); string = ctime(&date.time); printf("/date (%.6s %.4s %.8s) def\n", string+4, string+20, string+11); #endif #endif #endif /* Header file printing */ while ((c = getc(f)) != EOF) putchar(c); /* Close prolog */ printf("%%%%EndProlog\n\n"); /* Ask for printing n copies */ if (copies_number > 1) printf("/#copies %d def\n", copies_number); /* Go on */ printf("/docsave save def\n"); printf("startdoc\n"); }