Home | History | Annotate | Line # | Download | only in restore
      1 /*	$NetBSD: interactive.c,v 1.31 2021/08/29 09:17:58 christos 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.31 2021/08/29 09:17:58 christos 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 <ctype.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 	size_t	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 *, size_t, 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 (ino == UFS_ROOTINO)
    140 			dotflag = 1;
    141 		if (mflag)
    142 			pathcheck(name);
    143 		treescan(name, ino, addfile);
    144 		break;
    145 	/*
    146 	 * Change working directory.
    147 	 */
    148 	case 'c':
    149 		if (strncmp(cmd, "cd", strlen(cmd)) != 0)
    150 			goto bad;
    151 		ino = dirlookup(name);
    152 		if (ino == 0)
    153 			break;
    154 		if (inodetype(ino) == LEAF) {
    155 			fprintf(stderr, "%s: not a directory\n", name);
    156 			break;
    157 		}
    158 		(void) strcpy(curdir, name);
    159 		break;
    160 	/*
    161 	 * Delete elements from the extraction list.
    162 	 */
    163 	case 'd':
    164 		if (strncmp(cmd, "delete", strlen(cmd)) != 0)
    165 			goto bad;
    166 		np = lookupname(name);
    167 		if (np == NULL || (np->e_flags & NEW) == 0) {
    168 			fprintf(stderr, "%s: not on extraction list\n", name);
    169 			break;
    170 		}
    171 		treescan(name, np->e_ino, deletefile);
    172 		break;
    173 	/*
    174 	 * Extract the requested list.
    175 	 */
    176 	case 'e':
    177 		if (strncmp(cmd, "extract", strlen(cmd)) != 0)
    178 			goto bad;
    179 		createfiles();
    180 		createlinks();
    181 		setdirmodes(0);
    182 		if (dflag)
    183 			checkrestore();
    184 		volno = 0;
    185 		break;
    186 	/*
    187 	 * List available commands.
    188 	 */
    189 	case 'h':
    190 		if (strncmp(cmd, "help", strlen(cmd)) != 0)
    191 			goto bad;
    192 		/* FALLTHROUGH */
    193 	case '?':
    194 		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
    195 			"Available commands are:\n",
    196 			"\tls [arg] - list directory\n",
    197 			"\tcd arg - change directory\n",
    198 			"\tpwd - print current directory\n",
    199 			"\tadd [arg] - add `arg' to list of",
    200 			" files to be extracted\n",
    201 			"\tdelete [arg] - delete `arg' from",
    202 			" list of files to be extracted\n",
    203 			"\textract - extract requested files\n",
    204 			"\tsetmodes - set modes of requested directories\n",
    205 			"\tquit or xit - immediately exit program\n",
    206 			"\twhat - list dump header information\n",
    207 			"\tverbose - toggle verbose flag",
    208 			" (useful with ``ls'')\n",
    209 			"\thelp or `?' - print this list\n",
    210 			"\tDebug - turn on debugging\n",
    211 			"If no `arg' is supplied, the current",
    212 			" directory is used\n");
    213 		break;
    214 	/*
    215 	 * List a directory.
    216 	 */
    217 	case 'l':
    218 		if (strncmp(cmd, "ls", strlen(cmd)) != 0)
    219 			goto bad;
    220 		printlist(name, curdir);
    221 		break;
    222 	/*
    223 	 * Print current directory.
    224 	 */
    225 	case 'p':
    226 		if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
    227 			goto bad;
    228 		if (curdir[1] == '\0')
    229 			fprintf(stderr, "/\n");
    230 		else
    231 			fprintf(stderr, "%s\n", &curdir[1]);
    232 		break;
    233 	/*
    234 	 * Quit.
    235 	 */
    236 	case 'q':
    237 		if (strncmp(cmd, "quit", strlen(cmd)) != 0)
    238 			goto bad;
    239 		return;
    240 	case 'x':
    241 		if (strncmp(cmd, "xit", strlen(cmd)) != 0)
    242 			goto bad;
    243 		return;
    244 	/*
    245 	 * Toggle verbose mode.
    246 	 */
    247 	case 'v':
    248 		if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
    249 			goto bad;
    250 		if (vflag) {
    251 			fprintf(stderr, "verbose mode off\n");
    252 			vflag = 0;
    253 			break;
    254 		}
    255 		fprintf(stderr, "verbose mode on\n");
    256 		vflag++;
    257 		break;
    258 	/*
    259 	 * Just restore requested directory modes.
    260 	 */
    261 	case 's':
    262 		if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
    263 			goto bad;
    264 		setdirmodes(FORCE);
    265 		break;
    266 	/*
    267 	 * Print out dump header information.
    268 	 */
    269 	case 'w':
    270 		if (strncmp(cmd, "what", strlen(cmd)) != 0)
    271 			goto bad;
    272 		printdumpinfo();
    273 		break;
    274 	/*
    275 	 * Turn on debugging.
    276 	 */
    277 	case 'D':
    278 		if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
    279 			goto bad;
    280 		if (dflag) {
    281 			fprintf(stderr, "debugging mode off\n");
    282 			dflag = 0;
    283 			break;
    284 		}
    285 		fprintf(stderr, "debugging mode on\n");
    286 		dflag++;
    287 		break;
    288 	/*
    289 	 * Unknown command.
    290 	 */
    291 	default:
    292 	bad:
    293 		fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
    294 		break;
    295 	}
    296 	goto loop;
    297 }
    298 
    299 /*
    300  * Read and parse an interactive command.
    301  * The first word on the line is assigned to "cmd". If
    302  * there are no arguments on the command line, then "curdir"
    303  * is returned as the argument. If there are arguments
    304  * on the line they are returned one at a time on each
    305  * successive call to getcmd. Each argument is first assigned
    306  * to "name". If it does not start with "/" the pathname in
    307  * "curdir" is prepended to it. Finally "canon" is called to
    308  * eliminate any embedded ".." components.
    309  */
    310 static void
    311 getcmd(char *curdir, char *cmd, char *name, size_t size, struct arglist *ap)
    312 {
    313 	char *cp;
    314 	static char input[BUFSIZ];
    315 	char output[BUFSIZ * 2];
    316 	int globretval;
    317 #	define rawname input	/* save space by reusing input buffer */
    318 
    319 	/*
    320 	 * Check to see if still processing arguments.
    321 	 */
    322 	if (ap->argcnt > 0)
    323 		goto retnext;
    324 	if (nextarg != NULL)
    325 		goto getnext;
    326 	/*
    327 	 * Read a command line and trim off trailing white space.
    328 	 */
    329 	do	{
    330 		fprintf(stderr, "%s > ", getprogname());
    331 		(void) fflush(stderr);
    332 		(void) fgets(input, BUFSIZ, terminal);
    333 	} while (!feof(terminal) && input[0] == '\n');
    334 	if (feof(terminal)) {
    335 		(void) strlcpy(cmd, "quit", size);
    336 		return;
    337 	}
    338 	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
    339 		/* trim off trailing white space and newline */;
    340 	*++cp = '\0';
    341 	/*
    342 	 * Copy the command into "cmd".
    343 	 */
    344 	cp = copynext(input, cmd);
    345 	ap->cmd = cmd;
    346 	/*
    347 	 * If no argument, use curdir as the default.
    348 	 */
    349 	if (*cp == '\0') {
    350 		(void) strlcpy(name, curdir, size);
    351 		return;
    352 	}
    353 	nextarg = cp;
    354 	/*
    355 	 * Find the next argument.
    356 	 */
    357 getnext:
    358 	cp = copynext(nextarg, rawname);
    359 	if (*cp == '\0')
    360 		nextarg = NULL;
    361 	else
    362 		nextarg = cp;
    363 	/*
    364 	 * If it is an absolute pathname, canonicalize it and return it.
    365 	 */
    366 	if (rawname[0] == '/') {
    367 		canon(rawname, name, size);
    368 	} else {
    369 		/*
    370 		 * For relative pathnames, prepend the current directory to
    371 		 * it then canonicalize and return it.
    372 		 */
    373 		snprintf(output, sizeof(output), "%s/%s", curdir, rawname);
    374 		canon(output, name, size);
    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 	strlcpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size);
    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(char *input, char *output)
    412 {
    413 	char *cp, *bp;
    414 	char quote;
    415 
    416 	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
    417 		/* skip to argument */;
    418 	bp = output;
    419 	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
    420 		/*
    421 		 * Handle back slashes.
    422 		 */
    423 		if (*cp == '\\') {
    424 			if (*++cp == '\0') {
    425 				fprintf(stderr,
    426 					"command lines cannot be continued\n");
    427 				continue;
    428 			}
    429 			*bp++ = *cp++;
    430 			continue;
    431 		}
    432 		/*
    433 		 * The usual unquoted case.
    434 		 */
    435 		if (*cp != '\'' && *cp != '"') {
    436 			*bp++ = *cp++;
    437 			continue;
    438 		}
    439 		/*
    440 		 * Handle single and double quotes.
    441 		 */
    442 		quote = *cp++;
    443 		while (*cp != quote && *cp != '\0')
    444 			*bp++ = *cp++;
    445 		if (*cp++ == '\0') {
    446 			fprintf(stderr, "missing %c\n", quote);
    447 			cp--;
    448 			continue;
    449 		}
    450 	}
    451 	*bp = '\0';
    452 	return (cp);
    453 }
    454 
    455 /*
    456  * Canonicalize file names to always start with ``./'' and
    457  * remove any imbedded "." and ".." components.
    458  */
    459 void
    460 canon(const char *rawname, char *canonname, size_t len)
    461 {
    462 	char *cp, *np;
    463 
    464 	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
    465 		(void) strcpy(canonname, "");
    466 	else if (rawname[0] == '/')
    467 		(void) strcpy(canonname, ".");
    468 	else
    469 		(void) strcpy(canonname, "./");
    470 	if (strlen(canonname) + strlen(rawname) >= len) {
    471 		fprintf(stderr, "canonname: not enough buffer space\n");
    472 		exit(1);
    473 	}
    474 
    475 	(void) strcat(canonname, rawname);
    476 	/*
    477 	 * Eliminate multiple and trailing '/'s
    478 	 */
    479 	for (cp = np = canonname; *np != '\0'; cp++) {
    480 		*cp = *np++;
    481 		while (*cp == '/' && *np == '/')
    482 			np++;
    483 	}
    484 	*cp = '\0';
    485 	if (*--cp == '/')
    486 		*cp = '\0';
    487 	/*
    488 	 * Eliminate extraneous "." and ".." from pathnames.
    489 	 */
    490 	for (np = canonname; *np != '\0'; ) {
    491 		np++;
    492 		cp = np;
    493 		while (*np != '/' && *np != '\0')
    494 			np++;
    495 		if (np - cp == 1 && *cp == '.') {
    496 			cp--;
    497 			(void) strcpy(cp, np);
    498 			np = cp;
    499 		}
    500 		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
    501 			cp--;
    502 			while (cp > &canonname[1] && *--cp != '/')
    503 				/* find beginning of name */;
    504 			(void) strcpy(cp, np);
    505 			np = cp;
    506 		}
    507 	}
    508 }
    509 
    510 /*
    511  * Do an "ls" style listing of a directory
    512  */
    513 static void
    514 printlist(char *name, char *basename)
    515 {
    516 	struct afile *fp, *list, *listp;
    517 	struct direct *dp;
    518 	struct afile single;
    519 	RST_DIR *dirp;
    520 	int entries, len, namelen;
    521 	char locname[MAXPATHLEN + 1];
    522 
    523 	dp = pathsearch(name);
    524 	listp = NULL;
    525 	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
    526 	    (!vflag && dp->d_ino == UFS_WINO))
    527 		return;
    528 	if ((dirp = rst_opendir(name)) == NULL) {
    529 		entries = 1;
    530 		list = &single;
    531 		mkentry(name, dp, list);
    532 		len = strlen(basename) + 1;
    533 		if (strlen(name) - len > single.len) {
    534 			freename(single.fname);
    535 			single.fname = savename(&name[len]);
    536 			single.len = strlen(single.fname);
    537 		}
    538 	} else {
    539 		entries = 0;
    540 		while ((dp = rst_readdir(dirp)) != NULL)
    541 			entries++;
    542 		rst_closedir(dirp);
    543 		list = (struct afile *)malloc(entries * sizeof(struct afile));
    544 		if (list == NULL) {
    545 			fprintf(stderr, "ls: out of memory\n");
    546 			return;
    547 		}
    548 		if ((dirp = rst_opendir(name)) == NULL)
    549 			panic("directory reopen failed\n");
    550 		fprintf(stderr, "%s:\n", name);
    551 		entries = 0;
    552 		listp = list;
    553 		namelen = snprintf(locname, sizeof(locname), "%s/", name);
    554 		while ((dp = rst_readdir(dirp)) != NULL) {
    555 			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
    556 				continue;
    557 			if (!vflag && (dp->d_ino == UFS_WINO ||
    558 			     strcmp(dp->d_name, ".") == 0 ||
    559 			     strcmp(dp->d_name, "..") == 0))
    560 				continue;
    561 			locname[namelen] = '\0';
    562 			if (namelen + dp->d_namlen >= MAXPATHLEN) {
    563 				fprintf(stderr, "%s%s: name exceeds %d char\n",
    564 					locname, dp->d_name, MAXPATHLEN);
    565 			} else {
    566 				(void)strlcat(locname, dp->d_name, MAXPATHLEN);
    567 				mkentry(locname, dp, listp++);
    568 				entries++;
    569 			}
    570 		}
    571 		rst_closedir(dirp);
    572 		if (entries == 0) {
    573 			fprintf(stderr, "\n");
    574 			free(list);
    575 			return;
    576 		}
    577 		qsort((char *)list, entries, sizeof(struct afile), fcmp);
    578 	}
    579 	formatf(list, entries);
    580 	if (dirp != NULL) {
    581 		for (fp = listp - 1; fp >= list; fp--)
    582 			freename(fp->fname);
    583 		fprintf(stderr, "\n");
    584 		free(list);
    585 	}
    586 }
    587 
    588 /*
    589  * Read the contents of a directory.
    590  */
    591 static void
    592 mkentry(char *name, struct direct *dp, struct afile *fp)
    593 {
    594 	char *cp;
    595 	struct entry *np;
    596 
    597 	fp->fnum = dp->d_ino;
    598 	fp->fname = savename(dp->d_name);
    599 	for (cp = fp->fname; *cp; cp++)
    600 		if (!vflag && !isprint((unsigned char)*cp))
    601 			*cp = '?';
    602 	fp->len = cp - fp->fname;
    603 	if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
    604 		fp->prefix = '^';
    605 	else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
    606 		fp->prefix = '*';
    607 	else
    608 		fp->prefix = ' ';
    609 	switch(dp->d_type) {
    610 
    611 	default:
    612 		fprintf(stderr, "Warning: undefined file type %d\n",
    613 		    dp->d_type);
    614 		/* FALLTHROUGH */
    615 	case DT_REG:
    616 		fp->postfix = ' ';
    617 		break;
    618 
    619 	case DT_LNK:
    620 		fp->postfix = '@';
    621 		break;
    622 
    623 	case DT_FIFO:
    624 	case DT_SOCK:
    625 		fp->postfix = '=';
    626 		break;
    627 
    628 	case DT_CHR:
    629 	case DT_BLK:
    630 		fp->postfix = '#';
    631 		break;
    632 
    633 	case DT_WHT:
    634 		fp->postfix = '%';
    635 		break;
    636 
    637 	case DT_UNKNOWN:
    638 	case DT_DIR:
    639 		if (inodetype(dp->d_ino) == NODE)
    640 			fp->postfix = '/';
    641 		else
    642 			fp->postfix = ' ';
    643 		break;
    644 	}
    645 	return;
    646 }
    647 
    648 /*
    649  * Print out a pretty listing of a directory
    650  */
    651 static void
    652 formatf(struct afile *list, int nentry)
    653 {
    654 	struct afile *fp, *endlist;
    655 	int haveprefix, havepostfix;
    656 	ino_t bigino;
    657 	size_t width, w;
    658 	int i, j, precision, columns, lines;
    659 
    660 	width = 0;
    661 	haveprefix = 0;
    662 	havepostfix = 0;
    663 	precision = 0;
    664 	bigino = UFS_ROOTINO;
    665 	endlist = &list[nentry];
    666 	for (fp = &list[0]; fp < endlist; fp++) {
    667 		if (bigino < fp->fnum)
    668 			bigino = fp->fnum;
    669 		if (width < fp->len)
    670 			width = fp->len;
    671 		if (fp->prefix != ' ')
    672 			haveprefix = 1;
    673 		if (fp->postfix != ' ')
    674 			havepostfix = 1;
    675 	}
    676 	if (haveprefix)
    677 		width++;
    678 	if (havepostfix)
    679 		width++;
    680 	if (vflag) {
    681 		for (precision = 0, i = bigino; i > 0; i /= 10)
    682 			precision++;
    683 		width += precision + 1;
    684 	}
    685 	width++;
    686 	columns = 81 / width;
    687 	if (columns == 0)
    688 		columns = 1;
    689 	lines = (nentry + columns - 1) / columns;
    690 	for (i = 0; i < lines; i++) {
    691 		for (j = 0; j < columns; j++) {
    692 			fp = &list[j * lines + i];
    693 			if (vflag) {
    694 				fprintf(stderr, "%*ju ", precision,
    695 				    (uintmax_t)fp->fnum);
    696 				fp->len += precision + 1;
    697 			}
    698 			if (haveprefix) {
    699 				putc(fp->prefix, stderr);
    700 				fp->len++;
    701 			}
    702 			fprintf(stderr, "%s", fp->fname);
    703 			if (havepostfix) {
    704 				putc(fp->postfix, stderr);
    705 				fp->len++;
    706 			}
    707 			if (fp + lines >= endlist) {
    708 				fprintf(stderr, "\n");
    709 				break;
    710 			}
    711 			for (w = fp->len; w < width; w++)
    712 				putc(' ', stderr);
    713 		}
    714 	}
    715 }
    716 
    717 /*
    718  * Skip over directory entries that are not on the tape
    719  *
    720  * First have to get definition of a dirent.
    721  */
    722 #undef DIRBLKSIZ
    723 #include <dirent.h>
    724 #undef d_ino
    725 
    726 struct dirent *
    727 glob_readdir(RST_DIR *dirp)
    728 {
    729 	struct direct *dp;
    730 	static struct dirent adirent;
    731 
    732 	while ((dp = rst_readdir(dirp)) != NULL) {
    733 		if (!vflag && dp->d_fileno == UFS_WINO)
    734 			continue;
    735 		if (dflag || TSTINO(dp->d_fileno, dumpmap))
    736 			break;
    737 	}
    738 	if (dp == NULL)
    739 		return (NULL);
    740 	adirent.d_fileno = dp->d_fileno;
    741 	adirent.d_namlen = dp->d_namlen;
    742 	memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
    743 	return (&adirent);
    744 }
    745 
    746 /*
    747  * Return st_mode information in response to stat or lstat calls
    748  */
    749 static int
    750 glob_stat(const char *name, struct stat *stp)
    751 {
    752 	struct direct *dp;
    753 
    754 	dp = pathsearch(name);
    755 	if (dp == NULL || (!dflag && TSTINO(dp->d_fileno, dumpmap) == 0) ||
    756 	    (!vflag && dp->d_fileno == UFS_WINO))
    757 		return (-1);
    758 	if (inodetype(dp->d_fileno) == NODE)
    759 		stp->st_mode = S_IFDIR;
    760 	else
    761 		stp->st_mode = S_IFREG;
    762 	return (0);
    763 }
    764 
    765 /*
    766  * Comparison routine for qsort.
    767  */
    768 static int
    769 fcmp(const void *f1, const void *f2)
    770 {
    771 	return (strcoll(((const struct afile *)f1)->fname,
    772 	    ((const struct afile *)f2)->fname));
    773 }
    774 
    775 /*
    776  * respond to interrupts
    777  */
    778 void
    779 /*ARGSUSED*/
    780 onintr(int signo __unused)
    781 {
    782 	if (command == 'i' && runshell)
    783 		longjmp(reset, 1);
    784 	if (reply("restore interrupted, continue") == FAIL)
    785 		exit(1);
    786 }
    787