|
|
1.1 root 1: /*************************************************************************
2: * This program is copyright (C) 1985, 1986 by Jonathan Payne. It is *
3: * provided to you without charge for use only on a licensed Unix *
4: * system. You may copy JOVE provided that this notice is included with *
5: * the copy. You may not sell copies of this program or versions *
6: * modified for use on microcomputer systems, unless the copies are *
7: * included with a Unix system distribution and the source is provided. *
8: *************************************************************************/
9:
10: #include "jove.h"
11: #include "termcap.h"
12: #include "ctype.h"
13: #include <signal.h>
14: #include <varargs.h>
15:
16: #ifdef F_COMPLETION
17: # include <sys/stat.h>
18: #endif
19:
20: int Asking = 0;
21: char Minibuf[LBSIZE];
22: private Line *CurAskPtr = 0; /* points at some line in mini-buffer */
23: private Buffer *AskBuffer = 0; /* Askbuffer points to actual structure */
24:
25: /* The way the mini-buffer works is this: The first line of the mini-buffer
26: is where the user does his stuff. The rest of the buffer contains
27: strings that the user often wants to use, for instance, file names, or
28: common search strings, etc. If he types C-N or C-P while in ask(), we
29: bump the point up or down a line and extract the contents (we make sure
30: is somewhere in the mini-buffer). */
31:
32: static Buffer *
33: get_minibuf()
34: {
35: if (AskBuffer)
36: return AskBuffer;
37: AskBuffer = do_select((Window *) 0, "Minibuf");
38: AskBuffer->b_type = B_SCRATCH;
39: return AskBuffer;
40: }
41:
42: /* Add a string to the mini-buffer. */
43:
44: minib_add(str, movedown)
45: char *str;
46: {
47: register Buffer *saveb = curbuf;
48:
49: SetBuf(get_minibuf());
50: LineInsert();
51: ins_str(str, NO);
52: if (movedown)
53: CurAskPtr = curline;
54: SetBuf(saveb);
55: }
56:
57: static char *
58: real_ask(delim, d_proc, def, prompt)
59: char *delim,
60: *def,
61: *prompt;
62: int (*d_proc)();
63: {
64: static int InAsk = 0;
65: jmp_buf savejmp;
66: int c,
67: prompt_len;
68: Buffer *saveb = curbuf;
69: int abort = 0,
70: no_typed = 0;
71: data_obj *push_cmd = LastCmd;
72: int o_exp = exp,
73: o_exp_p = exp_p;
74:
75: if (InAsk)
76: complain((char *) 0);
77: push_env(savejmp);
78: InAsk++;
79: SetBuf(get_minibuf());
80: if (!inlist(AskBuffer->b_first, CurAskPtr))
81: CurAskPtr = curline;
82: prompt_len = strlen(prompt);
83: ToFirst(); /* Beginning of buffer. */
84: linebuf[0] = '\0';
85: modify();
86: makedirty(curline);
87:
88: if (setjmp(mainjmp))
89: if (InJoverc) { /* this is a kludge */
90: abort++;
91: goto cleanup;
92: }
93:
94: for (;;) {
95: exp = 1;
96: exp_p = 0;
97: last_cmd = this_cmd;
98: init_strokes();
99: cont: s_mess("%s%s", prompt, linebuf);
100: Asking = curchar + prompt_len;
101: c = getch();
102: if ((c == EOF) || index(delim, c)) {
103: if (d_proc == 0 || (*d_proc)(c) == 0)
104: goto cleanup;
105: } else switch (c) {
106: case CTL(G):
107: message("[Aborted]");
108: abort++;
109: goto cleanup;
110:
111: case CTL(N):
112: case CTL(P):
113: if (CurAskPtr != 0) {
114: int n = (c == CTL(P) ? -exp : exp);
115:
116: CurAskPtr = next_line(CurAskPtr, n);
117: if (CurAskPtr == curbuf->b_first && CurAskPtr->l_next != 0)
118: CurAskPtr = CurAskPtr->l_next;
119: (void) ltobuf(CurAskPtr, linebuf);
120: modify();
121: makedirty(curline);
122: Eol();
123: this_cmd = 0;
124: }
125: break;
126:
127: case CTL(R):
128: if (def)
129: ins_str(def, NO);
130: else
131: rbell();
132: break;
133:
134: default:
135: dispatch(c);
136: break;
137: }
138: if (curbuf != AskBuffer)
139: SetBuf(AskBuffer);
140: if (curline != curbuf->b_first) {
141: CurAskPtr = curline;
142: curline = curbuf->b_first; /* with whatever is in linebuf */
143: }
144: if (this_cmd == ARG_CMD)
145: goto cont;
146: }
147: cleanup:
148: pop_env(savejmp);
149:
150: LastCmd = push_cmd;
151: exp_p = o_exp_p;
152: exp = o_exp;
153: no_typed = (linebuf[0] == '\0');
154: strcpy(Minibuf, linebuf);
155: SetBuf(saveb);
156: InAsk = Asking = Interactive = 0;
157: if (!abort) {
158: if (!charp()) {
159: Placur(ILI, 0);
160: flusho();
161: }
162: if (no_typed)
163: return 0;
164: } else
165: complain(mesgbuf);
166: return Minibuf;
167: }
168:
169: /* VARARGS2 */
170:
171: char *
172: ask(def, fmt, va_alist)
173: char *def,
174: *fmt;
175: va_dcl
176: {
177: char prompt[128];
178: char *ans;
179: va_list ap;
180:
181: va_start(ap);
182: format(prompt, sizeof prompt, fmt, ap);
183: va_end(ap);
184: ans = real_ask("\r\n", (int (*)()) 0, def, prompt);
185: if (ans == 0) { /* Typed nothing. */
186: if (def == 0)
187: complain("[No default]");
188: return def;
189: }
190: return ans;
191: }
192:
193: /* VARARGS2 */
194:
195: char *
196: do_ask(delim, d_proc, def, fmt, va_alist)
197: char *delim,
198: *def,
199: *fmt;
200: int (*d_proc)();
201: va_dcl
202: {
203: char prompt[128];
204: va_list ap;
205:
206: va_start(ap);
207: format(prompt, sizeof prompt, fmt, ap);
208: va_end(ap);
209: return real_ask(delim, d_proc, def, prompt);
210: }
211:
212: #ifdef F_COMPLETION
213: static char *fc_filebase;
214: char BadExtensions[128] = ".o";
215:
216: static
217: bad_extension(name, bads)
218: char *name,
219: *bads;
220: {
221: char *ip;
222: int namelen = strlen(name),
223: ext_len,
224: stop = 0;
225:
226: do {
227: if (ip = index(bads, ' '))
228: *ip = 0;
229: else {
230: ip = bads + strlen(bads);
231: stop++;
232: }
233: if ((ext_len = ip - bads) == 0)
234: continue;
235: if ((ext_len < namelen) &&
236: (strcmp(&name[namelen - ext_len], bads) == 0))
237: return YES;
238: } while ((bads = ip + 1), !stop);
239: return NO;
240: }
241:
242: f_match(file)
243: char *file;
244: {
245: int len = strlen(fc_filebase);
246:
247: return ((len == 0) ||
248: (strncmp(file, fc_filebase, strlen(fc_filebase)) == 0));
249: }
250:
251: static
252: isdir(name)
253: char *name;
254: {
255: struct stat stbuf;
256: char filebuf[FILESIZE];
257:
258: PathParse(name, filebuf);
259: return ((stat(filebuf, &stbuf) != -1) &&
260: (stbuf.st_mode & S_IFDIR) == S_IFDIR);
261: }
262:
263: static
264: fill_in(dir_vec, n)
265: register char **dir_vec;
266: {
267: int minmatch = 0,
268: numfound = 0,
269: lastmatch = -1,
270: i,
271: the_same = TRUE, /* After filling in, are we the same
272: as when we were called? */
273: is_ntdir; /* Is Newly Typed Directory name */
274: char bads[128];
275:
276: for (i = 0; i < n; i++) {
277: strcpy(bads, BadExtensions);
278: /* bad_extension() is destructive */
279: if (bad_extension(dir_vec[i], bads))
280: continue;
281: if (numfound)
282: minmatch = min(minmatch,
283: numcomp(dir_vec[lastmatch], dir_vec[i]));
284: else
285: minmatch = strlen(dir_vec[i]);
286: lastmatch = i;
287: numfound++;
288: }
289: /* Ugh. Beware--this is hard to get right in a reasonable
290: manner. Please excuse this code--it's past my bedtime. */
291: if (numfound == 0) {
292: rbell();
293: return;
294: }
295: Eol();
296: if (minmatch > strlen(fc_filebase)) {
297: the_same = FALSE;
298: null_ncpy(fc_filebase, dir_vec[lastmatch], minmatch);
299: Eol();
300: makedirty(curline);
301: }
302: is_ntdir = ((numfound == 1) &&
303: (curchar > 0) &&
304: (linebuf[curchar - 1] != '/') &&
305: (isdir(linebuf)));
306: if (the_same && !is_ntdir) {
307: add_mess(n == 1 ? " [Unique]" : " [Ambiguous]");
308: (void) SitFor(7);
309: }
310: if (is_ntdir)
311: Insert('/');
312: }
313:
314: extern int alphacomp();
315:
316: /* called from do_ask() when one of "\r\n ?" is typed. Does the right
317: thing, depending on which. */
318:
319: static
320: f_complete(c)
321: {
322: char dir[FILESIZE],
323: **dir_vec;
324: int nentries,
325: i;
326:
327: if (c == CR || c == LF)
328: return 0; /* tells ask to return now */
329: if ((fc_filebase = rindex(linebuf, '/')) != 0) {
330: char tmp[FILESIZE];
331:
332: null_ncpy(tmp, linebuf, (++fc_filebase - linebuf));
333: if (tmp[0] == '\0')
334: strcpy(tmp, "/");
335: PathParse(tmp, dir);
336: } else {
337: fc_filebase = linebuf;
338: strcpy(dir, ".");
339: }
340: if ((nentries = scandir(dir, &dir_vec, f_match, alphacomp)) == -1) {
341: add_mess(" [Unknown directory: %s]", dir);
342: (void) SitFor(7);
343: return 1;
344: }
345: if (nentries == 0) {
346: add_mess(" [No match]");
347: (void) SitFor(7);
348: } else if (c == ' ')
349: fill_in(dir_vec, nentries);
350: else {
351: /* we're a '?' */
352: int maxlen = 0,
353: ncols,
354: col,
355: lines,
356: linespercol;
357:
358: TOstart("Completion", FALSE); /* false means newline only on request */
359: Typeout("(! means file will not be chosen unless typed explicitly)");
360: Typeout((char *) 0);
361: Typeout("Possible completions (in %s):", dir);
362: Typeout((char *) 0);
363:
364: for (i = 0; i < nentries; i++)
365: maxlen = max(strlen(dir_vec[i]), maxlen);
366: maxlen += 4; /* pad each column with at least 4 spaces */
367: ncols = (CO - 2) / maxlen;
368: linespercol = 1 + (nentries / ncols);
369:
370: for (lines = 0; lines < linespercol; lines++) {
371: for (col = 0; col < ncols; col++) {
372: int isbad,
373: which;
374: char bads[128];
375:
376: which = (col * linespercol) + lines;
377: if (which >= nentries)
378: break;
379: strcpy(bads, BadExtensions);
380: isbad = bad_extension(dir_vec[which], bads);
381: Typeout("%s%-*s", isbad ? "!" : NullStr,
382: maxlen - isbad, dir_vec[which]);
383: }
384: Typeout((char *) 0);
385: }
386: TOstop();
387: }
388: freedir(&dir_vec, nentries);
389: return 1;
390: }
391:
392: #endif
393:
394: char *
395: ask_file(def, buf)
396: char *def,
397: *buf;
398: {
399: char *ans,
400: prompt[128],
401: *pretty_name = pr_name(def);
402:
403: if (def != 0 && *def != '\0')
404: sprintf(prompt, ": %f (default %s) ", pretty_name);
405: else
406: sprintf(prompt, ProcFmt);
407: #ifdef F_COMPLETION
408: ans = real_ask("\r\n ?", f_complete, pretty_name, prompt);
409: if (ans == 0 && (ans = pretty_name) == 0)
410: complain("[No default file name]");
411: #else
412: ans = ask(pretty_name, prompt);
413: #endif
414: PathParse(ans, buf);
415:
416: return buf;
417: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.