home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume14 / jove4.9 / part10 / extend.c next >
C/C++ Source or Header  |  1988-04-25  |  20KB  |  1,047 lines

  1. /***************************************************************************
  2.  * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
  3.  * is 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. #include "jove.h"
  9. #include "io.h"
  10. #include "termcap.h"
  11. #include "ctype.h"
  12. #ifdef JOB_CONTROL
  13. #    include <signal.h>
  14. #endif
  15.  
  16. #ifdef MAC
  17. #    include "mac.h"
  18. #else
  19. #    include <varargs.h>
  20. #endif
  21.  
  22. #ifdef MSDOS
  23. #include <process.h>
  24. #endif
  25.  
  26. #ifdef MAC
  27. #    undef private
  28. #    define private
  29. #endif
  30.  
  31. #ifdef    LINT_ARGS
  32. private    void
  33.     fb_aux(data_obj *, data_obj **, char *, char *),
  34.     find_binds(data_obj *, char *),
  35.     vpr_aux(struct variable *, char *);
  36. #else
  37. private    void
  38.     fb_aux(),
  39.     find_binds(),
  40.     vpr_aux();
  41. #endif    /* LINT_ARGS */
  42.  
  43. #ifdef MAC
  44. #    undef private
  45. #    define private static
  46. #endif
  47.  
  48.  
  49. int    InJoverc = 0;
  50.  
  51. extern int    getch(),
  52.         getchar();
  53.  
  54. /* Auto execute code */
  55.  
  56. #define NEXECS    20
  57.  
  58. private struct {
  59.     char    *a_pattern;
  60.     data_obj    *a_cmd;
  61. } AutoExecs[NEXECS] = {0};
  62.  
  63. private int    ExecIndex = 0;
  64.  
  65. /* Command auto-execute. */
  66.  
  67. void
  68. CAutoExec()
  69. {
  70.     DefAutoExec(findcom);
  71. }
  72.  
  73. /* Macro auto-execute. */
  74.  
  75. void
  76. MAutoExec()
  77. {
  78.     DefAutoExec(findmac);
  79. }
  80.  
  81. /* VARARGS0 */
  82.  
  83. void
  84. DefAutoExec(proc)
  85. #ifdef LINT_ARGS
  86. data_obj    *(*proc)();
  87. #else
  88. data_obj    *(*proc)();
  89. #endif
  90. {
  91.     data_obj    *d;
  92.     char    *pattern;
  93.     int    i;
  94.  
  95.     if (ExecIndex >= NEXECS)
  96.         complain("Too many auto-executes, max %d.", NEXECS);
  97.     if ((d = (*proc)(ProcFmt)) == 0)
  98.         return;
  99.     pattern = do_ask("\r\n", (int (*)()) 0, (char *) 0, ": %f %s ", d->Name);
  100.     if (pattern != 0)
  101.         for (i = 0; i < ExecIndex; i++)
  102.         if ((AutoExecs[i].a_cmd == d) &&
  103.             (strcmp(pattern, AutoExecs[i].a_pattern) == 0))
  104.                 return;        /* eliminate duplicates */
  105.     AutoExecs[ExecIndex].a_pattern = copystr(pattern);
  106.     AutoExecs[ExecIndex].a_cmd = d;
  107.     ExecIndex += 1;
  108. }
  109.  
  110. /* DoAutoExec: NEW and OLD are file names, and if NEW and OLD aren't the
  111.    same kind of file (i.e., match the same pattern) or OLD is 0 and it
  112.    matches, OR if the pattern is 0 (none was specified) then, we execute
  113.    the command associated with that kind of file. */
  114.  
  115. void
  116. DoAutoExec(new, old)
  117. register char    *new,
  118.         *old;
  119. {
  120.     register int    i;
  121.  
  122.     set_arg_value(1);
  123.     for (i = 0; i < ExecIndex; i++)
  124.         if ((AutoExecs[i].a_pattern == 0) ||
  125.             ((new != 0 && LookingAt(AutoExecs[i].a_pattern, new, 0)) &&
  126.              (old == 0 || !LookingAt(AutoExecs[i].a_pattern, old, 0))))
  127.             ExecCmd(AutoExecs[i].a_cmd);
  128. }
  129.  
  130. void
  131. BindAKey()
  132. {
  133.     BindSomething(findcom);
  134. }
  135.  
  136. void
  137. BindMac()
  138. {
  139.     BindSomething(findmac);
  140. }
  141.  
  142. extern void    EscPrefix(),
  143.         CtlxPrefix(),
  144.         MiscPrefix();
  145.  
  146. data_obj **
  147. IsPrefix(cp)
  148. data_obj    *cp;
  149. {
  150. #ifdef MAC
  151.     void (*proc)();
  152. #else
  153.     int    (*proc)();
  154. #endif
  155.     
  156.     if (cp == 0 || (cp->Type & TYPEMASK) != FUNCTION)
  157.         return 0;
  158.     proc = ((struct cmd *) cp)->c_proc;
  159.     if (proc == EscPrefix)
  160.         return pref1map;
  161.     if (proc == CtlxPrefix)
  162.         return pref2map;
  163.     if (proc == MiscPrefix)
  164.         return miscmap;
  165.     return 0;
  166. }
  167.  
  168. void
  169. UnbindC()
  170. {
  171.     char    *keys;
  172.     data_obj    **map = mainmap;
  173.  
  174.     keys = ask((char *) 0, ProcFmt);
  175.     for (;;) {
  176.         if (keys[1] == '\0')
  177.             break;
  178.         if ((map = IsPrefix(map[*keys])) == 0)
  179.             break;
  180.         keys += 1;
  181.     }
  182.     if (keys[1] != 0)
  183.         complain("That's not a legitimate key sequence.");
  184.     map[keys[0]] = 0;
  185. }
  186.         
  187. int
  188. addgetc()
  189. {
  190.     int    c;
  191.  
  192.     if (!InJoverc) {
  193.         Asking = strlen(mesgbuf);
  194.         c = getch();
  195.         Asking = 0;
  196.         add_mess("%p ", c);
  197.     } else {
  198.         c = getch();
  199.         if (c == '\n')
  200.             return EOF;    /* this isn't part of the sequence */
  201.         else if (c == '\\') {
  202.             if ((c = getch()) == LF)
  203.                 complain("[Premature end of line]");
  204.         } else if (c == '^') {
  205.             if ((c = getch()) == '?')
  206.                 c = RUBOUT;
  207.             else if (isalpha(c) || index("@[\\]^_", c))
  208.                 c = CTL(c);
  209.             else
  210.                 complain("[Unknown control character]");
  211.         }
  212.     }
  213.     return c;
  214. }
  215.  
  216. void
  217. BindWMap(map, lastkey, cmd)
  218. data_obj    **map,
  219.         *cmd;
  220. {
  221.     data_obj    **nextmap;
  222.     int    c;
  223.  
  224.     c = addgetc();
  225.     if (c == EOF) {
  226.         if (lastkey == EOF)
  227.             complain("[Empty key sequence]");
  228.         complain("[Premature end of key sequence]");
  229.     } else {
  230.         if (nextmap = IsPrefix(map[c]))
  231.             BindWMap(nextmap, c, cmd);
  232.         else {
  233.             map[c] = cmd;
  234. #ifdef MAC
  235.             ((struct cmd *) cmd)->c_key = c;    /* see about_j() in mac.c */
  236.             if(map == mainmap) ((struct cmd *) cmd)->c_map = F_MAINMAP;
  237.             else if(map == pref1map) ((struct cmd *) cmd)->c_map = F_PREF1MAP;
  238.             else if(map == pref2map) ((struct cmd *) cmd)->c_map = F_PREF2MAP;
  239. #endif
  240.         }
  241.     }
  242. }
  243.  
  244. /* VARARGS0 */
  245.  
  246. void
  247. BindSomething(proc)
  248. #ifdef LINT_ARGS
  249. data_obj    *(*proc)();
  250. #else
  251. data_obj    *(*proc)();
  252. #endif
  253. {
  254.     data_obj    *d;
  255.  
  256.     if ((d = (*proc)(ProcFmt)) == 0)
  257.         return;
  258.     s_mess(": %f %s ", d->Name);
  259.     BindWMap(mainmap, EOF, d);
  260. }
  261.  
  262. /* Describe key */
  263.  
  264. void
  265. DescWMap(map, key)
  266. data_obj    **map;
  267. {
  268.     data_obj    *cp = map[key],
  269.             **prefp;
  270.  
  271.     if (cp == 0)
  272.         add_mess("is unbound.");
  273.     else if (prefp = IsPrefix(cp))
  274.         DescWMap(prefp, addgetc());
  275.     else
  276.         add_mess("is bound to %s.", cp->Name);
  277. }
  278.  
  279. void
  280. KeyDesc()
  281. {
  282.     s_mess(ProcFmt);
  283.     DescWMap(mainmap, addgetc());
  284. }
  285.  
  286. void
  287. DescCom()
  288. {
  289.     data_obj    *dp;
  290.     char    pattern[100],
  291.         doc_type[40],
  292.         *file = CmdDb;
  293.     File    *fp;
  294.  
  295.     if (!strcmp(LastCmd->Name, "describe-variable"))
  296.         dp = (data_obj *) findvar(ProcFmt);
  297.     else
  298.         dp = (data_obj *) findcom(ProcFmt);
  299.     if (dp == 0)
  300.         return;
  301.     fp = open_file(file, iobuff, F_READ, COMPLAIN, QUIET);
  302.     Placur(ILI, 0);
  303.     flusho();
  304.     sprintf(pattern, "^:entry \"%s\" \"\\([^\"]*\\)\"", dp->Name);
  305.     TOstart("Help", TRUE);
  306.     for (;;) {
  307.         if (f_gets(fp, genbuf, LBSIZE) == EOF) {
  308.             Typeout("There is no documentation for \"%s\".", dp->Name);
  309.             goto outahere;
  310.         }
  311.         if ((strncmp(genbuf, ":entry", 6) == 0) && LookingAt(pattern, genbuf, 0))
  312.             break;
  313.     }
  314.     /* found it ... let's print it */
  315.     putmatch(1, doc_type, sizeof doc_type);
  316.     if (strcmp("Variable", doc_type) == 0)
  317.         Typeout(dp->Name);
  318.     else if (strcmp("Command", doc_type) == 0) {
  319.         char    binding[128];
  320.  
  321.         find_binds(dp, binding);
  322.         if (blnkp(binding))
  323.             Typeout("To invoke %s, type \"ESC X %s<cr>\".",
  324.                 dp->Name,
  325.                 dp->Name);
  326.         else
  327.             Typeout("Type \"%s\" to invoke %s.", binding, dp->Name);
  328.     }
  329.     Typeout("");
  330.     while (f_gets(fp, genbuf, LBSIZE) != EOF)
  331.         if (strncmp(genbuf, ":entry", 6) == 0)
  332.             goto outahere;
  333.         else
  334.             Typeout("%s", genbuf);
  335. outahere:
  336.     f_close(fp);
  337.     TOstop();
  338. }
  339.  
  340. void
  341. DescBindings()
  342. {
  343.     extern void    Typeout();
  344.  
  345.     TOstart("Key Bindings", TRUE);
  346.     DescMap(mainmap, NullStr);
  347.     TOstop();
  348. }
  349.  
  350. extern int specialmap;
  351.  
  352. void
  353. DescMap(map, pref)
  354. data_obj    **map;
  355. char    *pref;
  356. {
  357.     int    c1,
  358.         c2 = 0,
  359.         numbetween;
  360.     char    keydescbuf[40];
  361.     data_obj    **prefp;
  362.  
  363. #ifdef IBMPC
  364.     specialmap = (map == miscmap);
  365. #endif
  366.  
  367.     for (c1 = 0; c1 < NCHARS && c2 < NCHARS; c1 = c2 + 1) {
  368.         c2 = c1;
  369.         if (map[c1] == 0)
  370.             continue;
  371.         while (++c2 < NCHARS && map[c1] == map[c2])
  372.             ;
  373.         c2 -= 1;
  374.         numbetween = c2 - c1;
  375.         if (numbetween == 1)
  376.             sprintf(keydescbuf, "%s {%p,%p}", pref, c1, c2);
  377.         else if (numbetween == 0)
  378.             sprintf(keydescbuf, "%s %p", pref, c1);
  379.         else
  380.             sprintf(keydescbuf, "%s [%p-%p]", pref, c1, c2);
  381.         if ((prefp = IsPrefix(map[c1])) && (prefp != map))
  382.             DescMap(prefp, keydescbuf);
  383.         else
  384.             Typeout("%-18s%s", keydescbuf, map[c1]->Name);
  385.     }
  386. }
  387.  
  388. private void
  389. find_binds(dp, buf)
  390. data_obj    *dp;
  391. char    *buf;
  392. {
  393.     char    *endp;
  394.  
  395.     buf[0] = '\0';
  396.     fb_aux(dp, mainmap, (char *) 0, buf);
  397.     endp = buf + strlen(buf) - 2;
  398.     if ((endp > buf) && (strcmp(endp, ", ") == 0))
  399.         *endp = '\0';
  400. }
  401.  
  402. private void
  403. fb_aux(cp, map, prefix, buf)
  404. register data_obj    *cp,
  405.             **map;
  406. char    *buf,
  407.     *prefix;
  408. {
  409.     int    c1,
  410.         c2;
  411.     char    *bufp = buf + strlen(buf),
  412.         prefbuf[20];
  413.     data_obj    **prefp;
  414.  
  415. #ifdef IBMPC
  416.     specialmap = (map == miscmap);
  417. #endif    
  418.  
  419.     for (c1 = c2 = 0; c1 < NCHARS && c2 < NCHARS; c1 = c2 + 1) {
  420.         c2 = c1;
  421.         if (map[c1] == cp) {
  422.             while (++c2 < NCHARS && map[c1] == map[c2])
  423.                 ;
  424.             c2 -= 1;
  425.             if (prefix)
  426.                 sprintf(bufp, "%s ", prefix);
  427.             bufp += strlen(bufp);
  428.             switch (c2 - c1) {
  429.             case 0:
  430.                 sprintf(bufp, "%p, ", c1);
  431.                 break;
  432.     
  433.             case 1:
  434.                 sprintf(bufp, "{%p,%p}, ", c1, c2);
  435.                 break;
  436.     
  437.             default:
  438.                 sprintf(bufp, "[%p-%p], ", c1, c2);
  439.                 break;
  440.             }
  441.         }
  442.         if ((prefp = IsPrefix(map[c1])) && (prefp != map))  {
  443.             sprintf(prefbuf, "%p", c1);
  444.             fb_aux(cp, prefp, prefbuf, bufp);
  445.         }
  446.         bufp += strlen(bufp);
  447.     }
  448. }
  449.  
  450. void
  451. Apropos()
  452. {
  453.     register struct cmd    *cp;
  454.     register struct macro    *m;
  455.     register struct variable    *v;
  456.     char    *ans;
  457.     int    anyfs = NO,
  458.         anyvs = NO,
  459.         anyms = NO;
  460.     char    buf[256];
  461.  
  462.     ans = ask((char *) 0, ": %f (keyword) ");
  463.     TOstart("Help", TRUE);
  464.     for (cp = commands; cp->Name != 0; cp++)
  465.         if (sindex(ans, cp->Name)) {
  466.             if (anyfs == 0) {
  467.                 Typeout("Commands");
  468.                 Typeout("--------");
  469.             }
  470.             find_binds((data_obj *) cp, buf);
  471.             if (buf[0])
  472.                 Typeout(": %-35s(%s)", cp->Name, buf);
  473.             else
  474.                 Typeout(": %s", cp->Name);
  475.             anyfs = YES;
  476.         }
  477.     if (anyfs)
  478.         Typeout(NullStr);
  479.     for (v = variables; v->Name != 0; v++)
  480.         if (sindex(ans, v->Name)) {
  481.             if (anyvs == 0) {
  482.                 Typeout("Variables");
  483.                 Typeout("---------");
  484.             }
  485.             anyvs = YES;
  486.             vpr_aux(v, buf);
  487.             Typeout(": set %-26s%s", v->Name, buf);
  488.         }
  489.     if (anyvs)
  490.         Typeout(NullStr);
  491.     for (m = macros; m != 0; m = m->m_nextm)
  492.         if (sindex(ans, m->Name)) {
  493.             if (anyms == 0) {
  494.                 Typeout("Macros");
  495.                 Typeout("------");
  496.             }
  497.             anyms = YES;
  498.             find_binds((data_obj *) m, buf);
  499.             if (buf[0])
  500.                 Typeout(": %-35s(%s)", m->Name, buf);
  501.             else
  502.                 Typeout(": %-35s%s", "execute-macro", m->Name);
  503.         }
  504.     TOstop();
  505. }
  506.  
  507. void
  508. Extend()
  509. {
  510.     data_obj    *d;
  511.  
  512.     if (d = findcom(": "))
  513.         ExecCmd(d);
  514. }
  515.  
  516. /* Read a positive integer from CP.  It must be in base BASE, and
  517.    complains if it isn't.  If allints is nonzero, all the characters
  518.    in the string must be integers or we return -1; otherwise we stop
  519.    reading at the first nondigit. */
  520.  
  521. int
  522. chr_to_int(cp, base, allints, result)
  523. register char    *cp;
  524. register int    *result;
  525. {
  526.     register int    c;
  527.     int    value = 0,
  528.         sign;
  529.  
  530.     if ((c = *cp) == '-') {
  531.         sign = -1;
  532.         cp += 1;
  533.     } else
  534.         sign = 1;
  535.     while (c = *cp++) {
  536.         if (!isdigit(c)) {
  537.             if (allints == YES)
  538.                 return INT_BAD;
  539.             break;
  540.         }
  541.         c = c - '0';
  542.         if (c >= base)
  543.             complain("You must specify in base %d.", base);
  544.         value = value * base + c;
  545.     }
  546.     *result = value * sign;
  547.     return INT_OKAY;
  548. }
  549.  
  550. int
  551. ask_int(prompt, base)
  552. char    *prompt;
  553. int    base;
  554. {
  555.     char    *val = ask((char *) 0, prompt);
  556.     int    value;
  557.  
  558.     if (chr_to_int(val, base, YES, &value) == INT_BAD)
  559.         complain("That's not a number!");
  560.     return value;
  561. }
  562.  
  563. private void
  564. vpr_aux(vp, buf)
  565. register struct variable    *vp;
  566. char    *buf;
  567. {
  568.     switch (vp->v_flags & V_TYPEMASK) {
  569.     case V_BASE10:
  570.         sprintf(buf, "%d", *(vp->v_value));
  571.         break;
  572.  
  573.     case V_BASE8:
  574.         sprintf(buf, "%o", *(vp->v_value));
  575.         break;
  576.  
  577.     case V_BOOL:
  578.         sprintf(buf, (*(vp->v_value)) ? "on" : "off");
  579.         break;
  580.  
  581.     case V_STRING:
  582.     case V_FILENAME:
  583.         sprintf(buf, "%s", (char *) vp->v_value);
  584.         break;
  585.  
  586.     case V_CHAR:
  587.         sprintf(buf, "%p", *(vp->v_value));
  588.         break;
  589.     }
  590. }
  591.  
  592. void
  593. PrVar()
  594. {
  595.     struct variable    *vp;
  596.     char    prbuf[256];
  597.  
  598.     if ((vp = (struct variable *) findvar(ProcFmt)) == 0)
  599.         return;
  600.     vpr_aux(vp, prbuf);
  601.     s_mess(": %f %s => %s", vp->Name, prbuf);
  602. }
  603.  
  604. void
  605. SetVar()
  606. {
  607.     struct variable    *vp;
  608.     char    *prompt;
  609.  
  610.     if ((vp = (struct variable *) findvar(ProcFmt)) == 0)
  611.         return;
  612.     prompt = sprint(": %f %s ", vp->Name);
  613.  
  614.     switch (vp->v_flags & V_TYPEMASK) {
  615.     case V_BASE10:
  616.     case V_BASE8:
  617.         {
  618.             int    value;
  619.  
  620.         value = ask_int(prompt, ((vp->v_flags & V_TYPEMASK) == V_BASE10)
  621.                       ? 10 : 8);
  622.         *(vp->v_value) = value;
  623.             break;
  624.         }
  625.  
  626.     case V_BOOL:
  627.         {
  628.             char    *def = *(vp->v_value) ? "off" : "on",
  629.                 *on_off;
  630.             int    value;
  631.  
  632.             on_off = ask(def, prompt);
  633.         if (casecmp(on_off, "on") == 0)
  634.             value = ON;
  635.             else if (casecmp(on_off, "off") == 0)
  636.                 value = OFF;
  637.             else
  638.                 complain("Boolean variables must be ON or OFF.");
  639.             *(vp->v_value) = value;
  640. #ifdef MAC
  641.         MarkVar(vp,-1,0);    /* mark the menu item */
  642. #endif
  643.             s_mess("%s%s", prompt, value ? "on" : "off");
  644.             break;
  645.         }
  646.  
  647.     case V_FILENAME:
  648.         {
  649.         char    fbuf[FILESIZE];
  650.  
  651.             sprintf(&prompt[strlen(prompt)], "(default %s) ", vp->v_value);
  652.             (void) ask_file(prompt, (char *) vp->v_value, fbuf);
  653.         strcpy((char *) vp->v_value, fbuf);
  654.             break;
  655.         }
  656.  
  657.     case V_STRING:
  658.         {
  659.         char    *str;
  660.  
  661.             /* Do_ask() so you can set string to "" if you so desire. */
  662.             str = do_ask("\r\n", (int (*)()) 0, (char *) vp->v_value, prompt);
  663.             if (str == 0)
  664.             str = NullStr;
  665.             strcpy((char *) vp->v_value, str);
  666.         /* ... and hope there is enough room. */
  667.             break;
  668.         }
  669.     case V_CHAR:
  670.         f_mess(prompt);
  671.             *(vp->v_value) = addgetc();
  672.         break;            
  673.  
  674.     }
  675.     if (vp->v_flags & V_MODELINE)
  676.         UpdModLine = YES;
  677.     if (vp->v_flags & V_CLRSCREEN) {
  678. #ifdef IBMPC
  679.         setcolor(Fgcolor, Bgcolor);
  680. #endif /* IBMPC */
  681.         ClAndRedraw();
  682.     }
  683.     if (vp->v_flags & V_TTY_RESET)
  684.         tty_reset();
  685. }
  686.             
  687. /* Command completion - possible is an array of strings, prompt is
  688.    the prompt to use, and flags are ... well read jove.h.
  689.  
  690.    If flags are RET_STATE, and the user hits <return> what they typed
  691.    so far is in the Minibuf string. */
  692.  
  693. private char    **Possible;
  694. private int    comp_value,
  695.         comp_flags;
  696.  
  697. int
  698. aux_complete(c)
  699. {
  700.     int    command,
  701.         length,
  702.         i;
  703.  
  704.     if (comp_flags & CASEIND) {
  705.         char    *lp;
  706.  
  707.         for (lp = linebuf; *lp != '\0'; lp++)
  708. #if (defined(IBMPC) || defined(MAC))
  709.             lower(lp);
  710. #else            
  711.             if (isupper(*lp))
  712.                 *lp = tolower(*lp);
  713. #endif
  714.     }
  715.     switch (c) {
  716.     case EOF:
  717.         comp_value = -1;
  718.         return 0;
  719.  
  720.     case '\r':
  721.     case '\n':
  722.         command = match(Possible, linebuf);
  723.         if (command >= 0) {
  724.             comp_value = command;
  725.             return 0;    /* tells ask to stop */
  726.         }
  727.         if (eolp() && bolp()) {
  728.             comp_value = NULLSTRING;
  729.             return 0;
  730.         }
  731.         if (comp_flags & RET_STATE) {
  732.             comp_value = command;
  733.             return 0;
  734.         }
  735.         if (InJoverc)
  736.             complain("[\"%s\" unknown]", linebuf);
  737.         rbell();
  738.         break;
  739.  
  740.     case '\t':
  741.     case ' ':
  742.         {
  743.         int    minmatch = 1000,
  744.                 maxmatch = 0,
  745.                 numfound = 0,
  746.                 lastmatch = -1,
  747.             length = strlen(linebuf);
  748.  
  749.         for (i = 0; Possible[i] != 0; i++) {
  750.             int    this_len;
  751.  
  752.             this_len = numcomp(Possible[i], linebuf);
  753.             maxmatch = max(maxmatch, this_len);
  754.             if (this_len >= length) {
  755.                 if (numfound)
  756.                     minmatch = min(minmatch, numcomp(Possible[lastmatch], Possible[i]));
  757.                 else
  758.                     minmatch = strlen(Possible[i]);
  759.                 numfound += 1;
  760.                 lastmatch = i;
  761.                 if (strcmp(linebuf, Possible[i]) == 0)
  762.                     break;
  763.             }
  764.         }
  765.  
  766.         if (numfound == 0) {
  767.             rbell();
  768.             if (InJoverc)
  769.                 complain("[\"%s\" unknown]", linebuf);
  770.             /* If we're not in the .joverc then
  771.                let's do something helpful for the
  772.                user. */
  773.             if (maxmatch < length) {
  774.                 char    *cp;
  775.  
  776.                 cp = linebuf + maxmatch;
  777.                 *cp = 0;
  778.                 Eol();
  779.             }
  780.             break;
  781.         }
  782.             if (c != '\t' && numfound == 1) {
  783.                 comp_value = lastmatch;
  784.             return 0;
  785.         }
  786.         null_ncpy(linebuf, Possible[lastmatch], minmatch);
  787.             Eol();
  788.         if (minmatch == length)    /* No difference */
  789.             rbell();
  790.         break;
  791.         }
  792.  
  793.     case '?':
  794.         if (InJoverc)
  795.             complain((char *) 0);
  796.         /* kludge: in case we're using UseBuffers, in which case
  797.            linebuf gets written all over */
  798.         strcpy(Minibuf, linebuf);
  799.         length = strlen(Minibuf);
  800.         TOstart("Completion", TRUE);    /* for now ... */
  801.         for (i = 0; Possible[i]; i++)
  802.             if (numcomp(Possible[i], Minibuf) >= length) {
  803.                 Typeout(Possible[i]);
  804.                 if (TOabort != 0)
  805.                     break;
  806.             }
  807.  
  808.         TOstop();
  809.         break;
  810.     }
  811.     return !FALSE;
  812. }
  813.  
  814. int
  815. complete(possible, prompt, flags)
  816. register char    *possible[];
  817. char    *prompt;
  818. {
  819.     Possible = possible;
  820.     comp_flags = flags;
  821.     (void) do_ask("\r\n \t?", aux_complete, NullStr, prompt);
  822.     return comp_value;
  823. }
  824.  
  825. int
  826. match(choices, what)
  827. register char    **choices,
  828.         *what;
  829. {
  830.     register int    len;
  831.     int    i,
  832.         found = 0,
  833.         save,
  834.         exactmatch = -1;
  835.  
  836.     len = strlen(what);
  837.     if (len == 0)
  838.         return NULLSTRING;
  839.     for (i = 0; choices[i]; i++) {
  840.         if (strncmp(what, choices[i], len) == 0) {
  841.             if (strcmp(what, choices[i]) == 0)
  842.                 exactmatch = i;
  843.             save = i;
  844.             found += 1;    /* found one */
  845.         }
  846.     }
  847.  
  848.     if (found == 0)
  849.         save = ORIGINAL;
  850.     else if (found > 1) {
  851.         if (exactmatch != -1)
  852.             save = exactmatch;
  853.         else
  854.             save = AMBIGUOUS;
  855.     }
  856.  
  857.     return save;
  858. }
  859.  
  860. void
  861. Source()
  862. {
  863.     char    *com, *getenv(),
  864.         buf[FILESIZE];
  865.  
  866. #ifndef MSDOS
  867.     sprintf(buf, "%s/.joverc", getenv("HOME"));
  868. #else /* MSDOS */
  869.     if (com = getenv("JOVERC"))
  870.         strcpy(buf, com);
  871.     else
  872.         strcpy(buf, Joverc);
  873. #endif /* MSDOS */
  874.     com = ask_file((char *) 0, buf, buf);
  875.     if (joverc(buf) == 0)
  876.         complain(IOerr("read", com));
  877. }
  878.  
  879. void
  880. BufPos()
  881. {
  882.     register Line    *lp = curbuf->b_first;
  883.     register int    i,
  884.             dotline;
  885.     long    dotchar,
  886.         nchars;
  887.  
  888.     for (i = nchars = 0; lp != 0; i++, lp = lp->l_next) {
  889.         if (lp == curline) {
  890.             dotchar = nchars + curchar;
  891.             dotline = i + 1;
  892.         }
  893.         nchars += length(lp) + (lp->l_next != 0); /* include the NL */
  894.     }
  895.  
  896.     s_mess("[\"%s\" line %d/%d, char %D/%D (%d%%), cursor = %d/%d]",
  897.            filename(curbuf), dotline, i, dotchar, nchars,
  898.            (nchars == 0) ? 100 : (int) (((long) dotchar * 100) / nchars),
  899.            calc_pos(linebuf, curchar),
  900.            calc_pos(linebuf, strlen(linebuf)));
  901. }
  902.  
  903. #define IF_UNBOUND    -1
  904. #define IF_TRUE        1
  905. #define IF_FALSE    !IF_TRUE
  906.  
  907. #ifndef MAC
  908. int
  909. do_if(cmd)
  910. char    *cmd;
  911. {
  912. #ifdef MSDOS
  913.     int status;
  914. #else    
  915.     int    pid,
  916.         status;
  917. #endif /* MSDOS */
  918. #ifndef MSDOS
  919.  
  920.     switch (pid = fork()) {
  921.     case -1:
  922.         complain("[Fork failed: if]");
  923.  
  924.     case 0:
  925.         {
  926. #endif /* MSDOS */
  927.         char    *args[12],
  928.             *cp = cmd,
  929.             **ap = args;
  930.  
  931.             *ap++ = cmd;
  932.             for (;;) {
  933.             if ((cp = index(cp, ' ')) == 0)
  934.                 break;
  935.             *cp++ = '\0';
  936.             *ap++ = cp;
  937.         }
  938.         *ap = 0;
  939.  
  940. #ifndef MSDOS
  941.         close(0);    /*    we want reads to fail */
  942.         /* close(1);     but not writes or ioctl's
  943.         close(2);    */
  944. #else /* MSDOS */
  945.     if ((status = spawnvp(0, args[0], args)) < 0)
  946.         complain("[Spawn failed: if]");
  947. #endif /* MSDOS */
  948.  
  949. #ifndef MSDOS
  950.             (void) execvp(args[0], args);
  951.         _exit(-10);    /* signals exec error (see below) */
  952.         }
  953.     }
  954. #ifdef IPROCS
  955.     sighold(SIGCHLD);
  956. #endif
  957.     dowait(pid, &status);
  958. #ifdef IPROCS
  959.     sigrelse(SIGCHLD);
  960. #endif
  961.     if (status == -10)
  962.         complain("[Exec failed]");
  963.     if (status < 0)
  964.         complain("[Exit %d]", status);
  965. #endif /* MSDOS */
  966.     return (status == 0);    /* 0 means successful */
  967. }
  968. #endif /* MAC */
  969.  
  970. int
  971. joverc(file)
  972. char    *file;
  973. {
  974.     char    buf[LBSIZE],
  975.         lbuf[LBSIZE];
  976.     int    lnum = 0,
  977.         eof = FALSE;
  978.     jmp_buf    savejmp;
  979.     int    IfStatus = IF_UNBOUND;
  980.     File    *fp;
  981.  
  982.     fp = open_file(file, buf, F_READ, !COMPLAIN, QUIET);
  983.     if (fp == NIL)
  984.         return NO;    /* joverc returns an integer */
  985.  
  986.     /* Catch any errors, here, and do the right thing with them,
  987.        and then restore the error handle to whoever did a setjmp
  988.        last. */
  989.  
  990.     InJoverc += 1;
  991.     push_env(savejmp);
  992.     if (setjmp(mainjmp)) {
  993.         Buffer    *savebuf = curbuf;
  994.  
  995.         SetBuf(do_select((Window *) 0, "RC errors"));
  996.         ins_str(sprint("%s:%d:%s\t%s\n", pr_name(file, YES), lnum, lbuf, mesgbuf), NO);
  997.         unmodify();
  998.         SetBuf(savebuf);
  999.         Asking = 0;
  1000.     }
  1001.     if (!eof) do {
  1002.         eof = (f_gets(fp, lbuf, sizeof lbuf) == EOF);
  1003.         lnum += 1;
  1004.         if (lbuf[0] == '#')        /* a comment */
  1005.             continue;
  1006. #ifndef MAC
  1007.         if (casencmp(lbuf, "if", 2) == 0) {
  1008.             char    cmd[128];
  1009.  
  1010.             if (IfStatus != IF_UNBOUND)
  1011.                 complain("[Cannot have nested if's]");
  1012.             if (LookingAt("if[ \t]*\\(.*\\)$", lbuf, 0) == 0)
  1013.                 complain("[If syntax error]");
  1014.             putmatch(1, cmd, sizeof cmd);
  1015.             IfStatus = do_if(cmd) ? IF_TRUE : IF_FALSE;
  1016.             continue;
  1017.         } else if (casencmp(lbuf, "else", 4) == 0) {
  1018.             if (IfStatus == IF_UNBOUND)
  1019.                 complain("[Unexpected `else']");
  1020.             IfStatus = !IfStatus;
  1021.             continue;
  1022.         } else if (casencmp(lbuf, "endif", 5) == 0) {
  1023.             if (IfStatus == IF_UNBOUND)
  1024.                 complain("[Unexpected `endif']");
  1025.             IfStatus = IF_UNBOUND;
  1026.             continue;
  1027.         }
  1028. #endif
  1029.         if (IfStatus == IF_FALSE)
  1030.             continue;
  1031.         (void) strcat(lbuf, "\n");
  1032.         Inputp = lbuf;
  1033.         while (*Inputp == ' ' || *Inputp == '\t')
  1034.             Inputp += 1;    /* skip white space */
  1035.         Extend();
  1036.     } while (!eof);
  1037.  
  1038.     f_close(fp);
  1039.     pop_env(savejmp);
  1040.     Inputp = 0;
  1041.     Asking = 0;
  1042.     InJoverc -= 1;
  1043.     if (IfStatus != IF_UNBOUND)
  1044.         complain("[Missing endif]");
  1045.     return 1;
  1046. }
  1047.