LCOV - code coverage report
Current view: top level - src/libs/opts - opts.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 576 600 96.0 %
Date: 2019-09-12 12:28:41 Functions: 21 21 100.0 %

          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             : }

Generated by: LCOV version 1.13