]> granicus.if.org Git - apache/commitdiff
Commit mod_sed: enable filtering of HTTP Requests and Responses through sed
authorNick Kew <niq@apache.org>
Tue, 2 Sep 2008 23:01:47 +0000 (23:01 +0000)
committerNick Kew <niq@apache.org>
Tue, 2 Sep 2008 23:01:47 +0000 (23:01 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@691418 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
docs/manual/mod/mod_sed.xml [new file with mode: 0644]
modules/filters/config.m4
modules/filters/libsed.h [new file with mode: 0644]
modules/filters/mod_sed.c [new file with mode: 0644]
modules/filters/regexp.c [new file with mode: 0644]
modules/filters/regexp.h [new file with mode: 0644]
modules/filters/sed.h [new file with mode: 0644]
modules/filters/sed0.c [new file with mode: 0644]
modules/filters/sed1.c [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index 845b2244d7a5861672d12e12dbac911c2d3ddb29..0c862efc2b4f1fd66b1ce8e53394bc9dbdc7f7d6 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,9 @@
 Changes with Apache 2.3.0
 [ When backported to 2.2.x, remove entry from this file ]
 
+  *) New module mod_sed: filter Request/Response bodies through sed
+     [Basant Kumar Kukreja <basant.kukreja sun.com>]
+
   *) mod_auth_form: Make sure that basic authentication is correctly
      faked directly after login. [Graham Leggett]
 
diff --git a/docs/manual/mod/mod_sed.xml b/docs/manual/mod/mod_sed.xml
new file mode 100644 (file)
index 0000000..e7797ba
--- /dev/null
@@ -0,0 +1,141 @@
+<?xml version="1.0"?>
+<!DOCTYPE modulesynopsis SYSTEM "../style/modulesynopsis.dtd">
+<?xml-stylesheet type="text/xsl" href="../style/manual.en.xsl"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+
+<modulesynopsis metafile="mod_sed.xml.meta">
+
+<name>mod_sed</name>
+<description>Filtering Input (request) and Output (response) content using sed commands</description>
+<status>Experimental</status>
+<sourcefile>mod_sed.c sed0.c sed1.c regexp.c regexp.h sed.h</sourcefile>
+<identifier>sed_module</identifier>
+<compatibility>Available in Apache 2.3 and later</compatibility>
+
+<summary>
+<p>
+mod_sed is a in-process content filter. mod_sed filter implement the sed edit
+commands implemented by Solaris 10 sed
+program as described in <a href="http://docs.sun.com/app/docs/doc/816-5165/sed-1b?a=view">man
+page</a>. However unlike sed, mod_sed doesn't take data from
+standard
+input. Instead filter act on the entity data sent between client and
+server. mod_sed can be used as a input or output filter. mod_sed is a
+content filter which means that it can not be used to modify client or
+server http headers.
+</p>
+<p>
+mod_sed output filter accept a chunk of data and execute the sed scripts on data and generates the output which is passed to next filter in the filter chain.
+</p>
+
+<p>
+mod_sed input filter reads the data from next filter in filter chain and executes the sed scripts and returns the generated data to caller filter in the filter chain.
+</p>
+
+<p>
+Both input and output filter only process the data if new line character is seen in the content. At the end of the data, rest of the data is treated as last line.
+</p>
+
+<p>A tutorial article on mod_sed, and why it is more powerful than simple
+string or regular expression search and replace, is available in <a
+href="http://blogs.sun.com/basant/entry/using_mod_sed_to_filter">on
+the author's blog</a>.</p>
+
+</summary>
+
+<directivesynopsis>
+<name>OutputSed</name>
+<description>Sed command for filter the response content</description>
+<syntax>OutputSed <var>sed-command</var></syntax>
+<contextlist><context>directory</context><context>.htaccess</context>
+</contextlist>
+
+<usage>
+    <p>The <directive>OutputSed</directive> directive specify the sed
+    command which will be executed on the response.
+    </p>
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>InputSed</name>
+<description>Sed command to filter the request data (typically post data)</description>
+<syntax>InputSed <var>sed-command</var></syntax>
+<contextlist><context>directory</context><context>.htaccess</context>
+</contextlist>
+
+<usage>
+    <p>The <directive>InputSed</directive> directive specify the sed command
+    which will be executed on the request data e.g POST data.
+    </p>
+</usage>
+</directivesynopsis>
+
+<section id="sampleconf"><title>Sample Configuration</title>
+    <example><title>Adding a output filter </title>
+         # In following example, sed filter will replace the string <br />
+         # "monday" to "MON" and the string "sunday" to SUN in html document <br />
+         # before sending to client. <br />
+        <indent>
+        &lt;Directory "/var/www/docs/sed"&gt; <br />
+           <indent>
+           AddOutputFilter Sed html <br />
+           OutputSed "s/monday/MON/g" <br />
+           OutputSed "s/sunday/SUN/g" <br />
+           </indent>
+        &lt;/Directory&gt; <br />
+        </indent>
+    </example>
+
+    <example><title>Adding a input filter </title>
+         # In following example, sed filter will replace the string <br />
+         # "monday" to "MON" and the string "sunday" to SUN in the POST data <br />
+         # sent to php <br />
+        <indent>
+        &lt;Directory "/var/www/docs/sed"&gt; <br />
+           <indent>
+           AddInputFilter Sed php <br />
+           OutputSed "s/monday/MON/g" <br />
+           OutputSed "s/sunday/SUN/g" <br />
+           </indent>
+        &lt;/Directory&gt; <br />
+        </indent>
+    </example>
+</section>
+<section id="sed_commands"><title>Sed Commands</title>
+    <p>
+    Complete details of the sed command can be found from
+   <a href="http://docs.sun.com/app/docs/doc/816-5165/sed-1b?a=view">sed man
+page</a>.
+    </p>
+    <dl>
+        <dt><code>b</code></dt>
+        <dd>branch to the label specified (Similar to goto)</dd>
+        <dt><code>h</code></dt>
+        <dd>Copy the current line to hold buffer.</dd>
+        <dt><code>H</code></dt>
+        <dd>Append the current line to hold buffer.</dd>
+        <dt><code>g</code></dt>
+        <dd>Copy the hold buffer into the current line</dd>
+        <dt><code>G</code></dt>
+        <dd>Append the hold buffer into the current line</dd>
+        <dt><code>x</code></dt>
+        <dd>Swap the content of hold buffer and current line</dd>
+    </dl>
+</section>
+</modulesynopsis>
index 84a334471f26a19459bb0f2513b2e8eb83e7544d..af334dd1c6141bf18eb8429ef3456fd8f6da4e0d 100644 (file)
@@ -10,6 +10,9 @@ APACHE_MODULE(include, Server Side Includes, , , yes)
 APACHE_MODULE(filter, Smart Filtering, , , yes)
 APACHE_MODULE(substitute, response content rewrite-like filtering, , , most)
 
+sed_obj="mod_sed.lo sed0.lo sed1.lo regexp.lo"
+APACHE_MODULE(sed, filter request and/or response bodies through sed, $sed_obj)
+
 if test "$ac_cv_ebcdic" = "yes"; then
 # mod_charset_lite can be very useful on an ebcdic system,
 #   so include it by default
diff --git a/modules/filters/libsed.h b/modules/filters/libsed.h
new file mode 100644 (file)
index 0000000..c130262
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * 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. 
+ */
+
+#ifndef LIBSED_H
+#define LIBSED_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <limits.h>
+
+#include "apr_file_io.h"
+#ifndef PATH_MAX
+#define PATH_MAX MAX_PATH
+#endif
+
+#define SED_NLINES 256
+#define SED_DEPTH 20
+#define SED_LABSIZE 50
+#define SED_ABUFSIZE 20
+
+typedef struct sed_reptr_s sed_reptr_t;
+
+struct sed_reptr_s {
+    sed_reptr_t *next;
+    char        *ad1;
+    char        *ad2;
+    char        *re1;
+    sed_reptr_t *lb1;
+    char        *rhs;
+    int         findex;
+    char        command;
+    int         gfl;
+    char        pfl;
+    char        negfl;
+    int         nrep;
+};
+
+typedef struct sed_label_s sed_label_t;
+
+struct sed_label_s {
+    char        asc[9];
+    sed_reptr_t *chain;
+    sed_reptr_t *address;
+};
+
+typedef void (sed_err_fn_t)(void *data, const char *error);
+typedef void (sed_write_fn_t)(void *ctx, char *buf, int sz);
+
+typedef struct sed_commands_s sed_commands_t;
+#define NWFILES 11 /* 10 plus one for standard output */
+
+struct sed_commands_s {
+    sed_err_fn_t *errfn;
+    void         *data;
+
+    unsigned     lsize;
+    char         *linebuf;
+    char         *lbend;
+    const char   *saveq;
+
+    char         *cp;
+    char         *lastre;
+    char         *respace;
+    char         sseof;
+    char         *reend;
+    const char   *earg;
+    int          eflag;
+    int          gflag;
+    int          nflag;
+    apr_int64_t  tlno[SED_NLINES];
+    int          nlno;
+    int          depth;
+
+    char         *fname[NWFILES];
+    int          nfiles;
+
+    sed_label_t  ltab[SED_LABSIZE];
+    sed_label_t  *labtab;
+    sed_label_t  *lab;
+    sed_label_t  *labend;
+
+    sed_reptr_t  **cmpend[SED_DEPTH];
+    sed_reptr_t  *ptrspace;
+    sed_reptr_t  *ptrend;
+    sed_reptr_t  *rep;
+    int          nrep;
+    apr_pool_t   *pool;
+    int          canbefinal;
+};
+
+typedef struct sed_eval_s sed_eval_t;
+
+struct sed_eval_s {
+    sed_err_fn_t   *errfn;
+    sed_write_fn_t *writefn;
+    void           *data;
+
+    sed_commands_t *commands;
+
+    apr_int64_t    lnum;
+    void           *fout;
+
+    unsigned       lsize;
+    char           *linebuf;
+    char           *lspend;
+
+    unsigned       hsize;
+    char           *holdbuf;
+    char           *hspend;
+
+    unsigned       gsize;
+    char           *genbuf;
+    char           *lcomend;
+
+    apr_file_t    *fcode[NWFILES];
+    sed_reptr_t    *abuf[SED_ABUFSIZE];
+    sed_reptr_t    **aptr;
+    sed_reptr_t    *pending;
+    unsigned char  *inar;
+    int            nrep;
+
+    int            dolflag;
+    int            sflag;
+    int            jflag;
+    int            delflag;
+    int            lreadyflag;
+    int            quitflag;
+    int            finalflag;
+    int            numpass;
+    int            nullmatch;
+    int            col;
+    apr_pool_t     *pool;
+};
+
+apr_status_t sed_init_commands(sed_commands_t *commands, sed_err_fn_t *errfn, void *data,
+                               apr_pool_t *p);
+apr_status_t sed_compile_string(sed_commands_t *commands, const char *s);
+apr_status_t sed_compile_file(sed_commands_t *commands, apr_file_t *fin);
+char* sed_get_finalize_error(const sed_commands_t *commands, apr_pool_t* pool);
+int sed_canbe_finalized(const sed_commands_t *commands);
+void sed_destroy_commands(sed_commands_t *commands);
+
+apr_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);
+apr_status_t sed_reset_eval(sed_eval_t *eval, sed_commands_t *commands, sed_err_fn_t *errfn, void *data);
+apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, int bufsz, void *fout);
+apr_status_t sed_eval_file(sed_eval_t *eval, apr_file_t *fin, void *fout);
+apr_status_t sed_finalize_eval(sed_eval_t *eval, void *f);
+void sed_destroy_eval(sed_eval_t *eval);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBSED_H */
diff --git a/modules/filters/mod_sed.c b/modules/filters/mod_sed.c
new file mode 100644 (file)
index 0000000..564e9c6
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Use is subject to license terms.
+ *
+ * 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 "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "apr_strings.h"
+#include "apr_general.h"
+#include "util_filter.h"
+#include "apr_buckets.h"
+#include "http_request.h"
+#include "libsed.h"
+
+static const char *sed_filter_name = "Sed";
+#define MODSED_OUTBUF_SIZE 4000
+
+typedef struct sed_expr_config
+{
+    sed_commands_t *sed_cmds;
+    const char *last_error;
+} sed_expr_config;
+
+typedef struct sed_config
+{
+    sed_expr_config output;
+    sed_expr_config input;
+} sed_config;
+
+/* Context for filter invocation for single HTTP request */
+typedef struct sed_filter_ctxt
+{
+    sed_eval_t eval;
+    request_rec *r;
+    apr_bucket_brigade *bb;
+    char *outbuf;
+    char *curoutbuf;
+    int bufsize;
+} sed_filter_ctxt;
+
+module AP_MODULE_DECLARE_DATA sed_module;
+
+/* This function will be call back from libsed functions if there is any error
+ * happend during execution of sed scripts
+ */
+static void log_sed_errf(void *data, const char *error)
+{
+    request_rec *r = (request_rec *) data;
+    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, error);
+}
+
+/* This function will be call back from libsed functions if there is any
+ * compilation error.
+ */
+static void sed_compile_errf(void *data, const char *error)
+{
+    sed_expr_config *sed_cfg = (sed_expr_config *) data;
+    sed_cfg->last_error = error;
+}
+
+/*
+ * flush_output_buffer
+ * Flush the  output data (stored in ctx->outbuf)
+ */
+static void flush_output_buffer(sed_filter_ctxt *ctx, char* buf, int sz)
+{
+    int size = ctx->curoutbuf - ctx->outbuf;
+    char *out;
+    apr_bucket *b;
+    if (size + sz <= 0)
+        return;
+    out = apr_palloc(ctx->r->pool, size + sz);
+    if (size) {
+        memcpy(out, ctx->outbuf, size);
+    }
+    if (buf && (sz > 0)) {
+        memcpy(out + size, buf, sz);
+    }
+    /* Reset the output buffer position */
+    ctx->curoutbuf = ctx->outbuf;
+    b = apr_bucket_pool_create(out, size + sz, ctx->r->pool,
+                               ctx->r->connection->bucket_alloc);
+    APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
+}
+
+/* This is a call back function. When libsed wants to generate the output,
+ * this function will be invoked.
+ */
+static void sed_write_output(void *dummy, char *buf, int sz)
+{
+    /* dummy is basically filter context. Context is passed during invocation
+     * of sed_eval_buffer
+     */
+    sed_filter_ctxt *ctx = (sed_filter_ctxt *) dummy;
+    if (((ctx->curoutbuf - ctx->outbuf) + sz) >= ctx->bufsize) {
+        /* flush current buffer */
+        flush_output_buffer(ctx, buf, sz);
+    }
+    else {
+        memcpy(ctx->curoutbuf, buf, sz);
+        ctx->curoutbuf += sz;
+    }
+}
+
+/* Compile a sed expression. Compiled context is saved in sed_cfg->sed_cmds.
+ * Memory required for compilation context is allocated from cmd->pool.
+ */
+static apr_status_t compile_sed_expr(sed_expr_config *sed_cfg,
+                                     cmd_parms *cmd,
+                                     const char *expr)
+{
+    apr_status_t status = APR_SUCCESS;
+
+    if (!sed_cfg->sed_cmds) {
+        sed_commands_t *sed_cmds;
+        sed_cmds = apr_pcalloc(cmd->pool, sizeof(sed_commands_t));
+        status = sed_init_commands(sed_cmds, sed_compile_errf, sed_cfg,
+                                   cmd->pool);
+        if (status != APR_SUCCESS) {
+            sed_destroy_commands(sed_cmds);
+            return status;
+        }
+        sed_cfg->sed_cmds = sed_cmds;
+    }
+    status = sed_compile_string(sed_cfg->sed_cmds, expr);
+    if (status != APR_SUCCESS) {
+        sed_destroy_commands(sed_cfg->sed_cmds);
+        sed_cfg->sed_cmds = NULL;
+    }
+    return status;
+}
+
+/* sed eval cleanup function */
+static apr_status_t sed_eval_cleanup(void *data)
+{
+    sed_eval_t *eval = (sed_eval_t *) data;
+    sed_destroy_eval(eval);
+    return APR_SUCCESS;
+}
+
+/* Initialize sed filter context. If successful then context is set in f->ctx
+ */
+static apr_status_t init_context(ap_filter_t *f, sed_expr_config *sed_cfg)
+{
+    apr_status_t status;
+    sed_filter_ctxt* ctx;
+    request_rec *r = f->r;
+    /* Create the context. Call sed_init_eval. libsed will generated
+     * output by calling sed_write_output and generates any error by
+     * invoking log_sed_errf.
+     */
+    ctx = apr_pcalloc(r->pool, sizeof(sed_filter_ctxt));
+    ctx->r = r;
+    ctx->bb = NULL;
+    status = sed_init_eval(&ctx->eval, sed_cfg->sed_cmds, log_sed_errf,
+                           r, &sed_write_output, r->pool);
+    if (status != APR_SUCCESS) {
+        return status;
+    }
+    apr_pool_cleanup_register(r->pool, &ctx->eval, sed_eval_cleanup,
+                              apr_pool_cleanup_null);
+    ctx->bufsize = MODSED_OUTBUF_SIZE;
+    ctx->outbuf = apr_palloc(r->pool, ctx->bufsize + 1);
+    ctx->curoutbuf = ctx->outbuf;
+    f->ctx = ctx;
+    return APR_SUCCESS;
+}
+
+/* Entry function for Sed output filter */
+static apr_status_t sed_response_filter(ap_filter_t *f,
+                                        apr_bucket_brigade *bb)
+{
+    apr_bucket *b;
+    apr_status_t status;
+    sed_config *cfg = ap_get_module_config(f->r->per_dir_config,
+                                           &sed_module);
+    sed_filter_ctxt *ctx = f->ctx;
+    sed_expr_config *sed_cfg = &cfg->output;
+
+    if ((sed_cfg == NULL) || (sed_cfg->sed_cmds == NULL)) {
+        /* No sed expressions */
+        ap_remove_output_filter(f);
+        return ap_pass_brigade(f->next, bb);
+    }
+
+    if (ctx == NULL) {
+
+        if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bb))) {
+            /* no need to run sed filter for Head requests */
+            ap_remove_output_filter(f);
+            return ap_pass_brigade(f->next, bb);
+        }
+
+        status = init_context(f, sed_cfg);
+        if (status != APR_SUCCESS)
+             return status;
+        ctx = f->ctx;
+    }
+
+    ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+
+    /* Here is the main logic. Iterate through all the buckets, read the
+     * content of the bucket, call sed_eval_buffer on the data.
+     * sed_eval_buffer will read the data line by line, run filters on each
+     * line. sed_eval_buffer will generates the output by calling
+     * sed_write_output which will add the output to ctx->bb. At the end of
+     * the loop, ctx->bb is passed to the next filter in chain. At the end of
+     * the data, if new line is not found then sed_eval_buffer will store the
+     * data in it's own buffer.
+     *
+     * Once eos bucket is found then sed_finalize_eval will flush the rest of
+     * the data. If there is no new line in last line of data, new line is
+     * appended (that is a solaris sed behavior). libsed's internal memory for
+     * evaluation is allocated on request's pool so it will be cleared once
+     * request is over.
+     *
+     * If flush bucket is found then append the the flush bucket to ctx->bb
+     * and pass it to next filter. There may be some data which will still be
+     * in sed's internal buffer which can't be flushed until new line
+     * character is arrived.
+     */
+    for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb);) {
+        const char *buf = NULL;
+        apr_size_t bytes = 0;
+        if (APR_BUCKET_IS_EOS(b)) {
+            apr_bucket *b1 = APR_BUCKET_NEXT(b);
+            /* Now clean up the internal sed buffer */
+            sed_finalize_eval(&ctx->eval, ctx);
+            flush_output_buffer(ctx, NULL, 0);
+            APR_BUCKET_REMOVE(b);
+            /* Insert the eos bucket to ctx->bb brigade */
+            APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
+            b = b1;
+        }
+        else if (APR_BUCKET_IS_FLUSH(b)) {
+            apr_bucket *b1 = APR_BUCKET_NEXT(b);
+            APR_BUCKET_REMOVE(b);
+            APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
+            status = ap_pass_brigade(f->next, ctx->bb);
+            apr_brigade_cleanup(ctx->bb);
+            if (status != APR_SUCCESS) {
+                return status;
+            }
+            b = b1;
+        }
+        else if (APR_BUCKET_IS_METADATA(b)) {
+            b = APR_BUCKET_NEXT(b);
+        }
+        else if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ)
+                 == APR_SUCCESS) {
+            apr_bucket *b1 = APR_BUCKET_NEXT(b);
+            status = sed_eval_buffer(&ctx->eval, buf, bytes, ctx);
+            if (status != APR_SUCCESS) {
+                return status;
+            }
+            flush_output_buffer(ctx, NULL, 0);
+            APR_BUCKET_REMOVE(b);
+            apr_bucket_delete(b);
+            b = b1;
+        }
+        else {
+            apr_bucket *b1 = APR_BUCKET_NEXT(b);
+            APR_BUCKET_REMOVE(b);
+            b = b1;
+        }
+    }
+    apr_brigade_cleanup(bb);
+    return ap_pass_brigade(f->next, ctx->bb);
+}
+
+/* Entry function for Sed input filter */
+static apr_status_t sed_request_filter(ap_filter_t *f,
+                                       apr_bucket_brigade *bb,
+                                       ap_input_mode_t mode,
+                                       apr_read_type_e block,
+                                       apr_off_t readbytes)
+{
+    sed_config *cfg = ap_get_module_config(f->r->per_dir_config,
+                                           &sed_module);
+    sed_filter_ctxt *ctx = f->ctx;
+    apr_status_t status;
+    sed_expr_config *sed_cfg = &cfg->input;
+
+    if (mode != AP_MODE_READBYTES) {
+        return ap_get_brigade(f->next, bb, mode, block, readbytes);
+    }
+
+    if ((sed_cfg == NULL) || (sed_cfg->sed_cmds == NULL)) {
+        /* No sed expression */
+        return ap_get_brigade(f->next, bb, mode, block, readbytes);
+    }
+
+    if (!ctx) {
+        if (!ap_is_initial_req(f->r)) {
+            ap_remove_input_filter(f);
+            /* XXX : Should we filter the sub requests too */
+            return ap_get_brigade(f->next, bb, mode, block, readbytes);
+        }
+        status = init_context(f, sed_cfg);
+        if (status != APR_SUCCESS)
+             return status;
+        ctx = f->ctx;
+        ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+    }
+
+    /* Here is the logic :
+     * Read the readbytes data from next level fiter into bbinp. Loop through
+     * the buckets in bbinp and read the data from buckets and invoke
+     * sed_eval_buffer on the data. libsed will generate it's output using
+     * sed_write_output which will add data in ctx->bb. Do it until it have
+     * atleast one bucket bucket in ctx->bb. At the end of data eos bucket
+     * should be there.
+     *
+     * Once eos bucket is seen, then invoke sed_finalize_eval to clear the
+     * output. If the last byte of data is not a new line character then sed
+     * will add a new line to the data that is default sed behaviour. Note
+     * that using this filter with POST data, caller may not expect this
+     * behaviour.
+     *
+     * If next level fiter generate the flush bucket, we can't do much about
+     * it. If we want to return the flush bucket in brigade bb (to the caller)
+     * the question is where to add it?
+     */
+    while (APR_BRIGADE_EMPTY(ctx->bb)) {
+        apr_bucket_brigade *bbinp;
+        apr_bucket *b;
+
+        /* read the bytes from next level filter */
+        bbinp = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+        status = ap_get_brigade(f->next, bbinp, mode, block, readbytes);
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+        for (b = APR_BRIGADE_FIRST(bbinp); b != APR_BRIGADE_SENTINEL(bbinp);
+             b = APR_BUCKET_NEXT(b)) {
+            const char *buf = NULL;
+            apr_size_t bytes;
+
+            if (APR_BUCKET_IS_EOS(b)) {
+                /* eos bucket. Clear the internal sed buffers */
+                sed_finalize_eval(&ctx->eval, ctx);
+                flush_output_buffer(ctx, NULL, 0);
+                APR_BUCKET_REMOVE(b);
+                APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
+                break;
+            }
+            else if (APR_BUCKET_IS_FLUSH(b)) {
+                /* What should we do with flush bucket */
+                continue;
+            }
+            if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ)
+                     == APR_SUCCESS) {
+                status = sed_eval_buffer(&ctx->eval, buf, bytes, ctx);
+                if (status != APR_SUCCESS)
+                    return status;
+                flush_output_buffer(ctx, NULL, 0);
+            }
+        }
+        apr_brigade_cleanup(bbinp);
+        apr_brigade_destroy(bbinp);
+    }
+
+    if (!APR_BRIGADE_EMPTY(ctx->bb)) {
+        apr_bucket_brigade *newbb = NULL;
+        apr_bucket *b = NULL;
+
+        /* This may return APR_INCOMPLETE which should be fine */
+        apr_brigade_partition(ctx->bb, readbytes, &b);
+
+        newbb = apr_brigade_split(ctx->bb, b);
+        APR_BRIGADE_CONCAT(bb, ctx->bb);
+        APR_BRIGADE_CONCAT(ctx->bb, newbb);
+    }
+    return APR_SUCCESS;
+}
+
+static const char *sed_add_expr(cmd_parms *cmd, void *cfg, const char *arg)
+{
+    int offset = (int) (long) cmd->info;
+    sed_expr_config *sed_cfg = 
+                (sed_expr_config *) (((char *) cfg) + offset);
+    if (compile_sed_expr(sed_cfg, cmd, arg) != APR_SUCCESS) {
+        return apr_psprintf(cmd->temp_pool,
+                            "Failed to compile sed expression. %s",
+                            sed_cfg->last_error);
+    }
+    return NULL;
+}
+
+static void *create_sed_dir_config(apr_pool_t *p, char *s)
+{
+    sed_config *cfg = apr_pcalloc(p, sizeof(sed_config));
+    return cfg;
+}
+
+static const command_rec sed_filter_cmds[] = {
+    AP_INIT_TAKE1("OutputSed", sed_add_expr,
+                  (void *) APR_OFFSETOF(sed_config, output),
+                  ACCESS_CONF,
+                  "Sed regular expression for Response"),
+    AP_INIT_TAKE1("InputSed", sed_add_expr,
+                  (void *) APR_OFFSETOF(sed_config, input),
+                  ACCESS_CONF,
+                  "Sed regular expression for Request"),
+    {NULL}
+};
+
+static void register_hooks(apr_pool_t *p)
+{
+    ap_register_output_filter(sed_filter_name, sed_response_filter, NULL,
+                              AP_FTYPE_RESOURCE);
+    ap_register_input_filter(sed_filter_name, sed_request_filter, NULL,
+                             AP_FTYPE_RESOURCE);
+}
+
+module AP_MODULE_DECLARE_DATA sed_module = {
+    STANDARD20_MODULE_STUFF,
+    create_sed_dir_config,      /* dir config creater */
+    NULL,                       /* dir merger --- default is to override */
+    NULL,                       /* server config */
+    NULL,                       /* merge server config */
+    sed_filter_cmds,            /* command table */
+    register_hooks              /* register hooks */
+};
diff --git a/modules/filters/regexp.c b/modules/filters/regexp.c
new file mode 100644 (file)
index 0000000..51a7ec9
--- /dev/null
@@ -0,0 +1,601 @@
+/*
+ * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Use is subject to license terms.
+ *
+ *     Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T 
+ *       All Rights Reserved   
+ *
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ *
+ * 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. 
+ */
+
+/* Code moved from regexp.h */
+
+#include "apr.h"
+#include "apr_lib.h"
+#ifdef APR_HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include "libsed.h"
+#include "regexp.h"
+#include "sed.h"
+
+#define GETC() ((unsigned char)*sp++)
+#define PEEKC() ((unsigned char)*sp)
+#define UNGETC(c) (--sp)
+#define SEDCOMPILE_ERROR(c) { \
+            regerrno = c; \
+            goto out; \
+            }
+#define ecmp(s1, s2, n)    (strncmp(s1, s2, n) == 0)
+#define uletter(c) (isalpha(c) || c == '_')
+
+
+static unsigned char bittab[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
+
+static int regerr(sed_commands_t *commands, int err);
+static void comperr(sed_commands_t *commands, char *msg);
+static void getrnge(char *str, step_vars_storage *vars);
+static int _advance(char *, char *, step_vars_storage *);
+extern int sed_step(char *p1, char *p2, int circf, step_vars_storage *vars);
+
+
+static void comperr(sed_commands_t *commands, char *msg)
+{
+    command_errf(commands, msg, commands->linebuf);
+}
+
+/*
+*/
+static int regerr(sed_commands_t *commands, int err)
+{
+    switch(err) {
+    case 0:
+        /* No error */
+        break;
+    case 11:
+        comperr(commands, "Range endpoint too large: %s");
+        break;
+
+    case 16:
+        comperr(commands, "Bad number: %s");
+        break;
+
+    case 25:
+        comperr(commands, "``\\digit'' out of range: %s");
+        break;
+
+    case 36:
+        comperr(commands, "Illegal or missing delimiter: %s");
+        break;
+
+    case 41:
+        comperr(commands, "No remembered search string: %s");
+        break;
+
+    case 42:
+        comperr(commands, "\\( \\) imbalance: %s");
+        break;
+
+    case 43:
+        comperr(commands, "Too many \\(: %s");
+        break;
+
+    case 44:
+        comperr(commands, "More than 2 numbers given in \\{ \\}: %s");
+        break;
+
+    case 45:
+        comperr(commands, "} expected after \\: %s");
+        break;
+
+    case 46:
+        comperr(commands, "First number exceeds second in \\{ \\}: %s");
+        break;
+
+    case 49:
+        comperr(commands, "[ ] imbalance: %s");
+        break;
+
+    case 50:
+        comperr(commands, SEDERR_TMMES);
+        break;
+
+    default:
+        comperr(commands, "Unknown regexp error code %s\n");
+        break;
+    }
+    return (0);
+}
+
+
+char *sed_compile(sed_commands_t *commands, sed_comp_args *compargs,
+                  char *ep, char *endbuf, int seof)
+{
+    int c;
+    int eof = seof;
+    char *lastep;
+    int cclcnt;
+    char bracket[NBRA], *bracketp;
+    int closed;
+    int neg;
+    int lc;
+    int i, cflg;
+    int iflag; /* used for non-ascii characters in brackets */
+    int nodelim = 0;
+    char *sp = commands->cp;
+    int regerrno = 0;
+
+    lastep = 0;
+    if ((c = GETC()) == eof || c == '\n') {
+        if (c == '\n') {
+            UNGETC(c);
+            nodelim = 1;
+        }
+        commands->cp = sp;
+        goto out;
+    }
+    bracketp = bracket;
+    compargs->circf = closed = compargs->nbra = 0;
+    if (c == '^')
+        compargs->circf++;
+    else
+        UNGETC(c);
+    while (1) {
+        if (ep >= endbuf)
+            SEDCOMPILE_ERROR(50);
+        c = GETC();
+        if (c != '*' && ((c != '\\') || (PEEKC() != '{')))
+            lastep = ep;
+        if (c == eof) {
+            *ep++ = CCEOF;
+            if (bracketp != bracket)
+                SEDCOMPILE_ERROR(42);
+            commands->cp = sp;
+            goto out;
+        }
+        switch (c) {
+
+        case '.':
+            *ep++ = CDOT;
+            continue;
+
+        case '\n':
+            SEDCOMPILE_ERROR(36);
+            commands->cp = sp;
+            goto out;
+        case '*':
+            if (lastep == 0 || *lastep == CBRA || *lastep == CKET)
+                goto defchar;
+            *lastep |= STAR;
+            continue;
+
+        case '$':
+            if (PEEKC() != eof && PEEKC() != '\n')
+                goto defchar;
+            *ep++ = CDOL;
+            continue;
+
+        case '[':
+            if (&ep[17] >= endbuf)
+                SEDCOMPILE_ERROR(50);
+
+            *ep++ = CCL;
+            lc = 0;
+            for (i = 0; i < 16; i++)
+                ep[i] = 0;
+
+            neg = 0;
+            if ((c = GETC()) == '^') {
+                neg = 1;
+                c = GETC();
+            }
+            iflag = 1;
+            do {
+                c &= 0377;
+                if (c == '\0' || c == '\n')
+                    SEDCOMPILE_ERROR(49);
+                if ((c & 0200) && iflag) {
+                    iflag = 0;
+                    if (&ep[32] >= endbuf)
+                        SEDCOMPILE_ERROR(50);
+                    ep[-1] = CXCL;
+                    for (i = 16; i < 32; i++)
+                        ep[i] = 0;
+                }
+                if (c == '-' && lc != 0) {
+                    if ((c = GETC()) == ']') {
+                        PLACE('-');
+                        break;
+                    }
+                    if ((c & 0200) && iflag) {
+                        iflag = 0;
+                        if (&ep[32] >= endbuf)
+                            SEDCOMPILE_ERROR(50);
+                        ep[-1] = CXCL;
+                        for (i = 16; i < 32; i++)
+                            ep[i] = 0;
+                    }
+                    while (lc < c) {
+                        PLACE(lc);
+                        lc++;
+                    }
+                }
+                lc = c;
+                PLACE(c);
+            } while ((c = GETC()) != ']');
+
+            if (iflag)
+                iflag = 16;
+            else
+                iflag = 32;
+
+            if (neg) {
+                if (iflag == 32) {
+                    for (cclcnt = 0; cclcnt < iflag;
+                        cclcnt++)
+                        ep[cclcnt] ^= 0377;
+                    ep[0] &= 0376;
+                } else {
+                    ep[-1] = NCCL;
+                    /* make nulls match so test fails */
+                    ep[0] |= 01;
+                }
+            }
+
+            ep += iflag;
+
+            continue;
+
+        case '\\':
+            switch (c = GETC()) {
+
+            case '(':
+                if (compargs->nbra >= NBRA)
+                    SEDCOMPILE_ERROR(43);
+                *bracketp++ = compargs->nbra;
+                *ep++ = CBRA;
+                *ep++ = compargs->nbra++;
+                continue;
+
+            case ')':
+                if (bracketp <= bracket)
+                    SEDCOMPILE_ERROR(42);
+                *ep++ = CKET;
+                *ep++ = *--bracketp;
+                closed++;
+                continue;
+
+            case '{':
+                if (lastep == (char *) 0)
+                    goto defchar;
+                *lastep |= RNGE;
+                cflg = 0;
+            nlim:
+                c = GETC();
+                i = 0;
+                do {
+                    if ('0' <= c && c <= '9')
+                        i = 10 * i + c - '0';
+                    else
+                        SEDCOMPILE_ERROR(16);
+                } while (((c = GETC()) != '\\') && (c != ','));
+                if (i >= 255)
+                    SEDCOMPILE_ERROR(11);
+                *ep++ = i;
+                if (c == ',') {
+                    if (cflg++)
+                        SEDCOMPILE_ERROR(44);
+                    if ((c = GETC()) == '\\')
+                        *ep++ = (char) 255;
+                    else {
+                        UNGETC(c);
+                        goto nlim;
+                        /* get 2'nd number */
+                    }
+                }
+                if (GETC() != '}')
+                    SEDCOMPILE_ERROR(45);
+                if (!cflg)    /* one number */
+                    *ep++ = i;
+                else if ((ep[-1] & 0377) < (ep[-2] & 0377))
+                    SEDCOMPILE_ERROR(46);
+                continue;
+
+            case '\n':
+                SEDCOMPILE_ERROR(36);
+
+            case 'n':
+                c = '\n';
+                goto defchar;
+
+            default:
+                if (c >= '1' && c <= '9') {
+                    if ((c -= '1') >= closed)
+                        SEDCOMPILE_ERROR(25);
+                    *ep++ = CBACK;
+                    *ep++ = c;
+                    continue;
+                }
+            }
+    /* Drop through to default to use \ to turn off special chars */
+
+        defchar:
+        default:
+            lastep = ep;
+            *ep++ = CCHR;
+            *ep++ = c;
+        }
+    }
+out:
+    if (regerrno) {
+        regerr(commands, regerrno);
+        return (char*) NULL;
+    }
+    /* XXX : Basant : what extra */
+    /* int reglength = (int)(ep - expbuf); */
+    return ep;
+}
+
+int sed_step(char *p1, char *p2, int circf, step_vars_storage *vars)
+{
+    int c;
+
+
+    if (circf) {
+        vars->loc1 = p1;
+        return (_advance(p1, p2, vars));
+    }
+    /* fast check for first character */
+    if (*p2 == CCHR) {
+        c = p2[1];
+        do {
+            if (*p1 != c)
+                continue;
+            if (_advance(p1, p2, vars)) {
+                vars->loc1 = p1;
+                return (1);
+            }
+        } while (*p1++);
+        return (0);
+    }
+        /* regular algorithm */
+    do {
+        if (_advance(p1, p2, vars)) {
+            vars->loc1 = p1;
+            return (1);
+        }
+    } while (*p1++);
+    return (0);
+}
+
+static int _advance(char *lp, char *ep, step_vars_storage *vars)
+{
+    char *curlp;
+    int c;
+    char *bbeg;
+    char neg;
+    int ct;
+    int epint; /* int value of *ep */
+
+    while (1) {
+        neg = 0;
+        switch (*ep++) {
+
+        case CCHR:
+            if (*ep++ == *lp++)
+                continue;
+            return (0);
+
+        case CDOT:
+            if (*lp++)
+                continue;
+            return (0);
+
+        case CDOL:
+            if (*lp == 0)
+                continue;
+            return (0);
+
+        case CCEOF:
+            vars->loc2 = lp;
+            return (1);
+
+        case CXCL:
+            c = (unsigned char)*lp++;
+            if (ISTHERE(c)) {
+                ep += 32;
+                continue;
+            }
+            return (0);
+
+        case NCCL:
+            neg = 1;
+
+        case CCL:
+            c = *lp++;
+            if (((c & 0200) == 0 && ISTHERE(c)) ^ neg) {
+                ep += 16;
+                continue;
+            }
+            return (0);
+
+        case CBRA:
+            epint = (int) *ep;
+            vars->braslist[epint] = lp;
+            ep++;
+            continue;
+
+        case CKET:
+            epint = (int) *ep;
+            vars->braelist[epint] = lp;
+            ep++;
+            continue;
+
+        case CCHR | RNGE:
+            c = *ep++;
+            getrnge(ep, vars);
+            while (vars->low--)
+                if (*lp++ != c)
+                    return (0);
+            curlp = lp;
+            while (vars->size--)
+                if (*lp++ != c)
+                    break;
+            if (vars->size < 0)
+                lp++;
+            ep += 2;
+            goto star;
+
+        case CDOT | RNGE:
+            getrnge(ep, vars);
+            while (vars->low--)
+                if (*lp++ == '\0')
+                    return (0);
+            curlp = lp;
+            while (vars->size--)
+                if (*lp++ == '\0')
+                    break;
+            if (vars->size < 0)
+                lp++;
+            ep += 2;
+            goto star;
+
+        case CXCL | RNGE:
+            getrnge(ep + 32, vars);
+            while (vars->low--) {
+                c = (unsigned char)*lp++;
+                if (!ISTHERE(c))
+                    return (0);
+            }
+            curlp = lp;
+            while (vars->size--) {
+                c = (unsigned char)*lp++;
+                if (!ISTHERE(c))
+                    break;
+            }
+            if (vars->size < 0)
+                lp++;
+            ep += 34;        /* 32 + 2 */
+            goto star;
+
+        case NCCL | RNGE:
+            neg = 1;
+
+        case CCL | RNGE:
+            getrnge(ep + 16, vars);
+            while (vars->low--) {
+                c = *lp++;
+                if (((c & 0200) || !ISTHERE(c)) ^ neg)
+                    return (0);
+            }
+            curlp = lp;
+            while (vars->size--) {
+                c = *lp++;
+                if (((c & 0200) || !ISTHERE(c)) ^ neg)
+                    break;
+            }
+            if (vars->size < 0)
+                lp++;
+            ep += 18;         /* 16 + 2 */
+            goto star;
+
+        case CBACK:
+            epint = (int) *ep;
+            bbeg = vars->braslist[epint];
+            ct = vars->braelist[epint] - bbeg;
+            ep++;
+
+            if (ecmp(bbeg, lp, ct)) {
+                lp += ct;
+                continue;
+            }
+            return (0);
+
+        case CBACK | STAR:
+            epint = (int) *ep;
+            bbeg = vars->braslist[epint];
+            ct = vars->braelist[epint] - bbeg;
+            ep++;
+            curlp = lp;
+            while (ecmp(bbeg, lp, ct))
+                lp += ct;
+
+            while (lp >= curlp) {
+                if (_advance(lp, ep, vars))
+                    return (1);
+                lp -= ct;
+            }
+            return (0);
+
+
+        case CDOT | STAR:
+            curlp = lp;
+            while (*lp++);
+            goto star;
+
+        case CCHR | STAR:
+            curlp = lp;
+            while (*lp++ == *ep);
+            ep++;
+            goto star;
+
+        case CXCL | STAR:
+            curlp = lp;
+            do {
+                c = (unsigned char)*lp++;
+            } while (ISTHERE(c));
+            ep += 32;
+            goto star;
+
+        case NCCL | STAR:
+            neg = 1;
+
+        case CCL | STAR:
+            curlp = lp;
+            do {
+                c = *lp++;
+            } while (((c & 0200) == 0 && ISTHERE(c)) ^ neg);
+            ep += 16;
+            goto star;
+
+        star:
+            do {
+                if (--lp == vars->locs)
+                    break;
+                if (_advance(lp, ep, vars))
+                    return (1);
+            } while (lp > curlp);
+            return (0);
+
+        }
+    }
+}
+
+static void getrnge(char *str, step_vars_storage *vars)
+{
+    vars->low = *str++ & 0377;
+    vars->size = ((*str & 0377) == 255)? 20000: (*str &0377) - vars->low;
+}
+
+
diff --git a/modules/filters/regexp.h b/modules/filters/regexp.h
new file mode 100644 (file)
index 0000000..47b2119
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2005, 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Use is subject to license terms.
+ *
+ *     Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T 
+ *       All Rights Reserved   
+ *
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ *
+ * 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. 
+ */
+
+#ifndef _REGEXP_H
+#define _REGEXP_H
+
+#include "libsed.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define    CBRA    2
+#define    CCHR    4
+#define    CDOT    8
+#define    CCL    12
+#define    CXCL    16
+#define    CDOL    20
+#define    CCEOF    22
+#define    CKET    24
+#define    CBACK    36
+#define    NCCL    40
+
+#define    STAR    01
+#define    RNGE    03
+
+#define    NBRA    9
+
+#define    PLACE(c)    ep[c >> 3] |= bittab[c & 07]
+#define    ISTHERE(c)    (ep[c >> 3] & bittab[c & 07])
+
+typedef struct _step_vars_storage {
+    char    *loc1, *loc2, *locs;
+    char    *braslist[NBRA];
+    char    *braelist[NBRA];
+    int    low;
+    int    size;
+} step_vars_storage;
+
+typedef struct _sed_comp_args {
+    int circf; /* Regular expression starts with ^ */
+    int nbra; /* braces count */
+} sed_comp_args;
+
+extern char *sed_compile(sed_commands_t *commands, sed_comp_args *compargs,
+                         char *ep, char *endbuf, int seof);
+extern void command_errf(sed_commands_t *commands, const char *fmt, ...);
+
+#define SEDERR_CGMES "command garbled: %s"
+#define SEDERR_SMMES "Space missing before filename: %s"
+#define SEDERR_TMMES "too much command text: %s"
+#define SEDERR_LTLMES "label too long: %s"
+#define SEDERR_ULMES "undefined label: %s"
+#define SEDERR_DLMES "duplicate labels: %s"
+#define SEDERR_TMLMES "too many labels: %s"
+#define SEDERR_AD0MES "no addresses allowed: %s"
+#define SEDERR_AD1MES "only one address allowed: %s"
+#define SEDERR_TOOBIG "suffix too large: %s"
+#define SEDERR_OOMMES "out of memory"
+#define SEDERR_COPFMES "cannot open pattern file: %s"
+#define SEDERR_COIFMES "cannot open input file: %s"
+#define SEDERR_TMOMES "too many {'s"
+#define SEDERR_TMCMES "too many }'s"
+#define SEDERR_NRMES "first RE may not be null"
+#define SEDERR_UCMES "unrecognized command: %s"
+#define SEDERR_TMWFMES "too many files in w commands"
+#define SEDERR_COMES "cannot open %s"
+#define SEDERR_CCMES "cannot create %s"
+#define SEDERR_TMLNMES "too many line numbers"
+#define SEDERR_TMAMES "too many appends after line %lld"
+#define SEDERR_TMRMES "too many reads after line %lld"
+#define SEDERR_DOORNG "``\\digit'' out of range: %s"
+#define SEDERR_EDMOSUB "ending delimiter missing on substitution: %s"
+#define SEDERR_EDMOSTR "ending delimiter missing on string: %s"
+#define SEDERR_FNTL "file name too long: %s"
+#define SEDERR_CLTL "command line too long"
+#define SEDERR_TSNTSS "transform strings not the same size: %s"
+#define SEDERR_OLTL "output line too long."
+#define SEDERR_HSOVERFLOW "hold space overflowed."
+#define SEDERR_INTERNAL "internal sed error"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _REGEXP_H */
diff --git a/modules/filters/sed.h b/modules/filters/sed.h
new file mode 100644 (file)
index 0000000..73f1420
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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. 
+ */
+
+#ifndef _SED_H
+#define _SED_H
+
+#include <stdlib.h>
+#include <limits.h>
+
+#define CEND    16
+#define CLNUM   14
+
+#define RESIZE  10000
+#define LBSIZE  1000
+
+#define ACOM    01
+#define BCOM    020
+#define CCOM    02
+#define CDCOM   025
+#define CNCOM   022
+#define COCOM   017
+#define CPCOM   023
+#define DCOM    03
+#define ECOM    015
+#define EQCOM   013
+#define FCOM    016
+#define GCOM    027
+#define CGCOM   030
+#define HCOM    031
+#define CHCOM   032
+#define ICOM    04
+#define LCOM    05
+#define NCOM    012
+#define PCOM    010
+#define QCOM    011
+#define RCOM    06
+#define SCOM    07
+#define TCOM    021
+#define WCOM    014
+#define CWCOM   024
+#define YCOM    026
+#define XCOM    033
+
+#endif /* _SED_H */
diff --git a/modules/filters/sed0.c b/modules/filters/sed0.c
new file mode 100644 (file)
index 0000000..1485987
--- /dev/null
@@ -0,0 +1,1026 @@
+/*
+ * 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);
+            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]) == 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]) == 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, commands->linebuf);
+                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, commands->linebuf);
+            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, commands->linebuf);
+                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, commands->linebuf);
+            *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;
+}
+
+
diff --git a/modules/filters/sed1.c b/modules/filters/sed1.c
new file mode 100644 (file)
index 0000000..e888010
--- /dev/null
@@ -0,0 +1,957 @@
+/*
+ * 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_lib.h"
+#include "libsed.h"
+#include "sed.h"
+#include "apr_strings.h"
+#include "regexp.h"
+
+char    *trans[040]  = {
+    "\\01",
+    "\\02",
+    "\\03",
+    "\\04",
+    "\\05",
+    "\\06",
+    "\\07",
+    "-\b<",
+    "-\b>",
+    "\n",
+    "\\13",
+    "\\14",
+    "\\15",
+    "\\16",
+    "\\17",
+    "\\20",
+    "\\21",
+    "\\22",
+    "\\23",
+    "\\24",
+    "\\25",
+    "\\26",
+    "\\27",
+    "\\30",
+    "\\31",
+    "\\32",
+    "\\33",
+    "\\34",
+    "\\35",
+    "\\36",
+    "\\37"
+};
+char rub[] = {"\\177"};
+
+extern int sed_step(char *p1, char *p2, int circf, step_vars_storage *vars);
+static int substitute(sed_eval_t *eval, sed_reptr_t *ipc,
+                      step_vars_storage *step_vars);
+static apr_status_t execute(sed_eval_t *eval);
+static int match(sed_eval_t *eval, char *expbuf, int gf,
+                 step_vars_storage *step_vars);
+static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n,
+                          step_vars_storage *step_vars);
+static char *place(sed_eval_t *eval, char *asp, char *al1, char *al2);
+static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
+                            step_vars_storage *step_vars);
+static void wline(sed_eval_t *eval, char *buf, int sz);
+static void arout(sed_eval_t *eval);
+
+static void eval_errf(sed_eval_t *eval, const char *fmt, ...)
+{
+    if (eval->errfn && eval->pool) {
+        va_list args;
+        const char* error;
+        va_start(args, fmt);
+        error = apr_pvsprintf(eval->pool, fmt, args);
+        eval->errfn(eval->data, error);
+        va_end(args);
+    }
+}
+
+#define INIT_BUF_SIZE 1024
+
+/*
+ * grow_buffer
+ */
+static void grow_buffer(apr_pool_t *pool, char **buffer,
+                        char **spend, unsigned int *cursize,
+                        unsigned int newsize)
+{
+    char* newbuffer = NULL;
+    int spendsize = 0;
+    if (*cursize >= newsize)
+        return;
+    /* Align it to 4 KB boundary */
+    newsize = (newsize  + ((1 << 12) - 1)) & ~((1 << 12) -1);
+    newbuffer = apr_pcalloc(pool, newsize);
+    if (*spend && *buffer && (*cursize > 0)) {
+        spendsize = *spend - *buffer;
+    }
+    if ((*cursize > 0) && *buffer) {
+        memcpy(newbuffer, *buffer, *cursize);
+    }
+    *buffer = newbuffer;
+    *cursize = newsize;
+    if (spend != buffer) {
+        *spend = *buffer + spendsize;
+    }
+}
+
+/*
+ * grow_line_buffer
+ */
+static void grow_line_buffer(sed_eval_t *eval, int newsize)
+{
+    grow_buffer(eval->pool, &eval->linebuf, &eval->lspend,
+                &eval->lsize, newsize);
+}
+
+/*
+ * grow_hold_buffer
+ */
+static void grow_hold_buffer(sed_eval_t *eval, int newsize)
+{
+    grow_buffer(eval->pool, &eval->holdbuf, &eval->hspend,
+                &eval->hsize, newsize);
+}
+
+/*
+ * grow_gen_buffer
+ */
+static void grow_gen_buffer(sed_eval_t *eval, int newsize,
+                            char **gspend)
+{
+    if (gspend == NULL) {
+        gspend = &eval->genbuf;
+    }
+    grow_buffer(eval->pool, &eval->genbuf, gspend,
+                &eval->gsize, newsize);
+    eval->lcomend = &eval->genbuf[71];
+}
+
+/*
+ * appendmem_to_linebuf
+ */
+static void appendmem_to_linebuf(sed_eval_t *eval, const char* sz, int len)
+{
+    unsigned int reqsize = (eval->lspend - eval->linebuf) + len;
+    if (eval->lsize < reqsize) {
+        grow_line_buffer(eval, reqsize);
+    }
+    memcpy(eval->lspend, sz, len);
+    eval->lspend += len;
+}
+
+/*
+ * append_to_linebuf
+ */
+static void append_to_linebuf(sed_eval_t *eval, const char* sz)
+{
+    int len = strlen(sz);
+    /* Copy string including null character */
+    appendmem_to_linebuf(eval, sz, len + 1);
+    --eval->lspend; /* lspend will now point to NULL character */
+}
+
+/*
+ * copy_to_linebuf
+ */
+static void copy_to_linebuf(sed_eval_t *eval, const char* sz)
+{
+    eval->lspend = eval->linebuf;
+    append_to_linebuf(eval, sz);
+}
+
+/*
+ * append_to_holdbuf
+ */
+static void append_to_holdbuf(sed_eval_t *eval, const char* sz)
+{
+    int len = strlen(sz);
+    unsigned int reqsize = (eval->hspend - eval->holdbuf) + len + 1;
+    if (eval->hsize <= reqsize) {
+        grow_hold_buffer(eval, reqsize);
+    }
+    strcpy(eval->hspend, sz);
+    /* hspend will now point to NULL character */
+    eval->hspend += len;
+}
+
+/*
+ * copy_to_holdbuf
+ */
+static void copy_to_holdbuf(sed_eval_t *eval, const char* sz)
+{
+    eval->hspend = eval->holdbuf;
+    append_to_holdbuf(eval, sz);
+}
+
+/*
+ * append_to_genbuf
+ */
+static void append_to_genbuf(sed_eval_t *eval, const char* sz, char **gspend)
+{
+    int len = strlen(sz);
+    unsigned int reqsize = (*gspend - eval->genbuf) + len + 1;
+    if (eval->gsize < reqsize) {
+        grow_gen_buffer(eval, reqsize, gspend);
+    }
+    strcpy(*gspend, sz);
+    /* *gspend will now point to NULL character */
+    *gspend += len;
+}
+
+/*
+ * copy_to_genbuf
+ */
+static void copy_to_genbuf(sed_eval_t *eval, const char* sz)
+{
+    int len = strlen(sz);
+    unsigned int reqsize = len + 1;
+    if (eval->gsize < reqsize) {
+        grow_gen_buffer(eval, reqsize, NULL);
+    }
+}
+
+/*
+ * sed_init_eval
+ */
+apr_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)
+{
+    memset(eval, 0, sizeof(*eval));
+    eval->pool = p;
+    eval->writefn = writefn;
+    return sed_reset_eval(eval, commands, errfn, data);
+}
+
+/*
+ * sed_reset_eval
+ */
+apr_status_t sed_reset_eval(sed_eval_t *eval, sed_commands_t *commands, sed_err_fn_t *errfn, void *data)
+{
+    int i;
+
+    eval->errfn = errfn;
+    eval->data = data;
+
+    eval->commands = commands;
+
+    eval->lnum = 0;
+    eval->fout = NULL;
+
+    if (eval->linebuf == NULL) {
+        eval->lsize = INIT_BUF_SIZE;
+        eval->linebuf = apr_pcalloc(eval->pool, eval->lsize);
+    }
+    if (eval->holdbuf == NULL) {
+        eval->hsize = INIT_BUF_SIZE;
+        eval->holdbuf = apr_pcalloc(eval->pool, eval->hsize);
+    }
+    if (eval->genbuf == NULL) {
+        eval->gsize = INIT_BUF_SIZE;
+        eval->genbuf = apr_pcalloc(eval->pool, eval->gsize);
+    }
+    eval->lspend = eval->linebuf;
+    eval->hspend = eval->holdbuf;
+    eval->lcomend = &eval->genbuf[71];
+
+    for (i = 0; i < sizeof(eval->abuf) / sizeof(eval->abuf[0]); i++)
+        eval->abuf[i] = NULL;
+    eval->aptr = eval->abuf;
+    eval->pending = NULL;
+    eval->inar = apr_pcalloc(eval->pool, commands->nrep * sizeof(unsigned char));
+    eval->nrep = commands->nrep;
+
+    eval->dolflag = 0;
+    eval->sflag = 0;
+    eval->jflag = 0;
+    eval->delflag = 0;
+    eval->lreadyflag = 0;
+    eval->quitflag = 0;
+    eval->finalflag = 1; /* assume we're evaluating only one file/stream */
+    eval->numpass = 0;
+    eval->nullmatch = 0;
+    eval->col = 0;
+
+    for (i = 0; i < commands->nfiles; i++) {
+        const char* filename = commands->fname[i];
+        if (apr_file_open(&eval->fcode[i], filename,
+                          APR_WRITE | APR_CREATE, APR_OS_DEFAULT,
+                          eval->pool) != APR_SUCCESS) {
+            eval_errf(eval, SEDERR_COMES, filename);
+            return APR_EGENERAL;
+        }
+    }
+
+    return APR_SUCCESS;
+}
+
+/*
+ * sed_destroy_eval
+ */
+void sed_destroy_eval(sed_eval_t *eval)
+{
+    int i;
+    /* eval->linebuf, eval->holdbuf, eval->genbuf and eval->inar are allocated
+     * on pool. It will be freed when pool will be freed */
+    for (i = 0; i < eval->commands->nfiles; i++) {
+        if (eval->fcode[i] != NULL) {
+            apr_file_close(eval->fcode[i]);
+            eval->fcode[i] = NULL;
+        }
+    }
+}
+
+/*
+ * sed_eval_file
+ */
+apr_status_t sed_eval_file(sed_eval_t *eval, apr_file_t *fin, void *fout)
+{
+    for (;;) {
+        char buf[1024];
+        apr_size_t read_bytes = 0;
+
+        read_bytes = sizeof(buf);
+        if (apr_file_read(fin, buf, &read_bytes) != APR_SUCCESS)
+            break;
+
+        if (sed_eval_buffer(eval, buf, read_bytes, fout) != APR_SUCCESS)
+            return APR_EGENERAL;
+
+        if (eval->quitflag)
+            return APR_SUCCESS;
+    }
+
+    return sed_finalize_eval(eval, fout);
+}
+
+/*
+ * sed_eval_buffer
+ */
+apr_status_t sed_eval_buffer(sed_eval_t *eval, const char *buf, int bufsz, void *fout)
+{
+    apr_status_t rv;
+
+    if (eval->quitflag)
+        return APR_SUCCESS;
+
+    if (!sed_canbe_finalized(eval->commands)) {
+        /* Commands were not finalized properly. */
+        const char* error = sed_get_finalize_error(eval->commands, eval->pool);
+        if (error) {
+            eval_errf(eval, error);
+            return APR_EGENERAL;
+        }
+    }
+
+    eval->fout = fout;
+
+    /* Process leftovers */
+    if (bufsz && eval->lreadyflag) {
+        eval->lreadyflag = 0;
+        eval->lspend--;
+        *eval->lspend = '\0';
+        rv = execute(eval);
+        if (rv != 0)
+            return APR_EGENERAL;
+    }
+
+    while (bufsz) {
+        char *n;
+        int llen;
+
+        n = memchr(buf, '\n', bufsz);
+        if (n == NULL)
+            break;
+
+        llen = n - buf;
+        if (llen == bufsz - 1) {
+            /* This might be the last line; delay its processing */
+            eval->lreadyflag = 1;
+            break;
+        }
+        
+        appendmem_to_linebuf(eval, buf, llen + 1);
+        --eval->lspend;
+        /* replace new line character with NULL */
+        *eval->lspend = '\0';
+        buf += (llen + 1);
+        bufsz -= (llen + 1);
+        rv = execute(eval);
+        if (rv != 0)
+            return APR_EGENERAL;
+        if (eval->quitflag)
+            break;
+    }
+
+    /* Save the leftovers for later */
+    if (bufsz) {
+        appendmem_to_linebuf(eval, buf, bufsz);
+    }
+
+    return APR_SUCCESS;
+}
+
+/*
+ * sed_finalize_eval
+ */
+apr_status_t sed_finalize_eval(sed_eval_t *eval, void *fout)
+{
+    if (eval->quitflag)
+        return APR_SUCCESS;
+
+    if (eval->finalflag)
+        eval->dolflag = 1;
+
+    eval->fout = fout;
+
+    /* Process leftovers */
+    if (eval->lspend > eval->linebuf) {
+        apr_status_t rv;
+
+        if (eval->lreadyflag) {
+            eval->lreadyflag = 0;
+            eval->lspend--;
+        } else {
+            /* Code can probably reach here when last character in output
+             * buffer is not a newline.
+             */
+            /* Assure space for NULL */
+            append_to_linebuf(eval, "");
+        }
+
+        *eval->lspend = '\0';
+        rv = execute(eval);
+        if (rv != 0)
+            return APR_EGENERAL;
+    }
+
+    eval->quitflag = 1;
+
+    return APR_SUCCESS;
+}
+
+/*
+ * execute
+ */
+static apr_status_t execute(sed_eval_t *eval)
+{
+    sed_reptr_t *ipc = eval->commands->ptrspace;
+    step_vars_storage step_vars;
+
+    eval->lnum++;
+
+    eval->sflag = 0;
+
+    if (eval->pending) {
+        ipc = eval->pending;
+        eval->pending = NULL;
+    }
+
+    memset(&step_vars, 0, sizeof(step_vars));
+
+    while (ipc->command) {
+        char *p1;
+        char *p2;
+        apr_status_t rv;
+        int c;
+
+        p1 = ipc->ad1;
+        p2 = ipc->ad2;
+
+        if (p1) {
+
+            if (eval->inar[ipc->nrep]) {
+                if (*p2 == CEND) {
+                    p1 = 0;
+                } else if (*p2 == CLNUM) {
+                    c = (unsigned char)p2[1];
+                    if (eval->lnum > eval->commands->tlno[c]) {
+                        eval->inar[ipc->nrep] = 0;
+                        if (ipc->negfl)
+                            goto yes;
+                        ipc = ipc->next;
+                        continue;
+                    }
+                    if (eval->lnum == eval->commands->tlno[c]) {
+                        eval->inar[ipc->nrep] = 0;
+                    }
+                } else if (match(eval, p2, 0, &step_vars)) {
+                    eval->inar[ipc->nrep] = 0;
+                }
+            } else if (*p1 == CEND) {
+                if (!eval->dolflag) {
+                    if (ipc->negfl)
+                        goto yes;
+                    ipc = ipc->next;
+                    continue;
+                }
+            } else if (*p1 == CLNUM) {
+                c = (unsigned char)p1[1];
+                if (eval->lnum != eval->commands->tlno[c]) {
+                    if (ipc->negfl)
+                        goto yes;
+                    ipc = ipc->next;
+                    continue;
+                }
+                if (p2)
+                    eval->inar[ipc->nrep] = 1;
+            } else if (match(eval, p1, 0, &step_vars)) {
+                if (p2)
+                    eval->inar[ipc->nrep] = 1;
+            } else {
+                if (ipc->negfl)
+                    goto yes;
+                ipc = ipc->next;
+                continue;
+            }
+        }
+
+        if (ipc->negfl) {
+            ipc = ipc->next;
+            continue;
+        }
+
+yes:
+        rv = command(eval, ipc, &step_vars);
+        if (rv != APR_SUCCESS)
+            return rv;
+
+        if (eval->quitflag)
+            return APR_SUCCESS;
+
+        if (eval->pending)
+            return APR_SUCCESS;
+
+        if (eval->delflag)
+            break;
+
+        if (eval->jflag) {
+            eval->jflag = 0;
+            if ((ipc = ipc->lb1) == 0) {
+                ipc = eval->commands->ptrspace;
+                break;
+            }
+        } else
+            ipc = ipc->next;
+    }
+
+    if (!eval->commands->nflag && !eval->delflag)
+        wline(eval, eval->linebuf, eval->lspend - eval->linebuf);
+
+    if (eval->aptr > eval->abuf)
+        arout(eval);
+
+    eval->delflag = 0;
+
+    eval->lspend = eval->linebuf;
+
+    return APR_SUCCESS;
+}
+
+/*
+ * match
+ */
+static int match(sed_eval_t *eval, char *expbuf, int gf,
+                 step_vars_storage *step_vars)
+{
+    char   *p1;
+    int circf;
+
+    if(gf) {
+        if(*expbuf)    return(0);
+        step_vars->locs = p1 = step_vars->loc2;
+    } else {
+        p1 = eval->linebuf;
+        step_vars->locs = 0;
+    }
+
+    circf = *expbuf++;
+    return(sed_step(p1, expbuf, circf, step_vars));
+}
+
+/*
+ * substitute
+ */
+static int substitute(sed_eval_t *eval, sed_reptr_t *ipc,
+                      step_vars_storage *step_vars)
+{
+    if(match(eval, ipc->re1, 0, step_vars) == 0)    return(0);
+
+    eval->numpass = 0;
+    eval->sflag = 0;        /* Flags if any substitution was made */
+    if (dosub(eval, ipc->rhs, ipc->gfl, step_vars) != APR_SUCCESS)
+        return -1;
+
+    if(ipc->gfl) {
+        while(*step_vars->loc2) {
+            if(match(eval, ipc->re1, 1, step_vars) == 0) break;
+            if (dosub(eval, ipc->rhs, ipc->gfl, step_vars) != APR_SUCCESS)
+                return -1;
+        }
+    }
+    return(eval->sflag);
+}
+
+/*
+ * dosub
+ */
+static apr_status_t dosub(sed_eval_t *eval, char *rhsbuf, int n,
+                          step_vars_storage *step_vars)
+{
+    char *lp, *sp, *rp;
+    int c;
+    apr_status_t rv = APR_SUCCESS;
+
+    if(n > 0 && n < 999) {
+        eval->numpass++;
+        if(n != eval->numpass) return APR_SUCCESS;
+    }
+    eval->sflag = 1;
+    lp = eval->linebuf;
+    sp = eval->genbuf;
+    rp = rhsbuf;
+    sp = place(eval, sp, lp, step_vars->loc1);
+    while ((c = *rp++) != 0) {
+        if (c == '&') {
+            sp = place(eval, sp, step_vars->loc1, step_vars->loc2);
+            if (sp == NULL)
+                return APR_EGENERAL;
+        }
+        else if (c == '\\') {
+            c = *rp++;
+            if (c >= '1' && c < NBRA+'1') {
+                sp = place(eval, sp, step_vars->braslist[c-'1'],
+                           step_vars->braelist[c-'1']);
+                if (sp == NULL)
+                    return APR_EGENERAL;
+            }
+            else
+                *sp++ = c;
+          } else
+            *sp++ = c;
+        if (sp >= eval->genbuf + eval->gsize) {
+            /* expand genbuf and set the sp appropriately */
+            grow_gen_buffer(eval, eval->gsize + 1024, &sp);
+        }
+    }
+    lp = step_vars->loc2;
+    step_vars->loc2 = sp - eval->genbuf + eval->linebuf;
+    append_to_genbuf(eval, lp, &sp);
+    copy_to_linebuf(eval, eval->genbuf);
+    return rv;
+}
+
+/*
+ * place
+ */
+static char *place(sed_eval_t *eval, char *asp, char *al1, char *al2)
+{
+    char *sp = asp;
+    int n = al2 - al1;
+    unsigned int reqsize = (sp - eval->genbuf) + n + 1;
+
+    if (eval->gsize < reqsize) {
+        grow_gen_buffer(eval, reqsize, &sp);
+    }
+    memcpy(sp, al1, n);
+    return sp + n;
+}
+
+/*
+ * command
+ */
+static apr_status_t command(sed_eval_t *eval, sed_reptr_t *ipc,
+                            step_vars_storage *step_vars)
+{
+    int    i;
+    char   *p1, *p2, *p3;
+    int length;
+    char sz[32]; /* 32 bytes enough to store 64 bit integer in decimal */
+
+
+    switch(ipc->command) {
+
+        case ACOM:
+            if(eval->aptr >= &eval->abuf[SED_ABUFSIZE]) {
+                eval_errf(eval, SEDERR_TMAMES, eval->lnum);
+            } else {
+                *eval->aptr++ = ipc;
+                *eval->aptr = NULL;
+            }
+            break;
+
+        case CCOM:
+            eval->delflag = 1;
+            if(!eval->inar[ipc->nrep] || eval->dolflag) {
+                for (p1 = ipc->re1; *p1; p1++)
+                    ;
+                wline(eval, ipc->re1, p1 - ipc->re1);
+            }
+            break;
+        case DCOM:
+            eval->delflag++;
+            break;
+        case CDCOM:
+            p1 = eval->linebuf;
+
+            while(*p1 != '\n') {
+                if(*p1++ == 0) {
+                    eval->delflag++;
+                    return APR_SUCCESS;
+                }
+            }
+
+            p1++;
+            copy_to_linebuf(eval, p1);
+            eval->jflag++;
+            break;
+
+        case EQCOM:
+            length = apr_snprintf(sz, sizeof(sz), "%d", (int) eval->lnum);
+            wline(eval, sz, length);
+            break;
+
+        case GCOM:
+            copy_to_linebuf(eval, eval->holdbuf);
+            break;
+
+        case CGCOM:
+            append_to_linebuf(eval, "\n");
+            append_to_linebuf(eval, eval->holdbuf);
+            break;
+
+        case HCOM:
+            copy_to_holdbuf(eval, eval->linebuf);
+            break;
+
+        case CHCOM:
+            append_to_holdbuf(eval, "\n");
+            append_to_holdbuf(eval, eval->linebuf);
+            break;
+
+        case ICOM:
+            for (p1 = ipc->re1; *p1; p1++);
+            wline(eval, ipc->re1, p1 - ipc->re1);
+            break;
+
+        case BCOM:
+            eval->jflag = 1;
+            break;
+
+
+        case LCOM:
+            p1 = eval->linebuf;
+            p2 = eval->genbuf;
+            eval->genbuf[72] = 0;
+            while(*p1)
+                if((unsigned char)*p1 >= 040) {
+                    if(*p1 == 0177) {
+                        p3 = rub;
+                        while ((*p2++ = *p3++) != 0)
+                            if(p2 >= eval->lcomend) {
+                                *p2 = '\\';
+                                wline(eval, eval->genbuf,
+                                      strlen(eval->genbuf));
+                                p2 = eval->genbuf;
+                            }
+                        p2--;
+                        p1++;
+                        continue;
+                    }
+                    if(!isprint(*p1 & 0377)) {
+                        *p2++ = '\\';
+                        if(p2 >= eval->lcomend) {
+                            *p2 = '\\';
+                            wline(eval, eval->genbuf, strlen(eval->genbuf));
+                            p2 = eval->genbuf;
+                        }
+                        *p2++ = (*p1 >> 6) + '0';
+                        if(p2 >= eval->lcomend) {
+                            *p2 = '\\';
+                            wline(eval, eval->genbuf, strlen(eval->genbuf));
+                            p2 = eval->genbuf;
+                        }
+                        *p2++ = ((*p1 >> 3) & 07) + '0';
+                        if(p2 >= eval->lcomend) {
+                            *p2 = '\\';
+                            wline(eval, eval->genbuf, strlen(eval->genbuf));
+                            p2 = eval->genbuf;
+                        }
+                        *p2++ = (*p1++ & 07) + '0';
+                        if(p2 >= eval->lcomend) {
+                            *p2 = '\\';
+                            wline(eval, eval->genbuf, strlen(eval->genbuf));
+                            p2 = eval->genbuf;
+                        }
+                    } else {
+                        *p2++ = *p1++;
+                        if(p2 >= eval->lcomend) {
+                            *p2 = '\\';
+                            wline(eval, eval->genbuf, strlen(eval->genbuf));
+                            p2 = eval->genbuf;
+                        }
+                    }
+                } else {
+                    p3 = trans[(unsigned char)*p1-1];
+                    while ((*p2++ = *p3++) != 0)
+                        if(p2 >= eval->lcomend) {
+                            *p2 = '\\';
+                            wline(eval, eval->genbuf, strlen(eval->genbuf));
+                            p2 = eval->genbuf;
+                        }
+                    p2--;
+                    p1++;
+                }
+            *p2 = 0;
+            wline(eval, eval->genbuf, strlen(eval->genbuf));
+            break;
+
+        case NCOM:
+            if(!eval->commands->nflag) {
+                wline(eval, eval->linebuf, eval->lspend - eval->linebuf);
+            }
+
+            if(eval->aptr > eval->abuf)
+                arout(eval);
+            eval->lspend = eval->linebuf;
+            eval->pending = ipc->next;
+
+            break;
+        case CNCOM:
+            if(eval->aptr > eval->abuf)
+                arout(eval);
+            append_to_linebuf(eval, "\n");
+            eval->pending = ipc->next;
+            break;
+
+        case PCOM:
+            wline(eval, eval->linebuf, eval->lspend - eval->linebuf);
+            break;
+        case CPCOM:
+            for (p1 = eval->linebuf; *p1 != '\n' && *p1 != '\0'; p1++);
+            wline(eval, eval->linebuf, p1 - eval->linebuf);
+            break;
+
+        case QCOM:
+            if (!eval->commands->nflag)
+                wline(eval, eval->linebuf, eval->lspend - eval->linebuf);
+
+            if(eval->aptr > eval->abuf)
+                arout(eval);
+
+            eval->quitflag = 1;
+            break;
+        case RCOM:
+            if(eval->aptr >= &eval->abuf[SED_ABUFSIZE]) {
+                eval_errf(eval, SEDERR_TMRMES, eval->lnum);
+            } else {
+                *eval->aptr++ = ipc;
+                *eval->aptr = NULL;
+            }
+            break;
+
+        case SCOM:
+            i = substitute(eval, ipc, step_vars);
+            if (i == -1) {
+                return APR_EGENERAL;
+            }
+            if(ipc->pfl && eval->commands->nflag && i) {
+                if(ipc->pfl == 1) {
+                    wline(eval, eval->linebuf, eval->lspend - eval->linebuf);
+                } else {
+                    for (p1 = eval->linebuf; *p1 != '\n' && *p1 != '\0'; p1++);
+                    wline(eval, eval->linebuf, p1 - eval->linebuf);
+                }
+            }
+            if (i && (ipc->findex >= 0) && eval->fcode[ipc->findex])
+                apr_file_printf(eval->fcode[ipc->findex], "%s\n",
+                                eval->linebuf);
+            break;
+
+        case TCOM:
+            if(eval->sflag == 0)  break;
+            eval->sflag = 0;
+            eval->jflag = 1;
+            break;
+
+        case WCOM:
+            if (ipc->findex >= 0)
+                apr_file_printf(eval->fcode[ipc->findex], "%s\n",
+                                eval->linebuf);
+            break;
+        case XCOM:
+            copy_to_genbuf(eval, eval->linebuf);
+            copy_to_linebuf(eval, eval->holdbuf);
+            copy_to_holdbuf(eval, eval->genbuf);
+            break;
+
+        case YCOM: 
+            p1 = eval->linebuf;
+            p2 = ipc->re1;
+            while((*p1 = p2[(unsigned char)*p1]) != 0)    p1++;
+            break;
+    }
+    return APR_SUCCESS;
+}
+
+/*
+ * arout
+ */
+static void arout(sed_eval_t *eval)
+{
+    eval->aptr = eval->abuf - 1;
+    while (*++eval->aptr) {
+        if ((*eval->aptr)->command == ACOM) {
+            char *p1;
+
+            for (p1 = (*eval->aptr)->re1; *p1; p1++);
+            wline(eval, (*eval->aptr)->re1, p1 - (*eval->aptr)->re1);
+        } else {
+            apr_file_t *fi = NULL;
+            char buf[512];
+            apr_size_t n = sizeof(buf);
+
+            if (apr_file_open(&fi, (*eval->aptr)->re1, APR_READ, 0, eval->pool)
+                              != APR_SUCCESS)
+                continue;
+            while ((apr_file_read(fi, buf, &n)) == APR_SUCCESS) {
+                if (n == 0)
+                    break;
+                eval->writefn(eval->fout, buf, n);
+                n = sizeof(buf);
+            }
+            apr_file_close(fi);
+        }
+    }
+    eval->aptr = eval->abuf;
+    *eval->aptr = NULL;
+}
+
+/*
+ * wline
+ */
+static void wline(sed_eval_t *eval, char *buf, int sz)
+{
+    eval->writefn(eval->fout, buf, sz);
+    eval->writefn(eval->fout, "\n", 1);
+}
+