home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / jove-4.16-src.tgz / tar.out / bsd / jove / jove.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  38KB  |  1,772 lines

  1. /************************************************************************
  2.  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
  3.  * provided to you without charge, and with no warranty.  You may give  *
  4.  * away copies of JOVE, including sources, provided that this notice is *
  5.  * included in all the files.                                           *
  6.  ************************************************************************/
  7.  
  8. /* Contains the main loop initializations, and some system dependent
  9.    type things, e.g. putting terminal in CBREAK mode, etc. */
  10.  
  11. #include "jove.h"
  12. #include "fp.h"
  13. #include "jctype.h"
  14. #include "chars.h"
  15. #include "disp.h"
  16. #include "re.h"    /* for dosearch() */
  17. #include "reapp.h"    /* for find_tag(), UseRE */
  18. #include "sysprocs.h"
  19. #include "rec.h"
  20. #include "ask.h"
  21. #include "extend.h"
  22. #include "fmt.h"
  23. #include "macros.h"
  24. #include "marks.h"
  25. #include "mouse.h"
  26. #ifndef MAC    /* Mac does without! */
  27. # include "paths.h"
  28. #endif
  29. #include "proc.h"
  30. #include "screen.h"
  31. #include "term.h"
  32. #include "version.h"
  33. #include "wind.h"
  34.  
  35. #ifdef IPROCS
  36. # include "iproc.h"
  37. #endif
  38.  
  39. #ifdef USE_SELECT
  40. #  include <sys/time.h>
  41. #  include "select.h"
  42. #endif
  43.  
  44. #ifdef SCO    /* ??? what is this for? */
  45. # include <sys/stream.h>
  46. # include <sys/ptem.h>
  47. #endif
  48.  
  49. #include <signal.h>
  50. #include <errno.h>
  51.  
  52. #ifdef MAC
  53. # include "mac.h"
  54. #else /* !MAC */
  55. # include <sys/stat.h>
  56. #endif /* !MAC */
  57.  
  58. #ifdef MSDOS
  59. # include <bios.h>
  60. # include <dos.h>
  61. # include <time.h>
  62. # include <process.h>
  63. # define SIGHUP    99
  64. # if defined(__WATCOMC__)
  65. #  include <malloc.h>    /* for _heapgrow */
  66. # endif
  67. #endif /* MSDOS */
  68.  
  69. #ifdef WIN32
  70. # include <windows.h>    /* ??? is this needed? */
  71. # undef FIONREAD     /* This is defined but ioctl isn't so we cannot use it. */
  72. #endif
  73.  
  74. #ifdef STACK_DECL    /* provision for setting up appropriate stack */
  75. STACK_DECL
  76. #endif
  77.  
  78. private void
  79.     UnsetTerm proto((bool)),
  80.     DoKeys proto((bool firsttime)),
  81.     ShowKeyStrokes proto((void));
  82.  
  83. #ifdef NONBLOCKINGREAD
  84. private void    setblock proto((bool on));
  85. #endif
  86.  
  87. #ifdef POSIX_SIGS
  88. SIGHANDLERTYPE
  89. setsighandler(signo, handler)    /* simulate BSD's safe signal() */
  90. int    signo;
  91. SIGHANDLERTYPE    handler;
  92. {
  93.     static struct sigaction    act;    /* static so unspecified fields are 0 */
  94.     struct sigaction    oact;
  95.  
  96.     act.sa_handler = handler;
  97.     sigemptyset(&act.sa_mask);
  98.     sigaddset(&act.sa_mask, signo);
  99.     sigaction(signo, &act, &oact);
  100.     return oact.sa_handler;
  101. }
  102. #endif
  103.  
  104. bool    TimeDisplayed = YES;    /* is time actually displayed in modeline? */
  105.  
  106. #ifdef UNIX
  107.  
  108. /* set things up to update the modeline every UpdFreq seconds */
  109.  
  110. int    UpdFreq = 30;    /* VAR: how often to update modeline */
  111. bool    InSlowRead = NO;
  112.  
  113. void
  114. SetClockAlarm(unset)
  115. bool    unset;    /* unset alarm if none needed */
  116. {
  117.     if (TimeDisplayed && UpdFreq != 0)
  118.         (void) alarm((unsigned) (UpdFreq - (time((time_t *)NULL) % UpdFreq)));
  119.     else if (unset)
  120.         alarm((unsigned)0);
  121. }
  122.  
  123. /* AlarmHandler gets all SIGALRMs.  It decides, based on InWaitChar,
  124.  * whether this is an alarm for updating the mode line or for showing
  125.  * keystrokes.  Theoretically, InWaitChar is a state variable that ought
  126.  * to be reset in complain and other exceptional cases, but waitchar
  127.  * is called often enough that it turns out to be self-correcting.
  128.  */
  129.  
  130. private volatile bool    InWaitChar = NO;
  131.  
  132. /*ARGSUSED*/
  133. private SIGRESTYPE
  134. AlarmHandler(junk)
  135. int    junk;    /* passed in on signal; of no interest */
  136. {
  137.     int save_errno = errno;    /* Subtle, but necessary! */
  138.  
  139.     resetsighandler(SIGALRM, AlarmHandler);
  140.     if (InWaitChar) {
  141.         if (InSlowRead) {
  142.             InSlowRead = NO;
  143.             ShowKeyStrokes();
  144.             redisplay();
  145.             InSlowRead = YES;
  146.             InWaitChar = NO;    /* might as well allow modeline updates */
  147.             SetClockAlarm(NO);
  148.         } else {
  149.             alarm((unsigned)1);    /* try again later */
  150.         }
  151.     } else {
  152.         UpdModLine = YES;
  153.         if (InSlowRead) {
  154.             /* needed because of stupid BSD restartable I/O */
  155.             InSlowRead = NO;
  156.             redisplay();
  157.             InSlowRead = YES;
  158.         }
  159.         SetClockAlarm(NO);
  160.     }
  161.     errno = save_errno;
  162.     return SIGRESVALUE;
  163. }
  164.  
  165. #endif /* UNIX */
  166.  
  167. bool    stickymsg;    /* the last message should stick around */
  168.  
  169. char    NullStr[] = "";
  170. jmp_buf    mainjmp;
  171.  
  172. #ifdef USE_SELECT
  173. fd_set    global_fd;    /* set of file descriptors of interest (for select) */
  174. int    global_maxfd;
  175. #endif
  176.  
  177. /* paths */
  178.  
  179. /* path of machine-independent library */
  180. #ifdef SHAREDIR
  181. char    ShareDir[FILESIZE] = SHAREDIR;
  182. #else
  183. char    ShareDir[FILESIZE];
  184. #endif
  185.  
  186. /* VAR: directory/device to store tmp files */
  187. #ifdef TMPDIR
  188. char    TmpDir[FILESIZE] = TMPDIR;
  189. #else
  190. char    TmpDir[FILESIZE];
  191. #endif
  192.  
  193. #ifdef SUBSHELL
  194. char
  195.     Shell[FILESIZE] = DFLTSHELL,    /* VAR: shell to use */
  196. # ifdef MSFILESYSTEM
  197.     ShFlags[sizeof(ShFlags)] = "/c";    /* VAR: flags to shell */
  198. # else
  199.     ShFlags[sizeof(ShFlags)] = "-c";    /* VAR: flags to shell */
  200. # endif /* MSFILESYSTEM */
  201. #endif
  202.  
  203. /* LibDir: path of machine-dependent library (for Portsrv and Recover) */
  204.  
  205. #if defined(SUBSHELL) || defined(PIPEPROCS)
  206. # define NEED_LIBDIR    1
  207. private char    LibDir[FILESIZE] = LIBDIR;
  208. #endif
  209.  
  210. /* finish: handle bad-news signals.
  211.  * It also handles internally generated requests for termination.
  212.  * For most values of code (signal types) an attempt
  213.  * is made to save the buffers for recovery by "recover".
  214.  * For most values of code, this routine stops JOVE
  215.  * by calling abort, which causes a core dump under UNIX.
  216.  *
  217.  * - code -1 is an internally generated request to die with an
  218.  *   attempt to save the buffers.  It had better not be the code
  219.  *   for some real signal (it cannot be one under UNIX).
  220.  *
  221.  * - code 0 is an internally generated request to die quietly.
  222.  *   It had better not be the code for some real signal
  223.  *   (it cannot be one under UNIX).  This is the only code
  224.  *   for which buffers are not saved.
  225.  *
  226.  * - SIGINT is caused by the user hitting the INTR key.
  227.  *   We give him a choice of death or continuation.
  228.  *
  229.  * - SIGHUP is caused by loss of connection.  This code and
  230.  *   code 0 are the only ones which don't cause an abort.
  231.  *   Generated by OS and by JOVE itself.
  232.  */
  233.  
  234. SIGRESTYPE
  235. finish(code)
  236. int    code;
  237. {
  238.     int save_errno = errno;    /* Subtle, but necessary! */
  239.     bool    DelTmps = YES;        /* Usually we delete them. */
  240.  
  241.     if (code == SIGINT) {
  242.         char    c;
  243. #ifdef WIN32
  244.         c = FatalErrorMessage("Fatal interrupt encountered. Abort?");
  245. #else /* !WIN32 */
  246. # ifdef PIPEPROCS
  247.         bool    started;
  248. # endif
  249.  
  250.         resetsighandler(SIGINT, finish);
  251.         f_mess("Abort (Type 'n' if you're not sure)? ");
  252.         Placur(ILI, min(CO - 2, calc_pos(mesgbuf, MAXCOLS)));
  253.         flushscreen();
  254. # ifdef UNIX
  255. #  ifdef PIPEPROCS
  256.         started = kbd_stop();
  257. #  endif
  258.         /*
  259.          * Yuk!  This doesn't deal with all cases, we really need a
  260.          * standard jove input routine that's lower than kbd_getch so
  261.          * that this can use it.  The code that this replaces was even
  262.          * more ugly.  What about nonblocking reads? -- MM.
  263.          */
  264. #  ifdef NONBLOCKINGREAD
  265.         setblock(YES);    /* turn blocking on (in case it was off) */
  266. #  endif
  267.         for (;;) {
  268.             c = 'n';
  269.             if (read(0, (UnivPtr) &c, sizeof(c)) < 0) {
  270.                 switch (errno) {
  271.                 case EINTR:
  272. #  if EWOULDBLOCK != EAGAIN    /* aliases in System Vr4 */
  273.                 case EWOULDBLOCK:
  274. #  endif
  275.                 case EAGAIN:
  276.                     continue;
  277.                 }
  278.             }
  279.             break;
  280.         }
  281. #  ifdef PIPEPROCS
  282.         if (started)
  283.             kbd_strt();
  284. #  endif /* PIPEPROCS */
  285. # endif /* UNIX */
  286. # ifdef MSDOS
  287.         c = getrawinchar();
  288. # endif /* MSDOS */
  289.         message(NullStr);
  290. #endif /* !WIN32 */
  291.         if (c != 'y') {
  292.             redisplay();
  293.             errno = save_errno;
  294.             return SIGRESVALUE;
  295.         }
  296.     }
  297.     DisabledRedisplay = YES;
  298. #ifndef MAC
  299.     UnsetTerm(NO);
  300. #endif
  301. #ifdef PIPEPROCS
  302.     kbd_kill();        /* kill the keyboard process */
  303. #endif
  304. #ifdef RECOVER
  305.     if (code != 0) {
  306.         static bool    Crashing = NO;    /* we are in the middle of crashing */
  307.  
  308.         if (!Crashing) {
  309.             Crashing = YES;
  310.             lsave();
  311.             SyncRec();
  312.             writef("JOVE CRASH!! (code %d; last errno %d)\n",
  313.                 code, save_errno);
  314.             if (ModBufs(YES)) {
  315.                 writef("Your buffers have been saved.\n");
  316.                 writef("Use \"jove -r\" to have a look at them.\n");
  317.                 DelTmps = NO;    /* Don't delete anymore. */
  318.             } else {
  319.                 writef("No buffers needed saving: you didn't lose any work.\n");
  320.             }
  321.         } else {
  322.             writef("\r\nYou may have lost your work!\n");
  323.         }
  324.     }
  325. #endif /* RECOVER */
  326.     flushscreen();
  327.     if (DelTmps) {
  328.         tmpremove();
  329. #ifdef RECOVER
  330.         recremove();
  331. #endif /* RECOVER */
  332.     }
  333. #ifdef UNIX
  334.     if (code != 0 && code != SIGHUP)
  335.         abort();
  336. # ifdef USE_EXIT
  337.     exit(0);
  338. # else
  339.     _exit(0);
  340. # endif
  341. #else /* !UNIX */
  342.     exit(0);
  343. #endif /* !UNIX */
  344.     /*NOTREACHED*/
  345. }
  346.  
  347. private char    smbuf[20],
  348.         *bp = smbuf;
  349. private int    nchars = 0;
  350.  
  351. #ifdef NONBLOCKINGREAD
  352.  
  353. # include <fcntl.h>
  354.  
  355. private void
  356. setblock(on)    /* turn blocking on or off */
  357. bool    on;
  358. {
  359.     static int blockf, nonblockf;
  360.     static bool    first = YES;
  361.  
  362.     if (first) {
  363.         int flags;
  364.  
  365.         first = NO;
  366.         if ((flags = fcntl(0, F_GETFL, 0)) == -1)
  367.             finish(SIGHUP);
  368. # ifdef O_NONBLOCK    /* POSIX form */
  369.         blockf = flags & ~O_NONBLOCK;    /* make sure O_NONBLOCK is off */
  370.         nonblockf = flags | O_NONBLOCK;    /* make sure O_NONBLOCK is on */
  371. # else    /* pre-POSIX form */
  372.         blockf = flags & ~O_NDELAY;    /* make sure O_NDELAY is off */
  373.         nonblockf = flags | O_NDELAY;    /* make sure O_NDELAY is on */
  374. # endif
  375.     }
  376.     if (fcntl(0, F_SETFL, on ? blockf : nonblockf) == -1)
  377.         finish(SIGHUP);
  378. }
  379.  
  380. #endif /* NONBLOCKINGREAD */
  381.  
  382. /* To optimize screen refreshing, we try to detect if there is pending input.
  383.  * If there is, we defer screen updating, hoping that when we eventually
  384.  * do an update it will be more efficient.  To implement this, we use
  385.  * charp to poll for pending input (from any source but macro body).
  386.  * "InputPending" records the last value returned by charp, or what
  387.  * was known by kbd_getch or kbd_ungetch.  Note that the kbd_* routines
  388.  * only consider keyboard input, but charp considers other sources of
  389.  * input.  These are the only routines that should set InputPending
  390.  * (currently, a fudge to redisplay for the mac also sets it --
  391.  * this should be fixed).
  392.  *
  393.  * This heuristic confuses the user if a command takes a long time:
  394.  * the screen may be "frozen" in an inaccurate state until the command
  395.  * completes.  The variable "SlowCmd" is a count of the nesting of slow
  396.  * commands.  If it is positive, display updating is not pre-empted.
  397.  * The macros PreEmptOutput() and CheapPreEmptOutput() implement the tests.
  398.  */
  399.  
  400. int    SlowCmd = 0;    /* depth of nesting of slow commands */
  401.  
  402. bool    InputPending = NO;    /* is there input waiting to be processed? */
  403.  
  404. /* Inputp is used to jam a NUL-terminated string into JOVE's input stream.
  405.  * It is used to feed each line of the joverc file, to fill in the default
  406.  * make_cmd in compile-it, and to fill in the default i-search string.
  407.  * To make this work, we prevent i-search and compile-it from using Inputp
  408.  * when it is already in use.
  409.  */
  410. char    *Inputp = NULL;
  411.  
  412. /* kbd_ungetch must only be used to push back a character that
  413.  * was just returned by kbd_getch.
  414.  */
  415. private ZXchar    kbdpeek = EOF;
  416.  
  417. void
  418. kbd_ungetch(c)
  419. ZXchar    c;
  420. {
  421.     InputPending = YES;
  422.     kbdpeek = c;
  423. }
  424.  
  425. ZXchar
  426. kbd_getch()
  427. {
  428.     register ZXchar    c;
  429.  
  430.     if (kbdpeek != EOF) {
  431.         c = kbdpeek;
  432.         kbdpeek = EOF;
  433.         InputPending = nchars > 0;
  434.         return c;
  435.     }
  436. #if NCHARS != UCHAR_ROOF
  437.     do {
  438. #endif
  439.         while (nchars <= 0) {
  440.             bp = smbuf;
  441. #ifdef MSDOS
  442.             *bp = getrawinchar();
  443.             nchars = 1;
  444. #else /* !MSDOS */
  445. # ifdef WIN32
  446.             if (!charp()) {
  447.                 redisplay();
  448.                 flushscreen();
  449.             }
  450.             nchars = getInputEvents(smbuf, sizeof(smbuf));
  451. # else /* !WIN32 */
  452. #  ifdef PTYPROCS
  453.             /* Get a character from the keyboard, first checking for
  454.                any input from a process.  Handle that first, and then
  455.                deal with the terminal input. */
  456.             {
  457.                 fd_set    reads;
  458.                 int
  459.                     nfds,
  460.                     fd;
  461.  
  462.                 bp = smbuf;
  463.                 for (;;) {
  464.                     while (procs_to_reap)
  465.                             reap_procs();    /* synchronous process reaping */
  466.                     reads = global_fd;
  467.                     InSlowRead = YES;
  468.                     nfds = select(global_maxfd,
  469.                         &reads, (fd_set *)NULL, (fd_set *)NULL,
  470.                         (struct timeval *)NULL);
  471.                     InSlowRead = NO;
  472.                     if (nfds >= 0)
  473.                         break;
  474.  
  475.                     if (errno != EINTR) {
  476.                         complain("\rerror in select: %s", strerror(errno));
  477.                         /* NOTREACHED */
  478.                     }
  479.                 }
  480.  
  481.                 if (FD_ISSET(0, &reads)) {
  482.                     do {
  483.                         nchars = read(0, (UnivPtr) smbuf, sizeof(smbuf));
  484.                     } while (nchars < 0 && errno == EINTR);
  485.                     if (nchars <= 0)
  486.                         finish(SIGHUP);
  487.                     nfds -= 1;
  488.                 }
  489.                 for (fd=1; nfds != 0; fd += 1) {
  490.                     if (FD_ISSET(fd, &reads)) {
  491.                         nfds -= 1;
  492.                         read_pty_proc(fd);
  493.                         if (UpdModLine)
  494.                             redisplay();
  495.                     }
  496.                 }
  497.             }
  498. #  else /* !PTYPROCS */
  499. #   ifdef PIPEPROCS
  500.             if (NumProcs > 0) {
  501.                 /* Handle process input until kbd input arrives */
  502.                 struct header    header;
  503.                 size_t    n;
  504.  
  505.                 InSlowRead = YES;
  506.                 n = f_readn(ProcInput, (char *) &header, sizeof(header));
  507.                 InSlowRead = NO;
  508.  
  509.                 if (n != sizeof(header)) {
  510.                     raw_complain("\r\nError reading kbd process, expected %d, got %d bytes", sizeof header, n);
  511.                     finish(SIGHUP);
  512.                 }
  513.                 if (header.pid == kbd_pid) {
  514.                     /* data is from the keyboard process */
  515.                     nchars = f_readn(ProcInput, smbuf, (size_t)header.nbytes);
  516.                     if (nchars != header.nbytes) {
  517.                         raw_complain("\r\nError reading kbd process, expected %d, got %d bytes.", header.nbytes, nchars);
  518.                         finish(SIGHUP);
  519.                     }
  520.                 } else {
  521.                     /* data is from an interactive process */
  522.                     read_pipe_proc(header.pid, header.nbytes);
  523.                     if (NumProcs == 0)
  524.                         (void) kbd_stop();
  525.                     if (UpdModLine)
  526.                         redisplay();
  527.                 }
  528.             } else /*...*/
  529. #   endif /* PIPEPROCS */
  530.             /*...*/ {
  531.                 do {
  532. #   ifdef UNIX
  533.                     InSlowRead = YES;
  534. #   endif
  535.                     nchars = read(0, (UnivPtr) smbuf, sizeof smbuf);
  536. #   ifdef UNIX
  537.                     InSlowRead = NO;
  538. #   endif
  539.                 } while (nchars < 0 && errno == EINTR);
  540.                 if (nchars <= 0)
  541.                     finish(SIGHUP);
  542.             }
  543. #  endif /* !PTYPROCS */
  544. # endif /* !WIN32 */
  545. #endif /* !MSDOS */
  546.         }
  547.         c = ZXRC(*bp++);
  548. #if !defined(PCNONASCII) && !defined(MAC)    /* if not done elsewhere */
  549.         if ((c & 0200) && MetaKey) {
  550.             *--bp = c & ~0200;
  551.             nchars += 1;
  552.             c = ESC;
  553.         }
  554. #endif /* !defined(PCNONASCII) && !defined(MAC) */
  555.         InputPending = --nchars > 0;
  556. #if NCHARS != UCHAR_ROOF
  557.     } while (c >= NCHARS);    /* discard c if it is a bad char */
  558. #endif
  559.     return c;
  560. }
  561.  
  562. /* Returns YES if a character waiting (excluding macro body) */
  563.  
  564. bool
  565. charp()
  566. {
  567.     if (InJoverc != 0 || kbdpeek != EOF || nchars > 0 || Inputp != NULL)
  568.         return InputPending = YES;
  569. #ifdef FIONREAD
  570.     {
  571.         /*
  572.          * Some manual pages, notably SunOS4.1.3 say 'c' should be
  573.          * 'long', but that's a lie -- it's an 'int' according to all
  574.          * kernels I've seen (including SunOS4.1.3) and most other
  575.          * manual pages.  At any rate, 'int' works correctly on 32- and
  576.          * 64-bit architectures, whereas long breaks on the 64
  577.          * bitters. -- MM.
  578.          */
  579.         int c;
  580.  
  581.         if (ioctl(0, FIONREAD, (UnivPtr) &c) == -1)
  582.             c = 0;
  583.         return InputPending = c > 0;
  584.     }
  585. #else /* !FIONREAD */
  586. # ifdef NONBLOCKINGREAD
  587.     setblock(NO);        /* turn blocking off */
  588.     nchars = read(0, (UnivPtr) smbuf, sizeof smbuf);    /* Is anything there? */
  589.     setblock(YES);        /* turn blocking on */
  590.     bp = smbuf;            /* make sure bp points to it */
  591.     return InputPending = nchars > 0;    /* just say we found something */
  592. # else /* !NONBLOCKINGREAD */
  593. #  ifdef USE_SELECT
  594.     {
  595.         struct timeval    timer;
  596.         fd_set    readfds;
  597.  
  598.         timer.tv_sec = 0;
  599.         timer.tv_usec = 0;
  600.         FD_ZERO(&readfds);
  601.         FD_SET(0, &readfds);
  602.         return InputPending = select(1,
  603.             &readfds, (fd_set *)NULL, (fd_set *)NULL,
  604.             &timer) > 0;
  605.     }
  606. #  else /* !USE_SELECT */
  607. #   ifdef MSDOS
  608.     return InputPending = rawkey_ready();
  609. #   else /* !MSDOS */
  610. #    ifdef MAC
  611.     return InputPending = rawchkc();
  612. #    else
  613. #     ifdef WIN32
  614.     return InputPending = inputEventWaiting(0);
  615. #     else
  616.     return InputPending = NO;    /* who knows? */
  617. #     endif /* !WIN32 */
  618. #    endif /* !MAC */
  619. #   endif /* !MSDOS */
  620. #  endif /* !USE_SELECT */
  621. # endif /* !NONBLOCKINGREAD */
  622. #endif /* !FIONREAD */
  623. }
  624.  
  625. /*
  626.  * Tries to pause for delay/10 seconds OR until a character is typed at the
  627.  * keyboard.  This works well on systems with select() and not so well on the
  628.  * rest.
  629.  */
  630.  
  631. #ifdef MAC
  632. # include <LoMem.h>    /* defines Ticks */
  633. #endif
  634.  
  635. void
  636. SitFor(delay)
  637. int    delay;
  638. {
  639. #ifdef MAC
  640.     long
  641.         start,
  642.         end;
  643.  
  644.     Keyonly = YES;
  645.     redisplay();
  646.     start = Ticks;
  647.  
  648.     end = start + delay * 6;
  649.     do ; while (!charp() && Ticks < end);
  650.  
  651. #else /* !MAC */
  652.  
  653. # ifndef MSDOS
  654.     if (!charp()) {
  655. #  ifdef USE_SELECT
  656.         struct timeval    timer;
  657.         fd_set    readfds;
  658.  
  659.         /* So messages that aren't error messages don't
  660.          * hang around forever.
  661.          * Gross that I had to snarf this from getch()
  662.          */
  663.         if (!UpdMesg && !Asking && mesgbuf[0] && !stickymsg)
  664.             message(NullStr);
  665.         redisplay();
  666.  
  667.         timer.tv_sec = (delay / 10);
  668.         timer.tv_usec = (delay % 10) * 100000;
  669.         FD_ZERO(&readfds);
  670.         FD_SET(0, &readfds);
  671.         (void) select(1,
  672.             &readfds, (fd_set *)NULL, (fd_set *)NULL,
  673.             &timer);
  674. #  else /* ! USE_SELECT */
  675. #   ifdef WIN32
  676.         redisplay();
  677.         inputEventWaiting(delay*100);
  678. #   else /* ! WIN32 */
  679.         /* Pause by spitting NULs at the terminal.  Ugh! */
  680.         static const int cps[] = {
  681.             0,
  682.             5,
  683.             7,
  684.             11,
  685.             13,
  686.             15,
  687.             20,
  688.             30,
  689.             60,
  690.             120,
  691.             180,
  692.             240,
  693.             480,
  694.             960,
  695.             1920,
  696.             1920,
  697.         };
  698.         register int    nchars,
  699.                 check_cnt;
  700.  
  701.         nchars = (delay * cps[ospeed]) / 10;
  702.         check_cnt = ScrBufSize;
  703.         redisplay();
  704.         if (!NP) {
  705.             while ((--nchars > 0) && !InputPending) {
  706.                 scr_putchar(PC);
  707.                 if (--check_cnt == 0) {
  708.                     check_cnt = ScrBufSize;
  709.                     (void) charp();
  710.                 }
  711.             }
  712.         }
  713. #   endif /* !WIN32 */
  714. #  endif /* !USE_SELECT */
  715.     }
  716. # else /* MSDOS */
  717.     /* All time representations must wrap eventually.
  718.      * Since all delays are much less than a minute, we represent
  719.      * time as hundredths of a second past the minute we start.
  720.      * NOTE: this is a busy wait.  I know of no alternative.
  721.      */
  722.     int    start,
  723.         now,
  724.         end;
  725.     struct dostime_t tc;
  726.  
  727.     redisplay();
  728.     _dos_gettime(&tc);
  729.     start = tc.second * 100 + tc.hsecond;
  730.     end = start + delay * 10;
  731.     for (;;)  {
  732.         if (charp())
  733.             break;
  734.         _dos_gettime(&tc);
  735.         now = tc.second * 100 + tc.hsecond;
  736.         if (now < start)
  737.             now += 60 * 100;    /* must be in next minute */
  738.         if (now >= end)
  739.             break;
  740.     }
  741. # endif /* MSDOS */
  742. #endif /* !MAC */
  743. }
  744.  
  745. #define WAITCHAR_CURSOR_DOWN    1    /* during slow keying, cursor is after displayed prefix */
  746.  
  747. private char
  748.     key_strokes[100],
  749.     *keys_p = key_strokes;
  750.  
  751. private bool    in_ask_ks;
  752.  
  753. private volatile bool    slow_keying = NO;    /* for waitchar() */
  754.  
  755. void
  756. cmd_sync()
  757. {
  758.     if (this_cmd != ARG_CMD) {
  759.         clr_arg_value();
  760.         last_cmd = this_cmd;
  761.         slow_keying = NO;
  762.         in_ask_ks = NO;
  763.         keys_p = key_strokes;
  764.     }
  765. }
  766.  
  767. ZXchar
  768. ask_ks()
  769. {
  770.     in_ask_ks = YES;
  771.     keys_p = key_strokes;
  772.     return waitchar();
  773. }
  774.  
  775. void
  776. add_stroke(c)
  777. ZXchar    c;
  778. {
  779.     if (keys_p < &key_strokes[sizeof (key_strokes) - 1])
  780.         *keys_p++ = c;
  781. }
  782.  
  783. void
  784. pp_key_strokes(buffer, size)
  785. char    *buffer;
  786. size_t    size;
  787. {
  788.     char
  789.         *buf_end = buffer + size - 1,
  790.         *kp = key_strokes;
  791.  
  792.     *buffer = '\0';
  793.     while (kp != keys_p) {
  794.         swritef(buffer, (size_t) (buf_end-buffer), "%p ", *kp++);
  795.         buffer += strlen(buffer);
  796.     }
  797. }
  798.  
  799. private void
  800. ShowKeyStrokes()
  801. {
  802.     char    buffer[100];
  803.  
  804.     slow_keying = YES;
  805.     pp_key_strokes(buffer, sizeof (buffer));
  806.     f_mess(in_ask_ks? ": %f %s" : "%s", buffer);
  807. #ifdef WAITCHAR_CURSOR_DOWN
  808.     Asking = YES;
  809.     AskingWidth = strlen(mesgbuf);
  810. #endif
  811. }
  812.  
  813. #define N_SEC    1    /* will be precisely 1 second on 4.2 */
  814.  
  815. ZXchar
  816. waitchar()
  817. {
  818.     ZXchar    c;
  819. #ifdef WAITCHAR_CURSOR_DOWN
  820.     bool    oldAsking;
  821.     int    oldAskingWidth;
  822. #endif
  823.  
  824.     /* short circuit, if we can */
  825.     if (InJoverc || (!Interactive && in_macro()) || InputPending)
  826.         return getch();
  827.  
  828. #ifdef WAITCHAR_CURSOR_DOWN
  829.     oldAsking = Asking;
  830.     oldAskingWidth = AskingWidth;
  831. #endif
  832.  
  833. #ifdef MAC
  834.     Keyonly = YES;
  835. #endif
  836.     if (!slow_keying) {
  837.         /* not yet slow_keying */
  838. #ifdef UNIX
  839.         /* set up alarm */
  840.         InWaitChar = YES;
  841.         (void) alarm((unsigned) N_SEC);
  842. #else /* !UNIX */
  843. # ifdef WIN32
  844.         if (!charp()) {
  845.             if ((slow_keying = !inputEventWaiting(N_SEC*1000))) {
  846.                 ShowKeyStrokes();
  847.             }
  848.         }
  849. # else /* !WIN32 */
  850.         /* NOTE: busy wait (until char typed or timeout)! */
  851.         time_t sw = N_SEC + time((time_t *)NULL);
  852.  
  853.         while (!slow_keying && !charp()) {
  854.             if (time((time_t *)NULL) > sw) {
  855.                 /* transition to slow_keying */
  856. #  ifdef MAC
  857.                 menus_off();
  858. #  endif
  859.                 ShowKeyStrokes();
  860.             }
  861.         }
  862. # endif /* !WIN32 */
  863. #endif /* !UNIX */
  864. #ifdef WAITCHAR_CURSOR_DOWN
  865.     } else {
  866.         /* Already slow_keying: presume bottom line has old keystrokes.
  867.          * Tell refresh(?) to place cursor at end of them.
  868.          */
  869.         Asking = YES;
  870.         AskingWidth = strlen(mesgbuf);
  871. #endif
  872.     }
  873.     c = getch();
  874.     if (slow_keying) {
  875.         ShowKeyStrokes();
  876. #ifdef UNIX
  877.     } else {
  878.         /* not yet slow_keying: tear down alarm */
  879.         InWaitChar = NO;
  880.         SetClockAlarm(YES);
  881. #endif
  882.     }
  883.  
  884. #ifdef WAITCHAR_CURSOR_DOWN
  885.     Asking = oldAsking;
  886.     AskingWidth = oldAskingWidth;
  887. #endif
  888.     return c;
  889. }
  890.  
  891. private void
  892. SetTerm()
  893. {
  894. #ifdef IBMPCDOS
  895.     pcSetTerm();
  896. #endif
  897.     ttysetattr(YES);
  898. #ifdef TERMCAP
  899.     putpad(TI, 1);    /* Cursor addressing start */
  900.     putpad(VS, 1);    /* Visual start */
  901.     putpad(KS, 1);    /* Keypad mode start */
  902. # ifdef MOUSE
  903.     MouseOn();
  904. # endif
  905. #endif
  906. #ifdef UNIX
  907.     (void) chkmail(YES);    /* force it to check so we can be accurate */
  908. #endif
  909. }
  910.  
  911. private void
  912. UnsetTerm(WarnUnwritten)
  913. bool    WarnUnwritten;
  914. {
  915. #ifdef TERMCAP
  916. # ifdef ID_CHAR
  917.     INSmode(NO);
  918. # endif /* ID_CHAR */
  919. # ifdef MOUSE
  920.     MouseOff();
  921. # endif
  922.     putpad(KE, 1);
  923.     putpad(VE, 1);
  924.     Placur(ILI, 0);
  925.     putpad(CE, 1);
  926.     putpad(TE, 1);
  927. #else /* !TERMCAP */
  928.     Placur(ILI, 0);
  929.     clr_eoln();
  930. #endif /* !TERMCAP */
  931.     flushscreen();
  932. #ifdef MSDOS
  933.     pcUnsetTerm();
  934. #endif
  935.     ttysetattr(NO);
  936.     if (WarnUnwritten && ModBufs(NO))
  937.         raw_complain("[There are modified buffers]");
  938. }
  939.  
  940. #ifdef JOB_CONTROL
  941. void
  942. PauseJove()
  943. {
  944.     UnsetTerm(YES);
  945.     (void) kill(0, SIGTSTP);
  946.     SetTerm();
  947. # ifdef WINRESIZE
  948.     /* Some systems (eg System V Release 4) don't give us SIGWINCHes
  949.      * that happen while we are away.
  950.      */
  951.     ResizePending = YES;
  952. # endif
  953.     ClAndRedraw();
  954. }
  955. #endif /* JOB_CONTROL */
  956.  
  957. #ifdef SUBSHELL
  958.  
  959. # ifndef MSDOS
  960. void
  961. jcloseall()
  962. {
  963.     tmpclose();
  964. #  ifdef RECOVER
  965.     recclose();
  966. #  endif
  967. #  ifdef IPROCS
  968.     closeiprocs();
  969. #  endif
  970. }
  971. # endif /* !MSDOS */
  972.  
  973. void
  974. Push()
  975. {
  976. # ifdef MSDOS_PROCS
  977. #  ifdef MSDOS
  978.     UnsetTerm(YES);
  979.     if (spawnl(0, Shell, basename(Shell), (char *)NULL) == -1)
  980.         s_mess("[Spawn failed %d]", errno);
  981.     SetTerm();
  982. #   ifdef WINRESIZE
  983.     /* Some systems (eg System V Release 4) don't give us SIGWINCHes
  984.      * that happen while we are away.
  985.      */
  986.     ResizePending = YES;
  987. #   endif
  988.     ClAndRedraw();
  989.     getCWD();
  990. #  else /* !MSDOS */
  991. #   ifdef WIN32
  992.     STARTUPINFO startinfo = { sizeof(STARTUPINFO) };
  993.     PROCESS_INFORMATION procinfo;
  994.     CreateProcess(Shell, NULL, NULL, NULL,
  995.         FALSE, CREATE_NEW_CONSOLE,
  996.         NULL, NULL,
  997.         &startinfo, &procinfo);
  998. #   endif /* WIN32 */
  999. #  endif /* !MSDOS */
  1000. # else /* !MSDOS_PROCS */
  1001.     /* UNIX, or something like it */
  1002.     SIGHANDLERTYPE    old_int = setsighandler(SIGINT, SIG_IGN);
  1003.     int    forkerr = 0;
  1004. #  ifdef PIPEPROCS
  1005.     bool    started = kbd_stop();
  1006. #  endif
  1007.  
  1008.     UnsetTerm(YES);
  1009.     switch (ChildPid = fork()) {
  1010.     case -1:
  1011.         /* parent, fork failed */
  1012.         forkerr = errno;
  1013.         break;
  1014.  
  1015.     default:
  1016.         /* parent, fork worked */
  1017.         dowait((wait_status_t *) NULL);
  1018.         break;
  1019.  
  1020.     case 0:
  1021.         /* child */
  1022.         /* (void) setsighandler(SIGTERM, SIG_DFL); */
  1023.         (void) setsighandler(SIGINT, SIG_DFL);
  1024.         jcloseall();
  1025.         /* note that curbuf->bfname may be NULL */
  1026.         execl(Shell, basename(Shell), "-is", pr_name(curbuf->b_fname, NO),
  1027.             (char *)NULL);
  1028.         raw_complain("[Execl failed: %s]", strerror(errno));
  1029.         _exit(1);
  1030.         /*NOTREACHED*/
  1031.     }
  1032.     SetTerm();
  1033. #  ifdef WINRESIZE
  1034.     /* Some systems (eg System V Release 4) don't give us SIGWINCHes
  1035.      * that happen while we are away.
  1036.      */
  1037.     ResizePending = YES;
  1038. #  endif
  1039.     ClAndRedraw();
  1040.     (void) setsighandler(SIGINT, old_int);
  1041.     SetClockAlarm(NO);
  1042. #  ifdef PIPEPROCS
  1043.     if (started)
  1044.         kbd_strt();
  1045. #  endif
  1046.     if (forkerr != 0)
  1047.         complain("[Fork failed: %s]", strerror(errno));
  1048. # endif /* !MSDOS_PROCS */
  1049. }
  1050.  
  1051. #endif /* SUBSHELL */
  1052.  
  1053. /* adjust the tty to reflect possible change to JOVE variables */
  1054. void
  1055. tty_adjust()
  1056. {
  1057.     ttysetattr(YES);
  1058. #ifdef MOUSE
  1059.     MouseOn();    /* XtermMouse might have changed */
  1060. #endif
  1061. }
  1062.  
  1063. bool    Interactive = NO;    /* True when we invoke with the command handler? */
  1064.  
  1065. ZXchar
  1066.     peekchar = EOF,    /* holds pushed-back getch output */
  1067.     LastKeyStruck;    /* used by SelfInsert and friends */
  1068.  
  1069. bool
  1070.     MetaKey = NO;        /* VAR: this terminal has a meta key */
  1071.  
  1072. void
  1073. Ungetc(c)
  1074. ZXchar    c;
  1075. {
  1076.     peekchar = c;
  1077. }
  1078.  
  1079. ZXchar
  1080. getch()
  1081. {
  1082.     register ZXchar    c;
  1083.  
  1084.     if (Inputp != NULL) {
  1085.         if ((c = ZXC(*Inputp++)) != '\0')
  1086.             return LastKeyStruck = c;
  1087.         Inputp = NULL;
  1088.     }
  1089.  
  1090.     if (InJoverc) {
  1091.         /* somethings wrong if Inputp runs out while
  1092.            we're reading a .joverc file. */
  1093.         complain("[command line too short]");
  1094.     }
  1095.  
  1096. #ifdef RECOVER
  1097.     if (ModCount >= SyncFreq) {
  1098.         ModCount = 0;
  1099.         SyncRec();
  1100.     }
  1101. #endif /* RECOVER */
  1102.  
  1103.     if ((c = peekchar) != EOF) {
  1104.         /* got input from pushback */
  1105.         peekchar = EOF;
  1106.     } else {
  1107.         if (!Interactive && (c = mac_getc()) != EOF) {
  1108.             /* got input from macro */
  1109.         } else {
  1110.             /* So messages that aren't error messages don't
  1111.              * hang around forever.
  1112.              * Note: this code is duplicated in SitFor()!
  1113.              */
  1114.             if (!UpdMesg && !Asking && mesgbuf[0] != '\0' && !stickymsg)
  1115.                 message(NullStr);
  1116.             redisplay();
  1117.             c = kbd_getch();
  1118.             if (!Interactive && InMacDefine)
  1119.                 mac_putc(c);
  1120.         }
  1121.         add_stroke(c);
  1122.     }
  1123.     return LastKeyStruck = c;
  1124. }
  1125.  
  1126. #if defined(SUBSHELL) && defined(RECOVER)
  1127. private void
  1128. dorecover()
  1129. {
  1130.     char    Recover[FILESIZE];    /* path to recover program (in LibDir) */
  1131.  
  1132.     /* Since recover is a normal cooked mode program, reset the terminal */
  1133.     UnsetTerm(NO);
  1134. # ifdef PIPEPROCS
  1135.     kbd_kill();        /* kill the keyboard process */
  1136. # endif
  1137.     swritef(Recover, sizeof(Recover), "%s/recover", LibDir);
  1138.     execl(Recover, "recover", "-d", TmpDir, (char *) NULL);
  1139.     writef("%s: execl failed! %s\n", Recover, strerror(errno));
  1140.     flushscreen();
  1141.     _exit(-1);
  1142.     /* NOTREACHED */
  1143. }
  1144. #endif /* defined(SUBSHELL) && defined(RECOVER) */
  1145.  
  1146. void
  1147. ShowVersion()
  1148. {
  1149.     s_mess("Jonathan's Own Version of Emacs (%s)", jversion);
  1150. }
  1151.  
  1152. private void
  1153. UNIX_cmdline(argc, argv)
  1154. int    argc;
  1155. char    *argv[];
  1156. {
  1157.     int    lineno = 0,
  1158.         nwinds = 1;
  1159.     char    *pattern = NULL;
  1160.     Buffer    *b;
  1161.  
  1162.     while (argc > 1) {
  1163.         switch (argv[1][0]) {
  1164.         case '+':
  1165.             if ('0' <= argv[1][1] && argv[1][1] <= '9') {
  1166.                 (void) chr_to_int(&argv[1][1], 10, NO, &lineno);
  1167.                 break;
  1168.             } else switch (argv[1][1]) {
  1169.             case '\0':
  1170.                 /* goto end of file just like some people's favourite editor */
  1171.                 lineno = -1;
  1172.                 break;
  1173.             case '/':    /* search for pattern */
  1174.                 /* check if syntax is +/pattern or +/ pattern */
  1175.                 if (argv[1][2] != 0) {
  1176.                     pattern = &argv[1][2];
  1177.                 } else {
  1178.                     argv += 1;
  1179.                     argc -= 1;
  1180.                     if (argv[1] != 0)
  1181.                         pattern = &argv[1][0];
  1182.                 }
  1183.                 break;
  1184.             default:
  1185.                 error("Invalid switch %s",argv[1]);
  1186.                 break;
  1187.             }
  1188.             break;
  1189.         case '-':
  1190.             switch (argv[1][1]) {
  1191.             case 'd':    /* Ignore current directory path */
  1192.             case 'l':    /* Ignore libdir path */
  1193.             case 's':    /* Ignore sharedir path */
  1194.                 argv += 1;
  1195.                 argc -= 1;
  1196.                 break;
  1197.  
  1198.             case 'J':    /* Ignore jove.rc in SHAREDIR */
  1199.             case 'j':    /* Ignore ~/.joverc */
  1200.                 break;
  1201.             case 'p':    /* parse errors in file */
  1202.                 argv += 1;
  1203.                 argc -= 1;
  1204.                 if (argv[1] != NULL) {
  1205.                     SetBuf(do_find(curwind, argv[1], YES, YES));
  1206.                     ErrParse();
  1207.                     nwinds = 0;
  1208.                 }
  1209.                 break;
  1210.             case 't':    /* find tag */
  1211.                 /* check if syntax is -tTag or -t Tag */
  1212.                 if (argv[1][2] != '\0') {
  1213.                     find_tag(&(argv[1][2]), YES);
  1214.                 } else {
  1215.                     argv += 1;
  1216.                     argc -= 1;
  1217.                     if (argv[1] != NULL)
  1218.                         find_tag(argv[1], YES);
  1219.                 }
  1220.                 break;
  1221.  
  1222.             case 'w':    /* multiple windows */
  1223.                 if (argv[1][2] == '\0')
  1224.                     nwinds += 1;
  1225.                 else {
  1226.                     int    n;
  1227.  
  1228.                     (void) chr_to_int(&argv[1][2], 10, NO, &n);
  1229.                     nwinds += -1 + n;
  1230.                 }
  1231.                 (void) div_wind(curwind, nwinds - 1);
  1232.                 break;
  1233.             default:
  1234.                 error("Invalid switch %s",argv[1]);
  1235.                 break;
  1236.             }
  1237.             break;
  1238.         default:
  1239.             {
  1240.             bool    force = (nwinds > 0 || lineno != 0 || pattern != NULL);
  1241.  
  1242.             minib_add(argv[1], force);
  1243.             b = do_find(nwinds > 0 ? curwind : (Window *) NULL,
  1244.                     argv[1], force, YES);
  1245.             if (force) {
  1246.                 SetABuf(curbuf);
  1247.                 SetBuf(b);
  1248.                 if (lineno > 0)
  1249.                     SetLine(next_line(curbuf->b_first, lineno - 1));
  1250.                 else if (lineno == -1)
  1251.                     SetLine(curbuf->b_last);
  1252.                 if (pattern != NULL) {
  1253.                     Bufpos    *bufp;
  1254.  
  1255.                     if ((bufp = dosearch(pattern, FORWARD, UseRE)) != NULL
  1256.                     || (bufp = dosearch(pattern, BACKWARD, UseRE)) != NULL)
  1257.                         SetDot(bufp);
  1258.                     else
  1259.                         complain("Couldn't match pattern in file.");
  1260.                 }
  1261.                 if (nwinds > 1)
  1262.                     NextWindow();
  1263.                 if (nwinds > 0)
  1264.                     nwinds -= 1;
  1265.             }
  1266.             lineno = 0;
  1267.             pattern = NULL;
  1268.             }
  1269.             break;
  1270.         }
  1271.         argv += 1;
  1272.         argc -= 1;
  1273.     }
  1274. }
  1275.  
  1276. #ifdef STDARGS
  1277. void
  1278. error(const char *fmt, ...)
  1279. #else
  1280. /*VARARGS1*/ void
  1281. error(fmt, va_alist)
  1282.     const char    *fmt;
  1283.     va_dcl
  1284. #endif
  1285. {
  1286.     va_list    ap;
  1287.  
  1288.     if (fmt) {
  1289.         va_init(ap, fmt);
  1290.         format(mesgbuf, sizeof mesgbuf, fmt, ap);
  1291.         va_end(ap);
  1292.         UpdMesg = YES;
  1293.     }
  1294.     rbell();
  1295.     longjmp(mainjmp, JMP_ERROR);
  1296. }
  1297.  
  1298. #ifdef STDARGS
  1299. void
  1300. complain(const char *fmt, ...)
  1301. #else
  1302. /*VARARGS1*/ void
  1303. complain(fmt, va_alist)
  1304.     const char    *fmt;
  1305.     va_dcl
  1306. #endif
  1307. {
  1308.     va_list    ap;
  1309.  
  1310.     if (fmt) {
  1311.         va_init(ap, fmt);
  1312.         format(mesgbuf, sizeof mesgbuf, fmt, ap);
  1313.         va_end(ap);
  1314.         UpdMesg = YES;
  1315.     }
  1316.     rbell();
  1317.     longjmp(mainjmp, JMP_COMPLAIN);
  1318. }
  1319.  
  1320. /* format and display a message without using the normal display mechanisms */
  1321.  
  1322. #ifdef STDARGS
  1323. void
  1324. raw_complain(const char *fmt, ...)
  1325. #else
  1326. /*VARARGS1*/ void
  1327. raw_complain(fmt, va_alist)
  1328.     const char    *fmt;
  1329.     va_dcl
  1330. #endif
  1331. {
  1332.     char    buf[MESG_SIZE];
  1333.     va_list    ap;
  1334.  
  1335.     va_init(ap, fmt);
  1336.     format(buf, sizeof(buf) - 2, fmt, ap);
  1337.     va_end(ap);
  1338.     strcat(buf, "\r\n");    /* \r *may* be redundant */
  1339.     (void) write(2, (UnivConstPtr)buf, strlen(buf));
  1340. }
  1341.  
  1342. #ifdef STDARGS
  1343. void
  1344. confirm(const char *fmt, ...)
  1345. #else
  1346. /*VARARGS1*/ void
  1347. confirm(fmt, va_alist)
  1348.     const char    *fmt;
  1349.     va_dcl
  1350. #endif
  1351. {
  1352.     va_list    ap;
  1353.  
  1354.     va_init(ap, fmt);
  1355.     format(mesgbuf, sizeof mesgbuf, fmt, ap);
  1356.     va_end(ap);
  1357.     if (!yes_or_no_p("%s", mesgbuf))
  1358.         longjmp(mainjmp, JMP_COMPLAIN);
  1359. }
  1360.  
  1361. /* Recursive edit.
  1362.  * Guarantee: current buffer will still be current and
  1363.  * it will be in the current window.  If not, complain!
  1364.  */
  1365.  
  1366. int    RecDepth = 0;
  1367.  
  1368. void
  1369. Recur()
  1370. {
  1371.     Buffer    *b = curbuf;
  1372.     Mark    *m;
  1373.  
  1374.     m = MakeMark(curline, curchar);
  1375.  
  1376.     RecDepth += 1;
  1377.     UpdModLine = YES;
  1378.     DoKeys(NO);    /* NO means not first time */
  1379.     UpdModLine = YES;
  1380.     RecDepth -= 1;
  1381.     if (!valid_bp(b))
  1382.         complain("Buffer gone!");
  1383.     tiewind(curwind, b);
  1384.     SetBuf(b);
  1385.     if (!is_an_arg())
  1386.         ToMark(m);
  1387.     DelMark(m);
  1388. }
  1389.  
  1390. private int    iniargc;    /* main sets these for DoKeys() */
  1391. private char    **iniargv;
  1392.  
  1393. private void
  1394. DoKeys(firsttime)
  1395. bool    firsttime;
  1396. {
  1397.     jmp_buf    savejmp;
  1398.  
  1399.     push_env(savejmp);
  1400.  
  1401.     switch (setjmp(mainjmp)) {
  1402.     case 0:
  1403.         if (firsttime)
  1404.             UNIX_cmdline(iniargc, iniargv);
  1405.         break;
  1406.  
  1407.     case JMP_QUIT:
  1408.         if (RecDepth == 0) {
  1409.             if (ModMacs()) {
  1410.                 rbell();
  1411.                 if (!yes_or_no_p("Some MACROS haven't been saved; leave anyway? "))
  1412.                     break;
  1413.             }
  1414.             if (ModBufs(NO)) {
  1415.                 rbell();
  1416.                 if (!yes_or_no_p("Some buffers haven't been saved; leave anyway? "))
  1417.                     break;
  1418.             }
  1419. #ifdef IPROCS
  1420.             if (!KillProcs())
  1421.                 break;    /* user chickened out */
  1422. #endif
  1423.         }
  1424.         pop_env(savejmp);
  1425.         return;
  1426.  
  1427.     case JMP_ERROR:
  1428.         getDOT();    /* God knows what state linebuf was in */
  1429.         /*FALLTHROUGH*/
  1430.     case JMP_COMPLAIN:
  1431.         {
  1432.         gc_openfiles();        /* close any files we left open */
  1433.         stickymsg = YES;
  1434.         unwind_macro_stack();
  1435.         Asking = NO;
  1436.         curwind->w_bufp = curbuf;
  1437.         DisabledRedisplay = NO;
  1438.         SlowCmd = 0;
  1439.         redisplay();
  1440.         break;
  1441.         }
  1442.     }
  1443.  
  1444.     this_cmd = last_cmd = OTHER_CMD;
  1445.  
  1446.     for (;;) {
  1447. #ifdef MAC
  1448.         setjmp(auxjmp);
  1449. #endif
  1450.         cmd_sync();
  1451. #ifdef MAC
  1452.         HiliteMenu(0);
  1453.         EventCmd = NO;
  1454.         menus_on();
  1455. #endif
  1456.         dispatch(getch());
  1457.     }
  1458. }
  1459.  
  1460. private char **
  1461. scanvec(args, str)
  1462. register char    **args,
  1463.         *str;
  1464. {
  1465.     while (*args) {
  1466.         if (strcmp(*args, str) == 0)
  1467.             return args;
  1468.         args += 1;
  1469.     }
  1470.     return NULL;
  1471. }
  1472.  
  1473. #ifdef WINRESIZE
  1474. /*ARGSUSED*/
  1475. SIGRESTYPE
  1476. win_reshape(junk)
  1477. int    junk;    /* passed in when invoked by a signal; of no interest */
  1478. {
  1479.     int save_errno = errno;    /* Subtle, but necessary! */
  1480.  
  1481.     ResizePending = YES;
  1482. #ifdef UNIX
  1483.     resetsighandler(SIGWINCH, win_reshape);
  1484.     if (InSlowRead) {
  1485.         /* needed because of stupid BSD restartable I/O */
  1486.         InSlowRead = NO;
  1487.         redisplay();
  1488.         InSlowRead = YES;
  1489.     }
  1490. #else
  1491.     redisplay();
  1492. #endif
  1493.     errno = save_errno;
  1494.     return SIGRESVALUE;
  1495. }
  1496. #endif /* WINRESIZE */
  1497.  
  1498. private bool
  1499. carefulcpy(to, from, maxsize, mess, raw)
  1500. char    *to,*from;
  1501. size_t    maxsize;
  1502. char    *mess;
  1503. bool    raw;
  1504. {
  1505.     if (from != NULL) {
  1506.         char    *ugh;
  1507.  
  1508.         if (strlen(from) >= maxsize)
  1509.             ugh = "too long";
  1510.         else if (*from == '\0')
  1511.             ugh = "empty";
  1512.         else {
  1513.             strcpy(to, from);
  1514.             return YES;
  1515.         }
  1516.         if (raw) {
  1517.             raw_complain("\r\n%s %s", mess, ugh);
  1518.         } else {
  1519.             swritef(mesgbuf, sizeof mesgbuf, "%s %s", mess, ugh);
  1520.             message(mesgbuf);
  1521.         }
  1522.     }
  1523.     return NO;
  1524. }
  1525.  
  1526. private void
  1527. dojovercs(dosys, dousr)
  1528. bool    dosys;
  1529. bool    dousr;
  1530. {
  1531.     char    Joverc[FILESIZE];
  1532.  
  1533.     if (dosys) {
  1534.         swritef(Joverc, sizeof(Joverc), "%s/jove.rc", ShareDir);
  1535.         (void) joverc(Joverc);    /* system wide jove.rc */
  1536.     }
  1537.  
  1538.     if (dousr && HomeDir != NULL) {
  1539. #ifdef MSFILESYSTEM
  1540.         /* We don't want to run the same rc file twice */
  1541.         if (!dosys || strcmp(HomeDir, ShareDir) != 0) {
  1542.             swritef(Joverc, sizeof(Joverc), "%s/jove.rc", HomeDir);
  1543.             (void) joverc(Joverc);    /* jove.rc in home directory */
  1544.         }
  1545. #else
  1546.         swritef(Joverc, sizeof(Joverc), "%s/.joverc", HomeDir);
  1547.         (void) joverc(Joverc);    /* .joverc in home directory */
  1548. #endif
  1549.     }
  1550. }
  1551.  
  1552. int
  1553. main(argc, argv)
  1554. int    argc;
  1555. char    *argv[];
  1556. {
  1557.     char    **argp;
  1558. #ifdef AUTO_BUFS
  1559.     /* allocate these usually static buffers on the stack:
  1560.      * preserves addressability on some systems.
  1561.      */
  1562.     char    s_iobuff[LBSIZE],
  1563.         s_genbuf[LBSIZE],
  1564.         s_linebuf[LBSIZE];
  1565.  
  1566.     iobuff = s_iobuff;
  1567.     genbuf = s_genbuf;
  1568.     linebuf = s_linebuf;
  1569. #endif
  1570.  
  1571. #ifdef MAC
  1572.     MacInit();        /* initializes all */
  1573.     argc = getArgs(&argv);
  1574. #endif
  1575.  
  1576. #if defined(__WATCOMC__) && defined(FAR_LINES)
  1577.     /* Watcom C under DOS won't grow the near heap after any far
  1578.      * allocation, so we must bump it up to the full 64K now.
  1579.      */
  1580.     _heapgrow();
  1581. #endif
  1582.  
  1583.     iniargc = argc;
  1584.     iniargv = argv;
  1585.  
  1586.     if (setjmp(mainjmp)) {
  1587.         ttysetattr(NO);
  1588.         writef("\nAck! I can't deal with error \"%s\" now.\n", mesgbuf);
  1589.         flushscreen();
  1590.         return 1;
  1591.     }
  1592.  
  1593. #if defined(USE_CTYPE) && !defined(NO_SETLOCALE)
  1594.     /* make ctype reflect "native environment" */
  1595.     locale_adjust();
  1596. #endif
  1597.  
  1598.     getTERM();    /* Get terminal. */
  1599. #ifndef MAC    /* no environment in MacOS */
  1600.     if (getenv("METAKEY"))
  1601.         MetaKey = YES;
  1602. #endif
  1603.     ttysetattr(YES);
  1604.     ttsize();
  1605.  
  1606. #ifdef UNIX
  1607. # ifdef WINRESIZE
  1608.     (void) setsighandler(SIGWINCH, win_reshape);
  1609. # endif
  1610. #endif
  1611.  
  1612. #ifdef MAC
  1613.     InitEvents();
  1614. #endif
  1615.  
  1616.     d_cache_init();        /* initialize the disk buffer cache */
  1617.  
  1618.     make_scr();
  1619.     flushscreen();    /* kludge: prevent interleaving output with diagnostic */
  1620.     mac_init();    /* Initialize Macros */
  1621.     winit();    /* Initialize Window */
  1622. #ifdef PTYPROCS
  1623. # ifdef SIGCHLD
  1624.     (void) setsighandler(SIGCHLD, sigchld_handler);
  1625. # endif
  1626. #endif
  1627. #ifdef USE_SELECT
  1628.     FD_ZERO(&global_fd);
  1629.     FD_SET(0, &global_fd);
  1630.     global_maxfd = 1;
  1631. #endif
  1632.     buf_init();
  1633.  
  1634.     if ((argp = scanvec(argv, "-d")) != NULL && chkCWD(argp[1]))
  1635.         setCWD(argp[1]);
  1636.     else
  1637.         getCWD();    /* After we setup curbuf in case we have to getwd() */
  1638.  
  1639. #ifdef MAC
  1640.     HomeDir = gethome();
  1641. #else /* !MAC */
  1642.     HomeDir = getenv("HOME");
  1643.     if (HomeDir == NULL) {
  1644. # ifdef MSDOS
  1645.         HomeDir = copystr(pwd());    /* guess at current (initial) directory */
  1646. # else
  1647. #  ifdef WIN32
  1648.         /* Following are set up automatically by NT on logon. */
  1649.         char *homedrive = getenv("HOMEDRIVE");
  1650.         char *homepath = getenv("HOMEPATH");
  1651.  
  1652.         if (homedrive != NULL && homepath != NULL) {
  1653.             HomeDir = emalloc(strlen(homedrive) + strlen(homepath) + 1);
  1654.             strcpy(HomeDir, homedrive);
  1655.             strcat(HomeDir, homepath);
  1656.         } else {
  1657.             HomeDir = copystr(pwd());
  1658.         }
  1659. #  else /* !WIN32 */
  1660.         HomeDir = "/";
  1661. #  endif /* !WIN32 */
  1662. # endif /* !MSDOS */
  1663.     }
  1664. #endif /* !MAC */
  1665.     HomeLen = strlen(HomeDir);
  1666.  
  1667.     InitKeymaps();
  1668.  
  1669.     settout();    /* not until we know baudrate */
  1670.     SetTerm();
  1671.  
  1672. #ifndef MAC    /* no environment in MacOS */
  1673.     /* Handle overrides for ShareDir and LibDir.
  1674.      * We take care to use the last specification.
  1675.      * Even if we don't use LibDir, we accept it.
  1676.      */
  1677.      {
  1678.         char
  1679.             *so = getenv("JOVESHARE");
  1680. # ifdef NEED_LIBDIR
  1681.         char
  1682.             *lo = getenv("JOVELIB");
  1683. # endif
  1684.  
  1685.         for (argp = argv; argp[0] != NULL && argp[1] != NULL; argp++) {
  1686.             if (strcmp(*argp, "-s") == 0)
  1687.                 so = *++argp;
  1688. # ifdef NEED_LIBDIR
  1689.             else if (strcmp(*argp, "-l") == 0)
  1690.                 lo = *++argp;
  1691.             else if (strcmp(*argp, "-ls") == 0 || strcmp(*argp, "-sl") == 0)
  1692.                 lo = so = *++argp;
  1693. # endif
  1694.         }
  1695.         if (so != NULL)
  1696.             if (!carefulcpy(ShareDir, so, sizeof(ShareDir)-9, "ShareDir", YES))
  1697.                 finish(0);
  1698. # ifdef NEED_LIBDIR
  1699.         if (lo != NULL)
  1700.             if (!carefulcpy(LibDir, lo, sizeof(LibDir)-9, "LibDir", YES))
  1701.                 finish(0);
  1702. #  ifdef PIPEPROCS
  1703.         swritef(Portsrv, sizeof(Portsrv), "%s/portsrv", LibDir);
  1704. #  endif
  1705. # endif /* NEED_LIBDIR */
  1706.     }
  1707. #endif /* !MAC */
  1708.  
  1709.     ShowVersion();    /* but the 'carefulcpy's which follow might overwrite it */
  1710.  
  1711.     /* import the temporary file path from the environment
  1712.        and fix the string, so that we can append a slash
  1713.        safely    */
  1714. #ifdef MSFILESYSTEM
  1715.     carefulcpy(TmpDir, getenv("TEMP"), sizeof(TmpDir), "TEMP", NO);
  1716. #endif
  1717. #ifndef MAC    /* no environment in MacOS */
  1718.     carefulcpy(TmpDir, getenv("TMPDIR"), sizeof(TmpDir), "TMPDIR", NO);
  1719. #endif
  1720.     {
  1721.         char    *cp = &TmpDir[strlen(TmpDir)];
  1722.  
  1723.         do ; while (cp != TmpDir && (*--cp == '/'
  1724. #ifdef MSFILESYSTEM
  1725.             || *cp == '\\'
  1726. #endif
  1727.             ));
  1728.         cp[1] = '\0';
  1729.     }
  1730.  
  1731. #ifdef SUBSHELL
  1732. # ifdef MSFILESYSTEM    /* ??? Is this the right test? */
  1733.     carefulcpy(Shell, getenv("COMSPEC"), sizeof(Shell), "COMSPEC", NO);
  1734.     /* SHELL, if present in DOS environment, will take precedence over COMSPEC */
  1735. # endif /* MSFILESYSTEM */
  1736.     carefulcpy(Shell, getenv("SHELL"), sizeof(Shell), "SHELL", NO);
  1737. #endif /* SUBSHELL */
  1738.  
  1739. #ifdef UNIX
  1740.     carefulcpy(Mailbox, getenv("MAIL"), sizeof(Mailbox), "MAIL", NO);
  1741. #endif
  1742.  
  1743.     dojovercs(scanvec(argv, "-J") == NULL, scanvec(argv, "-j") == NULL);
  1744.  
  1745. #if defined(SUBSHELL) && defined(RECOVER)
  1746.     if (scanvec(argv, "-r") != NULL)
  1747.         dorecover();
  1748. #endif
  1749.  
  1750. #ifdef UNIX
  1751. # ifndef DEBUGCRASH
  1752.     (void) setsighandler(SIGHUP, finish);
  1753.     (void) setsighandler(SIGINT, finish);
  1754. #  ifdef SIGBUS
  1755.     (void) setsighandler(SIGBUS, finish);
  1756. #  endif /* SIGBUS */
  1757.     (void) setsighandler(SIGSEGV, finish);
  1758.     (void) setsighandler(SIGPIPE, finish);
  1759.     /* ??? Why should we ignore SIGTERM? */
  1760.     /* (void) setsighandler(SIGTERM, SIG_IGN); */
  1761. # endif /* DEBUGCRASH */
  1762.     (void) setsighandler(SIGALRM, AlarmHandler);
  1763.     SetClockAlarm(NO);
  1764. #endif /* UNIX */
  1765.     ClAndRedraw();
  1766.     flushscreen();
  1767.     RedrawDisplay();    /* start the redisplay process. */
  1768.     DoKeys(YES);
  1769.     finish(0);
  1770.     /* NOTREACHED*/
  1771. }
  1772.