aboutsummaryrefslogtreecommitdiffstats
path: root/src/querykv1/cliopts.cpp
diff options
context:
space:
mode:
authorLibravatar Rutger Broekhoff2024-05-02 20:27:40 +0200
committerLibravatar Rutger Broekhoff2024-05-02 20:27:40 +0200
commit17a3ea880402338420699e03bcb24181e4ff3924 (patch)
treeda666ef91e0b60d20aa0b01529644c136fd1f4ab /src/querykv1/cliopts.cpp
downloadoeuf-17a3ea880402338420699e03bcb24181e4ff3924.tar.gz
oeuf-17a3ea880402338420699e03bcb24181e4ff3924.zip
Initial commit
Based on dc4ba6a
Diffstat (limited to 'src/querykv1/cliopts.cpp')
-rw-r--r--src/querykv1/cliopts.cpp456
1 files changed, 456 insertions, 0 deletions
diff --git a/src/querykv1/cliopts.cpp b/src/querykv1/cliopts.cpp
new file mode 100644
index 0000000..bef7a98
--- /dev/null
+++ b/src/querykv1/cliopts.cpp
@@ -0,0 +1,456 @@
1// vim:set sw=2 ts=2 sts et:
2
3#include <cstdlib>
4#include <cstdio>
5#include <string>
6#include <string_view>
7
8#include <getopt.h>
9
10#include "cliopts.hpp"
11
12using namespace std::string_view_literals;
13
14const char *opt_set = "";
15const char *opt_unset = nullptr;
16
17const char help[] = R"(Usage: %1$s [OPTIONS] <COMMAND>
18
19Global Options:
20 --kv1 <PATH> Path to file containing all KV1 data, '-' for stdin
21 -h, --help Print this help
22
23Commands:
24 joparoute Generate CSV for journey pattern route
25 journeyinfo Print some information on a journey
26 journeyroute Generate CSV for journey route
27 journeys List journeys of a specific line going from stop A to B
28 schedule Generate schedule
29)";
30
31const char joparoute_help[] = R"(Usage: %1$s joparoute --line <NUMBER> --jopa <CODE> [OPTIONS]
32
33Options:
34 --line <NUMBER> Line planning number as in schedule
35 --jopa <CODE> Journey pattern code as in KV1 data
36 -o <PATH> Path of file to write to, '-' for stdout
37
38Global Options:
39 --kv1 <PATH> Path to file containing all KV1 data, '-' for stdin
40 -h, --help Print this help
41)";
42
43const char journeyroute_help[] = R"(Usage: %1$s journeyroute --line <NUMBER> [OPTIONS]
44
45Options:
46 --line <NUMBER> Line planning number as in KV1 data
47 --journey <NUMBER> Journey number as in KV1 data
48 -o <PATH> Path of file to write to, '-' for stdout
49
50Global Options:
51 --kv1 <PATH> Path to file containing all KV1 data, '-' for stdin
52 -h, --help Print this help
53)";
54
55const char journeys_help[] = R"(Usage: %1$s journeys --line <NUMBER> --begin <STOP> --end <STOP> [OPTIONS]
56
57For the --begin and --end arguments, use the following format:
58 --begin/--end stop:<USRSTOP CODE>
59 --begin/--end star:<USRSTAR CODE>
60
61Options:
62 --begin <STOP> User stop code/area of stop the journey should begin at
63 --end <STOP> User stop code/area of stop the journey should end at
64 --line <NUMBER> Line planning number to filter on
65 -o <PATH> Path of file to write to, '-' for stdout
66
67Global Options:
68 --kv1 <PATH> Path to file containing all KV1 data, '-' for stdin
69 -h, --help Print this help
70)";
71
72const char journeyinfo_help[] = R"(Usage: %1$s journeyinfo --line <NUMBER> --journey <NUMBER> [OPTIONS]
73
74Options:
75 --line <NUMBER> Line planning number to filter on
76 --journey <NUMBER> Journey number as in schedule
77
78Global Options:
79 --kv1 <PATH> Path to file containing all KV1 data, '-' for stdin
80 -h, --help Print this help
81)";
82
83const char schedule_help[] = R"(Usage: %1$s schedule --line <NUMBER> [OPTIONS]
84
85Options:
86 --line <NUMBER> Line planning number to generate schedule for
87 -o <PATH> Path of file to write to, '-' for stdout
88
89Global Options:
90 --kv1 <PATH> Path to file containing all KV1 data, '-' for stdin
91 -h, --help Print this help
92)";
93
94void journeyRouteValidateOptions(const char *progname, Options *options) {
95#define X(name, argument, long_, short_) \
96 if (#name != "kv1_file_path"sv && #name != "line_planning_number"sv \
97 && #name != "journey_number"sv && #name != "help"sv && #name != "output_file_path"sv) \
98 if (options->name) { \
99 if (long_) { \
100 if (short_) fprintf(stderr, "%s: unexpected flag --%s (-%c) for journeyroute subcommand\n\n", progname, static_cast<const char *>(long_), short_); \
101 else fprintf(stderr, "%s: unexpected flag --%s for journeyroute subcommand\n\n", progname, static_cast<const char *>(long_)); \
102 } else if (short_) fprintf(stderr, "%s: unexpected flag -%c for journeyroute subcommand\n\n", progname, short_); \
103 fprintf(stderr, journeyroute_help, progname); \
104 exit(1); \
105 }
106 LONG_OPTIONS
107 SHORT_OPTIONS
108#undef X
109
110 if (options->positional.size() > 0) {
111 fprintf(stderr, "%s: unexpected positional argument(s) for journeyroute subcommand\n\n", progname);
112 for (auto pos : options->positional) fprintf(stderr, "opt: %s\n", pos);
113 fprintf(stderr, journeyroute_help, progname);
114 exit(1);
115 }
116
117 if (!options->kv1_file_path)
118 options->kv1_file_path = "-";
119 if (!options->output_file_path)
120 options->output_file_path = "-";
121 if (options->kv1_file_path == ""sv) {
122 fprintf(stderr, "%s: KV1 file path cannot be empty\n\n", progname);
123 fprintf(stderr, journeyroute_help, progname);
124 exit(1);
125 }
126 if (options->output_file_path == ""sv) {
127 fprintf(stderr, "%s: output file path cannot be empty\n\n", progname);
128 fprintf(stderr, journeyroute_help, progname);
129 exit(1);
130 }
131 if (!options->journey_number || options->journey_number == ""sv) {
132 fprintf(stderr, "%s: journey number must be provided\n\n", progname);
133 fprintf(stderr, journeyroute_help, progname);
134 exit(1);
135 }
136 if (!options->line_planning_number || options->line_planning_number == ""sv) {
137 fprintf(stderr, "%s: line planning number must be provided\n\n", progname);
138 fprintf(stderr, journeyroute_help, progname);
139 exit(1);
140 }
141}
142
143void scheduleValidateOptions(const char *progname, Options *options) {
144#define X(name, argument, long_, short_) \
145 if (#name != "kv1_file_path"sv && #name != "help"sv \
146 && #name != "line_planning_number"sv && #name != "output_file_path"sv) \
147 if (options->name) { \
148 if (long_) { \
149 if (short_) fprintf(stderr, "%s: unexpected flag --%s (-%c) for schedule subcommand\n\n", progname, static_cast<const char *>(long_), short_); \
150 else fprintf(stderr, "%s: unexpected flag --%s for schedule subcommand\n\n", progname, static_cast<const char *>(long_)); \
151 } else if (short_) fprintf(stderr, "%s: unexpected flag -%c for schedule subcommand\n\n", progname, short_); \
152 fprintf(stderr, schedule_help, progname); \
153 exit(1); \
154 }
155 LONG_OPTIONS
156 SHORT_OPTIONS
157#undef X
158
159 if (options->positional.size() > 0) {
160 fprintf(stderr, "%s: unexpected positional argument(s) for schedule subcommand\n\n", progname);
161 for (auto pos : options->positional) fprintf(stderr, "opt: %s\n", pos);
162 fprintf(stderr, schedule_help, progname);
163 exit(1);
164 }
165
166 if (!options->kv1_file_path)
167 options->kv1_file_path = "-";
168 if (!options->output_file_path)
169 options->output_file_path = "-";
170 if (options->kv1_file_path == ""sv) {
171 fprintf(stderr, "%s: KV1 file path cannot be empty\n\n", progname);
172 fprintf(stderr, schedule_help, progname);
173 exit(1);
174 }
175 if (options->output_file_path == ""sv) {
176 fprintf(stderr, "%s: output file path cannot be empty\n\n", progname);
177 fprintf(stderr, schedule_help, progname);
178 exit(1);
179 }
180 if (!options->line_planning_number || options->line_planning_number == ""sv) {
181 fprintf(stderr, "%s: line planning number must be provided\n\n", progname);
182 fprintf(stderr, schedule_help, progname);
183 exit(1);
184 }
185}
186
187void journeysValidateOptions(const char *progname, Options *options) {
188#define X(name, argument, long_, short_) \
189 if (#name != "kv1_file_path"sv && #name != "help"sv \
190 && #name != "line_planning_number"sv && #name != "output_file_path"sv \
191 && #name != "begin_stop_code"sv && #name != "end_stop_code"sv) \
192 if (options->name) { \
193 if (long_) { \
194 if (short_) fprintf(stderr, "%s: unexpected flag --%s (-%c) for journeys subcommand\n\n", progname, static_cast<const char *>(long_), short_); \
195 else fprintf(stderr, "%s: unexpected flag --%s for journeys subcommand\n\n", progname, static_cast<const char *>(long_)); \
196 } else if (short_) fprintf(stderr, "%s: unexpected flag -%c for journeys subcommand\n\n", progname, short_); \
197 fprintf(stderr, journeys_help, progname); \
198 exit(1); \
199 }
200 LONG_OPTIONS
201 SHORT_OPTIONS
202#undef X
203
204 if (options->positional.size() > 0) {
205 fprintf(stderr, "%s: unexpected positional argument(s) for journeys subcommand\n\n", progname);
206 for (auto pos : options->positional) fprintf(stderr, "opt: %s\n", pos);
207 fprintf(stderr, journeys_help, progname);
208 exit(1);
209 }
210
211 if (!options->kv1_file_path)
212 options->kv1_file_path = "-";
213 if (!options->output_file_path)
214 options->output_file_path = "-";
215 if (options->kv1_file_path == ""sv) {
216 fprintf(stderr, "%s: KV1 file path cannot be empty\n\n", progname);
217 fprintf(stderr, journeys_help, progname);
218 exit(1);
219 }
220 if (options->output_file_path == ""sv) {
221 fprintf(stderr, "%s: output file path cannot be empty\n\n", progname);
222 fprintf(stderr, journeys_help, progname);
223 exit(1);
224 }
225 if (!options->line_planning_number || options->line_planning_number == ""sv) {
226 fprintf(stderr, "%s: line planning number must be provided\n\n", progname);
227 fprintf(stderr, journeys_help, progname);
228 exit(1);
229 }
230 if (!options->begin_stop_code || options->begin_stop_code == ""sv) {
231 fprintf(stderr, "%s: start user stop code must be provided\n\n", progname);
232 fprintf(stderr, journeys_help, progname);
233 exit(1);
234 }
235 if (!options->end_stop_code || options->end_stop_code == ""sv) {
236 fprintf(stderr, "%s: end user stop code must be provided\n\n", progname);
237 fprintf(stderr, journeys_help, progname);
238 exit(1);
239 }
240 if (!std::string_view(options->begin_stop_code).starts_with("star:")
241 && !std::string_view(options->begin_stop_code).starts_with("stop:")) {
242 fprintf(stderr, "%s: begin user stop code must be prefixed with star:/stop:\n\n", progname);
243 fprintf(stderr, journeys_help, progname);
244 exit(1);
245 }
246 if (!std::string_view(options->end_stop_code).starts_with("star:")
247 && !std::string_view(options->end_stop_code).starts_with("stop:")) {
248 fprintf(stderr, "%s: end user stop code must be prefixed with star:/stop:\n\n", progname);
249 fprintf(stderr, journeys_help, progname);
250 exit(1);
251 }
252}
253
254void journeyInfoValidateOptions(const char *progname, Options *options) {
255#define X(name, argument, long_, short_) \
256 if (#name != "kv1_file_path"sv && #name != "line_planning_number"sv \
257 && #name != "journey_number"sv && #name != "help"sv) \
258 if (options->name) { \
259 if (long_) { \
260 if (short_) fprintf(stderr, "%s: unexpected flag --%s (-%c) for journeyinfo subcommand\n\n", progname, static_cast<const char *>(long_), short_); \
261 else fprintf(stderr, "%s: unexpected flag --%s for journeyinfo subcommand\n\n", progname, static_cast<const char *>(long_)); \
262 } else if (short_) fprintf(stderr, "%s: unexpected flag -%c for journeyinfo subcommand\n\n", progname, short_); \
263 fprintf(stderr, journeyinfo_help, progname); \
264 exit(1); \
265 }
266 LONG_OPTIONS
267 SHORT_OPTIONS
268#undef X
269
270 if (options->positional.size() > 0) {
271 fprintf(stderr, "%s: unexpected positional argument(s) for journeyinfo subcommand\n\n", progname);
272 for (auto pos : options->positional) fprintf(stderr, "opt: %s\n", pos);
273 fprintf(stderr, journeyinfo_help, progname);
274 exit(1);
275 }
276
277 if (!options->kv1_file_path)
278 options->kv1_file_path = "-";
279 if (options->kv1_file_path == ""sv) {
280 fprintf(stderr, "%s: KV1 file path cannot be empty\n\n", progname);
281 fprintf(stderr, journeyinfo_help, progname);
282 exit(1);
283 }
284 if (!options->journey_number || options->journey_number == ""sv) {
285 fprintf(stderr, "%s: journey number must be provided\n\n", progname);
286 fprintf(stderr, journeyinfo_help, progname);
287 exit(1);
288 }
289 if (!options->line_planning_number || options->line_planning_number == ""sv) {
290 fprintf(stderr, "%s: line planning number must be provided\n\n", progname);
291 fprintf(stderr, journeyinfo_help, progname);
292 exit(1);
293 }
294}
295
296void jopaRouteValidateOptions(const char *progname, Options *options) {
297#define X(name, argument, long_, short_) \
298 if (#name != "kv1_file_path"sv && #name != "line_planning_number"sv \
299 && #name != "journey_pattern_code"sv && #name != "help"sv && #name != "output_file_path"sv) \
300 if (options->name) { \
301 if (long_) { \
302 if (short_) fprintf(stderr, "%s: unexpected flag --%s (-%c) for joparoute subcommand\n\n", progname, static_cast<const char *>(long_), short_); \
303 else fprintf(stderr, "%s: unexpected flag --%s for joparoute subcommand\n\n", progname, static_cast<const char *>(long_)); \
304 } else if (short_) fprintf(stderr, "%s: unexpected flag -%c for joparoute subcommand\n\n", progname, short_); \
305 fprintf(stderr, joparoute_help, progname); \
306 exit(1); \
307 }
308 LONG_OPTIONS
309 SHORT_OPTIONS
310#undef X
311
312 if (options->positional.size() > 0) {
313 fprintf(stderr, "%s: unexpected positional argument(s) for joparoute subcommand\n\n", progname);
314 for (auto pos : options->positional) fprintf(stderr, "opt: %s\n", pos);
315 fprintf(stderr, joparoute_help, progname);
316 exit(1);
317 }
318
319 if (!options->kv1_file_path)
320 options->kv1_file_path = "-";
321 if (!options->output_file_path)
322 options->output_file_path = "-";
323 if (options->kv1_file_path == ""sv) {
324 fprintf(stderr, "%s: KV1 file path cannot be empty\n\n", progname);
325 fprintf(stderr, joparoute_help, progname);
326 exit(1);
327 }
328 if (options->output_file_path == ""sv) {
329 fprintf(stderr, "%s: output file path cannot be empty\n\n", progname);
330 fprintf(stderr, joparoute_help, progname);
331 exit(1);
332 }
333 if (!options->journey_pattern_code || options->journey_pattern_code == ""sv) {
334 fprintf(stderr, "%s: journey pattern code must be provided\n\n", progname);
335 fprintf(stderr, joparoute_help, progname);
336 exit(1);
337 }
338 if (!options->line_planning_number || options->line_planning_number == ""sv) {
339 fprintf(stderr, "%s: line planning number must be provided\n\n", progname);
340 fprintf(stderr, joparoute_help, progname);
341 exit(1);
342 }
343}
344
345struct ShortFlag {
346 int has_arg;
347 int c;
348};
349
350template<ShortFlag ...flags>
351const std::string mkargarr =
352 (std::string()
353 + ...
354 + (flags.c == 0
355 ? ""
356 : std::string((const char[]){ flags.c, '\0' })
357 + (flags.has_arg == required_argument
358 ? ":"
359 : flags.has_arg == optional_argument
360 ? "::"
361 : "")));
362
363#define X(name, has_arg, long_, short_) ShortFlag(has_arg, short_),
364const std::string argarr = mkargarr<SHORT_OPTIONS LONG_OPTIONS ShortFlag(no_argument, 0)>;
365#undef X
366
367Options parseOptions(int argc, char *argv[]) {
368 const char *progname = argv[0];
369
370 // Struct with options for augmentkv6.
371 Options options;
372
373 static option long_options[] = {
374#define X(name, argument, long_, short_) { long_, argument, nullptr, short_ },
375 LONG_OPTIONS
376#undef X
377 { 0 },
378 };
379
380 int c;
381 int option_index = 0;
382 bool error = false;
383 while ((c = getopt_long(argc, argv, argarr.c_str(), long_options, &option_index)) != -1) {
384 // If a long option was used, c corresponds with val. We have val = 0 for
385 // options which have no short alternative, so checking for c = 0 gives us
386 // whether a long option with no short alternative was used.
387 // Below, we check for c = 'h', which corresponds with the long option
388 // '--help', for which val = 'h'.
389 if (c == 0) {
390 const char *name = long_options[option_index].name;
391#define X(opt_name, opt_has_arg, opt_long, opt_short) \
392 if (name == opt_long ## sv) { options.opt_name = optarg; continue; }
393 LONG_OPTIONS
394#undef X
395 error = true;
396 }
397#define X(opt_name, opt_has_arg, opt_long, opt_short) \
398 if (c == opt_short) { options.opt_name = optarg ? optarg : opt_set; continue; }
399 LONG_OPTIONS
400 SHORT_OPTIONS
401#undef X
402 error = true;
403 }
404
405 if (optind < argc)
406 options.subcommand = argv[optind++];
407 while (optind < argc)
408 options.positional.push_back(argv[optind++]);
409
410 if (options.subcommand
411 && options.subcommand != "schedule"sv
412 && options.subcommand != "joparoute"sv
413 && options.subcommand != "journeyinfo"sv
414 && options.subcommand != "journeyroute"sv
415 && options.subcommand != "journeys"sv) {
416 fprintf(stderr, "%s: unknown subcommand '%s'\n\n", progname, options.subcommand);
417 fprintf(stderr, help, progname);
418 exit(1);
419 }
420 if (options.subcommand && error) {
421 fputc('\n', stderr);
422 if (options.subcommand == "joparoute"sv) fprintf(stderr, joparoute_help, progname);
423 if (options.subcommand == "journeyinfo"sv) fprintf(stderr, journeyinfo_help, progname);
424 if (options.subcommand == "journeyroute"sv) fprintf(stderr, journeyroute_help, progname);
425 if (options.subcommand == "journeys"sv) fprintf(stderr, journeys_help, progname);
426 if (options.subcommand == "schedule"sv) fprintf(stderr, schedule_help, progname);
427 exit(1);
428 }
429 if (error || !options.subcommand) {
430 if (!options.subcommand) fprintf(stderr, "%s: no subcommand provided\n", progname);
431 fputc('\n', stderr);
432 fprintf(stderr, help, progname);
433 exit(1);
434 }
435 if (options.help) {
436 if (options.subcommand == "joparoute"sv) fprintf(stderr, joparoute_help, progname);
437 if (options.subcommand == "journeyinfo"sv) fprintf(stderr, journeyinfo_help, progname);
438 if (options.subcommand == "journeyroute"sv) fprintf(stderr, journeyroute_help, progname);
439 if (options.subcommand == "journeys"sv) fprintf(stderr, journeys_help, progname);
440 if (options.subcommand == "schedule"sv) fprintf(stderr, schedule_help, progname);
441 exit(0);
442 }
443
444 if (options.subcommand == "joparoute"sv)
445 jopaRouteValidateOptions(progname, &options);
446 if (options.subcommand == "journeyinfo"sv)
447 journeyInfoValidateOptions(progname, &options);
448 if (options.subcommand == "journeyroute"sv)
449 journeyRouteValidateOptions(progname, &options);
450 if (options.subcommand == "journeys"sv)
451 journeysValidateOptions(progname, &options);
452 if (options.subcommand == "schedule"sv)
453 scheduleValidateOptions(progname, &options);
454
455 return options;
456}