home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / historic / v941.tgz / icon.v941src.tar / icon.v941src / src / runtime / rwindow.r < prev    next >
Text File  |  2002-01-10  |  93KB  |  3,443 lines

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