clang-tidy: add Chrome-inspired checks
[lttng-tools.git] / src / common / argpar-utils / argpar-utils.cpp
CommitLineData
d50d200a
SM
1/*
2 * Copyright (C) 2021 Simon Marchi <simon.marchi@efficios.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
c9e313bc 8#include "argpar-utils.hpp"
d50d200a 9
c9e313bc
SM
10#include <common/error.hpp>
11#include <common/string-utils/string-utils.hpp>
d50d200a 12
28ab034a
JG
13#include <stdio.h>
14
d50d200a
SM
15/*
16 * Given argpar error status `status` and error `error`, return a formatted
17 * error message describing the error.
18 *
19 * `argv` is the argument vector that was being parsed.
20 *
21 * `context_fmt`, if non-NULL, is formatted using `args` and prepended to the
22 * error message.
23 *
35c4b2b3
SM
24 * Add `argc_offset` the the argument index mentioned in the error message.
25 *
d50d200a
SM
26 * The returned string must be freed by the caller.
27 */
28ab034a
JG
28static ATTR_FORMAT_PRINTF(4, 0) char *format_arg_error_v(const struct argpar_error *error,
29 int argc_offset,
30 const char **argv,
31 const char *context_fmt,
32 va_list args)
d50d200a 33{
cd9adb8b
JG
34 char *str = nullptr;
35 char *str_ret = nullptr;
d50d200a
SM
36 int ret;
37
38 if (context_fmt) {
39 ret = vasprintf(&str, context_fmt, args);
40 if (ret == -1) {
41 /*
42 * If vasprintf fails, the content of str is undefined,
43 * and we shouldn't try to free it.
44 */
cd9adb8b 45 str = nullptr;
d50d200a
SM
46 goto end;
47 }
48
49 ret = strutils_append_str(&str, ": ");
50 if (ret < 0) {
51 goto end;
52 }
53 }
54
28ab034a 55 switch (argpar_error_type(error)) {
d50d200a
SM
56 case ARGPAR_ERROR_TYPE_MISSING_OPT_ARG:
57 {
b1a5fb96 58 const int orig_index = argpar_error_orig_index(error);
d50d200a
SM
59 const char *arg = argv[orig_index];
60
61 ret = strutils_appendf(&str,
28ab034a
JG
62 WHILE_PARSING_ARG_N_ARG_FMT
63 "Missing required argument for option `%s`",
64 orig_index + 1 + argc_offset,
65 argv[orig_index],
66 arg);
d50d200a
SM
67 if (ret < 0) {
68 goto end;
69 }
70
71 break;
72 }
73 case ARGPAR_ERROR_TYPE_UNEXPECTED_OPT_ARG:
74 {
75 bool is_short;
28ab034a 76 const struct argpar_opt_descr *descr = argpar_error_opt_descr(error, &is_short);
d50d200a
SM
77 int orig_index = argpar_error_orig_index(error);
78 const char *arg = argv[orig_index];
79
80 if (is_short) {
81 ret = strutils_appendf(&str,
28ab034a
JG
82 WHILE_PARSING_ARG_N_ARG_FMT
83 "Unexpected argument for option `-%c`",
84 orig_index + 1 + argc_offset,
85 arg,
86 descr->short_name);
d50d200a
SM
87 } else {
88 ret = strutils_appendf(&str,
28ab034a
JG
89 WHILE_PARSING_ARG_N_ARG_FMT
90 "Unexpected argument for option `--%s`",
91 orig_index + 1 + argc_offset,
92 arg,
93 descr->long_name);
d50d200a
SM
94 }
95
96 if (ret < 0) {
97 goto end;
98 }
99
100 break;
101 }
102 case ARGPAR_ERROR_TYPE_UNKNOWN_OPT:
103 {
b1a5fb96 104 int orig_index = argpar_error_orig_index(error);
d50d200a
SM
105 const char *unknown_opt = argpar_error_unknown_opt_name(error);
106
107 ret = strutils_appendf(&str,
28ab034a
JG
108 WHILE_PARSING_ARG_N_ARG_FMT "Unknown option `%s`",
109 orig_index + 1 + argc_offset,
110 argv[orig_index],
111 unknown_opt);
d50d200a
SM
112
113 if (ret < 0) {
114 goto end;
115 }
116
117 break;
118 }
119 default:
28ab034a 120 abort();
d50d200a
SM
121 }
122
123 str_ret = str;
cd9adb8b 124 str = nullptr;
d50d200a
SM
125
126end:
127 free(str);
128 return str_ret;
129}
130
131enum parse_next_item_status parse_next_item(struct argpar_iter *iter,
28ab034a
JG
132 const struct argpar_item **item,
133 int argc_offset,
134 const char **argv,
135 bool unknown_opt_is_error,
136 const struct argpar_error **error_out,
137 const char *context_fmt,
138 ...)
d50d200a
SM
139{
140 enum argpar_iter_next_status status;
cd9adb8b 141 const struct argpar_error *error = nullptr;
d50d200a
SM
142 enum parse_next_item_status ret;
143
144 ARGPAR_ITEM_DESTROY_AND_RESET(*item);
145 status = argpar_iter_next(iter, item, &error);
146
147 switch (status) {
148 case ARGPAR_ITER_NEXT_STATUS_ERROR_MEMORY:
149 ERR("Failed to get next argpar item.");
ef9ff9cb 150 ret = PARSE_NEXT_ITEM_STATUS_ERROR_MEMORY;
d50d200a
SM
151 break;
152 case ARGPAR_ITER_NEXT_STATUS_ERROR:
153 {
154 va_list args;
155 char *err_str;
156
157 if (argpar_error_type(error) == ARGPAR_ERROR_TYPE_UNKNOWN_OPT &&
28ab034a 158 !unknown_opt_is_error) {
d50d200a
SM
159 ret = PARSE_NEXT_ITEM_STATUS_END;
160 break;
161 }
162
163 va_start(args, context_fmt);
28ab034a 164 err_str = format_arg_error_v(error, argc_offset, argv, context_fmt, args);
d50d200a
SM
165 va_end(args);
166
167 if (err_str) {
168 ERR("%s", err_str);
169 free(err_str);
170 } else {
171 ERR("%s", "Failed to format argpar error.");
172 }
173
174 ret = PARSE_NEXT_ITEM_STATUS_ERROR;
175 break;
176 }
177 case ARGPAR_ITER_NEXT_STATUS_END:
178 ret = PARSE_NEXT_ITEM_STATUS_END;
179 break;
180 case ARGPAR_ITER_NEXT_STATUS_OK:
181 ret = PARSE_NEXT_ITEM_STATUS_OK;
182 break;
183 default:
184 abort();
185 }
186
ef9ff9cb
SM
187 if (error_out) {
188 argpar_error_destroy(*error_out);
189 *error_out = error;
cd9adb8b 190 error = nullptr;
ef9ff9cb
SM
191 }
192
d50d200a
SM
193 argpar_error_destroy(error);
194
195 return ret;
196}
This page took 0.041267 seconds and 4 git commands to generate.