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 <stelian@popies.net>, 1999-2000
6
* Stelian Pop <stelian@popies.net> - Alc�ve <www.alcove.com>, 2000-2002
10
* Copyright (c) 1985, 1993
11
* The Regents of the University of California. All rights reserved.
13
* Redistribution and use in source and binary forms, with or without
14
* modification, are permitted provided that the following conditions
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.
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
43
static const char rcsid[] =
44
"$Id: interactive.c,v 1.22 2002/02/04 12:07:38 stelian Exp $";
48
#include <sys/param.h>
52
#ifdef HAVE_EXT2FS_EXT2_FS_H
53
#include <ext2fs/ext2_fs.h>
55
#include <linux/ext2_fs.h>
57
#include <bsdcompat.h>
59
#include <ufs/ufs/dinode.h>
60
#include <ufs/ufs/dir.h>
61
#endif /* __linux__ */
62
#include <protocols/dumprestore.h>
65
#include <compaterr.h>
67
#include <compatglob.h>
73
#include <ext2fs/ext2fs.h>
74
extern char * __progname;
81
#include <readline/readline.h>
82
#include <readline/history.h>
84
static char *rl_gets (char *prompt);
85
static void initialize_readline(void);
86
static char **restore_completion (const char *text, int start, int end);
87
static char *command_generator(const char *text, int state);
88
static char *filename_generator(const char *text, int state);
91
#define round(a, b) (((a) + (b) - 1) / (b) * (b))
94
* Things to handle interruptions.
98
static char *nextarg = NULL;
99
static int pflag = 0; /* prompt mode */
101
* Structure and routines associated with listing directories.
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 */
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 */
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 *));
127
* Read and execute commands from the terminal.
134
struct arglist arglist;
135
char curdir[MAXPATHLEN];
136
char name[MAXPATHLEN];
140
initialize_readline();
142
arglist.freeglob = 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 = (int (*)(const char *, void *))glob_stat;
149
arglist.glob.gl_stat = (int (*)(const char *, void *))glob_stat;
150
canon("/", curdir, sizeof(curdir));
152
if (setjmp(reset) != 0) {
153
if (arglist.freeglob != 0) {
154
arglist.freeglob = 0;
156
globfree(&arglist.glob);
162
getcmd(curdir, cmd, name, sizeof(name), &arglist);
165
* Add elements to the extraction list.
168
if (strncmp(cmd, "add", strlen(cmd)) != 0)
170
ino = dirlookup(name);
175
treescan(name, ino, addfile);
178
* Change working directory.
181
if (strncmp(cmd, "cd", strlen(cmd)) != 0)
183
ino = dirlookup(name);
186
if (inodetype(ino) == LEAF) {
187
fprintf(stderr, "%s: not a directory\n", name);
190
(void) strncpy(curdir, name, sizeof(curdir));
191
curdir[sizeof(curdir) - 1] = '\0';
194
* Delete elements from the extraction list.
197
if (strncmp(cmd, "delete", strlen(cmd)) != 0)
199
np = lookupname(name);
200
if (np == NULL || (np->e_flags & NEW) == 0) {
201
fprintf(stderr, "%s: not on extraction list\n", name);
204
treescan(name, np->e_ino, deletefile);
207
* Extract the requested list.
210
if (strncmp(cmd, "extract", strlen(cmd)) != 0)
220
* List available commands.
223
if (strncmp(cmd, "help", strlen(cmd)) != 0)
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");
250
if (strncmp(cmd, "ls", strlen(cmd)) != 0)
252
printlist(name, curdir);
255
* Print current directory.
258
if (strncmp(cmd, "pwd", strlen(cmd)) == 0) {
259
if (curdir[1] == '\0')
260
fprintf(stderr, "/\n");
262
fprintf(stderr, "%s\n", &curdir[1]);
265
* Toggle prompt mode.
267
else if (strncmp(cmd, "prompt", strlen(cmd)) == 0) {
269
fprintf(stderr, "prompt mode off\n");
273
fprintf(stderr, "prompt mode on\n");
283
if (strncmp(cmd, "quit", strlen(cmd)) != 0)
287
if (strncmp(cmd, "xit", strlen(cmd)) != 0)
291
* Toggle verbose mode.
294
if (strncmp(cmd, "verbose", strlen(cmd)) != 0)
297
fprintf(stderr, "verbose mode off\n");
301
fprintf(stderr, "verbose mode on\n");
305
* Just restore requested directory modes.
308
if (strncmp(cmd, "setmodes", strlen(cmd)) != 0)
313
* Print out dump header information.
316
if (strncmp(cmd, "what", strlen(cmd)) != 0)
325
if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
328
fprintf(stderr, "debugging mode off\n");
332
fprintf(stderr, "debugging mode on\n");
340
fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
347
* Read and parse an interactive command.
348
* The first word on the line is assigned to "cmd". If
349
* there are no arguments on the command line, then "curdir"
350
* is returned as the argument. If there are arguments
351
* on the line they are returned one at a time on each
352
* successive call to getcmd. Each argument is first assigned
353
* to "name". If it does not start with "/" the pathname in
354
* "curdir" is prepended to it. Finally "canon" is called to
355
* eliminate any embedded ".." components.
358
getcmd(char *curdir, char *cmd, char *name, int size, struct arglist *ap)
361
static char input[BUFSIZ];
363
# define rawname input /* save space by reusing input buffer */
366
* Check to see if still processing arguments.
373
* Read a command line and trim off trailing white space.
376
snprintf(input, BUFSIZ, "%s\n", rl_gets(curdir));
380
fprintf(stderr, "%s:%s:%s > ",
383
curdir[1] ? &curdir[1] : "/");
385
fprintf(stderr, "%s > ", __progname);
386
(void) fflush(stderr);
387
(void) fgets(input, BUFSIZ, terminal);
388
} while (!feof(terminal) && input[0] == '\n');
389
if (feof(terminal)) {
390
(void) strcpy(cmd, "quit");
394
for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
395
/* trim off trailing white space and newline */;
398
* Copy the command into "cmd".
400
cp = copynext(input, cmd);
403
* If no argument, use curdir as the default.
406
(void) strncpy(name, curdir, size);
407
name[size - 1] = '\0';
412
* Find the next argument.
415
cp = copynext(nextarg, rawname);
421
* If it is an absolute pathname, canonicalize it and return it.
423
if (rawname[0] == '/') {
424
canon(rawname, name, size);
427
* For relative pathnames, prepend the current directory to
428
* it then canonicalize and return it.
430
snprintf(output, sizeof(output), "%s/%s", curdir, rawname);
431
canon(output, name, size);
433
if (glob(name, GLOB_ALTDIRFUNC, NULL, &ap->glob) < 0)
434
fprintf(stderr, "%s: out of memory\n", ap->cmd);
435
if (ap->glob.gl_pathc == 0)
438
ap->argcnt = ap->glob.gl_pathc;
441
strncpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size);
442
name[size - 1] = '\0';
443
if (--ap->argcnt == 0) {
451
* Strip off the next token of the input.
454
copynext(char *input, char *output)
459
for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
460
/* skip to argument */;
462
while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
464
* Handle back slashes.
469
"command lines cannot be continued\n");
476
* The usual unquoted case.
478
if (*cp != '\'' && *cp != '"') {
483
* Handle single and double quotes.
486
while (*cp != quote && *cp != '\0')
489
fprintf(stderr, "missing %c\n", quote);
499
* Canonicalize file names to always start with ``./'' and
500
* remove any embedded "." and ".." components.
503
canon(char *rawname, char *canonname, int len)
507
if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
508
(void) strcpy(canonname, "");
509
else if (rawname[0] == '/')
510
(void) strcpy(canonname, ".");
512
(void) strcpy(canonname, "./");
513
if (strlen(canonname) + strlen(rawname) >= len)
514
errx(1, "canonname: not enough buffer space");
516
(void) strcat(canonname, rawname);
518
* Eliminate multiple and trailing '/'s
520
for (cp = np = canonname; *np != '\0'; cp++) {
522
while (*cp == '/' && *np == '/')
529
* Eliminate extraneous "." and ".." from pathnames.
531
for (np = canonname; *np != '\0'; ) {
534
while (*np != '/' && *np != '\0')
536
if (np - cp == 1 && *cp == '.') {
538
(void) strcpy(cp, np);
541
if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
543
while (cp > &canonname[1] && *--cp != '/')
544
/* find beginning of name */;
545
(void) strcpy(cp, np);
552
* Do an "ls" style listing of a directory
555
printlist(char *name, char *basename)
557
struct afile *fp, *list, *listp = NULL;
561
int entries, len, namelen;
562
char locname[MAXPATHLEN + 1];
564
dp = pathsearch(name);
565
if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
566
(!vflag && dp->d_ino == WINO))
568
if ((dirp = rst_opendir(name)) == NULL) {
571
mkentry(name, dp, list);
572
len = strlen(basename) + 1;
573
if (strlen(name) - len > single.len) {
574
freename(single.fname);
575
single.fname = savename(&name[len]);
576
single.len = strlen(single.fname);
580
while ((dp = rst_readdir(dirp)))
583
list = (struct afile *)malloc(entries * sizeof(struct afile));
585
fprintf(stderr, "ls: out of memory\n");
588
if ((dirp = rst_opendir(name)) == NULL)
589
panic("directory reopen failed\n");
590
fprintf(stderr, "%s:\n", name);
593
namelen = snprintf(locname, sizeof(locname), "%s/", name);
594
if (namelen >= sizeof(locname))
595
namelen = sizeof(locname) - 1;
596
while ((dp = rst_readdir(dirp))) {
599
if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
601
if (!vflag && (dp->d_ino == WINO ||
602
strcmp(dp->d_name, ".") == 0 ||
603
strcmp(dp->d_name, "..") == 0))
605
locname[namelen] = '\0';
606
if (namelen + strlen(dp->d_name) >= MAXPATHLEN) {
607
fprintf(stderr, "%s%s: name exceeds %d char\n",
608
locname, dp->d_name, MAXPATHLEN);
610
(void) strncat(locname, dp->d_name,
611
(int)strlen(dp->d_name));
612
mkentry(locname, dp, listp++);
618
fprintf(stderr, "\n");
622
qsort((char *)list, entries, sizeof(struct afile), fcmp);
624
formatf(list, entries);
626
for (fp = listp - 1; fp >= list; fp--)
628
fprintf(stderr, "\n");
634
* Read the contents of a directory.
637
mkentry(char *name, struct direct *dp, struct afile *fp)
642
fp->fnum = dp->d_ino;
643
fp->fname = savename(dp->d_name);
644
for (cp = fp->fname; *cp; cp++)
645
if (!vflag && (*cp < ' ' || *cp >= 0177))
647
fp->len = cp - fp->fname;
648
if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
650
else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
657
fprintf(stderr, "Warning: undefined file type %d\n",
679
/* no need for this */
687
if (inodetype(dp->d_ino) == NODE)
697
* Print out a pretty listing of a directory
700
formatf(struct afile *list, int nentry)
702
struct afile *fp, *endlist;
703
int width, bigino, haveprefix, havepostfix;
704
int i, j, w, precision = 0, columns, lines;
710
endlist = &list[nentry];
711
for (fp = &list[0]; fp < endlist; fp++) {
712
if (bigino < fp->fnum)
716
if (fp->prefix != ' ')
718
if (fp->postfix != ' ')
726
for (precision = 0, i = bigino; i > 0; i /= 10)
728
width += precision + 1;
731
columns = 81 / width;
734
lines = (nentry + columns - 1) / columns;
735
for (i = 0; i < lines; i++) {
736
for (j = 0; j < columns; j++) {
737
fp = &list[j * lines + i];
739
fprintf(stderr, "%*ld ", precision, (long)fp->fnum);
740
fp->len += precision + 1;
743
putc(fp->prefix, stderr);
746
fprintf(stderr, "%s", fp->fname);
748
putc(fp->postfix, stderr);
751
if (fp + lines >= endlist) {
752
fprintf(stderr, "\n");
755
for (w = fp->len; w < width; w++)
762
* Skip over directory entries that are not on the tape
764
* First have to get definition of a dirent.
766
* For Linux the dirent struct is now included from bsdcompat.h
772
#endif /* ! __linux__ */
775
glob_readdir(RST_DIR *dirp)
778
static struct dirent adirent;
780
while ((dp = rst_readdir(dirp)) != NULL) {
781
if (!vflag && dp->d_ino == WINO)
783
if (dflag || TSTINO(dp->d_ino, dumpmap))
788
adirent.d_fileno = dp->d_ino;
789
memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
794
* Return st_mode information in response to stat or lstat calls
797
glob_stat(const char *name, struct stat *stp)
800
dp = pathsearch(name);
801
if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
802
(!vflag && dp->d_ino == WINO))
804
if (inodetype(dp->d_ino) == NODE)
805
stp->st_mode = IFDIR;
807
stp->st_mode = IFREG;
812
* Comparison routine for qsort.
815
fcmp(const void *f1, const void *f2)
817
return (strcmp(((struct afile *)f1)->fname,
818
((struct afile *)f2)->fname));
822
* respond to interrupts
827
int save_errno = errno;
829
if (command == 'i' && runshell)
831
if (reply("restore interrupted, continue") == FAIL)
839
/* A static variable for holding the line. */
840
static char *line_read = NULL;
842
static char completion_curdir[MAXPATHLEN];
844
static char *commands[] = {
845
"add ", "cd ", "delete ", "extract ", "help ",
846
"? ", "ls ", "pwd ", "prompt ", "quit ", "xit ",
847
"verbose ", "setmodes ", "what ", "Debug ",
850
static char *files = NULL;
858
snprintf(completion_curdir, MAXPATHLEN, "%s", dir);
859
completion_curdir[MAXPATHLEN - 1] = '\0';
862
sz = 6 + strlen(__progname) + strlen(spcl.c_filesys) + strlen((completion_curdir + 1 ? completion_curdir + 1 : "/"));
863
prompt = (char *)malloc(sz);
866
snprintf(prompt, sz, "%s:%s:%s > ",
869
(completion_curdir + 1 ? completion_curdir + 1 : "/"));
872
sz = 4 + strlen(__progname);
873
prompt = (char *)malloc(sz);
876
snprintf(prompt, sz, "%s > ", __progname);
878
prompt[sz - 1] = '\0';
882
line_read = (char *)NULL;
886
line_read = readline (prompt);
887
} while (line_read && !*line_read);
893
return strdup("quit");
896
add_history (line_read);
902
command_generator(const char *text, int state)
904
static int list_index, len;
912
while ( (name = commands[list_index]) != NULL) {
916
if (strncmp(name, text, len) == 0)
924
filename_generator(const char *text, int state)
926
static int list_index;
931
char pname[MAXPATHLEN];
932
char fname[MAXPATHLEN];
934
char ppname[MAXPATHLEN];
944
if ((slash = strrchr(text, '/')) != NULL) {
945
int idx = slash - text;
946
if (idx > MAXPATHLEN - 2)
947
idx = MAXPATHLEN - 2;
948
strncpy(ppname, text, MAXPATHLEN);
949
ppname[MAXPATHLEN - 1] = '\0';
952
snprintf(pname, MAXPATHLEN, ".%s", ppname);
954
snprintf(pname, MAXPATHLEN, "%s/%s", completion_curdir, ppname);
955
strncpy(fname, ppname + idx + 1, MAXPATHLEN);
957
ppname[idx + 1] = '\0';
960
strncpy(pname, completion_curdir, MAXPATHLEN);
961
strncpy(fname, text, MAXPATHLEN);
964
pname[MAXPATHLEN - 1] = '\0';
965
fname[MAXPATHLEN - 1] = '\0';
966
if ((dirp = rst_opendir(pname)) == NULL)
969
while ((dp = rst_readdir(dirp)))
972
files = (char *)malloc(entries * MAXPATHLEN);
974
fprintf(stderr, "Out of memory\n");
978
if ((dirp = rst_opendir(pname)) == NULL)
979
panic("directory reopen failed\n");
981
while ((dp = rst_readdir(dirp))) {
982
if (TSTINO(dp->d_ino, dumpmap) == 0)
984
if (strcmp(dp->d_name, ".") == 0 ||
985
strcmp(dp->d_name, "..") == 0)
987
if (strncmp(dp->d_name, fname, strlen(fname)) == 0) {
988
if (inodetype(dp->d_ino) == NODE)
989
snprintf(files + entries * MAXPATHLEN, MAXPATHLEN, "%s%s/", ppname, dp->d_name);
991
snprintf(files + entries * MAXPATHLEN, MAXPATHLEN, "%s%s ", ppname, dp->d_name);
992
*(files + (entries + 1) * MAXPATHLEN - 1) = '\0';
999
if (list_index >= entries)
1002
name = strdup(files + list_index * MAXPATHLEN);
1009
restore_completion (const char *text, int start, int end)
1014
matches = rl_completion_matches (text, command_generator);
1016
matches = rl_completion_matches (text, filename_generator);
1022
initialize_readline(void)
1024
rl_readline_name = "dump";
1025
rl_attempted_completion_function = restore_completion;
1026
rl_completion_entry_function = NULL;
1027
rl_completion_append_character = '\0';
1028
rl_instream = terminal;
1031
#endif /* HAVE_READLINE */