|
|
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.5 (Berkeley) 4/23/87";
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: if (strncmp(cmd, "add", strlen(cmd)) != 0)
70: goto bad;
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':
82: if (strncmp(cmd, "cd", strlen(cmd)) != 0)
83: goto bad;
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':
97: if (strncmp(cmd, "delete", strlen(cmd)) != 0)
98: goto bad;
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':
110: if (strncmp(cmd, "extract", strlen(cmd)) != 0)
111: goto bad;
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':
123: if (strncmp(cmd, "help", strlen(cmd)) != 0)
124: goto bad;
125: case '?':
126: fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
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",
136: "\tsetmodes - set modes of requested directories\n",
137: "\tquit - immediately exit program\n",
138: "\twhat - list dump header information\n",
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':
149: if (strncmp(cmd, "ls", strlen(cmd)) != 0)
150: goto bad;
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':
160: if (strncmp(cmd, "pwd", strlen(cmd)) != 0)
161: goto bad;
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':
171: if (strncmp(cmd, "quit", strlen(cmd)) != 0)
172: goto bad;
173: return;
174: case 'x':
175: if (strncmp(cmd, "xit", strlen(cmd)) != 0)
176: goto bad;
177: return;
178: /*
179: * Toggle verbose mode.
180: */
181: case 'v':
182: if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
183: goto bad;
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: */
195: case 's':
196: if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
197: goto bad;
198: setdirmodes();
199: break;
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;
208: /*
209: * Turn on debugging.
210: */
211: case 'D':
212: if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
213: goto bad;
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:
226: bad:
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: */
244: getcmd(curdir, cmd, name, ap)
245: char *curdir, *cmd, *name;
246: struct arglist *ap;
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: */
256: if (ap->head != ap->last) {
257: strcpy(name, ap->head->fname);
258: freename(ap->head->fname);
259: ap->head++;
260: return;
261: }
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);
283: ap->cmd = cmd;
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: }
316: expandarg(name, ap);
317: strcpy(name, ap->head->fname);
318: freename(ap->head->fname);
319: ap->head++;
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
374: * remove any imbedded "." and ".." components.
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);
389: /*
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.
402: */
403: for (np = canonname; *np != '\0'; ) {
404: np++;
405: cp = np;
406: while (*np != '/' && *np != '\0')
407: np++;
408: if (np - cp == 1 && *cp == '.') {
409: cp--;
410: (void) strcpy(cp, np);
411: np = cp;
412: }
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:
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: */
431: expandarg(arg, ap)
432: char *arg;
433: register struct arglist *ap;
434: {
435: static struct afile single;
436: struct entry *ep;
437: int size;
438:
439: ap->head = ap->last = (struct afile *)0;
440: size = expand(arg, 0, ap);
441: if (size == 0) {
442: ep = lookupname(arg);
443: single.fnum = ep ? ep->e_ino : 0;
444: single.fname = savename(arg);
445: ap->head = &single;
446: ap->last = ap->head + 1;
447: return;
448: }
449: qsort((char *)ap->head, ap->last - ap->head, sizeof *ap->head, fcmp);
450: }
451:
452: /*
453: * Expand a file name
454: */
455: expand(as, rflg, ap)
456: char *as;
457: int rflg;
458: register struct arglist *ap;
459: {
460: int count, size;
461: char dir = 0;
462: char *rescan = 0;
463: DIR *dirp;
464: register char *s, *cs;
465: int sindex, rindex, lindex;
466: struct direct *dp;
467: register char slash;
468: register char *rs;
469: register char c;
470:
471: /*
472: * check for meta chars
473: */
474: s = cs = as;
475: slash = 0;
476: while (*cs != '*' && *cs != '?' && *cs != '[') {
477: if (*cs++ == 0) {
478: if (rflg && slash)
479: break;
480: else
481: return (0) ;
482: } else if (*cs == '/') {
483: slash++;
484: }
485: }
486: for (;;) {
487: if (cs == s) {
488: s = "";
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)
501: *cs++ = 0200;
502: if (dir) {
503: /*
504: * check for rescan
505: */
506: rs = cs;
507: do {
508: if (*rs == '/') {
509: rescan = rs;
510: *rs = 0;
511: }
512: } while (*rs++);
513: sindex = ap->last - ap->head;
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)) {
520: if (addg(dp, s, rescan, ap) < 0)
521: return (-1);
522: count++;
523: }
524: }
525: if (rescan) {
526: rindex = sindex;
527: lindex = ap->last - ap->head;
528: if (count) {
529: count = 0;
530: while (rindex < lindex) {
531: size = expand(ap->head[rindex].fname,
532: 1, ap);
533: if (size < 0)
534: return (size);
535: count += size;
536: rindex++;
537: }
538: }
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;
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++) {
572: if (c == ']') {
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: */
610: addg(dp, as1, as3, ap)
611: struct direct *dp;
612: char *as1, *as3;
613: struct arglist *ap;
614: {
615: register char *s1, *s2;
616: register int c;
617: char buf[BUFSIZ];
618:
619: s2 = buf;
620: s1 = as1;
621: while (c = *s1++) {
622: if ((c &= 0177) == 0) {
623: *s2++ = '/';
624: break;
625: }
626: *s2++ = c;
627: }
628: s1 = dp->d_name;
629: while (*s2 = *s1++)
630: s2++;
631: if (s1 = as3) {
632: *s2++ = '/';
633: while (*s2++ = *++s1)
634: /* void */;
635: }
636: if (mkentry(buf, dp->d_ino, ap) == FAIL)
637: return (-1);
638: }
639:
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;
649: register struct direct *dp;
650: static struct arglist alist = { 0, 0, 0, 0, "ls" };
651: struct afile single;
652: DIR *dirp;
653:
654: if ((dirp = rst_opendir(name)) == NULL) {
655: single.fnum = ino;
656: single.fname = savename(name + strlen(basename) + 1);
657: alist.head = &single;
658: alist.last = alist.head + 1;
659: } else {
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);
681: }
682: if (dirp != NULL)
683: fprintf(stderr, "\n");
684: }
685:
686: /*
687: * Read the contents of a directory.
688: */
689: mkentry(name, ino, ap)
690: char *name;
691: ino_t ino;
692: register struct arglist *ap;
693: {
694: register struct afile *fp;
695:
696: if (ap->base == NULL) {
697: ap->nent = 20;
698: ap->base = (struct afile *)calloc((unsigned)ap->nent,
699: sizeof (struct afile));
700: if (ap->base == NULL) {
701: fprintf(stderr, "%s: out of memory\n", ap->cmd);
702: return (FAIL);
703: }
704: }
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);
717: }
718: ap->head = ap->base;
719: fp = ap->head + ap->nent;
720: ap->nent *= 2;
721: }
722: ap->last = fp;
723: return (GOOD);
724: }
725:
726: /*
727: * Print out a pretty listing of a directory
728: */
729: formatf(ap)
730: register struct arglist *ap;
731: {
732: register struct afile *fp;
733: struct entry *np;
734: int width = 0, w, nentry = ap->last - ap->head;
735: int i, j, len, columns, lines;
736: char *cp;
737:
738: if (ap->head == ap->last)
739: return;
740: for (fp = ap->head; fp < ap->last; fp++) {
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++) {
758: fp = ap->head + j * lines + i;
759: cp = fmtentry(fp);
760: fprintf(stderr, "%s", cp);
761: if (fp + lines >= ap->last) {
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];
792: static int precision = 0;
793: int i;
794: register char *cp, *dp;
795:
796: if (!vflag) {
797: fmtres[0] = '\0';
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: }
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.