diff --git a/NEWS.md b/NEWS.md index 4e24e115..a9d1d414 100644 --- a/NEWS.md +++ b/NEWS.md @@ -11,6 +11,10 @@ each archive name with a null character (like `find -print0`). If one or more -v arguments are specified, multiple null characters are used to separate fields; see the man page for details. +- tarsnap now accepts --null-output with -x and -t, which causes them to + separate each filename with a null character. If there are multiple fields + on a line, null characters are used instead of spaces; see the man page for + details. - tarsnap now accepts --null-input as a synonym for --null. For compatibility reasons, --null is still supported, and will not be deprecated. - tarsnap now accepts --hashes, which causes --list-archives to print hashes diff --git a/misc/zsh_completion/_tarsnap b/misc/zsh_completion/_tarsnap index 435c874a..3bd2d06e 100644 --- a/misc/zsh_completion/_tarsnap +++ b/misc/zsh_completion/_tarsnap @@ -719,6 +719,7 @@ _shtab_tarsnap_t_options=( "--include[process only certain files or directories]:pattern:" "--iso-dates[print dates as yyyy-mm-dd hh\:mm\:ss]" "--newer[only add files and dirs newer than ARG]:date:" + "--null-output[output split by NULs instead of newlines]" "-O[write files to stdout (-x) or stderr (-t)]" "-P[preserve pathnames]" "-q[stop after the first selected entry]" @@ -782,6 +783,7 @@ _shtab_tarsnap_x_options=( "--keep-newer-files[do not overwrite newer existing files]" "-m[do not extract modification time]" "--newer[only add files and dirs newer than ARG]:date:" + "--null-output[output split by NULs instead of newlines]" "--numeric-owner[ignore symbolic ownership when restoring]" "-O[write files to stdout (-x) or stderr (-t)]" "-o[use the current uid and gid\; requires -p]" diff --git a/tar/bsdtar.c b/tar/bsdtar.c index ec5d92bc..55d61770 100644 --- a/tar/bsdtar.c +++ b/tar/bsdtar.c @@ -1054,7 +1054,7 @@ main(int argc, char **argv) only_mode(bsdtar, "--null-input", "cxt"); if (bsdtar->option_null_output) { if (bsdtar->mode != OPTION_LIST_ARCHIVES) - only_mode(bsdtar, "--null-output", ""); + only_mode(bsdtar, "--null-output", "xt"); } /* We should only have remaining args in -c, -t, and -x modes. */ diff --git a/tar/bsdtar.h b/tar/bsdtar.h index b8f8feee..b3b1ae8d 100644 --- a/tar/bsdtar.h +++ b/tar/bsdtar.h @@ -332,5 +332,6 @@ int apply_substitution(struct bsdtar *, const char *, char **, int); void cleanup_substitution(struct bsdtar *); #endif +void print_sep(struct bsdtar *, FILE *, char, int); void list_item_verbose(struct bsdtar *, FILE *, struct archive_entry *); diff --git a/tar/read.c b/tar/read.c index d24de312..1c90e064 100644 --- a/tar/read.c +++ b/tar/read.c @@ -282,23 +282,23 @@ read_archive(struct bsdtar *bsdtar, char mode) fflush(out); r = archive_read_data_skip(a); if (r == ARCHIVE_WARN) { - fprintf(out, "\n"); + print_sep(bsdtar, out, '\n', 1); bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); } if (r == ARCHIVE_RETRY) { - fprintf(out, "\n"); + print_sep(bsdtar, out, '\n', 1); bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); } if (r == ARCHIVE_FATAL) { - fprintf(out, "\n"); + print_sep(bsdtar, out, '\n', 1); bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); bsdtar->return_value = 1; break; } - fprintf(out, "\n"); + print_sep(bsdtar, out, '\n', 1); } else { /* Note: some rewrite failures prevent extraction. */ if (edit_pathname(bsdtar, entry)) @@ -363,11 +363,11 @@ read_archive(struct bsdtar *bsdtar, char mode) safe_fprintf(stderr, ": %s", archive_error_string(a)); if (!bsdtar->verbose) - fprintf(stderr, "\n"); + print_sep(bsdtar, stderr, '\n', 1); bsdtar->return_value = 1; } if (bsdtar->verbose) - fprintf(stderr, "\n"); + print_sep(bsdtar, stderr, '\n', 1); if (r == ARCHIVE_FATAL) break; } @@ -386,9 +386,11 @@ read_archive(struct bsdtar *bsdtar, char mode) if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; - if (bsdtar->verbose > 2) - fprintf(stdout, "Archive Format: %s, Compression: %s\n", + if (bsdtar->verbose > 2) { + fprintf(stdout, "Archive Format: %s, Compression: %s", archive_format_name(a), archive_compression_name(a)); + print_sep(bsdtar, stdout, '\n', 1); + } /* Always print a final message for --progress-bytes. */ if ((mode == 'x') && (bsdtar->option_progress_bytes != 0)) diff --git a/tar/tarsnap.1-man.in b/tar/tarsnap.1-man.in index b07b952c..17ca6bdc 100644 --- a/tar/tarsnap.1-man.in +++ b/tar/tarsnap.1-man.in @@ -110,19 +110,6 @@ flag is specified two or more times, the command line with which \fB\%tarsnap\fP was invoked to create each archive is also printed. -.PP -If the -\fB\--null-output\fP -argument is also specified, each archive name will be separated by a single -null character. -If the -\fB\-v\fP -flag is also specified, then the creation time will be separated by two null -characters. -If the -\fB\-v\fP -flag is specified two times, then the arguments in the command line will be -separated by three null characters. .TP \fB\--print-stats\fP Print global statistics concerning the archives stored, and optionally @@ -752,8 +739,14 @@ option to \fBfind\fP(1). .TP \fB\--null-output\fP -(list-archives mode only) -Archive names in output are separated by null characters, not by newlines. +(x, t, and list-archives modes only) +Archive names and filenames in output are separated by null characters, not by +newlines. +.PP +If there are multiple fields on a line, they will be separated by two null +characters. +If a command line is printed within one of those fields, each argument will be +separated by three null characters. .TP \fB\--numeric-owner\fP (x mode only) diff --git a/tar/tarsnap.1-mdoc.in b/tar/tarsnap.1-mdoc.in index 0ce3f58b..ce927e95 100644 --- a/tar/tarsnap.1-mdoc.in +++ b/tar/tarsnap.1-mdoc.in @@ -121,19 +121,6 @@ flag is specified two or more times, the command line with which .Nm was invoked to create each archive is also printed. -.Pp -If the -.Fl -null-output -argument is also specified, each archive name will be separated by a single -null character. -If the -.Fl v -flag is also specified, then the creation time will be separated by two null -characters. -If the -.Fl v -flag is specified two times, then the arguments in the command line will be -separated by three null characters. .It Fl -print-stats Print global statistics concerning the archives stored, and optionally information about individual archive(s). @@ -680,8 +667,14 @@ This is often used to read filenames output by the option to .Xr find 1 . .It Fl -null-output -(list-archives mode only) -Archive names in output are separated by null characters, not by newlines. +(x, t, and list-archives modes only) +Archive names and filenames in output are separated by null characters, not by +newlines. +.Pp +If there are multiple fields on a line, they will be separated by two null +characters. +If a command line is printed within one of those fields, each argument will be +separated by three null characters. .It Fl -numeric-owner (x mode only) Ignore symbolic user and group names when restoring archives to disk, diff --git a/tar/util.c b/tar/util.c index ffb59714..59c25edf 100644 --- a/tar/util.c +++ b/tar/util.c @@ -686,6 +686,22 @@ pathcmp(const char *a, const char *b) return (*(const unsigned char *)a - *(const unsigned char *)b); } +/* Print ${sep} if appropriate; otherwise, print ${num} NULs. */ +void +print_sep(struct bsdtar *bsdtar, FILE * out, char sep, int num) +{ + int i; + + if (bsdtar->option_null_output) { + /* Print the specified number of NULs. */ + for (i = 0; i < num; i++) + fprintf(out, "%c", '\0'); + } else { + /* Print the normal separator. */ + fprintf(out, "%c", sep); + } +} + /* * Display information about the current file. * @@ -720,20 +736,25 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) } if (!now) time(&now); - fprintf(out, "%s %d ", - archive_entry_strmode(entry), - (int)(st->st_nlink)); + fprintf(out, "%s", archive_entry_strmode(entry)); + print_sep(bsdtar, out, ' ', 2); + fprintf(out, "%d", (int)(st->st_nlink)); + print_sep(bsdtar, out, ' ', 2); /* Use uname if it's present, else uid. */ p = archive_entry_uname(entry); if ((p == NULL) || (*p == '\0')) { - sprintf(tmp, "%lu ", (unsigned long)st->st_uid); + sprintf(tmp, "%lu", (unsigned long)st->st_uid); p = tmp; } w = strlen(p); if (w > bsdtar->u_width) bsdtar->u_width = w; - fprintf(out, "%-*s ", (int)bsdtar->u_width, p); + if (bsdtar->option_null_output) + fprintf(out, "%s", p); + else + fprintf(out, "%-*s", (int)bsdtar->u_width, p); + print_sep(bsdtar, out, ' ', 2); /* Use gname if it's present, else gid. */ p = archive_entry_gname(entry); @@ -766,7 +787,10 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) } if (w + strlen(tmp) >= bsdtar->gs_width) bsdtar->gs_width = w+strlen(tmp)+1; - fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp); + if (bsdtar->option_null_output) + fprintf(out, "%s", tmp); + else + fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp); /* Format the time. */ tim = (time_t)st->st_mtime; @@ -788,13 +812,22 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) #endif } strftime(tmp, sizeof(tmp), fmt, localtime(&tim)); - fprintf(out, " %s ", tmp); + print_sep(bsdtar, out, ' ', 2); + fprintf(out, "%s", tmp); + print_sep(bsdtar, out, ' ', 2); safe_fprintf(out, "%s", archive_entry_pathname(entry)); /* Extra information for links. */ - if (archive_entry_hardlink(entry)) /* Hard link */ - safe_fprintf(out, " link to %s", - archive_entry_hardlink(entry)); - else if (S_ISLNK(st->st_mode)) /* Symbolic link */ - safe_fprintf(out, " -> %s", archive_entry_symlink(entry)); + if (archive_entry_hardlink(entry)) { /* Hard link */ + print_sep(bsdtar, out, ' ', 2); + fprintf(out, "link to"); + print_sep(bsdtar, out, ' ', 2); + safe_fprintf(out, "%s", archive_entry_hardlink(entry)); + } else if (S_ISLNK(st->st_mode)) { /* Symbolic link */ + print_sep(bsdtar, out, ' ', 2); + fprintf(out, "->"); + print_sep(bsdtar, out, ' ', 2); + safe_fprintf(out, "%s", archive_entry_symlink(entry)); + print_sep(bsdtar, out, ' ', 2); + } }