home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / jove-4.16-src.tgz / tar.out / bsd / jove / ask.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  14KB  |  630 lines

  1. /************************************************************************
  2.  * This program is Copyright (C) 1986-1996 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 "jctype.h"
  10. #include "chars.h"
  11. #include "disp.h"
  12. #include "fp.h"
  13. #include "scandir.h"
  14. #include "screen.h"    /* for Placur */
  15. #include "ask.h"
  16. #include "delete.h"
  17. #include "insert.h"
  18. #include "extend.h"
  19. #include "fmt.h"
  20. #include "marks.h"
  21. #include "move.h"
  22.  
  23. #ifdef MAC
  24. # include "mac.h"
  25. #else /* !MAC */
  26. # ifdef F_COMPLETION
  27. #  include <sys/stat.h>
  28. # endif
  29. #endif /* !MAC */
  30.  
  31. ZXchar    AbortChar = CTL('G');    /* VAR: cancels command input */
  32.  
  33. bool    Asking = NO;
  34. int    AskingWidth;
  35.  
  36. char    Minibuf[LBSIZE];
  37. private LinePtr    CurAskPtr = NULL;    /* points at some line in mini-buffer */
  38. private Buffer    *AskBuffer = NULL;    /* Askbuffer points to actual structure */
  39.  
  40. /* The way the mini-buffer works is this:  The first line of the mini-buffer
  41.    is where the user does his stuff.  The rest of the buffer contains
  42.    strings that the user often wants to use, for instance, file names, or
  43.    common search strings, etc.  If he types ^N or ^P while in ask(), we
  44.    bump the point up or down a line and extract the contents (we make sure
  45.    is somewhere in the mini-buffer). */
  46.  
  47. private Buffer *
  48. get_minibuf()
  49. {
  50.     if (AskBuffer) {        /* make sure ut still exists */
  51.         register Buffer    *b;
  52.  
  53.         for (b = world; b != NULL; b = b->b_next)
  54.             if (b == AskBuffer)
  55.                 return b;
  56.     }
  57.     AskBuffer = do_select((Window *)NULL, "*minibuf*");
  58.     AskBuffer->b_type = B_SCRATCH;
  59.     return AskBuffer;
  60. }
  61.  
  62. /* Add a string to the mini-buffer. */
  63.  
  64. void
  65. minib_add(str, movedown)
  66. char    *str;
  67. bool    movedown;
  68. {
  69.     register Buffer    *saveb = curbuf;
  70.  
  71.     SetBuf(get_minibuf());
  72.     LineInsert(1);
  73.     ins_str(str);
  74.     if (movedown)
  75.         CurAskPtr = curline;
  76.     SetBuf(saveb);
  77. }
  78.  
  79. bool    InRealAsk = NO;
  80.  
  81. private char *
  82. real_ask(delim, d_proc, def, prompt)
  83. const char    *delim,
  84.     *def,
  85.     *prompt;
  86. bool    (*d_proc) ptrproto((ZXchar));
  87. {
  88.     jmp_buf    savejmp;
  89.     ZXchar    c;
  90.     int    prompt_len;
  91.     Buffer    *saveb = curbuf;
  92.     volatile bool    aborted = NO;
  93.     bool    no_typed = NO;
  94.     data_obj    *push_cmd = LastCmd;
  95.     int    saved_as, saved_ac;
  96.  
  97. #ifdef MAC
  98.     menus_off();
  99. #endif
  100.     if (InRealAsk)
  101.         complain((char *) NULL);
  102.     save_arg(saved_as, saved_ac);
  103.     push_env(savejmp);
  104.     InRealAsk = YES;
  105.     SetBuf(get_minibuf());
  106.     if (!inlist(AskBuffer->b_first, CurAskPtr))
  107.         CurAskPtr = curline;
  108.     prompt_len = strlen(prompt);
  109.     ToFirst();    /* Beginning of buffer. */
  110.     linebuf[0] = '\0';
  111.     modify();
  112.     makedirty(curline);
  113.  
  114.     if (setjmp(mainjmp)) {
  115.         if (InJoverc) {        /* this is a kludge */
  116.             aborted = YES;
  117.             goto cleanup;
  118.         }
  119.     }
  120.  
  121.     this_cmd = OTHER_CMD;    /* probably redundant */
  122.     for (;;) {
  123.         cmd_sync();
  124.         s_mess("%s%s", prompt, linebuf);
  125.         Asking = YES;
  126.         AskingWidth = curchar + prompt_len;
  127.         c = getch();
  128.         if (c != '\0' && strchr(delim, c) != NULL) {
  129.             if (d_proc == NULL_ASK_EXT || !(*d_proc)(c))
  130.                 break;
  131.         } else if (c == AbortChar) {
  132.             message("[Aborted]");
  133.             aborted = YES;
  134.             break;
  135.         } else switch (c) {
  136.         case CTL('N'):
  137.         case CTL('P'):
  138.             if (CurAskPtr != NULL) {
  139.                 int    n = (c == CTL('P') ? -arg_value() : arg_value());
  140.  
  141.                 CurAskPtr = next_line(CurAskPtr, n);
  142.                 if (CurAskPtr == curbuf->b_first && CurAskPtr->l_next != NULL)
  143.                     CurAskPtr = CurAskPtr->l_next;
  144.                 (void) ltobuf(CurAskPtr, linebuf);
  145.                 modify();
  146.                 makedirty(curline);
  147.                 Eol();
  148.                 this_cmd = OTHER_CMD;
  149.             }
  150.             break;
  151.  
  152.         case CTL('R'):
  153.             if (def != NULL)
  154.                 ins_str(def);
  155.             else
  156.                 rbell();
  157.             break;
  158.  
  159.         default:
  160.             dispatch(c);
  161.             break;
  162.         }
  163.         if (curbuf != AskBuffer)
  164.             SetBuf(AskBuffer);
  165.         if (curline != curbuf->b_first) {
  166.             CurAskPtr = curline;
  167.             curline = curbuf->b_first;    /* with whatever is in linebuf */
  168.         }
  169.     }
  170. cleanup:
  171.     pop_env(savejmp);
  172.  
  173.     LastCmd = push_cmd;
  174.     restore_arg(saved_as, saved_ac);
  175.     no_typed = (linebuf[0] == '\0');
  176.     strcpy(Minibuf, linebuf);
  177.     SetBuf(saveb);
  178.     InRealAsk = Asking = Interactive = NO;
  179.     if (!aborted) {
  180.         if (!PreEmptOutput()) {
  181.             Placur(ILI, 0);
  182.             flushscreen();
  183.         }
  184.         if (no_typed)
  185.             return NULL;
  186.     } else
  187.         complain((char *)NULL);
  188.     return Minibuf;
  189. }
  190.  
  191. #ifdef STDARGS
  192. char *
  193. ask(char *def, char *fmt, ...)
  194. #else
  195. /*VARARGS2*/ char *
  196. ask(def, fmt, va_alist)
  197.     char    *def,
  198.         *fmt;
  199.     va_dcl
  200. #endif
  201. {
  202.     char    prompt[128];
  203.     char    *ans;
  204.     va_list    ap;
  205.  
  206.     va_init(ap, fmt);
  207.     format(prompt, sizeof prompt, fmt, ap);
  208.     va_end(ap);
  209.     ans = real_ask("\r\n", NULL_ASK_EXT, def, prompt);
  210.     if (ans == NULL) {        /* Typed nothing. */
  211.         if (def == NULL)
  212.             complain("[No default]");
  213.         ans = def;
  214.     }
  215.     return ans;
  216. }
  217.  
  218. #ifdef STDARGS
  219. char *
  220. do_ask(const char *delim, bool (*d_proc) ptrproto((ZXchar)), const char *def, const char *fmt, ...)
  221. #else
  222. /*VARARGS4*/ char *
  223. do_ask(delim, d_proc, def, fmt, va_alist)
  224.     const char    *delim;
  225.     bool    (*d_proc) ptrproto((ZXchar));
  226.     const char    *def;
  227.     const char    *fmt;
  228.     va_dcl
  229. #endif
  230. {
  231.     char    prompt[128];
  232.     va_list    ap;
  233.  
  234.     va_init(ap, fmt);
  235.     format(prompt, sizeof prompt, fmt, ap);
  236.     va_end(ap);
  237.     return real_ask(delim, d_proc, def, prompt);
  238. }
  239.  
  240. bool    OneKeyConfirmation = NO;    /* VAR: single y or n keystroke sufficient? */
  241.  
  242. #ifdef STDARGS
  243. bool
  244. yes_or_no_p(char *fmt, ...)
  245. #else
  246. /*VARARGS1*/ bool
  247. yes_or_no_p(fmt, va_alist)
  248.     char    *fmt;
  249.     va_dcl
  250. #endif
  251. {
  252.     char    prompt[128];
  253.     va_list    ap;
  254.  
  255.     va_init(ap, fmt);
  256.     format(prompt, sizeof prompt, fmt, ap);
  257.     va_end(ap);
  258.  
  259.     for (;;) {
  260.         if (OneKeyConfirmation) {
  261.             ZXchar    c;
  262.  
  263.             message(prompt);
  264.             Asking = YES;    /* so redisplay works */
  265.             AskingWidth = strlen(prompt);
  266.             c = getch();
  267.             Asking = NO;
  268.             if (c == AbortChar)
  269.                 complain("[Aborted]");
  270.             switch (CharUpcase(c)) {
  271.                 case 'Y':
  272.                 return YES;
  273.  
  274.                 case 'N':
  275.                 return NO;
  276.  
  277.                 default:
  278.                 add_mess("[Type Y or N]");
  279.                 Asking = YES;    /* so cursor sits on question */
  280.                 SitFor(10);
  281.             }
  282.         } else {
  283.             char *ans = ask((char *) NULL, prompt);
  284.  
  285.             if (caseeqn(ans, "yes", strlen(ans)))
  286.                 return YES;
  287.             if (caseeqn(ans, "no", strlen(ans)))
  288.                 return NO;
  289.             rbell();
  290.         }
  291.     }
  292.     /* NOTREACHED */
  293. }
  294.  
  295. #ifdef F_COMPLETION
  296.  
  297. /* look for any substrings of the form $foo in linebuf, and expand
  298.    them according to their value in the environment (if possible) -
  299.    this munges all over curchar and linebuf without giving it a second
  300.    thought (I must be getting lazy in my old age) */
  301.  
  302. # ifndef MAC    /* no environment in MacOS */
  303.  
  304. bool    DoEVexpand = YES;    /* VAR: should we expand evironment variables? */
  305.  
  306. private void
  307. EVexpand()
  308. {
  309.     register char    c;
  310.     register char    *lp = linebuf,
  311.             *ep;
  312.     char    varname[128],
  313.         *vp,
  314.         *lp_start;
  315.     Mark    *m = MakeMark(curline, curchar);
  316.  
  317.     while ((c = *lp++) != '\0') {
  318.         if (c == '$') {
  319.             lp_start = lp - 1;    /* the $ */
  320.             vp = varname;
  321.             /* Pick up env. variable name ([a-zA-Z0-9_]*) */
  322.             while (('a' <= (c = *lp++) && c <= 'z')
  323.             || ('A' <= c && c <= 'Z')
  324.             || ('0' <= c && c <= '9')
  325.             || c == '_')
  326.                 *vp++ = c;
  327.             *vp = '\0';
  328.             /* if we find an env. variable with the right
  329.                name, we insert it in linebuf, and then delete
  330.                the variable name that we're replacing - and
  331.                then we continue in case there are others ... */
  332.             if ((ep = getenv(varname)) != NULL) {
  333.                 curchar = lp_start - linebuf;
  334.                 ins_str(ep);
  335.                 del_char(FORWARD, (int)strlen(varname) + 1, NO);
  336.                 lp = linebuf + curchar;
  337.             }
  338.         }
  339.     }
  340.     ToMark(m);
  341.     DelMark(m);
  342. }
  343.  
  344. # endif /* !MAC */
  345.  
  346. private char    *fc_filebase;
  347. bool    DispBadFs = YES;    /* VAR: display filenames with bad extensions? */
  348.  
  349. char    BadExtensions[sizeof(BadExtensions)] =    /* VAR: extensions to ignore */
  350. # ifndef MSFILESYSTEM
  351. /* .o: object code format
  352.  * .Z .gz: compressed formats
  353.  * .tar .zip .zoo: archive formats
  354.  * .gif .jpg .jpeg .mpg .mpeg .tif .tiff .rgb: graphics formats
  355.  * .dvi: TeX or ditroff output
  356.  * .elc: compiled GNU EMACS code
  357.  */
  358. ".o .Z .gz .tar .zip .zoo .gif .jpg .jpeg .mpg .mpeg .tif .tiff .rgb .dvi .elc";
  359. # else /* MSFILESYSTEM */
  360. /* .obj .lib .exe .com .dll: object code files
  361.  * .arc .zip .zoo: archive formats
  362.  * .bmp .gif .jpg .mpg .tif .pcx: graphics formats
  363.  * .wks .wk1 .xls: spreadsheet formats
  364.  */
  365. ".obj .lib .exe .com .dll .arc .zip .zoo .bmp .gif .jpg .mpg .tif .pcx .wks .wk1 .xls";
  366. # endif /* MSFILESYSTEM */
  367.  
  368. private bool
  369. bad_extension(name)
  370. char    *name;
  371. {
  372.     char    *ip,
  373.         *bads;
  374.     size_t    namelen = strlen(name),
  375.         ext_len;
  376.  
  377. # if defined(UNIX) || defined(MSFILESYSTEM)
  378. #  ifdef DIRECTORY_ADD_SLASH
  379.     if (strcmp(name, "./")==0 || strcmp(name, "../")==0)
  380.         return YES;
  381. #  else
  382.     if (strcmp(name, ".")==0 || strcmp(name, "..")==0)
  383.         return YES;
  384. #  endif /* DIRECTORY_ADD_SLASH */
  385. # endif /* UNIX || MSFILESYSTEM */
  386.     for (ip=bads=BadExtensions; *ip!='\0'; bads = ip+1) {
  387.         if ((ip = strchr(bads, ' ')) == NULL)
  388.             ip = bads + strlen(bads);
  389.         ext_len = ip - bads;
  390.         if (ext_len != 0 && ext_len < namelen
  391. # ifdef FILENAME_CASEINSENSITIVE
  392.         && caseeqn(&name[namelen - ext_len], bads, ext_len))
  393. # else
  394.         && strncmp(&name[namelen - ext_len], bads, ext_len) == 0)
  395. # endif
  396.             return YES;
  397.     }
  398.     return NO;
  399. }
  400.  
  401. private bool f_match proto((char* file));    /* needed to comfort dumb MS Visual C */
  402.  
  403. private bool
  404. f_match(file)
  405. char    *file;
  406. {
  407.     size_t    len = strlen(fc_filebase);
  408.  
  409.     if (!DispBadFs && bad_extension(file))
  410.         return NO;
  411.  
  412.     return len == 0
  413. # ifdef FILENAME_CASEINSENSITIVE
  414.         || caseeqn(file, fc_filebase, len);
  415. # else
  416.         || strncmp(file, fc_filebase, len) == 0;
  417. # endif
  418. }
  419.  
  420. # ifndef DIRECTORY_ADD_SLASH
  421. private bool
  422. isdir(name)
  423. char    *name;
  424. {
  425.     (void) do_stat(name, (Buffer *) NULL, DS_DIR);
  426.     return was_dir;
  427. }
  428. # endif /* !DIRECTORY_ADD_SLASH */
  429.  
  430. private void
  431. fill_in(dir_vec, n)
  432. register char    **dir_vec;
  433. int    n;
  434. {
  435.     bool    filter;
  436.  
  437.     for (filter = DispBadFs; ; filter = NO) {
  438.         int
  439.             minmatch = 0,
  440.             numfound = 0,
  441.             lastmatch = -1,
  442.             i;
  443.  
  444.         for (i = 0; i < n; i++) {
  445.             /* If filter is NO, we accept all entries
  446.                (either DispBadFs is NO, in which case filtering
  447.                was done when dir_vec was built, or DispBadFs
  448.                is YES, but we are trying again, after finding
  449.                no files passed the filter. */
  450.             if (!filter || !bad_extension(dir_vec[i])) {
  451.                 minmatch = numfound == 0
  452.                     ? (int)strlen(dir_vec[i])
  453. #ifdef FILENAME_CASEINSENSITIVE
  454.                     : min(minmatch, numcompcase(dir_vec[lastmatch], dir_vec[i]));
  455. #else
  456.                     : min(minmatch, numcomp(dir_vec[lastmatch], dir_vec[i]));
  457. #endif
  458.                 lastmatch = i;
  459.                 numfound += 1;
  460.             }
  461.         }
  462.         if (numfound == 0) {
  463.             if (!filter) {
  464.                 /* hopeless: complain and give up */
  465.                 rbell();
  466.                 break;
  467.             }
  468.         } else {
  469.             bool
  470.                 the_same = YES; /* After filling in, are we the same
  471.                             as when we were called? */
  472.  
  473.             if (minmatch > (int)strlen(fc_filebase)) {
  474.                 if (minmatch >= LBSIZE - (fc_filebase - linebuf))
  475.                     len_error(JMP_COMPLAIN);
  476.                 the_same = NO;
  477.                 null_ncpy(fc_filebase, dir_vec[lastmatch], (size_t) minmatch);
  478.                 makedirty(curline);
  479.             }
  480.             Eol();
  481. # ifndef DIRECTORY_ADD_SLASH
  482.             if (numfound == 1 && fc_filebase[0] != '\0' && isdir(linebuf)) {
  483.                 the_same = NO;
  484.                 insert_c('/', 1);
  485.             }
  486. # endif
  487.             if (the_same) {
  488.                 add_mess(" [%s%s]",
  489.                     !filter && DispBadFs? "!" : NullStr,
  490.                     numfound == 1? "Unique" : "Ambiguous");
  491.                 SitFor(7);
  492.             }
  493.             break;    /* we're done */
  494.         }
  495.     }
  496. }
  497.  
  498. /* called from do_ask() when one of "\r\n ?" is typed.  Does the right
  499.    thing, depending on which. */
  500.  
  501. private bool
  502. f_complete(c)
  503. ZXchar    c;
  504. {
  505.     char
  506.         dir[FILESIZE],
  507.         **dir_vec;
  508.     int
  509.         nentries;
  510.  
  511. # ifndef MAC    /* no environment in MacOS */
  512.     if (DoEVexpand)
  513.         EVexpand();
  514. # endif
  515.     if (c == CR || c == LF)
  516.         return NO;    /* tells ask to return now */
  517.     fc_filebase = strrchr(linebuf, '/');
  518. # ifdef MSFILESYSTEM
  519.     {
  520.         char    *p = strrchr(fc_filebase == NULL? linebuf : fc_filebase, '\\');
  521.  
  522.         if (p != NULL)
  523.             fc_filebase = p;
  524.         if (fc_filebase == NULL)
  525.             fc_filebase = strrchr(linebuf, ':');
  526.     }
  527. # endif /* MSFILESYSTEM */
  528.     if (fc_filebase != NULL) {
  529.         char    tmp[FILESIZE];
  530.  
  531.         fc_filebase += 1;
  532.         null_ncpy(tmp, linebuf, (size_t) (fc_filebase - linebuf));
  533.         if (tmp[0] == '\0')
  534.             strcpy(tmp, "/");
  535.         PathParse(tmp, dir);
  536.     } else {
  537.         fc_filebase = linebuf;
  538.         strcpy(dir, ".");
  539.     }
  540.     if ((nentries = jscandir(dir, &dir_vec, f_match, fnamecomp)) == -1) {
  541.         add_mess(" [Unknown directory: %s]", dir);
  542.         SitFor(7);
  543.         return YES;
  544.     }
  545.     if (nentries == 0) {
  546.         add_mess(" [No match]");
  547.         SitFor(7);
  548.     } else if (jiswhite(c)) {
  549.         fill_in(dir_vec, nentries);
  550.     } else {
  551.         /* we're a '?' */
  552.         int
  553.             i,
  554.             maxlen = 0,
  555.             ncols,
  556.             line,
  557.             entriespercol;
  558.  
  559.         TOstart("Completion");
  560.         Typeout("(! means file will not be chosen unless typed explicitly)");
  561.         Typeout("Possible completions (in %s):", dir);
  562.  
  563.         for (i = 0; i < nentries; i++)
  564.             maxlen = max((int)strlen(dir_vec[i]), maxlen);
  565.         maxlen += 4;    /* pad each column with at least 4 spaces */
  566.         ncols = min((CO - 2), MAX_TYPEOUT) / maxlen;
  567.         entriespercol = (nentries + ncols - 1) / ncols;
  568.  
  569.         for (line = 0; line < entriespercol; line++) {
  570.             int
  571.                 col,
  572.                 which = line,
  573.                 bufcol = 0;
  574.             char    buf[MAX_TYPEOUT + 1];
  575.  
  576.             for (col = 0; col < ncols; col++) {
  577.                 if (which < nentries) {
  578.                     bool    isbad = DispBadFs && bad_extension(dir_vec[which]);
  579.  
  580.                     swritef(buf+bufcol, sizeof(buf) - bufcol, "%s%-*s",
  581.                         isbad ? "!" : NullStr,
  582.                         maxlen - isbad, dir_vec[which]);
  583.                 }
  584.                 which += entriespercol;
  585.                 bufcol += maxlen;
  586.             }
  587.             Typeout("%s", buf);
  588.         }
  589.         TOstop();
  590.     }
  591.     freedir(&dir_vec, nentries);
  592.     return YES;
  593. }
  594.  
  595. #endif /* F_COMPLETION */
  596.  
  597. char *
  598. ask_file(prmt, def, buf)
  599. const char    *prmt;
  600. char
  601.     *def,
  602.     *buf;
  603. {
  604.     char    *ans,
  605.         prompt[128],
  606.         *pretty_name = pr_name(def, YES);
  607.  
  608.     if (prmt == NULL)
  609.         swritef(prompt, sizeof(prompt), ProcFmt);
  610.     else
  611.         strcpy(prompt, prmt);
  612.     if (def != NULL && *def != '\0') {
  613.         size_t    pl = strlen(prompt);
  614.  
  615.         swritef(prompt + pl, sizeof(prompt)-pl, "(default %s) ", pretty_name);
  616.         if ((int)strlen(prompt) * 2 >= CO)
  617.             prompt[pl] = '\0';
  618.     }
  619. #ifdef F_COMPLETION
  620.     ans = real_ask("\r\n \t?", f_complete, pretty_name, prompt);
  621.     if (ans == NULL && (ans = pretty_name) == NULL)
  622.         complain("[No default file name]");
  623. #else
  624.     ans = ask(pretty_name, prompt);
  625. #endif
  626.     PathParse(ans, buf);
  627.  
  628.     return buf;
  629. }
  630.