]>
Commit | Line | Data |
---|---|---|
6634f054 CC |
1 | /* |
2 | * Builtin "git interpret-trailers" | |
3 | * | |
4 | * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org> | |
5 | * | |
6 | */ | |
7 | ||
6634f054 | 8 | #include "builtin.h" |
f394e093 | 9 | #include "gettext.h" |
6634f054 CC |
10 | #include "parse-options.h" |
11 | #include "string-list.h" | |
ae0ec2e0 | 12 | #include "tempfile.h" |
6634f054 | 13 | #include "trailer.h" |
29c83fc2 | 14 | #include "config.h" |
6634f054 CC |
15 | |
16 | static const char * const git_interpret_trailers_usage[] = { | |
5af8b61c | 17 | N_("git interpret-trailers [--in-place] [--trim-empty]\n" |
67471bc7 | 18 | " [(--trailer (<key>|<key-alias>)[(=|:)<value>])...]\n" |
d9054a19 | 19 | " [--parse] [<file>...]"), |
6634f054 CC |
20 | NULL |
21 | }; | |
22 | ||
0ea5292e PB |
23 | static enum trailer_where where; |
24 | static enum trailer_if_exists if_exists; | |
25 | static enum trailer_if_missing if_missing; | |
26 | ||
27 | static int option_parse_where(const struct option *opt, | |
d775365d | 28 | const char *arg, int unset UNUSED) |
0ea5292e | 29 | { |
d775365d | 30 | /* unset implies NULL arg, which is handled in our helper */ |
66e33092 | 31 | return trailer_set_where(opt->value, arg); |
0ea5292e PB |
32 | } |
33 | ||
34 | static int option_parse_if_exists(const struct option *opt, | |
d775365d | 35 | const char *arg, int unset UNUSED) |
0ea5292e | 36 | { |
d775365d | 37 | /* unset implies NULL arg, which is handled in our helper */ |
66e33092 | 38 | return trailer_set_if_exists(opt->value, arg); |
0ea5292e PB |
39 | } |
40 | ||
41 | static int option_parse_if_missing(const struct option *opt, | |
d775365d | 42 | const char *arg, int unset UNUSED) |
0ea5292e | 43 | { |
d775365d | 44 | /* unset implies NULL arg, which is handled in our helper */ |
66e33092 | 45 | return trailer_set_if_missing(opt->value, arg); |
0ea5292e PB |
46 | } |
47 | ||
51166b87 PB |
48 | static void new_trailers_clear(struct list_head *trailers) |
49 | { | |
50 | struct list_head *pos, *tmp; | |
51 | struct new_trailer_item *item; | |
52 | ||
53 | list_for_each_safe(pos, tmp, trailers) { | |
54 | item = list_entry(pos, struct new_trailer_item, list); | |
55 | list_del(pos); | |
56 | free(item); | |
57 | } | |
58 | } | |
59 | ||
60 | static int option_parse_trailer(const struct option *opt, | |
61 | const char *arg, int unset) | |
62 | { | |
63 | struct list_head *trailers = opt->value; | |
64 | struct new_trailer_item *item; | |
65 | ||
66 | if (unset) { | |
67 | new_trailers_clear(trailers); | |
68 | return 0; | |
69 | } | |
70 | ||
71 | if (!arg) | |
72 | return -1; | |
73 | ||
74 | item = xmalloc(sizeof(*item)); | |
75 | item->text = arg; | |
0ea5292e PB |
76 | item->where = where; |
77 | item->if_exists = if_exists; | |
78 | item->if_missing = if_missing; | |
51166b87 PB |
79 | list_add_tail(&item->list, trailers); |
80 | return 0; | |
81 | } | |
82 | ||
99e09daf JK |
83 | static int parse_opt_parse(const struct option *opt, const char *arg, |
84 | int unset) | |
85 | { | |
86 | struct process_trailer_options *v = opt->value; | |
87 | v->only_trailers = 1; | |
88 | v->only_input = 1; | |
89 | v->unfold = 1; | |
517fe807 JK |
90 | BUG_ON_OPT_NEG(unset); |
91 | BUG_ON_OPT_ARG(arg); | |
99e09daf JK |
92 | return 0; |
93 | } | |
94 | ||
ae0ec2e0 LA |
95 | static struct tempfile *trailers_tempfile; |
96 | ||
97 | static FILE *create_in_place_tempfile(const char *file) | |
98 | { | |
99 | struct stat st; | |
100 | struct strbuf filename_template = STRBUF_INIT; | |
101 | const char *tail; | |
102 | FILE *outfile; | |
103 | ||
104 | if (stat(file, &st)) | |
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); | |
110 | ||
111 | /* Create temporary file in the same directory as the original */ | |
112 | tail = strrchr(file, '/'); | |
113 | if (tail) | |
114 | strbuf_add(&filename_template, file, tail - file + 1); | |
115 | strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX"); | |
116 | ||
117 | trailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode); | |
118 | strbuf_release(&filename_template); | |
119 | outfile = fdopen_tempfile(trailers_tempfile, "w"); | |
120 | if (!outfile) | |
121 | die_errno(_("could not open temporary file")); | |
122 | ||
123 | return outfile; | |
124 | } | |
125 | ||
126 | static void read_input_file(struct strbuf *sb, const char *file) | |
127 | { | |
128 | if (file) { | |
129 | if (strbuf_read_file(sb, file, 0) < 0) | |
130 | die_errno(_("could not read input file '%s'"), file); | |
131 | } else { | |
132 | if (strbuf_read(sb, fileno(stdin), 0) < 0) | |
133 | die_errno(_("could not read from stdin")); | |
134 | } | |
135 | } | |
136 | ||
137 | static void interpret_trailers(const struct process_trailer_options *opts, | |
138 | struct list_head *new_trailer_head, | |
139 | const char *file) | |
140 | { | |
141 | LIST_HEAD(head); | |
142 | struct strbuf sb = STRBUF_INIT; | |
bf35e0a0 | 143 | struct strbuf trailer_block = STRBUF_INIT; |
ae0ec2e0 LA |
144 | struct trailer_info info; |
145 | FILE *outfile = stdout; | |
146 | ||
147 | trailer_config_init(); | |
148 | ||
149 | read_input_file(&sb, file); | |
150 | ||
151 | if (opts->in_place) | |
152 | outfile = create_in_place_tempfile(file); | |
153 | ||
154 | parse_trailers(opts, &info, sb.buf, &head); | |
155 | ||
156 | /* Print the lines before the trailers */ | |
157 | if (!opts->only_trailers) | |
158 | fwrite(sb.buf, 1, info.trailer_block_start, outfile); | |
159 | ||
160 | if (!opts->only_trailers && !info.blank_line_before_trailer) | |
161 | fprintf(outfile, "\n"); | |
162 | ||
163 | ||
164 | if (!opts->only_input) { | |
165 | LIST_HEAD(config_head); | |
166 | LIST_HEAD(arg_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); | |
171 | } | |
172 | ||
bf35e0a0 LA |
173 | /* Print trailer block. */ |
174 | format_trailers(opts, &head, &trailer_block); | |
ae0ec2e0 | 175 | free_trailers(&head); |
bf35e0a0 LA |
176 | fwrite(trailer_block.buf, 1, trailer_block.len, outfile); |
177 | strbuf_release(&trailer_block); | |
ae0ec2e0 LA |
178 | |
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); | |
183 | ||
184 | if (opts->in_place) | |
185 | if (rename_tempfile(&trailers_tempfile, file)) | |
186 | die_errno(_("could not rename temporary file to %s"), file); | |
187 | ||
188 | strbuf_release(&sb); | |
189 | } | |
190 | ||
6634f054 CC |
191 | int cmd_interpret_trailers(int argc, const char **argv, const char *prefix) |
192 | { | |
8abc8980 | 193 | struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT; |
51166b87 | 194 | LIST_HEAD(trailers); |
6634f054 CC |
195 | |
196 | struct option options[] = { | |
8abc8980 JK |
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")), | |
51166b87 | 199 | |
c75e9149 | 200 | OPT_CALLBACK(0, "where", &where, N_("placement"), |
0ea5292e | 201 | N_("where to place the new trailer"), option_parse_where), |
66e33092 | 202 | OPT_CALLBACK(0, "if-exists", &if_exists, N_("action"), |
0ea5292e | 203 | N_("action if trailer already exists"), option_parse_if_exists), |
66e33092 | 204 | OPT_CALLBACK(0, "if-missing", &if_missing, N_("action"), |
0ea5292e PB |
205 | N_("action if trailer is missing"), option_parse_if_missing), |
206 | ||
56c493ed | 207 | OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")), |
a6c72e70 | 208 | OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply trailer.* configuration variables")), |
289a0b24 | 209 | OPT_BOOL(0, "unfold", &opts.unfold, N_("reformat multiline trailer values as single-line values")), |
8c7d4acb | 210 | OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("alias for --only-trailers --only-input --unfold"), |
203c8533 | 211 | PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse), |
b674f25b | 212 | OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat \"---\" as the end of input")), |
51166b87 PB |
213 | OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"), |
214 | N_("trailer(s) to add"), option_parse_trailer), | |
6634f054 CC |
215 | OPT_END() |
216 | }; | |
217 | ||
29c83fc2 JK |
218 | git_config(git_default_config, NULL); |
219 | ||
6634f054 CC |
220 | argc = parse_options(argc, argv, prefix, options, |
221 | git_interpret_trailers_usage, 0); | |
222 | ||
06cf4f2d | 223 | if (opts.only_input && !list_empty(&trailers)) |
fdbdb64f JK |
224 | usage_msg_opt( |
225 | _("--trailer with --only-input does not make sense"), | |
226 | git_interpret_trailers_usage, | |
227 | options); | |
228 | ||
6634f054 CC |
229 | if (argc) { |
230 | int i; | |
231 | for (i = 0; i < argc; i++) | |
7b1c6aa5 | 232 | interpret_trailers(&opts, &trailers, argv[i]); |
e1f89863 | 233 | } else { |
8abc8980 | 234 | if (opts.in_place) |
e1f89863 | 235 | die(_("no input file given for in-place editing")); |
7b1c6aa5 | 236 | interpret_trailers(&opts, &trailers, NULL); |
e1f89863 | 237 | } |
6634f054 | 238 | |
51166b87 | 239 | new_trailers_clear(&trailers); |
6634f054 CC |
240 | |
241 | return 0; | |
242 | } |