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