~db-keen/tanzanite/rat

« back to all changes in this revision

Viewing changes to sources/libarchive-1.2.53/tar/util.c

  • Committer: austin
  • Date: 2007-02-05 03:24:13 UTC
  • Revision ID: svn-v4:8b90d6ed-dc11-0410-98dd-e2d1db709ad4:rat/spikes/2006-05-13:45
Reorganizing the RAT project.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-
 
2
 * Copyright (c) 2003-2004 Tim Kientzle
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 * 1. Redistributions of source code must retain the above copyright
 
9
 *    notice, this list of conditions and the following disclaimer
 
10
 *    in this position and unchanged.
 
11
 * 2. Redistributions in binary form must reproduce the above copyright
 
12
 *    notice, this list of conditions and the following disclaimer in the
 
13
 *    documentation and/or other materials provided with the distribution.
 
14
 *
 
15
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 
16
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
17
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
18
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 
19
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
20
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
21
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
22
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
23
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
24
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
25
 */
 
26
 
 
27
#include "bsdtar_platform.h"
 
28
__FBSDID("$FreeBSD: src/usr.bin/tar/util.c,v 1.14 2006/03/21 17:03:51 kientzle Exp $");
 
29
 
 
30
#include <sys/stat.h>
 
31
#include <sys/types.h>  /* Linux doesn't define mode_t, etc. in sys/stat.h. */
 
32
#include <ctype.h>
 
33
#include <errno.h>
 
34
#include <stdarg.h>
 
35
#include <stdio.h>
 
36
#include <stdlib.h>
 
37
#include <string.h>
 
38
 
 
39
#include "bsdtar.h"
 
40
 
 
41
static void     bsdtar_vwarnc(struct bsdtar *, int code,
 
42
                    const char *fmt, va_list ap);
 
43
 
 
44
/*
 
45
 * Print a string, taking care with any non-printable characters.
 
46
 */
 
47
 
 
48
void
 
49
safe_fprintf(FILE *f, const char *fmt, ...)
 
50
{
 
51
        char *buff;
 
52
        char *buff_heap;
 
53
        int buff_length;
 
54
        int length;
 
55
        va_list ap;
 
56
        char *p;
 
57
        unsigned i;
 
58
        char buff_stack[256];
 
59
        char copy_buff[256];
 
60
 
 
61
        /* Use a stack-allocated buffer if we can, for speed and safety. */
 
62
        buff_heap = NULL;
 
63
        buff_length = sizeof(buff_stack);
 
64
        buff = buff_stack;
 
65
 
 
66
        va_start(ap, fmt);
 
67
        length = vsnprintf(buff, buff_length, fmt, ap);
 
68
        va_end(ap);
 
69
        /* If the result is too large, allocate a buffer on the heap. */
 
70
        if (length >= buff_length) {
 
71
                buff_length = length+1;
 
72
                buff_heap = malloc(buff_length);
 
73
                /* Failsafe: use the truncated string if malloc fails. */
 
74
                if (buff_heap != NULL) {
 
75
                        buff = buff_heap;
 
76
                        va_start(ap, fmt);
 
77
                        length = vsnprintf(buff, buff_length, fmt, ap);
 
78
                        va_end(ap);
 
79
                }
 
80
        }
 
81
 
 
82
        /* Write data, expanding unprintable characters. */
 
83
        p = buff;
 
84
        i = 0;
 
85
        while (*p != '\0') {
 
86
                unsigned char c = *p++;
 
87
 
 
88
                if (isprint(c) && c != '\\')
 
89
                        copy_buff[i++] = c;
 
90
                else {
 
91
                        copy_buff[i++] = '\\';
 
92
                        switch (c) {
 
93
                        case '\a': copy_buff[i++] = 'a'; break;
 
94
                        case '\b': copy_buff[i++] = 'b'; break;
 
95
                        case '\f': copy_buff[i++] = 'f'; break;
 
96
                        case '\n': copy_buff[i++] = 'n'; break;
 
97
#if '\r' != '\n'
 
98
                        /* On some platforms, \n and \r are the same. */
 
99
                        case '\r': copy_buff[i++] = 'r'; break;
 
100
#endif
 
101
                        case '\t': copy_buff[i++] = 't'; break;
 
102
                        case '\v': copy_buff[i++] = 'v'; break;
 
103
                        case '\\': copy_buff[i++] = '\\'; break;
 
104
                        default:
 
105
                                sprintf(copy_buff + i, "%03o", c);
 
106
                                i += 3;
 
107
                        }
 
108
                }
 
109
 
 
110
                /* If our temp buffer is full, dump it and keep going. */
 
111
                if (i > (sizeof(copy_buff) - 8)) {
 
112
                        copy_buff[i++] = '\0';
 
113
                        fprintf(f, "%s", copy_buff);
 
114
                        i = 0;
 
115
                }
 
116
        }
 
117
        copy_buff[i++] = '\0';
 
118
        fprintf(f, "%s", copy_buff);
 
119
 
 
120
        /* If we allocated a heap-based buffer, free it now. */
 
121
        if (buff_heap != NULL)
 
122
                free(buff_heap);
 
123
}
 
124
 
 
125
static void
 
126
bsdtar_vwarnc(struct bsdtar *bsdtar, int code, const char *fmt, va_list ap)
 
127
{
 
128
        fprintf(stderr, "%s: ", bsdtar->progname);
 
129
        vfprintf(stderr, fmt, ap);
 
130
        if (code != 0)
 
131
                fprintf(stderr, ": %s", strerror(code));
 
132
        fprintf(stderr, "\n");
 
133
}
 
134
 
 
135
void
 
136
bsdtar_warnc(struct bsdtar *bsdtar, int code, const char *fmt, ...)
 
137
{
 
138
        va_list ap;
 
139
 
 
140
        va_start(ap, fmt);
 
141
        bsdtar_vwarnc(bsdtar, code, fmt, ap);
 
142
        va_end(ap);
 
143
}
 
144
 
 
145
void
 
146
bsdtar_errc(struct bsdtar *bsdtar, int eval, int code, const char *fmt, ...)
 
147
{
 
148
        va_list ap;
 
149
 
 
150
        va_start(ap, fmt);
 
151
        bsdtar_vwarnc(bsdtar, code, fmt, ap);
 
152
        va_end(ap);
 
153
        exit(eval);
 
154
}
 
155
 
 
156
int
 
157
yes(const char *fmt, ...)
 
158
{
 
159
        char buff[32];
 
160
        char *p;
 
161
        ssize_t l;
 
162
 
 
163
        va_list ap;
 
164
        va_start(ap, fmt);
 
165
        vfprintf(stderr, fmt, ap);
 
166
        va_end(ap);
 
167
        fprintf(stderr, " (y/N)? ");
 
168
        fflush(stderr);
 
169
 
 
170
        l = read(2, buff, sizeof(buff));
 
171
        if (l <= 0)
 
172
                return (0);
 
173
        buff[l] = 0;
 
174
 
 
175
        for (p = buff; *p != '\0'; p++) {
 
176
                if (isspace(0xff & (int)*p))
 
177
                        continue;
 
178
                switch(*p) {
 
179
                case 'y': case 'Y':
 
180
                        return (1);
 
181
                case 'n': case 'N':
 
182
                        return (0);
 
183
                default:
 
184
                        return (0);
 
185
                }
 
186
        }
 
187
 
 
188
        return (0);
 
189
}
 
190
 
 
191
void
 
192
bsdtar_strmode(struct archive_entry *entry, char *bp)
 
193
{
 
194
        static const char *perms = "?rwxrwxrwx ";
 
195
        static const mode_t permbits[] =
 
196
            { S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP,
 
197
              S_IROTH, S_IWOTH, S_IXOTH };
 
198
        mode_t mode;
 
199
        int i;
 
200
 
 
201
        /* Fill in a default string, then selectively override. */
 
202
        strcpy(bp, perms);
 
203
 
 
204
        mode = archive_entry_mode(entry);
 
205
        switch (mode & S_IFMT) {
 
206
        case S_IFREG:  bp[0] = '-'; break;
 
207
        case S_IFBLK:  bp[0] = 'b'; break;
 
208
        case S_IFCHR:  bp[0] = 'c'; break;
 
209
        case S_IFDIR:  bp[0] = 'd'; break;
 
210
        case S_IFLNK:  bp[0] = 'l'; break;
 
211
        case S_IFSOCK: bp[0] = 's'; break;
 
212
#ifdef S_IFIFO
 
213
        case S_IFIFO:  bp[0] = 'p'; break;
 
214
#endif
 
215
#ifdef S_IFWHT
 
216
        case S_IFWHT:  bp[0] = 'w'; break;
 
217
#endif
 
218
        }
 
219
 
 
220
        for (i = 0; i < 9; i++)
 
221
                if (!(mode & permbits[i]))
 
222
                        bp[i+1] = '-';
 
223
 
 
224
        if (mode & S_ISUID) {
 
225
                if (mode & S_IXUSR) bp[3] = 's';
 
226
                else bp[3] = 'S';
 
227
        }
 
228
        if (mode & S_ISGID) {
 
229
                if (mode & S_IXGRP) bp[6] = 's';
 
230
                else bp[6] = 'S';
 
231
        }
 
232
        if (mode & S_ISVTX) {
 
233
                if (mode & S_IXOTH) bp[9] = 't';
 
234
                else bp[9] = 'T';
 
235
        }
 
236
        if (archive_entry_acl_count(entry, ARCHIVE_ENTRY_ACL_TYPE_ACCESS))
 
237
                bp[10] = '+';
 
238
}
 
239
 
 
240
 
 
241
/*
 
242
 * Read lines from file and do something with each one.  If option_null
 
243
 * is set, lines are terminated with zero bytes; otherwise, they're
 
244
 * terminated with newlines.
 
245
 *
 
246
 * This uses a self-sizing buffer to handle arbitrarily-long lines.
 
247
 * If the "process" function returns non-zero for any line, this
 
248
 * function will return non-zero after attempting to process all
 
249
 * remaining lines.
 
250
 */
 
251
int
 
252
process_lines(struct bsdtar *bsdtar, const char *pathname,
 
253
    int (*process)(struct bsdtar *, const char *))
 
254
{
 
255
        FILE *f;
 
256
        char *buff, *buff_end, *line_start, *line_end, *p;
 
257
        size_t buff_length, bytes_read, bytes_wanted;
 
258
        int separator;
 
259
        int ret;
 
260
 
 
261
        separator = bsdtar->option_null ? '\0' : '\n';
 
262
        ret = 0;
 
263
 
 
264
        if (strcmp(pathname, "-") == 0)
 
265
                f = stdin;
 
266
        else
 
267
                f = fopen(pathname, "r");
 
268
        if (f == NULL)
 
269
                bsdtar_errc(bsdtar, 1, errno, "Couldn't open %s", pathname);
 
270
        buff_length = 8192;
 
271
        buff = malloc(buff_length);
 
272
        if (buff == NULL)
 
273
                bsdtar_errc(bsdtar, 1, ENOMEM, "Can't read %s", pathname);
 
274
        line_start = line_end = buff_end = buff;
 
275
        for (;;) {
 
276
                /* Get some more data into the buffer. */
 
277
                bytes_wanted = buff + buff_length - buff_end;
 
278
                bytes_read = fread(buff_end, 1, bytes_wanted, f);
 
279
                buff_end += bytes_read;
 
280
                /* Process all complete lines in the buffer. */
 
281
                while (line_end < buff_end) {
 
282
                        if (*line_end == separator) {
 
283
                                *line_end = '\0';
 
284
                                if ((*process)(bsdtar, line_start) != 0)
 
285
                                        ret = -1;
 
286
                                line_start = line_end + 1;
 
287
                                line_end = line_start;
 
288
                        } else
 
289
                                line_end++;
 
290
                }
 
291
                if (feof(f))
 
292
                        break;
 
293
                if (ferror(f))
 
294
                        bsdtar_errc(bsdtar, 1, errno,
 
295
                            "Can't read %s", pathname);
 
296
                if (line_start > buff) {
 
297
                        /* Move a leftover fractional line to the beginning. */
 
298
                        memmove(buff, line_start, buff_end - line_start);
 
299
                        buff_end -= line_start - buff;
 
300
                        line_end -= line_start - buff;
 
301
                        line_start = buff;
 
302
                } else {
 
303
                        /* Line is too big; enlarge the buffer. */
 
304
                        p = realloc(buff, buff_length *= 2);
 
305
                        if (p == NULL)
 
306
                                bsdtar_errc(bsdtar, 1, ENOMEM,
 
307
                                    "Line too long in %s", pathname);
 
308
                        buff_end = p + (buff_end - buff);
 
309
                        line_end = p + (line_end - buff);
 
310
                        line_start = buff = p;
 
311
                }
 
312
        }
 
313
        /* At end-of-file, handle the final line. */
 
314
        if (line_end > line_start) {
 
315
                *line_end = '\0';
 
316
                if ((*process)(bsdtar, line_start) != 0)
 
317
                        ret = -1;
 
318
        }
 
319
        free(buff);
 
320
        if (f != stdin)
 
321
                fclose(f);
 
322
        return (ret);
 
323
}
 
324
 
 
325
/*-
 
326
 * The logic here for -C <dir> attempts to avoid
 
327
 * chdir() as long as possible.  For example:
 
328
 * "-C /foo -C /bar file"          needs chdir("/bar") but not chdir("/foo")
 
329
 * "-C /foo -C bar file"           needs chdir("/foo/bar")
 
330
 * "-C /foo -C bar /file1"         does not need chdir()
 
331
 * "-C /foo -C bar /file1 file2"   needs chdir("/foo/bar") before file2
 
332
 *
 
333
 * The only correct way to handle this is to record a "pending" chdir
 
334
 * request and combine multiple requests intelligently until we
 
335
 * need to process a non-absolute file.  set_chdir() adds the new dir
 
336
 * to the pending list; do_chdir() actually executes any pending chdir.
 
337
 *
 
338
 * This way, programs that build tar command lines don't have to worry
 
339
 * about -C with non-existent directories; such requests will only
 
340
 * fail if the directory must be accessed.
 
341
 */
 
342
void
 
343
set_chdir(struct bsdtar *bsdtar, const char *newdir)
 
344
{
 
345
        if (newdir[0] == '/') {
 
346
                /* The -C /foo -C /bar case; dump first one. */
 
347
                free(bsdtar->pending_chdir);
 
348
                bsdtar->pending_chdir = NULL;
 
349
        }
 
350
        if (bsdtar->pending_chdir == NULL)
 
351
                /* Easy case: no previously-saved dir. */
 
352
                bsdtar->pending_chdir = strdup(newdir);
 
353
        else {
 
354
                /* The -C /foo -C bar case; concatenate */
 
355
                char *old_pending = bsdtar->pending_chdir;
 
356
                size_t old_len = strlen(old_pending);
 
357
                bsdtar->pending_chdir = malloc(old_len + strlen(newdir) + 2);
 
358
                if (old_pending[old_len - 1] == '/')
 
359
                        old_pending[old_len - 1] = '\0';
 
360
                if (bsdtar->pending_chdir != NULL)
 
361
                        sprintf(bsdtar->pending_chdir, "%s/%s",
 
362
                            old_pending, newdir);
 
363
                free(old_pending);
 
364
        }
 
365
        if (bsdtar->pending_chdir == NULL)
 
366
                bsdtar_errc(bsdtar, 1, errno, "No memory");
 
367
}
 
368
 
 
369
void
 
370
do_chdir(struct bsdtar *bsdtar)
 
371
{
 
372
        if (bsdtar->pending_chdir == NULL)
 
373
                return;
 
374
 
 
375
        if (chdir(bsdtar->pending_chdir) != 0) {
 
376
                bsdtar_errc(bsdtar, 1, 0, "could not chdir to '%s'\n",
 
377
                    bsdtar->pending_chdir);
 
378
        }
 
379
        free(bsdtar->pending_chdir);
 
380
        bsdtar->pending_chdir = NULL;
 
381
}
 
382
 
 
383
/*
 
384
 * Handle --strip-components and any future path-rewriting options.
 
385
 * Returns non-zero if the pathname should not be extracted.
 
386
 */
 
387
int
 
388
edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry)
 
389
{
 
390
        const char *name = archive_entry_pathname(entry);
 
391
 
 
392
        /* Strip leading dir names as per --strip-components option. */
 
393
        if (bsdtar->strip_components > 0) {
 
394
                int r = bsdtar->strip_components;
 
395
                const char *p = name;
 
396
 
 
397
                while (r > 0) {
 
398
                        switch (*p++) {
 
399
                        case '/':
 
400
                                r--;
 
401
                                name = p;
 
402
                                break;
 
403
                        case '\0':
 
404
                                /* Path is too short, skip it. */
 
405
                                return (1);
 
406
                        }
 
407
                }
 
408
        }
 
409
 
 
410
        /* Strip redundant "./" from start of filename. */
 
411
        if (name[0] == '.' && name[1] == '/' && name[2] != '\0')
 
412
                name += 2;
 
413
 
 
414
        /* Strip redundant leading '/' characters. */
 
415
        while (name[0] == '/' && name[1] == '/')
 
416
                name++;
 
417
 
 
418
        /* Strip leading '/' unless user has asked us not to. */
 
419
        if (name[0] == '/' && !bsdtar->option_absolute_paths) {
 
420
                /* Generate a warning the first time this happens. */
 
421
                if (!bsdtar->warned_lead_slash) {
 
422
                        bsdtar_warnc(bsdtar, 0,
 
423
                            "Removing leading '/' from member names");
 
424
                        bsdtar->warned_lead_slash = 1;
 
425
                }
 
426
                name++;
 
427
                /* Special case: Stripping leading '/' from "/" yields ".". */
 
428
                if (*name == '\0')
 
429
                        name = ".";
 
430
        }
 
431
 
 
432
        /* Safely replace name in archive_entry. */
 
433
        if (name != archive_entry_pathname(entry)) {
 
434
                char *q = strdup(name);
 
435
                archive_entry_copy_pathname(entry, q);
 
436
                free(q);
 
437
        }
 
438
        return (0);
 
439
}