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