9 # define dbprintf(...) fprintf(stderr, __VA_ARGS__)
11 # define dbprintf(...) // nothing
14 typedef unsigned int uint
;
17 static int countopts(struct opttype
*opts
) {
20 // longname and short name are both 0, OR
21 !(((opts
[i
].longname
== NULL
) && (opts
[i
].shortname
== 0)) ||
22 // output type was unspecified OR
23 ((opts
[i
].type
== 0) || (opts
[i
].type
> OPTTYPE_STRING
)) ||
24 // nowhere to output data!
25 (opts
[i
].outdata
== NULL
));
32 static int get_option_index_short(char opt
, char *potentialopts
, uint len
) {
33 for (uint i
= 0; i
< len
; i
++) {
34 if (potentialopts
[i
] == 0) {
36 } else if (potentialopts
[i
] == opt
) {
43 static int get_option_index_long(char *opt
, char **potentialopts
, uint len
) {
44 for (uint i
= 0; i
< len
; i
++) {
45 if (potentialopts
[i
] == NULL
) {
47 } else if (!strcmp(potentialopts
[i
], opt
)) {
55 signed char fetchopts(int *argc
, char ***argv
, struct opttype
*opts
) {
56 uint numopts
= countopts(opts
);
61 char *longopts
[numopts
];
62 char shortopts
[numopts
];
64 char *format_specifier
;
66 struct opttype
*wasinarg
= NULL
;
68 // char to take up less memory, unsigned so compiler doesn't complain
69 unsigned char oneoffset
;
71 int argindex
, option_index
;;
74 // new argument variable with the arguments that aren't options
77 // fill these up with opts. That way they're easier to look up
78 for (uint i
= 0; i
< numopts
; i
++) {
79 longopts
[i
] = opts
[i
].longname
;
80 shortopts
[i
] = opts
[i
].shortname
;
83 // start at 1 because 0 is the executable name
84 for (argindex
= 1; argindex
< *argc
; argindex
++) {
85 if ((curropt
= (*argv
)[argindex
]) == NULL
) continue;
87 // Last argument was an option, now we're setting the actual value of that option
88 if (wasinarg
!= NULL
) {
89 switch (wasinarg
->type
) {
90 /* We set the format specifier here then make
91 * one sscanf call with it. We don't even need
92 * to cast it because it's already a pointer
93 * unless the user fucked something up which is
96 case OPTTYPE_CHAR
: format_specifier
= "%c"; break;
97 case OPTTYPE_SHORT
: format_specifier
= "%hi"; break;
98 case OPTTYPE_USHORT
: format_specifier
= "%hu"; break;
99 case OPTTYPE_INT
: format_specifier
= "%d"; break;
100 case OPTTYPE_UINT
: format_specifier
= "%u"; break;
101 case OPTTYPE_LONG
: format_specifier
= "%ld"; break;
102 case OPTTYPE_ULONG
: format_specifier
= "%lu"; break;
104 case OPTTYPE_LONGLONG
: format_specifier
= "%l64d"; break;
105 case OPTTYPE_ULONGLONG
: format_specifier
= "%l64u"; break;
107 case OPTTYPE_LONGLONG
: format_specifier
= "%lld"; break;
108 case OPTTYPE_ULONGLONG
: format_specifier
= "%llu"; break;
110 case OPTTYPE_FLOAT
: format_specifier
= "%f"; break;
111 case OPTTYPE_DOUBLE
: format_specifier
= "%lf"; break;
112 case OPTTYPE_LONGDOUBLE
: format_specifier
= "%Lf"; break;
114 /* Handled differently. This is because %s expects a char*, and copies one buffer to
115 * the other. This is an enormous waste because we would then have to allocate a buffer
116 * when we could just make the string point to the same place as the other string.
117 * Which is also better because it means we don't have to malloc data that is later freed
118 * by the user. Fun fact: there are no mallocs in this program! VLAs FTW!
121 *(char**)(wasinarg
->outdata
) = curropt
;
123 format_specifier
= NULL
;
126 sscanf(curropt
, format_specifier
, wasinarg
->outdata
);
128 format_specifier
= NULL
;
130 // Has the user manually demanded that the option-parsing end now?
131 if (!strcmp(curropt
, "--")) {
132 // copy over the remaining arguments to newargv
133 for (int i
= argindex
+1; i
< *argc
; i
++) {
134 newargv
[newargc
] = (*argv
)[i
];
141 // in an option, getting warmer!
142 if (curropt
[0] == '-') {
143 // was it a --foo or just a -foo?
144 if (curropt
[1] == '-') {
150 // is it a short opt (e.g. -f) or a long one (e.g. -foo)?
151 if (strlen(curropt
+oneoffset
) == 1) {
152 option_index
= get_option_index_short(curropt
[oneoffset
], shortopts
, numopts
);
155 option_index
= get_option_index_long(curropt
+oneoffset
, longopts
, numopts
);
159 if (option_index
== -1) {
160 newargv
[newargc
++] = curropt
;
161 dbprintf("Faulty option %s.\n", curropt
);
164 // it's a boolean option, so the next loop doesn't want to know about it
165 if ((opts
[option_index
]).type
== OPTTYPE_BOOL
) {
166 *(bool*)opts
[option_index
].outdata
= 1;
169 // let the next loop get the value
171 wasinarg
= &opts
[option_index
];
175 newargv
[newargc
++] = curropt
;
176 dbprintf("Regular argument %s.\n", curropt
);
184 for (uint i
= 1; i
<= newargc
; i
++) {
185 /* -1, because argv starts at 1 (with 0 as program name), but newargv starts at 0 */
186 (*argv
)[i
] = newargv
[i
-1];