ruby: add simpler option parser
[git] / builtin / shortlog.c
1 #include "builtin.h"
2 #include "cache.h"
3 #include "commit.h"
4 #include "string-list.h"
5 #include "revision.h"
6 #include "shortlog.h"
7 #include "parse-options.h"
8
9 static char const * const shortlog_usage[] = {
10         N_("git shortlog [<options>] [<revision-range>] [[--] [<path>...]]"),
11         NULL
12 };
13
14 static void read_from_stdin(struct shortlog *log)
15 {
16         struct strbuf author = STRBUF_INIT;
17         struct strbuf oneline = STRBUF_INIT;
18
19         while (strbuf_getline_lf(&author, stdin) != EOF) {
20                 const char *v;
21                 if (!skip_prefix(author.buf, "Author: ", &v) &&
22                     !skip_prefix(author.buf, "author ", &v))
23                         continue;
24                 while (strbuf_getline_lf(&oneline, stdin) != EOF &&
25                        oneline.len)
26                         ; /* discard headers */
27                 while (strbuf_getline_lf(&oneline, stdin) != EOF &&
28                        !oneline.len)
29                         ; /* discard blanks */
30                 shortlog_insert_one_record(log, v, oneline.buf);
31         }
32         strbuf_release(&author);
33         strbuf_release(&oneline);
34 }
35
36 static void get_from_rev(struct rev_info *rev, struct shortlog *log)
37 {
38         struct commit *commit;
39
40         if (prepare_revision_walk(rev))
41                 die(_("revision walk setup failed"));
42         while ((commit = get_revision(rev)) != NULL)
43                 shortlog_add_commit(log, commit);
44 }
45
46 static int parse_uint(char const **arg, int comma, int defval)
47 {
48         unsigned long ul;
49         int ret;
50         char *endp;
51
52         ul = strtoul(*arg, &endp, 10);
53         if (*endp && *endp != comma)
54                 return -1;
55         if (ul > INT_MAX)
56                 return -1;
57         ret = *arg == endp ? defval : (int)ul;
58         *arg = *endp ? endp + 1 : endp;
59         return ret;
60 }
61
62 static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]";
63
64 static int parse_wrap_args(const struct option *opt, const char *arg, int unset)
65 {
66         struct shortlog *log = opt->value;
67
68         log->wrap_lines = !unset;
69         if (unset)
70                 return 0;
71         if (!arg) {
72                 log->wrap = DEFAULT_WRAPLEN;
73                 log->in1 = DEFAULT_INDENT1;
74                 log->in2 = DEFAULT_INDENT2;
75                 return 0;
76         }
77
78         log->wrap = parse_uint(&arg, ',', DEFAULT_WRAPLEN);
79         log->in1 = parse_uint(&arg, ',', DEFAULT_INDENT1);
80         log->in2 = parse_uint(&arg, '\0', DEFAULT_INDENT2);
81         if (log->wrap < 0 || log->in1 < 0 || log->in2 < 0)
82                 return error(wrap_arg_usage);
83         if (log->wrap &&
84             ((log->in1 && log->wrap <= log->in1) ||
85              (log->in2 && log->wrap <= log->in2)))
86                 return error(wrap_arg_usage);
87         return 0;
88 }
89
90 int cmd_shortlog(int argc, const char **argv, const char *prefix)
91 {
92         static struct shortlog log;
93         static struct rev_info rev;
94         int nongit = !startup_info->have_repository;
95
96         static const struct option options[] = {
97                 OPT_BOOL('n', "numbered", &log.sort_by_number,
98                          N_("sort output according to the number of commits per author")),
99                 OPT_BOOL('s', "summary", &log.summary,
100                          N_("Suppress commit descriptions, only provides commit count")),
101                 OPT_BOOL('e', "email", &log.email,
102                          N_("Show the email address of each author")),
103                 { OPTION_CALLBACK, 'w', NULL, &log, N_("w[,i1[,i2]]"),
104                         N_("Linewrap output"), PARSE_OPT_OPTARG, &parse_wrap_args },
105                 OPT_END(),
106         };
107
108         struct parse_opt_ctx_t ctx;
109
110         git_config(git_default_config, NULL);
111         shortlog_init(&log);
112         init_revisions(&rev, prefix);
113         parse_options_start(&ctx, argc, argv, prefix, options,
114                             PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
115
116         for (;;) {
117                 switch (parse_options_step(&ctx, options, shortlog_usage)) {
118                 case PARSE_OPT_HELP:
119                         exit(129);
120                 case PARSE_OPT_DONE:
121                         goto parse_done;
122                 }
123                 parse_revision_opt(&rev, &ctx, options, shortlog_usage);
124         }
125 parse_done:
126         argc = parse_options_end(&ctx);
127
128         if (setup_revisions(argc, argv, &rev, NULL) != 1) {
129                 error(_("unrecognized argument: %s"), argv[1]);
130                 usage_with_options(shortlog_usage, options);
131         }
132
133         log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT;
134         log.abbrev = rev.abbrev;
135
136         /* assume HEAD if from a tty */
137         if (!nongit && !rev.pending.nr && isatty(0))
138                 add_head_to_pending(&rev);
139         if (rev.pending.nr == 0) {
140                 if (isatty(0))
141                         fprintf(stderr, _("(reading log message from standard input)\n"));
142                 read_from_stdin(&log);
143         }
144         else
145                 get_from_rev(&rev, &log);
146
147         shortlog_output(&log);
148         return 0;
149 }