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