xref: /csrg-svn/sbin/restore/interactive.c (revision 39942)
121166Sdist /*
236105Sbostic  * Copyright (c) 1985 The Regents of the University of California.
336105Sbostic  * All rights reserved.
436105Sbostic  *
536105Sbostic  * Redistribution and use in source and binary forms are permitted
636105Sbostic  * provided that the above copyright notice and this paragraph are
736105Sbostic  * duplicated in all such forms and that any documentation,
836105Sbostic  * advertising materials, and other materials related to such
936105Sbostic  * distribution and use acknowledge that the software was developed
1036105Sbostic  * by the University of California, Berkeley.  The name of the
1136105Sbostic  * University may not be used to endorse or promote products derived
1236105Sbostic  * from this software without specific prior written permission.
1336105Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1436105Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1536105Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1621166Sdist  */
1717752Smckusick 
1817752Smckusick #ifndef lint
19*39942Sbostic static char sccsid[] = "@(#)interactive.c	5.7 (Berkeley) 01/19/90";
2036105Sbostic #endif /* not lint */
2117752Smckusick 
2217752Smckusick #include "restore.h"
2323546Smckusick #include <protocols/dumprestore.h>
2417752Smckusick #include <setjmp.h>
2517752Smckusick 
2617756Smckusick #define round(a, b) (((a) + (b) - 1) / (b) * (b))
2717756Smckusick 
2817752Smckusick /*
2917752Smckusick  * Things to handle interruptions.
3017752Smckusick  */
3117752Smckusick static jmp_buf reset;
3217752Smckusick static char *nextarg = NULL;
3317752Smckusick 
3417752Smckusick /*
3517752Smckusick  * Structure and routines associated with listing directories.
3617752Smckusick  */
3717752Smckusick struct afile {
3817752Smckusick 	ino_t	fnum;		/* inode number of file */
3917752Smckusick 	char	*fname;		/* file name */
4017752Smckusick 	short	fflags;		/* extraction flags, if any */
4117752Smckusick 	char	ftype;		/* file type, e.g. LEAF or NODE */
4217752Smckusick };
4318003Smckusick struct arglist {
4418003Smckusick 	struct afile	*head;	/* start of argument list */
4518003Smckusick 	struct afile	*last;	/* end of argument list */
4618003Smckusick 	struct afile	*base;	/* current list arena */
4718003Smckusick 	int		nent;	/* maximum size of list */
4818003Smckusick 	char		*cmd;	/* the current command */
4918003Smckusick };
5017752Smckusick extern int fcmp();
5117752Smckusick extern char *fmtentry();
5217752Smckusick char *copynext();
5317752Smckusick 
5417752Smckusick /*
5517752Smckusick  * Read and execute commands from the terminal.
5617752Smckusick  */
5717752Smckusick runcmdshell()
5817752Smckusick {
5917752Smckusick 	register struct entry *np;
6017752Smckusick 	ino_t ino;
6118003Smckusick 	static struct arglist alist = { 0, 0, 0, 0, 0 };
6217752Smckusick 	char curdir[MAXPATHLEN];
6317752Smckusick 	char name[MAXPATHLEN];
6417752Smckusick 	char cmd[BUFSIZ];
6517752Smckusick 
6617752Smckusick 	canon("/", curdir);
6717752Smckusick loop:
6817752Smckusick 	if (setjmp(reset) != 0) {
6918003Smckusick 		for (; alist.head < alist.last; alist.head++)
7018003Smckusick 			freename(alist.head->fname);
7117752Smckusick 		nextarg = NULL;
7217752Smckusick 		volno = 0;
7317752Smckusick 	}
7418003Smckusick 	getcmd(curdir, cmd, name, &alist);
7517752Smckusick 	switch (cmd[0]) {
7617752Smckusick 	/*
7717752Smckusick 	 * Add elements to the extraction list.
7817752Smckusick 	 */
7917752Smckusick 	case 'a':
8029903Smckusick 		if (strncmp(cmd, "add", strlen(cmd)) != 0)
8129903Smckusick 			goto bad;
8217752Smckusick 		ino = dirlookup(name);
8317752Smckusick 		if (ino == 0)
8417752Smckusick 			break;
8517752Smckusick 		if (mflag)
8617752Smckusick 			pathcheck(name);
8717752Smckusick 		treescan(name, ino, addfile);
8817752Smckusick 		break;
8917752Smckusick 	/*
9017752Smckusick 	 * Change working directory.
9117752Smckusick 	 */
9217752Smckusick 	case 'c':
9329903Smckusick 		if (strncmp(cmd, "cd", strlen(cmd)) != 0)
9429903Smckusick 			goto bad;
9517752Smckusick 		ino = dirlookup(name);
9617752Smckusick 		if (ino == 0)
9717752Smckusick 			break;
9817752Smckusick 		if (inodetype(ino) == LEAF) {
9917752Smckusick 			fprintf(stderr, "%s: not a directory\n", name);
10017752Smckusick 			break;
10117752Smckusick 		}
10217752Smckusick 		(void) strcpy(curdir, name);
10317752Smckusick 		break;
10417752Smckusick 	/*
10517752Smckusick 	 * Delete elements from the extraction list.
10617752Smckusick 	 */
10717752Smckusick 	case 'd':
10829903Smckusick 		if (strncmp(cmd, "delete", strlen(cmd)) != 0)
10929903Smckusick 			goto bad;
11017752Smckusick 		np = lookupname(name);
11117752Smckusick 		if (np == NIL || (np->e_flags & NEW) == 0) {
11217752Smckusick 			fprintf(stderr, "%s: not on extraction list\n", name);
11317752Smckusick 			break;
11417752Smckusick 		}
11517752Smckusick 		treescan(name, np->e_ino, deletefile);
11617752Smckusick 		break;
11717752Smckusick 	/*
11817752Smckusick 	 * Extract the requested list.
11917752Smckusick 	 */
12017752Smckusick 	case 'e':
12129903Smckusick 		if (strncmp(cmd, "extract", strlen(cmd)) != 0)
12229903Smckusick 			goto bad;
12317752Smckusick 		createfiles();
12417752Smckusick 		createlinks();
12517752Smckusick 		setdirmodes();
12617752Smckusick 		if (dflag)
12717752Smckusick 			checkrestore();
12817752Smckusick 		volno = 0;
12917752Smckusick 		break;
13017752Smckusick 	/*
13117752Smckusick 	 * List available commands.
13217752Smckusick 	 */
13317752Smckusick 	case 'h':
13429903Smckusick 		if (strncmp(cmd, "help", strlen(cmd)) != 0)
13529903Smckusick 			goto bad;
13617752Smckusick 	case '?':
13729903Smckusick 		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
13817752Smckusick 			"Available commands are:\n",
13917752Smckusick 			"\tls [arg] - list directory\n",
14017752Smckusick 			"\tcd arg - change directory\n",
14117752Smckusick 			"\tpwd - print current directory\n",
14217752Smckusick 			"\tadd [arg] - add `arg' to list of",
14317752Smckusick 			" files to be extracted\n",
14417752Smckusick 			"\tdelete [arg] - delete `arg' from",
14517752Smckusick 			" list of files to be extracted\n",
14617752Smckusick 			"\textract - extract requested files\n",
14721096Smckusick 			"\tsetmodes - set modes of requested directories\n",
14817752Smckusick 			"\tquit - immediately exit program\n",
14929903Smckusick 			"\twhat - list dump header information\n",
15017752Smckusick 			"\tverbose - toggle verbose flag",
15117752Smckusick 			" (useful with ``ls'')\n",
15217752Smckusick 			"\thelp or `?' - print this list\n",
15317752Smckusick 			"If no `arg' is supplied, the current",
15417752Smckusick 			" directory is used\n");
15517752Smckusick 		break;
15617752Smckusick 	/*
15717752Smckusick 	 * List a directory.
15817752Smckusick 	 */
15917752Smckusick 	case 'l':
16029903Smckusick 		if (strncmp(cmd, "ls", strlen(cmd)) != 0)
16129903Smckusick 			goto bad;
16217752Smckusick 		ino = dirlookup(name);
16317752Smckusick 		if (ino == 0)
16417752Smckusick 			break;
16517752Smckusick 		printlist(name, ino, curdir);
16617752Smckusick 		break;
16717752Smckusick 	/*
16817752Smckusick 	 * Print current directory.
16917752Smckusick 	 */
17017752Smckusick 	case 'p':
17129903Smckusick 		if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
17229903Smckusick 			goto bad;
17317752Smckusick 		if (curdir[1] == '\0')
17417752Smckusick 			fprintf(stderr, "/\n");
17517752Smckusick 		else
17617752Smckusick 			fprintf(stderr, "%s\n", &curdir[1]);
17717752Smckusick 		break;
17817752Smckusick 	/*
17917752Smckusick 	 * Quit.
18017752Smckusick 	 */
18117752Smckusick 	case 'q':
18229903Smckusick 		if (strncmp(cmd, "quit", strlen(cmd)) != 0)
18329903Smckusick 			goto bad;
18429903Smckusick 		return;
18517752Smckusick 	case 'x':
18629903Smckusick 		if (strncmp(cmd, "xit", strlen(cmd)) != 0)
18729903Smckusick 			goto bad;
18817752Smckusick 		return;
18917752Smckusick 	/*
19017752Smckusick 	 * Toggle verbose mode.
19117752Smckusick 	 */
19217752Smckusick 	case 'v':
19329903Smckusick 		if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
19429903Smckusick 			goto bad;
19517752Smckusick 		if (vflag) {
19617752Smckusick 			fprintf(stderr, "verbose mode off\n");
19717752Smckusick 			vflag = 0;
19817752Smckusick 			break;
19917752Smckusick 		}
20017752Smckusick 		fprintf(stderr, "verbose mode on\n");
20117752Smckusick 		vflag++;
20217752Smckusick 		break;
20317752Smckusick 	/*
20417752Smckusick 	 * Just restore requested directory modes.
20517752Smckusick 	 */
20621096Smckusick 	case 's':
20729903Smckusick 		if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
20829903Smckusick 			goto bad;
20917752Smckusick 		setdirmodes();
21017752Smckusick 		break;
21117752Smckusick 	/*
21229903Smckusick 	 * Print out dump header information.
21329903Smckusick 	 */
21429903Smckusick 	case 'w':
21529903Smckusick 		if (strncmp(cmd, "what", strlen(cmd)) != 0)
21629903Smckusick 			goto bad;
21729903Smckusick 		printdumpinfo();
21829903Smckusick 		break;
21929903Smckusick 	/*
22017752Smckusick 	 * Turn on debugging.
22117752Smckusick 	 */
22217752Smckusick 	case 'D':
22329903Smckusick 		if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
22429903Smckusick 			goto bad;
22517752Smckusick 		if (dflag) {
22617752Smckusick 			fprintf(stderr, "debugging mode off\n");
22717752Smckusick 			dflag = 0;
22817752Smckusick 			break;
22917752Smckusick 		}
23017752Smckusick 		fprintf(stderr, "debugging mode on\n");
23117752Smckusick 		dflag++;
23217752Smckusick 		break;
23317752Smckusick 	/*
23417752Smckusick 	 * Unknown command.
23517752Smckusick 	 */
23617752Smckusick 	default:
23729903Smckusick 	bad:
23817752Smckusick 		fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
23917752Smckusick 		break;
24017752Smckusick 	}
24117752Smckusick 	goto loop;
24217752Smckusick }
24317752Smckusick 
24417752Smckusick /*
24517752Smckusick  * Read and parse an interactive command.
24617752Smckusick  * The first word on the line is assigned to "cmd". If
24717752Smckusick  * there are no arguments on the command line, then "curdir"
24817752Smckusick  * is returned as the argument. If there are arguments
24917752Smckusick  * on the line they are returned one at a time on each
25017752Smckusick  * successive call to getcmd. Each argument is first assigned
25117752Smckusick  * to "name". If it does not start with "/" the pathname in
25217752Smckusick  * "curdir" is prepended to it. Finally "canon" is called to
25317752Smckusick  * eliminate any embedded ".." components.
25417752Smckusick  */
25518003Smckusick getcmd(curdir, cmd, name, ap)
25617752Smckusick 	char *curdir, *cmd, *name;
25718003Smckusick 	struct arglist *ap;
25817752Smckusick {
25917752Smckusick 	register char *cp;
26017752Smckusick 	static char input[BUFSIZ];
26117752Smckusick 	char output[BUFSIZ];
26217752Smckusick #	define rawname input	/* save space by reusing input buffer */
26317752Smckusick 
26417752Smckusick 	/*
26517752Smckusick 	 * Check to see if still processing arguments.
26617752Smckusick 	 */
26718003Smckusick 	if (ap->head != ap->last) {
26818003Smckusick 		strcpy(name, ap->head->fname);
26918003Smckusick 		freename(ap->head->fname);
27018003Smckusick 		ap->head++;
27118003Smckusick 		return;
27218003Smckusick 	}
27317752Smckusick 	if (nextarg != NULL)
27417752Smckusick 		goto getnext;
27517752Smckusick 	/*
27617752Smckusick 	 * Read a command line and trim off trailing white space.
27717752Smckusick 	 */
27817752Smckusick 	do	{
27917752Smckusick 		fprintf(stderr, "restore > ");
28017752Smckusick 		(void) fflush(stderr);
28117752Smckusick 		(void) fgets(input, BUFSIZ, terminal);
28217752Smckusick 	} while (!feof(terminal) && input[0] == '\n');
28317752Smckusick 	if (feof(terminal)) {
28417752Smckusick 		(void) strcpy(cmd, "quit");
28517752Smckusick 		return;
28617752Smckusick 	}
28717752Smckusick 	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
28817752Smckusick 		/* trim off trailing white space and newline */;
28917752Smckusick 	*++cp = '\0';
29017752Smckusick 	/*
29117752Smckusick 	 * Copy the command into "cmd".
29217752Smckusick 	 */
29317752Smckusick 	cp = copynext(input, cmd);
29418003Smckusick 	ap->cmd = cmd;
29517752Smckusick 	/*
29617752Smckusick 	 * If no argument, use curdir as the default.
29717752Smckusick 	 */
29817752Smckusick 	if (*cp == '\0') {
29917752Smckusick 		(void) strcpy(name, curdir);
30017752Smckusick 		return;
30117752Smckusick 	}
30217752Smckusick 	nextarg = cp;
30317752Smckusick 	/*
30417752Smckusick 	 * Find the next argument.
30517752Smckusick 	 */
30617752Smckusick getnext:
30717752Smckusick 	cp = copynext(nextarg, rawname);
30817752Smckusick 	if (*cp == '\0')
30917752Smckusick 		nextarg = NULL;
31017752Smckusick 	else
31117752Smckusick 		nextarg = cp;
31217752Smckusick 	/*
31317752Smckusick 	 * If it an absolute pathname, canonicalize it and return it.
31417752Smckusick 	 */
31517752Smckusick 	if (rawname[0] == '/') {
31617752Smckusick 		canon(rawname, name);
31717752Smckusick 	} else {
31817752Smckusick 		/*
31917752Smckusick 		 * For relative pathnames, prepend the current directory to
32017752Smckusick 		 * it then canonicalize and return it.
32117752Smckusick 		 */
32217752Smckusick 		(void) strcpy(output, curdir);
32317752Smckusick 		(void) strcat(output, "/");
32417752Smckusick 		(void) strcat(output, rawname);
32517752Smckusick 		canon(output, name);
32617752Smckusick 	}
32718003Smckusick 	expandarg(name, ap);
32818003Smckusick 	strcpy(name, ap->head->fname);
32918003Smckusick 	freename(ap->head->fname);
33018003Smckusick 	ap->head++;
33117752Smckusick #	undef rawname
33217752Smckusick }
33317752Smckusick 
33417752Smckusick /*
33517752Smckusick  * Strip off the next token of the input.
33617752Smckusick  */
33717752Smckusick char *
33817752Smckusick copynext(input, output)
33917752Smckusick 	char *input, *output;
34017752Smckusick {
34117752Smckusick 	register char *cp, *bp;
34217752Smckusick 	char quote;
34317752Smckusick 
34417752Smckusick 	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
34517752Smckusick 		/* skip to argument */;
34617752Smckusick 	bp = output;
34717752Smckusick 	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
34817752Smckusick 		/*
34917752Smckusick 		 * Handle back slashes.
35017752Smckusick 		 */
35117752Smckusick 		if (*cp == '\\') {
35217752Smckusick 			if (*++cp == '\0') {
35317752Smckusick 				fprintf(stderr,
35417752Smckusick 					"command lines cannot be continued\n");
35517752Smckusick 				continue;
35617752Smckusick 			}
35717752Smckusick 			*bp++ = *cp++;
35817752Smckusick 			continue;
35917752Smckusick 		}
36017752Smckusick 		/*
36117752Smckusick 		 * The usual unquoted case.
36217752Smckusick 		 */
36317752Smckusick 		if (*cp != '\'' && *cp != '"') {
36417752Smckusick 			*bp++ = *cp++;
36517752Smckusick 			continue;
36617752Smckusick 		}
36717752Smckusick 		/*
36817752Smckusick 		 * Handle single and double quotes.
36917752Smckusick 		 */
37017752Smckusick 		quote = *cp++;
37117752Smckusick 		while (*cp != quote && *cp != '\0')
37217752Smckusick 			*bp++ = *cp++ | 0200;
37317752Smckusick 		if (*cp++ == '\0') {
37417752Smckusick 			fprintf(stderr, "missing %c\n", quote);
37517752Smckusick 			cp--;
37617752Smckusick 			continue;
37717752Smckusick 		}
37817752Smckusick 	}
37917752Smckusick 	*bp = '\0';
38017752Smckusick 	return (cp);
38117752Smckusick }
38217752Smckusick 
38317752Smckusick /*
38417752Smckusick  * Canonicalize file names to always start with ``./'' and
38518003Smckusick  * remove any imbedded "." and ".." components.
38617752Smckusick  */
38717752Smckusick canon(rawname, canonname)
38817752Smckusick 	char *rawname, *canonname;
38917752Smckusick {
39017752Smckusick 	register char *cp, *np;
39117752Smckusick 	int len;
39217752Smckusick 
39317752Smckusick 	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
39417752Smckusick 		(void) strcpy(canonname, "");
39517752Smckusick 	else if (rawname[0] == '/')
39617752Smckusick 		(void) strcpy(canonname, ".");
39717752Smckusick 	else
39817752Smckusick 		(void) strcpy(canonname, "./");
39917752Smckusick 	(void) strcat(canonname, rawname);
40017752Smckusick 	/*
40118003Smckusick 	 * Eliminate multiple and trailing '/'s
40217752Smckusick 	 */
40318003Smckusick 	for (cp = np = canonname; *np != '\0'; cp++) {
40418003Smckusick 		*cp = *np++;
40518003Smckusick 		while (*cp == '/' && *np == '/')
40618003Smckusick 			np++;
40718003Smckusick 	}
40818003Smckusick 	*cp = '\0';
40918003Smckusick 	if (*--cp == '/')
41018003Smckusick 		*cp = '\0';
41118003Smckusick 	/*
41218003Smckusick 	 * Eliminate extraneous "." and ".." from pathnames.
41318003Smckusick 	 */
41417752Smckusick 	for (np = canonname; *np != '\0'; ) {
41517752Smckusick 		np++;
41617752Smckusick 		cp = np;
41717752Smckusick 		while (*np != '/' && *np != '\0')
41817752Smckusick 			np++;
41918003Smckusick 		if (np - cp == 1 && *cp == '.') {
42018003Smckusick 			cp--;
42118003Smckusick 			(void) strcpy(cp, np);
42218003Smckusick 			np = cp;
42318003Smckusick 		}
42417752Smckusick 		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
42517752Smckusick 			cp--;
42617752Smckusick 			while (cp > &canonname[1] && *--cp != '/')
42717752Smckusick 				/* find beginning of name */;
42817752Smckusick 			(void) strcpy(cp, np);
42917752Smckusick 			np = cp;
43017752Smckusick 		}
43117752Smckusick 	}
43217752Smckusick }
43317752Smckusick 
43417752Smckusick /*
43517756Smckusick  * globals (file name generation)
43617756Smckusick  *
43717756Smckusick  * "*" in params matches r.e ".*"
43817756Smckusick  * "?" in params matches r.e. "."
43917756Smckusick  * "[...]" in params matches character class
44017756Smckusick  * "[...a-z...]" in params matches a through z.
44117756Smckusick  */
44218003Smckusick expandarg(arg, ap)
44317756Smckusick 	char *arg;
44418003Smckusick 	register struct arglist *ap;
44517756Smckusick {
44620850Smckusick 	static struct afile single;
44730950Smckusick 	struct entry *ep;
44817756Smckusick 	int size;
44917756Smckusick 
45018003Smckusick 	ap->head = ap->last = (struct afile *)0;
45118003Smckusick 	size = expand(arg, 0, ap);
45217756Smckusick 	if (size == 0) {
45330950Smckusick 		ep = lookupname(arg);
45430950Smckusick 		single.fnum = ep ? ep->e_ino : 0;
45518003Smckusick 		single.fname = savename(arg);
45618003Smckusick 		ap->head = &single;
45718003Smckusick 		ap->last = ap->head + 1;
45818003Smckusick 		return;
45917756Smckusick 	}
46018003Smckusick 	qsort((char *)ap->head, ap->last - ap->head, sizeof *ap->head, fcmp);
46117756Smckusick }
46217756Smckusick 
46317756Smckusick /*
46417756Smckusick  * Expand a file name
46517756Smckusick  */
46618003Smckusick expand(as, rflg, ap)
46717756Smckusick 	char *as;
46817756Smckusick 	int rflg;
46918003Smckusick 	register struct arglist *ap;
47017756Smckusick {
47117756Smckusick 	int		count, size;
47217756Smckusick 	char		dir = 0;
47317756Smckusick 	char		*rescan = 0;
47417756Smckusick 	DIR		*dirp;
47517756Smckusick 	register char	*s, *cs;
47618003Smckusick 	int		sindex, rindex, lindex;
47717756Smckusick 	struct direct	*dp;
47817756Smckusick 	register char	slash;
47917756Smckusick 	register char	*rs;
48017756Smckusick 	register char	c;
48117756Smckusick 
48217756Smckusick 	/*
48317756Smckusick 	 * check for meta chars
48417756Smckusick 	 */
48517756Smckusick 	s = cs = as;
48617756Smckusick 	slash = 0;
48717756Smckusick 	while (*cs != '*' && *cs != '?' && *cs != '[') {
48818003Smckusick 		if (*cs++ == 0) {
48917756Smckusick 			if (rflg && slash)
49017756Smckusick 				break;
49117756Smckusick 			else
49217756Smckusick 				return (0) ;
49318003Smckusick 		} else if (*cs == '/') {
49417756Smckusick 			slash++;
49517756Smckusick 		}
49617756Smckusick 	}
49717756Smckusick 	for (;;) {
49817756Smckusick 		if (cs == s) {
49918003Smckusick 			s = "";
50017756Smckusick 			break;
50117756Smckusick 		} else if (*--cs == '/') {
50217756Smckusick 			*cs = 0;
50317756Smckusick 			if (s == cs)
50417756Smckusick 				s = "/";
50517756Smckusick 			break;
50617756Smckusick 		}
50717756Smckusick 	}
50817756Smckusick 	if ((dirp = rst_opendir(s)) != NULL)
50917756Smckusick 		dir++;
51017756Smckusick 	count = 0;
51117756Smckusick 	if (*cs == 0)
51218003Smckusick 		*cs++ = 0200;
51317756Smckusick 	if (dir) {
51417756Smckusick 		/*
51517756Smckusick 		 * check for rescan
51617756Smckusick 		 */
51717756Smckusick 		rs = cs;
51817756Smckusick 		do {
51917756Smckusick 			if (*rs == '/') {
52017756Smckusick 				rescan = rs;
52117756Smckusick 				*rs = 0;
52217756Smckusick 			}
52317756Smckusick 		} while (*rs++);
52418003Smckusick 		sindex = ap->last - ap->head;
52517756Smckusick 		while ((dp = rst_readdir(dirp)) != NULL && dp->d_ino != 0) {
52617756Smckusick 			if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
52717756Smckusick 				continue;
52817756Smckusick 			if ((*dp->d_name == '.' && *cs != '.'))
52917756Smckusick 				continue;
53017756Smckusick 			if (gmatch(dp->d_name, cs)) {
53118003Smckusick 				if (addg(dp, s, rescan, ap) < 0)
53217756Smckusick 					return (-1);
53317756Smckusick 				count++;
53417756Smckusick 			}
53517756Smckusick 		}
53617756Smckusick 		if (rescan) {
53718003Smckusick 			rindex = sindex;
53818003Smckusick 			lindex = ap->last - ap->head;
53917756Smckusick 			if (count) {
54017756Smckusick 				count = 0;
54118003Smckusick 				while (rindex < lindex) {
54218003Smckusick 					size = expand(ap->head[rindex].fname,
54318003Smckusick 					    1, ap);
54417756Smckusick 					if (size < 0)
54517756Smckusick 						return (size);
54617756Smckusick 					count += size;
54718003Smckusick 					rindex++;
54817756Smckusick 				}
54917756Smckusick 			}
55018003Smckusick 			bcopy((char *)&ap->head[lindex],
55118003Smckusick 			     (char *)&ap->head[sindex],
55218003Smckusick 			     (ap->last - &ap->head[rindex]) * sizeof *ap->head);
55318003Smckusick 			ap->last -= lindex - sindex;
55417756Smckusick 			*rescan = '/';
55517756Smckusick 		}
55617756Smckusick 	}
55717756Smckusick 	s = as;
55817756Smckusick 	while (c = *s)
55917756Smckusick 		*s++ = (c&0177 ? c : '/');
56017756Smckusick 	return (count);
56117756Smckusick }
56217756Smckusick 
56317756Smckusick /*
56417756Smckusick  * Check for a name match
56517756Smckusick  */
56617756Smckusick gmatch(s, p)
56717756Smckusick 	register char	*s, *p;
56817756Smckusick {
56917756Smckusick 	register int	scc;
57017756Smckusick 	char		c;
57117756Smckusick 	char		ok;
57217756Smckusick 	int		lc;
57317756Smckusick 
57417756Smckusick 	if (scc = *s++)
57517756Smckusick 		if ((scc &= 0177) == 0)
57617756Smckusick 			scc = 0200;
57717756Smckusick 	switch (c = *p++) {
57817756Smckusick 
57917756Smckusick 	case '[':
58017756Smckusick 		ok = 0;
58117756Smckusick 		lc = 077777;
58217756Smckusick 		while (c = *p++) {
58318003Smckusick 			if (c == ']') {
58417756Smckusick 				return (ok ? gmatch(s, p) : 0);
58517756Smckusick 			} else if (c == '-') {
58617756Smckusick 				if (lc <= scc && scc <= (*p++))
58717756Smckusick 					ok++ ;
58817756Smckusick 			} else {
58917756Smckusick 				if (scc == (lc = (c&0177)))
59017756Smckusick 					ok++ ;
59117756Smckusick 			}
59217756Smckusick 		}
59317756Smckusick 		return (0);
59417756Smckusick 
59517756Smckusick 	default:
59617756Smckusick 		if ((c&0177) != scc)
59717756Smckusick 			return (0) ;
59817756Smckusick 		/* falls through */
59917756Smckusick 
60017756Smckusick 	case '?':
60117756Smckusick 		return (scc ? gmatch(s, p) : 0);
60217756Smckusick 
60317756Smckusick 	case '*':
60417756Smckusick 		if (*p == 0)
60517756Smckusick 			return (1) ;
60617756Smckusick 		s--;
60717756Smckusick 		while (*s) {
60817756Smckusick 			if (gmatch(s++, p))
60917756Smckusick 				return (1);
61017756Smckusick 		}
61117756Smckusick 		return (0);
61217756Smckusick 
61317756Smckusick 	case 0:
61417756Smckusick 		return (scc == 0);
61517756Smckusick 	}
61617756Smckusick }
61717756Smckusick 
61817756Smckusick /*
61917756Smckusick  * Construct a matched name.
62017756Smckusick  */
62118003Smckusick addg(dp, as1, as3, ap)
62218003Smckusick 	struct direct	*dp;
62318003Smckusick 	char		*as1, *as3;
62418003Smckusick 	struct arglist	*ap;
62517756Smckusick {
62617756Smckusick 	register char	*s1, *s2;
62717756Smckusick 	register int	c;
62818003Smckusick 	char		buf[BUFSIZ];
62917756Smckusick 
63018003Smckusick 	s2 = buf;
63117756Smckusick 	s1 = as1;
63217756Smckusick 	while (c = *s1++) {
63317756Smckusick 		if ((c &= 0177) == 0) {
63418003Smckusick 			*s2++ = '/';
63517756Smckusick 			break;
63617756Smckusick 		}
63717756Smckusick 		*s2++ = c;
63817756Smckusick 	}
63918003Smckusick 	s1 = dp->d_name;
64017756Smckusick 	while (*s2 = *s1++)
64117756Smckusick 		s2++;
64217756Smckusick 	if (s1 = as3) {
64317756Smckusick 		*s2++ = '/';
64417756Smckusick 		while (*s2++ = *++s1)
64517756Smckusick 			/* void */;
64617756Smckusick 	}
64718003Smckusick 	if (mkentry(buf, dp->d_ino, ap) == FAIL)
64818003Smckusick 		return (-1);
64917756Smckusick }
65017756Smckusick 
65117756Smckusick /*
65217752Smckusick  * Do an "ls" style listing of a directory
65317752Smckusick  */
65417752Smckusick printlist(name, ino, basename)
65517752Smckusick 	char *name;
65617752Smckusick 	ino_t ino;
65717752Smckusick 	char *basename;
65817752Smckusick {
65917752Smckusick 	register struct afile *fp;
66018003Smckusick 	register struct direct *dp;
66118003Smckusick 	static struct arglist alist = { 0, 0, 0, 0, "ls" };
66217752Smckusick 	struct afile single;
66317752Smckusick 	DIR *dirp;
66417752Smckusick 
66517752Smckusick 	if ((dirp = rst_opendir(name)) == NULL) {
66617752Smckusick 		single.fnum = ino;
66718003Smckusick 		single.fname = savename(name + strlen(basename) + 1);
66818003Smckusick 		alist.head = &single;
66918003Smckusick 		alist.last = alist.head + 1;
67017752Smckusick 	} else {
67118003Smckusick 		alist.head = (struct afile *)0;
67218003Smckusick 		fprintf(stderr, "%s:\n", name);
67318003Smckusick 		while (dp = rst_readdir(dirp)) {
67418003Smckusick 			if (dp == NULL || dp->d_ino == 0)
67518003Smckusick 				break;
67618003Smckusick 			if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
67718003Smckusick 				continue;
67818003Smckusick 			if (vflag == 0 &&
67918003Smckusick 			    (strcmp(dp->d_name, ".") == 0 ||
68018003Smckusick 			     strcmp(dp->d_name, "..") == 0))
68118003Smckusick 				continue;
68218003Smckusick 			if (!mkentry(dp->d_name, dp->d_ino, &alist))
68318003Smckusick 				return;
68418003Smckusick 		}
68517752Smckusick 	}
68618003Smckusick 	if (alist.head != 0) {
68718003Smckusick 		qsort((char *)alist.head, alist.last - alist.head,
68818003Smckusick 			sizeof *alist.head, fcmp);
68918003Smckusick 		formatf(&alist);
69018003Smckusick 		for (fp = alist.head; fp < alist.last; fp++)
69118003Smckusick 			freename(fp->fname);
69218003Smckusick 	}
69318003Smckusick 	if (dirp != NULL)
69418003Smckusick 		fprintf(stderr, "\n");
69517752Smckusick }
69617752Smckusick 
69717752Smckusick /*
69817752Smckusick  * Read the contents of a directory.
69917752Smckusick  */
70018003Smckusick mkentry(name, ino, ap)
70118003Smckusick 	char *name;
70218003Smckusick 	ino_t ino;
70318003Smckusick 	register struct arglist *ap;
70417752Smckusick {
70517752Smckusick 	register struct afile *fp;
70617752Smckusick 
70718003Smckusick 	if (ap->base == NULL) {
70818003Smckusick 		ap->nent = 20;
70918003Smckusick 		ap->base = (struct afile *)calloc((unsigned)ap->nent,
71017752Smckusick 			sizeof (struct afile));
71118003Smckusick 		if (ap->base == NULL) {
71218003Smckusick 			fprintf(stderr, "%s: out of memory\n", ap->cmd);
71317752Smckusick 			return (FAIL);
71417752Smckusick 		}
71517752Smckusick 	}
71618003Smckusick 	if (ap->head == 0)
71718003Smckusick 		ap->head = ap->last = ap->base;
71818003Smckusick 	fp = ap->last;
71918003Smckusick 	fp->fnum = ino;
72018003Smckusick 	fp->fname = savename(name);
72118003Smckusick 	fp++;
72218003Smckusick 	if (fp == ap->head + ap->nent) {
72318003Smckusick 		ap->base = (struct afile *)realloc((char *)ap->base,
72418003Smckusick 		    (unsigned)(2 * ap->nent * sizeof (struct afile)));
72518003Smckusick 		if (ap->base == 0) {
72618003Smckusick 			fprintf(stderr, "%s: out of memory\n", ap->cmd);
72718003Smckusick 			return (FAIL);
72817752Smckusick 		}
72918003Smckusick 		ap->head = ap->base;
73018003Smckusick 		fp = ap->head + ap->nent;
73118003Smckusick 		ap->nent *= 2;
73217752Smckusick 	}
73318003Smckusick 	ap->last = fp;
73417752Smckusick 	return (GOOD);
73517752Smckusick }
73617752Smckusick 
73717752Smckusick /*
73817752Smckusick  * Print out a pretty listing of a directory
73917752Smckusick  */
74018003Smckusick formatf(ap)
74118003Smckusick 	register struct arglist *ap;
74217752Smckusick {
74317752Smckusick 	register struct afile *fp;
74417752Smckusick 	struct entry *np;
74518003Smckusick 	int width = 0, w, nentry = ap->last - ap->head;
74617752Smckusick 	int i, j, len, columns, lines;
74717752Smckusick 	char *cp;
74817752Smckusick 
74918003Smckusick 	if (ap->head == ap->last)
75017752Smckusick 		return;
75118003Smckusick 	for (fp = ap->head; fp < ap->last; fp++) {
75217752Smckusick 		fp->ftype = inodetype(fp->fnum);
75317752Smckusick 		np = lookupino(fp->fnum);
75417752Smckusick 		if (np != NIL)
75517752Smckusick 			fp->fflags = np->e_flags;
75617752Smckusick 		else
75717752Smckusick 			fp->fflags = 0;
75817752Smckusick 		len = strlen(fmtentry(fp));
75917752Smckusick 		if (len > width)
76017752Smckusick 			width = len;
76117752Smckusick 	}
76217752Smckusick 	width += 2;
76317752Smckusick 	columns = 80 / width;
76417752Smckusick 	if (columns == 0)
76517752Smckusick 		columns = 1;
76617752Smckusick 	lines = (nentry + columns - 1) / columns;
76717752Smckusick 	for (i = 0; i < lines; i++) {
76817752Smckusick 		for (j = 0; j < columns; j++) {
76918003Smckusick 			fp = ap->head + j * lines + i;
77017752Smckusick 			cp = fmtentry(fp);
77117752Smckusick 			fprintf(stderr, "%s", cp);
77218003Smckusick 			if (fp + lines >= ap->last) {
77317752Smckusick 				fprintf(stderr, "\n");
77417752Smckusick 				break;
77517752Smckusick 			}
77617752Smckusick 			w = strlen(cp);
77717752Smckusick 			while (w < width) {
77817752Smckusick 				w++;
77917752Smckusick 				fprintf(stderr, " ");
78017752Smckusick 			}
78117752Smckusick 		}
78217752Smckusick 	}
78317752Smckusick }
78417752Smckusick 
78517752Smckusick /*
78617752Smckusick  * Comparison routine for qsort.
78717752Smckusick  */
78817752Smckusick fcmp(f1, f2)
78917752Smckusick 	register struct afile *f1, *f2;
79017752Smckusick {
79117752Smckusick 
79217752Smckusick 	return (strcmp(f1->fname, f2->fname));
79317752Smckusick }
79417752Smckusick 
79517752Smckusick /*
79617752Smckusick  * Format a directory entry.
79717752Smckusick  */
79817752Smckusick char *
79917752Smckusick fmtentry(fp)
80017752Smckusick 	register struct afile *fp;
80117752Smckusick {
80217752Smckusick 	static char fmtres[BUFSIZ];
80323982Smckusick 	static int precision = 0;
80423982Smckusick 	int i;
80517752Smckusick 	register char *cp, *dp;
80617752Smckusick 
80723982Smckusick 	if (!vflag) {
80817752Smckusick 		fmtres[0] = '\0';
80923982Smckusick 	} else {
81023982Smckusick 		if (precision == 0)
81123982Smckusick 			for (i = maxino; i > 0; i /= 10)
81223982Smckusick 				precision++;
81323982Smckusick 		(void) sprintf(fmtres, "%*d ", precision, fp->fnum);
81423982Smckusick 	}
81517752Smckusick 	dp = &fmtres[strlen(fmtres)];
81617752Smckusick 	if (dflag && BIT(fp->fnum, dumpmap) == 0)
81717752Smckusick 		*dp++ = '^';
81817752Smckusick 	else if ((fp->fflags & NEW) != 0)
81917752Smckusick 		*dp++ = '*';
82017752Smckusick 	else
82117752Smckusick 		*dp++ = ' ';
82217752Smckusick 	for (cp = fp->fname; *cp; cp++)
82317752Smckusick 		if (!vflag && (*cp < ' ' || *cp >= 0177))
82417752Smckusick 			*dp++ = '?';
82517752Smckusick 		else
82617752Smckusick 			*dp++ = *cp;
82717752Smckusick 	if (fp->ftype == NODE)
82817752Smckusick 		*dp++ = '/';
82917752Smckusick 	*dp++ = 0;
83017752Smckusick 	return (fmtres);
83117752Smckusick }
83217752Smckusick 
83317752Smckusick /*
83417752Smckusick  * respond to interrupts
83517752Smckusick  */
836*39942Sbostic void
83717752Smckusick onintr()
83817752Smckusick {
83917752Smckusick 	if (command == 'i')
84017752Smckusick 		longjmp(reset, 1);
84117752Smckusick 	if (reply("restore interrupted, continue") == FAIL)
84217752Smckusick 		done(1);
84317752Smckusick }
844