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