source: trunk/libtransmission/jsonsl.c

Last change on this file was 14216, checked in by jordan, 7 years ago

(trunk, libT) allocate in jsonsl, allocate jpr_root using the correct integral type. upstream xref: https://github.com/mnunberg/jsonsl/issues/6

File size: 38.5 KB
Line 
1/*
2 * jsonsl
3 * https://github.com/mnunberg/jsonsl
4 *
5 * Copyright (c) 2012 M. Nunberg, mnunberg@haskalah.org
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27#include "jsonsl.h"
28#include <assert.h>
29#include <ctype.h>
30
31#ifdef JSONSL_USE_METRICS
32#define XMETRICS \
33    X(STRINGY_INSIGNIFICANT) \
34    X(STRINGY_SLOWPATH) \
35    X(ALLOWED_WHITESPACE) \
36    X(QUOTE_FASTPATH) \
37    X(SPECIAL_FASTPATH) \
38    X(SPECIAL_WSPOP) \
39    X(SPECIAL_SLOWPATH) \
40    X(GENERIC) \
41    X(STRUCTURAL_TOKEN) \
42    X(SPECIAL_SWITCHFIRST) \
43    X(STRINGY_CATCH) \
44    X(ESCAPES) \
45    X(TOTAL) \
46
47struct jsonsl_metrics_st {
48#define X(m) \
49    unsigned long metric_##m;
50    XMETRICS
51#undef X
52};
53
54static struct jsonsl_metrics_st GlobalMetrics = { 0 };
55static unsigned long GenericCounter[0x100] = { 0 };
56static unsigned long StringyCatchCounter[0x100] = { 0 };
57
58#define INCR_METRIC(m) \
59    GlobalMetrics.metric_##m++;
60
61#define INCR_GENERIC(c) \
62        INCR_METRIC(GENERIC); \
63        GenericCounter[c]++; \
64
65#define INCR_STRINGY_CATCH(c) \
66    INCR_METRIC(STRINGY_CATCH); \
67    StringyCatchCounter[c]++;
68
69JSONSL_API
70void jsonsl_dump_global_metrics(void)
71{
72    int ii;
73    printf("JSONSL Metrics:\n");
74#define X(m) \
75    printf("\t%-30s %20lu (%0.2f%%)\n", #m, GlobalMetrics.metric_##m, \
76           (float)((float)(GlobalMetrics.metric_##m/(float)GlobalMetrics.metric_TOTAL)) * 100);
77    XMETRICS
78#undef X
79    printf("Generic Characters:\n");
80    for (ii = 0; ii < 0xff; ii++) {
81        if (GenericCounter[ii]) {
82            printf("\t[ %c ] %lu\n", ii, GenericCounter[ii]);
83        }
84    }
85    printf("Weird string loop\n");
86    for (ii = 0; ii < 0xff; ii++) {
87        if (StringyCatchCounter[ii]) {
88            printf("\t[ %c ] %lu\n", ii, StringyCatchCounter[ii]);
89        }
90    }
91}
92
93#else
94#define INCR_METRIC(m)
95#define INCR_GENERIC(c)
96#define INCR_STRINGY_CATCH(c)
97JSONSL_API
98void jsonsl_dump_global_metrics(void) { }
99#endif /* JSONSL_USE_METRICS */
100
101#define CASE_DIGITS \
102case '1': \
103case '2': \
104case '3': \
105case '4': \
106case '5': \
107case '6': \
108case '7': \
109case '8': \
110case '9': \
111case '0':
112
113
114
115/**
116 * This table (predeclared) contains characters which are recognized
117 * non-string values.
118 */
119static jsonsl_special_t *Special_table;
120#define extract_special(c) \
121    Special_table[(unsigned int)(c & 0xff)]
122
123/**
124 * This table (predeclared) contains the tokens and other characters
125 * which signal the termination of the non-string values.
126 */
127static int *Special_Endings;
128#define is_special_end(c) \
129    Special_Endings[(unsigned int)c & 0xff]
130
131/**
132 * This table contains entries for the allowed whitespace
133 * as per RFC 4627
134 */
135static int *Allowed_Whitespace;
136#define is_allowed_whitespace(c) \
137    (c == ' ' || Allowed_Whitespace[(unsigned int)c & 0xff])
138
139
140/**
141 * This table contains allowed two-character escapes
142 * as per the RFC
143 */
144static int *Allowed_Escapes;
145#define is_allowed_escape(c) \
146    Allowed_Escapes[(unsigned int)c & 0xff]
147
148JSONSL_API
149jsonsl_t jsonsl_new(int nlevels)
150{
151    struct jsonsl_st *jsn =
152            calloc(1, sizeof (*jsn) +
153                    ( (nlevels-1) * sizeof (struct jsonsl_state_st) )
154            );
155
156    jsn->levels_max = nlevels;
157    jsn->max_callback_level = -1;
158    jsonsl_reset(jsn);
159    return jsn;
160}
161
162JSONSL_API
163void jsonsl_reset(jsonsl_t jsn)
164{
165    unsigned int ii;
166    jsn->tok_last = 0;
167    jsn->can_insert = 1;
168    jsn->pos = 0;
169    jsn->level = 0;
170    jsn->in_escape = 0;
171    jsn->expecting = 0;
172
173    memset(jsn->stack, 0, (jsn->levels_max * sizeof (struct jsonsl_state_st)));
174
175    for (ii = 0; ii < jsn->levels_max; ii++) {
176        jsn->stack[ii].level = ii;
177    }
178}
179
180JSONSL_API
181void jsonsl_destroy(jsonsl_t jsn)
182{
183    if (jsn) {
184        free(jsn);
185    }
186}
187
188JSONSL_API
189void
190jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes)
191{
192
193#define INVOKE_ERROR(eb) \
194    if (jsn->error_callback(jsn, JSONSL_ERROR_##eb, state, (char*)c)) { \
195        goto GT_AGAIN; \
196    } \
197    return;
198
199#define STACK_PUSH \
200    if (jsn->level >= (levels_max-1)) { \
201        jsn->error_callback(jsn, JSONSL_ERROR_LEVELS_EXCEEDED, state, (char*)c); \
202        return; \
203    } \
204    state = jsn->stack + (++jsn->level); \
205    state->ignore_callback = jsn->stack[jsn->level-1].ignore_callback; \
206    state->pos_begin = jsn->pos;
207
208#define STACK_POP_NOPOS \
209    state->pos_cur = jsn->pos; \
210    state = jsn->stack + (--jsn->level);
211
212
213#define STACK_POP \
214    STACK_POP_NOPOS; \
215    state->pos_cur = jsn->pos;
216
217#define CALLBACK_AND_POP_NOPOS(T) \
218        state->pos_cur = jsn->pos; \
219        DO_CALLBACK(T, POP); \
220        state->nescapes = 0; \
221        state = jsn->stack + (--jsn->level);
222
223#define CALLBACK_AND_POP(T) \
224        CALLBACK_AND_POP_NOPOS(T); \
225        state->pos_cur = jsn->pos;
226
227#define SPECIAL_POP \
228    CALLBACK_AND_POP(SPECIAL); \
229    jsn->expecting = 0; \
230    jsn->tok_last = 0; \
231
232#define CUR_CHAR (*(jsonsl_uchar_t*)c)
233
234#define DO_CALLBACK(T, action) \
235    if (jsn->call_##T && \
236            jsn->max_callback_level > state->level && \
237            state->ignore_callback == 0) { \
238        \
239        if (jsn->action_callback_##action) { \
240            jsn->action_callback_##action(jsn, JSONSL_ACTION_##action, state, (jsonsl_char_t*)c); \
241        } else if (jsn->action_callback) { \
242            jsn->action_callback(jsn, JSONSL_ACTION_##action, state, (jsonsl_char_t*)c); \
243        } \
244    }
245
246    /**
247     * Verifies that we are able to insert the (non-string) item into a hash.
248     */
249#define ENSURE_HVAL \
250    if (state->nelem % 2 == 0 && state->type == JSONSL_T_OBJECT) { \
251        INVOKE_ERROR(HKEY_EXPECTED); \
252    }
253
254#define VERIFY_SPECIAL(lit) \
255        if (CUR_CHAR != (lit)[jsn->pos - state->pos_begin]) { \
256            INVOKE_ERROR(SPECIAL_EXPECTED); \
257        }
258
259    const jsonsl_uchar_t *c = (jsonsl_uchar_t*)bytes;
260    size_t levels_max = jsn->levels_max;
261    struct jsonsl_state_st *state = jsn->stack + jsn->level;
262    static int chrt_string_nopass[0x100] = { JSONSL_CHARTABLE_string_nopass };
263    jsn->base = bytes;
264
265    for (; nbytes; nbytes--, jsn->pos++, c++) {
266        register jsonsl_type_t state_type;
267        INCR_METRIC(TOTAL);
268        /* Special escape handling for some stuff */
269        if (jsn->in_escape) {
270            jsn->in_escape = 0;
271            if (!is_allowed_escape(CUR_CHAR)) {
272                INVOKE_ERROR(ESCAPE_INVALID);
273            } else if (CUR_CHAR == 'u') {
274                DO_CALLBACK(UESCAPE, UESCAPE);
275                if (jsn->return_UESCAPE) {
276                    return;
277                }
278            }
279            goto GT_NEXT;
280        }
281        GT_AGAIN:
282        /**
283         * Several fast-tracks for common cases:
284         */
285        state_type = state->type;
286        if (state_type & JSONSL_Tf_STRINGY) {
287            /* check if our character cannot ever change our current string state
288             * or throw an error
289             */
290            if (
291#ifdef JSONSL_USE_WCHAR
292                    CUR_CHAR >= 0x100 ||
293#endif /* JSONSL_USE_WCHAR */
294                    (!chrt_string_nopass[CUR_CHAR & 0xff])) {
295                INCR_METRIC(STRINGY_INSIGNIFICANT);
296                goto GT_NEXT;
297            } else if (CUR_CHAR == '"') {
298                goto GT_QUOTE;
299            } else if (CUR_CHAR == '\\') {
300                goto GT_ESCAPE;
301            } else {
302                INVOKE_ERROR(WEIRD_WHITESPACE);
303            }
304            INCR_METRIC(STRINGY_SLOWPATH);
305
306        } else if (state_type == JSONSL_T_SPECIAL) {
307            if (state->special_flags & JSONSL_SPECIALf_NUMERIC) {
308                switch (CUR_CHAR) {
309                CASE_DIGITS
310                    state->nelem = (state->nelem*10) + (CUR_CHAR-0x30);
311                    goto GT_NEXT;
312
313                case 'e':
314                case 'E':
315                case '-':
316                case '+':
317                    state->special_flags |= JSONSL_SPECIALf_EXPONENT;
318                    goto GT_NEXT;
319                case '.':
320                    state->special_flags |= JSONSL_SPECIALf_FLOAT;
321                    goto GT_NEXT;
322                default:
323                    if (is_special_end(CUR_CHAR)) {
324                        goto GT_SPECIAL_POP;
325                    }
326                    INVOKE_ERROR(INVALID_NUMBER);
327                    break;
328                }
329            }
330            /* else if (!NUMERIC) */
331            if (!is_special_end(CUR_CHAR)) {
332                /* Verify TRUE, FALSE, NULL */
333                if (state->special_flags == JSONSL_SPECIALf_TRUE) {
334                    VERIFY_SPECIAL("true");
335                } else if (state->special_flags == JSONSL_SPECIALf_FALSE) {
336                    VERIFY_SPECIAL("false");
337                } else if (state->special_flags == JSONSL_SPECIALf_NULL) {
338                    VERIFY_SPECIAL("null");
339                }
340                INCR_METRIC(SPECIAL_FASTPATH);
341                goto GT_NEXT;
342            }
343
344            GT_SPECIAL_POP:
345            SPECIAL_POP;
346            jsn->expecting = ',';
347            if (is_allowed_whitespace(CUR_CHAR)) {
348                goto GT_NEXT;
349            }
350            /**
351             * This works because we have a non-whitespace token
352             * which is not a special token. If this is a structural
353             * character then it will be gracefully handled by the
354             * switch statement. Otherwise it will default to the 'special'
355             * state again,
356             */
357            goto GT_STRUCTURAL_TOKEN;
358        } else if (is_allowed_whitespace(CUR_CHAR)) {
359            INCR_METRIC(ALLOWED_WHITESPACE);
360            /* So we're not special. Harmless insignificant whitespace
361             * passthrough
362             */
363            goto GT_NEXT;
364        } else if (extract_special(CUR_CHAR)) {
365            /* not a string, whitespace, or structural token. must be special */
366            goto GT_SPECIAL_BEGIN;
367        }
368
369        INCR_GENERIC(CUR_CHAR);
370
371        if (CUR_CHAR == '"') {
372            GT_QUOTE:
373            jsn->can_insert = 0;
374            switch (state_type) {
375
376            /* the end of a string or hash key */
377            case JSONSL_T_STRING:
378                CALLBACK_AND_POP(STRING);
379                goto GT_NEXT;
380            case JSONSL_T_HKEY:
381                CALLBACK_AND_POP(HKEY);
382                goto GT_NEXT;
383
384            case JSONSL_T_OBJECT:
385                state->nelem++;
386                if ( (state->nelem-1) % 2 ) {
387                    /* Odd, this must be a hash value */
388                    if (jsn->tok_last != ':') {
389                        INVOKE_ERROR(MISSING_TOKEN);
390                    }
391                    jsn->expecting = ','; /* Can't figure out what to expect next */
392                    jsn->tok_last = 0;
393
394                    STACK_PUSH;
395                    state->type = JSONSL_T_STRING;
396                    DO_CALLBACK(STRING, PUSH);
397
398                } else {
399                    /* hash key */
400                    if (jsn->expecting != '"') {
401                        INVOKE_ERROR(STRAY_TOKEN);
402                    }
403                    jsn->tok_last = 0;
404                    jsn->expecting = ':';
405
406                    STACK_PUSH;
407                    state->type = JSONSL_T_HKEY;
408                    DO_CALLBACK(HKEY, PUSH);
409                }
410                goto GT_NEXT;
411
412            case JSONSL_T_LIST:
413                state->nelem++;
414                STACK_PUSH;
415                state->type = JSONSL_T_STRING;
416                jsn->expecting = ',';
417                jsn->tok_last = 0;
418                DO_CALLBACK(STRING, PUSH);
419                goto GT_NEXT;
420
421            case JSONSL_T_SPECIAL:
422                INVOKE_ERROR(STRAY_TOKEN);
423                break;
424
425            default:
426                INVOKE_ERROR(STRING_OUTSIDE_CONTAINER);
427                break;
428            } /* switch(state->type) */
429        } else if (CUR_CHAR == '\\') {
430            GT_ESCAPE:
431            INCR_METRIC(ESCAPES);
432        /* Escape */
433            if ( (state->type & JSONSL_Tf_STRINGY) == 0 ) {
434                INVOKE_ERROR(ESCAPE_OUTSIDE_STRING);
435            }
436            state->nescapes++;
437            jsn->in_escape = 1;
438            goto GT_NEXT;
439        } /* " or \ */
440
441        GT_STRUCTURAL_TOKEN:
442        switch (CUR_CHAR) {
443        case ':':
444            INCR_METRIC(STRUCTURAL_TOKEN);
445            if (jsn->expecting != CUR_CHAR) {
446                INVOKE_ERROR(STRAY_TOKEN);
447            }
448            jsn->tok_last = ':';
449            jsn->can_insert = 1;
450            jsn->expecting = '"';
451            goto GT_NEXT;
452
453        case ',':
454            INCR_METRIC(STRUCTURAL_TOKEN);
455            /**
456             * The comma is one of the more generic tokens.
457             * In the context of an OBJECT, the can_insert flag
458             * should never be set, and no other action is
459             * necessary.
460             */
461            if (jsn->expecting != CUR_CHAR) {
462                /* make this branch execute only when we haven't manually
463                 * just placed the ',' in the expecting register.
464                 */
465                INVOKE_ERROR(STRAY_TOKEN);
466            }
467
468            if (state->type == JSONSL_T_OBJECT) {
469                /* end of hash value, expect a string as a hash key */
470                jsn->expecting = '"';
471            } else {
472                jsn->can_insert = 1;
473            }
474
475            jsn->tok_last = ',';
476            jsn->expecting = '"';
477            goto GT_NEXT;
478
479            /* new list or object */
480            /* hashes are more common */
481        case '{':
482        case '[':
483            INCR_METRIC(STRUCTURAL_TOKEN);
484            if (!jsn->can_insert) {
485                INVOKE_ERROR(CANT_INSERT);
486            }
487
488            ENSURE_HVAL;
489            state->nelem++;
490
491            STACK_PUSH;
492            /* because the constants match the opening delimiters, we can do this: */
493            state->type = CUR_CHAR;
494            state->nelem = 0;
495            jsn->can_insert = 1;
496            if (CUR_CHAR == '{') {
497                /* If we're a hash, we expect a key first, which is quouted */
498                jsn->expecting = '"';
499            }
500            if (CUR_CHAR == JSONSL_T_OBJECT) {
501                DO_CALLBACK(OBJECT, PUSH);
502            } else {
503                DO_CALLBACK(LIST, PUSH);
504            }
505            jsn->tok_last = 0;
506            goto GT_NEXT;
507
508            /* closing of list or object */
509        case '}':
510        case ']':
511            INCR_METRIC(STRUCTURAL_TOKEN);
512            if (jsn->tok_last == ',' && jsn->options.allow_trailing_comma == 0) {
513                INVOKE_ERROR(TRAILING_COMMA);
514            }
515
516            jsn->can_insert = 0;
517            jsn->level--;
518            jsn->expecting = ',';
519            jsn->tok_last = 0;
520            if (CUR_CHAR == ']') {
521                if (state->type != '[') {
522                    INVOKE_ERROR(BRACKET_MISMATCH);
523                }
524                DO_CALLBACK(LIST, POP);
525            } else {
526                if (state->type != '{') {
527                    INVOKE_ERROR(BRACKET_MISMATCH);
528                }
529                DO_CALLBACK(OBJECT, POP);
530            }
531            state = jsn->stack + jsn->level;
532            state->pos_cur = jsn->pos;
533            goto GT_NEXT;
534
535        default:
536            GT_SPECIAL_BEGIN:
537            /**
538             * Not a string, not a structural token, and not benign whitespace.
539             * Technically we should iterate over the character always, but since
540             * we are not doing full numerical/value decoding anyway (but only hinting),
541             * we only check upon entry.
542             */
543            if (state->type != JSONSL_T_SPECIAL) {
544                int special_flags = extract_special(CUR_CHAR);
545                if (!special_flags) {
546                    /**
547                     * Try to do some heuristics here anyway to figure out what kind of
548                     * error this is. The 'special' case is a fallback scenario anyway.
549                     */
550                    if (CUR_CHAR == '\0') {
551                        INVOKE_ERROR(FOUND_NULL_BYTE);
552                    } else if (CUR_CHAR < 0x20) {
553                        INVOKE_ERROR(WEIRD_WHITESPACE);
554                    } else {
555                        INVOKE_ERROR(SPECIAL_EXPECTED);
556                    }
557                }
558                ENSURE_HVAL;
559                state->nelem++;
560                if (!jsn->can_insert) {
561                    INVOKE_ERROR(CANT_INSERT);
562                }
563                STACK_PUSH;
564                state->type = JSONSL_T_SPECIAL;
565                state->special_flags = special_flags;
566                if (special_flags == JSONSL_SPECIALf_UNSIGNED) {
567                    state->nelem = CUR_CHAR - 0x30;
568                } else {
569                    state->nelem = 0;
570                }
571                DO_CALLBACK(SPECIAL, PUSH);
572            }
573            goto GT_NEXT;
574        }
575
576        GT_NEXT:
577        continue;
578    }
579}
580
581JSONSL_API
582const char* jsonsl_strerror(jsonsl_error_t err)
583{
584#define X(t) \
585    if (err == JSONSL_ERROR_##t) \
586        return #t;
587    JSONSL_XERR;
588#undef X
589    return "<UNKNOWN_ERROR>";
590}
591
592JSONSL_API
593const char *jsonsl_strtype(jsonsl_type_t type)
594{
595#define X(o,c) \
596    if (type == JSONSL_T_##o) \
597        return #o;
598    JSONSL_XTYPE
599#undef X
600    return "UNKNOWN TYPE";
601
602}
603
604/*
605 *
606 * JPR/JSONPointer functions
607 *
608 *
609 */
610#ifndef JSONSL_NO_JPR
611static
612jsonsl_jpr_type_t
613populate_component(char *in,
614                   struct jsonsl_jpr_component_st *component,
615                   char **next,
616                   jsonsl_error_t *errp)
617{
618    unsigned long pctval;
619    char *c = NULL, *outp = NULL, *end = NULL;
620    size_t input_len;
621    jsonsl_jpr_type_t ret = JSONSL_PATH_NONE;
622
623    if (*next == NULL || *(*next) == '\0') {
624        return JSONSL_PATH_NONE;
625    }
626
627    /* Replace the next / with a NULL */
628    *next = strstr(in, "/");
629    if (*next != NULL) {
630        *(*next) = '\0'; /* drop the forward slash */
631        input_len = *next - in;
632        end = *next;
633        *next += 1; /* next character after the '/' */
634    } else {
635        input_len = strlen(in);
636        end = in + input_len + 1;
637    }
638
639    component->pstr = in;
640
641    /* Check for special components of interest */
642    if (*in == JSONSL_PATH_WILDCARD_CHAR && input_len == 1) {
643        /* Lone wildcard */
644        ret = JSONSL_PATH_WILDCARD;
645        goto GT_RET;
646    } else if (isdigit(*in)) {
647        /* ASCII Numeric */
648        char *endptr;
649        component->idx = strtoul(in, &endptr, 10);
650        if (endptr && *endptr == '\0') {
651            ret = JSONSL_PATH_NUMERIC;
652            goto GT_RET;
653        }
654    }
655
656    /* Default, it's a string */
657    ret = JSONSL_PATH_STRING;
658    for (c = outp = in; c < end; c++, outp++) {
659        char origc;
660        if (*c != '%') {
661            goto GT_ASSIGN;
662        }
663        /*
664         * c = { [+0] = '%', [+1] = 'b', [+2] = 'e', [+3] = '\0' }
665         */
666
667        /* Need %XX */
668        if (c+2 >= end) {
669            *errp = JSONSL_ERROR_PERCENT_BADHEX;
670            return JSONSL_PATH_INVALID;
671        }
672        if (! (isxdigit(*(c+1)) && isxdigit(*(c+2))) ) {
673            *errp = JSONSL_ERROR_PERCENT_BADHEX;
674            return JSONSL_PATH_INVALID;
675        }
676
677        /* Temporarily null-terminate the characters */
678        origc = *(c+3);
679        *(c+3) = '\0';
680        pctval = strtoul(c+1, NULL, 16);
681        *(c+3) = origc;
682
683        *outp = (char) pctval;
684        c += 2;
685        continue;
686
687        GT_ASSIGN:
688        *outp = *c;
689    }
690    /* Null-terminate the string */
691    for (; outp < c; outp++) {
692        *outp = '\0';
693    }
694
695    GT_RET:
696    component->ptype = ret;
697    if (ret != JSONSL_PATH_WILDCARD) {
698        component->len = strlen(component->pstr);
699    }
700    return ret;
701}
702
703JSONSL_API
704jsonsl_jpr_t
705jsonsl_jpr_new(const char *path, jsonsl_error_t *errp)
706{
707    char *my_copy;
708    int count, curidx;
709    struct jsonsl_jpr_st *ret;
710    struct jsonsl_jpr_component_st *components;
711    size_t origlen;
712    jsonsl_error_t errstacked;
713
714    if (errp == NULL) {
715        errp = &errstacked;
716    }
717
718    if (path == NULL || *path != '/') {
719        *errp = JSONSL_ERROR_JPR_NOROOT;
720        return NULL;
721    }
722
723    count = 1;
724    path++;
725    {
726        const char *c = path;
727        for (; *c; c++) {
728            if (*c == '/') {
729                count++;
730                if (*(c+1) == '/') {
731                    *errp = JSONSL_ERROR_JPR_DUPSLASH;
732                    return NULL;
733                }
734            }
735        }
736    }
737    if(*path) {
738        count++;
739    }
740
741    components = malloc(sizeof(*components) * count);
742    my_copy = malloc(strlen(path) + 1);
743    strcpy(my_copy, path);
744
745    components[0].ptype = JSONSL_PATH_ROOT;
746
747    if (*my_copy) {
748        char *cur = my_copy;
749        int pathret = JSONSL_PATH_STRING;
750        curidx = 1;
751        while (pathret > 0 && curidx < count) {
752            pathret = populate_component(cur, components + curidx, &cur, errp);
753            if (pathret > 0) {
754                curidx++;
755            } else {
756                break;
757            }
758        }
759
760        if (pathret == JSONSL_PATH_INVALID) {
761            free(components);
762            free(my_copy);
763            return NULL;
764        }
765    } else {
766        curidx = 1;
767    }
768
769    path--; /*revert path to leading '/' */
770    origlen = strlen(path) + 1;
771    ret = malloc(sizeof(*ret));
772    ret->components = components;
773    ret->ncomponents = curidx;
774    ret->basestr = my_copy;
775    ret->orig = malloc(origlen);
776    ret->norig = origlen-1;
777    strcpy(ret->orig, path);
778
779    return ret;
780}
781
782void jsonsl_jpr_destroy(jsonsl_jpr_t jpr)
783{
784    free(jpr->components);
785    free(jpr->basestr);
786    free(jpr->orig);
787    free(jpr);
788}
789
790JSONSL_API
791jsonsl_jpr_match_t
792jsonsl_jpr_match(jsonsl_jpr_t jpr,
793                   jsonsl_type_t parent_type,
794                   unsigned int parent_level,
795                   const char *key,
796                   size_t nkey)
797{
798    /* find our current component. This is the child level */
799    int cmpret;
800    struct jsonsl_jpr_component_st *p_component;
801    p_component = jpr->components + parent_level;
802
803    if (parent_level >= jpr->ncomponents) {
804        return JSONSL_MATCH_NOMATCH;
805    }
806
807    /* Lone query for 'root' element. Always matches */
808    if (parent_level == 0) {
809        if (jpr->ncomponents == 1) {
810            return JSONSL_MATCH_COMPLETE;
811        } else {
812            return JSONSL_MATCH_POSSIBLE;
813        }
814    }
815
816    /* Wildcard, always matches */
817    if (p_component->ptype == JSONSL_PATH_WILDCARD) {
818        if (parent_level == jpr->ncomponents-1) {
819            return JSONSL_MATCH_COMPLETE;
820        } else {
821            return JSONSL_MATCH_POSSIBLE;
822        }
823    }
824
825    /* Check numeric array index */
826    if (p_component->ptype == JSONSL_PATH_NUMERIC
827            && parent_type == JSONSL_T_LIST) {
828        if (p_component->idx != nkey) {
829            return JSONSL_MATCH_NOMATCH;
830        } else {
831            if (parent_level == jpr->ncomponents-1) {
832                return JSONSL_MATCH_COMPLETE;
833            } else {
834                return JSONSL_MATCH_POSSIBLE;
835            }
836        }
837    }
838
839    /* Check lengths */
840    if (p_component->len != nkey) {
841        return JSONSL_MATCH_NOMATCH;
842    }
843
844    /* Check string comparison */
845    cmpret = strncmp(p_component->pstr, key, nkey);
846    if (cmpret != 0) {
847        return JSONSL_MATCH_NOMATCH;
848    } else {
849        if (parent_level == jpr->ncomponents-1) {
850            return JSONSL_MATCH_COMPLETE;
851        } else {
852            return JSONSL_MATCH_POSSIBLE;
853        }
854    }
855
856    /* Never reached, but make the compiler happy */
857    abort();
858    return JSONSL_MATCH_NOMATCH;
859}
860
861JSONSL_API
862void jsonsl_jpr_match_state_init(jsonsl_t jsn,
863                                 jsonsl_jpr_t *jprs,
864                                 size_t njprs)
865{
866    size_t ii, *firstjmp;
867    if (njprs == 0) {
868        return;
869    }
870    jsn->jprs = malloc(sizeof(jsonsl_jpr_t) * njprs);
871    jsn->jpr_count = njprs;
872    jsn->jpr_root = calloc(1, sizeof(size_t) * njprs * jsn->levels_max);
873    memcpy(jsn->jprs, jprs, sizeof(jsonsl_jpr_t) * njprs);
874    /* Set the initial jump table values */
875
876    firstjmp = jsn->jpr_root;
877    for (ii = 0; ii < njprs; ii++) {
878        firstjmp[ii] = ii+1;
879    }
880}
881
882JSONSL_API
883void jsonsl_jpr_match_state_cleanup(jsonsl_t jsn)
884{
885    if (jsn->jpr_count == 0) {
886        return;
887    }
888
889    free(jsn->jpr_root);
890    free(jsn->jprs);
891    jsn->jprs = NULL;
892    jsn->jpr_root = NULL;
893    jsn->jpr_count = 0;
894}
895
896/**
897 * This function should be called exactly once on each element...
898 * This should also be called in recursive order, since we rely
899 * on the parent having been initalized for a match.
900 *
901 * Since the parent is checked for a match as well, we maintain a 'serial' counter.
902 * Whenever we traverse an element, we expect the serial to be the same as a global
903 * integer. If they do not match, we re-initialize the context, and set the serial.
904 *
905 * This ensures a type of consistency without having a proactive reset by the
906 * main lexer itself.
907 *
908 */
909JSONSL_API
910jsonsl_jpr_t jsonsl_jpr_match_state(jsonsl_t jsn,
911                                    struct jsonsl_state_st *state,
912                                    const char *key,
913                                    size_t nkey,
914                                    jsonsl_jpr_match_t *out)
915{
916    struct jsonsl_state_st *parent_state;
917    jsonsl_jpr_t ret = NULL;
918
919    /* Jump and JPR tables for our own state and the parent state */
920    size_t *jmptable, *pjmptable;
921    size_t jmp_cur, ii, ourjmpidx;
922
923    if (!jsn->jpr_root) {
924        *out = JSONSL_MATCH_NOMATCH;
925        return NULL;
926    }
927
928    pjmptable = jsn->jpr_root + (jsn->jpr_count * (state->level-1));
929    jmptable = pjmptable + jsn->jpr_count;
930
931    /* If the parent cannot match, then invalidate it */
932    if (*pjmptable == 0) {
933        *jmptable = 0;
934        *out = JSONSL_MATCH_NOMATCH;
935        return NULL;
936    }
937
938    parent_state = jsn->stack + state->level - 1;
939
940    if (parent_state->type == JSONSL_T_LIST) {
941        nkey = (size_t) parent_state->nelem;
942    }
943
944    *jmptable = 0;
945    ourjmpidx = 0;
946    memset(jmptable, 0, sizeof(int) * jsn->jpr_count);
947
948    for (ii = 0; ii <  jsn->jpr_count; ii++) {
949        jmp_cur = pjmptable[ii];
950        if (jmp_cur) {
951            jsonsl_jpr_t jpr = jsn->jprs[jmp_cur-1];
952            *out = jsonsl_jpr_match(jpr,
953                                    parent_state->type,
954                                    parent_state->level,
955                                    key, nkey);
956            if (*out == JSONSL_MATCH_COMPLETE) {
957                ret = jpr;
958                *jmptable = 0;
959                return ret;
960            } else if (*out == JSONSL_MATCH_POSSIBLE) {
961                jmptable[ourjmpidx] = ii+1;
962                ourjmpidx++;
963            }
964        } else {
965            break;
966        }
967    }
968    if (!*jmptable) {
969        *out = JSONSL_MATCH_NOMATCH;
970    }
971    return NULL;
972}
973
974JSONSL_API
975const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match)
976{
977#define X(T,v) \
978    if ( match == JSONSL_MATCH_##T ) \
979        return #T;
980    JSONSL_XMATCH
981#undef X
982    return "<UNKNOWN>";
983}
984
985#endif /* JSONSL_WITH_JPR */
986
987/**
988 * Maps literal escape sequences with special meaning to their
989 * actual control codes (e.g.\n => 0x20)
990 */
991static unsigned char *Escape_Maps;
992/**
993 * Utility function to convert escape sequences
994 */
995JSONSL_API
996size_t jsonsl_util_unescape_ex(const char *in,
997                               char *out,
998                               size_t len,
999                               const int toEscape[128],
1000                               jsonsl_special_t *oflags,
1001                               jsonsl_error_t *err,
1002                               const char **errat)
1003{
1004    const unsigned char *c = (const unsigned char*)in;
1005    int in_escape = 0;
1006    size_t origlen = len;
1007    /* difference between the length of the input buffer and the output buffer */
1008    size_t ndiff = 0;
1009    if (oflags) {
1010        *oflags = 0;
1011    }
1012#define UNESCAPE_BAIL(e,offset) \
1013    *err = JSONSL_ERROR_##e; \
1014    if (errat) { \
1015        *errat = (const char*)(c+ (ptrdiff_t)(offset)); \
1016    } \
1017    return 0;
1018
1019    for (; len; len--, c++, out++) {
1020        unsigned int uesc_val[2];
1021        if (in_escape) {
1022            /* inside a previously ignored escape. Ignore */
1023            in_escape = 0;
1024            goto GT_ASSIGN;
1025        }
1026
1027        if (*c != '\\') {
1028            /* Not an escape, so we don't care about this */
1029            goto GT_ASSIGN;
1030        }
1031
1032        if (len < 2) {
1033            UNESCAPE_BAIL(ESCAPE_INVALID, 0);
1034        }
1035        if (!is_allowed_escape(c[1])) {
1036            UNESCAPE_BAIL(ESCAPE_INVALID, 1)
1037        }
1038        if ((toEscape[(unsigned char)c[1] & 0x7f] == 0 &&
1039                c[1] != '\\' && c[1] != '"')) {
1040            /* if we don't want to unescape this string, just continue with
1041             * the escape flag set
1042             */
1043            in_escape = 1;
1044            goto GT_ASSIGN;
1045        }
1046
1047        if (c[1] != 'u') {
1048            /* simple skip-and-replace using pre-defined maps.
1049             * TODO: should the maps actually reflect the desired
1050             * replacement character in toEscape?
1051             */
1052            if (Escape_Maps[c[1]]) {
1053                /* Check if there is a corresponding replacement */
1054                *out = Escape_Maps[c[1]];
1055            } else {
1056                /* Just gobble up the 'reverse-solidus' */
1057                *out = c[1];
1058            }
1059            len--;
1060            ndiff++;
1061            c++;
1062            /* do not assign, just continue */
1063            continue;
1064        }
1065
1066        /* next == 'u' */
1067        if (len < 6) {
1068            /* Need at least six characters:
1069             * { [0] = '\\', [1] = 'u', [2] = 'f', [3] = 'f', [4] = 'f', [5] = 'f' }
1070             */
1071            UNESCAPE_BAIL(UESCAPE_TOOSHORT, -1);
1072        }
1073
1074        if (sscanf((const char*)(c+2), "%02x%02x", uesc_val, uesc_val+1) != 2) {
1075            /* We treat the sequence as two octets */
1076            UNESCAPE_BAIL(UESCAPE_TOOSHORT, -1);
1077        }
1078
1079        /* By now, we gobble up all the six bytes (current implied + 5 next
1080         * characters), and have at least four missing bytes from the output
1081         * buffer.
1082         */
1083        len -= 5;
1084        c += 5;
1085
1086        ndiff += 4;
1087        if (uesc_val[0] == 0) {
1088            /* only one byte is extracted from the two
1089             * possible octets. Increment the diff counter by one.
1090             */
1091            *out = uesc_val[1];
1092            if (oflags && *(unsigned char*)out > 0x7f) {
1093                *oflags |= JSONSL_SPECIALf_NONASCII;
1094            }
1095            ndiff++;
1096        } else {
1097            *(out++) = uesc_val[0];
1098            *out = uesc_val[1];
1099            if (oflags && (uesc_val[0] > 0x7f || uesc_val[1] > 0x7f)) {
1100                *oflags |= JSONSL_SPECIALf_NONASCII;
1101            }
1102        }
1103        continue;
1104
1105        /* Only reached by previous branches */
1106        GT_ASSIGN:
1107        *out = *c;
1108    }
1109    *err = JSONSL_ERROR_SUCCESS;
1110    return origlen - ndiff;
1111}
1112
1113/**
1114 * Character Table definitions.
1115 * These were all generated via srcutil/genchartables.pl
1116 */
1117
1118/**
1119 * This table contains the beginnings of non-string
1120 * allowable (bareword) values.
1121 */
1122static jsonsl_special_t _special_table[0x100] = {
1123        /* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */
1124        /* 0x20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x2c */
1125        /* 0x2d */ JSONSL_SPECIALf_SIGNED /* - */, /* 0x2d */
1126        /* 0x2e */ 0,0, /* 0x2f */
1127        /* 0x30 */ JSONSL_SPECIALf_UNSIGNED /* 0 */, /* 0x30 */
1128        /* 0x31 */ JSONSL_SPECIALf_UNSIGNED /* 1 */, /* 0x31 */
1129        /* 0x32 */ JSONSL_SPECIALf_UNSIGNED /* 2 */, /* 0x32 */
1130        /* 0x33 */ JSONSL_SPECIALf_UNSIGNED /* 3 */, /* 0x33 */
1131        /* 0x34 */ JSONSL_SPECIALf_UNSIGNED /* 4 */, /* 0x34 */
1132        /* 0x35 */ JSONSL_SPECIALf_UNSIGNED /* 5 */, /* 0x35 */
1133        /* 0x36 */ JSONSL_SPECIALf_UNSIGNED /* 6 */, /* 0x36 */
1134        /* 0x37 */ JSONSL_SPECIALf_UNSIGNED /* 7 */, /* 0x37 */
1135        /* 0x38 */ JSONSL_SPECIALf_UNSIGNED /* 8 */, /* 0x38 */
1136        /* 0x39 */ JSONSL_SPECIALf_UNSIGNED /* 9 */, /* 0x39 */
1137        /* 0x3a */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x59 */
1138        /* 0x5a */ 0,0,0,0,0,0,0,0,0,0,0,0, /* 0x65 */
1139        /* 0x66 */ JSONSL_SPECIALf_FALSE /* f */, /* 0x66 */
1140        /* 0x67 */ 0,0,0,0,0,0,0, /* 0x6d */
1141        /* 0x6e */ JSONSL_SPECIALf_NULL /* n */, /* 0x6e */
1142        /* 0x6f */ 0,0,0,0,0, /* 0x73 */
1143        /* 0x74 */ JSONSL_SPECIALf_TRUE /* t */, /* 0x74 */
1144        /* 0x75 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x94 */
1145        /* 0x95 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb4 */
1146        /* 0xb5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xd4 */
1147        /* 0xd5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xf4 */
1148        /* 0xf5 */ 0,0,0,0,0,0,0,0,0,0 /* 0xfe */
1149};
1150static jsonsl_special_t *Special_table = _special_table;
1151
1152/**
1153 * Contains characters which signal the termination of any of the 'special' bareword
1154 * values.
1155 */
1156static int _special_endings[0x100] = {
1157        /* 0x00 */ 0,0,0,0,0,0,0,0,0, /* 0x08 */
1158        /* 0x09 */ 1 /* <TAB> */, /* 0x09 */
1159        /* 0x0a */ 1 /* <LF> */, /* 0x0a */
1160        /* 0x0b */ 0,0, /* 0x0c */
1161        /* 0x0d */ 1 /* <CR> */, /* 0x0d */
1162        /* 0x0e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */
1163        /* 0x20 */ 1 /* <SP> */, /* 0x20 */
1164        /* 0x21 */ 0, /* 0x21 */
1165        /* 0x22 */ 1 /* " */, /* 0x22 */
1166        /* 0x23 */ 0,0,0,0,0,0,0,0,0, /* 0x2b */
1167        /* 0x2c */ 1 /* , */, /* 0x2c */
1168        /* 0x2d */ 0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x39 */
1169        /* 0x3a */ 1 /* : */, /* 0x3a */
1170        /* 0x3b */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5a */
1171        /* 0x5b */ 1 /* [ */, /* 0x5b */
1172        /* 0x5c */ 1 /* \ */, /* 0x5c */
1173        /* 0x5d */ 1 /* ] */, /* 0x5d */
1174        /* 0x5e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x7a */
1175        /* 0x7b */ 1 /* { */, /* 0x7b */
1176        /* 0x7c */ 0, /* 0x7c */
1177        /* 0x7d */ 1 /* } */, /* 0x7d */
1178        /* 0x7e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x9d */
1179        /* 0x9e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xbd */
1180        /* 0xbe */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xdd */
1181        /* 0xde */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xfd */
1182        /* 0xfe */ 0 /* 0xfe */
1183};
1184static int *Special_Endings = _special_endings;
1185
1186/**
1187 * Contains allowable whitespace.
1188 */
1189static int _allowed_whitespace[0x100] = {
1190        /* 0x00 */ 0,0,0,0,0,0,0,0,0, /* 0x08 */
1191        /* 0x09 */ 1 /* <TAB> */, /* 0x09 */
1192        /* 0x0a */ 1 /* <LF> */, /* 0x0a */
1193        /* 0x0b */ 0,0, /* 0x0c */
1194        /* 0x0d */ 1 /* <CR> */, /* 0x0d */
1195        /* 0x0e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */
1196        /* 0x20 */ 1 /* <SP> */, /* 0x20 */
1197        /* 0x21 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x40 */
1198        /* 0x41 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x60 */
1199        /* 0x61 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x80 */
1200        /* 0x81 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xa0 */
1201        /* 0xa1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xc0 */
1202        /* 0xc1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xe0 */
1203        /* 0xe1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* 0xfe */
1204};
1205static int *Allowed_Whitespace = _allowed_whitespace;
1206
1207/**
1208 * Allowable two-character 'common' escapes:
1209 */
1210static int _allowed_escapes[0x100] = {
1211        /* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */
1212        /* 0x20 */ 0,0, /* 0x21 */
1213        /* 0x22 */ 1 /* <"> */, /* 0x22 */
1214        /* 0x23 */ 0,0,0,0,0,0,0,0,0,0,0,0, /* 0x2e */
1215        /* 0x2f */ 1 /* </> */, /* 0x2f */
1216        /* 0x30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x4f */
1217        /* 0x50 */ 0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5b */
1218        /* 0x5c */ 1 /* <\> */, /* 0x5c */
1219        /* 0x5d */ 0,0,0,0,0, /* 0x61 */
1220        /* 0x62 */ 1 /* <b> */, /* 0x62 */
1221        /* 0x63 */ 0,0,0, /* 0x65 */
1222        /* 0x66 */ 1 /* <f> */, /* 0x66 */
1223        /* 0x67 */ 0,0,0,0,0,0,0, /* 0x6d */
1224        /* 0x6e */ 1 /* <n> */, /* 0x6e */
1225        /* 0x6f */ 0,0,0, /* 0x71 */
1226        /* 0x72 */ 1 /* <r> */, /* 0x72 */
1227        /* 0x73 */ 0, /* 0x73 */
1228        /* 0x74 */ 1 /* <t> */, /* 0x74 */
1229        /* 0x75 */ 1 /* <u> */, /* 0x75 */
1230        /* 0x76 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x95 */
1231        /* 0x96 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb5 */
1232        /* 0xb6 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xd5 */
1233        /* 0xd6 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xf5 */
1234        /* 0xf6 */ 0,0,0,0,0,0,0,0,0, /* 0xfe */
1235};
1236
1237static int *Allowed_Escapes = _allowed_escapes;
1238
1239
1240static unsigned char _escape_maps[0x100] = {
1241        /* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */
1242        /* 0x20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x3f */
1243        /* 0x40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5f */
1244        /* 0x60 */ 0,0, /* 0x61 */
1245        /* 0x62 */ 8 /* <b> */, /* 0x62 */
1246        /* 0x63 */ 0,0,0, /* 0x65 */
1247        /* 0x66 */ 12 /* <f> */, /* 0x66 */
1248        /* 0x67 */ 0,0,0,0,0,0,0, /* 0x6d */
1249        /* 0x6e */ 10 /* <n> */, /* 0x6e */
1250        /* 0x6f */ 0,0,0, /* 0x71 */
1251        /* 0x72 */ 13 /* <r> */, /* 0x72 */
1252        /* 0x73 */ 0, /* 0x73 */
1253        /* 0x74 */ 9 /* <t> */, /* 0x74 */
1254        /* 0x75 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x94 */
1255        /* 0x95 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb4 */
1256        /* 0xb5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xd4 */
1257        /* 0xd5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xf4 */
1258        /* 0xf5 */ 0,0,0,0,0,0,0,0,0,0 /* 0xfe */
1259};
1260
1261static unsigned char *Escape_Maps = _escape_maps;
1262
1263
Note: See TracBrowser for help on using the repository browser.