xref: /csrg-svn/sbin/restore/interactive.c (revision 18003)
117752Smckusick /* Copyright (c) 1985 Regents of the University of California */
217752Smckusick 
317752Smckusick #ifndef lint
4*18003Smckusick static char sccsid[] = "@(#)interactive.c	3.3	(Berkeley)	02/18/85";
517752Smckusick #endif not lint
617752Smckusick 
717752Smckusick #include "restore.h"
817752Smckusick #include <dumprestor.h>
917752Smckusick #include <setjmp.h>
1017752Smckusick 
1117756Smckusick #define round(a, b) (((a) + (b) - 1) / (b) * (b))
1217756Smckusick 
1317752Smckusick /*
1417752Smckusick  * Things to handle interruptions.
1517752Smckusick  */
1617752Smckusick static jmp_buf reset;
1717752Smckusick static char *nextarg = NULL;
1817752Smckusick 
1917752Smckusick /*
2017752Smckusick  * Structure and routines associated with listing directories.
2117752Smckusick  */
2217752Smckusick struct afile {
2317752Smckusick 	ino_t	fnum;		/* inode number of file */
2417752Smckusick 	char	*fname;		/* file name */
2517752Smckusick 	short	fflags;		/* extraction flags, if any */
2617752Smckusick 	char	ftype;		/* file type, e.g. LEAF or NODE */
2717752Smckusick };
28*18003Smckusick struct arglist {
29*18003Smckusick 	struct afile	*head;	/* start of argument list */
30*18003Smckusick 	struct afile	*last;	/* end of argument list */
31*18003Smckusick 	struct afile	*base;	/* current list arena */
32*18003Smckusick 	int		nent;	/* maximum size of list */
33*18003Smckusick 	char		*cmd;	/* the current command */
34*18003Smckusick };
3517752Smckusick extern int fcmp();
3617752Smckusick extern char *fmtentry();
3717752Smckusick char *copynext();
3817752Smckusick 
3917752Smckusick /*
4017752Smckusick  * Read and execute commands from the terminal.
4117752Smckusick  */
4217752Smckusick runcmdshell()
4317752Smckusick {
4417752Smckusick 	register struct entry *np;
4517752Smckusick 	ino_t ino;
46*18003Smckusick 	static struct arglist alist = { 0, 0, 0, 0, 0 };
4717752Smckusick 	char curdir[MAXPATHLEN];
4817752Smckusick 	char name[MAXPATHLEN];
4917752Smckusick 	char cmd[BUFSIZ];
5017752Smckusick 
5117752Smckusick 	canon("/", curdir);
5217752Smckusick loop:
5317752Smckusick 	if (setjmp(reset) != 0) {
54*18003Smckusick 		for (; alist.head < alist.last; alist.head++)
55*18003Smckusick 			freename(alist.head->fname);
5617752Smckusick 		nextarg = NULL;
5717752Smckusick 		volno = 0;
5817752Smckusick 	}
59*18003Smckusick 	getcmd(curdir, cmd, name, &alist);
6017752Smckusick 	switch (cmd[0]) {
6117752Smckusick 	/*
6217752Smckusick 	 * Add elements to the extraction list.
6317752Smckusick 	 */
6417752Smckusick 	case 'a':
6517752Smckusick 		ino = dirlookup(name);
6617752Smckusick 		if (ino == 0)
6717752Smckusick 			break;
6817752Smckusick 		if (mflag)
6917752Smckusick 			pathcheck(name);
7017752Smckusick 		treescan(name, ino, addfile);
7117752Smckusick 		break;
7217752Smckusick 	/*
7317752Smckusick 	 * Change working directory.
7417752Smckusick 	 */
7517752Smckusick 	case 'c':
7617752Smckusick 		ino = dirlookup(name);
7717752Smckusick 		if (ino == 0)
7817752Smckusick 			break;
7917752Smckusick 		if (inodetype(ino) == LEAF) {
8017752Smckusick 			fprintf(stderr, "%s: not a directory\n", name);
8117752Smckusick 			break;
8217752Smckusick 		}
8317752Smckusick 		(void) strcpy(curdir, name);
8417752Smckusick 		break;
8517752Smckusick 	/*
8617752Smckusick 	 * Delete elements from the extraction list.
8717752Smckusick 	 */
8817752Smckusick 	case 'd':
8917752Smckusick 		np = lookupname(name);
9017752Smckusick 		if (np == NIL || (np->e_flags & NEW) == 0) {
9117752Smckusick 			fprintf(stderr, "%s: not on extraction list\n", name);
9217752Smckusick 			break;
9317752Smckusick 		}
9417752Smckusick 		treescan(name, np->e_ino, deletefile);
9517752Smckusick 		break;
9617752Smckusick 	/*
9717752Smckusick 	 * Extract the requested list.
9817752Smckusick 	 */
9917752Smckusick 	case 'e':
10017752Smckusick 		createfiles();
10117752Smckusick 		createlinks();
10217752Smckusick 		setdirmodes();
10317752Smckusick 		if (dflag)
10417752Smckusick 			checkrestore();
10517752Smckusick 		volno = 0;
10617752Smckusick 		break;
10717752Smckusick 	/*
10817752Smckusick 	 * List available commands.
10917752Smckusick 	 */
11017752Smckusick 	case 'h':
11117752Smckusick 	case '?':
11217752Smckusick 		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
11317752Smckusick 			"Available commands are:\n",
11417752Smckusick 			"\tls [arg] - list directory\n",
11517752Smckusick 			"\tcd arg - change directory\n",
11617752Smckusick 			"\tpwd - print current directory\n",
11717752Smckusick 			"\tadd [arg] - add `arg' to list of",
11817752Smckusick 			" files to be extracted\n",
11917752Smckusick 			"\tdelete [arg] - delete `arg' from",
12017752Smckusick 			" list of files to be extracted\n",
12117752Smckusick 			"\textract - extract requested files\n",
12217752Smckusick 			"\tquit - immediately exit program\n",
12317752Smckusick 			"\tverbose - toggle verbose flag",
12417752Smckusick 			" (useful with ``ls'')\n",
12517752Smckusick 			"\thelp or `?' - print this list\n",
12617752Smckusick 			"If no `arg' is supplied, the current",
12717752Smckusick 			" directory is used\n");
12817752Smckusick 		break;
12917752Smckusick 	/*
13017752Smckusick 	 * List a directory.
13117752Smckusick 	 */
13217752Smckusick 	case 'l':
13317752Smckusick 		ino = dirlookup(name);
13417752Smckusick 		if (ino == 0)
13517752Smckusick 			break;
13617752Smckusick 		printlist(name, ino, curdir);
13717752Smckusick 		break;
13817752Smckusick 	/*
13917752Smckusick 	 * Print current directory.
14017752Smckusick 	 */
14117752Smckusick 	case 'p':
14217752Smckusick 		if (curdir[1] == '\0')
14317752Smckusick 			fprintf(stderr, "/\n");
14417752Smckusick 		else
14517752Smckusick 			fprintf(stderr, "%s\n", &curdir[1]);
14617752Smckusick 		break;
14717752Smckusick 	/*
14817752Smckusick 	 * Quit.
14917752Smckusick 	 */
15017752Smckusick 	case 'q':
15117752Smckusick 	case 'x':
15217752Smckusick 		return;
15317752Smckusick 	/*
15417752Smckusick 	 * Toggle verbose mode.
15517752Smckusick 	 */
15617752Smckusick 	case 'v':
15717752Smckusick 		if (vflag) {
15817752Smckusick 			fprintf(stderr, "verbose mode off\n");
15917752Smckusick 			vflag = 0;
16017752Smckusick 			break;
16117752Smckusick 		}
16217752Smckusick 		fprintf(stderr, "verbose mode on\n");
16317752Smckusick 		vflag++;
16417752Smckusick 		break;
16517752Smckusick 	/*
16617752Smckusick 	 * Just restore requested directory modes.
16717752Smckusick 	 */
16817752Smckusick 	case 'R':
16917752Smckusick 		setdirmodes();
17017752Smckusick 		break;
17117752Smckusick 	/*
17217752Smckusick 	 * Turn on debugging.
17317752Smckusick 	 */
17417752Smckusick 	case 'D':
17517752Smckusick 		if (dflag) {
17617752Smckusick 			fprintf(stderr, "debugging mode off\n");
17717752Smckusick 			dflag = 0;
17817752Smckusick 			break;
17917752Smckusick 		}
18017752Smckusick 		fprintf(stderr, "debugging mode on\n");
18117752Smckusick 		dflag++;
18217752Smckusick 		break;
18317752Smckusick 	/*
18417752Smckusick 	 * Unknown command.
18517752Smckusick 	 */
18617752Smckusick 	default:
18717752Smckusick 		fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
18817752Smckusick 		break;
18917752Smckusick 	}
19017752Smckusick 	goto loop;
19117752Smckusick }
19217752Smckusick 
19317752Smckusick /*
19417752Smckusick  * Read and parse an interactive command.
19517752Smckusick  * The first word on the line is assigned to "cmd". If
19617752Smckusick  * there are no arguments on the command line, then "curdir"
19717752Smckusick  * is returned as the argument. If there are arguments
19817752Smckusick  * on the line they are returned one at a time on each
19917752Smckusick  * successive call to getcmd. Each argument is first assigned
20017752Smckusick  * to "name". If it does not start with "/" the pathname in
20117752Smckusick  * "curdir" is prepended to it. Finally "canon" is called to
20217752Smckusick  * eliminate any embedded ".." components.
20317752Smckusick  */
204*18003Smckusick getcmd(curdir, cmd, name, ap)
20517752Smckusick 	char *curdir, *cmd, *name;
206*18003Smckusick 	struct arglist *ap;
20717752Smckusick {
20817752Smckusick 	register char *cp;
20917752Smckusick 	static char input[BUFSIZ];
21017752Smckusick 	char output[BUFSIZ];
21117752Smckusick #	define rawname input	/* save space by reusing input buffer */
21217752Smckusick 
21317752Smckusick 	/*
21417752Smckusick 	 * Check to see if still processing arguments.
21517752Smckusick 	 */
216*18003Smckusick 	if (ap->head != ap->last) {
217*18003Smckusick 		strcpy(name, ap->head->fname);
218*18003Smckusick 		freename(ap->head->fname);
219*18003Smckusick 		ap->head++;
220*18003Smckusick 		return;
221*18003Smckusick 	}
22217752Smckusick 	if (nextarg != NULL)
22317752Smckusick 		goto getnext;
22417752Smckusick 	/*
22517752Smckusick 	 * Read a command line and trim off trailing white space.
22617752Smckusick 	 */
22717752Smckusick 	do	{
22817752Smckusick 		fprintf(stderr, "restore > ");
22917752Smckusick 		(void) fflush(stderr);
23017752Smckusick 		(void) fgets(input, BUFSIZ, terminal);
23117752Smckusick 	} while (!feof(terminal) && input[0] == '\n');
23217752Smckusick 	if (feof(terminal)) {
23317752Smckusick 		(void) strcpy(cmd, "quit");
23417752Smckusick 		return;
23517752Smckusick 	}
23617752Smckusick 	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
23717752Smckusick 		/* trim off trailing white space and newline */;
23817752Smckusick 	*++cp = '\0';
23917752Smckusick 	/*
24017752Smckusick 	 * Copy the command into "cmd".
24117752Smckusick 	 */
24217752Smckusick 	cp = copynext(input, cmd);
243*18003Smckusick 	ap->cmd = cmd;
24417752Smckusick 	/*
24517752Smckusick 	 * If no argument, use curdir as the default.
24617752Smckusick 	 */
24717752Smckusick 	if (*cp == '\0') {
24817752Smckusick 		(void) strcpy(name, curdir);
24917752Smckusick 		return;
25017752Smckusick 	}
25117752Smckusick 	nextarg = cp;
25217752Smckusick 	/*
25317752Smckusick 	 * Find the next argument.
25417752Smckusick 	 */
25517752Smckusick getnext:
25617752Smckusick 	cp = copynext(nextarg, rawname);
25717752Smckusick 	if (*cp == '\0')
25817752Smckusick 		nextarg = NULL;
25917752Smckusick 	else
26017752Smckusick 		nextarg = cp;
26117752Smckusick 	/*
26217752Smckusick 	 * If it an absolute pathname, canonicalize it and return it.
26317752Smckusick 	 */
26417752Smckusick 	if (rawname[0] == '/') {
26517752Smckusick 		canon(rawname, name);
26617752Smckusick 	} else {
26717752Smckusick 		/*
26817752Smckusick 		 * For relative pathnames, prepend the current directory to
26917752Smckusick 		 * it then canonicalize and return it.
27017752Smckusick 		 */
27117752Smckusick 		(void) strcpy(output, curdir);
27217752Smckusick 		(void) strcat(output, "/");
27317752Smckusick 		(void) strcat(output, rawname);
27417752Smckusick 		canon(output, name);
27517752Smckusick 	}
276*18003Smckusick 	expandarg(name, ap);
277*18003Smckusick 	strcpy(name, ap->head->fname);
278*18003Smckusick 	freename(ap->head->fname);
279*18003Smckusick 	ap->head++;
28017752Smckusick #	undef rawname
28117752Smckusick }
28217752Smckusick 
28317752Smckusick /*
28417752Smckusick  * Strip off the next token of the input.
28517752Smckusick  */
28617752Smckusick char *
28717752Smckusick copynext(input, output)
28817752Smckusick 	char *input, *output;
28917752Smckusick {
29017752Smckusick 	register char *cp, *bp;
29117752Smckusick 	char quote;
29217752Smckusick 
29317752Smckusick 	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
29417752Smckusick 		/* skip to argument */;
29517752Smckusick 	bp = output;
29617752Smckusick 	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
29717752Smckusick 		/*
29817752Smckusick 		 * Handle back slashes.
29917752Smckusick 		 */
30017752Smckusick 		if (*cp == '\\') {
30117752Smckusick 			if (*++cp == '\0') {
30217752Smckusick 				fprintf(stderr,
30317752Smckusick 					"command lines cannot be continued\n");
30417752Smckusick 				continue;
30517752Smckusick 			}
30617752Smckusick 			*bp++ = *cp++;
30717752Smckusick 			continue;
30817752Smckusick 		}
30917752Smckusick 		/*
31017752Smckusick 		 * The usual unquoted case.
31117752Smckusick 		 */
31217752Smckusick 		if (*cp != '\'' && *cp != '"') {
31317752Smckusick 			*bp++ = *cp++;
31417752Smckusick 			continue;
31517752Smckusick 		}
31617752Smckusick 		/*
31717752Smckusick 		 * Handle single and double quotes.
31817752Smckusick 		 */
31917752Smckusick 		quote = *cp++;
32017752Smckusick 		while (*cp != quote && *cp != '\0')
32117752Smckusick 			*bp++ = *cp++ | 0200;
32217752Smckusick 		if (*cp++ == '\0') {
32317752Smckusick 			fprintf(stderr, "missing %c\n", quote);
32417752Smckusick 			cp--;
32517752Smckusick 			continue;
32617752Smckusick 		}
32717752Smckusick 	}
32817752Smckusick 	*bp = '\0';
32917752Smckusick 	return (cp);
33017752Smckusick }
33117752Smckusick 
33217752Smckusick /*
33317752Smckusick  * Canonicalize file names to always start with ``./'' and
334*18003Smckusick  * remove any imbedded "." and ".." components.
33517752Smckusick  */
33617752Smckusick canon(rawname, canonname)
33717752Smckusick 	char *rawname, *canonname;
33817752Smckusick {
33917752Smckusick 	register char *cp, *np;
34017752Smckusick 	int len;
34117752Smckusick 
34217752Smckusick 	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
34317752Smckusick 		(void) strcpy(canonname, "");
34417752Smckusick 	else if (rawname[0] == '/')
34517752Smckusick 		(void) strcpy(canonname, ".");
34617752Smckusick 	else
34717752Smckusick 		(void) strcpy(canonname, "./");
34817752Smckusick 	(void) strcat(canonname, rawname);
34917752Smckusick 	/*
350*18003Smckusick 	 * Eliminate multiple and trailing '/'s
35117752Smckusick 	 */
352*18003Smckusick 	for (cp = np = canonname; *np != '\0'; cp++) {
353*18003Smckusick 		*cp = *np++;
354*18003Smckusick 		while (*cp == '/' && *np == '/')
355*18003Smckusick 			np++;
356*18003Smckusick 	}
357*18003Smckusick 	*cp = '\0';
358*18003Smckusick 	if (*--cp == '/')
359*18003Smckusick 		*cp = '\0';
360*18003Smckusick 	/*
361*18003Smckusick 	 * Eliminate extraneous "." and ".." from pathnames.
362*18003Smckusick 	 */
36317752Smckusick 	for (np = canonname; *np != '\0'; ) {
36417752Smckusick 		np++;
36517752Smckusick 		cp = np;
36617752Smckusick 		while (*np != '/' && *np != '\0')
36717752Smckusick 			np++;
368*18003Smckusick 		if (np - cp == 1 && *cp == '.') {
369*18003Smckusick 			cp--;
370*18003Smckusick 			(void) strcpy(cp, np);
371*18003Smckusick 			np = cp;
372*18003Smckusick 		}
37317752Smckusick 		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
37417752Smckusick 			cp--;
37517752Smckusick 			while (cp > &canonname[1] && *--cp != '/')
37617752Smckusick 				/* find beginning of name */;
37717752Smckusick 			(void) strcpy(cp, np);
37817752Smckusick 			np = cp;
37917752Smckusick 		}
38017752Smckusick 	}
38117752Smckusick }
38217752Smckusick 
38317752Smckusick /*
38417756Smckusick  * globals (file name generation)
38517756Smckusick  *
38617756Smckusick  * "*" in params matches r.e ".*"
38717756Smckusick  * "?" in params matches r.e. "."
38817756Smckusick  * "[...]" in params matches character class
38917756Smckusick  * "[...a-z...]" in params matches a through z.
39017756Smckusick  */
391*18003Smckusick expandarg(arg, ap)
39217756Smckusick 	char *arg;
393*18003Smckusick 	register struct arglist *ap;
39417756Smckusick {
395*18003Smckusick 	struct afile single;
39617756Smckusick 	int size;
39717756Smckusick 
398*18003Smckusick 	ap->head = ap->last = (struct afile *)0;
399*18003Smckusick 	size = expand(arg, 0, ap);
40017756Smckusick 	if (size == 0) {
401*18003Smckusick 		single.fnum = lookupname(arg)->e_ino;
402*18003Smckusick 		single.fname = savename(arg);
403*18003Smckusick 		ap->head = &single;
404*18003Smckusick 		ap->last = ap->head + 1;
405*18003Smckusick 		return;
40617756Smckusick 	}
407*18003Smckusick 	qsort((char *)ap->head, ap->last - ap->head, sizeof *ap->head, fcmp);
40817756Smckusick }
40917756Smckusick 
41017756Smckusick /*
41117756Smckusick  * Expand a file name
41217756Smckusick  */
413*18003Smckusick expand(as, rflg, ap)
41417756Smckusick 	char *as;
41517756Smckusick 	int rflg;
416*18003Smckusick 	register struct arglist *ap;
41717756Smckusick {
41817756Smckusick 	int		count, size;
41917756Smckusick 	char		dir = 0;
42017756Smckusick 	char		*rescan = 0;
42117756Smckusick 	DIR		*dirp;
42217756Smckusick 	register char	*s, *cs;
423*18003Smckusick 	int		sindex, rindex, lindex;
42417756Smckusick 	struct direct	*dp;
42517756Smckusick 	register char	slash;
42617756Smckusick 	register char	*rs;
42717756Smckusick 	register char	c;
42817756Smckusick 
42917756Smckusick 	/*
43017756Smckusick 	 * check for meta chars
43117756Smckusick 	 */
43217756Smckusick 	s = cs = as;
43317756Smckusick 	slash = 0;
43417756Smckusick 	while (*cs != '*' && *cs != '?' && *cs != '[') {
435*18003Smckusick 		if (*cs++ == 0) {
43617756Smckusick 			if (rflg && slash)
43717756Smckusick 				break;
43817756Smckusick 			else
43917756Smckusick 				return (0) ;
440*18003Smckusick 		} else if (*cs == '/') {
44117756Smckusick 			slash++;
44217756Smckusick 		}
44317756Smckusick 	}
44417756Smckusick 	for (;;) {
44517756Smckusick 		if (cs == s) {
446*18003Smckusick 			s = "";
44717756Smckusick 			break;
44817756Smckusick 		} else if (*--cs == '/') {
44917756Smckusick 			*cs = 0;
45017756Smckusick 			if (s == cs)
45117756Smckusick 				s = "/";
45217756Smckusick 			break;
45317756Smckusick 		}
45417756Smckusick 	}
45517756Smckusick 	if ((dirp = rst_opendir(s)) != NULL)
45617756Smckusick 		dir++;
45717756Smckusick 	count = 0;
45817756Smckusick 	if (*cs == 0)
459*18003Smckusick 		*cs++ = 0200;
46017756Smckusick 	if (dir) {
46117756Smckusick 		/*
46217756Smckusick 		 * check for rescan
46317756Smckusick 		 */
46417756Smckusick 		rs = cs;
46517756Smckusick 		do {
46617756Smckusick 			if (*rs == '/') {
46717756Smckusick 				rescan = rs;
46817756Smckusick 				*rs = 0;
46917756Smckusick 			}
47017756Smckusick 		} while (*rs++);
471*18003Smckusick 		sindex = ap->last - ap->head;
47217756Smckusick 		while ((dp = rst_readdir(dirp)) != NULL && dp->d_ino != 0) {
47317756Smckusick 			if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
47417756Smckusick 				continue;
47517756Smckusick 			if ((*dp->d_name == '.' && *cs != '.'))
47617756Smckusick 				continue;
47717756Smckusick 			if (gmatch(dp->d_name, cs)) {
478*18003Smckusick 				if (addg(dp, s, rescan, ap) < 0)
47917756Smckusick 					return (-1);
48017756Smckusick 				count++;
48117756Smckusick 			}
48217756Smckusick 		}
48317756Smckusick 		if (rescan) {
484*18003Smckusick 			rindex = sindex;
485*18003Smckusick 			lindex = ap->last - ap->head;
48617756Smckusick 			if (count) {
48717756Smckusick 				count = 0;
488*18003Smckusick 				while (rindex < lindex) {
489*18003Smckusick 					size = expand(ap->head[rindex].fname,
490*18003Smckusick 					    1, ap);
49117756Smckusick 					if (size < 0)
49217756Smckusick 						return (size);
49317756Smckusick 					count += size;
494*18003Smckusick 					rindex++;
49517756Smckusick 				}
49617756Smckusick 			}
497*18003Smckusick 			bcopy((char *)&ap->head[lindex],
498*18003Smckusick 			     (char *)&ap->head[sindex],
499*18003Smckusick 			     (ap->last - &ap->head[rindex]) * sizeof *ap->head);
500*18003Smckusick 			ap->last -= lindex - sindex;
50117756Smckusick 			*rescan = '/';
50217756Smckusick 		}
50317756Smckusick 	}
50417756Smckusick 	s = as;
50517756Smckusick 	while (c = *s)
50617756Smckusick 		*s++ = (c&0177 ? c : '/');
50717756Smckusick 	return (count);
50817756Smckusick }
50917756Smckusick 
51017756Smckusick /*
51117756Smckusick  * Check for a name match
51217756Smckusick  */
51317756Smckusick gmatch(s, p)
51417756Smckusick 	register char	*s, *p;
51517756Smckusick {
51617756Smckusick 	register int	scc;
51717756Smckusick 	char		c;
51817756Smckusick 	char		ok;
51917756Smckusick 	int		lc;
52017756Smckusick 
52117756Smckusick 	if (scc = *s++)
52217756Smckusick 		if ((scc &= 0177) == 0)
52317756Smckusick 			scc = 0200;
52417756Smckusick 	switch (c = *p++) {
52517756Smckusick 
52617756Smckusick 	case '[':
52717756Smckusick 		ok = 0;
52817756Smckusick 		lc = 077777;
52917756Smckusick 		while (c = *p++) {
530*18003Smckusick 			if (c == ']') {
53117756Smckusick 				return (ok ? gmatch(s, p) : 0);
53217756Smckusick 			} else if (c == '-') {
53317756Smckusick 				if (lc <= scc && scc <= (*p++))
53417756Smckusick 					ok++ ;
53517756Smckusick 			} else {
53617756Smckusick 				if (scc == (lc = (c&0177)))
53717756Smckusick 					ok++ ;
53817756Smckusick 			}
53917756Smckusick 		}
54017756Smckusick 		return (0);
54117756Smckusick 
54217756Smckusick 	default:
54317756Smckusick 		if ((c&0177) != scc)
54417756Smckusick 			return (0) ;
54517756Smckusick 		/* falls through */
54617756Smckusick 
54717756Smckusick 	case '?':
54817756Smckusick 		return (scc ? gmatch(s, p) : 0);
54917756Smckusick 
55017756Smckusick 	case '*':
55117756Smckusick 		if (*p == 0)
55217756Smckusick 			return (1) ;
55317756Smckusick 		s--;
55417756Smckusick 		while (*s) {
55517756Smckusick 			if (gmatch(s++, p))
55617756Smckusick 				return (1);
55717756Smckusick 		}
55817756Smckusick 		return (0);
55917756Smckusick 
56017756Smckusick 	case 0:
56117756Smckusick 		return (scc == 0);
56217756Smckusick 	}
56317756Smckusick }
56417756Smckusick 
56517756Smckusick /*
56617756Smckusick  * Construct a matched name.
56717756Smckusick  */
568*18003Smckusick addg(dp, as1, as3, ap)
569*18003Smckusick 	struct direct	*dp;
570*18003Smckusick 	char		*as1, *as3;
571*18003Smckusick 	struct arglist	*ap;
57217756Smckusick {
57317756Smckusick 	register char	*s1, *s2;
57417756Smckusick 	register int	c;
575*18003Smckusick 	char		buf[BUFSIZ];
57617756Smckusick 
577*18003Smckusick 	s2 = buf;
57817756Smckusick 	s1 = as1;
57917756Smckusick 	while (c = *s1++) {
58017756Smckusick 		if ((c &= 0177) == 0) {
581*18003Smckusick 			*s2++ = '/';
58217756Smckusick 			break;
58317756Smckusick 		}
58417756Smckusick 		*s2++ = c;
58517756Smckusick 	}
586*18003Smckusick 	s1 = dp->d_name;
58717756Smckusick 	while (*s2 = *s1++)
58817756Smckusick 		s2++;
58917756Smckusick 	if (s1 = as3) {
59017756Smckusick 		*s2++ = '/';
59117756Smckusick 		while (*s2++ = *++s1)
59217756Smckusick 			/* void */;
59317756Smckusick 	}
594*18003Smckusick 	if (mkentry(buf, dp->d_ino, ap) == FAIL)
595*18003Smckusick 		return (-1);
59617756Smckusick }
59717756Smckusick 
59817756Smckusick /*
59917752Smckusick  * Do an "ls" style listing of a directory
60017752Smckusick  */
60117752Smckusick printlist(name, ino, basename)
60217752Smckusick 	char *name;
60317752Smckusick 	ino_t ino;
60417752Smckusick 	char *basename;
60517752Smckusick {
60617752Smckusick 	register struct afile *fp;
607*18003Smckusick 	register struct direct *dp;
608*18003Smckusick 	static struct arglist alist = { 0, 0, 0, 0, "ls" };
60917752Smckusick 	struct afile single;
61017752Smckusick 	DIR *dirp;
61117752Smckusick 
61217752Smckusick 	if ((dirp = rst_opendir(name)) == NULL) {
61317752Smckusick 		single.fnum = ino;
614*18003Smckusick 		single.fname = savename(name + strlen(basename) + 1);
615*18003Smckusick 		alist.head = &single;
616*18003Smckusick 		alist.last = alist.head + 1;
61717752Smckusick 	} else {
618*18003Smckusick 		alist.head = (struct afile *)0;
619*18003Smckusick 		fprintf(stderr, "%s:\n", name);
620*18003Smckusick 		while (dp = rst_readdir(dirp)) {
621*18003Smckusick 			if (dp == NULL || dp->d_ino == 0)
622*18003Smckusick 				break;
623*18003Smckusick 			if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
624*18003Smckusick 				continue;
625*18003Smckusick 			if (vflag == 0 &&
626*18003Smckusick 			    (strcmp(dp->d_name, ".") == 0 ||
627*18003Smckusick 			     strcmp(dp->d_name, "..") == 0))
628*18003Smckusick 				continue;
629*18003Smckusick 			if (!mkentry(dp->d_name, dp->d_ino, &alist))
630*18003Smckusick 				return;
631*18003Smckusick 		}
63217752Smckusick 	}
633*18003Smckusick 	if (alist.head != 0) {
634*18003Smckusick 		qsort((char *)alist.head, alist.last - alist.head,
635*18003Smckusick 			sizeof *alist.head, fcmp);
636*18003Smckusick 		formatf(&alist);
637*18003Smckusick 		for (fp = alist.head; fp < alist.last; fp++)
638*18003Smckusick 			freename(fp->fname);
639*18003Smckusick 	}
640*18003Smckusick 	if (dirp != NULL)
641*18003Smckusick 		fprintf(stderr, "\n");
64217752Smckusick }
64317752Smckusick 
64417752Smckusick /*
64517752Smckusick  * Read the contents of a directory.
64617752Smckusick  */
647*18003Smckusick mkentry(name, ino, ap)
648*18003Smckusick 	char *name;
649*18003Smckusick 	ino_t ino;
650*18003Smckusick 	register struct arglist *ap;
65117752Smckusick {
65217752Smckusick 	register struct afile *fp;
65317752Smckusick 
654*18003Smckusick 	if (ap->base == NULL) {
655*18003Smckusick 		ap->nent = 20;
656*18003Smckusick 		ap->base = (struct afile *)calloc((unsigned)ap->nent,
65717752Smckusick 			sizeof (struct afile));
658*18003Smckusick 		if (ap->base == NULL) {
659*18003Smckusick 			fprintf(stderr, "%s: out of memory\n", ap->cmd);
66017752Smckusick 			return (FAIL);
66117752Smckusick 		}
66217752Smckusick 	}
663*18003Smckusick 	if (ap->head == 0)
664*18003Smckusick 		ap->head = ap->last = ap->base;
665*18003Smckusick 	fp = ap->last;
666*18003Smckusick 	fp->fnum = ino;
667*18003Smckusick 	fp->fname = savename(name);
668*18003Smckusick 	fp++;
669*18003Smckusick 	if (fp == ap->head + ap->nent) {
670*18003Smckusick 		ap->base = (struct afile *)realloc((char *)ap->base,
671*18003Smckusick 		    (unsigned)(2 * ap->nent * sizeof (struct afile)));
672*18003Smckusick 		if (ap->base == 0) {
673*18003Smckusick 			fprintf(stderr, "%s: out of memory\n", ap->cmd);
674*18003Smckusick 			return (FAIL);
67517752Smckusick 		}
676*18003Smckusick 		ap->head = ap->base;
677*18003Smckusick 		fp = ap->head + ap->nent;
678*18003Smckusick 		ap->nent *= 2;
67917752Smckusick 	}
680*18003Smckusick 	ap->last = fp;
68117752Smckusick 	return (GOOD);
68217752Smckusick }
68317752Smckusick 
68417752Smckusick /*
68517752Smckusick  * Print out a pretty listing of a directory
68617752Smckusick  */
687*18003Smckusick formatf(ap)
688*18003Smckusick 	register struct arglist *ap;
68917752Smckusick {
69017752Smckusick 	register struct afile *fp;
69117752Smckusick 	struct entry *np;
692*18003Smckusick 	int width = 0, w, nentry = ap->last - ap->head;
69317752Smckusick 	int i, j, len, columns, lines;
69417752Smckusick 	char *cp;
69517752Smckusick 
696*18003Smckusick 	if (ap->head == ap->last)
69717752Smckusick 		return;
698*18003Smckusick 	for (fp = ap->head; fp < ap->last; fp++) {
69917752Smckusick 		fp->ftype = inodetype(fp->fnum);
70017752Smckusick 		np = lookupino(fp->fnum);
70117752Smckusick 		if (np != NIL)
70217752Smckusick 			fp->fflags = np->e_flags;
70317752Smckusick 		else
70417752Smckusick 			fp->fflags = 0;
70517752Smckusick 		len = strlen(fmtentry(fp));
70617752Smckusick 		if (len > width)
70717752Smckusick 			width = len;
70817752Smckusick 	}
70917752Smckusick 	width += 2;
71017752Smckusick 	columns = 80 / width;
71117752Smckusick 	if (columns == 0)
71217752Smckusick 		columns = 1;
71317752Smckusick 	lines = (nentry + columns - 1) / columns;
71417752Smckusick 	for (i = 0; i < lines; i++) {
71517752Smckusick 		for (j = 0; j < columns; j++) {
716*18003Smckusick 			fp = ap->head + j * lines + i;
71717752Smckusick 			cp = fmtentry(fp);
71817752Smckusick 			fprintf(stderr, "%s", cp);
719*18003Smckusick 			if (fp + lines >= ap->last) {
72017752Smckusick 				fprintf(stderr, "\n");
72117752Smckusick 				break;
72217752Smckusick 			}
72317752Smckusick 			w = strlen(cp);
72417752Smckusick 			while (w < width) {
72517752Smckusick 				w++;
72617752Smckusick 				fprintf(stderr, " ");
72717752Smckusick 			}
72817752Smckusick 		}
72917752Smckusick 	}
73017752Smckusick }
73117752Smckusick 
73217752Smckusick /*
73317752Smckusick  * Comparison routine for qsort.
73417752Smckusick  */
73517752Smckusick fcmp(f1, f2)
73617752Smckusick 	register struct afile *f1, *f2;
73717752Smckusick {
73817752Smckusick 
73917752Smckusick 	return (strcmp(f1->fname, f2->fname));
74017752Smckusick }
74117752Smckusick 
74217752Smckusick /*
74317752Smckusick  * Format a directory entry.
74417752Smckusick  */
74517752Smckusick char *
74617752Smckusick fmtentry(fp)
74717752Smckusick 	register struct afile *fp;
74817752Smckusick {
74917752Smckusick 	static char fmtres[BUFSIZ];
75017752Smckusick 	register char *cp, *dp;
75117752Smckusick 
75217752Smckusick 	if (vflag)
75317752Smckusick 		(void) sprintf(fmtres, "%5d ", fp->fnum);
75417752Smckusick 	else
75517752Smckusick 		fmtres[0] = '\0';
75617752Smckusick 	dp = &fmtres[strlen(fmtres)];
75717752Smckusick 	if (dflag && BIT(fp->fnum, dumpmap) == 0)
75817752Smckusick 		*dp++ = '^';
75917752Smckusick 	else if ((fp->fflags & NEW) != 0)
76017752Smckusick 		*dp++ = '*';
76117752Smckusick 	else
76217752Smckusick 		*dp++ = ' ';
76317752Smckusick 	for (cp = fp->fname; *cp; cp++)
76417752Smckusick 		if (!vflag && (*cp < ' ' || *cp >= 0177))
76517752Smckusick 			*dp++ = '?';
76617752Smckusick 		else
76717752Smckusick 			*dp++ = *cp;
76817752Smckusick 	if (fp->ftype == NODE)
76917752Smckusick 		*dp++ = '/';
77017752Smckusick 	*dp++ = 0;
77117752Smckusick 	return (fmtres);
77217752Smckusick }
77317752Smckusick 
77417752Smckusick /*
77517752Smckusick  * respond to interrupts
77617752Smckusick  */
77717752Smckusick onintr()
77817752Smckusick {
77917752Smckusick 	if (command == 'i')
78017752Smckusick 		longjmp(reset, 1);
78117752Smckusick 	if (reply("restore interrupted, continue") == FAIL)
78217752Smckusick 		done(1);
78317752Smckusick }
784