ref-filter: add multiple-option parsing functions

The functions match_placeholder_arg_value() match_placeholder_bool_arg() were added in pretty 4f732e0fd7 (pretty: allow %(trailers) options with explicit value, 2019-01-29) to parse multiple options in an argument to --pretty. For example, git log --pretty="%(trailers:key=Signed-Off-By,separator=%x2C )" will output all the trailers matching the key and seperates them by a comma followed by a space per commit. Add similar functions, match_atom_arg_value() match_atom_bool_arg() in ref-filter. There is no atom yet that can use these functions in ref-filter, but we are going to add a new %(describe) atom in a subsequent commit where we parse options like tags=<bool-value> or match=<pattern> given to it. Helped-by: Junio C Hamano <gitster@pobox.com> Mentored-by: Christian Couder <christian.couder@gmail.com> Mentored-by: Hariom Verma <hariom18599@gmail.com> Signed-off-by: Kousik Sanagavarapu <five231003@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Kousik Sanagavarapu committed Jul 23, 2023 at 21:49 UTC f46094a5e6b80098786b4e1448be032dfbdf3f43
1 file changed +105
ref-filter.c
+105
@@ -1,6 +1,7 @@
1 #include "git-compat-util.h"
2 #include "environment.h"
3 #include "gettext.h"
4 +#include "config.h"
5 #include "gpg-interface.h"
6 #include "hex.h"
7 #include "parse-options.h"
@@ -255,6 +256,110 @@ static int err_bad_arg(struct strbuf *sb, const char *name, const char *arg)
256 return -1;
257 }
258
259 +/*
260 + * Parse option of name "candidate" in the option string "to_parse" of
261 + * the form
262 + *
263 + * "candidate1[=val1],candidate2[=val2],candidate3[=val3],..."
264 + *
265 + * The remaining part of "to_parse" is stored in "end" (if we are
266 + * parsing the last candidate, then this is NULL) and the value of
267 + * the candidate is stored in "valuestart" and its length in "valuelen",
268 + * that is the portion after "=". Since it is possible for a "candidate"
269 + * to not have a value, in such cases, "valuestart" is set to point to
270 + * NULL and "valuelen" to 0.
271 + *
272 + * The function returns 1 on success. It returns 0 if we don't find
273 + * "candidate" in "to_parse" or we find "candidate" but it is followed
274 + * by more chars (for example, "candidatefoo"), that is, we don't find
275 + * an exact match.
276 + *
277 + * This function only does the above for one "candidate" at a time. So
278 + * it has to be called each time trying to parse a "candidate" in the
279 + * option string "to_parse".
280 + */
281 +static int match_atom_arg_value(const char *to_parse, const char *candidate,
282 + const char **end, const char **valuestart,
283 + size_t *valuelen)
284 +{
285 + const char *atom;
286 +
287 + if (!skip_prefix(to_parse, candidate, &atom))
288 + return 0; /* definitely not "candidate" */
289 +
290 + if (*atom == '=') {
291 + /* we just saw "candidate=" */
292 + *valuestart = atom + 1;
293 + atom = strchrnul(*valuestart, ',');
294 + *valuelen = atom - *valuestart;
295 + } else if (*atom != ',' && *atom != '\0') {
296 + /* key begins with "candidate" but has more chars */
297 + return 0;
298 + } else {
299 + /* just "candidate" without "=val" */
300 + *valuestart = NULL;
301 + *valuelen = 0;
302 + }
303 +
304 + /* atom points at either the ',' or NUL after this key[=val] */
305 + if (*atom == ',')
306 + atom++;
307 + else if (*atom)
308 + BUG("Why is *atom not NULL yet?");
309 +
310 + *end = atom;
311 + return 1;
312 +}
313 +
314 +/*
315 + * Parse boolean option of name "candidate" in the option list "to_parse"
316 + * of the form
317 + *
318 + * "candidate1[=bool1],candidate2[=bool2],candidate3[=bool3],..."
319 + *
320 + * The remaining part of "to_parse" is stored in "end" (if we are parsing
321 + * the last candidate, then this is NULL) and the value (if given) is
322 + * parsed and stored in "val", so "val" always points to either 0 or 1.
323 + * If the value is not given, then "val" is set to point to 1.
324 + *
325 + * The boolean value is parsed using "git_parse_maybe_bool()", so the
326 + * accepted values are
327 + *
328 + * to set true - "1", "yes", "true"
329 + * to set false - "0", "no", "false"
330 + *
331 + * This function returns 1 on success. It returns 0 when we don't find
332 + * an exact match for "candidate" or when the boolean value given is
333 + * not valid.
334 + */
335 +static int match_atom_bool_arg(const char *to_parse, const char *candidate,
336 + const char **end, int *val)
337 +{
338 + const char *argval;
339 + char *strval;
340 + size_t arglen;
341 + int v;
342 +
343 + if (!match_atom_arg_value(to_parse, candidate, end, &argval, &arglen))
344 + return 0;
345 +
346 + if (!argval) {
347 + *val = 1;
348 + return 1;
349 + }
350 +
351 + strval = xstrndup(argval, arglen);
352 + v = git_parse_maybe_bool(strval);
353 + free(strval);
354 +
355 + if (v == -1)
356 + return 0;
357 +
358 + *val = v;
359 +
360 + return 1;
361 +}
362 +
363 static int color_atom_parser(struct ref_format *format, struct used_atom *atom,
364 const char *color_value, struct strbuf *err)
365 {