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