]> Kevux Git Server - fll/commitdiff
Progress: continue redesign of file and directoy structure
authorKevin Day <thekevinday@gmail.com>
Fri, 22 May 2020 02:42:18 +0000 (21:42 -0500)
committerKevin Day <thekevinday@gmail.com>
Fri, 22 May 2020 02:42:18 +0000 (21:42 -0500)
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.

13 files changed:
build/monolithic/settings
level_0/f_directory/c/directory.h
level_0/f_file/c/file.c
level_0/f_file/c/file.h
level_0/f_file/c/private-file.c
level_0/f_file/c/private-file.h
level_1/fl_directory/c/private-directory.c
level_1/fl_directory/c/private-directory.h
level_1/fl_fss/c/fss.c
level_1/fl_utf_file/c/private-utf_file.c
level_1/fl_utf_file/c/private-utf_file.h
level_1/fl_utf_file/c/utf_file.c
level_1/fl_utf_file/c/utf_file.h

index a07ef81f13d3825d2b3338851eaa624fedb2bb79..83bc815135a6ef7eb85b7b8430b8fb2f1c660d3d 100644 (file)
@@ -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
index 2432bd349d8467d95cc110c6c2d83779241e8b3f..6c1e537acfd3cf20c6e8b96cddb0642872834733 100644 (file)
@@ -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 { \
index f47c90bea2d3ec04720a113c218b7da3b4f7e1d3..0ef6071f623e986c0ea8a02c54981fe7b073c95f 100644 (file)
@@ -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
index f5658aefbb2ecfc70eb16e11153207c27b8923cc..321a042cba6c12b7a77baa181a4680eb84684893 100644 (file)
@@ -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
index a7a5bacb7bf0036b07e0fffeabf6a08ea49e1a92..5e1728be5bd46dfdf6ad258906e2143c683814e4 100644 (file)
@@ -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
index 48f830182538a0465b9ae20d121c4f7e4f0423cf..b63c22effe40172fd88d6ebfec93a9b933616d8b 100644 (file)
@@ -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
index f7074d2a19c326a673dbcd6a7af8ef0e9d2825cc..ac93ccb65bedc9c1105b4ab90760eade1d28858d 100644 (file)
@@ -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;
         }
 
index bf0ce6f3bd8d803c2d910cecd7c823dd67613e1b..c319940c235a92753247f278000508db28521e10 100644 (file)
@@ -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
index c4706306656b414512957360c29163033f569ef6..13ed5b0ba6f2b4e738d312a3c80623157382752b 100644 (file)
@@ -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);
index 342afd4a543d0fa99e8b2d9d613fd4511cf7af6e..a99e9ecbf61318076eeda9287a8fd1c5b99f7169 100644 (file)
@@ -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"
index a5d487ebfd223181b33494fb41970835a3109fd4..d9fe9f71a6b7dca679f194c1d90378b5b177a239 100644 (file)
 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"
index 7800435444f4b32d6086e0b68230ee650da1a0d1..22297aa06bfa6856314806182454d5f373b61bb2 100644 (file)
@@ -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"
index 171e7c66caceb48c72d82acf109ec7b40dc739b9..9472995596d7797f54821081a8f08503e2747aae 100644 (file)
@@ -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"