lmtstrlibext.c /size: 33 Kb    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5/*tex
6
7    The relative ordering of the header files is important here, otherwise some of the defines that
8    are needed for lua_sdump come out wrong.
9
10*/
11
12/* todo: byteconcat and utf concat (no separator) */
13
14# include "luametatex.h"
15
16/*tex Helpers */
17
18inline static int strlib_aux_tounicode(const char *s, size_t l, size_t *p)
19{
20    unsigned char i = s[*p];
21    *p += 1;
22    if (i < 0x80) {
23        return i;
24    } else if (i >= 0xF0) {
25        if ((*p + 2) < l) {
26            unsigned char j = s[*p];
27            unsigned char k = s[*p + 1];
28            unsigned char l = s[*p + 2];
29            if (j >= 0x80 && k >= 0x80 && l >= 0x80) {
30                *p += 3;
31                return (((((i - 0xF0) * 0x40) + (j - 0x80)) * 0x40) + (k - 0x80)) * 0x40 + (l - 0x80);
32            }
33        }
34    } else if (i >= 0xE0) {
35        if ((*p + 1) < l) {
36            unsigned char j = s[*p];
37            unsigned char k = s[*p + 1];
38            if (j >= 0x80 && k >= 0x80) {
39               *p += 2;
40               return (((i - 0xE0) * 0x40) + (j - 0x80)) * 0x40 + (k - 0x80);
41            }
42        }
43    } else if (i >= 0xC0) {
44        if (*p < l) {
45            unsigned char j = s[*p];
46            if (j >= 0x80) {
47               *p += 1;
48               return ((i - 0xC0) * 0x40) + (j - 0x80);
49            }
50        }
51    }
52    return 0xFFFD;
53}
54
55inline static int strlib_aux_tounichar(const char *s, size_t l, size_t p)
56{
57    unsigned char i = s[p++];
58    if (i < 0x80) {
59        return 1;
60    } else if (i >= 0xF0) {
61        if ((p + 2) < l) {
62            unsigned char j = s[p];
63            unsigned char k = s[p + 1];
64            unsigned char l = s[p + 2];
65            if (j >= 0x80 && k >= 0x80 && l >= 0x80) {
66                return 4;
67            }
68        }
69    } else if (i >= 0xE0) {
70        if ((p + 1) < l) {
71            unsigned char j = s[p];
72            unsigned char k = s[p + 1];
73            if (j >= 0x80 && k >= 0x80) {
74                return 3;
75            }
76        }
77    } else if (i >= 0xC0) {
78        if (p < l) {
79            unsigned char j = s[p];
80            if (j >= 0x80) {
81                return 2;
82            }
83        }
84    }
85    return 0;
86}
87
88inline static size_t strlib_aux_toline(const char *s, size_t l, size_t p, size_t *b)
89{
90    size_t i = p;
91    while (i < l) {
92        if (s[i] == 13) {
93            if ((i + 1) < l) {
94                if (s[i + 1] == 10) {
95                    *b = 2; /* cr lf */
96                } else {
97                    *b = 1; /* cr */
98                }
99            }
100            return i - p;
101        } else if (s[i] == 10) {
102            *b = 1; /* lf */
103            return i - p;
104        } else {
105            /* other */
106            i += 1;
107        }
108    }
109    return i - p ;
110}
111
112/*tex End of helpers. */
113
114static int strlib_aux_bytepairs(lua_State *L)
115{
116    size_t ls = 0;
117    const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
118    size_t ind = lmt_tointeger(L, lua_upvalueindex(2));
119    if (ind < ls) {
120        unsigned char i;
121        /*tex iterator */
122        if (ind + 1 < ls) {
123            lua_pushinteger(L, ind + 2);
124        } else {
125            lua_pushinteger(L, ind + 1);
126        }
127        lua_replace(L, lua_upvalueindex(2));
128        i = (unsigned char)*(s + ind);
129        /*tex byte one */
130        lua_pushinteger(L, i);
131        if (ind + 1 < ls) {
132            /*tex byte two */
133            i = (unsigned char)*(s + ind + 1);
134            lua_pushinteger(L, i);
135        } else {
136            /*tex odd string length */
137            lua_pushnil(L);
138        }
139        return 2;
140    } else {
141        return 0;
142    }
143}
144
145static int strlib_bytepairs(lua_State *L)
146{
147    luaL_checkstring(L, 1);
148    lua_settop(L, 1);
149    lua_pushinteger(L, 0);
150    lua_pushcclosure(L, strlib_aux_bytepairs, 2);
151    return 1;
152}
153
154static int strlib_aux_bytes(lua_State *L)
155{
156    size_t ls = 0;
157    const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
158    size_t ind = lmt_tointeger(L, lua_upvalueindex(2));
159    if (ind < ls) {
160        /*tex iterator */
161        lua_pushinteger(L, ind + 1);
162        lua_replace(L, lua_upvalueindex(2));
163        /*tex byte */
164        lua_pushinteger(L, (unsigned char)*(s + ind));
165        return 1;
166    } else {
167        return 0;
168    }
169}
170
171static int strlib_bytes(lua_State *L)
172{
173    luaL_checkstring(L, 1);
174    lua_settop(L, 1);
175    lua_pushinteger(L, 0);
176    lua_pushcclosure(L, strlib_aux_bytes, 2);
177    return 1;
178}
179
180static int strlib_aux_utf_failed(lua_State *L, int new_ind)
181{
182    lua_pushinteger(L, new_ind);
183    lua_replace(L, lua_upvalueindex(2));
184    lua_pushliteral(L, utf_fffd_string);
185    return 1;
186}
187
188/* kind of complex ... these masks */
189
190static int strlib_aux_utfcharacters(lua_State *L)
191{
192    static const unsigned char mask[4] = { 0x80, 0xE0, 0xF0, 0xF8 };
193    static const unsigned char mequ[4] = { 0x00, 0xC0, 0xE0, 0xF0 };
194    size_t ls = 0;
195    const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
196    size_t ind = lmt_tointeger(L, lua_upvalueindex(2));
197    size_t l = ls;
198    if (ind >= l) {
199        return 0;
200    } else {
201        unsigned char c = (unsigned char) s[ind];
202        for (size_t j = 0; j < 4; j++) {
203            if ((c & mask[j]) == mequ[j]) {
204                if (ind + 1 + j > l) {
205                    /*tex The result will not fit. */
206                    return strlib_aux_utf_failed(L, (int) l);
207                }
208                for (size_t k = 1; k <= j; k++) {
209                    c = (unsigned char) s[ind + k];
210                    if ((c & 0xC0) != 0x80) {
211                        /*tex We have a bad follow byte. */
212                        return strlib_aux_utf_failed(L, (int) (ind + k));
213                    }
214                }
215                /*tex The iterator. */
216                lua_pushinteger(L, ind + j + 1);
217                lua_replace(L, lua_upvalueindex(2));
218                lua_pushlstring(L, ind + s, j + 1);
219                return 1;
220            }
221        }
222        return strlib_aux_utf_failed(L, (int) (ind + 1)); /* we found a follow byte! */
223    }
224}
225
226static int strlib_utfcharacters(lua_State *L)
227{
228    luaL_checkstring(L, 1);
229    lua_settop(L, 1);
230    lua_pushinteger(L, 0);
231    lua_pushcclosure(L, strlib_aux_utfcharacters, 2);
232    return 1;
233}
234
235static int strlib_aux_utfvalues(lua_State *L)
236{
237    size_t l = 0;
238    const char *s = lua_tolstring(L, lua_upvalueindex(1), &l);
239    size_t ind = lmt_tointeger(L, lua_upvalueindex(2));
240    if (ind < l) {
241        int v = strlib_aux_tounicode(s, l, &ind);
242        lua_pushinteger(L, ind);
243        lua_replace(L, lua_upvalueindex(2));
244        lua_pushinteger(L, v);
245        return 1;
246    } else {
247        return 0;
248    }
249}
250
251static int strlib_utfvalues(lua_State *L)
252{
253    luaL_checkstring(L, 1);
254    lua_settop(L, 1);
255    lua_pushinteger(L, 0);
256    lua_pushcclosure(L, strlib_aux_utfvalues, 2);
257    return 1;
258}
259
260static int strlib_aux_characterpairs(lua_State *L)
261{
262    size_t ls = 0;
263    const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
264    size_t ind = lmt_tointeger(L, lua_upvalueindex(2));
265    if (ind < ls) {
266        char b[1];
267        lua_pushinteger(L, ind + 2); /*tex So we can overshoot ls here. */
268        lua_replace(L, lua_upvalueindex(2));
269        b[0] = s[ind];
270        lua_pushlstring(L, b, 1);
271        if ((ind + 1) < ls) {
272            b[0] = s[ind + 1];
273            lua_pushlstring(L, b, 1);
274        } else {
275            lua_pushliteral(L, "");
276        }
277        return 2;
278    } else {
279        return 0;  /* string ended */
280    }
281}
282
283static int strlib_characterpairs(lua_State *L)
284{
285    luaL_checkstring(L, 1);
286    lua_settop(L, 1);
287    lua_pushinteger(L, 0);
288    lua_pushcclosure(L, strlib_aux_characterpairs, 2);
289    return 1;
290}
291
292static int strlib_aux_characters(lua_State *L)
293{
294    size_t ls = 0;
295    const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);
296    size_t ind = lmt_tointeger(L, lua_upvalueindex(2));
297    if (ind < ls) {
298        char b[1];
299        lua_pushinteger(L, ind + 1); /* iterator */
300        lua_replace(L, lua_upvalueindex(2));
301        b[0] = *(s + ind);
302        lua_pushlstring(L, b, 1);
303        return 1;
304    } else {
305        return 0;  /* string ended */
306    }
307}
308
309static int strlib_characters(lua_State *L)
310{
311    luaL_checkstring(L, 1);
312    lua_settop(L, 1);
313    lua_pushinteger(L, 0);
314    lua_pushcclosure(L, strlib_aux_characters, 2);
315    return 1;
316}
317
318static int strlib_bytetable(lua_State *L)
319{
320    size_t l;
321    const char *s = luaL_checklstring(L, 1, &l);
322    lua_createtable(L, (int) l, 0);
323    for (size_t i = 0; i < l; i++) {
324        lua_pushinteger(L, (unsigned char)*(s + i));
325        lua_rawseti(L, -2, i + 1);
326    }
327    return 1;
328}
329
330static int strlib_utfvaluetable(lua_State *L)
331{
332    size_t n = 1;
333    size_t l = 0;
334    size_t p = 0;
335    const char *s = luaL_checklstring(L, 1, &l);
336    lua_createtable(L, (int) l, 0);
337    while (p < l) {
338        lua_pushinteger(L, strlib_aux_tounicode(s, l, &p));
339        lua_rawseti(L, -2, n++);
340    }
341    return 1;
342}
343
344static int strlib_utfcharactertable(lua_State *L)
345{
346    size_t n = 1;
347    size_t l = 0;
348    size_t p = 0;
349    const char *s = luaL_checklstring(L, 1, &l);
350    lua_createtable(L, (int) l, 0);
351    while (p < l) {
352        int b = strlib_aux_tounichar(s, l, p);
353        if (b) {
354            lua_pushlstring(L, s + p, b);
355            p += b;
356        } else {
357            lua_pushliteral(L, utf_fffd_string);
358            p += 1;
359        }
360        lua_rawseti(L, -2, n++);
361    }
362    return 1;
363}
364
365static int strlib_linetable(lua_State *L)
366{
367    size_t n = 1;
368    size_t l = 0;
369    size_t p = 0;
370    const char *s = luaL_checklstring(L, 1, &l);
371    lua_createtable(L, (int) l, 0);
372    while (p < l) {
373        size_t b = 0;
374        size_t m = strlib_aux_toline(s, l, p, &b);
375        if (m) {
376            lua_pushlstring(L, s + p, m);
377        } else {
378            lua_pushliteral(L, "");
379        }
380        p += m + b;
381        lua_rawseti(L, -2, n++);
382    }
383    return 1;
384}
385
386/*tex
387
388    We provide a few helpers that we derived from the lua utf8 module and slunicode. That way we're
389    sort of covering a decent mix.
390
391*/
392
393# define MAXUNICODE 0x10FFFF
394
395/*tex
396
397    This is a combination of slunicode and utf8 converters but without mode and a bit faster on the
398    average than the utf8 one. The one character branch is a bit more efficient, as is preallocating
399    the buffer size.
400
401*/
402
403static int strlib_utfcharacter(lua_State *L) /* todo: use tounichar here too */
404{
405    int n = lua_gettop(L);
406    if (n == 1) {
407        char u[6];
408        char *c = aux_uni2string(&u[0], (unsigned) lua_tointeger(L, 1));
409        *c = '\0';
410        lua_pushstring(L, u);
411        return 1;
412    } else {
413        luaL_Buffer b;
414        luaL_buffinitsize(L, &b, (size_t) n * 4);
415        for (int i = 1; i <= n; i++) {
416            unsigned u = (unsigned) lua_tointeger(L, i);
417            if (u <= MAXUNICODE) {
418                if (0x80 > u) {
419                    luaL_addchar(&b, (unsigned char) u);
420                } else {
421                    if (0x800 > u)
422                        luaL_addchar(&b, (unsigned char) (0xC0 | (u >> 6)));
423                    else {
424                        if (0x10000 > u)
425                            luaL_addchar(&b, (unsigned char) (0xE0 | (u >> 12)));
426                        else {
427                            luaL_addchar(&b, (unsigned char) (0xF0 | (u >> 18)));
428                            luaL_addchar(&b, (unsigned char) (0x80 | (0x3F & (u >> 12))));
429                        }
430                        luaL_addchar(&b, 0x80 | (0x3F & (u >> 6)));
431                    }
432                    luaL_addchar(&b, 0x80 | (0x3F & u));
433                }
434            }
435        }
436        luaL_pushresult(&b);
437        return 1;
438    }
439}
440
441/*tex
442
443    The \UTF8 codepoint function takes two arguments, being positions in the string, while slunicode
444    byte takes two arguments representing the number of utf characters. The variant below always
445    returns all codepoints.
446
447*/
448
449static int strlib_utfvalue(lua_State *L)
450{
451    size_t l = 0;
452    size_t p = 0;
453    int i = 0;
454    const char *s = luaL_checklstring(L, 1, &l);
455    while (p < l) {
456        lua_pushinteger(L, strlib_aux_tounicode(s, l, &p));
457        i++;
458    }
459    return i;
460}
461
462/*tex This is a simplified version of utf8.len but without range. */
463
464static int strlib_utflength(lua_State *L)
465{
466    size_t ls = 0;
467    size_t ind = 0;
468    size_t n = 0;
469    const char *s = lua_tolstring(L, 1, &ls);
470    while (ind < ls) {
471        unsigned char i = (unsigned char) *(s + ind);
472        if (i < 0x80) {
473            ind += 1;
474        } else if (i >= 0xF0) {
475            ind += 4;
476        } else if (i >= 0xE0) {
477            ind += 3;
478        } else if (i >= 0xC0) {
479            ind += 2;
480        } else {
481            /*tex bad news, stupid recovery */
482            ind += 1;
483        }
484        n++;
485    }
486    lua_pushinteger(L, n);
487    return 1;
488}
489
490/*tex A handy one that formats a float but also strips trailing zeros. */
491
492static int strlib_format_f6(lua_State *L)
493{
494    double n = luaL_optnumber(L, 1, 0.0);
495    if (n == 0.0) {
496        lua_pushliteral(L, "0");
497    } else if (n == 1.0) {
498        lua_pushliteral(L, "1");
499    } else {
500        char s[128];
501        int i, l;
502        /* we should check for max int */
503        if (fmod(n, 1) == 0) {
504            i = snprintf(s, 128, "%i", (int) n);
505        } else {
506            if (lua_type(L, 2) == LUA_TSTRING) {
507                const char *f = lua_tostring(L, 2);
508                i = snprintf(s, 128, f, n);
509            } else {
510                i = snprintf(s, 128, "%0.6f", n) ;
511            }
512            l = i - 1;
513            while (l > 1) {
514                if (s[l - 1] == '.') {
515                    break;
516                } else if (s[l] == '0') {
517                    s[l] = '\0';
518                    --i;
519                } else {
520                    break;
521                }
522                l--;
523            }
524        }
525        lua_pushlstring(L, s, i);
526    }
527    return 1;
528}
529
530/*tex
531    The next one is mostly provided as check because doing it in pure \LUA\ is not slower and it's
532    not a bottleneck anyway. There are soms subtle side effects when we don't check for these ranges,
533    especially the trigger bytes (|0xD7FF| etc.) because we can get negative numbers which means
534    wrapping around and such.
535*/
536
537inline static unsigned char strlib_aux_hexdigit(unsigned char n) {
538    return (n < 10 ? '0' : 'A' - 10) + n;
539}
540
541# define invalid_unicode(u) ( \
542    (u >= 0x00E000 && u <= 0x00F8FF) || \
543    (u >= 0x0F0000 && u <= 0x0FFFFF) || \
544    (u >= 0x100000 && u <= 0x10FFFF) || \
545 /* (u >= 0x00D800 && u <= 0x00DFFF)) { */ \
546    (u >= 0x00D7FF && u <= 0x00DFFF) \
547)
548
549static int strlib_format_tounicode16(lua_State *L)
550{
551    lua_Integer u = lua_tointeger(L, 1);
552    if (invalid_unicode(u)) {
553        lua_pushliteral(L, "FFFD");
554    } else if (u < 0xD7FF || (u > 0xDFFF && u <= 0xFFFF)) {
555        char s[4] ;
556        s[3] = strlib_aux_hexdigit((unsigned char) ((u & 0x000F) >>  0));
557        s[2] = strlib_aux_hexdigit((unsigned char) ((u & 0x00F0) >>  4));
558        s[1] = strlib_aux_hexdigit((unsigned char) ((u & 0x0F00) >>  8));
559        s[0] = strlib_aux_hexdigit((unsigned char) ((u & 0xF000) >> 12));
560        lua_pushlstring(L, s, 4);
561    } else {
562        unsigned u1, u2;
563        char     s[8] ;
564        u = u - 0x10000; /* negative when invalid range */
565        u1 = (unsigned) (u >> 10) + 0xD800;
566        u2 = (unsigned) (u % 0x400) + 0xDC00;
567        s[3] = strlib_aux_hexdigit((unsigned char) ((u1 & 0x000F) >>  0));
568        s[2] = strlib_aux_hexdigit((unsigned char) ((u1 & 0x00F0) >>  4));
569        s[1] = strlib_aux_hexdigit((unsigned char) ((u1 & 0x0F00) >>  8));
570        s[0] = strlib_aux_hexdigit((unsigned char) ((u1 & 0xF000) >> 12));
571        s[7] = strlib_aux_hexdigit((unsigned char) ((u2 & 0x000F) >>  0));
572        s[6] = strlib_aux_hexdigit((unsigned char) ((u2 & 0x00F0) >>  4));
573        s[5] = strlib_aux_hexdigit((unsigned char) ((u2 & 0x0F00) >>  8));
574        s[4] = strlib_aux_hexdigit((unsigned char) ((u2 & 0xF000) >> 12));
575        lua_pushlstring(L, s, 8);
576    }
577    return 1;
578}
579
580static int strlib_format_toutf8(lua_State *L) /* could be integrated into utfcharacter */
581{
582    if (lua_type(L, 1) == LUA_TTABLE) {
583        lua_Integer n = lua_rawlen(L, 1);
584        if (n > 0) {
585            luaL_Buffer b;
586            luaL_buffinitsize(L, &b, (n + 1) * 4);
587            for (lua_Integer i = 0; i <= n; i++) {
588                /* there should be one operation for getting a number from a table */
589                if (lua_rawgeti(L, 1, i) == LUA_TNUMBER) {
590                    unsigned u = (unsigned) lua_tointeger(L, -1);
591                    if (0x80 > u) {
592                        luaL_addchar(&b, (unsigned char) u);
593                    } else if (invalid_unicode(u)) {
594                        luaL_addchar(&b, 0xFF);
595                        luaL_addchar(&b, 0xFD);
596                    } else {
597                        if (0x800 > u)
598                            luaL_addchar(&b, (unsigned char) (0xC0 | (u >> 6)));
599                        else {
600                            if (0x10000 > u)
601                                luaL_addchar(&b, (unsigned char) (0xE0 | (u >> 12)));
602                            else {
603                                luaL_addchar(&b, (unsigned char) (0xF0 | (u >>18)));
604                                luaL_addchar(&b, (unsigned char) (0x80 | (0x3F & (u >> 12))));
605                            }
606                            luaL_addchar(&b, 0x80 | (0x3F & (u >> 6)));
607                        }
608                        luaL_addchar(&b, 0x80 | (0x3F & u));
609                    }
610                }
611                lua_pop(L, 1);
612            }
613            luaL_pushresult(&b);
614        } else {
615            lua_pushliteral(L, "");
616        }
617        return 1;
618    }
619    return 0;
620}
621
622/*
623static int strlib_format_toutf16(lua_State* L) {
624    if (lua_type(L, 1) == LUA_TTABLE) {
625        lua_Integer n = lua_rawlen(L, 1);
626        if (n > 0) {
627            luaL_Buffer b;
628            luaL_buffinitsize(L, &b, (n + 2) * 4);
629            for (lua_Integer i = 0; i <= n; i++) {
630                if (lua_rawgeti(L, 1, i) == LUA_TNUMBER) {
631                    unsigned u = (unsigned) lua_tointeger(L, -1);
632                    if (invalid_unicode(u)) {
633                        luaL_addchar(&b, 0xFF);
634                        luaL_addchar(&b, 0xFD);
635                    } else if (u < 0x10000) {
636                        luaL_addchar(&b, (unsigned char) ((u & 0x00FF)     ));
637                        luaL_addchar(&b, (unsigned char) ((u & 0xFF00) >> 8));
638                    } else {
639                        u = u - 0x10000;
640                        luaL_addchar(&b, (unsigned char) ((((u>>10)+0xD800) & 0x00FF)     ));
641                        luaL_addchar(&b, (unsigned char) ((((u>>10)+0xD800) & 0xFF00) >> 8));
642                        luaL_addchar(&b, (unsigned char) (( (u%1024+0xDC00) & 0x00FF)     ));
643                        luaL_addchar(&b, (unsigned char) (( (u%1024+0xDC00) & 0xFF00) >> 8));
644                    }
645                }
646                lua_pop(L, 1);
647            }
648            luaL_addchar(&b, 0);
649            luaL_addchar(&b, 0);
650            luaL_pushresult(&b);
651        } else {
652            lua_pushliteral(L, "");
653        }
654        return 1;
655    }
656    return 0;
657}
658*/
659
660static int strlib_format_toutf32(lua_State *L)
661{
662    if (lua_type(L, 1) == LUA_TTABLE) {
663        lua_Integer n = lua_rawlen(L, 1);
664        if (n > 0) {
665            luaL_Buffer b;
666            luaL_buffinitsize(L, &b, (n + 2) * 4);
667            for (lua_Integer i = 0; i <= n; i++) {
668                /* there should be one operation for getting a number from a table */
669                if (lua_rawgeti(L, 1, i) == LUA_TNUMBER) {
670                    unsigned u = (unsigned) lua_tointeger(L, -1);
671                    if (invalid_unicode(u)) {
672                        luaL_addchar(&b, 0x00);
673                        luaL_addchar(&b, 0x00);
674                        luaL_addchar(&b, 0xFF);
675                        luaL_addchar(&b, 0xFD);
676                    } else {
677                        luaL_addchar(&b, (unsigned char) ((u & 0x000000FF)      ));
678                        luaL_addchar(&b, (unsigned char) ((u & 0x0000FF00) >>  8));
679                        luaL_addchar(&b, (unsigned char) ((u & 0x00FF0000) >> 16));
680                        luaL_addchar(&b, (unsigned char) ((u & 0xFF000000) >> 24));
681                    }
682                }
683                lua_pop(L, 1);
684            }
685            for (int i = 0; i <= 3; i++) {
686                luaL_addchar(&b, 0);
687            }
688            luaL_pushresult(&b);
689        } else {
690            lua_pushliteral(L, "");
691        }
692        return 1;
693    }
694    return 0;
695}
696
697// static char map[] = {
698//     '0', '1', '2', '3',
699//     '4', '5', '6', '7',
700//     '8', '9', 'A', 'B',
701//     'C', 'D', 'E', 'F',
702// };
703
704static int strlib_pack_rows_columns(lua_State* L)
705{
706    if (lua_type(L, 1) == LUA_TTABLE) {
707        lua_Integer rows = lua_rawlen(L, 1);
708        if (lua_rawgeti(L, 1, 1) == LUA_TTABLE) {
709            lua_Integer columns = lua_rawlen(L, -1);
710            switch (lua_rawgeti(L, -1, 1)) {
711                case LUA_TNUMBER:
712                    {
713                        lua_Integer size = rows * columns;
714                        unsigned char *result = lmt_memory_malloc(size);
715                        lua_pop(L, 2); /* row and cell */
716                        if (result) {
717                            unsigned char *first = result;
718                            for (lua_Integer r = 1; r <= rows; r++) {
719                                if (lua_rawgeti(L, -1, r) == LUA_TTABLE) {
720                                    for (lua_Integer c = 1; c <= columns; c++) {
721                                        if (lua_rawgeti(L, -1, c) == LUA_TNUMBER) {
722                                             lua_Integer v = lua_tointeger(L, -1);
723                                            *result++ = v < 0 ? 0 : v > 255 ? 255 : (unsigned char) v;
724                                        } else { 
725                                            *result++ = 0;
726                                        }
727                                        lua_pop(L, 1);
728                                    }
729                                }
730                                lua_pop(L, 1);
731                            }
732                            lua_pushlstring(L, (char *) first, result - first);
733                            return 1;
734                        }
735                    }
736                case LUA_TTABLE:
737                    {
738                        lua_Integer mode = lua_rawlen(L, -1);
739                        lua_Integer size = rows * columns * mode;
740                        unsigned char *result = lmt_memory_malloc(size);
741                        lua_pop(L, 2); /* row and cell */
742                        if (result) {
743                            unsigned char *first = result;
744                            for (lua_Integer r = 1; r <= rows; r++) {
745                                if (lua_rawgeti(L, -1, r) == LUA_TTABLE) {
746                                    for (lua_Integer c = 1; c <= columns; c++) {
747                                        if (lua_rawgeti(L, -1, c) == LUA_TTABLE) {
748                                            for (int i = 1; i <= mode; i++) {
749                                                if (lua_rawgeti(L, -1, i) == LUA_TNUMBER) {
750                                                    lua_Integer v = lua_tointeger(L, -1);
751                                                    *result++ = v < 0 ? 0 : v > 255 ? 255 : (unsigned char) v;
752                                                } else { 
753                                                    *result++ = 0;
754                                                }
755                                                lua_pop(L, 1);
756                                            }
757                                        }
758                                        lua_pop(L, 1);
759                                    }
760                                }
761                                lua_pop(L, 1);
762                            }
763                            lua_pushlstring(L, (char *) first, result - first);
764                            return 1;
765                        }
766                    }
767            }
768        }
769    }
770    lua_pushnil(L);
771    return 1;
772}
773
774/*tex 
775    This converts a hex string to characters. Spacing is ignored and invalid characters result in 
776    a false result. EMpty strings are okay. 
777*/
778
779static int strlib_hextocharacters(lua_State *L)
780{
781    size_t ls = 0;
782    const char *s = lua_tolstring(L, 1, &ls);
783    if (ls > 0) {
784        luaL_Buffer b;
785        luaL_buffinitsize(L, &b, ls/2);
786        while (1) { 
787            unsigned char first = *s++;
788            switch (first) {
789                case ' ': case '\n': case '\r': case '\t':
790                    continue;
791                case '\0': 
792                    goto DONE;
793                default: 
794                    {
795                        unsigned char second = *s++;
796                        switch (second) {
797                            case ' ': case '\n': case '\r': case '\t':
798                                continue;
799                            case '\0': 
800                                goto BAD;
801                            default: 
802                                { 
803                                    unsigned char chr;
804                                    if (first >= '0' && first <= '9') {
805                                        chr = 16 * (first - '0');
806                                    } else if (first>= 'A' && first <= 'F') {
807                                        chr = 16 * (first - 'A' + 10);
808                                    } else if (first >= 'a' && first <= 'f') {
809                                        chr = 16 * (first - 'a' + 10);
810                                    } else { 
811                                        goto BAD;
812                                    }
813                                    if (second >= '0' && second <= '9') {
814                                        chr += second - '0';
815                                    } else if (second >= 'A' && second <= 'F') {
816                                        chr += second - 'A' + 10;
817                                    } else if (first >= 'a' && second <= 'f') {
818                                        chr += second - 'a' + 10;
819                                    } else { 
820                                        goto BAD;
821                                    }
822                                    luaL_addchar(&b, chr);
823                                    break;
824                                }
825                        }
826                        break;
827                    }
828            }
829        }
830      DONE:
831        luaL_pushresult(&b);
832        return 1;
833      BAD:
834        lua_pushboolean(L, 0);
835        return 1;
836    } else { 
837        lua_pushliteral(L, "");
838        return 1;
839    }
840}
841
842static int strlib_octtointeger(lua_State *L)
843{
844    const char *s = lua_tostring(L, 1);
845//  lua_Integer n = 0;
846//     int negate = *s == '-';
847//     if (negate) {
848//         s++;
849//     }
850//     while (*s && n < 0xFFFFFFFF) { /* large enough */
851//         if (*s >= '0' && *s <= '7') {
852//             n = n * 8 + *s - '0';
853//         } else { 
854//             break;
855//         }
856//         s++;
857//     }    
858//  lua_pushinteger(L, negate ? -n : n);
859    lua_pushinteger(L, strtoul(s, NULL, 8));
860    return 1; 
861}
862
863static int strlib_dectointeger(lua_State *L)
864{
865    const char *s = lua_tostring(L, 1);
866//  lua_Integer n = 0;
867//  int negate = *s == '-';
868//  if (negate) {
869//      s++;
870//  }
871//  while (*s && n < 0xFFFFFFFF) { /* large enough */
872//      if (*s >= '0' && *s <= '9') {
873//          n = n * 10 + *s - '0';
874//      } else { 
875//          break;
876//      }
877//      s++;
878//  }    
879//  lua_pushinteger(L, negate ? -n : n);
880//  lua_pushinteger(L, atol(s));
881    lua_pushinteger(L, strtoul(s, NULL, 10));
882    return 1; 
883}
884
885static int strlib_hextointeger(lua_State *L)
886{
887    const char *s = lua_tostring(L, 1);
888//  lua_Integer n = 0;
889//  int negate = *s == '-';
890//  if (negate) {
891//      s++;
892//  }
893//  while (*s && n < 0xFFFFFFFF) { /* large enough */
894//      if (*s >= '0' && *s <= '9') {
895//          n = n * 16 + *s - '0';
896//      } else if (*s >= 'A' && *s <= 'F') {
897//          n = n * 16 + *s - 'A' + 10;
898//      } else if (*s >= 'a' && *s <= 'f') {
899//          n = n * 16 + *s - 'a' + 10;
900//      } else { 
901//          break;
902//      }
903//      s++;
904//  }    
905//  lua_pushinteger(L, negate ? -n : n);
906    lua_pushinteger(L, strtoul(s, NULL, 16));
907    return 1; 
908}
909
910static int strlib_chrtointeger(lua_State *L)
911{
912    lua_Integer n = 0;
913    size_t l = 0;
914    const char *s = lua_tolstring(L, 1, &l);
915    if (l > 0) {
916        size_t p = 0;
917        while (p < l && n < 0xFFFFFFFF) { /* large enough */
918            n = n * 255 + (unsigned char) s[p];
919            p++;
920        }    
921        lua_pushinteger(L, n);
922    }
923    return 1; 
924}
925
926static const luaL_Reg strlib_function_list[] = {
927    { "characters",        strlib_characters         },
928    { "characterpairs",    strlib_characterpairs     },
929    { "bytes",             strlib_bytes              },
930    { "bytepairs",         strlib_bytepairs          },
931    { "bytetable",         strlib_bytetable          },
932    { "linetable",         strlib_linetable          },
933    { "utfvalues",         strlib_utfvalues          },
934    { "utfcharacters",     strlib_utfcharacters      },
935    { "utfcharacter",      strlib_utfcharacter       },
936    { "utfvalue",          strlib_utfvalue           },
937    { "utflength",         strlib_utflength          },
938    { "utfvaluetable",     strlib_utfvaluetable      },
939    { "utfcharactertable", strlib_utfcharactertable  },
940    { "f6",                strlib_format_f6          },
941    { "tounicode16",       strlib_format_tounicode16 },
942    { "toutf8",            strlib_format_toutf8      },
943 /* { "toutf16",           strlib_format_toutf16     }, */ /* untested */
944    { "toutf32",           strlib_format_toutf32     },
945    { "packrowscolumns",   strlib_pack_rows_columns  },
946    { "hextocharacters",   strlib_hextocharacters    },
947    { "octtointeger",      strlib_octtointeger       },
948    { "dectointeger",      strlib_dectointeger       },
949    { "hextointeger",      strlib_hextointeger       },
950    { "chrtointeger",      strlib_chrtointeger       },
951    { NULL,                NULL                      },
952};
953
954int luaextend_string(lua_State * L)
955{
956    lua_getglobal(L, "string");
957    for (const luaL_Reg *lib = strlib_function_list; lib->name; lib++) {
958        lua_pushcfunction(L, lib->func);
959        lua_setfield(L, -2, lib->name);
960    }
961    lua_pop(L, 1);
962    return 1;
963}
964
965/*
966    The next (old, moved here) experiment was used to check if using some buffer is more efficient
967    than using a table that we concat. It makes no difference. If we ever use this, the initializer
968    |luaextend_string_buffer| will me merged into |luaextend_string|. We could gain a little on a
969    bit more efficient |luaL_checkudata| as we use elsewhere because in practice (surprise) its
970    overhead makes buffers like this {\em 50 percent} slower than the concatinated variant and
971    twice as slow when we reuse a temporary table. It's just better to stay at the \LUA\ end.
972*/
973
974/*
975# define STRING_BUFFER_METATABLE "string.buffer"
976
977typedef struct lmt_string_buffer {
978    char   *buffer;
979    size_t  length;
980    size_t  size;
981    size_t  step;
982    size_t  padding;
983} lmt_string_buffer;
984
985static int strlib_buffer_gc(lua_State* L)
986{
987    lmt_string_buffer *b = (lmt_string_buffer *) luaL_checkudata(L, 1, STRING_BUFFER_METATABLE);
988    if (b && b->buffer) {
989        lmt_memory_free(b->buffer);
990    }
991    return 0;
992}
993
994static int strlib_buffer_new(lua_State* L)
995{
996    size_t size = lmt_optsizet(L, 1, LUAL_BUFFERSIZE);
997    size_t step = lmt_optsizet(L, 2, size);
998    lmt_string_buffer *b = (lmt_string_buffer *) lua_newuserdatauv(L, sizeof(lmt_string_buffer), 0);
999    b->buffer = lmt_memory_malloc(size);
1000    b->size   = size;
1001    b->step   = step;
1002    b->length = 0;
1003    luaL_setmetatable(L, STRING_BUFFER_METATABLE);
1004    return 1;
1005
1006}
1007
1008static int strlib_buffer_add(lua_State* L)
1009{
1010    lmt_string_buffer *b = (lmt_string_buffer *) luaL_checkudata(L, 1,  STRING_BUFFER_METATABLE);
1011    switch (lua_type(L, 2)) {
1012        case LUA_TSTRING:
1013            {
1014                size_t l;
1015                const char *s = lua_tolstring(L, 2, &l);
1016                size_t length = b->length + l;
1017                if (length >= b->size) {
1018                    while (length >= b->size) {
1019                         b->size += b->step;
1020                    }
1021                    b->buffer = lmt_memory_realloc(b->buffer, b->size);
1022                }
1023                memcpy(&b->buffer[b->length], s, l);
1024                b->length = length;
1025            }
1026            break;
1027        default:
1028            break;
1029    }
1030    return 0;
1031}
1032
1033static int strlib_buffer_get_data(lua_State* L)
1034{
1035    lmt_string_buffer *b = (lmt_string_buffer *) luaL_checkudata(L, 1,  STRING_BUFFER_METATABLE);
1036    if (b->buffer) {
1037        lua_pushlstring(L, b->buffer, b->length);
1038        lua_pushinteger(L, (int) b->length);
1039        return 2;
1040    } else {
1041        lua_pushnil(L);
1042        return 1;
1043    }
1044}
1045
1046static int strlib_buffer_get_size(lua_State* L)
1047{
1048    lmt_string_buffer *b = (lmt_string_buffer *) luaL_checkudata(L, 1,  STRING_BUFFER_METATABLE);
1049    lua_pushinteger(L, b->length);
1050    return 1;
1051}
1052
1053static const luaL_Reg strlib_function_list_buffer[] = {
1054    { "newbuffer",         strlib_buffer_new         },
1055    { "addtobuffer",       strlib_buffer_add         },
1056    { "getbufferdata",     strlib_buffer_get_data    },
1057    { "getbuffersize",     strlib_buffer_get_size    },
1058    { NULL,                NULL                      },
1059};
1060
1061int luaextend_string_buffer(lua_State * L)
1062{
1063    lua_getglobal(L, "string");
1064    for (const luaL_Reg *lib = strlib_function_list_buffer; lib->name; lib++) {
1065        lua_pushcfunction(L, lib->func);
1066        lua_setfield(L, -2, lib->name);
1067    }
1068    lua_pop(L, 1);
1069    luaL_newmetatable(L, STRING_BUFFER_METATABLE);
1070    lua_pushcfunction(L, strlib_buffer_gc);
1071    lua_setfield(L, -2, "__gc");
1072    lua_pop(L, 1);
1073    return 1;
1074}
1075
1076*/
1077