Skip to content

Commit f7b2be5

Browse files
committed
WIP cat-file: add a --stdin-cmd mode
This WIP patch is mostly stealing code from builtin/update-ref.c and implementing the same sort of prefixed command-mode that it supports. I.e. in addition to --batch now supporting: <object> LF It'll support with --stdin-cmd, with and without -z, respectively: object <object> NL object <object> NUL The plus being that we can now implement additional commands. Let's start that by scratching the itch John Cai wanted to address in [1] and implement a (with and without -z): fflush NL fflush NUL That command simply calls fflush(stdout), which could be done as an emergent effect before by feeding the input a "NL". I think this will be useful for other things, e.g. I've observed in the past that a not-trivial part of "cat-file --batch" time is spent on parsing its <object> argument and seeing if it's a revision, ref etc. So we could e.g. add a command that only accepts a full-length 40 character SHA-1, or switch the --format output mid-request etc. 1. https://lore.kernel.org/git/pull.1124.git.git.1636149400.gitgitgadget@gmail.com/ Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
1 parent ed83a28 commit f7b2be5

File tree

1 file changed

+115
-1
lines changed

1 file changed

+115
-1
lines changed

builtin/cat-file.c

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ struct batch_options {
2626
int unordered;
2727
int cmdmode; /* may be 'w' or 'c' for --filters or --textconv */
2828
const char *format;
29+
int stdin_cmd;
30+
int end_null;
2931
};
32+
static char line_termination = '\n';
3033

3134
static const char *force_path;
3235

@@ -508,13 +511,114 @@ static int batch_unordered_packed(const struct object_id *oid,
508511
data);
509512
}
510513

514+
enum batch_state {
515+
/* Non-transactional state open for commands. */
516+
BATCH_STATE_OPEN,
517+
};
518+
519+
static void parse_cmd_object(struct batch_options *opt,
520+
const char *next, const char *end,
521+
struct strbuf *output,
522+
struct expand_data *data)
523+
{
524+
size_t len = end - next - 1;
525+
char *p = (char *)next;
526+
char old = p[len];
527+
528+
p[len] = '\0';
529+
batch_one_object(next, output, opt, data);
530+
p[len] = old;
531+
}
532+
533+
static void parse_cmd_fflush(struct batch_options *opt,
534+
const char *next, const char *end,
535+
struct strbuf *output,
536+
struct expand_data *data)
537+
{
538+
if (*next != line_termination)
539+
die("fflush: extra input: %s", next);
540+
fflush(stdout);
541+
}
542+
543+
static const struct parse_cmd {
544+
const char *prefix;
545+
void (*fn)(struct batch_options *, const char *, const char *, struct strbuf *, struct expand_data *);
546+
unsigned args;
547+
enum batch_state state;
548+
} command[] = {
549+
{ "object", parse_cmd_object, 1, BATCH_STATE_OPEN },
550+
{ "fflush", parse_cmd_fflush, 0, BATCH_STATE_OPEN },
551+
};
552+
553+
static void batch_objects_stdin_cmd(struct batch_options *opt,
554+
struct strbuf *output,
555+
struct expand_data *data)
556+
{
557+
struct strbuf input = STRBUF_INIT;
558+
enum batch_state state = BATCH_STATE_OPEN;
559+
560+
/* Read each line dispatch its command */
561+
while (!strbuf_getwholeline(&input, stdin, line_termination)) {
562+
size_t i, j;
563+
const struct parse_cmd *cmd = NULL;
564+
565+
if (*input.buf == line_termination)
566+
die("empty command in input");
567+
else if (isspace(*input.buf))
568+
die("whitespace before command: %s", input.buf);
569+
570+
for (i = 0; i < ARRAY_SIZE(command); i++) {
571+
const char *prefix = command[i].prefix;
572+
char c;
573+
574+
if (!starts_with(input.buf, prefix))
575+
continue;
576+
577+
/*
578+
* If the command has arguments, verify that it's
579+
* followed by a space. Otherwise, it shall be followed
580+
* by a line terminator.
581+
*/
582+
c = command[i].args ? ' ' : line_termination;
583+
if (input.buf[strlen(prefix)] != c)
584+
continue;
585+
586+
cmd = &command[i];
587+
break;
588+
}
589+
if (!cmd)
590+
die("unknown command: %s", input.buf);
591+
592+
/*
593+
* Read additional arguments if NUL-terminated. Do not raise an
594+
* error in case there is an early EOF to let the command
595+
* handle missing arguments with a proper error message.
596+
*/
597+
for (j = 1; line_termination == '\0' && j < cmd->args; j++)
598+
if (strbuf_appendwholeline(&input, stdin, line_termination))
599+
break;
600+
601+
switch (state) {
602+
case BATCH_STATE_OPEN:
603+
/* TODO: command state management */
604+
break;
605+
}
606+
607+
cmd->fn(opt, input.buf + strlen(cmd->prefix) + !!cmd->args,
608+
input.buf + input.len, output, data);
609+
}
610+
611+
strbuf_release(&input);
612+
}
613+
511614
static int batch_objects(struct batch_options *opt)
512615
{
513616
struct strbuf input = STRBUF_INIT;
514617
struct strbuf output = STRBUF_INIT;
515618
struct expand_data data;
516619
int save_warning;
517620
int retval = 0;
621+
const int stdin_cmd = opt->stdin_cmd;
518622

519623
if (!opt->format)
520624
opt->format = "%(objectname) %(objecttype) %(objectsize)";
@@ -590,7 +694,8 @@ static int batch_objects(struct batch_options *opt)
590694
save_warning = warn_on_object_refname_ambiguity;
591695
warn_on_object_refname_ambiguity = 0;
592696

593-
while (strbuf_getline(&input, stdin) != EOF) {
697+
while (!stdin_cmd &&
698+
strbuf_getline(&input, stdin) != EOF) {
594699
if (data.split_on_whitespace) {
595700
/*
596701
* Split at first whitespace, tying off the beginning
@@ -608,6 +713,9 @@ static int batch_objects(struct batch_options *opt)
608713
batch_one_object(input.buf, &output, opt, &data);
609714
}
610715

716+
if (stdin_cmd)
717+
batch_objects_stdin_cmd(opt, &output, &data);
718+
611719
strbuf_release(&input);
612720
strbuf_release(&output);
613721
warn_on_object_refname_ambiguity = save_warning;
@@ -685,6 +793,10 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
685793
batch_option_callback),
686794
OPT_CMDMODE(0, "batch-all-objects", &opt,
687795
N_("with --batch[-check]: ignores stdin, batches all known objects"), 'b'),
796+
OPT_BOOL(0, "stdin-cmd", &batch.stdin_cmd,
797+
N_("with --batch[-check]: enters stdin 'command mode")),
798+
OPT_BOOL('z', NULL, &batch.end_null, N_("with --stdin-cmd, use NUL termination")),
799+
688800
/* Batch-specific options */
689801
OPT_GROUP(N_("Change or optimize batch output")),
690802
OPT_BOOL(0, "buffer", &batch.buffer_output, N_("buffer --batch output")),
@@ -738,6 +850,8 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
738850
/* Batch defaults */
739851
if (batch.buffer_output < 0)
740852
batch.buffer_output = batch.all_objects;
853+
if (batch.end_null)
854+
line_termination = '\0';
741855

742856
/* Return early if we're in batch mode? */
743857
if (batch.enabled) {

0 commit comments

Comments
 (0)