Revision: 77190
          http://sourceforge.net/p/brlcad/code/77190
Author:   starseeker
Date:     2020-09-22 02:51:00 +0000 (Tue, 22 Sep 2020)
Log Message:
-----------
Far enough along now to be interesting.  Implement a tabular stat command for 
reporting database object info, using https://github.com/seleznevae/libfort for 
table printing.  Can specify columns and sorting, as well as applying search 
filters to focus output.  Not sure this is a keeper, but checkpoint so progress 
is recorded.

Modified Paths:
--------------
    brlcad/trunk/doc/docbook/system/mann/CMakeLists.txt
    brlcad/trunk/include/ged/commands.h
    brlcad/trunk/src/libged/CMakeLists.txt
    brlcad/trunk/src/libged/exec_mapping.cpp
    brlcad/trunk/src/mged/cmd.c
    brlcad/trunk/src/mged/cmd.h
    brlcad/trunk/src/mged/setup.c

Added Paths:
-----------
    brlcad/trunk/doc/docbook/system/mann/stat.xml
    brlcad/trunk/src/libged/stat/
    brlcad/trunk/src/libged/stat/CMakeLists.txt
    brlcad/trunk/src/libged/stat/fort.c
    brlcad/trunk/src/libged/stat/fort.h
    brlcad/trunk/src/libged/stat/stat.cpp

Modified: brlcad/trunk/doc/docbook/system/mann/CMakeLists.txt
===================================================================
--- brlcad/trunk/doc/docbook/system/mann/CMakeLists.txt 2020-09-21 23:39:03 UTC 
(rev 77189)
+++ brlcad/trunk/doc/docbook/system/mann/CMakeLists.txt 2020-09-22 02:51:00 UTC 
(rev 77190)
@@ -195,6 +195,7 @@
   size.xml
   solids.xml
   sph-part.xml
+  stat.xml
   status.xml
   summary.xml
   sv.xml

Added: brlcad/trunk/doc/docbook/system/mann/stat.xml
===================================================================
--- brlcad/trunk/doc/docbook/system/mann/stat.xml                               
(rev 0)
+++ brlcad/trunk/doc/docbook/system/mann/stat.xml       2020-09-22 02:51:00 UTC 
(rev 77190)
@@ -0,0 +1,219 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<refentry xmlns="http://docbook.org/ns/docbook"; version="5.0" 
xml:id="STATFORMAT" xmlns:xlink="http://www.w3.org/1999/xlink";>
+  <refmeta>
+    <refentrytitle>STAT</refentrytitle>
+    <manvolnum>nged</manvolnum>
+    <refmiscinfo class="source">BRL-CAD</refmiscinfo>
+    <refmiscinfo class="manual">BRL-CAD</refmiscinfo>
+  </refmeta>
+
+  <refnamediv xml:id="name">
+    <refname>stat</refname>
+    <refpurpose>
+      report information about objects in a BRL-CAD database
+    </refpurpose>
+  </refnamediv>
+
+  <!-- body begins here -->
+  <refsynopsisdiv xml:id="synopsis">
+    <cmdsynopsis sepchar=" ">
+      <command>mater</command>
+      <arg choice="opt" rep="norepeat">-h</arg>
+      <arg choice="opt" rep="norepeat">-?</arg>
+      <arg choice="opt" rep="norepeat">-r</arg>
+      <arg choice="opt" rep="norepeat">-F 
<replaceable>string</replaceable></arg>
+      <arg choice="opt" rep="norepeat">-C 
<replaceable>type1[,type2]...</replaceable></arg>
+      <arg choice="opt" rep="norepeat">-S 
<replaceable>type1[,type2]...</replaceable></arg>
+      <arg choice="req">glob pattern</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  
+  <refsection xml:id="stat_overview"><info><title>Overview</title></info>
+
+  <para>
+    The GED <command>stat</command> command reports information about objects 
in
+    a BRL-CAD .g file.  It produces a tabular output, with options for user 
configurability
+    and sorting.
+  </para>
+
+  <para>
+    Available types reflect geometry object data:
+
+    <table frame="all"> <title>STAT Information Keys - Core Object Info</title>
+    <tgroup cols="2">
+      <colspec colwidth="1in" colname="c1"/>
+      <colspec colwidth="4in" colname="c2"/>
+      <tbody>
+       <row> <entry>name</entry> <entry>Object database name (for example, 
"primitive.s")</entry> </row>
+       <row> <entry>uses</entry> <entry>Number of uses, from 
instancing</entry> </row>
+       <row> <entry>size</entry> <entry>Size of database object</entry> </row>
+       <row> <entry>refs</entry> <entry>Number of times object is referenced 
by COMBs</entry> </row>
+       <row> <entry>flags</entry> <entry>Flags</entry> </row>
+       <row> <entry>major_type</entry> <entry>Object major type</entry> </row>
+       <row> <entry>minor_type</entry> <entry>Object minor type</entry> </row>
+       <row> <entry>type</entry> <entry>Object type (short name)</entry> </row>
+      </tbody>
+    </tgroup>
+    </table>
+  </para>
+    
+  <para>
+    In addition to the above standard keys, attributes may also be specified.  
If the desired attribute happens
+    to have the same name as one of the above standard keys, the prefix 
"attr:" may be added to denote an attribute.
+  </para>
+  </refsection>
+
+  <refsection xml:id="search_options"><title>OPTIONS</title>
+
+    <variablelist remap="TP">
+      <varlistentry>
+        <term><emphasis remap="B" role="bold">-h or -?</emphasis></term>
+        <listitem>
+          <para>
+            Print help and exit.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><emphasis remap="B" role="bold">-r</emphasis></term>
+        <listitem>
+          <para>
+            Print raw values, instead of human friendly strings (for example, 
5195952 for a size instead of 5.0M).
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><emphasis remap="B" role="bold">-F</emphasis></term>
+        <listitem>
+          <para>
+            Specify <command>search</command> style filters to trim down what 
is reported in <command>stat</command> output.
+          </para>
+        </listitem>
+      </varlistentry>
+       <varlistentry>
+        <term><emphasis remap="B" role="bold">-C</emphasis></term>
+        <listitem>
+          <para>
+            Supply a comma separated list of column specifiers or attribute 
keys to define the table layout for <command>stat</command>.
+          </para>
+        </listitem>
+       </varlistentry>
+        <varlistentry>
+        <term><emphasis remap="B" role="bold">-S</emphasis></term>
+        <listitem>
+          <para>
+            Supply a comma separated list of column specifiers or attribute 
keys to specify a sorting priority for <command>stat</command>.
+           Priority is left to right - sorting will be performed first based 
on the leftmost specifier, and subsequent specifiers
+           will be used to resolve any ties.  To revers the ordering decisions 
for a given column, prefix the specifier with a "!"
+           character.
+          </para>
+        </listitem>
+       </varlistentry>
+    </variablelist>
+
+  </refsection>
+
+  <refsection xml:id="examples"><title>EXAMPLES</title>
+
+    <example><title>Default Output</title>
+      <para>
+        <userinput>stat *</userinput>
+      </para>
+      <literallayout>
+ Object Name             uses   References   Flags   Type     Size 
+----------------------- ------ ------------ ------- -------- ------
+ Default                 0      1            2       comb     288B 
+ NIST_MBE_PMI_7-10.3dm   0      0            2       comb     264B 
+ Solid1.r                0      1            6       region   280B 
+ Solid1.r.s              0      1            1       brep     859K 
+ Solid1_2.r              0      1            6       region   280B 
+ Solid1_2.r.s            0      1            1       brep     672K 
+ Solid1_3.r              0      1            6       region   280B 
+ Solid1_3.r.s            0      1            1       brep     431K 
+ Solid1_4.r              0      1            6       region   280B 
+ Solid1_4.r.s            0      1            1       brep     771K 
+ _GLOBAL                 0      0            8                96B 
+      </literallayout>
+    </example>
+
+    <example><title>Sorting by Size</title>
+      <para>
+        <userinput>stat -S "size" *</userinput>
+      </para>
+      <literallayout>
+ Object Name             uses   References   Flags   Type     Size 
+----------------------- ------ ------------ ------- -------- ------
+ _GLOBAL                 0      0            8                96B  
+ NIST_MBE_PMI_7-10.3dm   0      0            2       comb     264B 
+ Solid1_2.r              0      1            6       region   280B 
+ Solid1_3.r              0      1            6       region   280B 
+ Solid1.r                0      1            6       region   280B 
+ Solid1_4.r              0      1            6       region   280B 
+ Default                 0      1            2       comb     288B 
+ Solid1_3.r.s            0      1            1       brep     431K 
+ Solid1_2.r.s            0      1            1       brep     672K 
+ Solid1_4.r.s            0      1            1       brep     771K 
+ Solid1.r.s              0      1            1       brep     859K 
+      </literallayout>
+    </example>
+
+    <example><title>Sorting by Size, Reversed Order</title>
+      <para>
+        <userinput>stat -S "!size" *</userinput>
+      </para>
+      <literallayout>
+ Object Name             uses   References   Flags   Type     Size 
+----------------------- ------ ------------ ------- -------- ------
+ Solid1.r.s              0      1            1       brep     859K 
+ Solid1_4.r.s            0      1            1       brep     771K 
+ Solid1_2.r.s            0      1            1       brep     672K 
+ Solid1_3.r.s            0      1            1       brep     431K 
+ Default                 0      1            2       comb     288B 
+ Solid1_3.r              0      1            6       region   280B 
+ Solid1.r                0      1            6       region   280B 
+ Solid1_2.r              0      1            6       region   280B 
+ Solid1_4.r              0      1            6       region   280B 
+ NIST_MBE_PMI_7-10.3dm   0      0            2       comb     264B 
+ _GLOBAL                 0      0            8                96B 
+      </literallayout>
+    </example>
+
+     <example><title>Filtering by Type</title>
+      <para>
+        <userinput>stat -F "-type brep" *</userinput>
+      </para>
+      <literallayout>
+ Object Name    uses   References   Flags   Type   Size 
+-------------- ------ ------------ ------- ------ ------
+ Solid1.r.s     0      1            1       brep   859K 
+ Solid1_2.r.s   0      1            1       brep   672K 
+ Solid1_3.r.s   0      1            1       brep   431K 
+ Solid1_4.r.s   0      1            1       brep   771K
+      </literallayout>
+     </example>
+
+     <example><title>Custom Columns</title>
+      <para>
+        <userinput>stat -C "name,type,size,color" *</userinput>
+      </para>
+      <literallayout>
+ Object Name             Type     Size   color       
+----------------------- -------- ------ -------------
+ Default                 comb     288B   0/0/0       
+ NIST_MBE_PMI_7-10.3dm   comb     264B   0/0/0       
+ Solid1.r                region   280B   153/231/254 
+ Solid1.r.s              brep     859K               
+ Solid1_2.r              region   280B   255/206/142 
+ Solid1_2.r.s            brep     672K               
+ Solid1_3.r              region   280B   237/255/168 
+ Solid1_3.r.s            brep     431K               
+ Solid1_4.r              region   280B   178/178/178 
+ Solid1_4.r.s            brep     771K               
+ _GLOBAL                          96B                
+      </literallayout>
+     </example>
+    
+  </refsection>
+  
+</refentry>


Property changes on: brlcad/trunk/doc/docbook/system/mann/stat.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/xml
\ No newline at end of property
Modified: brlcad/trunk/include/ged/commands.h
===================================================================
--- brlcad/trunk/include/ged/commands.h 2020-09-21 23:39:03 UTC (rev 77189)
+++ brlcad/trunk/include/ged/commands.h 2020-09-22 02:51:00 UTC (rev 77190)
@@ -855,6 +855,11 @@
  */
 GED_EXPORT int ged_pnts(struct ged *gedp, int argc, const char *argv[]);
 
+/**
+ * Report object information.
+ */
+GED_EXPORT extern int ged_stat(struct ged *gedp, int argc, const char *argv[]);
+
 /** @} */
 
 

Modified: brlcad/trunk/src/libged/CMakeLists.txt
===================================================================
--- brlcad/trunk/src/libged/CMakeLists.txt      2020-09-21 23:39:03 UTC (rev 
77189)
+++ brlcad/trunk/src/libged/CMakeLists.txt      2020-09-22 02:51:00 UTC (rev 
77190)
@@ -325,7 +325,7 @@
 add_subdirectory(solid_report)
 add_subdirectory(solids_on_ray)
 add_subdirectory(sphgroup)
-#add_subdirectory(stat)
+add_subdirectory(stat)
 add_subdirectory(summary)
 add_subdirectory(sync)
 add_subdirectory(tables)

Modified: brlcad/trunk/src/libged/exec_mapping.cpp
===================================================================
--- brlcad/trunk/src/libged/exec_mapping.cpp    2020-09-21 23:39:03 UTC (rev 
77189)
+++ brlcad/trunk/src/libged/exec_mapping.cpp    2020-09-22 02:51:00 UTC (rev 
77190)
@@ -320,7 +320,7 @@
 GED_CMD(solids)
 GED_CMD(solids_on_ray)
 GED_CMD(sphgroup)
-//GED_CMD(stat)
+GED_CMD(stat)
 GED_CMD(summary)
 GED_CMD(sv)
 GED_CMD(sync)

Added: brlcad/trunk/src/libged/stat/CMakeLists.txt
===================================================================
--- brlcad/trunk/src/libged/stat/CMakeLists.txt                         (rev 0)
+++ brlcad/trunk/src/libged/stat/CMakeLists.txt 2020-09-22 02:51:00 UTC (rev 
77190)
@@ -0,0 +1,28 @@
+include_directories(
+  ${CMAKE_CURRENT_SOURCE_DIR}
+  ${BRLCAD_BINARY_DIR}/include
+  ${BRLCAD_SOURCE_DIR}/include
+  ${BU_INCLUDE_DIRS}
+  ${GED_INCLUDE_DIRS}
+  )
+
+add_definitions(-DGED_PLUGIN)
+ged_plugin_library(ged-stat SHARED stat.cpp fort.c)
+target_link_libraries(ged-stat libged libbu)
+set_property(TARGET ged-stat APPEND PROPERTY COMPILE_DEFINITIONS BRLCADBUILD 
HAVE_CONFIG_H)
+VALIDATE_STYLE(ged-stat stat.cpp)
+PLUGIN_SETUP(ged-stat ged)
+
+CMAKEFILES(
+  CMakeLists.txt
+  stat.cpp
+  fort.c
+  fort.h
+  )
+
+# Local Variables:
+# tab-width: 8
+# mode: cmake
+# indent-tabs-mode: t
+# End:
+# ex: shiftwidth=2 tabstop=8


Property changes on: brlcad/trunk/src/libged/stat/CMakeLists.txt
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/trunk/src/libged/stat/fort.c
===================================================================
--- brlcad/trunk/src/libged/stat/fort.c                         (rev 0)
+++ brlcad/trunk/src/libged/stat/fort.c 2020-09-22 02:51:00 UTC (rev 77190)
@@ -0,0 +1,7746 @@
+/*
+libfort
+
+MIT License
+
+Copyright (c) 2017 - 2020 Seleznev Anton
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+/* The file was GENERATED by an amalgamation script.*/
+/* DO NOT EDIT BY HAND!!! */
+
+
+#define FT_AMALGAMED_SOURCE /* Macros to make internal libfort functions 
static */
+
+
+/********************************************************
+   Begin of file "fort_utils.h"
+ ********************************************************/
+
+#ifndef FORT_IMPL_H
+#define FORT_IMPL_H
+
+#if defined(_MSC_VER)
+#define _CRT_SECURE_NO_WARNINGS /* To disable warnings for unsafe functions */
+#endif
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include "fort.h"
+
+/* Define FT_INTERNAL to make internal libfort functions static
+ * in the result amalgamed source file.
+ */
+#ifdef FT_AMALGAMED_SOURCE
+#define FT_INTERNAL static
+#else
+#define FT_INTERNAL
+#endif /* FT_AMALGAMED_SORCE */
+
+
+#define FORT_DEFAULT_COL_SEPARATOR '|'
+extern char g_col_separator;
+
+#define FORT_COL_SEPARATOR_LENGTH 1
+
+#define FORT_UNUSED  __attribute__((unused))
+
+#define F_MALLOC fort_malloc
+#define F_FREE fort_free
+#define F_CALLOC fort_calloc
+#define F_REALLOC fort_realloc
+#define F_STRDUP fort_strdup
+#define F_WCSDUP fort_wcsdup
+/* @todo: replace with custom impl !!!*/
+#define F_UTF8DUP utf8dup
+
+#define F_CREATE(type) ((type *)F_CALLOC(sizeof(type), 1))
+
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+#define FT_NEWLINE "\n"
+#define FT_SPACE " "
+
+/*****************************************************************************
+ *               DEFAULT_SIZES
+ * ***************************************************************************/
+#define DEFAULT_STR_BUF_SIZE 1024
+#define DEFAULT_VECTOR_CAPACITY 10
+
+/*****************************************************************************
+ *               DATA TYPES
+ * ***************************************************************************/
+
+enum f_get_policy {
+    CREATE_ON_NULL,
+    DONT_CREATE_ON_NULL
+};
+
+enum f_bool {
+    F_FALSE = 0,
+    F_TRUE = 1
+};
+
+enum f_cell_type {
+    COMMON_CELL,
+    GROUP_MASTER_CELL,
+    GROUP_SLAVE_CELL
+};
+
+enum f_geometry_type {
+    VISIBLE_GEOMETRY,
+    INTERN_REPR_GEOMETRY
+};
+
+enum f_string_type {
+    CHAR_BUF,
+#ifdef FT_HAVE_WCHAR
+    W_CHAR_BUF,
+#endif /* FT_HAVE_WCHAR */
+#ifdef FT_HAVE_UTF8
+    UTF8_BUF,
+#endif /* FT_HAVE_WCHAR */
+};
+
+struct f_string_view {
+    union {
+        const char *cstr;
+#ifdef FT_HAVE_WCHAR
+        const wchar_t *wstr;
+#endif
+#ifdef FT_HAVE_UTF8
+        const void *u8str;
+#endif
+        const void *data;
+    } u;
+    enum f_string_type type;
+};
+typedef struct f_string_view f_string_view_t;
+
+
+#define FT_STR_2_CAT_(arg1, arg2) \
+    arg1##arg2
+#define FT_STR_2_CAT(arg1, arg2) \
+    FT_STR_2_CAT_(arg1, arg2)
+
+#define UNIQUE_NAME_(prefix) \
+    FT_STR_2_CAT(prefix,__COUNTER__)
+#define UNIQUE_NAME(prefix) \
+    UNIQUE_NAME_(prefix)
+
+typedef int f_status;
+
+
+
+
+struct f_table_properties;
+struct f_row;
+struct f_vector;
+struct f_cell;
+struct f_string_buffer;
+struct f_separator {
+    int enabled;
+};
+
+typedef struct f_table_properties f_table_properties_t;
+typedef struct f_vector f_vector_t;
+typedef struct f_cell f_cell_t;
+typedef struct f_string_buffer f_string_buffer_t;
+typedef struct f_row f_row_t;
+typedef struct f_separator f_separator_t;
+
+struct f_context {
+    f_table_properties_t *table_properties;
+    size_t row;
+    size_t column;
+};
+typedef struct f_context f_context_t;
+
+struct f_conv_context {
+    union {
+        char *buf;
+#ifdef FT_HAVE_WCHAR
+        wchar_t *wbuf;
+#endif
+#ifdef FT_HAVE_UTF8
+        const void *u8str;
+#endif
+    } u;
+    size_t raw_avail;
+    struct f_context *cntx;
+    enum f_string_type b_type;
+};
+typedef struct f_conv_context f_conv_context_t;
+
+
+/*****************************************************************************
+ *               LIBFORT helpers
+ *****************************************************************************/
+
+extern void *(*fort_malloc)(size_t size);
+extern void (*fort_free)(void *ptr);
+extern void *(*fort_calloc)(size_t nmemb, size_t size);
+extern void *(*fort_realloc)(void *ptr, size_t size);
+
+FT_INTERNAL
+void set_memory_funcs(void *(*f_malloc)(size_t size), void (*f_free)(void 
*ptr));
+
+FT_INTERNAL
+char *fort_strdup(const char *str);
+
+
+
+FT_INTERNAL
+size_t number_of_columns_in_format_string(const f_string_view_t *fmt);
+
+FT_INTERNAL
+size_t number_of_columns_in_format_buffer(const f_string_buffer_t *fmt);
+
+#if defined(FT_HAVE_WCHAR)
+FT_INTERNAL
+wchar_t *fort_wcsdup(const wchar_t *str);
+#endif
+
+
+
+FT_INTERNAL
+int print_n_strings(f_conv_context_t *cntx, size_t n, const char *str);
+
+
+FT_INTERNAL
+int ft_nprint(f_conv_context_t *cntx, const char *str, size_t strlen);
+#ifdef FT_HAVE_WCHAR
+FT_INTERNAL
+int ft_nwprint(f_conv_context_t *cntx, const wchar_t *str, size_t strlen);
+#endif /* FT_HAVE_WCHAR */
+#ifdef FT_HAVE_UTF8
+FT_INTERNAL
+int ft_nu8print(f_conv_context_t *cntx, const void *beg, const void *end);
+#endif /* FT_HAVE_UTF8 */
+
+
+/*#define PRINT_DEBUG_INFO fprintf(stderr, "error in %s(%s:%d)\n", 
__FUNCTION__, __FILE__, __LINE__);*/
+#define PRINT_DEBUG_INFO
+
+#define FT_CHECK(statement) \
+    do { \
+        tmp = statement; \
+        if (tmp < 0) {\
+            PRINT_DEBUG_INFO \
+            goto clear; \
+        } \
+    } while(0)
+
+#define CHCK_RSLT_ADD_TO_WRITTEN(statement) \
+    do { \
+        tmp = statement; \
+        if (tmp < 0) {\
+            PRINT_DEBUG_INFO \
+            goto clear; \
+        } \
+        written += (size_t)tmp; \
+    } while(0)
+
+#define CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(statement) \
+    do { \
+        tmp = statement; \
+        if (tmp < 0) {\
+            PRINT_DEBUG_INFO \
+            goto clear; \
+        } \
+        invisible_written += (size_t)tmp; \
+    } while(0)
+
+
+#define CHECK_NOT_NEGATIVE(x) \
+    do { if ((x) < 0) goto fort_fail; } while (0)
+
+#endif /* FORT_IMPL_H */
+
+/********************************************************
+   End of file "fort_utils.h"
+ ********************************************************/
+
+
+/********************************************************
+   Begin of file "vector.h"
+ ********************************************************/
+
+#ifndef VECTOR_H
+#define VECTOR_H
+
+/* #include "fort_utils.h" */ /* Commented by amalgamation script */
+
+
+#define INVALID_VEC_INDEX ((size_t) -1)
+
+FT_INTERNAL
+f_vector_t *create_vector(size_t item_size, size_t capacity);
+
+FT_INTERNAL
+void destroy_vector(f_vector_t *);
+
+FT_INTERNAL
+size_t vector_size(const f_vector_t *);
+
+FT_INTERNAL
+size_t vector_capacity(const f_vector_t *);
+
+FT_INTERNAL
+int vector_push(f_vector_t *, const void *item);
+
+FT_INTERNAL
+int vector_insert(f_vector_t *, const void *item, size_t pos);
+
+FT_INTERNAL
+f_vector_t *vector_split(f_vector_t *, size_t pos);
+
+FT_INTERNAL
+const void *vector_at_c(const f_vector_t *vector, size_t index);
+
+FT_INTERNAL
+void *vector_at(f_vector_t *, size_t index);
+
+FT_INTERNAL
+f_status vector_swap(f_vector_t *cur_vec, f_vector_t *mv_vec, size_t pos);
+
+FT_INTERNAL
+void vector_clear(f_vector_t *);
+
+FT_INTERNAL
+int vector_erase(f_vector_t *, size_t index);
+
+#ifdef FT_TEST_BUILD
+f_vector_t *copy_vector(f_vector_t *);
+size_t vector_index_of(const f_vector_t *, const void *item);
+#endif
+
+#define VECTOR_AT(vector, pos, data_type) \
+    *(data_type *)vector_at((vector), (pos))
+
+#define VECTOR_AT_C(vector, pos, const_data_type) \
+    *(const_data_type *)vector_at_c((vector), (pos))
+
+#endif /* VECTOR_H */
+
+/********************************************************
+   End of file "vector.h"
+ ********************************************************/
+
+
+/********************************************************
+   Begin of file "wcwidth.h"
+ ********************************************************/
+
+#ifndef WCWIDTH_H
+#define WCWIDTH_H
+
+/* #include "fort_utils.h" */ /* Commented by amalgamation script */
+
+#ifdef FT_HAVE_WCHAR
+#include <wchar.h>
+
+FT_INTERNAL
+int mk_wcswidth(const wchar_t *pwcs, size_t n);
+
+#endif /* FT_HAVE_WCHAR */
+
+#endif /* WCWIDTH_H */
+
+/********************************************************
+   End of file "wcwidth.h"
+ ********************************************************/
+
+
+/********************************************************
+   Begin of file "utf8.h"
+ ********************************************************/
+
+// The latest version of this library is available on GitHub;
+// https://github.com/sheredom/utf8.h
+
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+//
+// In jurisdictions that recognize copyright laws, the author or authors
+// of this software dedicate any and all copyright interest in the
+// software to the public domain. We make this dedication for the benefit
+// of the public at large and to the detriment of our heirs and
+// successors. We intend this dedication to be an overt act of
+// relinquishment in perpetuity of all present and future rights to this
+// software under copyright law.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// For more information, please refer to <http://unlicense.org/>
+
+#ifndef SHEREDOM_UTF8_H_INCLUDED
+#define SHEREDOM_UTF8_H_INCLUDED
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+
+// disable 'bytes padding added after construct' warning
+#pragma warning(disable : 4820)
+#endif
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#if defined(_MSC_VER)
+typedef __int32 utf8_int32_t;
+#else
+#include <stdint.h>
+typedef int32_t utf8_int32_t;
+#endif
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
+#pragma clang diagnostic ignored "-Wcast-qual"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__clang__) || defined(__GNUC__)
+#define utf8_nonnull __attribute__((nonnull))
+#define utf8_pure __attribute__((pure))
+#define utf8_restrict __restrict__
+#define utf8_weak __attribute__((weak))
+#elif defined(_MSC_VER)
+#define utf8_nonnull
+#define utf8_pure
+#define utf8_restrict __restrict
+#define utf8_weak __inline
+#else
+#define utf8_nonnull
+#define utf8_pure
+#define utf8_restrict
+#define utf8_weak inline
+#endif
+
+#ifdef __cplusplus
+#define utf8_null NULL
+#else
+#define utf8_null 0
+#endif
+
+// Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 >
+// src2 respectively, case insensitive.
+utf8_nonnull utf8_pure utf8_weak int utf8casecmp(const void *src1,
+        const void *src2);
+
+// Append the utf8 string src onto the utf8 string dst.
+utf8_nonnull utf8_weak void *utf8cat(void *utf8_restrict dst,
+                                     const void *utf8_restrict src);
+
+// Find the first match of the utf8 codepoint chr in the utf8 string src.
+utf8_nonnull utf8_pure utf8_weak void *utf8chr(const void *src,
+        utf8_int32_t chr);
+
+// Return less than 0, 0, greater than 0 if src1 < src2,
+// src1 == src2, src1 > src2 respectively.
+utf8_nonnull utf8_pure utf8_weak int utf8cmp(const void *src1,
+        const void *src2);
+
+// Copy the utf8 string src onto the memory allocated in dst.
+utf8_nonnull utf8_weak void *utf8cpy(void *utf8_restrict dst,
+                                     const void *utf8_restrict src);
+
+// Number of utf8 codepoints in the utf8 string src that consists entirely
+// of utf8 codepoints not from the utf8 string reject.
+utf8_nonnull utf8_pure utf8_weak size_t utf8cspn(const void *src,
+        const void *reject);
+
+// Duplicate the utf8 string src by getting its size, malloc'ing a new buffer
+// copying over the data, and returning that. Or 0 if malloc failed.
+utf8_nonnull utf8_weak void *utf8dup(const void *src);
+
+// Number of utf8 codepoints in the utf8 string str,
+// excluding the null terminating byte.
+utf8_nonnull utf8_pure utf8_weak size_t utf8len(const void *str);
+
+// Visible width of utf8string.
+utf8_nonnull utf8_pure utf8_weak size_t utf8width(const void *str);
+
+// Visible width of codepoint.
+utf8_nonnull utf8_pure utf8_weak int utf8cwidth(utf8_int32_t c);
+
+// Return less than 0, 0, greater than 0 if src1 < src2, src1 == src2, src1 >
+// src2 respectively, case insensitive. Checking at most n bytes of each utf8
+// string.
+utf8_nonnull utf8_pure utf8_weak int utf8ncasecmp(const void *src1,
+        const void *src2, size_t n);
+
+// Append the utf8 string src onto the utf8 string dst,
+// writing at most n+1 bytes. Can produce an invalid utf8
+// string if n falls partway through a utf8 codepoint.
+utf8_nonnull utf8_weak void *utf8ncat(void *utf8_restrict dst,
+                                      const void *utf8_restrict src, size_t n);
+
+// Return less than 0, 0, greater than 0 if src1 < src2,
+// src1 == src2, src1 > src2 respectively. Checking at most n
+// bytes of each utf8 string.
+utf8_nonnull utf8_pure utf8_weak int utf8ncmp(const void *src1,
+        const void *src2, size_t n);
+
+// Copy the utf8 string src onto the memory allocated in dst.
+// Copies at most n bytes. If there is no terminating null byte in
+// the first n bytes of src, the string placed into dst will not be
+// null-terminated. If the size (in bytes) of src is less than n,
+// extra null terminating bytes are appended to dst such that at
+// total of n bytes are written. Can produce an invalid utf8
+// string if n falls partway through a utf8 codepoint.
+utf8_nonnull utf8_weak void *utf8ncpy(void *utf8_restrict dst,
+                                      const void *utf8_restrict src, size_t n);
+
+// Similar to utf8dup, except that at most n bytes of src are copied. If src is
+// longer than n, only n bytes are copied and a null byte is added.
+//
+// Returns a new string if successful, 0 otherwise
+utf8_nonnull utf8_weak void *utf8ndup(const void *src, size_t n);
+
+// Locates the first occurence in the utf8 string str of any byte in the
+// utf8 string accept, or 0 if no match was found.
+utf8_nonnull utf8_pure utf8_weak void *utf8pbrk(const void *str,
+        const void *accept);
+
+// Find the last match of the utf8 codepoint chr in the utf8 string src.
+utf8_nonnull utf8_pure utf8_weak void *utf8rchr(const void *src, int chr);
+
+// Number of bytes in the utf8 string str,
+// including the null terminating byte.
+utf8_nonnull utf8_pure utf8_weak size_t utf8size(const void *str);
+
+// Number of utf8 codepoints in the utf8 string src that consists entirely
+// of utf8 codepoints from the utf8 string accept.
+utf8_nonnull utf8_pure utf8_weak size_t utf8spn(const void *src,
+        const void *accept);
+
+// The position of the utf8 string needle in the utf8 string haystack.
+utf8_nonnull utf8_pure utf8_weak void *utf8str(const void *haystack,
+        const void *needle);
+
+// The position of the utf8 string needle in the utf8 string haystack, case
+// insensitive.
+utf8_nonnull utf8_pure utf8_weak void *utf8casestr(const void *haystack,
+        const void *needle);
+
+// Return 0 on success, or the position of the invalid
+// utf8 codepoint on failure.
+utf8_nonnull utf8_pure utf8_weak void *utf8valid(const void *str);
+
+// Sets out_codepoint to the next utf8 codepoint in str, and returns the 
address
+// of the utf8 codepoint after the current one in str.
+utf8_nonnull utf8_weak void *
+utf8codepoint(const void *utf8_restrict str,
+              utf8_int32_t *utf8_restrict out_codepoint);
+
+// Returns the size of the given codepoint in bytes.
+utf8_weak size_t utf8codepointsize(utf8_int32_t chr);
+
+// Write a codepoint to the given string, and return the address to the next
+// place after the written codepoint. Pass how many bytes left in the buffer to
+// n. If there is not enough space for the codepoint, this function returns
+// null.
+utf8_nonnull utf8_weak void *utf8catcodepoint(void *utf8_restrict str,
+        utf8_int32_t chr, size_t n);
+
+// Returns 1 if the given character is lowercase, or 0 if it is not.
+utf8_weak int utf8islower(utf8_int32_t chr);
+
+// Returns 1 if the given character is uppercase, or 0 if it is not.
+utf8_weak int utf8isupper(utf8_int32_t chr);
+
+// Transform the given string into all lowercase codepoints.
+utf8_nonnull utf8_weak void utf8lwr(void *utf8_restrict str);
+
+// Transform the given string into all uppercase codepoints.
+utf8_nonnull utf8_weak void utf8upr(void *utf8_restrict str);
+
+// Make a codepoint lower case if possible.
+utf8_weak utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp);
+
+// Make a codepoint upper case if possible.
+utf8_weak utf8_int32_t utf8uprcodepoint(utf8_int32_t cp);
+
+#undef utf8_weak
+#undef utf8_pure
+#undef utf8_nonnull
+
+int utf8casecmp(const void *src1, const void *src2)
+{
+    utf8_int32_t src1_cp, src2_cp, src1_orig_cp, src2_orig_cp;
+
+    for (;;) {
+        src1 = utf8codepoint(src1, &src1_cp);
+        src2 = utf8codepoint(src2, &src2_cp);
+
+        // Take a copy of src1 & src2
+        src1_orig_cp = src1_cp;
+        src2_orig_cp = src2_cp;
+
+        // Lower the srcs if required
+        src1_cp = utf8lwrcodepoint(src1_cp);
+        src2_cp = utf8lwrcodepoint(src2_cp);
+
+        // Check if the lowered codepoints match
+        if ((0 == src1_orig_cp) && (0 == src2_orig_cp)) {
+            return 0;
+        } else if (src1_cp == src2_cp) {
+            continue;
+        }
+
+        // If they don't match, then we return which of the original's are less
+        if (src1_orig_cp < src2_orig_cp) {
+            return -1;
+        } else if (src1_orig_cp > src2_orig_cp) {
+            return 1;
+        }
+    }
+}
+
+void *utf8cat(void *utf8_restrict dst, const void *utf8_restrict src)
+{
+    char *d = (char *)dst;
+    const char *s = (const char *)src;
+
+    // find the null terminating byte in dst
+    while ('\0' != *d) {
+        d++;
+    }
+
+    // overwriting the null terminating byte in dst, append src byte-by-byte
+    while ('\0' != *s) {
+        *d++ = *s++;
+    }
+
+    // write out a new null terminating byte into dst
+    *d = '\0';
+
+    return dst;
+}
+
+void *utf8chr(const void *src, utf8_int32_t chr)
+{
+    char c[5] = {'\0', '\0', '\0', '\0', '\0'};
+
+    if (0 == chr) {
+        // being asked to return position of null terminating byte, so
+        // just run s to the end, and return!
+        const char *s = (const char *)src;
+        while ('\0' != *s) {
+            s++;
+        }
+        return (void *)s;
+    } else if (0 == ((utf8_int32_t)0xffffff80 & chr)) {
+        // 1-byte/7-bit ascii
+        // (0b0xxxxxxx)
+        c[0] = (char)chr;
+    } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) {
+        // 2-byte/11-bit utf8 code point
+        // (0b110xxxxx 0b10xxxxxx)
+        c[0] = 0xc0 | (char)(chr >> 6);
+        c[1] = 0x80 | (char)(chr & 0x3f);
+    } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) {
+        // 3-byte/16-bit utf8 code point
+        // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx)
+        c[0] = 0xe0 | (char)(chr >> 12);
+        c[1] = 0x80 | (char)((chr >> 6) & 0x3f);
+        c[2] = 0x80 | (char)(chr & 0x3f);
+    } else { // if (0 == ((int)0xffe00000 & chr)) {
+        // 4-byte/21-bit utf8 code point
+        // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx)
+        c[0] = 0xf0 | (char)(chr >> 18);
+        c[1] = 0x80 | (char)((chr >> 12) & 0x3f);
+        c[2] = 0x80 | (char)((chr >> 6) & 0x3f);
+        c[3] = 0x80 | (char)(chr & 0x3f);
+    }
+
+    // we've made c into a 2 utf8 codepoint string, one for the chr we are
+    // seeking, another for the null terminating byte. Now use utf8str to
+    // search
+    return utf8str(src, c);
+}
+
+int utf8cmp(const void *src1, const void *src2)
+{
+    const unsigned char *s1 = (const unsigned char *)src1;
+    const unsigned char *s2 = (const unsigned char *)src2;
+
+    while (('\0' != *s1) || ('\0' != *s2)) {
+        if (*s1 < *s2) {
+            return -1;
+        } else if (*s1 > *s2) {
+            return 1;
+        }
+
+        s1++;
+        s2++;
+    }
+
+    // both utf8 strings matched
+    return 0;
+}
+
+int utf8coll(const void *src1, const void *src2);
+
+void *utf8cpy(void *utf8_restrict dst, const void *utf8_restrict src)
+{
+    char *d = (char *)dst;
+    const char *s = (const char *)src;
+
+    // overwriting anything previously in dst, write byte-by-byte
+    // from src
+    while ('\0' != *s) {
+        *d++ = *s++;
+    }
+
+    // append null terminating byte
+    *d = '\0';
+
+    return dst;
+}
+
+size_t utf8cspn(const void *src, const void *reject)
+{
+    const char *s = (const char *)src;
+    size_t chars = 0;
+
+    while ('\0' != *s) {
+        const char *r = (const char *)reject;
+        size_t offset = 0;
+
+        while ('\0' != *r) {
+            // checking that if *r is the start of a utf8 codepoint
+            // (it is not 0b10xxxxxx) and we have successfully matched
+            // a previous character (0 < offset) - we found a match
+            if ((0x80 != (0xc0 & *r)) && (0 < offset)) {
+                return chars;
+            } else {
+                if (*r == s[offset]) {
+                    // part of a utf8 codepoint matched, so move our checking
+                    // onwards to the next byte
+                    offset++;
+                    r++;
+                } else {
+                    // r could be in the middle of an unmatching utf8 code 
point,
+                    // so we need to march it on to the next character 
beginning,
+
+                    do {
+                        r++;
+                    } while (0x80 == (0xc0 & *r));
+
+                    // reset offset too as we found a mismatch
+                    offset = 0;
+                }
+            }
+        }
+
+        // the current utf8 codepoint in src did not match reject, but src
+        // could have been partway through a utf8 codepoint, so we need to
+        // march it onto the next utf8 codepoint starting byte
+        do {
+            s++;
+        } while ((0x80 == (0xc0 & *s)));
+        chars++;
+    }
+
+    return chars;
+}
+
+size_t utf8size(const void *str);
+
+void *utf8dup(const void *src)
+{
+    const char *s = (const char *)src;
+    char *n = utf8_null;
+
+    // figure out how many bytes (including the terminator) we need to copy 
first
+    size_t bytes = utf8size(src);
+
+    n = (char *)malloc(bytes);
+
+    if (utf8_null == n) {
+        // out of memory so we bail
+        return utf8_null;
+    } else {
+        bytes = 0;
+
+        // copy src byte-by-byte into our new utf8 string
+        while ('\0' != s[bytes]) {
+            n[bytes] = s[bytes];
+            bytes++;
+        }
+
+        // append null terminating byte
+        n[bytes] = '\0';
+        return n;
+    }
+}
+
+void *utf8fry(const void *str);
+
+size_t utf8len(const void *str)
+{
+    const unsigned char *s = (const unsigned char *)str;
+    size_t length = 0;
+
+    while ('\0' != *s) {
+        if (0xf0 == (0xf8 & *s)) {
+            // 4-byte utf8 code point (began with 0b11110xxx)
+            s += 4;
+        } else if (0xe0 == (0xf0 & *s)) {
+            // 3-byte utf8 code point (began with 0b1110xxxx)
+            s += 3;
+        } else if (0xc0 == (0xe0 & *s)) {
+            // 2-byte utf8 code point (began with 0b110xxxxx)
+            s += 2;
+        } else { // if (0x00 == (0x80 & *s)) {
+            // 1-byte ascii (began with 0b0xxxxxxx)
+            s += 1;
+        }
+
+        // no matter the bytes we marched s forward by, it was
+        // only 1 utf8 codepoint
+        length++;
+    }
+
+    return length;
+}
+
+// See
+// https://unicode.org/Public/UNIDATA/EastAsianWidth.txt
+// http://www.unicode.org/reports/tr11/tr11-33.html
+int utf8cwidth(utf8_int32_t c)
+{
+    // TODO: add non printable characters check
+    if (c == 0)
+        return 0;
+
+    if (c < 0x1100)
+        return 1;
+
+    // Fullwidth
+    if ((0x3000 == c) ||
+        (0xFF01 <= c && c <= 0xFF60) ||
+        (0xFFE0 <= c && c <= 0xFFE6)) {
+        return 2;
+    }
+
+    // Wide
+    if ((0x1100 <= c && c <= 0x115F) ||
+        (0x11A3 <= c && c <= 0x11A7) ||
+        (0x11FA <= c && c <= 0x11FF) ||
+        (0x2329 <= c && c <= 0x232A) ||
+        (0x2E80 <= c && c <= 0x2E99) ||
+        (0x2E9B <= c && c <= 0x2EF3) ||
+        (0x2F00 <= c && c <= 0x2FD5) ||
+        (0x2FF0 <= c && c <= 0x2FFB) ||
+        (0x3001 <= c && c <= 0x303E) ||
+        (0x3041 <= c && c <= 0x3096) ||
+        (0x3099 <= c && c <= 0x30FF) ||
+        (0x3105 <= c && c <= 0x312D) ||
+        (0x3131 <= c && c <= 0x318E) ||
+        (0x3190 <= c && c <= 0x31BA) ||
+        (0x31C0 <= c && c <= 0x31E3) ||
+        (0x31F0 <= c && c <= 0x321E) ||
+        (0x3220 <= c && c <= 0x3247) ||
+        (0x3250 <= c && c <= 0x32FE) ||
+        (0x3300 <= c && c <= 0x4DBF) ||
+        (0x4E00 <= c && c <= 0xA48C) ||
+        (0xA490 <= c && c <= 0xA4C6) ||
+        (0xA960 <= c && c <= 0xA97C) ||
+        (0xAC00 <= c && c <= 0xD7A3) ||
+        (0xD7B0 <= c && c <= 0xD7C6) ||
+        (0xD7CB <= c && c <= 0xD7FB) ||
+        (0xF900 <= c && c <= 0xFAFF) ||
+        (0xFE10 <= c && c <= 0xFE19) ||
+        (0xFE30 <= c && c <= 0xFE52) ||
+        (0xFE54 <= c && c <= 0xFE66) ||
+        (0xFE68 <= c && c <= 0xFE6B) ||
+        (0x1B000 <= c && c <= 0x1B001) ||
+        (0x1F200 <= c && c <= 0x1F202) ||
+        (0x1F210 <= c && c <= 0x1F23A) ||
+        (0x1F240 <= c && c <= 0x1F248) ||
+        (0x1F250 <= c && c <= 0x1F251) ||
+        (0x20000 <= c && c <= 0x2F73F) ||
+        (0x2B740 <= c && c <= 0x2FFFD) ||
+        (0x30000 <= c && c <= 0x3FFFD)) {
+        return 2;
+    }
+
+    return 1;
+}
+
+size_t utf8width(const void *str)
+{
+    size_t length = 0;
+    utf8_int32_t c = 0;
+
+    str = utf8codepoint(str, &c);
+    while (c != 0) {
+        length += utf8cwidth(c);
+        str = utf8codepoint(str, &c);
+    }
+    return length;
+}
+
+int utf8ncasecmp(const void *src1, const void *src2, size_t n)
+{
+    utf8_int32_t src1_cp, src2_cp, src1_orig_cp, src2_orig_cp;
+
+    do {
+        const unsigned char *const s1 = (const unsigned char *)src1;
+        const unsigned char *const s2 = (const unsigned char *)src2;
+
+        // first check that we have enough bytes left in n to contain an entire
+        // codepoint
+        if (0 == n) {
+            return 0;
+        }
+
+        if ((1 == n) && ((0xc0 == (0xe0 & *s1)) || (0xc0 == (0xe0 & *s2)))) {
+            const utf8_int32_t c1 = (0xe0 & *s1);
+            const utf8_int32_t c2 = (0xe0 & *s2);
+
+            if (c1 < c2) {
+                return -1;
+            } else if (c1 > c2) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+
+        if ((2 >= n) && ((0xe0 == (0xf0 & *s1)) || (0xe0 == (0xf0 & *s2)))) {
+            const utf8_int32_t c1 = (0xf0 & *s1);
+            const utf8_int32_t c2 = (0xf0 & *s2);
+
+            if (c1 < c2) {
+                return -1;
+            } else if (c1 > c2) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+
+        if ((3 >= n) && ((0xf0 == (0xf8 & *s1)) || (0xf0 == (0xf8 & *s2)))) {
+            const utf8_int32_t c1 = (0xf8 & *s1);
+            const utf8_int32_t c2 = (0xf8 & *s2);
+
+            if (c1 < c2) {
+                return -1;
+            } else if (c1 > c2) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+
+        src1 = utf8codepoint(src1, &src1_cp);
+        src2 = utf8codepoint(src2, &src2_cp);
+        n -= utf8codepointsize(src1_cp);
+
+        // Take a copy of src1 & src2
+        src1_orig_cp = src1_cp;
+        src2_orig_cp = src2_cp;
+
+        // Lower srcs if required
+        src1_cp = utf8lwrcodepoint(src1_cp);
+        src2_cp = utf8lwrcodepoint(src2_cp);
+
+        // Check if the lowered codepoints match
+        if ((0 == src1_orig_cp) && (0 == src2_orig_cp)) {
+            return 0;
+        } else if (src1_cp == src2_cp) {
+            continue;
+        }
+
+        // If they don't match, then we return which of the original's are less
+        if (src1_orig_cp < src2_orig_cp) {
+            return -1;
+        } else if (src1_orig_cp > src2_orig_cp) {
+            return 1;
+        }
+    } while (0 < n);
+
+    // both utf8 strings matched
+    return 0;
+}
+
+void *utf8ncat(void *utf8_restrict dst, const void *utf8_restrict src,
+               size_t n)
+{
+    char *d = (char *)dst;
+    const char *s = (const char *)src;
+
+    // find the null terminating byte in dst
+    while ('\0' != *d) {
+        d++;
+    }
+
+    // overwriting the null terminating byte in dst, append src byte-by-byte
+    // stopping if we run out of space
+    do {
+        *d++ = *s++;
+    } while (('\0' != *s) && (0 != --n));
+
+    // write out a new null terminating byte into dst
+    *d = '\0';
+
+    return dst;
+}
+
+int utf8ncmp(const void *src1, const void *src2, size_t n)
+{
+    const unsigned char *s1 = (const unsigned char *)src1;
+    const unsigned char *s2 = (const unsigned char *)src2;
+
+    while ((0 != n--) && (('\0' != *s1) || ('\0' != *s2))) {
+        if (*s1 < *s2) {
+            return -1;
+        } else if (*s1 > *s2) {
+            return 1;
+        }
+
+        s1++;
+        s2++;
+    }
+
+    // both utf8 strings matched
+    return 0;
+}
+
+void *utf8ncpy(void *utf8_restrict dst, const void *utf8_restrict src,
+               size_t n)
+{
+    char *d = (char *)dst;
+    const char *s = (const char *)src;
+    size_t index;
+
+    // overwriting anything previously in dst, write byte-by-byte
+    // from src
+    for (index = 0; index < n; index++) {
+        d[index] = s[index];
+        if ('\0' == s[index]) {
+            break;
+        }
+    }
+
+    // append null terminating byte
+    for (; index < n; index++) {
+        d[index] = 0;
+    }
+
+    return dst;
+}
+
+void *utf8ndup(const void *src, size_t n)
+{
+    const char *s = (const char *)src;
+    char *c = utf8_null;
+    size_t bytes = 0;
+
+    // Find the end of the string or stop when n is reached
+    while ('\0' != s[bytes] && bytes < n) {
+        bytes++;
+    }
+
+    // In case bytes is actually less than n, we need to set it
+    // to be used later in the copy byte by byte.
+    n = bytes;
+
+    c = (char *)malloc(bytes + 1);
+    if (utf8_null == c) {
+        // out of memory so we bail
+        return utf8_null;
+    }
+
+    bytes = 0;
+
+    // copy src byte-by-byte into our new utf8 string
+    while ('\0' != s[bytes] && bytes < n) {
+        c[bytes] = s[bytes];
+        bytes++;
+    }
+
+    // append null terminating byte
+    c[bytes] = '\0';
+    return c;
+}
+
+void *utf8rchr(const void *src, int chr)
+{
+    const char *s = (const char *)src;
+    const char *match = utf8_null;
+    char c[5] = {'\0', '\0', '\0', '\0', '\0'};
+
+    if (0 == chr) {
+        // being asked to return position of null terminating byte, so
+        // just run s to the end, and return!
+        while ('\0' != *s) {
+            s++;
+        }
+        return (void *)s;
+    } else if (0 == ((int)0xffffff80 & chr)) {
+        // 1-byte/7-bit ascii
+        // (0b0xxxxxxx)
+        c[0] = (char)chr;
+    } else if (0 == ((int)0xfffff800 & chr)) {
+        // 2-byte/11-bit utf8 code point
+        // (0b110xxxxx 0b10xxxxxx)
+        c[0] = 0xc0 | (char)(chr >> 6);
+        c[1] = 0x80 | (char)(chr & 0x3f);
+    } else if (0 == ((int)0xffff0000 & chr)) {
+        // 3-byte/16-bit utf8 code point
+        // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx)
+        c[0] = 0xe0 | (char)(chr >> 12);
+        c[1] = 0x80 | (char)((chr >> 6) & 0x3f);
+        c[2] = 0x80 | (char)(chr & 0x3f);
+    } else { // if (0 == ((int)0xffe00000 & chr)) {
+        // 4-byte/21-bit utf8 code point
+        // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx)
+        c[0] = 0xf0 | (char)(chr >> 18);
+        c[1] = 0x80 | (char)((chr >> 12) & 0x3f);
+        c[2] = 0x80 | (char)((chr >> 6) & 0x3f);
+        c[3] = 0x80 | (char)(chr & 0x3f);
+    }
+
+    // we've created a 2 utf8 codepoint string in c that is
+    // the utf8 character asked for by chr, and a null
+    // terminating byte
+
+    while ('\0' != *s) {
+        size_t offset = 0;
+
+        while (s[offset] == c[offset]) {
+            offset++;
+        }
+
+        if ('\0' == c[offset]) {
+            // we found a matching utf8 code point
+            match = s;
+            s += offset;
+        } else {
+            s += offset;
+
+            // need to march s along to next utf8 codepoint start
+            // (the next byte that doesn't match 0b10xxxxxx)
+            if ('\0' != *s) {
+                do {
+                    s++;
+                } while (0x80 == (0xc0 & *s));
+            }
+        }
+    }
+
+    // return the last match we found (or 0 if no match was found)
+    return (void *)match;
+}
+
+void *utf8pbrk(const void *str, const void *accept)
+{
+    const char *s = (const char *)str;
+
+    while ('\0' != *s) {
+        const char *a = (const char *)accept;
+        size_t offset = 0;
+
+        while ('\0' != *a) {
+            // checking that if *a is the start of a utf8 codepoint
+            // (it is not 0b10xxxxxx) and we have successfully matched
+            // a previous character (0 < offset) - we found a match
+            if ((0x80 != (0xc0 & *a)) && (0 < offset)) {
+                return (void *)s;
+            } else {
+                if (*a == s[offset]) {
+                    // part of a utf8 codepoint matched, so move our checking
+                    // onwards to the next byte
+                    offset++;
+                    a++;
+                } else {
+                    // r could be in the middle of an unmatching utf8 code 
point,
+                    // so we need to march it on to the next character 
beginning,
+
+                    do {
+                        a++;
+                    } while (0x80 == (0xc0 & *a));
+
+                    // reset offset too as we found a mismatch
+                    offset = 0;
+                }
+            }
+        }
+
+        // we found a match on the last utf8 codepoint
+        if (0 < offset) {
+            return (void *)s;
+        }
+
+        // the current utf8 codepoint in src did not match accept, but src
+        // could have been partway through a utf8 codepoint, so we need to
+        // march it onto the next utf8 codepoint starting byte
+        do {
+            s++;
+        } while ((0x80 == (0xc0 & *s)));
+    }
+
+    return utf8_null;
+}
+
+size_t utf8size(const void *str)
+{
+    const char *s = (const char *)str;
+    size_t size = 0;
+    while ('\0' != s[size]) {
+        size++;
+    }
+
+    // we are including the null terminating byte in the size calculation
+    size++;
+    return size;
+}
+
+size_t utf8spn(const void *src, const void *accept)
+{
+    const char *s = (const char *)src;
+    size_t chars = 0;
+
+    while ('\0' != *s) {
+        const char *a = (const char *)accept;
+        size_t offset = 0;
+
+        while ('\0' != *a) {
+            // checking that if *r is the start of a utf8 codepoint
+            // (it is not 0b10xxxxxx) and we have successfully matched
+            // a previous character (0 < offset) - we found a match
+            if ((0x80 != (0xc0 & *a)) && (0 < offset)) {
+                // found a match, so increment the number of utf8 codepoints
+                // that have matched and stop checking whether any other utf8
+                // codepoints in a match
+                chars++;
+                s += offset;
+                break;
+            } else {
+                if (*a == s[offset]) {
+                    offset++;
+                    a++;
+                } else {
+                    // a could be in the middle of an unmatching utf8 
codepoint,
+                    // so we need to march it on to the next character 
beginning,
+                    do {
+                        a++;
+                    } while (0x80 == (0xc0 & *a));
+
+                    // reset offset too as we found a mismatch
+                    offset = 0;
+                }
+            }
+        }
+
+        // if a got to its terminating null byte, then we didn't find a match.
+        // Return the current number of matched utf8 codepoints
+        if ('\0' == *a) {
+            return chars;
+        }
+    }
+
+    return chars;
+}
+
+void *utf8str(const void *haystack, const void *needle)
+{
+    const char *h = (const char *)haystack;
+    utf8_int32_t throwaway_codepoint;
+
+    // if needle has no utf8 codepoints before the null terminating
+    // byte then return haystack
+    if ('\0' == *((const char *)needle)) {
+        return (void *)haystack;
+    }
+
+    while ('\0' != *h) {
+        const char *maybeMatch = h;
+        const char *n = (const char *)needle;
+
+        while (*h == *n && (*h != '\0' && *n != '\0')) {
+            n++;
+            h++;
+        }
+
+        if ('\0' == *n) {
+            // we found the whole utf8 string for needle in haystack at
+            // maybeMatch, so return it
+            return (void *)maybeMatch;
+        } else {
+            // h could be in the middle of an unmatching utf8 codepoint,
+            // so we need to march it on to the next character beginning
+            // starting from the current character
+            h = (const char *)utf8codepoint(maybeMatch, &throwaway_codepoint);
+        }
+    }
+
+    // no match
+    return utf8_null;
+}
+
+void *utf8casestr(const void *haystack, const void *needle)
+{
+    const void *h = haystack;
+
+    // if needle has no utf8 codepoints before the null terminating
+    // byte then return haystack
+    if ('\0' == *((const char *)needle)) {
+        return (void *)haystack;
+    }
+
+    for (;;) {
+        const void *maybeMatch = h;
+        const void *n = needle;
+        utf8_int32_t h_cp, n_cp;
+
+        // Get the next code point and track it
+        const void *nextH = h = utf8codepoint(h, &h_cp);
+        n = utf8codepoint(n, &n_cp);
+
+        while ((0 != h_cp) && (0 != n_cp)) {
+            h_cp = utf8lwrcodepoint(h_cp);
+            n_cp = utf8lwrcodepoint(n_cp);
+
+            // if we find a mismatch, bail out!
+            if (h_cp != n_cp) {
+                break;
+            }
+
+            h = utf8codepoint(h, &h_cp);
+            n = utf8codepoint(n, &n_cp);
+        }
+
+        if (0 == n_cp) {
+            // we found the whole utf8 string for needle in haystack at
+            // maybeMatch, so return it
+            return (void *)maybeMatch;
+        }
+
+        if (0 == h_cp) {
+            // no match
+            return utf8_null;
+        }
+
+        // Roll back to the next code point in the haystack to test
+        h = nextH;
+    }
+}
+
+void *utf8valid(const void *str)
+{
+    const char *s = (const char *)str;
+
+    while ('\0' != *s) {
+        if (0xf0 == (0xf8 & *s)) {
+            // ensure each of the 3 following bytes in this 4-byte
+            // utf8 codepoint began with 0b10xxxxxx
+            if ((0x80 != (0xc0 & s[1])) || (0x80 != (0xc0 & s[2])) ||
+                (0x80 != (0xc0 & s[3]))) {
+                return (void *)s;
+            }
+
+            // ensure that our utf8 codepoint ended after 4 bytes
+            if (0x80 == (0xc0 & s[4])) {
+                return (void *)s;
+            }
+
+            // ensure that the top 5 bits of this 4-byte utf8
+            // codepoint were not 0, as then we could have used
+            // one of the smaller encodings
+            if ((0 == (0x07 & s[0])) && (0 == (0x30 & s[1]))) {
+                return (void *)s;
+            }
+
+            // 4-byte utf8 code point (began with 0b11110xxx)
+            s += 4;
+        } else if (0xe0 == (0xf0 & *s)) {
+            // ensure each of the 2 following bytes in this 3-byte
+            // utf8 codepoint began with 0b10xxxxxx
+            if ((0x80 != (0xc0 & s[1])) || (0x80 != (0xc0 & s[2]))) {
+                return (void *)s;
+            }
+
+            // ensure that our utf8 codepoint ended after 3 bytes
+            if (0x80 == (0xc0 & s[3])) {
+                return (void *)s;
+            }
+
+            // ensure that the top 5 bits of this 3-byte utf8
+            // codepoint were not 0, as then we could have used
+            // one of the smaller encodings
+            if ((0 == (0x0f & s[0])) && (0 == (0x20 & s[1]))) {
+                return (void *)s;
+            }
+
+            // 3-byte utf8 code point (began with 0b1110xxxx)
+            s += 3;
+        } else if (0xc0 == (0xe0 & *s)) {
+            // ensure the 1 following byte in this 2-byte
+            // utf8 codepoint began with 0b10xxxxxx
+            if (0x80 != (0xc0 & s[1])) {
+                return (void *)s;
+            }
+
+            // ensure that our utf8 codepoint ended after 2 bytes
+            if (0x80 == (0xc0 & s[2])) {
+                return (void *)s;
+            }
+
+            // ensure that the top 4 bits of this 2-byte utf8
+            // codepoint were not 0, as then we could have used
+            // one of the smaller encodings
+            if (0 == (0x1e & s[0])) {
+                return (void *)s;
+            }
+
+            // 2-byte utf8 code point (began with 0b110xxxxx)
+            s += 2;
+        } else if (0x00 == (0x80 & *s)) {
+            // 1-byte ascii (began with 0b0xxxxxxx)
+            s += 1;
+        } else {
+            // we have an invalid 0b1xxxxxxx utf8 code point entry
+            return (void *)s;
+        }
+    }
+
+    return utf8_null;
+}
+
+void *utf8codepoint(const void *utf8_restrict str,
+                    utf8_int32_t *utf8_restrict out_codepoint)
+{
+    const char *s = (const char *)str;
+
+    if (0xf0 == (0xf8 & s[0])) {
+        // 4 byte utf8 codepoint
+        *out_codepoint = ((0x07 & s[0]) << 18) | ((0x3f & s[1]) << 12) |
+                         ((0x3f & s[2]) << 6) | (0x3f & s[3]);
+        s += 4;
+    } else if (0xe0 == (0xf0 & s[0])) {
+        // 3 byte utf8 codepoint
+        *out_codepoint =
+            ((0x0f & s[0]) << 12) | ((0x3f & s[1]) << 6) | (0x3f & s[2]);
+        s += 3;
+    } else if (0xc0 == (0xe0 & s[0])) {
+        // 2 byte utf8 codepoint
+        *out_codepoint = ((0x1f & s[0]) << 6) | (0x3f & s[1]);
+        s += 2;
+    } else {
+        // 1 byte utf8 codepoint otherwise
+        *out_codepoint = s[0];
+        s += 1;
+    }
+
+    return (void *)s;
+}
+
+size_t utf8codepointsize(utf8_int32_t chr)
+{
+    if (0 == ((utf8_int32_t)0xffffff80 & chr)) {
+        return 1;
+    } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) {
+        return 2;
+    } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) {
+        return 3;
+    } else { // if (0 == ((int)0xffe00000 & chr)) {
+        return 4;
+    }
+}
+
+void *utf8catcodepoint(void *utf8_restrict str, utf8_int32_t chr, size_t n)
+{
+    char *s = (char *)str;
+
+    if (0 == ((utf8_int32_t)0xffffff80 & chr)) {
+        // 1-byte/7-bit ascii
+        // (0b0xxxxxxx)
+        if (n < 1) {
+            return utf8_null;
+        }
+        s[0] = (char)chr;
+        s += 1;
+    } else if (0 == ((utf8_int32_t)0xfffff800 & chr)) {
+        // 2-byte/11-bit utf8 code point
+        // (0b110xxxxx 0b10xxxxxx)
+        if (n < 2) {
+            return utf8_null;
+        }
+        s[0] = 0xc0 | (char)(chr >> 6);
+        s[1] = 0x80 | (char)(chr & 0x3f);
+        s += 2;
+    } else if (0 == ((utf8_int32_t)0xffff0000 & chr)) {
+        // 3-byte/16-bit utf8 code point
+        // (0b1110xxxx 0b10xxxxxx 0b10xxxxxx)
+        if (n < 3) {
+            return utf8_null;
+        }
+        s[0] = 0xe0 | (char)(chr >> 12);
+        s[1] = 0x80 | (char)((chr >> 6) & 0x3f);
+        s[2] = 0x80 | (char)(chr & 0x3f);
+        s += 3;
+    } else { // if (0 == ((int)0xffe00000 & chr)) {
+        // 4-byte/21-bit utf8 code point
+        // (0b11110xxx 0b10xxxxxx 0b10xxxxxx 0b10xxxxxx)
+        if (n < 4) {
+            return utf8_null;
+        }
+        s[0] = 0xf0 | (char)(chr >> 18);
+        s[1] = 0x80 | (char)((chr >> 12) & 0x3f);
+        s[2] = 0x80 | (char)((chr >> 6) & 0x3f);
+        s[3] = 0x80 | (char)(chr & 0x3f);
+        s += 4;
+    }
+
+    return s;
+}
+
+int utf8islower(utf8_int32_t chr) { return chr != utf8uprcodepoint(chr); }
+
+int utf8isupper(utf8_int32_t chr) { return chr != utf8lwrcodepoint(chr); }
+
+void utf8lwr(void *utf8_restrict str)
+{
+    void *p, *pn;
+    utf8_int32_t cp;
+
+    p = (char *)str;
+    pn = utf8codepoint(p, &cp);
+
+    while (cp != 0) {
+        const utf8_int32_t lwr_cp = utf8lwrcodepoint(cp);
+        const size_t size = utf8codepointsize(lwr_cp);
+
+        if (lwr_cp != cp) {
+            utf8catcodepoint(p, lwr_cp, size);
+        }
+
+        p = pn;
+        pn = utf8codepoint(p, &cp);
+    }
+}
+
+void utf8upr(void *utf8_restrict str)
+{
+    void *p, *pn;
+    utf8_int32_t cp;
+
+    p = (char *)str;
+    pn = utf8codepoint(p, &cp);
+
+    while (cp != 0) {
+        const utf8_int32_t lwr_cp = utf8uprcodepoint(cp);
+        const size_t size = utf8codepointsize(lwr_cp);
+
+        if (lwr_cp != cp) {
+            utf8catcodepoint(p, lwr_cp, size);
+        }
+
+        p = pn;
+        pn = utf8codepoint(p, &cp);
+    }
+}
+
+utf8_int32_t utf8lwrcodepoint(utf8_int32_t cp)
+{
+    if (((0x0041 <= cp) && (0x005a >= cp)) ||
+        ((0x00c0 <= cp) && (0x00d6 >= cp)) ||
+        ((0x00d8 <= cp) && (0x00de >= cp)) ||
+        ((0x0391 <= cp) && (0x03a1 >= cp)) ||
+        ((0x03a3 <= cp) && (0x03ab >= cp))) {
+        cp += 32;
+    } else if (((0x0100 <= cp) && (0x012f >= cp)) ||
+               ((0x0132 <= cp) && (0x0137 >= cp)) ||
+               ((0x014a <= cp) && (0x0177 >= cp)) ||
+               ((0x0182 <= cp) && (0x0185 >= cp)) ||
+               ((0x01a0 <= cp) && (0x01a5 >= cp)) ||
+               ((0x01de <= cp) && (0x01ef >= cp)) ||
+               ((0x01f8 <= cp) && (0x021f >= cp)) ||
+               ((0x0222 <= cp) && (0x0233 >= cp)) ||
+               ((0x0246 <= cp) && (0x024f >= cp)) ||
+               ((0x03d8 <= cp) && (0x03ef >= cp))) {
+        cp |= 0x1;
+    } else if (((0x0139 <= cp) && (0x0148 >= cp)) ||
+               ((0x0179 <= cp) && (0x017e >= cp)) ||
+               ((0x01af <= cp) && (0x01b0 >= cp)) ||
+               ((0x01b3 <= cp) && (0x01b6 >= cp)) ||
+               ((0x01cd <= cp) && (0x01dc >= cp))) {
+        cp += 1;
+        cp &= ~0x1;
+    } else {
+        switch (cp) {
+            default: break;
+            case 0x0178: cp = 0x00ff; break;
+            case 0x0243: cp = 0x0180; break;
+            case 0x018e: cp = 0x01dd; break;
+            case 0x023d: cp = 0x019a; break;
+            case 0x0220: cp = 0x019e; break;
+            case 0x01b7: cp = 0x0292; break;
+            case 0x01c4: cp = 0x01c6; break;
+            case 0x01c7: cp = 0x01c9; break;
+            case 0x01ca: cp = 0x01cc; break;
+            case 0x01f1: cp = 0x01f3; break;
+            case 0x01f7: cp = 0x01bf; break;
+            case 0x0187: cp = 0x0188; break;
+            case 0x018b: cp = 0x018c; break;
+            case 0x0191: cp = 0x0192; break;
+            case 0x0198: cp = 0x0199; break;
+            case 0x01a7: cp = 0x01a8; break;
+            case 0x01ac: cp = 0x01ad; break;
+            case 0x01af: cp = 0x01b0; break;
+            case 0x01b8: cp = 0x01b9; break;
+            case 0x01bc: cp = 0x01bd; break;
+            case 0x01f4: cp = 0x01f5; break;
+            case 0x023b: cp = 0x023c; break;
+            case 0x0241: cp = 0x0242; break;
+            case 0x03fd: cp = 0x037b; break;
+            case 0x03fe: cp = 0x037c; break;
+            case 0x03ff: cp = 0x037d; break;
+            case 0x037f: cp = 0x03f3; break;
+            case 0x0386: cp = 0x03ac; break;
+            case 0x0388: cp = 0x03ad; break;
+            case 0x0389: cp = 0x03ae; break;
+            case 0x038a: cp = 0x03af; break;
+            case 0x038c: cp = 0x03cc; break;
+            case 0x038e: cp = 0x03cd; break;
+            case 0x038f: cp = 0x03ce; break;
+            case 0x0370: cp = 0x0371; break;
+            case 0x0372: cp = 0x0373; break;
+            case 0x0376: cp = 0x0377; break;
+            case 0x03f4: cp = 0x03d1; break;
+            case 0x03cf: cp = 0x03d7; break;
+            case 0x03f9: cp = 0x03f2; break;
+            case 0x03f7: cp = 0x03f8; break;
+            case 0x03fa: cp = 0x03fb; break;
+        };
+    }
+
+    return cp;
+}
+
+utf8_int32_t utf8uprcodepoint(utf8_int32_t cp)
+{
+    if (((0x0061 <= cp) && (0x007a >= cp)) ||
+        ((0x00e0 <= cp) && (0x00f6 >= cp)) ||
+        ((0x00f8 <= cp) && (0x00fe >= cp)) ||
+        ((0x03b1 <= cp) && (0x03c1 >= cp)) ||
+        ((0x03c3 <= cp) && (0x03cb >= cp))) {
+        cp -= 32;
+    } else if (((0x0100 <= cp) && (0x012f >= cp)) ||
+               ((0x0132 <= cp) && (0x0137 >= cp)) ||
+               ((0x014a <= cp) && (0x0177 >= cp)) ||
+               ((0x0182 <= cp) && (0x0185 >= cp)) ||
+               ((0x01a0 <= cp) && (0x01a5 >= cp)) ||
+               ((0x01de <= cp) && (0x01ef >= cp)) ||
+               ((0x01f8 <= cp) && (0x021f >= cp)) ||
+               ((0x0222 <= cp) && (0x0233 >= cp)) ||
+               ((0x0246 <= cp) && (0x024f >= cp)) ||
+               ((0x03d8 <= cp) && (0x03ef >= cp))) {
+        cp &= ~0x1;
+    } else if (((0x0139 <= cp) && (0x0148 >= cp)) ||
+               ((0x0179 <= cp) && (0x017e >= cp)) ||
+               ((0x01af <= cp) && (0x01b0 >= cp)) ||
+               ((0x01b3 <= cp) && (0x01b6 >= cp)) ||
+               ((0x01cd <= cp) && (0x01dc >= cp))) {
+        cp -= 1;
+        cp |= 0x1;
+    } else {
+        switch (cp) {
+            default: break;
+            case 0x00ff: cp = 0x0178; break;
+            case 0x0180: cp = 0x0243; break;
+            case 0x01dd: cp = 0x018e; break;
+            case 0x019a: cp = 0x023d; break;
+            case 0x019e: cp = 0x0220; break;
+            case 0x0292: cp = 0x01b7; break;
+            case 0x01c6: cp = 0x01c4; break;
+            case 0x01c9: cp = 0x01c7; break;
+            case 0x01cc: cp = 0x01ca; break;
+            case 0x01f3: cp = 0x01f1; break;
+            case 0x01bf: cp = 0x01f7; break;
+            case 0x0188: cp = 0x0187; break;
+            case 0x018c: cp = 0x018b; break;
+            case 0x0192: cp = 0x0191; break;
+            case 0x0199: cp = 0x0198; break;
+            case 0x01a8: cp = 0x01a7; break;
+            case 0x01ad: cp = 0x01ac; break;
+            case 0x01b0: cp = 0x01af; break;
+            case 0x01b9: cp = 0x01b8; break;
+            case 0x01bd: cp = 0x01bc; break;
+            case 0x01f5: cp = 0x01f4; break;
+            case 0x023c: cp = 0x023b; break;
+            case 0x0242: cp = 0x0241; break;
+            case 0x037b: cp = 0x03fd; break;
+            case 0x037c: cp = 0x03fe; break;
+            case 0x037d: cp = 0x03ff; break;
+            case 0x03f3: cp = 0x037f; break;
+            case 0x03ac: cp = 0x0386; break;
+            case 0x03ad: cp = 0x0388; break;
+            case 0x03ae: cp = 0x0389; break;
+            case 0x03af: cp = 0x038a; break;
+            case 0x03cc: cp = 0x038c; break;
+            case 0x03cd: cp = 0x038e; break;
+            case 0x03ce: cp = 0x038f; break;
+            case 0x0371: cp = 0x0370; break;
+            case 0x0373: cp = 0x0372; break;
+            case 0x0377: cp = 0x0376; break;
+            case 0x03d1: cp = 0x03f4; break;
+            case 0x03d7: cp = 0x03cf; break;
+            case 0x03f2: cp = 0x03f9; break;
+            case 0x03f8: cp = 0x03f7; break;
+            case 0x03fb: cp = 0x03fa; break;
+        };
+    }
+
+    return cp;
+}
+
+#undef utf8_restrict
+#undef utf8_null
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+#endif // SHEREDOM_UTF8_H_INCLUDED
+
+
+/********************************************************
+   End of file "utf8.h"
+ ********************************************************/
+
+
+/********************************************************
+   Begin of file "string_buffer.h"
+ ********************************************************/
+
+#ifndef STRING_BUFFER_H
+#define STRING_BUFFER_H
+
+/* #include "fort_utils.h" */ /* Commented by amalgamation script */
+
+
+/*****************************************************************************
+ *               STRING BUFFER
+ * ***************************************************************************/
+
+struct f_string_buffer {
+    union {
+        char *cstr;
+#ifdef FT_HAVE_WCHAR
+        wchar_t *wstr;
+#endif
+#ifdef FT_HAVE_UTF8
+        void *u8str;
+#endif
+        void *data;
+    } str;
+    size_t data_sz;
+    enum f_string_type type;
+};
+
+FT_INTERNAL
+f_string_buffer_t *create_string_buffer(size_t number_of_chars, enum 
f_string_type type);
+
+FT_INTERNAL
+void destroy_string_buffer(f_string_buffer_t *buffer);
+
+FT_INTERNAL
+f_string_buffer_t *copy_string_buffer(const f_string_buffer_t *buffer);
+
+FT_INTERNAL
+f_status realloc_string_buffer_without_copy(f_string_buffer_t *buffer);
+
+FT_INTERNAL
+f_status fill_buffer_from_string(f_string_buffer_t *buffer, const char *str);
+
+#ifdef FT_HAVE_WCHAR
+FT_INTERNAL
+f_status fill_buffer_from_wstring(f_string_buffer_t *buffer, const wchar_t 
*str);
+#endif /* FT_HAVE_WCHAR */
+
+#ifdef FT_HAVE_UTF8
+FT_INTERNAL
+f_status fill_buffer_from_u8string(f_string_buffer_t *buffer, const void *str);
+#endif /* FT_HAVE_UTF8 */
+
+FT_INTERNAL
+size_t buffer_text_visible_width(const f_string_buffer_t *buffer);
+
+FT_INTERNAL
+size_t buffer_text_visible_height(const f_string_buffer_t *buffer);
+
+FT_INTERNAL
+size_t string_buffer_cod_width_capacity(const f_string_buffer_t *buffer);
+
+FT_INTERNAL
+size_t string_buffer_raw_capacity(const f_string_buffer_t *buffer);
+
+FT_INTERNAL
+size_t string_buffer_width_capacity(const f_string_buffer_t *buffer);
+
+FT_INTERNAL
+void *buffer_get_data(f_string_buffer_t *buffer);
+
+FT_INTERNAL
+int buffer_check_align(f_string_buffer_t *buffer);
+
+FT_INTERNAL
+int buffer_printf(f_string_buffer_t *buffer, size_t buffer_row, 
f_conv_context_t *cntx, size_t cod_width,
+                  const char *content_style_tag, const char 
*reset_content_style_tag);
+
+#ifdef FT_HAVE_UTF8
+FT_INTERNAL
+void buffer_set_u8strwid_func(int (*u8strwid)(const void *beg, const void 
*end, size_t *width));
+#endif /* FT_HAVE_UTF8 */
+
+
+#endif /* STRING_BUFFER_H */
+
+/********************************************************
+   End of file "string_buffer.h"
+ ********************************************************/
+
+
+/********************************************************
+   Begin of file "properties.h"
+ ********************************************************/
+
+#ifndef PROPERTIES_H
+#define PROPERTIES_H
+
+/* #include "fort_utils.h" */ /* Commented by amalgamation script */
+#include <stdint.h>
+#include <limits.h>
+
+#define PROP_IS_SET(ft_props, property) ((ft_props) & (property))
+#define PROP_SET(ft_props, property) ((ft_props) |=(property))
+#define PROP_UNSET(ft_props, property) ((ft_props) &= ~((uint32_t)(property)))
+
+#define TEXT_STYLE_TAG_MAX_SIZE (64 * 2)
+
+FT_INTERNAL
+void get_style_tag_for_cell(const f_table_properties_t *props,
+                            size_t row, size_t col, char *style_tag, size_t 
sz);
+
+FT_INTERNAL
+void get_reset_style_tag_for_cell(const f_table_properties_t *props,
+                                  size_t row, size_t col, char *style_tag, 
size_t sz);
+
+FT_INTERNAL
+void get_style_tag_for_content(const f_table_properties_t *props,
+                               size_t row, size_t col, char *style_tag, size_t 
sz);
+
+FT_INTERNAL
+void get_reset_style_tag_for_content(const f_table_properties_t *props,
+                                     size_t row, size_t col, char *style_tag, 
size_t sz);
+
+
+struct f_cell_props {
+    size_t cell_row;
+    size_t cell_col;
+    uint32_t properties_flags;
+
+    unsigned int col_min_width;
+    enum ft_text_alignment align;
+    unsigned int cell_padding_top;
+    unsigned int cell_padding_bottom;
+    unsigned int cell_padding_left;
+    unsigned int cell_padding_right;
+    unsigned int cell_empty_string_height;
+    enum ft_row_type row_type;
+    unsigned int content_fg_color_number;
+    unsigned int content_bg_color_number;
+    unsigned int cell_bg_color_number;
+    enum ft_text_style cell_text_style;
+    enum ft_text_style content_text_style;
+};
+
+typedef struct f_cell_props f_cell_props_t;
+typedef f_vector_t f_cell_prop_container_t;
+
+FT_INTERNAL
+f_cell_prop_container_t *create_cell_prop_container(void);
+
+FT_INTERNAL
+void destroy_cell_prop_container(f_cell_prop_container_t *cont);
+
+FT_INTERNAL
+const f_cell_props_t *cget_cell_prop(const f_cell_prop_container_t *cont, 
size_t row, size_t col);
+
+FT_INTERNAL
+f_cell_props_t *get_cell_prop_and_create_if_not_exists(f_cell_prop_container_t 
*cont, size_t row, size_t col);
+
+FT_INTERNAL
+f_status set_cell_property(f_cell_prop_container_t *cont, size_t row, size_t 
col, uint32_t property, int value);
+
+FT_INTERNAL
+int get_cell_property_hierarchically(const f_table_properties_t *properties, 
size_t row, size_t column, uint32_t property);
+
+FT_INTERNAL
+f_status set_default_cell_property(uint32_t property, int value);
+
+
+/*         TABLE BORDER DESСRIPTION
+ *
+ *
+ *   TL TT TT TT TV TT TT TT TT TT TT TT TR
+ *   LL          IV                      RR
+ *   LL          IV                      RR
+ *   LH IH IH IH II IH IH IH TI IH IH IH RH
+ *   LL          IV          IV          RR
+ *   LL          IV          IV          RR
+ *   LL          LI IH IH IH RI          RH
+ *   LL          IV          IV          RR
+ *   LL          IV          IV          RR
+ *   LH IH IH IH BI IH IH IH II IH IH IH RH
+ *   LL                      IV          RR
+ *   LL                      IV          RR
+ *   BL BB BB BB BV BB BB BB BV BB BB BB BR
+ */
+
+
+/*      HORIZONTAL SEPARATOR DESCRIPTION
+ *
+ *
+ *   TL TT TT TT TV TT TT TT TV TT TT TT TR        <----- TOP_SEPARATOR
+ *   LL          IV          IV          RR
+ *   LH IH IH IH II IH IH IH II IH IH IH RH        <----- INSIDE_SEPARATOR
+ *   LL          IV          IV          RR
+ *   BL BB BB BB BV BB BB BB BV BB BB BB BR        <----- BOTTOM_SEPARATOR
+ */
+
+enum f_hor_separator_pos {
+    TOP_SEPARATOR,
+    INSIDE_SEPARATOR,
+    BOTTOM_SEPARATOR
+};
+
+enum f_border_item_pos {
+    TL_bip = 0,
+    TT_bip = 1,
+    TV_bip = 2,
+    TR_bip = 3,
+
+    LL_bip = 4,
+    IV_bip = 5,
+    RR_bip = 6,
+
+    LH_bip = 7,
+    IH_bip = 8,
+    II_bip = 9,
+    RH_bip = 10,
+
+    BL_bip = 11,
+    BB_bip = 12,
+    BV_bip = 13,
+    BR_bip = 14,
+
+    LI_bip = 15,
+    TI_bip = 16,
+    RI_bip = 17,
+    BI_bip = 18,
+
+    BORDER_ITEM_POS_SIZE
+};
+
+
+enum f_separator_item_pos {
+    LH_sip = 0,
+    IH_sip = 1,
+    II_sip = 2,
+    RH_sip = 3,
+
+    TI_sip = 4,
+    BI_sip = 5,
+
+    SEPARATOR_ITEM_POS_SIZE
+};
+
+
+struct fort_border_style {
+    const char *border_chars[BORDER_ITEM_POS_SIZE];
+    const char *header_border_chars[BORDER_ITEM_POS_SIZE];
+    const char *separator_chars[SEPARATOR_ITEM_POS_SIZE];
+};
+extern struct fort_border_style FORT_BASIC_STYLE;
+extern struct fort_border_style FORT_BASIC2_STYLE;
+extern struct fort_border_style FORT_SIMPLE_STYLE;
+extern struct fort_border_style FORT_PLAIN_STYLE;
+extern struct fort_border_style FORT_DOT_STYLE;
+extern struct fort_border_style FORT_EMPTY_STYLE;
+extern struct fort_border_style FORT_EMPTY2_STYLE;
+extern struct fort_border_style FORT_SOLID_STYLE;
+extern struct fort_border_style FORT_SOLID_ROUND_STYLE;
+extern struct fort_border_style FORT_NICE_STYLE;
+extern struct fort_border_style FORT_DOUBLE_STYLE;
+extern struct fort_border_style FORT_DOUBLE2_STYLE;
+extern struct fort_border_style FORT_BOLD_STYLE;
+extern struct fort_border_style FORT_BOLD2_STYLE;
+extern struct fort_border_style FORT_FRAME_STYLE;
+
+
+struct fort_entire_table_properties {
+    unsigned int left_margin;
+    unsigned int top_margin;
+    unsigned int right_margin;
+    unsigned int bottom_margin;
+    enum ft_adding_strategy add_strategy;
+};
+typedef struct fort_entire_table_properties fort_entire_table_properties_t;
+extern fort_entire_table_properties_t g_entire_table_properties;
+
+FT_INTERNAL
+f_status set_entire_table_property(f_table_properties_t *table_properties, 
uint32_t property, int value);
+
+FT_INTERNAL
+f_status set_default_entire_table_property(uint32_t property, int value);
+
+struct f_table_properties {
+    struct fort_border_style border_style;
+    f_cell_prop_container_t *cell_properties;
+    fort_entire_table_properties_t entire_table_properties;
+};
+extern f_table_properties_t g_table_properties;
+
+FT_INTERNAL
+size_t max_border_elem_strlen(struct f_table_properties *);
+
+FT_INTERNAL
+f_table_properties_t *create_table_properties(void);
+
+FT_INTERNAL
+void destroy_table_properties(f_table_properties_t *properties);
+
+FT_INTERNAL
+f_table_properties_t *copy_table_properties(const f_table_properties_t 
*property);
+
+#endif /* PROPERTIES_H */
+
+/********************************************************
+   End of file "properties.h"
+ ********************************************************/
+
+
+/********************************************************
+   Begin of file "cell.h"
+ ********************************************************/
+
+#ifndef CELL_H
+#define CELL_H
+
+/* #include "fort_utils.h" */ /* Commented by amalgamation script */
+
+FT_INTERNAL
+f_cell_t *create_cell(void);
+
+FT_INTERNAL
+void destroy_cell(f_cell_t *cell);
+
+FT_INTERNAL
+f_cell_t *copy_cell(f_cell_t *cell);
+
+FT_INTERNAL
+size_t cell_vis_width(const f_cell_t *cell, const f_context_t *context);
+
+FT_INTERNAL
+size_t cell_invis_codes_width(const f_cell_t *cell, const f_context_t 
*context);
+
+FT_INTERNAL
+size_t hint_height_cell(const f_cell_t *cell, const f_context_t *context);
+
+FT_INTERNAL
+void set_cell_type(f_cell_t *cell, enum f_cell_type type);
+
+FT_INTERNAL
+enum f_cell_type get_cell_type(const f_cell_t *cell);
+
+FT_INTERNAL
+int cell_printf(f_cell_t *cell, size_t row, f_conv_context_t *cntx, size_t 
cod_width);
+
+FT_INTERNAL
+f_status fill_cell_from_string(f_cell_t *cell, const char *str);
+
+#ifdef FT_HAVE_WCHAR
+FT_INTERNAL
+f_status fill_cell_from_wstring(f_cell_t *cell, const wchar_t *str);
+#endif
+
+FT_INTERNAL
+f_status fill_cell_from_buffer(f_cell_t *cell, const f_string_buffer_t *buf);
+
+FT_INTERNAL
+f_string_buffer_t *cell_get_string_buffer(f_cell_t *cell);
+
+#endif /* CELL_H */
+
+/********************************************************
+   End of file "cell.h"
+ ********************************************************/
+
+
+/********************************************************
+   Begin of file "row.h"
+ ********************************************************/
+
+#ifndef ROW_H
+#define ROW_H
+
+/* #include "fort_utils.h" */ /* Commented by amalgamation script */
+#include "fort.h"
+#include <stdarg.h>
+/* #include "properties.h" */ /* Commented by amalgamation script */
+#ifdef FT_HAVE_WCHAR
+#include <wchar.h>
+#endif
+
+FT_INTERNAL
+f_row_t *create_row(void);
+
+FT_INTERNAL
+void destroy_row(f_row_t *row);
+
+FT_INTERNAL
+f_row_t *copy_row(f_row_t *row);
+
+FT_INTERNAL
+f_row_t *split_row(f_row_t *row, size_t pos);
+
+// Delete range [left; right] of cells (both ends included)
+FT_INTERNAL
+int ft_row_erase_range(f_row_t *row, size_t left, size_t right);
+
+FT_INTERNAL
+f_row_t *create_row_from_string(const char *str);
+
+FT_INTERNAL
+f_row_t *create_row_from_fmt_string(const struct f_string_view  *fmt, va_list 
*va_args);
+
+FT_INTERNAL
+size_t columns_in_row(const f_row_t *row);
+
+FT_INTERNAL
+f_cell_t *get_cell(f_row_t *row, size_t col);
+
+FT_INTERNAL
+const f_cell_t *get_cell_c(const f_row_t *row, size_t col);
+
+FT_INTERNAL
+f_cell_t *get_cell_and_create_if_not_exists(f_row_t *row, size_t col);
+
+FT_INTERNAL
+f_cell_t *create_cell_in_position(f_row_t *row, size_t col);
+
+FT_INTERNAL
+f_status swap_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos);
+
+FT_INTERNAL
+f_status insert_row(f_row_t *cur_row, f_row_t *ins_row, size_t pos);
+
+FT_INTERNAL
+size_t group_cell_number(const f_row_t *row, size_t master_cell_col);
+
+FT_INTERNAL
+int get_row_cell_types(const f_row_t *row, enum f_cell_type *types, size_t 
types_sz);
+
+FT_INTERNAL
+f_status row_set_cell_span(f_row_t *row, size_t cell_column, size_t hor_span);
+
+FT_INTERNAL
+int print_row_separator(f_conv_context_t *cntx,
+                        const size_t *col_width_arr, size_t cols,
+                        const f_row_t *upper_row, const f_row_t *lower_row,
+                        enum f_hor_separator_pos separatorPos, const 
f_separator_t *sep);
+
+FT_INTERNAL
+int snprintf_row(const f_row_t *row, f_conv_context_t *cntx, size_t 
*col_width_arr, size_t col_width_arr_sz,
+                 size_t row_height);
+
+#ifdef FT_HAVE_WCHAR
+FT_INTERNAL
+f_row_t *create_row_from_wstring(const wchar_t *str);
+#endif
+
+
+#endif /* ROW_H */
+
+/********************************************************
+   End of file "row.h"
+ ********************************************************/
+
+
+/********************************************************
+   Begin of file "table.h"
+ ********************************************************/
+
+#ifndef TABLE_H
+#define TABLE_H
+
+/* #include "fort_utils.h" */ /* Commented by amalgamation script */
+
+struct ft_table {
+    f_vector_t *rows;
+    f_table_properties_t *properties;
+    f_string_buffer_t *conv_buffer;
+    size_t cur_row;
+    size_t cur_col;
+    f_vector_t *separators;
+};
+
+FT_INTERNAL
+f_separator_t *create_separator(int enabled);
+
+FT_INTERNAL
+void destroy_separator(f_separator_t *sep);
+
+FT_INTERNAL
+f_separator_t *copy_separator(f_separator_t *sep);
+
+FT_INTERNAL
+f_status get_table_sizes(const ft_table_t *table, size_t *rows, size_t *cols);
+
+FT_INTERNAL
+f_row_t *get_row(ft_table_t *table, size_t row);
+
+FT_INTERNAL
+const f_row_t *get_row_c(const ft_table_t *table, size_t row);
+
+FT_INTERNAL
+f_row_t *get_row_and_create_if_not_exists(ft_table_t *table, size_t row);
+
+FT_INTERNAL
+f_string_buffer_t *get_cur_str_buffer_and_create_if_not_exists(ft_table_t 
*table);
+
+
+FT_INTERNAL
+f_status table_rows_and_cols_geometry(const ft_table_t *table,
+                                      size_t **col_width_arr_p, size_t 
*col_width_arr_sz,
+                                      size_t **row_height_arr_p, size_t 
*row_height_arr_sz,
+                                      enum f_geometry_type geom);
+
+FT_INTERNAL
+f_status table_geometry(const ft_table_t *table, size_t *height, size_t 
*width);
+
+/*
+ * Returns geometry in codepoints(characters) (include codepoints of invisible
+ * elements: e.g. styles tags).
+ */
+FT_INTERNAL
+f_status table_internal_codepoints_geometry(const ft_table_t *table, size_t 
*height, size_t *width);
+
+#endif /* TABLE_H */
+
+/********************************************************
+   End of file "table.h"
+ ********************************************************/
+
+
+/********************************************************
+   Begin of file "cell.c"
+ ********************************************************/
+
+/* #include "cell.h" */ /* Commented by amalgamation script */
+/* #include "properties.h" */ /* Commented by amalgamation script */
+/* #include "string_buffer.h" */ /* Commented by amalgamation script */
+#include <assert.h>
+
+struct f_cell {
+    f_string_buffer_t *str_buffer;
+    enum f_cell_type cell_type;
+};
+
+FT_INTERNAL
+f_cell_t *create_cell(void)
+{
+    f_cell_t *cell = (f_cell_t *)F_CALLOC(sizeof(f_cell_t), 1);
+    if (cell == NULL)
+        return NULL;
+    cell->str_buffer = create_string_buffer(DEFAULT_STR_BUF_SIZE, CHAR_BUF);
+    if (cell->str_buffer == NULL) {
+        F_FREE(cell);
+        return NULL;
+    }
+    cell->cell_type = COMMON_CELL;
+    return cell;
+}
+
+FT_INTERNAL
+void destroy_cell(f_cell_t *cell)
+{
+    if (cell == NULL)
+        return;
+    destroy_string_buffer(cell->str_buffer);
+    F_FREE(cell);
+}
+
+FT_INTERNAL
+f_cell_t *copy_cell(f_cell_t *cell)
+{
+    assert(cell);
+
+    f_cell_t *result = create_cell();
+    if (result == NULL)
+        return NULL;
+    destroy_string_buffer(result->str_buffer);
+    result->str_buffer = copy_string_buffer(cell->str_buffer);
+    if (result->str_buffer == NULL) {
+        destroy_cell(result);
+        return NULL;
+    }
+    result->cell_type = cell->cell_type;
+    return result;
+}
+
+FT_INTERNAL
+void set_cell_type(f_cell_t *cell, enum f_cell_type type)
+{
+    assert(cell);
+    cell->cell_type = type;
+}
+
+FT_INTERNAL
+enum f_cell_type get_cell_type(const f_cell_t *cell)
+{
+    assert(cell);
+    return cell->cell_type;
+}
+
+FT_INTERNAL
+size_t cell_vis_width(const f_cell_t *cell, const f_context_t *context)
+{
+    /* todo:
+     * At the moment min width includes paddings. Maybe it is better that min 
width weren't include
+     * paddings but be min width of the cell content without padding
+     */
+
+    assert(cell);
+    assert(context);
+
+    f_table_properties_t *properties = context->table_properties;
+    size_t row = context->row;
+    size_t column = context->column;
+
+    size_t padding_left = get_cell_property_hierarchically(properties, row, 
column, FT_CPROP_LEFT_PADDING);
+    size_t padding_right = get_cell_property_hierarchically(properties, row, 
column, FT_CPROP_RIGHT_PADDING);
+    size_t result = padding_left + padding_right;
+    if (cell->str_buffer && cell->str_buffer->str.data) {
+        result += buffer_text_visible_width(cell->str_buffer);
+    }
+    result = MAX(result, (size_t)get_cell_property_hierarchically(properties, 
row, column, FT_CPROP_MIN_WIDTH));
+    return result;
+}
+
+FT_INTERNAL
+size_t cell_invis_codes_width(const f_cell_t *cell, const f_context_t *context)
+{
+    assert(cell);
+    assert(context);
+
+    f_table_properties_t *properties = context->table_properties;
+    size_t row = context->row;
+    size_t column = context->column;
+
+    size_t result = 0;
+    char cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
+    get_style_tag_for_cell(properties, row, column, cell_style_tag, 
TEXT_STYLE_TAG_MAX_SIZE);
+    result += strlen(cell_style_tag);
+
+    char reset_cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
+    get_reset_style_tag_for_cell(properties, row, column, 
reset_cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE);
+    result += strlen(reset_cell_style_tag);
+
+    char content_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
+    get_style_tag_for_content(properties, row, column, content_style_tag, 
TEXT_STYLE_TAG_MAX_SIZE);
+    result += strlen(content_style_tag);
+
+    char reset_content_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
+    get_reset_style_tag_for_content(properties, row, column, 
reset_content_style_tag, TEXT_STYLE_TAG_MAX_SIZE);
+    result += strlen(reset_content_style_tag);
+    return result;
+}
+
+FT_INTERNAL
+size_t hint_height_cell(const f_cell_t *cell, const f_context_t *context)
+{
+    assert(cell);
+    assert(context);
+    f_table_properties_t *properties = context->table_properties;
+    size_t row = context->row;
+    size_t column = context->column;
+
+    size_t padding_top = get_cell_property_hierarchically(properties, row, 
column, FT_CPROP_TOP_PADDING);
+    size_t padding_bottom = get_cell_property_hierarchically(properties, row, 
column, FT_CPROP_BOTTOM_PADDING);
+    size_t empty_string_height = get_cell_property_hierarchically(properties, 
row, column, FT_CPROP_EMPTY_STR_HEIGHT);
+
+    size_t result = padding_top + padding_bottom;
+    if (cell->str_buffer && cell->str_buffer->str.data) {
+        size_t text_height = buffer_text_visible_height(cell->str_buffer);
+        result += text_height == 0 ? empty_string_height : text_height;
+    }
+    return result;
+}
+
+
+FT_INTERNAL
+int cell_printf(f_cell_t *cell, size_t row, f_conv_context_t *cntx, size_t 
vis_width)
+{
+    const f_context_t *context = cntx->cntx;
+    size_t buf_len = vis_width;
+
+    if (cell == NULL || (vis_width < cell_vis_width(cell, context))) {
+        return -1;
+    }
+
+    f_table_properties_t *properties = context->table_properties;
+    unsigned int padding_top = get_cell_property_hierarchically(properties, 
context->row, context->column, FT_CPROP_TOP_PADDING);
+    unsigned int padding_left = get_cell_property_hierarchically(properties, 
context->row, context->column, FT_CPROP_LEFT_PADDING);
+    unsigned int padding_right = get_cell_property_hierarchically(properties, 
context->row, context->column, FT_CPROP_RIGHT_PADDING);
+
+    size_t written = 0;
+    size_t invisible_written = 0;
+    int tmp = 0;
+
+    /* todo: Dirty hack with changing buf_len! need refactoring. */
+    /* Also maybe it is better to move all struff with colors to buffers? */
+    char cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
+    get_style_tag_for_cell(properties, context->row, context->column, 
cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE);
+    buf_len += strlen(cell_style_tag);
+
+    char reset_cell_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
+    get_reset_style_tag_for_cell(properties, context->row, context->column, 
reset_cell_style_tag, TEXT_STYLE_TAG_MAX_SIZE);
+    buf_len += strlen(reset_cell_style_tag);
+
+    char content_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
+    get_style_tag_for_content(properties, context->row, context->column, 
content_style_tag, TEXT_STYLE_TAG_MAX_SIZE);
+    buf_len += strlen(content_style_tag);
+
+    char reset_content_style_tag[TEXT_STYLE_TAG_MAX_SIZE];
+    get_reset_style_tag_for_content(properties, context->row, context->column, 
reset_content_style_tag, TEXT_STYLE_TAG_MAX_SIZE);
+    buf_len += strlen(reset_content_style_tag);
+
+    /*    CELL_STYLE_T   LEFT_PADDING   CONTENT_STYLE_T  CONTENT   
RESET_CONTENT_STYLE_T    RIGHT_PADDING   RESET_CELL_STYLE_T
+     *  |              |              |                |         |             
          |                |                    |
+     *        L1                                                               
                                     R1
+     *                     L2                                                  
                 R2
+     *                                     L3                               R3
+     */
+
+    size_t L2 = padding_left;
+
+    size_t R2 = padding_right;
+    size_t R3 = strlen(reset_cell_style_tag);
+
+#define TOTAL_WRITTEN (written + invisible_written)
+#define RIGHT (padding_right + extra_right)
+
+#define WRITE_CELL_STYLE_TAG        
CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, cell_style_tag))
+#define WRITE_RESET_CELL_STYLE_TAG  
CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, 
reset_cell_style_tag))
+#define WRITE_CONTENT_STYLE_TAG        
CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, content_style_tag))
+#define WRITE_RESET_CONTENT_STYLE_TAG  
CHCK_RSLT_ADD_TO_INVISIBLE_WRITTEN(print_n_strings(cntx, 1, 
reset_content_style_tag))
+
+    if (row >= hint_height_cell(cell, context)
+        || row < padding_top
+        || row >= (padding_top + 
buffer_text_visible_height(cell->str_buffer))) {
+        WRITE_CELL_STYLE_TAG;
+        WRITE_CONTENT_STYLE_TAG;
+        WRITE_RESET_CONTENT_STYLE_TAG;
+        CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, buf_len - TOTAL_WRITTEN 
- R3, FT_SPACE));
+        WRITE_RESET_CELL_STYLE_TAG;
+
+        return (int)TOTAL_WRITTEN;
+    }
+
+    WRITE_CELL_STYLE_TAG;
+    CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, L2, FT_SPACE));
+    if (cell->str_buffer) {
+        CHCK_RSLT_ADD_TO_WRITTEN(buffer_printf(cell->str_buffer, row - 
padding_top, cntx, vis_width - L2 - R2, content_style_tag, 
reset_content_style_tag));
+    } else {
+        WRITE_CONTENT_STYLE_TAG;
+        WRITE_RESET_CONTENT_STYLE_TAG;
+        CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, vis_width - L2 - R2, 
FT_SPACE));
+    }
+    CHCK_RSLT_ADD_TO_WRITTEN(print_n_strings(cntx, R2, FT_SPACE));
+    WRITE_RESET_CELL_STYLE_TAG;
+
+    return (int)TOTAL_WRITTEN;
+
+clear:
+    return -1;
+#undef WRITE_CELL_STYLE_TAG
+#undef WRITE_RESET_CELL_STYLE_TAG
+#undef WRITE_CONTENT_STYLE_TAG
+#undef WRITE_RESET_CONTENT_STYLE_TAG
+#undef TOTAL_WRITTEN
+#undef RIGHT
+}
+
+FT_INTERNAL
+f_status fill_cell_from_string(f_cell_t *cell, const char *str)
+{
+    assert(str);
+    assert(cell);
+
+    return fill_buffer_from_string(cell->str_buffer, str);
+}
+
+#ifdef FT_HAVE_WCHAR
+FT_INTERNAL
+f_status fill_cell_from_wstring(f_cell_t *cell, const wchar_t *str)
+{
+    assert(str);
+    assert(cell);
+
+    return fill_buffer_from_wstring(cell->str_buffer, str);
+}
+#endif
+
+#ifdef FT_HAVE_UTF8
+static
+f_status fill_cell_from_u8string(f_cell_t *cell, const void *str)
+{
+    assert(str);
+    assert(cell);
+    return fill_buffer_from_u8string(cell->str_buffer, str);
+}
+#endif /* FT_HAVE_UTF8 */
+
+FT_INTERNAL
+f_string_buffer_t *cell_get_string_buffer(f_cell_t *cell)
+{
+    assert(cell);
+    assert(cell->str_buffer);
+    return cell->str_buffer;
+}
+
+FT_INTERNAL
+f_status fill_cell_from_buffer(f_cell_t *cell, const f_string_buffer_t *buffer)
+{
+    assert(cell);
+    assert(buffer);
+    switch (buffer->type) {
+        case CHAR_BUF:
+            return fill_cell_from_string(cell, buffer->str.cstr);
+#ifdef FT_HAVE_WCHAR
+        case W_CHAR_BUF:
+            return fill_cell_from_wstring(cell, buffer->str.wstr);
+#endif /* FT_HAVE_WCHAR */
+#ifdef FT_HAVE_UTF8
+        case UTF8_BUF:
+            return fill_cell_from_u8string(cell, buffer->str.u8str);
+#endif /* FT_HAVE_UTF8 */
+        default:
+            assert(0);
+            return FT_GEN_ERROR;
+    }
+
+}
+
+/********************************************************
+   End of file "cell.c"
+ ********************************************************/
+
+
+/********************************************************
+   Begin of file "fort_impl.c"
+ ********************************************************/
+
+/*
+libfort
+
+MIT License
+
+Copyright (c) 2017 - 2018 Seleznev Anton
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include "fort.h"
+#include <assert.h>
+#include <string.h>
+#include <wchar.h>
+
+/* #include "vector.h" */ /* Commented by amalgamation script */
+/* #include "fort_utils.h" */ /* Commented by amalgamation script */
+/* #include "string_buffer.h" */ /* Commented by amalgamation script */
+/* #include "table.h" */ /* Commented by amalgamation script */
+/* #include "row.h" */ /* Commented by amalgamation script */
+/* #include "properties.h" */ /* Commented by amalgamation script */
+
+
+ft_table_t *ft_create_table(void)
+{
+    ft_table_t *result = (ft_table_t *)F_CALLOC(1, sizeof(ft_table_t));
+    if (result == NULL)
+        return NULL;
+
+    result->rows = create_vector(sizeof(f_row_t *), DEFAULT_VECTOR_CAPACITY);
+    if (result->rows == NULL) {
+        F_FREE(result);
+        return NULL;
+    }
+    result->separators = create_vector(sizeof(f_separator_t *), 
DEFAULT_VECTOR_CAPACITY);
+    if (result->separators == NULL) {
+        destroy_vector(result->rows);
+        F_FREE(result);
+        return NULL;
+    }
+
+    result->properties = create_table_properties();
+    if (result->properties == NULL) {
+        destroy_vector(result->separators);
+        destroy_vector(result->rows);
+        F_FREE(result);
+        return NULL;
+    }
+    result->conv_buffer = NULL;
+    result->cur_row = 0;
+    result->cur_col = 0;
+    return result;
+}
+
+
+void ft_destroy_table(ft_table_t *table)
+{
+    size_t i = 0;
+
+    if (table == NULL)
+        return;
+
+    if (table->rows) {
+        size_t row_n = vector_size(table->rows);
+        for (i = 0; i < row_n; ++i) {
+            destroy_row(VECTOR_AT(table->rows, i, f_row_t *));
+        }
+        destroy_vector(table->rows);
+    }
+    if (table->separators) {
+        size_t row_n = vector_size(table->separators);
+        for (i = 0; i < row_n; ++i) {
+            destroy_separator(VECTOR_AT(table->separators, i, f_separator_t 
*));
+        }
+        destroy_vector(table->separators);
+    }
+    destroy_table_properties(table->properties);
+    destroy_string_buffer(table->conv_buffer);
+    F_FREE(table);
+}
+
+ft_table_t *ft_copy_table(ft_table_t *table)
+{
+    if (table == NULL)
+        return NULL;
+
+    ft_table_t *result = ft_create_table();
+    if (result == NULL)
+        return NULL;
+
+    size_t i = 0;
+    size_t rows_n = vector_size(table->rows);
+    for (i = 0; i < rows_n; ++i) {
+        f_row_t *row = VECTOR_AT(table->rows, i, f_row_t *);
+        f_row_t *new_row = copy_row(row);
+        if (new_row == NULL) {
+            ft_destroy_table(result);
+            return NULL;
+        }
+        vector_push(result->rows, &new_row);
+    }
+
+    size_t sep_sz = vector_size(table->separators);
+    for (i = 0; i < sep_sz; ++i) {
+        f_separator_t *sep = VECTOR_AT(table->separators, i, f_separator_t *);
+        f_separator_t *new_sep = copy_separator(sep);
+        if (new_sep == NULL) {
+            ft_destroy_table(result);
+            return NULL;
+        }
+        vector_push(result->separators, &new_sep);
+    }
+
+    /* note: by default new table has allocated default properties, so we
+     * have to destroy them first.
+     */
+    if (result->properties) {
+        destroy_table_properties(result->properties);
+    }
+    result->properties = copy_table_properties(table->properties);
+    if (result->properties == NULL) {
+        ft_destroy_table(result);
+        return NULL;
+    }
+
+    /* todo: copy conv_buffer  ??  */
+
+    result->cur_row = table->cur_row;
+    result->cur_col = table->cur_col;
+    return result;
+}
+
+static int split_cur_row(ft_table_t *table, f_row_t **tail_of_cur_row)
+{
+    if (table->cur_row >= vector_size(table->rows)) {
+        tail_of_cur_row = NULL;
+        return 0;
+    }
+
+    f_row_t *row = VECTOR_AT(table->rows, table->cur_row, f_row_t *);
+    if (table->cur_col >= columns_in_row(row)) {
+        tail_of_cur_row = NULL;
+        return 0;
+    }
+
+    f_row_t *tail = split_row(row, table->cur_col);
+    if (!tail) {
+        tail_of_cur_row = NULL;
+        return FT_GEN_ERROR;
+    }
+
+    *tail_of_cur_row = tail;
+    return 0;
+}
+
+int ft_ln(ft_table_t *table)
+{
+    assert(table);
+    fort_entire_table_properties_t *table_props = 
&table->properties->entire_table_properties;
+    switch (table_props->add_strategy) {
+        case FT_STRATEGY_INSERT: {
+            f_row_t *new_row = NULL;
+            if (FT_IS_ERROR(split_cur_row(table, &new_row))) {
+                return FT_GEN_ERROR;
+            }
+            if (new_row) {
+                if (FT_IS_ERROR(vector_insert(table->rows, &new_row, 
table->cur_row + 1))) {
+                    destroy_row(new_row);
+                    return FT_GEN_ERROR;
+                }
+            }
+            break;
+        }
+        case FT_STRATEGY_REPLACE:
+            // do nothing
+            break;
+        default:
+            assert(0 && "Unexpected situation inside libfort");
+            break;
+    }
+    table->cur_col = 0;
+    table->cur_row++;
+    return FT_SUCCESS;
+}
+
+size_t ft_cur_row(const ft_table_t *table)
+{
+    assert(table);
+    return table->cur_row;
+}
+
+size_t ft_cur_col(const ft_table_t *table)
+{
+    assert(table);
+    return table->cur_col;
+}
+
+void ft_set_cur_cell(ft_table_t *table, size_t row, size_t col)
+{
+    assert(table);
+    table->cur_row = row;
+    table->cur_col = col;
+}
+
+int ft_is_empty(const ft_table_t *table)
+{
+    assert(table);
+    return ft_row_count(table) == 0;
+}
+
+size_t ft_row_count(const ft_table_t *table)
+{
+    assert(table && table->rows);
+    return vector_size(table->rows);
+}
+
+int ft_erase_range(ft_table_t *table,
+                   size_t top_left_row, size_t top_left_col,
+                   size_t bottom_right_row, size_t bottom_right_col)
+{
+    assert(table && table->rows);
+    int status = FT_SUCCESS;
+
+    size_t rows_n = vector_size(table->rows);
+
+    if (top_left_row == FT_CUR_ROW)
+        top_left_row = table->cur_row;
+    if (bottom_right_row == FT_CUR_ROW)
+        bottom_right_row = table->cur_row;
+
+    if (top_left_col == FT_CUR_COLUMN)
+        top_left_col = table->cur_row;
+    if (bottom_right_col == FT_CUR_COLUMN)
+        bottom_right_col = table->cur_row;
+
+    if (top_left_row > bottom_right_row || top_left_col > bottom_right_col)
+        return FT_EINVAL;
+
+    f_row_t *row = NULL;
+    size_t i = top_left_row;
+    while (i < rows_n && i <= bottom_right_row) {
+        row = VECTOR_AT(table->rows, i, f_row_t *);
+        status = ft_row_erase_range(row, top_left_col, bottom_right_col);
+        if (FT_IS_ERROR(status))
+            return status;
+        ++i;
+    }
+
+    f_separator_t *separator = NULL;
+
+    size_t n_iterations = MIN(rows_n - 1, bottom_right_row) - top_left_row + 1;
+    size_t j = 0;
+    i = top_left_row;
+    for (j = 0; j < n_iterations; ++j) {
+        row = VECTOR_AT(table->rows, i, f_row_t *);
+        if (columns_in_row(row)) {
+            ++i;
+        } else {
+            destroy_row(row);
+            status = vector_erase(table->rows, i);
+            if (FT_IS_ERROR(status))
+                return status;
+            if (i < vector_size(table->separators)) {
+                separator = VECTOR_AT(table->separators, i, f_separator_t *);
+                destroy_separator(separator);
+                vector_erase(table->separators, i);
+            }
+        }
+    }
+
+    return FT_SUCCESS;
+}
+
+
+static int ft_row_printf_impl_(ft_table_t *table, size_t row, const struct 
f_string_view *fmt, va_list *va)
+{
+    size_t i = 0;
+    size_t new_cols = 0;
+
+    if (table == NULL)
+        return -1;
+
+    f_row_t *new_row = create_row_from_fmt_string(fmt, va);
+
+    if (new_row == NULL) {
+        return -1;
+    }
+
+    f_row_t **cur_row_p = NULL;
+    size_t sz = vector_size(table->rows);
+    if (row >= sz) {
+        size_t push_n = row - sz + 1;
+        for (i = 0; i < push_n; ++i) {
+            f_row_t *padding_row = create_row();
+            if (padding_row == NULL)
+                goto clear;
+
+            if (FT_IS_ERROR(vector_push(table->rows, &padding_row))) {
+                destroy_row(padding_row);
+                goto clear;
+            }
+        }
+    }
+    /* todo: clearing pushed items in case of error ?? */
+
+    new_cols = columns_in_row(new_row);
+    cur_row_p = &VECTOR_AT(table->rows, row, f_row_t *);
+

@@ Diff output truncated at 100000 characters. @@
This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.



_______________________________________________
BRL-CAD Source Commits mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/brlcad-commits

Reply via email to