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