dm@rainbow.oulu.fi (Hannu Helminen) (05/23/91)
#! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 2 (of 2)." # Contents: cmap.c x.c # Wrapped by dm@stekt2 on Thu May 23 16:38:45 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f cmap.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"cmap.c\" else echo shar: Extracting \"cmap.c\" \(15828 characters\) sed "s/^X//" >cmap.c <<'END_OF_cmap.c' X X/* This module is a dull one. It is somewhat important, however, X * because it handles most of the interface between the program X * and the user. X X * This module will allocate colors, free them; handle events of the X * colormap window, including gadgets, sliders and spectrum; X * it will also handle the mapping between iteration levels and X * colors. X X * callable functios: X * void ToggleCWin(display, window, mode); X * int HandleCEvent(Display *display, int screen, Window window, X GC gc1, GC gc2, GC gc3, Xevent *event); X * void AllocColors(Display *display, int screen); X * void FreeColors(Display *display, int screen); X * void SetNColors(int number_of_colors); X * unsigned long ToPixel(int level); X X */ X X#include <X11/Xlib.h> X#include <math.h> X#include <stdio.h> X#include "cmap.h" X#include "misc.h" X X/* Geometries of gadgets.. */ X#define GAD_Y1 5 X#define GAD_Y2 20 X#define GAD_LEFT 10 X#define GAD_GAP 8 X/* ...Sliders... */ X#define TOP_GAP 25 X#define CHAR_X 5 X#define LINE_X 20 X#define BOX_X1 30 X#define BOX_X2 10 X#define BOX_Y1 3 X#define BOX_Y2 3 X#define BOTTOM_GAP 25 X/* ... cmap */ X#define CMAP_Y1 20 X#define CMAP_Y2 5 X#define BLACK_SIZE 4 X X#define SLIDERSTEPS 64 X#define MAXSLIDE 65536 X X#define GAMMA 2.2 X Xstatic char *gadget[]= {"Apply", "Redraw", "Previous", "Restart", "Quit"}; X#define NUMGAD (sizeof(gadget)/sizeof(char *)) Xstatic struct gad_pos{ X int s, e; X } gadpos[NUMGAD]; X Xstatic long red[SLIDERSTEPS], green[SLIDERSTEPS], X blue[SLIDERSTEPS]; Xstatic long red0, green0, blue0; Xstatic int coff= 0; /* color offset */ X Xstatic unsigned long *pixel; Xstatic char *alloc; Xstatic int allocated= 0; Xstatic int ncolors= 0; X/* Does the current colormap reflect position of sliders? */ Xstatic int applied= 0; X Xstatic int mapped= 0; X Xstatic int width, height; X Xvoid ToggleCWin(display, win, mode) XDisplay *display; XWindow win; Xint mode; X{ Xif (mapped && (mode == CMAP_TOGGLE || mode == CMAP_CLOSE)) { X XUnmapWindow(display, win); X mapped= 0; X } Xelse if (!mapped && (mode == CMAP_TOGGLE || mode == CMAP_OPEN)) { X XMapWindow(display, win); X mapped= 1; X } X} X X Xvoid DrawHotSpot(display, win, gc, x, y) XDisplay *display; XWindow win; XGC gc; Xint x,y; X{ XXDrawRectangle(display, win, gc, x-3, y-3, 6, 6); XXDrawRectangle(display, win, gc, x-2, y-2, 4, 4); X} X X Xvoid DrawSliders(display, win, gc, n) XDisplay *display; XWindow win; XGC gc; Xint n; X{ Xint i; Xint zone= (height-TOP_GAP-BOTTOM_GAP)/3; Xlong *color; X Xswitch(n) { X case 0: color= red; break; X case 1: color= green; break; X case 2: color= blue; break; X } Xfor (i= 0; i < SLIDERSTEPS; i++) { X int j= (i+1) % SLIDERSTEPS; X int x1= BOX_X1 + 1+i*(width-BOX_X1-BOX_X2-2)/SLIDERSTEPS; X int x2= BOX_X1 + 1+(i+1)*(width-BOX_X1-BOX_X2-2)/SLIDERSTEPS; X int y1= TOP_GAP + n*zone + BOX_Y1 + X 1 + ((zone-BOX_Y1-BOX_Y2-2)*(MAXSLIDE-color[i]))/MAXSLIDE; X int y2= TOP_GAP + n*zone + BOX_Y1 + X 1 + ((zone-BOX_Y1-BOX_Y2-2)*(MAXSLIDE-color[j]))/MAXSLIDE; X X XDrawLine(display, win, gc, x1, y1, x2, y2); X } X} X X Xvoid DrawSlider(display, win, gc1, gc2, n, trash) XDisplay *display; XWindow win; XGC gc1, gc2; Xint n, trash; X{ Xint zone =(height-TOP_GAP-BOTTOM_GAP) / 3; Xstatic int oy[3]= {-1, -1, -1}; Xint y; Xint h; X Xswitch(n) { X case 0: h= red0; break; X case 1: h= green0; break; X case 2: h= blue0; break; X default: break; X } Xy= TOP_GAP + n*zone + BOX_Y1 + ((zone-BOX_Y1-BOX_Y2)*(MAXSLIDE-h))/MAXSLIDE; X Xif (y != oy[n] || trash) { X if (oy[n] >= 0) X DrawHotSpot(display, win, gc2, LINE_X, oy[n]); X DrawHotSpot(display, win, gc1, LINE_X, y); X oy[n]= y; X XDrawLine(display, win, gc1, X LINE_X, TOP_GAP+n*zone+BOX_Y1, LINE_X, TOP_GAP+(n+1)*zone-BOX_Y2); X } X} X X Xvoid DrawSliderBox(display, win, gc, n) XDisplay *display; XWindow win; XGC gc; Xint n; X{ Xint zone= (height-TOP_GAP-BOTTOM_GAP)/3; Xchar ch[2]; X Xch[1]= '\0'; Xswitch(n) { X case 0: ch[0]= 'R'; break; X case 1: ch[0]= 'G'; break; X case 2: ch[0]= 'B'; break; X } XXDrawRectangle(display, win, gc, X BOX_X1, TOP_GAP + n*zone + BOX_Y1, X width-BOX_X1-BOX_X2, zone-BOX_Y1-BOX_Y2); XWriteText(display, win, gc, ch, CHAR_X, X TOP_GAP + n*zone + BOX_Y1 + (zone-BOX_Y1-BOX_Y2)/2-6 ); X} X X Xvoid DrawColorBox(display, win, gc, gc1) XDisplay *display; XWindow win; XGC gc, gc1; X{ Xint i; XXGCValues gcval; X Xgcval.foreground= pixel[0]; XXChangeGC(display, gc, GCForeground, &gcval); X XXFillRectangle(display, win, gc, X LINE_X-BLACK_SIZE, height-CMAP_Y1, X 2*BLACK_SIZE, CMAP_Y1-CMAP_Y2); XXDrawRectangle(display, win, gc1, X LINE_X-BLACK_SIZE, height-CMAP_Y1, X 2*BLACK_SIZE, CMAP_Y1-CMAP_Y2); X Xfor (i= 1; i < ncolors; i++) { X int x1= ((width-BOX_X2-BOX_X1)*(i-1)/(ncolors-1)); X int x2= ((width-BOX_X2-BOX_X1)*i/(ncolors-1)); X X gcval.foreground= pixel[i]; X XChangeGC(display, gc, GCForeground, &gcval); X X XFillRectangle(display, win, gc, X BOX_X1+x1, height-CMAP_Y1, X x2-x1, CMAP_Y1-CMAP_Y2); X } XXDrawRectangle(display, win, gc1, X BOX_X1, height-CMAP_Y1, X width-BOX_X2-BOX_X1, CMAP_Y1-CMAP_Y2); X} /* DrawColorBox() */ X X Xint InCmap(x, y) Xint x, y; X{ Xint c; Xif (y < height-CMAP_Y1 || y > height-CMAP_Y2) X return(0); Xc= (x-BOX_X1)*ncolors/(width-BOX_X2-BOX_X1); Xif (c<= 0 || c >= ncolors) X return(0); Xreturn(c); X} X X Xint InSlider(x, y, n) Xint x, y, n; X{ Xint zone= (height-TOP_GAP-BOTTOM_GAP)/3; X Xif (y < TOP_GAP + (n%3)*zone + BOX_Y1 || y > TOP_GAP + ((n%3)+1)*zone - BOX_Y2) X return 0; Xif (n < 3) { X if (x > (3*LINE_X - BOX_X1) / 2 && x < (LINE_X+BOX_X1) / 2) X return 1; X } Xelse { X if (x > BOX_X1 && x < width-BOX_X2) X return 1; X } Xreturn 0; X} X X Xvoid DragSlider(display, win, gc1, gc2, n, x1, y1, x2, y2) XDisplay *display; XWindow win; XGC gc1, gc2; Xint n, x1, y1, x2, y2; X{ Xint zone= (height-TOP_GAP-BOTTOM_GAP)/3; Xint s1= (x1-BOX_X1)*SLIDERSTEPS / (width-BOX_X1-BOX_X2+1); Xint s2= (x2-BOX_X1)*SLIDERSTEPS / (width-BOX_X1-BOX_X2+1); Xint c1= ((TOP_GAP + ((n%3)+1)*zone - BOX_Y2) - y1) * X (MAXSLIDE-1) / (zone - BOX_Y2 - BOX_Y1); Xint c2= ((TOP_GAP + ((n%3)+1)*zone - BOX_Y2) - y2) * X (MAXSLIDE-1) / (zone - BOX_Y2 - BOX_Y1); X Xif (c1 < 0) c1= 0; Xif (c2 < 0) c2= 0; Xif (c1 >= MAXSLIDE) c1= MAXSLIDE-1; Xif (c2 >= MAXSLIDE) c2= MAXSLIDE-1; Xif (s1 < 0) s1= 0; Xif (s2 < 0) s2= 0; Xif (s1 >= SLIDERSTEPS) s1= SLIDERSTEPS-1; Xif (s2 >= SLIDERSTEPS) s2= SLIDERSTEPS-1; Xif (s1 > s2) { X int tmp= s2; s2= s1; s1= tmp; X tmp= c2; c2= c1; c1= tmp; X } X Xapplied= 0; /* colormap no longer valid */ Xif (n < 3) { X switch(n) { X case 0: red0= c2; break; X case 1: green0= c2; break; X case 2: blue0= c2; break; X } X DrawSlider(display, win, gc1, gc2, n, 0); X } /* if */ Xelse { X long *color; X int i; X n -= 3; X switch(n) { X case 0: color= red; break; X case 1: color= green; break; X case 2: color= blue; break; X } X for (i= s1-1; i <= s2; i++) { X int j= i; X int k= (i+1) % SLIDERSTEPS; X int x1, x2, y1, y2; X X if (j < 0) j += SLIDERSTEPS; X x1= BOX_X1 + 1+j*(width-BOX_X1-BOX_X2-2)/SLIDERSTEPS; X x2= BOX_X1 + 1+(j+1)*(width-BOX_X1-BOX_X2-2)/SLIDERSTEPS; X y1= TOP_GAP + n*zone + BOX_Y1 + X 1 + ((zone-BOX_Y1-BOX_Y2-2)*(MAXSLIDE-color[j]))/MAXSLIDE; X y2= TOP_GAP + n*zone + BOX_Y1 + X 1 + ((zone-BOX_Y1-BOX_Y2-2)*(MAXSLIDE-color[k]))/MAXSLIDE; X XDrawLine(display, win, gc2, x1, y1, x2, y2); X } X if (s1 == s2) X color[s1]= c1; X else X for (i= 0; i <= s2-s1; i++) X color[s1+i]= c1*(s2-s1-i)/(s2-s1) + c2*i/(s2-s1); X for (i= s1-1; i <= s2; i++) { X int j= i; X int k= (i+1) % SLIDERSTEPS; X int x1, x2, y1, y2; X X if (j < 0) j += SLIDERSTEPS; X x1= BOX_X1 + 1+j*(width-BOX_X1-BOX_X2-2)/SLIDERSTEPS; X x2= BOX_X1 + 1+(j+1)*(width-BOX_X1-BOX_X2-2)/SLIDERSTEPS; X y1= TOP_GAP + n*zone + BOX_Y1 + X 1 + ((zone-BOX_Y1-BOX_Y2-2)*(MAXSLIDE-color[j]))/MAXSLIDE; X y2= TOP_GAP + n*zone + BOX_Y1 + X 1 + ((zone-BOX_Y1-BOX_Y2-2)*(MAXSLIDE-color[k]))/MAXSLIDE; X XDrawLine(display, win, gc1, x1, y1, x2, y2); X } X } /* else */ X} /* DragSlider() */ X X Xvoid DrawGadgets(display, win, gc) XDisplay *display; XWindow win; XGC gc; X{ Xint i; Xint x= GAD_LEFT; Xfor (i= 0; i < NUMGAD; i++) { X int len= WriteText(display, win, gc, gadget[i], x+3, GAD_Y1+1); X gadpos[i].s= x; X gadpos[i].e= x+len+6; X XDrawRectangle(display, win, gc, x, GAD_Y1, X len+6, GAD_Y2-GAD_Y1); X x+= len+6 + GAD_GAP; X } X} X X Xint CheckGadget(display, win, gc1, gc2, x, y) XDisplay *display; XWindow win; XGC gc1, gc2; Xint x, y; X{ Xint i; Xstatic int drawn= -1; Xif (y >= GAD_Y1 && y <= GAD_Y2) { X for (i= 0; i < NUMGAD; i++) { X if (x >= gadpos[i].s && x <= gadpos[i].e) { X if (drawn >= 0 && drawn != i) X XDrawRectangle(display, win, gc2, gadpos[drawn].s+1, GAD_Y1+1, X gadpos[drawn].e-gadpos[drawn].s-2, GAD_Y2-GAD_Y1-2); X if (drawn != i) X XDrawRectangle(display, win, gc1, gadpos[i].s+1, GAD_Y1+1, X gadpos[i].e-gadpos[i].s-2, GAD_Y2-GAD_Y1-2); X drawn= i; X return(i); X } X } /* for */ X } /* if */ Xif (drawn >= 0) { X XDrawRectangle(display, win, gc2, gadpos[drawn].s+1, GAD_Y1+1, X gadpos[drawn].e-gadpos[drawn].s-2, GAD_Y2-GAD_Y1-2); X drawn= -1; X } Xreturn(-1); X} X X Xint HandleCEvent(display, screen, win, gc, gc1, gc2, event) XDisplay *display; Xint screen; XWindow win; XGC gc, gc1, gc2; XXEvent *event; X{ Xint i; Xstatic int dragging= -1; Xstatic int cmaphandle= 0, c1, c2; Xstatic int dragx, dragy; Xint gadget; X Xswitch (event->type) { X case Expose: X /* This is a simple application, redraw the whole X * window only when no more expose events exist. X */ X if (event->xexpose.count) X break; X for (i= 0; i < 3; i++) { X DrawSliderBox(display, win, gc1, i); X DrawSliders(display, win, gc1, i); X DrawSlider(display, win, gc1, gc2, i, 1); X } X DrawColorBox(display, win, gc, gc1); X DrawGadgets(display, win, gc1); X break; X X case ConfigureNotify: X width= event->xconfigure.width; X height= event->xconfigure.height; X break; X X case ButtonPress: X switch(event->xbutton.button) { X case 1: X for (i= 0; i < 6; i++) { X dragx= event->xbutton.x; X dragy= event->xbutton.y; X if (InSlider(dragx, dragy, i)) { X dragging= i; X SlideCursor(display, win); X DragSlider(display, win, gc1, gc2, dragging, X dragx, dragy, dragx, dragy); X } X } X if (dragging >= 0) X break; X gadget= CheckGadget(display, win, gc1, gc2, X event->xbutton.x, event->xbutton.y); X switch(gadget) { X case 0: /* apply */ X case 1: /* redraw */ X if (!applied) { X WaitCursor(display, win); X AllocColors(display, screen); X NormalCursor(display, win); X DrawColorBox(display, win, gc, gc1); X } X if (gadget == 1) X return(DO_REDRAW); X break; X case 2: X return(DO_PREVIOUS); X case 3: X return(DO_RESTART); X case 4: X return(DO_QUIT); X } /* switch */ X if (gadget >= 0) X break; X if (c1= InCmap(event->xbutton.x, event->xbutton.y)) { X cmaphandle= 1; X VertCursor(display, win); X } X break; X case 2: X dragging= -1; X WaitCursor(display, win); X AllocColors(display, screen); X NormalCursor(display, win); X DrawColorBox(display, win, gc, gc1); X break; X default: X dragging= -1; X ToggleCWin(display, win, CMAP_CLOSE); X break; X } X break; X case ButtonRelease: X if (dragging >= 0) { X dragging= -1; X NormalCursor(display, win); X } X if (cmaphandle) { X if (c2= InCmap(event->xbutton.x, event->xbutton.y)) { X coff = (coff + c2 - c1) % ncolors; X } X cmaphandle= 0; X NormalCursor(display, win); X } X break; X case MotionNotify: X if (dragging >= 0) { X DragSlider(display, win, gc1, gc2, dragging, dragx, dragy, X event->xmotion.x, event->xmotion.y); X dragx= event->xmotion.x; X dragy= event->xmotion.y; X } X else CheckGadget(display, win, gc1, gc2, X event->xmotion.x, event->xmotion.y); X break; X case KeyPress: X dragging= -1; X NormalCursor(display, win); X ToggleCWin(display, win, CMAP_CLOSE); X break; X case MapNotify: X case ReparentNotify: X case UnmapNotify: X dragging= -1; X break; X default: X printf("Unknown Event %d\n", event->type); X break; X } /* switch */ Xreturn(-1); X} /* HandleCEvent */ X X Xvoid SetNColors(nc) Xint nc; X{ Xint i; Xncolors= nc; X/* Initialize some nice-looking colors... */ Xfor (i= 0; i < SLIDERSTEPS; i++) { X float c= i/((double)SLIDERSTEPS) * 3; X float r, g, b; X if (c < 1) {r= 1-c; g= c; b= 0;} X else if (c < 2) {r= 0; g= 2-c; b= c-1;} X else {r= c-2; g= 0; b= 3-c;} X red[i]= r*(MAXSLIDE-1); X green[i]= g*(MAXSLIDE-1); X blue[i]= b*(MAXSLIDE-1); X } Xred0= green0= blue0= 0; X} X X Xvoid AllocColors(display, screen) XDisplay *display; Xint screen; X{ Xint i, first= -1; Xint d; XXColor color; X Xif (allocated) X FreeColors(display, screen); X Xallocated= 1; Xapplied= 1; /* colormap is being made valid again */ Xalloc= (char *)malloc(ncolors); Xpixel= (unsigned long *)malloc(ncolors * sizeof(unsigned long)); Xfor (i= 0; i < ncolors; i++) X alloc[i]= 0; X Xif (ncolors == 3) { X pixel[0]= BlackPixel(display, screen); X pixel[1]= WhitePixel(display, screen); X pixel[2]= BlackPixel(display, screen); X return; X } Xif (ncolors <= 2) { X pixel[0]= BlackPixel(display, screen); X pixel[1]= WhitePixel(display, screen); X return; X } X X/* Because first entry of colormap is so special, allocate it first. */ Xcolor.red= red0; Xcolor.green= green0; Xcolor.blue= blue0; Xcolor.flags= DoRed | DoGreen | DoBlue; Xif (XAllocColor(display, DefaultColormap(display, screen), &color)) { X pixel[0]= color.pixel; X alloc[0]= 1; X } Xelse X pixel[0]= BlackPixel(display, screen); X X/* Try to allocate colors. The order in which this is done should X * be as "random" as possible, so that failing requests would X * not be grouped at the end. X */ Xd= 1; Xwhile (d < ncolors) d <<= 1; Xwhile (d > 1) { X int c= d >> 1; X do { X double r, g, b; X int s1= c*SLIDERSTEPS/ncolors; X int s2= s1+1; X int j= (c*SLIDERSTEPS)%ncolors; X X if (s2 >= SLIDERSTEPS) X s2= 0; X X r= (red[s1]*(ncolors-j)/ncolors + red[s2]*j/ncolors)/(double)MAXSLIDE; X g= (green[s1]*(ncolors-j)/ncolors + green[s2]*j/ncolors)/(double)MAXSLIDE; X b= (blue[s1]*(ncolors-j)/ncolors + blue[s2]*j/ncolors)/(double)MAXSLIDE; X X /* Do reverse gamma correction... */ X color.red= pow(r, (1/GAMMA))*65535; X color.green= pow(g, (1/GAMMA))*65535; X color.blue= pow(b, (1/GAMMA))*65535; X X if (XAllocColor(display, DefaultColormap(display, screen), &color)) { X pixel[c]= color.pixel; X alloc[c]= 1; X if (first < 0) first= c; else if (first > c) first= c; X } /* if */ X c += d; X } while (c < ncolors); X d >>= 1; X } /* while */ X X/* If no colors could be allocated, use black&white. */ Xif (first < 0) { X for (i= 1; i < ncolors; i++) X pixel[i]= WhitePixel(display, screen); X return; X } X/* Fill in gaps... */ Xfor (i= 1; i < ncolors; i++) { X if (!alloc[i]) X pixel[i]= pixel[first]; X else X first= i; X } X} /* AllocColors */ X X Xvoid FreeColors(display, screen) XDisplay *display; Xint screen; X{ Xint i; X Xif (allocated) { X for (i= 0; i < ncolors; i++) X if (alloc[i]) X XFreeColors(display, DefaultColormap(display, screen), X &pixel[i], 1L, 0); X free(alloc); X free(pixel); X } X} X X Xunsigned long ToPixel(level) Xint level; X{ Xif (level != 0) X level= ((level+coff) % (ncolors - 1)) + 1; Xreturn(pixel[level]); X} X END_OF_cmap.c if test 15828 -ne `wc -c <cmap.c`; then echo shar: \"cmap.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f x.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"x.c\" else echo shar: Extracting \"x.c\" \(22670 characters\) sed "s/^X//" >x.c <<'END_OF_x.c' X X#include <X11/Xlib.h> X#include <X11/Xutil.h> X#include <X11/Xos.h> X#include <sys/types.h> X#include <sys/time.h> X#include <sys/resource.h> X#include <sys/param.h> X#include <termio.h> X#include <stdio.h> X#include <strings.h> X#include "mb.h" X#include "cmap.h" X#include "misc.h" X#include "pix.h" X X#ifdef USE_NETWORK X#include "ipc.h" X#endif X X#define GEOM_PLACE 1 X#define GEOM_RIGHT 2 X#define GEOM_DOWN 4 X#define GEOM_SIZE 8 X#define GEOM_ERROR 16 X X/* Just to make sure that the number of colors will be reasonable X * on a 24-bit true-color display ;) X */ X#define MAXCOLORS 1024 X X/* dummy procedure for XCheckIfEvent */ XBool dummy() {return(1);} X X#ifdef USE_NETWORK X/* Terminology: server is the one who shows pictures, and X * clients do the calculations. X */ X X#define MODE_STANDALONE 0 X#define MODE_SERVER 1 X#define MODE_CLIENT 2 X X#define RSHCMD "rsh %s \"xnetmb -nice 10 -port %d -server %s&\" </dev/null >/dev/null" X#endif X Xstatic Display *display; Xstatic int screen; Xstatic Window win= 0, cwin= 0; Xstatic GC gc, gc_inv, gc_set, gc_clear; X#ifdef USE_NETWORK Xstatic int mode= MODE_STANDALONE; Xstatic int portnum= DEF_PORT; Xstatic char *server; X#endif X Xvoid DrawSplitImage(); Xvoid CalcImage(); Xvoid DrawImage(); Xvoid DrawRect(); X#define EraseRect() DrawRect(0, 0,0, 0,0, 0,0); X Xstatic double xc= 0.0, yc= 0.0, cx= 0.0, cy= 0.0, d= 0.0, scale= 8.0; Xstatic int iter= 500, flags= 0; Xunsigned int width= 300, height= 300; X X Xint ParseComplex(string, x, y) Xchar *string; Xdouble *x, *y; X{ Xchar ii, sign; X/* x+-yi */ Xif (sscanf(string, "%lf%lf%c", x, y, &ii) == 3 && X ii == 'i') X ; X/* x+-iy */ Xelse if (sscanf(string, "%lf%ci%lf", x, &sign, y) == 3 && X (sign == '+' || sign == '-')) { X if (sign == '-') *y = -*y; X } X/* x+-i */ Xelse if (sscanf(string, "%lf%c%c", x, &sign, &ii) == 3 && X (sign == '+' || sign == '-') && ii == 'i') { X if (sign == '-') X *y= -1.0; X else X *y= 1.0; X } X/* +-iy */ Xelse if (sscanf(string, "%c%c%lf", &ii, &sign, y) == 3 && X (sign == '+' || sign == '-') && ii == 'i') { X *x= 0.0; X if (sign == '-') X *y= -*y; X } X/* yi */ Xelse if (sscanf(string, "%lf%c", y, &ii) == 2 && ii == 'i') X *x= 0.0; X/* iy */ Xelse if (sscanf(string, "%c%lf", &ii, y) == 2 && ii == 'i') X *x= 0.0; X/* +-i */ Xelse if (sscanf(string, "%c%c", &sign, &ii) == 2 && X (sign == '-' || sign == '+') && ii == 'i') { X *x= 0.0; X if (sign == '-') X *y= -1.0; X else X *y= 1.0; X } X/* i */ Xelse if (sscanf(string, "%c", &ii) == 1 && ii == 'i') { X *x= 0.0; X *y= 1.0; X } X/* x */ Xelse if (sscanf(string, "%lf", x) == 1) X *y= 0.0; Xelse X return(0); Xreturn(1); X} X X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ XXSizeHints size_hints; XXEvent event; XXGCValues gcval; XXWindowAttributes xwattr; XXSetWindowAttributes xswattr; Xunsigned int border_width= 1; Xunsigned int display_width, display_height; Xint win_x= 0, win_y= 0; Xint cwin_x= 0, cwin_y= 0; Xint dragx, dragy; Xint buttonpressed; Xint ncolors= 0; Xint zebra= 0; Xdouble xstore1= xc, ystore1= yc, scalestore1= scale; Xdouble xstore2= xc, ystore2= yc, scalestore2= scale; Xint flagstore= flags; Xint wingeom= 0; Xchar **mach; Xint nmach= 0; Xchar *window_name= "XMandelbrot"; Xchar *icon_name= "XMandel"; Xchar *cwindow_name= "MBcolors"; Xchar *cicon_name= "MBcolors"; Xchar *display_name= NULL; X Xint i; X X/* Command line options... boring. X * (OH NO, it is my pseudo-intelligent commenting style attack again. X * Sigh... Please be patient and hope for the best.) X */ Xfor (i= 1; i < argc; i++) { X if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "-display")) { X if (++i >= argc) { X fprintf(stderr, "%s: error in cmd line option 'display'\n", X argv[0]); X exit(1); X } X display_name= argv[i]; X } X X else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "-geometry")) { X char sign1, sign2; X if (++i >= argc) { X fprintf(stderr, "%s: error in cmd line option 'geometry'\n", X argv[0]); X exit(1); X } X switch (sscanf(argv[i], "%ux%u%c%d%c%d", X &width, &height, &sign1, &win_x, &sign2, &win_y)) { X case 6: X wingeom= GEOM_PLACE | GEOM_SIZE; X if (sign1 == '-') wingeom |= GEOM_RIGHT; X else if (sign1 != '+') wingeom |= GEOM_ERROR; X if (sign2 == '-') wingeom |= GEOM_DOWN; X else if (sign2 != '+') wingeom |= GEOM_ERROR; X if (wingeom & GEOM_ERROR) { X fprintf(stderr, X "%s: error in cmd line option 'geometry'\n", X argv[0]); X exit(1); X } X break; X case 2: X wingeom= GEOM_SIZE; X /* There is nothing else here to be done. */ X break; X default: X fprintf(stderr, "%s: error in cmd line option 'geometry'\n", X argv[0]); X exit(1); X } X } X X else if (!strcmp(argv[i], "-z") || !strcmp(argv[i], "-zebra")) { X /* Need to know which of the two -z options this is... */ X if (!strcmp(argv[i], "-z") && i+1 < argc && X ParseComplex(argv[i+1], &xc, &yc)) { X i++; X xstore1= xc; ystore1= yc; X } X else X zebra= 1; X } X X else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "-nice")) { X if (++i >= argc) { X fprintf(stderr, "%s: error in cmd line option 'nice'\n", X argv[0]); X exit(1); X } X setpriority(PRIO_PROCESS,getpid(), atoi(argv[i])); X } X X else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "-iters")) { X if (++i >= argc) { X fprintf(stderr, "%s: error in cmd line option 'iters'\n", X argv[0]); X exit(1); X } X iter= atoi(argv[i]); X } X X else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "-colors")) { X if (++i >= argc) { X fprintf(stderr, "%s: error in cmd line option 'colors\n", X argv[0]); X exit(1); X } X ncolors= atoi(argv[i]); X if (ncolors < 2) X ncolors= 2; X } X X else if (!strcmp(argv[i], "-j")) { X if (++i >= argc || !ParseComplex(argv[i], &cx, &cy)) { X fprintf(stderr, "%s: error in cmd line option 'j'\n", X argv[0]); X exit(1); X } X flagstore = (flags |= JULIA); X } X X else if (!strcmp(argv[i], "-x")) { X if (++i >= argc || sscanf(argv[i], "%lf", &scale) < 1) { X fprintf(stderr, "%s: error in cmd line option 'x'\n", X argv[0]); X exit(1); X } X scalestore1 = (scale *= 2); X } X X#ifdef USE_NETWORK X else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "-server")) { X int k; X if (++i >= argc || mode != MODE_STANDALONE) { X fprintf(stderr, "%s: error in cmd line option 'server'\n", X argv[0]); X exit(1); X } X /* We are the client, and server is specified here. */ X mode= MODE_CLIENT; X server= argv[i]; X /* daemon */ X for (k= getdtablesize() - 1; k >= 0; --k) X close(k); X k= open("/dev/null", O_RDONLY); X if (k >= 0) { X ioctl(k, TIOCNOTTY, 0); X close(k); X } X } X#endif X X#ifdef USE_NETWORK X else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "-port")) { X if (++i >= argc) { X fprintf(stderr, "%s: error in cmd line option 'iters'\n", X argv[0]); X exit(1); X } X portnum= atoi(argv[i]); X } X#endif X X#ifdef USE_NETWORK X else if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "-machines")) { X if (mode != MODE_STANDALONE) { X fprintf(stderr, "%s: error in command line option 'machines'\n", X argv[0]); X exit(1); X } X X mode= MODE_SERVER; X mach= &argv[i+1]; X while (++i < argc && argv[i][0] != '-') X nmach++; X --i; X } X#endif X X else if (!strcmp(argv[i], "-guru")) { X if (++i >= argc) { X fprintf(stderr, "%s: You are not a guru.\n", argv[0]); X exit(1); X } X if (!strcmp(argv[i], "fast")) { X flagstore= (flags |= FAST); X } X else { X fprintf(stderr, "%s: You are not a guru.\n", argv[0]); X exit(1); X } X } X X else { X fprintf(stderr, "%s: unrecognized cmd line option '%s'\n\n", X argv[0], argv[i]); X fprintf(stderr, X "Usage: %s -display <name> -geometry <geometry>\n", X argv[0]); X fprintf(stderr, X "-zebra -nice <value> -iters <i> -colors <number>\n"); X fprintf(stderr, X "-z <x+yi> -j <cx+cyi> -x <scale>\n"); X#ifdef USE_NETWORK X fprintf(stderr, X "-server <name> -port <port> -machines [list of rsh machines]\n"); X#endif X fprintf(stderr, "\nAll options may be abbreviated.\n\n"); X exit(1); X } X } /* for */ X X#ifdef USE_NETWORK Xif (mode == MODE_CLIENT) { X Client_run(server, portnum); X exit(0); X } Xelse if (mode == MODE_SERVER) { X char cmd[sizeof(RSHCMD) + 2*MAXHOSTNAMELEN], host[MAXHOSTNAMELEN]; X if (gethostname(host, MAXHOSTNAMELEN) < 0) { X perror("unable to find my name"); X exit(1); X } X CreateServer(portnum); X printf("Starting remote machines:"); X for (i= 0; i < nmach; i++) { X sprintf(cmd, RSHCMD, mach[i], portnum, host); X printf (" %s", mach[i]); fflush(stdout); X if ((system(cmd) >> 8) > 0) { X fprintf(stderr, "%s: unable to rsh %s\n", argv[0], X mach[i]); X } X } X --i; X putchar('\n'); X } X#endif X X/* Open the display, if possible. */ Xif(!(display= XOpenDisplay(display_name))) { X fprintf(stderr, "cannot reach X server %s\n", X XDisplayName(display_name)); X exit(1); X } X Xscreen= DefaultScreen(display); X Xdisplay_width= DisplayWidth(display, screen); Xdisplay_height= DisplayHeight(display, screen); X Xif (!(wingeom & GEOM_PLACE)) { X win_x= display_width / 2 - (int)width; X win_y= 2 * display_height / 3; X } Xelse { X if (wingeom & GEOM_RIGHT) win_x= (int)display_width - win_x - (int)width; X if (wingeom & GEOM_DOWN) win_y= (int)display_height - win_y - (int)height; X } X Xcwin_x= display_width / 2 - 200; Xcwin_y= display_height - 250; X X Xif (ncolors == 0) X ncolors= 1 << (DefaultDepth(display, screen)); Xif (zebra) ncolors= 3; Xif (ncolors > MAXCOLORS) X ncolors= MAXCOLORS; XSetNColors(ncolors); X Xxswattr.background_pixel= WhitePixel(display, screen); Xxswattr.border_pixel= BlackPixel(display, screen); Xwin= XCreateWindow(display, RootWindow(display, screen), win_x, win_y, X width, height, border_width, CopyFromParent, X InputOutput, CopyFromParent, X CWBackPixel | CWBorderPixel, &xswattr); Xcwin= XCreateWindow(display, RootWindow(display, screen), cwin_x, cwin_y, X 400, 200, border_width, CopyFromParent, X InputOutput, CopyFromParent, X CWBackPixel | CWBorderPixel, &xswattr); X Xif (!win || !cwin) { X#ifdef USE_NETWORK X if (mode == MODE_SERVER) X CloseDown(); X#endif X perror("could not open window"); X myExit(); X exit(0); X } X Xsize_hints.flags= PMinSize; Xif (wingeom & GEOM_PLACE) size_hints.flags |= PPosition; Xif (wingeom & GEOM_SIZE) size_hints.flags |= PSize; X Xsize_hints.width= width; Xsize_hints.height= height; Xsize_hints.min_width= 50; Xsize_hints.min_height= 50; Xsize_hints.x= win_x; Xsize_hints.y= win_y; XXSetStandardProperties(display, win, window_name, icon_name, X None, NULL, 0, &size_hints); X Xsize_hints.flags= PPosition | PSize | PMinSize; Xsize_hints.width= 400; Xsize_hints.height= 200; Xsize_hints.min_width= 200; Xsize_hints.min_height= 100; Xsize_hints.x= cwin_x; Xsize_hints.y= cwin_y; XXSetStandardProperties(display, cwin, cwindow_name, cicon_name, X None, NULL, 0, &size_hints); X XXSelectInput(display, win, ExposureMask | KeyPressMask | X ButtonPressMask | ButtonReleaseMask | X PointerMotionMask | StructureNotifyMask); XXSelectInput(display, cwin, ExposureMask | KeyPressMask | X ButtonPressMask | ButtonReleaseMask | X PointerMotionMask | StructureNotifyMask); X Xgcval.foreground= BlackPixel(display, screen); Xgcval.background= WhitePixel(display, screen); Xgc= XCreateGC(display, win, GCForeground | GCBackground, &gcval); Xgc_set= XCreateGC(display, win, GCForeground | GCBackground, &gcval); X Xgcval.foreground= WhitePixel(display, screen); Xgc_clear= XCreateGC(display, win, GCForeground | GCBackground, &gcval); X Xgcval.function= GXinvert; Xgc_inv= XCreateGC(display, win, GCFunction, &gcval); X Xif (!AllocFonts(display, gc_set)) { X fprintf(stderr, "Could not open fonts... no text will be drawn"); X } XAllocColors(display, screen); X XAllocCursors(display); XNormalCursor(display, win); XNormalCursor(display, cwin); X XXMapWindow(display, win); Xbuttonpressed= 0; X Xfor (;;) { X#ifdef USE_NETWORK X if (mode == MODE_SERVER) X CheckClients(NOBLOCK); X X if (mode == MODE_SERVER && PendingRequests()) { X while (!XCheckIfEvent(display, &event, dummy, (char *)NULL)) X CheckClients(1); X if (!PendingRequests()) { X NormalCursor(display, win); X NormalCursor(display, cwin); X } X } X else X XNextEvent(display, &event); X#else X XNextEvent(display, &event); X#endif X X if (event.xany.window == cwin) { X switch (HandleCEvent(display, screen, cwin, gc, X gc_set, gc_clear, &event)) { X case DO_REDRAW: X#ifdef USE_NETWORK X if (mode == MODE_SERVER) Invalidate(); X#endif X XClearArea(display, win, 0, 0, width, height, 1); X break; X case DO_PREVIOUS: X if (xc != xstore1 || yc != ystore1 || X scale != scalestore1 || flags != flagstore) { X xc= xstore1; yc= ystore1; X scale= scalestore1; X flags= flagstore; X d= scale / (width + height); X#ifdef USE_NETWORK X if (mode == MODE_SERVER) Invalidate(); X#endif X XClearArea(display, win, 0, 0, width, height, 1); X } X break; X case DO_RESTART: X xstore1= xc; ystore1= yc; X scalestore1= scale; X flagstore= flags; X X xc= 0.0; yc= 0.0; X scale= 8.0; flags &= ~JULIA; X d= scale / (width + height); X#ifdef USE_NETWORK X if (mode == MODE_SERVER) Invalidate(); X#endif X XClearArea(display, win, 0, 0, width, height, 1); X break; X X case DO_QUIT: X#ifdef USE_NETWORK X if (mode == MODE_SERVER) X CloseDown(); X#endif X myExit(); X exit(0); X } /* switch */ X } X else switch(event.type) { X case Expose: X WaitCursor(display, win); X WaitCursor(display, cwin); X CalcImage (event.xexpose.x, event.xexpose.y, X event.xexpose.width, event.xexpose.height); X break; X X case ConfigureNotify: X#ifdef USE_NETWORK X if (mode == MODE_SERVER) { X if (width != event.xconfigure.width || X height != event.xconfigure.height) X Invalidate(); X } X#endif X width= event.xconfigure.width; X height= event.xconfigure.height; X d= scale / (width + height); X break; X X case ButtonPress: X switch (event.xbutton.button) { X case 1: X ZoomCursor(display, win); X dragx= event.xbutton.x; X dragy= event.xbutton.y; X buttonpressed= 1; X break; X X case 2: /* julia stuff here */ X if (buttonpressed) { X EraseRect(); X buttonpressed= 0; X NormalCursor(display, win); X } X else if (!(flags & JULIA)) { X /* toggle to julia set; save current context */ X xstore1= xstore2= xc; ystore1= ystore2= yc; X scalestore1= scalestore2= scale; X flagstore= flags; X X flags |= JULIA; X cx= xc - d * (width/2.0 - event.xbutton.x); X cy= yc + d * (height/2.0 - event.xbutton.y); X xc= 0.0; yc= 0.0; X scale= 8.0; X d= scale / (width + height); X#ifdef USE_NETWORK X if (mode == MODE_SERVER) Invalidate(); X#endif X XClearArea(display, win, 0, 0, width, height, 1); X } X else { X /* toggle to mb set; restore context */ X xc= xstore2; yc= ystore2; X scale= scalestore2; X flags &= ~JULIA; X d= scale / (width + height); X#ifdef USE_NETWORK X if (mode == MODE_SERVER) Invalidate(); X#endif X XClearArea(display, win, 0, 0, width, height, 1); X } X break; X X case 3: /* open colormap window */ X if (buttonpressed) { X EraseRect(); X buttonpressed= 0; X NormalCursor(display, win); X } X else X ToggleCWin(display, cwin, CMAP_TOGGLE); X break; X default: X break; X } X break; X X case ButtonRelease: X switch (event.xbutton.button) { X case 1: X if (buttonpressed) { X int xdiff, ydiff; X xstore1= xc; ystore1= yc; X scalestore1= scale; X flagstore= flags; X X xdiff= dragx - event.xbutton.x; X ydiff= dragy - event.xbutton.y; X if (xdiff < 0) xdiff= -xdiff; X if (ydiff < 0) ydiff= -ydiff; X if (xdiff/(double)width < ydiff/(double)height) X scale *= (ydiff/(double)height); X else X scale *= (xdiff/(double)width); X buttonpressed= 0; X EraseRect(); X X xc= xc - d * ((int)width - (dragx + event.xbutton.x)) / 2.0; X yc= yc + d * ((int)height - (dragy + event.xbutton.y)) / 2.0; X d= scale / (width + height); X#ifdef USE_NETWORK X if (mode == MODE_SERVER) Invalidate(); X#endif X XClearArea(display, win, 0, 0, width, height, 1); X } X default: X break; X } X break; X X case MotionNotify: X if (buttonpressed) X DrawRect(1, dragx, dragy, event.xmotion.x, event.xmotion.y, X (int)width, (int)height); X break; X X case KeyPress: X#ifdef USE_NETWORK X if (mode == MODE_SERVER) X CloseDown(); X#endif X myExit(); X exit(0); X X case MapNotify: X /* Determine size of set (depends on width & height) */ X XGetWindowAttributes(display, win, &xwattr); X width= xwattr.width; height= xwattr.height; X d= scale / (width + height); X break; X X case ReparentNotify: X case UnmapNotify: X /* I can't think of anything reasonable to do here... */ X break; X X default: X printf("Unknown Event %d\n", event.type); X break; X } /* switch */ X } /* for(ever) */ X} /* main */ X X XmyExit() X{ XFreeFonts(display); XXFreeGC(display, gc); XXFreeGC(display, gc_inv); XXFreeGC(display, gc_set); XXFreeGC(display, gc_clear); X XFreeColors(display, screen); Xif (win) XDestroyWindow(display, win); Xif (cwin) XDestroyWindow(display, cwin); XXCloseDisplay(display); X} X X X/* Split the image into comfortably small chunks, which X * are then fed to DrawSplitImage() X */ Xvoid CalcImage(xoff, yoff, w, h) Xint xoff, yoff, w, h; X{ Xint xd=1, yd=1, xn, yn; X/* do not accidentally crawl arond some area... X */ Xif (xc - d*(width/2.0 - xoff) < -.5 && X yc + d*(height/2.0 - yoff) > .5 && X xc - d*(width/2.0 - xoff-w) > .5 && X yc + d*(height/2.0 - yoff-h) < -.5) { X int x0= width/2 - xc/d; X int y0= height/2 + yc/d; X int xdist= x0 - xoff; X int ydist= y0 - yoff; X if (w > 2*xdist) xdist= w-xdist; X if (h > 2*ydist) ydist= h-ydist; X if (xdist > ydist) { X CalcImage(xoff, yoff, x0-xoff, h); X CalcImage(x0, yoff, w-(x0-xoff), h); X } X else { X CalcImage(xoff, yoff, w, y0-yoff); X CalcImage(xoff, y0, w, h-(y0-yoff)); X } X return; X } /* if */ Xwhile (w*h/(xd*yd) > 65536) { X if (w*yd < h*xd) /* <==> w/xd<h/yd */ X yd++; X else X xd++; X } Xfor (yn= 0; yn < yd; yn++) X for (xn= 0; xn < xd; xn++) { X DrawSplitImage(xoff + xn*w/xd, yoff+yn*h/yd, X (xn+1)*w/xd - xn*w/xd, X (yn+1)*h/yd - yn*h/yd); X } X#ifdef USE_NETWORK Xif (mode == MODE_STANDALONE || !PendingRequests() ) { X NormalCursor(display, win); X NormalCursor(display, cwin); X } X#else XNormalCursor(display, win); XNormalCursor(display, cwin); X#endif X} X X X/* Calculate chunks either in local or in remote machines X */ Xvoid DrawSplitImage(xoff, yoff, w, h) Xint xoff, yoff, w, h; X{ Xdouble x,y; Xu_short *dest; Xint tmpflags; X Xx= xc - d*(width/2.0 - xoff); Xy= yc + d*(height/2.0 - yoff); X XXDrawRectangle(display, win, gc_set, xoff, yoff, w-1, h-1); XXFlush(display); X X#ifdef USE_NETWORK Xif (mode == MODE_SERVER) { X /* Try to delegate */ X if (CalcRequest(x, y, d, d, cx, cy, w, h, iter, flags, xoff, yoff)) X return; X } X#endif X/* Always use FASTmode when using local machine */ Xtmpflags= flags; Xflags |= FAST; Xiterate(x, y, d, d, cx, cy, w, h, iter, flags, &dest); XDrawImage(xoff, yoff, w, h, dest); Xflags= tmpflags; X} X X Xvoid DrawImage(xoff, yoff, w, h, dest) Xint xoff, yoff, w, h; Xu_short *dest; X{ Xint px, py; Xu_short *ptr, color; Xlong len, i; XXGCValues gcval; XXPoint *xpts; Xlong npts= 512; Xint fastmode= flags & FAST; X Xptr= dest; Xxpts= (XPoint *)malloc(npts * sizeof(XPoint)); X Xif (!fastmode) X initialize(w, h); Xfor (;;) { X /* In fast mode, first_point() and next_point() are omitted. X * Server will be faster but there will be over 100% more traffic X * in the net. X */ X if (fastmode) { X px= *ptr++; X if (px != LASTPIX) X py= *ptr++; X } X if (fastmode && px == LASTPIX) X break; X if (!fastmode && first_point(&px, &py) ==0) X break; X color= *ptr++; X gcval.foreground= ToPixel((int)(color & 0x7fff)); X XChangeGC(display, gc, GCForeground, &gcval); X X if (color & 0x8000) { X XDrawPoint(display, win, gc, px+xoff, py+yoff); X } X else { X int dir, bitindex= 7; X X len= *ptr++; X if (len > npts) { X free(xpts); X xpts= (XPoint *)malloc((npts= len) * sizeof(XPoint)); X } X for (i= 0; i < len; i++) { X dir= ((*ptr) >> (bitindex*2)) & 3; X if (!fastmode) X next_point(dir); X X XDrawPoint(display, win, gc, px+xoff, py+yoff); X xpts[i].x= px+xoff; X xpts[i].y= py+yoff; X switch (dir) { X case UP: py--; break; X case DOWN: py++; break; X case LEFT: px--; break; X case RIGHT: px++; break; X } X if (--bitindex < 0) { X ptr++; X bitindex= 7; X } X } X if (bitindex != 7) X ptr++; X XFillPolygon(display, win, gc, xpts, len, Complex, CoordModeOrigin); X } /* else */ X } /* for */ Xif (!fastmode) X deinit(); Xfree(dest); Xfree(xpts); X} /* DrawImage */ X X Xvoid DrawRect(mode, x1, y1, x2, y2, w, h) Xint mode, x1, y1, x2, y2, w, h; X{ Xstatic int drawn= 0; Xstatic int dx, dy, dw, dh; X Xif (drawn) X XDrawRectangle(display, win, gc_inv, dx, dy, dw, dh); Xdrawn= 0; X Xif (mode == 0) X return; X Xdw= x2 - x1; dh= y2 - y1; Xif (dw == 0 && dh == 0) X return; X Xdx= x1; dy= y1; Xif (dw < 0) { X dw= -dw; X dx= x2; X } Xif (dh < 0) { X dh= -dh; X dy= y2; X } X Xif (dw/(double)w > dh/(double)h) { X dh= dw * (h/(double)w); X dy= (y1 + y2 - dh)/2; X } Xelse { X dw= dh * (w/(double)h); X dx= (x1 + x2 - dw)/2; X } XXDrawRectangle(display, win, gc_inv, dx, dy, dw, dh); Xdrawn= 1; X} /* drawrect */ X END_OF_x.c if test 22670 -ne `wc -c <x.c`; then echo shar: \"x.c\" unpacked with wrong size! fi # end of overwriting check fi echo shar: End of archive 2 \(of 2\). cp /dev/null ark2isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0