1/*
2 * $Id: psorder.c,v 1.10 2010-04-12 14:28:47 franklahm Exp $
3 *
4 * Copyright (c) 1990,1991 Regents of The University of Michigan.
5 * All Rights Reserved.
6 *
7 * Permission to use, copy, modify, and distribute this software and
8 * its documentation for any purpose and without fee is hereby granted,
9 * provided that the above copyright notice appears in all copies and
10 * that both that copyright notice and this permission notice appear
11 * in supporting documentation, and that the name of The University
12 * of Michigan not be used in advertising or publicity pertaining to
13 * distribution of the software without specific, written prior
14 * permission. This software is supplied as is without expressed or
15 * implied warranties of any kind.
16 *
17 *	Research Systems Unix Group
18 *	The University of Michigan
19 *	c/o Mike Clark
20 *	535 W. William Street
21 *	Ann Arbor, Michigan
22 *	+1-313-763-0525
23 *	netatalk@itd.umich.edu
24 */
25
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif /* HAVE_CONFIG_H */
29
30#include <stdlib.h>
31#include <sys/types.h>
32#include <sys/param.h>
33#include <sys/time.h>
34#include <sys/stat.h>
35#include <sys/uio.h>
36#include <sys/file.h>
37#include <ctype.h>
38#include <limits.h>
39#ifdef HAVE_FCNTL_H
40#include <fcntl.h>
41#endif /* HAVE_FCNTL_H */
42#include <stdio.h>
43#include <string.h>
44#include <dirent.h>
45#ifdef HAVE_UNISTD_H
46#include <unistd.h>
47#endif /* HAVE_UNISTD_H */
48#include "pa.h"
49#include "psorder.h"
50
51#include <atalk/paths.h>
52
53/*
54 *			Global Variables
55 */
56
57static u_char			psbuf[ 8192 ];
58static struct psinfo_st		psinfo;
59static int			orderflag, forceflag;
60
61static void
62filecleanup( int errorcode, int tfd, char *tfile)
63{
64
65/*
66	Close and unlink the temporary file.
67 */
68
69    if ( tfd != 0 ) {
70	if ( close( tfd ) != 0 ) {
71	    perror( tfile );
72	    exit( errorcode );
73	}
74	if ( unlink( tfile ) != 0 ) {
75	    perror( tfile );
76	    exit( errorcode );
77	}
78    }
79
80    exit( errorcode );
81}
82
83static void
84filesetup( char *inputfile, int *infd, char *tfile, int *tfd)
85{
86    struct stat		st;
87    char		*template = _PATH_TMPPAGEORDER;
88
89    if ( strcmp( inputfile, STDIN ) != 0 ) {
90	if ( stat( inputfile, &st ) < 0 ) {
91	    perror( inputfile );
92	    filecleanup( -1, -1, "" );
93	}
94	if ( st.st_mode & S_IFMT & S_IFDIR ) {
95	    fprintf( stderr, "%s is a directory.\n", inputfile );
96	    filecleanup( 0, -1, "" );
97	}
98	if (( *infd = open( inputfile, O_RDONLY, 0600 )) < 0 ) {
99	    perror( inputfile );
100	    filecleanup( -1, -1, "" );
101	}
102    } else {
103	*infd = 0;
104    }
105
106#if DEBUG
107    fprintf( stderr, "Input file or stdin and stdout opened.\n" );
108    fprintf( stderr, "Input file descriptor is %d .\n", *infd );
109#endif /* DEBUG */
110
111/*
112	make temporary file
113 */
114
115#if defined(NAME_MAX)
116    (void *)strncpy( tfile, template, NAME_MAX );
117#else
118    (void *)strncpy( tfile, template, MAXNAMLEN );
119#endif
120    if (( *tfd = mkstemp( tfile )) == -1 ) {
121	fprintf( stderr, "can't create temporary file %s\n", tfile );
122	filecleanup( -1, -1, "" );
123    }
124
125#if DEBUG
126    fprintf( stderr, "Temporary file %s created and opened.\n", tfile );
127    fprintf( stderr, "Temporary file descriptor is %d .\n", *tfd );
128#endif /* DEBUG */
129
130    psinfo.firstpage = NULL;
131    psinfo.lastpage = NULL;
132    psinfo.trailer = 0;
133    psinfo.pages.offset = 0;
134    psinfo.pages.end = 0;
135    psinfo.pages.num[0] = '\0';
136    psinfo.pages.order[0] = '\0';
137
138    return;
139}
140
141static struct pspage_st
142*getpspage(off_t off)
143{
144    struct pspage_st	*newpspage;
145
146    newpspage = (struct pspage_st *)malloc( sizeof( struct pspage_st ));
147    if ( newpspage != NULL ) {
148	newpspage->offset = off;
149	newpspage->nextpage = NULL;
150	*newpspage->lable = '\0';
151	*newpspage->ord = '\0';
152    }
153    return( newpspage );
154}
155
156static int
157handletok(off_t count, char *token)
158{
159    int			incdoc = 0;
160    struct pspage_st	*newpage;
161    char		*tmp;
162
163    if (( strncmp( PENDDOC, token, strlen( PENDDOC )) == 0 ) && incdoc ) {
164	incdoc--;
165#if DEBUG
166	fprintf( stderr, "found an EndDoc\n" );
167#endif /* DEBUG */
168
169    } else if ( strncmp( PBEGINDOC, token, strlen( PBEGINDOC )) == 0 ) {
170	incdoc++;
171#if DEBUG
172	fprintf( stderr, "found a BeginDoc\n" );
173#endif /* DEBUG */
174
175    } else if ( !incdoc &&
176	    ( strncmp( PPAGE, token, strlen( PPAGE )) == 0 )) {
177#if DEBUG
178	fprintf( stderr, "found a Page\n" );
179#endif /* DEBUG */
180	if (( newpage = getpspage( count )) == NULL ) {
181	    return( -1 );
182	}
183	if ( psinfo.firstpage == NULL ) {
184	    newpage->prevpage = NULL;
185	    psinfo.firstpage = newpage;
186	} else {
187	    newpage->prevpage = psinfo.lastpage;
188	    psinfo.lastpage->nextpage = newpage;
189	}
190	psinfo.lastpage = newpage;
191	while ( *token++ != ':' );
192	if (( tmp = strtok( token, WHITESPACE )) != NULL ) {
193	    (void)strncpy( newpage->lable, tmp, NUMLEN );
194	    if (( tmp = strtok( NULL, WHITESPACE )) != NULL ) {
195		(void)strncpy( newpage->ord, tmp, ORDLEN );
196	    }
197	}
198#if DEBUG
199	fprintf( stderr, "page lable %s, page ord %s\n", newpage->lable,
200		newpage->ord );
201#endif /* DEBUG */
202
203    } else if ( !incdoc &&
204	    ( strncmp( PPAGES, token, strlen( PPAGES )) == 0 )) {
205#if DEBUG
206	fprintf( stderr, "found a Pages\n" );
207#endif /* DEBUG */
208	psinfo.pages.offset = count;
209	psinfo.pages.end = strlen( token ) + count;
210	while ( *token++ != ':' );
211	while ( isspace( *token )) token++;
212	if ( strncmp( ATEND, token, strlen( ATEND )) == 0 ) {
213#if DEBUG
214	    fprintf( stderr, "it is a Pages: (atend)\n" );
215#endif /* DEBUG */
216	    psinfo.pages.offset = 0;
217	    psinfo.pages.end = 0;
218	} else {
219	    if (( tmp = strtok( token, WHITESPACE )) != NULL ) {
220		(void)strncpy( psinfo.pages.num, tmp, NUMLEN );
221		if (( tmp = strtok( NULL, WHITESPACE )) != NULL ) {
222		    (void)strncpy( psinfo.pages.order, tmp, ORDERLEN );
223		}
224	    }
225#if DEBUG
226	    fprintf( stderr, "number of pages %s\n", psinfo.pages.num );
227	    fprintf( stderr, "order control number %s\n", psinfo.pages.order );
228#endif /* DEBUG */
229	}
230
231    } else if ( !incdoc &&
232	    ( strncmp( PTRAILER, token, strlen( PTRAILER )) == 0 )) {
233#if DEBUG
234	fprintf( stderr, "found the Trailer\n" );
235#endif /* DEBUG */
236	if  ( psinfo.trailer == 0 ) {
237	    psinfo.trailer = count;
238	}
239    }
240
241    return( 0 );
242}
243
244static void
245readps(int inputfd, int tempfd, char *tempfile)
246{
247    off_t		ccread = 0;
248    off_t		ccmatch;
249    char		*curtok = NULL;
250    FILE		*tempstream;
251    pa_buf_t		*pb;
252    int			n;
253    char		c = -1;
254    char		pc, cc = 0;
255
256    pb = pa_init( inputfd );
257    if (( tempstream = fdopen( tempfd, "w" )) == NULL ) {
258	perror( "fdopen fails for tempfile" );
259	filecleanup( -1, tempfd, tempfile );
260    }
261
262    if (( c = pa_getchar( pb )) != 0 ) {
263	ccread++;
264	(void)putc( c, tempstream );
265	pc = 0;
266	cc = c;
267	pa_match( pb );
268	n = strlen( PPSADOBE );
269	for ( ; ( n > 0 ) && (( c = pa_getchar( pb )) != 0 ) ; n-- ) {
270	    ccread++ ;
271	    (void)putc( c, tempstream );
272	    pc = cc;
273	    cc = c;
274	}
275	curtok = pa_gettok( pb );
276    }
277#if DEBUG
278    fprintf( stderr, "%s\n", curtok );
279#endif /* DEBUG */
280
281/*
282 * not postscript
283 */
284    if ( strcmp( curtok, PPSADOBE ) != 0 ) {
285#if DEBUG
286    fprintf( stderr, "in the not postscript section of readps\n" );
287#endif /* DEBUG */
288	while (( c = pa_getchar( pb )) != 0 ) {
289	    ccread++;
290	    (void)putc( c, tempstream );
291	    pc = cc;
292	    cc = c;
293	}
294
295	(void)fflush( tempstream );
296	return;
297    }
298
299/*
300 * postscript
301 */
302#if DEBUG
303    fprintf( stderr, "in the postscript section of readps\n" );
304#endif /* DEBUG */
305    while (( c = pa_getchar( pb )) != 0 ) {
306	ccread++;
307	(void)putc( c, tempstream );
308	pc = cc;
309	cc = c;
310	if ((( pc == '\r' ) || ( pc == '\n' )) && ( cc == '%' )) {
311#if DEBUG
312	    fprintf( stderr, "supposed start of match, cc = %c\n", cc );
313#endif /* DEBUG */
314	    pa_match( pb );
315	    ccmatch = ccread - 1;
316	    while ( ( c = pa_getchar( pb ) ) ) {
317		if ( c != 0 ) {
318		    ccread++;
319		    (void)putc( c, tempstream );
320		    pc = cc;
321		    cc = c;
322		}
323		if (( c == '\r' ) || ( c == '\n' ) || ( cc == '\0' )) {
324		    curtok = pa_gettok( pb );
325#if DEBUG
326		    fprintf( stderr, "%s\n", curtok );
327#endif /* DEBUG */
328		    if ( handletok( ccmatch, curtok ) < 0 ) {
329			perror( "malloc died" );
330			filecleanup( -1, tempfd, tempfile );
331		    }
332		    break;
333		}
334		if ( c == 0 ) break;
335	    }
336	    if ( c == 0 ) break;
337	}
338    }
339
340    (void)fflush( tempstream );
341    return;
342}
343
344static void
345temp2out(int tempfd, char *tempfile, off_t length)
346{
347    int			ccread;
348    int			ccwrite;
349    int			size;
350
351    while ( length > 0 ) {
352	if ( length > sizeof( psbuf )) {
353	    size = sizeof( psbuf );
354	} else size = length;
355	if (( ccread = read( tempfd, psbuf, size )) > 0 ) {
356	    size = ccread;
357	    while ( ccread > 0 ) {
358		ccwrite = write( 1, psbuf, ccread );
359		if ( ccwrite < 0 ) {
360		    perror( "stdout" );
361		    filecleanup( ccwrite, tempfd, tempfile );
362		} else {
363		    ccread -= ccwrite;
364		}
365	    }
366	}
367	if ( ccread < 0 ) {
368	    perror( "temporary file" );
369	    filecleanup( ccread, tempfd, tempfile );
370	}
371	length -= size;
372    }
373}
374
375static void
376writelable(int tempfd, char *tempfile, char *lable)
377{
378    char		line[256];
379    int			ccwrite;
380    int			linelen;
381    char		*argone;
382    char		*argtwo;
383
384    if ( strcmp( lable, PPAGES ) == 0 ) {
385	argone = psinfo.pages.num;
386	argtwo = psinfo.pages.order;
387    } else {
388	argone = argtwo = NULL;
389    }
390    (void)sprintf( line, "%s %s %s", lable, argone, argtwo );
391    linelen = strlen( line );
392
393    ccwrite = write( 1, line, linelen );
394    if ( ccwrite < 0 ) {
395	perror( "stdout" );
396	filecleanup( ccwrite, tempfd, tempfile );
397    } else {
398	linelen -= ccwrite;
399    }
400}
401
402static void
403writeps(int tempfd, char *tempfile)
404{
405    struct stat		st;
406    off_t		endofpage;
407    int			order;
408
409    if ( stat( tempfile, &st ) < 0 ) {
410	perror( "stat failed" );
411	filecleanup( -1, tempfd, tempfile );
412    }
413    if ( psinfo.trailer == 0 ) {
414	endofpage = st.st_size;
415    } else endofpage = psinfo.trailer;
416
417    if (( psinfo.firstpage == NULL ) ||
418	    ( psinfo.firstpage == psinfo.lastpage )) {
419	order = FORWARD;
420    } else if ( psinfo.pages.offset == 0 ) {
421	order = orderflag;
422    } else if (( strncmp( psinfo.pages.order, "", ORDERLEN ) == 0 ) ||
423	    ( strncmp( psinfo.pages.order, "1", ORDERLEN ) == 0 )) {
424	order = orderflag;
425	if ( order == REVERSE ) strcpy( psinfo.pages.order, "-1" );
426    } else if ( strncmp( psinfo.pages.order, "-1", ORDERLEN ) == 0 ) {
427	if ( orderflag == FORWARD ) {
428	    order = REVERSE;
429	    strcpy( psinfo.pages.order, "1" );
430	} else order = FORWARD;
431    } else if (( strncmp( psinfo.pages.order, "0", ORDERLEN ) == 0 ) &&
432	    forceflag ) {
433	order = orderflag;
434    } else order = FORWARD;
435
436    if ( order == FORWARD ) {
437	temp2out( tempfd, tempfile, st.st_size );
438    } else {
439/*
440 *	output the header stuff and rewrite the $$Pages line
441 *	if it is in the header and not %%Pages: (atend)
442 */
443	if ( psinfo.firstpage->offset > 0 ) {
444	    if (( psinfo.firstpage->offset > psinfo.pages.offset ) &&
445		    ( psinfo.pages.offset != 0 )) {
446		temp2out( tempfd, tempfile, psinfo.pages.offset );
447		writelable( tempfd, tempfile, PPAGES );
448		if ( lseek( tempfd, psinfo.pages.end, SEEK_SET ) < 0 ) {
449		    perror( tempfile );
450		    filecleanup( -1, tempfd, tempfile );
451		}
452		temp2out( tempfd, tempfile,
453			psinfo.firstpage->offset - psinfo.pages.end );
454	    } else temp2out( tempfd, tempfile, psinfo.firstpage->offset );
455	}
456/*
457 *	output the pages, last to first
458 */
459	while ( psinfo.lastpage != NULL ) {
460	    if ( lseek( tempfd, psinfo.lastpage->offset, SEEK_SET ) < 0 ) {
461		perror( tempfile );
462		filecleanup( -1, tempfd, tempfile );
463	    }
464	    temp2out( tempfd, tempfile, endofpage - psinfo.lastpage->offset );
465	    endofpage = psinfo.lastpage->offset;
466	    psinfo.lastpage = psinfo.lastpage->prevpage;
467	    if ( psinfo.lastpage != NULL ) {
468		(void)free( psinfo.lastpage->nextpage );
469		psinfo.lastpage->nextpage = NULL;
470	    }
471	}
472/*
473 *	output the trailer stuff and rewrite the $$Pages line
474 *	if it is in the trailer
475 */
476	if ( psinfo.trailer != 0 ) {
477	    if ( lseek( tempfd, psinfo.trailer, SEEK_SET ) < 0 ) {
478		perror( tempfile );
479		filecleanup( -1, tempfd, tempfile );
480	    }
481	    if ( psinfo.trailer < psinfo.pages.offset ) {
482		temp2out( tempfd, tempfile,
483			psinfo.pages.offset - psinfo.trailer );
484		writelable( tempfd, tempfile, PPAGES );
485		if ( lseek( tempfd, psinfo.pages.end, SEEK_SET ) < 0 ) {
486		    perror( tempfile );
487		    filecleanup( -1, tempfd, tempfile );
488		}
489		temp2out( tempfd, tempfile, st.st_size - psinfo.pages.end );
490	    } else temp2out( tempfd, tempfile, st.st_size - psinfo.trailer );
491	}
492    }
493
494    return;
495}
496
497static int
498psorder(char *path)
499{
500    int			tempfd;
501    int			inputfd;
502#if defined(NAME_MAX)
503    char		tempfile[NAME_MAX];
504#else
505    char		tempfile[MAXNAMLEN];
506#endif
507
508    filesetup( path, &inputfd, tempfile, &tempfd );
509    readps( inputfd, tempfd, tempfile );
510    if ( lseek( tempfd, REWIND, SEEK_SET ) < 0 ) {
511	perror( tempfile );
512	filecleanup( -1, tempfd, tempfile );
513    }
514    writeps( tempfd, tempfile );
515    filecleanup( 0, tempfd, tempfile );
516    return( 0 );
517}
518
519int main(int argc, char **argv)
520{
521    extern int	optind;
522    char	*progname;
523    int		errflag = 0;
524    int		c;
525
526    while (( c = getopt( argc, argv, OPTSTR )) != -1 ) {
527	switch ( c ) {
528	case REVCHAR:
529	    if ( orderflag ) errflag++;
530	    else orderflag = REVERSE;
531	    break;
532	case FORWCHAR:
533	    if ( orderflag ) errflag++;
534	    else orderflag = FORWARD;
535	    break;
536	case FORCECHAR:
537	    if ( forceflag ) errflag++;
538	    else forceflag++;
539	    break;
540	}
541    }
542    if ( errflag ) {
543	if (( progname = strrchr( argv[ 0 ], '/' )) == NULL ) {
544	    progname = argv[ 0 ];
545	} else progname++;
546	fprintf( stderr, "usage: %s [-duf] [sourcefile]\n", progname );
547	return( -1 );
548    } else if ( !orderflag ) orderflag = FORWARD;
549
550    if ( optind >= argc ) {
551	return( psorder( STDIN ));
552    }
553    return( psorder( argv[ optind ] ));
554}
555
556