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