Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #include <kdbopts.h>
10 :
11 : #include <stdlib.h>
12 : #include <string.h>
13 :
14 : #include <kdbease.h>
15 : #include <kdbhelper.h>
16 : #include <kdbmeta.h>
17 :
18 : #include <kdberrors.h>
19 :
20 : #ifdef _WIN32
21 : static const char SEP_ENV_VALUE = ';';
22 : #else
23 : static const char SEP_ENV_VALUE = ':';
24 : #endif
25 :
26 : struct OptionData
27 : {
28 : Key * specKey;
29 : const char * metaKey;
30 : const char * hasArg;
31 : const char * kind;
32 : const char * flagValue;
33 : const char * argName;
34 : bool hidden;
35 : };
36 :
37 : struct Specification
38 : {
39 : KeySet * options;
40 : KeySet * keys;
41 : bool hasOpts;
42 : bool hasArgs;
43 : };
44 :
45 6912 : static inline const char * keyGetMetaString (const Key * key, const char * meta)
46 : {
47 6912 : const Key * mk = keyGetMeta (key, meta);
48 6912 : const char * value = mk == NULL ? NULL : keyString (mk);
49 6912 : return value != NULL && value[0] == '\0' ? NULL : value;
50 : }
51 :
52 : static int addProcKey (KeySet * ks, const Key * key, Key * valueKey);
53 : static KeySet * parseEnvp (const char ** envp);
54 :
55 : static KeySet * parseArgs (KeySet * optionsSpec, int argc, const char ** argv, Key * errorKey);
56 : static void setOption (Key * option, const char * value, bool repeated);
57 :
58 : static Key * splitEnvValue (const Key * envKey);
59 :
60 : static KeySet * ksMetaGetSingleOrArray (Key * key, const char * metaName);
61 :
62 : static char * generateUsageLine (const char * progname, bool hasOpts, bool hasArgs);
63 : static char * generateOptionsList (KeySet * keysWithOpts);
64 :
65 : static bool processSpec (struct Specification * spec, KeySet * ks, Key * parentKey);
66 : static bool processOptions (struct Specification * spec, Key * specKey, Key ** keyWithOpt, Key * errorKey);
67 : static bool readOptionData (struct OptionData * optionData, Key * key, const char * metaKey, Key * errorKey);
68 : static bool processShortOptSpec (struct Specification * spec, struct OptionData * optionData, Key ** keyWithOpt, char ** shortOptLine,
69 : Key * errorKey);
70 : static bool processLongOptSpec (struct Specification * spec, struct OptionData * optionData, Key ** keyWithOpt, char ** longOptLine,
71 : Key * errorKey);
72 : static bool processEnvVars (KeySet * usedEnvVars, Key * specKey, Key ** keyWithOpt, Key * errorKey);
73 :
74 : static int writeOptionValues (KeySet * ks, Key * keyWithOpt, KeySet * options, Key * errorKey);
75 : static int writeEnvVarValues (KeySet * ks, Key * keyWithOpt, KeySet * envValues, Key * errorKey);
76 : static int writeArgsValues (KeySet * ks, Key * keyWithOpt, KeySet * args);
77 :
78 : static bool parseLongOption (KeySet * optionsSpec, KeySet * options, int argc, const char ** argv, int * index, Key * errorKey);
79 : static bool parseShortOptions (KeySet * optionsSpec, KeySet * options, int argc, const char ** argv, int * index, Key * errorKey);
80 :
81 : /**
82 : * This functions parses a specification of program options, together with a list of arguments
83 : * and environment variables to extract the option values.
84 : *
85 : * The options have to be defined in the metadata of keys in the spec namespace. If an option value
86 : * is found for any of the given keys, a new key with the same path but inside the proc namespace
87 : * will be inserted into @p ks. This enables a cascading lookup to find these values.
88 : *
89 : * Take look at https://www.libelektra.org/tutorials/command-line-options for information on how exactly
90 : * the specification works.
91 : *
92 : * NOTE: Per default option processing DOES NOT stop, when a non-option string is encountered in @p argv.
93 : * If you want processing to stop, set the metadata `posixly = 1` on @p parentKey.
94 : *
95 : * The basic usage of this function is as follows:
96 : * @snippet optsSnippets.c basic use
97 : *
98 : *
99 : * @param ks The KeySet containing the specification for the options.
100 : * @param argc The number of strings in argv.
101 : * @param argv The arguments to be processed.
102 : * @param envp A list of environment variables. This needs to be a null-terminated list of
103 : * strings of the format 'KEY=VALUE'.
104 : * @param parentKey The parent key below which the function while search for option specifications.
105 : * Also used for error reporting. The key will be translated into the spec namespace
106 : * automatically, i.e. 'user/test/parent' will be translated into 'spec/test/parent',
107 : * before checking against spec keys.
108 : *
109 : * @retval 0 on success, this is the only case in which @p ks will be modified
110 : * @retval -1 on error, the error will be set as metadata in @p errorKey
111 : * @retval 1 if a help option (-h, --help) was found, use elektraGetOptsHelpMessage() access the
112 : * generated help message
113 : */
114 380 : int elektraGetOpts (KeySet * ks, int argc, const char ** argv, const char ** envp, Key * parentKey)
115 : {
116 380 : cursor_t initial = ksGetCursor (ks);
117 :
118 : struct Specification spec;
119 380 : if (!processSpec (&spec, ks, parentKey))
120 : {
121 14 : ksSetCursor (ks, initial);
122 14 : return -1;
123 : }
124 :
125 366 : KeySet * options = parseArgs (spec.options, argc, argv, parentKey);
126 :
127 366 : if (options == NULL)
128 : {
129 26 : ksDel (spec.options);
130 26 : ksDel (spec.keys);
131 26 : ksSetCursor (ks, initial);
132 26 : return -1;
133 : }
134 :
135 340 : Key * helpKey = ksLookupByName (options, "/short/h", 0);
136 340 : if (helpKey == NULL)
137 : {
138 332 : helpKey = ksLookupByName (options, "/long/help", 0);
139 : }
140 :
141 340 : if (helpKey != NULL)
142 : {
143 : // show help
144 16 : const char * progname = argv[0];
145 16 : char * lastSlash = strrchr (progname, '/');
146 16 : if (lastSlash != NULL)
147 : {
148 0 : progname = lastSlash + 1;
149 : }
150 :
151 32 : char * usage = generateUsageLine (progname, spec.hasOpts, spec.hasArgs);
152 16 : char * optionsText = generateOptionsList (spec.keys);
153 :
154 16 : keySetMeta (parentKey, "internal/libopts/help/usage", usage);
155 16 : keySetMeta (parentKey, "internal/libopts/help/options", optionsText);
156 :
157 16 : elektraFree (usage);
158 16 : elektraFree (optionsText);
159 16 : ksDel (options);
160 16 : ksDel (spec.options);
161 16 : ksDel (spec.keys);
162 16 : ksSetCursor (ks, initial);
163 16 : return 1;
164 : }
165 324 : ksDel (spec.options);
166 :
167 324 : KeySet * envValues = parseEnvp (envp);
168 :
169 324 : Key * argsParent = keyNew ("/args", KEY_END);
170 324 : KeySet * args = elektraArrayGet (argsParent, options);
171 324 : keyDel (argsParent);
172 :
173 : Key * keyWithOpt;
174 324 : ksRewind (spec.keys);
175 1086 : while ((keyWithOpt = ksNext (spec.keys)) != NULL)
176 : {
177 444 : int result = writeOptionValues (ks, keyWithOpt, options, parentKey);
178 444 : if (result < 0)
179 : {
180 4 : ksDel (envValues);
181 4 : ksDel (options);
182 4 : ksDel (spec.keys);
183 4 : ksDel (args);
184 4 : ksSetCursor (ks, initial);
185 4 : return -1;
186 : }
187 440 : else if (result > 0)
188 : {
189 234 : continue;
190 : }
191 :
192 206 : result = writeArgsValues (ks, keyWithOpt, args);
193 206 : if (result < 0)
194 : {
195 0 : ksDel (envValues);
196 0 : ksDel (options);
197 0 : ksDel (spec.keys);
198 0 : ksDel (args);
199 0 : ksSetCursor (ks, initial);
200 0 : return -1;
201 : }
202 206 : else if (result > 0)
203 : {
204 10 : continue;
205 : }
206 :
207 196 : result = writeEnvVarValues (ks, keyWithOpt, envValues, parentKey);
208 196 : if (result < 0)
209 : {
210 2 : ksDel (envValues);
211 2 : ksDel (options);
212 2 : ksDel (spec.keys);
213 2 : ksDel (args);
214 2 : ksSetCursor (ks, initial);
215 2 : return -1;
216 : }
217 : }
218 :
219 318 : ksDel (envValues);
220 318 : ksDel (options);
221 318 : ksDel (spec.keys);
222 318 : ksDel (args);
223 :
224 318 : ksSetCursor (ks, initial);
225 318 : return 0;
226 : }
227 :
228 : /**
229 : * Extracts the help message from the @p errorKey used in elektraGetOpts().
230 : *
231 : * @param errorKey The same Key as passed to elektraGetOpts() as errorKey.
232 : * @param usage If this is not NULL, it will be used instead of the default usage line.
233 : * @param prefix If this is not NULL, it will be inserted between the usage line and the options list.
234 : *
235 : * @return The full help message extracted from @p errorKey, or NULL if no help message was found.
236 : * The returned string has to be freed with elektraFree().
237 : */
238 16 : char * elektraGetOptsHelpMessage (Key * errorKey, const char * usage, const char * prefix)
239 : {
240 16 : if (usage == NULL)
241 : {
242 16 : usage = keyGetMetaString (errorKey, "internal/libopts/help/usage");
243 : }
244 :
245 16 : if (usage == NULL)
246 : {
247 : return NULL;
248 : }
249 :
250 16 : const char * options = keyGetMetaString (errorKey, "internal/libopts/help/options");
251 16 : if (options == NULL)
252 : {
253 8 : options = "";
254 : }
255 :
256 16 : return elektraFormat ("%s%s%s", usage, prefix == NULL ? "" : prefix, options);
257 : }
258 :
259 : // -------------
260 : // static functions
261 : // -------------
262 :
263 : /**
264 : * Process the specification set in the keys of @p ks, into @p spec.
265 : */
266 380 : bool processSpec (struct Specification * spec, KeySet * ks, Key * parentKey)
267 : {
268 380 : KeySet * usedEnvVars = ksNew (0, KS_END);
269 380 : spec->options = ksNew (
270 : 2,
271 : keyNew ("/short/h", KEY_META, "hasarg", "none", KEY_META, "kind", "single", KEY_META, "flagvalue", "1", KEY_META, "longopt",
272 : "/long/help", KEY_END),
273 : keyNew ("/long/help", KEY_META, "hasarg", "none", KEY_META, "kind", "single", KEY_META, "flagvalue", "1", KEY_END), KS_END);
274 380 : spec->keys = ksNew (0, KS_END);
275 :
276 380 : spec->hasOpts = false;
277 380 : spec->hasArgs = false;
278 :
279 380 : Key * specParent = keyDup (parentKey);
280 380 : if (keyGetNamespace (parentKey) != KEY_NS_SPEC)
281 : {
282 14 : keySetName (specParent, "spec");
283 :
284 14 : const char * parent = strchr (keyName (parentKey), '/');
285 14 : if (parent != NULL)
286 : {
287 14 : keyAddName (specParent, parent + 1);
288 : }
289 : }
290 :
291 380 : ksRewind (ks);
292 : Key * cur;
293 1518 : while ((cur = ksNext (ks)) != NULL)
294 : {
295 772 : if (keyGetNamespace (cur) != KEY_NS_SPEC || !keyIsBelowOrSame (specParent, cur))
296 : {
297 160 : continue;
298 : }
299 :
300 612 : Key * keyWithOpt = NULL;
301 :
302 612 : if (!processOptions (spec, cur, &keyWithOpt, parentKey))
303 : {
304 12 : keyDel (specParent);
305 12 : ksDel (spec->options);
306 12 : ksDel (spec->keys);
307 12 : ksDel (usedEnvVars);
308 12 : return false;
309 : }
310 :
311 600 : if (!processEnvVars (usedEnvVars, cur, &keyWithOpt, parentKey))
312 : {
313 0 : keyDel (specParent);
314 0 : ksDel (spec->options);
315 0 : ksDel (spec->keys);
316 0 : ksDel (usedEnvVars);
317 0 : return false;
318 : }
319 :
320 600 : const char * argsMeta = keyGetMetaString (cur, "args");
321 600 : if (argsMeta != NULL && elektraStrCmp (argsMeta, "remaining") == 0)
322 : {
323 20 : if (elektraStrCmp (keyBaseName (cur), "#") != 0)
324 : {
325 2 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (
326 : parentKey, "'args=remaining' can only be set on array keys (basename = '#'). Offending key: %s",
327 : keyName (cur));
328 2 : keyDel (specParent);
329 2 : ksDel (spec->options);
330 2 : ksDel (spec->keys);
331 2 : ksDel (usedEnvVars);
332 2 : return false;
333 : }
334 :
335 18 : if (keyWithOpt == NULL)
336 : {
337 18 : keyWithOpt = keyNew (keyName (cur), KEY_END);
338 : }
339 18 : keySetMeta (keyWithOpt, "args", "remaining");
340 18 : spec->hasArgs = true;
341 : }
342 :
343 598 : if (keyWithOpt != NULL)
344 : {
345 502 : ksAppendKey (spec->keys, keyWithOpt);
346 : }
347 : }
348 366 : keyDel (specParent);
349 366 : ksDel (usedEnvVars);
350 :
351 366 : return true;
352 : }
353 :
354 : /**
355 : * Process the option specification for @p specKey.
356 : *
357 : * @retval true on success
358 : * @retval false on error
359 : */
360 612 : bool processOptions (struct Specification * spec, Key * specKey, Key ** keyWithOpt, Key * errorKey)
361 : {
362 612 : const char * optHelp = keyGetMetaString (specKey, "opt/help");
363 612 : const char * description = keyGetMetaString (specKey, "description");
364 :
365 612 : const char * help = optHelp != NULL ? optHelp : (description != NULL ? description : "");
366 :
367 612 : KeySet * opts = ksMetaGetSingleOrArray (specKey, "opt");
368 612 : if (opts == NULL)
369 : {
370 178 : const char * longOpt = keyGetMetaString (specKey, "opt/long");
371 178 : if (longOpt == NULL)
372 : {
373 : return true;
374 : }
375 :
376 : // no other way to create Key with name "opt"
377 2 : Key * k = keyNew ("/", KEY_META, "opt", "", KEY_END);
378 2 : opts = ksNew (2, keyNew ("/#", KEY_END), keyGetMeta (k, "opt"), KS_END);
379 2 : keyDel (k);
380 : }
381 :
382 436 : ksRewind (opts);
383 436 : ksNext (opts); // skip count
384 : Key * metaKey;
385 :
386 436 : char * shortOptLine = elektraStrDup ("");
387 436 : char * longOptLine = elektraStrDup ("");
388 1358 : while ((metaKey = ksNext (opts)) != NULL)
389 : {
390 : struct OptionData optionData;
391 :
392 498 : if (!readOptionData (&optionData, specKey, keyName (metaKey), errorKey))
393 : {
394 2 : ksDel (opts);
395 2 : elektraFree (shortOptLine);
396 2 : elektraFree (longOptLine);
397 2 : return false;
398 : }
399 :
400 :
401 496 : if (!processShortOptSpec (spec, &optionData, keyWithOpt, &shortOptLine, errorKey))
402 : {
403 6 : ksDel (opts);
404 6 : elektraFree (shortOptLine);
405 6 : elektraFree (longOptLine);
406 6 : return false;
407 : }
408 :
409 490 : if (!processLongOptSpec (spec, &optionData, keyWithOpt, &longOptLine, errorKey))
410 : {
411 4 : ksDel (opts);
412 4 : elektraFree (shortOptLine);
413 4 : elektraFree (longOptLine);
414 4 : return false;
415 : }
416 : }
417 :
418 424 : if (*keyWithOpt != NULL)
419 : {
420 420 : if (strlen (shortOptLine) > 2)
421 : {
422 404 : shortOptLine[strlen (shortOptLine) - 2] = '\0'; // trim ", " of end
423 : }
424 :
425 420 : if (strlen (longOptLine) > 2)
426 : {
427 396 : longOptLine[strlen (longOptLine) - 2] = '\0'; // trim ", " of end
428 : }
429 :
430 420 : char * optsLinePart = elektraFormat ("%s%s%s", shortOptLine, strlen (longOptLine) > 0 ? ", " : "", longOptLine);
431 420 : elektraFree (shortOptLine);
432 420 : elektraFree (longOptLine);
433 :
434 420 : size_t length = strlen (optsLinePart);
435 420 : if (length > 0)
436 : {
437 : char * optsLine;
438 412 : if (length < 30)
439 : {
440 366 : optsLine = elektraFormat (" %-28s%s", optsLinePart, help);
441 : }
442 : else
443 : {
444 46 : optsLine = elektraFormat (" %s\n %30s%s", optsLinePart, "", help);
445 : }
446 :
447 412 : keySetMeta (*keyWithOpt, "opt/help", optsLine);
448 412 : elektraFree (optsLine);
449 : }
450 420 : elektraFree (optsLinePart);
451 : }
452 : else
453 : {
454 4 : elektraFree (shortOptLine);
455 4 : elektraFree (longOptLine);
456 : }
457 :
458 424 : ksDel (opts);
459 :
460 424 : return true;
461 : }
462 :
463 : /**
464 : * Read the option data (i.e. hasarg, flagvalue, etc.) for the option
465 : * given by @p metaKey 's name from @p key.
466 : * @retval true on success
467 : * @retval false on error
468 : */
469 498 : bool readOptionData (struct OptionData * optionData, Key * key, const char * metaKey, Key * errorKey)
470 : {
471 : // two slashes in string because array index is inserted in-between
472 : char metaBuffer[ELEKTRA_MAX_ARRAY_SIZE + sizeof ("opt//flagvalue") + 1];
473 498 : strncpy (metaBuffer, metaKey, ELEKTRA_MAX_ARRAY_SIZE + 3); // 3 = opt/ - null byte from ELEKTRA_MAX_SIZE
474 498 : strncat (metaBuffer, "/arg", 11); // 11 = remaining space in metaBuffer
475 :
476 498 : const char * hasArg = keyGetMetaString (key, metaBuffer);
477 498 : if (hasArg == NULL)
478 : {
479 420 : hasArg = "required";
480 : }
481 :
482 498 : strncpy (metaBuffer, metaKey, ELEKTRA_MAX_ARRAY_SIZE + 3); // 3 = opt/ - null byte from ELEKTRA_MAX_SIZE
483 498 : strncat (metaBuffer, "/arg/help", 11); // 11 = remaining space in metaBuffer
484 :
485 498 : const char * argNameMeta = keyGetMetaString (key, metaBuffer);
486 :
487 498 : strncpy (metaBuffer, metaKey, ELEKTRA_MAX_ARRAY_SIZE + 3); // 3 = opt/ - null byte from ELEKTRA_MAX_SIZE
488 498 : strncat (metaBuffer, "/flagvalue", 11); // 11 = remaining space in metaBuffer
489 :
490 498 : const char * flagValue = keyGetMetaString (key, metaBuffer);
491 498 : if (flagValue == NULL)
492 : {
493 : flagValue = "1";
494 : }
495 26 : else if (elektraStrCmp (hasArg, "none") != 0 && elektraStrCmp (hasArg, "optional") != 0)
496 : {
497 2 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (
498 : errorKey,
499 : "The flagvalue metadata can only be used, if the opt/arg metadata is set to 'none' or "
500 : "'optional'. (key: %s)",
501 : keyName (key));
502 2 : return false;
503 : }
504 :
505 496 : strncpy (metaBuffer, metaKey, ELEKTRA_MAX_ARRAY_SIZE + 3); // 3 = opt/ - null byte from ELEKTRA_MAX_SIZE
506 496 : strncat (metaBuffer, "/hidden", 11); // 11 = remaining space in metaBuffer
507 :
508 496 : bool hidden = false;
509 496 : const char * hiddenStr = keyGetMetaString (key, metaBuffer);
510 496 : if (hiddenStr != NULL && elektraStrCmp (hiddenStr, "1") == 0)
511 : {
512 16 : hidden = true;
513 : }
514 :
515 496 : const char * kind = "single";
516 496 : if (elektraStrCmp (keyBaseName (key), "#") == 0)
517 : {
518 22 : kind = "array";
519 : }
520 :
521 496 : optionData->specKey = key;
522 496 : optionData->metaKey = metaKey;
523 496 : optionData->hasArg = hasArg;
524 496 : optionData->flagValue = flagValue;
525 496 : optionData->argName = argNameMeta;
526 496 : optionData->hidden = hidden;
527 496 : optionData->kind = kind;
528 :
529 496 : return true;
530 : }
531 :
532 : /**
533 : * Process a possible short option specification on @p keyWithOpt.
534 : * The specification will be added to @p optionsSpec. The option
535 : * string will be added to @p shortOptLine.
536 : *
537 : * @retval true on success
538 : * @retval false on error
539 : */
540 496 : bool processShortOptSpec (struct Specification * spec, struct OptionData * optionData, Key ** keyWithOpt, char ** shortOptLine,
541 : Key * errorKey)
542 : {
543 496 : Key * key = optionData->specKey;
544 496 : const char * hasArg = optionData->hasArg;
545 496 : const char * kind = optionData->kind;
546 496 : const char * flagValue = optionData->flagValue;
547 496 : const char * argName = optionData->argName;
548 496 : bool hidden = optionData->hidden;
549 :
550 496 : const char * shortOptStr = keyGetMetaString (optionData->specKey, optionData->metaKey);
551 496 : if (shortOptStr == NULL || shortOptStr[0] == '\0')
552 : {
553 : return true;
554 : }
555 :
556 480 : const char shortOpt = shortOptStr[0];
557 :
558 480 : if (shortOpt == '-')
559 : {
560 2 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey,
561 : "Character '-' cannot be used as a short option. It would collide with the "
562 : "special string '--'. Offending key: %s",
563 : keyName (key));
564 : return false;
565 : }
566 :
567 478 : if (shortOpt == 'h')
568 : {
569 2 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey,
570 : "Character 'h' cannot be used as a short option. It would collide with the "
571 : "help option '-h'. Offending key: %s",
572 : keyName (key));
573 : return false;
574 : }
575 :
576 476 : Key * shortOptSpec = keyNew ("/short", KEY_META, "key", keyName (key), KEY_META, "hasarg", hasArg, KEY_META, "kind", kind, KEY_META,
577 : "flagvalue", flagValue, KEY_END);
578 476 : keyAddBaseName (shortOptSpec, (char[]){ shortOpt, '\0' });
579 :
580 476 : Key * existing = ksLookupByName (spec->options, keyName (shortOptSpec), 0);
581 476 : if (existing != NULL)
582 : {
583 2 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey,
584 : "The option '-%c' has already been specified for the key '%s'. Additional key: %s",
585 : shortOpt, keyGetMetaString (existing, "key"), keyName (key));
586 2 : keyDel (shortOptSpec);
587 2 : keyDel (existing);
588 : return false;
589 : }
590 :
591 474 : ksAppendKey (spec->options, shortOptSpec);
592 :
593 474 : if (*keyWithOpt == NULL)
594 : {
595 412 : *keyWithOpt = keyNew (keyName (key), KEY_END);
596 : }
597 474 : elektraMetaArrayAdd (*keyWithOpt, "opt", keyName (shortOptSpec));
598 :
599 474 : if (!hidden)
600 : {
601 458 : char * argString = "";
602 458 : if (elektraStrCmp (hasArg, "required") == 0)
603 : {
604 380 : argString = argName == NULL ? " ARG" : elektraFormat (" %s", argName);
605 : }
606 :
607 458 : char * newShortOptLine = elektraFormat ("%s-%c%s, ", *shortOptLine, shortOpt, argString);
608 458 : elektraFree (*shortOptLine);
609 458 : if (argName != NULL)
610 : {
611 8 : elektraFree (argString);
612 : }
613 458 : *shortOptLine = newShortOptLine;
614 : }
615 :
616 474 : if (!optionData->hidden)
617 : {
618 458 : spec->hasOpts = true;
619 : }
620 :
621 : return true;
622 : }
623 :
624 : /**
625 : * Process a possible long option specification on @p keyWithOpt.
626 : * The specification will be added to @p optionsSpec. The option
627 : * string will be added to @p longOptLine.
628 : *
629 : * @retval true on success
630 : * @retval false on error
631 : */
632 490 : bool processLongOptSpec (struct Specification * spec, struct OptionData * optionData, Key ** keyWithOpt, char ** longOptLine,
633 : Key * errorKey)
634 : {
635 490 : Key * key = optionData->specKey;
636 490 : const char * hasArg = optionData->hasArg;
637 490 : const char * kind = optionData->kind;
638 490 : const char * flagValue = optionData->flagValue;
639 490 : const char * argName = optionData->argName;
640 490 : bool hidden = optionData->hidden;
641 :
642 490 : char * longMeta = elektraFormat ("%s/long", optionData->metaKey);
643 490 : const char * longOpt = keyGetMetaString (key, longMeta);
644 490 : elektraFree (longMeta);
645 :
646 490 : if (longOpt == NULL)
647 : {
648 : return true;
649 : }
650 :
651 454 : if (elektraStrCmp (longOpt, "help") == 0)
652 : {
653 2 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey,
654 : "Option 'help' cannot be used as a long option. It would collide with the "
655 : "help option '--help'. Offending key: %s",
656 : keyName (key));
657 : return false;
658 : }
659 :
660 452 : Key * longOptSpec = keyNew ("/long", KEY_META, "key", keyName (key), KEY_META, "hasarg", hasArg, KEY_META, "kind", kind, KEY_META,
661 : "flagvalue", flagValue, KEY_END);
662 452 : keyAddBaseName (longOptSpec, longOpt);
663 :
664 452 : Key * existing = ksLookupByName (spec->options, keyName (longOptSpec), 0);
665 452 : if (existing != NULL)
666 : {
667 2 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey,
668 : "The option '--%s' has already been specified for the key '%s'. Additional key: %s",
669 : longOpt, keyGetMetaString (existing, "key"), keyName (key));
670 2 : keyDel (longOptSpec);
671 : return false;
672 : }
673 :
674 450 : ksAppendKey (spec->options, longOptSpec);
675 :
676 450 : if (*keyWithOpt == NULL)
677 : {
678 8 : *keyWithOpt = keyNew (keyName (key), KEY_END);
679 : }
680 450 : elektraMetaArrayAdd (*keyWithOpt, "opt", keyName (longOptSpec));
681 :
682 450 : if (!hidden)
683 : {
684 450 : char * argString = "";
685 450 : if (elektraStrCmp (hasArg, "required") == 0)
686 : {
687 372 : argString = argName == NULL ? "=ARG" : elektraFormat ("=%s", argName);
688 : }
689 78 : else if (elektraStrCmp (hasArg, "optional") == 0)
690 : {
691 32 : argString = argName == NULL ? "=[ARG]" : elektraFormat ("=[%s]", argName);
692 : }
693 :
694 :
695 450 : char * newLongOptLine = elektraFormat ("%s--%s%s, ", *longOptLine, longOpt, argString);
696 450 : elektraFree (*longOptLine);
697 450 : if (argName != NULL)
698 : {
699 8 : elektraFree (argString);
700 : }
701 450 : *longOptLine = newLongOptLine;
702 : }
703 :
704 450 : if (!optionData->hidden)
705 : {
706 450 : spec->hasOpts = true;
707 : }
708 :
709 : return true;
710 : }
711 :
712 : /**
713 : * Process possible environment variable specifications on @p keyWithOpt.
714 : * @retval true on success
715 : * @retval false on error
716 : */
717 600 : bool processEnvVars (KeySet * usedEnvVars, Key * specKey, Key ** keyWithOpt, Key * errorKey ELEKTRA_UNUSED)
718 : {
719 600 : KeySet * envVars = ksMetaGetSingleOrArray (specKey, "env");
720 600 : if (envVars == NULL)
721 : {
722 : return true;
723 : }
724 :
725 166 : ksRewind (envVars);
726 166 : ksNext (envVars); // skip count
727 : Key * k;
728 358 : while ((k = ksNext (envVars)) != NULL)
729 : {
730 192 : const char * envVar = keyString (k);
731 192 : if (envVar == NULL)
732 : {
733 0 : continue;
734 : }
735 :
736 192 : Key * envVarKey = keyNew ("/", KEY_META, "key", keyName (specKey), KEY_END);
737 192 : keyAddBaseName (envVarKey, envVar);
738 :
739 192 : ksAppendKey (usedEnvVars, envVarKey);
740 :
741 192 : if (*keyWithOpt == NULL)
742 : {
743 64 : *keyWithOpt = keyNew (keyName (specKey), KEY_END);
744 : }
745 192 : elektraMetaArrayAdd (*keyWithOpt, "env", keyName (envVarKey));
746 : }
747 :
748 166 : ksDel (envVars);
749 :
750 : return true;
751 : }
752 :
753 : /**
754 : * Add keys to the proc namespace in @p ks for all options specified
755 : * on @p keyWithOpt. The env-vars are taken from @p envValues.
756 : * @retval -1 in case of error
757 : * @retval 0 if no key was added
758 : * @retval 1 if keys were added to @p ks
759 : */
760 444 : int writeOptionValues (KeySet * ks, Key * keyWithOpt, KeySet * options, Key * errorKey)
761 : {
762 444 : bool valueFound = false;
763 :
764 444 : KeySet * optMetas = elektraMetaArrayToKS (keyWithOpt, "opt");
765 444 : if (optMetas == NULL)
766 : {
767 : return 0;
768 : }
769 :
770 370 : ksRewind (optMetas);
771 370 : ksNext (optMetas); // skip count
772 : Key * optMeta;
773 370 : bool shortFound = false;
774 1538 : while ((optMeta = ksNext (optMetas)) != NULL)
775 : {
776 802 : Key * optKey = ksLookupByName (options, keyString (optMeta), 0);
777 802 : bool isShort = strncmp (keyString (optMeta), "/short", 6) == 0;
778 802 : if (shortFound && !isShort)
779 : {
780 : // ignore long options, if a short one was found
781 124 : continue;
782 : }
783 :
784 678 : int res = addProcKey (ks, keyWithOpt, optKey);
785 678 : if (res == 0)
786 : {
787 238 : valueFound = true;
788 238 : if (isShort)
789 : {
790 122 : shortFound = true;
791 : }
792 : }
793 440 : else if (res < 0)
794 : {
795 4 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (
796 : errorKey, "The option '%s%s' cannot be used, because another option has already been used for the key '%s'",
797 : isShort ? "-" : "--", isShort ? (const char[]){ keyBaseName (optKey)[0], '\0' } : keyBaseName (optKey),
798 : keyName (keyWithOpt));
799 4 : ksDel (optMetas);
800 4 : return -1;
801 : }
802 : }
803 :
804 366 : ksDel (optMetas);
805 :
806 366 : return valueFound ? 1 : 0;
807 : }
808 :
809 : /**
810 : * Add keys to the proc namespace in @p ks for everything that is specified
811 : * by the 'env' metadata on @p keyWithOpt. The env-vars are taken from @p envValues.
812 : * @retval -1 in case of error
813 : * @retval 0 if no key was added
814 : * @retval 1 if keys were added to @p ks
815 : */
816 196 : int writeEnvVarValues (KeySet * ks, Key * keyWithOpt, KeySet * envValues, Key * errorKey)
817 : {
818 196 : bool valueFound = false;
819 :
820 196 : KeySet * envMetas = elektraMetaArrayToKS (keyWithOpt, "env");
821 196 : if (envMetas == NULL)
822 : {
823 : return 0;
824 : }
825 :
826 104 : ksRewind (envMetas);
827 104 : ksNext (envMetas); // skip count
828 : Key * envMeta;
829 316 : while ((envMeta = ksNext (envMetas)) != NULL)
830 : {
831 110 : Key * envKey = ksLookupByName (envValues, keyString (envMeta), 0);
832 :
833 110 : bool isArray = strcmp (keyBaseName (keyWithOpt), "#") == 0;
834 : Key * envValueKey;
835 110 : if (envKey == NULL)
836 : {
837 : envValueKey = NULL;
838 : }
839 56 : else if (isArray)
840 : {
841 6 : envValueKey = splitEnvValue (envKey);
842 : }
843 : else
844 : {
845 50 : envValueKey = keyNew (keyName (envKey), KEY_VALUE, keyString (envKey), KEY_END);
846 : }
847 :
848 110 : int res = addProcKey (ks, keyWithOpt, envValueKey);
849 110 : if (res < 0)
850 : {
851 2 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (
852 : errorKey,
853 : "The environment variable '%s' cannot be used, because another variable has "
854 : "already been used for the key '%s'.",
855 : keyBaseName (envKey), keyName (keyWithOpt));
856 2 : keyDel (envValueKey);
857 2 : ksDel (envMetas);
858 2 : return -1;
859 : }
860 108 : else if (res == 0)
861 : {
862 54 : valueFound = true;
863 : }
864 108 : keyDel (envValueKey);
865 : }
866 102 : ksDel (envMetas);
867 :
868 102 : return valueFound ? 1 : 0;
869 : }
870 :
871 : /**
872 : * Add keys to the proc namespace in @p ks for everything that is specified
873 : * by the 'args' metadata on @p keyWithOpt. The args are taken from @p args.
874 : * @retval -1 in case of error
875 : * @retval 0 if no key was added
876 : * @retval 1 if keys were added to @p ks
877 : */
878 206 : int writeArgsValues (KeySet * ks, Key * keyWithOpt, KeySet * args)
879 : {
880 206 : const char * argsMeta = keyGetMetaString (keyWithOpt, "args");
881 206 : if (argsMeta == NULL || elektraStrCmp (argsMeta, "remaining") != 0)
882 : {
883 : return 0;
884 : }
885 :
886 10 : Key * procKey = keyNew ("proc", KEY_END);
887 10 : keyAddName (procKey, strchr (keyName (keyWithOpt), '/'));
888 :
889 10 : Key * insertKey = keyDup (procKey);
890 :
891 10 : ksRewind (args);
892 10 : Key * arg = NULL;
893 38 : while ((arg = ksNext (args)) != NULL)
894 : {
895 18 : elektraArrayIncName (insertKey);
896 :
897 18 : Key * k = keyDup (insertKey);
898 18 : keySetString (k, keyString (arg));
899 18 : ksAppendKey (ks, k);
900 : }
901 :
902 10 : keySetBaseName (procKey, NULL); // remove #
903 10 : keySetString (procKey, keyBaseName (insertKey));
904 10 : ksAppendKey (ks, procKey);
905 10 : keyDel (insertKey);
906 10 : return 1;
907 : }
908 :
909 : /**
910 : * Taken a key whose value is an environment variable like array (like $PATH)
911 : * and turn it into a Key with either a string value or a metadata array 'values'
912 : * containing the value of the variable.
913 : */
914 6 : Key * splitEnvValue (const Key * envKey)
915 : {
916 6 : Key * valueKey = keyNew (keyName (envKey), KEY_END);
917 :
918 6 : char * envValue = elektraStrDup (keyString (envKey));
919 6 : char * curEnvValue = envValue;
920 :
921 6 : char * c = strchr (curEnvValue, SEP_ENV_VALUE);
922 6 : if (c == NULL)
923 : {
924 0 : elektraMetaArrayAdd (valueKey, "values", curEnvValue);
925 : }
926 : else
927 : {
928 : char * lastEnvValue = curEnvValue;
929 16 : while (c != NULL)
930 : {
931 10 : *c = '\0';
932 :
933 10 : elektraMetaArrayAdd (valueKey, "values", curEnvValue);
934 :
935 10 : curEnvValue = c + 1;
936 10 : lastEnvValue = curEnvValue;
937 10 : c = strchr (curEnvValue, SEP_ENV_VALUE);
938 : }
939 6 : elektraMetaArrayAdd (valueKey, "values", lastEnvValue);
940 : }
941 :
942 6 : elektraFree (envValue);
943 :
944 6 : return valueKey;
945 : }
946 :
947 : /**
948 : * @retval 0 if a proc key was added to ks
949 : * @retval -1 on pre-existing value, except if key is an array key, or replace == true then 0
950 : * @retval 1 on NULL pointers and failed insertion
951 : */
952 788 : int addProcKey (KeySet * ks, const Key * key, Key * valueKey)
953 : {
954 788 : if (ks == NULL || key == NULL || valueKey == NULL)
955 : {
956 : return 1;
957 : }
958 :
959 298 : Key * procKey = keyNew ("proc", KEY_END);
960 298 : keyAddName (procKey, strchr (keyName (key), '/'));
961 :
962 298 : bool isArrayKey = elektraStrCmp (keyBaseName (procKey), "#") == 0;
963 298 : if (isArrayKey)
964 : {
965 22 : keySetBaseName (procKey, NULL); // remove # (for lookup)
966 : }
967 :
968 :
969 298 : Key * existing = ksLookupByName (ks, keyName (procKey), 0);
970 298 : if (existing != NULL)
971 : {
972 92 : const char * value = isArrayKey ? keyGetMetaString (existing, "array") : keyString (existing);
973 92 : if (value != NULL && strlen (value) > 0)
974 : {
975 6 : keyDel (procKey);
976 6 : return -1;
977 : }
978 : }
979 :
980 292 : if (isArrayKey)
981 : {
982 16 : Key * insertKey = keyDup (procKey);
983 16 : keyAddBaseName (insertKey, "#");
984 16 : KeySet * values = elektraMetaArrayToKS (valueKey, "values");
985 16 : if (values == NULL)
986 : {
987 0 : keyDel (procKey);
988 0 : keyDel (insertKey);
989 0 : return 1;
990 : }
991 :
992 16 : ksRewind (values);
993 : Key * cur;
994 16 : ksNext (values); // skip count
995 80 : while ((cur = ksNext (values)) != NULL)
996 : {
997 48 : elektraArrayIncName (insertKey);
998 :
999 48 : Key * k = keyDup (insertKey);
1000 48 : keySetString (k, keyString (cur));
1001 48 : ksAppendKey (ks, k);
1002 : }
1003 :
1004 16 : keySetMeta (procKey, "array", keyBaseName (insertKey));
1005 16 : keyDel (insertKey);
1006 16 : ksDel (values);
1007 : }
1008 : else
1009 : {
1010 276 : keySetString (procKey, keyString (valueKey));
1011 : }
1012 :
1013 292 : return ksAppendKey (ks, procKey) > 0 ? 0 : 1;
1014 : }
1015 :
1016 324 : KeySet * parseEnvp (const char ** envp)
1017 : {
1018 324 : KeySet * ks = ksNew (0, KS_END);
1019 :
1020 324 : const char ** cur = envp;
1021 1965 : while (*cur != NULL)
1022 : {
1023 1317 : const char * eq = strchr (*cur, '=');
1024 1317 : Key * key = keyNew ("/", KEY_VALUE, eq + 1, KEY_END);
1025 1317 : size_t len = eq - *cur;
1026 1317 : char * name = elektraStrNDup (*cur, len + 1);
1027 1317 : name[len] = '\0';
1028 1317 : keyAddBaseName (key, name);
1029 1317 : ksAppendKey (ks, key);
1030 1317 : elektraFree (name);
1031 :
1032 1317 : cur++;
1033 : }
1034 :
1035 324 : return ks;
1036 : }
1037 :
1038 366 : KeySet * parseArgs (KeySet * optionsSpec, int argc, const char ** argv, Key * errorKey)
1039 : {
1040 366 : const char * posixlyStr = keyGetMetaString (errorKey, "posixly");
1041 366 : bool posixly = false;
1042 366 : if (posixlyStr != NULL && elektraStrCmp (posixlyStr, "1") == 0)
1043 : {
1044 2 : posixly = true;
1045 : }
1046 :
1047 366 : Key * argKey = keyNew ("/args/#", KEY_END);
1048 :
1049 366 : KeySet * options = ksNew (0, KS_END);
1050 : int i;
1051 844 : for (i = 1; i < argc; ++i)
1052 : {
1053 514 : const char * cur = argv[i];
1054 514 : if (cur[0] == '-')
1055 : {
1056 : // possible option
1057 332 : if (cur[1] == '-')
1058 : {
1059 170 : if (cur[2] == '\0')
1060 : {
1061 : // end of options
1062 8 : ++i; // skip --
1063 8 : break;
1064 : }
1065 :
1066 162 : if (!parseLongOption (optionsSpec, options, argc, argv, &i, errorKey))
1067 : {
1068 12 : keyDel (argKey);
1069 12 : ksDel (options);
1070 12 : return NULL;
1071 : }
1072 :
1073 150 : continue;
1074 : }
1075 :
1076 162 : if (!parseShortOptions (optionsSpec, options, argc, argv, &i, errorKey))
1077 : {
1078 14 : keyDel (argKey);
1079 14 : ksDel (options);
1080 14 : return NULL;
1081 : }
1082 : }
1083 : else
1084 : {
1085 : // not an option
1086 182 : if (posixly)
1087 : {
1088 : break;
1089 : }
1090 :
1091 180 : elektraArrayIncName (argKey);
1092 180 : Key * newArgKey = keyDup (argKey);
1093 180 : keySetString (newArgKey, cur);
1094 180 : ksAppendKey (options, newArgKey);
1095 : }
1096 : }
1097 :
1098 : // collect rest of argv
1099 18 : for (; i < argc; ++i)
1100 : {
1101 18 : elektraArrayIncName (argKey);
1102 18 : Key * newArgKey = keyDup (argKey);
1103 18 : keySetString (newArgKey, argv[i]);
1104 18 : ksAppendKey (options, newArgKey);
1105 : }
1106 :
1107 340 : ksAppendKey (options, keyNew ("/args", KEY_VALUE, keyBaseName (argKey), KEY_END));
1108 340 : keyDel (argKey);
1109 :
1110 340 : return options;
1111 : }
1112 :
1113 162 : bool parseShortOptions (KeySet * optionsSpec, KeySet * options, int argc, const char ** argv, int * index, Key * errorKey)
1114 : {
1115 162 : int i = *index;
1116 254 : for (const char * c = &argv[i][1]; *c != '\0'; ++c)
1117 : {
1118 :
1119 172 : Key * shortOpt = keyNew ("/short", KEY_END);
1120 172 : keyAddBaseName (shortOpt, (char[]){ *c, '\0' });
1121 :
1122 172 : Key * optSpec = ksLookupByName (optionsSpec, keyName (shortOpt), 0);
1123 :
1124 172 : if (optSpec == NULL)
1125 : {
1126 10 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "Unknown short option: -%c", keyBaseName (shortOpt)[0]);
1127 10 : keyDel (shortOpt);
1128 10 : keyDel (optSpec);
1129 10 : return false;
1130 : }
1131 :
1132 162 : const char * hasArg = keyGetMetaString (optSpec, "hasarg");
1133 162 : const char * kind = keyGetMetaString (optSpec, "kind");
1134 162 : const char * flagValue = keyGetMetaString (optSpec, "flagvalue");
1135 :
1136 162 : bool repeated = elektraStrCmp (kind, "array") == 0;
1137 :
1138 162 : Key * option = ksLookupByName (options, keyName (shortOpt), 0);
1139 162 : if (option == NULL)
1140 : {
1141 146 : option = keyNew (keyName (shortOpt), KEY_META, "key", keyGetMetaString (optSpec, "key"), KEY_END);
1142 146 : ksAppendKey (options, option);
1143 : }
1144 16 : else if (!repeated)
1145 : {
1146 2 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "This option cannot be repeated: -%c", keyBaseName (shortOpt)[0]);
1147 2 : keyDel (shortOpt);
1148 2 : keyDel (optSpec);
1149 2 : return false;
1150 : }
1151 160 : keyDel (optSpec);
1152 :
1153 160 : bool last = false;
1154 160 : if (elektraStrCmp (hasArg, "required") == 0)
1155 : {
1156 124 : if (*(c + 1) == '\0')
1157 : {
1158 58 : if (i >= argc - 1)
1159 : {
1160 2 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "Missing argument for short option: -%c",
1161 : keyBaseName (shortOpt)[0]);
1162 2 : keyDel (shortOpt);
1163 2 : keyDel (option);
1164 2 : return false;
1165 : }
1166 : // use next as arg and skip
1167 56 : setOption (option, argv[++i], repeated);
1168 56 : *index = i;
1169 : }
1170 : else
1171 : {
1172 : // use rest as argument
1173 66 : setOption (option, c + 1, repeated);
1174 66 : last = true;
1175 : }
1176 : }
1177 : else
1178 : {
1179 : // use flag value
1180 36 : setOption (option, flagValue, repeated);
1181 : }
1182 158 : keyDel (shortOpt);
1183 :
1184 158 : keySetMeta (option, "short", "1");
1185 158 : ksAppendKey (options, option);
1186 :
1187 158 : if (last)
1188 : {
1189 : break;
1190 : }
1191 : }
1192 :
1193 : return true;
1194 : }
1195 :
1196 162 : bool parseLongOption (KeySet * optionsSpec, KeySet * options, int argc, const char ** argv, int * index, Key * errorKey)
1197 : {
1198 162 : int i = *index;
1199 162 : Key * longOpt = keyNew ("/long", KEY_END);
1200 :
1201 162 : char * opt = elektraStrDup (&argv[i][2]);
1202 162 : char * eq = strchr (opt, '=');
1203 162 : size_t argStart = 0;
1204 162 : if (eq != NULL)
1205 : {
1206 : // mark end of option
1207 64 : *eq = '\0';
1208 64 : argStart = eq - opt + 3;
1209 : }
1210 :
1211 162 : keyAddBaseName (longOpt, opt);
1212 162 : elektraFree (opt);
1213 :
1214 : // lookup spec
1215 162 : Key * optSpec = ksLookupByName (optionsSpec, keyName (longOpt), 0);
1216 :
1217 162 : if (optSpec == NULL)
1218 : {
1219 0 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "Unknown long option: --%s", keyBaseName (longOpt));
1220 0 : keyDel (longOpt);
1221 0 : return false;
1222 : }
1223 :
1224 162 : const char * hasArg = keyGetMetaString (optSpec, "hasarg");
1225 162 : const char * kind = keyGetMetaString (optSpec, "kind");
1226 162 : const char * flagValue = keyGetMetaString (optSpec, "flagvalue");
1227 :
1228 162 : bool repeated = elektraStrCmp (kind, "array") == 0;
1229 :
1230 162 : Key * option = ksLookupByName (options, keyName (longOpt), 0);
1231 162 : if (option == NULL)
1232 : {
1233 142 : option = keyNew (keyName (longOpt), KEY_META, "key", keyGetMetaString (optSpec, "key"), KEY_END);
1234 142 : ksAppendKey (options, option);
1235 : }
1236 20 : else if (!repeated)
1237 : {
1238 2 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "This option cannot be repeated: --%s", keyBaseName (longOpt));
1239 2 : keyDel (longOpt);
1240 2 : keyDel (optSpec);
1241 2 : return false;
1242 : }
1243 18 : else if (keyGetMetaString (option, "short") != NULL)
1244 : {
1245 0 : keyDel (longOpt);
1246 0 : keyDel (optSpec);
1247 : // short option found already ignore long version
1248 0 : return true;
1249 : }
1250 160 : keyDel (optSpec);
1251 :
1252 160 : if (elektraStrCmp (hasArg, "required") == 0)
1253 : {
1254 : // extract argument
1255 122 : if (argStart > 0)
1256 : {
1257 : // use '=' arg
1258 52 : setOption (option, &argv[i][argStart], repeated);
1259 : }
1260 : else
1261 : {
1262 70 : if (i >= argc - 1)
1263 : {
1264 2 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "Missing argument for long option: --%s",
1265 : keyBaseName (longOpt));
1266 2 : keyDel (longOpt);
1267 2 : return false;
1268 : }
1269 : // use next as arg and skip
1270 68 : setOption (option, argv[++i], repeated);
1271 68 : *index = i;
1272 : }
1273 : }
1274 38 : else if (elektraStrCmp (hasArg, "optional") == 0)
1275 : {
1276 12 : if (argStart > 0)
1277 : {
1278 : // only use '=' argument
1279 4 : setOption (option, &argv[i][argStart], repeated);
1280 : }
1281 8 : else if (flagValue != NULL)
1282 : {
1283 : // use flag value
1284 8 : setOption (option, flagValue, repeated);
1285 : }
1286 : }
1287 : else
1288 : {
1289 26 : if (argStart > 0)
1290 : {
1291 8 : ELEKTRA_SET_VALIDATION_SEMANTIC_ERRORF (errorKey, "This option cannot have an argument: --%s",
1292 : keyBaseName (longOpt));
1293 8 : keyDel (longOpt);
1294 8 : return false;
1295 : }
1296 :
1297 : // use flag value
1298 18 : setOption (option, flagValue, repeated);
1299 : }
1300 150 : keyDel (longOpt);
1301 :
1302 150 : return true;
1303 : }
1304 :
1305 308 : void setOption (Key * option, const char * value, bool repeated)
1306 : {
1307 308 : if (repeated)
1308 : {
1309 50 : elektraMetaArrayAdd (option, "values", value);
1310 : }
1311 : else
1312 : {
1313 258 : keySetString (option, value);
1314 : }
1315 308 : }
1316 :
1317 1212 : KeySet * ksMetaGetSingleOrArray (Key * key, const char * metaName)
1318 : {
1319 1212 : const Key * k = keyGetMeta (key, metaName);
1320 1212 : if (k == NULL)
1321 : {
1322 : return NULL;
1323 : }
1324 :
1325 600 : const char * value = keyString (k);
1326 600 : if (value[0] != '#')
1327 : {
1328 : // add dummy key to mimic elektraMetaArrayToKS
1329 528 : return ksNew (2, keyNew ("/#", KEY_END), k, KS_END);
1330 : }
1331 :
1332 72 : Key * testKey = keyDup (k);
1333 72 : keyAddBaseName (testKey, keyString (k));
1334 :
1335 72 : const Key * test = keyGetMeta (key, keyName (testKey));
1336 72 : keyDel (testKey);
1337 :
1338 72 : if (test == NULL)
1339 : {
1340 : // add dummy key to mimic elektraMetaArrayToKS
1341 0 : return ksNew (2, keyNew ("/#", KEY_END), k, KS_END);
1342 : }
1343 :
1344 72 : return elektraMetaArrayToKS (key, metaName);
1345 : }
1346 :
1347 : /**
1348 : * Generate help message from optionsSpec.
1349 : *
1350 : * @return a newly allocated string, must be freed with elektraFree()
1351 : */
1352 : char * generateUsageLine (const char * progname, bool hasOpts, bool hasArgs)
1353 : {
1354 16 : return elektraFormat ("Usage: %s%s%s\n", progname, hasOpts ? " [OPTION]..." : "", hasArgs ? " [ARG]..." : "");
1355 : }
1356 :
1357 16 : char * generateOptionsList (KeySet * keysWithOpts)
1358 : {
1359 16 : if (ksGetSize (keysWithOpts) == 0)
1360 : {
1361 8 : return elektraStrDup ("");
1362 : }
1363 :
1364 8 : cursor_t cursor = ksGetCursor (keysWithOpts);
1365 :
1366 8 : char * optionsList = elektraFormat ("OPTIONS");
1367 :
1368 8 : Key * cur = NULL;
1369 8 : ksRewind (keysWithOpts);
1370 48 : while ((cur = ksNext (keysWithOpts)) != NULL)
1371 : {
1372 32 : const char * optLine = keyGetMetaString (cur, "opt/help");
1373 32 : if (optLine != NULL)
1374 : {
1375 16 : char * newOptionsList = elektraFormat ("%s\n%s", optionsList, optLine);
1376 16 : elektraFree (optionsList);
1377 16 : optionsList = newOptionsList;
1378 : }
1379 : }
1380 :
1381 8 : char * newOptionsList = elektraFormat ("%s\n", optionsList);
1382 8 : elektraFree (optionsList);
1383 8 : optionsList = newOptionsList;
1384 :
1385 8 : ksSetCursor (keysWithOpts, cursor);
1386 8 : return optionsList;
1387 : }
|