/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* lwlp - Convert ASCII text to PostScript
*
* Usage:
* lwlp [-{2|4|8}] [-p] [-L] [-r] [-n#] [-l#|-w#] [-c#] [-t#]
* [-hstring] [-Bstring] [-Istring] [-Xstring] [-Pfile] [file ...]
*
* Options:
* -{1|2|4|8} print multiple logical pages per page
* -d debug, don't remove temporary file
* -L specify Landscape instead of Portrait
* -p filter input through pr
* -r toggle page reversal flag (default is off)
* -e elide unchanged functions
* -n# number with numberwidth digits
* -l# specify number of lines/logical page, default 66
* -w# specify number of columns
* -c# specify number of copies
* -t# specify tab spacing
* -htext specify header text
* -Btext specify bold font selector
* -Itext specify italic font selector
* -Xtext specify bold-italic font selector
* -Gtext specify graying selector
* -Pfile specify different Postscript prologue file
*
* If no files are specified, stdin is used.
* Form feeds handled
* Backspacing with underlining (or overprinting) works
* The output conforms to Adobe 2.0
*
* Problems:
* - assumes fixed-width (non-proportional) font in some places
* - can't back up (using backspaces) over tabs
* - assumes 8.5 x 11.0 paper
* - uses logical page with aspect ratio of 3 * 4
*
*/
#define USAGE1 "[-{1|2|4|8}] [-p] [-L] [-r] [-n<numberwidth]"
#define USAGE2 "[-l<lines>|-w<columns>] [-c<count>] [-t<tabs>]"
#define USAGE3 "[-hstring] [-Bstring] [-Istring] [-Xstring] [-Gstring]"
#define USAGE4 "[-Pfile] [file ...]"
#define USAGE6 "[-hstring] [-e] [-y comment] oldfile newfile"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/file.h>
#include <ctype.h>
#include <pwd.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <time.h>
#include <stdarg.h>
/*
* Configurable...
* BUFOUT should be fairly large
*/
#define BUFIN 1024 /* maximum length of an input line */
#define BUFOUT (BUFIN * 5)
#define MAXPAGES 10000
#define REVERSE_OFF 0
#define DEFAULT_PAPER_HEIGHT 11.0
#define DEFAULT_PAPER_WIDTH 8.50
#define DEFAULT_PAGE_HEIGHT 10.0
#define DEFAULT_PAGE_WIDTH 7.50
#define DEFAULT_LINES_PER_PAGE 66
#define DEFAULT_TAB_SIZE 8
static char *default_font = "Courier";
static char *default_font_bold = "Courier-Bold";
static char *default_font_italic = "Courier-Oblique";
static char *default_font_bold_italic = "Courier-BoldOblique";
static char *select_default_font = "FRN";
static char *select_default_font_bold = "FRB";
static char *select_default_font_italic = "FIN";
static char *select_default_font_bold_italic = "FIB";
#define DEFAULT_FONT select_default_font
#define DEFAULT_FONT_BOLD select_default_font_bold
#define DEFAULT_FONT_ITALIC select_default_font_italic
#define DEFAULT_FONT_BOLD_ITALIC select_default_font_bold_italic
#define DEFAULT_CHAR_WIDTH (.6)
#define DEFAULT_SPACES_AFTER_NUMBER 1
#define DEFAULT_DESCENDER_FRACTION 0.3
#define LWLP "lwlp"
#define CODEREVIEW "codereview"
#define END_C_FUNCTION '}'
#define END_ASM_FUNCTION "SET_SIZE("
static char *banner =
"**********************************************************";
/*
* PostScript command strings
*/
#define LINETO "lineto"
#define NEWPATH "newpath"
#define SETLINEWIDTH "setlinewidth"
#define STROKE "stroke"
/*
* PostScript command strings defined in the prologue file
*/
#define BACKSPACE "B"
#define MOVETO "M" /* x y */
#define SHOW "S" /* string */
#define TAB "T" /* spaces */
#define ZEROMOVETO "Z" /* y */
#define SELECT_FONT "SFT" /* size font */
#define SET_WIDTHS "SWT"
#define START_PAGE "SPG" /* angle scale x y */
#define END_PAGE "EPG"
#define FLUSH_PAGE "FPG" /* ncopies */
#define SHADE "SHD" /* x0 y0 x1 y1 */
/*
* Conformance requires that no PostScript line exceed 256 characters
*/
#define POINTS_PER_INCH 72
#define MAX_OUTPUT_LINE_LENGTH 256
#define START_X 0 /* position of start of each line */
#define THREE_HOLE_X 1.0 /* portrait x offset (inches) 3 hole */
#define THREE_HOLE_Y 0.5 /* landscape y offset (inches) 3 hole */
#define RULE_WIDTH 0.25 /* width in units of paging rules */
static struct print_state {
int page_count;
int logical_page_count;
int lineno;
long offset;
float row;
char *font;
} current, saved;
struct format_state {
int numberwidth, linenumber, altlinenumber;
int makegray;
char *font;
};
static int change_seen, dots_inserted, in_change, old_stuff, makegray;
static int lines_per_page;
static int columns;
static float point_size;
static int start_x, start_y, end_x;
static int landscape, rot_text;
static int ncopies;
static int tabstop;
static int reverse;
static int elide;
static int usetmp;
static int dflag, lflag, pflag, vflag, wflag;
static int numberwidth, linenumber, altlinenumber;
static int boldlength, itlclength, bitclength, graylength;
static char *boldstring, *itlcstring, *bitcstring, *graystring;
#define HEADER_EXPLICIT 1
#define HEADER_IMPLICIT 2
static int header = HEADER_IMPLICIT;
static char *headerstring;
static char *bannerfile;
static char bufin[BUFIN]; /* input buffer */
static char bufout[BUFOUT]; /* output buffer */
static long *page_map; /* offset of first byte of each page */
static char *username, *hostname, *currentdate;
static char *comment;
static void preamble(void);
static void postamble(void);
static void setcurrentfont(char *, FILE *);
static void savestate(FILE *);
static void restorestate(FILE *);
static void save_format_state(struct format_state *);
static void printfile(FILE *);
static int printpage(FILE *, FILE *);
static int startpage(FILE *);
static void endpage(FILE *);
static void copypage(FILE *, long, long);
static void process_elide(FILE *);
static void setheaderfile(char *);
static void restore_format_state(struct format_state *, FILE *);
static void flushpage(FILE *);
static void setuppage(FILE *);
static void reversepages(FILE *);
static void proc(char *, FILE *);
static void setup(void);
static int printbanner(char *, FILE *);
static char *fgetline(char *, int, FILE *);
static void fatal(char *fmt, ...);
static char *prologue;
static char *progname;
static int iscodereview;
static char *default_prologue[] = {
"%%EndComments\n",
"%\n",
"% PostScript Prologue for lwlp LaserWriter Line Printer\n",
"%\n",
"/SFT {findfont exch scalefont setfont}bind def\n",
"/SWT {( ) stringwidth pop dup /W exch def neg /NW exch def}bind def\n",
"/SPG {/SV save def translate dup scale rotate}bind def\n",
"/EPG {SV restore}bind def\n",
"/FPG {/#copies exch def showpage}bind def\n",
"/B {NW 0 rmoveto}def\n",
"/M /moveto load def\n",
"/T {W mul 0 rmoveto}def\n",
"/S /show load def\n",
"/Z {0 exch moveto}bind def\n",
"/SHD {save 5 1 roll % S x1 y1 x0 y0\n",
" 2 copy moveto % S x1 y1 x0 y0\n",
" 3 index exch lineto % S x1 y1 x0\n",
" 3 -1 roll 2 index lineto % S y1 x0\n",
" exch lineto % S\n",
" 0.95 setgray fill % S\n",
" restore}def\n",
"%%EndProlog\n",
NULL
};
struct layout {
float scale;
int pages, page_rows, page_cols;
int rotation;
};
static struct layout *layoutp;
static struct layout layout1 = { 1.000000, 1, 1, 1, 0 };
static struct layout layout2 = { 0.666666, 2, 2, 1, 90 };
static struct layout layout4 = { 0.500000, 4, 2, 2, 0 };
static struct layout layout8 = { 0.333333, 8, 4, 2, 90 };
static int box_width, box_height;
static int gap_width, gap_height;
static int margin_x, margin_y;
static struct position {
int base_x;
int base_y;
} positions[8];
int
main(int argc, char **argv)
{
int ch, i, j, first_file;
char *pc;
FILE *infile;
if ((pc = strrchr(argv[0], '/')) != NULL)
progname = pc + 1;
else
progname = argv[0];
lines_per_page = DEFAULT_LINES_PER_PAGE;
layoutp = &layout1;
tabstop = DEFAULT_TAB_SIZE;
current.page_count = 0;
ncopies = 1;
reverse = REVERSE_OFF;
/*LINTED*/
if (iscodereview = strncmp(progname, CODEREVIEW,
sizeof (CODEREVIEW) - 1) == 0) {
layoutp = &layout2;
numberwidth = 4;
columns = 85; /* extra space for numbering */
wflag = -1;
}
while ((ch = getopt(argc, argv,
"1248B:c:deG:h:I:l:Ln:P:prt:vw:X:y:")) != -1) {
switch (ch) {
case '1':
layoutp = &layout1;
break;
case '2':
layoutp = &layout2;
break;
case '4':
layoutp = &layout4;
break;
case '8':
layoutp = &layout8;
break;
case 'B':
boldlength = strlen(optarg);
boldstring = malloc((size_t)(boldlength + 1));
(void) strcpy(boldstring, optarg);
break;
case 'c':
ncopies = atof(optarg);
if (ncopies <= 0) {
fatal("number of copies must be > 0");
/*NOTREACHED*/
}
break;
case 'd':
dflag = 1;
break;
case 'e':
elide = 1;
break;
case 'G':
graylength = strlen(optarg);
graystring = malloc((size_t)(graylength + 1));
(void) strcpy(graystring, optarg);
break;
case 'h':
header = HEADER_EXPLICIT;
i = strlen(optarg);
headerstring = malloc((size_t)(i + 1));
(void) strcpy(headerstring, optarg);
if (strcmp(headerstring, "-") == 0)
header = HEADER_IMPLICIT;
break;
case 'I':
itlclength = strlen(optarg);
itlcstring = malloc((size_t)(itlclength + 1));
(void) strcpy(itlcstring, optarg);
break;
case 'l':
lines_per_page = atoi(optarg);
if (lines_per_page < 1) {
fatal("invalid number of lines/page");
/*NOTREACHED*/
}
lflag = 1;
if (wflag > 0) {
fatal("can't have both -l and -w");
/*NOTREACHED*/
}
wflag = 0;
break;
case 'L':
landscape = 1;
break;
case 'm':
break;
case 'n':
numberwidth = atoi(optarg);
if (numberwidth < 2) {
fatal("invalid numbering width");
/*NOTREACHED*/
}
break;
case 'P':
prologue = optarg;
break;
case 'p':
pflag = 1;
break;
case 'r':
reverse = !reverse;
break;
case 't':
tabstop = atoi(optarg);
if (tabstop < 1) {
fatal("negative tabstop");
/*NOTREACHED*/
}
break;
case 'v':
vflag = 1;
break;
case 'w':
columns = atoi(optarg);
if (columns < 1) {
fatal("invalid number of columns");
/*NOTREACHED*/
}
wflag = 1;
if (lflag) {
fatal("can't have both -l and -w");
/*NOTREACHED*/
}
break;
case 'X':
bitclength = strlen(optarg);
bitcstring = malloc((size_t)(bitclength + 1));
(void) strcpy(bitcstring, optarg);
break;
case 'y':
comment = optarg;
break;
default:
(void) fprintf(stderr,
"usage: %s %s\n\t%s\n\t%s\n\t%s\n",
iscodereview ? LWLP : progname,
USAGE1, USAGE2, USAGE3, USAGE4);
if (iscodereview)
(void) fprintf(stderr, "\t%s [%s flags] %s\n",
CODEREVIEW, LWLP, USAGE6);
exit(1);
}
}
if (elide && !iscodereview) {
fatal("-e option valid only with codereview");
/*NOTREACHED*/
}
usetmp = reverse || elide;
/* allocate page_map if we need one */
if (reverse) {
page_map = malloc((size_t)(MAXPAGES * sizeof (long *)));
if (page_map == NULL) {
fatal("unable to allocate memory for page reversal");
/*NOTREACHED*/
}
}
/*
* Check that all files are readable
* This is so that no output at all is produced if any file is not
* readable in case the output is being piped to a printer
*/
first_file = optind;
for (j = first_file; j < argc; j++) {
if (access(argv[j], R_OK) == -1 && !(iscodereview &&
strcmp(argv[j], "-") == 0)) {
fatal("cannot access %s", argv[j]);
/*NOTREACHED*/
}
}
if (iscodereview && (first_file + 2) != argc) {
fatal("codereview: need old and new file");
/*NOTREACHED*/
}
/* compute logical point size, logical dimensions */
if (!landscape) {
rot_text = layoutp->rotation;
start_y = DEFAULT_PAGE_HEIGHT * POINTS_PER_INCH;
start_x = START_X;
end_x = DEFAULT_PAGE_WIDTH * POINTS_PER_INCH;
if (wflag) {
point_size = DEFAULT_PAGE_WIDTH * POINTS_PER_INCH /
((columns + 0.5) * DEFAULT_CHAR_WIDTH);
lines_per_page = DEFAULT_PAGE_HEIGHT * POINTS_PER_INCH /
point_size;
} else {
point_size = DEFAULT_PAGE_HEIGHT * POINTS_PER_INCH /
(lines_per_page + 0.5);
columns = DEFAULT_PAGE_WIDTH * POINTS_PER_INCH /
(point_size * DEFAULT_CHAR_WIDTH);
}
} else {
rot_text = 90 - layoutp->rotation;
start_y = DEFAULT_PAGE_WIDTH * POINTS_PER_INCH;
start_x = START_X;
end_x = DEFAULT_PAGE_HEIGHT * POINTS_PER_INCH;
if (wflag) {
point_size = DEFAULT_PAGE_HEIGHT * POINTS_PER_INCH /
((columns + 0.5) * DEFAULT_CHAR_WIDTH);
lines_per_page = DEFAULT_PAGE_WIDTH * POINTS_PER_INCH /
point_size;
} else {
point_size = DEFAULT_PAGE_WIDTH * POINTS_PER_INCH /
(lines_per_page + 0.5);
columns = DEFAULT_PAGE_HEIGHT * POINTS_PER_INCH /
(point_size * DEFAULT_CHAR_WIDTH);
}
}
box_height = DEFAULT_PAGE_HEIGHT * POINTS_PER_INCH / layoutp->page_rows;
if (layoutp->rotation == 0)
box_width = box_height /
DEFAULT_PAGE_HEIGHT * DEFAULT_PAGE_WIDTH;
else
box_width = box_height *
DEFAULT_PAGE_HEIGHT / DEFAULT_PAGE_WIDTH;
gap_width = DEFAULT_PAPER_WIDTH * POINTS_PER_INCH /
layoutp->page_cols - box_width;
gap_height = DEFAULT_PAPER_HEIGHT * POINTS_PER_INCH /
layoutp->page_rows - box_height;
margin_x = gap_width/2;
margin_y = gap_height/2;
columns -= numberwidth + DEFAULT_SPACES_AFTER_NUMBER;
if (columns <= 0) {
fatal("numbering width exceeds number of columns");
/* NOT REACHED */
}
/* compute physical "lower left corner" of each logical page */
for (j = 0; j < layoutp->pages; j++) {
int phys_row; /* 0 is bottom row */
int phys_col; /* 0 is left column */
if (landscape == (rot_text == 0)) {
/* logical pages run physically up and down */
phys_row = j % layoutp->page_rows;
phys_col = j / layoutp->page_rows;
} else {
/* logical pages run physically left to right */
phys_row = j / layoutp->page_cols;
phys_col = j % layoutp->page_cols;
}
if (rot_text == 0) {
/* top physical row is logically first */
phys_row = layoutp->page_rows - 1 - phys_row;
}
positions[j].base_x = margin_x +
phys_col * (box_width + gap_width);
positions[j].base_y = margin_y +
phys_row * (box_height + gap_height);
if (rot_text != 0) {
positions[j].base_x += box_width;
}
}
if (vflag) {
(void) fprintf(stderr, "%s:\n\n", progname);
(void) fprintf(stderr, "Lines/page = %d\n", lines_per_page);
(void) fprintf(stderr, "Columns = %d\n", columns);
for (j = 0; j < layoutp->pages; j++) {
(void) fprintf(stderr, "\tx=%3d, y=%3d\n",
positions[j].base_x, positions[j].base_y);
}
(void) fprintf(stderr, "box_width=%3d, box_height=%3d\n",
box_width, box_height);
(void) fprintf(stderr, "gap_width=%3d, gap_height=%3d\n",
gap_width, gap_height);
}
setup();
preamble();
if (iscodereview) {
char command[BUFSIZ];
(void) snprintf(command, BUFSIZ, "diff -b -D %s %s %s",
CODEREVIEW, argv[first_file+1], argv[first_file]);
infile = popen(command, "r");
bannerfile = argv[first_file+1];
if (ungetc(getc(infile), infile) == EOF) {
(void) pclose(infile);
(void) sprintf(command,
"echo No differences encountered");
infile = popen(command, "r");
}
setheaderfile(bannerfile);
printfile(infile);
(void) pclose(infile);
} else if (first_file == argc) { /* no files on command line */
if (vflag)
(void) fprintf(stderr, "\tprinting stdin\n");
setheaderfile("stdin");
printfile(stdin);
} else {
for (i = first_file; i < argc; i++) {
if ((infile = fopen(argv[i], "r")) == (FILE *)NULL) {
fatal("can't open %s for reading", argv[i]);
/*NOTREACHED*/
}
if (pflag) {
char cmdbuf[BUFSIZ];
(void) snprintf(cmdbuf, BUFSIZ, "pr %s",
argv[i]);
(void) fclose(infile);
infile = popen(cmdbuf, "r");
}
if (vflag)
(void) fprintf(stderr, "\tprinting %s\n",
argv[i]);
setheaderfile(argv[i]);
printfile(infile);
if (pflag)
(void) pclose(infile);
else
(void) fclose(infile);
}
}
postamble();
if (fflush(stdout) == EOF) {
fatal("write error on stdout");
/*NOTREACHED*/
}
exit(0);
/*NOTREACHED*/
/*LINTED*/
}
/*
* Initial lines sent to the LaserWriter
* Generates the PostScript header and includes the prologue file
* There is limited checking for I/O errors here
*/
void
preamble(void)
{
(void) printf("%%!PS-Adobe-2.0\n");
(void) printf("%%%%Creator: %s on %s\n", progname, hostname);
(void) printf("%%%%CreationDate: %s\n", currentdate);
(void) printf("%%%%For: %s\n", username);
(void) printf("%%%%DocumentFonts: %s %s %s %s\n",
default_font, default_font_bold,
default_font_italic, default_font_bold_italic);
(void) printf("%%%%Pages: (atend)\n");
if (prologue == NULL) {
char **cpp;
for (cpp = default_prologue; *cpp; cpp++) {
(void) fputs(*cpp, stdout);
}
} else {
FILE *fp;
if ((fp = fopen(prologue, "r")) == NULL) {
fatal("can't open prologue file %s", prologue);
/*NOTREACHED*/
}
while (fgets(bufin, sizeof (bufin), fp) != NULL)
(void) fputs(bufin, stdout);
(void) fclose(fp);
}
if (ferror(stdout) || fflush(stdout) == EOF) {
fatal("write error on stdout");
/*NOTREACHED*/
}
(void) printf("/%s {%f /%s %s}bind def\n", DEFAULT_FONT,
point_size, default_font, SELECT_FONT);
(void) printf("/%s {%f /%s %s}bind def\n", DEFAULT_FONT_BOLD,
point_size, default_font_bold, SELECT_FONT);
(void) printf("/%s {%f /%s %s}bind def\n", DEFAULT_FONT_ITALIC,
point_size, default_font_italic, SELECT_FONT);
(void) printf("/%s {%f /%s %s}bind def\n", DEFAULT_FONT_BOLD_ITALIC,
point_size, default_font_bold_italic, SELECT_FONT);
}
void
postamble(void)
{
(void) printf("%%%%Trailer\n");
(void) printf("%%%%Pages: %d\n", current.page_count);
}
int
printbanner(char *filename, FILE *outfile)
{
char buffer[BUFSIZ];
struct stat statbuf;
struct format_state format_state;
int nlines = 0;
/* we've already verified readability */
(void) stat(filename, &statbuf);
save_format_state(&format_state);
numberwidth = 0;
setcurrentfont(DEFAULT_FONT_BOLD_ITALIC, outfile);
current.row -= point_size;
(void) fprintf(outfile, "%d %.2f %s\n", start_x, current.row, MOVETO);
proc(banner, outfile);
nlines++;
current.row -= point_size;
(void) fprintf(outfile, "%d %.2f %s\n", start_x, current.row, MOVETO);
(void) snprintf(buffer, BUFSIZ, "%8ld %.24s", statbuf.st_size,
ctime(&statbuf.st_mtime));
proc(buffer, outfile);
nlines++;
do {
current.row -= point_size;
(void) fprintf(outfile, "%d %.2f %s\n", start_x, current.row,
MOVETO);
filename += sprintf(buffer, "%.*s", columns, filename);
proc(buffer, outfile);
nlines++;
} while (strlen(filename) != 0);
if (comment != NULL && comment[0] != 0) {
const char *cur = comment;
const char *endl;
int len;
while (*cur != 0) {
current.row -= point_size;
(void) fprintf(outfile, "%d %.2f %s\n", start_x,
current.row, MOVETO);
endl = strchr(cur, '\n');
if (endl == NULL)
endl = cur + strlen(cur);
/* truncate to columns */
len = endl - cur;
if (len > columns)
len = columns;
(void) sprintf(buffer, "%.*s", len, cur);
proc(buffer, outfile);
nlines++;
if (*endl == 0)
break;
cur = endl + 1;
}
}
current.row -= point_size;
(void) fprintf(outfile, "%d %.2f %s\n", start_x, current.row, MOVETO);
proc(banner, outfile);
nlines++;
restore_format_state(&format_state, outfile);
return (nlines);
}
void
setcurrentfont(char *newfont, FILE *outfile)
{
if (current.font != newfont) {
if (newfont)
current.font = newfont;
(void) fprintf(outfile, "%s\n", current.font);
}
}
void
savestate(FILE *f)
{
current.offset = ftell(f);
saved = current;
}
void
restorestate(FILE *f)
{
char *font;
font = current.font;
(void) fseek(f, saved.offset, 0);
current = saved;
setcurrentfont(font, f);
}
void
save_format_state(struct format_state *fs)
{
fs->numberwidth = numberwidth;
fs->linenumber = linenumber;
fs->altlinenumber = altlinenumber;
fs->makegray = makegray;
fs->font = current.font;
}
void
restore_format_state(struct format_state *fs, FILE *outfile)
{
numberwidth = fs->numberwidth;
linenumber = fs->linenumber;
altlinenumber = fs->altlinenumber;
makegray = fs->makegray;
setcurrentfont(fs->font, outfile);
}
/*
* Print a file
*
* The input stream may be stdin, a file, or a pipe
*/
void
printfile(FILE *infile)
{
int eof;
char *p;
FILE *outfile;
if (reverse)
page_map[0] = 0L;
if (usetmp) {
(void) snprintf(bufin, BUFIN, "/tmp/%sXXXXXX", progname);
p = mktemp(bufin);
if ((outfile = fopen(p, "w+")) == NULL) {
fatal("can't open temporary file %s", p);
/* NOTREACHED */
}
if (!dflag)
(void) unlink(p);
else
(void) fprintf(stderr, "will not unlink %s\n", p);
}
else
outfile = stdout;
setcurrentfont(DEFAULT_FONT, outfile);
change_seen = 0;
dots_inserted = 0;
in_change = 0;
makegray = 0;
linenumber = 0;
altlinenumber = 0;
current.logical_page_count = 0;
do {
current.row = start_y;
eof = printpage(infile, outfile);
} while (!eof);
if (((int)current.row) != start_y)
endpage(outfile);
if ((current.logical_page_count % layoutp->pages) != 0)
flushpage(outfile);
if (vflag)
(void) fprintf(stderr, "\n");
if (fflush(outfile) == EOF) {
fatal("write error while flushing output");
/*NOTREACHED*/
}
if (usetmp) {
if (reverse)
reversepages(outfile);
else
copypage(outfile, 0L, current.offset);
(void) fclose(outfile);
}
}
void
process_elide(FILE *outfile)
{
if (!change_seen && !in_change) {
/* don't include function in output */
restorestate(outfile);
if (!dots_inserted) {
struct format_state format_state;
save_format_state(&format_state);
numberwidth = 0;
current.lineno++;
current.row -= point_size;
setcurrentfont(DEFAULT_FONT_BOLD_ITALIC, outfile);
proc("______unchanged_portion_omitted_", outfile);
restore_format_state(&format_state, outfile);
savestate(outfile);
dots_inserted = 1;
}
} else {
savestate(outfile);
change_seen = in_change;
dots_inserted = 0;
}
}
/*
* Process the next page
* Return 1 on EOF, 0 otherwise
*/
int
printpage(FILE *infile, FILE *outfile)
{
int tmplinenumber;
char command[BUFSIZ], flag[BUFSIZ];
if (ungetc(getc(infile), infile) == EOF)
return (1);
current.lineno = 0;
current.lineno += startpage(outfile);
if (bannerfile) {
current.lineno += printbanner(bannerfile, outfile);
bannerfile = NULL;
savestate(outfile);
}
for (; current.lineno < lines_per_page; ) {
if (fgetline(bufin, sizeof (bufin), infile) == (char *)NULL) {
if (elide)
process_elide(outfile);
return (1);
}
/*
* Allow C comment delimiters around flag; only really applies
* to #else and #endif, but we don't expect to see C comments
* around flag for #if. Also accept flag with no C comment
* delimiters.
*/
if (iscodereview &&
(sscanf(bufin, "#%32s /* %80s */", command, flag) == 2 ||
sscanf(bufin, "#%32s %80s", command, flag) == 2) &&
strcmp(flag, CODEREVIEW) == 0) {
if (strcmp(command, "ifdef") == 0) {
change_seen = 1;
in_change = 1;
makegray = 1;
old_stuff = 1;
tmplinenumber = linenumber;
linenumber = altlinenumber;
altlinenumber = tmplinenumber;
setcurrentfont(DEFAULT_FONT_ITALIC, outfile);
} else if (strcmp(command, "ifndef") == 0) {
change_seen = 1;
in_change = 1;
makegray = 1;
old_stuff = 0;
setcurrentfont(DEFAULT_FONT_BOLD, outfile);
} else if (strcmp(command, "else") == 0) {
makegray = 1;
old_stuff = !old_stuff;
tmplinenumber = linenumber;
linenumber = altlinenumber;
altlinenumber = tmplinenumber;
if (!old_stuff)
setcurrentfont(DEFAULT_FONT_BOLD,
outfile);
else
setcurrentfont(DEFAULT_FONT_ITALIC,
outfile);
} else /* if (strcmp(command, "endif") == 0) */ {
in_change = 0;
makegray = 0;
savestate(outfile);
setcurrentfont(DEFAULT_FONT, outfile);
if (old_stuff) {
tmplinenumber = linenumber;
linenumber = altlinenumber;
altlinenumber = tmplinenumber;
}
}
continue;
}
current.lineno++;
current.row -= point_size;
if (bufin[0] == '\f')
break;
proc(bufin, outfile);
if (elide && (bufin[0] == END_C_FUNCTION ||
(strstr(bufin, END_ASM_FUNCTION) != NULL)))
process_elide(outfile);
}
endpage(outfile);
return (0);
}
/*
* Start a new page
*/
int
startpage(FILE *outfile)
{
int logical_page, lines, buflen;
struct format_state format_state;
char buf[8];
logical_page = current.logical_page_count % layoutp->pages;
if (logical_page == 0)
setuppage(outfile);
else
setcurrentfont((char *)NULL, outfile);
(void) fprintf(outfile, "%s ", SET_WIDTHS);
(void) fprintf(outfile, "%d %f %d %d %s\n",
rot_text, layoutp->scale, positions[logical_page].base_x,
positions[logical_page].base_y, START_PAGE);
lines = 0;
if (header) {
save_format_state(&format_state);
setcurrentfont(DEFAULT_FONT_BOLD, outfile);
numberwidth = 0;
makegray = 0;
current.row -= point_size;
(void) fprintf(outfile, "%d %.2f %s\n", start_x, current.row,
MOVETO);
proc(headerstring, outfile);
(void) snprintf(buf, 8, "%d", current.logical_page_count + 1);
buflen = strlen(buf);
(void) fprintf(outfile, "%d %.2f %s (%s)%s\n",
(int)(end_x - (buflen + 0.5) *
DEFAULT_CHAR_WIDTH * point_size),
current.row, MOVETO, buf, SHOW);
current.row -= point_size;
restore_format_state(&format_state, outfile);
lines = 2;
}
return (lines);
}
void
setheaderfile(char *filename)
{
if (header == HEADER_IMPLICIT)
headerstring = filename;
}
/*
* Setup page
*/
void
setuppage(FILE *outfile)
{
int i, ilimit;
int begin, end, place;
(void) fprintf(outfile, "%%%%Page: ? %d\n", current.page_count + 1);
setcurrentfont((char *)NULL, outfile);
if (layoutp->pages == 1)
return;
(void) fprintf(outfile, "%f %s %s\n", RULE_WIDTH, SETLINEWIDTH,
NEWPATH);
begin = 0; end = DEFAULT_PAPER_WIDTH * POINTS_PER_INCH;
for (i = 1, ilimit = layoutp->page_rows; i < ilimit; i++) {
place = margin_y - gap_height/2 + i * (box_height+gap_height);
(void) fprintf(outfile, "%d %d %s ", begin, place, MOVETO);
(void) fprintf(outfile, "%d %d %s\n", end, place, LINETO);
}
begin = 0; end = DEFAULT_PAPER_HEIGHT * POINTS_PER_INCH;
for (i = 1, ilimit = layoutp->page_cols; i < ilimit; i++) {
place = margin_x - gap_width/2 + i * (box_width+gap_width);
(void) fprintf(outfile, "%d %d %s ", place, begin, MOVETO);
(void) fprintf(outfile, "%d %d %s\n", place, end, LINETO);
}
(void) fprintf(outfile, "%s\n", STROKE);
}
/*
* Terminate the logical page and indicate the start of the next
*/
void
endpage(FILE *outfile)
{
(void) fprintf(outfile, "%s\n", END_PAGE);
current.logical_page_count++;
if (vflag)
(void) fprintf(stderr, "x");
if ((current.logical_page_count % layoutp->pages) == 0)
flushpage(outfile);
}
/*
* Flush the physical page
* Record the start of the next page
*/
void
flushpage(FILE *outfile)
{
(void) fprintf(outfile, "%d %s\n", ncopies, FLUSH_PAGE);
current.page_count++;
current.offset = ftell(outfile);
if (reverse) {
if (current.page_count >= MAXPAGES) {
fatal("page reversal limit (%d) reached", MAXPAGES);
/* NOTREACHED */
}
page_map[current.page_count] = current.offset;
}
if (vflag)
(void) fprintf(stderr, "|");
}
/*
* reverse the order of pages
*/
void
reversepages(FILE *outfile)
{
int i;
if (vflag)
(void) fprintf(stderr, "\nreversing %d page%s\n",
current.page_count, current.page_count > 1 ? "s" : "");
for (i = current.page_count - 1; i >= 0; i--) {
copypage(outfile, page_map[i], page_map[i+1]);
}
}
/*
* copy a page (or more) from tempfile to stdout
*/
void
copypage(FILE *outfile, long off_beg, long off_end)
{
int bytecount, nbytes;
if (fseek(outfile, off_beg, 0) == -1L) {
fatal("temporary file seek error");
/* NOTREACHED */
}
nbytes = off_end - off_beg;
while (nbytes > 0) {
bytecount = nbytes;
if (bytecount > sizeof (bufout))
bytecount = sizeof (bufout);
bytecount = fread(bufout, 1, bytecount, outfile);
if (bytecount <= 0) {
fatal("temporary file read error");
/* NOTREACHED */
}
if (fwrite(bufout, 1, bytecount, stdout) != bytecount) {
fatal("write error during page copy");
/* NOTREACHED */
}
nbytes -= bytecount;
}
}
/*
* Process a line of input, escaping characters when necessary and handling
* tabs
*
* The output is improved somewhat by coalescing consecutive tabs and
* backspaces and eliminating tabs at the end of a line
*
* Overprinting (presumably most often used in underlining) can be far from
* optimal; in particular the way nroff underlines by sequences like
* "_\ba_\bb_\bc" creates a large volume of PostScript. This isn't too
* serious since a lot of nroff underlining is unlikely.
*
* Since a newline is generated for each call there will be more
* newlines in the output than is necessary
*/
void
proc(char *in, FILE *outfile)
{
int i;
char *last, *p, *q;
int currentp, instr, tabc, tabto, grayed;
char *altfont;
currentp = 0;
instr = 0;
tabto = 0;
if (iscodereview) {
grayed = makegray;
altfont = current.font;
} else {
grayed = 0;
altfont = DEFAULT_FONT;
}
/* subtract slop factor */
last = bufout + MAX_OUTPUT_LINE_LENGTH - 20;
for (;;) { /* check for any special line treatment */
if (graylength && strncmp(in, graystring, graylength) == 0) {
grayed++;
in += graylength;
} else if (boldlength &&
strncmp(in, boldstring, boldlength) == 0) {
altfont = DEFAULT_FONT_BOLD;
in += boldlength;
} else if (itlclength &&
strncmp(in, itlcstring, itlclength) == 0) {
altfont = DEFAULT_FONT_ITALIC;
in += itlclength;
} else if (bitclength &&
strncmp(in, bitcstring, bitclength) == 0) {
altfont = DEFAULT_FONT_BOLD_ITALIC;
in += bitclength;
} else
break;
}
if (grayed) {
(void) fprintf(outfile, "%d %.2f %d %.2f %s\n",
start_x,
current.row - DEFAULT_DESCENDER_FRACTION * point_size,
end_x,
current.row +
(1.0 - DEFAULT_DESCENDER_FRACTION) * point_size,
SHADE);
}
linenumber++;
if (!in_change)
altlinenumber++;
if (*in == '\0')
return;
if (start_x != 0) {
(void) fprintf(outfile, "%d %.2f %s\n",
start_x, current.row, MOVETO);
}
else
(void) fprintf(outfile, "%.2f %s\n",
current.row, ZEROMOVETO);
if (numberwidth) {
setcurrentfont(DEFAULT_FONT, outfile);
(void) sprintf(bufout, "%*d", numberwidth, linenumber);
for (q = bufout, i = 0; *q == ' '; q++, i++)
;
(void) fprintf(outfile, "%d %s (%s)%s %d %s ",
i, TAB, q, SHOW, DEFAULT_SPACES_AFTER_NUMBER, TAB);
}
setcurrentfont(altfont, outfile);
q = bufout;
*q = '\0';
for (p = in; *p != '\0'; p++) {
switch (*p) {
case '\t':
/*
* Count the number of tabs that immediately follow
* the one we're looking at
*/
tabc = 0;
while (*(p + 1) == '\t') {
p++;
tabc++;
}
if (currentp > 0) { /* not beginning of line */
i = tabstop - (currentp % tabstop) +
tabc * tabstop;
if (instr) {
(void) snprintf(q,
BUFOUT - (q - bufout), ")%s ",
SHOW);
q += strlen(q);
instr = 0;
}
}
else
i = (tabc + 1) * tabstop;
tabto += i;
currentp += i;
break;
case '\b':
/* backspacing over tabs doesn't work... */
if (tabto != 0) {
fatal("attempt to backspace over a tab");
/*NOTREACHED*/
}
p++;
for (i = 1; *p == '\b'; p++)
i++;
p--;
if (currentp - i < 0) {
fatal("too many backspaces");
/*NOTREACHED*/
}
if (instr) {
*q = '\0';
(void) fprintf(outfile, "%s)%s\n",
bufout, SHOW);
}
instr = 0;
if (currentp >= columns)
i -= currentp-columns;
if (i <= 0) {
/* backspace in truncated line */
bufout[0] = '\0';
} else if (i == 1) {
/* frequent case gets special attention */
(void) snprintf(bufout, BUFOUT, "%s ",
BACKSPACE);
} else
(void) snprintf(bufout, BUFOUT, "-%d %s ", i,
TAB);
q = bufout + strlen(bufout);
currentp -= i;
break;
case '\f':
tabto = 0; /* optimizes */
*q = '\0';
if (instr)
(void) fprintf(outfile, "%s)%s\n",
bufout, SHOW);
else
(void) fprintf(outfile, "%s\n", bufout);
endpage(outfile);
(void) startpage(outfile);
current.row = start_y;
(void) fprintf(outfile, "%d %.2f %s\n",
start_x, current.row, MOVETO);
if (numberwidth)
(void) fprintf(outfile, "%d %s\n", numberwidth +
DEFAULT_SPACES_AFTER_NUMBER, TAB);
q = bufout;
currentp = 0;
instr = 0;
break;
case '\r':
tabto = 0; /* optimizes */
if (instr) {
*q = '\0';
(void) fprintf(outfile, "%s)%s\n",
bufout, SHOW);
instr = 0;
q = bufout;
}
(void) fprintf(outfile, "%d %.2f %s\n",
start_x, current.row, MOVETO);
if (numberwidth)
(void) fprintf(outfile, "%d %s\n", numberwidth +
DEFAULT_SPACES_AFTER_NUMBER, TAB);
currentp = 0;
break;
case '\\':
case '(':
case ')':
if (currentp < columns) {
if (!instr) {
if (tabto) {
(void) snprintf(q,
BUFOUT - (q - bufout),
"%d %s ", tabto, TAB);
q += strlen(q);
tabto = 0;
}
*q++ = '(';
instr = 1;
}
*q++ = '\\';
*q++ = *p;
}
currentp++;
break;
default: {
/*
* According to the PostScript Language Manual,
* PostScript files can contain only "the printable
* subset of the ASCII character set (plus the
* newline marker)".
*/
char pchar;
pchar = *p;
if (currentp < columns) {
if (!instr) {
if (tabto) {
(void) snprintf(q,
BUFOUT - (q - bufout),
"%d %s ", tabto, TAB);
q += strlen(q);
tabto = 0;
}
*q++ = '(';
instr = 1;
}
if (!isascii(pchar) || !isprint(pchar)) {
if (iscntrl(pchar)) {
if (pchar == '\177')
pchar = '_';
else
pchar += '@';
*q++ = '^';
} else {
*q++ = '\\';
*q++ = '0' + ((pchar>>6) & 7);
*q++ = '0' + ((pchar>>3) & 7);
pchar = '0' + (pchar & 7);
}
}
*q++ = pchar;
}
currentp++;
break;
}
}
if (q >= last) {
*q = '\0';
if (instr)
(void) fprintf(outfile, "%s)%s\n", bufout,
SHOW);
else
(void) fprintf(outfile, "%s\n", bufout);
q = bufout;
instr = 0;
}
}
if (instr) {
(void) snprintf(q, BUFOUT - (q - bufout), ")%s", SHOW);
q += strlen(q);
}
else
*q = '\0';
if (q >= last) {
fatal("bufout overflow");
/*NOTREACHED*/
}
if (bufout[0] != '\0')
(void) fprintf(outfile, "%s\n", bufout);
}
/*
* Initialize globals:
* username - login name of user
* hostname - name of machine on which lwlp is run
* currentdate - what it says
* Possible system dependencies here...
*/
void
setup(void)
{
int len;
char *p;
long t;
struct utsname utsname;
struct passwd *pw;
if ((p = getlogin()) == (char *)NULL) {
if ((pw = getpwuid(getuid())) == (struct passwd *)NULL)
p = "Whoknows";
else
p = pw->pw_name;
endpwent();
}
username = strdup(p);
(void) uname(&utsname);
hostname = strdup(utsname.nodename);
t = time((long *)0);
p = ctime(&t);
len = strlen(p);
*(p + len - 1) = '\0'; /* zap the newline character */
currentdate = strdup(p);
current.font = DEFAULT_FONT;
}
/*
* Special version of fgets
* Read until a formfeed, newline, or overflow
* If a formfeed is the first character, return it immediately
* If a formfeed is found after the first character, replace it by a newline
* and push the formfeed back onto the input stream
* A special case is a formfeed followed by a newline in which case the
* newline is ignored
* The input buffer will be null-terminated and will *not* end with a newline
* The buffer size n includes the null
*/
char *
fgetline(char *s, int n, FILE *iop)
{
int ch;
char *cs;
if (n < 2) {
fatal("fgetline called with bad buffer size!?");
/*NOTREACHED*/
}
cs = s;
n--; /* the null */
/*
* Check out the special cases
*/
if ((ch = getc(iop)) == EOF)
return ((char *)NULL);
if (ch == '\f') {
if ((ch = getc(iop)) != '\n') {
/*
* If EOF was just read it will be noticed
* next time through
*/
if (ungetc(ch, iop) == EOF && !feof(iop)) {
/*
* Shouldn't happen since a getc()
* was just done
*/
fatal("fgetline - ungetc failed");
/*NOTREACHED*/
}
}
*cs++ = '\f';
*cs = '\0';
return (s);
}
/*
* Check for "weird" input characters is made in proc()
*/
while (n-- > 0) {
if (ch == '\f' || ch == '\n')
break;
*cs++ = ch;
if ((ch = getc(iop)) == EOF)
break;
}
if (ch == EOF && cs == s) /* Nothing was read */
return ((char *)NULL);
if (ch == '\f') {
if (ungetc(ch, iop) == EOF)
(void) fprintf(stderr, "fgetline - can't ungetc??\n");
} else if (ch != '\n' && ch != EOF) {
fatal("fgetline - input line too long");
/*NOTREACHED*/
}
*cs = '\0';
return (s);
}
/*PRINTFLIKE1*/
void
fatal(char *fmt, ...)
{
va_list ap;
(void) fprintf(stderr, "%s: ", progname);
va_start(ap, fmt);
(void) vfprintf(stderr, fmt, ap);
va_end(ap);
(void) fprintf(stderr, "\n");
exit(1);
/*NOTREACHED*/
}