xref: /csrg-svn/sbin/restore/interactive.c (revision 56567)
121166Sdist /*
236105Sbostic  * Copyright (c) 1985 The Regents of the University of California.
336105Sbostic  * All rights reserved.
436105Sbostic  *
542708Sbostic  * %sccs.include.redist.c%
621166Sdist  */
717752Smckusick 
817752Smckusick #ifndef lint
9*56567Sbostic static char sccsid[] = "@(#)interactive.c	5.17 (Berkeley) 10/16/92";
1036105Sbostic #endif /* not lint */
1117752Smckusick 
12*56567Sbostic #include <sys/param.h>
13*56567Sbostic #include <sys/time.h>
14*56567Sbostic 
15*56567Sbostic #include <ufs/ffs/fs.h>
16*56567Sbostic #include <ufs/ufs/dinode.h>
17*56567Sbostic #include <ufs/ufs/dir.h>
1823546Smckusick #include <protocols/dumprestore.h>
19*56567Sbostic 
2017752Smckusick #include <setjmp.h>
21*56567Sbostic #include <stdio.h>
22*56567Sbostic #include <stdlib.h>
23*56567Sbostic #include <string.h>
2417752Smckusick 
25*56567Sbostic #include "restore.h"
26*56567Sbostic #include "extern.h"
27*56567Sbostic 
2817756Smckusick #define round(a, b) (((a) + (b) - 1) / (b) * (b))
2917756Smckusick 
3017752Smckusick /*
3117752Smckusick  * Things to handle interruptions.
3217752Smckusick  */
3356429Smckusick static int runshell;
3417752Smckusick static jmp_buf reset;
3517752Smckusick static char *nextarg = NULL;
3617752Smckusick 
3717752Smckusick /*
3817752Smckusick  * Structure and routines associated with listing directories.
3917752Smckusick  */
4017752Smckusick struct afile {
4117752Smckusick 	ino_t	fnum;		/* inode number of file */
4217752Smckusick 	char	*fname;		/* file name */
4317752Smckusick 	short	fflags;		/* extraction flags, if any */
4417752Smckusick 	char	ftype;		/* file type, e.g. LEAF or NODE */
4554593Smckusick 	char	finotype;	/* file type specified in directory entry */
4617752Smckusick };
4718003Smckusick struct arglist {
4818003Smckusick 	struct afile	*head;	/* start of argument list */
4918003Smckusick 	struct afile	*last;	/* end of argument list */
5018003Smckusick 	struct afile	*base;	/* current list arena */
5118003Smckusick 	int		nent;	/* maximum size of list */
5218003Smckusick 	char		*cmd;	/* the current command */
5318003Smckusick };
5417752Smckusick 
55*56567Sbostic static int	 addg __P((struct direct *, char *, char *, struct arglist *));
56*56567Sbostic static char	*copynext __P((char *, char *));
57*56567Sbostic static int	 expand __P((char *, int, struct arglist *));
58*56567Sbostic static void	 expandarg __P((char *, struct arglist *));
59*56567Sbostic static int	 fcmp __P((const void *, const void *));
60*56567Sbostic static char	*fmtentry __P((struct afile *));
61*56567Sbostic static void	 formatf __P((struct arglist *));
62*56567Sbostic static void	 getcmd __P((char *, char *, char *, struct arglist *));
63*56567Sbostic static int	 gmatch __P((char *, char *));
64*56567Sbostic static int	 mkentry __P((char *, struct direct *, struct arglist *));
65*56567Sbostic static void	 printlist __P((char *, ino_t, char *));
66*56567Sbostic 
6717752Smckusick /*
6817752Smckusick  * Read and execute commands from the terminal.
6917752Smckusick  */
70*56567Sbostic void
7117752Smckusick runcmdshell()
7217752Smckusick {
7317752Smckusick 	register struct entry *np;
7417752Smckusick 	ino_t ino;
7518003Smckusick 	static struct arglist alist = { 0, 0, 0, 0, 0 };
7617752Smckusick 	char curdir[MAXPATHLEN];
7717752Smckusick 	char name[MAXPATHLEN];
7817752Smckusick 	char cmd[BUFSIZ];
7917752Smckusick 
8017752Smckusick 	canon("/", curdir);
8117752Smckusick loop:
8217752Smckusick 	if (setjmp(reset) != 0) {
8318003Smckusick 		for (; alist.head < alist.last; alist.head++)
8418003Smckusick 			freename(alist.head->fname);
8517752Smckusick 		nextarg = NULL;
8617752Smckusick 		volno = 0;
8717752Smckusick 	}
8856429Smckusick 	runshell = 1;
8918003Smckusick 	getcmd(curdir, cmd, name, &alist);
9017752Smckusick 	switch (cmd[0]) {
9117752Smckusick 	/*
9217752Smckusick 	 * Add elements to the extraction list.
9317752Smckusick 	 */
9417752Smckusick 	case 'a':
9529903Smckusick 		if (strncmp(cmd, "add", strlen(cmd)) != 0)
9629903Smckusick 			goto bad;
9717752Smckusick 		ino = dirlookup(name);
9817752Smckusick 		if (ino == 0)
9917752Smckusick 			break;
10017752Smckusick 		if (mflag)
10117752Smckusick 			pathcheck(name);
10217752Smckusick 		treescan(name, ino, addfile);
10317752Smckusick 		break;
10417752Smckusick 	/*
10517752Smckusick 	 * Change working directory.
10617752Smckusick 	 */
10717752Smckusick 	case 'c':
10829903Smckusick 		if (strncmp(cmd, "cd", strlen(cmd)) != 0)
10929903Smckusick 			goto bad;
11017752Smckusick 		ino = dirlookup(name);
11117752Smckusick 		if (ino == 0)
11217752Smckusick 			break;
11317752Smckusick 		if (inodetype(ino) == LEAF) {
11417752Smckusick 			fprintf(stderr, "%s: not a directory\n", name);
11517752Smckusick 			break;
11617752Smckusick 		}
11717752Smckusick 		(void) strcpy(curdir, name);
11817752Smckusick 		break;
11917752Smckusick 	/*
12017752Smckusick 	 * Delete elements from the extraction list.
12117752Smckusick 	 */
12217752Smckusick 	case 'd':
12329903Smckusick 		if (strncmp(cmd, "delete", strlen(cmd)) != 0)
12429903Smckusick 			goto bad;
12517752Smckusick 		np = lookupname(name);
126*56567Sbostic 		if (np == NULL || (np->e_flags & NEW) == 0) {
12717752Smckusick 			fprintf(stderr, "%s: not on extraction list\n", name);
12817752Smckusick 			break;
12917752Smckusick 		}
13017752Smckusick 		treescan(name, np->e_ino, deletefile);
13117752Smckusick 		break;
13217752Smckusick 	/*
13317752Smckusick 	 * Extract the requested list.
13417752Smckusick 	 */
13517752Smckusick 	case 'e':
13629903Smckusick 		if (strncmp(cmd, "extract", strlen(cmd)) != 0)
13729903Smckusick 			goto bad;
13817752Smckusick 		createfiles();
13917752Smckusick 		createlinks();
14055880Smckusick 		setdirmodes(0);
14117752Smckusick 		if (dflag)
14217752Smckusick 			checkrestore();
14317752Smckusick 		volno = 0;
14417752Smckusick 		break;
14517752Smckusick 	/*
14617752Smckusick 	 * List available commands.
14717752Smckusick 	 */
14817752Smckusick 	case 'h':
14929903Smckusick 		if (strncmp(cmd, "help", strlen(cmd)) != 0)
15029903Smckusick 			goto bad;
15117752Smckusick 	case '?':
15229903Smckusick 		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
15317752Smckusick 			"Available commands are:\n",
15417752Smckusick 			"\tls [arg] - list directory\n",
15517752Smckusick 			"\tcd arg - change directory\n",
15617752Smckusick 			"\tpwd - print current directory\n",
15717752Smckusick 			"\tadd [arg] - add `arg' to list of",
15817752Smckusick 			" files to be extracted\n",
15917752Smckusick 			"\tdelete [arg] - delete `arg' from",
16017752Smckusick 			" list of files to be extracted\n",
16117752Smckusick 			"\textract - extract requested files\n",
16221096Smckusick 			"\tsetmodes - set modes of requested directories\n",
16317752Smckusick 			"\tquit - immediately exit program\n",
16429903Smckusick 			"\twhat - list dump header information\n",
16517752Smckusick 			"\tverbose - toggle verbose flag",
16617752Smckusick 			" (useful with ``ls'')\n",
16717752Smckusick 			"\thelp or `?' - print this list\n",
16817752Smckusick 			"If no `arg' is supplied, the current",
16917752Smckusick 			" directory is used\n");
17017752Smckusick 		break;
17117752Smckusick 	/*
17217752Smckusick 	 * List a directory.
17317752Smckusick 	 */
17417752Smckusick 	case 'l':
17529903Smckusick 		if (strncmp(cmd, "ls", strlen(cmd)) != 0)
17629903Smckusick 			goto bad;
17717752Smckusick 		ino = dirlookup(name);
17817752Smckusick 		if (ino == 0)
17917752Smckusick 			break;
18017752Smckusick 		printlist(name, ino, curdir);
18117752Smckusick 		break;
18217752Smckusick 	/*
18317752Smckusick 	 * Print current directory.
18417752Smckusick 	 */
18517752Smckusick 	case 'p':
18629903Smckusick 		if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
18729903Smckusick 			goto bad;
18817752Smckusick 		if (curdir[1] == '\0')
18917752Smckusick 			fprintf(stderr, "/\n");
19017752Smckusick 		else
19117752Smckusick 			fprintf(stderr, "%s\n", &curdir[1]);
19217752Smckusick 		break;
19317752Smckusick 	/*
19417752Smckusick 	 * Quit.
19517752Smckusick 	 */
19617752Smckusick 	case 'q':
19729903Smckusick 		if (strncmp(cmd, "quit", strlen(cmd)) != 0)
19829903Smckusick 			goto bad;
19929903Smckusick 		return;
20017752Smckusick 	case 'x':
20129903Smckusick 		if (strncmp(cmd, "xit", strlen(cmd)) != 0)
20229903Smckusick 			goto bad;
20317752Smckusick 		return;
20417752Smckusick 	/*
20517752Smckusick 	 * Toggle verbose mode.
20617752Smckusick 	 */
20717752Smckusick 	case 'v':
20829903Smckusick 		if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
20929903Smckusick 			goto bad;
21017752Smckusick 		if (vflag) {
21117752Smckusick 			fprintf(stderr, "verbose mode off\n");
21217752Smckusick 			vflag = 0;
21317752Smckusick 			break;
21417752Smckusick 		}
21517752Smckusick 		fprintf(stderr, "verbose mode on\n");
21617752Smckusick 		vflag++;
21717752Smckusick 		break;
21817752Smckusick 	/*
21917752Smckusick 	 * Just restore requested directory modes.
22017752Smckusick 	 */
22121096Smckusick 	case 's':
22229903Smckusick 		if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
22329903Smckusick 			goto bad;
22455880Smckusick 		setdirmodes(FORCE);
22517752Smckusick 		break;
22617752Smckusick 	/*
22729903Smckusick 	 * Print out dump header information.
22829903Smckusick 	 */
22929903Smckusick 	case 'w':
23029903Smckusick 		if (strncmp(cmd, "what", strlen(cmd)) != 0)
23129903Smckusick 			goto bad;
23229903Smckusick 		printdumpinfo();
23329903Smckusick 		break;
23429903Smckusick 	/*
23517752Smckusick 	 * Turn on debugging.
23617752Smckusick 	 */
23717752Smckusick 	case 'D':
23829903Smckusick 		if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
23929903Smckusick 			goto bad;
24017752Smckusick 		if (dflag) {
24117752Smckusick 			fprintf(stderr, "debugging mode off\n");
24217752Smckusick 			dflag = 0;
24317752Smckusick 			break;
24417752Smckusick 		}
24517752Smckusick 		fprintf(stderr, "debugging mode on\n");
24617752Smckusick 		dflag++;
24717752Smckusick 		break;
24817752Smckusick 	/*
24917752Smckusick 	 * Unknown command.
25017752Smckusick 	 */
25117752Smckusick 	default:
25229903Smckusick 	bad:
25317752Smckusick 		fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
25417752Smckusick 		break;
25517752Smckusick 	}
25617752Smckusick 	goto loop;
25717752Smckusick }
25817752Smckusick 
25917752Smckusick /*
26017752Smckusick  * Read and parse an interactive command.
26117752Smckusick  * The first word on the line is assigned to "cmd". If
26217752Smckusick  * there are no arguments on the command line, then "curdir"
26317752Smckusick  * is returned as the argument. If there are arguments
26417752Smckusick  * on the line they are returned one at a time on each
26517752Smckusick  * successive call to getcmd. Each argument is first assigned
26617752Smckusick  * to "name". If it does not start with "/" the pathname in
26717752Smckusick  * "curdir" is prepended to it. Finally "canon" is called to
26817752Smckusick  * eliminate any embedded ".." components.
26917752Smckusick  */
270*56567Sbostic static void
27118003Smckusick getcmd(curdir, cmd, name, ap)
27217752Smckusick 	char *curdir, *cmd, *name;
27318003Smckusick 	struct arglist *ap;
27417752Smckusick {
27517752Smckusick 	register char *cp;
27617752Smckusick 	static char input[BUFSIZ];
27717752Smckusick 	char output[BUFSIZ];
27817752Smckusick #	define rawname input	/* save space by reusing input buffer */
27917752Smckusick 
28017752Smckusick 	/*
28117752Smckusick 	 * Check to see if still processing arguments.
28217752Smckusick 	 */
28318003Smckusick 	if (ap->head != ap->last) {
28418003Smckusick 		strcpy(name, ap->head->fname);
28518003Smckusick 		freename(ap->head->fname);
28618003Smckusick 		ap->head++;
28718003Smckusick 		return;
28818003Smckusick 	}
28917752Smckusick 	if (nextarg != NULL)
29017752Smckusick 		goto getnext;
29117752Smckusick 	/*
29217752Smckusick 	 * Read a command line and trim off trailing white space.
29317752Smckusick 	 */
29417752Smckusick 	do	{
29517752Smckusick 		fprintf(stderr, "restore > ");
29617752Smckusick 		(void) fflush(stderr);
29717752Smckusick 		(void) fgets(input, BUFSIZ, terminal);
29817752Smckusick 	} while (!feof(terminal) && input[0] == '\n');
29917752Smckusick 	if (feof(terminal)) {
30017752Smckusick 		(void) strcpy(cmd, "quit");
30117752Smckusick 		return;
30217752Smckusick 	}
30317752Smckusick 	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
30417752Smckusick 		/* trim off trailing white space and newline */;
30517752Smckusick 	*++cp = '\0';
30617752Smckusick 	/*
30717752Smckusick 	 * Copy the command into "cmd".
30817752Smckusick 	 */
30917752Smckusick 	cp = copynext(input, cmd);
31018003Smckusick 	ap->cmd = cmd;
31117752Smckusick 	/*
31217752Smckusick 	 * If no argument, use curdir as the default.
31317752Smckusick 	 */
31417752Smckusick 	if (*cp == '\0') {
31517752Smckusick 		(void) strcpy(name, curdir);
31617752Smckusick 		return;
31717752Smckusick 	}
31817752Smckusick 	nextarg = cp;
31917752Smckusick 	/*
32017752Smckusick 	 * Find the next argument.
32117752Smckusick 	 */
32217752Smckusick getnext:
32317752Smckusick 	cp = copynext(nextarg, rawname);
32417752Smckusick 	if (*cp == '\0')
32517752Smckusick 		nextarg = NULL;
32617752Smckusick 	else
32717752Smckusick 		nextarg = cp;
32817752Smckusick 	/*
32917752Smckusick 	 * If it an absolute pathname, canonicalize it and return it.
33017752Smckusick 	 */
33117752Smckusick 	if (rawname[0] == '/') {
33217752Smckusick 		canon(rawname, name);
33317752Smckusick 	} else {
33417752Smckusick 		/*
33517752Smckusick 		 * For relative pathnames, prepend the current directory to
33617752Smckusick 		 * it then canonicalize and return it.
33717752Smckusick 		 */
33817752Smckusick 		(void) strcpy(output, curdir);
33917752Smckusick 		(void) strcat(output, "/");
34017752Smckusick 		(void) strcat(output, rawname);
34117752Smckusick 		canon(output, name);
34217752Smckusick 	}
34318003Smckusick 	expandarg(name, ap);
34418003Smckusick 	strcpy(name, ap->head->fname);
34518003Smckusick 	freename(ap->head->fname);
34618003Smckusick 	ap->head++;
34717752Smckusick #	undef rawname
34817752Smckusick }
34917752Smckusick 
35017752Smckusick /*
35117752Smckusick  * Strip off the next token of the input.
35217752Smckusick  */
353*56567Sbostic static char *
35417752Smckusick copynext(input, output)
35517752Smckusick 	char *input, *output;
35617752Smckusick {
35717752Smckusick 	register char *cp, *bp;
35817752Smckusick 	char quote;
35917752Smckusick 
36017752Smckusick 	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
36117752Smckusick 		/* skip to argument */;
36217752Smckusick 	bp = output;
36317752Smckusick 	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
36417752Smckusick 		/*
36517752Smckusick 		 * Handle back slashes.
36617752Smckusick 		 */
36717752Smckusick 		if (*cp == '\\') {
36817752Smckusick 			if (*++cp == '\0') {
36917752Smckusick 				fprintf(stderr,
37017752Smckusick 					"command lines cannot be continued\n");
37117752Smckusick 				continue;
37217752Smckusick 			}
37317752Smckusick 			*bp++ = *cp++;
37417752Smckusick 			continue;
37517752Smckusick 		}
37617752Smckusick 		/*
37717752Smckusick 		 * The usual unquoted case.
37817752Smckusick 		 */
37917752Smckusick 		if (*cp != '\'' && *cp != '"') {
38017752Smckusick 			*bp++ = *cp++;
38117752Smckusick 			continue;
38217752Smckusick 		}
38317752Smckusick 		/*
38417752Smckusick 		 * Handle single and double quotes.
38517752Smckusick 		 */
38617752Smckusick 		quote = *cp++;
38717752Smckusick 		while (*cp != quote && *cp != '\0')
38817752Smckusick 			*bp++ = *cp++ | 0200;
38917752Smckusick 		if (*cp++ == '\0') {
39017752Smckusick 			fprintf(stderr, "missing %c\n", quote);
39117752Smckusick 			cp--;
39217752Smckusick 			continue;
39317752Smckusick 		}
39417752Smckusick 	}
39517752Smckusick 	*bp = '\0';
39617752Smckusick 	return (cp);
39717752Smckusick }
39817752Smckusick 
39917752Smckusick /*
40017752Smckusick  * Canonicalize file names to always start with ``./'' and
40118003Smckusick  * remove any imbedded "." and ".." components.
40217752Smckusick  */
403*56567Sbostic void
40417752Smckusick canon(rawname, canonname)
40517752Smckusick 	char *rawname, *canonname;
40617752Smckusick {
40717752Smckusick 	register char *cp, *np;
40817752Smckusick 
40917752Smckusick 	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
41017752Smckusick 		(void) strcpy(canonname, "");
41117752Smckusick 	else if (rawname[0] == '/')
41217752Smckusick 		(void) strcpy(canonname, ".");
41317752Smckusick 	else
41417752Smckusick 		(void) strcpy(canonname, "./");
41517752Smckusick 	(void) strcat(canonname, rawname);
41617752Smckusick 	/*
41718003Smckusick 	 * Eliminate multiple and trailing '/'s
41817752Smckusick 	 */
41918003Smckusick 	for (cp = np = canonname; *np != '\0'; cp++) {
42018003Smckusick 		*cp = *np++;
42118003Smckusick 		while (*cp == '/' && *np == '/')
42218003Smckusick 			np++;
42318003Smckusick 	}
42418003Smckusick 	*cp = '\0';
42518003Smckusick 	if (*--cp == '/')
42618003Smckusick 		*cp = '\0';
42718003Smckusick 	/*
42818003Smckusick 	 * Eliminate extraneous "." and ".." from pathnames.
42918003Smckusick 	 */
43017752Smckusick 	for (np = canonname; *np != '\0'; ) {
43117752Smckusick 		np++;
43217752Smckusick 		cp = np;
43317752Smckusick 		while (*np != '/' && *np != '\0')
43417752Smckusick 			np++;
43518003Smckusick 		if (np - cp == 1 && *cp == '.') {
43618003Smckusick 			cp--;
43718003Smckusick 			(void) strcpy(cp, np);
43818003Smckusick 			np = cp;
43918003Smckusick 		}
44017752Smckusick 		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
44117752Smckusick 			cp--;
44217752Smckusick 			while (cp > &canonname[1] && *--cp != '/')
44317752Smckusick 				/* find beginning of name */;
44417752Smckusick 			(void) strcpy(cp, np);
44517752Smckusick 			np = cp;
44617752Smckusick 		}
44717752Smckusick 	}
44817752Smckusick }
44917752Smckusick 
45017752Smckusick /*
45117756Smckusick  * globals (file name generation)
45217756Smckusick  *
45317756Smckusick  * "*" in params matches r.e ".*"
45417756Smckusick  * "?" in params matches r.e. "."
45517756Smckusick  * "[...]" in params matches character class
45617756Smckusick  * "[...a-z...]" in params matches a through z.
45717756Smckusick  */
458*56567Sbostic static void
45918003Smckusick expandarg(arg, ap)
46017756Smckusick 	char *arg;
46118003Smckusick 	register struct arglist *ap;
46217756Smckusick {
46320850Smckusick 	static struct afile single;
46430950Smckusick 	struct entry *ep;
46517756Smckusick 	int size;
46617756Smckusick 
46718003Smckusick 	ap->head = ap->last = (struct afile *)0;
46818003Smckusick 	size = expand(arg, 0, ap);
46917756Smckusick 	if (size == 0) {
47030950Smckusick 		ep = lookupname(arg);
47130950Smckusick 		single.fnum = ep ? ep->e_ino : 0;
47218003Smckusick 		single.fname = savename(arg);
47318003Smckusick 		ap->head = &single;
47418003Smckusick 		ap->last = ap->head + 1;
47518003Smckusick 		return;
47617756Smckusick 	}
47718003Smckusick 	qsort((char *)ap->head, ap->last - ap->head, sizeof *ap->head, fcmp);
47817756Smckusick }
47917756Smckusick 
48017756Smckusick /*
48117756Smckusick  * Expand a file name
48217756Smckusick  */
483*56567Sbostic static int
48418003Smckusick expand(as, rflg, ap)
48517756Smckusick 	char *as;
48617756Smckusick 	int rflg;
48718003Smckusick 	register struct arglist *ap;
48817756Smckusick {
48917756Smckusick 	int		count, size;
49017756Smckusick 	char		dir = 0;
49117756Smckusick 	char		*rescan = 0;
49250655Smckusick 	RST_DIR		*dirp;
49317756Smckusick 	register char	*s, *cs;
49418003Smckusick 	int		sindex, rindex, lindex;
49517756Smckusick 	struct direct	*dp;
49617756Smckusick 	register char	slash;
49717756Smckusick 	register char	*rs;
49817756Smckusick 	register char	c;
49917756Smckusick 
50017756Smckusick 	/*
50117756Smckusick 	 * check for meta chars
50217756Smckusick 	 */
50317756Smckusick 	s = cs = as;
50417756Smckusick 	slash = 0;
50517756Smckusick 	while (*cs != '*' && *cs != '?' && *cs != '[') {
50618003Smckusick 		if (*cs++ == 0) {
50717756Smckusick 			if (rflg && slash)
50817756Smckusick 				break;
50917756Smckusick 			else
51017756Smckusick 				return (0) ;
51118003Smckusick 		} else if (*cs == '/') {
51217756Smckusick 			slash++;
51317756Smckusick 		}
51417756Smckusick 	}
51517756Smckusick 	for (;;) {
51617756Smckusick 		if (cs == s) {
51718003Smckusick 			s = "";
51817756Smckusick 			break;
51917756Smckusick 		} else if (*--cs == '/') {
52017756Smckusick 			*cs = 0;
52117756Smckusick 			if (s == cs)
52217756Smckusick 				s = "/";
52317756Smckusick 			break;
52417756Smckusick 		}
52517756Smckusick 	}
52617756Smckusick 	if ((dirp = rst_opendir(s)) != NULL)
52717756Smckusick 		dir++;
52817756Smckusick 	count = 0;
52917756Smckusick 	if (*cs == 0)
53018003Smckusick 		*cs++ = 0200;
53117756Smckusick 	if (dir) {
53217756Smckusick 		/*
53317756Smckusick 		 * check for rescan
53417756Smckusick 		 */
53517756Smckusick 		rs = cs;
53617756Smckusick 		do {
53717756Smckusick 			if (*rs == '/') {
53817756Smckusick 				rescan = rs;
53917756Smckusick 				*rs = 0;
54017756Smckusick 			}
54117756Smckusick 		} while (*rs++);
54218003Smckusick 		sindex = ap->last - ap->head;
54317756Smckusick 		while ((dp = rst_readdir(dirp)) != NULL && dp->d_ino != 0) {
54456427Smckusick 			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
54517756Smckusick 				continue;
54617756Smckusick 			if ((*dp->d_name == '.' && *cs != '.'))
54717756Smckusick 				continue;
54817756Smckusick 			if (gmatch(dp->d_name, cs)) {
54918003Smckusick 				if (addg(dp, s, rescan, ap) < 0)
55017756Smckusick 					return (-1);
55117756Smckusick 				count++;
55217756Smckusick 			}
55317756Smckusick 		}
55417756Smckusick 		if (rescan) {
55518003Smckusick 			rindex = sindex;
55618003Smckusick 			lindex = ap->last - ap->head;
55717756Smckusick 			if (count) {
55817756Smckusick 				count = 0;
55918003Smckusick 				while (rindex < lindex) {
56018003Smckusick 					size = expand(ap->head[rindex].fname,
56118003Smckusick 					    1, ap);
56217756Smckusick 					if (size < 0)
56317756Smckusick 						return (size);
56417756Smckusick 					count += size;
56518003Smckusick 					rindex++;
56617756Smckusick 				}
56717756Smckusick 			}
56818003Smckusick 			bcopy((char *)&ap->head[lindex],
56918003Smckusick 			     (char *)&ap->head[sindex],
57018003Smckusick 			     (ap->last - &ap->head[rindex]) * sizeof *ap->head);
57118003Smckusick 			ap->last -= lindex - sindex;
57217756Smckusick 			*rescan = '/';
57317756Smckusick 		}
57417756Smckusick 	}
57517756Smckusick 	s = as;
57617756Smckusick 	while (c = *s)
57717756Smckusick 		*s++ = (c&0177 ? c : '/');
57817756Smckusick 	return (count);
57917756Smckusick }
58017756Smckusick 
58117756Smckusick /*
58217756Smckusick  * Check for a name match
58317756Smckusick  */
584*56567Sbostic static int
58517756Smckusick gmatch(s, p)
58617756Smckusick 	register char	*s, *p;
58717756Smckusick {
58817756Smckusick 	register int	scc;
58917756Smckusick 	char		c;
59017756Smckusick 	char		ok;
59117756Smckusick 	int		lc;
59217756Smckusick 
59317756Smckusick 	if (scc = *s++)
59417756Smckusick 		if ((scc &= 0177) == 0)
59517756Smckusick 			scc = 0200;
59617756Smckusick 	switch (c = *p++) {
59717756Smckusick 
59817756Smckusick 	case '[':
59917756Smckusick 		ok = 0;
60017756Smckusick 		lc = 077777;
60117756Smckusick 		while (c = *p++) {
60218003Smckusick 			if (c == ']') {
60317756Smckusick 				return (ok ? gmatch(s, p) : 0);
60417756Smckusick 			} else if (c == '-') {
60517756Smckusick 				if (lc <= scc && scc <= (*p++))
60617756Smckusick 					ok++ ;
60717756Smckusick 			} else {
60817756Smckusick 				if (scc == (lc = (c&0177)))
60917756Smckusick 					ok++ ;
61017756Smckusick 			}
61117756Smckusick 		}
61217756Smckusick 		return (0);
61317756Smckusick 
61417756Smckusick 	default:
61517756Smckusick 		if ((c&0177) != scc)
61617756Smckusick 			return (0) ;
61717756Smckusick 		/* falls through */
61817756Smckusick 
61917756Smckusick 	case '?':
62017756Smckusick 		return (scc ? gmatch(s, p) : 0);
62117756Smckusick 
62217756Smckusick 	case '*':
62317756Smckusick 		if (*p == 0)
62417756Smckusick 			return (1) ;
62517756Smckusick 		s--;
62617756Smckusick 		while (*s) {
62717756Smckusick 			if (gmatch(s++, p))
62817756Smckusick 				return (1);
62917756Smckusick 		}
63017756Smckusick 		return (0);
63117756Smckusick 
63217756Smckusick 	case 0:
63317756Smckusick 		return (scc == 0);
63417756Smckusick 	}
63517756Smckusick }
63617756Smckusick 
63717756Smckusick /*
63817756Smckusick  * Construct a matched name.
63917756Smckusick  */
640*56567Sbostic static int
64118003Smckusick addg(dp, as1, as3, ap)
64218003Smckusick 	struct direct	*dp;
64318003Smckusick 	char		*as1, *as3;
64418003Smckusick 	struct arglist	*ap;
64517756Smckusick {
64617756Smckusick 	register char	*s1, *s2;
64717756Smckusick 	register int	c;
64818003Smckusick 	char		buf[BUFSIZ];
64917756Smckusick 
65018003Smckusick 	s2 = buf;
65117756Smckusick 	s1 = as1;
65217756Smckusick 	while (c = *s1++) {
65317756Smckusick 		if ((c &= 0177) == 0) {
65418003Smckusick 			*s2++ = '/';
65517756Smckusick 			break;
65617756Smckusick 		}
65717756Smckusick 		*s2++ = c;
65817756Smckusick 	}
65918003Smckusick 	s1 = dp->d_name;
66017756Smckusick 	while (*s2 = *s1++)
66117756Smckusick 		s2++;
66217756Smckusick 	if (s1 = as3) {
66317756Smckusick 		*s2++ = '/';
66417756Smckusick 		while (*s2++ = *++s1)
66517756Smckusick 			/* void */;
66617756Smckusick 	}
66754593Smckusick 	if (mkentry(buf, dp, ap) == FAIL)
66818003Smckusick 		return (-1);
669*56567Sbostic 	return (0);
67017756Smckusick }
67117756Smckusick 
67217756Smckusick /*
67317752Smckusick  * Do an "ls" style listing of a directory
67417752Smckusick  */
675*56567Sbostic static void
67617752Smckusick printlist(name, ino, basename)
67717752Smckusick 	char *name;
67817752Smckusick 	ino_t ino;
67917752Smckusick 	char *basename;
68017752Smckusick {
68117752Smckusick 	register struct afile *fp;
68218003Smckusick 	register struct direct *dp;
68318003Smckusick 	static struct arglist alist = { 0, 0, 0, 0, "ls" };
68417752Smckusick 	struct afile single;
68550655Smckusick 	RST_DIR *dirp;
68617752Smckusick 
68717752Smckusick 	if ((dirp = rst_opendir(name)) == NULL) {
68817752Smckusick 		single.fnum = ino;
68956433Smckusick 		single.finotype = DT_UNKNOWN;
69018003Smckusick 		single.fname = savename(name + strlen(basename) + 1);
69118003Smckusick 		alist.head = &single;
69218003Smckusick 		alist.last = alist.head + 1;
69317752Smckusick 	} else {
69418003Smckusick 		alist.head = (struct afile *)0;
69518003Smckusick 		fprintf(stderr, "%s:\n", name);
69618003Smckusick 		while (dp = rst_readdir(dirp)) {
69718003Smckusick 			if (dp == NULL || dp->d_ino == 0)
69818003Smckusick 				break;
69956427Smckusick 			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
70018003Smckusick 				continue;
70118003Smckusick 			if (vflag == 0 &&
70218003Smckusick 			    (strcmp(dp->d_name, ".") == 0 ||
70318003Smckusick 			     strcmp(dp->d_name, "..") == 0))
70418003Smckusick 				continue;
70554593Smckusick 			if (!mkentry(dp->d_name, dp, &alist))
70618003Smckusick 				return;
70718003Smckusick 		}
70817752Smckusick 	}
70918003Smckusick 	if (alist.head != 0) {
71018003Smckusick 		qsort((char *)alist.head, alist.last - alist.head,
71118003Smckusick 			sizeof *alist.head, fcmp);
71218003Smckusick 		formatf(&alist);
71318003Smckusick 		for (fp = alist.head; fp < alist.last; fp++)
71418003Smckusick 			freename(fp->fname);
71518003Smckusick 	}
71618003Smckusick 	if (dirp != NULL)
71718003Smckusick 		fprintf(stderr, "\n");
71817752Smckusick }
71917752Smckusick 
72017752Smckusick /*
72117752Smckusick  * Read the contents of a directory.
72217752Smckusick  */
723*56567Sbostic static int
72454593Smckusick mkentry(name, dp, ap)
72518003Smckusick 	char *name;
72654593Smckusick 	struct direct *dp;
72718003Smckusick 	register struct arglist *ap;
72817752Smckusick {
72917752Smckusick 	register struct afile *fp;
73017752Smckusick 
73118003Smckusick 	if (ap->base == NULL) {
73218003Smckusick 		ap->nent = 20;
73318003Smckusick 		ap->base = (struct afile *)calloc((unsigned)ap->nent,
73417752Smckusick 			sizeof (struct afile));
73518003Smckusick 		if (ap->base == NULL) {
73618003Smckusick 			fprintf(stderr, "%s: out of memory\n", ap->cmd);
73717752Smckusick 			return (FAIL);
73817752Smckusick 		}
73917752Smckusick 	}
74018003Smckusick 	if (ap->head == 0)
74118003Smckusick 		ap->head = ap->last = ap->base;
74218003Smckusick 	fp = ap->last;
74354593Smckusick 	fp->fnum = dp->d_ino;
74454593Smckusick 	if (oldinofmt)
74554593Smckusick 		fp->finotype = DT_UNKNOWN;
74654593Smckusick 	else
74754593Smckusick 		fp->finotype = dp->d_type;
74818003Smckusick 	fp->fname = savename(name);
74918003Smckusick 	fp++;
75018003Smckusick 	if (fp == ap->head + ap->nent) {
75118003Smckusick 		ap->base = (struct afile *)realloc((char *)ap->base,
75218003Smckusick 		    (unsigned)(2 * ap->nent * sizeof (struct afile)));
75318003Smckusick 		if (ap->base == 0) {
75418003Smckusick 			fprintf(stderr, "%s: out of memory\n", ap->cmd);
75518003Smckusick 			return (FAIL);
75617752Smckusick 		}
75718003Smckusick 		ap->head = ap->base;
75818003Smckusick 		fp = ap->head + ap->nent;
75918003Smckusick 		ap->nent *= 2;
76017752Smckusick 	}
76118003Smckusick 	ap->last = fp;
76217752Smckusick 	return (GOOD);
76317752Smckusick }
76417752Smckusick 
76517752Smckusick /*
76617752Smckusick  * Print out a pretty listing of a directory
76717752Smckusick  */
768*56567Sbostic static void
76918003Smckusick formatf(ap)
77018003Smckusick 	register struct arglist *ap;
77117752Smckusick {
77217752Smckusick 	register struct afile *fp;
77317752Smckusick 	struct entry *np;
77418003Smckusick 	int width = 0, w, nentry = ap->last - ap->head;
77517752Smckusick 	int i, j, len, columns, lines;
77617752Smckusick 	char *cp;
77717752Smckusick 
77818003Smckusick 	if (ap->head == ap->last)
77917752Smckusick 		return;
78018003Smckusick 	for (fp = ap->head; fp < ap->last; fp++) {
78117752Smckusick 		fp->ftype = inodetype(fp->fnum);
78217752Smckusick 		np = lookupino(fp->fnum);
783*56567Sbostic 		if (np != NULL)
78417752Smckusick 			fp->fflags = np->e_flags;
78517752Smckusick 		else
78617752Smckusick 			fp->fflags = 0;
78717752Smckusick 		len = strlen(fmtentry(fp));
78817752Smckusick 		if (len > width)
78917752Smckusick 			width = len;
79017752Smckusick 	}
79117752Smckusick 	width += 2;
79217752Smckusick 	columns = 80 / width;
79317752Smckusick 	if (columns == 0)
79417752Smckusick 		columns = 1;
79517752Smckusick 	lines = (nentry + columns - 1) / columns;
79617752Smckusick 	for (i = 0; i < lines; i++) {
79717752Smckusick 		for (j = 0; j < columns; j++) {
79818003Smckusick 			fp = ap->head + j * lines + i;
79917752Smckusick 			cp = fmtentry(fp);
80017752Smckusick 			fprintf(stderr, "%s", cp);
80118003Smckusick 			if (fp + lines >= ap->last) {
80217752Smckusick 				fprintf(stderr, "\n");
80317752Smckusick 				break;
80417752Smckusick 			}
80517752Smckusick 			w = strlen(cp);
80617752Smckusick 			while (w < width) {
80717752Smckusick 				w++;
80817752Smckusick 				fprintf(stderr, " ");
80917752Smckusick 			}
81017752Smckusick 		}
81117752Smckusick 	}
81217752Smckusick }
81317752Smckusick 
81417752Smckusick /*
81517752Smckusick  * Comparison routine for qsort.
81617752Smckusick  */
817*56567Sbostic static int
81817752Smckusick fcmp(f1, f2)
819*56567Sbostic 	register const void *f1, *f2;
82017752Smckusick {
821*56567Sbostic 	return (strcmp(((struct afile *)f1)->fname,
822*56567Sbostic 	    ((struct afile *)f2)->fname));
82317752Smckusick }
82417752Smckusick 
82517752Smckusick /*
82617752Smckusick  * Format a directory entry.
82717752Smckusick  */
828*56567Sbostic static char *
82917752Smckusick fmtentry(fp)
83017752Smckusick 	register struct afile *fp;
83117752Smckusick {
83217752Smckusick 	static char fmtres[BUFSIZ];
83323982Smckusick 	static int precision = 0;
83423982Smckusick 	int i;
83517752Smckusick 	register char *cp, *dp;
83617752Smckusick 
83723982Smckusick 	if (!vflag) {
83817752Smckusick 		fmtres[0] = '\0';
83923982Smckusick 	} else {
84023982Smckusick 		if (precision == 0)
84123982Smckusick 			for (i = maxino; i > 0; i /= 10)
84223982Smckusick 				precision++;
84323982Smckusick 		(void) sprintf(fmtres, "%*d ", precision, fp->fnum);
84423982Smckusick 	}
84517752Smckusick 	dp = &fmtres[strlen(fmtres)];
84656427Smckusick 	if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
84717752Smckusick 		*dp++ = '^';
84817752Smckusick 	else if ((fp->fflags & NEW) != 0)
84917752Smckusick 		*dp++ = '*';
85017752Smckusick 	else
85117752Smckusick 		*dp++ = ' ';
85217752Smckusick 	for (cp = fp->fname; *cp; cp++)
85317752Smckusick 		if (!vflag && (*cp < ' ' || *cp >= 0177))
85417752Smckusick 			*dp++ = '?';
85517752Smckusick 		else
85617752Smckusick 			*dp++ = *cp;
85754593Smckusick 	switch(fp->finotype) {
85854593Smckusick 
85954593Smckusick 	case DT_LNK:
86054593Smckusick 		*dp++ = '@';
86154593Smckusick 		break;
86254593Smckusick 
86354593Smckusick 	case DT_FIFO:
86454593Smckusick 	case DT_SOCK:
86554593Smckusick 		*dp++ = '=';
86654593Smckusick 		break;
86754593Smckusick 
86854593Smckusick 	case DT_CHR:
86954593Smckusick 	case DT_BLK:
87054593Smckusick 		*dp++ = '#';
87154593Smckusick 		break;
87254593Smckusick 
87354593Smckusick 	case DT_UNKNOWN:
87454593Smckusick 	case DT_DIR:
87554593Smckusick 		if (fp->ftype == NODE)
87654593Smckusick 			*dp++ = '/';
87754593Smckusick 		break;
87854593Smckusick 
87954593Smckusick 	case DT_REG:
88054593Smckusick 		/* nothing */
88154593Smckusick 		break;
88254593Smckusick 
88354593Smckusick 	default:
88454593Smckusick 		fprintf(stderr, "Warning: undefined file type %d\n",
88554593Smckusick 		    fp->finotype);
88654593Smckusick 	}
88717752Smckusick 	*dp++ = 0;
88817752Smckusick 	return (fmtres);
88917752Smckusick }
89017752Smckusick 
89117752Smckusick /*
89217752Smckusick  * respond to interrupts
89317752Smckusick  */
89439942Sbostic void
895*56567Sbostic onintr(signo)
896*56567Sbostic 	int signo;
89717752Smckusick {
89856429Smckusick 	if (command == 'i' && runshell)
89917752Smckusick 		longjmp(reset, 1);
90017752Smckusick 	if (reply("restore interrupted, continue") == FAIL)
90117752Smckusick 		done(1);
90217752Smckusick }
903