|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - downloadIndex: ┃ T c ┃
Length: 29213 (0x721d) Types: TextFile Names: »complete.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki └─ ⟦this⟧ »EUUGD11/euug-87hel/sec8/mcp/src/complete.c«
#include <stdio.h> #include <sys/types.h> #include <sys/time.h> #include <signal.h> #include <setjmp.h> #include <strings.h> #include <errno.h> #include "sysdep.h" #include "macros.h" #include "mem.h" #include "history.h" #include "lists.h" #ifdef BSD4_3 char *getwd(); #endif #define swap(a, b) { char t; t=a; a=b; b=t; } #define SCMPN(a, b) !strncmp((a), (b), strlen(a)) extern jmp_buf in_continue; extern char Working_Directory[]; char *BACKSPACE = "\b \b", *dirof(), *fileof(), *pathcomplete(); static char line[BUFSIZ+1], hline[BUFSIZ+1]; struct hist currh = { hline, (char *)0, 0, 0, 0, 0, (struct list *)0 }; struct list History; static char *exprv[] = { 0, 0 }; #ifdef SENDMAIL extern struct list Aliases; #endif #ifdef HELPDIR extern struct list Terms; #endif extern struct list AllCommands, Commands, Classes, Groups, Sigs; extern struct list Null_List, Users, Vigs, Ranges; extern int DevTty; /* * Get the last part of a command. */ char * tail(s) char *s; { char *p, *sp; sp = rindex(s, ' '); if (!sp) { p = rindex(s, '-'); return p ? ++p : s; } else while (p = index(s, '-')) { if (p > sp) break; s = p + 1; } return s; } /* * Count how many characters in s1 until s1 differs with s2 */ int nmatch(s1, s2) char *s1, *s2; { register int i; for (i = 0; s1[i] && s2[i] && s1[i] == s2[i]; i++) ; return(i); } /* * Command and argument completion. The variable s will be changed to reflect * what it was completed to. The variable *iscomplete will contain a 1 if s * was expanded completely. * * One of these conditions will be satisfied and the apprpriate steps taken. * * 1) If c_list is empty return immediately. * 2) If s is an empty string and c_list contains more than one string, * return an empty string with no change. * 3) If c_list only contains one string and s prefixes it then * completely expand s to that string. * 4) If s is exactly equal to one of the strings in c_list, merely * report that s in completely expanded. * 5) If s prefixes none of the strings in c_list then chop characters * out of s until it will prefix at least one of the strings in c_list. * 6) If s uniquely prefixes a string in c_list then completely expand * s to that string. * 7) If s prefixes more than one string in c_list expand s to the point * where the strings differ. * * Note: the strings in c_list MUST ALREADY be sorted in collating * sequence! */ char * complete(s, c_list, iscomplete) char *s; struct list *c_list; int *iscomplete; { static char delta[LONG_BUF * 2]; int i, lindex, n_matched, s_len, found; delta[0] = '\0'; *iscomplete = 0; /* * Check for condition 1. If no completion list, forget it. */ if (c_list->l_count == 0) return delta; /* * Check for condition 2. If s is empty and there is more than one * word in the completion list, forget it. */ if (*s == '\0' && c_list->l_count > 1) return delta; /* * Check for condition 3. If there is only one word in the * completion list and s prefixes it, this is the one. */ s_len = strlen(s); if (c_list->l_count==1&&!strncmp(s,(char *)c_list->l_list[0],s_len)) { (void) strcpy(delta, &(((char *)c_list->l_list[0])[s_len])); (void) strcat(delta, " "); (void) strcat(s, delta); *iscomplete = 1; return delta; } /* * Binary search the completion list. * If s is not found, all the strings that s prefixes (if any) * will have indices >= lindex . */ lindex = search_list(c_list, s, strcmp, &found); /* * Check for condition 4. If we got a perfect match, skidaddle. */ if (found) { (void) strcpy(delta, " "); (void) strcat(s, delta); *iscomplete = 1; return delta; } /* * Check for condition 5. Hacksaw the garbage until we recognize * something. */ n_matched = 0; for (i=lindex-1; i < lindex+2; i++) { if (i < 0) continue; if (i >= c_list->l_count) break; n_matched=max(n_matched, nmatch(s,(char *)c_list->l_list[i])); /* * If s prefixes c_list->l_list[lindex] or one of the words * adjacent to it, we set lindex to its index so that lindex * now indexes to the first word that s prefixes. */ if (n_matched == s_len) { lindex = i; break; } } if (n_matched < s_len) { for (i=0; i < s_len - n_matched; i++) (void) strcat(delta, BACKSPACE); s[n_matched] = '\0'; return delta; } /* * Check for condition 6. If s can be unambigously expanded * then do so. */ if (lindex+1 == c_list->l_count || strncmp((char *)c_list->l_list[lindex], (char *)c_list->l_list[lindex+1], s_len)) { (void) strcpy(delta, ((char *)c_list->l_list[lindex])+s_len); (void) strcat(delta, " "); (void) strcat(s, delta); *iscomplete = 1; return delta; } /* * Gotta be condition 7. Expand as far as possible, but * don't set *iscomplete. */ for (i=lindex+1 ;; i++) { if (i == c_list->l_count) { break; } if (strncmp(s, (char *)c_list->l_list[i], s_len) != 0) break; } i--; n_matched = nmatch((char *)c_list->l_list[lindex], (char *)c_list->l_list[i]); (void) strncpy(delta, ((char *)c_list->l_list[lindex])+s_len, n_matched-s_len); delta[n_matched - s_len] = '\0'; (void) strcat(s, delta); return delta; } redraw(prompt, s) char *prompt, *s; { char_scr('\r'); str_scr(prompt); str_scr(s); return; } #include <ctype.h> /* * If this variable is non-zero then it must contain the length of * the latest visible completion help tag, e.g. "[Ambiguous]". * If zero then there is no floating completion tag at this time. */ static int MustEraseTag; char GET() { extern int errno; int count, nready, rmask = 1; struct timeval t, *timeout = 0; char c; errno = 0; for (;;) { rmask = 1; if (MustEraseTag) { t.tv_sec = 1; t.tv_usec = 250000; timeout = &t; } #ifdef BSD4_3 nready = select(1, (fd_set*)&rmask, (fd_set *)0, (fd_set *)0, timeout); #else nready = select(1, &rmask, 0, 0, timeout); #endif if (nready == -1 && errno != EINTR) { perr("select"); panic((char *)0); } if (nready == 0 || MustEraseTag) { erasetag(); timeout = 0; if (!nready) continue; } count = read(0, &c, 1); if (count == 1) break; if (count == 0) panic("EOF encountered"); if (count == -1 && errno != EINTR) { perr("read"); panic((char *)0); } } c &= 0177; return(c); } showtag(tag) char *tag; { register int i; MustEraseTag = strlen(tag) + 1; char_scr(' '); str_scr(tag); for (i=0; i < MustEraseTag; i++) char_scr('\b'); return; } erasetag() { register int i; for (i=0; i < MustEraseTag; i++) char_scr(' '); for (i=0; i < MustEraseTag; i++) char_scr('\b'); MustEraseTag = 0; return; } /* * Figure out which argument vector to use for argument completion. */ struct list * picklist(cmd) char *cmd; { cmd = tail(cmd); if (SCMPN("user", cmd)) return(&Users); else if (SCMPN("sig", cmd)) return(&Sigs); else if (SCMPN("class", cmd)) return(&Classes); else if (SCMPN("group", cmd)) return(&Groups); else if (SCMPN("range", cmd)) return(&Ranges); else if (SCMPN("vig", cmd)) return(&Vigs); else if (SCMPN("command", cmd)) return(&AllCommands); #ifdef SENDMAIL else if (SCMPN("alias", cmd)) return(&Aliases); #endif #ifdef HELPDIR else if (SCMPN("is", cmd)) return(&Terms); #endif else return(&Null_List); } #ifdef sun #define sighandler (void (*)()) #else #define sighandler (int (*)()) #endif #ifdef sun void #endif tstp_cleanup() { char_scr('\r'); nocbreak(); (void) signal(SIGTSTP, sighandler SIG_DFL); (void) kill(getpid(), SIGTSTP); return; } #ifdef sun void #endif input_continue() { cbreak(); (void) signal(SIGTSTP, tstp_cleanup); longjmp(in_continue, SIGCONT); } /* * Get a line of input using command and smart argument completion. */ GetCommandLine(prompt, nargs, argc, argv) char *prompt; int nargs, *argc; addr *argv; { addr *tmpargv; char buf[LONG_BUF], *p; int c, iscomplete, indx, windex, hindex, i, end_of_line, h_len, d; int quote_open; struct list *c_list; struct hist *hi, hh; cbreak(); MustEraseTag = 0; exprv[0] = buf; *argc = 0; *line = '\0'; quote_open = end_of_line = windex = indx = 0; hindex = History.l_count; c_list = &Commands; (void) signal(SIGTSTP, tstp_cleanup); (void) signal(SIGCONT, input_continue); (void) setjmp(in_continue); str_scr(prompt); while (!end_of_line && indx < BUFSIZ) { c = GET(); if (setjmp(in_continue) == SIGCONT) { redraw(prompt, line); continue; } switch (c) { case ':': break; /* ignore colons for pwd, group, etc. files */ /* * ^P goes one step back in the history list. */ case '\020': if (hindex == 0) { showtag("[History begins here...]"); break; } if (hindex == History.l_count) { (void) strcpy(currh.h_line, line); currh.h_prompt = prompt; currh.h_argc = *argc; currh.h_index = indx; currh.h_windex = windex; currh.h_qopen = quote_open; currh.h_list = c_list; } hi = (struct hist *) History.l_list[--hindex]; h_len = strlen(line) + strlen(prompt); for (i=strlen(hi->h_line); i < h_len; i++) str_scr(BACKSPACE); (void) strcpy(line, hi->h_line); prompt = hi->h_prompt; *argc = hi->h_argc; indx = hi->h_index; windex = hi->h_windex; quote_open = hi->h_qopen; c_list = hi->h_list; redraw(prompt, line); break; /* * ^N goes one step forward in the history list. */ case '\016': if (!History.l_count || hindex == History.l_count) { showtag("[End of history]"); break; } if (hindex == History.l_count-1) { h_len = strlen(line) + strlen(prompt); i=strlen(currh.h_line)+strlen(currh.h_prompt); for (; i < h_len; i++) str_scr(BACKSPACE); (void) strcpy(line, currh.h_line); prompt = currh.h_prompt; *argc = currh.h_argc; indx = currh.h_index; windex = currh.h_windex; quote_open = currh.h_qopen; c_list = currh.h_list; redraw(prompt, line); hindex++; break; } hi = (struct hist *) History.l_list[++hindex]; h_len = strlen(line) + strlen(prompt); i = strlen(hi->h_line) + strlen(hi->h_prompt); for (; i < h_len; i++) str_scr(BACKSPACE); (void) strcpy(line, hi->h_line); prompt = hi->h_prompt; *argc = hi->h_argc; indx = hi->h_index; windex = hi->h_windex; quote_open = hi->h_qopen; c_list = hi->h_list; redraw(prompt, line); break; /* * For space bar, do completion only for the * command (first) word. Allow normal spaces * only at the end of a word. */ case ' ': if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } if (windex != 0 && windex != indx) { char_scr(c); line[indx] = c; line[indx + 1] = '\0'; indx++; if (!quote_open) { windex = indx; (*argc)++; } break; } else if (windex == indx) /* balk at adjacent spaces */ break; else /* FALL THROUGH */ ; /* * Word is completion done here. Completion is activated * by the TAB or the ESC key. Activation by CR or LF only * if completing first word. */ case '\r': case '\n': if (*argc != 0) { end_of_line++; break; } if (nargs && *argc == nargs) { end_of_line++; break; } /* FALL THROUGH */ case '\t': case '\033': if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } p = complete(&line[windex], c_list, &iscomplete); str_scr(p); if (iscomplete) { if (windex == 0) c_list = picklist(line); windex = strlen(line); (*argc)++; } else if (*p == '\0' && indx != windex) showtag("[Ambiguous]"); indx = strlen(line); if ((c == '\r' || c == '\n') && iscomplete) end_of_line++; break; /* * Ctrl-T transposes the two characters before the cursor */ case '\024': if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } /* not enough characters */ if (indx < 2) break; /* can't let space to be first character on a line */ if (indx == 2 && line[1] == ' ') break; swap(line[indx-1], line[indx-2]); /* * If one of the transposed characters was a space, * we have either broken a word in two or merged * the current word with the preceding one. */ if (line[indx-2] == ' ') if (!quote_open && line[indx-1] == '"') { (*argc)++, windex--; c_list = picklist(line); } else if (line[indx-1] == ' ') if (!quote_open && line[indx-2] == '"') { (*argc)--, windex++; c_list = (*argc == 0) ? &Commands : picklist(line); } str_scr("\b \b\b \b"); str_scr(&line[indx-2]); break; /* * Show user possiblities if he wonders why * a word isn't completing. */ case '?': if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } str_scr("\r\n"); (void) strcpy(buf, "^"); (void) strcat(buf, &line[windex]); (void) strcat(buf, ".*"); nocbreak(); /* * We don't want completion info going to stdout * so we temporarily connect stdout to /dev/tty */ d = dup(1); (void) dup2(DevTty, 1); (void) showlist(c_list, (addr *)exprv); /* * Now re-connect stdout to whatever is was before */ (void) dup2(d, 1); (void) close(d); cbreak(); redraw(prompt, line); break; /* * Ctrl-R simply reprints the command line. */ case '\022': redraw(prompt, line); break; /* * Ctrl-W is accepted as the word-kill character. */ case '\027': if (indx == windex && windex != 0) { for (windex -= 2; windex >= 0; windex--) { if (line[windex] == '"') { quote_open = !quote_open; continue; } if (!quote_open&& line[windex] == ' ') break; } windex++; if (windex < 0) windex = 0; decr(*argc); } if (windex == 0) c_list = &Commands; for (; indx > windex; indx--) str_scr(BACKSPACE); line[indx] = '\0'; break; /* * Backspace (^H) and del are accepted as erase * characters, with the screen eraseure being * accomplished via backspace-space-backpsace * sequences */ case '\177': case '\b': if (--indx < windex) { for (windex-=2; windex >= 0; windex--) { if (line[windex] == '"') { quote_open = !quote_open; continue; } if (!quote_open&& line[windex] == ' ') break; } windex++; if (windex < 0) windex = 0; decr(*argc); } if (windex == 0) c_list = &Commands; if (indx >= 0) { if (line[indx] == '"') quote_open = !quote_open; line[indx] = '\0'; str_scr(BACKSPACE); } else indx = 0; break; /* * Ctrl-X and ctrl-U are accepted as line kill * characters. */ case '\030': case '\025': if (indx == 0) break; for (; indx>0; indx--) str_scr(BACKSPACE); quote_open = windex = indx = 0; *line = '\0'; c_list = &Commands; *argc = 0; break; default: if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } if (c == '"') quote_open = !quote_open; /* * Ignore unrecognized control characters */ if (isprint(c)) { char_scr(c); line[indx] = c; line[indx + 1] = '\0'; indx++; } break; } } (void) signal(SIGCONT, sighandler SIG_DFL); (void) signal(SIGTSTP, sighandler SIG_DFL); if (quote_open) { char_scr('"'); line[indx++] = '"'; line[indx] = '\0'; quote_open = 0; } critical(); savestr(&hh.h_prompt, prompt); savestr(&hh.h_line, line); hh.h_argc = *argc; hh.h_index = indx; hh.h_windex = windex; hh.h_qopen = quote_open; hh.h_list = c_list; trimhist(); genlistadd(&History, (addr)&hh, sizeof (struct hist)); non_critical(); if (indx > windex) (*argc)++; (void) cut(line); tmpargv = mkargv(line, *argc); for (i=0; tmpargv[i]; i++) savestr((char **)&argv[i], (char *)tmpargv[i]); argv[i] = NIL; str_scr("\r\n"); nocbreak(); return; } GetLine(prompt, nargs, argc, argv, c_list) char *prompt; int nargs, *argc; addr *argv; struct list *c_list; { addr *tmpargv; char buf[LONG_BUF], *p; int c, iscomplete, indx, windex, i, d, quote_open; cbreak(); exprv[0] = buf; MustEraseTag = 0; *argc = quote_open = 0; *line = '\0'; windex = indx = 0; (void) signal(SIGTSTP, tstp_cleanup); (void) signal(SIGCONT, input_continue); (void) setjmp(in_continue); str_scr(prompt); while ((c = GET()) != '\n' && indx < BUFSIZ) { if (setjmp(in_continue) == SIGCONT) { redraw(prompt, line); continue; } if (c == '\r') break; switch ( c ) { case ':': break; /* ignore colons for pwd, group, etc. files */ case ' ': if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } if (windex != indx) { char_scr(c); line[indx] = c; line[indx+1] = '\0'; indx++; if (!quote_open) { windex = indx; (*argc)++; } } break; case '\t': case '\033': if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } p = complete(&line[windex], c_list, &iscomplete); str_scr(p); if (iscomplete) { windex = strlen(line); (*argc)++; } else if (*p == '\b') showtag("[No match]"); else if (*p == '\0' && indx != windex) showtag("[Ambiguous]"); indx = strlen(line); break; /* * Ctrl-T transposes the two characters before the cursor */ case '\024': if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } if (indx < 2) break; if (indx == 2 && line[1] == ' ') break; swap(line[indx-1], line[indx-2]); /* * If one of the transposed characters was a space, * we have either broken a word in two or merged * the current word with the preceding one. */ if (line[indx-2] == ' ') if (!quote_open && line[indx-1] == '"') (*argc)++, windex--; else if (line[indx-1] == ' ') if (!quote_open && line[indx-2] == '"') (*argc)--, windex++; str_scr("\b \b\b \b"); str_scr(&line[indx-2]); break; /* * Show user possiblities if he wonders why * a word isn't completing. */ case '?': if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } str_scr("\r\n"); (void) strcpy(buf, "^"); (void) strcat(buf, &line[windex]); (void) strcat(buf, ".*"); nocbreak(); /* * We don't want completion info going to stdout * so we temporarily connect stdout to /dev/tty */ d = dup(1); (void) dup2(DevTty, 1); (void) showlist(c_list, (addr *)exprv); /* * Now re-connect stdout to whatever is was before */ (void) dup2(d, 1); (void) close(d); cbreak(); redraw(prompt, line); break; /* * Ctrl-R simply reprints the command line. */ case '\022': redraw(prompt, line); break; /* * Ctrl-W is accepted as the word-kill character. */ case '\027': if (indx == windex && windex != 0) { for (windex -= 2; windex >= 0; windex--) { if (line[windex] == '"') { quote_open = !quote_open; continue; } if (!quote_open&& line[windex] == ' ') break; } windex++; if (windex < 0) windex = 0; decr(*argc); } for (; indx > windex; indx--) str_scr(BACKSPACE); line[indx] = '\0'; break; /* * Backspace (^H) and del are accepted as erase * characters, with the screen erasure being * accomplished via backspace-space-backpsace * sequences */ case '\177': case '\b': if ( --indx < windex ) { for (windex-=2; windex >= 0; windex--) { if (line[windex] == '"') { quote_open = !quote_open; continue; } if (!quote_open&& line[windex] == ' ') break; } windex++; if (windex < 0) windex = 0; decr(*argc); } if (indx >= 0) { if (line[indx] == '"') quote_open = !quote_open; line[indx] = '\0'; str_scr(BACKSPACE); } else indx = 0; break; /* * Ctrl-X and ctrl-U are accepted as line kill * characters. */ case '\030': case '\025': if (indx == 0) break; for (; indx>0; indx--) str_scr(BACKSPACE); quote_open = windex = indx = 0; *line = '\0'; *argc = 0; break; default: if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } if (c == '"') quote_open = !quote_open; /* * Ignore unrecognized control characters */ if (isprint(c)) { char_scr(c); line[indx] = c; line[indx+1] = '\0'; indx++; } break; } } (void) signal(SIGCONT, sighandler SIG_DFL); (void) signal(SIGTSTP, sighandler SIG_DFL); if (quote_open) { char_scr('"'); line[indx++] = '"'; line[indx] = '\0'; } if (indx > windex) (*argc)++; (void) cut(line); tmpargv = mkargv(line, *argc); for (i=0; tmpargv[i]; i++) savestr((char **)&argv[i], (char *)tmpargv[i]); argv[i] = NIL; str_scr("\r\n"); nocbreak(); return; } trimhist() { struct hist *h; if (History.l_count <= MAXHIST) return; h = (struct hist *) listpop(&History); FREEMEM(h->h_line); FREEMEM((char *)h); return; } GetFilenames(prompt, nargs, argc, argv) char *prompt; int nargs, *argc; addr *argv; { addr *tmpargv; char buf[LONG_BUF], *p; int c, iscomplete, indx, windex, i, d, quote_open; static struct list c_list; exprv[0] = buf; MustEraseTag = 0; *argc = 0; *line = '\0'; quote_open = windex = indx = 0; zerolist(&c_list); tmplistadd(&c_list); cbreak(); (void) signal(SIGTSTP, tstp_cleanup); (void) signal(SIGCONT, input_continue); (void) setjmp(in_continue); str_scr(prompt); while ((c = GET()) != '\n' && indx < BUFSIZ) { if (setjmp(in_continue) == SIGCONT) { redraw(prompt, line); continue; } if (c == '\r') break; switch ( c ) { case ' ': if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } if (windex != indx) { char_scr(c); line[indx] = c; line[indx+1] = '\0'; indx++; if (!quote_open) { windex = indx; (*argc)++; } } break; case '\t': case '\033': if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } p = pathcomplete(&line[windex], &c_list, &iscomplete); str_scr(p); if (iscomplete) { windex = strlen(line); (*argc)++; } else if (*p == '\0' && indx != windex) showtag("[Ambiguous]"); indx = strlen(line); break; /* * Ctrl-T transposes the two characters before the cursor */ case '\024': if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } if (indx < 2) break; if (indx == 2 && line[1] == ' ') break; swap(line[indx-1], line[indx-2]); /* * If one of the transposed characters was a space, * we have either broken a word in two or merged * the current word with the preceding one. */ if (line[indx-2] == ' ') if (!quote_open && line[indx-1] == '"') (*argc)++, windex--; else if (line[indx-1] == ' ') if (!quote_open && line[indx-2] == '"') (*argc)--, windex++; str_scr("\b \b\b \b"); str_scr(&line[indx-2]); break; /* * Show user possiblities if he wonders why * a word isn't completing. */ case '?': if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } str_scr("\r\n"); freelist(&c_list); dirscan(dirof(&line[windex]), &c_list); (void) strcpy(buf, "^"); (void) strcat(buf, fileof(&line[windex])); (void) strcat(buf, ".*"); nocbreak(); /* * We don't want completion info going to stdout * so we temporarily connect stdout to /dev/tty */ d = dup(1); (void) dup2(DevTty, 1); (void) showlist(&c_list, (addr *)exprv); /* * Now re-connect stdout to whatever is was before */ (void) dup2(d, 1); (void) close(d); cbreak(); redraw(prompt, line); break; /* * Ctrl-R simply reprints the command line. */ case '\022': redraw(prompt, line); break; /* * Ctrl-W is accepted as the word-kill character. */ case '\027': if (indx == windex && windex != 0) { for (windex -= 2; windex >= 0; windex--) { if (line[windex] == '"') { quote_open = !quote_open; continue; } if (!quote_open&& line[windex] == ' ') break; } windex++; if (windex < 0) windex = 0; decr(*argc); } for (; indx > windex; indx--) str_scr(BACKSPACE); line[indx] = '\0'; break; /* * Backspace (^H) and del are accepted as erase * characters, with the screen erasure being * accomplished via backspace-space-backpsace * sequences */ case '\177': case '\b': if ( --indx < windex ) { for (windex-=2; windex >= 0; windex--) { if (line[windex] == '"') { quote_open = !quote_open; continue; } if (!quote_open&& line[windex] == ' ') break; } windex++; if (windex < 0) windex = 0; decr(*argc); } if (indx >= 0) { if (line[indx] == '"') quote_open = !quote_open; line[indx] = '\0'; str_scr(BACKSPACE); } else indx = 0; break; /* * Ctrl-X and ctrl-U are accepted as line kill * characters. */ case '\030': case '\025': if (indx == 0) break; for (; indx>0; indx--) str_scr(BACKSPACE); quote_open = windex = indx = 0; *line = '\0'; *argc = 0; break; default: if (nargs && *argc == nargs) { showtag("[Press RETURN]"); break; } if (c == '"') quote_open = !quote_open; /* * Ignore unrecognized control characters */ if (isprint(c)) { char_scr(c); line[indx] = c; line[indx+1] = '\0'; indx++; } break; } } (void) signal(SIGCONT, sighandler SIG_DFL); (void) signal(SIGTSTP, sighandler SIG_DFL); if (quote_open) { char_scr('"'); line[indx++] = '"'; line[indx] = '\0'; } if (indx > windex) (*argc)++; (void) cut(line); tmpargv = mkargv(line, *argc); for (i=0; tmpargv[i]; i++) savestr((char **)&argv[i], (char *)tmpargv[i]); argv[i] = NIL; str_scr("\r\n"); nocbreak(); return; } char * pathcomplete(path, c_list, iscomplete) char *path; struct list *c_list; int *iscomplete; { static char delta[LONG_BUF]; char buf[LONG_BUF], *dir, *file; int n_matched, d_len, i; delta[0] = '\0'; (void) strcpy(buf, path); /* * If we can't chdir to the directory prefix of the path, then * we hack away parts of the path until we can or it's all * gone. Note that this depends on the fact within UNIX "\0" * is synonymous with "." */ dir = dirof(buf); if (chdir(dir) == -1) { diraxe(buf); while (*buf && chdir(buf) == -1) diraxe(buf); (void) getwd(buf); /* * If this isn't the root directory, add a slash */ if (!eq(buf, "/")) (void) strcat(buf, "/"); /* note changes and store necessary cursor motions */ n_matched = nmatch(path, buf); d_len = strlen(path); for (i=d_len; i > n_matched; i--) (void) strcat(delta, BACKSPACE); (void) strcat(delta, buf+n_matched); (void) strcpy(path, buf); (void) chdir(Working_Directory); *iscomplete = 0; return delta; } /* * Chdir'ed successfully so now we make the completion list. */ freelist(c_list); dirscan(".", c_list); /* do completion of the file suffix of the path */ file = fileof(path); (void) complete(file, c_list, iscomplete); /* * Shed . , .. and symlinks */ (void) getwd(buf); /* * If this isn't the root directory, add a slash */ if (!eq(buf, "/")) (void) strcat(buf, "/"); /* * Add the result of file name completion */ (void) strcat(buf, file); /* * Now note the difference between the what was passed in and * what we have now and put the necessary cursor movements into * delta[] */ n_matched = nmatch(path, buf); d_len = strlen(path); for (i=d_len; i > n_matched; i--) (void) strcat(delta, BACKSPACE); (void) strcat(delta, buf+n_matched); /* get rid of side effects */ (void) chdir(Working_Directory); /* save results */ (void) strcpy(path, buf); /* * If the filename completed was in fact a directory then we append * a slash to the file name and note that the completion didn't * result in a regular file name (*iscomplete = 0). */ if (*iscomplete) { d_len = strlen(path) - 1; path[d_len] = '/'; if (isdir(path)) { delta[strlen(delta)-1] = '/'; *iscomplete = 0; return delta; } path[d_len] = ' '; } return delta; } char * dirof(path) char *path; { register char *cp; static char buf[LONG_BUF]; (void) strcpy(buf, path); cp = rindex(buf, '/'); if (cp) *++cp = '\0'; else *buf = '\0'; return buf; } char * fileof(path) char *path; { register char *cp; static char buf[LONG_BUF]; (void) strcpy(buf, path); cp = rindex(buf, '/'); return cp ? cp+1 : buf; } /* * Hack off the last directory in a path. The path should end with '/' * for this to work properly. */ diraxe(path) char *path; { register char *cp; cp = rindex(path, '/'); if (cp) *cp = '\0'; else { *path = '\0'; return; } cp = rindex(path, '/'); if (cp) *++cp = '\0'; else *path = '\0'; return; }