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