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