--- /dev/null
+.DS_Store
+Thumbs.db
--- /dev/null
+POSIX Regex functions for DataFlex 3.2\r
+--------------------------------------\r
+\r
+Requires:\r
+ DataFlex 3.1c onwards\r
+ GCC or compatible compiler like MinGW\r
+ GNU regex libraries for windows \r
+ http://sourceforge.net/projects/mingw/files/Other/UserContributed/regex\r
+\r
+I created this because despite VDF (Visual Dataflex) users being able to leverage\r
+vbscript.dll in Windows via COM to do regex operations, it seems that Console Mode \r
+users are out of luck.\r
+\r
+Despite being GNU libraries, this is intended to be used on Microsoft Windows\r
+using the DataFlex external_function import. No doubt if you're using DataFlex\r
+on Unix you can get these to work with minimal modifications.\r
--- /dev/null
+##-------------------------------------------------------------------------\r
+## posix regex extensions\r
+##\r
+## Copyright (c) 2015, glyn@8kb.co.uk\r
+## Author: Glyn Astill <glyn@8kb.co.uk>\r
+##\r
+##-------------------------------------------------------------------------\r
+##\r
+\r
+CPP = g++.exe\r
+CC = gcc.exe\r
+WINDRES = windres.exe\r
+RES = dfregex_private.res\r
+OBJ = dfregex.o memman.o gnuregex.o $(RES)\r
+LINKOBJ = dfregex.o memman.o gnuregex.o $(RES)\r
+LIBS = --no-export-all-symbols --add-stdcall-alias -lgnurx \r
+INCS = \r
+CXXINCS = \r
+BIN = dfregex.dll\r
+CXXFLAGS = $(CXXINCS) -DBUILDING_DLL=1 \r
+CFLAGS = $(INCS) -DBUILDING_DLL=1 \r
+RM = del -f\r
+\r
+.PHONY: all all-before all-after clean clean-custom\r
+\r
+all: all-before dfregex.dll all-after\r
+\r
+\r
+clean: clean-custom\r
+ ${RM} $(OBJ) $(BIN)\r
+\r
+DLLWRAP=dllwrap.exe\r
+DEFFILE=libdfregex.def\r
+STATICLIB=libdfregex.a\r
+\r
+$(BIN): $(LINKOBJ)\r
+ $(DLLWRAP) --output-def $(DEFFILE) --implib $(STATICLIB) $(LINKOBJ) $(LIBS) -o $(BIN)\r
+\r
+dfregex.o: dfregex.c\r
+ $(CC) -c dfregex.c -o dfregex.o $(CFLAGS)\r
+\r
+memman.o: memman.c\r
+ $(CC) -c memman.c -o memman.o $(CFLAGS)\r
+\r
+gnuregex.o: gnuregex.c\r
+ $(CC) -c gnuregex.c -o gnuregex.o $(CFLAGS)\r
+\r
+dfregex_private.res: dfregex_private.rc \r
+ $(WINDRES) -i dfregex_private.rc --input-format=rc -o dfregex_private.res -O coff \r
--- /dev/null
+/*-------------------------------------------------------------------------\r
+ * posix regex extensions\r
+ *\r
+ * Copyright (c) 2015, glyn@8kb.co.uk\r
+ * Author: Glyn Astill <glyn@8kb.co.uk>\r
+ *\r
+ *-------------------------------------------------------------------------\r
+ */\r
+\r
+#include <windows.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include "gnuregex.h"\r
+#include "dfregex.h"\r
+\r
+DLLIMPORT int RegexpMatch (const char *str, const char *pattern, const char *flags, int errors)\r
+{\r
+ return regexp_match(str, pattern, flags, errors);\r
+}\r
+\r
+DLLIMPORT int RegexpMatches(const char *str, const char *pattern, const char *flags, char *output, int output_len, int errors)\r
+{\r
+ char *matches = regexp_matches(str, pattern, flags, errors);\r
+ int matches_len;\r
+ int result = 0;\r
+\r
+ if (matches != NULL)\r
+ {\r
+ matches_len = strlen(matches);\r
+ if (matches_len <= output_len)\r
+ {\r
+ strncpy(output, matches, matches_len);\r
+ result = 0;\r
+ }\r
+ else\r
+ result = -1;\r
+\r
+ wfree(matches);\r
+ }\r
+ else\r
+ result = -2;\r
+\r
+ return result;\r
+}\r
+\r
+DLLIMPORT int RegexpReplace(const char *str, const char *pattern, const char *replacement, const char *flags, char *output, int output_len, int errors)\r
+{\r
+ char *replaced = regexp_replace(str, pattern, replacement, flags, errors);\r
+ int replaced_len;\r
+ int result = 0;\r
+\r
+ if (replaced != NULL)\r
+ {\r
+ replaced_len = strlen(replaced);\r
+\r
+ if (replaced_len <= output_len)\r
+ {\r
+ strncpy(output, replaced, replaced_len);\r
+ result = 0;\r
+ }\r
+ else\r
+ result = -1;\r
+\r
+ wfree(replaced);\r
+ }\r
+ else\r
+ result = -2;\r
+\r
+\r
+ return result;\r
+}\r
+\r
+BOOL APIENTRY DllMain (HINSTANCE hInst /* Library instance handle. */ ,\r
+ DWORD reason /* Reason this function is being called. */ ,\r
+ LPVOID reserved /* Not used. */ )\r
+{\r
+ switch (reason)\r
+ {\r
+ case DLL_PROCESS_ATTACH:\r
+ break;\r
+\r
+ case DLL_PROCESS_DETACH:\r
+ break;\r
+\r
+ case DLL_THREAD_ATTACH:\r
+ break;\r
+\r
+ case DLL_THREAD_DETACH:\r
+ break;\r
+ }\r
+\r
+ /* Returns TRUE on success, FALSE on failure */\r
+ return TRUE;\r
+}\r
--- /dev/null
+/*-------------------------------------------------------------------------\r
+ * posix regex extensions\r
+ *\r
+ * Copyright (c) 2015, glyn@8kb.co.uk\r
+ * Author: Glyn Astill <glyn@8kb.co.uk>\r
+ *\r
+ *-------------------------------------------------------------------------\r
+ */\r
+\r
+#ifndef _DFREGEX_H_\r
+#define _DFREGEX_H_\r
+\r
+#if BUILDING_DLL\r
+# define DLLIMPORT __declspec (dllexport)\r
+#else /* Not BUILDING_DLL */\r
+# define DLLIMPORT __declspec (dllimport)\r
+#endif /* Not BUILDING_DLL */\r
+\r
+\r
+DLLIMPORT int RegexpMatch(const char *str, const char *pattern, const char *flags, int errors);\r
+DLLIMPORT int RegexpMatches(const char *str, const char *pattern, const char *flags, char *output, int output_len, int errors);\r
+DLLIMPORT int RegexpReplace(const char *str, const char *pattern, const char *replacement, const char *flags, char *output, int output_len, int errors);\r
+\r
+#endif /* _DFREGEX_H_ */\r
--- /dev/null
+/*-------------------------------------------------------------------------\r
+ * posix regex extensions\r
+ *\r
+ * Copyright (c) 2015, glyn@8kb.co.uk\r
+ * Author: Glyn Astill <glyn@8kb.co.uk>\r
+ *\r
+ *-------------------------------------------------------------------------\r
+ */\r
+\r
+#ifndef DFREGEX_PRIVATE_H\r
+#define DFREGEX_PRIVATE_H\r
+\r
+/* VERSION DEFINITIONS */\r
+#define VER_STRING "0.1.1.1"\r
+#define VER_MAJOR 0\r
+#define VER_MINOR 1\r
+#define VER_RELEASE 1\r
+#define VER_BUILD 1\r
+#define COMPANY_NAME "8kb.co.uk"\r
+#define FILE_VERSION "0.1.1.1"\r
+#define FILE_DESCRIPTION "Regex for DataFlex 3.2"\r
+#define INTERNAL_NAME ""\r
+#define LEGAL_COPYRIGHT "Glyn Astill"\r
+#define LEGAL_TRADEMARKS ""\r
+#define ORIGINAL_FILENAME ""\r
+#define PRODUCT_NAME ""\r
+#define PRODUCT_VERSION ""\r
+\r
+#endif /*DFREGEX_PRIVATE_H*/\r
--- /dev/null
+/*-------------------------------------------------------------------------\r
+ * posix regex extensions\r
+ *\r
+ * Copyright (c) 2015, glyn@8kb.co.uk\r
+ * Author: Glyn Astill <glyn@8kb.co.uk>\r
+ *\r
+ *-------------------------------------------------------------------------\r
+ */\r
+\r
+#include <windows.h>\r
+\r
+1 VERSIONINFO\r
+FILEVERSION 0,1,1,1\r
+PRODUCTVERSION 0,1,1,1\r
+FILETYPE VFT_DLL\r
+{\r
+ BLOCK "StringFileInfo"\r
+ {\r
+ BLOCK "080904E4"\r
+ {\r
+ VALUE "CompanyName", "8kb.co.uk"\r
+ VALUE "FileVersion", "0.1.1.1"\r
+ VALUE "FileDescription", "Regex for DataFlex 3.2"\r
+ VALUE "InternalName", ""\r
+ VALUE "LegalCopyright", "Glyn Astill"\r
+ VALUE "LegalTrademarks", ""\r
+ VALUE "OriginalFilename", ""\r
+ VALUE "ProductName", ""\r
+ VALUE "ProductVersion", ""\r
+ }\r
+ }\r
+ BLOCK "VarFileInfo"\r
+ {\r
+ VALUE "Translation", 0x0809, 1252\r
+ }\r
+}\r
+\r
--- /dev/null
+/*-------------------------------------------------------------------------\r
+ * posix regex extensions\r
+ *\r
+ * Copyright (c) 2015, glyn@8kb.co.uk\r
+ * Author: Glyn Astill <glyn@8kb.co.uk>\r
+ *\r
+ *-------------------------------------------------------------------------\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <regex.h>\r
+#include "memman.h"\r
+\r
+#define MAX_ERROR_MSG 0x1000\r
+\r
+/*\r
+ * Return a properly escaped / quoted string\r
+ */\r
+static char * quote_output(char *str) {\r
+ char *result;\r
+ char *result_return;\r
+ int len;\r
+ int do_quote = 0;\r
+ char *ptr;\r
+\r
+ len = strlen(str);\r
+\r
+ /* Check for characters that need quoting */\r
+ for (ptr = str; *ptr; ptr++) {\r
+ char ch = *ptr;\r
+ if (ch == '\"' || ch =='\\' || ch == '\{' || ch == ',') {\r
+ do_quote = 1;\r
+ break;\r
+ }\r
+ }\r
+\r
+ /* If we find no characters that need quoting just return the input */\r
+ if (do_quote != 1)\r
+ return str;\r
+\r
+ /* Do the quoting, here the allocation is wasteful */\r
+ result = (char *) wmalloc((len * 2 + 3) * sizeof(char));\r
+ result_return = result;\r
+\r
+ /*\r
+ * Starting address of result is incremented as we modify it's contents here\r
+ * with result_return keeping the starting address\r
+ */\r
+ *result++ = '"';\r
+ while (len-- > 0) {\r
+ /* Escape double quotes and backslash with backslash */\r
+ if (*str == '"') {\r
+ *result++ = '\\';\r
+ }\r
+ if (*str == '\\') {\r
+ *result++ = '\\';\r
+ }\r
+ *result++ = *str++;\r
+ }\r
+ *result++ = '"';\r
+ *result++ = '\0';\r
+\r
+ return result_return;\r
+}\r
+\r
+/*\r
+ * Count open parenthesis to evaluate the number of subexpressions in the regex\r
+ */\r
+static int count_subexpressions(const char *str){\r
+ int result = 0;\r
+ int last_was_backslash = 0;\r
+ const char *ptr;\r
+\r
+ for(ptr = str; *ptr; ptr++){\r
+ if (*ptr == '\\' && !last_was_backslash){\r
+ last_was_backslash = 1;\r
+ continue;\r
+ }\r
+ if (*ptr == ')' && !last_was_backslash)\r
+ result++;\r
+ last_was_backslash = 0;\r
+ }\r
+ return result;\r
+}\r
+\r
+/*\r
+ * Check to see if string contains any escape chars\r
+ * these could of course just be escaped backslashes\r
+ * themselvs.\r
+ */\r
+static int has_escapes(const char *str){\r
+ const char *ptr;\r
+\r
+ for(ptr=str; *ptr; ptr++){\r
+ if (*ptr == '\\')\r
+ return 1;\r
+ }\r
+ return 0;\r
+}\r
+\r
+/*\r
+ * Compile the regex pattern\r
+ */\r
+static int compile_regex(regex_t *re, const char *pattern, const char *flags, int errors)\r
+{\r
+ int status;\r
+ int cflags = REG_EXTENDED;\r
+\r
+ if (strchr(flags, 'i')) {\r
+ cflags = cflags|REG_ICASE;\r
+ }\r
+ if (strchr(flags, 'n')) {\r
+ cflags = cflags|REG_NEWLINE;\r
+ }\r
+\r
+ status = regcomp(re, pattern, cflags);\r
+ if (status != REG_NOERROR) {\r
+ if (errors == 1) {\r
+ char *error_message;\r
+ regerror (status, re, error_message, MAX_ERROR_MSG);\r
+ fprintf (stderr, "Regex error compiling '%s': %s\n", pattern, error_message);\r
+ }\r
+ return 1;\r
+ }\r
+ return status;\r
+}\r
+\r
+/*\r
+ * Returns a pointer to a malloced array of regmatch_t containing match offsets\r
+ * in the input string. (As opposed to offests from each match)\r
+ *\r
+ * The regmatch struct info:\r
+ * regmatch_t.rm_so (regoff_t) = byte offset from start of string to start of substring\r
+ * regmatch_t.rm_eo (regoff_t) = byte offset from start of string to first character after the end of substring\r
+ */\r
+static int find_regex_matches(regex_t *re, const char *str, const int nsub, const char *flags, regmatch_t **result)\r
+{\r
+ /* Each individual match and it's subexpression matches stored in m */\r
+ regmatch_t m[nsub+1];\r
+\r
+ /* A pointer into the string at the end of the previous match */\r
+ const char *prev_match_eo = str;\r
+\r
+ /*\r
+ * We return a count of matches and pass back an array of regmatch_t in\r
+ * matches containing match offsets in the original string\r
+ */\r
+ int array_len = strchr(flags, 'g') ? 256 : 32;\r
+ int match_count = 0;\r
+ regmatch_t *matches;\r
+\r
+ matches = (regmatch_t *) wmalloc(sizeof(regmatch_t) * array_len);\r
+\r
+ while (!regexec(re, prev_match_eo, nsub+1, m, 0)) {\r
+ int i = 0;\r
+\r
+ /* resize the matches array; when more space is required double current size */\r
+ while (match_count + (nsub * 2) > array_len) {\r
+ array_len *= 2;\r
+ matches = (regmatch_t *) wrealloc(matches, sizeof(regmatch_t) * array_len);\r
+ }\r
+\r
+ /* when we have subexpressions, we're only interested in their match offsets */\r
+ if (nsub > 0) {\r
+ for (i = 1; i <= nsub; i++) {\r
+ if (m[i].rm_so < 0 || m[i].rm_eo < 0) {\r
+ matches[match_count].rm_so = -1;\r
+ matches[match_count++].rm_eo = -1;\r
+ }\r
+ else {\r
+ matches[match_count].rm_so = (prev_match_eo - str) + m[i].rm_so;\r
+ matches[match_count++].rm_eo = (prev_match_eo - str) + m[i].rm_eo;\r
+ }\r
+ }\r
+ }\r
+ /* else we want the original match offsets*/\r
+ else {\r
+ matches[match_count].rm_so = (prev_match_eo - str) + m[0].rm_so;\r
+ matches[match_count++].rm_eo = (prev_match_eo - str) + m[0].rm_eo;\r
+ }\r
+\r
+ /*\r
+ * If we have matched on a blank expression or we were\r
+ * not flagged to do greedy matching then break\r
+ */\r
+ if (!m[0].rm_eo || !strchr(flags, 'g'))\r
+ break;\r
+\r
+ /*\r
+ * Advance the search position to the end of the current match\r
+ * If the match happens to be zero length, advance search position\r
+ * by one?\r
+ */\r
+ if (m[0].rm_eo == m[0].rm_so)\r
+ prev_match_eo++;\r
+ else\r
+ prev_match_eo += m[0].rm_eo;\r
+ }\r
+ *result = matches;\r
+\r
+ return match_count;\r
+}\r
+\r
+/*\r
+ * Takes regmatch_t array returned by find_regex_matches and returns a malloced\r
+ * string representing the captured substrings.\r
+ */\r
+static char * regex_matches_to_string(const char *str, int nsub, int match_count, regmatch_t *matches) {\r
+ int j;\r
+ int i;\r
+ char *unquoted = NULL;\r
+ char *quoted = NULL;\r
+ int quoted_len;\r
+ char *result;\r
+\r
+ int str_len = strlen(str);\r
+ int allocated_sz = str_len+1;\r
+ result = wmalloc(allocated_sz * sizeof(char));\r
+ int result_sz = 0;\r
+\r
+ j = 0;\r
+ while (j < match_count) {\r
+\r
+ if (j > 0) {\r
+ result_sz += 2;\r
+ result = reallocate_block(result, &allocated_sz, result_sz * sizeof(char), str_len);\r
+ result[result_sz-2] = ',';\r
+ result[result_sz-1] = '{';\r
+ }\r
+ else {\r
+ result_sz++;\r
+ result = reallocate_block(result, &allocated_sz, result_sz * sizeof(char), str_len);\r
+ result[result_sz-1] = '{';\r
+ }\r
+\r
+ for (i = 0; i <= nsub; i++) {\r
+ if ((nsub > 0) && (i == 0))\r
+ continue;\r
+\r
+ if (i > 1) {\r
+ result_sz++;\r
+ result = reallocate_block(result, &allocated_sz, result_sz * sizeof(char), str_len);\r
+ result[result_sz-1] = ',';\r
+ }\r
+\r
+ int so = matches[j].rm_so;\r
+ int eo = matches[j].rm_eo;\r
+\r
+ if (so == -1 || eo == -1) {\r
+ result = reallocate_block(result, &allocated_sz, (result_sz+4) * sizeof(char), str_len);\r
+ strncpy(result+result_sz, "NULL", 4);\r
+ result_sz += 4;\r
+ }\r
+ else {\r
+ unquoted = wmalloc((eo-so)+1 * sizeof(char));\r
+ strncpy(unquoted, str+so, eo-so);\r
+ unquoted[eo-so] = '\0';\r
+ quoted = quote_output(unquoted);\r
+ quoted_len = strlen(quoted);\r
+\r
+ result = reallocate_block(result, &allocated_sz, (result_sz+quoted_len) * sizeof(char), str_len);\r
+ strncpy(result+result_sz, quoted, quoted_len);\r
+ result_sz += quoted_len;\r
+\r
+ if (quoted != unquoted)\r
+ wfree(unquoted);\r
+ wfree(quoted);\r
+ }\r
+ j++;\r
+ }\r
+\r
+ result_sz++;\r
+ result = reallocate_block(result, &allocated_sz, result_sz * sizeof(char), str_len);\r
+ result[result_sz-1] = '}';\r
+ }\r
+\r
+ result_sz++;\r
+ result = reallocate_block(result, &allocated_sz, result_sz * sizeof(char), str_len);\r
+ result[result_sz-1] = '\0';\r
+\r
+ return result;\r
+}\r
+\r
+/*\r
+ * Purely check for a match in the regex\r
+ */\r
+int regexp_match(const char *str, const char *pattern, const char *flags, int errors)\r
+{\r
+ regex_t re;\r
+ int result;\r
+ int status;\r
+\r
+ status = compile_regex(&re, pattern, flags, errors);\r
+ if (status == REG_NOERROR) {\r
+ result = regexec(&re, str, (size_t) 0, NULL, 0);\r
+ regfree(&re);\r
+\r
+ if (!result) /* match */\r
+ return 1;\r
+ else\r
+ return 0;\r
+ }\r
+ else /* no match */\r
+ return 0;\r
+}\r
+\r
+/*\r
+ * Return all matches in the regex as a string by first calling find_regex_matches\r
+ * and then regex_matches_to_string. Arguably this could all be one function\r
+ * however separation will make future multiple output formats easier.\r
+ */\r
+char * regexp_matches(const char *str, const char *pattern, const char *flags, int errors)\r
+{\r
+ regex_t re;\r
+ regmatch_t *matches_p = NULL;\r
+ int nsub;\r
+ int match_count;\r
+ int status;\r
+ char *result = NULL;\r
+\r
+ /* Compile the regex */\r
+ status = compile_regex(&re, pattern, flags, errors);\r
+ if (status == REG_NOERROR) {\r
+ /* Count our subexpressions to size our regmatch_t array */\r
+ nsub = count_subexpressions(pattern);\r
+ /* Find all the matches relative to the input string */\r
+ match_count = find_regex_matches(&re, str, nsub, flags, &matches_p);\r
+ /* Turn the matches into an output string */\r
+ result = regex_matches_to_string(str, nsub, match_count, matches_p);\r
+ /* Free up the regmatch_t malloced by find_regex_matches */\r
+ wfree(matches_p);\r
+ regfree(&re);\r
+ }\r
+\r
+ return result;\r
+}\r
+\r
+/*\r
+ * Substitutes matches with the regex pattern in the string with the replacement\r
+ * pattern/string.\r
+ */\r
+char * regexp_replace(const char *str, const char *pattern, const char *replacement, const char *flags, int errors)\r
+{\r
+ regex_t re;\r
+ int nsub;\r
+ char *result = NULL;\r
+ char *match_str;\r
+ int status;\r
+ const char *prev_match_eo = str;\r
+ int str_len = strlen(str);\r
+ int replacement_len = strlen(replacement);\r
+ int allocated_sz = str_len+1;\r
+ int result_sz = 0;\r
+\r
+ status = compile_regex(&re, pattern, flags, errors);\r
+ if (status == REG_NOERROR) {\r
+\r
+ result = wmalloc(allocated_sz * sizeof(char));\r
+\r
+ /* Count our subexpressions to size our regmatch_t array */\r
+ nsub = count_subexpressions(pattern);\r
+ regmatch_t m[nsub+1];\r
+\r
+ while (!regexec(&re, prev_match_eo, nsub+1, m, 0)) {\r
+\r
+ /* Copy everything to the left of the first match */\r
+ if (m[0].rm_so > 0) {\r
+ result = reallocate_block(result, &allocated_sz, (result_sz+m[0].rm_so) * sizeof(char), str_len);\r
+ strncpy(result+result_sz, prev_match_eo, m[0].rm_so);\r
+ result_sz += m[0].rm_so;\r
+ }\r
+\r
+ /* If there are no backreferences in the replacement, copy in the replacement */\r
+ if (!has_escapes(replacement)) {\r
+ result = reallocate_block(result, &allocated_sz, (result_sz+replacement_len) * sizeof(char), str_len);\r
+ strncpy(result+result_sz, replacement, replacement_len);\r
+ result_sz += replacement_len;\r
+ }\r
+ /* Otherwise process the backreferences and copy in subcaptures */\r
+ else {\r
+ /* find the next escape char */\r
+ const char *start = replacement;\r
+ const char *ptr;\r
+\r
+ for(ptr = replacement; *ptr; ptr++) {\r
+ if (*ptr != '\\')\r
+ continue;\r
+\r
+ /* append everything to the left of the current escape */\r
+ result = reallocate_block(result, &allocated_sz, (result_sz+(ptr-start)) * sizeof(char), str_len);\r
+ strncpy(result+result_sz, start, (ptr-start));\r
+ result_sz += (ptr-start);\r
+\r
+ ptr++;\r
+\r
+ if ((*ptr >= '1' && *ptr <= '9') || (*ptr == '&'))\r
+ {\r
+ /* Use the back reference of regexp. */\r
+ int sub;\r
+ if (*ptr == '&')\r
+ sub = 0;\r
+ else\r
+ sub = *ptr - '0';\r
+\r
+ if (m[sub].rm_so != -1 && m[sub].rm_eo != -1 && sub <= nsub) {\r
+ result = reallocate_block(result, &allocated_sz, (result_sz+(m[sub].rm_eo-m[sub].rm_so)) * sizeof(char), str_len);\r
+ strncpy(result+result_sz, prev_match_eo+m[sub].rm_so, (m[sub].rm_eo-m[sub].rm_so));\r
+ result_sz += (m[sub].rm_eo-m[sub].rm_so);\r
+ }\r
+ ptr++;\r
+ }\r
+ else if (*ptr == '\\')\r
+ {\r
+ /* append backsalsh */\r
+ result_sz++;\r
+ result = reallocate_block(result, &allocated_sz, result_sz * sizeof(char), str_len);\r
+ result[result_sz-1] = '\\';\r
+ ptr++;\r
+ }\r
+ else {\r
+ /* append backsalsh */\r
+ result_sz++;\r
+ result = reallocate_block(result, &allocated_sz, result_sz * sizeof(char), str_len);\r
+ result[result_sz-1] = '\\';\r
+ }\r
+ start = ptr;\r
+ }\r
+ /*\r
+ * Append right trailing replacement, except in the instance\r
+ * when it starts with character zero, which can happen when the\r
+ * last part of the replace string is escaped.\r
+ */\r
+ if (*start) {\r
+ result = reallocate_block(result, &allocated_sz, (result_sz+(ptr-start)) * sizeof(char), str_len);\r
+ strncpy(result+result_sz, start, (ptr-start));\r
+ result_sz += (ptr-start);\r
+ }\r
+\r
+ }\r
+ prev_match_eo += m[0].rm_eo;\r
+\r
+ /*\r
+ * If we have matched on a blank expression or we were\r
+ * not flagged to do greedy matching then break\r
+ */\r
+ if (!m[0].rm_eo || !strchr(flags, 'g'))\r
+ break;\r
+ }\r
+\r
+ /* Copy everything to the right of the last match */\r
+ result = reallocate_block(result, &allocated_sz, (result_sz+(str_len-(prev_match_eo-str))) * sizeof(char), str_len);\r
+ strncpy(result+result_sz, prev_match_eo, str_len-(prev_match_eo-str));\r
+ result_sz += str_len-(prev_match_eo-str);\r
+\r
+ regfree(&re);\r
+\r
+ result_sz++;\r
+ result = reallocate_block(result, &allocated_sz, result_sz * sizeof(char), str_len);\r
+ result[result_sz-1] = '\0';\r
+ }\r
+ return result;\r
+}\r
--- /dev/null
+/*-------------------------------------------------------------------------\r
+ * posix regex extensions\r
+ *\r
+ * Copyright (c) 2015, glyn@8kb.co.uk\r
+ * Author: Glyn Astill <glyn@8kb.co.uk>\r
+ *\r
+ *-------------------------------------------------------------------------\r
+ */\r
+\r
+#ifndef __GNUREGEX_H__\r
+#define __GNUREGEX_H__\r
+\r
+extern int regexp_match(const char *str, const char *pattern, const char *flags, int errors);\r
+extern char * regexp_matches(const char *str, const char *pattern, const char *flags, int errors);\r
+extern char * regexp_replace(const char *str, const char *pattern, const char *replacement, const char *flags, int errors);\r
+\r
+#endif\r
--- /dev/null
+/*-------------------------------------------------------------------------\r
+ * posix regex extensions\r
+ *\r
+ * Copyright (c) 2015, glyn@8kb.co.uk\r
+ * Author: Glyn Astill <glyn@8kb.co.uk>\r
+ *\r
+ *-------------------------------------------------------------------------\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <assert.h>\r
+\r
+/*\r
+ * Wrappers around malloc/realloc/free\r
+ */\r
+void * wmalloc(unsigned int size) {\r
+ char *result;\r
+\r
+ if ((result = malloc(size)) == NULL) {\r
+ fprintf(stderr, "Failed to malloc %d bytes\n", size);\r
+ exit(1);\r
+ }\r
+ return result;\r
+}\r
+\r
+void * wrealloc(void *iptr, unsigned int size) {\r
+ char *result;\r
+\r
+ assert(iptr != NULL);\r
+\r
+ if ((result = realloc(iptr, size)) == NULL) {\r
+ fprintf(stderr, "Failed to realloc %d bytes\n", size);\r
+ exit(1);\r
+ }\r
+ return result;\r
+}\r
+\r
+void wfree(void *iptr){\r
+ assert(iptr != NULL);\r
+\r
+ if (iptr) {\r
+ free(iptr);\r
+ }\r
+ iptr = NULL;\r
+}\r
+\r
+/*\r
+ * Reallocate memory block pointed to by iptr in chunks of chunk_size when\r
+ * required_size is greater than value pointed to be allocated_size.\r
+ * Sets value of allocated_size to current allocation.\r
+ */\r
+void * reallocate_block(void *iptr, int *allocated_size, int required_size, int chunk_size) {\r
+ void *result;\r
+\r
+ if (*allocated_size >= required_size)\r
+ return iptr;\r
+\r
+ *allocated_size += (((required_size-*allocated_size)/chunk_size)+1)*chunk_size;\r
+\r
+ result = wrealloc(iptr, *allocated_size);\r
+\r
+ return result;\r
+}\r
--- /dev/null
+/*-------------------------------------------------------------------------\r
+ * posix regex extensions\r
+ *\r
+ * Copyright (c) 2015, glyn@8kb.co.uk\r
+ * Author: Glyn Astill <glyn@8kb.co.uk>\r
+ *\r
+ *-------------------------------------------------------------------------\r
+ */\r
+\r
+#ifndef __MEMMAN_H__\r
+#define __MEMMAN_H__\r
+\r
+extern void * wmalloc(unsigned int size);\r
+extern void * wrealloc(void *iptr, unsigned int size);\r
+extern void sfree(void *iptr);\r
+extern void * reallocate_block(void *iptr, int *allocated_size, int required_size, int chunk_size);\r
+\r
+#endif\r
--- /dev/null
+//-------------------------------------------------------------------------\r
+// posix regex extensions\r
+//\r
+// Copyright (c) 2015, glyn@8kb.co.uk\r
+// Author: Glyn Astill <glyn@8kb.co.uk>\r
+//\r
+//-------------------------------------------------------------------------\r
+//\r
+\r
+use dll\r
+\r
+Define max_dfregex_buffer for 16384\r
+Define errors_to_stderr for 0\r
+\r
+external_function RegexpMatch "RegexpMatch" dfregex.dll pointer str pointer pattern pointer flags integer errors returns integer\r
+external_function RegexpMatches "RegexpMatches" dfregex.dll pointer str pointer pattern pointer flags pointer out pointer out_len integer errors returns integer\r
+external_function RegexpReplace "RegexpReplace" dfregex.dll pointer str pointer pattern pointer replacement pointer flags pointer out pointer out_len integer errors returns integer\r
+\r
+//Purely check if a regex expression produces match in the input string\r
+// Returns 1 on match, 0 on no match\r
+// E.g\r
+// move (regexp_match('the quick brown fox jumps over the lazy dog.', 'fox', 'g'))\r
+function regexp_match global string str string pattern string flags returns integer\r
+ local integer l_iReturn\r
+ local pointer l_pStr l_pPattern l_pFlags\r
+ \r
+ getaddress of str to l_pStr\r
+ getaddress of pattern to l_pPattern\r
+ getaddress of flags to l_pFlags\r
+ \r
+ move (RegexpMatch(l_pStr, l_pPattern, l_pFlags, errors_to_stderr)) to l_iReturn\r
+ \r
+ function_return l_iReturn\r
+end_function \r
+\r
+//Return a string containing all regex matches in the input string\r
+// E.g\r
+// move (regexp_matches('the quick brown fox jumps over the la\{zy d"og.', 'fox|(the)|brown|(la\\\{zy)|(d"og)', 'g')) to myString\r
+function regexp_matches global string str string pattern string flags returns string\r
+ local integer l_iReturn\r
+ local pointer l_pStr l_pPattern l_pFlags l_pOut\r
+ local string l_sOut l_sReturn\r
+ \r
+ move "" to l_sReturn\r
+ getaddress of str to l_pStr\r
+ getaddress of pattern to l_pPattern\r
+ getaddress of flags to l_pFlags\r
+ zerostring max_dfregex_buffer to l_sOut\r
+ getaddress of l_sOut to l_pOut\r
+ \r
+ move (RegexpMatches(l_pStr, l_pPattern, l_pFlags, l_pOut, max_dfregex_buffer, errors_to_stderr)) to l_iReturn\r
+ \r
+ if (l_iReturn = 0);\r
+ move (cstring(l_sOut)) To l_sReturn\r
+ else begin\r
+ if (l_iReturn = -1);\r
+ error 999997 "Regex output buffer too small"\r
+ if (l_iReturn = -2);\r
+ error 999998 "Regex compilation failure"\r
+ move "" to l_sReturn\r
+ end\r
+ \r
+ function_return l_sReturn\r
+end_function \r
+\r
+//Perform a replacement on the input string all matches with the given pattern\r
+// E.g.\r
+// move (regexp_replace('22 quick brown foxes jump over the 44 lazy dogs.', '([0-9]*).* (foxes) .* ([0-9]*) .* (dogs).*', 'SELECT build_data(\1,\2), build_data(\3,\4);', 'g')) to myString\r
+function regexp_replace global string str string pattern string replacement string flags returns string\r
+ local integer l_iReturn\r
+ local pointer l_pStr l_pPattern l_pFlags l_pReplacement l_pOut\r
+ local string l_sOut l_sReturn\r
+ \r
+ move "" to l_sReturn\r
+ getaddress of str to l_pStr\r
+ getaddress of pattern to l_pPattern\r
+ getaddress of flags to l_pFlags\r
+ getaddress of replacement to l_pReplacement\r
+ zerostring max_dfregex_buffer to l_sOut\r
+ getaddress of l_sOut to l_pOut\r
+ \r
+ move (RegexpReplace(l_pStr, l_pPattern, l_pReplacement, l_pFlags, l_pOut, max_dfregex_buffer, errors_to_stderr)) to l_iReturn\r
+ \r
+ if (l_iReturn = 0);\r
+ move (cstring(l_sOut)) To l_sReturn\r
+ else begin\r
+ if (l_iReturn = -1);\r
+ error 999997 "Regex output buffer too small" \r
+ if (l_iReturn = -2);\r
+ error 999998 "Regex compilation failure" \r
+ move "" to l_sReturn\r
+ end\r
+ \r
+ function_return l_sReturn\r
+end_function
\ No newline at end of file