|
|
1.1 root 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: */
6:
7: #ifndef lint
8: static char sccsid[] = "@(#)interactive.c 5.3 (Berkeley) 7/21/85";
9: #endif not lint
10:
11: #include "restore.h"
12: #include <protocols/dumprestore.h>
13: #include <setjmp.h>
14:
15: #define round(a, b) (((a) + (b) - 1) / (b) * (b))
16:
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: };
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: };
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;
50: static struct arglist alist = { 0, 0, 0, 0, 0 };
51: char curdir[MAXPATHLEN];
52: char name[MAXPATHLEN];
53: char cmd[BUFSIZ];
54:
55: canon("/", curdir);
56: loop:
57: if (setjmp(reset) != 0) {
58: for (; alist.head < alist.last; alist.head++)
59: freename(alist.head->fname);
60: nextarg = NULL;
61: volno = 0;
62: }
63: getcmd(curdir, cmd, name, &alist);
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 '?':
116: fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
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",
126: "\tsetmodes - set modes of requested directories\n",
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: */
173: case 's':
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: */
209: getcmd(curdir, cmd, name, ap)
210: char *curdir, *cmd, *name;
211: struct arglist *ap;
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: */
221: if (ap->head != ap->last) {
222: strcpy(name, ap->head->fname);
223: freename(ap->head->fname);
224: ap->head++;
225: return;
226: }
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);
248: ap->cmd = cmd;
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: }
281: expandarg(name, ap);
282: strcpy(name, ap->head->fname);
283: freename(ap->head->fname);
284: ap->head++;
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
339: * remove any imbedded "." and ".." components.
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);
354: /*
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.
367: */
368: for (np = canonname; *np != '\0'; ) {
369: np++;
370: cp = np;
371: while (*np != '/' && *np != '\0')
372: np++;
373: if (np - cp == 1 && *cp == '.') {
374: cp--;
375: (void) strcpy(cp, np);
376: np = cp;
377: }
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:
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: */
396: expandarg(arg, ap)
397: char *arg;
398: register struct arglist *ap;
399: {
400: static struct afile single;
401: int size;
402:
403: ap->head = ap->last = (struct afile *)0;
404: size = expand(arg, 0, ap);
405: if (size == 0) {
406: single.fnum = lookupname(arg)->e_ino;
407: single.fname = savename(arg);
408: ap->head = &single;
409: ap->last = ap->head + 1;
410: return;
411: }
412: qsort((char *)ap->head, ap->last - ap->head, sizeof *ap->head, fcmp);
413: }
414:
415: /*
416: * Expand a file name
417: */
418: expand(as, rflg, ap)
419: char *as;
420: int rflg;
421: register struct arglist *ap;
422: {
423: int count, size;
424: char dir = 0;
425: char *rescan = 0;
426: DIR *dirp;
427: register char *s, *cs;
428: int sindex, rindex, lindex;
429: struct direct *dp;
430: register char slash;
431: register char *rs;
432: register char c;
433:
434: /*
435: * check for meta chars
436: */
437: s = cs = as;
438: slash = 0;
439: while (*cs != '*' && *cs != '?' && *cs != '[') {
440: if (*cs++ == 0) {
441: if (rflg && slash)
442: break;
443: else
444: return (0) ;
445: } else if (*cs == '/') {
446: slash++;
447: }
448: }
449: for (;;) {
450: if (cs == s) {
451: s = "";
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)
464: *cs++ = 0200;
465: if (dir) {
466: /*
467: * check for rescan
468: */
469: rs = cs;
470: do {
471: if (*rs == '/') {
472: rescan = rs;
473: *rs = 0;
474: }
475: } while (*rs++);
476: sindex = ap->last - ap->head;
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)) {
483: if (addg(dp, s, rescan, ap) < 0)
484: return (-1);
485: count++;
486: }
487: }
488: if (rescan) {
489: rindex = sindex;
490: lindex = ap->last - ap->head;
491: if (count) {
492: count = 0;
493: while (rindex < lindex) {
494: size = expand(ap->head[rindex].fname,
495: 1, ap);
496: if (size < 0)
497: return (size);
498: count += size;
499: rindex++;
500: }
501: }
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;
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++) {
535: if (c == ']') {
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: */
573: addg(dp, as1, as3, ap)
574: struct direct *dp;
575: char *as1, *as3;
576: struct arglist *ap;
577: {
578: register char *s1, *s2;
579: register int c;
580: char buf[BUFSIZ];
581:
582: s2 = buf;
583: s1 = as1;
584: while (c = *s1++) {
585: if ((c &= 0177) == 0) {
586: *s2++ = '/';
587: break;
588: }
589: *s2++ = c;
590: }
591: s1 = dp->d_name;
592: while (*s2 = *s1++)
593: s2++;
594: if (s1 = as3) {
595: *s2++ = '/';
596: while (*s2++ = *++s1)
597: /* void */;
598: }
599: if (mkentry(buf, dp->d_ino, ap) == FAIL)
600: return (-1);
601: }
602:
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;
612: register struct direct *dp;
613: static struct arglist alist = { 0, 0, 0, 0, "ls" };
614: struct afile single;
615: DIR *dirp;
616:
617: if ((dirp = rst_opendir(name)) == NULL) {
618: single.fnum = ino;
619: single.fname = savename(name + strlen(basename) + 1);
620: alist.head = &single;
621: alist.last = alist.head + 1;
622: } else {
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);
644: }
645: if (dirp != NULL)
646: fprintf(stderr, "\n");
647: }
648:
649: /*
650: * Read the contents of a directory.
651: */
652: mkentry(name, ino, ap)
653: char *name;
654: ino_t ino;
655: register struct arglist *ap;
656: {
657: register struct afile *fp;
658:
659: if (ap->base == NULL) {
660: ap->nent = 20;
661: ap->base = (struct afile *)calloc((unsigned)ap->nent,
662: sizeof (struct afile));
663: if (ap->base == NULL) {
664: fprintf(stderr, "%s: out of memory\n", ap->cmd);
665: return (FAIL);
666: }
667: }
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);
680: }
681: ap->head = ap->base;
682: fp = ap->head + ap->nent;
683: ap->nent *= 2;
684: }
685: ap->last = fp;
686: return (GOOD);
687: }
688:
689: /*
690: * Print out a pretty listing of a directory
691: */
692: formatf(ap)
693: register struct arglist *ap;
694: {
695: register struct afile *fp;
696: struct entry *np;
697: int width = 0, w, nentry = ap->last - ap->head;
698: int i, j, len, columns, lines;
699: char *cp;
700:
701: if (ap->head == ap->last)
702: return;
703: for (fp = ap->head; fp < ap->last; fp++) {
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++) {
721: fp = ap->head + j * lines + i;
722: cp = fmtentry(fp);
723: fprintf(stderr, "%s", cp);
724: if (fp + lines >= ap->last) {
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: static int precision = 0;
756: int i;
757: register char *cp, *dp;
758:
759: if (!vflag) {
760: fmtres[0] = '\0';
761: } else {
762: if (precision == 0)
763: for (i = maxino; i > 0; i /= 10)
764: precision++;
765: (void) sprintf(fmtres, "%*d ", precision, fp->fnum);
766: }
767: dp = &fmtres[strlen(fmtres)];
768: if (dflag && BIT(fp->fnum, dumpmap) == 0)
769: *dp++ = '^';
770: else if ((fp->fflags & NEW) != 0)
771: *dp++ = '*';
772: else
773: *dp++ = ' ';
774: for (cp = fp->fname; *cp; cp++)
775: if (!vflag && (*cp < ' ' || *cp >= 0177))
776: *dp++ = '?';
777: else
778: *dp++ = *cp;
779: if (fp->ftype == NODE)
780: *dp++ = '/';
781: *dp++ = 0;
782: return (fmtres);
783: }
784:
785: /*
786: * respond to interrupts
787: */
788: onintr()
789: {
790: if (command == 'i')
791: longjmp(reset, 1);
792: if (reply("restore interrupted, continue") == FAIL)
793: done(1);
794: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.