aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
blob: 3356e992526b6db96cfd2bd71ce4c814a82e75ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
simple-opt
==========

[simple-opt.h](src/simple-opt.h) is a single header file which implements a
simple, flexible, and portable version of command line option parsing for
programs written in C. it is designed to be (hopefully) intuitive while also
being (hopefully) more powerful than traditional getopt or similar. it has no
dependencies outside the standard library and is C99 compatible.

what follows is a simple example usage. refer to
[interface.md](doc/interface.md) for more detail.


example
-------

the following example file is available as [example.c](doc/example.c), which
you can compile and test with yourself.

```C
#include "../src/simple-opt.h"

int main(int argc, char **argv)
{
	/* array containing all options and their types / attributes */
	struct simple_opt options[] = {
		{ SIMPLE_OPT_FLAG, 'h', "help", false,
			"print this help message and exit" },
		{ SIMPLE_OPT_INT, '\0', "int", true,
			"this thing needs an integer!" },
		{ SIMPLE_OPT_UNSIGNED, 'u', "uns", true,
			"this one has a custom_arg_string. normally it would say" 
				" \"UNSIGNED\" rather than \"NON-NEG-INT\"",
			"NON-NEG-INT" },
		{ SIMPLE_OPT_STRING, 's', NULL, true,
			"this one doesn't have a long opt version" },
		{ SIMPLE_OPT_BOOL, 'b', "bool", false,
			"(optionally) takes a boolean arg!" },
		{ SIMPLE_OPT_END },
	};

	/* contains an enum for identifying simple_opt_parse's return value, an
	 * array of the cli arguments which were not parsed as options, and
	 * information relevant for error handling */
	struct simple_opt_result result;

	int i;

	result = simple_opt_parse(argc, argv, options);

	/* handle errors */
	switch (result.result_type) {
	case SIMPLE_OPT_RESULT_UNRECOGNISED_OPTION:
		fprintf(stderr, "err: unrecognised option `%s`\n",
				result.option_string);
		return 1;

	case SIMPLE_OPT_RESULT_BAD_ARG:
		fprintf(stderr, "err: bad argument `%s` passed to option `%s`\n",
				result.argument_string, result.option_string);
		return 1;

	case SIMPLE_OPT_RESULT_MISSING_ARG:
		fprintf(stderr, "err: argument expected for option `%s`\n",
				result.option_string);
		return 1;

	case SIMPLE_OPT_RESULT_OPT_ARG_TOO_LONG:
		fprintf(stderr, "internal err: argument passed to option `%s` is too long\n",
				result.option_string);
		return 1;

	case SIMPLE_OPT_RESULT_TOO_MANY_ARGS:
		fprintf(stderr, "internal err: too many cli arguments passed\n");
		return 1;

	case SIMPLE_OPT_RESULT_MALFORMED_OPTION_STRUCT:
		fprintf(stderr, "internal err: malformed option struct\n");
		return 1;

	default:
		break;
	}

	/* if the help flag was passed, print usage */
	if (options[0].was_seen) {
		simple_opt_print_usage(stdout, 80, argv[0],
				"[OPTION]... [--] [NON-OPTION]...",
				"This is where you would put an overview description of the "
				"program and it's general functionality.", options);
		return 0;
	}

	/* print a summary of options passed */
	for (i = 0; options[i].type != SIMPLE_OPT_END; i++) {
		if (options[i].long_name != NULL)
			printf("--%s, ", options[i].long_name);
		else
			printf("-%c, ", options[i].short_name);

		printf("seen: %s", (options[i].was_seen ? "yes" : "no"));

		if (options[i].arg_is_stored) {
			switch (options[i].type) {
			case SIMPLE_OPT_INT:
				printf(", val: %d", options[i].val_int);
				break;

			case SIMPLE_OPT_UNSIGNED:
				printf(", val: %u", options[i].val_unsigned);
				break;

			case SIMPLE_OPT_STRING:
				printf(", val: %s", options[i].val_string);
				break;

			case SIMPLE_OPT_BOOL:
				printf(", val: %s", options[i].val_bool ? "true" : "false");
				break;
				
			default:
				break;
			}
		}

		puts("");
	}

	/* if any non-option arguments were passed, print them */
	if (result.argc > 0) {
		printf("\nnon-options:", result.argc);

		for (i = 0; i < result.argc; i++)
			printf(" %s", result.argv[i]);

		puts("");
	}

	return 0;
}
```

options are stored in an array of `struct simple_opt`, which contains fields
for the option's type, an (optional) short option alias, an (optional) long
option alias, a boolean indicating whether this option's argument is required
or optional, an (optional) string describing what the option does, and an
(optional) string describing how the option's argument type should be printed
by `simple_opt_print_usage`. the end of the array must be indicated with an
option of type `SIMPLE_OPT_END`.

this array is passed to `simple_opt_parse` and, if the parsing is successful,
relevant values (`was_seen`, `arg_is_stored`, `val_<type>`) are in-place stored
in the options. otherwise, the returned `struct simple_opt_result` will have a
type other than `SIMPLE_OPT_RESULT_SUCCESS`, in which case error reporting
occurs.

```
$ ./a.out -y
err: unrecognised option `-y`
```

```
$ ./a.out --int
err: argument expected for option `--int`
```

```
$ ./a.out --bool fake
err: bad argument `fake` passed to option `--bool`
```

if one of the options passed is the help flag (`-h` or `--help`), this example
uses `simple_opt_print_usage` to print out a neatly-formatted usage message
(using the description strings stored in the array earlier). that message looks
like this:

```
$ ./a.out --help
Usage: ./a.out [OPTION]... [--] [NON-OPTION]...

  This is where you would put an overview description of the program and it's
  general functionality.

  -h --help            print this help message and exit
     --int=INT         this thing needs an integer!
  -u --uns=NON-NEG-INT this one has a custom_arg_string. normally it would say
                       "UNSIGNED" rather than "NON-NEG-INT"
  -s STRING            this one doesn't have a long opt version
  -b --bool[=BOOL]     (optionally) takes a boolean arg!
```

note that the output is word-wrapped. it wraps to a maximum of 80 columns
because 80 is passed as the second argument to `simple_opt_print_usage`
(allowing printing to adapt to the user's terminal width if you want to add
support for that, via ncurses or something).

finally, if parsing was successful and usage not printed, this program prints a
quick summary of which options it accepts, which it saw, and what arguments
were passed to those options, and what non-option arguments were passed to the
command itself:

```
$ ./a.out 
--help, seen: no
--int, seen: no
--uns, seen: no
-s, seen: no
--bool, seen: no
```

```
$ ./a.out --int=-1 -s test_string -b -- trailing args passed
--help, seen: no
--int, seen: yes, val: -1
--uns, seen: no
-s, seen: yes, val: test_string
--bool, seen: yes

non-options: trailing args passed
```

```
$ ./a.out --bool=false -u 3
--help, seen: no
--int, seen: no
--uns, seen: yes, val: 3
-s, seen: no
--bool, seen: yes, val: false
```

```
$ ./a.out no options, just arguments
--help, seen: no
--int, seen: no
--uns, seen: no
-s, seen: no
--bool, seen: no

non-options: no options, just arguments
```

```
$ ./a.out non-options --int=+1 can -b on be --uns 0 interleaved
--help, seen: no
--int, seen: yes, val: 1
--uns, seen: yes, val: 0
-s, seen: no
--bool, seen: yes, val: true

non-options: non-options can be interleaved
```