]> granicus.if.org Git - apache/commitdiff
Make mod_include use a hash table to associate directive tags with
authorRyan Bloom <rbb@apache.org>
Thu, 7 Dec 2000 03:32:54 +0000 (03:32 +0000)
committerRyan Bloom <rbb@apache.org>
Thu, 7 Dec 2000 03:32:54 +0000 (03:32 +0000)
functions.  This allows modules to implement their own SSI tags easily.
The idea is simple enough, a module can insert it's own tag and function
combination into a hash table provided by mod_include.  While mod_include
parses an SSI file, when it encounters a tag in the file, it does a
hash lookup to find the function that implements that tag, and passes
all of the relevant data to the function.  That function is then
responsible for processing the tag and handing the remaining data back
to mod_include for further processing.
Submitted by: Paul J. Reder <rederpj@raleigh.ibm.com>
Reviewed by: Ryan Bloom

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@87241 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
modules/filters/mod_include.c
modules/filters/mod_include.h

diff --git a/CHANGES b/CHANGES
index 0a0440e35f9b5e460adca3c51440dd01e27b4d3c..3dc0b1ec7f5b1cdd3a7afc337f4ba66f046038a9 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,14 @@
 Changes with Apache 2.0a9
+  *) Make mod_include use a hash table to associate directive tags with
+     functions.  This allows modules to implement their own SSI tags easily.
+     The idea is simple enough, a module can insert it's own tag and function
+     combination into a hash table provided by mod_include.  While mod_include
+     parses an SSI file, when it encounters a tag in the file, it does a
+     hash lookup to find the function that implements that tag, and passes
+     all of the relevant data to the function.  That function is then
+     responsible for processing the tag and handing the remaining data back
+     to mod_include for further processing.
+     [Paul J. Reder <rederpj@raleigh.ibm.com>]
 
   *) Get rid of ap_new_apr_connection().  ap_new_connection() now has 
      fewer parameters: the local and remote socket addresses were removed
index 0ba6f2b278239403d5bf20beb2bfef91c7a56786..c45b9ef6b616c518365d8f24e94925648594dfbc 100644 (file)
@@ -67,6 +67,7 @@
 #include "apr.h"
 #include "apr_strings.h"
 #include "apr_thread_proc.h"
+#include "apr_hash.h"
 
 #define CORE_PRIVATE
 
@@ -93,6 +94,9 @@
 #endif
 #include "util_ebcdic.h"
 
+
+static apr_hash_t *include_hash;
+
 /* ------------------------ Environment function -------------------------- */
 
 /* XXX: could use ap_table_overlap here */
@@ -180,7 +184,7 @@ static ap_bucket *find_start_sequence(ap_bucket *dptr, include_ctx_t *ctx,
                     apr_size_t  start_index;
 
                     /* We want to split the bucket at the '<'. */
-                    ctx->state            = PARSE_TAG;
+                    ctx->state            = PARSE_DIRECTIVE;
                     ctx->tag_length       = 0;
                     ctx->parse_pos        = 0;
                     ctx->tag_start_bucket = dptr;
@@ -250,7 +254,7 @@ static ap_bucket *find_end_sequence(ap_bucket *dptr, include_ctx_t *ctx, ap_buck
         }
         while (c - buf != len) {
             if (*c == str[ctx->parse_pos]) {
-                if (ctx->state == PARSE_TAG) {
+                if (ctx->state != PARSE_TAIL) {
                     ctx->state             = PARSE_TAIL;
                     ctx->tail_start_bucket = dptr;
                     ctx->tail_start_index  = c - buf;
@@ -258,18 +262,28 @@ static ap_bucket *find_end_sequence(ap_bucket *dptr, include_ctx_t *ctx, ap_buck
                 ctx->parse_pos++;
             }
             else {
-                if (ctx->state == PARSE_TAG) {
+                if (ctx->state == PARSE_DIRECTIVE) {
                     if (ctx->tag_length == 0) {
                         if (!apr_isspace(*c)) {
                             ctx->tag_start_bucket = dptr;
                             ctx->tag_start_index  = c - buf;
                             ctx->tag_length       = 1;
+                            ctx->directive_length = 1;
                         }
                     }
                     else {
+                        if (!apr_isspace(*c)) {
+                            ctx->directive_length++;
+                        }
+                        else {
+                            ctx->state = PARSE_TAG;
+                        }
                         ctx->tag_length++;
                     }
                 }
+                else if (ctx->state == PARSE_TAG) {
+                    ctx->tag_length++;
+                }
                 else {
                     if (str[ctx->parse_pos] == '\0') {
                         ap_bucket *tmp_buck = dptr;
@@ -294,16 +308,24 @@ static ap_bucket *find_end_sequence(ap_bucket *dptr, include_ctx_t *ctx, ap_buck
                          ctx->tag_length += ctx->parse_pos;
 
                          if (*c == str[0]) {
-                             ctx->parse_pos         = 1;
                              ctx->state             = PARSE_TAIL;
                              ctx->tail_start_bucket = dptr;
                              ctx->tail_start_index  = c - buf;
+                             ctx->tag_length       += ctx->parse_pos;
+                             ctx->parse_pos         = 1;
                          }
                          else {
-                             ctx->parse_pos         = 0;
-                             ctx->state             = PARSE_TAG;
+                             if (ctx->tag_length > ctx->directive_length) {
+                                 ctx->state = PARSE_TAG;
+                             }
+                             else {
+                                 ctx->state = PARSE_DIRECTIVE;
+                                 ctx->directive_length += ctx->parse_pos;
+                             }
                              ctx->tail_start_bucket = NULL;
                              ctx->tail_start_index  = 0;
+                             ctx->tag_length       += ctx->parse_pos;
+                             ctx->parse_pos         = 0;
                          }
                     }
                 }
@@ -557,50 +579,6 @@ static void get_tag_and_value(include_ctx_t *ctx, char **tag,
     return;
 }
 
-static char *get_directive(include_ctx_t *ctx, dir_token_id *fnd_token)
-{
-    char *c = ctx->curr_tag_pos;
-    char *dest;
-    int len = 0;
-
-    SKIP_TAG_WHITESPACE(c);
-
-    dest = c;
-    /* now get directive */
-    while ((*c != '\0') && (!apr_isspace(*c))) {
-        *c = apr_tolower(*c);
-        c++;
-        len++;
-    }
-    
-    *c++ = '\0';
-    ctx->curr_tag_pos = c;
-
-    *fnd_token = TOK_UNKNOWN;
-    switch (len) {
-    case 2: if      (!strcmp(dest, "if"))       *fnd_token = TOK_IF;
-            break;
-    case 3: if      (!strcmp(dest, "set"))      *fnd_token = TOK_SET;
-            break;
-    case 4: if      (!strcmp(dest, "else"))     *fnd_token = TOK_ELSE;
-            else if (!strcmp(dest, "elif"))     *fnd_token = TOK_ELIF;
-            else if (!strcmp(dest, "exec"))     *fnd_token = TOK_EXEC;
-            else if (!strcmp(dest, "echo"))     *fnd_token = TOK_ECHO;
-            break;
-    case 5: if      (!strcmp(dest, "endif"))    *fnd_token = TOK_ENDIF;
-            else if (!strcmp(dest, "fsize"))    *fnd_token = TOK_FSIZE;
-            break;
-    case 6: if      (!strcmp(dest, "config"))   *fnd_token = TOK_CONFIG;
-            break;
-    case 7: if      (!strcmp(dest, "include"))  *fnd_token = TOK_INCLUDE;
-            break;
-    case 8: if      (!strcmp(dest, "flastmod")) *fnd_token = TOK_FLASTMOD;
-            else if (!strcmp(dest, "printenv")) *fnd_token = TOK_PRINTENV;
-            break;
-    }
-
-    return (dest);
-}
 
 /*
  * Do variable substitution on strings
@@ -2688,7 +2666,7 @@ static void send_parsed_content(ap_bucket_brigade **bb, request_rec *r,
             }
 
             /* Adjust the current bucket position based on what was found... */
-            if ((tmp_dptr != NULL) && (ctx->state == PARSE_TAG)) {
+            if ((tmp_dptr != NULL) && (ctx->state == PARSE_DIRECTIVE)) {
                 if (ctx->tag_start_bucket != NULL) {
                     dptr = ctx->tag_start_bucket;
                 }
@@ -2702,7 +2680,9 @@ static void send_parsed_content(ap_bucket_brigade **bb, request_rec *r,
         }
 
         /* State to check for the ENDING_SEQUENCE. */
-        if (((ctx->state == PARSE_TAG) || (ctx->state == PARSE_TAIL)) &&
+        if (((ctx->state == PARSE_DIRECTIVE) ||
+             (ctx->state == PARSE_TAG)       ||
+             (ctx->state == PARSE_TAIL))       &&
             (dptr != AP_BRIGADE_SENTINEL(*bb))) {
             tmp_dptr = find_end_sequence(dptr, ctx, *bb);
 
@@ -2729,9 +2709,10 @@ static void send_parsed_content(ap_bucket_brigade **bb, request_rec *r,
         /* State to processed the directive... */
         if (ctx->state == PARSED) {
             ap_bucket    *content_head = NULL, *tmp_bkt;
+            apr_size_t    tmp_i;
             char          tmp_buf[TMP_BUF_SIZE];
-            char         *directive_str = NULL;
-            dir_token_id  directive_token;
+            int (*handle_func)(include_ctx_t *, ap_bucket_brigade **, request_rec *,
+                           ap_filter_t *, ap_bucket *, ap_bucket **);
 
             /* By now the full tag (all buckets) should either be set aside into
              *  ssi_tag_brigade or contained within the current bb. All tag
@@ -2783,55 +2764,26 @@ static void send_parsed_content(ap_bucket_brigade **bb, request_rec *r,
              *  the contents of a single bucket!
              */
 
-            /* pjr - This is about to change to be a generic function
-             *       call from a hash table lookup. All functions need
-             *       to have the same parms...
+            /* Retrieve the handler function to be called for this directive from the
+             *  functions registered in the hash table.
+             * Need to lower case the directive for proper matching. Also need to have
+             *  it NULL terminated (and include the NULL in the length) for proper
+             *  hash matching.
              */
-            directive_str = get_directive(ctx, &directive_token);
-
-            switch (directive_token) {
-            case TOK_IF:
-                ret = handle_if(ctx, bb, r, f, dptr, &content_head);
-                break;
-            case TOK_ELSE:
-                ret = handle_else(ctx, bb, r, f, dptr, &content_head);
-                break;
-            case TOK_ELIF:
-                ret = handle_elif(ctx, bb, r, f, dptr, &content_head);
-                break;
-            case TOK_ENDIF:
-                ret = handle_endif(ctx, bb, r, f, dptr, &content_head);
-                break;
-            case TOK_EXEC:
-                ret = handle_exec(ctx, bb, r, f, dptr, &content_head);
-                break;
-            case TOK_INCLUDE:
-                ret = handle_include(ctx, bb, r, f, dptr, &content_head);
-                break;
-            case TOK_SET:
-                ret = handle_set(ctx, bb, r, f, dptr, &content_head);
-                break;
-            case TOK_ECHO:
-                ret = handle_echo(ctx, bb, r, f, dptr, &content_head);
-                break;
-            case TOK_FSIZE:
-                ret = handle_fsize(ctx, bb, r, f, dptr, &content_head);
-                break;
-            case TOK_CONFIG:
-                ret = handle_config(ctx, bb, r, f, dptr, &content_head);
-                break;
-            case TOK_FLASTMOD:
-                ret = handle_flastmod(ctx, bb, r, f, dptr, &content_head);
-                break;
-            case TOK_PRINTENV:
-                ret = handle_printenv(ctx, bb, r, f, dptr, &content_head);
-                break;
+            for (tmp_i = 0; tmp_i < ctx->directive_length; tmp_i++) {
+                ctx->combined_tag[tmp_i] = apr_tolower(ctx->combined_tag[tmp_i]);
+            }
+            ctx->combined_tag[ctx->directive_length] = '\0';
+            ctx->curr_tag_pos = &ctx->combined_tag[ctx->directive_length+1];
 
-            case TOK_UNKNOWN:
-            default:
+            handle_func = apr_hash_get(include_hash, ctx->combined_tag, ctx->directive_length+1);
+            if (handle_func != NULL) {
+                ret = (*handle_func)(ctx, bb, r, f, dptr, &content_head);
+            }
+            else {
                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
                               "unknown directive \"%s\" in parsed doc %s",
-                              directive_str, r->filename);
+                              ctx->combined_tag, r->filename);
                 CREATE_ERROR_BUCKET(ctx, tmp_bkt, dptr, content_head);
             }
 
@@ -2888,6 +2840,7 @@ static void send_parsed_content(ap_bucket_brigade **bb, request_rec *r,
             ctx->tail_start_index  = 0;
             ctx->curr_tag_pos      = NULL;
             ctx->tag_length        = 0;
+            ctx->directive_length  = 0;
 
             if (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
                 while (!AP_BRIGADE_EMPTY(ctx->ssi_tag_brigade)) {
@@ -3075,6 +3028,33 @@ static int includes_filter(ap_filter_t *f, ap_bucket_brigade *b)
     return OK;
 }
 
+void ap_register_include_handler(char *tag, handler func)
+{
+    apr_hash_set(include_hash, tag, strlen(tag) + 1, func);
+}
+
+static void include_post_config(apr_pool_t *p, apr_pool_t *plog,
+                                apr_pool_t *ptemp, server_rec *s)
+{
+    include_hash = apr_make_hash(p);
+
+    ap_register_include_handler("if", handle_if);
+    ap_register_include_handler("set", handle_set);
+    ap_register_include_handler("else", handle_else);
+    ap_register_include_handler("elif", handle_elif);
+    ap_register_include_handler("exec", handle_exec);
+    ap_register_include_handler("echo", handle_echo);
+    ap_register_include_handler("endif", handle_endif);
+    ap_register_include_handler("fsize", handle_fsize);
+    ap_register_include_handler("config", handle_config);
+    ap_register_include_handler("include", handle_include);
+    ap_register_include_handler("flastmod", handle_flastmod);
+    ap_register_include_handler("printenv", handle_printenv);
+}
+
+/*
+ * Module definition and configuration data structs...
+ */
 static const command_rec includes_cmds[] =
 {
     AP_INIT_TAKE1("XBitHack", set_xbithack, NULL, OR_OPTIONS, 
@@ -3084,6 +3064,7 @@ static const command_rec includes_cmds[] =
 
 static void register_hooks(void)
 {
+    ap_hook_post_config(include_post_config, NULL, NULL, AP_HOOK_REALLY_FIRST);
     ap_register_output_filter("INCLUDES", includes_filter, AP_FTYPE_CONTENT);
 }
 
index bd92b04337d2025279b0d83a6cdcbee3757f8d8d..32db22b1714b1f84307a505ffbcdf8ddc2958a94 100644 (file)
@@ -139,7 +139,7 @@ module AP_MODULE_DECLARE_DATA includes_module;
  * ssi_tag_brigade: The temporary brigade used by this filter to set aside
  *                  the buckets containing parts of the ssi tag and headers.
  */
-typedef enum {PRE_HEAD, PARSE_HEAD, PARSE_TAG, PARSE_TAIL, PARSED} states;
+typedef enum {PRE_HEAD, PARSE_HEAD, PARSE_DIRECTIVE, PARSE_TAG, PARSE_TAIL, PARSED} states;
 typedef struct include_filter_ctx {
     states       state;
     long         flags;    /* See the FLAG_XXXXX definitions. */
@@ -157,6 +157,7 @@ typedef struct include_filter_ctx {
 
     char        *combined_tag;
     char        *curr_tag_pos;
+    apr_size_t   directive_length;
     apr_size_t   tag_length;
 
     apr_size_t   error_length;
@@ -177,10 +178,6 @@ typedef struct include_filter_ctx {
 #define FLAG_CLEAR_PRINT_COND 0xFFFFFFFC  /* Reset PRINTING and COND_TRUE*/
 #define FLAG_CLEAR_PRINTING   0xFFFFFFFE  /* Reset just PRINTING bit.    */
 
-typedef enum {TOK_UNKNOWN, TOK_IF, TOK_SET, TOK_ECHO, TOK_ELIF, TOK_ELSE,
-              TOK_EXEC, TOK_PERL, TOK_ENDIF, TOK_FSIZE, TOK_CONFIG,
-              TOK_INCLUDE, TOK_FLASTMOD, TOK_PRINTENV} dir_token_id;
-
 #define CREATE_ERROR_BUCKET(cntx, t_buck, h_ptr, ins_head)        \
 {                                                                 \
     apr_size_t e_wrt;                                             \
@@ -203,4 +200,10 @@ if ((AP_BRIGADE_EMPTY(cntxt->ssi_tag_brigade)) &&                \
     brgd = tag_plus;                                             \
 }
 
+typedef int (*handler)(include_ctx_t *ctx, ap_bucket_brigade **bb, 
+                       request_rec *r, ap_filter_t *f, ap_bucket *head_ptr, 
+                       ap_bucket **inserted_head);
+
+void ap_register_include_handler(char *tag, handler func);
+
 #endif /* MOD_INCLUDE */