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

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: os_unix.c,v 4.37 1993/11/23 05:40:11 mikes Exp $";
  3. #endif
  4. /*
  5.  * Program:    Operating system dependent routines - Ultrix 4.1
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  * Copyright 1991-1993  University of Washington
  19.  *
  20.  *  Permission to use, copy, modify, and distribute this software and its
  21.  * documentation for any purpose and without fee to the University of
  22.  * Washington is hereby granted, provided that the above copyright notice
  23.  * appears in all copies and that both the above copyright notice and this
  24.  * permission notice appear in supporting documentation, and that the name
  25.  * of the University of Washington not be used in advertising or publicity
  26.  * pertaining to distribution of the software without specific, written
  27.  * prior permission.  This software is made available "as is", and
  28.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  29.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  30.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  31.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  32.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  33.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  34.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  35.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  36.  *
  37.  * Pine and Pico are trademarks of the University of Washington.
  38.  * No commercial use of these trademarks may be made without prior
  39.  * written permission of the University of Washington.
  40.  *
  41.  *
  42.  * Notes:
  43.  *
  44.  * - SGI IRIX 4.0.1 port by:
  45.  *       johnb@edge.cis.mcmaster.ca,  2 April 1992
  46.  *
  47.  * - Dynix/PTX port by:
  48.  *       Donn Cave, UCS/UW, 15 April 1992
  49.  *
  50.  * - 3B2, 3b1/7300, SCO ports by:
  51.  *       rll@felton.felton.ca.us, 7 Feb. 1993
  52.  *
  53.  * - Probably have to break this up into separate os_type.c files since
  54.  *   the #ifdef's are getting a bit cumbersome.
  55.  *
  56.  */
  57.  
  58. #include     <stdio.h>
  59. #include    <errno.h>
  60. #include    <setjmp.h>
  61. #include    <time.h>
  62. #include    <pwd.h>
  63. #if    defined(sv4) || defined(ptx)
  64. #include    <stropts.h>
  65. #include    <poll.h>
  66. #endif
  67. #if    defined(POSIX)
  68. #include    <termios.h>
  69. #if    defined(a32) || defined(cvx)
  70. #include    <sys/ioctl.h>
  71. #endif
  72. #else
  73. #if    defined(sv3) || defined(sgi) || defined(isc) || defined(ct)
  74. #include    <termio.h>
  75. #if    defined(isc)
  76. #include    <sys/sioctl.h>
  77. #endif
  78. #else
  79. #include    <sgtty.h>
  80. #endif    /* sv3 || sgi || isc */
  81. #endif    /* POSIX */
  82.  
  83. #include    "osdep.h"
  84. #include        "pico.h"
  85. #include    "estruct.h"
  86. #include        "edef.h"
  87. #include        "efunc.h"
  88. #include    <fcntl.h>
  89. #include    <sys/wait.h>
  90. #include    <sys/file.h>
  91. #include    <sys/types.h>
  92. #include    <sys/time.h>
  93. #if    defined(a32)
  94. #include    <sys/select.h>
  95. #endif
  96.  
  97.  
  98. /*
  99.  * Immediately below are includes and declarations for the 3 basic
  100.  * terminal drivers supported; POSIX, SysVR3, and BSD
  101.  */
  102. #ifdef    POSIX
  103.  
  104. struct termios nstate,
  105.         ostate;
  106. #else
  107. #if    defined(sv3) || defined(sgi) || defined(isc)
  108.  
  109. struct termio nstate,
  110.               ostate;
  111.  
  112. #else
  113. struct  sgttyb  ostate;                /* saved tty state */
  114. struct  sgttyb  nstate;                /* values for editor mode */
  115. struct  ltchars    oltchars;            /* old term special chars */
  116. struct  ltchars    nltchars = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  117. struct  tchars    otchars;            /* old term special chars */
  118. struct  tchars    ntchars = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  119.  
  120. #endif    /* sv3 || sgi || isc */
  121. #endif    /* POSIX */
  122.  
  123. #if    defined(sv3) || defined(ct)
  124. /*
  125.  * Windowing structure to support JWINSIZE/TIOCSWINSZ/TIOCGWINSZ 
  126.  */
  127. #define ENAMETOOLONG    78
  128.  
  129. struct winsize {
  130.     unsigned short ws_row;       /* rows, in characters*/
  131.     unsigned short ws_col;       /* columns, in character */
  132.     unsigned short ws_xpixel;    /* horizontal size, pixels */
  133.     unsigned short ws_ypixel;    /* vertical size, pixels */
  134. };
  135. #endif
  136.  
  137. #ifdef    bsd
  138. int    errno;                    /* ya, I know... */
  139. #endif
  140.  
  141. #if    (defined(bsd) || defined(dyn) || defined(ct)) && !defined(LINUX)
  142. #define    SIGTYPE int
  143. #else
  144. #define    SIGTYPE    void
  145. #endif
  146.  
  147.  
  148. #ifdef    ANSI
  149.     int      kbseq(int *);
  150.     SIGTYPE  do_hup_signal();
  151.     SIGTYPE  rtfrmshell();
  152. #ifdef    TIOCGWINSZ
  153.     SIGTYPE  winch_handler();
  154. #endif
  155. #else
  156.     int      kbseq();
  157.     SIGTYPE  do_hup_signal();
  158.     SIGTYPE  rtfrmshell();
  159. #ifdef    TIOCGWINSZ
  160.     SIGTYPE  winch_handler();
  161. #endif
  162. #endif
  163.  
  164.  
  165. /*
  166.  * for alt_editor arg[] building
  167.  */
  168. #define    MAXARGS    10
  169. #ifdef HEBREW
  170. #include "hebrew.h"
  171.  
  172. unsigned char xlat_tab[]={
  173.   0xf9,  /* a - shin*/
  174.   0xf0,  /* b - nun */
  175.   0xe1,  /* c - bet */
  176.   0xe2,  /* d - gimel*/
  177.   0xf7,  /* e - kuf*/
  178.   0xeb,  /* f - chaf */
  179.   0xf2,  /* g - ayin */
  180.   0xe9,  /* h - yud */
  181.   0xef,  /* i - nun sofit */
  182.   0xe7,  /* j - het */
  183.   0xec,  /* k - lamed */
  184.   0xea,  /* l - chaf sofit */
  185.   0xf6,  /* m - zadik */
  186.   0xee,  /* n - mem */
  187.   0xed,  /* o - mem sofit */
  188.   0xf4,  /* p - pey */
  189.   0x2f,  /* q - / */ 
  190.   0xf8,  /* r - raish */
  191.   0xe3,  /* s - dalet */
  192.   0xe0,  /* t - aleph */
  193.   0xe5,  /* u - vav */
  194.   0xe4,  /* v - hey */
  195.   0x27,  /* w - ' */
  196.   0xf1,  /* x - samech */
  197.   0xe8,  /* y - tet */
  198.   0xe6,  /* z - zayin */
  199. };
  200. unsigned char xlat_str1[]="`;',./";
  201. unsigned char xlat_str2[]={
  202.   ';',   /* ` - ; */
  203.   0xf3,  /* ; - pey sofit */
  204.   ',',   /* ' - , */
  205.   0xfa,  /* , - tav */
  206.   0xf5,  /* . - zadik sofit */
  207.   '.',   /* / - . */
  208. };
  209. #endif
  210.  
  211. /*
  212.  * ttopen - this function is called once to set up the terminal device 
  213.  *          streams.  if called as pine composer, don't mess with
  214.  *          tty modes, but set signal handlers.
  215.  */
  216. ttopen()
  217. {
  218.     if(Pmaster == NULL){
  219. #ifdef    POSIX
  220.     tcgetattr (0, &ostate);
  221.     tcgetattr (0, &nstate);
  222.     nstate.c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN);
  223.     nstate.c_iflag &= ~ICRNL;
  224.     nstate.c_oflag &= ~(ONLCR | OPOST);
  225.     nstate.c_cc[VMIN] = 1;
  226.     nstate.c_cc[VTIME] = 0;
  227.     tcsetattr (0, TCSADRAIN, &nstate);
  228. #else
  229. #if    defined(sv3) || defined(sgi) || defined(isc) || defined(ct)
  230.     (void) ioctl(0, TCGETA, &ostate);
  231.     (void) ioctl(0, TCGETA, &nstate);    /** again! **/
  232.  
  233.     nstate.c_lflag &= ~(ICANON | ISIG | ECHO);    /* noecho raw mode  */
  234.     nstate.c_oflag &= ~(OPOST | ONLCR);
  235.     nstate.c_iflag &= ~ICRNL;
  236.         
  237.     nstate.c_cc[VMIN] = '\01';  /* minimum # of chars to queue  */
  238.     nstate.c_cc[VTIME] = '\0'; /* minimum time to wait for input */
  239.     (void) ioctl(0, TCSETA, &nstate);
  240. #else
  241.     ioctl(0, TIOCGETP, &ostate);        /* save old state */
  242.     ioctl(0, TIOCGLTC, &oltchars);        /* Save old lcharacters */
  243.     ioctl(0, TIOCGETC, &otchars);        /* Save old characters */
  244.     ioctl(0, TIOCGETP, &nstate);        /* get base of new state */
  245.     nstate.sg_flags |= RAW;
  246.     nstate.sg_flags &= ~(ECHO|CRMOD);    /* no echo for now... */
  247.     ioctl(0, TIOCSETP, &nstate);        /* set mode */
  248.  
  249.     ioctl(0, TIOCSLTC, &nltchars);        /* put new lcharacter into K */
  250.     ioctl(0, TIOCSETC, &ntchars);        /* put new character into K */
  251. #endif    /* sv3 */
  252. #endif    /* POSIX */
  253.     }
  254.  
  255.     signal(SIGHUP,  do_hup_signal);    /* deal with SIGHUP */
  256.     signal(SIGTERM, do_hup_signal);    /* deal with SIGTERM */
  257. #ifdef    SIGTSTP
  258.     signal(SIGTSTP, SIG_DFL);
  259. #endif
  260. #ifdef    TIOCGWINSZ
  261.     signal(SIGWINCH, winch_handler); /* window size changes */
  262. #endif
  263.     return(1);
  264. }
  265.  
  266.  
  267.  
  268. /*
  269.  * ttclose - this function gets called just before we go back home to 
  270.  *           the command interpreter.  If called as pine composer, don't
  271.  *           worry about modes, but set signals to default, pine will 
  272.  *           rewire things as needed.
  273.  */
  274. ttclose()
  275. {
  276.     if(Pmaster){
  277.     signal(SIGHUP, SIG_DFL);
  278. #ifdef    SIGCONT
  279.     signal(SIGCONT, SIG_DFL);
  280. #endif
  281. #ifdef    TIOCGWINSZ
  282.     signal(SIGWINCH, SIG_DFL);
  283. #endif
  284.     }
  285.     else{
  286. #ifdef    POSIX
  287.     tcsetattr (0, TCSADRAIN, &ostate);
  288. #else
  289. #if    defined(sv3) || defined(sgi) || defined(isc) || defined(ct)
  290.         ioctl(0, TCSETA, &ostate);
  291. #else
  292.     ioctl(0, TIOCSETP, &ostate);
  293.     ioctl(0, TIOCSLTC, &oltchars);
  294.     ioctl(0, TIOCSETC, &otchars);
  295.  
  296.     /*
  297.      * This works around a really weird problem.  On slow speed lines,
  298.      * if an exit happens with some number of characters still to be
  299.      * written in the terminal driver, one or more characters will 
  300.      * be changed when they finally get drained.  This can be reproduced
  301.      * on a 2400bps line, writing a multi-line buffer on exit using
  302.      * a vt100 type terminal.  It turns out the last char in the
  303.      * escape sequence turning off reverse video was getting changed
  304.      * from 'm' to ' '.  I said it was weird.
  305.      */
  306.     if(ostate.sg_ospeed <= B2400)
  307.       sleep(1);
  308. #endif    /* sv3 || sgi || isc */
  309. #endif    /* POSIX */
  310.     }
  311.  
  312.     return(1);
  313. }
  314.  
  315.  
  316. /*
  317.  * ttspeed - return TRUE if tty line speed < 9600 else return FALSE
  318.  */
  319. ttisslow()
  320. {
  321. #if    defined(POSIX)
  322.     struct termios tty;
  323.  
  324.     return((tcgetattr (1, &tty) == 0) ? cfgetospeed (&tty) < B9600 : FALSE);
  325. #else
  326. #if    defined(sv3) || defined(sgi) || defined(isc)
  327.     struct termio tty;
  328.  
  329.     return((tcgetattr (1, &tty) == 0) ? cfgetospeed (&tty) < B9600 : FALSE);
  330. #else
  331.     struct  sgttyb tty;
  332.  
  333.     return((ioctl(1, TIOCGETP, &tty) == 0) ? tty.sg_ospeed < B9600 : FALSE);
  334. #endif
  335. #endif
  336. }
  337.  
  338.  
  339. /*
  340.  * ttgetwinsz - set global rows and columns values and return
  341.  */
  342. ttgetwinsz()
  343. {
  344. #ifdef TIOCGWINSZ
  345.     struct winsize win;
  346.  
  347.     if (ioctl(0, TIOCGWINSZ, &win) == 0) {
  348.         term.t_ncol = (win.ws_col) ? win.ws_col : 80;
  349.         term.t_nrow = (win.ws_row) ? win.ws_row - 1 : 23;
  350.     }
  351. #endif
  352. }
  353.  
  354.  
  355. /*
  356.  * ttputc - Write a character to the display. 
  357.  */
  358. #ifndef HEBREW
  359. ttputc(c)
  360. {
  361.     return putc(c, stdout);
  362. }
  363. #else
  364. ttputc(c)
  365. {
  366. extern int ttrow,ttcol;
  367. int ret;
  368.  
  369. if(compose_heb && hebmode){      
  370.     ret=stdputc(c);
  371.     (*term.t_move)(ttrow, ttcol+1);
  372. }
  373. else ret=stdputc(c);
  374. return ret;
  375. }
  376.  
  377. stdputc(c)
  378. {
  379.     return (putc(c, stdout));
  380. }
  381. #endif
  382.  
  383.  
  384. /*
  385.  * ttflush - flush terminal buffer. Does real work where the terminal 
  386.  *           output is buffered up. A no-operation on systems where byte 
  387.  *           at a time terminal I/O is done.
  388.  */
  389. ttflush()
  390. {
  391.     return(fflush(stdout));
  392. }
  393.  
  394.  
  395. /*
  396.  * ttgetc - Read a character from the terminal, performing no editing 
  397.  *          and doing no echo at all.
  398.  */
  399. ttgetc()
  400. {
  401.     unsigned char c;
  402.     int i;
  403.  
  404.     if((i = read(0, &c, 1)) <= 0){
  405.     if(i == 0 || errno == EINTR)
  406.       return(NODATA);
  407.     else
  408.       kill(getpid(), SIGHUP);
  409.     }
  410.     else
  411.       return((int)c);
  412. }
  413.  
  414.  
  415. #if    TYPEAH
  416. /* 
  417.  * typahead - Check to see if any characters are already in the
  418.  *          keyboard buffer
  419.  */
  420. typahead()
  421. {
  422.     int x;    /* holds # of pending chars */
  423.  
  424.     return((ioctl(0,FIONREAD,&x) < 0) ? 0 : x);
  425. }
  426. #endif
  427.  
  428.  
  429. /*
  430.  * GetKey - Read in a key.
  431.  * Do the standard keyboard preprocessing. Convert the keys to the internal
  432.  * character set.  Resolves escape sequences and returns no-op if global
  433.  * timeout value exceeded.
  434.  */
  435. GetKey()
  436. {
  437.     int    c;
  438. #ifdef HEBREW
  439.     unsigned char *ptr;
  440. #endif
  441.  
  442.     if(timeout){
  443.     /*
  444.      * simple use of select/poll here to handle requested timeouts
  445.      * while waiting for keyboard input...
  446.      */
  447. #if    defined(ptx) || defined(sv4)
  448.     struct pollfd pollfd;
  449.     int    rv;
  450.  
  451.     pollfd.fd     = 0;
  452.     pollfd.events = POLLIN;
  453.     while((rv = poll(&pollfd, 1, timeout * 1000)) < 0 && errno == EAGAIN)
  454.       ;
  455. #else
  456.     struct timeval ts;
  457.     fd_set readfds;
  458.     int    rv;
  459.  
  460.     FD_ZERO(&readfds);        /* blank out all bits */
  461.     FD_SET(0, &readfds);        /* set stdin's bit */
  462.     ts.tv_sec  = timeout;        /* set the timeout */
  463.     ts.tv_usec = 0;
  464.  
  465.     rv = select(1, &readfds, 0, &readfds, &ts); /* read stdin */
  466. #endif
  467.     if(rv < 0){
  468.         if(errno == EINTR)        /* interrupted? */
  469.           return(NODATA);        /* return like we timed out */
  470.         else
  471.           return(NODATA);        /* BUG: should bomb out? */
  472.     }
  473.     else if(rv == 0)
  474.       return(NODATA);        /* we really did time out */
  475.     }
  476.  
  477.     if ((c = (*term.t_getchar)()) == METACH) { /* Apply M- prefix      */
  478.     int status;
  479.         
  480.     /*
  481.      * this code should intercept special keypad keys
  482.      */
  483.     switch(status = kbseq(&c)){
  484.       case 0 :     /* no dice */
  485.         return(c);
  486.       case  K_PAD_UP        :
  487.       case  K_PAD_DOWN        :
  488.       case  K_PAD_RIGHT        :
  489.       case  K_PAD_LEFT        :
  490.       case  K_PAD_PREVPAGE    :
  491.       case  K_PAD_NEXTPAGE    :
  492.       case  K_PAD_HOME        :
  493.         return(status);
  494.       case F1  :
  495.       case F2  :
  496.       case F3  :
  497.       case F4  :
  498.       case F5  :
  499.       case F6  :
  500.       case F7  :
  501.       case F8  :
  502.       case F9  :
  503.       case F10 :
  504.       case F11 :
  505.       case F12 :
  506.         return(status);
  507.       case BADESC :
  508.         if(c == '\033'){
  509.         c = (*term.t_getchar)();
  510.         if(islower(c))    /* canonicalize c */
  511.           c = toupper(c);
  512.  
  513.         return((isalpha(c) || c == '@' || (c >= '[' && c <= '_'))
  514.                ? (CTRL | c) : c);
  515.         }
  516.  
  517.       default :                /* punt the whole thing    */
  518.         (*term.t_beep)();
  519.         break;
  520.     }
  521.     }
  522.  
  523.     if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
  524.       c = CTRL | (c+'@');
  525. #ifdef HEBREW
  526.       if(compose_heb && (!message_mode || search_mode) && (hebmain!=eng_in_heb)){
  527.     if(c >= 'a' && c <= 'z')c = xlat_tab[c-'a'];
  528.     else{
  529.       ptr = (unsigned char *)strchr(xlat_str1,c);
  530.       if(ptr!=NULL)c = xlat_str2[(int)(ptr-xlat_str1)];
  531.     }
  532.     }
  533. #endif
  534.     return (c);
  535.  
  536. }
  537.  
  538.  
  539.  
  540. /* 
  541.  * kbseq - looks at an escape sequence coming from the keyboard and 
  542.  *         compares it to a trie of known keyboard escape sequences, and
  543.  *         performs the function bound to the escape sequence.
  544.  * 
  545.  *         returns: BADESC, the escaped function, or 0 if not found.
  546.  */
  547. kbseq(c)
  548. int    *c;
  549. {
  550.     register char    b;
  551.     register int    first = 1;
  552.     register struct    KBSTREE    *current = kpadseqs;
  553. #ifdef HEBREW
  554.     register int        firsth = 1;
  555. #endif
  556.  
  557.     if(kpadseqs == NULL)            /* bag it */
  558.       return(BADESC);
  559.  
  560.     while(1){
  561.     *c = b = (*term.t_getchar)();
  562. #ifdef HEBREW    
  563.     if(firsth){
  564.       firsth=0;
  565.       if(strchr(escfuns,b&0xffdf)){
  566.           *c &= 0xffdf;
  567.           *c |= META;
  568.           return 0;
  569.         }
  570.     }
  571. #endif
  572.  
  573.     while(current->value != b){
  574.         if(current->left == NULL){        /* NO MATCH */
  575.         if(first)
  576.           return(BADESC);
  577.         else
  578.           return(0);
  579.         }
  580.         current = current->left;
  581.     }
  582.  
  583.     if(current->down == NULL)        /* match!!!*/
  584.       return(current->func);
  585.     else
  586.       current = current->down;
  587.  
  588.     first = 0;
  589.     }
  590. }
  591.  
  592.  
  593.  
  594. /*
  595.  * alt_editor - fork off an alternate editor for mail message composition 
  596.  *              if one is configured and passed from pine.  If not, only
  597.  *              ask for the editor if advanced user flag is set, and 
  598.  *              suggest environment's EDITOR value as default.
  599.  */
  600. alt_editor(f, n)
  601. {
  602.     char   eb[NLINE];                /* buf holding edit command */
  603.     char   *fn;                    /* tmp holder for file name */
  604.     char   *cp;
  605.     char   *args[MAXARGS];            /* ptrs into edit command */
  606.     char   *writetmp();
  607.     int       child, pid, i, done = 0;
  608.     long   l;
  609. #if    defined(POSIX) || defined(sv3) || defined(COHERENT) || defined(isc)
  610.     int    stat;
  611. #else
  612.     union  wait stat;
  613. #endif
  614.     FILE   *p;
  615.     SIGTYPE (*ohup)(), (*oint)(), (*osize)(), (*ostop)(), (*ostart)();
  616.  
  617.     if(Pmaster == NULL)
  618.       return;
  619.  
  620.     if(gmode&MDSCUR){
  621.     emlwrite("Alternate editor not available in restricted mode", NULL);
  622.     return;
  623.     }
  624.  
  625.     if(Pmaster->alt_ed == NULL){
  626.     if(!(gmode&MDADVN)){
  627.         emlwrite("\007Unknown Command",NULL);
  628.         return;
  629.     }
  630.  
  631.     if(getenv("EDITOR"))
  632.       strcpy(eb, (char *)getenv("EDITOR"));
  633.     else
  634.       *eb = '\0';
  635.  
  636.     while(!done){
  637.         pid = mlreplyd("Which alternate editor ? ", eb, NLINE, QDEFLT);
  638.  
  639.         switch(pid){
  640.           case ABORT:
  641.         return(-1);
  642.           case HELPCH:
  643.         emlwrite("no alternate editor help yet", NULL);
  644.  
  645. /* take sleep and break out after there's help */
  646.         sleep(3);
  647.         break;
  648.           case (CTRL|'L'):
  649.         sgarbf = TRUE;
  650.         update();
  651.         break;
  652.           case TRUE:
  653.           case FALSE:            /* does editor exist ? */
  654.         if(*eb == '\0'){        /* leave silently? */
  655.             mlerase();
  656.             return(-1);
  657.         }
  658.  
  659.         done++;
  660.         break;
  661.           default:
  662.         break;
  663.         }
  664.     }
  665.     }
  666.     else
  667.       strcpy(eb, Pmaster->alt_ed);
  668.  
  669.     if((fn=writetmp(0, 1)) == NULL){        /* get temp file */
  670.     emlwrite("Problem writing temp file for alt editor", NULL);
  671.     return(-1);
  672.     }
  673.  
  674.     strcat(eb, " ");
  675.     strcat(eb, fn);
  676.  
  677.     cp = eb;
  678.     for(i=0; *cp != '\0';i++){            /* build args array */
  679.     if(i < MAXARGS){
  680.         args[i] = NULL;            /* in case we break out */
  681.     }
  682.     else{
  683.         emlwrite("Too many args for command!", NULL);
  684.         return(-1);
  685.     }
  686.  
  687.     while(isspace(*cp))
  688.       if(*cp != '\0')
  689.         cp++;
  690.       else
  691.         break;
  692.  
  693.     args[i] = cp;
  694.  
  695.     while(!isspace(*cp))
  696.       if(*cp != '\0')
  697.         cp++;
  698.       else
  699.         break;
  700.  
  701.     if(*cp != '\0')
  702.       *cp++ = '\0';
  703.     }
  704.  
  705.     args[i] = NULL;
  706.  
  707.     if(Pmaster)
  708.       (*Pmaster->raw_io)(0);            /* turn OFF raw mode */
  709.  
  710.     if(child=fork()){            /* wait for the child to finish */
  711.     ohup = signal(SIGHUP, SIG_IGN);    /* ignore signals for now */
  712.     oint = signal(SIGINT, SIG_IGN);
  713. #ifdef    TIOCGWINSZ
  714.         osize = signal(SIGWINCH, SIG_IGN);
  715. #endif
  716.  
  717. /*
  718.  * BUG - wait should be made non-blocking and mail_pings or something 
  719.  * need to be done in the loop to keep the imap stream alive
  720.  */
  721.     while((pid=(int)wait(&stat)) != child)
  722.       ;
  723.  
  724.     signal(SIGHUP, ohup);    /* restore signals */
  725.     signal(SIGINT, oint);
  726. #ifdef    TIOCGWINSZ
  727.         signal(SIGWINCH, osize);
  728. #endif
  729.     }
  730.     else{                /* spawn editor */
  731.     signal(SIGHUP, SIG_DFL);    /* let editor handle signals */
  732.     signal(SIGINT, SIG_DFL);
  733. #ifdef    TIOCGWINSZ
  734.         signal(SIGWINCH, SIG_DFL);
  735. #endif
  736.     if(execvp(args[0], args) < 0)
  737.       exit(1);
  738.     }
  739.  
  740.     if(Pmaster)
  741.       (*Pmaster->raw_io)(1);        /* turn ON raw mode */
  742.  
  743.     /*
  744.      * replace edited text with new text 
  745.      */
  746.     curbp->b_flag &= ~BFCHG;        /* make sure old text gets blasted */
  747.     readin(fn, 0);            /* read new text overwriting old */
  748.     unlink(fn);                /* blast temp file */
  749.     curbp->b_flag |= BFCHG;        /* mark dirty for packbuf() */
  750.     ttopen();                /* reset the signals */
  751.     refresh(0, 1);            /* redraw */
  752. }
  753.  
  754.  
  755.  
  756. /*
  757.  *  bktoshell - suspend and wait to be woken up
  758.  */
  759. bktoshell()        /* suspend MicroEMACS and wait to wake up */
  760. {
  761. #ifdef    SIGTSTP
  762.     int pid;
  763.  
  764.     if(!(gmode&MDSSPD)){
  765.     emlwrite("\007Unknown command: ^Z", NULL);
  766.     return;
  767.     }
  768.  
  769.     if(Pmaster){
  770.     (*Pmaster->raw_io)(0);    /* actually in pine source */
  771.  
  772.     movecursor(term.t_nrow, 0);
  773.     printf("\n\n\nUse \"fg\" to return to Pine\n");
  774.  
  775.     }
  776.     else
  777.       vttidy();
  778.  
  779.     movecursor(term.t_nrow, 0);
  780.     peeol();
  781.     (*term.t_flush)();
  782.  
  783.     signal(SIGCONT, rtfrmshell);    /* prepare to restart */
  784.     signal(SIGTSTP, SIG_DFL);            /* prepare to stop */
  785.     kill(0, SIGTSTP);
  786. #endif
  787. }
  788.  
  789.  
  790. /* 
  791.  * rtfrmshell - back from shell, fix modes and return
  792.  */
  793. SIGTYPE
  794. rtfrmshell()
  795. {
  796. #ifdef    SIGCONT
  797.     signal(SIGCONT, SIG_DFL);
  798.  
  799.     if(Pmaster){
  800.     (*Pmaster->raw_io)(1);            /* actually in pine source */
  801.     (*Pmaster->keybinit)(gmode&MDFKEY);    /* using f-keys? */
  802.     }
  803.  
  804.     ttopen();
  805.  
  806. #ifdef    TIOCGWINSZ
  807.     {
  808.     struct winsize win;
  809.     extern int resize_pico();
  810.  
  811.     /*
  812.      * refit pico of window size changed....
  813.      */
  814.     if (ioctl(0, TIOCGWINSZ, &win) == 0) {
  815.         if (win.ws_col && win.ws_row)
  816.           resize_pico(win.ws_row - 1, win.ws_col);
  817.     }
  818.     }
  819. #endif
  820.  
  821.     sgarbf = TRUE;
  822.     curwp->w_flag = WFHARD;
  823.     refresh(0, 1);
  824. #endif
  825. }
  826.  
  827.  
  828.  
  829. /*
  830.  * do_hup_signal - jump back in the stack to where we can handle this
  831.  */
  832. SIGTYPE
  833. do_hup_signal()
  834. {
  835.     if(Pmaster){
  836.     extern jmp_buf finstate;
  837.  
  838.     signal(SIGHUP, SIG_IGN);         /* don't bother us. */
  839.     signal(SIGTERM, SIG_IGN);
  840.     longjmp(finstate, COMP_GOTHUP);
  841.     }
  842.     else{
  843.     /*
  844.      * if we've been interrupted and the buffer is changed,
  845.      * save it...
  846.      */
  847.     if(anycb() == TRUE){            /* time to save */
  848.         if(curbp->b_fname[0] == '\0'){    /* name it */
  849.         strcpy(curbp->b_fname, "pico.save");
  850.         }
  851.         else{
  852.         strcat(curbp->b_fname, ".save");
  853.         }
  854.         writeout(curbp->b_fname);
  855.     }
  856.     vttidy();
  857.     exit(1);
  858.     }
  859. }
  860.  
  861.  
  862. /*
  863.  * big bitmap of ASCII characters allowed in a file name
  864.  * (needs reworking for other char sets)
  865.  */
  866. unsigned char okinfname[32] = {
  867.       0,    0,             /* ^@ - ^G, ^H - ^O  */
  868.       0,    0,            /* ^P - ^W, ^X - ^_  */
  869.       0,    0x17,        /* SP - ' ,  ( - /   */
  870.       0xff, 0xc0,        /*  0 - 7 ,  8 - ?   */
  871.       0x7f, 0xff,        /*  @ - G ,  H - O   */
  872.       0xff, 0xe1,        /*  P - W ,  X - _   */
  873.       0x7f, 0xff,        /*  ` - g ,  h - o   */
  874.       0xff, 0xf6,        /*  p - w ,  x - DEL */
  875.       0,    0,             /*  > DEL   */
  876.       0,    0,            /*  > DEL   */
  877.       0,    0,             /*  > DEL   */
  878.       0,    0,             /*  > DEL   */
  879.       0,    0             /*  > DEL   */
  880. };
  881.  
  882.  
  883. /*
  884.  * fallowc - returns TRUE if c is allowable in filenames, FALSE otw
  885.  */
  886. fallowc(c)
  887. char c;
  888. {
  889.     return(okinfname[c>>3] & 0x80>>(c&7));
  890. }
  891.  
  892.  
  893. /*
  894.  * fexist - returns TRUE if the file exists with mode passed in m, 
  895.  *          FALSE otherwise.  By side effect returns length of file in l
  896.  */
  897. fexist(file, m, l)
  898. char *file;
  899. char *m;                    /* files mode: r, w or rw */
  900. long *l;
  901. {
  902.     struct stat    sbuf;
  903.  
  904.     if(l)
  905.       *l = 0L;
  906.  
  907.     if(stat(file, &sbuf) < 0){
  908.     switch(errno){
  909.       case ENOENT :                /* File not found */
  910.         return(FIOFNF);
  911. #ifdef    ENAMETOOLONG
  912.       case ENAMETOOLONG :            /* Name is too long */
  913.         return(FIOLNG);
  914. #endif
  915.       default:                /* Some other error */
  916.         return(FIOERR);
  917.     }
  918.     }
  919.  
  920.     if(l)
  921.       *l = sbuf.st_size;
  922.  
  923.     if((sbuf.st_mode&S_IFMT) == S_IFDIR)
  924.       return(FIODIR);
  925.  
  926.     if(m[0] == 'r')                /* read access? */
  927.       return((S_IREAD&sbuf.st_mode) ? FIOSUC : FIONRD);
  928.     else if(m[0] == 'w')            /* write access? */
  929.       return((S_IWRITE&sbuf.st_mode) ? FIOSUC : FIONWT);
  930.     else if(m[0] == 'x')            /* execute access? */
  931.       return((S_IEXEC&sbuf.st_mode) ? FIOSUC : FIONEX);
  932.     return(FIOERR);                /* what? */
  933. }
  934.  
  935.  
  936. /*
  937.  * isdir - returns true if fn is a readable directory, false otherwise
  938.  *         silent on errors (we'll let someone else notice the problem;)).
  939.  */
  940. isdir(fn, l)
  941. char *fn;
  942. long *l;
  943. {
  944.     struct stat sbuf;
  945.  
  946.     if(l)
  947.       *l = 0;
  948.  
  949.     if(stat(fn, &sbuf) < 0)
  950.       return(0);
  951.  
  952.     if(l)
  953.       *l = sbuf.st_size;
  954.     return((sbuf.st_mode&S_IFMT) == S_IFDIR);
  955. }
  956.  
  957.  
  958. #if    defined(bsd) || defined(nxt) || defined(dyn)
  959. /*
  960.  * getcwd - NeXT uses getwd()
  961.  */
  962. char *
  963. getcwd(pth, len)
  964. char *pth;
  965. int   len;
  966. {
  967.     extern char *getwd();
  968.  
  969.     return(getwd(pth));
  970. }
  971. #endif
  972.  
  973.  
  974. /*
  975.  * gethomedir - returns the users home directory
  976.  *              Note: home is malloc'd for life of pico
  977.  */
  978. char *
  979. gethomedir(l)
  980. int *l;
  981. {
  982.     static char *home = NULL;
  983.     static short hlen = 0;
  984.  
  985.     if(home == NULL){
  986.     strcpy(s, "~");
  987.     fixpath(s, NLINE);        /* let fixpath do the work! */
  988.     hlen = strlen(s);
  989.     if((home=(char *)malloc((strlen(s) + 1) * sizeof(char))) == NULL){
  990.         emlwrite("Problem allocating space for home dir", NULL);
  991.         return(0);
  992.     }
  993.     strcpy(home, s);
  994.     }
  995.  
  996.     if(l)
  997.       *l = hlen;
  998.  
  999.     return(home);
  1000. }
  1001.  
  1002.  
  1003. /*
  1004.  * homeless - returns true if given file does not reside in the current
  1005.  *            user's home directory tree. 
  1006.  */
  1007. homeless(f)
  1008. char *f;
  1009. {
  1010.     char *home;
  1011.     int   len;
  1012.  
  1013.     home = gethomedir(&len);
  1014.     return(strncmp(home, f, len));
  1015. }
  1016.  
  1017.  
  1018.  
  1019. /*
  1020.  * errstr - return system error string corresponding to given errno
  1021.  *          Note: strerror() is not provided on all systems, so it's 
  1022.  *          done here once and for all.
  1023.  */
  1024. char *
  1025. errstr(err)
  1026. int err;
  1027. {
  1028.     extern char *sys_errlist[];
  1029.     extern int  sys_nerr;
  1030.  
  1031.     return((err >= 0 && err < sys_nerr) ? sys_errlist[err] : NULL);
  1032. }
  1033.  
  1034.  
  1035.  
  1036. /*
  1037.  * getfnames - return all file names in the given directory in a single 
  1038.  *             malloc'd string.  n contains the number of names
  1039.  */
  1040. char *
  1041. getfnames(dn, n)
  1042. char *dn;
  1043. int  *n;
  1044. {
  1045.     long           l;
  1046.     char          *names, *np, *p;
  1047.     struct stat    sbuf;
  1048. #if    defined(ct)
  1049.     FILE          *dirp;
  1050.     char           fn[DIRSIZ+1];
  1051. #else
  1052.     DIR           *dirp;            /* opened directory */
  1053. #endif
  1054. #if    defined(POSIX) || defined(aix) || defined(COHERENT) || defined(isc) || defined(sv3)
  1055.     struct dirent *dp;
  1056. #else
  1057.     struct direct *dp;
  1058. #endif
  1059.  
  1060.     *n = 0;
  1061.  
  1062.     if(stat(dn, &sbuf) < 0){
  1063.     switch(errno){
  1064.       case ENOENT :                /* File not found */
  1065.         emlwrite("\007File not found: \"%s\"", dn);
  1066.         break;
  1067. #ifdef    ENAMETOOLONG
  1068.       case ENAMETOOLONG :            /* Name is too long */
  1069.         emlwrite("\007File name too long: \"%s\"", dn);
  1070.         break;
  1071. #endif
  1072.       default:                /* Some other error */
  1073.         emlwrite("\007Error getting file info: \"%s\"", dn);
  1074.         break;
  1075.     }
  1076.     return(NULL);
  1077.     } 
  1078.     else{
  1079.     l = sbuf.st_size;
  1080.     if((sbuf.st_mode&S_IFMT) != S_IFDIR){
  1081.         emlwrite("\007Not a directory: \"%s\"", dn);
  1082.         return(NULL);
  1083.     }
  1084.     }
  1085.  
  1086.     if((names=(char *)malloc(sizeof(char)*l)) == NULL){
  1087.     emlwrite("\007Can't malloc space for file names", NULL);
  1088.     return(NULL);
  1089.     }
  1090.  
  1091.     errno = 0;
  1092.     if((dirp=opendir(dn)) == NULL){
  1093.     sprintf(s,"\007Can't open \"%s\": %s", dn, errstr(errno));
  1094.     emlwrite(s, NULL);
  1095.     free((char *)names);
  1096.     return(NULL);
  1097.     }
  1098.  
  1099.     np = names;
  1100.  
  1101. #if    defined(ct)
  1102.     while(fread(&dp, sizeof(struct direct), 1, dirp) > 0) {
  1103.     /* skip empty slots with inode of 0 */
  1104.     if(dp.d_ino == 0)
  1105.          continue;
  1106.     (*n)++;                     /* count the number of active slots */
  1107.     (void)strncpy(fn, dp.d_name, DIRSIZ);
  1108.     fn[14] = '\0';
  1109.     p = fn;
  1110.     while((*np++ = *p++) != '\0')
  1111.       ;
  1112.     }
  1113. #else
  1114.     while((dp = readdir(dirp)) != NULL){
  1115.     (*n)++;
  1116.     p = dp->d_name;
  1117.     while((*np++ = *p++) != '\0')
  1118.       ;
  1119.     }
  1120. #endif
  1121.  
  1122.     closedir(dirp);                    /* shut down */
  1123.     return(names);
  1124. }
  1125.  
  1126.  
  1127. /*
  1128.  * fioperr - given the error number and file name, display error
  1129.  */
  1130. void
  1131. fioperr(e, f)
  1132. int  e;
  1133. char *f;
  1134. {
  1135.     switch(e){
  1136.       case FIOFNF:                /* File not found */
  1137.     emlwrite("\007File \"%s\" not found", f);
  1138.     break;
  1139.       case FIOEOF:                /* end of file */
  1140.     emlwrite("\007End of file \"%s\" reached", f);
  1141.     break;
  1142.       case FIOLNG:                /* name too long */
  1143.     emlwrite("\007File name \"%s\" too long", f);
  1144.     break;
  1145.       case FIODIR:                /* file is a directory */
  1146.     emlwrite("\007File \"%s\" is a directory", f);
  1147.     break;
  1148.       case FIONWT:
  1149.     emlwrite("\007Write permission denied: %s", f);
  1150.     break;
  1151.       case FIONRD:
  1152.     emlwrite("\007Read permission denied: %s", f);
  1153.     break;
  1154.       case FIONEX:
  1155.     emlwrite("\007Execute permission denied: %s", f);
  1156.     break;
  1157.       default:
  1158.     emlwrite("\007File I/O error: %s", f);
  1159.     }
  1160. }
  1161.  
  1162.  
  1163.  
  1164. /*
  1165.  * pfnexpand - pico's function to expand the given file name if there is 
  1166.  *           a leading '~'
  1167.  */
  1168. char *pfnexpand(fn, len)
  1169. char *fn;
  1170. int  len;
  1171. {
  1172.     struct passwd *pw;
  1173.     register char *x, *y, *z;
  1174.     char name[20];
  1175.     
  1176.     if(*fn == '~') {
  1177.         for(x = fn+1, y = name; *x != '/' && *x != '\0'; *y++ = *x++);
  1178.         *y = '\0';
  1179.         if(x == fn + 1) 
  1180.           pw = getpwuid(getuid());
  1181.         else
  1182.           pw = getpwnam(name);
  1183.         if(pw == NULL)
  1184.           return(NULL);
  1185.         if(strlen(pw->pw_dir) + strlen(fn) > len) {
  1186.             return(NULL);
  1187.         }
  1188.     /* make room for expanded path */
  1189.     for(z=x+strlen(x),y=fn+strlen(x)+strlen(pw->pw_dir);
  1190.         z >= x;
  1191.         *y-- = *z--);
  1192.     /* and insert the expanded address */
  1193.     for(x=fn,y=pw->pw_dir; *y != '\0'; *x++ = *y++);
  1194.     }
  1195.     return(fn);
  1196. }
  1197.  
  1198.  
  1199.  
  1200. /*
  1201.  * fixpath - make the given pathname into an absolute path
  1202.  */
  1203. fixpath(name, len)
  1204. char *name;
  1205. int  len;
  1206. {
  1207.     register char *shft;
  1208.  
  1209.     if(*name != '/' && *name != '.'){        /* filenames relative to ~ */
  1210.     if(Pmaster && (*name != '~' && strlen(name)+2 <= len)){
  1211.  
  1212.         for(shft = strchr(name, '\0'); shft >= name; shft--)
  1213.           shft[2] = *shft;
  1214.  
  1215.         name[0] = '~';
  1216.         name[1] = '/';
  1217.     }
  1218.  
  1219.     pfnexpand(name, len);
  1220.     }
  1221. }
  1222.  
  1223.  
  1224. /*
  1225.  * compresspath - given a base path and an additional directory, collapse
  1226.  *                ".." and "." elements and return absolute path (appending
  1227.  *                base if necessary).  
  1228.  *
  1229.  *                returns  1 if OK, 
  1230.  *                         0 if there's a problem
  1231.  *                         new path, by side effect, if things went OK
  1232.  */
  1233. compresspath(base, path, len)
  1234. char *base, *path;
  1235. int  len;
  1236. {
  1237.     register int i;
  1238.     int  depth = 0;
  1239.     char *p;
  1240.     char *stack[32];
  1241.  
  1242. #define PUSHD(X)  (stack[depth++] = X)
  1243. #define POPD()    ((depth > 0) ? stack[--depth] : "")
  1244.  
  1245.     if(*path == '~'){
  1246.     fixpath(path, len);
  1247.     strcpy(s, path);
  1248.     }
  1249.     else if(*path != C_FILESEP)
  1250.       sprintf(s, "%s%c%s", base, C_FILESEP, path);
  1251.     else
  1252.       strcpy(s, path);
  1253.  
  1254.     p = s;
  1255.     for(i=0; s[i] != '\0'; i++){        /* pass thru path name */
  1256.     if(s[i] == '/'){
  1257.         if(p != s)
  1258.           PUSHD(p);                /* push dir entry */
  1259.         p = &s[i+1];            /* advance p */
  1260.         s[i] = '\0';            /* cap old p off */
  1261.         continue;
  1262.     }
  1263.  
  1264.     if(s[i] == '.'){            /* special cases! */
  1265.         if(s[i+1] == '.'            /* parent */
  1266.            && (s[i+2] == '/' || s[i+2] == '\0')){
  1267.         if(!strcmp(POPD(),""))        /* bad news! */
  1268.           return(0);
  1269.  
  1270.         i += 2;
  1271.         p = (s[i] == '\0') ? "" : &s[i+1];
  1272.         }
  1273.         else if(s[i+1] == '/' || s[i+1] == '\0'){        /* no op */
  1274.         i++;
  1275.         p = (s[i] == '\0') ? "" : &s[i+1];
  1276.         }
  1277.     }
  1278.     }
  1279.  
  1280.     if(*p != '\0')
  1281.       PUSHD(p);                    /* get last element */
  1282.  
  1283.     path[0] = '\0';
  1284.     for(i = 0; i < depth; i++){
  1285.     strcat(path, S_FILESEP);
  1286.     strcat(path, stack[i]);
  1287.     }
  1288.  
  1289.     return(1);                    /* everything's ok */
  1290. }
  1291.  
  1292.  
  1293. /*
  1294.  * tmpname - return a temporary file name in the given buffer
  1295.  */
  1296. void
  1297. tmpname(name)
  1298. char *name;
  1299. {
  1300.     sprintf(name, "/tmp/pico.%d", getpid());    /* tmp file name */
  1301. }
  1302.  
  1303.  
  1304. /*
  1305.  * Take a file name, and from it
  1306.  * fabricate a buffer name. This routine knows
  1307.  * about the syntax of file names on the target system.
  1308.  * I suppose that this information could be put in
  1309.  * a better place than a line of code.
  1310.  */
  1311. void
  1312. makename(bname, fname)
  1313. char    bname[];
  1314. char    fname[];
  1315. {
  1316.     register char   *cp1;
  1317.     register char   *cp2;
  1318.  
  1319.     cp1 = &fname[0];
  1320.     while (*cp1 != 0)
  1321.       ++cp1;
  1322.  
  1323.     while (cp1!=&fname[0] && cp1[-1]!='/')
  1324.       --cp1;
  1325.  
  1326.     cp2 = &bname[0];
  1327.     while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
  1328.       *cp2++ = *cp1++;
  1329.  
  1330.     *cp2 = 0;
  1331. }
  1332.  
  1333.  
  1334. /*
  1335.  * copy - copy contents of file 'a' into a file named 'b'.  Return error
  1336.  *        if either isn't accessible or is a directory
  1337.  */
  1338. copy(a, b)
  1339. char *a, *b;
  1340. {
  1341.     int    in, out, n, rv = 0;
  1342.     char   *cb;
  1343.     struct stat tsb, fsb;
  1344.     extern int  errno;
  1345.  
  1346.     if(stat(a, &fsb) < 0){        /* get source file info */
  1347.     emlwrite("Can't Copy: %s", errstr(errno));
  1348.     return(-1);
  1349.     }
  1350.  
  1351.     if(!(fsb.st_mode&S_IREAD)){        /* can we read it? */
  1352.     emlwrite("\007Read permission denied: %s", a);
  1353.     return(-1);
  1354.     }
  1355.  
  1356.     if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */
  1357.     emlwrite("\007Can't copy: %s is a directory", a);
  1358.     return(-1);
  1359.     }
  1360.  
  1361.     if(stat(b, &tsb) < 0){        /* get dest file's mode */
  1362.     switch(errno){
  1363.       case ENOENT:
  1364.         break;            /* these are OK */
  1365.       default:
  1366.         emlwrite("\007Can't Copy: %s", errstr(errno));
  1367.         return(-1);
  1368.     }
  1369.     }
  1370.     else{
  1371.     if(!(tsb.st_mode&S_IWRITE)){    /* can we write it? */
  1372.         emlwrite("\007Write permission denied: %s", b);
  1373.         return(-1);
  1374.     }
  1375.  
  1376.     if((tsb.st_mode&S_IFMT) == S_IFDIR){    /* is it directory? */
  1377.         emlwrite("\007Can't copy: %s is a directory", b);
  1378.         return(-1);
  1379.     }
  1380.  
  1381.     if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){
  1382.         emlwrite("\007Identical files.  File not copied", NULL);
  1383.         return(-1);
  1384.     }
  1385.     }
  1386.  
  1387.     if((in = open(a, O_RDONLY)) < 0){
  1388.     emlwrite("Copy Failed: %s", errstr(errno));
  1389.     return(-1);
  1390.     }
  1391.  
  1392.     if((out=creat(b, fsb.st_mode&0xfff)) < 0){
  1393.     emlwrite("Can't Copy: %s", errstr(errno));
  1394.     close(in);
  1395.     return(-1);
  1396.     }
  1397.  
  1398.     if((cb = (char *)malloc(NLINE*sizeof(char))) == NULL){
  1399.     emlwrite("Can't allocate space for copy buffer!", NULL);
  1400.     close(in);
  1401.     close(out);
  1402.     return(-1);
  1403.     }
  1404.  
  1405.     while(1){                /* do the copy */
  1406.     if((n = read(in, cb, NLINE)) < 0){
  1407.         emlwrite("Can't Read Copy: %s", errstr(errno));
  1408.         rv = -1;
  1409.         break;            /* get out now */
  1410.     }
  1411.  
  1412.     if(n == 0)            /* done! */
  1413.       break;
  1414.  
  1415.     if(write(out, cb, n) != n){
  1416.         emlwrite("Can't Write Copy: %s", errstr(errno));
  1417.         rv = -1;
  1418.         break;
  1419.     }
  1420.     }
  1421.  
  1422.     free(cb);
  1423.     close(in);
  1424.     close(out);
  1425.     return(rv);
  1426. }
  1427.  
  1428.  
  1429. /*
  1430.  * Open a file for writing. Return TRUE if all is well, and FALSE on error
  1431.  * (cannot create).
  1432.  */
  1433. ffwopen(fn)
  1434. char    *fn;
  1435. {
  1436.     extern FILE *ffp;
  1437.  
  1438.     if ((ffp=fopen(fn, "w")) == NULL) {
  1439.         emlwrite("Cannot open file for writing", NULL);
  1440.         return (FIOERR);
  1441.     }
  1442.  
  1443.     return (FIOSUC);
  1444. }
  1445.  
  1446.  
  1447. /*
  1448.  * Close a file. Should look at the status in all systems.
  1449.  */
  1450. ffclose()
  1451. {
  1452.     extern FILE *ffp;
  1453.  
  1454.     if (fclose(ffp) != FALSE) {
  1455.         emlwrite("Error closing file", NULL);
  1456.         return(FIOERR);
  1457.     }
  1458.  
  1459.     return(FIOSUC);
  1460. }
  1461.  
  1462.  
  1463. /*
  1464.  * P_open - run the given command in a sub-shell returning a file pointer
  1465.  *        from which to read the output
  1466.  *
  1467.  * note:
  1468.  *    For OS's other than unix, you will have to rewrite this function.
  1469.  *    Hopefully it'll be easy to exec the command into a temporary file, 
  1470.  *    and return a file pointer to that opened file or something.
  1471.  */
  1472. FILE *P_open(s)
  1473. char *s;
  1474. {
  1475.     return(popen(s, "r"));
  1476. }
  1477.  
  1478.  
  1479.  
  1480. /*
  1481.  * P_close - close the given descriptor
  1482.  *
  1483.  */
  1484. P_close(fp)
  1485. FILE *fp;
  1486. {
  1487.     return(pclose(fp));
  1488. }
  1489.  
  1490.  
  1491.  
  1492. /*
  1493.  * worthit - generic sort of test to roughly gage usefulness of using 
  1494.  *           optimized scrolling.
  1495.  *
  1496.  * note:
  1497.  *    returns the line on the screen, l, that the dot is currently on
  1498.  */
  1499. worthit(l)
  1500. int *l;
  1501. {
  1502.     int i;            /* l is current line */
  1503.     unsigned below;        /* below is avg # of ch/line under . */
  1504.  
  1505.     *l = doton(&i, &below);
  1506.     below = (i > 0) ? below/(unsigned)i : 0;
  1507.  
  1508.     return(below > 3);
  1509. }
  1510.  
  1511.  
  1512.  
  1513. /*
  1514.  * pico_new_mail - just checks mtime and atime of mail file and notifies user 
  1515.  *               if it's possible that they have new mail.
  1516.  */
  1517. pico_new_mail()
  1518. {
  1519.     int ret = 0;
  1520.     static time_t lastchk = 0;
  1521.     struct stat sbuf;
  1522.     char   inbox[256], *p;
  1523.  
  1524.     if(p = (char *)getenv("MAIL"))
  1525.       sprintf(inbox, p);
  1526.     else
  1527.       sprintf(inbox,"%s/%s", MAILDIR, getlogin());
  1528.  
  1529.     if(stat(inbox, &sbuf) == 0){
  1530.     ret = sbuf.st_atime <= sbuf.st_mtime &&
  1531.       (lastchk < sbuf.st_mtime && lastchk < sbuf.st_atime);
  1532.     lastchk = sbuf.st_mtime;
  1533.     return(ret);
  1534.     }
  1535.     else
  1536.       return(ret);
  1537. }
  1538.  
  1539.  
  1540.  
  1541. /*
  1542.  * time_to_check - checks the current time against the last time called 
  1543.  *                 and returns true if the elapsed time is > timeout
  1544.  */
  1545. time_to_check()
  1546. {
  1547.     static time_t lasttime = 0L;
  1548.  
  1549.     if(!timeout)
  1550.       return(FALSE);
  1551.  
  1552.     if(time((long *) 0) - lasttime > (time_t)timeout){
  1553.     lasttime = time((long *) 0);
  1554.     return(TRUE);
  1555.     }
  1556.     else
  1557.       return(FALSE);
  1558. }
  1559.  
  1560.  
  1561. /*
  1562.  * sstrcasecmp - compare two pointers to strings case independently
  1563.  */
  1564. sstrcasecmp(s1, s2)
  1565. QcompType *s1, *s2;
  1566. {
  1567.     register char *a, *b;
  1568.  
  1569.     a = *(char **)s1;
  1570.     b = *(char **)s2;
  1571.     while(toupper(*a) == toupper(*b++))
  1572.     if(*a++ == '\0')
  1573.       return(0);
  1574.  
  1575.     return(toupper(*a) - toupper(*--b));
  1576. }
  1577.  
  1578.  
  1579. #ifdef    TIOCGWINSZ
  1580. /*
  1581.  * winch_handler - handle window change signal
  1582.  */
  1583. SIGTYPE winch_handler()
  1584. {
  1585.     struct winsize win;
  1586.     extern int resize_pico();
  1587.  
  1588.     signal(SIGWINCH, winch_handler);
  1589.  
  1590.     if (ioctl(0, TIOCGWINSZ, &win) == 0) {
  1591.     if (win.ws_col && win.ws_row)
  1592.       resize_pico(win.ws_row - 1, win.ws_col);
  1593.     }
  1594. }
  1595. #endif    /* TIOCGWINSZ */
  1596.  
  1597.  
  1598. #if    defined(sv3) || defined(ct)
  1599. /* Placed by rll to handle the rename function not found in AT&T */
  1600. rename(oldname, newname)
  1601.     char *oldname;
  1602.     char *newname;
  1603. {
  1604.     int rtn;
  1605.  
  1606.     if ((rtn = link(oldname, newname)) != 0) {
  1607.     perror("Was not able to rename file.");
  1608.     return(rtn);
  1609.     }
  1610.  
  1611.     if ((rtn = unlink(oldname)) != 0)
  1612.       perror("Was not able to unlink file.");
  1613.  
  1614.     return(rtn);
  1615. }
  1616. #endif
  1617.