diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ea14cf..f57d32f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,11 @@ if(NATIVE_MARCH) endif() +if(WIN32) + set(ENABLE_STATIC ON) +endif() + + set(LIBS) if(ENABLE_STATIC) set(PNG_NAMES png.a libpng.a) diff --git a/src/errors.hpp b/src/errors.hpp index dbf9147..0be57f0 100644 --- a/src/errors.hpp +++ b/src/errors.hpp @@ -5,7 +5,7 @@ enum { - NO_ERROR, + NAH_NO_ERROR, MISC_ERROR, TOO_MUCH_DATA_TO_ENCODE, @@ -45,6 +45,12 @@ enum { UNLIKELY_LONG_FILE_NAME, CANNOT_CREATE_FILE, + CANNOT_READ_FROM_STDIN, + CANNOT_WRITE_TO_STDOUT, + MISMATCH_BETWEEN_BYTES_READ_AND_WRITTEN, + + COULD_NOT_GET_FILE_SIZE, + N_ERRORS }; @@ -95,6 +101,12 @@ const char* const handler_msgs[] = { "Improbably long file name", "Cannot create file", + "Cannot read from stdin", + "Cannot write to stdout", + "Mismatch between bytes read and written", + + "Could not get file size", + "" }; #endif diff --git a/src/fmt.cpp b/src/fmt.cpp index f55204c..0203e4e 100644 --- a/src/fmt.cpp +++ b/src/fmt.cpp @@ -64,7 +64,7 @@ int main(const int argc, char** argv){ if (unlikely(n_msg_bytes == 0)) handler(TRYING_TO_ENCODE_MSG_OF_0_BYTES); os::write_exact_number_of_bytes_to_stdout((char*)(&n_msg_bytes), 8); - os::sendfile_from_stdout_to_file(fp, n_msg_bytes); + os::sendfile_from_file_to_stdout(fp, n_msg_bytes); } // After all messages, signal end with signalled size of 0 constexpr char zero[32] = {0}; diff --git a/src/fmt_os.cpp b/src/fmt_os.cpp index aadaf6e..e5d184a 100644 --- a/src/fmt_os.cpp +++ b/src/fmt_os.cpp @@ -11,10 +11,20 @@ #endif +#ifdef _WIN32 +static const char path_sep = '\\'; +constexpr HANDLE INVALID_HANDLE_VALUE2 = INVALID_HANDLE_VALUE; +#else +static const char path_sep = '/'; +constexpr int INVALID_HANDLE_VALUE = -1; +constexpr int INVALID_HANDLE_VALUE2 = 0; +#endif + + char* get_parent_dir(char* path){ do { --path; - } while ((*path != '/')); + } while ((*path != path_sep)); // No need to check whether path has overflown the full file path beginning, because we can assume the root path begins with a slash - and we would not encounter root anyway. return path; } @@ -23,38 +33,75 @@ char* get_parent_dir(char* path){ char* get_child_dir(char* path, char* const end_of_full_file_path){ do { ++path; - } while ((*path != '/') and (path != end_of_full_file_path)); - return (*path == '/') ? path : nullptr; + } while ((*path != path_sep) and (path != end_of_full_file_path)); + return (*path == path_sep) ? path : nullptr; } bool mkdir_path_between_pointers(char* const start, char* const end){ *end = 0; + #ifdef _WIN32 + const bool rc = CreateDirectoryA(start, nullptr); // TODO: Set security attributes + // Return code doesn't distinguish between ERROR_ALREADY_EXISTS and ERROR_PATH_NOT_FOUND + #else const int rc = mkdir(start, S_IRUSR | S_IWUSR | S_IXUSR); if (rc == -1){ if (unlikely(errno != ENOENT)) handler(CANNOT_CREATE_FILE, start); } - *end = '/'; + #endif + *end = path_sep; return (rc == 0); // i.e. return true on a success } +fout_typ create_file(const char* const file_path){ + #ifdef _WIN32 + return CreateFileA(file_path, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + #else + return open(file_path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IXUSR); + #endif +} + + +fout_typ open_file_for_reading(const char* const file_path){ + #ifdef _WIN32 + return CreateFileA(file_path, GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + #else + return open(file_path, O_RDONLY); + #endif +} + + +void close_file_handle(const fout_typ fd){ + #ifdef _WIN32 + CloseHandle(fd); + #else + close(fd); + #endif +} + + namespace os { -int create_file_with_parent_dirs(char* const file_path, const size_t file_path_len){ +fout_typ create_file_with_parent_dirs(char* const file_path, const size_t file_path_len){ #ifdef CHITTY_CHATTY fprintf(stderr, "Creating file: %s\n", file_path); #endif - int fd = open(file_path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IXUSR); - if (likely(fd != -1)) + fout_typ fd = create_file(file_path); + + if (likely(fd != INVALID_HANDLE_VALUE)) // File successfully created return fd; + #ifdef _WIN32 + // No information in documentation about possible error codes of GetLastError(), so we'll just assume that the error was due to a parent directory not existing + #else if (unlikely(errno != ENOENT)){ handler(CANNOT_CREATE_FILE, file_path); } + #endif // ENOENT: Either a directory component in pathname does not exist or is a dangling symbolic link @@ -74,37 +121,83 @@ int create_file_with_parent_dirs(char* const file_path, const size_t file_path_ mkdir_path_between_pointers(file_path, path); } - return open(file_path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IXUSR); + return create_file(path); } void read_exact_number_of_bytes_from_stdin(char* const buf, const size_t n){ size_t offset = 0; do { + #ifdef _WIN32 + DWORD n_bytes_read; + if (unlikely(ReadFile(GetStdHandle(STD_INPUT_HANDLE), buf + offset, n - offset, &n_bytes_read, nullptr) == 0)) + handler(CANNOT_READ_FROM_STDIN); + offset += n_bytes_read; + #else offset += read(STDIN_FILENO, buf + offset, n - offset); + #endif } while (offset != n); } void write_exact_number_of_bytes_to_stdout(char* const buf, size_t n){ do { + #ifdef _WIN32 + DWORD n_bytes_read; + if (unlikely(WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, n, &n_bytes_read, nullptr) == 0)) + handler(CANNOT_WRITE_TO_STDOUT); + n -= n_bytes_read; + #else n -= write(STDOUT_FILENO, buf, n); + #endif } while (n != 0); } -void sendfile_from_stdout_to_file(const char* const fp, const size_t n_bytes){ - const int msg_file = open(fp, O_RDONLY); - if (unlikely(msg_file == 0)) +#ifdef _WIN32 +void win__transfer_data_between_files(HANDLE in, HANDLE out, size_t n_bytes){ + do { + static char buf[1024 * 64]; + + size_t n_bytes_to_transfer = sizeof(buf); + if (n_bytes_to_transfer > n_bytes) + n_bytes_to_transfer = n_bytes; + + DWORD n_bytes_read; + if (unlikely(ReadFile(in, buf, n_bytes_to_transfer, &n_bytes_read, nullptr) == 0)) + handler(CANNOT_READ_FROM_STDIN); + DWORD n_bytes_written; + if (unlikely(WriteFile(out, buf, n_bytes_to_transfer, &n_bytes_written, nullptr) == 0)) + handler(CANNOT_WRITE_TO_STDOUT); + + if (unlikely((n_bytes_read != n_bytes_to_transfer) or (n_bytes_written != n_bytes_to_transfer))) + handler(MISMATCH_BETWEEN_BYTES_READ_AND_WRITTEN); + + n_bytes -= n_bytes_to_transfer; + } while (n_bytes != 0); +} +#endif + + +void sendfile_from_file_to_stdout(const char* const fp, const size_t n_bytes){ + const fout_typ msg_file = open_file_for_reading(fp); + if (unlikely(msg_file == INVALID_HANDLE_VALUE2)) handler(CANNOT_OPEN_FILE); + #ifdef _WIN32 + win__transfer_data_between_files(msg_file, GetStdHandle(STD_OUTPUT_HANDLE), n_bytes); + #else const auto rc5 = sendfile(STDOUT_FILENO, msg_file, nullptr, n_bytes); if (unlikely(rc5 == -1)) handler(SENDFILE_ERROR); - close(msg_file); + #endif + close_file_handle(msg_file); } void splice_from_stdin_to_fd(const fout_typ fout, const size_t n_bytes){ + #ifdef _WIN32 + win__transfer_data_between_files(GetStdHandle(STD_INPUT_HANDLE), fout, n_bytes); + #else loff_t n_bytes_written = 0; size_t n_bytes_yet_to_write = n_bytes; do { @@ -129,16 +222,26 @@ void splice_from_stdin_to_fd(const fout_typ fout, const size_t n_bytes){ } n_bytes_yet_to_write -= n_writ; } while(n_bytes_yet_to_write != 0); + #endif } #ifdef EMBEDDOR size_t get_file_sz(const char* const fp){ + #ifdef _WIN32 + HANDLE const f = open_file_for_reading(fp); + _LARGE_INTEGER f_sz; // For x86_32 compatibility + if (unlikely(GetFileSizeEx(f, &f_sz) == 0)) + handler(COULD_NOT_GET_FILE_SIZE); + close_file_handle(f); + return f_sz.QuadPart; + #else static struct stat stat_buf; const auto rc3 = stat(fp, &stat_buf); if (unlikely(rc3 == -1)) handler(COULD_NOT_STAT_FILE, fp); return stat_buf.st_size; + #endif } #endif diff --git a/src/fmt_os.hpp b/src/fmt_os.hpp index c148eb1..7f212dd 100644 --- a/src/fmt_os.hpp +++ b/src/fmt_os.hpp @@ -4,6 +4,7 @@ #ifdef _WIN32 +# include typedef HANDLE fout_typ; # define STDOUT_DESCR GetStdHandle(STD_OUTPUT_HANDLE); #else @@ -20,11 +21,11 @@ void read_exact_number_of_bytes_from_stdin(char* const buf, const size_t n); void write_exact_number_of_bytes_to_stdout(char* const buf, size_t n); -void sendfile_from_stdout_to_file(const char* const fp, const size_t n_bytes); +void sendfile_from_file_to_stdout(const char* const fp, const size_t n_bytes); void splice_from_stdin_to_fd(const fout_typ fout, const size_t n_bytes); -int create_file_with_parent_dirs(char* const file_path, const size_t file_path_len); +fout_typ create_file_with_parent_dirs(char* const file_path, const size_t file_path_len); size_t get_file_sz(const char* const fp); diff --git a/src/os.cpp b/src/os.cpp index 4f99083..7ab91a3 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1,6 +1,40 @@ #include "os.hpp" +#define WHILE_CONDITION while(not bpcs_stream.exhausted) +#ifdef ONLY_COUNT +# define DO_OR_WHILE WHILE_CONDITION +# define WHILE_OR_DO +#else +# define DO_OR_WHILE do +# define WHILE_OR_DO WHILE_CONDITION; +#endif + + +#ifdef _WIN32 +template +bool write_to_stdout(uchar(&io_buf)[sz]){ + LPDWORD n_bytes_read_ptr; + if (unlikely(WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), io_buf, sz, n_bytes_read_ptr, nullptr) != 0)) + return true; + return (*n_bytes_read_ptr == sz); +} +# define WRITE_STMT write_to_stdout(io_buf) +template +bool read_from_stdin(uchar(&io_buf)[sz]){ + LPDWORD n_bytes_read_ptr; + if (unlikely(ReadFile(GetStdHandle(STD_INPUT_HANDLE), io_buf, BYTES_PER_GRID, n_bytes_read_ptr, nullptr) != 0)) + return true; + return (*n_bytes_read_ptr == BYTES_PER_GRID); +} +# define READ_STMT read_from_stdin(io_buf) +#else +# include +# define WRITE_STMT write(STDOUT_FILENO, io_buf, sizeof(io_buf)) != sizeof(io_buf) +# define READ_STMT read (STDIN_FILENO, io_buf, BYTES_PER_GRID) == BYTES_PER_GRID +#endif + + namespace os { diff --git a/src/os.hpp b/src/os.hpp index a53385e..1f51ee8 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -1,38 +1,8 @@ #pragma once #include "bpcs.hpp" - - -#define WHILE_CONDITION while(not bpcs_stream.exhausted) -#ifdef ONLY_COUNT -# define DO_OR_WHILE WHILE_CONDITION -# define WHILE_OR_DO -#else -# define DO_OR_WHILE do -# define WHILE_OR_DO WHILE_CONDITION; -#endif - #ifdef _WIN32 -inline -bool write_to_stdout(){ - long n_bytes_read; - if (unlikely(WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), io_buf, sizeof(io_buf), &n_bytes_read, nullptr) != 0)) - return true; - return (n_bytes_read == sizeof(io_buf)); -} -# define WRITE_STMT write_to_stdout() -inline -bool read_from_stdin(){ - long n_bytes_read; - if (unlikely(ReadFile(GetStdHandle(STD_INPUT_HANDLE), io_buf, BYTES_PER_GRID, &n_bytes_read, nullptr) != 0)) - return true; - return (n_bytes_read == BYTES_PER_GRID); -} -# define READ_STMT read_from_stdin() -#else -# include -# define WRITE_STMT write(STDOUT_FILENO, io_buf, sizeof(io_buf)) != sizeof(io_buf) -# define READ_STMT read (STDIN_FILENO, io_buf, BYTES_PER_GRID) == BYTES_PER_GRID +# include "windows.h" #endif