Index: build.sh ================================================================== --- build.sh +++ build.sh @@ -214,16 +214,16 @@ # first pass: copy all headers into place, # including ones we need to generate for mod in ${modules[@]}; do - for h in $(scan $mod '*.h'); do + for h in $(scan mod/$mod '*.h'); do base=$(basename $h) cp "$h" "$to/k/$base" done - for h in $(scan $mod '*.h.m'); do + for h in $(scan mod/$mod '*.h.m'); do base=$(basename $h) dest=${base%%.m} comp_mac "$h" "$to/k/$dest" done done @@ -232,11 +232,11 @@ # versions of the documentation from the md src if test "$doc" = "yes"; then global/build-manpage.sh libk.md for mod in ${modules[@]}; do - for doc in $(scan $mod '*.md'); do + for doc in $(scan mod/$mod '*.md'); do base="$(basename $doc)" stem="${base%%.md}" report global/build-manpage.sh "$doc" done done @@ -249,32 +249,32 @@ fn_objects=() rt_objects=() data_objects=( $to/internal.ident.o ) for mod in ${modules[@]}; do - for fn in $(scan $mod '*.fn.c'); do + for fn in $(scan mod/$mod '*.fn.c'); do base="$(basename $fn)" dest="$to/$mod.${base%%.c}.o" comp_co "$fn" "$dest" fn_objects+=("$dest") done - for rt in $(scan $mod '*.rt.c'); do + for rt in $(scan mod/$mod '*.rt.c'); do base="$(basename $rt)" dest="$to/$mod.${base%%.c}.o" comp_co "$rt" "$dest" rt_objects+=("$dest") done - for fn in $(scan $mod "*.fn.$target.s"); do + for fn in $(scan mod/$mod "*.fn.$target.s"); do base="$(basename $fn)" dest="$to/$mod.${base%%.s}.o" comp_asm "$fn" "$dest" fn_objects+=("$dest") done - for rt in $(scan $mod "*.rt.$target.s"); do + for rt in $(scan mod/$mod "*.rt.$target.s"); do base="$(basename $rt)" dest="$to/$mod.${base%%.s}.o" comp_asm "$rt" "$dest" rt_objects+=("$dest") done @@ -297,11 +297,11 @@ # fifth pass: compile the executable tools # against the libraries created in pass 5 for mod in ${modules[@]}; do - for exe in $(scan $mod '*.exe.c'); do + for exe in $(scan mod/$mod '*.exe.c'); do base="$(basename $exe)" dest="$to/$mod.${base%%.exe.c}" if test $build_shared_library == yes; then comp_c "$to/boot.o $exe" "$dest" -lk else Index: global/common.sh ================================================================== --- global/common.sh +++ global/common.sh @@ -51,14 +51,14 @@ fi done echo } else - announce() { shift; echo " --> " $@; } + announce() { echo " --> " $@; } fi # the following function is called to report a command invocation # the person compiling the library. the first argument should be # an ansi format string; this is used to color-code the tool being # launched and thus should be different for each one. report() { announce $@; $@; } DELETED kbuild/kbuild.md Index: kbuild/kbuild.md ================================================================== --- kbuild/kbuild.md +++ kbuild/kbuild.md @@ -1,25 +0,0 @@ -# kbuild - -**kbuild** is a small build tool designed to maximize the ease of writing portable libk applications. kbuild is included because most C compilers automatically assume their input should be linked with libc, inserting several implicit flags that need to be changed or removed for libk. while it is fully possible to build libk programs without using kbuild, it can be somewhat tedious, especially for code too small to warrant a makefile. - -kbuild is not intended to replace complex build systems like gmake, nor is it intended to be used only with libk binaries. - -unlike most build systems, kbuild does not use a makefile or project file. kbuild is invoked simply by passing it a list of C files, which it will build and link together. kbuild recursively calculates dependencies for each file, by parsing `#include` directives and kbuild-specific pragmas. - -kbuild passes a series of variables through to the preprocessor, providing information about the current system that can be useful for conditional compilation. - -these are the phases of the kbuild process: - - -# pragmas - -to support automatic linking, kbuild handles the following pragmas. - - * `#pragma k lib` adds a library name to the list of libraries to include. on linux, pkg-config will be used to locate the library and add the appropriate flags to the command line. link flags will be applied to the final binary, while all other flags will apply only to the compilation of the source unit; for this reason, library pragmas should precede `#include` directives. `#pragma k lib c` can be used to link the standard C library; if it is present, libk will not be linked unless it is explicitly named (`#pragma k lib k`). - * `#pragma k flag` indicates that a file should be compiled with a specific flag. this will be passed directly to the compiler, so it should be used within `#if` statements to detect the operating system and/or compiler being used. - * `#pragma k link` adds a flag to the final linker command line - * `#pragma k env` requests a specific environment variable be passed to the compiler with the -D flag; `#pragma k env EDITOR` will create a macro `KE_EDITOR` if `$EDITOR` is set in the user's environment. on encountering an `env` predicate for a variable it has not already passed, `kbuild` will terminate parsing and re-run cpp with the new variable set. this allows the use of environment variables to make first-pass decisions, though its effects may be somewhat unintuitive - a `#pragma k env` directive can affect the evaluation of a macro expression earlier in the same file. `#pragma k env` without an argument requests all host environment variables be passed through to the program. - -pragmas are scanned from the preprocessed file, meaning `#if` blocks can be used to control whether a library is linked or flag is passed. - -the `_Pragma` form is not currently supported, but supporting it is a long-term goal. DELETED kconf/kconf.md Index: kconf/kconf.md ================================================================== --- kconf/kconf.md +++ kconf/kconf.md @@ -1,64 +0,0 @@ -# kconf - -while there are a number of existing configuration parser libraries out there, they all have their problems, and all depend on libc. since configuration string parsing is a fairly core functionality that would force many programs to either write their own code or drag libc back into their dependencies, supplying a basic parser with libk makes sense. - -## basic principles - -kconf is intended to be fast, lightweight, transparent, and low-overhead. - -to initialize a kconf structure, we begin by supplying a list of *atoms.* like in Xlib, an atom is a performant way to reference a known string of text from a compiled program. we can do this in two different ways, either by generating the values ourselves at compile time with an enum, or by generating them at runtime using the appropriate kconf interface. note that if you supply your own with an enum, because zero is used for in-band error signalling, the first atom should always be an error token. alternately, it may be explicitly set to a non-zero number and a return value of zero can be checked for implicitly or against a literal 0. - -note that, in a recurring libk pattern, if an element count of zero (or `null`) is passed to a function that takes an array of nullables, that function will treat its array argument as a null-terminated array to be counted at runtime. - -kconf is initialized by filling out the struct `kconf` with the parameters of operations and then passing it to the appropriate function. - - enum atoms { parse_error, user, pw, email, atomct }; - kconf kc = { - .decl = { atomct, { - { user, {4, "user" } }, - { pw, {8, "password"} }, - { email, {5, "email" } } - }} - }; - - /* with runtime counts: - * enum atoms { parse_error, user, pw, email }; - * kconf kc = { - * .decl = { null, { - * { user, {0, "user" } }, - * { pw, {0, "password"} }, - * { email, {0, "email" } }, - * { null } - * }} - * }; */ - - /* with macros: - * enum atoms { parse_error, user, password, email }; - * kconf kc = { - * .decl = {Kmpsa(kconf_decl, { - * Kconf_atom(user), - * Kconf_atom(password), - * Kconf_atom(email) - * })}; - * }; */ - -## types - -### struct kconf - * `union { kconf_decl decl; kconf_gen gen; };` - -### struct kconf_atom - - * `sz key` - * `ksraw string` - -### struct kconf_pair - - * `kconf_atom atom` - * `ksraw* dest` - - -## macros - - * `Kconf_atom(atom)` - a convenience macro for definining an pre-defined atom structure (`kconf_atom`), under the assumption that the C enumerator name is the same as the string used to name the field in the configuration file. inserts the text `{atom, {sizeof(#atom), #atom}}` - * `Kconf_atom_pfx(pfx,atom)` - a convenience macro for definining an pre-defined atom structure (`kconf_atom`), under the assumption that the C enumerator name is the string used to name the field in the configuration file with a prefix attached. inserts the text `{pfx##atom, {sizeof(#atom), #atom}}` DELETED kcore/__stack_chk_fail.fn.c Index: kcore/__stack_chk_fail.fn.c ================================================================== --- kcore/__stack_chk_fail.fn.c +++ kcore/__stack_chk_fail.fn.c @@ -1,3 +0,0 @@ -#include - -void __stack_chk_fail(void) { kstop(-1); } DELETED kcore/boot.rt.c Index: kcore/boot.rt.c ================================================================== --- kcore/boot.rt.c +++ kcore/boot.rt.c @@ -1,17 +0,0 @@ -#include -extern stat entry(kenv); - -unsigned long long -_boot(unsigned int argc, /* argument count */ - char** argv, /* arguments */ - char** envp /* environment */ ) { - kenv e = { - /* TODO: determine terminal class and set term vs ansi correctly! */ - { {kiostream_term, 0}, {kiostream_term, 1} }, // chan std - { {kiostream_closed}, {kiostream_term, 2} }, // chan err - argc, argv, - null // needs parsing - }; - return entry(e); -} - DELETED kcore/boot.rt.x86.lin.64.s Index: kcore/boot.rt.x86.lin.64.s ================================================================== --- kcore/boot.rt.x86.lin.64.s +++ kcore/boot.rt.x86.lin.64.s @@ -1,67 +0,0 @@ -; vim: ft=nasm -bits 64 -%include "../arch/posix/x86.lin.64.s" -global _start:function -extern _boot -extern entry - -_start: - mov rbp, 0 ; zero the stack base ptr. - ; attempted fix for a difficult-to- - ; reproduce bug - - mov rdi, 0 ; zero rdi - because argc - ; is 32 bits long, we have to store - ; its value in the lower half of rdi. - ; this ensures that the upper half is - ; zeroed as well. - - mov edi, [rsp + 0] ; sizeof arguments - ; first argument to _boot(): argc - ; this is a 32-bit signed(??) integer - ; that is equal to the number of - ; elements in argv (see below). it is - ; not strictly necessary, because argv - ; is per spec always null-terminated, - ; but we pass it just in case. - - lea rsi, [rsp + 8] ; &arguments - ; 2nd argument to _boot(): ptr to argv - ; this points to an array of strings - ; containing the program's command line - ; arguments. _boot() does not need to - ; parse this, but it does need to store - ; it in the structure passed to main() - - lea rdx, [rsp + 16] ; &environment - ; third argument to _boot(): ptr to envp - ; this points to the list of environment - ; variables for the running program. it - ; is the responsibility of _boot to parse - ; this list and arrange it into a set of - ; legible and useful C arrays. - - mov rax, 0 ; zero out %rax - ; this is required by the C ABI, and is - ; reputedly necessary for compatibility - ; with icc, intel's own proprietary C - ; compiler. - - call _boot ; invoke the start function - ; that will set up the runtime and - ; construct the necessary structures - ; that will be bassed to libc. - - ; boot has returned and left its - ; return value in the register %rax. - ; regardless of the size of the - ; return value of main(), _boot always - ; returns the system word length. - - mov sys.reg.1, sys.reg.ret ; fill in - ; the return value as exit's argument - - mov sys.reg.0, sys.exit ; set %rax to - ; the syscall number of exit - - sys.call ; invoke the kernel DELETED kcore/core.h Index: kcore/core.h ================================================================== --- kcore/core.h +++ kcore/core.h @@ -1,166 +0,0 @@ -#ifndef KIcore -#define KIcore -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct kvar { - ksraw name; - ksraw val; - char* platform; -} kvar; - -typedef struct kenv { - kiochan std; - kiochan err; - sz argc; char** argv; - kvar* vars; -} kenv; - -/* i'm really sorry okay */ -typedef -#if (__STDC_VERSION__ >= 199901L) - _Bool bool; -#endif -enum -#if !(__STDC_VERSION__ >= 199901L) - bool /* enum bool { */ -#endif -{ - false = 0, no = 0, - true = 1, yes = 1 -} -#if !(__STDC_VERSION__ >= 199901L) - bool /* } bool ; */ -#endif -; - -#ifndef KFclean -# include -# define Kokay(cond) (((cond) % kglobal_module_offset) == 0) -# if (__STDC_VERSION__ >= 199901L) ||\ - (__cplusplus >= 201103L) -# define KVvm_args __VA_ARGS__ -# define KVvm_spec ... -# define KFfeat_variadic_macro -# else -# define KVvm_args K_TEMP_M2QD52 -# define KVvm_spec K_TEMP_M2QD52 -# endif -# if defined(__GNUC__) || defined(__clang__) -# define KA(KVvm_spec) __attribute__((KVvm_args)) -# else -# define KA(KVvm_spec) -# endif -# define KAformat(KVvm_spec) KA(format(KVvm_args)) -# define KAexport(KVvm_spec) KA(visibility(KVvm_args)) - -# define KAunused KA(unused) -# define KAnoreturn KA(noreturn) -# define KApure KA(const) -# define KAinline KA(always_inline) -# define KAflatten KA(flatten) -# define KAexport_none KAexport("hidden") -# define KAexport_force KAexport("default") -/* now we define standard version flags, - * to make it easy for the user to write - * more portable code. */ -# if (__STDC_VERSION__ >= 199901L) -# define KFstd_c89 -# define KFstd_c99 -# define KVstd c99 -# else -# ifdef __STDC__ -# define KFstd_c89 -# define KVstd c89 -# else -# define KVstd K&R /* UH OH */ -# endif -# endif -# if (__STDC_VERSION__ >= 201103L) -# define KFstd_c11 -# define KVstd c11 -# endif -# ifdef __cplusplus -# define KFstd_c89 -# define KFstd_cpp -# define KVstd c++ -# if (__cplusplus >= 201103L) -# define KFstd_cpp11 -# define KVstd c++11 -# endif /* TODO: add more */ -# endif -#endif - -#ifdef KFstd_c11 -# define Kassert _Static_assert -#else -# define Kassert(x) { struct { int assertion_failed[(x) ? 1 : -1] }; } -#endif - -/* hooo boy. null. that one's got a storied - * history across the versions and dialects. - * below, we try to find the ideal way to - * offer a "null" "keyword" depending on - * dialect, version, and whether the user - * has asked for macros to be suspended. - * note that this may result in no "null" - * being defined in C++ or K&R C. */ -#if defined (__cplusplus) && ! defined(KFclean) -# if __cplusplus >= 201103L -# define null nullptr -# else -# define null ((void*)0) -# endif -#elif defined __STDC__ && (!defined __clang__ || defined KFclean) - enum { null = 0 }; - /* believe it or not, this is actually - * completely legal. doesn't even raise - * a single warning in GCC. i was surprised - * too. alas, it later turned out that - * clang will throw a fit about this, so - * we only use the enum method if __clang__ - * is undefined, or if the alternative is - * no null keyword at all. */ -#elif ! defined(KFclean) -# define null ((void*)0) -#endif - -#ifndef KFclean -# ifdef __cplusplus -# define noreturn [[ noreturn ]] -# elif __STDC_VERSION__ >= 201103L -# define noreturn _Noreturn -# else -# define noreturn -# endif -#endif - -#ifdef __cplusplus - [[noreturn]] void kstop(stat_long code); -#elif __STDC_VERSION__ >= 201103L - _Noreturn void kstop(stat_long code); -#else - void kstop(stat_long code); -#endif - -typedef u16 kcond; -/* this will probably not need to be altered, - * as libk sports a modest number of modules, - * and there are few enough error conditions - * in each that 16-bit address space should - * be more than enough for the foreseeable - * future. however if that changes, altering - * the definition here will effect all the - * necessary changes throughout the library */ -bool kokay(kcond); - -#ifdef __cplusplus -} -#endif - -#endif DELETED kcore/def.h.m Index: kcore/def.h.m ================================================================== --- kcore/def.h.m +++ kcore/def.h.m @@ -1,53 +0,0 @@ -dnl kcore/def.h.m → -dnl ~ lexi hale -dnl this file gathers information on the environment it's -dnl being compiled in, setting macros that other headers -dnl need. it will be emitted as . -dnl vim: ft=m4 -#ifndef KIdef -#define KIdef -define(`_atom',0)dnl -define(`def',`#define $1 $2')dnl -define(`defatom',`def($1,$2$3)')dnl -define(`newatom',`def($1,_atom) - define(`_atom',incr(_atom))')dnl - -ifdef(`atom_target_bits',` - define(`target',`atom_target_arch.atom_target_os.atom_target_bits') - def(KVbits,atom_target_bits)',` - define(`target',atom_target_arch.atom_target_os)') - -newatom(KA_os_lin)dnl -newatom(KA_os_fbsd)dnl -newatom(KA_os_obsd)dnl -newatom(KA_os_nbsd)dnl -newatom(KA_os_dar)dnl -newatom(KA_os_osx)dnl -newatom(KA_os_and)dnl -newatom(KA_os_hai)dnl -newatom(KA_os_win)dnl - -newatom(KA_arch_x86)dnl -newatom(KA_arch_arm)dnl -newatom(KA_arch_ppc)dnl -newatom(KA_arch_mips)dnl -newatom(KA_arch_itan)dnl - -defatom(KVos,KA_os_,atom_target_os) -defatom(KVarch,KA_arch_,atom_target_arch) - -ifelse(target_unix,`yes', - `def(`KFenv_unix',) - def(`KFenv_posix',)',` - ifelse(target_posix,`yes', - `def(KFenv_posix)')') - -#define Kpragma(p) _Pragma(#p) -#if defined(__GNUC__) || defined(__clang__) -# define Kerror(msg) Kpragma(GCC error #msg) -#else -# define Kerror(msg) Kpragma(message #msg) -#endif -def(`Knoimpl(fn)', Kerror(no implementation of fn for platform target)) - -#endif DELETED kcore/kcore.md Index: kcore/kcore.md ================================================================== --- kcore/kcore.md +++ kcore/kcore.md @@ -1,82 +0,0 @@ -# kcore -**kcore** is the foundation for the rest of libk. it defines types and structs that are needed by every program, and provides the stub that launches a program's "entry" function. unlike the non-core modules, kcore definitions simply use the prefix `k-`. - -# description -**kcore** contains the libk runtime and headers with important typedefs, structures and enumerations. - -it also provides `boot.o`, the runtime file that must be statically linked into every libk program for it to be able to load. `boot.o` is incorporated in the static library `libk.a` but for architectural reasons cannot be exported from the dynamic library `libk.so`. - -# program entry point -when using libk, your program's entry point will not be the `int main(int,char**)` function that libc opens into. libk will call the function `stat entry(kenv)` instead. like libc, the value returned by `entry` will be returned to the host platform. entry may have the return type `stat` or `stat_long`. on some operating systems, they are the same; on modern versions of UNIX, however, `stat_long` will allow you to return a 32-bit exit status. most shells will truncate such statuses, but they can be retrieved be the calling process with the appropriate syscall. - -# core types -kcore contains fixed-width integer types (in ``). these types are present on every platform, and are not prefixed. if a platform has a type with a given bit length, that type will be used will be used, otherwise, the type will round to the largest available type (except `u8` and `s8`, which round to the smallest). if you need to be absolutely certain that a type is the appropriate bit length, use sizeof to check its length in a conditional or static assert, for instance `if (sizeof(u16) == 16 / kc_byte_bits)`. - - * `u8` - an unsigned 8-bit integer, or the smallest available unsigned type - * `s8` - a signed 8-bit integer, or the smallest available signed type - * `u16` - an unsigned 16-bit integer - * `s16` - a signed 16-bit integer - * `u32` - an unsigned 32-bit integer - * `s32` - a signed 32-bit integer - * `u64` - an unsigned 64-bit integer - * `s64` - a signed 64-bit integer - * `u128` - an unsigned 128-bit integer (uses GCC and clang extensions to offer 128-bit integer support on x86-64) - * `s128` - a signed 128-bit integer (ibid) - * `ubig` - the largest available unsigned integer type - * `sbig` - the largest available signed integer type. note: ubig and sbig really are the *largest* possible integer types not just the largest native types - if your compiler has extensions for 128-bit types on your arch (as GCC and clang do on x86-64), ubig and sbig will be 128 bits long even if your system word length is less than 128. so only use ubig/sbig if you really, really mean it. - * `ubyte` - the smallest available unsigned integer type besides \_Bool - * `sbyte` - the smallest available signed integer type besides \_Bool - * `stat` - the type of process return values expected by the platform (usually u8 on linux) - * `sz` - a type large enough to cover a platform's entire address space (libc equivalent size_t) - * `offset` - a type that can contain the difference between two pointers (libc equivalent ptrdiff_t) - - * `byte_bits` - the bit length of ubyte, sbyte, and char; that is, the number of bits in the type `sizeof` measures things in terms of. `sizeof(type) * byte_bits` will always return the number of bits in a type. - -**kcore** also defines a number of important structs. - -## struct kenv -`kenv` is a struct that encompasses the environment the program was launched in. - * `kiochan std` - a stereo IO channel for reading and writing to and from stdout. - * `kiochan err` - a mono IO channel for writing to stderr. - * `kvar* vars` - a pointer into the program's environment - -## struct kvar -`kvar` is a struct that abstracts over platform environment variables. - * `kstr name` - the name of an environment variable - * `kstr val` - the value of an environment variable - * `char* platform` - a pointer into the platform's underlying representation - -# constants -in ``, every type has an associated min and max constant, containing the smallest and largest value that type can hold on your system. these can be access by suffixing a type with `_min` and `_max`, respectively. min and max values of native types can be accessed with the `kc_[us]` prefix - for instance, the minimum value of `signed long` is `kc_slong_min`. (`long long` can be referenced as `llong`). - -# functions -kcore contains only a handful of primitive functions, most of which provide the C runtime. - -## kstop() - -`noreturn void kstop(stat)` terminates the program immediately, returning the specified integer to the OS as an exit status. the type of integer it takes depends on your operating system. consider using the `enum kbad` defines in ``, which are designed to standardize exit statuses across different software and are synchronized with a FreeBSD effort to do the same (``). - -# definitions - -kcore is the only module that defines any terms outside the k- namespace. the only terms it so defines are native C terms like `noreturn`, which are implemented as keywords in a reserved namespace (`_` followed by an uppercase letter; in this case, `_Noreturn`). macros are then defined in new headers for the "natural" version of the term in order to avoid breaking older code. examples of this technique are `` and ``. since libk is designed for new, modern code, breaking old code isn't a concern, and we turn on all these new keywords as soon as you load ``. - -libk attempts to find the best possible definition and implementation for these various types and keywords, abstracting across different C versions and dialects. you can go ahead and use the normal version of the keywords (e.g. `noreturn`, `bool`) no matter what kind of compiler you're using and you're guaranteed that as long as you don't go fiddling with undefined- or implementation behavior, your code will behave the same every time. at worst, it may lack a few optimizations or warnings. - -(the one minor exception is `null`, a new keyword which libk uses in preference to the ugly, hard-to-type C macro `NULL`.) - -`bool` is implemented as a `_Bool` typedef if `_Bool` is available, and as an enum typedef otherwise. either way, and regardless of whether `KFclean` has been defined, booleans can be used and set to the values `true`, `false`, `yes`, or `no`. `yes` and `true` are synonyms, as are `false` and `no`. - -# macros - -if the flag `KFclean` has not been defined, kcore defines a number of macros to abstract over nonstandard C features. for instance, the `KA*` macros can be used to specify GNU/clang attributes without worrying about compatibility, as they'll automatically be blanked under an incompatible compiler. the KA series includes: - - * `KApure` - marks a "pure" function that changes no state and only takes purely numeric arguments, allowing the compiler to avoid repetetive calls to it or even evaluate it at runtime. - * `KAnoreturn` - the GNU/clang-specific version of `noreturn`. - * `KAunused` - acknowledges that a variable is unused and tells the compiler to shut up about it. - * `KAinline` - forces the compiler to inline a function whether it likes it or not. - * `KAflatten` - placed on a function, will force-inline all function calls *made by* that function — sort of like `KAinline` in reverse. - * `KAformat()` - specifies that the function has printf-style syntax for error-checking purposes. worst hack in history. - -if you wish to add more (there are like, hundreds) please consider making a merge request. however, the generic macro KA() is also available in the interim: `KA(unused)`. - -however, unlike C++ attributes, GNU-style attributes can only be placed on a function declaration, *not* its definition. DELETED kcore/magic.h Index: kcore/magic.h ================================================================== --- kcore/magic.h +++ kcore/magic.h @@ -1,76 +0,0 @@ -/* - * ~ lexi hale - * one of libk's biggest goals is standardization across - * software. for this reason, we define here a variety - * of "magical numbers" used to signal or describe - * various states, messages, and errors. suggestions are - * welcome. - */ -#ifndef KImagic -#define KImagic - -#include - -typedef enum kbad { - // values to be returned by an entry function - kbad_ok = 0, - /* the requested operation completed - * successfully */ - - kbad_mem = 1, - /* the system does not have enough free - * memory to complete the requested operation */ - - /* these are synchronized with freebsd's existing - * sysexit.h conventions. */ - kbad_usage = 64, - /* # fbsd EX_USAGE - * a usage screen was displayed to the user - * on request or due to improperly formed call */ - kbad_data = 65, - /* # fbsd EX_DATAERR - * data supplied by the user was incorrect in - * some way */ - kbad_no_input = 66, - /* # fbsd EX_NOINPUT - * input to the program is absent */ - kbad_user = 67, - /* # fbsd EX_NOUSER - * these aren't the droids you're looking for */ - kbad_host = 68, - /* # fbsd EX_NOHOST - * that host doesn't exist */ - kbad_avail = 69, - /* # fbsd EX_UNAVAILABLE - * a necessary resource or support system isn't - * available, or something else went wrong */ - kbad_internal = 70, - /* # fbsd EX_SOFTWARE - * "my bad" */ - kbad_os = 71, - /* # fbsd EX_OSERR - * the OS did a fucksy-wucksy */ - kbad_osfile = 72, - /* # fbsd EX_OSFILE - * a system file is fucked up or gone */ - kbad_creat = 73, - /* # fbsd EX_CANTCREAT */ - kbad_io = 74, - /* # fbsd EX_IOERR */ - kbad_try = 75, - /* # fbsd EX_TEMPFAIL - * something went wrong this time. try again - * some other time */ - kbad_proto = 76, - /* # fbsd EX_PROTOCOL - * failure to speak a protocol correctly */ - kbad_perm = 77, - /* # fbsd EX_NOPERM - * you or the program do not have permissions - * to do the thing requested */ - kbad_conf = 78, - /* # fbsd EX_CONFIG - * your configuration is fucked */ -} kbad; - -#endif DELETED kcore/platform.syscall.fn.c Index: kcore/platform.syscall.fn.c ================================================================== --- kcore/platform.syscall.fn.c +++ kcore/platform.syscall.fn.c @@ -1,36 +0,0 @@ -/* platform.syscall.fn.c - * ~ lexi hale - * this file provides a unified interface - * to the host operating system's syscalls. - * its function signature may vary across - * OSes, as the details of each's syscall - * implementation may vary drastically. - */ - -#include -#include - -#ifdef KFenv_posix -# include -#else - Knoimpl(k_platform_syscall) -#endif - -extern void k_platform_syscall_raw ( - k_platform_syscall_return* return_slot, - k_platform_syscall_error* error_no_slot, - enum k_platform_syscall syscall_no, - u8 valency, - s64* args); - -struct k_platform_syscall_answer -k_platform_syscall(enum k_platform_syscall call, u8 valency, s64 args[]) { - struct k_platform_syscall_answer answer; - - k_platform_syscall_raw - (&answer.ret, - &answer.error, - call, valency, args); - - return answer; -} DELETED kcore/stop.fn.c Index: kcore/stop.fn.c ================================================================== --- kcore/stop.fn.c +++ kcore/stop.fn.c @@ -1,29 +0,0 @@ -/* kcore/stop.fn.c - kstop() - * ~ lexi hale - * this file defines a function that prematurely exits from - * a running libk program with an appropriate exit status. */ - -#include -#include // so we know what system this is -#include -#ifdef KFenv_posix -# define KFplatform_define_types -# define KFplatform_define_constants -# include -#endif - -/* this manual redefinition is necessary to stop gcc - * bitching that kstop returns, which it def does not */ -noreturn extern struct k_platform_syscall_answer -k_platform_syscall(enum k_platform_syscall call, u8 valency, - k_platform_syscall_arg args[]); - -noreturn void kstop(stat_long code) { -# ifdef KFenv_posix - k_platform_syscall_arg exit_code[] = { code }; - k_platform_syscall(k_platform_syscall_exit, 1, exit_code); -# else - Knoimpl(kstop) -# endif -} - DELETED kcore/syscall.fn.x86.lin.64.s Index: kcore/syscall.fn.x86.lin.64.s ================================================================== --- kcore/syscall.fn.x86.lin.64.s +++ kcore/syscall.fn.x86.lin.64.s @@ -1,86 +0,0 @@ -; kcore/syscall.fn.x86.lin.64.s -; ~ lexi hale -; -; this function performs a syscall and stores its -; results in the variables provided. this makes it -; possible to bypass the hideous errno mechanism -; altogether and access the error value of a -; syscall directly. invoke as: -; -; void k_platform_syscall_raw(s64* result, u64* errno, -; syscall, u8 valency, s64[] args) - -bits 64 -%include "../arch/posix/x86.lin.64.s" -%include "../arch/x86.cdecl.64.s" -; vim: ft=nasm - -%macro handle_arg 1 - %assign v %1+1 - mov sys.reg. %+ v, [ccall.reg.4 + 8 * %1] - dec ccall.reg.3 - jz .perform_call -%endmacro - -global k_platform_syscall_raw:function -k_platform_syscall_raw: - ; locals: rbx = s64* result - ; r12 = u64* errno - ; arg 0 = s64* result - ; arg 1 = errno ptr - ; arg 2 = syscall num - ; arg 3 = valency - ; arg 4 = args ptr - - ; store the locals in registers that - ; are guaranteed not to be clobbered, - ; saving us some cycles pushing to - ; and popping back from the stack - mov rbx, ccall.reg.0 - mov r12, ccall.reg.1 - - ; this needs to go before the loop - ; or it'll get clobbered - mov sys.reg.0, ccall.reg.2 - - ; automatically generate the code - ; needed to move the arguments into - ; their correct registers. see above - %assign i 0 - %rep 6 - handle_arg i - %assign i i+1 - %endrep - - ; valency >= 7. this is not valid, so - ; we set our return value to -1 and the - ; error number to its maximum value in - ; order to indicate that the syscall - ; was invalid - mov qword [rbx], -1 - mov qword [r12], -1 - ret - - ; we have a valency match - perform the - ; requested syscall already store in rax - .perform_call: sys.call - - ; check for an error - on x86, error is - ; returned as a negative value in %rax - test sys.reg.ret, sys.reg.ret - js .error ; jump if sign flag set - - ; move our return values into place and - ; return to the caller (which should - ; always be k_platform_syscall, btw) - mov [rbx], sys.reg.ret - mov qword [r12], 0 ; no error - ret - - ; an error was returned - we need to set - ; the errno to its positive equivalent, - ; and store -1 in the return variable - .error: neg sys.reg.ret - mov qword [rbx], -1 - mov [r12], sys.reg.ret - ret DELETED kcore/testbin.exe.c Index: kcore/testbin.exe.c ================================================================== --- kcore/testbin.exe.c +++ kcore/testbin.exe.c @@ -1,33 +0,0 @@ -#include -#include -#include -#include - -struct object { - u8 a; - s16 b; - bool c; -}; - -stat_long entry(kenv e) { - const char msg[] = "hello from libk\n"; - ksraw ptr = { Kmsz(msg), msg }; - - bool maybe = true; - maybe = no; - - if (kiosend(e.std, ptr, null) == kiocond_ok) { - /* great, continue */ - } else { - return kbad_io; - } - - void* region = kmheapa(2048); - if (region == null) return kbad_mem; - - kmzero(region,2048); - - if (kmheapf(region) >= kmcond_fail) return kbad_mem; - - return kbad_ok; -} DELETED kcore/type.h.m Index: kcore/type.h.m ================================================================== --- kcore/type.h.m +++ kcore/type.h.m @@ -1,192 +0,0 @@ -dnl kcore/type.h.m → -dnl ~ lexi hale -dnl this file gathers information on the environment it's -dnl being compiled in, defining types that our code -dnl needs. it will be emitted as . -dnl vim: ft=c -include(`typesize.m') -changequote(`“',`”') -#ifndef KItype -#define KItype - -/* we define 64-bit types first due to an oddity in how - * 128-bit types are handled: we want kc_?big to reference - * the absolute largest type available to the compiler, - * but in some cases, 128-bits may not be among the - * standard C types despite being supported by the - * compiler. to work around this, we first check whether - * 64-bit types are available (__int128_t only works on - * 64-bit systems) and then whether the compiler is one - * that supports the 128-bit extension - but only if a - * native 128-bit type is not available. - * - * once this is done, we can be certain that u128 will - * reference the largest available integer type and can - * safely define kc_?big by reference to it. */ - - -ifdef(“type_bit64”,“ - typedef unsigned type_bit64 u64; - typedef signed type_bit64 s64; - ifdef(“type_bit128”,,“ - /* even if no native type is 128 bits long, clang and - * gcc have extensions to support 128 bit arithmetic - * on 64-bit hardware */ -# if defined(__GNUC__) || defined(__clang__) - typedef __uint128_t u128; - typedef __int128_t s128; -# else - /* if we don`'t have access to that extension - * or native 128-bit types, then we just use - * the largest native type specified in the - * C standard */ - typedef unsigned long long u128; - typedef signed long long s128; -# endif - ”)”,“dnl - typedef unsigned long long u64; - typedef signed long long s64; - - typedef u64 u128; - typedef s64 s128; -”)dnl - -ifdef(“type_bit128”,“dnl - typedef unsigned type_bit128 u128; - typedef signed type_bit128 s128; -”)dnl - -typedef unsigned char ubyte; -typedef signed char sbyte; -typedef u128 ubig; -typedef s128 sbig; - -ifdef(“type_bit8”,“dnl - typedef unsigned type_bit8 u8; - typedef signed type_bit8 s8; -”,“dnl - typedef ubyte u8; - typedef sbyte s8; -”)dnl - -ifdef(“type_bit16”,“ - typedef unsigned type_bit16 u16; - typedef signed type_bit16 s16; -”,“ - typedef ubig u16; - typedef sbig s16; -”)dnl - -ifdef(“type_bit32”,“ - typedef unsigned type_bit32 u32; - typedef signed type_bit32 s32; -”,“ - typedef ubig u32; - typedef sbig s32; -”)dnl - -define(“cat”, “$1$2”) -typedef enum kcendian { - kcendian_high, - kcendian_low, - kcendian_system = cat(kcendian_,prop_endian), -} kcendian; - -enum /* max-min values of each type */ { - byte_bits = arch_byte_bits, - - u8_min = 0, u8_max = ((u8)-1), - u16_min = 0, u16_max = ((u16)-1), - u32_min = 0, u32_max = ((u32)-1), - u64_min = 0, u64_max = ((u64)-1), - u128_min = 0, u128_max = ((u128)-1), - - /* assuming two's complement. TODO: check math */ - define(“sspec”,“ - $1_min = 0 - ((1 << sizeof($1) * byte_bits) / 2), - $1_max = (1 << sizeof($1) * byte_bits) / 2 - 1”)dnl - - sspec(s8), sspec(s16), sspec(s32), - sspec(s64), sspec(s128), - - kc_uchar_min = 0, kc_uchar_max = type_max_u_char, - kc_ushort_min = 0, kc_ushort_max = type_max_u_short, - kc_uint_min = 0, kc_uint_max = type_max_u_int, - kc_ulong_min = 0, kc_ulong_max = type_max_u_long, - kc_ullong_min = 0, kc_ullong_max = type_max_u_llong, - - kc_schar_min = type_min_s_char, kc_schar_max = type_max_s_char, - kc_sshort_min = type_min_s_short, kc_sshort_max = type_max_s_short, - kc_sint_min = type_min_s_int, kc_sint_max = type_max_s_int, - kc_slong_min = type_min_s_long, kc_slong_max = type_max_s_long, - kc_sllong_min = type_min_s_llong, kc_sllong_max = type_max_s_llong, - - ubig_min = u128_min, ubig_max = u128_max, - sbig_min = s128_min, sbig_max = s128_max, - - ubyte_min = kc_uchar_min, ubyte_max = kc_uchar_max, - sbyte_min = kc_schar_min, sbyte_max = kc_schar_max, -}; - -ifdef(“type_sz”,“ - typedef type_sz sz; -”,“dnl -# ifdef __cplusplus - /* C++ gives us a clean, standardized way to do this */ - typedef decltype (sizeof(char)) sz; -# else -# if defined(__GNUC__) || defined(__clang__) - typedef __typeof__ (sizeof(char)) sz; -# else - /* we're stumped - set sz to the safest possible value under - * the circumstances, and warn the user. */ -# warning no authoritative sz (size_t) type definition \ - available; defaulting to largest unsigned integer type - typedef ubig sz; -# endif -# endif -”)dnl - -ifdef(“type_offset”,“ - typedef type_offset offset; -”,“dnl -# ifdef __cplusplus - /* C++ gives us a clean, standardized way to do this */ - typedef decltype (((void*)-1) - 1) offset; -# else -# if defined(__GNUC__) || defined(__clang__) - typedef __typeof__ (((void*)10) - ((void*)5)) offset; -# else - /* no dice - set offset to the safest possible value under - * the circumstances, and warn the user. */ -# warning no authoritative offset (ptrdiff_t) type definition \ - available; defaulting to largest unsigned integer type - typedef sbig offset; -# endif -# endif -”)dnl - -// exit status integer types - pls use kbad in instead -ifelse(target_posix,“yes”,“ - /* by convention, posix return values are 8-bit, - * but note that many modern UNIXes do technically - * support higher-bit values. for this reason, - * longstat is defined differently under posix. */ - typedef u8 stat; - typedef u32 stat_long; -”,“dnl - ifelse(atom_target_os,“win”,“ - typedef u32 stat; - ”,“dnl - ifelse(atom_target_os,“vms”,“ - typedef u32 stat; - ”,“dnl - typedef u8 stat; - /* we don't know a specific exit status type - * for your arch so we're going with a sane - * default. if this is wrong, help us fix it! */ - ”)”)dnl - typedef stat stat_long; -”)dnl - -#endif DELETED kdb/kdb.md Index: kdb/kdb.md ================================================================== --- kdb/kdb.md +++ kdb/kdb.md @@ -1,34 +0,0 @@ -# kdb - -kdb is a module for storing and loading data from files. - -## model - -the first argument to all kdb functions is of type `struct kdb`, which describes the database to be accessed. the first field of that struct is a pointer to a struct of type `struct kdb_fmt` which describes the format of the database. - -kdb encodes records as a tuple of three fields: (kind, key, value). `kind` is a 16-bit integer, `key` is an integer of platform width, and `value` is a blob decoded according to the type of the record, which is not saved in the database itself. records are indexed by the tuple (kind, key); `key` may be null. `kind` denotes the structure of the record, and is user-defined. - -to use strings as the keys for database entries, either atoms should be stored in the database or a hash function may be used. kdb contains procedures to automate the use of atoms as keys. - -## types - -### struct kdb - - * `struct kdb_fmt* fmt` - * `enum kdb_conn conn` - * `union { kfile* file; const kdb_store* mem; kmptr ptr; } store;` - -### struct kdb_rec - - * `u16 kind` - * `word key` - * `ksraw value` - -### enum kdb_conn - - * `kdb_conn_none`: no database is connected; calls will fail - * `kdb_conn_file`: the database is stored in a file; calls to modify it will modify the file on-disk - * `kdb_conn_file_static`: the database is stored in a file; calls to modify it will fail. if the file is writable to the process, can be seamlessly changed to `kdb_conn_file` and vice-versa - * `kdb_conn_mem`: the database is stored in-memory, allocated by kmem and referenced by `ptr`. calls to modify it will re-allocate memory via the appropriate interfaces. - * `kdb_conn_mem_static`: the database is stored in-memory, referenced by `mem`; calls to modify it will fail - DELETED kdbg/dbg.h Index: kdbg/dbg.h ================================================================== --- kdbg/dbg.h +++ kdbg/dbg.h @@ -1,15 +0,0 @@ -#ifndef KIdbg -#define KIdbg -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -void kdbg_hexdump(kiochan, void*, sz); - -#ifdef __cplusplus -} -#endif -#endif DELETED kdbg/kdbg.md Index: kdbg/kdbg.md ================================================================== --- kdbg/kdbg.md +++ kdbg/kdbg.md @@ -1,5 +0,0 @@ -# kdbg -kdbg is a module offering convenience functions, useful primarily for debugging purposes. - -## functions - * `kdbg_hexdump(kiochan,void*,sz) → void` prints a canonical hexdump of a specified region of memory to the specified `kiochan`. DELETED kfile/file.h Index: kfile/file.h ================================================================== --- kfile/file.h +++ kfile/file.h @@ -1,94 +0,0 @@ -#ifndef KIfile -#define KIfile -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum kfcond { - kfcond_ok = 0, - kfcond_fail = 1, - // an unspecified error occurred - kfcond_bad_domain, - // the values passed to the function are not within - // that function's domain and are invalid - kfcond_overstep, - // kfstep() was only able to move part of the - // distance requested - kfcond_bad_index, - // the requested index is past the end of the file - kfcond_notfound, - // the specified object could not be found - kfcond_unauth, - // you do not have permission to open this object - kfcond_mem, - // there is not enough memory to complete the - // requested operation -} kfcond; - -typedef enum kfile_kind { - kfile_closed, - // the file object does not refer to an open file - kfile_open, - // the file object refers to an open file -} kfile_kind; - -enum kfopen { - /* file open modes */ - kf_read = 1 << 9, kf_write = 1 << 10, - kf_ascii = 1 << 11, kf_new = 1 << 12, - kf_create = 1 << 13, kf_load = 1 << 14, - kf_top = 1 << 15, kf_end = 1 << 16, - kf_wipe = 1 << 17, kf_map = 1 << 18, - - /* file permission flags */ - kf_ur = 4 << 6, kf_uw = 2 << 6, kf_ux = 1 << 6, - kf_gr = 4 << 3, kf_gw = 2 << 3, kf_gx = 1 << 3, - kf_or = 4 << 0, kf_ow = 2 << 0, kf_ox = 1 << 0, -} - -typedef struct kfile { - enum kfile_kind kind; - enum kfopen mode; - kiochan chan; - u8* content; -} kfile; - -enum kfset_mode { - kfset_none, // do not move - kfset_top, // move to x bytes from the start of the file - kfset_end, // move to x bytes from the end of the file -} - -enum kfplace { - kfplace_none, - kfplace_conf, - kfplace_home, - kfplace_desk, - kfplace_dl, - kfplace_img, - kfplace_vid, - kfplace_msc, - kfplace_doc, - kfplace_dat, - kfplace_share, - kfplace_share_global, - kfplace_cache, -} - -kfile kfplace(enum kfplace location, const char* file, enum kfopen mode); -kfile kfopen (const char* file, enum kfopen mode); -kfile kfopens(const ksraw file, enum kfopen mode); - -kfcond kfset (struct kfile file, enum kfset_mode mode, sz position); -kfcond kfstep(struct kfile file, offset position); - -kfcond kfshred(struct kfile file, sz count); - -#ifdef __cplusplus -} -#endif - -#endif DELETED kfile/kfile.md Index: kfile/kfile.md ================================================================== --- kfile/kfile.md +++ kfile/kfile.md @@ -1,81 +0,0 @@ -# kfile -kfile is libk's file handling library. kfile uses the short naming convention and its sigil is `f`. it is designed to abstract over OS idioms so that developers can write code that simply expresses their intent, and kfile will figure out the correct thing to do on a given platform. to that end, one of kfile's most important functions is `kfplace()` - see below. - -## functions - - * `struct kfile kfplace(enum kfplace location, const char* file, enum kfopen mode)` - when you need to open a file in a location that will vary between environments, such as configuration directories, download dirs, image dirs, etc, use `kfplace()` instead of `kfopen()`. the arguments work like `kfopen()`, except that `file` is not a normal path. instead, it specifies a file in the program's configuration directory to open, and the lead constant will tell it what kind of file you are trying to open. in most cases, a null or empty filename is an error, but see `enum kfplace` for exceptions. `file` must not begin with a slash; if they do, `kfcond_bad_domain` will be returned. - * `kfcond kfget(enum kfplace, ksmut buffer)` - this function will write the prefix for a particular environment directory into `buffer`, or return `kfcond_fail` if `buffer` is not big enough. the best way to use this function is with a for loop; see examples. do not use this to generate paths for file access; use `kfplace()` instead. - * `struct kfile kfopen(const char* file, enum kfopen mode)` - opens a file at the specified pathname. mode is a bit mask . if null is passed as a filename, a temporary file will be created in the appropriate place, using memfds or similar if available and the system temporary directory otherwise. `mode` is a bit flag; the lowest nine bits represent filesystem permissions to be used when creating a file and the rest control how the file will be opened - at least one of `kf_read` or `kf_write` must be passed. see `enum kfopen`. **do not** use kfopen to attempt direct access to configuration files in root of the user's home directory; libk **will** detect this and instruct your users to file bug reports. use kfplace(kfplace_conf) instead to open files in the configuration directory appropriate to the local environment. - * `struct kfile kfopens(const ksraw file, enum kfopen mode)` - like kfopen but allows specifying the filename via a `ksraw` string rather than a `const char*`. - * `kfcond kfset(struct kfile file, enum kfset_mode mode, sz position)` - repositions the read/write head at `position` bytes into the file (if mode is `kfset_top`) or at `position` bytes from the end of the file (if mode is `kfset_end`). attempting to index past the end of the file will return `kfcond_bad_index`. returns `kfcond_ok` on success. - * `kfcond kfstep(struct kfile file, offset position)` - moves the read/write head `position` bytes ahead if `position` is a positive number, or backwards otherwise. attempting to move past the start or end of the file will move the cursor to the start or end and return `kfcond_overstep`. returns `kfcond_ok` on success. - * `kfcond kfshred(struct kfile file, sz count)` - write over `file` with random data `count` times, then truncate it. - * **posix functions** - POSIX has a wide array of nonportable file IO calls that are important particularly with regards to `exec()` and piping data across processes. for this reason, on POSIX platforms, libk exposes these calls to the user. - * kfposix_pipe() - creates a pipe in memory - * kfposix_fifo() - creates a named pipe at the specified path - -## structs - * `struct kfile` - represents an open file. a `kfile` struct should always be initialized with `kfopen()` or `kfconf()` before use, or the values it contains will be meaningless and useless! - * `enum kfile_kind kind` - either `kfile_closed` if the object does not represent an open file, or `kfile_open` if it does - * `enum kfopen mode` - details about the open file. `kf_new` is only set if the file was created by a `kf_new`-flagged open call - * `struct kiochan chan` - a channel that can be used to read or write to a file - * `u8* content` - a pointer to the start of the file in memory, if it is loaded with `kf_map` - * `sz len` - size of the file as of open. this is not updated by `kiosend()` calls! - -## enums - -### enum kfplace -`enum kfplace` contains constants that refer to standardized locations that may vary across environments. on POSIX, the XDG specification will be respected to allow the user to select her own directories. unlike `kfopen()`, `kfplace()` will also create any necessary directories if `kf_new` or `kf_create` are passed. - - * `kfplace_conf` - this mode is designed specifically for opening configuration files. the configuration directory is determined based on OS, environment variables, and the name of the binary; on POSIX OSes, it will use `$XDG_CONFIG_HOME/argv[0]` as the configuration base, cleanly falling back to platform defaults (`$HOME/.config`, and determining home directories based on $USER or failing that UID if `$HOME` is unset). on OSX, it will use `~/Library/argv[0]`. note that if `null` or the empty string are passed as `file`, `kfconf()` will open a file with the name of the configuration base, instead of treating it as a folder to look for configuration files in. - * `kfplace_home` - open a file in the user's home folder. on POSIX, it will use the value of `$HOME` or get the user's home directory with the appropriate calls if `$HOME` is unset. **THINK CAREFULLY BEFORE USING THIS FUNCTIONALITY - NEVER USE IT FOR STORING CONFIGURATION.** if you use this flag to create dotfiles in the homedir, libk **will** inform the user and instruct her to file a bug report. - * `kfplace_desk` - open a file on the user's desktop, or failing that, home folder. on POSIX, it will use the value of `XDG_DESKTOP_DIR` in `$XDG_CONFIG_HOME/user-dirs.dirs`; on OSX, it will use `$HOME/Downloads`. the same restriction against dotfiles applies here as well. - * `kfplace_dl` - open a file in the user's download directory. on POSIX, it will use the value of `XDG_DOWNLOAD_DIR` in `$XDG_CONFIG_HOME/user-dirs.dirs`; on OSX, it will use `$HOME/Downloads`. - * `kfplace_img` - open a file in the user's image directory. on POSIX, it will use the value of `XDG_PICTURES_DIR` in `$XDG_CONFIG_HOME/user-dirs.dirs`; on OSX, it will use `$HOME/Pictures`. - * `kfplace_vid` - open a file in the user's video directory. on POSIX, it will use the value of `XDG_VIDEOS_DIR` in `$XDG_CONFIG_HOME/user-dirs.dirs`; on OSX, it will use `$HOME/Movies`. - * `kfplace_msc` - open a file in the user's video directory. on POSIX, it will use the value of `XDG_MUSIC_DIR` in `$XDG_CONFIG_HOME/user-dirs.dirs`; on OSX, it will use `$HOME/Music`. - * `kfplace_dat` - open a file in the user data directory, the homedir equivalent of `/usr/share`. on POSIX, it will use the value of `$XDG_DATA_HOME/argv[0]`; on OSX, it will use `$HOME/Library/Application Support/argv[0]`. - * `kfplace_share` - open a file in the system data directory. this is typified by `/usr/share/argv[0]` on POSIX. on OSX, it will use `/Library/Application Support/argv[0]`. - * `kfplace_share_global` - like `kfplace_share`, except without the program-specific suffix. - * `kfplace_cache` - open a file in the user's cache directory. on POSIX, this will generally be `$HOME/.cache/argv[0]`. - -### enum kfopen -`enum kfopen` contains flags that may be OR'd together and passed to `kfopen()` to alter its behavior. - * `kf_read` - opens a file for reading - * `kf_write` - opens a file for writing - * `kf_ascii` - opens a file in ascii mode, where available. if not set, the file will be opened in binary mode. - * `kf_new` - creates a new file with the chosen name if one does not already exist, opening the existing file otherwise - * `kf_create` - creates a new file with the chosen name if it does not already exist, failing otherwise - * `kf_load` - loads the file directly into memory, allowing it to be read via indexing into a pointer rather than kiosend() and kiorecv() calls. this is most useful for quickly opening files for reading. if kf_write is also passed, a kiochan will be created to permit writing to the file via standard mechanisms. note: `kf_load` will use mmap() to load the file where possible, but on systems without `mmap()` or `MMAP_SHARED`, `kf_load` will be implemented using standard file IO primitives. for this reason, writing to the region of memory containing the file is implementation-defined behavior. if you really want to be able to modify the file in memory, use `kf_map` instead - it is guaranteed to memory-map the file, but will fail outright on platforms without memory mapping available, so its use is discouraged unless absolutely necessary, or as a platform-specific optimization. - * `kf_top` - if passed, the kiochan will be positioned at the top of the file. this is the default if kf_read is passed; otherwise, it will be positioned at the end so the file can be appended to. - * if you wish to read the file with kiorecv() calls: `kf_read | kf_top` - * if you wish your writes to overwrite what is already present in the file: `kf_write | kf_top` - * if you wish to append to an existing file: `kf_write | kf_end` - * `kf_end` - positions the `kiochan` at the end of the file. see above. - * `kf_wipe` - if a file already exists, erase its contents before writing. to prevent accidental deletions, it is an error to pass `kf_read | kf_wipe`. - * kfopen also has constants that can be used to spell out file permissions; these are guaranteed to be the standard POSIX octal triple on all platforms. on POSIX platforms, the triple will be passed through to the OS unmodified; on others, libk itself will interpret it. - * `kf_ur` - represents the owner's permission to read the file - * `kf_uw` - represents the owner's permission to write to the file - * `kf_ux` - represents the owner's permission to execute the file - * `kf_gr` - represents the group's permission to read the file - * `kf_gw` - represents the group's permission to write to the file - * `kf_gx` - represents the group's permission to execute the file - * `kf_or` - represents all other users' permission to read the file - * `kf_ow` - represents all other users' permission to write to the file - * `kf_ox` - represents all other users' permission to execute the file - -## macros -if `KFclean` is not defined, the following macros will be: - - * `KFsep` is the string used to separate directories in file paths. it will be set to `"\\"` on Windows, `"/"` on POSIX, and so on. - -## examples -`kfget()` is somewhat tricky to use. this is a feature, not a bug - there are very few legitimate uses for it and it should not be convenient to use. use `kfplace()` instead. - - for (sz len = 80; len < kf_path_max; len << 2) { - char path [len]; - if (kfget(kfplace_desk, (ksmut){len, path}) != kfcond_ok) - continue; - kiosend(env.std, (ksraw){len, path}, null); - break; - } DELETED kgraft/attach.exe.c Index: kgraft/attach.exe.c ================================================================== --- kgraft/attach.exe.c +++ kgraft/attach.exe.c @@ -1,4 +0,0 @@ -#include -stat entry(kenv e) { - return 0; -} DELETED kgraft/graft.h Index: kgraft/graft.h ================================================================== --- kgraft/graft.h +++ kgraft/graft.h @@ -1,12 +0,0 @@ -#ifndef KIgraft -#define KIgraft - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - -#endif DELETED kgraft/kgraft.md Index: kgraft/kgraft.md ================================================================== --- kgraft/kgraft.md +++ kgraft/kgraft.md @@ -1,1 +0,0 @@ -# kgraft DELETED kio/io.h.m Index: kio/io.h.m ================================================================== --- kio/io.h.m +++ kio/io.h.m @@ -1,90 +0,0 @@ -#ifndef KIio -#define KIio -/* - * ~ lexi hale - * this header declares IO primitive functions and - * structures. it is the same for all platforms. - * platform-specific code is found in the *.platform.h - * files. - */ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum kiostream_kind { - kiostream_closed, - // this kiostream cannot be written to - kiostream_file, - // this kiostream represents a file - kiostream_sock, - // this kiostream is attached to a socket, - // UNIX, IP, or otherwise - kiostream_term, - // this socket is being used to communicate - // directly with a human being - kiostream_ansi, - // like kiostream_term, but can also understand - // ANSI control codes - kiostream_pipe, - // this kiostream sends or receives data to - // another running process - kiostream_other - // no fuckin idea -} kiostream_kind; - -typedef struct kiostream { - kiostream_kind kind; - ifelse(target_posix,`yes',` - int platform_fd; - ')dnl -} kiostream; - -typedef struct kiochan { - kiostream in; - // text can be read from this stream - kiostream out; - // text can be written to this stream -} kiochan; - -#include -typedef enum kiocond { - /* to check if a call failed, perform (x >= kiocond_fail) where x - * is that call's return value. more typically however you should - * select explicitly against kiocond_ok or kiocond_partial, since - * those situations will usually need to be handled differently. */ - - kiocond_ok = kiocond_id, // success - - kiocond_partial, // partial read or write - kiocond_poll, // call would block - kiocond_interrupt, // call was interrupted by signal - - kiocond_fail, // action failed - unspecified reason - kiocond_fail_closed_stream, // action failed because stream is closed - kiocond_fail_invalid, - kiocond_fail_io, - kiocond_fail_no_peer, - kiocond_fail_no_space, - kiocond_fail_forbidden, - kiocond_fail_over_quota, - kiocond_fail_pfault, - kiocond_fail_too_big, - kiocond_fail_stream_mismatch, -} kiocond; - -kiocond kiosend(kiochan, ksraw, sz*); // send data to a channel -kiocond kiorecv(kiochan, ksraw*); // receive data from a channel -kmptr kiorecvall(kiochan, kmcell*, kmkind); // automatically allocate a bufer for a channel - // kmkind is only used if kmcell* is null -kiocond kiocon(kiochan, kiochan); // connect one channel to another - -#ifdef __cplusplus -} -#endif - -#endif DELETED kio/kio.md Index: kio/kio.md ================================================================== --- kio/kio.md +++ kio/kio.md @@ -1,1 +0,0 @@ -# kio DELETED kio/send.fn.c Index: kio/send.fn.c ================================================================== --- kio/send.fn.c +++ kio/send.fn.c @@ -1,51 +0,0 @@ -#include -#include -#include -/* send.c - kiosend() "send to channel" - * ~ lexi hale - * kiosend() writes to a channel with an open out stream - */ - -/* we define all platform functions here, - * whether or not they're for the correct - * platform - only the ones actually called - * by the generated code will be linked */ -#include -#include - -kiocond kiosend(kiochan target, ksraw string, sz* len) { - if (target.out.kind == kiostream_closed) return kiocond_fail_closed_stream; - -# ifdef KFenv_posix - /* issue the write syscall here and now so we can - * retrieve errno and report it if necessary */ - - k_platform_syscall_arg args[] = { - target.out.platform_fd, (k_platform_syscall_arg)string.ptr, string.size }; - - struct k_platform_syscall_answer a = k_platform_syscall - (k_platform_syscall_write,3,args); - - sz size = a.ret; - if (size == -1) switch(a.error) { - case k_platform_error_EPERM: return kiocond_fail_forbidden; - case k_platform_error_EINVAL: return kiocond_fail_invalid; - case k_platform_error_EBADF: return kiocond_fail_invalid; - case k_platform_error_EFAULT: return kiocond_fail_pfault; - case k_platform_error_ENOSPC: return kiocond_fail_no_space; - case k_platform_error_EDQUOT: return kiocond_fail_over_quota; - case k_platform_error_EIO: return kiocond_fail_io; - case k_platform_error_EAGAIN: return kiocond_poll; - case k_platform_error_EFBIG: return kiocond_fail_too_big; - case k_platform_error_EINTR: return kiocond_interrupt; - case k_platform_error_EDESTADDRREQ: return kiocond_fail_no_peer; - default: return kiocond_fail; - } -# else - Knoimpl(kiosend,KVos); -# error missing implementation // boring error for plebs -# endif - - if (len != null) *len = size; - return kiocond_ok; -} DELETED kmem/free.fn.c Index: kmem/free.fn.c ================================================================== --- kmem/free.fn.c +++ kmem/free.fn.c @@ -1,22 +0,0 @@ -#include -#include -#include -#include - -/* free.fn.c - kmfree() "arbitrary free" - * ~ lexi hale - * kmfree() frees memory allocated in any manner. - * it ignores non-dynamically allocated memory, - * returning kmcond_unnecessary. to check for - * success, compare result < kmcond_fail. - */ - -kmcond kmfree(kmptr ptr) { - if (ptr.kind <= kmkind_fail) return kmcond_unnecessary; - switch (ptr.kind) { - case kmkind_heap: return kmheapf(ptr.ref); - } - - return kmcond_unhandled; -} - DELETED kmem/heapa.fn.c Index: kmem/heapa.fn.c ================================================================== --- kmem/heapa.fn.c +++ kmem/heapa.fn.c @@ -1,77 +0,0 @@ -#include -#include -#include -#include -/* heapa.c - kmheapa() "heap alloc" - * ~ lexi hale - * kmheapa() allocates a pointer on the heap à la libc malloc() - * see also: kmheapf() "heap free" - */ - -/* arch specific headers */ -#ifdef KFenv_posix -# include -#endif - -/* we define all our platform functions here, whether or not - * they're for the correct platform - only the ones that are - * called by the preprocessed form of the code will actually - * be linked, linker errors are our friend here! */ -extern void* kmem_platform_mmap(void* addr, - unsigned long sz, unsigned long prot, unsigned long flags, - unsigned long fd, unsigned long off); - -void* kmheapa(sz len) { - /* allocate an object on the heap and return - * a pointer, or NULL if the allocation failed. */ - union { - void* raw; - ubyte* byte; - kmbox* header; - } region; - /* we need to allocate space for the - * header and for the actual object */ - sz region_size = sizeof(kmbox) + len; - -# ifdef KFenv_posix - /* posix APIs - we've got it easy. currently for - * nonlinear heap allocation kmheapa simply uses - * m(un)map and lets the kernel worry about it. it - * may ultimately be worth replacing this with a - * more sophisticated implementation, most likely an - * existing allocator like jemalloc, though i'm wary - * of including outside code - it creates a licensing - * mess and i'd prefer libk to be AGPLv3 across the - * board. possibility: include hooks for multiple - * allocators, allowing the user to select & link in - * her preferred allocator at compile time? */ - - /* because munmap needs to be informed of the size of - * the region we are going to unmap, we need to store - * that information in the region that we are mapping. - * the user will receive an adjusted pointer that can - * be adjusted to point a field of type size_t that - * contains the size of the allocated space.*/ - - region.byte = kmem_platform_mmap(null, region_size, - posix_prot_read | posix_prot_write, - posix_flag_anonymous | posix_map_shared, -1, 0); - /* impl note: while per manpage fd is "ignored" - * for MAP_ANONYMOUS, "some implementations" require - * a value of -1 */ - - if (region.raw == (void*) -1) return null; - /* worth retrieving errno? discuss */ - -# else - Knoimpl(kmheapa,KVos); -# error missing implementation -# endif - - void* const object = (region.byte + sizeof (kmbox)); - - region.header -> kind = kmkind_heap; - region.header -> size = len; - - return object; -} DELETED kmem/heapao.fn.c Index: kmem/heapao.fn.c ================================================================== --- kmem/heapao.fn.c +++ kmem/heapao.fn.c @@ -1,17 +0,0 @@ -#include -#include -/* heapao.fn.c - kmheapao() "allocate heap object" - * ~ lexi hale - * kmheapao() allocates a region in heap memory - * and returns a kmptr struct referencing that - * newly allocated region. - */ - -kmptr kmheapao(sz size) { - void* ptr = kmheapa(size); - kmptr p = { - .kind = (ptr != null ? kmkind_heap : kmkind_fail), - .ref = ptr, - .shred = false, - }; return p; -} DELETED kmem/heapf.fn.c Index: kmem/heapf.fn.c ================================================================== --- kmem/heapf.fn.c +++ kmem/heapf.fn.c @@ -1,48 +0,0 @@ -#include -#include -#include -#include -/* heapf.c - kmheapf() "heap free" - * ~ lexi hale - * kmheapf() frees a region on the heap à la libc free() - * see also: kmheapa() "heap alloc" - */ - -/* arch specific headers */ -#ifdef KFenv_posix -# include -#endif - -/* we define all our platform functions here, whether or not - * they're for the correct platform - only the ones that are - * called by the preprocessed form of the code will actually - * be linked, linker errors are our friend here! */ -extern int kmem_platform_munmap(void* addr, unsigned long sz); - -kmcond kmheapf(void* ptr) { - /* take an object allocated on the heap and free it, - * returning kmcond_ok on success or an appropriate - * value on failure. */ - struct kmbox* header = (kmbox*) - (((ubyte*)ptr) - sizeof (kmbox)); - sz const total = header -> size + sizeof (kmbox); - - if (header -> kind != kmkind_heap) return kmcond_mismatch; -# ifdef KFenv_posix - /* currently allocation is handled on posix by naive use - * of MAP_ANONYMOUS. munmap needs to be told the size of - * the region to unmap (free), which kmheapa() stores at - * (ptr - sizeof sz). see kmheap.c for details. */ - - if(kmem_platform_munmap(header, total) == -1) { - /* we don't need to bother recovering errno; - * there's only one possible munmap error */ - return kmcond_bad_address; - } - -# else - Knoimpl(kmheapf,KVos); -# error missing implementation -# endif - return kmcond_ok; -} DELETED kmem/kmem.md Index: kmem/kmem.md ================================================================== --- kmem/kmem.md +++ kmem/kmem.md @@ -1,171 +0,0 @@ -# kmem - -**kmem** is a libk module that contains various functions for memory allocation and deallocation. it uses the **short** naming convention with the glyph `m`. - -kmem allocators can work in several different ways. they can allocate memory directly from the heap (like `kmheapa()` and `kmlina()`), use a header that has already been allocated by another function, or allocate memory only from a pre-allocated pool. linear allocation with pool allocation is particularly useful, as it permits the very rapid allocation and deallocation of lots of objects with only a few adjustments to the heap, and no possibility of fragmentation or need for expensive algorithms like `malloc()` or `kmheapa()` - -## module functions - -kmem supplies two module-level functions, used to interact with the `kmptr` container type. - - * `kmfree(kmptr) → void` - free, downref, or ignore the pasted object as appropriate - * `kmshred(kmptr) → void` - free, downref, or ignore the pasted object as appropriate. if deallocating, zero its contents - * `kmstat(void*) → kmptr` - convenience function to wrap a pointer to a non-managed object in a `kmptr` struct, so it can be passed to functions that accept arbitrary objects. `kmptr p = kmstat(raw)` is equivalent to `kmptr p = { kmkind_none, raw, NULL }`. - * `kmtaint(&kmptr) → void` - "taints" a `kmptr` object by setting it to be shredded when freed. this may be desirable if the object pointed to contains privileged information. - * `kmzero(void*,sz) → void` - zeroes a region of memory - * `kmozero(kmptr) → void` - zeroes an object in memory - * `kmcopy(void* dest, void* src, sz) → void` - copies one region of memory to another - * `kmdup(kmptr) → kmptr` - duplicates an object in memory, allocating it as sibling of the original - -## types - -kmem defines the following types: - - * `enum kmkind` - enumerates allocation strategies - * `struct kmptr` - abstract pointer object - * `struct kmcell` - abstract memory cell - * `struct kmref` - a reference-counted cell - * `struct kmnode` - a node in an allocation tree - * `struct kmpool` - a memory pool - -`kmptr` and `kmcell` are both very similar. the difference is that a kmptr points to a region in memory and can be passed around freely. a `kmcell` is the actual in-memory representation of an allocation cell. a `kmcell` cannot be usefully instantiated; rather, it is downcast from an actual cell type (e.g. `kmnode n; kmcell* s = (kmcell*)(&n)`) - - -### kmkind - -`kmkind` is an enum that specifies an allocation function. - - * `kmkind_none` - no allocation - * `kmkind_lin` - linear heap allocation - * `kmkind_heap` - random heap allocation - * `kmkind_pool` - pool allocation - * `kmkind_ref` - reference-counting allocation - * `kmkind_tree` - tree allocation - -### kmptr - -kmem functions can operate on both raw pointers and the `kmptr` struct type. `kmptr` is a generic struct that can contain any kind of pointer. this is useful if you wish to allocate different objects in different manners, but pass them on into a single interface. - -memory pointed at by `kmptr` pointers can be freed either with the usual specialized function, or by passing the `kmptr` structure itself to the generic function `kmfree`, which will handle it appropriately, even if it's a pointer to a garbage-collected object or to a static region of memory. - -a `kmptr` has the following layout: - - * `kmkind kind` - codes the type of pointer; `kmkind_none` indicates a non-allocated pointer to a static (global or on-stack) object. - * `kmshred shred` - an enum. if `kmshred_yes`, the value will be zeroed or otherwise made unreadable on free. if no, `kmfree` will consult `src` for shred policy if it is not NULL. - * `void* ref` - the raw pointer enclosed by `cell` - * `kmcell* cell` - a pointer to an object enclosure, typically either a memory pool or a referencing-counting object. NULL if not needed. - -the convenience function `kmstat(void*) → kmptr` wraps a pointer to a static object in a `kmptr` struct. - -### struct kmcell - -`kmcell` is a stub struct used to disambiguate between source types. a "source" is an object that can hold an allocated object, such as the heap, a memory pool, a fixed-length array on stack, or a fixed-length global array. all values produced by a kmem allocation function can be cast to `kmcell*`, and have an intial field `id` that contains a `kmcell`. - - * `kmkind kind` - kind of cell - * `size_t size` - size of cell (data plus all fields) - * `kmshred shred` - shredding flag - -### struct kmref - -`kmref` is a struct that constitutes the in-memory representation of a reference-counted cell. - - * `kmcell id = { .kind = kmkind_ref, … } ` - kind of cell - * `size_t refs` - number of active references - * `kmcell* src` - source, if any - * `char data[]` - content of cell - -### struct kmnode - -`kmnode` is the header struct for tree nodes. all tree nodes pointers can yield a `kmnode` structure by subtracting `sizeof (kmnode)` from the pointer. a utility function and macro are made available to automate this safely. - - * `kmcell id = { .kind = kmkind_tree, … } ` - kind of cell - * `kmnode* parent` - parent node - * `kmnode* child` - first child node - * `kmnode* lastchild` - last child node - * `kmnode* prev` - previous sibling, NULL if first - * `kmnode* next` - next sibling, NULL if last - -### struct kmpool - - * `kmcell id = { .kind = kmkind_pool, … } ` - kind of cell - * `size_t cellsz` - size of individual pool cells - * `kmpoolcell* top` - pointer to most recently allocated pool cell - * `kmpoolcell* bottom` - pointer to most recently freed pool cell - * `kmpoolcell data[]` - content of cell - -#### struct kmpoolcell - - * `kmpoolcell* last` - pointer to last element allocated before this one - * `char data[]` - pool data - -### enum kmshred - -`kmshred` is an enum used to indicate whether an object should be "shredded" (written over) in memory when it's deleted. this is a useful means to ensure that privileged information is not accidentally left in memory after use. if the shredding mechanism is not useful, compile libk with the flag `KFmem_noshred` to exclude its functions and fields. - - * `kmshred_no = 0` - marks an object not to shred on free - * `kmshred_yes = 1` - marks an object to shred on free - -## naming convention - -kmem function names are based on the **method** of allocation and the **action** being performed. methods are listed in the section below. kmem defines a number of standardized actions, though not every method uses every action. the character listed in brackets is suffixed to the name of the method to produce a function name: for instance, `kmheapa` will allocate memory on the heap, while `kmrefd` will decrement the reference count of its argument. - - * initialize [i] - initializes a memory store on the heap - * initialize fixed [if] - initialize a memory store on the stack or in a fixed-size global - * allocate [a] - return a raw pointer to a new region of memory of the given size, ready to write, or NULL if not possible. contents of that region undefined. takes parameter (size_t sz). - * allocate pointer object [ao] - like *allocate*, but returns a `kmptr` instead of a raw `void*`. - * zero [z] - allocate a new region of memory and zero it before returning it for writing. - * zero pointer object [zo] - like *zero*, but returns a `kmptr` instead of a raw `void*`. - * free [f] - free a section of memory, either decrementing a reference count or returning it to whatever pool it came from. - * shred [s] - destroy whatever was in the segment of memory, then return it to the pool it came from. - * destroy [x] - tears down a memory store - * upref [u] - increments a reference counter - -## methods - -kmem currently supports the following methods of memory management, along with which methods are defined for it. (note that `a` implies `z` and `f` implies `s`). a method may be excluded from a libk binary by defining the flag `KFmem_no[name]`, e.g. `KFmem_noheap`. - -the fastest allocator is the linear allocator, which should be sufficient for most simple programs. it allocates and deallocates memory simply by resizing the stack; there is no fragmentation, but objects must be freed in the order they are allocated. however, entire groups of objects can be freed at once at very little cost. - - * `lin` [iax] - linear heap allocator - * `kmlini(void) → void*` - return a pointer to the current top of the heap - * `kmlina(size_t) → void*` - allocate space on the heap and increase its size appropriately - * `kmlinz(size_t) → void*` - allocate zero-filled space on the heap and increase its size appropriately - * `kmlinx(void*) → void*` - returns the top of the heap to the location specified, freeing all memory allocated since the call to kmlini() or `kmlina()` that produced it - * `heap` [af] - random heap allocation - * `kmheapa(size_t) → void*` - allocate - * `kmheapz(size_t) → void*` - zero-allocate - * `kmheapao(size_t) → kmptr` - allocate pointer object - * `kmheapzo(size_t) → kmptr` - zero-allocate pointer object - * `kmheapf(void*) → void` - free - * `kmheaps(void*) → void` - shred - * `ref` [afu] - reference-counted heap object - * `kmrefa(kmcell*, size_t) → void*` - allocate - * `kmrefz(kmcell*, size_t) → void*` - zero-allocate - * `kmrefao(kmcell*, size_t) → void*` - allocate pointer object - * `kmrefzo(kmcell*, size_t) → void*` - zero-allocate pointer object - * `kmreff(void*) → void` - downref; free if last ref - * `kmrefs(void*) → void` - downref and mark for shred on last ref - * `pool` [ixaf] - memory pool - * `kmpooli(kmcell*, size_t sz, size_t n) → kmpool*` - initialize a fixed memory pool (a pool of `n` cells of length `sz`) - * `kmpoolx(kmpool*) → void` - tear down a memory pool - * `kmpoola(kmpool*) → void*` - allocate from pool - * `kmpoolz(kmpool*, size_t) → void*` - zero-allocate from pool - * `kmpoolao(kmpool*, size_t) → void*` - allocate pointer object - * `kmpoolzo(kmpool*, size_t) → void*` - zero-allocate pointer object - * `kmpoolf(void*) → void` - downref; free if last ref - * `kmpools(void*) → void` - downref and mark for shred on last ref - * `tree` [af] - uses a node-child strategy. when a node is freed, all of its children are automatically freed as well. - * `kmtreea(kmcell* src, void* parent, size_t) → void*` - create a tree node. if `parent` is NULL, the node will the top of a new tree. if src is null, allocate on-heap. - * `kmtreez(kmcell* src, void* parent, size_t) → void*` - like `kmtreea` but zeroed - * `kmtreeao(kmcell* src, void* parent, size_t) → kmptr` - like `kmtreea` but returns a `kmptr` - * `kmtreezo(kmcell* src, void* parent, size_t) → kmptr` - like `kmtreez` but returns a `kmptr` - * `kmtreef(void*) → kmptr` - frees a node and all its children - -## macros - -kmem defines the following macros. - - * `Kmsz(array)` - a convenience macro to return the number of elements in a static array. inserts the text `( sizeof (array) / sizeof (array) [0] )` - * `Kmszs(type, struct)` - a convenience macro to return the size of a struct. requires compound literals. - * `Kmszsa(type, array)` - calculates the number of elements in an array of a given struct type. inserts the text `( sizeof ( (type) array ) / sizeof (type) )` - * `Kmpsa(type, struct)` - a convenience macro to insert a "pascal struct array" - that is, a struct array prefixed with a size value. this is equivalent to `Kmszsa(type, array), array` DELETED kmem/mem.h Index: kmem/mem.h ================================================================== --- kmem/mem.h +++ kmem/mem.h @@ -1,82 +0,0 @@ -#ifndef KImem -#define KImem -#include - -#ifndef KFclean -# define Kmsz(e) ( sizeof (e) / sizeof (e) [0] ) -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum kmcond { - kmcond_ok, - kmcond_unnecessary, - - kmcond_fail, - kmcond_unhandled, - kmcond_mismatch, - kmcond_bad_address, -} kmcond; - -typedef enum kmkind { - kmkind_none, - kmkind_fail, - kmkind_linear, - kmkind_heap, - kmkind_pool, - kmkind_ref, - kmkind_tree -} kmkind; - -typedef enum kmshred { - kmshred_yes, - kmshred_no -} kmshred; - -typedef struct kmbox { - kmkind kind; - sz size; - kmshred shred; -} kmbox; - -typedef struct kmcell { - kmbox header; - sz refs; - struct kmcell* src; -} kmcell; - -typedef struct kmnode { - kmbox header; - struct kmnode* origin, - * branch, - * next, - * prev; -} kmnode; - -typedef struct kmptr { - kmkind kind; - kmshred shred; - void* ref; -} kmptr; - -/* heap functions */ - -void* kmheapa(sz); -kmptr kmheapao(sz); -kmcond kmheapf(void*); - -/* generic functions */ - -kmcond kmfree(kmptr); -kmkind kmtell(void*); -void kmzero(void*,sz); -void kmozero(kmptr); - - -#ifdef __cplusplus -} -#endif - -#endif DELETED kmem/platform.mmap.fn.x86.lin.64.s Index: kmem/platform.mmap.fn.x86.lin.64.s ================================================================== --- kmem/platform.mmap.fn.x86.lin.64.s +++ kmem/platform.mmap.fn.x86.lin.64.s @@ -1,25 +0,0 @@ -bits 64 -%include "../arch/posix/x86.lin.64.s" -%include "../arch/x86.cdecl.64.s" -; vim: ft=nasm - -global kmem_platform_mmap -kmem_platform_mmap: - ; to call mmap, we need to translate the cdecl64 - ; register arguments to their appropriate syscall64 - ; registers. these are mostly the same, with one - ; obnoxious exception. the NOPs have been written - ; in as comments to aid in understanding. - - mov sys.reg.1, ccall.reg.0 ;nop - rdi → rdi - mov sys.reg.2, ccall.reg.1 ;nop - rsi → rsi - mov sys.reg.3, ccall.reg.2 ;nop - rdx → rdx - mov sys.reg.4, ccall.reg.3 ; OP - rcx → r10 - mov sys.reg.5, ccall.reg.4 ;nop - r8 → r8 - mov sys.reg.6, ccall.reg.5 ;nop - r9 → r9 - - mov sys.reg.0, sys.mmap - sys.call - - mov ccall.reg.ret, sys.reg.ret ; rax → rdi - ret DELETED kmem/platform.munmap.fn.x86.lin.64.s Index: kmem/platform.munmap.fn.x86.lin.64.s ================================================================== --- kmem/platform.munmap.fn.x86.lin.64.s +++ kmem/platform.munmap.fn.x86.lin.64.s @@ -1,18 +0,0 @@ -bits 64 -%include "../arch/posix/x86.lin.64.s" -%include "../arch/x86.cdecl.64.s" -; vim: ft=nasm - -global kmem_platform_munmap -kmem_platform_munmap: - ; to call munmap, we need to translate the cdecl64 - ; register arguments to their appropriate syscall64 - ; registers. all those that matter are the same. - mov sys.reg.1, ccall.reg.0 ;nop - rdi → rdi - mov sys.reg.2, ccall.reg.1 ;nop - rsi → rsi - - mov sys.reg.0, sys.munmap - sys.call - - mov ccall.reg.ret, sys.reg.ret ; rax → rdi - ret DELETED kmem/zero.fn.c Index: kmem/zero.fn.c ================================================================== --- kmem/zero.fn.c +++ kmem/zero.fn.c @@ -1,29 +0,0 @@ -#include -#include -#include - -/* zero.fn.c - kmzero() "zero region" - * ~ lexi hale - * kmzero() zeroes a region of memory. it is - * libk's equivalent to libc's bzero(). - */ - -#define canzero(bits) (_canzero(sizeof(u##bits), bits, size)) -static bool _canzero(sz type, u8 bits, sz size) { - return (sizeof (u64) * byte_bits == 64 && size >= sizeof (u64)); -} - -void kmzero(void* memory, sz size) { - ubyte* elt = memory, - * const end = elt + size; - - if canzero(64) { - u64* p = (u64*) elt; - sz segments = (end - elt) / sizeof (u64); - - for(;(p - (u64*)elt) < segments; ++p) *p = 0; - elt = (ubyte*) p; - } - - for (;elt < end; ++elt) *elt = 0; -} DELETED kmsg/kmsg.md Index: kmsg/kmsg.md ================================================================== --- kmsg/kmsg.md +++ kmsg/kmsg.md @@ -1,2 +0,0 @@ -# kmsg -kmsg is libk's inter-process communication library. DELETED kmsg/msg.h Index: kmsg/msg.h ================================================================== --- kmsg/msg.h +++ kmsg/msg.h @@ -1,12 +0,0 @@ -#ifndef KImsg -#define KImsg - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - -#endif DELETED knet/knet.md Index: knet/knet.md ================================================================== --- knet/knet.md +++ knet/knet.md @@ -1,1 +0,0 @@ -# knet DELETED knet/net.h Index: knet/net.h ================================================================== --- knet/net.h +++ knet/net.h @@ -1,12 +0,0 @@ -#ifndef KInet -#define KInet - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - -#endif DELETED knum/knum.md Index: knum/knum.md ================================================================== --- knum/knum.md +++ knum/knum.md @@ -1,2 +0,0 @@ -# knum -knum contains libk's functions for dealing with numbers. knum is intended to be much more consistent and generic than libc, with support for arbitrary bases, arbitrarily sized integers, and interfaces at varying levels of abstraction. (for instance, converting numbers to strings will not require invoking a string formatting routine or IO function; string formatting routines will invoke the same conversion functions that users can.) integers larger and smaller than processor types should also be supported, through functions over arrays and bitfield structs. DELETED kproc/kproc.md Index: kproc/kproc.md ================================================================== --- kproc/kproc.md +++ kproc/kproc.md @@ -1,2 +0,0 @@ -# kproc -kproc is libk's library for starting, handling, and controlling userspace processes. DELETED kproc/proc.h Index: kproc/proc.h ================================================================== --- kproc/proc.h +++ kproc/proc.h @@ -1,12 +0,0 @@ -#ifndef KIproc -#define KIproc - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - -#endif DELETED kstr/ks_to_int.c Index: kstr/ks_to_int.c ================================================================== --- kstr/ks_to_int.c +++ kstr/ks_to_int.c @@ -1,11 +0,0 @@ -#include -#include -#include - -kscond ks_to_int(ksraw str, enum ksconv mode, u8* dest, sz size) { - u8 base = mode & 0xFF; - kcendian endian = (mode & ksconv_endh ? kcendian_high : - mode & ksconv_endl ? kcendian_low : - kcendian_system); - return kscond_unimplemented; -} DELETED kstr/kstr.md Index: kstr/kstr.md ================================================================== --- kstr/kstr.md +++ kstr/kstr.md @@ -1,75 +0,0 @@ -# kstr - -**kstr** is the libk string library. it uses the **short** naming convention with the glyph `s`. **kstr** implies `#include `. - -## types - -### struct kstr -`struct kstr` is a structure for holding pascal strings (length-prefixed strings). it is the basic libk string type. **note:** if `ptr.ref` ≠ NULL and `sz` = 0, the string's length is unknown and should be calculated by any function that operates on a kstr, storing the result in the object if possible. - * `sz size` - length of string, excluding any null terminator - * `kmptr ptr` - pointer to string in memory - -### struct ksraw -`struct ksraw` is like `kstr` except it uses raw `char` pointers instead of a `kmptr`. - * `sz size` - length of string, excluding any null terminator - * `char* ptr` - pointer to string in memory - -### struct ksbuf -`struct ksbuf` is a structure used to hold buffers. - * `sz size` - maximum size of buffer, including any null terminator - * `char* buf` - region of memory to store buffer in - * `ksalloc strat` - allocation strategy - * `kmkind rule` - kind of allocator to use. only needs to be set if `where` is NULL. see [kmem](../kmem/kmem.md). - * `kmcell* where` - where to allocate the object, in case of pool or tree allocation. - -### struct kschain -`struct kschain` is a structure used for string accumulators that works by aggregating pointers to strings, instead of copying the strings themselves. - * `kschain_kind kind` - kind of chain - * `kmkind rule` - kind of allocation to use if `kind` ≠ `kschain_kind_linked` - * `pstr* ptrs` - pointer to pointer list - * `sz ptrc` - number of pointers - * `sz size` - total amount of space in `ptrs` - -#### enum kschain_kind - * `kschain_kind_block` - occupies a single block of memory - * `kschain_kind_linked` - uses a linked list, allocated and deallocated as necessary - -### enum ksalloc -`enum ksalloc` is an enumerator that tells libk what strategy to use when filling a `ksbuf` or `kschain` struct. - * `ksalloc_static` - do not allocate memory, fill an already-allocated, statically-sized array. - * `ksalloc_alloc` - allocate a string in memory using the specified kind of allocator. - * `ksalloc_dynamic` - fill an already-allocated array if possible, allocate a string in memory if the string length exceeds available space. - -## functions - -### kssz -`size_t kssz(char* str, size_t max)` returns the number of characters in a C string, **including** the final null. will count at most `max` characters if `max` > 0. - -### kstr -`kstr kstr(char* str, size_t max)` takes a C string and returns a P-string, calculating the length of `str` and storing it in the return value. `max` works as in `kssz`. - -### kstoraw -`ksraw ksref(kstr)` is a simple convenience function that returns the `ksraw` form of a `kstr`. - -### 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. - - char mem[512]; - kmptr text = <...>; - char* src = <...>; - kmbuf buf = { sizeof mem, &mem, kmkind_none }; - kstr chain[] = { - Kstr(""), - ksref(text), - Kstr("") - }; - 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 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. DELETED kstr/str.h Index: kstr/str.h ================================================================== --- kstr/str.h +++ kstr/str.h @@ -1,61 +0,0 @@ -#ifndef KIstr -#define KIstr - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct kstr { - sz size; - kmptr ptr; -} kstr; - -typedef struct ksraw { - sz size; - const char* ptr; -} ksraw; - -typedef struct ksmut { - sz size; - char* ptr; -} ksmut; - -#include -typedef enum kscond { - kscond_ok = kscond_id, - kscond_fail, - kscond_unimplemented, - kscond_nonnumeric, -} kscond; - -enum ksconv { - ksconv_default = 0, - - ksconv_raw = 1, - ksconv_bin = 2, - ksconv_oct = 8, - ksconv_dec = 10, - ksconv_hex = 16, - ksconv_b32 = 32, - - ksconv_partial = 1 << 7, - ksconv_nopfx = 1 << 8, - ksconv_endh = 1 << 9, - ksconv_endl = 1 << 10, -}; - -kscond ks_to_int(ksraw str, - enum ksconv mode, - u8* dest, sz size); - -kscond ks_of_int(u8* number, sz size, - enum ksconv mode, - char* bufstart, sz bufsize); - -#ifdef __cplusplus -} -#endif - -#endif DELETED kterm/kterm.md Index: kterm/kterm.md ================================================================== --- kterm/kterm.md +++ kterm/kterm.md @@ -1,1 +0,0 @@ -# kterm DELETED kterm/term.h Index: kterm/term.h ================================================================== --- kterm/term.h +++ kterm/term.h @@ -1,12 +0,0 @@ -#ifndef KIterm -#define KIterm - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - -#endif ADDED mod/kbuild/kbuild.md Index: mod/kbuild/kbuild.md ================================================================== --- mod/kbuild/kbuild.md +++ mod/kbuild/kbuild.md @@ -0,0 +1,25 @@ +# kbuild + +**kbuild** is a small build tool designed to maximize the ease of writing portable libk applications. kbuild is included because most C compilers automatically assume their input should be linked with libc, inserting several implicit flags that need to be changed or removed for libk. while it is fully possible to build libk programs without using kbuild, it can be somewhat tedious, especially for code too small to warrant a makefile. + +kbuild is not intended to replace complex build systems like gmake, nor is it intended to be used only with libk binaries. + +unlike most build systems, kbuild does not use a makefile or project file. kbuild is invoked simply by passing it a list of C files, which it will build and link together. kbuild recursively calculates dependencies for each file, by parsing `#include` directives and kbuild-specific pragmas. + +kbuild passes a series of variables through to the preprocessor, providing information about the current system that can be useful for conditional compilation. + +these are the phases of the kbuild process: + + +# pragmas + +to support automatic linking, kbuild handles the following pragmas. + + * `#pragma k lib` adds a library name to the list of libraries to include. on linux, pkg-config will be used to locate the library and add the appropriate flags to the command line. link flags will be applied to the final binary, while all other flags will apply only to the compilation of the source unit; for this reason, library pragmas should precede `#include` directives. `#pragma k lib c` can be used to link the standard C library; if it is present, libk will not be linked unless it is explicitly named (`#pragma k lib k`). + * `#pragma k flag` indicates that a file should be compiled with a specific flag. this will be passed directly to the compiler, so it should be used within `#if` statements to detect the operating system and/or compiler being used. + * `#pragma k link` adds a flag to the final linker command line + * `#pragma k env` requests a specific environment variable be passed to the compiler with the -D flag; `#pragma k env EDITOR` will create a macro `KE_EDITOR` if `$EDITOR` is set in the user's environment. on encountering an `env` predicate for a variable it has not already passed, `kbuild` will terminate parsing and re-run cpp with the new variable set. this allows the use of environment variables to make first-pass decisions, though its effects may be somewhat unintuitive - a `#pragma k env` directive can affect the evaluation of a macro expression earlier in the same file. `#pragma k env` without an argument requests all host environment variables be passed through to the program. + +pragmas are scanned from the preprocessed file, meaning `#if` blocks can be used to control whether a library is linked or flag is passed. + +the `_Pragma` form is not currently supported, but supporting it is a long-term goal. ADDED mod/kconf/kconf.md Index: mod/kconf/kconf.md ================================================================== --- mod/kconf/kconf.md +++ mod/kconf/kconf.md @@ -0,0 +1,64 @@ +# kconf + +while there are a number of existing configuration parser libraries out there, they all have their problems, and all depend on libc. since configuration string parsing is a fairly core functionality that would force many programs to either write their own code or drag libc back into their dependencies, supplying a basic parser with libk makes sense. + +## basic principles + +kconf is intended to be fast, lightweight, transparent, and low-overhead. + +to initialize a kconf structure, we begin by supplying a list of *atoms.* like in Xlib, an atom is a performant way to reference a known string of text from a compiled program. we can do this in two different ways, either by generating the values ourselves at compile time with an enum, or by generating them at runtime using the appropriate kconf interface. note that if you supply your own with an enum, because zero is used for in-band error signalling, the first atom should always be an error token. alternately, it may be explicitly set to a non-zero number and a return value of zero can be checked for implicitly or against a literal 0. + +note that, in a recurring libk pattern, if an element count of zero (or `null`) is passed to a function that takes an array of nullables, that function will treat its array argument as a null-terminated array to be counted at runtime. + +kconf is initialized by filling out the struct `kconf` with the parameters of operations and then passing it to the appropriate function. + + enum atoms { parse_error, user, pw, email, atomct }; + kconf kc = { + .decl = { atomct, { + { user, {4, "user" } }, + { pw, {8, "password"} }, + { email, {5, "email" } } + }} + }; + + /* with runtime counts: + * enum atoms { parse_error, user, pw, email }; + * kconf kc = { + * .decl = { null, { + * { user, {0, "user" } }, + * { pw, {0, "password"} }, + * { email, {0, "email" } }, + * { null } + * }} + * }; */ + + /* with macros: + * enum atoms { parse_error, user, password, email }; + * kconf kc = { + * .decl = {Kmpsa(kconf_decl, { + * Kconf_atom(user), + * Kconf_atom(password), + * Kconf_atom(email) + * })}; + * }; */ + +## types + +### struct kconf + * `union { kconf_decl decl; kconf_gen gen; };` + +### struct kconf_atom + + * `sz key` + * `ksraw string` + +### struct kconf_pair + + * `kconf_atom atom` + * `ksraw* dest` + + +## macros + + * `Kconf_atom(atom)` - a convenience macro for definining an pre-defined atom structure (`kconf_atom`), under the assumption that the C enumerator name is the same as the string used to name the field in the configuration file. inserts the text `{atom, {sizeof(#atom), #atom}}` + * `Kconf_atom_pfx(pfx,atom)` - a convenience macro for definining an pre-defined atom structure (`kconf_atom`), under the assumption that the C enumerator name is the string used to name the field in the configuration file with a prefix attached. inserts the text `{pfx##atom, {sizeof(#atom), #atom}}` ADDED mod/kcore/__stack_chk_fail.fn.c Index: mod/kcore/__stack_chk_fail.fn.c ================================================================== --- mod/kcore/__stack_chk_fail.fn.c +++ mod/kcore/__stack_chk_fail.fn.c @@ -0,0 +1,3 @@ +#include + +void __stack_chk_fail(void) { kstop(-1); } ADDED mod/kcore/boot.rt.c Index: mod/kcore/boot.rt.c ================================================================== --- mod/kcore/boot.rt.c +++ mod/kcore/boot.rt.c @@ -0,0 +1,17 @@ +#include +extern stat entry(kenv); + +unsigned long long +_boot(unsigned int argc, /* argument count */ + char** argv, /* arguments */ + char** envp /* environment */ ) { + kenv e = { + /* TODO: determine terminal class and set term vs ansi correctly! */ + { {kiostream_term, 0}, {kiostream_term, 1} }, // chan std + { {kiostream_closed}, {kiostream_term, 2} }, // chan err + argc, argv, + null // needs parsing + }; + return entry(e); +} + ADDED mod/kcore/boot.rt.x86.lin.64.s Index: mod/kcore/boot.rt.x86.lin.64.s ================================================================== --- mod/kcore/boot.rt.x86.lin.64.s +++ mod/kcore/boot.rt.x86.lin.64.s @@ -0,0 +1,67 @@ +; vim: ft=nasm +bits 64 +%include "../arch/posix/x86.lin.64.s" +global _start:function +extern _boot +extern entry + +_start: + mov rbp, 0 ; zero the stack base ptr. + ; attempted fix for a difficult-to- + ; reproduce bug + + mov rdi, 0 ; zero rdi - because argc + ; is 32 bits long, we have to store + ; its value in the lower half of rdi. + ; this ensures that the upper half is + ; zeroed as well. + + mov edi, [rsp + 0] ; sizeof arguments + ; first argument to _boot(): argc + ; this is a 32-bit signed(??) integer + ; that is equal to the number of + ; elements in argv (see below). it is + ; not strictly necessary, because argv + ; is per spec always null-terminated, + ; but we pass it just in case. + + lea rsi, [rsp + 8] ; &arguments + ; 2nd argument to _boot(): ptr to argv + ; this points to an array of strings + ; containing the program's command line + ; arguments. _boot() does not need to + ; parse this, but it does need to store + ; it in the structure passed to main() + + lea rdx, [rsp + 16] ; &environment + ; third argument to _boot(): ptr to envp + ; this points to the list of environment + ; variables for the running program. it + ; is the responsibility of _boot to parse + ; this list and arrange it into a set of + ; legible and useful C arrays. + + mov rax, 0 ; zero out %rax + ; this is required by the C ABI, and is + ; reputedly necessary for compatibility + ; with icc, intel's own proprietary C + ; compiler. + + call _boot ; invoke the start function + ; that will set up the runtime and + ; construct the necessary structures + ; that will be bassed to libc. + + ; boot has returned and left its + ; return value in the register %rax. + ; regardless of the size of the + ; return value of main(), _boot always + ; returns the system word length. + + mov sys.reg.1, sys.reg.ret ; fill in + ; the return value as exit's argument + + mov sys.reg.0, sys.exit ; set %rax to + ; the syscall number of exit + + sys.call ; invoke the kernel ADDED mod/kcore/core.h Index: mod/kcore/core.h ================================================================== --- mod/kcore/core.h +++ mod/kcore/core.h @@ -0,0 +1,166 @@ +#ifndef KIcore +#define KIcore +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kvar { + ksraw name; + ksraw val; + char* platform; +} kvar; + +typedef struct kenv { + kiochan std; + kiochan err; + sz argc; char** argv; + kvar* vars; +} kenv; + +/* i'm really sorry okay */ +typedef +#if (__STDC_VERSION__ >= 199901L) + _Bool bool; +#endif +enum +#if !(__STDC_VERSION__ >= 199901L) + bool /* enum bool { */ +#endif +{ + false = 0, no = 0, + true = 1, yes = 1 +} +#if !(__STDC_VERSION__ >= 199901L) + bool /* } bool ; */ +#endif +; + +#ifndef KFclean +# include +# define Kokay(cond) (((cond) % kglobal_module_offset) == 0) +# if (__STDC_VERSION__ >= 199901L) ||\ + (__cplusplus >= 201103L) +# define KVvm_args __VA_ARGS__ +# define KVvm_spec ... +# define KFfeat_variadic_macro +# else +# define KVvm_args K_TEMP_M2QD52 +# define KVvm_spec K_TEMP_M2QD52 +# endif +# if defined(__GNUC__) || defined(__clang__) +# define KA(KVvm_spec) __attribute__((KVvm_args)) +# else +# define KA(KVvm_spec) +# endif +# define KAformat(KVvm_spec) KA(format(KVvm_args)) +# define KAexport(KVvm_spec) KA(visibility(KVvm_args)) + +# define KAunused KA(unused) +# define KAnoreturn KA(noreturn) +# define KApure KA(const) +# define KAinline KA(always_inline) +# define KAflatten KA(flatten) +# define KAexport_none KAexport("hidden") +# define KAexport_force KAexport("default") +/* now we define standard version flags, + * to make it easy for the user to write + * more portable code. */ +# if (__STDC_VERSION__ >= 199901L) +# define KFstd_c89 +# define KFstd_c99 +# define KVstd c99 +# else +# ifdef __STDC__ +# define KFstd_c89 +# define KVstd c89 +# else +# define KVstd K&R /* UH OH */ +# endif +# endif +# if (__STDC_VERSION__ >= 201103L) +# define KFstd_c11 +# define KVstd c11 +# endif +# ifdef __cplusplus +# define KFstd_c89 +# define KFstd_cpp +# define KVstd c++ +# if (__cplusplus >= 201103L) +# define KFstd_cpp11 +# define KVstd c++11 +# endif /* TODO: add more */ +# endif +#endif + +#ifdef KFstd_c11 +# define Kassert _Static_assert +#else +# define Kassert(x) { struct { int assertion_failed[(x) ? 1 : -1] }; } +#endif + +/* hooo boy. null. that one's got a storied + * history across the versions and dialects. + * below, we try to find the ideal way to + * offer a "null" "keyword" depending on + * dialect, version, and whether the user + * has asked for macros to be suspended. + * note that this may result in no "null" + * being defined in C++ or K&R C. */ +#if defined (__cplusplus) && ! defined(KFclean) +# if __cplusplus >= 201103L +# define null nullptr +# else +# define null ((void*)0) +# endif +#elif defined __STDC__ && (!defined __clang__ || defined KFclean) + enum { null = 0 }; + /* believe it or not, this is actually + * completely legal. doesn't even raise + * a single warning in GCC. i was surprised + * too. alas, it later turned out that + * clang will throw a fit about this, so + * we only use the enum method if __clang__ + * is undefined, or if the alternative is + * no null keyword at all. */ +#elif ! defined(KFclean) +# define null ((void*)0) +#endif + +#ifndef KFclean +# ifdef __cplusplus +# define noreturn [[ noreturn ]] +# elif __STDC_VERSION__ >= 201103L +# define noreturn _Noreturn +# else +# define noreturn +# endif +#endif + +#ifdef __cplusplus + [[noreturn]] void kstop(stat_long code); +#elif __STDC_VERSION__ >= 201103L + _Noreturn void kstop(stat_long code); +#else + void kstop(stat_long code); +#endif + +typedef u16 kcond; +/* this will probably not need to be altered, + * as libk sports a modest number of modules, + * and there are few enough error conditions + * in each that 16-bit address space should + * be more than enough for the foreseeable + * future. however if that changes, altering + * the definition here will effect all the + * necessary changes throughout the library */ +bool kokay(kcond); + +#ifdef __cplusplus +} +#endif + +#endif ADDED mod/kcore/def.h.m Index: mod/kcore/def.h.m ================================================================== --- mod/kcore/def.h.m +++ mod/kcore/def.h.m @@ -0,0 +1,53 @@ +dnl kcore/def.h.m → +dnl ~ lexi hale +dnl this file gathers information on the environment it's +dnl being compiled in, setting macros that other headers +dnl need. it will be emitted as . +dnl vim: ft=m4 +#ifndef KIdef +#define KIdef +define(`_atom',0)dnl +define(`def',`#define $1 $2')dnl +define(`defatom',`def($1,$2$3)')dnl +define(`newatom',`def($1,_atom) + define(`_atom',incr(_atom))')dnl + +ifdef(`atom_target_bits',` + define(`target',`atom_target_arch.atom_target_os.atom_target_bits') + def(KVbits,atom_target_bits)',` + define(`target',atom_target_arch.atom_target_os)') + +newatom(KA_os_lin)dnl +newatom(KA_os_fbsd)dnl +newatom(KA_os_obsd)dnl +newatom(KA_os_nbsd)dnl +newatom(KA_os_dar)dnl +newatom(KA_os_osx)dnl +newatom(KA_os_and)dnl +newatom(KA_os_hai)dnl +newatom(KA_os_win)dnl + +newatom(KA_arch_x86)dnl +newatom(KA_arch_arm)dnl +newatom(KA_arch_ppc)dnl +newatom(KA_arch_mips)dnl +newatom(KA_arch_itan)dnl + +defatom(KVos,KA_os_,atom_target_os) +defatom(KVarch,KA_arch_,atom_target_arch) + +ifelse(target_unix,`yes', + `def(`KFenv_unix',) + def(`KFenv_posix',)',` + ifelse(target_posix,`yes', + `def(KFenv_posix)')') + +#define Kpragma(p) _Pragma(#p) +#if defined(__GNUC__) || defined(__clang__) +# define Kerror(msg) Kpragma(GCC error #msg) +#else +# define Kerror(msg) Kpragma(message #msg) +#endif +def(`Knoimpl(fn)', Kerror(no implementation of fn for platform target)) + +#endif ADDED mod/kcore/kcore.md Index: mod/kcore/kcore.md ================================================================== --- mod/kcore/kcore.md +++ mod/kcore/kcore.md @@ -0,0 +1,82 @@ +# kcore +**kcore** is the foundation for the rest of libk. it defines types and structs that are needed by every program, and provides the stub that launches a program's "entry" function. unlike the non-core modules, kcore definitions simply use the prefix `k-`. + +# description +**kcore** contains the libk runtime and headers with important typedefs, structures and enumerations. + +it also provides `boot.o`, the runtime file that must be statically linked into every libk program for it to be able to load. `boot.o` is incorporated in the static library `libk.a` but for architectural reasons cannot be exported from the dynamic library `libk.so`. + +# program entry point +when using libk, your program's entry point will not be the `int main(int,char**)` function that libc opens into. libk will call the function `stat entry(kenv)` instead. like libc, the value returned by `entry` will be returned to the host platform. entry may have the return type `stat` or `stat_long`. on some operating systems, they are the same; on modern versions of UNIX, however, `stat_long` will allow you to return a 32-bit exit status. most shells will truncate such statuses, but they can be retrieved be the calling process with the appropriate syscall. + +# core types +kcore contains fixed-width integer types (in ``). these types are present on every platform, and are not prefixed. if a platform has a type with a given bit length, that type will be used will be used, otherwise, the type will round to the largest available type (except `u8` and `s8`, which round to the smallest). if you need to be absolutely certain that a type is the appropriate bit length, use sizeof to check its length in a conditional or static assert, for instance `if (sizeof(u16) == 16 / kc_byte_bits)`. + + * `u8` - an unsigned 8-bit integer, or the smallest available unsigned type + * `s8` - a signed 8-bit integer, or the smallest available signed type + * `u16` - an unsigned 16-bit integer + * `s16` - a signed 16-bit integer + * `u32` - an unsigned 32-bit integer + * `s32` - a signed 32-bit integer + * `u64` - an unsigned 64-bit integer + * `s64` - a signed 64-bit integer + * `u128` - an unsigned 128-bit integer (uses GCC and clang extensions to offer 128-bit integer support on x86-64) + * `s128` - a signed 128-bit integer (ibid) + * `ubig` - the largest available unsigned integer type + * `sbig` - the largest available signed integer type. note: ubig and sbig really are the *largest* possible integer types not just the largest native types - if your compiler has extensions for 128-bit types on your arch (as GCC and clang do on x86-64), ubig and sbig will be 128 bits long even if your system word length is less than 128. so only use ubig/sbig if you really, really mean it. + * `ubyte` - the smallest available unsigned integer type besides \_Bool + * `sbyte` - the smallest available signed integer type besides \_Bool + * `stat` - the type of process return values expected by the platform (usually u8 on linux) + * `sz` - a type large enough to cover a platform's entire address space (libc equivalent size_t) + * `offset` - a type that can contain the difference between two pointers (libc equivalent ptrdiff_t) + + * `byte_bits` - the bit length of ubyte, sbyte, and char; that is, the number of bits in the type `sizeof` measures things in terms of. `sizeof(type) * byte_bits` will always return the number of bits in a type. + +**kcore** also defines a number of important structs. + +## struct kenv +`kenv` is a struct that encompasses the environment the program was launched in. + * `kiochan std` - a stereo IO channel for reading and writing to and from stdout. + * `kiochan err` - a mono IO channel for writing to stderr. + * `kvar* vars` - a pointer into the program's environment + +## struct kvar +`kvar` is a struct that abstracts over platform environment variables. + * `kstr name` - the name of an environment variable + * `kstr val` - the value of an environment variable + * `char* platform` - a pointer into the platform's underlying representation + +# constants +in ``, every type has an associated min and max constant, containing the smallest and largest value that type can hold on your system. these can be access by suffixing a type with `_min` and `_max`, respectively. min and max values of native types can be accessed with the `kc_[us]` prefix - for instance, the minimum value of `signed long` is `kc_slong_min`. (`long long` can be referenced as `llong`). + +# functions +kcore contains only a handful of primitive functions, most of which provide the C runtime. + +## kstop() + +`noreturn void kstop(stat)` terminates the program immediately, returning the specified integer to the OS as an exit status. the type of integer it takes depends on your operating system. consider using the `enum kbad` defines in ``, which are designed to standardize exit statuses across different software and are synchronized with a FreeBSD effort to do the same (``). + +# definitions + +kcore is the only module that defines any terms outside the k- namespace. the only terms it so defines are native C terms like `noreturn`, which are implemented as keywords in a reserved namespace (`_` followed by an uppercase letter; in this case, `_Noreturn`). macros are then defined in new headers for the "natural" version of the term in order to avoid breaking older code. examples of this technique are `` and ``. since libk is designed for new, modern code, breaking old code isn't a concern, and we turn on all these new keywords as soon as you load ``. + +libk attempts to find the best possible definition and implementation for these various types and keywords, abstracting across different C versions and dialects. you can go ahead and use the normal version of the keywords (e.g. `noreturn`, `bool`) no matter what kind of compiler you're using and you're guaranteed that as long as you don't go fiddling with undefined- or implementation behavior, your code will behave the same every time. at worst, it may lack a few optimizations or warnings. + +(the one minor exception is `null`, a new keyword which libk uses in preference to the ugly, hard-to-type C macro `NULL`.) + +`bool` is implemented as a `_Bool` typedef if `_Bool` is available, and as an enum typedef otherwise. either way, and regardless of whether `KFclean` has been defined, booleans can be used and set to the values `true`, `false`, `yes`, or `no`. `yes` and `true` are synonyms, as are `false` and `no`. + +# macros + +if the flag `KFclean` has not been defined, kcore defines a number of macros to abstract over nonstandard C features. for instance, the `KA*` macros can be used to specify GNU/clang attributes without worrying about compatibility, as they'll automatically be blanked under an incompatible compiler. the KA series includes: + + * `KApure` - marks a "pure" function that changes no state and only takes purely numeric arguments, allowing the compiler to avoid repetetive calls to it or even evaluate it at runtime. + * `KAnoreturn` - the GNU/clang-specific version of `noreturn`. + * `KAunused` - acknowledges that a variable is unused and tells the compiler to shut up about it. + * `KAinline` - forces the compiler to inline a function whether it likes it or not. + * `KAflatten` - placed on a function, will force-inline all function calls *made by* that function — sort of like `KAinline` in reverse. + * `KAformat()` - specifies that the function has printf-style syntax for error-checking purposes. worst hack in history. + +if you wish to add more (there are like, hundreds) please consider making a merge request. however, the generic macro KA() is also available in the interim: `KA(unused)`. + +however, unlike C++ attributes, GNU-style attributes can only be placed on a function declaration, *not* its definition. ADDED mod/kcore/magic.h Index: mod/kcore/magic.h ================================================================== --- mod/kcore/magic.h +++ mod/kcore/magic.h @@ -0,0 +1,76 @@ +/* + * ~ lexi hale + * one of libk's biggest goals is standardization across + * software. for this reason, we define here a variety + * of "magical numbers" used to signal or describe + * various states, messages, and errors. suggestions are + * welcome. + */ +#ifndef KImagic +#define KImagic + +#include + +typedef enum kbad { + // values to be returned by an entry function + kbad_ok = 0, + /* the requested operation completed + * successfully */ + + kbad_mem = 1, + /* the system does not have enough free + * memory to complete the requested operation */ + + /* these are synchronized with freebsd's existing + * sysexit.h conventions. */ + kbad_usage = 64, + /* # fbsd EX_USAGE + * a usage screen was displayed to the user + * on request or due to improperly formed call */ + kbad_data = 65, + /* # fbsd EX_DATAERR + * data supplied by the user was incorrect in + * some way */ + kbad_no_input = 66, + /* # fbsd EX_NOINPUT + * input to the program is absent */ + kbad_user = 67, + /* # fbsd EX_NOUSER + * these aren't the droids you're looking for */ + kbad_host = 68, + /* # fbsd EX_NOHOST + * that host doesn't exist */ + kbad_avail = 69, + /* # fbsd EX_UNAVAILABLE + * a necessary resource or support system isn't + * available, or something else went wrong */ + kbad_internal = 70, + /* # fbsd EX_SOFTWARE + * "my bad" */ + kbad_os = 71, + /* # fbsd EX_OSERR + * the OS did a fucksy-wucksy */ + kbad_osfile = 72, + /* # fbsd EX_OSFILE + * a system file is fucked up or gone */ + kbad_creat = 73, + /* # fbsd EX_CANTCREAT */ + kbad_io = 74, + /* # fbsd EX_IOERR */ + kbad_try = 75, + /* # fbsd EX_TEMPFAIL + * something went wrong this time. try again + * some other time */ + kbad_proto = 76, + /* # fbsd EX_PROTOCOL + * failure to speak a protocol correctly */ + kbad_perm = 77, + /* # fbsd EX_NOPERM + * you or the program do not have permissions + * to do the thing requested */ + kbad_conf = 78, + /* # fbsd EX_CONFIG + * your configuration is fucked */ +} kbad; + +#endif ADDED mod/kcore/platform.syscall.fn.c Index: mod/kcore/platform.syscall.fn.c ================================================================== --- mod/kcore/platform.syscall.fn.c +++ mod/kcore/platform.syscall.fn.c @@ -0,0 +1,36 @@ +/* platform.syscall.fn.c + * ~ lexi hale + * this file provides a unified interface + * to the host operating system's syscalls. + * its function signature may vary across + * OSes, as the details of each's syscall + * implementation may vary drastically. + */ + +#include +#include + +#ifdef KFenv_posix +# include +#else + Knoimpl(k_platform_syscall) +#endif + +extern void k_platform_syscall_raw ( + k_platform_syscall_return* return_slot, + k_platform_syscall_error* error_no_slot, + enum k_platform_syscall syscall_no, + u8 valency, + s64* args); + +struct k_platform_syscall_answer +k_platform_syscall(enum k_platform_syscall call, u8 valency, s64 args[]) { + struct k_platform_syscall_answer answer; + + k_platform_syscall_raw + (&answer.ret, + &answer.error, + call, valency, args); + + return answer; +} ADDED mod/kcore/stop.fn.c Index: mod/kcore/stop.fn.c ================================================================== --- mod/kcore/stop.fn.c +++ mod/kcore/stop.fn.c @@ -0,0 +1,29 @@ +/* kcore/stop.fn.c - kstop() + * ~ lexi hale + * this file defines a function that prematurely exits from + * a running libk program with an appropriate exit status. */ + +#include +#include // so we know what system this is +#include +#ifdef KFenv_posix +# define KFplatform_define_types +# define KFplatform_define_constants +# include +#endif + +/* this manual redefinition is necessary to stop gcc + * bitching that kstop returns, which it def does not */ +noreturn extern struct k_platform_syscall_answer +k_platform_syscall(enum k_platform_syscall call, u8 valency, + k_platform_syscall_arg args[]); + +noreturn void kstop(stat_long code) { +# ifdef KFenv_posix + k_platform_syscall_arg exit_code[] = { code }; + k_platform_syscall(k_platform_syscall_exit, 1, exit_code); +# else + Knoimpl(kstop) +# endif +} + ADDED mod/kcore/syscall.fn.x86.lin.64.s Index: mod/kcore/syscall.fn.x86.lin.64.s ================================================================== --- mod/kcore/syscall.fn.x86.lin.64.s +++ mod/kcore/syscall.fn.x86.lin.64.s @@ -0,0 +1,86 @@ +; kcore/syscall.fn.x86.lin.64.s +; ~ lexi hale +; +; this function performs a syscall and stores its +; results in the variables provided. this makes it +; possible to bypass the hideous errno mechanism +; altogether and access the error value of a +; syscall directly. invoke as: +; +; void k_platform_syscall_raw(s64* result, u64* errno, +; syscall, u8 valency, s64[] args) + +bits 64 +%include "../arch/posix/x86.lin.64.s" +%include "../arch/x86.cdecl.64.s" +; vim: ft=nasm + +%macro handle_arg 1 + %assign v %1+1 + mov sys.reg. %+ v, [ccall.reg.4 + 8 * %1] + dec ccall.reg.3 + jz .perform_call +%endmacro + +global k_platform_syscall_raw:function +k_platform_syscall_raw: + ; locals: rbx = s64* result + ; r12 = u64* errno + ; arg 0 = s64* result + ; arg 1 = errno ptr + ; arg 2 = syscall num + ; arg 3 = valency + ; arg 4 = args ptr + + ; store the locals in registers that + ; are guaranteed not to be clobbered, + ; saving us some cycles pushing to + ; and popping back from the stack + mov rbx, ccall.reg.0 + mov r12, ccall.reg.1 + + ; this needs to go before the loop + ; or it'll get clobbered + mov sys.reg.0, ccall.reg.2 + + ; automatically generate the code + ; needed to move the arguments into + ; their correct registers. see above + %assign i 0 + %rep 6 + handle_arg i + %assign i i+1 + %endrep + + ; valency >= 7. this is not valid, so + ; we set our return value to -1 and the + ; error number to its maximum value in + ; order to indicate that the syscall + ; was invalid + mov qword [rbx], -1 + mov qword [r12], -1 + ret + + ; we have a valency match - perform the + ; requested syscall already store in rax + .perform_call: sys.call + + ; check for an error - on x86, error is + ; returned as a negative value in %rax + test sys.reg.ret, sys.reg.ret + js .error ; jump if sign flag set + + ; move our return values into place and + ; return to the caller (which should + ; always be k_platform_syscall, btw) + mov [rbx], sys.reg.ret + mov qword [r12], 0 ; no error + ret + + ; an error was returned - we need to set + ; the errno to its positive equivalent, + ; and store -1 in the return variable + .error: neg sys.reg.ret + mov qword [rbx], -1 + mov [r12], sys.reg.ret + ret ADDED mod/kcore/testbin.exe.c Index: mod/kcore/testbin.exe.c ================================================================== --- mod/kcore/testbin.exe.c +++ mod/kcore/testbin.exe.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +struct object { + u8 a; + s16 b; + bool c; +}; + +stat_long entry(kenv e) { + const char msg[] = "hello from libk\n"; + ksraw ptr = { Kmsz(msg), msg }; + + bool maybe = true; + maybe = no; + + if (kiosend(e.std, ptr, null) == kiocond_ok) { + /* great, continue */ + } else { + return kbad_io; + } + + void* region = kmheapa(2048); + if (region == null) return kbad_mem; + + kmzero(region,2048); + + if (kmheapf(region) >= kmcond_fail) return kbad_mem; + + return kbad_ok; +} ADDED mod/kcore/type.h.m Index: mod/kcore/type.h.m ================================================================== --- mod/kcore/type.h.m +++ mod/kcore/type.h.m @@ -0,0 +1,192 @@ +dnl kcore/type.h.m → +dnl ~ lexi hale +dnl this file gathers information on the environment it's +dnl being compiled in, defining types that our code +dnl needs. it will be emitted as . +dnl vim: ft=c +include(`typesize.m') +changequote(`“',`”') +#ifndef KItype +#define KItype + +/* we define 64-bit types first due to an oddity in how + * 128-bit types are handled: we want kc_?big to reference + * the absolute largest type available to the compiler, + * but in some cases, 128-bits may not be among the + * standard C types despite being supported by the + * compiler. to work around this, we first check whether + * 64-bit types are available (__int128_t only works on + * 64-bit systems) and then whether the compiler is one + * that supports the 128-bit extension - but only if a + * native 128-bit type is not available. + * + * once this is done, we can be certain that u128 will + * reference the largest available integer type and can + * safely define kc_?big by reference to it. */ + + +ifdef(“type_bit64”,“ + typedef unsigned type_bit64 u64; + typedef signed type_bit64 s64; + ifdef(“type_bit128”,,“ + /* even if no native type is 128 bits long, clang and + * gcc have extensions to support 128 bit arithmetic + * on 64-bit hardware */ +# if defined(__GNUC__) || defined(__clang__) + typedef __uint128_t u128; + typedef __int128_t s128; +# else + /* if we don`'t have access to that extension + * or native 128-bit types, then we just use + * the largest native type specified in the + * C standard */ + typedef unsigned long long u128; + typedef signed long long s128; +# endif + ”)”,“dnl + typedef unsigned long long u64; + typedef signed long long s64; + + typedef u64 u128; + typedef s64 s128; +”)dnl + +ifdef(“type_bit128”,“dnl + typedef unsigned type_bit128 u128; + typedef signed type_bit128 s128; +”)dnl + +typedef unsigned char ubyte; +typedef signed char sbyte; +typedef u128 ubig; +typedef s128 sbig; + +ifdef(“type_bit8”,“dnl + typedef unsigned type_bit8 u8; + typedef signed type_bit8 s8; +”,“dnl + typedef ubyte u8; + typedef sbyte s8; +”)dnl + +ifdef(“type_bit16”,“ + typedef unsigned type_bit16 u16; + typedef signed type_bit16 s16; +”,“ + typedef ubig u16; + typedef sbig s16; +”)dnl + +ifdef(“type_bit32”,“ + typedef unsigned type_bit32 u32; + typedef signed type_bit32 s32; +”,“ + typedef ubig u32; + typedef sbig s32; +”)dnl + +define(“cat”, “$1$2”) +typedef enum kcendian { + kcendian_high, + kcendian_low, + kcendian_system = cat(kcendian_,prop_endian), +} kcendian; + +enum /* max-min values of each type */ { + byte_bits = arch_byte_bits, + + u8_min = 0, u8_max = ((u8)-1), + u16_min = 0, u16_max = ((u16)-1), + u32_min = 0, u32_max = ((u32)-1), + u64_min = 0, u64_max = ((u64)-1), + u128_min = 0, u128_max = ((u128)-1), + + /* assuming two's complement. TODO: check math */ + define(“sspec”,“ + $1_min = 0 - ((1 << sizeof($1) * byte_bits) / 2), + $1_max = (1 << sizeof($1) * byte_bits) / 2 - 1”)dnl + + sspec(s8), sspec(s16), sspec(s32), + sspec(s64), sspec(s128), + + kc_uchar_min = 0, kc_uchar_max = type_max_u_char, + kc_ushort_min = 0, kc_ushort_max = type_max_u_short, + kc_uint_min = 0, kc_uint_max = type_max_u_int, + kc_ulong_min = 0, kc_ulong_max = type_max_u_long, + kc_ullong_min = 0, kc_ullong_max = type_max_u_llong, + + kc_schar_min = type_min_s_char, kc_schar_max = type_max_s_char, + kc_sshort_min = type_min_s_short, kc_sshort_max = type_max_s_short, + kc_sint_min = type_min_s_int, kc_sint_max = type_max_s_int, + kc_slong_min = type_min_s_long, kc_slong_max = type_max_s_long, + kc_sllong_min = type_min_s_llong, kc_sllong_max = type_max_s_llong, + + ubig_min = u128_min, ubig_max = u128_max, + sbig_min = s128_min, sbig_max = s128_max, + + ubyte_min = kc_uchar_min, ubyte_max = kc_uchar_max, + sbyte_min = kc_schar_min, sbyte_max = kc_schar_max, +}; + +ifdef(“type_sz”,“ + typedef type_sz sz; +”,“dnl +# ifdef __cplusplus + /* C++ gives us a clean, standardized way to do this */ + typedef decltype (sizeof(char)) sz; +# else +# if defined(__GNUC__) || defined(__clang__) + typedef __typeof__ (sizeof(char)) sz; +# else + /* we're stumped - set sz to the safest possible value under + * the circumstances, and warn the user. */ +# warning no authoritative sz (size_t) type definition \ + available; defaulting to largest unsigned integer type + typedef ubig sz; +# endif +# endif +”)dnl + +ifdef(“type_offset”,“ + typedef type_offset offset; +”,“dnl +# ifdef __cplusplus + /* C++ gives us a clean, standardized way to do this */ + typedef decltype (((void*)-1) - 1) offset; +# else +# if defined(__GNUC__) || defined(__clang__) + typedef __typeof__ (((void*)10) - ((void*)5)) offset; +# else + /* no dice - set offset to the safest possible value under + * the circumstances, and warn the user. */ +# warning no authoritative offset (ptrdiff_t) type definition \ + available; defaulting to largest unsigned integer type + typedef sbig offset; +# endif +# endif +”)dnl + +// exit status integer types - pls use kbad in instead +ifelse(target_posix,“yes”,“ + /* by convention, posix return values are 8-bit, + * but note that many modern UNIXes do technically + * support higher-bit values. for this reason, + * longstat is defined differently under posix. */ + typedef u8 stat; + typedef u32 stat_long; +”,“dnl + ifelse(atom_target_os,“win”,“ + typedef u32 stat; + ”,“dnl + ifelse(atom_target_os,“vms”,“ + typedef u32 stat; + ”,“dnl + typedef u8 stat; + /* we don't know a specific exit status type + * for your arch so we're going with a sane + * default. if this is wrong, help us fix it! */ + ”)”)dnl + typedef stat stat_long; +”)dnl + +#endif ADDED mod/kdb/kdb.md Index: mod/kdb/kdb.md ================================================================== --- mod/kdb/kdb.md +++ mod/kdb/kdb.md @@ -0,0 +1,34 @@ +# kdb + +kdb is a module for storing and loading data from files. + +## model + +the first argument to all kdb functions is of type `struct kdb`, which describes the database to be accessed. the first field of that struct is a pointer to a struct of type `struct kdb_fmt` which describes the format of the database. + +kdb encodes records as a tuple of three fields: (kind, key, value). `kind` is a 16-bit integer, `key` is an integer of platform width, and `value` is a blob decoded according to the type of the record, which is not saved in the database itself. records are indexed by the tuple (kind, key); `key` may be null. `kind` denotes the structure of the record, and is user-defined. + +to use strings as the keys for database entries, either atoms should be stored in the database or a hash function may be used. kdb contains procedures to automate the use of atoms as keys. + +## types + +### struct kdb + + * `struct kdb_fmt* fmt` + * `enum kdb_conn conn` + * `union { kfile* file; const kdb_store* mem; kmptr ptr; } store;` + +### struct kdb_rec + + * `u16 kind` + * `word key` + * `ksraw value` + +### enum kdb_conn + + * `kdb_conn_none`: no database is connected; calls will fail + * `kdb_conn_file`: the database is stored in a file; calls to modify it will modify the file on-disk + * `kdb_conn_file_static`: the database is stored in a file; calls to modify it will fail. if the file is writable to the process, can be seamlessly changed to `kdb_conn_file` and vice-versa + * `kdb_conn_mem`: the database is stored in-memory, allocated by kmem and referenced by `ptr`. calls to modify it will re-allocate memory via the appropriate interfaces. + * `kdb_conn_mem_static`: the database is stored in-memory, referenced by `mem`; calls to modify it will fail + ADDED mod/kdbg/dbg.h Index: mod/kdbg/dbg.h ================================================================== --- mod/kdbg/dbg.h +++ mod/kdbg/dbg.h @@ -0,0 +1,15 @@ +#ifndef KIdbg +#define KIdbg +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +void kdbg_hexdump(kiochan, void*, sz); + +#ifdef __cplusplus +} +#endif +#endif ADDED mod/kdbg/kdbg.md Index: mod/kdbg/kdbg.md ================================================================== --- mod/kdbg/kdbg.md +++ mod/kdbg/kdbg.md @@ -0,0 +1,5 @@ +# kdbg +kdbg is a module offering convenience functions, useful primarily for debugging purposes. + +## functions + * `kdbg_hexdump(kiochan,void*,sz) → void` prints a canonical hexdump of a specified region of memory to the specified `kiochan`. ADDED mod/kfile/file.h Index: mod/kfile/file.h ================================================================== --- mod/kfile/file.h +++ mod/kfile/file.h @@ -0,0 +1,94 @@ +#ifndef KIfile +#define KIfile +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum kfcond { + kfcond_ok = 0, + kfcond_fail = 1, + // an unspecified error occurred + kfcond_bad_domain, + // the values passed to the function are not within + // that function's domain and are invalid + kfcond_overstep, + // kfstep() was only able to move part of the + // distance requested + kfcond_bad_index, + // the requested index is past the end of the file + kfcond_notfound, + // the specified object could not be found + kfcond_unauth, + // you do not have permission to open this object + kfcond_mem, + // there is not enough memory to complete the + // requested operation +} kfcond; + +typedef enum kfile_kind { + kfile_closed, + // the file object does not refer to an open file + kfile_open, + // the file object refers to an open file +} kfile_kind; + +enum kfopen { + /* file open modes */ + kf_read = 1 << 9, kf_write = 1 << 10, + kf_ascii = 1 << 11, kf_new = 1 << 12, + kf_create = 1 << 13, kf_load = 1 << 14, + kf_top = 1 << 15, kf_end = 1 << 16, + kf_wipe = 1 << 17, kf_map = 1 << 18, + + /* file permission flags */ + kf_ur = 4 << 6, kf_uw = 2 << 6, kf_ux = 1 << 6, + kf_gr = 4 << 3, kf_gw = 2 << 3, kf_gx = 1 << 3, + kf_or = 4 << 0, kf_ow = 2 << 0, kf_ox = 1 << 0, +} + +typedef struct kfile { + enum kfile_kind kind; + enum kfopen mode; + kiochan chan; + u8* content; +} kfile; + +enum kfset_mode { + kfset_none, // do not move + kfset_top, // move to x bytes from the start of the file + kfset_end, // move to x bytes from the end of the file +} + +enum kfplace { + kfplace_none, + kfplace_conf, + kfplace_home, + kfplace_desk, + kfplace_dl, + kfplace_img, + kfplace_vid, + kfplace_msc, + kfplace_doc, + kfplace_dat, + kfplace_share, + kfplace_share_global, + kfplace_cache, +} + +kfile kfplace(enum kfplace location, const char* file, enum kfopen mode); +kfile kfopen (const char* file, enum kfopen mode); +kfile kfopens(const ksraw file, enum kfopen mode); + +kfcond kfset (struct kfile file, enum kfset_mode mode, sz position); +kfcond kfstep(struct kfile file, offset position); + +kfcond kfshred(struct kfile file, sz count); + +#ifdef __cplusplus +} +#endif + +#endif ADDED mod/kfile/kfile.md Index: mod/kfile/kfile.md ================================================================== --- mod/kfile/kfile.md +++ mod/kfile/kfile.md @@ -0,0 +1,81 @@ +# kfile +kfile is libk's file handling library. kfile uses the short naming convention and its sigil is `f`. it is designed to abstract over OS idioms so that developers can write code that simply expresses their intent, and kfile will figure out the correct thing to do on a given platform. to that end, one of kfile's most important functions is `kfplace()` - see below. + +## functions + + * `struct kfile kfplace(enum kfplace location, const char* file, enum kfopen mode)` - when you need to open a file in a location that will vary between environments, such as configuration directories, download dirs, image dirs, etc, use `kfplace()` instead of `kfopen()`. the arguments work like `kfopen()`, except that `file` is not a normal path. instead, it specifies a file in the program's configuration directory to open, and the lead constant will tell it what kind of file you are trying to open. in most cases, a null or empty filename is an error, but see `enum kfplace` for exceptions. `file` must not begin with a slash; if they do, `kfcond_bad_domain` will be returned. + * `kfcond kfget(enum kfplace, ksmut buffer)` - this function will write the prefix for a particular environment directory into `buffer`, or return `kfcond_fail` if `buffer` is not big enough. the best way to use this function is with a for loop; see examples. do not use this to generate paths for file access; use `kfplace()` instead. + * `struct kfile kfopen(const char* file, enum kfopen mode)` - opens a file at the specified pathname. mode is a bit mask . if null is passed as a filename, a temporary file will be created in the appropriate place, using memfds or similar if available and the system temporary directory otherwise. `mode` is a bit flag; the lowest nine bits represent filesystem permissions to be used when creating a file and the rest control how the file will be opened - at least one of `kf_read` or `kf_write` must be passed. see `enum kfopen`. **do not** use kfopen to attempt direct access to configuration files in root of the user's home directory; libk **will** detect this and instruct your users to file bug reports. use kfplace(kfplace_conf) instead to open files in the configuration directory appropriate to the local environment. + * `struct kfile kfopens(const ksraw file, enum kfopen mode)` - like kfopen but allows specifying the filename via a `ksraw` string rather than a `const char*`. + * `kfcond kfset(struct kfile file, enum kfset_mode mode, sz position)` - repositions the read/write head at `position` bytes into the file (if mode is `kfset_top`) or at `position` bytes from the end of the file (if mode is `kfset_end`). attempting to index past the end of the file will return `kfcond_bad_index`. returns `kfcond_ok` on success. + * `kfcond kfstep(struct kfile file, offset position)` - moves the read/write head `position` bytes ahead if `position` is a positive number, or backwards otherwise. attempting to move past the start or end of the file will move the cursor to the start or end and return `kfcond_overstep`. returns `kfcond_ok` on success. + * `kfcond kfshred(struct kfile file, sz count)` - write over `file` with random data `count` times, then truncate it. + * **posix functions** - POSIX has a wide array of nonportable file IO calls that are important particularly with regards to `exec()` and piping data across processes. for this reason, on POSIX platforms, libk exposes these calls to the user. + * kfposix_pipe() - creates a pipe in memory + * kfposix_fifo() - creates a named pipe at the specified path + +## structs + * `struct kfile` - represents an open file. a `kfile` struct should always be initialized with `kfopen()` or `kfconf()` before use, or the values it contains will be meaningless and useless! + * `enum kfile_kind kind` - either `kfile_closed` if the object does not represent an open file, or `kfile_open` if it does + * `enum kfopen mode` - details about the open file. `kf_new` is only set if the file was created by a `kf_new`-flagged open call + * `struct kiochan chan` - a channel that can be used to read or write to a file + * `u8* content` - a pointer to the start of the file in memory, if it is loaded with `kf_map` + * `sz len` - size of the file as of open. this is not updated by `kiosend()` calls! + +## enums + +### enum kfplace +`enum kfplace` contains constants that refer to standardized locations that may vary across environments. on POSIX, the XDG specification will be respected to allow the user to select her own directories. unlike `kfopen()`, `kfplace()` will also create any necessary directories if `kf_new` or `kf_create` are passed. + + * `kfplace_conf` - this mode is designed specifically for opening configuration files. the configuration directory is determined based on OS, environment variables, and the name of the binary; on POSIX OSes, it will use `$XDG_CONFIG_HOME/argv[0]` as the configuration base, cleanly falling back to platform defaults (`$HOME/.config`, and determining home directories based on $USER or failing that UID if `$HOME` is unset). on OSX, it will use `~/Library/argv[0]`. note that if `null` or the empty string are passed as `file`, `kfconf()` will open a file with the name of the configuration base, instead of treating it as a folder to look for configuration files in. + * `kfplace_home` - open a file in the user's home folder. on POSIX, it will use the value of `$HOME` or get the user's home directory with the appropriate calls if `$HOME` is unset. **THINK CAREFULLY BEFORE USING THIS FUNCTIONALITY - NEVER USE IT FOR STORING CONFIGURATION.** if you use this flag to create dotfiles in the homedir, libk **will** inform the user and instruct her to file a bug report. + * `kfplace_desk` - open a file on the user's desktop, or failing that, home folder. on POSIX, it will use the value of `XDG_DESKTOP_DIR` in `$XDG_CONFIG_HOME/user-dirs.dirs`; on OSX, it will use `$HOME/Downloads`. the same restriction against dotfiles applies here as well. + * `kfplace_dl` - open a file in the user's download directory. on POSIX, it will use the value of `XDG_DOWNLOAD_DIR` in `$XDG_CONFIG_HOME/user-dirs.dirs`; on OSX, it will use `$HOME/Downloads`. + * `kfplace_img` - open a file in the user's image directory. on POSIX, it will use the value of `XDG_PICTURES_DIR` in `$XDG_CONFIG_HOME/user-dirs.dirs`; on OSX, it will use `$HOME/Pictures`. + * `kfplace_vid` - open a file in the user's video directory. on POSIX, it will use the value of `XDG_VIDEOS_DIR` in `$XDG_CONFIG_HOME/user-dirs.dirs`; on OSX, it will use `$HOME/Movies`. + * `kfplace_msc` - open a file in the user's video directory. on POSIX, it will use the value of `XDG_MUSIC_DIR` in `$XDG_CONFIG_HOME/user-dirs.dirs`; on OSX, it will use `$HOME/Music`. + * `kfplace_dat` - open a file in the user data directory, the homedir equivalent of `/usr/share`. on POSIX, it will use the value of `$XDG_DATA_HOME/argv[0]`; on OSX, it will use `$HOME/Library/Application Support/argv[0]`. + * `kfplace_share` - open a file in the system data directory. this is typified by `/usr/share/argv[0]` on POSIX. on OSX, it will use `/Library/Application Support/argv[0]`. + * `kfplace_share_global` - like `kfplace_share`, except without the program-specific suffix. + * `kfplace_cache` - open a file in the user's cache directory. on POSIX, this will generally be `$HOME/.cache/argv[0]`. + +### enum kfopen +`enum kfopen` contains flags that may be OR'd together and passed to `kfopen()` to alter its behavior. + * `kf_read` - opens a file for reading + * `kf_write` - opens a file for writing + * `kf_ascii` - opens a file in ascii mode, where available. if not set, the file will be opened in binary mode. + * `kf_new` - creates a new file with the chosen name if one does not already exist, opening the existing file otherwise + * `kf_create` - creates a new file with the chosen name if it does not already exist, failing otherwise + * `kf_load` - loads the file directly into memory, allowing it to be read via indexing into a pointer rather than kiosend() and kiorecv() calls. this is most useful for quickly opening files for reading. if kf_write is also passed, a kiochan will be created to permit writing to the file via standard mechanisms. note: `kf_load` will use mmap() to load the file where possible, but on systems without `mmap()` or `MMAP_SHARED`, `kf_load` will be implemented using standard file IO primitives. for this reason, writing to the region of memory containing the file is implementation-defined behavior. if you really want to be able to modify the file in memory, use `kf_map` instead - it is guaranteed to memory-map the file, but will fail outright on platforms without memory mapping available, so its use is discouraged unless absolutely necessary, or as a platform-specific optimization. + * `kf_top` - if passed, the kiochan will be positioned at the top of the file. this is the default if kf_read is passed; otherwise, it will be positioned at the end so the file can be appended to. + * if you wish to read the file with kiorecv() calls: `kf_read | kf_top` + * if you wish your writes to overwrite what is already present in the file: `kf_write | kf_top` + * if you wish to append to an existing file: `kf_write | kf_end` + * `kf_end` - positions the `kiochan` at the end of the file. see above. + * `kf_wipe` - if a file already exists, erase its contents before writing. to prevent accidental deletions, it is an error to pass `kf_read | kf_wipe`. + * kfopen also has constants that can be used to spell out file permissions; these are guaranteed to be the standard POSIX octal triple on all platforms. on POSIX platforms, the triple will be passed through to the OS unmodified; on others, libk itself will interpret it. + * `kf_ur` - represents the owner's permission to read the file + * `kf_uw` - represents the owner's permission to write to the file + * `kf_ux` - represents the owner's permission to execute the file + * `kf_gr` - represents the group's permission to read the file + * `kf_gw` - represents the group's permission to write to the file + * `kf_gx` - represents the group's permission to execute the file + * `kf_or` - represents all other users' permission to read the file + * `kf_ow` - represents all other users' permission to write to the file + * `kf_ox` - represents all other users' permission to execute the file + +## macros +if `KFclean` is not defined, the following macros will be: + + * `KFsep` is the string used to separate directories in file paths. it will be set to `"\\"` on Windows, `"/"` on POSIX, and so on. + +## examples +`kfget()` is somewhat tricky to use. this is a feature, not a bug - there are very few legitimate uses for it and it should not be convenient to use. use `kfplace()` instead. + + for (sz len = 80; len < kf_path_max; len << 2) { + char path [len]; + if (kfget(kfplace_desk, (ksmut){len, path}) != kfcond_ok) + continue; + kiosend(env.std, (ksraw){len, path}, null); + break; + } ADDED mod/kgraft/attach.exe.c Index: mod/kgraft/attach.exe.c ================================================================== --- mod/kgraft/attach.exe.c +++ mod/kgraft/attach.exe.c @@ -0,0 +1,4 @@ +#include +stat entry(kenv e) { + return 0; +} ADDED mod/kgraft/graft.h Index: mod/kgraft/graft.h ================================================================== --- mod/kgraft/graft.h +++ mod/kgraft/graft.h @@ -0,0 +1,12 @@ +#ifndef KIgraft +#define KIgraft + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif ADDED mod/kgraft/kgraft.md Index: mod/kgraft/kgraft.md ================================================================== --- mod/kgraft/kgraft.md +++ mod/kgraft/kgraft.md @@ -0,0 +1,1 @@ +# kgraft ADDED mod/kio/io.h.m Index: mod/kio/io.h.m ================================================================== --- mod/kio/io.h.m +++ mod/kio/io.h.m @@ -0,0 +1,90 @@ +#ifndef KIio +#define KIio +/* + * ~ lexi hale + * this header declares IO primitive functions and + * structures. it is the same for all platforms. + * platform-specific code is found in the *.platform.h + * files. + */ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum kiostream_kind { + kiostream_closed, + // this kiostream cannot be written to + kiostream_file, + // this kiostream represents a file + kiostream_sock, + // this kiostream is attached to a socket, + // UNIX, IP, or otherwise + kiostream_term, + // this socket is being used to communicate + // directly with a human being + kiostream_ansi, + // like kiostream_term, but can also understand + // ANSI control codes + kiostream_pipe, + // this kiostream sends or receives data to + // another running process + kiostream_other + // no fuckin idea +} kiostream_kind; + +typedef struct kiostream { + kiostream_kind kind; + ifelse(target_posix,`yes',` + int platform_fd; + ')dnl +} kiostream; + +typedef struct kiochan { + kiostream in; + // text can be read from this stream + kiostream out; + // text can be written to this stream +} kiochan; + +#include +typedef enum kiocond { + /* to check if a call failed, perform (x >= kiocond_fail) where x + * is that call's return value. more typically however you should + * select explicitly against kiocond_ok or kiocond_partial, since + * those situations will usually need to be handled differently. */ + + kiocond_ok = kiocond_id, // success + + kiocond_partial, // partial read or write + kiocond_poll, // call would block + kiocond_interrupt, // call was interrupted by signal + + kiocond_fail, // action failed - unspecified reason + kiocond_fail_closed_stream, // action failed because stream is closed + kiocond_fail_invalid, + kiocond_fail_io, + kiocond_fail_no_peer, + kiocond_fail_no_space, + kiocond_fail_forbidden, + kiocond_fail_over_quota, + kiocond_fail_pfault, + kiocond_fail_too_big, + kiocond_fail_stream_mismatch, +} kiocond; + +kiocond kiosend(kiochan, ksraw, sz*); // send data to a channel +kiocond kiorecv(kiochan, ksraw*); // receive data from a channel +kmptr kiorecvall(kiochan, kmcell*, kmkind); // automatically allocate a bufer for a channel + // kmkind is only used if kmcell* is null +kiocond kiocon(kiochan, kiochan); // connect one channel to another + +#ifdef __cplusplus +} +#endif + +#endif ADDED mod/kio/kio.md Index: mod/kio/kio.md ================================================================== --- mod/kio/kio.md +++ mod/kio/kio.md @@ -0,0 +1,1 @@ +# kio ADDED mod/kio/send.fn.c Index: mod/kio/send.fn.c ================================================================== --- mod/kio/send.fn.c +++ mod/kio/send.fn.c @@ -0,0 +1,51 @@ +#include +#include +#include +/* send.c - kiosend() "send to channel" + * ~ lexi hale + * kiosend() writes to a channel with an open out stream + */ + +/* we define all platform functions here, + * whether or not they're for the correct + * platform - only the ones actually called + * by the generated code will be linked */ +#include +#include + +kiocond kiosend(kiochan target, ksraw string, sz* len) { + if (target.out.kind == kiostream_closed) return kiocond_fail_closed_stream; + +# ifdef KFenv_posix + /* issue the write syscall here and now so we can + * retrieve errno and report it if necessary */ + + k_platform_syscall_arg args[] = { + target.out.platform_fd, (k_platform_syscall_arg)string.ptr, string.size }; + + struct k_platform_syscall_answer a = k_platform_syscall + (k_platform_syscall_write,3,args); + + sz size = a.ret; + if (size == -1) switch(a.error) { + case k_platform_error_EPERM: return kiocond_fail_forbidden; + case k_platform_error_EINVAL: return kiocond_fail_invalid; + case k_platform_error_EBADF: return kiocond_fail_invalid; + case k_platform_error_EFAULT: return kiocond_fail_pfault; + case k_platform_error_ENOSPC: return kiocond_fail_no_space; + case k_platform_error_EDQUOT: return kiocond_fail_over_quota; + case k_platform_error_EIO: return kiocond_fail_io; + case k_platform_error_EAGAIN: return kiocond_poll; + case k_platform_error_EFBIG: return kiocond_fail_too_big; + case k_platform_error_EINTR: return kiocond_interrupt; + case k_platform_error_EDESTADDRREQ: return kiocond_fail_no_peer; + default: return kiocond_fail; + } +# else + Knoimpl(kiosend,KVos); +# error missing implementation // boring error for plebs +# endif + + if (len != null) *len = size; + return kiocond_ok; +} ADDED mod/kmem/free.fn.c Index: mod/kmem/free.fn.c ================================================================== --- mod/kmem/free.fn.c +++ mod/kmem/free.fn.c @@ -0,0 +1,22 @@ +#include +#include +#include +#include + +/* free.fn.c - kmfree() "arbitrary free" + * ~ lexi hale + * kmfree() frees memory allocated in any manner. + * it ignores non-dynamically allocated memory, + * returning kmcond_unnecessary. to check for + * success, compare result < kmcond_fail. + */ + +kmcond kmfree(kmptr ptr) { + if (ptr.kind <= kmkind_fail) return kmcond_unnecessary; + switch (ptr.kind) { + case kmkind_heap: return kmheapf(ptr.ref); + } + + return kmcond_unhandled; +} + ADDED mod/kmem/heapa.fn.c Index: mod/kmem/heapa.fn.c ================================================================== --- mod/kmem/heapa.fn.c +++ mod/kmem/heapa.fn.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +/* heapa.c - kmheapa() "heap alloc" + * ~ lexi hale + * kmheapa() allocates a pointer on the heap à la libc malloc() + * see also: kmheapf() "heap free" + */ + +/* arch specific headers */ +#ifdef KFenv_posix +# include +#endif + +/* we define all our platform functions here, whether or not + * they're for the correct platform - only the ones that are + * called by the preprocessed form of the code will actually + * be linked, linker errors are our friend here! */ +extern void* kmem_platform_mmap(void* addr, + unsigned long sz, unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long off); + +void* kmheapa(sz len) { + /* allocate an object on the heap and return + * a pointer, or NULL if the allocation failed. */ + union { + void* raw; + ubyte* byte; + kmbox* header; + } region; + /* we need to allocate space for the + * header and for the actual object */ + sz region_size = sizeof(kmbox) + len; + +# ifdef KFenv_posix + /* posix APIs - we've got it easy. currently for + * nonlinear heap allocation kmheapa simply uses + * m(un)map and lets the kernel worry about it. it + * may ultimately be worth replacing this with a + * more sophisticated implementation, most likely an + * existing allocator like jemalloc, though i'm wary + * of including outside code - it creates a licensing + * mess and i'd prefer libk to be AGPLv3 across the + * board. possibility: include hooks for multiple + * allocators, allowing the user to select & link in + * her preferred allocator at compile time? */ + + /* because munmap needs to be informed of the size of + * the region we are going to unmap, we need to store + * that information in the region that we are mapping. + * the user will receive an adjusted pointer that can + * be adjusted to point a field of type size_t that + * contains the size of the allocated space.*/ + + region.byte = kmem_platform_mmap(null, region_size, + posix_prot_read | posix_prot_write, + posix_flag_anonymous | posix_map_shared, -1, 0); + /* impl note: while per manpage fd is "ignored" + * for MAP_ANONYMOUS, "some implementations" require + * a value of -1 */ + + if (region.raw == (void*) -1) return null; + /* worth retrieving errno? discuss */ + +# else + Knoimpl(kmheapa,KVos); +# error missing implementation +# endif + + void* const object = (region.byte + sizeof (kmbox)); + + region.header -> kind = kmkind_heap; + region.header -> size = len; + + return object; +} ADDED mod/kmem/heapao.fn.c Index: mod/kmem/heapao.fn.c ================================================================== --- mod/kmem/heapao.fn.c +++ mod/kmem/heapao.fn.c @@ -0,0 +1,17 @@ +#include +#include +/* heapao.fn.c - kmheapao() "allocate heap object" + * ~ lexi hale + * kmheapao() allocates a region in heap memory + * and returns a kmptr struct referencing that + * newly allocated region. + */ + +kmptr kmheapao(sz size) { + void* ptr = kmheapa(size); + kmptr p = { + .kind = (ptr != null ? kmkind_heap : kmkind_fail), + .ref = ptr, + .shred = false, + }; return p; +} ADDED mod/kmem/heapf.fn.c Index: mod/kmem/heapf.fn.c ================================================================== --- mod/kmem/heapf.fn.c +++ mod/kmem/heapf.fn.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +/* heapf.c - kmheapf() "heap free" + * ~ lexi hale + * kmheapf() frees a region on the heap à la libc free() + * see also: kmheapa() "heap alloc" + */ + +/* arch specific headers */ +#ifdef KFenv_posix +# include +#endif + +/* we define all our platform functions here, whether or not + * they're for the correct platform - only the ones that are + * called by the preprocessed form of the code will actually + * be linked, linker errors are our friend here! */ +extern int kmem_platform_munmap(void* addr, unsigned long sz); + +kmcond kmheapf(void* ptr) { + /* take an object allocated on the heap and free it, + * returning kmcond_ok on success or an appropriate + * value on failure. */ + struct kmbox* header = (kmbox*) + (((ubyte*)ptr) - sizeof (kmbox)); + sz const total = header -> size + sizeof (kmbox); + + if (header -> kind != kmkind_heap) return kmcond_mismatch; +# ifdef KFenv_posix + /* currently allocation is handled on posix by naive use + * of MAP_ANONYMOUS. munmap needs to be told the size of + * the region to unmap (free), which kmheapa() stores at + * (ptr - sizeof sz). see kmheap.c for details. */ + + if(kmem_platform_munmap(header, total) == -1) { + /* we don't need to bother recovering errno; + * there's only one possible munmap error */ + return kmcond_bad_address; + } + +# else + Knoimpl(kmheapf,KVos); +# error missing implementation +# endif + return kmcond_ok; +} ADDED mod/kmem/kmem.md Index: mod/kmem/kmem.md ================================================================== --- mod/kmem/kmem.md +++ mod/kmem/kmem.md @@ -0,0 +1,171 @@ +# kmem + +**kmem** is a libk module that contains various functions for memory allocation and deallocation. it uses the **short** naming convention with the glyph `m`. + +kmem allocators can work in several different ways. they can allocate memory directly from the heap (like `kmheapa()` and `kmlina()`), use a header that has already been allocated by another function, or allocate memory only from a pre-allocated pool. linear allocation with pool allocation is particularly useful, as it permits the very rapid allocation and deallocation of lots of objects with only a few adjustments to the heap, and no possibility of fragmentation or need for expensive algorithms like `malloc()` or `kmheapa()` + +## module functions + +kmem supplies two module-level functions, used to interact with the `kmptr` container type. + + * `kmfree(kmptr) → void` - free, downref, or ignore the pasted object as appropriate + * `kmshred(kmptr) → void` - free, downref, or ignore the pasted object as appropriate. if deallocating, zero its contents + * `kmstat(void*) → kmptr` - convenience function to wrap a pointer to a non-managed object in a `kmptr` struct, so it can be passed to functions that accept arbitrary objects. `kmptr p = kmstat(raw)` is equivalent to `kmptr p = { kmkind_none, raw, NULL }`. + * `kmtaint(&kmptr) → void` - "taints" a `kmptr` object by setting it to be shredded when freed. this may be desirable if the object pointed to contains privileged information. + * `kmzero(void*,sz) → void` - zeroes a region of memory + * `kmozero(kmptr) → void` - zeroes an object in memory + * `kmcopy(void* dest, void* src, sz) → void` - copies one region of memory to another + * `kmdup(kmptr) → kmptr` - duplicates an object in memory, allocating it as sibling of the original + +## types + +kmem defines the following types: + + * `enum kmkind` - enumerates allocation strategies + * `struct kmptr` - abstract pointer object + * `struct kmcell` - abstract memory cell + * `struct kmref` - a reference-counted cell + * `struct kmnode` - a node in an allocation tree + * `struct kmpool` - a memory pool + +`kmptr` and `kmcell` are both very similar. the difference is that a kmptr points to a region in memory and can be passed around freely. a `kmcell` is the actual in-memory representation of an allocation cell. a `kmcell` cannot be usefully instantiated; rather, it is downcast from an actual cell type (e.g. `kmnode n; kmcell* s = (kmcell*)(&n)`) + + +### kmkind + +`kmkind` is an enum that specifies an allocation function. + + * `kmkind_none` - no allocation + * `kmkind_lin` - linear heap allocation + * `kmkind_heap` - random heap allocation + * `kmkind_pool` - pool allocation + * `kmkind_ref` - reference-counting allocation + * `kmkind_tree` - tree allocation + +### kmptr + +kmem functions can operate on both raw pointers and the `kmptr` struct type. `kmptr` is a generic struct that can contain any kind of pointer. this is useful if you wish to allocate different objects in different manners, but pass them on into a single interface. + +memory pointed at by `kmptr` pointers can be freed either with the usual specialized function, or by passing the `kmptr` structure itself to the generic function `kmfree`, which will handle it appropriately, even if it's a pointer to a garbage-collected object or to a static region of memory. + +a `kmptr` has the following layout: + + * `kmkind kind` - codes the type of pointer; `kmkind_none` indicates a non-allocated pointer to a static (global or on-stack) object. + * `kmshred shred` - an enum. if `kmshred_yes`, the value will be zeroed or otherwise made unreadable on free. if no, `kmfree` will consult `src` for shred policy if it is not NULL. + * `void* ref` - the raw pointer enclosed by `cell` + * `kmcell* cell` - a pointer to an object enclosure, typically either a memory pool or a referencing-counting object. NULL if not needed. + +the convenience function `kmstat(void*) → kmptr` wraps a pointer to a static object in a `kmptr` struct. + +### struct kmcell + +`kmcell` is a stub struct used to disambiguate between source types. a "source" is an object that can hold an allocated object, such as the heap, a memory pool, a fixed-length array on stack, or a fixed-length global array. all values produced by a kmem allocation function can be cast to `kmcell*`, and have an intial field `id` that contains a `kmcell`. + + * `kmkind kind` - kind of cell + * `size_t size` - size of cell (data plus all fields) + * `kmshred shred` - shredding flag + +### struct kmref + +`kmref` is a struct that constitutes the in-memory representation of a reference-counted cell. + + * `kmcell id = { .kind = kmkind_ref, … } ` - kind of cell + * `size_t refs` - number of active references + * `kmcell* src` - source, if any + * `char data[]` - content of cell + +### struct kmnode + +`kmnode` is the header struct for tree nodes. all tree nodes pointers can yield a `kmnode` structure by subtracting `sizeof (kmnode)` from the pointer. a utility function and macro are made available to automate this safely. + + * `kmcell id = { .kind = kmkind_tree, … } ` - kind of cell + * `kmnode* parent` - parent node + * `kmnode* child` - first child node + * `kmnode* lastchild` - last child node + * `kmnode* prev` - previous sibling, NULL if first + * `kmnode* next` - next sibling, NULL if last + +### struct kmpool + + * `kmcell id = { .kind = kmkind_pool, … } ` - kind of cell + * `size_t cellsz` - size of individual pool cells + * `kmpoolcell* top` - pointer to most recently allocated pool cell + * `kmpoolcell* bottom` - pointer to most recently freed pool cell + * `kmpoolcell data[]` - content of cell + +#### struct kmpoolcell + + * `kmpoolcell* last` - pointer to last element allocated before this one + * `char data[]` - pool data + +### enum kmshred + +`kmshred` is an enum used to indicate whether an object should be "shredded" (written over) in memory when it's deleted. this is a useful means to ensure that privileged information is not accidentally left in memory after use. if the shredding mechanism is not useful, compile libk with the flag `KFmem_noshred` to exclude its functions and fields. + + * `kmshred_no = 0` - marks an object not to shred on free + * `kmshred_yes = 1` - marks an object to shred on free + +## naming convention + +kmem function names are based on the **method** of allocation and the **action** being performed. methods are listed in the section below. kmem defines a number of standardized actions, though not every method uses every action. the character listed in brackets is suffixed to the name of the method to produce a function name: for instance, `kmheapa` will allocate memory on the heap, while `kmrefd` will decrement the reference count of its argument. + + * initialize [i] - initializes a memory store on the heap + * initialize fixed [if] - initialize a memory store on the stack or in a fixed-size global + * allocate [a] - return a raw pointer to a new region of memory of the given size, ready to write, or NULL if not possible. contents of that region undefined. takes parameter (size_t sz). + * allocate pointer object [ao] - like *allocate*, but returns a `kmptr` instead of a raw `void*`. + * zero [z] - allocate a new region of memory and zero it before returning it for writing. + * zero pointer object [zo] - like *zero*, but returns a `kmptr` instead of a raw `void*`. + * free [f] - free a section of memory, either decrementing a reference count or returning it to whatever pool it came from. + * shred [s] - destroy whatever was in the segment of memory, then return it to the pool it came from. + * destroy [x] - tears down a memory store + * upref [u] - increments a reference counter + +## methods + +kmem currently supports the following methods of memory management, along with which methods are defined for it. (note that `a` implies `z` and `f` implies `s`). a method may be excluded from a libk binary by defining the flag `KFmem_no[name]`, e.g. `KFmem_noheap`. + +the fastest allocator is the linear allocator, which should be sufficient for most simple programs. it allocates and deallocates memory simply by resizing the stack; there is no fragmentation, but objects must be freed in the order they are allocated. however, entire groups of objects can be freed at once at very little cost. + + * `lin` [iax] - linear heap allocator + * `kmlini(void) → void*` - return a pointer to the current top of the heap + * `kmlina(size_t) → void*` - allocate space on the heap and increase its size appropriately + * `kmlinz(size_t) → void*` - allocate zero-filled space on the heap and increase its size appropriately + * `kmlinx(void*) → void*` - returns the top of the heap to the location specified, freeing all memory allocated since the call to kmlini() or `kmlina()` that produced it + * `heap` [af] - random heap allocation + * `kmheapa(size_t) → void*` - allocate + * `kmheapz(size_t) → void*` - zero-allocate + * `kmheapao(size_t) → kmptr` - allocate pointer object + * `kmheapzo(size_t) → kmptr` - zero-allocate pointer object + * `kmheapf(void*) → void` - free + * `kmheaps(void*) → void` - shred + * `ref` [afu] - reference-counted heap object + * `kmrefa(kmcell*, size_t) → void*` - allocate + * `kmrefz(kmcell*, size_t) → void*` - zero-allocate + * `kmrefao(kmcell*, size_t) → void*` - allocate pointer object + * `kmrefzo(kmcell*, size_t) → void*` - zero-allocate pointer object + * `kmreff(void*) → void` - downref; free if last ref + * `kmrefs(void*) → void` - downref and mark for shred on last ref + * `pool` [ixaf] - memory pool + * `kmpooli(kmcell*, size_t sz, size_t n) → kmpool*` - initialize a fixed memory pool (a pool of `n` cells of length `sz`) + * `kmpoolx(kmpool*) → void` - tear down a memory pool + * `kmpoola(kmpool*) → void*` - allocate from pool + * `kmpoolz(kmpool*, size_t) → void*` - zero-allocate from pool + * `kmpoolao(kmpool*, size_t) → void*` - allocate pointer object + * `kmpoolzo(kmpool*, size_t) → void*` - zero-allocate pointer object + * `kmpoolf(void*) → void` - downref; free if last ref + * `kmpools(void*) → void` - downref and mark for shred on last ref + * `tree` [af] - uses a node-child strategy. when a node is freed, all of its children are automatically freed as well. + * `kmtreea(kmcell* src, void* parent, size_t) → void*` - create a tree node. if `parent` is NULL, the node will the top of a new tree. if src is null, allocate on-heap. + * `kmtreez(kmcell* src, void* parent, size_t) → void*` - like `kmtreea` but zeroed + * `kmtreeao(kmcell* src, void* parent, size_t) → kmptr` - like `kmtreea` but returns a `kmptr` + * `kmtreezo(kmcell* src, void* parent, size_t) → kmptr` - like `kmtreez` but returns a `kmptr` + * `kmtreef(void*) → kmptr` - frees a node and all its children + +## macros + +kmem defines the following macros. + + * `Kmsz(array)` - a convenience macro to return the number of elements in a static array. inserts the text `( sizeof (array) / sizeof (array) [0] )` + * `Kmszs(type, struct)` - a convenience macro to return the size of a struct. requires compound literals. + * `Kmszsa(type, array)` - calculates the number of elements in an array of a given struct type. inserts the text `( sizeof ( (type) array ) / sizeof (type) )` + * `Kmpsa(type, struct)` - a convenience macro to insert a "pascal struct array" - that is, a struct array prefixed with a size value. this is equivalent to `Kmszsa(type, array), array` ADDED mod/kmem/mem.h Index: mod/kmem/mem.h ================================================================== --- mod/kmem/mem.h +++ mod/kmem/mem.h @@ -0,0 +1,82 @@ +#ifndef KImem +#define KImem +#include + +#ifndef KFclean +# define Kmsz(e) ( sizeof (e) / sizeof (e) [0] ) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum kmcond { + kmcond_ok, + kmcond_unnecessary, + + kmcond_fail, + kmcond_unhandled, + kmcond_mismatch, + kmcond_bad_address, +} kmcond; + +typedef enum kmkind { + kmkind_none, + kmkind_fail, + kmkind_linear, + kmkind_heap, + kmkind_pool, + kmkind_ref, + kmkind_tree +} kmkind; + +typedef enum kmshred { + kmshred_yes, + kmshred_no +} kmshred; + +typedef struct kmbox { + kmkind kind; + sz size; + kmshred shred; +} kmbox; + +typedef struct kmcell { + kmbox header; + sz refs; + struct kmcell* src; +} kmcell; + +typedef struct kmnode { + kmbox header; + struct kmnode* origin, + * branch, + * next, + * prev; +} kmnode; + +typedef struct kmptr { + kmkind kind; + kmshred shred; + void* ref; +} kmptr; + +/* heap functions */ + +void* kmheapa(sz); +kmptr kmheapao(sz); +kmcond kmheapf(void*); + +/* generic functions */ + +kmcond kmfree(kmptr); +kmkind kmtell(void*); +void kmzero(void*,sz); +void kmozero(kmptr); + + +#ifdef __cplusplus +} +#endif + +#endif ADDED mod/kmem/platform.mmap.fn.x86.lin.64.s Index: mod/kmem/platform.mmap.fn.x86.lin.64.s ================================================================== --- mod/kmem/platform.mmap.fn.x86.lin.64.s +++ mod/kmem/platform.mmap.fn.x86.lin.64.s @@ -0,0 +1,25 @@ +bits 64 +%include "../arch/posix/x86.lin.64.s" +%include "../arch/x86.cdecl.64.s" +; vim: ft=nasm + +global kmem_platform_mmap +kmem_platform_mmap: + ; to call mmap, we need to translate the cdecl64 + ; register arguments to their appropriate syscall64 + ; registers. these are mostly the same, with one + ; obnoxious exception. the NOPs have been written + ; in as comments to aid in understanding. + + mov sys.reg.1, ccall.reg.0 ;nop - rdi → rdi + mov sys.reg.2, ccall.reg.1 ;nop - rsi → rsi + mov sys.reg.3, ccall.reg.2 ;nop - rdx → rdx + mov sys.reg.4, ccall.reg.3 ; OP - rcx → r10 + mov sys.reg.5, ccall.reg.4 ;nop - r8 → r8 + mov sys.reg.6, ccall.reg.5 ;nop - r9 → r9 + + mov sys.reg.0, sys.mmap + sys.call + + mov ccall.reg.ret, sys.reg.ret ; rax → rdi + ret ADDED mod/kmem/platform.munmap.fn.x86.lin.64.s Index: mod/kmem/platform.munmap.fn.x86.lin.64.s ================================================================== --- mod/kmem/platform.munmap.fn.x86.lin.64.s +++ mod/kmem/platform.munmap.fn.x86.lin.64.s @@ -0,0 +1,18 @@ +bits 64 +%include "../arch/posix/x86.lin.64.s" +%include "../arch/x86.cdecl.64.s" +; vim: ft=nasm + +global kmem_platform_munmap +kmem_platform_munmap: + ; to call munmap, we need to translate the cdecl64 + ; register arguments to their appropriate syscall64 + ; registers. all those that matter are the same. + mov sys.reg.1, ccall.reg.0 ;nop - rdi → rdi + mov sys.reg.2, ccall.reg.1 ;nop - rsi → rsi + + mov sys.reg.0, sys.munmap + sys.call + + mov ccall.reg.ret, sys.reg.ret ; rax → rdi + ret ADDED mod/kmem/zero.fn.c Index: mod/kmem/zero.fn.c ================================================================== --- mod/kmem/zero.fn.c +++ mod/kmem/zero.fn.c @@ -0,0 +1,29 @@ +#include +#include +#include + +/* zero.fn.c - kmzero() "zero region" + * ~ lexi hale + * kmzero() zeroes a region of memory. it is + * libk's equivalent to libc's bzero(). + */ + +#define canzero(bits) (_canzero(sizeof(u##bits), bits, size)) +static bool _canzero(sz type, u8 bits, sz size) { + return (sizeof (u64) * byte_bits == 64 && size >= sizeof (u64)); +} + +void kmzero(void* memory, sz size) { + ubyte* elt = memory, + * const end = elt + size; + + if canzero(64) { + u64* p = (u64*) elt; + sz segments = (end - elt) / sizeof (u64); + + for(;(p - (u64*)elt) < segments; ++p) *p = 0; + elt = (ubyte*) p; + } + + for (;elt < end; ++elt) *elt = 0; +} ADDED mod/kmsg/kmsg.md Index: mod/kmsg/kmsg.md ================================================================== --- mod/kmsg/kmsg.md +++ mod/kmsg/kmsg.md @@ -0,0 +1,2 @@ +# kmsg +kmsg is libk's inter-process communication library. ADDED mod/kmsg/msg.h Index: mod/kmsg/msg.h ================================================================== --- mod/kmsg/msg.h +++ mod/kmsg/msg.h @@ -0,0 +1,12 @@ +#ifndef KImsg +#define KImsg + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif ADDED mod/knet/knet.md Index: mod/knet/knet.md ================================================================== --- mod/knet/knet.md +++ mod/knet/knet.md @@ -0,0 +1,1 @@ +# knet ADDED mod/knet/net.h Index: mod/knet/net.h ================================================================== --- mod/knet/net.h +++ mod/knet/net.h @@ -0,0 +1,12 @@ +#ifndef KInet +#define KInet + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif ADDED mod/knum/knum.md Index: mod/knum/knum.md ================================================================== --- mod/knum/knum.md +++ mod/knum/knum.md @@ -0,0 +1,2 @@ +# knum +knum contains libk's functions for dealing with numbers. knum is intended to be much more consistent and generic than libc, with support for arbitrary bases, arbitrarily sized integers, and interfaces at varying levels of abstraction. (for instance, converting numbers to strings will not require invoking a string formatting routine or IO function; string formatting routines will invoke the same conversion functions that users can.) integers larger and smaller than processor types should also be supported, through functions over arrays and bitfield structs. ADDED mod/kproc/kproc.md Index: mod/kproc/kproc.md ================================================================== --- mod/kproc/kproc.md +++ mod/kproc/kproc.md @@ -0,0 +1,2 @@ +# kproc +kproc is libk's library for starting, handling, and controlling userspace processes. ADDED mod/kproc/proc.h Index: mod/kproc/proc.h ================================================================== --- mod/kproc/proc.h +++ mod/kproc/proc.h @@ -0,0 +1,12 @@ +#ifndef KIproc +#define KIproc + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif ADDED mod/kstr/ks_to_int.c Index: mod/kstr/ks_to_int.c ================================================================== --- mod/kstr/ks_to_int.c +++ mod/kstr/ks_to_int.c @@ -0,0 +1,11 @@ +#include +#include +#include + +kscond ks_to_int(ksraw str, enum ksconv mode, u8* dest, sz size) { + u8 base = mode & 0xFF; + kcendian endian = (mode & ksconv_endh ? kcendian_high : + mode & ksconv_endl ? kcendian_low : + kcendian_system); + return kscond_unimplemented; +} ADDED mod/kstr/kstr.md Index: mod/kstr/kstr.md ================================================================== --- mod/kstr/kstr.md +++ mod/kstr/kstr.md @@ -0,0 +1,75 @@ +# kstr + +**kstr** is the libk string library. it uses the **short** naming convention with the glyph `s`. **kstr** implies `#include `. + +## types + +### struct kstr +`struct kstr` is a structure for holding pascal strings (length-prefixed strings). it is the basic libk string type. **note:** if `ptr.ref` ≠ NULL and `sz` = 0, the string's length is unknown and should be calculated by any function that operates on a kstr, storing the result in the object if possible. + * `sz size` - length of string, excluding any null terminator + * `kmptr ptr` - pointer to string in memory + +### struct ksraw +`struct ksraw` is like `kstr` except it uses raw `char` pointers instead of a `kmptr`. + * `sz size` - length of string, excluding any null terminator + * `char* ptr` - pointer to string in memory + +### struct ksbuf +`struct ksbuf` is a structure used to hold buffers. + * `sz size` - maximum size of buffer, including any null terminator + * `char* buf` - region of memory to store buffer in + * `ksalloc strat` - allocation strategy + * `kmkind rule` - kind of allocator to use. only needs to be set if `where` is NULL. see [kmem](../kmem/kmem.md). + * `kmcell* where` - where to allocate the object, in case of pool or tree allocation. + +### struct kschain +`struct kschain` is a structure used for string accumulators that works by aggregating pointers to strings, instead of copying the strings themselves. + * `kschain_kind kind` - kind of chain + * `kmkind rule` - kind of allocation to use if `kind` ≠ `kschain_kind_linked` + * `pstr* ptrs` - pointer to pointer list + * `sz ptrc` - number of pointers + * `sz size` - total amount of space in `ptrs` + +#### enum kschain_kind + * `kschain_kind_block` - occupies a single block of memory + * `kschain_kind_linked` - uses a linked list, allocated and deallocated as necessary + +### enum ksalloc +`enum ksalloc` is an enumerator that tells libk what strategy to use when filling a `ksbuf` or `kschain` struct. + * `ksalloc_static` - do not allocate memory, fill an already-allocated, statically-sized array. + * `ksalloc_alloc` - allocate a string in memory using the specified kind of allocator. + * `ksalloc_dynamic` - fill an already-allocated array if possible, allocate a string in memory if the string length exceeds available space. + +## functions + +### kssz +`size_t kssz(char* str, size_t max)` returns the number of characters in a C string, **including** the final null. will count at most `max` characters if `max` > 0. + +### kstr +`kstr kstr(char* str, size_t max)` takes a C string and returns a P-string, calculating the length of `str` and storing it in the return value. `max` works as in `kssz`. + +### kstoraw +`ksraw ksref(kstr)` is a simple convenience function that returns the `ksraw` form of a `kstr`. + +### 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. + + char mem[512]; + kmptr text = <...>; + char* src = <...>; + kmbuf buf = { sizeof mem, &mem, kmkind_none }; + kstr chain[] = { + Kstr(""), + ksref(text), + Kstr("") + }; + 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 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. ADDED mod/kstr/str.h Index: mod/kstr/str.h ================================================================== --- mod/kstr/str.h +++ mod/kstr/str.h @@ -0,0 +1,61 @@ +#ifndef KIstr +#define KIstr + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kstr { + sz size; + kmptr ptr; +} kstr; + +typedef struct ksraw { + sz size; + const char* ptr; +} ksraw; + +typedef struct ksmut { + sz size; + char* ptr; +} ksmut; + +#include +typedef enum kscond { + kscond_ok = kscond_id, + kscond_fail, + kscond_unimplemented, + kscond_nonnumeric, +} kscond; + +enum ksconv { + ksconv_default = 0, + + ksconv_raw = 1, + ksconv_bin = 2, + ksconv_oct = 8, + ksconv_dec = 10, + ksconv_hex = 16, + ksconv_b32 = 32, + + ksconv_partial = 1 << 7, + ksconv_nopfx = 1 << 8, + ksconv_endh = 1 << 9, + ksconv_endl = 1 << 10, +}; + +kscond ks_to_int(ksraw str, + enum ksconv mode, + u8* dest, sz size); + +kscond ks_of_int(u8* number, sz size, + enum ksconv mode, + char* bufstart, sz bufsize); + +#ifdef __cplusplus +} +#endif + +#endif ADDED mod/kterm/kterm.md Index: mod/kterm/kterm.md ================================================================== --- mod/kterm/kterm.md +++ mod/kterm/kterm.md @@ -0,0 +1,1 @@ +# kterm ADDED mod/kterm/term.h Index: mod/kterm/term.h ================================================================== --- mod/kterm/term.h +++ mod/kterm/term.h @@ -0,0 +1,12 @@ +#ifndef KIterm +#define KIterm + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif