sources-request@mirror.UUCP (02/07/87)
Submitted by: Kyle Jones <xanth!kyle> Mod.sources: Volume 8, Issue 45 Archive-name: mcp/Part05 [ OOPS! I should have pointed this out earlier: MCP is a for BSD Unix, but I don't think it will be two hard to convert it to other variants. --r$ ] #! /bin/sh # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # If all goes well, you will see the message "End of archive 5 (of 8)." # Contents: src/complete.c src/describe.c src/lastlog.h src/lists.h # src/pause.c # Wrapped by rs@mirror on Fri Feb 6 15:56:03 1987 PATH=/bin:/usr/bin:/usr/ucb; export PATH echo shar: extracting "'src/complete.c'" '(29213 characters)' if test -f 'src/complete.c' ; then echo shar: will not over-write existing file "'src/complete.c'" else sed 's/^X//' >src/complete.c <<'@//E*O*F src/complete.c//' X#include <stdio.h> X#include <sys/types.h> X#include <sys/time.h> X#include <signal.h> X#include <setjmp.h> X#include <strings.h> X#include <errno.h> X#include "sysdep.h" X#include "macros.h" X#include "mem.h" X#include "history.h" X#include "lists.h" X X#ifdef BSD4_3 Xchar *getwd(); X#endif X X#define swap(a, b) { char t; t=a; a=b; b=t; } X#define SCMPN(a, b) !strncmp((a), (b), strlen(a)) X Xextern jmp_buf in_continue; Xextern char Working_Directory[]; X Xchar *BACKSPACE = "\b \b", *dirof(), *fileof(), *pathcomplete(); Xstatic char line[BUFSIZ+1], hline[BUFSIZ+1]; X Xstruct hist currh = { hline, (char *)0, 0, 0, 0, 0, (struct list *)0 }; Xstruct list History; X Xstatic char *exprv[] = { 0, 0 }; X X#ifdef SENDMAIL Xextern struct list Aliases; X#endif X#ifdef HELPDIR Xextern struct list Terms; X#endif Xextern struct list AllCommands, Commands, Classes, Groups, Sigs; Xextern struct list Null_List, Users, Vigs, Ranges; Xextern int DevTty; X X/* X * Get the last part of a command. X */ Xchar * Xtail(s) Xchar *s; X X{ X char *p, *sp; X X sp = rindex(s, ' '); X if (!sp) { X p = rindex(s, '-'); X return p ? ++p : s; X } X else while (p = index(s, '-')) { X if (p > sp) break; X s = p + 1; X } X return s; X} X X/* X * Count how many characters in s1 until s1 differs with s2 X */ Xint nmatch(s1, s2) Xchar *s1, *s2; X X{ X register int i; X X for (i = 0; s1[i] && s2[i] && s1[i] == s2[i]; i++) X ; X return(i); X} X X/* X * Command and argument completion. The variable s will be changed to reflect X * what it was completed to. The variable *iscomplete will contain a 1 if s X * was expanded completely. X * X * One of these conditions will be satisfied and the apprpriate steps taken. X * X * 1) If c_list is empty return immediately. X * 2) If s is an empty string and c_list contains more than one string, X * return an empty string with no change. X * 3) If c_list only contains one string and s prefixes it then X * completely expand s to that string. X * 4) If s is exactly equal to one of the strings in c_list, merely X * report that s in completely expanded. X * 5) If s prefixes none of the strings in c_list then chop characters X * out of s until it will prefix at least one of the strings in c_list. X * 6) If s uniquely prefixes a string in c_list then completely expand X * s to that string. X * 7) If s prefixes more than one string in c_list expand s to the point X * where the strings differ. X * X * Note: the strings in c_list MUST ALREADY be sorted in collating X * sequence! X */ Xchar * Xcomplete(s, c_list, iscomplete) Xchar *s; Xstruct list *c_list; Xint *iscomplete; X X{ X static char delta[LONG_BUF * 2]; X int i, lindex, n_matched, s_len, found; X X delta[0] = '\0'; *iscomplete = 0; X /* X * Check for condition 1. If no completion list, forget it. X */ X if (c_list->l_count == 0) X return delta; X /* X * Check for condition 2. If s is empty and there is more than one X * word in the completion list, forget it. X */ X if (*s == '\0' && c_list->l_count > 1) X return delta; X /* X * Check for condition 3. If there is only one word in the X * completion list and s prefixes it, this is the one. X */ X s_len = strlen(s); X if (c_list->l_count==1&&!strncmp(s,(char *)c_list->l_list[0],s_len)) { X (void) strcpy(delta, &(((char *)c_list->l_list[0])[s_len])); X (void) strcat(delta, " "); X (void) strcat(s, delta); X *iscomplete = 1; X return delta; X } X /* X * Binary search the completion list. X * If s is not found, all the strings that s prefixes (if any) X * will have indices >= lindex . X */ X lindex = search_list(c_list, s, strcmp, &found); X /* X * Check for condition 4. If we got a perfect match, skidaddle. X */ X if (found) { X (void) strcpy(delta, " "); X (void) strcat(s, delta); X *iscomplete = 1; X return delta; X } X /* X * Check for condition 5. Hacksaw the garbage until we recognize X * something. X */ X n_matched = 0; X for (i=lindex-1; i < lindex+2; i++) { X if (i < 0) X continue; X if (i >= c_list->l_count) X break; X n_matched=max(n_matched, nmatch(s,(char *)c_list->l_list[i])); X /* X * If s prefixes c_list->l_list[lindex] or one of the words X * adjacent to it, we set lindex to its index so that lindex X * now indexes to the first word that s prefixes. X */ X if (n_matched == s_len) { X lindex = i; X break; X } X } X if (n_matched < s_len) { X for (i=0; i < s_len - n_matched; i++) X (void) strcat(delta, BACKSPACE); X s[n_matched] = '\0'; X return delta; X } X /* X * Check for condition 6. If s can be unambigously expanded X * then do so. X */ X if (lindex+1 == c_list->l_count || X strncmp((char *)c_list->l_list[lindex], X (char *)c_list->l_list[lindex+1], s_len)) { X (void) strcpy(delta, ((char *)c_list->l_list[lindex])+s_len); X (void) strcat(delta, " "); X (void) strcat(s, delta); X *iscomplete = 1; X return delta; X } X /* X * Gotta be condition 7. Expand as far as possible, but X * don't set *iscomplete. X */ X for (i=lindex+1 ;; i++) { X if (i == c_list->l_count) { X break; X } X if (strncmp(s, (char *)c_list->l_list[i], s_len) != 0) X break; X } X i--; X n_matched = nmatch((char *)c_list->l_list[lindex], X (char *)c_list->l_list[i]); X (void) strncpy(delta, ((char *)c_list->l_list[lindex])+s_len, X n_matched-s_len); X delta[n_matched - s_len] = '\0'; X (void) strcat(s, delta); X return delta; X} X Xredraw(prompt, s) Xchar *prompt, *s; X X{ X char_scr('\r'); X str_scr(prompt); X str_scr(s); X return; X} X X#include <ctype.h> X X/* X * If this variable is non-zero then it must contain the length of X * the latest visible completion help tag, e.g. "[Ambiguous]". X * If zero then there is no floating completion tag at this time. X */ Xstatic int MustEraseTag; X Xchar XGET() X X{ X extern int errno; X int count, nready, rmask = 1; X struct timeval t, *timeout = 0; X char c; X X errno = 0; X for (;;) { X rmask = 1; X if (MustEraseTag) { X t.tv_sec = 1; X t.tv_usec = 250000; X timeout = &t; X } X#ifdef BSD4_3 X nready = select(1, (fd_set*)&rmask, (fd_set *)0, (fd_set *)0, timeout); X#else X nready = select(1, &rmask, 0, 0, timeout); X#endif X if (nready == -1 && errno != EINTR) { X perr("select"); X panic((char *)0); X } X if (nready == 0 || MustEraseTag) { X erasetag(); X timeout = 0; X if (!nready) X continue; X } X count = read(0, &c, 1); X if (count == 1) X break; X if (count == 0) X panic("EOF encountered"); X if (count == -1 && errno != EINTR) { X perr("read"); X panic((char *)0); X } X } X c &= 0177; X return(c); X} X Xshowtag(tag) Xchar *tag; X X{ X register int i; X X MustEraseTag = strlen(tag) + 1; X char_scr(' '); X str_scr(tag); X for (i=0; i < MustEraseTag; i++) X char_scr('\b'); X return; X} X Xerasetag() X X{ X register int i; X X for (i=0; i < MustEraseTag; i++) X char_scr(' '); X for (i=0; i < MustEraseTag; i++) X char_scr('\b'); X MustEraseTag = 0; X return; X} X X/* X * Figure out which argument vector to use for argument completion. X */ Xstruct list * Xpicklist(cmd) Xchar *cmd; X X{ X cmd = tail(cmd); X if (SCMPN("user", cmd)) X return(&Users); X else if (SCMPN("sig", cmd)) X return(&Sigs); X else if (SCMPN("class", cmd)) X return(&Classes); X else if (SCMPN("group", cmd)) X return(&Groups); X else if (SCMPN("range", cmd)) X return(&Ranges); X else if (SCMPN("vig", cmd)) X return(&Vigs); X else if (SCMPN("command", cmd)) X return(&AllCommands); X#ifdef SENDMAIL X else if (SCMPN("alias", cmd)) X return(&Aliases); X#endif X#ifdef HELPDIR X else if (SCMPN("is", cmd)) X return(&Terms); X#endif X else X return(&Null_List); X} X X#ifdef sun X#define sighandler (void (*)()) X#else X#define sighandler (int (*)()) X#endif X X#ifdef sun Xvoid X#endif Xtstp_cleanup() X X{ X char_scr('\r'); X nocbreak(); X (void) signal(SIGTSTP, sighandler SIG_DFL); X (void) kill(getpid(), SIGTSTP); X return; X} X X#ifdef sun Xvoid X#endif Xinput_continue() X X{ X cbreak(); X (void) signal(SIGTSTP, tstp_cleanup); X longjmp(in_continue, SIGCONT); X} X X/* X * Get a line of input using command and smart argument completion. X */ XGetCommandLine(prompt, nargs, argc, argv) Xchar *prompt; Xint nargs, *argc; Xaddr *argv; X X{ X addr *tmpargv; X char buf[LONG_BUF], *p; X int c, iscomplete, indx, windex, hindex, i, end_of_line, h_len, d; X int quote_open; X struct list *c_list; X struct hist *hi, hh; X X cbreak(); X MustEraseTag = 0; X exprv[0] = buf; X *argc = 0; X *line = '\0'; X quote_open = end_of_line = windex = indx = 0; X hindex = History.l_count; X c_list = &Commands; X (void) signal(SIGTSTP, tstp_cleanup); X (void) signal(SIGCONT, input_continue); X (void) setjmp(in_continue); X str_scr(prompt); X while (!end_of_line && indx < BUFSIZ) { X c = GET(); X if (setjmp(in_continue) == SIGCONT) { X redraw(prompt, line); X continue; X } X switch (c) { X case ':': X break; /* ignore colons for pwd, group, etc. files */ X /* X * ^P goes one step back in the history list. X */ X case '\020': X if (hindex == 0) { X showtag("[History begins here...]"); X break; X } X if (hindex == History.l_count) { X (void) strcpy(currh.h_line, line); X currh.h_prompt = prompt; X currh.h_argc = *argc; X currh.h_index = indx; X currh.h_windex = windex; X currh.h_qopen = quote_open; X currh.h_list = c_list; X } X hi = (struct hist *) History.l_list[--hindex]; X h_len = strlen(line) + strlen(prompt); X for (i=strlen(hi->h_line); i < h_len; i++) X str_scr(BACKSPACE); X (void) strcpy(line, hi->h_line); X prompt = hi->h_prompt; X *argc = hi->h_argc; X indx = hi->h_index; X windex = hi->h_windex; X quote_open = hi->h_qopen; X c_list = hi->h_list; X redraw(prompt, line); X break; X /* X * ^N goes one step forward in the history list. X */ X case '\016': X if (!History.l_count || hindex == History.l_count) { X showtag("[End of history]"); X break; X } X if (hindex == History.l_count-1) { X h_len = strlen(line) + strlen(prompt); X i=strlen(currh.h_line)+strlen(currh.h_prompt); X for (; i < h_len; i++) X str_scr(BACKSPACE); X (void) strcpy(line, currh.h_line); X prompt = currh.h_prompt; X *argc = currh.h_argc; X indx = currh.h_index; X windex = currh.h_windex; X quote_open = currh.h_qopen; X c_list = currh.h_list; X redraw(prompt, line); X hindex++; X break; X } X hi = (struct hist *) History.l_list[++hindex]; X h_len = strlen(line) + strlen(prompt); X i = strlen(hi->h_line) + strlen(hi->h_prompt); X for (; i < h_len; i++) X str_scr(BACKSPACE); X (void) strcpy(line, hi->h_line); X prompt = hi->h_prompt; X *argc = hi->h_argc; X indx = hi->h_index; X windex = hi->h_windex; X quote_open = hi->h_qopen; X c_list = hi->h_list; X redraw(prompt, line); X break; X /* X * For space bar, do completion only for the X * command (first) word. Allow normal spaces X * only at the end of a word. X */ X case ' ': X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X if (windex != 0 && windex != indx) { X char_scr(c); X line[indx] = c; X line[indx + 1] = '\0'; X indx++; X if (!quote_open) { X windex = indx; X (*argc)++; X } X break; X } X else if (windex == indx) /* balk at adjacent spaces */ X break; X else X /* FALL THROUGH */ X ; X /* X * Word is completion done here. Completion is activated X * by the TAB or the ESC key. Activation by CR or LF only X * if completing first word. X */ X case '\r': X case '\n': X if (*argc != 0) { X end_of_line++; X break; X } X if (nargs && *argc == nargs) { X end_of_line++; X break; X } X /* FALL THROUGH */ X case '\t': X case '\033': X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X p = complete(&line[windex], c_list, &iscomplete); X str_scr(p); X if (iscomplete) { X if (windex == 0) X c_list = picklist(line); X windex = strlen(line); X (*argc)++; X } X else if (*p == '\0' && indx != windex) X showtag("[Ambiguous]"); X indx = strlen(line); X if ((c == '\r' || c == '\n') && iscomplete) X end_of_line++; X break; X /* X * Ctrl-T transposes the two characters before the cursor X */ X case '\024': X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X /* not enough characters */ X if (indx < 2) X break; X /* can't let space to be first character on a line */ X if (indx == 2 && line[1] == ' ') X break; X swap(line[indx-1], line[indx-2]); X /* X * If one of the transposed characters was a space, X * we have either broken a word in two or merged X * the current word with the preceding one. X */ X if (line[indx-2] == ' ') X if (!quote_open && line[indx-1] == '"') { X (*argc)++, windex--; X c_list = picklist(line); X } X else if (line[indx-1] == ' ') X if (!quote_open && line[indx-2] == '"') { X (*argc)--, windex++; X c_list = (*argc == 0) ? X &Commands : picklist(line); X } X str_scr("\b \b\b \b"); X str_scr(&line[indx-2]); X break; X /* X * Show user possiblities if he wonders why X * a word isn't completing. X */ X case '?': X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X str_scr("\r\n"); X (void) strcpy(buf, "^"); X (void) strcat(buf, &line[windex]); X (void) strcat(buf, ".*"); X nocbreak(); X /* X * We don't want completion info going to stdout X * so we temporarily connect stdout to /dev/tty X */ X d = dup(1); X (void) dup2(DevTty, 1); X (void) showlist(c_list, (addr *)exprv); X /* X * Now re-connect stdout to whatever is was before X */ X (void) dup2(d, 1); X (void) close(d); X X cbreak(); X redraw(prompt, line); X break; X /* X * Ctrl-R simply reprints the command line. X */ X case '\022': X redraw(prompt, line); X break; X /* X * Ctrl-W is accepted as the word-kill character. X */ X case '\027': X if (indx == windex && windex != 0) { X for (windex -= 2; windex >= 0; windex--) { X if (line[windex] == '"') { X quote_open = !quote_open; X continue; X } X if (!quote_open&& line[windex] == ' ') X break; X } X windex++; X if (windex < 0) X windex = 0; X decr(*argc); X } X if (windex == 0) X c_list = &Commands; X for (; indx > windex; indx--) X str_scr(BACKSPACE); X line[indx] = '\0'; X break; X /* X * Backspace (^H) and del are accepted as erase X * characters, with the screen eraseure being X * accomplished via backspace-space-backpsace X * sequences X */ X case '\177': X case '\b': X if (--indx < windex) { X for (windex-=2; windex >= 0; windex--) { X if (line[windex] == '"') { X quote_open = !quote_open; X continue; X } X if (!quote_open&& line[windex] == ' ') X break; X } X windex++; X if (windex < 0) X windex = 0; X decr(*argc); X } X if (windex == 0) X c_list = &Commands; X if (indx >= 0) { X if (line[indx] == '"') X quote_open = !quote_open; X line[indx] = '\0'; X str_scr(BACKSPACE); X } X else X indx = 0; X break; X /* X * Ctrl-X and ctrl-U are accepted as line kill X * characters. X */ X case '\030': X case '\025': X if (indx == 0) X break; X for (; indx>0; indx--) X str_scr(BACKSPACE); X quote_open = windex = indx = 0; X *line = '\0'; X c_list = &Commands; X *argc = 0; X break; X default: X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X if (c == '"') X quote_open = !quote_open; X /* X * Ignore unrecognized control characters X */ X if (isprint(c)) { X char_scr(c); X line[indx] = c; X line[indx + 1] = '\0'; X indx++; X } X break; X } X } X (void) signal(SIGCONT, sighandler SIG_DFL); X (void) signal(SIGTSTP, sighandler SIG_DFL); X if (quote_open) { X char_scr('"'); X line[indx++] = '"'; X line[indx] = '\0'; X quote_open = 0; X } X X critical(); X X savestr(&hh.h_prompt, prompt); X savestr(&hh.h_line, line); X hh.h_argc = *argc; X hh.h_index = indx; X hh.h_windex = windex; X hh.h_qopen = quote_open; X hh.h_list = c_list; X trimhist(); X genlistadd(&History, (addr)&hh, sizeof (struct hist)); X X non_critical(); X X if (indx > windex) X (*argc)++; X (void) cut(line); X tmpargv = mkargv(line, *argc); X for (i=0; tmpargv[i]; i++) X savestr((char **)&argv[i], (char *)tmpargv[i]); X argv[i] = NIL; X str_scr("\r\n"); X nocbreak(); X return; X} X XGetLine(prompt, nargs, argc, argv, c_list) Xchar *prompt; Xint nargs, *argc; Xaddr *argv; Xstruct list *c_list; X X{ X addr *tmpargv; X char buf[LONG_BUF], *p; X int c, iscomplete, indx, windex, i, d, quote_open; X X cbreak(); X exprv[0] = buf; X MustEraseTag = 0; X *argc = quote_open = 0; X *line = '\0'; X windex = indx = 0; X (void) signal(SIGTSTP, tstp_cleanup); X (void) signal(SIGCONT, input_continue); X (void) setjmp(in_continue); X str_scr(prompt); X while ((c = GET()) != '\n' && indx < BUFSIZ) { X if (setjmp(in_continue) == SIGCONT) { X redraw(prompt, line); X continue; X } X if (c == '\r') X break; X switch ( c ) { X case ':': X break; /* ignore colons for pwd, group, etc. files */ X case ' ': X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X if (windex != indx) { X char_scr(c); X line[indx] = c; X line[indx+1] = '\0'; X indx++; X if (!quote_open) { X windex = indx; X (*argc)++; X } X } X break; X case '\t': X case '\033': X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X p = complete(&line[windex], c_list, &iscomplete); X str_scr(p); X if (iscomplete) { X windex = strlen(line); X (*argc)++; X } X else if (*p == '\b') X showtag("[No match]"); X else if (*p == '\0' && indx != windex) X showtag("[Ambiguous]"); X indx = strlen(line); X break; X /* X * Ctrl-T transposes the two characters before the cursor X */ X case '\024': X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X if (indx < 2) X break; X if (indx == 2 && line[1] == ' ') X break; X swap(line[indx-1], line[indx-2]); X /* X * If one of the transposed characters was a space, X * we have either broken a word in two or merged X * the current word with the preceding one. X */ X if (line[indx-2] == ' ') X if (!quote_open && line[indx-1] == '"') X (*argc)++, windex--; X else if (line[indx-1] == ' ') X if (!quote_open && line[indx-2] == '"') X (*argc)--, windex++; X str_scr("\b \b\b \b"); X str_scr(&line[indx-2]); X break; X /* X * Show user possiblities if he wonders why X * a word isn't completing. X */ X case '?': X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X str_scr("\r\n"); X (void) strcpy(buf, "^"); X (void) strcat(buf, &line[windex]); X (void) strcat(buf, ".*"); X nocbreak(); X /* X * We don't want completion info going to stdout X * so we temporarily connect stdout to /dev/tty X */ X d = dup(1); X (void) dup2(DevTty, 1); X (void) showlist(c_list, (addr *)exprv); X /* X * Now re-connect stdout to whatever is was before X */ X (void) dup2(d, 1); X (void) close(d); X X cbreak(); X redraw(prompt, line); X break; X /* X * Ctrl-R simply reprints the command line. X */ X case '\022': X redraw(prompt, line); X break; X /* X * Ctrl-W is accepted as the word-kill character. X */ X case '\027': X if (indx == windex && windex != 0) { X for (windex -= 2; windex >= 0; windex--) { X if (line[windex] == '"') { X quote_open = !quote_open; X continue; X } X if (!quote_open&& line[windex] == ' ') X break; X } X windex++; X if (windex < 0) X windex = 0; X decr(*argc); X } X for (; indx > windex; indx--) X str_scr(BACKSPACE); X line[indx] = '\0'; X break; X /* X * Backspace (^H) and del are accepted as erase X * characters, with the screen erasure being X * accomplished via backspace-space-backpsace X * sequences X */ X case '\177': X case '\b': X if ( --indx < windex ) { X for (windex-=2; windex >= 0; windex--) { X if (line[windex] == '"') { X quote_open = !quote_open; X continue; X } X if (!quote_open&& line[windex] == ' ') X break; X } X windex++; X if (windex < 0) X windex = 0; X decr(*argc); X } X if (indx >= 0) { X if (line[indx] == '"') X quote_open = !quote_open; X line[indx] = '\0'; X str_scr(BACKSPACE); X } X else X indx = 0; X break; X /* X * Ctrl-X and ctrl-U are accepted as line kill X * characters. X */ X case '\030': X case '\025': X if (indx == 0) X break; X for (; indx>0; indx--) X str_scr(BACKSPACE); X quote_open = windex = indx = 0; X *line = '\0'; X *argc = 0; X break; X default: X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X if (c == '"') X quote_open = !quote_open; X /* X * Ignore unrecognized control characters X */ X if (isprint(c)) { X char_scr(c); X line[indx] = c; X line[indx+1] = '\0'; X indx++; X } X break; X } X } X (void) signal(SIGCONT, sighandler SIG_DFL); X (void) signal(SIGTSTP, sighandler SIG_DFL); X if (quote_open) { X char_scr('"'); X line[indx++] = '"'; X line[indx] = '\0'; X } X if (indx > windex) X (*argc)++; X (void) cut(line); X tmpargv = mkargv(line, *argc); X for (i=0; tmpargv[i]; i++) X savestr((char **)&argv[i], (char *)tmpargv[i]); X argv[i] = NIL; X str_scr("\r\n"); X nocbreak(); X return; X} X Xtrimhist() X X{ X struct hist *h; X X if (History.l_count <= MAXHIST) return; X h = (struct hist *) listpop(&History); X FREEMEM(h->h_line); X FREEMEM((char *)h); X return; X} X XGetFilenames(prompt, nargs, argc, argv) Xchar *prompt; Xint nargs, *argc; Xaddr *argv; X X{ X addr *tmpargv; X char buf[LONG_BUF], *p; X int c, iscomplete, indx, windex, i, d, quote_open; X static struct list c_list; X X exprv[0] = buf; X MustEraseTag = 0; X *argc = 0; X *line = '\0'; X quote_open = windex = indx = 0; X zerolist(&c_list); X tmplistadd(&c_list); X cbreak(); X (void) signal(SIGTSTP, tstp_cleanup); X (void) signal(SIGCONT, input_continue); X (void) setjmp(in_continue); X str_scr(prompt); X while ((c = GET()) != '\n' && indx < BUFSIZ) { X if (setjmp(in_continue) == SIGCONT) { X redraw(prompt, line); X continue; X } X if (c == '\r') X break; X switch ( c ) { X case ' ': X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X if (windex != indx) { X char_scr(c); X line[indx] = c; X line[indx+1] = '\0'; X indx++; X if (!quote_open) { X windex = indx; X (*argc)++; X } X } X break; X case '\t': X case '\033': X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X p = pathcomplete(&line[windex], &c_list, &iscomplete); X str_scr(p); X if (iscomplete) { X windex = strlen(line); X (*argc)++; X } X else if (*p == '\0' && indx != windex) X showtag("[Ambiguous]"); X indx = strlen(line); X break; X /* X * Ctrl-T transposes the two characters before the cursor X */ X case '\024': X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X if (indx < 2) X break; X if (indx == 2 && line[1] == ' ') X break; X swap(line[indx-1], line[indx-2]); X /* X * If one of the transposed characters was a space, X * we have either broken a word in two or merged X * the current word with the preceding one. X */ X if (line[indx-2] == ' ') X if (!quote_open && line[indx-1] == '"') X (*argc)++, windex--; X else if (line[indx-1] == ' ') X if (!quote_open && line[indx-2] == '"') X (*argc)--, windex++; X str_scr("\b \b\b \b"); X str_scr(&line[indx-2]); X break; X /* X * Show user possiblities if he wonders why X * a word isn't completing. X */ X case '?': X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X str_scr("\r\n"); X freelist(&c_list); X dirscan(dirof(&line[windex]), &c_list); X (void) strcpy(buf, "^"); X (void) strcat(buf, fileof(&line[windex])); X (void) strcat(buf, ".*"); X nocbreak(); X /* X * We don't want completion info going to stdout X * so we temporarily connect stdout to /dev/tty X */ X d = dup(1); X (void) dup2(DevTty, 1); X (void) showlist(&c_list, (addr *)exprv); X /* X * Now re-connect stdout to whatever is was before X */ X (void) dup2(d, 1); X (void) close(d); X X cbreak(); X redraw(prompt, line); X break; X /* X * Ctrl-R simply reprints the command line. X */ X case '\022': X redraw(prompt, line); X break; X /* X * Ctrl-W is accepted as the word-kill character. X */ X case '\027': X if (indx == windex && windex != 0) { X for (windex -= 2; windex >= 0; windex--) { X if (line[windex] == '"') { X quote_open = !quote_open; X continue; X } X if (!quote_open&& line[windex] == ' ') X break; X } X windex++; X if (windex < 0) X windex = 0; X decr(*argc); X } X for (; indx > windex; indx--) X str_scr(BACKSPACE); X line[indx] = '\0'; X break; X /* X * Backspace (^H) and del are accepted as erase X * characters, with the screen erasure being X * accomplished via backspace-space-backpsace X * sequences X */ X case '\177': X case '\b': X if ( --indx < windex ) { X for (windex-=2; windex >= 0; windex--) { X if (line[windex] == '"') { X quote_open = !quote_open; X continue; X } X if (!quote_open&& line[windex] == ' ') X break; X } X windex++; X if (windex < 0) X windex = 0; X decr(*argc); X } X if (indx >= 0) { X if (line[indx] == '"') X quote_open = !quote_open; X line[indx] = '\0'; X str_scr(BACKSPACE); X } X else X indx = 0; X break; X /* X * Ctrl-X and ctrl-U are accepted as line kill X * characters. X */ X case '\030': X case '\025': X if (indx == 0) X break; X for (; indx>0; indx--) X str_scr(BACKSPACE); X quote_open = windex = indx = 0; X *line = '\0'; X *argc = 0; X break; X default: X if (nargs && *argc == nargs) { X showtag("[Press RETURN]"); X break; X } X if (c == '"') X quote_open = !quote_open; X /* X * Ignore unrecognized control characters X */ X if (isprint(c)) { X char_scr(c); X line[indx] = c; X line[indx+1] = '\0'; X indx++; X } X break; X } X } X (void) signal(SIGCONT, sighandler SIG_DFL); X (void) signal(SIGTSTP, sighandler SIG_DFL); X if (quote_open) { X char_scr('"'); X line[indx++] = '"'; X line[indx] = '\0'; X } X if (indx > windex) X (*argc)++; X (void) cut(line); X tmpargv = mkargv(line, *argc); X for (i=0; tmpargv[i]; i++) X savestr((char **)&argv[i], (char *)tmpargv[i]); X argv[i] = NIL; X str_scr("\r\n"); X nocbreak(); X return; X} X Xchar * Xpathcomplete(path, c_list, iscomplete) Xchar *path; Xstruct list *c_list; Xint *iscomplete; X X{ X static char delta[LONG_BUF]; X char buf[LONG_BUF], *dir, *file; X int n_matched, d_len, i; X X delta[0] = '\0'; X (void) strcpy(buf, path); X X /* X * If we can't chdir to the directory prefix of the path, then X * we hack away parts of the path until we can or it's all X * gone. Note that this depends on the fact within UNIX "\0" X * is synonymous with "." X */ X dir = dirof(buf); X if (chdir(dir) == -1) { X diraxe(buf); X while (*buf && chdir(buf) == -1) X diraxe(buf); X (void) getwd(buf); X /* X * If this isn't the root directory, add a slash X */ X if (!eq(buf, "/")) X (void) strcat(buf, "/"); X /* note changes and store necessary cursor motions */ X n_matched = nmatch(path, buf); X d_len = strlen(path); X for (i=d_len; i > n_matched; i--) X (void) strcat(delta, BACKSPACE); X (void) strcat(delta, buf+n_matched); X (void) strcpy(path, buf); X (void) chdir(Working_Directory); X *iscomplete = 0; X return delta; X } X /* X * Chdir'ed successfully so now we make the completion list. X */ X freelist(c_list); X dirscan(".", c_list); X /* do completion of the file suffix of the path */ X file = fileof(path); X (void) complete(file, c_list, iscomplete); X /* X * Shed . , .. and symlinks X */ X (void) getwd(buf); X /* X * If this isn't the root directory, add a slash X */ X if (!eq(buf, "/")) X (void) strcat(buf, "/"); X /* X * Add the result of file name completion X */ X (void) strcat(buf, file); X /* X * Now note the difference between the what was passed in and X * what we have now and put the necessary cursor movements into X * delta[] X */ X n_matched = nmatch(path, buf); X d_len = strlen(path); X for (i=d_len; i > n_matched; i--) X (void) strcat(delta, BACKSPACE); X (void) strcat(delta, buf+n_matched); X /* get rid of side effects */ X (void) chdir(Working_Directory); X /* save results */ X (void) strcpy(path, buf); X /* X * If the filename completed was in fact a directory then we append X * a slash to the file name and note that the completion didn't X * result in a regular file name (*iscomplete = 0). X */ X if (*iscomplete) { X d_len = strlen(path) - 1; X path[d_len] = '/'; X if (isdir(path)) { X delta[strlen(delta)-1] = '/'; X *iscomplete = 0; X return delta; X } X path[d_len] = ' '; X } X return delta; X} X Xchar * Xdirof(path) Xchar *path; X X{ X register char *cp; X static char buf[LONG_BUF]; X X (void) strcpy(buf, path); X cp = rindex(buf, '/'); X if (cp) X *++cp = '\0'; X else X *buf = '\0'; X return buf; X} X Xchar * Xfileof(path) Xchar *path; X X{ X register char *cp; X static char buf[LONG_BUF]; X X (void) strcpy(buf, path); X cp = rindex(buf, '/'); X return cp ? cp+1 : buf; X} X X/* X * Hack off the last directory in a path. The path should end with '/' X * for this to work properly. X */ Xdiraxe(path) Xchar *path; X X{ X register char *cp; X X cp = rindex(path, '/'); X if (cp) X *cp = '\0'; X else { X *path = '\0'; X return; X } X cp = rindex(path, '/'); X if (cp) X *++cp = '\0'; X else X *path = '\0'; X return; X} @//E*O*F src/complete.c// if test 29213 -ne "`wc -c <'src/complete.c'`"; then echo shar: error transmitting "'src/complete.c'" '(should have been 29213 characters)' fi fi # end of overwriting check echo shar: extracting "'src/describe.c'" '(14405 characters)' if test -f 'src/describe.c' ; then echo shar: will not over-write existing file "'src/describe.c'" else sed 's/^X//' >src/describe.c <<'@//E*O*F src/describe.c//' X#include <stdio.h> X#include <strings.h> X#include <sys/types.h> X#include <sys/file.h> X#include <sys/stat.h> X#include <lastlog.h> X#include "sysdep.h" X#include "macros.h" X#include "mem.h" X#include "lists.h" X#include "job.h" X#include "account.h" X#ifdef SENDMAIL X#include "alias.h" X#endif X#include "class.h" X#include "groupmap.h" X#include "range.h" X#include "sig.h" X#include "save.h" X#include "sort.h" X X#ifdef BSD4_3 Xtime_t time(); X#endif X#ifdef HELPDIR Xextern struct list AllCommands, Terms; X#endif X#ifdef SENDMAIL Xextern struct list AliasList; X#endif Xextern struct list AccountList, Jobs, RangeList; Xextern int ModBits; X Xchar *when(), *sprintf(); X X#ifdef SENDMAIL Xdesalias(c, v) Xint c; Xaddr *v; X X{ X struct alias *al; X static char *allv[2] = { ".*", 0 }; X X if (c > 2) { X err1("%s: too many arguments", (char *)v[0]); X return; X } X if (c < 2) { X err1("usage: %s <alias>", (char *)v[0]); X return; X } X al = getalnam((char *)v[1]); X if (!al) { X err1("%s: no such alias", (char *)v[1]); X return; X } X X (void) printf("Name: %s\n", al->al_name); X if (al->al_groups.l_count) { X (void) printf("Bound to group%-2s: ", X S(al->al_groups.l_count)); X listlist(&al->al_groups); X } X if (al->al_classes.l_count) { X (void) printf("Bound to class%-2s: ", ES(al->al_classes.l_count)); X listlist(&al->al_classes); X } X if (al->al_sigs.l_count) { X (void) printf("Bound to sig%-4s: ", X S(al->al_sigs.l_count)); X listlist(&al->al_sigs); X } X if (al->al_addresses.l_count) { X puts("\t- Addressees -"); X (void) showlist(&al->al_addresses, (addr *)allv); X (void) printf("%d addressee%s\n", al->al_addresses.l_count, X S(al->al_addresses.l_count)); X } X else X puts("No addressees."); X return; X} X#endif X Xdeschanges(c, v) Xint c; Xaddr *v; X X{ X struct job *jb; X char errmsg[LONG_BUF]; X int first, indx; X X first = (c > 1 ? Jobs.l_count - atoi((char *)v[1]) : 0); X for (indx=first; indx < Jobs.l_count; indx++) { X jb = (struct job *) Jobs.l_list[indx]; X (void) printf("%3d ", indx+1); X switch (jb->jb_todo) { X case JB_LASTLOG: X (void) printf("update lastlog entry for uid %d\n", X jb->jb_uid); X break; X case JB_MKDIR: X (void) printf("mkdir %s\n", jb->jb_name); X break; X case JB_MV: X (void) printf("rename %s to %s\n", jb->jb_oldname, X jb->jb_name); X break; X case JB_RMMAIL: X (void) printf("remove mail for user \"%s\"\n", X jb->jb_name); X break; X case JB_OMNICHOWN: X (void) printf("omnichown uid %d's files to uid %d\n", X jb->jb_olduid, jb->jb_uid); X break; X case JB_RMDIR: X (void) printf("remove directory %s\n", jb->jb_name); X break; X default: X (void) sprintf(errmsg, X "internal error: unknown todo (%d)\n", X jb->jb_todo); X err(errmsg); X break; X } X } X if (ModBits) { X fputs("Files modified:", stdout); X (ModBits&AC) && fputs(" account", stdout); X#ifdef SENDMAIL X (ModBits&AL) && fputs(" alias", stdout); X#endif X (ModBits&CS) && fputs(" class", stdout); X (ModBits&GR) && fputs(" group", stdout); X (ModBits&PW) && fputs(" passwd", stdout); X (ModBits&RG) && fputs(" range", stdout); X (ModBits&SG) && fputs(" sig", stdout); X (ModBits&VG) && fputs(" vig", stdout); X puts(""); X } X} X Xdesclass(c, v) Xint c; Xaddr *v; X X{ X struct class *cs; X struct account *ac; X int indx, members = 0; X X if ( c > 2 ) { X err1("%s: too many arguments", (char *)v[0]); X return; X } X if ( c != 2 ) { X err1("usage: %s <class>", (char *)v[0]); X return; X } X cs = getcsnam((char *)v[1]); X if (!cs) { X err1("%s: no such class", (char *)v[1]); X return; X } X (void) printf("Class: %s\n", cs->cs_name); X if (cs->cs_exptime) (void) printf("Ends: %s\n", when(cs->cs_exptime)); X#ifdef SENDMAIL X if (cs->cs_aliases.l_count) { X (void) printf("Bound to alias%s: ", ES(cs->cs_aliases.l_count)); X listlist(&cs->cs_aliases); X } X#endif X puts((char *)cs->cs_desc); X for (indx=0; indx < AccountList.l_count; indx++) { X ac = (struct account *) AccountList.l_list[indx]; X if (!instrlist(&ac->ac_classes, cs->cs_name)) X continue; X (void) printf("%-10s%-40s%3d", ac->ac_name, X ac->ac_realname, X ac->ac_uid); X if (ac->ac_ll.ll_time) X puts(" *"); X else X puts(""); X members++; X } X if (members) X (void) printf("\n%d member%s.\n", members, S(members)); X else X puts("No current members."); X return; X} X Xdescryos(c, v) Xint c; Xchar **v; X X{ X struct account *ac; X int indx, cryos = 0; X X if ( c > 1 ) { X err1("%s: too many arguments", (char *)v[0]); X return; X } X for (indx=0; indx < AccountList.l_count; indx++) { X ac = (struct account *) AccountList.l_list[indx]; X if (!eq(ac->ac_shell, FREEZE_SH)) X continue; X (void) printf("%-10s%-40s%3d", ac->ac_name, X ac->ac_realname, X ac->ac_uid); X if (ac->ac_ll.ll_time) X puts(" *"); X else X puts(""); X cryos++; X } X if (cryos) X (void) printf("\n%d cryo%s.\n", cryos, S(cryos)); X else X puts("No cryos."); X return; X} X X#ifdef HELPDIR Xchar *getenv(); X Xdescommand(c, v) Xint c; Xaddr *v; X X{ X char *pager = getenv("PAGER"); X char *pname, helpfile[MEDIUM_BUF]; X char *av[4]; X struct stat statbuf; X X if ( c > 2 ) { X err1("%s: too many arguments", (char *)v[0]); X return; X } X if ( c < 2 ) { X err1("usage: %s <mcp command>", (char *)v[0]); X return; X } X if (!instrlist(&AllCommands, (char *)v[1])) { X err2("%s: %s is not an mcp command", X (char *)v[0], (char *)v[1]); X return; X } X if (chdir(HELPDIR) == -1) { X perr(HELPDIR); X return; X } X (void) strcpy(helpfile, (char *)v[1]); X (void) strcat(helpfile, ".k"); X if (stat(helpfile, &statbuf) == -1) { X err1("No help available for \"%s\"", (char *)v[1]); X return; X } X if (statbuf.st_size == 0) { X err1("Help file for \"%s\" is empty, (oh, well)", X (char *)v[1]); X return; X } X if (!pager) X pager = DEF_PAGER; X pname = rindex(pager, '/') + 1; X pname = (pname ? pname : pager); X X av[0] = "shell-escape"; /* not really necessary */ X av[1] = pager; X av[2] = helpfile; X av[3] = (char *)0; X (void) shellescape(3, (addr *)av); X return; X} X#endif X Xdesdeadbeats(c, v) Xint c; Xchar **v; X X{ X struct account *ac; X struct groupmap *gm; X int indx, deadbeats = 0; X char errmsg[LONG_BUF]; X X if ( c > 1 ) { X err1("%s: too many arguments", (char *)v[0]); X return; X } X for (indx=0; indx < AccountList.l_count; indx++) { X ac = (struct account *) AccountList.l_list[indx]; X if (ac->ac_classes.l_count) X continue; X if (ac->ac_sigs.l_count) X continue; X /* X * Cryos are not deadbeats. X */ X if (eq(ac->ac_shell, FREEZE_SH)) X continue; X gm = getgmgid(ac->ac_gid); X if (!gm) { X (void) sprintf(errmsg, X "no group for gid %d!", X ac->ac_gid); X err(errmsg); X return; X } X if (vigexists(gm->gm_name)) X continue; X (void) printf("%-10s%-40s%3d", ac->ac_name, X ac->ac_realname, X ac->ac_uid); X if (ac->ac_ll.ll_time) X puts(" *"); X else X puts(""); X deadbeats++; X } X if (deadbeats) X (void) printf("\n%d deadbeat%s.\n", X deadbeats, S(deadbeats)); X else X puts("No deadbeats."); X return; X} X Xdesgroup(c, v) Xint c; Xaddr *v; X X{ X struct groupmap *gm; X struct range *rg; X struct account *ac; X static struct list members; X static char *allv[2] = { ".*", 0 }; X int indx, vig = 0; X X if ( c > 2 ) { X err1("%s: too many arguments", (char *)v[0]); X return; X } X if (c != 2) { X err1("usage: %s <group>", (char *)v[0]); X return; X } X gm = getgmnam((char *)v[1]); X if (!gm) { X err1("%s: no such group", (char *)v[1]); X return; X } X rg = getrgnam((char *)v[1]); X if (vigexists((char *)v[1])) X vig++; X zerolist(&members); X tmplistadd(&members); X for (indx=0; indx < AccountList.l_count; indx++) { X ac = (struct account *) AccountList.l_list[indx]; X if (ac->ac_gid == gm->gm_gid) X strlistadd(&members, (char *)ac->ac_name); X } X (void) printf("Group: %s (%u)%s\n", gm->gm_name, gm->gm_gid, X vig?" VIG":""); X if (rg) { X (void) printf("uid range: %d-%d ", rg->rg_from, X rg->rg_to); X puts(rg->rg_mode == RG_SHARED ? "shared" : "exclusive"); X } X#ifdef SENDMAIL X if (gm->gm_aliases.l_count) { X (void) printf("Bound to alias%s: ", ES(gm->gm_aliases.l_count)); X listlist(&gm->gm_aliases); X } X#endif X if (members.l_count) { X puts("\t- Members -"); X sort_list(&members, pstrcmp); X (void) showlist(&members, (addr *)allv); X } X if (gm->gm_mem.l_count) { X puts("\t- Groupies -"); X (void) showlist(&gm->gm_mem, (addr *)allv); X } X if (!members.l_count && !gm->gm_mem.l_count) X puts("No current members or groupies."); X else { X if (members.l_count) X (void) printf("%d member%s", members.l_count, X S(members.l_count)); X else X (void) printf("No members"); X if (gm->gm_mem.l_count) X (void) printf(", %d groupie%s\n", gm->gm_mem.l_count, X S(gm->gm_mem.l_count)); X else X puts(", no groupies."); X } X freelist(&members); X return; X} X Xdesinactives(c, v) Xint c; Xaddr *v; X X{ X struct account *ac; X struct groupmap *gm; X int indx, inactives = 0, days; X time_t now; X long toolong; X char errmsg[LONG_BUF]; X X if ( c > 2 ) { X err1("%s: too many arguments", (char *)v[0]); X return; X } X if ( c < 2 ) { X err1("usage: %s <days>", (char *)v[0]); X return; X } X if (!validint((char *)v[1])) { X err2("%s: %s doesn't make sense as a number", (char *)v[0], X (char *)v[1]); X return; X } X now = time((time_t *)0); X days = atoi((char *)v[1]); X toolong = days * 86400; X X for (indx=0; indx < AccountList.l_count; indx++) { X ac = (struct account *) AccountList.l_list[indx]; X if ((long)(now - ac->ac_ll.ll_time) < toolong) X continue; X /* X * Cryos are not inactive. X */ X if (eq(ac->ac_shell, FREEZE_SH)) X continue; X /* X * Vig members are not inactive. X */ X gm = getgmgid(ac->ac_gid); X if (!gm) { X (void) sprintf(errmsg, X "no group for gid %d!", X ac->ac_gid); X err(errmsg); X return; X } X if (vigexists(gm->gm_name)) X continue; X (void) printf("%-10s%-40s%3d", ac->ac_name, X ac->ac_realname, X ac->ac_uid); X if (ac->ac_ll.ll_time) X puts(" *"); X else X puts(""); X inactives++; X } X if (inactives) X (void) printf("\n%d user%s inactive for at least %d day%s.\n", X inactives, S(inactives), X days, S(days)); X else X (void) printf("No users inactive for %d day%s.\n", X days, S(days)); X return; X} X Xdesrange(c, v) Xint c; Xaddr *v; X X{ X struct range *rg; X X if ( c > 2 ) { X err1("%s: too many arguments", (char *)v[0]); X return; X } X if (c != 2) { X err1("usage: %s <range name>", (char *)v[0]); X return; X } X rg = getrgnam((char *)v[1]); X if (!rg) { X err1("%s: no such range", (char *)v[1]); X return; X } X (void) printf("%-16s %d to %d %s\n", rg->rg_name, rg->rg_from, X rg->rg_to, X (rg->rg_mode == RG_SHARED ? "shared" : "exclusive")); X return; X} X Xdessig(c, v) Xint c; Xaddr *v; X X{ X struct sig *sg; X struct account *ac; X int indx, members = 0; X X if ( c > 2 ) { X err1("%s: too many arguments", (char *)v[0]); X return; X } X if ( c != 2 ) { X err1("usage: %s <sig>", (char *)v[0]); X return; X } X sg = getsgnam((char *)v[1]); X if (!sg) { X err1("%s: no such sig", (char *)v[1]); X return; X } X (void) printf("Sig: %s\n", sg->sg_name); X if (sg->sg_exptime) (void) printf("Expires: %s\n", X when(sg->sg_exptime)); X#ifdef SENDMAIL X if (sg->sg_aliases.l_count) { X (void) printf("Bound to alias%s:", ES(sg->sg_aliases.l_count)); X listlist(&sg->sg_aliases); X } X#endif X puts((char *)sg->sg_desc); X for (indx=0; indx < AccountList.l_count; indx++) { X ac = (struct account *) AccountList.l_list[indx]; X if (!instrlist(&ac->ac_sigs, sg->sg_name)) X continue; X (void) printf("%-10s%-40s%3d", ac->ac_name, X ac->ac_realname, X ac->ac_uid); X if (ac->ac_ll.ll_time) X puts(" *"); X else X puts(""); X members++; X } X if (members) X (void) printf("\n%d member%s.\n", members, S(members)); X else X puts("No current members."); X return; X} X Xdesuser(c, v) Xint c; Xaddr *v; X X{ X#ifdef SENDMAIL X static struct list mal; X struct alias *al; X register int j; X#endif X struct account *ac; X struct groupmap *gm; X char *shell, errmsg[LONG_BUF]; X X if ( c > 2 ) { X err1("%s: too many arguments", (char *)v[0]); X return; X } X if ( c != 2 ) { X err1("usage: %s <user>", (char *)v[0]); X return; X } X X ac = getacnam((char *)v[1]); X if (!ac) { X err1("%s: no such user", (char *)v[1]); X return; X } X gm = getgmgid(ac->ac_gid); X if (!gm) { X (void) sprintf(errmsg, "no group name for gid %d!", X ac->ac_gid); X err(errmsg); X return; X } X if (strlen((char *)ac->ac_shell) == 0) X shell = "/bin/sh"; X else X shell = (char *)ac->ac_shell; X#ifdef SENDMAIL X /* X * Get the names of the aliases this user is in for later use X */ X zerolist(&mal); X tmplistadd(&mal); X for (j=0; j < AliasList.l_count; j++) { X al = (struct alias *) AliasList.l_list[j]; X if (instrlist(&al->al_addresses, (char *)ac->ac_name)) X strlistadd(&mal, (char *)al->al_name); X } X#endif X (void) printf("Login: %s (%d)\n", ac->ac_name, ac->ac_uid); X (void) printf("Name: %s (%s)\n", ac->ac_realname, ac->ac_gecos); X (void) printf("Id: %s\n", ac->ac_id); X (void) printf("Groups: %s ", gm->gm_name); X listlist(&ac->ac_groups); X fputs("Classes: ", stdout); X listlist(&ac->ac_classes); X fputs("Sigs: ", stdout); X listlist(&ac->ac_sigs); X#ifdef SENDMAIL X fputs("Aliases: ", stdout); X listlist(&mal); X#endif X (void) printf("Home: %s\n", ac->ac_dir); X (void) printf("Shell: %s\n", shell); X if (ac->ac_ll.ll_time) { X (void) printf("Last login %s on %s", when(ac->ac_ll.ll_time), X ac->ac_ll.ll_line); X if (ac->ac_ll.ll_host[0] != '\0') X (void) printf(" from %s", ac->ac_ll.ll_host); X puts(""); X } X else X puts("Never logged in."); X return; X} X X#ifdef HELPDIR Xwhatis(c, v) Xint c; Xaddr *v; X X{ X char *pager = getenv("PAGER"); X char *pname, helpfile[MEDIUM_BUF]; X char *av[4]; X struct stat statbuf; X X if ( c > 2 ) { X err1("%s: too many arguments", (char *)v[0]); X return; X } X if ( c != 2 ) { X err1("usage: %s <mcp term>", (char *)v[0]); X return; X } X if (!instrlist(&Terms, (char *)v[1])) { X err2("%s: %s is not an mcp term", X (char *)v[0], (char *)v[1]); X return; X } X X if (chdir(HELPDIR) == -1) { X perr(HELPDIR); X return; X } X (void) strcpy(helpfile, (char *)v[1]); X (void) strcat(helpfile, ".k"); X if (stat(helpfile, &statbuf) == -1) { X err1("No definition file for \"%s\"", (char *)v[1]); X return; X } X if (statbuf.st_size == 0) { X err1("Definition file for \"%s\" is empty, (alas and alack!)", X (char *)v[1]); X return; X } X if (!pager) X pager = DEF_PAGER; X pname = rindex(pager, '/') + 1; X pname = (pname ? pname : pager); X X av[0] = "shell-escape"; /* not really necessary */ X av[1] = pager; X av[2] = helpfile; X av[3] = (char *)0; X (void) shellescape(3, (addr *)av); X return; X} X#endif @//E*O*F src/describe.c// if test 14405 -ne "`wc -c <'src/describe.c'`"; then echo shar: error transmitting "'src/describe.c'" '(should have been 14405 characters)' fi fi # end of overwriting check echo shar: extracting "'src/lastlog.h'" '(116 characters)' if test -f 'src/lastlog.h' ; then echo shar: will not over-write existing file "'src/lastlog.h'" else sed 's/^X//' >src/lastlog.h <<'@//E*O*F src/lastlog.h//' Xstruct lastlog { X time_t ll_time; X char ll_line[8]; X char ll_host[16]; X}; X Xstruct lastlog *getlluid(), *getllent(); @//E*O*F src/lastlog.h// if test 116 -ne "`wc -c <'src/lastlog.h'`"; then echo shar: error transmitting "'src/lastlog.h'" '(should have been 116 characters)' fi fi # end of overwriting check echo shar: extracting "'src/lists.h'" '(294 characters)' if test -f 'src/lists.h' ; then echo shar: will not over-write existing file "'src/lists.h'" else sed 's/^X//' >src/lists.h <<'@//E*O*F src/lists.h//' X/* if l_spacefor == 0 malloc for this times sizeof (int *) */ X#define STARTSIZE 8 X Xstruct list { X int l_count; /* number of elements in list */ X int l_spacefor; /* how many elements there are room for */ X addr *l_list; /* array of pointers to elements */ X}; X Xaddr *mkargv(), glob(), listpop(); @//E*O*F src/lists.h// if test 294 -ne "`wc -c <'src/lists.h'`"; then echo shar: error transmitting "'src/lists.h'" '(should have been 294 characters)' fi fi # end of overwriting check echo shar: extracting "'src/pause.c'" '(25 characters)' if test -f 'src/pause.c' ; then echo shar: will not over-write existing file "'src/pause.c'" else sed 's/^X//' >src/pause.c <<'@//E*O*F src/pause.c//' Xpausemcp() X X{ X tstp(); X} @//E*O*F src/pause.c// if test 25 -ne "`wc -c <'src/pause.c'`"; then echo shar: error transmitting "'src/pause.c'" '(should have been 25 characters)' fi fi # end of overwriting check echo shar: "End of archive 5 (of 8)." cp /dev/null ark5isdone DONE=true for I in 1 2 3 4 5 6 7 8; do if test -! f ark${I}isdone; then echo "You still need to run archive ${I}." DONE=false fi done case $DONE in true) echo "You have run all 8 archives." echo 'See the README file' ;; esac ## End of shell archive. exit 0