xref: /original-bsd/local/transcript/src/psrev.c (revision b907ef3c)
1 #ifndef lint
2 static char Notice[] = "Copyright (c) 1985 Adobe Systems Incorporated";
3 static char *RCSID="$Header: psrev.c,v 2.1 85/11/24 11:51:02 shore Rel $";
4 #endif
5 /* psrev.c
6  *
7  * Copyright (c) 1985 Adobe Systems Incorporated
8  *
9  * page reversal and selection filter
10  *
11  * Original Version: Tom Malloy
12  * Edit History:
13  * Andrew Shore: Fri Nov 22 11:20:20 1985
14  * End Edit History.
15  *
16  * RCSLOG:
17  * $Log:	psrev.c,v $
18  * Revision 2.1  85/11/24  11:51:02  shore
19  * Product Release 2.0
20  *
21  * Revision 1.4  85/11/22  11:31:05  shore
22  * Last line of trailer was dropped if it didn't end in a newline
23  *
24  * Revision 1.3  85/11/20  00:52:21  shore
25  * Support for System V
26  * getopt!
27  * made lint a little happier
28  *
29  * Revision 1.2  85/05/14  11:26:38  shore
30  *
31  *
32  *
33  */
34 
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <pwd.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include "transcript.h"
41 
42 #define SCVERID "%!PS-Adobe-"
43 #define ichMaxPage 7
44 #define SCPAGE "%%Page:"
45 #define ichMaxTrailer 9
46 #define SCTRAILER "%%Trailer"
47 
48 #define ipgMax 2000	/* Maximum number of pages - first page index is 0 */
49 #define irngMax 30	/* Maximum number of intervals in a page range spec */
50 
51 struct Range	/* continuous interval of pages to be printed */
52 			/* from a page range specification (i.e. -s) */
53 			/* page numbers match the second parameter */
54 			/* of the "%%Page: " comment */
55     { int	pgnFst;
56     int		pgnLst;
57     };
58 struct Params	/* interesting info collected from command line */
59     { int	fReverse; /* true => reverse page order */
60     int		fRemove;  /* true => remove input file */
61     char	*scSrcFile; /* c-string containing source file name */
62     char	*scDstFile; /* c-string containing destination file name */
63     int		irngMac;  /* number of intervals in rgrange */
64     struct Range rgrange[irngMax]; /* array of intervals of pages */
65                                      /* to be printed.  One entry per */
66                                      /* entry in the "-s" parameter */
67     };
68 
69 struct Psd	/* PS file descriptor */
70     { FILE	*fp;	/* unix file descriptor  of source */
71     FILE	*fpTemp;/* temp file descriptor.  Contains a
72 			   copy of source when fd is not a regular file */
73     long int	posLineFst, posMac;
74 			/* file positions - current, beginning of line, eof */
75     };
76 
77 private struct stat S;
78 private char *prog;
79 
80 
81 /* Reads a signed integer from c-string */
82 /* Input is a pointer to pointer to string */
83 /* On return, it points to first character following the parsed numeral */
IntGet(prgch)84 private IntGet(prgch)
85 char	**prgch;
86 { int	i, fNeg;
87 char	*rgch, ch;
88 
89 i = 0; rgch = *prgch;
90 if ((ch = *rgch) == '-')
91      { fNeg = TRUE; rgch++;}
92 else { if (ch == '+') rgch++; fNeg = FALSE;}
93 while (((ch = *rgch) >= '0') && (ch <= '9'))
94     { rgch++; i = 10*i + (ch - '0');}
95 *prgch = rgch;
96 return(fNeg ? -i : i);
97 }
98 
99 
100 /* Parses the "-s" parameter.  Fills the array params.rgrange */
101 /* with a page interval range for each entry int the comma separated list */
102 /* *prgch points at the character following the "s" command */
SetPageRanges(rgch,pparams)103 private SetPageRanges(rgch, pparams)
104 char	*rgch;
105 struct Params *pparams;
106 { char	ch;
107 char	*scError;
108 struct Range *rgrange;
109 #define pgnMax 30000
110 
111 scError = "Syntax error in page range specification while parsing ";
112 rgrange = pparams->rgrange;
113 while (TRUE)
114     {
115     if ((ch = *rgch) == '-')
116         { rgrange[pparams->irngMac].pgnFst = 1;
117         rgch++;
118         rgrange[pparams->irngMac].pgnLst = IntGet(&rgch);
119         }
120     else
121         {
122         rgrange[pparams->irngMac].pgnFst = IntGet(&rgch);
123         if ((ch = *rgch) == '-')
124             { rgch++;
125             if ((( ch = *rgch) == ',') || (ch == '\0'))
126                  rgrange[pparams->irngMac].pgnLst = pgnMax;
127             else rgrange[pparams->irngMac].pgnLst = IntGet(&rgch);
128             }
129         else if ((( ch = *rgch) == ',') || (ch == '\0'))
130              { rgrange[pparams->irngMac].pgnLst =
131                      rgrange[pparams->irngMac].pgnFst;
132 	     }
133         else if (rgrange[pparams->irngMac].pgnFst > 0)
134              { fprintf(stderr, "%s%s\n", scError, rgch);
135              exit(4);
136              }
137         }
138     if ((rgrange[pparams->irngMac].pgnFst == 0) ||
139         (rgrange[pparams->irngMac].pgnLst == 0))
140             { fprintf(stderr, "%s%s\n",
141                               scError, rgch);
142              exit(4);
143 	    }
144     if (++pparams->irngMac >= irngMax)
145         { fprintf(stderr, "Too many intervals in page range specificaiton\n");
146         exit(4);
147 	}
148     if ((ch = *rgch) == ',')
149         rgch++;
150     else if (ch == '\0')
151          break;
152     else return;
153     }
154 }
155 
156 
157 /* Returns TRUE if ipg is to be printed; i.e. if it is in one of the
158    intervals in pparams->rgrange[0..pparams->irngMac) */
FPageInRange(pgn,pparams)159 private FPageInRange(pgn, pparams)
160 int pgn;
161 struct Params *pparams;
162 { int	irng;
163 struct Range *rgrange;
164 #define ichTMax 50
165 #define ichColon 6
166 
167 if (pparams->irngMac == 0) return(TRUE);
168 rgrange = pparams->rgrange;
169 for (irng = 0; irng < pparams->irngMac; irng++)
170     { if ((pgn >= rgrange[irng].pgnFst)
171             && (pgn <= rgrange[irng].pgnLst))
172         return(TRUE);
173     }
174 return(FALSE);
175 }
176 
177 
178 /* Reads a line from the source PS file */
179 /* Fills the c-string, scDst, with the first ichMacRead characters */
180 /* of the line.  Returns TRUE if it hits end of file, FALSE otherwise */
FEofReadSc(scDst,ichMacRead,ppsd)181 private FEofReadSc(scDst, ichMacRead, ppsd)
182 char	scDst[];
183 int	ichMacRead;
184 struct Psd *ppsd;
185 { int	ich;
186 int	ch;
187 static int ateof = FALSE;
188 
189 scDst[0] = '\0';
190 ppsd->posLineFst = ftell(ppsd->fp);
191 if (ateof) return(TRUE);
192 for (ich = 0; ich < ichMacRead; ich++)
193     { scDst[ich] = ch = getc(ppsd->fp);
194     if ((ch == '\n') || (ch == EOF)) break;
195     }
196 scDst[ich] = '\0';
197 while ((ch != '\n') && (ch != EOF))
198     { ch = getc(ppsd->fp);
199     }
200 if (ch == EOF) {
201     ateof = TRUE;
202     return (ich == 0);
203 }
204 return(FALSE);
205 }
206 
207 /* Returns TRUE if the source is a conforming PS file; */
208 /* i.e. first line of the source PS file contains "%!PS-Adobe-" */
FConforming(ppsd)209 private FConforming(ppsd)
210 struct Psd *ppsd;
211 {
212 #define ichMaxVerId 11
213 char	scVerIdT[ichMaxVerId+1];
214 
215 if (!FEofReadSc(scVerIdT, ichMaxVerId, ppsd)) {
216     if (strcmp(scVerIdT, SCVERID) == 0) return(TRUE);
217 }
218 return(FALSE);
219 }
220 
221 /* Finds the beginning of pages.  Loads rgposPage with the file position */
222 /* of the "%%Page:" comment line */
FindPageStarts(ppsd,rgposPage,rgpgnPage,pipgMac)223 private FindPageStarts(ppsd, rgposPage, rgpgnPage, pipgMac)
224 struct Psd *ppsd;
225 long	rgposPage[];
226 int	rgpgnPage[];
227 int	*pipgMac;
228 {
229 #define ichMaxScT 40
230 char scT[ichMaxScT+1];
231 char *scT1;
232 
233 while (1)
234     { if (FEofReadSc(scT, ichMaxScT, ppsd))
235         { rgposPage[*pipgMac] = ppsd->posLineFst; break;}
236     if (strncmp(scT, SCPAGE, ichMaxPage) == 0)
237         { rgposPage[*pipgMac] = ppsd->posLineFst;
238         scT1 = &scT[ichColon+1];
239         /* skip blanks */
240         while (*scT1 == ' ') scT1++;
241         /* skip label */
242         while ((*scT1 != ' ') && (*scT1 != '\n')) scT1++;
243         /* skip blanks */
244         while (*scT1 == ' ') scT1++;
245         rgpgnPage[*pipgMac] = IntGet(&scT1);
246         if ((*pipgMac) < (ipgMax-1))
247             (*pipgMac)++;
248         else break;
249         }
250     else if (strncmp(scT, SCTRAILER, ichMaxTrailer) == 0)
251         { rgposPage[*pipgMac] = ppsd->posLineFst;
252         break;
253 	}
254     }
255 while (! FEofReadSc(scT, 1, ppsd)) ;
256 ppsd->posMac = ppsd->posLineFst;
257 }
258 
259 /* Move the bytes from posFst to posLim from source to destination PS file */
MovePage(fdPsSrc,posFst,posLim,fdPsDst)260 private MovePage(fdPsSrc, posFst, posLim, fdPsDst)
261 int	fdPsSrc;
262 long int posFst, posLim;
263 int	fdPsDst;
264 {
265 #define ichMaxBuf 4096
266 char	rgchBuf[ichMaxBuf];
267 int cchRead, cchMove, cchWrite;
268 register unsigned nbr;
269 
270 VOIDC lseek(fdPsSrc, posFst, 0);
271 cchMove = posLim - posFst;
272 while (cchMove > 0)
273     {	nbr = (unsigned) (cchMove < ichMaxBuf) ? cchMove : ichMaxBuf;
274 	cchRead = read(fdPsSrc, rgchBuf, nbr);
275     if ((cchRead != ichMaxBuf) && (cchRead != cchMove))
276         { fprintf(stderr,"%s: problem reading source file\n",prog);
277 	  exit(2);
278 	}
279     cchWrite = write(fdPsDst, rgchBuf, (unsigned) cchRead);
280     if (cchWrite != cchRead)
281         { fprintf(stderr,"%s: problem writing new file\n",prog);
282 	  exit(2);
283 	}
284     cchMove = cchMove - cchRead;
285     }
286 }
287 
288 #define ARGS "Rrp:s:"
289 
main(argc,argv)290 main(argc, argv)
291 int	argc;
292 char  *argv[];
293 {
294     int	ipgMac, ipgT;
295     struct Psd psd;
296     struct	Params	params;
297     long rgposPage[ipgMax+1];
298     int	rgpgnPage[ipgMax+1];
299     FILE	*fpPsDst;
300     char	scTemp[50], *tempdir;
301     int	fSpooledPage;
302     int	fdPsSrc, fdPsDst;
303     int	ch;
304     register int argp;
305     extern int optind;
306     extern char *optarg;
307 
308     prog = *argv;
309     if ((tempdir = envget("PSTEMPDIR")) == NULL) tempdir = TempDir;
310     VOIDC mstrcat(scTemp, tempdir, REVTEMP,sizeof scTemp);
311     ipgMac = 0;
312     params.scSrcFile = NULL;
313     params.scDstFile = NULL;
314     params.fReverse = TRUE;
315     params.fRemove = FALSE;
316     params.irngMac = 0;
317 
318     psd.fp = NULL;
319     psd.fpTemp = NULL;
320 
321     /* process the command line arguments */
322     while ((argp = getopt(argc,argv,ARGS)) != EOF) {
323 	switch (argp) {
324 	    case 'r':
325 		params.fRemove = TRUE;
326 		break;
327 	    case 'R':
328 		params.fReverse = FALSE;
329 		break;
330 	    case 'p':
331 		params.scDstFile = optarg;
332 		break;
333 	    case 's':
334 		SetPageRanges(optarg,&params);
335 		break;
336 	    case '?':
337 	    default:
338 		fprintf(stderr,"%s: unknown option -%c\n",prog,argp);
339 		exit(2);
340 	}
341     }
342     if (optind < argc) {
343 	params.scSrcFile = argv[optind];
344 	if ((psd.fp = fopen(argv[optind],"r")) == NULL) {
345 	    fprintf(stderr,"%s: can't open %s\n",prog,params.scSrcFile);
346 	    exit(2);
347 	}
348     }
349     else psd.fp = stdin;
350 
351     VOIDC fstat(fileno(psd.fp), &S);
352     /* if its not a regular file then copy it to a temp file and use */
353     /* the temp from now on */
354     if ((S.st_mode & S_IFMT) != S_IFREG) {
355 	VOIDC mktemp(scTemp);
356 	if ((psd.fpTemp = fopen(scTemp, "w")) == NULL) {
357 	    fprintf(stderr,"%s: could not open temp file\n",prog);
358 	    exit(2);
359 	}
360 	while ((ch = getc(psd.fp)) != EOF) putc(ch, psd.fpTemp);
361 	VOIDC fclose(psd.fpTemp);
362 	psd.fpTemp = fopen(scTemp, "r");
363 	VOIDC unlink(scTemp);
364 	psd.fp = psd.fpTemp;
365     }
366 
367     if (FConforming(&psd)) {
368 	FindPageStarts(&psd, rgposPage, rgpgnPage, &ipgMac);
369 	if (ipgMac == ipgMax) {
370 	    fprintf(stderr, "%s: Too many pages in PS file, sorry\n",prog);
371 	    exit(1);
372 	}
373 	if (rgposPage[ipgMac] == psd.posMac) {
374 	    fprintf(stderr,"%s: PS file does not contain a conforming trailer\n",prog);
375 	    exit(1);
376 	}
377 	/* remove the input file if it was requested from */
378 	/* command line */
379 	if (params.fRemove) {
380 	    VOIDC unlink(params.scSrcFile);
381 	    params.scSrcFile = NULL;
382 	}
383 	if (params.scDstFile == NULL) {
384 	    fpPsDst = stdout;
385 	}
386 	else {
387 	    /* remove the input file if output file = input file */
388 	    if ((params.scSrcFile != NULL)
389 	    && (strcmp(params.scSrcFile, params.scDstFile) == 0))
390 		VOIDC unlink(params.scSrcFile);
391 	    fpPsDst = fopen(params.scDstFile, "w");
392 	    if (fpPsDst == NULL) {
393 		fprintf(stderr, "%s: could not open output file %s\n",
394 			    prog, params.scDstFile);
395 		exit(1);
396 	    }
397 	}
398 	fdPsSrc = fileno(psd.fp);
399 	fdPsDst = fileno(fpPsDst);
400 	MovePage(fdPsSrc, 0L, rgposPage[0], fdPsDst);
401 	fSpooledPage = FALSE;
402 	ipgT = params.fReverse ? ipgMac - 1 : 0;
403 	while (TRUE) {
404 	    if (FPageInRange(rgpgnPage[ipgT], &params)) {
405 		fSpooledPage = TRUE;
406 		MovePage(fdPsSrc,rgposPage[ipgT], rgposPage[ipgT+1], fdPsDst);
407 	    }
408 	    if (params.fReverse) {
409 		if (ipgT == 0) break;
410 		ipgT--;
411 	    }
412 	    else {
413 		ipgT++;
414 		if (ipgT == ipgMac) break;
415 	    }
416 	}
417 	if (!fSpooledPage)
418 	    fprintf(stderr, "%s: No pages in specified range!\n",prog);
419 	MovePage(fdPsSrc, rgposPage[ipgMac], psd.posMac, fdPsDst);
420 	VOIDC fclose(fpPsDst);
421 	if (psd.fpTemp != NULL) VOIDC fclose(psd.fpTemp);
422 	else VOIDC fclose(psd.fp);
423 	exit(0);
424     }
425     fprintf(stderr,"%s: PS file does not begin with a version identifier\n",
426     	prog);
427     exit(1);
428 }
429