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 (¶m, 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