|
|
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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.