xref: /original-bsd/sbin/restore/interactive.c (revision 7123cb22)
11f5353c0Sdist /*
25f8b5756Sbostic  * Copyright (c) 1985, 1993
35f8b5756Sbostic  *	The Regents of the University of California.  All rights reserved.
4179758f1Sbostic  *
560fe7ee1Sbostic  * %sccs.include.redist.c%
61f5353c0Sdist  */
7ae24bf55Smckusick 
8ae24bf55Smckusick #ifndef lint
9*7123cb22Smckusick static char sccsid[] = "@(#)interactive.c	8.5 (Berkeley) 05/01/95";
10179758f1Sbostic #endif /* not lint */
11ae24bf55Smckusick 
12e786d8ceSbostic #include <sys/param.h>
13e786d8ceSbostic #include <sys/time.h>
14caf145b9Smckusick #include <sys/stat.h>
15e786d8ceSbostic 
16e786d8ceSbostic #include <ufs/ufs/dinode.h>
179d95d65aSbostic #include <ufs/ufs/dir.h>
18*7123cb22Smckusick #include <ufs/ffs/fs.h>
19e786d8ceSbostic #include <protocols/dumprestore.h>
20e786d8ceSbostic 
21e786d8ceSbostic #include <setjmp.h>
22caf145b9Smckusick #include <glob.h>
23e786d8ceSbostic #include <stdio.h>
24e786d8ceSbostic #include <stdlib.h>
25e786d8ceSbostic #include <string.h>
26e786d8ceSbostic 
27e786d8ceSbostic #include "restore.h"
28e786d8ceSbostic #include "extern.h"
29ae24bf55Smckusick 
3004e80f63Smckusick #define round(a, b) (((a) + (b) - 1) / (b) * (b))
3104e80f63Smckusick 
32ae24bf55Smckusick /*
33ae24bf55Smckusick  * Things to handle interruptions.
34ae24bf55Smckusick  */
357e238082Smckusick static int runshell;
36ae24bf55Smckusick static jmp_buf reset;
37ae24bf55Smckusick static char *nextarg = NULL;
38ae24bf55Smckusick 
39ae24bf55Smckusick /*
40ae24bf55Smckusick  * Structure and routines associated with listing directories.
41ae24bf55Smckusick  */
42ae24bf55Smckusick struct afile {
43ae24bf55Smckusick 	ino_t	fnum;		/* inode number of file */
44ae24bf55Smckusick 	char	*fname;		/* file name */
45caf145b9Smckusick 	short	len;		/* name length */
46caf145b9Smckusick 	char	prefix;		/* prefix character */
47caf145b9Smckusick 	char	postfix;	/* postfix character */
48ae24bf55Smckusick };
493cb3d055Smckusick struct arglist {
50caf145b9Smckusick 	int	freeglob;	/* glob structure needs to be freed */
51caf145b9Smckusick 	int	argcnt;		/* next globbed argument to return */
52caf145b9Smckusick 	glob_t	glob;		/* globbing information */
533cb3d055Smckusick 	char	*cmd;		/* the current command */
543cb3d055Smckusick };
55e786d8ceSbostic 
56e786d8ceSbostic static char	*copynext __P((char *, char *));
57e786d8ceSbostic static int	 fcmp __P((const void *, const void *));
58caf145b9Smckusick static void	 formatf __P((struct afile *, int));
59e786d8ceSbostic static void	 getcmd __P((char *, char *, char *, struct arglist *));
60caf145b9Smckusick struct dirent	*glob_readdir __P((RST_DIR *dirp));
61323cff99Sbostic static int	 glob_stat __P((const char *, struct stat *));
6263d9c022Smckusick static void	 mkentry __P((char *, struct direct *, struct afile *));
63caf145b9Smckusick static void	 printlist __P((char *, char *));
64ae24bf55Smckusick 
65ae24bf55Smckusick /*
66ae24bf55Smckusick  * Read and execute commands from the terminal.
67ae24bf55Smckusick  */
68e786d8ceSbostic void
runcmdshell()69ae24bf55Smckusick runcmdshell()
70ae24bf55Smckusick {
71ae24bf55Smckusick 	register struct entry *np;
72ae24bf55Smckusick 	ino_t ino;
73caf145b9Smckusick 	struct arglist arglist;
74ae24bf55Smckusick 	char curdir[MAXPATHLEN];
75ae24bf55Smckusick 	char name[MAXPATHLEN];
76ae24bf55Smckusick 	char cmd[BUFSIZ];
77ae24bf55Smckusick 
78caf145b9Smckusick 	arglist.freeglob = 0;
79caf145b9Smckusick 	arglist.argcnt = 0;
80caf145b9Smckusick 	arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
81caf145b9Smckusick 	arglist.glob.gl_opendir = (void *)rst_opendir;
82caf145b9Smckusick 	arglist.glob.gl_readdir = (void *)glob_readdir;
831559c816Sbostic 	arglist.glob.gl_closedir = (void *)rst_closedir;
84caf145b9Smckusick 	arglist.glob.gl_lstat = glob_stat;
85caf145b9Smckusick 	arglist.glob.gl_stat = glob_stat;
86ae24bf55Smckusick 	canon("/", curdir);
87ae24bf55Smckusick loop:
88ae24bf55Smckusick 	if (setjmp(reset) != 0) {
89caf145b9Smckusick 		if (arglist.freeglob != 0) {
90caf145b9Smckusick 			arglist.freeglob = 0;
91caf145b9Smckusick 			arglist.argcnt = 0;
92caf145b9Smckusick 			globfree(&arglist.glob);
93caf145b9Smckusick 		}
94ae24bf55Smckusick 		nextarg = NULL;
95ae24bf55Smckusick 		volno = 0;
96ae24bf55Smckusick 	}
977e238082Smckusick 	runshell = 1;
98caf145b9Smckusick 	getcmd(curdir, cmd, name, &arglist);
99ae24bf55Smckusick 	switch (cmd[0]) {
100ae24bf55Smckusick 	/*
101ae24bf55Smckusick 	 * Add elements to the extraction list.
102ae24bf55Smckusick 	 */
103ae24bf55Smckusick 	case 'a':
1041e25ff23Smckusick 		if (strncmp(cmd, "add", strlen(cmd)) != 0)
1051e25ff23Smckusick 			goto bad;
106ae24bf55Smckusick 		ino = dirlookup(name);
107ae24bf55Smckusick 		if (ino == 0)
108ae24bf55Smckusick 			break;
109ae24bf55Smckusick 		if (mflag)
110ae24bf55Smckusick 			pathcheck(name);
111ae24bf55Smckusick 		treescan(name, ino, addfile);
112ae24bf55Smckusick 		break;
113ae24bf55Smckusick 	/*
114ae24bf55Smckusick 	 * Change working directory.
115ae24bf55Smckusick 	 */
116ae24bf55Smckusick 	case 'c':
1171e25ff23Smckusick 		if (strncmp(cmd, "cd", strlen(cmd)) != 0)
1181e25ff23Smckusick 			goto bad;
119ae24bf55Smckusick 		ino = dirlookup(name);
120ae24bf55Smckusick 		if (ino == 0)
121ae24bf55Smckusick 			break;
122ae24bf55Smckusick 		if (inodetype(ino) == LEAF) {
123ae24bf55Smckusick 			fprintf(stderr, "%s: not a directory\n", name);
124ae24bf55Smckusick 			break;
125ae24bf55Smckusick 		}
126ae24bf55Smckusick 		(void) strcpy(curdir, name);
127ae24bf55Smckusick 		break;
128ae24bf55Smckusick 	/*
129ae24bf55Smckusick 	 * Delete elements from the extraction list.
130ae24bf55Smckusick 	 */
131ae24bf55Smckusick 	case 'd':
1321e25ff23Smckusick 		if (strncmp(cmd, "delete", strlen(cmd)) != 0)
1331e25ff23Smckusick 			goto bad;
134ae24bf55Smckusick 		np = lookupname(name);
135e786d8ceSbostic 		if (np == NULL || (np->e_flags & NEW) == 0) {
136ae24bf55Smckusick 			fprintf(stderr, "%s: not on extraction list\n", name);
137ae24bf55Smckusick 			break;
138ae24bf55Smckusick 		}
139ae24bf55Smckusick 		treescan(name, np->e_ino, deletefile);
140ae24bf55Smckusick 		break;
141ae24bf55Smckusick 	/*
142ae24bf55Smckusick 	 * Extract the requested list.
143ae24bf55Smckusick 	 */
144ae24bf55Smckusick 	case 'e':
1451e25ff23Smckusick 		if (strncmp(cmd, "extract", strlen(cmd)) != 0)
1461e25ff23Smckusick 			goto bad;
147ae24bf55Smckusick 		createfiles();
148ae24bf55Smckusick 		createlinks();
1498ef64fc9Smckusick 		setdirmodes(0);
150ae24bf55Smckusick 		if (dflag)
151ae24bf55Smckusick 			checkrestore();
152ae24bf55Smckusick 		volno = 0;
153ae24bf55Smckusick 		break;
154ae24bf55Smckusick 	/*
155ae24bf55Smckusick 	 * List available commands.
156ae24bf55Smckusick 	 */
157ae24bf55Smckusick 	case 'h':
1581e25ff23Smckusick 		if (strncmp(cmd, "help", strlen(cmd)) != 0)
1591e25ff23Smckusick 			goto bad;
160ae24bf55Smckusick 	case '?':
1611e25ff23Smckusick 		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
162ae24bf55Smckusick 			"Available commands are:\n",
163ae24bf55Smckusick 			"\tls [arg] - list directory\n",
164ae24bf55Smckusick 			"\tcd arg - change directory\n",
165ae24bf55Smckusick 			"\tpwd - print current directory\n",
166ae24bf55Smckusick 			"\tadd [arg] - add `arg' to list of",
167ae24bf55Smckusick 			" files to be extracted\n",
168ae24bf55Smckusick 			"\tdelete [arg] - delete `arg' from",
169ae24bf55Smckusick 			" list of files to be extracted\n",
170ae24bf55Smckusick 			"\textract - extract requested files\n",
17126ed90a3Smckusick 			"\tsetmodes - set modes of requested directories\n",
172ae24bf55Smckusick 			"\tquit - immediately exit program\n",
1731e25ff23Smckusick 			"\twhat - list dump header information\n",
174ae24bf55Smckusick 			"\tverbose - toggle verbose flag",
175ae24bf55Smckusick 			" (useful with ``ls'')\n",
176ae24bf55Smckusick 			"\thelp or `?' - print this list\n",
177ae24bf55Smckusick 			"If no `arg' is supplied, the current",
178ae24bf55Smckusick 			" directory is used\n");
179ae24bf55Smckusick 		break;
180ae24bf55Smckusick 	/*
181ae24bf55Smckusick 	 * List a directory.
182ae24bf55Smckusick 	 */
183ae24bf55Smckusick 	case 'l':
1841e25ff23Smckusick 		if (strncmp(cmd, "ls", strlen(cmd)) != 0)
1851e25ff23Smckusick 			goto bad;
186caf145b9Smckusick 		printlist(name, curdir);
187ae24bf55Smckusick 		break;
188ae24bf55Smckusick 	/*
189ae24bf55Smckusick 	 * Print current directory.
190ae24bf55Smckusick 	 */
191ae24bf55Smckusick 	case 'p':
1921e25ff23Smckusick 		if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
1931e25ff23Smckusick 			goto bad;
194ae24bf55Smckusick 		if (curdir[1] == '\0')
195ae24bf55Smckusick 			fprintf(stderr, "/\n");
196ae24bf55Smckusick 		else
197ae24bf55Smckusick 			fprintf(stderr, "%s\n", &curdir[1]);
198ae24bf55Smckusick 		break;
199ae24bf55Smckusick 	/*
200ae24bf55Smckusick 	 * Quit.
201ae24bf55Smckusick 	 */
202ae24bf55Smckusick 	case 'q':
2031e25ff23Smckusick 		if (strncmp(cmd, "quit", strlen(cmd)) != 0)
2041e25ff23Smckusick 			goto bad;
2051e25ff23Smckusick 		return;
206ae24bf55Smckusick 	case 'x':
2071e25ff23Smckusick 		if (strncmp(cmd, "xit", strlen(cmd)) != 0)
2081e25ff23Smckusick 			goto bad;
209ae24bf55Smckusick 		return;
210ae24bf55Smckusick 	/*
211ae24bf55Smckusick 	 * Toggle verbose mode.
212ae24bf55Smckusick 	 */
213ae24bf55Smckusick 	case 'v':
2141e25ff23Smckusick 		if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
2151e25ff23Smckusick 			goto bad;
216ae24bf55Smckusick 		if (vflag) {
217ae24bf55Smckusick 			fprintf(stderr, "verbose mode off\n");
218ae24bf55Smckusick 			vflag = 0;
219ae24bf55Smckusick 			break;
220ae24bf55Smckusick 		}
221ae24bf55Smckusick 		fprintf(stderr, "verbose mode on\n");
222ae24bf55Smckusick 		vflag++;
223ae24bf55Smckusick 		break;
224ae24bf55Smckusick 	/*
225ae24bf55Smckusick 	 * Just restore requested directory modes.
226ae24bf55Smckusick 	 */
22726ed90a3Smckusick 	case 's':
2281e25ff23Smckusick 		if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
2291e25ff23Smckusick 			goto bad;
2308ef64fc9Smckusick 		setdirmodes(FORCE);
231ae24bf55Smckusick 		break;
232ae24bf55Smckusick 	/*
2331e25ff23Smckusick 	 * Print out dump header information.
2341e25ff23Smckusick 	 */
2351e25ff23Smckusick 	case 'w':
2361e25ff23Smckusick 		if (strncmp(cmd, "what", strlen(cmd)) != 0)
2371e25ff23Smckusick 			goto bad;
2381e25ff23Smckusick 		printdumpinfo();
2391e25ff23Smckusick 		break;
2401e25ff23Smckusick 	/*
241ae24bf55Smckusick 	 * Turn on debugging.
242ae24bf55Smckusick 	 */
243ae24bf55Smckusick 	case 'D':
2441e25ff23Smckusick 		if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
2451e25ff23Smckusick 			goto bad;
246ae24bf55Smckusick 		if (dflag) {
247ae24bf55Smckusick 			fprintf(stderr, "debugging mode off\n");
248ae24bf55Smckusick 			dflag = 0;
249ae24bf55Smckusick 			break;
250ae24bf55Smckusick 		}
251ae24bf55Smckusick 		fprintf(stderr, "debugging mode on\n");
252ae24bf55Smckusick 		dflag++;
253ae24bf55Smckusick 		break;
254ae24bf55Smckusick 	/*
255ae24bf55Smckusick 	 * Unknown command.
256ae24bf55Smckusick 	 */
257ae24bf55Smckusick 	default:
2581e25ff23Smckusick 	bad:
259ae24bf55Smckusick 		fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
260ae24bf55Smckusick 		break;
261ae24bf55Smckusick 	}
262ae24bf55Smckusick 	goto loop;
263ae24bf55Smckusick }
264ae24bf55Smckusick 
265ae24bf55Smckusick /*
266ae24bf55Smckusick  * Read and parse an interactive command.
267ae24bf55Smckusick  * The first word on the line is assigned to "cmd". If
268ae24bf55Smckusick  * there are no arguments on the command line, then "curdir"
269ae24bf55Smckusick  * is returned as the argument. If there are arguments
270ae24bf55Smckusick  * on the line they are returned one at a time on each
271ae24bf55Smckusick  * successive call to getcmd. Each argument is first assigned
272ae24bf55Smckusick  * to "name". If it does not start with "/" the pathname in
273ae24bf55Smckusick  * "curdir" is prepended to it. Finally "canon" is called to
274ae24bf55Smckusick  * eliminate any embedded ".." components.
275ae24bf55Smckusick  */
276e786d8ceSbostic static void
getcmd(curdir,cmd,name,ap)2773cb3d055Smckusick getcmd(curdir, cmd, name, ap)
278ae24bf55Smckusick 	char *curdir, *cmd, *name;
2793cb3d055Smckusick 	struct arglist *ap;
280ae24bf55Smckusick {
281ae24bf55Smckusick 	register char *cp;
282ae24bf55Smckusick 	static char input[BUFSIZ];
283ae24bf55Smckusick 	char output[BUFSIZ];
284ae24bf55Smckusick #	define rawname input	/* save space by reusing input buffer */
285ae24bf55Smckusick 
286ae24bf55Smckusick 	/*
287ae24bf55Smckusick 	 * Check to see if still processing arguments.
288ae24bf55Smckusick 	 */
289caf145b9Smckusick 	if (ap->argcnt > 0)
290caf145b9Smckusick 		goto retnext;
291ae24bf55Smckusick 	if (nextarg != NULL)
292ae24bf55Smckusick 		goto getnext;
293ae24bf55Smckusick 	/*
294ae24bf55Smckusick 	 * Read a command line and trim off trailing white space.
295ae24bf55Smckusick 	 */
296ae24bf55Smckusick 	do	{
297ae24bf55Smckusick 		fprintf(stderr, "restore > ");
298ae24bf55Smckusick 		(void) fflush(stderr);
299ae24bf55Smckusick 		(void) fgets(input, BUFSIZ, terminal);
300ae24bf55Smckusick 	} while (!feof(terminal) && input[0] == '\n');
301ae24bf55Smckusick 	if (feof(terminal)) {
302ae24bf55Smckusick 		(void) strcpy(cmd, "quit");
303ae24bf55Smckusick 		return;
304ae24bf55Smckusick 	}
305ae24bf55Smckusick 	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
306ae24bf55Smckusick 		/* trim off trailing white space and newline */;
307ae24bf55Smckusick 	*++cp = '\0';
308ae24bf55Smckusick 	/*
309ae24bf55Smckusick 	 * Copy the command into "cmd".
310ae24bf55Smckusick 	 */
311ae24bf55Smckusick 	cp = copynext(input, cmd);
3123cb3d055Smckusick 	ap->cmd = cmd;
313ae24bf55Smckusick 	/*
314ae24bf55Smckusick 	 * If no argument, use curdir as the default.
315ae24bf55Smckusick 	 */
316ae24bf55Smckusick 	if (*cp == '\0') {
317ae24bf55Smckusick 		(void) strcpy(name, curdir);
318ae24bf55Smckusick 		return;
319ae24bf55Smckusick 	}
320ae24bf55Smckusick 	nextarg = cp;
321ae24bf55Smckusick 	/*
322ae24bf55Smckusick 	 * Find the next argument.
323ae24bf55Smckusick 	 */
324ae24bf55Smckusick getnext:
325ae24bf55Smckusick 	cp = copynext(nextarg, rawname);
326ae24bf55Smckusick 	if (*cp == '\0')
327ae24bf55Smckusick 		nextarg = NULL;
328ae24bf55Smckusick 	else
329ae24bf55Smckusick 		nextarg = cp;
330ae24bf55Smckusick 	/*
331caf145b9Smckusick 	 * If it is an absolute pathname, canonicalize it and return it.
332ae24bf55Smckusick 	 */
333ae24bf55Smckusick 	if (rawname[0] == '/') {
334ae24bf55Smckusick 		canon(rawname, name);
335ae24bf55Smckusick 	} else {
336ae24bf55Smckusick 		/*
337ae24bf55Smckusick 		 * For relative pathnames, prepend the current directory to
338ae24bf55Smckusick 		 * it then canonicalize and return it.
339ae24bf55Smckusick 		 */
340ae24bf55Smckusick 		(void) strcpy(output, curdir);
341ae24bf55Smckusick 		(void) strcat(output, "/");
342ae24bf55Smckusick 		(void) strcat(output, rawname);
343ae24bf55Smckusick 		canon(output, name);
344ae24bf55Smckusick 	}
345caf145b9Smckusick 	if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0)
346caf145b9Smckusick 		fprintf(stderr, "%s: out of memory\n", ap->cmd);
347caf145b9Smckusick 	if (ap->glob.gl_pathc == 0)
348caf145b9Smckusick 		return;
349caf145b9Smckusick 	ap->freeglob = 1;
350caf145b9Smckusick 	ap->argcnt = ap->glob.gl_pathc;
351caf145b9Smckusick 
352caf145b9Smckusick retnext:
353caf145b9Smckusick 	strcpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt]);
354caf145b9Smckusick 	if (--ap->argcnt == 0) {
355caf145b9Smckusick 		ap->freeglob = 0;
356caf145b9Smckusick 		globfree(&ap->glob);
357caf145b9Smckusick 	}
358ae24bf55Smckusick #	undef rawname
359ae24bf55Smckusick }
360ae24bf55Smckusick 
361ae24bf55Smckusick /*
362ae24bf55Smckusick  * Strip off the next token of the input.
363ae24bf55Smckusick  */
364e786d8ceSbostic static char *
copynext(input,output)365ae24bf55Smckusick copynext(input, output)
366ae24bf55Smckusick 	char *input, *output;
367ae24bf55Smckusick {
368ae24bf55Smckusick 	register char *cp, *bp;
369ae24bf55Smckusick 	char quote;
370ae24bf55Smckusick 
371ae24bf55Smckusick 	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
372ae24bf55Smckusick 		/* skip to argument */;
373ae24bf55Smckusick 	bp = output;
374ae24bf55Smckusick 	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
375ae24bf55Smckusick 		/*
376ae24bf55Smckusick 		 * Handle back slashes.
377ae24bf55Smckusick 		 */
378ae24bf55Smckusick 		if (*cp == '\\') {
379ae24bf55Smckusick 			if (*++cp == '\0') {
380ae24bf55Smckusick 				fprintf(stderr,
381ae24bf55Smckusick 					"command lines cannot be continued\n");
382ae24bf55Smckusick 				continue;
383ae24bf55Smckusick 			}
384ae24bf55Smckusick 			*bp++ = *cp++;
385ae24bf55Smckusick 			continue;
386ae24bf55Smckusick 		}
387ae24bf55Smckusick 		/*
388ae24bf55Smckusick 		 * The usual unquoted case.
389ae24bf55Smckusick 		 */
390ae24bf55Smckusick 		if (*cp != '\'' && *cp != '"') {
391ae24bf55Smckusick 			*bp++ = *cp++;
392ae24bf55Smckusick 			continue;
393ae24bf55Smckusick 		}
394ae24bf55Smckusick 		/*
395ae24bf55Smckusick 		 * Handle single and double quotes.
396ae24bf55Smckusick 		 */
397ae24bf55Smckusick 		quote = *cp++;
398ae24bf55Smckusick 		while (*cp != quote && *cp != '\0')
399ae24bf55Smckusick 			*bp++ = *cp++ | 0200;
400ae24bf55Smckusick 		if (*cp++ == '\0') {
401ae24bf55Smckusick 			fprintf(stderr, "missing %c\n", quote);
402ae24bf55Smckusick 			cp--;
403ae24bf55Smckusick 			continue;
404ae24bf55Smckusick 		}
405ae24bf55Smckusick 	}
406ae24bf55Smckusick 	*bp = '\0';
407ae24bf55Smckusick 	return (cp);
408ae24bf55Smckusick }
409ae24bf55Smckusick 
410ae24bf55Smckusick /*
411ae24bf55Smckusick  * Canonicalize file names to always start with ``./'' and
4123cb3d055Smckusick  * remove any imbedded "." and ".." components.
413ae24bf55Smckusick  */
414e786d8ceSbostic void
canon(rawname,canonname)415ae24bf55Smckusick canon(rawname, canonname)
416ae24bf55Smckusick 	char *rawname, *canonname;
417ae24bf55Smckusick {
418ae24bf55Smckusick 	register char *cp, *np;
419ae24bf55Smckusick 
420ae24bf55Smckusick 	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
421ae24bf55Smckusick 		(void) strcpy(canonname, "");
422ae24bf55Smckusick 	else if (rawname[0] == '/')
423ae24bf55Smckusick 		(void) strcpy(canonname, ".");
424ae24bf55Smckusick 	else
425ae24bf55Smckusick 		(void) strcpy(canonname, "./");
426ae24bf55Smckusick 	(void) strcat(canonname, rawname);
427ae24bf55Smckusick 	/*
4283cb3d055Smckusick 	 * Eliminate multiple and trailing '/'s
4293cb3d055Smckusick 	 */
4303cb3d055Smckusick 	for (cp = np = canonname; *np != '\0'; cp++) {
4313cb3d055Smckusick 		*cp = *np++;
4323cb3d055Smckusick 		while (*cp == '/' && *np == '/')
4333cb3d055Smckusick 			np++;
4343cb3d055Smckusick 	}
4353cb3d055Smckusick 	*cp = '\0';
4363cb3d055Smckusick 	if (*--cp == '/')
4373cb3d055Smckusick 		*cp = '\0';
4383cb3d055Smckusick 	/*
4393cb3d055Smckusick 	 * Eliminate extraneous "." and ".." from pathnames.
440ae24bf55Smckusick 	 */
441ae24bf55Smckusick 	for (np = canonname; *np != '\0'; ) {
442ae24bf55Smckusick 		np++;
443ae24bf55Smckusick 		cp = np;
444ae24bf55Smckusick 		while (*np != '/' && *np != '\0')
445ae24bf55Smckusick 			np++;
4463cb3d055Smckusick 		if (np - cp == 1 && *cp == '.') {
4473cb3d055Smckusick 			cp--;
4483cb3d055Smckusick 			(void) strcpy(cp, np);
4493cb3d055Smckusick 			np = cp;
4503cb3d055Smckusick 		}
451ae24bf55Smckusick 		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
452ae24bf55Smckusick 			cp--;
453ae24bf55Smckusick 			while (cp > &canonname[1] && *--cp != '/')
454ae24bf55Smckusick 				/* find beginning of name */;
455ae24bf55Smckusick 			(void) strcpy(cp, np);
456ae24bf55Smckusick 			np = cp;
457ae24bf55Smckusick 		}
458ae24bf55Smckusick 	}
459ae24bf55Smckusick }
460ae24bf55Smckusick 
461ae24bf55Smckusick /*
462ae24bf55Smckusick  * Do an "ls" style listing of a directory
463ae24bf55Smckusick  */
464e786d8ceSbostic static void
printlist(name,basename)465caf145b9Smckusick printlist(name, basename)
466ae24bf55Smckusick 	char *name;
467ae24bf55Smckusick 	char *basename;
468ae24bf55Smckusick {
469caf145b9Smckusick 	register struct afile *fp, *list, *listp;
4703cb3d055Smckusick 	register struct direct *dp;
471ae24bf55Smckusick 	struct afile single;
472ea490c3cSmckusick 	RST_DIR *dirp;
47363d9c022Smckusick 	int entries, len, namelen;
47463d9c022Smckusick 	char locname[MAXPATHLEN + 1];
475ae24bf55Smckusick 
476caf145b9Smckusick 	dp = pathsearch(name);
47763d9c022Smckusick 	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
47863d9c022Smckusick 	    (!vflag && dp->d_ino == WINO))
479caf145b9Smckusick 		return;
480ae24bf55Smckusick 	if ((dirp = rst_opendir(name)) == NULL) {
481caf145b9Smckusick 		entries = 1;
482caf145b9Smckusick 		list = &single;
48363d9c022Smckusick 		mkentry(name, dp, list);
484caf145b9Smckusick 		len = strlen(basename) + 1;
485caf145b9Smckusick 		if (strlen(name) - len > single.len) {
486caf145b9Smckusick 			freename(single.fname);
487caf145b9Smckusick 			single.fname = savename(&name[len]);
488caf145b9Smckusick 			single.len = strlen(single.fname);
489caf145b9Smckusick 		}
490ae24bf55Smckusick 	} else {
491caf145b9Smckusick 		entries = 0;
492caf145b9Smckusick 		while (dp = rst_readdir(dirp))
493caf145b9Smckusick 			entries++;
494caf145b9Smckusick 		rst_closedir(dirp);
495caf145b9Smckusick 		list = (struct afile *)malloc(entries * sizeof(struct afile));
496caf145b9Smckusick 		if (list == NULL) {
497caf145b9Smckusick 			fprintf(stderr, "ls: out of memory\n");
498caf145b9Smckusick 			return;
499caf145b9Smckusick 		}
500caf145b9Smckusick 		if ((dirp = rst_opendir(name)) == NULL)
501caf145b9Smckusick 			panic("directory reopen failed\n");
5023cb3d055Smckusick 		fprintf(stderr, "%s:\n", name);
503caf145b9Smckusick 		entries = 0;
504caf145b9Smckusick 		listp = list;
50563d9c022Smckusick 		(void) strncpy(locname, name, MAXPATHLEN);
50663d9c022Smckusick 		(void) strncat(locname, "/", MAXPATHLEN);
50763d9c022Smckusick 		namelen = strlen(locname);
508ae24bf55Smckusick 		while (dp = rst_readdir(dirp)) {
50963d9c022Smckusick 			if (dp == NULL)
510ae24bf55Smckusick 				break;
5115232aad9Smckusick 			if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
512ae24bf55Smckusick 				continue;
51363d9c022Smckusick 			if (!vflag && (dp->d_ino == WINO ||
51463d9c022Smckusick 			     strcmp(dp->d_name, ".") == 0 ||
515ae24bf55Smckusick 			     strcmp(dp->d_name, "..") == 0))
516ae24bf55Smckusick 				continue;
51763d9c022Smckusick 			locname[namelen] = '\0';
51863d9c022Smckusick 			if (namelen + dp->d_namlen >= MAXPATHLEN) {
51963d9c022Smckusick 				fprintf(stderr, "%s%s: name exceeds %d char\n",
52063d9c022Smckusick 					locname, dp->d_name, MAXPATHLEN);
52163d9c022Smckusick 			} else {
52263d9c022Smckusick 				(void) strncat(locname, dp->d_name,
52363d9c022Smckusick 				    (int)dp->d_namlen);
52463d9c022Smckusick 				mkentry(locname, dp, listp++);
525caf145b9Smckusick 				entries++;
526caf145b9Smckusick 			}
52763d9c022Smckusick 		}
528caf145b9Smckusick 		rst_closedir(dirp);
529caf145b9Smckusick 		if (entries == 0) {
530caf145b9Smckusick 			fprintf(stderr, "\n");
531caf145b9Smckusick 			free(list);
5323cb3d055Smckusick 			return;
5333cb3d055Smckusick 		}
534caf145b9Smckusick 		qsort((char *)list, entries, sizeof(struct afile), fcmp);
5353cb3d055Smckusick 	}
536caf145b9Smckusick 	formatf(list, entries);
537caf145b9Smckusick 	if (dirp != NULL) {
538caf145b9Smckusick 		for (fp = listp - 1; fp >= list; fp--)
5393cb3d055Smckusick 			freename(fp->fname);
5403cb3d055Smckusick 		fprintf(stderr, "\n");
541caf145b9Smckusick 		free(list);
542caf145b9Smckusick 	}
5433cb3d055Smckusick }
5443cb3d055Smckusick 
5453cb3d055Smckusick /*
5463cb3d055Smckusick  * Read the contents of a directory.
5473cb3d055Smckusick  */
548caf145b9Smckusick static void
mkentry(name,dp,fp)54963d9c022Smckusick mkentry(name, dp, fp)
55063d9c022Smckusick 	char *name;
5515f3e5212Smckusick 	struct direct *dp;
5523cb3d055Smckusick 	register struct afile *fp;
553caf145b9Smckusick {
554caf145b9Smckusick 	char *cp;
555caf145b9Smckusick 	struct entry *np;
5563cb3d055Smckusick 
5575f3e5212Smckusick 	fp->fnum = dp->d_ino;
558caf145b9Smckusick 	fp->fname = savename(dp->d_name);
559caf145b9Smckusick 	for (cp = fp->fname; *cp; cp++)
560caf145b9Smckusick 		if (!vflag && (*cp < ' ' || *cp >= 0177))
561caf145b9Smckusick 			*cp = '?';
562caf145b9Smckusick 	fp->len = cp - fp->fname;
563caf145b9Smckusick 	if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
564caf145b9Smckusick 		fp->prefix = '^';
56563d9c022Smckusick 	else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
566caf145b9Smckusick 		fp->prefix = '*';
5675f3e5212Smckusick 	else
568caf145b9Smckusick 		fp->prefix = ' ';
569caf145b9Smckusick 	switch(dp->d_type) {
570caf145b9Smckusick 
571caf145b9Smckusick 	default:
572caf145b9Smckusick 		fprintf(stderr, "Warning: undefined file type %d\n",
573caf145b9Smckusick 		    dp->d_type);
574caf145b9Smckusick 		/* fall through */
575caf145b9Smckusick 	case DT_REG:
576caf145b9Smckusick 		fp->postfix = ' ';
577caf145b9Smckusick 		break;
578caf145b9Smckusick 
579caf145b9Smckusick 	case DT_LNK:
580caf145b9Smckusick 		fp->postfix = '@';
581caf145b9Smckusick 		break;
582caf145b9Smckusick 
583caf145b9Smckusick 	case DT_FIFO:
584caf145b9Smckusick 	case DT_SOCK:
585caf145b9Smckusick 		fp->postfix = '=';
586caf145b9Smckusick 		break;
587caf145b9Smckusick 
588caf145b9Smckusick 	case DT_CHR:
589caf145b9Smckusick 	case DT_BLK:
590caf145b9Smckusick 		fp->postfix = '#';
591caf145b9Smckusick 		break;
592caf145b9Smckusick 
59319b02dc7Smckusick 	case DT_WHT:
59419b02dc7Smckusick 		fp->postfix = '%';
59519b02dc7Smckusick 		break;
59619b02dc7Smckusick 
597caf145b9Smckusick 	case DT_UNKNOWN:
598caf145b9Smckusick 	case DT_DIR:
599caf145b9Smckusick 		if (inodetype(dp->d_ino) == NODE)
600caf145b9Smckusick 			fp->postfix = '/';
601caf145b9Smckusick 		else
602caf145b9Smckusick 			fp->postfix = ' ';
603caf145b9Smckusick 		break;
604ae24bf55Smckusick 	}
605caf145b9Smckusick 	return;
606ae24bf55Smckusick }
607ae24bf55Smckusick 
608ae24bf55Smckusick /*
609ae24bf55Smckusick  * Print out a pretty listing of a directory
610ae24bf55Smckusick  */
611e786d8ceSbostic static void
formatf(list,nentry)612caf145b9Smckusick formatf(list, nentry)
613caf145b9Smckusick 	register struct afile *list;
614caf145b9Smckusick 	int nentry;
615ae24bf55Smckusick {
616caf145b9Smckusick 	register struct afile *fp, *endlist;
617caf145b9Smckusick 	int width, bigino, haveprefix, havepostfix;
618caf145b9Smckusick 	int i, j, w, precision, columns, lines;
619ae24bf55Smckusick 
620caf145b9Smckusick 	width = 0;
621caf145b9Smckusick 	haveprefix = 0;
622caf145b9Smckusick 	havepostfix = 0;
623caf145b9Smckusick 	bigino = ROOTINO;
624caf145b9Smckusick 	endlist = &list[nentry];
625caf145b9Smckusick 	for (fp = &list[0]; fp < endlist; fp++) {
626caf145b9Smckusick 		if (bigino < fp->fnum)
627caf145b9Smckusick 			bigino = fp->fnum;
628caf145b9Smckusick 		if (width < fp->len)
629caf145b9Smckusick 			width = fp->len;
630caf145b9Smckusick 		if (fp->prefix != ' ')
631caf145b9Smckusick 			haveprefix = 1;
632caf145b9Smckusick 		if (fp->postfix != ' ')
633caf145b9Smckusick 			havepostfix = 1;
634ae24bf55Smckusick 	}
635caf145b9Smckusick 	if (haveprefix)
636caf145b9Smckusick 		width++;
637caf145b9Smckusick 	if (havepostfix)
638caf145b9Smckusick 		width++;
639caf145b9Smckusick 	if (vflag) {
640caf145b9Smckusick 		for (precision = 0, i = bigino; i > 0; i /= 10)
641caf145b9Smckusick 			precision++;
642caf145b9Smckusick 		width += precision + 1;
643caf145b9Smckusick 	}
644caf145b9Smckusick 	width++;
645caf145b9Smckusick 	columns = 81 / width;
646ae24bf55Smckusick 	if (columns == 0)
647ae24bf55Smckusick 		columns = 1;
648ae24bf55Smckusick 	lines = (nentry + columns - 1) / columns;
649ae24bf55Smckusick 	for (i = 0; i < lines; i++) {
650ae24bf55Smckusick 		for (j = 0; j < columns; j++) {
651caf145b9Smckusick 			fp = &list[j * lines + i];
652caf145b9Smckusick 			if (vflag) {
653caf145b9Smckusick 				fprintf(stderr, "%*d ", precision, fp->fnum);
654caf145b9Smckusick 				fp->len += precision + 1;
655caf145b9Smckusick 			}
656caf145b9Smckusick 			if (haveprefix) {
657caf145b9Smckusick 				putc(fp->prefix, stderr);
658caf145b9Smckusick 				fp->len++;
659caf145b9Smckusick 			}
660caf145b9Smckusick 			fprintf(stderr, "%s", fp->fname);
661caf145b9Smckusick 			if (havepostfix) {
662caf145b9Smckusick 				putc(fp->postfix, stderr);
663caf145b9Smckusick 				fp->len++;
664caf145b9Smckusick 			}
665caf145b9Smckusick 			if (fp + lines >= endlist) {
666ae24bf55Smckusick 				fprintf(stderr, "\n");
667ae24bf55Smckusick 				break;
668ae24bf55Smckusick 			}
669caf145b9Smckusick 			for (w = fp->len; w < width; w++)
670caf145b9Smckusick 				putc(' ', stderr);
671ae24bf55Smckusick 		}
672ae24bf55Smckusick 	}
673ae24bf55Smckusick }
674caf145b9Smckusick 
675caf145b9Smckusick /*
676caf145b9Smckusick  * Skip over directory entries that are not on the tape
677caf145b9Smckusick  *
678caf145b9Smckusick  * First have to get definition of a dirent.
679caf145b9Smckusick  */
680caf145b9Smckusick #undef DIRBLKSIZ
681caf145b9Smckusick #include <dirent.h>
682caf145b9Smckusick #undef d_ino
683caf145b9Smckusick 
684caf145b9Smckusick struct dirent *
glob_readdir(dirp)685caf145b9Smckusick glob_readdir(dirp)
686caf145b9Smckusick 	RST_DIR *dirp;
687caf145b9Smckusick {
688caf145b9Smckusick 	struct direct *dp;
689caf145b9Smckusick 	static struct dirent adirent;
690caf145b9Smckusick 
691caf145b9Smckusick 	while ((dp = rst_readdir(dirp)) != NULL) {
69219b02dc7Smckusick 		if (!vflag && dp->d_ino == WINO)
693caf145b9Smckusick 			continue;
694caf145b9Smckusick 		if (dflag || TSTINO(dp->d_ino, dumpmap))
695caf145b9Smckusick 			break;
696caf145b9Smckusick 	}
697caf145b9Smckusick 	if (dp == NULL)
698caf145b9Smckusick 		return (NULL);
699caf145b9Smckusick 	adirent.d_fileno = dp->d_ino;
700caf145b9Smckusick 	adirent.d_namlen = dp->d_namlen;
701608bde00Sbostic 	memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
702caf145b9Smckusick 	return (&adirent);
703caf145b9Smckusick }
704caf145b9Smckusick 
705caf145b9Smckusick /*
706caf145b9Smckusick  * Return st_mode information in response to stat or lstat calls
707caf145b9Smckusick  */
708caf145b9Smckusick static int
glob_stat(name,stp)709caf145b9Smckusick glob_stat(name, stp)
710323cff99Sbostic 	const char *name;
711caf145b9Smckusick 	struct stat *stp;
712caf145b9Smckusick {
713caf145b9Smckusick 	register struct direct *dp;
714caf145b9Smckusick 
715caf145b9Smckusick 	dp = pathsearch(name);
71619b02dc7Smckusick 	if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
71719b02dc7Smckusick 	    (!vflag && dp->d_ino == WINO))
718caf145b9Smckusick 		return (-1);
719caf145b9Smckusick 	if (inodetype(dp->d_ino) == NODE)
720caf145b9Smckusick 		stp->st_mode = IFDIR;
721caf145b9Smckusick 	else
722caf145b9Smckusick 		stp->st_mode = IFREG;
723caf145b9Smckusick 	return (0);
724ae24bf55Smckusick }
725ae24bf55Smckusick 
726ae24bf55Smckusick /*
727ae24bf55Smckusick  * Comparison routine for qsort.
728ae24bf55Smckusick  */
729e786d8ceSbostic static int
fcmp(f1,f2)730ae24bf55Smckusick fcmp(f1, f2)
731e786d8ceSbostic 	register const void *f1, *f2;
732ae24bf55Smckusick {
733e786d8ceSbostic 	return (strcmp(((struct afile *)f1)->fname,
734e786d8ceSbostic 	    ((struct afile *)f2)->fname));
735ae24bf55Smckusick }
736ae24bf55Smckusick 
737ae24bf55Smckusick /*
738ae24bf55Smckusick  * respond to interrupts
739ae24bf55Smckusick  */
740317354a2Sbostic void
onintr(signo)741e786d8ceSbostic onintr(signo)
742e786d8ceSbostic 	int signo;
743ae24bf55Smckusick {
7447e238082Smckusick 	if (command == 'i' && runshell)
745ae24bf55Smckusick 		longjmp(reset, 1);
746ae24bf55Smckusick 	if (reply("restore interrupted, continue") == FAIL)
747ae24bf55Smckusick 		done(1);
748ae24bf55Smckusick }
749