|
|
@@ -0,0 +1,669 @@
|
|
|
+#ifdef SQ_USE_PCRE2
|
|
|
+
|
|
|
+#include <ctype.h>
|
|
|
+#include <string.h>
|
|
|
+#include <stdio.h>
|
|
|
+
|
|
|
+#include "squirrel.h"
|
|
|
+#include "sqstdblobimpl.h"
|
|
|
+
|
|
|
+#define PCRE2_CODE_UNIT_WIDTH 8
|
|
|
+#include <pcre2.h>
|
|
|
+
|
|
|
+////////
|
|
|
+#include "dynamic_library.h"
|
|
|
+
|
|
|
+/*SquiLu
|
|
|
+
|
|
|
+local library_functions = [
|
|
|
+ ["pcre2_code *", "pcre2_compile", "PCRE2_SPTR, PCRE2_SIZE, uint32_t, int *, PCRE2_SIZE *, pcre2_compile_context *"],
|
|
|
+ ["int", "pcre2_jit_compile", "pcre2_code *, uint32_t"],
|
|
|
+ ["void", "pcre2_code_free", "pcre2_code *"],
|
|
|
+ ["pcre2_match_data *", "pcre2_match_data_create_from_pattern", "const pcre2_code *, pcre2_general_context *"],
|
|
|
+ ["PCRE2_SIZE *", "pcre2_get_ovector_pointer", "pcre2_match_data *match_data"],
|
|
|
+ ["uint32_t", "pcre2_get_ovector_count", "pcre2_match_data *match_data"],
|
|
|
+ ["void", "pcre2_match_data_free", "pcre2_match_data *"],
|
|
|
+ ["int", "pcre2_match", "const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, pcre2_match_data *, pcre2_match_context *"],
|
|
|
+ ["int", "pcre2_dfa_match", "const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length, PCRE2_SIZE startoffset, uint32_t options, pcre2_match_data *match_data, pcre2_match_context *mcontext, int *workspace, PCRE2_SIZE wscount"],
|
|
|
+ ["int", "pcre2_get_error_message", "int, PCRE2_UCHAR *, PCRE2_SIZE"],
|
|
|
+ ["pcre2_match_context *", "pcre2_match_context_create", "pcre2_general_context *gcontext"],
|
|
|
+ ["pcre2_match_context *", "pcre2_match_context_copy", "pcre2_match_context *"],
|
|
|
+ ["void", "pcre2_match_context_free", "pcre2_match_context *"],
|
|
|
+ ["int", "pcre2_set_match_limit", "pcre2_match_context *mcontext, uint32_t value"],
|
|
|
+ ["int", "pcre2_set_recursion_limit", "pcre2_match_context *mcontext, uint32_t value"],
|
|
|
+ ["int", "pcre2_set_callout", "pcre2_match_context *, int (*)(pcre2_callout_block *, void *), void *"],
|
|
|
+
|
|
|
+ //next entry should be the last one
|
|
|
+ //to make valid the test made on load_library function
|
|
|
+ ["int", "pcre2_config", "uint32_t what, void *where"],
|
|
|
+];
|
|
|
+
|
|
|
+function write_library_functions_declaration(){
|
|
|
+ foreach(k,v in library_functions) {
|
|
|
+ putsnl("typedef " + v[0] + " (*" + v[1] + "_t)(" + v[2] + ");");
|
|
|
+ putsnl("static " + v[1] + "_t dl" + v[1] + " = 0;");
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function write_library_functions_load(chsz){
|
|
|
+ foreach(k,v in library_functions){
|
|
|
+ putsnl("dl" + v[1] + " = (" + v[1] + "_t) libdyn.dlsym(\"" + v[1] + chsz + "\");");
|
|
|
+ putsnl("if(!dl" + v[1] + ") return false;");
|
|
|
+ }
|
|
|
+}
|
|
|
+SquiLu*/
|
|
|
+
|
|
|
+static DynamicLibrary libdyn;
|
|
|
+
|
|
|
+//@write_library_functions_declaration();
|
|
|
+// generated-code:begin
|
|
|
+typedef pcre2_code * (*pcre2_compile_t)(PCRE2_SPTR, PCRE2_SIZE, uint32_t, int *, PCRE2_SIZE *, pcre2_compile_context *);
|
|
|
+static pcre2_compile_t dlpcre2_compile = 0;
|
|
|
+typedef int (*pcre2_jit_compile_t)(pcre2_code *, uint32_t);
|
|
|
+static pcre2_jit_compile_t dlpcre2_jit_compile = 0;
|
|
|
+typedef void (*pcre2_code_free_t)(pcre2_code *);
|
|
|
+static pcre2_code_free_t dlpcre2_code_free = 0;
|
|
|
+typedef pcre2_match_data * (*pcre2_match_data_create_from_pattern_t)(const pcre2_code *, pcre2_general_context *);
|
|
|
+static pcre2_match_data_create_from_pattern_t dlpcre2_match_data_create_from_pattern = 0;
|
|
|
+typedef PCRE2_SIZE * (*pcre2_get_ovector_pointer_t)(pcre2_match_data *match_data);
|
|
|
+static pcre2_get_ovector_pointer_t dlpcre2_get_ovector_pointer = 0;
|
|
|
+typedef uint32_t (*pcre2_get_ovector_count_t)(pcre2_match_data *match_data);
|
|
|
+static pcre2_get_ovector_count_t dlpcre2_get_ovector_count = 0;
|
|
|
+typedef void (*pcre2_match_data_free_t)(pcre2_match_data *);
|
|
|
+static pcre2_match_data_free_t dlpcre2_match_data_free = 0;
|
|
|
+typedef int (*pcre2_match_t)(const pcre2_code *, PCRE2_SPTR, PCRE2_SIZE, PCRE2_SIZE, uint32_t, pcre2_match_data *, pcre2_match_context *);
|
|
|
+static pcre2_match_t dlpcre2_match = 0;
|
|
|
+typedef int (*pcre2_dfa_match_t)(const pcre2_code *code, PCRE2_SPTR subject, PCRE2_SIZE length, PCRE2_SIZE startoffset, uint32_t options, pcre2_match_data *match_data, pcre2_match_context *mcontext, int *workspace, PCRE2_SIZE wscount);
|
|
|
+static pcre2_dfa_match_t dlpcre2_dfa_match = 0;
|
|
|
+typedef int (*pcre2_get_error_message_t)(int, PCRE2_UCHAR *, PCRE2_SIZE);
|
|
|
+static pcre2_get_error_message_t dlpcre2_get_error_message = 0;
|
|
|
+typedef pcre2_match_context * (*pcre2_match_context_create_t)(pcre2_general_context *gcontext);
|
|
|
+static pcre2_match_context_create_t dlpcre2_match_context_create = 0;
|
|
|
+typedef pcre2_match_context * (*pcre2_match_context_copy_t)(pcre2_match_context *);
|
|
|
+static pcre2_match_context_copy_t dlpcre2_match_context_copy = 0;
|
|
|
+typedef void (*pcre2_match_context_free_t)(pcre2_match_context *);
|
|
|
+static pcre2_match_context_free_t dlpcre2_match_context_free = 0;
|
|
|
+typedef int (*pcre2_set_match_limit_t)(pcre2_match_context *mcontext, uint32_t value);
|
|
|
+static pcre2_set_match_limit_t dlpcre2_set_match_limit = 0;
|
|
|
+typedef int (*pcre2_set_recursion_limit_t)(pcre2_match_context *mcontext, uint32_t value);
|
|
|
+static pcre2_set_recursion_limit_t dlpcre2_set_recursion_limit = 0;
|
|
|
+typedef int (*pcre2_set_callout_t)(pcre2_match_context *, int (*)(pcre2_callout_block *, void *), void *);
|
|
|
+static pcre2_set_callout_t dlpcre2_set_callout = 0;
|
|
|
+typedef int (*pcre2_config_t)(uint32_t what, void *where);
|
|
|
+static pcre2_config_t dlpcre2_config = 0;
|
|
|
+// generated-code:end
|
|
|
+
|
|
|
+static const char *dynamicLibName = DYNLIB_FOR_OS(libpcre2-8);
|
|
|
+
|
|
|
+static bool load_library(const char *libname)
|
|
|
+{
|
|
|
+ if(dlpcre2_config) return true;
|
|
|
+ if(libdyn.open(libname))
|
|
|
+ {
|
|
|
+ //@write_library_functions_load("_8");
|
|
|
+// generated-code:begin
|
|
|
+dlpcre2_compile = (pcre2_compile_t) libdyn.dlsym("pcre2_compile_8");
|
|
|
+if(!dlpcre2_compile) return false;
|
|
|
+dlpcre2_jit_compile = (pcre2_jit_compile_t) libdyn.dlsym("pcre2_jit_compile_8");
|
|
|
+if(!dlpcre2_jit_compile) return false;
|
|
|
+dlpcre2_code_free = (pcre2_code_free_t) libdyn.dlsym("pcre2_code_free_8");
|
|
|
+if(!dlpcre2_code_free) return false;
|
|
|
+dlpcre2_match_data_create_from_pattern = (pcre2_match_data_create_from_pattern_t) libdyn.dlsym("pcre2_match_data_create_from_pattern_8");
|
|
|
+if(!dlpcre2_match_data_create_from_pattern) return false;
|
|
|
+dlpcre2_get_ovector_pointer = (pcre2_get_ovector_pointer_t) libdyn.dlsym("pcre2_get_ovector_pointer_8");
|
|
|
+if(!dlpcre2_get_ovector_pointer) return false;
|
|
|
+dlpcre2_get_ovector_count = (pcre2_get_ovector_count_t) libdyn.dlsym("pcre2_get_ovector_count_8");
|
|
|
+if(!dlpcre2_get_ovector_count) return false;
|
|
|
+dlpcre2_match_data_free = (pcre2_match_data_free_t) libdyn.dlsym("pcre2_match_data_free_8");
|
|
|
+if(!dlpcre2_match_data_free) return false;
|
|
|
+dlpcre2_match = (pcre2_match_t) libdyn.dlsym("pcre2_match_8");
|
|
|
+if(!dlpcre2_match) return false;
|
|
|
+dlpcre2_dfa_match = (pcre2_dfa_match_t) libdyn.dlsym("pcre2_dfa_match_8");
|
|
|
+if(!dlpcre2_dfa_match) return false;
|
|
|
+dlpcre2_get_error_message = (pcre2_get_error_message_t) libdyn.dlsym("pcre2_get_error_message_8");
|
|
|
+if(!dlpcre2_get_error_message) return false;
|
|
|
+dlpcre2_match_context_create = (pcre2_match_context_create_t) libdyn.dlsym("pcre2_match_context_create_8");
|
|
|
+if(!dlpcre2_match_context_create) return false;
|
|
|
+dlpcre2_match_context_copy = (pcre2_match_context_copy_t) libdyn.dlsym("pcre2_match_context_copy_8");
|
|
|
+if(!dlpcre2_match_context_copy) return false;
|
|
|
+dlpcre2_match_context_free = (pcre2_match_context_free_t) libdyn.dlsym("pcre2_match_context_free_8");
|
|
|
+if(!dlpcre2_match_context_free) return false;
|
|
|
+dlpcre2_set_match_limit = (pcre2_set_match_limit_t) libdyn.dlsym("pcre2_set_match_limit_8");
|
|
|
+if(!dlpcre2_set_match_limit) return false;
|
|
|
+dlpcre2_set_recursion_limit = (pcre2_set_recursion_limit_t) libdyn.dlsym("pcre2_set_recursion_limit_8");
|
|
|
+if(!dlpcre2_set_recursion_limit) return false;
|
|
|
+dlpcre2_set_callout = (pcre2_set_callout_t) libdyn.dlsym("pcre2_set_callout_8");
|
|
|
+if(!dlpcre2_set_callout) return false;
|
|
|
+dlpcre2_config = (pcre2_config_t) libdyn.dlsym("pcre2_config_8");
|
|
|
+if(!dlpcre2_config) return false;
|
|
|
+// generated-code:end
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+typedef int (*pre2_callout_func_t)(pcre2_callout_block *, void *);
|
|
|
+
|
|
|
+struct sqpcre2_st {
|
|
|
+ pcre2_code *re;
|
|
|
+ pcre2_match_context *match_context;
|
|
|
+ pcre2_match_data *match_data;
|
|
|
+ PCRE2_SIZE *ovector;
|
|
|
+
|
|
|
+ /* associated squirrel vm */
|
|
|
+ HSQUIRRELVM v;
|
|
|
+
|
|
|
+ HSQOBJECT callout_cb;
|
|
|
+ HSQOBJECT callout_cb_udata;
|
|
|
+};
|
|
|
+
|
|
|
+static const SQChar PCRE2_Tag[] = _SC("sqpcre2");
|
|
|
+#define GET_pcre2_INSTANCE() SQ_GET_INSTANCE(v, 1, sqpcre2_st, PCRE2_Tag) \
|
|
|
+ if(self == NULL) return sq_throwerror(v, _SC("sqpcre2 object already closed"));
|
|
|
+
|
|
|
+static SQRESULT sq_pcre2_releasehook(SQUserPointer p, SQInteger /*size*/, void */*ep*/)
|
|
|
+{
|
|
|
+ sqpcre2_st *self = ((sqpcre2_st *)p);
|
|
|
+ if(self)
|
|
|
+ {
|
|
|
+ if(self->match_data) dlpcre2_match_data_free(self->match_data);
|
|
|
+ dlpcre2_code_free(self->re);
|
|
|
+ if(self->match_context)
|
|
|
+ {
|
|
|
+ sq_release(self->v, &self->callout_cb);
|
|
|
+ sq_release(self->v, &self->callout_cb_udata);
|
|
|
+ dlpcre2_match_context_free(self->match_context);
|
|
|
+ }
|
|
|
+
|
|
|
+ sq_free(self, sizeof(*self));
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static SQRESULT sq_pcre2_constructor(HSQUIRRELVM v)
|
|
|
+{
|
|
|
+ if(!load_library(dynamicLibName)) return sq_throwerror(v, _SC("Failed to load libpcre !"));
|
|
|
+ SQ_FUNC_VARS(v);
|
|
|
+ SQ_GET_STRING(v, 2, pattern);
|
|
|
+ SQ_OPT_INTEGER(v, 3, options, 0);
|
|
|
+ PCRE2_SIZE erroroffset;
|
|
|
+ int errornumber;
|
|
|
+ pcre2_code *re = dlpcre2_compile(
|
|
|
+ (PCRE2_SPTR)pattern, /* the pattern */
|
|
|
+ PCRE2_ZERO_TERMINATED, /* indicates pattern is zero-terminated */
|
|
|
+ options, /* 0 = default options */
|
|
|
+ &errornumber, /* for error message */
|
|
|
+ &erroroffset, /* for error offset */
|
|
|
+ NULL); /* use default character tables */
|
|
|
+ if(!re)
|
|
|
+ {
|
|
|
+ PCRE2_UCHAR buffer[256];
|
|
|
+ dlpcre2_get_error_message(errornumber, buffer, sizeof(buffer));
|
|
|
+ return sq_throwerror(v,_SC("PCRE compilation failed at offset %d: %s"), (int)erroroffset, buffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ sqpcre2_st *sqpcre2 = (sqpcre2_st *)sq_malloc(sizeof(*sqpcre2));
|
|
|
+ sqpcre2->re = re;
|
|
|
+ sqpcre2->match_data = dlpcre2_match_data_create_from_pattern(re, NULL);
|
|
|
+ sqpcre2->ovector = dlpcre2_get_ovector_pointer(sqpcre2->match_data);
|
|
|
+ sqpcre2->v = v;
|
|
|
+ sqpcre2->match_context = NULL;
|
|
|
+ /*
|
|
|
+ sq_resetobject(&sqpcre2->callout_cb);
|
|
|
+ sq_resetobject(&sqpcre2->callout_cb_udata);
|
|
|
+ */
|
|
|
+
|
|
|
+ sq_setinstanceup(v,1,sqpcre2);
|
|
|
+ sq_setreleasehook(v,1,sq_pcre2_releasehook);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static SQRESULT sq_pcre2_exec(HSQUIRRELVM v)
|
|
|
+{
|
|
|
+ SQ_FUNC_VARS(v);
|
|
|
+ GET_pcre2_INSTANCE();
|
|
|
+ SQ_GET_STRING(v, 2, subject);
|
|
|
+ SQ_OPT_INTEGER(v, 4, start_offset, 0);
|
|
|
+ SQ_OPT_INTEGER(v, 5, options, 0);
|
|
|
+
|
|
|
+ int rc = dlpcre2_match(
|
|
|
+ self->re, /* the compiled pattern */
|
|
|
+ (PCRE2_SPTR)subject, /* the subject string */
|
|
|
+ subject_size, /* the length of the subject */
|
|
|
+ start_offset, /* start at offset 0 in the subject */
|
|
|
+ options, /* 0 = default options */
|
|
|
+ self->match_data, /* block for storing the result */
|
|
|
+ self->match_context); /* use default match context */
|
|
|
+
|
|
|
+ const int array_pos = 3;
|
|
|
+ SQInteger rtype = sq_gettype(v, array_pos);
|
|
|
+
|
|
|
+ /* The output vector wasn't big enough */
|
|
|
+
|
|
|
+ if (rc > 0)
|
|
|
+ {
|
|
|
+ if(rtype == OT_ARRAY)
|
|
|
+ {
|
|
|
+ int nelms = rc*2;
|
|
|
+ sq_arrayminsize(v, array_pos, nelms);
|
|
|
+ for (int i = 0; i < nelms; i++)
|
|
|
+ {
|
|
|
+ sq_pushinteger(v, self->ovector[i]);
|
|
|
+ sq_arrayset(v, array_pos, i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ sq_pushinteger(v, rc);
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static SQRESULT sq_pcre2_match(HSQUIRRELVM v)
|
|
|
+{
|
|
|
+ SQ_FUNC_VARS(v);
|
|
|
+ GET_pcre2_INSTANCE();
|
|
|
+ SQ_GET_STRING(v, 2, subject);
|
|
|
+ SQ_OPT_INTEGER(v, 3, start_offset, 0);
|
|
|
+ SQ_OPT_INTEGER(v, 4, options, 0);
|
|
|
+
|
|
|
+ int rc = dlpcre2_match(
|
|
|
+ self->re, /* the compiled pattern */
|
|
|
+ (PCRE2_SPTR)subject, /* the subject string */
|
|
|
+ subject_size, /* the length of the subject */
|
|
|
+ start_offset, /* start at offset 0 in the subject */
|
|
|
+ options, /* 0 = default options */
|
|
|
+ self->match_data, /* block for storing the result */
|
|
|
+ self->match_context); /* use default match context */
|
|
|
+
|
|
|
+ if(rc > 0)
|
|
|
+ {
|
|
|
+ SQInteger start_pos = self->ovector[0], end_pos = self->ovector[1];
|
|
|
+ sq_pushstring(v, subject + start_pos, end_pos - start_pos);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ sq_pushbool(v,SQFalse);
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static SQRESULT sq_pcre2_gmatch(HSQUIRRELVM v)
|
|
|
+{
|
|
|
+ SQ_FUNC_VARS(v);
|
|
|
+ GET_pcre2_INSTANCE();
|
|
|
+ SQ_GET_STRING(v, 2, subject);
|
|
|
+ SQ_OPT_INTEGER(v, 4, start_offset, 0);
|
|
|
+ SQ_OPT_INTEGER(v, 5, options, 0);
|
|
|
+
|
|
|
+ SQInteger rc;
|
|
|
+ bool isFirst = true;
|
|
|
+
|
|
|
+ while( (rc = dlpcre2_match(
|
|
|
+ self->re, /* the compiled pattern */
|
|
|
+ (PCRE2_SPTR)subject, /* the subject string */
|
|
|
+ subject_size, /* the length of the subject */
|
|
|
+ start_offset, /* start at offset 0 in the subject */
|
|
|
+ options, /* 0 = default options */
|
|
|
+ self->match_data, /* block for storing the result */
|
|
|
+ self->match_context)) > 0) /* use default match context */
|
|
|
+ {
|
|
|
+ if(!isFirst)
|
|
|
+ {
|
|
|
+ sq_push(v, 3); //push the function
|
|
|
+ isFirst = false;
|
|
|
+ }
|
|
|
+ sq_pushroottable(v); //this
|
|
|
+ SQInteger ov_offset = 0, i = 0;
|
|
|
+ for(;i < rc; i++) {
|
|
|
+ ov_offset = i*2;
|
|
|
+ SQInteger start_pos = self->ovector[ov_offset], end_pos = self->ovector[ov_offset+1];
|
|
|
+ sq_pushstring(v, subject + start_pos, end_pos - start_pos);
|
|
|
+ }
|
|
|
+ i = sq_call(v, i+1, SQFalse, SQTrue);
|
|
|
+ if(i < 0) return i;
|
|
|
+ start_offset = self->ovector[(rc*2)-1]; //the last match + 1
|
|
|
+ }
|
|
|
+ sq_pushbool(v,SQFalse);
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+#include "sqstdblobimpl.h"
|
|
|
+static SQRESULT sq_pcre2_gsub(HSQUIRRELVM v)
|
|
|
+{
|
|
|
+ SQ_FUNC_VARS(v);
|
|
|
+ GET_pcre2_INSTANCE();
|
|
|
+ SQ_GET_STRING(v, 2, str);
|
|
|
+ SQ_OPT_INTEGER(v, 4, start_offset, 0);
|
|
|
+ SQ_OPT_INTEGER(v, 5, options, 0);
|
|
|
+
|
|
|
+ SQBlob blob(0,8192);
|
|
|
+ const int replacement_idx = 3;
|
|
|
+ SQObjectType ptype = sq_gettype(v, replacement_idx);
|
|
|
+ const SQChar *replacement;
|
|
|
+ SQInteger replacement_size;
|
|
|
+
|
|
|
+ SQInteger rc;
|
|
|
+ bool isFirst = true;
|
|
|
+
|
|
|
+ while( (rc = dlpcre2_match(
|
|
|
+ self->re, /* the compiled pattern */
|
|
|
+ (PCRE2_SPTR)str, /* the subject string */
|
|
|
+ str_size, /* the length of the subject */
|
|
|
+ start_offset, /* start at offset 0 in the subject */
|
|
|
+ options, /* 0 = default options */
|
|
|
+ self->match_data, /* block for storing the result */
|
|
|
+ self->match_context)) > 0) /* use default match context */
|
|
|
+ {
|
|
|
+ SQInteger i, ov_offset, start_pos, end_pos;
|
|
|
+ blob.Write(str+start_offset, self->ovector[0]-start_offset);
|
|
|
+ switch(ptype){
|
|
|
+ case OT_CLOSURE:{
|
|
|
+ if(!isFirst)
|
|
|
+ {
|
|
|
+ sq_push(v, replacement_idx); //push the function
|
|
|
+ isFirst = false;
|
|
|
+ }
|
|
|
+ sq_pushroottable(v); //this
|
|
|
+ for(i=0; i < rc; i++) {
|
|
|
+ ov_offset = i*2;
|
|
|
+ start_pos = self->ovector[ov_offset], end_pos = self->ovector[ov_offset+1];
|
|
|
+ sq_pushstring(v, str + start_pos, end_pos - start_pos);
|
|
|
+ }
|
|
|
+ i = sq_call(v, rc, SQTrue, SQTrue);
|
|
|
+ if(i < 0) return i;
|
|
|
+ if(sq_gettype(v, -1) == OT_STRING){
|
|
|
+ const SQChar *svalue;
|
|
|
+ sq_getstring(v, -1, &svalue);
|
|
|
+ blob.Write(svalue, sq_getsize(v, -1));
|
|
|
+ }
|
|
|
+ sq_poptop(v);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case OT_ARRAY:{
|
|
|
+ for(i=0; i < rc; i++) {
|
|
|
+ sq_pushinteger(v, i);
|
|
|
+ if(SQ_SUCCEEDED(sq_get(v, replacement_idx)) &&
|
|
|
+ SQ_SUCCEEDED(sq_getstr_and_size(v, -1, &replacement, &replacement_size))){
|
|
|
+ blob.Write(replacement, replacement_size);
|
|
|
+ sq_pop(v, 1); //remove value
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case OT_TABLE:{
|
|
|
+ for(i=0; i < rc; i++) {
|
|
|
+ ov_offset = i*2;
|
|
|
+ start_pos = self->ovector[ov_offset], end_pos = self->ovector[ov_offset+1];
|
|
|
+ sq_pushstring(v, str + start_pos, end_pos - start_pos);
|
|
|
+ if(SQ_SUCCEEDED(sq_get(v, replacement_idx)) &&
|
|
|
+ SQ_SUCCEEDED(sq_getstr_and_size(v, -1, &replacement, &replacement_size))){
|
|
|
+ blob.Write(replacement, replacement_size);
|
|
|
+ sq_pop(v, 1); //remove value
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case OT_STRING:{
|
|
|
+ sq_getstr_and_size(v, replacement_idx, &replacement, &replacement_size);
|
|
|
+
|
|
|
+ for(i=0; i < replacement_size; i++) {
|
|
|
+ SQInteger c = replacement[i];
|
|
|
+ switch(c)
|
|
|
+ {
|
|
|
+ case '$':
|
|
|
+ ++i;
|
|
|
+ if(i < replacement_size)
|
|
|
+ {
|
|
|
+ SQInteger idx = replacement[i] - '0';
|
|
|
+ if(idx < rc)
|
|
|
+ {
|
|
|
+ ov_offset = idx*2;
|
|
|
+ start_pos = self->ovector[ov_offset], end_pos = self->ovector[ov_offset+1];
|
|
|
+ blob.Write(str+start_pos, end_pos-start_pos);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return sq_throwerror(v, _SC("there is no match for replacement $%d"), idx);
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return sq_throwerror(v, _SC("unexpected end of replacement string"));
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case '\\':
|
|
|
+ ++i;
|
|
|
+ if(i < replacement_size)
|
|
|
+ {
|
|
|
+ blob.WriteChar(replacement[i]);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ //falthrough last character on replacement string
|
|
|
+ default:
|
|
|
+ blob.WriteChar(c);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return sq_throwerror(v, _SC("gsub only works with closure, array, table for replacement"));
|
|
|
+ }
|
|
|
+ start_offset = self->ovector[(rc*2)-1]; //the last match + 1
|
|
|
+ }
|
|
|
+
|
|
|
+ if(str_size) blob.Write(str+start_offset, str_size-start_offset);
|
|
|
+ sq_pushstring(v, (const SQChar *)blob.GetBuf(), blob.Len());
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static SQRESULT sq_pcre2_jit_compile(HSQUIRRELVM v)
|
|
|
+{
|
|
|
+ SQ_FUNC_VARS(v);
|
|
|
+ GET_pcre2_INSTANCE();
|
|
|
+ SQ_OPT_INTEGER(v, 2, options, 0);
|
|
|
+ sq_pushinteger(v, dlpcre2_jit_compile(self->re, options));
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static SQRESULT sq_pcre2__typeof(HSQUIRRELVM v)
|
|
|
+{
|
|
|
+ sq_pushstring(v,_SC(PCRE2_Tag),-1);
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static SQRESULT sq_pcre2_version(HSQUIRRELVM v)
|
|
|
+{
|
|
|
+ PCRE2_UCHAR8 buf[32];
|
|
|
+ int rc = dlpcre2_config(PCRE2_CONFIG_VERSION, buf);
|
|
|
+ sq_pushstring(v, (const SQChar*)buf, -1);
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static SQRESULT sq_pcre2_loadlib(HSQUIRRELVM v)
|
|
|
+{
|
|
|
+ SQ_FUNC_VARS_NO_TOP(v);
|
|
|
+ SQ_GET_STRING(v, 2, libname);
|
|
|
+ sq_pushbool(v, load_library(libname));
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+The external callout function returns an integer to PCRE2.
|
|
|
+If the value is zero, matching proceeds as normal.
|
|
|
+If the value is greater than zero, matching fails at the current point,
|
|
|
+but the testing of other matching possibilities goes ahead,
|
|
|
+just as if a lookahead assertion had failed.
|
|
|
+If the value is less than zero, the match is abandoned,
|
|
|
+and the matching function returns the negative value.
|
|
|
+*/
|
|
|
+static int sq_pcre2_callout_callback(pcre2_callout_block *pcb, void *udata)
|
|
|
+{
|
|
|
+ SQInteger result = -1; /* abort by default */
|
|
|
+ sqpcre2_st *sqpcre2 = (sqpcre2_st*)udata;
|
|
|
+ HSQUIRRELVM v = sqpcre2->v;
|
|
|
+ int top = sq_gettop(v);
|
|
|
+
|
|
|
+ sq_pushobject(v, sqpcre2->callout_cb);
|
|
|
+ sq_pushroottable(v); //this
|
|
|
+ sq_pushinteger(v, pcb->callout_number);
|
|
|
+ if(pcb->callout_string_length) sq_pushstring(v,
|
|
|
+ (const SQChar*)pcb->callout_string, pcb->callout_string_length);
|
|
|
+ else sq_pushnull(v);
|
|
|
+ sq_pushinteger(v, pcb->start_match);
|
|
|
+ sq_pushinteger(v, pcb->current_position);
|
|
|
+ sq_pushobject(v, sqpcre2->callout_cb_udata);
|
|
|
+
|
|
|
+ /* call squilu function */
|
|
|
+ if (sq_call(v, 6, SQTrue, SQFalse) == SQ_OK)
|
|
|
+ sq_getinteger(v, -1, &result);
|
|
|
+
|
|
|
+ sq_settop(v, top);
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+static SQRESULT sq_pcre2_set_callout(HSQUIRRELVM v)
|
|
|
+{
|
|
|
+ SQ_FUNC_VARS(v);
|
|
|
+ GET_pcre2_INSTANCE();
|
|
|
+
|
|
|
+ if(!self->match_context)
|
|
|
+ {
|
|
|
+ self->match_context = dlpcre2_match_context_create(NULL);
|
|
|
+ sq_resetobject(&self->callout_cb);
|
|
|
+ sq_resetobject(&self->callout_cb_udata);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ /* clear progress handler */
|
|
|
+ dlpcre2_set_callout(self->match_context, NULL, NULL);
|
|
|
+ }
|
|
|
+
|
|
|
+ sq_release(v, &self->callout_cb);
|
|
|
+ sq_release(v, &self->callout_cb_udata);
|
|
|
+
|
|
|
+ sq_resetobject(&self->callout_cb);
|
|
|
+ sq_resetobject(&self->callout_cb_udata);
|
|
|
+
|
|
|
+ SQObjectType otype = sq_gettype(v, 2);
|
|
|
+ if (_top_ > 1 && (otype != OT_NULL))
|
|
|
+ {
|
|
|
+ if(sq_gettype(v, 2) != OT_CLOSURE)
|
|
|
+ return sq_throwerror(v, _SC("invalid second parameter expected closure"));
|
|
|
+
|
|
|
+ sq_getstackobj(v, 2, &self->callout_cb);
|
|
|
+ sq_addref(v, &self->callout_cb);
|
|
|
+ if(_top_ > 2)
|
|
|
+ {
|
|
|
+ sq_getstackobj(v, 3, &self->callout_cb_udata);
|
|
|
+ sq_addref(v, &self->callout_cb_udata);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* set progress callback */
|
|
|
+ dlpcre2_set_callout(self->match_context, sq_pcre2_callout_callback, self);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static SQRESULT sq_pcre2_set_callout_param(HSQUIRRELVM v)
|
|
|
+{
|
|
|
+ SQ_FUNC_VARS(v);
|
|
|
+ GET_pcre2_INSTANCE();
|
|
|
+
|
|
|
+ if(!self->match_context) return sq_throwerror(v, _SC("callout not set till now"));
|
|
|
+
|
|
|
+ sq_release(v, &self->callout_cb_udata);
|
|
|
+ sq_resetobject(&self->callout_cb_udata);
|
|
|
+
|
|
|
+ SQObjectType otype = sq_gettype(v, 2);
|
|
|
+ if (_top_ > 1 && (otype != OT_NULL))
|
|
|
+ {
|
|
|
+ sq_getstackobj(v, 2, &self->callout_cb_udata);
|
|
|
+ sq_addref(v, &self->callout_cb_udata);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#define _DECL_FUNC(name,nparams,tycheck) {_SC(#name),sq_pcre2_##name,nparams,tycheck}
|
|
|
+static SQRegFunction sq_pcre2_methods[] =
|
|
|
+{
|
|
|
+ _DECL_FUNC(constructor,-2,_SC(".sn")),
|
|
|
+ _DECL_FUNC(jit_compile,-1,_SC("xi")),
|
|
|
+ _DECL_FUNC(exec,-3,_SC("xsann")),
|
|
|
+ _DECL_FUNC(match,-2,_SC("xsnn")),
|
|
|
+ _DECL_FUNC(gmatch,-3,_SC("xscnn")),
|
|
|
+ _DECL_FUNC(gsub,-3,_SC("xs s|c|a|t nn")),
|
|
|
+ _DECL_FUNC(_typeof,1,_SC("x")),
|
|
|
+ _DECL_FUNC(version,1,_SC(".")),
|
|
|
+ _DECL_FUNC(loadlib,2,_SC(".s")),
|
|
|
+ _DECL_FUNC(set_callout,-2,_SC("xc.")),
|
|
|
+ _DECL_FUNC(set_callout_param,-1,_SC("x.")),
|
|
|
+ {0,0}
|
|
|
+};
|
|
|
+#undef _DECL_FUNC
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ const SQChar *Str;
|
|
|
+ SQInteger Val;
|
|
|
+} KeyIntType, * KeyIntPtrType;
|
|
|
+
|
|
|
+static KeyIntType sqpcre2_constants[] = {
|
|
|
+ #define MK_CONST(c) {_SC(#c), PCRE2_##c}
|
|
|
+ #define MK_CONST2(c) {_SC(c), (SQInteger)PCRE2_##c}
|
|
|
+ //MK_CONST(SSL_SESSION_ID_SIZE),
|
|
|
+
|
|
|
+ MK_CONST(ANCHORED),
|
|
|
+ MK_CONST(NOTBOL),
|
|
|
+ MK_CONST(NOTEOL),
|
|
|
+ MK_CONST(NOTEMPTY),
|
|
|
+ MK_CONST(NOTEMPTY_ATSTART),
|
|
|
+ MK_CONST(NO_START_OPTIMIZE),
|
|
|
+ MK_CONST(PARTIAL_HARD),
|
|
|
+ MK_CONST(PARTIAL_SOFT),
|
|
|
+ MK_CONST(NEWLINE_CR),
|
|
|
+ MK_CONST(NEWLINE_LF),
|
|
|
+ MK_CONST(NEWLINE_CRLF),
|
|
|
+ MK_CONST(NEWLINE_ANYCRLF),
|
|
|
+ MK_CONST(NEWLINE_ANY),
|
|
|
+ MK_CONST(INFO_NEWLINE),
|
|
|
+ MK_CONST(INFO_ALLOPTIONS),
|
|
|
+ MK_CONST(CONFIG_VERSION),
|
|
|
+ MK_CONST(UTF),
|
|
|
+ MK_CONST(NO_UTF_CHECK),
|
|
|
+ MK_CONST(ERROR_UTF8_ERR1),
|
|
|
+ MK_CONST(CONFIG_JIT),
|
|
|
+ MK_CONST(JIT_COMPLETE),
|
|
|
+ MK_CONST(JIT_PARTIAL_HARD),
|
|
|
+ MK_CONST(JIT_PARTIAL_SOFT),
|
|
|
+ MK_CONST(AUTO_CALLOUT),
|
|
|
+ {0,0}
|
|
|
+};
|
|
|
+
|
|
|
+#ifdef __cplusplus
|
|
|
+extern "C" {
|
|
|
+#endif
|
|
|
+
|
|
|
+SQRESULT sqext_register_pcre2(HSQUIRRELVM v)
|
|
|
+{
|
|
|
+ sq_pushstring(v,PCRE2_Tag,-1);
|
|
|
+ sq_newclass(v,SQFalse);
|
|
|
+ sq_settypetag(v,-1,(void*)PCRE2_Tag);
|
|
|
+ sq_insert_reg_funcs(v, sq_pcre2_methods);
|
|
|
+
|
|
|
+ //add constants
|
|
|
+ KeyIntPtrType KeyIntPtr;
|
|
|
+ for (KeyIntPtr = sqpcre2_constants; KeyIntPtr->Str; KeyIntPtr++) {
|
|
|
+ sq_pushstring(v, KeyIntPtr->Str, -1); //first the key
|
|
|
+ sq_pushinteger(v, KeyIntPtr->Val); //then the value
|
|
|
+ sq_newslot(v, -3, SQFalse); //store then
|
|
|
+ }
|
|
|
+
|
|
|
+ sq_newslot(v,-3,SQTrue);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef __cplusplus
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+#endif //SQ_USE_PCRE2
|