Skip to content

Commit bc4ce6f

Browse files
committed
Introduce flag_size with dd(1) style multiplicative suffixes
1 parent a58206e commit bc4ce6f

File tree

2 files changed

+74
-4
lines changed

2 files changed

+74
-4
lines changed

example.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ int main(int argc, char **argv)
1515
{
1616
bool *help = flag_bool("help", false, "Print this help to stdout and exit with 0");
1717
char **line = flag_str("line", "Hi!", "Line to output to the file");
18-
uint64_t *count = flag_uint64("count", 64, "Amount of lines to generate");
18+
size_t *count = flag_size("count", 64, "Amount of lines to generate");
1919

2020
if (!flag_parse(argc, argv)) {
2121
usage(stderr);
@@ -42,7 +42,7 @@ int main(int argc, char **argv)
4242
FILE *f = fopen(file_path, "w");
4343
assert(f);
4444

45-
for (uint64_t i = 0; i < *count; ++i) {
45+
for (size_t i = 0; i < *count; ++i) {
4646
fprintf(f, "%s\n", *line);
4747
}
4848

flag.h

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
char *flag_name(void *val);
2727
bool *flag_bool(const char *name, bool def, const char *desc);
2828
uint64_t *flag_uint64(const char *name, uint64_t def, const char *desc);
29+
size_t *flag_size(const char *name, uint64_t def, const char *desc);
2930
char **flag_str(const char *name, const char *def, const char *desc);
3031
bool flag_parse(int argc, char **argv);
3132
int flag_rest_argc(void);
@@ -40,15 +41,19 @@ void flag_print_options(FILE *stream);
4041
#ifdef FLAG_IMPLEMENTATION
4142

4243
typedef enum {
43-
FLAG_BOOL,
44+
FLAG_BOOL = 0,
4445
FLAG_UINT64,
46+
FLAG_SIZE,
4547
FLAG_STR,
48+
COUNT_FLAG_TYPES,
4649
} Flag_Type;
4750

51+
static_assert(COUNT_FLAG_TYPES == 4, "Exhaustive Flag_Value definition");
4852
typedef union {
4953
char *as_str;
5054
uint64_t as_uint64;
5155
bool as_bool;
56+
size_t as_size;
5257
} Flag_Value;
5358

5459
typedef enum {
@@ -57,6 +62,8 @@ typedef enum {
5762
FLAG_ERROR_NO_VALUE,
5863
FLAG_ERROR_INVALID_NUMBER,
5964
FLAG_ERROR_INTEGER_OVERFLOW,
65+
FLAG_ERROR_INVALID_SIZE_SUFFIX,
66+
COUNT_FLAG_ERRORS,
6067
} Flag_Error;
6168

6269
typedef struct {
@@ -120,6 +127,14 @@ uint64_t *flag_uint64(const char *name, uint64_t def, const char *desc)
120127
return &flag->val.as_uint64;
121128
}
122129

130+
size_t *flag_size(const char *name, uint64_t def, const char *desc)
131+
{
132+
Flag *flag = flag_new(FLAG_SIZE, name, desc);
133+
flag->val.as_size = def;
134+
flag->def.as_size = def;
135+
return &flag->val.as_size;
136+
}
137+
123138
char **flag_str(const char *name, const char *def, const char *desc)
124139
{
125140
Flag *flag = flag_new(FLAG_STR, name, desc);
@@ -176,6 +191,7 @@ bool flag_parse(int argc, char **argv)
176191
bool found = false;
177192
for (size_t i = 0; i < c->flags_count; ++i) {
178193
if (strcmp(c->flags[i].name, flag) == 0) {
194+
static_assert(COUNT_FLAG_TYPES == 4, "Exhaustive flag type parsing");
179195
switch (c->flags[i].type) {
180196
case FLAG_BOOL: {
181197
c->flags[i].val.as_bool = true;
@@ -207,11 +223,12 @@ bool flag_parse(int argc, char **argv)
207223
// That way we can get rid of the dependency on errno and static_assert
208224
unsigned long long int result = strtoull(arg, &endptr, 10);
209225

210-
if (arg == endptr || *endptr != '\0') {
226+
if (*endptr != '\0') {
211227
c->flag_error = FLAG_ERROR_INVALID_NUMBER;
212228
c->flag_error_name = flag;
213229
return false;
214230
}
231+
215232
if (result == ULLONG_MAX && errno == ERANGE) {
216233
c->flag_error = FLAG_ERROR_INTEGER_OVERFLOW;
217234
c->flag_error_name = flag;
@@ -222,6 +239,50 @@ bool flag_parse(int argc, char **argv)
222239
}
223240
break;
224241

242+
case FLAG_SIZE: {
243+
if (argc == 0) {
244+
c->flag_error = FLAG_ERROR_NO_VALUE;
245+
c->flag_error_name = flag;
246+
return false;
247+
}
248+
char *arg = flag_shift_args(&argc, &argv);
249+
250+
static_assert(sizeof(unsigned long long int) == sizeof(size_t), "The original author designed this for x86_64 machine with the compiler that expects unsigned long long int and size_t to be the same thing, so they could use strtoull() function to parse it. Please adjust this code for your case and maybe even send the patch to upstream to make it work on a wider range of environments.");
251+
char *endptr;
252+
// TODO: replace strtoull with a custom solution
253+
// That way we can get rid of the dependency on errno and static_assert
254+
unsigned long long int result = strtoull(arg, &endptr, 10);
255+
256+
// TODO: handle more multiplicative suffixes like in dd(1). From the dd(1) man page:
257+
// > N and BYTES may be followed by the following
258+
// > multiplicative suffixes: c =1, w =2, b =512, kB =1000, K
259+
// > =1024, MB =1000*1000, M =1024*1024, xM =M, GB
260+
// > =1000*1000*1000, G =1024*1024*1024, and so on for T, P,
261+
// > E, Z, Y.
262+
if (strcmp(endptr, "K") == 0) {
263+
result *= 1024;
264+
} else if (strcmp(endptr, "M") == 0) {
265+
result *= 1024*1024;
266+
} else if (strcmp(endptr, "G") == 0) {
267+
result *= 1024*1024*1024;
268+
} else if (strcmp(endptr, "") != 0) {
269+
c->flag_error = FLAG_ERROR_INVALID_SIZE_SUFFIX;
270+
c->flag_error_name = flag;
271+
// TODO: capability to report what exactly is the wrong suffix
272+
return false;
273+
}
274+
275+
if (result == ULLONG_MAX && errno == ERANGE) {
276+
c->flag_error = FLAG_ERROR_INTEGER_OVERFLOW;
277+
c->flag_error_name = flag;
278+
return false;
279+
}
280+
281+
c->flags[i].val.as_size = result;
282+
}
283+
break;
284+
285+
case COUNT_FLAG_TYPES:
225286
default: {
226287
assert(0 && "unreachable");
227288
exit(69);
@@ -252,6 +313,7 @@ void flag_print_options(FILE *stream)
252313

253314
fprintf(stream, " -%s\n", flag->name);
254315
fprintf(stream, " %s\n", flag->desc);
316+
static_assert(COUNT_FLAG_TYPES == 4, "Exhaustive flag type defaults printing");
255317
switch (c->flags[i].type) {
256318
case FLAG_BOOL:
257319
if (flag->def.as_bool) {
@@ -261,6 +323,9 @@ void flag_print_options(FILE *stream)
261323
case FLAG_UINT64:
262324
fprintf(stream, " Default: %" PRIu64 "\n", flag->def.as_uint64);
263325
break;
326+
case FLAG_SIZE:
327+
fprintf(stream, " Default: %zu\n", flag->def.as_size);
328+
break;
264329
case FLAG_STR:
265330
if (flag->def.as_str) {
266331
fprintf(stream, " Default: %s\n", flag->def.as_str);
@@ -276,6 +341,7 @@ void flag_print_options(FILE *stream)
276341
void flag_print_error(FILE *stream)
277342
{
278343
Flag_Context *c = &flag_global_context;
344+
static_assert(COUNT_FLAG_ERRORS == 6, "Exhaustive flag error printing");
279345
switch (c->flag_error) {
280346
case FLAG_NO_ERROR:
281347
// NOTE: don't call flag_print_error() if flag_parse() didn't return false, okay? ._.
@@ -293,6 +359,10 @@ void flag_print_error(FILE *stream)
293359
case FLAG_ERROR_INTEGER_OVERFLOW:
294360
fprintf(stream, "ERROR: -%s: integer overflow\n", c->flag_error_name);
295361
break;
362+
case FLAG_ERROR_INVALID_SIZE_SUFFIX:
363+
fprintf(stream, "ERROR: -%s: invalid size suffix\n", c->flag_error_name);
364+
break;
365+
case COUNT_FLAG_ERRORS:
296366
default:
297367
assert(0 && "unreachable");
298368
exit(69);

0 commit comments

Comments
 (0)