dbs@kodak.enet.dec.com (dan sears) (05/07/91)
Submitted-by: dbs@kodak.enet.dec.com (dan sears) Posting-number: Volume 12, Issue 54 Archive-name: xtiff/part02 ---- Cut Here and feed the following to sh ---- #!/bin/sh # this is xtiff.02 (part 2 of a multipart archive) # do not concatenate these parts, unpack them in order with /bin/sh # file xtiff/athena/xtiff.c continued # if test ! -r _shar_seq_.tmp; then echo 'Please unpack part 1 first!' exit 1 fi (read Scheck if test "$Scheck" != 2; then echo Please unpack part "$Scheck" next! exit 1 else exit 0 fi ) < _shar_seq_.tmp || exit 1 if test ! -f _shar_wnt_.tmp; then echo 'x - still skipping xtiff/athena/xtiff.c' else echo 'x - continuing file xtiff/athena/xtiff.c' sed 's/^X//' << 'SHAR_EOF' >> 'xtiff/athena/xtiff.c' && X } }; X Arg formArgs[] = { X { XtNresizable, True } }; X Arg listArgs[] = { X { XtNresizable, False }, X { XtNborderWidth, 0 }, X { XtNdefaultColumns, 3 }, X { XtNforceColumns, True }, X { XtNlist, (int) buttonStrings }, X { XtNnumberStrings, XtNumber(buttonStrings) }, X { XtNtop, XtChainTop }, X { XtNleft, XtChainLeft }, X { XtNbottom, XtChainTop }, X { XtNright, XtChainLeft } }; X Arg labelArgs[] = { X { XtNresizable, False }, X { XtNwidth, 200 }, X { XtNborderWidth, 0 }, X { XtNjustify, XtJustifyLeft }, X { XtNtop, XtChainTop }, X { XtNleft, XtChainLeft }, X { XtNbottom, XtChainTop }, X { XtNright, XtChainLeft } }; X Arg imageArgs[] = { X { XtNresizable, True }, X { XtNborderWidth, 0 }, X { XtNtop, XtChainTop }, X { XtNleft, XtChainLeft }, X { XtNbottom, XtChainTop }, X { XtNright, XtChainLeft } }; X XXtActionsRec actionsTable[] = { X { "quit", QuitProc }, X { "next", NextProc }, X { "previous", PreviousProc }, X { "notifyresize", ResizeProc } }; X char translationsTable[] = "<Key>q: quit() \n \ X <Key>Q: quit() \n \ X <Key>p: previous() \n \ X <Key>P: previous() \n \ X <Key>n: next() \n \ X <Key>N: next() \n \ X <Configure>: notifyresize()"; X /* X * X data structures X */ Colormap xColormap; Display * xDisplay; Pixmap xImagePixmap; Visual * xVisual; XXImage * xImage; GC xWinGc; int xImageDepth, xScreen, xRedMask, xGreenMask, xBlueMask, X xOffset = 0, yOffset = 0, grabX = -1, grabY = -1; X /* X * TIFF data structures X */ TIFF * tfFile = NULL; u_short tfBitsPerSample, tfSamplesPerPixel, tfPlanarConfiguration, X tfPhotometricInterpretation, tfGrayResponseUnit, X tfImageDepth, tfImageWidth, tfImageHeight, tfBytesPerRow; int tfDirectory = 0, tfMultiPage = False; double tfUnitMap, tfGrayResponseUnitMap[] = { X -1, -10, -100, -1000, -10000, -100000 X }; X /* X * display data structures X */ double dGamma, *dRed, *dGreen, *dBlue; X /* X * shared data structures X */ u_short * redMap = NULL, *greenMap = NULL, *blueMap = NULL, X *grayMap = NULL, colormapSize; u_char * imageMemory; char * fileName; X void main(argc, argv) X int argc; X char ** argv; { X XSetWindowAttributes window_attributes; X Widget widget_list[3]; X Arg args[5]; X X setbuf(stdout, NULL); setbuf(stderr, NULL); X X shellWidget = XtInitialize(argv[0], "XTiff", shellOptions, X XtNumber(shellOptions), &argc, argv); X X XSetErrorHandler(XTiffErrorHandler); X X XtGetApplicationResources(shellWidget, &appData, X clientResources, XtNumber(clientResources), NULL, 0); X X if ((argc <= 1) || (argc > 2) || appData.help) X Usage(); X X if (appData.verbose == False) { X close(2); /* otherwise ignore libtiff error and warning messages */ X (void) open("/dev/null", O_WRONLY); X } X X fileName = argv[1]; X X xDisplay = XtDisplay(shellWidget); X xScreen = DefaultScreen(xDisplay); X X OpenTIFFFile(); X GetTIFFHeader(); X SimpleGammaCorrection(); X GetVisual(); X GetTIFFImage(); X X /* X * Send visual, colormap, depth and iconPixmap to shellWidget. X * Sending the visual to the shell is only possible with the advent of R4. X */ X XtSetArg(args[0], XtNvisual, xVisual); X XtSetArg(args[1], XtNcolormap, xColormap); X XtSetArg(args[2], XtNdepth, X xImageDepth == 1 ? DefaultDepth(xDisplay, xScreen) : xImageDepth); X XtSetArg(args[3], XtNiconPixmap, X XCreateBitmapFromData(xDisplay, RootWindow(xDisplay, xScreen), X xtifficon_bits, xtifficon_width, xtifficon_height)); X XtSetArg(args[4], XtNallowShellResize, True); X XtSetValues(shellWidget, args, 5); X X /* X * widget instance hierarchy X */ X formWidget = XtCreateManagedWidget("form", formWidgetClass, X shellWidget, formArgs, XtNumber(formArgs)); X X widget_list[0] = listWidget = XtCreateWidget("list", X listWidgetClass, formWidget, listArgs, XtNumber(listArgs)); X X widget_list[1] = labelWidget = XtCreateWidget("label", X labelWidgetClass, formWidget, labelArgs, XtNumber(labelArgs)); X X widget_list[2] = imageWidget = XtCreateWidget("image", X widgetClass, formWidget, imageArgs, XtNumber(imageArgs)); X X XtManageChildren(widget_list, XtNumber(widget_list)); X X /* X * initial widget sizes - for small images let xtiff size itself X */ X if (tfImageWidth >= appData.viewportWidth) { X XtSetArg(args[0], XtNwidth, appData.viewportWidth); X XtSetValues(shellWidget, args, 1); X } X if (tfImageHeight >= appData.viewportHeight) { X XtSetArg(args[0], XtNheight, appData.viewportHeight); X XtSetValues(shellWidget, args, 1); X } X X XtSetArg(args[0], XtNwidth, tfImageWidth); X XtSetArg(args[1], XtNheight, tfImageHeight); X XtSetValues(imageWidget, args, 2); X X /* X * formWidget uses these constraints but they are stored in the children. X */ X XtSetArg(args[0], XtNfromVert, listWidget); X XtSetValues(imageWidget, args, 1); X XtSetArg(args[0], XtNfromHoriz, listWidget); X XtSetValues(labelWidget, args, 1); X X SetNameLabel(); X X XtAddCallback(listWidget, XtNcallback, SelectProc, NULL); X X XtAddActions(actionsTable, XtNumber(actionsTable)); X XtSetArg(args[0], XtNtranslations, X XtParseTranslationTable(translationsTable)); X XtSetValues(formWidget, &args[0], 1); X XtSetValues(imageWidget, &args[0], 1); X X /* X * This is intended to be a little faster than going through X * the translation manager. X */ X XtAddEventHandler(imageWidget, ExposureMask | ButtonPressMask X | ButtonReleaseMask | Button1MotionMask | KeyPressMask, X False, EventProc, NULL); X X XtRealizeWidget(shellWidget); X X window_attributes.cursor = XCreateFontCursor(xDisplay, XC_fleur); X XChangeWindowAttributes(xDisplay, XtWindow(imageWidget), X CWCursor, &window_attributes); X X CreateXImage(); X X XtMainLoop(); } X void OpenTIFFFile() { X if (tfFile != NULL) X TIFFClose(tfFile); X X if ((tfFile = TIFFOpen(fileName, "r")) == NULL) { X if (appData.verbose) X fprintf(stderr, "xtiff: can't open %s as a TIFF file\n", fileName); X else X fprintf(stdout, "xtiff: can't open %s as a TIFF file\n", fileName); X exit(0); X } X X if (TIFFReadDirectory(tfFile) == True) { X tfMultiPage = True; X (void) TIFFSetDirectory(tfFile, tfDirectory = 0); X } else X tfMultiPage = False; } X void GetTIFFHeader() { X register int i; X X if (!TIFFSetDirectory(tfFile, tfDirectory)) { X fprintf(stderr, "xtiff: can't seek to directory %d in %s\n", X tfDirectory, fileName); X exit(0); X } X X TIFFGetField(tfFile, TIFFTAG_IMAGEWIDTH, &tfImageWidth); X TIFFGetField(tfFile, TIFFTAG_IMAGELENGTH, &tfImageHeight); X X /* X * If the following tags aren't present then use the TIFF defaults. X */ X if (!TIFFGetField(tfFile, TIFFTAG_BITSPERSAMPLE, &tfBitsPerSample)) X tfBitsPerSample = 1; X if (!TIFFGetField(tfFile, TIFFTAG_SAMPLESPERPIXEL, &tfSamplesPerPixel)) X tfSamplesPerPixel = 1; X if (!TIFFGetField(tfFile, TIFFTAG_PLANARCONFIG, &tfPlanarConfiguration)) X tfPlanarConfiguration = PLANARCONFIG_CONTIG; X if (!TIFFGetField(tfFile, TIFFTAG_GRAYRESPONSEUNIT, &tfGrayResponseUnit)) X tfGrayResponseUnit = 2; X X tfUnitMap = tfGrayResponseUnitMap[tfGrayResponseUnit]; X colormapSize = 1 << tfBitsPerSample; X tfImageDepth = tfBitsPerSample * tfSamplesPerPixel; X X dRed = (double *) malloc(colormapSize * sizeof(double)); X dGreen = (double *) malloc(colormapSize * sizeof(double)); X dBlue = (double *) malloc(colormapSize * sizeof(double)); X MCHECK(dRed); MCHECK(dGreen); MCHECK(dBlue); X X /* X * If TIFFTAG_PHOTOMETRIC is not present then assign a reasonable default. X * The TIFF 5.0 specification doesn't give a default. X */ X if (!TIFFGetField(tfFile, TIFFTAG_PHOTOMETRIC, X &tfPhotometricInterpretation)) { X if (tfSamplesPerPixel != 1) X tfPhotometricInterpretation = PHOTOMETRIC_RGB; X else if (tfBitsPerSample == 1) X tfPhotometricInterpretation = PHOTOMETRIC_MINISBLACK; X else if (TIFFGetField(tfFile, TIFFTAG_COLORMAP, X &redMap, &greenMap, &blueMap)) { X tfPhotometricInterpretation = PHOTOMETRIC_PALETTE; X free(redMap); free(greenMap); free(blueMap); X redMap = greenMap = blueMap = NULL; X } else X tfPhotometricInterpretation = PHOTOMETRIC_MINISBLACK; X } X X /* X * Given TIFFTAG_PHOTOMETRIC extract or create the response curves. X */ X switch (tfPhotometricInterpretation) { X case PHOTOMETRIC_RGB: X if (!TIFFGetField(tfFile, TIFFTAG_COLORRESPONSECURVE, X &redMap, &greenMap, &blueMap)) { X redMap = (u_short *) malloc(colormapSize * sizeof(u_short)); X greenMap = (u_short *) malloc(colormapSize * sizeof(u_short)); X blueMap = (u_short *) malloc(colormapSize * sizeof(u_short)); X MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap); X for (i = 0; i < colormapSize; i++) X dRed[i] = dGreen[i] = dBlue[i] X = (double) SCALE(i, colormapSize - 1); X } else { X CheckAndCorrectColormap(); X for (i = 0; i < colormapSize; i++) { X dRed[i] = (double) redMap[i]; X dGreen[i] = (double) greenMap[i]; X dBlue[i] = (double) blueMap[i]; X } X } X break; X case PHOTOMETRIC_PALETTE: X if (!TIFFGetField(tfFile, TIFFTAG_COLORMAP, X &redMap, &greenMap, &blueMap)) { X redMap = (u_short *) malloc(colormapSize * sizeof(u_short)); X greenMap = (u_short *) malloc(colormapSize * sizeof(u_short)); X blueMap = (u_short *) malloc(colormapSize * sizeof(u_short)); X MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap); X for (i = 0; i < colormapSize; i++) X dRed[i] = dGreen[i] = dBlue[i] X = (double) SCALE(i, colormapSize - 1); X } else { X CheckAndCorrectColormap(); X for (i = 0; i < colormapSize; i++) { X dRed[i] = (double) redMap[i]; X dGreen[i] = (double) greenMap[i]; X dBlue[i] = (double) blueMap[i]; X } X } X break; X case PHOTOMETRIC_MINISWHITE: X redMap = (u_short *) malloc(colormapSize * sizeof(u_short)); X greenMap = (u_short *) malloc(colormapSize * sizeof(u_short)); X blueMap = (u_short *) malloc(colormapSize * sizeof(u_short)); X MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap); X if (!TIFFGetField(tfFile, TIFFTAG_GRAYRESPONSECURVE, &grayMap)) X for (i = 0; i < colormapSize; i++) X dRed[i] = dGreen[i] = dBlue[i] X = (double) SCALE(colormapSize - 1 - i, colormapSize - 1); X else { X dRed[colormapSize - 1] = dGreen[colormapSize - 1] X = dBlue[colormapSize - 1] = 0; X for (i = 0; i < colormapSize - 1; i++) X dRed[i] = dGreen[i] = dBlue[i] = 65535.0 - X (65535.0 * pow(10.0, (double) grayMap[i] / tfUnitMap)); X } X break; X case PHOTOMETRIC_MINISBLACK: X redMap = (u_short *) malloc(colormapSize * sizeof(u_short)); X greenMap = (u_short *) malloc(colormapSize * sizeof(u_short)); X blueMap = (u_short *) malloc(colormapSize * sizeof(u_short)); X MCHECK(redMap); MCHECK(greenMap); MCHECK(blueMap); X if (!TIFFGetField(tfFile, TIFFTAG_GRAYRESPONSECURVE, &grayMap)) X for (i = 0; i < colormapSize; i++) { X dRed[i] = dGreen[i] = dBlue[i] X = (double) SCALE(i, colormapSize - 1); X } X else { X dRed[0] = dGreen[0] = dBlue[0] = 0; X for (i = 1; i < colormapSize; i++) X dRed[i] = dGreen[i] = dBlue[i] = X (65535.0 * pow(10.0, (double) grayMap[i] / tfUnitMap)); X } X break; X default: X fprintf(stderr, X "xtiff: can't display photometric interpretation type %d\n", X tfPhotometricInterpretation); X exit(0); X } } X void SetNameLabel() { X char buffer[BUFSIZ]; X Arg args[1]; X X if (tfMultiPage) X sprintf(buffer, "%s - page %d", fileName, tfDirectory); X else X strcpy(buffer, fileName); X XtSetArg(args[0], XtNlabel, buffer); X XtSetValues(labelWidget, args, 1); } X /* X * Many programs get TIFF colormaps wrong. They use 8-bit colormaps instead of X * 16-bit colormaps. This function is a heuristic to detect and correct this. X */ void CheckAndCorrectColormap() { X register int i; X X for (i = 0; i < colormapSize; i++) X if ((redMap[i] > 255) || (greenMap[i] > 255) || (blueMap[i] > 255)) X return; X X for (i = 0; i < colormapSize; i++) { X redMap[i] = SCALE(redMap[i], 255); X greenMap[i] = SCALE(greenMap[i], 255); X blueMap[i] = SCALE(blueMap[i], 255); X } } X void SimpleGammaCorrection() { X register int i; X X dGamma = appData.gamma; X X for (i = 0; i < colormapSize; i++) { X if (((tfPhotometricInterpretation == PHOTOMETRIC_MINISWHITE) X && (i == colormapSize - 1)) X || ((tfPhotometricInterpretation == PHOTOMETRIC_MINISBLACK) X && (i == 0))) X redMap[i] = greenMap[i] = blueMap[i] = 0; X else { X redMap[i] = ROUND((pow(dRed[i] / 65535.0, 1.0 / dGamma) * 65535.0)); X greenMap[i] = ROUND((pow(dGreen[i] / 65535.0, 1.0 / dGamma) * 65535.0)); X blueMap[i] = ROUND((pow(dBlue[i] / 65535.0, 1.0 / dGamma) * 65535.0)); X } X } X X free(dRed); free(dGreen); free(dBlue); } X /* X * Current limitation: the visual is set initially by the first file. X * It cannot be changed. X */ void GetVisual() { X register XColor *colors = NULL; X register u_long *pixels = NULL; X register int i; X X switch (tfImageDepth) { X /* X * X really wants a 32-bit image with the fourth channel unused, X * but the visual structure thinks it's 24-bit. bitmap_unit is 32. X */ X case 32: X case 24: X if (SearchVisualList(24, DirectColor, &xVisual) == False) { X fprintf(stderr, "xtiff: 24-bit DirectColor visual not available\n"); X exit(0); X } X X colors = (XColor *) malloc(3 * colormapSize * sizeof(XColor)); X MCHECK(colors); X X for (i = 0; i < colormapSize; i++) { X colors[i].pixel = (u_long) (i << 16) + (i << 8) + i; X colors[i].red = redMap[i]; X colors[i].green = greenMap[i]; X colors[i].blue = blueMap[i]; X colors[i].flags = DoRed | DoGreen | DoBlue; X } X X xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen), X xVisual, AllocAll); X XStoreColors(xDisplay, xColormap, colors, colormapSize); X break; X case 8: X case 4: X case 2: X /* X * We assume that systems with 24-bit visuals also have 8-bit visuals. X * We don't promote from 8-bit PseudoColor to 24/32 bit DirectColor. X */ X switch (tfPhotometricInterpretation) { X case PHOTOMETRIC_MINISWHITE: X case PHOTOMETRIC_MINISBLACK: X if (SearchVisualList((int) tfImageDepth, GrayScale, &xVisual) == True) X break; X case PHOTOMETRIC_PALETTE: X if (SearchVisualList((int) tfImageDepth, PseudoColor, &xVisual) == True) X break; X default: X fprintf(stderr, "xtiff: Unsupported TIFF/X configuration\n"); X exit(0); X } X X colors = (XColor *) malloc(colormapSize * sizeof(XColor)); X MCHECK(colors); X X for (i = 0; i < colormapSize; i++) { X colors[i].pixel = (u_long) i; X colors[i].red = redMap[i]; X colors[i].green = greenMap[i]; X colors[i].blue = blueMap[i]; X colors[i].flags = DoRed | DoGreen | DoBlue; X } X X /* X * xtiff's colormap allocation is private. It does not attempt X * to detect whether any existing colormap entries are suitable X * for its use. This will cause colormap flashing. Furthermore, X * background and foreground are taken from the environment. X * For example, the foreground color may be red when the visual X * is GrayScale. If the colormap is completely populated, X * Xt will not be able to allocate fg and bg. X */ X if (tfImageDepth == 8) X xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen), X xVisual, AllocAll); X else { X xColormap = XCreateColormap(xDisplay, RootWindow(xDisplay, xScreen), X xVisual, AllocNone); X pixels = (u_long *) malloc(colormapSize * sizeof(u_long)); X MCHECK(pixels); X (void) XAllocColorCells(xDisplay, xColormap, True, X NULL, 0, pixels, colormapSize); X free(pixels); X } X XStoreColors(xDisplay, xColormap, colors, colormapSize); X break; X case 1: X xImageDepth = 1; X xVisual = DefaultVisual(xDisplay, xScreen); X xColormap = DefaultColormap(xDisplay, xScreen); X break; X default: X fprintf(stderr, "xtiff: unsupported image depth %d\n", tfImageDepth); X exit(0); X } X X if (colors != NULL) X free(colors); X if (grayMap != NULL) X free(grayMap); X if (redMap != NULL) X free(redMap); X if (greenMap != NULL) X free(greenMap); X if (blueMap != NULL) X free(blueMap); X X colors = NULL; grayMap = redMap = greenMap = blueMap = NULL; } X Boolean SearchVisualList(image_depth, visual_class, visual) X int image_depth, visual_class; X Visual **visual; { X XVisualInfo template_visual, *visual_list; X int i, n_visuals; X X template_visual.screen = xScreen; X visual_list = XGetVisualInfo(xDisplay, VisualScreenMask, X &template_visual, &n_visuals); X X if (n_visuals == 0) { X fprintf(stderr, "xtiff: visual list not available\n"); X exit(0); X } X X for (i = 0; i < n_visuals; visual_list++, i++) { X if ((visual_list->class == visual_class) X && (visual_list->depth >= image_depth)) { X *visual = visual_list->visual; X xImageDepth = visual_list->depth; X xRedMask = visual_list->red_mask; X xGreenMask = visual_list->green_mask; X xBlueMask = visual_list->blue_mask; X XFree((char *) visual_list); X return True; X } X } X X XFree((char *) visual_list); X return False; } X void GetTIFFImage() { X int pixel_map[3], red_shift, green_shift, blue_shift; X register u_char *scan_line, *output_p, *input_p; X register int i, j, s; X X scan_line = (u_char *) malloc(tfBytesPerRow = TIFFScanlineSize(tfFile)); X MCHECK(scan_line); X X if ((tfImageDepth == 32) || (tfImageDepth == 24)) { X output_p = imageMemory = (u_char *) X malloc(tfImageWidth * tfImageHeight * 4); X MCHECK(imageMemory); X X /* X * Handle different color masks for different frame buffers. X */ X if (ImageByteOrder(xDisplay) == LSBFirst) { /* DECstation 5000 */ X red_shift = pixel_map[0] = xRedMask == 0xFF000000 ? 3 X : (xRedMask == 0xFF0000 ? 2 : (xRedMask == 0xFF00 ? 1 : 0)); X green_shift = pixel_map[1] = xGreenMask == 0xFF000000 ? 3 X : (xGreenMask == 0xFF0000 ? 2 : (xGreenMask == 0xFF00 ? 1 : 0)); X blue_shift = pixel_map[2] = xBlueMask == 0xFF000000 ? 3 X : (xBlueMask == 0xFF0000 ? 2 : (xBlueMask == 0xFF00 ? 1 : 0)); X } else { /* Ardent */ X red_shift = pixel_map[0] = xRedMask == 0xFF000000 ? 0 X : (xRedMask == 0xFF0000 ? 1 : (xRedMask == 0xFF00 ? 2 : 3)); X green_shift = pixel_map[0] = xGreenMask == 0xFF000000 ? 0 X : (xGreenMask == 0xFF0000 ? 1 : (xGreenMask == 0xFF00 ? 2 : 3)); X blue_shift = pixel_map[0] = xBlueMask == 0xFF000000 ? 0 X : (xBlueMask == 0xFF0000 ? 1 : (xBlueMask == 0xFF00 ? 2 : 3)); X } X X if (tfPlanarConfiguration == PLANARCONFIG_CONTIG) { X for (i = 0; i < tfImageHeight; i++) { X if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0) X break; X for (input_p = scan_line, j = 0; j < tfImageWidth; j++) { X *(output_p + red_shift) = *input_p++; X *(output_p + green_shift) = *input_p++; X *(output_p + blue_shift) = *input_p++; X output_p += 4; X if (tfSamplesPerPixel == 4) /* skip the fourth channel */ X input_p++; X } X } X } else { X for (s = 0; s < tfSamplesPerPixel; s++) { X if (s == 3) /* skip the fourth channel */ X continue; X for (i = 0; i < tfImageHeight; i++) { X if (TIFFReadScanline(tfFile, scan_line, i, s) < 0) X break; X input_p = scan_line; X output_p = imageMemory + (i*tfImageWidth*4) + pixel_map[s]; X for (j = 0; j < tfImageWidth; j++, output_p += 4) X *output_p = *input_p++; X } X } X } X } else { X if (xImageDepth == tfImageDepth) { X output_p = imageMemory = (u_char *) X malloc(tfBytesPerRow * tfImageHeight); X MCHECK(imageMemory); X X for (i = 0; i < tfImageHeight; i++, output_p += tfBytesPerRow) X if (TIFFReadScanline(tfFile, output_p, i, 0) < 0) X break; X } else if ((xImageDepth == 8) && (tfImageDepth == 4)) { X output_p = imageMemory = (u_char *) X malloc(tfBytesPerRow * 2 * tfImageHeight + 2); X MCHECK(imageMemory); X X /* X * If a scanline is of odd size the inner loop below will overshoot. X * This is handled very simply by recalculating the start point at X * each scanline and padding imageMemory a little at the end. X */ X for (i = 0; i < tfImageHeight; i++) { X if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0) X break; X output_p = &imageMemory[i * tfImageWidth]; X input_p = scan_line; X for (j = 0; j < tfImageWidth; j += 2, input_p++) { X *output_p++ = *input_p >> 4; X *output_p++ = *input_p & 0xf; X } X } X } else if ((xImageDepth == 8) && (tfImageDepth == 2)) { X output_p = imageMemory = (u_char *) X malloc(tfBytesPerRow * 4 * tfImageHeight + 4); X MCHECK(imageMemory); X X for (i = 0; i < tfImageHeight; i++) { X if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0) X break; X output_p = &imageMemory[i * tfImageWidth]; X input_p = scan_line; X for (j = 0; j < tfImageWidth; j += 4, input_p++) { X *output_p++ = *input_p >> 6; X *output_p++ = (*input_p >> 4) & 3; X *output_p++ = (*input_p >> 2) & 3; X *output_p++ = *input_p & 3; X } X } X } else if ((xImageDepth == 4) && (tfImageDepth == 2)) { X output_p = imageMemory = (u_char *) X malloc(tfBytesPerRow * 2 * tfImageHeight + 2); X MCHECK(imageMemory); X X for (i = 0; i < tfImageHeight; i++) { X if (TIFFReadScanline(tfFile, scan_line, i, 0) < 0) X break; X output_p = &imageMemory[i * tfImageWidth]; X input_p = scan_line; X for (j = 0; j < tfImageWidth; j += 4, input_p++) { X *output_p++ = ((*input_p>>6) << 4) | ((*input_p >> 4) & 3); X *output_p++ = (((*input_p>>2) & 3) << 4) | (*input_p & 3); X } X } X } else { X fprintf(stderr, X "xtiff: can't handle %d-bit TIFF file on an %d-bit display\n", X tfImageDepth, xImageDepth); X exit(0); X } X } X X free(scan_line); } X void CreateXImage() { X XGCValues gc_values; X GC bitmap_gc; X X xOffset = yOffset = 0; X grabX = grabY = -1; X X xImage = XCreateImage(xDisplay, xVisual, xImageDepth, X xImageDepth == 1 ? XYBitmap : ZPixmap, /* offset */ 0, X (char *) imageMemory, tfImageWidth, tfImageHeight, X /* bitmap_pad */ 8, /* bytes_per_line */ 0); X X /* X * libtiff converts LSB data into MSB but doesn't change the FillOrder tag. X */ X if (xImageDepth == 1) X xImage->bitmap_bit_order = MSBFirst; X if (xImageDepth <= 8) X xImage->byte_order = MSBFirst; X X /* X * create an appropriate GC X */ X gc_values.function = GXcopy; X gc_values.plane_mask = AllPlanes; X if (tfPhotometricInterpretation == PHOTOMETRIC_MINISBLACK) { X gc_values.foreground = XWhitePixel(xDisplay, xScreen); X gc_values.background = XBlackPixel(xDisplay, xScreen); X } else { X gc_values.foreground = XBlackPixel(xDisplay, xScreen); X gc_values.background = XWhitePixel(xDisplay, xScreen); X } X xWinGc = XCreateGC(xDisplay, XtWindow(shellWidget), X GCFunction | GCPlaneMask | GCForeground | GCBackground, &gc_values); X X /* X * create the pixmap and load the image X */ X if (appData.usePixmap == True) { X xImagePixmap = XCreatePixmap(xDisplay, RootWindow(xDisplay, xScreen), X xImage->width, xImage->height, xImageDepth); X X /* X * According to the O'Reilly X Protocol Reference Manual, page 53, X * "A pixmap depth of one is always supported and listed, but windows X * of depth one might not be supported." Therefore we create a pixmap X * of depth one and use XCopyPlane(). This is idiomatic. X */ X if (xImageDepth == 1) { /* just pass the bits through */ X gc_values.foreground = 1; /* foreground describes set bits */ X gc_values.background = 0; /* background describes clear bits */ X bitmap_gc = XCreateGC(xDisplay, xImagePixmap, X GCForeground | GCBackground, &gc_values); X XPutImage(xDisplay, xImagePixmap, bitmap_gc, xImage, X 0, 0, 0, 0, xImage->width, xImage->height); X } else X XPutImage(xDisplay, xImagePixmap, xWinGc, xImage, X 0, 0, 0, 0, xImage->width, xImage->height); X XDestroyImage(xImage); X } } X XXtCallbackProc SelectProc(w, unused_1, unused_2) X Widget w; X caddr_t unused_1; X caddr_t unused_2; { X XawListReturnStruct *list_return; X X list_return = XawListShowCurrent(w); X X switch (list_return->list_index) { X case ButtonQuit: X QuitProc(); X break; X case ButtonPreviousPage: X PreviousProc(); X break; X case ButtonNextPage: X NextProc(); X break; X default: X fprintf(stderr, "error in SelectProc\n"); X exit(0); X } X XawListUnhighlight(w); } X void QuitProc(void) { X exit(0); } X void NextProc() { X PageProc(ButtonNextPage); } X void PreviousProc() { X PageProc(ButtonPreviousPage); } X void PageProc(direction) X int direction; { X XEvent fake_event; X Arg args[4]; X X switch (direction) { X case ButtonPreviousPage: X if (tfDirectory > 0) X TIFFSetDirectory(tfFile, --tfDirectory); X else X return; X break; X case ButtonNextPage: X if (TIFFReadDirectory(tfFile) == True) X tfDirectory++; X else X return; X break; X default: X fprintf(stderr, "error in PageProc\n"); X exit(0); X } X X xOffset = yOffset = 0; X grabX = grabY = -1; X X GetTIFFHeader(); X SetNameLabel(); X GetTIFFImage(); X X if (appData.usePixmap == True) X XFreePixmap(xDisplay, xImagePixmap); X else X XDestroyImage(xImage); X X CreateXImage(); X X /* X * Using XtSetValues() to set the widget size causes a resize. X * This resize gets propagated up to the parent shell. X * In order to disable this visually disconcerting effect, X * shell resizing is temporarily disabled. X */ X XtSetArg(args[0], XtNallowShellResize, False); X XtSetValues(shellWidget, args, 1); X X XtSetArg(args[0], XtNwidth, tfImageWidth); X XtSetArg(args[1], XtNheight, tfImageHeight); X XtSetValues(imageWidget, args, 2); X X XtSetArg(args[0], XtNallowShellResize, True); X XtSetValues(shellWidget, args, 1); X X XClearWindow(xDisplay, XtWindow(imageWidget)); X X fake_event.type = Expose; X fake_event.xexpose.x = fake_event.xexpose.y = 0; X fake_event.xexpose.width = tfImageWidth; /* the window will clip */ X fake_event.xexpose.height = tfImageHeight; X EventProc(imageWidget, NULL, &fake_event); } X void EventProc(widget, unused, event) X Widget widget; X caddr_t unused; X XEvent *event; { X int ih, iw, ww, wh, sx, sy, w, h, dx, dy; X Dimension w_width, w_height; X XEvent next_event; X Arg args[2]; X X if (event->type == MappingNotify) { X XRefreshKeyboardMapping((XMappingEvent *) event); X return; X } X X if (!XtIsRealized(widget)) X return; X X if ((event->type == ButtonPress) || (event->type == ButtonRelease)) X if (event->xbutton.button != Button1) X return; X X iw = tfImageWidth; /* avoid sign problems */ X ih = tfImageHeight; X X /* X * The grabX and grabY variables record where the user grabbed the image. X * They also record whether the mouse button is down or not. X */ X if (event->type == ButtonPress) { X grabX = event->xbutton.x; X grabY = event->xbutton.y; X return; X } X X /* X * imageWidget is a Core widget and doesn't get resized. X * So we calculate the size of its viewport here. X */ X XtSetArg(args[0], XtNwidth, &w_width); X XtSetArg(args[1], XtNheight, &w_height); X XtGetValues(shellWidget, args, 2); X ww = w_width; X wh = w_height; X XtGetValues(listWidget, args, 2); X wh -= w_height; X X switch (event->type) { X case Expose: X dx = event->xexpose.x; X dy = event->xexpose.y; X sx = dx + xOffset; X sy = dy + yOffset; X w = MIN(event->xexpose.width, iw); X h = MIN(event->xexpose.height, ih); X break; X case KeyPress: X if ((grabX >= 0) || (grabY >= 0)) /* Mouse button is still down */ X return; X switch (XLookupKeysym((XKeyEvent *) event, /* KeySyms index */ 0)) { X case XK_Up: X if (ih < wh) /* Don't scroll if the window fits the image. */ X return; X sy = yOffset + appData.translate; X sy = MIN(ih - wh, sy); X if (sy == yOffset) /* Filter redundant stationary refreshes. */ X return; X yOffset = sy; X sx = xOffset; X dx = dy = 0; X w = ww; h = wh; X break; X case XK_Down: X if (ih < wh) X return; X sy = yOffset - appData.translate; X sy = MAX(sy, 0); X if (sy == yOffset) X return; X yOffset = sy; X sx = xOffset; X dx = dy = 0; X w = ww; h = wh; X break; X case XK_Left: X if (iw < ww) X return; X sx = xOffset + appData.translate; X sx = MIN(iw - ww, sx); X if (sx == xOffset) X return; X xOffset = sx; X sy = yOffset; X dx = dy = 0; X w = ww; h = wh; X break; X case XK_Right: X if (iw < ww) X return; X sx = xOffset - appData.translate; X sx = MAX(sx, 0); X if (sx == xOffset) X return; X xOffset = sx; X sy = yOffset; X dx = dy = 0; X w = ww; h = wh; X break; X default: X return; X } X break; X case MotionNotify: X /* X * MotionEvent compression. Ignore multiple motion events. X * Ignore motion events if the mouse button is up. X */ X if (XPending(xDisplay)) /* Xlib doesn't flush the output buffer */ X if (XtPeekEvent(&next_event)) X if (next_event.type == MotionNotify) X return; X if ((grabX < 0) || (grabY < 0)) X return; X sx = xOffset + grabX - (int) event->xmotion.x; X if (sx >= (iw - ww)) /* clamp x motion but allow y motion */ X sx = iw - ww; X sx = MAX(sx, 0); X sy = yOffset + grabY - (int) event->xmotion.y; X if (sy >= (ih - wh)) /* clamp y motion but allow x motion */ X sy = ih - wh; X sy = MAX(sy, 0); X if ((sx == xOffset) && (sy == yOffset)) X return; X dx = dy = 0; X w = ww; h = wh; X break; X case ButtonRelease: X xOffset = xOffset + grabX - (int) event->xbutton.x; X xOffset = MIN(iw - ww, xOffset); X xOffset = MAX(xOffset, 0); X yOffset = yOffset + grabY - (int) event->xbutton.y; X yOffset = MIN(ih - wh, yOffset); X yOffset = MAX(yOffset, 0); X grabX = grabY = -1; X default: X return; X } X X if (appData.usePixmap == True) { X if (xImageDepth == 1) X XCopyPlane(xDisplay, xImagePixmap, XtWindow(widget), X xWinGc, sx, sy, w, h, dx, dy, 1); X else X XCopyArea(xDisplay, xImagePixmap, XtWindow(widget), X xWinGc, sx, sy, w, h, dx, dy); X } else X XPutImage(xDisplay, XtWindow(widget), xWinGc, xImage, X sx, sy, dx, dy, w, h); } X void ResizeProc() { X Dimension w_width, w_height; X int xo, yo, ww, wh; X XEvent fake_event; X Arg args[2]; X X if ((xOffset == 0) && (yOffset == 0)) X return; X X XtSetArg(args[0], XtNwidth, &w_width); X XtSetArg(args[1], XtNheight, &w_height); X XtGetValues(shellWidget, args, 2); X ww = w_width; X wh = w_height; X XtGetValues(listWidget, args, 2); X wh -= w_height; X X xo = xOffset; yo = yOffset; X X if ((xOffset + ww) >= tfImageWidth) X xOffset = MAX((int) tfImageWidth - ww, 0); X if ((yOffset + wh) >= tfImageHeight) X yOffset = MAX((int) tfImageHeight - wh, 0); X X /* X * Send an ExposeEvent if the origin changed. X * We have to do this because of the use and semantics of bit gravity. X */ X if ((xo != xOffset) || (yo != yOffset)) { X fake_event.type = Expose; X fake_event.xexpose.x = fake_event.xexpose.y = 0; X fake_event.xexpose.width = tfImageWidth; X fake_event.xexpose.height = tfImageHeight; X EventProc(imageWidget, NULL, &fake_event); X } } X int XXTiffErrorHandler(display, error_event) X Display *display; X XErrorEvent *error_event; { X char message[80]; X X /* X * Some X servers limit the size of pixmaps. X */ X if ((error_event->error_code == BadAlloc) X && (error_event->request_code == X_CreatePixmap)) X fprintf(stderr, "xtiff: requested pixmap too big for display\n"); X else { X XGetErrorText(display, error_event->error_code, message, 80); X fprintf(stderr, "xtiff: error code %s\n", message); X } X X exit(0); } X void Usage() { X fprintf(stderr, "Usage xtiff: [options] tiff-file\n"); X fprintf(stderr, "\tstandard Xt options\n"); X fprintf(stderr, "\t[-help]\n"); X fprintf(stderr, "\t[-gamma gamma]\n"); X fprintf(stderr, "\t[-usePixmap (True | False)]\n"); X fprintf(stderr, "\t[-viewportWidth pixels]\n"); X fprintf(stderr, "\t[-viewportHeight pixels]\n"); X fprintf(stderr, "\t[-translate pixels]\n"); X fprintf(stderr, "\t[-verbose (True | False)]\n"); X exit(0); } SHAR_EOF echo 'File xtiff/athena/xtiff.c is complete' && chmod 0644 xtiff/athena/xtiff.c || echo 'restore of xtiff/athena/xtiff.c failed' Wc_c="`wc -c < 'xtiff/athena/xtiff.c'`" test 42696 -eq "$Wc_c" || echo 'xtiff/athena/xtiff.c: original size 42696, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= xtiff/athena/xtiff.man ============== if test -f 'xtiff/athena/xtiff.man' -a X"$1" != X"-c"; then echo 'x - skipping xtiff/athena/xtiff.man (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting xtiff/athena/xtiff.man (Text)' sed 's/^X//' << 'SHAR_EOF' > 'xtiff/athena/xtiff.man' && .TH XTIFF 1 "December 20, 1990" "X Version 11" .SH NAME xtiff \- view a TIFF file in an X window .SH SYNOPSIS .B xtiff [ .IR "standard Xt options" ] [ .IR "\-gamma gamma" ] [ .IR "\-usePixmap (False | True)" ] [ .IR "\-viewportWidth pixels" ] [ .IR "\-viewportHeight pixels" ] [ .IR "\-translate pixels" ] [ .IR "\-verbose (False | True)" ] .IR file .SH DESCRIPTION .IR xtiff is a TIFF viewer for X based on the .IR libtiff (3) package. It displays a TIFF file in an X window that can be resized and panned. On appropriate display hardware it can handle 24-bit RGB color TIFF files, 8, 4 and 2-bit palette color TIFF files and 8, 4, 2 and 1-bit grayscale TIFF files. The .IR Next and .IR Previous buttons allow the user to view different pages in a multi-page TIFF file. .PP If the image is larger than the window, you can pan around the image with the mouse or arrow keys. Grab the image by pressing down and holding the left mouse button and then drag the mouse to expose a different region of the image. The arrow keys provide another method for moving a large image inside a smaller window. .PP .IR xtiff manages the negotiation between the needs of an image and the visual capabilities made available by an X server. If necessary, it will promote an image to a deeper visual, but it will not demote an image by quantizing and/or dithering. In that case it will fail to display the image. .PP .IR xtiff reads the .IR "Gray Response Curve" , .IR "Gray Response Unit" and .IR "Color Response Curve" tags in a TIFF file. The data in these tags describe gamma compensation or image companding. Together with the .IR \-gamma option or the NTSC default gamma value of 2.2, the image will be gamma corrected and displayed. .PP For example, if a TIFF file has been prepared for a typical display, it has a gamma compensation of 2.2 built into either the image or preferably the .IR "Color Response Curve" tag. This is a device-dependent image and, in this case, the value for the .IR \-gamma command line option should be 1.0. If the .IR "Color Response Curve" tag describes a companded but otherwise device-independent image then the command line gamma should be set according to the monitor. .PP Unfortunately there is no way of knowing a priori whether or not an image is device-independent without knowing its ancestry. If the image conforms to the TIFF 5.0 specification it should be device-independent; but many scanner and image processing programs do not adhere rigorously to the standard. .SH BUTTONS AND KEYS By default, .IR xtiff has certain buttons and keys bound to certain functions. .TP 8 .B Left Mouse Button Drags the image around the window. .TP 8 .B N \fRor\fP n Identical to the .IR Next function. .TP 8 .B N \fRor\fP n Identical to the .IR Previous function. .TP 8 .B Q \fRor\fP q Identical to the .IR Quit function. .TP 8 .B arrow keys Pressing one of the arrow keys translates the image by an amount specified by the .IR translate option. .SH OPTIONS All options are resources and can be set by either the command line or the .IR \&.Xdefaults file. .TP 8 .B -gamma gamma Specifies the value used to build compensation tables for simple gamma correction. .TP 8 .B -usePixmap (False | True) Indicates that .IR xtiff should not use a pixmap to store the image on a server. This option is necessary because some servers impose size limitations on pixmaps. Not using a pixmap is slower because the image must be stored on the client side and transferred for each exposure or refresh. Default: True. .TP 8 .B -viewportWidth pixels Indicates the width of the image viewport. Default: 700 or image width, whichever is less. .TP 8 .B -viewportHeight pixels Indicates the height of the image viewport. Default: 500 or image height, whichever is less. .TP 8 .B -translate pixels Indicates the step size that the arrow keys use for translating an image. Default: 20 pixels. .TP 8 .B -verbose (False | True) Indicates whether .IR xtiff displays .IR libtiff error messages. Default: False. .SH SEE ALSO .IR libtiff (3), .br .IR "Tag Image File Format Specification \(em Revision 5.0" , Aldus Corporation, August 8, 1988. .br .IR "The Spirit of TIFF Class F" , Cygnet Technologies, revised March 29, 1990. .SH LIMITATIONS .IR xtiff does not support the complete repertoire of all possible TIFF files on all possible visual/depth combinations. .PP .IR xtiff supports TIFF class B (bilevel), class G (grayscale), class P (palette color), class R (RGB color) and class F (FAX). .PP Only the top-left orientation is supported. This is both the X orientation and the TIFF default. .PP Gamma correction is simple and there is no colorimetry support. .PP .IR xtiff assumes that servers with 24-bit visuals also have 8-bit visuals. An 8-bit image is not promoted to 24-bit on the client side. .PP There is no support for .IR StaticGray , .IR StaticColor or .IR TrueColor visuals except for 1-bit images. There is no support for 3/3/2 .IR DirectColor visuals. .PP When displaying 8-bit images, .IR xtiff creates and populates a private colormap with the TIFF colormap or response curve. .IR Xt will complain about failing to allocate colors for the foreground and background colors. .IR xtiff can't allow this because the colormap is already full. .IR Xt should find the nearest substitutes and use them but it doesn't. SHAR_EOF chmod 0644 xtiff/athena/xtiff.man || echo 'restore of xtiff/athena/xtiff.man failed' Wc_c="`wc -c < 'xtiff/athena/xtiff.man'`" test 5315 -eq "$Wc_c" || echo 'xtiff/athena/xtiff.man: original size 5315, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= xtiff/athena/xtifficon.h ============== if test -f 'xtiff/athena/xtifficon.h' -a X"$1" != X"-c"; then echo 'x - skipping xtiff/athena/xtifficon.h (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting xtiff/athena/xtifficon.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'xtiff/athena/xtifficon.h' && #define xtifficon_width 32 #define xtifficon_height 32 static char xtifficon_bits[] = { X 0xff, 0x00, 0x00, 0xc0, 0xfe, 0x01, 0x7e, 0xc0, 0xfc, 0x03, 0x7e, 0x60, X 0xf8, 0x07, 0x06, 0x30, 0xf8, 0x07, 0x1e, 0x18, 0xf0, 0x0f, 0x1e, 0x0c, X 0xe0, 0x1f, 0x06, 0x06, 0xc0, 0x3f, 0x06, 0x06, 0xc0, 0x3f, 0x06, 0x03, X 0x80, 0x7f, 0x80, 0x01, 0x00, 0xff, 0xc0, 0x00, 0x00, 0xfe, 0x61, 0x00, X 0x00, 0xfe, 0x31, 0x7e, 0x7e, 0xfc, 0x33, 0x7e, 0x7e, 0xf8, 0x1b, 0x06, X 0x18, 0xf0, 0x0d, 0x1e, 0x18, 0xf0, 0x0e, 0x1e, 0x18, 0x60, 0x1f, 0x06, X 0x18, 0xb0, 0x3f, 0x06, 0x18, 0x98, 0x7f, 0x06, 0x18, 0x98, 0x7f, 0x00, X 0x00, 0x0c, 0xff, 0x00, 0x00, 0x06, 0xfe, 0x01, 0x00, 0x63, 0xfc, 0x03, X 0x80, 0x61, 0xfc, 0x03, 0xc0, 0x60, 0xf8, 0x07, 0xc0, 0x60, 0xf0, 0x0f, X 0x60, 0x60, 0xe0, 0x1f, 0x30, 0x60, 0xe0, 0x1f, 0x18, 0x60, 0xc0, 0x3f, X 0x0c, 0x60, 0x80, 0x7f, 0x06, 0x00, 0x00, 0xff}; SHAR_EOF chmod 0644 xtiff/athena/xtifficon.h || echo 'restore of xtiff/athena/xtifficon.h failed' Wc_c="`wc -c < 'xtiff/athena/xtifficon.h'`" test 890 -eq "$Wc_c" || echo 'xtiff/athena/xtifficon.h: original size 890, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= xtiff/athena/Imakefile ============== if test -f 'xtiff/athena/Imakefile' -a X"$1" != X"-c"; then echo 'x - skipping xtiff/athena/Imakefile (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting xtiff/athena/Imakefile (Text)' sed 's/^X//' << 'SHAR_EOF' > 'xtiff/athena/Imakefile' && # # Imakefile -- to generate a Makefile for xtiff, use: # /usr/local/X11/mit/config/imake \ # -I/usr/local/X11/mit/config \ # -DTOPDIR=/usr/local/X11/mit \ # -DCURDIR=/usr/local/X11/mit \ # -DDESTDIR=/usr/local/X11/mit # X X SYS_LIBRARIES = -lm X LOCAL_LIBRARIES = XawClientLibs X DEPLIBS = XawClientDepLibs X TIFF = ../2.3/libtiff X EXTRA_LIBRARIES = $(TIFF)/libtiff.a X EXTRA_INCLUDES = -I$(TIFF) X SimpleProgramTarget(xtiff) SHAR_EOF chmod 0644 xtiff/athena/Imakefile || echo 'restore of xtiff/athena/Imakefile failed' Wc_c="`wc -c < 'xtiff/athena/Imakefile'`" test 468 -eq "$Wc_c" || echo 'xtiff/athena/Imakefile: original size 468, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= xtiff/athena/patchlevel.h ============== if test -f 'xtiff/athena/patchlevel.h' -a X"$1" != X"-c"; then echo 'x - skipping xtiff/athena/patchlevel.h (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting xtiff/athena/patchlevel.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'xtiff/athena/patchlevel.h' && #define PATCHLEVEL 0 SHAR_EOF chmod 0644 xtiff/athena/patchlevel.h || echo 'restore of xtiff/athena/patchlevel.h failed' Wc_c="`wc -c < 'xtiff/athena/patchlevel.h'`" test 21 -eq "$Wc_c" || echo 'xtiff/athena/patchlevel.h: original size 21, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi rm -f _shar_seq_.tmp echo You have unpacked the last part exit 0 -- Dan Heller O'Reilly && Associates Z-Code Software Comp-sources-x: Senior Writer President comp-sources.x@uunet.uu.net argv@ora.com argv@zipcode.com