home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume8 / jove / part03 / extend.c next >
Encoding:
C/C++ Source or Header  |  1987-02-02  |  17.5 KB  |  935 lines

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