summaryrefslogtreecommitdiffstats
path: root/usr.bin/tar/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/tar/util.c')
-rw-r--r--usr.bin/tar/util.c153
1 files changed, 46 insertions, 107 deletions
diff --git a/usr.bin/tar/util.c b/usr.bin/tar/util.c
index b3df5f0..858d472 100644
--- a/usr.bin/tar/util.c
+++ b/usr.bin/tar/util.c
@@ -36,9 +36,15 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
@@ -59,6 +65,10 @@ __FBSDID("$FreeBSD$");
static size_t bsdtar_expand_char(char *, size_t, char);
static const char *strip_components(const char *path, int elements);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#define read _read
+#endif
+
/* TODO: Hack up a version of mbtowc for platforms with no wide
* character support at all. I think the following might suffice,
* but it needs careful testing.
@@ -87,7 +97,7 @@ safe_fprintf(FILE *f, const char *fmt, ...)
char *fmtbuff_heap; /* If fmtbuff_stack is too small, we use malloc */
char *fmtbuff; /* Pointer to fmtbuff_stack or fmtbuff_heap. */
int fmtbuff_length;
- int length;
+ int length, n;
va_list ap;
const char *p;
unsigned i;
@@ -124,14 +134,13 @@ safe_fprintf(FILE *f, const char *fmt, ...)
/* Note: mbrtowc() has a cleaner API, but mbtowc() seems a bit
* more portable, so we use that here instead. */
- mbtowc(NULL, NULL, 0); /* Reset the shift state. */
+ n = mbtowc(NULL, NULL, 1); /* Reset the shift state. */
/* Write data, expanding unprintable characters. */
p = fmtbuff;
i = 0;
try_wc = 1;
while (*p != '\0') {
- int n;
/* Convert to wide char, test if the wide
* char is printable in the current locale. */
@@ -144,24 +153,24 @@ safe_fprintf(FILE *f, const char *fmt, ...)
} else {
/* Not printable, format the bytes. */
while (n-- > 0)
- i += bsdtar_expand_char(
+ i += (unsigned)bsdtar_expand_char(
outbuff, i, *p++);
}
} else {
/* After any conversion failure, don't bother
* trying to convert the rest. */
- i += bsdtar_expand_char(outbuff, i, *p++);
+ i += (unsigned)bsdtar_expand_char(outbuff, i, *p++);
try_wc = 0;
}
/* If our output buffer is full, dump it and keep going. */
if (i > (sizeof(outbuff) - 20)) {
- outbuff[i++] = '\0';
+ outbuff[i] = '\0';
fprintf(f, "%s", outbuff);
i = 0;
}
}
- outbuff[i++] = '\0';
+ outbuff[i] = '\0';
fprintf(f, "%s", outbuff);
/* If we allocated a heap-based formatting buffer, free it now. */
@@ -237,95 +246,6 @@ yes(const char *fmt, ...)
return (0);
}
-/*
- * Read lines from file and do something with each one. If option_null
- * is set, lines are terminated with zero bytes; otherwise, they're
- * terminated with newlines.
- *
- * This uses a self-sizing buffer to handle arbitrarily-long lines.
- * If the "process" function returns non-zero for any line, this
- * function will return non-zero after attempting to process all
- * remaining lines.
- */
-int
-process_lines(struct bsdtar *bsdtar, const char *pathname,
- int (*process)(struct bsdtar *, const char *))
-{
- FILE *f;
- char *buff, *buff_end, *line_start, *line_end, *p;
- size_t buff_length, new_buff_length, bytes_read, bytes_wanted;
- int separator;
- int ret;
-
- separator = bsdtar->option_null ? '\0' : '\n';
- ret = 0;
-
- if (strcmp(pathname, "-") == 0)
- f = stdin;
- else
- f = fopen(pathname, "r");
- if (f == NULL)
- bsdtar_errc(1, errno, "Couldn't open %s", pathname);
- buff_length = 8192;
- buff = malloc(buff_length);
- if (buff == NULL)
- bsdtar_errc(1, ENOMEM, "Can't read %s", pathname);
- line_start = line_end = buff_end = buff;
- for (;;) {
- /* Get some more data into the buffer. */
- bytes_wanted = buff + buff_length - buff_end;
- bytes_read = fread(buff_end, 1, bytes_wanted, f);
- buff_end += bytes_read;
- /* Process all complete lines in the buffer. */
- while (line_end < buff_end) {
- if (*line_end == separator) {
- *line_end = '\0';
- if ((*process)(bsdtar, line_start) != 0)
- ret = -1;
- line_start = line_end + 1;
- line_end = line_start;
- } else
- line_end++;
- }
- if (feof(f))
- break;
- if (ferror(f))
- bsdtar_errc(1, errno,
- "Can't read %s", pathname);
- if (line_start > buff) {
- /* Move a leftover fractional line to the beginning. */
- memmove(buff, line_start, buff_end - line_start);
- buff_end -= line_start - buff;
- line_end -= line_start - buff;
- line_start = buff;
- } else {
- /* Line is too big; enlarge the buffer. */
- new_buff_length = buff_length * 2;
- if (new_buff_length <= buff_length)
- bsdtar_errc(1, ENOMEM,
- "Line too long in %s", pathname);
- buff_length = new_buff_length;
- p = realloc(buff, buff_length);
- if (p == NULL)
- bsdtar_errc(1, ENOMEM,
- "Line too long in %s", pathname);
- buff_end = p + (buff_end - buff);
- line_end = p + (line_end - buff);
- line_start = buff = p;
- }
- }
- /* At end-of-file, handle the final line. */
- if (line_end > line_start) {
- *line_end = '\0';
- if ((*process)(bsdtar, line_start) != 0)
- ret = -1;
- }
- free(buff);
- if (f != stdin)
- fclose(f);
- return (ret);
-}
-
/*-
* The logic here for -C <dir> attempts to avoid
* chdir() as long as possible. For example:
@@ -342,6 +262,8 @@ process_lines(struct bsdtar *bsdtar, const char *pathname,
* This way, programs that build tar command lines don't have to worry
* about -C with non-existent directories; such requests will only
* fail if the directory must be accessed.
+ *
+ * TODO: Make this handle Windows paths correctly.
*/
void
set_chdir(struct bsdtar *bsdtar, const char *newdir)
@@ -384,16 +306,17 @@ do_chdir(struct bsdtar *bsdtar)
bsdtar->pending_chdir = NULL;
}
-const char *
-strip_components(const char *path, int elements)
+static const char *
+strip_components(const char *p, int elements)
{
- const char *p = path;
-
+ /* Skip as many elements as necessary. */
while (elements > 0) {
switch (*p++) {
case '/':
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ case '\\': /* Support \ path sep on Windows ONLY. */
+#endif
elements--;
- path = p;
break;
case '\0':
/* Path is too short, skip it. */
@@ -401,12 +324,25 @@ strip_components(const char *path, int elements)
}
}
- while (*path == '/')
- ++path;
- if (*path == '\0')
- return (NULL);
-
- return (path);
+ /* Skip any / characters. This handles short paths that have
+ * additional / termination. This also handles the case where
+ * the logic above stops in the middle of a duplicate //
+ * sequence (which would otherwise get converted to an
+ * absolute path). */
+ for (;;) {
+ switch (*p) {
+ case '/':
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ case '\\': /* Support \ path sep on Windows ONLY. */
+#endif
+ ++p;
+ break;
+ case '\0':
+ return (NULL);
+ default:
+ return (p);
+ }
+ }
}
/*
@@ -590,6 +526,9 @@ tar_i64toa(int64_t n0)
* TODO: Publish the path normalization routines in libarchive so
* that bsdtar can normalize paths and use fast strcmp() instead
* of this.
+ *
+ * Note: This is currently only used within write.c, so should
+ * not handle \ path separators.
*/
int
OpenPOWER on IntegriCloud