Cleanup code. Remove VLAs. Support c89.
[OptFetch.git] / optfetch.c
blob4d2fd4a1b3191cce8993e834c1929a5669d95c71
1 #include <stdbool.h>
2 #include <stdarg.h>
3 #include <stdio.h>
4 #include <string.h>
6 #include "optfetch.h"
8 typedef unsigned int uint;
11 static int countopts(const struct opttype *opts) {
12 uint i;
14 for (i = 0;
15 /* longname and short name are both 0, OR */
16 !(((opts[i].longname == NULL) && (opts[i].shortname == '\0')) ||
17 /* output type was unspecified OR */
18 ((opts[i].type == 0) || (opts[i].type > OPTTYPE_STRING)) ||
19 /* nowhere to output data */
20 (opts[i].outdata == NULL));
22 i++);
24 return i;
27 static int get_option_index_short(char opt, const struct opttype *potentialopts, uint len) {
28 uint i;
30 for (i = 0; i < len; i++) {
31 if (!potentialopts[i].shortname) {
32 continue;
33 } else if (potentialopts[i].shortname == opt) {
34 return i;
37 return -1;
40 static int get_option_index_long(const char *opt, const struct opttype *potentialopts, uint len) {
41 uint i;
43 for (i = 0; i < len; i++) {
44 if (!potentialopts[i].longname) {
45 continue;
46 } else if (!strcmp(potentialopts[i].longname, opt)) {
47 return i;
50 return -1;
54 void fetchopts(int *argc, char ***argv, struct opttype *opts) {
55 uint numopts = countopts(opts);
57 int argindex;
58 int newargc = 1;
60 struct opttype *wasinarg = NULL;
62 char *curropt;
63 /* start at 1 because index 0 is the executable name */
64 for (argindex = 1; argindex < *argc; argindex++) {
65 if ((curropt = (*argv)[argindex]) == NULL) continue;
67 /* Last argument was an option, now we're setting the actual value of that option */
68 if (wasinarg != NULL) {
69 char *format_specifier;
71 switch (wasinarg->type) {
72 /* We set the format specifier here then make
73 * one sscanf call with it. We don't even need
74 * to cast it because it's already a pointer
75 * unless the user fucked something up which is
76 * their fault!
78 case OPTTYPE_CHAR: format_specifier = "%c"; break;
79 case OPTTYPE_SHORT: format_specifier = "%hi"; break;
80 case OPTTYPE_USHORT: format_specifier = "%hu"; break;
81 case OPTTYPE_INT: format_specifier = "%d"; break;
82 case OPTTYPE_UINT: format_specifier = "%u"; break;
83 case OPTTYPE_LONG: format_specifier = "%ld"; break;
84 case OPTTYPE_ULONG: format_specifier = "%lu"; break;
85 #ifdef _WIN32
86 case OPTTYPE_LONGLONG: format_specifier = "%l64d"; break;
87 case OPTTYPE_ULONGLONG: format_specifier = "%l64u"; break;
88 #else
89 case OPTTYPE_LONGLONG: format_specifier = "%lld"; break;
90 case OPTTYPE_ULONGLONG: format_specifier = "%llu"; break;
91 #endif
92 case OPTTYPE_FLOAT: format_specifier = "%f"; break;
93 case OPTTYPE_DOUBLE: format_specifier = "%lf"; break;
94 case OPTTYPE_LONGDOUBLE: format_specifier = "%Lf"; break;
96 case OPTTYPE_STRING:
97 *(char**)(wasinarg->outdata) = curropt;
98 wasinarg = NULL;
99 format_specifier = NULL;
100 continue;
101 /* OPTTYPE_BOOL already handled */
103 sscanf(curropt, format_specifier, wasinarg->outdata);
104 wasinarg = NULL;
105 format_specifier = NULL;
106 } else {
107 /* Has the user manually demanded that the option-parsing end now? */
108 if (!strcmp(curropt, "--")) {
109 argindex++;
111 goto end;
114 /* in an option, getting warmer */
115 if (curropt[0] == '-') {
116 int option_index = -1;
117 unsigned char oneoffset;
119 /* was it a --foo or just a -foo? */
120 if (curropt[1] == '-') {
121 oneoffset = 2;
122 } else {
123 oneoffset = 1;
126 /* is it a short opt (e.g. -f) or a long one (e.g. -foo)? */
127 if (strlen(curropt+oneoffset) == 1) {
128 option_index = get_option_index_short(curropt[oneoffset], opts, numopts);
131 /* long opt OR nothing matched for short opt ('f' is a valid longname) */
132 if (strlen(curropt+oneoffset) > 1 || option_index == -1) {
133 option_index = get_option_index_long(curropt+oneoffset, opts, numopts);
136 /* not an option */
137 if (option_index == -1) {
138 (*argv)[newargc++] = curropt;
139 continue;
140 } else {
141 /* it's a boolean option, so the next loop doesn't want to know about it */
142 if ((opts[option_index]).type == OPTTYPE_BOOL) {
143 *(bool*)opts[option_index].outdata = 1;
144 /* let the next loop iteration get the value */
145 } else {
146 wasinarg = &opts[option_index];
149 } else {
150 (*argv)[newargc++] = curropt;
155 end:
157 int i;
159 for (i = argindex; i < *argc; i++) {
160 /* -1, because argv starts at 1 (with 0 as program name), but newargv starts at 0 */
161 (*argv)[newargc++] = (*argv)[i];
165 *argc = newargc - 1;