[metapost] make_text related scanning improvements

Marcel Krueger marcel at 2krueger.de
Fri May 18 00:51:35 CEST 2018


Hi,

some time ago I found the undocumented but very useful `make_text` callback in mplib.
Sadly the code for scanning the text between `btex/verbatimtex` and `etex`
is not compatible with normal `btex...etex` scanning:
Especially `etex` at the end of a line is not accepted because of an off-by-one error and only a single line of TeX material is allowed.

This is a problem when using this with `verbatimtex`, where `etex` often comes at the end of a line and sometimes multiple lines are used.

To fix these problems I implemented a new version, which also adds some error reporting and more consistent detection of the etex token.
I think mplib would really benefit from merging this code into the official sources.
The corresponding CWEB change file is

@x
@ @<Pass btex ... etex to script@>=
{
    int first ;
    while ((loc < limit - 4) && (mp->buffer[loc] == ' ')) {
        incr(loc);
    }
    first = loc ;
    if (mp->buffer[loc-1] == ' ') {
        decr(loc);
    }
    while (loc < limit - 5) {
        if (mp->buffer[loc] == ' ') {
            incr(loc);
            if (mp->buffer[loc] == 'e') {
                incr(loc);
                if (mp->buffer[loc] == 't') {
                    incr(loc) ;
                    if (mp->buffer[loc] == 'e') {
                        incr(loc) ;
                        if (mp->buffer[loc] == 'x') {
                            /* start action */
                            char *s, *txt ;
                            int size ;
                            mp_value new_expr;
                            size = loc - first + 1 - 4 ;
                            if (size < 0) {
                                size = 0 ;
                            } else {
                                while ((size > 1) && (mp->buffer[first+size-1] == ' ')) {
                                    decr(size);
                                }
                            }
                            txt = malloc(size+1);
                            if (size > 0) {
                                (void) memcpy (txt, mp->buffer + first, size);
                            }
                            txt[size] = '\0';
                            incr(loc);
                            s = mp->make_text(mp,txt,(cur_mod() == verbatim_code)) ; /* we could pass the size */
                            @<Run a script@>
                            /* done */
                            free(txt);
                            break ;
                        } else {
                      //      decr(loc) ;
                        }
                    }
                }
            }
        } else {
            incr(loc);
        }
    }
}
@y
@ @<Pass btex ... etex to script@>=
{
    char *s, *txt, *orig_txt ;
    int size, first ;
    quarterword cclass; /* the |char_class| of previous token */
    mp_value new_expr;
    old_info = line;
    size = 0;
    first = loc ;
    cclass = mp->char_class[mp->buffer[loc - 1]] ;
    while (   cclass == letter_class
           || loc + 4 > limit
           || memcmp("etex", mp->buffer + loc, 4)
           || (loc + 4 < limit && letter_class == mp->char_class[mp->buffer[loc + 4]])) {
        cclass = mp->char_class[mp->buffer[loc]];
        if (loc == limit) {
            if (size) {
                txt = realloc(txt, size + limit - first + 1);
            } else {
                txt = malloc(limit - first + 1);
            }
            (void) memcpy (txt + size, mp->buffer + first, limit - first);
            size += limit - first + 1;
            txt[size - 1] = '\n';
            if (move_to_next_line(mp)) {
                char msg[256];
                const char *hlp[] = {
                       "The file ended while I was looking for the `etex' to",
                       "finish this TeX material.  I've inserted `etex' now.",
                        NULL };
                mp_snprintf(msg, 256, "TeX mode didn't end; all text was ignored after line %d", (int)old_info);
                mp_error (mp, msg, hlp, false);
                goto READING_FINISHED;
            }
            first = loc;
            cclass = percent_class;
        } else {
            incr(loc);
        }
    }
    if (size) {
        txt = realloc(txt, size + loc - first + 1);
    } else {
        txt = malloc(loc - first + 1);
    }
    (void) memcpy (txt + size, mp->buffer + first, loc - first);
    size += loc - first + 1;
    loc += 4;
READING_FINISHED:
    if (cur_mod() == btex_code) {
        while ((size > 1) && (cclass == space_class || txt[size - 2] == '\n')) {
            decr(size);
            cclass = mp->char_class[(ASCII_code) txt[size - 2]];
        }
    }
    orig_txt = txt;
    cclass = mp->char_class[(ASCII_code) txt[0]];
    while ((size > 1) && (cclass == space_class || txt[0] == '\n')) {
        incr(txt);
        decr(size);
        cclass = mp->char_class[(ASCII_code) txt[0]];
    }
    txt[size - 1] = '\0';
    s = mp->make_text(mp,txt,(cur_mod() == verbatim_code)) ; /* we could pass the size */
    @<Run a script@>
    free(orig_txt);
}
@z

Best regards
Marcel Krüger




More information about the metapost mailing list