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