home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / historic / v92.tgz / v92.tar / v92 / src / runtime / rwindow.r < prev    next >
Text File  |  1996-03-22  |  90KB  |  3,316 lines

  1. /*
  2.  * File: rwindow.r
  3.  *  non window-system-specific window support routines
  4.  */
  5.  
  6. #ifdef Graphics
  7.  
  8. hidden    int    colorphrase    Params((char *buf, long *r, long *g, long *b));
  9. hidden    double    rgbval        Params((double n1, double n2, double hue));
  10.  
  11. hidden    int    setpos          Params((wbp w, char *s));
  12. hidden    int    sicmp        Params((siptr sip1, siptr sip2));
  13.  
  14. int canvas_serial, context_serial;
  15.  
  16. #ifdef PresentationManager
  17. extern LONG ScreenBitsPerPel;
  18. #endif                    /* PresentationManager */
  19.  
  20. #ifndef MultiThread
  21. struct descrip amperX = {D_Integer};
  22. struct descrip amperY = {D_Integer};
  23. struct descrip amperCol = {D_Integer};
  24. struct descrip amperRow = {D_Integer};
  25. struct descrip amperInterval = {D_Integer};
  26. struct descrip lastEventWin = {D_Null};
  27. int lastEvFWidth = 0, lastEvLeading = 0, lastEvAscent = 0;
  28. uword xmod_control, xmod_shift, xmod_meta;
  29. #endif                    /* MultiThread */
  30.  
  31. /*
  32.  * A non-existent library function (exported to fsys.c)
  33.  */
  34. char *strnchr(s,ci,len)
  35. char *s;
  36. int ci;
  37. long len;
  38.    {
  39.    register long i;
  40.    char c = (char)ci;
  41.    for(i=0;i<len;i++,s++) if (*s == c) return s;
  42.    return NULL;
  43.    }
  44.  
  45. /*
  46.  * subscript the already-processed-events "queue" to index i.
  47.  * used in "cooked mode" I/O to determine, e.g. how far to backspace.
  48.  */
  49. char *evquesub(w,i)
  50. wbp w;
  51. int i;
  52.    {
  53.    wsp ws = w->window;
  54.    int j = ws->eQback+i;
  55.  
  56.    if (i < 0) {
  57.       if (j < 0) j+= EQUEUELEN;
  58.       else if (j > EQUEUELEN) j -= EQUEUELEN;
  59.       return &(ws->eventQueue[j]);
  60.       }
  61.    else {
  62.       /* "this isn't getting called in the forwards direction!\n" */
  63.       return NULL;
  64.       }
  65.    }
  66.  
  67.  
  68. /*
  69.  * get event from window, assigning to &x, &y, and &interval
  70.  *
  71.  * returns 0 for success, -1 if window died, -2 for malformed queue
  72.  */
  73. int wgetevent(w,res)
  74. wbp w;
  75. dptr res;
  76.    {
  77.    struct descrip xdesc, ydesc;
  78.    uword i;
  79.  
  80.    if (wstates != NULL && wstates->next != NULL        /* if multiple windows*/
  81.    && (BlkLoc(w->window->listp)->list.size == 0)) {    /* & queue is empty */
  82.       while (BlkLoc(w->window->listp)->list.size == 0) {
  83.      if (pollevent() < 0)                /* poll all windows */
  84.         break;                    /* break on error */
  85. #if UNIX || VMS || OS2_32
  86.          idelay(XICONSLEEP);
  87. #endif                    /* UNIX || VMS || OS2_32 */
  88.      }
  89.       }
  90.  
  91.    if (wgetq(w,res) == -1)
  92.       return -1;                    /* window died */
  93.    if (BlkLoc(w->window->listp)->list.size < 2)
  94.       return -2;                    /* malformed queue */
  95.  
  96.    wgetq(w,&xdesc);
  97.    wgetq(w,&ydesc);
  98.  
  99.    if (xdesc.dword != D_Integer || ydesc.dword != D_Integer)
  100.       return -2;            /* bad values on queue */
  101.  
  102.    IntVal(amperX) = IntVal(xdesc) & 0xFFFF;        /* &x */
  103.    if (IntVal(amperX) >= 0x8000)
  104.       IntVal(amperX) -= 0x10000;
  105.    IntVal(amperY) = IntVal(ydesc) & 0xFFFF;        /* &y */
  106.    if (IntVal(amperY) >= 0x8000)
  107.       IntVal(amperY) -= 0x10000;
  108.    IntVal(amperX) -= w->context->dx;
  109.    IntVal(amperY) -= w->context->dy;
  110.    MakeInt(1 + XTOCOL(w,IntVal(amperX)), &(amperCol));    /* &col */
  111.    MakeInt(YTOROW(w,IntVal(amperY)) , &(amperRow));    /* &row */
  112.  
  113.    xmod_control = IntVal(xdesc) & EQ_MOD_CONTROL;    /* &control */
  114.    xmod_meta = IntVal(xdesc) & EQ_MOD_META;        /* &meta */
  115.    xmod_shift = IntVal(xdesc) & EQ_MOD_SHIFT;        /* &shift */
  116.  
  117.    i = (((uword) IntVal(ydesc)) >> 16) & 0xFFF;        /* mantissa */
  118.    i <<= 4 * ((((uword) IntVal(ydesc)) >> 28) & 0x7);    /* scale it */
  119.    IntVal(amperInterval) = i;                /* &interval */
  120.    return 0;
  121.    }
  122.  
  123. /*
  124.  * get event from window (drop mouse events), no echo
  125.  *
  126.  * returns 1 for success, -1 if window died, -2 for malformed queue
  127.  */
  128. int wgetchne(w,res)
  129. wbp w;
  130. dptr res;
  131.    {
  132.    int i;
  133.  
  134.    while (1) {
  135.       i = wgetevent(w,res);
  136.       if (i != 0)
  137.      return i;
  138.       if (is:string(*res))
  139.          return 1;
  140.       }
  141.    }
  142.  
  143. /*
  144.  * get event from window (drop mouse events), with echo
  145.  *
  146.  * returns 1 for success, -1 if window died, -2 for malformed queue
  147.  */
  148. int wgetche(w,res)
  149. wbp w;
  150. dptr res;
  151.    {
  152.    int i;
  153.    i = wgetchne(w,res);
  154.    if (i != 1)
  155.       return i;
  156.    i = *StrLoc(*res);
  157.    if ((0 <= i) && (i <= 127) && (ISECHOON(w))) {
  158. #ifndef PresentationManager
  159.       wputc(i, w);
  160.       if (i == '\r') wputc((int)'\n', w); /* CR -> CR/LF */
  161. #else                    /* PresentationManager */
  162.      wputc(((i == '\r') ? '\n' : i), w);
  163. #endif                    /* PresentationManager */
  164.       }
  165.    return 1;
  166.    }
  167.  
  168. /*
  169.  * Get a window that has an event pending (queued)
  170.  */
  171. wsp getactivewindow()
  172.    {
  173.    static LONG next = 0;
  174.    LONG i, j, nwindows = 0;
  175.    wsp ptr, ws, stdws = NULL;
  176.    extern FILE *ConsoleBinding;
  177.  
  178.    if (wstates == NULL) return NULL;
  179.    for(ws = wstates; ws; ws=ws->next) nwindows++;
  180.    if (ConsoleBinding) stdws = ((wbp)ConsoleBinding)->window;
  181.    /*
  182.     * make sure we are still in bounds
  183.     */
  184.    next %= nwindows;
  185.    /*
  186.     * position ptr on the next window to get events from
  187.     */
  188.    for (ptr = wstates, i = 0; i < next; i++, ptr = ptr->next);
  189.    /*
  190.     * Infinite loop, checking for an event somewhere, sleeping awhile
  191.     * each iteration.
  192.     */
  193.    for (;;) {
  194.       /*
  195.        * Check for any new pending events.
  196.        */
  197.       switch (pollevent()) {
  198.       case -1: ReturnErrNum(141, NULL);
  199.       case 0: return NULL;
  200.      }
  201.       /*
  202.        * go through windows, looking for one with an event pending
  203.        */
  204.       for (ws = ptr, i = 0, j = next + 1; i < nwindows;
  205.        (ws = (ws->next) ? ws->next : wstates), i++, j++)
  206.      if (ws != stdws && BlkLoc(ws->listp)->list.size > 0) {
  207.         next = j;
  208.         return ws;
  209.         }
  210. #if UNIX || VMS || OS2_32
  211.       /*
  212.        * couldn't find a pending event - wait awhile
  213.        */
  214.       idelay(XICONSLEEP);
  215. #endif                    /* UNIX || VMS || OS2_32 */
  216.       }
  217.    }
  218.  
  219. /*
  220.  * strncpy with guaranteed NUL termination
  221.  */
  222. novalue mystrncpy(dest,src,n)
  223. char *dest, *src;
  224. int n;
  225.    {
  226.    strncpy(dest,src,n);
  227.    dest[n] = '\0';
  228.    }
  229.  
  230. /*
  231.  * wlongread(s,elsize,nelem,f) -- read string from window for reads(w)
  232.  *
  233.  * returns length(>=0) for success, -1 if window died, -2 for malformed queue
  234.  */
  235. int wlongread(s, elsize, nelem, f)
  236. char *s;
  237. int elsize, nelem;
  238. FILE *f;
  239.    {
  240.    int c;
  241.    tended char *ts = s;
  242.    struct descrip foo;
  243.    long l = 0, bytes = elsize * nelem;
  244.  
  245.    while (l < bytes) {
  246.      c = wgetche((wbp)f, &foo);
  247.      if (c < 0)
  248.     return c;
  249.      c = *StrLoc(foo);
  250.      switch(c) {
  251.        case '\177':
  252.        case '\010':
  253.          if (l > 0) { ts--; l--; }
  254.          break;
  255.        default:
  256.          *ts++ = c; l++;
  257.          break;
  258.        }
  259.      }
  260.    return l;
  261.    }
  262.  
  263. /*
  264.  * wgetstrg(s,maxlen,f) -- get string from window for read(w) or !w
  265.  *
  266.  * returns length(>=0) for success, -1 if window died, -2 for malformed queue
  267.  */
  268. int wgetstrg(s, maxlen, f)
  269. char *s;
  270. long  maxlen;
  271. FILE *f;
  272.    {
  273.    int c;
  274.    tended char *ts = s;
  275.    long l = 0;
  276.    struct descrip foo;
  277.  
  278.    while (l < maxlen) {
  279.       c = wgetche((wbp)f,&foo);
  280.       if (c < 0)
  281.      return c;
  282.       c = *StrLoc(foo);
  283.       switch(c) {
  284.         case '\177':
  285.         case '\010':
  286.           if (l > 0) { ts--; l--; }
  287.           break;
  288.         case '\r':
  289.         case '\n':
  290.           return l;
  291.         default:
  292.           *ts++ = c; l++;
  293.           break;
  294.         }
  295.       }
  296.    return l;
  297.    }
  298.  
  299.  
  300. /*
  301.  * Assignment side-effects for &x,&y,&row,&col
  302.  */
  303. int xyrowcol(dx)
  304. dptr dx;
  305. {
  306.    if (VarLoc(*dx) == &erX) { /* update &col too */
  307.       wbp w;
  308.       if (!is:file(lastEventWin) ||
  309.           ((BlkLoc(lastEventWin)->file.status & Fs_Window) == 0) ||
  310.           ((BlkLoc(lastEventWin)->file.status & (Fs_Read|Fs_Write)) == 0)) {
  311.          MakeInt(1 + IntVal(amperX)/lastEvFWidth, &erCol);
  312.      }
  313.       else {
  314.          w = (wbp)BlkLoc(lastEventWin)->file.fd;
  315.          MakeInt(1 + XTOCOL(w, IntVal(amperX)), &erCol);
  316.          }
  317.       }
  318.    else if (VarLoc(*dx) == &erY) { /* update &row too */
  319.       wbp w;
  320.       if (!is:file(lastEventWin) ||
  321.           ((BlkLoc(lastEventWin)->file.status & Fs_Window) == 0) ||
  322.           ((BlkLoc(lastEventWin)->file.status & (Fs_Read|Fs_Write)) == 0)) {
  323.          MakeInt(IntVal(amperY) / lastEvLeading + 1, &erRow);
  324.          }
  325.       else {
  326.          w = (wbp)BlkLoc(lastEventWin)->file.fd;
  327.          MakeInt(YTOROW(w, IntVal(amperY)), &erRow);
  328.          }
  329.       }
  330.    else if (VarLoc(*dx) == &erCol) { /* update &x too */
  331.       wbp w;
  332.       if (!is:file(lastEventWin) ||
  333.           ((BlkLoc(lastEventWin)->file.status & Fs_Window) == 0) ||
  334.           ((BlkLoc(lastEventWin)->file.status & (Fs_Read|Fs_Write)) == 0)) {
  335.          MakeInt((IntVal(amperCol) - 1) * lastEvFWidth, &erX);
  336.          }
  337.       else {
  338.          w = (wbp)BlkLoc(lastEventWin)->file.fd;
  339.          MakeInt(COLTOX(w, IntVal(amperCol)), &erX);
  340.          }
  341.       }
  342.    else if (VarLoc(*dx) == &erRow) { /* update &y too */
  343.       wbp w;
  344.       if (!is:file(lastEventWin) ||
  345.           ((BlkLoc(lastEventWin)->file.status & Fs_Window) == 0) ||
  346.           ((BlkLoc(lastEventWin)->file.status & (Fs_Read|Fs_Write)) == 0)) {
  347.          MakeInt((IntVal(amperRow)-1) * lastEvLeading + lastEvAscent, &erY);
  348.          }
  349.       else {
  350.          w = (wbp)BlkLoc(lastEventWin)->file.fd;
  351.          MakeInt(ROWTOY(w, IntVal(amperRow)), &erY);
  352.          }
  353.       }
  354.    return 0;
  355.    }
  356.  
  357.  
  358. /*
  359.  * Enqueue an event, encoding time interval and key state with x and y values.
  360.  */
  361. novalue qevent(ws,e,x,y,t,f)
  362. wsp ws;        /* canvas */
  363. dptr e;        /* event code (descriptor pointer) */
  364. int x, y;    /* x and y values */
  365. uword t;    /* ms clock value */
  366. long f;        /* modifier key flags */
  367.    {
  368.    dptr q = &(ws->listp);    /* a window's event queue (Icon list value) */
  369.    struct descrip d;
  370.    uword ivl, mod;
  371.    int expo;
  372.  
  373.    mod = 0;                /* set modifier key bits */
  374.    if (f & ControlMask) mod |= EQ_MOD_CONTROL;
  375.    if (f & Mod1Mask)    mod |= EQ_MOD_META;
  376.    if (f & ShiftMask)   mod |= EQ_MOD_SHIFT;
  377.  
  378.    if (t != ~(uword)0) {        /* if clock value supplied */
  379.       if (ws->timestamp == 0)        /* if first time */
  380.      ws->timestamp = t;
  381.       if (t < ws->timestamp)        /* if clock went backwards */
  382.      t = ws->timestamp;
  383.       ivl = t - ws->timestamp;        /* calc interval in milliseconds */
  384.       ws->timestamp = t;        /* save new clock value */
  385.       expo = 0;
  386.       while (ivl >= 0x1000) {        /* if too big */
  387.      ivl >>= 4;            /* reduce significance */
  388.      expo += 0x1000;        /* bump exponent */
  389.      }
  390.       ivl += expo;            /* combine exponent with mantissa */
  391.       }
  392.    else
  393.       ivl = 0;                /* report 0 if interval unknown */
  394.  
  395.    c_put(q, e);
  396.    d.dword = D_Integer;
  397.    IntVal(d) = mod | (x & 0xFFFF);
  398.    c_put(q, &d);
  399.    IntVal(d) = (ivl << 16) | (y & 0xFFFF);
  400.    c_put(q, &d);
  401.    }
  402.  
  403. /*
  404.  * setpos() - set (move) canvas position on the screen
  405.  */
  406. static int setpos(w,s)
  407. wbp w;
  408. char *s;
  409.    {
  410.    char *s2, tmp[32];
  411.    int posx, posy;
  412.  
  413.    s2 = s;
  414.    while (isspace(*s2)) s2++;
  415.    if (!isdigit(*s2) && (*s2 != '-')) return Error;
  416.    posx = atol(s2);
  417.    if (*s2 == '-') s2++;
  418.    while (isdigit(*s2)) s2++;
  419.    if (*s2++ != ',') return Error;
  420.    posy = atol(s2);
  421.    if (posx < 0) {
  422.       if (posy < 0) sprintf(tmp,"%d%d",posx,posy);
  423.       else sprintf(tmp,"%d+%d",posx,posy);
  424.       }
  425.    else {
  426.       if (posy < 0) sprintf(tmp,"+%d%d",posx,posy);
  427.       else sprintf(tmp,"+%d+%d",posx,posy);
  428.       }
  429.    return setgeometry(w,tmp);
  430.    }
  431.  
  432. /*
  433.  * setsize() - set canvas size
  434.  */
  435. int setsize(w,s)
  436. wbp w;
  437. char *s;
  438.    {
  439.    char *s2, tmp[32];
  440.    int width, height;
  441.  
  442.    s2 = s;
  443.    while (isspace(*s2)) s2++;
  444.    if (!isdigit(*s2) && (*s2 != '-')) return Error;
  445.    width = atol(s2);
  446.    if (*s2 == '-') s2++;
  447.    while (isdigit(*s2)) s2++;
  448.    if (*s2++ != ',') return Error;
  449.    height = atol(s2);
  450.    sprintf(tmp,"%dx%d",width,height);
  451.    return setgeometry(w,tmp);
  452.    }
  453.  
  454.  
  455.  
  456. /*
  457.  * put a string out to a window using the current attributes
  458.  */
  459. novalue wputstr(w,s,len)
  460. wbp w;
  461. char *s;
  462. int len;
  463.    {
  464.   char *s2 = s;
  465.   wstate *ws = w->window;
  466.   /* turn off the cursor */
  467.   hidecrsr(ws);
  468.  
  469.   while (len > 0) {
  470.     /* find a chunk of printable text */
  471.     while (isprint(*s2) && len > 0) { s2++; len--; }
  472.     /* if a chunk was parsed, write it out */
  473.     if (s2 != s)
  474.        xdis(w, s, s2 - s);
  475.     /* put the 'unprintable' character, if didn't just hit the end */
  476.     if (len-- > 0)
  477.        wputc(*s2++, w);
  478.     s = s2;
  479.     }
  480.  
  481.   /* show the cursor again */
  482.   UpdateCursorPos(ws, w->context);
  483.   showcrsr(ws);
  484.   return;
  485. }
  486.  
  487.  
  488. /*
  489.  * Structures and tables used for color parsing.
  490.  *  Tables must be kept lexically sorted.
  491.  */
  492.  
  493. typedef struct {    /* color name entry */
  494.    char name[8];    /* basic color name */
  495.    char ish[12];    /* -ish form */
  496.    short hue;        /* hue, in degrees */
  497.    char lgt;        /* lightness, as percentage */
  498.    char sat;        /* saturation, as percentage */
  499. } colrname;
  500.  
  501. typedef struct {    /* arbitrary lookup entry */
  502.    char word[10];    /* word */
  503.    char val;        /* value, as percentage */
  504. } colrmod;
  505.  
  506. static colrname colortable[] = {        /* known colors */
  507.    /* color       ish-form     hue  lgt  sat */
  508.    { "black",    "blackish",     0,   0,   0 },
  509.    { "blue",     "bluish",     240,  50, 100 },
  510.    { "brown",    "brownish",    30,  25, 100 },
  511.    { "cyan",     "cyanish",    180,  50, 100 },
  512.    { "gray",     "grayish",      0,  50,   0 },
  513.    { "green",    "greenish",   120,  50, 100 },
  514.    { "grey",     "greyish",      0,  50,   0 },
  515.    { "magenta",  "magentaish", 300,  50, 100 },
  516.    { "orange",   "orangish",    15,  50, 100 },
  517.    { "pink",     "pinkish",    345,  75, 100 },
  518.    { "purple",   "purplish",   270,  50, 100 },
  519.    { "red",      "reddish",      0,  50, 100 },
  520.    { "violet",   "violetish",  270,  75, 100 },
  521.    { "white",    "whitish",      0, 100,   0 },
  522.    { "yellow",   "yellowish",   60,  50, 100 },
  523.    };
  524.  
  525. static colrmod lighttable[] = {            /* lightness modifiers */
  526.    { "dark",       0 },
  527.    { "deep",       0 },        /* = very dark (see code) */
  528.    { "light",    100 },
  529.    { "medium",    50 },
  530.    { "pale",     100 },        /* = very light (see code) */
  531.    };
  532.  
  533. static colrmod sattable[] = {            /* saturation levels */
  534.    { "moderate",  50 },
  535.    { "strong",    75 },
  536.    { "vivid",    100 },
  537.    { "weak",      25 },
  538.    };
  539.  
  540. /*
  541.  *  parsecolor(w, s, &r, &g, &b) - parse a color specification
  542.  *
  543.  *  parsecolor interprets a color specification and produces r/g/b values
  544.  *  scaled linearly from 0 to 65535.  parsecolor returns Succeeded or Failed.
  545.  *
  546.  *  An Icon color specification can be any of the forms
  547.  *
  548.  *     #rgb            (hexadecimal digits)
  549.  *     #rrggbb
  550.  *     #rrrgggbbb
  551.  *     #rrrrggggbbbb
  552.  *     nnnnn,nnnnn,nnnnn    (integers 0 - 65535)
  553.  *     <Icon color phrase>
  554.  *     <native color spec>
  555.  */
  556.  
  557. int parsecolor(w, buf, r, g, b)
  558. wbp w;
  559. char *buf;
  560. long *r, *g, *b;
  561.    {
  562.    int len, mul;
  563.    char *fmt, c, cbuffer[MAXCOLORNAME];
  564.    double dr, dg, db;
  565.  
  566.    *r = *g = *b = 0L;
  567.  
  568.    /* trim leading spaces */
  569.    while (isspace(*buf))
  570.       buf++;
  571.  
  572.    /* try interpreting as three comma-separated integers */
  573.    if (sscanf(buf, "%lf,%lf,%lf%c", &dr, &dg, &db, &c) == 3) {
  574.       *r = dr;
  575.       *g = dg;
  576.       *b = db;
  577.       if (*r>=0 && *r<=65535 && *g>=0 && *g<=65535 && *b>=0 && *b<=65535)
  578.          return Succeeded;
  579.       else
  580.          return Failed;
  581.       }
  582.  
  583.    /* try interpreting as a hexadecimal value */
  584.    if (*buf == '#') {
  585.       buf++;
  586.       for (len = 0; isalnum(buf[len]); len++);
  587.       switch (len) {
  588.          case  3:  fmt = "%1x%1x%1x%c";  mul = 0x1111;  break;
  589.          case  6:  fmt = "%2x%2x%2x%c";  mul = 0x0101;  break;
  590.          case  9:  fmt = "%3x%3x%3x%c";  mul = 0x0010;  break;
  591.          case 12:  fmt = "%4x%4x%4x%c";  mul = 0x0001;  break;
  592.          default:  return Failed;
  593.       }
  594.       if (sscanf(buf, fmt, r, g, b, &c) != 3)
  595.          return Failed;
  596.       *r *= mul;
  597.       *g *= mul;
  598.       *b *= mul;
  599.       return Succeeded;
  600.       }
  601.  
  602.    /* try interpreting as a color phrase or as a native color spec */
  603.    if (colorphrase(buf, r, g, b) || nativecolor(w, buf, r, g, b))
  604.       return Succeeded;
  605.    else
  606.       return Failed;
  607.    }
  608.  
  609. /*
  610.  *  colorphrase(s, &r, &g, &b) -- parse Icon color phrase.
  611.  *
  612.  *  An Icon color phrase matches the pattern
  613.  *
  614.  *                               weak
  615.  *                  pale         moderate
  616.  *                  light        strong
  617.  *          [[very] medium ]   [ vivid    ]   [color[ish]]   color
  618.  *                  dark 
  619.  *                  deep
  620.  *
  621.  *  where "color" is any of:
  622.  *
  623.  *          black gray grey white pink violet brown
  624.  *          red orange yellow green cyan blue purple magenta
  625.  *
  626.  *  A single space or hyphen separates each word from its neighbor.  The
  627.  *  default lightness is "medium", and the default saturation is "vivid".
  628.  *
  629.  *  "pale" means "very light"; "deep" means "very dark".
  630.  *
  631.  *  This naming scheme is based loosely on
  632.  *    A New Color-Naming System for Graphics Languages
  633.  *    Toby Berk, Lee Brownston, and Arie Kaufman
  634.  *    IEEE Computer Graphics & Applications, May 1982
  635.  */
  636.  
  637. static int colorphrase(buf, r, g, b)
  638. char *buf; 
  639. long *r, *g, *b;
  640.    {
  641.    int len, very;
  642.    char *p, *ebuf, cbuffer[MAXCOLORNAME];
  643.    float lgt, sat, blend, bl2, m1, m2;
  644.    float h1, l1, s1, h2, l2, s2, r2, g2, b2;
  645.  
  646.    lgt = -1.0;                /* default no lightness mod */
  647.    sat =  1.0;                /* default vivid saturation */
  648.    len = strlen(buf);
  649.    while (isspace(buf[len-1]))
  650.       len--;                /* trim trailing spaces */
  651.  
  652.    /* copy spec, replacing spaces and hyphens with NULs */
  653.    if (len >= sizeof(cbuffer))
  654.       return 0;                /* if too long for valid Icon spec */
  655.    strncpy(cbuffer, buf, len);
  656.    ebuf = cbuffer + len;
  657.    for (buf = cbuffer; buf < ebuf; buf++)
  658.       if (*buf == ' ' || *buf == '-')
  659.          *buf = '\0';
  660.    *ebuf = '\0';
  661.    buf = cbuffer;
  662.  
  663.    /* check for "very" */
  664.    if (strcmp(buf, "very") == 0) {
  665.       very = 1;
  666.       buf += strlen(buf) + 1;
  667.       if (buf >= ebuf)
  668.          return 0;
  669.       }
  670.    else
  671.       very = 0;
  672.  
  673.    /* check for lightness adjective */
  674.    p = qsearch(buf, (char *)lighttable,
  675.       ElemCount(lighttable), ElemSize(lighttable), strcmp);
  676.    if (p) {
  677.       /* set the "very" flag for "pale" or "deep" */
  678.       if (strcmp(buf, "pale") == 0)
  679.          very = 1;            /* pale = very light */
  680.       else if (strcmp(buf, "deep") == 0)
  681.          very = 1;            /* deep = very dark */
  682.       /* skip past word */
  683.       buf += strlen(buf) + 1;
  684.       if (buf >= ebuf)
  685.          return 0;
  686.       /* save lightness value, but ignore "medium" */
  687.       if ((((colrmod *)p) -> val) != 50)
  688.          lgt = ((colrmod *)p) -> val / 100.0;
  689.       }
  690.    else if (very)
  691.       return 0;
  692.  
  693.    /* check for saturation adjective */
  694.    p = qsearch(buf, (char *)sattable,
  695.       ElemCount(sattable), ElemSize(sattable), strcmp);
  696.    if (p) {
  697.       sat = ((colrmod *)p) -> val / 100.0;
  698.       buf += strlen(buf) + 1;
  699.       if (buf >= ebuf)
  700.          return 0;
  701.       }
  702.  
  703.    if (buf + strlen(buf) >= ebuf)
  704.       blend = 0.0;            /* only one word left */
  705.    else {
  706.       /* we have two (or more) name words; get the first */
  707.       if (p = qsearch(buf, colortable[0].name,
  708.             ElemCount(colortable), ElemSize(colortable), strcmp)) {
  709.          blend = 0.5;
  710.          }
  711.       else if (p = qsearch(buf, colortable[0].ish,
  712.             ElemCount(colortable), ElemSize(colortable), strcmp)) {
  713.          p -= sizeof(colortable[0].name);
  714.          blend = 0.25;
  715.          }
  716.       else
  717.          return 0;
  718.  
  719.       h1 = ((colrname *)p) -> hue;
  720.       l1 = ((colrname *)p) -> lgt / 100.0;
  721.       s1 = ((colrname *)p) -> sat / 100.0;
  722.       buf += strlen(buf) + 1;
  723.       }
  724.  
  725.    /* process second (or only) name word */
  726.    p = qsearch(buf, colortable[0].name,
  727.       ElemCount(colortable), ElemSize(colortable), strcmp);
  728.    if (!p || buf + strlen(buf) < ebuf)
  729.       return 0;
  730.    h2 = ((colrname *)p) -> hue;
  731.    l2 = ((colrname *)p) -> lgt / 100.0;
  732.    s2 = ((colrname *)p) -> sat / 100.0;
  733.  
  734.    /* at this point we know we have a valid spec */
  735.  
  736.    /* interpolate hls specs */
  737.    if (blend > 0) {
  738.       bl2 = 1.0 - blend;
  739.    
  740.       if (s1 == 0.0)
  741.          ; /* use h2 unchanged */
  742.       else if (s2 == 0.0)
  743.          h2 = h1;
  744.       else if (h2 - h1 > 180)
  745.          h2 = blend * h1 + bl2 * (h2 - 360);
  746.       else if (h1 - h2 > 180)
  747.          h2 = blend * (h1 - 360) + bl2 * h2;
  748.       else
  749.          h2 = blend * h1 + bl2 * h2;
  750.       if (h2 < 0)
  751.          h2 += 360;
  752.    
  753.       l2 = blend * l1 + bl2 * l2;
  754.       s2 = blend * s1 + bl2 * s2;
  755.       }
  756.  
  757.    /* apply saturation and lightness modifiers */
  758.    if (lgt >= 0.0) {
  759.       if (very)
  760.          l2 = (2 * lgt + l2) / 3.0;
  761.       else
  762.          l2 = (lgt + 2 * l2) / 3.0;
  763.       }
  764.    s2 *= sat;
  765.  
  766.    /* convert h2,l2,s2 to r2,g2,b2 */
  767.    /* from Foley & Van Dam, 1st edition, p. 619 */
  768.    /* beware of dangerous typos in 2nd edition */
  769.    if (s2 == 0)
  770.       r2 = g2 = b2 = l2;
  771.    else {
  772.       if (l2 < 0.5)
  773.          m2 = l2 * (1 + s2);
  774.       else
  775.          m2 = l2 + s2 - l2 * s2;
  776.       m1 = 2 * l2 - m2;
  777.       r2 = rgbval(m1, m2, h2 + 120);
  778.       g2 = rgbval(m1, m2, h2);
  779.       b2 = rgbval(m1, m2, h2 - 120);
  780.       }
  781.  
  782.    /* scale and convert the calculated result */
  783.    *r = 65535 * r2;
  784.    *g = 65535 * g2;
  785.    *b = 65535 * b2;
  786.  
  787.    return 1;
  788.    }
  789.  
  790. /*
  791.  * rgbval(n1, n2, hue) - helper function for HLS to RGB conversion
  792.  */
  793. static double rgbval(n1, n2, hue)
  794. double n1, n2, hue;
  795.    {
  796.    if (hue > 360)
  797.       hue -= 360;
  798.    else if (hue < 0)
  799.       hue += 360;
  800.  
  801.    if (hue < 60)
  802.       return n1 + (n2 - n1) * hue / 60.0;
  803.    else if (hue < 180)
  804.       return n2;
  805.    else if (hue < 240)
  806.       return n1 + (n2 - n1) * (240 - hue) / 60.0;
  807.    else
  808.       return n1;
  809.    }
  810.  
  811. /*
  812.  *  Functions and data for reading and writing GIF images
  813.  */
  814.  
  815. #define GifSeparator    0x2C    /* (',') beginning of image */
  816. #define GifTerminator    0x3B    /* (';') end of image */
  817. #define GifExtension    0x21    /* ('!') extension block */
  818. #define GifControlExt    0xF9    /*       graphic control extension label */
  819. #define GifEmpty    -1    /* internal flag indicating no prefix */
  820.  
  821. #define GifTableSize    4096    /* maximum number of entries in table */
  822. #define GifBlockSize    255    /* size of output block */
  823.  
  824. typedef struct lzwnode {    /* structure of LZW encoding tree node */
  825.    unsigned short tcode;        /* token code */
  826.    unsigned short child;    /* first child node */
  827.    unsigned short sibling;    /* next sibling */
  828.    } lzwnode;
  829.  
  830. hidden    int    gfread        Params((char *fn, int p));
  831. hidden    int    gfheader    Params((FILE *f));
  832. hidden    int    gfskip        Params((FILE *f));
  833. hidden    novalue    gfcontrol    Params((FILE *f));
  834. hidden    int    gfimhdr        Params((FILE *f));
  835. hidden    int    gfmap        Params((FILE *f, int p));
  836. hidden    int    gfsetup        Params((noargs));
  837. hidden    int    gfrdata        Params((FILE *f));
  838. hidden    int    gfrcode        Params((FILE *f));
  839. hidden    novalue    gfinsert    Params((int prev, int c));
  840. hidden    int    gffirst        Params((int c));
  841. hidden    novalue    gfgen        Params((int c));
  842. hidden    novalue    gfput        Params((int b));
  843.  
  844. hidden    int    gfwrite        Params((wbp w, char *filename,
  845.                    int x, int y, int width, int height));
  846. hidden    novalue    gfpack        Params((unsigned char *data, long len, 
  847.                    struct palentry *paltbl));
  848. hidden    novalue    gfmktree    Params((lzwnode *tree));
  849. hidden    novalue    gfout        Params((int tcode));
  850. hidden    novalue    gfdump        Params((noargs));
  851.  
  852. static FILE *gf_f;            /* input file */
  853.  
  854. static int gf_gcmap, gf_lcmap;        /* global color map? local color map? */
  855. static int gf_nbits;            /* number of bits per pixel */
  856. static int gf_ilace;            /* interlace flag */
  857. static int gf_width, gf_height;        /* image size */
  858.  
  859. static short *gf_prefix, *gf_suffix;    /* prefix and suffix tables */
  860. static int gf_free;            /* next free position */
  861.  
  862. static struct palentry *gf_paltbl;    /* palette table */
  863. static unsigned char *gf_string;    /* image string */
  864. static unsigned char *gf_nxt, *gf_lim;    /* store pointer and its limit */
  865. static int gf_row, gf_step;        /* current row and step size */
  866.  
  867. static int gf_cdsize;            /* code size */
  868. static int gf_clear, gf_eoi;        /* values of CLEAR and EOI codes */
  869. static int gf_lzwbits, gf_lzwmask;    /* current bits per code */
  870.  
  871. static unsigned char *gf_obuf;        /* output buffer */
  872. static unsigned long gf_curr;        /* current partial byte(s) */
  873. static int gf_valid;            /* number of valid bits */
  874. static int gf_rem;            /* remaining bytes in this block */
  875.  
  876. /*
  877.  * readGIF(filename, p, imd) - read GIF file into image data structure
  878.  *
  879.  * p is a palette number to which the GIF colors are to be coerced;
  880.  * p=0 uses the colors exactly as given in the GIF file.
  881.  */
  882. int readGIF(filename, p, imd)
  883. char *filename;
  884. int p;
  885. struct imgdata *imd;
  886.    {
  887.    int r;
  888.  
  889.    r = gfread(filename, p);            /* read image */
  890.  
  891.    if (gf_prefix) free((pointer)gf_prefix);    /* deallocate temp memory */
  892.    if (gf_suffix) free((pointer)gf_suffix);
  893.    if (gf_f) fclose(gf_f);
  894.  
  895.    if (r != Succeeded) {            /* if no success, free mem */
  896.       if (gf_paltbl) free((pointer) gf_paltbl);
  897.       if (gf_string) free((pointer) gf_string);
  898.       return r;                    /* return Failed or Error */
  899.       }
  900.  
  901.    imd->width = gf_width;            /* set return variables */
  902.    imd->height = gf_height;
  903.    imd->paltbl = gf_paltbl;
  904.    imd->data = gf_string;
  905.  
  906.    return Succeeded;                /* return success */
  907.    }
  908.  
  909. /*
  910.  * gfread(filename, p) - read GIF file, setting gf_ globals
  911.  */
  912. static int gfread(filename, p)
  913. char *filename;
  914. int p;
  915.    {
  916.    int i;
  917.  
  918.    gf_f = NULL;
  919.    gf_prefix = NULL;
  920.    gf_suffix = NULL;
  921.    gf_string = NULL;
  922.  
  923.    if (!(gf_paltbl = (struct palentry *)malloc(256 * sizeof(struct palentry))))
  924.       return Failed;
  925.  
  926. #ifdef MSWindows
  927.    if ((gf_f = fopen(filename, "rb")) == NULL)
  928. #else                    /* MSWindows */
  929.    if ((gf_f = fopen(filename, "r")) == NULL)
  930. #endif                    /* MSWindows */
  931.       return Failed;
  932.  
  933.    for (i = 0; i < 256; i++)        /* init palette table */
  934.       gf_paltbl[i].used = gf_paltbl[i].valid = gf_paltbl[i].transpt = 0;
  935.  
  936.    if (!gfheader(gf_f))            /* read file header */
  937.       return Failed;
  938.    if (gf_gcmap)            /* read global color map, if any */
  939.       if (!gfmap(gf_f, p))
  940.          return Failed;
  941.    if (!gfskip(gf_f))            /* skip to start of image */
  942.       return Failed;
  943.    if (!gfimhdr(gf_f))            /* read image header */
  944.       return Failed;
  945.    if (gf_lcmap)            /* read local color map, if any */
  946.       if (!gfmap(gf_f, p))
  947.          return Failed;
  948.    if (!gfsetup())            /* prepare to read image */
  949.       return Error;
  950.    if (!gfrdata(gf_f))            /* read image data */
  951.       return Failed;
  952.    while (gf_row < gf_height)        /* pad if too short */
  953.       gfput(0);
  954.  
  955.    return Succeeded;
  956.    }
  957.  
  958. /*
  959.  * gfheader(f) - read GIF file header; return nonzero if successful
  960.  */
  961. static int gfheader(f)
  962. FILE *f;
  963.    {
  964.    unsigned char hdr[13];        /* size of a GIF header */
  965.    int b;
  966.  
  967.    if (fread((char *)hdr, sizeof(char), sizeof(hdr), f) != sizeof(hdr))
  968.       return 0;                /* header short or missing */
  969.    if (strncmp((char *)hdr, "GIF", 3) != 0 ||
  970.          !isdigit(hdr[3]) || !isdigit(hdr[4]))
  971.       return 0;                /* not GIFnn */
  972.  
  973.    b = hdr[10];                /* flag byte */
  974.    gf_gcmap = b & 0x80;            /* global color map flag */
  975.    gf_nbits = (b & 7) + 1;        /* number of bits per pixel */
  976.    return 1;
  977.    }
  978.  
  979. /*
  980.  * gfskip(f) - skip intermediate blocks and locate image
  981.  */
  982. static int gfskip(f)
  983. FILE *f;
  984.    {
  985.    int c, n;
  986.  
  987.    while ((c = getc(f)) != GifSeparator) { /* look for start-of-image flag */
  988.       if (c == EOF)
  989.          return 0;
  990.       if (c == GifExtension) {        /* if extension block is present */
  991.          c = getc(f);                /* get label */
  992.      if ((c & 0xFF) == GifControlExt)
  993.         gfcontrol(f);            /* process control subblock */
  994.          while ((n = getc(f)) != 0) {        /* read blks until empty one */
  995.             if (n == EOF)
  996.                return 0;
  997.         n &= 0xFF;                /* ensure positive count */
  998.             while (n--)                /* skip block contents */
  999.                getc(f);
  1000.             }
  1001.          }
  1002.       }
  1003.    return 1;
  1004.    }
  1005.  
  1006. /*
  1007.  * gfcontrol(f) - process control extension subblock
  1008.  */
  1009. static novalue gfcontrol(f)
  1010. FILE *f;
  1011.    {
  1012.    int i, n, c, t;
  1013.  
  1014.    n = getc(f) & 0xFF;                /* subblock length (s/b 4) */
  1015.    for (i = 0; i < n; i++) {
  1016.       c = getc(f) & 0xFF;
  1017.       if (i == 0)
  1018.      t = c & 1;                /* transparency flag */
  1019.       else if (i == 3 && t != 0) {
  1020.      gf_paltbl[c].transpt = 1;        /* set flag for transpt color */
  1021.      gf_paltbl[c].valid = 0;        /* color is no longer "valid" */
  1022.      }
  1023.       }
  1024.    }
  1025.  
  1026. /*
  1027.  * gfimhdr(f) - read image header
  1028.  */
  1029. static int gfimhdr(f)
  1030. FILE *f;
  1031.    {
  1032.    unsigned char hdr[9];        /* size of image hdr excl separator */
  1033.    int b;
  1034.  
  1035.    if (fread((char *)hdr, sizeof(char), sizeof(hdr), f) != sizeof(hdr))
  1036.       return 0;                /* header short or missing */
  1037.    gf_width = hdr[4] + 256 * hdr[5];
  1038.    gf_height = hdr[6] + 256 * hdr[7];
  1039.    b = hdr[8];                /* flag byte */
  1040.    gf_lcmap = b & 0x80;            /* local color map flag */
  1041.    gf_ilace = b & 0x40;            /* interlace flag */
  1042.    if (gf_lcmap)
  1043.       gf_nbits = (b & 7) + 1;        /* if local map, reset nbits also */
  1044.    return 1;
  1045.    }
  1046.  
  1047. /*
  1048.  * gfmap(f, p) - read GIF color map into paltbl under control of palette p
  1049.  */
  1050. static int gfmap(f, p)
  1051. FILE *f;
  1052. int p;
  1053.    {
  1054.    int ncolors, i, r, g, b, c;
  1055.    struct palentry *stdpal = 0;
  1056.  
  1057.    if (p)
  1058.       stdpal = palsetup(p);
  1059.  
  1060.    ncolors = 1 << gf_nbits;
  1061.  
  1062.    for (i = 0; i < ncolors; i++) {
  1063.       r = getc(f);
  1064.       g = getc(f);
  1065.       b = getc(f);
  1066.       if (r == EOF || g == EOF || b == EOF)
  1067.          return 0;
  1068.       if (p) {
  1069.          c = *(unsigned char *)(rgbkey(p, r / 255.0, g / 255.0, b / 255.0));
  1070.          gf_paltbl[i].clr = stdpal[c].clr;
  1071.          }
  1072.       else {
  1073.          gf_paltbl[i].clr.red   = 257 * r;    /* 257 * 255 -> 65535 */
  1074.          gf_paltbl[i].clr.green = 257 * g;
  1075.          gf_paltbl[i].clr.blue  = 257 * b;
  1076.          }
  1077.       if (!gf_paltbl[i].transpt)        /* if not transparent color */
  1078.          gf_paltbl[i].valid = 1;        /* mark as valid/opaque */
  1079.       }
  1080.  
  1081.    return 1;
  1082.    }
  1083.  
  1084. /*
  1085.  * gfsetup() - prepare to read GIF data
  1086.  */
  1087. static int gfsetup()
  1088.    {
  1089.    int i;
  1090.    word len;
  1091.  
  1092.    len = (word)gf_width * (word)gf_height;
  1093.    gf_string = (unsigned char *)malloc((msize)len);
  1094.    gf_prefix = (short *)malloc(GifTableSize * sizeof(short));
  1095.    gf_suffix = (short *)malloc(GifTableSize * sizeof(short));
  1096.    if (!gf_string || !gf_prefix || !gf_suffix)
  1097.       return 0;
  1098.    for (i = 0; i < GifTableSize; i++) {
  1099.       gf_prefix[i] = GifEmpty;
  1100.       gf_suffix[i] = i;
  1101.       }
  1102.  
  1103.    gf_row = 0;                /* current row is 0 */
  1104.    gf_nxt = gf_string;            /* set store pointer */
  1105.  
  1106.    if (gf_ilace) {            /* if interlaced */
  1107.       gf_step = 8;            /* step rows by 8 */
  1108.       gf_lim = gf_string + gf_width;    /* stop at end of one row */
  1109.       }
  1110.    else {
  1111.       gf_lim = gf_string + len;        /* do whole image at once */
  1112.       gf_step = gf_height;        /* step to end when full */
  1113.       }
  1114.  
  1115.    return 1;
  1116.    }
  1117.  
  1118. /*
  1119.  * gfrdata(f) - read GIF data
  1120.  */
  1121. static int gfrdata(f)
  1122. FILE *f;
  1123.    {
  1124.    int curr, prev, c;
  1125.  
  1126.    if ((gf_cdsize = getc(f)) == EOF)
  1127.       return 0;
  1128.    gf_clear = 1 << gf_cdsize;
  1129.    gf_eoi = gf_clear + 1;
  1130.    gf_free = gf_eoi + 1;
  1131.  
  1132.    gf_lzwbits = gf_cdsize + 1;
  1133.    gf_lzwmask = (1 << gf_lzwbits) - 1;
  1134.  
  1135.    gf_curr = 0;
  1136.    gf_valid = 0;
  1137.    gf_rem = 0;
  1138.  
  1139.    prev = curr = gfrcode(f);
  1140.    while (curr != gf_eoi) {
  1141.       if (curr == gf_clear) {        /* if reset code */
  1142.          gf_lzwbits = gf_cdsize + 1;
  1143.          gf_lzwmask = (1 << gf_lzwbits) - 1;
  1144.          gf_free = gf_eoi + 1;
  1145.          prev = curr = gfrcode(f);
  1146.          gfgen(curr);
  1147.          }
  1148.       else if (curr < gf_free) {    /* if code is in table */
  1149.          gfgen(curr);
  1150.          gfinsert(prev, gffirst(curr));
  1151.          prev = curr;
  1152.          }
  1153.       else if (curr == gf_free) {    /* not yet in table */
  1154.          c = gffirst(prev);
  1155.          gfgen(prev);
  1156.          gfput(c);
  1157.          gfinsert(prev, c);
  1158.          prev = curr;
  1159.          }
  1160.       else {                /* illegal code */
  1161.          if (gf_nxt == gf_lim)
  1162.             return 1;            /* assume just extra stuff after end */
  1163.          else
  1164.             return 0;            /* more badly confused */
  1165.          }
  1166.       curr = gfrcode(f);
  1167.       }
  1168.  
  1169.    return 1;
  1170.    }
  1171.  
  1172. /*
  1173.  * gfrcode(f) - read next LZW code
  1174.  */
  1175. static int gfrcode(f)
  1176. FILE *f;
  1177.    {
  1178.    int c, r;
  1179.  
  1180.    while (gf_valid < gf_lzwbits) {
  1181.       if (--gf_rem <= 0) {
  1182.          if ((gf_rem = getc(f)) == EOF)
  1183.             return gf_eoi;
  1184.          }
  1185.       if ((c = getc(f)) == EOF)
  1186.          return gf_eoi;
  1187.       gf_curr |= ((c & 0xFF) << gf_valid);
  1188.       gf_valid += 8;
  1189.       }
  1190.    r = gf_curr & gf_lzwmask;
  1191.    gf_curr >>= gf_lzwbits;
  1192.    gf_valid -= gf_lzwbits;
  1193.    return r;
  1194.    }
  1195.  
  1196. /*
  1197.  * gfinsert(prev, c) - insert into table
  1198.  */
  1199. static novalue gfinsert(prev, c)
  1200. int prev, c;
  1201.    {
  1202.  
  1203.    if (gf_free >= GifTableSize)        /* sanity check */
  1204.       return;
  1205.  
  1206.    gf_prefix[gf_free] = prev;
  1207.    gf_suffix[gf_free] = c;
  1208.  
  1209.    /* increase code size if code bits are exhausted, up to max of 12 bits */
  1210.    if (++gf_free > gf_lzwmask && gf_lzwbits < 12) {
  1211.       gf_lzwmask = gf_lzwmask * 2 + 1;
  1212.       gf_lzwbits++;
  1213.       }
  1214.  
  1215.    }
  1216.  
  1217. /*
  1218.  * gffirst(c) - return the first pixel in a map structure
  1219.  */
  1220. static int gffirst(c)
  1221. int c;
  1222.    {
  1223.    int d;
  1224.  
  1225.    if (c >= gf_free)
  1226.       return 0;                /* not in table (error) */
  1227.    while ((d = gf_prefix[c]) != GifEmpty)
  1228.       c = d;
  1229.    return gf_suffix[c];
  1230.    }
  1231.  
  1232. /*
  1233.  * gfgen(c) - generate and output prefix
  1234.  */
  1235. static novalue gfgen(c)
  1236. int c;
  1237.    {
  1238.    int d;
  1239.  
  1240.    if ((d = gf_prefix[c]) != GifEmpty)
  1241.       gfgen(d);
  1242.    gfput(gf_suffix[c]);
  1243.    }
  1244.  
  1245. /*
  1246.  * gfput(b) - add a byte to the output string
  1247.  */
  1248. static novalue gfput(b)
  1249. int b;
  1250.    {
  1251.    if (gf_nxt >= gf_lim) {        /* if current row is full */
  1252.       gf_row += gf_step;
  1253.       while (gf_row >= gf_height && gf_ilace && gf_step > 2) {
  1254.          if (gf_step == 4) {
  1255.             gf_row = 1;
  1256.             gf_step = 2;
  1257.             }
  1258.      else if ((gf_row % 8) != 0) {
  1259.             gf_row = 2;
  1260.             gf_step = 4;
  1261.             }
  1262.          else {
  1263.             gf_row = 4;
  1264.         /* gf_step remains 8 */
  1265.         }
  1266.      }
  1267.  
  1268.       if (gf_row >= gf_height) {
  1269.      gf_step = 0;
  1270.      return;            /* too much data; ignore it */
  1271.      }
  1272.       gf_nxt = gf_string + ((word)gf_row * (word)gf_width);
  1273.       gf_lim = gf_nxt + gf_width;
  1274.       }
  1275.  
  1276.    *gf_nxt++ = b;            /* store byte */
  1277.    gf_paltbl[b].used = 1;        /* mark color entry as used */
  1278.    }
  1279.  
  1280. /*
  1281.  * writeGIF(w, filename, x, y, width, height) - write GIF image
  1282.  *
  1283.  * Returns Succeeded, Failed, or Error.
  1284.  * We assume that the area specified is within the window.
  1285.  */
  1286. int writeGIF(w, filename, x, y, width, height)
  1287. wbp w;
  1288. char *filename;
  1289. int x, y, width, height;
  1290.    {
  1291.    int r;
  1292.  
  1293.    r = gfwrite(w, filename, x, y, width, height);
  1294.    if (gf_f) fclose(gf_f);
  1295.    if (gf_string) free((pointer)gf_string);
  1296.    return r;
  1297.    }
  1298.  
  1299. #ifdef ConsoleWindow
  1300. #undef fprintf
  1301. #undef putc
  1302. #define putc fputc
  1303. #endif                    /* ConsoleWindow */
  1304. /*
  1305.  * gfwrite(w, filename, x, y, width, height) - write GIF file
  1306.  *
  1307.  * We write GIF87a format (not 89a) for maximum acceptability and because
  1308.  * we don't need any of the extensions of GIF89.
  1309.  */
  1310.  
  1311. static int gfwrite(w, filename, x, y, width, height)
  1312. wbp w;
  1313. char *filename;
  1314. int x, y, width, height;
  1315.    {
  1316.    int i, c, cur;
  1317.    long len;
  1318.    LinearColor *cp;
  1319.    unsigned char *p, *q;
  1320.    struct palentry paltbl[DMAXCOLORS];
  1321.    unsigned char obuf[GifBlockSize];
  1322.    lzwnode tree[GifTableSize + 1];
  1323.  
  1324.    len = (long)width * (long)height;    /* total length of data */
  1325.  
  1326.    if (!(gf_f = fopen(filename, "wb")))
  1327.       return Failed;
  1328.    if (!(gf_string = (unsigned char*)malloc((msize)len)))
  1329.       return Error;
  1330.  
  1331.    for (i = 0; i < DMAXCOLORS; i++)
  1332.       paltbl[i].used = paltbl[i].valid = paltbl[i].transpt = 0;
  1333.    if (!getimstr(w, x, y, width, height, paltbl, gf_string))
  1334.       return Error;
  1335.  
  1336.    gfpack(gf_string, len, paltbl);    /* pack color table, set color params */
  1337.  
  1338.    gf_clear = 1 << gf_cdsize;        /* set encoding variables */
  1339.    gf_eoi = gf_clear + 1;
  1340.    gf_free = gf_eoi + 1;
  1341.    gf_lzwbits = gf_cdsize + 1;
  1342.  
  1343.    /* 
  1344.     * Write the header, global color table, and image descriptor.
  1345.     */
  1346.  
  1347.    fprintf(gf_f, "GIF87a%c%c%c%c%c%c%c", width, width >> 8, height, height >> 8,
  1348.       0x80 | ((gf_nbits - 1) << 4) | (gf_nbits - 1), 0, 0);
  1349.  
  1350.  
  1351.    for (i = 0; i < (1 << gf_nbits); i++) {    /* output color table */
  1352.       if (i < DMAXCOLORS && paltbl[i].valid) {
  1353.          cp = &paltbl[i].clr;
  1354.          putc(cp->red >> 8, gf_f);
  1355.          putc(cp->green >> 8, gf_f);
  1356.          putc(cp->blue >> 8, gf_f);
  1357.          }
  1358.       else {
  1359.          putc(0, gf_f);
  1360.          putc(0, gf_f);
  1361.          putc(0, gf_f);
  1362.          }
  1363.       }
  1364.  
  1365.    fprintf(gf_f, "%c%c%c%c%c%c%c%c%c%c%c", GifSeparator, 0, 0, 0, 0,
  1366.       width, width >> 8, height, height >> 8, gf_nbits - 1, gf_cdsize);
  1367.  
  1368.    /*
  1369.     * Encode and write the image.
  1370.     */
  1371.    gf_obuf = obuf;            /* initialize output state */
  1372.    gf_curr = 0;
  1373.    gf_valid = 0;
  1374.    gf_rem = GifBlockSize;
  1375.  
  1376.    gfmktree(tree);            /* initialize encoding tree */
  1377.  
  1378.    gfout(gf_clear);            /* start with CLEAR code */
  1379.  
  1380.    p = gf_string;
  1381.    q = p + len;
  1382.    cur = *p++;                /* first pixel is special */
  1383.    while (p < q) {
  1384.       c = *p++;                /* get code */
  1385.       for (i = tree[cur].child; i != 0; i = tree[i].sibling)
  1386.          if (tree[i].tcode == c)    /* find as suffix of previous string */
  1387.             break;
  1388.       if (i != 0) {            /* if found in encoding tree */
  1389.          cur = i;            /* note where */
  1390.          continue;            /* and accumulate more */
  1391.          }
  1392.       gfout(cur);            /* new combination -- output prefix */
  1393.       tree[gf_free].tcode = c;        /* make node for new combination */
  1394.       tree[gf_free].child = 0;
  1395.       tree[gf_free].sibling = tree[cur].child;
  1396.       tree[cur].child = gf_free;
  1397.       cur = c;                /* restart string from single pixel */
  1398.       ++gf_free;            /* grow tree to account for new node */
  1399.       if (gf_free > (1 << gf_lzwbits)) {
  1400.          if (gf_free > GifTableSize) {
  1401.             gfout(gf_clear);        /* table is full; reset to empty */
  1402.             gf_lzwbits = gf_cdsize + 1;
  1403.             gfmktree(tree);
  1404.             }
  1405.          else
  1406.             gf_lzwbits++;        /* time to make output one bit wider */
  1407.          }
  1408.       }
  1409.  
  1410.    /*
  1411.     * Finish up.
  1412.     */
  1413.    gfout(cur);                /* flush accumulated prefix */
  1414.    gfout(gf_eoi);            /* send EOI code */
  1415.    gf_lzwbits = 7;
  1416.    gfout(0);                /* force out last partial byte */
  1417.    gfdump();                /* dump final block */
  1418.    putc(0, gf_f);            /* terminate image (block of size 0) */
  1419.    putc(GifTerminator, gf_f);        /* terminate file */
  1420.  
  1421.    fflush(gf_f);
  1422.    if (ferror(gf_f))
  1423.       return Failed;
  1424.    else
  1425.       return Succeeded;            /* caller will close file */
  1426.    }
  1427.  
  1428. /*
  1429.  * gfpack() - pack palette table to eliminate gaps
  1430.  *
  1431.  * Sets gf_nbits and gf_cdsize based on the number of colors.
  1432.  */
  1433. static novalue gfpack(data, len, paltbl)
  1434. unsigned char *data;
  1435. long len;
  1436. struct palentry *paltbl;
  1437.    {
  1438.    int i, ncolors, lastcolor;
  1439.    unsigned char *p, *q, cmap[DMAXCOLORS];
  1440.  
  1441.    ncolors = 0;
  1442.    lastcolor = 0;
  1443.    for (i = 0; i < DMAXCOLORS; i++)
  1444.       if (paltbl[i].used) {
  1445.          lastcolor = i;
  1446.          cmap[i] = ncolors;        /* mapping to output color */
  1447.          if (i != ncolors) {
  1448.             paltbl[ncolors] = paltbl[i];        /* shift down */
  1449.             paltbl[i].used = paltbl[i].valid = paltbl[i].transpt = 0;
  1450.                             /* invalidate old */
  1451.             }
  1452.          ncolors++;
  1453.          }
  1454.  
  1455.    if (ncolors < lastcolor + 1) {    /* if entries were moved to fill gaps */
  1456.       p = data;
  1457.       q = p + len;
  1458.       while (p < q) {
  1459.          *p = cmap[*p];            /* adjust color values in data string */
  1460.          p++;
  1461.          }
  1462.       }
  1463.  
  1464.    gf_nbits = 1;
  1465.    while ((1 << gf_nbits) < ncolors)
  1466.       gf_nbits++;
  1467.    if (gf_nbits < 2)
  1468.       gf_cdsize = 2;
  1469.    else
  1470.       gf_cdsize = gf_nbits;
  1471.    }
  1472.  
  1473. /*
  1474.  * gfmktree() - initialize or reinitialize encoding tree
  1475.  */
  1476.  
  1477. hidden novalue gfmktree(tree)
  1478. lzwnode *tree;
  1479.    {
  1480.    int i;
  1481.  
  1482.    for (i = 0; i < gf_clear; i++) {    /* for each basic entry */
  1483.       tree[i].tcode = i;            /* code is pixel value */
  1484.       tree[i].child = 0;        /* no suffixes yet */
  1485.       tree[i].sibling = i + 1;        /* next code is sibling */
  1486.       }
  1487.    tree[gf_clear - 1].sibling = 0;    /* last entry has no sibling */
  1488.    gf_free = gf_eoi + 1;        /* reset next free entry */
  1489.    }
  1490.  
  1491. /*
  1492.  * gfout(code) - output one LZW token
  1493.  */
  1494. hidden novalue gfout(tcode)
  1495. int tcode;
  1496.    {
  1497.    gf_curr |= tcode << gf_valid;        /* add to current word */
  1498.    gf_valid += gf_lzwbits;        /* count the bits */
  1499.    while (gf_valid >= 8) {        /* while we have a byte to output */
  1500.       gf_obuf[GifBlockSize - gf_rem] = gf_curr;    /* put in buffer */
  1501.       gf_curr >>= 8;                /* remove from word */
  1502.       gf_valid -= 8;
  1503.       if (--gf_rem == 0)            /* flush buffer when full */
  1504.          gfdump();
  1505.       }
  1506.    }
  1507.  
  1508. /*
  1509.  * gfdump() - dump output buffer
  1510.  */
  1511. hidden novalue gfdump()
  1512.    {
  1513.    int n;
  1514.  
  1515.    n = GifBlockSize - gf_rem;
  1516.    putc(n, gf_f);            /* write block size */
  1517.    fwrite((pointer)gf_obuf, 1, n, gf_f); /*write block */
  1518.    gf_rem = GifBlockSize;        /* reset buffer to empty */
  1519.    }
  1520. #ifdef ConsoleWindow
  1521. #undef fprintf
  1522. #define fprintf Consolefprintf
  1523. #undef putc
  1524. #define putc Consoleputc
  1525. #endif                    /* ConsoleWindow */
  1526.  
  1527. /*
  1528.  * Static data for XDrawImage and XPalette functions
  1529.  */
  1530.  
  1531. /*
  1532.  * c<n>list - the characters of the palettes that are not contiguous ASCII
  1533.  */
  1534. char c1list[] = "0123456789?!nNAa#@oOBb$%pPCc&|\
  1535. qQDd,.rREe;:sSFf+-tTGg*/uUHh`'vVIi<>wWJj()xXKk[]yYLl{}zZMm^=";
  1536. char c2list[] = "kbgcrmywx";
  1537. char c3list[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZabcd";
  1538. char c4list[] =
  1539.    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz{}$%&*+-/?@";
  1540.  
  1541. /*
  1542.  * cgrays -- lists of grayscales contained within color palettes
  1543.  */
  1544. static char *cgrays[] = { "0123456", "kxw", "@abMcdZ", "0$%&L*+-g/?@}", 
  1545. "\0}~\177\200\37\201\202\203\204>\205\206\207\210]\211\212\213\214|",
  1546. "\0\330\331\332\333\334+\335\336\337\340\341V\342\343\344\345\346\201\
  1547. \347\350\351\352\353\254\354\355\356\357\360\327" };
  1548.  
  1549. /*
  1550.  * c1cube - a precomputed mapping from a color cube to chars in c1 palette
  1551.  *
  1552.  * This is 10x10x10 cube (A Thousand Points of Light).
  1553.  */
  1554. #define C1Side 10              /* length of one side of C1 cube */
  1555. static char c1cube[] = {
  1556.    '0', '0', 'w', 'w', 'w', 'W', 'W', 'W', 'J', 'J', '0', '0', 'v', 'v', 'v',
  1557.    'W', 'W', 'W', 'J', 'J', 's', 't', 't', 'v', 'v', 'V', 'V', 'V', 'V', 'J',
  1558.    's', 't', 't', 'u', 'u', 'V', 'V', 'V', 'V', 'I', 's', 't', 't', 'u', 'u',
  1559.    'V', 'V', 'V', 'I', 'I', 'S', 'S', 'T', 'T', 'T', 'U', 'U', 'U', 'I', 'I',
  1560.    'S', 'S', 'T', 'T', 'T', 'U', 'U', 'U', 'U', 'I', 'S', 'S', 'T', 'T', 'T',
  1561.    'U', 'U', 'U', 'U', 'H', 'F', 'F', 'T', 'T', 'G', 'G', 'U', 'U', 'H', 'H',
  1562.    'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H', 'H', '0', '0', 'x', 'x', 'x',
  1563.    'W', 'W', 'W', 'J', 'J', '!', '1', '1', 'v', 'v', 'W', 'W', 'W', 'J', 'J',
  1564.    'r', '1', '1', 'v', 'v', 'V', 'V', 'V', 'j', 'j', 'r', 'r', 't', 'u', 'u',
  1565.    'V', 'V', 'V', 'j', 'j', 'r', 'r', 't', 'u', 'u', 'V', 'V', 'V', 'I', 'I',
  1566.    'S', 'S', 'T', 'T', 'T', 'U', 'U', 'U', 'I', 'I', 'S', 'S', 'T', 'T', 'T',
  1567.    'U', 'U', 'U', 'i', 'i', 'S', 'S', 'T', 'T', 'T', 'U', 'U', 'U', 'i', 'i',
  1568.    'F', 'F', 'f', 'f', 'G', 'G', 'g', 'g', 'H', 'H', 'F', 'F', 'f', 'f', 'G',
  1569.    'G', 'g', 'g', 'H', 'H', 'n', 'z', 'x', 'x', 'x', 'X', 'X', 'X', 'X', 'J',
  1570.    '!', '1', '1', 'x', 'x', 'X', 'X', 'X', 'j', 'j', 'p', '1', '1', '2', '2',
  1571.    ')', 'V', 'j', 'j', 'j', 'r', 'r', '2', '2', '2', ')', 'V', 'j', 'j', 'j',
  1572.    'r', 'r', '2', '2', '2', '>', '>', '>', 'j', 'j', 'R', 'R', '-', '-', '/',
  1573.    '/', '>', '>', 'i', 'i', 'R', 'R', 'R', 'T', '/', '/', '\'','i', 'i', 'i',
  1574.    'R', 'R', 'f', 'f', '/', '/', 'g', 'g', 'i', 'i', 'R', 'f', 'f', 'f', 'f',
  1575.    'g', 'g', 'g', 'h', 'h', 'F', 'f', 'f', 'f', 'f', 'g', 'g', 'g', 'h', 'h',
  1576.    'n', 'z', 'z', 'y', 'y', 'X', 'X', 'X', 'X', 'K', 'o', 'o', 'z', 'y', 'y',
  1577.    'X', 'X', 'X', 'j', 'j', 'p', 'p', '2', '2', '2', ')', 'X', 'j', 'j', 'j',
  1578.    'q', 'q', '2', '2', '2', ')', ')', 'j', 'j', 'j', 'q', 'q', '2', '2', '2',
  1579.    '>', '>', '>', 'j', 'j', 'R', 'R', '-', '-', '/', '/', '>', '>', 'i', 'i',
  1580.    'R', 'R', 'R', '-', '/', '/', '\'','\'','i', 'i', 'R', 'R', 'f', 'f', '/',
  1581.    '/', '\'','g', 'i', 'i', 'R', 'f', 'f', 'f', 'f', 'g', 'g', 'g', 'h', 'h',
  1582.    'E', 'f', 'f', 'f', 'f', 'g', 'g', 'g', 'h', 'h', 'n', 'z', 'z', 'y', 'y',
  1583.    'X', 'X', 'X', 'K', 'K', 'o', 'o', 'z', 'y', 'y', 'X', 'X', 'X', 'K', 'K',
  1584.    '?', '?', '?', '2', '2', ']', ']', ']', 'j', 'j', 'q', 'q', '2', '2', '2',
  1585.    ']', ']', ']', 'j', 'j', 'q', 'q', '2', '2', '3', '3', '>', '>', 'j', 'j',
  1586.    'R', 'R', ':', ':', '3', '3', '>', '>', 'i', 'i', 'R', 'R', ':', ':', ':',
  1587.    '/', '\'','\'','i', 'i', 'R', 'R', ':', ':', ':', '/', '\'','\'','i', 'i',
  1588.    'E', 'E', 'f', 'f', 'f', 'g', 'g', 'g', 'h', 'h', 'E', 'E', 'f', 'f', 'f',
  1589.    'g', 'g', 'g', 'h', 'h', 'N', 'N', 'Z', 'Z', 'Z', 'Y', 'Y', 'Y', 'K', 'K',
  1590.    'O', 'O', 'Z', 'Z', 'Z', 'Y', 'Y', 'Y', 'K', 'K', '?', '?', '?', '@', '=',
  1591.    ']', ']', ']', 'k', 'k', 'P', 'P', '@', '@', '=', ']', ']', ']', 'k', 'k',
  1592.    'P', 'P', '%', '%', '%', '3', ']', ']', 'k', 'k', 'Q', 'Q', '|', '|', '3',
  1593.    '3', '4', '4', '(', '(', 'Q', 'Q', ':', ':', ':', '4', '4', '4', '(', '(',
  1594.    'Q', 'Q', ':', ':', ':', '4', '4', '4', '<', '<', 'E', 'E', 'e', 'e', 'e',
  1595.    '+', '+', '*', '*', '<', 'E', 'E', 'e', 'e', 'e', '+', '+', '*', '*', '`',
  1596.    'N', 'N', 'Z', 'Z', 'Z', 'Y', 'Y', 'Y', 'Y', 'K', 'O', 'O', 'Z', 'Z', 'Z',
  1597.    'Y', 'Y', 'Y', 'k', 'k', 'O', 'O', 'O', 'Z', '=', '=', '}', 'k', 'k', 'k',
  1598.    'P', 'P', 'P', '@', '=', '=', '}', '}', 'k', 'k', 'P', 'P', '%', '%', '%',
  1599.    '=', '}', '}', 'k', 'k', 'Q', 'Q', '|', '|', '|', '4', '4', '4', '(', '(',
  1600.    'Q', 'Q', '.', '.', '.', '4', '4', '4', '(', '(', 'Q', 'Q', 'e', '.', '.',
  1601.    '4', '4', '4', '<', '<', 'Q', 'e', 'e', 'e', 'e', '+', '+', '*', '*', '<',
  1602.    'E', 'e', 'e', 'e', 'e', '+', '+', '*', '*', '`', 'N', 'N', 'Z', 'Z', 'Z',
  1603.    'Y', 'Y', 'Y', 'Y', 'L', 'O', 'O', 'Z', 'Z', 'Z', 'Y', 'Y', 'Y', 'k', 'k',
  1604.    'O', 'O', 'O', 'a', '=', '=', 'm', 'k', 'k', 'k', 'P', 'P', 'a', 'a', '=',
  1605.    '=', '}', 'k', 'k', 'k', 'P', 'P', '%', '%', '%', '=', '}', '8', '8', '8',
  1606.    'Q', 'Q', '|', '|', '|', '4', '4', '8', '8', '8', 'Q', 'Q', 'c', '.', '.',
  1607.    '4', '4', '4', '[', '[', 'Q', 'Q', 'c', 'c', '9', '9', '4', '5', '5', '<',
  1608.    'Q', 'e', 'e', 'e', 'e', ';', ';', '5', '5', '<', 'D', 'e', 'e', 'e', 'e',
  1609.    ';', ';', ';', '*', '`', 'A', 'A', 'Z', 'Z', 'M', 'M', 'Y', 'Y', 'L', 'L',
  1610.    'A', 'A', 'a', 'a', 'M', 'M', 'm', 'm', 'L', 'L', 'B', 'B', 'a', 'a', 'a',
  1611.    'm', 'm', 'm', 'l', 'l', 'B', 'B', 'a', 'a', 'a', 'm', 'm', 'm', 'l', 'l',
  1612.    'C', 'C', 'b', 'b', 'b', '7', '7', '7', '8', '8', 'C', 'C', 'b', 'b', 'b',
  1613.    '7', '7', '^', '[', '[', 'Q', 'c', 'c', 'c', 'c', '#', '#', '^', '[', '[',
  1614.    'Q', 'c', 'c', 'c', '9', '9', '$', '5', '5', '[', 'D', 'D', 'd', 'd', '9',
  1615.    '&', '&', '5', '5', '6', 'D', 'D', 'd', 'd', 'd', ';', ';', ';', '6', '6',
  1616.    'A', 'A', 'A', 'M', 'M', 'M', 'M', 'L', 'L', 'L', 'A', 'A', 'a', 'a', 'M',
  1617.    'M', 'm', 'm', 'L', 'L', 'B', 'B', 'a', 'a', 'a', 'm', 'm', 'm', 'l', 'l',
  1618.    'B', 'B', 'a', 'a', 'a', 'm', 'm', 'm', 'l', 'l', 'C', 'C', 'b', 'b', 'b',
  1619.    '7', '7', '7', 'l', 'l', 'C', 'C', 'b', 'b', 'b', '7', '7', '^', '^', '{',
  1620.    'C', 'c', 'c', 'c', 'c', '#', '#', '^', '^', '{', 'D', 'c', 'c', 'c', '9',
  1621.    '9', '$', '$', '^', '{', 'D', 'D', 'd', 'd', '9', '&', '&', '&', '6', '6',
  1622.    'D', 'D', 'd', 'd', 'd', ',', ',', ',', '6', '6'
  1623. };
  1624.  
  1625. /*
  1626.  * c1rgb - RGB values for c1 palette entries
  1627.  *
  1628.  * Entry order corresponds to c1list (above).
  1629.  * Each entry gives r,g,b in linear range 0 to 48.
  1630.  */
  1631. static unsigned char c1rgb[] = {
  1632.    0, 0, 0,        /*  0             black        */
  1633.    8, 8, 8,        /*  1   very dark gray        */
  1634.    16, 16, 16,        /*  2        dark gray        */
  1635.    24, 24, 24,        /*  3             gray        */
  1636.    32, 32, 32,        /*  4       light gray        */
  1637.    40, 40, 40,        /*  5  very light gray        */
  1638.    48, 48, 48,        /*  6             white        */
  1639.    48, 24, 30,        /*  7             pink        */
  1640.    36, 24, 48,        /*  8             violet    */
  1641.    48, 36, 24,        /*  9  very light brown        */
  1642.    24, 12, 0,        /*  ?             brown        */
  1643.    8, 4, 0,        /*  !   very dark brown        */
  1644.    16, 0, 0,        /*  n   very dark red        */
  1645.    32, 0, 0,        /*  N        dark red        */
  1646.    48, 0, 0,        /*  A             red        */
  1647.    48, 16, 16,        /*  a       light red        */
  1648.    48, 32, 32,        /*  #  very light red        */
  1649.    30, 18, 18,        /*  @        weak red        */
  1650.    16, 4, 0,        /*  o   very dark orange    */
  1651.    32, 8, 0,        /*  O        dark orange    */
  1652.    48, 12, 0,        /*  B             orange    */
  1653.    48, 24, 16,        /*  b       light orange    */
  1654.    48, 36, 32,        /*  $  very light orange    */
  1655.    30, 21, 18,        /*  %        weak orange    */
  1656.    16, 8, 0,        /*  p   very dark red-yellow    */
  1657.    32, 16, 0,        /*  P        dark red-yellow    */
  1658.    48, 24, 0,        /*  C             red-yellow    */
  1659.    48, 32, 16,        /*  c       light red-yellow    */
  1660.    48, 40, 32,        /*  &  very light red-yellow    */
  1661.    30, 24, 18,        /*  |        weak red-yellow    */
  1662.    16, 16, 0,        /*  q   very dark yellow    */
  1663.    32, 32, 0,        /*  Q        dark yellow    */
  1664.    48, 48, 0,        /*  D             yellow    */
  1665.    48, 48, 16,        /*  d       light yellow    */
  1666.    48, 48, 32,        /*  ,  very light yellow    */
  1667.    30, 30, 18,        /*  .        weak yellow    */
  1668.    8, 16, 0,        /*  r   very dark yellow-green    */
  1669.    16, 32, 0,        /*  R        dark yellow-green    */
  1670.    24, 48, 0,        /*  E             yellow-green    */
  1671.    32, 48, 16,        /*  e       light yellow-green    */
  1672.    40, 48, 32,        /*  ;  very light yellow-green    */
  1673.    24, 30, 18,        /*  :        weak yellow-green    */
  1674.    0, 16, 0,        /*  s   very dark green        */
  1675.    0, 32, 0,        /*  S        dark green        */
  1676.    0, 48, 0,        /*  F             green        */
  1677.    16, 48, 16,        /*  f       light green        */
  1678.    32, 48, 32,        /*  +  very light green        */
  1679.    18, 30, 18,        /*  -        weak green        */
  1680.    0, 16, 8,        /*  t   very dark cyan-green    */
  1681.    0, 32, 16,        /*  T        dark cyan-green    */
  1682.    0, 48, 24,        /*  G             cyan-green    */
  1683.    16, 48, 32,        /*  g       light cyan-green    */
  1684.    32, 48, 40,        /*  *  very light cyan-green    */
  1685.    18, 30, 24,        /*  /        weak cyan-green    */
  1686.    0, 16, 16,        /*  u   very dark cyan        */
  1687.    0, 32, 32,        /*  U        dark cyan        */
  1688.    0, 48, 48,        /*  H             cyan        */
  1689.    16, 48, 48,        /*  h       light cyan        */
  1690.    32, 48, 48,        /*  `  very light cyan        */
  1691.    18, 30, 30,        /*  '        weak cyan        */
  1692.    0, 8, 16,        /*  v   very dark blue-cyan    */
  1693.    0, 16, 32,        /*  V        dark blue-cyan    */
  1694.    0, 24, 48,        /*  I             blue-cyan    */
  1695.    16, 32, 48,        /*  i       light blue-cyan    */
  1696.    32, 40, 48,        /*  <  very light blue-cyan    */
  1697.    18, 24, 30,        /*  >        weak blue-cyan    */
  1698.    0, 0, 16,        /*  w   very dark blue        */
  1699.    0, 0, 32,        /*  W        dark blue        */
  1700.    0, 0, 48,        /*  J             blue        */
  1701.    16, 16, 48,        /*  j       light blue        */
  1702.    32, 32, 48,        /*  (  very light blue        */
  1703.    18, 18, 30,        /*  )        weak blue        */
  1704.    8, 0, 16,        /*  x   very dark purple    */
  1705.    16, 0, 32,        /*  X        dark purple    */
  1706.    24, 0, 48,        /*  K             purple    */
  1707.    32, 16, 48,        /*  k       light purple    */
  1708.    40, 32, 48,        /*  [  very light purple    */
  1709.    24, 18, 30,        /*  ]        weak purple    */
  1710.    16, 0, 16,        /*  y   very dark magenta    */
  1711.    32, 0, 32,        /*  Y        dark magenta    */
  1712.    48, 0, 48,        /*  L             magenta    */
  1713.    48, 16, 48,        /*  l       light magenta    */
  1714.    48, 32, 48,        /*  {  very light magenta    */
  1715.    30, 18, 30,        /*  }        weak magenta    */
  1716.    16, 0, 8,        /*  z   very dark magenta-red    */
  1717.    32, 0, 16,        /*  Z        dark magenta-red    */
  1718.    48, 0, 24,        /*  M             magenta-red    */
  1719.    48, 16, 32,        /*  m       light magenta-red    */
  1720.    48, 32, 40,        /*  ^  very light magenta-red    */
  1721.    30, 18, 24,        /*  =        weak magenta-red    */
  1722.    };
  1723.  
  1724. /*
  1725.  * palnum(d) - return palette number, or 0 if unrecognized.
  1726.  *
  1727.  *    returns +1 ... +6 for "c1" through "c6"
  1728.  *    returns +1 for &null
  1729.  *    returns -2 ... -256 for "g2" through "g256"
  1730.  *    returns 0 for unrecognized palette name
  1731.  *    returns -1 for non-string argument
  1732.  */
  1733. int palnum(d)
  1734. dptr d;
  1735.    {
  1736.    tended char *s;
  1737.    char c, x;
  1738.    int n;
  1739.  
  1740.    if (is:null(*d))
  1741.       return 1;
  1742.    if (!cnv:C_string(*d, s))
  1743.       return -1;
  1744.    if (sscanf(s, "%c%d%c", &c, &n, &x) != 2)
  1745.       return 0;
  1746.    if (c == 'c' && n >= 1 && n <= 6)
  1747.       return n;
  1748.    if (c == 'g' && n >= 2 && n <= 256)
  1749.       return -n;
  1750.    return 0;
  1751.    }
  1752.  
  1753. /*
  1754.  * palsetup(p) - set up palette for specified palette.
  1755.  */
  1756. struct palentry *palsetup(p)
  1757. int p;
  1758.    {
  1759.    int r, g, b, i, n, c;
  1760.    unsigned int rr, gg, bb;
  1761.    unsigned char *s, *t;
  1762.    double m;
  1763.    struct palentry *e;
  1764.  
  1765.    static struct palentry *palette;    /* current palette */
  1766.    static int palnumber;        /* current palette number */
  1767.  
  1768.    if (palnumber == p)
  1769.       return palette;
  1770.    if (palette == NULL) {
  1771.       palette = (struct palentry *)malloc(256 * sizeof(struct palentry));
  1772.       if (palette == NULL)
  1773.          return NULL;
  1774.       }
  1775.    palnumber = p;
  1776.  
  1777.    for (i = 0; i < 256; i++)
  1778.       palette[i].valid = palette[i].transpt = 0;
  1779.    palette[TCH1].transpt = 1;
  1780.    palette[TCH2].transpt = 1;
  1781.    
  1782.    if (p < 0) {                /* grayscale palette */
  1783.       n = -p;
  1784.       if (n <= 64)
  1785.          s = (unsigned char *)c4list;
  1786.       else
  1787.          s = allchars;
  1788.       m = 1.0 / (n - 1);
  1789.  
  1790.       for (i = 0; i < n; i++) {
  1791.          e = &palette[*s++];
  1792.          gg = 65535 * m * i;
  1793.          e->clr.red = e->clr.green = e->clr.blue = gg;
  1794.          e->valid = 1;
  1795.      e->transpt = 0;
  1796.          }
  1797.       return palette;
  1798.       }
  1799.  
  1800.    if (p == 1) {            /* special c1 palette */
  1801.       s = (unsigned char *)c1list;
  1802.       t = c1rgb;
  1803.       while (c = *s++) {
  1804.          e = &palette[c];
  1805.          e->clr.red   = 65535 * (((int)*t++) / 48.0);
  1806.          e->clr.green = 65535 * (((int)*t++) / 48.0);
  1807.          e->clr.blue  = 65535 * (((int)*t++) / 48.0);
  1808.          e->valid = 1;
  1809.      e->transpt = 0;
  1810.          }
  1811.       return palette;
  1812.       }
  1813.  
  1814.    switch (p) {                /* color cube plus extra grays */
  1815.       case  2:  s = (unsigned char *)c2list;    break;    /* c2 */
  1816.       case  3:  s = (unsigned char *)c3list;    break;    /* c3 */
  1817.       case  4:  s = (unsigned char *)c4list;    break;    /* c4 */
  1818.       case  5:  s = allchars;            break;    /* c5 */
  1819.       case  6:  s = allchars;             break;    /* c6 */
  1820.       }
  1821.    m = 1.0 / (p - 1);
  1822.    for (r = 0; r < p; r++) {
  1823.       rr = 65535 * m * r;
  1824.       for (g = 0; g < p; g++) {
  1825.          gg = 65535 * m * g;
  1826.          for (b = 0; b < p; b++) {
  1827.             bb = 65535 * m * b;
  1828.             e = &palette[*s++];
  1829.             e->clr.red = rr;
  1830.             e->clr.green = gg;
  1831.             e->clr.blue = bb;
  1832.             e->valid = 1;
  1833.         e->transpt = 0;
  1834.             }
  1835.          }
  1836.       }
  1837.    m = 1.0 / (p * (p - 1));
  1838.    for (g = 0; g < p * (p - 1); g++)
  1839.       if (g % p != 0) {
  1840.          gg = 65535 * m * g;
  1841.          e = &palette[*s++];
  1842.          e->clr.red = e->clr.green = e->clr.blue = gg;
  1843.          e->valid = 1;
  1844.      e->transpt = 0;
  1845.          }
  1846.    return palette;
  1847.    }
  1848.  
  1849. /*
  1850.  * rgbkey(p,r,g,b) - return pointer to key of closest color in palette number p.
  1851.  *
  1852.  * In color cubes, finds "extra" grays only if r == g == b.
  1853.  */
  1854. char *rgbkey(p, r, g, b)
  1855. int p;
  1856. double r, g, b;
  1857.    {
  1858.    int n, i;
  1859.    double m;
  1860.    char *s;
  1861.  
  1862.    if (p > 0) {             /* color */
  1863.       if (r == g && g == b) {
  1864.          if (p == 1)
  1865.             m = 6;
  1866.          else
  1867.             m = p * (p - 1);
  1868.          return cgrays[p - 1] + (int)(0.501 + m * g);
  1869.          }
  1870.       else {
  1871.          if (p == 1)
  1872.             n = C1Side;
  1873.          else
  1874.             n = p;
  1875.          m = n - 1;
  1876.          i = (int)(0.501 + m * r);
  1877.          i = n * i + (int)(0.501 + m * g);
  1878.          i = n * i + (int)(0.501 + m * b);
  1879.          switch(p) {
  1880.             case  1:  return c1cube + i;        /* c1 */
  1881.             case  2:  return c2list + i;        /* c2 */
  1882.             case  3:  return c3list + i;        /* c3 */
  1883.             case  4:  return c4list + i;        /* c4 */
  1884.             case  5:  return (char *)allchars + i;    /* c5 */
  1885.             case  6:  return (char *)allchars + i;    /* c6 */
  1886.             }
  1887.          }
  1888.       }
  1889.    else {                /* grayscale */
  1890.       if (p < -64)
  1891.          s = (char *)allchars;
  1892.       else
  1893.          s = c4list;
  1894.       return s + (int)(0.5 + (0.299 * r + 0.587 * g + 0.114 * b) * (-p - 1));
  1895.       }
  1896.  
  1897.    }
  1898.  
  1899. /*
  1900.  * mapping from recognized style attributes to flag values
  1901.  */
  1902. stringint fontwords[] = {
  1903.    { 0,            17 },        /* number of entries */
  1904.    { "bold",        FONTATT_WEIGHT    | FONTFLAG_BOLD },
  1905.    { "condensed",    FONTATT_WIDTH    | FONTFLAG_CONDENSED },
  1906.    { "demi",        FONTATT_WEIGHT    | FONTFLAG_DEMI },
  1907.    { "demibold",    FONTATT_WEIGHT    | FONTFLAG_DEMI | FONTFLAG_BOLD },
  1908.    { "extended",    FONTATT_WIDTH    | FONTFLAG_EXTENDED },
  1909.    { "italic",        FONTATT_SLANT    | FONTFLAG_ITALIC },
  1910.    { "light",        FONTATT_WEIGHT    | FONTFLAG_LIGHT },
  1911.    { "medium",        FONTATT_WEIGHT    | FONTFLAG_MEDIUM },
  1912.    { "mono",        FONTATT_SPACING    | FONTFLAG_MONO },
  1913.    { "narrow",        FONTATT_WIDTH    | FONTFLAG_NARROW },
  1914.    { "normal",        FONTATT_WIDTH    | FONTFLAG_NORMAL },
  1915.    { "oblique",        FONTATT_SLANT    | FONTFLAG_OBLIQUE },
  1916.    { "proportional",    FONTATT_SPACING    | FONTFLAG_PROPORTIONAL },
  1917.    { "roman",        FONTATT_SLANT    | FONTFLAG_ROMAN },
  1918.    { "sans",        FONTATT_SERIF    | FONTFLAG_SANS },
  1919.    { "serif",        FONTATT_SERIF    | FONTFLAG_SERIF },
  1920.    { "wide",        FONTATT_WIDTH    | FONTFLAG_WIDE },
  1921. };
  1922.  
  1923. /*
  1924.  * parsefont - extract font family name, style attributes, and size
  1925.  *
  1926.  * these are window system independent values, so they require
  1927.  *  further translation into window system dependent values.
  1928.  *
  1929.  * returns 1 on an OK font name
  1930.  * returns 0 on a "malformed" font (might be a window-system fontname)
  1931.  */
  1932. int parsefont(s, family, style, size)
  1933. char *s;
  1934. char family[MAXFONTWORD+1];
  1935. int *style;
  1936. int *size;
  1937.    {
  1938.    char c, *a, attr[MAXFONTWORD+1];
  1939.    int tmp;
  1940.  
  1941.    /*
  1942.     * set up the defaults
  1943.     */
  1944.    *family = '\0';
  1945.    *style = 0;
  1946.    *size = -1;
  1947.  
  1948.    /*
  1949.     * now, scan through the raw and break out pieces
  1950.     */
  1951.    for (;;) {
  1952.  
  1953.       /*
  1954.        * find start of next comma-separated attribute word
  1955.        */
  1956.       while (isspace(*s) || *s == ',')    /* trim leading spaces & empty words */
  1957.          s++;
  1958.       if (*s == '\0')            /* stop at end of string */
  1959.          break;
  1960.  
  1961.       /*
  1962.        * copy word, converting to lower case to implement case insensitivity
  1963.        */
  1964.       for (a = attr; (c = *s) != '\0' && c != ','; s++) {
  1965.          if (isupper(c))
  1966.             c = tolower(c);
  1967.          *a++ = c;
  1968.          if (a - attr >= MAXFONTWORD)
  1969.             return 0;            /* too long */
  1970.          }
  1971.  
  1972.       /*
  1973.        * trim trailing spaces and terminate word
  1974.        */
  1975.       while (isspace(a[-1]))
  1976.          a--;
  1977.       *a = '\0';
  1978.  
  1979.       /*
  1980.        * interpret word as family name, size, or style characteristic
  1981.        */
  1982.       if (*family == '\0')
  1983.          strcpy(family, attr);        /* first word is the family name */
  1984.  
  1985.       else if (sscanf(attr, "%d%c", &tmp, &c) == 1 && tmp > 0) {
  1986.          if (*size != -1 && *size != tmp)
  1987.             return 0;            /* if conflicting sizes given */
  1988.          *size = tmp;            /* integer value is a size */
  1989.          }
  1990.          
  1991.       else {                /* otherwise it's a style attribute */
  1992.          tmp = si_s2i(fontwords, attr);    /* look up in table */
  1993.          if (tmp != -1) {        /* if recognized */
  1994.             if ((tmp & *style) != 0 && (tmp & *style) != tmp)
  1995.                return 0;        /* conflicting attribute */
  1996.             *style |= tmp;
  1997.             }
  1998.          }
  1999.       }
  2000.  
  2001.    /* got to end of string; it's OK if it had at least a font family */
  2002.    return (*family != '\0');
  2003.    }
  2004.  
  2005. /*
  2006.  * parsepattern() - parse an encoded numeric stipple pattern
  2007.  */
  2008. int parsepattern(s, len, width, nbits, bits)
  2009. char *s;
  2010. int len;
  2011. int *width, *nbits;
  2012. C_integer *bits;
  2013.    {
  2014.    C_integer v;
  2015.    int i, j, hexdigits_per_row, maxbits = *nbits;
  2016.  
  2017.    /*
  2018.     * Get the width
  2019.     */
  2020.    if (sscanf(s, "%d,", width) != 1) return Error;
  2021.    if (*width < 1) return Failed;
  2022.  
  2023.    /*
  2024.     * skip over width
  2025.     */
  2026.    while ((len > 0) && isdigit(*s)) {
  2027.       len--; s++;
  2028.       }
  2029.    if ((len <= 1) || (*s != ',')) return Error;
  2030.    len--; s++;                    /* skip over ',' */
  2031.    
  2032.    if (*s == '#') {
  2033.       /*
  2034.        * get remaining bits as hex constant
  2035.        */
  2036.       s++; len--;
  2037.       if (len == 0) return Error;
  2038.       hexdigits_per_row = *width / 4;
  2039.       if (*width % 4) hexdigits_per_row++;
  2040.       *nbits = len / hexdigits_per_row;
  2041.       if (len % hexdigits_per_row) *nbits++;
  2042.       if (*nbits > maxbits) return Failed;
  2043.       for (i = 0; i < *nbits; i++) {
  2044.          v = 0;
  2045.      for (j = 0; j < hexdigits_per_row; j++, len--, s++) {
  2046.         if (len == 0) break;
  2047.         v <<= 4;
  2048.         if (isdigit(*s)) v += *s - '0';
  2049.         else switch (*s) {
  2050.         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  2051.            v += *s - 'a' + 10; break;
  2052.         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  2053.            v += *s - 'A' + 10; break;
  2054.         default: return Error;
  2055.            }
  2056.         }
  2057.      *bits++ = v;
  2058.      }
  2059.       }
  2060.    else {
  2061.       if (*width > 32) return Failed;
  2062.       /*
  2063.        * get remaining bits as comma-separated decimals
  2064.        */
  2065.       v = 0;
  2066.       *nbits = 0;
  2067.       while (len > 0) {
  2068.      while ((len > 0) && isdigit(*s)) {
  2069.         v = v * 10 + *s - '0';
  2070.         len--; s++;
  2071.         }
  2072.      (*nbits)++;
  2073.      if (*nbits > maxbits) return Failed;
  2074.      *bits++ = v;
  2075.      v = 0;
  2076.  
  2077.      if (len > 0)
  2078.         if (*s == ',') { len--; s++; }
  2079.         else {
  2080.            ReturnErrNum(205, Error);
  2081.            }
  2082.      }
  2083.       }
  2084.    return Succeeded;
  2085.    }
  2086.  
  2087. /*
  2088.  * parsegeometry - parse a string of the form: intxint[+-]int[+-]int
  2089.  * Returns:
  2090.  *  0 on bad value, 1 if size is set, 2 if position is set, 3 if both are set
  2091.  */
  2092. int parsegeometry(buf, x, y, width, height)
  2093. char *buf;
  2094. SHORT *x, *y, *width, *height;
  2095.    {
  2096.    int retval = 0;
  2097.    if (isdigit(*buf)) {
  2098.       retval++;
  2099.       *width = atoi(buf);
  2100.       while (isdigit(*++buf));
  2101.       if (*buf++ != 'x') return 0;
  2102.       if ((*height = atoi(buf)) <= 0) return 0;
  2103.       while (isdigit(*++buf));
  2104.       }
  2105.  
  2106.    if (*buf == '+' || *buf == '-') {
  2107.       retval += 2;
  2108.       *x = atoi(buf);
  2109.       buf++; /* skip over +/- */
  2110.       while (isdigit(*buf)) buf++;
  2111.  
  2112.       if (*buf != '+' && *buf != '-') return 0;
  2113.       *y = atoi(buf);
  2114.       buf++; /* skip over +/- */
  2115.       while (isdigit(*buf)) buf++;
  2116.       if (*buf) return 0;
  2117.       }
  2118.    return retval;
  2119.    }
  2120.  
  2121.  
  2122. /* return failure if operation returns either failure or error */
  2123. #define AttemptAttr(operation) if ((operation) != Succeeded) return Failed;
  2124.  
  2125. /* does string (already checked for "on" or "off") say "on"? */
  2126. #define ATOBOOL(s) (s[1]=='n')
  2127.  
  2128. /*
  2129.  * Attribute manipulation
  2130.  *
  2131.  * wattrib() - get/set a single attribute in a window, return the result attr
  2132.  *  string.
  2133.  */
  2134. int wattrib(w, s, len, answer, abuf)
  2135. wbp w;
  2136. char *s;
  2137. long len;
  2138. dptr answer;
  2139. char * abuf;
  2140.    {
  2141.    char val[128], *valptr;
  2142.    struct descrip d;
  2143.    char *mid, c;
  2144.    int r, a;
  2145.    C_integer tmp;
  2146.    long lenattr, lenval;
  2147.    double gamma;
  2148.    SHORT new_height, new_width;
  2149.    wsp ws = w->window;
  2150.    wcp wc = w->context;
  2151.  
  2152.    valptr = val;
  2153.    /*
  2154.     * catch up on any events pending - mainly to update pointerx, pointery
  2155.     */
  2156.    if (pollevent() == -1)
  2157.       fatalerr(141,NULL);
  2158.  
  2159.    if (mid = strnchr(s, '=', len)) {
  2160.       /*
  2161.        * set an attribute
  2162.        */
  2163.       lenattr = mid - s;
  2164.       lenval  = len - lenattr - 1;
  2165.       mid++;
  2166.  
  2167.       mystrncpy(abuf, s, lenattr);
  2168.       mystrncpy(val, mid, lenval);
  2169.       StrLen(d) = strlen(val);
  2170.       StrLoc(d) = val;
  2171.  
  2172.       switch (a = si_s2i(attribs, abuf)) {
  2173.       case A_LINES: case A_ROWS: {
  2174.      if (!cnv:C_integer(d, tmp))
  2175.         return Failed;
  2176.      if ((new_height = tmp) < 1)
  2177.         return Failed;
  2178.      new_height = ROWTOY(w, new_height);
  2179.      new_height += MAXDESCENDER(w);
  2180.      if (setheight(w, new_height) == Failed) return Failed;
  2181.      break;
  2182.          }
  2183.       case A_COLUMNS: {
  2184.      if (!cnv:C_integer(d, tmp))
  2185.         return Failed;
  2186.      if ((new_width = tmp) < 1)
  2187.         return Failed;
  2188.      new_width = COLTOX(w, new_width + 1);
  2189.      if (setwidth(w, new_width) == Failed) return Failed;
  2190.      break;
  2191.          }
  2192.       case A_HEIGHT: {
  2193.      if (!cnv:C_integer(d, tmp))
  2194.         return Failed;
  2195.          if ((new_height = tmp) < 1) return Failed;
  2196.      if (setheight(w, new_height) == Failed) return Failed;
  2197.      break;
  2198.          }
  2199.       case A_WIDTH: {
  2200.      if (!cnv:C_integer(d, tmp))
  2201.         return Failed;
  2202.          if ((new_width = tmp) < 1) return Failed;
  2203.      if (setwidth(w, new_width) == Failed) return Failed;
  2204.      break;
  2205.          }
  2206.       case A_SIZE: {
  2207.      AttemptAttr(setsize(w, val));
  2208.      break;
  2209.      }
  2210.       case A_GEOMETRY: {
  2211.      AttemptAttr(setgeometry(w, val));
  2212.      break;
  2213.          }
  2214.       case A_RESIZE: {
  2215.      int on_off;
  2216.      if (strcmp(val, "on") & strcmp(val, "off"))
  2217.         return Failed;
  2218.          allowresize(w, ATOBOOL(val));
  2219.      break;
  2220.          }
  2221.       case A_ROW: {
  2222.      if (!cnv:C_integer(d, tmp))
  2223.         return Failed;
  2224.      ws->y = ROWTOY(w, tmp) + wc->dy;
  2225.      break;
  2226.      }
  2227.       case A_COL: {
  2228.      if (!cnv:C_integer(d, tmp))
  2229.         return Failed;
  2230.      ws->x = COLTOX(w, tmp) + wc->dx;
  2231.      break;
  2232.      }
  2233.       case A_CANVAS: { 
  2234.      AttemptAttr(setcanvas(w,val));
  2235.      break; 
  2236.      }
  2237.       case A_ICONIC: { 
  2238.      AttemptAttr(seticonicstate(w,val));
  2239.      break; 
  2240.      }
  2241.       case A_ICONIMAGE: {
  2242.      if (!val[0]) return Failed;
  2243.      AttemptAttr(seticonimage(w, &d));
  2244.          break;
  2245.      }
  2246.       case A_ICONLABEL: { 
  2247.      AttemptAttr(seticonlabel(w, val));
  2248.      break; 
  2249.      }
  2250.       case A_ICONPOS: {
  2251.      AttemptAttr(seticonpos(w,val));
  2252.      break;
  2253.      }
  2254.       case A_LABEL:
  2255.       case A_WINDOWLABEL: {
  2256.      AttemptAttr(setwindowlabel(w, val));
  2257.      break;
  2258.          }
  2259.       case A_CURSOR: {
  2260.      int on_off;
  2261.      if (strcmp(val, "on") & strcmp(val, "off"))
  2262.         return Failed;
  2263.      on_off = ATOBOOL(val);
  2264.          setcursor(w, on_off);
  2265.      break;
  2266.          }
  2267.       case A_FONT: {
  2268.      AttemptAttr(setfont(w, &valptr));
  2269.      break;
  2270.          }
  2271.       case A_PATTERN: {
  2272.      AttemptAttr(SetPattern(w, val, strlen(val)));
  2273.          break;
  2274.      }
  2275.       case A_POS: {
  2276.      AttemptAttr(setpos(w, val));
  2277.      break;
  2278.      }
  2279.       case A_POSX: {
  2280.      char tmp[20];
  2281.      sprintf(tmp,"%s,%d",val,ws->posy);
  2282.      AttemptAttr(setpos(w, tmp));
  2283.      break;
  2284.      }
  2285.       case A_POSY: {
  2286.      char tmp[20];
  2287.      sprintf(tmp,"%d,%s",ws->posx,val);
  2288.      AttemptAttr(setpos(w, tmp));
  2289.          break;
  2290.      }
  2291.       case A_FG: {
  2292.      if (cnv:C_integer(d, tmp) && tmp < 0) {
  2293.         if (isetfg(w, tmp) != Succeeded) return Failed;
  2294.         }
  2295.      else {
  2296.         if (setfg(w, val) != Succeeded) return Failed;
  2297.         }
  2298.      break;
  2299.          }
  2300.       case A_BG: {
  2301.      if (cnv:C_integer(d, tmp) && tmp < 0) {
  2302.         if (isetbg(w, tmp) != Succeeded) return Failed;
  2303.         }
  2304.      else {
  2305.         if (setbg(w, val) != Succeeded) return Failed;
  2306.         }
  2307.      break;
  2308.          }
  2309.       case A_GAMMA: {
  2310.          if (sscanf(val, "%lf%c", &gamma, &c) != 1 || gamma <= 0.0)
  2311.             return Failed;
  2312.          if (setgamma(w, gamma) != Succeeded)
  2313.             return Failed;
  2314.          break;
  2315.          }
  2316.       case A_FILLSTYLE: {
  2317.      AttemptAttr(setfillstyle(w, val));
  2318.      break;
  2319.      }
  2320.       case A_LINESTYLE: {
  2321.      AttemptAttr(setlinestyle(w, val));
  2322.      break;
  2323.      }
  2324.       case A_LINEWIDTH: {
  2325.      if (!cnv:C_integer(d, tmp))
  2326.         return Failed;
  2327.      if (setlinewidth(w, tmp) == Error)
  2328.         return Failed;
  2329.      break;
  2330.      }
  2331.       case A_POINTER: {
  2332.      AttemptAttr(setpointer(w, val));
  2333.      break;
  2334.          }
  2335.       case A_DRAWOP: {
  2336.      AttemptAttr(setdrawop(w, val));
  2337.      break;
  2338.          }
  2339.       case A_DISPLAY: {
  2340.      AttemptAttr(setdisplay(w,val));
  2341.      break;
  2342.          }
  2343.       case A_X: {
  2344.      if (!cnv:C_integer(d, tmp))
  2345.         return Failed;
  2346.      ws->x = tmp + wc->dx;
  2347.          UpdateCursorPos(ws, wc);    /* tell system where to blink it */
  2348.      break;
  2349.      }
  2350.       case A_Y: {
  2351.      if (!cnv:C_integer(d, tmp))
  2352.         return Failed;
  2353.      ws->y = tmp + wc->dy;
  2354.          UpdateCursorPos(ws, wc);    /* tell system where to blink it */
  2355.      break;
  2356.      }
  2357.       case A_DX: {
  2358.      if (!cnv:C_integer(d, tmp))
  2359.         return Failed;
  2360.      wc->dx = tmp;
  2361.          UpdateCursorPos(ws, wc);    /* tell system where to blink it */
  2362.      break;
  2363.      }
  2364.       case A_DY: {
  2365.      if (!cnv:C_integer(d, tmp))
  2366.         return Failed;
  2367.      wc->dy = tmp;
  2368.          UpdateCursorPos(ws, wc);    /* tell system where to blink it */
  2369.      break;
  2370.      }
  2371.       case A_LEADING: { 
  2372.      if (!cnv:C_integer(d, tmp))
  2373.         return Failed;
  2374.      setleading(w, tmp);
  2375.          break;
  2376.          }
  2377.       case A_IMAGE: {
  2378.          /* first try GIF; then try platform-dependent format */
  2379.          r = readGIF(val, 0, &ws->initimage);
  2380.          if (r == Succeeded) {
  2381.             setwidth(w, ws->initimage.width);
  2382.             setheight(w, ws->initimage.height);
  2383.             }
  2384.          else
  2385.             r = setimage(w, val);
  2386.      AttemptAttr(r);
  2387.          break;
  2388.          }
  2389.       case A_ECHO: {
  2390.      if (strcmp(val, "on") & strcmp(val, "off"))
  2391.         return Failed;
  2392.      if (ATOBOOL(val)) SETECHOON(w);
  2393.      else CLRECHOON(w);
  2394.      break;
  2395.          }
  2396.       case A_CLIPX:
  2397.       case A_CLIPY:
  2398.       case A_CLIPW:
  2399.       case A_CLIPH: {
  2400.      if (!*val) {
  2401.         wc->clipx = wc->clipy = 0;
  2402.         wc->clipw = wc->cliph = -1;
  2403.         unsetclip(w);
  2404.         }
  2405.      else {
  2406.         if (!cnv:C_integer(d, tmp))
  2407.            return Failed;
  2408.         if (wc->clipw < 0) {
  2409.            wc->clipx = wc->clipy = 0;
  2410.            wc->clipw = ws->width;
  2411.            wc->cliph = ws->height;
  2412.            }
  2413.         switch (a) {
  2414.            case A_CLIPX:  wc->clipx = tmp;  break;
  2415.            case A_CLIPY:  wc->clipy = tmp;  break;
  2416.            case A_CLIPW:  wc->clipw = tmp;  break;
  2417.            case A_CLIPH:  wc->cliph = tmp;  break;
  2418.            }
  2419.         setclip(w);
  2420.         }
  2421.      break;
  2422.      }
  2423.       case A_REVERSE: {
  2424.      if (strcmp(val, "on") & strcmp(val, "off"))
  2425.         return Failed;
  2426.      if ((!ATOBOOL(val) && ISREVERSE(w)) ||
  2427.          (ATOBOOL(val) && !ISREVERSE(w))) {
  2428.         toggle_fgbg(w);
  2429.         ISREVERSE(w) ? CLRREVERSE(w) : SETREVERSE(w);
  2430.         }
  2431.      break;
  2432.          }
  2433.       case A_POINTERX: {
  2434.      if (!cnv:C_integer(d, tmp))
  2435.         return Failed;
  2436.      ws->pointerx = tmp + wc->dx;
  2437.      warpPointer(w, ws->pointerx, ws->pointery);
  2438.      break;
  2439.      }
  2440.       case A_POINTERY: {
  2441.      if (!cnv:C_integer(d, tmp))
  2442.         return Failed;
  2443.      ws->pointery = tmp + wc->dy;
  2444.      warpPointer(w, ws->pointerx, ws->pointery);
  2445.      break;
  2446.      }
  2447.       case A_POINTERCOL: {
  2448.      if (!cnv:C_integer(d, tmp))
  2449.         return Failed;
  2450.      ws->pointerx = COLTOX(w, tmp) + wc->dx;
  2451.      warpPointer(w, ws->pointerx, ws->pointery);
  2452.      break;
  2453.      }
  2454.       case A_POINTERROW: {
  2455.      if (!cnv:C_integer(d, tmp))
  2456.         return Failed;
  2457.      ws->pointery = ROWTOY(w, tmp) + wc->dy;
  2458.      warpPointer(w, ws->pointerx, ws->pointery);
  2459.      break;
  2460.      }
  2461.       default:
  2462.      ReturnErrNum(145, Error);
  2463.      }
  2464.       mystrncpy(abuf, s, len);
  2465.       }
  2466.    else {
  2467.       int a;
  2468.       /*
  2469.        * get an attribute
  2470.        */
  2471.       mystrncpy(abuf, s, len);
  2472.       switch (a=si_s2i(attribs, abuf)) {
  2473.       case A_VISUAL:
  2474.      if (getvisual(w, abuf) == Failed) return Failed;
  2475.      MakeStr(abuf, strlen(abuf), answer);
  2476.      break;
  2477.       case A_DEPTH:
  2478.      MakeInt(SCREENDEPTH(w), answer);
  2479.      break;
  2480.       case A_DISPLAY:
  2481.      getdisplay(w, abuf);
  2482.      MakeStr(abuf, strlen(abuf), answer);
  2483.      break;
  2484.       case A_ASCENT:
  2485.      MakeInt(ASCENT(w), answer);
  2486.      break;
  2487.       case A_DESCENT:
  2488.      MakeInt(DESCENT(w), answer);
  2489.      break;
  2490.       case A_FHEIGHT:
  2491.      MakeInt(FHEIGHT(w), answer);
  2492.      break;
  2493.       case A_FWIDTH:
  2494.      MakeInt(FWIDTH(w), answer);
  2495.      break;
  2496.       case A_ROW:
  2497.      MakeInt(YTOROW(w, ws->y - wc->dy), answer);
  2498.      break;
  2499.       case A_COL:
  2500.      MakeInt(1 + XTOCOL(w, ws->x - wc->dx), answer);
  2501.      break;
  2502.       case A_POINTERROW: {
  2503.      XPoint xp;
  2504.      query_pointer(w, &xp);
  2505.      MakeInt(YTOROW(w, xp.y - wc->dy), answer);
  2506.      break;
  2507.      }
  2508.       case A_POINTERCOL: {
  2509.      XPoint xp;
  2510.      query_pointer(w, &xp);
  2511.      MakeInt(1 + XTOCOL(w, xp.x - wc->dx), answer);
  2512.      break;
  2513.      }
  2514.       case A_LINES:
  2515.       case A_ROWS:
  2516.      MakeInt(YTOROW(w,ws->height - DESCENT(w)), answer);
  2517.      break;
  2518.       case A_COLUMNS:
  2519.      MakeInt(XTOCOL(w,ws->width), answer);
  2520.      break;
  2521.       case A_POS: case A_POSX: case A_POSY:
  2522.      if (getpos(w) == Failed) return Failed;
  2523.      switch (a) {
  2524.      case A_POS:
  2525.         sprintf(abuf, "%d,%d", ws->posx, ws->posy);
  2526.         MakeStr(abuf, strlen(abuf), answer);
  2527.         break;
  2528.      case A_POSX:
  2529.         MakeInt(ws->posx, answer);
  2530.         break;
  2531.      case A_POSY:
  2532.         MakeInt(ws->posy, answer);
  2533.         break;
  2534.         }
  2535.      break;
  2536.       case A_FG:
  2537.      getfg(w, abuf);
  2538.      MakeStr(abuf, strlen(abuf), answer);
  2539.      break; 
  2540.       case A_BG:
  2541.      getbg(w, abuf);
  2542.      MakeStr(abuf, strlen(abuf), answer);
  2543.          break; 
  2544.       case A_GAMMA:
  2545.          Protect(BlkLoc(*answer) = (union block *)alcreal(wc->gamma),
  2546.             return Error);
  2547.          answer->dword = D_Real;
  2548.          break;
  2549.       case A_FILLSTYLE:
  2550.          sprintf(abuf, "%s",
  2551.             (wc->fillstyle == FS_SOLID) ? "solid" :
  2552.             (wc->fillstyle == FS_STIPPLE) ? "masked" : "textured");
  2553.      MakeStr(abuf, strlen(abuf), answer);
  2554.      break;
  2555.       case A_LINESTYLE:
  2556.      getlinestyle(w, abuf);
  2557.      MakeStr(abuf, strlen(abuf), answer);
  2558.      break;
  2559.       case A_LINEWIDTH:
  2560.      MakeInt(LINEWIDTH(w), answer);
  2561.      break;
  2562.       case A_HEIGHT: { MakeInt(ws->height, answer); break; }
  2563.       case A_WIDTH: { MakeInt(ws->width, answer); break; }
  2564.       case A_SIZE:
  2565.      sprintf(abuf, "%d,%d", ws->width, ws->height);
  2566.      MakeStr(abuf, strlen(abuf), answer);
  2567.      break;
  2568.       case A_RESIZE:
  2569.      sprintf(abuf,"%s",(ISRESIZABLE(w)?"on":"off"));
  2570.      MakeStr(abuf, strlen(abuf), answer);
  2571.      break;
  2572.       case A_DISPLAYHEIGHT:
  2573.      MakeInt(DISPLAYHEIGHT(w), answer);
  2574.      break;
  2575.       case A_DISPLAYWIDTH:
  2576.      MakeInt(DISPLAYWIDTH(w), answer);
  2577.      break;
  2578.       case A_CURSOR:
  2579.      sprintf(abuf,"%s",(ISCURSORON(w)?"on":"off"));
  2580.      MakeStr(abuf, strlen(abuf), answer);
  2581.      break;
  2582.       case A_ECHO:
  2583.      sprintf(abuf,"%s",(ISECHOON(w)?"on":"off"));
  2584.      MakeStr(abuf, strlen(abuf), answer);
  2585.      break;
  2586.       case A_REVERSE:
  2587.      sprintf(abuf,"%s",(ISREVERSE(w)?"on":"off"));
  2588.      MakeStr(abuf, strlen(abuf), answer);
  2589.      break;
  2590.       case A_FONT:
  2591.      getfntnam(w, abuf);
  2592.      MakeStr(abuf, strlen(abuf), answer);
  2593.      break; 
  2594.       case A_X:  MakeInt(ws->x - wc->dx, answer); break;
  2595.       case A_Y:  MakeInt(ws->y - wc->dy, answer); break;
  2596.       case A_DX: MakeInt(wc->dx, answer); break;
  2597.       case A_DY: MakeInt(wc->dy, answer); break;
  2598.       case A_LEADING: MakeInt(LEADING(w), answer); break;
  2599.       case A_POINTERX: {
  2600.      XPoint xp;
  2601.      query_pointer(w, &xp);
  2602.      MakeInt(xp.x - wc->dx, answer);
  2603.      break;
  2604.      }
  2605.       case A_POINTERY: {
  2606.      XPoint xp;
  2607.      query_pointer(w, &xp);
  2608.      MakeInt(xp.y - wc->dy, answer);
  2609.      break;
  2610.      }
  2611.       case A_POINTER:
  2612.      getpointername(w, abuf);
  2613.      MakeStr(abuf, strlen(abuf), answer);
  2614.      break;
  2615.       case A_DRAWOP:
  2616.      getdrawop(w, abuf);
  2617.      MakeStr(abuf, strlen(abuf), answer);
  2618.      break;
  2619.       case A_GEOMETRY:
  2620.      if (getpos(w) == Failed) return Failed;
  2621. #if MACINTOSH
  2622.          if (ws->theWindow)
  2623. #else
  2624.          if (ws->win)
  2625. #endif                        /* MACINTOSH */
  2626.            sprintf(abuf, "%dx%d+%d+%d",
  2627.            ws->width, ws->height, ws->posx, ws->posy);
  2628.          else
  2629.            sprintf(abuf, "%dx%d", ws->pixwidth, ws->pixheight);
  2630.      MakeStr(abuf, strlen(abuf), answer);
  2631.      break;
  2632.       case A_CANVAS:
  2633.      getcanvas(w, abuf);
  2634.      MakeStr(abuf, strlen(abuf), answer);
  2635.      break;
  2636.       case A_ICONIC:
  2637.      geticonic(w, abuf);
  2638.      MakeStr(abuf, strlen(abuf), answer);
  2639.      break;
  2640.       case A_ICONIMAGE:
  2641.      if (ICONFILENAME(w) != NULL)
  2642.         sprintf(abuf, "%s", ICONFILENAME(w));
  2643.      else return Failed;
  2644.      MakeStr(abuf, strlen(abuf), answer);
  2645.      break;
  2646.       case A_ICONLABEL:
  2647.      if (ICONLABEL(w) != NULL)
  2648.         sprintf(abuf, "%s", ICONLABEL(w));
  2649.      else return Failed;
  2650.      MakeStr(abuf, strlen(abuf), answer);
  2651.      break;
  2652.       case A_LABEL:
  2653.       case A_WINDOWLABEL:
  2654.      if (WINDOWLABEL(w) != NULL)
  2655.         sprintf(abuf,"%s", WINDOWLABEL(w));
  2656.      else return Failed;
  2657.      MakeStr(abuf, strlen(abuf), answer);
  2658.      break;
  2659.       case A_ICONPOS: {
  2660.      switch (geticonpos(w,abuf)) {
  2661.         case Failed: return Failed;
  2662.         case Error:  return Failed;
  2663.         }
  2664.      MakeStr(abuf, strlen(abuf), answer);
  2665.      break;
  2666.      }
  2667.       case A_PATTERN: {
  2668.      s = w->context->patternname;
  2669.      if (s != NULL)
  2670.         MakeStr(s, strlen(s), answer);
  2671.      else
  2672.         MakeStr("black", 5, answer);
  2673.      break;
  2674.      }
  2675.       case A_CLIPX:
  2676.      if (wc->clipw >= 0)
  2677.         MakeInt(wc->clipx, answer);
  2678.      else
  2679.         *answer = nulldesc;
  2680.      break;
  2681.       case A_CLIPY:
  2682.      if (wc->clipw >= 0)
  2683.         MakeInt(wc->clipy, answer);
  2684.      else
  2685.         *answer = nulldesc;
  2686.      break;
  2687.       case A_CLIPW:
  2688.      if (wc->clipw >= 0)
  2689.         MakeInt(wc->clipw, answer);
  2690.      else
  2691.         *answer = nulldesc;
  2692.      break;
  2693.       case A_CLIPH:
  2694.      if (wc->clipw >= 0)
  2695.         MakeInt(wc->cliph, answer);
  2696.      else
  2697.         *answer = nulldesc;
  2698.      break;
  2699.       default:
  2700.      ReturnErrNum(145, Error);
  2701.      }
  2702.    }
  2703.    wflush(w);
  2704.    return Succeeded;
  2705.    }
  2706.  
  2707. /*
  2708.  * rectargs -- interpret rectangle arguments uniformly
  2709.  *
  2710.  *  Given an arglist and the index of the next x value, rectargs sets
  2711.  *  x/y/width/height to explicit or defaulted values.  These result values
  2712.  *  are in canonical form:  Width and height are nonnegative and x and y
  2713.  *  have been corrected by dx and dy.
  2714.  *
  2715.  *  Returns index of bad argument, if any, or -1 for success.
  2716.  */
  2717. int rectargs(w, argc, argv, i, px, py, pw, ph)
  2718. wbp w;
  2719. int argc;
  2720. dptr argv;
  2721. int i;
  2722. C_integer *px, *py, *pw, *ph;
  2723.    {
  2724.    int defw, defh;
  2725.    wcp wc = w->context;
  2726.    wsp ws = w->window;
  2727.  
  2728.    /*
  2729.     * Get x and y, defaulting to -dx and -dy.
  2730.     */
  2731.    if (i >= argc)
  2732.       *px = -wc->dx;
  2733.    else if (!def:C_integer(argv[i], -wc->dx, *px))
  2734.       return i;
  2735.  
  2736.    if (++i >= argc)
  2737.       *py = -wc->dy;
  2738.    else if (!def:C_integer(argv[i], -wc->dy, *py))
  2739.       return i;
  2740.  
  2741.    *px += wc->dx;
  2742.    *py += wc->dy;
  2743.  
  2744.    /*
  2745.     * Get w and h, defaulting to extend to the edge
  2746.     */
  2747.    defw = ws->width - *px;
  2748.    defh = ws->height - *py;
  2749.  
  2750.    if (++i >= argc)
  2751.       *pw = defw;
  2752.    else if (!def:C_integer(argv[i], defw, *pw))
  2753.       return i;
  2754.  
  2755.    if (++i >= argc)
  2756.       *ph = defh;
  2757.    else if (!def:C_integer(argv[i], defh, *ph))
  2758.       return i;
  2759.  
  2760.    /*
  2761.     * Correct negative w/h values.
  2762.     */
  2763.    if (*pw < 0)
  2764.       *px -= (*pw = -*pw);
  2765.    if (*ph < 0)
  2766.       *py -= (*ph = -*ph);
  2767.  
  2768.    return -1;
  2769.    }
  2770.  
  2771. /*
  2772.  * docircles -- draw or file circles.
  2773.  *
  2774.  *  Helper for DrawCircle and FillCircle.
  2775.  *  Returns index of bad argument, or -1 for success.
  2776.  */
  2777. int docircles(w, argc, argv, fill)
  2778. wbp w;
  2779. int argc;
  2780. dptr argv;
  2781. int fill;
  2782.    {
  2783.    XArc arc;
  2784.    int i, dx, dy;
  2785.    double x, y, r, theta, alpha;
  2786.  
  2787.    dx = w->context->dx;
  2788.    dy = w->context->dy;
  2789.  
  2790.    for (i = 0; i < argc; i += 5) {    /* for each set of five args */
  2791.  
  2792.       /*
  2793.        * Collect arguments.
  2794.        */
  2795.       if (i + 2 >= argc)
  2796.          return i + 2;            /* missing y or r */
  2797.       if (!cnv:C_double(argv[i], x))
  2798.          return i;
  2799.       if (!cnv:C_double(argv[i + 1], y))
  2800.          return i + 1;
  2801.       if (!cnv:C_double(argv[i + 2], r))
  2802.          return i + 2;
  2803.       if (i + 3 >= argc)
  2804.          theta = 0.0;
  2805.       else if (!def:C_double(argv[i + 3], 0.0, theta))
  2806.          return i + 3;
  2807.       if (i + 4 >= argc)
  2808.          alpha = 2 * Pi;
  2809.       else if (!def:C_double(argv[i + 4], 2 * Pi, alpha))
  2810.          return i + 4;
  2811.  
  2812.       /*
  2813.        * Put in canonical form: r >= 0, -2*pi <= theta < 0, alpha >= 0.
  2814.        */
  2815.       if (r < 0) {            /* ensure positive radius */
  2816.          r = -r;
  2817.          theta += Pi;
  2818.          }
  2819.       if (alpha < 0) {            /* ensure positive extent */
  2820.          theta += alpha;
  2821.          alpha = -alpha;
  2822.          }
  2823.  
  2824.       theta = fmod(theta, 2 * Pi);
  2825.       if (theta > 0)            /* normalize initial angle */
  2826.          theta -= 2 * Pi;
  2827.  
  2828.       /*
  2829.        * Build the Arc descriptor.
  2830.        */
  2831.       arc.x = x + dx - r;
  2832.       arc.y = y + dy - r;
  2833.       ARCWIDTH(arc) = 2 * r;
  2834.       ARCHEIGHT(arc) = 2 * r;
  2835.  
  2836.       arc.angle1 = ANGLE(theta);
  2837.       if (alpha >= 2 * Pi)
  2838.          arc.angle2 = EXTENT(2 * Pi);
  2839.       else
  2840.          arc.angle2 = EXTENT(alpha);
  2841.  
  2842.       /*
  2843.        * Draw or fill the arc.
  2844.        */
  2845.       if (fill) {            /* {} required due to form of macros */
  2846.          fillarcs(w, &arc, 1);
  2847.          }
  2848.       else {
  2849.          drawarcs(w, &arc, 1);
  2850.          }
  2851.       }
  2852.    return -1;
  2853.    }
  2854.  
  2855.  
  2856. /*
  2857.  * genCurve - draw a smooth curve through a set of points.  Algorithm from
  2858.  *  Barry, Phillip J., and Goldman, Ronald N. (1988).
  2859.  *  A Recursive Evaluation Algorithm for a class of Catmull-Rom Splines.
  2860.  *  Computer Graphics 22(4), 199-204.
  2861.  */
  2862. novalue genCurve(w, p, n, helper)
  2863. wbp w;
  2864. XPoint *p;
  2865. int n;
  2866. void (*helper)    Params((wbp, XPoint [], int));
  2867.    {
  2868.    int    i, j, steps;
  2869.    float  ax, ay, bx, by, stepsize, stepsize2, stepsize3;
  2870.    float  x, dx, d2x, d3x, y, dy, d2y, d3y;
  2871.    XPoint *thepoints = NULL;
  2872.    long npoints = 0;
  2873.  
  2874.    for (i = 3; i < n; i++) {
  2875.       /*
  2876.        * build the coefficients ax, ay, bx and by, using:
  2877.        *                             _              _   _    _
  2878.        *   i                 i    1 | -1   3  -3   1 | | Pi-3 |
  2879.        *  Q (t) = T * M   * G   = - |  2  -5   4  -1 | | Pi-2 |
  2880.        *               CR    Bs   2 | -1   0   1   0 | | Pi-1 |
  2881.        *                            |_ 0   2   0   0_| |_Pi  _|
  2882.        */
  2883.  
  2884.       ax = p[i].x - 3 * p[i-1].x + 3 * p[i-2].x - p[i-3].x;
  2885.       ay = p[i].y - 3 * p[i-1].y + 3 * p[i-2].y - p[i-3].y;
  2886.       bx = 2 * p[i-3].x - 5 * p[i-2].x + 4 * p[i-1].x - p[i].x;
  2887.       by = 2 * p[i-3].y - 5 * p[i-2].y + 4 * p[i-1].y - p[i].y;
  2888.  
  2889.       /*
  2890.        * calculate the forward differences for the function using
  2891.        * intervals of size 0.1
  2892.        */
  2893. #ifndef abs
  2894. #define abs(x) ((x)<0?-(x):(x))
  2895. #endif
  2896. #ifndef max
  2897. #define max(x,y) ((x>y)?x:y)
  2898. #endif
  2899.  
  2900. #if VMS
  2901.       {
  2902.       int tmp1 = abs(p[i-1].x - p[i-2].x);
  2903.       int tmp2 = abs(p[i-1].y - p[i-2].y);
  2904.       steps = max(tmp1, tmp2) + 10;
  2905.       }
  2906. #else                        /* VMS */
  2907.       steps = max(abs(p[i-1].x - p[i-2].x), abs(p[i-1].y - p[i-2].y)) + 10;
  2908. #endif                        /* VMS */
  2909.  
  2910.       if (steps+4 > npoints) {
  2911.          if (thepoints != NULL) free(thepoints);
  2912.      thepoints = (XPoint *)malloc((steps+4) * sizeof(XPoint));
  2913.      npoints = steps+4;
  2914.          }      
  2915.  
  2916.       stepsize = 1.0/steps;
  2917.       stepsize2 = stepsize * stepsize;
  2918.       stepsize3 = stepsize * stepsize2;
  2919.  
  2920.       x = thepoints[0].x = p[i-2].x;
  2921.       y = thepoints[0].y = p[i-2].y;
  2922.       dx = (stepsize3*0.5)*ax + (stepsize2*0.5)*bx + (stepsize*0.5)*(p[i-1].x-p[i-3].x);
  2923.       dy = (stepsize3*0.5)*ay + (stepsize2*0.5)*by + (stepsize*0.5)*(p[i-1].y-p[i-3].y);
  2924.       d2x = (stepsize3*3) * ax + stepsize2 * bx;
  2925.       d2y = (stepsize3*3) * ay + stepsize2 * by;
  2926.       d3x = (stepsize3*3) * ax;
  2927.       d3y = (stepsize3*3) * ay;
  2928.  
  2929.       /* calculate the points for drawing the curve */
  2930.  
  2931.       for (j = 0; j < steps; j++) {
  2932.      x = x + dx;
  2933.      y = y + dy;
  2934.      dx = dx + d2x;
  2935.      dy = dy + d2y;
  2936.      d2x = d2x + d3x;
  2937.      d2y = d2y + d3y;
  2938.          thepoints[j + 1].x = (int)x;
  2939.          thepoints[j + 1].y = (int)y;
  2940.          }
  2941.       helper(w, thepoints, steps + 1);
  2942.       }
  2943.    if (thepoints != NULL) {
  2944.       free(thepoints);
  2945.       thepoints = NULL;
  2946.       }
  2947.    }
  2948.  
  2949. static void curveHelper(w, thepoints, n)
  2950. wbp w;
  2951. XPoint thepoints[];
  2952. int n;
  2953.    {
  2954.    /*
  2955.     * Could use drawpoints(w, thepoints, n)
  2956.     *  but that ignores the linewidth and linestyle attributes...
  2957.     * Might make linestyle work a little better by "compressing" straight
  2958.     *  sections produced by genCurve into single drawline points.
  2959.     */
  2960.    drawlines(w, thepoints, n);
  2961.    }
  2962.  
  2963. /*
  2964.  * draw a smooth curve through the array of points
  2965.  */
  2966. novalue drawCurve(w, p, n)
  2967. wbp w;
  2968. XPoint *p;
  2969. int n;
  2970.    {
  2971.    genCurve(w, p, n, curveHelper);
  2972.    }
  2973.  
  2974. #ifdef ConsoleWindow
  2975. void wattr(FILE *w, char *s, int len)
  2976. {
  2977.    struct descrip answer;
  2978.    wattrib((wbp)w, s, len, &answer, s);
  2979. }
  2980.  
  2981. void waitkey(FILE *w)
  2982. {
  2983.    struct descrip answer;
  2984. #if BORLAND_286
  2985.     while(kbhit()) ;
  2986.     getch();
  2987. #else
  2988.    /* throw away pending events */
  2989.    while (BlkLoc(((wbp)w)->window->listp)->list.size > 0) {
  2990.       wgetevent((wbp)w, &answer);
  2991.       }
  2992.    /* wait for an event */
  2993.    wgetevent((wbp)w, &answer);
  2994. #endif
  2995. }
  2996.  
  2997. FILE *flog;
  2998.  
  2999. /*
  3000.  * OpenConsole
  3001.  */
  3002. FILE *OpenConsole()
  3003.    {
  3004.    struct descrip attrs[4];
  3005.    int eindx;
  3006.  
  3007.    if (!ConsoleBinding) {
  3008. #if BORLAND_286
  3009.       _InitEasyWin();
  3010.       ConsoleBinding = stdout;
  3011. #else                    /* BORLAND_286 */
  3012.       char ConsoleTitle[256];
  3013.       tended struct b_list *hp;
  3014.       tended struct b_lelem *bp;
  3015.  
  3016.       /*
  3017.        * If we haven't already allocated regions, we are called due to a
  3018.        *  failure during program startup; allocate enough memory to
  3019.        *  get the message out.
  3020.        */
  3021.       if (!curblock) {
  3022.          curstring = (struct region *)malloc(sizeof (struct region));
  3023.          curblock = (struct region *)malloc(sizeof (struct region));
  3024.          curstring->size = MaxStrSpace;
  3025.          curblock->size  = MaxAbrSize;
  3026.          initalloc(1000);
  3027.          }
  3028.  
  3029.       /*
  3030.        * allocate an empty event queue
  3031.        */
  3032.       hp = alclist(0);
  3033.       bp = alclstb(MinListSlots, (word)0, 0);
  3034.       hp->listhead = hp->listtail = (union block *)bp;
  3035.  
  3036.       /*
  3037.        * build the attribute list
  3038.        */
  3039.       StrLoc(attrs[0]) = "cursor=on";
  3040.       StrLen(attrs[0]) = strlen("cursor=on");
  3041.       StrLoc(attrs[1]) = "rows=24";
  3042.       StrLen(attrs[1]) = strlen("rows=24");
  3043.       StrLoc(attrs[2]) = "columns=80";
  3044.       StrLen(attrs[2]) = strlen("columns=80");
  3045.       /*
  3046.        * enable these last two (by telling wopen it has 4 args)
  3047.        * if you want white text on black bg
  3048.        */
  3049.       StrLoc(attrs[3]) = "reverse=on";
  3050.       StrLen(attrs[3]) = strlen("reverse=on");
  3051.  
  3052.       mystrncpy(ConsoleTitle, StrLoc(kywd_prog), StrLen(kywd_prog));
  3053.       strcat(ConsoleTitle, " - console");
  3054.       ConsoleBinding = wopen(ConsoleTitle, hp, attrs, 3, &eindx);
  3055. #endif                    /* BORLAND_286 */
  3056.       }
  3057.    return ConsoleBinding;
  3058.    }
  3059.  
  3060. /*
  3061.  * Consolefprintf - fprintf to the Icon console
  3062.  */
  3063. int Consolefprintf(FILE *file, char *format, ...)
  3064.    {
  3065.    va_list list;
  3066.    int retval = -1;
  3067.    wbp console;
  3068.  
  3069.    va_start(list, format);
  3070.  
  3071.    if (ConsoleFlags & OutputToBuf) {
  3072.      retval = vsprintf(ConsoleStringBufPtr, format, list);
  3073.      ConsoleStringBufPtr += max(retval, 0);
  3074.      }
  3075.    else if ((file == stdout && !(ConsoleFlags & StdOutRedirect)) ||
  3076.        (file == stderr && !(ConsoleFlags & StdErrRedirect))) {
  3077.       console = (wbp)OpenConsole();
  3078.       if (console == NULL) return 0;
  3079.       if ((retval = vsprintf(ConsoleStringBuf, format, list)) > 0) {
  3080.         int len = strlen(ConsoleStringBuf);
  3081.         if (flog != NULL) {
  3082.            int i;
  3083.        for(i=0;i<len;i++) fputc(ConsoleStringBuf[i], flog);
  3084.        }
  3085. #if BORLAND_286
  3086.         ConsoleStringBuf[len] = '\0';
  3087.         retval = fputs(ConsoleStringBuf, stdout);
  3088. #else                    /* BORLAND_286 */
  3089.         wputstr(console, ConsoleStringBuf, len);
  3090. #endif                    /* BORLAND_286 */
  3091.     }
  3092.       }
  3093.    else 
  3094.       retval = vfprintf(file, format, list);
  3095.    va_end(list);
  3096.    return retval;
  3097.    }
  3098.  
  3099. int Consoleprintf(char *format, ...)
  3100.    {
  3101.    va_list list;
  3102.    int retval = -1;
  3103.    wbp console;
  3104.    FILE *file = stdout;
  3105.  
  3106.    va_start(list, format);
  3107.  
  3108.    if (ConsoleFlags & OutputToBuf) {
  3109.      retval = vsprintf(ConsoleStringBufPtr, format, list);
  3110.      ConsoleStringBufPtr += max(retval, 0);
  3111.      }
  3112.    else if (!(ConsoleFlags & StdOutRedirect)) {
  3113.       console = (wbp)OpenConsole();
  3114.       if (console == NULL) return 0;
  3115.       if ((retval = vsprintf(ConsoleStringBuf, format, list)) > 0) {
  3116.         int len = strlen(ConsoleStringBuf);
  3117.         if (flog != NULL) {
  3118.            int i;
  3119.        for(i=0;i<len;i++) fputc(ConsoleStringBuf[i], flog);
  3120.        }
  3121. #if BORLAND_286
  3122.         ConsoleStringBuf[len] = '\0';
  3123.         retval = fputs(ConsoleStringBuf, stdout);
  3124. #else                    /* BORLAND_286 */
  3125.         wputstr(console, ConsoleStringBuf, len);
  3126. #endif                    /* BORLAND_286 */
  3127.     }
  3128.       }
  3129.    else 
  3130.       retval = vfprintf(file, format, list);
  3131.    va_end(list);
  3132.    return retval;
  3133.    }
  3134.  
  3135. /*
  3136.  * Consoleputc -
  3137.  *   If output is to stdio and not redirected, open a console for it.
  3138.  */
  3139. int Consoleputc(int c, FILE *f)
  3140.    {
  3141.    if (ConsoleFlags & OutputToBuf) {
  3142.      *ConsoleStringBufPtr++ = c;
  3143.      *ConsoleStringBufPtr = '\0';
  3144.      return 1;
  3145.      }
  3146.    if ((f == stdout && !(ConsoleFlags & StdOutRedirect)) || 
  3147.        (f == stderr && !(ConsoleFlags & StdErrRedirect))) {
  3148.       if (flog) fputc(c, flog);
  3149.       console = (wbp)OpenConsole();
  3150.       if (console == NULL) return 0;
  3151. #if BORLAND_286
  3152.       return fputc(c, console);
  3153. #else                    /* BORLAND_286 */
  3154.       return wputc(c, console);
  3155. #endif                    /* BORLAND_286 */
  3156.       }
  3157.    return fputc(c, f);
  3158.    }
  3159.  
  3160. #undef fflush
  3161. #passthru #undef fflush
  3162.  
  3163. int Consolefflush(FILE *f)
  3164. {
  3165.    wbp console;
  3166.    extern int fflush(FILE *);
  3167.    if ((f == stdout && !(ConsoleFlags & StdOutRedirect)) || 
  3168.        (f == stderr && !(ConsoleFlags & StdErrRedirect))) {
  3169.       if (flog) fflush(flog);
  3170.       console = (wbp)OpenConsole();
  3171.       if (console == NULL) return 0;
  3172. #if BORLAND_286
  3173.       return fflush(console);
  3174. #else                    /* BORLAND_286 */
  3175.       return wflush(console);
  3176. #endif                    /* BORLAND_286 */
  3177.       }
  3178.   return fflush(f);
  3179. }
  3180. #passthru #define fflush Consolefflush
  3181. #endif                    /* ConsoleWindow */
  3182.  
  3183. /*
  3184.  * Compare two unsigned long values for qsort or qsearch.
  3185.  */
  3186. int ulcmp(p1, p2)
  3187. pointer p1, p2;
  3188.    {
  3189.    register unsigned long u1 = *(unsigned int *)p1;
  3190.    register unsigned long u2 = *(unsigned int *)p2;
  3191.  
  3192.    if (u1 < u2)
  3193.       return -1;
  3194.    else
  3195.       return (u1 > u2);
  3196.    }
  3197.  
  3198. /*
  3199.  * the next section consists of code to deal with string-integer
  3200.  * (stringint) symbols.  See graphics.h.
  3201.  */
  3202.  
  3203. /*
  3204.  * string-integer comparison, for qsearch()
  3205.  */
  3206. static int sicmp(sip1,sip2)
  3207. siptr sip1, sip2;
  3208. {
  3209.   return strcmp(sip1->s, sip2->s);
  3210. }
  3211.  
  3212. /*
  3213.  * string-integer lookup function: given a string, return its integer
  3214.  */
  3215. int si_s2i(sip,s)
  3216. siptr sip;
  3217. char *s;
  3218. {
  3219.   stringint key;
  3220.   siptr p;
  3221.   key.s = s;
  3222.  
  3223.   p = (siptr)qsearch((char *)&key,(char *)(sip+1),sip[0].i,sizeof(key),sicmp);
  3224.   if (p) return p->i;
  3225.   return -1;
  3226. }
  3227.  
  3228. /*
  3229.  * string-integer inverse function: given an integer, return its string
  3230.  */
  3231. char *si_i2s(sip,i)
  3232. siptr sip;
  3233. int i;
  3234. {
  3235.   register siptr sip2 = sip+1;
  3236.   for(;sip2<=sip+sip[0].i;sip2++) if (sip2->i == i) return sip2->s;
  3237.   return NULL;
  3238. }
  3239.  
  3240.  
  3241. /*
  3242.  * And now, the stringint data.
  3243.  * Convention: the 0'th element of a stringint array contains the
  3244.  * NULL string, and an integer count of the # of elements in the array.
  3245.  */
  3246.  
  3247. stringint attribs[] = {
  3248.    { 0,            NUMATTRIBS},
  3249.    {"ascent",        A_ASCENT},
  3250.    {"bg",        A_BG},
  3251.    {"canvas",        A_CANVAS},
  3252.    {"ceol",        A_CEOL},
  3253.    {"cliph",        A_CLIPH},
  3254.    {"clipw",        A_CLIPW},
  3255.    {"clipx",        A_CLIPX},
  3256.    {"clipy",        A_CLIPY},
  3257.    {"col",        A_COL},
  3258.    {"columns",        A_COLUMNS},
  3259.    {"cursor",        A_CURSOR},
  3260.    {"depth",        A_DEPTH},
  3261.    {"descent",        A_DESCENT},
  3262.    {"display",        A_DISPLAY},
  3263.    {"displayheight",    A_DISPLAYHEIGHT},
  3264.    {"displaywidth",    A_DISPLAYWIDTH},
  3265.    {"drawop",        A_DRAWOP},
  3266.    {"dx",        A_DX},
  3267.    {"dy",        A_DY},
  3268.    {"echo",        A_ECHO},
  3269.    {"fg",        A_FG},
  3270.    {"fheight",        A_FHEIGHT},
  3271.    {"fillstyle",    A_FILLSTYLE},
  3272.    {"font",        A_FONT},
  3273.    {"fwidth",        A_FWIDTH},
  3274.    {"gamma",        A_GAMMA},
  3275.    {"geometry",        A_GEOMETRY},
  3276.    {"height",        A_HEIGHT},
  3277.    {"iconic",        A_ICONIC},
  3278.    {"iconimage",    A_ICONIMAGE},
  3279.    {"iconlabel",    A_ICONLABEL},
  3280.    {"iconpos",        A_ICONPOS},
  3281.    {"image",        A_IMAGE},
  3282.    {"label",        A_LABEL},
  3283.    {"leading",        A_LEADING},
  3284.    {"lines",        A_LINES},
  3285.    {"linestyle",    A_LINESTYLE},
  3286.    {"linewidth",    A_LINEWIDTH},
  3287.    {"pattern",        A_PATTERN},
  3288.    {"pointer",        A_POINTER},
  3289.    {"pointercol",    A_POINTERCOL},
  3290.    {"pointerrow",    A_POINTERROW},
  3291.    {"pointerx",        A_POINTERX},
  3292.    {"pointery",        A_POINTERY},
  3293.    {"pos",        A_POS},
  3294.    {"posx",        A_POSX},
  3295.    {"posy",        A_POSY},
  3296.    {"resize",        A_RESIZE},
  3297.    {"reverse",        A_REVERSE},
  3298.    {"row",        A_ROW},
  3299.    {"rows",        A_ROWS},
  3300.    {"size",        A_SIZE},
  3301.    {"visual",        A_VISUAL},
  3302.    {"width",        A_WIDTH},
  3303.    {"windowlabel",    A_WINDOWLABEL},
  3304.    {"x",        A_X},
  3305.    {"y",        A_Y},
  3306. };
  3307.  
  3308.  
  3309. /*
  3310.  * There are more, X-specific stringint arrays in ../common/xwindow.c
  3311.  */
  3312.  
  3313. #else                    /* Graphics */
  3314. static char junk;        /* avoid empty module */
  3315. #endif                    /* Graphics */
  3316.