home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff280.lzh / Graph / uio.c < prev    next >
C/C++ Source or Header  |  1989-11-20  |  37KB  |  1,220 lines

  1. /*
  2.  *                 GRAPH, Version 1.00 - 4 August 1989
  3.  *
  4.  *            Copyright 1989, David Gay. All Rights Reserved.
  5.  *            This software is freely redistrubatable.
  6.  */
  7.  
  8. /* User interface routines */
  9.  
  10. #include <exec/types.h>
  11. #include <exec/interrupts.h>
  12. #include <exec/ports.h>
  13. #include <exec/io.h>
  14. #include <exec/interrupts.h>
  15. #include <devices/input.h>
  16. #include <devices/inputevent.h>
  17. #define INTUITIONPRIVATE
  18. #include <intuition/intuitionbase.h>
  19. #include <intuition/intuition.h>
  20. #include <graphics/text.h>
  21. #include <libraries/diskfont.h>
  22. #include "libraries/arpbase.h"
  23. #include <math.h>
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <assert.h>
  27. #include <dos.h>
  28. #include <stdarg.h>
  29. #include <ctype.h>
  30.  
  31. #include "uio.h"
  32. #include "graph.h"
  33. #include "grph.h"
  34. #include "list.h"
  35. #include "object.h"
  36. #include "user/gadgets.h"
  37. #include "tracker.h"
  38.  
  39. #include <proto/exec.h>
  40. #include <proto/intuition.h>
  41. #include <proto/graphics.h>
  42. #include <proto/diskfont.h>
  43. #define NODOS
  44. #include "proto/arp.h"
  45.  
  46. #define DEFAVAILSIZE 2048 /* Default space reserved for AvailFonts */
  47. #define ARROW 42 /* Identifier of arrow gadget */
  48. #define CROSS 666 /* Identifer of cross gadget */
  49.  
  50. struct fnode /* font node */
  51. {
  52.     tnode node;
  53.     char name[FONTLEN];
  54. };
  55.  
  56. tlist flist;
  57.  
  58. extern struct IntuitionBase *IntuitionBase;
  59.  
  60. /* Default graph window */
  61. static struct NewWindow graph_win = {
  62.     0, 0,
  63.     640, 200,
  64.     -1, -1,
  65.     RAWKEY | REFRESHWINDOW | CLOSEWINDOW | MENUPICK | GADGETDOWN | GADGETUP | R
  66. EQCLEAR | REQSET | MOUSEBUTTONS | MOUSEMOVE,
  67.     WINDOWSIZING | WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE | SIMPLE_REFRESH | AC
  68. TIVATE,
  69.     NULL,
  70.     NULL,
  71.     "Graph",
  72.     NULL,
  73.     NULL,
  74.     85, 60,
  75.     -1, -1,
  76.     WBENCHSCREEN
  77. };
  78.  
  79. /* The two icon images */
  80. static UWORD chip arrow_data[] = {
  81.     0x7fff, 0x7fff, 0x601f, 0x603f, 0x607f, 0x607f, 0x603f, 0x661f, 0x6f0f, 0x7
  82. f87, 0x7fc3, 0x7fe3, 0x7fff,
  83.     0x0000, 0x0000, 0x1fe0, 0x1fc0, 0x1f80, 0x1f80, 0x1fc0, 0x19e0, 0x10f0, 0x0
  84. 078, 0x003c, 0x001c, 0x0000
  85. };
  86.  
  87. static struct Image arrow_image = {
  88.     -1, 0, 16, 13, 2,
  89.     arrow_data,
  90.     3, 0
  91. };
  92.  
  93. static struct Gadget arrow = {
  94.     NULL,
  95.     -18, 18, 15, 13,
  96.     GADGHCOMP | GADGIMAGE | GRELRIGHT,
  97.     RELVERIFY | RIGHTBORDER | TOGGLESELECT,
  98.     BOOLGADGET,
  99.     (APTR)&arrow_image, NULL, NULL,
  100.     2,
  101.     NULL,
  102.     ARROW
  103. };
  104.  
  105. static UWORD chip cross_data[] = {
  106.     0x7fff, 0x7f7f, 0x7f7f, 0x7f7f, 0x7f7f, 0x7e3f, 0x4081, 0x7e3f, 0x7f7f, 0x7
  107. f7f, 0x7f7f, 0x7f7f, 0x7fff,
  108.     0x0000, 0x0080, 0x0080, 0x0080, 0x0080, 0x01c0, 0x3f7e, 0x01c0, 0x0080, 0x0
  109. 080, 0x0080, 0x0080, 0x0000
  110. };
  111.  
  112. static struct Image cross_image = {
  113.     -1, 0, 16, 13, 2,
  114.     cross_data,
  115.     3, 0
  116. };
  117.  
  118. static struct Gadget cross = {
  119.     NULL,
  120.     -18, 36, 15, 13,
  121.     GADGHCOMP | GADGIMAGE | GRELRIGHT | SELECTED,
  122.     RELVERIFY | RIGHTBORDER | TOGGLESELECT,
  123.     BOOLGADGET,
  124.     (APTR)&cross_image, NULL, NULL,
  125.     1,
  126.     NULL,
  127.     CROSS
  128. };
  129.  
  130. static struct Memory *abort_mem; /* for abort requester */
  131.  
  132. static struct TextAttr alert_attr = { "topaz.font", 8 };
  133. static struct TextFont *alert_font;
  134.  
  135. static struct Task *me;
  136. static struct MsgPort *inputDevPort; /* for input device */
  137. static struct IOStdReq *inputRequestBlock;
  138. static struct Interrupt handlerStuff;
  139. static int inputOpen;
  140.  
  141. static long window_sigs;  /* Combined sigs of all windows */
  142.  
  143. static struct TextAttr font = { "topaz.font", 8 }; /* font for menus, requester
  144. s, etc */
  145. static struct Requester *req;    /* Current requester */
  146. static int ok;                   /* Value of last GadgetID */
  147. static short reqdone;            /* Req must go away. Don't activate next str g
  148. adget */
  149. static gadgevent *gadgethandler; /* Handler for gadget events */
  150.  
  151. /* Add .font extension to fname (assumed of size FONTLEN) */
  152. char *addfont(char *fname)
  153. {
  154.     return strncat(fname, ".font", FONTLEN - 1 - strlen(fname));
  155. }
  156.  
  157. /* Removes .font extension if present */
  158. char *remfont(char *fname)
  159. {
  160.     int l = strlen(fname);
  161.  
  162.     if (l >= 5 && strcmp(&fname[l - 5], ".font") == 0) fname[l - 5] = '\0';
  163.  
  164.     return fname;
  165. }
  166.  
  167. /* Make a list of font names, one entry per font. Sizes, etc ignored. */
  168. int make_font_list(void)
  169. {
  170.     int ok = FALSE;
  171.     char *abuf;
  172.  
  173.     new_list(&flist);
  174.     if (abuf = AllocMem(DEFAVAILSIZE, 0L)) /* Alloc default avail buffer */
  175.     {
  176.         int needs = AvailFonts(abuf, DEFAVAILSIZE, AFF_MEMORY | AFF_DISK);
  177.  
  178.         ok = TRUE;
  179.         if (needs != 0) /* Need a bigger buffer */
  180.         {
  181.             FreeMem(abuf, DEFAVAILSIZE);
  182.             if (abuf = AllocMem(DEFAVAILSIZE + needs, 0L))
  183.                 if (AvailFonts(abuf, DEFAVAILSIZE + needs, AFF_MEMORY | AFF_DIS
  184. K))
  185.                     ok = FALSE; /* Definite failure */
  186.         }
  187.         if (ok) /* Construct font list */
  188.         {
  189.             struct AvailFontsHeader *hdr = (struct AvailFontsHeader *)abuf;
  190.             struct AvailFonts *fl = (struct AvailFonts *)(hdr + 1);
  191.             int i;
  192.  
  193.             /* Add font entries to sorted list, by name. Duplicate entries remo
  194. ved */
  195.             for (i = hdr->afh_NumEntries; i > 0; i--, fl++)
  196.             {
  197.                 struct fnode *scan;
  198.                 char *name = fl->af_Attr.ta_Name;
  199.                 int cmp = -1;
  200.  
  201.                 remfont(name); /* remove extension */
  202.  
  203.                 /* Find insertion position */
  204.                 for (scan = first(&flist); succ(scan) && (cmp = strcmp(name, sc
  205. an->name)) > 0; scan = succ(scan))
  206.                     ;
  207.                 if (cmp != 0) /* Not already present, add to list */
  208.                 {
  209.                     struct fnode *n = alloc_node(sizeof(struct fnode));
  210.  
  211.                     if (!n)
  212.                     {
  213.                         ok = FALSE;
  214.                         break;
  215.                     }
  216.                     n->node.ln_Name = n->name;
  217.                     n->name[FONTLEN - 1] = '\0';
  218.                     strncpy(n->name, name, FONTLEN - 1);
  219.  
  220.                     /* Add at correct position */
  221.                     insert(&flist, n, scan->node.ln_Pred);
  222.                 }
  223.             }
  224.             if (!ok)
  225.             {
  226.                 free_list((list *)&flist, sizeof(struct fnode));
  227.                 new_list(&flist);
  228.             }
  229.         }
  230.         FreeMem(abuf, DEFAVAILSIZE + needs);
  231.     }
  232.     if (!ok) nomem(NULL);
  233.     return ok;
  234. }
  235.  
  236.  
  237. /* Implement mutual exclude seeing Intuition is lazy */
  238. /* (for border gadgets)                              */
  239. /* ------------------------------------------------- */
  240. static void MutualExclude(struct Gadget *us, struct Gadget *gadg, struct Window
  241.  *win)
  242. {
  243.     register int i;
  244.     register struct Gadget *gp;
  245.     register LONG mutex = us->MutualExclude;
  246.     UWORD pos;
  247.  
  248.     /* scan gadget list */
  249.     for (i = 1, gp = gadg; gp && i != 0; i <<= 1, gp = gp->NextGadget)
  250.         if (i & mutex)
  251.         {
  252.             pos = RemoveGadget(win, gp);
  253.             gp->Flags &= ~SELECTED; /* unselect */
  254.             AddGadget(win, gp, pos);
  255.         }
  256.     pos = RemoveGadget(win, us);
  257.     us->Flags |= SELECTED;
  258.     AddGadget(win, us, pos);
  259.  
  260.     RefreshWindowFrame(win); /* This works for border gadgets */
  261. }
  262.  
  263. /* Implement mutual exclude seeing Intuition is lazy */
  264. /* (for requester gadgets)                           */
  265. static void MutEx(struct Gadget *us, struct Requester *req)
  266. {
  267.     register int nb = 0, doneus = FALSE;
  268.     register struct Gadget *gp, *first = NULL;
  269.     register LONG mutex = us->MutualExclude;
  270.     UWORD pos;
  271.  
  272.     /* scan gadget list */
  273.     for (gp = req->ReqGadget; gp && (mutex != 0 || !doneus); mutex >>= 1, gp =
  274. gp->NextGadget)
  275.     {
  276.         if ((mutex & 1) || (doneus = gp == us))
  277.         {
  278.             if (!first) first = gp;
  279.             pos = RemoveGList(req->RWindow, gp, 1);
  280.             if (gp == us)
  281.                 gp->Flags |= SELECTED;  /* select */
  282.             else
  283.                 gp->Flags &= ~SELECTED; /* unselect */
  284.             AddGList(req->RWindow, gp, pos, 1, req);
  285.         }
  286.         if (first) nb++;
  287.     }
  288.     if (first) RefreshGList(first, req->RWindow, req, nb);
  289. }
  290.  
  291. /* Display requester, & handle everything until it goes away */
  292. int DoRequest(struct Requester *r, struct graph *g, gadgevent *handle)
  293. {
  294.     r->Flags |= NOISYREQ; /* We want keystrokes */
  295.  
  296.     ok = FALSE;
  297.     if (Request(r, g->io.win))
  298.     {
  299.         /* setup vital info */
  300.         gadgethandler = handle;
  301.         req = r;
  302.         reqdone = FALSE;
  303.         while (next_command().command != reqgone) ; /* Wait till it leaves */
  304.         req = NULL; /* No req. present */
  305.         gadgethandler = NULL;
  306.     }
  307.     return ok;
  308. }
  309.  
  310. /* Find first string gadget */
  311. static struct Gadget *NextText(struct Gadget *look)
  312. {
  313.     while (look && (look->GadgetType & ~GADGETTYPE) != STRGADGET) look = look->
  314. NextGadget;
  315.     return look;
  316. }
  317.  
  318. /* Default gadget handler, activates string gadgets in sequence & handles
  319.    mutual exclude. Will normally be called by custom handlers if they don't
  320.    have anything special to do.
  321.    Returens the new value of ok */
  322. int std_ghandler(struct Gadget *gg, ULONG class, struct Requester *req, struct
  323. graph *g)
  324. {
  325.     if ((gg->GadgetType & ~GADGETTYPE) == STRGADGET && !reqdone) /* Activate ne
  326. xt one */
  327.     {
  328.         struct Gadget *ng = NextText(gg->NextGadget);
  329.         if (!ng) ng = NextText(req->ReqGadget);
  330.         if (ng) ActivateGadget(ng, req->RWindow, req);
  331.     }
  332.     else if (gg->MutualExclude != 0) MutEx(gg, req);
  333.  
  334.     return gg->GadgetID != 0;
  335. }
  336.  
  337. /* Insert ins in front of into, checking for string overflow
  338.    ( sizeof(into)=maxlen ) */
  339. static char *strinsert(char *into, char *ins, int maxlen)
  340. {
  341.     int delta = strlen(ins);
  342.     int start = strlen(into);
  343.     int i;
  344.  
  345.     if (start + delta >= maxlen) start = maxlen - delta - 1;
  346.  
  347.     for (i = start - 1; i >= 0; i--) into[i + delta] = into[i];
  348.     into[start + delta] = '\0';
  349.     memcpy(into, ins, delta);
  350.  
  351.     return into;
  352. }
  353.  
  354. /* Convert a lock to a path, store in to (maxlen chars long) */
  355. static char *pathstr(char *to, long l, int maxlen)
  356. {
  357.     long tl;
  358.     int notfirst = FALSE;
  359.     struct FileInfoBlock *fib = (struct FileInfoBlock *)AllocMem(sizeof(struct
  360. FileInfoBlock), 0);
  361.  
  362.     if (!fib) return(NULL);
  363.     to[0] = '\0';
  364.  
  365.     do {
  366.         if (!Examine(l, fib))
  367.         {
  368.             to = NULL;
  369.             goto error;
  370.         }
  371.         if (fib->fib_DirEntryType > 0) strinsert(to, "/", maxlen);
  372.         /* Is this still necessary ? */
  373.         if (fib->fib_FileName[0] == '\0') strinsert(to, "RAM", maxlen);
  374.         else strinsert(to, fib->fib_FileName, maxlen);
  375.         tl = l;
  376.         l = ParentDir(l);
  377.         if (notfirst) UnLock(tl); /* Release allocated locks */
  378.         notfirst = TRUE;
  379.     } while (l);
  380.  
  381.     *(strchr(to, '/')) = ':'; /* First name is disk name */
  382.  
  383. error:
  384.     FreeMem((char *)fib, sizeof(struct FileInfoBlock));
  385.     return(to);
  386. }
  387.  
  388. /* Request a file from the user (save in file), return TRUE if OK,
  389.    FALSE if cancelled or failed. Currently uses arp file requester. */
  390. int getfile(char *file, char *msg)
  391. {
  392.     static char directory[DSIZE + 1];
  393.     static struct FileRequester FR;
  394.     char filename[FCHARS + 1];
  395.  
  396.     filename[0] = '\0';
  397.     FR.fr_Hail = msg;
  398.     FR.fr_File = filename;
  399.     FR.fr_Dir = directory;
  400.  
  401.     if (FileRequest(&FR))
  402.     {
  403.         long lock = Lock(directory, SHARED_LOCK);
  404.  
  405.         if (lock)
  406.         {
  407.             if (!pathstr(file, lock, FILELEN)) /* get dir path */
  408.             {
  409.                 UnLock(lock);
  410.                 return FALSE;
  411.             }
  412.             strncat(file, filename, FILELEN - 1 - strlen(file));
  413.  
  414.             UnLock(lock);
  415.             return TRUE;
  416.         }
  417.         else
  418.             alert(NULL, "Failed to lock directory", directory);
  419.     }
  420.     return FALSE;
  421. }
  422.  
  423. /* Setup an "abort" requester, in graph g with text msg (must be as long as max
  424.  message) */
  425. struct Requester *abort_request(struct graph *g, char *msg)
  426. {
  427.     int len = strlen(msg);
  428.     struct Requester *req;
  429.     struct Gadget *gl = NULL;
  430.     int height, width;
  431.     static struct Gadget text = {
  432.         NULL,
  433.         10, 10, 1, 1,
  434.         GADGHNONE, 0L, BOOLGADGET | REQGADGET
  435.     };
  436.  
  437.     /* Construct requester */
  438.     height = 8 * 1 + 10 + 12 + 25;
  439.     width = 8 * len + 2 * 10;
  440.     if (width < 85) width = 85;
  441.  
  442.     text.GadgetText = NULL;
  443.     if ((abort_mem = NewMemory()) &&
  444.         (req = InitReq(50, 15, width, height, abort_mem)) &&
  445.         SetReqBorder(req, 1, abort_mem) &&
  446.         AddBox(&gl, TRUE, "Stop!", 0, RELVERIFY, (width - 65) / 2, height - 25,
  447.  65, 15, FALSE, abort_mem) &&
  448.         AddIntuiText(&text.GadgetText, msg, 0, 0, abort_mem))
  449.     {
  450.         SetReqGadgets(req, gl);
  451.         text.NextGadget = req->ReqGadget;
  452.         req->ReqGadget = &text;
  453.         if (!Request(req, g->io.win)) req = NULL; /* display req */
  454.     }
  455.     else
  456.         req = NULL;
  457.  
  458.     if (!req) Free(abort_mem);
  459.  
  460.     return req;
  461. }
  462.  
  463. /* Change abort requester message. msg must not be longer than the first msg */
  464.      
  465. void set_abort_msg(struct Requester *req, char *msg)
  466. {
  467.     req->ReqGadget->GadgetText->IText = msg;
  468.     RefreshGList(req->ReqGadget, req->RWindow, req, 1);
  469. }
  470.  
  471. /* Clear abort requester */
  472. void end_abort_request(struct Requester *req)
  473. {
  474.     EndRequest(req, req->RWindow);
  475.     Free(abort_mem);
  476. }
  477.  
  478. /* Has the user asked for an abort ? */
  479. int aborted(struct Requester *req)
  480. {
  481.     int abort = FALSE;
  482.     struct IntuiMessage *msg;
  483.  
  484.     while (msg = (struct IntuiMessage *)GetMsg(req->RWindow->UserPort))
  485.     {
  486.         ULONG class = msg->Class;
  487.  
  488.         ReplyMsg((struct Message *)msg);
  489.  
  490.         if (class == REFRESHWINDOW) /* Ignore refreshes at this time */
  491.         {
  492.             BeginRefresh(req->RWindow);
  493.             EndRefresh(req->RWindow, TRUE);
  494.         }
  495.         else if (class == GADGETUP) abort = TRUE;
  496.     }
  497.     return abort;
  498. }
  499.  
  500. /* Display a message in graph g. You pass as many string as you want, followed
  501.    by (char *)NULL. This will try very hard to actually display it, calling
  502.    alert with the first two strings if it fails. */
  503. void message(struct graph *g, ...)
  504. {
  505.     int nb, len;
  506.     va_list msgs;
  507.     char *scan;
  508.     struct Memory *m;
  509.     struct Requester *req;
  510.     struct Gadget *gl = NULL;
  511.     int height, width;
  512.     int ok = FALSE;
  513.  
  514.     /* Find number of lines and maxmimum length */
  515.     nb = 0; len = 0;
  516.     va_start(msgs, g);
  517.     while (scan = va_arg(msgs, char *))
  518.     {
  519.         int nl = strlen(scan);
  520.  
  521.         nb++;
  522.         if (nl > len) len = nl;
  523.     }
  524.     va_end(msgs);
  525.  
  526.     /* Construct requester */
  527.     height = 8 * nb + 10 + 12 + 25;
  528.     width = 8 * len + 2 * 10;
  529.     if (width < 85) width = 85;
  530.  
  531.     if ((m = NewMemory()) &&
  532.         (req = InitReq(50, 15, width, height, m)) &&
  533.         SetReqBorder(req, 1, m) &&
  534.         AddBox(&gl, TRUE, "Ok", 0, RELVERIFY | ENDGADGET, (width - 65) / 2, hei
  535. ght - 25, 65, 15, FALSE, m))
  536.     {
  537.         int y = 10 - 8;
  538.  
  539.         ok = TRUE;
  540.         SetReqGadgets(req, gl);
  541.  
  542.         /* Add message strings */
  543.         va_start(msgs, g);
  544.         while (ok && (scan = va_arg(msgs, char *)))
  545.             ok = ok && AddIntuiText(&req->ReqText, scan, 10, (y += 8), m);
  546.         va_end(msgs);
  547.  
  548.         /* You'll have a surprise if you press Amiga-B ... */
  549.         if (ok && g) ok = DoRequest(req, g, std_ghandler);
  550.     }
  551.     Free(m);
  552.     if (!ok || !g) /* call alert */
  553.     {
  554.         va_start(msgs, g);
  555.         if (nb == 1)
  556.             alert(g ? g->io.win : NULL, va_arg(msgs, char *), NULL);
  557.         else if (nb >= 2)
  558.         {
  559.             char *m1 = va_arg(msgs, char *);
  560.             char *m2 = va_arg(msgs, char *);
  561.  
  562.             alert(g ? g->io.win : NULL, m1, m2);
  563.         }
  564.     }
  565. }
  566.  
  567. /* Display a two line auto request. Doesn't alloc any resources */
  568. void alert(struct Window *win, char *msg1, char *msg2)
  569. {
  570.     struct IntuiText text1, text2, negative;
  571.     const static struct IntuiText template = {
  572.         0, 1, JAM1,
  573.         8, 0,
  574.         &alert_attr
  575.     };
  576.     int width, height;
  577.     int ysize = alert_font ? alert_font->tf_YSize : 8;
  578.  
  579.     text1 = text2 = negative = template;
  580.     text1.TopEdge = 8;
  581.     text1.IText = msg1;
  582.     width = IntuiTextLength(&text1) + 20;
  583.     height = 37 + 2 * ysize;
  584.     if (msg2 != NULL)
  585.     {
  586.         int w;
  587.  
  588.         text1.NextText = &text2;
  589.         text2.TopEdge = text1.TopEdge + ysize;
  590.         text2.IText = msg2;
  591.  
  592.         height += ysize;
  593.         w = IntuiTextLength(&text2) + 20;
  594.         if (w > width) width = w;
  595.     }
  596.     negative.LeftEdge = 6;
  597.     negative.TopEdge = 4;
  598.     negative.IText = "Ok";
  599.  
  600.     AutoRequest(win, &text1, NULL, &negative, 0L, 0L, width, height);
  601. }
  602.  
  603. /* Easy no mem requester */
  604. void nomem(struct Window *win)
  605. {
  606.     alert(win, "No memory !", NULL);
  607. }
  608.  
  609. /* Return next menu selection that is a command (in graph g).
  610.    *choice is the item number of the next menu selection */
  611. static struct cmd process(UWORD *choice, struct graph *g)
  612. {
  613.     struct MenuItem *item;
  614.     UWORD itemnb, subnb;
  615.     struct cmd cmd;
  616.     struct pos *rect = (struct pos *)g->s.current;
  617.  
  618.     /* Prepare command */
  619.     cmd.command = none;
  620.     cmd.g = g;
  621.  
  622.     /* Try & find a command */
  623.     while (cmd.command == none && *choice != MENUNULL)
  624.     {
  625.         item = ItemAddress(g->io.menu, *choice);
  626.         itemnb = ITEMNUM(*choice);
  627.         subnb = SUBNUM(*choice);
  628.  
  629.         /* Rem: illegal choices are disabled ==> no checking here */
  630.         switch (MENUNUM(*choice)) {
  631.             case 0 : switch (itemnb) { /* Project */
  632.                 case 0 : /* New Graph */
  633.                     cmd.command = _new_graph;
  634.                     break;
  635.                 case 1 : /* Delete Graph */
  636.                     cmd.command = close;
  637.                     break;
  638.                 case 2 : /* Load Graph */
  639.                     cmd.command = _load_graph;
  640.                     break;
  641.                 case 3 : /* Save Graph */
  642.                     cmd.command = _save_graph;
  643.                     break;
  644.                 case 4 : /* Output Graph */
  645.                     switch (subnb)
  646.                     {
  647.                         case 0 : /* To Printer */
  648.                             cmd.command = print_graph;
  649.                             break;
  650.                         case 1 : /* To Disk */
  651.                             cmd.command = iff_graph;
  652.                             break;
  653.                     }
  654.                     break;
  655.                 case 6 : /* Load Variables */
  656.                     cmd.command = load_vars;
  657.                     break;
  658.                 case 7 : /* Save Variables */
  659.                     cmd.command = save_vars;
  660.                     break;
  661.                 case 9 : /* Quit */
  662.                     cmd.command = quit;
  663.                     break;
  664.                 } break;
  665.  
  666.             case 1 : switch (itemnb) { /* Graph */
  667.                 case 0 : /* Scale */
  668.                     deselect(g);
  669.                     cmd.command = limits;
  670.                     break;
  671.                 case 1 : /* Axes */
  672.                     deselect(g);
  673.                     cmd.command = axes;
  674.                     break;
  675.                 case 2 : /* Zoom */
  676.                     cmd.command = zoom;
  677.                     cmd.data.zoom_in.x0 = rect->x0;
  678.                     cmd.data.zoom_in.y0 = rect->y0;
  679.                     cmd.data.zoom_in.x1 = rect->x1;
  680.                     cmd.data.zoom_in.y1 = rect->y1;
  681.                     deselect(g);
  682.                     break;
  683.                 case 3 : /* Zoom Out */
  684.                     cmd.command = zoom_out;
  685.                     cmd.data.zoom_out = 2.0;
  686.                     break;
  687.                 case 4 : /* Center */
  688.                     cmd.command = center;
  689.                     cmd.data.pt.x = rect->x0;
  690.                     cmd.data.pt.y = rect->y0;
  691.                     deselect(g);
  692.                     break;
  693.                 } break;
  694.  
  695.             case 2 : switch (itemnb) { /* Add */
  696.                 case 0 : /* Function */
  697.                     deselect(g);
  698.                     cmd.command = add_function;
  699.                     break;
  700.                 case 1 : /* Label */
  701.                     cmd.command = add_label;
  702.                     cmd.data.pt.x = rect->x0;
  703.                     cmd.data.pt.y = rect->y0;
  704.                     deselect(g);
  705.                     break;
  706.                 } break;
  707.  
  708.             case 3 : switch (itemnb) { /* Edit */
  709.                 case 0 : /* Variables */
  710.                     cmd.command = edit_vars;
  711.                     break;
  712.                 case 2 : /* Select function */
  713.                     {   /* Processed locally */
  714.                         struct object *o;
  715.  
  716.                         if (o = choose_object(g, "Select"))
  717.                         {
  718.                             deselect(g);
  719.                             select_object(g, o);
  720.                         }
  721.                     }
  722.                     break;
  723.                 case 3 : /* Deselect */
  724.                     /* Processed locally */
  725.                     deselect(g);
  726.                     break;
  727.                 case 5 : /* Edit */
  728.                     cmd.data.o = g->s.current;
  729.                     cmd.command = edit;
  730.                     break;
  731.                 case 6 : /* Improve */
  732.                     cmd.data.f = g->s.current;
  733.                     cmd.command = improve;
  734.                     break;
  735.                 case 7 : /* Delete */
  736.                     cmd.data.o = g->s.current;
  737.                     cmd.command = del_object;
  738.                     break;
  739.                 } break;
  740.         }
  741.         *choice = item->NextSelect;
  742.         *choice = MENUNULL;
  743.     }
  744.     return cmd;
  745. }
  746.  
  747. /* Return next command (in any graph) */
  748. struct cmd next_command(void)
  749. {
  750.     struct cmd cmd;
  751.     static int gone = FALSE;
  752.  
  753.     cmd.command = none;
  754.  
  755.     while (cmd.command == none) /* Wait for one */
  756.     {
  757.         struct graph *g;
  758.  
  759.         /* Scan all graphs, checking for commands */
  760.         for (g = first(&graph_list); cmd.command == none && succ(g); g = succ(g
  761. ))
  762.         {
  763.             int moved = FALSE;
  764.             struct IntuiMessage *msg;
  765.             WORD sx, sy;
  766.  
  767.             cmd.g = g;
  768.             /* Any pending menu selections ? */
  769.             if (!req && g->io.nextmenu != MENUNULL) cmd = process(&g->io.nextme
  770. nu, g);
  771.  
  772.             /* Check for messages on window */
  773.             while (cmd.command == none && (msg = (struct IntuiMessage *)GetMsg(
  774. g->io.win->UserPort)))
  775.             {
  776.                 /* Save interesting info */
  777.                 ULONG class = msg->Class;
  778.                 UWORD code = msg->Code;
  779.                 UWORD qualifier = msg->Qualifier;
  780.                 struct Gadget *gg = (struct Gadget *)msg->IAddress, *gadg;
  781.  
  782.                 sx = msg->MouseX; sy = msg->MouseY;
  783.                 ReplyMsg((struct Message *)msg);
  784.  
  785.                 if (class == MOUSEMOVE) /* Accumulate moves */
  786.                     moved = TRUE;
  787.                 else
  788.                 {
  789.                     if (moved) mouse_move(g, sx, sy); /* process any mouse move
  790. ment */
  791.                     moved = FALSE;
  792.                     /* Most messages imply that the mouse button is released. M
  793. ake sure that the program thinks so */
  794.                     if (class != MOUSEBUTTONS && class != REFRESHWINDOW) mouse_
  795. up(g, sx, sy);
  796.  
  797.                     switch (class)
  798.                     {
  799.                         /* Note that most messages are ignored while a
  800.                            requester is up in *any* window */
  801.  
  802.                         case RAWKEY:
  803.                             /* Check for Amiga-V/B (Ok, Cancel shortcut) */
  804.                             if (req && (qualifier & AMIGALEFT) && (code == KEYC
  805. ODE_V || code == KEYCODE_B))
  806.                             {
  807.                                 ok = code == KEYCODE_V;
  808.                                 /* Never let this happen while a string gadget
  809. is active ... */
  810.                                 EndRequest(req, req->RWindow);
  811.                             }
  812.                             break;
  813.                         case CLOSEWINDOW:
  814.                             if (!req) cmd.command = close;
  815.                             break;
  816.                         case MENUPICK:
  817.                             if (!req)
  818.                             {
  819.                                 g->io.nextmenu = code;
  820.                                 cmd = process(&g->io.nextmenu, g);
  821.                             }
  822.                             break;
  823.                         case REFRESHWINDOW:
  824.                             {
  825.                                 int changed;
  826.  
  827.                                 /* Refresh partially only if size hasn't change
  828. d */
  829.                                 BeginRefresh(g->io.win);
  830.                                 changed = g->io.win->Width != g->io.oldwidth ||
  831.  g->io.win->Height != g->io.oldheight;
  832.                                 if (!changed) draw_graph(g, FALSE); /* No messa
  833. ges during refresh !!! */
  834.                                 EndRefresh(g->io.win, TRUE);
  835.                                 if (changed)
  836.                                 {
  837.                                     set_scale(g);
  838.                                     draw_graph(g, TRUE);
  839.                                 }
  840.                                 /* Wait for refresh after requester's disappear
  841. ance
  842.                                    before signaling it */
  843.                                 if (gone)
  844.                                 {
  845.                                     gone = FALSE;
  846.                                     cmd.command = reqgone;
  847.                                 }
  848.                             }
  849.                             break;
  850.                         case REQSET: /* Activate first string gadget */
  851.                             gadg = NextText(req->ReqGadget);
  852.                             if (gadg && !reqdone) ActivateGadget(gadg, g->io.wi
  853. n, req);
  854.                             break;
  855.                         case REQCLEAR:
  856.                             /* Allow 1 refresh before signaling this. There is
  857.                                probably a better way ! */
  858.                             gone = TRUE;
  859.                             break;
  860.                         case GADGETUP: case GADGETDOWN:
  861.                             /* Ack! Hack to avoid bug in string gadget activati
  862. on (V1.2-1.3) */
  863.                             if (class == GADGETUP && (gg->GadgetType & STRGADGE
  864. T) != 0 && IntuitionBase->LibNode.lib_Version <= 34) IntuitionBase->ActiveGadget
  865.  = NULL;
  866.  
  867.                             /* Handle requester hgadgets */
  868.                             if (gadgethandler) ok = gadgethandler(gg, class, re
  869. q, g);
  870.                             else if (gg->GadgetID == ARROW || gg->GadgetID == C
  871. ROSS)
  872.                             {   /* Select new mode */
  873.                                 MutualExclude(gg, g->io.gadgets, g->io.win);
  874.                                 set_mode(g, gg->GadgetID == ARROW);
  875.                             }
  876.                             break;
  877.                         case MOUSEBUTTONS: /* Pass on to graph */
  878.                             if (code == SELECTDOWN)
  879.                                 mouse_down(g, sx, sy);
  880.                             else
  881.                                 mouse_up(g, sx, sy);
  882.                             break;
  883.                     }
  884.                 }
  885.             }
  886.             /* Handle any pending moves */
  887.             if (moved) mouse_move(g, sx, sy);
  888.         }
  889.         /* Wait for something to happen */
  890.         if (cmd.command == none) Wait(window_sigs);
  891.     }
  892.     return cmd;
  893. }
  894.  
  895.  
  896. /* Enable/Disable various menus */
  897.  
  898. void disable_rect_menus(struct graph *g)
  899. {
  900.     struct Window *win = g->io.win;
  901.  
  902.     OffMenu(win, SHIFTMENU(1) | SHIFTITEM(2) | SHIFTSUB(NOSUB)); /* Zoom */
  903.     OffMenu(win, SHIFTMENU(1) | SHIFTITEM(4) | SHIFTSUB(NOSUB)); /* Center */
  904.     OffMenu(win, SHIFTMENU(2) | SHIFTITEM(1) | SHIFTSUB(NOSUB)); /* Label */
  905.     OffMenu(win, SHIFTMENU(3) | SHIFTITEM(3) | SHIFTSUB(NOSUB)); /* Deselect */
  906.      
  907. }
  908.  
  909. void enable_rect_menus(struct graph *g)
  910. {
  911.     struct Window *win = g->io.win;
  912.  
  913.     OnMenu(win, SHIFTMENU(1) | SHIFTITEM(2) | SHIFTSUB(NOSUB)); /* Zoom */
  914.     OnMenu(win, SHIFTMENU(1) | SHIFTITEM(4) | SHIFTSUB(NOSUB)); /* Center */
  915.     OnMenu(win, SHIFTMENU(2) | SHIFTITEM(1) | SHIFTSUB(NOSUB)); /* Label */
  916.     OnMenu(win, SHIFTMENU(3) | SHIFTITEM(3) | SHIFTSUB(NOSUB)); /* Deselect */
  917. }
  918.  
  919. void disable_object_menus(struct graph *g)
  920. {
  921.     struct Window *win = g->io.win;
  922.  
  923.     OffMenu(win, SHIFTMENU(3) | SHIFTITEM(3) | SHIFTSUB(NOSUB)); /* Deselect */
  924.      
  925.     OffMenu(win, SHIFTMENU(3) | SHIFTITEM(5) | SHIFTSUB(NOSUB)); /* Edit */
  926.     OffMenu(win, SHIFTMENU(3) | SHIFTITEM(6) | SHIFTSUB(NOSUB)); /* Improve */
  927.     OffMenu(win, SHIFTMENU(3) | SHIFTITEM(7) | SHIFTSUB(NOSUB)); /* Delete */
  928. }
  929.  
  930. void enable_object_menus(struct graph *g)
  931. {
  932.     struct Window *win = g->io.win;
  933.  
  934.     OnMenu(win, SHIFTMENU(3) | SHIFTITEM(3) | SHIFTSUB(NOSUB)); /* Deselect */
  935.     OnMenu(win, SHIFTMENU(3) | SHIFTITEM(5) | SHIFTSUB(NOSUB)); /* Edit */
  936.     OnMenu(win, SHIFTMENU(3) | SHIFTITEM(6) | SHIFTSUB(NOSUB)); /* Improve */
  937.     OnMenu(win, SHIFTMENU(3) | SHIFTITEM(7) | SHIFTSUB(NOSUB)); /* Delete */
  938. }
  939.  
  940. /* Convert double to nice string (must be of size NBLEN) */
  941. char *double2str(char *to, double x)
  942. {
  943.     int l;
  944.  
  945.     if (x == NOVAL) to[0] = '\0';
  946.     else sprintf(to, "%-5.3g", x);
  947.  
  948.     l = strlen(to);
  949.     while (l > 0 && to[--l] == ' ') to[l] = '\0';
  950.  
  951.     return to;
  952. }
  953.  
  954.  
  955. /* Convert string to double */
  956. double str2double(char *from)
  957. {
  958.     double x;
  959.  
  960.     if (sscanf(from, "%lf", &x) != 1) x = NOVAL;
  961.  
  962.     return x;
  963. }
  964.  
  965. /* convert x to a string (must be of size INTLEN) */
  966. char *int2str(char *to, int x)
  967. {
  968.     if (x == INOVAL) to[0] = '\0';
  969.     else sprintf(to, "%d", x);
  970.  
  971.     return to;
  972. }
  973.  
  974. /* convert string to integer */
  975. int str2int(char *from)
  976. {
  977.     int x;
  978.  
  979.     if (sscanf(from, "%d", &x) != 1) x = INOVAL;
  980.  
  981.     return x;
  982. }
  983.  
  984. /* Removes leading & trailing blanks fron name. Returns name */
  985. char *strip(char *name)
  986. {
  987.     char *scan = name;
  988.  
  989.     while (isspace(*scan)) scan++;
  990.  
  991.     if (*scan)
  992.     {
  993.         char *copy = name;
  994.  
  995.         while (*scan) *(copy++) = *(scan++);
  996.  
  997.         while (isspace(*--copy))
  998.             ;
  999.         *(copy + 1) = '\0';
  1000.     }
  1001.     else
  1002.         *name = '\0';
  1003.  
  1004.     return name;
  1005. }
  1006.  
  1007. /* Hack to allow you to press Amiga-V/B even while entering a string in a
  1008.    string requester */
  1009. static long __saveds __asm handle_ok_cancel(register __a0 struct InputEvent *ev
  1010. , register __a1 void *dummy)
  1011. {
  1012.     struct InputEvent *ep, *laste;
  1013.     static struct InputEvent retkey;
  1014.  
  1015.     if (req)
  1016.         /* run down the list of events to see if they pressed the magic key */
  1017.         for (ep = ev, laste = NULL; ep != NULL; ep = ep->ie_NextEvent)
  1018.         {
  1019.             if (ep->ie_Class == IECLASS_RAWKEY && (ep->ie_Qualifier & AMIGALEFT
  1020. ) && (ep->ie_Code == KEYCODE_V || ep->ie_Code == KEYCODE_B) && IntuitionBase->Ac
  1021. tiveWindow == req->RWindow)
  1022.             {
  1023.                 reqdone = TRUE; /* The requester is going away */
  1024.                 /* Add an extra "return key" event */
  1025.                 retkey.ie_Class = IECLASS_RAWKEY;
  1026.                 retkey.ie_SubClass = 0;
  1027.                 retkey.ie_Code = 0x44; /* return key */
  1028.                 retkey.ie_Qualifier = 0;
  1029.                 retkey.ie_position = ep->ie_position;
  1030.                 retkey.ie_TimeStamp = ep->ie_TimeStamp;
  1031.                 retkey.ie_NextEvent = ep;
  1032.                 /* we can handle this event so take it off the chain */
  1033.                 if (laste == NULL)
  1034.                     ev = &retkey;
  1035.                 else
  1036.                     laste->ie_NextEvent = &retkey;
  1037.                 break;
  1038.             }
  1039.             else
  1040.                 laste = ep;
  1041.         }
  1042.  
  1043.    /* pass on the pointer to the event */
  1044.    return (long)ev;
  1045. }
  1046.  
  1047. /* Create window, menus for a new graph */
  1048. int init_uio(struct graph *g)
  1049. {
  1050.     /* Create mode select gadgets */
  1051.     struct Gadget *a = AllocMem(sizeof(struct Gadget), 0L);
  1052.     struct Gadget *c = AllocMem(sizeof(struct Gadget), 0L);
  1053.  
  1054.     ModSys(0, 1, JAM2, &font);
  1055.     if (a && c)
  1056.     {
  1057.         struct Menu *ml = NULL, *project, *edit, *add, *graph;
  1058.         struct MenuItem *print;
  1059.  
  1060.         *a = arrow;
  1061.         *c = cross;
  1062.         a->NextGadget = c;
  1063.         g->io.gadgets = graph_win.FirstGadget = a;
  1064.         g->io.mem = NULL;
  1065.  
  1066.         /* Create window & menus */
  1067.         if (g->io.win = OpenWindow(&graph_win))
  1068.             if ((g->io.mem = NewMemory()) &&
  1069.                 (project = AddMenu(&ml, NULL, "Project", MENUENABLED, g->io.mem
  1070. )) &&
  1071.                     AddItem(project, "New Graph", ITEMENABLED | HIGHCOMP, 0, 'N
  1072. ', FALSE, g->io.mem) &&
  1073.                     AddItem(project, "Delete Graph", ITEMENABLED | HIGHCOMP, 0,
  1074.  0, FALSE, g->io.mem) &&
  1075.                     AddItem(project, "Load Graph...", ITEMENABLED | HIGHCOMP, 0
  1076. , 'O', FALSE, g->io.mem) &&
  1077.                     AddItem(project, "Save Graph...", ITEMENABLED | HIGHCOMP, 0
  1078. , 'S', FALSE, g->io.mem) &&
  1079.                     (print = AddItem(project, "Output Graph", ITEMENABLED | HIG
  1080. HCOMP, 0, 0, TRUE, g->io.mem)) &&
  1081.                         AddSub(print, "To Printer", ITEMENABLED | HIGHCOMP, 0,
  1082. 'P', g->io.mem) &&
  1083.                         AddSub(print, "To Disk...", ITEMENABLED | HIGHCOMP, 0,
  1084. 0, g->io.mem) &&
  1085.                     AddRule(project, g->io.mem) &&
  1086.                     AddItem(project, "Load Variables...", ITEMENABLED | HIGHCOM
  1087. P, 0, 0, FALSE, g->io.mem) &&
  1088.                     AddItem(project, "Save Variables...", ITEMENABLED | HIGHCOM
  1089. P, 0, 0, FALSE, g->io.mem) &&
  1090.                     AddRule(project, g->io.mem) &&
  1091.                     AddItem(project, "Quit", ITEMENABLED | HIGHCOMP, 0, 'Q', FA
  1092. LSE, g->io.mem) &&
  1093.                 (graph = AddMenu(&ml, NULL, "Graph", MENUENABLED, g->io.mem)) &
  1094. &
  1095.                     AddItem(graph, "Scale...", ITEMENABLED | HIGHCOMP, 0, 'L',
  1096. FALSE, g->io.mem) &&
  1097.                     AddItem(graph, "Axes...", ITEMENABLED | HIGHCOMP, 0, 'X', F
  1098. ALSE, g->io.mem) &&
  1099.                     AddItem(graph, "Zoom", ITEMENABLED | HIGHCOMP, 0, 'Z', FALS
  1100. E, g->io.mem) &&
  1101.                     AddItem(graph, "Zoom out", ITEMENABLED | HIGHCOMP, 0, 0, FA
  1102. LSE, g->io.mem) &&
  1103.                     AddItem(graph, "Centre", ITEMENABLED | HIGHCOMP, 0, 'C', FA
  1104. LSE, g->io.mem) &&
  1105.                 (add = AddMenu(&ml, NULL, "Add", MENUENABLED, g->io.mem)) &&
  1106.                     AddItem(add, "Function...", ITEMENABLED | HIGHCOMP, 0, 'F',
  1107.  FALSE, g->io.mem) &&
  1108.                     AddItem(add, "Label...", ITEMENABLED | HIGHCOMP, 0, 0, FALS
  1109. E, g->io.mem) &&
  1110.                 (edit = AddMenu(&ml, NULL, "Edit", MENUENABLED, g->io.mem)) &&
  1111.                     AddItem(edit, "Variables...", ITEMENABLED | HIGHCOMP, 0, 'V
  1112. ', FALSE, g->io.mem) &&
  1113.                     AddRule(edit, g->io.mem) &&
  1114.                     AddItem(edit, "Select function...", ITEMENABLED | HIGHCOMP,
  1115.  0, 0, FALSE, g->io.mem) &&
  1116.                     AddItem(edit, "Deselect", ITEMENABLED | HIGHCOMP, 0, 0, FAL
  1117. SE, g->io.mem) &&
  1118.                     AddRule(edit, g->io.mem) &&
  1119.                     AddItem(edit, "Edit...", ITEMENABLED | HIGHCOMP, 0, 'E', FA
  1120. LSE, g->io.mem) &&
  1121.                     AddItem(edit, "Improve", ITEMENABLED | HIGHCOMP, 0, 'I', FA
  1122. LSE, g->io.mem) &&
  1123.                     AddItem(edit, "Delete", ITEMENABLED | HIGHCOMP, 0, 0, FALSE
  1124. , g->io.mem))
  1125.             {
  1126.                 SetMenuStrip(g->io.win, ml);
  1127.                 g->io.menu = ml;
  1128.                 disable_rect_menus(g);
  1129.                 disable_object_menus(g);
  1130.                 /* Add signal bit */
  1131.                 window_sigs |= 1 << g->io.win->UserPort->mp_SigBit;
  1132.  
  1133.                 return TRUE; /* all done ok */
  1134.             }
  1135.             else
  1136.             {
  1137.                 Free(g->io.mem);
  1138.                 CloseWindow(g->io.win);
  1139.                 alert(NULL, "No memory !", NULL);
  1140.             }
  1141.         else alert(NULL, "Couldn't open window", NULL);
  1142.     }
  1143.     else
  1144.         alert(NULL, "No memory !", NULL);
  1145.  
  1146.     if (a) FreeMem(a, sizeof(struct Gadget));
  1147.     if (c) FreeMem(c, sizeof(struct Gadget));
  1148.  
  1149.     return FALSE;
  1150. }
  1151.  
  1152. /* Close window, etc */
  1153. void cleanup_uio(struct graph *g)
  1154. {
  1155.     struct Gadget *gg, *next;
  1156.  
  1157.     ClearMenuStrip(g->io.win);
  1158.     CloseWindow(g->io.win);
  1159.     Free(g->io.mem);
  1160.     for (gg = g->io.gadgets; gg; gg = next)
  1161.     {
  1162.         next = gg->NextGadget;
  1163.         FreeMem(gg, sizeof(struct Gadget));
  1164.     }
  1165.     /* Construct new signal list */
  1166.     window_sigs = 0;
  1167.     for (g = first(&graph_list); succ(g); g = succ(g))
  1168.         window_sigs |= 1 << g->io.win->UserPort->mp_SigBit;
  1169. }
  1170.  
  1171. /* Global initialisation */
  1172. int init_user()
  1173. {
  1174.     if (!make_font_list()) return FALSE;
  1175.     /* Open alert font */
  1176.     alert_font = OpenFont(&alert_attr);
  1177.  
  1178.     /* Install input handler */
  1179.     me = FindTask(0);
  1180.     inputDevPort = CreatePort(0,0);
  1181.     if (inputDevPort)
  1182.     {
  1183.         if (inputRequestBlock = CreateStdIO(inputDevPort))
  1184.         {
  1185.  
  1186.             handlerStuff.is_Data = NULL;
  1187.             handlerStuff.is_Code = (void *)handle_ok_cancel;
  1188.             handlerStuff.is_Node.ln_Pri = 51;
  1189.  
  1190.             if (OpenDevice("input.device", 0, inputRequestBlock, 0) == 0)
  1191.             {
  1192.                 inputOpen = TRUE;
  1193.                 inputRequestBlock->io_Command = IND_ADDHANDLER;
  1194.                 inputRequestBlock->io_Data    = (APTR)&handlerStuff;
  1195.  
  1196.                 DoIO(inputRequestBlock);
  1197.                 return TRUE;
  1198.             }
  1199.         }
  1200.     }
  1201.     alert(NULL, "Couldn't install input handler", NULL);
  1202.     return FALSE;
  1203. }
  1204.  
  1205. /* Global cleanup */
  1206. void cleanup_user()
  1207. {
  1208.     if (inputOpen)
  1209.     {
  1210.         inputRequestBlock->io_Command = IND_REMHANDLER;
  1211.         inputRequestBlock->io_Data    = (APTR)&handlerStuff;
  1212.         DoIO(inputRequestBlock);
  1213.         CloseDevice(inputRequestBlock);
  1214.     }
  1215.     if (inputRequestBlock) DeleteStdIO(inputRequestBlock);
  1216.     if (inputDevPort) DeletePort(inputDevPort);
  1217.     free_list((list *)&flist, sizeof(struct fnode));
  1218. }
  1219.  
  1220.