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