/** * @file File.h * @author apio (cloudapio.eu) * @brief A C++-friendly API for file access. * * @copyright Copyright (c) 2023, the Luna authors. * */ #pragma once #include #include #include #include #include #include #include #include #include namespace os { /** * @brief An object-oriented file handle, which is closed when all references to it go out of scope. */ class File : public Shareable { public: /** * @brief The supported file opening modes. */ enum OpenMode { ReadOnly = O_RDONLY, // Open the file read-only. WriteOnly = O_WRONLY | O_TRUNC, // Open the file write-only, truncating it. ReadWrite = O_RDWR | O_TRUNC, // Open the file read-write, truncating it. Append = O_WRONLY | O_APPEND, // Open the file write-only, and append to it. ReadAppend = O_RDWR | O_APPEND, // Open the file read-write, and append to it. }; enum class SeekMode { Beginning = SEEK_SET, // Seek from the beginning of the file. Current = SEEK_CUR, // Seek from the current file position. End = SEEK_END, // Seek from the end of the file. }; /** * @brief Create a new File object from a file path, opening the file. * * @param path The path to open. * @param flags The opening mode to pass to open(2). * @return Result> A new File object, or ENOENT if the file did not exist. */ static Result> open(const Path& path, OpenMode flags); /** * @brief Create a new File object from a file path, opening the file or creating it if it does not exist. * * @param path The path to open. * @param flags The opening mode to pass to open(2). * @param mode If a new file is created, the file permissions to apply to it. (default: u=rw,g=r,o=r) * @return Result> A new File object. */ static Result> open_or_create(const Path& path, OpenMode flags, mode_t mode = 0644); /** * @brief Create a new File object from a file path, creating the file but erroring out if the file already * exists. * * @param path The path to open. * @param flags The opening mode to pass to open(2). * @param mode The file permissions to apply to the created file. (default: u=rw,g=r,o=r) * @return Result> A new File object, or EEXIST if the file already existed. */ static Result> create(const Path& path, OpenMode flags, mode_t mode = 0644); /** * @brief If path is "-", return standard input (as is common for many CLI apps). Otherwise, open path for * reading. * * @param path The path to open. * @return Result> A new File object if path was not "-", or the File * object for standard input if path was "-". */ static Result> open_input_file(StringView path); /** * @brief Returns the File corresponding to standard input. * * @return SharedPtr The File object for standard input. */ static SharedPtr standard_input(); /** * @brief Returns the File corresponding to standard output. * * @return SharedPtr The File object for standard output. */ static SharedPtr standard_output(); /** * @brief Returns the File corresponding to standard error. * * @return SharedPtr The File object for standard error. */ static SharedPtr standard_error(); /** * @brief Make this File object automatically close on the next call to execve(2). */ void set_close_on_exec(); /** * @brief Write a string to this File. * * @param str The string to write (doesn't have to be null-terminated). * @return Result Whether the operation succeeded. */ Result write(StringView str); /** * @brief Write a memory buffer to this File. * * @param buf The buffer to write. * @return Result Whether the operation succeeded. */ Result write(const Buffer& buf); /** * @brief Write a formatted string to this File. * * @param format The format string. * @param ... The format arguments. * @return Result Whether the operation succeeded. */ Result write_formatted(const char* format, ...) _format(2, 3); /** * @brief Write a formatted string to this File. * * @param format The format string. * @param args The format arguments. * @return Result Whether the operation succeeded. */ Result write_vformatted(const char* format, va_list args); /** * @brief Read a line from this File. * * @return Result The line as an owned String object, or an empty string if there is no more * data to read. */ Result read_line(); /** * @brief Read the entire File's contents as a string. * * @return Result The file's contents as an owned String object. */ Result read_all_as_string(); /** * @brief Read data from this File. * * @param buf The output buffer to store the data in. Will be resized to fit the data. * @param size The maximum number of bytes to read. * @return Result Whether the operation succeeded. */ Result read(Buffer& buf, usize size); /** * @brief Read an object from this File. * * @tparam T The type of the object to read. * @param object A reference to the object in question. * @return Result Whether the operation succeeded. */ template Result read_typed(T& object) { TRY(raw_read((u8*)&object, sizeof(T))); return {}; } /** * @brief Read the entire File's contents. * * @return Result The file's contents as an owned Buffer object. */ Result read_all(); /** * @brief Read a single character from the File. * * @return Result A character value above 0 (to be cast to an unsigned char), or EOF (typically -1) * if there is no more data to read. */ Result getchar(); /** * @brief Returns the file descriptor associated with this File. * * @return int The file descriptor. */ int fd() const { return fileno(m_file); } /** * @brief Flushes the stream's buffers. */ void flush(); /** * @brief Move this File's file pointer back to the beginning. */ void rewind(); /** * @brief Change the file pointer's position. * * @param offset The number of bytes to move the file pointer (can be negative, to move backwards). * @param mode Where to seek from. * @return Result Whether the operation succeeded. */ Result seek(off_t offset, SeekMode mode); /** * @brief Read the file pointer's current position. * * @return Result An error, or the file pointer's position. */ Result tell(); /** * @brief Buffering modes for a File. */ enum BufferingMode { NotBuffered = _IONBF, LineBuffered = _IOLBF, FullyBuffered = _IOFBF, }; /** * @brief Change the buffering mode of this File. * * @param mode The buffering mode. */ void set_buffer(BufferingMode mode); File(Badge); ~File(); private: static Result> construct(const Path& path, int flags, mode_t mode); static void initialize_standard_streams(); Result raw_read(u8* buf, usize length); Result raw_write(const u8* buf, usize length); FILE* m_file { nullptr }; }; /** * @brief Print a formatted string to standard output. * * @param format The format string (in the same format as printf(3)). * @param ... The format arguments. * @return Result Whether the operation succeeded. */ Result print(const char* format, ...) _format(1, 2); /** * @brief Print a newline-terminated formatted string to standard output. * * @param format The format string (in the same format as printf(3)). * @param ... The format arguments. * @return Result Whether the operation succeeded. */ Result println(const char* format, ...) _format(1, 2); /** * @brief Print a formatted string to standard error. * * @param format The format string (in the same format as printf(3)). * @param ... The format arguments. * @return Result Whether the operation succeeded. */ Result eprint(const char* format, ...) _format(1, 2); /** * @brief Print a newline-terminated formatted string to standard error. * * @param format The format string (in the same format as printf(3)). * @param ... The format arguments. * @return Result Whether the operation succeeded. */ Result eprintln(const char* format, ...) _format(1, 2); }