Home | History | Annotate | Line # | Download | only in restore
interactive.c revision 1.3
      1 /*
      2  * Copyright (c) 1985 The Regents of the University of California.
      3  * 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 /* from: static char sccsid[] = "@(#)interactive.c	5.18 (Berkeley) 12/2/92"; */
     36 static char *rcsid = "$Id: interactive.c,v 1.3 1993/12/22 10:31:47 cgd Exp $";
     37 #endif /* not lint */
     38 
     39 #include <sys/param.h>
     40 #include <sys/time.h>
     41 #include <sys/stat.h>
     42 
     43 #include <ufs/fs.h>
     44 #include <ufs/dinode.h>
     45 #include <ufs/dir.h>
     46 #include <protocols/dumprestore.h>
     47 
     48 #include <setjmp.h>
     49 #include <glob.h>
     50 #include <stdio.h>
     51 #include <stdlib.h>
     52 #include <string.h>
     53 
     54 #include "restore.h"
     55 #include "extern.h"
     56 
     57 #define round(a, b) (((a) + (b) - 1) / (b) * (b))
     58 
     59 /*
     60  * Things to handle interruptions.
     61  */
     62 static int runshell;
     63 static jmp_buf reset;
     64 static char *nextarg = NULL;
     65 
     66 /*
     67  * Structure and routines associated with listing directories.
     68  */
     69 struct afile {
     70 	ino_t	fnum;		/* inode number of file */
     71 	char	*fname;		/* file name */
     72 	short	len;		/* name length */
     73 	char	prefix;		/* prefix character */
     74 	char	postfix;	/* postfix character */
     75 };
     76 struct arglist {
     77 	int	freeglob;	/* glob structure needs to be freed */
     78 	int	argcnt;		/* next globbed argument to return */
     79 	glob_t	glob;		/* globbing information */
     80 	char	*cmd;		/* the current command */
     81 };
     82 
     83 static char	*copynext __P((char *, char *));
     84 static int	 fcmp __P((const void *, const void *));
     85 static char	*fmtentry __P((struct afile *));
     86 static void	 formatf __P((struct afile *, int));
     87 static void	 getcmd __P((char *, char *, char *, struct arglist *));
     88 struct dirent	*glob_readdir __P((RST_DIR *dirp));
     89 static int	 glob_stat __P((const char *, struct stat *));
     90 static void	 mkentry __P((struct direct *, struct afile *));
     91 static void	 printlist __P((char *, char *));
     92 
     93 /*
     94  * Read and execute commands from the terminal.
     95  */
     96 void
     97 runcmdshell()
     98 {
     99 	register struct entry *np;
    100 	ino_t ino;
    101 	struct arglist arglist;
    102 	char curdir[MAXPATHLEN];
    103 	char name[MAXPATHLEN];
    104 	char cmd[BUFSIZ];
    105 
    106 	arglist.freeglob = 0;
    107 	arglist.argcnt = 0;
    108 	arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
    109 	arglist.glob.gl_opendir = (void *)rst_opendir;
    110 	arglist.glob.gl_readdir = (void *)glob_readdir;
    111 	arglist.glob.gl_closedir = (void *)rst_closedir;
    112 	arglist.glob.gl_lstat = glob_stat;
    113 	arglist.glob.gl_stat = glob_stat;
    114 	canon("/", curdir);
    115 loop:
    116 	if (setjmp(reset) != 0) {
    117 		if (arglist.freeglob != 0) {
    118 			arglist.freeglob = 0;
    119 			arglist.argcnt = 0;
    120 			globfree(&arglist.glob);
    121 		}
    122 		nextarg = NULL;
    123 		volno = 0;
    124 	}
    125 	runshell = 1;
    126 	getcmd(curdir, cmd, name, &arglist);
    127 	switch (cmd[0]) {
    128 	/*
    129 	 * Add elements to the extraction list.
    130 	 */
    131 	case 'a':
    132 		if (strncmp(cmd, "add", strlen(cmd)) != 0)
    133 			goto bad;
    134 		ino = dirlookup(name);
    135 		if (ino == 0)
    136 			break;
    137 		if (mflag)
    138 			pathcheck(name);
    139 		treescan(name, ino, addfile);
    140 		break;
    141 	/*
    142 	 * Change working directory.
    143 	 */
    144 	case 'c':
    145 		if (strncmp(cmd, "cd", strlen(cmd)) != 0)
    146 			goto bad;
    147 		ino = dirlookup(name);
    148 		if (ino == 0)
    149 			break;
    150 		if (inodetype(ino) == LEAF) {
    151 			fprintf(stderr, "%s: not a directory\n", name);
    152 			break;
    153 		}
    154 		(void) strcpy(curdir, name);
    155 		break;
    156 	/*
    157 	 * Delete elements from the extraction list.
    158 	 */
    159 	case 'd':
    160 		if (strncmp(cmd, "delete", strlen(cmd)) != 0)
    161 			goto bad;
    162 		np = lookupname(name);
    163 		if (np == NULL || (np->e_flags & NEW) == 0) {
    164 			fprintf(stderr, "%s: not on extraction list\n", name);
    165 			break;
    166 		}
    167 		treescan(name, np->e_ino, deletefile);
    168 		break;
    169 	/*
    170 	 * Extract the requested list.
    171 	 */
    172 	case 'e':
    173 		if (strncmp(cmd, "extract", strlen(cmd)) != 0)
    174 			goto bad;
    175 		createfiles();
    176 		createlinks();
    177 		setdirmodes(0);
    178 		if (dflag)
    179 			checkrestore();
    180 		volno = 0;
    181 		break;
    182 	/*
    183 	 * List available commands.
    184 	 */
    185 	case 'h':
    186 		if (strncmp(cmd, "help", strlen(cmd)) != 0)
    187 			goto bad;
    188 	case '?':
    189 		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
    190 			"Available commands are:\n",
    191 			"\tls [arg] - list directory\n",
    192 			"\tcd arg - change directory\n",
    193 			"\tpwd - print current directory\n",
    194 			"\tadd [arg] - add `arg' to list of",
    195 			" files to be extracted\n",
    196 			"\tdelete [arg] - delete `arg' from",
    197 			" list of files to be extracted\n",
    198 			"\textract - extract requested files\n",
    199 			"\tsetmodes - set modes of requested directories\n",
    200 			"\tquit - immediately exit program\n",
    201 			"\twhat - list dump header information\n",
    202 			"\tverbose - toggle verbose flag",
    203 			" (useful with ``ls'')\n",
    204 			"\thelp or `?' - print this list\n",
    205 			"If no `arg' is supplied, the current",
    206 			" directory is used\n");
    207 		break;
    208 	/*
    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  */
    304 static void
    305 getcmd(curdir, cmd, name, ap)
    306 	char *curdir, *cmd, *name;
    307 	struct arglist *ap;
    308 {
    309 	register 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 		(void) fgets(input, BUFSIZ, terminal);
    328 	} while (!feof(terminal) && input[0] == '\n');
    329 	if (feof(terminal)) {
    330 		(void) strcpy(cmd, "quit");
    331 		return;
    332 	}
    333 	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
    334 		/* trim off trailing white space and newline */;
    335 	*++cp = '\0';
    336 	/*
    337 	 * Copy the command into "cmd".
    338 	 */
    339 	cp = copynext(input, cmd);
    340 	ap->cmd = cmd;
    341 	/*
    342 	 * If no argument, use curdir as the default.
    343 	 */
    344 	if (*cp == '\0') {
    345 		(void) strcpy(name, curdir);
    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);
    363 	} else {
    364 		/*
    365 		 * For relative pathnames, prepend the current directory to
    366 		 * it then canonicalize and return it.
    367 		 */
    368 		(void) strcpy(output, curdir);
    369 		(void) strcat(output, "/");
    370 		(void) strcat(output, rawname);
    371 		canon(output, name);
    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 
    380 retnext:
    381 	strcpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt]);
    382 	if (--ap->argcnt == 0) {
    383 		ap->freeglob = 0;
    384 		globfree(&ap->glob);
    385 	}
    386 #	undef rawname
    387 }
    388 
    389 /*
    390  * Strip off the next token of the input.
    391  */
    392 static char *
    393 copynext(input, output)
    394 	char *input, *output;
    395 {
    396 	register char *cp, *bp;
    397 	char quote;
    398 
    399 	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
    400 		/* skip to argument */;
    401 	bp = output;
    402 	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
    403 		/*
    404 		 * Handle back slashes.
    405 		 */
    406 		if (*cp == '\\') {
    407 			if (*++cp == '\0') {
    408 				fprintf(stderr,
    409 					"command lines cannot be continued\n");
    410 				continue;
    411 			}
    412 			*bp++ = *cp++;
    413 			continue;
    414 		}
    415 		/*
    416 		 * The usual unquoted case.
    417 		 */
    418 		if (*cp != '\'' && *cp != '"') {
    419 			*bp++ = *cp++;
    420 			continue;
    421 		}
    422 		/*
    423 		 * Handle single and double quotes.
    424 		 */
    425 		quote = *cp++;
    426 		while (*cp != quote && *cp != '\0')
    427 			*bp++ = *cp++ | 0200;
    428 		if (*cp++ == '\0') {
    429 			fprintf(stderr, "missing %c\n", quote);
    430 			cp--;
    431 			continue;
    432 		}
    433 	}
    434 	*bp = '\0';
    435 	return (cp);
    436 }
    437 
    438 /*
    439  * Canonicalize file names to always start with ``./'' and
    440  * remove any imbedded "." and ".." components.
    441  */
    442 void
    443 canon(rawname, canonname)
    444 	char *rawname, *canonname;
    445 {
    446 	register char *cp, *np;
    447 
    448 	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
    449 		(void) strcpy(canonname, "");
    450 	else if (rawname[0] == '/')
    451 		(void) strcpy(canonname, ".");
    452 	else
    453 		(void) strcpy(canonname, "./");
    454 	(void) strcat(canonname, rawname);
    455 	/*
    456 	 * Eliminate multiple and trailing '/'s
    457 	 */
    458 	for (cp = np = canonname; *np != '\0'; cp++) {
    459 		*cp = *np++;
    460 		while (*cp == '/' && *np == '/')
    461 			np++;
    462 	}
    463 	*cp = '\0';
    464 	if (*--cp == '/')
    465 		*cp = '\0';
    466 	/*
    467 	 * Eliminate extraneous "." and ".." from pathnames.
    468 	 */
    469 	for (np = canonname; *np != '\0'; ) {
    470 		np++;
    471 		cp = np;
    472 		while (*np != '/' && *np != '\0')
    473 			np++;
    474 		if (np - cp == 1 && *cp == '.') {
    475 			cp--;
    476 			(void) strcpy(cp, np);
    477 			np = cp;
    478 		}
    479 		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
    480 			cp--;
    481 			while (cp > &canonname[1] && *--cp != '/')
    482 				/* find beginning of name */;
    483 			(void) strcpy(cp, np);
    484 			np = cp;
    485 		}
    486 	}
    487 }
    488 
    489 /*
    490  * Do an "ls" style listing of a directory
    491  */
    492 static void
    493 printlist(name, basename)
    494 	char *name;
    495 	char *basename;
    496 {
    497 	register struct afile *fp, *list, *listp;
    498 	register struct direct *dp;
    499 	struct afile single;
    500 	RST_DIR *dirp;
    501 	int entries, len;
    502 
    503 	dp = pathsearch(name);
    504 	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0))
    505 		return;
    506 	if ((dirp = rst_opendir(name)) == NULL) {
    507 		entries = 1;
    508 		list = &single;
    509 		mkentry(dp, list);
    510 		len = strlen(basename) + 1;
    511 		if (strlen(name) - len > single.len) {
    512 			freename(single.fname);
    513 			single.fname = savename(&name[len]);
    514 			single.len = strlen(single.fname);
    515 		}
    516 	} else {
    517 		entries = 0;
    518 		while (dp = rst_readdir(dirp))
    519 			entries++;
    520 		rst_closedir(dirp);
    521 		list = (struct afile *)malloc(entries * sizeof(struct afile));
    522 		if (list == NULL) {
    523 			fprintf(stderr, "ls: out of memory\n");
    524 			return;
    525 		}
    526 		if ((dirp = rst_opendir(name)) == NULL)
    527 			panic("directory reopen failed\n");
    528 		fprintf(stderr, "%s:\n", name);
    529 		entries = 0;
    530 		listp = list;
    531 		while (dp = rst_readdir(dirp)) {
    532 			if (dp == NULL || dp->d_ino == 0)
    533 				break;
    534 			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
    535 				continue;
    536 			if (vflag == 0 &&
    537 			    (strcmp(dp->d_name, ".") == 0 ||
    538 			     strcmp(dp->d_name, "..") == 0))
    539 				continue;
    540 			mkentry(dp, listp++);
    541 			entries++;
    542 		}
    543 		rst_closedir(dirp);
    544 		if (entries == 0) {
    545 			fprintf(stderr, "\n");
    546 			free(list);
    547 			return;
    548 		}
    549 		qsort((char *)list, entries, sizeof(struct afile), fcmp);
    550 	}
    551 	formatf(list, entries);
    552 	if (dirp != NULL) {
    553 		for (fp = listp - 1; fp >= list; fp--)
    554 			freename(fp->fname);
    555 		fprintf(stderr, "\n");
    556 		free(list);
    557 	}
    558 }
    559 
    560 /*
    561  * Read the contents of a directory.
    562  */
    563 static void
    564 mkentry(dp, fp)
    565 	struct direct *dp;
    566 	register struct afile *fp;
    567 {
    568 	char *cp;
    569 	struct entry *np;
    570 	int type;
    571 
    572 	fp->fnum = dp->d_ino;
    573 	fp->fname = savename(dp->d_name);
    574 	for (cp = fp->fname; *cp; cp++)
    575 		if (!vflag && (*cp < ' ' || *cp >= 0177))
    576 			*cp = '?';
    577 	fp->len = cp - fp->fname;
    578 	if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
    579 		fp->prefix = '^';
    580 	else if ((np = lookupino(fp->fnum)) != NULL && (np->e_flags & NEW))
    581 		fp->prefix = '*';
    582 	else
    583 		fp->prefix = ' ';
    584 
    585 #ifndef BSD44
    586 	type = (np && (np->e_type == NODE)) ? DT_DIR : DT_REG;
    587 #else
    588 	type = dp->d_type;
    589 #endif
    590 	switch(type) {
    591 
    592 	default:
    593 		fprintf(stderr, "Warning: undefined file type %d\n", type);
    594 		/* fall through */
    595 	case DT_REG:
    596 		fp->postfix = ' ';
    597 		break;
    598 
    599 	case DT_LNK:
    600 		fp->postfix = '@';
    601 		break;
    602 
    603 	case DT_FIFO:
    604 	case DT_SOCK:
    605 		fp->postfix = '=';
    606 		break;
    607 
    608 	case DT_CHR:
    609 	case DT_BLK:
    610 		fp->postfix = '#';
    611 		break;
    612 
    613 	case DT_UNKNOWN:
    614 	case DT_DIR:
    615 		if (inodetype(dp->d_ino) == NODE)
    616 			fp->postfix = '/';
    617 		else
    618 			fp->postfix = ' ';
    619 		break;
    620 	}
    621 
    622 	return;
    623 }
    624 
    625 /*
    626  * Print out a pretty listing of a directory
    627  */
    628 static void
    629 formatf(list, nentry)
    630 	register struct afile *list;
    631 	int nentry;
    632 {
    633 	register struct afile *fp, *endlist;
    634 	int width, bigino, haveprefix, havepostfix;
    635 	int i, j, w, precision, columns, lines;
    636 
    637 	width = 0;
    638 	haveprefix = 0;
    639 	havepostfix = 0;
    640 	bigino = ROOTINO;
    641 	endlist = &list[nentry];
    642 	for (fp = &list[0]; fp < endlist; fp++) {
    643 		if (bigino < fp->fnum)
    644 			bigino = fp->fnum;
    645 		if (width < fp->len)
    646 			width = fp->len;
    647 		if (fp->prefix != ' ')
    648 			haveprefix = 1;
    649 		if (fp->postfix != ' ')
    650 			havepostfix = 1;
    651 	}
    652 	if (haveprefix)
    653 		width++;
    654 	if (havepostfix)
    655 		width++;
    656 	if (vflag) {
    657 		for (precision = 0, i = bigino; i > 0; i /= 10)
    658 			precision++;
    659 		width += precision + 1;
    660 	}
    661 	width++;
    662 	columns = 81 / width;
    663 	if (columns == 0)
    664 		columns = 1;
    665 	lines = (nentry + columns - 1) / columns;
    666 	for (i = 0; i < lines; i++) {
    667 		for (j = 0; j < columns; j++) {
    668 			fp = &list[j * lines + i];
    669 			if (vflag) {
    670 				fprintf(stderr, "%*d ", precision, fp->fnum);
    671 				fp->len += precision + 1;
    672 			}
    673 			if (haveprefix) {
    674 				putc(fp->prefix, stderr);
    675 				fp->len++;
    676 			}
    677 			fprintf(stderr, "%s", fp->fname);
    678 			if (havepostfix) {
    679 				putc(fp->postfix, stderr);
    680 				fp->len++;
    681 			}
    682 			if (fp + lines >= endlist) {
    683 				fprintf(stderr, "\n");
    684 				break;
    685 			}
    686 			for (w = fp->len; w < width; w++)
    687 				putc(' ', stderr);
    688 		}
    689 	}
    690 }
    691 
    692 /*
    693  * Skip over directory entries that are not on the tape
    694  *
    695  * First have to get definition of a dirent.
    696  */
    697 #undef DIRBLKSIZ
    698 #include <dirent.h>
    699 #undef d_ino
    700 
    701 struct dirent *
    702 glob_readdir(dirp)
    703 	RST_DIR *dirp;
    704 {
    705 	struct direct *dp;
    706 	static struct dirent adirent;
    707 
    708 	while ((dp = rst_readdir(dirp)) != NULL) {
    709 		if (dp->d_ino == 0)
    710 			continue;
    711 		if (dflag || TSTINO(dp->d_ino, dumpmap))
    712 			break;
    713 	}
    714 	if (dp == NULL)
    715 		return (NULL);
    716 	adirent.d_fileno = dp->d_ino;
    717 	adirent.d_namlen = dp->d_namlen;
    718 	bcopy(dp->d_name, adirent.d_name, dp->d_namlen + 1);
    719 	return (&adirent);
    720 }
    721 
    722 /*
    723  * Return st_mode information in response to stat or lstat calls
    724  */
    725 static int
    726 glob_stat(name, stp)
    727 	const char *name;
    728 	struct stat *stp;
    729 {
    730 	register struct direct *dp;
    731 
    732 	dp = pathsearch(name);
    733 	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0))
    734 		return (-1);
    735 	if (inodetype(dp->d_ino) == NODE)
    736 		stp->st_mode = IFDIR;
    737 	else
    738 		stp->st_mode = IFREG;
    739 	return (0);
    740 }
    741 
    742 /*
    743  * Comparison routine for qsort.
    744  */
    745 static int
    746 fcmp(f1, f2)
    747 	register const void *f1, *f2;
    748 {
    749 	return (strcmp(((struct afile *)f1)->fname,
    750 	    ((struct afile *)f2)->fname));
    751 }
    752 
    753 /*
    754  * respond to interrupts
    755  */
    756 void
    757 onintr(signo)
    758 	int signo;
    759 {
    760 	if (command == 'i' && runshell)
    761 		longjmp(reset, 1);
    762 	if (reply("restore interrupted, continue") == FAIL)
    763 		done(1);
    764 }
    765