/*
 * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved.
 * Use is subject to license terms.
 *
 *      Copyright (c) 1984 AT&T
 *        All Rights Reserved
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "apr.h"
#include "apr_strings.h"
#include "libsed.h"
#include "sed.h"
#include "regexp.h"

#define CCEOF 22

static int fcomp(sed_commands_t *commands, apr_file_t *fin);
static char *compsub(sed_commands_t *commands,
                     sed_comp_args *compargs, char *rhsbuf);
static int rline(sed_commands_t *commands, apr_file_t *fin,
                 char *lbuf, char *lbend);
static char *address(sed_commands_t *commands, char *expbuf,
                     apr_status_t* status);
static char *text(sed_commands_t *commands, char *textbuf, char *endbuf);
static sed_label_t *search(sed_commands_t *commands);
static char *ycomp(sed_commands_t *commands, char *expbuf);
static char *comple(sed_commands_t *commands, sed_comp_args *compargs,
                    char *x1, char *ep, char *x3, char x4);
static sed_reptr_t *alloc_reptr(sed_commands_t *commands);
static int check_finalized(const sed_commands_t *commands);

void command_errf(sed_commands_t *commands, const char *fmt, ...)
{
    if (commands->errfn && commands->pool) {
        va_list args;
        const char* error;
        va_start(args, fmt);
        error = apr_pvsprintf(commands->pool, fmt, args);
        commands->errfn(commands->data, error);
        va_end(args);
    }
}

/*
 * sed_init_commands
 */
apr_status_t sed_init_commands(sed_commands_t *commands, sed_err_fn_t *errfn, void *data,
                               apr_pool_t *p)
{
    memset(commands, 0, sizeof(*commands));

    commands->errfn = errfn;
    commands->data = data;

    commands->labtab = commands->ltab;
    commands->lab = commands->labtab + 1;
    commands->pool = p;

    commands->respace = apr_pcalloc(p, RESIZE);
    if (commands->respace == NULL) {
        command_errf(commands, SEDERR_OOMMES);
        return APR_EGENERAL;
    }

    commands->rep = alloc_reptr(commands);
    if (commands->rep == NULL)
        return APR_EGENERAL;

    commands->rep->ad1 = commands->respace;
    commands->reend = &commands->respace[RESIZE - 1];
    commands->labend = &commands->labtab[SED_LABSIZE];
    commands->canbefinal = 1;

    return APR_SUCCESS;
}

/*
 * sed_destroy_commands
 */
void sed_destroy_commands(sed_commands_t *commands)
{
}

/*
 * sed_compile_string
 */
apr_status_t sed_compile_string(sed_commands_t *commands, const char *s)
{
    apr_status_t rv;

    commands->earg = s;
    commands->eflag = 1;

    rv = fcomp(commands, NULL);
    if (rv == APR_SUCCESS)
        commands->canbefinal = check_finalized(commands);

    commands->eflag = 0;

    return (rv != 0 ? APR_EGENERAL : APR_SUCCESS);
}

/*
 * sed_compile_file
 */
apr_status_t sed_compile_file(sed_commands_t *commands, apr_file_t *fin)
{
    apr_status_t rv = fcomp(commands, fin);
    return (rv != 0 ? APR_EGENERAL : APR_SUCCESS);
}

/*
 * sed_get_finalize_error
 */
char* sed_get_finalize_error(const sed_commands_t *commands, apr_pool_t* pool)
{
    const sed_label_t *lab;
    if (commands->depth) {
        return SEDERR_TMOMES;
    }

    /* Empty branch chain is not a issue */
    for (lab = commands->labtab + 1; lab < commands->lab; lab++) {
        char *error;
        if (lab->address == 0) {
            error = apr_psprintf(pool, SEDERR_ULMES, lab->asc);
            return error;
        }

        if (lab->chain) {
            return SEDERR_INTERNAL;
        }
    }
    return NULL;
}

/*
 * sed_canbe_finalized
 */
int sed_canbe_finalized(const sed_commands_t *commands)
{
    return commands->canbefinal;
}

/*
 * check_finalized
 */
static int check_finalized(const sed_commands_t *commands)
{
    const sed_label_t *lab;
    if (commands->depth) {
        return 0;
    }

    /* Empty branch chain is not a issue */
    for (lab = commands->labtab + 1; lab < commands->lab; lab++) {
        if (lab->address == 0 || (lab->chain)) {
            return 0;
        }
    }
    return 1;
}

/*
 * dechain
 */
static void dechain(sed_label_t *lpt, sed_reptr_t *address)
{
    sed_reptr_t *rep;
    if ((lpt == NULL) || (lpt->chain == NULL) || (address == NULL))
        return;
    rep = lpt->chain;
    while (rep->lb1) {
        sed_reptr_t *next;

        next = rep->lb1;
        rep->lb1 = address;
        rep = next;
    }
    rep->lb1 = address;
    lpt->chain = NULL;
}

/*
 * fcomp
 */
static int fcomp(sed_commands_t *commands, apr_file_t *fin)
{
    char *p, *op, *tp;
    sed_reptr_t *pt, *pt1;
    int i, ii;
    sed_label_t *lpt;
    char fnamebuf[APR_PATH_MAX];
    apr_status_t status;
    sed_comp_args compargs;

    op = commands->lastre;
    if (!commands->linebuf) {
        commands->linebuf = apr_pcalloc(commands->pool, LBSIZE + 1);
    }

    if (rline(commands, fin, commands->linebuf,
              (commands->linebuf + LBSIZE + 1)) < 0)
        return 0;
    if (*commands->linebuf == '#') {
        if (commands->linebuf[1] == 'n')
            commands->nflag = 1;
    }
    else {
        commands->cp = commands->linebuf;
        goto comploop;
    }

    for (;;) {
        if (rline(commands, fin, commands->linebuf,
                  (commands->linebuf + LBSIZE + 1)) < 0)
            break;

        commands->cp = commands->linebuf;

comploop:
        while (*commands->cp == ' ' || *commands->cp == '\t')
            commands->cp++;
        if (*commands->cp == '\0' || *commands->cp == '#')
            continue;
        if (*commands->cp == ';') {
            commands->cp++;
            goto comploop;
        }

        p = address(commands, commands->rep->ad1, &status);
        if (status != APR_SUCCESS) {
            command_errf(commands, SEDERR_CGMES, commands->linebuf);
            return -1;
        }

        if (p == commands->rep->ad1) {
            if (op)
                commands->rep->ad1 = op;
            else {
                command_errf(commands, SEDERR_NRMES);
                return -1;
            }
        } else if (p == 0) {
            p = commands->rep->ad1;
            commands->rep->ad1 = 0;
        } else {
            op = commands->rep->ad1;
            if (*commands->cp == ',' || *commands->cp == ';') {
                commands->cp++;
                commands->rep->ad2 = p;
                p = address(commands, commands->rep->ad2, &status);
                if ((status != APR_SUCCESS) || (p == 0)) {
                    command_errf(commands, SEDERR_CGMES, commands->linebuf);
                    return -1;
                }
                if (p == commands->rep->ad2)
                    commands->rep->ad2 = op;
                else
                    op = commands->rep->ad2;
            } else
                commands->rep->ad2 = 0;
        }

        if(p > &commands->respace[RESIZE-1]) {
            command_errf(commands, SEDERR_TMMES, commands->linebuf);
            return -1;
        }

        while (*commands->cp == ' ' || *commands->cp == '\t')
            commands->cp++;

swit:
        switch(*commands->cp++) {
        default:
            command_errf(commands, SEDERR_UCMES, commands->linebuf);
            return -1;

        case '!':
            commands->rep->negfl = 1;
            goto swit;

        case '{':
            commands->rep->command = BCOM;
            commands->rep->negfl = !(commands->rep->negfl);
            commands->cmpend[commands->depth++] = &commands->rep->lb1;
            commands->rep = alloc_reptr(commands);
            commands->rep->ad1 = p;
            if (*commands->cp == '\0')
                continue;
            goto comploop;

        case '}':
            if (commands->rep->ad1) {
                command_errf(commands, SEDERR_AD0MES, commands->linebuf);
                return -1;
            }

            if (--commands->depth < 0) {
                command_errf(commands, SEDERR_TMCMES);
                return -1;
            }
            *commands->cmpend[commands->depth] = commands->rep;

            commands->rep->ad1 = p;
            continue;

        case '=':
            commands->rep->command = EQCOM;
            if (commands->rep->ad2) {
                command_errf(commands, SEDERR_AD1MES, commands->linebuf);
                return -1;
            }
            break;

        case ':':
            if (commands->rep->ad1) {
                command_errf(commands, SEDERR_AD0MES, commands->linebuf);
                return -1;
            }

            while (*commands->cp++ == ' ');
            commands->cp--;

            tp = commands->lab->asc;
            while ((*tp++ = *commands->cp++)) {
                if (tp >= &(commands->lab->asc[8])) {
                    command_errf(commands, SEDERR_LTLMES, commands->linebuf);
                    return -1;
                }
            }
            *--tp = '\0';

            if ((lpt = search(commands)) != NULL) {
                if (lpt->address) {
                    command_errf(commands, SEDERR_DLMES, commands->linebuf);
                    return -1;
                }
                dechain(lpt, commands->rep);
            } else {
                commands->lab->chain = 0;
                lpt = commands->lab;
                if (++commands->lab >= commands->labend) {
                    command_errf(commands, SEDERR_TMLMES, commands->linebuf);
                    return -1;
                }
            }
            lpt->address = commands->rep;
            commands->rep->ad1 = p;

            continue;

        case 'a':
            commands->rep->command = ACOM;
            if (commands->rep->ad2) {
                command_errf(commands, SEDERR_AD1MES, commands->linebuf);
                return -1;
            }
            if (*commands->cp == '\\')
                commands->cp++;
            if (*commands->cp++ != '\n') {
                command_errf(commands, SEDERR_CGMES, commands->linebuf);
                return -1;
            }
            commands->rep->re1 = p;
            p = text(commands, commands->rep->re1, commands->reend);
            if (p == NULL)
                return -1;
            break;

        case 'c':
            commands->rep->command = CCOM;
            if (*commands->cp == '\\') commands->cp++;
            if (*commands->cp++ != ('\n')) {
                command_errf(commands, SEDERR_CGMES, commands->linebuf);
                return -1;
            }
            commands->rep->re1 = p;
            p = text(commands, commands->rep->re1, commands->reend);
            if (p == NULL)
                return -1;
            break;

        case 'i':
            commands->rep->command = ICOM;
            if (commands->rep->ad2) {
                command_errf(commands, SEDERR_AD1MES, commands->linebuf);
                return -1;
            }
            if (*commands->cp == '\\') commands->cp++;
            if (*commands->cp++ != ('\n')) {
                command_errf(commands, SEDERR_CGMES, commands->linebuf);
                return -1;
            }
            commands->rep->re1 = p;
            p = text(commands, commands->rep->re1, commands->reend);
            if (p == NULL)
                return -1;
            break;

        case 'g':
            commands->rep->command = GCOM;
            break;

        case 'G':
            commands->rep->command = CGCOM;
            break;

        case 'h':
            commands->rep->command = HCOM;
            break;

        case 'H':
            commands->rep->command = CHCOM;
            break;

        case 't':
            commands->rep->command = TCOM;
            goto jtcommon;

        case 'b':
            commands->rep->command = BCOM;
jtcommon:
            while (*commands->cp++ == ' ');
            commands->cp--;

            if (*commands->cp == '\0') {
                if ((pt = commands->labtab->chain) != NULL) {
                    while ((pt1 = pt->lb1) != NULL)
                        pt = pt1;
                    pt->lb1 = commands->rep;
                } else
                    commands->labtab->chain = commands->rep;
                break;
            }
            tp = commands->lab->asc;
            while ((*tp++ = *commands->cp++))
                if (tp >= &(commands->lab->asc[8])) {
                    command_errf(commands, SEDERR_LTLMES, commands->linebuf);
                    return -1;
                }
            commands->cp--;
            *--tp = '\0';

            if ((lpt = search(commands)) != NULL) {
                if (lpt->address) {
                    commands->rep->lb1 = lpt->address;
                } else {
                    pt = lpt->chain;
                    while ((pt1 = pt->lb1) != NULL)
                        pt = pt1;
                    pt->lb1 = commands->rep;
                }
            } else {
                commands->lab->chain = commands->rep;
                commands->lab->address = 0;
                if (++commands->lab >= commands->labend) {
                    command_errf(commands, SEDERR_TMLMES, commands->linebuf);
                    return -1;
                }
            }
            break;

        case 'n':
            commands->rep->command = NCOM;
            break;

        case 'N':
            commands->rep->command = CNCOM;
            break;

        case 'p':
            commands->rep->command = PCOM;
            break;

        case 'P':
            commands->rep->command = CPCOM;
            break;

        case 'r':
            commands->rep->command = RCOM;
            if (commands->rep->ad2) {
                command_errf(commands, SEDERR_AD1MES, commands->linebuf);
                return -1;
            }
            if (*commands->cp++ != ' ') {
                command_errf(commands, SEDERR_CGMES, commands->linebuf);
                return -1;
            }
            commands->rep->re1 = p;
            p = text(commands, commands->rep->re1, commands->reend);
            if (p == NULL)
                return -1;
            break;

        case 'd':
            commands->rep->command = DCOM;
            break;

        case 'D':
            commands->rep->command = CDCOM;
            commands->rep->lb1 = commands->ptrspace;
            break;

        case 'q':
            commands->rep->command = QCOM;
            if (commands->rep->ad2) {
                command_errf(commands, SEDERR_AD1MES, commands->linebuf);
                return -1;
            }
            break;

        case 'l':
            commands->rep->command = LCOM;
            break;

        case 's':
            commands->rep->command = SCOM;
            commands->sseof = *commands->cp++;
            commands->rep->re1 = p;
            p = comple(commands, &compargs, (char *) 0, commands->rep->re1,
                       commands->reend, commands->sseof);
            if (p == NULL)
                return -1;
            if (p == commands->rep->re1) {
                if (op)
                    commands->rep->re1 = op;
                else {
                    command_errf(commands, SEDERR_NRMES);
                    return -1;
                }
            } else
                op = commands->rep->re1;
            commands->rep->rhs = p;

            p = compsub(commands, &compargs, commands->rep->rhs);
            if ((p) == NULL)
                return -1;

            if (*commands->cp == 'g') {
                commands->cp++;
                commands->rep->gfl = 999;
            } else if (commands->gflag)
                commands->rep->gfl = 999;

            if (*commands->cp >= '1' && *commands->cp <= '9') {
                i = *commands->cp - '0';
                commands->cp++;
                while (1) {
                    ii = *commands->cp;
                    if (ii < '0' || ii > '9')
                        break;
                    i = i*10 + ii - '0';
                    if (i > 512) {
                        command_errf(commands, SEDERR_TOOBIG, commands->linebuf);
                        return -1;
                    }
                    commands->cp++;
                }
                commands->rep->gfl = i;
            }

            if (*commands->cp == 'p') {
                commands->cp++;
                commands->rep->pfl = 1;
            }

            if (*commands->cp == 'P') {
                commands->cp++;
                commands->rep->pfl = 2;
            }

            if (*commands->cp == 'w') {
                commands->cp++;
                if (*commands->cp++ !=  ' ') {
                    command_errf(commands, SEDERR_SMMES, commands->linebuf);
                    return -1;
                }
                if (text(commands, fnamebuf, &fnamebuf[APR_PATH_MAX-1]) == NULL) {
                    command_errf(commands, SEDERR_FNTL, commands->linebuf);
                    return -1;
                }
                for (i = commands->nfiles - 1; i >= 0; i--)
                    if (strcmp(fnamebuf,commands->fname[i]) == 0) {
                        commands->rep->findex = i;
                        goto done;
                    }
                if (commands->nfiles >= NWFILES) {
                    command_errf(commands, SEDERR_TMWFMES);
                    return -1;
                }
                commands->fname[commands->nfiles] =
                            apr_pstrdup(commands->pool, fnamebuf);
                if (commands->fname[commands->nfiles] == NULL) {
                    command_errf(commands, SEDERR_OOMMES);
                    return -1;
                }
                commands->rep->findex = commands->nfiles++;
            }
            break;

        case 'w':
            commands->rep->command = WCOM;
            if (*commands->cp++ != ' ') {
                command_errf(commands, SEDERR_SMMES, commands->linebuf);
                return -1;
            }
            if (text(commands, fnamebuf, &fnamebuf[APR_PATH_MAX-1]) == NULL) {
                command_errf(commands, SEDERR_FNTL, commands->linebuf);
                return -1;
            }
            for (i = commands->nfiles - 1; i >= 0; i--)
                if (strcmp(fnamebuf, commands->fname[i]) == 0) {
                    commands->rep->findex = i;
                    goto done;
                }
            if (commands->nfiles >= NWFILES) {
                command_errf(commands, SEDERR_TMWFMES);
                return -1;
            }
            if ((commands->fname[commands->nfiles] =
                        apr_pstrdup(commands->pool, fnamebuf)) == NULL) {
                command_errf(commands, SEDERR_OOMMES);
                return -1;
            }

            commands->rep->findex = commands->nfiles++;
            break;

        case 'x':
            commands->rep->command = XCOM;
            break;

        case 'y':
            commands->rep->command = YCOM;
            commands->sseof = *commands->cp++;
            commands->rep->re1 = p;
            p = ycomp(commands, commands->rep->re1);
            if (p == NULL)
                return -1;
            break;
        }
done:
        commands->rep = alloc_reptr(commands);

        commands->rep->ad1 = p;

        if (*commands->cp++ != '\0') {
            if (commands->cp[-1] == ';')
                goto comploop;
            command_errf(commands, SEDERR_CGMES, commands->linebuf);
            return -1;
        }
    }
    commands->rep->command = 0;
    commands->lastre = op;

    return 0;
}

static char *compsub(sed_commands_t *commands,
                     sed_comp_args *compargs, char *rhsbuf)
{
    char   *p, *q;

    p = rhsbuf;
    q = commands->cp;
    for(;;) {
        if(p > &commands->respace[RESIZE-1]) {
            command_errf(commands, SEDERR_TMMES, commands->linebuf);
            return NULL;
        }
        if((*p = *q++) == '\\') {
            p++;
            if(p > &commands->respace[RESIZE-1]) {
                command_errf(commands, SEDERR_TMMES, commands->linebuf);
                return NULL;
            }
            *p = *q++;
            if(*p > compargs->nbra + '0' && *p <= '9') {
                command_errf(commands, SEDERR_DOORNG, commands->linebuf);
                return NULL;
            }
            p++;
            continue;
        }
        if(*p == commands->sseof) {
            *p++ = '\0';
            commands->cp = q;
            return(p);
        }
          if(*p++ == '\0') {
            command_errf(commands, SEDERR_EDMOSUB, commands->linebuf);
            return NULL;
        }
    }
}

/*
 * rline
 */
static int rline(sed_commands_t *commands, apr_file_t *fin,
                 char *lbuf, char *lbend)
{
    char   *p;
    const char *q;
    int    t;
    apr_size_t bytes_read;

    p = lbuf;

    if(commands->eflag) {
        if(commands->eflag > 0) {
            commands->eflag = -1;
            q = commands->earg;
            while((t = *q++) != '\0') {
                if(t == '\n') {
                    commands->saveq = q;
                    goto out1;
                }
                if (p < lbend)
                    *p++ = t;
                if(t == '\\') {
                    if((t = *q++) == '\0') {
                        commands->saveq = NULL;
                        return(-1);
                    }
                    if (p < lbend)
                        *p++ = t;
                }
            }
            commands->saveq = NULL;

        out1:
            if (p == lbend) {
                command_errf(commands, SEDERR_CLTL);
                return -1;
            }
            *p = '\0';
            return(1);
        }
        if((q = commands->saveq) == 0)    return(-1);

        while((t = *q++) != '\0') {
            if(t == '\n') {
                commands->saveq = q;
                goto out2;
            }
            if(p < lbend)
                *p++ = t;
            if(t == '\\') {
                if((t = *q++) == '\0') {
                    commands->saveq = NULL;
                    return(-1);
                }
                if (p < lbend)
                    *p++ = t;
            }
        }
        commands->saveq = NULL;

    out2:
        if (p == lbend) {
            command_errf(commands, SEDERR_CLTL);
            return -1;
        }
        *p = '\0';
        return(1);
    }

    bytes_read = 1;
    /* XXX extremely inefficient 1 byte reads */
    while (apr_file_read(fin, &t, &bytes_read) != APR_SUCCESS) {
        if(t == '\n') {
            if (p == lbend) {
                command_errf(commands, SEDERR_CLTL);
                return -1;
            }
            *p = '\0';
            return(1);
        }
        if (p < lbend)
            *p++ = t;
        if(t == '\\') {
            bytes_read = 1;
            if (apr_file_read(fin, &t, &bytes_read) != APR_SUCCESS) {
                return -1;
            }
            if(p < lbend)
                *p++ = t;
        }
        bytes_read = 1;
    }
    return(-1);
}

/*
 * address
 */
static char *address(sed_commands_t *commands, char *expbuf,
                     apr_status_t* status)
{
    char   *rcp;
    apr_int64_t lno;
    sed_comp_args compargs;

    *status = APR_SUCCESS;
    if(*commands->cp == '$') {
        if (expbuf > &commands->respace[RESIZE-2]) {
            command_errf(commands, SEDERR_TMMES, commands->linebuf);
            *status = APR_EGENERAL;
            return NULL;
        }
        commands->cp++;
        *expbuf++ = CEND;
        *expbuf++ = CCEOF;
        return(expbuf);
    }
    if (*commands->cp == '/' || *commands->cp == '\\' ) {
        if ( *commands->cp == '\\' )
            commands->cp++;
        commands->sseof = *commands->cp++;
        return(comple(commands, &compargs, (char *) 0, expbuf, commands->reend,
                      commands->sseof));
    }

    rcp = commands->cp;
    lno = 0;

    while(*rcp >= '0' && *rcp <= '9')
        lno = lno*10 + *rcp++ - '0';

    if(rcp > commands->cp) {
        if (expbuf > &commands->respace[RESIZE-3]) {
            command_errf(commands, SEDERR_TMMES, commands->linebuf);
            *status = APR_EGENERAL;
            return NULL;
        }
        *expbuf++ = CLNUM;
        *expbuf++ = commands->nlno;
        commands->tlno[commands->nlno++] = lno;
        if(commands->nlno >= SED_NLINES) {
            command_errf(commands, SEDERR_TMLNMES);
            *status = APR_EGENERAL;
            return NULL;
        }
        *expbuf++ = CCEOF;
        commands->cp = rcp;
        return(expbuf);
    }
    return(NULL);
}

/*
 * text
 */
static char *text(sed_commands_t *commands, char *textbuf, char *tbend)
{
    char   *p, *q;

    p = textbuf;
    q = commands->cp;
#ifndef S5EMUL
    /*
     * Strip off indentation from text to be inserted.
     */
    while(*q == '\t' || *q == ' ')    q++;
#endif
    for(;;) {

        if(p > tbend)
            return(NULL);    /* overflowed the buffer */
        if((*p = *q++) == '\\')
            *p = *q++;
        if(*p == '\0') {
            commands->cp = --q;
            return(++p);
        }
#ifndef S5EMUL
        /*
         * Strip off indentation from text to be inserted.
         */
        if(*p == '\n') {
            while(*q == '\t' || *q == ' ')    q++;
        }
#endif
        p++;
    }
}


/*
 * search
 */
static sed_label_t *search(sed_commands_t *commands)
{
    sed_label_t *rp;
    sed_label_t *ptr;

    rp = commands->labtab;
    ptr = commands->lab;
    while (rp < ptr) {
        if (strcmp(rp->asc, ptr->asc) == 0)
            return rp;
        rp++;
    }

    return 0;
}

/*
 * ycomp
 */
static char *ycomp(sed_commands_t *commands, char *expbuf)
{
    char    c;
    int cint; /* integer value of char c */
    char *ep, *tsp;
    int i;
    char    *sp;

    ep = expbuf;
    if(ep + 0377 > &commands->respace[RESIZE-1]) {
        command_errf(commands, SEDERR_TMMES, commands->linebuf);
        return NULL;
    }
    sp = commands->cp;
    for(tsp = commands->cp; (c = *tsp) != commands->sseof; tsp++) {
        if(c == '\\')
            tsp++;
        if(c == '\0' || c == '\n') {
            command_errf(commands, SEDERR_EDMOSTR, commands->linebuf);
            return NULL;
        }
    }
    tsp++;
    memset(ep, 0, 0400);

    while((c = *sp++) != commands->sseof) {
        c &= 0377;
        if(c == '\\' && *sp == 'n') {
            sp++;
            c = '\n';
        }
        cint = (int) c;
        if((ep[cint] = *tsp++) == '\\' && *tsp == 'n') {
            ep[cint] = '\n';
            tsp++;
        }
        if(ep[cint] == commands->sseof || ep[cint] == '\0') {
            command_errf(commands, SEDERR_TSNTSS, commands->linebuf);
        }
    }
    if(*tsp != commands->sseof) {
        if(*tsp == '\0') {
            command_errf(commands, SEDERR_EDMOSTR, commands->linebuf);
        }
        else {
            command_errf(commands, SEDERR_TSNTSS, commands->linebuf);
        }
        return NULL;
    }
    commands->cp = ++tsp;

    for(i = 0; i < 0400; i++)
        if(ep[i] == 0)
            ep[i] = i;

    return(ep + 0400);
}

/*
 * comple
 */
static char *comple(sed_commands_t *commands, sed_comp_args *compargs,
                    char *x1, char *ep, char *x3, char x4)
{
    char *p;

    p = sed_compile(commands, compargs, ep + 1, x3, x4);
    if(p == ep + 1)
        return(ep);
    *ep = compargs->circf;
    return(p);
}

/*
 * alloc_reptr
 */
static sed_reptr_t *alloc_reptr(sed_commands_t *commands)
{
    sed_reptr_t *var;

    var = apr_pcalloc(commands->pool, sizeof(sed_reptr_t));
    if (var == NULL) {
        command_errf(commands, SEDERR_OOMMES);
        return 0;
    }

    var->nrep = commands->nrep;
    var->findex = -1;
    commands->nrep++;

    if (commands->ptrspace == NULL)
        commands->ptrspace = var;
    else
        commands->ptrend->next = var;

    commands->ptrend = var;
    commands->labtab->address = var;
    return var;
}


