Skip to content

Commit

Permalink
added first draft for flag parser
Browse files Browse the repository at this point in the history
  • Loading branch information
RaphGL committed Jul 6, 2024
1 parent d3a4cbd commit e38e3ae
Show file tree
Hide file tree
Showing 21 changed files with 271 additions and 0 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
18 changes: 18 additions & 0 deletions flag/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -Wpedantic
DEBUGCFLAGS = -fsanitize=address,leak,undefined -g
LIBS = -lm

.PHONY: all
all: flag maintest

test: all
@rm -f a.out
$(CC) $(DEBUGCFLAGS) $(LIBS) *.o
@./a.out

flag: flag.c ../vec/vector.c ../bstr/bstr.c
$(CC) $(CFLAGS) -O2 -c $^

maintest: maintest.c
$(CC) $(CFLAGS) -c $^
36 changes: 36 additions & 0 deletions flag/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Vector

Flag parser inspired by Go's flag package.

## Usage

Create a parser

```c
flag_Parser p = flag_new(argc, argv);
```

Declare a flag

```c
// long *flag_long(flag_Parser *p, char *name, long default_val, char *usage);
long *n = flag_long(&p, "n", 0, "a number");
char **user = flag_str(&p, "user", 0, "get user name");
```

Cleanup

```c
flag_free(&p);
```
---
On the terminal:
```sh
# both - and -- are accepted
$ program -n 200 --user root
```

## Dependencies
`flag` depends on the `bstr` and `vector` libraries to function.
166 changes: 166 additions & 0 deletions flag/flag.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#include "../bstr/bstr.h"
#include "../vec/vector.h"
#include <stdbool.h>
#include "flag.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// todo: parser --flag=value
// todo: find way to retrieve remaining args outside of flags

struct flag_flag {
bstr name;
bstr usage;

enum {
FLAG_BOOL,
FLAG_STR,
FLAG_LONG,
FLAG_ULONG,
FLAG_LONGLONG,
FLAG_ULONGLONG,
FLAG_FLOAT,
FLAG_DOUBLE,
} type;

union {
bool bool_flag;
char *str_flag;
long long_flag;
unsigned long ulong_flag;
long long longlong_flag;
unsigned long long ulonglong_flag;
float float_flag;
double double_flag;
};
};

flag_Parser flag_new(int argc, char *argv[]) {
return (flag_Parser){
.argc = argc,
.argv = argv,
.flags = vec_new(sizeof(struct flag_flag *)),
.parsed = false,
};
}

void flag_free(flag_Parser *p) {
for (size_t i = 0; i < vec_len(p->flags); i++) {
struct flag_flag *flag;
vec_get(p->flags, i, &flag);
free(flag);
}

vec_free(p->flags);
}

#define CREATE_FLAG(T, flag_type) \
T *flag_##T(flag_Parser *p, char *name, T default_val, char *usage) { \
struct flag_flag *flag = malloc(sizeof(*flag)); \
if (!flag) { \
return NULL; \
} \
\
*flag = (struct flag_flag){ \
.name = bstr_new(name), \
.usage = bstr_new(usage), \
.T##_flag = default_val, \
.type = (flag_type), \
}; \
\
if (!vec_push(p->flags, &flag)) { \
free(flag); \
return NULL; \
} \
\
return &flag->T##_flag; \
}

CREATE_FLAG(long, FLAG_LONG)
CREATE_FLAG(float, FLAG_FLOAT)
CREATE_FLAG(bool, FLAG_BOOL)
CREATE_FLAG(double, FLAG_DOUBLE)
typedef char *str;
CREATE_FLAG(str, FLAG_STR)
typedef long long longlong;
CREATE_FLAG(longlong, FLAG_LONGLONG)
typedef unsigned long ulong;
CREATE_FLAG(ulong, FLAG_ULONG)
typedef unsigned long long ulonglong;
CREATE_FLAG(ulonglong, FLAG_ULONGLONG)

size_t flag_nflags(flag_Parser *p) { return vec_len(p->flags); }

static bool arg_is_flag(bstr *arg) {
bstr one_dash = bstr_new("-");
bstr two_dashes = bstr_new("--");

if (bstr_startswith(*arg, two_dashes)) {
arg->cstr += 2;
arg->len -= 2;
} else if (bstr_startswith(*arg, one_dash)) {
arg->cstr += 1;
arg->len -= 1;
} else {
return false;
}

return true;
}

bool flag_parsed(flag_Parser *p) {
return p->parsed;
}

void flag_parse(flag_Parser *p) {
for (int curr_arg_idx = 0; curr_arg_idx < p->argc; curr_arg_idx++) {
bstr arg = bstr_new(p->argv[curr_arg_idx]);

if (!arg_is_flag(&arg)) {
continue;
}

struct flag_flag *curr_flag = NULL;
for (size_t j = 0; j < vec_len(p->flags); j++) {
vec_get(p->flags, j, &curr_flag);

if (bstr_equal(arg, curr_flag->name)) {
if (++curr_arg_idx >= p->argc) {
break;
}

char *flag_val = p->argv[curr_arg_idx];

switch (curr_flag->type) {
case FLAG_BOOL:
curr_flag->bool_flag = true;
break;
case FLAG_STR:
curr_flag->str_flag = flag_val;
break;
case FLAG_LONG:
curr_flag->long_flag = strtol(flag_val, NULL, 10);
break;
case FLAG_ULONG:
curr_flag->ulong_flag = strtoul(flag_val, NULL, 10);
break;
case FLAG_LONGLONG:
curr_flag->longlong_flag = strtoll(flag_val, NULL, 10);
break;
case FLAG_ULONGLONG:
curr_flag->ulonglong_flag = strtoull(flag_val, NULL, 10);
break;
case FLAG_DOUBLE:
curr_flag->double_flag = strtod(flag_val, NULL);
break;
case FLAG_FLOAT:
curr_flag->float_flag = strtof(flag_val, NULL);
break;
}
}
}
}

p->parsed = true;
}
24 changes: 24 additions & 0 deletions flag/flag.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include <stddef.h>
#include <stdbool.h>

typedef struct flag_parser {
int argc;
char **argv;
bool parsed;
struct vec_vector *flags;
} flag_Parser;

flag_Parser flag_new(int argc, char *argv[]);
void flag_free(flag_Parser *p);
size_t flag_nflags(flag_Parser *p);
void flag_parse(flag_Parser *p);
bool flag_parsed(flag_Parser *p);

long *flag_long(flag_Parser *p, char *name, long default_val, char *usage);
float *flag_float(flag_Parser *p, char *name, float default_val, char *usage);
bool *flag_bool(flag_Parser *p, char *name, bool default_val, char *usage);
double *flag_double(flag_Parser *p, char *name, double default_val, char *usage);
char **flag_str(flag_Parser *p, char *name, char *default_val, char *usage);
long long *flag_longlong(flag_Parser *p, char *name, long long default_val, char *usage);
unsigned long *flag_ulong(flag_Parser *p, char *name, unsigned long default_val, char *usage);
unsigned long long *flag_ulonglong(flag_Parser *p, char *name, unsigned long long default_val, char *usage);
27 changes: 27 additions & 0 deletions flag/maintest.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include "flag.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>

int main(void) {
char *argv[] = {"--funnyno", "69420", "-pi", "3.14159265359", "--hello", "world"};
int argc = sizeof(argv) / sizeof(argv[0]);

flag_Parser p = flag_new(argc, argv);
assert(flag_nflags(&p) == 0);
long *funny_num = flag_long(&p, "funnyno", 0, "the funny number");
char **hello = flag_str(&p, "hello", "test", "says hello");
double *pi = flag_double(&p, "pi", 0, "the number for pi");
assert(flag_nflags(&p) == 3);

assert(flag_parsed(&p) == false);
flag_parse(&p);
assert(flag_parsed(&p) == true);

assert(*funny_num == 69420);
assert(strcmp(*hello, "world") == 0);
assert(*pi == 3.14159265359);

flag_free(&p);
puts("test passed.");
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit e38e3ae

Please sign in to comment.