Annotation of researchv10no/cmd/post.src/postreverse/postreverse.c, revision 1.1

1.1     ! root        1: /*
        !             2:  *
        !             3:  * postreverse - reverse the page order in certain PostScript files.
        !             4:  *
        !             5:  * Page reversal relies on being able to locate sections of a document using file
        !             6:  * structuring comments defined by Adobe (ie. the 1.0 and now 2.0 conventions) and
        !             7:  * a few I've added. Among other things a minimally conforming document, according
        !             8:  * to the 1.0 conventions,
        !             9:  *
        !            10:  *     1) Marks the end of the prologue with an %%EndProlog comment.
        !            11:  *
        !            12:  *     2) Starts each page with a %%Page: comment.
        !            13:  *
        !            14:  *     3) Marks the end of all the pages %%Trailer comment.
        !            15:  *
        !            16:  *     4) Obeys page independence (ie. pages can be arbitrarily rearranged).
        !            17:  *
        !            18:  * The most important change (at least for this program) that Adobe made in going
        !            19:  * from the 1.0 to the 2.0 structuring conventions was in the prologue. They now
        !            20:  * say the prologue should only define things, and the global initialization that
        !            21:  * was in the prologue (1.0 conventions) should now come after the %%EndProlog
        !            22:  * comment but before the first %%Page: comment and be bracketed by %%BeginSetup
        !            23:  * and %%EndSetup comments. So a document that conforms to Adobe's 2.0 conventions,
        !            24:  *
        !            25:  *     1) Marks the end of the prologue (only definitions) with %%EndProlog.
        !            26:  *
        !            27:  *     2) Brackets global initialization with %%BeginSetup and %%EndSetup comments
        !            28:  *        which come after the prologue but before the first %Page: comment.
        !            29:  *
        !            30:  *     3) Starts each page with a %%Page: comment.
        !            31:  *
        !            32:  *     4) Marks the end of all the pages with a %%Trailer comment.
        !            33:  *
        !            34:  *     5) Obeys page independence.
        !            35:  *
        !            36:  * postreverse can handle documents that follow the 1.0 or 2.0 conventions, but has
        !            37:  * also been extended slightly so it works properly with the translators (primarily
        !            38:  * dpost) supplied with this package. The page independence requirement has been
        !            39:  * relaxed some. In particular definitions exported to the global environment from
        !            40:  * within a page should be bracketed by %%BeginGlobal and %%EndGlobal comments.
        !            41:  * postreverse pulls them out of each page and inserts them in the setup section
        !            42:  * of the document, immediately before it writes the %%EndProlog (for version 1.0)
        !            43:  * or %%EndSetup (for version 2.0) comments.
        !            44:  *
        !            45:  * In addition postreverse accepts documents that choose to mark the end of each
        !            46:  * page with a %%EndPage: comment, which from a translator's point of view is often
        !            47:  * a more natural approach. Both page boundary comments (ie. Page: and %%EndPage:)
        !            48:  * are also accepted, but be warned that everything between consecutive %%EndPage:
        !            49:  * and %%Page: comments will be ignored.
        !            50:  *
        !            51:  * So a document that will reverse properly with postreverse,
        !            52:  *
        !            53:  *     1) Marks the end of the prologue with %%EndProlog.
        !            54:  *
        !            55:  *     2) May have a %%BeginSetup/%%EndSetup comment pair before the first %%Page:
        !            56:  *        comment that brackets any global initialization.
        !            57:  *
        !            58:  *     3) Marks the start of each page with a %%Page: comment, or the end of each
        !            59:  *        page with a %%EndPage: comment. Both page boundary comments are allowed.
        !            60:  *
        !            61:  *     4) Marks the end of all the pages with a %%Trailer comment.
        !            62:  *
        !            63:  *     5) Obeys page independence or violates it to a rather limited extent and
        !            64:  *        marks the violations with %%BeginGlobal and %%EndGlobal comments.
        !            65:  *
        !            66:  * If no file arguments are given postreverse copies stdin to a temporary file and
        !            67:  * then processes that file. That means the input is read three times (rather than
        !            68:  * two) whenever we handle stdin. That's expensive, and shouldn't be too difficult
        !            69:  * to fix, but I haven't gotten around to it yet.
        !            70:  *
        !            71:  */
        !            72: 
        !            73: #include <stdio.h>
        !            74: #include <signal.h>
        !            75: #include <fcntl.h>
        !            76: 
        !            77: #include "comments.h"                  /* PostScript file structuring comments */
        !            78: #include "gen.h"                       /* general purpose definitions */
        !            79: #include "path.h"                      /* for temporary directory */
        !            80: #include "ext.h"                       /* external variable declarations */
        !            81: #include "postreverse.h"               /* a few special definitions */
        !            82: 
        !            83: int    page = 1;                       /* current page number */
        !            84: int    forms = 1;                      /* forms per page in the input file */
        !            85: 
        !            86: char   *temp_dir = TEMPDIR;            /* temp directory for copying stdin */
        !            87: 
        !            88: Pages  pages[1000];                    /* byte offsets for all pages */
        !            89: int    next_page = 0;                  /* next page goes here */
        !            90: long   start;                          /* starting offset for next page */
        !            91: long   endoff = -1;                    /* offset where TRAILER was found */
        !            92: int    noreverse = FALSE;              /* don't reverse pages if TRUE */
        !            93: char   *endprolog = ENDPROLOG;         /* occasionally changed to ENDSETUP */
        !            94: 
        !            95: double version = 3.3;                  /* of the input file */
        !            96: int    ignoreversion = FALSE;          /* ignore possible forms.ps problems */
        !            97: 
        !            98: char   buf[2048];                      /* line buffer for input file */
        !            99: 
        !           100: FILE   *fp_in;                         /* stuff is read from this file */
        !           101: FILE   *fp_out = stdout;               /* and written here */
        !           102: 
        !           103: /*****************************************************************************/
        !           104: 
        !           105: main(agc, agv)
        !           106: 
        !           107:     int                agc;
        !           108:     char       *agv[];
        !           109: 
        !           110: {
        !           111: 
        !           112: /*
        !           113:  *
        !           114:  * A simple program that reverses the pages in specially formatted PostScript
        !           115:  * files. Will work with all the translators in this package, and should handle
        !           116:  * any document that conforms to Adobe's version 1.0 or 2.0 file structuring
        !           117:  * conventions. Only one input file is allowed, and it can either be a named (on
        !           118:  * the command line) file or stdin.
        !           119:  *
        !           120:  */
        !           121: 
        !           122:     argc = agc;                                /* other routines may want them */
        !           123:     argv = agv;
        !           124: 
        !           125:     prog_name = argv[0];               /* just for error messages */
        !           126: 
        !           127:     init_signals();                    /* sets up interrupt handling */
        !           128:     options();                         /* first get command line options */
        !           129:     arguments();                       /* then process non-option arguments */
        !           130:     done();                            /* and clean things up */
        !           131: 
        !           132:     exit(x_stat);                      /* not much could be wrong */
        !           133: 
        !           134: }   /* End of main */
        !           135: 
        !           136: /*****************************************************************************/
        !           137: 
        !           138: init_signals()
        !           139: 
        !           140: {
        !           141: 
        !           142: /*
        !           143:  *
        !           144:  * Makes sure we handle interrupts properly.
        !           145:  *
        !           146:  */
        !           147: 
        !           148:     if ( signal(SIGINT, interrupt) == SIG_IGN )  {
        !           149:        signal(SIGINT, SIG_IGN);
        !           150:        signal(SIGQUIT, SIG_IGN);
        !           151:        signal(SIGHUP, SIG_IGN);
        !           152:     } else {
        !           153:        signal(SIGHUP, interrupt);
        !           154:        signal(SIGQUIT, interrupt);
        !           155:     }   /* End else */
        !           156: 
        !           157:     signal(SIGTERM, interrupt);
        !           158: 
        !           159: }   /* End of init_signals */
        !           160: 
        !           161: /*****************************************************************************/
        !           162: 
        !           163: options()
        !           164: 
        !           165: {
        !           166: 
        !           167:     int                ch;                     /* return value from getopt() */
        !           168:     char       *optnames = "n:o:rvT:DI";
        !           169: 
        !           170:     extern char        *optarg;                /* used by getopt() */
        !           171:     extern int optind;
        !           172: 
        !           173: /*
        !           174:  *
        !           175:  * Reads and processes the command line options. The -r option (ie. the one that
        !           176:  * turns page reversal off) is really only useful if you want to take dpost output
        !           177:  * and produce a page independent output file. In that case global definitions
        !           178:  * made within pages and bracketed by %%BeginGlobal/%%EndGlobal comments will be
        !           179:  * moved into the prologue or setup section of the document.
        !           180:  *
        !           181:  */
        !           182: 
        !           183:     while ( (ch = getopt(argc, argv, optnames)) != EOF )  {
        !           184:        switch ( ch )  {
        !           185:            case 'n':                   /* forms per page */
        !           186:                    if ( (forms = atoi(optarg)) <= 0 )
        !           187:                        error(FATAL, "illegal forms request %s", optarg);
        !           188:                    break;
        !           189: 
        !           190:            case 'o':                   /* output page list */
        !           191:                    out_list(optarg);
        !           192:                    break;
        !           193: 
        !           194:            case 'r':                   /* don't reverse the pages */
        !           195:                    noreverse = TRUE;
        !           196:                    break;
        !           197: 
        !           198:            case 'v':                   /* ignore possible forms.ps problems */
        !           199:                    ignoreversion = TRUE;
        !           200:                    break;
        !           201: 
        !           202:            case 'T':                   /* temporary file directory */
        !           203:                    temp_dir = optarg;
        !           204:                    break;
        !           205: 
        !           206:            case 'D':                   /* debug flag */
        !           207:                    debug = ON;
        !           208:                    break;
        !           209: 
        !           210:            case 'I':                   /* ignore FATAL errors */
        !           211:                    ignore = ON;
        !           212:                    break;
        !           213: 
        !           214:            case '?':                   /* don't understand the option */
        !           215:                    error(FATAL, "");
        !           216:                    break;
        !           217: 
        !           218:            default:                    /* don't know what to do for ch */
        !           219:                    error(FATAL, "missing case for option %c\n", ch);
        !           220:                    break;
        !           221:        }   /* End switch */
        !           222:     }   /* End while */
        !           223: 
        !           224:     argc -= optind;                    /* get ready for non-option args */
        !           225:     argv += optind;
        !           226: 
        !           227: }   /* End of options */
        !           228: 
        !           229: /*****************************************************************************/
        !           230: 
        !           231: arguments()
        !           232: 
        !           233: {
        !           234: 
        !           235:     char       *name;                  /* name of the input file */
        !           236: 
        !           237: /*
        !           238:  *
        !           239:  * postreverse only handles one input file at a time, so if there's more than one
        !           240:  * argument left when we get here we'll quit. If none remain we copy stdin to a
        !           241:  * temporary file and process that file.
        !           242:  *
        !           243:  */
        !           244: 
        !           245:     if ( argc > 1 )                    /* can't handle more than one file */
        !           246:        error(FATAL, "too many arguments");
        !           247: 
        !           248:     if ( argc == 0 )                   /* copy stdin to a temporary file */
        !           249:        name = copystdin();
        !           250:     else name = *argv;
        !           251: 
        !           252:     if ( (fp_in = fopen(name, "r")) == NULL )
        !           253:        error(FATAL, "can't open %s", name);
        !           254: 
        !           255:     reverse();
        !           256: 
        !           257: }   /* End of arguments */
        !           258: 
        !           259: /*****************************************************************************/
        !           260: 
        !           261: done()
        !           262: 
        !           263: {
        !           264: 
        !           265: /*
        !           266:  *
        !           267:  * Cleans things up after we've finished reversing the pages in the input file.
        !           268:  * All that's really left to do is remove the temp file, provided we used one.
        !           269:  *
        !           270:  */
        !           271: 
        !           272:     if ( temp_file != NULL )
        !           273:        unlink(temp_file);
        !           274: 
        !           275: }   /* End of done */
        !           276: 
        !           277: /*****************************************************************************/
        !           278: 
        !           279: char *copystdin()
        !           280: 
        !           281: {
        !           282: 
        !           283:     int                fd_out;                 /* for the temporary file */
        !           284:     int                fd_in;                  /* for stdin */
        !           285:     int                count;                  /* number of bytes put in buf[] */
        !           286: 
        !           287: /*
        !           288:  *
        !           289:  * Copies stdin to a temporary file and returns the pathname of that file to the
        !           290:  * caller. It's an expensive way of doing things, because it means we end up
        !           291:  * reading the input file three times - rather than just twice. Could probably be
        !           292:  * fixed by creating the temporary file on the fly as we read the file the first
        !           293:  * time.
        !           294:  *
        !           295:  */
        !           296: 
        !           297:     if ( (temp_file = tempnam(temp_dir, "post")) == NULL )
        !           298:        error(FATAL, "can't generate temp file name");
        !           299: 
        !           300:     if ( (fd_out = creat(temp_file, 0660)) == -1 )
        !           301:        error(FATAL, "can't open %s", temp_file);
        !           302: 
        !           303:     fd_in = fileno(stdin);
        !           304: 
        !           305:     while ( (count = read(fd_in, buf, sizeof(buf))) > 0 )
        !           306:        if ( write(fd_out, buf, count) != count )
        !           307:            error(FATAL, "error writing to %s", temp_file);
        !           308: 
        !           309:     close(fd_out);
        !           310: 
        !           311:     return(temp_file);
        !           312: 
        !           313: }   /* End of copystdin */
        !           314: 
        !           315: /*****************************************************************************/
        !           316: 
        !           317: reverse()
        !           318: 
        !           319: {
        !           320: 
        !           321: /*
        !           322:  *
        !           323:  * Begins by looking for the ENDPROLOG comment in the input file. Everything up to
        !           324:  * that comment is copied to the output file. If the comment isn't found the entire
        !           325:  * input file is copied and moreprolog() returns FALSE. Otherwise readpages() reads
        !           326:  * the rest of the input file and remembers (in pages[]) where each page starts and
        !           327:  * ends. In addition everything bracketed by %%BeginGlobal and %%EndGlobal comments
        !           328:  * is immediately added to the new prologue (or setup section) and ends up being
        !           329:  * removed from the individual pages. When readpages() finds the TRAILER comment
        !           330:  * or gets to the end of the input file we go back to the pages[] array and use
        !           331:  * the saved offsets to write the pages out in reverse order. Finally everything
        !           332:  * from the TRAILER comment to the end of the input file is copied to the output
        !           333:  * file.
        !           334:  *
        !           335:  */
        !           336: 
        !           337:     if ( moreprolog(ENDPROLOG) == TRUE )  {
        !           338:        readpages();
        !           339:        writepages();
        !           340:        trailer();
        !           341:     }  /* End if */
        !           342: 
        !           343: }   /* End of reverse */
        !           344: 
        !           345: /*****************************************************************************/
        !           346: 
        !           347: moreprolog(str)
        !           348: 
        !           349:     char       *str;                   /* copy everything up to this string */
        !           350: 
        !           351: {
        !           352: 
        !           353:     int                len;                    /* length of FORMSPERPAGE string */
        !           354:     int                vlen;                   /* length of VERSION string */
        !           355: 
        !           356: /*
        !           357:  *
        !           358:  * Looks for string *str at the start of a line and copies everything up to that
        !           359:  * string to the output file. If *str isn't found the entire input file will end
        !           360:  * up being copied to the output file and FALSE will be returned to the caller.
        !           361:  * The first call (made from reverse()) looks for ENDPROLOG. Any other call comes
        !           362:  * from readpages() and will be looking for the ENDSETUP comment.
        !           363:  * 
        !           364:  */
        !           365: 
        !           366:     len = strlen(FORMSPERPAGE);
        !           367:     vlen = strlen(VERSION);
        !           368: 
        !           369:     while ( fgets(buf, sizeof(buf), fp_in) != NULL )  {
        !           370:        if ( strcmp(buf, str) == 0 )
        !           371:            return(TRUE);
        !           372:        else if ( strncmp(buf, FORMSPERPAGE, len) == 0 )
        !           373:            forms = atoi(&buf[len+1]);
        !           374:        else if ( strncmp(buf, VERSION, vlen) == 0 )
        !           375:            version = atof(&buf[vlen+1]);
        !           376:        fprintf(fp_out, "%s", buf);
        !           377:     }  /* End while */
        !           378: 
        !           379:     return(FALSE);
        !           380: 
        !           381: }   /* End of moreprolog */
        !           382: 
        !           383: /*****************************************************************************/
        !           384: 
        !           385: readpages()
        !           386: 
        !           387: {
        !           388: 
        !           389:     int                endpagelen;             /* length of ENDPAGE */
        !           390:     int                pagelen;                /* and PAGE strings */
        !           391:     int                sawendpage = TRUE;      /* ENDPAGE equivalent marked last page */
        !           392:     int                gotpage = FALSE;        /* TRUE disables BEGINSETUP stuff */
        !           393: 
        !           394: /*
        !           395:  *
        !           396:  * Records starting and ending positions of the requested pages (usually all of
        !           397:  * them), puts global definitions in the prologue, and remembers where the TRAILER
        !           398:  * was found.
        !           399:  *
        !           400:  * Page boundaries are marked by the strings PAGE, ENDPAGE, or perhaps both.
        !           401:  * Application programs will normally find one or the other more convenient, so
        !           402:  * in most cases only one kind of page delimiter will be found in a particular
        !           403:  * document.
        !           404:  *
        !           405:  */
        !           406: 
        !           407:     pages[0].start = ftell(fp_in);     /* first page starts after ENDPROLOG */
        !           408:     endprolog = ENDPROLOG;
        !           409: 
        !           410:     endpagelen = strlen(ENDPAGE);
        !           411:     pagelen = strlen(PAGE);
        !           412: 
        !           413:     while ( fgets(buf, sizeof(buf), fp_in) != NULL )
        !           414:        if ( buf[0] != '%' )
        !           415:            continue;
        !           416:        else if ( strncmp(buf, ENDPAGE, endpagelen) == 0 )  {
        !           417:            if ( in_olist(page++) == ON )  {
        !           418:                pages[next_page].empty = FALSE;
        !           419:                pages[next_page++].stop = ftell(fp_in);
        !           420:            }   /* End if */
        !           421:            pages[next_page].start = ftell(fp_in);
        !           422:            sawendpage = TRUE;
        !           423:            gotpage = TRUE;
        !           424:        } else if ( strncmp(buf, PAGE, pagelen) == 0 )  {
        !           425:            if ( sawendpage == FALSE && in_olist(page++) == ON )  {
        !           426:                pages[next_page].empty = FALSE;
        !           427:                pages[next_page++].stop = ftell(fp_in) - strlen(buf);
        !           428:            }   /* End if */
        !           429:            pages[next_page].start = ftell(fp_in) - strlen(buf);
        !           430:            sawendpage = FALSE;
        !           431:            gotpage = TRUE;
        !           432:        } else if ( gotpage == FALSE && strcmp(buf, BEGINSETUP) == 0 )  {
        !           433:            fprintf(fp_out, "%s", endprolog);
        !           434:            fprintf(fp_out, "%s", BEGINSETUP);
        !           435:            moreprolog(ENDSETUP);
        !           436:            endprolog = ENDSETUP;
        !           437:        } else if ( strcmp(buf, BEGINGLOBAL) == 0 )  {
        !           438:            moreprolog(ENDGLOBAL);
        !           439:        } else if ( strcmp(buf, TRAILER) == 0 )  {
        !           440:            if ( sawendpage == FALSE )
        !           441:                pages[next_page++].stop = ftell(fp_in) - strlen(buf);
        !           442:            endoff = ftell(fp_in);
        !           443:            break;
        !           444:        }   /* End if */
        !           445: 
        !           446: }   /* End of readpages */
        !           447: 
        !           448: /*****************************************************************************/
        !           449: 
        !           450: writepages()
        !           451: 
        !           452: {
        !           453: 
        !           454:     int                i, j, k;                /* loop indices */
        !           455: 
        !           456: /*
        !           457:  *
        !           458:  * Goes through the pages[] array, usually from the bottom up, and writes out all
        !           459:  * the pages. Documents that print more than one form per page cause things to get
        !           460:  * a little more complicated. Each physical page has to have its subpages printed
        !           461:  * in the correct order, and we have to build a few dummy subpages for the last
        !           462:  * (and now first) sheet of paper, otherwise things will only occasionally work.
        !           463:  *
        !           464:  */
        !           465: 
        !           466:     fprintf(fp_out, "%s", endprolog);
        !           467: 
        !           468:     if ( noreverse == FALSE )          /* fill out the first page */
        !           469:        for ( i = (forms - next_page % forms) % forms; i > 0; i--, next_page++ )
        !           470:            pages[next_page].empty = TRUE;
        !           471:     else forms = next_page;            /* turns reversal off in next loop */
        !           472: 
        !           473:     for ( i = next_page - forms; i >= 0; i -= forms )
        !           474:        for ( j = i, k = 0; k < forms; j++, k++ )
        !           475:            if ( pages[j].empty == TRUE ) {
        !           476:                if ( ignoreversion == TRUE || version > 3.1 ) {
        !           477:                    fprintf(fp_out, "%s 0 0\n", PAGE);
        !           478:                    fprintf(fp_out, "/saveobj save def\n");
        !           479:                    fprintf(fp_out, "showpage\n");
        !           480:                    fprintf(fp_out, "saveobj restore\n");
        !           481:                    fprintf(fp_out, "%s 0 0\n", ENDPAGE);
        !           482:                } else {
        !           483:                    fprintf(fp_out, "%s 0 0\n", PAGE);
        !           484:                    fprintf(fp_out, "save showpage restore\n");
        !           485:                    fprintf(fp_out, "%s 0 0\n", ENDPAGE);
        !           486:                }   /* End else */
        !           487:            } else copypage(pages[j].start, pages[j].stop);
        !           488: 
        !           489: }   /* End of writepages */
        !           490: 
        !           491: /*****************************************************************************/
        !           492: 
        !           493: copypage(start, stop)
        !           494: 
        !           495:     long       start;                  /* starting from this offset */
        !           496:     long       stop;                   /* and ending here */
        !           497: 
        !           498: {
        !           499: 
        !           500: /*
        !           501:  *
        !           502:  * Copies the page beginning at offset start and ending at stop to the output
        !           503:  * file. Global definitions are skipped since they've already been added to the
        !           504:  * prologue.
        !           505:  *
        !           506:  */
        !           507: 
        !           508:     fseek(fp_in, start, 0);
        !           509: 
        !           510:     while ( ftell(fp_in) < stop && fgets(buf, sizeof(buf), fp_in) != NULL )
        !           511:        if ( buf[0] == '%' && strcmp(buf, BEGINGLOBAL) == 0 )
        !           512:            while ( fgets(buf, sizeof(buf), fp_in) != NULL && strcmp(buf, ENDGLOBAL) != 0 ) ;
        !           513:        else fprintf(fp_out, "%s", buf);
        !           514: 
        !           515: }   /* End of copypage */
        !           516: 
        !           517: /*****************************************************************************/
        !           518: 
        !           519: trailer()
        !           520: 
        !           521: {
        !           522: 
        !           523: /*
        !           524:  *
        !           525:  * Makes sure everything from the TRAILER string to EOF is copied to the output
        !           526:  * file.
        !           527:  *
        !           528:  */
        !           529: 
        !           530:     if ( endoff > 0 )  {
        !           531:        fprintf(fp_out, "%s", TRAILER);
        !           532:        fseek(fp_in, endoff, 0);
        !           533:        while ( fgets(buf, sizeof(buf), fp_in) != NULL )
        !           534:            fprintf(fp_out, "%s", buf);
        !           535:     }  /* End if */
        !           536: 
        !           537: }   /* End of trailer */
        !           538: 
        !           539: /*****************************************************************************/
        !           540: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.