v08i045: Account creation/manipulation program, Part05/08
sources-request at mirror.UUCP
sources-request at mirror.UUCP
Sat Feb 7 07:16:18 AEST 1987
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 at 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
More information about the Mod.sources
mailing list