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 | |
17f18765 | 8 | static char sccsid[] = "@(#)interactive.c 5.5 (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; |
17f18765 | 436 | struct entry *ep; |
5bb9ce2d | 437 | int size; |
87276fab KM |
438 | |
439 | ap->head = ap->last = (struct afile *)0; | |
440 | size = expand(arg, 0, ap); | |
5bb9ce2d | 441 | if (size == 0) { |
17f18765 KM |
442 | ep = lookupname(arg); |
443 | single.fnum = ep ? ep->e_ino : 0; | |
87276fab KM |
444 | single.fname = savename(arg); |
445 | ap->head = &single; | |
446 | ap->last = ap->head + 1; | |
447 | return; | |
5bb9ce2d | 448 | } |
87276fab | 449 | qsort((char *)ap->head, ap->last - ap->head, sizeof *ap->head, fcmp); |
5bb9ce2d KM |
450 | } |
451 | ||
452 | /* | |
453 | * Expand a file name | |
454 | */ | |
87276fab | 455 | expand(as, rflg, ap) |
5bb9ce2d KM |
456 | char *as; |
457 | int rflg; | |
87276fab | 458 | register struct arglist *ap; |
5bb9ce2d KM |
459 | { |
460 | int count, size; | |
461 | char dir = 0; | |
462 | char *rescan = 0; | |
463 | DIR *dirp; | |
464 | register char *s, *cs; | |
87276fab | 465 | int sindex, rindex, lindex; |
5bb9ce2d KM |
466 | struct direct *dp; |
467 | register char slash; | |
468 | register char *rs; | |
5bb9ce2d KM |
469 | register char c; |
470 | ||
471 | /* | |
472 | * check for meta chars | |
473 | */ | |
474 | s = cs = as; | |
475 | slash = 0; | |
476 | while (*cs != '*' && *cs != '?' && *cs != '[') { | |
87276fab | 477 | if (*cs++ == 0) { |
5bb9ce2d KM |
478 | if (rflg && slash) |
479 | break; | |
480 | else | |
481 | return (0) ; | |
87276fab | 482 | } else if (*cs == '/') { |
5bb9ce2d KM |
483 | slash++; |
484 | } | |
485 | } | |
486 | for (;;) { | |
487 | if (cs == s) { | |
87276fab | 488 | s = ""; |
5bb9ce2d KM |
489 | break; |
490 | } else if (*--cs == '/') { | |
491 | *cs = 0; | |
492 | if (s == cs) | |
493 | s = "/"; | |
494 | break; | |
495 | } | |
496 | } | |
497 | if ((dirp = rst_opendir(s)) != NULL) | |
498 | dir++; | |
499 | count = 0; | |
500 | if (*cs == 0) | |
87276fab | 501 | *cs++ = 0200; |
5bb9ce2d KM |
502 | if (dir) { |
503 | /* | |
504 | * check for rescan | |
505 | */ | |
506 | rs = cs; | |
507 | do { | |
508 | if (*rs == '/') { | |
509 | rescan = rs; | |
510 | *rs = 0; | |
5bb9ce2d KM |
511 | } |
512 | } while (*rs++); | |
87276fab | 513 | sindex = ap->last - ap->head; |
5bb9ce2d KM |
514 | while ((dp = rst_readdir(dirp)) != NULL && dp->d_ino != 0) { |
515 | if (!dflag && BIT(dp->d_ino, dumpmap) == 0) | |
516 | continue; | |
517 | if ((*dp->d_name == '.' && *cs != '.')) | |
518 | continue; | |
519 | if (gmatch(dp->d_name, cs)) { | |
87276fab | 520 | if (addg(dp, s, rescan, ap) < 0) |
5bb9ce2d KM |
521 | return (-1); |
522 | count++; | |
523 | } | |
524 | } | |
525 | if (rescan) { | |
87276fab KM |
526 | rindex = sindex; |
527 | lindex = ap->last - ap->head; | |
5bb9ce2d KM |
528 | if (count) { |
529 | count = 0; | |
87276fab KM |
530 | while (rindex < lindex) { |
531 | size = expand(ap->head[rindex].fname, | |
532 | 1, ap); | |
5bb9ce2d KM |
533 | if (size < 0) |
534 | return (size); | |
535 | count += size; | |
87276fab | 536 | rindex++; |
5bb9ce2d KM |
537 | } |
538 | } | |
87276fab KM |
539 | bcopy((char *)&ap->head[lindex], |
540 | (char *)&ap->head[sindex], | |
541 | (ap->last - &ap->head[rindex]) * sizeof *ap->head); | |
542 | ap->last -= lindex - sindex; | |
5bb9ce2d KM |
543 | *rescan = '/'; |
544 | } | |
545 | } | |
546 | s = as; | |
547 | while (c = *s) | |
548 | *s++ = (c&0177 ? c : '/'); | |
549 | return (count); | |
550 | } | |
551 | ||
552 | /* | |
553 | * Check for a name match | |
554 | */ | |
555 | gmatch(s, p) | |
556 | register char *s, *p; | |
557 | { | |
558 | register int scc; | |
559 | char c; | |
560 | char ok; | |
561 | int lc; | |
562 | ||
563 | if (scc = *s++) | |
564 | if ((scc &= 0177) == 0) | |
565 | scc = 0200; | |
566 | switch (c = *p++) { | |
567 | ||
568 | case '[': | |
569 | ok = 0; | |
570 | lc = 077777; | |
571 | while (c = *p++) { | |
87276fab | 572 | if (c == ']') { |
5bb9ce2d KM |
573 | return (ok ? gmatch(s, p) : 0); |
574 | } else if (c == '-') { | |
575 | if (lc <= scc && scc <= (*p++)) | |
576 | ok++ ; | |
577 | } else { | |
578 | if (scc == (lc = (c&0177))) | |
579 | ok++ ; | |
580 | } | |
581 | } | |
582 | return (0); | |
583 | ||
584 | default: | |
585 | if ((c&0177) != scc) | |
586 | return (0) ; | |
587 | /* falls through */ | |
588 | ||
589 | case '?': | |
590 | return (scc ? gmatch(s, p) : 0); | |
591 | ||
592 | case '*': | |
593 | if (*p == 0) | |
594 | return (1) ; | |
595 | s--; | |
596 | while (*s) { | |
597 | if (gmatch(s++, p)) | |
598 | return (1); | |
599 | } | |
600 | return (0); | |
601 | ||
602 | case 0: | |
603 | return (scc == 0); | |
604 | } | |
605 | } | |
606 | ||
607 | /* | |
608 | * Construct a matched name. | |
609 | */ | |
87276fab KM |
610 | addg(dp, as1, as3, ap) |
611 | struct direct *dp; | |
612 | char *as1, *as3; | |
613 | struct arglist *ap; | |
5bb9ce2d KM |
614 | { |
615 | register char *s1, *s2; | |
616 | register int c; | |
87276fab | 617 | char buf[BUFSIZ]; |
5bb9ce2d | 618 | |
87276fab | 619 | s2 = buf; |
5bb9ce2d KM |
620 | s1 = as1; |
621 | while (c = *s1++) { | |
622 | if ((c &= 0177) == 0) { | |
87276fab | 623 | *s2++ = '/'; |
5bb9ce2d KM |
624 | break; |
625 | } | |
626 | *s2++ = c; | |
627 | } | |
87276fab | 628 | s1 = dp->d_name; |
5bb9ce2d KM |
629 | while (*s2 = *s1++) |
630 | s2++; | |
631 | if (s1 = as3) { | |
632 | *s2++ = '/'; | |
633 | while (*s2++ = *++s1) | |
634 | /* void */; | |
635 | } | |
87276fab KM |
636 | if (mkentry(buf, dp->d_ino, ap) == FAIL) |
637 | return (-1); | |
5bb9ce2d KM |
638 | } |
639 | ||
a15cd7fc KM |
640 | /* |
641 | * Do an "ls" style listing of a directory | |
642 | */ | |
643 | printlist(name, ino, basename) | |
644 | char *name; | |
645 | ino_t ino; | |
646 | char *basename; | |
647 | { | |
648 | register struct afile *fp; | |
87276fab KM |
649 | register struct direct *dp; |
650 | static struct arglist alist = { 0, 0, 0, 0, "ls" }; | |
a15cd7fc KM |
651 | struct afile single; |
652 | DIR *dirp; | |
653 | ||
654 | if ((dirp = rst_opendir(name)) == NULL) { | |
655 | single.fnum = ino; | |
87276fab KM |
656 | single.fname = savename(name + strlen(basename) + 1); |
657 | alist.head = &single; | |
658 | alist.last = alist.head + 1; | |
a15cd7fc | 659 | } else { |
87276fab KM |
660 | alist.head = (struct afile *)0; |
661 | fprintf(stderr, "%s:\n", name); | |
662 | while (dp = rst_readdir(dirp)) { | |
663 | if (dp == NULL || dp->d_ino == 0) | |
664 | break; | |
665 | if (!dflag && BIT(dp->d_ino, dumpmap) == 0) | |
666 | continue; | |
667 | if (vflag == 0 && | |
668 | (strcmp(dp->d_name, ".") == 0 || | |
669 | strcmp(dp->d_name, "..") == 0)) | |
670 | continue; | |
671 | if (!mkentry(dp->d_name, dp->d_ino, &alist)) | |
672 | return; | |
673 | } | |
674 | } | |
675 | if (alist.head != 0) { | |
676 | qsort((char *)alist.head, alist.last - alist.head, | |
677 | sizeof *alist.head, fcmp); | |
678 | formatf(&alist); | |
679 | for (fp = alist.head; fp < alist.last; fp++) | |
680 | freename(fp->fname); | |
a15cd7fc | 681 | } |
87276fab KM |
682 | if (dirp != NULL) |
683 | fprintf(stderr, "\n"); | |
a15cd7fc KM |
684 | } |
685 | ||
686 | /* | |
687 | * Read the contents of a directory. | |
688 | */ | |
87276fab KM |
689 | mkentry(name, ino, ap) |
690 | char *name; | |
691 | ino_t ino; | |
692 | register struct arglist *ap; | |
a15cd7fc KM |
693 | { |
694 | register struct afile *fp; | |
a15cd7fc | 695 | |
87276fab KM |
696 | if (ap->base == NULL) { |
697 | ap->nent = 20; | |
698 | ap->base = (struct afile *)calloc((unsigned)ap->nent, | |
a15cd7fc | 699 | sizeof (struct afile)); |
87276fab KM |
700 | if (ap->base == NULL) { |
701 | fprintf(stderr, "%s: out of memory\n", ap->cmd); | |
a15cd7fc KM |
702 | return (FAIL); |
703 | } | |
704 | } | |
87276fab KM |
705 | if (ap->head == 0) |
706 | ap->head = ap->last = ap->base; | |
707 | fp = ap->last; | |
708 | fp->fnum = ino; | |
709 | fp->fname = savename(name); | |
710 | fp++; | |
711 | if (fp == ap->head + ap->nent) { | |
712 | ap->base = (struct afile *)realloc((char *)ap->base, | |
713 | (unsigned)(2 * ap->nent * sizeof (struct afile))); | |
714 | if (ap->base == 0) { | |
715 | fprintf(stderr, "%s: out of memory\n", ap->cmd); | |
716 | return (FAIL); | |
a15cd7fc | 717 | } |
87276fab KM |
718 | ap->head = ap->base; |
719 | fp = ap->head + ap->nent; | |
720 | ap->nent *= 2; | |
a15cd7fc | 721 | } |
87276fab | 722 | ap->last = fp; |
a15cd7fc KM |
723 | return (GOOD); |
724 | } | |
725 | ||
726 | /* | |
727 | * Print out a pretty listing of a directory | |
728 | */ | |
87276fab KM |
729 | formatf(ap) |
730 | register struct arglist *ap; | |
a15cd7fc KM |
731 | { |
732 | register struct afile *fp; | |
733 | struct entry *np; | |
87276fab | 734 | int width = 0, w, nentry = ap->last - ap->head; |
a15cd7fc KM |
735 | int i, j, len, columns, lines; |
736 | char *cp; | |
737 | ||
87276fab | 738 | if (ap->head == ap->last) |
a15cd7fc | 739 | return; |
87276fab | 740 | for (fp = ap->head; fp < ap->last; fp++) { |
a15cd7fc KM |
741 | fp->ftype = inodetype(fp->fnum); |
742 | np = lookupino(fp->fnum); | |
743 | if (np != NIL) | |
744 | fp->fflags = np->e_flags; | |
745 | else | |
746 | fp->fflags = 0; | |
747 | len = strlen(fmtentry(fp)); | |
748 | if (len > width) | |
749 | width = len; | |
750 | } | |
751 | width += 2; | |
752 | columns = 80 / width; | |
753 | if (columns == 0) | |
754 | columns = 1; | |
755 | lines = (nentry + columns - 1) / columns; | |
756 | for (i = 0; i < lines; i++) { | |
757 | for (j = 0; j < columns; j++) { | |
87276fab | 758 | fp = ap->head + j * lines + i; |
a15cd7fc KM |
759 | cp = fmtentry(fp); |
760 | fprintf(stderr, "%s", cp); | |
87276fab | 761 | if (fp + lines >= ap->last) { |
a15cd7fc KM |
762 | fprintf(stderr, "\n"); |
763 | break; | |
764 | } | |
765 | w = strlen(cp); | |
766 | while (w < width) { | |
767 | w++; | |
768 | fprintf(stderr, " "); | |
769 | } | |
770 | } | |
771 | } | |
772 | } | |
773 | ||
774 | /* | |
775 | * Comparison routine for qsort. | |
776 | */ | |
777 | fcmp(f1, f2) | |
778 | register struct afile *f1, *f2; | |
779 | { | |
780 | ||
781 | return (strcmp(f1->fname, f2->fname)); | |
782 | } | |
783 | ||
784 | /* | |
785 | * Format a directory entry. | |
786 | */ | |
787 | char * | |
788 | fmtentry(fp) | |
789 | register struct afile *fp; | |
790 | { | |
791 | static char fmtres[BUFSIZ]; | |
935b6ff8 KM |
792 | static int precision = 0; |
793 | int i; | |
a15cd7fc KM |
794 | register char *cp, *dp; |
795 | ||
935b6ff8 | 796 | if (!vflag) { |
a15cd7fc | 797 | fmtres[0] = '\0'; |
935b6ff8 KM |
798 | } else { |
799 | if (precision == 0) | |
800 | for (i = maxino; i > 0; i /= 10) | |
801 | precision++; | |
802 | (void) sprintf(fmtres, "%*d ", precision, fp->fnum); | |
803 | } | |
a15cd7fc KM |
804 | dp = &fmtres[strlen(fmtres)]; |
805 | if (dflag && BIT(fp->fnum, dumpmap) == 0) | |
806 | *dp++ = '^'; | |
807 | else if ((fp->fflags & NEW) != 0) | |
808 | *dp++ = '*'; | |
809 | else | |
810 | *dp++ = ' '; | |
811 | for (cp = fp->fname; *cp; cp++) | |
812 | if (!vflag && (*cp < ' ' || *cp >= 0177)) | |
813 | *dp++ = '?'; | |
814 | else | |
815 | *dp++ = *cp; | |
816 | if (fp->ftype == NODE) | |
817 | *dp++ = '/'; | |
818 | *dp++ = 0; | |
819 | return (fmtres); | |
820 | } | |
821 | ||
822 | /* | |
823 | * respond to interrupts | |
824 | */ | |
825 | onintr() | |
826 | { | |
827 | if (command == 'i') | |
828 | longjmp(reset, 1); | |
829 | if (reply("restore interrupted, continue") == FAIL) | |
830 | done(1); | |
831 | } |