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