comparison mupdf-source/thirdparty/mujs/jsregexp.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 #include "jsi.h"
2 #include "regexp.h"
3
4 static char *escaperegexp(js_State *J, const char *pattern) {
5 char *copy, *p;
6 const char *s;
7 int n = 0;
8 for (s = pattern; *s; ++s) {
9 if (*s == '/')
10 ++n;
11 ++n;
12 }
13 copy = p = js_malloc(J, n+1);
14 for (s = pattern; *s; ++s) {
15 if (*s == '/')
16 *p++ = '\\';
17 *p++ = *s;
18 }
19 *p = 0;
20 return copy;
21 }
22
23 static void js_newregexpx(js_State *J, const char *pattern, int flags, int is_clone)
24 {
25 const char *error;
26 js_Object *obj;
27 Reprog *prog;
28 int opts;
29
30 obj = jsV_newobject(J, JS_CREGEXP, J->RegExp_prototype);
31
32 opts = 0;
33 if (flags & JS_REGEXP_I) opts |= REG_ICASE;
34 if (flags & JS_REGEXP_M) opts |= REG_NEWLINE;
35
36 prog = js_regcompx(J->alloc, J->actx, pattern, opts, &error);
37 if (!prog)
38 js_syntaxerror(J, "regular expression: %s", error);
39
40 obj->u.r.prog = prog;
41 obj->u.r.source = is_clone ? js_strdup(J, pattern) : escaperegexp(J, pattern);
42 obj->u.r.flags = flags;
43 obj->u.r.last = 0;
44 js_pushobject(J, obj);
45 }
46
47 void js_newregexp(js_State *J, const char *pattern, int flags)
48 {
49 js_newregexpx(J, pattern, flags, 0);
50 }
51
52 void js_RegExp_prototype_exec(js_State *J, js_Regexp *re, const char *text)
53 {
54 const char *haystack;
55 int result;
56 int i;
57 int opts;
58 Resub m;
59
60 haystack = text;
61 opts = 0;
62 if (re->flags & JS_REGEXP_G) {
63 if (re->last > strlen(haystack)) {
64 re->last = 0;
65 js_pushnull(J);
66 return;
67 }
68 if (re->last > 0) {
69 haystack = text + re->last;
70 opts |= REG_NOTBOL;
71 }
72 }
73
74 result = js_regexec(re->prog, haystack, &m, opts);
75 if (result < 0)
76 js_error(J, "regexec failed");
77 if (result == 0) {
78 js_newarray(J);
79 js_pushstring(J, text);
80 js_setproperty(J, -2, "input");
81 js_pushnumber(J, js_utfptrtoidx(text, m.sub[0].sp));
82 js_setproperty(J, -2, "index");
83 for (i = 0; i < m.nsub; ++i) {
84 js_pushlstring(J, m.sub[i].sp, m.sub[i].ep - m.sub[i].sp);
85 js_setindex(J, -2, i);
86 }
87 if (re->flags & JS_REGEXP_G)
88 re->last = m.sub[0].ep - text;
89 return;
90 }
91
92 if (re->flags & JS_REGEXP_G)
93 re->last = 0;
94
95 js_pushnull(J);
96 }
97
98 static void Rp_test(js_State *J)
99 {
100 js_Regexp *re;
101 const char *text;
102 int result;
103 int opts;
104 Resub m;
105
106 re = js_toregexp(J, 0);
107 text = js_tostring(J, 1);
108
109 opts = 0;
110 if (re->flags & JS_REGEXP_G) {
111 if (re->last > strlen(text)) {
112 re->last = 0;
113 js_pushboolean(J, 0);
114 return;
115 }
116 if (re->last > 0) {
117 text += re->last;
118 opts |= REG_NOTBOL;
119 }
120 }
121
122 result = js_regexec(re->prog, text, &m, opts);
123 if (result < 0)
124 js_error(J, "regexec failed");
125 if (result == 0) {
126 if (re->flags & JS_REGEXP_G)
127 re->last = re->last + (m.sub[0].ep - text);
128 js_pushboolean(J, 1);
129 return;
130 }
131
132 if (re->flags & JS_REGEXP_G)
133 re->last = 0;
134
135 js_pushboolean(J, 0);
136 }
137
138 static void jsB_new_RegExp(js_State *J)
139 {
140 js_Regexp *old;
141 const char *pattern;
142 int flags;
143 int is_clone = 0;
144
145 if (js_isregexp(J, 1)) {
146 if (js_isdefined(J, 2))
147 js_typeerror(J, "cannot supply flags when creating one RegExp from another");
148 old = js_toregexp(J, 1);
149 pattern = old->source;
150 flags = old->flags;
151 is_clone = 1;
152 } else if (js_isundefined(J, 1)) {
153 pattern = "(?:)";
154 flags = 0;
155 } else {
156 pattern = js_tostring(J, 1);
157 flags = 0;
158 }
159
160 if (strlen(pattern) == 0)
161 pattern = "(?:)";
162
163 if (js_isdefined(J, 2)) {
164 const char *s = js_tostring(J, 2);
165 int g = 0, i = 0, m = 0;
166 while (*s) {
167 if (*s == 'g') ++g;
168 else if (*s == 'i') ++i;
169 else if (*s == 'm') ++m;
170 else js_syntaxerror(J, "invalid regular expression flag: '%c'", *s);
171 ++s;
172 }
173 if (g > 1) js_syntaxerror(J, "invalid regular expression flag: 'g'");
174 if (i > 1) js_syntaxerror(J, "invalid regular expression flag: 'i'");
175 if (m > 1) js_syntaxerror(J, "invalid regular expression flag: 'm'");
176 if (g) flags |= JS_REGEXP_G;
177 if (i) flags |= JS_REGEXP_I;
178 if (m) flags |= JS_REGEXP_M;
179 }
180
181 js_newregexpx(J, pattern, flags, is_clone);
182 }
183
184 static void jsB_RegExp(js_State *J)
185 {
186 if (js_isregexp(J, 1))
187 return;
188 jsB_new_RegExp(J);
189 }
190
191 static void Rp_toString(js_State *J)
192 {
193 js_Regexp *re;
194 char * volatile out = NULL;
195
196 re = js_toregexp(J, 0);
197
198 if (js_try(J)) {
199 js_free(J, out);
200 js_throw(J);
201 }
202
203 out = js_malloc(J, strlen(re->source) + 6); /* extra space for //gim */
204 strcpy(out, "/");
205 strcat(out, re->source);
206 strcat(out, "/");
207 if (re->flags & JS_REGEXP_G) strcat(out, "g");
208 if (re->flags & JS_REGEXP_I) strcat(out, "i");
209 if (re->flags & JS_REGEXP_M) strcat(out, "m");
210
211 js_pop(J, 0);
212 js_pushstring(J, out);
213 js_endtry(J);
214 js_free(J, out);
215 }
216
217 static void Rp_exec(js_State *J)
218 {
219 js_RegExp_prototype_exec(J, js_toregexp(J, 0), js_tostring(J, 1));
220 }
221
222 void jsB_initregexp(js_State *J)
223 {
224 js_pushobject(J, J->RegExp_prototype);
225 {
226 jsB_propf(J, "RegExp.prototype.toString", Rp_toString, 0);
227 jsB_propf(J, "RegExp.prototype.test", Rp_test, 0);
228 jsB_propf(J, "RegExp.prototype.exec", Rp_exec, 0);
229 }
230 js_newcconstructor(J, jsB_RegExp, jsB_new_RegExp, "RegExp", 1);
231 js_defglobal(J, "RegExp", JS_DONTENUM);
232 }