--- /dev/null
+build
+config.mk
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2022 Olaf Wintermann. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+all: config.mk
+ $(MAKE) -f make/Makefile.mk
+
+config.mk:
+ ./configure
+
+clean: FORCE
+ rm -fR build/*
+
+FORCE:
--- /dev/null
+/*
+ * Copyright 2021 Olaf Wintermann
+ *
+ * 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.
+ */
+
+//define FSB_ENABLE_DETAIL
+
+#include "Fsb.h"
+#include "FsbP.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <limits.h>
+#include <dirent.h>
+#include <fnmatch.h>
+
+#include <Xm/XmAll.h>
+#include <Xm/DropDown.h>
+
+#ifdef FSB_ENABLE_DETAIL
+#include <XmL/Grid.h>
+#endif
+
+#define WIDGET_SPACING 5
+#define WINDOW_SPACING 8
+
+#define BUTTON_EXTRA_SPACE 4
+
+#define DATE_FORMAT_SAME_YEAR "%b %d %H:%M"
+#define DATE_FORMAT_OTHER_YEAR "%b %d %Y"
+
+#define KB_SUFFIX "KiB"
+#define MB_SUFFIX "MiB"
+#define GB_SUFFIX "GiB"
+#define TB_SUFFIX "TiB"
+
+#define FSB_ERROR_TITLE "Error"
+#define FSB_ERROR_CHAR "Character '/' is not allowed in file names"
+#define FSB_ERROR_RENAME "Cannot rename file: %s"
+#define FSB_ERROR_DELETE "Cannot delete file: %s"
+#define FSB_ERROR_CREATE_FOLDER "Cannot create folder: %s"
+#define FSB_ERROR_OPEN_DIR "Cannot open directory: %s"
+
+#define FSB_DETAIL_HEADINGS "Name|Size|Last Modified"
+
+static void fsb_class_init(void);
+static void fsb_class_part_init (WidgetClass wc);
+static void fsb_init(Widget request, Widget neww, ArgList args, Cardinal *num_args);
+static void fsb_resize(Widget widget);
+static void fsb_realize(Widget widget, XtValueMask *mask, XSetWindowAttributes *attributes);
+static void fsb_destroy(Widget widget);
+static Boolean fsb_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args);
+static Boolean fsb_acceptfocus(Widget widget, Time *time);
+
+static void fsb_insert_child(Widget child);
+
+static void fsb_mapcb(Widget widget, XtPointer u, XtPointer cb);
+
+static int FSBGlobFilter(const char *a, const char *b);
+
+static void FSBUpdateTitle(Widget w);
+
+static void FocusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs);
+
+static void ErrDialog(XnFileSelectionBox w, const char *title, const char *errmsg);
+
+static void FSBRename(XnFileSelectionBox fsb, const char *path);
+static void FSBDelete(XnFileSelectionBox fsb, const char *path);
+
+static void FSBSelectItem(XnFileSelectionBox fsb, const char *item);
+
+static char* set_selected_path(XnFileSelectionBox data, XmString item);
+
+static void FileContextMenuCB(Widget item, XtPointer index, XtPointer cd);
+
+static Widget CreateContextMenu(XnFileSelectionBox fsb, Widget parent, XtCallbackProc callback);
+
+static void FileListUpdate(Widget fsb, Widget view, FileElm *dirlist, int dircount, FileElm *files, int filecount, const char *filter, int maxnamelen, void *userData);
+static void FileListSelect(Widget fsb, Widget view, const char *item);
+static void FileListCleanup(Widget fsb, Widget view, void *userData);
+static void FileListDestroy(Widget fsb, Widget view, void *userData);
+
+static void FileListActivateCB(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb);
+static void FileListSelectCB(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb);
+
+static void FileListWidgetAdd(XnFileSelectionBox fsb, Widget w, int showHidden, const char *filter, FileElm *ls, int count);
+
+#ifdef FSB_ENABLE_DETAIL
+static void FileListDetailUpdate(Widget fsb, Widget view, FileElm *dirlist, int dircount, FileElm *files, int filecount, const char *filter, int maxnamelen, void *userData);
+static void FileListDetailSelect(Widget fsb, Widget view, const char *item);
+static void FileListDetailCleanup(Widget fsb, Widget view, void *userData);
+static void FileListDetailDestroy(Widget fsb, Widget view, void *userData);
+static void FileListDetailAdjustColWidth(Widget grid);
+static void FileListDetailAdd(XnFileSelectionBox fsb, Widget grid, int showHidden, const char *filter, FileElm *ls, int count, int maxWidth);
+#endif
+
+static void FSBNewFolder(Widget w, XnFileSelectionBox data, XtPointer u);
+
+static void FSBHome(Widget w, XnFileSelectionBox data, XtPointer u);
+
+static void FileSelectionCallback(XnFileSelectionBox fsb, XtCallbackList cb, int reason, const char *value);
+
+static void CreateUI(XnFileSelectionBox w);
+static FSBViewWidgets CreateView(XnFileSelectionBox w, FSBViewCreateProc createProc, void *userData, Boolean useDirList);
+static void AddViewMenuItem(XnFileSelectionBox w, const char *name, int viewIndex);
+static void SelectView(XnFileSelectionBox f, int view);
+
+static char* FSBDialogTitle(Widget w);
+
+static FSBViewWidgets CreateListView(Widget fsb, ArgList args, int n, void *userData);
+static FSBViewWidgets CreateDetailView(Widget fsb, ArgList args, int n, void *userData);
+
+static const char* GetHomeDir(void);
+
+static char* ConcatPath(const char *parent, const char *name);
+static char* FileName(char *path);
+static char* ParentPath(const char *path);
+//static int CheckFileName(const char *name);
+
+static int filedialog_update_dir(XnFileSelectionBox data, const char *path);
+static void filedialog_cleanup_filedata(XnFileSelectionBox data);
+
+static void pathbar_resize(Widget w, PathBar *p, XtPointer d);
+
+static XtResource resources[] = {
+ {XmNokCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), XtOffset(XnFileSelectionBox, fsb.okCallback), XmRCallback, NULL},
+ {XmNcancelCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), XtOffset(XnFileSelectionBox, fsb.cancelCallback), XmRCallback, NULL},
+ {XnNwidgetSpacing, XmCSpacing, XmRDimension, sizeof(Dimension), XtOffset(XnFileSelectionBox, fsb.widgetSpacing), XmRImmediate, (XtPointer)WIDGET_SPACING},
+ {XnNwindowSpacing, XmCSpacing, XmRDimension, sizeof(Dimension), XtOffset(XnFileSelectionBox, fsb.windowSpacing), XmRImmediate, (XtPointer)WINDOW_SPACING},
+ {XnNfsbType, XnCfsbType, XmRInt, sizeof(int), XtOffset(XnFileSelectionBox, fsb.type), XmRImmediate, (XtPointer)FILEDIALOG_OPEN},
+ {XnNshowHidden, XnCshowHidden, XmRBoolean, sizeof(Boolean), XtOffset(XnFileSelectionBox, fsb.showHidden), XmRImmediate, (XtPointer)False},
+ {XnNshowHiddenButton, XnCshowHiddenButton, XmRBoolean, sizeof(Boolean), XtOffset(XnFileSelectionBox, fsb.showHiddenButton), XmRImmediate, (XtPointer)True},
+ {XnNshowViewMenu, XnCshowViewMenu, XmRBoolean, sizeof(Boolean), XtOffset(XnFileSelectionBox, fsb.showViewMenu), XmRImmediate, (XtPointer)False},
+ {XnNselectedView, XnCselectedView, XmRInt, sizeof(int), XtOffset(XnFileSelectionBox, fsb.selectedview), XmRImmediate, (XtPointer)0},
+
+ {XnNdirectory, XnCdirectory, XmRString, sizeof(XmString), XtOffset(XnFileSelectionBox, fsb.currentPath), XmRString, NULL},
+ {XnNselectedPath, XnCselectedPath, XmRString, sizeof(XmString), XtOffset(XnFileSelectionBox, fsb.selectedPath), XmRString, NULL},
+ {XnNhomePath, XnChomePath, XmRString, sizeof(XmString), XtOffset(XnFileSelectionBox, fsb.homePath), XmRString, NULL},
+
+ {XnNfilter,XnCfilter,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.filterStr), XmRString, "*"},
+ {XnNfilterFunc,XnCfilterFunc,XmRFunction,sizeof(FSBFilterFunc),XtOffset(XnFileSelectionBox, fsb.filterFunc), XmRFunction, NULL},
+
+ {XnNlabelListView,XnClabelListView,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelListView), XmRString, "List"},
+ {XnNlabelDetailView,XnClabelDetailView,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDetailView), XmRString, "Detail"},
+ {XnNlabelOpenFileTitle,XnClabelOpenFileTitle,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelOpenFileTitle), XmRString, "Open File"},
+ {XnNlabelSaveFileTitle,XnClabelSaveFileTitle,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelSaveFileTitle), XmRString, "Save File"},
+ {XnNlabelDirUp,XnClabelDirUp,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDirUp), XmRString, "Dir Up"},
+ {XnNlabelHome,XnClabelHome,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelHome), XmRString, "Home"},
+ {XnNlabelNewFolder,XnClabelNewFolder,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelNewFolder), XmRString, "New Folder"},
+ {XnNlabelFilterButton,XnClabelFilterButton,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelFilterButton), XmRString, "Filter"},
+ {XnNlabelShowHiddenFiles,XnClabelShowHiddenFiles,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelShowHiddenFiles), XmRString, "Show hiden files"},
+ {XnNlabelDirectories,XnClabelDirectories,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDirectories), XmRString, "Directories"},
+ {XnNlabelFiles,XnClabelFiles,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelFiles), XmRString, "Files"},
+ {XnNlabelRename,XnClabelRename,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelRename), XmRString, "Rename"},
+ {XnNlabelDelete,XnClabelDelete,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDelete), XmRString, "Delete"},
+ {XnNlabelOpen,XnClabelOpen,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelOpen), XmRString, "Open"},
+ {XnNlabelSave,XnClabelSave,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelSave), XmRString, "Save"},
+ {XnNlabelOk,XnClabelOk,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelOk), XmRString, "OK"},
+ {XnNlabelCancel,XnClabelCancel,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelCancel), XmRString, "Cancel"},
+ {XnNlabelHelp,XnClabelHelp,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelHelp), XmRString, "Help"},
+ {XnNlabelFileName,XnClabelFileName,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelFileName), XmRString, "New File Name"},
+ {XnNlabelDirectoryName,XnClabelDirectoryName,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDirectoryName), XmRString, "Directory name:"},
+ {XnNlabelNewFileName,XnClabelNewFileName,XmRXmString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelNewFileName), XmRString, "New file name:"},
+ {XnNlabelDeleteFile,XnClabelDeleteFile,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.labelDeleteFile), XmRString, "Delete file '%s'?"},
+ {XnNdetailHeadings,XnCdetailHeadings,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.detailHeadings), XmRString,FSB_DETAIL_HEADINGS},
+ {XnNdateFormatSameYear,XnCdateFormatSameYear,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.dateFormatSameYear), XmRString,DATE_FORMAT_SAME_YEAR},
+ {XnNdateFormatOtherYear,XnNdateFormatOtherYear,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.dateFormatOtherYear), XmRString,DATE_FORMAT_OTHER_YEAR},
+ {XnNsuffixBytes,XnCsuffixBytes,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixBytes), XmRString,"bytes"},
+ {XnNsuffixKB,XnCsuffixKB,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixKB), XmRString,KB_SUFFIX},
+ {XnNsuffixMB,XnCsuffixKB,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixMB), XmRString,MB_SUFFIX},
+ {XnNsuffixGB,XnCsuffixKB,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixGB), XmRString,GB_SUFFIX},
+ {XnNsuffixTB,XnCsuffixKB,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.suffixTB), XmRString,TB_SUFFIX},
+
+ {XnNerrorTitle,XnCerrorTitle,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorTitle), XmRString,FSB_ERROR_TITLE},
+ {XnNerrorIllegalChar,XnCerrorIllegalChar,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorIllegalChar), XmRString,FSB_ERROR_CHAR},
+ {XnNerrorRename,XnCerrorRename,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorRename), XmRString,FSB_ERROR_RENAME},
+ {XnNerrorCreateFolder,XnCerrorCreateFolder,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorFolder), XmRString,FSB_ERROR_CREATE_FOLDER},
+ {XnNerrorDelete,XnCerrorDelete,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorDelete), XmRString,FSB_ERROR_DELETE},
+ {XnNerrorOpenDir,XnCerrorOpenDir,XmRString,sizeof(XmString),XtOffset(XnFileSelectionBox, fsb.errorOpenDir), XmRString,FSB_ERROR_OPEN_DIR}
+};
+
+static XtActionsRec actionslist[] = {
+ {"focusIn", FocusInAP},
+ {"NULL", NULL}
+};
+
+
+static char defaultTranslations[] = "<FocusIn>: focusIn()";
+
+static XtResource constraints[] = {};
+
+FSBClassRec fsbWidgetClassRec = {
+ // Core Class
+ {
+ (WidgetClass)&xmFormClassRec,
+ "XnFSB", // class_name
+ sizeof(FSBRec), // widget_size
+ fsb_class_init, // class_initialize
+ fsb_class_part_init, // class_part_initialize
+ FALSE, // class_inited
+ fsb_init, // initialize
+ NULL, // initialize_hook
+ fsb_realize, // realize
+ actionslist, // actions
+ XtNumber(actionslist), // num_actions
+ resources, // resources
+ XtNumber(resources), // num_resources
+ NULLQUARK, // xrm_class
+ True, // compress_motion
+ True, // compress_exposure
+ True, // compress_enterleave
+ False, // visible_interest
+ fsb_destroy, // destroy
+ fsb_resize, // resize
+ XtInheritExpose, // expose
+ fsb_set_values, // set_values
+ NULL, // set_values_hook
+ XtInheritSetValuesAlmost, // set_values_almost
+ NULL, // get_values_hook
+ fsb_acceptfocus, // accept_focus
+ XtVersion, // version
+ NULL, // callback_offsets
+ defaultTranslations, // tm_table
+ XtInheritQueryGeometry, // query_geometry
+ XtInheritDisplayAccelerator, // display_accelerator
+ NULL, // extension
+ },
+ // Composite Class
+ {
+ XtInheritGeometryManager, // geometry_manager
+ XtInheritChangeManaged, // change_managed
+ fsb_insert_child, // insert_child
+ XtInheritDeleteChild, // delete_child
+ NULL, // extension
+ },
+ // Constraint Class
+ {
+ constraints, // resources
+ XtNumber(constraints), // num_resources
+ sizeof(XmFormConstraintRec), // constraint_size
+ NULL, // initialize
+ NULL, // destroy
+ NULL, // set_value
+ NULL, // extension
+ },
+ // XmManager Class
+ {
+ XtInheritTranslations, // translations
+ NULL, // syn_resources
+ 0, // num_syn_resources
+ NULL, // syn_constraint_resources
+ 0, // num_syn_constraint_resources
+ XmInheritParentProcess, // parent_process
+ NULL // extension
+ },
+ // XmBulletinBoard
+ {
+ FALSE,
+ NULL,
+ XmInheritFocusMovedProc,
+ NULL
+ },
+ // XmForm Class
+ {
+ NULL
+ },
+ // FSB Class
+ {
+ 0
+ }
+};
+
+WidgetClass xnFsbWidgetClass = (WidgetClass)&fsbWidgetClassRec;
+
+
+static void fsb_class_init(void) {
+
+}
+
+static void fsb_class_part_init (WidgetClass wc) {
+ FSBClassRec *fsbClass = (FSBClassRec*)wc;
+ XmFormClassRec *formClass = (XmFormClassRec*)xmFormWidgetClass;
+
+ fsbClass->constraint_class.initialize = formClass->constraint_class.initialize;
+ fsbClass->constraint_class.set_values = formClass->constraint_class.set_values;
+}
+
+
+#define STRDUP_RES(a) if(a) a = strdup(a)
+#define XMS_STRDUP_RES(a) if(a) a = XmStringCopy(a)
+
+static void fsb_init(Widget request, Widget neww, ArgList args, Cardinal *num_args) {
+ XnFileSelectionBox fsb = (XnFileSelectionBox)neww;
+ (xmFormClassRec.core_class.initialize)(request, neww, args, num_args);
+
+ fsb->fsb.disable_set_values = 0;
+
+ STRDUP_RES(fsb->fsb.homePath);
+ STRDUP_RES(fsb->fsb.selectedPath);
+ STRDUP_RES(fsb->fsb.currentPath);
+ STRDUP_RES(fsb->fsb.filterStr);
+ STRDUP_RES(fsb->fsb.labelListView);
+ STRDUP_RES(fsb->fsb.labelDetailView);
+ STRDUP_RES(fsb->fsb.labelOpenFileTitle);
+ STRDUP_RES(fsb->fsb.labelSaveFileTitle);
+ XMS_STRDUP_RES(fsb->fsb.labelDirUp);
+ XMS_STRDUP_RES(fsb->fsb.labelHome);
+ XMS_STRDUP_RES(fsb->fsb.labelNewFolder);
+ XMS_STRDUP_RES(fsb->fsb.labelFilterButton);
+ XMS_STRDUP_RES(fsb->fsb.labelShowHiddenFiles);
+ XMS_STRDUP_RES(fsb->fsb.labelDirectories);
+ XMS_STRDUP_RES(fsb->fsb.labelFiles);
+ XMS_STRDUP_RES(fsb->fsb.labelRename);
+ XMS_STRDUP_RES(fsb->fsb.labelDelete);
+ XMS_STRDUP_RES(fsb->fsb.labelOpen);
+ XMS_STRDUP_RES(fsb->fsb.labelSave);
+ XMS_STRDUP_RES(fsb->fsb.labelCancel);
+ XMS_STRDUP_RES(fsb->fsb.labelHelp);
+ XMS_STRDUP_RES(fsb->fsb.labelFileName);
+ XMS_STRDUP_RES(fsb->fsb.labelDirectoryName);
+ XMS_STRDUP_RES(fsb->fsb.labelNewFileName);
+ STRDUP_RES(fsb->fsb.labelDeleteFile);
+ STRDUP_RES(fsb->fsb.detailHeadings);
+ STRDUP_RES(fsb->fsb.dateFormatSameYear);
+ STRDUP_RES(fsb->fsb.dateFormatOtherYear);
+ STRDUP_RES(fsb->fsb.suffixBytes);
+ STRDUP_RES(fsb->fsb.suffixKB);
+ STRDUP_RES(fsb->fsb.suffixMB);
+ STRDUP_RES(fsb->fsb.suffixGB);
+ STRDUP_RES(fsb->fsb.suffixTB);
+ STRDUP_RES(fsb->fsb.errorTitle);
+ STRDUP_RES(fsb->fsb.errorIllegalChar);
+ STRDUP_RES(fsb->fsb.errorRename);
+ STRDUP_RES(fsb->fsb.errorFolder);
+ STRDUP_RES(fsb->fsb.errorDelete);
+ STRDUP_RES(fsb->fsb.errorOpenDir);
+
+ CreateUI((XnFileSelectionBox)fsb);
+
+ XtAddCallback(neww, XmNmapCallback, fsb_mapcb, NULL);
+}
+
+#define STR_FREE(a) if(a) free(a)
+#define XMSTR_FREE(a) if(a) XmStringFree(a)
+
+static void fsb_destroy(Widget widget) {
+ XnFileSelectionBox w = (XnFileSelectionBox)widget;
+
+ // destroy all views
+ for(int i=0;i<w->fsb.numviews;i++) {
+ FSBView v = w->fsb.view[i];
+ v.destroy(widget, v.widget, v.userData);
+ }
+
+ STR_FREE(w->fsb.homePath);
+
+ // free filelists
+ filedialog_cleanup_filedata(w);
+ STR_FREE(w->fsb.currentPath);
+ STR_FREE(w->fsb.selectedPath);
+ STR_FREE(w->fsb.filterStr);
+
+ PathBarDestroy(w->fsb.pathBar);
+
+ // free strings
+ STR_FREE(w->fsb.labelListView);
+ STR_FREE(w->fsb.labelDetailView);
+ STR_FREE(w->fsb.labelOpenFileTitle);
+ STR_FREE(w->fsb.labelSaveFileTitle);
+
+ XMSTR_FREE(w->fsb.labelDirUp);
+ XMSTR_FREE(w->fsb.labelHome);
+ XMSTR_FREE(w->fsb.labelNewFolder);
+ XMSTR_FREE(w->fsb.labelFilterButton);
+ XMSTR_FREE(w->fsb.labelShowHiddenFiles);
+ XMSTR_FREE(w->fsb.labelDirectories);
+ XMSTR_FREE(w->fsb.labelFiles);
+ XMSTR_FREE(w->fsb.labelRename);
+ XMSTR_FREE(w->fsb.labelDelete);
+ XMSTR_FREE(w->fsb.labelOpen);
+ XMSTR_FREE(w->fsb.labelSave);
+ XMSTR_FREE(w->fsb.labelCancel);
+ XMSTR_FREE(w->fsb.labelHelp);
+ XMSTR_FREE(w->fsb.labelFileName);
+ XMSTR_FREE(w->fsb.labelDirectoryName);
+ XMSTR_FREE(w->fsb.labelNewFileName);
+ STR_FREE(w->fsb.labelDeleteFile);
+ STR_FREE(w->fsb.detailHeadings);
+
+ STR_FREE(w->fsb.dateFormatSameYear);
+ STR_FREE(w->fsb.dateFormatOtherYear);
+ STR_FREE(w->fsb.suffixBytes);
+ STR_FREE(w->fsb.suffixKB);
+ STR_FREE(w->fsb.suffixMB);
+ STR_FREE(w->fsb.suffixGB);
+ STR_FREE(w->fsb.suffixTB);
+
+ STR_FREE(w->fsb.errorTitle);
+ STR_FREE(w->fsb.errorIllegalChar);
+ STR_FREE(w->fsb.errorRename);
+ STR_FREE(w->fsb.errorFolder);
+ STR_FREE(w->fsb.errorDelete);
+ STR_FREE(w->fsb.errorOpenDir);
+}
+
+static void fsb_resize(Widget widget) {
+ XnFileSelectionBox w = (XnFileSelectionBox)widget;
+ (xmFormClassRec.core_class.resize)(widget);
+
+#ifdef FSB_ENABLE_DETAIL
+ if(w->fsb.view[w->fsb.selectedview].update == FileListDetailUpdate) {
+ FileListDetailAdjustColWidth(w->fsb.grid);
+ }
+#endif
+}
+
+static void fsb_realize(Widget widget, XtValueMask *mask, XSetWindowAttributes *attributes) {
+ XnFileSelectionBox w = (XnFileSelectionBox)widget;
+ (xmFormClassRec.core_class.realize)(widget, mask, attributes);
+
+ FSBView view = w->fsb.view[w->fsb.selectedview];
+ XmProcessTraversal(view.focus, XmTRAVERSE_CURRENT);
+
+#ifdef FSB_ENABLE_DETAIL
+ if(w->fsb.view[w->fsb.selectedview].update == FileListDetailUpdate) {
+ FileListDetailAdjustColWidth(w->fsb.grid);
+ }
+#endif
+}
+
+static void FSBUpdateTitle(Widget w) {
+ if(XtParent(w)->core.widget_class == xmDialogShellWidgetClass) {
+ char *title = FSBDialogTitle(w);
+ XtVaSetValues(XtParent(w), XmNtitle, title, NULL);
+ }
+}
+
+static Boolean fsb_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) {
+ Boolean r = False;
+
+ XnFileSelectionBox o = (XnFileSelectionBox)old;
+ XnFileSelectionBox n = (XnFileSelectionBox)neww;
+
+ int setOkBtnLabel = 0;
+ int ismanaged = XtIsManaged(neww);
+ Dimension width, height;
+ if(!ismanaged) {
+ width = n->core.width;
+ height = n->core.height;
+ if(n->fsb.pathBar) {
+ n->fsb.pathBar->disableResize = True;
+ }
+ }
+
+ if(o->fsb.selectedview != n->fsb.selectedview) {
+ int selectedview = n->fsb.selectedview;
+ n->fsb.selectedview = o->fsb.selectedview;
+ SelectView(n, selectedview);
+ }
+
+ char *updateDir = NULL;
+ int selectItem = 0;
+ if(o->fsb.selectedPath != n->fsb.selectedPath) {
+ STR_FREE(o->fsb.selectedPath);
+ STRDUP_RES(n->fsb.selectedPath);
+ XmTextFieldSetString(n->fsb.name, FileName(n->fsb.selectedPath));
+ // also update current directory
+ updateDir = ParentPath(n->fsb.selectedPath);
+ selectItem = 1;
+ }
+ if(o->fsb.currentPath != n->fsb.currentPath) {
+ STR_FREE(o->fsb.currentPath);
+ updateDir = strdup(n->fsb.currentPath);
+ n->fsb.currentPath = NULL;
+ }
+
+ if(o->fsb.filterStr != n->fsb.filterStr) {
+ STR_FREE(o->fsb.filterStr);
+ STRDUP_RES(n->fsb.filterStr);
+ XmTextFieldSetString(XmDropDownGetText(n->fsb.filter), n->fsb.filterStr);
+ if(!updateDir) {
+ filedialog_update_dir(n, NULL);
+ }
+ }
+
+ if(updateDir) {
+ filedialog_update_dir(n, updateDir);
+ PathBarSetPath(n->fsb.pathBar, updateDir);
+ free(updateDir);
+ }
+
+ if(o->fsb.type != n->fsb.type) {
+ if(n->fsb.type == FILEDIALOG_OPEN) {
+ XtVaSetValues(n->fsb.workarea, XmNbottomWidget, n->fsb.separator, NULL);
+ XtUnmanageChild(n->fsb.name);
+ XtUnmanageChild(n->fsb.nameLabel);
+ } else {
+ XtManageChild(n->fsb.name);
+ XtManageChild(n->fsb.nameLabel);
+ XtVaSetValues(n->fsb.workarea, XmNbottomWidget, n->fsb.nameLabel, NULL);
+ }
+ FSBUpdateTitle(neww);
+ setOkBtnLabel = 1;
+ }
+
+ // label strings
+ int updateTitle = 0;
+ if(o->fsb.labelListView != n->fsb.labelListView) {
+ STR_FREE(o->fsb.labelListView);
+ STRDUP_RES(n->fsb.labelListView);
+ XmString label = XmStringCreateLocalized(n->fsb.labelListView);
+ XtVaSetValues(n->fsb.viewSelectorList, XmNlabelString, label, NULL);
+ XmStringFree(label);
+ }
+ if(o->fsb.labelDetailView != n->fsb.labelDetailView) {
+ STR_FREE(o->fsb.labelDetailView);
+ STRDUP_RES(n->fsb.labelDetailView);
+ XmString label = XmStringCreateLocalized(n->fsb.labelDetailView);
+ XtVaSetValues(n->fsb.viewSelectorDetail, XmNlabelString, label, NULL);
+ if(n->fsb.detailToggleButton) {
+ XtVaSetValues(n->fsb.detailToggleButton, XmNlabelString, label, NULL);
+ }
+ XmStringFree(label);
+ }
+ if(o->fsb.labelOpenFileTitle != n->fsb.labelOpenFileTitle) {
+ STR_FREE(o->fsb.labelOpenFileTitle);
+ STRDUP_RES(n->fsb.labelOpenFileTitle);
+ updateTitle = 1;
+ }
+ if(o->fsb.labelSaveFileTitle != n->fsb.labelSaveFileTitle) {
+ STR_FREE(o->fsb.labelSaveFileTitle);
+ STRDUP_RES(n->fsb.labelSaveFileTitle);
+ updateTitle = 1;
+ }
+
+ if(o->fsb.labelDirUp != n->fsb.labelDirUp) {
+ XMSTR_FREE(o->fsb.labelDirUp);
+ XMS_STRDUP_RES(n->fsb.labelDirUp);
+ XtVaSetValues(n->fsb.dirUp, XmNlabelString, n->fsb.labelDirUp, NULL);
+ }
+ if(o->fsb.labelHome != n->fsb.labelHome) {
+ XMSTR_FREE(o->fsb.labelHome);
+ XMS_STRDUP_RES(n->fsb.labelHome);
+ XtVaSetValues(n->fsb.dirUp, XmNlabelString, n->fsb.labelHome, NULL);
+ }
+ if(o->fsb.labelNewFolder != n->fsb.labelNewFolder) {
+ XMSTR_FREE(o->fsb.labelNewFolder);
+ XMS_STRDUP_RES(n->fsb.labelNewFolder);
+ XtVaSetValues(n->fsb.newFolder, XmNlabelString, n->fsb.labelNewFolder, NULL);
+ }
+ if(o->fsb.labelFilterButton != n->fsb.labelFilterButton) {
+ XMSTR_FREE(o->fsb.labelFilterButton);
+ XMS_STRDUP_RES(n->fsb.labelFilterButton);
+ XtVaSetValues(n->fsb.filterButton, XmNlabelString, n->fsb.labelFilterButton, NULL);
+ }
+ if(o->fsb.labelShowHiddenFiles != n->fsb.labelShowHiddenFiles) {
+ XMSTR_FREE(o->fsb.labelShowHiddenFiles);
+ XMS_STRDUP_RES(n->fsb.labelShowHiddenFiles);
+ XtVaSetValues(n->fsb.showHiddenButtonW, XmNlabelString, n->fsb.labelShowHiddenFiles, NULL);
+ }
+ if(o->fsb.labelDirectories != n->fsb.labelDirectories) {
+ XMSTR_FREE(o->fsb.labelDirectories);
+ XMS_STRDUP_RES(n->fsb.labelDirectories);
+ XtVaSetValues(n->fsb.lsDirLabel, XmNlabelString, n->fsb.labelDirectories, NULL);
+ }
+ if(o->fsb.labelFiles != n->fsb.labelFiles) {
+ XMSTR_FREE(o->fsb.labelFiles);
+ XMS_STRDUP_RES(n->fsb.labelFiles);
+ XtVaSetValues(n->fsb.lsFileLabel, XmNlabelString, n->fsb.labelFiles, NULL);
+ }
+ int recreateContextMenu = 0;
+ if(o->fsb.labelRename != n->fsb.labelRename) {
+ XMSTR_FREE(o->fsb.labelRename);
+ XMS_STRDUP_RES(n->fsb.labelRename);
+ recreateContextMenu = 1;
+ }
+ if(o->fsb.labelDelete != n->fsb.labelDelete) {
+ XMSTR_FREE(o->fsb.labelDelete);
+ XMS_STRDUP_RES(n->fsb.labelDelete);
+ recreateContextMenu = 1;
+ }
+
+ if(o->fsb.labelOpen != n->fsb.labelOpen) {
+ XMSTR_FREE(o->fsb.labelOpen);
+ XMS_STRDUP_RES(n->fsb.labelOpen);
+ setOkBtnLabel = 1;
+ }
+ if(o->fsb.labelSave != n->fsb.labelSave) {
+ XMSTR_FREE(o->fsb.labelSave);
+ XMS_STRDUP_RES(n->fsb.labelSave);
+ setOkBtnLabel = 1;
+ }
+ if(o->fsb.labelCancel != n->fsb.labelCancel) {
+ XMSTR_FREE(o->fsb.labelCancel);
+ XMS_STRDUP_RES(n->fsb.labelCancel);
+ XtVaSetValues(n->fsb.cancelBtn, XmNlabelString, n->fsb.labelCancel, NULL);
+ }
+ if(o->fsb.labelHelp != n->fsb.labelHelp) {
+ XMSTR_FREE(o->fsb.labelHelp);
+ XMS_STRDUP_RES(n->fsb.labelHelp);
+ XtVaSetValues(n->fsb.helpBtn, XmNlabelString, n->fsb.labelHelp, NULL);
+ }
+ if(o->fsb.labelFileName != n->fsb.labelFileName) {
+ XMSTR_FREE(o->fsb.labelFileName);
+ XMS_STRDUP_RES(n->fsb.labelFileName);
+ XtVaSetValues(n->fsb.nameLabel, XmNlabelString, n->fsb.labelFileName, NULL);
+ }
+ if(o->fsb.labelDirectoryName != n->fsb.labelDirectoryName) {
+ XMSTR_FREE(o->fsb.labelDirectoryName);
+ XMS_STRDUP_RES(n->fsb.labelDirectoryName);
+ }
+ if(o->fsb.labelNewFileName != n->fsb.labelNewFileName) {
+ XMSTR_FREE(o->fsb.labelNewFileName);
+ XMS_STRDUP_RES(n->fsb.labelNewFileName);
+ }
+
+ if(o->fsb.labelDeleteFile != n->fsb.labelDeleteFile) {
+ STR_FREE(o->fsb.labelDeleteFile);
+ STRDUP_RES(n->fsb.labelDeleteFile);
+ }
+#ifdef FSB_ENABLE_DETAIL
+ if(o->fsb.detailHeadings != n->fsb.detailHeadings) {
+ STR_FREE(o->fsb.detailHeadings);
+ STRDUP_RES(n->fsb.detailHeadings);
+ XtVaSetValues(n->fsb.grid, XmNsimpleHeadings, n->fsb.detailHeadings, NULL);
+ }
+#endif
+ if(o->fsb.dateFormatSameYear != n->fsb.dateFormatSameYear) {
+ STR_FREE(o->fsb.dateFormatSameYear);
+ STRDUP_RES(n->fsb.dateFormatSameYear);
+ }
+ if(o->fsb.dateFormatOtherYear != n->fsb.dateFormatOtherYear) {
+ STR_FREE(o->fsb.dateFormatOtherYear);
+ STRDUP_RES(n->fsb.dateFormatOtherYear);
+ }
+ if(o->fsb.suffixBytes != n->fsb.suffixBytes) {
+ STR_FREE(o->fsb.suffixBytes);
+ STRDUP_RES(n->fsb.suffixBytes);
+ }
+ if(o->fsb.suffixMB != n->fsb.suffixMB) {
+ STR_FREE(o->fsb.suffixMB);
+ STRDUP_RES(n->fsb.suffixMB);
+ }
+ if(o->fsb.suffixGB != n->fsb.suffixGB) {
+ STR_FREE(o->fsb.suffixGB);
+ STRDUP_RES(n->fsb.suffixGB);
+ }
+ if(o->fsb.suffixTB != n->fsb.suffixTB) {
+ STR_FREE(o->fsb.suffixTB);
+ STRDUP_RES(n->fsb.suffixTB);
+ }
+ if(o->fsb.errorTitle != n->fsb.errorTitle) {
+ STR_FREE(o->fsb.errorTitle);
+ STRDUP_RES(n->fsb.errorTitle);
+ }
+ if(o->fsb.errorIllegalChar != n->fsb.errorIllegalChar) {
+ STR_FREE(o->fsb.errorIllegalChar);
+ STRDUP_RES(n->fsb.errorIllegalChar);
+ }
+ if(o->fsb.errorRename != n->fsb.errorRename) {
+ STR_FREE(o->fsb.errorRename);
+ STRDUP_RES(n->fsb.errorRename);
+ }
+ if(o->fsb.errorFolder != n->fsb.errorFolder) {
+ STR_FREE(o->fsb.errorFolder);
+ STRDUP_RES(n->fsb.errorFolder);
+ }
+ if(o->fsb.errorDelete != n->fsb.errorDelete) {
+ STR_FREE(o->fsb.errorDelete);
+ STRDUP_RES(n->fsb.errorDelete);
+ }
+ if(o->fsb.errorOpenDir != n->fsb.errorOpenDir) {
+ STR_FREE(o->fsb.errorOpenDir);
+ STRDUP_RES(n->fsb.errorOpenDir);
+ }
+
+ if(updateTitle) {
+ FSBUpdateTitle(neww);
+ }
+ if(recreateContextMenu) {
+ XtDestroyWidget(n->fsb.listContextMenu);
+ XtDestroyWidget(n->fsb.gridContextMenu);
+ n->fsb.listContextMenu = CreateContextMenu(n, n->fsb.filelist, FileContextMenuCB);
+ n->fsb.gridContextMenu = CreateContextMenu(n, n->fsb.grid, FileContextMenuCB);
+ }
+ if(setOkBtnLabel) {
+ XtVaSetValues(n->fsb.okBtn, XmNlabelString, n->fsb.type == FILEDIALOG_OPEN ? n->fsb.labelOpen : n->fsb.labelSave, NULL);
+ }
+
+ if(!ismanaged && !n->fsb.disable_set_values) {
+ n->fsb.disable_set_values = 1;
+ XtVaSetValues(neww, XmNwidth, width, XmNheight, height, NULL);
+ n->fsb.disable_set_values = 0;
+
+ if(n->fsb.pathBar)
+ n->fsb.pathBar->disableResize = False;
+ }
+
+ if(selectItem) {
+ if(ismanaged) {
+ FSBSelectItem(n, FileName(n->fsb.selectedPath));
+ }
+ }
+
+ Boolean fr = (xmFormClassRec.core_class.set_values)(old, request, neww, args, num_args);
+ return fr ? fr : r;
+}
+
+static void fsb_insert_child(Widget child) {
+ XnFileSelectionBox p = (XnFileSelectionBox)XtParent(child);
+ (xmFormClassRec.composite_class.insert_child)(child);
+
+ if(!p->fsb.gui_created) {
+ return;
+ }
+
+ // custom child widget insert
+ XtVaSetValues(child,
+ XmNbottomAttachment, XmATTACH_WIDGET,
+ XmNbottomWidget, p->fsb.bottom_widget,
+ XmNbottomOffset, p->fsb.widgetSpacing,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNleftOffset, p->fsb.windowSpacing,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNrightOffset, p->fsb.windowSpacing,
+ NULL);
+
+
+ XtVaSetValues(p->fsb.listform,
+ XmNbottomWidget, child,
+ XmNbottomOffset, 0,
+ NULL);
+
+ p->fsb.workarea = child;
+}
+
+Boolean fsb_acceptfocus(Widget widget, Time *time) {
+ return 0;
+}
+
+static void fsb_mapcb(Widget widget, XtPointer u, XtPointer cb) {
+ XnFileSelectionBox w = (XnFileSelectionBox)widget;
+ pathbar_resize(w->fsb.pathBar->widget, w->fsb.pathBar, NULL);
+
+ if(w->fsb.type == FILEDIALOG_OPEN) {
+ FSBView view = w->fsb.view[w->fsb.selectedview];
+ XmProcessTraversal(view.focus, XmTRAVERSE_CURRENT);
+ } else {
+ XmProcessTraversal(w->fsb.name, XmTRAVERSE_CURRENT);
+ }
+
+
+ if(w->fsb.selectedPath) {
+ FSBSelectItem(w, FileName(w->fsb.selectedPath));
+ }
+}
+
+static void FocusInAP(Widget w, XEvent *event, String *args, Cardinal *nArgs) {
+
+}
+
+static int apply_filter(XnFileSelectionBox w, const char *pattern, const char *string) {
+ if(!pattern) return 0;
+
+ FSBFilterFunc func = w->fsb.filterFunc ? w->fsb.filterFunc : FSBGlobFilter;
+ return func(pattern, string);
+}
+
+static int FSBGlobFilter(const char *a, const char *b) {
+ return fnmatch(a, b, 0);
+}
+
+
+static void errCB(Widget w, XtPointer d, XtPointer cbs) {
+ XtDestroyWidget(w);
+}
+
+static void ErrDialog(XnFileSelectionBox w, const char *title, const char *errmsg) {
+ Arg args[16];
+ int n = 0;
+
+ XmString titleStr = XmStringCreateLocalized((char*)title);
+ XmString msg = XmStringCreateLocalized((char*)errmsg);
+
+ XtSetArg(args[n], XmNdialogTitle, titleStr); n++;
+ XtSetArg(args[n], XmNselectionLabelString, msg); n++;
+ XtSetArg(args[n], XmNokLabelString, w->fsb.labelOk); n++;
+ XtSetArg(args[n], XmNcancelLabelString, w->fsb.labelCancel); n++;
+
+ Widget dialog = XmCreatePromptDialog ((Widget)w, "NewFolderPrompt", args, n);
+
+ Widget help = XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON);
+ XtUnmanageChild(help);
+ Widget cancel = XmSelectionBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON);
+ XtUnmanageChild(cancel);
+ Widget text = XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT);
+ XtUnmanageChild(text);
+
+ XtAddCallback(dialog, XmNokCallback, errCB, NULL);
+
+ XtManageChild(dialog);
+
+ XmStringFree(titleStr);
+ XmStringFree(msg);
+}
+
+static void rename_file_cb(Widget w, const char *path, XmSelectionBoxCallbackStruct *cb) {
+ XnFileSelectionBox fsb = NULL;
+ XtVaGetValues(w, XmNuserData, &fsb, NULL);
+
+ char *fileName = NULL;
+ XmStringGetLtoR(cb->value, XmSTRING_DEFAULT_CHARSET, &fileName);
+
+ // make sure the new file name doesn't contain a path separator
+ if(strchr(fileName, '/')) {
+ ErrDialog(fsb, fsb->fsb.errorTitle, fsb->fsb.errorIllegalChar);
+ XtFree(fileName);
+ return;
+ }
+
+ char *parentPath = ParentPath(path);
+ char *newPath = ConcatPath(parentPath, fileName);
+
+ if(rename(path, newPath)) {
+ char errmsg[256];
+ snprintf(errmsg, 256, fsb->fsb.errorRename, strerror(errno));
+ ErrDialog(fsb, fsb->fsb.errorTitle, errmsg);
+ } else {
+ filedialog_update_dir(fsb, parentPath);
+ }
+
+ free(parentPath);
+ free(newPath);
+ XtFree(fileName);
+ XtDestroyWidget(XtParent(w));
+}
+
+static void selectionbox_cancel(Widget w, XtPointer data, XtPointer d) {
+ XtDestroyWidget(XtParent(w));
+}
+
+static void FSBRename(XnFileSelectionBox fsb, const char *path) {
+ Arg args[16];
+ int n = 0;
+ Widget w = (Widget)fsb;
+
+ char *name = FileName((char*)path);
+
+ XmString filename = XmStringCreateLocalized(name);
+ XtSetArg(args[n], XmNselectionLabelString,fsb->fsb.labelNewFileName); n++;
+ XtSetArg(args[n], XmNtextString, filename); n++;
+ XtSetArg(args[n], XmNuserData, fsb); n++;
+ XtSetArg(args[n], XmNdialogTitle, fsb->fsb.labelRename); n++;
+ XtSetArg(args[n], XmNokLabelString, fsb->fsb.labelOk); n++;
+ XtSetArg(args[n], XmNcancelLabelString, fsb->fsb.labelCancel); n++;
+ Widget dialog = XmCreatePromptDialog (w, "RenameFilePrompt", args, n);
+
+ Widget help = XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON);
+ XtUnmanageChild(help);
+
+ XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)rename_file_cb, (char*)path);
+ XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)selectionbox_cancel, NULL);
+
+ XmStringFree(filename);
+ XtManageChild(dialog);
+}
+
+static void delete_file_cb(Widget w, const char *path, XmSelectionBoxCallbackStruct *cb) {
+ XnFileSelectionBox fsb = NULL;
+ XtVaGetValues(w, XmNuserData, &fsb, NULL);
+
+ if(unlink(path)) {
+ char errmsg[256];
+ snprintf(errmsg, 256, fsb->fsb.errorDelete, strerror(errno));
+ ErrDialog(fsb, fsb->fsb.errorTitle, errmsg);
+ } else {
+ char *parentPath = ParentPath(path);
+ filedialog_update_dir(fsb, parentPath);
+ free(parentPath);
+ }
+
+ XtDestroyWidget(XtParent(w));
+}
+
+static void FSBDelete(XnFileSelectionBox fsb, const char *path) {
+ Arg args[16];
+ int n = 0;
+ Widget w = (Widget)fsb;
+
+ char *name = FileName((char*)path);
+ size_t len = strlen(name);
+ size_t msglen = len + strlen(fsb->fsb.labelDeleteFile) + 4;
+ char *msg = malloc(msglen);
+ snprintf(msg, msglen, fsb->fsb.labelDeleteFile, name);
+
+ XmString prompt = XmStringCreateLocalized(msg);
+ XtSetArg(args[n], XmNselectionLabelString, prompt); n++;
+ XtSetArg(args[n], XmNuserData, fsb); n++;
+ XtSetArg(args[n], XmNdialogTitle, fsb->fsb.labelDelete); n++;
+ XtSetArg(args[n], XmNokLabelString, fsb->fsb.labelOk); n++;
+ XtSetArg(args[n], XmNcancelLabelString, fsb->fsb.labelCancel); n++;
+ Widget dialog = XmCreatePromptDialog (w, "DeleteFilePrompt", args, n);
+
+ Widget help = XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON);
+ XtUnmanageChild(help);
+ Widget text = XmSelectionBoxGetChild(dialog, XmDIALOG_TEXT);
+ XtUnmanageChild(text);
+
+ XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)delete_file_cb, (char*)path);
+ XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)selectionbox_cancel, NULL);
+
+ free(msg);
+ XmStringFree(prompt);
+ XtManageChild(dialog);
+}
+
+static void FSBSelectItem(XnFileSelectionBox fsb, const char *item) {
+ FSBView view = fsb->fsb.view[fsb->fsb.selectedview];
+ if(view.select) {
+ view.select((Widget)fsb, view.widget, item);
+ }
+}
+
+static char* set_selected_path(XnFileSelectionBox data, XmString item)
+{
+ char *name = NULL;
+ XmStringGetLtoR(item, XmFONTLIST_DEFAULT_TAG, &name);
+ if(!name) {
+ return NULL;
+ }
+ char *path = ConcatPath(data->fsb.currentPath, name);
+ XtFree(name);
+
+ if(data->fsb.selectedPath) {
+ free(data->fsb.selectedPath);
+ }
+ data->fsb.selectedPath = path;
+
+ return path;
+}
+
+// item0: rename
+// item1: delete
+static void FileContextMenuCB(Widget item, XtPointer index, XtPointer cd) {
+ intptr_t i = (intptr_t)index;
+ Widget parent = XtParent(item);
+ XnFileSelectionBox fsb = NULL;
+ XtVaGetValues(parent, XmNuserData, &fsb, NULL);
+
+ const char *path = fsb->fsb.selectedPath;
+ if(path) {
+ if(i == 0) {
+ FSBRename(fsb, path);
+ } else if(i == 1) {
+ FSBDelete(fsb, path);
+ }
+ }
+}
+
+static Widget CreateContextMenu(XnFileSelectionBox fsb, Widget parent, XtCallbackProc callback) {
+ return XmVaCreateSimplePopupMenu(
+ parent, "popup", callback, XmNpopupEnabled, XmPOPUP_AUTOMATIC,
+ XmNuserData, fsb,
+ XmVaPUSHBUTTON, fsb->fsb.labelRename, 'R', NULL, NULL,
+ XmVaPUSHBUTTON, fsb->fsb.labelDelete, 'D', NULL, NULL,
+ NULL);
+}
+
+
+static void FileListUpdate(Widget fsb, Widget view, FileElm *dirs, int dircount, FileElm *files, int filecount, const char *filter, int maxnamelen, void *userData) {
+ XnFileSelectionBox data = userData;
+ FileListWidgetAdd(data, data->fsb.filelist, data->fsb.showHidden, filter, files, filecount);
+}
+
+static void FileListSelect(Widget fsb, Widget view, const char *item) {
+ XnFileSelectionBox w = (XnFileSelectionBox)fsb;
+
+ int numItems = 0;
+ XmStringTable items = NULL;
+ XtVaGetValues(w->fsb.filelist, XmNitemCount, &numItems, XmNitems, &items, NULL);
+
+ for(int i=0;i<numItems;i++) {
+ char *str = NULL;
+ XmStringGetLtoR(items[i], XmFONTLIST_DEFAULT_TAG, &str);
+ if(!strcmp(str, item)) {
+ XmListSelectPos(w->fsb.filelist, i+1, False);
+ break;
+ }
+ XtFree(str);
+ }
+}
+
+static void FileListCleanup(Widget fsb, Widget view, void *userData) {
+ XnFileSelectionBox data = userData;
+ XmListDeleteAllItems(data->fsb.filelist);
+}
+
+static void FileListDestroy(Widget fsb, Widget view, void *userData) {
+ // unused
+}
+
+static void FileListActivateCB(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb)
+{
+ char *path = set_selected_path(data, cb->item);
+ if(path) {
+ data->fsb.end = True;
+ data->fsb.status = FILEDIALOG_OK;
+ data->fsb.selIsDir = False;
+ FileSelectionCallback(data, data->fsb.okCallback, XmCR_OK, data->fsb.selectedPath);
+ }
+}
+
+static void FileListSelectCB(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb)
+{
+ if(data->fsb.type == FILEDIALOG_SAVE) {
+ char *name = NULL;
+ XmStringGetLtoR(cb->item, XmFONTLIST_DEFAULT_TAG, &name);
+ XmTextFieldSetString(data->fsb.name, name);
+ XtFree(name);
+ } else {
+ char *path = set_selected_path(data, cb->item);
+ if(path) {
+ data->fsb.selIsDir = False;
+ }
+ }
+}
+
+
+static void FileListWidgetAdd(XnFileSelectionBox fsb, Widget w, int showHidden, const char *filter, FileElm *ls, int count)
+{
+ if(count > 0) {
+ XmStringTable items = calloc(count, sizeof(XmString));
+ int i = 0;
+
+ for(int j=0;j<count;j++) {
+ FileElm *e = &ls[j];
+
+ char *name = FileName(e->path);
+ if((!showHidden && name[0] == '.') || apply_filter(fsb, filter, name)) {
+ continue;
+ }
+
+ items[i] = XmStringCreateLocalized(name);
+ i++;
+ }
+ XmListAddItems(w, items, i, 0);
+ for(i=0;i<count;i++) {
+ XmStringFree(items[i]);
+ }
+ free(items);
+ }
+}
+
+#ifdef FSB_ENABLE_DETAIL
+static void FileListDetailUpdate(Widget fsb, Widget view, FileElm *dirs, int dircount, FileElm *files, int filecount, const char *filter, int maxnamelen, void *userData) {
+ XnFileSelectionBox data = userData;
+ FileListDetailAdd(data, data->fsb.grid, data->fsb.showHidden, filter, files, filecount, maxnamelen);
+}
+#endif
+
+/*
+ * create file size string with kb/mb/gb/tb suffix
+ */
+static char* size_str(XnFileSelectionBox fsb, FileElm *f) {
+ char *str = malloc(16);
+ uint64_t size = f->size;
+
+ if(f->isDirectory) {
+ str[0] = '\0';
+ } else if(size < 0x400) {
+ snprintf(str, 16, "%d %s", (int)size, fsb->fsb.suffixBytes);
+ } else if(size < 0x100000) {
+ float s = (float)size/0x400;
+ int diff = (s*100 - (int)s*100);
+ if(diff > 90) {
+ diff = 0;
+ s += 0.10f;
+ }
+ if(size < 0x2800 && diff != 0) {
+ // size < 10 KiB
+ snprintf(str, 16, "%.1f %s", s, fsb->fsb.suffixKB);
+ } else {
+ snprintf(str, 16, "%.0f %s", s, fsb->fsb.suffixKB);
+ }
+ } else if(size < 0x40000000) {
+ float s = (float)size/0x100000;
+ int diff = (s*100 - (int)s*100);
+ if(diff > 90) {
+ diff = 0;
+ s += 0.10f;
+ }
+ if(size < 0xa00000 && diff != 0) {
+ // size < 10 MiB
+ snprintf(str, 16, "%.1f %s", s, fsb->fsb.suffixMB);
+ } else {
+ snprintf(str, 16, "%.0f %s", s, fsb->fsb.suffixMB);
+ }
+ } else if(size < 0x1000000000ULL) {
+ float s = (float)size/0x40000000;
+ int diff = (s*100 - (int)s*100);
+ if(diff > 90) {
+ diff = 0;
+ s += 0.10f;
+ }
+ if(size < 0x280000000 && diff != 0) {
+ // size < 10 GiB
+ snprintf(str, 16, "%.1f %s", s, fsb->fsb.suffixGB);
+ } else {
+ snprintf(str, 16, "%.0f %s", s, fsb->fsb.suffixGB);
+ }
+ } else {
+ size /= 1024;
+ float s = (float)size/0x40000000;
+ int diff = (s*100 - (int)s*100);
+ if(diff > 90) {
+ diff = 0;
+ s += 0.10f;
+ }
+ if(size < 0x280000000 && diff != 0) {
+ // size < 10 TiB
+ snprintf(str, 16, "%.1f %s", s, fsb->fsb.suffixTB);
+ } else {
+ snprintf(str, 16, "%.0f %s", s, fsb->fsb.suffixTB);
+ }
+ }
+ return str;
+}
+
+static char* date_str(XnFileSelectionBox fsb, time_t tm) {
+ struct tm t;
+ struct tm n;
+ time_t now = time(NULL);
+
+ localtime_r(&tm, &t);
+ localtime_r(&now, &n);
+
+ char *str = malloc(24);
+ if(t.tm_year == n.tm_year) {
+ strftime(str, 24, fsb->fsb.dateFormatSameYear, &t);
+ } else {
+ strftime(str, 24, fsb->fsb.dateFormatOtherYear, &t);
+ }
+ return str;
+}
+
+#ifdef FSB_ENABLE_DETAIL
+static void FileListDetailAdjustColWidth(Widget grid) {
+ XmLGridColumn column0 = XmLGridGetColumn(grid, XmCONTENT, 0);
+ XmLGridColumn column1 = XmLGridGetColumn(grid, XmCONTENT, 1);
+ XmLGridColumn column2 = XmLGridGetColumn(grid, XmCONTENT, 2);
+
+ Dimension col0Width = XmLGridColumnWidthInPixels(column0);
+ Dimension col1Width = XmLGridColumnWidthInPixels(column1);
+ Dimension col2Width = XmLGridColumnWidthInPixels(column2);
+
+ Dimension totalWidth = col0Width + col1Width + col2Width;
+
+ Dimension gridWidth = 0;
+ Dimension gridShadow = 0;
+ XtVaGetValues(grid, XmNwidth, &gridWidth, XmNshadowThickness, &gridShadow, NULL);
+
+ Dimension widthDiff = gridWidth - totalWidth - gridShadow - gridShadow;
+
+ if(gridWidth > totalWidth) {
+ XtVaSetValues(grid,
+ XmNcolumnRangeStart, 0,
+ XmNcolumnRangeEnd, 0,
+ XmNcolumnWidth, col0Width + widthDiff - XmLGridVSBWidth(grid) - 2,
+ XmNcolumnSizePolicy, XmCONSTANT,
+ NULL);
+ }
+}
+
+static void FileListDetailAdd(XnFileSelectionBox fsb, Widget grid, int showHidden, const char *filter, FileElm *ls, int count, int maxWidth)
+{
+ XmLGridAddRows(grid, XmCONTENT, 1, count);
+
+ int row = 0;
+ for(int i=0;i<count;i++) {
+ FileElm *e = &ls[i];
+
+ char *name = FileName(e->path);
+ if((!showHidden && name[0] == '.') || (!e->isDirectory && apply_filter(fsb, filter, name))) {
+ continue;
+ }
+
+ // name
+ XmString str = XmStringCreateLocalized(name);
+ XtVaSetValues(grid,
+ XmNcolumn, 0,
+ XmNrow, row,
+ XmNcellString, str, NULL);
+ XmStringFree(str);
+ // size
+ char *szbuf = size_str(fsb, e);
+ str = XmStringCreateLocalized(szbuf);
+ XtVaSetValues(grid,
+ XmNcolumn, 1,
+ XmNrow, row,
+ XmNcellString, str, NULL);
+ free(szbuf);
+ XmStringFree(str);
+ // date
+ char *datebuf = date_str(fsb, e->lastModified);
+ str = XmStringCreateLocalized(datebuf);
+ XtVaSetValues(grid,
+ XmNcolumn, 2,
+ XmNrow, row,
+ XmNcellString, str, NULL);
+ free(datebuf);
+ XmStringFree(str);
+
+ XtVaSetValues(grid, XmNrow, row, XmNrowUserData, e, NULL);
+ row++;
+ }
+
+ // remove unused rows
+ if(count > row) {
+ XmLGridDeleteRows(grid, XmCONTENT, row, count-row);
+ }
+
+ if(maxWidth < 16) {
+ maxWidth = 16;
+ }
+
+ XtVaSetValues(grid,
+ XmNcolumnRangeStart, 0,
+ XmNcolumnRangeEnd, 0,
+ XmNcolumnWidth, maxWidth,
+ XmNcellAlignment, XmALIGNMENT_LEFT,
+ XmNcolumnSizePolicy, XmVARIABLE,
+ NULL);
+ XtVaSetValues(grid,
+ XmNcolumnRangeStart, 1,
+ XmNcolumnRangeEnd, 1,
+ XmNcolumnWidth, 9,
+ XmNcellAlignment, XmALIGNMENT_LEFT,
+ XmNcolumnSizePolicy, XmVARIABLE,
+ NULL);
+ XtVaSetValues(grid,
+ XmNcolumnRangeStart, 2,
+ XmNcolumnRangeEnd, 2,
+ XmNcolumnWidth, 16,
+ XmNcellAlignment, XmALIGNMENT_RIGHT,
+ XmNcolumnSizePolicy, XmVARIABLE,
+ NULL);
+
+ FileListDetailAdjustColWidth(grid);
+}
+
+static void FileListDetailSelect(Widget fsb, Widget view, const char *item) {
+ XnFileSelectionBox w = (XnFileSelectionBox)fsb;
+
+ int numRows = 0;
+ XtVaGetValues(w->fsb.grid, XmNrows, &numRows, NULL);
+
+ XmLGridColumn col = XmLGridGetColumn(w->fsb.grid, XmCONTENT, 0);
+ for(int i=0;i<numRows;i++) {
+ XmLGridRow row = XmLGridGetRow(w->fsb.grid, XmCONTENT, i);
+ FileElm *elm = NULL;
+ XtVaGetValues(w->fsb.grid, XmNrowPtr, row, XmNcolumnPtr, col, XmNrowUserData, &elm, NULL);
+ if(elm) {
+ if(!strcmp(item, FileName(elm->path))) {
+ XmLGridSelectRow(w->fsb.grid, i, False);
+ XmLGridFocusAndShowRow(w->fsb.grid, i+1);
+ break;
+ }
+ }
+ }
+}
+
+static void FileListDetailCleanup(Widget fsb, Widget view, void *userData) {
+ XnFileSelectionBox data = userData;
+ // cleanup grid
+ Cardinal rows = 0;
+ XtVaGetValues(data->fsb.grid, XmNrows, &rows, NULL);
+ XmLGridDeleteRows(data->fsb.grid, XmCONTENT, 0, rows);
+}
+
+static void FileListDetailDestroy(Widget fsb, Widget view, void *userData) {
+ // unused
+}
+#endif
+
+static void create_folder(Widget w, XnFileSelectionBox data, XmSelectionBoxCallbackStruct *cbs) {
+ char *fileName = NULL;
+ XmStringGetLtoR(cbs->value, XmSTRING_DEFAULT_CHARSET, &fileName);
+
+ char *newFolder = ConcatPath(data->fsb.currentPath ? data->fsb.currentPath : "", fileName);
+ if(mkdir(newFolder, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) {
+ char errmsg[256];
+ snprintf(errmsg, 256, data->fsb.errorFolder, strerror(errno));
+ ErrDialog(data, data->fsb.errorTitle, errmsg);
+ } else {
+ char *p = strdup(data->fsb.currentPath);
+ filedialog_update_dir(data, p);
+ free(p);
+ }
+ free(newFolder);
+
+ XtDestroyWidget(XtParent(w));
+}
+
+static void new_folder_cancel(Widget w, XnFileSelectionBox data, XtPointer d) {
+ XtDestroyWidget(XtParent(w));
+}
+
+static void FSBNewFolder(Widget w, XnFileSelectionBox data, XtPointer u)
+{
+ Arg args[16];
+ int n = 0;
+
+ XtSetArg(args[n], XmNdialogTitle, data->fsb.labelNewFolder); n++;
+ XtSetArg (args[n], XmNselectionLabelString, data->fsb.labelDirectoryName); n++;
+ XtSetArg(args[n], XmNokLabelString, data->fsb.labelOk); n++;
+ XtSetArg(args[n], XmNcancelLabelString, data->fsb.labelCancel); n++;
+ Widget dialog = XmCreatePromptDialog (w, "NewFolderPrompt", args, n);
+
+ Widget help = XmSelectionBoxGetChild(dialog, XmDIALOG_HELP_BUTTON);
+ XtUnmanageChild(help);
+
+ XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)create_folder, data);
+ XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)new_folder_cancel, data);
+
+ XtManageChild(dialog);
+
+}
+
+static void FSBHome(Widget w, XnFileSelectionBox data, XtPointer u) {
+ const char *homePath = data->fsb.homePath ? data->fsb.homePath : GetHomeDir();
+ filedialog_update_dir(data, homePath);
+ PathBarSetPath(data->fsb.pathBar, homePath);
+}
+
+
+/*
+ * file_cmp_field
+ * 0: compare path
+ * 1: compare size
+ * 2: compare mtime
+ */
+static int file_cmp_field = 0;
+
+/*
+ * 1 or -1
+ */
+static int file_cmp_order = 1;
+
+static int filecmp(const void *f1, const void *f2)
+{
+ const FileElm *file1 = f1;
+ const FileElm *file2 = f2;
+ if(file1->isDirectory != file2->isDirectory) {
+ return file1->isDirectory < file2->isDirectory;
+ }
+
+ int cmp_field = file_cmp_field;
+ int cmp_order = file_cmp_order;
+ if(file1->isDirectory) {
+ cmp_field = 0;
+ cmp_order = 1;
+ }
+
+ int ret = 0;
+ switch(cmp_field) {
+ case 0: {
+ ret = strcmp(FileName(file1->path), FileName(file2->path));
+ break;
+ }
+ case 1: {
+ if(file1->size < file2->size) {
+ ret = -1;
+ } else if(file1->size == file2->size) {
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+ break;
+ }
+ case 2: {
+ if(file1->lastModified < file2->lastModified) {
+ ret = -1;
+ } else if(file1->lastModified == file2->lastModified) {
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+ break;
+ }
+ }
+
+ return ret * cmp_order;
+}
+
+
+static void free_files(FileElm *ls, int count)
+{
+ for(int i=0;i<count;i++) {
+ if(ls[i].path) {
+ free(ls[i].path);
+ }
+ }
+ free(ls);
+}
+
+static void filedialog_cleanup_filedata(XnFileSelectionBox data)
+{
+ free_files(data->fsb.dirs, data->fsb.dircount);
+ free_files(data->fsb.files, data->fsb.filecount);
+ data->fsb.dirs = NULL;
+ data->fsb.files = NULL;
+ data->fsb.dircount = 0;
+ data->fsb.filecount = 0;
+ data->fsb.maxnamelen = 0;
+}
+
+#define FILE_ARRAY_SIZE 1024
+
+static void file_array_add(FileElm **files, int *alloc, int *count, FileElm elm) {
+ int c = *count;
+ int a = *alloc;
+ if(c >= a) {
+ a *= 2;
+ FileElm *newarray = realloc(*files, sizeof(FileElm) * a);
+
+ *files = newarray;
+ *alloc = a;
+ }
+
+ (*files)[c] = elm;
+ c++;
+ *count = c;
+}
+
+static int filedialog_update_dir(XnFileSelectionBox data, const char *path)
+{
+ DIR *dir = NULL;
+ if(path) {
+ // try to check first, if we can open the path
+ dir = opendir(path);
+ if(!dir) {
+ char errmsg[256];
+ snprintf(errmsg, 256, data->fsb.errorOpenDir, strerror(errno));
+
+ ErrDialog(data, data->fsb.errorTitle, errmsg);
+ return 1;
+ }
+ }
+
+ FSBView view = data->fsb.view[data->fsb.selectedview];
+ view.cleanup((Widget)data, view.widget, view.userData);
+
+ if(view.useDirList) {
+ XmListDeleteAllItems(data->fsb.dirlist);
+ }
+
+ /* read dir and insert items */
+ if(path) {
+ int dircount = 0;
+ int filecount = 0;
+ size_t maxNameLen = 0;
+
+ FileElm *dirs = calloc(sizeof(FileElm), FILE_ARRAY_SIZE);
+ FileElm *files = calloc(sizeof(FileElm), FILE_ARRAY_SIZE);
+ int dirs_alloc = FILE_ARRAY_SIZE;
+ int files_alloc = FILE_ARRAY_SIZE;
+
+ filedialog_cleanup_filedata(data);
+
+ /* dir reading complete - set the path textfield */
+ XmTextFieldSetString(data->fsb.path, (char*)path);
+ char *oldPath = data->fsb.currentPath;
+ data->fsb.currentPath = strdup(path);
+ if(oldPath) {
+ free(oldPath);
+ }
+ path = data->fsb.currentPath;
+
+ struct dirent *ent;
+ while((ent = readdir(dir)) != NULL) {
+ if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
+ continue;
+ }
+
+ char *entpath = ConcatPath(path, ent->d_name);
+
+ struct stat s;
+ if(stat(entpath, &s)) {
+ free(entpath);
+ continue;
+ }
+
+ FileElm new_entry;
+ new_entry.path = entpath;
+ new_entry.isDirectory = S_ISDIR(s.st_mode);
+ new_entry.size = (uint64_t)s.st_size;
+ new_entry.lastModified = s.st_mtime;
+
+ size_t nameLen = strlen(ent->d_name);
+ if(nameLen > maxNameLen) {
+ maxNameLen = nameLen;
+ }
+
+ if(new_entry.isDirectory) {
+ file_array_add(&dirs, &dirs_alloc, &dircount, new_entry);
+ } else {
+ file_array_add(&files, &files_alloc, &filecount, new_entry);
+ }
+ }
+ closedir(dir);
+
+ data->fsb.dirs = dirs;
+ data->fsb.files = files;
+ data->fsb.dircount = dircount;
+ data->fsb.filecount = filecount;
+ data->fsb.maxnamelen = maxNameLen;
+
+ // sort file arrays
+ qsort(dirs, dircount, sizeof(FileElm), filecmp);
+ qsort(files, filecount, sizeof(FileElm), filecmp);
+ }
+
+ Widget filterTF = XmDropDownGetText(data->fsb.filter);
+ char *filter = XmTextFieldGetString(filterTF);
+ char *filterStr = filter;
+ if(!filter || strlen(filter) == 0) {
+ filterStr = "*";
+ }
+
+ if(view.useDirList) {
+ FileListWidgetAdd(data, data->fsb.dirlist, data->fsb.showHidden, NULL, data->fsb.dirs, data->fsb.dircount);
+ view.update(
+ (Widget)data,
+ view.widget,
+ NULL,
+ 0,
+ data->fsb.files,
+ data->fsb.filecount,
+ filterStr,
+ data->fsb.maxnamelen,
+ view.userData);
+ } else {
+ view.update(
+ (Widget)data,
+ view.widget,
+ data->fsb.dirs,
+ data->fsb.dircount,
+ data->fsb.files,
+ data->fsb.filecount,
+ filterStr,
+ data->fsb.maxnamelen,
+ view.userData);
+ }
+
+ if(filter) {
+ XtFree(filter);
+ }
+
+ return 0;
+}
+
+
+static void dirlist_activate(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb)
+{
+ char *path = set_selected_path(data, cb->item);
+ if(path) {
+ if(!filedialog_update_dir(data, path)) {
+ PathBarSetPath(data->fsb.pathBar, path);
+ data->fsb.selIsDir = TRUE;
+ }
+ }
+}
+
+static void dirlist_select(Widget w, XnFileSelectionBox data, XmListCallbackStruct *cb)
+{
+ char *path = set_selected_path(data, cb->item);
+ if(path) {
+ data->fsb.selIsDir = TRUE;
+ }
+}
+
+static void filedialog_enable_detailview(Widget w, XnFileSelectionBox data, XmToggleButtonCallbackStruct *tb) {
+ SelectView(data, tb->set); // 0: list, 1: detail
+}
+
+
+static void filedialog_setshowhidden(
+ Widget w,
+ XnFileSelectionBox data,
+ XmToggleButtonCallbackStruct *tb)
+{
+ data->fsb.showHidden = tb->set;
+ filedialog_update_dir(data, NULL);
+}
+
+static void filedialog_filter(Widget w, XnFileSelectionBox data, XtPointer c)
+{
+ filedialog_update_dir(data, NULL);
+}
+
+static void filedialog_update_filter(Widget w, XnFileSelectionBox data, XtPointer c)
+{
+ filedialog_update_dir(data, NULL);
+
+}
+
+static void filedialog_goup(Widget w, XnFileSelectionBox data, XtPointer d)
+{
+ char *newPath = ParentPath(data->fsb.currentPath);
+ filedialog_update_dir(data, newPath);
+ PathBarSetPath(data->fsb.pathBar, newPath);
+ free(newPath);
+}
+
+static void filedialog_ok(Widget w, XnFileSelectionBox data, XtPointer d)
+{
+ if(data->fsb.type == FILEDIALOG_SAVE) {
+ char *newName = XmTextFieldGetString(data->fsb.name);
+ if(newName) {
+ if(strchr(newName, '/')) {
+ ErrDialog(data, data->fsb.errorTitle, data->fsb.errorIllegalChar);
+ XtFree(newName);
+ return;
+ }
+
+ if(strlen(newName) > 0) {
+ char *selPath = ConcatPath(data->fsb.currentPath, newName);
+ if(data->fsb.selectedPath) free(data->fsb.selectedPath);
+ data->fsb.selectedPath = selPath;
+ }
+ XtFree(newName);
+
+ data->fsb.selIsDir = False;
+ }
+ }
+
+ if(data->fsb.selectedPath) {
+ if(!data->fsb.selIsDir) {
+ data->fsb.status = FILEDIALOG_OK;
+ data->fsb.end = True;
+ FileSelectionCallback(data, data->fsb.okCallback, XmCR_OK, data->fsb.selectedPath);
+ }
+ }
+}
+
+static void filedialog_cancel(Widget w, XnFileSelectionBox data, XtPointer d)
+{
+ data->fsb.end = 1;
+ data->fsb.status = FILEDIALOG_CANCEL;
+ FileSelectionCallback(data, data->fsb.cancelCallback, XmCR_CANCEL, data->fsb.currentPath);
+}
+
+static void filedialog_help(Widget w, XnFileSelectionBox data, XtPointer d)
+{
+ FileSelectionCallback(data, data->manager.help_callback, XmCR_HELP, data->fsb.currentPath);
+}
+
+static void FileSelectionCallback(XnFileSelectionBox fsb, XtCallbackList cb, int reason, const char *value) {
+ XmFileSelectionBoxCallbackStruct cbs;
+ memset(&cbs, 0, sizeof(XmFileSelectionBoxCallbackStruct));
+
+ char *dir = fsb->fsb.currentPath;
+ size_t dirlen = dir ? strlen(dir) : 0;
+ if(dir && dirlen > 0) {
+ char *dir2 = NULL;
+ if(dir[dirlen-1] != '/') {
+ // add a trailing / to the dir string
+ dir2 = malloc(dirlen+2);
+ memcpy(dir2, dir, dirlen);
+ dir2[dirlen] = '/';
+ dir2[dirlen+1] = '\0';
+ dirlen++;
+ dir = dir2;
+ }
+ cbs.dir = XmStringCreateLocalized(dir);
+ cbs.dir_length = dirlen;
+ if(dir2) {
+ free(dir2);
+ }
+ } else {
+ cbs.dir = XmStringCreateLocalized("");
+ cbs.dir_length = 0;
+ }
+ cbs.reason = reason;
+
+ cbs.value = XmStringCreateLocalized((char*)value);
+ cbs.length = strlen(value);
+
+ XtCallCallbackList((Widget)fsb, cb, (XtPointer)&cbs);
+
+ XmStringFree(cbs.dir);
+ XmStringFree(cbs.value);
+}
+
+static void CreateUI(XnFileSelectionBox w) {
+ Arg args[32];
+ int n = 0;
+ XmString str;
+
+ int widget_spacing = w->fsb.widgetSpacing;
+ int window_spacing = w->fsb.windowSpacing;
+
+ Widget form = (Widget)w;
+ int type = w->fsb.type;
+
+ XtVaSetValues((Widget)w, XmNautoUnmanage, False, NULL);
+
+ /* upper part of the gui */
+
+ n = 0;
+ XtSetArg(args[n], XmNlabelString, w->fsb.labelDirUp); n++;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNtopOffset, window_spacing); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNleftOffset, window_spacing); n++;
+ XtSetArg(args[n], XmNresizable, True); n++;
+ XtSetArg(args[n], XmNarrowDirection, XmARROW_UP); n++;
+ w->fsb.dirUp = XmCreatePushButton(form, "DirUp", args, n);
+ XtManageChild(w->fsb.dirUp);
+ XtAddCallback(w->fsb.dirUp, XmNactivateCallback,
+ (XtCallbackProc)filedialog_goup, w);
+
+ // View Option Menu
+ n = 0;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNtopOffset, window_spacing); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightOffset, window_spacing); n++;
+ XtSetArg(args[n], XmNshadowThickness, 0); n++;
+ Widget viewframe = XmCreateForm(form, "vframe", args, n);
+ XtManageChild(viewframe);
+
+ w->fsb.viewMenu = XmCreatePulldownMenu(viewframe, "menu", NULL, 0);
+
+ Widget view;
+ if(w->fsb.showViewMenu) {
+ n = 0;
+ XtSetArg(args[n], XmNsubMenuId, w->fsb.viewMenu); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNmarginHeight, 0); n++;
+ XtSetArg(args[n], XmNmarginWidth, 0); n++;
+ view = XmCreateOptionMenu(viewframe, "option_menu", args, n);
+ XtManageChild(view);
+ w->fsb.viewOption = view;
+ w->fsb.detailToggleButton = NULL;
+ } else {
+ n = 0;
+ str = XmStringCreateLocalized(w->fsb.labelDetailView);
+ XtSetArg(args[n], XmNlabelString, str); n++;
+ XtSetArg(args[n], XmNfillOnSelect, True); n++;
+ XtSetArg(args[n], XmNindicatorOn, False); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ if(w->fsb.selectedview == 1) {
+ XtSetArg(args[n], XmNset, 1); n++;
+ }
+ w->fsb.detailToggleButton = XmCreateToggleButton(viewframe, "ToggleDetailView", args, n);
+ XtManageChild(w->fsb.detailToggleButton);
+ view = w->fsb.detailToggleButton;
+ XmStringFree(str);
+
+ XtAddCallback(
+ w->fsb.detailToggleButton,
+ XmNvalueChangedCallback,
+ (XtCallbackProc)filedialog_enable_detailview,
+ w);
+
+ w->fsb.viewOption = NULL;
+ }
+
+ n = 0;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightWidget, view); n++;
+ XtSetArg(args[n], XmNmarginHeight, 0); n++;
+ XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n++;
+ XtSetArg(args[n], XmNlabelString, w->fsb.labelNewFolder); n++;
+ w->fsb.newFolder = XmCreatePushButton(viewframe, "NewFolder", args, n);
+ XtManageChild(w->fsb.newFolder);
+ XtAddCallback(
+ w->fsb.newFolder,
+ XmNactivateCallback,
+ (XtCallbackProc)FSBNewFolder,
+ w);
+
+
+ n = 0;
+ XtSetArg(args[n], XmNlabelString, w->fsb.labelHome); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNrightWidget, w->fsb.newFolder); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ w->fsb.home = XmCreatePushButton(viewframe, "Home", args, n);
+ XtManageChild(w->fsb.home);
+ XtAddCallback(
+ w->fsb.home,
+ XmNactivateCallback,
+ (XtCallbackProc)FSBHome,
+ w);
+
+ // match visual appearance of detailToggleButton with the other buttons
+ if(w->fsb.detailToggleButton) {
+ Dimension highlight, shadow;
+ XtVaGetValues(w->fsb.newFolder, XmNshadowThickness, &shadow, XmNhighlightThickness, &highlight, NULL);
+ XtVaSetValues(w->fsb.detailToggleButton, XmNshadowThickness, shadow, XmNhighlightThickness, highlight, NULL);
+ }
+
+ // pathbar
+ n = 0;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNtopOffset, window_spacing); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNleftWidget, w->fsb.dirUp); n++;
+ XtSetArg(args[n], XmNleftOffset, widget_spacing); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNrightWidget, viewframe); n++;
+ XtSetArg(args[n], XmNrightOffset, widget_spacing); n++;
+ XtSetArg(args[n], XmNshadowType, XmSHADOW_IN); n++;
+ Widget pathBarFrame = XmCreateFrame(form, "pathbar_frame", args, n);
+ XtManageChild(pathBarFrame);
+ w->fsb.pathBar = CreatePathBar(pathBarFrame, args, 0);
+ w->fsb.pathBar->updateDir = (updatedir_callback)filedialog_update_dir;
+ w->fsb.pathBar->updateDirData = w;
+ XtManageChild(w->fsb.pathBar->widget);
+ w->fsb.path = XmCreateTextField(form, "textfield", args, 0);
+
+ XtVaSetValues(w->fsb.dirUp, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, pathBarFrame, NULL);
+ if(!w->fsb.showViewMenu) {
+ XtVaSetValues(viewframe, XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET, XmNbottomWidget, pathBarFrame, NULL);
+ }
+
+ n = 0;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNleftOffset, window_spacing); n++;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNtopWidget, pathBarFrame); n++;
+ XtSetArg(args[n], XmNtopOffset, 2*widget_spacing); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightOffset, window_spacing); n++;
+ w->fsb.filterForm = XmCreateForm(form, "filterform", args, n);
+ XtManageChild(w->fsb.filterForm);
+
+ n = 0;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNlabelString, w->fsb.labelDirectories); n++;
+ w->fsb.lsDirLabel = XmCreateLabel(w->fsb.filterForm, "labelDirs", args, n);
+ XtManageChild(w->fsb.lsDirLabel);
+
+ n = 0;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
+ XtSetArg(args[n], XmNleftPosition, 35); n++;
+ XtSetArg(args[n], XmNleftOffset, widget_spacing); n++;
+ XtSetArg(args[n], XmNlabelString, w->fsb.labelFiles); n++;
+ w->fsb.lsFileLabel = XmCreateLabel(w->fsb.filterForm, "labelFiles", args, n);
+ XtManageChild(w->fsb.lsFileLabel);
+
+ if(w->fsb.showHiddenButton) {
+ n = 0;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNlabelString, w->fsb.labelShowHiddenFiles); n++;
+ XtSetArg(args[n], XmNset, w->fsb.showHidden); n++;
+ w->fsb.showHiddenButtonW = XmCreateToggleButton(w->fsb.filterForm, "showHidden", args, n);
+ XtManageChild(w->fsb.showHiddenButtonW);
+ XtAddCallback(w->fsb.showHiddenButtonW, XmNvalueChangedCallback,
+ (XtCallbackProc)filedialog_setshowhidden, w);
+ }
+
+ n = 0;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNlabelString, w->fsb.labelFilterButton); n++;
+ if(w->fsb.showHiddenButton) {
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNrightWidget, w->fsb.showHiddenButtonW); n++;
+ } else {
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ }
+ w->fsb.filterButton = XmCreatePushButton(w->fsb.filterForm, "filedialog_filter", args, n);
+ XtManageChild(w->fsb.filterButton);
+ XtAddCallback(w->fsb.filterButton, XmNactivateCallback,
+ (XtCallbackProc)filedialog_filter, w);
+
+ n = 0;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNleftWidget, w->fsb.lsFileLabel); n++;
+ XtSetArg(args[n], XmNleftOffset, widget_spacing); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNrightWidget, w->fsb.filterButton); n++;
+ XtSetArg(args[n], XmNrightOffset, widget_spacing); n++;
+ XtSetArg(args[n], XmNshowLabel, False); n++;
+ XtSetArg(args[n], XmNuseTextField, True); n++;
+ XtSetArg(args[n], XmNverify, False); n++;
+ w->fsb.filter = XmCreateDropDown(w->fsb.filterForm, "filedialog_filter_textfield", args, n);
+ XtManageChild(w->fsb.filter);
+ XmTextFieldSetString(XmDropDownGetText(w->fsb.filter), w->fsb.filterStr);
+ XtAddCallback(XmDropDownGetText(w->fsb.filter), XmNactivateCallback,
+ (XtCallbackProc)filedialog_filter, w);
+ XtAddCallback(w->fsb.filter, XmNupdateTextCallback,
+ (XtCallbackProc)filedialog_update_filter, w);
+ Widget filterList = XmDropDownGetList(w->fsb.filter);
+ str = XmStringCreateSimple("*");
+ XmListAddItem(filterList, str, 0);
+ XmStringFree(str);
+
+ /* lower part */
+ n = 0;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNbottomOffset, window_spacing); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNleftOffset, window_spacing); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightOffset, window_spacing); n++;
+ XtSetArg(args[n], XmNtopOffset, widget_spacing * 2); n++;
+ Widget buttons = XmCreateForm(form, "buttons", args, n);
+ XtManageChild(buttons);
+
+ n = 0;
+ str = type == FILEDIALOG_OPEN ? w->fsb.labelOpen : w->fsb.labelSave;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNlabelString, str); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
+ XtSetArg(args[n], XmNrightPosition, 14); n++;
+ w->fsb.okBtn = XmCreatePushButton(buttons, "filedialog_open", args, n);
+ XtManageChild(w->fsb.okBtn);
+ XmStringFree(str);
+ XtAddCallback(w->fsb.okBtn, XmNactivateCallback,
+ (XtCallbackProc)filedialog_ok, w);
+
+ n = 0;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNlabelString, w->fsb.labelHelp); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
+ XtSetArg(args[n], XmNleftPosition, 86); n++;
+ w->fsb.helpBtn = XmCreatePushButton(buttons, "filedialog_help", args, n);
+ XtManageChild(w->fsb.helpBtn);
+ XtAddCallback(w->fsb.helpBtn, XmNactivateCallback,
+ (XtCallbackProc)filedialog_help, w);
+
+ n = 0;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
+ XtSetArg(args[n], XmNleftPosition, 43); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
+ XtSetArg(args[n], XmNrightPosition, 57); n++;
+ XtSetArg(args[n], XmNlabelString, w->fsb.labelCancel); n++;
+ w->fsb.cancelBtn = XmCreatePushButton(buttons, "filedialog_cancel", args, n);
+ XtManageChild(w->fsb.cancelBtn);
+ XtAddCallback(w->fsb.cancelBtn, XmNactivateCallback,
+ (XtCallbackProc)filedialog_cancel, w);
+
+ n = 0;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNbottomWidget, buttons); n++;
+ XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNleftOffset, 1); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightOffset, 1); n++;
+ w->fsb.separator = XmCreateSeparator(form, "ofd_separator", args, n);
+ XtManageChild(w->fsb.separator);
+
+ Widget bottomWidget = w->fsb.separator;
+
+ n = 0;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNbottomWidget, w->fsb.separator); n++;
+ XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNleftOffset, window_spacing); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightOffset, window_spacing); n++;
+ w->fsb.name = XmCreateTextField(form, "textfield", args, n);
+ XtAddCallback(w->fsb.name, XmNactivateCallback,
+ (XtCallbackProc)filedialog_ok, w);
+
+ n = 0;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNbottomWidget, w->fsb.name); n++;
+ XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNleftOffset, window_spacing); n++;
+ XtSetArg(args[n], XmNlabelString, w->fsb.labelFileName); n++;
+ w->fsb.nameLabel = XmCreateLabel(form, "label", args, n);
+
+ if(type == FILEDIALOG_SAVE) {
+ bottomWidget = w->fsb.nameLabel;
+ XtManageChild(w->fsb.name);
+ XtManageChild(w->fsb.nameLabel);
+ }
+ w->fsb.bottom_widget = bottomWidget;
+
+
+ // middle
+ // form for dir/file lists
+ n = 0;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNtopWidget, w->fsb.filterForm); n++;
+ XtSetArg(args[n], XmNtopOffset, widget_spacing); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNbottomWidget, bottomWidget); n++;
+ XtSetArg(args[n], XmNleftOffset, window_spacing); n++;
+ XtSetArg(args[n], XmNrightOffset, window_spacing); n++;
+ XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++;
+ XtSetArg(args[n], XmNwidth, 580); n++;
+ XtSetArg(args[n], XmNheight, 400); n++;
+ w->fsb.listform = XmCreateForm(form, "fds_listform", args, n);
+
+ // dir/file lists
+
+ n = 0;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNtopWidget, w->fsb.lsDirLabel); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
+ XtSetArg(args[n], XmNrightPosition, 35); n++;
+ w->fsb.dirlist = XmCreateScrolledList(w->fsb.listform, "dirlist", args, n);
+ Dimension width, height;
+ XtMakeResizeRequest(w->fsb.dirlist, 150, 200, &width, &height);
+ XtManageChild(w->fsb.dirlist);
+
+ XtAddCallback(
+ w->fsb.dirlist,
+ XmNdefaultActionCallback,
+ (XtCallbackProc)dirlist_activate,
+ w);
+ XtAddCallback(
+ w->fsb.dirlist,
+ XmNbrowseSelectionCallback,
+ (XtCallbackProc)dirlist_select,
+ w);
+
+ // FileList
+ XnFileSelectionBoxAddView(
+ (Widget)w,
+ w->fsb.labelListView,
+ CreateListView,
+ FileListUpdate,
+ FileListSelect,
+ FileListCleanup,
+ FileListDestroy,
+ True,
+ w);
+
+ // Detail FileList
+#ifdef FSB_ENABLE_DETAIL
+ XnFileSelectionBoxAddView(
+ (Widget)w,
+ w->fsb.labelDetailView,
+ CreateDetailView,
+ FileListDetailUpdate,
+ FileListDetailSelect,
+ FileListDetailCleanup,
+ FileListDetailDestroy,
+ True,
+ w);
+#endif
+
+ /*
+ n = 0;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNleftWidget, w->fsb.dirlist); n++;
+ XtSetArg(args[n], XmNleftOffset, widget_spacing); n++;
+ //XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
+ //XtSetArg(args[n], XmNbottomWidget, w->fsb.filelist); n++;
+ XtSetArg(args[n], XmNbottomOffset, widget_spacing); n++;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNlabelString, w->fsb.labelFiles); n++;
+ w->fsb.lsFileLabel = XmCreateLabel(w->fsb.listform, "label", args, n);
+ XtManageChild(w->fsb.lsFileLabel);
+ */
+
+ XtManageChild(w->fsb.listform);
+
+ int selview = w->fsb.selectedview;
+ if(selview < 2) {
+ XtManageChild(w->fsb.view[selview].widget);
+ } else {
+ w->fsb.selectedview = 0;
+ }
+
+
+ if(w->fsb.selectedPath) {
+ char *parentPath = ParentPath(w->fsb.selectedPath);
+ filedialog_update_dir(w, parentPath);
+ PathBarSetPath(w->fsb.pathBar, parentPath);
+ free(parentPath);
+
+ if(w->fsb.type == FILEDIALOG_SAVE) {
+ XmTextFieldSetString(w->fsb.name, FileName(w->fsb.selectedPath));
+ }
+ } else {
+ char cwd[PATH_MAX];
+ const char *currentPath = w->fsb.currentPath;
+ if(!currentPath) {
+ if(getcwd(cwd, PATH_MAX)) {
+ currentPath = cwd;
+ } else {
+ currentPath = GetHomeDir();
+ }
+ }
+
+ filedialog_update_dir(w, currentPath);
+ PathBarSetPath(w->fsb.pathBar, w->fsb.currentPath);
+ }
+
+
+ w->fsb.selectedview = selview;
+
+ XtVaSetValues((Widget)w, XmNcancelButton, w->fsb.cancelBtn, NULL);
+
+ w->fsb.gui_created = 1;
+}
+
+static char* FSBDialogTitle(Widget widget) {
+ XnFileSelectionBox w = (XnFileSelectionBox)widget;
+ if(w->fsb.type == FILEDIALOG_OPEN) {
+ return w->fsb.labelOpenFileTitle;
+ } else {
+ return w->fsb.labelSaveFileTitle;
+ }
+}
+
+static FSBViewWidgets CreateView(XnFileSelectionBox w, FSBViewCreateProc create, void *userData, Boolean useDirList) {
+ Arg args[64];
+ int n = 0;
+
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ if(useDirList) {
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNleftWidget, w->fsb.dirlist); n++;
+ XtSetArg(args[n], XmNleftOffset, w->fsb.widgetSpacing); n++;
+ } else {
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNtopOffset, w->fsb.widgetSpacing); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ }
+
+ return create(w->fsb.listform, args, n, userData);
+}
+
+
+typedef struct FSBViewSelection {
+ XnFileSelectionBox fsb;
+ int index;
+} FSBViewSelection;
+
+static void SelectView(XnFileSelectionBox f, int view) {
+ FSBView current = f->fsb.view[f->fsb.selectedview];
+ FSBView newview = f->fsb.view[view];
+
+ XtUnmanageChild(current.widget);
+ if(newview.useDirList != current.useDirList) {
+ if(current.useDirList) {
+ XtUnmanageChild(f->fsb.listform);
+ } else {
+ XtManageChild(f->fsb.listform);
+ }
+ }
+
+ current.cleanup((Widget)f, current.widget, current.userData);
+ XtManageChild(newview.widget);
+
+ f->fsb.selectedview = view;
+
+ filedialog_update_dir(f, NULL);
+ XmProcessTraversal(newview.focus, XmTRAVERSE_CURRENT);
+}
+
+static void SelectViewCallback(Widget w, FSBViewSelection *data, XtPointer u) {
+ SelectView(data->fsb, data->index);
+}
+
+static void SelectViewItemDestroy(Widget w, FSBViewSelection *data, XtPointer u) {
+ free(data);
+}
+
+static void AddViewMenuItem(XnFileSelectionBox w, const char *name, int viewIndex) {
+ Arg args[4];
+ int n = 0;
+
+ XmString label = XmStringCreateLocalized((char*)name);
+
+ XtSetArg(args[n], XmNlabelString, label); n++;
+ XtSetArg(args[1], XmNpositionIndex, w->fsb.selectedview == w->fsb.numviews ? 0 : w->fsb.numviews+1); n++;
+ Widget item = XmCreatePushButton(w->fsb.viewMenu, "menuitem", args, n);
+
+ if(viewIndex == 0) {
+ w->fsb.viewSelectorList = item;
+ } else if(viewIndex == 1) {
+ w->fsb.viewSelectorDetail = item;
+ }
+
+ XtManageChild(item);
+ XmStringFree(label);
+
+ FSBViewSelection *data = malloc(sizeof(FSBViewSelection));
+ data->fsb = w;
+ data->index = viewIndex;
+
+ XtAddCallback(
+ item,
+ XmNactivateCallback,
+ (XtCallbackProc)SelectViewCallback,
+ data);
+ XtAddCallback(
+ item,
+ XmNdestroyCallback,
+ (XtCallbackProc)SelectViewItemDestroy,
+ data);
+}
+
+static FSBViewWidgets CreateListView(Widget parent, ArgList args, int n, void *userData) {
+ XnFileSelectionBox fsb = (XnFileSelectionBox)userData;
+
+ XtSetArg(args[n], XmNshadowThickness, 0); n++;
+ Widget frame = XmCreateFrame(parent, "filelistframe", args, n);
+
+ fsb->fsb.filelist = XmCreateScrolledList(frame, "filelist", NULL, 0);
+ XtManageChild(fsb->fsb.filelist);
+
+ XtAddCallback(
+ fsb->fsb.filelist,
+ XmNdefaultActionCallback,
+ (XtCallbackProc)FileListActivateCB,
+ userData);
+ XtAddCallback(
+ fsb->fsb.filelist,
+ XmNbrowseSelectionCallback,
+ (XtCallbackProc)FileListSelectCB,
+ userData);
+
+ fsb->fsb.listContextMenu = CreateContextMenu(fsb, fsb->fsb.filelist, FileContextMenuCB);
+
+ FSBViewWidgets widgets;
+ widgets.view = frame;
+ widgets.focus = fsb->fsb.filelist;
+ return widgets;
+}
+
+#ifdef FSB_ENABLE_DETAIL
+static void set_path_from_row(XnFileSelectionBox data, int row) {
+ FileElm *elm = NULL;
+ XmLGridRow rowPtr = XmLGridGetRow(data->fsb.grid, XmCONTENT, row);
+ XtVaGetValues(data->fsb.grid, XmNrowPtr, rowPtr, XmNrowUserData, &elm, NULL);
+ if(!elm) {
+ fprintf(stderr, "error: no row data\n");
+ return;
+ }
+
+ char *path = strdup(elm->path);
+
+ data->fsb.selIsDir = False;
+ if(data->fsb.type == FILEDIALOG_SAVE) {
+ XmTextFieldSetString(data->fsb.name, FileName(path));
+ }
+
+ if(data->fsb.selectedPath) {
+ free(data->fsb.selectedPath);
+ }
+ data->fsb.selectedPath = path;
+}
+
+static void grid_select(Widget w, XnFileSelectionBox data, XmLGridCallbackStruct *cb) {
+ set_path_from_row(data, cb->row);
+}
+
+static void grid_activate(Widget w, XnFileSelectionBox data, XmLGridCallbackStruct *cb) {
+ set_path_from_row(data, cb->row);
+ data->fsb.end = True;
+ data->fsb.status = FILEDIALOG_OK;
+
+ FileSelectionCallback(data, data->fsb.okCallback, XmCR_OK, data->fsb.selectedPath);
+}
+
+static void grid_key_pressed(Widget w, XnFileSelectionBox data, XmLGridCallbackStruct *cb) {
+ char chars[16];
+ KeySym keysym;
+ int nchars;
+
+ nchars = XLookupString(&cb->event->xkey, chars, 15, &keysym, NULL);
+
+ if(nchars == 0) return;
+
+ // if data->showHidden is 0, data->files contains more items than the grid
+ // this means SelectedRow might not be the correct index for data->files
+ // we have to count files manually and increase 'row', if the file
+ // is actually displayed in the grid
+ int row = 0;
+ int selectedRow = XmLGridGetSelectedRow(w);
+
+ int match = -1;
+
+ for(int i=0;i<data->fsb.filecount;i++) {
+ const char *name = FileName(data->fsb.files[i].path);
+ if(!data->fsb.showHidden && name[0] == '.') continue;
+
+ size_t namelen = strlen(name);
+
+ size_t cmplen = namelen < nchars ? namelen : nchars;
+ if(!memcmp(name, chars, cmplen)) {
+ if(row <= selectedRow) {
+ if(match == -1) {
+ match = row;
+ }
+ } else {
+ match = row;
+ break;
+ }
+ }
+
+ row++;
+ }
+
+ if(match > -1) {
+ XmLGridSelectRow(w, match, True);
+ XmLGridFocusAndShowRow(w, match+1);
+ } else {
+ XBell(XtDisplay(w), 0);
+ }
+}
+
+static void grid_header_clicked(Widget w, XnFileSelectionBox data, XmLGridCallbackStruct *cb) {
+ int new_cmp_field = 0;
+ switch(cb->column) {
+ case 0: {
+ new_cmp_field = 0;
+ break;
+ }
+ case 1: {
+ new_cmp_field = 1;
+ break;
+ }
+ case 2: {
+ new_cmp_field = 2;
+ break;
+ }
+ }
+
+ if(new_cmp_field == file_cmp_field) {
+ file_cmp_order = -file_cmp_order; // revert sort order
+ } else {
+ file_cmp_field = new_cmp_field; // change file cmp order to new field
+ file_cmp_order = 1;
+ }
+
+ int sort_type = file_cmp_order == 1 ? XmSORT_ASCENDING : XmSORT_DESCENDING;
+ XmLGridSetSort(data->fsb.grid, file_cmp_field, sort_type);
+
+ qsort(data->fsb.files, data->fsb.filecount, sizeof(FileElm), filecmp);
+
+ // refresh widget
+ filedialog_update_dir(data, NULL);
+}
+
+static FSBViewWidgets CreateDetailView(Widget parent, ArgList args, int n, void *userData) {
+ XnFileSelectionBox w = userData;
+
+ XtSetArg(args[n], XmNshadowThickness, 0); n++;
+ Widget gridcontainer = XmCreateFrame(parent, "gridcontainer", args, n);
+ XtManageChild(gridcontainer);
+
+ n = 0;
+ XtSetArg(args[n], XmNcolumns, 3); n++;
+ XtSetArg(args[n], XmNheadingColumns, 0); n++;
+ XtSetArg(args[n], XmNheadingRows, 1); n++;
+ XtSetArg(args[n], XmNallowColumnResize, 1); n++;
+ XtSetArg(args[n], XmNsimpleHeadings, w->fsb.detailHeadings); n++;
+ XtSetArg(args[n], XmNhorizontalSizePolicy, XmCONSTANT); n++;
+
+ w->fsb.grid = XmLCreateGrid(gridcontainer, "grid", args, n);
+ XmLGridSetIgnoreModifyVerify(w->fsb.grid, True);
+ XtManageChild(w->fsb.grid);
+
+ XtVaSetValues(
+ w->fsb.grid,
+ XmNcellDefaults, True,
+ XtVaTypedArg, XmNblankBackground, XmRString, "white", 6,
+ XtVaTypedArg, XmNcellBackground, XmRString, "white", 6,
+ NULL);
+
+ XtAddCallback(w->fsb.grid, XmNselectCallback, (XtCallbackProc)grid_select, w);
+ XtAddCallback(w->fsb.grid, XmNactivateCallback, (XtCallbackProc)grid_activate, w);
+ XtAddCallback(w->fsb.grid, XmNheaderClickCallback, (XtCallbackProc)grid_header_clicked, w);
+ XtAddCallback(w->fsb.grid, XmNgridKeyPressedCallback, (XtCallbackProc)grid_key_pressed, w);
+
+ // context menu
+ w->fsb.gridContextMenu = CreateContextMenu(w, w->fsb.grid, FileContextMenuCB);
+
+ FSBViewWidgets widgets;
+ widgets.view = gridcontainer;
+ widgets.focus = w->fsb.grid;
+ return widgets;
+}
+#endif
+
+
+/* ------------------------------ Path Utils ------------------------------ */
+
+const char* GetHomeDir(void) {
+ char *home = getenv("HOME");
+ if(!home) {
+ home = getenv("USERPROFILE");
+ if(!home) {
+ home = "/";
+ }
+ }
+ return home;
+}
+
+static char* ConcatPath(const char *parent, const char *name)
+{
+ size_t parentlen = strlen(parent);
+ size_t namelen = strlen(name);
+
+ size_t pathlen = parentlen + namelen + 2;
+ char *path = malloc(pathlen);
+
+ memcpy(path, parent, parentlen);
+ if(parentlen > 0 && parent[parentlen-1] != '/') {
+ path[parentlen] = '/';
+ parentlen++;
+ }
+ if(name[0] == '/') {
+ name++;
+ namelen--;
+ }
+ memcpy(path+parentlen, name, namelen);
+ path[parentlen+namelen] = '\0';
+ return path;
+}
+
+static char* FileName(char *path) {
+ int si = 0;
+ int osi = 0;
+ int i = 0;
+ int p = 0;
+ char c;
+ while((c = path[i]) != 0) {
+ if(c == '/') {
+ osi = si;
+ si = i;
+ p = 1;
+ }
+ i++;
+ }
+
+ char *name = path + si + p;
+ if(name[0] == 0) {
+ name = path + osi + p;
+ if(name[0] == 0) {
+ return path;
+ }
+ }
+
+ return name;
+}
+
+static char* ParentPath(const char *path) {
+ char *name = FileName((char*)path);
+ size_t namelen = strlen(name);
+ size_t pathlen = strlen(path);
+ size_t parentlen = pathlen - namelen;
+ if(parentlen == 0) {
+ parentlen++;
+ }
+ char *parent = malloc(parentlen + 1);
+ memcpy(parent, path, parentlen);
+ parent[parentlen] = '\0';
+ return parent;
+}
+
+// unused at the moment, maybe reactivate if more illegal characters
+// are defined
+/*
+static int CheckFileName(const char *fileName) {
+ size_t len = strlen(fileName);
+ for(int i=0;i<len;i++) {
+ if(fileName[i] == '/') {
+ return 0;
+ }
+ }
+ return 1;
+}
+*/
+
+/* ------------------------------ PathBar ------------------------------ */
+
+static void pathbar_resize(Widget w, PathBar *p, XtPointer d)
+{
+ if(p->disableResize) return;
+
+ Dimension width, height;
+ XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL);
+
+ Dimension xoff;
+ XtVaGetValues(p->down, XmNwidth, &xoff, NULL);
+
+ Dimension *segW = calloc(p->numSegments, sizeof(Dimension));
+
+ Dimension maxHeight = 0;
+
+ /* get width/height from all widgets */
+ Dimension pathWidth = 0;
+ for(int i=0;i<p->numSegments;i++) {
+ Dimension segWidth;
+ Dimension segHeight;
+ XtVaGetValues(p->pathSegments[i], XmNwidth, &segWidth, XmNheight, &segHeight, NULL);
+ segW[i] = segWidth;
+ pathWidth += segWidth;
+ if(segHeight > maxHeight) {
+ maxHeight = segHeight;
+ }
+ }
+ Dimension tfHeight;
+ XtVaGetValues(p->textfield, XmNheight, &tfHeight, NULL);
+ if(tfHeight > maxHeight) {
+ maxHeight = tfHeight;
+ }
+
+ Boolean arrows = False;
+ if(pathWidth + xoff + 10 > width) {
+ arrows = True;
+ //pathWidth += p->lw + p->rw;
+ }
+
+ /* calc max visible widgets */
+ int start = 0;
+ if(arrows) {
+ Dimension vis = p->lw+p->rw;
+ for(int i=p->numSegments;i>0;i--) {
+ Dimension segWidth = segW[i-1];
+ if(vis + segWidth + xoff + 10 > width) {
+ start = i;
+ arrows = True;
+ break;
+ }
+ vis += segWidth;
+ }
+ } else {
+ p->shift = 0;
+ }
+
+ int leftShift = 0;
+ if(p->shift < 0) {
+ if(start + p->shift < 0) {
+ leftShift = start;
+ start = 0;
+ p->shift = -leftShift;
+ } else {
+ leftShift = -p->shift; /* negative shift */
+ start += p->shift;
+ }
+ }
+
+ int x = 0;
+ if(arrows) {
+ XtManageChild(p->left);
+ XtManageChild(p->right);
+ x += p->lw;
+ } else {
+ XtUnmanageChild(p->left);
+ XtUnmanageChild(p->right);
+ }
+
+ for(int i=0;i<p->numSegments;i++) {
+ if(i >= start && i < p->numSegments - leftShift && !p->input) {
+ XtVaSetValues(p->pathSegments[i], XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL);
+ x += segW[i];
+ XtManageChild(p->pathSegments[i]);
+ } else {
+ XtUnmanageChild(p->pathSegments[i]);
+ }
+ }
+
+ if(arrows) {
+ XtVaSetValues(p->left, XmNx, 0, XmNy, 0, XmNheight, maxHeight, NULL);
+ XtVaSetValues(p->right, XmNx, x, XmNy, 0, XmNheight, maxHeight, NULL);
+ }
+ XtVaSetValues(p->down, XmNx, width-xoff, XmNheight, maxHeight, NULL);
+ free(segW);
+
+ Dimension rw, rh;
+ XtMakeResizeRequest(w, width, maxHeight, &rw, &rh);
+ XtVaSetValues(p->textfield, XmNwidth, rw-xoff, XmNheight, rh, NULL);
+}
+
+static void pathbar_input(Widget w, PathBar *p, XtPointer c)
+{
+ XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct*)c;
+ XEvent *xevent = cbs->event;
+
+ if (cbs->reason == XmCR_INPUT) {
+ if (xevent->xany.type == ButtonPress) {
+ XtUnmanageChild(p->left);
+ XtUnmanageChild(p->right);
+
+ XtManageChild(p->textfield);
+ p->input = 1;
+
+ XmProcessTraversal(p->textfield, XmTRAVERSE_CURRENT);
+
+ pathbar_resize(p->widget, p, NULL);
+ }
+ }
+}
+
+static void pathbar_losingfocus(Widget w, PathBar *p, XtPointer c)
+{
+ p->input = False;
+ XtUnmanageChild(p->textfield);
+ pathbar_resize(p->widget, p, NULL);
+}
+
+static void pathbar_pathinput(Widget w, PathBar *pb, XtPointer d)
+{
+ char *newpath = XmTextFieldGetString(pb->textfield);
+ if(newpath) {
+ if(newpath[0] == '~') {
+ char *p = newpath+1;
+ char *cp = ConcatPath(GetHomeDir(), p);
+ XtFree(newpath);
+ newpath = cp;
+ } else if(newpath[0] != '/') {
+ char cwd[PATH_MAX];
+ if(!getcwd(cwd, sizeof(cwd))) {
+ cwd[0] = '/';
+ cwd[1] = '\0';
+ }
+ char *cp = ConcatPath(cwd, newpath);
+ XtFree(newpath);
+ newpath = cp;
+ }
+
+ /* update path */
+ if(pb->updateDir) {
+ if(!pb->updateDir(pb->updateDirData, newpath)) {
+ PathBarSetPath(pb, newpath);
+ }
+ } else {
+ PathBarSetPath(pb, newpath);
+ }
+ XtFree(newpath);
+
+ /* hide textfield and show path as buttons */
+ XtUnmanageChild(pb->textfield);
+ pathbar_resize(pb->widget, pb, NULL);
+ }
+}
+
+static void pathbar_shift_left(Widget w, PathBar *p, XtPointer d) {
+ p->shift--;
+ pathbar_resize(p->widget, p, NULL);
+}
+
+static void pathbar_shift_right(Widget w, PathBar *p, XtPointer d) {
+ if(p->shift < 0) {
+ p->shift++;
+ }
+ pathbar_resize(p->widget, p, NULL);
+}
+
+static void pathbar_list_select(Widget w, PathBar *p, XmListCallbackStruct *cb) {
+ char *value = NULL;
+ XmStringGetLtoR(cb->item, XmSTRING_DEFAULT_CHARSET, &value);
+ p->updateDir(p->updateDirData, value);
+ PathBarSetPath(p, value);
+ free(value);
+}
+
+static void pathbar_popup(Widget w, PathBar *p, XtPointer d) {
+ Widget parent = XtParent(w);
+ Display *dp = XtDisplay(parent);
+ Window root = XDefaultRootWindow(dp);
+
+ int x, y;
+ Window child;
+ XTranslateCoordinates(dp, XtWindow(parent), root, 0, 0, &x, &y, &child);
+
+ XtManageChild(p->list);
+ XtPopupSpringLoaded(p->popup);
+ XtVaSetValues(p->popup, XmNx, x, XmNy, y + parent->core.height, XmNwidth, parent->core.width, XmNheight, 200, NULL);
+
+ XmProcessTraversal(p->list, XmTRAVERSE_CURRENT);
+}
+
+static void popupEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
+ PathBar *bar = data;
+
+ Window w1 = bar->hs ? XtWindow(bar->hs) : 0;
+ Window w2 = bar->vs ? XtWindow(bar->vs) : 0;
+
+ if(event->type == ButtonPress) {
+ if(event->xbutton.window != 0 && (event->xbutton.window == w1 || event->xbutton.window == w2)) {
+ bar->popupScrollEvent = 1;
+ } else {
+ bar->popupScrollEvent = 0;
+ }
+ } else if(event->type == ButtonRelease) {
+ if(bar->popupScrollEvent) {
+ *dispatch = False;
+ }
+ bar->popupScrollEvent = 0;
+ } else if(event->type == KeyReleaseMask) {
+ int keycode = event->xkey.keycode;
+ if(keycode == 36 || keycode == 9) {
+ XtUnmapWidget(bar->popup);
+ }
+ }
+}
+
+static void pathTextEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
+ PathBar *pb = data;
+ if(event->type == KeyReleaseMask) {
+ if(event->xkey.keycode == 9) {
+ XtUnmanageChild(pb->textfield);
+ pathbar_resize(pb->widget, pb, NULL);
+ *dispatch = False;
+ }
+ }
+}
+
+
+PathBar* CreatePathBar(Widget parent, ArgList args, int n)
+{
+ PathBar *bar = malloc(sizeof(PathBar));
+ bar->path = NULL;
+ bar->updateDir = NULL;
+ bar->updateDirData = NULL;
+ bar->disableResize = False;
+
+ bar->shift = 0;
+
+ XtSetArg(args[n], XmNmarginWidth, 0); n++;
+ XtSetArg(args[n], XmNmarginHeight, 0); n++;
+ bar->widget = XmCreateDrawingArea(parent, "pathbar", args, n);
+ XtAddCallback(
+ bar->widget,
+ XmNresizeCallback,
+ (XtCallbackProc)pathbar_resize,
+ bar);
+ XtAddCallback(
+ bar->widget,
+ XmNinputCallback,
+ (XtCallbackProc)pathbar_input,
+ bar);
+
+ n = 0;
+ XtSetArg(args[n], XmNownerEvents, True), n++;
+ XtSetArg(args[n], XmNgrabStyle, GrabModeSync), n++;
+ bar->popup = XmCreateGrabShell(bar->widget, "pbpopup", args, n);
+ bar->list = XmCreateScrolledList(bar->popup, "pblist", NULL, 0);
+ XtAddEventHandler(bar->popup, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask, FALSE, popupEH, bar);
+ bar->popupScrollEvent = 0;
+
+ XtAddCallback(
+ bar->list,
+ XmNdefaultActionCallback,
+ (XtCallbackProc)pathbar_list_select,
+ bar);
+ XtAddCallback(
+ bar->list,
+ XmNbrowseSelectionCallback,
+ (XtCallbackProc)pathbar_list_select,
+ bar);
+
+ bar->vs = NULL;
+ bar->hs = NULL;
+ XtVaGetValues(XtParent(bar->list), XmNhorizontalScrollBar, &bar->hs, XmNverticalScrollBar, &bar->vs, NULL);
+
+ Arg a[4];
+ XtSetArg(a[0], XmNshadowThickness, 0);
+ XtSetArg(a[1], XmNx, 0);
+ XtSetArg(a[2], XmNy, 0);
+ bar->textfield = XmCreateTextField(bar->widget, "pbtext", a, 3);
+ bar->input = 0;
+ XtAddCallback(
+ bar->textfield,
+ XmNlosingFocusCallback,
+ (XtCallbackProc)pathbar_losingfocus,
+ bar);
+ XtAddCallback(bar->textfield, XmNactivateCallback,
+ (XtCallbackProc)pathbar_pathinput, bar);
+ XtAddEventHandler(bar->textfield, KeyPressMask | KeyReleaseMask, FALSE, pathTextEH, bar);
+
+ XtSetArg(a[0], XmNarrowDirection, XmARROW_DOWN);
+ bar->down = XmCreateArrowButton(bar->widget, "pbbutton", a, 1);
+ XtManageChild(bar->down);
+ XtSetArg(a[0], XmNarrowDirection, XmARROW_LEFT);
+ bar->left = XmCreateArrowButton(bar->widget, "pbbutton", a, 1);
+ XtSetArg(a[0], XmNarrowDirection, XmARROW_RIGHT);
+ bar->right = XmCreateArrowButton(bar->widget, "pbbutton", a, 1);
+ XtAddCallback(
+ bar->down,
+ XmNactivateCallback,
+ (XtCallbackProc)pathbar_popup,
+ bar);
+ XtAddCallback(
+ bar->left,
+ XmNactivateCallback,
+ (XtCallbackProc)pathbar_shift_left,
+ bar);
+ XtAddCallback(
+ bar->right,
+ XmNactivateCallback,
+ (XtCallbackProc)pathbar_shift_right,
+ bar);
+
+ Pixel bg;
+ XtVaGetValues(bar->textfield, XmNbackground, &bg, NULL);
+ XtVaSetValues(bar->widget, XmNbackground, bg, NULL);
+
+ //XtManageChild(bar->left);
+ //XtManageChild(bar->right);
+
+ XtVaGetValues(bar->left, XmNwidth, &bar->lw, NULL);
+ XtVaGetValues(bar->right, XmNwidth, &bar->rw, NULL);
+
+ bar->segmentAlloc = 16;
+ bar->numSegments = 0;
+ bar->pathSegments = calloc(16, sizeof(Widget));
+
+ bar->selection = 0;
+
+ return bar;
+}
+
+static void PathBarChangeDir(Widget w, PathBar *bar, XtPointer unused) {
+ XmToggleButtonSetState(bar->pathSegments[bar->selection], False, False);
+
+ for(int i=0;i<bar->numSegments;i++) {
+ if(bar->pathSegments[i] == w) {
+ bar->selection = i;
+ XmToggleButtonSetState(w, True, False);
+ break;
+ }
+ }
+
+ int plen = strlen(bar->path);
+ int countSeg = 0;
+ for(int i=0;i<=plen;i++) {
+ char c = bar->path[i];
+ if(c == '/' || c == '\0') {
+ if(countSeg == bar->selection) {
+ char *dir = malloc(i+2);
+ memcpy(dir, bar->path, i+1);
+ dir[i+1] = '\0';
+ if(bar->updateDir) {
+ bar->updateDir(bar->updateDirData, dir);
+ }
+ free(dir);
+ }
+ countSeg++;
+ }
+ }
+}
+
+void PathBarSetPath(PathBar *bar, const char *path) {
+ if(bar->path) {
+ free(bar->path);
+ }
+ bar->path = strdup(path);
+
+ for(int i=0;i<bar->numSegments;i++) {
+ XtDestroyWidget(bar->pathSegments[i]);
+ }
+ XtUnmanageChild(bar->textfield);
+ //XtManageChild(bar->left);
+ //XtManageChild(bar->right);
+ bar->input = False;
+
+ Arg args[4];
+ XmString str;
+
+ bar->numSegments = 0;
+
+ int i=0;
+ if(path[0] == '/') {
+ str = XmStringCreateLocalized("/");
+ XtSetArg(args[0], XmNlabelString, str);
+ XtSetArg(args[1], XmNfillOnSelect, True);
+ XtSetArg(args[2], XmNindicatorOn, False);
+ bar->pathSegments[0] = XmCreateToggleButton(
+ bar->widget, "pbbutton", args, 3);
+ XtAddCallback(
+ bar->pathSegments[0],
+ XmNvalueChangedCallback,
+ (XtCallbackProc)PathBarChangeDir,
+ bar);
+ XmStringFree(str);
+ bar->numSegments++;
+ i++;
+ }
+
+ int len = strlen(path);
+ int begin = i;
+ for(;i<=len;i++) {
+ char c = path[i];
+ if((c == '/' || c == '\0') && i > begin) {
+ char *segStr = malloc(i - begin + 1);
+ memcpy(segStr, path+begin, i-begin);
+ segStr[i-begin] = '\0';
+ begin = i+1;
+
+ str = XmStringCreateLocalized(segStr);
+ free(segStr);
+ XtSetArg(args[0], XmNlabelString, str);
+ XtSetArg(args[1], XmNfillOnSelect, True);
+ XtSetArg(args[2], XmNindicatorOn, False);
+ Widget button = XmCreateToggleButton(bar->widget, "pbbutton", args, 3);
+ XtAddCallback(
+ button,
+ XmNvalueChangedCallback,
+ (XtCallbackProc)PathBarChangeDir,
+ bar);
+ XmStringFree(str);
+
+ if(bar->numSegments >= bar->segmentAlloc) {
+ bar->segmentAlloc += 8;
+ bar->pathSegments = realloc(bar->pathSegments, bar->segmentAlloc * sizeof(Widget));
+ }
+
+ bar->pathSegments[bar->numSegments++] = button;
+ }
+ }
+
+ bar->selection = bar->numSegments-1;
+ XmToggleButtonSetState(bar->pathSegments[bar->selection], True, False);
+
+ XmTextFieldSetString(bar->textfield, (char*)path);
+ XmTextFieldSetInsertionPosition(bar->textfield, XmTextFieldGetLastPosition(bar->textfield));
+
+ pathbar_resize(bar->widget, bar, NULL);
+}
+
+void PathBarSetDirList(PathBar *bar, const char **dirlist, size_t nelm) {
+ XmStringTable items = calloc(nelm, sizeof(XmString));
+ for(int i=0;i<nelm;i++) {
+ items[i] = XmStringCreateLocalized((char*)dirlist[i]);
+ }
+ XmListDeleteAllItems(bar->list);
+ XmListAddItems(bar->list, items, nelm, 0);
+ XmListSelectPos(bar->list, 1, False);
+ for(int i=0;i<nelm;i++) {
+ XmStringFree(items[i]);
+ }
+ free(items);
+}
+
+void PathBarDestroy(PathBar *bar) {
+ if(bar->path) {
+ free(bar->path);
+ }
+ free(bar->pathSegments);
+ free(bar);
+}
+
+/* ------------------------------ public API ------------------------------ */
+
+Widget XnCreateFileSelectionDialog(
+ Widget parent,
+ String name,
+ ArgList arglist,
+ Cardinal argcount)
+{
+ Widget dialog = XmCreateDialogShell(parent, "FileDialog", NULL, 0);
+ Widget fsb = XnCreateFileSelectionBox(dialog, name, arglist, argcount);
+ char *title = FSBDialogTitle(fsb);
+ XtVaSetValues(dialog, XmNtitle, title, NULL);
+ return fsb;
+}
+
+Widget XnCreateFileSelectionBox(
+ Widget parent,
+ String name,
+ ArgList arglist,
+ Cardinal argcount)
+{
+ Widget fsb = XtCreateWidget(name, xnFsbWidgetClass, parent, arglist, argcount);
+ return fsb;
+}
+
+void XnFileSelectionBoxAddView(
+ Widget fsb,
+ const char *name,
+ FSBViewCreateProc create,
+ FSBViewUpdateProc update,
+ FSBViewSelectProc select,
+ FSBViewCleanupProc cleanup,
+ FSBViewDestroyProc destroy,
+ Boolean useDirList,
+ void *userData)
+{
+ XnFileSelectionBox f = (XnFileSelectionBox)fsb;
+ if(f->fsb.numviews >= FSB_MAX_VIEWS) {
+ fprintf(stderr, "XnFileSelectionBox: too many views\n");
+ return;
+ }
+
+ FSBView view;
+ view.update = update;
+ view.select = select;
+ view.cleanup = cleanup;
+ view.destroy = destroy;
+ view.useDirList = useDirList;
+ view.userData = userData;
+
+ FSBViewWidgets widgets = CreateView(f, create, userData, useDirList);
+ view.widget = widgets.view;
+ view.focus = widgets.focus;
+
+ AddViewMenuItem(f, name, f->fsb.numviews);
+
+ f->fsb.view[f->fsb.numviews++] = view;
+}
+
+void XnFileSelectionBoxSetDirList(Widget fsb, const char **dirlist, size_t nelm) {
+ XnFileSelectionBox f = (XnFileSelectionBox)fsb;
+ PathBarSetDirList(f->fsb.pathBar, dirlist, nelm);
+}
+
+Widget XnFileSelectionBoxWorkArea(Widget fsb) {
+ XnFileSelectionBox f = (XnFileSelectionBox)fsb;
+ return f->fsb.workarea;
+}
+
+Widget XnFileSelectionBoxGetChild(Widget fsb, enum XnFSBChild child) {
+ XnFileSelectionBox w = (XnFileSelectionBox)fsb;
+ switch(child) {
+ case XnFSB_DIR_UP_BUTTON: return w->fsb.dirUp;
+ case XnFSB_HOME_BUTTON: return w->fsb.home;
+ case XnFSB_NEW_FOLDER_BUTTON: return w->fsb.newFolder;
+ case XnFSB_DETAIL_TOGGLE_BUTTON: return w->fsb.detailToggleButton;
+ case XnFSB_VIEW_OPTION_BUTTON: return w->fsb.viewOption;
+ case XnFSB_FILTER_DROPDOWN: return w->fsb.filter;
+ case XnFSB_FILTER_BUTTON: return w->fsb.filterButton;
+ case XnFSB_SHOW_HIDDEN_TOGGLE_BUTTON: return w->fsb.showHiddenButtonW;
+ case XnFSB_DIRECTORIES_LABEL: return w->fsb.lsDirLabel;
+ case XnFSB_FILES_LABEL: return w->fsb.lsFileLabel;
+ case XnFSB_DIRLIST: return w->fsb.dirlist;
+ case XnFSB_FILELIST: return w->fsb.filelist;
+ case XnFSB_GRID: return w->fsb.grid;
+ case XnFSB_OK_BUTTON: return w->fsb.okBtn;
+ case XnFSB_CANCEL_BUTTON: return w->fsb.cancelBtn;
+ case XnFSB_HELP_BUTTON: return w->fsb.helpBtn;
+ }
+ return NULL;
+}
+
+void XnFileSelectionBoxDeleteFilters(Widget fsb) {
+ XnFileSelectionBox w = (XnFileSelectionBox)fsb;
+ Widget filterList = XmDropDownGetList(w->fsb.filter);
+ XmListDeleteAllItems(filterList);
+}
+
+void XnFileSelectionBoxAddFilter(Widget fsb, const char *filter) {
+ XnFileSelectionBox w = (XnFileSelectionBox)fsb;
+ Widget filterList = XmDropDownGetList(w->fsb.filter);
+
+ XmString str = XmStringCreateSimple((char*)filter);
+ XmListAddItem(filterList, str, 0);
+ XmStringFree(str);
+}
--- /dev/null
+/*
+ * Copyright 2021 Olaf Wintermann
+ *
+ * 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.
+ */
+
+#ifndef FSB_H
+#define FSB_H
+
+#include <X11/Intrinsic.h>
+#include <Xm/PrimitiveP.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern WidgetClass xnFsbWidgetClass;
+
+#define FILEDIALOG_OPEN 1
+#define FILEDIALOG_SAVE 2
+
+#define FILEDIALOG_OK 1
+#define FILEDIALOG_CANCEL 2
+
+#define XnNwidgetSpacing "fsbWidgetSpacing"
+#define XnNwindowSpacing "fsbWindowSpacing"
+
+#define XnNfsbType "fsbType"
+#define XnCfsbType "fsbType"
+
+#define XnNshowHidden "showHidden"
+#define XnCshowHidden "showHidden"
+#define XnNshowHiddenButton "showHiddenButton"
+#define XnCshowHiddenButton "showHiddenButton"
+
+#define XnNshowViewMenu "showViewMenu"
+#define XnCshowViewMenu "showViewMenu"
+
+#define XnNselectedView "fsbSelectedView"
+#define XnCselectedView "fsbSelectedView"
+
+#define XnNdirectory "directory"
+#define XnCdirectory "directory"
+#define XnNselectedPath "selectedPath"
+#define XnCselectedPath "selectedPath"
+#define XnNhomePath "homePath"
+#define XnChomePath "homePath"
+
+#define XnNfilter "filter"
+#define XnCfilter "filter"
+
+#define XnNfilterFunc "filterFunc"
+#define XnCfilterFunc "filterFunc"
+
+#define XnNlabelListView "labelListView"
+#define XnClabelListView "labelListView"
+#define XnNlabelDetailView "labelDetailView"
+#define XnClabelDetailView "labelDetailView"
+#define XnNlabelOpenFileTitle "labelOpenFileTitle"
+#define XnClabelOpenFileTitle "labelOpenFileTitle"
+#define XnNlabelSaveFileTitle "labelSaveFileTitle"
+#define XnClabelSaveFileTitle "labelSaveFileTitel"
+#define XnNlabelDirUp "labelDirUp"
+#define XnClabelDirUp "labelDirUp"
+#define XnNlabelHome "labelHome"
+#define XnClabelHome "labelHome"
+#define XnNlabelNewFolder "labelNewFolder"
+#define XnClabelNewFolder "labelNewFolder"
+#define XnNlabelFilter "labelFilter"
+#define XnClabelFilter "labelFilter"
+#define XnNlabelFilterButton "labelFilterButton"
+#define XnClabelFilterButton "labelFilterButton"
+#define XnNlabelShowHiddenFiles "labelShowHiddenFiles"
+#define XnClabelShowHiddenFiles "labelShowHiddenFiles"
+#define XnNlabelDirectories "labelDirectories"
+#define XnClabelDirectories "labelDirectories"
+#define XnNlabelFiles "labelFiles"
+#define XnClabelFiles "labelFiles"
+#define XnNlabelRename "labelRename"
+#define XnClabelRename "labelRename"
+#define XnNlabelDelete "labelDelete"
+#define XnClabelDelete "labelDelete"
+#define XnNlabelOpen "labelOpen"
+#define XnClabelOpen "labelOpen"
+#define XnNlabelSave "labelSave"
+#define XnClabelSave "labelSave"
+#define XnNlabelOk "labelOk"
+#define XnClabelOk "labelOk"
+#define XnNlabelCancel "labelCancel"
+#define XnClabelCancel "labelCancel"
+#define XnNlabelHelp "labelHelp"
+#define XnClabelHelp "labelHelp"
+#define XnNlabelFileName "labelFileName"
+#define XnClabelFileName "labelFileName"
+#define XnNlabelDirectoryName "labelDirectoryName"
+#define XnClabelDirectoryName "labelDirectoryName"
+#define XnNlabelNewFileName "labelNewFileName"
+#define XnClabelNewFileName "labelNewFileName"
+#define XnNlabelDeleteFile "labelDeleteFile"
+#define XnClabelDeleteFile "labelDeleteFile"
+#define XnNdetailHeadings "detailHeadings"
+#define XnCdetailHeadings "detailHeadings"
+#define XnNdateFormatSameYear "dateFormatSameYear"
+#define XnCdateFormatSameYear "dateFormatSameYear"
+#define XnNdateFormatOtherYear "dateFormatOtherYear"
+#define XnCdateFormatOtherYear "dateFormatOtherYear"
+#define XnNsuffixBytes "suffixBytes"
+#define XnCsuffixBytes "suffixBytes"
+#define XnNsuffixKB "suffixKB"
+#define XnCsuffixKB "suffixKB"
+#define XnNsuffixMB "suffixMB"
+#define XnCsuffixMB "suffixMB"
+#define XnNsuffixGB "suffixGB"
+#define XnCsuffixGB "suffixGB"
+#define XnNsuffixTB "suffixTB"
+#define XnCsuffixTB "suffixTB"
+#define XnNerrorTitle "errorTitle"
+#define XnCerrorTitle "errorTitle"
+#define XnNerrorIllegalChar "errorIllegalChar"
+#define XnCerrorIllegalChar "errorIllegalChar"
+#define XnNerrorRename "errorRename"
+#define XnCerrorRename "errorRename"
+#define XnNerrorCreateFolder "errorCreateFolder"
+#define XnCerrorCreateFolder "errorCreateFolder"
+#define XnNerrorDelete "errorDelete"
+#define XnCerrorDelete "errorDelete"
+#define XnNerrorOpenDir "errorOpenDir"
+#define XnCerrorOpenDir "errorOpenDir"
+
+/*
+ * int FSBFilterFunc(const char *pattern, const char *string)
+ *
+ * Checks checks whether the string matches the pattern
+ *
+ * Return
+ * zero if the string matches the pattern
+ * non-zero if there is no match
+ */
+typedef int(*FSBFilterFunc)(const char*, const char*);
+
+typedef int(*updatedir_callback)(void*,char*);
+
+typedef struct PathBar {
+ Widget widget;
+ Widget textfield;
+
+ Widget down;
+ Widget left;
+ Widget right;
+ Dimension lw;
+ Dimension rw;
+
+ Widget popup;
+ Widget list;
+
+ int shift;
+
+ Widget *pathSegments;
+ size_t numSegments;
+ size_t segmentAlloc;
+
+ char *path;
+ int selection;
+ Boolean input;
+
+ Widget hs;
+ Widget vs;
+ int popupScrollEvent;
+
+ updatedir_callback updateDir;
+ void *updateDirData;
+
+ Boolean disableResize;
+} PathBar;
+
+typedef struct FileElm FileElm;
+struct FileElm {
+ char *path;
+ int isDirectory;
+ unsigned long long size;
+ time_t lastModified;
+};
+
+typedef struct {
+ Widget view;
+ Widget focus;
+} FSBViewWidgets;
+
+enum XnFSBChild {
+ XnFSB_DIR_UP_BUTTON = 0,
+ XnFSB_HOME_BUTTON,
+ XnFSB_NEW_FOLDER_BUTTON,
+ XnFSB_DETAIL_TOGGLE_BUTTON,
+ XnFSB_VIEW_OPTION_BUTTON,
+ XnFSB_FILTER_DROPDOWN,
+ XnFSB_FILTER_BUTTON,
+ XnFSB_SHOW_HIDDEN_TOGGLE_BUTTON,
+ XnFSB_DIRECTORIES_LABEL,
+ XnFSB_FILES_LABEL,
+ XnFSB_DIRLIST,
+ XnFSB_FILELIST,
+ XnFSB_GRID,
+ XnFSB_OK_BUTTON,
+ XnFSB_CANCEL_BUTTON,
+ XnFSB_HELP_BUTTON
+};
+
+typedef FSBViewWidgets(*FSBViewCreateProc)(Widget parent, ArgList args, int n, void *userData);
+typedef void(*FSBViewUpdateProc)(Widget fsb, Widget view, FileElm *dirs, int dircount, FileElm *files, int filecount, const char *filter, int maxnamelen, void *userData);
+typedef void(*FSBViewSelectProc)(Widget fsb, Widget view, const char *item);
+typedef void(*FSBViewCleanupProc)(Widget fsb, Widget view, void *userData);
+typedef void(*FSBViewDestroyProc)(Widget fsb, Widget view, void *userData);
+
+PathBar* CreatePathBar(Widget parent, ArgList args, int n);
+void PathBarSetPath(PathBar *bar, const char *path);
+void PathBarSetDirList(PathBar *bar, const char **dirlist, size_t nelm);
+void PathBarDestroy(PathBar *bar);
+
+Widget XnCreateFileSelectionDialog(
+ Widget parent,
+ String name,
+ ArgList arglist,
+ Cardinal argcount);
+
+Widget XnCreateFileSelectionBox(
+ Widget parent,
+ String name,
+ ArgList arglist,
+ Cardinal argcount);
+
+void XnFileSelectionBoxAddView(
+ Widget fsb,
+ const char *name,
+ FSBViewCreateProc create,
+ FSBViewUpdateProc update,
+ FSBViewSelectProc select,
+ FSBViewCleanupProc cleanup,
+ FSBViewDestroyProc destroy,
+ Boolean useDirList,
+ void *userData);
+
+void XnFileSelectionBoxSetDirList(Widget fsb, const char **dirlist, size_t nelm);
+
+Widget XnFileSelectionBoxWorkArea(Widget fsb);
+
+Widget XnFileSelectionBoxGetChild(Widget fsb, enum XnFSBChild child);
+
+void XnFileSelectionBoxDeleteFilters(Widget fsb);
+
+void XnFileSelectionBoxAddFilter(Widget fsb, const char *filter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FSB_H */
--- /dev/null
+/*
+ * Copyright 2021 Olaf Wintermann
+ *
+ * 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.
+ */
+
+#ifndef FSBP_H
+#define FSBP_H
+
+#include <X11/CoreP.h>
+#include <Xm/XmP.h>
+#include <Xm/PrimitiveP.h>
+#include <Xm/ManagerP.h>
+#include <Xm/FormP.h>
+
+#include "Fsb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FSB_MAX_VIEWS 8
+
+
+typedef struct FSBView FSBView;
+struct FSBView {
+ Widget widget;
+ Widget focus;
+ FSBViewUpdateProc update;
+ FSBViewSelectProc select;
+ FSBViewCleanupProc cleanup;
+ FSBViewDestroyProc destroy;
+ void *userData;
+ Boolean useDirList;
+};
+
+
+typedef struct FSBClassPart {
+ int unused;
+} FSBClassPart;
+
+typedef struct FSBClassRec {
+ CoreClassPart core_class;
+ CompositeClassPart composite_class;
+ ConstraintClassPart constraint_class;
+ XmManagerClassPart manager_class;
+ XmBulletinBoardClassPart bulletin_board_class;
+ XmFormClassPart form_class;
+ FSBClassPart fsb_class;
+} FSBClassRec;
+
+typedef struct FSBPart {
+ XtCallbackList okCallback;
+ XtCallbackList cancelCallback;
+
+ Dimension widgetSpacing;
+ Dimension windowSpacing;
+
+ Boolean showHiddenButton;
+
+ Widget path;
+ PathBar *pathBar;
+ Widget filter;
+ Widget filterButton;
+ Widget showHiddenButtonW;
+
+ FSBFilterFunc filterFunc;
+
+ char *filterStr;
+
+ Widget dirUp;
+ Widget home;
+ Widget newFolder;
+
+ Widget viewSelectorList;
+ Widget viewSelectorDetail;
+
+ Widget viewMenu;
+ Widget viewOption;
+ Widget detailToggleButton;
+
+ Widget filterForm;
+ Widget lsDirLabel;
+ Widget lsFileLabel;
+
+ Widget listContextMenu;
+ Widget gridContextMenu;
+
+ // icon view
+
+ // dir/file list view
+ Widget listform;
+ Widget dirlist;
+
+ FSBView view[FSB_MAX_VIEWS];
+ int numviews;
+ int selectedview;
+
+ Widget filelist;
+ Widget grid;
+
+ Widget separator;
+
+ Widget nameLabel;
+ Widget name;
+
+ Widget bottom_widget;
+
+ Widget workarea;
+
+ Widget okBtn;
+ Widget cancelBtn;
+ Widget helpBtn;
+
+ FileElm *dirs;
+ FileElm *files;
+ int dircount;
+ int filecount;
+ int maxnamelen;
+
+ char *homePath;
+
+ char *currentPath;
+ char *selectedPath;
+ int selIsDir;
+ Boolean showHidden;
+ Boolean showViewMenu;
+
+ int type;
+
+ int end;
+ int status;
+
+ int disable_set_values;
+ int gui_created;
+
+ char *labelListView;
+ char *labelDetailView;
+ char* labelOpenFileTitle;
+ char* labelSaveFileTitle;
+ XmString labelDirUp;
+ XmString labelHome;
+ XmString labelNewFolder;
+ XmString labelFilterButton;
+ XmString labelShowHiddenFiles;
+ XmString labelDirectories;
+ XmString labelFiles;
+ XmString labelRename;
+ XmString labelDelete;
+ XmString labelOpen;
+ XmString labelSave;
+ XmString labelOk;
+ XmString labelCancel;
+ XmString labelHelp;
+ XmString labelFileName;
+ XmString labelDirectoryName;
+ XmString labelNewFileName;
+ char *labelDeleteFile;
+
+ char *detailHeadings;
+
+ char *dateFormatSameYear;
+ char *dateFormatOtherYear;
+ char *suffixBytes;
+ char *suffixKB;
+ char *suffixMB;
+ char *suffixGB;
+ char *suffixTB;
+
+ char *errorTitle;
+ char *errorIllegalChar;
+ char *errorRename;
+ char *errorFolder;
+ char *errorDelete;
+ char *errorOpenDir;
+} FSBPart;
+
+typedef struct FSBRec {
+ CorePart core;
+ CompositePart composite;
+ ConstraintPart constraint;
+ XmManagerPart manager;
+ XmBulletinBoardPart bulletin_board;
+ XmFormPart form;
+ FSBPart fsb;
+} FSBRec;
+
+typedef struct FSBRec *XnFileSelectionBox;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FSBP_H */
+
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2022 Olaf Wintermann. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+BUILD_ROOT = ..
+include $(BUILD_ROOT)/config.mk
+
+CFLAGS += -I../ucx -I..
+
+SRC = main.c
+SRC += window.c
+SRC += player.c
+SRC += Fsb.c
+
+OBJ = $(SRC:%.c=$(BUILD_ROOT)/build/application/%.$(OBJ_EXT))
+
+BINTARGET = mediaplayer
+
+all: $(BUILD_ROOT)/build/bin/$(BINTARGET)
+
+$(BUILD_ROOT)/build/bin/$(BINTARGET): $(OBJ)
+ $(LD) -o $(BUILD_ROOT)/build/bin/$(BINTARGET) $(OBJ) -L$(BUILD_ROOT)/build/lib -lucx $(LDFLAGS) $(APP_LDFLAGS)
+
+$(BUILD_ROOT)/build/application/%.$(OBJ_EXT): %.c
+ $(CC) $(CFLAGS) $(APP_CFLAGS) -o $@ -c $<
+
--- /dev/null
+/*
+ * Copyright 2022 Olaf Wintermann
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "window.h"
+
+#include <ucx/buffer.h>
+#include <ucx/utils.h>
+
+#define APP_NAME "MediaPlayer"
+
+static XtAppContext app;
+static Display *display;
+
+static String fallback[] = {
+ "*renderTable: rt",
+ "*rt*fontType: FONT_IS_XFT",
+ "*rt*fontName: Sans",
+ "*rt*fontSize: 9",
+ NULL
+};
+
+int main(int argc, char** argv) {
+ // disable stdout buffering, because the netbeans's internal terminal
+ // has a bug on freebsd and doesn't flush the output after a newline
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ XtToolkitInitialize();
+ XtSetLanguageProc(NULL, NULL, NULL);
+ app = XtCreateApplicationContext();
+ XtAppSetFallbackResources(app, fallback);
+
+ display = XtOpenDisplay(app, NULL, APP_NAME, APP_NAME, NULL, 0, &argc, argv);
+
+ MainWindow *window = WindowCreate(display);
+
+ WindowShow(window);
+ XtAppMainLoop(app);
+
+ return 0;
+}
+
+XtAppContext* GetAppContext(void) {
+ return &app;
+}
+
+void ApplicationExit(void) {
+ XtAppSetExitFlag(app);
+}
--- /dev/null
+/*
+ * Copyright 2022 Olaf Wintermann
+ *
+ * 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.
+ */
+
+#ifndef MAIN_H
+#define MAIN_H
+
+#include <Xm/XmAll.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+XtAppContext* GetAppContext(void);
+
+void ApplicationExit(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MAIN_H */
+
--- /dev/null
+/*
+ * Copyright 2022 Olaf Wintermann
+ *
+ * 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 "player.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <spawn.h>
+#include <sys/wait.h>
+
+#include <pthread.h>
+
+extern char **environ;
+
+#define WID_ARG_BUFSIZE 24
+
+static void* start_player(void *data);
+
+void PlayerOpenFile(MainWindow *win) {
+ pthread_t tid;
+ if(pthread_create(&tid, NULL, start_player, win)) {
+ perror("pthread_create");
+ }
+}
+
+static void* start_player(void *data) {
+ MainWindow *win = data;
+
+ char *player_bin = "/usr/local/bin/mpv"; // TODO: get bin from settings
+
+ // -wid parameter value for embedding the player in the player_widget
+ Window wid = XtWindow(win->player_widget);
+ char wid_arg[WID_ARG_BUFSIZE];
+ if(snprintf(wid_arg, WID_ARG_BUFSIZE, "%lu", wid) >= WID_ARG_BUFSIZE) {
+ return NULL;
+ }
+
+ // create player arg list
+ char *args[32];
+ args[0] = player_bin;
+ args[1] = "-wid";
+ args[2] = wid_arg;
+ args[3] = win->file;
+ args[4] = NULL;
+
+ // redirect stdin/stdout
+ int pout[2];
+ int pin[2];
+ if(pipe(pout)) {
+ perror("pipe");
+ return NULL;
+ }
+ if(pipe(pin)) {
+ perror("pipe");
+ return NULL;
+ }
+
+ posix_spawn_file_actions_t actions;
+ posix_spawn_file_actions_init(&actions);
+
+ posix_spawn_file_actions_adddup2(&actions, pin[0], STDIN_FILENO);
+ posix_spawn_file_actions_adddup2(&actions, pout[1], STDOUT_FILENO);
+
+ // start player
+ pid_t player_pid;
+ if(posix_spawn(&player_pid, player_bin, &actions, NULL, args, environ)) {
+ perror("posix_spawn");
+ return NULL;
+ }
+ posix_spawn_file_actions_destroy(&actions);
+
+ Player *player = malloc(sizeof(Player));
+ memset(player, 0, sizeof(Player));
+
+ player->in = pin[1];
+ player->out = pout[0];
+ close(pin[0]);
+ close(pout[1]);
+ player->process = player_pid;
+
+ if(win->player) {
+ PlayerDestroy(win->player);
+ }
+ win->player = player;
+
+ return NULL;
+}
+
+void PlayerDestroy(Player *p) {
+ free(p);
+}
--- /dev/null
+/*
+ * Copyright 2022 Olaf Wintermann
+ *
+ * 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.
+ */
+
+
+#ifndef PLAYER_H
+#define PLAYER_H
+
+#include "window.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void PlayerOpenFile(MainWindow *win);
+
+void PlayerDestroy(Player *p);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PLAYER_H */
+
--- /dev/null
+/*
+ * Copyright 2022 Olaf Wintermann
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "window.h"
+#include "main.h"
+#include "player.h"
+
+#include "Fsb.h"
+
+static MainWindow *main_window;
+
+static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *args, int nargs);
+
+static void FileOpenCB(Widget w, void *udata, void *cdata);
+static void ViewFullscreenCB(Widget w, void *udata, void *cdata);
+
+static void window_close_handler(Widget window, void *udata, void *cdata) {
+ ApplicationExit();
+}
+
+static unsigned int keycodeF;
+
+static void windowKeyEH(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch) {
+ MainWindow *win = data;
+ if(win->fullscreen && event->xkey.keycode == keycodeF) {
+ WindowFullscreen(main_window, FALSE);
+ *dispatch = FALSE;
+ }
+}
+
+MainWindow* WindowCreate(Display *display) {
+ Arg args[32];
+ int n;
+
+ MainWindow *window = malloc(sizeof(MainWindow));
+ memset(window, 0, sizeof(MainWindow));
+ main_window = window;
+
+ // toplevel window
+ n = 0;
+ XtSetArg(args[n], XmNtitle, "MediaPlayer"); n++;
+ window->window = XtAppCreateShell(
+ "mediaplayer",
+ "mediaplayer",
+ //applicationShellWidgetClass,
+ vendorShellWidgetClass,
+ display,
+ args,
+ n);
+
+
+ Atom wm_delete_window;
+ wm_delete_window = XmInternAtom(
+ display,
+ "WM_DELETE_WINDOW",
+ 0);
+ XmAddWMProtocolCallback(
+ window->window,
+ wm_delete_window,
+ window_close_handler,
+ window);
+
+
+ n = 0;
+ XtSetArg(args[n], XmNwidth, 360); n++;
+ XtSetArg(args[n], XmNheight, 220); n++;
+ XtSetArg(args[n], XmNshadowThickness, 0); n++;
+ Widget container = XmCreateForm(window->window, "form", args, n);
+ XtManageChild(container);
+ XtAddEventHandler(container, KeyPressMask, FALSE, windowKeyEH, window);
+
+ n = 0;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ WindowCreateMenu(window, container, args, n);
+
+ n = 0;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
+ XtSetArg(args[n], XmNtopWidget, window->menubar); n++;
+ XtSetArg(args[n], XmNbackground, BlackPixelOfScreen(XtScreen(window->window))); n++;
+ window->player_widget = XmCreateDrawingArea(container, "player", args, n);
+ XtManageChild(window->player_widget);
+
+ // get F keycode
+ keycodeF = XKeysymToKeycode(XtDisplay(window->window), XStringToKeysym("F"));
+
+ return window;
+}
+
+void WindowShow(MainWindow *win) {
+ XtRealizeWidget(win->window);
+}
+
+/*
+ * Creates a XmPushButton menu item
+ */
+static Widget createMenuItem(
+ Widget menu,
+ char *name,
+ char *label,
+ char mnemonic,
+ const char *accelerator,
+ char *accelerator_text,
+ XtCallbackProc callback,
+ void *cbData)
+{
+ Arg args[16];
+ int n = 0;
+
+ XmString s1 = XmStringCreateSimple(label);
+ XtSetArg(args[n], XmNlabelString, s1); n++;
+ XtSetArg(args[n], XmNmnemonic, mnemonic); n++;
+ XmString at = NULL;
+ if(accelerator && accelerator_text) {
+ at = XmStringCreateSimple(accelerator_text);
+ XtSetArg(args[n], XmNaccelerator, accelerator); n++;
+ XtSetArg(args[n], XmNacceleratorText, at); n++;
+ }
+
+ Widget menuItem = XmCreatePushButtonGadget(menu, name, args, n);
+ XtManageChild(menuItem);
+ XmStringFree(s1);
+ if(at) XmStringFree(at);
+
+ if(callback) {
+ XtAddCallback(menuItem, XmNactivateCallback, (XtCallbackProc)callback, cbData);
+ }
+
+ return menuItem;
+}
+
+static void WindowCreateMenu(MainWindow *win, Widget parent, Arg *mbargs, int nmbargs) {
+ Widget menubar = XmCreateMenuBar(parent, "menubar", mbargs, nmbargs);
+ XtManageChild(menubar);
+ win->menubar = menubar;
+
+ Arg args[16];
+ int n;
+
+ // menus
+ XmString s = XmStringCreateSimple("File");
+ Widget fileMenuItem = XtVaCreateManagedWidget(
+ "menuitem",
+ xmCascadeButtonWidgetClass,
+ menubar,
+ XmNlabelString, s,
+ NULL);
+ XmStringFree(s);
+ Widget fileMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 0, NULL, NULL);
+
+ s = XmStringCreateSimple("Playback");
+ Widget playMenuItem = XtVaCreateManagedWidget(
+ "menuitem",
+ xmCascadeButtonWidgetClass,
+ menubar,
+ XmNlabelString, s,
+ NULL);
+ XmStringFree(s);
+ Widget playMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 1, NULL, NULL);
+
+ s = XmStringCreateSimple("View");
+ Widget viewMenuItem = XtVaCreateManagedWidget(
+ "menuitem",
+ xmCascadeButtonWidgetClass,
+ menubar,
+ XmNlabelString, s,
+ NULL);
+ XmStringFree(s);
+ Widget viewMenu = XmVaCreateSimplePulldownMenu(menubar, "menu", 2, NULL, NULL);
+
+ // file menu
+ createMenuItem(fileMenu, "fileOpen", "Open...", 'O', "Ctrl<Key>O", "Ctrl+O", FileOpenCB, NULL);
+
+ // view menu
+ createMenuItem(viewMenu, "viewFullscreen", "Fullscreen", 'F', "<Key>F", "F", ViewFullscreenCB, NULL);
+}
+
+void go_fullscreen(Display *dsp, Window win)
+{
+ XEvent xev;
+ Atom wm_state = XInternAtom(dsp, "_NET_WM_STATE", False);
+ Atom fullscreen = XInternAtom(dsp, "_NET_WM_STATE_FULLSCREEN", False);
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.xclient.window = win;
+ xev.xclient.message_type = wm_state;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
+ xev.xclient.data.l[1] = fullscreen;
+ xev.xclient.data.l[2] = 0;
+ XSendEvent(dsp, DefaultRootWindow(dsp), False,
+ SubstructureNotifyMask, &xev);
+}
+
+static Atom net_wm_state;
+static Atom net_wm_state_fullscreen;
+static int net_wm_atoms_initialized = 0;
+
+void WindowFullscreen(MainWindow *win, bool enableFullscreen) {
+ Display *dpy = XtDisplay(win->window);
+
+ // init net_wm_state atoms
+ if(!net_wm_atoms_initialized) {
+ net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
+ net_wm_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
+ net_wm_atoms_initialized = 1;
+ }
+
+ WindowMenubarSetVisible(win, !enableFullscreen);
+ if(enableFullscreen && !win->fullscreen) {
+ XtUnmanageChild(main_window->menubar);
+ main_window->fullscreen = TRUE;
+ } else if(!enableFullscreen && win->fullscreen) {
+ XtManageChild(main_window->menubar);
+ main_window->fullscreen = FALSE;
+ }
+
+ XEvent ev;
+ memset(&ev, 0, sizeof(XEvent));
+ ev.type = ClientMessage;
+ ev.xclient.window = XtWindow(win->window);
+ ev.xclient.message_type = net_wm_state;
+ ev.xclient.format = 32;
+ ev.xclient.data.l[0] = enableFullscreen ? 1 : 0;
+ ev.xclient.data.l[1] = net_wm_state_fullscreen;
+ ev.xclient.data.l[2] = 0;
+ XSendEvent(dpy, DefaultRootWindow(dpy), False, SubstructureNotifyMask | SubstructureRedirectMask, &ev);
+}
+
+void WindowMenubarSetVisible(MainWindow *win, bool visible) {
+ if(visible) {
+ XtManageChild(main_window->menubar);
+ XtVaSetValues(main_window->player_widget, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, main_window->menubar, NULL);
+ } else {
+ XtUnmanageChild(main_window->menubar);
+ XtVaSetValues(main_window->player_widget, XmNtopAttachment, XmATTACH_FORM, NULL);
+ }
+}
+
+
+
+static void filedialog_end(
+ Widget widget,
+ MainWindow *data,
+ XmFileSelectionBoxCallbackStruct *selection)
+{
+ XtUnmanageChild(widget);
+ XtDestroyWidget(widget);
+}
+
+static void filedialog_select(
+ Widget widget,
+ MainWindow *data,
+ XmFileSelectionBoxCallbackStruct *selection)
+{
+ char *value = NULL;
+ if(selection->value) {
+ XmStringGetLtoR(selection->value, XmSTRING_DEFAULT_CHARSET, &value);
+ if(value) {
+ if(data->file) {
+ XtFree(data->file);
+ }
+ data->file = value;
+ // no need to free the value, because it is stored in MainWindow
+
+ PlayerOpenFile(data);
+ }
+ }
+ filedialog_end(widget, data, NULL);
+}
+
+
+
+
+static void FileOpenCB(Widget w, void *udata, void *cdata) {
+ MainWindow *win = main_window;
+ Widget dialog = XnCreateFileSelectionDialog(win->window, "dialog", NULL, 0);
+ XtAddCallback(dialog, XmNokCallback, (XtCallbackProc)filedialog_select, win);
+ XtAddCallback(dialog, XmNcancelCallback, (XtCallbackProc)filedialog_end, win);
+ XtManageChild(dialog);
+}
+
+static void ViewFullscreenCB(Widget w, void *udata, void *cdata) {
+ if(main_window->fullscreen) {
+ WindowFullscreen(main_window, FALSE);
+ } else {
+ WindowFullscreen(main_window, TRUE);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2022 Olaf Wintermann
+ *
+ * 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.
+ */
+
+
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <Xm/XmAll.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct Player {
+ pid_t process;
+ int in;
+ int out;
+} Player;
+
+typedef struct MainWindow {
+ Widget window;
+ Widget menubar;
+ Widget player_widget;
+ char *file;
+ Player *player;
+ bool fullscreen;
+ bool mbvisible;
+} MainWindow;
+
+MainWindow* WindowCreate(Display *dp);
+
+void WindowShow(MainWindow *win);
+
+void WindowFullscreen(MainWindow *win, bool enableFullscreen);
+
+void WindowMenubarSetVisible(MainWindow *win, bool visible);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WINDOW_H */
+
--- /dev/null
+#!/bin/sh
+
+
+PREFIX=/usr
+EPREFIX=$PREFIX
+
+BINDIR=
+SBINDIR=
+LIBDIR=
+LIBEXECDIR=
+DATADIR=
+SYSCONFDIR=
+SHAREDSTATEDIR=
+LOCALSTATEDIR=
+INCLUDEDIR=
+INFODIR=
+MANDIR=
+
+OS=`uname -s`
+OS_VERSION=`uname -r`
+
+TEMP_DIR=".tmp-`uname -n`"
+mkdir -p $TEMP_DIR
+if [ $? -ne 0 ]; then
+ echo "Cannot create tmp dir"
+ echo "Abort"
+fi
+touch $TEMP_DIR/options
+touch $TEMP_DIR/features
+
+# features
+
+# help text
+printhelp()
+{
+ echo "Usage: $0 [OPTIONS]..."
+ cat << __EOF__
+Installation directories:
+ --prefix=PREFIX path prefix for architecture-independent files
+ [/usr]
+ --exec-prefix=EPREFIX path prefix for architecture-dependent files
+ [PREFIX]
+
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR system configuration files [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+
+__EOF__
+}
+
+#
+# parse arguments
+#
+for ARG in $@
+do
+ case "$ARG" in
+ "--prefix="*) PREFIX=${ARG#--prefix=} ;;
+ "--exec-prefix="*) EPREFIX=${ARG#--exec-prefix=} ;;
+ "--bindir="*) BINDIR=${ARG#----bindir=} ;;
+ "--sbindir="*) SBINDIR=${ARG#--sbindir=} ;;
+ "--libdir="*) LIBDIR=${ARG#--libdir=} ;;
+ "--libexecdir="*) LIBEXECDIR=${ARG#--libexecdir=} ;;
+ "--datadir="*) DATADIR=${ARG#--datadir=} ;;
+ "--sysconfdir="*) SYSCONFDIR=${ARG#--sysconfdir=} ;;
+ "--sharedstatedir="*) SHAREDSTATEDIR=${ARG#--sharedstatedir=} ;;
+ "--localstatedir="*) LOCALSTATEDIR=${ARG#--localstatedir=} ;;
+ "--includedir="*) INCLUDEDIR=${ARG#--includedir=} ;;
+ "--infodir="*) INFODIR=${ARG#--infodir=} ;;
+ "--mandir"*) MANDIR=${ARG#--mandir} ;;
+ "--help"*) printhelp; exit 1 ;;
+ "-"*) echo "unknown option: $ARG"; exit 1 ;;
+ esac
+done
+
+# set dir variables
+if [ -z "$BINDIR" ]; then
+ BINDIR=$EPREFIX/bin
+fi
+if [ -z "$SBINDIR" ]; then
+ SBINDIR=$EPREFIX/sbin
+fi
+if [ -z "$LIBDIR" ]; then
+ LIBDIR=$EPREFIX/lib
+fi
+if [ -z "$LIBEXEC" ]; then
+ LIBEXECDIR=$EPREFIX/libexec
+fi
+if [ -z "$DATADIR" ]; then
+ DATADIR=$PREFIX/share
+fi
+if [ -z "$SYSCONFDIR" ]; then
+ SYSCONFDIR=$PREFIX/etc
+fi
+if [ -z "$SHAREDSTATEDIR" ]; then
+ SHAREDSTATEDIR=$PREFIX/com
+fi
+if [ -z "$LOCALSTATEDIR" ]; then
+ LOCALSTATEDIR=$PREFIX/var
+fi
+if [ -z "$INCLUDEDIR" ]; then
+ INCLUDEDIR=$PREFIX/include
+fi
+if [ -z "$INFODIR" ]; then
+ INFODIR=$PREFIX/info
+fi
+if [ -z "$MANDIR" ]; then
+ MANDIR=$PREFIX/man
+fi
+
+which pkg-config > /dev/null
+if [ $? -eq 0 ]; then
+ PKG_CONFIG=pkg-config
+else
+ PKG_CONFIG=false
+fi
+
+# Simple uname based platform detection
+# $PLATFORM is used for platform dependent dependency selection
+printf "detect platform... "
+if [ $OS = SunOS ]; then
+ PLATFORM="solaris sunos unix svr4"
+fi
+if [ $OS = Linux ]; then
+ PLATFORM="linux unix"
+fi
+if [ $OS = FreeBSD ]; then
+ PLATFORM="freebsd bsd unix"
+fi
+if [ $OS = Darwin ]; then
+ PLATFORM="macos osx bsd unix"
+fi
+echo $OS | grep "MINGW" > /dev/null
+if [ $? -eq 0 ]; then
+ PLATFORM="windows mingw"
+fi
+
+if [ -z "$PLATFORM" ]; then
+ PLATFORM="unix"
+fi
+
+for p in $PLATFORM
+do
+ PLATFORM_NAME=$p
+ break
+done
+echo $PLATFORM_NAME
+
+isplatform()
+{
+ for p in $PLATFORM
+ do
+ if [ $p = $1 ]; then
+ return 0
+ fi
+ done
+ return 1
+}
+isnotplatform()
+{
+ for p in $PLATFORM
+ do
+ if [ $p = $1 ]; then
+ return 1
+ fi
+ done
+ return 0
+}
+
+# generate config.mk and config.h
+cat > $TEMP_DIR/config.mk << __EOF__
+#
+# config.mk generated by configure
+#
+
+# general vars
+
+PREFIX=$PREFIX
+EPREFIX=$EPREFIX
+
+BINDIR=$BINDIR
+SBINDIR=$SBINDIR
+LIBDIR=$LIBDIR
+LIBEXECDIR=$LIBEXECDIR
+DATADIR=$DATADIR
+SYSCONFDIR=$SYSCONFDIR
+SHAREDSTATEDIR=$SHAREDSTATEDIR
+LOCALSTATEDIR=$LOCALSTATEDIR
+INCLUDEDIR=$INCLUDEDIR
+INFODIR=$INFODIR
+MANDIR=$MANDIR
+
+__EOF__
+
+echo > $TEMP_DIR/make.mk
+
+ENV_CFLAGS=$CFLAGS
+ENV_LDFLAGS=$LDFLAGS
+ENV_CXXFLAGS=$CXXFLAGS
+
+# Toolchain detection
+# this will insert make vars to config.mk
+. make/toolchain.sh
+
+# add user specified flags to config.mk
+echo >> $TEMP_DIR/config.mk
+if [ ! -z "${ENV_CFLAGS}" ]; then
+ echo "CFLAGS += $ENV_CFLAGS" >> $TEMP_DIR/config.mk
+fi
+if [ ! -z "${ENV_CXXFLAGS}" ]; then
+ echo "CXXFLAGS += $ENV_CXXFLAGS" >> $TEMP_DIR/config.mk
+fi
+if [ ! -z "${ENV_LDFLAGS}" ]; then
+ echo "LDFLAGS += $ENV_LDFLAGS" >> $TEMP_DIR/config.mk
+fi
+
+#
+# DEPENDENCIES
+#
+
+dependency_motif()
+{
+ printf "checking for motif... "
+ # dependency motif
+ while true
+ do
+ CFLAGS="$CFLAGS -DUI_MOTIF"
+ LDFLAGS="$LDFLAGS -lXm -lXt -lX11 -lpthread"
+ echo yes
+ return 0
+ done
+
+ echo no
+ return 1
+}
+
+DEPENDENCIES_FAILED=
+ERROR=0
+# general dependencies
+CFLAGS=
+LDFLAGS=
+while true
+do
+ if isnotplatform "unix"; then
+ break
+ fi
+ while true
+ do
+
+ cat >> $TEMP_DIR/make.mk << __EOF__
+OBJ_EXT = o
+LIB_EXT = a
+PACKAGE_SCRIPT = package_unix.sh
+
+__EOF__
+
+ break
+ done
+
+ break
+done
+while true
+do
+ while true
+ do
+
+ LDFLAGS="$LDFLAGS -lpthread"
+
+ break
+ done
+
+ break
+done
+while true
+do
+ if isnotplatform "bsd"; then
+ break
+ fi
+ while true
+ do
+
+ CFLAGS="$CFLAGS -I/usr/local/include"
+ LDFLAGS="$LDFLAGS -L/usr/local/lib"
+
+ break
+ done
+
+ break
+done
+
+# add general dependency flags to config.mk
+echo >> $TEMP_DIR/config.mk
+if [ ! -z "${CFLAGS}" ]; then
+ echo "CFLAGS += $CFLAGS" >> $TEMP_DIR/config.mk
+fi
+if [ ! -z "${CXXFLAGS}" ]; then
+ echo "CXXFLAGS += $CXXFLAGS" >> $TEMP_DIR/config.mk
+fi
+if [ ! -z "${LDFLAGS}" ]; then
+ echo "LDFLAGS += $LDFLAGS" >> $TEMP_DIR/config.mk
+fi
+
+#
+# OPTION VALUES
+#
+
+#
+# TARGETS
+#
+CFLAGS=
+CXXFLAGS=
+LDFLAGS=
+
+# Target: app
+CFLAGS=
+LDFLAGS=
+CXXFLAGS=
+
+dependency_motif
+if [ $? -ne 0 ]; then
+ DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED motif "
+ ERROR=1
+fi
+
+# Features
+
+
+echo >> $TEMP_DIR/config.mk
+if [ ! -z "${CFLAGS}" ]; then
+ echo "APP_CFLAGS += $CFLAGS" >> $TEMP_DIR/config.mk
+fi
+if [ ! -z "${CXXFLAGS}" ]; then
+ echo "APP_CXXFLAGS += $CXXFLAGS" >> $TEMP_DIR/config.mk
+fi
+if [ ! -z "${LDFLAGS}" ]; then
+ echo "APP_LDFLAGS += $LDFLAGS" >> $TEMP_DIR/config.mk
+fi
+
+if [ $ERROR -ne 0 ]; then
+ echo
+ echo "Error: Unresolved dependencies"
+ echo $DEPENDENCIES_FAILED
+ rm -Rf $TEMP_DIR
+ exit 1
+fi
+
+echo "configure finished"
+echo
+echo "Build Config:"
+echo " PREFIX: $PREFIX"
+echo " TOOLCHAIN: $TOOLCHAIN_NAME"
+echo
+cat $TEMP_DIR/config.mk $TEMP_DIR/make.mk > config.mk
+rm -Rf $TEMP_DIR
+
+
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2021 Olaf Wintermann. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+# this makefile is invoked from the build root directory
+
+BUILD_ROOT = ./
+include config.mk
+
+BUILD_DIRS = build/bin build/lib
+BUILD_DIRS += build/ucx
+BUILD_DIRS += build/application
+
+all: $(BUILD_DIRS) ucx application
+ make/$(PACKAGE_SCRIPT)
+
+$(BUILD_DIRS):
+ mkdir -p $@
+
+ucx: $(BUILD_DIRS) FORCE
+ cd ucx; $(MAKE)
+
+application: $(BUILD_DIRS) ucx FORCE
+ cd application; $(MAKE)
+
+FORCE:
+
--- /dev/null
+#
+# clang toolchain config
+#
+
+CFLAGS =
+LDFLAGS =
+
+SHLIB_CFLAGS = -fPIC
+SHLIB_LDFLAGS = -shared
--- /dev/null
+#!/bin/sh
+
+#foreach( $var in $vars )
+#if( $var.exec )
+${var.name}=`${var.value}`
+#else
+${var.name}=${var.value}
+#end
+#end
+
+#if ( ! $project.hasVar("PREFIX") )
+PREFIX=/usr
+#end
+#if ( ! $project.hasVar("EPREFIX") )
+EPREFIX=$PREFIX
+#end
+
+#if ( ! $project.hasVar("BINDIR") )
+BINDIR=
+#end
+#if ( ! $project.hasVar("SBINDIR") )
+SBINDIR=
+#end
+#if ( ! $project.hasVar("LIBDIR") )
+LIBDIR=
+#end
+#if ( ! $project.hasVar("LIBEXECDIR") )
+LIBEXECDIR=
+#end
+#if ( ! $project.hasVar("DATADIR") )
+DATADIR=
+#end
+#if ( ! $project.hasVar("SYSCONFDIR") )
+SYSCONFDIR=
+#end
+#if ( ! $project.hasVar("SHAREDSTATEDIR") )
+SHAREDSTATEDIR=
+#end
+#if ( ! $project.hasVar("LOCALSTATEDIR") )
+LOCALSTATEDIR=
+#end
+#if ( ! $project.hasVar("INCLUDEDIR") )
+INCLUDEDIR=
+#end
+#if ( ! $project.hasVar("INFODIR") )
+INFODIR=
+#end
+#if ( ! $project.hasVar("MANDIR") )
+MANDIR=
+#end
+
+OS=`uname -s`
+OS_VERSION=`uname -r`
+
+TEMP_DIR=".tmp-`uname -n`"
+mkdir -p $TEMP_DIR
+if [ $? -ne 0 ]; then
+ echo "Cannot create tmp dir"
+ echo "Abort"
+fi
+touch $TEMP_DIR/options
+touch $TEMP_DIR/features
+
+# features
+#foreach( $feature in $features )
+#if( ${feature.isDefault()} )
+${feature.getVarName()}=on
+#end
+#end
+
+# help text
+printhelp()
+{
+ echo "Usage: $0 [OPTIONS]..."
+ cat << __EOF__
+Installation directories:
+ --prefix=PREFIX path prefix for architecture-independent files
+ [/usr]
+ --exec-prefix=EPREFIX path prefix for architecture-dependent files
+ [PREFIX]
+
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR system configuration files [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+
+#if( $options.size() > 0 )
+Options:
+#foreach( $opt in $options )
+ --${opt.getArgument()}=${opt.getValuesString()}
+#end
+
+#end
+#if( $features.size() > 0 )
+Optional Features:
+#foreach( $feature in $features )
+#if( $feature.default )
+ --disable-${feature.arg}
+#else
+ --enable-${feature.arg}
+#end
+#end
+
+#end
+__EOF__
+}
+
+#
+# parse arguments
+#
+#set( $D = '$' )
+for ARG in $@
+do
+ case "$ARG" in
+ "--prefix="*) PREFIX=${D}{ARG#--prefix=} ;;
+ "--exec-prefix="*) EPREFIX=${D}{ARG#--exec-prefix=} ;;
+ "--bindir="*) BINDIR=${D}{ARG#----bindir=} ;;
+ "--sbindir="*) SBINDIR=${D}{ARG#--sbindir=} ;;
+ "--libdir="*) LIBDIR=${D}{ARG#--libdir=} ;;
+ "--libexecdir="*) LIBEXECDIR=${D}{ARG#--libexecdir=} ;;
+ "--datadir="*) DATADIR=${D}{ARG#--datadir=} ;;
+ "--sysconfdir="*) SYSCONFDIR=${D}{ARG#--sysconfdir=} ;;
+ "--sharedstatedir="*) SHAREDSTATEDIR=${D}{ARG#--sharedstatedir=} ;;
+ "--localstatedir="*) LOCALSTATEDIR=${D}{ARG#--localstatedir=} ;;
+ "--includedir="*) INCLUDEDIR=${D}{ARG#--includedir=} ;;
+ "--infodir="*) INFODIR=${D}{ARG#--infodir=} ;;
+ "--mandir"*) MANDIR=${D}{ARG#--mandir} ;;
+ "--help"*) printhelp; exit 1 ;;
+ #foreach( $opt in $options )
+ "--${opt.getArgument()}="*) ${opt.getVarName()}=${D}{ARG#--${opt.getArgument()}=} ;;
+ #end
+ #foreach( $feature in $features )
+ "--enable-${feature.arg}") ${feature.getVarName()}=on ;;
+ "--disable-${feature.arg}") unset ${feature.getVarName()} ;;
+ #end
+ "-"*) echo "unknown option: $ARG"; exit 1 ;;
+ esac
+done
+
+# set dir variables
+if [ -z "$BINDIR" ]; then
+ BINDIR=$EPREFIX/bin
+fi
+if [ -z "$SBINDIR" ]; then
+ SBINDIR=$EPREFIX/sbin
+fi
+if [ -z "$LIBDIR" ]; then
+ LIBDIR=$EPREFIX/lib
+fi
+if [ -z "$LIBEXEC" ]; then
+ LIBEXECDIR=$EPREFIX/libexec
+fi
+if [ -z "$DATADIR" ]; then
+ DATADIR=$PREFIX/share
+fi
+if [ -z "$SYSCONFDIR" ]; then
+ SYSCONFDIR=$PREFIX/etc
+fi
+if [ -z "$SHAREDSTATEDIR" ]; then
+ SHAREDSTATEDIR=$PREFIX/com
+fi
+if [ -z "$LOCALSTATEDIR" ]; then
+ LOCALSTATEDIR=$PREFIX/var
+fi
+if [ -z "$INCLUDEDIR" ]; then
+ INCLUDEDIR=$PREFIX/include
+fi
+if [ -z "$INFODIR" ]; then
+ INFODIR=$PREFIX/info
+fi
+if [ -z "$MANDIR" ]; then
+ MANDIR=$PREFIX/man
+fi
+
+which pkg-config > /dev/null
+if [ $? -eq 0 ]; then
+ PKG_CONFIG=pkg-config
+else
+ PKG_CONFIG=false
+fi
+
+# Simple uname based platform detection
+# $PLATFORM is used for platform dependent dependency selection
+printf "detect platform... "
+if [ $OS = SunOS ]; then
+ PLATFORM="solaris sunos unix svr4"
+fi
+if [ $OS = Linux ]; then
+ PLATFORM="linux unix"
+fi
+if [ $OS = FreeBSD ]; then
+ PLATFORM="freebsd bsd unix"
+fi
+if [ $OS = Darwin ]; then
+ PLATFORM="macos osx bsd unix"
+fi
+echo $OS | grep "MINGW" > /dev/null
+if [ $? -eq 0 ]; then
+ PLATFORM="windows mingw"
+fi
+
+if [ -z "$PLATFORM" ]; then
+ PLATFORM="unix"
+fi
+
+for p in $PLATFORM
+do
+ PLATFORM_NAME=$p
+ break
+done
+echo $PLATFORM_NAME
+
+isplatform()
+{
+ for p in $PLATFORM
+ do
+ if [ $p = $1 ]; then
+ return 0
+ fi
+ done
+ return 1
+}
+isnotplatform()
+{
+ for p in $PLATFORM
+ do
+ if [ $p = $1 ]; then
+ return 1
+ fi
+ done
+ return 0
+}
+
+# generate config.mk and config.h
+cat > $TEMP_DIR/config.mk << __EOF__
+#
+# config.mk generated by configure
+#
+
+# general vars
+#foreach( $var in $vars )
+${var.name}=$${var.name}
+#end
+
+#if ( ! $project.hasVar("PREFIX") )
+PREFIX=$PREFIX
+#end
+#if ( ! $project.hasVar("EPREFIX") )
+EPREFIX=$EPREFIX
+#end
+
+#if ( ! $project.hasVar("BINDIR") )
+BINDIR=$BINDIR
+#end
+#if ( ! $project.hasVar("SBINDIR") )
+SBINDIR=$SBINDIR
+#end
+#if ( ! $project.hasVar("LIBDIR") )
+LIBDIR=$LIBDIR
+#end
+#if ( ! $project.hasVar("LIBEXECDIR") )
+LIBEXECDIR=$LIBEXECDIR
+#end
+#if ( ! $project.hasVar("DATADIR") )
+DATADIR=$DATADIR
+#end
+#if ( ! $project.hasVar("SYSCONFDIR") )
+SYSCONFDIR=$SYSCONFDIR
+#end
+#if ( ! $project.hasVar("SHAREDSTATEDIR") )
+SHAREDSTATEDIR=$SHAREDSTATEDIR
+#end
+#if ( ! $project.hasVar("LOCALSTATEDIR") )
+LOCALSTATEDIR=$LOCALSTATEDIR
+#end
+#if ( ! $project.hasVar("INCLUDEDIR") )
+INCLUDEDIR=$INCLUDEDIR
+#end
+#if ( ! $project.hasVar("INFODIR") )
+INFODIR=$INFODIR
+#end
+#if ( ! $project.hasVar("MANDIR") )
+MANDIR=$MANDIR
+#end
+
+__EOF__
+
+echo > $TEMP_DIR/make.mk
+
+ENV_CFLAGS=$CFLAGS
+ENV_LDFLAGS=$LDFLAGS
+ENV_CXXFLAGS=$CXXFLAGS
+
+# Toolchain detection
+# this will insert make vars to config.mk
+. make/toolchain.sh
+
+# add user specified flags to config.mk
+echo >> $TEMP_DIR/config.mk
+if [ ! -z "${ENV_CFLAGS}" ]; then
+ echo "CFLAGS += $ENV_CFLAGS" >> $TEMP_DIR/config.mk
+fi
+if [ ! -z "${ENV_CXXFLAGS}" ]; then
+ echo "CXXFLAGS += $ENV_CXXFLAGS" >> $TEMP_DIR/config.mk
+fi
+if [ ! -z "${ENV_LDFLAGS}" ]; then
+ echo "LDFLAGS += $ENV_LDFLAGS" >> $TEMP_DIR/config.mk
+fi
+
+#
+# DEPENDENCIES
+#
+
+#foreach( $dependency in $namedDependencies )
+dependency_${dependency.name}()
+{
+ printf "checking for ${dependency.name}... "
+ #foreach( $sub in $dependency.getSubdependencies() )
+ # dependency $sub.name $sub.getPlatformString()
+ while true
+ do
+ #if( $sub.platform )
+ if isnotplatform "${sub.platform}"; then
+ break
+ fi
+ #end
+ #foreach( $not in $sub.getNotList() )
+ if isplatform "${not}"; then
+ break
+ fi
+ #end
+ #if( $sub.pkgconfig.size() > 0 )
+ if [ -z "$PKG_CONFIG" ]; then
+ break
+ fi
+ #end
+ #foreach( $pkg in $sub.pkgconfig )
+ $PKG_CONFIG $pkg.getPkgConfigParam()
+ if [ $? -ne 0 ] ; then
+ break
+ fi
+ CFLAGS="$CFLAGS `$PKG_CONFIG --cflags $pkg.getPkgConfigParam()`"
+ LDFLAGS="$LDFLAGS `$PKG_CONFIG --libs $pkg.getPkgConfigParam()`"
+ #end
+ #foreach( $flags in $sub.flags )
+ #if( $flags.exec )
+ $flags.value > /dev/null
+ if [ $? -eq 0 ]; then
+ $flags.varName="$$flags.varName `$flags.value`"
+ else
+ break
+ fi
+ #else
+ $flags.varName="$$flags.varName $flags.value"
+ #end
+ #end
+ #foreach( $test in $sub.tests )
+ $test > /dev/null
+ if [ $? -ne 0 ]; then
+ break
+ fi
+ #end
+ #if ( $sub.make.length() > 0 )
+ cat >> $TEMP_DIR/make.mk << __EOF__
+# Dependency: $dependency.name
+$sub.make
+__EOF__
+ #end
+ echo yes
+ return 0
+ done
+
+ #end
+ echo no
+ return 1
+}
+#end
+
+DEPENDENCIES_FAILED=
+ERROR=0
+#if( $dependencies.size() > 0 )
+# general dependencies
+CFLAGS=
+LDFLAGS=
+#foreach( $dependency in $dependencies )
+while true
+do
+ #if( $dependency.platform )
+ if isnotplatform "${dependency.platform}"; then
+ break
+ fi
+ #end
+ #foreach( $not in $dependency.getNotList() )
+ if isplatform "${not}"; then
+ break
+ fi
+ #end
+ while true
+ do
+ #if( $dependency.pkgconfig.size() > 0 )
+ if [ -z "$PKG_CONFIG" ]; then
+ ERROR=1
+ break
+ fi
+ #end
+ #foreach( $pkg in $dependency.pkgconfig )
+ printf "checking for pkg-config package $pkg.getPkgConfigParam()... "
+ $PKG_CONFIG $pkg.getPkgConfigParam()
+ if [ $? -ne 0 ]; then
+ echo no
+ ERROR=1
+ break
+ fi
+ echo yes
+ CFLAGS="$CFLAGS `$PKG_CONFIG --cflags $pkg.getPkgConfigParam()`"
+ LDFLAGS="$LDFLAGS `$PKG_CONFIG --libs $pkg.getPkgConfigParam()`"
+ #end
+
+ #foreach( $flags in $dependency.flags )
+ #if( $flags.exec )
+ $flags.value > /dev/null
+ if [ $? -ne 0 ]; then
+ $flags.varName="$$flags.varName `$flags.value`"
+ else
+ ERROR=1
+ break
+ fi
+ #else
+ $flags.varName="$$flags.varName $flags.value"
+ #end
+ #end
+ #if ( $dependency.make.length() > 0 )
+ cat >> $TEMP_DIR/make.mk << __EOF__
+$dependency.make
+__EOF__
+ #end
+
+ break
+ done
+
+ break
+done
+#end
+
+# add general dependency flags to config.mk
+echo >> $TEMP_DIR/config.mk
+if [ ! -z "${CFLAGS}" ]; then
+ echo "CFLAGS += $CFLAGS" >> $TEMP_DIR/config.mk
+fi
+if [ ! -z "${CXXFLAGS}" ]; then
+ echo "CXXFLAGS += $CXXFLAGS" >> $TEMP_DIR/config.mk
+fi
+if [ ! -z "${LDFLAGS}" ]; then
+ echo "LDFLAGS += $LDFLAGS" >> $TEMP_DIR/config.mk
+fi
+#end
+
+#
+# OPTION VALUES
+#
+#foreach( $opt in $options )
+#foreach( $val in $opt.values )
+${val.func}()
+{
+ VERR=0
+ #foreach( $dep in $val.dependencies )
+ dependency_$dep
+ if [ $? -ne 0 ]; then
+ VERR=1
+ fi
+ #end
+ if [ $VERR -ne 0 ]; then
+ return 1
+ fi
+ #foreach( $def in $val.defines )
+ CFLAGS="$CFLAGS ${def.toFlags()}"
+ #end
+ #if( $val.hasMake() )
+ cat >> $TEMP_DIR/make.mk << __EOF__
+$val.make
+__EOF__
+ #end
+ return 0
+}
+#end
+#end
+
+#
+# TARGETS
+#
+CFLAGS=
+CXXFLAGS=
+LDFLAGS=
+
+#foreach( $target in $targets )
+#if ( $target.name )
+# Target: $target.name
+#else
+# Target
+#end
+CFLAGS=
+LDFLAGS=
+CXXFLAGS=
+
+#foreach( $dependency in $target.dependencies )
+dependency_$dependency
+if [ $? -ne 0 ]; then
+ DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED ${dependency} "
+ ERROR=1
+fi
+#end
+
+# Features
+#foreach( $feature in $target.features )
+if [ ! -z "$${feature.getVarName()}" ]; then
+#foreach( $dependency in $feature.dependencies )
+ # check dependency
+ dependency_$dependency
+ if [ $? -ne 0 ]; then
+ # "auto" features can fail and are just disabled in this case
+ if [ $${feature.getVarName()} != "auto" ]; then
+ DEPENDENCIES_FAILED="$DEPENDENCIES_FAILED ${dependency} "
+ ERROR=1
+ fi
+ fi
+#end
+fi
+#end
+
+#foreach( $opt in $target.options )
+# Option: --${opt.argument}
+if [ -z ${D}${opt.getVarName()} ]; then
+ SAVED_ERROR=$ERROR
+ SAVED_DEPENDENCIES_FAILED=$DEPENDENCIES_FAILED
+ ERROR=0
+ while true
+ do
+ #foreach( $optdef in $opt.defaults )
+ #if( $optdef.platform )
+ if isplatform "$optdef.platform"; then
+ #end
+ $optdef.func
+ if [ $? -eq 0 ]; then
+ echo " ${opt.argument}: ${optdef.valueName}" >> $TEMP_DIR/options
+ ERROR=0
+ break
+ fi
+ #if( $optdef.platform )
+ fi
+ #end
+ #end
+ break
+ done
+ if [ $ERROR -ne 0 ]; then
+ SAVED_ERROR=1
+ fi
+ ERROR=$SAVED_ERROR
+ DEPENDENCIES_FAILED=$SAVED_DEPENDENCIES_FAILED=
+else
+ if false; then
+ false
+ #foreach( $optval in $opt.values )
+ elif [ ${D}${opt.getVarName()} = "${optval.value}" ]; then
+ echo " ${opt.argument}: ${D}${opt.getVarName()}" >> $TEMP_DIR/options
+ $optval.func
+ if [ $? -ne 0 ]; then
+ ERROR=1
+ fi
+ #end
+ fi
+fi
+#end
+
+echo >> $TEMP_DIR/config.mk
+if [ ! -z "${CFLAGS}" ]; then
+ echo "${target.getCFlags()} += $CFLAGS" >> $TEMP_DIR/config.mk
+fi
+if [ ! -z "${CXXFLAGS}" ]; then
+ echo "${target.getCXXFlags()} += $CXXFLAGS" >> $TEMP_DIR/config.mk
+fi
+if [ ! -z "${LDFLAGS}" ]; then
+ echo "${target.getLDFlags()} += $LDFLAGS" >> $TEMP_DIR/config.mk
+fi
+
+#end
+if [ $ERROR -ne 0 ]; then
+ echo
+ echo "Error: Unresolved dependencies"
+ echo $DEPENDENCIES_FAILED
+ rm -Rf $TEMP_DIR
+ exit 1
+fi
+
+echo "configure finished"
+echo
+echo "Build Config:"
+echo " PREFIX: $PREFIX"
+echo " TOOLCHAIN: $TOOLCHAIN_NAME"
+#if ( $options.size() > 0 )
+echo "Options:"
+cat $TEMP_DIR/options
+#end
+echo
+cat $TEMP_DIR/config.mk $TEMP_DIR/make.mk > config.mk
+rm -Rf $TEMP_DIR
+
+
--- /dev/null
+#
+# gcc toolchain config
+#
+
+CFLAGS =
+LDFLAGS =
+
+SHLIB_CFLAGS = -fPIC
+SHLIB_LDFLAGS = -shared
+
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2011 Olaf Wintermann. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+CC = gcc
+LD = gcc
+AR = ar
+RM = rm
+MSBUILD = MSBuild.exe
+
+CFLAGS = -std=gnu99 -c -O2 -m64
+COFLAGS = -o
+LDFLAGS =
+LOFLAGS = -o
+ARFLAGS = -r
+RMFLAGS = -f
+
+OBJ_EXT = o
+LIB_EXT = a
+APP_EXT = .exe
+
+PACKAGE_SCRIPT = package_windows.sh
\ No newline at end of file
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2011 Olaf Wintermann. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+CC = gcc
+LD = gcc
+AR = ar
+RM = rm
+
+CFLAGS += -std=gnu99 -g -I/usr/include/libxml2
+LDFLAGS += -lxml2 -lz -lpthread -licucore -lm
+ARFLAGS = -r
+RMFLAGS = -f
+
+OBJ_EXT = o
+LIB_EXT = a
+APP_EXT =
+
+PACKAGE_SCRIPT = package_osx.sh
--- /dev/null
+#!/bin/sh
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project>
+ <dependency name="motif">
+ <cflags>-DUI_MOTIF</cflags>
+ <ldflags>-lXm -lXt -lX11 -lpthread</ldflags>
+ </dependency>
+
+ <dependency platform="unix">
+ <make>OBJ_EXT = o</make>
+ <make>LIB_EXT = a</make>
+ <make>PACKAGE_SCRIPT = package_unix.sh</make>
+ </dependency>
+
+ <dependency>
+ <ldflags>-lpthread</ldflags>
+ </dependency>
+
+ <dependency platform="bsd">
+ <cflags>-I/usr/local/include</cflags>
+ <ldflags>-L/usr/local/lib</ldflags>
+ </dependency>
+
+ <target name="app">
+ <dependencies>motif</dependencies>
+ </target>
+</project>
+
--- /dev/null
+#
+# suncc toolchain
+#
+
+CFLAGS =
+LDFLAGS =
+
+SHLIB_CFLAGS = -Kpic
+SHLIB_LDFLAGS = -G
+
--- /dev/null
+#!/bin/sh
+#
+# toolchain detection
+#
+
+C_COMPILERS="cc gcc clang suncc"
+CPP_COMPILERS="CC g++ clang++ sunCC"
+unset CC_ARG_CHECKED
+unset TOOLCHAIN_DETECTION_ERROR
+unset TOOLCHAIN_NAME
+
+check_c_compiler()
+{
+ cat > $TEMP_DIR/test.c << __EOF__
+/* test file */
+#include <stdio.h>
+int main(int argc, char **argv) {
+#if defined(__clang__)
+ printf("clang\n");
+#elif defined(__GNUC__)
+ printf("gcc\n");
+#elif defined(__sun)
+ printf("suncc\n");
+#else
+ printf("unknown\n");
+#endif
+ return 0;
+}
+__EOF__
+ rm -f $TEMP_DIR/checkcc
+ $1 -o $TEMP_DIR/checkcc $CFLAGS $LDFLAGS $TEMP_DIR/test.c 2> /dev/null
+
+ if [ $? -ne 0 ]; then
+ return 1
+ fi
+ return 0
+}
+
+check_cpp_compiler()
+{
+ cat > $TEMP_DIR/test.cpp << __EOF__
+/* test file */
+#include <iostream>
+int main(int argc, char **argv) {
+#if defined(__clang__)
+ std::cout << "clang" << std::endl;
+#elif defined(__GNUC__)
+ std::cout << "gcc" << std::endl;
+#elif defined(__sun)
+ std::cout << "suncc" << std::endl;
+#else
+ std::cout << "unknown" << std::endl;
+#endif
+ return 0;
+}
+__EOF__
+ rm -f $TEMP_DIR/checkcc
+ $1 -o $TEMP_DIR/checkcc $CXXFLAGS $LDFLAGS $TEMP_DIR/test.cpp 2> /dev/null
+
+ if [ $? -ne 0 ]; then
+ return 1
+ fi
+ return 0
+}
+
+printf "detect C compiler... "
+
+for COMP in $C_COMPILERS
+do
+ check_c_compiler $COMP
+ if [ $? -ne 0 ]; then
+ if [ ! -z "$CC" ]; then
+ if [ $COMP = $CC ]; then
+ echo "$CC is not a working C Compiler"
+ TOOLCHAIN_DETECTION_ERROR="error"
+ break
+ fi
+ fi
+ else
+ TOOLCHAIN_NAME=`$TEMP_DIR/checkcc`
+ USE_TOOLCHAIN=$TOOLCHAIN_NAME
+ if [ $COMP = "cc" ]; then
+ # we have found a working compiler, but in case
+ # the compiler is gcc or clang, we try to use
+ # these commands and not 'cc'
+ TOOLCHAIN_NAME=`$TEMP_DIR/checkcc`
+ if [ $TOOLCHAIN_NAME = "gcc" ]; then
+ check_c_compiler "gcc"
+ if [ $? -eq 0 ]; then
+ COMP=gcc
+ USE_TOOLCHAIN="gcc"
+ fi
+ fi
+ if [ $TOOLCHAIN_NAME = "clang" ]; then
+ check_c_compiler "clang"
+ if [ $? -eq 0 ]; then
+ COMP=clang
+ USE_TOOLCHAIN="clang"
+ fi
+ fi
+ fi
+
+ TOOLCHAIN_NAME=$USE_TOOLCHAIN
+ TOOLCHAIN_CC=$COMP
+ echo $COMP
+ break
+ fi
+done
+if [ -z $TOOLCHAIN_CC ]; then
+ echo "not found"
+fi
+
+printf "detect C++ compiler... "
+
+for COMP in $CPP_COMPILERS
+do
+ check_cpp_compiler $COMP
+ if [ $? -ne 0 ]; then
+ if [ ! -z "$CXX" ]; then
+ if [ $COMP = $CXX ]; then
+ echo "$CC is not a working C++ Compiler"
+ TOOLCHAIN_DETECTION_ERROR="error"
+ break
+ fi
+ fi
+ else
+ if [ $COMP = "CC" ]; then
+ # we have found a working compiler, but in case
+ # the compiler is gcc or clang, we try to use
+ # these commands and not 'cc'
+ TOOLCHAIN_NAME=`$TEMP_DIR/checkcc`
+ USE_TOOLCHAIN=$TOOLCHAIN_NAME
+ if [ $TOOLCHAIN_NAME = "gcc" ]; then
+ check_cpp_compiler "g++"
+ if [ $? -eq 0 ]; then
+ COMP=g++
+ USE_TOOLCHAIN="gcc"
+ fi
+ fi
+ if [ $TOOLCHAIN_NAME = "clang" ]; then
+ check_cpp_compiler "clang++"
+ if [ $? -eq 0 ]; then
+ COMP=clang++
+ USE_TOOLCHAIN="clang"
+ fi
+ fi
+ fi
+
+ TOOLCHAIN_NAME=$USE_TOOLCHAIN
+ TOOLCHAIN_CXX=$COMP
+ echo $COMP
+ break
+ fi
+done
+if [ -z $TOOLCHAIN_CXX ]; then
+ echo "not found"
+fi
+
+TOOLCHAIN_LD=$TOOLCHAIN_CC
+
+if [ -z "$TOOLCHAIN_NAME" ]; then
+ TOOLCHAIN_DETECTION_ERROR="error"
+else
+ cat >> $TEMP_DIR/config.mk << __EOF__
+# toolchain
+__EOF__
+ echo "CC = ${TOOLCHAIN_CC}" >> $TEMP_DIR/config.mk
+ if [ ! -z "$TOOLCHAIN_CXX" ]; then
+ echo "CXX = ${TOOLCHAIN_CXX}" >> $TEMP_DIR/config.mk
+ fi
+ echo "LD = ${TOOLCHAIN_LD}" >> $TEMP_DIR/config.mk
+ echo >> $TEMP_DIR/config.mk
+
+ cat "make/${TOOLCHAIN_NAME}.mk" > /dev/null 2>&1
+ if [ $? -eq 0 ]; then
+ echo "include \$(BUILD_ROOT)/make/${TOOLCHAIN_NAME}.mk" >> $TEMP_DIR/config.mk
+ else
+ echo "SHLIB_CFLAGS = -fPIC" >> $TEMP_DIR/config.mk
+ echo "SHLIB_LDFLAGS = -shared" >> $TEMP_DIR/config.mk
+ fi
+fi
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2011 Olaf Wintermann. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+CC = gcc
+LD = gcc
+AR = ar
+RM = rm
+
+CFLAGS = -std=gnu99
+LDFLAGS =
+ARFLAGS = -r
+RMFLAGS = -f
+
+OBJ_EXT = obj
+LIB_EXT = lib
+APP_EXT = .exe
+
--- /dev/null
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2013 Olaf Wintermann. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+BUILD_ROOT = ../
+include ../config.mk
+
+# list of source files
+SRC = utils.c
+SRC += list.c
+SRC += map.c
+SRC += avl.c
+SRC += properties.c
+SRC += mempool.c
+SRC += string.c
+SRC += test.c
+SRC += allocator.c
+SRC += logging.c
+SRC += buffer.c
+SRC += stack.c
+SRC += ucx.c
+SRC += array.c
+
+OBJ = $(SRC:%.c=../build/ucx/%.$(OBJ_EXT))
+
+UCX_LIB = ../build/lib/libucx.$(LIB_EXT)
+
+all: ../build/ucx $(UCX_LIB)
+
+$(UCX_LIB): $(OBJ)
+ $(AR) $(ARFLAGS) $(UCX_LIB) $(OBJ)
+
+../build/ucx:
+ mkdir -p ../build/ucx
+
+../build/ucx/%.$(OBJ_EXT): %.c
+ $(CC) $(CFLAGS) -o $@ -c $<
+
--- /dev/null
+UCX is a library for common data structures, algorithms and string functions.
+
+More informations at: https://develop.uap-core.de/ucx/
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucx/allocator.h"
+
+#include <stdlib.h>
+
+static UcxAllocator default_allocator = {
+ NULL,
+ ucx_default_malloc,
+ ucx_default_calloc,
+ ucx_default_realloc,
+ ucx_default_free
+};
+
+UcxAllocator *ucx_default_allocator() {
+ UcxAllocator *allocator = &default_allocator;
+ return allocator;
+}
+
+void *ucx_default_malloc(void *ignore, size_t n) {
+ return malloc(n);
+}
+
+void *ucx_default_calloc(void *ignore, size_t n, size_t size) {
+ return calloc(n, size);
+}
+
+void *ucx_default_realloc(void *ignore, void *data, size_t n) {
+ return realloc(data, n);
+}
+
+void ucx_default_free(void *ignore, void *data) {
+ free(data);
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2019 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE /* we want to use qsort_r(), if available */
+#define __STDC_WANT_LIB_EXT1__ 1 /* use qsort_s, if available */
+
+
+#include "ucx/array.h"
+#include "ucx/utils.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifndef UCX_ARRAY_DISABLE_QSORT
+#ifdef __GLIBC__
+#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
+#define ucx_array_sort_impl qsort_r
+#endif /* glibc version >= 2.8 */
+#elif /* not __GLIBC__ */ defined(__APPLE__) || defined(__FreeBSD__)
+#define ucx_array_sort_impl ucx_qsort_r
+#define USE_UCX_QSORT_R
+#elif /* not (__APPLE || __FreeBSD__) */ defined(__sun)
+#if __STDC_VERSION__ >= 201112L
+#define ucx_array_sort_impl qsort_s
+#endif
+#endif /* __GLIBC__, __APLE__, __FreeBSD__, __sun */
+#endif /* UCX_ARRAY_DISABLE_QSORT */
+
+#ifndef ucx_array_sort_impl
+#define ucx_array_sort_impl ucx_mergesort
+#endif
+
+static int ucx_array_ensurecap(UcxArray *array, size_t reqcap) {
+ size_t required_capacity = array->capacity;
+ while (reqcap > required_capacity) {
+ if (required_capacity * 2 < required_capacity)
+ return 1;
+ required_capacity <<= 1;
+ }
+ if (ucx_array_reserve(array, required_capacity)) {
+ return 1;
+ }
+ return 0;
+}
+
+int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity,
+ size_t elmsize, size_t index, void* data) {
+
+ if(!alloc || !capacity || !array) {
+ errno = EINVAL;
+ return 1;
+ }
+
+ size_t newcapacity = *capacity;
+ while(index >= newcapacity) {
+ if(ucx_szmul(newcapacity, 2, &newcapacity)) {
+ errno = EOVERFLOW;
+ return 1;
+ }
+ }
+
+ size_t memlen, offset;
+ if(ucx_szmul(newcapacity, elmsize, &memlen)) {
+ errno = EOVERFLOW;
+ return 1;
+ }
+ /* we don't need to check index*elmsize - it is smaller than memlen */
+
+
+ void* newptr = alrealloc(alloc, *array, memlen);
+ if(newptr == NULL) {
+ errno = ENOMEM; /* we cannot assume that every allocator sets this */
+ return 1;
+ }
+ *array = newptr;
+ *capacity = newcapacity;
+
+
+ char* dest = *array;
+ dest += elmsize*index;
+ memcpy(dest, data, elmsize);
+
+ return 0;
+}
+
+int ucx_array_util_setptr_a(UcxAllocator* alloc, void** array, size_t* capacity,
+ size_t index, void* data) {
+
+ return ucx_array_util_set_a(alloc, array, capacity, sizeof(void*),
+ index, &data);
+}
+
+UcxArray* ucx_array_new(size_t capacity, size_t elemsize) {
+ return ucx_array_new_a(capacity, elemsize, ucx_default_allocator());
+}
+
+UcxArray* ucx_array_new_a(size_t capacity, size_t elemsize,
+ UcxAllocator* allocator) {
+ UcxArray* array = almalloc(allocator, sizeof(UcxArray));
+ if(array) {
+ ucx_array_init_a(array, capacity, elemsize, allocator);
+ }
+ return array;
+}
+
+void ucx_array_init(UcxArray* array, size_t capacity, size_t elemsize) {
+ ucx_array_init_a(array, capacity, elemsize, ucx_default_allocator());
+}
+
+void ucx_array_init_a(UcxArray* array, size_t capacity, size_t elemsize,
+ UcxAllocator* allocator) {
+
+ array->allocator = allocator;
+ array->elemsize = elemsize;
+ array->size = 0;
+ array->data = alcalloc(allocator, capacity, elemsize);
+
+ if (array->data) {
+ array->capacity = capacity;
+ } else {
+ array->capacity = 0;
+ }
+}
+
+int ucx_array_clone(UcxArray* dest, UcxArray const* src) {
+ if (ucx_array_ensurecap(dest, src->capacity)) {
+ return 1;
+ }
+
+ dest->elemsize = src->elemsize;
+ dest->size = src->size;
+
+ if (dest->data) {
+ memcpy(dest->data, src->data, src->size*src->elemsize);
+ }
+
+ return 0;
+}
+
+int ucx_array_equals(UcxArray const *array1, UcxArray const *array2,
+ cmp_func cmpfnc, void* data) {
+
+ if (array1->size != array2->size || array1->elemsize != array2->elemsize) {
+ return 0;
+ } else {
+ if (array1->size == 0)
+ return 1;
+
+ size_t elemsize;
+ if (cmpfnc == NULL) {
+ cmpfnc = ucx_cmp_mem;
+ elemsize = array1->elemsize;
+ data = &elemsize;
+ }
+
+ for (size_t i = 0 ; i < array1->size ; i++) {
+ int r = cmpfnc(
+ ucx_array_at(array1, i),
+ ucx_array_at(array2, i),
+ data);
+ if (r != 0)
+ return 0;
+ }
+ return 1;
+ }
+}
+
+void ucx_array_destroy(UcxArray *array) {
+ if(array->data)
+ alfree(array->allocator, array->data);
+ array->data = NULL;
+ array->capacity = array->size = 0;
+}
+
+void ucx_array_free(UcxArray *array) {
+ ucx_array_destroy(array);
+ alfree(array->allocator, array);
+}
+
+int ucx_array_append_from(UcxArray *array, void *data, size_t count) {
+ if (ucx_array_ensurecap(array, array->size + count))
+ return 1;
+
+ void* dest = ucx_array_at(array, array->size);
+ if (data) {
+ memcpy(dest, data, array->elemsize*count);
+ } else {
+ memset(dest, 0, array->elemsize*count);
+ }
+ array->size += count;
+
+ return 0;
+}
+
+int ucx_array_prepend_from(UcxArray *array, void *data, size_t count) {
+ if (ucx_array_ensurecap(array, array->size + count))
+ return 1;
+
+ if (array->size > 0) {
+ void *dest = ucx_array_at(array, count);
+ memmove(dest, array->data, array->elemsize*array->size);
+ }
+
+ if (data) {
+ memcpy(array->data, data, array->elemsize*count);
+ } else {
+ memset(array->data, 0, array->elemsize*count);
+ }
+ array->size += count;
+
+ return 0;
+}
+
+int ucx_array_set_from(UcxArray *array, size_t index,
+ void *data, size_t count) {
+ if (ucx_array_ensurecap(array, index + count))
+ return 1;
+
+ if (index+count > array->size) {
+ array->size = index+count;
+ }
+
+ void *dest = ucx_array_at(array, index);
+ if (data) {
+ memcpy(dest, data, array->elemsize*count);
+ } else {
+ memset(dest, 0, array->elemsize*count);
+ }
+
+ return 0;
+}
+
+int ucx_array_concat(UcxArray *array1, const UcxArray *array2) {
+
+ if (array1->elemsize != array2->elemsize)
+ return 1;
+
+ size_t capacity = array1->capacity+array2->capacity;
+
+ if (array1->capacity < capacity) {
+ if (ucx_array_reserve(array1, capacity)) {
+ return 1;
+ }
+ }
+
+ void* dest = ucx_array_at(array1, array1->size);
+ memcpy(dest, array2->data, array2->size*array2->elemsize);
+
+ array1->size += array2->size;
+
+ return 0;
+}
+
+void *ucx_array_at(UcxArray const *array, size_t index) {
+ char* memory = array->data;
+ char* loc = memory + index*array->elemsize;
+ return loc;
+}
+
+size_t ucx_array_find(UcxArray const *array, void *elem,
+ cmp_func cmpfnc, void *data) {
+
+ size_t elemsize;
+ if (cmpfnc == NULL) {
+ cmpfnc = ucx_cmp_mem;
+ elemsize = array->elemsize;
+ data = &elemsize;
+ }
+
+ if (array->size > 0) {
+ for (size_t i = 0 ; i < array->size ; i++) {
+ void* ptr = ucx_array_at(array, i);
+ if (cmpfnc(ptr, elem, data) == 0) {
+ return i;
+ }
+ }
+ return array->size;
+ } else {
+ return 0;
+ }
+}
+
+int ucx_array_contains(UcxArray const *array, void *elem,
+ cmp_func cmpfnc, void *data) {
+ return ucx_array_find(array, elem, cmpfnc, data) != array->size;
+}
+
+static void ucx_mergesort_merge(void *arrdata,size_t elemsize,
+ cmp_func cmpfnc, void *data,
+ size_t start, size_t mid, size_t end) {
+
+ char* array = arrdata;
+
+ size_t rightstart = mid + 1;
+
+ if (cmpfnc(array + mid*elemsize,
+ array + rightstart*elemsize, data) <= 0) {
+ /* already sorted */
+ return;
+ }
+
+ /* we need memory for one element */
+ void *value = malloc(elemsize);
+
+ while (start <= mid && rightstart <= end) {
+ if (cmpfnc(array + start*elemsize,
+ array + rightstart*elemsize, data) <= 0) {
+ start++;
+ } else {
+ /* save the value from the right */
+ memcpy(value, array + rightstart*elemsize, elemsize);
+
+ /* shift all left elements one element to the right */
+ size_t shiftcount = rightstart-start;
+ void *startptr = array + start*elemsize;
+ void *dest = array + (start+1)*elemsize;
+ memmove(dest, startptr, shiftcount*elemsize);
+
+ /* bring the first value from the right to the left */
+ memcpy(startptr, value, elemsize);
+
+ start++;
+ mid++;
+ rightstart++;
+ }
+ }
+
+ /* free the temporary memory */
+ free(value);
+}
+
+static void ucx_mergesort_impl(void *arrdata, size_t elemsize,
+ cmp_func cmpfnc, void *data, size_t l, size_t r) {
+ if (l < r) {
+ size_t m = l + (r - l) / 2;
+
+ ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, l, m);
+ ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, m + 1, r);
+ ucx_mergesort_merge(arrdata, elemsize, cmpfnc, data, l, m, r);
+ }
+}
+
+static void ucx_mergesort(void *arrdata, size_t count, size_t elemsize,
+ cmp_func cmpfnc, void *data) {
+
+ ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, 0, count-1);
+}
+
+#ifdef USE_UCX_QSORT_R
+struct cmpfnc_swapargs_info {
+ cmp_func func;
+ void *data;
+};
+
+static int cmp_func_swap_args(void *data, const void *x, const void *y) {
+ struct cmpfnc_swapargs_info* info = data;
+ return info->func(x, y, info->data);
+}
+
+static void ucx_qsort_r(void *array, size_t count, size_t elemsize,
+ cmp_func cmpfnc, void *data) {
+ struct cmpfnc_swapargs_info info;
+ info.func = cmpfnc;
+ info.data = data;
+ qsort_r(array, count, elemsize, &info, cmp_func_swap_args);
+}
+#endif /* USE_UCX_QSORT_R */
+
+void ucx_array_sort(UcxArray* array, cmp_func cmpfnc, void *data) {
+ ucx_array_sort_impl(array->data, array->size, array->elemsize,
+ cmpfnc, data);
+}
+
+void ucx_array_remove(UcxArray *array, size_t index) {
+ array->size--;
+ if (index < array->size) {
+ void* dest = ucx_array_at(array, index);
+ void* src = ucx_array_at(array, index+1);
+ memmove(dest, src, (array->size - index)*array->elemsize);
+ }
+}
+
+void ucx_array_remove_fast(UcxArray *array, size_t index) {
+ array->size--;
+ if (index < array->size) {
+ void* dest = ucx_array_at(array, index);
+ void* src = ucx_array_at(array, array->size);
+ memcpy(dest, src, array->elemsize);
+ }
+}
+
+int ucx_array_shrink(UcxArray* array) {
+ void* newptr = alrealloc(array->allocator, array->data,
+ array->size*array->elemsize);
+ if (newptr) {
+ array->data = newptr;
+ array->capacity = array->size;
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+int ucx_array_resize(UcxArray* array, size_t capacity) {
+ if (array->capacity >= capacity) {
+ void* newptr = alrealloc(array->allocator, array->data,
+ capacity*array->elemsize);
+ if (newptr) {
+ array->data = newptr;
+ array->capacity = capacity;
+ if (array->size > array->capacity) {
+ array->size = array->capacity;
+ }
+ return 0;
+ } else {
+ return 1;
+ }
+ } else {
+ return ucx_array_reserve(array, capacity);
+ }
+}
+
+int ucx_array_reserve(UcxArray* array, size_t capacity) {
+ if (array->capacity > capacity) {
+ return 0;
+ } else {
+ void* newptr = alrealloc(array->allocator, array->data,
+ capacity*array->elemsize);
+ if (newptr) {
+ array->data = newptr;
+ array->capacity = capacity;
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+}
+
+int ucx_array_grow(UcxArray* array, size_t count) {
+ return ucx_array_reserve(array, array->size+count);
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucx/avl.h"
+
+#include <limits.h>
+
+#define ptrcast(ptr) ((void*)(ptr))
+#define alloc_tree(al) (UcxAVLTree*) almalloc((al), sizeof(UcxAVLTree))
+#define alloc_node(al) (UcxAVLNode*) almalloc((al), sizeof(UcxAVLNode))
+
+static void ucx_avl_connect(UcxAVLTree *tree,
+ UcxAVLNode *node, UcxAVLNode *child, intptr_t nullkey) {
+ if (child) {
+ child->parent = node;
+ }
+ // if child is NULL, nullkey decides if left or right pointer is cleared
+ if (tree->cmpfunc(
+ ptrcast(child ? child->key : nullkey),
+ ptrcast(node->key), tree->userdata) > 0) {
+ node->right = child;
+ } else {
+ node->left = child;
+ }
+ size_t lh = node->left ? node->left->height : 0;
+ size_t rh = node->right ? node->right->height : 0;
+ node->height = 1 + (lh > rh ? lh : rh);
+}
+
+#define avlheight(node) ((node) ? (node)->height : 0)
+
+static UcxAVLNode* avl_rotright(UcxAVLTree *tree, UcxAVLNode *l0) {
+ UcxAVLNode *p = l0->parent;
+ UcxAVLNode *l1 = l0->left;
+ if (p) {
+ ucx_avl_connect(tree, p, l1, 0);
+ } else {
+ l1->parent = NULL;
+ }
+ ucx_avl_connect(tree, l0, l1->right, l1->key);
+ ucx_avl_connect(tree, l1, l0, 0);
+ return l1;
+}
+
+static UcxAVLNode* avl_rotleft(UcxAVLTree *tree, UcxAVLNode *l0) {
+ UcxAVLNode *p = l0->parent;
+ UcxAVLNode *l1 = l0->right;
+ if (p) {
+ ucx_avl_connect(tree, p, l1, 0);
+ } else {
+ l1->parent = NULL;
+ }
+ ucx_avl_connect(tree, l0, l1->left, l1->key);
+ ucx_avl_connect(tree, l1, l0, 0);
+ return l1;
+}
+
+static void ucx_avl_balance(UcxAVLTree *tree, UcxAVLNode *n) {
+ int lh = avlheight(n->left);
+ int rh = avlheight(n->right);
+ n->height = 1 + (lh > rh ? lh : rh);
+
+ if (lh - rh == 2) {
+ UcxAVLNode *c = n->left;
+ if (avlheight(c->right) - avlheight(c->left) == 1) {
+ avl_rotleft(tree, c);
+ }
+ n = avl_rotright(tree, n);
+ } else if (rh - lh == 2) {
+ UcxAVLNode *c = n->right;
+ if (avlheight(c->left) - avlheight(c->right) == 1) {
+ avl_rotright(tree, c);
+ }
+ n = avl_rotleft(tree, n);
+ }
+
+ if (n->parent) {
+ ucx_avl_balance(tree, n->parent);
+ } else {
+ tree->root = n;
+ }
+}
+
+UcxAVLTree *ucx_avl_new(cmp_func cmpfunc) {
+ return ucx_avl_new_a(cmpfunc, ucx_default_allocator());
+}
+
+UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator) {
+ UcxAVLTree* tree = alloc_tree(allocator);
+ if (tree) {
+ tree->allocator = allocator;
+ tree->cmpfunc = cmpfunc;
+ tree->root = NULL;
+ tree->userdata = NULL;
+ }
+
+ return tree;
+}
+
+static void ucx_avl_free_node(UcxAllocator *al, UcxAVLNode *node) {
+ if (node) {
+ ucx_avl_free_node(al, node->left);
+ ucx_avl_free_node(al, node->right);
+ alfree(al, node);
+ }
+}
+
+void ucx_avl_free(UcxAVLTree *tree) {
+ UcxAllocator *al = tree->allocator;
+ ucx_avl_free_node(al, tree->root);
+ alfree(al, tree);
+}
+
+static void ucx_avl_free_content_node(UcxAllocator *al, UcxAVLNode *node,
+ ucx_destructor destr) {
+ if (node) {
+ ucx_avl_free_content_node(al, node->left, destr);
+ ucx_avl_free_content_node(al, node->right, destr);
+ if (destr) {
+ destr(node->value);
+ } else {
+ alfree(al, node->value);
+ }
+ }
+}
+
+void ucx_avl_free_content(UcxAVLTree *tree, ucx_destructor destr) {
+ ucx_avl_free_content_node(tree->allocator, tree->root, destr);
+}
+
+UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key) {
+ UcxAVLNode *n = tree->root;
+ int cmpresult;
+ while (n && (cmpresult = tree->cmpfunc(
+ ptrcast(key), ptrcast(n->key), tree->userdata))) {
+ n = cmpresult > 0 ? n->right : n->left;
+ }
+ return n;
+}
+
+void *ucx_avl_get(UcxAVLTree *tree, intptr_t key) {
+ UcxAVLNode *n = ucx_avl_get_node(tree, key);
+ return n ? n->value : NULL;
+}
+
+UcxAVLNode *ucx_avl_find_node(UcxAVLTree *tree, intptr_t key,
+ distance_func dfnc, int mode) {
+ UcxAVLNode *n = tree->root;
+ UcxAVLNode *closest = NULL;
+
+ intmax_t cmpresult;
+ intmax_t closest_dist;
+ closest_dist = mode == UCX_AVL_FIND_LOWER_BOUNDED ? INTMAX_MIN : INTMAX_MAX;
+
+ while (n && (cmpresult = dfnc(
+ ptrcast(key), ptrcast(n->key), tree->userdata))) {
+ if (mode == UCX_AVL_FIND_CLOSEST) {
+ intmax_t dist = cmpresult;
+ if (dist < 0) dist *= -1;
+ if (dist < closest_dist) {
+ closest_dist = dist;
+ closest = n;
+ }
+ } else if (mode == UCX_AVL_FIND_LOWER_BOUNDED && cmpresult <= 0) {
+ if (cmpresult > closest_dist) {
+ closest_dist = cmpresult;
+ closest = n;
+ }
+ } else if (mode == UCX_AVL_FIND_UPPER_BOUNDED && cmpresult >= 0) {
+ if (cmpresult < closest_dist) {
+ closest_dist = cmpresult;
+ closest = n;
+ }
+ }
+ n = cmpresult > 0 ? n->right : n->left;
+ }
+ return n ? n : closest;
+}
+
+void *ucx_avl_find(UcxAVLTree *tree, intptr_t key,
+ distance_func dfnc, int mode) {
+ UcxAVLNode *n = ucx_avl_find_node(tree, key, dfnc, mode);
+ return n ? n->value : NULL;
+}
+
+int ucx_avl_put(UcxAVLTree *tree, intptr_t key, void *value) {
+ return ucx_avl_put_s(tree, key, value, NULL);
+}
+
+int ucx_avl_put_s(UcxAVLTree *tree, intptr_t key, void *value,
+ void **oldvalue) {
+ if (tree->root) {
+ UcxAVLNode *n = tree->root;
+ int cmpresult;
+ while ((cmpresult = tree->cmpfunc(
+ ptrcast(key), ptrcast(n->key), tree->userdata))) {
+ UcxAVLNode *m = cmpresult > 0 ? n->right : n->left;
+ if (m) {
+ n = m;
+ } else {
+ break;
+ }
+ }
+
+ if (cmpresult) {
+ UcxAVLNode* e = alloc_node(tree->allocator);
+ if (e) {
+ e->key = key; e->value = value; e->height = 1;
+ e->parent = e->left = e->right = NULL;
+ ucx_avl_connect(tree, n, e, 0);
+ ucx_avl_balance(tree, n);
+ return 0;
+ } else {
+ return 1;
+ }
+ } else {
+ if (oldvalue) {
+ *oldvalue = n->value;
+ }
+ n->value = value;
+ return 0;
+ }
+ } else {
+ tree->root = alloc_node(tree->allocator);
+ if (tree->root) {
+ tree->root->key = key; tree->root->value = value;
+ tree->root->height = 1;
+ tree->root->parent = tree->root->left = tree->root->right = NULL;
+
+ if (oldvalue) {
+ *oldvalue = NULL;
+ }
+
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+}
+
+int ucx_avl_remove(UcxAVLTree *tree, intptr_t key) {
+ return ucx_avl_remove_s(tree, key, NULL, NULL);
+}
+
+int ucx_avl_remove_node(UcxAVLTree *tree, UcxAVLNode *node) {
+ return ucx_avl_remove_s(tree, node->key, NULL, NULL);
+}
+
+int ucx_avl_remove_s(UcxAVLTree *tree, intptr_t key,
+ intptr_t *oldkey, void **oldvalue) {
+
+ UcxAVLNode *n = tree->root;
+ int cmpresult;
+ while (n && (cmpresult = tree->cmpfunc(
+ ptrcast(key), ptrcast(n->key), tree->userdata))) {
+ n = cmpresult > 0 ? n->right : n->left;
+ }
+ if (n) {
+ if (oldkey) {
+ *oldkey = n->key;
+ }
+ if (oldvalue) {
+ *oldvalue = n->value;
+ }
+
+ UcxAVLNode *p = n->parent;
+ if (n->left && n->right) {
+ UcxAVLNode *s = n->right;
+ while (s->left) {
+ s = s->left;
+ }
+ ucx_avl_connect(tree, s->parent, s->right, s->key);
+ n->key = s->key; n->value = s->value;
+ p = s->parent;
+ alfree(tree->allocator, s);
+ } else {
+ if (p) {
+ ucx_avl_connect(tree, p, n->right ? n->right:n->left, n->key);
+ } else {
+ tree->root = n->right ? n->right : n->left;
+ if (tree->root) {
+ tree->root->parent = NULL;
+ }
+ }
+ alfree(tree->allocator, n);
+ }
+
+ if (p) {
+ ucx_avl_balance(tree, p);
+ }
+
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static size_t ucx_avl_countn(UcxAVLNode *node) {
+ if (node) {
+ return 1 + ucx_avl_countn(node->left) + ucx_avl_countn(node->right);
+ } else {
+ return 0;
+ }
+}
+
+size_t ucx_avl_count(UcxAVLTree *tree) {
+ return ucx_avl_countn(tree->root);
+}
+
+UcxAVLNode* ucx_avl_pred(UcxAVLNode* node) {
+ if (node->left) {
+ UcxAVLNode* n = node->left;
+ while (n->right) {
+ n = n->right;
+ }
+ return n;
+ } else {
+ UcxAVLNode* n = node;
+ while (n->parent) {
+ if (n->parent->right == n) {
+ return n->parent;
+ } else {
+ n = n->parent;
+ }
+ }
+ return NULL;
+ }
+}
+
+UcxAVLNode* ucx_avl_succ(UcxAVLNode* node) {
+ if (node->right) {
+ UcxAVLNode* n = node->right;
+ while (n->left) {
+ n = n->left;
+ }
+ return n;
+ } else {
+ UcxAVLNode* n = node;
+ while (n->parent) {
+ if (n->parent->left == n) {
+ return n->parent;
+ } else {
+ n = n->parent;
+ }
+ }
+ return NULL;
+ }
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucx/buffer.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags) {
+ UcxBuffer *buffer = (UcxBuffer*) malloc(sizeof(UcxBuffer));
+ if (buffer) {
+ buffer->flags = flags;
+ if (!space) {
+ buffer->space = (char*)malloc(capacity);
+ if (!buffer->space) {
+ free(buffer);
+ return NULL;
+ }
+ memset(buffer->space, 0, capacity);
+ buffer->flags |= UCX_BUFFER_AUTOFREE;
+ } else {
+ buffer->space = (char*)space;
+ }
+ buffer->capacity = capacity;
+ buffer->size = 0;
+
+ buffer->pos = 0;
+ }
+
+ return buffer;
+}
+
+void ucx_buffer_free(UcxBuffer *buffer) {
+ if ((buffer->flags & UCX_BUFFER_AUTOFREE) == UCX_BUFFER_AUTOFREE) {
+ free(buffer->space);
+ }
+ free(buffer);
+}
+
+UcxBuffer* ucx_buffer_extract(
+ UcxBuffer *src, size_t start, size_t length, int flags) {
+ if (src->size == 0 || length == 0 ||
+ ((size_t)-1) - start < length || start+length > src->capacity)
+ {
+ return NULL;
+ }
+
+ UcxBuffer *dst = (UcxBuffer*) malloc(sizeof(UcxBuffer));
+ if (dst) {
+ dst->space = (char*)malloc(length);
+ if (!dst->space) {
+ free(dst);
+ return NULL;
+ }
+ dst->capacity = length;
+ dst->size = length;
+ dst->flags = flags | UCX_BUFFER_AUTOFREE;
+ dst->pos = 0;
+ memcpy(dst->space, src->space+start, length);
+ }
+ return dst;
+}
+
+int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence) {
+ size_t npos;
+ switch (whence) {
+ case SEEK_CUR:
+ npos = buffer->pos;
+ break;
+ case SEEK_END:
+ npos = buffer->size;
+ break;
+ case SEEK_SET:
+ npos = 0;
+ break;
+ default:
+ return -1;
+ }
+
+ size_t opos = npos;
+ npos += offset;
+
+ if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) {
+ return -1;
+ }
+
+ if (npos >= buffer->size) {
+ return -1;
+ } else {
+ buffer->pos = npos;
+ return 0;
+ }
+
+}
+
+int ucx_buffer_eof(UcxBuffer *buffer) {
+ return buffer->pos >= buffer->size;
+}
+
+int ucx_buffer_extend(UcxBuffer *buffer, size_t len) {
+ size_t newcap = buffer->capacity;
+
+ if (buffer->capacity + len < buffer->capacity) {
+ return -1;
+ }
+
+ while (buffer->capacity + len > newcap) {
+ newcap <<= 1;
+ if (newcap < buffer->capacity) {
+ return -1;
+ }
+ }
+
+ char *newspace = (char*)realloc(buffer->space, newcap);
+ if (newspace) {
+ memset(newspace+buffer->size, 0, newcap-buffer->size);
+ buffer->space = newspace;
+ buffer->capacity = newcap;
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems,
+ UcxBuffer *buffer) {
+ size_t len;
+ if(ucx_szmul(size, nitems, &len)) {
+ return 0;
+ }
+ size_t required = buffer->pos + len;
+ if (buffer->pos > required) {
+ return 0;
+ }
+
+ if (required > buffer->capacity) {
+ if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
+ if (ucx_buffer_extend(buffer, required - buffer->capacity)) {
+ return 0;
+ }
+ } else {
+ len = buffer->capacity - buffer->pos;
+ if (size > 1) {
+ len -= len%size;
+ }
+ }
+ }
+
+ if (len == 0) {
+ return len;
+ }
+
+ memcpy(buffer->space + buffer->pos, ptr, len);
+ buffer->pos += len;
+ if(buffer->pos > buffer->size) {
+ buffer->size = buffer->pos;
+ }
+
+ return len / size;
+}
+
+size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems,
+ UcxBuffer *buffer) {
+ size_t len;
+ if(ucx_szmul(size, nitems, &len)) {
+ return 0;
+ }
+ if (buffer->pos + len > buffer->size) {
+ len = buffer->size - buffer->pos;
+ if (size > 1) len -= len%size;
+ }
+
+ if (len <= 0) {
+ return len;
+ }
+
+ memcpy(ptr, buffer->space + buffer->pos, len);
+ buffer->pos += len;
+
+ return len / size;
+}
+
+int ucx_buffer_putc(UcxBuffer *buffer, int c) {
+ if(buffer->pos >= buffer->capacity) {
+ if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
+ if(ucx_buffer_extend(buffer, 1)) {
+ return EOF;
+ }
+ } else {
+ return EOF;
+ }
+ }
+
+ c &= 0xFF;
+ buffer->space[buffer->pos] = (char) c;
+ buffer->pos++;
+ if(buffer->pos > buffer->size) {
+ buffer->size = buffer->pos;
+ }
+ return c;
+}
+
+int ucx_buffer_getc(UcxBuffer *buffer) {
+ if (ucx_buffer_eof(buffer)) {
+ return EOF;
+ } else {
+ int c = ((unsigned char*)buffer->space)[buffer->pos];
+ buffer->pos++;
+ return c;
+ }
+}
+
+size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str) {
+ return ucx_buffer_write((const void*)str, 1, strlen(str), buffer);
+}
+
+int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift) {
+ if (shift >= buffer->size) {
+ buffer->pos = buffer->size = 0;
+ } else {
+ memmove(buffer->space, buffer->space + shift, buffer->size - shift);
+ buffer->size -= shift;
+
+ if (buffer->pos >= shift) {
+ buffer->pos -= shift;
+ } else {
+ buffer->pos = 0;
+ }
+ }
+ return 0;
+}
+
+int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift) {
+ size_t req_capacity = buffer->size + shift;
+ size_t movebytes;
+
+ // auto extend buffer, if required and enabled
+ if (buffer->capacity < req_capacity) {
+ if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
+ if (ucx_buffer_extend(buffer, req_capacity - buffer->capacity)) {
+ return 1;
+ }
+ movebytes = buffer->size;
+ } else {
+ movebytes = buffer->capacity - shift;
+ }
+ } else {
+ movebytes = buffer->size;
+ }
+
+ memmove(buffer->space + shift, buffer->space, movebytes);
+ buffer->size = shift+movebytes;
+
+ buffer->pos += shift;
+ if (buffer->pos > buffer->size) {
+ buffer->pos = buffer->size;
+ }
+
+ return 0;
+}
+
+int ucx_buffer_shift(UcxBuffer* buffer, off_t shift) {
+ if (shift < 0) {
+ return ucx_buffer_shift_left(buffer, (size_t) (-shift));
+ } else if (shift > 0) {
+ return ucx_buffer_shift_right(buffer, (size_t) shift);
+ } else {
+ return 0;
+ }
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucx/list.h"
+
+UcxList *ucx_list_clone(const UcxList *l, copy_func fnc, void *data) {
+ return ucx_list_clone_a(ucx_default_allocator(), l, fnc, data);
+}
+
+UcxList *ucx_list_clone_a(UcxAllocator *alloc, const UcxList *l,
+ copy_func fnc, void *data) {
+ UcxList *ret = NULL;
+ while (l) {
+ if (fnc) {
+ ret = ucx_list_append_a(alloc, ret, fnc(l->data, data));
+ } else {
+ ret = ucx_list_append_a(alloc, ret, l->data);
+ }
+ l = l->next;
+ }
+ return ret;
+}
+
+int ucx_list_equals(const UcxList *l1, const UcxList *l2,
+ cmp_func fnc, void* data) {
+ if (l1 == l2) return 1;
+
+ while (l1 != NULL && l2 != NULL) {
+ if (fnc == NULL) {
+ if (l1->data != l2->data) return 0;
+ } else {
+ if (fnc(l1->data, l2->data, data) != 0) return 0;
+ }
+ l1 = l1->next;
+ l2 = l2->next;
+ }
+
+ return (l1 == NULL && l2 == NULL);
+}
+
+void ucx_list_free(UcxList *l) {
+ ucx_list_free_a(ucx_default_allocator(), l);
+}
+
+void ucx_list_free_a(UcxAllocator *alloc, UcxList *l) {
+ UcxList *e = l, *f;
+ while (e != NULL) {
+ f = e;
+ e = e->next;
+ alfree(alloc, f);
+ }
+}
+
+void ucx_list_free_content(UcxList* list, ucx_destructor destr) {
+ if (!destr) destr = free;
+ while (list != NULL) {
+ destr(list->data);
+ list = list->next;
+ }
+}
+
+UcxList *ucx_list_append(UcxList *l, void *data) {
+ return ucx_list_append_a(ucx_default_allocator(), l, data);
+}
+
+UcxList *ucx_list_append_a(UcxAllocator *alloc, UcxList *l, void *data) {
+ UcxList *nl = (UcxList*) almalloc(alloc, sizeof(UcxList));
+ if (!nl) {
+ return NULL;
+ }
+
+ nl->data = data;
+ nl->next = NULL;
+ if (l) {
+ UcxList *t = ucx_list_last(l);
+ t->next = nl;
+ nl->prev = t;
+ return l;
+ } else {
+ nl->prev = NULL;
+ return nl;
+ }
+}
+
+UcxList *ucx_list_prepend(UcxList *l, void *data) {
+ return ucx_list_prepend_a(ucx_default_allocator(), l, data);
+}
+
+UcxList *ucx_list_prepend_a(UcxAllocator *alloc, UcxList *l, void *data) {
+ UcxList *nl = ucx_list_append_a(alloc, NULL, data);
+ if (!nl) {
+ return NULL;
+ }
+ l = ucx_list_first(l);
+
+ if (l) {
+ nl->next = l;
+ l->prev = nl;
+ }
+ return nl;
+}
+
+UcxList *ucx_list_concat(UcxList *l1, UcxList *l2) {
+ if (l1) {
+ UcxList *last = ucx_list_last(l1);
+ last->next = l2;
+ if (l2) {
+ l2->prev = last;
+ }
+ return l1;
+ } else {
+ return l2;
+ }
+}
+
+UcxList *ucx_list_last(const UcxList *l) {
+ if (l == NULL) return NULL;
+
+ const UcxList *e = l;
+ while (e->next != NULL) {
+ e = e->next;
+ }
+ return (UcxList*)e;
+}
+
+ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem) {
+ ssize_t index = 0;
+ while (list) {
+ if (list == elem) {
+ return index;
+ }
+ list = list->next;
+ index++;
+ }
+ return -1;
+}
+
+UcxList *ucx_list_get(const UcxList *l, size_t index) {
+ if (l == NULL) return NULL;
+
+ const UcxList *e = l;
+ while (e->next && index > 0) {
+ e = e->next;
+ index--;
+ }
+
+ return (UcxList*)(index == 0 ? e : NULL);
+}
+
+ssize_t ucx_list_find(const UcxList *l, void *elem,
+ cmp_func fnc, void *cmpdata) {
+ ssize_t index = 0;
+ UCX_FOREACH(e, l) {
+ if (fnc) {
+ if (fnc(elem, e->data, cmpdata) == 0) {
+ return index;
+ }
+ } else {
+ if (elem == e->data) {
+ return index;
+ }
+ }
+ index++;
+ }
+ return -1;
+}
+
+int ucx_list_contains(const UcxList *l, void *elem,
+ cmp_func fnc, void *cmpdata) {
+ return ucx_list_find(l, elem, fnc, cmpdata) > -1;
+}
+
+size_t ucx_list_size(const UcxList *l) {
+ if (l == NULL) return 0;
+
+ const UcxList *e = l;
+ size_t s = 1;
+ while (e->next != NULL) {
+ e = e->next;
+ s++;
+ }
+
+ return s;
+}
+
+static UcxList *ucx_list_sort_merge(size_t length,
+ UcxList* ls, UcxList* le, UcxList* re,
+ cmp_func fnc, void* data) {
+
+ UcxList** sorted = (UcxList**) malloc(sizeof(UcxList*)*length);
+ UcxList *rc, *lc;
+
+ lc = ls; rc = le;
+ size_t n = 0;
+ while (lc && lc != le && rc != re) {
+ if (fnc(lc->data, rc->data, data) <= 0) {
+ sorted[n] = lc;
+ lc = lc->next;
+ } else {
+ sorted[n] = rc;
+ rc = rc->next;
+ }
+ n++;
+ }
+ while (lc && lc != le) {
+ sorted[n] = lc;
+ lc = lc->next;
+ n++;
+ }
+ while (rc && rc != re) {
+ sorted[n] = rc;
+ rc = rc->next;
+ n++;
+ }
+
+ // Update pointer
+ sorted[0]->prev = NULL;
+ for (int i = 0 ; i < length-1 ; i++) {
+ sorted[i]->next = sorted[i+1];
+ sorted[i+1]->prev = sorted[i];
+ }
+ sorted[length-1]->next = NULL;
+
+ UcxList *ret = sorted[0];
+ free(sorted);
+ return ret;
+}
+
+UcxList *ucx_list_sort(UcxList *l, cmp_func fnc, void *data) {
+ if (l == NULL) {
+ return NULL;
+ }
+
+ UcxList *lc;
+ size_t ln = 1;
+
+ UcxList *ls = l, *le, *re;
+
+ // check how many elements are already sorted
+ lc = ls;
+ while (lc->next != NULL && fnc(lc->next->data, lc->data, data) > 0) {
+ lc = lc->next;
+ ln++;
+ }
+ le = lc->next;
+
+ if (le == NULL) {
+ return l; // this list is already sorted :)
+ } else {
+ UcxList *rc;
+ size_t rn = 1;
+ rc = le;
+ // skip already sorted elements
+ while (rc->next != NULL && fnc(rc->next->data, rc->data, data) > 0) {
+ rc = rc->next;
+ rn++;
+ }
+ re = rc->next;
+
+ // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them
+ UcxList *sorted = ucx_list_sort_merge(ln+rn,
+ ls, le, re,
+ fnc, data);
+
+ // Something left? Sort it!
+ size_t remainder_length = ucx_list_size(re);
+ if (remainder_length > 0) {
+ UcxList *remainder = ucx_list_sort(re, fnc, data);
+
+ // merge sorted list with (also sorted) remainder
+ l = ucx_list_sort_merge(ln+rn+remainder_length,
+ sorted, remainder, NULL, fnc, data);
+ } else {
+ // no remainder - we've got our sorted list
+ l = sorted;
+ }
+
+ return l;
+ }
+}
+
+UcxList *ucx_list_first(const UcxList *l) {
+ if (!l) {
+ return NULL;
+ }
+
+ const UcxList *e = l;
+ while (e->prev) {
+ e = e->prev;
+ }
+ return (UcxList *)e;
+}
+
+UcxList *ucx_list_remove(UcxList *l, UcxList *e) {
+ return ucx_list_remove_a(ucx_default_allocator(), l, e);
+}
+
+UcxList *ucx_list_remove_a(UcxAllocator *alloc, UcxList *l, UcxList *e) {
+ if (l == e) {
+ l = e->next;
+ }
+
+ if (e->next) {
+ e->next->prev = e->prev;
+ }
+
+ if (e->prev) {
+ e->prev->next = e->next;
+ }
+
+ alfree(alloc, e);
+ return l;
+}
+
+
+static UcxList* ucx_list_setoperation_a(UcxAllocator *allocator,
+ UcxList const *left, UcxList const *right,
+ cmp_func cmpfnc, void* cmpdata,
+ copy_func cpfnc, void* cpdata,
+ int op) {
+
+ UcxList *res = NULL;
+ UcxList *cur = NULL;
+ const UcxList *src = left;
+
+ do {
+ UCX_FOREACH(node, src) {
+ void* elem = node->data;
+ if (
+ (op == 0 && !ucx_list_contains(res, elem, cmpfnc, cmpdata)) ||
+ (op == 1 && ucx_list_contains(right, elem, cmpfnc, cmpdata)) ||
+ (op == 2 && !ucx_list_contains(right, elem, cmpfnc, cmpdata))) {
+ UcxList *nl = almalloc(allocator, sizeof(UcxList));
+ nl->prev = cur;
+ nl->next = NULL;
+ if (cpfnc) {
+ nl->data = cpfnc(elem, cpdata);
+ } else {
+ nl->data = elem;
+ }
+ if (cur != NULL)
+ cur->next = nl;
+ cur = nl;
+ if (res == NULL)
+ res = cur;
+ }
+ }
+ if (op == 0 && src == left)
+ src = right;
+ else
+ src = NULL;
+ } while (src != NULL);
+
+ return res;
+}
+
+UcxList* ucx_list_union(UcxList const *left, UcxList const *right,
+ cmp_func cmpfnc, void* cmpdata,
+ copy_func cpfnc, void* cpdata) {
+ return ucx_list_union_a(ucx_default_allocator(),
+ left, right, cmpfnc, cmpdata, cpfnc, cpdata);
+}
+
+UcxList* ucx_list_union_a(UcxAllocator *allocator,
+ UcxList const *left, UcxList const *right,
+ cmp_func cmpfnc, void* cmpdata,
+ copy_func cpfnc, void* cpdata) {
+
+ return ucx_list_setoperation_a(allocator, left, right,
+ cmpfnc, cmpdata, cpfnc, cpdata, 0);
+}
+
+UcxList* ucx_list_intersection(UcxList const *left, UcxList const *right,
+ cmp_func cmpfnc, void* cmpdata,
+ copy_func cpfnc, void* cpdata) {
+ return ucx_list_intersection_a(ucx_default_allocator(), left, right,
+ cmpfnc, cmpdata, cpfnc, cpdata);
+}
+
+UcxList* ucx_list_intersection_a(UcxAllocator *allocator,
+ UcxList const *left, UcxList const *right,
+ cmp_func cmpfnc, void* cmpdata,
+ copy_func cpfnc, void* cpdata) {
+
+ return ucx_list_setoperation_a(allocator, left, right,
+ cmpfnc, cmpdata, cpfnc, cpdata, 1);
+}
+
+UcxList* ucx_list_difference(UcxList const *left, UcxList const *right,
+ cmp_func cmpfnc, void* cmpdata,
+ copy_func cpfnc, void* cpdata) {
+ return ucx_list_difference_a(ucx_default_allocator(), left, right,
+ cmpfnc, cmpdata, cpfnc, cpdata);
+}
+
+UcxList* ucx_list_difference_a(UcxAllocator *allocator,
+ UcxList const *left, UcxList const *right,
+ cmp_func cmpfnc, void* cmpdata,
+ copy_func cpfnc, void* cpdata) {
+
+ return ucx_list_setoperation_a(allocator, left, right,
+ cmpfnc, cmpdata, cpfnc, cpdata, 2);
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucx/logging.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+
+UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask) {
+ UcxLogger *logger = (UcxLogger*) malloc(sizeof(UcxLogger));
+ if (logger != NULL) {
+ logger->stream = stream;
+ logger->writer = (write_func)fwrite;
+ logger->dateformat = (char*) "%F %T %z ";
+ logger->level = level;
+ logger->mask = mask;
+ logger->levels = ucx_map_new(8);
+
+ unsigned int l;
+ l = UCX_LOGGER_ERROR;
+ ucx_map_int_put(logger->levels, l, (void*) "[ERROR]");
+ l = UCX_LOGGER_WARN;
+ ucx_map_int_put(logger->levels, l, (void*) "[WARNING]");
+ l = UCX_LOGGER_INFO;
+ ucx_map_int_put(logger->levels, l, (void*) "[INFO]");
+ l = UCX_LOGGER_DEBUG;
+ ucx_map_int_put(logger->levels, l, (void*) "[DEBUG]");
+ l = UCX_LOGGER_TRACE;
+ ucx_map_int_put(logger->levels, l, (void*) "[TRACE]");
+ }
+
+ return logger;
+}
+
+void ucx_logger_free(UcxLogger *logger) {
+ ucx_map_free(logger->levels);
+ free(logger);
+}
+
+// estimated max. message length (documented)
+#define UCX_LOGGER_MSGMAX 4096
+
+void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file,
+ const unsigned int line, const char *format, ...) {
+ if (level <= logger->level) {
+ char msg[UCX_LOGGER_MSGMAX];
+ const char *text;
+ size_t k = 0;
+ size_t n;
+
+ if ((logger->mask & UCX_LOGGER_LEVEL) > 0) {
+ text = (const char*) ucx_map_int_get(logger->levels, level);
+ if (!text) {
+ text = "[UNKNOWN]";
+ }
+ n = strlen(text);
+ n = n > 256 ? 256 : n;
+ memcpy(msg+k, text, n);
+ k += n;
+ msg[k++] = ' ';
+ }
+ if ((logger->mask & UCX_LOGGER_TIMESTAMP) > 0) {
+ time_t now = time(NULL);
+ k += strftime(msg+k, 128, logger->dateformat, localtime(&now));
+ }
+ if ((logger->mask & UCX_LOGGER_SOURCE) > 0) {
+ char *fpart = strrchr(file, '/');
+ if (fpart) file = fpart+1;
+ fpart = strrchr(file, '\\');
+ if (fpart) file = fpart+1;
+ n = strlen(file);
+ memcpy(msg+k, file, n);
+ k += n;
+ k += sprintf(msg+k, ":%u ", line);
+ }
+
+ if (k > 0) {
+ msg[k++] = '-'; msg[k++] = ' ';
+ }
+
+ va_list args;
+ va_start (args, format);
+ k += vsnprintf(msg+k, UCX_LOGGER_MSGMAX-k-1, format, args);
+ va_end (args);
+
+ msg[k++] = '\n';
+
+ logger->writer(msg, 1, k, logger->stream);
+ }
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucx/map.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+UcxMap *ucx_map_new(size_t size) {
+ return ucx_map_new_a(NULL, size);
+}
+
+UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size) {
+ if(size == 0) {
+ size = 16;
+ }
+
+ if(!allocator) {
+ allocator = ucx_default_allocator();
+ }
+
+ UcxMap *map = (UcxMap*)almalloc(allocator, sizeof(UcxMap));
+ if (!map) {
+ return NULL;
+ }
+
+ map->allocator = allocator;
+ map->map = (UcxMapElement**)alcalloc(
+ allocator, size, sizeof(UcxMapElement*));
+ if(map->map == NULL) {
+ alfree(allocator, map);
+ return NULL;
+ }
+ map->size = size;
+ map->count = 0;
+
+ return map;
+}
+
+static void ucx_map_free_elmlist_contents(UcxMap *map) {
+ for (size_t n = 0 ; n < map->size ; n++) {
+ UcxMapElement *elem = map->map[n];
+ if (elem != NULL) {
+ do {
+ UcxMapElement *next = elem->next;
+ alfree(map->allocator, elem->key.data);
+ alfree(map->allocator, elem);
+ elem = next;
+ } while (elem != NULL);
+ }
+ }
+}
+
+void ucx_map_free(UcxMap *map) {
+ ucx_map_free_elmlist_contents(map);
+ alfree(map->allocator, map->map);
+ alfree(map->allocator, map);
+}
+
+void ucx_map_free_content(UcxMap *map, ucx_destructor destr) {
+ UcxMapIterator iter = ucx_map_iterator(map);
+ void *val;
+ UCX_MAP_FOREACH(key, val, iter) {
+ if (destr) {
+ destr(val);
+ } else {
+ alfree(map->allocator, val);
+ }
+ }
+}
+
+void ucx_map_clear(UcxMap *map) {
+ if (map->count == 0) {
+ return; // nothing to do
+ }
+ ucx_map_free_elmlist_contents(map);
+ memset(map->map, 0, map->size*sizeof(UcxMapElement*));
+ map->count = 0;
+}
+
+int ucx_map_copy(UcxMap const *from, UcxMap *to, copy_func fnc, void *data) {
+ UcxMapIterator i = ucx_map_iterator(from);
+ void *value;
+ UCX_MAP_FOREACH(key, value, i) {
+ if (ucx_map_put(to, key, fnc ? fnc(value, data) : value)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+UcxMap *ucx_map_clone(UcxMap const *map, copy_func fnc, void *data) {
+ return ucx_map_clone_a(ucx_default_allocator(), map, fnc, data);
+}
+
+UcxMap *ucx_map_clone_a(UcxAllocator *allocator,
+ UcxMap const *map, copy_func fnc, void *data) {
+ size_t bs = (map->count * 5) >> 1;
+ UcxMap *newmap = ucx_map_new_a(allocator, bs > map->size ? bs : map->size);
+ if (!newmap) {
+ return NULL;
+ }
+ ucx_map_copy(map, newmap, fnc, data);
+ return newmap;
+}
+
+int ucx_map_rehash(UcxMap *map) {
+ size_t load = (map->size * 3) >> 2;
+ if (map->count > load) {
+ UcxMap oldmap;
+ oldmap.map = map->map;
+ oldmap.size = map->size;
+ oldmap.count = map->count;
+ oldmap.allocator = map->allocator;
+
+ map->size = (map->count * 5) >> 1;
+ map->map = (UcxMapElement**)alcalloc(
+ map->allocator, map->size, sizeof(UcxMapElement*));
+ if (!map->map) {
+ *map = oldmap;
+ return 1;
+ }
+ map->count = 0;
+ ucx_map_copy(&oldmap, map, NULL, NULL);
+
+ /* free the UcxMapElement list of oldmap */
+ ucx_map_free_elmlist_contents(&oldmap);
+ alfree(map->allocator, oldmap.map);
+ }
+ return 0;
+}
+
+int ucx_map_put(UcxMap *map, UcxKey key, void *data) {
+ UcxAllocator *allocator = map->allocator;
+
+ if (key.hash == 0) {
+ key.hash = ucx_hash((const char*)key.data, key.len);
+ }
+
+ struct UcxMapKey mapkey;
+ mapkey.hash = key.hash;
+
+ size_t slot = mapkey.hash%map->size;
+ UcxMapElement *elm = map->map[slot];
+ UcxMapElement *prev = NULL;
+
+ while (elm && elm->key.hash < mapkey.hash) {
+ prev = elm;
+ elm = elm->next;
+ }
+
+ if (!elm || elm->key.hash != mapkey.hash) {
+ UcxMapElement *e = (UcxMapElement*)almalloc(
+ allocator, sizeof(UcxMapElement));
+ if (!e) {
+ return -1;
+ }
+ e->key.data = NULL;
+ if (prev) {
+ prev->next = e;
+ } else {
+ map->map[slot] = e;
+ }
+ e->next = elm;
+ elm = e;
+ }
+
+ if (!elm->key.data) {
+ void *kd = almalloc(allocator, key.len);
+ if (!kd) {
+ return -1;
+ }
+ memcpy(kd, key.data, key.len);
+ mapkey.data = kd;
+ mapkey.len = key.len;
+ elm->key = mapkey;
+ map->count++;
+ }
+ elm->data = data;
+
+ return 0;
+}
+
+static void* ucx_map_get_and_remove(UcxMap *map, UcxKey key, int remove) {
+ if(key.hash == 0) {
+ key.hash = ucx_hash((const char*)key.data, key.len);
+ }
+
+ size_t slot = key.hash%map->size;
+ UcxMapElement *elm = map->map[slot];
+ UcxMapElement *pelm = NULL;
+ while (elm && elm->key.hash <= key.hash) {
+ if(elm->key.hash == key.hash) {
+ int n = (key.len > elm->key.len) ? elm->key.len : key.len;
+ if (memcmp(elm->key.data, key.data, n) == 0) {
+ void *data = elm->data;
+ if (remove) {
+ if (pelm) {
+ pelm->next = elm->next;
+ } else {
+ map->map[slot] = elm->next;
+ }
+ alfree(map->allocator, elm->key.data);
+ alfree(map->allocator, elm);
+ map->count--;
+ }
+
+ return data;
+ }
+ }
+ pelm = elm;
+ elm = pelm->next;
+ }
+
+ return NULL;
+}
+
+void *ucx_map_get(UcxMap const *map, UcxKey key) {
+ return ucx_map_get_and_remove((UcxMap *)map, key, 0);
+}
+
+void *ucx_map_remove(UcxMap *map, UcxKey key) {
+ return ucx_map_get_and_remove(map, key, 1);
+}
+
+UcxKey ucx_key(const void *data, size_t len) {
+ UcxKey key;
+ key.data = data;
+ key.len = len;
+ key.hash = ucx_hash((const char*)data, len);
+ return key;
+}
+
+
+int ucx_hash(const char *data, size_t len) {
+ /* murmur hash 2 */
+
+ int m = 0x5bd1e995;
+ int r = 24;
+
+ int h = 25 ^ len;
+
+ int i = 0;
+ while (len >= 4) {
+ int k = data[i + 0] & 0xFF;
+ k |= (data[i + 1] & 0xFF) << 8;
+ k |= (data[i + 2] & 0xFF) << 16;
+ k |= (data[i + 3] & 0xFF) << 24;
+
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+
+ h *= m;
+ h ^= k;
+
+ i += 4;
+ len -= 4;
+ }
+
+ switch (len) {
+ case 3: h ^= (data[i + 2] & 0xFF) << 16;
+ /* no break */
+ case 2: h ^= (data[i + 1] & 0xFF) << 8;
+ /* no break */
+ case 1: h ^= (data[i + 0] & 0xFF); h *= m;
+ /* no break */
+ }
+
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+
+ return h;
+}
+
+UcxMapIterator ucx_map_iterator(UcxMap const *map) {
+ UcxMapIterator i;
+ i.map = map;
+ i.cur = NULL;
+ i.index = 0;
+ return i;
+}
+
+int ucx_map_iter_next(UcxMapIterator *i, UcxKey *key, void **elm) {
+ UcxMapElement *e = i->cur;
+
+ if (e) {
+ e = e->next;
+ } else {
+ e = i->map->map[0];
+ }
+
+ while (i->index < i->map->size) {
+ if (e) {
+ if (e->data) {
+ i->cur = e;
+ *elm = e->data;
+ key->data = e->key.data;
+ key->hash = e->key.hash;
+ key->len = e->key.len;
+ return 1;
+ }
+
+ e = e->next;
+ } else {
+ i->index++;
+
+ if (i->index < i->map->size) {
+ e = i->map->map[i->index];
+ }
+ }
+ }
+
+ return 0;
+}
+
+UcxMap* ucx_map_union(const UcxMap *first, const UcxMap *second,
+ copy_func cpfnc, void* cpdata) {
+ return ucx_map_union_a(ucx_default_allocator(),
+ first, second, cpfnc, cpdata);
+}
+
+UcxMap* ucx_map_union_a(UcxAllocator *allocator,
+ const UcxMap *first, const UcxMap *second,
+ copy_func cpfnc, void* cpdata) {
+ UcxMap* result = ucx_map_clone_a(allocator, first, cpfnc, cpdata);
+ ucx_map_copy(second, result, cpfnc, cpdata);
+ return result;
+}
+
+UcxMap* ucx_map_intersection(const UcxMap *first, const UcxMap *second,
+ copy_func cpfnc, void* cpdata) {
+ return ucx_map_intersection_a(ucx_default_allocator(),
+ first, second, cpfnc, cpdata);
+}
+
+UcxMap* ucx_map_intersection_a(UcxAllocator *allocator,
+ const UcxMap *first, const UcxMap *second,
+ copy_func cpfnc, void* cpdata) {
+ UcxMap *result = ucx_map_new_a(allocator, first->size < second->size ?
+ first->size : second->size);
+
+ UcxMapIterator iter = ucx_map_iterator(first);
+ void* value;
+ UCX_MAP_FOREACH(key, value, iter) {
+ if (ucx_map_get(second, key)) {
+ ucx_map_put(result, key, cpfnc ? cpfnc(value, cpdata) : value);
+ }
+ }
+
+ return result;
+}
+
+UcxMap* ucx_map_difference(const UcxMap *first, const UcxMap *second,
+ copy_func cpfnc, void* cpdata) {
+ return ucx_map_difference_a(ucx_default_allocator(),
+ first, second, cpfnc, cpdata);
+}
+
+UcxMap* ucx_map_difference_a(UcxAllocator *allocator,
+ const UcxMap *first, const UcxMap *second,
+ copy_func cpfnc, void* cpdata) {
+
+ UcxMap *result = ucx_map_new_a(allocator, first->size - second->count);
+
+ UcxMapIterator iter = ucx_map_iterator(first);
+ void* value;
+ UCX_MAP_FOREACH(key, value, iter) {
+ if (!ucx_map_get(second, key)) {
+ ucx_map_put(result, key, cpfnc ? cpfnc(value, cpdata) : value);
+ }
+ }
+
+ ucx_map_rehash(result);
+ return result;
+}
\ No newline at end of file
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucx/mempool.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#ifdef __cplusplus
+#define __STDC_FORMAT_MACROS
+#endif
+#include <inttypes.h>
+
+/** Capsule for destructible memory chunks. */
+typedef struct {
+ /** The destructor for the memory chunk. */
+ ucx_destructor destructor;
+ /**
+ * First byte of the memory chunk.
+ * Note, that the address <code>&c</code> is also the address
+ * of the whole memory chunk.
+ */
+ char c;
+} ucx_memchunk;
+
+/** Capsule for data and its destructor. */
+typedef struct {
+ /** The destructor for the data. */
+ ucx_destructor destructor;
+ /** A pointer to the data. */
+ void *ptr;
+} ucx_regdestr;
+
+#ifdef __cplusplus
+extern "C"
+#endif
+void ucx_mempool_shared_destr(void* ptr) {
+ ucx_regdestr *rd = (ucx_regdestr*)ptr;
+ rd->destructor(rd->ptr);
+}
+
+UcxMempool *ucx_mempool_new(size_t n) {
+ size_t poolsz;
+ if(ucx_szmul(n, sizeof(void*), &poolsz)) {
+ return NULL;
+ }
+
+ UcxMempool *pool = (UcxMempool*)malloc(sizeof(UcxMempool));
+ if (!pool) {
+ return NULL;
+ }
+
+ pool->data = (void**) malloc(poolsz);
+ if (pool->data == NULL) {
+ free(pool);
+ return NULL;
+ }
+
+ pool->ndata = 0;
+ pool->size = n;
+
+ UcxAllocator *allocator = (UcxAllocator*)malloc(sizeof(UcxAllocator));
+ if(!allocator) {
+ free(pool->data);
+ free(pool);
+ return NULL;
+ }
+ allocator->malloc = (ucx_allocator_malloc)ucx_mempool_malloc;
+ allocator->calloc = (ucx_allocator_calloc)ucx_mempool_calloc;
+ allocator->realloc = (ucx_allocator_realloc)ucx_mempool_realloc;
+ allocator->free = (ucx_allocator_free)ucx_mempool_free;
+ allocator->pool = pool;
+ pool->allocator = allocator;
+
+ return pool;
+}
+
+int ucx_mempool_chcap(UcxMempool *pool, size_t newcap) {
+ if (newcap < pool->ndata) {
+ return 1;
+ }
+
+ size_t newcapsz;
+ if(ucx_szmul(newcap, sizeof(void*), &newcapsz)) {
+ return 1;
+ }
+
+ void **data = (void**) realloc(pool->data, newcapsz);
+ if (data) {
+ pool->data = data;
+ pool->size = newcap;
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+void *ucx_mempool_malloc(UcxMempool *pool, size_t n) {
+ if(((size_t)-1) - sizeof(ucx_destructor) < n) {
+ return NULL;
+ }
+
+ if (pool->ndata >= pool->size) {
+ size_t newcap = pool->size*2;
+ if (newcap < pool->size || ucx_mempool_chcap(pool, newcap)) {
+ return NULL;
+ }
+ }
+
+ void *p = malloc(sizeof(ucx_destructor) + n);
+ ucx_memchunk *mem = (ucx_memchunk*)p;
+ if (!mem) {
+ return NULL;
+ }
+
+ mem->destructor = NULL;
+ pool->data[pool->ndata] = mem;
+ pool->ndata++;
+
+ return &(mem->c);
+}
+
+void *ucx_mempool_calloc(UcxMempool *pool, size_t nelem, size_t elsize) {
+ size_t msz;
+ if(ucx_szmul(nelem, elsize, &msz)) {
+ return NULL;
+ }
+
+ void *ptr = ucx_mempool_malloc(pool, msz);
+ if (!ptr) {
+ return NULL;
+ }
+ memset(ptr, 0, nelem * elsize);
+ return ptr;
+}
+
+void *ucx_mempool_realloc(UcxMempool *pool, void *ptr, size_t n) {
+ if(((size_t)-1) - sizeof(ucx_destructor) < n) {
+ return NULL;
+ }
+
+ char *mem = ((char*)ptr) - sizeof(ucx_destructor);
+ char *newm = (char*) realloc(mem, n + sizeof(ucx_destructor));
+ if (!newm) {
+ return NULL;
+ }
+ if (mem != newm) {
+ for(size_t i=0 ; i < pool->ndata ; i++) {
+ if(pool->data[i] == mem) {
+ pool->data[i] = newm;
+ return newm + sizeof(ucx_destructor);
+ }
+ }
+ fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n",
+ (intptr_t)ptr, (intptr_t)pool);
+ abort();
+ } else {
+ return newm + sizeof(ucx_destructor);
+ }
+}
+
+void ucx_mempool_free(UcxMempool *pool, void *ptr) {
+ ucx_memchunk *chunk = (ucx_memchunk*)((char*)ptr-sizeof(ucx_destructor));
+ for(size_t i=0 ; i<pool->ndata ; i++) {
+ if(chunk == pool->data[i]) {
+ if(chunk->destructor != NULL) {
+ chunk->destructor(&(chunk->c));
+ }
+ free(chunk);
+ size_t last_index = pool->ndata - 1;
+ if(i != last_index) {
+ pool->data[i] = pool->data[last_index];
+ pool->data[last_index] = NULL;
+ }
+ pool->ndata--;
+ return;
+ }
+ }
+ fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n",
+ (intptr_t)ptr, (intptr_t)pool);
+ abort();
+}
+
+void ucx_mempool_destroy(UcxMempool *pool) {
+ ucx_memchunk *chunk;
+ for(size_t i=0 ; i<pool->ndata ; i++) {
+ chunk = (ucx_memchunk*) pool->data[i];
+ if(chunk) {
+ if(chunk->destructor) {
+ chunk->destructor(&(chunk->c));
+ }
+ free(chunk);
+ }
+ }
+ free(pool->data);
+ free(pool->allocator);
+ free(pool);
+}
+
+void ucx_mempool_set_destr(void *ptr, ucx_destructor func) {
+ *(ucx_destructor*)((char*)ptr-sizeof(ucx_destructor)) = func;
+}
+
+void ucx_mempool_reg_destr(UcxMempool *pool, void *ptr, ucx_destructor destr) {
+ ucx_regdestr *rd = (ucx_regdestr*)ucx_mempool_malloc(
+ pool,
+ sizeof(ucx_regdestr));
+ rd->destructor = destr;
+ rd->ptr = ptr;
+ ucx_mempool_set_destr(rd, ucx_mempool_shared_destr);
+}
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucx/properties.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+UcxProperties *ucx_properties_new() {
+ UcxProperties *parser = (UcxProperties*)malloc(
+ sizeof(UcxProperties));
+ if(!parser) {
+ return NULL;
+ }
+
+ parser->buffer = NULL;
+ parser->buflen = 0;
+ parser->pos = 0;
+ parser->tmp = NULL;
+ parser->tmplen = 0;
+ parser->tmpcap = 0;
+ parser->error = 0;
+ parser->delimiter = '=';
+ parser->comment1 = '#';
+ parser->comment2 = 0;
+ parser->comment3 = 0;
+
+ return parser;
+}
+
+void ucx_properties_free(UcxProperties *parser) {
+ if(parser->tmp) {
+ free(parser->tmp);
+ }
+ free(parser);
+}
+
+void ucx_properties_fill(UcxProperties *parser, char *buf, size_t len) {
+ parser->buffer = buf;
+ parser->buflen = len;
+ parser->pos = 0;
+}
+
+static void parser_tmp_append(UcxProperties *parser, char *buf, size_t len) {
+ if(parser->tmpcap - parser->tmplen < len) {
+ size_t newcap = parser->tmpcap + len + 64;
+ parser->tmp = (char*)realloc(parser->tmp, newcap);
+ parser->tmpcap = newcap;
+ }
+ memcpy(parser->tmp + parser->tmplen, buf, len);
+ parser->tmplen += len;
+}
+
+int ucx_properties_next(UcxProperties *parser, sstr_t *name, sstr_t *value) {
+ if(parser->tmplen > 0) {
+ char *buf = parser->buffer + parser->pos;
+ size_t len = parser->buflen - parser->pos;
+ sstr_t str = sstrn(buf, len);
+ sstr_t nl = sstrchr(str, '\n');
+ if(nl.ptr) {
+ size_t newlen = (size_t)(nl.ptr - buf) + 1;
+ parser_tmp_append(parser, buf, newlen);
+ // the tmp buffer contains exactly one line now
+
+ char *orig_buf = parser->buffer;
+ size_t orig_len = parser->buflen;
+
+ parser->buffer = parser->tmp;
+ parser->buflen = parser->tmplen;
+ parser->pos = 0;
+ parser->tmp = NULL;
+ parser->tmpcap = 0;
+ parser->tmplen = 0;
+ // run ucx_properties_next with the tmp buffer as main buffer
+ int ret = ucx_properties_next(parser, name, value);
+
+ // restore original buffer
+ parser->tmp = parser->buffer;
+ parser->buffer = orig_buf;
+ parser->buflen = orig_len;
+ parser->pos = newlen;
+
+ /*
+ * if ret == 0 the tmp buffer contained just space or a comment
+ * we parse again with the original buffer to get a name/value
+ * or a new tmp buffer
+ */
+ return ret ? ret : ucx_properties_next(parser, name, value);
+ } else {
+ parser_tmp_append(parser, buf, len);
+ return 0;
+ }
+ } else if(parser->tmp) {
+ free(parser->tmp);
+ parser->tmp = NULL;
+ }
+
+ char comment1 = parser->comment1;
+ char comment2 = parser->comment2;
+ char comment3 = parser->comment3;
+ char delimiter = parser->delimiter;
+
+ // get one line and parse it
+ while(parser->pos < parser->buflen) {
+ char *buf = parser->buffer + parser->pos;
+ size_t len = parser->buflen - parser->pos;
+
+ /*
+ * First we check if we have at least one line. We also get indices of
+ * delimiter and comment chars
+ */
+ size_t delimiter_index = 0;
+ size_t comment_index = 0;
+ int has_comment = 0;
+
+ size_t i = 0;
+ char c = 0;
+ for(;i<len;i++) {
+ c = buf[i];
+ if(c == comment1 || c == comment2 || c == comment3) {
+ if(comment_index == 0) {
+ comment_index = i;
+ has_comment = 1;
+ }
+ } else if(c == delimiter) {
+ if(delimiter_index == 0 && !has_comment) {
+ delimiter_index = i;
+ }
+ } else if(c == '\n') {
+ break;
+ }
+ }
+
+ if(c != '\n') {
+ // we don't have enough data for a line
+ // store remaining bytes in temporary buffer for next round
+ parser->tmpcap = len + 128;
+ parser->tmp = (char*)malloc(parser->tmpcap);
+ parser->tmplen = len;
+ memcpy(parser->tmp, buf, len);
+ return 0;
+ }
+
+ sstr_t line = has_comment ? sstrn(buf, comment_index) : sstrn(buf, i);
+ // check line
+ if(delimiter_index == 0) {
+ line = sstrtrim(line);
+ if(line.length != 0) {
+ parser->error = 1;
+ }
+ } else {
+ sstr_t n = sstrn(buf, delimiter_index);
+ sstr_t v = sstrn(
+ buf + delimiter_index + 1,
+ line.length - delimiter_index - 1);
+ n = sstrtrim(n);
+ v = sstrtrim(v);
+ if(n.length != 0 || v.length != 0) {
+ *name = n;
+ *value = v;
+ parser->pos += i + 1;
+ return 1;
+ } else {
+ parser->error = 1;
+ }
+ }
+
+ parser->pos += i + 1;
+ }
+
+ return 0;
+}
+
+int ucx_properties2map(UcxProperties *parser, UcxMap *map) {
+ sstr_t name;
+ sstr_t value;
+ while(ucx_properties_next(parser, &name, &value)) {
+ value = sstrdup_a(map->allocator, value);
+ if(!value.ptr) {
+ return 1;
+ }
+ if(ucx_map_sstr_put(map, name, value.ptr)) {
+ alfree(map->allocator, value.ptr);
+ return 1;
+ }
+ }
+ if (parser->error) {
+ return parser->error;
+ } else {
+ return 0;
+ }
+}
+
+// buffer size is documented - change doc, when you change bufsize!
+#define UCX_PROPLOAD_BUFSIZE 1024
+int ucx_properties_load(UcxMap *map, FILE *file) {
+ UcxProperties *parser = ucx_properties_new();
+ if(!(parser && map && file)) {
+ return 1;
+ }
+
+ int error = 0;
+ size_t r;
+ char buf[UCX_PROPLOAD_BUFSIZE];
+ while((r = fread(buf, 1, UCX_PROPLOAD_BUFSIZE, file)) != 0) {
+ ucx_properties_fill(parser, buf, r);
+ error = ucx_properties2map(parser, map);
+ if (error) {
+ break;
+ }
+ }
+ ucx_properties_free(parser);
+ return error;
+}
+
+int ucx_properties_store(UcxMap *map, FILE *file) {
+ UcxMapIterator iter = ucx_map_iterator(map);
+ void *v;
+ sstr_t value;
+ size_t written;
+
+ UCX_MAP_FOREACH(k, v, iter) {
+ value = sstr((char*)v);
+
+ written = 0;
+ written += fwrite(k.data, 1, k.len, file);
+ written += fwrite(" = ", 1, 3, file);
+ written += fwrite(value.ptr, 1, value.length, file);
+ written += fwrite("\n", 1, 1, file);
+
+ if (written != k.len + value.length + 4) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucx/stack.h"
+
+#include <string.h>
+
+static size_t ucx_stack_align(size_t n) {
+ int align = n % sizeof(void*);
+ if (align) {
+ n += sizeof(void*) - align;
+ }
+ return n;
+}
+
+void ucx_stack_init(UcxStack *stack, char* space, size_t size) {
+ stack->size = size - size % sizeof(void*);
+ stack->space = space;
+ stack->top = NULL;
+
+ stack->allocator.pool = stack;
+ stack->allocator.malloc = (ucx_allocator_malloc) ucx_stack_malloc;
+ stack->allocator.calloc = (ucx_allocator_calloc) ucx_stack_calloc;
+ stack->allocator.realloc = (ucx_allocator_realloc) ucx_stack_realloc;
+ stack->allocator.free = (ucx_allocator_free) ucx_stack_free;
+}
+
+void *ucx_stack_malloc(UcxStack *stack, size_t n) {
+
+ if (ucx_stack_avail(stack) < ucx_stack_align(n)) {
+ return NULL;
+ } else {
+ char *prev = stack->top;
+ if (stack->top) {
+ stack->top += ucx_stack_align(ucx_stack_topsize(stack));
+ } else {
+ stack->top = stack->space;
+ }
+
+ ((struct ucx_stack_metadata*)stack->top)->prev = prev;
+ ((struct ucx_stack_metadata*)stack->top)->size = n;
+ stack->top += sizeof(struct ucx_stack_metadata);
+
+ return stack->top;
+ }
+}
+
+void *ucx_stack_calloc(UcxStack *stack, size_t nelem, size_t elsize) {
+ void *mem = ucx_stack_malloc(stack, nelem*elsize);
+ memset(mem, 0, nelem*elsize);
+ return mem;
+}
+
+void *ucx_stack_realloc(UcxStack *stack, void *ptr, size_t n) {
+ if (ptr == stack->top) {
+ if (stack->size - (stack->top - stack->space) < ucx_stack_align(n)) {
+ return NULL;
+ } else {
+ ((struct ucx_stack_metadata*)stack->top - 1)->size = n;
+ return ptr;
+ }
+ } else {
+ if (ucx_stack_align(((struct ucx_stack_metadata*)ptr - 1)->size) <
+ ucx_stack_align(n)) {
+ void *nptr = ucx_stack_malloc(stack, n);
+ if (nptr) {
+ memcpy(nptr, ptr, n);
+ ucx_stack_free(stack, ptr);
+
+ return nptr;
+ } else {
+ return NULL;
+ }
+ } else {
+ ((struct ucx_stack_metadata*)ptr - 1)->size = n;
+ return ptr;
+ }
+ }
+}
+
+void ucx_stack_free(UcxStack *stack, void *ptr) {
+ if (ptr == stack->top) {
+ stack->top = ((struct ucx_stack_metadata*) stack->top - 1)->prev;
+ } else {
+ struct ucx_stack_metadata *next = (struct ucx_stack_metadata*)(
+ (char*)ptr +
+ ucx_stack_align(((struct ucx_stack_metadata*) ptr - 1)->size)
+ );
+ next->prev = ((struct ucx_stack_metadata*) ptr - 1)->prev;
+ }
+}
+
+void ucx_stack_popn(UcxStack *stack, void *dest, size_t n) {
+ if (ucx_stack_empty(stack)) {
+ return;
+ }
+
+ if (dest) {
+ size_t len = ucx_stack_topsize(stack);
+ if (len > n) {
+ len = n;
+ }
+
+ memcpy(dest, stack->top, len);
+ }
+
+ ucx_stack_free(stack, stack->top);
+}
+
+size_t ucx_stack_avail(UcxStack *stack) {
+ size_t avail = ((stack->top ? (stack->size
+ - (stack->top - stack->space)
+ - ucx_stack_align(ucx_stack_topsize(stack)))
+ : stack->size));
+
+ if (avail > sizeof(struct ucx_stack_metadata)) {
+ return avail - sizeof(struct ucx_stack_metadata);
+ } else {
+ return 0;
+ }
+}
+
+void *ucx_stack_push(UcxStack *stack, size_t n, const void *data) {
+ void *space = ucx_stack_malloc(stack, n);
+ if (space) {
+ memcpy(space, data, n);
+ }
+ return space;
+}
+
+void *ucx_stack_pusharr(UcxStack *stack,
+ size_t nelem, size_t elsize, const void *data) {
+
+ // skip the memset by using malloc
+ void *space = ucx_stack_malloc(stack, nelem*elsize);
+ if (space) {
+ memcpy(space, data, nelem*elsize);
+ }
+ return space;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucx/string.h"
+
+#include "ucx/allocator.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <ctype.h>
+
+#ifndef _WIN32
+#include <strings.h> /* for strncasecmp() */
+#endif /* _WIN32 */
+
+sstr_t sstr(char *cstring) {
+ sstr_t string;
+ string.ptr = cstring;
+ string.length = strlen(cstring);
+ return string;
+}
+
+sstr_t sstrn(char *cstring, size_t length) {
+ sstr_t string;
+ string.ptr = cstring;
+ string.length = length;
+ return string;
+}
+
+scstr_t scstr(const char *cstring) {
+ scstr_t string;
+ string.ptr = cstring;
+ string.length = strlen(cstring);
+ return string;
+}
+
+scstr_t scstrn(const char *cstring, size_t length) {
+ scstr_t string;
+ string.ptr = cstring;
+ string.length = length;
+ return string;
+}
+
+
+size_t scstrnlen(size_t n, ...) {
+ if (n == 0) return 0;
+
+ va_list ap;
+ va_start(ap, n);
+
+ size_t size = 0;
+
+ for (size_t i = 0 ; i < n ; i++) {
+ scstr_t str = va_arg(ap, scstr_t);
+ if(SIZE_MAX - str.length < size) {
+ size = SIZE_MAX;
+ break;
+ }
+ size += str.length;
+ }
+ va_end(ap);
+
+ return size;
+}
+
+static sstr_t sstrvcat_a(
+ UcxAllocator *a,
+ size_t count,
+ scstr_t s1,
+ va_list ap) {
+ sstr_t str;
+ str.ptr = NULL;
+ str.length = 0;
+ if(count < 2) {
+ return str;
+ }
+
+ scstr_t s2 = va_arg (ap, scstr_t);
+
+ if(((size_t)-1) - s1.length < s2.length) {
+ return str;
+ }
+
+ scstr_t *strings = (scstr_t*) calloc(count, sizeof(scstr_t));
+ if(!strings) {
+ return str;
+ }
+
+ // get all args and overall length
+ strings[0] = s1;
+ strings[1] = s2;
+ size_t slen = s1.length + s2.length;
+ int error = 0;
+ for (size_t i=2;i<count;i++) {
+ scstr_t s = va_arg (ap, scstr_t);
+ strings[i] = s;
+ if(((size_t)-1) - s.length < slen) {
+ error = 1;
+ break;
+ }
+ slen += s.length;
+ }
+ if(error) {
+ free(strings);
+ return str;
+ }
+
+ // create new string
+ str.ptr = (char*) almalloc(a, slen + 1);
+ str.length = slen;
+ if(!str.ptr) {
+ free(strings);
+ str.length = 0;
+ return str;
+ }
+
+ // concatenate strings
+ size_t pos = 0;
+ for (size_t i=0;i<count;i++) {
+ scstr_t s = strings[i];
+ memcpy(str.ptr + pos, s.ptr, s.length);
+ pos += s.length;
+ }
+
+ str.ptr[str.length] = '\0';
+
+ free(strings);
+
+ return str;
+}
+
+sstr_t scstrcat(size_t count, scstr_t s1, ...) {
+ va_list ap;
+ va_start(ap, s1);
+ sstr_t s = sstrvcat_a(ucx_default_allocator(), count, s1, ap);
+ va_end(ap);
+ return s;
+}
+
+sstr_t scstrcat_a(UcxAllocator *a, size_t count, scstr_t s1, ...) {
+ va_list ap;
+ va_start(ap, s1);
+ sstr_t s = sstrvcat_a(a, count, s1, ap);
+ va_end(ap);
+ return s;
+}
+
+static int ucx_substring(
+ size_t str_length,
+ size_t start,
+ size_t length,
+ size_t *newlen,
+ size_t *newpos)
+{
+ *newlen = 0;
+ *newpos = 0;
+
+ if(start > str_length) {
+ return 0;
+ }
+
+ if(length > str_length - start) {
+ length = str_length - start;
+ }
+ *newlen = length;
+ *newpos = start;
+ return 1;
+}
+
+sstr_t sstrsubs(sstr_t s, size_t start) {
+ return sstrsubsl (s, start, s.length-start);
+}
+
+sstr_t sstrsubsl(sstr_t s, size_t start, size_t length) {
+ size_t pos;
+ sstr_t ret = { NULL, 0 };
+ if(ucx_substring(s.length, start, length, &ret.length, &pos)) {
+ ret.ptr = s.ptr + pos;
+ }
+ return ret;
+}
+
+scstr_t scstrsubs(scstr_t string, size_t start) {
+ return scstrsubsl(string, start, string.length-start);
+}
+
+scstr_t scstrsubsl(scstr_t s, size_t start, size_t length) {
+ size_t pos;
+ scstr_t ret = { NULL, 0 };
+ if(ucx_substring(s.length, start, length, &ret.length, &pos)) {
+ ret.ptr = s.ptr + pos;
+ }
+ return ret;
+}
+
+
+static int ucx_strchr(const char *str, size_t length, int chr, size_t *pos) {
+ for(size_t i=0;i<length;i++) {
+ if(str[i] == chr) {
+ *pos = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int ucx_strrchr(const char *str, size_t length, int chr, size_t *pos) {
+ if(length > 0) {
+ for(size_t i=length ; i>0 ; i--) {
+ if(str[i-1] == chr) {
+ *pos = i-1;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+sstr_t sstrchr(sstr_t s, int c) {
+ size_t pos = 0;
+ if(ucx_strchr(s.ptr, s.length, c, &pos)) {
+ return sstrsubs(s, pos);
+ }
+ return sstrn(NULL, 0);
+}
+
+sstr_t sstrrchr(sstr_t s, int c) {
+ size_t pos = 0;
+ if(ucx_strrchr(s.ptr, s.length, c, &pos)) {
+ return sstrsubs(s, pos);
+ }
+ return sstrn(NULL, 0);
+}
+
+scstr_t scstrchr(scstr_t s, int c) {
+ size_t pos = 0;
+ if(ucx_strchr(s.ptr, s.length, c, &pos)) {
+ return scstrsubs(s, pos);
+ }
+ return scstrn(NULL, 0);
+}
+
+scstr_t scstrrchr(scstr_t s, int c) {
+ size_t pos = 0;
+ if(ucx_strrchr(s.ptr, s.length, c, &pos)) {
+ return scstrsubs(s, pos);
+ }
+ return scstrn(NULL, 0);
+}
+
+#define ptable_r(dest, useheap, ptable, index) (dest = useheap ? \
+ ((size_t*)ptable)[index] : (size_t) ((uint8_t*)ptable)[index])
+
+#define ptable_w(useheap, ptable, index, src) do {\
+ if (!useheap) ((uint8_t*)ptable)[index] = (uint8_t) src;\
+ else ((size_t*)ptable)[index] = src;\
+ } while (0);
+
+
+static const char* ucx_strstr(
+ const char *str,
+ size_t length,
+ const char *match,
+ size_t matchlen,
+ size_t *newlen)
+{
+ *newlen = length;
+ if (matchlen == 0) {
+ return str;
+ }
+
+ const char *result = NULL;
+ size_t resultlen = 0;
+
+ /*
+ * IMPORTANT:
+ * our prefix table contains the prefix length PLUS ONE
+ * this is our decision, because we want to use the full range of size_t
+ * the original algorithm needs a (-1) at one single place
+ * and we want to avoid that
+ */
+
+ /* static prefix table */
+ static uint8_t s_prefix_table[256];
+
+ /* check pattern length and use appropriate prefix table */
+ /* if the pattern exceeds static prefix table, allocate on the heap */
+ register int useheap = matchlen > 255;
+ register void* ptable = useheap ?
+ calloc(matchlen+1, sizeof(size_t)): s_prefix_table;
+
+ /* keep counter in registers */
+ register size_t i, j;
+
+ /* fill prefix table */
+ i = 0; j = 0;
+ ptable_w(useheap, ptable, i, j);
+ while (i < matchlen) {
+ while (j >= 1 && match[j-1] != match[i]) {
+ ptable_r(j, useheap, ptable, j-1);
+ }
+ i++; j++;
+ ptable_w(useheap, ptable, i, j);
+ }
+
+ /* search */
+ i = 0; j = 1;
+ while (i < length) {
+ while (j >= 1 && str[i] != match[j-1]) {
+ ptable_r(j, useheap, ptable, j-1);
+ }
+ i++; j++;
+ if (j-1 == matchlen) {
+ size_t start = i - matchlen;
+ result = str + start;
+ resultlen = length - start;
+ break;
+ }
+ }
+
+ /* if prefix table was allocated on the heap, free it */
+ if (ptable != s_prefix_table) {
+ free(ptable);
+ }
+
+ *newlen = resultlen;
+ return result;
+}
+
+sstr_t scstrsstr(sstr_t string, scstr_t match) {
+ sstr_t result;
+
+ size_t reslen;
+ const char *resstr = ucx_strstr(string.ptr, string.length, match.ptr, match.length, &reslen);
+ if(!resstr) {
+ result.ptr = NULL;
+ result.length = 0;
+ return result;
+ }
+
+ size_t pos = resstr - string.ptr;
+ result.ptr = string.ptr + pos;
+ result.length = reslen;
+
+ return result;
+}
+
+scstr_t scstrscstr(scstr_t string, scstr_t match) {
+ scstr_t result;
+
+ size_t reslen;
+ const char *resstr = ucx_strstr(string.ptr, string.length, match.ptr, match.length, &reslen);
+ if(!resstr) {
+ result.ptr = NULL;
+ result.length = 0;
+ return result;
+ }
+
+ size_t pos = resstr - string.ptr;
+ result.ptr = string.ptr + pos;
+ result.length = reslen;
+
+ return result;
+}
+
+#undef ptable_r
+#undef ptable_w
+
+sstr_t* scstrsplit(scstr_t s, scstr_t d, ssize_t *n) {
+ return scstrsplit_a(ucx_default_allocator(), s, d, n);
+}
+
+sstr_t* scstrsplit_a(UcxAllocator *allocator, scstr_t s, scstr_t d, ssize_t *n) {
+ if (s.length == 0 || d.length == 0) {
+ *n = -1;
+ return NULL;
+ }
+
+ /* special cases: delimiter is at least as large as the string */
+ if (d.length >= s.length) {
+ /* exact match */
+ if (sstrcmp(s, d) == 0) {
+ *n = 0;
+ return NULL;
+ } else /* no match possible */ {
+ *n = 1;
+ sstr_t *result = (sstr_t*) almalloc(allocator, sizeof(sstr_t));
+ if(result) {
+ *result = sstrdup_a(allocator, s);
+ } else {
+ *n = -2;
+ }
+ return result;
+ }
+ }
+
+ ssize_t nmax = *n;
+ size_t arrlen = 16;
+ sstr_t* result = (sstr_t*) alcalloc(allocator, arrlen, sizeof(sstr_t));
+
+ if (result) {
+ scstr_t curpos = s;
+ ssize_t j = 1;
+ while (1) {
+ scstr_t match;
+ /* optimize for one byte delimiters */
+ if (d.length == 1) {
+ match = curpos;
+ for (size_t i = 0 ; i < curpos.length ; i++) {
+ if (curpos.ptr[i] == *(d.ptr)) {
+ match.ptr = curpos.ptr + i;
+ break;
+ }
+ match.length--;
+ }
+ } else {
+ match = scstrscstr(curpos, d);
+ }
+ if (match.length > 0) {
+ /* is this our last try? */
+ if (nmax == 0 || j < nmax) {
+ /* copy the current string to the array */
+ scstr_t item = scstrn(curpos.ptr, match.ptr - curpos.ptr);
+ result[j-1] = sstrdup_a(allocator, item);
+ size_t processed = item.length + d.length;
+ curpos.ptr += processed;
+ curpos.length -= processed;
+
+ /* allocate memory for the next string */
+ j++;
+ if (j > arrlen) {
+ arrlen *= 2;
+ size_t reallocsz;
+ sstr_t* reallocated = NULL;
+ if(!ucx_szmul(arrlen, sizeof(sstr_t), &reallocsz)) {
+ reallocated = (sstr_t*) alrealloc(
+ allocator, result, reallocsz);
+ }
+ if (reallocated) {
+ result = reallocated;
+ } else {
+ for (ssize_t i = 0 ; i < j-1 ; i++) {
+ alfree(allocator, result[i].ptr);
+ }
+ alfree(allocator, result);
+ *n = -2;
+ return NULL;
+ }
+ }
+ } else {
+ /* nmax reached, copy the _full_ remaining string */
+ result[j-1] = sstrdup_a(allocator, curpos);
+ break;
+ }
+ } else {
+ /* no more matches, copy last string */
+ result[j-1] = sstrdup_a(allocator, curpos);
+ break;
+ }
+ }
+ *n = j;
+ } else {
+ *n = -2;
+ }
+
+ return result;
+}
+
+int scstrcmp(scstr_t s1, scstr_t s2) {
+ if (s1.length == s2.length) {
+ return memcmp(s1.ptr, s2.ptr, s1.length);
+ } else if (s1.length > s2.length) {
+ return 1;
+ } else {
+ return -1;
+ }
+}
+
+int scstrcasecmp(scstr_t s1, scstr_t s2) {
+ if (s1.length == s2.length) {
+#ifdef _WIN32
+ return _strnicmp(s1.ptr, s2.ptr, s1.length);
+#else
+ return strncasecmp(s1.ptr, s2.ptr, s1.length);
+#endif
+ } else if (s1.length > s2.length) {
+ return 1;
+ } else {
+ return -1;
+ }
+}
+
+sstr_t scstrdup(scstr_t s) {
+ return sstrdup_a(ucx_default_allocator(), s);
+}
+
+sstr_t scstrdup_a(UcxAllocator *allocator, scstr_t s) {
+ sstr_t newstring;
+ newstring.ptr = (char*)almalloc(allocator, s.length + 1);
+ if (newstring.ptr) {
+ newstring.length = s.length;
+ newstring.ptr[newstring.length] = 0;
+
+ memcpy(newstring.ptr, s.ptr, s.length);
+ } else {
+ newstring.length = 0;
+ }
+
+ return newstring;
+}
+
+
+static size_t ucx_strtrim(const char *s, size_t len, size_t *newlen) {
+ const char *newptr = s;
+ size_t length = len;
+
+ while(length > 0 && isspace(*newptr)) {
+ newptr++;
+ length--;
+ }
+ while(length > 0 && isspace(newptr[length-1])) {
+ length--;
+ }
+
+ *newlen = length;
+ return newptr - s;
+}
+
+sstr_t sstrtrim(sstr_t string) {
+ sstr_t newstr;
+ newstr.ptr = string.ptr
+ + ucx_strtrim(string.ptr, string.length, &newstr.length);
+ return newstr;
+}
+
+scstr_t scstrtrim(scstr_t string) {
+ scstr_t newstr;
+ newstr.ptr = string.ptr
+ + ucx_strtrim(string.ptr, string.length, &newstr.length);
+ return newstr;
+}
+
+int scstrprefix(scstr_t string, scstr_t prefix) {
+ if (string.length == 0) {
+ return prefix.length == 0;
+ }
+ if (prefix.length == 0) {
+ return 1;
+ }
+
+ if (prefix.length > string.length) {
+ return 0;
+ } else {
+ return memcmp(string.ptr, prefix.ptr, prefix.length) == 0;
+ }
+}
+
+int scstrsuffix(scstr_t string, scstr_t suffix) {
+ if (string.length == 0) {
+ return suffix.length == 0;
+ }
+ if (suffix.length == 0) {
+ return 1;
+ }
+
+ if (suffix.length > string.length) {
+ return 0;
+ } else {
+ return memcmp(string.ptr+string.length-suffix.length,
+ suffix.ptr, suffix.length) == 0;
+ }
+}
+
+int scstrcaseprefix(scstr_t string, scstr_t prefix) {
+ if (string.length == 0) {
+ return prefix.length == 0;
+ }
+ if (prefix.length == 0) {
+ return 1;
+ }
+
+ if (prefix.length > string.length) {
+ return 0;
+ } else {
+ scstr_t subs = scstrsubsl(string, 0, prefix.length);
+ return scstrcasecmp(subs, prefix) == 0;
+ }
+}
+
+int scstrcasesuffix(scstr_t string, scstr_t suffix) {
+ if (string.length == 0) {
+ return suffix.length == 0;
+ }
+ if (suffix.length == 0) {
+ return 1;
+ }
+
+ if (suffix.length > string.length) {
+ return 0;
+ } else {
+ scstr_t subs = scstrsubs(string, string.length-suffix.length);
+ return scstrcasecmp(subs, suffix) == 0;
+ }
+}
+
+sstr_t scstrlower(scstr_t string) {
+ sstr_t ret = sstrdup(string);
+ for (size_t i = 0; i < ret.length ; i++) {
+ ret.ptr[i] = tolower(ret.ptr[i]);
+ }
+ return ret;
+}
+
+sstr_t scstrlower_a(UcxAllocator *allocator, scstr_t string) {
+ sstr_t ret = sstrdup_a(allocator, string);
+ for (size_t i = 0; i < ret.length ; i++) {
+ ret.ptr[i] = tolower(ret.ptr[i]);
+ }
+ return ret;
+}
+
+sstr_t scstrupper(scstr_t string) {
+ sstr_t ret = sstrdup(string);
+ for (size_t i = 0; i < ret.length ; i++) {
+ ret.ptr[i] = toupper(ret.ptr[i]);
+ }
+ return ret;
+}
+
+sstr_t scstrupper_a(UcxAllocator *allocator, scstr_t string) {
+ sstr_t ret = sstrdup_a(allocator, string);
+ for (size_t i = 0; i < ret.length ; i++) {
+ ret.ptr[i] = toupper(ret.ptr[i]);
+ }
+ return ret;
+}
+
+#define REPLACE_INDEX_BUFFER_MAX 100
+
+struct scstrreplace_ibuf {
+ size_t* buf;
+ unsigned int len; /* small indices */
+ struct scstrreplace_ibuf* next;
+};
+
+static void scstrrepl_free_ibuf(struct scstrreplace_ibuf *buf) {
+ while (buf) {
+ struct scstrreplace_ibuf *next = buf->next;
+ free(buf->buf);
+ free(buf);
+ buf = next;
+ }
+}
+
+sstr_t scstrreplacen_a(UcxAllocator *allocator, scstr_t str,
+ scstr_t pattern, scstr_t replacement, size_t replmax) {
+
+ if (pattern.length == 0 || pattern.length > str.length || replmax == 0)
+ return sstrdup(str);
+
+ /* Compute expected buffer length */
+ size_t ibufmax = str.length / pattern.length;
+ size_t ibuflen = replmax < ibufmax ? replmax : ibufmax;
+ if (ibuflen > REPLACE_INDEX_BUFFER_MAX) {
+ ibuflen = REPLACE_INDEX_BUFFER_MAX;
+ }
+
+ /* Allocate first index buffer */
+ struct scstrreplace_ibuf *firstbuf, *curbuf;
+ firstbuf = curbuf = calloc(1, sizeof(struct scstrreplace_ibuf));
+ if (!firstbuf) return sstrn(NULL, 0);
+ firstbuf->buf = calloc(ibuflen, sizeof(size_t));
+ if (!firstbuf->buf) {
+ free(firstbuf);
+ return sstrn(NULL, 0);
+ }
+
+ /* Search occurrences */
+ scstr_t searchstr = str;
+ size_t found = 0;
+ do {
+ scstr_t match = scstrscstr(searchstr, pattern);
+ if (match.length > 0) {
+ /* Allocate next buffer in chain, if required */
+ if (curbuf->len == ibuflen) {
+ struct scstrreplace_ibuf *nextbuf =
+ calloc(1, sizeof(struct scstrreplace_ibuf));
+ if (!nextbuf) {
+ scstrrepl_free_ibuf(firstbuf);
+ return sstrn(NULL, 0);
+ }
+ nextbuf->buf = calloc(ibuflen, sizeof(size_t));
+ if (!nextbuf->buf) {
+ free(nextbuf);
+ scstrrepl_free_ibuf(firstbuf);
+ return sstrn(NULL, 0);
+ }
+ curbuf->next = nextbuf;
+ curbuf = nextbuf;
+ }
+
+ /* Record match index */
+ found++;
+ size_t idx = match.ptr - str.ptr;
+ curbuf->buf[curbuf->len++] = idx;
+ searchstr.ptr = match.ptr + pattern.length;
+ searchstr.length = str.length - idx - pattern.length;
+ } else {
+ break;
+ }
+ } while (searchstr.length > 0 && found < replmax);
+
+ /* Allocate result string */
+ sstr_t result;
+ {
+ ssize_t adjlen = (ssize_t) replacement.length - (ssize_t) pattern.length;
+ size_t rcount = 0;
+ curbuf = firstbuf;
+ do {
+ rcount += curbuf->len;
+ curbuf = curbuf->next;
+ } while (curbuf);
+ result.length = str.length + rcount * adjlen;
+ result.ptr = almalloc(allocator, result.length);
+ if (!result.ptr) {
+ scstrrepl_free_ibuf(firstbuf);
+ return sstrn(NULL, 0);
+ }
+ }
+
+ /* Build result string */
+ curbuf = firstbuf;
+ size_t srcidx = 0;
+ char* destptr = result.ptr;
+ do {
+ for (size_t i = 0; i < curbuf->len; i++) {
+ /* Copy source part up to next match*/
+ size_t idx = curbuf->buf[i];
+ size_t srclen = idx - srcidx;
+ if (srclen > 0) {
+ memcpy(destptr, str.ptr+srcidx, srclen);
+ destptr += srclen;
+ srcidx += srclen;
+ }
+
+ /* Copy the replacement and skip the source pattern */
+ srcidx += pattern.length;
+ memcpy(destptr, replacement.ptr, replacement.length);
+ destptr += replacement.length;
+ }
+ curbuf = curbuf->next;
+ } while (curbuf);
+ memcpy(destptr, str.ptr+srcidx, str.length-srcidx);
+
+ /* Free index buffer */
+ scstrrepl_free_ibuf(firstbuf);
+
+ return result;
+}
+
+sstr_t scstrreplacen(scstr_t str, scstr_t pattern,
+ scstr_t replacement, size_t replmax) {
+ return scstrreplacen_a(ucx_default_allocator(),
+ str, pattern, replacement, replmax);
+}
+
+
+// type adjustment functions
+scstr_t ucx_sc2sc(scstr_t str) {
+ return str;
+}
+scstr_t ucx_ss2sc(sstr_t str) {
+ scstr_t cs;
+ cs.ptr = str.ptr;
+ cs.length = str.length;
+ return cs;
+}
+scstr_t ucx_ss2c_s(scstr_t c) {
+ return c;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucx/test.h"
+
+UcxTestSuite* ucx_test_suite_new() {
+ UcxTestSuite* suite = (UcxTestSuite*) malloc(sizeof(UcxTestSuite));
+ if (suite != NULL) {
+ suite->success = 0;
+ suite->failure = 0;
+ suite->tests = NULL;
+ }
+
+ return suite;
+}
+
+void ucx_test_suite_free(UcxTestSuite* suite) {
+ UcxTestList *l = suite->tests;
+ while (l != NULL) {
+ UcxTestList *e = l;
+ l = l->next;
+ free(e);
+ }
+ free(suite);
+}
+
+int ucx_test_register(UcxTestSuite* suite, UcxTest test) {
+ if (suite->tests) {
+ UcxTestList *newelem = (UcxTestList*) malloc(sizeof(UcxTestList));
+ if (newelem) {
+ newelem->test = test;
+ newelem->next = NULL;
+
+ UcxTestList *last = suite->tests;
+ while (last->next) {
+ last = last->next;
+ }
+ last->next = newelem;
+
+ return EXIT_SUCCESS;
+ } else {
+ return EXIT_FAILURE;
+ }
+ } else {
+ suite->tests = (UcxTestList*) malloc(sizeof(UcxTestList));
+ if (suite->tests) {
+ suite->tests->test = test;
+ suite->tests->next = NULL;
+
+ return EXIT_SUCCESS;
+ } else {
+ return EXIT_FAILURE;
+ }
+ }
+}
+
+void ucx_test_run(UcxTestSuite* suite, FILE* output) {
+ suite->success = 0;
+ suite->failure = 0;
+ for (UcxTestList* elem = suite->tests ; elem ; elem = elem->next) {
+ elem->test(suite, output);
+ }
+ fwrite("\nAll test completed.\n", 1, 21, output);
+ fprintf(output, " Total: %u\n Success: %u\n Failure: %u\n",
+ suite->success+suite->failure, suite->success, suite->failure);
+}
--- /dev/null
+/**
+ * @mainpage UAP Common Extensions
+ * Library with common and useful functions, macros and data structures.
+ * <p>
+ * Latest available source:<br>
+ * <a href="https://sourceforge.net/projects/ucx/files/">
+ * https://sourceforge.net/projects/ucx/files/</a>
+ * </p>
+ *
+ * <p>
+ * Repositories:<br>
+ * <a href="https://sourceforge.net/p/ucx/code">
+ * https://sourceforge.net/p/ucx/code</a>
+ * - or -
+ * <a href="https://develop.uap-core.de/hg/ucx">
+ * https://develop.uap-core.de/hg/ucx</a>
+ * </p>
+ *
+ * <h2>LICENCE</h2>
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucx/ucx.h"
+
+int ucx_szmul_impl(size_t a, size_t b, size_t *result) {
+ if(a == 0 || b == 0) {
+ *result = 0;
+ return 0;
+ }
+ size_t r = a * b;
+ if(r / b == a) {
+ *result = r;
+ return 0;
+ } else {
+ *result = 0;
+ return 1;
+ }
+}
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Allocator for custom memory management.
+ *
+ * A UCX allocator consists of a pointer to the memory area / pool and four
+ * function pointers to memory management functions operating on this memory
+ * area / pool. These functions shall behave equivalent to the standard libc
+ * functions <code>malloc(), calloc(), realloc()</code> and <code>free()</code>.
+ *
+ * The signature of the memory management functions is based on the signature
+ * of the respective libc function but each of them takes the pointer to the
+ * memory area / pool as first argument.
+ *
+ * As the pointer to the memory area / pool can be arbitrarily chosen, any data
+ * can be provided to the memory management functions. A UcxMempool is just
+ * one example.
+ *
+ * @see mempool.h
+ * @see UcxMap
+ *
+ * @file allocator.h
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_ALLOCATOR_H
+#define UCX_ALLOCATOR_H
+
+#include "ucx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * A function pointer to the allocators <code>malloc()</code> function.
+ * @see UcxAllocator
+ */
+typedef void*(*ucx_allocator_malloc)(void *pool, size_t n);
+
+/**
+ * A function pointer to the allocators <code>calloc()</code> function.
+ * @see UcxAllocator
+ */
+typedef void*(*ucx_allocator_calloc)(void *pool, size_t n, size_t size);
+
+/**
+ * A function pointer to the allocators <code>realloc()</code> function.
+ * @see UcxAllocator
+ */
+typedef void*(*ucx_allocator_realloc)(void *pool, void *data, size_t n);
+
+/**
+ * A function pointer to the allocators <code>free()</code> function.
+ * @see UcxAllocator
+ */
+typedef void(*ucx_allocator_free)(void *pool, void *data);
+
+/**
+ * UCX allocator data structure containing memory management functions.
+ */
+typedef struct {
+ /** Pointer to an area of memory or a complex memory pool.
+ * This pointer will be passed to any memory management function as first
+ * argument.
+ */
+ void *pool;
+ /**
+ * The <code>malloc()</code> function for this allocator.
+ */
+ ucx_allocator_malloc malloc;
+ /**
+ * The <code>calloc()</code> function for this allocator.
+ */
+ ucx_allocator_calloc calloc;
+ /**
+ * The <code>realloc()</code> function for this allocator.
+ */
+ ucx_allocator_realloc realloc;
+ /**
+ * The <code>free()</code> function for this allocator.
+ */
+ ucx_allocator_free free;
+} UcxAllocator;
+
+/**
+ * Returns a pointer to the default allocator.
+ *
+ * The default allocator contains wrappers to the standard libc memory
+ * management functions. Use this function to get a pointer to a globally
+ * available allocator. You may also define an own UcxAllocator by assigning
+ * #UCX_ALLOCATOR_DEFAULT to a variable and pass the address of this variable
+ * to any function that takes a UcxAllocator as argument. Note that using
+ * this function is the recommended way of passing a default allocator, thus
+ * it never runs out of scope.
+ *
+ * @return a pointer to the default allocator
+ *
+ * @see UCX_ALLOCATOR_DEFAULT
+ */
+UcxAllocator *ucx_default_allocator();
+
+/**
+ * A wrapper for the standard libc <code>malloc()</code> function.
+ * @param ignore ignored (may be used by allocators for pooled memory)
+ * @param n argument passed to <code>malloc()</code>
+ * @return return value of <code>malloc()</code>
+ */
+void *ucx_default_malloc(void *ignore, size_t n);
+/**
+ * A wrapper for the standard libc <code>calloc()</code> function.
+ * @param ignore ignored (may be used by allocators for pooled memory)
+ * @param n argument passed to <code>calloc()</code>
+ * @param size argument passed to <code>calloc()</code>
+ * @return return value of <code>calloc()</code>
+ */
+void *ucx_default_calloc(void *ignore, size_t n, size_t size);
+/**
+ * A wrapper for the standard libc <code>realloc()</code> function.
+ * @param ignore ignored (may be used by allocators for pooled memory)
+ * @param data argumend passed to <code>realloc()</code>
+ * @param n argument passed to <code>realloc()</code>
+ * @return return value of <code>realloc()</code>
+ */
+void *ucx_default_realloc(void *ignore, void *data, size_t n);
+/**
+ * A wrapper for the standard libc <code>free()</code> function.
+ * @param ignore ignored (may be used by allocators for pooled memory)
+ * @param data argument passed to <code>free()</code>
+ */
+void ucx_default_free(void *ignore, void *data);
+
+/**
+ * Shorthand for calling an allocators malloc function.
+ * @param allocator the allocator to use
+ * @param n size of space to allocate
+ * @return a pointer to the allocated memory area
+ */
+#define almalloc(allocator, n) ((allocator)->malloc((allocator)->pool, n))
+
+/**
+ * Shorthand for calling an allocators calloc function.
+ * @param allocator the allocator to use
+ * @param n the count of elements the space should be allocated for
+ * @param size the size of each element
+ * @return a pointer to the allocated memory area
+ */
+#define alcalloc(allocator, n, size) \
+ ((allocator)->calloc((allocator)->pool, n, size))
+
+/**
+ * Shorthand for calling an allocators realloc function.
+ * @param allocator the allocator to use
+ * @param ptr the pointer to the memory area that shall be reallocated
+ * @param n the new size of the allocated memory area
+ * @return a pointer to the reallocated memory area
+ */
+#define alrealloc(allocator, ptr, n) \
+ ((allocator)->realloc((allocator)->pool, ptr, n))
+
+/**
+ * Shorthand for calling an allocators free function.
+ * @param allocator the allocator to use
+ * @param ptr the pointer to the memory area that shall be freed
+ */
+#define alfree(allocator, ptr) ((allocator)->free((allocator)->pool, ptr))
+
+/**
+ * Convenient macro for a default allocator <code>struct</code> definition.
+ */
+#define UCX_ALLOCATOR_DEFAULT {NULL, \
+ ucx_default_malloc, ucx_default_calloc, ucx_default_realloc, \
+ ucx_default_free }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_ALLOCATOR_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2019 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Dynamically allocated array implementation.
+ *
+ * @file array.h
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_ARRAY_H
+#define UCX_ARRAY_H
+
+#include "ucx.h"
+#include "allocator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * UCX array type.
+ */
+typedef struct {
+ /**
+ * The current capacity of the array.
+ */
+ size_t capacity;
+ /**
+ * The actual number of elements in the array.
+ */
+ size_t size;
+ /**
+ * The size of an individual element in bytes.
+ */
+ size_t elemsize;
+ /**
+ * A pointer to the data.
+ */
+ void* data;
+ /**
+ * The allocator used for the data.
+ */
+ UcxAllocator* allocator;
+} UcxArray;
+
+/**
+ * Sets an element in an arbitrary user defined array.
+ * The data is copied from the specified data location.
+ *
+ * If the capacity is insufficient, the array is automatically reallocated and
+ * the possibly new pointer is stored in the <code>array</code> argument.
+ *
+ * On reallocation the capacity of the array is doubled until it is sufficient.
+ * The new capacity is stored back to <code>capacity</code>.
+ *
+ * @param array a pointer to location of the array pointer
+ * @param capacity a pointer to the capacity
+ * @param elmsize the size of each element
+ * @param idx the index of the element to set
+ * @param data a pointer to the element data
+ * @return zero on success or non-zero on error (errno will be set)
+ */
+#define ucx_array_util_set(array, capacity, elmsize, idx, data) \
+ ucx_array_util_set_a(ucx_default_allocator(), (void**)(array), capacity, \
+ elmsize, idx, data)
+
+/**
+ * Sets an element in an arbitrary user defined array.
+ * The data is copied from the specified data location.
+ *
+ * If the capacity is insufficient, the array is automatically reallocated
+ * using the specified allocator and the possibly new pointer is stored in
+ * the <code>array</code> argument.
+ *
+ * On reallocation the capacity of the array is doubled until it is sufficient.
+ * The new capacity is stored back to <code>capacity</code>.
+ *
+ * @param alloc the allocator that shall be used to reallocate the array
+ * @param array a pointer to location of the array pointer
+ * @param capacity a pointer to the capacity
+ * @param elmsize the size of each element
+ * @param idx the index of the element to set
+ * @param data a pointer to the element data
+ * @return zero on success or non-zero on error (errno will be set)
+ */
+int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity,
+ size_t elmsize, size_t idx, void* data);
+
+/**
+ * Stores a pointer in an arbitrary user defined array.
+ * The element size of the array must be sizeof(void*).
+ *
+ * If the capacity is insufficient, the array is automatically reallocated and
+ * the possibly new pointer is stored in the <code>array</code> argument.
+ *
+ * On reallocation the capacity of the array is doubled until it is sufficient.
+ * The new capacity is stored back to <code>capacity</code>.
+ *
+ * @param array a pointer to location of the array pointer
+ * @param capacity a pointer to the capacity
+ * @param idx the index of the element to set
+ * @param ptr the pointer to store
+ * @return zero on success or non-zero on error (errno will be set)
+ */
+#define ucx_array_util_setptr(array, capacity, idx, ptr) \
+ ucx_array_util_setptr_a(ucx_default_allocator(), (void**)(array), \
+ capacity, idx, ptr)
+
+/**
+ * Stores a pointer in an arbitrary user defined array.
+ * The element size of the array must be sizeof(void*).
+ *
+ * If the capacity is insufficient, the array is automatically reallocated
+ * using the specified allocator and the possibly new pointer is stored in
+ * the <code>array</code> argument.
+ *
+ * On reallocation the capacity of the array is doubled until it is sufficient.
+ * The new capacity is stored back to <code>capacity</code>.
+ *
+ * @param alloc the allocator that shall be used to reallocate the array
+ * @param array a pointer to location of the array pointer
+ * @param capacity a pointer to the capacity
+ * @param idx the index of the element to set
+ * @param ptr the pointer to store
+ * @return zero on success or non-zero on error (errno will be set)
+ */
+int ucx_array_util_setptr_a(UcxAllocator* alloc, void** array, size_t* capacity,
+ size_t idx, void* ptr);
+
+
+/**
+ * Creates a new UCX array with the given capacity and element size.
+ * @param capacity the initial capacity
+ * @param elemsize the element size
+ * @return a pointer to a new UCX array structure
+ */
+UcxArray* ucx_array_new(size_t capacity, size_t elemsize);
+
+/**
+ * Creates a new UCX array using the specified allocator.
+ *
+ * @param capacity the initial capacity
+ * @param elemsize the element size
+ * @param allocator the allocator to use
+ * @return a pointer to new UCX array structure
+ */
+UcxArray* ucx_array_new_a(size_t capacity, size_t elemsize,
+ UcxAllocator* allocator);
+
+/**
+ * Initializes a UCX array structure with the given capacity and element size.
+ * The structure must be uninitialized as the data pointer will be overwritten.
+ *
+ * @param array the structure to initialize
+ * @param capacity the initial capacity
+ * @param elemsize the element size
+ */
+void ucx_array_init(UcxArray* array, size_t capacity, size_t elemsize);
+
+/**
+ * Initializes a UCX array structure using the specified allocator.
+ * The structure must be uninitialized as the data pointer will be overwritten.
+ *
+ * @param array the structure to initialize
+ * @param capacity the initial capacity
+ * @param elemsize the element size
+ * @param allocator the allocator to use
+ */
+void ucx_array_init_a(UcxArray* array, size_t capacity, size_t elemsize,
+ UcxAllocator* allocator);
+
+/**
+ * Creates an shallow copy of an array.
+ *
+ * This function clones the specified array by using memcpy().
+ * If the destination capacity is insufficient, an automatic reallocation is
+ * attempted.
+ *
+ * Note: if the destination array is uninitialized, the behavior is undefined.
+ *
+ * @param dest the array to copy to
+ * @param src the array to copy from
+ * @return zero on success, non-zero on reallocation failure.
+ */
+int ucx_array_clone(UcxArray* dest, UcxArray const* src);
+
+
+/**
+ * Compares two UCX arrays element-wise by using a compare function.
+ *
+ * Elements of the two specified arrays are compared by using the specified
+ * compare function and the additional data. The type and content of this
+ * additional data depends on the cmp_func() used.
+ *
+ * This function always returns zero, if the element sizes of the arrays do
+ * not match and performs no comparisons in this case.
+ *
+ * @param array1 the first array
+ * @param array2 the second array
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return 1, if and only if the two arrays equal element-wise, 0 otherwise
+ */
+int ucx_array_equals(UcxArray const *array1, UcxArray const *array2,
+ cmp_func cmpfnc, void* data);
+
+/**
+ * Destroys the array.
+ *
+ * The data is freed and both capacity and count are reset to zero.
+ * If the array structure itself has been dynamically allocated, it has to be
+ * freed separately.
+ *
+ * @param array the array to destroy
+ */
+void ucx_array_destroy(UcxArray *array);
+
+/**
+ * Destroys and frees the array.
+ *
+ * @param array the array to free
+ */
+void ucx_array_free(UcxArray *array);
+
+/**
+ * Inserts elements at the end of the array.
+ *
+ * This is an O(1) operation.
+ * The array will automatically grow, if the capacity is exceeded.
+ * If a pointer to data is provided, the data is copied into the array with
+ * memcpy(). Otherwise the new elements are completely zeroed.
+ *
+ * @param array a pointer the array where to append the data
+ * @param data a pointer to the data to insert (may be <code>NULL</code>)
+ * @param count number of elements to copy from data (if data is
+ * <code>NULL</code>, zeroed elements are appended)
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_set_from()
+ * @see ucx_array_append()
+ */
+int ucx_array_append_from(UcxArray *array, void *data, size_t count);
+
+
+/**
+ * Inserts elements at the beginning of the array.
+ *
+ * This is an expensive operation, because the contents must be moved.
+ * If there is no particular reason to prepend data, you should use
+ * ucx_array_append_from() instead.
+ *
+ * @param array a pointer the array where to prepend the data
+ * @param data a pointer to the data to insert (may be <code>NULL</code>)
+ * @param count number of elements to copy from data (if data is
+ * <code>NULL</code>, zeroed elements are inserted)
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_append_from()
+ * @see ucx_array_set_from()
+ * @see ucx_array_prepend()
+ */
+int ucx_array_prepend_from(UcxArray *array, void *data, size_t count);
+
+
+/**
+ * Sets elements starting at the specified index.
+ *
+ * If the any index is out of bounds, the array automatically grows.
+ * The pointer to the data may be NULL, in which case the elements are zeroed.
+ *
+ * @param array a pointer the array where to set the data
+ * @param index the index of the element to set
+ * @param data a pointer to the data to insert (may be <code>NULL</code>)
+ * @param count number of elements to copy from data (if data is
+ * <code>NULL</code>, the memory in the array is zeroed)
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_append_from()
+ * @see ucx_array_set()
+ */
+int ucx_array_set_from(UcxArray *array, size_t index, void *data, size_t count);
+
+/**
+ * Concatenates two arrays.
+ *
+ * The contents of the second array are appended to the first array in one
+ * single operation. The second array is otherwise left untouched.
+ *
+ * The first array may grow automatically. If this fails, both arrays remain
+ * unmodified.
+ *
+ * @param array1 first array
+ * @param array2 second array
+ * @return zero on success, non-zero if reallocation was necessary but failed
+ * or the element size does not match
+ */
+int ucx_array_concat(UcxArray *array1, const UcxArray *array2);
+
+/**
+ * Returns a pointer to the array element at the specified index.
+ *
+ * @param array the array to retrieve the element from
+ * @param index index of the element to return
+ * @return a pointer to the element at the specified index or <code>NULL</code>,
+ * if the index is greater than the array size
+ */
+void *ucx_array_at(UcxArray const* array, size_t index);
+
+/**
+ * Returns the index of an element containing the specified data.
+ *
+ * This function uses a cmp_func() to compare the data of each list element
+ * with the specified data. If no cmp_func is provided, memcmp() is used.
+ *
+ * If the array contains the data more than once, the index of the first
+ * occurrence is returned.
+ * If the array does not contain the data, the size of array is returned.
+ *
+ * @param array the array where to search for the data
+ * @param elem the element data
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return the index of the element containing the specified data or the size of
+ * the array, if the data is not found in this array
+ */
+size_t ucx_array_find(UcxArray const *array, void *elem,
+ cmp_func cmpfnc, void *data);
+
+/**
+ * Checks, if an array contains a specific element.
+ *
+ * An element is found, if ucx_array_find() returns a value less than the size.
+ *
+ * @param array the array where to search for the data
+ * @param elem the element data
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return 1, if and only if the array contains the specified element data
+ * @see ucx_array_find()
+ */
+int ucx_array_contains(UcxArray const *array, void *elem,
+ cmp_func cmpfnc, void *data);
+
+/**
+ * Sorts a UcxArray with the best available sort algorithm.
+ *
+ * The qsort_r() function is used, if available (glibc, FreeBSD or MacOS).
+ * The order of arguments is automatically adjusted for the FreeBSD and MacOS
+ * version of qsort_r().
+ *
+ * If qsort_r() is not available, a merge sort algorithm is used, which is
+ * guaranteed to use no more additional memory than for exactly one element.
+ *
+ * @param array the array to sort
+ * @param cmpfnc the function that shall be used to compare the element data
+ * @param data additional data for the cmp_func() or <code>NULL</code>
+ */
+void ucx_array_sort(UcxArray* array, cmp_func cmpfnc, void *data);
+
+/**
+ * Removes an element from the array.
+ *
+ * This is in general an expensive operation, because several elements may
+ * be moved. If the order of the elements is not relevant, use
+ * ucx_array_remove_fast() instead.
+ *
+ * @param array pointer to the array from which the element shall be removed
+ * @param index the index of the element to remove
+ */
+void ucx_array_remove(UcxArray *array, size_t index);
+
+/**
+ * Removes an element from the array.
+ *
+ * This is an O(1) operation, but does not maintain the order of the elements.
+ * The last element in the array is moved to the location of the removed
+ * element.
+ *
+ * @param array pointer to the array from which the element shall be removed
+ * @param index the index of the element to remove
+ */
+void ucx_array_remove_fast(UcxArray *array, size_t index);
+
+/**
+ * Shrinks the memory to exactly fit the contents.
+ *
+ * After this operation, the capacity equals the size.
+ *
+ * @param array a pointer to the array
+ * @return zero on success, non-zero if reallocation failed
+ */
+int ucx_array_shrink(UcxArray* array);
+
+/**
+ * Sets the capacity of the array.
+ *
+ * If the new capacity is smaller than the size of the array, the elements
+ * are removed and the size is adjusted accordingly.
+ *
+ * @param array a pointer to the array
+ * @param capacity the new capacity
+ * @return zero on success, non-zero if reallocation failed
+ */
+int ucx_array_resize(UcxArray* array, size_t capacity);
+
+/**
+ * Resizes the array only, if the capacity is insufficient.
+ *
+ * If the requested capacity is smaller than the current capacity, this
+ * function does nothing.
+ *
+ * @param array a pointer to the array
+ * @param capacity the guaranteed capacity
+ * @return zero on success, non-zero if reallocation failed
+ */
+int ucx_array_reserve(UcxArray* array, size_t capacity);
+
+/**
+ * Resizes the capacity, if the specified number of elements would not fit.
+ *
+ * A call to ucx_array_grow(array, count) is effectively the same as
+ * ucx_array_reserve(array, array->size+count).
+ *
+ * @param array a pointer to the array
+ * @param count the number of elements that should additionally fit
+ * into the array
+ * @return zero on success, non-zero if reallocation failed
+ */
+int ucx_array_grow(UcxArray* array, size_t count);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_ARRAY_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @file avl.h
+ *
+ * AVL tree implementation.
+ *
+ * This binary search tree implementation allows average O(1) insertion and
+ * removal of elements (excluding binary search time).
+ *
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_AVL_H
+#define UCX_AVL_H
+
+#include "ucx.h"
+#include "allocator.h"
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * UCX AVL Node type.
+ *
+ * @see UcxAVLNode
+ */
+typedef struct UcxAVLNode UcxAVLNode;
+
+/**
+ * UCX AVL Node.
+ */
+struct UcxAVLNode {
+ /**
+ * The key for this node.
+ */
+ intptr_t key;
+ /**
+ * Data contained by this node.
+ */
+ void *value;
+ /**
+ * The height of this (sub)-tree.
+ */
+ size_t height;
+ /**
+ * Parent node.
+ */
+ UcxAVLNode *parent;
+ /**
+ * Root node of left subtree.
+ */
+ UcxAVLNode *left;
+ /**
+ * Root node of right subtree.
+ */
+ UcxAVLNode *right;
+};
+
+/**
+ * UCX AVL Tree.
+ */
+typedef struct {
+ /**
+ * The UcxAllocator that shall be used to manage the memory for node data.
+ */
+ UcxAllocator *allocator;
+ /**
+ * Root node of the tree.
+ */
+ UcxAVLNode *root;
+ /**
+ * Compare function that shall be used to compare the UcxAVLNode keys.
+ * @see UcxAVLNode.key
+ */
+ cmp_func cmpfunc;
+ /**
+ * Custom user data.
+ * This data will also be provided to the cmpfunc.
+ */
+ void *userdata;
+} UcxAVLTree;
+
+/**
+ * Initializes a new UcxAVLTree with a default allocator.
+ *
+ * @param cmpfunc the compare function that shall be used
+ * @return a new UcxAVLTree object
+ * @see ucx_avl_new_a()
+ */
+UcxAVLTree *ucx_avl_new(cmp_func cmpfunc);
+
+/**
+ * Initializes a new UcxAVLTree with the specified allocator.
+ *
+ * The cmpfunc should be capable of comparing two keys within this AVL tree.
+ * So if you want to use null terminated strings as keys, you could use the
+ * ucx_cmp_str() function here.
+ *
+ * @param cmpfunc the compare function that shall be used
+ * @param allocator the UcxAllocator that shall be used
+ * @return a new UcxAVLTree object
+ */
+UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator);
+
+/**
+ * Destroys a UcxAVLTree.
+ *
+ * Note, that the contents are not automatically freed.
+ * Use may use #ucx_avl_free_content() before calling this function.
+ *
+ * @param tree the tree to destroy
+ * @see ucx_avl_free_content()
+ */
+void ucx_avl_free(UcxAVLTree *tree);
+
+/**
+ * Frees the contents of a UcxAVLTree.
+ *
+ * This is a convenience function that iterates over the tree and passes all
+ * values to the specified destructor function.
+ *
+ * If no destructor is specified (<code>NULL</code>), the free() function of
+ * the tree's own allocator is used.
+ *
+ * You must ensure, that it is valid to pass each value in the map to the same
+ * destructor function.
+ *
+ * You should free the entire tree afterwards, as the contents will be invalid.
+ *
+ * @param tree for which the contents shall be freed
+ * @param destr optional pointer to a destructor function
+ * @see ucx_avl_free()
+ */
+void ucx_avl_free_content(UcxAVLTree *tree, ucx_destructor destr);
+
+/**
+ * Macro for initializing a new UcxAVLTree with the default allocator and a
+ * ucx_cmp_ptr() compare function.
+ *
+ * @return a new default UcxAVLTree object
+ */
+#define ucx_avl_default_new() \
+ ucx_avl_new_a(ucx_cmp_ptr, ucx_default_allocator())
+
+/**
+ * Gets the node from the tree, that is associated with the specified key.
+ * @param tree the UcxAVLTree
+ * @param key the key
+ * @return the node (or <code>NULL</code>, if the key is not present)
+ */
+UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key);
+
+/**
+ * Gets the value from the tree, that is associated with the specified key.
+ * @param tree the UcxAVLTree
+ * @param key the key
+ * @return the value (or <code>NULL</code>, if the key is not present)
+ */
+void *ucx_avl_get(UcxAVLTree *tree, intptr_t key);
+
+/**
+ * A mode for #ucx_avl_find_node() with the same behavior as
+ * #ucx_avl_get_node().
+ */
+#define UCX_AVL_FIND_EXACT 0
+/**
+ * A mode for #ucx_avl_find_node() finding the node whose key is at least
+ * as large as the specified key.
+ */
+#define UCX_AVL_FIND_LOWER_BOUNDED 1
+/**
+ * A mode for #ucx_avl_find_node() finding the node whose key is at most
+ * as large as the specified key.
+ */
+#define UCX_AVL_FIND_UPPER_BOUNDED 2
+/**
+ * A mode for #ucx_avl_find_node() finding the node with a key that is as close
+ * to the specified key as possible. If the key is present, the behavior is
+ * like #ucx_avl_get_node(). This mode only returns <code>NULL</code> on
+ * empty trees.
+ */
+#define UCX_AVL_FIND_CLOSEST 3
+
+/**
+ * Finds a node within the tree. The following modes are supported:
+ * <ul>
+ * <li>#UCX_AVL_FIND_EXACT: the same behavior as #ucx_avl_get_node()</li>
+ * <li>#UCX_AVL_FIND_LOWER_BOUNDED: finds the node whose key is at least
+ * as large as the specified key</li>
+ * <li>#UCX_AVL_FIND_UPPER_BOUNDED: finds the node whose key is at most
+ * as large as the specified key</li>
+ * <li>#UCX_AVL_FIND_CLOSEST: finds the node with a key that is as close to
+ * the specified key as possible. If the key is present, the behavior is
+ * like #ucx_avl_get_node(). This mode only returns <code>NULL</code> on
+ * empty trees.</li>
+ * </ul>
+ *
+ * The distance function provided MUST agree with the compare function of
+ * the AVL tree.
+ *
+ * @param tree the UcxAVLTree
+ * @param key the key
+ * @param dfnc the distance function
+ * @param mode the find mode
+ * @return the node (or <code>NULL</code>, if no node can be found)
+ */
+UcxAVLNode *ucx_avl_find_node(UcxAVLTree *tree, intptr_t key,
+ distance_func dfnc, int mode);
+
+/**
+ * Finds a value within the tree.
+ * See #ucx_avl_find_node() for details.
+ *
+ * @param tree the UcxAVLTree
+ * @param key the key
+ * @param dfnc the distance function
+ * @param mode the find mode
+ * @return the value (or <code>NULL</code>, if no value can be found)
+ */
+void *ucx_avl_find(UcxAVLTree *tree, intptr_t key,
+ distance_func dfnc, int mode);
+
+/**
+ * Puts a key/value pair into the tree.
+ *
+ * Attention: use this function only, if a possible old value does not need
+ * to be preserved.
+ *
+ * @param tree the UcxAVLTree
+ * @param key the key
+ * @param value the new value
+ * @return zero, if and only if the operation succeeded
+ */
+int ucx_avl_put(UcxAVLTree *tree, intptr_t key, void *value);
+
+/**
+ * Puts a key/value pair into the tree.
+ *
+ * This is a secure function which saves the old value to the variable pointed
+ * at by oldvalue.
+ *
+ * @param tree the UcxAVLTree
+ * @param key the key
+ * @param value the new value
+ * @param oldvalue optional: a pointer to the location where a possible old
+ * value shall be stored
+ * @return zero, if and only if the operation succeeded
+ */
+int ucx_avl_put_s(UcxAVLTree *tree, intptr_t key, void *value, void **oldvalue);
+
+/**
+ * Removes a node from the AVL tree.
+ *
+ * Note: the specified node is logically removed. The tree implementation
+ * decides which memory area is freed. In most cases the here provided node
+ * is freed, so its further use is generally undefined.
+ *
+ * @param tree the UcxAVLTree
+ * @param node the node to remove
+ * @return zero, if and only if an element has been removed
+ */
+int ucx_avl_remove_node(UcxAVLTree *tree, UcxAVLNode *node);
+
+/**
+ * Removes an element from the AVL tree.
+ *
+ * @param tree the UcxAVLTree
+ * @param key the key
+ * @return zero, if and only if an element has been removed
+ */
+int ucx_avl_remove(UcxAVLTree *tree, intptr_t key);
+
+/**
+ * Removes an element from the AVL tree.
+ *
+ * This is a secure function which saves the old key and value data from node
+ * to the variables at the location of oldkey and oldvalue (if specified), so
+ * they can be freed afterwards (if necessary).
+ *
+ * Note: the returned key in oldkey is possibly not the same as the provided
+ * key for the lookup (in terms of memory location).
+ *
+ * @param tree the UcxAVLTree
+ * @param key the key of the element to remove
+ * @param oldkey optional: a pointer to the location where the old key shall be
+ * stored
+ * @param oldvalue optional: a pointer to the location where the old value
+ * shall be stored
+ * @return zero, if and only if an element has been removed
+ */
+int ucx_avl_remove_s(UcxAVLTree *tree, intptr_t key,
+ intptr_t *oldkey, void **oldvalue);
+
+/**
+ * Counts the nodes in the specified UcxAVLTree.
+ * @param tree the AVL tree
+ * @return the node count
+ */
+size_t ucx_avl_count(UcxAVLTree *tree);
+
+/**
+ * Finds the in-order predecessor of the given node.
+ * @param node an AVL node
+ * @return the in-order predecessor of the given node, or <code>NULL</code> if
+ * the given node is the in-order minimum
+ */
+UcxAVLNode* ucx_avl_pred(UcxAVLNode* node);
+
+/**
+ * Finds the in-order successor of the given node.
+ * @param node an AVL node
+ * @return the in-order successor of the given node, or <code>NULL</code> if
+ * the given node is the in-order maximum
+ */
+UcxAVLNode* ucx_avl_succ(UcxAVLNode* node);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_AVL_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file buffer.h
+ *
+ * Advanced buffer implementation.
+ *
+ * Instances of UcxBuffer can be used to read from or to write to like one
+ * would do with a stream. This allows the use of ucx_stream_copy() to copy
+ * contents from one buffer to another.
+ *
+ * Some features for convenient use of the buffer
+ * can be enabled. See the documentation of the macro constants for more
+ * information.
+ *
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_BUFFER_H
+#define UCX_BUFFER_H
+
+#include "ucx.h"
+#include <sys/types.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * No buffer features enabled (all flags cleared).
+ */
+#define UCX_BUFFER_DEFAULT 0x00
+
+/**
+ * If this flag is enabled, the buffer will automatically free its contents.
+ */
+#define UCX_BUFFER_AUTOFREE 0x01
+
+/**
+ * If this flag is enabled, the buffer will automatically extends its capacity.
+ */
+#define UCX_BUFFER_AUTOEXTEND 0x02
+
+/** UCX Buffer. */
+typedef struct {
+ /** A pointer to the buffer contents. */
+ char *space;
+ /** Current position of the buffer. */
+ size_t pos;
+ /** Current capacity (i.e. maximum size) of the buffer. */
+ size_t capacity;
+ /** Current size of the buffer content. */
+ size_t size;
+ /**
+ * Flag register for buffer features.
+ * @see #UCX_BUFFER_DEFAULT
+ * @see #UCX_BUFFER_AUTOFREE
+ * @see #UCX_BUFFER_AUTOEXTEND
+ */
+ int flags;
+} UcxBuffer;
+
+/**
+ * Creates a new buffer.
+ *
+ * <b>Note:</b> you may provide <code>NULL</code> as argument for
+ * <code>space</code>. Then this function will allocate the space and enforce
+ * the #UCX_BUFFER_AUTOFREE flag.
+ *
+ * @param space pointer to the memory area, or <code>NULL</code> to allocate
+ * new memory
+ * @param capacity the capacity of the buffer
+ * @param flags buffer features (see UcxBuffer.flags)
+ * @return the new buffer
+ */
+UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags);
+
+/**
+ * Destroys a buffer.
+ *
+ * If the #UCX_BUFFER_AUTOFREE feature is enabled, the contents of the buffer
+ * are also freed.
+ *
+ * @param buffer the buffer to destroy
+ */
+void ucx_buffer_free(UcxBuffer* buffer);
+
+/**
+ * Creates a new buffer and fills it with extracted content from another buffer.
+ *
+ * <b>Note:</b> the #UCX_BUFFER_AUTOFREE feature is enforced for the new buffer.
+ *
+ * @param src the source buffer
+ * @param start the start position of extraction
+ * @param length the count of bytes to extract (must not be zero)
+ * @param flags feature mask for the new buffer
+ * @return a new buffer containing the extraction
+ */
+UcxBuffer* ucx_buffer_extract(UcxBuffer *src,
+ size_t start, size_t length, int flags);
+
+/**
+ * A shorthand macro for the full extraction of the buffer.
+ *
+ * @param src the source buffer
+ * @param flags feature mask for the new buffer
+ * @return a new buffer with the extracted content
+ */
+#define ucx_buffer_clone(src,flags) \
+ ucx_buffer_extract(src, 0, (src)->capacity, flags)
+
+
+/**
+ * Shifts the contents of the buffer by the given offset.
+ *
+ * If the offset is positive, the contents are shifted to the right.
+ * If auto extension is enabled, the buffer grows, if necessary.
+ * In case the auto extension fails, this function returns a non-zero value and
+ * no contents are changed.
+ * If auto extension is disabled, the contents that do not fit into the buffer
+ * are discarded.
+ *
+ * If the offset is negative, the contents are shifted to the left where the
+ * first <code>shift</code> bytes are discarded.
+ * The new size of the buffer is the old size minus
+ * the absolute shift value.
+ * If this value is larger than the buffer size, the buffer is emptied (but
+ * not cleared, see the security note below).
+ *
+ * The buffer position gets shifted alongside with the content but is kept
+ * within the boundaries of the buffer.
+ *
+ * <b>Security note:</b> the shifting operation does <em>not</em> erase the
+ * previously occupied memory cells. You can easily do that manually, e.g. by
+ * calling <code>memset(buffer->space, 0, shift)</code> for a right shift or
+ * <code>memset(buffer->size, 0, buffer->capacity-buffer->size)</code>
+ * for a left shift.
+ *
+ * @param buffer the buffer
+ * @param shift the shift offset (negative means left shift)
+ * @return 0 on success, non-zero if a required auto-extension fails
+ */
+int ucx_buffer_shift(UcxBuffer* buffer, off_t shift);
+
+/**
+ * Shifts the buffer to the right.
+ * See ucx_buffer_shift() for details.
+ *
+ * @param buffer the buffer
+ * @param shift the shift offset
+ * @return 0 on success, non-zero if a required auto-extension fails
+ * @see ucx_buffer_shift()
+ */
+int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift);
+
+/**
+ * Shifts the buffer to the left.
+ *
+ * See ucx_buffer_shift() for details. Note, however, that this method expects
+ * a positive shift offset.
+ *
+ * Since a left shift cannot fail due to memory allocation problems, this
+ * function always returns zero.
+ *
+ * @param buffer the buffer
+ * @param shift the shift offset
+ * @return always zero
+ * @see ucx_buffer_shift()
+ */
+int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift);
+
+
+/**
+ * Moves the position of the buffer.
+ *
+ * The new position is relative to the <code>whence</code> argument.
+ *
+ * SEEK_SET marks the start of the buffer.
+ * SEEK_CUR marks the current position.
+ * SEEK_END marks the end of the buffer.
+ *
+ * With an offset of zero, this function sets the buffer position to zero
+ * (SEEK_SET), the buffer size (SEEK_END) or leaves the buffer position
+ * unchanged (SEEK_CUR).
+ *
+ * @param buffer
+ * @param offset position offset relative to <code>whence</code>
+ * @param whence one of SEEK_SET, SEEK_CUR or SEEK_END
+ * @return 0 on success, non-zero if the position is invalid
+ *
+ */
+int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence);
+
+/**
+ * Clears the buffer by resetting the position and deleting the data.
+ *
+ * The data is deleted by a zeroing it with call to <code>memset()</code>.
+ *
+ * @param buffer the buffer to be cleared
+ */
+#define ucx_buffer_clear(buffer) memset((buffer)->space, 0, (buffer)->size); \
+ (buffer)->size = 0; (buffer)->pos = 0;
+
+/**
+ * Tests, if the buffer position has exceeded the buffer capacity.
+ *
+ * @param buffer the buffer to test
+ * @return non-zero, if the current buffer position has exceeded the last
+ * available byte of the buffer.
+ */
+int ucx_buffer_eof(UcxBuffer *buffer);
+
+
+/**
+ * Extends the capacity of the buffer.
+ *
+ * <b>Note:</b> The buffer capacity increased by a power of two. I.e.
+ * the buffer capacity is doubled, as long as it would not hold the current
+ * content plus the additional required bytes.
+ *
+ * <b>Attention:</b> the argument provided is the number of <i>additional</i>
+ * bytes the buffer shall hold. It is <b>NOT</b> the total number of bytes the
+ * buffer shall hold.
+ *
+ * @param buffer the buffer to extend
+ * @param additional_bytes the number of additional bytes the buffer shall
+ * <i>at least</i> hold
+ * @return 0 on success or a non-zero value on failure
+ */
+int ucx_buffer_extend(UcxBuffer *buffer, size_t additional_bytes);
+
+/**
+ * Writes data to a UcxBuffer.
+ *
+ * The position of the buffer is increased by the number of bytes written.
+ *
+ * @param ptr a pointer to the memory area containing the bytes to be written
+ * @param size the length of one element
+ * @param nitems the element count
+ * @param buffer the UcxBuffer to write to
+ * @return the total count of bytes written
+ */
+size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems,
+ UcxBuffer *buffer);
+
+/**
+ * Reads data from a UcxBuffer.
+ *
+ * The position of the buffer is increased by the number of bytes read.
+ *
+ * @param ptr a pointer to the memory area where to store the read data
+ * @param size the length of one element
+ * @param nitems the element count
+ * @param buffer the UcxBuffer to read from
+ * @return the total number of elements read
+ */
+size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems,
+ UcxBuffer *buffer);
+
+/**
+ * Writes a character to a buffer.
+ *
+ * The least significant byte of the argument is written to the buffer. If the
+ * end of the buffer is reached and #UCX_BUFFER_AUTOEXTEND feature is enabled,
+ * the buffer capacity is extended by ucx_buffer_extend(). If the feature is
+ * disabled or buffer extension fails, <code>EOF</code> is returned.
+ *
+ * On successful write the position of the buffer is increased.
+ *
+ * @param buffer the buffer to write to
+ * @param c the character to write as <code>int</code> value
+ * @return the byte that has bean written as <code>int</code> value or
+ * <code>EOF</code> when the end of the stream is reached and automatic
+ * extension is not enabled or not possible
+ */
+int ucx_buffer_putc(UcxBuffer *buffer, int c);
+
+/**
+ * Gets a character from a buffer.
+ *
+ * The current position of the buffer is increased after a successful read.
+ *
+ * @param buffer the buffer to read from
+ * @return the character as <code>int</code> value or <code>EOF</code>, if the
+ * end of the buffer is reached
+ */
+int ucx_buffer_getc(UcxBuffer *buffer);
+
+/**
+ * Writes a string to a buffer.
+ *
+ * @param buffer the buffer
+ * @param str the string
+ * @return the number of bytes written
+ */
+size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str);
+
+/**
+ * Returns the complete buffer content as sstr_t.
+ * @param buffer the buffer
+ * @return the result of <code>sstrn()</code> with the buffer space and size
+ * as arguments
+ */
+#define ucx_buffer_to_sstr(buffer) sstrn((buffer)->space, (buffer)->size)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_BUFFER_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Doubly linked list implementation.
+ *
+ * @file list.h
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_LIST_H
+#define UCX_LIST_H
+
+#include "ucx.h"
+#include "allocator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Loop statement for UCX lists.
+ *
+ * The first argument is the name of the iteration variable. The scope of
+ * this variable is limited to the <code>UCX_FOREACH</code> statement.
+ *
+ * The second argument is a pointer to the list. In most cases this will be the
+ * pointer to the first element of the list, but it may also be an arbitrary
+ * element of the list. The iteration will then start with that element.
+ *
+ * @param list The first element of the list
+ * @param elem The variable name of the element
+ */
+#define UCX_FOREACH(elem,list) \
+ for (UcxList* elem = (UcxList*) list ; elem != NULL ; elem = elem->next)
+
+/**
+ * UCX list type.
+ * @see UcxList
+ */
+typedef struct UcxList UcxList;
+
+/**
+ * UCX list structure.
+ */
+struct UcxList {
+ /**
+ * List element payload.
+ */
+ void *data;
+ /**
+ * Pointer to the next list element or <code>NULL</code>, if this is the
+ * last element.
+ */
+ UcxList *next;
+ /**
+ * Pointer to the previous list element or <code>NULL</code>, if this is
+ * the first element.
+ */
+ UcxList *prev;
+};
+
+/**
+ * Creates an element-wise copy of a list.
+ *
+ * This function clones the specified list by creating new list elements and
+ * copying the data with the specified copy_func(). If no copy_func() is
+ * specified, a shallow copy is created and the new list will reference the
+ * same data as the source list.
+ *
+ * @param list the list to copy
+ * @param cpyfnc a pointer to the function that shall copy an element (may be
+ * <code>NULL</code>)
+ * @param data additional data for the copy_func()
+ * @return a pointer to the copy
+ */
+UcxList *ucx_list_clone(const UcxList *list, copy_func cpyfnc, void* data);
+
+/**
+ * Creates an element-wise copy of a list using a UcxAllocator.
+ *
+ * See ucx_list_clone() for details.
+ *
+ * You might want to pass the allocator via the <code>data</code> parameter,
+ * to access it within the copy function for making deep copies.
+ *
+ * @param allocator the allocator to use
+ * @param list the list to copy
+ * @param cpyfnc a pointer to the function that shall copy an element (may be
+ * <code>NULL</code>)
+ * @param data additional data for the copy_func()
+ * @return a pointer to the copy
+ * @see ucx_list_clone()
+ */
+UcxList *ucx_list_clone_a(UcxAllocator *allocator, const UcxList *list,
+ copy_func cpyfnc, void* data);
+
+/**
+ * Compares two UCX lists element-wise by using a compare function.
+ *
+ * Each element of the two specified lists are compared by using the specified
+ * compare function and the additional data. The type and content of this
+ * additional data depends on the cmp_func() used.
+ *
+ * If the list pointers denote elements within a list, the lists are compared
+ * starting with the denoted elements. Thus any previous elements are not taken
+ * into account. This might be useful to check, if certain list tails match
+ * each other.
+ *
+ * @param list1 the first list
+ * @param list2 the second list
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return 1, if and only if the two lists equal element-wise, 0 otherwise
+ */
+int ucx_list_equals(const UcxList *list1, const UcxList *list2,
+ cmp_func cmpfnc, void* data);
+
+/**
+ * Destroys the entire list.
+ *
+ * The members of the list are not automatically freed, so ensure they are
+ * otherwise referenced or destroyed by ucx_list_free_contents().
+ * Otherwise, a memory leak is likely to occur.
+ *
+ * <b>Caution:</b> the argument <b>MUST</b> denote an entire list (i.e. a call
+ * to ucx_list_first() on the argument must return the argument itself)
+ *
+ * @param list the list to free
+ * @see ucx_list_free_contents()
+ */
+void ucx_list_free(UcxList *list);
+
+/**
+ * Destroys the entire list using a UcxAllocator.
+ *
+ * See ucx_list_free() for details.
+ *
+ * @param allocator the allocator to use
+ * @param list the list to free
+ * @see ucx_list_free()
+ */
+void ucx_list_free_a(UcxAllocator *allocator, UcxList *list);
+
+/**
+ * Destroys the contents of the specified list by calling the specified
+ * destructor on each of them.
+ *
+ * Note, that the contents are not usable afterwards and the list should be
+ * destroyed with ucx_list_free().
+ *
+ * If no destructor is specified (<code>NULL</code>), stdlib's free() is used.
+ *
+ * @param list the list for which the contents shall be freed
+ * @param destr optional destructor function
+ * @see ucx_list_free()
+ */
+void ucx_list_free_content(UcxList* list, ucx_destructor destr);
+
+
+/**
+ * Inserts an element at the end of the list.
+ *
+ * This is generally an O(n) operation, as the end of the list is retrieved with
+ * ucx_list_last().
+ *
+ * @param list the list where to append the data, or <code>NULL</code> to
+ * create a new list
+ * @param data the data to insert
+ * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to
+ * the newly created list otherwise
+ */
+UcxList *ucx_list_append(UcxList *list, void *data);
+
+/**
+ * Inserts an element at the end of the list using a UcxAllocator.
+ *
+ * See ucx_list_append() for details.
+ *
+ * @param allocator the allocator to use
+ * @param list the list where to append the data, or <code>NULL</code> to
+ * create a new list
+ * @param data the data to insert
+ * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to
+ * the newly created list otherwise
+ * @see ucx_list_append()
+ */
+UcxList *ucx_list_append_a(UcxAllocator *allocator, UcxList *list, void *data);
+
+
+/**
+ * Inserts an element at the beginning of the list.
+ *
+ * You <i>should</i> overwrite the old list pointer by calling
+ * <code>mylist = ucx_list_prepend(mylist, mydata);</code>. However, you may
+ * also perform successive calls of ucx_list_prepend() on the same list pointer,
+ * as this function always searchs for the head of the list with
+ * ucx_list_first().
+ *
+ * @param list the list where to insert the data or <code>NULL</code> to create
+ * a new list
+ * @param data the data to insert
+ * @return a pointer to the new list head
+ */
+UcxList *ucx_list_prepend(UcxList *list, void *data);
+
+/**
+ * Inserts an element at the beginning of the list using a UcxAllocator.
+ *
+ * See ucx_list_prepend() for details.
+ *
+ * @param allocator the allocator to use
+ * @param list the list where to insert the data or <code>NULL</code> to create
+ * a new list
+ * @param data the data to insert
+ * @return a pointer to the new list head
+ * @see ucx_list_prepend()
+ */
+UcxList *ucx_list_prepend_a(UcxAllocator *allocator, UcxList *list, void *data);
+
+/**
+ * Concatenates two lists.
+ *
+ * Either of the two arguments may be <code>NULL</code>.
+ *
+ * This function modifies the references to the next/previous element of
+ * the last/first element of <code>list1</code>/<code>
+ * list2</code>.
+ *
+ * @param list1 first list
+ * @param list2 second list
+ * @return if <code>list1</code> is <code>NULL</code>, <code>list2</code> is
+ * returned, otherwise <code>list1</code> is returned
+ */
+UcxList *ucx_list_concat(UcxList *list1, UcxList *list2);
+
+/**
+ * Returns the first element of a list.
+ *
+ * If the argument is the list pointer, it is directly returned. Otherwise
+ * this function traverses to the first element of the list and returns the
+ * list pointer.
+ *
+ * @param elem one element of the list
+ * @return the first element of the list, the specified element is a member of
+ */
+UcxList *ucx_list_first(const UcxList *elem);
+
+/**
+ * Returns the last element of a list.
+ *
+ * If the argument has no successor, it is the last element and therefore
+ * directly returned. Otherwise this function traverses to the last element of
+ * the list and returns it.
+ *
+ * @param elem one element of the list
+ * @return the last element of the list, the specified element is a member of
+ */
+UcxList *ucx_list_last(const UcxList *elem);
+
+/**
+ * Returns the list element at the specified index.
+ *
+ * @param list the list to retrieve the element from
+ * @param index index of the element to return
+ * @return the element at the specified index or <code>NULL</code>, if the
+ * index is greater than the list size
+ */
+UcxList *ucx_list_get(const UcxList *list, size_t index);
+
+/**
+ * Returns the index of an element.
+ *
+ * @param list the list where to search for the element
+ * @param elem the element to find
+ * @return the index of the element or -1 if the list does not contain the
+ * element
+ */
+ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem);
+
+/**
+ * Returns the element count of the list.
+ *
+ * @param list the list whose elements are counted
+ * @return the element count
+ */
+size_t ucx_list_size(const UcxList *list);
+
+/**
+ * Returns the index of an element containing the specified data.
+ *
+ * This function uses a cmp_func() to compare the data of each list element
+ * with the specified data. If no cmp_func is provided, the pointers are
+ * compared.
+ *
+ * If the list contains the data more than once, the index of the first
+ * occurrence is returned.
+ *
+ * @param list the list where to search for the data
+ * @param elem the element data
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return the index of the element containing the specified data or -1 if the
+ * data is not found in this list
+ */
+ssize_t ucx_list_find(const UcxList *list, void *elem,
+ cmp_func cmpfnc, void *data);
+
+/**
+ * Checks, if a list contains a specific element.
+ *
+ * An element is found, if ucx_list_find() returns a value greater than -1.
+ *
+ * @param list the list where to search for the data
+ * @param elem the element data
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return 1, if and only if the list contains the specified element data
+ * @see ucx_list_find()
+ */
+int ucx_list_contains(const UcxList *list, void *elem,
+ cmp_func cmpfnc, void *data);
+
+/**
+ * Sorts a UcxList with natural merge sort.
+ *
+ * This function uses O(n) additional temporary memory for merge operations
+ * that is automatically freed after each merge.
+ *
+ * As the head of the list might change, you <b>MUST</b> call this function
+ * as follows: <code>mylist = ucx_list_sort(mylist, mycmpfnc, mydata);</code>.
+ *
+ * @param list the list to sort
+ * @param cmpfnc the function that shall be used to compare the element data
+ * @param data additional data for the cmp_func()
+ * @return the sorted list
+ */
+UcxList *ucx_list_sort(UcxList *list, cmp_func cmpfnc, void *data);
+
+/**
+ * Removes an element from the list.
+ *
+ * If the first element is removed, the list pointer changes. So it is
+ * <i>highly recommended</i> to <i>always</i> update the pointer by calling
+ * <code>mylist = ucx_list_remove(mylist, myelem);</code>.
+ *
+ * @param list the list from which the element shall be removed
+ * @param element the element to remove
+ * @return returns the updated list pointer or <code>NULL</code>, if the list
+ * is now empty
+ */
+UcxList *ucx_list_remove(UcxList *list, UcxList *element);
+
+/**
+ * Removes an element from the list using a UcxAllocator.
+ *
+ * See ucx_list_remove() for details.
+ *
+ * @param allocator the allocator to use
+ * @param list the list from which the element shall be removed
+ * @param element the element to remove
+ * @return returns the updated list pointer or <code>NULL</code>, if the list
+ * @see ucx_list_remove()
+ */
+UcxList *ucx_list_remove_a(UcxAllocator *allocator, UcxList *list,
+ UcxList *element);
+
+/**
+ * Returns the union of two lists.
+ *
+ * The union is a list of unique elements regarding cmpfnc obtained from
+ * both source lists.
+ *
+ * @param left the left source list
+ * @param right the right source list
+ * @param cmpfnc a function to compare elements
+ * @param cmpdata additional data for the compare function
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the union
+ */
+UcxList* ucx_list_union(const UcxList *left, const UcxList *right,
+ cmp_func cmpfnc, void* cmpdata,
+ copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the union of two lists.
+ *
+ * The union is a list of unique elements regarding cmpfnc obtained from
+ * both source lists.
+ *
+ * @param allocator allocates the new list elements
+ * @param left the left source list
+ * @param right the right source list
+ * @param cmpfnc a function to compare elements
+ * @param cmpdata additional data for the compare function
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the union
+ */
+UcxList* ucx_list_union_a(UcxAllocator *allocator,
+ const UcxList *left, const UcxList *right,
+ cmp_func cmpfnc, void* cmpdata,
+ copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the intersection of two lists.
+ *
+ * The intersection contains all elements of the left list
+ * (including duplicates) that can be found in the right list.
+ *
+ * @param left the left source list
+ * @param right the right source list
+ * @param cmpfnc a function to compare elements
+ * @param cmpdata additional data for the compare function
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the intersection
+ */
+UcxList* ucx_list_intersection(const UcxList *left, const UcxList *right,
+ cmp_func cmpfnc, void* cmpdata,
+ copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the intersection of two lists.
+ *
+ * The intersection contains all elements of the left list
+ * (including duplicates) that can be found in the right list.
+ *
+ * @param allocator allocates the new list elements
+ * @param left the left source list
+ * @param right the right source list
+ * @param cmpfnc a function to compare elements
+ * @param cmpdata additional data for the compare function
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the intersection
+ */
+UcxList* ucx_list_intersection_a(UcxAllocator *allocator,
+ const UcxList *left, const UcxList *right,
+ cmp_func cmpfnc, void* cmpdata,
+ copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the difference of two lists.
+ *
+ * The difference contains all elements of the left list
+ * (including duplicates) that are not equal to any element of the right list.
+ *
+ * @param left the left source list
+ * @param right the right source list
+ * @param cmpfnc a function to compare elements
+ * @param cmpdata additional data for the compare function
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the difference
+ */
+UcxList* ucx_list_difference(const UcxList *left, const UcxList *right,
+ cmp_func cmpfnc, void* cmpdata,
+ copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the difference of two lists.
+ *
+ * The difference contains all elements of the left list
+ * (including duplicates) that are not equal to any element of the right list.
+ *
+ * @param allocator allocates the new list elements
+ * @param left the left source list
+ * @param right the right source list
+ * @param cmpfnc a function to compare elements
+ * @param cmpdata additional data for the compare function
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the difference
+ */
+UcxList* ucx_list_difference_a(UcxAllocator *allocator,
+ const UcxList *left, const UcxList *right,
+ cmp_func cmpfnc, void* cmpdata,
+ copy_func cpfnc, void* cpdata);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_LIST_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Logging API.
+ *
+ * @file logging.h
+ * @author Mike Becker, Olaf Wintermann
+ */
+#ifndef UCX_LOGGING_H
+#define UCX_LOGGING_H
+
+#include "ucx.h"
+#include "map.h"
+#include "string.h"
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* leave enough space for custom log levels */
+
+/** Log level for error messages. */
+#define UCX_LOGGER_ERROR 0x00
+
+/** Log level for warning messages. */
+#define UCX_LOGGER_WARN 0x10
+
+/** Log level for information messages. */
+#define UCX_LOGGER_INFO 0x20
+
+/** Log level for debug messages. */
+#define UCX_LOGGER_DEBUG 0x30
+
+/** Log level for trace messages. */
+#define UCX_LOGGER_TRACE 0x40
+
+/**
+ * Output flag for the log level.
+ * If this flag is set, the log message will contain the log level.
+ * @see UcxLogger.mask
+ */
+#define UCX_LOGGER_LEVEL 0x01
+
+/**
+ * Output flag for the timestmap.
+ * If this flag is set, the log message will contain the timestmap.
+ * @see UcxLogger.mask
+ */
+#define UCX_LOGGER_TIMESTAMP 0x02
+
+/**
+ * Output flag for the source.
+ * If this flag is set, the log message will contain the source file and line
+ * number.
+ * @see UcxLogger.mask
+ */
+#define UCX_LOGGER_SOURCE 0x04
+
+/**
+ * The UCX Logger object.
+ */
+typedef struct {
+ /** The stream this logger writes its messages to.*/
+ void *stream;
+
+ /**
+ * The write function that shall be used.
+ * For standard file or stdout loggers this might be standard fwrite
+ * (default).
+ */
+ write_func writer;
+
+ /**
+ * The date format for timestamp outputs including the delimiter
+ * (default: <code>"%F %T %z "</code>).
+ * @see UCX_LOGGER_TIMESTAMP
+ */
+ char *dateformat;
+
+ /**
+ * The level, this logger operates on.
+ * If a log command is issued, the message will only be logged, if the log
+ * level of the message is less or equal than the log level of the logger.
+ */
+ unsigned int level;
+
+ /**
+ * A configuration mask for automatic output.
+ * For each flag that is set, the logger automatically outputs some extra
+ * information like the timestamp or the source file and line number.
+ * See the documentation for the flags for details.
+ */
+ unsigned int mask;
+
+ /**
+ * A map of valid log levels for this logger.
+ *
+ * The keys represent all valid log levels and the values provide string
+ * representations, that are used, if the UCX_LOGGER_LEVEL flag is set.
+ *
+ * The exact data types are <code>unsigned int</code> for the key and
+ * <code>const char*</code> for the value.
+ *
+ * @see UCX_LOGGER_LEVEL
+ */
+ UcxMap* levels;
+} UcxLogger;
+
+/**
+ * Creates a new logger.
+ * @param stream the stream, which the logger shall write to
+ * @param level the level on which the logger shall operate
+ * @param mask configuration mask (cf. UcxLogger.mask)
+ * @return a new logger object
+ */
+UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask);
+
+/**
+ * Destroys the logger.
+ *
+ * The map containing the valid log levels is also automatically destroyed.
+ *
+ * @param logger the logger to destroy
+ */
+void ucx_logger_free(UcxLogger* logger);
+
+/**
+ * Internal log function - use macros instead.
+ *
+ * This function uses the <code>format</code> and variadic arguments for a
+ * printf()-style output of the log message.
+ *
+ * Dependent on the UcxLogger.mask some information is prepended. The complete
+ * format is:
+ *
+ * <code>[LEVEL] [TIMESTAMP] [SOURCEFILE]:[LINENO] message</code>
+ *
+ * The source file name is reduced to the actual file name. This is necessary to
+ * get consistent behavior over different definitions of the __FILE__ macro.
+ *
+ * <b>Attention:</b> the message (including automatically generated information)
+ * is limited to 4096 characters. The level description is limited to
+ * 256 characters and the timestamp string is limited to 128 characters.
+ *
+ * @param logger the logger to use
+ * @param level the level to log on
+ * @param file information about the source file
+ * @param line information about the source line number
+ * @param format format string
+ * @param ... arguments
+ * @see ucx_logger_log()
+ */
+void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file,
+ const unsigned int line, const char* format, ...);
+
+/**
+ * Registers a custom log level.
+ * @param logger the logger
+ * @param level the log level as unsigned integer
+ * @param name a string literal describing the level
+ */
+#define ucx_logger_register_level(logger, level, name) {\
+ unsigned int l; \
+ l = level; \
+ ucx_map_int_put(logger->levels, l, (void*) "[" name "]"); \
+ } while (0);
+
+/**
+ * Logs a message at the specified level.
+ * @param logger the logger to use
+ * @param level the level to log the message on
+ * @param ... format string and arguments
+ * @see ucx_logger_logf()
+ */
+#define ucx_logger_log(logger, level, ...) \
+ ucx_logger_logf(logger, level, __FILE__, __LINE__, __VA_ARGS__)
+
+/**
+ * Shortcut for logging an error message.
+ * @param logger the logger to use
+ * @param ... format string and arguments
+ * @see ucx_logger_logf()
+ */
+#define ucx_logger_error(logger, ...) \
+ ucx_logger_log(logger, UCX_LOGGER_ERROR, __VA_ARGS__)
+
+/**
+ * Shortcut for logging an information message.
+ * @param logger the logger to use
+ * @param ... format string and arguments
+ * @see ucx_logger_logf()
+ */
+#define ucx_logger_info(logger, ...) \
+ ucx_logger_log(logger, UCX_LOGGER_INFO, __VA_ARGS__)
+
+/**
+ * Shortcut for logging a warning message.
+ * @param logger the logger to use
+ * @param ... format string and arguments
+ * @see ucx_logger_logf()
+ */
+#define ucx_logger_warn(logger, ...) \
+ ucx_logger_log(logger, UCX_LOGGER_WARN, __VA_ARGS__)
+
+/**
+ * Shortcut for logging a debug message.
+ * @param logger the logger to use
+ * @param ... format string and arguments
+ * @see ucx_logger_logf()
+ */
+#define ucx_logger_debug(logger, ...) \
+ ucx_logger_log(logger, UCX_LOGGER_DEBUG, __VA_ARGS__)
+
+/**
+ * Shortcut for logging a trace message.
+ * @param logger the logger to use
+ * @param ... format string and arguments
+ * @see ucx_logger_logf()
+ */
+#define ucx_logger_trace(logger, ...) \
+ ucx_logger_log(logger, UCX_LOGGER_TRACE, __VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_LOGGING_H */
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file map.h
+ *
+ * Hash map implementation.
+ *
+ * This implementation uses murmur hash 2 and separate chaining with linked
+ * lists.
+ *
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_MAP_H
+#define UCX_MAP_H
+
+#include "ucx.h"
+#include "string.h"
+#include "allocator.h"
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Loop statement for UCX maps.
+ *
+ * The <code>key</code> variable is implicitly defined, but the
+ * <code>value</code> variable must be already declared as type information
+ * cannot be inferred.
+ *
+ * @param key the variable name for the key
+ * @param value the variable name for the value
+ * @param iter a UcxMapIterator
+ * @see ucx_map_iterator()
+ */
+#define UCX_MAP_FOREACH(key,value,iter) \
+ for(UcxKey key;ucx_map_iter_next(&iter,&key, (void**)&value);)
+
+/** Type for the UCX map. @see UcxMap */
+typedef struct UcxMap UcxMap;
+
+/** Type for a key of a UcxMap. @see UcxKey */
+typedef struct UcxKey UcxKey;
+
+/** Type for an element of a UcxMap. @see UcxMapElement */
+typedef struct UcxMapElement UcxMapElement;
+
+/** Type for an iterator over a UcxMap. @see UcxMapIterator */
+typedef struct UcxMapIterator UcxMapIterator;
+
+/** Structure for the UCX map. */
+struct UcxMap {
+ /** An allocator that is used for the map elements. */
+ UcxAllocator *allocator;
+ /** The array of map element lists. */
+ UcxMapElement **map;
+ /** The size of the map is the length of the element list array. */
+ size_t size;
+ /** The count of elements currently stored in this map. */
+ size_t count;
+};
+
+/** Structure to publicly denote a key of a UcxMap. */
+struct UcxKey {
+ /** The key data. */
+ const void *data;
+ /** The length of the key data. */
+ size_t len;
+ /** A cache for the hash value of the key data. */
+ int hash;
+};
+
+/** Internal structure for a key of a UcxMap. */
+struct UcxMapKey {
+ /** The key data. */
+ void *data;
+ /** The length of the key data. */
+ size_t len;
+ /** The hash value of the key data. */
+ int hash;
+};
+
+/** Structure for an element of a UcxMap. */
+struct UcxMapElement {
+ /** The value data. */
+ void *data;
+
+ /** A pointer to the next element in the current list. */
+ UcxMapElement *next;
+
+ /** The corresponding key. */
+ struct UcxMapKey key;
+};
+
+/** Structure for an iterator over a UcxMap. */
+struct UcxMapIterator {
+ /** The map to iterate over. */
+ UcxMap const *map;
+
+ /** The current map element. */
+ UcxMapElement *cur;
+
+ /**
+ * The current index of the element list array.
+ * <b>Attention: </b> this is <b>NOT</b> the element index! Do <b>NOT</b>
+ * manually iterate over the map by increasing this index. Use
+ * ucx_map_iter_next().
+ * @see UcxMap.map*/
+ size_t index;
+};
+
+/**
+ * Creates a new hash map with the specified size.
+ * @param size the size of the hash map
+ * @return a pointer to the new hash map
+ */
+UcxMap *ucx_map_new(size_t size);
+
+/**
+ * Creates a new hash map with the specified size using a UcxAllocator.
+ * @param allocator the allocator to use
+ * @param size the size of the hash map
+ * @return a pointer to the new hash map
+ */
+UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size);
+
+/**
+ * Frees a hash map.
+ *
+ * <b>Note:</b> the contents are <b>not</b> freed, use ucx_map_free_content()
+ * before calling this function to achieve that.
+ *
+ * @param map the map to be freed
+ * @see ucx_map_free_content()
+ */
+void ucx_map_free(UcxMap *map);
+
+/**
+ * Frees the contents of a hash map.
+ *
+ * This is a convenience function that iterates over the map and passes all
+ * values to the specified destructor function.
+ *
+ * If no destructor is specified (<code>NULL</code>), the free() function of
+ * the map's own allocator is used.
+ *
+ * You must ensure, that it is valid to pass each value in the map to the same
+ * destructor function.
+ *
+ * You should free or clear the map afterwards, as the contents will be invalid.
+ *
+ * @param map for which the contents shall be freed
+ * @param destr optional pointer to a destructor function
+ * @see ucx_map_free()
+ * @see ucx_map_clear()
+ */
+void ucx_map_free_content(UcxMap *map, ucx_destructor destr);
+
+/**
+ * Clears a hash map.
+ *
+ * <b>Note:</b> the contents are <b>not</b> freed, use ucx_map_free_content()
+ * before calling this function to achieve that.
+ *
+ * @param map the map to be cleared
+ * @see ucx_map_free_content()
+ */
+void ucx_map_clear(UcxMap *map);
+
+
+/**
+ * Copies contents from a map to another map using a copy function.
+ *
+ * <b>Note:</b> The destination map does not need to be empty. However, if it
+ * contains data with keys that are also present in the source map, the contents
+ * are overwritten.
+ *
+ * @param from the source map
+ * @param to the destination map
+ * @param fnc the copy function or <code>NULL</code> if the pointer address
+ * shall be copied
+ * @param data additional data for the copy function
+ * @return 0 on success or a non-zero value on memory allocation errors
+ */
+int ucx_map_copy(UcxMap const *from, UcxMap *to, copy_func fnc, void *data);
+
+/**
+ * Clones the map and rehashes if necessary.
+ *
+ * <b>Note:</b> In contrast to ucx_map_rehash() the load factor is irrelevant.
+ * This function <i>always</i> ensures a new UcxMap.size of at least
+ * 2.5*UcxMap.count.
+ *
+ * @param map the map to clone
+ * @param fnc the copy function to use or <code>NULL</code> if the new and
+ * the old map shall share the data pointers
+ * @param data additional data for the copy function
+ * @return the cloned map
+ * @see ucx_map_copy()
+ */
+UcxMap *ucx_map_clone(UcxMap const *map, copy_func fnc, void *data);
+
+/**
+ * Clones the map and rehashes if necessary.
+ *
+ * <b>Note:</b> In contrast to ucx_map_rehash() the load factor is irrelevant.
+ * This function <i>always</i> ensures a new UcxMap.size of at least
+ * 2.5*UcxMap.count.
+ *
+ * @param allocator the allocator to use for the cloned map
+ * @param map the map to clone
+ * @param fnc the copy function to use or <code>NULL</code> if the new and
+ * the old map shall share the data pointers
+ * @param data additional data for the copy function
+ * @return the cloned map
+ * @see ucx_map_copy()
+ */
+UcxMap *ucx_map_clone_a(UcxAllocator *allocator,
+ UcxMap const *map, copy_func fnc, void *data);
+
+/**
+ * Increases size of the hash map, if necessary.
+ *
+ * The load value is 0.75*UcxMap.size. If the element count exceeds the load
+ * value, the map needs to be rehashed. Otherwise no action is performed and
+ * this function simply returns 0.
+ *
+ * The rehashing process ensures, that the UcxMap.size is at least
+ * 2.5*UcxMap.count. So there is enough room for additional elements without
+ * the need of another soon rehashing.
+ *
+ * You can use this function to dramatically increase access performance.
+ *
+ * @param map the map to rehash
+ * @return 1, if a memory allocation error occurred, 0 otherwise
+ */
+int ucx_map_rehash(UcxMap *map);
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+int ucx_map_put(UcxMap *map, UcxKey key, void *value);
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+void* ucx_map_get(UcxMap const *map, UcxKey key);
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the removed value
+ */
+void* ucx_map_remove(UcxMap *map, UcxKey key);
+
+/**
+ * Shorthand for putting data with a sstr_t key into the map.
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ * @see ucx_map_put()
+ */
+#define ucx_map_sstr_put(map, key, value) \
+ ucx_map_put(map, ucx_key(key.ptr, key.length), (void*)value)
+
+/**
+ * Shorthand for putting data with a C string key into the map.
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ * @see ucx_map_put()
+ */
+#define ucx_map_cstr_put(map, key, value) \
+ ucx_map_put(map, ucx_key(key, strlen(key)), (void*)value)
+
+/**
+ * Shorthand for putting data with an integer key into the map.
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ * @see ucx_map_put()
+ */
+#define ucx_map_int_put(map, key, value) \
+ ucx_map_put(map, ucx_key(&key, sizeof(key)), (void*)value)
+
+/**
+ * Shorthand for getting data from the map with a sstr_t key.
+ * @param map the map
+ * @param key the key
+ * @return the value
+ * @see ucx_map_get()
+ */
+#define ucx_map_sstr_get(map, key) \
+ ucx_map_get(map, ucx_key(key.ptr, key.length))
+
+/**
+ * Shorthand for getting data from the map with a C string key.
+ * @param map the map
+ * @param key the key
+ * @return the value
+ * @see ucx_map_get()
+ */
+#define ucx_map_cstr_get(map, key) \
+ ucx_map_get(map, ucx_key(key, strlen(key)))
+
+/**
+ * Shorthand for getting data from the map with an integer key.
+ * @param map the map
+ * @param key the key
+ * @return the value
+ * @see ucx_map_get()
+ */
+#define ucx_map_int_get(map, key) \
+ ucx_map_get(map, ucx_key(&key, sizeof(int)))
+
+/**
+ * Shorthand for removing data from the map with a sstr_t key.
+ * @param map the map
+ * @param key the key
+ * @return the removed value
+ * @see ucx_map_remove()
+ */
+#define ucx_map_sstr_remove(map, key) \
+ ucx_map_remove(map, ucx_key(key.ptr, key.length))
+
+/**
+ * Shorthand for removing data from the map with a C string key.
+ * @param map the map
+ * @param key the key
+ * @return the removed value
+ * @see ucx_map_remove()
+ */
+#define ucx_map_cstr_remove(map, key) \
+ ucx_map_remove(map, ucx_key(key, strlen(key)))
+
+/**
+ * Shorthand for removing data from the map with an integer key.
+ * @param map the map
+ * @param key the key
+ * @return the removed value
+ * @see ucx_map_remove()
+ */
+#define ucx_map_int_remove(map, key) \
+ ucx_map_remove(map, ucx_key(&key, sizeof(key)))
+
+/**
+ * Creates a UcxKey based on the given data.
+ *
+ * This function implicitly computes the hash.
+ *
+ * @param data the data for the key
+ * @param len the length of the data
+ * @return a UcxKey with implicitly computed hash
+ * @see ucx_hash()
+ */
+UcxKey ucx_key(const void *data, size_t len);
+
+/**
+ * Computes a murmur hash-2.
+ *
+ * @param data the data to hash
+ * @param len the length of the data
+ * @return the murmur hash-2 of the data
+ */
+int ucx_hash(const char *data, size_t len);
+
+/**
+ * Creates an iterator for a map.
+ *
+ * <b>Note:</b> A UcxMapIterator iterates over all elements in all element
+ * lists successively. Therefore the order highly depends on the key hashes and
+ * may vary under different map sizes. So generally you may <b>NOT</b> rely on
+ * the iteration order.
+ *
+ * <b>Note:</b> The iterator is <b>NOT</b> initialized. You need to call
+ * ucx_map_iter_next() at least once before accessing any information. However,
+ * it is not recommended to access the fields of a UcxMapIterator directly.
+ *
+ * @param map the map to create the iterator for
+ * @return an iterator initialized on the first element of the
+ * first element list
+ * @see ucx_map_iter_next()
+ */
+UcxMapIterator ucx_map_iterator(UcxMap const *map);
+
+/**
+ * Proceeds to the next element of the map (if any).
+ *
+ * Subsequent calls on the same iterator proceed to the next element and
+ * store the key/value-pair into the memory specified as arguments of this
+ * function.
+ *
+ * If no further elements are found, this function returns zero and leaves the
+ * last found key/value-pair in memory.
+ *
+ * @param iterator the iterator to use
+ * @param key a pointer to the memory where to store the key
+ * @param value a pointer to the memory where to store the value
+ * @return 1, if another element was found, 0 if all elements has been processed
+ * @see ucx_map_iterator()
+ */
+int ucx_map_iter_next(UcxMapIterator *iterator, UcxKey *key, void **value);
+
+/**
+ * Returns the union of two maps.
+ *
+ * The union is a fresh map which is filled by two successive calls of
+ * ucx_map_copy() on the two input maps.
+ *
+ * @param first the first source map
+ * @param second the second source map
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new map containing the union
+ */
+UcxMap* ucx_map_union(const UcxMap *first, const UcxMap *second,
+ copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the union of two maps.
+ *
+ * The union is a fresh map which is filled by two successive calls of
+ * ucx_map_copy() on the two input maps.
+ *
+ * @param allocator the allocator that shall be used by the new map
+ * @param first the first source map
+ * @param second the second source map
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new map containing the union
+ */
+UcxMap* ucx_map_union_a(UcxAllocator *allocator,
+ const UcxMap *first, const UcxMap *second,
+ copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the intersection of two maps.
+ *
+ * The intersection is defined as a copy of the first map with every element
+ * removed that has no valid key in the second map.
+ *
+ * @param first the first source map
+ * @param second the second source map
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new map containing the intersection
+ */
+UcxMap* ucx_map_intersection(const UcxMap *first, const UcxMap *second,
+ copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the intersection of two maps.
+ *
+ * The intersection is defined as a copy of the first map with every element
+ * removed that has no valid key in the second map.
+ *
+ * @param allocator the allocator that shall be used by the new map
+ * @param first the first source map
+ * @param second the second source map
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new map containing the intersection
+ */
+UcxMap* ucx_map_intersection_a(UcxAllocator *allocator,
+ const UcxMap *first, const UcxMap *second,
+ copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the difference of two maps.
+ *
+ * The difference contains a copy of all elements of the first map
+ * for which the corresponding keys cannot be found in the second map.
+ *
+ * @param first the first source map
+ * @param second the second source map
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the difference
+ */
+UcxMap* ucx_map_difference(const UcxMap *first, const UcxMap *second,
+ copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the difference of two maps.
+ *
+ * The difference contains a copy of all elements of the first map
+ * for which the corresponding keys cannot be found in the second map.
+ *
+ * @param allocator the allocator that shall be used by the new map
+ * @param first the first source map
+ * @param second the second source map
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the difference
+ */
+UcxMap* ucx_map_difference_a(UcxAllocator *allocator,
+ const UcxMap *first, const UcxMap *second,
+ copy_func cpfnc, void* cpdata);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_MAP_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file mempool.h
+ *
+ * Memory pool implementation.
+ *
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_MEMPOOL_H
+#define UCX_MEMPOOL_H
+
+#include "ucx.h"
+#include "allocator.h"
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * UCX mempool structure.
+ */
+typedef struct {
+ /** UcxAllocator based on this pool */
+ UcxAllocator *allocator;
+
+ /** List of pointers to pooled memory. */
+ void **data;
+
+ /** Count of pooled memory items. */
+ size_t ndata;
+
+ /** Memory pool size. */
+ size_t size;
+} UcxMempool;
+
+/** Shorthand for a new default memory pool with a capacity of 16 elements. */
+#define ucx_mempool_new_default() ucx_mempool_new(16)
+
+
+/**
+ * Creates a memory pool with the specified initial size.
+ *
+ * As the created memory pool automatically grows in size by factor two when
+ * trying to allocate memory on a full pool, it is recommended that you use
+ * a power of two for the initial size.
+ *
+ * @param n initial pool size (should be a power of two, e.g. 16)
+ * @return a pointer to the new memory pool
+ * @see ucx_mempool_new_default()
+ */
+UcxMempool *ucx_mempool_new(size_t n);
+
+/**
+ * Resizes a memory pool.
+ *
+ * This function will fail if the new capacity is not sufficient for the
+ * present data.
+ *
+ * @param pool the pool to resize
+ * @param newcap the new capacity
+ * @return zero on success or non-zero on failure
+ */
+int ucx_mempool_chcap(UcxMempool *pool, size_t newcap);
+
+/**
+ * Allocates pooled memory.
+ *
+ * @param pool the memory pool
+ * @param n amount of memory to allocate
+ * @return a pointer to the allocated memory
+ * @see ucx_allocator_malloc()
+ */
+void *ucx_mempool_malloc(UcxMempool *pool, size_t n);
+/**
+ * Allocates a pooled memory array.
+ *
+ * The content of the allocated memory is set to zero.
+ *
+ * @param pool the memory pool
+ * @param nelem amount of elements to allocate
+ * @param elsize amount of memory per element
+ * @return a pointer to the allocated memory
+ * @see ucx_allocator_calloc()
+ */
+void *ucx_mempool_calloc(UcxMempool *pool, size_t nelem, size_t elsize);
+
+/**
+ * Reallocates pooled memory.
+ *
+ * If the memory to be reallocated is not contained by the specified pool, the
+ * behavior is undefined.
+ *
+ * @param pool the memory pool
+ * @param ptr a pointer to the memory that shall be reallocated
+ * @param n the new size of the memory
+ * @return a pointer to the new location of the memory
+ * @see ucx_allocator_realloc()
+ */
+void *ucx_mempool_realloc(UcxMempool *pool, void *ptr, size_t n);
+
+/**
+ * Frees pooled memory.
+ *
+ * Before freeing the memory, the specified destructor function (if any)
+ * is called.
+ *
+ * If you specify memory, that is not pooled by the specified memory pool, the
+ * program will terminate with a call to <code>abort()</code>.
+ *
+ * @param pool the memory pool
+ * @param ptr a pointer to the memory that shall be freed
+ * @see ucx_mempool_set_destr()
+ */
+void ucx_mempool_free(UcxMempool *pool, void *ptr);
+
+/**
+ * Destroys a memory pool.
+ *
+ * For each element the destructor function (if any) is called and the element
+ * is freed.
+ *
+ * Each of the registered destructor function that has no corresponding element
+ * within the pool (namely those registered by ucx_mempool_reg_destr) is
+ * called interleaving with the element destruction, but with guarantee to the
+ * order in which they were registered (FIFO order).
+ *
+ *
+ * @param pool the mempool to destroy
+ */
+void ucx_mempool_destroy(UcxMempool *pool);
+
+/**
+ * Sets a destructor function for the specified memory.
+ *
+ * The destructor is automatically called when the memory is freed or the
+ * pool is destroyed.
+ * A destructor for pooled memory <b>MUST NOT</b> free the memory itself,
+ * as this is done by the pool. Use a destructor to free any resources
+ * managed by the pooled object.
+ *
+ * The only requirement for the specified memory is, that it <b>MUST</b> be
+ * pooled memory by a UcxMempool or an element-compatible mempool. The pointer
+ * to the destructor function is saved in a reserved area before the actual
+ * memory.
+ *
+ * @param ptr pooled memory
+ * @param func a pointer to the destructor function
+ * @see ucx_mempool_free()
+ * @see ucx_mempool_destroy()
+ */
+void ucx_mempool_set_destr(void *ptr, ucx_destructor func);
+
+/**
+ * Registers a destructor function for the specified (non-pooled) memory.
+ *
+ * This is useful, if you have memory that has not been allocated by a mempool,
+ * but shall be managed by a mempool.
+ *
+ * This function creates an entry in the specified mempool and the memory will
+ * therefore (logically) convert to pooled memory.
+ * <b>However, this does not cause the memory to be freed automatically!</b>.
+ * If you want to use this function, make the memory pool free non-pooled
+ * memory, the specified destructor function must call <code>free()</code>
+ * by itself. But keep in mind, that you then MUST NOT use this destructor
+ * function with pooled memory (e.g. in ucx_mempool_set_destr()), as it
+ * would cause a double-free.
+ *
+ * @param pool the memory pool
+ * @param ptr data the destructor is registered for
+ * @param destr a pointer to the destructor function
+ */
+void ucx_mempool_reg_destr(UcxMempool *pool, void *ptr, ucx_destructor destr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_MEMPOOL_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * @file properties.h
+ *
+ * Load / store utilities for properties files.
+ *
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_PROPERTIES_H
+#define UCX_PROPERTIES_H
+
+#include "ucx.h"
+#include "map.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * UcxProperties object for parsing properties data.
+ * Most of the fields are for internal use only. You may configure the
+ * properties parser, e.g. by changing the used delimiter or specifying
+ * up to three different characters that shall introduce comments.
+ */
+typedef struct {
+ /**
+ * Input buffer (don't set manually).
+ * Automatically set by calls to ucx_properties_fill().
+ */
+ char *buffer;
+
+ /**
+ * Length of the input buffer (don't set manually).
+ * Automatically set by calls to ucx_properties_fill().
+ */
+ size_t buflen;
+
+ /**
+ * Current buffer position (don't set manually).
+ * Used by ucx_properties_next().
+ */
+ size_t pos;
+
+ /**
+ * Internal temporary buffer (don't set manually).
+ * Used by ucx_properties_next().
+ */
+ char *tmp;
+
+ /**
+ * Internal temporary buffer length (don't set manually).
+ * Used by ucx_properties_next().
+ */
+ size_t tmplen;
+
+ /**
+ * Internal temporary buffer capacity (don't set manually).
+ * Used by ucx_properties_next().
+ */
+ size_t tmpcap;
+
+ /**
+ * Parser error code.
+ * This is always 0 on success and a nonzero value on syntax errors.
+ * The value is set by ucx_properties_next().
+ */
+ int error;
+
+ /**
+ * The delimiter that shall be used.
+ * This is '=' by default.
+ */
+ char delimiter;
+
+ /**
+ * The first comment character.
+ * This is '#' by default.
+ */
+ char comment1;
+
+ /**
+ * The second comment character.
+ * This is not set by default.
+ */
+ char comment2;
+
+ /**
+ * The third comment character.
+ * This is not set by default.
+ */
+ char comment3;
+} UcxProperties;
+
+
+/**
+ * Constructs a new UcxProperties object.
+ * @return a pointer to the new UcxProperties object
+ */
+UcxProperties *ucx_properties_new();
+
+/**
+ * Destroys a UcxProperties object.
+ * @param prop the UcxProperties object to destroy
+ */
+void ucx_properties_free(UcxProperties *prop);
+
+/**
+ * Sets the input buffer for the properties parser.
+ *
+ * After calling this function, you may parse the data by calling
+ * ucx_properties_next() until it returns 0. The function ucx_properties2map()
+ * is a convenience function that reads as much data as possible by using this
+ * function.
+ *
+ *
+ * @param prop the UcxProperties object
+ * @param buf a pointer to the new buffer
+ * @param len the payload length of the buffer
+ * @see ucx_properties_next()
+ * @see ucx_properties2map()
+ */
+void ucx_properties_fill(UcxProperties *prop, char *buf, size_t len);
+
+/**
+ * Retrieves the next key/value-pair.
+ *
+ * This function returns a nonzero value as long as there are key/value-pairs
+ * found. If no more key/value-pairs are found, you may refill the input buffer
+ * with ucx_properties_fill().
+ *
+ * <b>Attention:</b> the sstr_t.ptr pointers of the output parameters point to
+ * memory within the input buffer of the parser and will get invalid some time.
+ * If you want long term copies of the key/value-pairs, use sstrdup() after
+ * calling this function.
+ *
+ * @param prop the UcxProperties object
+ * @param name a pointer to the sstr_t that shall contain the property name
+ * @param value a pointer to the sstr_t that shall contain the property value
+ * @return Nonzero, if a key/value-pair was successfully retrieved
+ * @see ucx_properties_fill()
+ */
+int ucx_properties_next(UcxProperties *prop, sstr_t *name, sstr_t *value);
+
+/**
+ * Retrieves all available key/value-pairs and puts them into a UcxMap.
+ *
+ * This is done by successive calls to ucx_properties_next() until no more
+ * key/value-pairs can be retrieved.
+ *
+ * The memory for the map values is allocated by the map's own allocator.
+ *
+ * @param prop the UcxProperties object
+ * @param map the target map
+ * @return The UcxProperties.error code (i.e. 0 on success).
+ * @see ucx_properties_fill()
+ * @see UcxMap.allocator
+ */
+int ucx_properties2map(UcxProperties *prop, UcxMap *map);
+
+/**
+ * Loads a properties file to a UcxMap.
+ *
+ * This is a convenience function that reads data from an input
+ * stream until the end of the stream is reached.
+ *
+ * @param map the map object to write the key/value-pairs to
+ * @param file the <code>FILE*</code> stream to read from
+ * @return 0 on success, or a non-zero value on error
+ *
+ * @see ucx_properties_fill()
+ * @see ucx_properties2map()
+ */
+int ucx_properties_load(UcxMap *map, FILE *file);
+
+/**
+ * Stores a UcxMap to a file.
+ *
+ * The key/value-pairs are written by using the following format:
+ *
+ * <code>[key] = [value]\\n</code>
+ *
+ * @param map the map to store
+ * @param file the <code>FILE*</code> stream to write to
+ * @return 0 on success, or a non-zero value on error
+ */
+int ucx_properties_store(UcxMap *map, FILE *file);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_PROPERTIES_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file stack.h
+ *
+ * Default stack memory allocation implementation.
+ *
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_STACK_H
+#define UCX_STACK_H
+
+#include "ucx.h"
+#include "allocator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * UCX stack structure.
+ */
+typedef struct {
+ /** UcxAllocator based on this stack */
+ UcxAllocator allocator;
+
+ /** Stack size. */
+ size_t size;
+
+ /** Pointer to the bottom of the stack */
+ char *space;
+
+ /** Pointer to the top of the stack */
+ char *top;
+} UcxStack;
+
+/**
+ * Metadata for each UCX stack element.
+ */
+struct ucx_stack_metadata {
+ /**
+ * Location of the previous element (<code>NULL</code> if this is the first)
+ */
+ char *prev;
+
+ /** Size of this element */
+ size_t size;
+};
+
+/**
+ * Initializes UcxStack structure with memory.
+ *
+ * @param stack a pointer to an uninitialized stack structure
+ * @param space the memory area that shall be managed
+ * @param size size of the memory area
+ * @return a new UcxStack structure
+ */
+void ucx_stack_init(UcxStack *stack, char* space, size_t size);
+
+/**
+ * Allocates stack memory.
+ *
+ * @param stack a pointer to the stack
+ * @param n amount of memory to allocate
+ * @return a pointer to the allocated memory or <code>NULL</code> on stack
+ * overflow
+ * @see ucx_allocator_malloc()
+ */
+void *ucx_stack_malloc(UcxStack *stack, size_t n);
+
+/**
+ * Allocates memory with #ucx_stack_malloc() and copies the specified data if
+ * the allocation was successful.
+ *
+ * @param stack a pointer to the stack
+ * @param n amount of memory to allocate
+ * @param data a pointer to the data to copy
+ * @return a pointer to the allocated memory
+ * @see ucx_stack_malloc
+ */
+void *ucx_stack_push(UcxStack *stack, size_t n, const void *data);
+
+/**
+ * Allocates an array of stack memory
+ *
+ * The content of the allocated memory is set to zero.
+ *
+ * @param stack a pointer to the stack
+ * @param nelem amount of elements to allocate
+ * @param elsize amount of memory per element
+ * @return a pointer to the allocated memory
+ * @see ucx_allocator_calloc()
+ */
+void *ucx_stack_calloc(UcxStack *stack, size_t nelem, size_t elsize);
+
+/**
+ * Allocates memory with #ucx_stack_calloc() and copies the specified data if
+ * the allocation was successful.
+ *
+ * @param stack a pointer to the stack
+ * @param nelem amount of elements to allocate
+ * @param elsize amount of memory per element
+ * @param data a pointer to the data
+ * @return a pointer to the allocated memory
+ * @see ucx_stack_calloc
+ */
+void *ucx_stack_pusharr(UcxStack *stack,
+ size_t nelem, size_t elsize, const void *data);
+
+/**
+ * Reallocates memory on the stack.
+ *
+ * Shrinking memory is always safe. Extending memory can be very expensive.
+ *
+ * @param stack the stack
+ * @param ptr a pointer to the memory that shall be reallocated
+ * @param n the new size of the memory
+ * @return a pointer to the new location of the memory
+ * @see ucx_allocator_realloc()
+ */
+void *ucx_stack_realloc(UcxStack *stack, void *ptr, size_t n);
+
+/**
+ * Frees memory on the stack.
+ *
+ * Freeing stack memory behaves in a special way.
+ *
+ * If the element, that should be freed, is the top most element of the stack,
+ * it is removed from the stack. Otherwise it is marked as freed. Marked
+ * elements are removed, when they become the top most elements of the stack.
+ *
+ * @param stack a pointer to the stack
+ * @param ptr a pointer to the memory that shall be freed
+ */
+void ucx_stack_free(UcxStack *stack, void *ptr);
+
+
+/**
+ * Returns the size of the top most element.
+ * @param stack a pointer to the stack
+ * @return the size of the top most element
+ */
+#define ucx_stack_topsize(stack) ((stack)->top ? ((struct ucx_stack_metadata*)\
+ (stack)->top - 1)->size : 0)
+
+/**
+ * Removes the top most element from the stack and copies the content to <code>
+ * dest</code>, if specified.
+ *
+ * Use #ucx_stack_topsize()# to get the amount of memory that must be available
+ * at the location of <code>dest</code>.
+ *
+ * @param stack a pointer to the stack
+ * @param dest the location where the contents shall be written to, or <code>
+ * NULL</code>, if the element shall only be removed.
+ * @see ucx_stack_free
+ * @see ucx_stack_popn
+ */
+#define ucx_stack_pop(stack, dest) ucx_stack_popn(stack, dest, (size_t)-1)
+
+/**
+ * Removes the top most element from the stack and copies the content to <code>
+ * dest</code>.
+ *
+ * This function copies at most <code>n</code> bytes to the destination, but
+ * the element is always freed as a whole.
+ * If the element was larger than <code>n</code>, the remaining data is lost.
+ *
+ * @param stack a pointer to the stack
+ * @param dest the location where the contents shall be written to
+ * @param n copies at most n bytes to <code>dest</code>
+ * @see ucx_stack_pop
+ */
+void ucx_stack_popn(UcxStack *stack, void *dest, size_t n);
+
+/**
+ * Returns the remaining available memory on the specified stack.
+ *
+ * @param stack a pointer to the stack
+ * @return the remaining available memory
+ */
+size_t ucx_stack_avail(UcxStack *stack);
+
+/**
+ * Checks, if the stack is empty.
+ *
+ * @param stack a pointer to the stack
+ * @return nonzero, if the stack is empty, zero otherwise
+ */
+#define ucx_stack_empty(stack) (!(stack)->top)
+
+/**
+ * Computes a recommended size for the stack memory area. Note, that
+ * reallocations have not been taken into account, so you might need to reserve
+ * twice as much memory to allow many reallocations.
+ *
+ * @param size the approximate payload
+ * @param elems the approximate count of element allocations
+ * @return a recommended size for the stack space based on the information
+ * provided
+ */
+#define ucx_stack_dim(size, elems) (size+sizeof(struct ucx_stack_metadata) * \
+ (elems + 1))
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_STACK_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Bounded string implementation.
+ *
+ * The UCX strings (<code>sstr_t</code>) provide an alternative to C strings.
+ * The main difference to C strings is, that <code>sstr_t</code> does <b>not
+ * need to be <code>NULL</code>-terminated</b>. Instead the length is stored
+ * within the structure.
+ *
+ * When using <code>sstr_t</code>, developers must be full aware of what type
+ * of string (<code>NULL</code>-terminated) or not) they are using, when
+ * accessing the <code>char* ptr</code> directly.
+ *
+ * The UCX string module provides some common string functions, known from
+ * standard libc, working with <code>sstr_t</code>.
+ *
+ * @file string.h
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_STRING_H
+#define UCX_STRING_H
+
+#include "ucx.h"
+#include "allocator.h"
+#include <stddef.h>
+
+/*
+ * Use this macro to disable the shortcuts if you experience macro collision.
+ */
+#ifndef UCX_NO_SSTR_SHORTCUTS
+/**
+ * Shortcut for a <code>sstr_t struct</code>
+ * or <code>scstr_t struct</code> literal.
+ */
+#define ST(s) { s, sizeof(s)-1 }
+
+/** Shortcut for the conversion of a C string to a <code>sstr_t</code>. */
+#define S(s) sstrn(s, sizeof(s)-1)
+
+/** Shortcut for the conversion of a C string to a <code>scstr_t</code>. */
+#define SC(s) scstrn(s, sizeof(s)-1)
+#endif /* UCX_NO_SSTR_SHORTCUTS */
+
+/*
+ * Use this macro to disable the format macros.
+ */
+#ifndef UCX_NO_SSTR_FORMAT_MACROS
+/** Expands a sstr_t or scstr_t to printf arguments. */
+#define SFMT(s) (int) (s).length, (s).ptr
+
+/** Format specifier for a sstr_t or scstr_t. */
+#define PRIsstr ".*s"
+#endif /* UCX_NO_SSTR_FORMAT_MACROS */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The UCX string structure.
+ */
+typedef struct {
+ /** A pointer to the string
+ * (<b>not necessarily <code>NULL</code>-terminated</b>) */
+ char *ptr;
+ /** The length of the string */
+ size_t length;
+} sstr_t;
+
+/**
+ * The UCX string structure for immutable (constant) strings.
+ */
+typedef struct {
+ /** A constant pointer to the immutable string
+ * (<b>not necessarily <code>NULL</code>-terminated</b>) */
+ const char *ptr;
+ /** The length of the string */
+ size_t length;
+} scstr_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#ifdef __cplusplus
+/**
+ * One of two type adjustment functions that return an scstr_t.
+ *
+ * Used <b>internally</b> to convert a UCX string to an immutable UCX string.
+ *
+ * <b>Do not use this function manually.</b>
+ *
+ * @param str some sstr_t
+ * @return an immutable (scstr_t) version of the provided string.
+ */
+inline scstr_t s2scstr(sstr_t s) {
+ scstr_t c;
+ c.ptr = s.ptr;
+ c.length = s.length;
+ return c;
+}
+
+/**
+ * One of two type adjustment functions that return an scstr_t.
+ *
+ * Used <b>internally</b> to convert a UCX string to an immutable UCX string.
+ * This variant is used, when the string is already immutable and no operation
+ * needs to be performed.
+ *
+ * <b>Do not use this function manually.</b>
+ *
+ * @param str some scstr_t
+ * @return the argument itself
+ */
+inline scstr_t s2scstr(scstr_t str) {
+ return str;
+}
+
+/**
+ * Converts a UCX string to an immutable UCX string (scstr_t).
+ * @param str some UCX string
+ * @return an immutable version of the provided string
+ */
+#define SCSTR(s) s2scstr(s)
+#else
+
+/**
+ * One of two type adjustment functions that return an scstr_t.
+ *
+ * Used <b>internally</b> to convert a UCX string to an immutable UCX string.
+ * This variant is used, when the string is already immutable and no operation
+ * needs to be performed.
+ *
+ * <b>Do not use this function manually.</b>
+ *
+ * @param str some scstr_t
+ * @return the argument itself
+ */
+scstr_t ucx_sc2sc(scstr_t str);
+
+/**
+ * One of two type adjustment functions that return an scstr_t.
+ *
+ * Used <b>internally</b> to convert a UCX string to an immutable UCX string.
+ *
+ * <b>Do not use this function manually.</b>
+ *
+ * @param str some sstr_t
+ * @return an immutable (scstr_t) version of the provided string.
+ */
+scstr_t ucx_ss2sc(sstr_t str);
+
+#if __STDC_VERSION__ >= 201112L
+/**
+ * Converts a UCX string to an immutable UCX string (scstr_t).
+ * @param str some UCX string
+ * @return an immutable version of the provided string
+ */
+#define SCSTR(str) _Generic(str, sstr_t: ucx_ss2sc, scstr_t: ucx_sc2sc)(str)
+
+#elif defined(__GNUC__) || defined(__clang__)
+
+/**
+ * Converts a UCX string to an immutable UCX string (scstr_t).
+ * @param str some UCX string
+ * @return an immutable version of the provided string
+ */
+#define SCSTR(str) __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(str), sstr_t), \
+ ucx_ss2sc, \
+ ucx_sc2sc)(str)
+
+#elif defined(__sun)
+
+/**
+ * Converts a UCX string to an immutable UCX string (scstr_t).
+ * @param str some UCX string
+ * @return the an immutable version of the provided string
+ */
+#define SCSTR(str) ({typeof(str) ucx_tmp_var_str = str; \
+ scstr_t ucx_tmp_var_c; \
+ ucx_tmp_var_c.ptr = ucx_tmp_var_str.ptr;\
+ ucx_tmp_var_c.length = ucx_tmp_var_str.length;\
+ ucx_tmp_var_c; })
+#else /* no generics and no builtins */
+
+/**
+ * Converts a UCX string to an immutable UCX string (scstr_t).
+ *
+ * This <b>internal</b> function (ab)uses the C standard an expects one single
+ * argument which is then implicitly converted to scstr_t without a warning.
+ *
+ * <b>Do not use this function manually.</b>
+ *
+ * @return the an immutable version of the provided string
+ */
+scstr_t ucx_ss2c_s();
+
+/**
+ * Converts a UCX string to an immutable UCX string (scstr_t).
+ * @param str some UCX string
+ * @return the an immutable version of the provided string
+ */
+#define SCSTR(str) ucx_ss2c_s(str)
+#endif /* C11 feature test */
+
+#endif /* C++ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * Creates a new sstr_t based on a C string.
+ *
+ * The length is implicitly inferred by using a call to <code>strlen()</code>.
+ *
+ * <b>Note:</b> the sstr_t will share the specified pointer to the C string.
+ * If you do want a copy, use sstrdup() on the return value of this function.
+ *
+ * If you need to wrap a constant string, use scstr().
+ *
+ * @param cstring the C string to wrap
+ * @return a new sstr_t containing the C string
+ *
+ * @see sstrn()
+ */
+sstr_t sstr(char *cstring);
+
+/**
+ * Creates a new sstr_t of the specified length based on a C string.
+ *
+ * <b>Note:</b> the sstr_t will share the specified pointer to the C string.
+ * If you do want a copy, use sstrdup() on the return value of this function.
+ *
+ * If you need to wrap a constant string, use scstrn().
+ *
+ * @param cstring the C string to wrap
+ * @param length the length of the string
+ * @return a new sstr_t containing the C string
+ *
+ * @see sstr()
+ * @see S()
+ */
+sstr_t sstrn(char *cstring, size_t length);
+
+/**
+ * Creates a new scstr_t based on a constant C string.
+ *
+ * The length is implicitly inferred by using a call to <code>strlen()</code>.
+ *
+ * <b>Note:</b> the scstr_t will share the specified pointer to the C string.
+ * If you do want a copy, use scstrdup() on the return value of this function.
+ *
+ * @param cstring the C string to wrap
+ * @return a new scstr_t containing the C string
+ *
+ * @see scstrn()
+ */
+scstr_t scstr(const char *cstring);
+
+
+/**
+ * Creates a new scstr_t of the specified length based on a constant C string.
+ *
+ * <b>Note:</b> the scstr_t will share the specified pointer to the C string.
+ * If you do want a copy, use scstrdup() on the return value of this function. *
+ *
+ * @param cstring the C string to wrap
+ * @param length the length of the string
+ * @return a new scstr_t containing the C string
+ *
+ * @see scstr()
+ */
+scstr_t scstrn(const char *cstring, size_t length);
+
+/**
+ * Returns the accumulated length of all specified strings.
+ *
+ * <b>Attention:</b> if the count argument is larger than the count of the
+ * specified strings, the behavior is undefined.
+ *
+ * @param count the total number of specified strings
+ * @param ... all strings
+ * @return the accumulated length of all strings
+ */
+size_t scstrnlen(size_t count, ...);
+
+/**
+ * Returns the accumulated length of all specified strings.
+ *
+ * <b>Attention:</b> if the count argument is larger than the count of the
+ * specified strings, the behavior is undefined.
+ *
+ * @param count the total number of specified strings
+ * @param ... all strings
+ * @return the cumulated length of all strings
+ */
+#define sstrnlen(count, ...) scstrnlen(count, __VA_ARGS__)
+
+/**
+ * Concatenates two or more strings.
+ *
+ * The resulting string will be allocated by standard <code>malloc()</code>.
+ * So developers <b>MUST</b> pass the sstr_t.ptr to <code>free()</code>.
+ *
+ * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
+ * terminated.
+ *
+ * @param count the total number of strings to concatenate
+ * @param s1 first string
+ * @param ... all remaining strings
+ * @return the concatenated string
+ */
+sstr_t scstrcat(size_t count, scstr_t s1, ...);
+
+/**
+ * Concatenates two or more strings.
+ *
+ * The resulting string will be allocated by standard <code>malloc()</code>.
+ * So developers <b>MUST</b> pass the sstr_t.ptr to <code>free()</code>.
+ *
+ * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
+ * terminated.
+ *
+ * @param count the total number of strings to concatenate
+ * @param s1 first string
+ * @param ... all remaining strings
+ * @return the concatenated string
+ */
+#define sstrcat(count, s1, ...) scstrcat(count, SCSTR(s1), __VA_ARGS__)
+
+/**
+ * Concatenates two or more strings using a UcxAllocator.
+ *
+ * The resulting string must be freed by the allocators <code>free()</code>
+ * implementation.
+ *
+ * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
+ * terminated.
+ *
+ * @param alloc the allocator to use
+ * @param count the total number of strings to concatenate
+ * @param s1 first string
+ * @param ... all remaining strings
+ * @return the concatenated string
+ *
+ * @see scstrcat()
+ */
+sstr_t scstrcat_a(UcxAllocator *alloc, size_t count, scstr_t s1, ...);
+
+/**
+ * Concatenates two or more strings using a UcxAllocator.
+ *
+ * The resulting string must be freed by the allocators <code>free()</code>
+ * implementation.
+ *
+ * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
+ * terminated.
+ *
+ * @param alloc the allocator to use
+ * @param count the total number of strings to concatenate
+ * @param s1 first string
+ * @param ... all remaining strings
+ * @return the concatenated string
+ *
+ * @see sstrcat()
+ */
+#define sstrcat_a(alloc, count, s1, ...) \
+ scstrcat_a(alloc, count, SCSTR(s1), __VA_ARGS__)
+
+/**
+ * Returns a substring starting at the specified location.
+ *
+ * <b>Attention:</b> the new string references the same memory area as the
+ * input string and is <b>NOT</b> required to be <code>NULL</code>-terminated.
+ * Use sstrdup() to get a copy.
+ *
+ * @param string input string
+ * @param start start location of the substring
+ * @return a substring of <code>string</code> starting at <code>start</code>
+ *
+ * @see sstrsubsl()
+ * @see sstrchr()
+ */
+sstr_t sstrsubs(sstr_t string, size_t start);
+
+/**
+ * Returns a substring with the given length starting at the specified location.
+ *
+ * <b>Attention:</b> the new string references the same memory area as the
+ * input string and is <b>NOT</b> required to be <code>NULL</code>-terminated.
+ * Use sstrdup() to get a copy.
+ *
+ * @param string input string
+ * @param start start location of the substring
+ * @param length the maximum length of the substring
+ * @return a substring of <code>string</code> starting at <code>start</code>
+ * with a maximum length of <code>length</code>
+ *
+ * @see sstrsubs()
+ * @see sstrchr()
+ */
+sstr_t sstrsubsl(sstr_t string, size_t start, size_t length);
+
+/**
+ * Returns a substring of an immutable string starting at the specified
+ * location.
+ *
+ * <b>Attention:</b> the new string references the same memory area as the
+* input string and is <b>NOT</b> required to be <code>NULL</code>-terminated.
+ * Use scstrdup() to get a copy.
+ *
+ * @param string input string
+ * @param start start location of the substring
+ * @return a substring of <code>string</code> starting at <code>start</code>
+ *
+ * @see scstrsubsl()
+ * @see scstrchr()
+ */
+scstr_t scstrsubs(scstr_t string, size_t start);
+
+/**
+ * Returns a substring of an immutable string with a maximum length starting
+ * at the specified location.
+ *
+ * <b>Attention:</b> the new string references the same memory area as the
+ * input string and is <b>NOT</b> required to be <code>NULL</code>-terminated.
+ * Use scstrdup() to get a copy.
+ *
+ * @param string input string
+ * @param start start location of the substring
+ * @param length the maximum length of the substring
+ * @return a substring of <code>string</code> starting at <code>start</code>
+ * with a maximum length of <code>length</code>
+ *
+ * @see scstrsubs()
+ * @see scstrchr()
+ */
+scstr_t scstrsubsl(scstr_t string, size_t start, size_t length);
+
+/**
+ * Returns a substring starting at the location of the first occurrence of the
+ * specified character.
+ *
+ * If the string does not contain the character, an empty string is returned.
+ *
+ * @param string the string where to locate the character
+ * @param chr the character to locate
+ * @return a substring starting at the first location of <code>chr</code>
+ *
+ * @see sstrsubs()
+ */
+sstr_t sstrchr(sstr_t string, int chr);
+
+/**
+ * Returns a substring starting at the location of the last occurrence of the
+ * specified character.
+ *
+ * If the string does not contain the character, an empty string is returned.
+ *
+ * @param string the string where to locate the character
+ * @param chr the character to locate
+ * @return a substring starting at the last location of <code>chr</code>
+ *
+ * @see sstrsubs()
+ */
+sstr_t sstrrchr(sstr_t string, int chr);
+
+/**
+ * Returns an immutable substring starting at the location of the first
+ * occurrence of the specified character.
+ *
+ * If the string does not contain the character, an empty string is returned.
+ *
+ * @param string the string where to locate the character
+ * @param chr the character to locate
+ * @return a substring starting at the first location of <code>chr</code>
+ *
+ * @see scstrsubs()
+ */
+scstr_t scstrchr(scstr_t string, int chr);
+
+/**
+ * Returns an immutable substring starting at the location of the last
+ * occurrence of the specified character.
+ *
+ * If the string does not contain the character, an empty string is returned.
+ *
+ * @param string the string where to locate the character
+ * @param chr the character to locate
+ * @return a substring starting at the last location of <code>chr</code>
+ *
+ * @see scstrsubs()
+ */
+scstr_t scstrrchr(scstr_t string, int chr);
+
+/**
+ * Returns a substring starting at the location of the first occurrence of the
+ * specified string.
+ *
+ * If the string does not contain the other string, an empty string is returned.
+ *
+ * If <code>match</code> is an empty string, the complete <code>string</code> is
+ * returned.
+ *
+ * @param string the string to be scanned
+ * @param match string containing the sequence of characters to match
+ * @return a substring starting at the first occurrence of
+ * <code>match</code>, or an empty string, if the sequence is not
+ * present in <code>string</code>
+ */
+sstr_t scstrsstr(sstr_t string, scstr_t match);
+
+/**
+ * Returns a substring starting at the location of the first occurrence of the
+ * specified string.
+ *
+ * If the string does not contain the other string, an empty string is returned.
+ *
+ * If <code>match</code> is an empty string, the complete <code>string</code> is
+ * returned.
+ *
+ * @param string the string to be scanned
+ * @param match string containing the sequence of characters to match
+ * @return a substring starting at the first occurrence of
+ * <code>match</code>, or an empty string, if the sequence is not
+ * present in <code>string</code>
+ */
+#define sstrstr(string, match) scstrsstr(string, SCSTR(match))
+
+/**
+ * Returns an immutable substring starting at the location of the
+ * first occurrence of the specified immutable string.
+ *
+ * If the string does not contain the other string, an empty string is returned.
+ *
+ * If <code>match</code> is an empty string, the complete <code>string</code> is
+ * returned.
+ *
+ * @param string the string to be scanned
+ * @param match string containing the sequence of characters to match
+ * @return a substring starting at the first occurrence of
+ * <code>match</code>, or an empty string, if the sequence is not
+ * present in <code>string</code>
+ */
+scstr_t scstrscstr(scstr_t string, scstr_t match);
+
+/**
+ * Returns an immutable substring starting at the location of the
+ * first occurrence of the specified immutable string.
+ *
+ * If the string does not contain the other string, an empty string is returned.
+ *
+ * If <code>match</code> is an empty string, the complete <code>string</code> is
+ * returned.
+ *
+ * @param string the string to be scanned
+ * @param match string containing the sequence of characters to match
+ * @return a substring starting at the first occurrence of
+ * <code>match</code>, or an empty string, if the sequence is not
+ * present in <code>string</code>
+ */
+#define sstrscstr(string, match) scstrscstr(string, SCSTR(match))
+
+/**
+ * Splits a string into parts by using a delimiter string.
+ *
+ * This function will return <code>NULL</code>, if one of the following happens:
+ * <ul>
+ * <li>the string length is zero</li>
+ * <li>the delimeter length is zero</li>
+ * <li>the string equals the delimeter</li>
+ * <li>memory allocation fails</li>
+ * </ul>
+ *
+ * The integer referenced by <code>count</code> is used as input and determines
+ * the maximum size of the resulting array, i.e. the maximum count of splits to
+ * perform + 1.
+ *
+ * The integer referenced by <code>count</code> is also used as output and is
+ * set to
+ * <ul>
+ * <li>-2, on memory allocation errors</li>
+ * <li>-1, if either the string or the delimiter is an empty string</li>
+ * <li>0, if the string equals the delimiter</li>
+ * <li>1, if the string does not contain the delimiter</li>
+ * <li>the count of array items, otherwise</li>
+ * </ul>
+ *
+ * If the string starts with the delimiter, the first item of the resulting
+ * array will be an empty string.
+ *
+ * If the string ends with the delimiter and the maximum list size is not
+ * exceeded, the last array item will be an empty string.
+ * In case the list size would be exceeded, the last array item will be the
+ * remaining string after the last split, <i>including</i> the terminating
+ * delimiter.
+ *
+ * <b>Attention:</b> The array pointer <b>AND</b> all sstr_t.ptr of the array
+ * items must be manually passed to <code>free()</code>. Use scstrsplit_a() with
+ * an allocator to managed memory, to avoid this.
+ *
+ * @param string the string to split
+ * @param delim the delimiter string
+ * @param count IN: the maximum size of the resulting array (0 = no limit),
+ * OUT: the actual size of the array
+ * @return a sstr_t array containing the split strings or
+ * <code>NULL</code> on error
+ *
+ * @see scstrsplit_a()
+ */
+sstr_t* scstrsplit(scstr_t string, scstr_t delim, ssize_t *count);
+
+/**
+ * Splits a string into parts by using a delimiter string.
+ *
+ * This function will return <code>NULL</code>, if one of the following happens:
+ * <ul>
+ * <li>the string length is zero</li>
+ * <li>the delimeter length is zero</li>
+ * <li>the string equals the delimeter</li>
+ * <li>memory allocation fails</li>
+ * </ul>
+ *
+ * The integer referenced by <code>count</code> is used as input and determines
+ * the maximum size of the resulting array, i.e. the maximum count of splits to
+ * perform + 1.
+ *
+ * The integer referenced by <code>count</code> is also used as output and is
+ * set to
+ * <ul>
+ * <li>-2, on memory allocation errors</li>
+ * <li>-1, if either the string or the delimiter is an empty string</li>
+ * <li>0, if the string equals the delimiter</li>
+ * <li>1, if the string does not contain the delimiter</li>
+ * <li>the count of array items, otherwise</li>
+ * </ul>
+ *
+ * If the string starts with the delimiter, the first item of the resulting
+ * array will be an empty string.
+ *
+ * If the string ends with the delimiter and the maximum list size is not
+ * exceeded, the last array item will be an empty string.
+ * In case the list size would be exceeded, the last array item will be the
+ * remaining string after the last split, <i>including</i> the terminating
+ * delimiter.
+ *
+ * <b>Attention:</b> The array pointer <b>AND</b> all sstr_t.ptr of the array
+ * items must be manually passed to <code>free()</code>. Use sstrsplit_a() with
+ * an allocator to managed memory, to avoid this.
+ *
+ * @param string the string to split
+ * @param delim the delimiter string
+ * @param count IN: the maximum size of the resulting array (0 = no limit),
+ * OUT: the actual size of the array
+ * @return a sstr_t array containing the split strings or
+ * <code>NULL</code> on error
+ *
+ * @see sstrsplit_a()
+ */
+#define sstrsplit(string, delim, count) \
+ scstrsplit(SCSTR(string), SCSTR(delim), count)
+
+/**
+ * Performing scstrsplit() using a UcxAllocator.
+ *
+ * <i>Read the description of scstrsplit() for details.</i>
+ *
+ * The memory for the sstr_t.ptr pointers of the array items and the memory for
+ * the sstr_t array itself are allocated by using the UcxAllocator.malloc()
+ * function.
+ *
+ * @param allocator the UcxAllocator used for allocating memory
+ * @param string the string to split
+ * @param delim the delimiter string
+ * @param count IN: the maximum size of the resulting array (0 = no limit),
+ * OUT: the actual size of the array
+ * @return a sstr_t array containing the split strings or
+ * <code>NULL</code> on error
+ *
+ * @see scstrsplit()
+ */
+sstr_t* scstrsplit_a(UcxAllocator *allocator, scstr_t string, scstr_t delim,
+ ssize_t *count);
+
+/**
+ * Performing sstrsplit() using a UcxAllocator.
+ *
+ * <i>Read the description of sstrsplit() for details.</i>
+ *
+ * The memory for the sstr_t.ptr pointers of the array items and the memory for
+ * the sstr_t array itself are allocated by using the UcxAllocator.malloc()
+ * function.
+ *
+ * @param allocator the UcxAllocator used for allocating memory
+ * @param string the string to split
+ * @param delim the delimiter string
+ * @param count IN: the maximum size of the resulting array (0 = no limit),
+ * OUT: the actual size of the array
+ * @return a sstr_t array containing the split strings or
+ * <code>NULL</code> on error
+ *
+ * @see sstrsplit()
+ */
+#define sstrsplit_a(allocator, string, delim, count) \
+ scstrsplit_a(allocator, SCSTR(string), SCSTR(delim), count)
+
+/**
+ * Compares two UCX strings with standard <code>memcmp()</code>.
+ *
+ * At first it compares the scstr_t.length attribute of the two strings. The
+ * <code>memcmp()</code> function is called, if and only if the lengths match.
+ *
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return -1, if the length of s1 is less than the length of s2 or 1, if the
+ * length of s1 is greater than the length of s2 or the result of
+ * <code>memcmp()</code> otherwise (i.e. 0 if the strings match)
+ */
+int scstrcmp(scstr_t s1, scstr_t s2);
+
+/**
+ * Compares two UCX strings with standard <code>memcmp()</code>.
+ *
+ * At first it compares the sstr_t.length attribute of the two strings. The
+ * <code>memcmp()</code> function is called, if and only if the lengths match.
+ *
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return -1, if the length of s1 is less than the length of s2 or 1, if the
+ * length of s1 is greater than the length of s2 or the result of
+ * <code>memcmp()</code> otherwise (i.e. 0 if the strings match)
+ */
+#define sstrcmp(s1, s2) scstrcmp(SCSTR(s1), SCSTR(s2))
+
+/**
+ * Compares two UCX strings ignoring the case.
+ *
+ * At first it compares the scstr_t.length attribute of the two strings. If and
+ * only if the lengths match, both strings are compared char by char ignoring
+ * the case.
+ *
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return -1, if the length of s1 is less than the length of s2 or 1, if the
+ * length of s1 is greater than the length of s2 or the result of the platform
+ * specific string comparison function ignoring the case.
+ */
+int scstrcasecmp(scstr_t s1, scstr_t s2);
+
+/**
+ * Compares two UCX strings ignoring the case.
+ *
+ * At first it compares the sstr_t.length attribute of the two strings. If and
+ * only if the lengths match, both strings are compared char by char ignoring
+ * the case.
+ *
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return -1, if the length of s1 is less than the length of s2 or 1, if the
+ * length of s1 is greater than the length of s2 or the result of the platform
+ * specific string comparison function ignoring the case.
+ */
+#define sstrcasecmp(s1, s2) scstrcasecmp(SCSTR(s1), SCSTR(s2))
+
+/**
+ * Creates a duplicate of the specified string.
+ *
+ * The new sstr_t will contain a copy allocated by standard
+ * <code>malloc()</code>. So developers <b>MUST</b> pass the sstr_t.ptr to
+ * <code>free()</code>.
+ *
+ * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
+ * terminated and mutable, regardless of the argument.
+ *
+ * @param string the string to duplicate
+ * @return a duplicate of the string
+ * @see scstrdup_a()
+ */
+sstr_t scstrdup(scstr_t string);
+
+/**
+ * Creates a duplicate of the specified string.
+ *
+ * The new sstr_t will contain a copy allocated by standard
+ * <code>malloc()</code>. So developers <b>MUST</b> pass the sstr_t.ptr to
+ * <code>free()</code>.
+ *
+ * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
+ * terminated, regardless of the argument.
+ *
+ * @param string the string to duplicate
+ * @return a duplicate of the string
+ * @see sstrdup_a()
+ */
+#define sstrdup(string) scstrdup(SCSTR(string))
+
+/**
+ * Creates a duplicate of the specified string using a UcxAllocator.
+ *
+ * The new sstr_t will contain a copy allocated by the allocators
+ * UcxAllocator.malloc() function. So it is implementation depended, whether the
+ * returned sstr_t.ptr pointer must be passed to the allocators
+ * UcxAllocator.free() function manually.
+ *
+ * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
+ * terminated and mutable, regardless of the argument.
+ *
+ * @param allocator a valid instance of a UcxAllocator
+ * @param string the string to duplicate
+ * @return a duplicate of the string
+ * @see scstrdup()
+ */
+sstr_t scstrdup_a(UcxAllocator *allocator, scstr_t string);
+
+/**
+ * Creates a duplicate of the specified string using a UcxAllocator.
+ *
+ * The new sstr_t will contain a copy allocated by the allocators
+ * UcxAllocator.malloc() function. So it is implementation depended, whether the
+ * returned sstr_t.ptr pointer must be passed to the allocators
+ * UcxAllocator.free() function manually.
+ *
+ * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
+ * terminated, regardless of the argument.
+ *
+ * @param allocator a valid instance of a UcxAllocator
+ * @param string the string to duplicate
+ * @return a duplicate of the string
+ * @see scstrdup()
+ */
+#define sstrdup_a(allocator, string) scstrdup_a(allocator, SCSTR(string))
+
+
+/**
+ * Omits leading and trailing spaces.
+ *
+ * This function returns a new sstr_t containing a trimmed version of the
+ * specified string.
+ *
+ * <b>Note:</b> the new sstr_t references the same memory, thus you
+ * <b>MUST NOT</b> pass the sstr_t.ptr of the return value to
+ * <code>free()</code>. It is also highly recommended to avoid assignments like
+ * <code>mystr = sstrtrim(mystr);</code> as you lose the reference to the
+ * source string. Assignments of this type are only permitted, if the
+ * sstr_t.ptr of the source string does not need to be freed or if another
+ * reference to the source string exists.
+ *
+ * @param string the string that shall be trimmed
+ * @return a new sstr_t containing the trimmed string
+ */
+sstr_t sstrtrim(sstr_t string);
+
+/**
+ * Omits leading and trailing spaces.
+ *
+ * This function returns a new scstr_t containing a trimmed version of the
+ * specified string.
+ *
+ * <b>Note:</b> the new scstr_t references the same memory, thus you
+ * <b>MUST NOT</b> pass the scstr_t.ptr of the return value to
+ * <code>free()</code>. It is also highly recommended to avoid assignments like
+ * <code>mystr = scstrtrim(mystr);</code> as you lose the reference to the
+ * source string. Assignments of this type are only permitted, if the
+ * scstr_t.ptr of the source string does not need to be freed or if another
+ * reference to the source string exists.
+ *
+ * @param string the string that shall be trimmed
+ * @return a new scstr_t containing the trimmed string
+ */
+scstr_t scstrtrim(scstr_t string);
+
+/**
+ * Checks, if a string has a specific prefix.
+ *
+ * @param string the string to check
+ * @param prefix the prefix the string should have
+ * @return 1, if and only if the string has the specified prefix, 0 otherwise
+ */
+int scstrprefix(scstr_t string, scstr_t prefix);
+
+/**
+ * Checks, if a string has a specific prefix.
+ *
+ * @param string the string to check
+ * @param prefix the prefix the string should have
+ * @return 1, if and only if the string has the specified prefix, 0 otherwise
+ */
+#define sstrprefix(string, prefix) scstrprefix(SCSTR(string), SCSTR(prefix))
+
+/**
+ * Checks, if a string has a specific suffix.
+ *
+ * @param string the string to check
+ * @param suffix the suffix the string should have
+ * @return 1, if and only if the string has the specified suffix, 0 otherwise
+ */
+int scstrsuffix(scstr_t string, scstr_t suffix);
+
+/**
+ * Checks, if a string has a specific suffix.
+ *
+ * @param string the string to check
+ * @param suffix the suffix the string should have
+ * @return 1, if and only if the string has the specified suffix, 0 otherwise
+ */
+#define sstrsuffix(string, suffix) scstrsuffix(SCSTR(string), SCSTR(suffix))
+
+/**
+ * Checks, if a string has a specific prefix, ignoring the case.
+ *
+ * @param string the string to check
+ * @param prefix the prefix the string should have
+ * @return 1, if and only if the string has the specified prefix, 0 otherwise
+ */
+int scstrcaseprefix(scstr_t string, scstr_t prefix);
+
+/**
+ * Checks, if a string has a specific prefix, ignoring the case.
+ *
+ * @param string the string to check
+ * @param prefix the prefix the string should have
+ * @return 1, if and only if the string has the specified prefix, 0 otherwise
+ */
+#define sstrcaseprefix(string, prefix) \
+ scstrcaseprefix(SCSTR(string), SCSTR(prefix))
+
+/**
+ * Checks, if a string has a specific suffix, ignoring the case.
+ *
+ * @param string the string to check
+ * @param suffix the suffix the string should have
+ * @return 1, if and only if the string has the specified suffix, 0 otherwise
+ */
+int scstrcasesuffix(scstr_t string, scstr_t suffix);
+
+/**
+ * Checks, if a string has a specific suffix, ignoring the case.
+ *
+ * @param string the string to check
+ * @param suffix the suffix the string should have
+ * @return 1, if and only if the string has the specified suffix, 0 otherwise
+ */
+#define sstrcasesuffix(string, suffix) \
+ scstrcasesuffix(SCSTR(string), SCSTR(suffix))
+
+/**
+ * Returns a lower case version of a string.
+ *
+ * This function creates a duplicate of the input string, first
+ * (see scstrdup()).
+ *
+ * @param string the input string
+ * @return the resulting lower case string
+ * @see scstrdup()
+ */
+sstr_t scstrlower(scstr_t string);
+
+/**
+ * Returns a lower case version of a string.
+ *
+ * This function creates a duplicate of the input string, first
+ * (see sstrdup()).
+ *
+ * @param string the input string
+ * @return the resulting lower case string
+ */
+#define sstrlower(string) scstrlower(SCSTR(string))
+
+/**
+ * Returns a lower case version of a string.
+ *
+ * This function creates a duplicate of the input string, first
+ * (see scstrdup_a()).
+ *
+ * @param allocator the allocator used for duplicating the string
+ * @param string the input string
+ * @return the resulting lower case string
+ * @see scstrdup_a()
+ */
+sstr_t scstrlower_a(UcxAllocator *allocator, scstr_t string);
+
+
+/**
+ * Returns a lower case version of a string.
+ *
+ * This function creates a duplicate of the input string, first
+ * (see sstrdup_a()).
+ *
+ * @param allocator the allocator used for duplicating the string
+ * @param string the input string
+ * @return the resulting lower case string
+ */
+#define sstrlower_a(allocator, string) scstrlower_a(allocator, SCSTR(string))
+
+/**
+ * Returns a upper case version of a string.
+ *
+ * This function creates a duplicate of the input string, first
+ * (see scstrdup()).
+ *
+ * @param string the input string
+ * @return the resulting upper case string
+ * @see scstrdup()
+ */
+sstr_t scstrupper(scstr_t string);
+
+/**
+ * Returns a upper case version of a string.
+ *
+ * This function creates a duplicate of the input string, first
+ * (see sstrdup()).
+ *
+ * @param string the input string
+ * @return the resulting upper case string
+ */
+#define sstrupper(string) scstrupper(SCSTR(string))
+
+/**
+ * Returns a upper case version of a string.
+ *
+ * This function creates a duplicate of the input string, first
+ * (see scstrdup_a()).
+ *
+ * @param allocator the allocator used for duplicating the string
+ * @param string the input string
+ * @return the resulting upper case string
+ * @see scstrdup_a()
+ */
+sstr_t scstrupper_a(UcxAllocator *allocator, scstr_t string);
+
+/**
+ * Returns a upper case version of a string.
+ *
+ * This function creates a duplicate of the input string, first
+ * (see sstrdup_a()).
+ *
+ * @param allocator the allocator used for duplicating the string
+ * @param string the input string
+ * @return the resulting upper case string
+ */
+#define sstrupper_a(allocator, string) scstrupper_a(allocator, string)
+
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most <code>replmax</code> occurrences.
+ *
+ * The resulting string is allocated by the specified allocator. I.e. it
+ * depends on the used allocator, whether the sstr_t.ptr must be freed
+ * manually.
+ *
+ * If allocation fails, the sstr_t.ptr of the return value is NULL.
+ *
+ * @param allocator the allocator to use
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @param replmax maximum number of replacements
+ * @return the resulting string after applying the replacements
+ */
+sstr_t scstrreplacen_a(UcxAllocator *allocator, scstr_t str,
+ scstr_t pattern, scstr_t replacement, size_t replmax);
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most <code>replmax</code> occurrences.
+ *
+ * The sstr_t.ptr of the resulting string must be freed manually.
+ *
+ * If allocation fails, the sstr_t.ptr of the return value is NULL.
+ *
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @param replmax maximum number of replacements
+ * @return the resulting string after applying the replacements
+ */
+sstr_t scstrreplacen(scstr_t str, scstr_t pattern,
+ scstr_t replacement, size_t replmax);
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most <code>replmax</code> occurrences.
+ *
+ * The resulting string is allocated by the specified allocator. I.e. it
+ * depends on the used allocator, whether the sstr_t.ptr must be freed
+ * manually.
+ *
+ * @param allocator the allocator to use
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @param replmax maximum number of replacements
+ * @return the resulting string after applying the replacements
+ */
+#define sstrreplacen_a(allocator, str, pattern, replacement, replmax) \
+ scstrreplacen_a(allocator, SCSTR(str), SCSTR(pattern), \
+ SCSTR(replacement), replmax)
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most <code>replmax</code> occurrences.
+ *
+ * The sstr_t.ptr of the resulting string must be freed manually.
+ *
+ * If allocation fails, the sstr_t.ptr of the return value is NULL.
+ *
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @param replmax maximum number of replacements
+ * @return the resulting string after applying the replacements
+ */
+#define sstrreplacen(str, pattern, replacement, replmax) \
+ scstrreplacen(SCSTR(str), SCSTR(pattern), SCSTR(replacement), replmax)
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most <code>replmax</code> occurrences.
+ *
+ * The resulting string is allocated by the specified allocator. I.e. it
+ * depends on the used allocator, whether the sstr_t.ptr must be freed
+ * manually.
+ *
+ * If allocation fails, the sstr_t.ptr of the return value is NULL.
+ *
+ * @param allocator the allocator to use
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @return the resulting string after applying the replacements
+ */
+#define sstrreplace_a(allocator, str, pattern, replacement) \
+ scstrreplacen_a(allocator, SCSTR(str), SCSTR(pattern), \
+ SCSTR(replacement), SIZE_MAX)
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most <code>replmax</code> occurrences.
+ *
+ * The sstr_t.ptr of the resulting string must be freed manually.
+ *
+ * If allocation fails, the sstr_t.ptr of the return value is NULL.
+ *
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @return the resulting string after applying the replacements
+ */
+#define sstrreplace(str, pattern, replacement) \
+ scstrreplacen(SCSTR(str), SCSTR(pattern), SCSTR(replacement), SIZE_MAX)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_STRING_H */
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file: test.h
+ *
+ * UCX Test Framework.
+ *
+ * Usage of this test framework:
+ *
+ * **** IN HEADER FILE: ****
+ *
+ * <pre>
+ * UCX_TEST(function_name);
+ * UCX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional
+ * </pre>
+ *
+ * **** IN SOURCE FILE: ****
+ * <pre>
+ * UCX_TEST_SUBROUTINE(subroutine_name, paramlist) {
+ * // tests with UCX_TEST_ASSERT()
+ * }
+ *
+ * UCX_TEST(function_name) {
+ * // memory allocation and other stuff here
+ * #UCX_TEST_BEGIN
+ * // tests with UCX_TEST_ASSERT() and/or
+ * // calls with UCX_TEST_CALL_SUBROUTINE() here
+ * #UCX_TEST_END
+ * // cleanup of memory here
+ * }
+ * </pre>
+ *
+ * <b>Note:</b> if a test fails, a longjump is performed
+ * back to the #UCX_TEST_BEGIN macro!
+ *
+ * <b>Attention:</b> Do not call own functions within a test, that use
+ * UCX_TEST_ASSERT() macros and are not defined by using UCX_TEST_SUBROUTINE().
+ *
+ *
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ *
+ */
+
+#ifndef UCX_TEST_H
+#define UCX_TEST_H
+
+#include "ucx.h"
+#include <stdio.h>
+#include <string.h>
+#include <setjmp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __FUNCTION__
+
+/**
+ * Alias for the <code>__func__</code> preprocessor macro.
+ * Some compilers use <code>__func__</code> and others use __FUNCTION__.
+ * We use __FUNCTION__ so we define it for those compilers which use
+ * <code>__func__</code>.
+ */
+#define __FUNCTION__ __func__
+#endif
+
+/** Type for the UcxTestSuite. */
+typedef struct UcxTestSuite UcxTestSuite;
+
+/** Pointer to a test function. */
+typedef void(*UcxTest)(UcxTestSuite*,FILE*);
+
+/** Type for the internal list of test cases. */
+typedef struct UcxTestList UcxTestList;
+
+/** Structure for the internal list of test cases. */
+struct UcxTestList {
+
+ /** Test case. */
+ UcxTest test;
+
+ /** Pointer to the next list element. */
+ UcxTestList *next;
+};
+
+/**
+ * A test suite containing multiple test cases.
+ */
+struct UcxTestSuite {
+
+ /** The number of successful tests after the suite has been run. */
+ unsigned int success;
+
+ /** The number of failed tests after the suite has been run. */
+ unsigned int failure;
+
+ /**
+ * Internal list of test cases.
+ * Use ucx_test_register() to add tests to this list.
+ */
+ UcxTestList *tests;
+};
+
+/**
+ * Creates a new test suite.
+ * @return a new test suite
+ */
+UcxTestSuite* ucx_test_suite_new();
+
+/**
+ * Destroys a test suite.
+ * @param suite the test suite to destroy
+ */
+void ucx_test_suite_free(UcxTestSuite* suite);
+
+/**
+ * Registers a test function with the specified test suite.
+ *
+ * @param suite the suite, the test function shall be added to
+ * @param test the test function to register
+ * @return <code>EXIT_SUCCESS</code> on success or
+ * <code>EXIT_FAILURE</code> on failure
+ */
+int ucx_test_register(UcxTestSuite* suite, UcxTest test);
+
+/**
+ * Runs a test suite and writes the test log to the specified stream.
+ * @param suite the test suite to run
+ * @param outstream the stream the log shall be written to
+ */
+void ucx_test_run(UcxTestSuite* suite, FILE* outstream);
+
+/**
+ * Macro for a #UcxTest function header.
+ *
+ * Use this macro to declare and/or define a #UcxTest function.
+ *
+ * @param name the name of the test function
+ */
+#define UCX_TEST(name) void name(UcxTestSuite* _suite_,FILE *_output_)
+
+/**
+ * Marks the begin of a test.
+ * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>after</b>
+ * #UCX_TEST_BEGIN.
+ *
+ * @see #UCX_TEST_END
+ */
+#define UCX_TEST_BEGIN fwrite("Running ", 1, 8, _output_);\
+ fwrite(__FUNCTION__, 1, strlen(__FUNCTION__), _output_);\
+ fwrite("... ", 1, 4, _output_);\
+ jmp_buf _env_; \
+ if (!setjmp(_env_)) {
+
+/**
+ * Checks a test assertion.
+ * If the assertion is correct, the test carries on. If the assertion is not
+ * correct, the specified message (terminated by a dot and a line break) is
+ * written to the test suites output stream.
+ * @param condition the condition to check
+ * @param message the message that shall be printed out on failure
+ */
+#define UCX_TEST_ASSERT(condition,message) if (!(condition)) { \
+ fwrite(message".\n", 1, 2+strlen(message), _output_); \
+ _suite_->failure++; \
+ longjmp(_env_, 1);\
+ }
+
+/**
+ * Macro for a test subroutine function header.
+ *
+ * Use this to declare and/or define a subroutine that can be called by using
+ * UCX_TEST_CALL_SUBROUTINE().
+ *
+ * @param name the name of the subroutine
+ * @param ... the parameter list
+ *
+ * @see UCX_TEST_CALL_SUBROUTINE()
+ */
+#define UCX_TEST_SUBROUTINE(name,...) void name(UcxTestSuite* _suite_,\
+ FILE *_output_, jmp_buf _env_, __VA_ARGS__)
+
+/**
+ * Macro for calling a test subroutine.
+ *
+ * Subroutines declared with UCX_TEST_SUBROUTINE() can be called by using this
+ * macro.
+ *
+ * <b>Note:</b> You may <b>only</b> call subroutines within a #UCX_TEST_BEGIN-
+ * #UCX_TEST_END-block.
+ *
+ * @param name the name of the subroutine
+ * @param ... the argument list
+ *
+ * @see UCX_TEST_SUBROUTINE()
+ */
+#define UCX_TEST_CALL_SUBROUTINE(name,...) \
+ name(_suite_,_output_,_env_,__VA_ARGS__);
+
+/**
+ * Marks the end of a test.
+ * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>before</b>
+ * #UCX_TEST_END.
+ *
+ * @see #UCX_TEST_BEGIN
+ */
+#define UCX_TEST_END fwrite("success.\n", 1, 9, _output_); _suite_->success++;}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_TEST_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Main UCX Header providing most common definitions.
+ *
+ * @file ucx.h
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_H
+#define UCX_H
+
+/** Major UCX version as integer constant. */
+#define UCX_VERSION_MAJOR 2
+
+/** Minor UCX version as integer constant. */
+#define UCX_VERSION_MINOR 1
+
+/** Version constant which ensures to increase monotonically. */
+#define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR)
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#ifdef _WIN32
+#if !(defined __ssize_t_defined || defined _SSIZE_T_)
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+#define __ssize_t_defined
+#define _SSIZE_T_
+#endif /* __ssize_t_defined and _SSIZE_T */
+#else /* !_WIN32 */
+#include <sys/types.h>
+#endif /* _WIN32 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * A function pointer to a destructor function.
+ * @see ucx_mempool_setdestr()
+ * @see ucx_mempool_regdestr()
+ */
+typedef void(*ucx_destructor)(void*);
+
+/**
+ * Function pointer to a compare function.
+ *
+ * The compare function shall take three arguments: the two values that shall be
+ * compared and optional additional data.
+ * The function shall then return -1 if the first argument is less than the
+ * second argument, 1 if the first argument is greater than the second argument
+ * and 0 if both arguments are equal. If the third argument is
+ * <code>NULL</code>, it shall be ignored.
+ */
+typedef int(*cmp_func)(const void*,const void*,void*);
+
+/**
+ * Function pointer to a distance function.
+ *
+ * The distance function shall take three arguments: the two values for which
+ * the distance shall be computed and optional additional data.
+ * The function shall then return the signed distance as integer value.
+ */
+typedef intmax_t(*distance_func)(const void*,const void*,void*);
+
+/**
+ * Function pointer to a copy function.
+ *
+ * The copy function shall create a copy of the first argument and may use
+ * additional data provided by the second argument. If the second argument is
+ * <code>NULL</code>, it shall be ignored.
+
+ * <b>Attention:</b> if pointers returned by functions of this type may be
+ * passed to <code>free()</code> depends on the implementation of the
+ * respective <code>copy_func</code>.
+ */
+typedef void*(*copy_func)(const void*,void*);
+
+/**
+ * Function pointer to a write function.
+ *
+ * The signature of the write function shall be compatible to the signature
+ * of standard <code>fwrite</code>, though it may use arbitrary data types for
+ * source and destination.
+ *
+ * The arguments shall contain (in ascending order): a pointer to the source,
+ * the length of one element, the element count and a pointer to the
+ * destination.
+ */
+typedef size_t(*write_func)(const void*, size_t, size_t, void*);
+
+/**
+ * Function pointer to a read function.
+ *
+ * The signature of the read function shall be compatible to the signature
+ * of standard <code>fread</code>, though it may use arbitrary data types for
+ * source and destination.
+ *
+ * The arguments shall contain (in ascending order): a pointer to the
+ * destination, the length of one element, the element count and a pointer to
+ * the source.
+ */
+typedef size_t(*read_func)(void*, size_t, size_t, void*);
+
+
+
+#if __GNUC__ >= 5 || defined(__clang__)
+#define UCX_MUL_BUILTIN
+
+#if __WORDSIZE == 32
+/**
+ * Alias for <code>__builtin_umul_overflow</code>.
+ *
+ * Performs a multiplication of size_t values and checks for overflow.
+ *
+ * @param a first operand
+ * @param b second operand
+ * @param result a pointer to a size_t, where the result should
+ * be stored
+ * @return zero, if no overflow occurred and the result is correct, non-zero
+ * otherwise
+ */
+#define ucx_szmul(a, b, result) __builtin_umul_overflow(a, b, result)
+#else /* __WORDSIZE != 32 */
+/**
+ * Alias for <code>__builtin_umull_overflow</code>.
+ *
+ * Performs a multiplication of size_t values and checks for overflow.
+ *
+ * @param a first operand
+ * @param b second operand
+ * @param result a pointer to a size_t, where the result should
+ * be stored
+ * @return zero, if no overflow occurred and the result is correct, non-zero
+ * otherwise
+ */
+#define ucx_szmul(a, b, result) __builtin_umull_overflow(a, b, result)
+#endif /* __WORDSIZE */
+
+#else /* no GNUC or clang bultin */
+
+/**
+ * Performs a multiplication of size_t values and checks for overflow.
+ *
+ * @param a first operand
+ * @param b second operand
+ * @param result a pointer to a size_t, where the result should
+ * be stored
+ * @return zero, if no overflow occurred and the result is correct, non-zero
+ * otherwise
+ */
+#define ucx_szmul(a, b, result) ucx_szmul_impl(a, b, result)
+
+/**
+ * Performs a multiplication of size_t values and checks for overflow.
+ *
+ * This is a custom implementation in case there is no compiler builtin
+ * available.
+ *
+ * @param a first operand
+ * @param b second operand
+ * @param result a pointer to a size_t where the result should be stored
+ * @return zero, if no overflow occurred and the result is correct, non-zero
+ * otherwise
+ */
+int ucx_szmul_impl(size_t a, size_t b, size_t *result);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file utils.h
+ *
+ * Compare, copy and printf functions.
+ *
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_UTILS_H
+#define UCX_UTILS_H
+
+#include "ucx.h"
+#include "string.h"
+#include "allocator.h"
+#include <inttypes.h>
+#include <string.h>
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Default buffer size for ucx_stream_copy() and ucx_stream_ncopy().
+ */
+#define UCX_STREAM_COPY_BUFSIZE 4096
+
+/**
+ * Copies a string.
+ * @param s the string to copy
+ * @param data omitted
+ * @return a pointer to a copy of s1 that can be passed to free(void*)
+ */
+void *ucx_strcpy(const void *s, void *data);
+
+/**
+ * Copies a memory area.
+ * @param m a pointer to the memory area
+ * @param n a pointer to the size_t containing the size of the memory area
+ * @return a pointer to a copy of the specified memory area that can
+ * be passed to free(void*)
+ */
+void *ucx_memcpy(const void *m, void *n);
+
+
+/**
+ * Reads data from a stream and writes it to another stream.
+ *
+ * @param src the source stream
+ * @param dest the destination stream
+ * @param rfnc the read function
+ * @param wfnc the write function
+ * @param buf a pointer to the copy buffer or <code>NULL</code> if a buffer
+ * shall be implicitly created on the heap
+ * @param bufsize the size of the copy buffer - if <code>NULL</code> was
+ * provided for <code>buf</code>, this is the size of the buffer that shall be
+ * implicitly created
+ * @param n the maximum number of bytes that shall be copied
+ * @return the total number of bytes copied
+ */
+size_t ucx_stream_bncopy(void *src, void *dest, read_func rfnc, write_func wfnc,
+ char* buf, size_t bufsize, size_t n);
+
+/**
+ * Shorthand for an unbounded ucx_stream_bncopy call using a default buffer.
+ *
+ * @param src the source stream
+ * @param dest the destination stream
+ * @param rfnc the read function
+ * @param wfnc the write function
+ * @return total number of bytes copied
+ *
+ * @see #UCX_STREAM_COPY_BUFSIZE
+ */
+#define ucx_stream_copy(src,dest,rfnc,wfnc) ucx_stream_bncopy(\
+ src, dest, (read_func)rfnc, (write_func)wfnc, \
+ NULL, UCX_STREAM_COPY_BUFSIZE, (size_t)-1)
+
+/**
+ * Shorthand for ucx_stream_bncopy using a default copy buffer.
+ *
+ * @param src the source stream
+ * @param dest the destination stream
+ * @param rfnc the read function
+ * @param wfnc the write function
+ * @param n maximum number of bytes that shall be copied
+ * @return total number of bytes copied
+ */
+#define ucx_stream_ncopy(src,dest,rfnc,wfnc, n) ucx_stream_bncopy(\
+ src, dest, (read_func)rfnc, (write_func)wfnc, \
+ NULL, UCX_STREAM_COPY_BUFSIZE, n)
+
+/**
+ * Shorthand for an unbounded ucx_stream_bncopy call using the specified buffer.
+ *
+ * @param src the source stream
+ * @param dest the destination stream
+ * @param rfnc the read function
+ * @param wfnc the write function
+ * @param buf a pointer to the copy buffer or <code>NULL</code> if a buffer
+ * shall be implicitly created on the heap
+ * @param bufsize the size of the copy buffer - if <code>NULL</code> was
+ * provided for <code>buf</code>, this is the size of the buffer that shall be
+ * implicitly created
+ * @return total number of bytes copied
+ */
+#define ucx_stream_bcopy(src,dest,rfnc,wfnc, buf, bufsize) ucx_stream_bncopy(\
+ src, dest, (read_func)rfnc, (write_func)wfnc, \
+ buf, bufsize, (size_t)-1)
+
+/**
+ * Wraps the strcmp function.
+ * @param s1 string one
+ * @param s2 string two
+ * @param data omitted
+ * @return the result of strcmp(s1, s2)
+ */
+int ucx_cmp_str(const void *s1, const void *s2, void *data);
+
+/**
+ * Wraps the strncmp function.
+ * @param s1 string one
+ * @param s2 string two
+ * @param n a pointer to the size_t containing the third strncmp parameter
+ * @return the result of strncmp(s1, s2, *n)
+ */
+int ucx_cmp_strn(const void *s1, const void *s2, void *n);
+
+/**
+ * Wraps the sstrcmp function.
+ * @param s1 sstr one
+ * @param s2 sstr two
+ * @param data ignored
+ * @return the result of sstrcmp(s1, s2)
+ */
+int ucx_cmp_sstr(const void *s1, const void *s2, void *data);
+
+/**
+ * Compares two integers of type int.
+ * @param i1 pointer to integer one
+ * @param i2 pointer to integer two
+ * @param data omitted
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int ucx_cmp_int(const void *i1, const void *i2, void *data);
+
+/**
+ * Compares two integers of type long int.
+ * @param i1 pointer to long integer one
+ * @param i2 pointer to long integer two
+ * @param data omitted
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int ucx_cmp_longint(const void *i1, const void *i2, void *data);
+
+/**
+ * Compares two integers of type long long.
+ * @param i1 pointer to long long one
+ * @param i2 pointer to long long two
+ * @param data omitted
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int ucx_cmp_longlong(const void *i1, const void *i2, void *data);
+
+/**
+ * Compares two integers of type int16_t.
+ * @param i1 pointer to int16_t one
+ * @param i2 pointer to int16_t two
+ * @param data omitted
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int ucx_cmp_int16(const void *i1, const void *i2, void *data);
+
+/**
+ * Compares two integers of type int32_t.
+ * @param i1 pointer to int32_t one
+ * @param i2 pointer to int32_t two
+ * @param data omitted
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int ucx_cmp_int32(const void *i1, const void *i2, void *data);
+
+/**
+ * Compares two integers of type int64_t.
+ * @param i1 pointer to int64_t one
+ * @param i2 pointer to int64_t two
+ * @param data omitted
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int ucx_cmp_int64(const void *i1, const void *i2, void *data);
+
+/**
+ * Compares two integers of type unsigned int.
+ * @param i1 pointer to unsigned integer one
+ * @param i2 pointer to unsigned integer two
+ * @param data omitted
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int ucx_cmp_uint(const void *i1, const void *i2, void *data);
+
+/**
+ * Compares two integers of type unsigned long int.
+ * @param i1 pointer to unsigned long integer one
+ * @param i2 pointer to unsigned long integer two
+ * @param data omitted
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int ucx_cmp_ulongint(const void *i1, const void *i2, void *data);
+
+/**
+ * Compares two integers of type unsigned long long.
+ * @param i1 pointer to unsigned long long one
+ * @param i2 pointer to unsigned long long two
+ * @param data omitted
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int ucx_cmp_ulonglong(const void *i1, const void *i2, void *data);
+
+/**
+ * Compares two integers of type uint16_t.
+ * @param i1 pointer to uint16_t one
+ * @param i2 pointer to uint16_t two
+ * @param data omitted
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int ucx_cmp_uint16(const void *i1, const void *i2, void *data);
+
+/**
+ * Compares two integers of type uint32_t.
+ * @param i1 pointer to uint32_t one
+ * @param i2 pointer to uint32_t two
+ * @param data omitted
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int ucx_cmp_uint32(const void *i1, const void *i2, void *data);
+
+/**
+ * Compares two integers of type uint64_t.
+ * @param i1 pointer to uint64_t one
+ * @param i2 pointer to uint64_t two
+ * @param data omitted
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int ucx_cmp_uint64(const void *i1, const void *i2, void *data);
+
+/**
+ * Distance function for integers of type int.
+ * @param i1 pointer to integer one
+ * @param i2 pointer to integer two
+ * @param data omitted
+ * @return i1 minus i2
+ */
+intmax_t ucx_dist_int(const void *i1, const void *i2, void *data);
+
+/**
+ * Distance function for integers of type long int.
+ * @param i1 pointer to long integer one
+ * @param i2 pointer to long integer two
+ * @param data omitted
+ * @return i1 minus i2
+ */
+intmax_t ucx_dist_longint(const void *i1, const void *i2, void *data);
+
+/**
+ * Distance function for integers of type long long.
+ * @param i1 pointer to long long one
+ * @param i2 pointer to long long two
+ * @param data omitted
+ * @return i1 minus i2
+ */
+intmax_t ucx_dist_longlong(const void *i1, const void *i2, void *data);
+
+/**
+ * Distance function for integers of type int16_t.
+ * @param i1 pointer to int16_t one
+ * @param i2 pointer to int16_t two
+ * @param data omitted
+ * @return i1 minus i2
+ */
+intmax_t ucx_dist_int16(const void *i1, const void *i2, void *data);
+
+/**
+ * Distance function for integers of type int32_t.
+ * @param i1 pointer to int32_t one
+ * @param i2 pointer to int32_t two
+ * @param data omitted
+ * @return i1 minus i2
+ */
+intmax_t ucx_dist_int32(const void *i1, const void *i2, void *data);
+
+/**
+ * Distance function for integers of type int64_t.
+ * @param i1 pointer to int64_t one
+ * @param i2 pointer to int64_t two
+ * @param data omitted
+ * @return i1 minus i2
+ */
+intmax_t ucx_dist_int64(const void *i1, const void *i2, void *data);
+
+/**
+ * Distance function for integers of type unsigned int.
+ * @param i1 pointer to unsigned integer one
+ * @param i2 pointer to unsigned integer two
+ * @param data omitted
+ * @return i1 minus i2
+ */
+intmax_t ucx_dist_uint(const void *i1, const void *i2, void *data);
+
+/**
+ * Distance function for integers of type unsigned long int.
+ * @param i1 pointer to unsigned long integer one
+ * @param i2 pointer to unsigned long integer two
+ * @param data omitted
+ * @return i1 minus i2
+ */
+intmax_t ucx_dist_ulongint(const void *i1, const void *i2, void *data);
+
+/**
+ * Distance function for integers of type unsigned long long.
+ * @param i1 pointer to unsigned long long one
+ * @param i2 pointer to unsigned long long two
+ * @param data omitted
+ * @return i1 minus i2
+ */
+intmax_t ucx_dist_ulonglong(const void *i1, const void *i2, void *data);
+
+/**
+ * Distance function for integers of type uint16_t.
+ * @param i1 pointer to uint16_t one
+ * @param i2 pointer to uint16_t two
+ * @param data omitted
+ * @return i1 minus i2
+ */
+intmax_t ucx_dist_uint16(const void *i1, const void *i2, void *data);
+
+/**
+ * Distance function for integers of type uint32_t.
+ * @param i1 pointer to uint32_t one
+ * @param i2 pointer to uint32_t two
+ * @param data omitted
+ * @return i1 minus i2
+ */
+intmax_t ucx_dist_uint32(const void *i1, const void *i2, void *data);
+
+/**
+ * Distance function for integers of type uint64_t.
+ * @param i1 pointer to uint64_t one
+ * @param i2 pointer to uint64_t two
+ * @param data omitted
+ * @return i1 minus i2
+ */
+intmax_t ucx_dist_uint64(const void *i1, const void *i2, void *data);
+
+/**
+ * Compares two real numbers of type float.
+ * @param f1 pointer to float one
+ * @param f2 pointer to float two
+ * @param data if provided: a pointer to precision (default: 1e-6f)
+ * @return -1, if *f1 is less than *f2, 0 if both are equal,
+ * 1 if *f1 is greater than *f2
+ */
+
+int ucx_cmp_float(const void *f1, const void *f2, void *data);
+
+/**
+ * Compares two real numbers of type double.
+ * @param d1 pointer to double one
+ * @param d2 pointer to double two
+ * @param data if provided: a pointer to precision (default: 1e-14)
+ * @return -1, if *d1 is less than *d2, 0 if both are equal,
+ * 1 if *d1 is greater than *d2
+ */
+int ucx_cmp_double(const void *d1, const void *d2, void *data);
+
+/**
+ * Compares two pointers.
+ * @param ptr1 pointer one
+ * @param ptr2 pointer two
+ * @param data omitted
+ * @return -1 if ptr1 is less than ptr2, 0 if both are equal,
+ * 1 if ptr1 is greater than ptr2
+ */
+int ucx_cmp_ptr(const void *ptr1, const void *ptr2, void *data);
+
+/**
+ * Compares two memory areas.
+ * @param ptr1 pointer one
+ * @param ptr2 pointer two
+ * @param n a pointer to the size_t containing the third parameter for memcmp
+ * @return the result of memcmp(ptr1, ptr2, *n)
+ */
+int ucx_cmp_mem(const void *ptr1, const void *ptr2, void *n);
+
+/**
+ * A <code>printf()</code> like function which writes the output to a stream by
+ * using a write_func().
+ * @param stream the stream the data is written to
+ * @param wfc the write function
+ * @param fmt format string
+ * @param ... additional arguments
+ * @return the total number of bytes written
+ */
+int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...);
+
+/**
+ * <code>va_list</code> version of ucx_fprintf().
+ * @param stream the stream the data is written to
+ * @param wfc the write function
+ * @param fmt format string
+ * @param ap argument list
+ * @return the total number of bytes written
+ * @see ucx_fprintf()
+ */
+int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap);
+
+/**
+ * A <code>printf()</code> like function which allocates space for a sstr_t
+ * the result is written to.
+ *
+ * <b>Attention</b>: The sstr_t data is allocated with the allocators
+ * ucx_allocator_malloc() function. So it is implementation dependent, if
+ * the returned sstr_t.ptr pointer must be passed to the allocators
+ * ucx_allocator_free() function manually.
+ *
+ * <b>Note</b>: The sstr_t.ptr of the return value will <i>always</i> be
+ * <code>NULL</code>-terminated.
+ *
+ * @param allocator the UcxAllocator used for allocating the result sstr_t
+ * @param fmt format string
+ * @param ... additional arguments
+ * @return a sstr_t containing the formatted string
+ */
+sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...);
+
+/**
+ * <code>va_list</code> version of ucx_asprintf().
+ *
+ * @param allocator the UcxAllocator used for allocating the result sstr_t
+ * @param fmt format string
+ * @param ap argument list
+ * @return a sstr_t containing the formatted string
+ * @see ucx_asprintf()
+ */
+sstr_t ucx_vasprintf(UcxAllocator *allocator, const char *fmt, va_list ap);
+
+/** Shortcut for ucx_asprintf() with default allocator. */
+#define ucx_sprintf(...) \
+ ucx_asprintf(ucx_default_allocator(), __VA_ARGS__)
+
+/**
+ * A <code>printf()</code> like function which writes the output to a
+ * UcxBuffer.
+ *
+ * @param buffer the buffer the data is written to
+ * @param ... format string and additional arguments
+ * @return the total number of bytes written
+ * @see ucx_fprintf()
+ */
+#define ucx_bprintf(buffer, ...) ucx_fprintf((UcxBuffer*)buffer, \
+ (write_func)ucx_buffer_write, __VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_UTILS_H */
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ucx/utils.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+
+/* COPY FUCNTIONS */
+void* ucx_strcpy(const void* s, void* data) {
+ const char *str = (const char*) s;
+ size_t n = 1+strlen(str);
+ char *cpy = (char*) malloc(n);
+ memcpy(cpy, str, n);
+ return cpy;
+}
+
+void* ucx_memcpy(const void* m, void* n) {
+ size_t k = *((size_t*)n);
+ void *cpy = malloc(k);
+ memcpy(cpy, m, k);
+ return cpy;
+}
+
+size_t ucx_stream_bncopy(void *src, void *dest, read_func readfnc,
+ write_func writefnc, char* buf, size_t bufsize, size_t n) {
+ if(n == 0 || bufsize == 0) {
+ return 0;
+ }
+
+ char *lbuf;
+ size_t ncp = 0;
+
+ if(buf) {
+ lbuf = buf;
+ } else {
+ lbuf = (char*)malloc(bufsize);
+ if(lbuf == NULL) {
+ return 0;
+ }
+ }
+
+ size_t r;
+ size_t rn = bufsize > n ? n : bufsize;
+ while((r = readfnc(lbuf, 1, rn, src)) != 0) {
+ r = writefnc(lbuf, 1, r, dest);
+ ncp += r;
+ n -= r;
+ rn = bufsize > n ? n : bufsize;
+ if(r == 0 || n == 0) {
+ break;
+ }
+ }
+
+ if (lbuf != buf) {
+ free(lbuf);
+ }
+
+ return ncp;
+}
+
+/* COMPARE FUNCTIONS */
+
+int ucx_cmp_str(const void *s1, const void *s2, void *data) {
+ return strcmp((const char*)s1, (const char*)s2);
+}
+
+int ucx_cmp_strn(const void *s1, const void *s2, void *n) {
+ return strncmp((const char*)s1, (const char*)s2, *((size_t*) n));
+}
+
+int ucx_cmp_sstr(const void *s1, const void *s2, void *data) {
+ sstr_t a = *(const sstr_t*) s1;
+ sstr_t b = *(const sstr_t*) s2;
+ return sstrcmp(a, b);
+}
+
+int ucx_cmp_int(const void *i1, const void *i2, void *data) {
+ int a = *((const int*) i1);
+ int b = *((const int*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int ucx_cmp_longint(const void *i1, const void *i2, void *data) {
+ long int a = *((const long int*) i1);
+ long int b = *((const long int*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int ucx_cmp_longlong(const void *i1, const void *i2, void *data) {
+ long long a = *((const long long*) i1);
+ long long b = *((const long long*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int ucx_cmp_int16(const void *i1, const void *i2, void *data) {
+ int16_t a = *((const int16_t*) i1);
+ int16_t b = *((const int16_t*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int ucx_cmp_int32(const void *i1, const void *i2, void *data) {
+ int32_t a = *((const int32_t*) i1);
+ int32_t b = *((const int32_t*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int ucx_cmp_int64(const void *i1, const void *i2, void *data) {
+ int64_t a = *((const int64_t*) i1);
+ int64_t b = *((const int64_t*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int ucx_cmp_uint(const void *i1, const void *i2, void *data) {
+ unsigned int a = *((const unsigned int*) i1);
+ unsigned int b = *((const unsigned int*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int ucx_cmp_ulongint(const void *i1, const void *i2, void *data) {
+ unsigned long int a = *((const unsigned long int*) i1);
+ unsigned long int b = *((const unsigned long int*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int ucx_cmp_ulonglong(const void *i1, const void *i2, void *data) {
+ unsigned long long a = *((const unsigned long long*) i1);
+ unsigned long long b = *((const unsigned long long*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int ucx_cmp_uint16(const void *i1, const void *i2, void *data) {
+ uint16_t a = *((const uint16_t*) i1);
+ uint16_t b = *((const uint16_t*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int ucx_cmp_uint32(const void *i1, const void *i2, void *data) {
+ uint32_t a = *((const uint32_t*) i1);
+ uint32_t b = *((const uint32_t*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int ucx_cmp_uint64(const void *i1, const void *i2, void *data) {
+ uint64_t a = *((const uint64_t*) i1);
+ uint64_t b = *((const uint64_t*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+intmax_t ucx_dist_int(const void *i1, const void *i2, void *data) {
+ intmax_t a = *((const int*) i1);
+ intmax_t b = *((const int*) i2);
+ return a - b;
+}
+
+intmax_t ucx_dist_longint(const void *i1, const void *i2, void *data) {
+ intmax_t a = *((const long int*) i1);
+ intmax_t b = *((const long int*) i2);
+ return a - b;
+}
+
+intmax_t ucx_dist_longlong(const void *i1, const void *i2, void *data) {
+ intmax_t a = *((const long long*) i1);
+ intmax_t b = *((const long long*) i2);
+ return a - b;
+}
+
+intmax_t ucx_dist_int16(const void *i1, const void *i2, void *data) {
+ intmax_t a = *((const int16_t*) i1);
+ intmax_t b = *((const int16_t*) i2);
+ return a - b;
+}
+
+intmax_t ucx_dist_int32(const void *i1, const void *i2, void *data) {
+ intmax_t a = *((const int32_t*) i1);
+ intmax_t b = *((const int32_t*) i2);
+ return a - b;
+}
+
+intmax_t ucx_dist_int64(const void *i1, const void *i2, void *data) {
+ intmax_t a = *((const int64_t*) i1);
+ intmax_t b = *((const int64_t*) i2);
+ return a - b;
+}
+
+intmax_t ucx_dist_uint(const void *i1, const void *i2, void *data) {
+ uintmax_t a = *((const unsigned int*) i1);
+ uintmax_t b = *((const unsigned int*) i2);
+ return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
+}
+
+intmax_t ucx_dist_ulongint(const void *i1, const void *i2, void *data) {
+ uintmax_t a = *((const unsigned long int*) i1);
+ uintmax_t b = *((const unsigned long int*) i2);
+ return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
+}
+
+intmax_t ucx_dist_ulonglong(const void *i1, const void *i2, void *data) {
+ uintmax_t a = *((const unsigned long long*) i1);
+ uintmax_t b = *((const unsigned long long*) i2);
+ return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
+}
+
+intmax_t ucx_dist_uint16(const void *i1, const void *i2, void *data) {
+ uintmax_t a = *((const uint16_t*) i1);
+ uintmax_t b = *((const uint16_t*) i2);
+ return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
+}
+
+intmax_t ucx_dist_uint32(const void *i1, const void *i2, void *data) {
+ uintmax_t a = *((const uint32_t*) i1);
+ uintmax_t b = *((const uint32_t*) i2);
+ return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
+}
+
+intmax_t ucx_dist_uint64(const void *i1, const void *i2, void *data) {
+ uintmax_t a = *((const uint64_t*) i1);
+ uintmax_t b = *((const uint64_t*) i2);
+ return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
+}
+
+int ucx_cmp_float(const void *f1, const void *f2, void *epsilon) {
+ float a = *((const float*) f1);
+ float b = *((const float*) f2);
+ float e = !epsilon ? 1e-6f : *((float*)epsilon);
+ if (fabsf(a - b) < e) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int ucx_cmp_double(const void *d1, const void *d2, void *epsilon) {
+ double a = *((const double*) d1);
+ double b = *((const double*) d2);
+ double e = !epsilon ? 1e-14 : *((double*)epsilon);
+ if (fabs(a - b) < e) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int ucx_cmp_ptr(const void *ptr1, const void *ptr2, void *data) {
+ const intptr_t p1 = (const intptr_t) ptr1;
+ const intptr_t p2 = (const intptr_t) ptr2;
+ if (p1 == p2) {
+ return 0;
+ } else {
+ return p1 < p2 ? -1 : 1;
+ }
+}
+
+int ucx_cmp_mem(const void *ptr1, const void *ptr2, void *n) {
+ return memcmp(ptr1, ptr2, *((size_t*)n));
+}
+
+/* PRINTF FUNCTIONS */
+
+#ifdef va_copy
+#define UCX_PRINTF_BUFSIZE 256
+#else
+#pragma message("WARNING: C99 va_copy macro not supported by this platform" \
+ " - limiting ucx_*printf to 2 KiB")
+#define UCX_PRINTF_BUFSIZE 0x800
+#endif
+
+int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...) {
+ int ret;
+ va_list ap;
+ va_start(ap, fmt);
+ ret = ucx_vfprintf(stream, wfc, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap) {
+ char buf[UCX_PRINTF_BUFSIZE];
+#ifdef va_copy
+ va_list ap2;
+ va_copy(ap2, ap);
+ int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
+ if (ret < 0) {
+ return ret;
+ } else if (ret < UCX_PRINTF_BUFSIZE) {
+ return (int)wfc(buf, 1, ret, stream);
+ } else {
+ if (ret == INT_MAX) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ int len = ret + 1;
+ char *newbuf = (char*)malloc(len);
+ if (!newbuf) {
+ return -1;
+ }
+
+ ret = vsnprintf(newbuf, len, fmt, ap2);
+ if (ret > 0) {
+ ret = (int)wfc(newbuf, 1, ret, stream);
+ }
+ free(newbuf);
+ }
+ return ret;
+#else
+ int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
+ if (ret < 0) {
+ return ret;
+ } else if (ret < UCX_PRINTF_BUFSIZE) {
+ return (int)wfc(buf, 1, ret, stream);
+ } else {
+ errno = ENOMEM;
+ return -1;
+ }
+#endif
+}
+
+sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...) {
+ va_list ap;
+ sstr_t ret;
+ va_start(ap, fmt);
+ ret = ucx_vasprintf(allocator, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+sstr_t ucx_vasprintf(UcxAllocator *a, const char *fmt, va_list ap) {
+ sstr_t s;
+ s.ptr = NULL;
+ s.length = 0;
+ char buf[UCX_PRINTF_BUFSIZE];
+#ifdef va_copy
+ va_list ap2;
+ va_copy(ap2, ap);
+ int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
+ if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) {
+ s.ptr = (char*)almalloc(a, ret + 1);
+ if (s.ptr) {
+ s.length = (size_t)ret;
+ memcpy(s.ptr, buf, ret);
+ s.ptr[s.length] = '\0';
+ }
+ } else if (ret == INT_MAX) {
+ errno = ENOMEM;
+ } else {
+ int len = ret + 1;
+ s.ptr = (char*)almalloc(a, len);
+ if (s.ptr) {
+ ret = vsnprintf(s.ptr, len, fmt, ap2);
+ if (ret < 0) {
+ free(s.ptr);
+ s.ptr = NULL;
+ } else {
+ s.length = (size_t)ret;
+ }
+ }
+ }
+#else
+ int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
+ if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) {
+ s.ptr = (char*)almalloc(a, ret + 1);
+ if (s.ptr) {
+ s.length = (size_t)ret;
+ memcpy(s.ptr, buf, ret);
+ s.ptr[s.length] = '\0';
+ }
+ } else {
+ errno = ENOMEM;
+ }
+#endif
+ return s;
+}