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 * 4. Neither the name of the University nor the names of its contributors
141558Srgrimes *    may be used to endorse or promote products derived from this software
151558Srgrimes *    without specific prior written permission.
161558Srgrimes *
171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271558Srgrimes * SUCH DAMAGE.
281558Srgrimes */
291558Srgrimes
301558Srgrimes#ifndef lint
3137906Scharnier#if 0
3223685Speterstatic char sccsid[] = "@(#)interactive.c	8.5 (Berkeley) 5/1/95";
3337906Scharnier#endif
341558Srgrimes#endif /* not lint */
351558Srgrimes
36146754Scharnier#include <sys/cdefs.h>
37146754Scharnier__FBSDID("$FreeBSD$");
38146754Scharnier
391558Srgrimes#include <sys/param.h>
401558Srgrimes#include <sys/stat.h>
411558Srgrimes
421558Srgrimes#include <ufs/ufs/dinode.h>
431558Srgrimes#include <ufs/ufs/dir.h>
441558Srgrimes#include <protocols/dumprestore.h>
451558Srgrimes
46118526Sache#include <ctype.h>
471558Srgrimes#include <glob.h>
48103949Smike#include <limits.h>
49118526Sache#include <setjmp.h>
50241013Smdf#include <stdint.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
8492837Simpstatic char	*copynext(char *, char *);
8592837Simpstatic int	 fcmp(const void *, const void *);
8692837Simpstatic void	 formatf(struct afile *, int);
87203155Sjhstatic void	 getcmd(char *, char *, char *, size_t, struct arglist *);
88129666Sstefanfstruct dirent	*glob_readdir(void *);
8992837Simpstatic int	 glob_stat(const char *, struct stat *);
9092837Simpstatic void	 mkentry(char *, struct direct *, struct afile *);
9192837Simpstatic void	 printlist(char *, char *);
921558Srgrimes
931558Srgrimes/*
941558Srgrimes * Read and execute commands from the terminal.
951558Srgrimes */
961558Srgrimesvoid
9792837Simpruncmdshell(void)
981558Srgrimes{
9992806Sobrien	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;
109129666Sstefanf	arglist.glob.gl_opendir = rst_opendir;
110129666Sstefanf	arglist.glob.gl_readdir = glob_readdir;
111129666Sstefanf	arglist.glob.gl_closedir = 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
305203155Sjhgetcmd(char *curdir, char *cmd, char *name, size_t size, struct arglist *ap)
3061558Srgrimes{
30792806Sobrien	char *cp;
3081558Srgrimes	static char input[BUFSIZ];
3091558Srgrimes	char output[BUFSIZ];
3101558Srgrimes#	define rawname input	/* save space by reusing input buffer */
3111558Srgrimes
3121558Srgrimes	/*
3131558Srgrimes	 * Check to see if still processing arguments.
3141558Srgrimes	 */
3151558Srgrimes	if (ap->argcnt > 0)
3161558Srgrimes		goto retnext;
3171558Srgrimes	if (nextarg != NULL)
3181558Srgrimes		goto getnext;
3191558Srgrimes	/*
3201558Srgrimes	 * Read a command line and trim off trailing white space.
3211558Srgrimes	 */
3221558Srgrimes	do	{
3231558Srgrimes		fprintf(stderr, "restore > ");
3241558Srgrimes		(void) fflush(stderr);
32569906Siedowse		if (fgets(input, BUFSIZ, terminal) == NULL) {
32669906Siedowse			strcpy(cmd, "quit");
32769906Siedowse			return;
32869906Siedowse		}
32969906Siedowse	} while (input[0] == '\n');
3301558Srgrimes	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
3311558Srgrimes		/* trim off trailing white space and newline */;
3321558Srgrimes	*++cp = '\0';
3331558Srgrimes	/*
3341558Srgrimes	 * Copy the command into "cmd".
3351558Srgrimes	 */
3361558Srgrimes	cp = copynext(input, cmd);
3371558Srgrimes	ap->cmd = cmd;
3381558Srgrimes	/*
3391558Srgrimes	 * If no argument, use curdir as the default.
3401558Srgrimes	 */
3411558Srgrimes	if (*cp == '\0') {
34241845Simp		(void) strncpy(name, curdir, size);
34341845Simp		name[size - 1] = '\0';
3441558Srgrimes		return;
3451558Srgrimes	}
3461558Srgrimes	nextarg = cp;
3471558Srgrimes	/*
3481558Srgrimes	 * Find the next argument.
3491558Srgrimes	 */
3501558Srgrimesgetnext:
3511558Srgrimes	cp = copynext(nextarg, rawname);
3521558Srgrimes	if (*cp == '\0')
3531558Srgrimes		nextarg = NULL;
3541558Srgrimes	else
3551558Srgrimes		nextarg = cp;
3561558Srgrimes	/*
3571558Srgrimes	 * If it is an absolute pathname, canonicalize it and return it.
3581558Srgrimes	 */
3591558Srgrimes	if (rawname[0] == '/') {
36021997Simp		canon(rawname, name, size);
3611558Srgrimes	} else {
3621558Srgrimes		/*
3631558Srgrimes		 * For relative pathnames, prepend the current directory to
3641558Srgrimes		 * it then canonicalize and return it.
3651558Srgrimes		 */
36639430Simp		snprintf(output, sizeof(output), "%s/%s", curdir, rawname);
36721997Simp		canon(output, name, size);
3681558Srgrimes	}
369148244Sdds	switch (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob)) {
370148244Sdds	case GLOB_NOSPACE:
3711558Srgrimes		fprintf(stderr, "%s: out of memory\n", ap->cmd);
372148244Sdds		break;
373148244Sdds	case GLOB_NOMATCH:
374148244Sdds		fprintf(stderr, "%s %s: no such file or directory\n", ap->cmd, name);
375148244Sdds		break;
376148244Sdds	}
3771558Srgrimes	if (ap->glob.gl_pathc == 0)
3781558Srgrimes		return;
3791558Srgrimes	ap->freeglob = 1;
3801558Srgrimes	ap->argcnt = ap->glob.gl_pathc;
3811558Srgrimes
3821558Srgrimesretnext:
38341845Simp	strncpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size);
38441845Simp	name[size - 1] = '\0';
3851558Srgrimes	if (--ap->argcnt == 0) {
3861558Srgrimes		ap->freeglob = 0;
3871558Srgrimes		globfree(&ap->glob);
3881558Srgrimes	}
3891558Srgrimes#	undef rawname
3901558Srgrimes}
3911558Srgrimes
3921558Srgrimes/*
3931558Srgrimes * Strip off the next token of the input.
3941558Srgrimes */
3951558Srgrimesstatic char *
39692837Simpcopynext(char *input, char *output)
3971558Srgrimes{
39892806Sobrien	char *cp, *bp;
3991558Srgrimes	char quote;
4001558Srgrimes
4011558Srgrimes	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
4021558Srgrimes		/* skip to argument */;
4031558Srgrimes	bp = output;
4041558Srgrimes	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
4051558Srgrimes		/*
4061558Srgrimes		 * Handle back slashes.
4071558Srgrimes		 */
4081558Srgrimes		if (*cp == '\\') {
4091558Srgrimes			if (*++cp == '\0') {
4101558Srgrimes				fprintf(stderr,
4111558Srgrimes					"command lines cannot be continued\n");
4121558Srgrimes				continue;
4131558Srgrimes			}
4141558Srgrimes			*bp++ = *cp++;
4151558Srgrimes			continue;
4161558Srgrimes		}
4171558Srgrimes		/*
4181558Srgrimes		 * The usual unquoted case.
4191558Srgrimes		 */
4201558Srgrimes		if (*cp != '\'' && *cp != '"') {
4211558Srgrimes			*bp++ = *cp++;
4221558Srgrimes			continue;
4231558Srgrimes		}
4241558Srgrimes		/*
4251558Srgrimes		 * Handle single and double quotes.
4261558Srgrimes		 */
4271558Srgrimes		quote = *cp++;
4281558Srgrimes		while (*cp != quote && *cp != '\0')
429118526Sache			*bp++ = *cp++;
4301558Srgrimes		if (*cp++ == '\0') {
4311558Srgrimes			fprintf(stderr, "missing %c\n", quote);
4321558Srgrimes			cp--;
4331558Srgrimes			continue;
4341558Srgrimes		}
4351558Srgrimes	}
4361558Srgrimes	*bp = '\0';
4371558Srgrimes	return (cp);
4381558Srgrimes}
4391558Srgrimes
4401558Srgrimes/*
4411558Srgrimes * Canonicalize file names to always start with ``./'' and
44237906Scharnier * remove any embedded "." and ".." components.
4431558Srgrimes */
4441558Srgrimesvoid
445203155Sjhcanon(char *rawname, char *canonname, size_t len)
4461558Srgrimes{
44792806Sobrien	char *cp, *np;
4481558Srgrimes
4491558Srgrimes	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
4501558Srgrimes		(void) strcpy(canonname, "");
4511558Srgrimes	else if (rawname[0] == '/')
4521558Srgrimes		(void) strcpy(canonname, ".");
4531558Srgrimes	else
4541558Srgrimes		(void) strcpy(canonname, "./");
45521174Sguido	if (strlen(canonname) + strlen(rawname) >= len) {
45637906Scharnier		fprintf(stderr, "canonname: not enough buffer space\n");
45721174Sguido		done(1);
45821174Sguido	}
45921174Sguido
4601558Srgrimes	(void) strcat(canonname, rawname);
4611558Srgrimes	/*
4621558Srgrimes	 * Eliminate multiple and trailing '/'s
4631558Srgrimes	 */
4641558Srgrimes	for (cp = np = canonname; *np != '\0'; cp++) {
4651558Srgrimes		*cp = *np++;
4661558Srgrimes		while (*cp == '/' && *np == '/')
4671558Srgrimes			np++;
4681558Srgrimes	}
4691558Srgrimes	*cp = '\0';
4701558Srgrimes	if (*--cp == '/')
4711558Srgrimes		*cp = '\0';
4721558Srgrimes	/*
4731558Srgrimes	 * Eliminate extraneous "." and ".." from pathnames.
4741558Srgrimes	 */
4751558Srgrimes	for (np = canonname; *np != '\0'; ) {
4761558Srgrimes		np++;
4771558Srgrimes		cp = np;
4781558Srgrimes		while (*np != '/' && *np != '\0')
4791558Srgrimes			np++;
4801558Srgrimes		if (np - cp == 1 && *cp == '.') {
4811558Srgrimes			cp--;
4821558Srgrimes			(void) strcpy(cp, np);
4831558Srgrimes			np = cp;
4841558Srgrimes		}
4851558Srgrimes		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
4861558Srgrimes			cp--;
4871558Srgrimes			while (cp > &canonname[1] && *--cp != '/')
4881558Srgrimes				/* find beginning of name */;
4891558Srgrimes			(void) strcpy(cp, np);
4901558Srgrimes			np = cp;
4911558Srgrimes		}
4921558Srgrimes	}
4931558Srgrimes}
4941558Srgrimes
4951558Srgrimes/*
4961558Srgrimes * Do an "ls" style listing of a directory
4971558Srgrimes */
4981558Srgrimesstatic void
49992837Simpprintlist(char *name, char *basename)
5001558Srgrimes{
50192806Sobrien	struct afile *fp, *list, *listp;
50292806Sobrien	struct direct *dp;
5031558Srgrimes	struct afile single;
5041558Srgrimes	RST_DIR *dirp;
50523685Speter	int entries, len, namelen;
506177894Simp	char locname[MAXPATHLEN];
5071558Srgrimes
5081558Srgrimes	dp = pathsearch(name);
50923685Speter	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
51023685Speter	    (!vflag && dp->d_ino == WINO))
5111558Srgrimes		return;
5121558Srgrimes	if ((dirp = rst_opendir(name)) == NULL) {
5131558Srgrimes		entries = 1;
5141558Srgrimes		list = &single;
51523685Speter		mkentry(name, dp, list);
5161558Srgrimes		len = strlen(basename) + 1;
5171558Srgrimes		if (strlen(name) - len > single.len) {
5181558Srgrimes			freename(single.fname);
5191558Srgrimes			single.fname = savename(&name[len]);
5201558Srgrimes			single.len = strlen(single.fname);
5211558Srgrimes		}
5221558Srgrimes	} else {
5231558Srgrimes		entries = 0;
52437906Scharnier		while ((dp = rst_readdir(dirp)))
5251558Srgrimes			entries++;
5261558Srgrimes		rst_closedir(dirp);
5271558Srgrimes		list = (struct afile *)malloc(entries * sizeof(struct afile));
5281558Srgrimes		if (list == NULL) {
5291558Srgrimes			fprintf(stderr, "ls: out of memory\n");
5301558Srgrimes			return;
5311558Srgrimes		}
5321558Srgrimes		if ((dirp = rst_opendir(name)) == NULL)
5331558Srgrimes			panic("directory reopen failed\n");
5341558Srgrimes		fprintf(stderr, "%s:\n", name);
5351558Srgrimes		entries = 0;
5361558Srgrimes		listp = list;
537177894Simp		(void)strlcpy(locname, name, MAXPATHLEN);
538177894Simp		(void)strlcat(locname, "/", MAXPATHLEN);
53923685Speter		namelen = strlen(locname);
54037906Scharnier		while ((dp = rst_readdir(dirp))) {
54123685Speter			if (dp == NULL)
5421558Srgrimes				break;
5431558Srgrimes			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
5441558Srgrimes				continue;
54523685Speter			if (!vflag && (dp->d_ino == WINO ||
54623685Speter			     strcmp(dp->d_name, ".") == 0 ||
5471558Srgrimes			     strcmp(dp->d_name, "..") == 0))
5481558Srgrimes				continue;
549187828Simp			locname[namelen] = '\0';
55023685Speter			if (namelen + dp->d_namlen >= MAXPATHLEN) {
55123685Speter				fprintf(stderr, "%s%s: name exceeds %d char\n",
55223685Speter					locname, dp->d_name, MAXPATHLEN);
55323685Speter			} else {
554177894Simp				(void)strlcat(locname, dp->d_name, MAXPATHLEN);
55523685Speter				mkentry(locname, dp, listp++);
55623685Speter				entries++;
55723685Speter			}
5581558Srgrimes		}
5591558Srgrimes		rst_closedir(dirp);
5601558Srgrimes		if (entries == 0) {
5611558Srgrimes			fprintf(stderr, "\n");
5621558Srgrimes			free(list);
5631558Srgrimes			return;
5641558Srgrimes		}
5651558Srgrimes		qsort((char *)list, entries, sizeof(struct afile), fcmp);
5661558Srgrimes	}
5671558Srgrimes	formatf(list, entries);
5681558Srgrimes	if (dirp != NULL) {
5691558Srgrimes		for (fp = listp - 1; fp >= list; fp--)
5701558Srgrimes			freename(fp->fname);
5711558Srgrimes		fprintf(stderr, "\n");
5721558Srgrimes		free(list);
5731558Srgrimes	}
5741558Srgrimes}
5751558Srgrimes
5761558Srgrimes/*
5771558Srgrimes * Read the contents of a directory.
5781558Srgrimes */
5791558Srgrimesstatic void
58092837Simpmkentry(char *name, struct direct *dp, struct afile *fp)
5811558Srgrimes{
5821558Srgrimes	char *cp;
5831558Srgrimes	struct entry *np;
5841558Srgrimes
5851558Srgrimes	fp->fnum = dp->d_ino;
5861558Srgrimes	fp->fname = savename(dp->d_name);
5871558Srgrimes	for (cp = fp->fname; *cp; cp++)
588118526Sache		if (!vflag && !isprint((unsigned char)*cp))
5891558Srgrimes			*cp = '?';
5901558Srgrimes	fp->len = cp - fp->fname;
5911558Srgrimes	if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
5921558Srgrimes		fp->prefix = '^';
59323685Speter	else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
5941558Srgrimes		fp->prefix = '*';
5951558Srgrimes	else
5961558Srgrimes		fp->prefix = ' ';
5971558Srgrimes	switch(dp->d_type) {
5981558Srgrimes
5991558Srgrimes	default:
6001558Srgrimes		fprintf(stderr, "Warning: undefined file type %d\n",
6011558Srgrimes		    dp->d_type);
602102411Scharnier		/* FALLTHROUGH */
6031558Srgrimes	case DT_REG:
6041558Srgrimes		fp->postfix = ' ';
6051558Srgrimes		break;
6061558Srgrimes
6071558Srgrimes	case DT_LNK:
6081558Srgrimes		fp->postfix = '@';
6091558Srgrimes		break;
6101558Srgrimes
6111558Srgrimes	case DT_FIFO:
6121558Srgrimes	case DT_SOCK:
6131558Srgrimes		fp->postfix = '=';
6141558Srgrimes		break;
6151558Srgrimes
6161558Srgrimes	case DT_CHR:
6171558Srgrimes	case DT_BLK:
6181558Srgrimes		fp->postfix = '#';
6191558Srgrimes		break;
6201558Srgrimes
62123685Speter	case DT_WHT:
62223685Speter		fp->postfix = '%';
62323685Speter		break;
62423685Speter
6251558Srgrimes	case DT_UNKNOWN:
6261558Srgrimes	case DT_DIR:
6271558Srgrimes		if (inodetype(dp->d_ino) == NODE)
6281558Srgrimes			fp->postfix = '/';
6291558Srgrimes		else
6301558Srgrimes			fp->postfix = ' ';
6311558Srgrimes		break;
6321558Srgrimes	}
6331558Srgrimes	return;
6341558Srgrimes}
6351558Srgrimes
6361558Srgrimes/*
6371558Srgrimes * Print out a pretty listing of a directory
6381558Srgrimes */
6391558Srgrimesstatic void
64092837Simpformatf(struct afile *list, int nentry)
6411558Srgrimes{
64292806Sobrien	struct afile *fp, *endlist;
6431558Srgrimes	int width, bigino, haveprefix, havepostfix;
6441558Srgrimes	int i, j, w, precision, columns, lines;
6451558Srgrimes
6461558Srgrimes	width = 0;
6471558Srgrimes	haveprefix = 0;
6481558Srgrimes	havepostfix = 0;
6491558Srgrimes	bigino = ROOTINO;
6501558Srgrimes	endlist = &list[nentry];
6511558Srgrimes	for (fp = &list[0]; fp < endlist; fp++) {
6521558Srgrimes		if (bigino < fp->fnum)
6531558Srgrimes			bigino = fp->fnum;
6541558Srgrimes		if (width < fp->len)
6551558Srgrimes			width = fp->len;
6561558Srgrimes		if (fp->prefix != ' ')
6571558Srgrimes			haveprefix = 1;
6581558Srgrimes		if (fp->postfix != ' ')
6591558Srgrimes			havepostfix = 1;
6601558Srgrimes	}
6611558Srgrimes	if (haveprefix)
6621558Srgrimes		width++;
6631558Srgrimes	if (havepostfix)
6641558Srgrimes		width++;
6651558Srgrimes	if (vflag) {
6661558Srgrimes		for (precision = 0, i = bigino; i > 0; i /= 10)
6671558Srgrimes			precision++;
6681558Srgrimes		width += precision + 1;
6691558Srgrimes	}
6701558Srgrimes	width++;
6711558Srgrimes	columns = 81 / width;
6721558Srgrimes	if (columns == 0)
6731558Srgrimes		columns = 1;
6741558Srgrimes	lines = (nentry + columns - 1) / columns;
6751558Srgrimes	for (i = 0; i < lines; i++) {
6761558Srgrimes		for (j = 0; j < columns; j++) {
6771558Srgrimes			fp = &list[j * lines + i];
6781558Srgrimes			if (vflag) {
679241013Smdf				fprintf(stderr, "%*ju ",
680241013Smdf				    precision, (uintmax_t)fp->fnum);
6811558Srgrimes				fp->len += precision + 1;
6821558Srgrimes			}
6831558Srgrimes			if (haveprefix) {
6841558Srgrimes				putc(fp->prefix, stderr);
6851558Srgrimes				fp->len++;
6861558Srgrimes			}
6871558Srgrimes			fprintf(stderr, "%s", fp->fname);
6881558Srgrimes			if (havepostfix) {
6891558Srgrimes				putc(fp->postfix, stderr);
6901558Srgrimes				fp->len++;
6911558Srgrimes			}
6921558Srgrimes			if (fp + lines >= endlist) {
6931558Srgrimes				fprintf(stderr, "\n");
6941558Srgrimes				break;
6951558Srgrimes			}
6961558Srgrimes			for (w = fp->len; w < width; w++)
6971558Srgrimes				putc(' ', stderr);
6981558Srgrimes		}
6991558Srgrimes	}
7001558Srgrimes}
7011558Srgrimes
7021558Srgrimes/*
7031558Srgrimes * Skip over directory entries that are not on the tape
7041558Srgrimes *
7051558Srgrimes * First have to get definition of a dirent.
7061558Srgrimes */
7071558Srgrimes#undef DIRBLKSIZ
7081558Srgrimes#include <dirent.h>
7091558Srgrimes#undef d_ino
7101558Srgrimes
7111558Srgrimesstruct dirent *
712129666Sstefanfglob_readdir(void *dirp)
7131558Srgrimes{
7141558Srgrimes	struct direct *dp;
7151558Srgrimes	static struct dirent adirent;
7161558Srgrimes
7171558Srgrimes	while ((dp = rst_readdir(dirp)) != NULL) {
71823685Speter		if (!vflag && dp->d_ino == WINO)
7191558Srgrimes			continue;
7201558Srgrimes		if (dflag || TSTINO(dp->d_ino, dumpmap))
7211558Srgrimes			break;
7221558Srgrimes	}
7231558Srgrimes	if (dp == NULL)
7241558Srgrimes		return (NULL);
7251558Srgrimes	adirent.d_fileno = dp->d_ino;
7261558Srgrimes	adirent.d_namlen = dp->d_namlen;
72723685Speter	memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
7281558Srgrimes	return (&adirent);
7291558Srgrimes}
7301558Srgrimes
7311558Srgrimes/*
7321558Srgrimes * Return st_mode information in response to stat or lstat calls
7331558Srgrimes */
7341558Srgrimesstatic int
73592837Simpglob_stat(const char *name, struct stat *stp)
7361558Srgrimes{
73792806Sobrien	struct direct *dp;
7381558Srgrimes
7391558Srgrimes	dp = pathsearch(name);
74023685Speter	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
74123685Speter	    (!vflag && dp->d_ino == WINO))
7421558Srgrimes		return (-1);
7431558Srgrimes	if (inodetype(dp->d_ino) == NODE)
7441558Srgrimes		stp->st_mode = IFDIR;
7451558Srgrimes	else
7461558Srgrimes		stp->st_mode = IFREG;
7471558Srgrimes	return (0);
7481558Srgrimes}
7491558Srgrimes
7501558Srgrimes/*
7511558Srgrimes * Comparison routine for qsort.
7521558Srgrimes */
7531558Srgrimesstatic int
75492837Simpfcmp(const void *f1, const void *f2)
7551558Srgrimes{
756118526Sache	return (strcoll(((struct afile *)f1)->fname,
7571558Srgrimes	    ((struct afile *)f2)->fname));
7581558Srgrimes}
7591558Srgrimes
7601558Srgrimes/*
7611558Srgrimes * respond to interrupts
7621558Srgrimes */
7631558Srgrimesvoid
764146754Scharnieronintr(int signo __unused)
7651558Srgrimes{
7661558Srgrimes	if (command == 'i' && runshell)
7671558Srgrimes		longjmp(reset, 1);
7681558Srgrimes	if (reply("restore interrupted, continue") == FAIL)
7691558Srgrimes		done(1);
7701558Srgrimes}
771