]> git.wh0rd.org - dump.git/blob - restore/interactive.c
Copyright notice for Alcôve, for paying me to work on dump...
[dump.git] / restore / interactive.c
1 /*
2 * Ported to Linux's Second Extended File System as part of the
3 * dump and restore backup suit
4 * Remy Card <card@Linux.EU.Org>, 1994-1997
5 * Stelian Pop <pop@cybercable.fr>, 1999-2000
6 * Stelian Pop <pop@cybercable.fr> - Alcôve <www.alcove.fr>, 2000
7 */
8
9 /*
10 * Copyright (c) 1985, 1993
11 * The Regents of the University of California. All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 */
41
42 #ifndef lint
43 static const char rcsid[] =
44 "$Id: interactive.c,v 1.13 2000/11/10 14:42:25 stelian Exp $";
45 #endif /* not lint */
46
47 #include <sys/param.h>
48 #include <sys/stat.h>
49
50 #ifdef __linux__
51 #include <linux/ext2_fs.h>
52 #include <bsdcompat.h>
53 #else /* __linux__ */
54 #include <ufs/ufs/dinode.h>
55 #include <ufs/ufs/dir.h>
56 #endif /* __linux__ */
57 #include <protocols/dumprestore.h>
58
59 #include <setjmp.h>
60 #include <compaterr.h>
61 #include <errno.h>
62 #include <compatglob.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66
67 #ifdef __linux__
68 #include <ext2fs/ext2fs.h>
69 extern char * __progname;
70 #endif
71
72 #include "restore.h"
73 #include "extern.h"
74
75 #if HAVE_READLINE
76 #include <readline/readline.h>
77 #include <readline/history.h>
78
79 static char *rl_gets (char *prompt);
80 static void initialize_readline(void);
81 static char **restore_completion (char *text, int start, int end);
82 static char *command_generator(char *text, int state);
83 static char *filename_generator(char *text, int state);
84 #endif
85
86 #define round(a, b) (((a) + (b) - 1) / (b) * (b))
87
88 /*
89 * Things to handle interruptions.
90 */
91 static int runshell;
92 static jmp_buf reset;
93 static char *nextarg = NULL;
94 static int pflag = 0; /* prompt mode */
95 /*
96 * Structure and routines associated with listing directories.
97 */
98 struct afile {
99 ino_t fnum; /* inode number of file */
100 char *fname; /* file name */
101 short len; /* name length */
102 char prefix; /* prefix character */
103 char postfix; /* postfix character */
104 };
105 struct arglist {
106 int freeglob; /* glob structure needs to be freed */
107 int argcnt; /* next globbed argument to return */
108 glob_t glob; /* globbing information */
109 char *cmd; /* the current command */
110 };
111
112 static char *copynext __P((char *, char *));
113 static int fcmp __P((const void *, const void *));
114 static void formatf __P((struct afile *, int));
115 static void getcmd __P((char *, char *, char *, int, struct arglist *));
116 struct dirent *glob_readdir __P((RST_DIR *dirp));
117 static int glob_stat __P((const char *, struct stat *));
118 static void mkentry __P((char *, struct direct *, struct afile *));
119 static void printlist __P((char *, char *));
120
121 /*
122 * Read and execute commands from the terminal.
123 */
124 void
125 runcmdshell(void)
126 {
127 register struct entry *np;
128 ino_t ino;
129 struct arglist arglist;
130 char curdir[MAXPATHLEN];
131 char name[MAXPATHLEN];
132 char cmd[BUFSIZ];
133
134 #if HAVE_READLINE
135 initialize_readline();
136 #endif
137 arglist.freeglob = 0;
138 arglist.argcnt = 0;
139 arglist.glob.gl_flags = GLOB_ALTDIRFUNC;
140 arglist.glob.gl_opendir = (void *)rst_opendir;
141 arglist.glob.gl_readdir = (void *)glob_readdir;
142 arglist.glob.gl_closedir = (void *)rst_closedir;
143 arglist.glob.gl_lstat = glob_stat;
144 arglist.glob.gl_stat = glob_stat;
145 canon("/", curdir, sizeof(curdir));
146 loop:
147 if (setjmp(reset) != 0) {
148 if (arglist.freeglob != 0) {
149 arglist.freeglob = 0;
150 arglist.argcnt = 0;
151 globfree(&arglist.glob);
152 }
153 nextarg = NULL;
154 volno = 0;
155 }
156 runshell = 1;
157 getcmd(curdir, cmd, name, sizeof(name), &arglist);
158 switch (cmd[0]) {
159 /*
160 * Add elements to the extraction list.
161 */
162 case 'a':
163 if (strncmp(cmd, "add", strlen(cmd)) != 0)
164 goto bad;
165 ino = dirlookup(name);
166 if (ino == 0)
167 break;
168 if (mflag)
169 pathcheck(name);
170 treescan(name, ino, addfile);
171 break;
172 /*
173 * Change working directory.
174 */
175 case 'c':
176 if (strncmp(cmd, "cd", strlen(cmd)) != 0)
177 goto bad;
178 ino = dirlookup(name);
179 if (ino == 0)
180 break;
181 if (inodetype(ino) == LEAF) {
182 fprintf(stderr, "%s: not a directory\n", name);
183 break;
184 }
185 (void) strncpy(curdir, name, sizeof(curdir));
186 curdir[sizeof(curdir) - 1] = '\0';
187 break;
188 /*
189 * Delete elements from the extraction list.
190 */
191 case 'd':
192 if (strncmp(cmd, "delete", strlen(cmd)) != 0)
193 goto bad;
194 np = lookupname(name);
195 if (np == NULL || (np->e_flags & NEW) == 0) {
196 fprintf(stderr, "%s: not on extraction list\n", name);
197 break;
198 }
199 treescan(name, np->e_ino, deletefile);
200 break;
201 /*
202 * Extract the requested list.
203 */
204 case 'e':
205 if (strncmp(cmd, "extract", strlen(cmd)) != 0)
206 goto bad;
207 createfiles();
208 createlinks();
209 setdirmodes(0);
210 if (dflag)
211 checkrestore();
212 volno = 0;
213 break;
214 /*
215 * List available commands.
216 */
217 case 'h':
218 if (strncmp(cmd, "help", strlen(cmd)) != 0)
219 goto bad;
220 case '?':
221 fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
222 "Available commands are:\n",
223 "\tls [arg] - list directory\n",
224 "\tcd arg - change directory\n",
225 "\tpwd - print current directory\n",
226 "\tadd [arg] - add `arg' to list of",
227 " files to be extracted\n",
228 "\tdelete [arg] - delete `arg' from",
229 " list of files to be extracted\n",
230 "\textract - extract requested files\n",
231 "\tsetmodes - set modes of requested directories\n",
232 "\tquit - immediately exit program\n",
233 "\twhat - list dump header information\n",
234 "\tverbose - toggle verbose flag",
235 " (useful with ``ls'')\n",
236 "\tprompt - toggle the prompt display\n",
237 "\thelp or `?' - print this list\n",
238 "If no `arg' is supplied, the current",
239 " directory is used\n");
240 break;
241 /*
242 * List a directory.
243 */
244 case 'l':
245 if (strncmp(cmd, "ls", strlen(cmd)) != 0)
246 goto bad;
247 printlist(name, curdir);
248 break;
249 /*
250 * Print current directory.
251 */
252 case 'p':
253 if (strncmp(cmd, "pwd", strlen(cmd)) == 0) {
254 if (curdir[1] == '\0')
255 fprintf(stderr, "/\n");
256 else
257 fprintf(stderr, "%s\n", &curdir[1]);
258 }
259 /*
260 * Toggle prompt mode.
261 */
262 else if (strncmp(cmd, "prompt", strlen(cmd)) == 0) {
263 if (pflag) {
264 fprintf(stderr, "prompt mode off\n");
265 pflag = 0;
266 break;
267 }
268 fprintf(stderr, "prompt mode on\n");
269 pflag++;
270 break;
271 }
272 else goto bad;
273 break;
274 /*
275 * Quit.
276 */
277 case 'q':
278 if (strncmp(cmd, "quit", strlen(cmd)) != 0)
279 goto bad;
280 return;
281 case 'x':
282 if (strncmp(cmd, "xit", strlen(cmd)) != 0)
283 goto bad;
284 return;
285 /*
286 * Toggle verbose mode.
287 */
288 case 'v':
289 if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
290 goto bad;
291 if (vflag) {
292 fprintf(stderr, "verbose mode off\n");
293 vflag = 0;
294 break;
295 }
296 fprintf(stderr, "verbose mode on\n");
297 vflag++;
298 break;
299 /*
300 * Just restore requested directory modes.
301 */
302 case 's':
303 if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
304 goto bad;
305 setdirmodes(FORCE);
306 break;
307 /*
308 * Print out dump header information.
309 */
310 case 'w':
311 if (strncmp(cmd, "what", strlen(cmd)) != 0)
312 goto bad;
313 printdumpinfo();
314 break;
315 /*
316 * Turn on debugging.
317 */
318 case 'D':
319 if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
320 goto bad;
321 if (dflag) {
322 fprintf(stderr, "debugging mode off\n");
323 dflag = 0;
324 break;
325 }
326 fprintf(stderr, "debugging mode on\n");
327 dflag++;
328 break;
329 /*
330 * Unknown command.
331 */
332 default:
333 bad:
334 fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
335 break;
336 }
337 goto loop;
338 }
339
340 /*
341 * Read and parse an interactive command.
342 * The first word on the line is assigned to "cmd". If
343 * there are no arguments on the command line, then "curdir"
344 * is returned as the argument. If there are arguments
345 * on the line they are returned one at a time on each
346 * successive call to getcmd. Each argument is first assigned
347 * to "name". If it does not start with "/" the pathname in
348 * "curdir" is prepended to it. Finally "canon" is called to
349 * eliminate any embedded ".." components.
350 */
351 static void
352 getcmd(char *curdir, char *cmd, char *name, int size, struct arglist *ap)
353 {
354 register char *cp;
355 static char input[BUFSIZ];
356 char output[BUFSIZ];
357 # define rawname input /* save space by reusing input buffer */
358
359 /*
360 * Check to see if still processing arguments.
361 */
362 if (ap->argcnt > 0)
363 goto retnext;
364 if (nextarg != NULL)
365 goto getnext;
366 /*
367 * Read a command line and trim off trailing white space.
368 */
369 #if HAVE_READLINE
370 snprintf(input, BUFSIZ, "%s\n", rl_gets(curdir));
371 #else
372 do {
373 if (pflag)
374 fprintf(stderr, "%s:%s:%s > ",
375 __progname,
376 spcl.c_filesys,
377 curdir[1] ? &curdir[1] : "/");
378 else
379 fprintf(stderr, "%s > ", __progname);
380 (void) fflush(stderr);
381 (void) fgets(input, BUFSIZ, terminal);
382 } while (!feof(terminal) && input[0] == '\n');
383 if (feof(terminal)) {
384 (void) strcpy(cmd, "quit");
385 return;
386 }
387 #endif
388 for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
389 /* trim off trailing white space and newline */;
390 *++cp = '\0';
391 /*
392 * Copy the command into "cmd".
393 */
394 cp = copynext(input, cmd);
395 ap->cmd = cmd;
396 /*
397 * If no argument, use curdir as the default.
398 */
399 if (*cp == '\0') {
400 (void) strncpy(name, curdir, size);
401 name[size - 1] = '\0';
402 return;
403 }
404 nextarg = cp;
405 /*
406 * Find the next argument.
407 */
408 getnext:
409 cp = copynext(nextarg, rawname);
410 if (*cp == '\0')
411 nextarg = NULL;
412 else
413 nextarg = cp;
414 /*
415 * If it is an absolute pathname, canonicalize it and return it.
416 */
417 if (rawname[0] == '/') {
418 canon(rawname, name, size);
419 } else {
420 /*
421 * For relative pathnames, prepend the current directory to
422 * it then canonicalize and return it.
423 */
424 snprintf(output, sizeof(output), "%s/%s", curdir, rawname);
425 canon(output, name, size);
426 }
427 if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0)
428 fprintf(stderr, "%s: out of memory\n", ap->cmd);
429 if (ap->glob.gl_pathc == 0)
430 return;
431 ap->freeglob = 1;
432 ap->argcnt = ap->glob.gl_pathc;
433
434 retnext:
435 strncpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size);
436 name[size - 1] = '\0';
437 if (--ap->argcnt == 0) {
438 ap->freeglob = 0;
439 globfree(&ap->glob);
440 }
441 # undef rawname
442 }
443
444 /*
445 * Strip off the next token of the input.
446 */
447 static char *
448 copynext(char *input, char *output)
449 {
450 register char *cp, *bp;
451 char quote;
452
453 for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
454 /* skip to argument */;
455 bp = output;
456 while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
457 /*
458 * Handle back slashes.
459 */
460 if (*cp == '\\') {
461 if (*++cp == '\0') {
462 fprintf(stderr,
463 "command lines cannot be continued\n");
464 continue;
465 }
466 *bp++ = *cp++;
467 continue;
468 }
469 /*
470 * The usual unquoted case.
471 */
472 if (*cp != '\'' && *cp != '"') {
473 *bp++ = *cp++;
474 continue;
475 }
476 /*
477 * Handle single and double quotes.
478 */
479 quote = *cp++;
480 while (*cp != quote && *cp != '\0')
481 *bp++ = *cp++;
482 if (*cp++ == '\0') {
483 fprintf(stderr, "missing %c\n", quote);
484 cp--;
485 continue;
486 }
487 }
488 *bp = '\0';
489 return (cp);
490 }
491
492 /*
493 * Canonicalize file names to always start with ``./'' and
494 * remove any embedded "." and ".." components.
495 */
496 void
497 canon(char *rawname, char *canonname, int len)
498 {
499 register char *cp, *np;
500
501 if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
502 (void) strcpy(canonname, "");
503 else if (rawname[0] == '/')
504 (void) strcpy(canonname, ".");
505 else
506 (void) strcpy(canonname, "./");
507 if (strlen(canonname) + strlen(rawname) >= len)
508 errx(1, "canonname: not enough buffer space");
509
510 (void) strcat(canonname, rawname);
511 /*
512 * Eliminate multiple and trailing '/'s
513 */
514 for (cp = np = canonname; *np != '\0'; cp++) {
515 *cp = *np++;
516 while (*cp == '/' && *np == '/')
517 np++;
518 }
519 *cp = '\0';
520 if (*--cp == '/')
521 *cp = '\0';
522 /*
523 * Eliminate extraneous "." and ".." from pathnames.
524 */
525 for (np = canonname; *np != '\0'; ) {
526 np++;
527 cp = np;
528 while (*np != '/' && *np != '\0')
529 np++;
530 if (np - cp == 1 && *cp == '.') {
531 cp--;
532 (void) strcpy(cp, np);
533 np = cp;
534 }
535 if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
536 cp--;
537 while (cp > &canonname[1] && *--cp != '/')
538 /* find beginning of name */;
539 (void) strcpy(cp, np);
540 np = cp;
541 }
542 }
543 }
544
545 /*
546 * Do an "ls" style listing of a directory
547 */
548 static void
549 printlist(char *name, char *basename)
550 {
551 register struct afile *fp, *list, *listp = NULL;
552 register struct direct *dp;
553 struct afile single;
554 RST_DIR *dirp;
555 int entries, len, namelen;
556 char locname[MAXPATHLEN + 1];
557
558 dp = pathsearch(name);
559 if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
560 (!vflag && dp->d_ino == WINO))
561 return;
562 if ((dirp = rst_opendir(name)) == NULL) {
563 entries = 1;
564 list = &single;
565 mkentry(name, dp, list);
566 len = strlen(basename) + 1;
567 if (strlen(name) - len > single.len) {
568 freename(single.fname);
569 single.fname = savename(&name[len]);
570 single.len = strlen(single.fname);
571 }
572 } else {
573 entries = 0;
574 while ((dp = rst_readdir(dirp)))
575 entries++;
576 rst_closedir(dirp);
577 list = (struct afile *)malloc(entries * sizeof(struct afile));
578 if (list == NULL) {
579 fprintf(stderr, "ls: out of memory\n");
580 return;
581 }
582 if ((dirp = rst_opendir(name)) == NULL)
583 panic("directory reopen failed\n");
584 fprintf(stderr, "%s:\n", name);
585 entries = 0;
586 listp = list;
587 namelen = snprintf(locname, sizeof(locname), "%s/", name);
588 if (namelen >= sizeof(locname))
589 namelen = sizeof(locname) - 1;
590 while ((dp = rst_readdir(dirp))) {
591 if (dp == NULL)
592 break;
593 if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
594 continue;
595 if (!vflag && (dp->d_ino == WINO ||
596 strcmp(dp->d_name, ".") == 0 ||
597 strcmp(dp->d_name, "..") == 0))
598 continue;
599 locname[namelen] = '\0';
600 if (namelen + strlen(dp->d_name) >= MAXPATHLEN) {
601 fprintf(stderr, "%s%s: name exceeds %d char\n",
602 locname, dp->d_name, MAXPATHLEN);
603 } else {
604 (void) strncat(locname, dp->d_name,
605 (int)strlen(dp->d_name));
606 mkentry(locname, dp, listp++);
607 entries++;
608 }
609 }
610 rst_closedir(dirp);
611 if (entries == 0) {
612 fprintf(stderr, "\n");
613 free(list);
614 return;
615 }
616 qsort((char *)list, entries, sizeof(struct afile), fcmp);
617 }
618 formatf(list, entries);
619 if (dirp != NULL) {
620 for (fp = listp - 1; fp >= list; fp--)
621 freename(fp->fname);
622 fprintf(stderr, "\n");
623 free(list);
624 }
625 }
626
627 /*
628 * Read the contents of a directory.
629 */
630 static void
631 mkentry(char *name, struct direct *dp, struct afile *fp)
632 {
633 char *cp;
634 struct entry *np;
635
636 fp->fnum = dp->d_ino;
637 fp->fname = savename(dp->d_name);
638 for (cp = fp->fname; *cp; cp++)
639 if (!vflag && (*cp < ' ' || *cp >= 0177))
640 *cp = '?';
641 fp->len = cp - fp->fname;
642 if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
643 fp->prefix = '^';
644 else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
645 fp->prefix = '*';
646 else
647 fp->prefix = ' ';
648 switch(dp->d_type) {
649
650 default:
651 fprintf(stderr, "Warning: undefined file type %d\n",
652 dp->d_type);
653 /* fall through */
654 case DT_REG:
655 fp->postfix = ' ';
656 break;
657
658 case DT_LNK:
659 fp->postfix = '@';
660 break;
661
662 case DT_FIFO:
663 case DT_SOCK:
664 fp->postfix = '=';
665 break;
666
667 case DT_CHR:
668 case DT_BLK:
669 fp->postfix = '#';
670 break;
671
672 #ifndef __linux__
673 /* no need for this */
674 case DT_WHT:
675 fp->postfix = '%';
676 break;
677 #endif
678
679 case DT_UNKNOWN:
680 case DT_DIR:
681 if (inodetype(dp->d_ino) == NODE)
682 fp->postfix = '/';
683 else
684 fp->postfix = ' ';
685 break;
686 }
687 return;
688 }
689
690 /*
691 * Print out a pretty listing of a directory
692 */
693 static void
694 formatf(struct afile *list, int nentry)
695 {
696 register struct afile *fp, *endlist;
697 int width, bigino, haveprefix, havepostfix;
698 int i, j, w, precision = 0, columns, lines;
699
700 width = 0;
701 haveprefix = 0;
702 havepostfix = 0;
703 bigino = ROOTINO;
704 endlist = &list[nentry];
705 for (fp = &list[0]; fp < endlist; fp++) {
706 if (bigino < fp->fnum)
707 bigino = fp->fnum;
708 if (width < fp->len)
709 width = fp->len;
710 if (fp->prefix != ' ')
711 haveprefix = 1;
712 if (fp->postfix != ' ')
713 havepostfix = 1;
714 }
715 if (haveprefix)
716 width++;
717 if (havepostfix)
718 width++;
719 if (vflag) {
720 for (precision = 0, i = bigino; i > 0; i /= 10)
721 precision++;
722 width += precision + 1;
723 }
724 width++;
725 columns = 81 / width;
726 if (columns == 0)
727 columns = 1;
728 lines = (nentry + columns - 1) / columns;
729 for (i = 0; i < lines; i++) {
730 for (j = 0; j < columns; j++) {
731 fp = &list[j * lines + i];
732 if (vflag) {
733 fprintf(stderr, "%*ld ", precision, (long)fp->fnum);
734 fp->len += precision + 1;
735 }
736 if (haveprefix) {
737 putc(fp->prefix, stderr);
738 fp->len++;
739 }
740 fprintf(stderr, "%s", fp->fname);
741 if (havepostfix) {
742 putc(fp->postfix, stderr);
743 fp->len++;
744 }
745 if (fp + lines >= endlist) {
746 fprintf(stderr, "\n");
747 break;
748 }
749 for (w = fp->len; w < width; w++)
750 putc(' ', stderr);
751 }
752 }
753 }
754
755 /*
756 * Skip over directory entries that are not on the tape
757 *
758 * First have to get definition of a dirent.
759 *
760 * For Linux the dirent struct is now included from bsdcompat.h
761 */
762 #ifndef __linux__
763 #undef DIRBLKSIZ
764 #include <dirent.h>
765 #undef d_ino
766 #endif /* ! __linux__ */
767
768 struct dirent *
769 glob_readdir(RST_DIR *dirp)
770 {
771 struct direct *dp;
772 static struct dirent adirent;
773
774 while ((dp = rst_readdir(dirp)) != NULL) {
775 if (!vflag && dp->d_ino == WINO)
776 continue;
777 if (dflag || TSTINO(dp->d_ino, dumpmap))
778 break;
779 }
780 if (dp == NULL)
781 return (NULL);
782 adirent.d_fileno = dp->d_ino;
783 memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
784 return (&adirent);
785 }
786
787 /*
788 * Return st_mode information in response to stat or lstat calls
789 */
790 static int
791 glob_stat(const char *name, struct stat *stp)
792 {
793 register struct direct *dp;
794 dp = pathsearch(name);
795 if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
796 (!vflag && dp->d_ino == WINO))
797 return (-1);
798 if (inodetype(dp->d_ino) == NODE)
799 stp->st_mode = IFDIR;
800 else
801 stp->st_mode = IFREG;
802 return (0);
803 }
804
805 /*
806 * Comparison routine for qsort.
807 */
808 static int
809 fcmp(const void *f1, const void *f2)
810 {
811 return (strcmp(((struct afile *)f1)->fname,
812 ((struct afile *)f2)->fname));
813 }
814
815 /*
816 * respond to interrupts
817 */
818 void
819 onintr(int signo)
820 {
821 int save_errno = errno;
822
823 if (command == 'i' && runshell)
824 longjmp(reset, 1);
825 if (reply("restore interrupted, continue") == FAIL)
826 exit(1);
827 errno = save_errno;
828 }
829
830
831 #if HAVE_READLINE
832
833 /* A static variable for holding the line. */
834 static char *line_read = NULL;
835
836 static char completion_curdir[MAXPATHLEN];
837
838 static char *commands[] = {
839 "add ", "cd ", "delete ", "extract ", "help ",
840 "? ", "ls ", "pwd ", "prompt ", "quit ", "xit ",
841 "verbose ", "setmodes ", "what ", "Debug ",
842 NULL };
843
844 static char *files = NULL;
845
846 static char *
847 rl_gets (char *dir)
848 {
849 char *prompt;
850 int sz;
851
852 snprintf(completion_curdir, MAXPATHLEN, "%s", dir);
853 completion_curdir[MAXPATHLEN - 1] = '\0';
854
855 if (pflag) {
856 sz = 6 + strlen(__progname) + strlen(spcl.c_filesys) + strlen((completion_curdir + 1 ? completion_curdir + 1 : "/"));
857 prompt = (char *)malloc(sz);
858 if (!prompt)
859 return NULL;
860 snprintf(prompt, sz, "%s:%s:%s > ",
861 __progname,
862 spcl.c_filesys,
863 (completion_curdir + 1 ? completion_curdir + 1 : "/"));
864 }
865 else {
866 sz = 4 + strlen(__progname);
867 prompt = (char *)malloc(sz);
868 if (!prompt)
869 return NULL;
870 snprintf(prompt, sz, "%s > ", __progname);
871 }
872 prompt[sz - 1] = '\0';
873
874 if (line_read) {
875 free (line_read);
876 line_read = (char *)NULL;
877 }
878
879 do {
880 line_read = readline (prompt);
881 } while (line_read && !*line_read);
882
883 free(prompt);
884
885 if (!line_read) {
886 printf("\n");
887 return strdup("quit");
888 }
889
890 add_history (line_read);
891
892 return (line_read);
893 }
894
895 static void
896 initialize_readline(void)
897 {
898 rl_attempted_completion_function = restore_completion;
899 rl_completion_entry_function = (Function *)NULL;
900 rl_completion_append_character = '\0';
901 rl_instream = terminal;
902 }
903
904 static char **
905 restore_completion (char *text, int start, int end)
906 {
907 char **matches;
908
909 if (start == 0)
910 matches = completion_matches (text, command_generator);
911 else
912 matches = completion_matches (text, filename_generator);
913
914 return (matches);
915 }
916
917 static char *
918 command_generator(char *text, int state)
919 {
920 static int list_index, len;
921 char *name;
922
923 if (!state) {
924 list_index = 0;
925 len = strlen(text);
926 }
927
928 while ( (name = commands[list_index]) != NULL) {
929
930 list_index ++;
931
932 if (strncmp(name, text, len) == 0)
933 return strdup(name);
934 }
935
936 return NULL;
937 }
938
939 static char *
940 filename_generator(char *text, int state)
941 {
942 static int list_index;
943 char *name;
944 RST_DIR *dirp;
945 struct direct *dp;
946 static int entries;
947 char pname[MAXPATHLEN];
948 char fname[MAXPATHLEN];
949 char *slash;
950 char ppname[MAXPATHLEN];
951
952 if (!state) {
953 list_index = 0;
954
955 if (files != NULL) {
956 free(files);
957 entries = 0;
958 files = NULL;
959 }
960 if ((slash = strrchr(text, '/')) != NULL) {
961 int idx = slash - text;
962 if (idx > MAXPATHLEN - 2)
963 idx = MAXPATHLEN - 2;
964 strncpy(ppname, text, MAXPATHLEN);
965 ppname[MAXPATHLEN - 1] = '\0';
966 ppname[idx] = '\0';
967 if (text[0] == '/')
968 snprintf(pname, MAXPATHLEN, ".%s", ppname);
969 else
970 snprintf(pname, MAXPATHLEN, "%s/%s", completion_curdir, ppname);
971 strncpy(fname, ppname + idx + 1, MAXPATHLEN);
972 ppname[idx] = '/';
973 ppname[idx + 1] = '\0';
974 }
975 else {
976 strncpy(pname, completion_curdir, MAXPATHLEN);
977 strncpy(fname, text, MAXPATHLEN);
978 ppname[0] = '\0';
979 }
980 pname[MAXPATHLEN - 1] = '\0';
981 fname[MAXPATHLEN - 1] = '\0';
982 if ((dirp = rst_opendir(pname)) == NULL)
983 return NULL;
984 entries = 0;
985 while ((dp = rst_readdir(dirp)))
986 entries++;
987 rst_closedir(dirp);
988 files = (char *)malloc(entries * MAXPATHLEN);
989 if (files == NULL) {
990 fprintf(stderr, "Out of memory\n");
991 entries = 0;
992 return NULL;
993 }
994 if ((dirp = rst_opendir(pname)) == NULL)
995 panic("directory reopen failed\n");
996 entries = 0;
997 while ((dp = rst_readdir(dirp))) {
998 if (TSTINO(dp->d_ino, dumpmap) == 0)
999 continue;
1000 if (strcmp(dp->d_name, ".") == 0 ||
1001 strcmp(dp->d_name, "..") == 0)
1002 continue;
1003 if (strncmp(dp->d_name, fname, strlen(fname)) == 0) {
1004 if (inodetype(dp->d_ino) == NODE)
1005 snprintf(files + entries * MAXPATHLEN, MAXPATHLEN, "%s%s/", ppname, dp->d_name);
1006 else
1007 snprintf(files + entries * MAXPATHLEN, MAXPATHLEN, "%s%s ", ppname, dp->d_name);
1008 *(files + (entries + 1) * MAXPATHLEN - 1) = '\0';
1009 ++entries;
1010 }
1011 }
1012 rst_closedir(dirp);
1013 }
1014
1015 if (list_index >= entries)
1016 return NULL;
1017
1018 name = strdup(files + list_index * MAXPATHLEN);
1019 list_index ++;
1020
1021 return name;
1022 }
1023 #endif /* HAVE_READLINE */