Skip to content

Commit a9ea4c2

Browse files
committed
Merge branch 'ps/cat-file-null-output'
"git cat-file --batch" and friends learned "-Z" that uses NUL delimiter for both input and output. * ps/cat-file-null-output: cat-file: add option '-Z' that delimits input and output with NUL cat-file: simplify reading from standard input strbuf: provide CRLF-aware helper to read until a specified delimiter t1006: modernize test style to use `test_cmp` t1006: don't strip timestamps from expected results
2 parents d9f9f6b + f79e188 commit a9ea4c2

File tree

5 files changed

+232
-138
lines changed

5 files changed

+232
-138
lines changed

Documentation/git-cat-file.txt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ SYNOPSIS
1414
'git cat-file' (-t | -s) [--allow-unknown-type] <object>
1515
'git cat-file' (--batch | --batch-check | --batch-command) [--batch-all-objects]
1616
[--buffer] [--follow-symlinks] [--unordered]
17-
[--textconv | --filters] [-z]
17+
[--textconv | --filters] [-Z]
1818
'git cat-file' (--textconv | --filters)
1919
[<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]
2020

@@ -243,10 +243,16 @@ respectively print:
243243
/etc/passwd
244244
--
245245

246+
-Z::
247+
Only meaningful with `--batch`, `--batch-check`, or
248+
`--batch-command`; input and output is NUL-delimited instead of
249+
newline-delimited.
250+
246251
-z::
247252
Only meaningful with `--batch`, `--batch-check`, or
248253
`--batch-command`; input is NUL-delimited instead of
249-
newline-delimited.
254+
newline-delimited. This option is deprecated in favor of
255+
`-Z` as the output can otherwise be ambiguous.
250256

251257

252258
OUTPUT
@@ -384,6 +390,11 @@ notdir SP <size> LF
384390
is printed when, during symlink resolution, a file is used as a
385391
directory name.
386392

393+
Alternatively, when `-Z` is passed, the line feeds in any of the above examples
394+
are replaced with NUL terminators. This ensures that output will be parsable if
395+
the output itself would contain a linefeed and is thus recommended for
396+
scripting purposes.
397+
387398
CAVEATS
388399
-------
389400

builtin/cat-file.c

Lines changed: 43 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ struct batch_options {
4242
int all_objects;
4343
int unordered;
4444
int transform_mode; /* may be 'w' or 'c' for --filters or --textconv */
45-
int nul_terminated;
45+
char input_delim;
46+
char output_delim;
4647
const char *format;
4748
};
4849

@@ -437,11 +438,12 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
437438
}
438439
}
439440

440-
static void print_default_format(struct strbuf *scratch, struct expand_data *data)
441+
static void print_default_format(struct strbuf *scratch, struct expand_data *data,
442+
struct batch_options *opt)
441443
{
442-
strbuf_addf(scratch, "%s %s %"PRIuMAX"\n", oid_to_hex(&data->oid),
444+
strbuf_addf(scratch, "%s %s %"PRIuMAX"%c", oid_to_hex(&data->oid),
443445
type_name(data->type),
444-
(uintmax_t)data->size);
446+
(uintmax_t)data->size, opt->output_delim);
445447
}
446448

447449
/*
@@ -470,8 +472,8 @@ static void batch_object_write(const char *obj_name,
470472
&data->oid, &data->info,
471473
OBJECT_INFO_LOOKUP_REPLACE);
472474
if (ret < 0) {
473-
printf("%s missing\n",
474-
obj_name ? obj_name : oid_to_hex(&data->oid));
475+
printf("%s missing%c",
476+
obj_name ? obj_name : oid_to_hex(&data->oid), opt->output_delim);
475477
fflush(stdout);
476478
return;
477479
}
@@ -492,17 +494,17 @@ static void batch_object_write(const char *obj_name,
492494
strbuf_reset(scratch);
493495

494496
if (!opt->format) {
495-
print_default_format(scratch, data);
497+
print_default_format(scratch, data, opt);
496498
} else {
497499
strbuf_expand(scratch, opt->format, expand_format, data);
498-
strbuf_addch(scratch, '\n');
500+
strbuf_addch(scratch, opt->output_delim);
499501
}
500502

501503
batch_write(opt, scratch->buf, scratch->len);
502504

503505
if (opt->batch_mode == BATCH_MODE_CONTENTS) {
504506
print_object_or_die(opt, data);
505-
batch_write(opt, "\n", 1);
507+
batch_write(opt, &opt->output_delim, 1);
506508
}
507509
}
508510

@@ -520,22 +522,25 @@ static void batch_one_object(const char *obj_name,
520522
if (result != FOUND) {
521523
switch (result) {
522524
case MISSING_OBJECT:
523-
printf("%s missing\n", obj_name);
525+
printf("%s missing%c", obj_name, opt->output_delim);
524526
break;
525527
case SHORT_NAME_AMBIGUOUS:
526-
printf("%s ambiguous\n", obj_name);
528+
printf("%s ambiguous%c", obj_name, opt->output_delim);
527529
break;
528530
case DANGLING_SYMLINK:
529-
printf("dangling %"PRIuMAX"\n%s\n",
530-
(uintmax_t)strlen(obj_name), obj_name);
531+
printf("dangling %"PRIuMAX"%c%s%c",
532+
(uintmax_t)strlen(obj_name),
533+
opt->output_delim, obj_name, opt->output_delim);
531534
break;
532535
case SYMLINK_LOOP:
533-
printf("loop %"PRIuMAX"\n%s\n",
534-
(uintmax_t)strlen(obj_name), obj_name);
536+
printf("loop %"PRIuMAX"%c%s%c",
537+
(uintmax_t)strlen(obj_name),
538+
opt->output_delim, obj_name, opt->output_delim);
535539
break;
536540
case NOT_DIR:
537-
printf("notdir %"PRIuMAX"\n%s\n",
538-
(uintmax_t)strlen(obj_name), obj_name);
541+
printf("notdir %"PRIuMAX"%c%s%c",
542+
(uintmax_t)strlen(obj_name),
543+
opt->output_delim, obj_name, opt->output_delim);
539544
break;
540545
default:
541546
BUG("unknown get_sha1_with_context result %d\n",
@@ -547,9 +552,9 @@ static void batch_one_object(const char *obj_name,
547552
}
548553

549554
if (ctx.mode == 0) {
550-
printf("symlink %"PRIuMAX"\n%s\n",
555+
printf("symlink %"PRIuMAX"%c%s%c",
551556
(uintmax_t)ctx.symlink_path.len,
552-
ctx.symlink_path.buf);
557+
opt->output_delim, ctx.symlink_path.buf, opt->output_delim);
553558
fflush(stdout);
554559
return;
555560
}
@@ -694,20 +699,12 @@ static void batch_objects_command(struct batch_options *opt,
694699
struct queued_cmd *queued_cmd = NULL;
695700
size_t alloc = 0, nr = 0;
696701

697-
while (1) {
698-
int i, ret;
702+
while (strbuf_getdelim_strip_crlf(&input, stdin, opt->input_delim) != EOF) {
703+
int i;
699704
const struct parse_cmd *cmd = NULL;
700705
const char *p = NULL, *cmd_end;
701706
struct queued_cmd call = {0};
702707

703-
if (opt->nul_terminated)
704-
ret = strbuf_getline_nul(&input, stdin);
705-
else
706-
ret = strbuf_getline(&input, stdin);
707-
708-
if (ret)
709-
break;
710-
711708
if (!input.len)
712709
die(_("empty command in input"));
713710
if (isspace(*input.buf))
@@ -851,16 +848,7 @@ static int batch_objects(struct batch_options *opt)
851848
goto cleanup;
852849
}
853850

854-
while (1) {
855-
int ret;
856-
if (opt->nul_terminated)
857-
ret = strbuf_getline_nul(&input, stdin);
858-
else
859-
ret = strbuf_getline(&input, stdin);
860-
861-
if (ret == EOF)
862-
break;
863-
851+
while (strbuf_getdelim_strip_crlf(&input, stdin, opt->input_delim) != EOF) {
864852
if (data.split_on_whitespace) {
865853
/*
866854
* Split at first whitespace, tying off the beginning
@@ -929,14 +917,16 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
929917
const char *exp_type = NULL, *obj_name = NULL;
930918
struct batch_options batch = {0};
931919
int unknown_type = 0;
920+
int input_nul_terminated = 0;
921+
int nul_terminated = 0;
932922

933923
const char * const usage[] = {
934924
N_("git cat-file <type> <object>"),
935925
N_("git cat-file (-e | -p) <object>"),
936926
N_("git cat-file (-t | -s) [--allow-unknown-type] <object>"),
937927
N_("git cat-file (--batch | --batch-check | --batch-command) [--batch-all-objects]\n"
938928
" [--buffer] [--follow-symlinks] [--unordered]\n"
939-
" [--textconv | --filters] [-z]"),
929+
" [--textconv | --filters] [-Z]"),
940930
N_("git cat-file (--textconv | --filters)\n"
941931
" [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"),
942932
NULL
@@ -965,7 +955,9 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
965955
N_("like --batch, but don't emit <contents>"),
966956
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
967957
batch_option_callback),
968-
OPT_BOOL('z', NULL, &batch.nul_terminated, N_("stdin is NUL-terminated")),
958+
OPT_BOOL_F('z', NULL, &input_nul_terminated, N_("stdin is NUL-terminated"),
959+
PARSE_OPT_HIDDEN),
960+
OPT_BOOL('Z', NULL, &nul_terminated, N_("stdin and stdout is NUL-terminated")),
969961
OPT_CALLBACK_F(0, "batch-command", &batch, N_("format"),
970962
N_("read commands from stdin"),
971963
PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
@@ -1024,9 +1016,18 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
10241016
else if (batch.all_objects)
10251017
usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
10261018
"--batch-all-objects");
1027-
else if (batch.nul_terminated)
1019+
else if (input_nul_terminated)
10281020
usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
10291021
"-z");
1022+
else if (nul_terminated)
1023+
usage_msg_optf(_("'%s' requires a batch mode"), usage, options,
1024+
"-Z");
1025+
1026+
batch.input_delim = batch.output_delim = '\n';
1027+
if (input_nul_terminated)
1028+
batch.input_delim = '\0';
1029+
if (nul_terminated)
1030+
batch.input_delim = batch.output_delim = '\0';
10301031

10311032
/* Batch defaults */
10321033
if (batch.buffer_output < 0)

strbuf.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -721,18 +721,23 @@ static int strbuf_getdelim(struct strbuf *sb, FILE *fp, int term)
721721
return 0;
722722
}
723723

724-
int strbuf_getline(struct strbuf *sb, FILE *fp)
724+
int strbuf_getdelim_strip_crlf(struct strbuf *sb, FILE *fp, int term)
725725
{
726-
if (strbuf_getwholeline(sb, fp, '\n'))
726+
if (strbuf_getwholeline(sb, fp, term))
727727
return EOF;
728-
if (sb->buf[sb->len - 1] == '\n') {
728+
if (term == '\n' && sb->buf[sb->len - 1] == '\n') {
729729
strbuf_setlen(sb, sb->len - 1);
730730
if (sb->len && sb->buf[sb->len - 1] == '\r')
731731
strbuf_setlen(sb, sb->len - 1);
732732
}
733733
return 0;
734734
}
735735

736+
int strbuf_getline(struct strbuf *sb, FILE *fp)
737+
{
738+
return strbuf_getdelim_strip_crlf(sb, fp, '\n');
739+
}
740+
736741
int strbuf_getline_lf(struct strbuf *sb, FILE *fp)
737742
{
738743
return strbuf_getdelim(sb, fp, '\n');

strbuf.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,18 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
475475
*/
476476
ssize_t strbuf_write(struct strbuf *sb, FILE *stream);
477477

478+
/**
479+
* Read from a FILE * until the specified terminator is encountered,
480+
* overwriting the existing contents of the strbuf.
481+
*
482+
* Reading stops after the terminator or at EOF. The terminator is
483+
* removed from the buffer before returning. If the terminator is LF
484+
* and if it is preceded by a CR, then the whole CRLF is stripped.
485+
* Returns 0 unless there was nothing left before EOF, in which case
486+
* it returns `EOF`.
487+
*/
488+
int strbuf_getdelim_strip_crlf(struct strbuf *sb, FILE *fp, int term);
489+
478490
/**
479491
* Read a line from a FILE *, overwriting the existing contents of
480492
* the strbuf. The strbuf_getline*() family of functions share

0 commit comments

Comments
 (0)