diff options
Diffstat (limited to 'strlib.c')
-rw-r--r-- | strlib.c | 541 |
1 files changed, 0 insertions, 541 deletions
diff --git a/strlib.c b/strlib.c deleted file mode 100644 index 6d16043a..00000000 --- a/strlib.c +++ /dev/null | |||
@@ -1,541 +0,0 @@ | |||
1 | /* | ||
2 | ** strlib.c | ||
3 | ** String library to LUA | ||
4 | */ | ||
5 | |||
6 | char *rcs_strlib="$Id: strlib.c,v 1.46 1997/06/19 18:49:40 roberto Exp roberto $"; | ||
7 | |||
8 | #include <string.h> | ||
9 | #include <stdio.h> | ||
10 | #include <stdlib.h> | ||
11 | #include <ctype.h> | ||
12 | |||
13 | #include "lua.h" | ||
14 | #include "auxlib.h" | ||
15 | #include "lualib.h" | ||
16 | |||
17 | |||
18 | struct lbuff { | ||
19 | char *b; | ||
20 | size_t max; | ||
21 | size_t size; | ||
22 | }; | ||
23 | |||
24 | static struct lbuff lbuffer = {NULL, 0, 0}; | ||
25 | |||
26 | |||
27 | static char *strbuffer (unsigned long size) | ||
28 | { | ||
29 | if (size > lbuffer.max) { | ||
30 | /* ANSI "realloc" doesn't need this test, but some machines (Sun!) | ||
31 | don't follow ANSI */ | ||
32 | lbuffer.b = (lbuffer.b) ? realloc(lbuffer.b, lbuffer.max=size) : | ||
33 | malloc(lbuffer.max=size); | ||
34 | if (lbuffer.b == NULL) | ||
35 | lua_error("memory overflow"); | ||
36 | } | ||
37 | return lbuffer.b; | ||
38 | } | ||
39 | |||
40 | static char *openspace (unsigned long size) | ||
41 | { | ||
42 | char *buff = strbuffer(lbuffer.size+size); | ||
43 | return buff+lbuffer.size; | ||
44 | } | ||
45 | |||
46 | char *luaI_addchar (int c) | ||
47 | { | ||
48 | if (lbuffer.size >= lbuffer.max) | ||
49 | strbuffer(lbuffer.max == 0 ? 100 : lbuffer.max*2); | ||
50 | lbuffer.b[lbuffer.size++] = c; | ||
51 | return lbuffer.b; | ||
52 | } | ||
53 | |||
54 | void luaI_emptybuff (void) | ||
55 | { | ||
56 | lbuffer.size = 0; /* prepare for next string */ | ||
57 | } | ||
58 | |||
59 | |||
60 | static void addnchar (char *s, int n) | ||
61 | { | ||
62 | char *b = openspace(n); | ||
63 | strncpy(b, s, n); | ||
64 | lbuffer.size += n; | ||
65 | } | ||
66 | |||
67 | static void addstr (char *s) | ||
68 | { | ||
69 | addnchar(s, strlen(s)); | ||
70 | } | ||
71 | |||
72 | |||
73 | /* | ||
74 | ** Return the string length | ||
75 | */ | ||
76 | static void str_len (void) | ||
77 | { | ||
78 | lua_pushnumber(strlen(luaL_check_string(1))); | ||
79 | } | ||
80 | |||
81 | /* | ||
82 | ** Return the substring of a string | ||
83 | */ | ||
84 | static void str_sub (void) | ||
85 | { | ||
86 | char *s = luaL_check_string(1); | ||
87 | long l = strlen(s); | ||
88 | long start = (long)luaL_check_number(2); | ||
89 | long end = (long)luaL_opt_number(3, -1); | ||
90 | if (start < 0) start = l+start+1; | ||
91 | if (end < 0) end = l+end+1; | ||
92 | if (1 <= start && start <= end && end <= l) { | ||
93 | luaI_emptybuff(); | ||
94 | addnchar(s+start-1, end-start+1); | ||
95 | lua_pushstring(luaI_addchar(0)); | ||
96 | } | ||
97 | else lua_pushstring(""); | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | ** Convert a string to lower case. | ||
102 | */ | ||
103 | static void str_lower (void) | ||
104 | { | ||
105 | char *s; | ||
106 | luaI_emptybuff(); | ||
107 | for (s = luaL_check_string(1); *s; s++) | ||
108 | luaI_addchar(tolower((unsigned char)*s)); | ||
109 | lua_pushstring(luaI_addchar(0)); | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | ** Convert a string to upper case. | ||
114 | */ | ||
115 | static void str_upper (void) | ||
116 | { | ||
117 | char *s; | ||
118 | luaI_emptybuff(); | ||
119 | for (s = luaL_check_string(1); *s; s++) | ||
120 | luaI_addchar(toupper((unsigned char)*s)); | ||
121 | lua_pushstring(luaI_addchar(0)); | ||
122 | } | ||
123 | |||
124 | static void str_rep (void) | ||
125 | { | ||
126 | char *s = luaL_check_string(1); | ||
127 | int n = (int)luaL_check_number(2); | ||
128 | luaI_emptybuff(); | ||
129 | while (n-- > 0) | ||
130 | addstr(s); | ||
131 | lua_pushstring(luaI_addchar(0)); | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | ** get ascii value of a character in a string | ||
136 | */ | ||
137 | static void str_ascii (void) | ||
138 | { | ||
139 | char *s = luaL_check_string(1); | ||
140 | long pos = (long)luaL_opt_number(2, 1) - 1; | ||
141 | luaL_arg_check(0<=pos && pos<strlen(s), 2, "out of range"); | ||
142 | lua_pushnumber((unsigned char)s[pos]); | ||
143 | } | ||
144 | |||
145 | |||
146 | /* pattern matching */ | ||
147 | |||
148 | #define ESC '%' | ||
149 | #define SPECIALS "^$*?.([%-" | ||
150 | |||
151 | static char *bracket_end (char *p) | ||
152 | { | ||
153 | return (*p == 0) ? NULL : strchr((*p=='^') ? p+2 : p+1, ']'); | ||
154 | } | ||
155 | |||
156 | char *luaL_item_end (char *p) | ||
157 | { | ||
158 | switch (*p++) { | ||
159 | case '\0': return p-1; | ||
160 | case ESC: | ||
161 | if (*p == 0) luaL_verror("incorrect pattern (ends with `%c')", ESC); | ||
162 | return p+1; | ||
163 | case '[': { | ||
164 | char *end = bracket_end(p); | ||
165 | if (end == NULL) lua_error("incorrect pattern (missing `]')"); | ||
166 | return end+1; | ||
167 | } | ||
168 | default: | ||
169 | return p; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | static int matchclass (int c, int cl) | ||
174 | { | ||
175 | int res; | ||
176 | switch (tolower((unsigned char)cl)) { | ||
177 | case 'a' : res = isalpha((unsigned char)c); break; | ||
178 | case 'c' : res = iscntrl((unsigned char)c); break; | ||
179 | case 'd' : res = isdigit((unsigned char)c); break; | ||
180 | case 'l' : res = islower((unsigned char)c); break; | ||
181 | case 'p' : res = ispunct((unsigned char)c); break; | ||
182 | case 's' : res = isspace((unsigned char)c); break; | ||
183 | case 'u' : res = isupper((unsigned char)c); break; | ||
184 | case 'w' : res = isalnum((unsigned char)c); break; | ||
185 | default: return (cl == c); | ||
186 | } | ||
187 | return (islower((unsigned char)cl) ? res : !res); | ||
188 | } | ||
189 | |||
190 | int luaL_singlematch (int c, char *p) | ||
191 | { | ||
192 | if (c == 0) return 0; | ||
193 | switch (*p) { | ||
194 | case '.': return 1; | ||
195 | case ESC: return matchclass(c, *(p+1)); | ||
196 | case '[': { | ||
197 | char *end = bracket_end(p+1); | ||
198 | int sig = *(p+1) == '^' ? (p++, 0) : 1; | ||
199 | while (++p < end) { | ||
200 | if (*p == ESC) { | ||
201 | if (((p+1) < end) && matchclass(c, *++p)) return sig; | ||
202 | } | ||
203 | else if ((*(p+1) == '-') && (p+2 < end)) { | ||
204 | p+=2; | ||
205 | if (*(p-2) <= c && c <= *p) return sig; | ||
206 | } | ||
207 | else if (*p == c) return sig; | ||
208 | } | ||
209 | return !sig; | ||
210 | } | ||
211 | default: return (*p == c); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | #define MAX_CAPT 9 | ||
216 | |||
217 | static struct { | ||
218 | char *init; | ||
219 | int len; /* -1 signals unfinished capture */ | ||
220 | } capture[MAX_CAPT]; | ||
221 | |||
222 | static int num_captures; /* only valid after a sucessful call to match */ | ||
223 | |||
224 | |||
225 | static void push_captures (void) | ||
226 | { | ||
227 | int i; | ||
228 | for (i=0; i<num_captures; i++) { | ||
229 | int l = capture[i].len; | ||
230 | char *buff = openspace(l+1); | ||
231 | if (l == -1) lua_error("unfinished capture"); | ||
232 | strncpy(buff, capture[i].init, l); | ||
233 | buff[l] = 0; | ||
234 | lua_pushstring(buff); | ||
235 | } | ||
236 | } | ||
237 | |||
238 | static int check_cap (int l, int level) | ||
239 | { | ||
240 | l -= '1'; | ||
241 | if (!(0 <= l && l < level && capture[l].len != -1)) | ||
242 | lua_error("invalid capture index"); | ||
243 | return l; | ||
244 | } | ||
245 | |||
246 | static int capture_to_close (int level) | ||
247 | { | ||
248 | for (level--; level>=0; level--) | ||
249 | if (capture[level].len == -1) return level; | ||
250 | lua_error("invalid pattern capture"); | ||
251 | return 0; /* to avoid warnings */ | ||
252 | } | ||
253 | |||
254 | static char *matchbalance (char *s, int b, int e) | ||
255 | { | ||
256 | if (*s != b) return NULL; | ||
257 | else { | ||
258 | int cont = 1; | ||
259 | while (*(++s)) { | ||
260 | if (*s == e) { | ||
261 | if (--cont == 0) return s+1; | ||
262 | } | ||
263 | else if (*s == b) cont++; | ||
264 | } | ||
265 | } | ||
266 | return NULL; /* string ends out of balance */ | ||
267 | } | ||
268 | |||
269 | static char *match (char *s, char *p, int level) | ||
270 | { | ||
271 | init: /* using goto's to optimize tail recursion */ | ||
272 | switch (*p) { | ||
273 | case '(': /* start capture */ | ||
274 | if (level >= MAX_CAPT) lua_error("too many captures"); | ||
275 | capture[level].init = s; | ||
276 | capture[level].len = -1; | ||
277 | level++; p++; goto init; /* return match(s, p+1, level); */ | ||
278 | case ')': { /* end capture */ | ||
279 | int l = capture_to_close(level); | ||
280 | char *res; | ||
281 | capture[l].len = s - capture[l].init; /* close capture */ | ||
282 | if ((res = match(s, p+1, level)) == NULL) /* match failed? */ | ||
283 | capture[l].len = -1; /* undo capture */ | ||
284 | return res; | ||
285 | } | ||
286 | case ESC: | ||
287 | if (isdigit((unsigned char)(*(p+1)))) { /* capture */ | ||
288 | int l = check_cap(*(p+1), level); | ||
289 | if (strncmp(capture[l].init, s, capture[l].len) == 0) { | ||
290 | /* return match(p+2, s+capture[l].len, level); */ | ||
291 | p+=2; s+=capture[l].len; goto init; | ||
292 | } | ||
293 | else return NULL; | ||
294 | } | ||
295 | else if (*(p+1) == 'b') { /* balanced string */ | ||
296 | if (*(p+2) == 0 || *(p+3) == 0) | ||
297 | lua_error("bad balanced pattern specification"); | ||
298 | s = matchbalance(s, *(p+2), *(p+3)); | ||
299 | if (s == NULL) return NULL; | ||
300 | else { /* return match(p+4, s, level); */ | ||
301 | p+=4; goto init; | ||
302 | } | ||
303 | } | ||
304 | else goto dflt; | ||
305 | case '\0': case '$': /* (possibly) end of pattern */ | ||
306 | if (*p == 0 || (*(p+1) == 0 && *s == 0)) { | ||
307 | num_captures = level; | ||
308 | return s; | ||
309 | } | ||
310 | else goto dflt; | ||
311 | default: dflt: { /* it is a pattern item */ | ||
312 | int m = luaL_singlematch(*s, p); | ||
313 | char *ep = luaL_item_end(p); /* get what is next */ | ||
314 | switch (*ep) { | ||
315 | case '*': { /* repetition */ | ||
316 | char *res; | ||
317 | if (m && (res = match(s+1, p, level))) | ||
318 | return res; | ||
319 | p=ep+1; goto init; /* else return match(s, ep+1, level); */ | ||
320 | } | ||
321 | case '-': { /* repetition */ | ||
322 | char *res; | ||
323 | if ((res = match(s, ep+1, level)) != 0) | ||
324 | return res; | ||
325 | else if (m) { | ||
326 | s++; | ||
327 | goto init; /* return match(s+1, p, level); */ | ||
328 | } | ||
329 | else | ||
330 | return NULL; | ||
331 | } | ||
332 | case '?': { /* optional */ | ||
333 | char *res; | ||
334 | if (m && (res = match(s+1, ep+1, level))) | ||
335 | return res; | ||
336 | p=ep+1; goto init; /* else return match(s, ep+1, level); */ | ||
337 | } | ||
338 | default: | ||
339 | if (m) { s++; p=ep; goto init; } /* return match(s+1, ep, level); */ | ||
340 | else return NULL; | ||
341 | } | ||
342 | } | ||
343 | } | ||
344 | } | ||
345 | |||
346 | static void str_find (void) | ||
347 | { | ||
348 | char *s = luaL_check_string(1); | ||
349 | char *p = luaL_check_string(2); | ||
350 | long init = (long)luaL_opt_number(3, 1) - 1; | ||
351 | luaL_arg_check(0 <= init && init <= strlen(s), 3, "out of range"); | ||
352 | if (lua_getparam(4) != LUA_NOOBJECT || | ||
353 | strpbrk(p, SPECIALS) == NULL) { /* no special caracters? */ | ||
354 | char *s2 = strstr(s+init, p); | ||
355 | if (s2) { | ||
356 | lua_pushnumber(s2-s+1); | ||
357 | lua_pushnumber(s2-s+strlen(p)); | ||
358 | } | ||
359 | } | ||
360 | else { | ||
361 | int anchor = (*p == '^') ? (p++, 1) : 0; | ||
362 | char *s1=s+init; | ||
363 | do { | ||
364 | char *res; | ||
365 | if ((res=match(s1, p, 0)) != NULL) { | ||
366 | lua_pushnumber(s1-s+1); /* start */ | ||
367 | lua_pushnumber(res-s); /* end */ | ||
368 | push_captures(); | ||
369 | return; | ||
370 | } | ||
371 | } while (*s1++ && !anchor); | ||
372 | } | ||
373 | } | ||
374 | |||
375 | static void add_s (lua_Object newp, lua_Object table, int n) | ||
376 | { | ||
377 | if (lua_isstring(newp)) { | ||
378 | char *news = lua_getstring(newp); | ||
379 | while (*news) { | ||
380 | if (*news != ESC || !isdigit((unsigned char)*++news)) | ||
381 | luaI_addchar(*news++); | ||
382 | else { | ||
383 | int l = check_cap(*news++, num_captures); | ||
384 | addnchar(capture[l].init, capture[l].len); | ||
385 | } | ||
386 | } | ||
387 | } | ||
388 | else if (lua_isfunction(newp)) { | ||
389 | lua_Object res; | ||
390 | struct lbuff oldbuff; | ||
391 | int status; | ||
392 | lua_beginblock(); | ||
393 | if (lua_istable(table)) { | ||
394 | lua_pushobject(table); | ||
395 | lua_pushnumber(n); | ||
396 | } | ||
397 | push_captures(); | ||
398 | /* function may use lbuffer, so save it and create a new one */ | ||
399 | oldbuff = lbuffer; | ||
400 | lbuffer.b = NULL; lbuffer.max = lbuffer.size = 0; | ||
401 | status = lua_callfunction(newp); | ||
402 | /* restore old buffer */ | ||
403 | free(lbuffer.b); | ||
404 | lbuffer = oldbuff; | ||
405 | if (status != 0) | ||
406 | lua_error(NULL); | ||
407 | res = lua_getresult(1); | ||
408 | addstr(lua_isstring(res) ? lua_getstring(res) : ""); | ||
409 | lua_endblock(); | ||
410 | } | ||
411 | else luaL_arg_check(0, 3, NULL); | ||
412 | } | ||
413 | |||
414 | static void str_gsub (void) | ||
415 | { | ||
416 | char *src = luaL_check_string(1); | ||
417 | char *p = luaL_check_string(2); | ||
418 | lua_Object newp = lua_getparam(3); | ||
419 | lua_Object table = lua_getparam(4); | ||
420 | int max_s = (int)luaL_opt_number(lua_istable(table)?5:4, strlen(src)+1); | ||
421 | int anchor = (*p == '^') ? (p++, 1) : 0; | ||
422 | int n = 0; | ||
423 | luaI_emptybuff(); | ||
424 | while (n < max_s) { | ||
425 | char *e = match(src, p, 0); | ||
426 | if (e) { | ||
427 | n++; | ||
428 | add_s(newp, table, n); | ||
429 | } | ||
430 | if (e && e>src) /* non empty match? */ | ||
431 | src = e; /* skip it */ | ||
432 | else if (*src) | ||
433 | luaI_addchar(*src++); | ||
434 | else break; | ||
435 | if (anchor) break; | ||
436 | } | ||
437 | addstr(src); | ||
438 | lua_pushstring(luaI_addchar(0)); | ||
439 | lua_pushnumber(n); /* number of substitutions */ | ||
440 | } | ||
441 | |||
442 | static void str_set (void) | ||
443 | { | ||
444 | char *item = luaL_check_string(1); | ||
445 | int i; | ||
446 | luaL_arg_check(*luaL_item_end(item) == 0, 1, "wrong format"); | ||
447 | luaI_emptybuff(); | ||
448 | for (i=1; i<256; i++) /* 0 cannot be part of a set */ | ||
449 | if (luaL_singlematch(i, item)) | ||
450 | luaI_addchar(i); | ||
451 | lua_pushstring(luaI_addchar(0)); | ||
452 | } | ||
453 | |||
454 | |||
455 | void luaI_addquoted (char *s) | ||
456 | { | ||
457 | luaI_addchar('"'); | ||
458 | for (; *s; s++) { | ||
459 | if (strchr("\"\\\n", *s)) | ||
460 | luaI_addchar('\\'); | ||
461 | luaI_addchar(*s); | ||
462 | } | ||
463 | luaI_addchar('"'); | ||
464 | } | ||
465 | |||
466 | #define MAX_FORMAT 200 | ||
467 | |||
468 | static void str_format (void) | ||
469 | { | ||
470 | int arg = 1; | ||
471 | char *strfrmt = luaL_check_string(arg); | ||
472 | luaI_emptybuff(); /* initialize */ | ||
473 | while (*strfrmt) { | ||
474 | if (*strfrmt != '%') | ||
475 | luaI_addchar(*strfrmt++); | ||
476 | else if (*++strfrmt == '%') | ||
477 | luaI_addchar(*strfrmt++); /* %% */ | ||
478 | else { /* format item */ | ||
479 | char form[MAX_FORMAT]; /* store the format ('%...') */ | ||
480 | char *buff; | ||
481 | char *initf = strfrmt; | ||
482 | form[0] = '%'; | ||
483 | strfrmt = match(strfrmt, "%d?%$?[-+ #]*(%d*)%.?(%d*)", 0); | ||
484 | if (capture[0].len > 3 || capture[1].len > 3) /* < 1000? */ | ||
485 | lua_error("invalid format (width or precision too long)"); | ||
486 | if (isdigit((unsigned char)initf[0]) && initf[1] == '$') { | ||
487 | arg = initf[0] - '0'; | ||
488 | initf += 2; /* skip the 'n$' */ | ||
489 | } | ||
490 | arg++; | ||
491 | strncpy(form+1, initf, strfrmt-initf+1); /* +1 to include convertion */ | ||
492 | form[strfrmt-initf+2] = 0; | ||
493 | buff = openspace(1000); /* to store the formated value */ | ||
494 | switch (*strfrmt++) { | ||
495 | case 'q': | ||
496 | luaI_addquoted(luaL_check_string(arg)); | ||
497 | continue; | ||
498 | case 's': { | ||
499 | char *s = luaL_check_string(arg); | ||
500 | buff = openspace(strlen(s)); | ||
501 | sprintf(buff, form, s); | ||
502 | break; | ||
503 | } | ||
504 | case 'c': case 'd': case 'i': case 'o': | ||
505 | case 'u': case 'x': case 'X': | ||
506 | sprintf(buff, form, (int)luaL_check_number(arg)); | ||
507 | break; | ||
508 | case 'e': case 'E': case 'f': case 'g': | ||
509 | sprintf(buff, form, luaL_check_number(arg)); | ||
510 | break; | ||
511 | default: /* also treat cases 'pnLlh' */ | ||
512 | lua_error("invalid format option in function `format'"); | ||
513 | } | ||
514 | lbuffer.size += strlen(buff); | ||
515 | } | ||
516 | } | ||
517 | lua_pushstring(luaI_addchar(0)); /* push the result */ | ||
518 | } | ||
519 | |||
520 | |||
521 | static struct luaL_reg strlib[] = { | ||
522 | {"strlen", str_len}, | ||
523 | {"strsub", str_sub}, | ||
524 | {"strset", str_set}, | ||
525 | {"strlower", str_lower}, | ||
526 | {"strupper", str_upper}, | ||
527 | {"strrep", str_rep}, | ||
528 | {"ascii", str_ascii}, | ||
529 | {"format", str_format}, | ||
530 | {"strfind", str_find}, | ||
531 | {"gsub", str_gsub} | ||
532 | }; | ||
533 | |||
534 | |||
535 | /* | ||
536 | ** Open string library | ||
537 | */ | ||
538 | void strlib_open (void) | ||
539 | { | ||
540 | luaL_openlib(strlib, (sizeof(strlib)/sizeof(strlib[0]))); | ||
541 | } | ||