aboutsummaryrefslogtreecommitdiffstats
path: root/doc/interface.md
blob: 5408332a9fb4871ebe5f5dc740335533f155ee00 (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
simple-opt
==========

data types
----------

### struct simple_opt

an array of `struct simple_opt`, terminating in an element with a `type` field
equal to `SIMPLE_OPT_END`, must be passed when parsing or usage printing. the
fields which should be defined in these elements are:

```
	enum simple_opt_type type;
	const char short_name;
	const char *long_name;
	bool arg_is_required;

	/* optional, used for usage printing */
	const char *description;

	/* optional, a custom string describing the arg, used for usage printing */
	const char *custom_arg_string;
```

if `type` is `SIMPLE_OPT_FLAG`, this option may not accept arguments. if `type`
is `SIMPLE_OPT_END`, parsing and usage printing will return at this point when
iterating through the array and will not see any elements which may follow.

`short_name` is optional, and may be undefined for this option by passing '\0'.
`long_name` is also optional, and may be left undefined for this option by
passing `NULL`. however, at fewest one of these two must be defined for every
option.

the fields which are set by `simple_opt_parse` are:

```
	bool was_seen;
	bool arg_is_stored;

	union {
		bool val_bool;
		int val_int;
		unsigned val_unsigned;
		char val_string[SIMPLE_OPT_ARG_MAX_WIDTH];
	};
```

`was_seen` indicates if this option was encountered during parsing,
`arg_is_stored` if an argument was passed to the option, and the `val_<type>`
fields contain the value passed (with the correct field to set being determined
by the `type` field shown above).

options of the following types:

```
	SIMPLE_OPT_BOOL,
	SIMPLE_OPT_INT,
	SIMPLE_OPT_UNSIGNED,
	SIMPLE_OPT_STRING,
```

take arguments. if the user passes a short option on the cli, that options
argument is passed as the following cli argument, like so:

```
./a.out -x <arg_goes_here>
```

if the user passes a long option on the cli, that option's argument can be
passed either as the following cli argument or following an `=` typed at the
end of the argument, like so:

```
./a.out --opt-x <arg_goes_here>
./a.out --opt-x=<arg_goes_here>
```

arguments acceptable to type `SIMPLE_OPT_BOOL` are `true`, `yes`, or `on`, all
of which result in a value of true, and `false`, `no`, or `off`, which result
in a value of false.

arguments acceptable to type `SIMPLE_OPT_INT` must be decimal integers (that is
digit-only strings) with an optional leading sign indicator of `-` or `+`.

arguments acceptable to type `SIMPLE_OPT_UNSIGNED` must be decimal integers
(that is digit-only strings).

arguments acceptable to type `SIMPLE_OPT_STRING` may be any string of
characters the user passes.


### struct simple_opt_result

```
struct simple_opt_result {
	enum simple_opt_result_type result_type;
	enum simple_opt_type option_type;
	char option_string[SIMPLE_OPT_OPT_MAX_WIDTH];
	char argument_string[SIMPLE_OPT_OPT_ARG_MAX_WIDTH];
	int argc;
	char *argv[SIMPLE_OPT_MAX_ARGC];
};
```

`simple_opt_parse` returns a `struct simple_opt_result`. upon successful
parsing, its `result_type` field will contain `SIMPLE_OPT_RESULT_SUCCESS`.
otherwise, it will contain an error which should be handled by the caller. the
first three are user-caused errors:

```
	SIMPLE_OPT_RESULT_UNRECOGNISED_OPTION,
	SIMPLE_OPT_RESULT_BAD_ARG,
	SIMPLE_OPT_RESULT_MISSING_ARG,
```

in the case of `SIMPLE_OPT_RESULT_UNRECOGNISED_OPTION`, `option_string` will
contain the unrecognised option which was passed.

if the type is `SIMPLE_OPT_BAD_ARG`, then `option_string` will be set,
`option_type` will be set to the type of that option (`SIMPLE_OPT_BOOL` etc),
and the bad argument will be stored in `argument_string`.

if the type is `SIMPLE_OPT_MISSING_ARG`, `option_string` and `option_type` will
be set.

the remaining result types are internal errors:

```
	SIMPLE_OPT_RESULT_ARG_TOO_LONG,
	SIMPLE_OPT_RESULT_TOO_MANY_ARGS,
	SIMPLE_OPT_RESULT_MALFORMED_OPTION_STRUCT,
```

`SIMPLE_OPT_RESULT_OPT_ARG_TOO_LONG` will be returned if an option argument,
passed on the command line by a user, was too long for the internal buffer. the
internal buffer can be resized by defining `SIMPLE_OPT_OPT_MAX_WIDTH` at some
point before `simple-opt.h` is included.

`SIMPLE_OPT_RESULT_TOO_MANY_ARGS` is returned if the user passed too many
non-option arguments to the command for its internal argv filter buffer. this
limit can also be resized by defining `SIMPLE_OPT_MAX_ARGC`

finally, `SIMPLE_OPT_RESULT_MALFORMED_OPTION_STRUCT` is returned if the
programmer has passed a `struct simple_opt` array which contains disallowed
option configurations (that is, two options share a `short_name` or
`long_name`, an option has neither a `short_name` nor a `long_name`, or an
option of type `SIMPLE_OPT_FLAG` is marked as requiring an argument)


functions
---------

`simple-opt.h` defines two functions for external use, `simple_opt_parse` and
`simple_opt_print_usage`. any other functions are prefixed `sub_simple_opt` and
should not be called directly (their visibility is a by-product of simple-opt's
single header file nature)


### simple_opt_parse

`simple_opt_parse` takes three arguments and returns a `struct
simple_opt_result`:

```
static struct simple_opt_result simple_opt_parse(int argc, char **argv,
		struct simple_opt *options);
```

`argc` is the number of arguments contained in `argv`, and `argv` is an array
of character string pointers. normally here the programmer would just pass on
the `argc` and `argv` received as arguments from the `main` function.

`struct simple_opt` is an array of the options available to be parsed
(described above) and `struct simple_opt_result` contains a set of results
about that parsing (also described above).


### simple_opt_print_usage

`simple_opt_print_usage` takes six arguments and prints a neatly-formatted
usage message, similar to those typical of GNU cli commands:

```
static void simple_opt_print_usage(FILE *f, unsigned width, char *usage_name,
		char *usage_options, char *usage_summary, struct simple_opt *options);
```

`f` is a file pointer to which the message should be printed

`width` is the column width to which the output should be word-wrapped (passing
0 disables wrapping). a reasonable value here would be 80, but this could also
be used to allow more dynamic behaviour (e.g. using something like `ncurses` or
`ioctl` to get the users's current terminal width)

`usage_name` is the name of the command as it will be printed in the usage
statement. easiest is just to pass `argv[0]` here.

`usage_options` is a summary of what options the command takes (e.g. something
like `[OPTION]...`)

together, these two result in something that looks like:

```
Usage: ./a.out [OPTION]...
```

if both `usage_name` and `usage_options` are `NULL`, this initial line will not
be printed, allowing more flexibility to the programmer (if you wanted to, for
example, print multiple such lines on your own in order to represent different
use-cases)

`usage_summary` is usually a one or two sentence overview summary of how the
command behaves. if this is left as `NULL`, no summary will be printed.

the final argument, `struct simple_opt *options`, is an array of options as
defined above.

*note:* usage printing's word wrap operates under the assumptions that your
language delimits words with spaces (i.e. "when i was a child..." vs.
"子供時代に..."), that the font used is fixed-width, and that every character
occupies one column (that is, there are no wide characters, combining
diacritics, etc) and one byte (no multi-byte utf-8 characters). if these
assumptions do not apply to your use case, you should use an alternative method
for usage printing.