/// \file /// a bunch of OS-dependent functions #ifndef OS_H_ #define OS_H_ #include "base.h" typedef enum { FS_NON_EXISTENT, FS_FILE, FS_DIRECTORY, FS_OTHER } FsType; enum { FS_PERMISSION_READ = 0x01, FS_PERMISSION_WRITE = 0x02, }; typedef u8 FsPermission; typedef struct { FsType type; char name[]; } FsDirectoryEntry; /// returns what kind of thing this is. FsType fs_path_type(const char *path); FsPermission fs_path_permission(const char *path); /// Does this file exist? Returns false for directories. bool fs_file_exists(const char *path); /// Returns a NULL-terminated array of the files/directories in this directory, or NULL if the directory does not exist/out of memory. /// When you're done with the entries, call fs_dir_entries_free (or call free on each entry, then on the whole array). /// NOTE: The files/directories aren't returned in any particular order! FsDirectoryEntry **fs_list_directory(const char *dirname); /// Create the directory specified by `path`\n /// Returns:\n /// 1 if the directory was created successfully\n /// 0 if the directory already exists\n /// -1 if the path already exists, but it's not a directory, or if there's another error (e.g. don't have permission to create directory).\n int fs_mkdir(const char *path); // Puts the current working directory into buf, including a null-terminator, writing at most buflen bytes.\n // Returns:\n // 1 if the working directory was inserted into buf successfully\n // 0 if buf is too short to hold the cwd\n // -1 if we can't get the cwd for whatever reason.\n int os_get_cwd(char *buf, size_t buflen); // Unlike ISO C rename() function, this will overwrite `newname` if it exists.\n // Returns:\n // >= 0 if successful\n // < 0 on error\n int os_rename_overwrite(const char *oldname, const char *newname); struct timespec time_last_modified(const char *filename); struct timespec time_get(void); /// sleep for a certain number of nanoseconds void time_sleep_ns(u64 ns); /// runs xdg-open or equivalent on the given path, which can be a URL. /// /// returns `true` on success. bool open_with_default_application(const char *path); /// equivalent to POSIX function chdir, but returns `true` on success and `false` on failure. bool change_directory(const char *path); /// free the entries generated by fs_list_directory. static void fs_dir_entries_free(FsDirectoryEntry **entries) { for (int i = 0; entries[i]; ++i) free(entries[i]); free(entries); } /// get current time in seconds since some arbitrary point static double time_get_seconds(void) { struct timespec t = time_get(); return (double)t.tv_sec + (double)t.tv_nsec * 1e-9; } /// sleep for microseconds static void time_sleep_us(u64 us) { time_sleep_ns(us * 1000); } /// sleep for milliseconds static void time_sleep_ms(u64 ms) { time_sleep_ns(ms * 1000000); } /// sleep for seconds static void time_sleep_s(u64 s) { time_sleep_ns(s * 1000000000); } /// a process typedef struct Process Process; /// zero everything except what you're using typedef struct { bool separate_stderr; const char *working_directory; /// for forwards compatibility char _reserved[256]; } ProcessSettings; typedef struct { /// string like "exited with code 9" char message[62]; /// it might be possible that both `signalled` and `exited` are false, /// if something weird happens. bool signalled; bool exited; /// only relevant if `exited = true` int exit_code; /// only relevant if `signalled = true` int signal; } ProcessExitInfo; /// get process ID of this process int process_get_id(void); /// execute the given command (like if it was passed to `system()`), creating a new \ref Process object. /// /// returns a valid process object on failure, but it will have an error, according to \ref process_geterr Process *process_run_ex(const char *command, const ProcessSettings *settings); /// like \ref process_run_ex, but with the default settings Process *process_run(const char *command); /// returns the error last error produced, or NULL if there was no error. const char *process_geterr(Process *process); /// write to stdin /// /// \returns -2 on error,\n /// -1 if the read end of the pipe was closed,\n /// or a non-negative number indicating the number of bytes written. long long process_write(Process *process, const char *data, size_t size); /// read from stdout+stderr. /// /// \returns /// -2 on error\n /// -1 if no data is available right now\n /// 0 on end of file\n /// or a positive number indicating the number of bytes read to `data` (at most `size`)\n /// This does a nonblocking read. long long process_read(Process *process, char *data, size_t size); /// like \ref process_read, but reads stderr. /// /// this function ALWAYS RETURNS -2 if `separate_stderr` is not specified in the \ref ProcessSettings. /// if `separate_stderr` is false, then both stdout and stderr will be sent via \ref process_read. long long process_read_stderr(Process *process, char *data, size_t size); /// Checks if the process has exited. /// /// \returns /// -1 if the process returned a non-zero exit code, or got a signal.\n /// 1 if the process exited successfully\n /// 0 if the process hasn't exited.\n /// If the process has exited, `*info` will be filled out with details. /// if the process is no longer running, `*process` will be freed and set to NULL. int process_check_status(Process **process, ProcessExitInfo *info); /// kills process if still running /// /// this also frees any resources used by `*process`. /// `*process` will be set to NULL. void process_kill(Process **process); typedef struct Socket Socket; /// create TCP socket with address and port. /// /// currently only supports IPv4. /// if you pass `NULL` for `address`, `127.0.0.1` will be used. Socket *socket_connect_tcp(const char *address, u16 port); /// get last error from socket /// /// returns `""` if there is no error. const char *socket_get_error(Socket *socket); /// read from socket. /// /// \returns /// -2 on error\n /// -1 if no data is available right now\n /// 0 on end of file\n /// or a positive number indicating the number of bytes read to `data` (at most `size`)\n /// This does a nonblocking read. long long socket_read(Socket *socket, char *data, size_t size); /// write to socket /// /// \returns -2 on error\n /// -1 if the read end of the socket was closed\n /// or a non-negative number indicating the number of bytes written. long long socket_write(Socket *socket, const char *data, size_t size); /// close socket /// /// if `*psocket` is `NULL`, this does nothing. /// /// sets `*psocket` to `NULL`. void socket_close(Socket **psocket); #endif // OS_H_