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