home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume8 / jove / part01 / ask.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-02-02  |  9.0 KB  |  462 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 "termcap.h"
  10. #include "ctype.h"
  11. #include <signal.h>
  12. #include <varargs.h>
  13.  
  14. #ifdef F_COMPLETION
  15. #    include <sys/stat.h>
  16. #endif
  17.  
  18. int    Asking = NO;
  19. char    Minibuf[LBSIZE];
  20. private Line    *CurAskPtr = 0;    /* points at some line in mini-buffer */
  21. private Buffer    *AskBuffer = 0;    /* Askbuffer points to actual structure */
  22.  
  23. /* The way the mini-buffer works is this:  The first line of the mini-buffer
  24.    is where the user does his stuff.  The rest of the buffer contains
  25.    strings that the user often wants to use, for instance, file names, or
  26.    common search strings, etc.  If he types C-N or C-P while in ask(), we
  27.    bump the point up or down a line and extract the contents (we make sure
  28.    is somewhere in the mini-buffer). */
  29.  
  30. static Buffer *
  31. get_minibuf()
  32. {
  33.     if (AskBuffer) {        /* make sure ut still exists */
  34.         register Buffer    *b;
  35.  
  36.         for (b = world; b != 0; b = b->b_next)
  37.             if (b == AskBuffer)
  38.                 return b;
  39.     }
  40.     AskBuffer = do_select((Window *) 0, "*minibuf*");
  41.     AskBuffer->b_type = B_SCRATCH;
  42.     return AskBuffer;
  43. }
  44.  
  45. /* Add a string to the mini-buffer. */
  46.  
  47. minib_add(str, movedown)
  48. char    *str;
  49. {
  50.     register Buffer    *saveb = curbuf;
  51.  
  52.     SetBuf(get_minibuf());
  53.     LineInsert(1);
  54.     ins_str(str, NO);
  55.     if (movedown)
  56.         CurAskPtr = curline;
  57.     SetBuf(saveb);
  58. }
  59.  
  60. static char *
  61. real_ask(delim, d_proc, def, prompt)
  62. char    *delim,
  63.     *def,
  64.     *prompt;
  65. int    (*d_proc)();
  66. {
  67.     static int    InAsk = 0;
  68.     jmp_buf    savejmp;
  69.     int    c,
  70.         prompt_len;
  71.     Buffer    *saveb = curbuf;
  72.     int    abort = 0,
  73.         no_typed = 0;
  74.     data_obj    *push_cmd = LastCmd;
  75.     int    o_exp = exp,
  76.         o_exp_p = exp_p;
  77.  
  78.     if (InAsk)
  79.         complain((char *) 0);
  80.     push_env(savejmp);
  81.     InAsk++;
  82.     SetBuf(get_minibuf());
  83.     if (!inlist(AskBuffer->b_first, CurAskPtr))
  84.         CurAskPtr = curline;
  85.     prompt_len = strlen(prompt);
  86.     ToFirst();    /* Beginning of buffer. */
  87.     linebuf[0] = '\0';
  88.     modify();
  89.     makedirty(curline);
  90.  
  91.     if (setjmp(mainjmp))
  92.         if (InJoverc) {        /* this is a kludge */
  93.             abort++;
  94.             goto cleanup;
  95.         }
  96.  
  97.     for (;;) {
  98.         exp = 1;
  99.         exp_p = NO;
  100.         last_cmd = this_cmd;
  101.         init_strokes();
  102. cont:        s_mess("%s%s", prompt, linebuf);
  103.         Asking = curchar + prompt_len;
  104.         c = getch();
  105.         if ((c == EOF) || index(delim, c)) {
  106.             if (d_proc == 0 || (*d_proc)(c) == 0)
  107.                 goto cleanup;
  108.         } else switch (c) {
  109.         case CTL(G):
  110.             message("[Aborted]");
  111.             abort++;
  112.             goto cleanup;
  113.  
  114.         case CTL(N):
  115.         case CTL(P):
  116.             if (CurAskPtr != 0) {
  117.                 int    n = (c == CTL(P) ? -exp : exp);
  118.  
  119.                 CurAskPtr = next_line(CurAskPtr, n);
  120.                 if (CurAskPtr == curbuf->b_first && CurAskPtr->l_next != 0)
  121.                     CurAskPtr = CurAskPtr->l_next;
  122.                 (void) ltobuf(CurAskPtr, linebuf);
  123.                 modify();
  124.                 makedirty(curline);
  125.                 Eol();
  126.                 this_cmd = 0;
  127.             }
  128.             break;
  129.  
  130.         case CTL(R):
  131.             if (def)
  132.                 ins_str(def, NO);
  133.             else
  134.                 rbell();
  135.             break;
  136.  
  137.         default:
  138.             dispatch(c);
  139.             break;
  140.         }
  141.         if (curbuf != AskBuffer)
  142.             SetBuf(AskBuffer);
  143.         if (curline != curbuf->b_first) {
  144.             CurAskPtr = curline;
  145.             curline = curbuf->b_first;    /* with whatever is in linebuf */
  146.         }
  147.         if (this_cmd == ARG_CMD)
  148.             goto cont;
  149.     }
  150. cleanup:
  151.     pop_env(savejmp);
  152.  
  153.     LastCmd = push_cmd;
  154.     exp_p = o_exp_p;
  155.     exp = o_exp;
  156.     no_typed = (linebuf[0] == '\0');
  157.     strcpy(Minibuf, linebuf);
  158.     SetBuf(saveb);
  159.     InAsk = Asking = Interactive = NO;
  160.     if (!abort) {
  161.         if (!charp()) {
  162.             Placur(ILI, 0);
  163.             flusho();
  164.         }
  165.         if (no_typed)
  166.             return 0;
  167.     } else
  168.         complain(mesgbuf);
  169.     return Minibuf;
  170. }
  171.  
  172. /* VARARGS2 */
  173.  
  174. char *
  175. ask(def, fmt, va_alist)
  176. char    *def,
  177.     *fmt;
  178. va_dcl
  179. {
  180.     char    prompt[128];
  181.     char    *ans;
  182.     va_list    ap;
  183.  
  184.     va_start(ap);
  185.     format(prompt, sizeof prompt, fmt, ap);
  186.     va_end(ap);
  187.     ans = real_ask("\r\n", (int (*)()) 0, def, prompt);
  188.     if (ans == 0) {        /* Typed nothing. */
  189.         if (def == 0)
  190.             complain("[No default]");
  191.         return def;
  192.     }
  193.     return ans;
  194. }
  195.  
  196. /* VARARGS1 */
  197.  
  198. char *
  199. do_ask(delim, d_proc, def, fmt, va_alist)
  200. char    *delim,
  201.     *def,
  202.     *fmt;
  203. int    (*d_proc)();
  204. va_dcl
  205. {
  206.     char    prompt[128];
  207.     va_list    ap;
  208.  
  209.     va_start(ap);
  210.     format(prompt, sizeof prompt, fmt, ap);
  211.     va_end(ap);
  212.     return real_ask(delim, d_proc, def, prompt);
  213. }
  214.  
  215. /* VARARGS2 */
  216.  
  217. yes_or_no_p(fmt, va_alist)
  218. char    *fmt;
  219. va_dcl
  220. {
  221.     char    prompt[128];
  222.     int    c;
  223.     va_list    ap;
  224.  
  225.     va_start(ap);
  226.     format(prompt, sizeof prompt, fmt, ap);
  227.     va_end(ap);
  228.     for (;;) {
  229.         message(prompt);
  230.         Asking = strlen(prompt);    /* so redisplay works */
  231.         c = getch();
  232.         Asking = NO;
  233.         switch (Upper(c)) {
  234.         case 'Y':
  235.             return YES;
  236.  
  237.         case 'N':
  238.             return NO;
  239.  
  240.         case CTL(G):
  241.             complain("[Aborted]");
  242.  
  243.         default:
  244.             add_mess("[Type Y or N]");
  245.             SitFor(10);
  246.         }
  247.     }
  248.     /* NOTREACHED */
  249. }
  250.  
  251. #ifdef F_COMPLETION
  252. static char    *fc_filebase;
  253. char    BadExtensions[128] = ".o";
  254.  
  255. static
  256. bad_extension(name, bads)
  257. char    *name,
  258.     *bads;
  259. {
  260.     char    *ip;
  261.     int    namelen = strlen(name),
  262.         ext_len,
  263.         stop = 0;
  264.  
  265.     do {
  266.         if (ip = index(bads, ' '))
  267.             *ip = 0;
  268.         else {
  269.             ip = bads + strlen(bads);
  270.             stop++;
  271.         }
  272.         if ((ext_len = ip - bads) == 0)
  273.             continue;
  274.         if ((ext_len < namelen) &&
  275.             (strcmp(&name[namelen - ext_len], bads) == 0))
  276.             return YES;
  277.     } while ((bads = ip + 1), !stop);
  278.     return NO;
  279. }
  280.  
  281. f_match(file)
  282. char    *file;
  283. {
  284.     int    len = strlen(fc_filebase);
  285.  
  286.     return ((len == 0) ||
  287.         (strncmp(file, fc_filebase, strlen(fc_filebase)) == 0));
  288. }
  289.  
  290. static
  291. isdir(name)
  292. char    *name;
  293. {
  294.     struct stat    stbuf;
  295.     char    filebuf[FILESIZE];
  296.  
  297.     PathParse(name, filebuf);
  298.     return ((stat(filebuf, &stbuf) != -1) &&
  299.         (stbuf.st_mode & S_IFDIR) == S_IFDIR);
  300. }
  301.  
  302. static
  303. fill_in(dir_vec, n)
  304. register char    **dir_vec;
  305. {
  306.     int    minmatch = 0,
  307.             numfound = 0,
  308.             lastmatch = -1,
  309.         i,
  310.         the_same = TRUE, /* After filling in, are we the same
  311.                     as when we were called? */
  312.         is_ntdir;    /* Is Newly Typed Directory name */
  313.     char    bads[128];
  314.  
  315.     for (i = 0; i < n; i++) {
  316.         strcpy(bads, BadExtensions);
  317.         /* bad_extension() is destructive */
  318.         if (bad_extension(dir_vec[i], bads))
  319.             continue;
  320.         if (numfound)
  321.             minmatch = min(minmatch,
  322.                        numcomp(dir_vec[lastmatch], dir_vec[i]));
  323.         else
  324.             minmatch = strlen(dir_vec[i]);
  325.         lastmatch = i;
  326.         numfound++;
  327.     }
  328.     /* Ugh.  Beware--this is hard to get right in a reasonable
  329.        manner.  Please excuse this code--it's past my bedtime. */
  330.     if (numfound == 0) {
  331.         rbell();
  332.         return;
  333.     }
  334.     Eol();
  335.     if (minmatch > strlen(fc_filebase)) {
  336.         the_same = FALSE;
  337.         null_ncpy(fc_filebase, dir_vec[lastmatch], minmatch);
  338.         Eol();
  339.         makedirty(curline);
  340.     }
  341.     is_ntdir = ((numfound == 1) &&
  342.             (curchar > 0) &&
  343.             (linebuf[curchar - 1] != '/') &&
  344.             (isdir(linebuf)));
  345.     if (the_same && !is_ntdir) {
  346.         add_mess(n == 1 ? " [Unique]" : " [Ambiguous]");
  347.         SitFor(7);
  348.     }
  349.     if (is_ntdir)
  350.         Insert('/');
  351. }
  352.  
  353. extern int    alphacomp();
  354.  
  355. /* called from do_ask() when one of "\r\n ?" is typed.  Does the right
  356.    thing, depending on which. */
  357.  
  358. static
  359. f_complete(c)
  360. {
  361.     char    dir[FILESIZE],
  362.         **dir_vec;
  363.     int    nentries,
  364.         i;
  365.  
  366.     if (c == CR || c == LF)
  367.         return 0;    /* tells ask to return now */
  368.     if ((fc_filebase = rindex(linebuf, '/')) != 0) {
  369.         char    tmp[FILESIZE];
  370.  
  371.         null_ncpy(tmp, linebuf, (++fc_filebase - linebuf));
  372.         if (tmp[0] == '\0')
  373.             strcpy(tmp, "/");
  374.         PathParse(tmp, dir);
  375.     } else {        
  376.         fc_filebase = linebuf;
  377.         strcpy(dir, ".");
  378.     }
  379.     if ((nentries = scandir(dir, &dir_vec, f_match, alphacomp)) == -1) {
  380.         add_mess(" [Unknown directory: %s]", dir);
  381.         SitFor(7);
  382.         return 1;
  383.     }
  384.     if (nentries == 0) {
  385.         add_mess(" [No match]");
  386.         SitFor(7);
  387.     } else if (c == ' ' || c == '\t')
  388.         fill_in(dir_vec, nentries);
  389.     else {
  390.         /* we're a '?' */
  391.         int    maxlen = 0,
  392.             ncols,
  393.             col,
  394.             lines,
  395.             linespercol;
  396.  
  397.         TOstart("Completion", FALSE);    /* false means newline only on request */
  398.         Typeout("(! means file will not be chosen unless typed explicitly)");
  399.         Typeout((char *) 0);
  400.         Typeout("Possible completions (in %s):", dir);
  401.         Typeout((char *) 0);
  402.  
  403.         for (i = 0; i < nentries; i++)
  404.             maxlen = max(strlen(dir_vec[i]), maxlen);
  405.         maxlen += 4;    /* pad each column with at least 4 spaces */
  406.         ncols = (CO - 2) / maxlen;
  407.         linespercol = 1 + (nentries / ncols);
  408.  
  409.         for (lines = 0; lines < linespercol; lines++) {
  410.             for (col = 0; col < ncols; col++) {
  411.                 int    isbad,
  412.                     which;
  413.                 char    bads[128];
  414.  
  415.                 which = (col * linespercol) + lines;
  416.                 if (which >= nentries)
  417.                     break;
  418.                 strcpy(bads, BadExtensions);
  419.                 isbad = bad_extension(dir_vec[which], bads);
  420.                 Typeout("%s%-*s", isbad ? "!" : NullStr,
  421.                     maxlen - isbad, dir_vec[which]);
  422.             }
  423.             Typeout((char *) 0);
  424.         }
  425.         TOstop();
  426.     }
  427.     freedir(&dir_vec, nentries);
  428.     return 1;
  429. }
  430.  
  431. #endif
  432.  
  433. char *
  434. ask_file(prmt, def, buf)
  435. char    *prmt,
  436.     *def,
  437.     *buf;
  438. {
  439.     char    *ans,
  440.         prompt[128],
  441.         *pretty_name = pr_name(def);
  442.  
  443.     if (prmt)
  444.         sprintf(prompt, prmt);
  445.     else {
  446.         if (def != 0 && *def != '\0')
  447.             sprintf(prompt, ": %f (default %s) ", pretty_name);
  448.         else
  449.             sprintf(prompt, ProcFmt);
  450.     }
  451. #ifdef F_COMPLETION
  452.       ans = real_ask("\r\n \t?", f_complete, pretty_name, prompt);
  453.     if (ans == 0 && (ans = pretty_name) == 0)
  454.         complain("[No default file name]");
  455. #else
  456.     ans = ask(pretty_name, prompt);
  457. #endif
  458.     PathParse(ans, buf);
  459.  
  460.     return buf;
  461. }
  462.