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