xref: /original-bsd/sbin/restore/interactive.c (revision e0851aa6)
1 /*
2  * Copyright (c) 1985 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)interactive.c	5.17 (Berkeley) 10/16/92";
10 #endif /* not lint */
11 
12 #include <sys/param.h>
13 #include <sys/time.h>
14 
15 #include <ufs/ffs/fs.h>
16 #include <ufs/ufs/dinode.h>
17 #include <ufs/ufs/dir.h>
18 #include <protocols/dumprestore.h>
19 
20 #include <setjmp.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "restore.h"
26 #include "extern.h"
27 
28 #define round(a, b) (((a) + (b) - 1) / (b) * (b))
29 
30 /*
31  * Things to handle interruptions.
32  */
33 static int runshell;
34 static jmp_buf reset;
35 static char *nextarg = NULL;
36 
37 /*
38  * Structure and routines associated with listing directories.
39  */
40 struct afile {
41 	ino_t	fnum;		/* inode number of file */
42 	char	*fname;		/* file name */
43 	short	fflags;		/* extraction flags, if any */
44 	char	ftype;		/* file type, e.g. LEAF or NODE */
45 	char	finotype;	/* file type specified in directory entry */
46 };
47 struct arglist {
48 	struct afile	*head;	/* start of argument list */
49 	struct afile	*last;	/* end of argument list */
50 	struct afile	*base;	/* current list arena */
51 	int		nent;	/* maximum size of list */
52 	char		*cmd;	/* the current command */
53 };
54 
55 static int	 addg __P((struct direct *, char *, char *, struct arglist *));
56 static char	*copynext __P((char *, char *));
57 static int	 expand __P((char *, int, struct arglist *));
58 static void	 expandarg __P((char *, struct arglist *));
59 static int	 fcmp __P((const void *, const void *));
60 static char	*fmtentry __P((struct afile *));
61 static void	 formatf __P((struct arglist *));
62 static void	 getcmd __P((char *, char *, char *, struct arglist *));
63 static int	 gmatch __P((char *, char *));
64 static int	 mkentry __P((char *, struct direct *, struct arglist *));
65 static void	 printlist __P((char *, ino_t, char *));
66 
67 /*
68  * Read and execute commands from the terminal.
69  */
70 void
71 runcmdshell()
72 {
73 	register struct entry *np;
74 	ino_t ino;
75 	static struct arglist alist = { 0, 0, 0, 0, 0 };
76 	char curdir[MAXPATHLEN];
77 	char name[MAXPATHLEN];
78 	char cmd[BUFSIZ];
79 
80 	canon("/", curdir);
81 loop:
82 	if (setjmp(reset) != 0) {
83 		for (; alist.head < alist.last; alist.head++)
84 			freename(alist.head->fname);
85 		nextarg = NULL;
86 		volno = 0;
87 	}
88 	runshell = 1;
89 	getcmd(curdir, cmd, name, &alist);
90 	switch (cmd[0]) {
91 	/*
92 	 * Add elements to the extraction list.
93 	 */
94 	case 'a':
95 		if (strncmp(cmd, "add", strlen(cmd)) != 0)
96 			goto bad;
97 		ino = dirlookup(name);
98 		if (ino == 0)
99 			break;
100 		if (mflag)
101 			pathcheck(name);
102 		treescan(name, ino, addfile);
103 		break;
104 	/*
105 	 * Change working directory.
106 	 */
107 	case 'c':
108 		if (strncmp(cmd, "cd", strlen(cmd)) != 0)
109 			goto bad;
110 		ino = dirlookup(name);
111 		if (ino == 0)
112 			break;
113 		if (inodetype(ino) == LEAF) {
114 			fprintf(stderr, "%s: not a directory\n", name);
115 			break;
116 		}
117 		(void) strcpy(curdir, name);
118 		break;
119 	/*
120 	 * Delete elements from the extraction list.
121 	 */
122 	case 'd':
123 		if (strncmp(cmd, "delete", strlen(cmd)) != 0)
124 			goto bad;
125 		np = lookupname(name);
126 		if (np == NULL || (np->e_flags & NEW) == 0) {
127 			fprintf(stderr, "%s: not on extraction list\n", name);
128 			break;
129 		}
130 		treescan(name, np->e_ino, deletefile);
131 		break;
132 	/*
133 	 * Extract the requested list.
134 	 */
135 	case 'e':
136 		if (strncmp(cmd, "extract", strlen(cmd)) != 0)
137 			goto bad;
138 		createfiles();
139 		createlinks();
140 		setdirmodes(0);
141 		if (dflag)
142 			checkrestore();
143 		volno = 0;
144 		break;
145 	/*
146 	 * List available commands.
147 	 */
148 	case 'h':
149 		if (strncmp(cmd, "help", strlen(cmd)) != 0)
150 			goto bad;
151 	case '?':
152 		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
153 			"Available commands are:\n",
154 			"\tls [arg] - list directory\n",
155 			"\tcd arg - change directory\n",
156 			"\tpwd - print current directory\n",
157 			"\tadd [arg] - add `arg' to list of",
158 			" files to be extracted\n",
159 			"\tdelete [arg] - delete `arg' from",
160 			" list of files to be extracted\n",
161 			"\textract - extract requested files\n",
162 			"\tsetmodes - set modes of requested directories\n",
163 			"\tquit - immediately exit program\n",
164 			"\twhat - list dump header information\n",
165 			"\tverbose - toggle verbose flag",
166 			" (useful with ``ls'')\n",
167 			"\thelp or `?' - print this list\n",
168 			"If no `arg' is supplied, the current",
169 			" directory is used\n");
170 		break;
171 	/*
172 	 * List a directory.
173 	 */
174 	case 'l':
175 		if (strncmp(cmd, "ls", strlen(cmd)) != 0)
176 			goto bad;
177 		ino = dirlookup(name);
178 		if (ino == 0)
179 			break;
180 		printlist(name, ino, curdir);
181 		break;
182 	/*
183 	 * Print current directory.
184 	 */
185 	case 'p':
186 		if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
187 			goto bad;
188 		if (curdir[1] == '\0')
189 			fprintf(stderr, "/\n");
190 		else
191 			fprintf(stderr, "%s\n", &curdir[1]);
192 		break;
193 	/*
194 	 * Quit.
195 	 */
196 	case 'q':
197 		if (strncmp(cmd, "quit", strlen(cmd)) != 0)
198 			goto bad;
199 		return;
200 	case 'x':
201 		if (strncmp(cmd, "xit", strlen(cmd)) != 0)
202 			goto bad;
203 		return;
204 	/*
205 	 * Toggle verbose mode.
206 	 */
207 	case 'v':
208 		if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
209 			goto bad;
210 		if (vflag) {
211 			fprintf(stderr, "verbose mode off\n");
212 			vflag = 0;
213 			break;
214 		}
215 		fprintf(stderr, "verbose mode on\n");
216 		vflag++;
217 		break;
218 	/*
219 	 * Just restore requested directory modes.
220 	 */
221 	case 's':
222 		if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
223 			goto bad;
224 		setdirmodes(FORCE);
225 		break;
226 	/*
227 	 * Print out dump header information.
228 	 */
229 	case 'w':
230 		if (strncmp(cmd, "what", strlen(cmd)) != 0)
231 			goto bad;
232 		printdumpinfo();
233 		break;
234 	/*
235 	 * Turn on debugging.
236 	 */
237 	case 'D':
238 		if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
239 			goto bad;
240 		if (dflag) {
241 			fprintf(stderr, "debugging mode off\n");
242 			dflag = 0;
243 			break;
244 		}
245 		fprintf(stderr, "debugging mode on\n");
246 		dflag++;
247 		break;
248 	/*
249 	 * Unknown command.
250 	 */
251 	default:
252 	bad:
253 		fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
254 		break;
255 	}
256 	goto loop;
257 }
258 
259 /*
260  * Read and parse an interactive command.
261  * The first word on the line is assigned to "cmd". If
262  * there are no arguments on the command line, then "curdir"
263  * is returned as the argument. If there are arguments
264  * on the line they are returned one at a time on each
265  * successive call to getcmd. Each argument is first assigned
266  * to "name". If it does not start with "/" the pathname in
267  * "curdir" is prepended to it. Finally "canon" is called to
268  * eliminate any embedded ".." components.
269  */
270 static void
271 getcmd(curdir, cmd, name, ap)
272 	char *curdir, *cmd, *name;
273 	struct arglist *ap;
274 {
275 	register char *cp;
276 	static char input[BUFSIZ];
277 	char output[BUFSIZ];
278 #	define rawname input	/* save space by reusing input buffer */
279 
280 	/*
281 	 * Check to see if still processing arguments.
282 	 */
283 	if (ap->head != ap->last) {
284 		strcpy(name, ap->head->fname);
285 		freename(ap->head->fname);
286 		ap->head++;
287 		return;
288 	}
289 	if (nextarg != NULL)
290 		goto getnext;
291 	/*
292 	 * Read a command line and trim off trailing white space.
293 	 */
294 	do	{
295 		fprintf(stderr, "restore > ");
296 		(void) fflush(stderr);
297 		(void) fgets(input, BUFSIZ, terminal);
298 	} while (!feof(terminal) && input[0] == '\n');
299 	if (feof(terminal)) {
300 		(void) strcpy(cmd, "quit");
301 		return;
302 	}
303 	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
304 		/* trim off trailing white space and newline */;
305 	*++cp = '\0';
306 	/*
307 	 * Copy the command into "cmd".
308 	 */
309 	cp = copynext(input, cmd);
310 	ap->cmd = cmd;
311 	/*
312 	 * If no argument, use curdir as the default.
313 	 */
314 	if (*cp == '\0') {
315 		(void) strcpy(name, curdir);
316 		return;
317 	}
318 	nextarg = cp;
319 	/*
320 	 * Find the next argument.
321 	 */
322 getnext:
323 	cp = copynext(nextarg, rawname);
324 	if (*cp == '\0')
325 		nextarg = NULL;
326 	else
327 		nextarg = cp;
328 	/*
329 	 * If it an absolute pathname, canonicalize it and return it.
330 	 */
331 	if (rawname[0] == '/') {
332 		canon(rawname, name);
333 	} else {
334 		/*
335 		 * For relative pathnames, prepend the current directory to
336 		 * it then canonicalize and return it.
337 		 */
338 		(void) strcpy(output, curdir);
339 		(void) strcat(output, "/");
340 		(void) strcat(output, rawname);
341 		canon(output, name);
342 	}
343 	expandarg(name, ap);
344 	strcpy(name, ap->head->fname);
345 	freename(ap->head->fname);
346 	ap->head++;
347 #	undef rawname
348 }
349 
350 /*
351  * Strip off the next token of the input.
352  */
353 static char *
354 copynext(input, output)
355 	char *input, *output;
356 {
357 	register char *cp, *bp;
358 	char quote;
359 
360 	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
361 		/* skip to argument */;
362 	bp = output;
363 	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
364 		/*
365 		 * Handle back slashes.
366 		 */
367 		if (*cp == '\\') {
368 			if (*++cp == '\0') {
369 				fprintf(stderr,
370 					"command lines cannot be continued\n");
371 				continue;
372 			}
373 			*bp++ = *cp++;
374 			continue;
375 		}
376 		/*
377 		 * The usual unquoted case.
378 		 */
379 		if (*cp != '\'' && *cp != '"') {
380 			*bp++ = *cp++;
381 			continue;
382 		}
383 		/*
384 		 * Handle single and double quotes.
385 		 */
386 		quote = *cp++;
387 		while (*cp != quote && *cp != '\0')
388 			*bp++ = *cp++ | 0200;
389 		if (*cp++ == '\0') {
390 			fprintf(stderr, "missing %c\n", quote);
391 			cp--;
392 			continue;
393 		}
394 	}
395 	*bp = '\0';
396 	return (cp);
397 }
398 
399 /*
400  * Canonicalize file names to always start with ``./'' and
401  * remove any imbedded "." and ".." components.
402  */
403 void
404 canon(rawname, canonname)
405 	char *rawname, *canonname;
406 {
407 	register char *cp, *np;
408 
409 	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
410 		(void) strcpy(canonname, "");
411 	else if (rawname[0] == '/')
412 		(void) strcpy(canonname, ".");
413 	else
414 		(void) strcpy(canonname, "./");
415 	(void) strcat(canonname, rawname);
416 	/*
417 	 * Eliminate multiple and trailing '/'s
418 	 */
419 	for (cp = np = canonname; *np != '\0'; cp++) {
420 		*cp = *np++;
421 		while (*cp == '/' && *np == '/')
422 			np++;
423 	}
424 	*cp = '\0';
425 	if (*--cp == '/')
426 		*cp = '\0';
427 	/*
428 	 * Eliminate extraneous "." and ".." from pathnames.
429 	 */
430 	for (np = canonname; *np != '\0'; ) {
431 		np++;
432 		cp = np;
433 		while (*np != '/' && *np != '\0')
434 			np++;
435 		if (np - cp == 1 && *cp == '.') {
436 			cp--;
437 			(void) strcpy(cp, np);
438 			np = cp;
439 		}
440 		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
441 			cp--;
442 			while (cp > &canonname[1] && *--cp != '/')
443 				/* find beginning of name */;
444 			(void) strcpy(cp, np);
445 			np = cp;
446 		}
447 	}
448 }
449 
450 /*
451  * globals (file name generation)
452  *
453  * "*" in params matches r.e ".*"
454  * "?" in params matches r.e. "."
455  * "[...]" in params matches character class
456  * "[...a-z...]" in params matches a through z.
457  */
458 static void
459 expandarg(arg, ap)
460 	char *arg;
461 	register struct arglist *ap;
462 {
463 	static struct afile single;
464 	struct entry *ep;
465 	int size;
466 
467 	ap->head = ap->last = (struct afile *)0;
468 	size = expand(arg, 0, ap);
469 	if (size == 0) {
470 		ep = lookupname(arg);
471 		single.fnum = ep ? ep->e_ino : 0;
472 		single.fname = savename(arg);
473 		ap->head = &single;
474 		ap->last = ap->head + 1;
475 		return;
476 	}
477 	qsort((char *)ap->head, ap->last - ap->head, sizeof *ap->head, fcmp);
478 }
479 
480 /*
481  * Expand a file name
482  */
483 static int
484 expand(as, rflg, ap)
485 	char *as;
486 	int rflg;
487 	register struct arglist *ap;
488 {
489 	int		count, size;
490 	char		dir = 0;
491 	char		*rescan = 0;
492 	RST_DIR		*dirp;
493 	register char	*s, *cs;
494 	int		sindex, rindex, lindex;
495 	struct direct	*dp;
496 	register char	slash;
497 	register char	*rs;
498 	register char	c;
499 
500 	/*
501 	 * check for meta chars
502 	 */
503 	s = cs = as;
504 	slash = 0;
505 	while (*cs != '*' && *cs != '?' && *cs != '[') {
506 		if (*cs++ == 0) {
507 			if (rflg && slash)
508 				break;
509 			else
510 				return (0) ;
511 		} else if (*cs == '/') {
512 			slash++;
513 		}
514 	}
515 	for (;;) {
516 		if (cs == s) {
517 			s = "";
518 			break;
519 		} else if (*--cs == '/') {
520 			*cs = 0;
521 			if (s == cs)
522 				s = "/";
523 			break;
524 		}
525 	}
526 	if ((dirp = rst_opendir(s)) != NULL)
527 		dir++;
528 	count = 0;
529 	if (*cs == 0)
530 		*cs++ = 0200;
531 	if (dir) {
532 		/*
533 		 * check for rescan
534 		 */
535 		rs = cs;
536 		do {
537 			if (*rs == '/') {
538 				rescan = rs;
539 				*rs = 0;
540 			}
541 		} while (*rs++);
542 		sindex = ap->last - ap->head;
543 		while ((dp = rst_readdir(dirp)) != NULL && dp->d_ino != 0) {
544 			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
545 				continue;
546 			if ((*dp->d_name == '.' && *cs != '.'))
547 				continue;
548 			if (gmatch(dp->d_name, cs)) {
549 				if (addg(dp, s, rescan, ap) < 0)
550 					return (-1);
551 				count++;
552 			}
553 		}
554 		if (rescan) {
555 			rindex = sindex;
556 			lindex = ap->last - ap->head;
557 			if (count) {
558 				count = 0;
559 				while (rindex < lindex) {
560 					size = expand(ap->head[rindex].fname,
561 					    1, ap);
562 					if (size < 0)
563 						return (size);
564 					count += size;
565 					rindex++;
566 				}
567 			}
568 			bcopy((char *)&ap->head[lindex],
569 			     (char *)&ap->head[sindex],
570 			     (ap->last - &ap->head[rindex]) * sizeof *ap->head);
571 			ap->last -= lindex - sindex;
572 			*rescan = '/';
573 		}
574 	}
575 	s = as;
576 	while (c = *s)
577 		*s++ = (c&0177 ? c : '/');
578 	return (count);
579 }
580 
581 /*
582  * Check for a name match
583  */
584 static int
585 gmatch(s, p)
586 	register char	*s, *p;
587 {
588 	register int	scc;
589 	char		c;
590 	char		ok;
591 	int		lc;
592 
593 	if (scc = *s++)
594 		if ((scc &= 0177) == 0)
595 			scc = 0200;
596 	switch (c = *p++) {
597 
598 	case '[':
599 		ok = 0;
600 		lc = 077777;
601 		while (c = *p++) {
602 			if (c == ']') {
603 				return (ok ? gmatch(s, p) : 0);
604 			} else if (c == '-') {
605 				if (lc <= scc && scc <= (*p++))
606 					ok++ ;
607 			} else {
608 				if (scc == (lc = (c&0177)))
609 					ok++ ;
610 			}
611 		}
612 		return (0);
613 
614 	default:
615 		if ((c&0177) != scc)
616 			return (0) ;
617 		/* falls through */
618 
619 	case '?':
620 		return (scc ? gmatch(s, p) : 0);
621 
622 	case '*':
623 		if (*p == 0)
624 			return (1) ;
625 		s--;
626 		while (*s) {
627 			if (gmatch(s++, p))
628 				return (1);
629 		}
630 		return (0);
631 
632 	case 0:
633 		return (scc == 0);
634 	}
635 }
636 
637 /*
638  * Construct a matched name.
639  */
640 static int
641 addg(dp, as1, as3, ap)
642 	struct direct	*dp;
643 	char		*as1, *as3;
644 	struct arglist	*ap;
645 {
646 	register char	*s1, *s2;
647 	register int	c;
648 	char		buf[BUFSIZ];
649 
650 	s2 = buf;
651 	s1 = as1;
652 	while (c = *s1++) {
653 		if ((c &= 0177) == 0) {
654 			*s2++ = '/';
655 			break;
656 		}
657 		*s2++ = c;
658 	}
659 	s1 = dp->d_name;
660 	while (*s2 = *s1++)
661 		s2++;
662 	if (s1 = as3) {
663 		*s2++ = '/';
664 		while (*s2++ = *++s1)
665 			/* void */;
666 	}
667 	if (mkentry(buf, dp, ap) == FAIL)
668 		return (-1);
669 	return (0);
670 }
671 
672 /*
673  * Do an "ls" style listing of a directory
674  */
675 static void
676 printlist(name, ino, basename)
677 	char *name;
678 	ino_t ino;
679 	char *basename;
680 {
681 	register struct afile *fp;
682 	register struct direct *dp;
683 	static struct arglist alist = { 0, 0, 0, 0, "ls" };
684 	struct afile single;
685 	RST_DIR *dirp;
686 
687 	if ((dirp = rst_opendir(name)) == NULL) {
688 		single.fnum = ino;
689 		single.finotype = DT_UNKNOWN;
690 		single.fname = savename(name + strlen(basename) + 1);
691 		alist.head = &single;
692 		alist.last = alist.head + 1;
693 	} else {
694 		alist.head = (struct afile *)0;
695 		fprintf(stderr, "%s:\n", name);
696 		while (dp = rst_readdir(dirp)) {
697 			if (dp == NULL || dp->d_ino == 0)
698 				break;
699 			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
700 				continue;
701 			if (vflag == 0 &&
702 			    (strcmp(dp->d_name, ".") == 0 ||
703 			     strcmp(dp->d_name, "..") == 0))
704 				continue;
705 			if (!mkentry(dp->d_name, dp, &alist))
706 				return;
707 		}
708 	}
709 	if (alist.head != 0) {
710 		qsort((char *)alist.head, alist.last - alist.head,
711 			sizeof *alist.head, fcmp);
712 		formatf(&alist);
713 		for (fp = alist.head; fp < alist.last; fp++)
714 			freename(fp->fname);
715 	}
716 	if (dirp != NULL)
717 		fprintf(stderr, "\n");
718 }
719 
720 /*
721  * Read the contents of a directory.
722  */
723 static int
724 mkentry(name, dp, ap)
725 	char *name;
726 	struct direct *dp;
727 	register struct arglist *ap;
728 {
729 	register struct afile *fp;
730 
731 	if (ap->base == NULL) {
732 		ap->nent = 20;
733 		ap->base = (struct afile *)calloc((unsigned)ap->nent,
734 			sizeof (struct afile));
735 		if (ap->base == NULL) {
736 			fprintf(stderr, "%s: out of memory\n", ap->cmd);
737 			return (FAIL);
738 		}
739 	}
740 	if (ap->head == 0)
741 		ap->head = ap->last = ap->base;
742 	fp = ap->last;
743 	fp->fnum = dp->d_ino;
744 	if (oldinofmt)
745 		fp->finotype = DT_UNKNOWN;
746 	else
747 		fp->finotype = dp->d_type;
748 	fp->fname = savename(name);
749 	fp++;
750 	if (fp == ap->head + ap->nent) {
751 		ap->base = (struct afile *)realloc((char *)ap->base,
752 		    (unsigned)(2 * ap->nent * sizeof (struct afile)));
753 		if (ap->base == 0) {
754 			fprintf(stderr, "%s: out of memory\n", ap->cmd);
755 			return (FAIL);
756 		}
757 		ap->head = ap->base;
758 		fp = ap->head + ap->nent;
759 		ap->nent *= 2;
760 	}
761 	ap->last = fp;
762 	return (GOOD);
763 }
764 
765 /*
766  * Print out a pretty listing of a directory
767  */
768 static void
769 formatf(ap)
770 	register struct arglist *ap;
771 {
772 	register struct afile *fp;
773 	struct entry *np;
774 	int width = 0, w, nentry = ap->last - ap->head;
775 	int i, j, len, columns, lines;
776 	char *cp;
777 
778 	if (ap->head == ap->last)
779 		return;
780 	for (fp = ap->head; fp < ap->last; fp++) {
781 		fp->ftype = inodetype(fp->fnum);
782 		np = lookupino(fp->fnum);
783 		if (np != NULL)
784 			fp->fflags = np->e_flags;
785 		else
786 			fp->fflags = 0;
787 		len = strlen(fmtentry(fp));
788 		if (len > width)
789 			width = len;
790 	}
791 	width += 2;
792 	columns = 80 / width;
793 	if (columns == 0)
794 		columns = 1;
795 	lines = (nentry + columns - 1) / columns;
796 	for (i = 0; i < lines; i++) {
797 		for (j = 0; j < columns; j++) {
798 			fp = ap->head + j * lines + i;
799 			cp = fmtentry(fp);
800 			fprintf(stderr, "%s", cp);
801 			if (fp + lines >= ap->last) {
802 				fprintf(stderr, "\n");
803 				break;
804 			}
805 			w = strlen(cp);
806 			while (w < width) {
807 				w++;
808 				fprintf(stderr, " ");
809 			}
810 		}
811 	}
812 }
813 
814 /*
815  * Comparison routine for qsort.
816  */
817 static int
818 fcmp(f1, f2)
819 	register const void *f1, *f2;
820 {
821 	return (strcmp(((struct afile *)f1)->fname,
822 	    ((struct afile *)f2)->fname));
823 }
824 
825 /*
826  * Format a directory entry.
827  */
828 static char *
829 fmtentry(fp)
830 	register struct afile *fp;
831 {
832 	static char fmtres[BUFSIZ];
833 	static int precision = 0;
834 	int i;
835 	register char *cp, *dp;
836 
837 	if (!vflag) {
838 		fmtres[0] = '\0';
839 	} else {
840 		if (precision == 0)
841 			for (i = maxino; i > 0; i /= 10)
842 				precision++;
843 		(void) sprintf(fmtres, "%*d ", precision, fp->fnum);
844 	}
845 	dp = &fmtres[strlen(fmtres)];
846 	if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
847 		*dp++ = '^';
848 	else if ((fp->fflags & NEW) != 0)
849 		*dp++ = '*';
850 	else
851 		*dp++ = ' ';
852 	for (cp = fp->fname; *cp; cp++)
853 		if (!vflag && (*cp < ' ' || *cp >= 0177))
854 			*dp++ = '?';
855 		else
856 			*dp++ = *cp;
857 	switch(fp->finotype) {
858 
859 	case DT_LNK:
860 		*dp++ = '@';
861 		break;
862 
863 	case DT_FIFO:
864 	case DT_SOCK:
865 		*dp++ = '=';
866 		break;
867 
868 	case DT_CHR:
869 	case DT_BLK:
870 		*dp++ = '#';
871 		break;
872 
873 	case DT_UNKNOWN:
874 	case DT_DIR:
875 		if (fp->ftype == NODE)
876 			*dp++ = '/';
877 		break;
878 
879 	case DT_REG:
880 		/* nothing */
881 		break;
882 
883 	default:
884 		fprintf(stderr, "Warning: undefined file type %d\n",
885 		    fp->finotype);
886 	}
887 	*dp++ = 0;
888 	return (fmtres);
889 }
890 
891 /*
892  * respond to interrupts
893  */
894 void
895 onintr(signo)
896 	int signo;
897 {
898 	if (command == 'i' && runshell)
899 		longjmp(reset, 1);
900 	if (reply("restore interrupted, continue") == FAIL)
901 		done(1);
902 }
903