home *** CD-ROM | disk | FTP | other *** search
/ vsiftp.vmssoftware.com / VSIPUBLIC@vsiftp.vmssoftware.com.tar / FREEWARE / FREEWARE40.ZIP / pine / pico / os_dos.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-06  |  32.0 KB  |  1,460 lines

  1. /*
  2.  * $Id: os_dos.c,v 4.15 1993/11/22 21:36:28 mikes Exp $
  3.  *
  4.  * Program:    Operating system dependent routines - MS DOS
  5.  *
  6.  *
  7.  * Michael Seibel
  8.  * Networks and Distributed Computing
  9.  * Computing and Communications
  10.  * University of Washington
  11.  * Administration Builiding, AG-44
  12.  * Seattle, Washington, 98195, USA
  13.  * Internet: mikes@cac.washington.edu
  14.  *
  15.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  16.  *
  17.  * Copyright 1991-1993  University of Washington
  18.  *
  19.  *  Permission to use, copy, modify, and distribute this software and its
  20.  * documentation for any purpose and without fee to the University of
  21.  * Washington is hereby granted, provided that the above copyright notice
  22.  * appears in all copies and that both the above copyright notice and this
  23.  * permission notice appear in supporting documentation, and that the name
  24.  * of the University of Washington not be used in advertising or publicity
  25.  * pertaining to distribution of the software without specific, written
  26.  * prior permission.  This software is made available "as is", and
  27.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  28.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  29.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  30.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  31.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  32.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  33.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  34.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  35.  *
  36.  * Pine and Pico are trademarks of the University of Washington.
  37.  * No commercial use of these trademarks may be made without prior
  38.  * written permission of the University of Washington.
  39.  *
  40.  *
  41.  * Notes:
  42.  *      - mouse support added (mss, 921215)
  43.  *
  44.  *  Portions of this code derived from MicroEMACS 3.10:
  45.  *
  46.  *    MSDOS.C:    Operating specific I/O and Spawning functions
  47.  *            under the MS/PCDOS operating system
  48.  *            for MicroEMACS 3.10
  49.  *            (C)opyright 1988 by Daniel M. Lawrence
  50.  *
  51.  */
  52.  
  53. #include     <stdio.h>
  54. #include    <errno.h>
  55. #include    <signal.h>
  56. #include    <setjmp.h>
  57. #include    <time.h>
  58. #include    <fcntl.h>
  59. #include    <io.h>
  60. #include    <bios.h>
  61.  
  62. #include    "osdep.h"
  63. #include    "estruct.h"
  64. #include        "edef.h"
  65. #include        "pico.h"
  66.  
  67.  
  68. #ifdef    MOUSE
  69. typedef struct point {
  70.     unsigned    r:8;        /* row value                */
  71.     unsigned    c:8;        /* column value                */
  72. } POINT;
  73.  
  74.  
  75. typedef struct menuitem {
  76.     unsigned    val;        /* return value                */
  77.     unsigned long (*action)();    /* action to perform            */
  78.     POINT    tl;        /* top-left corner of active area    */
  79.     POINT    br;        /* bottom-right corner of active area    */
  80.     POINT    lbl;        /* where the label starts        */
  81.     char    *label;
  82. } MENUITEM;
  83. MENUITEM menuitems[12];        /* key labels and functions */
  84. MENUITEM mfunc;            /* single generic function  */
  85.  
  86. #define    M_ACTIVE(R, C, X)    (((R) >= (X)->tl.r && (R) <= (X)->br.r) \
  87.                  && ((C) >= (X)->tl.c && (C) < (X)->br.c))
  88. #endif    /* MOUSE */
  89.  
  90.  
  91. #ifdef    ANSI
  92. #ifdef    MOUSE
  93.     int      checkmouse(unsigned *);
  94.     int         register_keys(char *, char *, unsigned);
  95.     void     invert_label(int, MENUITEM *);
  96. #endif
  97.     int enhanced_keybrd(void);
  98.     int dont_interrupt(void);
  99.     int interrupt_ok(void);
  100.     int kbseq(int *);
  101.     int specialkey(unsigned int);
  102.     void do_alarm_signal(void);
  103.     void do_hup_signal(void);
  104.     char *pfnexpand(char *, int);
  105.     int ssleep(long);
  106.     int sleep(int);
  107. #else
  108. #ifdef    MOUSE
  109.     int      checkmouse();
  110. #endif
  111.     int enhanced_keybrd();
  112.     int dont_interrupt();
  113.     int interrupt_ok();
  114.     int kbseq();
  115.     int specialkey();
  116.     void do_alarm_signal();
  117.     void do_hup_signal();
  118.     char *pfnexpand();
  119.     int ssleep();
  120.     int sleep();
  121. #endif
  122.  
  123.  
  124. #ifdef TURBOC
  125. /*
  126.  * big stack for turbo C
  127.  */
  128. extern    unsigned    _stklen = 16384;
  129. #endif
  130.  
  131.  
  132. /*
  133.  * Useful global def's
  134.  */
  135. char   ptmpfile[128];        /* popen temp file */
  136. static int enhncd = 0;        /* keyboard of enhanced variety? */
  137. union  REGS   rg;
  138. struct SREGS  segreg;
  139.  
  140. static int mexist = 0;        /* is the mouse driver installed? */
  141. static int nbuttons;        /* number of buttons on the mouse */
  142. static int oldbut;        /* Previous state of mouse buttons */
  143. static unsigned short oldbreak;    /* Original state of break key */
  144. static unsigned mnoop;
  145.  
  146.  
  147. /*
  148.  * DISable ctrl-break interruption
  149.  */
  150. dont_interrupt()
  151. {
  152.     /* get original value, to be restored later... */
  153.     rg.h.ah = 0x33;        /* control-break check dos call */
  154.     rg.h.al = 0;        /* get the current state */
  155.     rg.h.dl = 0;        /* pre-set it OFF */
  156.     intdos(&rg, &rg);        /* go for it! */
  157.     oldbreak = rg.h.dl;
  158.     /* kill the ctrl-break interupt */
  159.     rg.h.ah = 0x33;        /* control-break check dos call */
  160.     rg.h.al = 1;        /* set the current state */
  161.     rg.h.dl = 0;        /* set it OFF */
  162.     intdos(&rg, &rg);        /* go for it! */
  163. }
  164.  
  165.  
  166. /*
  167.  * re-enable ctrl-break interruption
  168.  */
  169. interrupt_ok()
  170. {
  171.     /* restore the ctrl-break interupt */
  172.     rg.h.ah = 0x33;        /* control-break check dos call */
  173.     rg.h.al = 1;        /* set to new state */
  174.     rg.h.dl = oldbreak;        /* set it to its original value */
  175.     intdos(&rg, &rg);    /* go for it! */
  176. }
  177.  
  178.  
  179. /*
  180.  * return true if an enhanced keyboard is present
  181.  */
  182. enhanced_keybrd()
  183. {
  184.     /* and check for extended keyboard */
  185.     rg.h.ah = 0x05;
  186.     rg.x.cx = 0xffff;
  187.     int86(BIOS_KEYBRD, &rg, &rg);
  188.     rg.h.ah = 0x10;
  189.     int86(BIOS_KEYBRD, &rg, &rg);
  190.     return(rg.x.ax == 0xffff);
  191. }
  192.  
  193.  
  194. /*
  195.  * This function is called once to set up the terminal device streams.
  196.  */
  197. ttopen()
  198. {
  199.     dont_interrupt();            /* don't allow interrupt */
  200.     enhncd = enhanced_keybrd();        /* check for extra keys */
  201. #if    MOUSE
  202.     init_mouse();
  203. #else    /* !MOUSE */
  204.     mexist = 0;
  205. #endif    /* MOUSE */
  206.     return(1);
  207. }
  208.  
  209. #ifdef    MOUSE
  210. /* 
  211.  * init_mouse - check for and initialize mouse driver...
  212.  */
  213. init_mouse()
  214. {
  215.     long miaddr;        /* mouse interupt routine address */
  216.  
  217.     if(mexist)
  218.       return(TRUE);
  219.  
  220.     /* check if the mouse drive exists first */
  221.     rg.x.ax = 0x3533;        /* look at the interrupt 33 address */
  222.     intdosx(&rg, &rg, &segreg);
  223.     miaddr = (((long)segreg.es) << 16) + (long)rg.x.bx;
  224.     if (miaddr == 0 || *(char *)miaddr == 0xcf) {
  225.     mexist = FALSE;
  226.     return(TRUE);
  227.     }
  228.  
  229.     /* and then check for the mouse itself */
  230.     rg.x.ax = 0;            /* mouse status flag */
  231.     int86(BIOS_MOUSE, &rg, &rg);    /* check for the mouse interupt */
  232.     mexist = (rg.x.ax != 0);
  233.     nbuttons = rg.x.bx;
  234.  
  235.     if (mexist == FALSE)
  236.     return(TRUE);
  237.  
  238.     /* if the mouse exists.. get it in the upper right corner */
  239.     rg.x.ax = 4;            /* set mouse cursor position */
  240.     rg.x.cx = (term.t_ncol/2) << 3;    /* Center of display... */
  241.     rg.x.dx = 1 << 3;            /* Second line down */
  242.     int86(BIOS_MOUSE, &rg, &rg);
  243.  
  244.     /* and set its attributes */
  245.     rg.x.ax = 10;        /* set text cursor */
  246.     rg.x.bx = 0;        /* software text cursor please */
  247.     rg.x.cx = 0x77ff;    /* screen mask */
  248.     rg.x.dx = 0x7700;    /* cursor mask */
  249.     int86(BIOS_MOUSE, &rg, &rg);
  250.     return(TRUE);
  251. }
  252. #endif
  253.  
  254.  
  255. /*
  256.  * This function gets called just before we go back home to the command
  257.  * interpreter.
  258.  */
  259. ttclose()
  260. {
  261.     if(!Pmaster)
  262.       interrupt_ok();
  263.  
  264.     return(1);
  265. }
  266.  
  267.  
  268. /*
  269.  * ttspeed - return tty line speed
  270.  */
  271. ttspeed()
  272. {
  273.     return(0);                /* DOS NO OP */
  274. }
  275.  
  276.  
  277. /*
  278.  * Write a character to the display. 
  279.  */
  280. ttputc(c)
  281. {
  282.     return(bdos(6, c, 0));
  283. }
  284.  
  285.  
  286. /*
  287.  * Flush terminal buffer. Does real work where the terminal output is buffered
  288.  * up. A no-operation on systems where byte at a time terminal I/O is done.
  289.  */
  290. ttflush()
  291. {
  292.     return(1);
  293. }
  294.  
  295.  
  296. /*
  297.  * specialkey - return special key definition
  298.  */
  299. specialkey(kc)
  300. unsigned  kc;
  301. {
  302.     switch(kc){
  303.     case 0x3b00 : return(F1);
  304.     case 0x3c00 : return(F2);
  305.     case 0x3d00 : return(F3);
  306.     case 0x3e00 : return(F4);
  307.     case 0x3f00 : return(F5);
  308.     case 0x4000 : return(F6);
  309.     case 0x4100 : return(F7);
  310.     case 0x4200 : return(F8);
  311.     case 0x4300 : return(F9);
  312.     case 0x4400 : return(F10);
  313.     case 0x8500 : return(F11);
  314.     case 0x8600 : return(F12);
  315.     case 0x4800 : return(K_PAD_UP);
  316.     case 0x5000 : return(K_PAD_DOWN);
  317.     case 0x4b00 : return(K_PAD_LEFT);
  318.     case 0x4d00 : return(K_PAD_RIGHT);
  319.     case 0x4700 : return(K_PAD_HOME);
  320.     case 0x4f00 : return(K_PAD_END);
  321.     case 0x4900 : return(K_PAD_PREVPAGE);
  322.     case 0x5100 : return(K_PAD_NEXTPAGE);
  323.     case 0x5300 : return(K_PAD_DELETE);
  324.     case 0x48e0 : return(K_PAD_UP);            /* grey key version */
  325.     case 0x50e0 : return(K_PAD_DOWN);        /* grey key version */
  326.     case 0x4be0 : return(K_PAD_LEFT);        /* grey key version */
  327.     case 0x4de0 : return(K_PAD_RIGHT);        /* grey key version */
  328.     case 0x47e0 : return(K_PAD_HOME);        /* grey key version */
  329.     case 0x4fe0 : return(K_PAD_END);        /* grey key version */
  330.     case 0x49e0 : return(K_PAD_PREVPAGE);        /* grey key version */
  331.     case 0x51e0 : return(K_PAD_NEXTPAGE);        /* grey key version */
  332.     case 0x53e0 : return(K_PAD_DELETE);        /* grey key version */
  333.     default     : return(NODATA);
  334.     }
  335. }
  336.  
  337.  
  338. /*
  339.  * Read a character from the terminal, performing no editing and doing no echo
  340.  * at all. Also mouse events are forced into the input stream here.
  341.  */
  342. ttgetc()
  343. {
  344.     return(_bios_keybrd(enhncd ? _NKEYBRD_READ : _KEYBRD_READ));
  345. }
  346.  
  347.  
  348. /*
  349.  * ctrlkey - used to check if the key hit was a control key.
  350.  */
  351. ctrlkey()
  352. {
  353.     return(_bios_keybrd(enhncd ? _NKEYBRD_SHIFTSTATUS : _KEYBRD_SHIFTSTATUS)
  354.             & 0x04);
  355. }
  356.  
  357.  
  358. /*
  359.  * Read in a key.
  360.  * Do the standard keyboard preprocessing. Convert the keys to the internal
  361.  * character set.  Resolves escape sequences and returns no-op if global
  362.  * timeout value exceeded.
  363.  */
  364. GetKey()
  365. {
  366.     unsigned ch = 0, lch;
  367.     long timein;
  368.  
  369.     if(mexist || timeout){
  370.     timein = time(0L);
  371. #ifdef    MOUSE
  372.     if(mexist){
  373.         rg.x.ax = 1;            /* Show Cursor */
  374.         int86(BIOS_MOUSE, &rg, &rg); 
  375.     }
  376. #endif
  377.     while(!_bios_keybrd(enhncd ? _NKEYBRD_READY : _KEYBRD_READY)){
  378. #if    MOUSE
  379.         if(timeout && time(0L) >= timein+timeout){
  380.         if(mexist){
  381.             rg.x.ax = 2;        /* Hide Cursor */
  382.             int86(BIOS_MOUSE, &rg, &rg);
  383.         }
  384.         return(NODATA);
  385.         }
  386.  
  387.         if(checkmouse(&ch)){        /* something happen ?? */
  388.         if(mexist){
  389.             rg.x.ax = 2;        /* Hide Cursor */
  390.             int86(BIOS_MOUSE, &rg, &rg);
  391.         }
  392.         curwp->w_flag |= WFHARD;
  393.         return(ch);
  394.         }
  395. #else
  396.         if(time(0L) >= timein+timeout)
  397.           return(NODATA);
  398. #endif    /* MOUSE */
  399.         }
  400. #ifdef    MOUSE
  401.     if(mexist){
  402.         rg.x.ax = 2;            /* Hide Cursor */
  403.         int86(BIOS_MOUSE, &rg, &rg);
  404.     }
  405. #endif    /* MOUSE */
  406.     }
  407.  
  408.     ch  = (*term.t_getchar)();
  409.     lch = (ch&0xff);
  410.     return((lch && (lch != 0xe0)) 
  411.             ? (lch < ' ') ? (CTRL|(lch + '@')) 
  412.                           : (lch == ' ' && ctrlkey()) ? (CTRL|'@') : lch
  413.             : specialkey(ch));
  414. }
  415.  
  416.  
  417. #if    MOUSE
  418. /* 
  419.  * checkmouse - look for mouse events in key menu and return 
  420.  *              appropriate value.
  421.  */
  422. int
  423. checkmouse(ch)
  424. unsigned *ch;
  425. {
  426.     register int k;        /* current bit/button of mouse */
  427.     int mcol;            /* current mouse column */
  428.     int mrow;            /* current mouse row */
  429.     int sstate;            /* current shift key status */
  430.     int newbut;            /* new state of the mouse buttons */
  431.     int rv = 0;
  432.  
  433.     if(!mexist)
  434.     return(FALSE);
  435.  
  436.     /* check to see if any mouse buttons are different */
  437.     rg.x.ax = 3;        /* Get button status and mouse position */
  438.     int86(BIOS_MOUSE, &rg, &rg);
  439.     newbut = rg.x.bx;
  440.     mcol = rg.x.cx >> 3;
  441.     mrow = (rg.x.dx >> 3);
  442.  
  443.     /* only notice changes */
  444.     if (oldbut == newbut)
  445.     return(FALSE);
  446.  
  447.     if (mcol < 0)        /* only on screen presses are legit! */
  448.     mcol = 0;
  449.     if (mrow < 0)
  450.     mrow = 0;
  451.  
  452.     sstate = 0;            /* get the shift key status as well */
  453.     rg.h.ah = 2;
  454.     int86(BIOS_KEYBRD, &rg, &rg);
  455.     sstate = rg.h.al;
  456.  
  457.     for (k=1; k != (1 << nbuttons); k = k<<1) {
  458.     /* For each button on the mouse */
  459.     if ((oldbut&k) != (newbut&k)) {
  460.         if(k == 1){
  461.         static int oindex;
  462.         int i = 0;
  463.  
  464.         if(newbut&k)            /* button down */
  465.           oindex = -1;
  466.  
  467.         if(mfunc.action && M_ACTIVE(mrow, mcol, &mfunc)){
  468.             unsigned long r;
  469.  
  470.             if((r = (*mfunc.action)(newbut&k, mrow, mcol))&0xffff){
  471.             *ch = (unsigned)((r>>16)&0xffff);
  472.             rv  = TRUE;
  473.             }
  474.         }
  475.         else{
  476.             while(1){    /* see if we understand event */
  477.             if(i >= 12){
  478.                 i = -1;
  479.                 break;
  480.             }
  481.  
  482.             if(M_ACTIVE(mrow, mcol, &menuitems[i]))
  483.               break;
  484.  
  485.             i++;
  486.             }
  487.  
  488.             if(newbut&k){            /* button down */
  489.             oindex = i;            /* remember where */
  490.             if(i != -1)            /* invert label */
  491.               invert_label(1, &menuitems[i]);
  492.             }
  493.             else{                /* button up */
  494.             if(oindex != -1){
  495.                 if(i == oindex){
  496.                 *ch = menuitems[i].val;
  497.                 rv = 1;
  498.                 }
  499.             }
  500.             }
  501.         }
  502.  
  503.         if(!(newbut&k) && oindex != -1)
  504.           invert_label(0, &menuitems[oindex]);    /* restore label */
  505.         }
  506.  
  507.         oldbut = newbut;
  508.         return(rv);
  509.     }
  510.     }
  511.  
  512.     return(FALSE);
  513. }
  514.  
  515.  
  516. /*
  517.  * invert_label - highlight the label of the given menu item.
  518.  */
  519. void
  520. invert_label(state, m)
  521. int state;
  522. MENUITEM *m;
  523. {
  524.     int i, j, r, c, p;
  525.     char *lp;
  526.     int old_state = getrevstate();
  527.  
  528.     if(m->val == mnoop)
  529.       return;
  530.  
  531.     rg.h.ah = 3;                /* get cursor position */
  532.     int86(BIOS_VIDEO, &rg, &rg);
  533.     p = rg.h.bh;
  534.     c = rg.h.dl;
  535.     r = rg.h.dh;
  536.     rg.x.ax = 2;                /* Hide Cursor */
  537.     int86(BIOS_MOUSE, &rg, &rg);
  538.     (*term.t_move)(m->tl.r, m->tl.c);
  539.     (*term.t_rev)(state);
  540.     for(i = m->tl.r; i <= m->br.r; i++)
  541.       for(j = m->tl.c; j <= m->br.c; j++)
  542.     if(i == m->lbl.r && j == m->lbl.c){    /* show label?? */
  543.         lp = m->label;
  544.         while(*lp && j++ < m->br.c)
  545.           (*term.t_putchar)(*lp++);
  546.  
  547.         continue;
  548.     }
  549.     else
  550.       (*term.t_putchar)(' ');
  551.  
  552.     (*term.t_rev)(old_state);
  553.     rg.h.ah = 2;
  554.     rg.h.bh = p;
  555.     rg.h.dh = r;
  556.     rg.h.dl = c;
  557.     int86(BIOS_VIDEO, &rg, &rg);        /* restore old position */
  558.     rg.x.ax = 1;                /* Show Cursor */
  559.     int86(BIOS_MOUSE, &rg, &rg);
  560. }
  561.  
  562.  
  563. /*
  564.  * register_mfunc - register the given function to get called
  565.  *             on mouse events in the given display region
  566.  */
  567. register_mfunc(f, tlr, tlc, brr, brc)
  568. unsigned long (*f)();
  569. int      tlr, tlc, brr, brc;
  570. {
  571.     if(!mexist)
  572.       return(FALSE);
  573.  
  574.     mfunc.action = f;
  575.     mfunc.tl.r   = tlr;
  576.     mfunc.br.r   = brr;
  577.     mfunc.tl.c   = tlc;
  578.     mfunc.br.c   = brc;
  579.     mfunc.lbl.c  = mfunc.lbl.r = 0;
  580.     mfunc.label  = "";
  581.     return(TRUE);
  582. }
  583.  
  584.  
  585. /*
  586.  * clear_mfunc - clear any previously set mouse function
  587.  */
  588. void
  589. clear_mfunc()
  590. {
  591.     mfunc.action = NULL;
  592. }
  593.  
  594.  
  595. /*
  596.  * register_key - register the given keystroke to accept mouse events
  597.  */
  598. void
  599. register_key(i, rval, label, row, col, len)
  600. int       i;
  601. unsigned  rval;
  602. char     *label;
  603. int       row, col, len;
  604. {
  605.     if(i > 11)
  606.       return;
  607.  
  608.     menuitems[i].val   = rval;
  609.     menuitems[i].tl.r  = menuitems[i].br.r = row;
  610.     menuitems[i].tl.c  = col;
  611.     menuitems[i].br.c  = col + len;
  612.     menuitems[i].lbl.r = menuitems[i].tl.r;
  613.     menuitems[i].lbl.c = menuitems[i].tl.c;
  614.     menuitems[i].label = label;
  615. }
  616.  
  617.  
  618. /*
  619.  * register_keys - take pico's key help strings and fit them into
  620.  *                 the array the mouse uses
  621.  */
  622. register_keys(k, l, noop)
  623. char     *k, *l;
  624. unsigned  noop;
  625. {
  626.     unsigned  val;
  627.     int       i = 0, ks, ls, n, slop;
  628.     char     *ln, *hk = HelpKeyNames;
  629.  
  630.     if(!mexist)
  631.       return(FALSE);
  632.  
  633.     mnoop = noop;
  634.     /* size of key portion */
  635.     ls = term.t_ncol/6;
  636.     ks = (hk && hk[2] == ',') ? 2 : 3;
  637.     for(i = 0;i < 12; i++){
  638.     if(k[i] != '0'){            /* fill in struct */
  639.         if(!hk || hk[1] == '^')
  640.           val = (CTRL|(k[i]));
  641.         else if(hk[1] == 'F' && hk[2] != ',')
  642.           val = F1 + ((i < 6) ?  (2*i) : ((2*(i-6))+1));
  643.         else
  644.           val = k[i];
  645.  
  646.         slop = (hk[1] == 'F' && hk[2] != ',' && val > F8) 
  647.             ? (val > F10) ? 2 : 1 : 0;
  648.         ln   = l;
  649.         for(n = 0;*l != ',' && *l != '\0'; l++)
  650.           if(n < ls - ks)
  651.         n++;
  652.  
  653.         l++;
  654.         register_key(i, val, ln, 
  655.              term.t_nrow - ((i < 6) ? 1 : 0),
  656.              ((i - ((i < 6) ? 0 : 6 )) * ls) + ks + slop,
  657.              n);
  658.     }
  659.     else
  660.       register_key(i, noop, NULL, 0, 0, 0);
  661.     }
  662. }
  663.  
  664.  
  665. /*
  666.  * pico_mouse - general handler for mouse events in the pico's
  667.  *              text region.
  668.  */
  669. unsigned long
  670. pico_mouse(down, row, col)
  671. int down, row, col;
  672. {
  673.     static   int ldown = 0, lrow = 0, lcol = 0, double_click = 0;
  674.     static   clock_t lastcalled = 0;
  675.     unsigned long rv = FALSE;
  676.     unsigned c;
  677.  
  678.     if(down){
  679.     if(lrow == row && lcol == col){        /* same event!! */
  680.         if(clock() < (lastcalled + (clock_t)(CLOCKS_PER_SEC/2)))
  681.           double_click++;
  682.     }
  683.  
  684.     lrow       = row;
  685.     lcol       = col;
  686.     lastcalled = clock();
  687.     }
  688.     else if(lrow == row && lcol == col){
  689.     LINE *lp = curwp->w_linep;
  690.     int    i;
  691.  
  692.     i = lrow - ((Pmaster) ? ComposerTopLine : 2);
  693.     while(i-- && lp != curbp->b_linep)
  694.       lp = lforw(lp);
  695.  
  696.         curgoal = col;
  697.     curwp->w_dotp = lp;
  698.     curwp->w_doto = getgoal(lp);
  699.     curwp->w_flag |= WFMOVE;
  700.  
  701.     if(double_click)
  702.       setmark(0, 1);
  703.  
  704.     double_click = 0;
  705.     rv = NODATA;
  706.     rv = (rv<<16)|TRUE;
  707.     }
  708.     return(rv);
  709. }
  710.  
  711.  
  712. void
  713. mouseon()
  714. {
  715.     rg.x.ax = 1;            /* Show Cursor */
  716.     int86(BIOS_MOUSE, &rg, &rg); 
  717. }
  718.  
  719.  
  720. void
  721. mouseoff()
  722. {
  723.     rg.x.ax = 2;            /* Hide Cursor */
  724.     int86(BIOS_MOUSE, &rg, &rg);
  725. }
  726. #endif    /* MOUSE */
  727.  
  728.  
  729. /* kbseq - looks at an escape sequence coming from the keyboard and 
  730.  *         compares it to a trie of known keyboard escape sequences, and
  731.  *         performs the function bound to the escape sequence.
  732.  * 
  733.  *         returns: BADESC, the escaped function, or 0 if not found.
  734.  */
  735. kbseq(c)
  736. int    *c;
  737. {
  738.     return(0);
  739. }
  740.  
  741.  
  742. /*
  743.  * alt_editor - fork off an alternate editor for mail message composition
  744.  *
  745.  *  NOTE: Not yet used under DOS
  746.  */
  747. alt_editor(f, n)
  748. {
  749.     return(0);
  750. }
  751.  
  752.  
  753. /*
  754.  *  bktoshell - suspend and wait to be woken up
  755.  */
  756. bktoshell()        /* suspend MicroEMACS and wait to wake up */
  757. {
  758.     int i;
  759.  
  760.     (*term.t_move)(term.t_nrow, 0);
  761.     i = system("command");
  762.     /* redraw */
  763.     if(i == -1)
  764.       emlwrite("Error loading COMMAND.COM");
  765.     else
  766.       refresh(0, 1);
  767. }
  768.  
  769.  
  770. /* 
  771.  * rtfrmshell - back from shell, fix modes and return
  772.  */
  773. void
  774. rtfrmshell()
  775. {
  776. }
  777.  
  778.  
  779. /*
  780.  * do_alarm_signal - jump back in the stack to where we can handle this
  781.  */
  782. void
  783. do_alarm_signal()
  784. {
  785. }
  786.  
  787.  
  788. /*
  789.  * do_hup_signal - jump back in the stack to where we can handle this
  790.  */
  791. void
  792. do_hup_signal()
  793. {
  794. }
  795.  
  796.  
  797. unsigned char okinfname[32] = {
  798.       0,    0,             /* ^@ - ^G, ^H - ^O  */
  799.       0,    0,            /* ^P - ^W, ^X - ^_  */
  800.       0,    0x17,        /* SP - ' ,  ( - /   */
  801.       0xff, 0xe0,        /*  0 - 7 ,  8 - ?   */
  802.       0x7f, 0xff,        /*  @ - G ,  H - O   */
  803.       0xff, 0xe9,        /*  P - W ,  X - _   */
  804.       0x7f, 0xff,        /*  ` - g ,  h - o   */
  805.       0xff, 0xf6,        /*  p - w ,  x - DEL */
  806.       0,    0,             /*  > DEL   */
  807.       0,    0,            /*  > DEL   */
  808.       0,    0,             /*  > DEL   */
  809.       0,    0,             /*  > DEL   */
  810.       0,    0             /*  > DEL   */
  811. };
  812.  
  813.  
  814. /*
  815.  * fallowc - returns TRUE if c is allowable in filenames, FALSE otw
  816.  */
  817. fallowc(c)
  818. int c;
  819. {
  820.     return(okinfname[c>>3] & 0x80>>(c&7));
  821. }
  822.  
  823.  
  824. /*
  825.  * fexist - returns TRUE if the file exists, FALSE otherwise
  826.  */
  827. fexist(file, m, l)
  828. char *file, *m;
  829. long *l;
  830. {
  831.     struct stat    sbuf;
  832.  
  833.     if(l != NULL)
  834.       *l = 0L;
  835.  
  836.     if(stat(file, &sbuf) < 0){
  837.     if(ENOENT)                /* File not found */
  838.       return(FIOFNF);
  839.     else
  840.       return(FIOERR);
  841.     }
  842.  
  843.     if(l != NULL)
  844.       *l = sbuf.st_size;
  845.  
  846.     if(sbuf.st_mode & S_IFDIR)
  847.       return(FIODIR);
  848.  
  849.     if(m[0] == 'r')                /* read access? */
  850.       return((S_IREAD & sbuf.st_mode) ? FIOSUC : FIONRD);
  851.     else if(m[0] == 'w')            /* write access? */
  852.       return((S_IWRITE & sbuf.st_mode) ? FIOSUC : FIONWT);
  853.     else if(m[0] == 'x')            /* execute access? */
  854.       return((S_IEXEC & sbuf.st_mode) ? FIOSUC : FIONEX);
  855.     return(FIOERR);                /* what? */
  856. }
  857.  
  858.  
  859. /*
  860.  * isdir - returns true if fn is a readable directory, false otherwise
  861.  *         silent on errors (we'll let someone else notice the problem;)).
  862.  */
  863. isdir(fn, l)
  864. char *fn;
  865. long *l;
  866. {
  867.     struct stat sbuf;
  868.  
  869.     if(l)
  870.       *l = 0;
  871.  
  872.     if(stat(fn, &sbuf) < 0)
  873.       return(0);
  874.  
  875.     if(l)
  876.       *l = sbuf.st_size;
  877.  
  878.     return(sbuf.st_mode & S_IFDIR);
  879. }
  880.  
  881.  
  882. /*
  883.  * gethomedir - returns the users home directory
  884.  *              Note: home is malloc'd for life of pico
  885.  */
  886. char *gethomedir(l)
  887. int *l;
  888. {
  889.     static char *home = NULL;
  890.     static short hlen = 0;
  891.  
  892.     if(home == NULL){
  893.     sprintf(s, "%c:\\", _getdrive() + 'A' - 1);
  894.     hlen = strlen(s);
  895.     if((home=(char *)malloc(((size_t)hlen + 1) * sizeof(char))) == NULL){
  896.         emlwrite("Problem allocating space for home dir", NULL);
  897.         return(0);
  898.     }
  899.     strcpy(home, s);
  900.     }
  901.  
  902.     if(l)
  903.       *l = hlen;
  904.  
  905.     return(home);
  906. }
  907.  
  908.  
  909. /*
  910.  * homeless - returns true if given file does not reside in the current
  911.  *            user's home directory tree. 
  912.  */
  913. homeless(f)
  914. char *f;
  915. {
  916.     char *home;
  917.     int   len;
  918.  
  919.     home = gethomedir(&len);
  920.     return(strncmp(home, f, len));
  921. }
  922.  
  923.  
  924. /*
  925.  * errstr - return system error string corresponding to given errno
  926.  *          Note: strerror() is not provided on all systems, so it's 
  927.  *          done here once and for all.
  928.  */
  929. char *errstr(err)
  930. int err;
  931. {
  932.     return((err >= 0 && err < sys_nerr) ? sys_errlist[err] : NULL);
  933. }
  934.  
  935.  
  936. /*
  937.  * getfnames - return all file names in the given directory in a single 
  938.  *             malloc'd string.  n contains the number of names
  939.  */
  940. char *getfnames(dn, n)
  941. char *dn;
  942. int  *n;
  943. {
  944.     int status;
  945.     long l;
  946.     char *names, *np, *p;
  947.     struct stat sbuf;
  948.     struct find_t dbuf;                    /* opened directory */
  949.  
  950.     *n = 0;
  951.  
  952.     if(stat(dn, &sbuf) < 0){
  953.     sprintf(s, "\007Dir \"%s\": %s", dn, strerror(errno));
  954.     emlwrite(s, NULL);
  955.     return(NULL);
  956.     } 
  957.     else{
  958.     l = sbuf.st_size;
  959.     if(!(sbuf.st_mode & S_IFDIR)){
  960.         emlwrite("\007Not a directory: \"%s\"", dn);
  961.         return(NULL);
  962.     }
  963.     }
  964.  
  965.     if((names=(char *)malloc(sizeof(char)*3072)) == NULL){
  966.     emlwrite("\007Can't malloc space for file names");
  967.     return(NULL);
  968.     }
  969.     np = names;
  970.  
  971.     strcpy(s, dn);
  972.     if(dn[strlen(dn)-1] == '\\')
  973.       strcat(s, "*.*");
  974.     else
  975.       strcat(s, "\\*.*");
  976.  
  977.     if(_dos_findfirst(s, _A_NORMAL|_A_SUBDIR, &dbuf) != 0){
  978.     emlwrite("Can't find first file in \"%s\"", dn);
  979.     free((char *) names);
  980.     return(NULL);
  981.     }
  982.  
  983.     do{
  984.     (*n)++;
  985.     p = dbuf.name;
  986.     while((*np++ = *p++) != '\0')
  987.       ;
  988.     }
  989.     while(_dos_findnext(&dbuf) == 0);
  990.  
  991.     return(names);
  992. }
  993.  
  994.  
  995. /*
  996.  * fioperr - given the error number and file name, display error
  997.  */
  998. void
  999. fioperr(e, f)
  1000. int  e;
  1001. char *f;
  1002. {
  1003.     switch(e){
  1004.       case FIOFNF:                /* File not found */
  1005.     emlwrite("\007File \"%s\" not found", f);
  1006.     break;
  1007.       case FIOEOF:                /* end of file */
  1008.     emlwrite("\007End of file \"%s\" reached", f);
  1009.     break;
  1010.       case FIOLNG:                /* name too long */
  1011.     emlwrite("\007File name \"%s\" too long", f);
  1012.     break;
  1013.       case FIODIR:                /* file is a directory */
  1014.     emlwrite("\007File \"%s\" is a directory", f);
  1015.     break;
  1016.       case FIONWT:
  1017.     emlwrite("\007Write permission denied: %s", f);
  1018.     break;
  1019.       case FIONRD:
  1020.     emlwrite("\007Read permission denied: %s", f);
  1021.     break;
  1022.       case FIONEX:
  1023.     emlwrite("\007Execute permission denied: %s", f);
  1024.     break;
  1025.       default:
  1026.     emlwrite("\007File I/O error: %s", f);
  1027.     }
  1028. }
  1029.  
  1030.  
  1031. /*
  1032.  * pfnexpand - pico's function to expand the given file name if there is 
  1033.  *           a leading '~'
  1034.  */
  1035. char *pfnexpand(fn, len)
  1036. char *fn;
  1037. int  len;
  1038. {
  1039.     return(fn);
  1040. }
  1041.  
  1042.  
  1043. /*
  1044.  * fixpath - make the given pathname into an absolute path
  1045.  */
  1046. fixpath(name, len)
  1047. char *name;
  1048. int  len;
  1049. {
  1050.     char file[_MAX_PATH];
  1051.     int  dr;
  1052.  
  1053.     if(!len)
  1054.       return(0);
  1055.     
  1056.     /* return the full path of given file */
  1057.     if(isalpha(name[0]) && name[1] == ':'){    /* have drive spec? */
  1058.     if(name[2] != '\\'){            /* including path? */
  1059.         dr = toupper(name[0]) - 'A' + 1;
  1060.         if(_getdcwd(dr, file, _MAX_PATH) != NULL){
  1061.         strcat(file, "\\");
  1062.         strcat(file, &name[2]);        /* add file name */
  1063.         }
  1064.         else
  1065.           return(0);
  1066.     }
  1067.     else
  1068.       return(1);        /* fully qualified with drive and path! */
  1069.     }
  1070.     else if(name[0] == '\\') {            /* no drive spec! */
  1071.     sprintf(file, "%c:%s", _getdrive()+'A'-1, name);
  1072.     }
  1073.     else{
  1074.     if(Pmaster)                /* home dir relative */
  1075.       strcpy(file, gethomedir(NULL));
  1076.     else if(!_getcwd(file, _MAX_PATH))    /* no qualification */
  1077.       return(0);
  1078.  
  1079.     if(*name){                /* if name, append it */
  1080.         strcat(file, "\\");
  1081.         strcat(file, name);
  1082.     }
  1083.     }
  1084.  
  1085.     strncpy(name, file, len);            /* copy back to real buffer */
  1086.     name[len-1] = '\0';                /* tie off just in case */
  1087.     return(1);
  1088. }
  1089.  
  1090.  
  1091. /*
  1092.  * compresspath - given a base path and an additional directory, collapse
  1093.  *                ".." and "." elements and return absolute path (appending
  1094.  *                base if necessary).  
  1095.  *
  1096.  *                returns  1 if OK, 
  1097.  *                         0 if there's a problem
  1098.  *                         new path, by side effect, if things went OK
  1099.  */
  1100. compresspath(base, path, len)
  1101. char *base, *path;
  1102. int  len;
  1103. {
  1104.     register int i;
  1105.     int  depth = 0;
  1106.     char *p;
  1107.     char *stack[32];
  1108.  
  1109. #define PUSHD(X)  (stack[depth++] = X)
  1110. #define POPD()    ((depth > 0) ? stack[--depth] : "")
  1111.  
  1112.     strcpy(s, path);
  1113.     fixpath(s, len);
  1114.  
  1115.     p = s;
  1116.     for(i=0; s[i] != '\0'; i++){        /* pass thru path name */
  1117.     if(s[i] == C_FILESEP){
  1118.         if(p != s)
  1119.           PUSHD(p);                /* push dir entry */
  1120.         p = &s[i+1];            /* advance p */
  1121.         s[i] = '\0';            /* cap old p off */
  1122.         continue;
  1123.     }
  1124.  
  1125.     if(s[i] == '.'){            /* special cases! */
  1126.         if(s[i+1] == '.'            /* parent */
  1127.            && (s[i+2] == C_FILESEP || s[i+2] == '\0')){
  1128.         if(!strcmp(POPD(),""))        /* bad news! */
  1129.           return(0);
  1130.  
  1131.         i += 2;
  1132.         p = (s[i] == '\0') ? "" : &s[i+1];
  1133.         }
  1134.         else if(s[i+1] == C_FILESEP || s[i+1] == '\0'){    /* no op */
  1135.         i++;
  1136.         p = (s[i] == '\0') ? "" : &s[i+1];
  1137.         }
  1138.     }
  1139.     }
  1140.  
  1141.     if(*p != '\0')
  1142.       PUSHD(p);                    /* get last element */
  1143.  
  1144.     path[0] = '\0';
  1145.     for(i = 0; i < depth; i++){
  1146.     strcat(path, S_FILESEP);
  1147.     strcat(path, stack[i]);
  1148.     }
  1149.  
  1150.     return(1);                    /* everything's ok */
  1151. }
  1152.  
  1153.  
  1154. /*
  1155.  * tmpname - return a temporary file name in the given buffer
  1156.  */
  1157. void
  1158. tmpname(name)
  1159. char *name;
  1160. {
  1161.     sprintf(name, tmpnam(NULL));    /* tmp file name */
  1162. }
  1163.  
  1164.  
  1165. /*
  1166.  * Take a file name, and from it
  1167.  * fabricate a buffer name. This routine knows
  1168.  * about the syntax of file names on the target system.
  1169.  * I suppose that this information could be put in
  1170.  * a better place than a line of code.
  1171.  */
  1172. void
  1173. makename(bname, fname)
  1174. char    bname[];
  1175. char    fname[];
  1176. {
  1177.         register char   *cp1;
  1178.         register char   *cp2;
  1179.  
  1180.         cp1 = &fname[0];
  1181.         while (*cp1 != 0)
  1182.                 ++cp1;
  1183.  
  1184.         while (cp1!=&fname[0] && cp1[-1]!='\\')
  1185.                 --cp1;
  1186.         cp2 = &bname[0];
  1187.         while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
  1188.                 *cp2++ = *cp1++;
  1189.         *cp2 = 0;
  1190. }
  1191.  
  1192.  
  1193. /*
  1194.  * copy - copy contents of file 'a' into a file named 'b'.  Return error
  1195.  *        if either isn't accessible or is a directory
  1196.  */
  1197. copy(a, b)
  1198. char *a, *b;
  1199. {
  1200.     int    in, out, n, rv = 0;
  1201.     char   *cb;
  1202.     struct stat tsb, fsb;
  1203.  
  1204.     if(stat(a, &fsb) < 0){        /* get source file info */
  1205.     emlwrite("Can't Copy: %s", errstr(errno));
  1206.     return(-1);
  1207.     }
  1208.  
  1209.     if(!(fsb.st_mode&S_IREAD)){        /* can we read it? */
  1210.     emlwrite("\007Read permission denied: %s", a);
  1211.     return(-1);
  1212.     }
  1213.  
  1214.     if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */
  1215.     emlwrite("\007Can't copy: %s is a directory", a);
  1216.     return(-1);
  1217.     }
  1218.  
  1219.     if(stat(b, &tsb) < 0){        /* get dest file's mode */
  1220.     switch(errno){
  1221.       case ENOENT:
  1222.         break;            /* these are OK */
  1223.       default:
  1224.         emlwrite("\007Can't Copy: %s", errstr(errno));
  1225.         return(-1);
  1226.     }
  1227.     }
  1228.     else{
  1229.     if(!(tsb.st_mode&S_IWRITE)){    /* can we write it? */
  1230.         emlwrite("\007Write permission denied: %s", b);
  1231.         return(-1);
  1232.     }
  1233.  
  1234.     if((tsb.st_mode&S_IFMT) == S_IFDIR){    /* is it directory? */
  1235.         emlwrite("\007Can't copy: %s is a directory", b);
  1236.         return(-1);
  1237.     }
  1238.  
  1239.     if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){
  1240.         emlwrite("\007Identical files.  File not copied", NULL);
  1241.         return(-1);
  1242.     }
  1243.     }
  1244.  
  1245.     if((in = open(a, _O_RDONLY)) < 0){
  1246.     emlwrite("Copy Failed: %s", errstr(errno));
  1247.     return(-1);
  1248.     }
  1249.  
  1250.     if((out=creat(b, fsb.st_mode&0xfff)) < 0){
  1251.     emlwrite("Can't Copy: %s", errstr(errno));
  1252.     close(in);
  1253.     return(-1);
  1254.     }
  1255.  
  1256.     if((cb = (char *)malloc(NLINE*sizeof(char))) == NULL){
  1257.     emlwrite("Can't allocate space for copy buffer!", NULL);
  1258.     close(in);
  1259.     close(out);
  1260.     return(-1);
  1261.     }
  1262.  
  1263.     while(1){                /* do the copy */
  1264.     if((n = read(in, cb, NLINE)) < 0){
  1265.         emlwrite("Can't Read Copy: %s", errstr(errno));
  1266.         rv = -1;
  1267.         break;            /* get out now */
  1268.     }
  1269.  
  1270.     if(n == 0)            /* done! */
  1271.       break;
  1272.  
  1273.     if(write(out, cb, n) != n){
  1274.         emlwrite("Can't Write Copy: %s", errstr(errno));
  1275.         rv = -1;
  1276.         break;
  1277.     }
  1278.     }
  1279.  
  1280.     free(cb);
  1281.     close(in);
  1282.     close(out);
  1283.     return(rv);
  1284. }
  1285.  
  1286.  
  1287. /*
  1288.  * Open a file for writing. Return TRUE if all is well, and FALSE on error
  1289.  * (cannot create).
  1290.  */
  1291. ffwopen(fn)
  1292. char    *fn;
  1293. {
  1294.     extern FILE *ffp;
  1295.  
  1296.     if ((ffp=fopen(fn, "w")) == NULL) {
  1297.         emlwrite("Cannot open file for writing");
  1298.         return (FIOERR);
  1299.     }
  1300.  
  1301.     return (FIOSUC);
  1302. }
  1303.  
  1304.  
  1305. /*
  1306.  * Close a file. Should look at the status in all systems.
  1307.  */
  1308. ffclose()
  1309. {
  1310.     extern FILE *ffp;
  1311.  
  1312.     if (fclose(ffp) != FALSE) {
  1313.         emlwrite("Error closing file");
  1314.         return(FIOERR);
  1315.     }
  1316.  
  1317.     return(FIOSUC);
  1318. }
  1319.  
  1320.  
  1321. /*
  1322.  * P_open - run the given command in a sub-shell returning a file pointer
  1323.  *        from which to read the output
  1324.  *
  1325.  * note:
  1326.  *    For OS's other than unix, you will have to rewrite this function.
  1327.  *    Hopefully it'll be easy to exec the command into a temporary file, 
  1328.  *    and return a file pointer to that opened file or something.
  1329.  */
  1330. FILE *P_open(c)
  1331. char *c;
  1332. {
  1333.     sprintf(ptmpfile, tmpnam(NULL));
  1334.     sprintf(s, "%s > %s", c, ptmpfile);
  1335.     if(system(s) == -1){
  1336.     unlink(ptmpfile);
  1337.     return(NULL);
  1338.     }
  1339.  
  1340.     return(fopen(ptmpfile,"r"));
  1341. }
  1342.  
  1343.  
  1344.  
  1345. /*
  1346.  * P_close - close the given descriptor
  1347.  *
  1348.  */
  1349. P_close(fp)
  1350. FILE *fp;
  1351. {
  1352.     fclose(fp);            /* doesn't handle return codes */
  1353.     unlink(ptmpfile);
  1354.     return(0);;
  1355. }
  1356.  
  1357.  
  1358. /*
  1359.  * worthit - generic sort of test to roughly gage usefulness of using 
  1360.  *           optimized scrolling.
  1361.  *
  1362.  * note:
  1363.  *    returns the line on the screen, l, that the dot is currently on
  1364.  */
  1365. worthit(l)
  1366. int *l;
  1367. {
  1368.     int i;            /* l is current line */
  1369.     unsigned below;        /* below is avg # of ch/line under . */
  1370.  
  1371.     *l = doton(&i, &below);
  1372.     below = (i > 0) ? below/(unsigned)i : 0;
  1373.  
  1374.     return(below > 3);
  1375. }
  1376.  
  1377.  
  1378. /*
  1379.  * o_insert - optimize screen insert of char c
  1380.  */
  1381. o_insert(c)
  1382. char c;
  1383. {
  1384.     return(0);
  1385. }
  1386.  
  1387.  
  1388. /*
  1389.  * o_delete - optimized character deletion
  1390.  */
  1391. o_delete()
  1392. {
  1393.     return(0);
  1394. }
  1395.  
  1396.  
  1397. /*
  1398.  * pico_new_mail - just checks mtime and atime of mail file and notifies user 
  1399.  *               if it's possible that they have new mail.
  1400.  */
  1401. pico_new_mail()
  1402. {
  1403.     return(0);
  1404. }
  1405.  
  1406.  
  1407.  
  1408. /*
  1409.  * time_to_check - checks the current time against the last time called 
  1410.  *                 and returns true if the elapsed time is > timeout
  1411.  */
  1412. time_to_check()
  1413. {
  1414.     static time_t lasttime = 0L;
  1415.  
  1416.     if(!timeout)
  1417.       return(FALSE);
  1418.  
  1419.     if(time((long *) 0) - lasttime > (time_t)timeout){
  1420.     lasttime = time((long *) 0);
  1421.     return(TRUE);
  1422.     }
  1423.     else
  1424.       return(FALSE);
  1425. }
  1426.  
  1427.  
  1428. /*
  1429.  * sstrcasecmp - compare two pointers to strings case independently
  1430.  */
  1431. sstrcasecmp(s1, s2)
  1432. QcompType *s1, *s2;
  1433. {
  1434.     return(stricmp(*(char **)s1, *(char **)s2));
  1435. }
  1436.  
  1437.  
  1438. /*
  1439.  * sleep the given number of microseconds
  1440.  */
  1441. ssleep(s)
  1442.     clock_t s;
  1443. {
  1444.     s += clock();
  1445.     while(s > clock())
  1446.       ;
  1447. }
  1448.  
  1449.  
  1450. /*
  1451.  * sleep the given number of seconds
  1452.  */
  1453. sleep(t)
  1454.     int t;
  1455. {
  1456.     time_t out = (time_t)t + time((long *) 0);
  1457.     while(out > time((long *) 0))
  1458.       ;
  1459. }