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: }