libk  Check-in [927371b674]

Overview
Comment:add usage display for parameters and command line switches for kcli_set, the structure used to define command line syntax for the parser; add more string & buffer functions
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 927371b67487d7546aab169d03e001d0568918f1d94d0f0cf34caf19e7b4f2b0
User & Date: lexi on 2019-10-31 03:44:25
Other Links: manifest | tags
Context
2019-11-01
06:28
Mostly fix NixOS build. Docs are still broken. The rest of the problems, mostly from small changes since NixOS was last tested, are fixed. check-in: 009b0289f2 user: glowpelt tags: trunk
06:27
remove nonexistant --normalize option check-in: 0ed5f80174 user: lexi tags: trunk
2019-10-31
03:44
add usage display for parameters and command line switches for kcli_set, the structure used to define command line syntax for the parser; add more string & buffer functions check-in: 927371b674 user: lexi tags: trunk
2019-10-30
07:44
factor out write buffer code so any module and libk users can call it; update documentation to match; add kssz string length function check-in: 8d6b36fcac user: lexi tags: trunk
Changes

Modified mod/kcli/cli.h from [a50dc5f1ed] to [5126e2cc55].

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
46
47
48
49
50
51
52

53
54
55


56
57
58
59
60
61
62
..
68
69
70
71
72
73
74

75
76
77
78
79
80
81
#include <k/io.h>
#include <k/type.h>
#include <k/internal.egroup.h>

/* input types */

typedef struct kcli_opt {
	codepoint id;
	const char* name;
	enum kcli_opt_kind {
		kcli_opt_none /* flag is disabled and hidden */,
		kcli_opt_string /* flag takes a string argument */,
		kcli_opt_flag /* on if present, off if absent */,
		kcli_opt_toggle /* off by default, value toggles
		                 * for each occurrence */,
................................................................................
		kcli_param_int = 0x40,
			kcli_param_oct = kcli_param_int | 8,
			kcli_param_dec = kcli_param_int | 10,
			kcli_param_hex = kcli_param_int | 16,
	} kind;
	enum kcli_rule {
		kcli_rule_forbidden,

		kcli_rule_optional,
		kcli_rule_required,
	} rule;


} kcli_param;

typedef struct kcli_set {
	const char*       name;
	const char*       version;
	const char**      args;   sz argc;
	const char*       desc;
................................................................................

typedef enum kcli_cond {
	kcli_cond_ok = kcli_cond_id,
	kcli_cond_extra /* parse succeded, but arguments or
	                   flags were left over */,
	kcli_cond_fail /* unspecified error */,
	kcli_cond_parse /* bad syntax, parse failed */,

} kcli_cond;

typedef enum kcli_flag {
	kcli_flag_off = 0,
	kcli_flag_on = 1,
} kcli_flag;








|







 







>

|

>
>







 







>







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
..
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <k/io.h>
#include <k/type.h>
#include <k/internal.egroup.h>

/* input types */

typedef struct kcli_opt {
	rune id;
	const char* name;
	enum kcli_opt_kind {
		kcli_opt_none /* flag is disabled and hidden */,
		kcli_opt_string /* flag takes a string argument */,
		kcli_opt_flag /* on if present, off if absent */,
		kcli_opt_toggle /* off by default, value toggles
		                 * for each occurrence */,
................................................................................
		kcli_param_int = 0x40,
			kcli_param_oct = kcli_param_int | 8,
			kcli_param_dec = kcli_param_int | 10,
			kcli_param_hex = kcli_param_int | 16,
	} kind;
	enum kcli_rule {
		kcli_rule_forbidden,
		kcli_rule_required,
		kcli_rule_optional,
		kcli_rule_overflow,
	} rule;
	void* val;
	const char* desc;
} kcli_param;

typedef struct kcli_set {
	const char*       name;
	const char*       version;
	const char**      args;   sz argc;
	const char*       desc;
................................................................................

typedef enum kcli_cond {
	kcli_cond_ok = kcli_cond_id,
	kcli_cond_extra /* parse succeded, but arguments or
	                   flags were left over */,
	kcli_cond_fail /* unspecified error */,
	kcli_cond_parse /* bad syntax, parse failed */,
	kcli_cond_overlong /* a string in the configuration structures is longer than allowed */,
} kcli_cond;

typedef enum kcli_flag {
	kcli_flag_off = 0,
	kcli_flag_on = 1,
} kcli_flag;

Modified mod/kcli/kcli.md from [6a6012e12f] to [0390b41ce1].

84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

		return 0;
	}

## struct kcli_opt
a `kcli_opt` is a representation of a command-line flag and its function. each option must have a unique `id` and/or a unique `name`.

 * `char id` - the short single-character form of the flag (or NUL for no short form)
 * `const char* name` - the long string form of the flag (or NULL for no long form)
 * `kcli_opt_kind kind` - enum that describes how the flag will function
 * `void* val` - a pointer to an appropriate type to store the return value in.
 * `const char* desc` - a description of the flag's purpose and function (or NULL for no description)

## enum kcli_opt_kind








|







84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

		return 0;
	}

## struct kcli_opt
a `kcli_opt` is a representation of a command-line flag and its function. each option must have a unique `id` and/or a unique `name`.

 * `rune id` - the short single-character form of the flag as a UTF-8 codepoint (or initial NUL for no short form)
 * `const char* name` - the long string form of the flag (or NULL for no long form)
 * `kcli_opt_kind kind` - enum that describes how the flag will function
 * `void* val` - a pointer to an appropriate type to store the return value in.
 * `const char* desc` - a description of the flag's purpose and function (or NULL for no description)

## enum kcli_opt_kind

Modified mod/kcli/testbin.exe.c from [9c55c30fa9] to [92e78f7400].

1
2
3
4
5





















6
7
8


9
10
11
12
13
14
#include <k/core.h>
#include <k/cli.h>

stat
entry (kenv e) {





















	kcli_set testbin = {
		"testbin", "1.0.0", e.args, e.argc,
		"this is a test of the kcli module",


	};

	kcond c = kcli_usage(testbin, e.err);

	return c;
}





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



>
>






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
#include <k/core.h>
#include <k/cli.h>

stat
entry (kenv e) {
	kcli_flag verbose = false,
	          debug = false;
	
	kcli_opt options[] = {
		{ "v", "verbose", kcli_opt_flag, &verbose,
		  "induce information overload" },
		{ "d", "debug-mode", kcli_opt_flag, &debug,
		  "run the program in debug mode"},
	};

	const char* perp, * place, * weapon;

	kcli_param params[] = {
		{"perp", kcli_param_string, kcli_rule_required, &perp,
		 "the one who did the dastardly deed"},
		{"place", kcli_param_string, kcli_rule_required, &place,
		 "where the crime was committed"},
		{"murder weapon", kcli_param_string, kcli_rule_optional, &weapon,
		 "the implement used to murder the victim"},
	};

	kcli_set testbin = {
		"testbin", "1.0.0", e.args, e.argc,
		"this is a test of the kcli module",
		params, Kmsz(params),
		options, Kmsz(options),
	};

	kcond c = kcli_usage(testbin, e.err);

	return c;
}

Modified mod/kcli/usage.fn.c from [09e6f94502] to [51bd833f89].


1
2
3








4
5
6



7
8
9



10
11
12


13





14
15







16
17
18
















































19
20

#include <k/cli.h>
#include <k/io.h>
#include <k/str.h>









kcond
kcli_usage(kcli_set prg, kiochan ch) {



	ubyte buf_space [sizeof(ksbuf) + 256];
	ksbuf* out = ksbufmk(buf_space, ch, 256);




	const char* msg [] = {
		prg.name, " v", prg.version, "\n\n",
		prg.desc, "\n\n",


	};





	for (sz i = 0; i != Kmsz(msg); ++ i) {
		ksraw str = { 0, msg[i] };







		kcond c = ksbufput(out, str);
	}
	
















































	return ksbufflush(out);
}
>



>
>
>
>
>
>
>
>



>
>
>



>
>
>

|

>
>

>
>
>
>
>
|
<
>
>
>
>
>
>
>
|

|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


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
#include <k/core.h>
#include <k/cli.h>
#include <k/io.h>
#include <k/str.h>

extern char* _k_internal_binary_name;

static const struct { const char* front, * back; } param_styles [] = {
	[kcli_rule_required] = { "<",">" },
	[kcli_rule_optional] = { "[","]" },
	[kcli_rule_overflow] = { "","..." },
};

kcond
kcli_usage(kcli_set prg, kiochan ch) {
	enum { max_name_len = 32 };

	kcond c;
	ubyte buf_space [sizeof(ksbuf) + 256];
	ksbuf* out = ksbufmk(buf_space, ch, 256);

	const char* name = prg.name != null ?
		prg.name : _k_internal_binary_name;

	const char* msg [] = {
		name, " v", prg.version, "\n\n",
		prg.desc, "\n\n",
		"usage: ", _k_internal_binary_name,
		prg.optc == 0 ? null : " [-",
	};

	if (!kokay(c = ksbufwrite(out, msg))) return c;

	u8 longest_opt = 0;
	u8 opt_lens [prg.optc];
	for (sz i = 0; i != prg.optc; ++ i) {

		if (prg.opts[i].id[0] == 0) continue;

		opt_lens[i] = kssz(prg.opts[i].name, max_name_len);
		if (opt_lens[i] > longest_opt)
			longest_opt = opt_lens[i];

		ksraw str = { kssz(prg.opts[i].id, 4), prg.opts[i].id };
		if (!kokay(c = ksbufput(out, str))) return c;
	}

	if (!kokay(c = ksbufput(out, Ksraw("]")))) return c;
	
	u8 longest_param = 0;
	u8 param_lens [prg.paramc];
	for (sz i = 0; i != prg.paramc; ++ i) {
		enum kcli_rule r = prg.params[i].rule;

		param_lens[i] = kssz(prg.params[i].name, max_name_len);
		if (param_lens[i] > longest_param)
			longest_param = param_lens[i];

		const char* param[] = {
			" ", param_styles[r].front, prg.params[i].name,
			param_styles[r].back, null
		};

		if (!kokay(c = ksbufwrite(out,param))) return c;
	}

	if (!kokay(c = ksbufput(out, Ksraw("\n")))) return c;

	const char spacing [] = "                                ";
	Kassert(sizeof spacing - 1 == max_name_len,
			"spacing string not equal in length to max_name_len");
	/* yes, yes, i know */

	for (sz i = 0; i != prg.paramc; ++ i) {
		u8 pad = (longest_param - param_lens[i]) + 1;
		const char* tpl [] = {
			"\n    ", spacing + (sizeof spacing - pad),
			prg.params[i].name, ": ", prg.params[i].desc, null
		};

		if (!kokay(c = ksbufwrite(out, tpl))) return c;
	}

	if (!kokay(c = ksbufput(out, (ksraw){1, "\n"}))) return c;

	for (sz i = 0; i != prg.optc; ++ i) {
		u8 pad = (longest_opt - opt_lens[i]) + 1;
		const char* tpl [] = {
			"\n    -", prg.opts[i].id, ", --", prg.opts[i].name,
			spacing + (sizeof spacing - pad), ": ", prg.opts[i].desc, null
		};

		if (!kokay(c = ksbufwrite(out, tpl))) return c;
	}

	return ksbufflush(out);
}

Modified mod/kcore/boot.rt.c from [e3e3209cc1] to [a8282db34a].

1
2
3
4


5
6
7
8


9
10
11
12
13
14
15
#include <k/core.h>
#include <k/type.h>
extern stat entry(kenv);



unsigned long long
_boot(unsigned int argc, /* argument count */
		const char** argv, /* arguments */
		char** envp /* environment */ ) {



	envp ++; /* envp seems to point at a leading null;
				this is probably a sign of breakage but
				i don't know what else to do about it for
				the moment. */

	char** ep;




>
>




>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <k/core.h>
#include <k/type.h>
extern stat entry(kenv);

const char* _k_internal_binary_name;

unsigned long long
_boot(unsigned int argc, /* argument count */
		const char** argv, /* arguments */
		char** envp /* environment */ ) {

	_k_internal_binary_name = argv[0];

	envp ++; /* envp seems to point at a leading null;
				this is probably a sign of breakage but
				i don't know what else to do about it for
				the moment. */

	char** ep;

Added mod/kstr/bufwrite.fn.c version [564ff21d85].





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <k/type.h>
#include <k/str.h>
#include <k/io.h>

kcond
ksbufwrite(ksbuf* out, const char** array) {
	for (sz i=0; array[i] != null; ++ i) {
		ksraw str = { 0, array[i] };
		kcond c = ksbufput(out, str);
		if (!kokay(c)) return c;
	}

	return kscond_ok;
}

Added mod/kstr/emitc.fn.c version [0654040df9].





























>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <k/type.h>
#include <k/str.h>
#include <k/io.h>

kcond
ksemitc(const char** array, sz bufsz, kiochan channel) {
	ubyte cache [sizeof(ksbuf) + bufsz];
	ksbuf* out = ksbufmk(cache, channel, bufsz);

	kcond c = ksbufwrite(out, array);
	if (!kokay(c)) return c;

	return ksbufflush(out);
}

Modified mod/kstr/kstr.md from [7f4b99ca6c] to [0b0860371c].

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
..
76
77
78
79
80
81
82






83
84
85
86

## ksbufmk
`ksbuf* ksbufmk(void* where, kiochan channel, sz run)` initializes a new buffer at the specified address. `run` should be equivalent to the full length of the memory region minus `sizeof(struct ksbuf)` - in other words, the size of the string the `ksbuf` can hold. memory should be allocated by the user, either by creating an array on the stack or with `kmem` allocation functions. `ksbufmk()` returns a pointer to the new structure. the return value will always point to the same point in memory as `where`, but will be of the appropriate type to pass to buffer functions.

## ksbufput
`kcond ksbufput(ksbuf* b, ksraw str)` copies a string into a buffer with `kscp`. flushing it as necessary.

# ksbufflush
`kcond ksbufflush(ksbuf* b)` flushes a buffer to its assigned channel, emptying it and readying it for another write cycle. a buffer should almost always be flushed before it goes out of scope or is deallocated.

## kscomp
`char* kscomp(size_t ct, ksraw struct[], kmbuf* buf)` is a **string composition** function. it serves as an efficient, generalized replacement for functions like `strcat` and `strdup`.

to use kscomp, create an array of `kstr` and fill it with the strings you wish to concatenate. for example, to programmatically generate an HTML link tag, you might use the following code.

................................................................................
			ksref(text),
		Kstr("</a>")
	};
	char* html = kscomp(Kmsz(chain), chain, &buf);

kscomp will only calculate the length of individual strings if they are not already known. when it needs to calculate the length of a string, it will store that length in the original array so repeated calls can be made without needing to repeatedly calculate the lengths. this is not always desirable, so the variant `kscompc` exists, which is exactly the same as `kscomp` in every respect except that `chain` is not altered in any way.







## macros
if `KFclean` is not set when <k/str.h> is included, the following macros are defined.

 * `Kstr(string)` - the compile-time equivalent to `kstr()`. `Kstr` takes a literal string and inserts the text `{ sizeof (string), string }` into the document, suitable for initializing a kstr.







|







 







>
>
>
>
>
>
|


|
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
..
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

## ksbufmk
`ksbuf* ksbufmk(void* where, kiochan channel, sz run)` initializes a new buffer at the specified address. `run` should be equivalent to the full length of the memory region minus `sizeof(struct ksbuf)` - in other words, the size of the string the `ksbuf` can hold. memory should be allocated by the user, either by creating an array on the stack or with `kmem` allocation functions. `ksbufmk()` returns a pointer to the new structure. the return value will always point to the same point in memory as `where`, but will be of the appropriate type to pass to buffer functions.

## ksbufput
`kcond ksbufput(ksbuf* b, ksraw str)` copies a string into a buffer with `kscp`. flushing it as necessary.

## ksbufflush
`kcond ksbufflush(ksbuf* b)` flushes a buffer to its assigned channel, emptying it and readying it for another write cycle. a buffer should almost always be flushed before it goes out of scope or is deallocated.

## kscomp
`char* kscomp(size_t ct, ksraw struct[], kmbuf* buf)` is a **string composition** function. it serves as an efficient, generalized replacement for functions like `strcat` and `strdup`.

to use kscomp, create an array of `kstr` and fill it with the strings you wish to concatenate. for example, to programmatically generate an HTML link tag, you might use the following code.

................................................................................
			ksref(text),
		Kstr("</a>")
	};
	char* html = kscomp(Kmsz(chain), chain, &buf);

kscomp will only calculate the length of individual strings if they are not already known. when it needs to calculate the length of a string, it will store that length in the original array so repeated calls can be made without needing to repeatedly calculate the lengths. this is not always desirable, so the variant `kscompc` exists, which is exactly the same as `kscomp` in every respect except that `chain` is not altered in any way.

## ksemit
`ksemit(sz len, ksraw* array, kiochan channel)` takes a `len`-length `array` of `ksraw`s and prints them to a channel. a buffer will be allocated based on the total length of the strings to avoid unnecessary write calls.

## ksemitc
`ksemitc(const char** array, sz bufsz, kiochan channel)` takes a null-terminated `array` of NUL-terminated strings and buffer-prints them to a channel. `bufsz` controls the size of the buffer used, and should be as close as possible to the size of the strings emitted. the buffer will be kept on the stack, so no memory management or cleanup is necessary.

# macros
if `KFclean` is not set when <k/str.h> is included, the following macros are defined.

 * `Kstr(string)` - the compile-time equivalent to `kstr()`. `Kstr` takes a literal string and inserts the text `{ sizeof (string) - 1, string }` into the document, suitable for initializing a kstr.

Modified mod/kstr/str.h from [18dd12a221] to [48272504c4].

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
..
75
76
77
78
79
80
81
82
83
84


85
86
87
88
89
90
#endif

typedef struct ksraw {
	sz size;
	const char* ptr;
} ksraw;



typedef struct ksmut {
	sz size;
	char* ptr;
} ksmut;

sz kssz(const char* str, sz max);

#include <k/mem.h>

typedef struct kstr {
	sz size;
	kmptr ptr;
} kstr;


#include <k/io.h>

#include <k/internal.egroup.h>
typedef enum kscond {
	kscond_ok = kscond_id,
	kscond_partial,
................................................................................

kscond kscp(ksraw str, ksmut dest, sz* len);

ksbuf* ksbufmk(void* where, kiochan channel, sz run);

#include <k/core.h>

kcond ksbufput(ksbuf*, ksraw);

kiocond ksbufflush(ksbuf*);



#ifdef __cplusplus
}
#endif

#endif







>
>













>







 







|
<

>
>






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
..
78
79
80
81
82
83
84
85

86
87
88
89
90
91
92
93
94
#endif

typedef struct ksraw {
	sz size;
	const char* ptr;
} ksraw;

#define Ksraw(str) ((ksraw){sizeof(str),str})

typedef struct ksmut {
	sz size;
	char* ptr;
} ksmut;

sz kssz(const char* str, sz max);

#include <k/mem.h>

typedef struct kstr {
	sz size;
	kmptr ptr;
} kstr;


#include <k/io.h>

#include <k/internal.egroup.h>
typedef enum kscond {
	kscond_ok = kscond_id,
	kscond_partial,
................................................................................

kscond kscp(ksraw str, ksmut dest, sz* len);

ksbuf* ksbufmk(void* where, kiochan channel, sz run);

#include <k/core.h>

kcond   ksbufput(ksbuf*, ksraw);

kiocond ksbufflush(ksbuf*);
kcond   ksbufwrite(ksbuf*, const char** array);
kcond   ksemitc(const char** array, sz bufsz, kiochan channel);

#ifdef __cplusplus
}
#endif

#endif