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