1*32020Spc /*
2*32020Spc  * Copyright (c) 1985 Regents of the University of California.
3*32020Spc  * All rights reserved.  The Berkeley software License Agreement
4*32020Spc  * specifies the terms and conditions for redistribution.
5*32020Spc  */
6*32020Spc 
7*32020Spc #ifndef lint
8*32020Spc static char sccsid[] = "@(#)interactive.c	5.3 (Berkeley) 7/21/85";
9*32020Spc #endif not lint
10*32020Spc 
11*32020Spc #include "restore.h"
12*32020Spc #include <protocols/dumprestore.h>
13*32020Spc #include <setjmp.h>
14*32020Spc 
15*32020Spc #define round(a, b) (((a) + (b) - 1) / (b) * (b))
16*32020Spc 
17*32020Spc /*
18*32020Spc  * Things to handle interruptions.
19*32020Spc  */
20*32020Spc static jmp_buf reset;
21*32020Spc static char *nextarg = NULL;
22*32020Spc 
23*32020Spc /*
24*32020Spc  * Structure and routines associated with listing directories.
25*32020Spc  */
26*32020Spc struct afile {
27*32020Spc 	ino_t	fnum;		/* inode number of file */
28*32020Spc 	char	*fname;		/* file name */
29*32020Spc 	short	fflags;		/* extraction flags, if any */
30*32020Spc 	char	ftype;		/* file type, e.g. LEAF or NODE */
31*32020Spc };
32*32020Spc struct arglist {
33*32020Spc 	struct afile	*head;	/* start of argument list */
34*32020Spc 	struct afile	*last;	/* end of argument list */
35*32020Spc 	struct afile	*base;	/* current list arena */
36*32020Spc 	int		nent;	/* maximum size of list */
37*32020Spc 	char		*cmd;	/* the current command */
38*32020Spc };
39*32020Spc extern int fcmp();
40*32020Spc extern char *fmtentry();
41*32020Spc char *copynext();
42*32020Spc 
43*32020Spc /*
44*32020Spc  * Read and execute commands from the terminal.
45*32020Spc  */
46*32020Spc runcmdshell()
47*32020Spc {
48*32020Spc 	register struct entry *np;
49*32020Spc 	ino_t ino;
50*32020Spc 	static struct arglist alist = { 0, 0, 0, 0, 0 };
51*32020Spc 	char curdir[MAXPATHLEN];
52*32020Spc 	char name[MAXPATHLEN];
53*32020Spc 	char cmd[BUFSIZ];
54*32020Spc 
55*32020Spc 	canon("/", curdir);
56*32020Spc loop:
57*32020Spc 	if (setjmp(reset) != 0) {
58*32020Spc 		for (; alist.head < alist.last; alist.head++)
59*32020Spc 			freename(alist.head->fname);
60*32020Spc 		nextarg = NULL;
61*32020Spc 		volno = 0;
62*32020Spc 	}
63*32020Spc 	getcmd(curdir, cmd, name, &alist);
64*32020Spc 	switch (cmd[0]) {
65*32020Spc 	/*
66*32020Spc 	 * Add elements to the extraction list.
67*32020Spc 	 */
68*32020Spc 	case 'a':
69*32020Spc 		ino = dirlookup(name);
70*32020Spc 		if (ino == 0)
71*32020Spc 			break;
72*32020Spc 		if (mflag)
73*32020Spc 			pathcheck(name);
74*32020Spc 		treescan(name, ino, addfile);
75*32020Spc 		break;
76*32020Spc 	/*
77*32020Spc 	 * Change working directory.
78*32020Spc 	 */
79*32020Spc 	case 'c':
80*32020Spc 		ino = dirlookup(name);
81*32020Spc 		if (ino == 0)
82*32020Spc 			break;
83*32020Spc 		if (inodetype(ino) == LEAF) {
84*32020Spc 			fprintf(stderr, "%s: not a directory\n", name);
85*32020Spc 			break;
86*32020Spc 		}
87*32020Spc 		(void) strcpy(curdir, name);
88*32020Spc 		break;
89*32020Spc 	/*
90*32020Spc 	 * Delete elements from the extraction list.
91*32020Spc 	 */
92*32020Spc 	case 'd':
93*32020Spc 		np = lookupname(name);
94*32020Spc 		if (np == NIL || (np->e_flags & NEW) == 0) {
95*32020Spc 			fprintf(stderr, "%s: not on extraction list\n", name);
96*32020Spc 			break;
97*32020Spc 		}
98*32020Spc 		treescan(name, np->e_ino, deletefile);
99*32020Spc 		break;
100*32020Spc 	/*
101*32020Spc 	 * Extract the requested list.
102*32020Spc 	 */
103*32020Spc 	case 'e':
104*32020Spc 		createfiles();
105*32020Spc 		createlinks();
106*32020Spc 		setdirmodes();
107*32020Spc 		if (dflag)
108*32020Spc 			checkrestore();
109*32020Spc 		volno = 0;
110*32020Spc 		break;
111*32020Spc 	/*
112*32020Spc 	 * List available commands.
113*32020Spc 	 */
114*32020Spc 	case 'h':
115*32020Spc 	case '?':
116*32020Spc 		fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
117*32020Spc 			"Available commands are:\n",
118*32020Spc 			"\tls [arg] - list directory\n",
119*32020Spc 			"\tcd arg - change directory\n",
120*32020Spc 			"\tpwd - print current directory\n",
121*32020Spc 			"\tadd [arg] - add `arg' to list of",
122*32020Spc 			" files to be extracted\n",
123*32020Spc 			"\tdelete [arg] - delete `arg' from",
124*32020Spc 			" list of files to be extracted\n",
125*32020Spc 			"\textract - extract requested files\n",
126*32020Spc 			"\tsetmodes - set modes of requested directories\n",
127*32020Spc 			"\tquit - immediately exit program\n",
128*32020Spc 			"\tverbose - toggle verbose flag",
129*32020Spc 			" (useful with ``ls'')\n",
130*32020Spc 			"\thelp or `?' - print this list\n",
131*32020Spc 			"If no `arg' is supplied, the current",
132*32020Spc 			" directory is used\n");
133*32020Spc 		break;
134*32020Spc 	/*
135*32020Spc 	 * List a directory.
136*32020Spc 	 */
137*32020Spc 	case 'l':
138*32020Spc 		ino = dirlookup(name);
139*32020Spc 		if (ino == 0)
140*32020Spc 			break;
141*32020Spc 		printlist(name, ino, curdir);
142*32020Spc 		break;
143*32020Spc 	/*
144*32020Spc 	 * Print current directory.
145*32020Spc 	 */
146*32020Spc 	case 'p':
147*32020Spc 		if (curdir[1] == '\0')
148*32020Spc 			fprintf(stderr, "/\n");
149*32020Spc 		else
150*32020Spc 			fprintf(stderr, "%s\n", &curdir[1]);
151*32020Spc 		break;
152*32020Spc 	/*
153*32020Spc 	 * Quit.
154*32020Spc 	 */
155*32020Spc 	case 'q':
156*32020Spc 	case 'x':
157*32020Spc 		return;
158*32020Spc 	/*
159*32020Spc 	 * Toggle verbose mode.
160*32020Spc 	 */
161*32020Spc 	case 'v':
162*32020Spc 		if (vflag) {
163*32020Spc 			fprintf(stderr, "verbose mode off\n");
164*32020Spc 			vflag = 0;
165*32020Spc 			break;
166*32020Spc 		}
167*32020Spc 		fprintf(stderr, "verbose mode on\n");
168*32020Spc 		vflag++;
169*32020Spc 		break;
170*32020Spc 	/*
171*32020Spc 	 * Just restore requested directory modes.
172*32020Spc 	 */
173*32020Spc 	case 's':
174*32020Spc 		setdirmodes();
175*32020Spc 		break;
176*32020Spc 	/*
177*32020Spc 	 * Turn on debugging.
178*32020Spc 	 */
179*32020Spc 	case 'D':
180*32020Spc 		if (dflag) {
181*32020Spc 			fprintf(stderr, "debugging mode off\n");
182*32020Spc 			dflag = 0;
183*32020Spc 			break;
184*32020Spc 		}
185*32020Spc 		fprintf(stderr, "debugging mode on\n");
186*32020Spc 		dflag++;
187*32020Spc 		break;
188*32020Spc 	/*
189*32020Spc 	 * Unknown command.
190*32020Spc 	 */
191*32020Spc 	default:
192*32020Spc 		fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
193*32020Spc 		break;
194*32020Spc 	}
195*32020Spc 	goto loop;
196*32020Spc }
197*32020Spc 
198*32020Spc /*
199*32020Spc  * Read and parse an interactive command.
200*32020Spc  * The first word on the line is assigned to "cmd". If
201*32020Spc  * there are no arguments on the command line, then "curdir"
202*32020Spc  * is returned as the argument. If there are arguments
203*32020Spc  * on the line they are returned one at a time on each
204*32020Spc  * successive call to getcmd. Each argument is first assigned
205*32020Spc  * to "name". If it does not start with "/" the pathname in
206*32020Spc  * "curdir" is prepended to it. Finally "canon" is called to
207*32020Spc  * eliminate any embedded ".." components.
208*32020Spc  */
209*32020Spc getcmd(curdir, cmd, name, ap)
210*32020Spc 	char *curdir, *cmd, *name;
211*32020Spc 	struct arglist *ap;
212*32020Spc {
213*32020Spc 	register char *cp;
214*32020Spc 	static char input[BUFSIZ];
215*32020Spc 	char output[BUFSIZ];
216*32020Spc #	define rawname input	/* save space by reusing input buffer */
217*32020Spc 
218*32020Spc 	/*
219*32020Spc 	 * Check to see if still processing arguments.
220*32020Spc 	 */
221*32020Spc 	if (ap->head != ap->last) {
222*32020Spc 		strcpy(name, ap->head->fname);
223*32020Spc 		freename(ap->head->fname);
224*32020Spc 		ap->head++;
225*32020Spc 		return;
226*32020Spc 	}
227*32020Spc 	if (nextarg != NULL)
228*32020Spc 		goto getnext;
229*32020Spc 	/*
230*32020Spc 	 * Read a command line and trim off trailing white space.
231*32020Spc 	 */
232*32020Spc 	do	{
233*32020Spc 		fprintf(stderr, "restore > ");
234*32020Spc 		(void) fflush(stderr);
235*32020Spc 		(void) fgets(input, BUFSIZ, terminal);
236*32020Spc 	} while (!feof(terminal) && input[0] == '\n');
237*32020Spc 	if (feof(terminal)) {
238*32020Spc 		(void) strcpy(cmd, "quit");
239*32020Spc 		return;
240*32020Spc 	}
241*32020Spc 	for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
242*32020Spc 		/* trim off trailing white space and newline */;
243*32020Spc 	*++cp = '\0';
244*32020Spc 	/*
245*32020Spc 	 * Copy the command into "cmd".
246*32020Spc 	 */
247*32020Spc 	cp = copynext(input, cmd);
248*32020Spc 	ap->cmd = cmd;
249*32020Spc 	/*
250*32020Spc 	 * If no argument, use curdir as the default.
251*32020Spc 	 */
252*32020Spc 	if (*cp == '\0') {
253*32020Spc 		(void) strcpy(name, curdir);
254*32020Spc 		return;
255*32020Spc 	}
256*32020Spc 	nextarg = cp;
257*32020Spc 	/*
258*32020Spc 	 * Find the next argument.
259*32020Spc 	 */
260*32020Spc getnext:
261*32020Spc 	cp = copynext(nextarg, rawname);
262*32020Spc 	if (*cp == '\0')
263*32020Spc 		nextarg = NULL;
264*32020Spc 	else
265*32020Spc 		nextarg = cp;
266*32020Spc 	/*
267*32020Spc 	 * If it an absolute pathname, canonicalize it and return it.
268*32020Spc 	 */
269*32020Spc 	if (rawname[0] == '/') {
270*32020Spc 		canon(rawname, name);
271*32020Spc 	} else {
272*32020Spc 		/*
273*32020Spc 		 * For relative pathnames, prepend the current directory to
274*32020Spc 		 * it then canonicalize and return it.
275*32020Spc 		 */
276*32020Spc 		(void) strcpy(output, curdir);
277*32020Spc 		(void) strcat(output, "/");
278*32020Spc 		(void) strcat(output, rawname);
279*32020Spc 		canon(output, name);
280*32020Spc 	}
281*32020Spc 	expandarg(name, ap);
282*32020Spc 	strcpy(name, ap->head->fname);
283*32020Spc 	freename(ap->head->fname);
284*32020Spc 	ap->head++;
285*32020Spc #	undef rawname
286*32020Spc }
287*32020Spc 
288*32020Spc /*
289*32020Spc  * Strip off the next token of the input.
290*32020Spc  */
291*32020Spc char *
292*32020Spc copynext(input, output)
293*32020Spc 	char *input, *output;
294*32020Spc {
295*32020Spc 	register char *cp, *bp;
296*32020Spc 	char quote;
297*32020Spc 
298*32020Spc 	for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
299*32020Spc 		/* skip to argument */;
300*32020Spc 	bp = output;
301*32020Spc 	while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
302*32020Spc 		/*
303*32020Spc 		 * Handle back slashes.
304*32020Spc 		 */
305*32020Spc 		if (*cp == '\\') {
306*32020Spc 			if (*++cp == '\0') {
307*32020Spc 				fprintf(stderr,
308*32020Spc 					"command lines cannot be continued\n");
309*32020Spc 				continue;
310*32020Spc 			}
311*32020Spc 			*bp++ = *cp++;
312*32020Spc 			continue;
313*32020Spc 		}
314*32020Spc 		/*
315*32020Spc 		 * The usual unquoted case.
316*32020Spc 		 */
317*32020Spc 		if (*cp != '\'' && *cp != '"') {
318*32020Spc 			*bp++ = *cp++;
319*32020Spc 			continue;
320*32020Spc 		}
321*32020Spc 		/*
322*32020Spc 		 * Handle single and double quotes.
323*32020Spc 		 */
324*32020Spc 		quote = *cp++;
325*32020Spc 		while (*cp != quote && *cp != '\0')
326*32020Spc 			*bp++ = *cp++ | 0200;
327*32020Spc 		if (*cp++ == '\0') {
328*32020Spc 			fprintf(stderr, "missing %c\n", quote);
329*32020Spc 			cp--;
330*32020Spc 			continue;
331*32020Spc 		}
332*32020Spc 	}
333*32020Spc 	*bp = '\0';
334*32020Spc 	return (cp);
335*32020Spc }
336*32020Spc 
337*32020Spc /*
338*32020Spc  * Canonicalize file names to always start with ``./'' and
339*32020Spc  * remove any imbedded "." and ".." components.
340*32020Spc  */
341*32020Spc canon(rawname, canonname)
342*32020Spc 	char *rawname, *canonname;
343*32020Spc {
344*32020Spc 	register char *cp, *np;
345*32020Spc 	int len;
346*32020Spc 
347*32020Spc 	if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
348*32020Spc 		(void) strcpy(canonname, "");
349*32020Spc 	else if (rawname[0] == '/')
350*32020Spc 		(void) strcpy(canonname, ".");
351*32020Spc 	else
352*32020Spc 		(void) strcpy(canonname, "./");
353*32020Spc 	(void) strcat(canonname, rawname);
354*32020Spc 	/*
355*32020Spc 	 * Eliminate multiple and trailing '/'s
356*32020Spc 	 */
357*32020Spc 	for (cp = np = canonname; *np != '\0'; cp++) {
358*32020Spc 		*cp = *np++;
359*32020Spc 		while (*cp == '/' && *np == '/')
360*32020Spc 			np++;
361*32020Spc 	}
362*32020Spc 	*cp = '\0';
363*32020Spc 	if (*--cp == '/')
364*32020Spc 		*cp = '\0';
365*32020Spc 	/*
366*32020Spc 	 * Eliminate extraneous "." and ".." from pathnames.
367*32020Spc 	 */
368*32020Spc 	for (np = canonname; *np != '\0'; ) {
369*32020Spc 		np++;
370*32020Spc 		cp = np;
371*32020Spc 		while (*np != '/' && *np != '\0')
372*32020Spc 			np++;
373*32020Spc 		if (np - cp == 1 && *cp == '.') {
374*32020Spc 			cp--;
375*32020Spc 			(void) strcpy(cp, np);
376*32020Spc 			np = cp;
377*32020Spc 		}
378*32020Spc 		if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
379*32020Spc 			cp--;
380*32020Spc 			while (cp > &canonname[1] && *--cp != '/')
381*32020Spc 				/* find beginning of name */;
382*32020Spc 			(void) strcpy(cp, np);
383*32020Spc 			np = cp;
384*32020Spc 		}
385*32020Spc 	}
386*32020Spc }
387*32020Spc 
388*32020Spc /*
389*32020Spc  * globals (file name generation)
390*32020Spc  *
391*32020Spc  * "*" in params matches r.e ".*"
392*32020Spc  * "?" in params matches r.e. "."
393*32020Spc  * "[...]" in params matches character class
394*32020Spc  * "[...a-z...]" in params matches a through z.
395*32020Spc  */
396*32020Spc expandarg(arg, ap)
397*32020Spc 	char *arg;
398*32020Spc 	register struct arglist *ap;
399*32020Spc {
400*32020Spc 	static struct afile single;
401*32020Spc 	int size;
402*32020Spc 
403*32020Spc 	ap->head = ap->last = (struct afile *)0;
404*32020Spc 	size = expand(arg, 0, ap);
405*32020Spc 	if (size == 0) {
406*32020Spc 		single.fnum = lookupname(arg)->e_ino;
407*32020Spc 		single.fname = savename(arg);
408*32020Spc 		ap->head = &single;
409*32020Spc 		ap->last = ap->head + 1;
410*32020Spc 		return;
411*32020Spc 	}
412*32020Spc 	qsort((char *)ap->head, ap->last - ap->head, sizeof *ap->head, fcmp);
413*32020Spc }
414*32020Spc 
415*32020Spc /*
416*32020Spc  * Expand a file name
417*32020Spc  */
418*32020Spc expand(as, rflg, ap)
419*32020Spc 	char *as;
420*32020Spc 	int rflg;
421*32020Spc 	register struct arglist *ap;
422*32020Spc {
423*32020Spc 	int		count, size;
424*32020Spc 	char		dir = 0;
425*32020Spc 	char		*rescan = 0;
426*32020Spc 	DIR		*dirp;
427*32020Spc 	register char	*s, *cs;
428*32020Spc 	int		sindex, rindex, lindex;
429*32020Spc 	struct direct	*dp;
430*32020Spc 	register char	slash;
431*32020Spc 	register char	*rs;
432*32020Spc 	register char	c;
433*32020Spc 
434*32020Spc 	/*
435*32020Spc 	 * check for meta chars
436*32020Spc 	 */
437*32020Spc 	s = cs = as;
438*32020Spc 	slash = 0;
439*32020Spc 	while (*cs != '*' && *cs != '?' && *cs != '[') {
440*32020Spc 		if (*cs++ == 0) {
441*32020Spc 			if (rflg && slash)
442*32020Spc 				break;
443*32020Spc 			else
444*32020Spc 				return (0) ;
445*32020Spc 		} else if (*cs == '/') {
446*32020Spc 			slash++;
447*32020Spc 		}
448*32020Spc 	}
449*32020Spc 	for (;;) {
450*32020Spc 		if (cs == s) {
451*32020Spc 			s = "";
452*32020Spc 			break;
453*32020Spc 		} else if (*--cs == '/') {
454*32020Spc 			*cs = 0;
455*32020Spc 			if (s == cs)
456*32020Spc 				s = "/";
457*32020Spc 			break;
458*32020Spc 		}
459*32020Spc 	}
460*32020Spc 	if ((dirp = rst_opendir(s)) != NULL)
461*32020Spc 		dir++;
462*32020Spc 	count = 0;
463*32020Spc 	if (*cs == 0)
464*32020Spc 		*cs++ = 0200;
465*32020Spc 	if (dir) {
466*32020Spc 		/*
467*32020Spc 		 * check for rescan
468*32020Spc 		 */
469*32020Spc 		rs = cs;
470*32020Spc 		do {
471*32020Spc 			if (*rs == '/') {
472*32020Spc 				rescan = rs;
473*32020Spc 				*rs = 0;
474*32020Spc 			}
475*32020Spc 		} while (*rs++);
476*32020Spc 		sindex = ap->last - ap->head;
477*32020Spc 		while ((dp = rst_readdir(dirp)) != NULL && dp->d_ino != 0) {
478*32020Spc 			if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
479*32020Spc 				continue;
480*32020Spc 			if ((*dp->d_name == '.' && *cs != '.'))
481*32020Spc 				continue;
482*32020Spc 			if (gmatch(dp->d_name, cs)) {
483*32020Spc 				if (addg(dp, s, rescan, ap) < 0)
484*32020Spc 					return (-1);
485*32020Spc 				count++;
486*32020Spc 			}
487*32020Spc 		}
488*32020Spc 		if (rescan) {
489*32020Spc 			rindex = sindex;
490*32020Spc 			lindex = ap->last - ap->head;
491*32020Spc 			if (count) {
492*32020Spc 				count = 0;
493*32020Spc 				while (rindex < lindex) {
494*32020Spc 					size = expand(ap->head[rindex].fname,
495*32020Spc 					    1, ap);
496*32020Spc 					if (size < 0)
497*32020Spc 						return (size);
498*32020Spc 					count += size;
499*32020Spc 					rindex++;
500*32020Spc 				}
501*32020Spc 			}
502*32020Spc 			bcopy((char *)&ap->head[lindex],
503*32020Spc 			     (char *)&ap->head[sindex],
504*32020Spc 			     (ap->last - &ap->head[rindex]) * sizeof *ap->head);
505*32020Spc 			ap->last -= lindex - sindex;
506*32020Spc 			*rescan = '/';
507*32020Spc 		}
508*32020Spc 	}
509*32020Spc 	s = as;
510*32020Spc 	while (c = *s)
511*32020Spc 		*s++ = (c&0177 ? c : '/');
512*32020Spc 	return (count);
513*32020Spc }
514*32020Spc 
515*32020Spc /*
516*32020Spc  * Check for a name match
517*32020Spc  */
518*32020Spc gmatch(s, p)
519*32020Spc 	register char	*s, *p;
520*32020Spc {
521*32020Spc 	register int	scc;
522*32020Spc 	char		c;
523*32020Spc 	char		ok;
524*32020Spc 	int		lc;
525*32020Spc 
526*32020Spc 	if (scc = *s++)
527*32020Spc 		if ((scc &= 0177) == 0)
528*32020Spc 			scc = 0200;
529*32020Spc 	switch (c = *p++) {
530*32020Spc 
531*32020Spc 	case '[':
532*32020Spc 		ok = 0;
533*32020Spc 		lc = 077777;
534*32020Spc 		while (c = *p++) {
535*32020Spc 			if (c == ']') {
536*32020Spc 				return (ok ? gmatch(s, p) : 0);
537*32020Spc 			} else if (c == '-') {
538*32020Spc 				if (lc <= scc && scc <= (*p++))
539*32020Spc 					ok++ ;
540*32020Spc 			} else {
541*32020Spc 				if (scc == (lc = (c&0177)))
542*32020Spc 					ok++ ;
543*32020Spc 			}
544*32020Spc 		}
545*32020Spc 		return (0);
546*32020Spc 
547*32020Spc 	default:
548*32020Spc 		if ((c&0177) != scc)
549*32020Spc 			return (0) ;
550*32020Spc 		/* falls through */
551*32020Spc 
552*32020Spc 	case '?':
553*32020Spc 		return (scc ? gmatch(s, p) : 0);
554*32020Spc 
555*32020Spc 	case '*':
556*32020Spc 		if (*p == 0)
557*32020Spc 			return (1) ;
558*32020Spc 		s--;
559*32020Spc 		while (*s) {
560*32020Spc 			if (gmatch(s++, p))
561*32020Spc 				return (1);
562*32020Spc 		}
563*32020Spc 		return (0);
564*32020Spc 
565*32020Spc 	case 0:
566*32020Spc 		return (scc == 0);
567*32020Spc 	}
568*32020Spc }
569*32020Spc 
570*32020Spc /*
571*32020Spc  * Construct a matched name.
572*32020Spc  */
573*32020Spc addg(dp, as1, as3, ap)
574*32020Spc 	struct direct	*dp;
575*32020Spc 	char		*as1, *as3;
576*32020Spc 	struct arglist	*ap;
577*32020Spc {
578*32020Spc 	register char	*s1, *s2;
579*32020Spc 	register int	c;
580*32020Spc 	char		buf[BUFSIZ];
581*32020Spc 
582*32020Spc 	s2 = buf;
583*32020Spc 	s1 = as1;
584*32020Spc 	while (c = *s1++) {
585*32020Spc 		if ((c &= 0177) == 0) {
586*32020Spc 			*s2++ = '/';
587*32020Spc 			break;
588*32020Spc 		}
589*32020Spc 		*s2++ = c;
590*32020Spc 	}
591*32020Spc 	s1 = dp->d_name;
592*32020Spc 	while (*s2 = *s1++)
593*32020Spc 		s2++;
594*32020Spc 	if (s1 = as3) {
595*32020Spc 		*s2++ = '/';
596*32020Spc 		while (*s2++ = *++s1)
597*32020Spc 			/* void */;
598*32020Spc 	}
599*32020Spc 	if (mkentry(buf, dp->d_ino, ap) == FAIL)
600*32020Spc 		return (-1);
601*32020Spc }
602*32020Spc 
603*32020Spc /*
604*32020Spc  * Do an "ls" style listing of a directory
605*32020Spc  */
606*32020Spc printlist(name, ino, basename)
607*32020Spc 	char *name;
608*32020Spc 	ino_t ino;
609*32020Spc 	char *basename;
610*32020Spc {
611*32020Spc 	register struct afile *fp;
612*32020Spc 	register struct direct *dp;
613*32020Spc 	static struct arglist alist = { 0, 0, 0, 0, "ls" };
614*32020Spc 	struct afile single;
615*32020Spc 	DIR *dirp;
616*32020Spc 
617*32020Spc 	if ((dirp = rst_opendir(name)) == NULL) {
618*32020Spc 		single.fnum = ino;
619*32020Spc 		single.fname = savename(name + strlen(basename) + 1);
620*32020Spc 		alist.head = &single;
621*32020Spc 		alist.last = alist.head + 1;
622*32020Spc 	} else {
623*32020Spc 		alist.head = (struct afile *)0;
624*32020Spc 		fprintf(stderr, "%s:\n", name);
625*32020Spc 		while (dp = rst_readdir(dirp)) {
626*32020Spc 			if (dp == NULL || dp->d_ino == 0)
627*32020Spc 				break;
628*32020Spc 			if (!dflag && BIT(dp->d_ino, dumpmap) == 0)
629*32020Spc 				continue;
630*32020Spc 			if (vflag == 0 &&
631*32020Spc 			    (strcmp(dp->d_name, ".") == 0 ||
632*32020Spc 			     strcmp(dp->d_name, "..") == 0))
633*32020Spc 				continue;
634*32020Spc 			if (!mkentry(dp->d_name, dp->d_ino, &alist))
635*32020Spc 				return;
636*32020Spc 		}
637*32020Spc 	}
638*32020Spc 	if (alist.head != 0) {
639*32020Spc 		qsort((char *)alist.head, alist.last - alist.head,
640*32020Spc 			sizeof *alist.head, fcmp);
641*32020Spc 		formatf(&alist);
642*32020Spc 		for (fp = alist.head; fp < alist.last; fp++)
643*32020Spc 			freename(fp->fname);
644*32020Spc 	}
645*32020Spc 	if (dirp != NULL)
646*32020Spc 		fprintf(stderr, "\n");
647*32020Spc }
648*32020Spc 
649*32020Spc /*
650*32020Spc  * Read the contents of a directory.
651*32020Spc  */
652*32020Spc mkentry(name, ino, ap)
653*32020Spc 	char *name;
654*32020Spc 	ino_t ino;
655*32020Spc 	register struct arglist *ap;
656*32020Spc {
657*32020Spc 	register struct afile *fp;
658*32020Spc 
659*32020Spc 	if (ap->base == NULL) {
660*32020Spc 		ap->nent = 20;
661*32020Spc 		ap->base = (struct afile *)calloc((unsigned)ap->nent,
662*32020Spc 			sizeof (struct afile));
663*32020Spc 		if (ap->base == NULL) {
664*32020Spc 			fprintf(stderr, "%s: out of memory\n", ap->cmd);
665*32020Spc 			return (FAIL);
666*32020Spc 		}
667*32020Spc 	}
668*32020Spc 	if (ap->head == 0)
669*32020Spc 		ap->head = ap->last = ap->base;
670*32020Spc 	fp = ap->last;
671*32020Spc 	fp->fnum = ino;
672*32020Spc 	fp->fname = savename(name);
673*32020Spc 	fp++;
674*32020Spc 	if (fp == ap->head + ap->nent) {
675*32020Spc 		ap->base = (struct afile *)realloc((char *)ap->base,
676*32020Spc 		    (unsigned)(2 * ap->nent * sizeof (struct afile)));
677*32020Spc 		if (ap->base == 0) {
678*32020Spc 			fprintf(stderr, "%s: out of memory\n", ap->cmd);
679*32020Spc 			return (FAIL);
680*32020Spc 		}
681*32020Spc 		ap->head = ap->base;
682*32020Spc 		fp = ap->head + ap->nent;
683*32020Spc 		ap->nent *= 2;
684*32020Spc 	}
685*32020Spc 	ap->last = fp;
686*32020Spc 	return (GOOD);
687*32020Spc }
688*32020Spc 
689*32020Spc /*
690*32020Spc  * Print out a pretty listing of a directory
691*32020Spc  */
692*32020Spc formatf(ap)
693*32020Spc 	register struct arglist *ap;
694*32020Spc {
695*32020Spc 	register struct afile *fp;
696*32020Spc 	struct entry *np;
697*32020Spc 	int width = 0, w, nentry = ap->last - ap->head;
698*32020Spc 	int i, j, len, columns, lines;
699*32020Spc 	char *cp;
700*32020Spc 
701*32020Spc 	if (ap->head == ap->last)
702*32020Spc 		return;
703*32020Spc 	for (fp = ap->head; fp < ap->last; fp++) {
704*32020Spc 		fp->ftype = inodetype(fp->fnum);
705*32020Spc 		np = lookupino(fp->fnum);
706*32020Spc 		if (np != NIL)
707*32020Spc 			fp->fflags = np->e_flags;
708*32020Spc 		else
709*32020Spc 			fp->fflags = 0;
710*32020Spc 		len = strlen(fmtentry(fp));
711*32020Spc 		if (len > width)
712*32020Spc 			width = len;
713*32020Spc 	}
714*32020Spc 	width += 2;
715*32020Spc 	columns = 80 / width;
716*32020Spc 	if (columns == 0)
717*32020Spc 		columns = 1;
718*32020Spc 	lines = (nentry + columns - 1) / columns;
719*32020Spc 	for (i = 0; i < lines; i++) {
720*32020Spc 		for (j = 0; j < columns; j++) {
721*32020Spc 			fp = ap->head + j * lines + i;
722*32020Spc 			cp = fmtentry(fp);
723*32020Spc 			fprintf(stderr, "%s", cp);
724*32020Spc 			if (fp + lines >= ap->last) {
725*32020Spc 				fprintf(stderr, "\n");
726*32020Spc 				break;
727*32020Spc 			}
728*32020Spc 			w = strlen(cp);
729*32020Spc 			while (w < width) {
730*32020Spc 				w++;
731*32020Spc 				fprintf(stderr, " ");
732*32020Spc 			}
733*32020Spc 		}
734*32020Spc 	}
735*32020Spc }
736*32020Spc 
737*32020Spc /*
738*32020Spc  * Comparison routine for qsort.
739*32020Spc  */
740*32020Spc fcmp(f1, f2)
741*32020Spc 	register struct afile *f1, *f2;
742*32020Spc {
743*32020Spc 
744*32020Spc 	return (strcmp(f1->fname, f2->fname));
745*32020Spc }
746*32020Spc 
747*32020Spc /*
748*32020Spc  * Format a directory entry.
749*32020Spc  */
750*32020Spc char *
751*32020Spc fmtentry(fp)
752*32020Spc 	register struct afile *fp;
753*32020Spc {
754*32020Spc 	static char fmtres[BUFSIZ];
755*32020Spc 	static int precision = 0;
756*32020Spc 	int i;
757*32020Spc 	register char *cp, *dp;
758*32020Spc 
759*32020Spc 	if (!vflag) {
760*32020Spc 		fmtres[0] = '\0';
761*32020Spc 	} else {
762*32020Spc 		if (precision == 0)
763*32020Spc 			for (i = maxino; i > 0; i /= 10)
764*32020Spc 				precision++;
765*32020Spc 		(void) sprintf(fmtres, "%*d ", precision, fp->fnum);
766*32020Spc 	}
767*32020Spc 	dp = &fmtres[strlen(fmtres)];
768*32020Spc 	if (dflag && BIT(fp->fnum, dumpmap) == 0)
769*32020Spc 		*dp++ = '^';
770*32020Spc 	else if ((fp->fflags & NEW) != 0)
771*32020Spc 		*dp++ = '*';
772*32020Spc 	else
773*32020Spc 		*dp++ = ' ';
774*32020Spc 	for (cp = fp->fname; *cp; cp++)
775*32020Spc 		if (!vflag && (*cp < ' ' || *cp >= 0177))
776*32020Spc 			*dp++ = '?';
777*32020Spc 		else
778*32020Spc 			*dp++ = *cp;
779*32020Spc 	if (fp->ftype == NODE)
780*32020Spc 		*dp++ = '/';
781*32020Spc 	*dp++ = 0;
782*32020Spc 	return (fmtres);
783*32020Spc }
784*32020Spc 
785*32020Spc /*
786*32020Spc  * respond to interrupts
787*32020Spc  */
788*32020Spc onintr()
789*32020Spc {
790*32020Spc 	if (command == 'i')
791*32020Spc 		longjmp(reset, 1);
792*32020Spc 	if (reply("restore interrupted, continue") == FAIL)
793*32020Spc 		done(1);
794*32020Spc }
795