interactive.c revision 41845
178064Sume/*
278064Sume * Copyright (c) 1985, 1993
355163Sshin *	The Regents of the University of California.  All rights reserved.
455163Sshin *
555163Sshin * Redistribution and use in source and binary forms, with or without
662752Skris * modification, are permitted provided that the following conditions
755163Sshin * are met:
855163Sshin * 1. Redistributions of source code must retain the above copyright
955163Sshin *    notice, this list of conditions and the following disclaimer.
1055163Sshin * 2. Redistributions in binary form must reproduce the above copyright
1155163Sshin *    notice, this list of conditions and the following disclaimer in the
1255163Sshin *    documentation and/or other materials provided with the distribution.
1355163Sshin * 3. All advertising materials mentioning features or use of this software
1455163Sshin *    must display the following acknowledgement:
1555163Sshin *	This product includes software developed by the University of
1655163Sshin *	California, Berkeley and its contributors.
1755163Sshin * 4. Neither the name of the University nor the names of its contributors
1862752Skris *    may be used to endorse or promote products derived from this software
1955163Sshin *    without specific prior written permission.
2055163Sshin *
2155163Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2255163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455163Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2555163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155163Sshin * SUCH DAMAGE.
3255163Sshin */
3355163Sshin
3455163Sshin#ifndef lint
3555163Sshin#if 0
3655163Sshinstatic char sccsid[] = "@(#)interactive.c	8.5 (Berkeley) 5/1/95";
3755163Sshin#endif
3855163Sshinstatic const char rcsid[] =
3955163Sshin	"$Id: interactive.c,v 1.6 1998/09/17 20:18:11 imp Exp $";
4055163Sshin#endif /* not lint */
4162752Skris
4255163Sshin#include <sys/param.h>
4355163Sshin#include <sys/stat.h>
4455163Sshin
4555163Sshin#include <ufs/ufs/dinode.h>
4678064Sume#include <ufs/ufs/dir.h>
4778064Sume#include <protocols/dumprestore.h>
4855163Sshin
4962752Skris#include <setjmp.h>
5055163Sshin#include <glob.h>
5162752Skris#include <stdio.h>
5255163Sshin#include <stdlib.h>
5355163Sshin#include <string.h>
5455163Sshin
5555163Sshin#include "restore.h"
5655163Sshin#include "extern.h"
5755163Sshin
5855163Sshin#define round(a, b) (((a) + (b) - 1) / (b) * (b))
5955163Sshin
6055163Sshin/*
6162752Skris * Things to handle interruptions.
6255163Sshin */
6355163Sshinstatic int runshell;
6455163Sshinstatic jmp_buf reset;
6555163Sshinstatic char *nextarg = NULL;
6655163Sshin
6755163Sshin/*
6855163Sshin * Structure and routines associated with listing directories.
6955163Sshin */
7055163Sshinstruct afile {
7155163Sshin	ino_t	fnum;		/* inode number of file */
7255163Sshin	char	*fname;		/* file name */
7355163Sshin	short	len;		/* name length */
7455163Sshin	char	prefix;		/* prefix character */
7555163Sshin	char	postfix;	/* postfix character */
7655163Sshin};
7755163Sshinstruct arglist {
7855163Sshin	int	freeglob;	/* glob structure needs to be freed */
7955163Sshin	int	argcnt;		/* next globbed argument to return */
8055163Sshin	glob_t	glob;		/* globbing information */
8155163Sshin	char	*cmd;		/* the current command */
8255163Sshin};
8355163Sshin
8455163Sshinstatic char	*copynext __P((char *, char *));
8555163Sshinstatic int	 fcmp __P((const void *, const void *));
8655163Sshinstatic void	 formatf __P((struct afile *, int));
8755163Sshinstatic void	 getcmd __P((char *, char *, char *, int, struct arglist *));
8855163Sshinstruct dirent	*glob_readdir __P((RST_DIR *dirp));
8978064Sumestatic int	 glob_stat __P((const char *, struct stat *));
9055163Sshinstatic void	 mkentry __P((char *, struct direct *, struct afile *));
9155163Sshinstatic void	 printlist __P((char *, char *));
9255163Sshin
9355163Sshin/*
9455163Sshin * Read and execute commands from the terminal.
9555163Sshin */
9655163Sshinvoid
9755163Sshinruncmdshell()
9855163Sshin{
9955163Sshin	register struct entry *np;
10055163Sshin	ino_t ino;
10155163Sshin	struct arglist arglist;
10255163Sshin	char curdir[MAXPATHLEN];
10355163Sshin	char name[MAXPATHLEN];
10455163Sshin	char cmd[BUFSIZ];
10555163Sshin
10655163Sshin	arglist.freeglob = 0;
10755163Sshin	arglist.argcnt = 0;
10855163Sshin	arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
10978064Sume	arglist.glob.gl_opendir = (void *)rst_opendir;
11055163Sshin	arglist.glob.gl_readdir = (void *)glob_readdir;
11155163Sshin	arglist.glob.gl_closedir = (void *)rst_closedir;
11255163Sshin	arglist.glob.gl_lstat = glob_stat;
11355163Sshin	arglist.glob.gl_stat = glob_stat;
11455163Sshin	canon("/", curdir, sizeof(curdir));
11555163Sshinloop:
11655163Sshin	if (setjmp(reset) != 0) {
11755163Sshin		if (arglist.freeglob != 0) {
11855163Sshin			arglist.freeglob = 0;
11955163Sshin			arglist.argcnt = 0;
12055163Sshin			globfree(&arglist.glob);
12162752Skris		}
12255163Sshin		nextarg = NULL;
12355163Sshin		volno = 0;
12462752Skris	}
12555163Sshin	runshell = 1;
12655163Sshin	getcmd(curdir, cmd, name, sizeof(name), &arglist);
12755163Sshin	switch (cmd[0]) {
12855163Sshin	/*
12955163Sshin	 * Add elements to the extraction list.
13055163Sshin	 */
13155163Sshin	case 'a':
13255163Sshin		if (strncmp(cmd, "add", strlen(cmd)) != 0)
13355163Sshin			goto bad;
13455163Sshin		ino = dirlookup(name);
13555163Sshin		if (ino == 0)
13655163Sshin			break;
13755163Sshin		if (mflag)
13855163Sshin			pathcheck(name);
13955163Sshin		treescan(name, ino, addfile);
14055163Sshin		break;
14155163Sshin	/*
14255163Sshin	 * Change working directory.
14355163Sshin	 */
14455163Sshin	case 'c':
14555163Sshin		if (strncmp(cmd, "cd", strlen(cmd)) != 0)
14655163Sshin			goto bad;
14755163Sshin		ino = dirlookup(name);
14855163Sshin		if (ino == 0)
14955163Sshin			break;
15055163Sshin		if (inodetype(ino) == LEAF) {
15155163Sshin			fprintf(stderr, "%s: not a directory\n", name);
15255163Sshin			break;
15355163Sshin		}
15455163Sshin		(void) strcpy(curdir, name);
15555163Sshin		break;
15655163Sshin	/*
15755163Sshin	 * Delete elements from the extraction list.
15855163Sshin	 */
15955163Sshin	case 'd':
16055163Sshin		if (strncmp(cmd, "delete", strlen(cmd)) != 0)
16155163Sshin			goto bad;
16255163Sshin		np = lookupname(name);
16355163Sshin		if (np == NULL || (np->e_flags & NEW) == 0) {
16455163Sshin			fprintf(stderr, "%s: not on extraction list\n", name);
16555163Sshin			break;
16655163Sshin		}
16755163Sshin		treescan(name, np->e_ino, deletefile);
16855163Sshin		break;
16955163Sshin	/*
17055163Sshin	 * Extract the requested list.
17155163Sshin	 */
17255163Sshin	case 'e':
17355163Sshin		if (strncmp(cmd, "extract", strlen(cmd)) != 0)
17455163Sshin			goto bad;
17555163Sshin		createfiles();
17655163Sshin		createlinks();
17755163Sshin		setdirmodes(0);
17855163Sshin		if (dflag)
17955163Sshin			checkrestore();
18055163Sshin		volno = 0;
18155163Sshin		break;
18255163Sshin	/*
18355163Sshin	 * List available commands.
18495258Sdes	 */
18555163Sshin	case 'h':
18655163Sshin		if (strncmp(cmd, "help", strlen(cmd)) != 0)
18755163Sshin			goto bad;
18855163Sshin	case '?':
18955163Sshin		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
19055163Sshin			"Available commands are:\n",
19155163Sshin			"\tls [arg] - list directory\n",
19262752Skris			"\tcd arg - change directory\n",
19355163Sshin			"\tpwd - print current directory\n",
19455163Sshin			"\tadd [arg] - add `arg' to list of",
19555163Sshin			" files to be extracted\n",
19655163Sshin			"\tdelete [arg] - delete `arg' from",
19755163Sshin			" list of files to be extracted\n",
19855163Sshin			"\textract - extract requested files\n",
19955163Sshin			"\tsetmodes - set modes of requested directories\n",
20055163Sshin			"\tquit - immediately exit program\n",
20155163Sshin			"\twhat - list dump header information\n",
20255163Sshin			"\tverbose - toggle verbose flag",
20355163Sshin			" (useful with ``ls'')\n",
20455163Sshin			"\thelp or `?' - print this list\n",
20562752Skris			"If no `arg' is supplied, the current",
20655163Sshin			" directory is used\n");
20755163Sshin		break;
20855163Sshin	/*
209	 * List a directory.
210	 */
211	case 'l':
212		if (strncmp(cmd, "ls", strlen(cmd)) != 0)
213			goto bad;
214		printlist(name, curdir);
215		break;
216	/*
217	 * Print current directory.
218	 */
219	case 'p':
220		if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
221			goto bad;
222		if (curdir[1] == '\0')
223			fprintf(stderr, "/\n");
224		else
225			fprintf(stderr, "%s\n", &curdir[1]);
226		break;
227	/*
228	 * Quit.
229	 */
230	case 'q':
231		if (strncmp(cmd, "quit", strlen(cmd)) != 0)
232			goto bad;
233		return;
234	case 'x':
235		if (strncmp(cmd, "xit", strlen(cmd)) != 0)
236			goto bad;
237		return;
238	/*
239	 * Toggle verbose mode.
240	 */
241	case 'v':
242		if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
243			goto bad;
244		if (vflag) {
245			fprintf(stderr, "verbose mode off\n");
246			vflag = 0;
247			break;
248		}
249		fprintf(stderr, "verbose mode on\n");
250		vflag++;
251		break;
252	/*
253	 * Just restore requested directory modes.
254	 */
255	case 's':
256		if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
257			goto bad;
258		setdirmodes(FORCE);
259		break;
260	/*
261	 * Print out dump header information.
262	 */
263	case 'w':
264		if (strncmp(cmd, "what", strlen(cmd)) != 0)
265			goto bad;
266		printdumpinfo();
267		break;
268	/*
269	 * Turn on debugging.
270	 */
271	case 'D':
272		if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
273			goto bad;
274		if (dflag) {
275			fprintf(stderr, "debugging mode off\n");
276			dflag = 0;
277			break;
278		}
279		fprintf(stderr, "debugging mode on\n");
280		dflag++;
281		break;
282	/*
283	 * Unknown command.
284	 */
285	default:
286	bad:
287		fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
288		break;
289	}
290	goto loop;
291}
292
293/*
294 * Read and parse an interactive command.
295 * The first word on the line is assigned to "cmd". If
296 * there are no arguments on the command line, then "curdir"
297 * is returned as the argument. If there are arguments
298 * on the line they are returned one at a time on each
299 * successive call to getcmd. Each argument is first assigned
300 * to "name". If it does not start with "/" the pathname in
301 * "curdir" is prepended to it. Finally "canon" is called to
302 * eliminate any embedded ".." components.
303 */
304static void
305getcmd(curdir, cmd, name, size, ap)
306	char *curdir, *cmd, *name;
307	struct arglist *ap;
308	int size;
309{
310	register char *cp;
311	static char input[BUFSIZ];
312	char output[BUFSIZ];
313#	define rawname input	/* save space by reusing input buffer */
314
315	/*
316	 * Check to see if still processing arguments.
317	 */
318	if (ap->argcnt > 0)
319		goto retnext;
320	if (nextarg != NULL)
321		goto getnext;
322	/*
323	 * Read a command line and trim off trailing white space.
324	 */
325	do	{
326		fprintf(stderr, "restore > ");
327		(void) fflush(stderr);
328		(void) fgets(input, BUFSIZ, terminal);
329	} while (!feof(terminal) && input[0] == '\n');
330	if (feof(terminal)) {
331		(void) strcpy(cmd, "quit");
332		return;
333	}
334	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
335		/* trim off trailing white space and newline */;
336	*++cp = '\0';
337	/*
338	 * Copy the command into "cmd".
339	 */
340	cp = copynext(input, cmd);
341	ap->cmd = cmd;
342	/*
343	 * If no argument, use curdir as the default.
344	 */
345	if (*cp == '\0') {
346		(void) strncpy(name, curdir, size);
347		name[size - 1] = '\0';
348		return;
349	}
350	nextarg = cp;
351	/*
352	 * Find the next argument.
353	 */
354getnext:
355	cp = copynext(nextarg, rawname);
356	if (*cp == '\0')
357		nextarg = NULL;
358	else
359		nextarg = cp;
360	/*
361	 * If it is an absolute pathname, canonicalize it and return it.
362	 */
363	if (rawname[0] == '/') {
364		canon(rawname, name, size);
365	} else {
366		/*
367		 * For relative pathnames, prepend the current directory to
368		 * it then canonicalize and return it.
369		 */
370		snprintf(output, sizeof(output), "%s/%s", curdir, rawname);
371		canon(output, name, size);
372	}
373	if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0)
374		fprintf(stderr, "%s: out of memory\n", ap->cmd);
375	if (ap->glob.gl_pathc == 0)
376		return;
377	ap->freeglob = 1;
378	ap->argcnt = ap->glob.gl_pathc;
379
380retnext:
381	strncpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size);
382	name[size - 1] = '\0';
383	if (--ap->argcnt == 0) {
384		ap->freeglob = 0;
385		globfree(&ap->glob);
386	}
387#	undef rawname
388}
389
390/*
391 * Strip off the next token of the input.
392 */
393static char *
394copynext(input, output)
395	char *input, *output;
396{
397	register char *cp, *bp;
398	char quote;
399
400	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
401		/* skip to argument */;
402	bp = output;
403	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
404		/*
405		 * Handle back slashes.
406		 */
407		if (*cp == '\\') {
408			if (*++cp == '\0') {
409				fprintf(stderr,
410					"command lines cannot be continued\n");
411				continue;
412			}
413			*bp++ = *cp++;
414			continue;
415		}
416		/*
417		 * The usual unquoted case.
418		 */
419		if (*cp != '\'' && *cp != '"') {
420			*bp++ = *cp++;
421			continue;
422		}
423		/*
424		 * Handle single and double quotes.
425		 */
426		quote = *cp++;
427		while (*cp != quote && *cp != '\0')
428			*bp++ = *cp++ | 0200;
429		if (*cp++ == '\0') {
430			fprintf(stderr, "missing %c\n", quote);
431			cp--;
432			continue;
433		}
434	}
435	*bp = '\0';
436	return (cp);
437}
438
439/*
440 * Canonicalize file names to always start with ``./'' and
441 * remove any embedded "." and ".." components.
442 */
443void
444canon(rawname, canonname, len)
445	char *rawname, *canonname;
446	int len;
447{
448	register char *cp, *np;
449
450	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
451		(void) strcpy(canonname, "");
452	else if (rawname[0] == '/')
453		(void) strcpy(canonname, ".");
454	else
455		(void) strcpy(canonname, "./");
456	if (strlen(canonname) + strlen(rawname) >= len) {
457		fprintf(stderr, "canonname: not enough buffer space\n");
458		done(1);
459	}
460
461	(void) strcat(canonname, rawname);
462	/*
463	 * Eliminate multiple and trailing '/'s
464	 */
465	for (cp = np = canonname; *np != '\0'; cp++) {
466		*cp = *np++;
467		while (*cp == '/' && *np == '/')
468			np++;
469	}
470	*cp = '\0';
471	if (*--cp == '/')
472		*cp = '\0';
473	/*
474	 * Eliminate extraneous "." and ".." from pathnames.
475	 */
476	for (np = canonname; *np != '\0'; ) {
477		np++;
478		cp = np;
479		while (*np != '/' && *np != '\0')
480			np++;
481		if (np - cp == 1 && *cp == '.') {
482			cp--;
483			(void) strcpy(cp, np);
484			np = cp;
485		}
486		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
487			cp--;
488			while (cp > &canonname[1] && *--cp != '/')
489				/* find beginning of name */;
490			(void) strcpy(cp, np);
491			np = cp;
492		}
493	}
494}
495
496/*
497 * Do an "ls" style listing of a directory
498 */
499static void
500printlist(name, basename)
501	char *name;
502	char *basename;
503{
504	register struct afile *fp, *list, *listp;
505	register struct direct *dp;
506	struct afile single;
507	RST_DIR *dirp;
508	int entries, len, namelen;
509	char locname[MAXPATHLEN + 1];
510
511	dp = pathsearch(name);
512	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
513	    (!vflag && dp->d_ino == WINO))
514		return;
515	if ((dirp = rst_opendir(name)) == NULL) {
516		entries = 1;
517		list = &single;
518		mkentry(name, dp, list);
519		len = strlen(basename) + 1;
520		if (strlen(name) - len > single.len) {
521			freename(single.fname);
522			single.fname = savename(&name[len]);
523			single.len = strlen(single.fname);
524		}
525	} else {
526		entries = 0;
527		while ((dp = rst_readdir(dirp)))
528			entries++;
529		rst_closedir(dirp);
530		list = (struct afile *)malloc(entries * sizeof(struct afile));
531		if (list == NULL) {
532			fprintf(stderr, "ls: out of memory\n");
533			return;
534		}
535		if ((dirp = rst_opendir(name)) == NULL)
536			panic("directory reopen failed\n");
537		fprintf(stderr, "%s:\n", name);
538		entries = 0;
539		listp = list;
540		(void) strncpy(locname, name, MAXPATHLEN);
541		(void) strncat(locname, "/", MAXPATHLEN);
542		namelen = strlen(locname);
543		while ((dp = rst_readdir(dirp))) {
544			if (dp == NULL)
545				break;
546			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
547				continue;
548			if (!vflag && (dp->d_ino == WINO ||
549			     strcmp(dp->d_name, ".") == 0 ||
550			     strcmp(dp->d_name, "..") == 0))
551				continue;
552			locname[namelen] = '\0';
553			if (namelen + dp->d_namlen >= MAXPATHLEN) {
554				fprintf(stderr, "%s%s: name exceeds %d char\n",
555					locname, dp->d_name, MAXPATHLEN);
556			} else {
557				(void) strncat(locname, dp->d_name,
558				    (int)dp->d_namlen);
559				mkentry(locname, dp, listp++);
560				entries++;
561			}
562		}
563		rst_closedir(dirp);
564		if (entries == 0) {
565			fprintf(stderr, "\n");
566			free(list);
567			return;
568		}
569		qsort((char *)list, entries, sizeof(struct afile), fcmp);
570	}
571	formatf(list, entries);
572	if (dirp != NULL) {
573		for (fp = listp - 1; fp >= list; fp--)
574			freename(fp->fname);
575		fprintf(stderr, "\n");
576		free(list);
577	}
578}
579
580/*
581 * Read the contents of a directory.
582 */
583static void
584mkentry(name, dp, fp)
585	char *name;
586	struct direct *dp;
587	register struct afile *fp;
588{
589	char *cp;
590	struct entry *np;
591
592	fp->fnum = dp->d_ino;
593	fp->fname = savename(dp->d_name);
594	for (cp = fp->fname; *cp; cp++)
595		if (!vflag && (*cp < ' ' || *cp >= 0177))
596			*cp = '?';
597	fp->len = cp - fp->fname;
598	if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
599		fp->prefix = '^';
600	else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
601		fp->prefix = '*';
602	else
603		fp->prefix = ' ';
604	switch(dp->d_type) {
605
606	default:
607		fprintf(stderr, "Warning: undefined file type %d\n",
608		    dp->d_type);
609		/* fall through */
610	case DT_REG:
611		fp->postfix = ' ';
612		break;
613
614	case DT_LNK:
615		fp->postfix = '@';
616		break;
617
618	case DT_FIFO:
619	case DT_SOCK:
620		fp->postfix = '=';
621		break;
622
623	case DT_CHR:
624	case DT_BLK:
625		fp->postfix = '#';
626		break;
627
628	case DT_WHT:
629		fp->postfix = '%';
630		break;
631
632	case DT_UNKNOWN:
633	case DT_DIR:
634		if (inodetype(dp->d_ino) == NODE)
635			fp->postfix = '/';
636		else
637			fp->postfix = ' ';
638		break;
639	}
640	return;
641}
642
643/*
644 * Print out a pretty listing of a directory
645 */
646static void
647formatf(list, nentry)
648	register struct afile *list;
649	int nentry;
650{
651	register struct afile *fp, *endlist;
652	int width, bigino, haveprefix, havepostfix;
653	int i, j, w, precision, columns, lines;
654
655	width = 0;
656	haveprefix = 0;
657	havepostfix = 0;
658	bigino = ROOTINO;
659	endlist = &list[nentry];
660	for (fp = &list[0]; fp < endlist; fp++) {
661		if (bigino < fp->fnum)
662			bigino = fp->fnum;
663		if (width < fp->len)
664			width = fp->len;
665		if (fp->prefix != ' ')
666			haveprefix = 1;
667		if (fp->postfix != ' ')
668			havepostfix = 1;
669	}
670	if (haveprefix)
671		width++;
672	if (havepostfix)
673		width++;
674	if (vflag) {
675		for (precision = 0, i = bigino; i > 0; i /= 10)
676			precision++;
677		width += precision + 1;
678	}
679	width++;
680	columns = 81 / width;
681	if (columns == 0)
682		columns = 1;
683	lines = (nentry + columns - 1) / columns;
684	for (i = 0; i < lines; i++) {
685		for (j = 0; j < columns; j++) {
686			fp = &list[j * lines + i];
687			if (vflag) {
688				fprintf(stderr, "%*d ", precision, fp->fnum);
689				fp->len += precision + 1;
690			}
691			if (haveprefix) {
692				putc(fp->prefix, stderr);
693				fp->len++;
694			}
695			fprintf(stderr, "%s", fp->fname);
696			if (havepostfix) {
697				putc(fp->postfix, stderr);
698				fp->len++;
699			}
700			if (fp + lines >= endlist) {
701				fprintf(stderr, "\n");
702				break;
703			}
704			for (w = fp->len; w < width; w++)
705				putc(' ', stderr);
706		}
707	}
708}
709
710/*
711 * Skip over directory entries that are not on the tape
712 *
713 * First have to get definition of a dirent.
714 */
715#undef DIRBLKSIZ
716#include <dirent.h>
717#undef d_ino
718
719struct dirent *
720glob_readdir(dirp)
721	RST_DIR *dirp;
722{
723	struct direct *dp;
724	static struct dirent adirent;
725
726	while ((dp = rst_readdir(dirp)) != NULL) {
727		if (!vflag && dp->d_ino == WINO)
728			continue;
729		if (dflag || TSTINO(dp->d_ino, dumpmap))
730			break;
731	}
732	if (dp == NULL)
733		return (NULL);
734	adirent.d_fileno = dp->d_ino;
735	adirent.d_namlen = dp->d_namlen;
736	memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
737	return (&adirent);
738}
739
740/*
741 * Return st_mode information in response to stat or lstat calls
742 */
743static int
744glob_stat(name, stp)
745	const char *name;
746	struct stat *stp;
747{
748	register struct direct *dp;
749
750	dp = pathsearch(name);
751	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
752	    (!vflag && dp->d_ino == WINO))
753		return (-1);
754	if (inodetype(dp->d_ino) == NODE)
755		stp->st_mode = IFDIR;
756	else
757		stp->st_mode = IFREG;
758	return (0);
759}
760
761/*
762 * Comparison routine for qsort.
763 */
764static int
765fcmp(f1, f2)
766	register const void *f1, *f2;
767{
768	return (strcmp(((struct afile *)f1)->fname,
769	    ((struct afile *)f2)->fname));
770}
771
772/*
773 * respond to interrupts
774 */
775void
776onintr(signo)
777	int signo;
778{
779	if (command == 'i' && runshell)
780		longjmp(reset, 1);
781	if (reply("restore interrupted, continue") == FAIL)
782		done(1);
783}
784