2 * Builtin "git interpret-trailers"
4 * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
7 #define USE_THE_REPOSITORY_VARIABLE
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"));
135 strbuf_complete_line(sb
);
138 static void interpret_trailers(const struct process_trailer_options
*opts
,
139 struct list_head
*new_trailer_head
,
143 struct strbuf sb
= STRBUF_INIT
;
144 struct strbuf trailer_block_sb
= STRBUF_INIT
;
145 struct trailer_block
*trailer_block
;
146 FILE *outfile
= stdout
;
148 trailer_config_init();
150 read_input_file(&sb
, file
);
153 outfile
= create_in_place_tempfile(file
);
155 trailer_block
= parse_trailers(opts
, sb
.buf
, &head
);
157 /* Print the lines before the trailer block */
158 if (!opts
->only_trailers
)
159 fwrite(sb
.buf
, 1, trailer_block_start(trailer_block
), outfile
);
161 if (!opts
->only_trailers
&& !blank_line_before_trailer_block(trailer_block
))
162 fprintf(outfile
, "\n");
165 if (!opts
->only_input
) {
166 LIST_HEAD(config_head
);
168 parse_trailers_from_config(&config_head
);
169 parse_trailers_from_command_line_args(&arg_head
, new_trailer_head
);
170 list_splice(&config_head
, &arg_head
);
171 process_trailers_lists(&head
, &arg_head
);
174 /* Print trailer block. */
175 format_trailers(opts
, &head
, &trailer_block_sb
);
176 free_trailers(&head
);
177 fwrite(trailer_block_sb
.buf
, 1, trailer_block_sb
.len
, outfile
);
178 strbuf_release(&trailer_block_sb
);
180 /* Print the lines after the trailer block as is. */
181 if (!opts
->only_trailers
)
182 fwrite(sb
.buf
+ trailer_block_end(trailer_block
), 1,
183 sb
.len
- trailer_block_end(trailer_block
), outfile
);
184 trailer_block_release(trailer_block
);
187 if (rename_tempfile(&trailers_tempfile
, file
))
188 die_errno(_("could not rename temporary file to %s"), file
);
193 int cmd_interpret_trailers(int argc
,
196 struct repository
*repo UNUSED
)
198 struct process_trailer_options opts
= PROCESS_TRAILER_OPTIONS_INIT
;
201 struct option options
[] = {
202 OPT_BOOL(0, "in-place", &opts
.in_place
, N_("edit files in place")),
203 OPT_BOOL(0, "trim-empty", &opts
.trim_empty
, N_("trim empty trailers")),
205 OPT_CALLBACK(0, "where", &where
, N_("placement"),
206 N_("where to place the new trailer"), option_parse_where
),
207 OPT_CALLBACK(0, "if-exists", &if_exists
, N_("action"),
208 N_("action if trailer already exists"), option_parse_if_exists
),
209 OPT_CALLBACK(0, "if-missing", &if_missing
, N_("action"),
210 N_("action if trailer is missing"), option_parse_if_missing
),
212 OPT_BOOL(0, "only-trailers", &opts
.only_trailers
, N_("output only the trailers")),
213 OPT_BOOL(0, "only-input", &opts
.only_input
, N_("do not apply trailer.* configuration variables")),
214 OPT_BOOL(0, "unfold", &opts
.unfold
, N_("reformat multiline trailer values as single-line values")),
215 OPT_CALLBACK_F(0, "parse", &opts
, NULL
, N_("alias for --only-trailers --only-input --unfold"),
216 PARSE_OPT_NOARG
| PARSE_OPT_NONEG
, parse_opt_parse
),
217 OPT_BOOL(0, "no-divider", &opts
.no_divider
, N_("do not treat \"---\" as the end of input")),
218 OPT_CALLBACK(0, "trailer", &trailers
, N_("trailer"),
219 N_("trailer(s) to add"), option_parse_trailer
),
223 git_config(git_default_config
, NULL
);
225 argc
= parse_options(argc
, argv
, prefix
, options
,
226 git_interpret_trailers_usage
, 0);
228 if (opts
.only_input
&& !list_empty(&trailers
))
230 _("--trailer with --only-input does not make sense"),
231 git_interpret_trailers_usage
,
236 for (i
= 0; i
< argc
; i
++)
237 interpret_trailers(&opts
, &trailers
, argv
[i
]);
240 die(_("no input file given for in-place editing"));
241 interpret_trailers(&opts
, &trailers
, NULL
);
244 new_trailers_clear(&trailers
);