xref: /csrg-svn/sbin/restore/interactive.c (revision 17756)
117752Smckusick /* Copyright (c) 1985 Regents of the University of California */
217752Smckusick 
317752Smckusick #ifndef lint
4*17756Smckusick static char sccsid[] = "@(#)interactive.c	3.2	(Berkeley)	01/18/85";
517752Smckusick #endif not lint
617752Smckusick 
717752Smckusick #include "restore.h"
817752Smckusick #include <dumprestor.h>
917752Smckusick #include <setjmp.h>
1017752Smckusick 
11*17756Smckusick #define round(a, b) (((a) + (b) - 1) / (b) * (b))
12*17756Smckusick 
1317752Smckusick /*
1417752Smckusick  * Things to handle interruptions.
1517752Smckusick  */
1617752Smckusick static jmp_buf reset;
1717752Smckusick static char *nextarg = NULL;
1817752Smckusick 
1917752Smckusick /*
20*17756Smckusick  * Structure associated with file name globbing.
21*17756Smckusick  */
22*17756Smckusick struct argnod {
23*17756Smckusick 	struct argnod *argnxt;
24*17756Smckusick 	char argval[1];
25*17756Smckusick };
26*17756Smckusick static struct argnod *gchain, *stakbot, *staktop;
27*17756Smckusick static char *brkend, *nullstr = "";
28*17756Smckusick struct argnod *locstak(), *endstak();
29*17756Smckusick 
30*17756Smckusick /*
3117752Smckusick  * Structure and routines associated with listing directories.
3217752Smckusick  */
3317752Smckusick struct afile {
3417752Smckusick 	ino_t	fnum;		/* inode number of file */
3517752Smckusick 	char	*fname;		/* file name */
3617752Smckusick 	short	fflags;		/* extraction flags, if any */
3717752Smckusick 	char	ftype;		/* file type, e.g. LEAF or NODE */
3817752Smckusick };
3917752Smckusick extern int fcmp();
4017752Smckusick extern char *fmtentry();
4117752Smckusick char *copynext();
4217752Smckusick 
4317752Smckusick /*
4417752Smckusick  * Read and execute commands from the terminal.
4517752Smckusick  */
4617752Smckusick runcmdshell()
4717752Smckusick {
4817752Smckusick 	register struct entry *np;
4917752Smckusick 	ino_t ino;
5017752Smckusick 	char curdir[MAXPATHLEN];
5117752Smckusick 	char name[MAXPATHLEN];
5217752Smckusick 	char cmd[BUFSIZ];
5317752Smckusick 
5417752Smckusick 	canon("/", curdir);
5517752Smckusick loop:
5617752Smckusick 	if (setjmp(reset) != 0) {
57*17756Smckusick 		gchain = 0;
5817752Smckusick 		nextarg = NULL;
5917752Smckusick 		volno = 0;
6017752Smckusick 	}
6117752Smckusick 	getcmd(curdir, cmd, name);
6217752Smckusick 	switch (cmd[0]) {
6317752Smckusick 	/*
6417752Smckusick 	 * Add elements to the extraction list.
6517752Smckusick 	 */
6617752Smckusick 	case 'a':
6717752Smckusick 		ino = dirlookup(name);
6817752Smckusick 		if (ino == 0)
6917752Smckusick 			break;
7017752Smckusick 		if (mflag)
7117752Smckusick 			pathcheck(name);
7217752Smckusick 		treescan(name, ino, addfile);
7317752Smckusick 		break;
7417752Smckusick 	/*
7517752Smckusick 	 * Change working directory.
7617752Smckusick 	 */
7717752Smckusick 	case 'c':
7817752Smckusick 		ino = dirlookup(name);
7917752Smckusick 		if (ino == 0)
8017752Smckusick 			break;
8117752Smckusick 		if (inodetype(ino) == LEAF) {
8217752Smckusick 			fprintf(stderr, "%s: not a directory\n", name);
8317752Smckusick 			break;
8417752Smckusick 		}
8517752Smckusick 		(void) strcpy(curdir, name);
8617752Smckusick 		break;
8717752Smckusick 	/*
8817752Smckusick 	 * Delete elements from the extraction list.
8917752Smckusick 	 */
9017752Smckusick 	case 'd':
9117752Smckusick 		np = lookupname(name);
9217752Smckusick 		if (np == NIL || (np->e_flags & NEW) == 0) {
9317752Smckusick 			fprintf(stderr, "%s: not on extraction list\n", name);
9417752Smckusick 			break;
9517752Smckusick 		}
9617752Smckusick 		treescan(name, np->e_ino, deletefile);
9717752Smckusick 		break;
9817752Smckusick 	/*
9917752Smckusick 	 * Extract the requested list.
10017752Smckusick 	 */
10117752Smckusick 	case 'e':
10217752Smckusick 		createfiles();
10317752Smckusick 		createlinks();
10417752Smckusick 		setdirmodes();
10517752Smckusick 		if (dflag)
10617752Smckusick 			checkrestore();
10717752Smckusick 		volno = 0;
10817752Smckusick 		break;
10917752Smckusick 	/*
11017752Smckusick 	 * List available commands.
11117752Smckusick 	 */
11217752Smckusick 	case 'h':
11317752Smckusick 	case '?':
11417752Smckusick 		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
11517752Smckusick 			"Available commands are:\n",
11617752Smckusick 			"\tls [arg] - list directory\n",
11717752Smckusick 			"\tcd arg - change directory\n",
11817752Smckusick 			"\tpwd - print current directory\n",
11917752Smckusick 			"\tadd [arg] - add `arg' to list of",
12017752Smckusick 			" files to be extracted\n",
12117752Smckusick 			"\tdelete [arg] - delete `arg' from",
12217752Smckusick 			" list of files to be extracted\n",
12317752Smckusick 			"\textract - extract requested files\n",
12417752Smckusick 			"\tquit - immediately exit program\n",
12517752Smckusick 			"\tverbose - toggle verbose flag",
12617752Smckusick 			" (useful with ``ls'')\n",
12717752Smckusick 			"\thelp or `?' - print this list\n",
12817752Smckusick 			"If no `arg' is supplied, the current",
12917752Smckusick 			" directory is used\n");
13017752Smckusick 		break;
13117752Smckusick 	/*
13217752Smckusick 	 * List a directory.
13317752Smckusick 	 */
13417752Smckusick 	case 'l':
13517752Smckusick 		ino = dirlookup(name);
13617752Smckusick 		if (ino == 0)
13717752Smckusick 			break;
13817752Smckusick 		printlist(name, ino, curdir);
13917752Smckusick 		break;
14017752Smckusick 	/*
14117752Smckusick 	 * Print current directory.
14217752Smckusick 	 */
14317752Smckusick 	case 'p':
14417752Smckusick 		if (curdir[1] == '\0')
14517752Smckusick 			fprintf(stderr, "/\n");
14617752Smckusick 		else
14717752Smckusick 			fprintf(stderr, "%s\n", &curdir[1]);
14817752Smckusick 		break;
14917752Smckusick 	/*
15017752Smckusick 	 * Quit.
15117752Smckusick 	 */
15217752Smckusick 	case 'q':
15317752Smckusick 	case 'x':
15417752Smckusick 		return;
15517752Smckusick 	/*
15617752Smckusick 	 * Toggle verbose mode.
15717752Smckusick 	 */
15817752Smckusick 	case 'v':
15917752Smckusick 		if (vflag) {
16017752Smckusick 			fprintf(stderr, "verbose mode off\n");
16117752Smckusick 			vflag = 0;
16217752Smckusick 			break;
16317752Smckusick 		}
16417752Smckusick 		fprintf(stderr, "verbose mode on\n");
16517752Smckusick 		vflag++;
16617752Smckusick 		break;
16717752Smckusick 	/*
16817752Smckusick 	 * Just restore requested directory modes.
16917752Smckusick 	 */
17017752Smckusick 	case 'R':
17117752Smckusick 		setdirmodes();
17217752Smckusick 		break;
17317752Smckusick 	/*
17417752Smckusick 	 * Turn on debugging.
17517752Smckusick 	 */
17617752Smckusick 	case 'D':
17717752Smckusick 		if (dflag) {
17817752Smckusick 			fprintf(stderr, "debugging mode off\n");
17917752Smckusick 			dflag = 0;
18017752Smckusick 			break;
18117752Smckusick 		}
18217752Smckusick 		fprintf(stderr, "debugging mode on\n");
18317752Smckusick 		dflag++;
18417752Smckusick 		break;
18517752Smckusick 	/*
18617752Smckusick 	 * Unknown command.
18717752Smckusick 	 */
18817752Smckusick 	default:
18917752Smckusick 		fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
19017752Smckusick 		break;
19117752Smckusick 	}
19217752Smckusick 	goto loop;
19317752Smckusick }
19417752Smckusick 
19517752Smckusick /*
19617752Smckusick  * Read and parse an interactive command.
19717752Smckusick  * The first word on the line is assigned to "cmd". If
19817752Smckusick  * there are no arguments on the command line, then "curdir"
19917752Smckusick  * is returned as the argument. If there are arguments
20017752Smckusick  * on the line they are returned one at a time on each
20117752Smckusick  * successive call to getcmd. Each argument is first assigned
20217752Smckusick  * to "name". If it does not start with "/" the pathname in
20317752Smckusick  * "curdir" is prepended to it. Finally "canon" is called to
20417752Smckusick  * eliminate any embedded ".." components.
20517752Smckusick  */
20617752Smckusick getcmd(curdir, cmd, name)
20717752Smckusick 	char *curdir, *cmd, *name;
20817752Smckusick {
20917752Smckusick 	register char *cp;
21017752Smckusick 	static char input[BUFSIZ];
21117752Smckusick 	char output[BUFSIZ];
21217752Smckusick #	define rawname input	/* save space by reusing input buffer */
21317752Smckusick 
21417752Smckusick 	/*
21517752Smckusick 	 * Check to see if still processing arguments.
21617752Smckusick 	 */
217*17756Smckusick 	if (gchain != 0)
218*17756Smckusick 		goto getnextexp;
21917752Smckusick 	if (nextarg != NULL)
22017752Smckusick 		goto getnext;
22117752Smckusick 	/*
22217752Smckusick 	 * Read a command line and trim off trailing white space.
22317752Smckusick 	 */
22417752Smckusick 	do	{
22517752Smckusick 		fprintf(stderr, "restore > ");
22617752Smckusick 		(void) fflush(stderr);
22717752Smckusick 		(void) fgets(input, BUFSIZ, terminal);
22817752Smckusick 	} while (!feof(terminal) && input[0] == '\n');
22917752Smckusick 	if (feof(terminal)) {
23017752Smckusick 		(void) strcpy(cmd, "quit");
23117752Smckusick 		return;
23217752Smckusick 	}
23317752Smckusick 	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
23417752Smckusick 		/* trim off trailing white space and newline */;
23517752Smckusick 	*++cp = '\0';
23617752Smckusick 	/*
23717752Smckusick 	 * Copy the command into "cmd".
23817752Smckusick 	 */
23917752Smckusick 	cp = copynext(input, cmd);
24017752Smckusick 	/*
24117752Smckusick 	 * If no argument, use curdir as the default.
24217752Smckusick 	 */
24317752Smckusick 	if (*cp == '\0') {
24417752Smckusick 		(void) strcpy(name, curdir);
24517752Smckusick 		return;
24617752Smckusick 	}
24717752Smckusick 	nextarg = cp;
24817752Smckusick 	/*
24917752Smckusick 	 * Find the next argument.
25017752Smckusick 	 */
25117752Smckusick getnext:
25217752Smckusick 	cp = copynext(nextarg, rawname);
25317752Smckusick 	if (*cp == '\0')
25417752Smckusick 		nextarg = NULL;
25517752Smckusick 	else
25617752Smckusick 		nextarg = cp;
25717752Smckusick 	/*
25817752Smckusick 	 * If it an absolute pathname, canonicalize it and return it.
25917752Smckusick 	 */
26017752Smckusick 	if (rawname[0] == '/') {
26117752Smckusick 		canon(rawname, name);
26217752Smckusick 	} else {
26317752Smckusick 		/*
26417752Smckusick 		 * For relative pathnames, prepend the current directory to
26517752Smckusick 		 * it then canonicalize and return it.
26617752Smckusick 		 */
26717752Smckusick 		(void) strcpy(output, curdir);
26817752Smckusick 		(void) strcat(output, "/");
26917752Smckusick 		(void) strcat(output, rawname);
27017752Smckusick 		canon(output, name);
27117752Smckusick 	}
272*17756Smckusick 	expandarg(name);
273*17756Smckusick getnextexp:
274*17756Smckusick 	strcpy(name, gchain->argval);
275*17756Smckusick 	gchain = gchain->argnxt;
27617752Smckusick #	undef rawname
27717752Smckusick }
27817752Smckusick 
27917752Smckusick /*
28017752Smckusick  * Strip off the next token of the input.
28117752Smckusick  */
28217752Smckusick char *
28317752Smckusick copynext(input, output)
28417752Smckusick 	char *input, *output;
28517752Smckusick {
28617752Smckusick 	register char *cp, *bp;
28717752Smckusick 	char quote;
28817752Smckusick 
28917752Smckusick 	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
29017752Smckusick 		/* skip to argument */;
29117752Smckusick 	bp = output;
29217752Smckusick 	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
29317752Smckusick 		/*
29417752Smckusick 		 * Handle back slashes.
29517752Smckusick 		 */
29617752Smckusick 		if (*cp == '\\') {
29717752Smckusick 			if (*++cp == '\0') {
29817752Smckusick 				fprintf(stderr,
29917752Smckusick 					"command lines cannot be continued\n");
30017752Smckusick 				continue;
30117752Smckusick 			}
30217752Smckusick 			*bp++ = *cp++;
30317752Smckusick 			continue;
30417752Smckusick 		}
30517752Smckusick 		/*
30617752Smckusick 		 * The usual unquoted case.
30717752Smckusick 		 */
30817752Smckusick 		if (*cp != '\'' && *cp != '"') {
30917752Smckusick 			*bp++ = *cp++;
31017752Smckusick 			continue;
31117752Smckusick 		}
31217752Smckusick 		/*
31317752Smckusick 		 * Handle single and double quotes.
31417752Smckusick 		 */
31517752Smckusick 		quote = *cp++;
31617752Smckusick 		while (*cp != quote && *cp != '\0')
31717752Smckusick 			*bp++ = *cp++ | 0200;
31817752Smckusick 		if (*cp++ == '\0') {
31917752Smckusick 			fprintf(stderr, "missing %c\n", quote);
32017752Smckusick 			cp--;
32117752Smckusick 			continue;
32217752Smckusick 		}
32317752Smckusick 	}
32417752Smckusick 	*bp = '\0';
32517752Smckusick 	return (cp);
32617752Smckusick }
32717752Smckusick 
32817752Smckusick /*
32917752Smckusick  * Canonicalize file names to always start with ``./'' and
33017752Smckusick  * remove any imbedded ".." components.
33117752Smckusick  */
33217752Smckusick canon(rawname, canonname)
33317752Smckusick 	char *rawname, *canonname;
33417752Smckusick {
33517752Smckusick 	register char *cp, *np;
33617752Smckusick 	int len;
33717752Smckusick 
33817752Smckusick 	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
33917752Smckusick 		(void) strcpy(canonname, "");
34017752Smckusick 	else if (rawname[0] == '/')
34117752Smckusick 		(void) strcpy(canonname, ".");
34217752Smckusick 	else
34317752Smckusick 		(void) strcpy(canonname, "./");
34417752Smckusick 	(void) strcat(canonname, rawname);
34517752Smckusick 	len = strlen(canonname) - 1;
34617752Smckusick 	if (canonname[len] == '/')
34717752Smckusick 		canonname[len] = '\0';
34817752Smckusick 	/*
34917752Smckusick 	 * Eliminate extraneous ".." from pathnames.
35017752Smckusick 	 */
35117752Smckusick 	for (np = canonname; *np != '\0'; ) {
35217752Smckusick 		np++;
35317752Smckusick 		cp = np;
35417752Smckusick 		while (*np != '/' && *np != '\0')
35517752Smckusick 			np++;
35617752Smckusick 		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
35717752Smckusick 			cp--;
35817752Smckusick 			while (cp > &canonname[1] && *--cp != '/')
35917752Smckusick 				/* find beginning of name */;
36017752Smckusick 			(void) strcpy(cp, np);
36117752Smckusick 			np = cp;
36217752Smckusick 		}
36317752Smckusick 	}
36417752Smckusick }
36517752Smckusick 
36617752Smckusick /*
367*17756Smckusick  * globals (file name generation)
368*17756Smckusick  *
369*17756Smckusick  * "*" in params matches r.e ".*"
370*17756Smckusick  * "?" in params matches r.e. "."
371*17756Smckusick  * "[...]" in params matches character class
372*17756Smckusick  * "[...a-z...]" in params matches a through z.
373*17756Smckusick  */
374*17756Smckusick expandarg(arg)
375*17756Smckusick 	char *arg;
376*17756Smckusick {
377*17756Smckusick 	static char *expbuf = NULL;
378*17756Smckusick 	static unsigned expsize = BUFSIZ;
379*17756Smckusick 	int size;
380*17756Smckusick 	char argbuf[BUFSIZ];
381*17756Smckusick 
382*17756Smckusick 	do {
383*17756Smckusick 		if (expbuf != NULL)
384*17756Smckusick 			free(expbuf);
385*17756Smckusick 		expbuf = malloc(expsize);
386*17756Smckusick 		brkend = expbuf + expsize;
387*17756Smckusick 		expsize <<= 1;
388*17756Smckusick 		stakbot = (struct argnod *)expbuf;
389*17756Smckusick 		gchain = 0;
390*17756Smckusick 		(void)strcpy(argbuf, arg);
391*17756Smckusick 		size = expand(argbuf, 0);
392*17756Smckusick 	} while (size < 0);
393*17756Smckusick 	if (size == 0) {
394*17756Smckusick 		gchain = (struct argnod *)expbuf;
395*17756Smckusick 		gchain->argnxt = 0;
396*17756Smckusick 		(void)strcpy(gchain->argval, arg);
397*17756Smckusick 	}
398*17756Smckusick }
399*17756Smckusick 
400*17756Smckusick /*
401*17756Smckusick  * Expand a file name
402*17756Smckusick  */
403*17756Smckusick expand(as, rflg)
404*17756Smckusick 	char *as;
405*17756Smckusick 	int rflg;
406*17756Smckusick {
407*17756Smckusick 	int		count, size;
408*17756Smckusick 	char		dir = 0;
409*17756Smckusick 	char		*rescan = 0;
410*17756Smckusick 	DIR		*dirp;
411*17756Smckusick 	register char	*s, *cs;
412*17756Smckusick 	struct argnod	*schain = gchain;
413*17756Smckusick 	struct direct	*dp;
414*17756Smckusick 	register char	slash;
415*17756Smckusick 	register char	*rs;
416*17756Smckusick 	struct argnod	*rchain;
417*17756Smckusick 	register char	c;
418*17756Smckusick 
419*17756Smckusick 	/*
420*17756Smckusick 	 * check for meta chars
421*17756Smckusick 	 */
422*17756Smckusick 	s = cs = as;
423*17756Smckusick 	slash = 0;
424*17756Smckusick 	while (*cs != '*' && *cs != '?' && *cs != '[') {
425*17756Smckusick 		if (*cs++==0) {
426*17756Smckusick 			if (rflg && slash)
427*17756Smckusick 				break;
428*17756Smckusick 			else
429*17756Smckusick 				return (0) ;
430*17756Smckusick 		} else if (*cs=='/') {
431*17756Smckusick 			slash++;
432*17756Smckusick 		}
433*17756Smckusick 	}
434*17756Smckusick 	for (;;) {
435*17756Smckusick 		if (cs == s) {
436*17756Smckusick 			s = nullstr;
437*17756Smckusick 			break;
438*17756Smckusick 		} else if (*--cs == '/') {
439*17756Smckusick 			*cs = 0;
440*17756Smckusick 			if (s == cs)
441*17756Smckusick 				s = "/";
442*17756Smckusick 			break;
443*17756Smckusick 		}
444*17756Smckusick 	}
445*17756Smckusick 	if ((dirp = rst_opendir(s)) != NULL)
446*17756Smckusick 		dir++;
447*17756Smckusick 	count = 0;
448*17756Smckusick 	if (*cs == 0)
449*17756Smckusick 		*cs++=0200 ;
450*17756Smckusick 	if (dir) {
451*17756Smckusick 		/*
452*17756Smckusick 		 * check for rescan
453*17756Smckusick 		 */
454*17756Smckusick 		rs = cs;
455*17756Smckusick 		do {
456*17756Smckusick 			if (*rs == '/') {
457*17756Smckusick 				rescan = rs;
458*17756Smckusick 				*rs = 0;
459*17756Smckusick 				gchain = 0 ;
460*17756Smckusick 			}
461*17756Smckusick 		} while (*rs++);
462*17756Smckusick 		while ((dp = rst_readdir(dirp)) != NULL && dp->d_ino != 0) {
463*17756Smckusick 			if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
464*17756Smckusick 				continue;
465*17756Smckusick 			if ((*dp->d_name == '.' && *cs != '.'))
466*17756Smckusick 				continue;
467*17756Smckusick 			if (gmatch(dp->d_name, cs)) {
468*17756Smckusick 				if (addg(s, dp->d_name, rescan) < 0)
469*17756Smckusick 					return (-1);
470*17756Smckusick 				count++;
471*17756Smckusick 			}
472*17756Smckusick 		}
473*17756Smckusick 		if (rescan) {
474*17756Smckusick 			rchain = gchain;
475*17756Smckusick 			gchain = schain;
476*17756Smckusick 			if (count) {
477*17756Smckusick 				count = 0;
478*17756Smckusick 				while (rchain) {
479*17756Smckusick 					size = expand(rchain->argval, 1);
480*17756Smckusick 					if (size < 0)
481*17756Smckusick 						return (size);
482*17756Smckusick 					count += size;
483*17756Smckusick 					rchain = rchain->argnxt;
484*17756Smckusick 				}
485*17756Smckusick 			}
486*17756Smckusick 			*rescan = '/';
487*17756Smckusick 		}
488*17756Smckusick 	}
489*17756Smckusick 	s = as;
490*17756Smckusick 	while (c = *s)
491*17756Smckusick 		*s++ = (c&0177 ? c : '/');
492*17756Smckusick 	return (count);
493*17756Smckusick }
494*17756Smckusick 
495*17756Smckusick /*
496*17756Smckusick  * Check for a name match
497*17756Smckusick  */
498*17756Smckusick gmatch(s, p)
499*17756Smckusick 	register char	*s, *p;
500*17756Smckusick {
501*17756Smckusick 	register int	scc;
502*17756Smckusick 	char		c;
503*17756Smckusick 	char		ok;
504*17756Smckusick 	int		lc;
505*17756Smckusick 
506*17756Smckusick 	if (scc = *s++)
507*17756Smckusick 		if ((scc &= 0177) == 0)
508*17756Smckusick 			scc = 0200;
509*17756Smckusick 	switch (c = *p++) {
510*17756Smckusick 
511*17756Smckusick 	case '[':
512*17756Smckusick 		ok = 0;
513*17756Smckusick 		lc = 077777;
514*17756Smckusick 		while (c = *p++) {
515*17756Smckusick 			if (c==']') {
516*17756Smckusick 				return (ok ? gmatch(s, p) : 0);
517*17756Smckusick 			} else if (c == '-') {
518*17756Smckusick 				if (lc <= scc && scc <= (*p++))
519*17756Smckusick 					ok++ ;
520*17756Smckusick 			} else {
521*17756Smckusick 				if (scc == (lc = (c&0177)))
522*17756Smckusick 					ok++ ;
523*17756Smckusick 			}
524*17756Smckusick 		}
525*17756Smckusick 		return (0);
526*17756Smckusick 
527*17756Smckusick 	default:
528*17756Smckusick 		if ((c&0177) != scc)
529*17756Smckusick 			return (0) ;
530*17756Smckusick 		/* falls through */
531*17756Smckusick 
532*17756Smckusick 	case '?':
533*17756Smckusick 		return (scc ? gmatch(s, p) : 0);
534*17756Smckusick 
535*17756Smckusick 	case '*':
536*17756Smckusick 		if (*p == 0)
537*17756Smckusick 			return (1) ;
538*17756Smckusick 		s--;
539*17756Smckusick 		while (*s) {
540*17756Smckusick 			if (gmatch(s++, p))
541*17756Smckusick 				return (1);
542*17756Smckusick 		}
543*17756Smckusick 		return (0);
544*17756Smckusick 
545*17756Smckusick 	case 0:
546*17756Smckusick 		return (scc == 0);
547*17756Smckusick 	}
548*17756Smckusick }
549*17756Smckusick 
550*17756Smckusick /*
551*17756Smckusick  * Construct a matched name.
552*17756Smckusick  */
553*17756Smckusick addg(as1, as2, as3)
554*17756Smckusick 	char		*as1, *as2, *as3;
555*17756Smckusick {
556*17756Smckusick 	register char	*s1, *s2;
557*17756Smckusick 	register int	c;
558*17756Smckusick 
559*17756Smckusick 	if ((s2 = (char *)locstak()) == 0)
560*17756Smckusick 		return (-1);
561*17756Smckusick 	s2 += sizeof(char *);
562*17756Smckusick 	s1 = as1;
563*17756Smckusick 	while (c = *s1++) {
564*17756Smckusick 		if ((c &= 0177) == 0) {
565*17756Smckusick 			*s2++='/';
566*17756Smckusick 			break;
567*17756Smckusick 		}
568*17756Smckusick 		*s2++ = c;
569*17756Smckusick 	}
570*17756Smckusick 	s1 = as2;
571*17756Smckusick 	while (*s2 = *s1++)
572*17756Smckusick 		s2++;
573*17756Smckusick 	if (s1 = as3) {
574*17756Smckusick 		*s2++ = '/';
575*17756Smckusick 		while (*s2++ = *++s1)
576*17756Smckusick 			/* void */;
577*17756Smckusick 	}
578*17756Smckusick 	makearg(endstak(s2));
579*17756Smckusick 	return (0);
580*17756Smckusick }
581*17756Smckusick 
582*17756Smckusick /*
583*17756Smckusick  * Add a matched name to the list.
584*17756Smckusick  */
585*17756Smckusick makearg(args)
586*17756Smckusick 	register struct argnod *args;
587*17756Smckusick {
588*17756Smckusick 	args->argnxt = gchain;
589*17756Smckusick 	gchain = args;
590*17756Smckusick }
591*17756Smckusick 
592*17756Smckusick /*
593*17756Smckusick  * set up stack for local use
594*17756Smckusick  * should be followed by `endstak'
595*17756Smckusick  */
596*17756Smckusick struct argnod *
597*17756Smckusick locstak()
598*17756Smckusick {
599*17756Smckusick 	if (brkend - (char *)stakbot < 100) {
600*17756Smckusick 		fprintf(stderr, "ran out of arg space\n");
601*17756Smckusick 		return (0);
602*17756Smckusick 	}
603*17756Smckusick 	return (stakbot);
604*17756Smckusick }
605*17756Smckusick 
606*17756Smckusick /*
607*17756Smckusick  * tidy up after `locstak'
608*17756Smckusick  */
609*17756Smckusick struct argnod *
610*17756Smckusick endstak(argp)
611*17756Smckusick 	register char *argp;
612*17756Smckusick {
613*17756Smckusick 	register struct argnod *oldstak;
614*17756Smckusick 
615*17756Smckusick 	*argp++ = 0;
616*17756Smckusick 	oldstak = stakbot;
617*17756Smckusick 	stakbot = staktop = (struct argnod *)round((int)argp, sizeof(char *));
618*17756Smckusick 	return (oldstak);
619*17756Smckusick }
620*17756Smckusick 
621*17756Smckusick /*
62217752Smckusick  * Do an "ls" style listing of a directory
62317752Smckusick  */
62417752Smckusick printlist(name, ino, basename)
62517752Smckusick 	char *name;
62617752Smckusick 	ino_t ino;
62717752Smckusick 	char *basename;
62817752Smckusick {
62917752Smckusick 	register struct afile *fp;
63017752Smckusick 	struct afile *dfp0, *dfplast;
63117752Smckusick 	struct afile single;
63217752Smckusick 	DIR *dirp;
63317752Smckusick 
63417752Smckusick 	if ((dirp = rst_opendir(name)) == NULL) {
63517752Smckusick 		single.fnum = ino;
63617752Smckusick 		single.fname = savename(name + strlen(basename));
63717752Smckusick 		dfp0 = &single;
63817752Smckusick 		dfplast = dfp0 + 1;
63917752Smckusick 	} else {
64017752Smckusick 		if (getdir(dirp, &dfp0, &dfplast) == FAIL)
64117752Smckusick 			return;
64217752Smckusick 	}
64317752Smckusick 	qsort((char *)dfp0, dfplast - dfp0, sizeof (struct afile), fcmp);
64417752Smckusick 	formatf(dfp0, dfplast);
64517752Smckusick 	for (fp = dfp0; fp < dfplast; fp++)
64617752Smckusick 		freename(fp->fname);
64717752Smckusick }
64817752Smckusick 
64917752Smckusick /*
65017752Smckusick  * Read the contents of a directory.
65117752Smckusick  */
65217752Smckusick getdir(dirp, pfp0, pfplast)
65317752Smckusick 	DIR *dirp;
65417752Smckusick 	struct afile **pfp0, **pfplast;
65517752Smckusick {
65617752Smckusick 	register struct afile *fp;
65717752Smckusick 	register struct direct *dp;
65817752Smckusick 	static struct afile *basefp = NULL;
65917752Smckusick 	static long nent = 20;
66017752Smckusick 
66117752Smckusick 	if (basefp == NULL) {
66217752Smckusick 		basefp = (struct afile *)calloc((unsigned)nent,
66317752Smckusick 			sizeof (struct afile));
66417752Smckusick 		if (basefp == NULL) {
66517752Smckusick 			fprintf(stderr, "ls: out of memory\n");
66617752Smckusick 			return (FAIL);
66717752Smckusick 		}
66817752Smckusick 	}
66917752Smckusick 	fp = *pfp0 = basefp;
67017752Smckusick 	*pfplast = *pfp0 + nent;
67117752Smckusick 	while (dp = rst_readdir(dirp)) {
67217752Smckusick 		if (dp == NULL || dp->d_ino == 0)
67317752Smckusick 			break;
67417752Smckusick 		if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
67517752Smckusick 			continue;
67617752Smckusick 		if (vflag == 0 &&
67717752Smckusick 		    (strcmp(dp->d_name, ".") == 0 ||
67817752Smckusick 		     strcmp(dp->d_name, "..") == 0))
67917752Smckusick 			continue;
68017752Smckusick 		fp->fnum = dp->d_ino;
68117752Smckusick 		fp->fname = savename(dp->d_name);
68217752Smckusick 		fp++;
68317752Smckusick 		if (fp == *pfplast) {
68417752Smckusick 			basefp = (struct afile *)realloc((char *)basefp,
68517752Smckusick 			    (unsigned)(2 * nent * sizeof (struct afile)));
68617752Smckusick 			if (basefp == 0) {
68717752Smckusick 				fprintf(stderr, "ls: out of memory\n");
68817752Smckusick 				return (FAIL);
68917752Smckusick 			}
69017752Smckusick 			*pfp0 = basefp;
69117752Smckusick 			fp = *pfp0 + nent;
69217752Smckusick 			*pfplast = fp + nent;
69317752Smckusick 			nent *= 2;
69417752Smckusick 		}
69517752Smckusick 	}
69617752Smckusick 	*pfplast = fp;
69717752Smckusick 	return (GOOD);
69817752Smckusick }
69917752Smckusick 
70017752Smckusick /*
70117752Smckusick  * Print out a pretty listing of a directory
70217752Smckusick  */
70317752Smckusick formatf(fp0, fplast)
70417752Smckusick 	struct afile *fp0, *fplast;
70517752Smckusick {
70617752Smckusick 	register struct afile *fp;
70717752Smckusick 	struct entry *np;
70817752Smckusick 	int width = 0, w, nentry = fplast - fp0;
70917752Smckusick 	int i, j, len, columns, lines;
71017752Smckusick 	char *cp;
71117752Smckusick 
71217752Smckusick 	if (fp0 == fplast)
71317752Smckusick 		return;
71417752Smckusick 	for (fp = fp0; fp < fplast; fp++) {
71517752Smckusick 		fp->ftype = inodetype(fp->fnum);
71617752Smckusick 		np = lookupino(fp->fnum);
71717752Smckusick 		if (np != NIL)
71817752Smckusick 			fp->fflags = np->e_flags;
71917752Smckusick 		else
72017752Smckusick 			fp->fflags = 0;
72117752Smckusick 		len = strlen(fmtentry(fp));
72217752Smckusick 		if (len > width)
72317752Smckusick 			width = len;
72417752Smckusick 	}
72517752Smckusick 	width += 2;
72617752Smckusick 	columns = 80 / width;
72717752Smckusick 	if (columns == 0)
72817752Smckusick 		columns = 1;
72917752Smckusick 	lines = (nentry + columns - 1) / columns;
73017752Smckusick 	for (i = 0; i < lines; i++) {
73117752Smckusick 		for (j = 0; j < columns; j++) {
73217752Smckusick 			fp = fp0 + j * lines + i;
73317752Smckusick 			cp = fmtentry(fp);
73417752Smckusick 			fprintf(stderr, "%s", cp);
73517752Smckusick 			if (fp + lines >= fplast) {
73617752Smckusick 				fprintf(stderr, "\n");
73717752Smckusick 				break;
73817752Smckusick 			}
73917752Smckusick 			w = strlen(cp);
74017752Smckusick 			while (w < width) {
74117752Smckusick 				w++;
74217752Smckusick 				fprintf(stderr, " ");
74317752Smckusick 			}
74417752Smckusick 		}
74517752Smckusick 	}
74617752Smckusick }
74717752Smckusick 
74817752Smckusick /*
74917752Smckusick  * Comparison routine for qsort.
75017752Smckusick  */
75117752Smckusick fcmp(f1, f2)
75217752Smckusick 	register struct afile *f1, *f2;
75317752Smckusick {
75417752Smckusick 
75517752Smckusick 	return (strcmp(f1->fname, f2->fname));
75617752Smckusick }
75717752Smckusick 
75817752Smckusick /*
75917752Smckusick  * Format a directory entry.
76017752Smckusick  */
76117752Smckusick char *
76217752Smckusick fmtentry(fp)
76317752Smckusick 	register struct afile *fp;
76417752Smckusick {
76517752Smckusick 	static char fmtres[BUFSIZ];
76617752Smckusick 	register char *cp, *dp;
76717752Smckusick 
76817752Smckusick 	if (vflag)
76917752Smckusick 		(void) sprintf(fmtres, "%5d ", fp->fnum);
77017752Smckusick 	else
77117752Smckusick 		fmtres[0] = '\0';
77217752Smckusick 	dp = &fmtres[strlen(fmtres)];
77317752Smckusick 	if (dflag && BIT(fp->fnum, dumpmap) == 0)
77417752Smckusick 		*dp++ = '^';
77517752Smckusick 	else if ((fp->fflags & NEW) != 0)
77617752Smckusick 		*dp++ = '*';
77717752Smckusick 	else
77817752Smckusick 		*dp++ = ' ';
77917752Smckusick 	for (cp = fp->fname; *cp; cp++)
78017752Smckusick 		if (!vflag && (*cp < ' ' || *cp >= 0177))
78117752Smckusick 			*dp++ = '?';
78217752Smckusick 		else
78317752Smckusick 			*dp++ = *cp;
78417752Smckusick 	if (fp->ftype == NODE)
78517752Smckusick 		*dp++ = '/';
78617752Smckusick 	*dp++ = 0;
78717752Smckusick 	return (fmtres);
78817752Smckusick }
78917752Smckusick 
79017752Smckusick /*
79117752Smckusick  * respond to interrupts
79217752Smckusick  */
79317752Smckusick onintr()
79417752Smckusick {
79517752Smckusick 	if (command == 'i')
79617752Smckusick 		longjmp(reset, 1);
79717752Smckusick 	if (reply("restore interrupted, continue") == FAIL)
79817752Smckusick 		done(1);
79917752Smckusick }
800