Home | History | Annotate | Line # | Download | only in restore
      1  1.31  christos /*	$NetBSD: interactive.c,v 1.31 2021/08/29 09:17:58 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.31  christos __RCSID("$NetBSD: interactive.c,v 1.31 2021/08/29 09:17:58 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.29  christos #include <ctype.h>
     53   1.3       cgd #include <stdio.h>
     54   1.3       cgd #include <stdlib.h>
     55   1.3       cgd #include <string.h>
     56   1.3       cgd 
     57   1.3       cgd #include "restore.h"
     58   1.3       cgd #include "extern.h"
     59   1.1       cgd 
     60   1.1       cgd #define round(a, b) (((a) + (b) - 1) / (b) * (b))
     61   1.1       cgd 
     62   1.1       cgd /*
     63   1.1       cgd  * Things to handle interruptions.
     64   1.1       cgd  */
     65   1.3       cgd static int runshell;
     66   1.1       cgd static jmp_buf reset;
     67   1.1       cgd static char *nextarg = NULL;
     68   1.1       cgd 
     69   1.1       cgd /*
     70   1.1       cgd  * Structure and routines associated with listing directories.
     71   1.1       cgd  */
     72   1.1       cgd struct afile {
     73   1.1       cgd 	ino_t	fnum;		/* inode number of file */
     74   1.1       cgd 	char	*fname;		/* file name */
     75  1.26     lukem 	size_t	len;		/* name length */
     76   1.3       cgd 	char	prefix;		/* prefix character */
     77   1.3       cgd 	char	postfix;	/* postfix character */
     78   1.1       cgd };
     79   1.1       cgd struct arglist {
     80   1.3       cgd 	int	freeglob;	/* glob structure needs to be freed */
     81   1.3       cgd 	int	argcnt;		/* next globbed argument to return */
     82   1.3       cgd 	glob_t	glob;		/* globbing information */
     83   1.3       cgd 	char	*cmd;		/* the current command */
     84   1.1       cgd };
     85   1.3       cgd 
     86  1.21   xtraeme static char	*copynext(char *, char *);
     87  1.21   xtraeme static int	 fcmp(const void *, const void *);
     88  1.21   xtraeme static void	 formatf(struct afile *, int);
     89  1.29  christos static void	 getcmd(char *, char *, char *, size_t, struct arglist *);
     90  1.21   xtraeme struct dirent	*glob_readdir(RST_DIR *dirp);
     91  1.21   xtraeme static int	 glob_stat(const char *, struct stat *);
     92  1.21   xtraeme static void	 mkentry(char *, struct direct *, struct afile *);
     93  1.21   xtraeme static void	 printlist(char *, char *);
     94   1.1       cgd 
     95   1.1       cgd /*
     96   1.1       cgd  * Read and execute commands from the terminal.
     97   1.1       cgd  */
     98   1.3       cgd void
     99  1.21   xtraeme runcmdshell(void)
    100   1.1       cgd {
    101  1.10     lukem 	struct entry *np;
    102   1.1       cgd 	ino_t ino;
    103   1.3       cgd 	struct arglist arglist;
    104   1.1       cgd 	char curdir[MAXPATHLEN];
    105   1.1       cgd 	char name[MAXPATHLEN];
    106   1.1       cgd 	char cmd[BUFSIZ];
    107   1.1       cgd 
    108   1.3       cgd 	arglist.freeglob = 0;
    109   1.3       cgd 	arglist.argcnt = 0;
    110   1.3       cgd 	arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
    111   1.3       cgd 	arglist.glob.gl_opendir = (void *)rst_opendir;
    112   1.3       cgd 	arglist.glob.gl_readdir = (void *)glob_readdir;
    113   1.3       cgd 	arglist.glob.gl_closedir = (void *)rst_closedir;
    114   1.3       cgd 	arglist.glob.gl_lstat = glob_stat;
    115   1.3       cgd 	arglist.glob.gl_stat = glob_stat;
    116  1.29  christos 	canon("/", curdir, sizeof(curdir));
    117   1.1       cgd loop:
    118   1.1       cgd 	if (setjmp(reset) != 0) {
    119   1.3       cgd 		if (arglist.freeglob != 0) {
    120   1.3       cgd 			arglist.freeglob = 0;
    121   1.3       cgd 			arglist.argcnt = 0;
    122   1.3       cgd 			globfree(&arglist.glob);
    123   1.3       cgd 		}
    124   1.1       cgd 		nextarg = NULL;
    125   1.1       cgd 		volno = 0;
    126   1.1       cgd 	}
    127   1.3       cgd 	runshell = 1;
    128  1.29  christos 	getcmd(curdir, cmd, name, sizeof(name), &arglist);
    129   1.1       cgd 	switch (cmd[0]) {
    130   1.1       cgd 	/*
    131   1.1       cgd 	 * Add elements to the extraction list.
    132   1.1       cgd 	 */
    133   1.1       cgd 	case 'a':
    134   1.1       cgd 		if (strncmp(cmd, "add", strlen(cmd)) != 0)
    135   1.1       cgd 			goto bad;
    136   1.1       cgd 		ino = dirlookup(name);
    137   1.1       cgd 		if (ino == 0)
    138   1.1       cgd 			break;
    139  1.27  dholland 		if (ino == UFS_ROOTINO)
    140  1.20     fredb 			dotflag = 1;
    141   1.1       cgd 		if (mflag)
    142   1.1       cgd 			pathcheck(name);
    143   1.1       cgd 		treescan(name, ino, addfile);
    144   1.1       cgd 		break;
    145   1.1       cgd 	/*
    146   1.1       cgd 	 * Change working directory.
    147   1.1       cgd 	 */
    148   1.1       cgd 	case 'c':
    149   1.1       cgd 		if (strncmp(cmd, "cd", strlen(cmd)) != 0)
    150   1.1       cgd 			goto bad;
    151   1.1       cgd 		ino = dirlookup(name);
    152   1.1       cgd 		if (ino == 0)
    153   1.1       cgd 			break;
    154   1.1       cgd 		if (inodetype(ino) == LEAF) {
    155   1.1       cgd 			fprintf(stderr, "%s: not a directory\n", name);
    156   1.1       cgd 			break;
    157   1.1       cgd 		}
    158   1.1       cgd 		(void) strcpy(curdir, name);
    159   1.1       cgd 		break;
    160   1.1       cgd 	/*
    161   1.1       cgd 	 * Delete elements from the extraction list.
    162   1.1       cgd 	 */
    163   1.1       cgd 	case 'd':
    164   1.1       cgd 		if (strncmp(cmd, "delete", strlen(cmd)) != 0)
    165   1.1       cgd 			goto bad;
    166   1.1       cgd 		np = lookupname(name);
    167   1.3       cgd 		if (np == NULL || (np->e_flags & NEW) == 0) {
    168   1.1       cgd 			fprintf(stderr, "%s: not on extraction list\n", name);
    169   1.1       cgd 			break;
    170   1.1       cgd 		}
    171   1.1       cgd 		treescan(name, np->e_ino, deletefile);
    172   1.1       cgd 		break;
    173   1.1       cgd 	/*
    174   1.1       cgd 	 * Extract the requested list.
    175   1.1       cgd 	 */
    176   1.1       cgd 	case 'e':
    177   1.1       cgd 		if (strncmp(cmd, "extract", strlen(cmd)) != 0)
    178   1.1       cgd 			goto bad;
    179   1.1       cgd 		createfiles();
    180   1.1       cgd 		createlinks();
    181   1.3       cgd 		setdirmodes(0);
    182   1.1       cgd 		if (dflag)
    183   1.1       cgd 			checkrestore();
    184   1.1       cgd 		volno = 0;
    185   1.1       cgd 		break;
    186   1.1       cgd 	/*
    187   1.1       cgd 	 * List available commands.
    188   1.1       cgd 	 */
    189   1.1       cgd 	case 'h':
    190   1.1       cgd 		if (strncmp(cmd, "help", strlen(cmd)) != 0)
    191   1.1       cgd 			goto bad;
    192  1.28       mrg 		/* FALLTHROUGH */
    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.29  christos getcmd(char *curdir, char *cmd, char *name, size_t size, struct arglist *ap)
    312   1.1       cgd {
    313  1.10     lukem 	char *cp;
    314   1.1       cgd 	static char input[BUFSIZ];
    315  1.29  christos 	char output[BUFSIZ * 2];
    316  1.15     lukem 	int globretval;
    317   1.1       cgd #	define rawname input	/* save space by reusing input buffer */
    318   1.1       cgd 
    319   1.1       cgd 	/*
    320   1.1       cgd 	 * Check to see if still processing arguments.
    321   1.1       cgd 	 */
    322   1.3       cgd 	if (ap->argcnt > 0)
    323   1.3       cgd 		goto retnext;
    324   1.1       cgd 	if (nextarg != NULL)
    325   1.1       cgd 		goto getnext;
    326   1.1       cgd 	/*
    327   1.1       cgd 	 * Read a command line and trim off trailing white space.
    328   1.1       cgd 	 */
    329   1.1       cgd 	do	{
    330  1.18       cgd 		fprintf(stderr, "%s > ", getprogname());
    331   1.1       cgd 		(void) fflush(stderr);
    332   1.1       cgd 		(void) fgets(input, BUFSIZ, terminal);
    333   1.1       cgd 	} while (!feof(terminal) && input[0] == '\n');
    334   1.1       cgd 	if (feof(terminal)) {
    335  1.29  christos 		(void) strlcpy(cmd, "quit", size);
    336   1.1       cgd 		return;
    337   1.1       cgd 	}
    338   1.1       cgd 	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
    339   1.1       cgd 		/* trim off trailing white space and newline */;
    340   1.1       cgd 	*++cp = '\0';
    341   1.1       cgd 	/*
    342   1.1       cgd 	 * Copy the command into "cmd".
    343   1.1       cgd 	 */
    344   1.1       cgd 	cp = copynext(input, cmd);
    345   1.1       cgd 	ap->cmd = cmd;
    346   1.1       cgd 	/*
    347   1.1       cgd 	 * If no argument, use curdir as the default.
    348   1.1       cgd 	 */
    349   1.1       cgd 	if (*cp == '\0') {
    350  1.29  christos 		(void) strlcpy(name, curdir, size);
    351   1.1       cgd 		return;
    352   1.1       cgd 	}
    353   1.1       cgd 	nextarg = cp;
    354   1.1       cgd 	/*
    355   1.1       cgd 	 * Find the next argument.
    356   1.1       cgd 	 */
    357   1.1       cgd getnext:
    358   1.1       cgd 	cp = copynext(nextarg, rawname);
    359   1.1       cgd 	if (*cp == '\0')
    360   1.1       cgd 		nextarg = NULL;
    361   1.1       cgd 	else
    362   1.1       cgd 		nextarg = cp;
    363   1.1       cgd 	/*
    364   1.3       cgd 	 * If it is an absolute pathname, canonicalize it and return it.
    365   1.1       cgd 	 */
    366   1.1       cgd 	if (rawname[0] == '/') {
    367  1.29  christos 		canon(rawname, name, size);
    368   1.1       cgd 	} else {
    369   1.1       cgd 		/*
    370   1.1       cgd 		 * For relative pathnames, prepend the current directory to
    371   1.1       cgd 		 * it then canonicalize and return it.
    372   1.1       cgd 		 */
    373  1.29  christos 		snprintf(output, sizeof(output), "%s/%s", curdir, rawname);
    374  1.30  christos 		canon(output, name, size);
    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.29  christos 	strlcpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size);
    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.29  christos canon(const char *rawname, char *canonname, size_t len)
    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.29  christos 	if (strlen(canonname) + strlen(rawname) >= len) {
    471  1.29  christos 		fprintf(stderr, "canonname: not enough buffer space\n");
    472  1.29  christos 		exit(1);
    473  1.29  christos 	}
    474  1.29  christos 
    475   1.1       cgd 	(void) strcat(canonname, rawname);
    476   1.1       cgd 	/*
    477   1.1       cgd 	 * Eliminate multiple and trailing '/'s
    478   1.1       cgd 	 */
    479   1.1       cgd 	for (cp = np = canonname; *np != '\0'; cp++) {
    480   1.1       cgd 		*cp = *np++;
    481   1.1       cgd 		while (*cp == '/' && *np == '/')
    482   1.1       cgd 			np++;
    483   1.1       cgd 	}
    484   1.1       cgd 	*cp = '\0';
    485   1.1       cgd 	if (*--cp == '/')
    486   1.1       cgd 		*cp = '\0';
    487   1.1       cgd 	/*
    488   1.1       cgd 	 * Eliminate extraneous "." and ".." from pathnames.
    489   1.1       cgd 	 */
    490   1.1       cgd 	for (np = canonname; *np != '\0'; ) {
    491   1.1       cgd 		np++;
    492   1.1       cgd 		cp = np;
    493   1.1       cgd 		while (*np != '/' && *np != '\0')
    494   1.1       cgd 			np++;
    495   1.1       cgd 		if (np - cp == 1 && *cp == '.') {
    496   1.1       cgd 			cp--;
    497   1.1       cgd 			(void) strcpy(cp, np);
    498   1.1       cgd 			np = cp;
    499   1.1       cgd 		}
    500   1.1       cgd 		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
    501   1.1       cgd 			cp--;
    502   1.1       cgd 			while (cp > &canonname[1] && *--cp != '/')
    503   1.1       cgd 				/* find beginning of name */;
    504   1.1       cgd 			(void) strcpy(cp, np);
    505   1.1       cgd 			np = cp;
    506   1.1       cgd 		}
    507   1.1       cgd 	}
    508   1.1       cgd }
    509   1.1       cgd 
    510   1.1       cgd /*
    511   1.1       cgd  * Do an "ls" style listing of a directory
    512   1.1       cgd  */
    513   1.3       cgd static void
    514  1.21   xtraeme printlist(char *name, char *basename)
    515   1.1       cgd {
    516  1.10     lukem 	struct afile *fp, *list, *listp;
    517  1.10     lukem 	struct direct *dp;
    518   1.1       cgd 	struct afile single;
    519   1.3       cgd 	RST_DIR *dirp;
    520   1.6   mycroft 	int entries, len, namelen;
    521   1.6   mycroft 	char locname[MAXPATHLEN + 1];
    522   1.1       cgd 
    523   1.3       cgd 	dp = pathsearch(name);
    524  1.11     lukem 	listp = NULL;
    525   1.6   mycroft 	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
    526  1.27  dholland 	    (!vflag && dp->d_ino == UFS_WINO))
    527   1.3       cgd 		return;
    528   1.1       cgd 	if ((dirp = rst_opendir(name)) == NULL) {
    529   1.3       cgd 		entries = 1;
    530   1.3       cgd 		list = &single;
    531   1.6   mycroft 		mkentry(name, dp, list);
    532   1.3       cgd 		len = strlen(basename) + 1;
    533   1.3       cgd 		if (strlen(name) - len > single.len) {
    534   1.3       cgd 			freename(single.fname);
    535   1.3       cgd 			single.fname = savename(&name[len]);
    536   1.3       cgd 			single.len = strlen(single.fname);
    537   1.3       cgd 		}
    538   1.1       cgd 	} else {
    539   1.3       cgd 		entries = 0;
    540  1.11     lukem 		while ((dp = rst_readdir(dirp)) != NULL)
    541   1.3       cgd 			entries++;
    542   1.3       cgd 		rst_closedir(dirp);
    543   1.3       cgd 		list = (struct afile *)malloc(entries * sizeof(struct afile));
    544   1.3       cgd 		if (list == NULL) {
    545   1.3       cgd 			fprintf(stderr, "ls: out of memory\n");
    546   1.3       cgd 			return;
    547   1.3       cgd 		}
    548   1.3       cgd 		if ((dirp = rst_opendir(name)) == NULL)
    549   1.3       cgd 			panic("directory reopen failed\n");
    550   1.1       cgd 		fprintf(stderr, "%s:\n", name);
    551   1.3       cgd 		entries = 0;
    552   1.3       cgd 		listp = list;
    553  1.29  christos 		namelen = snprintf(locname, sizeof(locname), "%s/", name);
    554  1.11     lukem 		while ((dp = rst_readdir(dirp)) != NULL) {
    555   1.3       cgd 			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
    556   1.1       cgd 				continue;
    557  1.27  dholland 			if (!vflag && (dp->d_ino == UFS_WINO ||
    558   1.6   mycroft 			     strcmp(dp->d_name, ".") == 0 ||
    559   1.1       cgd 			     strcmp(dp->d_name, "..") == 0))
    560   1.1       cgd 				continue;
    561   1.6   mycroft 			locname[namelen] = '\0';
    562   1.6   mycroft 			if (namelen + dp->d_namlen >= MAXPATHLEN) {
    563   1.6   mycroft 				fprintf(stderr, "%s%s: name exceeds %d char\n",
    564   1.6   mycroft 					locname, dp->d_name, MAXPATHLEN);
    565   1.6   mycroft 			} else {
    566  1.29  christos 				(void)strlcat(locname, dp->d_name, MAXPATHLEN);
    567   1.6   mycroft 				mkentry(locname, dp, listp++);
    568   1.6   mycroft 				entries++;
    569   1.6   mycroft 			}
    570   1.1       cgd 		}
    571   1.3       cgd 		rst_closedir(dirp);
    572   1.3       cgd 		if (entries == 0) {
    573   1.3       cgd 			fprintf(stderr, "\n");
    574   1.3       cgd 			free(list);
    575   1.3       cgd 			return;
    576   1.3       cgd 		}
    577   1.3       cgd 		qsort((char *)list, entries, sizeof(struct afile), fcmp);
    578   1.3       cgd 	}
    579   1.3       cgd 	formatf(list, entries);
    580   1.3       cgd 	if (dirp != NULL) {
    581   1.3       cgd 		for (fp = listp - 1; fp >= list; fp--)
    582   1.1       cgd 			freename(fp->fname);
    583   1.3       cgd 		fprintf(stderr, "\n");
    584   1.3       cgd 		free(list);
    585   1.1       cgd 	}
    586   1.1       cgd }
    587   1.1       cgd 
    588   1.1       cgd /*
    589   1.1       cgd  * Read the contents of a directory.
    590   1.1       cgd  */
    591   1.3       cgd static void
    592  1.21   xtraeme mkentry(char *name, struct direct *dp, struct afile *fp)
    593   1.1       cgd {
    594   1.3       cgd 	char *cp;
    595   1.3       cgd 	struct entry *np;
    596   1.3       cgd 
    597   1.3       cgd 	fp->fnum = dp->d_ino;
    598   1.3       cgd 	fp->fname = savename(dp->d_name);
    599   1.3       cgd 	for (cp = fp->fname; *cp; cp++)
    600  1.31  christos 		if (!vflag && !isprint((unsigned char)*cp))
    601   1.3       cgd 			*cp = '?';
    602   1.3       cgd 	fp->len = cp - fp->fname;
    603   1.3       cgd 	if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
    604   1.3       cgd 		fp->prefix = '^';
    605   1.6   mycroft 	else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
    606   1.3       cgd 		fp->prefix = '*';
    607   1.3       cgd 	else
    608   1.3       cgd 		fp->prefix = ' ';
    609   1.4   mycroft 	switch(dp->d_type) {
    610   1.3       cgd 
    611   1.3       cgd 	default:
    612   1.4   mycroft 		fprintf(stderr, "Warning: undefined file type %d\n",
    613   1.4   mycroft 		    dp->d_type);
    614  1.29  christos 		/* FALLTHROUGH */
    615   1.3       cgd 	case DT_REG:
    616   1.3       cgd 		fp->postfix = ' ';
    617   1.3       cgd 		break;
    618   1.3       cgd 
    619   1.3       cgd 	case DT_LNK:
    620   1.3       cgd 		fp->postfix = '@';
    621   1.3       cgd 		break;
    622   1.3       cgd 
    623   1.3       cgd 	case DT_FIFO:
    624   1.3       cgd 	case DT_SOCK:
    625   1.3       cgd 		fp->postfix = '=';
    626   1.3       cgd 		break;
    627   1.3       cgd 
    628   1.3       cgd 	case DT_CHR:
    629   1.3       cgd 	case DT_BLK:
    630   1.3       cgd 		fp->postfix = '#';
    631   1.3       cgd 		break;
    632   1.1       cgd 
    633   1.6   mycroft 	case DT_WHT:
    634   1.6   mycroft 		fp->postfix = '%';
    635   1.6   mycroft 		break;
    636   1.6   mycroft 
    637   1.3       cgd 	case DT_UNKNOWN:
    638   1.3       cgd 	case DT_DIR:
    639   1.3       cgd 		if (inodetype(dp->d_ino) == NODE)
    640   1.3       cgd 			fp->postfix = '/';
    641   1.3       cgd 		else
    642   1.3       cgd 			fp->postfix = ' ';
    643   1.3       cgd 		break;
    644   1.1       cgd 	}
    645   1.3       cgd 	return;
    646   1.1       cgd }
    647   1.1       cgd 
    648   1.1       cgd /*
    649   1.1       cgd  * Print out a pretty listing of a directory
    650   1.1       cgd  */
    651   1.3       cgd static void
    652  1.21   xtraeme formatf(struct afile *list, int nentry)
    653   1.3       cgd {
    654  1.10     lukem 	struct afile *fp, *endlist;
    655  1.26     lukem 	int haveprefix, havepostfix;
    656  1.26     lukem 	ino_t bigino;
    657  1.26     lukem 	size_t width, w;
    658  1.26     lukem 	int i, j, precision, columns, lines;
    659   1.3       cgd 
    660   1.3       cgd 	width = 0;
    661   1.3       cgd 	haveprefix = 0;
    662   1.3       cgd 	havepostfix = 0;
    663  1.11     lukem 	precision = 0;
    664  1.27  dholland 	bigino = UFS_ROOTINO;
    665   1.3       cgd 	endlist = &list[nentry];
    666   1.3       cgd 	for (fp = &list[0]; fp < endlist; fp++) {
    667   1.3       cgd 		if (bigino < fp->fnum)
    668   1.3       cgd 			bigino = fp->fnum;
    669   1.3       cgd 		if (width < fp->len)
    670   1.3       cgd 			width = fp->len;
    671   1.3       cgd 		if (fp->prefix != ' ')
    672   1.3       cgd 			haveprefix = 1;
    673   1.3       cgd 		if (fp->postfix != ' ')
    674   1.3       cgd 			havepostfix = 1;
    675   1.3       cgd 	}
    676   1.3       cgd 	if (haveprefix)
    677   1.3       cgd 		width++;
    678   1.3       cgd 	if (havepostfix)
    679   1.3       cgd 		width++;
    680   1.3       cgd 	if (vflag) {
    681   1.3       cgd 		for (precision = 0, i = bigino; i > 0; i /= 10)
    682   1.3       cgd 			precision++;
    683   1.3       cgd 		width += precision + 1;
    684   1.1       cgd 	}
    685   1.3       cgd 	width++;
    686   1.3       cgd 	columns = 81 / width;
    687   1.1       cgd 	if (columns == 0)
    688   1.1       cgd 		columns = 1;
    689   1.1       cgd 	lines = (nentry + columns - 1) / columns;
    690   1.1       cgd 	for (i = 0; i < lines; i++) {
    691   1.1       cgd 		for (j = 0; j < columns; j++) {
    692   1.3       cgd 			fp = &list[j * lines + i];
    693   1.3       cgd 			if (vflag) {
    694  1.29  christos 				fprintf(stderr, "%*ju ", precision,
    695  1.29  christos 				    (uintmax_t)fp->fnum);
    696   1.3       cgd 				fp->len += precision + 1;
    697   1.3       cgd 			}
    698   1.3       cgd 			if (haveprefix) {
    699   1.3       cgd 				putc(fp->prefix, stderr);
    700   1.3       cgd 				fp->len++;
    701   1.3       cgd 			}
    702   1.3       cgd 			fprintf(stderr, "%s", fp->fname);
    703   1.3       cgd 			if (havepostfix) {
    704   1.3       cgd 				putc(fp->postfix, stderr);
    705   1.3       cgd 				fp->len++;
    706   1.3       cgd 			}
    707   1.3       cgd 			if (fp + lines >= endlist) {
    708   1.1       cgd 				fprintf(stderr, "\n");
    709   1.1       cgd 				break;
    710   1.1       cgd 			}
    711   1.3       cgd 			for (w = fp->len; w < width; w++)
    712   1.3       cgd 				putc(' ', stderr);
    713   1.1       cgd 		}
    714   1.1       cgd 	}
    715   1.1       cgd }
    716   1.1       cgd 
    717   1.1       cgd /*
    718   1.3       cgd  * Skip over directory entries that are not on the tape
    719   1.3       cgd  *
    720   1.3       cgd  * First have to get definition of a dirent.
    721   1.1       cgd  */
    722   1.3       cgd #undef DIRBLKSIZ
    723   1.3       cgd #include <dirent.h>
    724   1.3       cgd #undef d_ino
    725   1.3       cgd 
    726   1.3       cgd struct dirent *
    727  1.21   xtraeme glob_readdir(RST_DIR *dirp)
    728   1.1       cgd {
    729   1.3       cgd 	struct direct *dp;
    730   1.3       cgd 	static struct dirent adirent;
    731   1.1       cgd 
    732   1.3       cgd 	while ((dp = rst_readdir(dirp)) != NULL) {
    733  1.27  dholland 		if (!vflag && dp->d_fileno == UFS_WINO)
    734   1.3       cgd 			continue;
    735  1.24  christos 		if (dflag || TSTINO(dp->d_fileno, dumpmap))
    736   1.3       cgd 			break;
    737   1.3       cgd 	}
    738   1.3       cgd 	if (dp == NULL)
    739   1.3       cgd 		return (NULL);
    740  1.24  christos 	adirent.d_fileno = dp->d_fileno;
    741   1.3       cgd 	adirent.d_namlen = dp->d_namlen;
    742  1.13     lukem 	memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
    743   1.3       cgd 	return (&adirent);
    744   1.1       cgd }
    745   1.1       cgd 
    746   1.1       cgd /*
    747   1.3       cgd  * Return st_mode information in response to stat or lstat calls
    748   1.1       cgd  */
    749   1.3       cgd static int
    750  1.21   xtraeme glob_stat(const char *name, struct stat *stp)
    751   1.1       cgd {
    752  1.10     lukem 	struct direct *dp;
    753   1.1       cgd 
    754   1.3       cgd 	dp = pathsearch(name);
    755  1.24  christos 	if (dp == NULL || (!dflag && TSTINO(dp->d_fileno, dumpmap) == 0) ||
    756  1.27  dholland 	    (!vflag && dp->d_fileno == UFS_WINO))
    757   1.3       cgd 		return (-1);
    758  1.24  christos 	if (inodetype(dp->d_fileno) == NODE)
    759   1.7   mycroft 		stp->st_mode = S_IFDIR;
    760   1.1       cgd 	else
    761   1.7   mycroft 		stp->st_mode = S_IFREG;
    762   1.3       cgd 	return (0);
    763   1.3       cgd }
    764   1.3       cgd 
    765   1.3       cgd /*
    766   1.3       cgd  * Comparison routine for qsort.
    767   1.3       cgd  */
    768   1.3       cgd static int
    769  1.21   xtraeme fcmp(const void *f1, const void *f2)
    770   1.3       cgd {
    771  1.29  christos 	return (strcoll(((const struct afile *)f1)->fname,
    772  1.22  christos 	    ((const struct afile *)f2)->fname));
    773   1.1       cgd }
    774   1.1       cgd 
    775   1.1       cgd /*
    776   1.1       cgd  * respond to interrupts
    777   1.1       cgd  */
    778   1.1       cgd void
    779  1.25  christos /*ARGSUSED*/
    780  1.25  christos onintr(int signo __unused)
    781   1.1       cgd {
    782   1.3       cgd 	if (command == 'i' && runshell)
    783   1.1       cgd 		longjmp(reset, 1);
    784   1.1       cgd 	if (reply("restore interrupted, continue") == FAIL)
    785   1.8   mycroft 		exit(1);
    786   1.1       cgd }
    787