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_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
*/
#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 { \
#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);
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
#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);
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;
}
#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) {
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);
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;
}
#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
* 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_
/**
* @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_
/**
* @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_
/**
* 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_
/**
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
}
#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
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
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;
}
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);
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;
}
*
* 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" {
} // extern "C"
#endif
-#endif // _PRIVATE_F_directory_h
+#endif // _PRIVATE_FL_directory_h
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);
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"
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"
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);
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) {
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);
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) {
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;
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);
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]);
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"
* @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_
/**
*
* @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.
*
* @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"