From 07830cda02e756ce7db961bee37d20326239e603 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Thu, 21 May 2020 21:42:18 -0500 Subject: [PATCH] Progress: continue redesign of file and directoy structure This gets the code in a state where I can focus on updating all of the programs (level_3 files). The UTF-8 related types have @todo and @fixme as I intend to come back later and finish them. --- build/monolithic/settings | 2 +- level_0/f_directory/c/directory.h | 14 +- level_0/f_file/c/file.c | 167 ++++++++-- level_0/f_file/c/file.h | 141 ++++++++- level_0/f_file/c/private-file.c | 42 +++ level_0/f_file/c/private-file.h | 36 +++ level_1/fl_directory/c/private-directory.c | 6 +- level_1/fl_directory/c/private-directory.h | 6 +- level_1/fl_fss/c/fss.c | 2 +- level_1/fl_utf_file/c/private-utf_file.c | 113 +++++++ level_1/fl_utf_file/c/private-utf_file.h | 36 +++ level_1/fl_utf_file/c/utf_file.c | 483 +++++++++++------------------ level_1/fl_utf_file/c/utf_file.h | 196 ++++++------ 13 files changed, 798 insertions(+), 446 deletions(-) diff --git a/build/monolithic/settings b/build/monolithic/settings index a07ef81..83bc815 100644 --- a/build/monolithic/settings +++ b/build/monolithic/settings @@ -11,7 +11,7 @@ build_compiler gcc build_linker ar build_libraries -lc build_libraries_fll -build_sources_library level_0/console.c level_0/conversion.c level_0/directory.c level_0/private-directory.c level_0/environment.c level_0/private-environment.c level_0/file.c level_0/private-file.c level_0/memory.c level_0/path.c level_0/pipe.c level_0/print.c level_0/utf.c level_0/private-utf.c level_1/color.c level_1/console.c level_1/directory.c level_1/file.c level_1/fss.c level_1/fss_basic.c level_1/fss_basic_list.c level_1/fss_extended.c level_1/fss_extended_list.c level_1/print.c level_1/serialized.c level_1/private-serialized.c level_1/socket.c level_1/status.c level_1/string.c level_1/private-string.c level_1/utf.c level_1/private-utf.c level_1/utf_file.c level_1/private-utf_file.c level_2/execute.c level_2/private-execute.c level_2/file.c level_2/fss.c level_2/fss_basic.c level_2/fss_basic_list.c level_2/fss_extended.c level_2/fss_extended_list.c level_2/fss_status.c level_2/program.c level_2/status.c +build_sources_library level_0/console.c level_0/conversion.c level_0/directory.c level_0/private-directory.c level_0/environment.c level_0/private-environment.c level_0/file.c level_0/private-file.c level_0/memory.c level_0/path.c level_0/pipe.c level_0/print.c level_0/utf.c level_0/private-utf.c level_1/color.c level_1/console.c level_1/directory.c level_1/private-directory.c level_1/file.c level_1/fss.c level_1/fss_basic.c level_1/fss_basic_list.c level_1/fss_extended.c level_1/fss_extended_list.c level_1/print.c level_1/serialized.c level_1/private-serialized.c level_1/socket.c level_1/status.c level_1/string.c level_1/private-string.c level_1/utf.c level_1/private-utf.c level_1/utf_file.c level_1/private-utf_file.c level_2/execute.c level_2/private-execute.c level_2/file.c level_2/fss.c level_2/fss_basic.c level_2/fss_basic_list.c level_2/fss_extended.c level_2/fss_extended_list.c level_2/fss_status.c level_2/program.c level_2/status.c build_sources_program build_sources_headers level_0/color.h level_0/console.h level_0/conversion.h level_0/directory.h level_0/environment.h level_0/file.h level_0/fss.h level_0/memory.h level_0/path_fll.h level_0/path_filesystem.h level_0/path.h level_0/pipe.h level_0/print.h level_0/serialized.h level_0/socket.h level_0/status.h level_0/string.h level_0/type.h level_0/type_array.h level_0/utf.h level_1/color.h level_1/console.h level_1/directory.h level_1/file.h level_1/fss.h level_1/fss_basic.h level_1/fss_basic_list.h level_1/fss_status.h level_1/fss_extended.h level_1/fss_extended_list.h level_1/fss_macro.h level_1/print.h level_1/serialized.h level_1/socket.h level_1/status.h level_1/string.h level_1/utf.h level_1/utf_file.h level_2/execute.h level_2/file.h level_2/fss.h level_2/fss_basic.h level_2/fss_basic_list.h level_2/fss_extended.h level_2/fss_extended_list.h level_2/fss_status.h level_2/program.h level_2/status.h build_sources_bash diff --git a/level_0/f_directory/c/directory.h b/level_0/f_directory/c/directory.h index 2432bd3..6c1e537 100644 --- a/level_0/f_directory/c/directory.h +++ b/level_0/f_directory/c/directory.h @@ -133,13 +133,13 @@ extern "C" { */ #ifndef _di_f_directory_mode_ typedef struct { - f_string_dynamics block; // S_IFBLK - f_string_dynamics character; // S_IFCHR - f_string_dynamics directory; // S_IFDIR - f_string_dynamics file; // S_IFREG - f_string_dynamics link; // S_IFLNK - f_string_dynamics socket; // S_IFSOCK - f_string_dynamics unknown; + mode_t block; // S_IFBLK + mode_t character; // S_IFCHR + mode_t directory; // S_IFDIR + mode_t file; // S_IFREG + mode_t link; // S_IFLNK + mode_t socket; // S_IFSOCK + mode_t unknown; } f_directory_mode; #define f_directory_mode_initialize { \ diff --git a/level_0/f_file/c/file.c b/level_0/f_file/c/file.c index f47c90b..0ef6071 100644 --- a/level_0/f_file/c/file.c +++ b/level_0/f_file/c/file.c @@ -364,26 +364,22 @@ extern "C" { #endif // _di_f_file_open_at_ #ifndef _di_f_file_read_ - f_return_status f_file_read(f_file *file, f_string_dynamic *buffer) { + f_return_status f_file_read(const f_file file, f_string_dynamic *buffer) { #ifndef _di_level_0_parameter_checking_ - if (file == 0) return f_status_set_error(f_invalid_parameter); - if (file->size_chunk == 0) return f_status_set_error(f_invalid_parameter); - if (file->size_block == 0) return f_status_set_error(f_invalid_parameter); + if (file.size_read == 0) return f_status_set_error(f_invalid_parameter); if (buffer->used >= buffer->size) return f_status_set_error(f_invalid_parameter); #endif // _di_level_0_parameter_checking_ - if (file->id <= 0) return f_status_set_error(f_file_not_open); + if (file.id <= 0) return f_status_set_error(f_file_not_open); f_status status = f_none; ssize_t size_read = 0; - // use a custom buffer so that memory is allocated post-read instead of pre-read. - const f_string_length buffer_size = file->size_chunk * file->size_block; - char buffer_read[buffer_size]; + char buffer_read[file.size_read]; - memset(&buffer_read, 0, sizeof(buffer_size)); + memset(&buffer_read, 0, sizeof(file.size_read)); - while ((size_read = read(file->id, buffer_read, buffer_size)) > 0) { + while ((size_read = read(file.id, buffer_read, file.size_read)) > 0) { if (buffer->used + size_read > buffer->size) { if (buffer->size + size_read > f_string_length_size) { return f_status_set_error(f_string_too_large); @@ -393,7 +389,7 @@ extern "C" { if (f_status_is_error(status)) return status; } - memcpy(buffer->string + buffer->used, buffer_read, buffer_size); + memcpy(buffer->string + buffer->used, buffer_read, size_read); buffer->used += size_read; } // while @@ -418,25 +414,22 @@ extern "C" { #endif // _di_f_file_read_ #ifndef _di_f_file_read_block_ - f_return_status f_file_read_block(f_file *file, f_string_dynamic *buffer) { + f_return_status f_file_read_block(const f_file file, f_string_dynamic *buffer) { #ifndef _di_level_0_parameter_checking_ - if (file == 0) return f_status_set_error(f_invalid_parameter); - if (file->size_chunk == 0) return f_status_set_error(f_invalid_parameter); - if (file->size_block == 0) return f_status_set_error(f_invalid_parameter); + if (file.size_read == 0) return f_status_set_error(f_invalid_parameter); if (buffer->used >= buffer->size) return f_status_set_error(f_invalid_parameter); #endif // _di_level_0_parameter_checking_ - if (file->id <= 0) return f_status_set_error(f_file_not_open); + if (file.id <= 0) return f_status_set_error(f_file_not_open); f_status status = f_none; ssize_t size_read = 0; - const f_string_length buffer_size = file->size_chunk * file->size_block; - char buffer_read[buffer_size]; + char buffer_read[file.size_read]; - memset(&buffer_read, 0, sizeof(buffer_size)); + memset(&buffer_read, 0, sizeof(file.size_read)); - if ((size_read = read(file->id, buffer_read, buffer_size)) > 0) { + if ((size_read = read(file.id, buffer_read, file.size_read)) > 0) { if (buffer->used + size_read > buffer->size) { if (buffer->size + size_read > f_string_length_size) { return f_status_set_error(f_string_too_large); @@ -446,7 +439,7 @@ extern "C" { if (f_status_is_error(status)) return status; } - memcpy(buffer->string + buffer->used, buffer_read, buffer_size); + memcpy(buffer->string + buffer->used, buffer_read, size_read); buffer->used += size_read; } @@ -471,20 +464,18 @@ extern "C" { #endif // _di_f_file_read_block_ #ifndef _di_f_file_read_until_ - f_return_status f_file_read_until(f_file *file, f_string_dynamic *buffer, const f_string_length total) { + f_return_status f_file_read_until(const f_file file, f_string_dynamic *buffer, const f_string_length total) { #ifndef _di_level_0_parameter_checking_ - if (file == 0) return f_status_set_error(f_invalid_parameter); - if (file->size_chunk == 0) return f_status_set_error(f_invalid_parameter); - if (file->size_block == 0) return f_status_set_error(f_invalid_parameter); + if (file.size_read == 0) return f_status_set_error(f_invalid_parameter); if (buffer->used >= buffer->size) return f_status_set_error(f_invalid_parameter); #endif // _di_level_0_parameter_checking_ - if (file->id <= 0) return f_status_set_error(f_file_not_open); + if (file.id <= 0) return f_status_set_error(f_file_not_open); f_status status = f_none; ssize_t size_read = 0; - f_string_length buffer_size = file->size_chunk * file->size_block; + f_string_length buffer_size = file.size_read; f_string_length buffer_count = 0; if (total < buffer_size) { @@ -495,7 +486,7 @@ extern "C" { memset(&buffer_read, 0, sizeof(buffer_size)); - if ((size_read = read(file->id, buffer_read, buffer_size)) > 0) { + while (buffer_count < total && (size_read = read(file.id, buffer_read, buffer_size)) > 0) { if (buffer->used + size_read > buffer->size) { if (buffer->size + size_read > f_string_length_size) { return f_status_set_error(f_string_too_large); @@ -505,10 +496,10 @@ extern "C" { if (f_status_is_error(status)) return status; } - memcpy(buffer->string + buffer->used, buffer_read, buffer_size); + memcpy(buffer->string + buffer->used, buffer_read, size_read); buffer->used += size_read; buffer_count += size_read; - } + } // while if (size_read == 0) { return f_none_on_eof; @@ -695,6 +686,122 @@ extern "C" { } #endif // _di_f_file_stat_by_id_ +#ifndef _di_f_file_write_ + f_return_status f_file_write(const f_file file, const f_string_dynamic buffer, f_string_length *written) { + #ifndef _di_level_0_parameter_checking_ + if (file.size_write == 0) return f_status_set_error(f_invalid_parameter); + if (buffer.used >= buffer.size) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ + + if (file.id <= 0) return f_status_set_error(f_file_not_open); + + if (buffer.used == 0) { + *written = 0; + return f_no_data; + } + + f_status status = private_f_file_write_until(file, buffer.string, buffer.used, written); + if (f_status_is_error(status)) return f_status_set_error(status); + + if (status == f_none && *written == buffer.used) return f_none_on_eos; + + return status; + } +#endif // _di_f_file_write_ + +#ifndef _di_f_file_write_block_ + f_return_status f_file_write_block(const f_file file, const f_string_dynamic buffer, f_string_length *written) { + #ifndef _di_level_0_parameter_checking_ + if (file.size_write == 0) return f_status_set_error(f_invalid_parameter); + if (buffer.used >= buffer.size) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ + + if (file.id <= 0) return f_status_set_error(f_file_not_open); + + if (buffer.used == 0) { + *written = 0; + return f_no_data; + } + + f_string_length write_max = file.size_write; + + if (write_max > buffer.used) { + write_max = buffer.used; + } + + f_status status = private_f_file_write_until(file, buffer.string, write_max, written); + if (f_status_is_error(status)) return f_status_set_error(status); + + if (status == f_none && *written == buffer.used) return f_none_on_eos; + + return status; + } +#endif // _di_f_file_write_block_ + +#ifndef _di_f_file_write_until_ + f_return_status f_file_write_until(const f_file file, const f_string_dynamic buffer, const f_string_length total, f_string_length *written) { + #ifndef _di_level_0_parameter_checking_ + if (file.size_write == 0) return f_status_set_error(f_invalid_parameter); + if (buffer.used >= buffer.size) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ + + if (file.id <= 0) return f_status_set_error(f_file_not_open); + + if (buffer.used == 0 || total == 0) { + *written = 0; + return f_no_data; + } + + f_string_length write_max = file.size_write; + + if (write_max > buffer.used) { + write_max = buffer.used; + } + + f_status status = private_f_file_write_until(file, buffer.string, write_max, written); + if (f_status_is_error(status)) return f_status_set_error(status); + + if (status == f_none && *written == buffer.used) return f_none_on_eos; + + return status; + } +#endif // _di_f_file_write_until_ + +#ifndef _di_f_file_write_range_ + f_return_status f_file_write_range(const f_file file, const f_string_dynamic buffer, const f_string_range range, f_string_length *written) { + #ifndef _di_level_0_parameter_checking_ + if (file.size_write == 0) return f_status_set_error(f_invalid_parameter); + if (buffer.used >= buffer.size) return f_status_set_error(f_invalid_parameter); + if (range.stop < range.start) return f_status_set_error(f_invalid_parameter); + if (range.start >= buffer.used) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ + + if (file.id <= 0) return f_status_set_error(f_file_not_open); + + if (buffer.used == 0) { + *written = 0; + return f_no_data; + } + + const f_string_length total = (range.stop - range.start) + 1; + f_string_length write_max = total; + + if (write_max > buffer.used) { + write_max = buffer.used; + } + + f_status status = private_f_file_write_until(file, buffer.string + range.start, write_max, written); + if (f_status_is_error(status)) return f_status_set_error(status); + + if (status == f_none) { + if (range.start + *written == total) return f_none_on_stop; + if (range.start + *written == buffer.used) return f_none_on_eos; + } + + return status; + } +#endif // _di_f_file_write_range_ + #ifdef __cplusplus } // extern "C" #endif diff --git a/level_0/f_file/c/file.h b/level_0/f_file/c/file.h index f5658ae..321a042 100644 --- a/level_0/f_file/c/file.h +++ b/level_0/f_file/c/file.h @@ -86,17 +86,16 @@ extern "C" { * Commonly used file related properties. * * id: File descriptor. - * size_chunk: Number of bytes to consider a character, a value of 1 means 1-byte (aka: uint8_t) (for normal string handling this should be sizeof(f_string)). - * size_block: The default number of chunks to read at a time (use (size_chunk * size_block) to determine total number of bytes). + * size_block: The default number of 1-byte characters to read at a time and is often used for the read/write buffer size. */ #ifndef _di_f_file_ typedef struct { int id; - size_t size_chunk; - size_t size_block; + size_t size_read; + size_t size_write; } f_file; - #define f_file_initialize { 0, 1, f_file_default_read_size } + #define f_file_initialize { 0, f_file_default_read_size, f_file_default_write_size } #endif // _di_f_file_ /** @@ -908,7 +907,7 @@ extern "C" { * @see read() */ #ifndef _di_f_file_read_ - extern f_return_status f_file_read(f_file *file, f_string_dynamic *buffer); + extern f_return_status f_file_read(const f_file file, f_string_dynamic *buffer); #endif // _di_f_file_read_ /** @@ -938,7 +937,7 @@ extern "C" { * @see read() */ #ifndef _di_f_file_read_ - extern f_return_status f_file_read_block(f_file *file, f_string_dynamic *buffer); + extern f_return_status f_file_read_block(const f_file file, f_string_dynamic *buffer); #endif // _di_f_file_read_ /** @@ -966,10 +965,10 @@ extern "C" { * f_file_not_open (with error bit) if file is not open. * f_file_is_type_directory (with error bit) if file descriptor represents a directory. * - * @see read + * @see read() */ #ifndef _di_f_file_read_until_ - extern f_return_status f_file_read_until(f_file *file, f_string_dynamic *buffer, const f_string_length total); + extern f_return_status f_file_read_until(const f_file file, f_string_dynamic *buffer, const f_string_length total); #endif // _di_f_file_read_until_ /** @@ -1225,6 +1224,130 @@ extern "C" { extern f_return_status f_file_stat_by_id(const int id, struct stat *file_stat); #endif // _di_f_file_stat_by_id_ +/** + * Write until entire buffer is written. + * + * @param file + * The file to write to. + * The file must already be open. + * @param buffer + * The buffer to write to the file. + * @param written + * The total bytes written. + * + * @return + * f_none on success. + * f_none_on_stop on success but no data was written (written == 0) (not an error and often happens if file type is not a regular file). + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_block (with error bit) if file descriptor is set to non-block and the write would result in a blocking operation. + * f_file_error_descriptor (with error bit) if the file descriptor is invalid. + * f_invalid_buffer (with error bit) if the buffer is invalid. + * f_interrupted (with error bit) if interrupt was received. + * f_error_input_output (with error bit) on I/O error. + * f_file_not_open (with error bit) if file is not open. + * f_file_is_type_directory (with error bit) if file descriptor represents a directory. + * + * @see write() + */ +#ifndef _di_f_file_write_ + extern f_return_status f_file_write(const f_file file, const f_string_dynamic buffer, f_string_length *written); +#endif // _di_f_file_write_ + +/** + * Write until a single block is filled or entire buffer is written. + * + * To check how much was write into the buffer, record buffer->used before execution and compare to buffer->used after execution. + * + * @param file + * The file to write to. + * The file must already be open. + * @param buffer + * The buffer to write to the file. + * @param written + * The total bytes written. + * + * @return + * f_none on success. + * f_none_on_stop on success but no data was written (written == 0) (not an error and often happens if file type is not a regular file). + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_block (with error bit) if file descriptor is set to non-block and the write would result in a blocking operation. + * f_file_error_descriptor (with error bit) if the file descriptor is invalid. + * f_invalid_buffer (with error bit) if the buffer is invalid. + * f_interrupted (with error bit) if interrupt was received. + * f_error_input_output (with error bit) on I/O error. + * f_file_not_open (with error bit) if file is not open. + * f_file_is_type_directory (with error bit) if file descriptor represents a directory. + * + * @see write() + */ +#ifndef _di_f_file_write_block_ + extern f_return_status f_file_write_block(const f_file file, const f_string_dynamic buffer, f_string_length *written); +#endif // _di_f_file_write_block_ + +/** + * Write until a given number or entire buffer is written. + * + * @param file + * The file to write to. + * The file must already be open. + * @param buffer + * The buffer to write to the file. + * @param total + * The total bytes to write, unless end of buffer is reached first. + * @param written + * The total bytes written. + * + * @return + * f_none on success. + * f_none_on_stop on success but no data was written (written == 0) (not an error and often happens if file type is not a regular file). + * f_none_on_eos on success but range.stop exceeded buffer.used (only wrote up to buffer.used). + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_block (with error bit) if file descriptor is set to non-block and the write would result in a blocking operation. + * f_file_error_descriptor (with error bit) if the file descriptor is invalid. + * f_invalid_buffer (with error bit) if the buffer is invalid. + * f_interrupted (with error bit) if interrupt was received. + * f_error_input_output (with error bit) on I/O error. + * f_file_not_open (with error bit) if file is not open. + * f_file_is_type_directory (with error bit) if file descriptor represents a directory. + * + * @see write() + */ +#ifndef _di_f_file_write_until_ + extern f_return_status f_file_write_until(const f_file file, const f_string_dynamic buffer, const f_string_length total, f_string_length *written); +#endif // _di_f_file_write_until_ + +/** + * Write a given range within the buffer. + * + * @param file + * The file to write to. + * The file must already be open. + * @param buffer + * The buffer to write to the file. + * @param range + * An inclusive start an stop range within the buffer to read. + * @param written + * The total bytes written. + * + * @return + * f_none on success. + * f_none_on_stop on success but no data was written (written == 0) (not an error and often happens if file type is not a regular file). + * f_none_on_eos on success but range.stop exceeded buffer.used (only wrote up to buffer.used). + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_block (with error bit) if file descriptor is set to non-block and the write would result in a blocking operation. + * f_file_error_descriptor (with error bit) if the file descriptor is invalid. + * f_invalid_buffer (with error bit) if the buffer is invalid. + * f_interrupted (with error bit) if interrupt was received. + * f_error_input_output (with error bit) on I/O error. + * f_file_not_open (with error bit) if file is not open. + * f_file_is_type_directory (with error bit) if file descriptor represents a directory. + * + * @see write() + */ +#ifndef _di_f_file_write_range_ + extern f_return_status f_file_write_range(const f_file file, const f_string_dynamic buffer, const f_string_range range, f_string_length *written); +#endif // _di_f_file_write_range_ + #ifdef __cplusplus } // extern "C" #endif diff --git a/level_0/f_file/c/private-file.c b/level_0/f_file/c/private-file.c index a7a5bac..5e1728b 100644 --- a/level_0/f_file/c/private-file.c +++ b/level_0/f_file/c/private-file.c @@ -408,6 +408,48 @@ extern "C" { } #endif // !defined(_di_f_file_stat_by_id_) || !defined(_di_f_file_size_by_id_) +#if !defined(f_file_write) || !defined(f_file_write_until) || !defined(f_file_write_range) + f_return_status private_f_file_write_until(const f_file file, const f_string string, const f_string_length total, f_string_length *written) { + *written = 0; + + f_status status = f_none; + f_string_length write_size = file.size_write; + f_string_length write_max = total; + + ssize_t size_write = 0; + + if (write_max < write_size) { + write_size = write_max; + } + + while (*written < write_max && (size_write = write(file.id, string + *written, write_size)) > 0) { + *written += size_write; + + if (*written + write_size > write_max) { + write_size = write_max - *written; + } + } // while + + if (size_write == 0) { + return f_none_on_stop; + } + + if (size_write < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) return f_status_set_error(f_block); + if (errno == EBADF) return f_status_set_error(f_file_error_descriptor); + if (errno == EFAULT) return f_status_set_error(f_invalid_buffer); + if (errno == EINTR) return f_status_set_error(f_interrupted); + if (errno == EINVAL) return f_status_set_error(f_invalid_parameter); + if (errno == EIO) return f_status_set_error(f_error_input_output); + if (errno == EISDIR) return f_status_set_error(f_file_is_type_directory); + + return f_status_set_error(f_failure); + } + + return f_none; + } +#endif // !defined(f_file_write) || !defined(f_file_write_until) || !defined(f_file_write_range) + #ifdef __cplusplus } // extern "C" #endif diff --git a/level_0/f_file/c/private-file.h b/level_0/f_file/c/private-file.h index 48f8301..b63c22e 100644 --- a/level_0/f_file/c/private-file.h +++ b/level_0/f_file/c/private-file.h @@ -540,6 +540,42 @@ extern "C" { extern f_return_status private_f_file_stat_by_id(const int id, struct stat *file_stat) f_gcc_attribute_visibility_internal; #endif // !defined(_di_f_file_stat_by_id_) || !defined(_di_f_file_size_by_id_) +/** + * Private implementation of private_f_file_write_until(). + * + * Intended to be shared to each of the different implementation variations. + * + * @param file + * The file to write to. + * The file must already be open. + * @param string + * The string to write to the file. + * @param total + * The total bytes to write. + * @param written + * The total bytes written. + * + * @return + * f_none on success. + * f_none_on_stop on success but no data was written (written == 0) (not an error and often happens if file type is not a regular file). + * f_none_on_eos on success but range.stop exceeded buffer.used (only wrote up to buffer.used). + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_block (with error bit) if file descriptor is set to non-block and the write would result in a blocking operation. + * f_file_error_descriptor (with error bit) if the file descriptor is invalid. + * f_invalid_buffer (with error bit) if the buffer is invalid. + * f_interrupted (with error bit) if interrupt was received. + * f_error_input_output (with error bit) on I/O error. + * f_file_not_open (with error bit) if file is not open. + * f_file_is_type_directory (with error bit) if file descriptor represents a directory. + * + * @see f_file_write() + * @see f_file_write_range() + * @see f_file_write_until() + */ +#if !defined(f_file_write) || !defined(f_file_write_until) || !defined(f_file_write_range) + extern f_return_status private_f_file_write_until(const f_file file, const f_string string, const f_string_length total, f_string_length *written) f_gcc_attribute_visibility_internal; +#endif // !defined(f_file_write) || !defined(f_file_write_until) || !defined(f_file_write_range) + #ifdef __cplusplus } // extern "C" #endif diff --git a/level_1/fl_directory/c/private-directory.c b/level_1/fl_directory/c/private-directory.c index f7074d2..ac93ccb 100644 --- a/level_1/fl_directory/c/private-directory.c +++ b/level_1/fl_directory/c/private-directory.c @@ -25,7 +25,7 @@ extern "C" { if (f_status_is_error(status)) return status; } else { - status = f_directory_create(destination, mode); + status = f_directory_create(destination, mode.directory); if (f_status_is_error(status)) return status; } @@ -168,7 +168,7 @@ extern "C" { memset(&file_stat, 0, sizeof(struct stat)); - status = f_file_stat_at(parent_fd, entity[i]->d_name, &file_stat, 0); + status = f_file_stat_at(parent_fd, entity[i]->d_name, 0, &file_stat); if (f_status_is_error(status)) break; mode = f_macro_file_type_get(file_stat.st_mode); @@ -216,7 +216,7 @@ extern "C" { if (total > names->array[names->used].size) { f_status status = f_none; - f_macro_string_dynamic_resize(status, (*destination), total); + f_macro_string_dynamics_resize(status, (*names), total); if (f_status_is_error(status)) break; } diff --git a/level_1/fl_directory/c/private-directory.h b/level_1/fl_directory/c/private-directory.h index bf0ce6f..c319940 100644 --- a/level_1/fl_directory/c/private-directory.h +++ b/level_1/fl_directory/c/private-directory.h @@ -7,8 +7,8 @@ * * Provides operations for directory handling. */ -#ifndef _PRIVATE_F_directory_h -#define _PRIVATE_F_directory_h +#ifndef _PRIVATE_FL_directory_h +#define _PRIVATE_FL_directory_h #ifdef __cplusplus extern "C" { @@ -76,4 +76,4 @@ extern "C" { } // extern "C" #endif -#endif // _PRIVATE_F_directory_h +#endif // _PRIVATE_FL_directory_h diff --git a/level_1/fl_fss/c/fss.c b/level_1/fl_fss/c/fss.c index c470630..13ed5b0 100644 --- a/level_1/fl_fss/c/fss.c +++ b/level_1/fl_fss/c/fss.c @@ -189,7 +189,7 @@ extern "C" { f_macro_string_dynamic_resize(status, buffer, f_fss_max_header_length + 1); if (f_status_is_error(status)) return status; - status = f_file_read_until(file, &buffer, f_fss_max_header_length + 1); + status = f_file_read_until(*file, &buffer, f_fss_max_header_length + 1); if (f_status_is_error(status)) return status; return fl_fss_identify(buffer, header); diff --git a/level_1/fl_utf_file/c/private-utf_file.c b/level_1/fl_utf_file/c/private-utf_file.c index 342afd4..a99e9ec 100644 --- a/level_1/fl_utf_file/c/private-utf_file.c +++ b/level_1/fl_utf_file/c/private-utf_file.c @@ -5,6 +5,119 @@ extern "C" { #endif +#if !defined(fl_utf_file_write) || !defined(fl_utf_file_write_until) || !defined(fl_utf_file_write_range) + f_return_status private_fl_utf_file_write_until(const f_file file, const f_utf_string string, const f_utf_string_length total, f_utf_string_length *written) { + *written = 0; + + f_status status = f_none; + f_utf_string_length write_size = file.size_write > 4 ? file.size_write : 4; + f_utf_string_length write_max = total; + f_utf_string_length i = 0; + + if (write_size > write_max) { + write_size = write_max; + } + + f_string_length last = 0; + f_string_length used = 0; + + ssize_t size_write = 0; + uint8_t buffer_write[write_size]; + uint8_t width = 0; + uint8_t width_written = 0; + + do { + memset(buffer_write, 0, write_size); + + for (i = 0, used = 0; used < write_size && *written + i < write_max; i++, used += width) { + if (width_written < width) { + if (width_written < 2) { + buffer_write[used] = f_macro_utf_character_to_char_2(string[*written + i]); + width_written++; + used++; + } + + if (width > 2 && width_written < 3) { + buffer_write[used + 1] = f_macro_utf_character_to_char_3(string[*written + i]); + width_written++; + used++; + } + + if (width == 4 && width_written < 4) { + buffer_write[used + 2] = f_macro_utf_character_to_char_4(string[*written + i]); + width_written++; + used++; + } + + width = 0; + } + else { + width = f_macro_utf_character_width(string[*written + i]); + width_written = width; + + buffer_write[used] = f_macro_utf_character_to_char_1(string[*written + i]); + + if (width > 1) { + if (used + 1 > write_size) { + width_written = 1; + used += 1; + break; + } + + buffer_write[used + 1] = f_macro_utf_character_to_char_2(string[*written + i]); + + if (width > 2) { + if (used + 2 > write_size) { + width_written = 2; + used += 2; + break; + } + + buffer_write[used + 2] = f_macro_utf_character_to_char_3(string[*written + i]); + + if (width == 4) { + if (used + 3 > write_size) { + width_written = 3; + used += 3; + break; + } + + buffer_write[used + 3] = f_macro_utf_character_to_char_4(string[*written + i]); + } + } + } + } + } // for + + size_write = write(file.id, buffer_write, used); + + if (size_write == 0) { + return f_none_on_stop; + } + + if (size_write < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) return f_status_set_error(f_block); + if (errno == EBADF) return f_status_set_error(f_file_error_descriptor); + if (errno == EFAULT) return f_status_set_error(f_invalid_buffer); + if (errno == EINTR) return f_status_set_error(f_interrupted); + if (errno == EINVAL) return f_status_set_error(f_invalid_parameter); + if (errno == EIO) return f_status_set_error(f_error_input_output); + if (errno == EISDIR) return f_status_set_error(f_file_is_type_directory); + + return f_status_set_error(f_failure); + } + + *written += i; + last += used; + + if (*written + write_size > write_max) { + write_size = write_max - *written; + } + } while (*written < write_max); + + return f_none; + } +#endif // !defined(fl_utf_file_write) || !defined(fl_utf_file_write_until) || !defined(fl_utf_file_write_range) #ifdef __cplusplus } // extern "C" diff --git a/level_1/fl_utf_file/c/private-utf_file.h b/level_1/fl_utf_file/c/private-utf_file.h index a5d487e..d9fe9f7 100644 --- a/level_1/fl_utf_file/c/private-utf_file.h +++ b/level_1/fl_utf_file/c/private-utf_file.h @@ -14,6 +14,42 @@ extern "C" { #endif +/** + * Private implementation of fl_utf_file_write_until(). + * + * Intended to be shared to each of the different implementation variations. + * + * @param file + * The file to write to. + * The file must already be open. + * The file.size_write is forced to be no less than 4 (if greater than 0). + * @param string + * The string to write to the file. + * @param total + * The total bytes to write. + * @param written + * The total bytes written. + * + * @return + * f_none on success. + * f_none_on_stop on success but no data was written (written == 0) (not an error and often happens if file type is not a regular file). + * f_none_on_eos on success but range.stop exceeded buffer.used (only wrote up to buffer.used). + * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_block (with error bit) if file descriptor is set to non-block and the write would result in a blocking operation. + * f_file_error_descriptor (with error bit) if the file descriptor is invalid. + * f_invalid_buffer (with error bit) if the buffer is invalid. + * f_interrupted (with error bit) if interrupt was received. + * f_error_input_output (with error bit) on I/O error. + * f_file_not_open (with error bit) if file is not open. + * f_file_is_type_directory (with error bit) if file descriptor represents a directory. + * + * @see fl_utf_file_write() + * @see fl_utf_file_write_range() + * @see fl_utf_file_write_until() + */ +#if !defined(fl_utf_file_write) || !defined(fl_utf_file_write_until) || !defined(fl_utf_file_write_range) + extern f_return_status private_fl_utf_file_write_until(const f_file file, const f_utf_string string, const f_utf_string_length total, f_utf_string_length *written) f_gcc_attribute_visibility_internal; +#endif // !defined(fl_utf_file_write) || !defined(fl_utf_file_write_until) || !defined(fl_utf_file_write_range) #ifdef __cplusplus } // extern "C" diff --git a/level_1/fl_utf_file/c/utf_file.c b/level_1/fl_utf_file/c/utf_file.c index 7800435..22297aa 100644 --- a/level_1/fl_utf_file/c/utf_file.c +++ b/level_1/fl_utf_file/c/utf_file.c @@ -5,27 +5,28 @@ extern "C" { #endif -#ifndef _di_f_file_read_ - f_return_status f_file_read(f_file *file, f_string_dynamic *buffer) { - #ifndef _di_level_0_parameter_checking_ - if (file == 0) return f_status_set_error(f_invalid_parameter); - if (file->size_chunk == 0) return f_status_set_error(f_invalid_parameter); - if (file->size_block == 0) return f_status_set_error(f_invalid_parameter); +#ifndef _di_fl_utf_file_read_ + f_return_status fl_utf_file_read(const f_file file, f_utf_string_dynamic *buffer) { + #ifndef _di_level_1_parameter_checking_ + if (file.size_read == 0) return f_status_set_error(f_invalid_parameter); if (buffer->used >= buffer->size) return f_status_set_error(f_invalid_parameter); - #endif // _di_level_0_parameter_checking_ + #endif // _di_level_1_parameter_checking_ - if (file->id <= 0) return f_status_set_error(f_file_not_open); + if (file.id <= 0) return f_status_set_error(f_file_not_open); f_status status = f_none; + ssize_t size_read = 0; + uint8_t width = 0; - // use a custom buffer so that memory is allocated post-read instead of pre-read. - const f_string_length buffer_size = file->size_chunk * file->size_block; - char buffer_read[buffer_size]; + f_utf_string_length i = 0; + f_utf_character character = 0; - memset(&buffer_read, 0, sizeof(buffer_size)); + char buffer_read[file.size_read]; - while ((size_read = read(file->id, buffer_read, buffer_size)) > 0) { + memset(&buffer_read, 0, sizeof(file.size_read)); + + while ((size_read = read(file.id, buffer_read, file.size_read)) > 0) { if (buffer->used + size_read > buffer->size) { if (buffer->size + size_read > f_string_length_size) { return f_status_set_error(f_string_too_large); @@ -35,8 +36,28 @@ extern "C" { if (f_status_is_error(status)) return status; } - memcpy(buffer->string + buffer->used, buffer_read, buffer_size); - buffer->used += size_read; + for (i = 0; i < size_read; i += width) { + width = f_macro_utf_byte_width(buffer_read[i]); + + // @fixme this needs to properly validate the UTF-8 width available and also carry ove the count across the outer loop. + + character = f_macro_utf_character_from_char_1(buffer_read[i]); + + if (width > 1 && i + 1 < size_read) { + character |= f_macro_utf_character_from_char_2(buffer_read[i]); + + if (width > 2 && i + 2 < size_read) { + character |= f_macro_utf_character_from_char_3(buffer_read[i]); + + if (width > 3 && i + 3 < size_read) { + character |= f_macro_utf_character_from_char_4(buffer_read[i]); + } + } + } + + buffer->string[i] = character; + buffer->used++; + } // for } // while if (size_read == 0) { @@ -57,28 +78,30 @@ extern "C" { return f_none; } -#endif // _di_f_file_read_ +#endif // _di_fl_utf_file_read_ -#ifndef _di_f_file_read_block_ - f_return_status f_file_read_block(f_file *file, f_string_dynamic *buffer) { - #ifndef _di_level_0_parameter_checking_ - if (file == 0) return f_status_set_error(f_invalid_parameter); - if (file->size_chunk == 0) return f_status_set_error(f_invalid_parameter); - if (file->size_block == 0) return f_status_set_error(f_invalid_parameter); +#ifndef _di_fl_utf_file_read_block_ + f_return_status fl_utf_file_read_block(const f_file file, f_utf_string_dynamic *buffer) { + #ifndef _di_level_1_parameter_checking_ + if (file.size_read == 0) return f_status_set_error(f_invalid_parameter); if (buffer->used >= buffer->size) return f_status_set_error(f_invalid_parameter); - #endif // _di_level_0_parameter_checking_ + #endif // _di_level_1_parameter_checking_ - if (file->id <= 0) return f_status_set_error(f_file_not_open); + if (file.id <= 0) return f_status_set_error(f_file_not_open); f_status status = f_none; + ssize_t size_read = 0; + uint8_t width = 0; - const f_string_length buffer_size = file->size_chunk * file->size_block; - char buffer_read[buffer_size]; + f_utf_string_length i = 0; + f_utf_character character = 0; - memset(&buffer_read, 0, sizeof(buffer_size)); + char buffer_read[file.size_read]; + + memset(&buffer_read, 0, sizeof(file.size_read)); - if ((size_read = read(file->id, buffer_read, buffer_size)) > 0) { + if ((size_read = read(file.id, buffer_read, file.size_read)) > 0) { if (buffer->used + size_read > buffer->size) { if (buffer->size + size_read > f_string_length_size) { return f_status_set_error(f_string_too_large); @@ -88,8 +111,28 @@ extern "C" { if (f_status_is_error(status)) return status; } - memcpy(buffer->string + buffer->used, buffer_read, buffer_size); - buffer->used += size_read; + for (i = 0; i < size_read; i += width) { + width = f_macro_utf_byte_width(buffer_read[i]); + + // @fixme this needs to properly validate the UTF-8 width available and also carry ove the count across the outer loop. + + character = f_macro_utf_character_from_char_1(buffer_read[i]); + + if (width > 1 && i + 1 < size_read) { + character |= f_macro_utf_character_from_char_2(buffer_read[i]); + + if (width > 2 && i + 2 < size_read) { + character |= f_macro_utf_character_from_char_3(buffer_read[i]); + + if (width > 3 && i + 3 < size_read) { + character |= f_macro_utf_character_from_char_4(buffer_read[i]); + } + } + } + + buffer->string[i] = character; + buffer->used++; + } // for } if (size_read == 0) { @@ -110,24 +153,27 @@ extern "C" { return f_none; } -#endif // _di_f_file_read_block_ +#endif // _di_fl_utf_file_read_block_ -#ifndef _di_f_file_read_until_ - f_return_status f_file_read_until(f_file *file, f_string_dynamic *buffer, const f_string_length total) { - #ifndef _di_level_0_parameter_checking_ - if (file == 0) return f_status_set_error(f_invalid_parameter); - if (file->size_chunk == 0) return f_status_set_error(f_invalid_parameter); - if (file->size_block == 0) return f_status_set_error(f_invalid_parameter); +#ifndef _di_fl_utf_file_read_until_ + f_return_status fl_utf_file_read_until(const f_file file, f_utf_string_dynamic *buffer, const f_utf_string_length total) { + #ifndef _di_level_1_parameter_checking_ + if (file.size_read == 0) return f_status_set_error(f_invalid_parameter); if (buffer->used >= buffer->size) return f_status_set_error(f_invalid_parameter); - #endif // _di_level_0_parameter_checking_ + #endif // _di_level_1_parameter_checking_ - if (file->id <= 0) return f_status_set_error(f_file_not_open); + if (file.id <= 0) return f_status_set_error(f_file_not_open); f_status status = f_none; + ssize_t size_read = 0; + uint8_t width = 0; - f_string_length buffer_size = file->size_chunk * file->size_block; - f_string_length buffer_count = 0; + f_utf_string_length i = 0; + f_utf_character character = 0; + + f_utf_string_length buffer_size = file.size_read; + f_utf_string_length buffer_count = 0; if (total < buffer_size) { buffer_size = total; @@ -137,7 +183,7 @@ extern "C" { memset(&buffer_read, 0, sizeof(buffer_size)); - if ((size_read = read(file->id, buffer_read, buffer_size)) > 0) { + while (buffer_count < total && (size_read = read(file.id, buffer_read, buffer_size)) > 0) { if (buffer->used + size_read > buffer->size) { if (buffer->size + size_read > f_string_length_size) { return f_status_set_error(f_string_too_large); @@ -147,92 +193,10 @@ extern "C" { if (f_status_is_error(status)) return status; } - memcpy(buffer->string + buffer->used, buffer_read, buffer_size); - buffer->used += size_read; - buffer_count += size_read; - } - - if (size_read == 0) { - return f_none_on_eof; - } - - if (size_read < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) return f_status_set_error(f_block); - if (errno == EBADF) return f_status_set_error(f_file_error_descriptor); - if (errno == EFAULT) return f_status_set_error(f_invalid_buffer); - if (errno == EINTR) return f_status_set_error(f_interrupted); - if (errno == EINVAL) return f_status_set_error(f_invalid_parameter); - if (errno == EIO) return f_status_set_error(f_error_input_output); - if (errno == EISDIR) return f_status_set_error(f_file_is_type_directory); - - return f_status_set_error(f_failure); - } - - return f_none; - } -#endif // _di_f_file_read_until_ - - - - - - - - - - - - - - - - - - -#ifndef _di_fl_utf_file_read_ - f_return_status fl_utf_file_read(f_file *file, f_utf_string_dynamic *buffer) { - #ifndef _di_level_1_parameter_checking_ - if (file == 0) return f_status_set_error(f_invalid_parameter); - if (buffer == 0) return f_status_set_error(f_invalid_parameter); - if (buffer->used >= buffer->size) return f_status_set_error(f_invalid_parameter); - #endif // _di_level_1_parameter_checking_ - - if (file->address == 0) return f_status_set_error(f_file_not_open); - - f_status status = f_none; - - f_string_length i = 0; - f_string_length total = 0; - - int result = 0; - const f_number_unsigned bytes_total = file->size_block * file->size_chunk; - - uint8_t width = 0; - uint8_t buffer_read[bytes_total]; - f_utf_character character = 0; - - for (;;) { - if (buffer->used + bytes_total >= buffer->used) { - if (buffer->used + bytes_total > f_utf_string_max_size) return f_status_set_error(f_string_too_large); - - f_macro_string_dynamic_resize(status, (*buffer), buffer->used + bytes_total); - - if (f_status_is_error(status)) return status; - } - - memset(&buffer_read, 0, bytes_total); - - result = fread(buffer->string + buffer->used, file->size_chunk, file->size_block, file->address); - - if (file->address == 0) return f_status_set_error(f_file_error_read); - if (ferror(file->address) != 0) return f_status_set_error(f_file_error_read); - - total = result * file->size_chunk; - - for (i = 0; i < total; i += width) { + for (i = 0; i < size_read; i += width) { width = f_macro_utf_byte_width(buffer_read[i]); - if (i + width > total) return f_status_set_error(f_incomplete_utf_on_eof); + // @fixme this needs to properly validate the UTF-8 width available and also carry ove the count across the outer loop. character = f_macro_utf_character_from_char_1(buffer_read[i]); @@ -250,216 +214,147 @@ extern "C" { buffer->string[i] = character; buffer->used++; + buffer_count++; } // for + } // while - if (feof(file->address)) return f_none_on_eof; - } // for - - return status; - } -#endif // _di_fl_utf_file_read_ - -#ifndef _di_fl_utf_file_read_position - f_return_status fl_utf_file_read_position(f_file *file, f_utf_string_dynamic *buffer, const f_string_quantity quantity) { - #ifndef _di_level_1_parameter_checking_ - if (file == 0) return f_status_set_error(f_invalid_parameter); - if (buffer == 0) return f_status_set_error(f_invalid_parameter); - #endif // _di_level_1_parameter_checking_ - - if (file->address == 0) return f_status_set_error(f_file_not_open); - - int result = 0; - - // first seek to 'where' we need to begin the read. - { - long current_file_quantity = ftell(file->address); - - if (current_file_quantity == -1) return f_status_set_error(f_file_error_seek); - - if (current_file_quantity > quantity.start) { - result = f_macro_file_seek_to(file->address, file->size_chunk * (0 - (current_file_quantity - quantity.start))); - } - else if (current_file_quantity < quantity.start) { - result = f_macro_file_seek_to(file->address, file->size_chunk * (quantity.start - current_file_quantity)); - } - - if (result != 0) return f_status_set_error(f_file_error_seek); + if (size_read == 0) { + return f_none_on_eof; } - f_status status = f_none; - bool infinite = f_false; - - f_string_length i = 0; - f_string_length total = 0; - - f_number_unsigned bytes_total; + if (size_read < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) return f_status_set_error(f_block); + if (errno == EBADF) return f_status_set_error(f_file_error_descriptor); + if (errno == EFAULT) return f_status_set_error(f_invalid_buffer); + if (errno == EINTR) return f_status_set_error(f_interrupted); + if (errno == EINVAL) return f_status_set_error(f_invalid_parameter); + if (errno == EIO) return f_status_set_error(f_error_input_output); + if (errno == EISDIR) return f_status_set_error(f_file_is_type_directory); - // when total is 0, this means the file read will until EOF is reached. - if (quantity.total == 0) { - infinite = f_true; - bytes_total = file->size_block * file->size_chunk; - } - else { - bytes_total = quantity.total * file->size_chunk; + return f_status_set_error(f_failure); } - uint8_t width = 0; - uint8_t buffer_read[bytes_total]; - f_utf_character character = 0; - - do { - if (buffer->used + bytes_total > buffer->size) { - if (buffer->used + bytes_total > f_string_length_size) return f_status_set_error(f_string_too_large); + return f_none; + } +#endif // _di_fl_utf_file_read_until_ - f_macro_string_dynamic_resize(status, (*buffer), buffer->used + bytes_total); +#ifndef _di_fl_utf_file_write_ + f_return_status fl_utf_file_write(const f_file file, const f_utf_string_dynamic buffer, f_utf_string_length *written) { + #ifndef _di_level_0_parameter_checking_ + if (file.size_write == 0) return f_status_set_error(f_invalid_parameter); + if (buffer.used >= buffer.size) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ - if (f_status_is_error(status)) return status; - } + if (file.id <= 0) return f_status_set_error(f_file_not_open); - if (quantity.total == 0) { - result = fread(buffer->string + buffer->used, file->size_chunk, file->size_block, file->address); - } - else { - result = fread(buffer->string + buffer->used, file->size_chunk, quantity.total, file->address); - } + if (buffer.used == 0) { + *written = 0; + return f_no_data; + } - if (file->address == 0) return f_status_set_error(f_file_error_read); - if (ferror(file->address) != 0) return f_status_set_error(f_file_error_read); + f_status status = private_fl_utf_file_write_until(file, buffer.string, buffer.used, written); + if (f_status_is_error(status)) return f_status_set_error(status); - total = result * file->size_chunk; + if (status == f_none && *written == buffer.used) return f_none_on_eos; - for (i = 0; i < total; i += width) { - width = f_macro_utf_byte_width(buffer_read[i]); + return status; + } +#endif // _di_fl_utf_file_write_ - if (i + width > total) return f_status_set_error(f_incomplete_utf_on_eof); +#ifndef _di_fl_utf_file_write_block_ + f_return_status fl_utf_file_write_block(const f_file file, const f_utf_string_dynamic buffer, f_utf_string_length *written) { + #ifndef _di_level_0_parameter_checking_ + if (file.size_write == 0) return f_status_set_error(f_invalid_parameter); + if (buffer.used >= buffer.size) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ - character = f_macro_utf_character_from_char_1(buffer_read[i]); + if (file.id <= 0) return f_status_set_error(f_file_not_open); - if (width > 1 && i + 1 < total) { - character |= f_macro_utf_character_from_char_2(buffer_read[i]); + if (buffer.used == 0) { + *written = 0; + return f_no_data; + } - if (width > 2 && i + 2 < total) { - character |= f_macro_utf_character_from_char_3(buffer_read[i]); + f_utf_string_length write_max = file.size_write; - if (width > 3 && i + 3 < total) { - character |= f_macro_utf_character_from_char_4(buffer_read[i]); - } - } - } + if (write_max > buffer.used) { + write_max = buffer.used; + } - buffer->string[i] = character; - buffer->used++; - } // for + f_status status = private_fl_utf_file_write_until(file, buffer.string, write_max, written); + if (f_status_is_error(status)) return f_status_set_error(status); - if (feof(file->address)) return f_none_on_eof; - } while (infinite); + if (status == f_none && *written == buffer.used) return f_none_on_eos; return status; } -#endif // _di_fl_utf_file_read_position - -#ifndef _di_fl_utf_file_write_ - f_return_status fl_utf_file_write(f_file *file, const f_utf_string_static buffer) { - #ifndef _di_level_1_parameter_checking_ - if (file == 0) return f_status_set_error(f_invalid_parameter); - #endif // _di_level_1_parameter_checking_ - - if (file->address == 0) return f_status_set_error(f_file_not_open); - - f_string_length total = 0; - f_string_length last = 0; - f_string_length used = 0; - f_string_length i = 0; +#endif // _di_fl_utf_file_write_block_ - size_t written = 0; - uint8_t buffer_write[f_file_default_write_size]; - uint8_t width = 0; - - do { - memset(&buffer_write[f_file_default_write_size], 0, f_file_default_write_size); - - for (i = 0; used < f_file_default_write_size && total + i < buffer.used; i++, used += width) { - width = f_macro_utf_character_width(buffer.string[total + i]); +#ifndef _di_fl_utf_file_write_until_ + f_return_status fl_utf_file_write_until(const f_file file, const f_utf_string_dynamic buffer, const f_utf_string_length total, f_utf_string_length *written) { + #ifndef _di_level_0_parameter_checking_ + if (file.size_write == 0) return f_status_set_error(f_invalid_parameter); + if (buffer.used >= buffer.size) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ - buffer_write[used] = f_macro_utf_character_to_char_1(buffer.string[total + i]); + if (file.id <= 0) return f_status_set_error(f_file_not_open); - if (width > 1) { - buffer_write[used + 1] = f_macro_utf_character_to_char_2(buffer.string[total + i]); + if (buffer.used == 0 || total == 0) { + *written = 0; + return f_no_data; + } - if (width > 2) { - buffer_write[used + 2] = f_macro_utf_character_to_char_3(buffer.string[total + i]); + f_utf_string_length write_max = file.size_write; - if (width > 3) { - buffer_write[used + 3] = f_macro_utf_character_to_char_4(buffer.string[total + i]); - } - } - } - } // for - - written = fwrite(buffer.string + last, file->size_chunk, used, file->address); + if (write_max > buffer.used) { + write_max = buffer.used; + } - if (written < i * file->size_chunk) return f_status_set_error(f_file_error_write); + f_status status = private_fl_utf_file_write_until(file, buffer.string, write_max, written); + if (f_status_is_error(status)) return f_status_set_error(status); - total += i; - last += used; - } while (total < buffer.used); + if (status == f_none && *written == buffer.used) return f_none_on_eos; - return f_none; + return status; } -#endif // _di_fl_utf_file_write_ - -#ifndef _di_fl_utf_file_write_position_ - f_return_status fl_utf_file_write_position(f_file *file, const f_utf_string_static buffer, const f_utf_string_range position) { - #ifndef _di_level_1_parameter_checking_ - if (file == 0) return f_status_set_error(f_invalid_parameter); - if (position.start < position.stop) return f_status_set_error(f_invalid_parameter); - #endif // _di_level_1_parameter_checking_ - - if (file->address == 0) return f_file_not_open; - - f_string_length total = 0; - f_string_length last = position.start; - f_string_length used = 0; - f_string_length i = 0; - - size_t written = 0; - uint8_t buffer_write[f_file_default_write_size]; - uint8_t width = 0; - - const f_string_length max = buffer.used - (position.stop - position.start + 1); +#endif // _di_fl_utf_file_write_until_ - do { - memset(&buffer_write[f_file_default_write_size], 0, f_file_default_write_size); - - for (i = 0; used < f_file_default_write_size && total + i < max; i++, used += width) { - width = f_macro_utf_character_width(buffer.string[total + i]); +#ifndef _di_fl_utf_file_write_range_ + f_return_status fl_utf_file_write_range(const f_file file, const f_utf_string_dynamic buffer, const f_utf_string_range range, f_utf_string_length *written) { + #ifndef _di_level_0_parameter_checking_ + if (file.size_write == 0) return f_status_set_error(f_invalid_parameter); + if (buffer.used >= buffer.size) return f_status_set_error(f_invalid_parameter); + if (range.stop < range.start) return f_status_set_error(f_invalid_parameter); + if (range.start >= buffer.used) return f_status_set_error(f_invalid_parameter); + #endif // _di_level_0_parameter_checking_ - buffer_write[used] = f_macro_utf_character_to_char_1(buffer.string[total + i]); + if (file.id <= 0) return f_status_set_error(f_file_not_open); - if (width > 1) { - buffer_write[used + 1] = f_macro_utf_character_to_char_2(buffer.string[total + i]); + if (buffer.used == 0) { + *written = 0; + return f_no_data; + } - if (width > 2) { - buffer_write[used + 2] = f_macro_utf_character_to_char_3(buffer.string[total + i]); + // @todo consider adding a custom status return for when an invalid UTF-8 is written due to range limitations. - if (width > 3) { - buffer_write[used + 3] = f_macro_utf_character_to_char_4(buffer.string[total + i]); - } - } - } - } // for + const f_utf_string_length total = (range.stop - range.start) + 1; + f_utf_string_length write_max = total; - written = fwrite(buffer.string + last, file->size_chunk, used, file->address); + if (write_max > buffer.used) { + write_max = buffer.used; + } - if (written < i * file->size_chunk) return f_status_set_error(f_file_error_write); + f_status status = private_fl_utf_file_write_until(file, buffer.string + range.start, write_max, written); + if (f_status_is_error(status)) return f_status_set_error(status); - total += i; - last += used; - } while (total < max); + if (status == f_none) { + if (range.start + *written == total) return f_none_on_stop; + if (range.start + *written == buffer.used) return f_none_on_eos; + } - return f_none; + return status; } -#endif // _di_fl_utf_file_write_position_ +#endif // _di_fl_utf_file_write_range_ #ifdef __cplusplus } // extern "C" diff --git a/level_1/fl_utf_file/c/utf_file.h b/level_1/fl_utf_file/c/utf_file.h index 171e7c6..9472995 100644 --- a/level_1/fl_utf_file/c/utf_file.h +++ b/level_1/fl_utf_file/c/utf_file.h @@ -52,7 +52,7 @@ extern "C" { * @see read() */ #ifndef _di_fl_utf_file_read_ - extern f_return_status fl_utf_file_read(f_file *file, f_utf_string_dynamic *buffer); + extern f_return_status fl_utf_file_read(const f_file file, f_utf_string_dynamic *buffer); #endif // _di_fl_utf_file_read_ /** @@ -82,9 +82,9 @@ extern "C" { * * @see read() */ -#ifndef _di_f_utf_file_read_ - extern f_return_status f_utf_file_read_block(f_file *file, f_utf_string_dynamic *buffer); -#endif // _di_f_utf_file_read_ +#ifndef _di_fl_utf_file_read_ + extern f_return_status fl_utf_file_read_block(const f_file file, f_utf_string_dynamic *buffer); +#endif // _di_fl_utf_file_read_ /** * Read until a given number or EOF is reached, storing it in the buffer. @@ -114,141 +114,141 @@ extern "C" { * * @see read */ -#ifndef _di_f_utf_file_read_until_ - extern f_return_status f_utf_file_read_until(f_file *file, f_utf_string_dynamic *buffer, const f_utf_string_length total); -#endif // _di_f_utf_file_read_until_ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +#ifndef _di_fl_utf_file_read_until_ + extern f_return_status fl_utf_file_read_until(const f_file file, f_utf_string_dynamic *buffer, const f_utf_string_length total); +#endif // _di_fl_utf_file_read_until_ /** - * Load entire file into the UTF-8 buffer. - * - * This does not validate the UTF-8 codes. + * Write until entire buffer is written. * * @param file - * The file to read from. + * The file to write to. + * The file must already be open. * @param buffer - * The buffer to load the file into. + * The buffer to write to the file. + * @param written + * The total bytes written. * * @return * f_none on success. - * f_none_on_eof on success and EOF was reached. - * f_file_not_open (with error bit) if file is not open. - * f_file_error_seek (with error bit) if file seek failed. - * f_file_error_read (with error bit) if file read failed. + * f_none_on_stop on success but no data was written (written == 0) (not an error and often happens if file type is not a regular file). * f_invalid_parameter (with error bit) if a parameter is invalid. - * f_error_reallocation (with error bit) on memory reallocation error. - * f_string_too_large (with error bit) if string is too large to fit into the buffer. - * f_incomplete_utf_on_eof (with error bit) if UTF-8 character was incomplete at the end of the file. + * f_block (with error bit) if file descriptor is set to non-block and the write would result in a blocking operation. + * f_file_error_descriptor (with error bit) if the file descriptor is invalid. + * f_invalid_buffer (with error bit) if the buffer is invalid. + * f_interrupted (with error bit) if interrupt was received. + * f_error_input_output (with error bit) on I/O error. + * f_file_not_open (with error bit) if file is not open. + * f_file_is_type_directory (with error bit) if file descriptor represents a directory. + * f_incomplete_utf_on_stop (with error bit) if UTF-8 character was incomplete at the stop location. + * f_incomplete_utf_on_eos (with error bit) if UTF-8 character was incomplete at the end of the string. + * + * @see write() */ -#ifndef _di_fl_utf_file_read_ - extern f_return_status fl_utf_file_read(f_file *file, f_utf_string_dynamic *buffer); -#endif // _di_fl_utf_file_read_ +#ifndef _di_fl_utf_file_write_ + extern f_return_status fl_utf_file_write(const f_file file, const f_utf_string_dynamic buffer, f_utf_string_length *written); +#endif // _di_fl_utf_file_write_ /** - * Load file into the UTF-8 buffer, based on specified positions. + * Write until a single block is filled or entire buffer is written. * - * This does not validate the UTF-8 codes. + * To check how much was write into the buffer, record buffer->used before execution and compare to buffer->used after execution. * * @param file - * The file to read from. + * The file to write to. + * The file must already be open. * @param buffer - * The buffer to save the file. - * @param quantity - * The file position to base reading off of. + * The buffer to write to the file. + * @param written + * The total bytes written. * * @return * f_none on success. - * f_none_on_eof on success and EOF was reached. - * f_file_not_open (with error bit) if file is not open. - * f_file_error_seek (with error bit) if file seek failed. - * f_file_error_read (with error bit) if file read failed. + * f_none_on_stop on success but no data was written (written == 0) (not an error and often happens if file type is not a regular file). * f_invalid_parameter (with error bit) if a parameter is invalid. - * f_error_reallocation (with error bit) on memory reallocation error. - * f_string_too_large (with error bit) if string is too large to fit into the buffer. - * f_incomplete_utf_on_eof (with error bit) if UTF-8 character was incomplete at the end of the file. + * f_block (with error bit) if file descriptor is set to non-block and the write would result in a blocking operation. + * f_file_error_descriptor (with error bit) if the file descriptor is invalid. + * f_invalid_buffer (with error bit) if the buffer is invalid. + * f_interrupted (with error bit) if interrupt was received. + * f_error_input_output (with error bit) on I/O error. + * f_file_not_open (with error bit) if file is not open. + * f_file_is_type_directory (with error bit) if file descriptor represents a directory. + * f_incomplete_utf_on_stop (with error bit) if UTF-8 character was incomplete at the stop location. + * f_incomplete_utf_on_eos (with error bit) if UTF-8 character was incomplete at the end of the string. + * + * @see write() */ -#ifndef _di_fl_utf_file_read_position_ - extern f_return_status fl_utf_file_read_position(f_file *file, f_utf_string_dynamic *buffer, const f_string_quantity quantity); -#endif // _di_fl_utf_file_read_position +#ifndef _di_fl_utf_file_write_block_ + extern f_return_status fl_utf_file_write_block(const f_file file, const f_utf_string_dynamic buffer, f_utf_string_length *written); +#endif // _di_fl_utf_file_write_block_ /** - * Save entire UTF-8 buffer into file. - * - * This does not validate the UTF-8 codes. + * Write until a given number or entire buffer is written. * * @param file - * The file to save to. + * The file to write to. + * The file must already be open. * @param buffer - * The buffer to save to the file. + * The buffer to write to the file. + * @param total + * The total bytes to write, unless end of buffer is reached first. + * @param written + * The total bytes written. * * @return * f_none on success. - * f_file_not_open (with error bit) if file is not open. - * f_file_error_write (with error bit) if write failed. + * f_none_on_stop on success but no data was written (written == 0) (not an error and often happens if file type is not a regular file). + * f_none_on_eos on success but range.stop exceeded buffer.used (only wrote up to buffer.used). * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_block (with error bit) if file descriptor is set to non-block and the write would result in a blocking operation. + * f_file_error_descriptor (with error bit) if the file descriptor is invalid. + * f_invalid_buffer (with error bit) if the buffer is invalid. + * f_interrupted (with error bit) if interrupt was received. + * f_error_input_output (with error bit) on I/O error. + * f_file_not_open (with error bit) if file is not open. + * f_file_is_type_directory (with error bit) if file descriptor represents a directory. + * f_incomplete_utf_on_stop (with error bit) if UTF-8 character was incomplete at the stop location. + * f_incomplete_utf_on_eos (with error bit) if UTF-8 character was incomplete at the end of the string. + * + * @see write() */ -#ifndef _di_fl_utf_file_write_ - extern f_return_status fl_utf_file_write(f_file *file, const f_utf_string_static buffer); -#endif // _di_fl_utf_file_write_ +#ifndef _di_fl_utf_file_write_until_ + extern f_return_status fl_utf_file_write_until(const f_file file, const f_utf_string_dynamic buffer, const f_utf_string_length total, f_utf_string_length *written); +#endif // _di_fl_utf_file_write_until_ /** - * Save entire UTF-8 buffer into file, based on specified positions. - * - * This does not validate the UTF-8 codes. + * Write a given range within the buffer. * * @param file - * The file to save to. + * The file to write to. + * The file must already be open. * @param buffer - * The buffer to save to the file. - * @param position - * The file position to base writing off of. + * The buffer to write to the file. + * @param range + * An inclusive start an stop range within the buffer to read. + * @param written + * The total bytes written. * * @return * f_none on success. - * f_file_not_open (with error bit) if file is not open. - * f_file_error_write (with error bit) if write failed. + * f_none_on_stop on success but no data was written (written == 0) (not an error and often happens if file type is not a regular file). + * f_none_on_eos on success but range.stop exceeded buffer.used (only wrote up to buffer.used). * f_invalid_parameter (with error bit) if a parameter is invalid. + * f_block (with error bit) if file descriptor is set to non-block and the write would result in a blocking operation. + * f_file_error_descriptor (with error bit) if the file descriptor is invalid. + * f_invalid_buffer (with error bit) if the buffer is invalid. + * f_interrupted (with error bit) if interrupt was received. + * f_error_input_output (with error bit) on I/O error. + * f_file_not_open (with error bit) if file is not open. + * f_file_is_type_directory (with error bit) if file descriptor represents a directory. + * f_incomplete_utf_on_stop (with error bit) if UTF-8 character was incomplete at the stop location. + * f_incomplete_utf_on_eos (with error bit) if UTF-8 character was incomplete at the end of the string. + * + * @see write() */ -#ifndef _di_fl_utf_file_write_position_ - extern f_return_status fl_utf_file_write_position(f_file *file, const f_utf_string_static buffer, const f_utf_string_range position); -#endif // _di_fl_utf_file_write_position_ +#ifndef _di_fl_utf_file_write_range_ + extern f_return_status fl_utf_file_write_range(const f_file file, const f_utf_string_dynamic buffer, const f_utf_string_range range, f_utf_string_length *written); +#endif // _di_fl_utf_file_write_range_ #ifdef __cplusplus } // extern "C" -- 1.8.3.1