/************************************************************************ * This program is Copyright (C) 1986 by Jonathan Payne. JOVE is * * provided to you without charge, and with no warranty. You may give * * away copies of JOVE, including sources, provided that this notice is * * included in all the files. * ************************************************************************/ /* * Modified: Nov-88, Apr-89 by Tom Hageman * - add arrow keys in real_ask * - make real_ask re-entrant. * Jun-90, update to v4.14 */ #define NO_PROCDECL /* kludge for teensy pdp11 compiler */ #include "jove.h" RCS("$Id: ask.c,v 14.32.0.12 1994/06/23 02:46:23 tom Exp tom $") #include "ctype.h" #include "io.h" #include "screen.h" #define PROMPTSIZE 128 /* size of prompt buffer */ int Asking ZERO; char Minibuf[LBSIZE]; private Line *CurAskPtr ZERO; /* points at some line in mini-buffer */ private Buffer *AskBuffer ZERO; /* Askbuffer points to actual structure */ /* The way the mini-buffer works is this: The first line of the mini-buffer is where the user does his stuff. The rest of the buffer contains strings that the user often wants to use, for instance, file names, or common search strings, etc. If he types C-N or C-P while in ask(), we bump the point up or down a line and extract the contents (we make sure is somewhere in the mini-buffer). */ private Buffer *get_minibuf __(( void )); private Buffer * get_minibuf() { if (!bufno(AskBuffer)) /* make sure it still exists */ (AskBuffer = do_select((Window *) 0, "*minibuf*"))->b_type = B_SCRATCH; return AskBuffer; } /* Add a string to the mini-buffer. */ void minib_add(str, movedown) const char *str; { register Buffer *saveb = curbuf; SetBuf(get_minibuf()); ToLast(); LineInsert(1); ins_str(str, NO); if (movedown) CurAskPtr = curline; SetBuf(saveb); } void AskNextLine() { register Line *lp; lp = next_line(CurAskPtr, exp); if (lp->l_prev == NULL && lp->l_next != NULL) lp = lp->l_next; CurAskPtr = lp; ltobuf(lp, linebuf); makedirty(curline); Eol(); } /* Kludge: truncate prompt if it's too long. Also try to do something sensible with defaults... Assumes prompt contains printable characters only. Returns length of (possibly truncated) prompt. */ private int fix_prompt __(( char *_(prompt), int _(limit) )); private int fix_prompt(prompt, limit) register char *prompt; register int limit; { register int i, len; if ((len = strlen(prompt)) <= limit) return len; if ((i = sindex("(default ", prompt)) == 0 || i > limit) { i = limit >> 1; goto dots; } else if (i + 3 >= limit) /* no room for "...", */ limit = --i; /* so truncate at `('. */ else if ((len - 8) <= limit) /* just enough room if */ limit = (len - 8); /* we remove "default ". */ else { /* remove "default ", */ dots: strcpy(&prompt[i], "..."); /* insert "...", and */ i += 3; /* setup to truncate tail */ } prompt += i; strcpy(prompt, &prompt[len - limit]); return limit; } private char *real_ask __(( const char *_(delim), int (*_(d_proc))(int _(c)), const char *_(def), char *_(prompt) )); private char * real_ask(delim, d_proc, def, prompt) const char *delim, *def; char *prompt; int (*d_proc)__(( int _(c) )); { extern int DOLsave; static int InAsk ZERO; Savejmp savejmp; register Buffer *ask_bp; /* Invariant: ask_bp == curbuf */ register int c, abort = NO, prompt_len, start_col, start_off; Buffer *saveb = curbuf, *saveb_next_window = NULL; const data_obj *push_cmd = LastCmd; int o_exp = exp, o_exp_p = exp_p, o_Asking = Asking, o_Interactive = Interactive; char *save_line; const char *insert; if (!one_windp(curwind)) saveb_next_window = curwind->w_next->w_bufp; SetBuf(ask_bp = get_minibuf()); if (!inlist(ask_bp->b_first, CurAskPtr)) CurAskPtr = ask_bp->b_dot; /* i.e. curline */ ToFirst(); /* Beginning of buffer. */ if (InAsk) save_line = copystr(linebuf), /* to make ask re-entrant */ RecDepth++, updmodline(); /* to show recursive ask */ InAsk++; linebuf[0] = '\0'; modify(); makedirty(ask_bp->b_dot); if (!InJoverc) { prompt_len = fix_prompt(prompt, (3 * CO >> 2)); UpdMesg = start_col = start_off = 0; } if (push_env(savejmp)) { if (InJoverc || (in_macro() && !Interactive)) abort++; /* this is a kludge */ } this_cmd = 0; insert = def; while (!abort) { if (!InJoverc) { extern int alarmed; /* show error message, or message from a command, or message modified by d_proc for a short time. In the latter case, keep current cursor position. But don't show alarmed keystrokes! */ if (UpdMesg && mesgbuf[0] && (!in_macro() || Interactive) && (!alarmed || strcmp(mesgbuf, key_strokes()) != 0)) { if (c >= 0) Asking = strlen(mesgbuf); SitFor(3 * PDelay >> 1); } /* calculate start_column of horizontal window. These are PRINT columns, so we have to convert to character offset in linebuf. Fortunately we can get away with re-calculating it whenever start_col changes (which isn't too often). */ c = HorWindow(calc_pos(linebuf, ask_bp->b_char), start_col, prompt_len); if (c != start_col) { start_col = c; start_off = how_far(linebuf, c); } Asking = ask_bp->b_char - start_off + prompt_len; s_mess("%s%s", prompt, &linebuf[start_off]); } if (this_cmd != ARG_CMD) { exp = 1; exp_p = NO; last_cmd = this_cmd; init_strokes(); } if ((c = getch()) < 0 /*EOF*/ || (c && index(delim, c))) { if (!d_proc || !(*d_proc)(c)) break; c = EOF; /* for delayed display */ } else { UpdMesg = NO; /* Kludge: this suppresses delay after Ungetc'd character. */ if (c == AbortChar) { abort++; message("[Aborted]"); continue; } switch (c) { case CTL('S'): /* Spew out current file name */ case CTL('\\'): { register Buffer *b = curwind->w_bufp; if (exp_p) { /* ...of buffer # */ c = exp; b = world; while (--c > 0 && (b = b->b_next)) continue; } insert = (b && b->b_fname) ? filename(b) : NULL; } #if/*ndef TINY*/ 1 goto ins; case CTL('C'): SetBuf(curwind->w_bufp); WordAtDot((char *)(insert = Minibuf), sizeof Minibuf); SetBuf(ask_bp); /* goto ins; */ ins: #endif case CTL('R'): /* Recall default */ if (insert && *insert) ins_str(insert, NO); else rbell(); this_cmd = 0; insert = def; continue; #if NO /* this is now handled in NextLine, so that key rebindings will work as they should. */ case UPARROW: /* move up in minibuf */ case CTL('P'): exp = -exp; /* fall into... */ case DOWNARROW: /* move down in minibuf */ case CTL('N'): AskNextLine(); continue; #endif default: dispatch(c); break; } } if (curbuf != ask_bp) SetBuf(ask_bp = get_minibuf()); if (ask_bp->b_dot != ask_bp->b_first) { CurAskPtr = ask_bp->b_dot; ask_bp->b_dot = ask_bp->b_first; /* with whatever is in linebuf */ } } pop_env(savejmp); DOLsave = 0; /* [TRH] prevent lsave */ LastCmd = push_cmd; exp_p = o_exp_p; exp = o_exp; Asking = o_Asking; Interactive = o_Interactive; strcpy(Minibuf, linebuf); if (--InAsk) { strcpy(linebuf, save_line); free(save_line); Eol(); UpdMesg = NO; RecDepth--, updmodline(); } SetBuf(saveb); if (/*- True(UseBuffers) && -*/ /* remove completion/help windows */ !one_windp(curwind) && curwind->w_next->w_bufp != saveb_next_window) if (saveb_next_window) tiewind(curwind->w_next, saveb_next_window); else del_wind(curwind->w_next); if (abort) complain((char *)0); /* propagate... */ if (!charp() && !in_macro()) { curstoLL(); flusho(); } if (Minibuf[0] == '\0') return NULL; return Minibuf; } /* VARARGS2 */ char * DEFVARG(ask, (const char *def, const char *fmt, ...), (def, fmt, va_alist) const char *def; const char *fmt;) { char promptbuf[PROMPTSIZE]; register char *prompt = promptbuf; register const char *ans; va_register va_list ap; if (!InJoverc) { va_begin(ap, fmt); format(prompt, PROMPTSIZE, fmt, ap); va_end(ap); } if ((ans = real_ask("\r\n", (int (*)()) 0, def, prompt)) == NULL) { /* Typed nothing. */ if ((ans = def) == NULL) complain("[No default]"); } return (char *) ans; } /* VARARGS4 */ char * DEFVARG(do_ask, (const char *delim, int (*d_proc)(int c), const char *def, const char *fmt, ...), (delim, d_proc, def, fmt, va_alist) const char *delim; int (*d_proc)(); const char *def; const char *fmt;) { char promptbuf[PROMPTSIZE]; register char *prompt = promptbuf; va_register va_list ap; if (!InJoverc) { va_begin(ap, fmt); format(prompt, PROMPTSIZE, fmt, ap); va_end(ap); } return real_ask(delim, d_proc, def, prompt); } /* * Ask for confirmation. * def is default answer, which must be 'Y', 'N' or zero (no default). * If a default is present you can respond with to get the default, * or any command key to get the default and execute that command. * If no default is present you must explicitly respond with 'y' or 'n'. */ /* VARARGS2 */ DEFVARG(yes_or_no_p, (int def, const char *fmt, ...), (def, fmt, va_alist) int def; const char *fmt;) { register int c, d; va_register va_list ap; for (;;) { VA_INIT_PROPAGATE va_begin(ap, fmt); s_mess(VA_PROPAGATE(fmt, ap)); va_end(ap); Asking = fix_prompt(mesgbuf, CO - 3); /* so redisplay works */ if (d = def) Asking++, add_mess("[%c]", d); if ((c = Peekc()) < 0) { redisplay(); c = getchar(); add_stroke(c); } if (c == AbortChar) complain("[Aborted]"); switch (toupper(c)) { default: if (!d || (' ' < c && c < '\177')) { add_mess("[Type Y or N]"); SitFor(PDelay<<1); continue; } if (c != CR && c != ' ') Ungetc(c); if (d == 'Y') case 'Y': d = YES; else case 'N': d = NO; } Asking = NO; message(NullStr); return d; } /* NOTREACHED */ } /* * Expand environment variables in line. * this is called in `f_complete' before any file searching is done. * (the original JOVE 4.14 expanded environment variables in real_ask * conditional to a JOVE variable; I [TRH] don't think that that is a * good idea since it will probably hit you when you're least expecting it, * e.g., in a search string. In practice, you need environment variable * expansion only in file names) */ int env_expand(buf) char *buf; { char work[LBSIZE]; /* we build string in here */ register char *s = buf, *d = work, *v; register int nsubst = 0; # define DOLLAR '$' #if vms /* `$' is a valid filename character; use single quote instead. */ # undef DOLLAR # define DOLLAR '\'' #endif while (*d = *s++) { if (*d++ != DOLLAR) continue; if ((*d = *s++) == '\0') break; if (*d == DOLLAR) /* '$$' -> '$' */ continue; v = d; if (*d == '{') { while (*d = *s++) if (*d++ == '}') { --d; ++s; /* skip '}' */ break; } } else { WITH_TABLE(CMODE); do { if (*d == DOLLAR) { #if vms ++s; #endif break; } if (!isword(*d++)) { --d; break; } } while (*d = *s++); END_TABLE(); } *d = '\0'; --s; /* one too far */ if (d == v) /* no word following '$' */ continue; if (d = getenv(v)) d = appcpy(v - 1, d); /* overwrite '$' */ else d = v - 1; nsubst++; } if (nsubst) strcpy(buf, work); return nsubst; } #ifdef CHDIR # ifndef TINY public int ask_dir_only ZERO; # define ASK_DIR # endif #endif #ifdef F_COMPLETION #include "readdir.h" private char *fc_filebase; DEF_STR( "bad-filename-extensions", BadExtensions, 128, V_STRING ) _IF(def F_COMPLETION)_IF(def PRIVATE) = ".o"; DEF_INT( "display-bad-filenames", DispBadFs, V_BOOL ) _IF(def F_COMPLETION)_IF(def PRIVATE) = YES; #ifndef TINY DEF_INT( "display-filetype", DispFileType, V_BOOL ) _IF(def F_COMPLETION)_IF(ndef TINY)_IF(def PRIVATE) = YES; /* filetype(): {{mostly stolen from tcsh}} * Return a character that signifies the filetype of `filename' * symbology from 4.3 ls command. */ private int filetype __(( const char *_(filename) )); private int filetype(filename) const char *filename; { struct stat stbuf; register int mode; if (lstat(rel_name(filename), &stbuf) == 0) { mode = stbuf.st_mode; #ifdef S_ISLNK if (S_ISLNK(mode)) { /* Symbolic link */ # ifdef S_ISVTX /* Some file systems (like autonfsmount) set the `sticky' bit on symbolic links that point to a(n unmounted) directory, for performance reasons. */ if (stbuf.st_mode & S_ISVTX) return ('>'); # endif if (zstat(filename, &stbuf) != 0) return ('&'); if (S_ISDIR(stbuf.st_mode)) return ('>'); return ('@'); } #endif #ifdef S_ISSOCK if (S_ISSOCK(mode)) /* Socket */ return ('='); #endif #ifdef S_ISFIFO if (S_ISFIFO(mode)) /* Named Pipe */ return ('|'); #endif #ifdef S_ISHIDDEN if (S_ISHIDDEN(mode)) /* Hidden Directory [aix] */ return ('+'); #endif #ifdef S_ISCDF if (S_ISCDF(mode)) /* Context Dependent Files [hpux] */ return ('+'); #endif #ifdef S_ISNWK if (S_ISNWK(mode)) /* Network Special [hpux] */ return (':'); #endif if (S_ISCHR(mode)) /* char device */ return ('%'); if (S_ISBLK(mode)) /* block device */ return ('#'); if (S_ISDIR(mode)) /* normal Directory */ return ('/'); if (mode & (S_IXUSR|S_IXGRP|S_IXOTH)) return ('*'); } return ('\0'); } #endif /* TINY */ int isdir(name) const char *name; { struct stat stbuf; char filebuf[FILESIZE]; zstat(PathParse(name, filebuf), &stbuf); return S_ISDIR(stbuf.st_mode); } /* * [TRH] this routine is now non-destructive. */ int bad_extension(name) const char *name; { register const char *ext, *p; register int ext_len, name_len = strlen(p = name); #if vms /* ignore version numbers */ for (ext = p + name_len; ext > p; ) if (*--ext == ';') { name_len = ext - p; break; } #endif /* also ignore "." and ".." */ if (*p++ == '.' && (*p == '\0' || (*p++ == '.' && *p == '\0'))) return YES; p = BadExtensions; do { for (ext = p; *p != ' '; ) { if (*p++ == '\0') { --p; break; } } if ((ext_len = (p - ext)) == 0) continue; if (ext_len >= name_len) continue; if (numcomp(&name[name_len - ext_len], ext) == ext_len) return YES; } while (*p++); return NO; } private int f_match __(( const char *_(file) )); private int f_match(file) const char *file; { register char *base = fc_filebase; register int matching = NO, n; if ((base[n = numcomp(file, base)] == '\0') && (file[n] == '\0' /* exact match */ || True(DispBadFs) || !bad_extension(file))) { ++matching; #ifdef ASK_DIR # ifndef DOS /* We arranged for DOS to select directories in readdir (see readdir.h), so we won't need this. */ # ifndef MAC if (ask_dir_only >= 0) { /* `base' is a prefix of `file'... */ strcpy(base, file); matching = isdir(linebuf); base[n] = '\0'; /* ...so this restores it. */ } # endif # endif #endif } return matching; } private const char NoMatch[] = " [No match]"; /* Complete a single filename. Also, returns whether to auto-list. */ private int fill_in __(( char **_(dir_vec) )); private int fill_in(dir_vec) register char **dir_vec; { register int i, minmatch = strlen(fc_filebase), numfound = 0, fc_filelen = minmatch, exactmatch = NO; const char *lastmatch = *dir_vec; for (; *dir_vec; *dir_vec++) { i = numcomp(lastmatch, *dir_vec); /* * Check for bad filenames only if we didn't already have * them filtered out in f_match(), that is, if it is an * exact match OR if "display-bad-filenames" is ON. * (Exact match is ALWAYS checked.) */ if (((i == fc_filelen && (*dir_vec)[i] == '\0' && (exactmatch++, TRUE)) || True(DispBadFs)) && bad_extension(*dir_vec)) continue; if (!numfound) minmatch = strlen(*dir_vec); else if (i < minmatch) minmatch = i; numfound++; lastmatch = *dir_vec; } if (minmatch > fc_filelen) { null_ncpy(fc_filebase, lastmatch, minmatch); minmatch = -1; /* to indicate that something changed */ makedirty(curline); } Eol(); /* did we find a new directory? */ if ((--numfound == 0) && ((i = curchar - 1) >= 0) && (!ISDIRSEP(linebuf[i])) && (isdir(linebuf))) { #if vms /* Kludge for VMS: remove trailing ".dir" extension. (assumes version number is already removed by readdir) */ if (i - 3 >= 0 && casencmp(&linebuf[i - 3], ".dir", 4) == 0) exp_p = NO, exp = -4, DelNChar(), exp = 1; #endif Insert(SLASH); } else if (minmatch >= 0) { /* nothing changed... (remember we decremented numfound) */ add_mess((numfound == 0) ? " [Unique]" : (exactmatch) ? " [Exact]" : (numfound > 0) ? " [Ambiguous]" : NoMatch); if ((True(ComplAutoHelp) || exp_p) && numfound > 0) { redisplay(); /* kludge...to show the above. */ updmesg(); return YES; } } return NO; } private int max_len __(( char **_(dir_vec) )); private int max_len(dir_vec) register char **dir_vec; { register int len, maxlen = 0; while (*dir_vec) if ((len = strlen(*dir_vec++)) > maxlen) maxlen = len; return maxlen; } /* called from do_ask() when one of "\r\n \t?" is typed. Does the right thing, depending on which. */ private int f_complete __(( int _(c) )); private int f_complete(c) { char dirbuf[FILESIZE], **dir_vec; const char *dir; { register char *lp = linebuf, *bp; if (env_expand(lp)) Eol(); if (c == CR || c == LF) return 0; /* tells ask to return now */ dir = pwd(); FIX_FILENAME(lp); if ((bp = basename(lp)) != lp) { register char savec = *bp; *bp = '\0'; dir = PathParse(lp, dirbuf); *bp = savec; } fc_filebase = bp; } { register int nentries; if ((nentries = scandir(dir, &dir_vec, f_match, alphacomp)) < 0) { add_mess(" [Unknown directory: %s]", dir); return 1; } if (nentries == 0) add_mess(NoMatch); else if (c == '?' || True(fill_in(dir_vec))) { /* we're a '?' */ register int i, which, lines, colwidth; TOstart("*Completion*", FALSE); /* false means newline only on request */ if (True(DispBadFs)) { Typeout("(! means file will not be chosen unless typed explicitly)"); Typeout((char *) 0); } Typeout("Possible completions (in %s):", dir); Typeout((char *) 0); /* each column padded with at least 3 blanks */ colwidth = max_len(dir_vec) + 3; lines = 1 + (nentries / (CO / colwidth)); { i = 0; } do { { which = i; } do { int isbad = NO; const char *f = dir_vec[which]; if (True(DispBadFs)) isbad = bad_extension(f); #ifndef TINY { /* a la "ls -F" */ char tmp[FILESIZE]; int tc; if (True(DispFileType) && (tc = filetype(make_filename(tmp, dir, f)))) f = sprint("%s%c", f, tc); } #endif /* !TINY */ Typeout("%s%-*s", isbad ? "!" : NullStr, colwidth - isbad, f); } while ((which += lines) < nentries); Typeout((char *) 0); #ifndef TINY if (TOabort) break; #endif } while (++i < lines); TOstop(); } freedir(&dir_vec, nentries); } return 1; } #endif /* F_COMPLETION */ #ifdef FILESELECTOR DEF_INT( "file-selector", UseFSel, V_BOOL ) _IF(def FILESELECTOR)_IF(def PRIVATE) ZERO; int FSelMode ZERO; #endif char * ask_file(prompt, def, buf) const char *prompt; const char *def; char *buf; { register const char *_prompt; register char *ans, *pretty = pr_name(def); #ifdef ASK_DIR /* `ask_dir_only' flag is set to +1 by "cd" and "pushd" prior to asking, to request directories only. Decrement it now, so that after an abort in `do_ask' or `FileSelector', the next call to this function will consider all files again. */ ask_dir_only--; #endif #ifdef FILESELECTOR if (True(UseFSel) && !Inputp && (!in_macro() || Interactive)) { if (!(_prompt = prompt)) _prompt = ProcFmt; if (!(ans = FileSelector(sprint(_prompt), pretty, Minibuf)) && !(ans = pretty)) complain("[No default file name]"); } else { FSelMode = READ; /* reset for next time */ #endif if (!(_prompt = prompt)) if (pretty && *pretty) _prompt = ProcDefFmt; else _prompt = ProcFmt; #ifdef F_COMPLETION if (!(ans = do_ask("\r\n \t?", f_complete, pretty, _prompt, pretty)) && !(ans = pretty)) complain("[No default file name]"); #else ans = ask(pretty, _prompt, pretty); #endif #ifdef FILESELECTOR } #endif #ifdef ASK_DIR ask_dir_only = 0; #endif return PathParse(ans, buf); } #ifdef ARG_EXPAND # ifndef F_COMPLETION #error "ARG_EXPAND requires F_COMPLETION" # endif #include "re.h" /* This is for systems without a decent (unix-like) shell. The usual /bin/sh file metacharacters (plus some expansions) are supported. */ DEF_INT( "expand-command-line-arguments", ExpCmdArgs, V_BOOL ) _IF(def ARG_EXPAND)_IF(def PRIVATE) = YES; /* Check if `filepattern' contains any wildcard characters, remove escape characters from it and build the equivalent RE pattern. */ private int wildcarded __(( const char *_(pattern), char *_(REsult) )); private int wildcarded(filepattern, REsult) const char *filepattern; char *REsult; /* equivalent RE pattern ends up here */ { register char c; register char *d = REsult; register const char *s = filepattern; register int wildcarded = NO; # define BRA '[' # define KET ']' # define QUOTE '\\' # if vms /* VMS already uses `[' and `]' as directory delimiters, so we have to use something else for character sets. */ # undef BRA # define BRA '(' # undef KET # define KET ')' # endif # ifdef DOS /* we cannot use backslash as escape character since it is already in use as directory separator. Sigh... */ # undef QUOTE # define QUOTE '`' # endif while (c = *s++) { switch (*d++ = c) { default: continue; case QUOTE: # if (QUOTE != '\\') d[-1] = '\\'; # endif c = *s; if (index("[*?+,", c)) *d++ = c; else d[-1] = c; /* remove escape in literal string */ strcpy(s - 1, s); continue; case '+': wildcarded++; continue; case '?': case '*': wildcarded++; d[-1] = '.', *d++ = c; continue; case '|': case '{': case '}': wildcarded++; case '.': d[-1] = '\\', *d++ = c; continue; case BRA: # if (BRA != '[') d[-1] = '['; # endif if (*s == '^' || *s == '!' || *s == '~') *d++ = '^', s++; wildcarded++; do { if (*s == '\0') { /* bail out on malformed pattern */ wildcarded = NO; break; } *d++ = *s++; } while (*s != KET); # if (KET != ']') *d++ = ']'; s++; # endif continue; } } *d++ = '$'; *d = '\0'; return wildcarded; } /* filter for scandir. Minibuf should contain RE pattern to match. */ private int arg_match __(( const char *_(name) )); private int arg_match(name) const char *name; { return LookingAt(Minibuf, name, 0); } /* first call, with arg != NULL, initializes the name list and returns the first match. Subsequent calls, with arg == NULL, hand out the rest of the list, one by one, until the list is exhausted. */ char * arg_expand(arg) const char *arg; { static struct { char **base; char **curr; int length; char *directory; } list ZERO; char tmp[FILESIZE]; register char *s, *d; if (False(ExpCmdArgs)) return (char *) arg; if (arg) { /* build a new list */ if (list.base) { /* free any leftover list */ freedir(&list.base, list.length); free(list.directory); } env_expand(d = strcpy(genbuf, arg)); /* If the argument does not contain wildcard characters, we have an easy job. (we don't support wildcards in the directory part of the pathname yet.) */ if (!wildcarded(s = basename(d), Minibuf)) return d; if (s != d) { char save = *s; *s = '\0'; list.directory = copystr(d); d = PathParse(d, tmp); *s = save; } else { list.directory = copystr(NullStr); d = pwd(); } #ifdef ASK_DIR ask_dir_only = -1; #endif list.length = scandir(d, &list.base, arg_match, alphacomp); list.curr = list.base; if (list.length <= 0) return genbuf; /* no match; return original pattern */ } if (list.base == NULL) return NULL; if ((s = *list.curr++) == NULL) { freedir(&list.base, list.length); free(list.directory); return NULL; } return make_filename(genbuf, list.directory, s); } #endif /* ARG_EXPAND */ /*====================================================================== * $Log: ask.c,v $ * Revision 14.32.0.12 1994/06/23 02:46:23 tom * (f_complete): fix name of scratch buffer. * * Revision 14.32.0.10 1994/04/22 18:24:21 tom * (ask,do_ask,yes_or_no_p): use `va_register va_list'. * * Revision 14.32.0.8 1993/11/01 16:42:20 tom * (real_ask): reset this_cmd ater ^C ^S ^R insert to force num.arg reset; * (fill_in): show completion list with C-U . * * Revision 14.32.0.8 1993/11/01 16:42:20 tom * *** empty log message *** * * Revision 14.32.0.7 1993/10/29 02:43:03 tom * wildcarded(): const-ify. * * Revision 14.32.0.6 1993/10/28 00:54:50 tom * f_complete: add FIX_FILENAME to allow completion on case-insensitive names. * wildcarded: const fix. * * Revision 14.32 1993/06/09 01:02:21 tom * optimize for autonfsmount symbolic directory links. * * Revision 14.31 1993/02/15 02:27:35 tom * remove (void) casts; real_ask() now always restores next window to orig. * buffer; lotsa random optimizations. * * Revision 14.30 1993/02/05 18:11:00 tom * cleanup whitespace; some random optimizations. * * Revision 14.28 1992/10/22 01:19:58 tom * make filetype() private and add prototype. * * Revision 14.27 1992/09/10 05:17:36 tom * real_ask(): don't abort macro execution when Interactive; * fill_in(): force display of [Ambiguous] etc. when "completion-auto-help" set. * * Revision 14.26 1992/08/26 23:56:50 tom * add support for "completion-auto-help" in f_complete() and fill_in(); * PRIVATE-ized some Variable defs; add RCS directives. * */