1 /*===========================================================================
2  *
3  *                            PUBLIC DOMAIN NOTICE
4  *               National Center for Biotechnology Information
5  *
6  *  This software/database is a "United States Government Work" under the
7  *  terms of the United States Copyright Act.  It was written as part of
8  *  the author's official duties as a United States Government employee and
9  *  thus cannot be copyrighted.  This software/database is freely available
10  *  to the public for use. The National Library of Medicine and the U.S.
11  *  Government have not placed any restriction on its use or reproduction.
12  *
13  *  Although all reasonable efforts have been taken to ensure the accuracy
14  *  and reliability of the software and data, the NLM and the U.S.
15  *  Government do not and cannot warrant the performance or results that
16  *  may be obtained by using this software or data. The NLM and the U.S.
17  *  Government disclaim all warranties, express or implied, including
18  *  warranties of performance, merchantability or fitness for any particular
19  *  purpose.
20  *
21  *  Please cite the author in any work or product based on this material.
22  *
23  * ===========================================================================
24  *
25  */
26 
27 #include <kapp/extern.h>
28 #include <sysalloc.h>
29 
30 #include <klib/container.h>
31 #include <klib/debug.h>
32 #include <klib/log.h>
33 #include <klib/out.h>
34 #include <klib/printf.h>
35 #include <klib/rc.h>
36 #include <klib/report.h>
37 #include <klib/sra-release-version.h> /* SraReleaseVersionGet */
38 #include <klib/status.h>
39 #include <klib/text.h>
40 #include <klib/vector.h>
41 
42 #include <kfg/config.h>
43 #include <kapp/main.h>
44 #include <kapp/args.h>
45 #include <kapp/args-conv.h>
46 
47 #include "args_debug.h"
48 
49 #include <assert.h>
50 #include <ctype.h>
51 #include <os-native.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <stdarg.h>
56 
57 #define USE_INLINING   0
58 #ifndef USE_OPTFILE
59 #define USE_OPTFILE    0
60 #endif
61 #define USE_EARLY_HELP 1
62 #define USE_EARLY_VERSION 1
63 
64 /* many tools acquired some usage for '-q'
65    other than the standard meaning of "be quiet".
66    while we phase this out, allow these tools
67    to replace the default usage. */
68 #define HONOR_LEGACY_Q_ALIAS 1
69 #if HONOR_LEGACY_Q_ALIAS
70 #define LEGACY_Q_ALIAS_DEPRECATED 1
71 #define LEGACY_Q_ALIAS_ERROR      0
72 #endif
73 
is_valid_name(const char * string)74 bool CC is_valid_name (const char * string)
75 {
76     /* we do not allow leading - or empty names */
77     if ((*string == '\0') || (*string == '-'))
78         return false;
79     /* we do not allow ASCII control or ASCII space
80      * but we do not disallow punctuation.  Use at your own risk */
81     for ( ; *string != '\0'; ++string)
82         if (isspace (*string) || iscntrl (*string))
83             return false;
84     return true;
85 }
86 
87 
88 /* ==========
89  * ParamValue
90  *   the value for an option is a NUL terminated ASCII / UTF-8 string
91  */
92 typedef void * ParamValueP;
93 
94 typedef struct ParamValueContainer {
95     uint32_t        param_index;
96     ParamValueP     param_value;
97     ConvertParamFnP convert_fn;
98     WhackParamFnP   whack;
99 } ParamValueContainer;
100 
101 /*
102  * Whack
103  *   undo the Make.  That is free the memory
104  */
105 static
ParamValueNotConvWhack(void * self)106 void CC ParamValueNotConvWhack (void * self)
107 {
108     assert (self); /* not an absolute requirement but if NULL we got programming error */
109 
110     free (self);
111 }
112 
113 static
ParamValueVectorWhack(void * self,void * ignored)114 void CC ParamValueVectorWhack (void * self, void * ignored)
115 {
116     ParamValueContainer * p_container;
117     assert (self);
118 
119     p_container = (ParamValueContainer *)self;
120     if (p_container->whack)
121         p_container->whack(p_container->param_value);
122 
123     free (self);
124 }
125 
126 /*
127  * Make
128  *   allocated memory for a ASCII/UTF-8 string including one extra for NUL
129  *   the value passed in must be a NUL termintaed string as well
130  */
131 static
ParamValueMake(ParamValueContainer * p_container,uint32_t arg_index,const char * value,size_t value_size,ConvertParamFnP convert_fn)132 rc_t CC ParamValueMake (ParamValueContainer * p_container, uint32_t arg_index, const char * value, size_t value_size, ConvertParamFnP convert_fn)
133 {
134     size_t alloc_size;
135 
136     assert (p_container);
137     assert (value);
138 
139     if (value_size == 0)
140         return RC ( rcExe, rcArgv, rcConstructing, rcParam, rcEmpty );
141 
142     p_container->param_index = arg_index;
143     p_container->whack = ParamValueNotConvWhack;
144     p_container->convert_fn = convert_fn;
145 
146     alloc_size = value_size + 1;
147     p_container->param_value = malloc (alloc_size);
148 
149     if (p_container->param_value == NULL)
150     {
151         fprintf (stderr, "Error allocating memory for option parameter %s\n",
152                  value);
153         return RC (rcExe, rcArgv, rcConstructing, rcMemory, rcExhausted);
154     }
155 
156     string_copy (p_container->param_value, alloc_size, value, value_size);
157     return 0;
158 }
159 
160 /* ==========
161  * Option
162  *  this is the primary node ofr an option
163  *  It contains the name of the long option that is also the key to pulling
164  *  out values.  The storage of these nodes is in a BSTree.
165  */
166 typedef struct Option
167 {
168     BSTNode     n;              /* BSTree node storage */
169 
170     size_t      size;           /* name length (size if UTF-8) */
171     Vector      values;         /* Vector of set values */
172     uint32_t    count;          /* count of times option seen on the command line */
173     uint32_t    max_count;      /* if non-zero, how many times is it legal to be seen */
174     bool        needs_value;    /* does this option take/require a value? */
175     bool        required;
176     bool        deprecated;     /* a warning if used */
177     bool        error;          /* an error if used */
178     bool        called_by_alias;/* short name was used for this option */
179     ConvertParamFnP  convert_fn;
180     char        name [1];       /* key value The 1 will be the NUL */
181 } Option;
182 
183 static
OptionMake(Option ** pself,const char * name,size_t size,uint32_t max_count,bool needs_value,bool required,ConvertParamFnP convert_fn)184 rc_t CC OptionMake (Option ** pself, const char * name, size_t size, uint32_t max_count, bool needs_value, bool required, ConvertParamFnP convert_fn)
185 {
186     Option *   self;
187     rc_t rc;
188 
189     assert (pself);
190     assert (name);
191     assert ((needs_value == true)||(needs_value == false)); /* not really but lets be rigorous */
192     assert ((required == true)||(required == false)); /* not really but lets be rigorous */
193 
194     self = calloc (1, sizeof (*self) + size);
195     if (self == NULL)
196     {
197         rc = RC (rcExe, rcArgv, rcConstructing, rcMemory, rcExhausted);
198         PLOGERR (klogErr, (klogErr, rc, "Error adding option '$(O)'","O=--%s",name));
199     }
200     else
201     {
202         if ((self->needs_value = needs_value) != false)
203             VectorInit (&self->values,0,4);
204         else
205             memset (&self->values, 0, sizeof(self->values) );
206 
207         self->required = required;
208         self->deprecated = self->error = false;
209         self->count = 0;
210         self->max_count = max_count;
211         self->size = size;
212         self->convert_fn = convert_fn;
213         string_copy (self->name, size+1, name, size);
214     }
215     *pself = self;
216     return 0;
217 }
218 
219 static
OptionWhack(Option * self)220 void CC OptionWhack (Option * self)
221 {
222     assert (self);
223 
224     if (self->needs_value)
225         VectorWhack (&self->values, ParamValueVectorWhack, NULL);
226 
227     free (self);
228 }
229 
230 static
OptionTreeWhack(BSTNode * node,void * ignored)231 void CC OptionTreeWhack (BSTNode * node, void * ignored)
232 {
233     OptionWhack ((Option*)node);
234 }
235 #ifdef OptionGetName_needed
236 static
OptionGetName(const Option * self,size_t * size)237 const char * CC OptionGetName (const Option * self, size_t * size)
238 {
239     assert (self);
240     if (size)
241         *size = self->size;
242     return self->name;
243 }
244 #endif
245 /*
246  * NeedsValue
247  *  return bool, does this option require a value
248  */
249 static
OptionNeedsValue(const Option * self)250 bool CC OptionNeedsValue (const Option * self)
251 {
252     assert (self);
253 
254     return self->needs_value;
255 }
256 
257 /*
258  * GetCount
259  *  return the count of values seen so far.
260  */
261 static
OptionGetCount(const Option * self)262 uint32_t CC OptionGetCount (const Option *self)
263 {
264     assert (self);
265 
266     return self->count;
267 }
268 
269 /*
270  * GetValue
271  *    returns the address of the Nth value (not a copy of the value)
272  */
273 static
OptionGetValue(const Option * self,uint32_t number,const void ** value)274 rc_t CC OptionGetValue (const Option * self, uint32_t number, const void ** value)
275 {
276     ParamValueContainer * p_container;
277     /* SKH -- not sure why this was here.
278        const char * pc; */
279     /* uint32_t count; */
280 
281     assert (self);
282     assert (value);
283 
284     /* count = OptionGetCount(self); */
285 
286     p_container = VectorGet (&self->values, number);
287     /* SKH -- this was checking pc, which is uninitialized */
288     if (p_container == NULL)
289         return RC (rcExe, rcArgv, rcAccessing, rcIndex, rcExcessive);
290 
291     *value = p_container->param_value;
292 
293     return 0;
294 }
295 
296 /*
297  * add a value to this node.  If a value isn't needed this is just incrementing the count
298  */
299 static
OptionAddValue(Option * option,uint32_t arg_index,const char * value,size_t size)300 rc_t CC OptionAddValue (Option * option, uint32_t arg_index, const char * value, size_t size)
301 {
302     rc_t rc = 0;
303 
304     assert (option);
305 
306     ++option->count;
307 
308 /*     KOutMsg ("%s: name %s count %u max_count %u value %s\n", __func__, option->name, option->count, option->max_count, value); */
309     if (option->max_count && (option->count > option->max_count))
310     {
311         --option->count;
312         rc = RC (rcExe, rcArgv, rcInserting, rcData, rcExcessive);
313         PLOGERR (klogErr,
314                  (klogErr, rc, "Too many occurrences of option '$(O)'",
315                   "O=--%s", option->name));
316     }
317     else if (option->needs_value)
318     {
319         ParamValueContainer * p_container;
320 
321         assert (value);     /* gotta have a value */
322         /* value can't be a NULL string */
323         if (size == 0)
324         {
325             rc = RC (rcExe, rcArgv, rcConstructing, rcParam, rcEmpty );
326             return rc;
327         }
328 
329         p_container = (ParamValueContainer *)malloc( sizeof *p_container );
330         if (p_container == NULL)
331         {
332             rc = RC (rcExe, rcArgv, rcConstructing, rcMemory, rcExhausted);
333             return rc;
334         }
335 
336         rc = ParamValueMake (p_container, arg_index, value, size, option->convert_fn);
337         if (rc == 0)
338         {
339             /* NOTE: effectively going in as a char ** though we will
340              * pull it out as a char* with the same value */
341             rc = VectorAppend (&option->values, NULL, p_container);
342             if (rc)
343             {
344                 PLOGERR (klogErr,
345                          (klogErr, rc, "error capturing parameter '$(O)'",
346                           "O=--%s", option->name));
347                 p_container->whack (p_container->param_value);
348             }
349             else
350             {
351                 ARGS_DBG( "added option-value %s", option->name );
352             }
353         }
354     }
355     return rc;
356 }
357 
358 static
OptionCmp(const void * item,const BSTNode * n)359 int64_t CC OptionCmp (const void * item, const BSTNode * n)
360 {
361     const char * name;
362     const Option * option;
363     size_t l;
364 
365     assert (item);
366     assert (n);
367 
368     name = item;
369     option = (Option*)n;
370     l = string_size (name);
371     return string_cmp (name, l, option->name, option->size, (uint32_t)(l + option->size) );
372 }
373 
374 static
OptionSort(const BSTNode * item,const BSTNode * n)375 int64_t CC OptionSort (const BSTNode * item, const BSTNode * n)
376 {
377     const Option * l = (Option*)item;
378     const Option * r = (Option*)n;
379     return string_cmp (l->name, l->size, r->name, r->size, (uint32_t)(l->size + r->size) );
380 }
381 
382 /* ==========
383  */
384 typedef struct OptAlias
385 {
386     BSTNode     n;              /* BSTree Node storage */
387 
388     Option *   option;          /* full name node for which this is an alias */
389     size_t      size;
390     bool        deprecated;     /* warning if used */
391     bool        error;          /* error if used */
392     char        name[1];        /* utf8 name */
393 } OptAlias;
394 
395 static
OptAliasMake(OptAlias ** pself,const char * name,size_t size,Option * option)396 rc_t CC OptAliasMake (OptAlias ** pself, const char * name, size_t size,
397                       Option * option)
398 {
399     OptAlias * self;
400 
401     assert (pself);
402     assert (name);
403     if (size == 0)
404     {
405         rc_t rc = RC (rcExe, rcArgv, rcConstructing, rcName, rcEmpty);
406         PLOGERR (klogErr,
407                  (klogErr, rc, "Alias name is empty for parameter '$(B)",
408                   "B=%s", option->name));
409         *pself = NULL;
410         return rc;
411     }
412 
413     self = malloc (sizeof (*self) + size);
414     if (self == NULL)
415     {
416         rc_t rc = RC (rcExe, rcArgv, rcConstructing, rcMemory, rcExhausted);
417         PLOGERR (klogErr,
418                  (klogErr, rc, "Error creating structure for alias '$(A)' for parameter '$(B)",
419                   "A=%s,B=%s", name, option->name));
420         *pself = NULL;
421         return rc;
422     }
423     self->option = option;
424     self->size = size;
425     self->deprecated = self->error = false;
426     string_copy (self->name, size + 1, name, size);
427     *pself = self;
428     return 0;
429 }
430 
431 static
OptAliasWhack(const OptAlias * self)432 void CC OptAliasWhack (const OptAlias * self)
433 {
434     assert (self);
435     free ((void*)self);
436 }
437 
438 static
OptAliasTreeWhack(BSTNode * node,void * ignored)439 void CC OptAliasTreeWhack (BSTNode * node, void * ignored)
440 {
441     assert (node); /* catch programming errors; freeing NULL wouldn't hurt */
442 
443     OptAliasWhack ((OptAlias*)node);
444 }
445 
446 #if 0
447 static
448 const char * CC OptAliasName (const OptAlias * self, size_t * size)
449 {
450     assert (self);
451 
452     if (size)
453         *size = self->size;
454     return self->name;
455 }
456 #endif
457 static
OptAliasOption(const OptAlias * self)458 Option * CC OptAliasOption (const OptAlias *self)
459 {
460     assert (self != NULL);
461 
462     return self->option;
463 }
464 
465 static
OptAliasCmp(const void * item,const BSTNode * n)466 int64_t CC OptAliasCmp (const void * item, const BSTNode * n)
467 {
468     const char * name;
469     const OptAlias * option;
470     size_t l;
471 
472     assert (item);
473     assert (n);
474 
475     name = item;
476     option = (OptAlias*)n;
477     l = string_size (name);
478     return string_cmp (name, l, option->name, option->size, (uint32_t)( l + option->size ) );
479 }
480 
481 static
OptAliasSort(const BSTNode * item,const BSTNode * n)482 int64_t CC OptAliasSort (const BSTNode * item, const BSTNode * n)
483 {
484     const OptAlias * l;
485     const OptAlias * r;
486 
487     assert (item);
488     assert (n);
489 
490     l = (OptAlias*)item;
491     r = (OptAlias*)n;
492     return string_cmp (l->name, l->size, r->name, r->size, (uint32_t)( l->size + r->size ) );
493 }
494 
495 #if NOT_USED_YET
496 static
OptDefMakeCopy(OptDef ** pself,OptDef * original)497 rc_t CC OptDefMakeCopy (OptDef ** pself, OptDef * original)
498 {
499     OptDef * self;
500     size_t nsize;
501     size_t asize;
502     size_t hsize;
503 
504     nsize = string_size (original->name);
505     asize = original->aliases ? string_size (original->aliases) : 0;
506     hsize = original->help ? string_size (original->help) : 0;
507 
508     self = malloc (sizeof (*self) + nsize + 1 + asize + 1 + hsize + 1);
509     if (self == NULL)
510     {
511         rc_t rc;
512         /* assuming DebugMsg is stderr equivalent */
513         rc = RC (rcExe, rcArgv, rcConstructing, rcMemory, rcExhausted);
514         LOGERR (klogFatal, rc, "error creating help for option");
515         return rc;
516     }
517     self->name = (char*)(self+1);
518     string_copy (self->name, nsize + 1, original->name, nsize);
519     self->aliases = self->name + nsize + 1;
520     if (original->aliases)
521     {
522         string_copy (self->aliases, asize + 1, original->aliases, asize);
523     }
524     else
525         self->aliases[0] = '\0';
526     self->help = self->aliases + asize + 1;
527     if (original->help)
528     {
529         string_copy (self->help, hsize + 1, original->help, asize);
530     }
531     else
532         self->help[0] = '\0';
533     self->max_count = original->max_count;
534     self->needs_value = original->needs_value;
535     *pself = self;
536     return 0;
537 }
538 static
OptDefCopyVectorWhack(void * self,void * ignored)539 void CC OptDefCopyVectorWhack (void * self, void * ignored)
540 {
541     free (self);
542 }
543 
544 #endif
545 
546 #if NOT_USED_YET
547 
548 typedef struct HelpGroup
549 {
550     rc_t ( CC * header_fmt) (Args * args, const char * header);
551     Vector options;
552     const char header[1];
553 } HelpGroup;
554 
555 
556 static
HelpGroupMake(HelpGroup ** pself,const char * name)557 rc_t CC HelpGroupMake (HelpGroup ** pself, const char * name)
558 {
559     HelpGroup * self;
560     size_t size;
561 
562     size = string_size (name);
563     self = malloc (sizeof (*self) + size);
564     if (self == NULL)
565     {
566         fprintf (stderr, "Error allocating help group structure %s\n", name);
567         return RC (rcExe, rcArgv, rcConstructing, rcMemory, rcExhausted);
568     }
569     string_copy (self->name, size+1, name, size);
570     VectorInit (&self->optdefs, 0, 16);
571 
572     *pself = self;
573     return 0;
574 }
575 
576 
577 static
HelpGroupAddOptDef(HelpGroup * self,OptDef * option)578 rc_t CC HelpGroupAddOptDef (HelpGroup * self, OptDef * option)
579 {
580     OptDef * opt;
581     rc_t rc;
582 
583     rc = OptDefCopy (&opt, option);
584     if (rc)
585         return rc;
586 
587     rc = VectorAppend (&self->optdefs, NULL, opt);
588     if (rc)
589     {
590         fprintf (stderr, "Error appending option for help\n");
591         OptDefCopyVectorWhack (opt, NULL);
592         return rc;
593     }
594     return 0;
595 }
596 
597 static
HelpGroupVectorWhack(void * item,void * ignored)598 void CC HelpGroupVectorWhack (void * item, void * ignored)
599 {
600     HelpGroup * self = item;
601 
602     assert (self);
603     VectorWhack (&self->optdefs, OptDefCopyVectorWhack, NULL);
604     free (self);
605 }
606 #endif
607 
608 typedef struct Parameter
609 {
610     uint32_t position;
611     ConvertParamFnP  convert_fn;
612 } Parameter;
613 
614 static
ParamMake(Parameter ** p_parameter,uint32_t position,ConvertParamFnP convert_fn)615 rc_t CC ParamMake (Parameter ** p_parameter, uint32_t position, ConvertParamFnP convert_fn)
616 {
617     Parameter * parameter;
618 
619     parameter = malloc (sizeof(Parameter));
620     if (parameter == NULL)
621     {
622         fprintf (stderr, "Error allocating parameter structure\n");
623         return RC (rcExe, rcArgv, rcConstructing, rcMemory, rcExhausted);
624 
625     }
626 
627     parameter->position = position;
628     parameter->convert_fn = convert_fn;
629 
630     *p_parameter = parameter;
631     return 0;
632 }
633 
634 static
ParamWhack(Parameter * param)635 void ParamWhack (Parameter * param)
636 {
637     assert(param);
638     free(param);
639 }
640 
641 static
ParamVectorWhack(void * item,void * ignored)642 void CC ParamVectorWhack (void * item, void * ignored)
643 {
644     ParamWhack(item);
645 }
646 
647 static
ParamGetValue(const Vector * param_values,uint32_t number,const void ** value)648 rc_t CC ParamGetValue (const Vector * param_values, uint32_t number, const void ** value)
649 {
650     ParamValueContainer * p_container;
651 
652     assert (param_values);
653     assert (value);
654 
655     p_container = VectorGet (param_values, number);
656     if (p_container == NULL)
657         return RC (rcExe, rcArgv, rcAccessing, rcIndex, rcExcessive);
658 
659     *value = p_container->param_value;
660 
661     return 0;
662 }
663 
664 /*
665  * add a param value to param_values vector.  Calls convert_fn if provided
666  */
667 static
ParamAddValue(Vector * param_values,uint32_t arg_index,const char * value,size_t size,ConvertParamFnP convert_fn)668 rc_t CC ParamAddValue (Vector * param_values, uint32_t arg_index, const char * value, size_t size, ConvertParamFnP convert_fn)
669 {
670     ParamValueContainer * p_container;
671     rc_t rc = 0;
672 
673     assert (param_values);
674 
675     p_container = (ParamValueContainer *)malloc( sizeof *p_container );
676     if (p_container == NULL)
677     {
678         rc = RC (rcExe, rcArgv, rcConstructing, rcMemory, rcExhausted);
679         return rc;
680     }
681 
682     assert (value);     /* gotta have a value */
683     /* value can't be a NULL string */
684     if (size == 0)
685     {
686         rc = RC (rcExe, rcArgv, rcConstructing, rcParam, rcEmpty);
687         return rc;
688     }
689 
690     rc = ParamValueMake (p_container, arg_index, value, size, convert_fn);
691     if (rc == 0)
692     {
693         rc = VectorAppend (param_values, NULL, p_container);
694         if (rc)
695         {
696             PLOGERR (klogErr,
697                      (klogErr, rc, "error capturing parameter at index: $(O)",
698                       "O=%u", arg_index));
699             p_container->whack (p_container->param_value);
700         }
701         else
702         {
703             ARGS_DBG( "added param-value at index: %u", arg_index );
704         }
705     }
706 
707     return rc;
708 }
709 
710 
711 /* ==========
712  */
713 struct Args
714 {
715     BSTree names;
716     BSTree aliases;
717     Vector params;
718     Vector argv;
719     Vector param_values;
720     Vector help;
721 #if NOT_USED_YET
722     HelpGroup * def_help;
723 #endif
724 #if HONOR_LEGACY_Q_ALIAS
725     bool   qalias_replaced;
726 #endif
727 };
728 
ArgsMake(Args ** pself)729 rc_t CC ArgsMake (Args ** pself)
730 {
731     rc_t rc;
732     Args * self;
733 
734     assert (pself);
735 
736     self = malloc (sizeof (*self));
737     if (self == NULL)
738     {
739         rc = RC (rcExe, rcArgv, rcConstructing, rcMemory, rcExhausted);
740     }
741     else
742     {
743 #if NOT_USED_YET
744         HelpGroup * help;
745 #endif
746         BSTreeInit (&self->names);
747         BSTreeInit (&self->aliases);
748         VectorInit (&self->params,0,8);
749         VectorInit (&self->argv,0,8);
750         VectorInit (&self->param_values,0,8);
751         VectorInit (&self->help,0,16);
752 #if HONOR_LEGACY_Q_ALIAS
753         self -> qalias_replaced = false;
754 #endif
755 #if NOT_USED_YET
756         rc = HelpGroupMake (&help, "NCBI Options");
757         if (rc)
758         {
759             ArgsWhack (self);
760         }
761         else
762         {
763             self->def_help = help;
764             rc = VectorAppend (&self->help, NULL, help);
765         }
766 #else
767         rc = 0;
768 #endif
769     }
770     *pself = (rc == 0) ? self : NULL;
771     return rc;
772 }
773 
ArgsWhack(Args * self)774 rc_t CC ArgsWhack (Args * self)
775 {
776     if (self)
777     {
778         BSTreeWhack (&self->names, OptionTreeWhack, NULL);
779         BSTreeWhack (&self->aliases, OptAliasTreeWhack, NULL);
780         VectorWhack (&self->params, ParamVectorWhack, NULL);
781         VectorWhack (&self->argv, ParamValueVectorWhack, NULL);
782         VectorWhack (&self->param_values, ParamValueVectorWhack, NULL);
783 #if NOT_USED_YET
784         VectorWhack (&self->help, HelpGroupVectorWhack, NULL);
785 #endif
786         free (self);
787     }
788     return 0;
789 }
790 
791 
ArgsAddOption(Args * self,const OptDef * option)792 rc_t CC ArgsAddOption (Args * self, const OptDef * option)
793 {
794     rc_t rc = 0;
795     Option * node;
796     Option * prev;
797     const char * name;
798     size_t size; /* will be used for the help tree side */
799 
800     if (self == NULL)
801     {
802         rc = RC (rcExe, rcArgv, rcConstructing, rcSelf, rcNull);
803         LOGERR (klogInt, rc, "Error adding an opion with no object");
804         return rc;
805     }
806     if (option == NULL)
807     {
808         rc = RC (rcExe, rcArgv, rcConstructing, rcParam, rcNull);
809         LOGERR (klogInt, rc, "Error adding an option with no option name");
810         return rc;
811     }
812     name = option->name;
813     if (! is_valid_name (name))
814     {
815         rc = RC (rcExe, rcArgv, rcConstructing, rcName, rcInvalid);
816         PLOGERR (klogInt, (klogInt, rc, "Error using illegal option name '$(O)'", "O=--%s", name));
817         return rc;
818     }
819     size = string_size (name);
820     rc = OptionMake (&node, name, size, option->max_count, option->needs_value, option->required, option->convert_fn);
821     if (rc)
822         return rc;
823     size ++;
824 
825     prev = NULL;
826     rc = BSTreeInsertUnique (&self->names, &node->n, (BSTNode**)&prev, OptionSort);
827     if (rc)
828     {
829         if (GetRCState(rc) == rcBusy)
830         {
831             rc = RC (rcExe, rcArgv, rcConstructing, rcName, rcBusy);
832             PLOGERR (klogInt,
833                      (klogInt, rc, "duplicate option name '$(O)'", "O=--%s", name));
834         }
835         else
836             PLOGERR (klogInt,
837                      (klogInt, rc, "unknown error inserting option name '$(O)'", "O=--%s", name));
838 
839         OptionWhack (node);
840         return rc;
841     }
842     if (option->aliases)     /* any single character aliases? */
843     {
844         const char * startc;
845         const char * endc;
846         int incr;
847 
848         for ((startc = option->aliases),(endc = startc + string_size (startc));
849              startc < endc; startc += incr)
850         {
851             OptAlias * snode;
852             OptAlias * sprev;
853             uint32_t c;
854             char alias_name [8]; /* 4 should be enough + 1 for NUL */
855 
856             incr = utf8_utf32 (&c, startc, endc);
857             if (incr < 0)
858             {
859                 rc = RC (rcExe, rcArgv, rcConstructing, rcName, rcCorrupt);
860                 PLOGERR (klogInt,
861                          (klogInt, rc, "Error parsing alias string '$(A)' from '$(B)' for '$(C)'",
862                           "A=%s,B=%s,C=%s", startc, option->aliases, name));
863                 break;
864             }
865             if (incr > 4)
866             {
867                 rc = RC (rcExe, rcArgv, rcConstructing, rcName, rcCorrupt);
868                 PLOGERR (klogInt,
869                          (klogInt, rc,"Error parsing UTF-8 string '$(S)'",
870                           "S=%s", startc));
871                 break;
872             }
873             size = string_copy (alias_name, sizeof (alias_name), startc, incr);
874             if (! is_valid_name (alias_name))
875             {
876                 rc = RC (rcExe, rcArgv, rcConstructing, rcName, rcInvalid);
877                 PLOGERR (klogInt,
878                          (klogInt, rc, "Error using invalid alias name '$(S)'",
879                           "S=%s", alias_name));
880                 break;
881             }
882 #if 0
883             size += incr + 1;
884 #endif
885             rc = OptAliasMake (&snode, alias_name, incr, node);
886             if (rc)
887                 break;
888             rc = BSTreeInsertUnique (&self->aliases, &snode->n, (BSTNode**)&sprev, OptAliasSort);
889             if (rc)
890             {
891                 if (GetRCState(rc) == rcExists)
892                 {
893 #if HONOR_LEGACY_Q_ALIAS
894                     if ( ! self -> qalias_replaced && size == 1 && alias_name [ 0 ] == 'q' )
895                     {
896                         BSTreeUnlink ( & self -> aliases, & sprev -> n );
897                         rc = BSTreeInsertUnique ( & self -> aliases, & snode -> n, NULL, OptAliasSort );
898                         if ( rc != 0 )
899                             BSTreeInsert ( & self -> aliases, & sprev -> n, OptAliasSort );
900                         else
901                         {
902                             OptAliasWhack ( sprev );
903                             self -> qalias_replaced = true;
904 #if LEGACY_Q_ALIAS_DEPRECATED
905                             snode -> deprecated = true;
906 #elif LEGACY_Q_ALIAS_ERROR
907                             snode -> error = true;
908 #endif
909                             continue;
910                         }
911                     }
912 #endif
913                     rc = RC (rcExe, rcArgv, rcConstructing, rcName, rcExists);
914                     PLOGERR (klogInt,
915                              (klogInt, rc, "duplicate alias name '$(S)'",
916                               "S=%s", alias_name));
917                 }
918                 else
919                 {
920                     PLOGERR (klogErr,
921                              (klogErr, rc, "unknown error inserting alias '$(S)'",
922                               "S=%s", alias_name));
923                 }
924 
925                 OptAliasWhack (snode);
926                 break;
927             }
928         }
929     }
930     return rc;
931 }
932 
ArgsAddLongOption(Args * self,const char * opt_short_names,const char * long_name,const char * opt_param_names,const char * help_text,uint32_t max_count,bool required)933 rc_t CC ArgsAddLongOption ( Args * self, const char * opt_short_names, const char * long_name,
934     const char * opt_param_names, const char * help_text, uint32_t max_count, bool required )
935 {
936     OptDef opt;
937     const char * help [ 2 ];
938 
939     if ( max_count > 0xFFFF )
940         return RC ( rcExe, rcArgv, rcConstructing, rcParam, rcExcessive );
941 
942     memset ( & opt, 0, sizeof opt );
943     memset ( help, 0, sizeof help );
944 
945     help [ 0 ] = help_text;
946 
947     opt . name = long_name;
948     opt . aliases = opt_short_names;
949     opt . help = help;
950     opt . max_count = ( uint16_t ) max_count;
951     opt . needs_value = ( bool ) ( opt_param_names != NULL && opt_param_names [ 0 ] != 0 );
952     opt . required = required;
953 
954     return ArgsAddOption ( self, & opt );
955 }
956 
ArgsAddOptionArray(Args * self,const OptDef * option,uint32_t count)957 rc_t CC ArgsAddOptionArray (Args * self, const OptDef * option, uint32_t count
958     /*, rc_t (*header_fmt)(Args * args, const char * header), const char * header */ )
959 {
960     rc_t rc;
961 #if NOT_USED_YET
962     HelpGroup * hg;
963 
964     rc = HelpGroupMake (&hg, header, header_fmt, option, count);
965     if (rc == 0)
966     {
967 
968         rc = VectorAppend (&self->help, NULL, hg);
969         if (rc == 0)
970         {
971 
972             /* count might be zero for help only sections */
973             for (rc = 0; count > 0; --count, ++option)
974             {
975                 rc = ArgsAddOption (self, option);
976                 if (rc)
977                     break;
978             }
979             if (rc == 0)
980                 return 0;
981         }
982         else
983             HelpGroupVectorWhack (hg, NULL);
984     }
985 #else
986     for (rc = 0; (rc == 0) && (count > 0); --count, ++option)
987     {
988         rc = ArgsAddOption (self, option);
989     }
990 #endif
991     return rc;
992 }
993 
ArgsAddParam(Args * self,const ParamDef * param_def)994 rc_t CC ArgsAddParam ( Args * self, const ParamDef * param_def )
995 {
996     rc_t rc;
997     Parameter * param;
998     uint32_t params_count;
999 
1000     assert(self);
1001     assert(param_def);
1002 
1003     params_count = VectorLength(&self->params);
1004 
1005     rc = ParamMake (&param, params_count, param_def->convert_fn);
1006     if (rc)
1007         return rc;
1008 
1009     rc = VectorAppend(&self->params, NULL, param);
1010     if (rc != 0)
1011     {
1012         ParamWhack(param);
1013     }
1014 
1015     return rc;
1016 }
1017 
ArgsAddLongParam(Args * self,const char * param_name,const char * help_text,ConvertParamFnP opt_cvt)1018 rc_t CC ArgsAddLongParam ( Args * self, const char * param_name, const char * help_text, ConvertParamFnP opt_cvt )
1019 {
1020     ParamDef param;
1021 
1022     memset ( & param, 0, sizeof param );
1023 
1024     param . convert_fn = opt_cvt;
1025 
1026     return ArgsAddParam ( self, & param );
1027 }
1028 
ArgsAddParamArray(Args * self,const ParamDef * param,uint32_t count)1029 rc_t CC ArgsAddParamArray (Args * self, const ParamDef * param, uint32_t count)
1030 {
1031     rc_t rc;
1032 
1033     for (rc = 0; (rc == 0) && (count > 0); --count, ++param)
1034     {
1035         rc = ArgsAddParam (self, param);
1036     }
1037 
1038     return 0;
1039 }
1040 
1041 /*
1042  */
next_arg(const Args * self,int * pix,int max,const char ** pvalue)1043 rc_t CC next_arg (const Args * self, int * pix, int max, const char ** pvalue)
1044 {
1045     int ix;
1046     ParamValueContainer * p_container;
1047 
1048     if (*pix >= max)
1049         return RC (rcApp, rcArgv, rcAccessing, rcString, rcNotFound);
1050 
1051     ix = *pix;
1052     ix++;
1053     p_container = (ParamValueContainer *) VectorGet (&self->argv, ix);
1054     assert(p_container);
1055     *pvalue = (const char *) p_container->param_value;
1056     *pix = ix;
1057     return 0;
1058 }
1059 
1060 typedef struct ArgsReqCheckData
1061 {
1062     Option * missing_option;
1063     rc_t rc;
1064 } ArgsReqCheckData;
1065 
1066 static
ArgsCheckRequiredInt(BSTNode * n,void * _data)1067 void CC ArgsCheckRequiredInt (BSTNode * n, void * _data)
1068 {
1069 #if 1
1070     ArgsReqCheckData * data;
1071     Option * opt;
1072     rc_t rc;
1073 
1074     data = _data;
1075     opt = (Option*)n;
1076     if (opt->required && ! opt->count)
1077     {
1078         rc = RC( rcExe, rcArgv, rcParsing, rcParam, rcNotFound );
1079         PLOGERR (klogWarn, (klogWarn, rc, "Error missing required parameter '$(P)'",
1080                             "P=%s", opt->name));
1081         if (data->missing_option == NULL)
1082         {
1083             data->missing_option = opt;
1084             data->rc = rc;
1085         }
1086     }
1087 #endif
1088 }
1089 
1090 /* check for required parameters */
ArgsCheckRequired(Args * self)1091 rc_t CC ArgsCheckRequired (Args * self)
1092 {
1093     ArgsReqCheckData r = { NULL, false };
1094 
1095     BSTreeForEach ( &self->names, false, ArgsCheckRequiredInt, &r );
1096 
1097     return r.rc;
1098 }
1099 
1100 static
ProcessArgConversion(Args * self,ParamValueContainer * p_container)1101 rc_t CC ProcessArgConversion(Args * self, ParamValueContainer * p_container)
1102 {
1103     rc_t rc = 0;
1104 
1105     assert(p_container);
1106 
1107     if (p_container->convert_fn)
1108     {
1109         char * param_value = (char *)p_container->param_value;
1110         WhackParamFnP whack = p_container->whack;
1111 
1112         p_container->param_value = NULL;
1113         p_container->whack = NULL;
1114         rc = p_container->convert_fn(self, p_container->param_index, param_value, string_size(param_value), &p_container->param_value, &p_container->whack);
1115         whack(param_value);
1116 
1117         if (rc == 0 && p_container->whack == NULL)
1118         {
1119             p_container->whack = ParamValueNotConvWhack;
1120         }
1121     }
1122 
1123     return rc;
1124 }
1125 
1126 static
ProcessArgsConversion(Args * self)1127 rc_t CC ProcessArgsConversion(Args * self)
1128 {
1129     rc_t rc = 0;
1130     uint32_t i, param_count = VectorLength(&self->param_values);
1131     ParamValueContainer * p_container;
1132 
1133     for (i = 0; i < param_count; ++i)
1134     {
1135         p_container = VectorGet (&self->param_values, i);
1136         rc = ProcessArgConversion(self, p_container);
1137         if (rc != 0)
1138             break;
1139     }
1140 
1141     if (rc == 0)
1142     {
1143         BSTNode* node = BSTreeFirst(&self->names);
1144         while (node)
1145         {
1146             Option * option = (Option *)node;
1147             uint32_t option_values_count = VectorLength(&option->values);
1148             if (option->needs_value)
1149             {
1150                 for (i = 0; i < option_values_count; ++i)
1151                 {
1152                     p_container = VectorGet (&option->values, i);
1153                     rc = ProcessArgConversion(self, p_container);
1154                     if (rc != 0)
1155                         break;
1156                 }
1157 
1158                 if (rc != 0)
1159                     break;
1160             }
1161 
1162             node = BSTNodeNext(node);
1163         }
1164     }
1165 
1166     return rc;
1167 
1168 }
1169 
1170 static
ArgsParseInt(Args * self,int argc,char * argv[])1171 rc_t ArgsParseInt (Args * self, int argc, char *argv[])
1172 {
1173     rc_t rc = 0;        /* hard fail - quit processing */
1174     rc_t orc = 0;       /* soft fail - keep processing but we'll fail in the end */
1175     rc_t qrc = 0;       /* transient - if set will move to a previously unset orc */
1176     int ix, ix_from, ix_to;
1177     size_t jx;
1178     Option * node;
1179     const char * parg;
1180     char * equal_sign;
1181 
1182     char name [32];
1183     const char * value = NULL;
1184     size_t value_len;
1185     bool needs_value;
1186     uint32_t n_in_argv_before;
1187 
1188     n_in_argv_before = VectorLength( &self->argv );
1189 
1190     /* first capture original argument list and store in our format */
1191     for ( ix = 0; ix < argc; ++ix )
1192     {
1193         size_t len;
1194         ParamValueContainer * p_container;
1195 
1196         parg = argv[ix];
1197         len = string_size ( parg );
1198 
1199         p_container = (ParamValueContainer *)malloc( sizeof *p_container );
1200         if (p_container == NULL)
1201         {
1202             rc = RC (rcExe, rcArgv, rcConstructing, rcMemory, rcExhausted);
1203             break;
1204         }
1205 
1206         rc = ParamValueMake ( p_container, ix, parg, len, NULL );
1207         if ( rc == 0 )
1208             rc = VectorAppend ( &self->argv, NULL, p_container );
1209         if ( rc )
1210         {
1211             if (p_container->whack != NULL)
1212                 p_container->whack(p_container->param_value);
1213             break;
1214         }
1215         else
1216             ARGS_DBG( "ArgsParse: inserted '%s' into self->argv", parg );
1217     }
1218 
1219     if ( rc )
1220         return rc;
1221 
1222     if ( n_in_argv_before == 0 )
1223     {
1224         ix_from = 1;
1225         ix_to = argc;
1226     }
1227     else
1228     {
1229         ix_from = n_in_argv_before;
1230         ix_to = n_in_argv_before + argc;
1231     }
1232 
1233     for ( ix = ix_from; ix < ix_to; ++ix )
1234     {
1235         ParamValueContainer * p_container = (ParamValueContainer *)VectorGet( &self->argv, ix );
1236         parg = ( const char * )p_container->param_value;
1237 
1238         ARGS_DBG( "ArgsParse: parsing '%s' from self->argv", parg );
1239 
1240         if ( parg[ 0 ] != '-' )
1241         {
1242             uint32_t params_count = VectorLength( &self->param_values );
1243             Parameter * param = VectorGet( &self->params, params_count );
1244             rc = ParamAddValue( &self->param_values, ix, parg, string_size( parg ), param ? param->convert_fn : NULL );
1245             if ( rc )
1246                 break;
1247             ARGS_DBG( "ArgsParse: appending to param_values '%s'", parg );
1248         }
1249         else
1250         {
1251             node = NULL;
1252             if ( parg[ 1 ] == '-' )
1253             {
1254                 size_t nlen = string_copy( name, sizeof( name ), parg + 2, string_size( parg + 2 ) );
1255                 ARGS_DBG( "ArgsParse: '%s' is a '--' parameter!", parg );
1256                 equal_sign = string_chr( name, nlen, '=' );
1257                 if ( equal_sign )
1258                     *equal_sign = '\0';
1259 
1260                 node = ( Option* )BSTreeFind( &self->names, name, OptionCmp );
1261                 if ( node == NULL )
1262                 {
1263                     qrc = RC( rcApp, rcArgv, rcParsing, rcParam, rcUnknown );
1264                     PLOGERR (klogErr,
1265                              (klogErr, qrc,
1266                               "Unknown argument '$(O)'", "O=--%s", name ));
1267 /*                     break; */
1268                     if (orc == 0) /* non-fatal */
1269                         orc = qrc;
1270                 }
1271                 else
1272                 {
1273                     needs_value = OptionNeedsValue( node );
1274                     ARGS_DBG( "ArgsParse: found node for parameter '%s'", parg );
1275                     if ( needs_value )
1276                     {
1277                         ARGS_DBG( "ArgsParse: parameter '%s' needs value", parg );
1278                         if ( equal_sign != NULL )
1279                             value = parg + 2 + ( ( equal_sign + 1 ) - name );
1280 
1281                         else if ( ix + 1 >= ix_to )
1282                         {
1283                             rc = RC( rcExe, rcArgv, rcParsing, rcParam, rcExhausted );
1284                             PLOGERR (klogErr,
1285                                      (klogErr, qrc,"Option '$(O)' is missing a value", "O=--%s", node->name ));
1286                         }
1287                         else
1288                         {
1289                             ARGS_DBG( "ArgsParse: calling next_arg()", 0 );
1290                             rc = next_arg( self, &ix, ix_to, &value );
1291                         }
1292 
1293                         if ( rc == 0 )
1294                         {
1295                             assert ( value != NULL );
1296 
1297                             value_len = string_size( value );
1298 
1299                             rc = OptionAddValue( node, ix, value, value_len );
1300                         }
1301                     }
1302                     else
1303                     {
1304                         rc = OptionAddValue( node, ix, NULL, 0 );
1305                     }
1306 
1307                     if ( rc != 0 )
1308                         break;
1309 
1310                     if ( node -> error )
1311                     {
1312                         rc = RC ( rcApp, rcArgv, rcParsing, rcParam, rcUnsupported );
1313                         PLOGERR (klogErr,
1314                                  (klogErr, rc,
1315                                   "Deprecated argument '$(O)' is no longer supported.", "O=--%s", name ));
1316                         break;
1317                     }
1318 
1319                     if ( node -> deprecated )
1320                     {
1321                         PLOGMSG (klogWarn,
1322                                  (klogWarn,
1323                                   "Deprecated argument '$(O)' may not be supported in future releases.", "O=--%s", name ));
1324                     }
1325                 }
1326             }
1327             else
1328             {
1329                 const char * end;
1330                 uint32_t name_len;
1331 
1332                 end = parg + string_size( parg );
1333                 ARGS_DBG( "ArgsParse: '%s' is a '-' parameter!", parg );
1334                 for ( jx = 1; parg[ jx ]; jx += name_len )
1335                 {
1336                     OptAlias *alias;
1337                     uint32_t c;
1338 
1339                     name_len = utf8_utf32( &c, parg + jx, end );
1340                     string_copy( name, sizeof( name ), parg + jx, name_len );
1341 
1342                     alias = ( OptAlias* )BSTreeFind( &self->aliases, name, OptAliasCmp );
1343                     if ( alias == NULL )
1344                     {
1345                         qrc = RC( rcApp, rcArgv, rcParsing, rcParam, rcUnknown );
1346                         PLOGERR (klogErr,
1347                                  (klogErr, qrc,
1348                                   "Unknown argument '$(O)'", "O=-%s", name ));
1349 
1350                         if (orc == 0)
1351                             orc = qrc;
1352                     }
1353                     else
1354                     {
1355                         bool break_loop = false;
1356                         node = OptAliasOption( alias );
1357                         if ( OptionNeedsValue( node ) )
1358                         {
1359                             if ( parg[ jx + name_len ] == '=' )
1360                             {
1361                                 ++jx;
1362                                 if ( parg[ jx + name_len ] )
1363                                     value = parg + jx + name_len;
1364                                 else
1365                                 {
1366                                     qrc = RC( rcExe, rcArgv, rcParsing, rcParam, rcExhausted );
1367                                     LOGERR (klogErr, qrc,
1368                                             "Value missing with alias followed by =" );
1369                                     if (orc == 0)
1370                                         orc = qrc;
1371                                     break_loop = true;
1372                                 }
1373                             }
1374                             else if ( parg[ jx + name_len ] )
1375                             {
1376                                 value = parg + jx + name_len;
1377                             }
1378                             else if ( ix + 1 >= ix_to )
1379                             {
1380                                 rc = RC( rcExe, rcArgv, rcParsing, rcParam, rcExhausted );
1381                                 PLOGERR (klogErr,
1382                                          (klogErr, rc,
1383                                           "Option '$(O)' ( alias for '$(N)' ) is missing a value",
1384                                           "O=-%s,N=--%s", name, node->name ));
1385                                 break;
1386                             }
1387                             else
1388                             {
1389                                 ParamValueContainer * next_p_container = (ParamValueContainer *) VectorGet( &self->argv, ++ix );
1390                                 assert(next_p_container);
1391                                 value = ( const char * ) next_p_container->param_value;
1392                             }
1393                             ARGS_DBG( "ArgsParse: the value of '%s' is '%s'", name, value );
1394 
1395                             if ( rc == 0 )
1396                                 rc = OptionAddValue( node, ix, value, string_size( value ) );
1397                             node -> called_by_alias = true;
1398                             break_loop = true;
1399                         }
1400                         else
1401                         {
1402                             rc = OptionAddValue( node, ix, NULL, 0 );
1403                         }
1404 
1405                         if ( rc != 0 )
1406                             break;
1407 
1408                         if ( node -> error )
1409                         {
1410                             rc = RC ( rcApp, rcArgv, rcParsing, rcParam, rcUnsupported );
1411                             PLOGERR (klogErr,
1412                                      (klogErr, rc,
1413                                       "Deprecated argument '$(O)' ( alias for '$(N)' ) is no longer supported.",
1414                                       "O=-%s,N=--%s"
1415                                       , name
1416                                       , node->name
1417                                          ));
1418                             break;
1419                         }
1420 
1421                         if ( alias -> error )
1422                         {
1423                             rc = RC ( rcApp, rcArgv, rcParsing, rcParam, rcUnsupported );
1424                             PLOGERR (klogErr,
1425                                      (klogErr, rc,
1426                                       "Deprecated argument '$(O)' is no longer supported. "
1427                                       "Please use '$(N)' instead.",
1428                                       "O=-%s,N=--%s"
1429                                       , name
1430                                       , node->name
1431                                          ));
1432                             break;
1433                         }
1434 
1435                         if ( node -> error )
1436                         {
1437                             PLOGMSG (klogWarn,
1438                                      (klogWarn,
1439                                       "Deprecated argument '$(O)' ( alias for '$(N)' ) may not be supported in future releases.",
1440                                       "O=-%s,N=--%s"
1441                                       , name
1442                                       , node->name
1443                                          ));
1444                         }
1445 
1446                         else if ( alias -> deprecated )
1447                         {
1448                             PLOGMSG (klogWarn,
1449                                      (klogWarn,
1450                                       "Deprecated argument '$(O)' may not be supported in future releases. "
1451                                       "Please use '$(N)' instead.",
1452                                       "O=-%s,N=--%s"
1453                                       , name
1454                                       , node->name
1455                                          ));
1456                         }
1457 
1458                         if ( break_loop )
1459                             break;
1460                     }
1461                 }
1462             }
1463         }
1464         if ( rc )
1465             break;
1466     }
1467 
1468     if (rc == 0)
1469     {
1470         rc = ProcessArgsConversion(self);
1471     }
1472 
1473 #if _DEBUGGING
1474     (void)ArgsHandleDebug (self);
1475 #endif
1476 #if USE_EARLY_HELP
1477     if (rc == 0)
1478     {
1479         rc = ArgsHandleHelp (self);
1480         /* if "help" wasn't found we aren't using standard arguments so don't
1481          * call it an error: if OptionGout changes this might have to as well */
1482         if (rc == SILENT_RC (rcExe, rcArgv, rcAccessing, rcName, rcNotFound))
1483             rc = 0;
1484     }
1485 #endif
1486 #if USE_EARLY_VERSION
1487     if (rc == 0)
1488     {
1489         rc = ArgsHandleVersion (self);
1490         if (rc == SILENT_RC (rcExe, rcArgv, rcAccessing, rcName, rcNotFound))
1491             rc = 0;
1492     }
1493 #endif
1494     /* now recovery non-fatal errors into final rc */
1495     if (rc == 0)
1496         rc = orc;
1497 
1498     if ( rc )
1499     {
1500         ReportSilence();
1501     }
1502     return rc;
1503 }
1504 
ArgsParse(Args * self,int argc,char * argv[])1505 rc_t CC ArgsParse (Args * self, int argc, char *argv[])
1506 {
1507     KLogLevel lvl = KLogLevelGet ();
1508     rc_t rc = KLogLevelSet ( klogWarn );
1509     rc = ArgsParseInt ( self, argc, argv );
1510     KLogLevelSet ( lvl );
1511     return rc;
1512 }
1513 
ArgsOptionCount(const Args * self,const char * option_name,uint32_t * count)1514 rc_t CC ArgsOptionCount (const Args * self, const char * option_name, uint32_t * count)
1515 {
1516     rc_t rc;
1517 
1518     if (self == NULL)
1519         return RC (rcExe, rcArgv, rcAccessing, rcSelf, rcNull);
1520     else if (count == NULL)
1521         return RC (rcExe, rcArgv, rcAccessing, rcParam, rcNull);
1522     else
1523     {
1524         const Option * node;
1525 
1526         node = (const Option*)BSTreeFind (&self->names, option_name, OptionCmp);
1527         if (node == NULL)
1528         {
1529             rc = RC (rcExe, rcArgv, rcAccessing, rcName, rcNotFound);
1530 /* this was removed to shut up about "help" not being found during tests
1531             PLOGERR (klogWarn, (klogWarn, rc, "Option name not found '$(S)'", "S=%s", option_name));
1532  */
1533             return rc;
1534         }
1535 
1536         *count = OptionGetCount (node);
1537         return 0;
1538     }
1539 }
1540 
ArgsOptionValueExt(const Args * self,const char * option_name,uint32_t iteration,const void ** value_bin,bool * called_as_alias)1541 rc_t CC ArgsOptionValueExt (const Args * self, const char * option_name,
1542     uint32_t iteration, const void ** value_bin, bool * called_as_alias)
1543 {
1544     const Option * node;
1545     rc_t rc;
1546 
1547     if (self == NULL)
1548         return RC (rcExe, rcArgv, rcAccessing, rcSelf, rcNull);
1549 
1550     if ((option_name == NULL) || (value_bin == NULL) ||
1551         (called_as_alias == NULL))
1552     {
1553         return RC (rcExe, rcArgv, rcAccessing, rcParam, rcNull);
1554     }
1555 
1556     *value_bin = NULL;
1557 
1558     node = (const Option*)BSTreeFind (&self->names, option_name, OptionCmp);
1559     if (node == NULL)
1560         return RC (rcExe, rcArgv, rcAccessing, rcName, rcNotFound);
1561 
1562     rc = OptionGetValue (node, iteration, value_bin);
1563 
1564     * called_as_alias = node -> called_by_alias;
1565 
1566     return rc;
1567 }
1568 
ArgsOptionValue(const Args * self,const char * option_name,uint32_t iteration,const void ** value_bin)1569 rc_t CC ArgsOptionValue (const Args * self, const char * option_name,
1570     uint32_t iteration, const void ** value_bin)
1571 {
1572     bool val = false;
1573     return ArgsOptionValueExt (self, option_name, iteration, value_bin, & val );
1574 }
1575 
ArgsParamCount(const Args * self,uint32_t * count)1576 rc_t CC ArgsParamCount (const Args * self, uint32_t * count)
1577 {
1578     if (self == NULL)
1579         return RC (rcExe, rcArgv, rcAccessing, rcSelf, rcNull);
1580     else if (count == NULL)
1581         return RC (rcExe, rcArgv, rcAccessing, rcParam, rcNull);
1582 
1583     *count = VectorLength (&self->param_values);
1584     return 0;
1585 }
1586 
ArgsParamValue(const Args * self,uint32_t iteration,const void ** value)1587 rc_t CC ArgsParamValue (const Args * self, uint32_t iteration, const void ** value)
1588 {
1589     rc_t rc;
1590 
1591     if (self == NULL)
1592         return RC (rcExe, rcArgv, rcAccessing, rcSelf, rcNull);
1593 
1594     if (value == NULL)
1595         return RC (rcExe, rcArgv, rcAccessing, rcParam, rcNull);
1596 
1597     if (iteration >= VectorLength (&self->param_values))
1598     {
1599         *value = NULL;
1600         return RC (rcExe, rcArgv, rcAccessing, rcParam, rcExcessive);
1601     }
1602 
1603 
1604     rc = ParamGetValue (&self->param_values, iteration, value);
1605     return rc;
1606 }
1607 
ArgsArgvCount(const Args * self,uint32_t * count)1608 rc_t CC ArgsArgvCount (const Args * self, uint32_t * count)
1609 {
1610     if (self == NULL)
1611         return RC (rcExe, rcArgv, rcAccessing, rcSelf, rcNull);
1612     else if (count == NULL)
1613         return RC (rcExe, rcArgv, rcAccessing, rcArgv, rcNull);
1614 
1615     *count = VectorLength (&self->argv);
1616     return 0;
1617 }
1618 #ifdef ArgsArgc
1619 #undef ArgsArgc
1620 #endif
1621 
ArgsArgc(const Args * self,uint32_t * count)1622 rc_t CC ArgsArgc (const Args * self, uint32_t * count)
1623 {
1624     return ArgsArgvCount (self, count);
1625 }
1626 
ArgsArgvValue(const Args * self,uint32_t iteration,const char ** value_string)1627 rc_t CC ArgsArgvValue (const Args * self, uint32_t iteration, const char ** value_string)
1628 {
1629     ParamValueContainer * p_container;
1630 
1631     if (self == NULL)
1632         return RC (rcExe, rcArgv, rcAccessing, rcSelf, rcNull);
1633 
1634     if (value_string == NULL)
1635         return RC (rcExe, rcArgv, rcAccessing, rcArgv, rcNull);
1636 
1637     if (iteration >= VectorLength (&self->argv))
1638     {
1639         *value_string = NULL;
1640         return RC (rcExe, rcArgv, rcAccessing, rcArgv, rcExcessive);
1641     }
1642 
1643     p_container = (ParamValueContainer*) VectorGet (&self->argv, iteration);
1644     assert(p_container);
1645 
1646     *value_string = (const char *)p_container->param_value;
1647     return 0;
1648 }
1649 
1650 #define USAGE_MAX_SIZE 81
1651 static
1652 const char * verbose_usage[] =
1653 { "Increase the verbosity of the program status messages.",
1654   "Use multiple times for more verbosity.","Negates quiet.", NULL };
1655 static
1656 const char * quiet_usage[] =
1657 { "Turn off all status messages for the program.",
1658   "Negated by verbose.", NULL };
1659 static
1660 const char * debug_usage[] =
1661 { "Turn on debug output for module.",
1662   "All flags if not specified.", NULL };
1663 static
1664 const char * help_usage[] =
1665 { "Output a brief explanation for the program.", NULL };
1666 static
1667 const char * report_usage[] =
1668 { "Control program execution environment report generation (if implemented).",
1669     "One of (never|error|always). Default is error.", NULL };
1670 static
1671 const char * version_usage[] =
1672 { "Display the version of the program then quit.", NULL };
1673 static
1674 const char * optfile_usage[] =
1675 { "Read more options and parameters from the file.", NULL};
1676 static
1677 char log0 [USAGE_MAX_SIZE];
1678 static
1679 char log1 [USAGE_MAX_SIZE];
1680 static
1681 const char * log_usage[] =
1682 { "Logging level as number or enum string.", log0, log1, NULL };
1683 static
1684 const char * no_user_settings_usage[] =
1685 { "Turn off user-specific configuration.", NULL };
1686 
1687     /*  We need dat here
1688      */
1689 static
1690 const char * append_usage[] =
1691         { "Append program output to a file if it does exist. Otherwise new file will be created", NULL };
1692 
1693 static
gen_log_usage(const char ** _buffers)1694 void CC gen_log_usage (const char ** _buffers)
1695 {
1696     static const char div[] = "|";
1697 
1698     /* these have to be mutable for this to work */
1699     char ** buffers = (char **) _buffers;
1700 
1701     char * pc;
1702     char * p0;
1703     char * p1;
1704     size_t rem;
1705     size_t used;
1706     KLogLevel level;
1707 
1708     rc_t rc;
1709     char buffv[USAGE_MAX_SIZE];
1710 
1711 
1712     p0 = pc = buffers[1];
1713     p1 = pc = buffers[2];
1714 
1715     *p0 = *p1 = '\0';
1716 
1717     rem = USAGE_MAX_SIZE; /* makes an assumption */
1718     pc = buffv;
1719     for (level = klogLevelMin; level <= klogLevelMax; ++level)
1720     {
1721         rc = KLogLevelExplain (level, pc, rem, &used);
1722         if (rc || used == 0)
1723         {
1724             p0 = NULL;
1725             pc = NULL;
1726             break;
1727         }
1728         pc += used;
1729         rem -= used;
1730         strcpy (pc, div);
1731         pc += sizeof div - 1;
1732         rem -= sizeof div - 1;
1733     }
1734     if (p0)
1735     {
1736         pc -= sizeof div - 1;
1737         rem += sizeof div - 1;
1738         *pc = '\0';
1739 
1740         rc = string_printf (p0, USAGE_MAX_SIZE, & used,
1741                             "One of (%s) or (%u-%u)",
1742                             buffv, klogLevelMin, klogLevelMax);
1743         if (used == 0)
1744             p0 = NULL;
1745     }
1746     rc = KLogLevelExplain (KLogLevelGet(), buffv, sizeof buffv, &used);
1747     if (rc || used == 0)
1748         p1 = NULL;
1749     else
1750     {
1751         buffv[used] = '\0';
1752         rc = string_printf (p1, USAGE_MAX_SIZE, & used,
1753                          "Current/default is %s",
1754                          buffv);
1755         if (used == 0)
1756             p1 = NULL;
1757     }
1758     if (p0 == NULL)
1759     {
1760         p0 = p1;
1761         p1 = NULL;
1762     }
1763 }
1764 
1765 
1766 
1767 OptDef StandardOptions[]  =
1768 {
1769     {
1770         OPTION_HELP            , ALIAS_HELP     , NULL, help_usage,
1771         OPT_UNLIM, false, false
1772     },
1773     {
1774         OPTION_VERSION         , ALIAS_VERSION  , NULL, version_usage,
1775         OPT_UNLIM, false, false
1776     },
1777     {
1778         OPTION_LOG_LEVEL       , ALIAS_LOG_LEVEL, gen_log_usage,     log_usage,
1779         OPT_UNLIM, true, false
1780     },
1781     {
1782         OPTION_VERBOSE         , ALIAS_VERBOSE  , NULL, verbose_usage,
1783         OPT_UNLIM, false, false
1784     },
1785     {
1786         OPTION_QUIET           , ALIAS_QUIET    , NULL, quiet_usage,
1787         OPT_UNLIM, false, false
1788     },
1789 #if USE_OPTFILE
1790     {
1791         OPTION_OPTFILE         , ALIAS_OPTFILE  , NULL, optfile_usage,
1792         OPT_UNLIM, true , false, ArgsConvFilepath
1793     },
1794 #endif
1795     {
1796         OPTION_DEBUG           , ALIAS_DEBUG    , NULL, debug_usage,
1797         OPT_UNLIM, true , false
1798     },
1799     {
1800         OPTION_NO_USER_SETTINGS, NULL           , NULL, no_user_settings_usage,
1801         OPT_UNLIM, false, false
1802     },
1803     {   /* OPTION_REPORT is used in klib/report.c */
1804         OPTION_REPORT          , NULL           , NULL, report_usage,
1805         OPT_UNLIM, true , false
1806     }
1807 };
1808 
ArgsAddStandardOptions(Args * self)1809 rc_t CC ArgsAddStandardOptions(Args * self)
1810 {
1811     return ArgsAddOptionArray (self, StandardOptions,
1812                                sizeof (StandardOptions) / sizeof (OptDef)
1813                                /*, NULL, NULL */ );
1814 }
1815 
ArgsMakeStandardOptions(Args ** pself)1816 rc_t CC ArgsMakeStandardOptions (Args** pself)
1817 {
1818     Args * self;
1819     rc_t rc;
1820 
1821     rc = ArgsMake (&self);
1822     if (rc == 0)
1823     {
1824         rc = ArgsAddStandardOptions (self);
1825         if (rc)
1826             ArgsWhack (self);
1827     }
1828     *pself = (rc == 0) ? self : NULL;
1829     return rc;
1830 }
1831 
ArgsHandleHelp(Args * self)1832 rc_t CC ArgsHandleHelp (Args * self)
1833 {
1834     uint32_t count;
1835     rc_t rc;
1836 
1837     rc = ArgsOptionCount (self, OPTION_HELP, &count);
1838     if (rc == 0)
1839     {
1840         if (count)
1841         {
1842             /* this is a call into the main program code */
1843             Usage(self);
1844             ArgsWhack (self);
1845             exit (0);
1846         }
1847     }
1848     return rc;
1849 }
1850 
1851 
ArgsHandleVersion(Args * self)1852 rc_t CC ArgsHandleVersion (Args * self)
1853 {
1854     uint32_t count;
1855     rc_t rc;
1856 
1857     rc = ArgsOptionCount (self, OPTION_VERSION, &count);
1858     if (rc == 0)
1859     {
1860         if (count)
1861         {
1862             const char * progname = UsageDefaultName;
1863             const char * fullpath = UsageDefaultName;
1864 
1865             char cSra [ 512 ] = "";
1866             SraReleaseVersion sraVersion;
1867             memset ( & sraVersion, 0, sizeof sraVersion );
1868             {
1869                 rc_t rc = SraReleaseVersionGet ( & sraVersion );
1870                 if ( rc == 0 ) {
1871                     rc = SraReleaseVersionPrint
1872                         ( & sraVersion, cSra, sizeof cSra, NULL );
1873                 }
1874             }
1875 
1876             if (self)
1877                 rc = ArgsProgram (self, &fullpath, &progname);
1878 
1879             HelpVersion (fullpath, KAppVersion());
1880 
1881             ArgsWhack (self);
1882             exit (0);
1883         }
1884     }
1885     return rc;
1886 }
1887 
ArgsHandleLogLevel(const Args * self)1888 rc_t CC ArgsHandleLogLevel (const Args * self)
1889 {
1890     uint32_t count;
1891     rc_t rc;
1892 
1893     rc = ArgsOptionCount (self, OPTION_LOG_LEVEL, &count);
1894     if (rc == 0)
1895     {
1896         if (count != 0) {
1897             const char * value;
1898             uint32_t ix;
1899 
1900             for (ix = 0; ix < count; ++ix)
1901             {
1902                 rc = ArgsOptionValue (self, OPTION_LOG_LEVEL,
1903                                       ix, (const void**)&value);
1904                 if (rc == 0)
1905                     rc = LogLevelSet (value);
1906                 if (rc)
1907                     break;
1908             }
1909         }
1910     }
1911     return rc;
1912 }
1913 
ArgsHandleStatusLevel(const Args * self)1914 rc_t CC ArgsHandleStatusLevel (const Args * self)
1915 {
1916     uint32_t vcount, qcount;
1917     rc_t rc;
1918 
1919     rc = ArgsOptionCount (self, OPTION_VERBOSE, &vcount);
1920     if (rc == 0)
1921     {
1922         rc = ArgsOptionCount (self, OPTION_QUIET, &qcount);
1923         if (rc == 0)
1924         {
1925             KStsLevel current;
1926 
1927             current = KStsLevelGet();
1928 
1929             if (vcount)
1930             {
1931                 rc_t irc;
1932 
1933                 irc = SILENT_RC (rcExe, rcArgv, rcParsing, rcParam, rcIncorrect);
1934                 if (qcount)
1935                 {
1936                     PLOGERR (klogErr,
1937                              (klogErr, irc,
1938                               "$(V)($(v)) and $(Q)($(q)) should not be used together",
1939                               "V=%s,v=%s,Q=$s,q=%s",
1940                               OPTION_VERBOSE,ALIAS_VERBOSE,
1941                               OPTION_QUIET,ALIAS_QUIET));
1942                 }
1943                 KStsLevelSet (current + vcount);
1944             }
1945             else if (qcount)
1946                 KStsLevelSet (0);
1947         }
1948     }
1949     return rc;
1950 }
1951 
ArgsHandleNoUserSettings(const Args * self)1952 rc_t CC ArgsHandleNoUserSettings (const Args * self)
1953 {
1954     uint32_t count = 0;
1955     rc_t rc = ArgsOptionCount (self, OPTION_NO_USER_SETTINGS, &count);
1956     if (rc == 0 && count != 0)
1957         KConfigDisableUserSettings ();
1958     return rc;
1959 }
1960 
1961 
1962 #if USE_OPTFILE
ArgsHandleOptfile(Args * self)1963 rc_t CC ArgsHandleOptfile (Args * self)
1964 {
1965     return Args_parse_inf_file (self, OPTION_OPTFILE);
1966 }
1967 #endif
1968 
1969 
1970 #if _DEBUGGING
ArgsHandleDebug(const Args * self)1971 rc_t CC ArgsHandleDebug (const Args * self)
1972 {
1973     uint32_t count;
1974     rc_t rc;
1975 
1976     rc = ArgsOptionCount (self, OPTION_DEBUG, &count);
1977     if (rc == 0)
1978     {
1979         if (count == 0)
1980         {
1981         }
1982         else
1983         {
1984             const char * value;
1985             uint32_t ix;
1986 
1987             for (ix = 0; ix < count; ++ix)
1988             {
1989                 rc = ArgsOptionValue (self, OPTION_DEBUG,
1990                                       ix, (const void**)&value);
1991                 if (rc == 0)
1992                     rc = KDbgSetString (value);
1993                 if (rc)
1994                     break;
1995             }
1996         }
1997     }
1998     return rc;
1999 }
2000 #endif
2001 
ArgsHandleStandardOptions(Args * self)2002 rc_t CC ArgsHandleStandardOptions (Args * self)
2003 {
2004     rc_t rc = 0;
2005 
2006     do
2007     {
2008         rc = ArgsHandleHelp (self);
2009         if (rc != 0)
2010             break;
2011 
2012         rc = ArgsHandleVersion (self);
2013         if (rc != 0)
2014             break;
2015 
2016         rc = ArgsHandleLogLevel (self);
2017         if (rc != 0)
2018             break;
2019 
2020         rc = ArgsHandleStatusLevel (self);
2021         if (rc != 0)
2022             break;
2023 
2024         rc = ArgsHandleNoUserSettings (self);
2025         if (rc != 0)
2026             break;
2027 
2028 #if _DEBUGGING
2029 	/* called a second time in case more symbols in in files */
2030         rc = ArgsHandleDebug (self);
2031         if (rc != 0)
2032             break;
2033 #endif
2034     } while (0); /* not a real loop */
2035 
2036     return rc;
2037 }
2038 
2039 /* if pself == NULL
2040    then process / initialize standard arguments and release Args */
2041 static
ArgsMakeAndHandleInt(Args ** pself,int argc,char ** argv,const ParamDef * params,uint32_t param_count,uint32_t optdef_count,va_list ap)2042 rc_t ArgsMakeAndHandleInt ( Args ** pself, int argc, char ** argv,
2043     const ParamDef *params, uint32_t param_count, uint32_t optdef_count, va_list ap )
2044 {
2045     rc_t rc;
2046     Args * self;
2047 
2048     if ( pself != NULL ) {
2049         * pself = NULL;
2050     }
2051 
2052     rc = ArgsMakeStandardOptions (&self);
2053     if ( rc == 0 && param_count != 0 )
2054     {
2055         if ( params == NULL )
2056             return RC ( rcExe, rcArgv, rcConstructing, rcParam, rcNull );
2057 
2058         rc = ArgsAddParamArray ( self, params, param_count );
2059     }
2060     if (rc == 0)
2061     {
2062         for (;;)
2063         {
2064             /* load added OptDef tables */
2065             if (optdef_count != 0)
2066             {
2067                 while (optdef_count--)
2068                 {
2069                     OptDef * options;
2070                     uint32_t opt_count;
2071 
2072                     options = va_arg (ap, OptDef *);
2073                     opt_count = va_arg (ap, uint32_t);
2074 
2075                     rc = ArgsAddOptionArray (self, options, opt_count /* , NULL, NULL */);
2076                     if (rc)
2077                         break;
2078                 }
2079 
2080                 if (rc)
2081                     break;
2082             }
2083 
2084             rc = ArgsParse (self, argc, argv);
2085             if (rc)
2086                 break;
2087 
2088 #if USE_OPTFILE
2089             /*
2090              * recursively check for files full of options
2091              * we want this done before flagging missing arguments
2092              */
2093             rc = ArgsHandleOptfile (self);
2094             if (rc)
2095                 break;
2096 #endif
2097             /*
2098              * now handle (maybe even rehandle except the optfile
2099              * the standard arguments all applications/tools support
2100              */
2101             rc = ArgsHandleStandardOptions (self);
2102             if (rc)
2103                 break;
2104 
2105             rc = ArgsCheckRequired (self);
2106             if (rc)
2107             {
2108                 MiniUsage(self);
2109                 break;
2110             }
2111 
2112             if ( pself != NULL ) {
2113                 * pself = self;
2114             }
2115             else {
2116                 ArgsWhack ( self );
2117             }
2118             return 0;
2119 
2120             break;
2121         }
2122 
2123         ArgsWhack (self);
2124     }
2125     return rc;
2126 }
2127 
ArgsMakeAndHandle(Args ** pself,int argc,char ** argv,uint32_t table_count,...)2128 rc_t CC ArgsMakeAndHandle (Args ** pself, int argc, char ** argv, uint32_t table_count, ...)
2129 {
2130     rc_t rc;
2131     va_list args;
2132     va_start ( args, table_count );
2133     rc = ArgsMakeAndHandleInt ( pself, argc, argv, NULL, 0, table_count, args );
2134     va_end ( args );
2135     return rc;
2136 }
2137 
ArgsMakeAndHandle2(Args ** pself,int argc,char ** argv,ParamDef * params,uint32_t param_count,uint32_t table_count,...)2138 rc_t CC ArgsMakeAndHandle2 (Args ** pself, int argc, char ** argv,
2139                             ParamDef * params, uint32_t param_count, uint32_t table_count, ...)
2140 {
2141     rc_t rc;
2142     va_list args;
2143     va_start ( args, table_count );
2144     rc = ArgsMakeAndHandleInt ( pself, argc, argv, params, param_count, table_count, args );
2145     va_end ( args );
2146     return rc;
2147 }
2148 
2149 
2150 /* NOTE:
2151  * This needs to move into a unix/win32 seperated file
2152  * and quite probably outside of args.
2153  */
2154 
trim_path(const char * full_name)2155 const char * CC trim_path (const char * full_name)
2156 {
2157     const char * name;
2158 
2159     name = strrchr (full_name, '/');
2160     if (name == NULL)
2161         name = full_name;
2162     else
2163         ++name; /* skip past '/' */
2164     return name;
2165 }
2166 
2167 
ArgsProgram(const Args * args,const char ** fullpath,const char ** progname)2168 rc_t CC ArgsProgram (const Args * args, const char ** fullpath, const char ** progname)
2169 {
2170     const char * defaultname = UsageDefaultName;
2171     const char * f;
2172     rc_t rc;
2173 
2174     rc = ArgsArgvValue (args, 0, &f);
2175     if (rc == 0)
2176     {
2177         if (fullpath)
2178             *fullpath = f;
2179         if (progname)
2180             *progname = trim_path (f);
2181     }
2182     else
2183     {
2184         f = defaultname;
2185 
2186         if (fullpath != NULL)
2187         {
2188             if (*fullpath == NULL)
2189                 *fullpath = f;
2190             else
2191                 f = *fullpath;
2192         }
2193         if (progname)
2194         {
2195             if (*progname == NULL)
2196                 *progname = trim_path (f);
2197         }
2198     }
2199     return rc;
2200 }
2201 
HelpVersion(const char * fullpath,ver_t version)2202 void CC HelpVersion (const char * fullpath, ver_t version)
2203 {
2204     rc_t rc = 0;
2205     char cSra[512] = "";
2206     SraReleaseVersion sraVersion;
2207     memset(&sraVersion, 0, sizeof sraVersion);
2208     rc = SraReleaseVersionGet(&sraVersion);
2209     if (rc == 0) {
2210         rc = SraReleaseVersionPrint(&sraVersion, cSra, sizeof cSra, NULL);
2211     }
2212     if (rc != 0 || cSra[0] == '\0' ||
2213         (sraVersion.version == version && sraVersion.revision == 0 &&
2214          sraVersion.type == eSraReleaseVersionTypeFinal))
2215     {
2216         OUTMSG (("\n%s : %.3V\n\n", fullpath, version));
2217     }
2218     else {
2219         OUTMSG (("\n%s : %.3V ( %s )\n\n", fullpath, version, cSra));
2220     }
2221 }
2222 
2223 
print_indented(const size_t first_indent,const size_t indent,const size_t max_line_len,const char ** msgs)2224 static void print_indented( const size_t first_indent, const size_t indent,
2225                             const size_t max_line_len, const char ** msgs )
2226 {
2227     const char * msg;
2228     size_t line_len;
2229 
2230     if ( *msgs == NULL )
2231     {
2232         OUTMSG(( "\n" ));
2233         return;
2234     }
2235 
2236     if ( first_indent < indent )
2237     {
2238         OUTMSG(( "%*s", indent - first_indent, " " ));
2239         line_len = indent;
2240     }
2241     else
2242     {
2243         OUTMSG(( "  " ));
2244         line_len = first_indent + 2;
2245     }
2246     while ( ( msg = *msgs++ ) != NULL )
2247     {
2248         while ( msg != NULL )
2249         {
2250             const char * space = strchr( msg, ' ' );
2251             if ( space != NULL )
2252             {
2253                 /* space found, can we print the word on the current line? */
2254                 int wordlen = (int) ( space - msg );
2255                 if ( ( line_len + wordlen + 1 ) < max_line_len )
2256                 {
2257                     if ( wordlen > 1 )
2258                         OUTMSG(( "%.*s", wordlen + 1, msg )); /* yes */
2259                 }
2260                 else
2261                 {
2262                     OUTMSG(( "\n%*s%.*s", indent, " ", wordlen + 1, msg )); /* no: new line */
2263                     line_len = indent;
2264                 }
2265                 line_len += ( wordlen + 1 );
2266                 msg += ( wordlen + 1 );
2267             }
2268             else
2269             {
2270                 /* no space found, can we print the string on the current line? */
2271                 size_t remainder = string_size( msg );
2272                 if ( line_len + remainder < max_line_len )
2273                 {
2274                     OUTMSG(( "%s ", msg )); /* yes */
2275                 }
2276                 else
2277                 {
2278                     OUTMSG(( "\n%*s%s ", indent, " ", msg )); /* no: new line */
2279                     line_len = indent;
2280                 }
2281                 line_len += remainder;
2282                 msg = NULL; /* we are done with one source-line... */
2283             }
2284         }
2285     }
2286     OUTMSG(( "\n" ));
2287 }
2288 
HelpOptionLine(const char * alias,const char * option,const char * param,const char ** msgs)2289 void CC HelpOptionLine(const char * alias, const char * option, const char * param, const char ** msgs)
2290 {
2291 /*    const char * msg; */
2292 #define INDENT 2
2293 #define MSG_INDENT 35
2294 #define MSG_MAXLEN 80
2295 
2296     bool has_alias = (alias != NULL && alias[0] != '\0');
2297     bool has_opt = (option != NULL && option[0] != '\0');
2298 
2299     if( has_alias || has_opt )
2300     {
2301         int n = 0, msgc = 0;
2302 
2303         OUTMSG(("%*s%n", INDENT, " ", & msgc ));
2304 
2305         if( has_alias )
2306         {
2307             OUTMSG(("-%s%n", alias, &n));
2308             msgc += n;
2309         }
2310 
2311         if( has_alias && has_opt )
2312         {
2313             OUTMSG(("|"));
2314             msgc++;
2315         }
2316 
2317         if( has_opt )
2318         {
2319             OUTMSG(("--%s%n", option, &n));
2320             msgc += n;
2321         }
2322 
2323         if( param != NULL)
2324         {
2325             OUTMSG((" <%s>%n", param, &n));
2326             msgc += n;
2327         }
2328 
2329         print_indented( msgc, MSG_INDENT, MSG_MAXLEN, msgs );
2330     }
2331 }
2332 
HelpParamLine(const char * param,const char * const * msgs)2333 void CC HelpParamLine (const char * param, const char * const * msgs)
2334 {
2335     int msgc = 0;
2336     const char * msg;
2337 
2338     msg = *msgs++;
2339 
2340     if (param)
2341     {
2342         OUTMSG (("%*s%s%n", INDENT, " ", param, &msgc));
2343         if (msg == NULL)
2344             OUTMSG (("\n"));
2345         else
2346         {
2347             OUTMSG (("%*s%s\n", MSG_INDENT-msgc, " ", msg));
2348         }
2349     }
2350     if (msg != NULL)
2351         while ((msg = *msgs++) != NULL)
2352             OUTMSG (("%*s%s\n", MSG_INDENT, " ", msg));
2353 }
2354 
2355     /*  Actually it is better to use
2356      *  BSTreeFind ( Args -> names, OPTION_APPEND_OUTPUT ... )
2357      *  but HelpOptionsStandart () does not accept args as arg lol
2358      */
2359 bool CC ArgsAppendModeWasSet ( void );
2360 
HelpOptionsStandard(void)2361 void CC HelpOptionsStandard(void){
2362     HelpOptionLine(ALIAS_HELP1    ,OPTION_HELP     , NULL    , help_usage);
2363 
2364     HelpOptionLine(ALIAS_VERSION  ,OPTION_VERSION  , NULL    , version_usage);
2365 
2366     gen_log_usage(log_usage);
2367     HelpOptionLine(ALIAS_LOG_LEVEL,OPTION_LOG_LEVEL, "level" , log_usage);
2368 
2369     HelpOptionLine(ALIAS_VERBOSE  ,OPTION_VERBOSE  , NULL    , verbose_usage);
2370     HelpOptionLine(ALIAS_QUIET    ,OPTION_QUIET    , NULL    , quiet_usage);
2371 #if USE_OPTFILE
2372     HelpOptionLine(ALIAS_OPTFILE  ,OPTION_OPTFILE  , "file"  , optfile_usage);
2373 #endif
2374 #if _DEBUGGING
2375     HelpOptionLine(ALIAS_DEBUG    ,OPTION_DEBUG, "Module[-Flag]", debug_usage);
2376         /* Dat is spooky place, we will print 'standard' append mode
2377          * help only it is necessary and only in debugging mode
2378          */
2379     if ( ArgsAppendModeWasSet () )
2380     {
2381         HelpOptionLine(NULL, OPTION_APPEND_OUTPUT, NULL, append_usage);
2382     }
2383 #endif
2384 }
2385 
HelpOptionsReport(void)2386 void CC HelpOptionsReport (void)
2387 {
2388     HelpOptionLine (ALIAS_REPORT, OPTION_REPORT, "type", report_usage);
2389 }
2390 
2391 
MiniUsage(const Args * args)2392 rc_t CC MiniUsage (const Args * args)
2393 {
2394     KWrtWriter w;
2395     void * d;
2396     const char * progname;
2397     rc_t rc;
2398 
2399     w = KOutWriterGet();
2400     d = KOutDataGet();
2401 
2402     rc = ArgsProgram (args, NULL, &progname);
2403     if (rc)
2404         progname = UsageDefaultName;
2405     KOutHandlerSetStdErr();
2406     UsageSummary (progname);
2407     KOutMsg ("\nUse option --help for more information.\n\n");
2408 
2409     KOutHandlerSet (w,d);
2410 
2411     return rc;
2412 }
2413 
2414 
Is32BitAndDisplayMessage(void)2415 bool CC Is32BitAndDisplayMessage( void )
2416 {
2417 #if _ARCH_BITS == 32
2418     KOutMsg ( "\nThis tool cannot run in a 32-bit environment,\nplease use the 64-bit version of this tool\n\n" );
2419     return true;
2420 #else
2421     return false;
2422 #endif
2423 }
2424 
2425 /* ==========
2426  * AppendMode option lives here
2427  */
2428 OptDef AppenddModeOptions []  =
2429 {
2430     {
2431         OPTION_APPEND_OUTPUT,  /* option name */
2432         NULL,   /* option alias */
2433         NULL,           /* helper function */
2434         append_usage,     /* array of strings used as a helper */
2435         1,              /* "There can be only one" (c) Highlander */
2436         false,          /* it is a flag, so no argument value */
2437         false           /* that is not required parameter */
2438     }
2439 };
2440 
2441 static bool G_append_mode_was_set = false;
2442 
ArgsAppendModeWasSet(void)2443 bool CC ArgsAppendModeWasSet ( void )
2444 {
2445     return G_append_mode_was_set;
2446 }   /* ArgsIsAppendModeWasSet () */
2447 
2448 
ArgsAddAppendModeOption(Args * self)2449 rc_t CC ArgsAddAppendModeOption ( Args * self )
2450 {
2451     G_append_mode_was_set = true;
2452     return ArgsAddOptionArray (
2453                         self,
2454                         AppenddModeOptions,
2455                         sizeof ( AppenddModeOptions ) / sizeof (OptDef)
2456                         /*, NULL, NULL */
2457                         );
2458 }   /* ArgsAddAppendModeOption () */
2459 
ArgsHandleAppendMode(const Args * self)2460 rc_t CC ArgsHandleAppendMode (const Args * self)
2461 {
2462     uint32_t count;
2463     rc_t rc;
2464 
2465     rc = ArgsOptionCount ( self, OPTION_APPEND_OUTPUT, & count);
2466     if (rc == 0)
2467     {
2468         ArgsAppendModeSet ( count != 0 );
2469     }
2470 
2471     return rc;
2472 }   /* ArgsHandleAppendMode () */
2473 
2474 static bool G_append_mode = false;
2475 
ArgsIsAppendModeSet(void)2476 bool CC ArgsIsAppendModeSet ( void )
2477 {
2478     return G_append_mode;
2479 }   /* ArgsIsAppendModeSet () */
2480 
ArgsAppendModeSet(bool AppendMode)2481 void CC ArgsAppendModeSet ( bool AppendMode )
2482 {
2483     G_append_mode = AppendMode;
2484 }   /* ArgsAppendModeSet () */
2485