interactive.c revision 103949
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[] =
3950476Speter  "$FreeBSD: head/sbin/restore/interactive.c 103949 2002-09-25 04:06:37Z mike $";
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>
51103949Smike#include <limits.h>
521558Srgrimes#include <stdio.h>
531558Srgrimes#include <stdlib.h>
541558Srgrimes#include <string.h>
551558Srgrimes
561558Srgrimes#include "restore.h"
571558Srgrimes#include "extern.h"
581558Srgrimes
591558Srgrimes#define round(a, b) (((a) + (b) - 1) / (b) * (b))
601558Srgrimes
611558Srgrimes/*
621558Srgrimes * Things to handle interruptions.
631558Srgrimes */
641558Srgrimesstatic int runshell;
651558Srgrimesstatic jmp_buf reset;
661558Srgrimesstatic char *nextarg = NULL;
671558Srgrimes
681558Srgrimes/*
691558Srgrimes * Structure and routines associated with listing directories.
701558Srgrimes */
711558Srgrimesstruct afile {
721558Srgrimes	ino_t	fnum;		/* inode number of file */
731558Srgrimes	char	*fname;		/* file name */
741558Srgrimes	short	len;		/* name length */
751558Srgrimes	char	prefix;		/* prefix character */
761558Srgrimes	char	postfix;	/* postfix character */
771558Srgrimes};
781558Srgrimesstruct arglist {
791558Srgrimes	int	freeglob;	/* glob structure needs to be freed */
801558Srgrimes	int	argcnt;		/* next globbed argument to return */
811558Srgrimes	glob_t	glob;		/* globbing information */
821558Srgrimes	char	*cmd;		/* the current command */
831558Srgrimes};
841558Srgrimes
8592837Simpstatic char	*copynext(char *, char *);
8692837Simpstatic int	 fcmp(const void *, const void *);
8792837Simpstatic void	 formatf(struct afile *, int);
8892837Simpstatic void	 getcmd(char *, char *, char *, int, struct arglist *);
8992837Simpstruct dirent	*glob_readdir(RST_DIR *dirp);
9092837Simpstatic int	 glob_stat(const char *, struct stat *);
9192837Simpstatic void	 mkentry(char *, struct direct *, struct afile *);
9292837Simpstatic void	 printlist(char *, char *);
931558Srgrimes
941558Srgrimes/*
951558Srgrimes * Read and execute commands from the terminal.
961558Srgrimes */
971558Srgrimesvoid
9892837Simpruncmdshell(void)
991558Srgrimes{
10092806Sobrien	struct entry *np;
1011558Srgrimes	ino_t ino;
1021558Srgrimes	struct arglist arglist;
1031558Srgrimes	char curdir[MAXPATHLEN];
1041558Srgrimes	char name[MAXPATHLEN];
1051558Srgrimes	char cmd[BUFSIZ];
1061558Srgrimes
1071558Srgrimes	arglist.freeglob = 0;
1081558Srgrimes	arglist.argcnt = 0;
1091558Srgrimes	arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
1101558Srgrimes	arglist.glob.gl_opendir = (void *)rst_opendir;
1111558Srgrimes	arglist.glob.gl_readdir = (void *)glob_readdir;
1121558Srgrimes	arglist.glob.gl_closedir = (void *)rst_closedir;
1131558Srgrimes	arglist.glob.gl_lstat = glob_stat;
1141558Srgrimes	arglist.glob.gl_stat = glob_stat;
11521174Sguido	canon("/", curdir, sizeof(curdir));
1161558Srgrimesloop:
1171558Srgrimes	if (setjmp(reset) != 0) {
1181558Srgrimes		if (arglist.freeglob != 0) {
1191558Srgrimes			arglist.freeglob = 0;
1201558Srgrimes			arglist.argcnt = 0;
1211558Srgrimes			globfree(&arglist.glob);
1221558Srgrimes		}
1231558Srgrimes		nextarg = NULL;
1241558Srgrimes		volno = 0;
1251558Srgrimes	}
1261558Srgrimes	runshell = 1;
12721997Simp	getcmd(curdir, cmd, name, sizeof(name), &arglist);
1281558Srgrimes	switch (cmd[0]) {
1291558Srgrimes	/*
1301558Srgrimes	 * Add elements to the extraction list.
1311558Srgrimes	 */
1321558Srgrimes	case 'a':
1331558Srgrimes		if (strncmp(cmd, "add", strlen(cmd)) != 0)
1341558Srgrimes			goto bad;
1351558Srgrimes		ino = dirlookup(name);
1361558Srgrimes		if (ino == 0)
1371558Srgrimes			break;
1381558Srgrimes		if (mflag)
1391558Srgrimes			pathcheck(name);
1401558Srgrimes		treescan(name, ino, addfile);
1411558Srgrimes		break;
1421558Srgrimes	/*
1431558Srgrimes	 * Change working directory.
1441558Srgrimes	 */
1451558Srgrimes	case 'c':
1461558Srgrimes		if (strncmp(cmd, "cd", strlen(cmd)) != 0)
1471558Srgrimes			goto bad;
1481558Srgrimes		ino = dirlookup(name);
1491558Srgrimes		if (ino == 0)
1501558Srgrimes			break;
1511558Srgrimes		if (inodetype(ino) == LEAF) {
1521558Srgrimes			fprintf(stderr, "%s: not a directory\n", name);
1531558Srgrimes			break;
1541558Srgrimes		}
1551558Srgrimes		(void) strcpy(curdir, name);
1561558Srgrimes		break;
1571558Srgrimes	/*
1581558Srgrimes	 * Delete elements from the extraction list.
1591558Srgrimes	 */
1601558Srgrimes	case 'd':
1611558Srgrimes		if (strncmp(cmd, "delete", strlen(cmd)) != 0)
1621558Srgrimes			goto bad;
1631558Srgrimes		np = lookupname(name);
1641558Srgrimes		if (np == NULL || (np->e_flags & NEW) == 0) {
1651558Srgrimes			fprintf(stderr, "%s: not on extraction list\n", name);
1661558Srgrimes			break;
1671558Srgrimes		}
1681558Srgrimes		treescan(name, np->e_ino, deletefile);
1691558Srgrimes		break;
1701558Srgrimes	/*
1711558Srgrimes	 * Extract the requested list.
1721558Srgrimes	 */
1731558Srgrimes	case 'e':
1741558Srgrimes		if (strncmp(cmd, "extract", strlen(cmd)) != 0)
1751558Srgrimes			goto bad;
1761558Srgrimes		createfiles();
1771558Srgrimes		createlinks();
1781558Srgrimes		setdirmodes(0);
1791558Srgrimes		if (dflag)
1801558Srgrimes			checkrestore();
1811558Srgrimes		volno = 0;
1821558Srgrimes		break;
1831558Srgrimes	/*
1841558Srgrimes	 * List available commands.
1851558Srgrimes	 */
1861558Srgrimes	case 'h':
1871558Srgrimes		if (strncmp(cmd, "help", strlen(cmd)) != 0)
1881558Srgrimes			goto bad;
1891558Srgrimes	case '?':
1901558Srgrimes		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
1911558Srgrimes			"Available commands are:\n",
1921558Srgrimes			"\tls [arg] - list directory\n",
1931558Srgrimes			"\tcd arg - change directory\n",
1941558Srgrimes			"\tpwd - print current directory\n",
1951558Srgrimes			"\tadd [arg] - add `arg' to list of",
1961558Srgrimes			" files to be extracted\n",
1971558Srgrimes			"\tdelete [arg] - delete `arg' from",
1981558Srgrimes			" list of files to be extracted\n",
1991558Srgrimes			"\textract - extract requested files\n",
2001558Srgrimes			"\tsetmodes - set modes of requested directories\n",
2011558Srgrimes			"\tquit - immediately exit program\n",
2021558Srgrimes			"\twhat - list dump header information\n",
2031558Srgrimes			"\tverbose - toggle verbose flag",
2041558Srgrimes			" (useful with ``ls'')\n",
2051558Srgrimes			"\thelp or `?' - print this list\n",
2061558Srgrimes			"If no `arg' is supplied, the current",
2071558Srgrimes			" directory is used\n");
2081558Srgrimes		break;
2091558Srgrimes	/*
2101558Srgrimes	 * List a directory.
2111558Srgrimes	 */
2121558Srgrimes	case 'l':
2131558Srgrimes		if (strncmp(cmd, "ls", strlen(cmd)) != 0)
2141558Srgrimes			goto bad;
2151558Srgrimes		printlist(name, curdir);
2161558Srgrimes		break;
2171558Srgrimes	/*
2181558Srgrimes	 * Print current directory.
2191558Srgrimes	 */
2201558Srgrimes	case 'p':
2211558Srgrimes		if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
2221558Srgrimes			goto bad;
2231558Srgrimes		if (curdir[1] == '\0')
2241558Srgrimes			fprintf(stderr, "/\n");
2251558Srgrimes		else
2261558Srgrimes			fprintf(stderr, "%s\n", &curdir[1]);
2271558Srgrimes		break;
2281558Srgrimes	/*
2291558Srgrimes	 * Quit.
2301558Srgrimes	 */
2311558Srgrimes	case 'q':
2321558Srgrimes		if (strncmp(cmd, "quit", strlen(cmd)) != 0)
2331558Srgrimes			goto bad;
2341558Srgrimes		return;
2351558Srgrimes	case 'x':
2361558Srgrimes		if (strncmp(cmd, "xit", strlen(cmd)) != 0)
2371558Srgrimes			goto bad;
2381558Srgrimes		return;
2391558Srgrimes	/*
2401558Srgrimes	 * Toggle verbose mode.
2411558Srgrimes	 */
2421558Srgrimes	case 'v':
2431558Srgrimes		if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
2441558Srgrimes			goto bad;
2451558Srgrimes		if (vflag) {
2461558Srgrimes			fprintf(stderr, "verbose mode off\n");
2471558Srgrimes			vflag = 0;
2481558Srgrimes			break;
2491558Srgrimes		}
2501558Srgrimes		fprintf(stderr, "verbose mode on\n");
2511558Srgrimes		vflag++;
2521558Srgrimes		break;
2531558Srgrimes	/*
2541558Srgrimes	 * Just restore requested directory modes.
2551558Srgrimes	 */
2561558Srgrimes	case 's':
2571558Srgrimes		if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
2581558Srgrimes			goto bad;
2591558Srgrimes		setdirmodes(FORCE);
2601558Srgrimes		break;
2611558Srgrimes	/*
2621558Srgrimes	 * Print out dump header information.
2631558Srgrimes	 */
2641558Srgrimes	case 'w':
2651558Srgrimes		if (strncmp(cmd, "what", strlen(cmd)) != 0)
2661558Srgrimes			goto bad;
2671558Srgrimes		printdumpinfo();
2681558Srgrimes		break;
2691558Srgrimes	/*
2701558Srgrimes	 * Turn on debugging.
2711558Srgrimes	 */
2721558Srgrimes	case 'D':
2731558Srgrimes		if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
2741558Srgrimes			goto bad;
2751558Srgrimes		if (dflag) {
2761558Srgrimes			fprintf(stderr, "debugging mode off\n");
2771558Srgrimes			dflag = 0;
2781558Srgrimes			break;
2791558Srgrimes		}
2801558Srgrimes		fprintf(stderr, "debugging mode on\n");
2811558Srgrimes		dflag++;
2821558Srgrimes		break;
2831558Srgrimes	/*
2841558Srgrimes	 * Unknown command.
2851558Srgrimes	 */
2861558Srgrimes	default:
2871558Srgrimes	bad:
2881558Srgrimes		fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
2891558Srgrimes		break;
2901558Srgrimes	}
2911558Srgrimes	goto loop;
2921558Srgrimes}
2931558Srgrimes
2941558Srgrimes/*
2951558Srgrimes * Read and parse an interactive command.
2961558Srgrimes * The first word on the line is assigned to "cmd". If
2971558Srgrimes * there are no arguments on the command line, then "curdir"
2981558Srgrimes * is returned as the argument. If there are arguments
2991558Srgrimes * on the line they are returned one at a time on each
3001558Srgrimes * successive call to getcmd. Each argument is first assigned
3011558Srgrimes * to "name". If it does not start with "/" the pathname in
3021558Srgrimes * "curdir" is prepended to it. Finally "canon" is called to
3031558Srgrimes * eliminate any embedded ".." components.
3041558Srgrimes */
3051558Srgrimesstatic void
30692837Simpgetcmd(char *curdir, char *cmd, char *name, int size, struct arglist *ap)
3071558Srgrimes{
30892806Sobrien	char *cp;
3091558Srgrimes	static char input[BUFSIZ];
3101558Srgrimes	char output[BUFSIZ];
3111558Srgrimes#	define rawname input	/* save space by reusing input buffer */
3121558Srgrimes
3131558Srgrimes	/*
3141558Srgrimes	 * Check to see if still processing arguments.
3151558Srgrimes	 */
3161558Srgrimes	if (ap->argcnt > 0)
3171558Srgrimes		goto retnext;
3181558Srgrimes	if (nextarg != NULL)
3191558Srgrimes		goto getnext;
3201558Srgrimes	/*
3211558Srgrimes	 * Read a command line and trim off trailing white space.
3221558Srgrimes	 */
3231558Srgrimes	do	{
3241558Srgrimes		fprintf(stderr, "restore > ");
3251558Srgrimes		(void) fflush(stderr);
32669906Siedowse		if (fgets(input, BUFSIZ, terminal) == NULL) {
32769906Siedowse			strcpy(cmd, "quit");
32869906Siedowse			return;
32969906Siedowse		}
33069906Siedowse	} while (input[0] == '\n');
3311558Srgrimes	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
3321558Srgrimes		/* trim off trailing white space and newline */;
3331558Srgrimes	*++cp = '\0';
3341558Srgrimes	/*
3351558Srgrimes	 * Copy the command into "cmd".
3361558Srgrimes	 */
3371558Srgrimes	cp = copynext(input, cmd);
3381558Srgrimes	ap->cmd = cmd;
3391558Srgrimes	/*
3401558Srgrimes	 * If no argument, use curdir as the default.
3411558Srgrimes	 */
3421558Srgrimes	if (*cp == '\0') {
34341845Simp		(void) strncpy(name, curdir, size);
34441845Simp		name[size - 1] = '\0';
3451558Srgrimes		return;
3461558Srgrimes	}
3471558Srgrimes	nextarg = cp;
3481558Srgrimes	/*
3491558Srgrimes	 * Find the next argument.
3501558Srgrimes	 */
3511558Srgrimesgetnext:
3521558Srgrimes	cp = copynext(nextarg, rawname);
3531558Srgrimes	if (*cp == '\0')
3541558Srgrimes		nextarg = NULL;
3551558Srgrimes	else
3561558Srgrimes		nextarg = cp;
3571558Srgrimes	/*
3581558Srgrimes	 * If it is an absolute pathname, canonicalize it and return it.
3591558Srgrimes	 */
3601558Srgrimes	if (rawname[0] == '/') {
36121997Simp		canon(rawname, name, size);
3621558Srgrimes	} else {
3631558Srgrimes		/*
3641558Srgrimes		 * For relative pathnames, prepend the current directory to
3651558Srgrimes		 * it then canonicalize and return it.
3661558Srgrimes		 */
36739430Simp		snprintf(output, sizeof(output), "%s/%s", curdir, rawname);
36821997Simp		canon(output, name, size);
3691558Srgrimes	}
3701558Srgrimes	if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0)
3711558Srgrimes		fprintf(stderr, "%s: out of memory\n", ap->cmd);
3721558Srgrimes	if (ap->glob.gl_pathc == 0)
3731558Srgrimes		return;
3741558Srgrimes	ap->freeglob = 1;
3751558Srgrimes	ap->argcnt = ap->glob.gl_pathc;
3761558Srgrimes
3771558Srgrimesretnext:
37841845Simp	strncpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size);
37941845Simp	name[size - 1] = '\0';
3801558Srgrimes	if (--ap->argcnt == 0) {
3811558Srgrimes		ap->freeglob = 0;
3821558Srgrimes		globfree(&ap->glob);
3831558Srgrimes	}
3841558Srgrimes#	undef rawname
3851558Srgrimes}
3861558Srgrimes
3871558Srgrimes/*
3881558Srgrimes * Strip off the next token of the input.
3891558Srgrimes */
3901558Srgrimesstatic char *
39192837Simpcopynext(char *input, char *output)
3921558Srgrimes{
39392806Sobrien	char *cp, *bp;
3941558Srgrimes	char quote;
3951558Srgrimes
3961558Srgrimes	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
3971558Srgrimes		/* skip to argument */;
3981558Srgrimes	bp = output;
3991558Srgrimes	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
4001558Srgrimes		/*
4011558Srgrimes		 * Handle back slashes.
4021558Srgrimes		 */
4031558Srgrimes		if (*cp == '\\') {
4041558Srgrimes			if (*++cp == '\0') {
4051558Srgrimes				fprintf(stderr,
4061558Srgrimes					"command lines cannot be continued\n");
4071558Srgrimes				continue;
4081558Srgrimes			}
4091558Srgrimes			*bp++ = *cp++;
4101558Srgrimes			continue;
4111558Srgrimes		}
4121558Srgrimes		/*
4131558Srgrimes		 * The usual unquoted case.
4141558Srgrimes		 */
4151558Srgrimes		if (*cp != '\'' && *cp != '"') {
4161558Srgrimes			*bp++ = *cp++;
4171558Srgrimes			continue;
4181558Srgrimes		}
4191558Srgrimes		/*
4201558Srgrimes		 * Handle single and double quotes.
4211558Srgrimes		 */
4221558Srgrimes		quote = *cp++;
4231558Srgrimes		while (*cp != quote && *cp != '\0')
4241558Srgrimes			*bp++ = *cp++ | 0200;
4251558Srgrimes		if (*cp++ == '\0') {
4261558Srgrimes			fprintf(stderr, "missing %c\n", quote);
4271558Srgrimes			cp--;
4281558Srgrimes			continue;
4291558Srgrimes		}
4301558Srgrimes	}
4311558Srgrimes	*bp = '\0';
4321558Srgrimes	return (cp);
4331558Srgrimes}
4341558Srgrimes
4351558Srgrimes/*
4361558Srgrimes * Canonicalize file names to always start with ``./'' and
43737906Scharnier * remove any embedded "." and ".." components.
4381558Srgrimes */
4391558Srgrimesvoid
44092837Simpcanon(char *rawname, char *canonname, int len)
4411558Srgrimes{
44292806Sobrien	char *cp, *np;
4431558Srgrimes
4441558Srgrimes	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
4451558Srgrimes		(void) strcpy(canonname, "");
4461558Srgrimes	else if (rawname[0] == '/')
4471558Srgrimes		(void) strcpy(canonname, ".");
4481558Srgrimes	else
4491558Srgrimes		(void) strcpy(canonname, "./");
45021174Sguido	if (strlen(canonname) + strlen(rawname) >= len) {
45137906Scharnier		fprintf(stderr, "canonname: not enough buffer space\n");
45221174Sguido		done(1);
45321174Sguido	}
45421174Sguido
4551558Srgrimes	(void) strcat(canonname, rawname);
4561558Srgrimes	/*
4571558Srgrimes	 * Eliminate multiple and trailing '/'s
4581558Srgrimes	 */
4591558Srgrimes	for (cp = np = canonname; *np != '\0'; cp++) {
4601558Srgrimes		*cp = *np++;
4611558Srgrimes		while (*cp == '/' && *np == '/')
4621558Srgrimes			np++;
4631558Srgrimes	}
4641558Srgrimes	*cp = '\0';
4651558Srgrimes	if (*--cp == '/')
4661558Srgrimes		*cp = '\0';
4671558Srgrimes	/*
4681558Srgrimes	 * Eliminate extraneous "." and ".." from pathnames.
4691558Srgrimes	 */
4701558Srgrimes	for (np = canonname; *np != '\0'; ) {
4711558Srgrimes		np++;
4721558Srgrimes		cp = np;
4731558Srgrimes		while (*np != '/' && *np != '\0')
4741558Srgrimes			np++;
4751558Srgrimes		if (np - cp == 1 && *cp == '.') {
4761558Srgrimes			cp--;
4771558Srgrimes			(void) strcpy(cp, np);
4781558Srgrimes			np = cp;
4791558Srgrimes		}
4801558Srgrimes		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
4811558Srgrimes			cp--;
4821558Srgrimes			while (cp > &canonname[1] && *--cp != '/')
4831558Srgrimes				/* find beginning of name */;
4841558Srgrimes			(void) strcpy(cp, np);
4851558Srgrimes			np = cp;
4861558Srgrimes		}
4871558Srgrimes	}
4881558Srgrimes}
4891558Srgrimes
4901558Srgrimes/*
4911558Srgrimes * Do an "ls" style listing of a directory
4921558Srgrimes */
4931558Srgrimesstatic void
49492837Simpprintlist(char *name, char *basename)
4951558Srgrimes{
49692806Sobrien	struct afile *fp, *list, *listp;
49792806Sobrien	struct direct *dp;
4981558Srgrimes	struct afile single;
4991558Srgrimes	RST_DIR *dirp;
50023685Speter	int entries, len, namelen;
50123685Speter	char locname[MAXPATHLEN + 1];
5021558Srgrimes
5031558Srgrimes	dp = pathsearch(name);
50423685Speter	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
50523685Speter	    (!vflag && dp->d_ino == WINO))
5061558Srgrimes		return;
5071558Srgrimes	if ((dirp = rst_opendir(name)) == NULL) {
5081558Srgrimes		entries = 1;
5091558Srgrimes		list = &single;
51023685Speter		mkentry(name, dp, list);
5111558Srgrimes		len = strlen(basename) + 1;
5121558Srgrimes		if (strlen(name) - len > single.len) {
5131558Srgrimes			freename(single.fname);
5141558Srgrimes			single.fname = savename(&name[len]);
5151558Srgrimes			single.len = strlen(single.fname);
5161558Srgrimes		}
5171558Srgrimes	} else {
5181558Srgrimes		entries = 0;
51937906Scharnier		while ((dp = rst_readdir(dirp)))
5201558Srgrimes			entries++;
5211558Srgrimes		rst_closedir(dirp);
5221558Srgrimes		list = (struct afile *)malloc(entries * sizeof(struct afile));
5231558Srgrimes		if (list == NULL) {
5241558Srgrimes			fprintf(stderr, "ls: out of memory\n");
5251558Srgrimes			return;
5261558Srgrimes		}
5271558Srgrimes		if ((dirp = rst_opendir(name)) == NULL)
5281558Srgrimes			panic("directory reopen failed\n");
5291558Srgrimes		fprintf(stderr, "%s:\n", name);
5301558Srgrimes		entries = 0;
5311558Srgrimes		listp = list;
53223685Speter		(void) strncpy(locname, name, MAXPATHLEN);
53323685Speter		(void) strncat(locname, "/", MAXPATHLEN);
53423685Speter		namelen = strlen(locname);
53537906Scharnier		while ((dp = rst_readdir(dirp))) {
53623685Speter			if (dp == NULL)
5371558Srgrimes				break;
5381558Srgrimes			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
5391558Srgrimes				continue;
54023685Speter			if (!vflag && (dp->d_ino == WINO ||
54123685Speter			     strcmp(dp->d_name, ".") == 0 ||
5421558Srgrimes			     strcmp(dp->d_name, "..") == 0))
5431558Srgrimes				continue;
54423685Speter			locname[namelen] = '\0';
54523685Speter			if (namelen + dp->d_namlen >= MAXPATHLEN) {
54623685Speter				fprintf(stderr, "%s%s: name exceeds %d char\n",
54723685Speter					locname, dp->d_name, MAXPATHLEN);
54823685Speter			} else {
54923685Speter				(void) strncat(locname, dp->d_name,
55023685Speter				    (int)dp->d_namlen);
55123685Speter				mkentry(locname, dp, listp++);
55223685Speter				entries++;
55323685Speter			}
5541558Srgrimes		}
5551558Srgrimes		rst_closedir(dirp);
5561558Srgrimes		if (entries == 0) {
5571558Srgrimes			fprintf(stderr, "\n");
5581558Srgrimes			free(list);
5591558Srgrimes			return;
5601558Srgrimes		}
5611558Srgrimes		qsort((char *)list, entries, sizeof(struct afile), fcmp);
5621558Srgrimes	}
5631558Srgrimes	formatf(list, entries);
5641558Srgrimes	if (dirp != NULL) {
5651558Srgrimes		for (fp = listp - 1; fp >= list; fp--)
5661558Srgrimes			freename(fp->fname);
5671558Srgrimes		fprintf(stderr, "\n");
5681558Srgrimes		free(list);
5691558Srgrimes	}
5701558Srgrimes}
5711558Srgrimes
5721558Srgrimes/*
5731558Srgrimes * Read the contents of a directory.
5741558Srgrimes */
5751558Srgrimesstatic void
57692837Simpmkentry(char *name, struct direct *dp, struct afile *fp)
5771558Srgrimes{
5781558Srgrimes	char *cp;
5791558Srgrimes	struct entry *np;
5801558Srgrimes
5811558Srgrimes	fp->fnum = dp->d_ino;
5821558Srgrimes	fp->fname = savename(dp->d_name);
5831558Srgrimes	for (cp = fp->fname; *cp; cp++)
5841558Srgrimes		if (!vflag && (*cp < ' ' || *cp >= 0177))
5851558Srgrimes			*cp = '?';
5861558Srgrimes	fp->len = cp - fp->fname;
5871558Srgrimes	if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
5881558Srgrimes		fp->prefix = '^';
58923685Speter	else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
5901558Srgrimes		fp->prefix = '*';
5911558Srgrimes	else
5921558Srgrimes		fp->prefix = ' ';
5931558Srgrimes	switch(dp->d_type) {
5941558Srgrimes
5951558Srgrimes	default:
5961558Srgrimes		fprintf(stderr, "Warning: undefined file type %d\n",
5971558Srgrimes		    dp->d_type);
598102411Scharnier		/* FALLTHROUGH */
5991558Srgrimes	case DT_REG:
6001558Srgrimes		fp->postfix = ' ';
6011558Srgrimes		break;
6021558Srgrimes
6031558Srgrimes	case DT_LNK:
6041558Srgrimes		fp->postfix = '@';
6051558Srgrimes		break;
6061558Srgrimes
6071558Srgrimes	case DT_FIFO:
6081558Srgrimes	case DT_SOCK:
6091558Srgrimes		fp->postfix = '=';
6101558Srgrimes		break;
6111558Srgrimes
6121558Srgrimes	case DT_CHR:
6131558Srgrimes	case DT_BLK:
6141558Srgrimes		fp->postfix = '#';
6151558Srgrimes		break;
6161558Srgrimes
61723685Speter	case DT_WHT:
61823685Speter		fp->postfix = '%';
61923685Speter		break;
62023685Speter
6211558Srgrimes	case DT_UNKNOWN:
6221558Srgrimes	case DT_DIR:
6231558Srgrimes		if (inodetype(dp->d_ino) == NODE)
6241558Srgrimes			fp->postfix = '/';
6251558Srgrimes		else
6261558Srgrimes			fp->postfix = ' ';
6271558Srgrimes		break;
6281558Srgrimes	}
6291558Srgrimes	return;
6301558Srgrimes}
6311558Srgrimes
6321558Srgrimes/*
6331558Srgrimes * Print out a pretty listing of a directory
6341558Srgrimes */
6351558Srgrimesstatic void
63692837Simpformatf(struct afile *list, int nentry)
6371558Srgrimes{
63892806Sobrien	struct afile *fp, *endlist;
6391558Srgrimes	int width, bigino, haveprefix, havepostfix;
6401558Srgrimes	int i, j, w, precision, columns, lines;
6411558Srgrimes
6421558Srgrimes	width = 0;
6431558Srgrimes	haveprefix = 0;
6441558Srgrimes	havepostfix = 0;
6451558Srgrimes	bigino = ROOTINO;
6461558Srgrimes	endlist = &list[nentry];
6471558Srgrimes	for (fp = &list[0]; fp < endlist; fp++) {
6481558Srgrimes		if (bigino < fp->fnum)
6491558Srgrimes			bigino = fp->fnum;
6501558Srgrimes		if (width < fp->len)
6511558Srgrimes			width = fp->len;
6521558Srgrimes		if (fp->prefix != ' ')
6531558Srgrimes			haveprefix = 1;
6541558Srgrimes		if (fp->postfix != ' ')
6551558Srgrimes			havepostfix = 1;
6561558Srgrimes	}
6571558Srgrimes	if (haveprefix)
6581558Srgrimes		width++;
6591558Srgrimes	if (havepostfix)
6601558Srgrimes		width++;
6611558Srgrimes	if (vflag) {
6621558Srgrimes		for (precision = 0, i = bigino; i > 0; i /= 10)
6631558Srgrimes			precision++;
6641558Srgrimes		width += precision + 1;
6651558Srgrimes	}
6661558Srgrimes	width++;
6671558Srgrimes	columns = 81 / width;
6681558Srgrimes	if (columns == 0)
6691558Srgrimes		columns = 1;
6701558Srgrimes	lines = (nentry + columns - 1) / columns;
6711558Srgrimes	for (i = 0; i < lines; i++) {
6721558Srgrimes		for (j = 0; j < columns; j++) {
6731558Srgrimes			fp = &list[j * lines + i];
6741558Srgrimes			if (vflag) {
6751558Srgrimes				fprintf(stderr, "%*d ", precision, fp->fnum);
6761558Srgrimes				fp->len += precision + 1;
6771558Srgrimes			}
6781558Srgrimes			if (haveprefix) {
6791558Srgrimes				putc(fp->prefix, stderr);
6801558Srgrimes				fp->len++;
6811558Srgrimes			}
6821558Srgrimes			fprintf(stderr, "%s", fp->fname);
6831558Srgrimes			if (havepostfix) {
6841558Srgrimes				putc(fp->postfix, stderr);
6851558Srgrimes				fp->len++;
6861558Srgrimes			}
6871558Srgrimes			if (fp + lines >= endlist) {
6881558Srgrimes				fprintf(stderr, "\n");
6891558Srgrimes				break;
6901558Srgrimes			}
6911558Srgrimes			for (w = fp->len; w < width; w++)
6921558Srgrimes				putc(' ', stderr);
6931558Srgrimes		}
6941558Srgrimes	}
6951558Srgrimes}
6961558Srgrimes
6971558Srgrimes/*
6981558Srgrimes * Skip over directory entries that are not on the tape
6991558Srgrimes *
7001558Srgrimes * First have to get definition of a dirent.
7011558Srgrimes */
7021558Srgrimes#undef DIRBLKSIZ
7031558Srgrimes#include <dirent.h>
7041558Srgrimes#undef d_ino
7051558Srgrimes
7061558Srgrimesstruct dirent *
70792837Simpglob_readdir(RST_DIR *dirp)
7081558Srgrimes{
7091558Srgrimes	struct direct *dp;
7101558Srgrimes	static struct dirent adirent;
7111558Srgrimes
7121558Srgrimes	while ((dp = rst_readdir(dirp)) != NULL) {
71323685Speter		if (!vflag && dp->d_ino == WINO)
7141558Srgrimes			continue;
7151558Srgrimes		if (dflag || TSTINO(dp->d_ino, dumpmap))
7161558Srgrimes			break;
7171558Srgrimes	}
7181558Srgrimes	if (dp == NULL)
7191558Srgrimes		return (NULL);
7201558Srgrimes	adirent.d_fileno = dp->d_ino;
7211558Srgrimes	adirent.d_namlen = dp->d_namlen;
72223685Speter	memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
7231558Srgrimes	return (&adirent);
7241558Srgrimes}
7251558Srgrimes
7261558Srgrimes/*
7271558Srgrimes * Return st_mode information in response to stat or lstat calls
7281558Srgrimes */
7291558Srgrimesstatic int
73092837Simpglob_stat(const char *name, struct stat *stp)
7311558Srgrimes{
73292806Sobrien	struct direct *dp;
7331558Srgrimes
7341558Srgrimes	dp = pathsearch(name);
73523685Speter	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
73623685Speter	    (!vflag && dp->d_ino == WINO))
7371558Srgrimes		return (-1);
7381558Srgrimes	if (inodetype(dp->d_ino) == NODE)
7391558Srgrimes		stp->st_mode = IFDIR;
7401558Srgrimes	else
7411558Srgrimes		stp->st_mode = IFREG;
7421558Srgrimes	return (0);
7431558Srgrimes}
7441558Srgrimes
7451558Srgrimes/*
7461558Srgrimes * Comparison routine for qsort.
7471558Srgrimes */
7481558Srgrimesstatic int
74992837Simpfcmp(const void *f1, const void *f2)
7501558Srgrimes{
7511558Srgrimes	return (strcmp(((struct afile *)f1)->fname,
7521558Srgrimes	    ((struct afile *)f2)->fname));
7531558Srgrimes}
7541558Srgrimes
7551558Srgrimes/*
7561558Srgrimes * respond to interrupts
7571558Srgrimes */
7581558Srgrimesvoid
75992837Simponintr(int signo)
7601558Srgrimes{
7611558Srgrimes	if (command == 'i' && runshell)
7621558Srgrimes		longjmp(reset, 1);
7631558Srgrimes	if (reply("restore interrupted, continue") == FAIL)
7641558Srgrimes		done(1);
7651558Srgrimes}
766