Index: modules/mappers/mod_negotiation.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/mappers/mod_negotiation.c,v
retrieving revision 1.71
diff -u -r1.71 mod_negotiation.c
--- modules/mappers/mod_negotiation.c	2001/08/07 00:51:22	1.71
+++ modules/mappers/mod_negotiation.c	2001/08/20 15:44:11
@@ -181,7 +181,8 @@
 typedef struct var_rec {
     request_rec *sub_req;       /* May be NULL (is, for map files) */
     char *mime_type;            /* MUST be lowercase */
-    char *file_name;
+    char *file_name;            /* Set to 'this' (for map file body content) */
+    apr_off_t body;             /* Only for map file body content */
     const char *content_encoding;
     apr_array_header_t *content_languages;   /* list of languages for this variant */
     char *content_charset;
@@ -258,6 +259,7 @@
     mime_info->sub_req = NULL;
     mime_info->mime_type = "";
     mime_info->file_name = "";
+    mime_info->body = 0;
     mime_info->content_encoding = NULL;
     mime_info->content_languages = NULL;
     mime_info->content_charset = "";
@@ -509,6 +511,10 @@
     new->accept_charsets =
         do_header_line(r->pool, apr_table_get(hdrs, "Accept-Charset"));
 
+    /* This is possibly overkill for some servers, heck, we have 
+     * only 33 index.html variants in docs/docroot (today).
+     * Make this configurable?
+     */
     new->avail_vars = apr_array_make(r->pool, 40, sizeof(var_rec));
 
     return new;
@@ -679,6 +685,12 @@
 
     cp += strlen(cp);
 
+    /* We need to shortcut the rest of this block following the Body:
+     * tag - we will not look for continutation after this line.
+     */
+    if (!strncasecmp(buffer, "Body:", 5))
+        return header_seen;
+
     while (apr_file_getc(&c, map) != APR_EOF) {
         if (c == '#') {
             /* Comment line */
@@ -724,6 +736,47 @@
     return header_seen;
 }
 
+static apr_off_t get_body(char *buffer, apr_size_t *len, const char *tag, 
+                          apr_file_t *map)
+{
+    char *endbody;
+    int bodylen;
+    apr_off_t pos;
+
+    /* We are at the first character following a body:tag\n entry 
+     * Suck in the body, then backspace to the first char after the 
+     * closing tag entry.  If we fail to read, find the tag or back
+     * up then we have a hosed file, so give up already
+     */
+    if (apr_file_read(map, buffer, len) != APR_SUCCESS) {
+        return -1;
+    }      
+    endbody = strstr(buffer, tag);
+    if (!endbody) {
+        return -1;
+    }
+    bodylen = endbody - buffer;
+    endbody += strlen(tag);
+    /* Skip all the trailing cruft after the end tag to the next line */
+    while (*endbody) {
+        if (*endbody == '\n') {
+            ++endbody;
+            break;
+        }
+        ++endbody;
+    }
+
+    pos = -(apr_off_t)(*len - (endbody - buffer));
+    if (apr_file_seek(map, APR_CUR, &pos) != APR_SUCCESS) {
+        return -1;
+    }
+
+    /* Give the caller back the actual body's offset and length */
+    *len = bodylen;
+    return pos - (endbody - buffer);
+}
+
+
 /* Stripping out RFC822 comments */
 
 static void strip_paren_comments(char *hdr)
@@ -784,20 +837,23 @@
     return cp;
 }
 
-static int read_type_map(negotiation_state *neg, request_rec *rr)
+static int read_type_map(apr_file_t **map, negotiation_state *neg, request_rec *rr)
 {
     request_rec *r = neg->r;
-    apr_file_t *map = NULL;
+    apr_file_t *map_ = NULL;
     apr_status_t status;
     char buffer[MAX_STRING_LEN];
     enum header_state hstate;
     struct var_rec mime_info;
     int has_content;
 
+    if (!map)
+        map = &map_;
+
     /* We are not using multiviews */
     neg->count_multiviews_variants = 0;
 
-    if ((status = apr_file_open(&map, rr->filename, APR_READ,
+    if ((status = apr_file_open(map, rr->filename, APR_READ,
                 APR_OS_DEFAULT, neg->pool)) != APR_SUCCESS) {
         ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
                       "cannot access type map file: %s", rr->filename);
@@ -808,7 +864,7 @@
     has_content = 0;
 
     do {
-        hstate = get_header_line(buffer, MAX_STRING_LEN, map);
+        hstate = get_header_line(buffer, MAX_STRING_LEN, *map);
 
         if (hstate == header_seen) {
             char *body1 = lcase_header_name_return_body(buffer, neg->r);
@@ -854,6 +910,17 @@
                 if (cp>desc) *(cp-1)=0;
                 mime_info.description = desc;
             }
+            else if (!strncmp(buffer, "body:", 5)) {
+                char *tag = apr_pstrdup(neg->pool, body);
+                char *eol = strchr(tag, '\0');
+                apr_size_t len = MAX_STRING_LEN;
+                while (--eol >= tag && apr_isspace(*eol)) 
+                    *eol = '\0';
+                if ((mime_info.body = get_body(buffer, &len, tag, *map)) < 0)
+                     break;
+                mime_info.bytes = len;
+                mime_info.file_name = rr->filename;
+            }
         }
         else {
             if (*mime_info.file_name && has_content) {
@@ -867,7 +934,8 @@
         }
     } while (hstate != header_eof);
 
-    apr_file_close(map);
+    if (map_)
+        apr_file_close(map_);
 
     set_vlist_validator(r, rr);
 
@@ -1040,7 +1108,7 @@
             if (sub_req->status != HTTP_OK) {
                 return sub_req->status;
             }
-            return read_type_map(neg, sub_req);
+            return read_type_map(NULL, neg, sub_req);
         }
 
         /* Have reasonable variant --- gather notes. */
@@ -2638,6 +2706,7 @@
 static int handle_map_file(request_rec *r)
 {
     negotiation_state *neg;
+    apr_file_t *map;
     var_rec *best;
     int res;
     char *udir;
@@ -2646,20 +2715,64 @@
 	return DECLINED;
 
     neg = parse_accept_headers(r);
-    if ((res = read_type_map(neg, r))) {
+    if ((res = read_type_map(&map, neg, r))) {
         return res;
     }
 
     res = do_negotiation(r, neg, &best, 0);
     if (res != 0) return res;
 
+    if (best->body)
+    {
+        apr_bucket_brigade *bb;
+        apr_bucket *e;
+
+        ap_allow_methods(r, REPLACE_ALLOW, "GET", "OPTIONS", "POST", NULL);
+        if ((res = ap_discard_request_body(r)) != OK) {
+            return res;
+        }
+        //if (r->method_number == M_OPTIONS) {
+        //    return ap_send_http_options(r);
+        //}
+        if (r->method_number != M_GET && r->method_number != M_POST) {
+            return HTTP_METHOD_NOT_ALLOWED;
+        }
+
+        /* ### These may be implemented by adding some 'extra' info
+         *     of the file offset onto the etag
+         * ap_update_mtime(r, r->finfo.mtime);
+         * ap_set_last_modified(r);
+         * ap_set_etag(r);
+         */
+        apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");
+        ap_set_content_length(r, best->bytes); 
+        if ((res = ap_meets_conditions(r)) != OK) {
+            return res;
+        }
+
+        bb = apr_brigade_create(r->pool);
+        e = apr_bucket_file_create(map, best->body, 
+                                   (apr_size_t)best->bytes, r->pool);
+        APR_BRIGADE_INSERT_TAIL(bb, e);
+        e = apr_bucket_eos_create();
+        APR_BRIGADE_INSERT_TAIL(bb, e);
+
+        return ap_pass_brigade(r->output_filters, bb);
+    }
+
+    /* XXX This looks entirely bogus.  Why can't negotiated content
+     *     have additional path_info?  Just because I choose a given
+     *     script based on content type doesn't mean I don't want it
+     *     to run the same set of actions (think three languages of
+     *     viewcvs.cgi.xx, all serving the same repository.)
+     */
     if (r->path_info && *r->path_info) {
         r->uri[ap_find_path_info(r->uri, r->path_info)] = '\0';
     }
     udir = ap_make_dirstr_parent(r->pool, r->uri);
     udir = ap_escape_uri(r->pool, udir);
     ap_internal_redirect(apr_pstrcat(r->pool, udir, best->file_name,
-                                    r->path_info, NULL), r);
+                                     r->path_info, NULL), r);
     return OK;
 }
 
