1 #include "git-compat-util.h"
2 #include "parse-options.h"
13 static inline const char *get_arg(struct optparse_t
*p
)
16 const char *res
= p
->opt
;
24 static inline const char *skip_prefix(const char *str
, const char *prefix
)
26 size_t len
= strlen(prefix
);
27 return strncmp(str
, prefix
, len
) ? NULL
: str
+ len
;
30 static int opterror(const struct option
*opt
, const char *reason
, int flags
)
32 if (flags
& OPT_SHORT
)
33 return error("switch `%c' %s", opt
->short_name
, reason
);
34 if (flags
& OPT_UNSET
)
35 return error("option `no-%s' %s", opt
->long_name
, reason
);
36 return error("option `%s' %s", opt
->long_name
, reason
);
39 static int get_value(struct optparse_t
*p
,
40 const struct option
*opt
, int flags
)
44 if (p
->opt
&& (flags
& OPT_UNSET
))
45 return opterror(opt
, "takes no value", flags
);
49 if (!(flags
& OPT_SHORT
) && p
->opt
)
50 return opterror(opt
, "takes no value", flags
);
51 if (flags
& OPT_UNSET
)
52 *(int *)opt
->value
= 0;
54 (*(int *)opt
->value
)++;
58 if (flags
& OPT_UNSET
) {
59 *(const char **)opt
->value
= (const char *)NULL
;
62 if (!p
->opt
&& p
->argc
<= 1)
63 return opterror(opt
, "requires a value", flags
);
64 *(const char **)opt
->value
= get_arg(p
);
68 if (flags
& OPT_UNSET
) {
69 *(int *)opt
->value
= 0;
72 if (!p
->opt
&& p
->argc
<= 1)
73 return opterror(opt
, "requires a value", flags
);
74 *(int *)opt
->value
= strtol(get_arg(p
), (char **)&s
, 10);
76 return opterror(opt
, "expects a numerical value", flags
);
80 die("should not happen, someone must be hit on the forehead");
84 static int parse_short_opt(struct optparse_t
*p
, const struct option
*options
)
86 for (; options
->type
!= OPTION_END
; options
++) {
87 if (options
->short_name
== *p
->opt
) {
88 p
->opt
= p
->opt
[1] ? p
->opt
+ 1 : NULL
;
89 return get_value(p
, options
, OPT_SHORT
);
92 return error("unknown switch `%c'", *p
->opt
);
95 static int parse_long_opt(struct optparse_t
*p
, const char *arg
,
96 const struct option
*options
)
98 for (; options
->type
!= OPTION_END
; options
++) {
102 if (!options
->long_name
)
105 rest
= skip_prefix(arg
, options
->long_name
);
107 if (strncmp(arg
, "no-", 3))
110 rest
= skip_prefix(arg
+ 3, options
->long_name
);
119 return get_value(p
, options
, flags
);
121 return error("unknown option `%s'", arg
);
124 int parse_options(int argc
, const char **argv
, const struct option
*options
,
125 const char * const usagestr
[], int flags
)
127 struct optparse_t args
= { argv
+ 1, argc
- 1, NULL
};
130 for (; args
.argc
; args
.argc
--, args
.argv
++) {
131 const char *arg
= args
.argv
[0];
133 if (*arg
!= '-' || !arg
[1]) {
134 argv
[j
++] = args
.argv
[0];
141 if (*args
.opt
== 'h')
142 usage_with_options(usagestr
, options
);
143 if (parse_short_opt(&args
, options
) < 0)
144 usage_with_options(usagestr
, options
);
149 if (!arg
[2]) { /* "--" */
150 if (!(flags
& PARSE_OPT_KEEP_DASHDASH
)) {
157 if (!strcmp(arg
+ 2, "help"))
158 usage_with_options(usagestr
, options
);
159 if (parse_long_opt(&args
, arg
+ 2, options
))
160 usage_with_options(usagestr
, options
);
163 memmove(argv
+ j
, args
.argv
, args
.argc
* sizeof(*argv
));
164 argv
[j
+ args
.argc
] = NULL
;
165 return j
+ args
.argc
;
168 #define USAGE_OPTS_WIDTH 24
171 void usage_with_options(const char * const *usagestr
,
172 const struct option
*opts
)
174 fprintf(stderr
, "usage: %s\n", *usagestr
++);
175 while (*usagestr
&& **usagestr
)
176 fprintf(stderr
, " or: %s\n", *usagestr
++);
178 fprintf(stderr
, " %s\n", *usagestr
++);
180 if (opts
->type
!= OPTION_GROUP
)
183 for (; opts
->type
!= OPTION_END
; opts
++) {
187 if (opts
->type
== OPTION_GROUP
) {
190 fprintf(stderr
, "%s\n", opts
->help
);
194 pos
= fprintf(stderr
, " ");
195 if (opts
->short_name
)
196 pos
+= fprintf(stderr
, "-%c", opts
->short_name
);
197 if (opts
->long_name
&& opts
->short_name
)
198 pos
+= fprintf(stderr
, ", ");
200 pos
+= fprintf(stderr
, "--%s", opts
->long_name
);
202 switch (opts
->type
) {
204 pos
+= fprintf(stderr
, " <n>");
208 pos
+= fprintf(stderr
, " <%s>", opts
->argh
);
210 pos
+= fprintf(stderr
, " ...");
216 if (pos
<= USAGE_OPTS_WIDTH
)
217 pad
= USAGE_OPTS_WIDTH
- pos
;
220 pad
= USAGE_OPTS_WIDTH
;
222 fprintf(stderr
, "%*s%s\n", pad
+ USAGE_GAP
, "", opts
->help
);