home *** CD-ROM | disk | FTP | other *** search
/ ftp.freefriends.org / ftp.freefriends.org.tar / ftp.freefriends.org / arnold / Source / mush.rstevens.tar.gz / mush.tar / curs_io.c < prev    next >
C/C++ Source or Header  |  1994-07-11  |  18KB  |  693 lines

  1. /* @(#)curs_io.c    (c) copyright 3/18/87 (Dan Heller) */
  2.  
  3. /* curs_io.c -- curses based I/O */
  4. #include "mush.h"
  5. #include "bindings.h"
  6. #include "glob.h"
  7.  
  8. static backspace();
  9.  
  10. #if !defined(M_XENIX) || (defined(M_XENIX) && !defined(CURSES))
  11. char *_unctrl[] = {
  12.     "^@", "^A", "^B", "^C", "^D", "^E", "^F", "^G", "^H", "^I", "^J", "^K",
  13.     "^L", "^M", "^N", "^O", "^P", "^Q", "^R", "^S", "^T", "^U", "^V", "^W",
  14.     "^X", "^Y", "^Z", "^[", "^\\", "^]", "^~", "^_",
  15.     " ", "!", "\"", "#", "$",  "%", "&", "'", "(", ")", "*", "+", ",", "-",
  16.     ".", "/", "0",  "1", "2",  "3", "4", "5", "6", "7", "8", "9", ":", ";",
  17.     "<", "=", ">",  "?", "@",  "A", "B", "C", "D", "E", "F", "G", "H", "I",
  18.     "J", "K", "L",  "M", "N",  "O", "P", "Q", "R", "S", "T", "U", "V", "W",
  19.     "X", "Y", "Z",  "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e",
  20.     "f", "g", "h",  "i", "j",  "k", "l", "m", "n", "o", "p", "q", "r", "s",
  21.     "t", "u", "v",  "w", "x",  "y", "z", "{", "|", "}", "~", "^?"
  22. };
  23. #endif /* !M_XENIX || (M_XENIX && !CURSES) */
  24.  
  25. char    del_line;    /* tty delete line character */
  26. char    del_word;    /* tty delete word character */
  27. char    del_char;    /* backspace */
  28. char    reprint_line;    /* usually ^R */
  29. char    eofc;        /* usually ^D */
  30. char    lit_next;    /* usually ^V */
  31. char    complete;    /* word completion, usually ESC */
  32. char    complist;    /* completion listing, usually ^D */
  33.  
  34. tty_settings()
  35. {
  36.     int is_tty = isatty(0);
  37.  
  38.     if (is_tty)
  39.     savetty();
  40.  
  41. #if defined(SYSV) || defined(AIX)
  42.     eofc = _tty.c_cc[VEOF];
  43. #else
  44. #ifdef BSD
  45.     if (ioctl(0, TIOCGETC, &tchars) != -1)
  46.     eofc = tchars.t_eofc;
  47.     else
  48. #endif /* BSD */
  49.     eofc = CTRL('D');
  50. #endif /* SYSV */
  51.  
  52.     if (!is_tty) {
  53.     del_line = CTRL('U');
  54.     del_char = CTRL('H');
  55.     } else {
  56.     del_line = _tty.sg_kill;
  57.     del_char = _tty.sg_erase;
  58.     }
  59.  
  60. #ifdef TIOCGLTC
  61. #ifndef AIX    /* Just in case */
  62. #ifndef AUX    /* AUX defines TIOCGLTC but doesn't use it */
  63.     if (ioctl(0, TIOCGLTC, <chars) != -1) {
  64.     del_word = ltchars.t_werasc;
  65.     reprint_line = ltchars.t_rprntc;
  66.     lit_next = ltchars.t_lnextc;
  67.     } else
  68. #endif /* AUX */
  69. #endif /* AIX */
  70. #endif /* TIOCGLTC */
  71.     {
  72.     del_word = CTRL('W');
  73.     reprint_line = CTRL('R');
  74.     lit_next = CTRL('V');
  75.     }
  76. }
  77.  
  78. #ifdef Addch
  79. #undef Addch
  80. #endif /* Addch */
  81.  
  82. #ifndef CURSES
  83.  
  84. /* Make sure all ifs have matching elses! */
  85.  
  86. #define Addch(c) \
  87.     if (ison(glob_flags, ECHO_FLAG)) \
  88.     {;} \
  89.     else \
  90.     (void) fputc(c, stdout), (void) fflush(stdout)
  91.  
  92. #else
  93.  
  94. /* see end of Getstr */
  95. #define Addch(c)  \
  96.     if (iscurses) \
  97.     addch(c), refresh(); \
  98.     else if (ison(glob_flags, ECHO_FLAG)) \
  99.     {;} \
  100.     else \
  101.     (void) fputc(c, stdout), (void) fflush(stdout)
  102. #endif /* CURSES */
  103.  
  104. /*
  105.  * get a string of at most 'length' chars.
  106.  * allow backspace-space-backspace, kill word and kill line
  107.  * (options set by user in stty).
  108.  * length is the max length this string can get. offset is from beginning
  109.  * of string.
  110.  * input of ^D returns -1; otherwise, return the number of chars in string.
  111.  */
  112. Getstr(String, length, offset)
  113. char String[];
  114. register int length;
  115. {
  116.     register int c, literal_next = FALSE, lit_bs = FALSE;
  117.     struct cmd_map *curr_map;
  118.     int count = offset, save_wc = wrapcolumn;
  119.  
  120.     (void) fflush(stdout); /* make sure everything is flushed before getting input */
  121.  
  122.     if (mac_hide) {
  123.     curr_map = NULL_MAP;
  124.     wrapcolumn = 0;
  125.     } else if (ison(glob_flags, IS_GETTING))
  126.     curr_map = bang_map;
  127.     else if (iscurses)
  128.     curr_map = NULL_MAP;
  129.     else
  130.     curr_map = line_map;
  131.  
  132.     while ((c = m_getchar()) != '\n' && c != '\r' && c != EOF &&
  133.         isoff(glob_flags, WAS_INTR)) {
  134.     /* echo isn't set, so whatever the character, enter it */
  135.     if (ison(glob_flags, QUOTE_MACRO) || ison(glob_flags, ECHO_FLAG)) {
  136.         if (count < length) {
  137.         String[count++] = c;
  138.         /* Note: Addch includes ECHO_FLAG test */
  139.         if (iscntrl(c)) {
  140.             Addch('^');
  141.             Addch(_unctrl[c][1]);
  142.         } else
  143.             Addch(c);
  144.         } else {
  145.         print("\nWarning: string too long. Truncated at %d chars.",
  146.             length);
  147.         if (ison(glob_flags, QUOTE_MACRO)) {
  148.             mac_flush();
  149.             m_ungetc(reprint_line);
  150.             continue;
  151.         } else
  152.             break;
  153.         }
  154.     }
  155.     /* ^D as the first char on a line or two ^D's in a row is EOF */
  156.     else if (c == eofc && !count)
  157.         break;
  158.     else if (c == '\\' && count < length) {
  159.         literal_next = TRUE, lit_bs = FALSE;
  160.         Addch(String[count++] = '\\');
  161.         } else if (c == lit_next && count < length) {
  162.         literal_next = lit_bs = TRUE;
  163.         String[count++] = '\\';
  164.         if (!in_macro()) {
  165.         /* if (iscntrl(c)) */
  166.             Addch('^');
  167.         /* Addch(_unctrl[c][1]); */
  168.         }
  169.     } else if (literal_next) {
  170.         struct cmd_map *list;
  171.  
  172.         literal_next = FALSE;
  173.         if (iscntrl(c) || c == del_line || c == del_char || c == del_word
  174.             || c == lit_next || lit_bs)
  175.         if (!in_macro() || !lit_bs)
  176.             backspace(String, &count);
  177.         else
  178.             --count;
  179.         else if (in_macro() && c == MAC_LONG_CMD)
  180.         --count;
  181.         /* check to see if user is escaping a map or map! */
  182.         else
  183.         for (list = curr_map; list; list = list->m_next)
  184.             if (list->m_str[0] == c) {
  185.             if (!in_macro())
  186.                 backspace(String, &count);
  187.             else
  188.                 --count;
  189.             break;
  190.             }
  191.         /* A literal-next advances the macro offset */
  192.         String[count++] = c;
  193.         if (iscntrl(c) || c == del_char) {
  194.         if (iscntrl(c)) {
  195.             /*
  196.              * Decrement wrapcolumn because two chars added.
  197.              * It will be restored from save_wc before return.
  198.              */
  199.             if (wrapcolumn > 1)
  200.             wrapcolumn--;
  201.             Addch('^');
  202.         }
  203.         Addch(_unctrl[c][1]);
  204.         } else
  205.         Addch(c);
  206.     } else if (complete && (c == complete || c == complist)) {
  207.         (void) completion(String, &count, (c == complist), (c == complete));
  208.     } else if (c == del_line) {
  209.         if (count) {
  210.         do
  211.             backspace(String, &count);
  212.         while (count);
  213.         }
  214.     } else if (c == reprint_line)
  215.         String[count] = 0, wprint("\n%s", String);
  216.     else if (c == del_word) /* word erase */
  217.         while (count) {
  218.         backspace(String, &count);
  219.         if (!count ||
  220.             isspace(String[count-1]) && !isspace(String[count]) ||
  221.             !isalnum(String[count-1]) && isalnum(String[count]))
  222.             break;
  223.         }
  224.     else if (c == del_char || c == CTRL('H') || c == 127 /* CTRL('?') */) {
  225.         if (count)
  226.         backspace(String, &count);
  227.         /* if iscurses, then backspacing too far cancels a function */
  228.         else if (!count && iscurses && isoff(glob_flags, LINE_MACRO)) {
  229.         mac_flush();
  230.         String[0] = '\0';
  231.         wrapcolumn = save_wc;
  232.         return -1;
  233.         }
  234.     } else if (count == length)
  235.         bell();
  236.     else if (c == '\t')
  237.         do  {
  238.         /* Yuck -- tabs break map! */
  239.         Addch(' ');
  240.         String[count] = ' ';
  241.         } while (++count % 8 && count < length);
  242.     else if (in_macro() && c == MAC_LONG_CMD) {
  243.         char cbuf[MAX_LONG_CMD + 1];
  244.  
  245.         if ((c = read_long_cmd(cbuf)) == 0) {
  246.         c = MAC_LONG_CMD;
  247.         goto check_expand;    /* How could I avoid this? */
  248.         } else if (c > 0) {
  249.         int ok;
  250.  
  251.         String[count] = '\0';
  252.         if ((ok = reserved_cmd(cbuf, TRUE)) > 0) {
  253.             /* Reprint the line */
  254.             if (iscurses)
  255.             print(":%s", String);
  256.             else
  257.             wprint("\r%s", String);
  258.             continue;    /* Get next char without changing count */
  259.         } else if (ok < 0) {
  260.             String[offset] = '\0';
  261.             wrapcolumn = save_wc;
  262.             return ok;
  263.         } else
  264.             goto push_back;
  265.         } else {
  266.         /*
  267.          * Ooops.  We read a bunch of stuff we should not
  268.          * have read, because this isn't really a long command.
  269.          * Use a trick to push the whole thing back, ala ungetc.
  270.          * Wouldn't it be nifty if stdio worked this way? :-)
  271.          */
  272. push_back:
  273.         if (c > 0) {
  274.             cbuf[c++] = MAC_LONG_END;
  275.             cbuf[c] = '\0';
  276.         }
  277.         c = MAC_LONG_CMD;
  278.         Ungetstr(cbuf);
  279.         goto check_expand;    /* How could I avoid this goto? */
  280.         }
  281.     } else {
  282. check_expand:
  283.         if (!curr_map || !check_map(c, curr_map)) {
  284.         /* else if (match != MATCH) */
  285.         if (c != '\t' && iscntrl(c)) {
  286.             Addch('^');
  287.             Addch(_unctrl[c][1]);
  288.             /* Decrement wrapcolumn as above */
  289.             if (wrapcolumn > 1)
  290.             wrapcolumn--;
  291.         } else
  292.             Addch(c);
  293.         String[count++] = c;
  294.         }
  295.     }
  296.     /* Null-terminate for macro lookup purposes.
  297.      * This will be overwritten by the next character.
  298.      */
  299.     String[count] = '\0';
  300.     if (line_wrap(String, &count))
  301.         break;
  302.     }
  303.     (void) fflush(stdout); /* for sys-v folks */
  304.  
  305.     if (c == eofc || c == EOF || ison(glob_flags, WAS_INTR)) {
  306.     if (feof(stdin))
  307.         clearerr(stdin);
  308.     wrapcolumn = save_wc;
  309.     return -1;
  310.     }
  311.     if (count && String[count-1] == '\\') {
  312.     int count2;
  313.     if (isoff(glob_flags, ECHO_FLAG))
  314.         putchar('\n');
  315.     wrapcolumn = save_wc;
  316.     /*
  317.      * NOTE: If the offset passed here is ever made greater than 0,
  318.      * the value of wrapcolumn must again be changed/restored ...
  319.      */
  320.     if ((count2 = Getstr(&String[count-1], length - count + 1, 0)) == -1)
  321.         return -1;
  322.     return count + count2;
  323.     }
  324.     if (!iscurses && isoff(glob_flags, ECHO_FLAG))
  325.     putchar('\n');
  326.     /* Should be null-terminated already, but just in case */
  327.     String[count] = '\0';
  328.     wrapcolumn = save_wc;
  329.     return count;
  330. }
  331.  
  332. static
  333. backspace(str, n)
  334. register char *str;
  335. int *n;
  336. {
  337.     (*n)--;
  338.     Addch('\b'); Addch(' '); Addch('\b');
  339.     if (iscntrl(str[*n])) {
  340.     Addch('\b'); Addch(' '); Addch('\b');
  341.     /* Re-increment wrapcolumn -- see Getstr */
  342.     if (wrapcolumn)
  343.         wrapcolumn++;
  344.     }
  345. }
  346.  
  347. #undef Addch
  348.  
  349. /*
  350.  * Check to see if what the user is typing is supposed to be expanded
  351.  * into a longer string.  The first char is 'c' and the map list to use
  352.  * is in map_list.  Continue looping (reading chars from stdin or a
  353.  * currently active mapping) until a match happens or we've determined
  354.  * that there is no match.
  355.  */
  356. check_map(c, map_list)
  357. char c;
  358. struct cmd_map *map_list;
  359. {
  360.     char mbuf[MAX_MACRO_LEN], *p = mbuf;
  361.     struct cmd_map *list;
  362.     int m, n, match;
  363.  
  364.     *p++ = c;
  365.  
  366.     while (isoff(glob_flags, WAS_INTR)) {
  367.     m = 0;
  368.     *p = 0; /* make sure it's null terminated */
  369.     /*
  370.      * loop thru the list of maps and check to see if the typed
  371.      * char matches the mapping.  If it matches completely, substitute
  372.      * the stuff in x_str and return.  If a partial match occurs, then
  373.      * read the next char until a timeout or no match.
  374.      */
  375.     for (list = map_list; list; list = list->m_next) {
  376.         if ((match = prefix(mbuf, list->m_str)) == MATCH) {
  377.         /* Must turn on flags BEFORE pushing */
  378.         line_macro(list->x_str);
  379.         return 1;
  380.         } else if (match != NO_MATCH)
  381.         m++; /* something matched partially */
  382.     }
  383.     if (!m)
  384.         break;
  385.     /* see if there's anything on the queue to read... */
  386.     if (mac_pending()
  387. #if !defined(SELECT) && !defined(M_UNIX)
  388. #ifdef FIONREAD
  389.         || !ioctl(0, FIONREAD, &n) && n > 0
  390. #else
  391. #ifdef M_XENIX
  392.         || rdchk(0) > 0
  393. #endif /* M_XENIX */
  394. #endif /* FIONREAD */
  395. #endif /* SELECT */
  396.                            )
  397.         *p++ = m_getchar();
  398.     else {
  399.     /* The user has typed the first part of a map or macro.  Give him
  400.      * a chance to finish it.
  401.      */
  402. #if defined(BSD) || defined(M_UNIX) || defined(SELECT)
  403.         /* If the system has select(), use it.  It's much faster and
  404.          * more aesthetic since there is no mandatory timeout.
  405.          */
  406.         struct timeval timer;
  407. #ifdef FD_SET
  408.         fd_set rmask, wmask, xmask;
  409.         FD_SET(0, &rmask);    /* Test stdin for read */
  410.         FD_ZERO(&wmask);    /* Don't care about write */
  411.         FD_ZERO(&xmask);    /* Don't care about exception */
  412. #else
  413.         int rmask = 1, wmask = 0, xmask = 0;
  414. #endif /* FD_SET */
  415.         timer.tv_sec = 1;
  416.         timer.tv_usec = 0;
  417.         n = select(1, &rmask, &wmask, &xmask, &timer);
  418. #else /* !SELECT */
  419. #ifdef FIONREAD
  420.         /* system doesn't have select(), so use FIONREAD to see if
  421.          * there are any chars on the queue to read.
  422.          */
  423.         (void) sleep(1);
  424.         (void) ioctl(0, FIONREAD, &n);
  425. #else
  426. #ifdef M_XENIX
  427.         (void) sleep(1);
  428.         n = rdchk(0);
  429. #else
  430.  
  431.         /* system has neither select() nor FIONREAD, so just set n
  432.          * and force the user to either complete the map or fail it
  433.          * without a timeout.  Chars won't echo till he does one or
  434.          * the other.
  435.          */
  436.         n = 1;
  437. #endif /* M_XENIX  */
  438. #endif /* FIONREAD */
  439. #endif /* SELECT */
  440.         if (n > 0)
  441.         /* don't read all 'n' chars -- there may be a match early */
  442.         *p++ = m_getchar();    /* To flush macros and reset flags */
  443.         else /* still nothing to read? User doesn't want to use map */
  444.         break;
  445.     }
  446.     }
  447.     /* no match or a timeout.  This isn't a map, just return. */
  448.     *p = 0;
  449.     if (mbuf[1])
  450.     (void) mac_push(mbuf + 1);
  451.     return 0;
  452. }
  453.  
  454. /*
  455.  * Check for line wrap.  This should happen only in composition mode and
  456.  * only when the variable wrapcolumn has a value greater than zero.  Line
  457.  * wrap is implemented using Ungetstr [that is, mac_push()].
  458.  *
  459.  * Returns 1 if the line was wrapped, 0 if not.
  460.  */
  461. line_wrap(string, count)
  462. char *string;    /* The string to be wrapped */
  463. int *count;    /* Offset of string terminator */
  464. {
  465.     char *tail = NULL;
  466.     int n = *count;
  467.  
  468.     if (wrapcolumn < 1 || *count <= wrapcolumn
  469.         || isoff(glob_flags, IS_GETTING)    /* Wrap only in msg body */
  470.         || ison(glob_flags, QUOTE_MACRO)    /* Don't wrap quoted macros */
  471.         || ison(glob_flags, ECHO_FLAG))    /* Can't wrap in echo mode */
  472.     return 0;
  473.  
  474.     /* Back up past the wrapcolumn point */
  475.     for (; n > wrapcolumn; --n)
  476.     ;
  477.     /* Look for a space */
  478.     while (n && !isspace(string[n]))
  479.     --n;
  480.     /* If no break found, return no wrap */
  481.     if (!n)
  482.     return 0;
  483.     tail = &string[n+1];
  484.     /* Skip the break char and any whitespace */
  485.     while (n && isspace(string[n]))
  486.     --n;
  487.     ++n; /* move back into the whitespace */
  488.     /* Erase the stuff that will wrap */
  489.     while (*count > n)
  490.     backspace(string,count);
  491.     string[*count] = '\0';
  492.     /* Push the tail, if any */
  493.     if (*tail)
  494.     Ungetstr(tail);
  495.     return 1;
  496. }
  497.  
  498. /*
  499.  * Error bell used by completion()
  500.  */
  501. errbell(ret)
  502. int ret;
  503. {
  504.     if (ret < 0 || !chk_option("quiet", "complete,completion"))
  505.     bell();
  506.     return ret;
  507. }
  508.  
  509. /*
  510.  * Perform word completion on the input string
  511.  */
  512. completion(string, count, showlist, ignore)
  513. char *string;    /* The string to be completed */
  514. int *count;    /* Offset of string terminator */
  515. int showlist;    /* Display list, complete if also ignore */
  516. int ignore;    /* Ignore the fignore matches, do complete */
  517. {
  518.     char buf[MAXPATHLEN], *b = buf, **exp;
  519.     int n = *count, f, len, prefix, trim, overstrike, expandall;
  520.  
  521.     if (!*string || !*count)
  522.     return errbell(-1);
  523.  
  524.     /* Look for a delimiter */
  525.     while (n > 0 && !index(DELIM, string[--n]))
  526.     ;
  527.     if (n > 0 || index(DELIM, string[n]))
  528.     n++;
  529.     b = buf + (len = Strcpy(buf, &string[n]));
  530.     Debug("\nexpanding (%s) ... ", buf);
  531.     if (!any(buf, FMETA)) {
  532.     expandall = 0;
  533.     overstrike = (*buf == '+' || *buf == '~' || *buf == '%');
  534.     trim = (overstrike && len > 1);
  535.     if (!overstrike || len > 1 || (*buf == '+' && showlist))
  536.         *b++ = '*', *b = 0;
  537.     /* Previous behavior for '+' completions (trailing '/'):
  538.     if (len > 1 || *buf != '~' || *buf != '%')
  539.         *b++ = '*', *b = 0;
  540.     */
  541.     f = filexp(buf, &exp);
  542.     if (*--b == '*')
  543.         *b = 0; /* We need the original buf below */
  544.     } else {
  545.     overstrike = 1;
  546.     trim = (*buf == '+' || *buf == '~');
  547.     /*
  548.      * Check first to see if the base pattern matches.
  549.      * If not, append a '*' and try again.
  550.      * Don't expand all matches in the latter case.
  551.      */
  552.     if ((f = filexp(buf, &exp)) < 1) {
  553.         *b++ = '*', *b = 0;
  554.         f = filexp(buf, &exp);
  555.         *--b = 0; /* We need the original buf below */
  556.         expandall = 0;
  557.     } else
  558.         expandall = !showlist;
  559.     }
  560.     if (ignore)
  561.     f = fignore(f, &exp);
  562.     if (f < 0) {
  563.     Debug("globbing error!\n%s", string);
  564.     free_vec(exp);
  565.     return errbell(-1);
  566.     } else if (f > 0) {
  567.     Debug("result is: "), print_argv(exp);
  568.     if (!expandall && f > 1)
  569.         prefix = lcprefix(exp, overstrike ? 0 : len);
  570.     else
  571.         prefix = 0;
  572.     if (showlist && (f > 1 || !ignore)) {
  573.         int pfx = prefix;
  574.         if (!expandall)
  575.         while (pfx && exp[0][pfx - 1] != '/')
  576.             --pfx;
  577.         putchar('\n');
  578.         if (columnate(f, exp, pfx) < 0)
  579.         (void) errbell(-1);
  580.         /* Reprint the line */
  581.         if (iscurses) {
  582.         wprint(":%s", string);
  583.         turnon(glob_flags, CNTD_CMD);
  584.         } else {
  585.         if (isoff(glob_flags, IS_GETTING))
  586.             mail_status(1);
  587.         wprint("%s", string);
  588.         }
  589.         if (!ignore)
  590.         overstrike = 0;
  591.     } 
  592.     if (ignore || !showlist) {
  593.         if (expandall || strlen(exp[0]) > len) {
  594.         if (!showlist)
  595.             Debug("%s", string);
  596.         if (overstrike && (prefix || expandall || f == 1)) {
  597.             char *tmpv[3];
  598.             tmpv[0] = buf;
  599.             if (trim)
  600.             tmpv[1] = trim_filename(exp[0]);
  601.             else
  602.             tmpv[1] = exp[0];
  603.             tmpv[2] = NULL;
  604.             /* Back up as far as is necessary */
  605.             len = lcprefix(tmpv, 0);
  606.             /* If nothing will be erased, we may need to beep */
  607.             if (n + len == *count) {
  608.             if (!expandall && !tmpv[1][len])
  609.                 (void) errbell(0);
  610.             }
  611.             /* Erase the stuff that will complete */
  612.             while (*count > n + len)
  613.             backspace(string,count);
  614.             string[*count] = '\0';
  615.         }
  616.         if (expandall || f == 1) {
  617.             /* Unget the names IN REVERSE ORDER! */
  618.             while (f--) {
  619.             if (trim)
  620.                 b = trim_filename(exp[f]);
  621.             else
  622.                 b = exp[f];
  623.             if (f) {
  624.                 Ungetstr(b);
  625.                 Ungetstr(" ");
  626.             } else
  627.                 Ungetstr(b + len);
  628.             }
  629.         } else {
  630.             if (prefix > len) {
  631.             exp[0][prefix] = 0;
  632.             if (!showlist)
  633.                 Debug("\ncompletion is (%s)\n%s", exp[0], string);
  634.             if (trim)
  635.                 Ungetstr(trim_filename(exp[0]) + len);
  636.             else
  637.                 Ungetstr(&exp[0][len]);
  638.             } else if (!showlist)
  639.             Debug("\nno longer prefix\n%s", string);
  640.             /* Special case because "+" always tries to expand "+*"
  641.              * to get listings and avoid getpath()'s trailing '/'.
  642.              * No error bell is needed in those cases.
  643.              */
  644.             if (strcmp(buf, "+") != 0)
  645.             (void) errbell(0);
  646.         }
  647.         } else {
  648.         Debug("no longer prefix\n%s", string);
  649.         (void) errbell(0);
  650.         }
  651.     }
  652.     } else {
  653.     Debug("no match\n%s", string);
  654.     (void) errbell(0);
  655.     }
  656.     free_vec(exp);
  657.     return 1;
  658. }
  659.  
  660. fignore(argc, argvp)
  661. int argc;
  662. char ***argvp;
  663. {
  664.     char *fign = do_set(set_options, "fignore");
  665.     char **flist, buf[MAXPATHLEN], *b = buf;
  666.     int fcnt, i;
  667.  
  668.     if (argc < 2 || !fign || !*fign)
  669.     return argc;
  670.     if (!argvp || !*argvp && !**argvp)
  671.     return -1;
  672.     
  673.     if ((flist = mk_argv(fign, &fcnt, FALSE)) && fcnt > 0) {
  674.     *b++ = '*';
  675.     for (i = 0; i < fcnt; i++) {
  676.         if (flist[i][0] == '.' && !any(flist[i], FMETA)) {
  677.         (void) strcpy(b, flist[i]);
  678.         (void) strdup(flist[i], buf);
  679.         }
  680.     }
  681.     Debug("ignoring "), print_argv(flist);
  682.     fcnt = gdiffv(argc, argvp, fcnt, flist);
  683.     free_vec(flist);
  684.     if (fcnt == 0)
  685.         fcnt = argc;
  686.     else {
  687.         free_elems(&((*argvp)[fcnt]));
  688.         (*argvp)[fcnt] = NULL;
  689.     }
  690.     }
  691.     return fcnt;
  692. }
  693.