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