interactive.c revision 37906
11558Srgrimes/*
21558Srgrimes * Copyright (c) 1985, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * Redistribution and use in source and binary forms, with or without
61558Srgrimes * modification, are permitted provided that the following conditions
71558Srgrimes * are met:
81558Srgrimes * 1. Redistributions of source code must retain the above copyright
91558Srgrimes *    notice, this list of conditions and the following disclaimer.
101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111558Srgrimes *    notice, this list of conditions and the following disclaimer in the
121558Srgrimes *    documentation and/or other materials provided with the distribution.
131558Srgrimes * 3. All advertising materials mentioning features or use of this software
141558Srgrimes *    must display the following acknowledgement:
151558Srgrimes *	This product includes software developed by the University of
161558Srgrimes *	California, Berkeley and its contributors.
171558Srgrimes * 4. Neither the name of the University nor the names of its contributors
181558Srgrimes *    may be used to endorse or promote products derived from this software
191558Srgrimes *    without specific prior written permission.
201558Srgrimes *
211558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311558Srgrimes * SUCH DAMAGE.
321558Srgrimes */
331558Srgrimes
341558Srgrimes#ifndef lint
3537906Scharnier#if 0
3623685Speterstatic char sccsid[] = "@(#)interactive.c	8.5 (Berkeley) 5/1/95";
3737906Scharnier#endif
3837906Scharnierstatic const char rcsid[] =
3937906Scharnier	"$Id$";
401558Srgrimes#endif /* not lint */
411558Srgrimes
421558Srgrimes#include <sys/param.h>
431558Srgrimes#include <sys/stat.h>
441558Srgrimes
451558Srgrimes#include <ufs/ufs/dinode.h>
461558Srgrimes#include <ufs/ufs/dir.h>
471558Srgrimes#include <protocols/dumprestore.h>
481558Srgrimes
491558Srgrimes#include <setjmp.h>
501558Srgrimes#include <glob.h>
511558Srgrimes#include <stdio.h>
521558Srgrimes#include <stdlib.h>
531558Srgrimes#include <string.h>
541558Srgrimes
551558Srgrimes#include "restore.h"
561558Srgrimes#include "extern.h"
571558Srgrimes
581558Srgrimes#define round(a, b) (((a) + (b) - 1) / (b) * (b))
591558Srgrimes
601558Srgrimes/*
611558Srgrimes * Things to handle interruptions.
621558Srgrimes */
631558Srgrimesstatic int runshell;
641558Srgrimesstatic jmp_buf reset;
651558Srgrimesstatic char *nextarg = NULL;
661558Srgrimes
671558Srgrimes/*
681558Srgrimes * Structure and routines associated with listing directories.
691558Srgrimes */
701558Srgrimesstruct afile {
711558Srgrimes	ino_t	fnum;		/* inode number of file */
721558Srgrimes	char	*fname;		/* file name */
731558Srgrimes	short	len;		/* name length */
741558Srgrimes	char	prefix;		/* prefix character */
751558Srgrimes	char	postfix;	/* postfix character */
761558Srgrimes};
771558Srgrimesstruct arglist {
781558Srgrimes	int	freeglob;	/* glob structure needs to be freed */
791558Srgrimes	int	argcnt;		/* next globbed argument to return */
801558Srgrimes	glob_t	glob;		/* globbing information */
811558Srgrimes	char	*cmd;		/* the current command */
821558Srgrimes};
831558Srgrimes
841558Srgrimesstatic char	*copynext __P((char *, char *));
851558Srgrimesstatic int	 fcmp __P((const void *, const void *));
861558Srgrimesstatic void	 formatf __P((struct afile *, int));
8721997Simpstatic void	 getcmd __P((char *, char *, char *, int, struct arglist *));
881558Srgrimesstruct dirent	*glob_readdir __P((RST_DIR *dirp));
891558Srgrimesstatic int	 glob_stat __P((const char *, struct stat *));
9023685Speterstatic void	 mkentry __P((char *, struct direct *, struct afile *));
911558Srgrimesstatic void	 printlist __P((char *, char *));
921558Srgrimes
931558Srgrimes/*
941558Srgrimes * Read and execute commands from the terminal.
951558Srgrimes */
961558Srgrimesvoid
971558Srgrimesruncmdshell()
981558Srgrimes{
991558Srgrimes	register struct entry *np;
1001558Srgrimes	ino_t ino;
1011558Srgrimes	struct arglist arglist;
1021558Srgrimes	char curdir[MAXPATHLEN];
1031558Srgrimes	char name[MAXPATHLEN];
1041558Srgrimes	char cmd[BUFSIZ];
1051558Srgrimes
1061558Srgrimes	arglist.freeglob = 0;
1071558Srgrimes	arglist.argcnt = 0;
1081558Srgrimes	arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
1091558Srgrimes	arglist.glob.gl_opendir = (void *)rst_opendir;
1101558Srgrimes	arglist.glob.gl_readdir = (void *)glob_readdir;
1111558Srgrimes	arglist.glob.gl_closedir = (void *)rst_closedir;
1121558Srgrimes	arglist.glob.gl_lstat = glob_stat;
1131558Srgrimes	arglist.glob.gl_stat = glob_stat;
11421174Sguido	canon("/", curdir, sizeof(curdir));
1151558Srgrimesloop:
1161558Srgrimes	if (setjmp(reset) != 0) {
1171558Srgrimes		if (arglist.freeglob != 0) {
1181558Srgrimes			arglist.freeglob = 0;
1191558Srgrimes			arglist.argcnt = 0;
1201558Srgrimes			globfree(&arglist.glob);
1211558Srgrimes		}
1221558Srgrimes		nextarg = NULL;
1231558Srgrimes		volno = 0;
1241558Srgrimes	}
1251558Srgrimes	runshell = 1;
12621997Simp	getcmd(curdir, cmd, name, sizeof(name), &arglist);
1271558Srgrimes	switch (cmd[0]) {
1281558Srgrimes	/*
1291558Srgrimes	 * Add elements to the extraction list.
1301558Srgrimes	 */
1311558Srgrimes	case 'a':
1321558Srgrimes		if (strncmp(cmd, "add", strlen(cmd)) != 0)
1331558Srgrimes			goto bad;
1341558Srgrimes		ino = dirlookup(name);
1351558Srgrimes		if (ino == 0)
1361558Srgrimes			break;
1371558Srgrimes		if (mflag)
1381558Srgrimes			pathcheck(name);
1391558Srgrimes		treescan(name, ino, addfile);
1401558Srgrimes		break;
1411558Srgrimes	/*
1421558Srgrimes	 * Change working directory.
1431558Srgrimes	 */
1441558Srgrimes	case 'c':
1451558Srgrimes		if (strncmp(cmd, "cd", strlen(cmd)) != 0)
1461558Srgrimes			goto bad;
1471558Srgrimes		ino = dirlookup(name);
1481558Srgrimes		if (ino == 0)
1491558Srgrimes			break;
1501558Srgrimes		if (inodetype(ino) == LEAF) {
1511558Srgrimes			fprintf(stderr, "%s: not a directory\n", name);
1521558Srgrimes			break;
1531558Srgrimes		}
1541558Srgrimes		(void) strcpy(curdir, name);
1551558Srgrimes		break;
1561558Srgrimes	/*
1571558Srgrimes	 * Delete elements from the extraction list.
1581558Srgrimes	 */
1591558Srgrimes	case 'd':
1601558Srgrimes		if (strncmp(cmd, "delete", strlen(cmd)) != 0)
1611558Srgrimes			goto bad;
1621558Srgrimes		np = lookupname(name);
1631558Srgrimes		if (np == NULL || (np->e_flags & NEW) == 0) {
1641558Srgrimes			fprintf(stderr, "%s: not on extraction list\n", name);
1651558Srgrimes			break;
1661558Srgrimes		}
1671558Srgrimes		treescan(name, np->e_ino, deletefile);
1681558Srgrimes		break;
1691558Srgrimes	/*
1701558Srgrimes	 * Extract the requested list.
1711558Srgrimes	 */
1721558Srgrimes	case 'e':
1731558Srgrimes		if (strncmp(cmd, "extract", strlen(cmd)) != 0)
1741558Srgrimes			goto bad;
1751558Srgrimes		createfiles();
1761558Srgrimes		createlinks();
1771558Srgrimes		setdirmodes(0);
1781558Srgrimes		if (dflag)
1791558Srgrimes			checkrestore();
1801558Srgrimes		volno = 0;
1811558Srgrimes		break;
1821558Srgrimes	/*
1831558Srgrimes	 * List available commands.
1841558Srgrimes	 */
1851558Srgrimes	case 'h':
1861558Srgrimes		if (strncmp(cmd, "help", strlen(cmd)) != 0)
1871558Srgrimes			goto bad;
1881558Srgrimes	case '?':
1891558Srgrimes		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
1901558Srgrimes			"Available commands are:\n",
1911558Srgrimes			"\tls [arg] - list directory\n",
1921558Srgrimes			"\tcd arg - change directory\n",
1931558Srgrimes			"\tpwd - print current directory\n",
1941558Srgrimes			"\tadd [arg] - add `arg' to list of",
1951558Srgrimes			" files to be extracted\n",
1961558Srgrimes			"\tdelete [arg] - delete `arg' from",
1971558Srgrimes			" list of files to be extracted\n",
1981558Srgrimes			"\textract - extract requested files\n",
1991558Srgrimes			"\tsetmodes - set modes of requested directories\n",
2001558Srgrimes			"\tquit - immediately exit program\n",
2011558Srgrimes			"\twhat - list dump header information\n",
2021558Srgrimes			"\tverbose - toggle verbose flag",
2031558Srgrimes			" (useful with ``ls'')\n",
2041558Srgrimes			"\thelp or `?' - print this list\n",
2051558Srgrimes			"If no `arg' is supplied, the current",
2061558Srgrimes			" directory is used\n");
2071558Srgrimes		break;
2081558Srgrimes	/*
2091558Srgrimes	 * List a directory.
2101558Srgrimes	 */
2111558Srgrimes	case 'l':
2121558Srgrimes		if (strncmp(cmd, "ls", strlen(cmd)) != 0)
2131558Srgrimes			goto bad;
2141558Srgrimes		printlist(name, curdir);
2151558Srgrimes		break;
2161558Srgrimes	/*
2171558Srgrimes	 * Print current directory.
2181558Srgrimes	 */
2191558Srgrimes	case 'p':
2201558Srgrimes		if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
2211558Srgrimes			goto bad;
2221558Srgrimes		if (curdir[1] == '\0')
2231558Srgrimes			fprintf(stderr, "/\n");
2241558Srgrimes		else
2251558Srgrimes			fprintf(stderr, "%s\n", &curdir[1]);
2261558Srgrimes		break;
2271558Srgrimes	/*
2281558Srgrimes	 * Quit.
2291558Srgrimes	 */
2301558Srgrimes	case 'q':
2311558Srgrimes		if (strncmp(cmd, "quit", strlen(cmd)) != 0)
2321558Srgrimes			goto bad;
2331558Srgrimes		return;
2341558Srgrimes	case 'x':
2351558Srgrimes		if (strncmp(cmd, "xit", strlen(cmd)) != 0)
2361558Srgrimes			goto bad;
2371558Srgrimes		return;
2381558Srgrimes	/*
2391558Srgrimes	 * Toggle verbose mode.
2401558Srgrimes	 */
2411558Srgrimes	case 'v':
2421558Srgrimes		if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
2431558Srgrimes			goto bad;
2441558Srgrimes		if (vflag) {
2451558Srgrimes			fprintf(stderr, "verbose mode off\n");
2461558Srgrimes			vflag = 0;
2471558Srgrimes			break;
2481558Srgrimes		}
2491558Srgrimes		fprintf(stderr, "verbose mode on\n");
2501558Srgrimes		vflag++;
2511558Srgrimes		break;
2521558Srgrimes	/*
2531558Srgrimes	 * Just restore requested directory modes.
2541558Srgrimes	 */
2551558Srgrimes	case 's':
2561558Srgrimes		if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
2571558Srgrimes			goto bad;
2581558Srgrimes		setdirmodes(FORCE);
2591558Srgrimes		break;
2601558Srgrimes	/*
2611558Srgrimes	 * Print out dump header information.
2621558Srgrimes	 */
2631558Srgrimes	case 'w':
2641558Srgrimes		if (strncmp(cmd, "what", strlen(cmd)) != 0)
2651558Srgrimes			goto bad;
2661558Srgrimes		printdumpinfo();
2671558Srgrimes		break;
2681558Srgrimes	/*
2691558Srgrimes	 * Turn on debugging.
2701558Srgrimes	 */
2711558Srgrimes	case 'D':
2721558Srgrimes		if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
2731558Srgrimes			goto bad;
2741558Srgrimes		if (dflag) {
2751558Srgrimes			fprintf(stderr, "debugging mode off\n");
2761558Srgrimes			dflag = 0;
2771558Srgrimes			break;
2781558Srgrimes		}
2791558Srgrimes		fprintf(stderr, "debugging mode on\n");
2801558Srgrimes		dflag++;
2811558Srgrimes		break;
2821558Srgrimes	/*
2831558Srgrimes	 * Unknown command.
2841558Srgrimes	 */
2851558Srgrimes	default:
2861558Srgrimes	bad:
2871558Srgrimes		fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
2881558Srgrimes		break;
2891558Srgrimes	}
2901558Srgrimes	goto loop;
2911558Srgrimes}
2921558Srgrimes
2931558Srgrimes/*
2941558Srgrimes * Read and parse an interactive command.
2951558Srgrimes * The first word on the line is assigned to "cmd". If
2961558Srgrimes * there are no arguments on the command line, then "curdir"
2971558Srgrimes * is returned as the argument. If there are arguments
2981558Srgrimes * on the line they are returned one at a time on each
2991558Srgrimes * successive call to getcmd. Each argument is first assigned
3001558Srgrimes * to "name". If it does not start with "/" the pathname in
3011558Srgrimes * "curdir" is prepended to it. Finally "canon" is called to
3021558Srgrimes * eliminate any embedded ".." components.
3031558Srgrimes */
3041558Srgrimesstatic void
30521997Simpgetcmd(curdir, cmd, name, size, ap)
3061558Srgrimes	char *curdir, *cmd, *name;
3071558Srgrimes	struct arglist *ap;
30821997Simp	int size;
3091558Srgrimes{
3101558Srgrimes	register char *cp;
3111558Srgrimes	static char input[BUFSIZ];
3121558Srgrimes	char output[BUFSIZ];
3131558Srgrimes#	define rawname input	/* save space by reusing input buffer */
3141558Srgrimes
3151558Srgrimes	/*
3161558Srgrimes	 * Check to see if still processing arguments.
3171558Srgrimes	 */
3181558Srgrimes	if (ap->argcnt > 0)
3191558Srgrimes		goto retnext;
3201558Srgrimes	if (nextarg != NULL)
3211558Srgrimes		goto getnext;
3221558Srgrimes	/*
3231558Srgrimes	 * Read a command line and trim off trailing white space.
3241558Srgrimes	 */
3251558Srgrimes	do	{
3261558Srgrimes		fprintf(stderr, "restore > ");
3271558Srgrimes		(void) fflush(stderr);
3281558Srgrimes		(void) fgets(input, BUFSIZ, terminal);
3291558Srgrimes	} while (!feof(terminal) && input[0] == '\n');
3301558Srgrimes	if (feof(terminal)) {
3311558Srgrimes		(void) strcpy(cmd, "quit");
3321558Srgrimes		return;
3331558Srgrimes	}
3341558Srgrimes	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
3351558Srgrimes		/* trim off trailing white space and newline */;
3361558Srgrimes	*++cp = '\0';
3371558Srgrimes	/*
3381558Srgrimes	 * Copy the command into "cmd".
3391558Srgrimes	 */
3401558Srgrimes	cp = copynext(input, cmd);
3411558Srgrimes	ap->cmd = cmd;
3421558Srgrimes	/*
3431558Srgrimes	 * If no argument, use curdir as the default.
3441558Srgrimes	 */
3451558Srgrimes	if (*cp == '\0') {
3461558Srgrimes		(void) strcpy(name, curdir);
3471558Srgrimes		return;
3481558Srgrimes	}
3491558Srgrimes	nextarg = cp;
3501558Srgrimes	/*
3511558Srgrimes	 * Find the next argument.
3521558Srgrimes	 */
3531558Srgrimesgetnext:
3541558Srgrimes	cp = copynext(nextarg, rawname);
3551558Srgrimes	if (*cp == '\0')
3561558Srgrimes		nextarg = NULL;
3571558Srgrimes	else
3581558Srgrimes		nextarg = cp;
3591558Srgrimes	/*
3601558Srgrimes	 * If it is an absolute pathname, canonicalize it and return it.
3611558Srgrimes	 */
3621558Srgrimes	if (rawname[0] == '/') {
36321997Simp		canon(rawname, name, size);
3641558Srgrimes	} else {
3651558Srgrimes		/*
3661558Srgrimes		 * For relative pathnames, prepend the current directory to
3671558Srgrimes		 * it then canonicalize and return it.
3681558Srgrimes		 */
3691558Srgrimes		(void) strcpy(output, curdir);
3701558Srgrimes		(void) strcat(output, "/");
3711558Srgrimes		(void) strcat(output, rawname);
37221997Simp		canon(output, name, size);
3731558Srgrimes	}
3741558Srgrimes	if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0)
3751558Srgrimes		fprintf(stderr, "%s: out of memory\n", ap->cmd);
3761558Srgrimes	if (ap->glob.gl_pathc == 0)
3771558Srgrimes		return;
3781558Srgrimes	ap->freeglob = 1;
3791558Srgrimes	ap->argcnt = ap->glob.gl_pathc;
3801558Srgrimes
3811558Srgrimesretnext:
3821558Srgrimes	strcpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt]);
3831558Srgrimes	if (--ap->argcnt == 0) {
3841558Srgrimes		ap->freeglob = 0;
3851558Srgrimes		globfree(&ap->glob);
3861558Srgrimes	}
3871558Srgrimes#	undef rawname
3881558Srgrimes}
3891558Srgrimes
3901558Srgrimes/*
3911558Srgrimes * Strip off the next token of the input.
3921558Srgrimes */
3931558Srgrimesstatic char *
3941558Srgrimescopynext(input, output)
3951558Srgrimes	char *input, *output;
3961558Srgrimes{
3971558Srgrimes	register char *cp, *bp;
3981558Srgrimes	char quote;
3991558Srgrimes
4001558Srgrimes	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
4011558Srgrimes		/* skip to argument */;
4021558Srgrimes	bp = output;
4031558Srgrimes	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
4041558Srgrimes		/*
4051558Srgrimes		 * Handle back slashes.
4061558Srgrimes		 */
4071558Srgrimes		if (*cp == '\\') {
4081558Srgrimes			if (*++cp == '\0') {
4091558Srgrimes				fprintf(stderr,
4101558Srgrimes					"command lines cannot be continued\n");
4111558Srgrimes				continue;
4121558Srgrimes			}
4131558Srgrimes			*bp++ = *cp++;
4141558Srgrimes			continue;
4151558Srgrimes		}
4161558Srgrimes		/*
4171558Srgrimes		 * The usual unquoted case.
4181558Srgrimes		 */
4191558Srgrimes		if (*cp != '\'' && *cp != '"') {
4201558Srgrimes			*bp++ = *cp++;
4211558Srgrimes			continue;
4221558Srgrimes		}
4231558Srgrimes		/*
4241558Srgrimes		 * Handle single and double quotes.
4251558Srgrimes		 */
4261558Srgrimes		quote = *cp++;
4271558Srgrimes		while (*cp != quote && *cp != '\0')
4281558Srgrimes			*bp++ = *cp++ | 0200;
4291558Srgrimes		if (*cp++ == '\0') {
4301558Srgrimes			fprintf(stderr, "missing %c\n", quote);
4311558Srgrimes			cp--;
4321558Srgrimes			continue;
4331558Srgrimes		}
4341558Srgrimes	}
4351558Srgrimes	*bp = '\0';
4361558Srgrimes	return (cp);
4371558Srgrimes}
4381558Srgrimes
4391558Srgrimes/*
4401558Srgrimes * Canonicalize file names to always start with ``./'' and
44137906Scharnier * remove any embedded "." and ".." components.
4421558Srgrimes */
4431558Srgrimesvoid
44421174Sguidocanon(rawname, canonname, len)
4451558Srgrimes	char *rawname, *canonname;
44621174Sguido	int len;
4471558Srgrimes{
4481558Srgrimes	register char *cp, *np;
4491558Srgrimes
4501558Srgrimes	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
4511558Srgrimes		(void) strcpy(canonname, "");
4521558Srgrimes	else if (rawname[0] == '/')
4531558Srgrimes		(void) strcpy(canonname, ".");
4541558Srgrimes	else
4551558Srgrimes		(void) strcpy(canonname, "./");
45621174Sguido	if (strlen(canonname) + strlen(rawname) >= len) {
45737906Scharnier		fprintf(stderr, "canonname: not enough buffer space\n");
45821174Sguido		done(1);
45921174Sguido	}
46021174Sguido
4611558Srgrimes	(void) strcat(canonname, rawname);
4621558Srgrimes	/*
4631558Srgrimes	 * Eliminate multiple and trailing '/'s
4641558Srgrimes	 */
4651558Srgrimes	for (cp = np = canonname; *np != '\0'; cp++) {
4661558Srgrimes		*cp = *np++;
4671558Srgrimes		while (*cp == '/' && *np == '/')
4681558Srgrimes			np++;
4691558Srgrimes	}
4701558Srgrimes	*cp = '\0';
4711558Srgrimes	if (*--cp == '/')
4721558Srgrimes		*cp = '\0';
4731558Srgrimes	/*
4741558Srgrimes	 * Eliminate extraneous "." and ".." from pathnames.
4751558Srgrimes	 */
4761558Srgrimes	for (np = canonname; *np != '\0'; ) {
4771558Srgrimes		np++;
4781558Srgrimes		cp = np;
4791558Srgrimes		while (*np != '/' && *np != '\0')
4801558Srgrimes			np++;
4811558Srgrimes		if (np - cp == 1 && *cp == '.') {
4821558Srgrimes			cp--;
4831558Srgrimes			(void) strcpy(cp, np);
4841558Srgrimes			np = cp;
4851558Srgrimes		}
4861558Srgrimes		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
4871558Srgrimes			cp--;
4881558Srgrimes			while (cp > &canonname[1] && *--cp != '/')
4891558Srgrimes				/* find beginning of name */;
4901558Srgrimes			(void) strcpy(cp, np);
4911558Srgrimes			np = cp;
4921558Srgrimes		}
4931558Srgrimes	}
4941558Srgrimes}
4951558Srgrimes
4961558Srgrimes/*
4971558Srgrimes * Do an "ls" style listing of a directory
4981558Srgrimes */
4991558Srgrimesstatic void
5001558Srgrimesprintlist(name, basename)
5011558Srgrimes	char *name;
5021558Srgrimes	char *basename;
5031558Srgrimes{
5041558Srgrimes	register struct afile *fp, *list, *listp;
5051558Srgrimes	register struct direct *dp;
5061558Srgrimes	struct afile single;
5071558Srgrimes	RST_DIR *dirp;
50823685Speter	int entries, len, namelen;
50923685Speter	char locname[MAXPATHLEN + 1];
5101558Srgrimes
5111558Srgrimes	dp = pathsearch(name);
51223685Speter	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
51323685Speter	    (!vflag && dp->d_ino == WINO))
5141558Srgrimes		return;
5151558Srgrimes	if ((dirp = rst_opendir(name)) == NULL) {
5161558Srgrimes		entries = 1;
5171558Srgrimes		list = &single;
51823685Speter		mkentry(name, dp, list);
5191558Srgrimes		len = strlen(basename) + 1;
5201558Srgrimes		if (strlen(name) - len > single.len) {
5211558Srgrimes			freename(single.fname);
5221558Srgrimes			single.fname = savename(&name[len]);
5231558Srgrimes			single.len = strlen(single.fname);
5241558Srgrimes		}
5251558Srgrimes	} else {
5261558Srgrimes		entries = 0;
52737906Scharnier		while ((dp = rst_readdir(dirp)))
5281558Srgrimes			entries++;
5291558Srgrimes		rst_closedir(dirp);
5301558Srgrimes		list = (struct afile *)malloc(entries * sizeof(struct afile));
5311558Srgrimes		if (list == NULL) {
5321558Srgrimes			fprintf(stderr, "ls: out of memory\n");
5331558Srgrimes			return;
5341558Srgrimes		}
5351558Srgrimes		if ((dirp = rst_opendir(name)) == NULL)
5361558Srgrimes			panic("directory reopen failed\n");
5371558Srgrimes		fprintf(stderr, "%s:\n", name);
5381558Srgrimes		entries = 0;
5391558Srgrimes		listp = list;
54023685Speter		(void) strncpy(locname, name, MAXPATHLEN);
54123685Speter		(void) strncat(locname, "/", MAXPATHLEN);
54223685Speter		namelen = strlen(locname);
54337906Scharnier		while ((dp = rst_readdir(dirp))) {
54423685Speter			if (dp == NULL)
5451558Srgrimes				break;
5461558Srgrimes			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
5471558Srgrimes				continue;
54823685Speter			if (!vflag && (dp->d_ino == WINO ||
54923685Speter			     strcmp(dp->d_name, ".") == 0 ||
5501558Srgrimes			     strcmp(dp->d_name, "..") == 0))
5511558Srgrimes				continue;
55223685Speter			locname[namelen] = '\0';
55323685Speter			if (namelen + dp->d_namlen >= MAXPATHLEN) {
55423685Speter				fprintf(stderr, "%s%s: name exceeds %d char\n",
55523685Speter					locname, dp->d_name, MAXPATHLEN);
55623685Speter			} else {
55723685Speter				(void) strncat(locname, dp->d_name,
55823685Speter				    (int)dp->d_namlen);
55923685Speter				mkentry(locname, dp, listp++);
56023685Speter				entries++;
56123685Speter			}
5621558Srgrimes		}
5631558Srgrimes		rst_closedir(dirp);
5641558Srgrimes		if (entries == 0) {
5651558Srgrimes			fprintf(stderr, "\n");
5661558Srgrimes			free(list);
5671558Srgrimes			return;
5681558Srgrimes		}
5691558Srgrimes		qsort((char *)list, entries, sizeof(struct afile), fcmp);
5701558Srgrimes	}
5711558Srgrimes	formatf(list, entries);
5721558Srgrimes	if (dirp != NULL) {
5731558Srgrimes		for (fp = listp - 1; fp >= list; fp--)
5741558Srgrimes			freename(fp->fname);
5751558Srgrimes		fprintf(stderr, "\n");
5761558Srgrimes		free(list);
5771558Srgrimes	}
5781558Srgrimes}
5791558Srgrimes
5801558Srgrimes/*
5811558Srgrimes * Read the contents of a directory.
5821558Srgrimes */
5831558Srgrimesstatic void
58423685Spetermkentry(name, dp, fp)
58523685Speter	char *name;
5861558Srgrimes	struct direct *dp;
5871558Srgrimes	register struct afile *fp;
5881558Srgrimes{
5891558Srgrimes	char *cp;
5901558Srgrimes	struct entry *np;
5911558Srgrimes
5921558Srgrimes	fp->fnum = dp->d_ino;
5931558Srgrimes	fp->fname = savename(dp->d_name);
5941558Srgrimes	for (cp = fp->fname; *cp; cp++)
5951558Srgrimes		if (!vflag && (*cp < ' ' || *cp >= 0177))
5961558Srgrimes			*cp = '?';
5971558Srgrimes	fp->len = cp - fp->fname;
5981558Srgrimes	if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
5991558Srgrimes		fp->prefix = '^';
60023685Speter	else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
6011558Srgrimes		fp->prefix = '*';
6021558Srgrimes	else
6031558Srgrimes		fp->prefix = ' ';
6041558Srgrimes	switch(dp->d_type) {
6051558Srgrimes
6061558Srgrimes	default:
6071558Srgrimes		fprintf(stderr, "Warning: undefined file type %d\n",
6081558Srgrimes		    dp->d_type);
6091558Srgrimes		/* fall through */
6101558Srgrimes	case DT_REG:
6111558Srgrimes		fp->postfix = ' ';
6121558Srgrimes		break;
6131558Srgrimes
6141558Srgrimes	case DT_LNK:
6151558Srgrimes		fp->postfix = '@';
6161558Srgrimes		break;
6171558Srgrimes
6181558Srgrimes	case DT_FIFO:
6191558Srgrimes	case DT_SOCK:
6201558Srgrimes		fp->postfix = '=';
6211558Srgrimes		break;
6221558Srgrimes
6231558Srgrimes	case DT_CHR:
6241558Srgrimes	case DT_BLK:
6251558Srgrimes		fp->postfix = '#';
6261558Srgrimes		break;
6271558Srgrimes
62823685Speter	case DT_WHT:
62923685Speter		fp->postfix = '%';
63023685Speter		break;
63123685Speter
6321558Srgrimes	case DT_UNKNOWN:
6331558Srgrimes	case DT_DIR:
6341558Srgrimes		if (inodetype(dp->d_ino) == NODE)
6351558Srgrimes			fp->postfix = '/';
6361558Srgrimes		else
6371558Srgrimes			fp->postfix = ' ';
6381558Srgrimes		break;
6391558Srgrimes	}
6401558Srgrimes	return;
6411558Srgrimes}
6421558Srgrimes
6431558Srgrimes/*
6441558Srgrimes * Print out a pretty listing of a directory
6451558Srgrimes */
6461558Srgrimesstatic void
6471558Srgrimesformatf(list, nentry)
6481558Srgrimes	register struct afile *list;
6491558Srgrimes	int nentry;
6501558Srgrimes{
6511558Srgrimes	register struct afile *fp, *endlist;
6521558Srgrimes	int width, bigino, haveprefix, havepostfix;
6531558Srgrimes	int i, j, w, precision, columns, lines;
6541558Srgrimes
6551558Srgrimes	width = 0;
6561558Srgrimes	haveprefix = 0;
6571558Srgrimes	havepostfix = 0;
6581558Srgrimes	bigino = ROOTINO;
6591558Srgrimes	endlist = &list[nentry];
6601558Srgrimes	for (fp = &list[0]; fp < endlist; fp++) {
6611558Srgrimes		if (bigino < fp->fnum)
6621558Srgrimes			bigino = fp->fnum;
6631558Srgrimes		if (width < fp->len)
6641558Srgrimes			width = fp->len;
6651558Srgrimes		if (fp->prefix != ' ')
6661558Srgrimes			haveprefix = 1;
6671558Srgrimes		if (fp->postfix != ' ')
6681558Srgrimes			havepostfix = 1;
6691558Srgrimes	}
6701558Srgrimes	if (haveprefix)
6711558Srgrimes		width++;
6721558Srgrimes	if (havepostfix)
6731558Srgrimes		width++;
6741558Srgrimes	if (vflag) {
6751558Srgrimes		for (precision = 0, i = bigino; i > 0; i /= 10)
6761558Srgrimes			precision++;
6771558Srgrimes		width += precision + 1;
6781558Srgrimes	}
6791558Srgrimes	width++;
6801558Srgrimes	columns = 81 / width;
6811558Srgrimes	if (columns == 0)
6821558Srgrimes		columns = 1;
6831558Srgrimes	lines = (nentry + columns - 1) / columns;
6841558Srgrimes	for (i = 0; i < lines; i++) {
6851558Srgrimes		for (j = 0; j < columns; j++) {
6861558Srgrimes			fp = &list[j * lines + i];
6871558Srgrimes			if (vflag) {
6881558Srgrimes				fprintf(stderr, "%*d ", precision, fp->fnum);
6891558Srgrimes				fp->len += precision + 1;
6901558Srgrimes			}
6911558Srgrimes			if (haveprefix) {
6921558Srgrimes				putc(fp->prefix, stderr);
6931558Srgrimes				fp->len++;
6941558Srgrimes			}
6951558Srgrimes			fprintf(stderr, "%s", fp->fname);
6961558Srgrimes			if (havepostfix) {
6971558Srgrimes				putc(fp->postfix, stderr);
6981558Srgrimes				fp->len++;
6991558Srgrimes			}
7001558Srgrimes			if (fp + lines >= endlist) {
7011558Srgrimes				fprintf(stderr, "\n");
7021558Srgrimes				break;
7031558Srgrimes			}
7041558Srgrimes			for (w = fp->len; w < width; w++)
7051558Srgrimes				putc(' ', stderr);
7061558Srgrimes		}
7071558Srgrimes	}
7081558Srgrimes}
7091558Srgrimes
7101558Srgrimes/*
7111558Srgrimes * Skip over directory entries that are not on the tape
7121558Srgrimes *
7131558Srgrimes * First have to get definition of a dirent.
7141558Srgrimes */
7151558Srgrimes#undef DIRBLKSIZ
7161558Srgrimes#include <dirent.h>
7171558Srgrimes#undef d_ino
7181558Srgrimes
7191558Srgrimesstruct dirent *
7201558Srgrimesglob_readdir(dirp)
7211558Srgrimes	RST_DIR *dirp;
7221558Srgrimes{
7231558Srgrimes	struct direct *dp;
7241558Srgrimes	static struct dirent adirent;
7251558Srgrimes
7261558Srgrimes	while ((dp = rst_readdir(dirp)) != NULL) {
72723685Speter		if (!vflag && dp->d_ino == WINO)
7281558Srgrimes			continue;
7291558Srgrimes		if (dflag || TSTINO(dp->d_ino, dumpmap))
7301558Srgrimes			break;
7311558Srgrimes	}
7321558Srgrimes	if (dp == NULL)
7331558Srgrimes		return (NULL);
7341558Srgrimes	adirent.d_fileno = dp->d_ino;
7351558Srgrimes	adirent.d_namlen = dp->d_namlen;
73623685Speter	memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
7371558Srgrimes	return (&adirent);
7381558Srgrimes}
7391558Srgrimes
7401558Srgrimes/*
7411558Srgrimes * Return st_mode information in response to stat or lstat calls
7421558Srgrimes */
7431558Srgrimesstatic int
7441558Srgrimesglob_stat(name, stp)
7451558Srgrimes	const char *name;
7461558Srgrimes	struct stat *stp;
7471558Srgrimes{
7481558Srgrimes	register struct direct *dp;
7491558Srgrimes
7501558Srgrimes	dp = pathsearch(name);
75123685Speter	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
75223685Speter	    (!vflag && dp->d_ino == WINO))
7531558Srgrimes		return (-1);
7541558Srgrimes	if (inodetype(dp->d_ino) == NODE)
7551558Srgrimes		stp->st_mode = IFDIR;
7561558Srgrimes	else
7571558Srgrimes		stp->st_mode = IFREG;
7581558Srgrimes	return (0);
7591558Srgrimes}
7601558Srgrimes
7611558Srgrimes/*
7621558Srgrimes * Comparison routine for qsort.
7631558Srgrimes */
7641558Srgrimesstatic int
7651558Srgrimesfcmp(f1, f2)
7661558Srgrimes	register const void *f1, *f2;
7671558Srgrimes{
7681558Srgrimes	return (strcmp(((struct afile *)f1)->fname,
7691558Srgrimes	    ((struct afile *)f2)->fname));
7701558Srgrimes}
7711558Srgrimes
7721558Srgrimes/*
7731558Srgrimes * respond to interrupts
7741558Srgrimes */
7751558Srgrimesvoid
7761558Srgrimesonintr(signo)
7771558Srgrimes	int signo;
7781558Srgrimes{
7791558Srgrimes	if (command == 'i' && runshell)
7801558Srgrimes		longjmp(reset, 1);
7811558Srgrimes	if (reply("restore interrupted, continue") == FAIL)
7821558Srgrimes		done(1);
7831558Srgrimes}
784