1/*
2 * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved.
3 * Use is subject to license terms.
4 *
5 *      Copyright (c) 1984 AT&T
6 *        All Rights Reserved
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *  http://www.apache.org/licenses/LICENSE-2.0.
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
16 * or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21#include "apr.h"
22#include "apr_lib.h"
23#include "libsed.h"
24#include "sed.h"
25#include "apr_strings.h"
26#include "regexp.h"
27
28static const char *const trans[040]  = {
29    "\\01",
30    "\\02",
31    "\\03",
32    "\\04",
33    "\\05",
34    "\\06",
35    "\\07",
36    "\\10",
37    "\\11",
38    "\n",
39    "\\13",
40    "\\14",
41    "\\15",
42    "\\16",
43    "\\17",
44    "\\20",
45    "\\21",
46    "\\22",
47    "\\23",
48    "\\24",
49    "\\25",
50    "\\26",
51    "\\27",
52    "\\30",
53    "\\31",
54    "\\32",
55    "\\33",
56    "\\34",
57    "\\35",
58    "\\36",
59    "\\37"
60};
61static const char rub[] = {"\\177"};
62
63extern int sed_step(char *p1, char *p2, int circf, step_vars_storage *vars);
64static int substitute(sed_eval_t *eval, sed_reptr_t *ipc,
65                      step_vars_storage *step_vars);
66static apr_status_t execute(sed_eval_t *eval);
67static int match(sed_eval_t *eval, char *expbuf, int gf,
68                 step_vars_storage *step_vars);
69static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n,
70                          step_vars_storage *step_vars);
71static char *place(sed_eval_t *eval, char *asp, char *al1, char *al2);
72static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
73                            step_vars_storage *step_vars);
74static apr_status_t wline(sed_eval_t *eval, char *buf, int sz);
75static apr_status_t arout(sed_eval_t *eval);
76
77static void eval_errf(sed_eval_t *eval, const char *fmt, ...)
78{
79    if (eval->errfn && eval->pool) {
80        va_list args;
81        const char* error;
82        va_start(args, fmt);
83        error = apr_pvsprintf(eval->pool, fmt, args);
84        eval->errfn(eval->data, error);
85        va_end(args);
86    }
87}
88
89#define INIT_BUF_SIZE 1024
90
91/*
92 * grow_buffer
93 */
94static void grow_buffer(apr_pool_t *pool, char **buffer,
95                        char **spend, unsigned int *cursize,
96                        unsigned int newsize)
97{
98    char* newbuffer = NULL;
99    int spendsize = 0;
100    if (*cursize >= newsize)
101        return;
102    /* Avoid number of times realloc is called. It could cause huge memory
103     * requirement if line size is huge e.g 2 MB */
104    if (newsize < *cursize * 2) {
105        newsize = *cursize * 2;
106    }
107
108    /* Align it to 4 KB boundary */
109    newsize = (newsize  + ((1 << 12) - 1)) & ~((1 << 12) -1);
110    newbuffer = apr_pcalloc(pool, newsize);
111    if (*spend && *buffer && (*cursize > 0)) {
112        spendsize = *spend - *buffer;
113    }
114    if ((*cursize > 0) && *buffer) {
115        memcpy(newbuffer, *buffer, *cursize);
116    }
117    *buffer = newbuffer;
118    *cursize = newsize;
119    if (spend != buffer) {
120        *spend = *buffer + spendsize;
121    }
122}
123
124/*
125 * grow_line_buffer
126 */
127static void grow_line_buffer(sed_eval_t *eval, int newsize)
128{
129    grow_buffer(eval->pool, &eval->linebuf, &eval->lspend,
130                &eval->lsize, newsize);
131}
132
133/*
134 * grow_hold_buffer
135 */
136static void grow_hold_buffer(sed_eval_t *eval, int newsize)
137{
138    grow_buffer(eval->pool, &eval->holdbuf, &eval->hspend,
139                &eval->hsize, newsize);
140}
141
142/*
143 * grow_gen_buffer
144 */
145static void grow_gen_buffer(sed_eval_t *eval, int newsize,
146                            char **gspend)
147{
148    if (gspend == NULL) {
149        gspend = &eval->genbuf;
150    }
151    grow_buffer(eval->pool, &eval->genbuf, gspend,
152                &eval->gsize, newsize);
153    eval->lcomend = &eval->genbuf[71];
154}
155
156/*
157 * appendmem_to_linebuf
158 */
159static void appendmem_to_linebuf(sed_eval_t *eval, const char* sz, int len)
160{
161    unsigned int reqsize = (eval->lspend - eval->linebuf) + len;
162    if (eval->lsize < reqsize) {
163        grow_line_buffer(eval, reqsize);
164    }
165    memcpy(eval->lspend, sz, len);
166    eval->lspend += len;
167}
168
169/*
170 * append_to_linebuf
171 */
172static void append_to_linebuf(sed_eval_t *eval, const char* sz)
173{
174    int len = strlen(sz);
175    /* Copy string including null character */
176    appendmem_to_linebuf(eval, sz, len + 1);
177    --eval->lspend; /* lspend will now point to NULL character */
178}
179
180/*
181 * copy_to_linebuf
182 */
183static void copy_to_linebuf(sed_eval_t *eval, const char* sz)
184{
185    eval->lspend = eval->linebuf;
186    append_to_linebuf(eval, sz);
187}
188
189/*
190 * append_to_holdbuf
191 */
192static void append_to_holdbuf(sed_eval_t *eval, const char* sz)
193{
194    int len = strlen(sz);
195    unsigned int reqsize = (eval->hspend - eval->holdbuf) + len + 1;
196    if (eval->hsize <= reqsize) {
197        grow_hold_buffer(eval, reqsize);
198    }
199    strcpy(eval->hspend, sz);
200    /* hspend will now point to NULL character */
201    eval->hspend += len;
202}
203
204/*
205 * copy_to_holdbuf
206 */
207static void copy_to_holdbuf(sed_eval_t *eval, const char* sz)
208{
209    eval->hspend = eval->holdbuf;
210    append_to_holdbuf(eval, sz);
211}
212
213/*
214 * append_to_genbuf
215 */
216static void append_to_genbuf(sed_eval_t *eval, const char* sz, char **gspend)
217{
218    int len = strlen(sz);
219    unsigned int reqsize = (*gspend - eval->genbuf) + len + 1;
220    if (eval->gsize < reqsize) {
221        grow_gen_buffer(eval, reqsize, gspend);
222    }
223    strcpy(*gspend, sz);
224    /* *gspend will now point to NULL character */
225    *gspend += len;
226}
227
228/*
229 * copy_to_genbuf
230 */
231static void copy_to_genbuf(sed_eval_t *eval, const char* sz)
232{
233    int len = strlen(sz);
234    unsigned int reqsize = len + 1;
235    if (eval->gsize < reqsize) {
236        grow_gen_buffer(eval, reqsize, NULL);
237    }
238}
239
240/*
241 * sed_init_eval
242 */
243apr_status_t sed_init_eval(sed_eval_t *eval, sed_commands_t *commands, sed_err_fn_t *errfn, void *data, sed_write_fn_t *writefn, apr_pool_t* p)
244{
245    memset(eval, 0, sizeof(*eval));
246    eval->pool = p;
247    eval->writefn = writefn;
248    return sed_reset_eval(eval, commands, errfn, data);
249}
250
251/*
252 * sed_reset_eval
253 */
254apr_status_t sed_reset_eval(sed_eval_t *eval, sed_commands_t *commands, sed_err_fn_t *errfn, void *data)
255{
256    int i;
257
258    eval->errfn = errfn;
259    eval->data = data;
260
261    eval->commands = commands;
262
263    eval->lnum = 0;
264    eval->fout = NULL;
265
266    if (eval->linebuf == NULL) {
267        eval->lsize = INIT_BUF_SIZE;
268        eval->linebuf = apr_pcalloc(eval->pool, eval->lsize);
269    }
270    if (eval->holdbuf == NULL) {
271        eval->hsize = INIT_BUF_SIZE;
272        eval->holdbuf = apr_pcalloc(eval->pool, eval->hsize);
273    }
274    if (eval->genbuf == NULL) {
275        eval->gsize = INIT_BUF_SIZE;
276        eval->genbuf = apr_pcalloc(eval->pool, eval->gsize);
277    }
278    eval->lspend = eval->linebuf;
279    eval->hspend = eval->holdbuf;
280    eval->lcomend = &eval->genbuf[71];
281
282    for (i = 0; i < sizeof(eval->abuf) / sizeof(eval->abuf[0]); i++)
283        eval->abuf[i] = NULL;
284    eval->aptr = eval->abuf;
285    eval->pending = NULL;
286    eval->inar = apr_pcalloc(eval->pool, commands->nrep * sizeof(unsigned char));
287    eval->nrep = commands->nrep;
288
289    eval->dolflag = 0;
290    eval->sflag = 0;
291    eval->jflag = 0;
292    eval->delflag = 0;
293    eval->lreadyflag = 0;
294    eval->quitflag = 0;
295    eval->finalflag = 1; /* assume we're evaluating only one file/stream */
296    eval->numpass = 0;
297    eval->nullmatch = 0;
298    eval->col = 0;
299
300    for (i = 0; i < commands->nfiles; i++) {
301        const char* filename = commands->fname[i];
302        if (apr_file_open(&eval->fcode[i], filename,
303                          APR_WRITE | APR_CREATE, APR_OS_DEFAULT,
304                          eval->pool) != APR_SUCCESS) {
305            eval_errf(eval, SEDERR_COMES, filename);
306            return APR_EGENERAL;
307        }
308    }
309
310    return APR_SUCCESS;
311}
312
313/*
314 * sed_destroy_eval
315 */
316void sed_destroy_eval(sed_eval_t *eval)
317{
318    int i;
319    /* eval->linebuf, eval->holdbuf, eval->genbuf and eval->inar are allocated
320     * on pool. It will be freed when pool will be freed */
321    for (i = 0; i < eval->commands->nfiles; i++) {
322        if (eval->fcode[i] != NULL) {
323            apr_file_close(eval->fcode[i]);
324            eval->fcode[i] = NULL;
325        }
326    }
327}
328
329/*
330 * sed_eval_file
331 */
332apr_status_t sed_eval_file(sed_eval_t *eval, apr_file_t *fin, void *fout)
333{
334    for (;;) {
335        char buf[1024];
336        apr_size_t read_bytes = 0;
337
338        read_bytes = sizeof(buf);
339        if (apr_file_read(fin, buf, &read_bytes) != APR_SUCCESS)
340            break;
341
342        if (sed_eval_buffer(eval, buf, read_bytes, fout) != APR_SUCCESS)
343            return APR_EGENERAL;
344
345        if (eval->quitflag)
346            return APR_SUCCESS;
347    }
348
349    return sed_finalize_eval(eval, fout);
350}
351
352/*
353 * sed_eval_buffer
354 */
355apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, int bufsz, void *fout)
356{
357    apr_status_t rv;
358
359    if (eval->quitflag)
360        return APR_SUCCESS;
361
362    if (!sed_canbe_finalized(eval->commands)) {
363        /* Commands were not finalized properly. */
364        const char* error = sed_get_finalize_error(eval->commands, eval->pool);
365        if (error) {
366            eval_errf(eval, error);
367            return APR_EGENERAL;
368        }
369    }
370
371    eval->fout = fout;
372
373    /* Process leftovers */
374    if (bufsz && eval->lreadyflag) {
375        eval->lreadyflag = 0;
376        eval->lspend--;
377        *eval->lspend = '\0';
378        rv = execute(eval);
379        if (rv != APR_SUCCESS)
380            return rv;
381    }
382
383    while (bufsz) {
384        char *n;
385        int llen;
386
387        n = memchr(buf, '\n', bufsz);
388        if (n == NULL)
389            break;
390
391        llen = n - buf;
392        if (llen == bufsz - 1) {
393            /* This might be the last line; delay its processing */
394            eval->lreadyflag = 1;
395            break;
396        }
397
398        appendmem_to_linebuf(eval, buf, llen + 1);
399        --eval->lspend;
400        /* replace new line character with NULL */
401        *eval->lspend = '\0';
402        buf += (llen + 1);
403        bufsz -= (llen + 1);
404        rv = execute(eval);
405        if (rv != APR_SUCCESS)
406            return rv;
407        if (eval->quitflag)
408            break;
409    }
410
411    /* Save the leftovers for later */
412    if (bufsz) {
413        appendmem_to_linebuf(eval, buf, bufsz);
414    }
415
416    return APR_SUCCESS;
417}
418
419/*
420 * sed_finalize_eval
421 */
422apr_status_t sed_finalize_eval(sed_eval_t *eval, void *fout)
423{
424    if (eval->quitflag)
425        return APR_SUCCESS;
426
427    if (eval->finalflag)
428        eval->dolflag = 1;
429
430    eval->fout = fout;
431
432    /* Process leftovers */
433    if (eval->lspend > eval->linebuf) {
434        apr_status_t rv;
435
436        if (eval->lreadyflag) {
437            eval->lreadyflag = 0;
438            eval->lspend--;
439        } else {
440            /* Code can probably reach here when last character in output
441             * buffer is not a newline.
442             */
443            /* Assure space for NULL */
444            append_to_linebuf(eval, "");
445        }
446
447        *eval->lspend = '\0';
448        rv = execute(eval);
449        if (rv != APR_SUCCESS)
450            return rv;
451    }
452
453    eval->quitflag = 1;
454
455    return APR_SUCCESS;
456}
457
458/*
459 * execute
460 */
461static apr_status_t execute(sed_eval_t *eval)
462{
463    sed_reptr_t *ipc = eval->commands->ptrspace;
464    step_vars_storage step_vars;
465    apr_status_t rv = APR_SUCCESS;
466
467    eval->lnum++;
468
469    eval->sflag = 0;
470
471    if (eval->pending) {
472        ipc = eval->pending;
473        eval->pending = NULL;
474    }
475
476    memset(&step_vars, 0, sizeof(step_vars));
477
478    while (ipc->command) {
479        char *p1;
480        char *p2;
481        int c;
482
483        p1 = ipc->ad1;
484        p2 = ipc->ad2;
485
486        if (p1) {
487
488            if (eval->inar[ipc->nrep]) {
489                if (*p2 == CEND) {
490                    p1 = 0;
491                } else if (*p2 == CLNUM) {
492                    c = (unsigned char)p2[1];
493                    if (eval->lnum > eval->commands->tlno[c]) {
494                        eval->inar[ipc->nrep] = 0;
495                        if (ipc->negfl)
496                            goto yes;
497                        ipc = ipc->next;
498                        continue;
499                    }
500                    if (eval->lnum == eval->commands->tlno[c]) {
501                        eval->inar[ipc->nrep] = 0;
502                    }
503                } else if (match(eval, p2, 0, &step_vars)) {
504                    eval->inar[ipc->nrep] = 0;
505                }
506            } else if (*p1 == CEND) {
507                if (!eval->dolflag) {
508                    if (ipc->negfl)
509                        goto yes;
510                    ipc = ipc->next;
511                    continue;
512                }
513            } else if (*p1 == CLNUM) {
514                c = (unsigned char)p1[1];
515                if (eval->lnum != eval->commands->tlno[c]) {
516                    if (ipc->negfl)
517                        goto yes;
518                    ipc = ipc->next;
519                    continue;
520                }
521                if (p2)
522                    eval->inar[ipc->nrep] = 1;
523            } else if (match(eval, p1, 0, &step_vars)) {
524                if (p2)
525                    eval->inar[ipc->nrep] = 1;
526            } else {
527                if (ipc->negfl)
528                    goto yes;
529                ipc = ipc->next;
530                continue;
531            }
532        }
533
534        if (ipc->negfl) {
535            ipc = ipc->next;
536            continue;
537        }
538
539yes:
540        rv = command(eval, ipc, &step_vars);
541        if (rv != APR_SUCCESS)
542            return rv;
543
544        if (eval->quitflag)
545            return APR_SUCCESS;
546
547        if (eval->pending)
548            return APR_SUCCESS;
549
550        if (eval->delflag)
551            break;
552
553        if (eval->jflag) {
554            eval->jflag = 0;
555            if ((ipc = ipc->lb1) == 0) {
556                ipc = eval->commands->ptrspace;
557                break;
558            }
559        } else
560            ipc = ipc->next;
561    }
562
563    if (!eval->commands->nflag && !eval->delflag) {
564        rv = wline(eval, eval->linebuf, eval->lspend - eval->linebuf);
565        if (rv != APR_SUCCESS)
566            return rv;
567    }
568
569    if (eval->aptr > eval->abuf)
570        rv = arout(eval);
571
572    eval->delflag = 0;
573
574    eval->lspend = eval->linebuf;
575
576    return rv;
577}
578
579/*
580 * match
581 */
582static int match(sed_eval_t *eval, char *expbuf, int gf,
583                 step_vars_storage *step_vars)
584{
585    char   *p1;
586    int circf;
587
588    if(gf) {
589        if(*expbuf)    return(0);
590        step_vars->locs = p1 = step_vars->loc2;
591    } else {
592        p1 = eval->linebuf;
593        step_vars->locs = 0;
594    }
595
596    circf = *expbuf++;
597    return(sed_step(p1, expbuf, circf, step_vars));
598}
599
600/*
601 * substitute
602 */
603static int substitute(sed_eval_t *eval, sed_reptr_t *ipc,
604                      step_vars_storage *step_vars)
605{
606    if(match(eval, ipc->re1, 0, step_vars) == 0)    return(0);
607
608    eval->numpass = 0;
609    eval->sflag = 0;        /* Flags if any substitution was made */
610    if (dosub(eval, ipc->rhs, ipc->gfl, step_vars) != APR_SUCCESS)
611        return -1;
612
613    if(ipc->gfl) {
614        while(*step_vars->loc2) {
615            if(match(eval, ipc->re1, 1, step_vars) == 0) break;
616            if (dosub(eval, ipc->rhs, ipc->gfl, step_vars) != APR_SUCCESS)
617                return -1;
618        }
619    }
620    return(eval->sflag);
621}
622
623/*
624 * dosub
625 */
626static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n,
627                          step_vars_storage *step_vars)
628{
629    char *lp, *sp, *rp;
630    int c;
631    apr_status_t rv = APR_SUCCESS;
632
633    if(n > 0 && n < 999) {
634        eval->numpass++;
635        if(n != eval->numpass) return APR_SUCCESS;
636    }
637    eval->sflag = 1;
638    lp = eval->linebuf;
639    sp = eval->genbuf;
640    rp = rhsbuf;
641    sp = place(eval, sp, lp, step_vars->loc1);
642    while ((c = *rp++) != 0) {
643        if (c == '&') {
644            sp = place(eval, sp, step_vars->loc1, step_vars->loc2);
645            if (sp == NULL)
646                return APR_EGENERAL;
647        }
648        else if (c == '\\') {
649            c = *rp++;
650            if (c >= '1' && c < NBRA+'1') {
651                sp = place(eval, sp, step_vars->braslist[c-'1'],
652                           step_vars->braelist[c-'1']);
653                if (sp == NULL)
654                    return APR_EGENERAL;
655            }
656            else
657                *sp++ = c;
658          } else
659            *sp++ = c;
660        if (sp >= eval->genbuf + eval->gsize) {
661            /* expand genbuf and set the sp appropriately */
662            grow_gen_buffer(eval, eval->gsize + 1024, &sp);
663        }
664    }
665    lp = step_vars->loc2;
666    step_vars->loc2 = sp - eval->genbuf + eval->linebuf;
667    append_to_genbuf(eval, lp, &sp);
668    copy_to_linebuf(eval, eval->genbuf);
669    return rv;
670}
671
672/*
673 * place
674 */
675static char *place(sed_eval_t *eval, char *asp, char *al1, char *al2)
676{
677    char *sp = asp;
678    int n = al2 - al1;
679    unsigned int reqsize = (sp - eval->genbuf) + n + 1;
680
681    if (eval->gsize < reqsize) {
682        grow_gen_buffer(eval, reqsize, &sp);
683    }
684    memcpy(sp, al1, n);
685    return sp + n;
686}
687
688/*
689 * command
690 */
691static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
692                            step_vars_storage *step_vars)
693{
694    int    i;
695    char   *p1, *p2;
696    const char *p3;
697    int length;
698    char sz[32]; /* 32 bytes enough to store 64 bit integer in decimal */
699    apr_status_t rv = APR_SUCCESS;
700
701
702    switch(ipc->command) {
703
704        case ACOM:
705            if(eval->aptr >= &eval->abuf[SED_ABUFSIZE]) {
706                eval_errf(eval, SEDERR_TMAMES, eval->lnum);
707            } else {
708                *eval->aptr++ = ipc;
709                *eval->aptr = NULL;
710            }
711            break;
712
713        case CCOM:
714            eval->delflag = 1;
715            if(!eval->inar[ipc->nrep] || eval->dolflag) {
716                for (p1 = ipc->re1; *p1; p1++)
717                    ;
718                rv = wline(eval, ipc->re1, p1 - ipc->re1);
719            }
720            break;
721        case DCOM:
722            eval->delflag++;
723            break;
724        case CDCOM:
725            p1 = eval->linebuf;
726
727            while(*p1 != '\n') {
728                if(*p1++ == 0) {
729                    eval->delflag++;
730                    return APR_SUCCESS;
731                }
732            }
733
734            p1++;
735            copy_to_linebuf(eval, p1);
736            eval->jflag++;
737            break;
738
739        case EQCOM:
740            length = apr_snprintf(sz, sizeof(sz), "%d", (int) eval->lnum);
741            rv = wline(eval, sz, length);
742            break;
743
744        case GCOM:
745            copy_to_linebuf(eval, eval->holdbuf);
746            break;
747
748        case CGCOM:
749            append_to_linebuf(eval, "\n");
750            append_to_linebuf(eval, eval->holdbuf);
751            break;
752
753        case HCOM:
754            copy_to_holdbuf(eval, eval->linebuf);
755            break;
756
757        case CHCOM:
758            append_to_holdbuf(eval, "\n");
759            append_to_holdbuf(eval, eval->linebuf);
760            break;
761
762        case ICOM:
763            for (p1 = ipc->re1; *p1; p1++);
764            rv = wline(eval, ipc->re1, p1 - ipc->re1);
765            break;
766
767        case BCOM:
768            eval->jflag = 1;
769            break;
770
771
772        case LCOM:
773            p1 = eval->linebuf;
774            p2 = eval->genbuf;
775            eval->genbuf[72] = 0;
776            while(*p1)
777                if((unsigned char)*p1 >= 040) {
778                    if(*p1 == 0177) {
779                        p3 = rub;
780                        while ((*p2++ = *p3++) != 0)
781                            if(p2 >= eval->lcomend) {
782                                *p2 = '\\';
783                                rv = wline(eval, eval->genbuf,
784                                           strlen(eval->genbuf));
785                                if (rv != APR_SUCCESS)
786                                    return rv;
787                                p2 = eval->genbuf;
788                            }
789                        p2--;
790                        p1++;
791                        continue;
792                    }
793                    if(!isprint(*p1 & 0377)) {
794                        *p2++ = '\\';
795                        if(p2 >= eval->lcomend) {
796                            *p2 = '\\';
797                            rv = wline(eval, eval->genbuf,
798                                       strlen(eval->genbuf));
799                            if (rv != APR_SUCCESS)
800                                return rv;
801                            p2 = eval->genbuf;
802                        }
803                        *p2++ = (*p1 >> 6) + '0';
804                        if(p2 >= eval->lcomend) {
805                            *p2 = '\\';
806                            rv = wline(eval, eval->genbuf,
807                                       strlen(eval->genbuf));
808                            if (rv != APR_SUCCESS)
809                                return rv;
810                            p2 = eval->genbuf;
811                        }
812                        *p2++ = ((*p1 >> 3) & 07) + '0';
813                        if(p2 >= eval->lcomend) {
814                            *p2 = '\\';
815                            rv = wline(eval, eval->genbuf,
816                                       strlen(eval->genbuf));
817                            if (rv != APR_SUCCESS)
818                                return rv;
819                            p2 = eval->genbuf;
820                        }
821                        *p2++ = (*p1++ & 07) + '0';
822                        if(p2 >= eval->lcomend) {
823                            *p2 = '\\';
824                            rv = wline(eval, eval->genbuf,
825                                       strlen(eval->genbuf));
826                            if (rv != APR_SUCCESS)
827                                return rv;
828                            p2 = eval->genbuf;
829                        }
830                    } else {
831                        *p2++ = *p1++;
832                        if(p2 >= eval->lcomend) {
833                            *p2 = '\\';
834                            rv = wline(eval, eval->genbuf,
835                                       strlen(eval->genbuf));
836                            if (rv != APR_SUCCESS)
837                                return rv;
838                            p2 = eval->genbuf;
839                        }
840                    }
841                } else {
842                    p3 = trans[(unsigned char)*p1-1];
843                    while ((*p2++ = *p3++) != 0)
844                        if(p2 >= eval->lcomend) {
845                            *p2 = '\\';
846                            rv = wline(eval, eval->genbuf,
847                                       strlen(eval->genbuf));
848                            if (rv != APR_SUCCESS)
849                                return rv;
850                            p2 = eval->genbuf;
851                        }
852                    p2--;
853                    p1++;
854                }
855            *p2 = 0;
856            rv = wline(eval, eval->genbuf, strlen(eval->genbuf));
857            break;
858
859        case NCOM:
860            if(!eval->commands->nflag) {
861                rv = wline(eval, eval->linebuf, eval->lspend - eval->linebuf);
862                if (rv != APR_SUCCESS)
863                    return rv;
864            }
865
866            if(eval->aptr > eval->abuf) {
867                rv = arout(eval);
868                if (rv != APR_SUCCESS)
869                    return rv;
870            }
871            eval->lspend = eval->linebuf;
872            eval->pending = ipc->next;
873
874            break;
875        case CNCOM:
876            if(eval->aptr > eval->abuf) {
877                rv = arout(eval);
878                if (rv != APR_SUCCESS)
879                    return rv;
880            }
881            append_to_linebuf(eval, "\n");
882            eval->pending = ipc->next;
883            break;
884
885        case PCOM:
886            rv = wline(eval, eval->linebuf, eval->lspend - eval->linebuf);
887            break;
888        case CPCOM:
889            for (p1 = eval->linebuf; *p1 != '\n' && *p1 != '\0'; p1++);
890            rv = wline(eval, eval->linebuf, p1 - eval->linebuf);
891            break;
892
893        case QCOM:
894            if (!eval->commands->nflag) {
895                rv = wline(eval, eval->linebuf, eval->lspend - eval->linebuf);
896                if (rv != APR_SUCCESS)
897                    break;
898            }
899
900            if(eval->aptr > eval->abuf) {
901                rv = arout(eval);
902                if (rv != APR_SUCCESS)
903                    return rv;
904            }
905
906            eval->quitflag = 1;
907            break;
908        case RCOM:
909            if(eval->aptr >= &eval->abuf[SED_ABUFSIZE]) {
910                eval_errf(eval, SEDERR_TMRMES, eval->lnum);
911            } else {
912                *eval->aptr++ = ipc;
913                *eval->aptr = NULL;
914            }
915            break;
916
917        case SCOM:
918            i = substitute(eval, ipc, step_vars);
919            if (i == -1) {
920                return APR_EGENERAL;
921            }
922            if(ipc->pfl && eval->commands->nflag && i) {
923                if(ipc->pfl == 1) {
924                    rv = wline(eval, eval->linebuf, eval->lspend -
925                               eval->linebuf);
926                    if (rv != APR_SUCCESS)
927                        return rv;
928                } else {
929                    for (p1 = eval->linebuf; *p1 != '\n' && *p1 != '\0'; p1++);
930                    rv = wline(eval, eval->linebuf, p1 - eval->linebuf);
931                    if (rv != APR_SUCCESS)
932                        return rv;
933                }
934            }
935            if (i && (ipc->findex >= 0) && eval->fcode[ipc->findex])
936                apr_file_printf(eval->fcode[ipc->findex], "%s\n",
937                                eval->linebuf);
938            break;
939
940        case TCOM:
941            if(eval->sflag == 0)  break;
942            eval->sflag = 0;
943            eval->jflag = 1;
944            break;
945
946        case WCOM:
947            if (ipc->findex >= 0)
948                apr_file_printf(eval->fcode[ipc->findex], "%s\n",
949                                eval->linebuf);
950            break;
951        case XCOM:
952            copy_to_genbuf(eval, eval->linebuf);
953            copy_to_linebuf(eval, eval->holdbuf);
954            copy_to_holdbuf(eval, eval->genbuf);
955            break;
956
957        case YCOM:
958            p1 = eval->linebuf;
959            p2 = ipc->re1;
960            while((*p1 = p2[(unsigned char)*p1]) != 0)    p1++;
961            break;
962    }
963    return rv;
964}
965
966/*
967 * arout
968 */
969static apr_status_t arout(sed_eval_t *eval)
970{
971    apr_status_t rv = APR_SUCCESS;
972    eval->aptr = eval->abuf - 1;
973    while (*++eval->aptr) {
974        if ((*eval->aptr)->command == ACOM) {
975            char *p1;
976
977            for (p1 = (*eval->aptr)->re1; *p1; p1++);
978            rv = wline(eval, (*eval->aptr)->re1, p1 - (*eval->aptr)->re1);
979            if (rv != APR_SUCCESS)
980                return rv;
981        } else {
982            apr_file_t *fi = NULL;
983            char buf[512];
984            apr_size_t n = sizeof(buf);
985
986            if (apr_file_open(&fi, (*eval->aptr)->re1, APR_READ, 0, eval->pool)
987                              != APR_SUCCESS)
988                continue;
989            while ((apr_file_read(fi, buf, &n)) == APR_SUCCESS) {
990                if (n == 0)
991                    break;
992                rv = eval->writefn(eval->fout, buf, n);
993                if (rv != APR_SUCCESS) {
994                    apr_file_close(fi);
995                    return rv;
996                }
997                n = sizeof(buf);
998            }
999            apr_file_close(fi);
1000        }
1001    }
1002    eval->aptr = eval->abuf;
1003    *eval->aptr = NULL;
1004    return rv;
1005}
1006
1007/*
1008 * wline
1009 */
1010static apr_status_t wline(sed_eval_t *eval, char *buf, int sz)
1011{
1012    apr_status_t rv = APR_SUCCESS;
1013    rv = eval->writefn(eval->fout, buf, sz);
1014    if (rv != APR_SUCCESS)
1015        return rv;
1016    rv = eval->writefn(eval->fout, "\n", 1);
1017    return rv;
1018}
1019
1020