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 >
Wrap
C/C++ Source or Header
|
1996-09-28
|
14KB
|
630 lines
/************************************************************************
* This program is Copyright (C) 1986-1996 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. *
************************************************************************/
#include "jove.h"
#include "jctype.h"
#include "chars.h"
#include "disp.h"
#include "fp.h"
#include "scandir.h"
#include "screen.h" /* for Placur */
#include "ask.h"
#include "delete.h"
#include "insert.h"
#include "extend.h"
#include "fmt.h"
#include "marks.h"
#include "move.h"
#ifdef MAC
# include "mac.h"
#else /* !MAC */
# ifdef F_COMPLETION
# include <sys/stat.h>
# endif
#endif /* !MAC */
ZXchar AbortChar = CTL('G'); /* VAR: cancels command input */
bool Asking = NO;
int AskingWidth;
char Minibuf[LBSIZE];
private LinePtr CurAskPtr = NULL; /* points at some line in mini-buffer */
private Buffer *AskBuffer = NULL; /* 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 ^N or ^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()
{
if (AskBuffer) { /* make sure ut still exists */
register Buffer *b;
for (b = world; b != NULL; b = b->b_next)
if (b == AskBuffer)
return b;
}
AskBuffer = do_select((Window *)NULL, "*minibuf*");
AskBuffer->b_type = B_SCRATCH;
return AskBuffer;
}
/* Add a string to the mini-buffer. */
void
minib_add(str, movedown)
char *str;
bool movedown;
{
register Buffer *saveb = curbuf;
SetBuf(get_minibuf());
LineInsert(1);
ins_str(str);
if (movedown)
CurAskPtr = curline;
SetBuf(saveb);
}
bool InRealAsk = NO;
private char *
real_ask(delim, d_proc, def, prompt)
const char *delim,
*def,
*prompt;
bool (*d_proc) ptrproto((ZXchar));
{
jmp_buf savejmp;
ZXchar c;
int prompt_len;
Buffer *saveb = curbuf;
volatile bool aborted = NO;
bool no_typed = NO;
data_obj *push_cmd = LastCmd;
int saved_as, saved_ac;
#ifdef MAC
menus_off();
#endif
if (InRealAsk)
complain((char *) NULL);
save_arg(saved_as, saved_ac);
push_env(savejmp);
InRealAsk = YES;
SetBuf(get_minibuf());
if (!inlist(AskBuffer->b_first, CurAskPtr))
CurAskPtr = curline;
prompt_len = strlen(prompt);
ToFirst(); /* Beginning of buffer. */
linebuf[0] = '\0';
modify();
makedirty(curline);
if (setjmp(mainjmp)) {
if (InJoverc) { /* this is a kludge */
aborted = YES;
goto cleanup;
}
}
this_cmd = OTHER_CMD; /* probably redundant */
for (;;) {
cmd_sync();
s_mess("%s%s", prompt, linebuf);
Asking = YES;
AskingWidth = curchar + prompt_len;
c = getch();
if (c != '\0' && strchr(delim, c) != NULL) {
if (d_proc == NULL_ASK_EXT || !(*d_proc)(c))
break;
} else if (c == AbortChar) {
message("[Aborted]");
aborted = YES;
break;
} else switch (c) {
case CTL('N'):
case CTL('P'):
if (CurAskPtr != NULL) {
int n = (c == CTL('P') ? -arg_value() : arg_value());
CurAskPtr = next_line(CurAskPtr, n);
if (CurAskPtr == curbuf->b_first && CurAskPtr->l_next != NULL)
CurAskPtr = CurAskPtr->l_next;
(void) ltobuf(CurAskPtr, linebuf);
modify();
makedirty(curline);
Eol();
this_cmd = OTHER_CMD;
}
break;
case CTL('R'):
if (def != NULL)
ins_str(def);
else
rbell();
break;
default:
dispatch(c);
break;
}
if (curbuf != AskBuffer)
SetBuf(AskBuffer);
if (curline != curbuf->b_first) {
CurAskPtr = curline;
curline = curbuf->b_first; /* with whatever is in linebuf */
}
}
cleanup:
pop_env(savejmp);
LastCmd = push_cmd;
restore_arg(saved_as, saved_ac);
no_typed = (linebuf[0] == '\0');
strcpy(Minibuf, linebuf);
SetBuf(saveb);
InRealAsk = Asking = Interactive = NO;
if (!aborted) {
if (!PreEmptOutput()) {
Placur(ILI, 0);
flushscreen();
}
if (no_typed)
return NULL;
} else
complain((char *)NULL);
return Minibuf;
}
#ifdef STDARGS
char *
ask(char *def, char *fmt, ...)
#else
/*VARARGS2*/ char *
ask(def, fmt, va_alist)
char *def,
*fmt;
va_dcl
#endif
{
char prompt[128];
char *ans;
va_list ap;
va_init(ap, fmt);
format(prompt, sizeof prompt, fmt, ap);
va_end(ap);
ans = real_ask("\r\n", NULL_ASK_EXT, def, prompt);
if (ans == NULL) { /* Typed nothing. */
if (def == NULL)
complain("[No default]");
ans = def;
}
return ans;
}
#ifdef STDARGS
char *
do_ask(const char *delim, bool (*d_proc) ptrproto((ZXchar)), const char *def, const char *fmt, ...)
#else
/*VARARGS4*/ char *
do_ask(delim, d_proc, def, fmt, va_alist)
const char *delim;
bool (*d_proc) ptrproto((ZXchar));
const char *def;
const char *fmt;
va_dcl
#endif
{
char prompt[128];
va_list ap;
va_init(ap, fmt);
format(prompt, sizeof prompt, fmt, ap);
va_end(ap);
return real_ask(delim, d_proc, def, prompt);
}
bool OneKeyConfirmation = NO; /* VAR: single y or n keystroke sufficient? */
#ifdef STDARGS
bool
yes_or_no_p(char *fmt, ...)
#else
/*VARARGS1*/ bool
yes_or_no_p(fmt, va_alist)
char *fmt;
va_dcl
#endif
{
char prompt[128];
va_list ap;
va_init(ap, fmt);
format(prompt, sizeof prompt, fmt, ap);
va_end(ap);
for (;;) {
if (OneKeyConfirmation) {
ZXchar c;
message(prompt);
Asking = YES; /* so redisplay works */
AskingWidth = strlen(prompt);
c = getch();
Asking = NO;
if (c == AbortChar)
complain("[Aborted]");
switch (CharUpcase(c)) {
case 'Y':
return YES;
case 'N':
return NO;
default:
add_mess("[Type Y or N]");
Asking = YES; /* so cursor sits on question */
SitFor(10);
}
} else {
char *ans = ask((char *) NULL, prompt);
if (caseeqn(ans, "yes", strlen(ans)))
return YES;
if (caseeqn(ans, "no", strlen(ans)))
return NO;
rbell();
}
}
/* NOTREACHED */
}
#ifdef F_COMPLETION
/* look for any substrings of the form $foo in linebuf, and expand
them according to their value in the environment (if possible) -
this munges all over curchar and linebuf without giving it a second
thought (I must be getting lazy in my old age) */
# ifndef MAC /* no environment in MacOS */
bool DoEVexpand = YES; /* VAR: should we expand evironment variables? */
private void
EVexpand()
{
register char c;
register char *lp = linebuf,
*ep;
char varname[128],
*vp,
*lp_start;
Mark *m = MakeMark(curline, curchar);
while ((c = *lp++) != '\0') {
if (c == '$') {
lp_start = lp - 1; /* the $ */
vp = varname;
/* Pick up env. variable name ([a-zA-Z0-9_]*) */
while (('a' <= (c = *lp++) && c <= 'z')
|| ('A' <= c && c <= 'Z')
|| ('0' <= c && c <= '9')
|| c == '_')
*vp++ = c;
*vp = '\0';
/* if we find an env. variable with the right
name, we insert it in linebuf, and then delete
the variable name that we're replacing - and
then we continue in case there are others ... */
if ((ep = getenv(varname)) != NULL) {
curchar = lp_start - linebuf;
ins_str(ep);
del_char(FORWARD, (int)strlen(varname) + 1, NO);
lp = linebuf + curchar;
}
}
}
ToMark(m);
DelMark(m);
}
# endif /* !MAC */
private char *fc_filebase;
bool DispBadFs = YES; /* VAR: display filenames with bad extensions? */
char BadExtensions[sizeof(BadExtensions)] = /* VAR: extensions to ignore */
# ifndef MSFILESYSTEM
/* .o: object code format
* .Z .gz: compressed formats
* .tar .zip .zoo: archive formats
* .gif .jpg .jpeg .mpg .mpeg .tif .tiff .rgb: graphics formats
* .dvi: TeX or ditroff output
* .elc: compiled GNU EMACS code
*/
".o .Z .gz .tar .zip .zoo .gif .jpg .jpeg .mpg .mpeg .tif .tiff .rgb .dvi .elc";
# else /* MSFILESYSTEM */
/* .obj .lib .exe .com .dll: object code files
* .arc .zip .zoo: archive formats
* .bmp .gif .jpg .mpg .tif .pcx: graphics formats
* .wks .wk1 .xls: spreadsheet formats
*/
".obj .lib .exe .com .dll .arc .zip .zoo .bmp .gif .jpg .mpg .tif .pcx .wks .wk1 .xls";
# endif /* MSFILESYSTEM */
private bool
bad_extension(name)
char *name;
{
char *ip,
*bads;
size_t namelen = strlen(name),
ext_len;
# if defined(UNIX) || defined(MSFILESYSTEM)
# ifdef DIRECTORY_ADD_SLASH
if (strcmp(name, "./")==0 || strcmp(name, "../")==0)
return YES;
# else
if (strcmp(name, ".")==0 || strcmp(name, "..")==0)
return YES;
# endif /* DIRECTORY_ADD_SLASH */
# endif /* UNIX || MSFILESYSTEM */
for (ip=bads=BadExtensions; *ip!='\0'; bads = ip+1) {
if ((ip = strchr(bads, ' ')) == NULL)
ip = bads + strlen(bads);
ext_len = ip - bads;
if (ext_len != 0 && ext_len < namelen
# ifdef FILENAME_CASEINSENSITIVE
&& caseeqn(&name[namelen - ext_len], bads, ext_len))
# else
&& strncmp(&name[namelen - ext_len], bads, ext_len) == 0)
# endif
return YES;
}
return NO;
}
private bool f_match proto((char* file)); /* needed to comfort dumb MS Visual C */
private bool
f_match(file)
char *file;
{
size_t len = strlen(fc_filebase);
if (!DispBadFs && bad_extension(file))
return NO;
return len == 0
# ifdef FILENAME_CASEINSENSITIVE
|| caseeqn(file, fc_filebase, len);
# else
|| strncmp(file, fc_filebase, len) == 0;
# endif
}
# ifndef DIRECTORY_ADD_SLASH
private bool
isdir(name)
char *name;
{
(void) do_stat(name, (Buffer *) NULL, DS_DIR);
return was_dir;
}
# endif /* !DIRECTORY_ADD_SLASH */
private void
fill_in(dir_vec, n)
register char **dir_vec;
int n;
{
bool filter;
for (filter = DispBadFs; ; filter = NO) {
int
minmatch = 0,
numfound = 0,
lastmatch = -1,
i;
for (i = 0; i < n; i++) {
/* If filter is NO, we accept all entries
(either DispBadFs is NO, in which case filtering
was done when dir_vec was built, or DispBadFs
is YES, but we are trying again, after finding
no files passed the filter. */
if (!filter || !bad_extension(dir_vec[i])) {
minmatch = numfound == 0
? (int)strlen(dir_vec[i])
#ifdef FILENAME_CASEINSENSITIVE
: min(minmatch, numcompcase(dir_vec[lastmatch], dir_vec[i]));
#else
: min(minmatch, numcomp(dir_vec[lastmatch], dir_vec[i]));
#endif
lastmatch = i;
numfound += 1;
}
}
if (numfound == 0) {
if (!filter) {
/* hopeless: complain and give up */
rbell();
break;
}
} else {
bool
the_same = YES; /* After filling in, are we the same
as when we were called? */
if (minmatch > (int)strlen(fc_filebase)) {
if (minmatch >= LBSIZE - (fc_filebase - linebuf))
len_error(JMP_COMPLAIN);
the_same = NO;
null_ncpy(fc_filebase, dir_vec[lastmatch], (size_t) minmatch);
makedirty(curline);
}
Eol();
# ifndef DIRECTORY_ADD_SLASH
if (numfound == 1 && fc_filebase[0] != '\0' && isdir(linebuf)) {
the_same = NO;
insert_c('/', 1);
}
# endif
if (the_same) {
add_mess(" [%s%s]",
!filter && DispBadFs? "!" : NullStr,
numfound == 1? "Unique" : "Ambiguous");
SitFor(7);
}
break; /* we're done */
}
}
}
/* called from do_ask() when one of "\r\n ?" is typed. Does the right
thing, depending on which. */
private bool
f_complete(c)
ZXchar c;
{
char
dir[FILESIZE],
**dir_vec;
int
nentries;
# ifndef MAC /* no environment in MacOS */
if (DoEVexpand)
EVexpand();
# endif
if (c == CR || c == LF)
return NO; /* tells ask to return now */
fc_filebase = strrchr(linebuf, '/');
# ifdef MSFILESYSTEM
{
char *p = strrchr(fc_filebase == NULL? linebuf : fc_filebase, '\\');
if (p != NULL)
fc_filebase = p;
if (fc_filebase == NULL)
fc_filebase = strrchr(linebuf, ':');
}
# endif /* MSFILESYSTEM */
if (fc_filebase != NULL) {
char tmp[FILESIZE];
fc_filebase += 1;
null_ncpy(tmp, linebuf, (size_t) (fc_filebase - linebuf));
if (tmp[0] == '\0')
strcpy(tmp, "/");
PathParse(tmp, dir);
} else {
fc_filebase = linebuf;
strcpy(dir, ".");
}
if ((nentries = jscandir(dir, &dir_vec, f_match, fnamecomp)) == -1) {
add_mess(" [Unknown directory: %s]", dir);
SitFor(7);
return YES;
}
if (nentries == 0) {
add_mess(" [No match]");
SitFor(7);
} else if (jiswhite(c)) {
fill_in(dir_vec, nentries);
} else {
/* we're a '?' */
int
i,
maxlen = 0,
ncols,
line,
entriespercol;
TOstart("Completion");
Typeout("(! means file will not be chosen unless typed explicitly)");
Typeout("Possible completions (in %s):", dir);
for (i = 0; i < nentries; i++)
maxlen = max((int)strlen(dir_vec[i]), maxlen);
maxlen += 4; /* pad each column with at least 4 spaces */
ncols = min((CO - 2), MAX_TYPEOUT) / maxlen;
entriespercol = (nentries + ncols - 1) / ncols;
for (line = 0; line < entriespercol; line++) {
int
col,
which = line,
bufcol = 0;
char buf[MAX_TYPEOUT + 1];
for (col = 0; col < ncols; col++) {
if (which < nentries) {
bool isbad = DispBadFs && bad_extension(dir_vec[which]);
swritef(buf+bufcol, sizeof(buf) - bufcol, "%s%-*s",
isbad ? "!" : NullStr,
maxlen - isbad, dir_vec[which]);
}
which += entriespercol;
bufcol += maxlen;
}
Typeout("%s", buf);
}
TOstop();
}
freedir(&dir_vec, nentries);
return YES;
}
#endif /* F_COMPLETION */
char *
ask_file(prmt, def, buf)
const char *prmt;
char
*def,
*buf;
{
char *ans,
prompt[128],
*pretty_name = pr_name(def, YES);
if (prmt == NULL)
swritef(prompt, sizeof(prompt), ProcFmt);
else
strcpy(prompt, prmt);
if (def != NULL && *def != '\0') {
size_t pl = strlen(prompt);
swritef(prompt + pl, sizeof(prompt)-pl, "(default %s) ", pretty_name);
if ((int)strlen(prompt) * 2 >= CO)
prompt[pl] = '\0';
}
#ifdef F_COMPLETION
ans = real_ask("\r\n \t?", f_complete, pretty_name, prompt);
if (ans == NULL && (ans = pretty_name) == NULL)
complain("[No default file name]");
#else
ans = ask(pretty_name, prompt);
#endif
PathParse(ans, buf);
return buf;
}