/*
* 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
* 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
* -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
*
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <pwd.h>
#include <unistd.h>
#include <time.h>
#include <stdarg.h>
/*
* Configurable...
* BUFOUT should be fairly large
*/
#define REVERSE_OFF 0
static char *banner =
"**********************************************************";
/*
* PostScript command strings
*/
/*
* PostScript command strings defined in the prologue file
*/
/*
* Conformance requires that no PostScript line exceed 256 characters
*/
static struct print_state {
int page_count;
int logical_page_count;
int lineno;
long offset;
float row;
char *font;
struct format_state {
int makegray;
char *font;
};
static int lines_per_page;
static int columns;
static float point_size;
static int ncopies;
static int tabstop;
static int reverse;
static int elide;
static int usetmp;
static char *headerstring;
static char *bannerfile;
static char *comment;
static void preamble(void);
static void postamble(void);
static void setcurrentfont(char *, FILE *);
static void restorestate(FILE *);
static void save_format_state(struct format_state *);
static void process_elide(FILE *);
static void setheaderfile(char *);
static void reversepages(FILE *);
static void setup(void);
static int printbanner(char *, FILE *);
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",
};
struct layout {
float scale;
int rotation;
};
static struct position {
int base_x;
int base_y;
int
{
char *pc;
else
current.page_count = 0;
ncopies = 1;
/*LINTED*/
sizeof (CODEREVIEW) - 1) == 0) {
numberwidth = 4;
wflag = -1;
}
"1248B:c:deG:h:I:l:Ln:P:prt:vw:X:y:")) != -1) {
switch (ch) {
case '1':
break;
case '2':
break;
case '4':
break;
case '8':
break;
case 'B':
break;
case 'c':
if (ncopies <= 0) {
fatal("number of copies must be > 0");
/*NOTREACHED*/
}
break;
case 'd':
dflag = 1;
break;
case 'e':
elide = 1;
break;
case 'G':
break;
case 'h':
break;
case 'I':
break;
case 'l':
if (lines_per_page < 1) {
/*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':
if (numberwidth < 2) {
fatal("invalid numbering width");
/*NOTREACHED*/
}
break;
case 'P':
break;
case 'p':
pflag = 1;
break;
case 'r':
break;
case 't':
if (tabstop < 1) {
fatal("negative tabstop");
/*NOTREACHED*/
}
break;
case 'v':
vflag = 1;
break;
case 'w':
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':
break;
case 'y':
break;
default:
"usage: %s %s\n\t%s\n\t%s\n\t%s\n",
if (iscodereview)
exit(1);
}
}
if (elide && !iscodereview) {
fatal("-e option valid only with codereview");
/*NOTREACHED*/
}
/* allocate page_map if we need one */
if (reverse) {
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++) {
/*NOTREACHED*/
}
}
fatal("codereview: need old and new file");
/*NOTREACHED*/
}
/* compute logical point size, logical dimensions */
if (!landscape) {
if (wflag) {
} else {
(lines_per_page + 0.5);
}
} else {
if (wflag) {
} else {
(lines_per_page + 0.5);
}
}
box_width = box_height /
else
box_width = box_height *
if (columns <= 0) {
fatal("numbering width exceeds number of columns");
/* NOT REACHED */
}
/* compute physical "lower left corner" of each logical page */
/* logical pages run physically up and down */
} else {
/* logical pages run physically left to right */
}
if (rot_text == 0) {
/* top physical row is logically first */
}
if (rot_text != 0) {
}
}
if (vflag) {
}
}
setup();
preamble();
if (iscodereview) {
"echo No differences encountered");
}
if (vflag)
setheaderfile("stdin");
} else {
for (i = first_file; i < argc; i++) {
/*NOTREACHED*/
}
if (pflag) {
argv[i]);
}
if (vflag)
argv[i]);
setheaderfile(argv[i]);
if (pflag)
else
}
}
postamble();
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("%%%%DocumentFonts: %s %s %s %s\n",
(void) printf("%%%%Pages: (atend)\n");
char **cpp;
}
} else {
/*NOTREACHED*/
}
}
fatal("write error on stdout");
/*NOTREACHED*/
}
}
void
postamble(void)
{
(void) printf("%%%%Trailer\n");
}
int
{
int nlines = 0;
/* we've already verified readability */
numberwidth = 0;
nlines++;
nlines++;
do {
MOVETO);
nlines++;
const char *endl;
int len;
while (*cur != 0) {
/* truncate to columns */
nlines++;
if (*endl == 0)
break;
}
}
nlines++;
return (nlines);
}
void
{
if (newfont)
}
}
void
{
}
void
{
char *font;
setcurrentfont(font, f);
}
void
{
}
void
{
}
/*
* Print a file
*
* The input stream may be stdin, a file, or a pipe
*/
void
{
int eof;
char *p;
if (reverse)
page_map[0] = 0L;
if (usetmp) {
fatal("can't open temporary file %s", p);
/* NOTREACHED */
}
if (!dflag)
(void) unlink(p);
else
}
else
change_seen = 0;
dots_inserted = 0;
in_change = 0;
makegray = 0;
linenumber = 0;
altlinenumber = 0;
do {
} while (!eof);
if (vflag)
fatal("write error while flushing output");
/*NOTREACHED*/
}
if (usetmp) {
if (reverse)
else
}
}
void
{
if (!change_seen && !in_change) {
/* don't include function in output */
if (!dots_inserted) {
numberwidth = 0;
dots_inserted = 1;
}
} else {
dots_inserted = 0;
}
}
/*
* Process the next page
* Return 1 on EOF, 0 otherwise
*/
int
{
int tmplinenumber;
return (1);
if (bannerfile) {
bannerfile = NULL;
}
if (elide)
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 &&
change_seen = 1;
in_change = 1;
makegray = 1;
old_stuff = 1;
change_seen = 1;
in_change = 1;
makegray = 1;
old_stuff = 0;
makegray = 1;
if (!old_stuff)
outfile);
else
outfile);
} else /* if (strcmp(command, "endif") == 0) */ {
in_change = 0;
makegray = 0;
if (old_stuff) {
}
}
continue;
}
if (bufin[0] == '\f')
break;
}
return (0);
}
/*
* Start a new page
*/
int
{
if (logical_page == 0)
else
lines = 0;
if (header) {
numberwidth = 0;
makegray = 0;
MOVETO);
lines = 2;
}
return (lines);
}
void
{
if (header == HEADER_IMPLICIT)
}
/*
* Setup page
*/
void
{
int i, ilimit;
return;
NEWPATH);
}
}
}
/*
* Terminate the logical page and indicate the start of the next
*/
void
{
if (vflag)
}
/*
* Flush the physical page
* Record the start of the next page
*/
void
{
if (reverse) {
/* NOTREACHED */
}
}
if (vflag)
}
/*
* reverse the order of pages
*/
void
{
int i;
if (vflag)
}
}
/*
* copy a page (or more) from tempfile to stdout
*/
void
{
fatal("temporary file seek error");
/* NOTREACHED */
}
while (nbytes > 0) {
if (bytecount <= 0) {
fatal("temporary file read error");
/* NOTREACHED */
}
fatal("write error during page copy");
/* NOTREACHED */
}
}
}
/*
* 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
{
int i;
char *last, *p, *q;
char *altfont;
currentp = 0;
instr = 0;
tabto = 0;
if (iscodereview) {
} else {
grayed = 0;
}
/* subtract slop factor */
for (;;) { /* check for any special line treatment */
grayed++;
in += graylength;
} else if (boldlength &&
in += boldlength;
} else if (itlclength &&
in += itlclength;
} else if (bitclength &&
in += bitclength;
} else
break;
}
if (grayed) {
SHADE);
}
linenumber++;
if (!in_change)
if (*in == '\0')
return;
if (start_x != 0) {
}
else
if (numberwidth) {
for (q = bufout, i = 0; *q == ' '; q++, i++)
;
}
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 */
if (instr) {
(void) snprintf(q,
SHOW);
q += strlen(q);
instr = 0;
}
}
else
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';
}
instr = 0;
if (i <= 0) {
/* backspace in truncated line */
bufout[0] = '\0';
} else if (i == 1) {
/* frequent case gets special attention */
} else
TAB);
currentp -= i;
break;
case '\f':
tabto = 0; /* optimizes */
*q = '\0';
if (instr)
else
if (numberwidth)
q = bufout;
currentp = 0;
instr = 0;
break;
case '\r':
tabto = 0; /* optimizes */
if (instr) {
*q = '\0';
instr = 0;
q = bufout;
}
if (numberwidth)
currentp = 0;
break;
case '\\':
case '(':
case ')':
if (!instr) {
if (tabto) {
(void) snprintf(q,
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 (!instr) {
if (tabto) {
(void) snprintf(q,
q += strlen(q);
tabto = 0;
}
*q++ = '(';
instr = 1;
}
if (pchar == '\177')
pchar = '_';
else
pchar += '@';
*q++ = '^';
} else {
*q++ = '\\';
}
}
*q++ = pchar;
}
currentp++;
break;
}
}
if (q >= last) {
*q = '\0';
if (instr)
SHOW);
else
q = bufout;
instr = 0;
}
}
if (instr) {
q += strlen(q);
}
else
*q = '\0';
if (q >= last) {
fatal("bufout overflow");
/*NOTREACHED*/
}
if (bufout[0] != '\0')
}
/*
* 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;
p = "Whoknows";
else
endpwent();
}
t = time((long *)0);
p = ctime(&t);
currentdate = strdup(p);
}
/*
* 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 *
{
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
*/
return ((char *)NULL);
if (ch == '\f') {
/*
* If EOF was just read it will be noticed
* next time through
*/
/*
* 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) {
break;
break;
}
return ((char *)NULL);
if (ch == '\f') {
fatal("fgetline - input line too long");
/*NOTREACHED*/
}
*cs = '\0';
return (s);
}
/*PRINTFLIKE1*/
void
{
exit(1);
/*NOTREACHED*/
}