-
-
Notifications
You must be signed in to change notification settings - Fork 45
/
dmalloc_argv.c
3500 lines (3105 loc) · 83.5 KB
/
dmalloc_argv.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Generic argv processor...
*
* Copyright 2020 by Gray Watson
*
* This file is part of the argv library.
*
* Permission to use, copy, modify, and distribute this software for
* any purpose and without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies, and that the name of Gray Watson not be used in advertising
* or publicity pertaining to distribution of the document or software
* without specific, written prior permission.
*
* Gray Watson makes no representations about the suitability of the
* software described herein for any purpose. It is provided "as is"
* without express or implied warranty.
*
* The author may be contacted via https://dmalloc.com/
*/
#include <ctype.h>
#if HAVE_STRING_H
# include <string.h>
#endif
#if HAVE_STDIO_H
# include <stdio.h>
#endif
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#include "conf.h"
#include "append.h"
#include "dmalloc_argv.h"
#include "dmalloc_argv_loc.h"
#include "compat.h"
/* internal routines */
static void do_list(argv_t *grid, const int arg_c, char **argv,
argv_t **queue_list, int *queue_head_p,
int *queue_tail_p, int *okay_bp);
/*
* exported variables
*/
/* This is a processed version of argv[0], pre-path removed: /bin/ls -> ls */
const char *argv_program = "Unknown";
/* A global value of argv from main after argv_process has been called */
char **argv_argv = NULL;
/* A global value of argc from main after argv_process has been called */
int argv_argc = 0;
/* This should be set externally to provide general program help to user */
char *argv_help_string = NULL;
/* This should be set externally to provide version information to the user */
char *argv_version_string = NULL;
/*
* Are we running interactively? This will exit on errors. Set to
* false to return error codes instead.
*/
int argv_interactive = ARGV_TRUE;
/*
* The FILE stream that argv out_puts all its errors. Set to NULL to
* not dump any error messages. Default is stderr.
*/
FILE *argv_error_stream = ERROR_STREAM_INIT;
/*
* This is the error code to exit with when we have a usage error and
* we are in interactive mode.
*/
int argv_error_code = EXIT_CODE;
/*
* global settings
*/
/*
* Set to 1 (the default) to enable the handling of -l=foo or
* --logfile=foo type of arguments. Set to 0 to disable. This allows
* you to specifically assign a value to an argument.
*/
int argv_close_enable_b = 1;
/*
* If the library sees a "--" argument, it will turn off further
* argument process. Set to 1 to enable the ability of specifying
* additional "--" arguments to reenable (basically toggle on then
* off) argument processing. Set to 0 (the default) to disable this
* behavior.
*/
int argv_last_toggle_b = 0;
/*
* Set to 1 (the default) to have the library accept multiple usage of
* the same argument. Set to 0 to have the library generate an error
* if you use an argument twice.
*/
int argv_multi_accept_b = 1;
/*
* Set to one of the ARGV_USAGE_ defines in the argv.h file. This
* tell the library what usage information to display when --usage is
* specified by the user. Default is ARGV_USAGE_LONG.
*/
int argv_usage_type = ARGV_USAGE_LONG;
/*
* Set to one of the ARGV_USAGE_ defines in the argv.h file. This
* tell the library what usage information to display when an error is
* encountered. The usage information accompanies the error message.
* Default is ARGV_USAGE_SEE.
*/
int argv_error_type = ARGV_USAGE_SEE;
/*
* Set to 1 (the default) if you want the library look for associated
* arguments from the associated program's environmental variable. If
* set the 0 then no environmental variable will be used. If you are
* running program foo then the library will look for the
* environmental variable ARGV_foo and will add those to the argument
* list specified on the command line. By default they will be
* inserted in front of those on the command line unless the
* argv_env_after_b is set to 1.
*
* NOTE: this is set by argv_process automatically. If you do not
* want this behavior, you should use argv_process_no_env.
*/
int argv_process_env_b = 1;
/*
* Set to 1 if you want the library to append the arguments from the
* program's environmental variable after those specified on the
* command line. If set the 0 (the default) then they will be
* inserted before those specified on the command line. See
* argv_process_env_b for more information.
*/
int argv_env_after_b = 0;
/*
* local variables
*/
/* empty argument array */
static argv_t empty[] = {{ ARGV_LAST, NULL, 0, NULL, NULL, NULL }};
static int enabled_b = ARGV_FALSE; /* are the lights on? */
/****************************** startup routine ******************************/
/*
* static void argv_startup
*
* Turn on the lights.
*/
static void argv_startup(void)
{
if (enabled_b) {
return;
}
enabled_b = ARGV_TRUE;
/* ANSI says we cannot predefine this above */
if (argv_error_stream == ERROR_STREAM_INIT) {
argv_error_stream = stderr;
}
}
/***************************** general utilities *****************************/
/*
* static int btoi
*
* Binary string to integer translation.
*
* Returns an integer converted from the string.
*
* ARGUMENTS:
*
* str - String of binary 0s and 1s that we are converting.
*/
static int btoi(const char *str)
{
int ret = 0;
/* strip off spaces */
for (; isspace(*str); str++) {
}
for (; *str == '0' || *str == '1'; str++) {
ret *= 2;
ret += *str - '0';
}
return ret;
}
/*
* static int otoi
*
* Octal string to integer translation.
*
* Returns an integer converted from the string.
*
* ARGUMENTS:
*
* str - String of octal digits that we are converting.
*/
static int otoi(const char *str)
{
int ret = 0;
/* strip off spaces */
for (; isspace(*str); str++) {
}
for (; *str >= '0' && *str <= '7'; str++) {
ret *= 8;
ret += *str - '0';
}
return ret;
}
/*
* static int htoi
*
* Hexadecimal string to integer translation.
*
* Returns an integer converted from the string.
*
* ARGUMENTS:
*
* str - String of hexadecimal characters and digits that we are
* converting.
*/
static int htoi(const char *str)
{
int ret = 0;
/* strip off spaces */
for (; isspace(*str); str++) {
}
/* skip a leading 0[xX] */
if (*str == '0' && (*(str + 1) == 'x' || *(str + 1) == 'X')) {
str += 2;
}
for (; isdigit(*str) ||
(*str >= 'a' && *str <= 'f') || (*str >= 'A' && *str <= 'F');
str++) {
ret *= 16;
if (*str >= 'a' && *str <= 'f') {
ret += *str - 'a' + 10;
}
else if (*str >= 'A' && *str <= 'F') {
ret += *str - 'A' + 10;
}
else {
ret += *str - '0';
}
}
return ret;
}
/*
* static char *string_copy
*
* Basically a strdup for compatibility sake.
*
* Returns a character pointer that must be freed later.
*
* ARGUMENTS:
*
* str - String we are copying.
*/
static char *string_copy(const char *str)
{
const char *str_p;
char *copy, *copy_p;
int len;
len = strlen(str);
copy = (char *)malloc(len + 1);
if (copy == NULL) {
if (argv_error_stream != NULL) {
(void)fprintf(argv_error_stream,
"%s: memory error during argument processing\n",
argv_program);
}
if (argv_interactive) {
(void)exit(argv_error_code);
}
return NULL;
}
for (str_p = str, copy_p = copy; *str_p != '\0';) {
*copy_p++ = *str_p++;
}
*copy_p = '\0';
return copy;
}
/*
* static char **vectorize
*
* Break a string up into its arguments separated by one of the
* characters in a token string and return an array of char pointers.
*
* NOTE: the string argument should stay around until that time.
*
* Returns Allocated list of character poiners into the string
* argument which must be freed later or NULL on failure.
*
* ARGUMENTS:
*
* str - String we are tokenizing.
*
* tok - List of token characters to look for in the string.
*
* num_tok_p - Pointer to an integer which will be set to the number
* of tokens found in the string.
*/
static char **vectorize(char *str, const char *tok, int *num_tok_p)
{
char **vect_p;
char *tmp, *str_p, *tok_p;
int tok_c, tok_n;
/* count the tokens */
tmp = string_copy(str);
if (tmp == NULL) {
return NULL;
}
str_p = tmp;
tok_c = 0;
while (1) {
tok_p = strsep(&str_p, tok);
if (tok_p == NULL) {
break;
}
if (*tok_p != '\0') {
tok_c++;
}
}
tok_n = tok_c;
free(tmp);
*num_tok_p = tok_n;
if (tok_c == 0) {
return NULL;
}
/* allocate the pointer grid */
vect_p = (char **)malloc(sizeof(char *) * tok_c);
if (vect_p == NULL) {
if (argv_error_stream != NULL) {
(void)fprintf(argv_error_stream,
"%s: memory error during argument processing\n",
argv_program);
}
if (argv_interactive) {
(void)exit(argv_error_code);
}
return NULL;
}
/* load the tokens into the list */
str_p = str;
for (tok_c = 0; tok_c < tok_n;) {
tok_p = strsep(&str_p, tok);
if (tok_p == NULL) {
break;
}
if (*tok_p != '\0') {
vect_p[tok_c] = tok_p;
tok_c++;
}
}
return vect_p;
}
/*
* static int expand_buf
*
* Translates a buffer of bytes into its printable version.
*
* NOTE: it does _not_ add a \0 at the end of OUT.
*
* Returns a number of characters written in to the output buffer.
*
* ARGUMENTS:
*
* buf - Input buffer of bytes.
*
* buf_size - Size of the input buffer. If < 0 then the routing will
* translate up to the first \0.
*
* out - Output buffer for the translated characters.
*
* out_size - Maximum size of the output buffer.
*/
static int expand_buf(const void *buf, const int buf_size,
char *out, const int out_size)
{
int buf_c;
const unsigned char *buf_p, *spec_p;
char *max_p, *out_p = out;
/* setup our max pointer */
max_p = out + out_size;
/* run through the input buffer, counting the characters as we go */
for (buf_c = 0, buf_p = (const unsigned char *)buf;; buf_c++, buf_p++) {
/* did we reach the end of the buffer? */
if (buf_size < 0) {
if (*buf_p == '\0') {
break;
}
}
else {
if (buf_c >= buf_size) {
break;
}
}
/* search for special characters */
for (spec_p = (unsigned char *)SPECIAL_CHARS + 1;
*(spec_p - 1) != '\0';
spec_p += 2) {
if (*spec_p == *buf_p) {
break;
}
}
/* did we find one? */
if (*(spec_p - 1) != '\0') {
if (out_p + 2 >= max_p) {
break;
}
(void)loc_snprintf(out_p, max_p - out_p, "\\%c", *(spec_p - 1));
out_p += 2;
continue;
}
/* print out any 7-bit printable characters */
if (*buf_p < 128 && isprint(*buf_p)) {
if (out_p + 1 >= max_p) {
break;
}
*out_p = *(char *)buf_p;
out_p += 1;
}
else {
if (out_p + 4 >= max_p) {
break;
}
(void)loc_snprintf(out_p, max_p - out_p, "\\%03o", *buf_p);
out_p += 4;
}
}
return out_p - out;
}
/****************************** usage routines *******************************/
/*
* static void usage_short
*
* Print a short-format usage message.
*
* ARGUMENTS:
*
* args - Array of argv_t structions whose usage messages you print.
*
* flags - User flags.
*/
static void usage_short(const argv_t *args, const int flag)
{
const argv_t *arg_p;
int len, col_c = 0;
int mark_b = ARGV_FALSE;
char *prefix;
if (argv_error_stream == NULL) {
return;
}
/* print the usage message header */
(void)fprintf(argv_error_stream, "%s%s", USAGE_LABEL, argv_program);
col_c += USAGE_LABEL_LENGTH + strlen(argv_program);
/*
* print all of the boolean arguments first.
* NOTE: we assume they all fit on the line
*/
for (arg_p = args; arg_p->ar_short_arg != ARGV_LAST; arg_p++) {
/* skip or-specifiers */
if (arg_p->ar_short_arg == ARGV_OR
|| arg_p->ar_short_arg == ARGV_XOR) {
continue;
}
/* skip non booleans */
if (HAS_ARG(arg_p->ar_type)) {
continue;
}
/* skip args with no short component */
if (arg_p->ar_short_arg == '\0') {
continue;
}
if (! mark_b) {
len = 2 + SHORT_PREFIX_LENGTH;
prefix = " [";
/* we check for -2 here because we should have 1 arg and ] on line */
if (col_c + len > SCREEN_WIDTH - 2) {
(void)fprintf(argv_error_stream, "\n%*.*s",
(int)USAGE_LABEL_LENGTH, (int)USAGE_LABEL_LENGTH, "");
col_c = USAGE_LABEL_LENGTH;
/* if we are the start of a line, skip any starting spaces */
if (*prefix == ' ') {
prefix++;
len--;
}
}
(void)fprintf(argv_error_stream, "%s%s", prefix, SHORT_PREFIX);
col_c += len;
mark_b = ARGV_TRUE;
}
len = 1;
/* we check for -1 here because we should need ] */
if (col_c + len > SCREEN_WIDTH - 1) {
(void)fprintf(argv_error_stream, "]\n%*.*s",
(int)USAGE_LABEL_LENGTH, (int)USAGE_LABEL_LENGTH, "");
col_c = USAGE_LABEL_LENGTH;
/* restart the short option list */
(void)fprintf(argv_error_stream, "[%s", SHORT_PREFIX);
col_c += 1 + SHORT_PREFIX_LENGTH;
}
(void)fprintf(argv_error_stream, "%c", arg_p->ar_short_arg);
col_c++;
}
if (mark_b) {
(void)fprintf(argv_error_stream, "]");
col_c++;
}
/* print remaining (non-boolean) arguments */
for (arg_p = args; arg_p->ar_short_arg != ARGV_LAST; arg_p++) {
int var_len;
char *var_str, *postfix;
/* skip or-specifiers */
if (arg_p->ar_short_arg == ARGV_OR
|| arg_p->ar_short_arg == ARGV_XOR) {
continue;
}
/* skip booleans types */
if (! HAS_ARG(arg_p->ar_type)) {
continue;
}
if (arg_p->ar_var_label == NULL) {
if (ARGV_TYPE(arg_p->ar_type) == ARGV_BOOL_ARG
|| ARGV_TYPE(arg_p->ar_type) == ARGV_BOOL_INT_ARG) {
var_str = BOOL_ARG_LABEL;
var_len = BOOL_ARG_LENGTH;
}
else {
var_len = UNKNOWN_ARG_LENGTH;
var_str = UNKNOWN_ARG;
}
}
else {
var_len = strlen(arg_p->ar_var_label);
var_str = arg_p->ar_var_label;
}
if (arg_p->ar_short_arg == ARGV_MAND) {
/* print the mandatory argument desc */
len = 1 + var_len;
prefix = " ";
postfix = "";
}
else if (arg_p->ar_short_arg == ARGV_MAYBE) {
/* print the maybe argument desc */
len = 2 + var_len + 1;
prefix = " [";
postfix = "]";
}
else {
/* handle options with arguments */
/* " [" + short_prefix + char */
len = 2 + SHORT_PREFIX_LENGTH + 1;
prefix = " [";
/* do we need to wrap */
if (col_c + len > SCREEN_WIDTH) {
(void)fprintf(argv_error_stream, "\n%*.*s",
(int)USAGE_LABEL_LENGTH, (int)USAGE_LABEL_LENGTH, "");
col_c = USAGE_LABEL_LENGTH;
/* if we are the start of a line, skip any starting spaces */
if (*prefix == ' ') {
prefix++;
len--;
}
}
(void)fprintf(argv_error_stream, "%s%s%c",
prefix, SHORT_PREFIX, arg_p->ar_short_arg);
col_c += len;
len = 1 + var_len + 1;
prefix = " ";
postfix = "]";
}
if (col_c + len > SCREEN_WIDTH) {
(void)fprintf(argv_error_stream, "\n%*.*s",
(int)USAGE_LABEL_LENGTH, (int)USAGE_LABEL_LENGTH, "");
col_c = USAGE_LABEL_LENGTH;
/* if we are the start of a line, skip any starting spaces */
if (*prefix == ' ') {
prefix++;
len--;
}
}
(void)fprintf(argv_error_stream, "%s%s%s", prefix, var_str, postfix);
col_c += len;
}
(void)fprintf(argv_error_stream, "\n");
if (flag == ARGV_USAGE_SHORT_REM) {
(void)fprintf(argv_error_stream,
"%*.*sUse the '%s%s' argument for more assistance.\n",
(int)USAGE_LABEL_LENGTH, (int)USAGE_LABEL_LENGTH, "",
LONG_PREFIX, USAGE_ARG);
}
}
/*
* static void display_arg
*
* Display an argument type while keeping track of the column we are in.
*
* ARGUMENTS:
*
* stream - Output stream we are writing to.
*
* arg_p - Argument that we are displaying.
*
* max - Maximum column position to write to.
*
* col_cp - Pointer to an integer to record the column position.
*/
static void display_arg(FILE *stream, const argv_t *arg_p, const int max,
int *col_cp)
{
int var_len, len;
if (arg_p->ar_var_label == NULL) {
var_len = 0;
}
else {
var_len = strlen(arg_p->ar_var_label);
}
switch (ARGV_TYPE(arg_p->ar_type)) {
case ARGV_BOOL:
case ARGV_BOOL_NEG:
case ARGV_INCR:
case ARGV_BOOL_INT:
case ARGV_BOOL_INT_NEG:
break;
case ARGV_BOOL_ARG:
case ARGV_BOOL_INT_ARG:
(void)fprintf(stream, "%s", BOOL_ARG_LABEL);
(*col_cp) += BOOL_ARG_LENGTH;
break;
case ARGV_CHAR:
case ARGV_CHAR_P:
case ARGV_SHORT:
case ARGV_U_SHORT:
case ARGV_INT:
case ARGV_U_INT:
case ARGV_LONG:
case ARGV_U_LONG:
case ARGV_FLOAT:
case ARGV_DOUBLE:
case ARGV_BIN:
case ARGV_OCT:
case ARGV_HEX:
case ARGV_SIZE:
case ARGV_U_SIZE:
if (arg_p->ar_var_label == NULL) {
len = max - *col_cp;
(void)fprintf(stream, "%-.*s", len, UNKNOWN_ARG);
*col_cp += MIN(len, (int)UNKNOWN_ARG_LENGTH);
}
else {
len = max - *col_cp;
(void)fprintf(stream, "%-.*s", len, arg_p->ar_var_label);
*col_cp += MIN(len, var_len);
}
break;
}
}
/*
* static void display_option
*
* Display an option entry while while keeping track of the column we are in.
*
* ARGUMENTS:
*
* stream - Output stream we are writing to.
*
* arg_p - Argument that we are displaying.
*
* max - Maximum column position to write to.
*
* col_cp - Pointer to an integer to record the column position.
*/
static void display_option(FILE *stream, const argv_t *arg_p, int *col_cp)
{
if (stream == NULL) {
return;
}
(void)fputc('[', stream);
(*col_cp)++;
/* arg maybe does not have a -? preface */
if (arg_p->ar_short_arg != ARGV_MAYBE) {
(void)fprintf(stream, "%s%c",
SHORT_PREFIX, arg_p->ar_short_arg);
*col_cp += SHORT_PREFIX_LENGTH + 1;
if (HAS_ARG(arg_p->ar_type)) {
/* display optional argument */
(void)fputc(' ', stream);
(*col_cp)++;
}
}
display_arg(stream, arg_p, LONG_COLUMN - 1, col_cp);
(void)fputc(']', stream);
(*col_cp)++;
}
/*
* static void usage_long
*
* Print a long-format usage message.
*
* ARGUMENTS:
*
* ars - Array of argv_t structures whose usage we are printing.
*/
static void usage_long(const argv_t *args)
{
const argv_t *arg_p;
int col_c, len;
if (argv_error_stream == NULL) {
return;
}
/* print the usage message header */
(void)fprintf(argv_error_stream, "%s%s\n", USAGE_LABEL, argv_program);
/* run through the argument structure */
for (arg_p = args; arg_p->ar_short_arg != ARGV_LAST; arg_p++) {
/* skip or specifiers */
if (arg_p->ar_short_arg == ARGV_OR || arg_p->ar_short_arg == ARGV_XOR) {
continue;
}
/* indent to the short-option col_c */
(void)fprintf(argv_error_stream, "%*.*s", SHORT_COLUMN, SHORT_COLUMN, "");
/* start column counter */
col_c = SHORT_COLUMN;
/* print the short-arg stuff if there */
if (arg_p->ar_short_arg == '\0') {
(void)fputc('[', argv_error_stream);
col_c++;
}
else {
if (arg_p->ar_short_arg == '\0') {
;
}
else if (arg_p->ar_short_arg == ARGV_MAND) {
display_arg(argv_error_stream, arg_p, COMMENT_COLUMN, &col_c);
}
else {
/* ARGV_MAYBE handled here */
display_option(argv_error_stream, arg_p, &col_c);
}
/* put the long-option message on the correct column */
if (col_c < LONG_COLUMN) {
(void)fprintf(argv_error_stream, "%*.*s",
LONG_COLUMN - col_c, LONG_COLUMN - col_c, "");
col_c = LONG_COLUMN;
}
}
/* print the long-option message */
if (arg_p->ar_long_arg != NULL) {
len = COMMENT_COLUMN - col_c - (LONG_PREFIX_LENGTH + 1);
if (arg_p->ar_short_arg != '\0') {
(void)fprintf(argv_error_stream, "%s", LONG_LABEL);
col_c += LONG_LABEL_LENGTH;
len -= LONG_LABEL_LENGTH;
}
(void)fprintf(argv_error_stream, "%s%-.*s",
LONG_PREFIX, len, arg_p->ar_long_arg);
col_c += LONG_PREFIX_LENGTH + MIN(len, (int)strlen(arg_p->ar_long_arg));
}
/* add the optional argument if no short-arg */
if (arg_p->ar_short_arg == '\0') {
if (HAS_ARG(arg_p->ar_type)) {
(void)fputc(' ', argv_error_stream);
col_c++;
}
/* display any optional arguments */
display_arg(argv_error_stream, arg_p, COMMENT_COLUMN - 1, &col_c);
(void)fputc(']', argv_error_stream);
col_c++;
}
/* print the comment */
if (arg_p->ar_comment != NULL) {
/* put the comment message on the correct column */
if (col_c < COMMENT_COLUMN) {
(void)fprintf(argv_error_stream, "%*.*s",
COMMENT_COLUMN - col_c,
COMMENT_COLUMN - col_c, "");
col_c = COMMENT_COLUMN;
}
len = SCREEN_WIDTH - col_c - COMMENT_LABEL_LENGTH;
(void)fprintf(argv_error_stream, "%s%-.*s",
COMMENT_LABEL, len, arg_p->ar_comment);
}
(void)fprintf(argv_error_stream, "\n");
}
}
/*
* static void do_usage
*
* Print the usage messages.
*
* ARGUMENTS:
*
* args - Array of argv_t structures.
*
* flag - Users flags which will tell us whether to display short or
* long usage messages.
*/
static void do_usage(const argv_t *args, const int flag)
{
if (argv_error_stream == NULL) {
return;
}
if (flag == ARGV_USAGE_SEE) {
(void)fprintf(argv_error_stream,
"%*.*sUse the '%s%s' argument for assistance.\n",
(int)USAGE_LABEL_LENGTH, (int)USAGE_LABEL_LENGTH, "",
LONG_PREFIX, USAGE_ARG);
}
else if (flag == ARGV_USAGE_SHORT || flag == ARGV_USAGE_SHORT_REM) {
usage_short(args, flag);
}
else if (flag == ARGV_USAGE_LONG || flag == ARGV_USAGE_ALL) {
usage_long(args);
}
if (flag == ARGV_USAGE_ALL) {
(void)fprintf(argv_error_stream, "\n");
(void)fprintf(argv_error_stream,
"%*.*sUse '%s%s' for default usage information.\n",
SHORT_COLUMN, SHORT_COLUMN, "",
LONG_PREFIX, USAGE_ARG);
(void)fprintf(argv_error_stream,
"%*.*sUse '%s%s' for short usage information.\n",
SHORT_COLUMN, SHORT_COLUMN, "",
LONG_PREFIX, USAGE_SHORT_ARG);
(void)fprintf(argv_error_stream,
"%*.*sUse '%s%s' for long usage information.\n",
SHORT_COLUMN, SHORT_COLUMN, "",
LONG_PREFIX, USAGE_LONG_ARG);
(void)fprintf(argv_error_stream,
"%*.*sUse '%s%s' for all usage information.\n",
SHORT_COLUMN, SHORT_COLUMN, "",
LONG_PREFIX, USAGE_ALL_ARG);
(void)fprintf(argv_error_stream,
"%*.*sUse '%s%s' to display the help message.\n",
SHORT_COLUMN, SHORT_COLUMN, "",
LONG_PREFIX, HELP_ARG);
(void)fprintf(argv_error_stream,
"%*.*sUse '%s%s' to display the version message.\n",
SHORT_COLUMN, SHORT_COLUMN, "",
LONG_PREFIX, VERSION_ARG);
(void)fprintf(argv_error_stream,
"%*.*sUse '%s%s' to display the options and their values.\n",
SHORT_COLUMN, SHORT_COLUMN, "",
LONG_PREFIX, DISPLAY_ARG);
}
}
/******************************* preprocessing *******************************/
/*
* static int preprocess_array
*
* Preprocess argument array entries and set the mandatory and maybe flags.
*
* Returns 0 on success or -1 on failure.
*
* ARGUMENTS:
*
* args - Array of argv_t structures.
*
* arg_n - Number of entries in the argv_t array. We need this for a
* couple of reasons.
*/
static int preprocess_array(argv_t *args, const int arg_n)
{
argv_t *arg_p;
int mand_array_b = ARGV_FALSE, maybe_field_b = ARGV_FALSE;
/* count the args and find the first mandatory */
for (arg_p = args; arg_p < args + arg_n; arg_p++) {
/* clear internal flags */
arg_p->ar_type &= ~ARGV_FLAG_USED;
/* do we have a mandatory-array? */
if (arg_p->ar_short_arg == ARGV_MAND) {
if (mand_array_b) {
if (argv_error_stream != NULL) {
(void)fprintf(argv_error_stream,
"%s: %s, no ARGV_MAND's can follow a MAND or MAYBE array\n",
argv_program, INTERNAL_ERROR_NAME);
}
if (argv_interactive) {
(void)exit(argv_error_code);
}
return ERROR;
}
if (maybe_field_b) {
if (argv_error_stream != NULL) {
(void)fprintf(argv_error_stream,
"%s: %s, no ARGV_MAND's can follow a ARGV_MAYBE\n",
argv_program, INTERNAL_ERROR_NAME);
}
if (argv_interactive) {
(void)exit(argv_error_code);
}
return ERROR;