xref: /freebsd/sbin/restore/interactive.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
1 /*
2  * Copyright (c) 1985, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)interactive.c	8.5 (Berkeley) 5/1/95";
37 #endif
38 static const char rcsid[] =
39   "$FreeBSD$";
40 #endif /* not lint */
41 
42 #include <sys/param.h>
43 #include <sys/stat.h>
44 
45 #include <ufs/ufs/dinode.h>
46 #include <ufs/ufs/dir.h>
47 #include <protocols/dumprestore.h>
48 
49 #include <ctype.h>
50 #include <glob.h>
51 #include <limits.h>
52 #include <setjmp.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 
57 #include "restore.h"
58 #include "extern.h"
59 
60 #define round(a, b) (((a) + (b) - 1) / (b) * (b))
61 
62 /*
63  * Things to handle interruptions.
64  */
65 static int runshell;
66 static jmp_buf reset;
67 static char *nextarg = NULL;
68 
69 /*
70  * Structure and routines associated with listing directories.
71  */
72 struct afile {
73 	ino_t	fnum;		/* inode number of file */
74 	char	*fname;		/* file name */
75 	short	len;		/* name length */
76 	char	prefix;		/* prefix character */
77 	char	postfix;	/* postfix character */
78 };
79 struct arglist {
80 	int	freeglob;	/* glob structure needs to be freed */
81 	int	argcnt;		/* next globbed argument to return */
82 	glob_t	glob;		/* globbing information */
83 	char	*cmd;		/* the current command */
84 };
85 
86 static char	*copynext(char *, char *);
87 static int	 fcmp(const void *, const void *);
88 static void	 formatf(struct afile *, int);
89 static void	 getcmd(char *, char *, char *, int, struct arglist *);
90 struct dirent	*glob_readdir(RST_DIR *dirp);
91 static int	 glob_stat(const char *, struct stat *);
92 static void	 mkentry(char *, struct direct *, struct afile *);
93 static void	 printlist(char *, char *);
94 
95 /*
96  * Read and execute commands from the terminal.
97  */
98 void
99 runcmdshell(void)
100 {
101 	struct entry *np;
102 	ino_t ino;
103 	struct arglist arglist;
104 	char curdir[MAXPATHLEN];
105 	char name[MAXPATHLEN];
106 	char cmd[BUFSIZ];
107 
108 	arglist.freeglob = 0;
109 	arglist.argcnt = 0;
110 	arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
111 	arglist.glob.gl_opendir = (void *)rst_opendir;
112 	arglist.glob.gl_readdir = (void *)glob_readdir;
113 	arglist.glob.gl_closedir = (void *)rst_closedir;
114 	arglist.glob.gl_lstat = glob_stat;
115 	arglist.glob.gl_stat = glob_stat;
116 	canon("/", curdir, sizeof(curdir));
117 loop:
118 	if (setjmp(reset) != 0) {
119 		if (arglist.freeglob != 0) {
120 			arglist.freeglob = 0;
121 			arglist.argcnt = 0;
122 			globfree(&arglist.glob);
123 		}
124 		nextarg = NULL;
125 		volno = 0;
126 	}
127 	runshell = 1;
128 	getcmd(curdir, cmd, name, sizeof(name), &arglist);
129 	switch (cmd[0]) {
130 	/*
131 	 * Add elements to the extraction list.
132 	 */
133 	case 'a':
134 		if (strncmp(cmd, "add", strlen(cmd)) != 0)
135 			goto bad;
136 		ino = dirlookup(name);
137 		if (ino == 0)
138 			break;
139 		if (mflag)
140 			pathcheck(name);
141 		treescan(name, ino, addfile);
142 		break;
143 	/*
144 	 * Change working directory.
145 	 */
146 	case 'c':
147 		if (strncmp(cmd, "cd", strlen(cmd)) != 0)
148 			goto bad;
149 		ino = dirlookup(name);
150 		if (ino == 0)
151 			break;
152 		if (inodetype(ino) == LEAF) {
153 			fprintf(stderr, "%s: not a directory\n", name);
154 			break;
155 		}
156 		(void) strcpy(curdir, name);
157 		break;
158 	/*
159 	 * Delete elements from the extraction list.
160 	 */
161 	case 'd':
162 		if (strncmp(cmd, "delete", strlen(cmd)) != 0)
163 			goto bad;
164 		np = lookupname(name);
165 		if (np == NULL || (np->e_flags & NEW) == 0) {
166 			fprintf(stderr, "%s: not on extraction list\n", name);
167 			break;
168 		}
169 		treescan(name, np->e_ino, deletefile);
170 		break;
171 	/*
172 	 * Extract the requested list.
173 	 */
174 	case 'e':
175 		if (strncmp(cmd, "extract", strlen(cmd)) != 0)
176 			goto bad;
177 		createfiles();
178 		createlinks();
179 		setdirmodes(0);
180 		if (dflag)
181 			checkrestore();
182 		volno = 0;
183 		break;
184 	/*
185 	 * List available commands.
186 	 */
187 	case 'h':
188 		if (strncmp(cmd, "help", strlen(cmd)) != 0)
189 			goto bad;
190 	case '?':
191 		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
192 			"Available commands are:\n",
193 			"\tls [arg] - list directory\n",
194 			"\tcd arg - change directory\n",
195 			"\tpwd - print current directory\n",
196 			"\tadd [arg] - add `arg' to list of",
197 			" files to be extracted\n",
198 			"\tdelete [arg] - delete `arg' from",
199 			" list of files to be extracted\n",
200 			"\textract - extract requested files\n",
201 			"\tsetmodes - set modes of requested directories\n",
202 			"\tquit - immediately exit program\n",
203 			"\twhat - list dump header information\n",
204 			"\tverbose - toggle verbose flag",
205 			" (useful with ``ls'')\n",
206 			"\thelp or `?' - print this list\n",
207 			"If no `arg' is supplied, the current",
208 			" directory is used\n");
209 		break;
210 	/*
211 	 * List a directory.
212 	 */
213 	case 'l':
214 		if (strncmp(cmd, "ls", strlen(cmd)) != 0)
215 			goto bad;
216 		printlist(name, curdir);
217 		break;
218 	/*
219 	 * Print current directory.
220 	 */
221 	case 'p':
222 		if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
223 			goto bad;
224 		if (curdir[1] == '\0')
225 			fprintf(stderr, "/\n");
226 		else
227 			fprintf(stderr, "%s\n", &curdir[1]);
228 		break;
229 	/*
230 	 * Quit.
231 	 */
232 	case 'q':
233 		if (strncmp(cmd, "quit", strlen(cmd)) != 0)
234 			goto bad;
235 		return;
236 	case 'x':
237 		if (strncmp(cmd, "xit", strlen(cmd)) != 0)
238 			goto bad;
239 		return;
240 	/*
241 	 * Toggle verbose mode.
242 	 */
243 	case 'v':
244 		if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
245 			goto bad;
246 		if (vflag) {
247 			fprintf(stderr, "verbose mode off\n");
248 			vflag = 0;
249 			break;
250 		}
251 		fprintf(stderr, "verbose mode on\n");
252 		vflag++;
253 		break;
254 	/*
255 	 * Just restore requested directory modes.
256 	 */
257 	case 's':
258 		if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
259 			goto bad;
260 		setdirmodes(FORCE);
261 		break;
262 	/*
263 	 * Print out dump header information.
264 	 */
265 	case 'w':
266 		if (strncmp(cmd, "what", strlen(cmd)) != 0)
267 			goto bad;
268 		printdumpinfo();
269 		break;
270 	/*
271 	 * Turn on debugging.
272 	 */
273 	case 'D':
274 		if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
275 			goto bad;
276 		if (dflag) {
277 			fprintf(stderr, "debugging mode off\n");
278 			dflag = 0;
279 			break;
280 		}
281 		fprintf(stderr, "debugging mode on\n");
282 		dflag++;
283 		break;
284 	/*
285 	 * Unknown command.
286 	 */
287 	default:
288 	bad:
289 		fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
290 		break;
291 	}
292 	goto loop;
293 }
294 
295 /*
296  * Read and parse an interactive command.
297  * The first word on the line is assigned to "cmd". If
298  * there are no arguments on the command line, then "curdir"
299  * is returned as the argument. If there are arguments
300  * on the line they are returned one at a time on each
301  * successive call to getcmd. Each argument is first assigned
302  * to "name". If it does not start with "/" the pathname in
303  * "curdir" is prepended to it. Finally "canon" is called to
304  * eliminate any embedded ".." components.
305  */
306 static void
307 getcmd(char *curdir, char *cmd, char *name, int size, struct arglist *ap)
308 {
309 	char *cp;
310 	static char input[BUFSIZ];
311 	char output[BUFSIZ];
312 #	define rawname input	/* save space by reusing input buffer */
313 
314 	/*
315 	 * Check to see if still processing arguments.
316 	 */
317 	if (ap->argcnt > 0)
318 		goto retnext;
319 	if (nextarg != NULL)
320 		goto getnext;
321 	/*
322 	 * Read a command line and trim off trailing white space.
323 	 */
324 	do	{
325 		fprintf(stderr, "restore > ");
326 		(void) fflush(stderr);
327 		if (fgets(input, BUFSIZ, terminal) == NULL) {
328 			strcpy(cmd, "quit");
329 			return;
330 		}
331 	} while (input[0] == '\n');
332 	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
333 		/* trim off trailing white space and newline */;
334 	*++cp = '\0';
335 	/*
336 	 * Copy the command into "cmd".
337 	 */
338 	cp = copynext(input, cmd);
339 	ap->cmd = cmd;
340 	/*
341 	 * If no argument, use curdir as the default.
342 	 */
343 	if (*cp == '\0') {
344 		(void) strncpy(name, curdir, size);
345 		name[size - 1] = '\0';
346 		return;
347 	}
348 	nextarg = cp;
349 	/*
350 	 * Find the next argument.
351 	 */
352 getnext:
353 	cp = copynext(nextarg, rawname);
354 	if (*cp == '\0')
355 		nextarg = NULL;
356 	else
357 		nextarg = cp;
358 	/*
359 	 * If it is an absolute pathname, canonicalize it and return it.
360 	 */
361 	if (rawname[0] == '/') {
362 		canon(rawname, name, size);
363 	} else {
364 		/*
365 		 * For relative pathnames, prepend the current directory to
366 		 * it then canonicalize and return it.
367 		 */
368 		snprintf(output, sizeof(output), "%s/%s", curdir, rawname);
369 		canon(output, name, size);
370 	}
371 	if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0)
372 		fprintf(stderr, "%s: out of memory\n", ap->cmd);
373 	if (ap->glob.gl_pathc == 0)
374 		return;
375 	ap->freeglob = 1;
376 	ap->argcnt = ap->glob.gl_pathc;
377 
378 retnext:
379 	strncpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size);
380 	name[size - 1] = '\0';
381 	if (--ap->argcnt == 0) {
382 		ap->freeglob = 0;
383 		globfree(&ap->glob);
384 	}
385 #	undef rawname
386 }
387 
388 /*
389  * Strip off the next token of the input.
390  */
391 static char *
392 copynext(char *input, char *output)
393 {
394 	char *cp, *bp;
395 	char quote;
396 
397 	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
398 		/* skip to argument */;
399 	bp = output;
400 	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
401 		/*
402 		 * Handle back slashes.
403 		 */
404 		if (*cp == '\\') {
405 			if (*++cp == '\0') {
406 				fprintf(stderr,
407 					"command lines cannot be continued\n");
408 				continue;
409 			}
410 			*bp++ = *cp++;
411 			continue;
412 		}
413 		/*
414 		 * The usual unquoted case.
415 		 */
416 		if (*cp != '\'' && *cp != '"') {
417 			*bp++ = *cp++;
418 			continue;
419 		}
420 		/*
421 		 * Handle single and double quotes.
422 		 */
423 		quote = *cp++;
424 		while (*cp != quote && *cp != '\0')
425 			*bp++ = *cp++;
426 		if (*cp++ == '\0') {
427 			fprintf(stderr, "missing %c\n", quote);
428 			cp--;
429 			continue;
430 		}
431 	}
432 	*bp = '\0';
433 	return (cp);
434 }
435 
436 /*
437  * Canonicalize file names to always start with ``./'' and
438  * remove any embedded "." and ".." components.
439  */
440 void
441 canon(char *rawname, char *canonname, int len)
442 {
443 	char *cp, *np;
444 
445 	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
446 		(void) strcpy(canonname, "");
447 	else if (rawname[0] == '/')
448 		(void) strcpy(canonname, ".");
449 	else
450 		(void) strcpy(canonname, "./");
451 	if (strlen(canonname) + strlen(rawname) >= len) {
452 		fprintf(stderr, "canonname: not enough buffer space\n");
453 		done(1);
454 	}
455 
456 	(void) strcat(canonname, rawname);
457 	/*
458 	 * Eliminate multiple and trailing '/'s
459 	 */
460 	for (cp = np = canonname; *np != '\0'; cp++) {
461 		*cp = *np++;
462 		while (*cp == '/' && *np == '/')
463 			np++;
464 	}
465 	*cp = '\0';
466 	if (*--cp == '/')
467 		*cp = '\0';
468 	/*
469 	 * Eliminate extraneous "." and ".." from pathnames.
470 	 */
471 	for (np = canonname; *np != '\0'; ) {
472 		np++;
473 		cp = np;
474 		while (*np != '/' && *np != '\0')
475 			np++;
476 		if (np - cp == 1 && *cp == '.') {
477 			cp--;
478 			(void) strcpy(cp, np);
479 			np = cp;
480 		}
481 		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
482 			cp--;
483 			while (cp > &canonname[1] && *--cp != '/')
484 				/* find beginning of name */;
485 			(void) strcpy(cp, np);
486 			np = cp;
487 		}
488 	}
489 }
490 
491 /*
492  * Do an "ls" style listing of a directory
493  */
494 static void
495 printlist(char *name, char *basename)
496 {
497 	struct afile *fp, *list, *listp;
498 	struct direct *dp;
499 	struct afile single;
500 	RST_DIR *dirp;
501 	int entries, len, namelen;
502 	char locname[MAXPATHLEN + 1];
503 
504 	dp = pathsearch(name);
505 	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
506 	    (!vflag && dp->d_ino == WINO))
507 		return;
508 	if ((dirp = rst_opendir(name)) == NULL) {
509 		entries = 1;
510 		list = &single;
511 		mkentry(name, dp, list);
512 		len = strlen(basename) + 1;
513 		if (strlen(name) - len > single.len) {
514 			freename(single.fname);
515 			single.fname = savename(&name[len]);
516 			single.len = strlen(single.fname);
517 		}
518 	} else {
519 		entries = 0;
520 		while ((dp = rst_readdir(dirp)))
521 			entries++;
522 		rst_closedir(dirp);
523 		list = (struct afile *)malloc(entries * sizeof(struct afile));
524 		if (list == NULL) {
525 			fprintf(stderr, "ls: out of memory\n");
526 			return;
527 		}
528 		if ((dirp = rst_opendir(name)) == NULL)
529 			panic("directory reopen failed\n");
530 		fprintf(stderr, "%s:\n", name);
531 		entries = 0;
532 		listp = list;
533 		(void) strncpy(locname, name, MAXPATHLEN);
534 		(void) strncat(locname, "/", MAXPATHLEN);
535 		namelen = strlen(locname);
536 		while ((dp = rst_readdir(dirp))) {
537 			if (dp == NULL)
538 				break;
539 			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
540 				continue;
541 			if (!vflag && (dp->d_ino == WINO ||
542 			     strcmp(dp->d_name, ".") == 0 ||
543 			     strcmp(dp->d_name, "..") == 0))
544 				continue;
545 			locname[namelen] = '\0';
546 			if (namelen + dp->d_namlen >= MAXPATHLEN) {
547 				fprintf(stderr, "%s%s: name exceeds %d char\n",
548 					locname, dp->d_name, MAXPATHLEN);
549 			} else {
550 				(void) strncat(locname, dp->d_name,
551 				    (int)dp->d_namlen);
552 				mkentry(locname, dp, listp++);
553 				entries++;
554 			}
555 		}
556 		rst_closedir(dirp);
557 		if (entries == 0) {
558 			fprintf(stderr, "\n");
559 			free(list);
560 			return;
561 		}
562 		qsort((char *)list, entries, sizeof(struct afile), fcmp);
563 	}
564 	formatf(list, entries);
565 	if (dirp != NULL) {
566 		for (fp = listp - 1; fp >= list; fp--)
567 			freename(fp->fname);
568 		fprintf(stderr, "\n");
569 		free(list);
570 	}
571 }
572 
573 /*
574  * Read the contents of a directory.
575  */
576 static void
577 mkentry(char *name, struct direct *dp, struct afile *fp)
578 {
579 	char *cp;
580 	struct entry *np;
581 
582 	fp->fnum = dp->d_ino;
583 	fp->fname = savename(dp->d_name);
584 	for (cp = fp->fname; *cp; cp++)
585 		if (!vflag && !isprint((unsigned char)*cp))
586 			*cp = '?';
587 	fp->len = cp - fp->fname;
588 	if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
589 		fp->prefix = '^';
590 	else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
591 		fp->prefix = '*';
592 	else
593 		fp->prefix = ' ';
594 	switch(dp->d_type) {
595 
596 	default:
597 		fprintf(stderr, "Warning: undefined file type %d\n",
598 		    dp->d_type);
599 		/* FALLTHROUGH */
600 	case DT_REG:
601 		fp->postfix = ' ';
602 		break;
603 
604 	case DT_LNK:
605 		fp->postfix = '@';
606 		break;
607 
608 	case DT_FIFO:
609 	case DT_SOCK:
610 		fp->postfix = '=';
611 		break;
612 
613 	case DT_CHR:
614 	case DT_BLK:
615 		fp->postfix = '#';
616 		break;
617 
618 	case DT_WHT:
619 		fp->postfix = '%';
620 		break;
621 
622 	case DT_UNKNOWN:
623 	case DT_DIR:
624 		if (inodetype(dp->d_ino) == NODE)
625 			fp->postfix = '/';
626 		else
627 			fp->postfix = ' ';
628 		break;
629 	}
630 	return;
631 }
632 
633 /*
634  * Print out a pretty listing of a directory
635  */
636 static void
637 formatf(struct afile *list, int nentry)
638 {
639 	struct afile *fp, *endlist;
640 	int width, bigino, haveprefix, havepostfix;
641 	int i, j, w, precision, columns, lines;
642 
643 	width = 0;
644 	haveprefix = 0;
645 	havepostfix = 0;
646 	bigino = ROOTINO;
647 	endlist = &list[nentry];
648 	for (fp = &list[0]; fp < endlist; fp++) {
649 		if (bigino < fp->fnum)
650 			bigino = fp->fnum;
651 		if (width < fp->len)
652 			width = fp->len;
653 		if (fp->prefix != ' ')
654 			haveprefix = 1;
655 		if (fp->postfix != ' ')
656 			havepostfix = 1;
657 	}
658 	if (haveprefix)
659 		width++;
660 	if (havepostfix)
661 		width++;
662 	if (vflag) {
663 		for (precision = 0, i = bigino; i > 0; i /= 10)
664 			precision++;
665 		width += precision + 1;
666 	}
667 	width++;
668 	columns = 81 / width;
669 	if (columns == 0)
670 		columns = 1;
671 	lines = (nentry + columns - 1) / columns;
672 	for (i = 0; i < lines; i++) {
673 		for (j = 0; j < columns; j++) {
674 			fp = &list[j * lines + i];
675 			if (vflag) {
676 				fprintf(stderr, "%*d ", precision, fp->fnum);
677 				fp->len += precision + 1;
678 			}
679 			if (haveprefix) {
680 				putc(fp->prefix, stderr);
681 				fp->len++;
682 			}
683 			fprintf(stderr, "%s", fp->fname);
684 			if (havepostfix) {
685 				putc(fp->postfix, stderr);
686 				fp->len++;
687 			}
688 			if (fp + lines >= endlist) {
689 				fprintf(stderr, "\n");
690 				break;
691 			}
692 			for (w = fp->len; w < width; w++)
693 				putc(' ', stderr);
694 		}
695 	}
696 }
697 
698 /*
699  * Skip over directory entries that are not on the tape
700  *
701  * First have to get definition of a dirent.
702  */
703 #undef DIRBLKSIZ
704 #include <dirent.h>
705 #undef d_ino
706 
707 struct dirent *
708 glob_readdir(RST_DIR *dirp)
709 {
710 	struct direct *dp;
711 	static struct dirent adirent;
712 
713 	while ((dp = rst_readdir(dirp)) != NULL) {
714 		if (!vflag && dp->d_ino == WINO)
715 			continue;
716 		if (dflag || TSTINO(dp->d_ino, dumpmap))
717 			break;
718 	}
719 	if (dp == NULL)
720 		return (NULL);
721 	adirent.d_fileno = dp->d_ino;
722 	adirent.d_namlen = dp->d_namlen;
723 	memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
724 	return (&adirent);
725 }
726 
727 /*
728  * Return st_mode information in response to stat or lstat calls
729  */
730 static int
731 glob_stat(const char *name, struct stat *stp)
732 {
733 	struct direct *dp;
734 
735 	dp = pathsearch(name);
736 	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
737 	    (!vflag && dp->d_ino == WINO))
738 		return (-1);
739 	if (inodetype(dp->d_ino) == NODE)
740 		stp->st_mode = IFDIR;
741 	else
742 		stp->st_mode = IFREG;
743 	return (0);
744 }
745 
746 /*
747  * Comparison routine for qsort.
748  */
749 static int
750 fcmp(const void *f1, const void *f2)
751 {
752 	return (strcoll(((struct afile *)f1)->fname,
753 	    ((struct afile *)f2)->fname));
754 }
755 
756 /*
757  * respond to interrupts
758  */
759 void
760 onintr(int signo)
761 {
762 	if (command == 'i' && runshell)
763 		longjmp(reset, 1);
764 	if (reply("restore interrupted, continue") == FAIL)
765 		done(1);
766 }
767