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