]> Kevux Git Server - fll/commitdiff
Feature: expand string functions and utf-8 string functions
authorKevin Day <thekevinday@gmail.com>
Wed, 22 Apr 2020 02:00:18 +0000 (21:00 -0500)
committerKevin Day <thekevinday@gmail.com>
Wed, 22 Apr 2020 02:01:31 +0000 (21:01 -0500)
The need for this was realized while developing this trim parameter feature.
This has been added as is for commit isolation and may be incomplete.

This utilizes private functions to reduce duplicate code.
While the use of private functions is generally unwanted in this project, this specific case seems to be an exception.

Add rip functions.
Add non-dynamic equivalent of some string functions.
Add trim functions.

12 files changed:
build/level_1/settings
build/monolithic/settings
level_1/fl_string/c/private-string.c [new file with mode: 0644]
level_1/fl_string/c/private-string.h [new file with mode: 0644]
level_1/fl_string/c/string.c
level_1/fl_string/c/string.h
level_1/fl_string/data/build/settings
level_1/fl_utf/c/private-utf.c [new file with mode: 0644]
level_1/fl_utf/c/private-utf.h [new file with mode: 0644]
level_1/fl_utf/c/utf.c
level_1/fl_utf/c/utf.h
level_1/fl_utf/data/build/settings

index 63263ce13804bc2219c961fdf40036fa068b8268..9b7c26da5e367cb14335257bb7eae893ae3a8475 100644 (file)
@@ -12,7 +12,7 @@ build_linker ar
 build_libraries -lc
 build_libraries_fll -lfll_0
 build_libraries_fll-level -lfll_0
-build_sources_library color.c console.c directory.c file.c fss.c fss_basic.c fss_basic_list.c fss_extended.c fss_extended_list.c print.c serialized.c socket.c status.c string.c utf.c
+build_sources_library color.c console.c directory.c file.c fss.c fss_basic.c fss_basic_list.c fss_extended.c fss_extended_list.c print.c serialized.c socket.c status.c string.c utf.c private-string.c private-utf.c
 build_sources_program 
 build_sources_headers color.h console.h directory.h file.h fss.h fss_basic.h fss_basic_list.h fss_status.h fss_extended.h fss_extended_list.h fss_macro.h print.h serialized.h socket.h status.h string.h utf.h
 build_shared yes
index f3d5fee25640305ef6725b06fc06eb862c966fb4..db4f0eeecbb82cbb3c25a298a5200e9f41002dde 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/file.c level_0/memory.c level_0/pipe.c level_0/print.c level_0/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/serialized.c level_1/socket.c level_1/status.c level_1/string.c level_1/utf.c level_1/print.c level_2/execute.c level_2/file.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/file.c level_0/memory.c level_0/pipe.c level_0/print.c level_0/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/serialized.c level_1/socket.c level_1/status.c level_1/string.c level_1/utf.c level_1/print.c level_1/private-string.c level_1/private-utf.c level_2/execute.c level_2/file.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/file.h level_0/fss.h level_0/memory.h level_0/path_fll.h level_0/path_filesystem.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/serialized.h level_1/socket.h level_1/status.h level_1/string.h level_1/utf.h level_1/print.h level_2/execute.h level_2/file.h level_2/fss_basic.h level_2/fss_basic_list.h level_2/fss_extended.h level_2/fss_extended_list.h level_2/fss_status.h level_2/program.h level_2/status.h
 build_sources_bash
diff --git a/level_1/fl_string/c/private-string.c b/level_1/fl_string/c/private-string.c
new file mode 100644 (file)
index 0000000..c151feb
--- /dev/null
@@ -0,0 +1,283 @@
+#include <level_1/string.h>
+#include "private-string.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined(_di_fl_string_compare_) || !defined(_di_fl_string_dynamic_compare_) || !defined(_di_fl_string_dynamic_partial_compare_)
+  f_return_status private_fl_string_compare(const f_string string1, const f_string string2, const f_string_length offset1, const f_string_length offset2, const f_string_length stop1, const f_string_length stop2) {
+    f_string_length i1 = offset1;
+    f_string_length i2 = offset2;
+
+    for (; i1 < stop1 && i2 < stop2; i1++, i2++) {
+      // skip past NULL in string1.
+      while (i1 < stop1 && string1[i1] == f_string_eos) i1++;
+      if (i1 == stop1) break;
+
+      // skip past NULL in string2.
+      while (i2 < stop2 && string2[i2] == f_string_eos) i2++;
+      if (i2 == stop2) break;
+
+      if (string1[i1] != string2[i2]) return f_not_equal_to;
+    } // for
+
+    // only return f_equal_to if all remaining characters are NULL.
+    for (; i1 < stop1; i1++) {
+      if (string1[i1] != f_string_eos) return f_not_equal_to;
+    } // for
+
+    for (; i2 < stop2; i2++) {
+      if (string2[i2] != f_string_eos) return f_not_equal_to;
+    } // for
+  }
+#endif // !defined(_di_fl_string_compare_) || !defined(_di_fl_string_dynamic_compare_) || !defined(_di_fl_string_dynamic_partial_compare_)
+
+#if !defined(_di_fl_string_compare_trim_) || !defined(_di_fl_string_dynamic_compare_trim_) || !defined(_di_fl_string_dynamic_partial_compare_trim_)
+  f_return_status private_fl_string_compare_trim(const f_string string1, const f_string string2, const f_string_length offset1, const f_string_length offset2, const f_string_length stop1, const f_string_length stop2) {
+    f_string_length i1 = offset1;
+    f_string_length i2 = offset2;
+
+    uint8_t width = 0;
+    uint8_t width_max = 0;
+    f_status status = f_none;
+
+    // skip past leading whitespace in string1.
+    for (; i1 < stop1; i1 += width) {
+      // skip past NULL in string1.
+      while (i1 < stop1 && string1[i1] == f_string_eos) i1++;
+      if (i1 == stop1) break;
+
+      width_max = (stop1 - i1) + 1;
+      status = f_utf_is_whitespace(string1 + i1, width_max);
+      if (f_status_is_error(status)) {
+        if (f_status_set_fine(status) == f_maybe) {
+          return f_status_set_error(f_invalid_utf);
+        }
+
+        return status;
+      }
+
+      if (status == f_false) break;
+
+      width = f_macro_utf_byte_width(string1[i1]);
+    } // for
+
+    // skip past leading whitespace in string2.
+    for (; i2 < stop2; i2 += width) {
+      // skip past NULL in string2.
+      while (i2 < stop2 && string2[i2] == f_string_eos) i2++;
+      if (i2 == stop2) break;
+
+      width_max = (stop2 - i2) + 1;
+      status = f_utf_is_whitespace(string2 + i2, width_max);
+      if (f_status_is_error(status)) {
+        if (f_status_set_fine(status) == f_maybe) {
+          return f_status_set_error(f_invalid_utf);
+        }
+
+        return status;
+      }
+
+      if (status == f_false) break;
+
+      width = f_macro_utf_byte_width(string2[i2]);
+    } // for
+
+    f_string_length last1 = i1;
+    f_string_length last2 = i2;
+
+    {
+      // size1 and size2 are to represent to total number of characters after trim.
+      f_string_length size1 = 0;
+      f_string_length size2 = 0;
+
+      // determine where the last non-whitespace is in string1.
+      for (f_string_length j = i1; j < stop1; j += width) {
+        // skip past NULL in string1.
+        while (j < stop1 && string1[j] == f_string_eos) j++;
+        if (j == stop1) break;
+
+        width_max = (stop1 - j) + 1;
+        status = f_utf_is_whitespace(string1 + j, width_max);
+        if (f_status_is_error(status)) {
+          if (f_status_set_fine(status) == f_maybe) {
+            return f_status_set_error(f_invalid_utf);
+          }
+
+          return status;
+        }
+
+        width = f_macro_utf_byte_width(string1[j]);
+
+        if (status == f_false) {
+          last1 = j;
+          size1++;
+        }
+      } // for
+
+      // determine where the last non-whitespace is in string2.
+      for (f_string_length j = i2; j < stop2; j += width) {
+        // skip past NULL in string2.
+        while (j < stop2 && string2[j] == f_string_eos) j++;
+        if (j == stop2) break;
+
+        width_max = (stop2 - j) + 1;
+        status = f_utf_is_whitespace(string2 + j, width_max);
+        if (f_status_is_error(status)) {
+          if (f_status_set_fine(status) == f_maybe) {
+            return f_status_set_error(f_invalid_utf);
+          }
+
+          return status;
+        }
+
+        width = f_macro_utf_byte_width(string2[j]);
+
+        if (status == f_false) {
+          last2 = j;
+          size2++;
+        }
+      } // for
+
+      if (size1 != size2) return f_not_equal_to;
+    }
+
+    for (; i1 < last1 && i2 < last2; i1++, i2++) {
+      // skip past NULL in string1.
+      while (i1 < last1 && string1[i1] == f_string_eos) i1++;
+      if (i1 == last1) break;
+
+      // skip past NULL in string2.
+      while (i2 < last2 && string2[i2] == f_string_eos) i2++;
+      if (i2 == last2) break;
+
+      if (string1[i1] != string2[i2]) return f_not_equal_to;
+    } // for
+
+    // only return f_equal_to if all remaining characters are NULL.
+    while (i1 < last1) {
+      if (string1[i1] != f_string_eos) return f_not_equal_to;
+      i1++;
+    } // while
+
+    while (i2 < last2) {
+      if (string2[i2] != f_string_eos) return f_not_equal_to;
+      i2++;
+    } // while
+
+    return f_equal_to;
+  }
+#endif // !defined(_di_fl_string_compare_trim_) || !defined(_di_fl_string_dynamic_compare_trim_) || !defined(_di_fl_string_dynamic_partial_compare_trim_)
+
+#if !defined(_di_fl_string_rip_) || !defined(_di_fl_string_dynamic_rip_)
+  f_return_status private_fl_string_rip(const f_string string, const f_string_length start, const f_string_length stop, f_string_dynamic *result) {
+    // The start and stop point are inclusive locations, and therefore start - stop is actually 1 too few locations.
+    f_string_length size = (stop - start) + 1;
+
+    if (size == 0) return f_no_data;
+
+    f_status status = f_none;
+
+    if (result == 0) {
+      f_macro_string_dynamic_new(status, (*result), size);
+    }
+    else {
+      f_macro_string_dynamic_resize(status, (*result), size);
+    }
+
+    if (f_status_is_error(status)) {
+      return status;
+    }
+
+    memcpy(result->string, string + start, size);
+    result->used = size;
+
+    return f_none;
+  }
+#endif // !defined(_di_fl_string_rip_) || !defined(_di_fl_string_dynamic_rip_)
+
+#if !defined(_di_fl_string_rip_trim_) || !defined(_di_fl_string_dynamic_rip_trim_)
+  f_return_status private_fl_string_rip_trim(const f_string string, const f_string_length start, const f_string_length stop, f_string_dynamic *result) {
+    // The start and stop point are inclusive locations, and therefore start - stop is actually 1 too few locations.
+    f_string_length size = (stop - start) + 1;
+
+    if (size == 0) return f_no_data;
+
+    f_status status = f_none;
+
+    if (result == 0) {
+      f_macro_string_dynamic_new(status, (*result), size);
+    }
+    else {
+      f_macro_string_dynamic_resize(status, (*result), size);
+    }
+
+    if (f_status_is_error(status)) {
+      return status;
+    }
+
+    f_string_length begin = start;
+    f_string_length end = stop;
+
+    uint8_t width = 0;
+
+    // skip past leading whitespace.
+    for (; begin <= end; begin += width) {
+      // skip past NULL.
+      while (begin < size && string[begin] == f_string_eos) begin++;
+      if (begin > end) break;
+
+      status = f_utf_is_whitespace(string + begin, (end - begin) + 1);
+      if (f_status_is_error(status)) {
+        if (f_status_set_fine(status) == f_maybe) {
+          return f_status_set_error(f_invalid_utf);
+        }
+
+        return status;
+      }
+
+      if (status == f_false) break;
+
+      width = f_macro_utf_byte_width_is(string[begin]);
+    } // for
+
+    for (; end > begin; end--) {
+      // skip past NULL.
+      if (string[end] == f_string_eos) continue;
+      if (end == begin) break;
+
+      for (;;) {
+        width = f_macro_utf_byte_width_is(string[end]);
+        if (width == 1) {
+          end--;
+
+          if (end == begin) break;
+        }
+        else break;
+      } // for
+
+      if (end == begin) break;
+
+      status = f_utf_is_whitespace(string + end, (stop - end) + 1);
+      if (f_status_is_error(status)) {
+        if (f_status_set_fine(status) == f_maybe) {
+          return f_status_set_error(f_invalid_utf);
+        }
+
+        return status;
+      }
+
+      if (status == f_false) break;
+    } // for
+
+    memcpy(result->string, string + begin, (end - begin) + 1);
+    result->used = (end - begin) + 1;
+
+    return f_none;
+  }
+#endif // !defined(_di_fl_string_rip_trim_) || !defined(_di_fl_string_dynamic_rip_trim_)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_1/fl_string/c/private-string.h b/level_1/fl_string/c/private-string.h
new file mode 100644 (file)
index 0000000..cb7e011
--- /dev/null
@@ -0,0 +1,142 @@
+/**
+ * FLL - Level 1
+ *
+ * Project: String
+ * API Version: 0.5
+ * Licenses: lgplv2.1
+ *
+ * Provides basic string manipulation and processing capabilities.
+ *
+ * These are provided for internal reduction in redundant code.
+ * These should not be exposed/used outside of this project.
+ */
+#ifndef _PRIVATE_FL_string_h
+#define _PRIVATE_FL_string_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Private implementation of fl_string_compare().
+ *
+ * Intended to be shared to each of the different implementation variations.
+ *
+ * @param string1
+ *   String to compare.
+ * @param string2
+ *   String to compare.
+ * @param offset1
+ *   Offset of string1 to start at.
+ * @param offset2
+ *   Offset of string2 to start at.
+ * @param stop1
+ *   Exclusive stop position for string1.
+ * @param stop2
+ *   Exclusive stop position for string2.
+ *
+ * @return
+ *   f_equal_to when both strings equal.
+ *   f_not_equal_to when both strings do not equal.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_string_compare()
+ * @see fl_string_dynamic_compare()
+ * @see fl_string_dynamic_partial_compare()
+ */
+#if !defined(_di_fl_string_compare_) || !defined(_di_fl_string_dynamic_compare_) || !defined(_di_fl_string_dynamic_partial_compare_)
+  extern f_return_status private_fl_string_compare(const f_string string1, const f_string string2, const f_string_length offset1, const f_string_length offset2, const f_string_length stop1, const f_string_length stop2) f_gcc_attribute_visibility_internal;
+#endif // !defined(_di_fl_string_compare_) || !defined(_di_fl_string_dynamic_compare_) || !defined(_di_fl_string_dynamic_partial_compare_)
+
+/**
+ * Private implementation of fl_string_compare_trim().
+ *
+ * Intended to be shared to each of the different implementation variations.
+ *
+ * @param string1
+ *   String to compare.
+ * @param string2
+ *   String to compare.
+ * @param offset1
+ *   Offset of string1 to start at.
+ * @param offset2
+ *   Offset of string2 to start at.
+ * @param stop1
+ *   Exclusive stop position for string1.
+ * @param stop2
+ *   Exclusive stop position for string2.
+ *
+ * @return
+ *   f_equal_to when both strings equal.
+ *   f_not_equal_to when both strings do not equal.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_string_compare_trim()
+ * @see fl_string_dynamic_compare_trim()
+ * @see fl_string_dynamic_partial_compare_trim()
+ */
+#if !defined(_di_fl_string_compare_trim_) || !defined(_di_fl_string_dynamic_compare_trim_) || !defined(_di_fl_string_dynamic_partial_compare_trim_)
+  extern f_return_status private_fl_string_compare_trim(const f_string string1, const f_string string2, const f_string_length offset1, const f_string_length offset2, const f_string_length stop1, const f_string_length stop2) f_gcc_attribute_visibility_internal;
+#endif // !defined(_di_fl_string_compare_trim_) || !defined(_di_fl_string_dynamic_compare_trim_) || !defined(_di_fl_string_dynamic_partial_compare_trim_)
+
+/**
+ * Private implementation of fl_string_rip().
+ *
+ * Intended to be shared to each of the different implementation variations.
+ *
+ * @param string
+ *   The string to rip from.
+ * @param start
+ *   Inclusive start point of string to rip.
+ * @param stop
+ *   Inclusive stop point of string to rip.
+ * @param result
+ *   The new string, which will be allocated or reallocated as necessary.
+ *
+ * @return
+ *   f_none on success.
+ *   f_no_data if nothing to rip, no allocations or reallocations are performed.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_error_allocation (with error bit) on memory allocation error.
+ *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see fl_string_dynamic_rip()
+ * @see fl_string_rip()
+ */
+#if !defined(_di_fl_string_rip_) || !defined(_di_fl_string_dynamic_rip_)
+  extern f_return_status private_fl_string_rip(const f_string string, const f_string_length start, const f_string_length stop, f_string_dynamic *result) f_gcc_attribute_visibility_internal;
+#endif // !defined(_di_fl_string_rip_) || !defined(_di_fl_string_dynamic_rip_)
+
+/**
+ * Private implementation of fl_string_rip_trim().
+ *
+ * Intended to be shared to each of the different implementation variations.
+ *
+ * @param string
+ *   The string to rip from.
+ * @param start
+ *   Inclusive start point of string to rip.
+ * @param stop
+ *   Inclusive stop point of string to rip.
+ * @param result
+ *   The new string, which will be allocated or reallocated as necessary.
+ *
+ * @return
+ *   f_none on success.
+ *   f_no_data if nothing to rip, no allocations or reallocations are performed.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_error_allocation (with error bit) on memory allocation error.
+ *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see fl_string_dynamic_rip()
+ * @see fl_string_rip()
+ */
+#if !defined(_di_fl_string_rip_trim_) || !defined(_di_fl_string_dynamic_rip_trim_)
+  extern f_return_status private_fl_string_rip_trim(const f_string string, const f_string_length start, const f_string_length stop, f_string_dynamic *result) f_gcc_attribute_visibility_internal;
+#endif // !defined(_di_fl_string_rip_trim_) || !defined(_di_fl_string_dynamic_rip_trim_)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_FL_string_h
index 6230dbd8809b59151521b5d440f9c8cb8d4604a2..514c95c961bed58dffe11d64add2080a462ea8c5 100644 (file)
@@ -1,4 +1,5 @@
 #include <level_1/string.h>
+#include "private-string.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -11,72 +12,42 @@ extern "C" {
       if (length2 <= 0) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
-    f_string_length i1 = 0;
-    f_string_length i2 = 0;
-
-    for (; i1 < length1 && i2 < length2; i1++, i2++) {
-      // skip past newlines in string1.
-      while (i1 < length1 && string1[i1] == f_string_eos) i1++;
-      if (i1 == length1) break;
-
-      // skip past newlines in string2.
-      while (i2 < length2 && string2[i2] == f_string_eos) i2++;
-      if (i2 == length2) break;
-
-      if (string1[i1] != string2[i2]) return f_not_equal_to;
-    } // for
-
-    // only return f_equal_to if all remaining characters are NULL.
-    while (i1 < length1) {
-      if (string1[i1] != f_string_eos) return f_not_equal_to;
-      i1++;
-    } // while
+    return private_fl_string_compare(string1, string2, 0, 0, length1, length2);
+  }
+#endif // _di_fl_string_compare_
 
-    while (i2 < length2) {
-      if (string2[i2] != f_string_eos) return f_not_equal_to;
-      i2++;
-    } // while
+#ifndef _di_fl_string_compare_trim_
+  f_return_status fl_string_compare_trim(const f_string string1, const f_string string2, const f_string_length length1, const f_string_length length2) {
+    #ifndef _di_level_1_parameter_checking_
+      if (length1 <= 0) return f_status_set_error(f_invalid_parameter);
+      if (length2 <= 0) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
 
-    return f_equal_to;
+    return private_fl_string_compare_trim(string1, string2, 0, 0, length1, length2);
   }
-#endif // _di_fl_string_compare_
+#endif // _di_fl_string_compare_trim_
 
-#ifndef _di_f_string_dynamics_compare_
-  f_return_status f_string_dynamics_compare(const f_string_dynamic string1, const f_string_dynamic string2) {
+#ifndef _di_f_string_dynamic_compare_
+  f_return_status f_string_dynamic_compare(const f_string_dynamic string1, const f_string_dynamic string2) {
     #ifndef _di_level_1_parameter_checking_
       if (string1.used <= 0) return f_status_set_error(f_invalid_parameter);
       if (string2.used <= 0) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
-    f_string_length i1 = 0;
-    f_string_length i2 = 0;
-
-    for (; i1 < string1.used && i2 < string2.used; i1++, i2++) {
-      // skip past newlines in string1.
-      while (i1 < string1.used && string1.string[i1] == f_string_eos) i1++;
-      if (i1 == string1.used) break;
-
-      // skip past newlines in string2.
-      while (i2 < string2.used && string2.string[i2] == f_string_eos) i2++;
-      if (i2 == string2.used) break;
-
-      if (string1.string[i1] != string2.string[i2]) return f_not_equal_to;
-    } // for
-
-    // only return f_equal_to if all remaining characters are NULL.
-    while (i1 < string1.used) {
-      if (string1.string[i1] != f_string_eos) return f_not_equal_to;
-      i1++;
-    } // while
+    return private_fl_string_compare(string1.string, string2.string, 0, 0, string1.used, string2.used);
+  }
+#endif // _di_f_string_dynamic_compare_
 
-    while (i2 < string2.used) {
-      if (string2.string[i2] != f_string_eos) return f_not_equal_to;
-      i2++;
-    } // while
+#ifndef _di_f_string_dynamic_compare_trim_
+  f_return_status f_string_dynamic_compare_trim(const f_string_dynamic string1, const f_string_dynamic string2) {
+    #ifndef _di_level_1_parameter_checking_
+      if (string1.used <= 0) return f_status_set_error(f_invalid_parameter);
+      if (string2.used <= 0) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
 
-    return f_equal_to;
+    return private_fl_string_compare_trim(string1.string, string2.string, 0, 0, string1.used, string2.used);
   }
-#endif // _di_f_string_dynamics_compare_
+#endif // _di_f_string_dynamic_compare_trim_
 
 #ifndef _di_fl_string_dynamic_partial_compare_
   f_return_status fl_string_dynamic_partial_compare(const f_string_dynamic string1, const f_string_dynamic string2, const f_string_location offset1, const f_string_location offset2) {
@@ -91,83 +62,64 @@ extern "C" {
       if (string2.used <= offset2.stop) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
-    f_string_length i1 = offset1.start;
-    f_string_length i2 = offset2.start;
-
-    const f_string_length stop1 = offset1.stop + 1;
-    const f_string_length stop2 = offset2.stop + 1;
-
-    for (; i1 < stop1 && i2 < stop2; i1++, i2++) {
-      // skip past newlines in string1.
-      while (i1 < stop1 && string1.string[i1] == f_string_eos) i1++;
-      if (i1 == stop1) break;
-
-      // skip past newlines in string2.
-      while (i2 < stop2 && string2.string[i2] == f_string_eos) i2++;
-      if (i2 == stop2) break;
+    return private_fl_string_compare(string1.string, string2.string, offset1.start, offset2.start, offset1.stop + 1, offset2.stop + 1);
+  }
+#endif // _di_fl_string_dynamic_partial_compare_
 
-      if (string1.string[i1] != string2.string[i2]) return f_not_equal_to;
-    } // for
+#ifndef _di_fl_string_dynamic_partial_compare_trim_
+  f_return_status fl_string_dynamic_partial_compare_trim(const f_string_dynamic string1, const f_string_dynamic string2, const f_string_location offset1, const f_string_location offset2) {
+    #ifndef _di_level_1_parameter_checking_
+      if (string1.used <= 0) return f_status_set_error(f_invalid_parameter);
+      if (string2.used <= 0) return f_status_set_error(f_invalid_parameter);
 
-    // only return f_equal_to if all remaining characters are NULL.
-    while (i1 < stop1) {
-      if (string1.string[i1] != f_string_eos) return f_not_equal_to;
-      i1++;
-    } // while
+      if (offset1.start > offset1.stop) return f_status_set_error(f_invalid_parameter);
+      if (offset2.start > offset2.stop) return f_status_set_error(f_invalid_parameter);
 
-    while (i2 < stop2) {
-      if (string2.string[i2] != f_string_eos) return f_not_equal_to;
-      i2++;
-    } // while
+      if (string1.used <= offset1.stop) return f_status_set_error(f_invalid_parameter);
+      if (string2.used <= offset2.stop) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
 
-    return f_equal_to;
+    return private_fl_string_compare_trim(string1.string, string2.string, offset1.start, offset2.start, offset1.stop + 1, offset2.stop + 1);
   }
-#endif // _di_fl_string_dynamic_partial_compare_
+#endif // _di_fl_string_dynamic_partial_compare_trim_
 
-#ifndef _di_fl_string_rip_
-  f_return_status fl_string_rip(const f_string_dynamic buffer, const f_string_location location, f_string_dynamic *result) {
+#ifndef _di_fl_string_dynamic_rip_
+  f_return_status fl_string_dynamic_rip(const f_string_dynamic buffer, const f_string_location location, f_string_dynamic *result) {
     #ifndef _di_level_1_parameter_checking_
       if (location.start < 0) return f_status_set_error(f_invalid_parameter);
       if (location.stop < location.start) return f_status_set_error(f_invalid_parameter);
       if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (location.start >= buffer.used) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location.start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location.stop) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
-    // The start and stop point are inclusive locations, and therefore start - stop is actually 1 too few locations.
-    f_string_length size = (location.stop - location.start) + 1;
-
-    if (size > 0) {
-      f_status status = f_none;
-
-      if (result == 0) {
-        f_macro_string_dynamic_new(status, (*result), size);
-      }
-      else {
-        f_macro_string_dynamic_resize(status, (*result), size);
-      }
-
-      if (f_status_is_error(status)) {
-        return status;
-      }
-
-      memcpy(result->string, buffer.string + location.start, size);
-      result->used = size;
+    return private_fl_string_rip(buffer.string, location.start, location.stop, result);
+  }
+#endif // _di_fl_string_dynamic_rip_
 
-      return f_none;
-    }
+#ifndef _di_fl_string_dynamic_rip_trim_
+  f_return_status fl_string_dynamic_rip_trim(const f_string_dynamic buffer, const f_string_location location, f_string_dynamic *result) {
+    #ifndef _di_level_1_parameter_checking_
+      if (location.start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location.stop < location.start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location.start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location.stop) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
 
-    return f_no_data;
+    return private_fl_string_rip_trim(buffer.string, location.start, location.stop, result);
   }
-#endif // _di_fl_string_rip_
+#endif // _di_fl_string_dynamic_rip_trim_
 
-#ifndef _di_fl_string_seek_line_to_
-  f_return_status fl_string_seek_line_to(const f_string_dynamic buffer, f_string_location *location, const int8_t seek_to_this) {
+#ifndef _di_fl_string_dynamic_seek_line_to_
+  f_return_status fl_string_dynamic_seek_line_to(const f_string_dynamic buffer, f_string_location *location, const int8_t seek_to_this) {
     #ifndef _di_level_1_parameter_checking_
       if (location == 0) return f_status_set_error(f_invalid_parameter);
       if (location->start < 0) return f_status_set_error(f_invalid_parameter);
       if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
       if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (location->start >= buffer.used) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->stop) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
     while (buffer.string[location->start] != seek_to_this) {
@@ -181,16 +133,17 @@ extern "C" {
 
     return f_none;
   }
-#endif // _di_fl_string_seek_line_to_
+#endif // _di_fl_string_dynamic_seek_line_to_
 
-#ifndef _di_fl_string_seek_line_to_utf_character_
-  f_return_status fl_string_seek_line_to_utf_character(const f_string_dynamic buffer, f_string_location *location, const f_utf_character seek_to_this) {
+#ifndef _di_fl_string_dynamic_seek_line_to_utf_character_
+  f_return_status fl_string_dynamic_seek_line_to_utf_character(const f_string_dynamic buffer, f_string_location *location, const f_utf_character seek_to_this) {
     #ifndef _di_level_1_parameter_checking_
       if (location == 0) return f_status_set_error(f_invalid_parameter);
       if (location->start < 0) return f_status_set_error(f_invalid_parameter);
       if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
       if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (location->start >= buffer.used) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->stop) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
     const unsigned short seek_width = f_macro_utf_character_width(seek_to_this);
@@ -248,16 +201,17 @@ extern "C" {
 
     return f_none_on_eos;
   }
-#endif // _di_fl_string_seek_line_to_utf_character_
+#endif // _di_fl_string_dynamic_seek_line_to_utf_character_
 
-#ifndef _di_fl_string_seek_line_until_graph_
-  f_return_status fl_string_seek_line_until_graph(const f_string_dynamic buffer, f_string_location *location, const int8_t placeholder) {
+#ifndef _di_fl_string_dynamic_seek_line_until_graph_
+  f_return_status fl_string_dynamic_seek_line_until_graph(const f_string_dynamic buffer, f_string_location *location, const int8_t placeholder) {
     #ifndef _di_level_1_parameter_checking_
       if (location == 0) return f_status_set_error(f_invalid_parameter);
       if (location->start < 0) return f_status_set_error(f_invalid_parameter);
       if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
       if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (location->start >= buffer.used) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->stop) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
     f_status status = f_none;
@@ -308,16 +262,17 @@ extern "C" {
 
     return f_none;
   }
-#endif // _di_fl_string_seek_line_until_graph_
+#endif // _di_fl_string_dynamic_seek_line_until_graph_
 
-#ifndef _di_fl_string_seek_line_until_non_graph_
-  f_return_status fl_string_seek_line_until_non_graph(const f_string_dynamic buffer, f_string_location *location, const int8_t placeholder) {
+#ifndef _di_fl_string_dynamic_seek_line_until_non_graph_
+  f_return_status fl_string_dynamic_seek_line_until_non_graph(const f_string_dynamic buffer, f_string_location *location, const int8_t placeholder) {
     #ifndef _di_level_1_parameter_checking_
       if (location == 0) return f_status_set_error(f_invalid_parameter);
       if (location->start < 0) return f_status_set_error(f_invalid_parameter);
       if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
       if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (location->start >= buffer.used) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->stop) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
     f_status status = f_none;
@@ -368,16 +323,17 @@ extern "C" {
 
     return f_none;
   }
-#endif // _di_fl_string_seek_line_until_non_graph_
+#endif // _di_fl_string_dynamic_seek_line_until_non_graph_
 
-#ifndef _di_fl_string_seek_to_
-  f_return_status fl_string_seek_to(const f_string_dynamic buffer, f_string_location *location, const int8_t seek_to_this) {
+#ifndef _di_fl_string_dynamic_seek_to_
+  f_return_status fl_string_dynamic_seek_to(const f_string_dynamic buffer, f_string_location *location, const int8_t seek_to_this) {
     #ifndef _di_level_1_parameter_checking_
       if (location == 0) return f_status_set_error(f_invalid_parameter);
       if (location->start < 0) return f_status_set_error(f_invalid_parameter);
       if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
       if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (location->start >= buffer.used) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->stop) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
     while (buffer.string[location->start] != seek_to_this) {
@@ -389,16 +345,17 @@ extern "C" {
 
     return f_none;
   }
-#endif // _di_fl_string_seek_to_
+#endif // _di_fl_string_dynamic_seek_to_
 
-#ifndef _di_fl_string_seek_to_utf_character_
-  f_return_status fl_string_seek_to_utf_character(const f_string_dynamic buffer, f_string_location *location, const f_utf_character seek_to_this) {
+#ifndef _di_fl_string_dynamic_seek_to_utf_character_
+  f_return_status fl_string_dynamic_seek_to_utf_character(const f_string_dynamic buffer, f_string_location *location, const f_utf_character seek_to_this) {
     #ifndef _di_level_1_parameter_checking_
       if (location == 0) return f_status_set_error(f_invalid_parameter);
       if (location->start < 0) return f_status_set_error(f_invalid_parameter);
       if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
       if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (location->start >= buffer.used) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->stop) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
     const unsigned short seek_width = f_macro_utf_character_width(seek_to_this);
@@ -454,6 +411,270 @@ extern "C" {
 
     return f_none_on_eos;
   }
+#endif // _di_fl_string_dynamic_seek_to_utf_character_
+
+#ifndef _di_fl_string_rip_
+  f_return_status fl_string_rip(const f_string string, const f_string_length start, const f_string_length stop, f_string_dynamic *result) {
+    #ifndef _di_level_1_parameter_checking_
+      if (start > stop) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    return private_fl_string_rip(string, start, stop, result);
+  }
+#endif // _di_fl_string_rip_
+
+#ifndef _di_fl_string_rip_trim_
+  f_return_status fl_string_rip_trim(const f_string string, const f_string_length start, const f_string_length stop, f_string_dynamic *result) {
+    #ifndef _di_level_1_parameter_checking_
+      if (start > stop) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    return private_fl_string_rip_trim(string, start, stop, result);
+  }
+#endif // _di_fl_string_rip_trim_
+
+#ifndef _di_fl_string_seek_line_to_
+  f_return_status fl_string_seek_line_to(const f_string string, f_string_location *location, const int8_t seek_to_this) {
+    #ifndef _di_level_1_parameter_checking_
+      if (location == 0) return f_status_set_error(f_invalid_parameter);
+      if (location->start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    while (string[location->start] != seek_to_this) {
+      if (string[location->start] == f_string_eol) return f_none_on_eol;
+
+      location->start++;
+
+      if (location->start > location->stop) return f_none_on_stop;
+    } // while
+
+    return f_none;
+  }
+#endif // _di_fl_string_seek_line_to_
+
+#ifndef _di_fl_string_seek_line_to_utf_character_
+  f_return_status fl_string_seek_line_to_utf_character(const f_string string, f_string_location *location, const f_utf_character seek_to_this) {
+    #ifndef _di_level_1_parameter_checking_
+      if (location == 0) return f_status_set_error(f_invalid_parameter);
+      if (location->start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    const unsigned short seek_width = f_macro_utf_character_width(seek_to_this);
+
+    f_status status = f_none;
+
+    unsigned short width = 0;
+
+    f_string_length max_width = (location->stop - location->start) + 1;
+
+    for (; location->start <= location->stop; location->start += width) {
+      max_width = (location->stop - location->start) + 1;
+
+      width = f_macro_utf_byte_width_is(string[location->start]);
+
+      if (width == 0) {
+        width = 1;
+
+        if (string[location->start] == f_string_eol) return f_none_on_eol;
+
+        if (seek_width == width) {
+          if (string[location->start] == seek_to_this) return f_none;
+        }
+      }
+      // Do not operate on UTF-8 fragments that are not the first byte of the character.
+      else if (width == 1) {
+        return f_status_set_error(f_incomplete_utf);
+      }
+      else {
+        if (location->start + width > location->stop) return f_status_set_error(f_incomplete_utf_on_eos);
+
+        if (width == seek_width) {
+          f_utf_character character = 0;
+          status = f_utf_char_to_character(string + location->start, max_width, &character);
+
+          if (f_status_is_error(status)) {
+            return status;
+          }
+
+          if (character == seek_to_this) {
+            return f_none;
+          }
+        }
+      }
+    } // for
+
+    return f_none_on_stop;
+  }
+#endif // _di_fl_string_seek_line_to_utf_character_
+
+#ifndef _di_fl_string_seek_line_until_graph_
+  f_return_status fl_string_seek_line_until_graph(const f_string string, f_string_location *location, const int8_t placeholder) {
+    #ifndef _di_level_1_parameter_checking_
+      if (location == 0) return f_status_set_error(f_invalid_parameter);
+      if (location->start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    f_status status = f_none;
+    unsigned short width = 0;
+
+    f_string_length max_width = (location->stop - location->start) + 1;
+
+    while (string[location->start] == placeholder || (status = f_utf_is_graph(string + location->start, max_width)) == f_false) {
+      if (f_status_is_error(status)) {
+        return status;
+      }
+
+      if (string[location->start] == f_string_eol) return f_none_on_eol;
+
+      width = f_macro_utf_byte_width_is(string[location->start]);
+
+      if (width == 0) {
+        width = 1;
+      }
+      // Do not operate on UTF-8 fragments that are not the first byte of the character.
+      else if (width == 1) {
+        return f_status_set_error(f_incomplete_utf);
+      }
+      else {
+        if (location->start + width > location->stop) return f_status_set_error(f_incomplete_utf_on_stop);
+      }
+
+      location->start += width;
+
+      if (location->start > location->stop) return f_none_on_stop;
+
+      max_width = (location->stop - location->start) + 1;
+    } // while
+
+    if (f_status_is_error(status)) {
+      return status;
+    }
+
+    return f_none;
+  }
+#endif // _di_fl_string_seek_line_until_graph_
+
+#ifndef _di_fl_string_seek_line_until_non_graph_
+  f_return_status fl_string_seek_line_until_non_graph(const f_string string, f_string_location *location, const int8_t placeholder) {
+    #ifndef _di_level_1_parameter_checking_
+      if (location == 0) return f_status_set_error(f_invalid_parameter);
+      if (location->start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    f_status status = f_none;
+    unsigned short width = 0;
+
+    f_string_length max_width = (location->stop - location->start) + 1;
+
+    while (string[location->start] == placeholder || (status = f_utf_is_whitespace(string + location->start, max_width)) == f_false) {
+      if (f_status_is_error(status)) {
+        return status;
+      }
+
+      if (string[location->start] == f_string_eol) return f_none_on_eol;
+
+      width = f_macro_utf_byte_width_is(string[location->start]);
+
+      if (width == 0) {
+        width = 1;
+      }
+      // Do not operate on UTF-8 fragments that are not the first byte of the character.
+      else if (width == 1) {
+        return f_status_set_error(f_incomplete_utf);
+      }
+      else {
+        if (location->start + width > location->stop) return f_status_set_error(f_incomplete_utf_on_stop);
+      }
+
+      location->start += width;
+
+      if (location->start > location->stop) return f_none_on_stop;
+
+      max_width = (location->stop - location->start) + 1;
+    } // while
+
+    if (f_status_is_error(status)) {
+      return status;
+    }
+
+    return f_none;
+  }
+#endif // _di_fl_string_seek_line_until_non_graph_
+
+#ifndef _di_fl_string_seek_to_
+  f_return_status fl_string_seek_to(const f_string string, f_string_location *location, const int8_t seek_to_this) {
+    #ifndef _di_level_1_parameter_checking_
+      if (location == 0) return f_status_set_error(f_invalid_parameter);
+      if (location->start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    while (string[location->start] != seek_to_this) {
+      location->start++;
+
+      if (location->start > location->stop) return f_none_on_stop;
+    } // while
+
+    return f_none;
+  }
+#endif // _di_fl_string_seek_to_
+
+#ifndef _di_fl_string_seek_to_utf_character_
+  f_return_status fl_string_seek_to_utf_character(const f_string string, f_string_location *location, const f_utf_character seek_to_this) {
+    #ifndef _di_level_1_parameter_checking_
+      if (location == 0) return f_status_set_error(f_invalid_parameter);
+      if (location->start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    const unsigned short seek_width = f_macro_utf_character_width(seek_to_this);
+
+    f_status status = f_none;
+
+    unsigned short width = 0;
+
+    f_string_length max_width = 0;
+
+    for (; location->start <= location->stop; location->start += width) {
+      max_width = (location->stop - location->start) + 1;
+
+      width = f_macro_utf_byte_width_is(string[location->start]);
+
+      if (width == 0) {
+        width = 1;
+
+        if (seek_width == width) {
+          if (string[location->start] == seek_to_this) return f_none;
+        }
+      }
+      // Do not operate on UTF-8 fragments that are not the first byte of the character.
+      else if (width == 1) {
+        return f_status_set_error(f_incomplete_utf);
+      }
+      else {
+        if (location->start + width > location->stop) return f_status_set_error(f_incomplete_utf_on_stop);
+
+        if (width == seek_width) {
+          f_utf_character character = 0;
+          status = f_utf_char_to_character(string + location->start, max_width, &character);
+
+          if (f_status_is_error(status)) {
+            return status;
+          }
+
+          if (character == seek_to_this) {
+            return f_none;
+          }
+        }
+      }
+    } // for
+
+    return f_none_on_stop;
+  }
 #endif // _di_fl_string_seek_to_utf_character_
 
 #ifdef __cplusplus
index 37d8a638fcd7fd58b84b781235a32d7a38d5d493..c44da945671df506e6f7bff0fcb9b4772cdb7bfe 100644 (file)
@@ -6,6 +6,15 @@
  * Licenses: lgplv2.1
  *
  * Provides basic string manipulation and processing capabilities.
+ *
+ * UTF-8 is handled within these functions via normal string characters (f_string).
+ * Unlike f_utf_string, these f_string base functions do not force padding of ASCII or UTF-8 characters.
+ * An ASCII character is 1-byte and a 4-byte wide UTF-8 character is 4-byte.
+ *
+ * This shortens the size of the string at the cost of complexity vs f_utf_string.
+ *
+ * Because of the variable width, it is generally unsafe to ignore invalid UTF-8 sequences.
+ * Invalid UTF-8 will be generally result in an error.
  */
 #ifndef _FL_string_h
 #define _FL_string_h
@@ -45,8 +54,10 @@ extern "C" {
  *   f_not_equal_to when both strings do not equal.
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *
- * @see f_string_dynamics_compare()
+ * @see fl_string_dynamic_compare()
+ * @see fl_string_dynamic_compare_trim()
  * @see fl_string_dynamic_partial_compare()
+ * @see fl_string_dynamic_partial_compare_trim()
  */
 #ifndef _di_fl_string_compare_
   extern f_return_status fl_string_compare(const f_string string1, const f_string string2, const f_string_length length1, const f_string_length length2);
@@ -57,6 +68,62 @@ extern "C" {
  *
  * This does not stop on NULL.
  * NULL characters are ignored.
+ * Ignores leading and trailing whitespace.
+ *
+ * @param string1
+ *   String to compare.
+ * @param string2
+ *   String to compare.
+ * @param length1
+ *   Length of string1.
+ * @param length2
+ *   Length of string2.
+ *
+ * @return
+ *   f_equal_to when both strings equal.
+ *   f_not_equal_to when both strings do not equal.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_string_dynamic_compare()
+ * @see fl_string_dynamic_compare_trim()
+ * @see fl_string_dynamic_partial_compare()
+ * @see fl_string_dynamic_partial_compare_trim()
+ */
+#ifndef _di_fl_string_compare_trim_
+  extern f_return_status fl_string_compare_trim(const f_string string1, const f_string string2, const f_string_length length1, const f_string_length length2);
+#endif // _di_fl_string_compare_trim_
+
+/**
+ * Compare two strings, similar to strncmp().
+ *
+ * This does not stop on NULL.
+ * NULL characters are ignored.
+ *
+ * @param string1
+ *   String to compare.
+ * @param string2
+ *   String to compare.
+ *
+ * @return
+ *   f_equal_to when both strings equal.
+ *   f_not_equal_to when both strings do not equal.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_string_compare()
+ * @see fl_string_compare_trim()
+ * @see fl_string_dynamic_partial_compare()
+ * @see fl_string_dynamic_partial_compare_trim()
+ */
+#ifndef _di_f_string_dynamic_compare_
+  extern f_return_status f_string_dynamic_compare(const f_string_dynamic string1, const f_string_dynamic string2);
+#endif // _di_f_string_dynamic_compare_
+
+/**
+ * Compare two strings, similar to strncmp().
+ *
+ * This does not stop on NULL.
+ * NULL characters are ignored.
+ * Ignores leading and trailing whitespace.
  *
  * @param string1
  *   String to compare.
@@ -69,11 +136,13 @@ extern "C" {
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *
  * @see fl_string_compare()
+ * @see fl_string_compare_trim()
  * @see fl_string_dynamic_partial_compare()
+ * @see fl_string_dynamic_partial_compare_trim()
  */
-#ifndef _di_f_string_dynamics_compare_
-  extern f_return_status f_string_dynamics_compare(const f_string_dynamic string1, const f_string_dynamic string2);
-#endif // _di_f_string_dynamics_compare_
+#ifndef _di_f_string_dynamic_compare_trim_
+  extern f_return_status f_string_dynamic_compare_trim(const f_string_dynamic string1, const f_string_dynamic string2);
+#endif // _di_f_string_dynamic_compare_trim_
 
 /**
  * Compare two strings, similar to strncmp(), but restricted to the given ranges.
@@ -96,14 +165,46 @@ extern "C" {
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *
  * @see fl_string_compare()
- * @see f_string_dynamics_compare()
+ * @see fl_string_compare_trim()
+ * @see fl_string_dynamic_compare()
+ * @see fl_string_dynamic_compare_trim()
  */
 #ifndef _di_fl_string_dynamic_partial_compare_
   extern f_return_status fl_string_dynamic_partial_compare(const f_string_dynamic string1, const f_string_dynamic string2, const f_string_location offset1, const f_string_location offset2);
 #endif // _di_fl_string_dynamic_partial_compare_
 
 /**
- * Allocated a new string from the provided range in the buffer.
+ * Compare two strings, similar to strncmp(), but restricted to the given ranges.
+ *
+ * This does not stop on NULL.
+ * NULL characters are ignored.
+ * Ignores leading and trailing whitespace.
+ *
+ * @param string1
+ *   String to compare.
+ * @param string2
+ *   String to compare.
+ * @param offset1
+ *   A range within the string1 to restrict the comparison to.
+ * @param offset2
+ *   A range within the string2 to restrict the comparison to.
+ *
+ * @return
+ *   f_equal_to when both strings equal.
+ *   f_not_equal_to when both strings do not equal.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_string_compare()
+ * @see fl_string_compare_trim()
+ * @see fl_string_dynamic_compare()
+ * @see fl_string_dynamic_compare_trim()
+ */
+#ifndef _di_fl_string_dynamic_partial_compare_trim_
+  extern f_return_status fl_string_dynamic_partial_compare_trim(const f_string_dynamic string1, const f_string_dynamic string2, const f_string_location offset1, const f_string_location offset2);
+#endif // _di_fl_string_dynamic_partial_compare_trim_
+
+/**
+ * Allocate a new string from the provided range in the buffer.
  *
  * @param buffer
  *   The buffer to rip from.
@@ -118,10 +219,42 @@ extern "C" {
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *   f_error_allocation (with error bit) on memory allocation error.
  *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see fl_string_dynamic_rip_trim()
+ * @see fl_string_rip()
+ * @see fl_string_rip_trim()
  */
-#ifndef _di_fl_string_rip_
-  extern f_return_status fl_string_rip(const f_string_dynamic buffer, const f_string_location location, f_string_dynamic *result);
-#endif // _di_fl_string_rip_
+#ifndef _di_fl_string_dynamic_rip_
+  extern f_return_status fl_string_dynamic_rip(const f_string_dynamic buffer, const f_string_location location, f_string_dynamic *result);
+#endif // _di_fl_string_dynamic_rip_
+
+/**
+ * Allocate a new string from the provided range in the buffer.
+ *
+ * Ignores leading and trailing whitespace.
+ * As a result, resulting size may be smaller than requested range.
+ *
+ * @param buffer
+ *   The buffer to rip from.
+ * @param location
+ *   A range within the buffer representing the string to rip.
+ * @param result
+ *   The new string, which will be allocated or reallocated as necessary.
+ *
+ * @return
+ *   f_none on success.
+ *   f_no_data if nothing to rip, no allocations or reallocations are performed.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_error_allocation (with error bit) on memory allocation error.
+ *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see fl_string_dynamic_rip()
+ * @see fl_string_dynamic_rip_trim()
+ * @see fl_string_rip()
+ */
+#ifndef _di_fl_string_dynamic_rip_trim_
+  extern f_return_status fl_string_dynamic_rip_trim(const f_string_dynamic buffer, const f_string_location location, f_string_dynamic *result);
+#endif // _di_fl_string_dynamic_rip_trim_
 
 /**
  * Seek the buffer location forward until the character (1-byte wide) or EOL is reached.
@@ -141,11 +274,12 @@ extern "C" {
  *   f_none_on_stop on success, but stopped stop location.
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *
+ * @see fl_string_dynamic_seek_line_to_utf_character()
  * @see fl_string_seek_line_to_utf_character()
  */
-#ifndef _di_fl_string_seek_line_to_
-  extern f_return_status fl_string_seek_line_to(const f_string_dynamic buffer, f_string_location *location, const int8_t seek_to_this);
-#endif // _di_fl_string_seek_line_to_
+#ifndef _di_fl_string_dynamic_seek_line_to_
+  extern f_return_status fl_string_dynamic_seek_line_to(const f_string_dynamic buffer, f_string_location *location, const int8_t seek_to_this);
+#endif // _di_fl_string_dynamic_seek_line_to_
 
 /**
  * Seek the buffer location forward until the character (up to 4-byte wide) or EOL is reached.
@@ -168,11 +302,12 @@ extern "C" {
  *   f_incomplete_utf_on_eos (with error bit) if end of string is reached before a complete UTF-8 character can be processed.
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *
+ * @see fl_string_dynamic_seek_line_to()
  * @see fl_string_seek_line_to()
  */
-#ifndef _di_fl_string_seek_line_to_utf_character_
-  extern f_return_status fl_string_seek_line_to_utf_character(const f_string_dynamic buffer, f_string_location *location, const f_utf_character seek_to_this);
-#endif // _di_fl_string_seek_line_to_utf_character_
+#ifndef _di_fl_string_dynamic_seek_line_to_utf_character_
+  extern f_return_status fl_string_dynamic_seek_line_to_utf_character(const f_string_dynamic buffer, f_string_location *location, const f_utf_character seek_to_this);
+#endif // _di_fl_string_dynamic_seek_line_to_utf_character_
 
 /**
  * Increment buffer location until a graph character (including UTF-8) or an EOL is matched.
@@ -194,10 +329,12 @@ extern "C" {
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *   f_error_allocation (with error bit) on memory allocation error.
  *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see fl_string_seek_line_until_graph()
  */
-#ifndef _di_fl_string_seek_line_until_graph_
-  extern f_return_status fl_string_seek_line_until_graph(const f_string_dynamic buffer, f_string_location *location, const int8_t placeholder);
-#endif // _di_fl_string_seek_line_until_graph_
+#ifndef _di_fl_string_dynamic_seek_line_until_graph_
+  extern f_return_status fl_string_dynamic_seek_line_until_graph(const f_string_dynamic buffer, f_string_location *location, const int8_t placeholder);
+#endif // _di_fl_string_dynamic_seek_line_until_graph_
 
 /**
  * Increment buffer location until a non-graph character (including UTF-8) or an EOL is matched.
@@ -220,10 +357,12 @@ extern "C" {
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *   f_error_allocation (with error bit) on memory allocation error.
  *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see fl_string_seek_line_until_non_graph()
  */
-#ifndef _di_fl_string_seek_line_until_non_graph_
-  extern f_return_status fl_string_seek_line_until_non_graph(const f_string_dynamic buffer, f_string_location *location, const int8_t placeholder);
-#endif // _di_fl_string_seek_line_until_non_graph_
+#ifndef _di_fl_string_dynamic_seek_line_until_non_graph_
+  extern f_return_status fl_string_dynamic_seek_line_until_non_graph(const f_string_dynamic buffer, f_string_location *location, const int8_t placeholder);
+#endif // _di_fl_string_dynamic_seek_line_until_non_graph_
 
 /**
  * Seek the buffer location forward until the character (1-byte wide) is reached.
@@ -245,11 +384,13 @@ extern "C" {
  *   f_incomplete_utf_on_eos (with error bit) if end of string is reached before a complete UTF-8 character can be processed.
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *
+ * @see fl_string_dynamic_seek_to_utf_character()
+ * @see fl_string_seek_to()
  * @see fl_string_seek_to_utf_character()
  */
-#ifndef _di_fl_string_seek_to_
-  extern f_return_status fl_string_seek_to(const f_string_dynamic buffer, f_string_location *location, const int8_t seek_to_this);
-#endif // _di_fl_string_seek_to_
+#ifndef _di_fl_string_dynamic_seek_to_
+  extern f_return_status fl_string_dynamic_seek_to(const f_string_dynamic buffer, f_string_location *location, const int8_t seek_to_this);
+#endif // _di_fl_string_dynamic_seek_to_
 
 /**
  * Seek the buffer location forward until the UTF-8 character (up to 4-byte wide) is reached.
@@ -272,10 +413,226 @@ extern "C" {
  *   f_incomplete_utf_on_eos (with error bit) if end of string is reached before a complete UTF-8 character can be processed.
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *
+ * @see fl_string_dynamic_seek_to()
+ * @see fl_string_seek_to()
+ * @see fl_string_seek_to_character()
+ */
+#ifndef _di_fl_string_dynamic_seek_to_utf_character_
+  extern f_return_status fl_string_dynamic_seek_to_utf_character(const f_string_dynamic buffer, f_string_location *location, const f_utf_character seek_to_this);
+#endif // _di_fl_string_dynamic_seek_to_utf_character_
+
+/**
+ * Allocate a new string from the provided range in the string.
+ *
+ * @param string
+ *   The string to rip from.
+ * @param start
+ *   An inclusive start location within string.
+ * @param stop
+ *   An inclusive stop location within string.
+ * @param result
+ *   The new string, which will be allocated or reallocated as necessary.
+ *
+ * @return
+ *   f_none on success.
+ *   f_no_data if nothing to rip, no allocations or reallocations are performed.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_error_allocation (with error bit) on memory allocation error.
+ *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see fl_string_dynamic_rip()
+ * @see fl_string_dynamic_rip_trim()
+ * @see fl_string_rip_trim()
+ */
+#ifndef _di_fl_string_rip_
+  extern f_return_status fl_string_rip(const f_string string, const f_string_length start, const f_string_length stop, f_string_dynamic *result);
+#endif // _di_fl_string_rip_
+
+/**
+ * Allocate a new string from the provided range in the string.
+ *
+ * Ignores leading and trailing whitespace.
+ * As a result, resulting size may be smaller than requested range.
+ *
+ * @param string
+ *   The string to rip from.
+ * @param start
+ *   An inclusive start location within string.
+ * @param stop
+ *   An inclusive stop location within string.
+ * @param result
+ *   The new string, which will be allocated or reallocated as necessary.
+ *
+ * @return
+ *   f_none on success.
+ *   f_no_data if nothing to rip, no allocations or reallocations are performed.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_error_allocation (with error bit) on memory allocation error.
+ *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see fl_string_dynamic_rip()
+ * @see fl_string_rip()
+ * @see fl_string_rip_trim()
+ */
+#ifndef _di_fl_string_rip_trim_
+  extern f_return_status fl_string_rip_trim(const f_string string, const f_string_length start, const f_string_length stop, f_string_dynamic *result);
+#endif // _di_fl_string_rip_trim_
+
+/**
+ * Seek the string location forward until the character (1-byte wide) or EOL is reached.
+ *
+ * @param string
+ *   The string to traverse.
+ * @param location
+ *   A range within the buffer representing the start and stop locations.
+ *   The start location will be incremented by seek.
+ * @param seek_to_this
+ *   A single-width character representing a character to seek to.
+ *
+ * @return
+ *   f_none on success.
+ *   f_none_on_eol on success, but stopped at EOL.
+ *   f_none_on_stop on success, but stopped stop location.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_string_dynamic_seek_line_to()
+ * @see fl_string_dynamic_seek_line_to_utf_character()
+ * @see fl_string_seek_line_to_utf_character()
+ */
+#ifndef _di_fl_string_seek_line_to_
+  extern f_return_status fl_string_seek_line_to(const f_string string, f_string_location *location, const int8_t seek_to_this);
+#endif // _di_fl_string_seek_line_to_
+
+/**
+ * Seek the string location forward until the character (up to 4-byte wide) or EOL is reached.
+ *
+ * @param string
+ *   The string to traverse.
+ * @param location
+ *   A range within the buffer representing the start and stop locations.
+ *   The start location will be incremented by seek.
+ * @param seek_to_this
+ *   A 1-width, 2-width, 3-width, or 4-width character representing a character to seek to.
+ *
+ * @return
+ *   f_none on success.
+ *   f_none_on_eol on success, but stopped at EOL.
+ *   f_invalid_utf (with error bit) if character is an invalid UTF-8 character.
+ *   f_incomplete_utf (with error bit) if character is an incomplete UTF-8 fragment.
+ *   f_incomplete_utf_on_stop (with error bit) if the stop location is reached before the complete UTF-8 character can be processed.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_string_dynamic_seek_line_to()
+ * @see fl_string_seek_line_to()
+ */
+#ifndef _di_fl_string_seek_line_to_utf_character_
+  extern f_return_status fl_string_seek_line_to_utf_character(const f_string string, f_string_location *location, const f_utf_character seek_to_this);
+#endif // _di_fl_string_seek_line_to_utf_character_
+
+/**
+ * Increment string location until a graph character (including UTF-8) or an EOL is matched.
+ *
+ * @param string
+ *   The string to traverse.
+ * @param location
+ *   A range within the buffer representing the start and stop locations.
+ *   The start location will be incremented by seek.
+ * @param placeholder
+ *   A single-width character representing a placeholder to ignore (may be NULL).
+ *
+ * @return
+ *   f_none on success.
+ *   f_none_on_eol on success, but stopped at EOL.
+ *   f_incomplete_utf (with error bit) if character is an incomplete UTF-8 fragment.
+ *   f_incomplete_utf_on_stop (with error bit) if the stop location is reached before the complete UTF-8 character can be processed.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_error_allocation (with error bit) on memory allocation error.
+ *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see fl_string_dynamic_seek_line_until_graph()
+ */
+#ifndef _di_fl_string_seek_line_until_graph_
+  extern f_return_status fl_string_seek_line_until_graph(const f_string string, f_string_location *location, const int8_t placeholder);
+#endif // _di_fl_string_seek_line_until_graph_
+
+/**
+ * Increment string location until a non-graph character (including UTF-8) or an EOL is matched.
+ *
+ * @param string
+ *   The string to traverse.
+ * @param location
+ *   A range within the buffer representing the start and stop locations.
+ *   The start location will be incremented by seek.
+ * @param placeholder
+ *   A single-width character representing a placeholder to ignore (may be NULL).
+ *
+ * @return
+ *   f_none on success.
+ *   f_none_on_eol on success, but stopped at EOL.
+ *   f_none_on_stop on success, but stopped stop location.
+ *   f_incomplete_utf (with error bit) if character is an incomplete UTF-8 fragment.
+ *   f_incomplete_utf_on_stop (with error bit) if the stop location is reached before the complete UTF-8 character can be processed.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_error_allocation (with error bit) on memory allocation error.
+ *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see fl_string_dynamic_seek_line_until_non_graph()
+ */
+#ifndef _di_fl_string_seek_line_until_non_graph_
+  extern f_return_status fl_string_seek_line_until_non_graph(const f_string string, f_string_location *location, const int8_t placeholder);
+#endif // _di_fl_string_seek_line_until_non_graph_
+
+/**
+ * Seek the string location forward until the character (1-byte wide) is reached.
+ *
+ * @param string
+ *   The string to traverse.
+ * @param location
+ *   A range within the buffer representing the start and stop locations.
+ *   The start location will be incremented by seek.
+ * @param seek_to_this
+ *   A single-width character representing a character to seek to.
+ *
+ * @return
+ *   f_none on success.
+ *   f_none_on_stop on success, but stopped stop location.
+ *   f_incomplete_utf (with error bit) if character is an incomplete UTF-8 fragment.
+ *   f_incomplete_utf_on_stop (with error bit) if the stop location is reached before the complete UTF-8 character can be processed.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_string_dynamic_seek_to()
+ * @see fl_string_dynamic_seek_to_utf_character()
+ * @see fl_string_seek_to_utf_character()
+ */
+#ifndef _di_fl_string_seek_to_
+  extern f_return_status fl_string_seek_to(const f_string string, f_string_location *location, const int8_t seek_to_this);
+#endif // _di_fl_string_seek_to_
+
+/**
+ * Seek the string location forward until the UTF-8 character (up to 4-byte wide) is reached.
+ *
+ * @param string
+ *   The string to traverse.
+ * @param location
+ *   A range within the buffer representing the start and stop locations.
+ *   The start location will be incremented by seek.
+ * @param seek_to_this
+ *   A 1-width, 2-width, 3-width, or 4-width character representing a character to seek to.
+ *
+ * @return
+ *   f_none on success.
+ *   f_none_on_stop on success, but stopped stop location.
+ *   f_invalid_utf (with error bit) if character is an invalid UTF-8 character.
+ *   f_incomplete_utf (with error bit) if character is an incomplete UTF-8 fragment.
+ *   f_incomplete_utf_on_stop (with error bit) if the stop location is reached before the complete UTF-8 character can be processed.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_string_dynamic_seek_to()
+ * @see fl_string_dynamic_seek_to_utf_character()
  * @see fl_string_seek_to()
  */
 #ifndef _di_fl_string_seek_to_utf_character_
-  extern f_return_status fl_string_seek_to_utf_character(const f_string_dynamic buffer, f_string_location *location, const f_utf_character seek_to_this);
+  extern f_return_status fl_string_seek_to_utf_character(const f_string string, f_string_location *location, const f_utf_character seek_to_this);
 #endif // _di_fl_string_seek_to_utf_character_
 
 #ifdef __cplusplus
index 55b4150fd20578c7f3226fca28aeaf7b44bc3cbf..7c9a377afa96c6491833d52bd3ea0b3af60d1b66 100644 (file)
@@ -11,7 +11,7 @@ build_compiler gcc
 build_linker ar
 build_libraries -lc
 build_libraries_fll -lf_utf -lf_memory
-build_sources_library string.c
+build_sources_library string.c private-string.c
 build_sources_program
 build_sources_headers string.h
 build_sources_bash
diff --git a/level_1/fl_utf/c/private-utf.c b/level_1/fl_utf/c/private-utf.c
new file mode 100644 (file)
index 0000000..51779ea
--- /dev/null
@@ -0,0 +1,245 @@
+#include <level_1/utf.h>
+#include "private-utf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined(_di_fl_utf_string_compare_) || !defined(_di_fl_utf_string_dynamic_compare_) || !defined(_di_fl_utf_string_dynamic_partial_compare_)
+  f_return_status private_fl_utf_string_compare(const f_utf_string string1, const f_utf_string string2, const f_utf_string_length offset1, const f_utf_string_length offset2, const f_utf_string_length stop1, const f_utf_string_length stop2) {
+    f_utf_string_length i1 = offset1;
+    f_utf_string_length i2 = offset2;
+
+    for (; i1 < stop1 && i2 < stop2; i1++, i2++) {
+      // skip past NULL in string1.
+      while (i1 < stop1 && string1[i1] == f_string_eos) i1++;
+      if (i1 == stop1) break;
+
+      // skip past NULL in string2.
+      while (i2 < stop2 && string2[i2] == f_string_eos) i2++;
+      if (i2 == stop2) break;
+
+      if (string1[i1] != string2[i2]) return f_not_equal_to;
+    } // for
+
+    // only return f_equal_to if all remaining characters are NULL.
+    for (; i1 < stop1; i1++) {
+      if (string1[i1] != f_string_eos) return f_not_equal_to;
+    } // for
+
+    for (; i2 < stop2; i2++) {
+      if (string2[i2] != f_string_eos) return f_not_equal_to;
+    } // for
+  }
+#endif // !defined(_di_fl_utf_string_compare_) || !defined(_di_fl_utf_string_dynamic_compare_) || !defined(_di_fl_utf_string_dynamic_partial_compare_)
+
+#if !defined(_di_fl_utf_string_compare_trim_) || !defined(_di_fl_utf_string_dynamic_compare_trim_) || !defined(_di_fl_utf_string_dynamic_partial_compare_trim_)
+  f_return_status private_fl_utf_string_compare_trim(const f_utf_string string1, const f_utf_string string2, const f_utf_string_length offset1, const f_utf_string_length offset2, const f_utf_string_length stop1, const f_utf_string_length stop2) {
+    f_utf_string_length i1 = offset1;
+    f_utf_string_length i2 = offset2;
+
+    f_status status = f_none;
+
+    // skip past leading whitespace in string1.
+    for (; i1 < stop1; i1++) {
+      // skip past NULL in string1.
+      while (i1 < stop1 && string1[i1] == f_string_eos) i1++;
+      if (i1 == stop1) break;
+
+      status = f_utf_character_is_whitespace(string1[i1]);
+      if (f_status_is_error(status)) {
+        // ignore possibly invalid UTF-8 codes.
+        if (f_status_set_fine(status) != f_maybe) {
+          return status;
+        }
+      }
+
+      if (status == f_false) break;
+    } // for
+
+    // skip past leading whitespace in string2.
+    for (; i2 < stop2; i2++) {
+      // skip past NULL in string2.
+      while (i2 < stop2 && string2[i2] == f_string_eos) i2++;
+      if (i2 == stop2) break;
+
+      status = f_utf_character_is_whitespace(string2[i2]);
+      if (f_status_is_error(status)) {
+        // ignore possibly invalid UTF-8 codes.
+        if (f_status_set_fine(status) != f_maybe) {
+          return status;
+        }
+      }
+
+      if (status == f_false) break;
+    } // for
+
+    f_utf_string_length last1 = i1;
+    f_utf_string_length last2 = i2;
+
+    {
+      // size1 and size2 are to represent to total number of characters after trim.
+      f_utf_string_length size1 = 0;
+      f_utf_string_length size2 = 0;
+
+      // determine where the last non-whitespace is in string1.
+      for (f_utf_string_length j = i1; j < stop1; j++) {
+        // skip past NULL in string1.
+        while (j < stop1 && string1[j] == f_string_eos) j++;
+        if (j == stop1) break;
+
+        status = f_utf_character_is_whitespace(string1[j]);
+        if (f_status_is_error(status)) {
+          // ignore possibly invalid UTF-8 codes.
+          if (f_status_set_fine(status) != f_maybe) {
+            return status;
+          }
+        }
+
+        if (status == f_false) {
+          last1 = j;
+          size1++;
+        }
+      } // for
+
+      // determine where the last non-whitespace is in string2.
+      for (f_utf_string_length j = i2; j < stop2; j++) {
+        // skip past NULL in string2.
+        while (j < stop2 && string2[j] == f_string_eos) j++;
+        if (j == stop2) break;
+
+        status = f_utf_character_is_whitespace(string2[j]);
+        if (f_status_is_error(status)) {
+          // ignore possibly invalid UTF-8 codes.
+          if (f_status_set_fine(status) != f_maybe) {
+            return status;
+          }
+        }
+
+        if (status == f_false) {
+          last2 = j;
+          size2++;
+        }
+      } // for
+
+      if (size1 != size2) return f_not_equal_to;
+    }
+
+    for (; i1 < last1 && i2 < last2; i1++, i2++) {
+      // skip past NULL in string1.
+      while (i1 < last1 && string1[i1] == f_string_eos) i1++;
+      if (i1 == last1) break;
+
+      // skip past NULL in string2.
+      while (i2 < last2 && string2[i2] == f_string_eos) i2++;
+      if (i2 == last2) break;
+
+      if (string1[i1] != string2[i2]) return f_not_equal_to;
+    } // for
+
+    // only return f_equal_to if all remaining characters are NULL.
+    for (; i1 < last1; i1++) {
+      if (string1[i1] != f_string_eos) return f_not_equal_to;
+    } // for
+
+    for (; i2 < last2; i2++) {
+      if (string2[i2] != f_string_eos) return f_not_equal_to;
+    } // for
+
+    return f_equal_to;
+  }
+#endif // !defined(_di_fl_utf_string_compare_trim_) || !defined(_di_fl_utf_string_dynamic_compare_trim_) || !defined(_di_fl_utf_string_dynamic_partial_compare_trim_)
+
+#if !defined(_di_fl_utf_string_rip_) || !defined(_di_fl_utf_string_dynamic_rip_)
+  f_return_status private_fl_utf_string_rip(const f_utf_string string, const f_utf_string_length start, const f_utf_string_length stop, f_utf_string_dynamic *result) {
+    // The start and stop point are inclusive locations, and therefore start - stop is actually 1 too few locations.
+    f_utf_string_length size = (stop - start) + 1;
+
+    if (size == 0) return f_no_data;
+
+    f_status status = f_none;
+
+    if (result == 0) {
+      f_macro_string_dynamic_new(status, (*result), size);
+    }
+    else {
+      f_macro_string_dynamic_resize(status, (*result), size);
+    }
+
+    if (f_status_is_error(status)) {
+      return status;
+    }
+
+    memcpy(result->string, string + start, size);
+    result->used = size;
+
+    return f_none;
+  }
+#endif // !defined(_di_fl_utf_string_rip_) || !defined(_di_fl_utf_string_dynamic_rip_)
+
+#if !defined(_di_fl_utf_string_rip_trim_) || !defined(_di_fl_utf_string_dynamic_rip_trim_)
+  f_return_status private_fl_utf_string_rip_trim(const f_utf_string string, const f_utf_string_length start, const f_utf_string_length stop, f_utf_string_dynamic *result) {
+    // The start and stop point are inclusive locations, and therefore start - stop is actually 1 too few locations.
+    f_utf_string_length size = (stop - start) + 1;
+
+    if (size == 0) return f_no_data;
+
+    f_status status = f_none;
+
+    if (result == 0) {
+      f_macro_string_dynamic_new(status, (*result), size);
+    }
+    else {
+      f_macro_string_dynamic_resize(status, (*result), size);
+    }
+
+    if (f_status_is_error(status)) {
+      return status;
+    }
+
+    f_utf_string_length begin = start;
+    f_utf_string_length end = stop;
+
+    // skip past leading whitespace.
+    for (; begin <= end; begin++) {
+      // skip past NULL.
+      while (begin < size && string[begin] == f_string_eos) begin++;
+      if (begin > end) break;
+
+      status = f_utf_character_is_whitespace(string[begin]);
+      if (f_status_is_error(status)) {
+        // ignore possibly invalid UTF-8 codes.
+        if (f_status_set_fine(status) != f_maybe) {
+          return status;
+        }
+      }
+
+      if (status == f_false) break;
+    } // for
+
+    for (; end > begin; end--) {
+      // skip past NULL.
+      if (string[end] == f_string_eos) continue;
+      if (end == begin) break;
+
+      status = f_utf_character_is_whitespace(string[end]);
+      if (f_status_is_error(status)) {
+        // ignore possibly invalid UTF-8 codes.
+        if (f_status_set_fine(status) != f_maybe) {
+          return status;
+        }
+      }
+
+      if (status == f_false) break;
+    } // for
+
+    memcpy(result->string, string + begin, (end - begin) + 1);
+    result->used = (end - begin) + 1;
+
+    return f_none;
+  }
+#endif // !defined(_di_fl_utf_string_rip_trim_) || !defined(_di_fl_utf_string_dynamic_rip_trim_)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_1/fl_utf/c/private-utf.h b/level_1/fl_utf/c/private-utf.h
new file mode 100644 (file)
index 0000000..d6ce958
--- /dev/null
@@ -0,0 +1,142 @@
+/**
+ * FLL - Level 1
+ *
+ * Project: Utf
+ * API Version: 0.5
+ * Licenses: lgplv2.1
+ *
+ * Provides UTF-8 character manipulation and processing capabilities.
+ *
+ * These are provided for internal reduction in redundant code.
+ * These should not be exposed/used outside of this project.
+ */
+#ifndef _PRIVATE_FL_utf_h
+#define _PRIVATE_FL_utf_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Private implementation of fl_utf_string_compare().
+ *
+ * Intended to be shared to each of the different implementation variations.
+ *
+ * @param string1
+ *   String to compare.
+ * @param string2
+ *   String to compare.
+ * @param offset1
+ *   Offset of string1 to start at.
+ * @param offset2
+ *   Offset of string2 to start at.
+ * @param stop1
+ *   Exclusive stop position for string1.
+ * @param stop2
+ *   Exclusive stop position for string2.
+ *
+ * @return
+ *   f_equal_to when both strings equal.
+ *   f_not_equal_to when both strings do not equal.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_utf_string_compare()
+ * @see fl_utf_string_dynamic_compare()
+ * @see fl_utf_string_dynamic_partial_compare()
+ */
+#if !defined(_di_fl_utf_string_compare_) || !defined(_di_fl_utf_string_dynamic_compare_) || !defined(_di_fl_utf_string_dynamic_partial_compare_)
+  extern f_return_status private_fl_utf_string_compare(const f_utf_string string1, const f_utf_string string2, const f_utf_string_length offset1, const f_utf_string_length offset2, const f_utf_string_length stop1, const f_utf_string_length stop2) f_gcc_attribute_visibility_internal;
+#endif // !defined(_di_fl_utf_string_compare_) || !defined(_di_fl_utf_string_dynamic_compare_) || !defined(_di_fl_utf_string_dynamic_partial_compare_)
+
+/**
+ * Private implementation of fl_utf_string_compare_trim().
+ *
+ * Intended to be shared to each of the different implementation variations.
+ *
+ * @param string1
+ *   String to compare.
+ * @param string2
+ *   String to compare.
+ * @param offset1
+ *   Offset of string1 to start at.
+ * @param offset2
+ *   Offset of string2 to start at.
+ * @param stop1
+ *   Exclusive stop position for string1.
+ * @param stop2
+ *   Exclusive stop position for string2.
+ *
+ * @return
+ *   f_equal_to when both strings equal.
+ *   f_not_equal_to when both strings do not equal.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_utf_string_compare_trim()
+ * @see fl_utf_string_dynamic_compare_trim()
+ * @see fl_utf_string_dynamic_partial_compare_trim()
+ */
+#if !defined(_di_fl_utf_string_compare_trim_) || !defined(_di_fl_utf_string_dynamic_compare_trim_) || !defined(_di_fl_utf_string_dynamic_partial_compare_trim_)
+  extern f_return_status private_fl_utf_string_compare_trim(const f_utf_string string1, const f_utf_string string2, const f_utf_string_length offset1, const f_utf_string_length offset2, const f_utf_string_length stop1, const f_utf_string_length stop2) f_gcc_attribute_visibility_internal;
+#endif // !defined(_di_fl_utf_string_compare_trim_) || !defined(_di_fl_utf_string_dynamic_compare_trim_) || !defined(_di_fl_utf_string_dynamic_partial_compare_trim_)
+
+/**
+ * Private implementation of fl_utf_string_rip().
+ *
+ * Intended to be shared to each of the different implementation variations.
+ *
+ * @param string
+ *   The string to rip from.
+ * @param start
+ *   Inclusive start point of string to rip.
+ * @param stop
+ *   Inclusive stop point of string to rip.
+ * @param result
+ *   The new string, which will be allocated or reallocated as necessary.
+ *
+ * @return
+ *   f_none on success.
+ *   f_no_data if nothing to rip, no allocations or reallocations are performed.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_error_allocation (with error bit) on memory allocation error.
+ *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see fl_utf_string_dynamic_rip()
+ * @see fl_utf_string_rip()
+ */
+#if !defined(_di_fl_utf_string_rip_) || !defined(_di_fl_utf_string_dynamic_rip_)
+  extern f_return_status private_fl_utf_string_rip(const f_utf_string string, const f_utf_string_length start, const f_utf_string_length stop, f_utf_string_dynamic *result) f_gcc_attribute_visibility_internal;
+#endif // !defined(_di_fl_utf_string_rip_) || !defined(_di_fl_utf_string_dynamic_rip_)
+
+/**
+ * Private implementation of fl_string_rip_trim().
+ *
+ * Intended to be shared to each of the different implementation variations.
+ *
+ * @param string
+ *   The string to rip from.
+ * @param start
+ *   Inclusive start point of string to rip.
+ * @param stop
+ *   Inclusive stop point of string to rip.
+ * @param result
+ *   The new string, which will be allocated or reallocated as necessary.
+ *
+ * @return
+ *   f_none on success.
+ *   f_no_data if nothing to rip, no allocations or reallocations are performed.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_error_allocation (with error bit) on memory allocation error.
+ *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see fl_utf_string_dynamic_rip()
+ * @see fl_utf_string_rip()
+ */
+#if !defined(_di_fl_utf_string_rip_trim_) || !defined(_di_fl_utf_string_dynamic_rip_trim_)
+  extern f_return_status private_fl_utf_string_rip_trim(const f_utf_string string, const f_utf_string_length start, const f_utf_string_length stop, f_utf_string_dynamic *result) f_gcc_attribute_visibility_internal;
+#endif // !defined(_di_fl_utf_string_rip_trim_) || !defined(_di_fl_utf_string_dynamic_rip_trim_)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _PRIVATE_FL_utf_h
index b92cbdf4309fe495d2eb3013ece8da57cb13d622..e1ac61e2b702470dcb685acd11c9699d14be3a74 100644 (file)
 #include <level_1/utf.h>
+#include "private-utf.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-#ifndef _di_fl_utf_seek_line_until_graph_
-  f_return_status fl_utf_seek_line_until_graph(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character placeholder) {
+#ifndef _di_fl_utf_string_compare_
+  f_return_status fl_utf_string_compare(const f_utf_string string1, const f_utf_string string2, const f_utf_string_length length1, const f_utf_string_length length2) {
+    #ifndef _di_level_1_parameter_checking_
+      if (length1 <= 0) return f_status_set_error(f_invalid_parameter);
+      if (length2 <= 0) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    return private_fl_utf_string_compare(string1, string2, 0, 0, length1, length2);
+  }
+#endif // _di_fl_utf_string_compare_
+
+#ifndef _di_fl_utf_string_compare_trim_
+  f_return_status fl_utf_string_compare_trim(const f_utf_string string1, const f_utf_string string2, const f_utf_string_length length1, const f_utf_string_length length2) {
+    #ifndef _di_level_1_parameter_checking_
+      if (length1 <= 0) return f_status_set_error(f_invalid_parameter);
+      if (length2 <= 0) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    return private_fl_utf_string_compare_trim(string1, string2, 0, 0, length1, length2);
+  }
+#endif // _di_fl_utf_string_compare_trim_
+
+#ifndef _di_fl_utf_string_dynamic_compare_
+  f_return_status fl_utf_string_dynamic_compare(const f_utf_string_dynamic string1, const f_utf_string_dynamic string2) {
+    #ifndef _di_level_1_parameter_checking_
+      if (string1.used <= 0) return f_status_set_error(f_invalid_parameter);
+      if (string2.used <= 0) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    return private_fl_utf_string_compare(string1.string, string2.string, 0, 0, string1.used, string2.used);
+  }
+#endif // _di_fl_utf_string_dynamic_compare_
+
+#ifndef _di_f_utf_string_dynamic_compare_trim_
+  f_return_status f_utf_string_dynamic_compare_trim(const f_utf_string_dynamic string1, const f_utf_string_dynamic string2) {
+    #ifndef _di_level_1_parameter_checking_
+      if (string1.used <= 0) return f_status_set_error(f_invalid_parameter);
+      if (string2.used <= 0) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    return private_fl_utf_string_compare_trim(string1.string, string2.string, 0, 0, string1.used, string2.used);
+  }
+#endif // _di_f_utf_string_dynamic_compare_trim_
+
+#ifndef _di_fl_string_dynamic_partial_compare_
+  f_return_status fl_utf_string_dynamic_partial_compare(const f_utf_string_dynamic string1, const f_utf_string_dynamic string2, const f_utf_string_location offset1, const f_utf_string_location offset2) {
+    #ifndef _di_level_1_parameter_checking_
+      if (string1.used <= 0) return f_status_set_error(f_invalid_parameter);
+      if (string2.used <= 0) return f_status_set_error(f_invalid_parameter);
+
+      if (offset1.start > offset1.stop) return f_status_set_error(f_invalid_parameter);
+      if (offset2.start > offset2.stop) return f_status_set_error(f_invalid_parameter);
+
+      if (string1.used <= offset1.stop) return f_status_set_error(f_invalid_parameter);
+      if (string2.used <= offset2.stop) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    return private_fl_utf_string_compare(string1.string, string2.string, offset1.start, offset2.start, offset1.stop + 1, offset2.stop + 1);
+  }
+#endif // _di_fl_string_dynamic_partial_compare_
+
+#ifndef _di_fl_string_dynamic_partial_compare_trim_
+  f_return_status fl_utf_string_dynamic_partial_comparetrim(const f_utf_string_dynamic string1, const f_utf_string_dynamic string2, const f_utf_string_location offset1, const f_utf_string_location offset2) {
+    #ifndef _di_level_1_parameter_checking_
+      if (string1.used <= 0) return f_status_set_error(f_invalid_parameter);
+      if (string2.used <= 0) return f_status_set_error(f_invalid_parameter);
+
+      if (offset1.start > offset1.stop) return f_status_set_error(f_invalid_parameter);
+      if (offset2.start > offset2.stop) return f_status_set_error(f_invalid_parameter);
+
+      if (string1.used <= offset1.stop) return f_status_set_error(f_invalid_parameter);
+      if (string2.used <= offset2.stop) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    return private_fl_utf_string_compare_trim(string1.string, string2.string, offset1.start, offset2.start, offset1.stop + 1, offset2.stop + 1);
+  }
+#endif // _di_fl_string_dynamic_partial_comparetrim_
+
+#ifndef _di_fl_utf_string_dynamic_rip_
+  f_return_status fl_utf_string_dynamic_rip(const f_utf_string_dynamic buffer, const f_utf_string_location location, f_utf_string_dynamic *result) {
+    #ifndef _di_level_1_parameter_checking_
+      if (location.start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location.stop < location.start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location.start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location.stop) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    return private_fl_utf_string_rip(buffer.string, location.start, location.stop, result);
+  }
+#endif // _di_fl_utf_string_dynamic_rip_
+
+#ifndef _di_fl_utf_string_dynamic_rip_trim_
+  f_return_status fl_utf_string_dynamic_rip_trim(const f_utf_string_dynamic buffer, const f_utf_string_location location, f_utf_string_dynamic *result) {
+    #ifndef _di_level_1_parameter_checking_
+      if (location.start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location.stop < location.start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location.start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location.stop) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    return private_fl_utf_string_rip_trim(buffer.string, location.start, location.stop, result);
+  }
+#endif // _di_fl_utf_string_dynamic_rip_trim_
+
+#ifndef _di_fl_utf_string_dynamic_seek_line_to_
+  f_return_status fl_utf_string_dynamic_seek_line_to(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character seek_to_this) {
+    #ifndef _di_level_1_parameter_checking_
+      if (location == 0) return f_status_set_error(f_invalid_parameter);
+      if (location->start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->stop) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    if (f_macro_utf_character_width_is(buffer.string[location->start]) == 1) {
+      return f_status_set_error(f_invalid_utf);
+    }
+
+    while (buffer.string[location->start] != seek_to_this) {
+      if (buffer.string[location->start] == f_utf_character_eol) return f_none_on_eol;
+
+      location->start++;
+
+      if (f_macro_utf_character_width_is(buffer.string[location->start]) == 1) {
+        return f_status_set_error(f_invalid_utf);
+      }
+
+      if (location->start >= buffer.used) return f_none_on_eos;
+      if (location->start > location->stop) return f_none_on_stop;
+    } // while
+
+    return f_none;
+  }
+#endif // _di_fl_utf_string_dynamic_seek_line_to_
+
+#ifndef _di_fl_utf_string_dynamic_seek_line_to_char_
+  f_return_status fl_utf_string_dynamic_seek_line_to_char(const f_utf_string_dynamic buffer, f_utf_string_location *location, const int8_t seek_to_this) {
     #ifndef _di_level_1_parameter_checking_
       if (location == 0) return f_status_set_error(f_invalid_parameter);
       if (location->start < 0) return f_status_set_error(f_invalid_parameter);
       if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
       if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (location->start >= buffer.used) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->stop) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    f_utf_character seek_to_character = seek_to_this << 24;
+
+    if (f_macro_utf_character_width_is(buffer.string[location->start]) == 1) {
+      return f_status_set_error(f_invalid_utf);
+    }
+
+    while (buffer.string[location->start] != seek_to_character) {
+      if (buffer.string[location->start] == f_utf_character_eol) return f_none_on_eol;
+
+      location->start++;
+
+      if (f_macro_utf_character_width_is(buffer.string[location->start]) == 1) {
+        return f_status_set_error(f_invalid_utf);
+      }
+
+      if (location->start >= buffer.used) return f_none_on_eos;
+      if (location->start > location->stop) return f_none_on_stop;
+    } // while
+
+    return f_none;
+  }
+#endif // _di_fl_utf_string_dynamic_seek_line_to_character_
+
+#ifndef _di_fl_utf_string_dynamic_seek_line_until_graph_
+  f_return_status fl_utf_string_dynamic_seek_line_until_graph(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character placeholder) {
+    #ifndef _di_level_1_parameter_checking_
+      if (location == 0) return f_status_set_error(f_invalid_parameter);
+      if (location->start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->stop) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
     f_status status = f_none;
 
+    if (f_macro_utf_character_width_is(buffer.string[location->start]) == 1) {
+      return f_status_set_error(f_invalid_utf);
+    }
+
     while (buffer.string[location->start] == placeholder || (status = f_utf_character_is_graph(buffer.string[location->start])) == f_false) {
       if (f_status_is_error(status)) {
         return status;
@@ -23,12 +201,12 @@ extern "C" {
 
       if (buffer.string[location->start] == f_utf_character_eol) return f_none_on_eol;
 
+      location->start++;
+
       if (f_macro_utf_character_width_is(buffer.string[location->start]) == 1) {
         return f_status_set_error(f_invalid_utf);
       }
 
-      location->start++;
-
       if (location->start >= buffer.used) return f_none_on_eos;
       if (location->start > location->stop) return f_none_on_stop;
     } // while
@@ -39,20 +217,25 @@ extern "C" {
 
     return f_none;
   }
-#endif // _di_fl_utf_seek_line_until_graph_
+#endif // _di_fl_utf_string_dynamic_seek_line_until_graph_
 
-#ifndef _di_fl_utf_seek_line_until_non_graph_
-  f_return_status fl_utf_seek_line_until_non_graph(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character placeholder) {
+#ifndef _di_fl_utf_string_dynamic_seek_line_until_non_graph_
+  f_return_status fl_utf_string_dynamic_seek_line_until_non_graph(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character placeholder) {
     #ifndef _di_level_1_parameter_checking_
       if (location == 0) return f_status_set_error(f_invalid_parameter);
       if (location->start < 0) return f_status_set_error(f_invalid_parameter);
       if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
       if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (location->start >= buffer.used) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->stop) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
     f_status status = f_none;
 
+    if (f_macro_utf_character_width_is(buffer.string[location->start]) == 1) {
+      return f_status_set_error(f_invalid_utf);
+    }
+
     while (buffer.string[location->start] == placeholder || (status = f_utf_character_is_whitespace(buffer.string[location->start])) == f_false) {
       if (f_status_is_error(status)) {
         return status;
@@ -60,12 +243,12 @@ extern "C" {
 
       if (buffer.string[location->start] == f_utf_character_eol) return f_none_on_eol;
 
+      location->start++;
+
       if (f_macro_utf_character_width_is(buffer.string[location->start]) == 1) {
         return f_status_set_error(f_invalid_utf);
       }
 
-      location->start++;
-
       if (location->start >= buffer.used) return f_none_on_eos;
       if (location->start > location->stop) return f_none_on_stop;
     } // while
@@ -76,271 +259,274 @@ extern "C" {
 
     return f_none;
   }
-#endif // _di_fl_utf_seek_line_until_non_graph_
+#endif // _di_fl_utf_string_dynamic_seek_line_until_non_graph_
 
-#ifndef _di_fl_utf_seek_line_to_
-  f_return_status fl_utf_seek_line_to(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character seek_to_this) {
+#ifndef _di_fl_utf_string_dynamic_seek_to_
+  f_return_status fl_utf_string_dynamic_seek_to(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character seek_to_this) {
     #ifndef _di_level_1_parameter_checking_
       if (location == 0) return f_status_set_error(f_invalid_parameter);
       if (location->start < 0) return f_status_set_error(f_invalid_parameter);
       if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
       if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (location->start >= buffer.used) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->stop) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
+    if (f_macro_utf_character_width_is(buffer.string[location->start]) == 1) {
+      return f_status_set_error(f_invalid_utf);
+    }
+
     while (buffer.string[location->start] != seek_to_this) {
-      if (buffer.string[location->start] == f_utf_character_eol) return f_none_on_eol;
+      location->start++;
 
       if (f_macro_utf_character_width_is(buffer.string[location->start]) == 1) {
         return f_status_set_error(f_invalid_utf);
       }
 
-      location->start++;
-
       if (location->start >= buffer.used) return f_none_on_eos;
       if (location->start > location->stop) return f_none_on_stop;
     } // while
 
     return f_none;
   }
-#endif // _di_fl_utf_seek_line_to_
+#endif // _di_fl_utf_string_dynamic_seek_to_
 
-#ifndef _di_fl_utf_seek_line_to_char_
-  f_return_status fl_utf_seek_line_to_char(const f_utf_string_dynamic buffer, f_utf_string_location *location, const int8_t seek_to_this) {
+#ifndef _di_fl_utf_string_dynamic_seek_to_char_
+  f_return_status fl_utf_string_dynamic_seek_to_char(const f_utf_string_dynamic buffer, f_utf_string_location *location, const int8_t seek_to_this) {
     #ifndef _di_level_1_parameter_checking_
       if (location == 0) return f_status_set_error(f_invalid_parameter);
       if (location->start < 0) return f_status_set_error(f_invalid_parameter);
       if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
       if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (location->start >= buffer.used) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->start) return f_status_set_error(f_invalid_parameter);
+      if (buffer.used <= location->stop) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
     f_utf_character seek_to_character = seek_to_this << 24;
 
+    if (f_macro_utf_character_width_is(buffer.string[location->start]) == 1) {
+      return f_status_set_error(f_invalid_utf);
+    }
+
     while (buffer.string[location->start] != seek_to_character) {
-      if (buffer.string[location->start] == f_utf_character_eol) return f_none_on_eol;
+      location->start++;
 
       if (f_macro_utf_character_width_is(buffer.string[location->start]) == 1) {
         return f_status_set_error(f_invalid_utf);
       }
 
-      location->start++;
-
       if (location->start >= buffer.used) return f_none_on_eos;
       if (location->start > location->stop) return f_none_on_stop;
     } // while
 
     return f_none;
   }
-#endif // _di_fl_utf_seek_line_to_character_
+#endif // _di_fl_utf_string_dynamic_seek_to_char_
 
 #ifndef _di_fl_utf_string_rip_
-  f_return_status fl_utf_string_rip(const f_utf_string_dynamic buffer, const f_utf_string_location location, f_utf_string_dynamic *result) {
+  f_return_status fl_utf_string_rip(const f_utf_string string, const f_string_length start, const f_string_length stop, f_utf_string_dynamic *result) {
     #ifndef _di_level_1_parameter_checking_
-      if (location.start < 0) return f_status_set_error(f_invalid_parameter);
-      if (location.stop < location.start) return f_status_set_error(f_invalid_parameter);
-      if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (location.start >= buffer.used) return f_status_set_error(f_invalid_parameter);
+      if (start > stop) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
-    // the start and stop point are inclusive locations, and therefore start - stop is actually 1 too few locations
-    f_utf_string_length size = (location.stop - location.start) + 1;
-
-    if (size > 0) {
-      f_status status = f_none;
-
-      if (result == 0) {
-        f_macro_utf_string_dynamic_new(status, (*result), size);
-      }
-      else {
-        f_macro_utf_string_dynamic_resize(status, (*result), size);
-      }
-
-      if (f_status_is_error(status)) {
-        return status;
-      }
-
-      memcpy(result->string, buffer.string + location.start, sizeof(f_utf_character) * size);
-      result->used = size;
+    return private_fl_utf_string_rip(string, start, stop, result);
+  }
+#endif // _di_fl_utf_string_rip_
 
-      return f_none;
-    }
+#ifndef _di_fl_utf_string_rip_trim_
+  f_return_status fl_utf_string_rip_trim(const f_utf_string string, const f_string_length start, const f_string_length stop, f_utf_string_dynamic *result) {
+    #ifndef _di_level_1_parameter_checking_
+      if (start > stop) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
 
-    return f_no_data;
+    return private_fl_utf_string_rip_trim(string, start, stop, result);
   }
-#endif // _di_fl_utf_string_rip_
+#endif // _di_fl_utf_string_rip_trim_
 
-#ifndef _di_fl_utf_string_seek_to_
-  f_return_status fl_utf_string_seek_to(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character seek_to_this) {
+#ifndef _di_fl_utf_string_seek_line_to_
+  f_return_status fl_utf_string_seek_line_to(const f_utf_string string, f_utf_string_location *location, const f_utf_character seek_to_this) {
     #ifndef _di_level_1_parameter_checking_
       if (location == 0) return f_status_set_error(f_invalid_parameter);
       if (location->start < 0) return f_status_set_error(f_invalid_parameter);
       if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
-      if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (location->start >= buffer.used) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
-    while (buffer.string[location->start] != seek_to_this) {
-      if (f_macro_utf_character_width_is(buffer.string[location->start]) == 1) {
-        return f_status_set_error(f_invalid_utf);
-      }
+    if (f_macro_utf_character_width_is(string[location->start]) == 1) {
+      return f_status_set_error(f_invalid_utf);
+    }
+
+    while (string[location->start] != seek_to_this) {
+      if (string[location->start] == f_utf_character_eol) return f_none_on_eol;
 
       location->start++;
 
-      if (location->start >= buffer.used) return f_none_on_eos;
+      if (f_macro_utf_character_width_is(string[location->start]) == 1) {
+        return f_status_set_error(f_invalid_utf);
+      }
+
       if (location->start > location->stop) return f_none_on_stop;
     } // while
 
     return f_none;
   }
-#endif // _di_fl_utf_string_seek_to_
+#endif // _di_fl_utf_string_seek_line_to_
 
-#ifndef _di_fl_utf_string_seek_to_char_
-  f_return_status fl_utf_string_seek_to_char(const f_utf_string_dynamic buffer, f_utf_string_location *location, const int8_t seek_to_this) {
+#ifndef _di_fl_utf_string_seek_line_to_char_
+  f_return_status fl_utf_string_seek_line_to_char(const f_utf_string string, f_utf_string_location *location, const int8_t seek_to_this) {
     #ifndef _di_level_1_parameter_checking_
       if (location == 0) return f_status_set_error(f_invalid_parameter);
       if (location->start < 0) return f_status_set_error(f_invalid_parameter);
       if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
-      if (buffer.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (location->start >= buffer.used) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
     f_utf_character seek_to_character = seek_to_this << 24;
 
-    while (buffer.string[location->start] != seek_to_character) {
-      if (f_macro_utf_character_width_is(buffer.string[location->start]) == 1) {
+    f_status status = f_none;
+
+    for (; location->start <= location->stop; location->start++) {
+      if (f_macro_utf_character_width_is(string[location->start]) == 1) {
         return f_status_set_error(f_invalid_utf);
       }
 
+      if (string[location->start] == f_utf_character_eol) return f_none_on_eol;
+      if (string[location->start] == seek_to_character) return f_none;
+    } // for
+
+    return f_none_on_stop;
+  }
+#endif // _di_fl_utf_string_seek_line_to_char_
+
+#ifndef _di_fl_utf_string_seek_line_until_graph_
+  f_return_status fl_utf_string_seek_line_until_graph(const f_utf_string string, f_utf_string_location *location, const f_utf_character placeholder) {
+    #ifndef _di_level_1_parameter_checking_
+      if (location == 0) return f_status_set_error(f_invalid_parameter);
+      if (location->start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
+    #endif // _di_level_1_parameter_checking_
+
+    f_status status = f_none;
+
+    if (f_macro_utf_character_width_is(string[location->start]) == 1) {
+      return f_status_set_error(f_invalid_utf);
+    }
+
+    while (string[location->start] == placeholder || (status = f_utf_character_is_graph(string[location->start])) == f_false) {
+      if (f_status_is_error(status)) {
+        return status;
+      }
+
+      if (string[location->start] == f_utf_character_eol) return f_none_on_eol;
+
       location->start++;
 
-      if (location->start >= buffer.used) return f_none_on_eos;
+      if (f_macro_utf_character_width_is(string[location->start]) == 1) {
+        return f_status_set_error(f_invalid_utf);
+      }
+
       if (location->start > location->stop) return f_none_on_stop;
     } // while
 
+    if (f_status_is_error(status)) {
+      return status;
+    }
+
     return f_none;
   }
-#endif // _di_fl_utf_string_seek_to_char_
+#endif // _di_fl_utf_string_seek_line_until_graph_
 
-#ifndef _di_fl_utf_string_compare_
-  f_return_status fl_utf_string_compare(const f_utf_string string1, const f_utf_string string2, const f_utf_string_length length1, const f_utf_string_length length2) {
+#ifndef _di_fl_utf_string_seek_line_until_non_graph_
+  f_return_status fl_utf_string_seek_line_until_non_graph(const f_utf_string string, f_utf_string_location *location, const f_utf_character placeholder) {
     #ifndef _di_level_1_parameter_checking_
-      if (length1 <= 0) return f_status_set_error(f_invalid_parameter);
-      if (length2 <= 0) return f_status_set_error(f_invalid_parameter);
+      if (location == 0) return f_status_set_error(f_invalid_parameter);
+      if (location->start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
-    f_utf_string_length i1 = 0;
-    f_utf_string_length i2 = 0;
+    f_status status = f_none;
 
-    for (; i1 < length1 && i2 < length2; i1++, i2++) {
-      // skip past newlines in string1.
-      while (i1 < length1 && string1[i1] == f_utf_character_eos) i1++;
-      if (i1 == length1) break;
+    if (f_macro_utf_character_width_is(string[location->start]) == 1) {
+      return f_status_set_error(f_invalid_utf);
+    }
 
-      // skip past newlines in string2.
-      while (i2 < length2 && string2[i2] == f_utf_character_eos) i2++;
-      if (i2 == length2) break;
+    while (string[location->start] == placeholder || (status = f_utf_character_is_whitespace(string[location->start])) == f_false) {
+      if (f_status_is_error(status)) {
+        return status;
+      }
 
-      if (string1[i1] != string2[i2]) return f_not_equal_to;
-    } // for
+      if (string[location->start] == f_utf_character_eol) return f_none_on_eol;
 
-    // only return f_equal_to if all remaining characters are NULL.
-    while (i1 < length1) {
-      if (string1[i1] != f_utf_character_eos) return f_not_equal_to;
-      i1++;
-    } // while
+      location->start++;
 
-    while (i2 < length2) {
-      if (string2[i2] != f_utf_character_eos) return f_not_equal_to;
-      i2++;
+      if (f_macro_utf_character_width_is(string[location->start]) == 1) {
+        return f_status_set_error(f_invalid_utf);
+      }
+
+      if (location->start > location->stop) return f_none_on_stop;
     } // while
 
-    return f_equal_to;
+    if (f_status_is_error(status)) {
+      return status;
+    }
+
+    return f_none;
   }
-#endif // _di_fl_utf_string_compare_
+#endif // _di_fl_utf_string_seek_line_until_non_graph_
 
-#ifndef _di_fl_utf_string_dynamic_compare_
-  f_return_status fl_utf_string_dynamic_compare(const f_utf_string_dynamic string1, const f_utf_string_dynamic string2) {
+#ifndef _di_fl_utf_string_seek_to_
+  f_return_status fl_utf_string_seek_to(const f_utf_string string, f_utf_string_location *location, const f_utf_character seek_to_this) {
     #ifndef _di_level_1_parameter_checking_
-      if (string1.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (string2.used <= 0) return f_status_set_error(f_invalid_parameter);
+      if (location == 0) return f_status_set_error(f_invalid_parameter);
+      if (location->start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
-    f_utf_string_length i1 = 0;
-    f_utf_string_length i2 = 0;
-
-    for (; i1 < string1.used && i2 < string2.used; i1++, i2++) {
-      // skip past newlines in string1.
-      while (i1 < string1.used && string1.string[i1] == f_utf_character_eos) i1++;
-      if (i1 == string1.used) break;
-
-      // skip past newlines in string2.
-      while (i2 < string2.used && string2.string[i2] == f_utf_character_eos) i2++;
-      if (i2 == string2.used) break;
+    if (f_macro_utf_character_width_is(string[location->start]) == 1) {
+      return f_status_set_error(f_invalid_utf);
+    }
 
-      if (string1.string[i1] != string2.string[i2]) return f_not_equal_to;
-    } // for
+    while (string[location->start] != seek_to_this) {
+      location->start++;
 
-    // only return f_equal_to if all remaining characters are NULL.
-    while (i1 < string1.used) {
-      if (string1.string[i1] != f_utf_character_eos) return f_not_equal_to;
-      i1++;
-    } // while
+      if (f_macro_utf_character_width_is(string[location->start]) == 1) {
+        return f_status_set_error(f_invalid_utf);
+      }
 
-    while (i2 < string2.used) {
-      if (string2.string[i2] != f_utf_character_eos) return f_not_equal_to;
-      i2++;
+      if (location->start > location->stop) return f_none_on_stop;
     } // while
 
-    return f_equal_to;
+    return f_none;
   }
-#endif // _di_fl_utf_string_dynamic_compare_
+#endif // _di_fl_string_seek_to_
 
-#ifndef _di_fl_string_dynamic_partial_compare_
-  f_return_status fl_utf_string_dynamic_partial_compare(const f_utf_string_dynamic string1, const f_utf_string_dynamic string2, const f_utf_string_location offset1, const f_utf_string_location offset2) {
+#ifndef _di_fl_utf_string_seek_to_char_
+  f_return_status fl_utf_string_seek_to_char(const f_utf_string string, f_utf_string_location *location, const int8_t seek_to_this) {
     #ifndef _di_level_1_parameter_checking_
-      if (string1.used <= 0) return f_status_set_error(f_invalid_parameter);
-      if (string2.used <= 0) return f_status_set_error(f_invalid_parameter);
-
-      if (offset1.start > offset1.stop) return f_status_set_error(f_invalid_parameter);
-      if (offset2.start > offset2.stop) return f_status_set_error(f_invalid_parameter);
-
-      if (string1.used <= offset1.stop) return f_status_set_error(f_invalid_parameter);
-      if (string2.used <= offset2.stop) return f_status_set_error(f_invalid_parameter);
+      if (location == 0) return f_status_set_error(f_invalid_parameter);
+      if (location->start < 0) return f_status_set_error(f_invalid_parameter);
+      if (location->stop < location->start) return f_status_set_error(f_invalid_parameter);
     #endif // _di_level_1_parameter_checking_
 
-    f_utf_string_length i1 = offset1.start;
-    f_utf_string_length i2 = offset2.start;
-
-    const f_utf_string_length stop1 = offset1.stop + 1;
-    const f_utf_string_length stop2 = offset2.stop + 1;
+    f_utf_character seek_to_character = seek_to_this << 24;
 
-    for (; i1 < stop1 && i2 < stop2; i1++, i2++) {
-      // skip past newlines in string1.
-      while (i1 < stop1 && string1.string[i1] == f_utf_character_eos) i1++;
-      if (i1 == stop1) break;
+    f_status status = f_none;
 
-      // skip past newlines in string2.
-      while (i2 < stop2 && string2.string[i2] == f_utf_character_eos) i2++;
-      if (i2 == stop2) break;
+    if (f_macro_utf_character_width_is(string[0]) == 1) {
+      return f_status_set_error(f_invalid_utf);
+    }
 
-      if (string1.string[i1] != string2.string[i2]) return f_not_equal_to;
-    } // for
+    while (location->start <= location->stop) {
+      if (string[location->start] == seek_to_character) return f_none;
 
-    // only return f_equal_to if all remaining characters are NULL.
-    while (i1 < stop1) {
-      if (string1.string[i1] != f_utf_character_eos) return f_not_equal_to;
-      i1++;
-    } // while
+      location->start++;
 
-    while (i2 < stop2) {
-      if (string2.string[i2] != f_utf_character_eos) return f_not_equal_to;
-      i2++;
+      if (f_macro_utf_character_width_is(string[location->start]) == 1) {
+        return f_status_set_error(f_invalid_utf);
+      }
     } // while
 
-    return f_equal_to;
+    return f_none_on_stop;
   }
-#endif // _di_fl_string_dynamic_partial_compare_
+#endif // _di_fl_utf_string_seek_to_utf_character_
 
 #ifdef __cplusplus
 } // extern "C"
index 45c3fed453e515502d7024062ef42ffd12517050..ebe5b27c05c7aa1d29f5945777cefc8eacf17049 100644 (file)
@@ -6,6 +6,15 @@
  * Licenses: lgplv2.1
  *
  * Provides UTF-8 character manipulation and processing capabilities.
+ *
+ * UTF-8 is handled within these functions via padded string characters (f_utf_string).
+ * Unlike f_string, these f_utf_string base functions force padding of ASCII or UTF-8 characters.
+ * An ASCII character is 4-byte and a 4-byte wide UTF-8 character is 4-byte.
+ *
+ * This simplifies traversing the strings at the cost of size vs f_string.
+ *
+ * Because of the set width, it is generally safe to ignore invalid UTF-8 sequences.
+ * Invalid UTF-8 will be generally ignored here, where reasonably possible.
  */
 #ifndef _FL_string_h
 #define _FL_string_h
@@ -26,6 +35,287 @@ extern "C" {
 #endif
 
 /**
+ * Compare two UTF-8 strings, similar to strncmp().
+ *
+ * This does not stop on NULL.
+ * NULL characters are ignored.
+ *
+ * @param string1
+ *   String to compare.
+ * @param string2
+ *   String to compare.
+ * @param length1
+ *   Length of string1.
+ * @param length2
+ *   Length of string2.
+ *
+ * @return
+ *   f_equal_to when both strings equal.
+ *   f_not_equal_to when both strings do not equal.
+ *   f_invalid_utf (with error bit) if a character in the string is an invalid UTF-8 character.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_utf_string_dynamic_compare()
+ * @see fl_utf_string_dynamic_compare_trim()
+ * @see fl_utf_string_dynamic_partial_compare()
+ * @see fl_utf_string_dynamic_partial_compare_trim()
+ */
+#ifndef _di_fl_utf_string_compare_
+  extern f_return_status fl_utf_string_compare(const f_utf_string string1, const f_utf_string string2, const f_utf_string_length length1, const f_utf_string_length length2);
+#endif // _di_fl_utf_string_compare_
+
+/**
+ * Compare two UTF-8 strings, similar to strncmp().
+ *
+ * This does not stop on NULL.
+ * NULL characters are ignored.
+ * Ignores leading and trailing whitespace.
+ *
+ * @param string1
+ *   String to compare.
+ * @param string2
+ *   String to compare.
+ * @param length1
+ *   Length of string1.
+ * @param length2
+ *   Length of string2.
+ *
+ * @return
+ *   f_equal_to when both strings equal.
+ *   f_not_equal_to when both strings do not equal.
+ *   f_invalid_utf (with error bit) if a character in the string is an invalid UTF-8 character.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_utf_string_dynamic_compare()
+ * @see fl_utf_string_dynamic_compare_trim()
+ * @see fl_utf_string_dynamic_partial_compare()
+ * @see fl_utf_string_dynamic_partial_compare_trim()
+ */
+#ifndef _di_fl_utf_string_compare_trim_
+  extern f_return_status fl_utf_string_compare_trim(const f_utf_string string1, const f_utf_string string2, const f_utf_string_length length1, const f_utf_string_length length2);
+#endif // _di_fl_utf_string_compare_trim_
+
+/**
+ * Compare two UTF-8 strings, similar to strncmp().
+ *
+ * This does not stop on NULL.
+ * NULL characters are ignored.
+ *
+ * @param string1
+ *   String to compare.
+ * @param string2
+ *   String to compare.
+ *
+ * @return
+ *   f_equal_to when both strings equal.
+ *   f_not_equal_to when both strings do not equal.
+ *   f_invalid_utf (with error bit) if a character in the string is an invalid UTF-8 character.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_utf_string_compare()
+ * @see fl_utf_string_compare_trim()
+ * @see fl_utf_string_dynamic_partial_compare()
+ * @see fl_utf_string_dynamic_partial_compare_trim()
+ */
+#ifndef _di_fl_utf_string_dynamic_compare_
+  extern f_return_status fl_utf_string_dynamic_compare(const f_utf_string_dynamic string1, const f_utf_string_dynamic string2);
+#endif // _di_fl_utf_string_dynamic_compare_
+
+/**
+ * Compare two UTF-8 strings, similar to strncmp().
+ *
+ * This does not stop on NULL.
+ * NULL characters are ignored.
+ * Ignores leading and trailing whitespace.
+ *
+ * @param string1
+ *   String to compare.
+ * @param string2
+ *   String to compare.
+ *
+ * @return
+ *   f_equal_to when both strings equal.
+ *   f_not_equal_to when both strings do not equal.
+ *   f_invalid_utf (with error bit) if a character in the string is an invalid UTF-8 character.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_utf_string_compare()
+ * @see fl_utf_string_compare_trim()
+ * @see fl_utf_string_dynamic_partial_compare()
+ * @see fl_utf_string_dynamic_partial_compare_trim()
+ */
+#ifndef _di_f_utf_string_dynamic_compare_trim_
+  extern f_return_status f_utf_string_dynamic_compare_trim(const f_utf_string_dynamic string1, const f_utf_string_dynamic string2);
+#endif // _di_f_utf_string_dynamic_compare_trim_
+
+/**
+ * Compare two UTF-8 strings, similar to strncmp(), but restricted to the given ranges.
+ *
+ * This does not stop on NULL.
+ * NULL characters are ignored.
+ *
+ * @param string1
+ *   String to compare.
+ * @param string2
+ *   String to compare.
+ * @param offset1
+ *   A range within the string1 to restrict the comparison to.
+ * @param offset2
+ *   A range within the string2 to restrict the comparison to.
+ *
+ * @return
+ *   f_equal_to when both strings equal.
+ *   f_not_equal_to when both strings do not equal.
+ *   f_invalid_utf (with error bit) if a character in the string is an invalid UTF-8 character.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_utf_string_compare()
+ * @see fl_utf_string_compare_trim()
+ * @see fl_utf_string_dynamic_compare()
+ * @see fl_utf_string_dynamic_compare_trim()
+ */
+#ifndef _di_fl_utf_string_dynamic_partial_compare_
+  extern f_return_status fl_utf_string_dynamic_partial_compare(const f_utf_string_dynamic string1, const f_utf_string_dynamic string2, const f_utf_string_location offset1, const f_utf_string_location offset2);
+#endif // _di_fl_utf_string_dynamic_partial_compare_
+
+/**
+ * Compare two UTF-8 strings, similar to strncmp(), but restricted to the given ranges.
+ *
+ * This does not stop on NULL.
+ * NULL characters are ignored.
+ * Ignores leading and trailing whitespace.
+ *
+ * @param string1
+ *   String to compare.
+ * @param string2
+ *   String to compare.
+ * @param offset1
+ *   A range within the string1 to restrict the comparison to.
+ * @param offset2
+ *   A range within the string2 to restrict the comparison to.
+ *
+ * @return
+ *   f_equal_to when both strings equal.
+ *   f_not_equal_to when both strings do not equal.
+ *   f_invalid_utf (with error bit) if a character in the string is an invalid UTF-8 character.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_utf_string_compare()
+ * @see fl_utf_string_compare_trim()
+ * @see fl_utf_string_dynamic_compare()
+ * @see fl_utf_string_dynamic_compare_trim()
+ */
+#ifndef _di_fl_utf_string_dynamic_partial_compare_trim_
+  extern f_return_status fl_utf_string_dynamic_partial_compare_trim(const f_utf_string_dynamic string1, const f_utf_string_dynamic string2, const f_utf_string_location offset1, const f_utf_string_location offset2);
+#endif // _di_fl_utf_string_dynamic_partial_compare_trim_
+
+/**
+ * Allocated a new UTF-8 string from the provided range in the buffer.
+ *
+ * @param buffer
+ *   The buffer to rip from.
+ * @param location
+ *   A range within the buffer representing the string to rip.
+ * @param result
+ *   The new string, which will be allocated or reallocated as necessary.
+ *
+ * @return
+ *   f_none on success.
+ *   f_no_data if nothing to rip, no allocations or reallocations are performed.
+ *   f_invalid_utf (with error bit) if a character in the string is an invalid UTF-8 character.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_error_allocation (with error bit) on memory allocation error.
+ *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see fl_utf_string_dynamic_rip_trim()
+ * @see fl_utf_string_rip()
+ * @see fl_utf_string_rip_trim()
+ */
+#ifndef _di_fl_utf_string_dynamic_rip_
+  extern f_return_status fl_utf_string_dynamic_rip(const f_utf_string_dynamic buffer, const f_utf_string_location location, f_utf_string_dynamic *result);
+#endif // _di_fl_utf_string_dynamic_rip_
+
+/**
+ * Allocate a new UTF-8 string from the provided range in the buffer.
+ *
+ * Ignores leading and trailing whitespace.
+ * As a result, resulting size may be smaller than requested range.
+ *
+ * @param buffer
+ *   The buffer to rip from.
+ * @param location
+ *   A range within the buffer representing the string to rip.
+ * @param result
+ *   The new string, which will be allocated or reallocated as necessary.
+ *
+ * @return
+ *   f_none on success.
+ *   f_no_data if nothing to rip, no allocations or reallocations are performed.
+ *   f_invalid_utf (with error bit) if a character in the string is an invalid UTF-8 character.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_error_allocation (with error bit) on memory allocation error.
+ *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see: fl_utf_string_dynamic_rip()
+ * @see: fl_utf_string_rip()
+ * @see: fl_utf_string_rip_trim()
+ */
+#ifndef _di_fl_utf_string_dynamic_rip_trim_
+  extern f_return_status fl_utf_string_dynamic_rip_trim(const f_utf_string_dynamic buffer, const f_utf_string_location location, f_utf_string_dynamic *result);
+#endif // _di_fl_utf_string_dynamic_rip_trim_
+
+/**
+ * Seek the buffer location forward until the UTF-8 character or EOL is reached.
+ *
+ * @param buffer
+ *   The buffer to traverse.
+ * @param location
+ *   A range within the buffer representing the start and stop locations.
+ *   The start location will be incremented by seek.
+ * @param seek_to_this
+ *   A UTF-8 character representing a character to seek to.
+ *
+ * @return
+ *   f_none on success.
+ *   f_none_on_eol on success, but stopped at EOL.
+ *   f_none_on_eos on success, but stopped at end of buffer.
+ *   f_none_on_stop on success, but stopped stop location.
+ *   f_invalid_utf (with error bit) if a character in the buffer is an invalid UTF-8 character.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_utf_string_dynamic_seek_line_to_char()
+ * @see fl_utf_string_seek_line_to_char()
+ */
+#ifndef _di_fl_utf_string_dynamic_seek_line_to_
+  extern f_return_status fl_utf_string_dynamic_seek_line_to(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character seek_to_this);
+#endif // _di_fl_utf_string_dynamic_seek_line_to_
+
+/**
+ * Seek the buffer location forward until the 1-byte wide character or EOL is reached.
+ *
+ * @param buffer
+ *   The buffer to traverse.
+ * @param location
+ *   A range within the buffer representing the start and stop locations.
+ *   The start location will be incremented by seek.
+ * @param seek_to_this
+ *   A single-width non-UTF-8 character.
+ *
+ * @return
+ *   f_none on success.
+ *   f_none_on_eol on success, but stopped at EOL.
+ *   f_none_on_eos on success, but stopped at end of buffer.
+ *   f_invalid_utf (with error bit) if a character in the buffer is an invalid UTF-8 character.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_utf_string_dynamic_seek_line_to()
+ * @see fl_utf_string_seek_line_to()
+ */
+#ifndef _di_fl_utf_string_seek_line_to_char_
+  extern f_return_status fl_utf_string_dynamic_seek_line_to_char(const f_utf_string_dynamic buffer, f_utf_string_location *location, const int8_t seek_to_this);
+#endif // _di_fl_utf_string_seek_line_to_char_
+
+/**
  * Increment buffer location until a graph character or an EOL is matched.
  *
  * @param buffer
@@ -40,11 +330,13 @@ extern "C" {
  *   f_none_on_eol on success, but stopped at EOL.
  *   f_none_on_eos on success, but stopped at end of buffer.
  *   f_invalid_utf (with error bit) if a character in the buffer is an invalid UTF-8 character.
- *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid
+ *
+ * @see fl_utf_string_seek_line_untile_graph()
  */
-#ifndef _di_fl_utf_seek_line_until_graph_
-  extern f_return_status fl_utf_seek_line_until_graph(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character placeholder);
-#endif // _di_fl_utf_seek_line_until_graph_
+#ifndef _di_fl_utf_string_dynamic_seek_line_until_graph_
+  extern f_return_status fl_utf_string_dynamic_seek_line_until_graph(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character placeholder);
+#endif // _di_fl_utf_string_dynamic_seek_line_until_graph_
 
 /**
  * Increment buffer location until a non-graph character or an EOL is matched.
@@ -63,13 +355,15 @@ extern "C" {
  *   f_none_on_stop on success, but stopped stop location.
  *   f_invalid_utf (with error bit) if a character in the buffer is an invalid UTF-8 character.
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_utf_string_seek_line_until_non_graph()
  */
-#ifndef _di_fl_utf_seek_line_until_non_graph_
-  extern f_return_status fl_utf_seek_line_until_non_graph(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character placeholder);
-#endif // _di_fl_utf_seek_line_until_non_graph_
+#ifndef _di_fl_utf_string_dynamic_seek_line_until_non_graph_
+  extern f_return_status fl_utf_string_dynamic_seek_line_until_non_graph(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character placeholder);
+#endif // _di_fl_utf_string_dynamic_seek_line_until_non_graph_
 
 /**
- * Seek the buffer location forward until the UTF-8 character or EOL is reached.
+ * Seek the buffer location forward until the character (1-byte wide) is reached.
  *
  * @param buffer
  *   The buffer to traverse.
@@ -81,68 +375,109 @@ extern "C" {
  *
  * @return
  *   f_none on success.
- *   f_none_on_eol on success, but stopped at EOL.
  *   f_none_on_eos on success, but stopped at end of buffer.
  *   f_none_on_stop on success, but stopped stop location.
  *   f_invalid_utf (with error bit) if a character in the buffer is an invalid UTF-8 character.
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *
- * @see fl_utf_seek_line_to_char()
+ * @see fl_utf_string_dynamic_seek_to_char()
+ * @see fl_utf_string_seek_to()
+ * @see fl_utf_string_seek_to_char()
  */
-#ifndef _di_fl_utf_seek_line_to_
-  extern f_return_status fl_utf_seek_line_to(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character seek_to_this);
-#endif // _di_fl_utf_seek_line_to_
+#ifndef _di_fl_utf_string_dynamic_seek_to_
+  extern f_return_status fl_utf_string_dynamic_seek_to(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character seek_to_this);
+#endif // _di_fl_utf_string_dynamic_seek_to_
+
+/**
+ * Seek the buffer location forward until the UTF-8 character (up to 4-byte wide) is reached.
+ *
+ * @param buffer
+ *   The buffer to traverse.
+ * @param location
+ *   A range within the buffer representing the start and stop locations.
+ *   The start location will be incremented by seek.
+ * @param seek_to_this
+ *   A single-width non-UTF-8 character.
+ *
+ * @return
+ *   f_none on success.
+ *   f_none_on_eos on success, but stopped at end of buffer.
+ *   f_none_on_stop on success, but stopped stop location.
+ *   f_invalid_utf (with error bit) if a character in the buffer is an invalid UTF-8 character.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_utf_string_dynamic_seek_to()
+ * @see fl_utf_string_seek_to()
+ * @see fl_utf_string_seek_to_char()
+ */
+#ifndef _di_fl_utf_string_dynamic_seek_to_char_
+  extern f_return_status fl_utf_string_dynamic_seek_to_char(const f_utf_string_dynamic buffer, f_utf_string_location *location, const int8_t seek_to_this);
+#endif // _di_fl_utf_string_dynamic_seek_to_char_
 
 /**
  * Allocated a new UTF-8 string from the provided range in the buffer.
  *
  * @param buffer
  *   The buffer to rip from.
- * @param location
- *   A range within the buffer representing the string to rip.
+ * @param start
+ *   An inclusive start location within string.
+ * @param stop
+ *   An inclusive stop location within string.
  * @param result
  *   The new string, which will be allocated or reallocated as necessary.
  *
  * @return
  *   f_none on success.
  *   f_no_data if nothing to rip, no allocations or reallocations are performed.
+ *   f_invalid_utf (with error bit) if a character in the string is an invalid UTF-8 character.
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *   f_error_allocation (with error bit) on memory allocation error.
  *   f_error_reallocation (with error bit) on memory reallocation error.
+ *
+ * @see fl_utf_string_dynamic_rip()
+ * @see fl_utf_string_dynamic_rip_trim()
+ * @see fl_utf_string_rip_trim()
  */
 #ifndef _di_fl_utf_string_rip_
-  extern f_return_status fl_utf_string_rip(const f_utf_string_dynamic buffer, const f_utf_string_location location, f_utf_string_dynamic *result);
+  extern f_return_status fl_utf_string_rip(const f_utf_string string, const f_string_length start, const f_string_length stop, f_utf_string_dynamic *result);
 #endif // _di_fl_utf_string_rip_
 
 /**
- * Seek the buffer location forward until the 1-byte wide character or EOL is reached.
+ * Allocate a new string from the provided range in the string.
  *
- * @param buffer
- *   The buffer to traverse.
- * @param location
- *   A range within the buffer representing the start and stop locations.
- *   The start location will be incremented by seek.
- * @param seek_to_this
- *   A single-width non-UTF-8 character.
+ * Ignores leading and trailing whitespace.
+ * As a result, resulting size may be smaller than requested range.
+ *
+ * @param string
+ *   The string to rip from.
+ * @param start
+ *   An inclusive start location within string.
+ * @param stop
+ *   An inclusive stop location within string.
+ * @param result
+ *   The new string, which will be allocated or reallocated as necessary.
  *
  * @return
  *   f_none on success.
- *   f_none_on_eol on success, but stopped at EOL.
- *   f_none_on_eos on success, but stopped at end of buffer.
- *   f_invalid_utf (with error bit) if a character in the buffer is an invalid UTF-8 character.
+ *   f_no_data if nothing to rip, no allocations or reallocations are performed.
+ *   f_invalid_utf (with error bit) if a character in the string is an invalid UTF-8 character.
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_error_allocation (with error bit) on memory allocation error.
+ *   f_error_reallocation (with error bit) on memory reallocation error.
  *
- * @see fl_utf_seek_line_to()
+ * @see fl_utf_string_dynamic_rip()
+ * @see fl_utf_string_dynamic_rip_trim()
+ * @see fl_utf_string_rip()
  */
-#ifndef _di_fl_utf_string_seek_line_to_char_
-  extern f_return_status fl_utf_seek_line_to_char(const f_utf_string_dynamic buffer, f_utf_string_location *location, const int8_t seek_to_this);
-#endif // _di_fl_utf_string_seek_line_to_char_
+#ifndef _di_fl_utf_string_rip_trim_
+  extern f_return_status fl_utf_string_rip_trim(const f_utf_string string, const f_string_length start, const f_string_length stop, f_utf_string_dynamic *result);
+#endif // _di_fl_utf_string_rip_trim_
 
 /**
- * Seek the buffer location forward until the character (1-byte wide) is reached.
+ * Seek the string location forward until the UTF-8 character or EOL is reached.
  *
- * @param buffer
- *   The buffer to traverse.
+ * @param string
+ *   The string to traverse.
  * @param location
  *   A range within the buffer representing the start and stop locations.
  *   The start location will be incremented by seek.
@@ -151,22 +486,24 @@ extern "C" {
  *
  * @return
  *   f_none on success.
+ *   f_none_on_eol on success, but stopped at EOL.
  *   f_none_on_eos on success, but stopped at end of buffer.
  *   f_none_on_stop on success, but stopped stop location.
  *   f_invalid_utf (with error bit) if a character in the buffer is an invalid UTF-8 character.
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *
- * @see fl_utf_string_seek_to_char()
+ * @see fl_utf_string_dynamic_seek_line_to_char()
+ * @see fl_utf_string_seek_line_to_char()
  */
-#ifndef _di_fl_utf_string_seek_to_
-  extern f_return_status fl_utf_string_seek_to(const f_utf_string_dynamic buffer, f_utf_string_location *location, const f_utf_character seek_to_this);
-#endif // _di_fl_utf_string_seek_to_
+#ifndef _di_fl_utf_string_seek_line_to_
+  extern f_return_status fl_utf_string_seek_line_to(const f_utf_string string, f_utf_string_location *location, const f_utf_character seek_to_this);
+#endif // _di_fl_utf_string_seek_line_to_
 
 /**
- * Seek the buffer location forward until the UTF-8 character (up to 4-byte wide) is reached.
+ * Seek the string location forward until the 1-byte wide character or EOL is reached.
  *
- * @param buffer
- *   The buffer to traverse.
+ * @param string
+ *   The string to traverse.
  * @param location
  *   A range within the buffer representing the start and stop locations.
  *   The start location will be incremented by seek.
@@ -175,93 +512,124 @@ extern "C" {
  *
  * @return
  *   f_none on success.
+ *   f_none_on_eol on success, but stopped at EOL.
  *   f_none_on_eos on success, but stopped at end of buffer.
- *   f_none_on_stop on success, but stopped stop location.
  *   f_invalid_utf (with error bit) if a character in the buffer is an invalid UTF-8 character.
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *
- * @see fl_utf_string_seek_to()
+ * @see fl_utf_string_dynamic_seek_line_to()
+ * @see fl_utf_string_seek_line_to()
  */
-#ifndef _di_fl_utf_string_seek_to_character_
-  extern f_return_status fl_utf_string_seek_to_char(const f_utf_string_dynamic buffer, f_utf_string_location *location, const int8_t seek_to_this);
-#endif // _di_fl_utf_string_seek_to_character_
+#ifndef _di_fl_utf_string_seek_line_to_char_
+  extern f_return_status fl_utf_string_seek_line_to_char(const f_utf_string string, f_utf_string_location *location, const int8_t seek_to_this);
+#endif // _di_fl_utf_string_seek_line_to_char_
 
 /**
- * Compare two strings, similar to strncmp().
- *
- * This does not stop on NULL.
- * NULL characters are ignored.
+ * Increment string location until a UTF-8 graph character or an EOL is matched.
  *
- * @param string1
- *   String to compare.
- * @param string2
- *   String to compare.
- * @param length1
- *   Length of string1.
- * @param length2
- *   Length of string2.
+ * @param string
+ *   The string to traverse.
+ * @param location
+ *   A range within the buffer representing the start and stop locations.
+ *   The start location will be incremented by seek.
+ * @param placeholder
+ *   A UTF-8 character representing a placeholder to ignore (may be NULL).
  *
  * @return
- *   f_equal_to when both strings equal.
- *   f_not_equal_to when both strings do not equal.
+ *   f_none on success.
+ *   f_none_on_eol on success, but stopped at EOL.
+ *   f_incomplete_utf (with error bit) if character is an incomplete UTF-8 fragment.
+ *   f_incomplete_utf_on_stop (with error bit) if the stop location is reached before the complete UTF-8 character can be processed.
+ *   f_invalid_utf (with error bit) if a character in the string is an invalid UTF-8 character.
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_error_allocation (with error bit) on memory allocation error.
+ *   f_error_reallocation (with error bit) on memory reallocation error.
  *
- * @see fl_utf_string_dynamic_compare()
- * @see fl_utf_string_dynamic_partial_compare()
+ * @see fl_utf_string_dynamic_seek_line_until_graph()
  */
-#ifndef _di_fl_utf_string_compare_
-  extern f_return_status fl_utf_string_compare(const f_utf_string string1, const f_utf_string string2, const f_utf_string_length length1, const f_utf_string_length length2);
-#endif // _di_fl_utf_string_compare_
+#ifndef _di_fl_utf_string_seek_line_until_graph_
+  extern f_return_status fl_utf_string_seek_line_until_graph(const f_utf_string string, f_utf_string_location *location, const f_utf_character placeholder);
+#endif // _di_fl_utf_string_seek_line_until_graph_
 
 /**
- * Compare two strings, similar to strncmp().
- *
- * This does not stop on NULL.
- * NULL characters are ignored.
+ * Increment string location until a non-graph UTF-8 character or an EOL is matched.
  *
- * @param string1
- *   String to compare.
- * @param string2
- *   String to compare.
+ * @param string
+ *   The string to traverse.
+ * @param location
+ *   A range within the buffer representing the start and stop locations.
+ *   The start location will be incremented by seek.
+ * @param placeholder
+ *   A UTF-8 character representing a placeholder to ignore (may be NULL).
  *
  * @return
- *   f_equal_to when both strings equal.
- *   f_not_equal_to when both strings do not equal.
+ *   f_none on success.
+ *   f_none_on_eol on success, but stopped at EOL.
+ *   f_none_on_stop on success, but stopped stop location.
+ *   f_incomplete_utf (with error bit) if character is an incomplete UTF-8 fragment.
+ *   f_incomplete_utf_on_stop (with error bit) if the stop location is reached before the complete UTF-8 character can be processed.
+ *   f_invalid_utf (with error bit) if a character in the string is an invalid UTF-8 character.
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *   f_error_allocation (with error bit) on memory allocation error.
+ *   f_error_reallocation (with error bit) on memory reallocation error.
  *
- * @see fl_utf_string_compare()
- * @see fl_utf_string_dynamic_partial_compare()
+ * @see fl_utf_string_dynamic_seek_line_until_non_graph()
  */
-#ifndef _di_fl_utf_string_dynamic_compare_
-  extern f_return_status fl_utf_string_dynamic_compare(const f_utf_string_dynamic string1, const f_utf_string_dynamic string2);
-#endif // _di_fl_utf_string_dynamic_compare_
+#ifndef _di_fl_utf_string_seek_line_until_non_graph_
+  extern f_return_status fl_utf_string_seek_line_until_non_graph(const f_utf_string string, f_utf_string_location *location, const f_utf_character placeholder);
+#endif // _di_fl_utf_string_seek_line_until_non_graph_
 
 /**
- * Compare two strings, similar to strncmp(), but restricted to the given ranges.
+ * Seek the string location forward until the character (1-byte wide) is reached.
  *
- * This does not stop on NULL.
- * NULL characters are ignored.
+ * @param string
+ *   The sting to traverse.
+ * @param location
+ *   A range within the buffer representing the start and stop locations.
+ *   The start location will be incremented by seek.
+ * @param seek_to_this
+ *   A UTF-8 character representing a character to seek to.
  *
- * @param string1
- *   String to compare.
- * @param string2
- *   String to compare.
- * @param offset1
- *   A range within the string1 to restrict the comparison to.
- * @param offset2
- *   A range within the string2 to restrict the comparison to.
+ * @return
+ *   f_none on success.
+ *   f_none_on_eos on success, but stopped at end of buffer.
+ *   f_none_on_stop on success, but stopped stop location.
+ *   f_invalid_utf (with error bit) if a character in the buffer is an invalid UTF-8 character.
+ *   f_invalid_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see fl_utf_string_dynamic_seek_to()
+ * @see fl_utf_string_dynamic_seek_to_char()
+ * @see fl_utf_string_seek_to_char()
+ */
+#ifndef _di_fl_utf_string_seek_to_
+  extern f_return_status fl_utf_string_seek_to(const f_utf_string string, f_utf_string_location *location, const f_utf_character seek_to_this);
+#endif // _di_fl_utf_string_seek_to_
+
+/**
+ * Seek the string location forward until the UTF-8 character (up to 4-byte wide) is reached.
+ *
+ * @param string
+ *   The string to traverse.
+ * @param location
+ *   A range within the buffer representing the start and stop locations.
+ *   The start location will be incremented by seek.
+ * @param seek_to_this
+ *   A single-width non-UTF-8 character.
  *
  * @return
- *   f_equal_to when both strings equal.
- *   f_not_equal_to when both strings do not equal.
+ *   f_none on success.
+ *   f_none_on_eos on success, but stopped at end of buffer.
+ *   f_none_on_stop on success, but stopped stop location.
+ *   f_invalid_utf (with error bit) if a character in the buffer is an invalid UTF-8 character.
  *   f_invalid_parameter (with error bit) if a parameter is invalid.
  *
- * @see fl_utf_string_compare()
- * @see fl_utf_string_dynamic_compare()
+ * @see fl_utf_string_dynamic_seek_to()
+ * @see fl_utf_string_dynamic_seek_to_char()
+ * @see fl_utf_string_seek_to()
  */
-#ifndef _di_fl_utf_string_dynamic_partial_compare_
-  extern f_return_status fl_utf_string_dynamic_partial_compare(const f_utf_string_dynamic string1, const f_utf_string_dynamic string2, const f_utf_string_location offset1, const f_utf_string_location offset2);
-#endif // _di_fl_utf_string_dynamic_partial_compare_
+#ifndef _di_fl_utf_string_seek_to_character_
+  extern f_return_status fl_utf_string_seek_to_char(const f_utf_string string, f_utf_string_location *location, const int8_t seek_to_this);
+#endif // _di_fl_utf_string__seek_to_character_
 
 #ifdef __cplusplus
 } // extern "C"
index 82819e3eaae8c35e77e6a81c2a6538181bbe4eb5..38922799c5d6adac03d7f55327548f2f73f4a67d 100644 (file)
@@ -11,7 +11,7 @@ build_compiler gcc
 build_linker ar
 build_libraries -lc
 build_libraries_fll -lf_utf -lf_memory
-build_sources_library utf.c
+build_sources_library utf.c private-utf.c
 build_sources_program
 build_sources_headers utf.h
 build_sources_bash