home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume14 / jove4.9 / part06 / ask.c next >
C/C++ Source or Header  |  1988-04-25  |  12KB  |  584 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 "termcap.h"
  10. #include "ctype.h"
  11. #include <signal.h>
  12.  
  13. #ifdef MAC
  14. #    include "mac.h"
  15. #else
  16. #    include <varargs.h>
  17. #    ifdef F_COMPLETION
  18. #       include <sys/stat.h>
  19. #    endif
  20. #endif /* MAC */
  21.  
  22. #ifdef MAC
  23. #    undef private
  24. #    define private
  25. #endif
  26.  
  27. #ifdef    LINT_ARGS
  28. private Buffer * get_minibuf(void);
  29. private char * real_ask(char *, int (*)(), char *, char *);
  30.  
  31. private int
  32.     f_complete(int),
  33.     bad_extension(char *, char *),
  34.     crush_bads(char **, int),
  35.     isdir(char *);
  36. private void
  37.     fill_in(char **, int),
  38.     EVexpand(void);
  39. #else
  40. private Buffer * get_minibuf();
  41. private char * real_ask();
  42.  
  43. private int
  44.     f_complete(),
  45.     bad_extension(),
  46.     crush_bads(),
  47.     isdir();
  48. private void
  49.     fill_in(),
  50.     EVexpand();
  51. #endif    /* LINT_ARGS */
  52.  
  53. #ifdef MAC
  54. #    undef private
  55. #    define private static
  56. #endif
  57.  
  58. int    AbortChar = CTL('G'),
  59.     DoEVexpand = NO;    /* should we expand evironment variables? */
  60.  
  61. int    Asking = NO;
  62. char    Minibuf[LBSIZE];
  63. private Line    *CurAskPtr = 0;    /* points at some line in mini-buffer */
  64. private Buffer    *AskBuffer = 0;    /* Askbuffer points to actual structure */
  65.  
  66. /* The way the mini-buffer works is this:  The first line of the mini-buffer
  67.    is where the user does his stuff.  The rest of the buffer contains
  68.    strings that the user often wants to use, for instance, file names, or
  69.    common search strings, etc.  If he types C-N or C-P while in ask(), we
  70.    bump the point up or down a line and extract the contents (we make sure
  71.    is somewhere in the mini-buffer). */
  72.  
  73. static Buffer *
  74. get_minibuf()
  75. {
  76.     if (AskBuffer) {        /* make sure ut still exists */
  77.         register Buffer    *b;
  78.  
  79.         for (b = world; b != 0; b = b->b_next)
  80.             if (b == AskBuffer)
  81.                 return b;
  82.     }
  83.     AskBuffer = do_select((Window *) 0, "*minibuf*");
  84.     AskBuffer->b_type = B_SCRATCH;
  85.     return AskBuffer;
  86. }
  87.  
  88. /* Add a string to the mini-buffer. */
  89.  
  90. void
  91. minib_add(str, movedown)
  92. char    *str;
  93. {
  94.     register Buffer    *saveb = curbuf;
  95.  
  96.     SetBuf(get_minibuf());
  97.     LineInsert(1);
  98.     ins_str(str, NO);
  99.     if (movedown)
  100.         CurAskPtr = curline;
  101.     SetBuf(saveb);
  102. }
  103.  
  104. /* look for any substrings of the form $foo in linebuf, and expand
  105.    them according to their value in the environment (if possible) -
  106.    this munges all over curchar and linebuf without giving it a second
  107.    thought (I must be getting lazy in my old age) */
  108. private void
  109. EVexpand()
  110. {
  111.     register int    c;
  112.     register char    *lp = linebuf,
  113.             *ep;
  114.     char    varname[128],
  115.         *vp,
  116.         *lp_start;
  117.     Mark    *m = MakeMark(curline, curchar, M_FLOATER);
  118.  
  119.     while (c = *lp++) {
  120.         if (c != '$')
  121.             continue;
  122.         lp_start = lp - 1;    /* the $ */
  123.         vp = varname;
  124.         while (c = *lp++) {
  125.             if (!isword(c))
  126.                 break;
  127.             *vp++ = c;
  128.         }
  129.         *vp = '\0';
  130.         /* if we find an env. variable with the right
  131.            name, we insert it in linebuf, and then delete
  132.            the variable name that we're replacing - and
  133.             then we continue in case there are others ... */
  134.         if (ep = getenv(varname)) {
  135.             curchar = lp_start - linebuf;
  136.             ins_str(ep, NO);
  137.             del_char(FORWARD, strlen(varname) + 1);
  138.             lp = linebuf + curchar;
  139.         }
  140.     }
  141.     ToMark(m);
  142.     DelMark(m);
  143. }
  144.  
  145. private char *
  146. real_ask(delim, d_proc, def, prompt)
  147. char    *delim,
  148.     *def,
  149.     *prompt;
  150. int    (*d_proc)();
  151. {
  152.     static int    InAsk = 0;
  153.     jmp_buf    savejmp;
  154.     int    c,
  155.         prompt_len;
  156.     Buffer    *saveb = curbuf;
  157.     int    abort = 0,
  158.         no_typed = 0;
  159.     data_obj    *push_cmd = LastCmd;
  160.     int    o_a_v = arg_value(),
  161.         o_i_an_a = is_an_arg();
  162. #ifdef MAC
  163.         menus_off();
  164. #endif
  165.  
  166.     if (InAsk)
  167.         complain((char *) 0);
  168.     push_env(savejmp);
  169.     InAsk += 1;
  170.     SetBuf(get_minibuf());
  171.     if (!inlist(AskBuffer->b_first, CurAskPtr))
  172.         CurAskPtr = curline;
  173.     prompt_len = strlen(prompt);
  174.     ToFirst();    /* Beginning of buffer. */
  175.     linebuf[0] = '\0';
  176.     modify();
  177.     makedirty(curline);
  178.  
  179.     if (setjmp(mainjmp))
  180.         if (InJoverc) {        /* this is a kludge */
  181.             abort = YES;
  182.             goto cleanup;
  183.         }
  184.  
  185.     for (;;) {
  186.         clr_arg_value();
  187.         last_cmd = this_cmd;
  188.         init_strokes();
  189. cont:        s_mess("%s%s", prompt, linebuf);
  190.         Asking = curchar + prompt_len;
  191.         c = getch();
  192.         if ((c == EOF) || index(delim, c)) {
  193.             if (DoEVexpand)
  194.                 EVexpand();
  195.             if (d_proc == (int(*)())0 || (*d_proc)(c) == 0)
  196.                 goto cleanup;
  197.         } else if (c == AbortChar) {
  198.             message("[Aborted]");
  199.             abort = YES;
  200.             goto cleanup;
  201.         } else switch (c) {
  202.         case CTL('N'):
  203.         case CTL('P'):
  204.             if (CurAskPtr != 0) {
  205.                 int    n = (c == CTL('P') ? -arg_value() : arg_value());
  206.                 CurAskPtr = next_line(CurAskPtr, n);
  207.                 if (CurAskPtr == curbuf->b_first && CurAskPtr->l_next != 0)
  208.                     CurAskPtr = CurAskPtr->l_next;
  209.                 (void) ltobuf(CurAskPtr, linebuf);
  210.                 modify();
  211.                 makedirty(curline);
  212.                 Eol();
  213.                 this_cmd = 0;
  214.             }
  215.             break;
  216.  
  217.         case CTL('R'):
  218.             if (def)
  219.                 ins_str(def, NO);
  220.             else
  221.                 rbell();
  222.             break;
  223.  
  224.         default:
  225.             dispatch(c);
  226.             break;
  227.         }
  228.         if (curbuf != AskBuffer)
  229.             SetBuf(AskBuffer);
  230.         if (curline != curbuf->b_first) {
  231.             CurAskPtr = curline;
  232.             curline = curbuf->b_first;    /* with whatever is in linebuf */
  233.         }
  234.         if (this_cmd == ARG_CMD)
  235.             goto cont;
  236.     }
  237. cleanup:
  238.     pop_env(savejmp);
  239.  
  240.     LastCmd = push_cmd;
  241.     set_arg_value(o_a_v);
  242.     set_is_an_arg(o_i_an_a);
  243.     no_typed = (linebuf[0] == '\0');
  244.     strcpy(Minibuf, linebuf);
  245.     SetBuf(saveb);
  246.     InAsk = Asking = Interactive = NO;
  247.     if (!abort) {
  248.         if (!charp()) {
  249.             Placur(ILI, 0);
  250.             flusho();
  251.         }
  252.         if (no_typed)
  253.             return 0;
  254.     } else
  255.         complain(mesgbuf);
  256.     return Minibuf;
  257. }
  258.  
  259. /* VARARGS2 */
  260.  
  261. char *
  262. ask(def, fmt, va_alist)
  263. char    *def,
  264.     *fmt;
  265. va_dcl
  266. {
  267.     char    prompt[128];
  268.     char    *ans;
  269.     va_list    ap;
  270.  
  271.     va_start(ap);
  272.     format(prompt, sizeof prompt, fmt, ap);
  273.     va_end(ap);
  274.     ans = real_ask("\r\n", (int (*)()) 0, def, prompt);
  275.     if (ans == 0) {        /* Typed nothing. */
  276.         if (def == 0)
  277.             complain("[No default]");
  278.         return def;
  279.     }
  280.     return ans;
  281. }
  282.  
  283. /* VARARGS1 */
  284.  
  285. char *
  286. do_ask(delim, d_proc, def, fmt, va_alist)
  287. char    *delim,
  288.     *def,
  289.     *fmt;
  290. int    (*d_proc)();
  291. va_dcl
  292. {
  293.     char    prompt[128];
  294.     va_list    ap;
  295.  
  296.     va_start(ap);
  297.     format(prompt, sizeof prompt, fmt, ap);
  298.     va_end(ap);
  299.     return real_ask(delim, d_proc, def, prompt);
  300. }
  301.  
  302. /* VARARGS1 */
  303.  
  304. int
  305. yes_or_no_p(fmt, va_alist)
  306. char    *fmt;
  307. va_dcl
  308. {
  309.     char    prompt[128];
  310.     int    c;
  311.     va_list    ap;
  312.  
  313.     va_start(ap);
  314.     format(prompt, sizeof prompt, fmt, ap);
  315.     va_end(ap);
  316.     for (;;) {
  317.         message(prompt);
  318.         Asking = strlen(prompt);    /* so redisplay works */
  319.         c = getch();
  320.         Asking = NO;
  321.         if (c == AbortChar)
  322.             complain("[Aborted]");
  323.         switch (CharUpcase(c)) {
  324.         case 'Y':
  325.             return YES;
  326.  
  327.         case 'N':
  328.             return NO;
  329.  
  330.         default:
  331.             add_mess("[Type Y or N]");
  332.             SitFor(10);
  333.         }
  334.     }
  335.     /* NOTREACHED */
  336. }
  337.  
  338. #ifdef F_COMPLETION
  339. static char    *fc_filebase;
  340. int    DispBadFs = YES;    /* display bad file names? */
  341. #ifndef MSDOS
  342. char    BadExtensions[128] = ".o";
  343. #else /* MSDOS */
  344. char    BadExtensions[128] = ".obj .exe .com .bak .arc .lib .zoo";
  345. #endif /* MSDOS */
  346.  
  347. static
  348. bad_extension(name, bads)
  349. char    *name,
  350.     *bads;
  351. {
  352.     char    *ip;
  353.     int    namelen = strlen(name),
  354.         ext_len,
  355.         stop = 0;
  356.  
  357.     do {
  358.         if (ip = index(bads, ' '))
  359.             *ip = 0;
  360.         else {
  361.             ip = bads + strlen(bads);
  362.             stop = YES;
  363.         }
  364.         if ((ext_len = ip - bads) == 0)
  365.             continue;
  366.         if ((ext_len < namelen) &&
  367.             (strcmp(&name[namelen - ext_len], bads) == 0))
  368.             return YES;
  369.     } while ((bads = ip + 1), !stop);
  370.     return NO;
  371. }
  372.  
  373. int
  374. f_match(file)
  375. char    *file;
  376. {
  377.     int    len = strlen(fc_filebase);
  378.     char    bads[128];
  379.  
  380.     if (DispBadFs == NO) {
  381.         strcpy(bads, BadExtensions);
  382.         /* bad_extension() is destructive */
  383.         if (bad_extension(file, bads))
  384.             return NO;
  385.     }
  386.  
  387.     return ((len == 0) ||
  388. #ifdef MSDOS
  389.         (casencmp(file, fc_filebase, strlen(fc_filebase)) == 0)
  390. #else
  391.         (strncmp(file, fc_filebase, strlen(fc_filebase)) == 0)
  392. #endif
  393.         );
  394. }
  395.  
  396. static
  397. isdir(name)
  398. char    *name;
  399. {
  400.     struct stat    stbuf;
  401.     char    filebuf[FILESIZE];
  402.  
  403.     PathParse(name, filebuf);
  404.     return ((stat(filebuf, &stbuf) != -1) &&
  405.         (stbuf.st_mode & S_IFDIR) == S_IFDIR);
  406. }
  407.  
  408. private void
  409. fill_in(dir_vec, n)
  410. register char    **dir_vec;
  411. {
  412.     int    minmatch = 0,
  413.             numfound = 0,
  414.             lastmatch = -1,
  415.         i,
  416.         the_same = TRUE, /* After filling in, are we the same
  417.                     as when we were called? */
  418.         is_ntdir;    /* Is Newly Typed Directory name */
  419.     char    bads[128];
  420.  
  421.     for (i = 0; i < n; i++) {
  422.         /* if it's no, then we have already filtered them out
  423.            in f_match() so there's no point in doing it again */
  424.         if (DispBadFs == YES) {
  425.             strcpy(bads, BadExtensions);
  426.             /* bad_extension() is destructive */
  427.             if (bad_extension(dir_vec[i], bads))
  428.                 continue;
  429.         }
  430.         if (numfound)
  431.             minmatch = min(minmatch,
  432.                        numcomp(dir_vec[lastmatch], dir_vec[i]));
  433.         else
  434.             minmatch = strlen(dir_vec[i]);
  435.         lastmatch = i;
  436.         numfound += 1;
  437.     }
  438.     /* Ugh.  Beware--this is hard to get right in a reasonable
  439.        manner.  Please excuse this code--it's past my bedtime. */
  440.     if (numfound == 0) {
  441.         rbell();
  442.         return;
  443.     }
  444.     Eol();
  445.     if (minmatch > strlen(fc_filebase)) {
  446.         the_same = FALSE;
  447.         null_ncpy(fc_filebase, dir_vec[lastmatch], minmatch);
  448.         Eol();
  449.         makedirty(curline);
  450.     }
  451.     is_ntdir = ((numfound == 1) &&
  452.             (curchar > 0) &&
  453.             (linebuf[curchar - 1] != '/') &&
  454.             (isdir(linebuf)));
  455.     if (the_same && !is_ntdir) {
  456.         add_mess((n == 1) ? " [Unique]" : " [Ambiguous]");
  457.         SitFor(7);
  458.     }
  459.     if (is_ntdir)
  460.         insert_c('/', 1);
  461. }
  462.  
  463. extern int    alphacomp();
  464.  
  465. /* called from do_ask() when one of "\r\n ?" is typed.  Does the right
  466.    thing, depending on which. */
  467.  
  468. static
  469. f_complete(c)
  470. {
  471.     char    dir[FILESIZE],
  472.         **dir_vec;
  473.     int    nentries,
  474.         i;
  475.  
  476.     if (c == CR || c == LF)
  477.         return 0;    /* tells ask to return now */
  478. #ifndef MSDOS        /* kg */
  479.     if ((fc_filebase = rindex(linebuf, '/')) != 0) {
  480. #else /* MSDOS */
  481.     fc_filebase = rindex(linebuf, '/');
  482.     if (fc_filebase == (char *)0)
  483.         fc_filebase = rindex(linebuf, '\\');
  484.     if (fc_filebase == (char *)0)
  485.         fc_filebase = rindex(linebuf, ':');
  486.     if (fc_filebase != (char *)0) {
  487. #endif /* MSDOS */
  488.         char    tmp[FILESIZE];
  489.  
  490.         fc_filebase += 1;
  491.         null_ncpy(tmp, linebuf, (fc_filebase - linebuf));
  492.         if (tmp[0] == '\0')
  493.             strcpy(tmp, "/");
  494.         PathParse(tmp, dir);
  495.     } else {        
  496.         fc_filebase = linebuf;
  497.         strcpy(dir, ".");
  498.     }
  499.     if ((nentries = scandir(dir, &dir_vec, f_match, alphacomp)) == -1) {
  500.         add_mess(" [Unknown directory: %s]", dir);
  501.         SitFor(7);
  502.         return 1;
  503.     }
  504.     if (nentries == 0) {
  505.         add_mess(" [No match]");
  506.         SitFor(7);
  507.     } else if (c == ' ' || c == '\t')
  508.         fill_in(dir_vec, nentries);
  509.     else {
  510.         /* we're a '?' */
  511.         int    maxlen = 0,
  512.             ncols,
  513.             col,
  514.             lines,
  515.             linespercol;
  516.  
  517.         TOstart("Completion", FALSE);    /* false means newline only on request */
  518.         Typeout("(! means file will not be chosen unless typed explicitly)");
  519.         Typeout((char *) 0);
  520.         Typeout("Possible completions (in %s):", dir);
  521.         Typeout((char *) 0);
  522.  
  523.         for (i = 0; i < nentries; i++)
  524.             maxlen = max(strlen(dir_vec[i]), maxlen);
  525.         maxlen += 4;    /* pad each column with at least 4 spaces */
  526.         ncols = (CO - 2) / maxlen;
  527.         linespercol = 1 + (nentries / ncols);
  528.  
  529.         for (lines = 0; lines < linespercol; lines++) {
  530.             for (col = 0; col < ncols; col++) {
  531.                 int    isbad,
  532.                     which;
  533.                 char    bads[128];
  534.  
  535.                 which = (col * linespercol) + lines;
  536.                 if (which >= nentries)
  537.                     break;
  538.                 if (DispBadFs == YES) {
  539.                     strcpy(bads, BadExtensions);
  540.                     isbad = bad_extension(dir_vec[which], bads);
  541.                 } else
  542.                     isbad = NO;
  543.                 Typeout("%s%-*s", isbad ? "!" : NullStr,
  544.                     maxlen - isbad, dir_vec[which]);
  545.             }
  546.             Typeout((char *) 0);
  547.         }
  548.         TOstop();
  549.     }
  550.     freedir(&dir_vec, nentries);
  551.     return 1;
  552. }
  553.  
  554. #endif
  555.  
  556. char *
  557. ask_file(prmt, def, buf)
  558. char    *prmt,
  559.     *def,
  560.     *buf;
  561. {
  562.     char    *ans,
  563.         prompt[128],
  564.         *pretty_name = pr_name(def, YES);
  565.     if (prmt)
  566.         sprintf(prompt, prmt);
  567.     else {
  568.         if (def != 0 && *def != '\0')
  569.             sprintf(prompt, ": %f (default %s) ", pretty_name);
  570.         else
  571.             sprintf(prompt, ProcFmt);
  572.     }
  573. #ifdef F_COMPLETION
  574.       ans = real_ask("\r\n \t?", f_complete, pretty_name, prompt);
  575.     if (ans == 0 && (ans = pretty_name) == 0)
  576.         complain("[No default file name]");
  577. #else
  578.     ans = ask(pretty_name, prompt);
  579. #endif
  580.     PathParse(ans, buf);
  581.  
  582.     return buf;
  583. }
  584.