~ubuntu-branches/ubuntu/natty/dump/natty

« back to all changes in this revision

Viewing changes to restore/interactive.c

  • Committer: Bazaar Package Importer
  • Author(s): Bdale Garbee
  • Date: 2002-03-20 01:23:11 UTC
  • Revision ID: james.westby@ubuntu.com-20020320012311-94kbs6nojd19hmd6
Tags: upstream-0.4b27
Import upstream version 0.4b27

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 <stelian@popies.net>, 1999-2000
 
6
 *      Stelian Pop <stelian@popies.net> - Alc�ve <www.alcove.com>, 2000-2002
 
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.22 2002/02/04 12:07:38 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 (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);
 
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
        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 = (int (*)(const char *, void *))glob_stat;
 
149
        arglist.glob.gl_stat = (int (*)(const char *, void *))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
                printvolinfo();
 
320
                break;
 
321
        /*
 
322
         * Turn on debugging.
 
323
         */
 
324
        case 'D':
 
325
                if (strncmp(cmd, "Debug", strlen(cmd)) != 0)
 
326
                        goto bad;
 
327
                if (dflag) {
 
328
                        fprintf(stderr, "debugging mode off\n");
 
329
                        dflag = 0;
 
330
                        break;
 
331
                }
 
332
                fprintf(stderr, "debugging mode on\n");
 
333
                dflag++;
 
334
                break;
 
335
        /*
 
336
         * Unknown command.
 
337
         */
 
338
        default:
 
339
        bad:
 
340
                fprintf(stderr, "%s: unknown command; type ? for help\n", cmd);
 
341
                break;
 
342
        }
 
343
        goto loop;
 
344
}
 
345
 
 
346
/*
 
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.
 
356
 */
 
357
static void
 
358
getcmd(char *curdir, char *cmd, char *name, int size, struct arglist *ap)
 
359
{
 
360
        char *cp;
 
361
        static char input[BUFSIZ];
 
362
        char output[BUFSIZ];
 
363
#       define rawname input    /* save space by reusing input buffer */
 
364
 
 
365
        /*
 
366
         * Check to see if still processing arguments.
 
367
         */
 
368
        if (ap->argcnt > 0)
 
369
                goto retnext;
 
370
        if (nextarg != NULL)
 
371
                goto getnext;
 
372
        /*
 
373
         * Read a command line and trim off trailing white space.
 
374
         */
 
375
#if HAVE_READLINE
 
376
        snprintf(input, BUFSIZ, "%s\n", rl_gets(curdir));
 
377
#else
 
378
        do      {
 
379
                if (pflag)
 
380
                        fprintf(stderr, "%s:%s:%s > ", 
 
381
                                __progname,
 
382
                                spcl.c_filesys, 
 
383
                                curdir[1] ? &curdir[1] : "/");
 
384
                else
 
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");
 
391
                return;
 
392
        }
 
393
#endif
 
394
        for (cp = &input[strlen(input) - 2]; *cp == ' ' || *cp == '\t'; cp--)
 
395
                /* trim off trailing white space and newline */;
 
396
        *++cp = '\0';
 
397
        /*
 
398
         * Copy the command into "cmd".
 
399
         */
 
400
        cp = copynext(input, cmd);
 
401
        ap->cmd = cmd;
 
402
        /*
 
403
         * If no argument, use curdir as the default.
 
404
         */
 
405
        if (*cp == '\0') {
 
406
                (void) strncpy(name, curdir, size);
 
407
                name[size - 1] = '\0';
 
408
                return;
 
409
        }
 
410
        nextarg = cp;
 
411
        /*
 
412
         * Find the next argument.
 
413
         */
 
414
getnext:
 
415
        cp = copynext(nextarg, rawname);
 
416
        if (*cp == '\0')
 
417
                nextarg = NULL;
 
418
        else
 
419
                nextarg = cp;
 
420
        /*
 
421
         * If it is an absolute pathname, canonicalize it and return it.
 
422
         */
 
423
        if (rawname[0] == '/') {
 
424
                canon(rawname, name, size);
 
425
        } else {
 
426
                /*
 
427
                 * For relative pathnames, prepend the current directory to
 
428
                 * it then canonicalize and return it.
 
429
                 */
 
430
                snprintf(output, sizeof(output), "%s/%s", curdir, rawname);
 
431
                canon(output, name, size);
 
432
        }
 
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)
 
436
                return;
 
437
        ap->freeglob = 1;
 
438
        ap->argcnt = ap->glob.gl_pathc;
 
439
 
 
440
retnext:
 
441
        strncpy(name, ap->glob.gl_pathv[ap->glob.gl_pathc - ap->argcnt], size);
 
442
        name[size - 1] = '\0';
 
443
        if (--ap->argcnt == 0) {
 
444
                ap->freeglob = 0;
 
445
                globfree(&ap->glob);
 
446
        }
 
447
#       undef rawname
 
448
}
 
449
 
 
450
/*
 
451
 * Strip off the next token of the input.
 
452
 */
 
453
static char *
 
454
copynext(char *input, char *output)
 
455
{
 
456
        char *cp, *bp;
 
457
        char quote;
 
458
 
 
459
        for (cp = input; *cp == ' ' || *cp == '\t'; cp++)
 
460
                /* skip to argument */;
 
461
        bp = output;
 
462
        while (*cp != ' ' && *cp != '\t' && *cp != '\0') {
 
463
                /*
 
464
                 * Handle back slashes.
 
465
                 */
 
466
                if (*cp == '\\') {
 
467
                        if (*++cp == '\0') {
 
468
                                fprintf(stderr,
 
469
                                        "command lines cannot be continued\n");
 
470
                                continue;
 
471
                        }
 
472
                        *bp++ = *cp++;
 
473
                        continue;
 
474
                }
 
475
                /*
 
476
                 * The usual unquoted case.
 
477
                 */
 
478
                if (*cp != '\'' && *cp != '"') {
 
479
                        *bp++ = *cp++;
 
480
                        continue;
 
481
                }
 
482
                /*
 
483
                 * Handle single and double quotes.
 
484
                 */
 
485
                quote = *cp++;
 
486
                while (*cp != quote && *cp != '\0')
 
487
                        *bp++ = *cp++;
 
488
                if (*cp++ == '\0') {
 
489
                        fprintf(stderr, "missing %c\n", quote);
 
490
                        cp--;
 
491
                        continue;
 
492
                }
 
493
        }
 
494
        *bp = '\0';
 
495
        return (cp);
 
496
}
 
497
 
 
498
/*
 
499
 * Canonicalize file names to always start with ``./'' and
 
500
 * remove any embedded "." and ".." components.
 
501
 */
 
502
void
 
503
canon(char *rawname, char *canonname, int len)
 
504
{
 
505
        char *cp, *np;
 
506
 
 
507
        if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0)
 
508
                (void) strcpy(canonname, "");
 
509
        else if (rawname[0] == '/')
 
510
                (void) strcpy(canonname, ".");
 
511
        else
 
512
                (void) strcpy(canonname, "./");
 
513
        if (strlen(canonname) + strlen(rawname) >= len)
 
514
                errx(1, "canonname: not enough buffer space");
 
515
                
 
516
        (void) strcat(canonname, rawname);
 
517
        /*
 
518
         * Eliminate multiple and trailing '/'s
 
519
         */
 
520
        for (cp = np = canonname; *np != '\0'; cp++) {
 
521
                *cp = *np++;
 
522
                while (*cp == '/' && *np == '/')
 
523
                        np++;
 
524
        }
 
525
        *cp = '\0';
 
526
        if (*--cp == '/')
 
527
                *cp = '\0';
 
528
        /*
 
529
         * Eliminate extraneous "." and ".." from pathnames.
 
530
         */
 
531
        for (np = canonname; *np != '\0'; ) {
 
532
                np++;
 
533
                cp = np;
 
534
                while (*np != '/' && *np != '\0')
 
535
                        np++;
 
536
                if (np - cp == 1 && *cp == '.') {
 
537
                        cp--;
 
538
                        (void) strcpy(cp, np);
 
539
                        np = cp;
 
540
                }
 
541
                if (np - cp == 2 && strncmp(cp, "..", 2) == 0) {
 
542
                        cp--;
 
543
                        while (cp > &canonname[1] && *--cp != '/')
 
544
                                /* find beginning of name */;
 
545
                        (void) strcpy(cp, np);
 
546
                        np = cp;
 
547
                }
 
548
        }
 
549
}
 
550
 
 
551
/*
 
552
 * Do an "ls" style listing of a directory
 
553
 */
 
554
static void
 
555
printlist(char *name, char *basename)
 
556
{
 
557
        struct afile *fp, *list, *listp = NULL;
 
558
        struct direct *dp;
 
559
        struct afile single;
 
560
        RST_DIR *dirp;
 
561
        int entries, len, namelen;
 
562
        char locname[MAXPATHLEN + 1];
 
563
 
 
564
        dp = pathsearch(name);
 
565
        if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
 
566
            (!vflag && dp->d_ino == WINO))
 
567
                return;
 
568
        if ((dirp = rst_opendir(name)) == NULL) {
 
569
                entries = 1;
 
570
                list = &single;
 
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);
 
577
                }
 
578
        } else {
 
579
                entries = 0;
 
580
                while ((dp = rst_readdir(dirp)))
 
581
                        entries++;
 
582
                rst_closedir(dirp);
 
583
                list = (struct afile *)malloc(entries * sizeof(struct afile));
 
584
                if (list == NULL) {
 
585
                        fprintf(stderr, "ls: out of memory\n");
 
586
                        return;
 
587
                }
 
588
                if ((dirp = rst_opendir(name)) == NULL)
 
589
                        panic("directory reopen failed\n");
 
590
                fprintf(stderr, "%s:\n", name);
 
591
                entries = 0;
 
592
                listp = list;
 
593
                namelen = snprintf(locname, sizeof(locname), "%s/", name);
 
594
                if (namelen >= sizeof(locname))
 
595
                        namelen = sizeof(locname) - 1;
 
596
                while ((dp = rst_readdir(dirp))) {
 
597
                        if (dp == NULL)
 
598
                                break;
 
599
                        if (!dflag && TSTINO(dp->d_ino, dumpmap) == 0)
 
600
                                continue;
 
601
                        if (!vflag && (dp->d_ino == WINO ||
 
602
                             strcmp(dp->d_name, ".") == 0 ||
 
603
                             strcmp(dp->d_name, "..") == 0))
 
604
                                continue;
 
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);
 
609
                        } else {
 
610
                                (void) strncat(locname, dp->d_name,
 
611
                                    (int)strlen(dp->d_name));
 
612
                                mkentry(locname, dp, listp++);
 
613
                                entries++;
 
614
                        }
 
615
                }
 
616
                rst_closedir(dirp);
 
617
                if (entries == 0) {
 
618
                        fprintf(stderr, "\n");
 
619
                        free(list);
 
620
                        return;
 
621
                }
 
622
                qsort((char *)list, entries, sizeof(struct afile), fcmp);
 
623
        }
 
624
        formatf(list, entries);
 
625
        if (dirp != NULL) {
 
626
                for (fp = listp - 1; fp >= list; fp--)
 
627
                        freename(fp->fname);
 
628
                fprintf(stderr, "\n");
 
629
                free(list);
 
630
        }
 
631
}
 
632
 
 
633
/*
 
634
 * Read the contents of a directory.
 
635
 */
 
636
static void
 
637
mkentry(char *name, struct direct *dp, struct afile *fp)
 
638
{
 
639
        char *cp;
 
640
        struct entry *np;
 
641
 
 
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))
 
646
                        *cp = '?';
 
647
        fp->len = cp - fp->fname;
 
648
        if (dflag && TSTINO(fp->fnum, dumpmap) == 0)
 
649
                fp->prefix = '^';
 
650
        else if ((np = lookupname(name)) != NULL && (np->e_flags & NEW))
 
651
                fp->prefix = '*';
 
652
        else
 
653
                fp->prefix = ' ';
 
654
        switch(dp->d_type) {
 
655
 
 
656
        default:
 
657
                fprintf(stderr, "Warning: undefined file type %d\n",
 
658
                    dp->d_type);
 
659
                /* fall through */
 
660
        case DT_REG:
 
661
                fp->postfix = ' ';
 
662
                break;
 
663
 
 
664
        case DT_LNK:
 
665
                fp->postfix = '@';
 
666
                break;
 
667
 
 
668
        case DT_FIFO:
 
669
        case DT_SOCK:
 
670
                fp->postfix = '=';
 
671
                break;
 
672
 
 
673
        case DT_CHR:
 
674
        case DT_BLK:
 
675
                fp->postfix = '#';
 
676
                break;
 
677
 
 
678
#ifndef __linux__
 
679
        /* no need for this */
 
680
        case DT_WHT:
 
681
                fp->postfix = '%';
 
682
                break;
 
683
#endif
 
684
 
 
685
        case DT_UNKNOWN:
 
686
        case DT_DIR:
 
687
                if (inodetype(dp->d_ino) == NODE)
 
688
                        fp->postfix = '/';
 
689
                else
 
690
                        fp->postfix = ' ';
 
691
                break;
 
692
        }
 
693
        return;
 
694
}
 
695
 
 
696
/*
 
697
 * Print out a pretty listing of a directory
 
698
 */
 
699
static void
 
700
formatf(struct afile *list, int nentry)
 
701
{
 
702
        struct afile *fp, *endlist;
 
703
        int width, bigino, haveprefix, havepostfix;
 
704
        int i, j, w, precision = 0, columns, lines;
 
705
 
 
706
        width = 0;
 
707
        haveprefix = 0;
 
708
        havepostfix = 0;
 
709
        bigino = ROOTINO;
 
710
        endlist = &list[nentry];
 
711
        for (fp = &list[0]; fp < endlist; fp++) {
 
712
                if (bigino < fp->fnum)
 
713
                        bigino = fp->fnum;
 
714
                if (width < fp->len)
 
715
                        width = fp->len;
 
716
                if (fp->prefix != ' ')
 
717
                        haveprefix = 1;
 
718
                if (fp->postfix != ' ')
 
719
                        havepostfix = 1;
 
720
        }
 
721
        if (haveprefix)
 
722
                width++;
 
723
        if (havepostfix)
 
724
                width++;
 
725
        if (vflag) {
 
726
                for (precision = 0, i = bigino; i > 0; i /= 10)
 
727
                        precision++;
 
728
                width += precision + 1;
 
729
        }
 
730
        width++;
 
731
        columns = 81 / width;
 
732
        if (columns == 0)
 
733
                columns = 1;
 
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];
 
738
                        if (vflag) {
 
739
                                fprintf(stderr, "%*ld ", precision, (long)fp->fnum);
 
740
                                fp->len += precision + 1;
 
741
                        }
 
742
                        if (haveprefix) {
 
743
                                putc(fp->prefix, stderr);
 
744
                                fp->len++;
 
745
                        }
 
746
                        fprintf(stderr, "%s", fp->fname);
 
747
                        if (havepostfix) {
 
748
                                putc(fp->postfix, stderr);
 
749
                                fp->len++;
 
750
                        }
 
751
                        if (fp + lines >= endlist) {
 
752
                                fprintf(stderr, "\n");
 
753
                                break;
 
754
                        }
 
755
                        for (w = fp->len; w < width; w++)
 
756
                                putc(' ', stderr);
 
757
                }
 
758
        }
 
759
}
 
760
 
 
761
/*
 
762
 * Skip over directory entries that are not on the tape
 
763
 *
 
764
 * First have to get definition of a dirent.
 
765
 *
 
766
 * For Linux the dirent struct is now included from bsdcompat.h
 
767
 */
 
768
#ifndef __linux__
 
769
#undef DIRBLKSIZ
 
770
#include <dirent.h>
 
771
#undef d_ino
 
772
#endif  /* ! __linux__ */
 
773
 
 
774
struct dirent *
 
775
glob_readdir(RST_DIR *dirp)
 
776
{
 
777
        struct direct *dp;
 
778
        static struct dirent adirent; 
 
779
 
 
780
        while ((dp = rst_readdir(dirp)) != NULL) {
 
781
                if (!vflag && dp->d_ino == WINO)
 
782
                        continue;
 
783
                if (dflag || TSTINO(dp->d_ino, dumpmap))
 
784
                        break;
 
785
        }
 
786
        if (dp == NULL)
 
787
                return (NULL);
 
788
        adirent.d_fileno = dp->d_ino;
 
789
        memmove(adirent.d_name, dp->d_name, dp->d_namlen + 1);
 
790
        return (&adirent);
 
791
}
 
792
 
 
793
/*
 
794
 * Return st_mode information in response to stat or lstat calls
 
795
 */
 
796
static int
 
797
glob_stat(const char *name, struct stat *stp)
 
798
{
 
799
        struct direct *dp;
 
800
        dp = pathsearch(name);
 
801
        if (dp == NULL || (!dflag && TSTINO(dp->d_ino, dumpmap) == 0) ||
 
802
            (!vflag && dp->d_ino == WINO))
 
803
                return (-1);
 
804
        if (inodetype(dp->d_ino) == NODE)
 
805
                stp->st_mode = IFDIR;
 
806
        else
 
807
                stp->st_mode = IFREG;
 
808
        return (0);
 
809
}
 
810
 
 
811
/*
 
812
 * Comparison routine for qsort.
 
813
 */
 
814
static int
 
815
fcmp(const void *f1, const void *f2)
 
816
{
 
817
        return (strcmp(((struct afile *)f1)->fname,
 
818
            ((struct afile *)f2)->fname));
 
819
}
 
820
 
 
821
/*
 
822
 * respond to interrupts
 
823
 */
 
824
void
 
825
onintr(int signo)
 
826
{
 
827
        int save_errno = errno;
 
828
 
 
829
        if (command == 'i' && runshell)
 
830
                longjmp(reset, 1);
 
831
        if (reply("restore interrupted, continue") == FAIL)
 
832
                exit(1);
 
833
        errno = save_errno;
 
834
}
 
835
 
 
836
 
 
837
#if HAVE_READLINE
 
838
 
 
839
/* A static variable for holding the line. */
 
840
static char *line_read = NULL;
 
841
 
 
842
static char completion_curdir[MAXPATHLEN];
 
843
 
 
844
static char *commands[] = { 
 
845
        "add ", "cd ", "delete ", "extract ", "help ", 
 
846
        "? ", "ls ", "pwd ", "prompt ", "quit ", "xit ", 
 
847
        "verbose ", "setmodes ", "what ", "Debug ",
 
848
        NULL };
 
849
 
 
850
static char *files = NULL;
 
851
 
 
852
static char *
 
853
rl_gets (char *dir)
 
854
{
 
855
        char *prompt;
 
856
        int sz;
 
857
 
 
858
        snprintf(completion_curdir, MAXPATHLEN, "%s", dir);
 
859
        completion_curdir[MAXPATHLEN - 1] = '\0';
 
860
 
 
861
        if (pflag) {
 
862
                sz = 6 + strlen(__progname) + strlen(spcl.c_filesys) + strlen((completion_curdir + 1 ? completion_curdir + 1 : "/"));
 
863
                prompt = (char *)malloc(sz);
 
864
                if (!prompt)
 
865
                        return NULL;
 
866
                snprintf(prompt, sz, "%s:%s:%s > ", 
 
867
                        __progname,
 
868
                        spcl.c_filesys, 
 
869
                        (completion_curdir + 1 ? completion_curdir + 1 : "/"));
 
870
        }
 
871
        else {
 
872
                sz = 4 + strlen(__progname);
 
873
                prompt = (char *)malloc(sz);
 
874
                if (!prompt)
 
875
                        return NULL;
 
876
                snprintf(prompt, sz, "%s > ", __progname);
 
877
        }
 
878
        prompt[sz - 1] = '\0';
 
879
 
 
880
        if (line_read) {
 
881
                free (line_read);
 
882
                line_read = (char *)NULL;
 
883
        }
 
884
 
 
885
        do {
 
886
                line_read = readline (prompt);
 
887
        } while (line_read && !*line_read);
 
888
 
 
889
        free(prompt);
 
890
 
 
891
        if (!line_read) {
 
892
                printf("\n");
 
893
                return strdup("quit");
 
894
        }
 
895
 
 
896
        add_history (line_read);
 
897
 
 
898
        return (line_read);
 
899
}
 
900
 
 
901
static char *
 
902
command_generator(const char *text, int state)
 
903
{
 
904
        static int list_index, len;
 
905
        char *name;
 
906
 
 
907
        if (!state) {
 
908
                list_index = 0;
 
909
                len = strlen(text);
 
910
        }
 
911
 
 
912
        while ( (name = commands[list_index]) != NULL) {
 
913
 
 
914
                list_index ++;
 
915
 
 
916
                if (strncmp(name, text, len) == 0)
 
917
                        return strdup(name);
 
918
        }
 
919
 
 
920
        return NULL;
 
921
}
 
922
 
 
923
static char *
 
924
filename_generator(const char *text, int state)
 
925
{
 
926
        static int list_index;
 
927
        char *name;
 
928
        RST_DIR *dirp;
 
929
        struct direct *dp;
 
930
        static int entries;
 
931
        char pname[MAXPATHLEN];
 
932
        char fname[MAXPATHLEN];
 
933
        char *slash;
 
934
        char ppname[MAXPATHLEN];
 
935
 
 
936
        if (!state) {
 
937
                list_index = 0;
 
938
 
 
939
                if (files != NULL) {
 
940
                        free(files);
 
941
                        entries = 0;
 
942
                        files = NULL;
 
943
                }
 
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';
 
950
                        ppname[idx] = '\0';
 
951
                        if (text[0] == '/')
 
952
                                snprintf(pname, MAXPATHLEN, ".%s", ppname);
 
953
                        else
 
954
                                snprintf(pname, MAXPATHLEN, "%s/%s", completion_curdir, ppname);
 
955
                        strncpy(fname, ppname + idx + 1, MAXPATHLEN);
 
956
                        ppname[idx] = '/';
 
957
                        ppname[idx + 1] = '\0';
 
958
                }
 
959
                else {
 
960
                        strncpy(pname, completion_curdir, MAXPATHLEN);
 
961
                        strncpy(fname, text, MAXPATHLEN);
 
962
                        ppname[0] = '\0';
 
963
                }
 
964
                pname[MAXPATHLEN - 1] = '\0';
 
965
                fname[MAXPATHLEN - 1] = '\0';
 
966
                if ((dirp = rst_opendir(pname)) == NULL)
 
967
                        return NULL;
 
968
                entries = 0;
 
969
                while ((dp = rst_readdir(dirp)))
 
970
                        entries++;
 
971
                rst_closedir(dirp);
 
972
                files = (char *)malloc(entries * MAXPATHLEN);
 
973
                if (files == NULL) {
 
974
                        fprintf(stderr, "Out of memory\n");
 
975
                        entries = 0;
 
976
                        return NULL;
 
977
                }
 
978
                if ((dirp = rst_opendir(pname)) == NULL)
 
979
                        panic("directory reopen failed\n");
 
980
                entries = 0;
 
981
                while ((dp = rst_readdir(dirp))) {
 
982
                        if (TSTINO(dp->d_ino, dumpmap) == 0)
 
983
                                continue;
 
984
                        if (strcmp(dp->d_name, ".") == 0 ||
 
985
                            strcmp(dp->d_name, "..") == 0)
 
986
                                continue;
 
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);
 
990
                                else
 
991
                                        snprintf(files + entries * MAXPATHLEN, MAXPATHLEN, "%s%s ", ppname, dp->d_name);
 
992
                                *(files + (entries + 1) * MAXPATHLEN - 1) = '\0';
 
993
                                ++entries;
 
994
                        }
 
995
                 }
 
996
                 rst_closedir(dirp);
 
997
        }
 
998
 
 
999
        if (list_index >= entries)
 
1000
                return NULL;
 
1001
 
 
1002
        name = strdup(files + list_index * MAXPATHLEN);
 
1003
        list_index ++;
 
1004
 
 
1005
        return name;
 
1006
}
 
1007
 
 
1008
static char **
 
1009
restore_completion (const char *text, int start, int end)
 
1010
{
 
1011
        char **matches;
 
1012
 
 
1013
        if (start == 0)
 
1014
                matches = rl_completion_matches (text, command_generator);
 
1015
        else
 
1016
                matches = rl_completion_matches (text, filename_generator);
 
1017
 
 
1018
        return (matches);
 
1019
}
 
1020
 
 
1021
static void 
 
1022
initialize_readline(void) 
 
1023
{
 
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;
 
1029
}
 
1030
 
 
1031
#endif /* HAVE_READLINE */