interactive.c revision 1559
168651Skris/*
268651Skris * Copyright (c) 1985, 1993
368651Skris *	The Regents of the University of California.  All rights reserved.
468651Skris *
568651Skris * Redistribution and use in source and binary forms, with or without
668651Skris * modification, are permitted provided that the following conditions
768651Skris * are met:
868651Skris * 1. Redistributions of source code must retain the above copyright
9280304Sjkim *    notice, this list of conditions and the following disclaimer.
1068651Skris * 2. Redistributions in binary form must reproduce the above copyright
1168651Skris *    notice, this list of conditions and the following disclaimer in the
1268651Skris *    documentation and/or other materials provided with the distribution.
1368651Skris * 3. All advertising materials mentioning features or use of this software
1468651Skris *    must display the following acknowledgement:
1568651Skris *	This product includes software developed by the University of
1668651Skris *	California, Berkeley and its contributors.
1768651Skris * 4. Neither the name of the University nor the names of its contributors
1868651Skris *    may be used to endorse or promote products derived from this software
1968651Skris *    without specific prior written permission.
2068651Skris *
2168651Skris * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2268651Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2368651Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2468651Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2568651Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2668651Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2768651Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2868651Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2968651Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3068651Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3168651Skris * SUCH DAMAGE.
3268651Skris */
3368651Skris
3468651Skris#ifndef lint
3568651Skrisstatic char sccsid[] = "@(#)interactive.c	8.1 (Berkeley) 6/5/93";
3668651Skris#endif /* not lint */
3768651Skris
3868651Skris#include <sys/param.h>
3968651Skris#include <sys/time.h>
4068651Skris#include <sys/stat.h>
4168651Skris
4268651Skris#include <ufs/ffs/fs.h>
4368651Skris#include <ufs/ufs/dinode.h>
4468651Skris#include <ufs/ufs/dir.h>
4568651Skris#include <protocols/dumprestore.h>
4668651Skris
4768651Skris#include <setjmp.h>
4868651Skris#include <glob.h>
4968651Skris#include <stdio.h>
5068651Skris#include <stdlib.h>
5168651Skris#include <string.h>
5268651Skris
5368651Skris#include "restore.h"
5468651Skris#include "extern.h"
5568651Skris
56280304Sjkim#define round(a, b) (((a) + (b) - 1) / (b) * (b))
5768651Skris
58280304Sjkim/*
59109998Smarkm * Things to handle interruptions.
60280304Sjkim */
61280304Sjkimstatic int runshell;
62280304Sjkimstatic jmp_buf reset;
63280304Sjkimstatic char *nextarg = NULL;
64280304Sjkim
65280304Sjkim/*
66280304Sjkim * Structure and routines associated with listing directories.
67280304Sjkim */
68280304Sjkimstruct afile {
69280304Sjkim	ino_t	fnum;		/* inode number of file */
70280304Sjkim	char	*fname;		/* file name */
71280304Sjkim	short	len;		/* name length */
72280304Sjkim	char	prefix;		/* prefix character */
7368651Skris	char	postfix;	/* postfix character */
74109998Smarkm};
75280304Sjkimstruct arglist {
76280304Sjkim	int	freeglob;	/* glob structure needs to be freed */
77280304Sjkim	int	argcnt;		/* next globbed argument to return */
78280304Sjkim	glob_t	glob;		/* globbing information */
79109998Smarkm	char	*cmd;		/* the current command */
8068651Skris};
81280304Sjkim
82280304Sjkimstatic char	*copynext __P((char *, char *));
8368651Skrisstatic int	 fcmp __P((const void *, const void *));
84280304Sjkimstatic void	 formatf __P((struct afile *, int));
85280304Sjkimstatic void	 getcmd __P((char *, char *, char *, struct arglist *));
8668651Skrisstruct dirent	*glob_readdir __P((RST_DIR *dirp));
87280304Sjkimstatic int	 glob_stat __P((const char *, struct stat *));
88280304Sjkimstatic void	 mkentry __P((struct direct *, struct afile *));
89280304Sjkimstatic void	 printlist __P((char *, char *));
90280304Sjkim
91280304Sjkim/*
9268651Skris * Read and execute commands from the terminal.
93280304Sjkim */
94280304Sjkimvoid
9568651Skrisruncmdshell()
96280304Sjkim{
97280304Sjkim	register struct entry *np;
98280304Sjkim	ino_t ino;
99280304Sjkim	struct arglist arglist;
100280304Sjkim	char curdir[MAXPATHLEN];
10168651Skris	char name[MAXPATHLEN];
102280304Sjkim	char cmd[BUFSIZ];
103280304Sjkim
10468651Skris	arglist.freeglob = 0;
105280304Sjkim	arglist.argcnt = 0;
106280304Sjkim	arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
107280304Sjkim	arglist.glob.gl_opendir = (void *)rst_opendir;
108280304Sjkim	arglist.glob.gl_readdir = (void *)glob_readdir;
109280304Sjkim	arglist.glob.gl_closedir = (void *)rst_closedir;
11068651Skris	arglist.glob.gl_lstat = glob_stat;
11168651Skris	arglist.glob.gl_stat = glob_stat;
112280304Sjkim	canon("/", curdir);
113280304Sjkimloop:
114280304Sjkim	if (setjmp(reset) != 0) {
115280304Sjkim		if (arglist.freeglob != 0) {
116280304Sjkim			arglist.freeglob = 0;
117280304Sjkim			arglist.argcnt = 0;
118280304Sjkim			globfree(&arglist.glob);
119280304Sjkim		}
120280304Sjkim		nextarg = NULL;
121280304Sjkim		volno = 0;
12268651Skris	}
12368651Skris	runshell = 1;
124280304Sjkim	getcmd(curdir, cmd, name, &arglist);
125280304Sjkim	switch (cmd[0]) {
126280304Sjkim	/*
127280304Sjkim	 * Add elements to the extraction list.
128280304Sjkim	 */
129280304Sjkim	case 'a':
130280304Sjkim		if (strncmp(cmd, "add", strlen(cmd)) != 0)
131280304Sjkim			goto bad;
132280304Sjkim		ino = dirlookup(name);
133280304Sjkim		if (ino == 0)
13468651Skris			break;
13568651Skris		if (mflag)
136280304Sjkim			pathcheck(name);
137280304Sjkim		treescan(name, ino, addfile);
13868651Skris		break;
13968651Skris	/*
140280304Sjkim	 * Change working directory.
141280304Sjkim	 */
142280304Sjkim	case 'c':
143280304Sjkim		if (strncmp(cmd, "cd", strlen(cmd)) != 0)
144280304Sjkim			goto bad;
145280304Sjkim		ino = dirlookup(name);
146280304Sjkim		if (ino == 0)
147280304Sjkim			break;
148280304Sjkim		if (inodetype(ino) == LEAF) {
149280304Sjkim			fprintf(stderr, "%s: not a directory\n", name);
15068651Skris			break;
15168651Skris		}
152280304Sjkim		(void) strcpy(curdir, name);
153280304Sjkim		break;
154280304Sjkim	/*
155280304Sjkim	 * Delete elements from the extraction list.
156280304Sjkim	 */
157280304Sjkim	case 'd':
158280304Sjkim		if (strncmp(cmd, "delete", strlen(cmd)) != 0)
159280304Sjkim			goto bad;
160280304Sjkim		np = lookupname(name);
161280304Sjkim		if (np == NULL || (np->e_flags & NEW) == 0) {
162280304Sjkim			fprintf(stderr, "%s: not on extraction list\n", name);
163280304Sjkim			break;
164280304Sjkim		}
165280304Sjkim		treescan(name, np->e_ino, deletefile);
166280304Sjkim		break;
167280304Sjkim	/*
16868651Skris	 * Extract the requested list.
16968651Skris	 */
170280304Sjkim	case 'e':
171280304Sjkim		if (strncmp(cmd, "extract", strlen(cmd)) != 0)
172280304Sjkim			goto bad;
173280304Sjkim		createfiles();
174280304Sjkim		createlinks();
175280304Sjkim		setdirmodes(0);
176280304Sjkim		if (dflag)
177280304Sjkim			checkrestore();
178280304Sjkim		volno = 0;
179280304Sjkim		break;
180280304Sjkim	/*
181280304Sjkim	 * List available commands.
182280304Sjkim	 */
183280304Sjkim	case 'h':
184280304Sjkim		if (strncmp(cmd, "help", strlen(cmd)) != 0)
185280304Sjkim			goto bad;
186280304Sjkim	case '?':
187280304Sjkim		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
188280304Sjkim			"Available commands are:\n",
189280304Sjkim			"\tls [arg] - list directory\n",
190280304Sjkim			"\tcd arg - change directory\n",
191280304Sjkim			"\tpwd - print current directory\n",
192280304Sjkim			"\tadd [arg] - add `arg' to list of",
193280304Sjkim			" files to be extracted\n",
194280304Sjkim			"\tdelete [arg] - delete `arg' from",
195280304Sjkim			" list of files to be extracted\n",
196280304Sjkim			"\textract - extract requested files\n",
197280304Sjkim			"\tsetmodes - set modes of requested directories\n",
198280304Sjkim			"\tquit - immediately exit program\n",
199280304Sjkim			"\twhat - list dump header information\n",
200280304Sjkim			"\tverbose - toggle verbose flag",
201280304Sjkim			" (useful with ``ls'')\n",
202280304Sjkim			"\thelp or `?' - print this list\n",
203280304Sjkim			"If no `arg' is supplied, the current",
204280304Sjkim			" directory is used\n");
205280304Sjkim		break;
206280304Sjkim	/*
207280304Sjkim	 * List a directory.
208280304Sjkim	 */
209280304Sjkim	case 'l':
210280304Sjkim		if (strncmp(cmd, "ls", strlen(cmd)) != 0)
211280304Sjkim			goto bad;
212280304Sjkim		printlist(name, curdir);
213280304Sjkim		break;
214280304Sjkim	/*
215280304Sjkim	 * Print current directory.
216280304Sjkim	 */
217280304Sjkim	case 'p':
218280304Sjkim		if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
219280304Sjkim			goto bad;
220205128Ssimon		if (curdir[1] == '\0')
22168651Skris			fprintf(stderr, "/\n");
222280304Sjkim		else
223280304Sjkim			fprintf(stderr, "%s\n", &curdir[1]);
224280304Sjkim		break;
225280304Sjkim	/*
226280304Sjkim	 * Quit.
227280304Sjkim	 */
228280304Sjkim	case 'q':
229280304Sjkim		if (strncmp(cmd, "quit", strlen(cmd)) != 0)
230280304Sjkim			goto bad;
231280304Sjkim		return;
232280304Sjkim	case 'x':
233280304Sjkim		if (strncmp(cmd, "xit", strlen(cmd)) != 0)
234280304Sjkim			goto bad;
235280304Sjkim		return;
236280304Sjkim	/*
237280304Sjkim	 * Toggle verbose mode.
238280304Sjkim	 */
239280304Sjkim	case 'v':
240280304Sjkim		if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
241280304Sjkim			goto bad;
242280304Sjkim		if (vflag) {
24368651Skris			fprintf(stderr, "verbose mode off\n");
244109998Smarkm			vflag = 0;
245280304Sjkim			break;
246280304Sjkim		}
247280304Sjkim		fprintf(stderr, "verbose mode on\n");
248280304Sjkim		vflag++;
249280304Sjkim		break;
250280304Sjkim	/*
25168651Skris	 * Just restore requested directory modes.
252109998Smarkm	 */
253280304Sjkim	case 's':
254280304Sjkim		if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
255280304Sjkim			goto bad;
256280304Sjkim		setdirmodes(FORCE);
25768651Skris		break;
258109998Smarkm	/*
259280304Sjkim	 * Print out dump header information.
260280304Sjkim	 */
261280304Sjkim	case 'w':
262280304Sjkim		if (strncmp(cmd, "what", strlen(cmd)) != 0)
263280304Sjkim			goto bad;
264280304Sjkim		printdumpinfo();
265109998Smarkm		break;
266109998Smarkm	/*
267280304Sjkim	 * Turn on debugging.
268280304Sjkim	 */
269280304Sjkim	case 'D':
270280304Sjkim		if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
271280304Sjkim			goto bad;
272280304Sjkim		if (dflag) {
273280304Sjkim			fprintf(stderr, "debugging mode off\n");
274280304Sjkim			dflag = 0;
275280304Sjkim			break;
276280304Sjkim		}
277280304Sjkim		fprintf(stderr, "debugging mode on\n");
278109998Smarkm		dflag++;
279280304Sjkim		break;
280280304Sjkim	/*
281109998Smarkm	 * Unknown command.
282280304Sjkim	 */
283280304Sjkim	default:
284280304Sjkim	bad:
285280304Sjkim		fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
286280304Sjkim		break;
287280304Sjkim	}
288280304Sjkim	goto loop;
289280304Sjkim}
290280304Sjkim
291280304Sjkim/*
292280304Sjkim * Read and parse an interactive command.
293280304Sjkim * The first word on the line is assigned to "cmd". If
294160814Ssimon * there are no arguments on the command line, then "curdir"
295280304Sjkim * is returned as the argument. If there are arguments
296280304Sjkim * on the line they are returned one at a time on each
297280304Sjkim * successive call to getcmd. Each argument is first assigned
298280304Sjkim * to "name". If it does not start with "/" the pathname in
299280304Sjkim * "curdir" is prepended to it. Finally "canon" is called to
300280304Sjkim * eliminate any embedded ".." components.
301280304Sjkim */
302280304Sjkimstatic void
303280304Sjkimgetcmd(curdir, cmd, name, ap)
304280304Sjkim	char *curdir, *cmd, *name;
305280304Sjkim	struct arglist *ap;
306280304Sjkim{
307160814Ssimon	register char *cp;
308280304Sjkim	static char input[BUFSIZ];
309280304Sjkim	char output[BUFSIZ];
310160814Ssimon#	define rawname input	/* save space by reusing input buffer */
311280304Sjkim
312280304Sjkim	/*
313160814Ssimon	 * Check to see if still processing arguments.
314280304Sjkim	 */
315280304Sjkim	if (ap->argcnt > 0)
316280304Sjkim		goto retnext;
317280304Sjkim	if (nextarg != NULL)
318280304Sjkim		goto getnext;
319280304Sjkim	/*
320280304Sjkim	 * Read a command line and trim off trailing white space.
321280304Sjkim	 */
322280304Sjkim	do	{
323280304Sjkim		fprintf(stderr, "restore > ");
324280304Sjkim		(void) fflush(stderr);
325280304Sjkim		(void) fgets(input, BUFSIZ, terminal);
326280304Sjkim	} while (!feof(terminal) && input[0] == '\n');
327280304Sjkim	if (feof(terminal)) {
328280304Sjkim		(void) strcpy(cmd, "quit");
329280304Sjkim		return;
330280304Sjkim	}
331280304Sjkim	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
332280304Sjkim		/* trim off trailing white space and newline */;
333280304Sjkim	*++cp = '\0';
334109998Smarkm	/*
335280304Sjkim	 * Copy the command into "cmd".
336280304Sjkim	 */
337109998Smarkm	cp = copynext(input, cmd);
338280304Sjkim	ap->cmd = cmd;
339280304Sjkim	/*
340109998Smarkm	 * If no argument, use curdir as the default.
341280304Sjkim	 */
342280304Sjkim	if (*cp == '\0') {
343109998Smarkm		(void) strcpy(name, curdir);
344280304Sjkim		return;
345280304Sjkim	}
346109998Smarkm	nextarg = cp;
347280304Sjkim	/*
348280304Sjkim	 * Find the next argument.
349160814Ssimon	 */
350280304Sjkimgetnext:
351280304Sjkim	cp = copynext(nextarg, rawname);
352160814Ssimon	if (*cp == '\0')
353280304Sjkim		nextarg = NULL;
354280304Sjkim	else
355160814Ssimon		nextarg = cp;
356280304Sjkim	/*
357280304Sjkim	 * If it is an absolute pathname, canonicalize it and return it.
358109998Smarkm	 */
359160814Ssimon	if (rawname[0] == '/') {
360280304Sjkim		canon(rawname, name);
361280304Sjkim	} else {
362280304Sjkim		/*
363280304Sjkim		 * For relative pathnames, prepend the current directory to
364280304Sjkim		 * it then canonicalize and return it.
365280304Sjkim		 */
366280304Sjkim		(void) strcpy(output, curdir);
367280304Sjkim		(void) strcat(output, "/");
368280304Sjkim		(void) strcat(output, rawname);
369280304Sjkim		canon(output, name);
370280304Sjkim	}
371280304Sjkim	if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0)
372280304Sjkim		fprintf(stderr, "%s: out of memory\n", ap->cmd);
373280304Sjkim	if (ap->glob.gl_pathc == 0)
374280304Sjkim		return;
375280304Sjkim	ap->freeglob = 1;
376280304Sjkim	ap->argcnt = ap->glob.gl_pathc;
377280304Sjkim
378280304Sjkimretnext:
379280304Sjkim	strcpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt]);
380280304Sjkim	if (--ap->argcnt == 0) {
381280304Sjkim		ap->freeglob = 0;
382280304Sjkim		globfree(&ap->glob);
383280304Sjkim	}
384280304Sjkim#	undef rawname
385280304Sjkim}
386280304Sjkim
387280304Sjkim/*
388280304Sjkim * Strip off the next token of the input.
389280304Sjkim */
390280304Sjkimstatic char *
391280304Sjkimcopynext(input, output)
392280304Sjkim	char *input, *output;
393280304Sjkim{
394280304Sjkim	register char *cp, *bp;
395280304Sjkim	char quote;
396280304Sjkim
397280304Sjkim	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
398280304Sjkim		/* skip to argument */;
399280304Sjkim	bp = output;
400280304Sjkim	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
401280304Sjkim		/*
402280304Sjkim		 * Handle back slashes.
403280304Sjkim		 */
404280304Sjkim		if (*cp == '\\') {
405280304Sjkim			if (*++cp == '\0') {
406280304Sjkim				fprintf(stderr,
407280304Sjkim					"command lines cannot be continued\n");
408160814Ssimon				continue;
409238405Sjkim			}
410280304Sjkim			*bp++ = *cp++;
411280304Sjkim			continue;
412280304Sjkim		}
413280304Sjkim		/*
414280304Sjkim		 * The usual unquoted case.
415280304Sjkim		 */
416238405Sjkim		if (*cp != '\'' && *cp != '"') {
417194206Ssimon			*bp++ = *cp++;
418280304Sjkim			continue;
419280304Sjkim		}
420280304Sjkim		/*
421280304Sjkim		 * Handle single and double quotes.
422280304Sjkim		 */
423280304Sjkim		quote = *cp++;
424280304Sjkim		while (*cp != quote && *cp != '\0')
425280304Sjkim			*bp++ = *cp++ | 0200;
426280304Sjkim		if (*cp++ == '\0') {
427280304Sjkim			fprintf(stderr, "missing %c\n", quote);
428280304Sjkim			cp--;
429280304Sjkim			continue;
430194206Ssimon		}
431205128Ssimon	}
432280304Sjkim	*bp = '\0';
433280304Sjkim	return (cp);
434205128Ssimon}
435238405Sjkim
436280304Sjkim/*
437280304Sjkim * Canonicalize file names to always start with ``./'' and
438280304Sjkim * remove any imbedded "." and ".." components.
439280304Sjkim */
440238405Sjkimvoid
441238405Sjkimcanon(rawname, canonname)
442280304Sjkim	char *rawname, *canonname;
443280304Sjkim{
444280304Sjkim	register char *cp, *np;
445280304Sjkim
446238405Sjkim	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
447280304Sjkim		(void) strcpy(canonname, "");
448109998Smarkm	else if (rawname[0] == '/')
449238405Sjkim		(void) strcpy(canonname, ".");
450280304Sjkim	else
451280304Sjkim		(void) strcpy(canonname, "./");
452280304Sjkim	(void) strcat(canonname, rawname);
453280304Sjkim	/*
454280304Sjkim	 * Eliminate multiple and trailing '/'s
455109998Smarkm	 */
456280304Sjkim	for (cp = np = canonname; *np != '\0'; cp++) {
457280304Sjkim		*cp = *np++;
458280304Sjkim		while (*cp == '/' && *np == '/')
459280304Sjkim			np++;
460280304Sjkim	}
461280304Sjkim	*cp = '\0';
462280304Sjkim	if (*--cp == '/')
463280304Sjkim		*cp = '\0';
464160814Ssimon	/*
465280304Sjkim	 * Eliminate extraneous "." and ".." from pathnames.
466280304Sjkim	 */
467280304Sjkim	for (np = canonname; *np != '\0'; ) {
468280304Sjkim		np++;
469280304Sjkim		cp = np;
470280304Sjkim		while (*np != '/' && *np != '\0')
471280304Sjkim			np++;
472280304Sjkim		if (np - cp == 1 && *cp == '.') {
473280304Sjkim			cp--;
474280304Sjkim			(void) strcpy(cp, np);
475280304Sjkim			np = cp;
476280304Sjkim		}
477280304Sjkim		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
478280304Sjkim			cp--;
479109998Smarkm			while (cp > &canonname[1] && *--cp != '/')
480238405Sjkim				/* find beginning of name */;
481280304Sjkim			(void) strcpy(cp, np);
482280304Sjkim			np = cp;
483109998Smarkm		}
484280304Sjkim	}
48568651Skris}
486280304Sjkim
487/*
488 * Do an "ls" style listing of a directory
489 */
490static void
491printlist(name, basename)
492	char *name;
493	char *basename;
494{
495	register struct afile *fp, *list, *listp;
496	register struct direct *dp;
497	struct afile single;
498	RST_DIR *dirp;
499	int entries, len;
500
501	dp = pathsearch(name);
502	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0))
503		return;
504	if ((dirp = rst_opendir(name)) == NULL) {
505		entries = 1;
506		list = &single;
507		mkentry(dp, list);
508		len = strlen(basename) + 1;
509		if (strlen(name) - len > single.len) {
510			freename(single.fname);
511			single.fname = savename(&name[len]);
512			single.len = strlen(single.fname);
513		}
514	} else {
515		entries = 0;
516		while (dp = rst_readdir(dirp))
517			entries++;
518		rst_closedir(dirp);
519		list = (struct afile *)malloc(entries * sizeof(struct afile));
520		if (list == NULL) {
521			fprintf(stderr, "ls: out of memory\n");
522			return;
523		}
524		if ((dirp = rst_opendir(name)) == NULL)
525			panic("directory reopen failed\n");
526		fprintf(stderr, "%s:\n", name);
527		entries = 0;
528		listp = list;
529		while (dp = rst_readdir(dirp)) {
530			if (dp == NULL || dp->d_ino == 0)
531				break;
532			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
533				continue;
534			if (vflag == 0 &&
535			    (strcmp(dp->d_name, ".") == 0 ||
536			     strcmp(dp->d_name, "..") == 0))
537				continue;
538			mkentry(dp, listp++);
539			entries++;
540		}
541		rst_closedir(dirp);
542		if (entries == 0) {
543			fprintf(stderr, "\n");
544			free(list);
545			return;
546		}
547		qsort((char *)list, entries, sizeof(struct afile), fcmp);
548	}
549	formatf(list, entries);
550	if (dirp != NULL) {
551		for (fp = listp - 1; fp >= list; fp--)
552			freename(fp->fname);
553		fprintf(stderr, "\n");
554		free(list);
555	}
556}
557
558/*
559 * Read the contents of a directory.
560 */
561static void
562mkentry(dp, fp)
563	struct direct *dp;
564	register struct afile *fp;
565{
566	char *cp;
567	struct entry *np;
568
569	fp->fnum = dp->d_ino;
570	fp->fname = savename(dp->d_name);
571	for (cp = fp->fname; *cp; cp++)
572		if (!vflag && (*cp < ' ' || *cp >= 0177))
573			*cp = '?';
574	fp->len = cp - fp->fname;
575	if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
576		fp->prefix = '^';
577	else if ((np = lookupino(fp->fnum)) != NULL && (np->e_flags & NEW))
578		fp->prefix = '*';
579	else
580		fp->prefix = ' ';
581	switch(dp->d_type) {
582
583	default:
584		fprintf(stderr, "Warning: undefined file type %d\n",
585		    dp->d_type);
586		/* fall through */
587	case DT_REG:
588		fp->postfix = ' ';
589		break;
590
591	case DT_LNK:
592		fp->postfix = '@';
593		break;
594
595	case DT_FIFO:
596	case DT_SOCK:
597		fp->postfix = '=';
598		break;
599
600	case DT_CHR:
601	case DT_BLK:
602		fp->postfix = '#';
603		break;
604
605	case DT_UNKNOWN:
606	case DT_DIR:
607		if (inodetype(dp->d_ino) == NODE)
608			fp->postfix = '/';
609		else
610			fp->postfix = ' ';
611		break;
612	}
613	return;
614}
615
616/*
617 * Print out a pretty listing of a directory
618 */
619static void
620formatf(list, nentry)
621	register struct afile *list;
622	int nentry;
623{
624	register struct afile *fp, *endlist;
625	int width, bigino, haveprefix, havepostfix;
626	int i, j, w, precision, columns, lines;
627
628	width = 0;
629	haveprefix = 0;
630	havepostfix = 0;
631	bigino = ROOTINO;
632	endlist = &list[nentry];
633	for (fp = &list[0]; fp < endlist; fp++) {
634		if (bigino < fp->fnum)
635			bigino = fp->fnum;
636		if (width < fp->len)
637			width = fp->len;
638		if (fp->prefix != ' ')
639			haveprefix = 1;
640		if (fp->postfix != ' ')
641			havepostfix = 1;
642	}
643	if (haveprefix)
644		width++;
645	if (havepostfix)
646		width++;
647	if (vflag) {
648		for (precision = 0, i = bigino; i > 0; i /= 10)
649			precision++;
650		width += precision + 1;
651	}
652	width++;
653	columns = 81 / width;
654	if (columns == 0)
655		columns = 1;
656	lines = (nentry + columns - 1) / columns;
657	for (i = 0; i < lines; i++) {
658		for (j = 0; j < columns; j++) {
659			fp = &list[j * lines + i];
660			if (vflag) {
661				fprintf(stderr, "%*d ", precision, fp->fnum);
662				fp->len += precision + 1;
663			}
664			if (haveprefix) {
665				putc(fp->prefix, stderr);
666				fp->len++;
667			}
668			fprintf(stderr, "%s", fp->fname);
669			if (havepostfix) {
670				putc(fp->postfix, stderr);
671				fp->len++;
672			}
673			if (fp + lines >= endlist) {
674				fprintf(stderr, "\n");
675				break;
676			}
677			for (w = fp->len; w < width; w++)
678				putc(' ', stderr);
679		}
680	}
681}
682
683/*
684 * Skip over directory entries that are not on the tape
685 *
686 * First have to get definition of a dirent.
687 */
688#undef DIRBLKSIZ
689#include <dirent.h>
690#undef d_ino
691
692struct dirent *
693glob_readdir(dirp)
694	RST_DIR *dirp;
695{
696	struct direct *dp;
697	static struct dirent adirent;
698
699	while ((dp = rst_readdir(dirp)) != NULL) {
700		if (dp->d_ino == 0)
701			continue;
702		if (dflag || TSTINO(dp->d_ino, dumpmap))
703			break;
704	}
705	if (dp == NULL)
706		return (NULL);
707	adirent.d_fileno = dp->d_ino;
708	adirent.d_namlen = dp->d_namlen;
709	bcopy(dp->d_name, adirent.d_name, dp->d_namlen + 1);
710	return (&adirent);
711}
712
713/*
714 * Return st_mode information in response to stat or lstat calls
715 */
716static int
717glob_stat(name, stp)
718	const char *name;
719	struct stat *stp;
720{
721	register struct direct *dp;
722
723	dp = pathsearch(name);
724	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0))
725		return (-1);
726	if (inodetype(dp->d_ino) == NODE)
727		stp->st_mode = IFDIR;
728	else
729		stp->st_mode = IFREG;
730	return (0);
731}
732
733/*
734 * Comparison routine for qsort.
735 */
736static int
737fcmp(f1, f2)
738	register const void *f1, *f2;
739{
740	return (strcmp(((struct afile *)f1)->fname,
741	    ((struct afile *)f2)->fname));
742}
743
744/*
745 * respond to interrupts
746 */
747void
748onintr(signo)
749	int signo;
750{
751	if (command == 'i' && runshell)
752		longjmp(reset, 1);
753	if (reply("restore interrupted, continue") == FAIL)
754		done(1);
755}
756