libk  Check-in [c0e04b9015]

Overview
Comment:begin work on kcli module; continue to build out infra for error explanation function
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: c0e04b901565b65b6b949db7cab9ebed04da7705fb44af9b3f5b4e627ec4836f
User & Date: lexi on 2019-10-30 03:34:34
Other Links: manifest | tags
Context
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
03:34
begin work on kcli module; continue to build out infra for error explanation function check-in: c0e04b9015 user: lexi tags: trunk
2019-10-23
10:05
continue work on plans for kconf module check-in: 623e0fdd96 user: lexi tags: trunk
Changes

Modified build.sh from [1a5246e64d] to [4e982ff144].

    21     21   if test "$1" = "-C"; then
    22     22   	say "precleaning repo"
    23     23   	./clean.sh
    24     24   fi
    25     25   
    26     26   # TODO: make it possible for user to change
    27     27   #       default set with environment vars
    28         -modules=(kcore kmem kstr kio kgraft kfile)
           28  +modules=(kcore kmem kstr kio kgraft kfile kcli)
    29     29   
    30     30   # compose an arch tuple. this is used in
    31     31   # places, mostly to select the correct
    32     32   # version of assembly files for a given
    33     33   # platform (each has an arch tuple in its
    34     34   # name, following the linkage specifier)
    35     35   target=$arch.$os

Modified global/build-manpage.sh from [132a7e50b4] to [af11c2c865].

    42     42   	descline=$(echo "$dhead" | cut -d: -f1)
    43     43   	offset=1
    44     44   else
    45     45   	descline=$(grep -m2 -n "^#" "$file" | tail -n1 | cut -d: -f1)
    46     46   	offset=0
    47     47   fi
    48     48   
    49         -tail -n+2 $file | head -n$(expr $descline - 2) | cmark -t man >>"$fmt"
           49  +tail -n+2 $file | head -n$(expr $descline - 2) | cmark --smart -t man >>"$fmt"
    50     50   
    51     51   echo >>"$fmt" ".SH DESCRIPTION"
    52     52   
    53         -tail -n+$(expr $descline + $offset) "$file"  | cmark -t man >> "$fmt"
           53  +tail -n+$(expr $descline + $offset) "$file"  | cmark --smart --normalize -t man >> "$fmt"
    54     54   
    55     55   test "$doc_html" = "yes" && {
    56     56   	mkdir -p "$htmldest"
    57     57   	groff -Thtml -Kutf8 -m man "$fmt" > "$html"
    58     58   	test "$verbose" != "loud" ||
    59     59   	say "wrote html page for $stem to $html"
    60     60   }

Added mod/kcli/cli.h version [a50dc5f1ed].

            1  +#ifndef KIcli
            2  +#define KIcli
            3  +
            4  +#include <k/core.h>
            5  +#include <k/io.h>
            6  +#include <k/type.h>
            7  +#include <k/internal.egroup.h>
            8  +
            9  +/* input types */
           10  +
           11  +typedef struct kcli_opt {
           12  +	codepoint id;
           13  +	const char* name;
           14  +	enum kcli_opt_kind {
           15  +		kcli_opt_none /* flag is disabled and hidden */,
           16  +		kcli_opt_string /* flag takes a string argument */,
           17  +		kcli_opt_flag /* on if present, off if absent */,
           18  +		kcli_opt_toggle /* off by default, value toggles
           19  +		                 * for each occurrence */,
           20  +		kcli_opt_accumulate /* u8, value increases for each
           21  +		                     * occurrence, saturates at 255 */,
           22  +		kcli_opt_enum /* matched against a table of string
           23  +		               * values to retrieve the corresponding
           24  +					   * integer ID */,
           25  +
           26  +		kcli_opt_int = 0x40,
           27  +		/* kcli_opt_int takes a default base parameter.
           28  +		 * for instance, to take a decimal number, you
           29  +		 * might write `kcli_opt_int | 10.` if the
           30  +		 * argument takes a base prefix, the default
           31  +		 * base will be disregarded. */
           32  +			kcli_opt_oct = kcli_opt_int | 8,
           33  +			kcli_opt_dec = kcli_opt_int | 10,
           34  +			kcli_opt_hex = kcli_opt_int | 16,
           35  +	} kind;
           36  +	void* val;
           37  +	const char* desc;
           38  +} kcli_opt;
           39  +
           40  +typedef struct kcli_param {
           41  +	const char* name;
           42  +	enum kcli_param_kind {
           43  +		kcli_param_none,
           44  +		kcli_param_string,
           45  +		kcli_param_enum,
           46  +		kcli_param_int = 0x40,
           47  +			kcli_param_oct = kcli_param_int | 8,
           48  +			kcli_param_dec = kcli_param_int | 10,
           49  +			kcli_param_hex = kcli_param_int | 16,
           50  +	} kind;
           51  +	enum kcli_rule {
           52  +		kcli_rule_forbidden,
           53  +		kcli_rule_optional,
           54  +		kcli_rule_required,
           55  +	} rule;
           56  +} kcli_param;
           57  +
           58  +typedef struct kcli_set {
           59  +	const char*       name;
           60  +	const char*       version;
           61  +	const char**      args;   sz argc;
           62  +	const char*       desc;
           63  +	const kcli_param* params; sz paramc;
           64  +	const kcli_opt*   opts;   sz optc;
           65  +} kcli_set;
           66  +
           67  +/* output types */
           68  +
           69  +typedef enum kcli_cond {
           70  +	kcli_cond_ok = kcli_cond_id,
           71  +	kcli_cond_extra /* parse succeded, but arguments or
           72  +	                   flags were left over */,
           73  +	kcli_cond_fail /* unspecified error */,
           74  +	kcli_cond_parse /* bad syntax, parse failed */,
           75  +} kcli_cond;
           76  +
           77  +typedef enum kcli_flag {
           78  +	kcli_flag_off = 0,
           79  +	kcli_flag_on = 1,
           80  +} kcli_flag;
           81  +
           82  +/* functions */
           83  +
           84  +kcond kcli_usage(kcli_set, kiochan);
           85  +kcli_cond kcli_parse(kcli_set);
           86  +
           87  +/* macros */
           88  +
           89  +#define Kcli_param(field,kind,rule,description) \
           90  +	{#field, kcli_param_##kind, kcli_rule_##rule, &field, description}
           91  +#define Kcli_opt(long,short,kind,description) \
           92  +	{short, #long, kcli_opt_##kind, &long, description}
           93  +
           94  +#endif

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

    28     28   
    29     29   ## kcli_usage(kcli_set, kiochan)
    30     30   kcli_usage() takes a `kcli_set` and prints a succinct usage summary to a [kiochan](../kio/kio.md).
    31     31   
    32     32   # types
    33     33   
    34     34   ## struct kcli_set
    35         -`kcli_set` is a struct containing information about your program, such as its name, a synopsis of its function, a pointer to `argv`, and a list of `kcli_opt`s.
           35  +`kcli_set` is a struct containing information about your program, such as its name, a synopsis of its function, a pointer to an array of arguments, and a list of `kcli_opt`s.
    36     36   
    37     37    * `const char* name` - program name (if null, will be determined from argv instead)
           38  + * `const char* version` - a string describing the program version (if null, version will not be printed)
           39  + * `const char** args` - the `argv` pointer passed to the `entry` function, representing the command-line arguments passed to the program.
    38     40    * `size_t argc` - the number of arguments in the `argv` array.
    39         - * `const char** argv` - the `argv` pointer passed to the `entry` function, representing the command-line arguments passed to the program.
    40     41    * `const char* desc` - program description
    41     42    * `const kcli_param* params` - a list of options expected by the program.
    42     43    * `size_t paramc` - the number of params in the list to process.
    43     44    * `const kcli_opt* opts` - a list of options expected by the program.
    44     45    * `size_t optc` - the number of options in the list to process.
    45     46   
    46     47   a kcli_set might be used like so:
................................................................................
    51     52       stat entry(kenv e) {
    52     53       	kcli_flag aardvark;
    53     54       	kcli_flag zebra;
    54     55       	char* user;
    55     56       	char* password;
    56     57   		long age;
    57     58   		kcli_param params[] = {
    58         -    		{ "user", kcli_param_string, kcli_class_required,
           59  +    		{ "user", kcli_param_string, kcli_rule_required,
    59     60       		  &user, "the user to log in as" }
    60     61   			  // or Kcli_param(user,string,required,"the user to log in as"),
    61     62   
    62         -			{ "age", kcli_param_dec, kcli_class_optional,
           63  +			{ "age", kcli_param_dec, kcli_rule_optional,
    63     64       		  &age, "the age of the user" }
    64     65   			  // or Kcli_param(age,dec,optional,"the age of the user"),
    65     66   		};
    66     67       	kcli_opt options[] = {
    67     68       		{ 'a', "aardvark", kcli_opt_flag, &aardvark,
    68     69       		  "a nocturnal burrowing mammal" },
    69     70   			  // or Kcli_opt(aardvark, 'a', flag, "a nocturnal burrowing mammal")
    70     71       		{ 'z', "zebra", kcli_opt_flag, &zebra,
    71     72       		  "a striped equine" },
    72     73       		{ 'p', "password", kcli_opt_string, &password,
    73     74       		  "the password to log in with" }
    74     75       	};
    75     76       	kcli_set argset = {
    76         -    		"demo", e.argc, e.argv,
           77  +    		"demo", e.args, e.argc,
    77     78       		"a demonstration of the kcli_set type",
    78     79   			params, Kmsz(params),
    79     80   			options, Kmsz(options)
    80     81       	},
    81         -		size_t args_parsed = kcli_parse(&argset);
    82         -		if (args_parsed == 0) { kcli_usage(&me, e.err); return 1; }
           82  +		size_t args_parsed = kcli_parse(argset);
           83  +		if (args_parsed == 0) { kcli_usage(me, e.err); return 1; }
    83     84   
    84     85   		return 0;
    85     86   	}
    86     87   
    87         -### struct kcli_opt
           88  +## struct kcli_opt
    88     89   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`.
    89     90   
    90     91    * `char id` - the short single-character form of the flag (or NUL for no short form)
    91     92    * `const char* name` - the long string form of the flag (or NULL for no long form)
    92     93    * `kcli_opt_kind kind` - enum that describes how the flag will function
    93     94    * `void* val` - a pointer to an appropriate type to store the return value in.
    94     95    * `const char* desc` - a description of the flag's purpose and function (or NULL for no description)
    95     96   
    96         -#### enum kcli_opt_kind
           97  +## enum kcli_opt_kind
    97     98   
    98     99    * `kcli_opt_none` - flag is disabled and will not be shown in usage
    99    100    * `kcli_opt_string` - flag tells kcli to add a string to the list of expected parameters; appropriate string will be returned
          101  + * `kcli_opt_int` - flag tells kcli to add an integer to the last of expected parameters; must be bitwise-OR'd with default base
   100    102    * `kcli_opt_oct` - flag tells kcli to add an octal number to the list of expected parameters
   101    103    * `kcli_opt_dec` - flag tells kcli to add a decimal number to the list of expected parameters
   102    104    * `kcli_opt_hex` - flag tells kcli to add a hexdecimal number to the list of expected parameters
   103    105    * `kcli_opt_flag` - flag is an option: will return `kcli_flag_on` if entered at least once, `kcli_flag_off` otherwise.
   104    106    * `kcli_opt_toggle` - flag toggles value on and off: will return `kcli_flag_on` if entered an odd number of times, `kcli_flag_off` otherwise.
   105    107    * `kcli_opt_accumulate` - flag increments a value every time it is entered; often used to implement `-v (--verbose)`-style options (e.g. `-vvvv` would return a value of `4`).
   106    108    * `kcli_opt_enum` - flag is one of a series of enumerated values, which will be matched against a table to yield the associated integer.
   107    109   
   108         -### struct kcli_param
          110  +## struct kcli_param
   109    111   `kcli_param` describes a parameter that may be passed to the program whether or not any flags are passed.
   110    112   
   111    113    * `const char* name` - a short name for the parameter
   112    114    * `kcli_param_kind kind` - the kind of parameter passed
   113         - * `kcli_class class` - whether or not the parameter is optional
          115  + * `kcli_rule rule` - whether or not the parameter is optional
   114    116    * `void* val` - a pointer to an appropriate type of variable to fill
   115    117    * `const char* desc` - a description of the parameter's function
   116    118   
   117         -#### enum kcli_param_kind
          119  +### enum kcli_param_kind
   118    120    * `kcli_param_none` - parameter is disabled and will not be expected or accepted
   119    121    * `kcli_param_string` - parameter will not be parsed; a raw string will be returned
          122  + * `kcli_param_enum` - parameter will be matched against an enumeration table
          123  + * `kcli_param_int` - parameter will be parsed as an integer; must be bitwise-OR'd with default base
   120    124    * `kcli_param_oct` - parameter will be parsed as an octal number
   121    125    * `kcli_param_dec` - parameter will be parsed as a decimal number
   122    126    * `kcli_param_hex` - parameter will be parsed as a hexadecimal number
   123    127   
   124         -### enum kcli_class
   125         - * `kcli_class_forbidden` - parameter may not be passed
   126         - * `kcli_class_optional` - parameter may or may not be passed
   127         - * `kcli_class_required` - parameter must be passed
          128  +## enum kcli_rule
          129  + * `kcli_rule_forbidden` - parameter may not be passed
          130  + * `kcli_rule_optional` - parameter may or may not be passed
          131  + * `kcli_rule_required` - parameter must be passed
   128    132   
   129         -### enum kcli_flag
          133  +## enum kcli_flag
   130    134   results that an option of kind `kcli_opt_flag` can return.
   131    135   
   132    136    * `kcli_flag_off = 0` - flag is off
   133    137    * `kcli_flag_on = 1` - flag is on
   134    138    
   135         -## macros
          139  +# macros
   136    140   
   137         -### Kcli_param(field, kind, class, description)
          141  +## Kcli_param(field, kind, class, description)
   138    142   a convenience macro for filling out parameter lists.
   139    143   
   140    144   `Kcli_param(field,a,b,"description")` is transformed into:
   141    145   
   142         -    { "field", kcli_param_a, kcli_class_b, &field, "description" }
          146  +    { "field", kcli_param_a, kcli_rule_b, &field, "description" }
   143    147   
   144         -### Kcli_opt(field, kind, class, description)
          148  +## Kcli_opt(field, kind, class, description)
   145    149   a convenience macro for filling out option lists.
   146    150   
   147    151   `Kcli_opt(name,'n',string,"description")` is transformed into:
   148    152   
   149    153       { 'n', "name", kcli_opt_string, &name, "description" }

Added mod/kcli/parse.fn.c version [1d4ed1c3cd].

            1  +#include <k/cli.h>
            2  +
            3  +kcli_cond
            4  +kcli_parse(kcli_set prg) {
            5  +
            6  +}

Added mod/kcli/testbin.exe.c version [7d0c94d714].

            1  +#include <k/core.h>
            2  +#include <k/cli.h>
            3  +
            4  +stat
            5  +entry (kenv e) {
            6  +	kcli_set testbin = {
            7  +		"testbin", "1.0.0", e.args, e.argc,
            8  +		"this is a test of the kcli module",
            9  +	};
           10  +
           11  +	kcli_usage(testbin, e.err);
           12  +
           13  +	return 0;
           14  +}

Added mod/kcli/usage.fn.c version [2416d97ee0].

            1  +#include <k/cli.h>
            2  +#include <k/io.h>
            3  +#include <k/str.h>
            4  +
            5  +typedef struct buffer {
            6  +	char* cur;
            7  +	kiochan channel;
            8  +	sz run;
            9  +	char buf [];
           10  +} buffer;
           11  +
           12  +static buffer*
           13  +buffer_new(void* mem, kiochan channel, sz run) {
           14  +	buffer* r = mem;
           15  +	r -> cur = r -> buf;
           16  +	r -> channel = channel;
           17  +	r -> run = run;
           18  +	return r;
           19  +}
           20  +
           21  +static kiocond
           22  +buffer_flush(buffer* b) {
           23  +	ksraw str = {b -> cur - b -> buf, b -> buf};
           24  +	b -> cur = b -> buf;
           25  +	return kiosend(b -> channel, str, null);
           26  +}
           27  +
           28  +static kcond
           29  +buffer_send(buffer* b, const char* str) {
           30  +	ksmut buf = { b->run - (b->cur - b->buf), b->cur };
           31  +	ksraw src = { 0, str };
           32  +	kscond sc = kscp(src, buf, &src.size);
           33  +	if (sc != kscond_ok) return sc;
           34  +
           35  +	b->cur += src.size;
           36  +	if (b->cur >= (b->buf + b->run)) {
           37  +		return buffer_flush(b);
           38  +	} else return kiocond_ok;
           39  +}
           40  +
           41  +kcond
           42  +kcli_usage(kcli_set prg, kiochan ch) {
           43  +	ubyte buf_space [sizeof(buffer) + 256];
           44  +	buffer* out = buffer_new(buf_space, ch, 256);
           45  +
           46  +	const char* msg [] = {
           47  +		prg.name, " v", prg.version, "\n\n",
           48  +		prg.desc, "\n\n",
           49  +	};
           50  +	for (sz i = 0; i != sizeof msg / sizeof msg[0]; ++ i) {
           51  +		kcond c = buffer_send(out, msg[i]);
           52  +		if (!kokay(c)) return c;
           53  +	}
           54  +	
           55  +	return buffer_flush(out);
           56  +}

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

     1      1   #include <k/core.h>
     2      2   #include <k/type.h>
     3      3   extern stat entry(kenv);
     4      4   
     5      5   unsigned long long
     6      6   _boot(unsigned int argc, /* argument count */
     7         -		char** argv, /* arguments */
            7  +		const char** argv, /* arguments */
     8      8   		char** envp /* environment */ ) {
     9      9   
    10     10   	envp ++; /* envp seems to point at a leading null;
    11     11   				this is probably a sign of breakage but
    12     12   				i don't know what else to do about it for
    13     13   				the moment. */
    14     14   
................................................................................
    35     35   
    36     36   	kenv e = { 
    37     37   		/* TODO: determine terminal class and set term vs ansi correctly! */
    38     38   		{ {kiostream_term, 0}, {kiostream_term, 1} }, // chan std
    39     39   		{ {kiostream_closed},  {kiostream_term, 2} }, // chan err
    40     40   		argc, argv, ep - envp, variables
    41     41   	};
           42  +
    42     43   	return entry(e);
    43     44   }

Modified mod/kcore/core.h from [2582f2493d] to [4d5847ec2c].

    14     14   	ksraw val;
    15     15   	char* platform;
    16     16   } kvar;
    17     17   
    18     18   typedef struct kenv {
    19     19   	kiochan std;
    20     20   	kiochan err;
    21         -	sz argc; char** args;
           21  +	sz argc; const char** args;
    22     22   	sz varc; kvar* vars;
    23     23   } kenv;
    24     24   
    25     25   /* i'm really sorry okay */
    26     26   typedef
    27     27   #if (__STDC_VERSION__ >= 199901L)
    28     28   	_Bool bool;

Modified mod/kcore/type.h.m from [efe9d86768] to [3822f60b11].

   179    179   ”)dnl
   180    180   
   181    181   // exit status integer types - pls use kbad in <k/magic.h> instead
   182    182   ifelse(target_posix,“yes”,“
   183    183   	/* by convention, posix return values are 8-bit,
   184    184   	 * but note that many modern UNIXes do technically
   185    185   	 * support higher-bit values. for this reason,
   186         -	 * longstat is defined differently under posix. */
          186  +	 * stat_long is defined differently under posix. */
   187    187   	typedef u8 stat;
   188    188   	typedef u32 stat_long;
   189    189   ”,“dnl
   190    190   	ifelse(atom_target_os,“win”,“
   191    191   		typedef u32 stat;
   192    192   	”,“dnl
   193    193   	ifelse(atom_target_os,“vms”,“
................................................................................
   197    197   		/* we don't know a specific exit status type
   198    198   		 * for your arch so we're going with a sane
   199    199   		 * default. if this is wrong, help us fix it! */
   200    200   	”)”)dnl
   201    201   	typedef stat stat_long;
   202    202   ”)dnl
   203    203   
          204  +/* unicode types */
          205  +
          206  +typedef u32 codepoint;
          207  +	/* a codepoint is a UTF-32 character */
          208  +typedef u8 rune [5];
          209  +	/* a rune is a valid UTF-8 character terminated with a nul. */
          210  +
   204    211   #endif

Modified mod/kio/io.h.m from [f45741da3f] to [34396da060].

    74     74   	kiocond_fail_over_quota,
    75     75   	kiocond_fail_pfault,
    76     76   	kiocond_fail_too_big,
    77     77   	kiocond_fail_stream_mismatch,
    78     78   } kiocond;
    79     79   
    80     80   kiocond kiosend(kiochan, ksraw, sz*); // send data to a channel
           81  +kiocond kiosendall(kiochan, ksraw); // keep sending data to a channel until it's all sent
    81     82   kiocond kiorecv(kiochan, ksraw*); // receive data from a channel
    82     83   kmptr kiorecvall(kiochan, kmcell*, kmkind); // automatically allocate a bufer for a channel
    83     84     // kmkind is only used if kmcell* is null
    84     85   kiocond kiocon(kiochan, kiochan); // connect one channel to another
    85     86   
    86     87   #ifdef __cplusplus
    87     88   }
    88     89   #endif
    89     90   
    90     91   #endif

Added mod/kstr/scp.fn.c version [214672f460].

            1  +#include <k/core.h>
            2  +#include <k/str.h>
            3  +
            4  +kscond kscp(ksraw str, ksmut dest, sz* len) {
            5  +	if (str.size == 0) { /* calculate length on the fly */
            6  +		for(; str.ptr[str.size] != 0; ++ str.size) {
            7  +			if (str.size >= dest.size) return kscond_no_room;
            8  +
            9  +			dest.ptr[str.size] = str.ptr[str.size];
           10  +		}
           11  +	} else {
           12  +		if (dest.size < str.size) return kscond_no_room;
           13  +
           14  +		if (dest.ptr == null || str.ptr == null) return kscond_null;
           15  +
           16  +		for(sz idx = 0; idx < str.size; ++ idx) {
           17  +			dest.ptr[idx] = str.ptr[idx];
           18  +		}
           19  +	}
           20  +
           21  +	/* did we copy a nul? */
           22  +	if (str.ptr[str.size - 1] != 0)
           23  +		/* we didn't, can we? */
           24  +		if (dest.size > str.size)
           25  +			/* set a final nul */
           26  +			dest.ptr[str.size] = 0;
           27  +
           28  +	if (len != null) *len = str.size;
           29  +	return kscond_ok;
           30  +}

Modified mod/kstr/str.h from [0c79e2a160] to [43535adcf4].

    24     24   
    25     25   #include <k/internal.egroup.h>
    26     26   typedef enum kscond {
    27     27   	kscond_ok = kscond_id,
    28     28   	kscond_fail,
    29     29   	kscond_unimplemented,
    30     30   	kscond_nonnumeric,
           31  +	kscond_no_room,
           32  +	kscond_null,
    31     33   } kscond;
    32     34   
    33     35   enum ksconv {
    34     36   	ksconv_default = 0,
    35     37   
    36     38   	ksconv_raw = 1,
    37     39   	ksconv_bin = 2,
................................................................................
    49     51   kscond ks_to_int(ksraw str,
    50     52   		enum ksconv mode,
    51     53   		u8* dest, sz size);
    52     54   
    53     55   kscond ks_of_int(u8* number, sz size,
    54     56   		enum ksconv mode,
    55     57   		char* bufstart, sz bufsize);
           58  +
           59  +kscond kscp(ksraw str, ksmut dest, sz* len);
    56     60   
    57     61   #ifdef __cplusplus
    58     62   }
    59     63   #endif
    60     64   
    61     65   #endif