8 #define dprintf(...) fprintf(stderr, __VA_ARGS__);
14 uint32_t countopts(struct opttype
*opts
) {
18 // longname and short name are both 0, OR
19 if (((opts
[i
].longname
== NULL
) && (opts
[i
].shortname
== 0)) ||
20 // output type was unspecified OR
21 ((opts
[i
].type
== 0) || (opts
[i
].type
> OPTTYPE_STRING
)) ||
22 // nowhere to output data!
23 (opts
[i
].outdata
== NULL
)) {
33 int32_t get_option_index_short(char opt
, char *potentialopts
, uint32_t len
) {
34 for (uint32_t i
= 0; i
< len
; i
++) {
35 if (potentialopts
[i
] == 0) {
39 if (potentialopts
[i
] == opt
) {
47 int32_t get_option_index_long(char *opt
, char **potentialopts
, uint32_t len
) {
48 for (uint32_t i
= 0; i
< len
; i
++) {
49 if (potentialopts
[i
] == NULL
) {
53 if (!strcmp(potentialopts
[i
], opt
)) {
63 signed char fetchopts(uint32_t *argc
, char ***argv
, struct opttype
*opts
) {
64 uint32_t numopts
= countopts(opts
);
68 char *longopts
[numopts
];
69 char shortopts
[numopts
];
73 // max 5 digits (%l64u) on windows,
74 // but only 4 (%llu) on unix (plus an EOF)
76 char format_specifier
[6];
78 char format_specifier
[5];
80 // gotta save that extra byte of memory
82 struct opttype
*wasinarg
= NULL
;
84 // char to take up less memory, unsigned so compiler doesn't complain
85 unsigned char oneoffset
;
87 uint32_t argindex
, newargc
= 0;
89 // new argument variable with the arguments that aren't options
94 // fill these up with opts. That way they're easier to look up
95 for (uint32_t i
= 0; i
< numopts
; i
++) {
96 longopts
[i
] = opts
[i
].longname
;
97 shortopts
[i
] = opts
[i
].shortname
;
100 // start at 1 because 0 is the executable name
101 for (argindex
= 1; argindex
< *argc
; argindex
++) {
102 if ((curropt
= (*argv
)[argindex
]) == NULL
) continue;
104 // Last argument was an option, now we're setting the actual value of that option
105 if (wasinarg
!= NULL
) {
106 switch (wasinarg
->type
) {
107 // We set the format specifier here then make
108 // one sscanf call with it. We don't even need
109 // to cast it because it's already a pointer
110 // unless the user fucked something up which is
112 case OPTTYPE_CHAR
: strcpy(format_specifier
, "%c"); break;
113 case OPTTYPE_SHORT
: strcpy(format_specifier
, "%hi"); break;
114 case OPTTYPE_USHORT
: strcpy(format_specifier
, "%hu"); break;
115 case OPTTYPE_INT
: strcpy(format_specifier
, "%d"); break;
116 case OPTTYPE_UINT
: strcpy(format_specifier
, "%u"); break;
117 case OPTTYPE_LONG
: strcpy(format_specifier
, "%ld"); break;
118 case OPTTYPE_ULONG
: strcpy(format_specifier
, "%lu"); break;
120 case OPTTYPE_LONGLONG
: strcpy(format_specifier
, "%l64d"); break;
121 case OPTTYPE_ULONGLONG
: strcpy(format_specifier
, "%l64u"); break;
123 case OPTTYPE_LONGLONG
: strcpy(format_specifier
, "%lld"); break;
124 case OPTTYPE_ULONGLONG
: strcpy(format_specifier
, "%llu"); break;
126 case OPTTYPE_FLOAT
: strcpy(format_specifier
, "%f"); break;
127 case OPTTYPE_DOUBLE
: strcpy(format_specifier
, "%lf"); break;
128 case OPTTYPE_LONGDOUBLE
: strcpy(format_specifier
, "%Lf"); break;
130 // Handled differently. This is because %s expects a char*, and copies one buffer to
131 // the other. This is an enormous waste because we would then have to allocate a buffer
132 // when we could just make the string point to the same place as the other string.
134 *(char**)(wasinarg
->outdata
) = curropt
;
136 format_specifier
[0] = 0;
139 sscanf(curropt
, format_specifier
, wasinarg
->outdata
);
141 format_specifier
[0] = 0;
143 // Has the user manually demanded that the option-parsing end now?
144 if (!strcmp(curropt
, "--")) {
145 // copy over the remaining arguments to newargv
146 for (uint32_t i
= argindex
+1; i
< *argc
; i
++) {
147 newargv
[newargc
] = (*argv
)[i
];
154 // in an option, getting warmer!
155 if (curropt
[0] == '-') {
156 // was it a --foo or just a -foo?
157 if (curropt
[1] == '-') {
163 // is it a short opt (e.g. -f) or a long one (e.g. -foo)?
164 if (strlen(curropt
+oneoffset
) == 1) {
165 option_index
= get_option_index_short(curropt
[oneoffset
], shortopts
, numopts
);
168 option_index
= get_option_index_long(curropt
+oneoffset
, longopts
, numopts
);
172 if (option_index
== -1) {
173 newargv
[newargc
++] = curropt
;
174 dprintf("Faulty option %s.\n", curropt
);
177 // it's a boolean option, so the next loop doesn't want to know about it
178 if ((opts
[option_index
]).type
== OPTTYPE_BOOL
) {
179 *(bool*)opts
[option_index
].outdata
= 1;
182 // let the next loop get the value
184 wasinarg
= &opts
[option_index
];
188 newargv
[newargc
++] = curropt
;
189 dprintf("Regular argument %s.\n", curropt
);
197 for (uint32_t i
= 1; i
<= newargc
; i
++) {
198 // -1, because argv starts at 1 (with 0 as program name), but newargv starts at 0
199 (*argv
)[i
] = newargv
[i
-1];