Index: kcore/core.h ================================================================== --- kcore/core.h +++ kcore/core.h @@ -1,10 +1,14 @@ #ifndef KIcore #define KIcore #include #include #include + +#ifdef __cplusplus +extern "C" { +#endif typedef struct kvar { ksraw name; ksraw val; char* platform; @@ -79,10 +83,11 @@ # 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 @@ -108,29 +113,42 @@ # if __cplusplus >= 201103L # define null nullptr # else # define null ((void*)0) # endif -#elif defined __STDC__ +#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. i was surprised too. */ + * 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 -# define noreturn [[ noreturn ]] + [[noreturn]] void kstop(stat_long code); #elif __STDC_VERSION__ >= 201103L -# define noreturn _Noreturn + _Noreturn void kstop(stat_long code); #else -# define noreturn + void kstop(stat_long code); #endif -noreturn void kstop(stat_long code); - -#ifdef KFclean -# undef noreturn +#ifdef __cplusplus +} #endif #endif Index: kfile/file.h ================================================================== --- kfile/file.h +++ kfile/file.h @@ -1,4 +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 Index: kfile/kfile.md ================================================================== --- kfile/kfile.md +++ kfile/kfile.md @@ -1,1 +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; + } Index: kgraft/graft.h ================================================================== --- kgraft/graft.h +++ kgraft/graft.h @@ -1,4 +1,12 @@ #ifndef KIgraft #define KIgraft + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif #endif Index: kio/io.h ================================================================== --- kio/io.h +++ kio/io.h @@ -9,10 +9,14 @@ */ #include #include #include + +#ifdef __cplusplus +extern "C" { +#endif typedef enum kiostream_kind { kiostream_closed, // this kiostream cannot be written to kiostream_file, @@ -24,10 +28,13 @@ // 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 { @@ -56,9 +63,13 @@ } 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 + // kmkind is only used if kmcell* is null kiocond kiocon(kiochan, kiochan); // connect one channel to another + +#ifdef __cplusplus +} +#endif #endif Index: kmem/mem.h ================================================================== --- kmem/mem.h +++ kmem/mem.h @@ -3,10 +3,14 @@ #include #ifndef KFclean # define Kmsz(e) ( sizeof (e) / sizeof (e) [0] ) #endif + +#ifdef __cplusplus +extern "C" { +#endif typedef enum kmkind { kmkind_none, kmkind_heap, kmkind_pool, @@ -37,7 +41,11 @@ /* heap functions */ void* kmheapa(sz); void kmheapf(void*); + +#ifdef __cplusplus +} +#endif #endif Index: kmsg/msg.h ================================================================== --- kmsg/msg.h +++ kmsg/msg.h @@ -1,4 +1,12 @@ #ifndef KImsg #define KImsg + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif #endif Index: knet/net.h ================================================================== --- knet/net.h +++ knet/net.h @@ -1,4 +1,12 @@ #ifndef KInet #define KInet + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif #endif Index: kproc/proc.h ================================================================== --- kproc/proc.h +++ kproc/proc.h @@ -1,4 +1,12 @@ #ifndef KIproc #define KIproc + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif #endif Index: kstr/str.h ================================================================== --- kstr/str.h +++ kstr/str.h @@ -1,9 +1,13 @@ #ifndef KIstr #define KIstr #include + +#ifdef __cplusplus +extern "C" { +#endif typedef struct kstr { sz size; kmptr ptr; } kstr; @@ -15,6 +19,11 @@ typedef struct ksmut { sz size; char* ptr; } ksmut; + +#ifdef __cplusplus +} +#endif + #endif Index: kterm/term.h ================================================================== --- kterm/term.h +++ kterm/term.h @@ -1,4 +1,12 @@ #ifndef KIterm #define KIterm + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif #endif