2 * Builtin "git interpret-trailers"
4 * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
10 #include "parse-options.h"
11 #include "string-list.h"
16 static const char * const git_interpret_trailers_usage
[] = {
17 N_("git interpret-trailers [--in-place] [--trim-empty]\n"
18 " [(--trailer (<key>|<key-alias>)[(=|:)<value>])...]\n"
19 " [--parse] [<file>...]"),
23 static enum trailer_where where
;
24 static enum trailer_if_exists if_exists
;
25 static enum trailer_if_missing if_missing
;
27 static int option_parse_where(const struct option
*opt
,
28 const char *arg
, int unset UNUSED
)
30 /* unset implies NULL arg, which is handled in our helper */
31 return trailer_set_where(opt
->value
, arg
);
34 static int option_parse_if_exists(const struct option
*opt
,
35 const char *arg
, int unset UNUSED
)
37 /* unset implies NULL arg, which is handled in our helper */
38 return trailer_set_if_exists(opt
->value
, arg
);
41 static int option_parse_if_missing(const struct option
*opt
,
42 const char *arg
, int unset UNUSED
)
44 /* unset implies NULL arg, which is handled in our helper */
45 return trailer_set_if_missing(opt
->value
, arg
);
48 static void new_trailers_clear(struct list_head
*trailers
)
50 struct list_head
*pos
, *tmp
;
51 struct new_trailer_item
*item
;
53 list_for_each_safe(pos
, tmp
, trailers
) {
54 item
= list_entry(pos
, struct new_trailer_item
, list
);
60 static int option_parse_trailer(const struct option
*opt
,
61 const char *arg
, int unset
)
63 struct list_head
*trailers
= opt
->value
;
64 struct new_trailer_item
*item
;
67 new_trailers_clear(trailers
);
74 item
= xmalloc(sizeof(*item
));
77 item
->if_exists
= if_exists
;
78 item
->if_missing
= if_missing
;
79 list_add_tail(&item
->list
, trailers
);
83 static int parse_opt_parse(const struct option
*opt
, const char *arg
,
86 struct process_trailer_options
*v
= opt
->value
;
90 BUG_ON_OPT_NEG(unset
);
95 static struct tempfile
*trailers_tempfile
;
97 static FILE *create_in_place_tempfile(const char *file
)
100 struct strbuf filename_template
= STRBUF_INIT
;
105 die_errno(_("could not stat %s"), file
);
106 if (!S_ISREG(st
.st_mode
))
107 die(_("file %s is not a regular file"), file
);
108 if (!(st
.st_mode
& S_IWUSR
))
109 die(_("file %s is not writable by user"), file
);
111 /* Create temporary file in the same directory as the original */
112 tail
= strrchr(file
, '/');
114 strbuf_add(&filename_template
, file
, tail
- file
+ 1);
115 strbuf_addstr(&filename_template
, "git-interpret-trailers-XXXXXX");
117 trailers_tempfile
= xmks_tempfile_m(filename_template
.buf
, st
.st_mode
);
118 strbuf_release(&filename_template
);
119 outfile
= fdopen_tempfile(trailers_tempfile
, "w");
121 die_errno(_("could not open temporary file"));
126 static void read_input_file(struct strbuf
*sb
, const char *file
)
129 if (strbuf_read_file(sb
, file
, 0) < 0)
130 die_errno(_("could not read input file '%s'"), file
);
132 if (strbuf_read(sb
, fileno(stdin
), 0) < 0)
133 die_errno(_("could not read from stdin"));
137 static void interpret_trailers(const struct process_trailer_options
*opts
,
138 struct list_head
*new_trailer_head
,
142 struct strbuf sb
= STRBUF_INIT
;
143 struct strbuf trailer_block
= STRBUF_INIT
;
144 struct trailer_info info
;
145 FILE *outfile
= stdout
;
147 trailer_config_init();
149 read_input_file(&sb
, file
);
152 outfile
= create_in_place_tempfile(file
);
154 parse_trailers(opts
, &info
, sb
.buf
, &head
);
156 /* Print the lines before the trailers */
157 if (!opts
->only_trailers
)
158 fwrite(sb
.buf
, 1, info
.trailer_block_start
, outfile
);
160 if (!opts
->only_trailers
&& !info
.blank_line_before_trailer
)
161 fprintf(outfile
, "\n");
164 if (!opts
->only_input
) {
165 LIST_HEAD(config_head
);
167 parse_trailers_from_config(&config_head
);
168 parse_trailers_from_command_line_args(&arg_head
, new_trailer_head
);
169 list_splice(&config_head
, &arg_head
);
170 process_trailers_lists(&head
, &arg_head
);
173 /* Print trailer block. */
174 format_trailers(opts
, &head
, &trailer_block
);
175 free_trailers(&head
);
176 fwrite(trailer_block
.buf
, 1, trailer_block
.len
, outfile
);
177 strbuf_release(&trailer_block
);
179 /* Print the lines after the trailers as is */
180 if (!opts
->only_trailers
)
181 fwrite(sb
.buf
+ info
.trailer_block_end
, 1, sb
.len
- info
.trailer_block_end
, outfile
);
182 trailer_info_release(&info
);
185 if (rename_tempfile(&trailers_tempfile
, file
))
186 die_errno(_("could not rename temporary file to %s"), file
);
191 int cmd_interpret_trailers(int argc
, const char **argv
, const char *prefix
)
193 struct process_trailer_options opts
= PROCESS_TRAILER_OPTIONS_INIT
;
196 struct option options
[] = {
197 OPT_BOOL(0, "in-place", &opts
.in_place
, N_("edit files in place")),
198 OPT_BOOL(0, "trim-empty", &opts
.trim_empty
, N_("trim empty trailers")),
200 OPT_CALLBACK(0, "where", &where
, N_("placement"),
201 N_("where to place the new trailer"), option_parse_where
),
202 OPT_CALLBACK(0, "if-exists", &if_exists
, N_("action"),
203 N_("action if trailer already exists"), option_parse_if_exists
),
204 OPT_CALLBACK(0, "if-missing", &if_missing
, N_("action"),
205 N_("action if trailer is missing"), option_parse_if_missing
),
207 OPT_BOOL(0, "only-trailers", &opts
.only_trailers
, N_("output only the trailers")),
208 OPT_BOOL(0, "only-input", &opts
.only_input
, N_("do not apply trailer.* configuration variables")),
209 OPT_BOOL(0, "unfold", &opts
.unfold
, N_("reformat multiline trailer values as single-line values")),
210 OPT_CALLBACK_F(0, "parse", &opts
, NULL
, N_("alias for --only-trailers --only-input --unfold"),
211 PARSE_OPT_NOARG
| PARSE_OPT_NONEG
, parse_opt_parse
),
212 OPT_BOOL(0, "no-divider", &opts
.no_divider
, N_("do not treat \"---\" as the end of input")),
213 OPT_CALLBACK(0, "trailer", &trailers
, N_("trailer"),
214 N_("trailer(s) to add"), option_parse_trailer
),
218 git_config(git_default_config
, NULL
);
220 argc
= parse_options(argc
, argv
, prefix
, options
,
221 git_interpret_trailers_usage
, 0);
223 if (opts
.only_input
&& !list_empty(&trailers
))
225 _("--trailer with --only-input does not make sense"),
226 git_interpret_trailers_usage
,
231 for (i
= 0; i
< argc
; i
++)
232 interpret_trailers(&opts
, &trailers
, argv
[i
]);
235 die(_("no input file given for in-place editing"));
236 interpret_trailers(&opts
, &trailers
, NULL
);
239 new_trailers_clear(&trailers
);