Annotation of researchv10no/cmd/post.src/postmd/postmd.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  *
                      3:  * postmd - matrix display program for PostScript printers.
                      4:  *
                      5:  * A simple program that can be used to display a matrix as a gray scale image on
                      6:  * a PostScript printer using the image operator. Much of the code was borrowed
                      7:  * from postdmd, the bitmap display program DMD screen dumps. May help if you have
                      8:  * a large matix (of floating point numbers) and want a simple way to look for
                      9:  * patterns.
                     10:  *
                     11:  * Matrix elements are a series of floating point numbers arranged in the input
                     12:  * file in row major order. The actual matrix elements can be preceeded by a simple
                     13:  * header that sets things like the matrix dimensions, interval list, and possibly
                     14:  * a window into the matrix that we'll use for display. The dimension statement is
                     15:  * perhaps the most important. If present it determines the number of rows and
                     16:  * columns in the matrix. For example, either of the following defines a 50x50
                     17:  * matrix,
                     18:  *
                     19:  *             dimension       50
                     20:  *             dimension       50x50
                     21:  *
                     22:  * If no dimension statement appears in the input file, the matrix is assumed to
                     23:  * be square, and the number of rows (and columns) is set to the square root of
                     24:  * the number of elements in the input file.
                     25:  *
                     26:  * Each matrix element is mapped into an integer in the range 0 to 255 (actually
                     27:  * 254) and PostScript's image operator then maps that number into a gray scale
                     28:  * appropriate for the particular printer. The mapping from the floating point
                     29:  * matrix elements to integers is accomplished using an interval list that can be
                     30:  * set using the -i option. The format of the interval string is,
                     31:  *
                     32:  *             num1,num2,num3,...,numn
                     33:  *
                     34:  * where each num is a floating point number. The list must be given in increasing
                     35:  * numerical order. A list of n numbers partitions the real line into 2n+1 regions
                     36:  * given as,
                     37:  *
                     38:  *             region1         element < num1
                     39:  *             region2         element = num1
                     40:  *             region3         element < num2
                     41:  *             region4         element = num2
                     42:  *                .
                     43:  *                .
                     44:  *                .
                     45:  *             region2n        element = numn
                     46:  *             region2n+1      element > numn
                     47:  *
                     48:  * Every number in a region is mapped one integer in the range 0 to 254, and that
                     49:  * number, when displayed on a printer using the image operator, prints as a square
                     50:  * filled with a gray shade that reflects the integer that was chosen. 0 maps to
                     51:  * black and 255 maps to white (which by default will not be used).
                     52:  *
                     53:  * The default gray scale gets darker as the region number increases, but can be
                     54:  * changed by supplying a gray scale list with the -g option or in the optional
                     55:  * matrix header. The color map is again a comman or space separated list that
                     56:  * looks like,
                     57:  *
                     58:  *             color1,color2, ... ,color2n+1
                     59:  *
                     60:  * where color1 applies to region 1 and color2n+1 applies to region2n+1. Each
                     61:  * number in the list should be an integer between 0 and 255. If less than 2n+1
                     62:  * colors are given default assignments will be used for missing regions.
                     63:  *
                     64:  * The size of the matrix that we can display reasonably well is a function of the
                     65:  * number of elements in the interval list, paper size, and printer resolution.
                     66:  * For example a 300dpi printer using 8.5x11 inch paper gives us an image area of
                     67:  * about 2400x2400 pixels. An interval list of two numbers generates five separate
                     68:  * regions and will therefore need that many different shades of gray. Since we're
                     69:  * not using white we'll need to partion our image area into 4x4 pixel squares,
                     70:  * and that means a 600x600 matrix is about as big as we can go. In practice that's
                     71:  * optimistic, but the argument illustrates some of the limitations.
                     72:  *
                     73:  * A submatrix can be selected to display by windowing into the matrix. The window
                     74:  * list can be given using the -w option or can be set in the optional header that
                     75:  * can preceed each matrix.  The list should be a comma or space separated list
                     76:  * that looks like,
                     77:  *
                     78:  *             lower-column, lower-row, upper-column, upper-row
                     79:  *
                     80:  * where each element in the list must be a positive integer. Rows and columns in
                     81:  * the input matrix start at 1. The dimension of the displayed window will be from
                     82:  * lower-column to upper-column and from lower-row to upper-row inclusive.
                     83:  *
                     84:  * The encoding produced by the program is essentially identical to what's done
                     85:  * by postdmd. See the comments at the beginning of that program if you need more
                     86:  * details. The prologue also shares much of the same code. 
                     87:  *
                     88:  * The PostScript prologue is copied from *prologue before any of the input files
                     89:  * are translated. The program expects that the following PostScript procedures
                     90:  * are defined in that file:
                     91:  *
                     92:  *     setup
                     93:  *
                     94:  *       mark ... setup -
                     95:  *
                     96:  *         Handles special initialization stuff that depends on how this program
                     97:  *         was called. Expects to find a mark followed by key/value pairs on the
                     98:  *         stack. The def operator is applied to each pair up to the mark, then
                     99:  *         the default state is set up.
                    100:  *
                    101:  *     pagesetup
                    102:  *
                    103:  *       page pagesetup -
                    104:  *
                    105:  *         Does whatever is needed to set things up for the next page. Expects
                    106:  *         to find the current page number on the stack.
                    107:  *
                    108:  *     bitmap
                    109:  *
                    110:  *       columns rows bitmap -
                    111:  *
                    112:  *         Prints the image that's read as a hex string from standard input. The
                    113:  *         image consists of rows lines, each of which includes columns elements.
                    114:  *         Eight bits per pixel are used to encode the matrix elements.
                    115:  *
                    116:  *     labelmatrix
                    117:  *
                    118:  *       matrixname matrixlimits labelmatrix -
                    119:  *
                    120:  *         Prints string matrixname just below the lower left corner of the image
                    121:  *         and prints string martixlimits near the lower right corner. Outlines
                    122:  *         the entire image with a (one pixel wide) box and then draws tick marks
                    123:  *         along the top and left sides of the image. One tick mark is printed
                    124:  *         for every ten elements.
                    125:  *
                    126:  *     legend
                    127:  *
                    128:  *       n1 ... nN N c1 m1 ... cM mM total regions legend -
                    129:  *
                    130:  *         Prints the legend as a bar graph below the matrix image. n1 ... nN are
                    131:  *         strings that represent the interval list. c1 m1 ... cm mM are pairs
                    132:  *         that consist of a region's color and the statistics count. Actually
                    133:  *         the c's are trivial procedures that just leave a one character string
                    134:  *         on the stack when they're executed by image - which is the way the
                    135:  *         bar graph is drawn.
                    136:  *
                    137:  *     done
                    138:  *
                    139:  *       done
                    140:  *
                    141:  *         Makes sure the last page is printed. Only needed when we're printing
                    142:  *         more than one page on each sheet of paper.
                    143:  *
                    144:  * Many default values, like the magnification and orientation, are defined in 
                    145:  * the prologue, which is where they belong. If they're changed (by options), an
                    146:  * appropriate definition is made after the prologue is added to the output file.
                    147:  * The -P option passes arbitrary PostScript through to the output file. Among
                    148:  * other things it can be used to set (or change) values that can't be accessed by
                    149:  * other options.
                    150:  *
                    151:  */
                    152: 
                    153: #include <stdio.h>
                    154: #include <signal.h>
                    155: #include <ctype.h>
                    156: #include <fcntl.h>
                    157: 
                    158: #include "comments.h"                  /* PostScript file structuring comments */
                    159: #include "gen.h"                       /* general purpose definitions */
                    160: #include "path.h"                      /* for the prologue */
                    161: #include "ext.h"                       /* external variable declarations */
                    162: #include "postmd.h"                    /* special matrix display definitions */
                    163: 
                    164: char   *optnames = "a:b:c:d:g:i:m:n:o:p:w:x:y:A:C:E:J:L:P:R:DI";
                    165: 
                    166: char   *prologue = POSTMD;             /* default PostScript prologue */
                    167: char   *formfile = FORMFILE;           /* stuff for multiple pages per sheet */
                    168: char   *temp_dir = TEMPDIR;            /* temp directory for copying stdin */
                    169: 
                    170: int    formsperpage = 1;               /* page images on each piece of paper */
                    171: int    copies = 1;                     /* and this many copies of each sheet */
                    172: int    bytespp = 6;                    /* bytes per pattern - on output */
                    173: 
                    174: int    dostats = ON;                   /* permanent statistics flag */
                    175: int    nxtstat = ON;                   /* and the one for the next matrix */
                    176: 
                    177: char   *interval = DFLTILIST;          /* string representations of the interval */
                    178: char   *colormap = NULL;               /* color map */
                    179: char   *window = NULL;                 /* and window lists */
                    180: char   *matrixname = "pipe.end";       /* name for the next plot */
                    181: 
                    182: Ilist  ilist[128];                     /* active interval list and color map */
                    183: int    next = 0;                       /* one past the last element in ilist[] */
                    184: int    regions;                        /* an index assigned to the last region */
                    185: int    wlist[4];                       /* upper left and lower right corners */
                    186: 
                    187: int    page = 0;                       /* last page we worked on */
                    188: int    printed = 0;                    /* and the number of pages printed */
                    189: 
                    190: int    dfltrows = 0;                   /* default rows */
                    191: int    dfltcols = 0;                   /* and columns - changed by -d option */
                    192: int    rows;                           /* real number of rows */
                    193: int    columns;                        /* and columns in the matrix */
                    194: int    patcount = 0;                   /* will be set to columns * rows */
                    195: 
                    196: double element;                        /* next matrix element */
                    197: 
                    198: char   *raster = NULL;                 /* next raster line */
                    199: char   *rptr;                          /* next free byte in raster */
                    200: char   *eptr;                          /* one past the last byte in raster */
                    201: 
                    202: FILE   *fp_in = stdin;                 /* read from this file */
                    203: FILE   *fp_out = stdout;               /* and write stuff here */
                    204: FILE   *fp_acct = NULL;                /* for accounting data */
                    205: 
                    206: /*****************************************************************************/
                    207: 
                    208: main(agc, agv)
                    209: 
                    210:     int                agc;
                    211:     char       *agv[];
                    212: 
                    213: {
                    214: 
                    215: /*
                    216:  *
                    217:  * Bitmap display program for matrices. Only one matrix is allowed per input file,
                    218:  * and each one will be displayed on a page by itself. Input files consist of an
                    219:  * optional header followed by floating point numbers that represent the matrix
                    220:  * elements - in row major order.
                    221:  *
                    222:  */
                    223: 
                    224:     argc = agc;                                /* other routines may want them */
                    225:     argv = agv;
                    226: 
                    227:     prog_name = argv[0];               /* really just for error messages */
                    228: 
                    229:     init_signals();                    /* sets up interrupt handling */
                    230:     header();                          /* PostScript header comments */
                    231:     options();                         /* handle the command line options */
                    232:     setup();                           /* for PostScript */
                    233:     arguments();                       /* followed by each input file */
                    234:     done();                            /* print the last page etc. */
                    235:     account();                         /* job accounting data */
                    236: 
                    237:     exit(x_stat);                      /* not much could be wrong */
                    238: 
                    239: }   /* End of main */
                    240: 
                    241: /*****************************************************************************/
                    242: 
                    243: init_signals()
                    244: 
                    245: {
                    246: 
                    247: /*
                    248:  *
                    249:  * Make sure we handle interrupts.
                    250:  *
                    251:  */
                    252: 
                    253:     if ( signal(SIGINT, interrupt) == SIG_IGN )  {
                    254:        signal(SIGINT, SIG_IGN);
                    255:        signal(SIGQUIT, SIG_IGN);
                    256:        signal(SIGHUP, SIG_IGN);
                    257:     } else {
                    258:        signal(SIGHUP, interrupt);
                    259:        signal(SIGQUIT, interrupt);
                    260:     }   /* End else */
                    261: 
                    262:     signal(SIGTERM, interrupt);
                    263:     signal(SIGFPE, interrupt);
                    264: 
                    265: }   /* End of init_signals */
                    266: 
                    267: /*****************************************************************************/
                    268: 
                    269: header()
                    270: 
                    271: {
                    272: 
                    273:     int                ch;                     /* return value from getopt() */
                    274:     int                old_optind = optind;    /* for restoring optind - should be 1 */
                    275: 
                    276: /*
                    277:  *
                    278:  * Scans the option list looking for things, like the prologue file, that we need
                    279:  * right away but could be changed from the default. Doing things this way is an
                    280:  * attempt to conform to Adobe's latest file structuring conventions. In particular
                    281:  * they now say there should be nothing executed in the prologue, and they have
                    282:  * added two new comments that delimit global initialization calls. Once we know
                    283:  * where things really are we write out the job header, follow it by the prologue,
                    284:  * and then add the ENDPROLOG and BEGINSETUP comments.
                    285:  *
                    286:  */
                    287: 
                    288:     while ( (ch = getopt(argc, argv, optnames)) != EOF )
                    289:        if ( ch == 'L' )
                    290:            prologue = optarg;
                    291:        else if ( ch == '?' )
                    292:            error(FATAL, "");
                    293: 
                    294:     optind = old_optind;               /* get ready for option scanning */
                    295: 
                    296:     fprintf(stdout, "%s", CONFORMING);
                    297:     fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION);
                    298:     fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND);
                    299:     fprintf(stdout, "%s %s\n", PAGES, ATEND);
                    300:     fprintf(stdout, "%s", ENDCOMMENTS);
                    301: 
                    302:     if ( cat(prologue) == FALSE )
                    303:        error(FATAL, "can't read %s", prologue);
                    304: 
                    305:     fprintf(stdout, "%s", ENDPROLOG);
                    306:     fprintf(stdout, "%s", BEGINSETUP);
                    307:     fprintf(stdout, "mark\n");
                    308: 
                    309: }   /* End of header */
                    310: 
                    311: /*****************************************************************************/
                    312: 
                    313: options()
                    314: 
                    315: {
                    316: 
                    317:     int                ch;                     /* return value from getopt() */
                    318: 
                    319: /*
                    320:  *
                    321:  * Reads and processes the command line options. Added the -P option so arbitrary
                    322:  * PostScript code can be passed through. Expect it could be useful for changing
                    323:  * definitions in the prologue for which options have not been defined.
                    324:  *
                    325:  */
                    326: 
                    327:     while ( (ch = getopt(argc, argv, optnames)) != EOF )  {
                    328:        switch ( ch )  {
                    329:            case 'a':                   /* aspect ratio */
                    330:                    fprintf(stdout, "/aspectratio %s def\n", optarg);
                    331:                    break;
                    332: 
                    333:            case 'b':                   /* bytes per pattern - on output */
                    334:                    bytespp = atoi(optarg);
                    335:                    break;
                    336: 
                    337:            case 'c':                   /* copies */
                    338:                    copies = atoi(optarg);
                    339:                    fprintf(stdout, "/#copies %s store\n", optarg);
                    340:                    break;
                    341: 
                    342:            case 'd':                   /* default matrix dimensions */
                    343:                    sscanf(optarg, "%dx%d", &dfltrows, &dfltcols);
                    344:                    break;
                    345: 
                    346:            case 'g':                   /* set the colormap (ie. grayscale) */
                    347:                    colormap = optarg;
                    348:                    break;
                    349: 
                    350:            case 'i':                   /* matrix element interval list */
                    351:                    interval = optarg;
                    352:                    break;
                    353: 
                    354:            case 'm':                   /* magnification */
                    355:                    fprintf(stdout, "/magnification %s def\n", optarg);
                    356:                    break;
                    357: 
                    358:            case 'n':                   /* forms per page */
                    359:                    formsperpage = atoi(optarg);
                    360:                    fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
                    361:                    fprintf(stdout, "/formsperpage %s def\n", optarg);
                    362:                    break;
                    363: 
                    364:            case 'o':                   /* output page list */
                    365:                    out_list(optarg);
                    366:                    break;
                    367: 
                    368:            case 'p':                   /* landscape or portrait mode */
                    369:                    if ( *optarg == 'l' )
                    370:                        fprintf(stdout, "/landscape true def\n");
                    371:                    else fprintf(stdout, "/landscape false def\n");
                    372:                    break;
                    373: 
                    374:            case 'w':                   /* set the window */
                    375:                    window = optarg;
                    376:                    break;
                    377: 
                    378:            case 'x':                   /* shift things horizontally */
                    379:                    fprintf(stdout, "/xoffset %s def\n", optarg);
                    380:                    break;
                    381: 
                    382:            case 'y':                   /* and vertically on the page */
                    383:                    fprintf(stdout, "/yoffset %s def\n", optarg);
                    384:                    break;
                    385: 
                    386:            case 'A':                   /* force job accounting */
                    387:            case 'J':
                    388:                    if ( (fp_acct = fopen(optarg, "a")) == NULL )
                    389:                        error(FATAL, "can't open accounting file %s", optarg);
                    390:                    break;
                    391: 
                    392:            case 'C':                   /* copy file straight to output */
                    393:                    if ( cat(optarg) == FALSE )
                    394:                        error(FATAL, "can't read %s", optarg);
                    395:                    break;
                    396: 
                    397:            case 'E':                   /* text font encoding */
                    398:                    fontencoding = optarg;
                    399:                    break;
                    400: 
                    401:            case 'L':                   /* PostScript prologue file */
                    402:                    prologue = optarg;
                    403:                    break;
                    404: 
                    405:            case 'P':                   /* PostScript pass through */
                    406:                    fprintf(stdout, "%s\n", optarg);
                    407:                    break;
                    408: 
                    409:            case 'R':                   /* special global or page level request */
                    410:                    saverequest(optarg);
                    411:                    break;
                    412: 
                    413:            case 'D':                   /* debug flag */
                    414:                    debug = ON;
                    415:                    break;
                    416: 
                    417:            case 'I':                   /* ignore FATAL errors */
                    418:                    ignore = ON;
                    419:                    break;
                    420: 
                    421:            case '?':                   /* don't understand the option */
                    422:                    error(FATAL, "");
                    423:                    break;
                    424: 
                    425:            default:                    /* don't know what to do for ch */
                    426:                    error(FATAL, "missing case for option %c\n", ch);
                    427:                    break;
                    428:        }   /* End switch */
                    429:     }   /* End while */
                    430: 
                    431:     argc -= optind;                    /* get ready for non-option args */
                    432:     argv += optind;
                    433: 
                    434: }   /* End of options */
                    435: 
                    436: /*****************************************************************************/
                    437: 
                    438: setup()
                    439: 
                    440: {
                    441: 
                    442: /*
                    443:  *
                    444:  * Handles things that must be done after the options are read but before the
                    445:  * input files are processed.
                    446:  *
                    447:  */
                    448: 
                    449:     writerequest(0, stdout);           /* global requests eg. manual feed */
                    450:     setencoding(fontencoding);
                    451:     fprintf(stdout, "setup\n");
                    452: 
                    453:     if ( formsperpage > 1 )  {
                    454:        if ( cat(formfile) == FALSE )
                    455:            error(FATAL, "can't read %s", formfile);
                    456:        fprintf(stdout, "%d setupforms\n", formsperpage);
                    457:     }  /* End if */
                    458: 
                    459:     fprintf(stdout, "%s", ENDSETUP);
                    460: 
                    461: }   /* End of setup */
                    462: 
                    463: /*****************************************************************************/
                    464: 
                    465: arguments()
                    466: 
                    467: {
                    468: 
                    469: /*
                    470:  *
                    471:  * Makes sure all the non-option command line arguments are processed. If we get
                    472:  * here and there aren't any arguments left, or if '-' is one of the input files
                    473:  * we'll process stdin.
                    474:  *
                    475:  */
                    476: 
                    477:     if ( argc < 1 )
                    478:        matrix();
                    479:     else  {                            /* at least one argument is left */
                    480:        while ( argc > 0 )  {
                    481:            matrixname = *argv;
                    482:            if ( strcmp(*argv, "-") == 0 )  {
                    483:                fp_in = stdin;
                    484:                matrixname = "pipe.end";
                    485:            } else if ( (fp_in = fopen(*argv, "r")) == NULL )
                    486:                error(FATAL, "can't open %s", *argv);
                    487:            matrix();
                    488:            if ( fp_in != stdin )
                    489:                fclose(fp_in);
                    490:            argc--;
                    491:            argv++;
                    492:        }   /* End while */
                    493:     }   /* End else */
                    494: 
                    495: }   /* End of arguments */
                    496: 
                    497: /*****************************************************************************/
                    498: 
                    499: done()
                    500: 
                    501: {
                    502: 
                    503: /*
                    504:  *
                    505:  * Finished with all the input files, so mark the end of the pages, make sure the
                    506:  * last page is printed, and restore the initial environment.
                    507:  *
                    508:  */
                    509: 
                    510:     fprintf(stdout, "%s", TRAILER);
                    511:     fprintf(stdout, "done\n");
                    512:     fprintf(stdout, "%s %d\n", PAGES, printed);
                    513: 
                    514:     if ( temp_file != NULL )
                    515:        unlink(temp_file);
                    516: 
                    517: }   /* End of done */
                    518: 
                    519: /*****************************************************************************/
                    520: 
                    521: account()
                    522: 
                    523: {
                    524: 
                    525: /*
                    526:  *
                    527:  * Writes an accounting record to *fp_acct provided it's not NULL. Accounting
                    528:  * is requested using the -A or -J options.
                    529:  *
                    530:  */
                    531: 
                    532:     if ( fp_acct != NULL )
                    533:        fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
                    534: 
                    535: }   /* End of account */
                    536: 
                    537: /*****************************************************************************/
                    538: 
                    539: matrix()
                    540: 
                    541: {
                    542: 
                    543:     int                count;                  /* pattern repeats this many times */
                    544:     long       total;                  /* expect this many patterns */
                    545: 
                    546: /*
                    547:  *
                    548:  * Reads a matrix from *fp_in, translates it into a PostScript gray scale image,
                    549:  * and writes the result on stdout. For now only one matrix is allowed per input
                    550:  * file. Matrix elements are floating point numbers arranged in row major order
                    551:  * in the input file. In addition each input file may contain an optional header
                    552:  * that defines special things like the dimension of the matrix, a window into
                    553:  * the matrix that will be displayed, and an interval list.
                    554:  *
                    555:  * If we're reading from stdin we first make a copy in a temporary file so we can
                    556:  * can properly position ourselves after we've looked for the header. Originally
                    557:  * wasn't always making a copy of stdin, but I've added a few things to what's
                    558:  * accepted in the header and this simplifies the job. An alternative would be
                    559:  * to always require a header and mark the end of it by some string. Didn't like
                    560:  * that approach much - may fix things up later.
                    561:  *
                    562:  */
                    563: 
                    564:     if ( fp_in == stdin )              /* make a copy so we can seek etc. */
                    565:        copystdin();
                    566: 
                    567:     rows = dfltrows;                   /* new dimensions for the next matrix */
                    568:     columns = dfltcols;
                    569: 
                    570:     buildilist(interval);              /* build the default ilist[] */
                    571:     addcolormap(colormap);             /* add the colormap - if not NULL */
                    572:     setwindow(window);                 /* and setup the initial matrix window */
                    573:     nxtstat = dostats;                 /* want statistics? */
                    574:     getheader();                       /* matrix dimensions at the very least */
                    575:     dimensions();                      /* make sure we have the dimensions etc. */
                    576: 
                    577:     patcount = 0;
                    578:     total = rows * columns;
                    579: 
                    580:     eptr = rptr + (wlist[2] - wlist[0] + 1);
                    581: 
                    582:     redirect(++page);
                    583: 
                    584:     fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1);
                    585:     fprintf(fp_out, "/saveobj save def\n");
                    586:     writerequest(printed+1, fp_out);
                    587:     fprintf(fp_out, "%d %d bitmap\n", wlist[2] - wlist[0] + 1, wlist[3] - wlist[1] + 1);
                    588: 
                    589:     while ( patcount != total && fscanf(fp_in, "%f", &element) != EOF )  {
                    590:        if ( inwindow() ) *rptr++ = mapfloat(element);
                    591:        if ( ++patcount % columns == 0 )
                    592:            if ( inrange() )
                    593:                putrow();
                    594:     }  /* End while */
                    595: 
                    596:     if ( total != patcount )
                    597:        error(FATAL, "matrix format error");
                    598: 
                    599:     labelmatrix();
                    600: 
                    601:     if ( fp_out == stdout ) printed++;
                    602: 
                    603:     fprintf(fp_out, "showpage\n");
                    604:     fprintf(fp_out, "saveobj restore\n");
                    605:     fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed);
                    606: 
                    607: }   /* End of matrix */
                    608: 
                    609: /*****************************************************************************/
                    610: 
                    611: copystdin()
                    612: 
                    613: {
                    614: 
                    615:     int                fd_out;                 /* for the temporary file */
                    616:     int                fd_in;                  /* for stdin */
                    617:     int                buf[512];               /* buffer for reads and writes */
                    618:     int                count;                  /* number of bytes put in buf */
                    619: 
                    620: /*
                    621:  *
                    622:  * If we're reading the matrix from stdin and the matrix dimension isn't set by
                    623:  * a dimension statement at the beginning of the file we'll copy stdin to a
                    624:  * temporary file and reset *fp_in so reads come from the temp file. Simplifies
                    625:  * reading the header (if present), but is expensive.
                    626:  *
                    627:  */
                    628: 
                    629:     if ( temp_file != NULL )           /* been here already */
                    630:        unlink(temp_file);
                    631: 
                    632:     if ( (temp_file = tempnam(temp_dir, "post")) == NULL )
                    633:        error(FATAL, "can't generate temp file name");
                    634: 
                    635:     if ( (fd_out = creat(temp_file, 0660)) == -1 )
                    636:        error(FATAL, "can't create %s", temp_file);
                    637: 
                    638:     fd_in = fileno(stdin);
                    639: 
                    640:     while ( (count = read(fd_in, buf, sizeof(buf))) > 0 )
                    641:        if ( write(fd_out, buf, count) != count )
                    642:            error(FATAL, "error writing to %s", temp_file);
                    643: 
                    644:     close(fd_out);
                    645: 
                    646:     if ( (fp_in = fopen(temp_file, "r")) == NULL )
                    647:        error(FATAL, "can't open %s", temp_file);
                    648: 
                    649: }   /* End of copystdin */
                    650: 
                    651: /*****************************************************************************/
                    652: 
                    653: getheader()
                    654: 
                    655: {
                    656: 
                    657:     char       buf[512];               /* temporary string space */
                    658:     char       *cmap = NULL;           /* remember header colormap list */
                    659:     long       pos;                    /* for seeking back to first element */
                    660: 
                    661: /*
                    662:  *
                    663:  * Looks for the optional header information at the beginning of the input file,
                    664:  * reads it if it's there, and sets *fp_in to be just past the header. That should
                    665:  * be the beginning of the matrix element list. The recognized header keywords are
                    666:  * dimension, interval, colormap (or grayscale), window, name, and statistics. All
                    667:  * are optional, but may be useful in a spooling environment when the user doesn't
                    668:  * doesn't actually run the translator.
                    669:  *
                    670:  * The dimension statement specifies the number of rows and columns. For example
                    671:  * either of the following two lines define a 50 by 50 element matrix,
                    672:  *
                    673:  *     dimension       50
                    674:  *     dimension       50x50
                    675:  *
                    676:  * The first integer is the number of rows and the second, if given, is the number
                    677:  * of columns. If columns are missing from the dimension statement we assume the
                    678:  * matrix is square.
                    679:  *
                    680:  * interval can be used to redefine the interval list used for mapping floating
                    681:  * point numbers into integers in the range 0 to 254. The string following the
                    682:  * interval keyword has the same format as the -i option. For example to set the
                    683:  * interval list to -1, 0, and 1 you can add the line,
                    684:  *
                    685:  *     interval        -1,0,1
                    686:  *
                    687:  * The numbers are floats given in increasing order, and separated by commas or
                    688:  * blanks. The last interval list in a header takes precedence.
                    689:  *
                    690:  * colormap can be used to redefine the grayscale list.  The string following
                    691:  * the colormap keyword has the same format as the -g option.  For example
                    692:  *
                    693:  *     colormap        0,50,100,150,200,250
                    694:  * or  grayscale       0,50,100,150,200,250
                    695:  *
                    696:  * The window keyword can be used to select a submatrix. The numbers following
                    697:  * window are the upper left and lower right matix coordinates. May not be
                    698:  * implemented yet but shouldn't be difficult. For example
                    699:  *
                    700:  *     window          10 10 40 40
                    701:  *
                    702:  * selects the submatrix with corners at (10, 10) and (40, 40). The edges of the
                    703:  * window are included in the display.
                    704:  *
                    705:  * The name keyword can be used to define the title of the display.  For example,
                    706:  *
                    707:  *     name            Plot Of Matrix 1
                    708:  *
                    709:  * prints the string "Plot Of Matrix 1" at the top of the page. Everything up to
                    710:  * the next newline is taken as the name string.
                    711:  *
                    712:  */
                    713: 
                    714:     pos = ftell(fp_in);
                    715: 
                    716:     while ( fscanf(fp_in, "%s", buf) != EOF )  {
                    717:        if ( strncmp(buf, "dimension", strlen("dimension")) == 0 )
                    718:            fscanf(fp_in, "%dx%d", &rows, &columns);
                    719:        else if ( strncmp(buf, "window", strlen("window")) == 0 )  {
                    720:            fgets(buf, sizeof(buf), fp_in);
                    721:            setwindow(buf);
                    722:        } else if ( strncmp(buf, "name", strlen("name")) == 0 )  {
                    723:            fgets(buf, sizeof(buf), fp_in);
                    724:            matrixname = savestring(buf);
                    725:        } else if ( strncmp(buf, "colormap", strlen("colormap")) == 0 )  {
                    726:            fgets(buf, sizeof(buf), fp_in);
                    727:            cmap = savestring(buf);
                    728:        } else if ( strncmp(buf, "grayscale", strlen("grayscale")) == 0 )  {
                    729:            fgets(buf, sizeof(buf), fp_in);
                    730:            cmap = savestring(buf);
                    731:        } else if ( strncmp(buf, "interval", strlen("interval")) == 0 )  {
                    732:            fgets(buf, sizeof(buf), fp_in);
                    733:            buildilist(buf);
                    734:        } else if ( strncmp(buf, "statistics", strlen("statistics")) == 0 )  {
                    735:            fscanf(fp_in, "%s", buf);
                    736:            if ( strcmp(buf, "on") == 0 || strcmp(buf, "ON") == 0 )
                    737:                nxtstat = ON;
                    738:            else nxtstat = OFF;
                    739:        } else break;
                    740:        pos = ftell(fp_in);
                    741:     }  /* End while */
                    742: 
                    743:     addcolormap(cmap);                 /* must happen last */
                    744:     fseek(fp_in, pos, 0);              /* back to the start of the matrix */
                    745: 
                    746: }   /* End of getheader */
                    747: 
                    748: /*****************************************************************************/
                    749: 
                    750: dimensions()
                    751: 
                    752: {
                    753: 
                    754:     char       buf[100];               /* temporary storage for the elements */
                    755:     long       count = 0;              /* number of elements in the matrix */
                    756:     long       pos;                    /* matrix elements start here */
                    757: 
                    758: /*
                    759:  *
                    760:  * Need to know the dimensions of the matrix before we can go any farther. If
                    761:  * rows and columns are still 0 we'll read the entire input file, starting from
                    762:  * the current position, count the number of elements, take the square root of it,
                    763:  * and use it as the number of rows and columns. Then we seek back to the start
                    764:  * of the real matrix, make sure columns is set, and allocate enough memory for
                    765:  * storing each raster line. After we're certain we've got the number of rows and
                    766:  * columns we check the window coordinates, and if they're not legitimate they're
                    767:  * reset to cover the entire matrix.
                    768:  *
                    769:  */
                    770: 
                    771:     if ( rows == 0 )  {
                    772:        pos = ftell(fp_in);
                    773:        while ( fscanf(fp_in, "%s", buf) != EOF )
                    774:            count++;
                    775:        rows = sqrt((double) count);
                    776:        fseek(fp_in, pos, 0);
                    777:     }  /* End if */
                    778: 
                    779:     if ( columns <= 0 ) columns = rows;
                    780: 
                    781:     if ( raster != NULL ) free(raster);
                    782: 
                    783:     if ( (rptr = raster = malloc(columns)) == NULL )
                    784:        error(FATAL, "no memory");
                    785: 
                    786:     eptr = rptr + columns;
                    787: 
                    788:     if ( rows <= 0 || columns <= 0 )
                    789:        error(FATAL, "bad matrix dimensions");
                    790: 
                    791:     if ( wlist[0] > wlist[2] || wlist[1] > wlist[3] )  {
                    792:        wlist[0] = wlist[1] = 1;
                    793:        wlist[2] = columns;
                    794:        wlist[3] = rows;
                    795:     }  /* End if */
                    796: 
                    797: }   /* End of dimensions */
                    798: 
                    799: /*****************************************************************************/
                    800: 
                    801: buildilist(list)
                    802: 
                    803:     char       *list;                  /* use this as the interval list */
                    804: 
                    805: {
                    806: 
                    807:     static char        *templist = NULL;       /* a working copy of the list */
                    808:     char       *ptr;                   /* next number in *templist */
                    809:     int                i;                      /* loop index - for checking the list */
                    810: 
                    811: /*
                    812:  *
                    813:  * Reads string *list and builds up the ilist[] that will be used in the next
                    814:  * matrix. Since strtok() modifies the string it's parsing we make a copy first.
                    815:  * The format of the interval list is described in detail in the comments at the
                    816:  * beginning of this program. Basically consists of a comma or space separated
                    817:  * list of floating point numbers that must be given in increasing numerical order.
                    818:  * The list determines how floating point numbers are mapped into integers in the
                    819:  * range 0 to 254.
                    820:  *
                    821:  */
                    822: 
                    823:     if ( templist != NULL )            /* free the space used by the last list */
                    824:        free(templist);
                    825: 
                    826:     while ( isascii(*list) && isspace(*list) )
                    827:        list++;
                    828: 
                    829:     for ( ptr = list, regions = 3; *ptr != '\0'; ptr++ )  {
                    830:        if ( *ptr == ',' || *ptr == '/' || isspace(*ptr) )
                    831:            regions += 2;
                    832:        while ( isascii(*ptr) && isspace(*ptr) ) ptr++;
                    833:     }  /* End for */
                    834: 
                    835:     next = 0;
                    836:     templist = savestring(list);
                    837: 
                    838:     ptr = strtok(templist, ",/ \t\n");
                    839:     while ( ptr != NULL )  {
                    840:        ilist[next].count = 0;
                    841:        ilist[next++].color = 254 * (regions - 1 - next) / (regions - 1);
                    842:        ilist[next].val = atof(ptr);
                    843:        ilist[next].count = 0;
                    844:        ilist[next++].color = 254 * (regions - 1 - next) / (regions - 1);
                    845:        ptr = strtok(NULL, ",/ \t\n");
                    846:     }  /* End while */
                    847: 
                    848:     ilist[next].count = 0;
                    849:     ilist[next].color = 254 * (regions - 1 - next) / (regions - 1);
                    850: 
                    851:     if ( next == 0 )                   /* make sure we have a list */
                    852:        error(FATAL, "missing interval list");
                    853: 
                    854:     for ( i = 3; i < next; i += 2 )    /* that's in increasing numerical order */
                    855:        if ( ilist[i].val <= ilist[i-2].val )
                    856:            error(FATAL, "bad interval list");
                    857: 
                    858: }   /* End of buildilist */
                    859: 
                    860: /*****************************************************************************/
                    861: 
                    862: addcolormap(list)
                    863: 
                    864:     char       *list;                  /* use this color map */
                    865: 
                    866: {
                    867: 
                    868:     static char        *templist = NULL;       /* a working copy of the color list */
                    869:     char       *ptr;                   /* next color in *templist */
                    870:     int                i = 0;                  /* assigned to this region in ilist[] */
                    871: 
                    872: /*
                    873:  *
                    874:  * Assigns the integers in *list to the color field for the regions defined in
                    875:  * ilist[]. Assumes ilist[] has already been setup.
                    876:  *
                    877:  */
                    878: 
                    879:     if ( list != NULL )  {
                    880:        if ( templist != NULL )
                    881:            free(templist);
                    882:        templist = savestring(list);
                    883: 
                    884:        ptr = strtok(templist, ",/ \t\n");
                    885:        while ( ptr != NULL )  {
                    886:            ilist[i++].color = atoi(ptr) % 256;
                    887:            ptr = strtok(NULL, ",/ \t\n");
                    888:        }   /* End while */
                    889:     }  /* End if */
                    890: 
                    891: }   /* End of addcolormap */
                    892: 
                    893: /*****************************************************************************/
                    894: 
                    895: setwindow(list)
                    896: 
                    897:     char       *list;                  /* corners of window into the matrix */
                    898: 
                    899: {
                    900: 
                    901:     static char        *templist = NULL;       /* a working copy of the window list */
                    902:     char       *ptr;                   /* next window coordinate in *templist */
                    903:     int                i = 0;                  /* assigned to this region in wlist[] */
                    904: 
                    905: /*
                    906:  *
                    907:  * Sets up an optional window into the matrix.
                    908:  *
                    909:  */
                    910: 
                    911:     wlist[0] = wlist[1] = 1;
                    912:     wlist[2] = wlist[3] = 0;
                    913: 
                    914:     if ( list != NULL )  {
                    915:        if ( templist != NULL )
                    916:            free(templist);
                    917:        templist = savestring(list);
                    918: 
                    919:        ptr = strtok(templist, ",/ \t\n");
                    920:        while ( ptr != NULL )  {
                    921:            wlist[i++] = atoi(ptr);
                    922:            ptr = strtok(NULL, ",/ \t\n");
                    923:        }   /* End while */
                    924:     }  /* End if */
                    925: 
                    926: }   /* End of setwindow */
                    927: 
                    928: /*****************************************************************************/
                    929: 
                    930: inwindow()
                    931: 
                    932: {
                    933: 
                    934:     int                r;                      /* row of the patcount element */
                    935:     int                c;                      /* column of the patcount element */
                    936: 
                    937: /*
                    938:  *
                    939:  * Checks if the patcount element of the matrix is in the window.
                    940:  *
                    941:  */
                    942: 
                    943:     r = (patcount/columns) + 1;
                    944:     c = (patcount%columns) + 1;
                    945: 
                    946:     return((c >= wlist[0]) && (r >= wlist[1]) && (c <= wlist[2]) && (r <= wlist[3]));
                    947: 
                    948: }   /* End of inwindow */
                    949: 
                    950: /*****************************************************************************/
                    951: 
                    952: inrange()
                    953: 
                    954: {
                    955: 
                    956: /*
                    957:  *
                    958:  * Checks if the current row lies in the window. Used right before we output the
                    959:  * raster lines.
                    960:  *
                    961:  */
                    962: 
                    963:     return(((patcount/columns) >= wlist[1]) && ((patcount/columns) <= wlist[3]));
                    964: 
                    965: }   /* End of inrange */
                    966: 
                    967: /*****************************************************************************/
                    968: 
                    969: mapfloat(element)
                    970: 
                    971:     double     element;                /* floating point matrix element */
                    972: 
                    973: {
                    974: 
                    975:     int                i;                      /* loop index */
                    976: 
                    977: /*
                    978:  *
                    979:  * Maps element into an integer in the range 0 to 255, and returns the result to
                    980:  * the caller. Mapping is done using the color map that was saved in ilist[]. Also
                    981:  * updates the count field for the region that contains element - not good!
                    982:  *
                    983:  */
                    984: 
                    985:     for ( i = 1; i < next && ilist[i].val < element; i += 2 ) ;
                    986: 
                    987:     if ( i > next || element < ilist[i].val )
                    988:        i--;
                    989: 
                    990:     ilist[i].count++;
                    991:     return(ilist[i].color);
                    992: 
                    993: }   /* End of mapfloat */
                    994: 
                    995: /*****************************************************************************/
                    996: 
                    997: putrow()
                    998: 
                    999: {
                   1000: 
                   1001:     char       *p1, *p2;               /* starting and ending columns */
                   1002:     int                n;                      /* set to bytes per pattern */
                   1003:     int                i;                      /* loop index */
                   1004: 
                   1005: /*
                   1006:  *
                   1007:  * Takes the scanline that's been saved in *raster, encodes it according to the
                   1008:  * value that's been assigned to bytespp, and writes the result to *fp_out. Each
                   1009:  * line in the output bitmap is terminated by a 0 on a line by itself.
                   1010:  *
                   1011:  */
                   1012: 
                   1013:     n = (bytespp <= 0) ? columns : bytespp;
                   1014: 
                   1015:     for ( p1 = raster, p2 = raster + n; p1 < eptr; p1 = p2 )
                   1016:        if ( patncmp(p1, n) == TRUE )  {
                   1017:            while ( patncmp(p2, n) == TRUE ) p2 += n;
                   1018:            p2 += n;
                   1019:            fprintf(fp_out, "%d ", n);
                   1020:            for ( i = 0; i < n; i++, p1++ )
                   1021:                fprintf(fp_out, "%.2X", ((int) *p1) & 0377);
                   1022:            fprintf(fp_out, " %d\n", (p2 - p1) / n);
                   1023:        } else {
                   1024:            while ( p2 < eptr && patncmp(p2, n) == FALSE ) p2 += n;
                   1025:            if ( p2 > eptr ) p2 = eptr;
                   1026:            fprintf(fp_out, "%d ", p2 - p1);
                   1027:            while ( p1 < p2 )
                   1028:                fprintf(fp_out, "%.2X", ((int) *p1++) & 0377);
                   1029:            fprintf(fp_out, " 0\n");
                   1030:        }   /* End else */
                   1031: 
                   1032:     fprintf(fp_out, "0\n");
                   1033: 
                   1034:     rptr = raster;
                   1035: 
                   1036: }   /* End of putrow */
                   1037: 
                   1038: /*****************************************************************************/
                   1039: 
                   1040: labelmatrix()
                   1041: 
                   1042: {
                   1043: 
                   1044:     int                total;                  /* number of elements in the window */
                   1045:     int                i;                      /* loop index */
                   1046: 
                   1047: /*
                   1048:  *
                   1049:  * Responsible for generating the PostScript calls that label the matrix, generate
                   1050:  * the legend, and print the matrix name.
                   1051:  *
                   1052:  */
                   1053: 
                   1054:     fprintf(fp_out, "(%s) ((%d, %d) to (%d, %d)) labelmatrix\n", matrixname,
                   1055:                        wlist[0], wlist[1], wlist[2], wlist[3]);
                   1056: 
                   1057:     total = (wlist[2] - wlist[0] + 1) * (wlist[3] - wlist[1] + 1);
                   1058: 
                   1059:     if ( nxtstat == OFF )
                   1060:        for ( i = 0; i < regions; i++ )
                   1061:            ilist[i].count = 0;
                   1062: 
                   1063:     for ( i = 1; i < next; i += 2 )
                   1064:        fprintf(fp_out, "(%g) ", ilist[i].val);
                   1065:     fprintf(fp_out, "%d ", (regions - 1) / 2);
                   1066: 
                   1067:     for ( i = regions - 1; i >= 0; i-- )
                   1068:        fprintf(fp_out, "{(\\%.3o)} %d ", ilist[i].color, ilist[i].count);
                   1069:     fprintf(fp_out, "%d %d legend\n", total, regions);
                   1070: 
                   1071: }   /* End of labelmatrix */
                   1072: 
                   1073: /*****************************************************************************/
                   1074: 
                   1075: patncmp(p1, n)
                   1076: 
                   1077:     char       *p1;                    /* first patterns starts here */
                   1078:     int                n;                      /* and extends this many bytes */
                   1079: 
                   1080: {
                   1081: 
                   1082:     char       *p2;                    /* address of the second pattern */
                   1083: 
                   1084: /*
                   1085:  *
                   1086:  * Compares the two n byte patterns *p1 and *(p1+n). FALSE if returned is they're
                   1087:  * different or extend past the end of the current raster line.
                   1088:  *
                   1089:  */
                   1090: 
                   1091:     p2 = p1 + n;
                   1092: 
                   1093:     for ( ; n > 0; n--, p1++, p2++ )
                   1094:        if ( p2 >= eptr || *p1 != *p2 )
                   1095:            return(FALSE);
                   1096: 
                   1097:     return(TRUE);
                   1098: 
                   1099: }   /* End of patncmp */
                   1100: 
                   1101: /*****************************************************************************/
                   1102: 
                   1103: char *savestring(str)
                   1104: 
                   1105:     char       *str;                   /* save this string */
                   1106: 
                   1107: {
                   1108: 
                   1109:     char       *ptr = NULL;            /* at this address */
                   1110: 
                   1111: /*
                   1112:  *
                   1113:  * Copies string *str to a permanent place and returns the address to the caller.
                   1114:  *
                   1115:  */
                   1116: 
                   1117:     if ( str != NULL && *str != '\0' )  {
                   1118:        if ( (ptr = malloc(strlen(str) + 1)) == NULL )
                   1119:            error(FATAL, "no memory available for string %s", str);
                   1120:        strcpy(ptr, str);
                   1121:     }  /* End if */
                   1122: 
                   1123:     return(ptr);
                   1124: 
                   1125: }   /* End of savestring */
                   1126: 
                   1127: /*****************************************************************************/
                   1128: 
                   1129: redirect(pg)
                   1130: 
                   1131:     int                pg;                     /* next page we're printing */
                   1132: 
                   1133: {
                   1134: 
                   1135:     static FILE        *fp_null = NULL;        /* if output is turned off */
                   1136: 
                   1137: /*
                   1138:  *
                   1139:  * If we're not supposed to print page pg, fp_out will be directed to /dev/null,
                   1140:  * otherwise output goes to stdout.
                   1141:  *
                   1142:  */
                   1143: 
                   1144:     if ( pg >= 0 && in_olist(pg) == ON )
                   1145:        fp_out = stdout;
                   1146:     else if ( (fp_out = fp_null) == NULL )
                   1147:        fp_out = fp_null = fopen("/dev/null", "w");
                   1148: 
                   1149: }   /* End of redirect */
                   1150: 
                   1151: /*****************************************************************************/
                   1152: 

unix.superglobalmegacorp.com

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