/*****************************************************************************/
#ifdef COMMENTS_WITH_COMMENTS
/*
                                MapUrl.c

Functions supporting mapping of URLs to VMS file specifications and VMS 
specifications to URLs, uses the HTTPd rule mapping file.  The number of rules 
it can store is not fixed, but is limited by available memory and the 
practical considerations of processing large, linear rule databases. 

Comment lines may be provided by beginning a line with a '#' character.

Lines may be continued by ensuring the last character on the line is a '\'.

Virtual server syntax is the same as for authentication/authorization rules,
[[virtual-host]] (all ports of host) or [[virtual-host:virtual-port]] (
matching specified port of host) for a specific host and [[*]] to resume rules
for all virtual hosts.


Wildcards
---------
A single '*' wildcard matches intervening text up until the first character
encountered that matches the character immediately following the wildcard.

The rule

  map /one/*/three/* /first/*/third/*

would map the following request path

  /one/two/three/four

into

  /first/two/third/four

but would not match the request path

  /one/two/and-a-half/three/four

Two consecutive ('**') wildcards match intervening text up until the next
string that matches the string following the wildcard.

The rule

  map /one/**/three/* /first/*/third/*

would map the following request paths

  /one/two/three/four
  /one/two/and-a-half/three/four

respectively into

  /first/two/third/four
  /first/two/and-a-half/third/four

Historically (pre v8.1) the template wildcard-matched portions of path strings
could only be substituted in the positions of corresponding wildcards in the
result string.  There can be ocassions where there is not a one-to-one
relationship between these wildcard sections in the source (template) and
target (result) strings.  It is sometimes useful to be able to drop one or more
strings from the template and/or change the order of substitution.

The use of 'positional wildcards' (for want of an even marginally better term)
will allow this.  When a wildcard is used in the result string it may
optionally be followed by a single apostrophe character (') and a single digit
character '1'..'9'.  The digit is used to indicate which of the template
wildcards is intended (first to last).

The following request path

  /one/two/three/four/five.six

would be processed by the rule

  pass /one/*/three/*/* /*'2/one/*'1/*'3

into the mapped path

  /four/one/two/five.six

Using this notation all template wildcards must be specified 'positionally' :^)
This rule is not legal.

  pass /one/*/three/* /two/*'2/four/*

Positional wildcards ONLY APPLY to PASS, MAP, REDIRECT and USER rules.
They cannot be used with EXEC, SCRIPT and UXEC rules.


Extended File Specification
---------------------------
Extended file specifications (required for ODS-5) are supported.  A rule is
used to flag such paths for ODS-5 tailored processing.  Things that are not
done to ODS-5 paths; case-changes and character substitution.  Things that are
done to ODS-5 paths; escaping to and from the extended file specification
canonical escape syntax (i.e. '^').

Paths located on ODS-5 volumes need to have the ODS-5 rule SET against them.

  set /extended/* ODS-5

It is possible to explicitly mark a path as ODS-2 also, although as this is the
default it is usually unnecessary.  One situation where this might be useful is
where all paths are ODS-5 except a small minority, in which case a rule such as
the following might be used.

  set /* ODS-5
  set /old-path/* ODS-2

Paths derived using the USER rule are specially processed.  The SYSUAF user
home directory's structure is determined using a sys$getdvi() to determined
it's ACPTYPE.  Hence no explicit rule is required for these.


Mapping Rules
-------------
[[virtual-host:optional-virtual-port]]
EXEC       template  result           [setting(s)] [conditional(s)]
EXEC       (runtime)template  result  [setting(s)] [conditional(s)]
EXEC+      template  result           [setting(s)] [conditional(s)]
FAIL       template                                [conditional(s)]
MAP        template  result           [setting(s)] [conditional(s)]
PASS       template                                [conditional(s)]
PASS       template  result           [setting(s)] [conditional(s)]
PROTECT    template  access-control   [setting(s)] [conditional(s)]
REDIRECT   template  result           [setting(s)] [conditional(s)]
SCRIPT     template  result           [setting(s)] [conditional(s)]
SCRIPT     (runtime)template  result  [setting(s)] [conditional(s)]
SCRIPT+    template  result           [setting(s)] [conditional(s)]
SET        template  setting(s)                    [conditional(s)]
USER       template  result           [setting(s)] [conditional(s)]
UXEC       template  result           [setting(s)] [conditional(s)]
UXEC       (runtime)template  result  [setting(s)] [conditional(s)]
UXEC+      template  result           [setting(s)] [conditional(s)]

(Somewhat awkwardly the square brackets both denote optional fields and to
literally delimit a conditional.  Please do not confuse the two!)

Note that as of V8.0 path settings (anything that can be set with a SET rule)
can also follow the result string.  If the rule applies and any conditional is
matched then the settings are applied to the path BEFORE the mapping is done.

PASS rules can map to a result comprising an HTTP status code (e.g. 403) with a
following textual message (including white-space).  Just delimit the result
string with double or single quotes, or within curly braces.  For example:

PASS /private/stuff/* "403 Can't go in there!" [!ho:my.host.name]
PASS /private/stuff/* '403 "/private/stuff/" is off-limits!' [!ho:my.host.name]

EXEC rules can be used to map both directories and file types.  The former only
requires a single, trailing wildcard (example 1).  The latter requires two
wildcards (examples 2 and 3).  The  irst to locate the script, the second to
map any path.  This format can be used to globally map a file type, or confine
that mapping to specified part of a tree.

EXEC /cgi-bin/*
EXEC *.cgi* *.cgi*
EXEC /specified/path/*.cgi* /specified/path/*.cgi*

PROTECT rules can be used require authorization for for the matching path.  It
complements the facility available via the WASD_CONFIG_AUTH configuration file,
providing a simpler (and to certain extent less capble) authorization schema. 
See the relevant section in the description to AUTH.C module.


General Run-Time Mapping
------------------------
This is distinct from "Run-Time (RTE) Environment" scripting discussed below. 
It also uses a similar rule construct and historically came much after RTE
mappings.  Hence the '!' kludge mentioned shortly.  The historical way to map
script file types (other than .EXE and .COM which are handled implicitly) has
been to add [DclScriptRunTime] entries to WASD_CONFIG_GLOBAL.  The (very small)
disadvantage to using this is the need to completely restart the server to
apply changes.  Design-wise the better place for these sorts of mappings (file
types to run-time engine) is here in WASD_CONFIG_MAP (it also allows /DO=MAP to
change these).  From v8.2 we can use an RTE-like construct to specify what
non-RTE engine should be activated to process the specified script.  It can be
an executable, a DCL procedure or even verb.  Here's how we might activate Perl
using these rules:

  exec /cgi-bin/*.pl* (!perl)/cgi-bin/*.pl*
  exec /cgi-bin/*.pl* (!$perl_root:[000000]perl.exe)/cgi-bin/*.pl*
  exec /cgi-bin/*.pl* (!@cgi-bin:[000000]perl.com)/cgi-bin/*.pl*
  script /this_one* (!$perl_root:[000000]perl.exe)/cgi-bin/this_one.pl*

The first uses a CLI verb, the second activates a specified executable and the
last executes a DCL procedure (which in this presumably sets up some
environment before activating Perl).

Note the leading '!'.  This is a bit of a kludge but not too onerous.


Mapping Logical-Name Content
----------------------------
Generally logical names only provide concealed devices into the file-system
for "root"ing some part of the web-space.  That is the value of the logical
name is only used in RMS (file-system) parsing, etc.

There is a single exception.  When template and result patterns contain no
wildcard(s) a one-to-one mapping if performed.  For example

  pass /this_is_an_example /dka0/example/this.txt

and the file content at

  DKA0:[EXAMPLE]THIS.TXT

is returned.

With a one-to-one mapping the result string can represent a logical name and
the translation of that name used as the mapping element.  For a PASS rule the
logical name value is used as the VMS result of the mapping (and therefore a
VMS file specification).  For a REDIRECT rule it should be a URL undertood by
redirection.  Here are examples:

  $ DEFINE /SYSTEM /EXEC EXAMPLE_OF DKA0:[EXAMPLE]ONE.TXT
  $ DEFINE /SYSTEM /EXEC EXAMPLE_GO "/wasd_root/runtime/httpd/"

  pass /of EXAMPLE_OF
  redirect /go EXAMPLE_GO

Using this capability, the target of a rule may be changed simply by modifying
the translated value of a logical name.

Note that the mapping rule logical name translation is case-sensitive, and so
common use of logical names (all upper-case) means the rule must be all upper
case.  With all lower or mixed case the rule must specify exact case, as in
this example:

  $ DEFINE /SYSTEM /EXEC "EXAMPLE_go_two" "/wasd_root/src/httpd/"

  redirect /go_2 EXAMPLE_go_two


Run-Time Environment (RTE) Mapping
----------------------------------
Each persistent run-time environment must have it's own mapping environment. 
This may, or may not, correspond to physically distinct directory areas.  It is
the mapping rules that identify run-time environments.  A parenthesized
interprester specification leads the result component of the rule.  This is
first extracted and then the EXEC rule behaves as normal.  Hence the following 
example shows two such environments each with it's own interpreter.

  exec /plbin/* (cgi-bin:[000000]perlrte.exe)/ht_root/perl_local/*
  exec /pybin/* (cgi-bin:[000000]pyrte.exe)/ht_root/python_local/*
  script /this_one* (cgi-bin:[000000]oneeng.exe)/cgi-bin/this.one*

The "/plbin/*" identifies request paths beginning with this string as a
hypothetical, persistent Perl run-time interpreter, with the Perl source
scripts available to it from HT_ROOT:[PERL_LOCAL].  Similarly with a
hypothetical Java Environment.

If a request with the following URI is given to the server

  http://the.host.name/plbin/plsearch/web/doc/

the following components will be derived

  run-time interpreter ... CGI-BIN:[000000]PERLRT.EXE
  Perl source script ..... HT_ROOT:[PERL_LOCAL]PLSEARCH.PL
  request path-info ...... WEB:[DOC]


Mapping User Directories
------------------------
The USER rule effectively PASSes the user's SYSUAF default device and directory
as the first wildcard substitution in the rule.  Accounts that are disusered,
captive or have expired passwords are never mapped, they are always "denied
access by default".  In the same way privileged accounts (those possessing
SYSPRV) cannot be mapped this way (see below for a workaround).

An example; if the user name DANIEL has the default device USER$DISK: and
default directory [DANIEL] the following request path

  /~daniel/

would be mapped to the result and VMS file specification

  /user$disk/daniel/www/
  USER$DISK:[DANIEL.WWW]

using the following rule.

  USER  /~*/*  /*/www/

Note the "/www" subdirectory component.  It is recommended that user
directories always be mapped to a subdirectory of the physcial area.  This
effectively "sandboxes" Web access to that subdirectory hierarchy, allowing the
user privacy elsewhere in the home area.

To accomodate request user paths that do not incorporate a trailing delimiter
after the username the following redirect may be used to cause the browser to
re-request with a more appropriate path.

  REDIRECT  /~*  ///*/www/

WASD also "reverse maps" VMS specifications into paths and so requires
additional rules to provides these mappings.  (Reverse mapping is required
during directory listings and error reporting.)  In the above case the
following rules would actually be required (and in the stated order).

  USER  /~*/*  /*/www/
  PASS  /~*/*  /user$disk/*/www/*

Where user home directories are spread over multiple devices (physical or
concealed logical) a reverse-mapping rule would be required for each. Consider
the following situation, where user directories are distributed across these
devices (concealed logicals)

  USER$GROUP1:
  USER$GROUP2:
  USER$GROUP2:
  USER$OTHER:

This would require the following mapping rules (in the stated order).

  USER  /~*/*  /*/www/
  PASS  /~*/*  /user$group1/*/www/*
  PASS  /~*/*  /user$group2/*/www/*
  PASS  /~*/*  /user$group3/*/www/*
  PASS  /~*/*  /user$other/*/www/*

Of course vanilla mapping rules may be used to provide for special cases.  For
instance, if there is requirement for a particular, privileged account to have
a user mapping that could be provided as in the following (rather exagerated)
example.

  PASS  /~system/*  /sys$common/sysmgr/www/*
  USER  /~*/*  /*/www/
  PASS  /~*/*  /user$disk/*/www/*


Path SETings
------------
The SET rule allows characteristics to be set against a particular path.  They
are then transfered to any request matching the path and applied to the
request.  This applies to FILES, not necessarily to scripts that supply a full
HTTP response.  These need to check and act on information provided by relevant
CGI variables.  Note that as of V8.0 path SETings can be applied with any rule
that has a result string (i.e. the setting must follow both a template and a
result and preceded any conditional).

[NO]ACCEPT=LANG=([DEFAULT=<language>][,CHAR=<char>][,VARIANT=NAME|TYPE])
                path will attempt to resolve a language-specific document,
                with default language (optional), 'char' is a character
                (optional) separating language from file type (default '_'),
                if the path template has a file type wildcard included the
                file content-type does not need to be "text/.."

ACCESS=[NO]SERVER    access as server account (best effort) 
ACCESS=[NO]PROFILE   access according to SYSUAF profile
ACCESS=[NO]READ      allowed to read this path
ACCESS=[NO]WRITE     allowed to write to this path (implied read)

[NO]ALERT         indicate that a request with this path has been detected,
                  HTTPDMON and OPCOM alert, and the process log gets a record
ALERT=MAP         alert provided immediately after mapping,
ALERT=AUTH        or immediately after authorization,
ALERT=END         or provided at request rundown (the default)
ALERT=<integer>   the integer can be a specific HTTP status value (e.g. 501)
                  or the category (range of values, e.g. 599) alert if match

[NO]AUTH=ALL      all access must be subject to authorization (or fail)
[NO]AUTH=ONCE     authorize only the script portion of the request,
                  do not repeat authorization on any path component
[NO]AUTH=MAPPED   authorize against the mapped path

AUTH=REVALIDATE=<hh:mm:ss>  revalidate authentication for this path
                            at the specified interval

AUTH=SYSUAF=PWDEXPURL=<string>  redirection URL if s SYSUAF-authenticated
                                user's primary password has expired

[NO]CACHE              cache files (not any other output)
CACHE=NONE             do not cache
CACHE=[NO]CGI          cache CGI-compliant script output
CACHE=[NO]COOKIE       cache including requests with cookies
CACHE=EXPIRES=0        cancels path cache entry expiry
CACHE=EXPIRES=DAY      entry expires on change of day
CACHE=EXPIRES=HOUR     entry expires on change of hour
CACHE=EXPIRES=MINUTE   entry expires on change of minute
CACHE=EXPIRES=<hh:mm:ss>  specify the exact period before expiry
CACHE=[NO]FILE         cache files
CACHE=GUARD=<hh:mm:ss> prevent (Pragma:) reloads during these seconds
CACHE=MAX=<integer>    up to kBytes in size (overrides [CacheFileKBytesMax])
CACHE=[NO]NET          cache response network output of any kind
CACHE=[NO]NPH          cache NPH-compliant script output
CACHE=[NO]PERMANENT    cache this, do not allow it to be flushed
CACHE=[NO]QUERY        cache including requests with query strings
CACHE=[NO]SCRIPT       synonym for (both) 'cache=cgi cache=nph'
CACHE=[NO]SSI          cache Server-Side Includes output

CGIPLUSIN=CC=NONE   set explicit carriage-control to be added to each
CGIPLUSIN=CC=LF     CGIPLUSIN record (esoteric? a bit; ask Alex Ivanov :^)
CGIPLUSIN=CC=CR
CGIPLUSIN=CC=CRLF

CGIPLUSIN=EOF       terminate each request's CGIPLUSIN stream with an EOF
                    (same comment as immediately above)

CGI=PREFIX=<string>  instead of the default "WWW_" specify what is needed,
                     to have no prefix at all specify CGI=PREFIX=

CHARSET=<string>  any "text/..." content-type files use the specified charset

CLIENT=FORWARDED         replace client IP address with Forwarded: header
CLIENT=IF=FORWARDED      replace client IP address IF a Forwarded: header
CLIENT=LITERAL=<string>  really just for ease of testing
CLIENT=RESET             revert to original (upstream) client data
CLIENT=XFORWARDEDFOR     replace IP address with X-Forwarded-for: header
CLIENT=IF=XFORWARDEDFOR  replace IP address IF an X-Forwarded-for: header

CORS=AGE=       Access-Control-Max-Age: response header
CORS=CRED=      Access-Control-Allow-Credentials: response header
CORS=EXPOSE=    Access-Control-Expose-Headers: response header
CORS=HEADERS=   Access-Control-Allow-Headers: response header
CORS=METHODS=   Access-Control-Allow-Methods: response header
CORS=ORIGIN=    Access-Control-Allow-Origin: response header

CONTENT=        all files have the "Content-Type:" forced to that specified
                (obvious use is to provide plain-text or binary versions of
                files using perhaps "set /plain-text/* content=text/plain",
                then mapping that generally back with "map /plain-text/* /*")
                another: "set /bogus/* "content=text/html; charset=BLAH"

CSS=<url>       style sheet to load to customise server-generated pages

DICT=[<key>[=<value]] manipulate the meta dictionary

DIR=ACCESS            directory listing enabled  (same as [DirAccess])
DIR=ACCESS=SELECTIVE  allowed if directory contains .WWW_BROWSABLE
DIR=NOACCESS          directory listing disabled

DIR=CHARSET=<string>  directory listing page is given this charset
                      (this could be called the Alex Ivanov rule ;^)

DIR=DELIMIT[=BOTH]    both top and bottom rule and any readme
DIR=DELIMIT=HEADER    only top rule and any readme
DIR=DELIMIT=FOOTER    only bottom rule and any readme
DIR=DELIMIT=NONE      no top or bottom rule or readme

DIR=FONT=INHERIT      listing font inherited from browser or document
DIR=FONT=MONOSPACE    listing font monospace (default)

DIR=ILINK       icon plain-text link (default)
DIR=NOILINK     no icon plain-text link

DIR=IMPLIEDWILDCARD    implied wildcard enabled
DIR=NOIMPLIEDWILDCARD  implied wildcard disabled

DIR=LAYOUT=     same as index=<string>

DIR=SORT=<char>[+|-]   pre-sort directory listing

DIR=STYLE[=DEFAULT]    current (post-10.4) style
DIR=STYLE=ORIGINAL[2]  pre-v8.2 WASD style
DIR=STYLE=ANCHOR[2]    post-v8.2 WASD style
DIR=STYLE=HTDIR[2]     Alex Ivanov's listing style
DIR=STYLE=SORT[2]      HTML table and sortable
DIR=STYLE=TABLE[2]     post-v10.4 WASD style (current, default)

DIR=TARGET=<string>            open a file in this window (e.g. "_blank")

DIR=TITLE=<integer>    directory name (0 = none, 1 = top, 2, 3..99 = bottom)
DIR=TITLE=DEFAULT      whatever the default is for the style
DIR=TITLE=OWNER        the VMS owner
DIR=TITLE=REMOTE       if authorised access just the remote user name
DIR=TITLE=THIS=".."    literal string

DIR=THESE=<string>[,<string>]  one of more wildcard file name selectors

DIR=VERSIONS=<integer>|*       display a maximum of this many file versions

DIR=WILDCARD    wildcard-forced listing enabled   (same as [DirWildcard])
DIR=NOWILDCARD  wildcard-forced listing disabled

[NO]EXPIRED     files in the path are sent "pre-expired"

NOHTML            cancel any/all previously set html=
HTML=BODYTAG=     HTML <body..> tag
HTML=HEADER=      HTML page header text (can include markup)
HTML=HEADERTAG=   HTML header tag <td..> (usually colour)
HTML=FOOTER=      HTML page footer text (can include markup)
HTML=FOOTERTAG=   HTML footer tag <td..> (usually colour)

HTTP=ACCEPT-CHARSET=<string>   explicitly set the request's accepted charset
HTTP=ACCEPT-LANGUAGE=<string>  explicitly set the request's accepted language

HTTP2=PROTOCOL=1.1              downgrade HTTP/2 to HTTP/1.1 (immediately)
HTTP2=SEND=GOAWAY[=<integer>]   send GOAWAY frame with optional error code
HTTP2=SEND=PING                 send PING frame
HTTP2=SEND=RESET[=<integer>]    send RST_STREAM frame with optional error code
HTTP2=STREAMS=MAX=<integer>     per-path maximum concurrent streams
HTTP2=WRITE=[low|normal|high]   set response write priority

INDEX=<string>  "Index of" format string (same as "?httpd=index&...")

[NO]LOG         requests to this path are not to be access-logged

MAP=[NO]AFTER      (re)map after authorization
MAP=[NO]ELLIPSIS   allows mapping of the VMS '...' ellipsis wildcard that
                   provides for tree traversal
MAP=[NO]EMPTY      traditionally a path must have contained at least one char
                   to be mapped, this allows "empty" paths to be mapped
MAP=[NO]METHOD     set the path to process an HTTP (/1.1) extension-method
                   e.g. 'if (request-method:QUARK) \
                            script /quark* /cgi-bin/quark* map=method'
MAP=[NO]ONCE       normally, when a script has been derived from a path, WASD
                   goes on to map the 'path' portion in a separate pass - this
                   SETing provides the derived path as-is
MAP=RESTART        begin again at rule number one (limit on this action)
MAP=ROOT=<string>  prefix all result paths with this string (path)
MAP=SET=[NO]REQUEST  path SETings affect the request (or only the mapping)
MAP=SET=[NO]IGNORE   all path SETings are ignored (except 'map=set=noignore')
MAP=URI            begin to process against raw request URI (path plus query)

NOTEPAD=[+]<string>  text (perhaps keywords) added to a request during mapping
                     that can be detected during subsequent metacon, mapping
                     and authorization rule processing (not externally visible)
                     NOTEPAD PERSISTS ACROSS INTERNALLY REDIRECTED REQUESTS!
                     
ODS=2             path marked as ODS-2 (path default)
ODS=5             path marked as ODS-5, with EFS encoded file names
ODS=ADS           path marked as having filenames Advanced Server (6) encoded
ODS=PWK           path marked as having filenames PATHWORKS (4 & 5) encoded
ODS=SMB           same as ODS=ADS (only for the comfort of Samba users)
ODS=SRI           path marked as having filenames SRI (MultiNet NFS) encoded
NOODS             reset the path's ODS file naming
ODS=NAME=8BIT     file name uses native ODS 8 bit encoding (default)
ODS=NAME=UTF8     file name uses explicit UTF-8 encoding
ODS=NAME=DEFAULT  return naming to the default encoding

[NO]PROFILE     VMS SYSUAF-authenticated security profile applies/does not
                apply to this path

PROXY=[NO]AFFINITY             enable/disable affinity

PROXY=NOFORWARDED              disable the "Forwarded: by" field
PROXY=FORWARDED[=BY]           enable the field with "by" data
PROXY=FORWARDED=FOR            enable the field with "by" and "for" data
PROXY=FORWARDED=ADDRESS        enable the field with "by" and "for" address

PROXY=NOXFORWARDEDFOR          disable "X-Forwarded-For:" field
PROXY=XFORWARDEDFOR[=ENABLED]  add client host, propagate any existing
PROXY=XFORWARDEDFOR=ADDRESS    add client address, propagate any existing
PROXY=XFORWARDEDFOR=UNKNOWN    hide this host, propagate any existing

PROXY=BIND=<string>    bind the outgoing proxy socket to this IP address
PROXY=CHAIN=<string>   chain to this proxy server
PROXY=CHAIN=CRED=<string>   <username>:<password> credentials for chain

PROXY=HEADER=<name>[=<string>]  remove or modify this request header

PROXY=REVERSE=[NO]AUTH  suppress propagation of any "Authorize:" header
PROXY=REVERSE=LOCATION  rewrite a reverse proxy 302 "Location:" response to
                        contain the proxy server (can ONLY be used following
                        a 'redirect /foo/* /http://foo.bar/*' rule)
PROXY=REVERSE=[NO]VERIFY   for a locally authorized, reverse proxied request
                           create a verifyable authorization header containing
                           the username and a unique identifier for 'callback'

PROXY=TUNNEL=REQUEST=<string>  make this the tunnelled request

PUT=MAX=<integer>          maximum Kbytes allowed to be PUT/POSTed
                           is '*' is used instead it means no limit
PUT=RFM=[FIX512|STM|STMCR|STMLF|UDF] set the record format for binary PUT/POSTs

QUERY-STRING=<string>  set the request's query-string (URL-encode if necessary)

REGEX=<keyword>  AWK, DEFAULT, ED, EGREP, GREP, POSIX_AWK, POSIX_BASIC,
                 POSIX_EGREP, POSIX_EXTENDED, POSIX_MINIMAL_BASIC,
                 POSIX_MINIMAL_EXTENDED, SED, WASD

REPORT=400=<integer>   bad request reported as (e.g. "report=400=403")
REPORT=403=<integer>   forbidden reported as (e.g. "report=403=404")
REPORT=404=<integer>   not found reported as (e.g. "report=404=403")
REPORT=BASIC     supply basic error information for requests on this path
REPORT=DETAILED  supply detailed error information for requests on this path
REPORT=TUNNEL    supply error message suitable for firewall/raw tunnel

RESPONSE=200=203       modify a 200 response to 203 for log analysis
RESPONSE=203=200       undoes the above

RESPONSE=[NO]CSP=<string>    add Content Security Policy to response header
RESPONSE=[NO]CSPRO=<string>  add CSP Report Only to response header

RESPONSE=HEADER=ADD=<string>  append this header field to the response header
RESPONSE=HEADER=NOADD         cancel (potentially) additional header fields
RESPONSE=HEADER=FULL   revert to normal response header transmission
RESPONSE=HEADER=NONE   suppress response header transmission (i.e. the file 
                       or content contains *full* HTTP response - NPH-like!)
RESPONSE=HEADER=BEGIN  suppress the end-of-header blank line so that the file
                       or other content can provide additional header lines

RESPONSE=GZIP=ALL         compress all responses (those possible anyway)
RESPONSE=GZIP=<integer>   compress responses known to be larger than kBytes
RESPONSE=GZIP=NONE        do not compress any responses

RESPONSE=HTTP=ORIGINAL    respond with HTTP/1.0 if request was that version

RESPONSE=[NO]CHUNKED      return applicable responses chunked

RESPONSE=STS=<value>      Strict-Transport-Security: header

RESPONSE=VAR              reset variable length record to default (<LF>)
RESPONSE=VAR=CRLF         variable length record file append <CR><LF>
RESPONSE=VAR=LF                                       append <LF> (default)
RESPONSE=VAR=NONE                                     append nothing

RMSCHAR=<char>  provides the RMS-invalid substitution character, which unless
                otherwise specified is the dollar symbol. Any general
                RMS-valid character may be specified (e.g. alpha-numeric,
                '$', '-' or '_', although the latter three are probably the
                only REAL choices).  When an RMS-invalid character (e.g. '+')
                or syntax (e.g. multiple periods) is encountered this character
                is substituted.  NOTE: is not applied to ODS-5 volumes.

SCRIPT=AS=<user>  the script process will be using this username/account
                  (only applies if detached script processes are enabled)
SCRIPT=BIT-BUCKET=<hh:mm:ss>  per-path equivalent to config file setting
SCRIPT=BODY=[NO]DECODE    decode any chunked or gzip encoded request body
                          before delivering the content to the script
SCRIPT=COMMAND=<string>   provide the script activation command-line with
                          qualifiers, parameters, etc. (first character must
                          be an asterisk or it overrides the full verb)
SCRIPT=CONTROL=<string>   equivalent of "Script-Control:" in CGI response
SCRIPT=CPU=<hh:mm:ss>  CPU limit for each *script's* processing
                       (not a per-process limit like CPULM would be)
SCRIPT=DEFAULT=<string>   default directory script process should be set to
                          before activation ('#" disables this, can be a U**x
                          style spec beginning '/' in which case it's ignored)
SCRIPT=LIFETIME=<hh:mm:ss>  CGI or CGIplus lifetime for quiescent processes
                            (per-path equivalent of [DclZombieLifetime] and
                             DclCGIplusLifeTime] global directives)
SCRIPT=SYMBOLS=[NO]TRUNCATE  truncate CGI symbols that would otherwise exceed
                             the maxima of this VMS versions CLI
SCRIPT=SYNTAX=[NO]UNIX    script file name and path translated is passed in
                          U**x syntax
SCRIPT=FIND     (default) search for the script file in any 'script' or 'exec'
SCRIPT=NOFIND   do not search for the script file (for use with RTEs)
SCRIPT=PARAMS=[+](name=value)  a list of additional (non-CGI) variables
                               assigned and passed to a script
[NO]SCRIPT=PATH=FIND      check that the PATH_TRANSLATED exists and before
                          invoking the script report 404 if it doesn't
[NO]SCRIPT=QUERY=NONE     do not parse query string into form or key elements
[NO]SCRIPT=QUERY=RELAXED  don't error report form-url-encoding issues,
                          let the script deal with it if necessary

[NO]SEARCH=NONE           do not activate (any) default search script

SERVICE=<string>          move request to this service (when mapping finished)

SSI=PRIV        mark a path as allowed to contain SSI documents that can
SSI=NOPRIV      use privileged SSI statements (e.g. "#dcl")
SSI=<string>    where string is a list of "#dcl" parameters allowed
                (this is a finer-grained control that SSI=[NO]PRIV)

SSLCGI=NONE     sets the style of SSL-related CGI variables 
SSLCGI=APACHE_MOD_SSL
SSLCGI=APACHE_MOD_SSL_CLIENT
SSLCGI=APACHE_MOD_SSL_EXTENS
SSLCGI=APACHE_MOD_SSL_OID
SSLCGI=PURVEYOR
[NO]SSLCGI

[NO]STMLF       files in this path can be converted from variable to stream-LF

THROTTLE=FROM=             n1, requests after which queuing occurs
THROTTLE=TO=               n2, requests after which the queue is FIFOed
THROTTLE=RESUME=           n3, requests after which FIFOing stops
THROTTLE=BUSY=             n4, requests after which an immediate 503 "busy"
THROTTLE=NONE              resets previously mapped throttle
THROTTLE=TIMEOUT=QUEUE=    t/o1, hh:mm:ss before a queued request processes
THROTTLE=TIMEOUT=BUSY=     t/o2, hh:mm:ss before a queued request terminates
THROTTLE=USER=             u1, per-user throttling
THROTTLE=n1[/u1][,n2,n3,n4,t/o1,t/o2]
                           n1 = 'from',
                           u1 = 'per-user',
                           n2 = 'to',
                           n3 = 'resume',
                           n4 = 'busy',
                           t/o1 = 'timeout-queue',
                           t/o2 = 'timeout-busy'

TIMEOUT=PERSISTENT=  hh:mm:ss  ("none" to disable, formerly 'KEEPALIVE=')
TIMEOUT=NOPROGRESS=  hh:mm:ss
TIMEOUT=OUTPUT=      hh:mm:ss
TIMEOUT=n,n,n        hh:mm:ss; persistent, noprogress, output
NOTIMEOUT            sets all three (n,n,n) timeouts to infinite (none)

WEBDAV=[NO]HIDDEN    display (default), do not display, hidden file names
WEBDAV=[NO]LOCK      allow/apply WebDAV locking to this path
WEBDAV=META=DIR=     string (subdirectory spec or full directory spec)
WEBDAV=[NO]PROFILE   WebDAV access according to SYSUAF profile
WEBDAV=[NO]PROP      allow/apply WebDAV 'dead' property(ies) to this path
WEBDAV=[NO]PUT=LOCK  a resource must be locked before a PUT is allowed
WEBDAV=[NO]READ      WebDAV methods allowed read this tree
WEBDAV=[NO]SERVER    WebDAV access as server account (best effort) 
WEBDAV=[NO]WINPROP   when NOWINPROP windows properties are ignored and emulated
WEBDAV=[NO]WRITE     WebDAV methods allowed write to this path (implied read)
WEBDAV=LOCK=TIMEOUT=DEFAULT=      hh:mm:ss
WEBDAV=LOCK=TIMEOUT=MAX=          hh:mm:ss

WEBSOCKET=INPUT=<integer>   size of WEBSOCKET_INPUT mailbox
WEBSOCKET=OUTPUT=<integer>  size of WEBSOCKET_OUTPUT mailbox


[CONFIGFILE]
------------
The non-global [IncludeFile] equivalent [ConfigFile] designed for local,
per-service configuration management can include a subset of the full set of
mapping rules and settings.  The following are ALLOWED:

  FAIL
  MAP
  PASS
  PROTECT
  REDIRECT

  SET [NO]ACCEPT= 
  SET CHARSET= 
  SET [NO]EXPIRED
  SET [NO]HTML=
  SET INDEX=
  SET [NO]MAP=ONCE
  SET QUERY-STRING=
  SET RESPONSE=HEADER=

Those that cannot be used in [ConfigFile] are reported as errors during
configuration load.


MAP=ROOT Mapping
----------------
This path SETing allows for the more straight-forward mapping of virtual
service paths into a virtual-service-specific directory structure.  It ensures
that for all applicable rules the specified string is prepended to the mapped
path before it is converted into a file-system specification.  This remove the
need to do it with each virtual-service-specific rule.  It needs to be redone
or undone (e.g. set to empty) for each virtual service or when reverting to
general rules.  For example

  [[a.virtual.host]]
  set * map=root=/dka0/a/
  # 'a' has no access to any scripts at all
  pass /* /*
  fail *
  [[b.virtual.host]]
  set * map=root=/dka0/b/
  # 'b' are allowed to execute their own scripts
  exec /cgi-bin/* /cgi-bin/*
  pass /* /*
  fail *
  [[c.virtual.host]]
  # 'c' is allowed to execute the general server scripts
  exec /cgi-bin/* /cgi-bin/*
  set * map=root=/dka0/c/
  pass /* /*
  fail *
  [[*]]
  set * map=root=
  exec /cgi-bin/* /cgi-bin/*
  pass /* /web/*
  fail *


PERSONA-based User Scripting
----------------------------
For use with VMS V6.2 and greater (or 6.0 and 6.1 if compiled with the
PERSONA_MACRO build option), with the /PERSONA qualifier active.
The following rule set will allow user account based scripting.

  SET   /~*/www/cgi-bin/*  script=as=~
  UXEC  /~*/cgi-bin/*  /*/www/cgi-bin/*
  USER  /~*/*  /*/www/*
  REDIRECT  /~*  /~*/
  PASS  /~*/*  /dka0/users/*/*


DECnet-based User Scripting
---------------------------
If DECnet/OSU scripting is enabled then the following rule will activate a
script in the specified user's directory (all other things being equal).
Always map to some unique and specific subdirectory of the home directory.

exec /~*/cgi-bin/* /0""::/where/ever/*/cgi-bin/*

It's a little like the user-mapping rules:

pass /~* /where/ever/*


Mapping Conditionals
--------------------

 ***************
 * OBSOLESCENT *
 ***************

 ALTHOUGH MAPPING RULE CONDITIONALS DESCRIBED BELOW WILL CONTINUE
 TO BE SUPPORTED IN THE FORESEEABLE FUTURE THE PREFERED POST-7.2
 MECHANISM FOR CONDITIONAL PROCESSING OF RULES IS PROVIDED BY THE
 META-CONFIG CONDITIONAL STRUCTURES AND DIRECTIVES.

Conditionals are new to v4.4 and allow mapping rules to be applied
conditionally! That is, the conditionals contain strings that are matched to
specific elements of the request and only if matched are the corresponding
rules applied. The conditionals may contain the '*' and '%' wildcards, and
optionally be negated by an '!' at the start of the conditional string.

Conditionals must follow the rule and are delimited by '[' and ']'. Multiple,
space-separated conditions may be included within one '[...]'. This behaves as
a logical OR (i.e. the condition is true if only one is matched). Multiple
'[...]' conditionals may be included against a rule. These act as a logical
AND (i.e. all must have at least one condition matched). The result of an
entire conditional may be optionally negated by prefixing the '[' with a '!'.

If a conditional is not met the line is completely ignored.  Conditional rules
are not used for 'backward' translation (i.e. from VMS to URL path) so another
rule must exist somewhere to do these mappings.  Conditional rules are only
used by the HTTPd server, they are completely ignored by any scripts using the
MapUrl.c module.

[!]ac:accept               'Accept:'
[!]al:accept-language      'Accept-Language:'
[!]as:accept-charset       'Accept-Charset:'
[!]ca:                     boolean, callout in progress?
[!]ck:cookie               'Cookie:'
[!]dr:string               document root (set map=root=)
[!]ex:                     boolean, extended file specification path
[!]fo:host-name/address    'Forwarded:', proxies/gateways
[!]ho:host-name/address    client's of course!
[!]hm:host network mask    client mask, see immediately below
[!]me:http-method          GET, POST, ?, etc.
[!]mp:string               derived mapped path (after script or map rule)
[!]no:string               notepad keywords/tokens
[!]pa:digit                1 if first pass, 2 if second pass through rules
[!]pi:path-info            request path information
[!]qs:query-string         request query string
[!]rc:[digit]              internally redirected count, 0..4, or boolean
[!]rf:refering-URL         'Referer:'
[!]rq:string               request field (e.g. "Keep-Alive: 300")
[!]ru:string               request URI - non-decoded path string
[!]sc:scheme               request scheme 'http:', 'http', 'https:' or 'https'
[!]sn:server-name          server host name
[!]sp:server-port          server port
[!]st:script-name          if during second pass compare to script name
[!]ua:user-agent           'User-Agent:'
[!]vs:host-name/address    virtual server, "Host:", destination
[!]xf:client               "X-Forwarded-For:" proxied cleint details

The host-mask ('HM') directive is a dotted-decimal network address, a slash,
then a dotted-decimal mask.  For example "[HM:131.185.250.0/255.255.255.192]". 
This has a 6 bit subnet.  It operates by bitwise-ANDing the client host address
with the mask, bitwise-ANDing the network address supplied with the mask, then
comparing the two results for equality.  Using the above example the host
131.185.250.250 would be accepted, but 131.185.250.50 would be rejected.


VERSION HISTORY
---------------
12-FEB-2020  MGD  SET response=[no]csp=<string> ("content-security-policy:")
                  SET response=[no]cspro=<string> ("..policy-report-only:")
                  refactor MapUrl_ExplainPathSet()
20-JAN-2020  MGD  quieten string overflow reports
30-JAN-2019  MGD  SET response=200=203 for request tracking and log analysis
15-AUG-2018  MGD  bugfix; for DECnet tasks when StringSliceValue() modified
                    (25-MAY-2017 allow quote-delim inside space-delimited)
13-AUG-2018  MGD  SET response=var=crlf
                  SET response=var=lf
                  SET response=var=none
06-APR-2018  MGD  MapUrl__Map() allow one-to-one logical name content
05-APR-2018  MGD  bugfix; longstanding MapUrl__Map() mutiple template wildcards
                    when reverse mapping (search for the version log date)
25-JAN-2018  MGD  SET dir=title=default
                  SET dir=title=owner
                  SET dir=title=remote
                  SET dir=title=<integer>
                  SET dir=title=this=<string>
15-JUN-2017  MGD  MapUrl_GuaranteeAccess() mapping as well as authorisation
25-MAY-2017  MGD  bugfix; MapUrl_ExplainPathSet() response=header=add=..
07-MAR-2017  MGD  SET proxy=header=<name>[=<string>]
26-OCT-2015  MGD  SET dict=<key>[=<value>]
                  SET http2=streams=max=<integer>
                  SET http2=protocol=1.1
                  SET http2=write=[low|normal|high]
                  SET http2=send=goaway[=<integer>]
                  SET http2=send=ping
                  SET http2=send=reset[=<integer>]
                  SET sslcgi=apache_mod_ssl_client
                  SET sslcgi=apache_mod_ssl_extens
17-APR-2015  MGD  #define MAP_BUFFER_SIZE for fundamental mapping buffers
04-APR-2015  MGD  bugfix; MapUrl_ExplainPathSet() ->ResponseChunked
24-MAR-2015  MGD  SET response=sts=<value> (Strict-Transport-Security: header)
21-NOV-2014  MGD  MapUrl_SetClientAddress() and MapUrl_ResetClientAddress()
                    call WatchFilterClientService() if client address changed
06-NOV-2014  MGD  SET client=[forwarded|if=forwarded|literal=|reset|
                              if=xforwardedfor|xforwardedfor]
06-SEP-2014  MGD  SET dir=font=[inherit|monospace(D)]
                  SET dir=style=TABLE (new default)
                  SET ods=name=8bit, ods=name=utf8, ods=name=default
                  SET webdav=[no]hidden
                  SET webdav=meta=dir=<string>
28-SEP-2013  MGD  SET cors=<detail>
27-SEP-2013  MGD  SET dir=versions=<integer>|*
17-AUG-2013  MGD  put=rfm=[STM|STMCD|UDF] added to FIX512,STMLF
                  bugfix; MapUrl__Map() MAPURL_REPORT_MATCH_RULENOCOND
25-JUN-2013  MGD  SET dir=delimit=<which>
                  SET dir=[no]ilink
                  SET dir=style=sort (plus the dir=style=<which>2)
                  SET dir=sort=<char>[+|-]
                  SET dir=target=<string>
                  SET dir=these=<wildcard1>[,<wildcard2>]
30-MAY-2013  MGD  bugfix; MapUrl__Map() reverse mapping wildcard copy
15-SEP-2011  MGD  SET map=uri
                  bugfix; map=noonce
06-MAR-2011  MGD  SET cache=[no]cookie
26-FEB-2011  MGD  bugfix; set rule 'CacheSetting' boolean with any CACHE=..
02-JAN-2011  MGD  SET regex=<keyword>
07-SEP-2010  MGD  SET proxy=tunnel=request=<string>
18-JUL-2010  MGD  Uni Malaga pushes report items from !3ZL to !4ZL :-)
01-JUL-2010  MGD  bugfix; MapUrl_ControlReload()
19-JUN-2010  MGD  put=max=* represents no (effective) limit
27-FEB-2010  MGD  SET proxy=chain=cred=<string>
09-FEB-2010  MGD  SET script=lifetime=<hh:mm:ss>
                  SET response=HTTP=original
23-JAN-2010  MGD  SET service=<string>
                  SET websocket=<keyword>
                  SET notimeout (short-hand for timeout=none,none,none)
11-JAN-2010  JPP  MapUrl__Map() increase some buffer sizes
05-SEP-2009  MGD  allow "pass:-1" to indicate reverse-mapping
23-AUG-2009  MGD  SET css=<style-sheet-URL>
                  bugfix; MapUrl__Map() to URL use request ODS not path ODS
21-JUN-2009  MGD  SET put=max=<kbytes>, put=rfm=[FIX512|STMLF]
08-OCT-2008  MGD  bugfix; MapUrl__Map() proxy 'fall-thru'
19-APR-2008  MGD  SET script=agent=as=<account>
23-MAY-2007  MGD  refine loading and mapping of path SETings,
                  MapUrl_Map()/__Map() now have a REQUEST_PATHSET parameter
                  (to better decouple file-system mapping and path SETing)
11-MAY-2007  MGD  bugfix; MapUrl_Map() auth agent modifying path SETings
01-MAY-2007  MGD  SET access=[no]access, access=[no]profile,
                      access=[no]read, access=[no]write,
                      webdav=[no]access, webdav=[no]profile,
                      webdav=[no]read, webdav=[no]write,
                      webdav=[no]lock, webdav=[no]prop,
                      webdav=lock=timeout=
22-APR-2006  MGD  SET proxy=[no]affinity
26-JAN-2006  MGD  increase MapUrl__Map() WildBuffer[] storage to 4096
26-OCT-2005  MGD  bugfix; MapUrl__Map() SCRIPT result copy not checking
                  for null resulting in occasional overflow error status
10-SEP-2005  JPP  SET proxy=reverse=[no]auth
01-JUN-2005  MGD  SET script=symbol=[no]truncate
21-MAY-2005  MGD  SET throttle=<integer>/<integer> per-user throttle
08-JAN-2005  MGD  SET script=control=<...>
16-DEC-2004  MGD  SET response=[no]chunked
04-NOV-2004  MGD  SET script=syntax=[no]unix
16-OCT-2004  MGD  SET response=gzip=<...>,
                  SET script=body=[no]decode,
                  allow a throttle of zero to be set,
                  throttle=none to reset previously mapped throttle,
                  unbundle some functionality into MAPCON.C, MAPODS.C and
                  MAPUSER.C modules (been wanting to do this for ages)
24-SEP-2004  MGD  bugfix; auth=revalidate= is minutes not seconds
31-AUG-2004  MGD  SET report=tunnel
12-AUG-2004  MGD  bugfix; parse timeout=n,n,n allowing to specify "none"
20-JUL-2004  MGD  SET timeout=keepalive= superceded by timeout=persistent=
20-APR-2004  MGD  actual on-disk structure for each PASS result (ODS-2 or
                  ODS-5) is applied to a path unless otherwise SET with ODS=,
                  don't bother translating a PASS unless it looks file-system
                  (consumes a few less cycles for things like proxy)
08-MAR-2004  MGD  MapUrl_Map() increase wildcard buffer space
26-FEB-2004  MGD  SET script=default=<directory>
13-JAN-2004  MGD  review string SETings for empty string behaviour
10-JAN-2004  MGD  SET ssi=exec=<string>
08-JAN-2004  MGD  SET response=header=[no]add[="<string>"]
16-DEC-2003  MGD  mapping now URL-encodes a redirect wildcard path portions
20-NOV-2003  MGD  SET proxy=reverse=location, proxy=reverse=verify
28-SEP-2003  MGD  when do a callout path mapping use a 'throw-away' path
                  SETing structure to prevent modification of request itself,
                  SET path=set=[no]ignore, 
                      path=set=[no]request 
                  prevent ODS-2 directory paths from containing single periods
01-SEP-2003  MGD  SET map=root=<string> (groan %^p)
                  'DR' conditional (parallels metacon "document-root:")
07-JUL-2003  MGD  SET script=command=<string>,
                  SET cache=[no]cgi, cache=expires=.., cache=[no]file,
                      cache=[no]net, cache=maxkbytes=.., cache=[no]nph,
                      cache=[no]script, cache=[no]ssi,
                      response=header=[append|full|none]
                  bugfix; do not allow SET mapping during a callout
27-JUN-2003  MGD  bugfix; request Html.. memory allocation (jpp@esme.fr)
24-MAY-2003  MGD  SET cache=[no]permanent, cache=max=<integer>
09-MAY-2003  MGD  regular expression support,
                  SET notepad=[+]<string> (associated 'NO' conditional),
                  SET map=restart,
                  SET proxy=unknown
                  'RQ' conditional (parallels metacon "request:")
22-APR-2003  MGD  improve SET rule error reporting (if no rule or no template)
02-APR-2003  MGD  SET proxy=xforwardedfor=[disabled|enabled|address|unknown]
                  SET proxy=forwarded=[disabled|by|for|address]
                  added 'XF' conditional, removed 'UD' (meaningless)
26-MAR-2003  MGD  SET alert=nnn (were 'nnn' can be 500, 599, 403, etc.)
08-MAR-2003  MGD  SET html=[bodytag|header|headertag|footer|footertag]=[..],
                  dir=style[=default|original|anchor|htdir]
15-FEB-2002  MGD  SET [no]search=none,
                  script=params=+(name=value) concatenates to any existing
17-JAN-2003  MGD  general (non-RTE) run-time allowed with (!..) syntax,
                  both run-time specifications allowed with SCRIPT rule
                  SET cgiplusin=[none|cr|lf|crlf],
                  SET cgiplusin=eof,
                  SET script=query=none,
                  SET script=path=find
10-JAN-2003  MGD  SET script=query=relaxed
16-NOV-2002  MGD  SET auth=all (all access must be authorized or fail)
06-NOV-2002  MGD  added 'MP', 'RC' and 'ST' conditionals
12-OCT-2002  MGD  refine metacon reporting,
                  refine mapping rule processing to ensure that paths with
                  forbidden syntax (e.g. multiple '/') generate RMS bad syntax
05-OCT-2002  MGD  'positional' wildcards,
                  SET query-string=,
                  provide 'rqptr->MetaConPass', 
                  confine script=as= to first pass (script resolution)
                  added 'PA' conditional
24-SEP-2002  MGD  added 'PI' and 'RU' conditionals
16-SEP-2002  MGD  MapOdsElementsToVms() excise parent directory syntax,
                  SET path [no]map=ellipsis (off by default),
                  SET report=4nn=nnn for mapping HTTP status
14-SEP-2002  MGD  only use MapUrl_VmsUserName() path ODS if not already set,
                  SET dir=charset= directory listing charset mapping rule
10-SEP-2002  MGD  add en/decoding for Advanced Server (Samba) file names
15-AUG-2002  MGD  SET alert=map, alert=auth, alert=end variants for ALERT,
                  SET ods=2, ods=5, now ods=pwk and ods=sri for below,
                  rework ..UrlToVms() and ..VmsToUrl() to allow for mapping
                  to and from SRI encoding (NFS) and PATHWORKS encoding
                  (and VMS to URL functions no longer explicitly URL-encode)
07-AUG-2002  MGD  bugfix; 'script' and 'exec' MetaConParseReset() state
04-AUG-2002  MGD  allow for 'pass /* 400' (i.e. no trailing message),
                  bugfix; template/result wildcard checking for scripting rules
31-MAY-2001  MGD  SET dir=access, dir=noaccess, dir=access=selective,
                  dir=impliedwildcard, dir=noimpliedwildcard,
                  dir=wildcard, dir=nowildcard
13-MAY-2002  MGD  SET auth=SYSUAF=pwdexpURL=
07-APR-2002  MGD  exchange DEVICE:[DIRECTORY]FILE.TYPE for the hopefully
                  more informative NO:[REVERSE.MAPPING.FOR.THIS]FILE.PATH
14-MAR-2002  MGD  bugfix; throttle report (jf.pieronne@laposte.net)
14-JAN-2002  MGD  added '**' and '*!' wildcard match and discard to template,
                  eliminate device:[.directory] (multiple consecutive '/'),
                  bugfix; arrghhh - wildcard substitution in MapUrl__Map()
18-NOV-2001  MGD  path SETings may now trail any rule with a result string,
                  and are applied to the matched path before mapping occurs,
                  SET http=accept-charset=, accept=lang
28-OCT-2001  MGD  bugfix; DECnet user script mapping
13-OCT-2001  MGD  ensure a conditional is not mistaken for a missing template,
                  script=params=(name=value[,name="value1 value2"]),
                  proxy=bind=IP-address, proxy=chain=host:port, alert,
                  auth=revalidate=hh:mm:ss,
                  bugfix; wildcard substitution in MapUrl__Map()
25-AUG-2001  MGD  meta-config (even workin' on my birthday, sigh)
04-AUG-2001  MGD  support module WATCHing
15-MAY-2001  MGD  bugfix; remove 'RU' conditional (mapping before auth!)
10-MAY-2001  MGD  modify throttle parameter meanings,
                  set rule SCRIPT=CPU= for limiting script process CPU
13-APR-2001  MGD  add queue length to THROTTLE=n[,n,n,hh:mm:ss,hh:mm:ss]
                  add SCRIPT=BIT-BUCKET=hh:mm:ss,
                  bugfix; conditions for NetThisVirtualService() call
05-APR-2001  MGD  bugfix; 'HM' network mask processing
28-MAR-2001  MGD  EXEC of file type
13-MAR-2001  MGD  set rules THROTTLE=n[,n,n,n] & TIMEOUT=n,n,n
                  "hh:mm:ss" for providing a more versatile period
28-FEB-2001  MGD  OdsLoadTextFile(), OdsParseTextFile(), [IncludeFile]
22-DEC-2000  MGD  use of backslash to escape non-wildcard reserved characters
                  in rule and conditional strings (i.e. \] for ])
19-NOV-2000  MGD  bugfix; mapping '/'
10-NOV-2000  MGD  bugfix; MapOdsUrlToVms() final ';'
01-OCT-2000  MGD  set rule SCRIPT=AS=,
                  rework mapping code somewhat,
                  MapUrl_VmsUserNameCache() on reset free entries
31-AUG-2000  MGD  bugfix; MapOdsUrlToVms() escaped version semi-colon
24-JUN-2000  MGD  add network-mask to conditionals,
                  persistent run-time environment in EXEC rule (...),
                  change 'HH' to 'VS' (virtual service) backward compatible
06-APR-2000  MGD  set rules SSLCGI= and MAP=
04-MAR-2000  MGD  use FaolToNet(), et.al.
02-JAN-2000  MGD  config file opened via ODS module,
                  detection of a path's on-disk-structure (ODS-2 or ODS-5),
                  modify URL->VMS and VMS->URL mapping for extended syntax,
                  avoid confusion; redirect indicators changed from '^' and
                  '@' for local and remote, to '<' and '>' respectively,
                  bugfix; trailing wildcard in set rule comparison
24-OCT-1999  MGD  groan; ensure storage does not overflow!!
                  (plus copious quantities of self-flagellation),
                  SYSUAF-based user directory mapping via a "USER" rule,
                  CGIprefix, [no]profile and [no]authonce SET rules,
                  happy seventh wedding anniversary
12-SEP-1999  MGD  virtual services modifications
10-MAY-1999  MGD  conditional mapping on HTTP cookie and referer,
                  increase the size of scratch storage,
                  mapping endless-loop detection
16-JAN-1999  MGD  allow for proxy service mappings by removing requirement
                  for template and result paths to be absolute ("/...") paths
                  (every time I have to deal with this module I groan)
10-JAN-1999  MGD  bugfix; pass rules not returning mapped path
17-OCT-1998  MGD  SET mapping rule,
                  virtual services via "[[virtual-host:virtual-port]]",
                  "qs:" query string conditional,
                  invalid RMS file name/character substitution configurable
12-AUG-1998  MGD  MapOdsVmsToUrl() now only optionally absorbs MFD
19-JUL-1998  MGD  'E'xec DECnet-based user scripts
10-MAR-1998  MGD  allow for local redirects (using '^' detect character)
07-FEB-1998  MGD  added "sc:" scheme/protocol conditional for SSL support
20-DEC-1997  MGD  added DECnet support in _UrlToVms(),
                  result paths can now begin with an '*'
25-OCT-1997  MGD  added "as:", "fo:", and "hh:" conditionals,
                  bugfix; MsgFor() required request pointer to find langauge
15-SEP-1997  MGD  MsgFor() was not returning a leading-null message (does now),
                  HTTP status code rules (e.g. "pass /no/* 403 forbidden"),
                  removed two consecutive slashes inhibiting rule mapping
07-SEP-1997  MGD  added "sn:" and "sp:" conditionals
09-AUG-1997  MGD  v4.3, message database, mapping "conditionals",
                  more cart-wheels, hand-stands and contortions (see 01-OCT-96)
05-JUN-1997  MGD  v4.2, CGIplus "exec+" and "script+" rules
01-FEB-1997  MGD  HTTPd version 4, major changes for form-based config
01-OCT-1996  MGD  not proud of how I shoe-horned the reporting features in,
                  good example of slop-down or object-disoriented programming,
                  :^( will rework the whole thing one day, sigh!
                  bugfix; requiring use of 'TheFirstRuleWillBeTheNextRule'
06-APR-1996  MGD  modified conversion of non-VMS characters in MapOdsUrlToVms()
15-FEB-1995  MGD  bugfix; redirect rule
01-DEC-1995  MGD  v3.0
24-MAR-1995  MGD  bugfix; end-of-string sometimes not detected when matching
20-DEC-1994  MGD  revised for multi-threaded HTTP daemon
20-JUN-1994  MGD  single-threaded daemon
*/
#endif /* COMMENTS_WITH_COMMENTS */
/*****************************************************************************/

#ifdef WASD_VMS_V7
#undef _VMS__V6__SOURCE
#define _VMS__V6__SOURCE
#undef __VMS_VER
#undef __VMS_VER
#undef __CRTL_VER
#define __CRTL_VER 70000000
#endif

/* standard C header files */
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

/* VMS related header files */
#include <devdef.h>
#include <dvidef.h>
#include <prvdef.h>
#include <ssdef.h>
#include <stsdef.h>
#include <uaidef.h>

#include "wasd.h"

#define WASD_MODULE "MAPURL"

/* fundamental buffer size */
#ifdef __VAX
#define MAP_BUFFER_SIZE 8192
#else
#ifdef __ALPHA
#define MAP_BUFFER_SIZE (8192 * 2)
#else
/* IA64 and whatever else may follow */
#define MAP_BUFFER_SIZE (8192 * 4)
#endif
#endif

/**********/
/* macros */
/**********/

#define MAPURL_RMS_SUBSTITUTION_DEFAULT '$'

#define WATCH_MODULE_DETAIL (WATCH_MODULE(WATCH_MOD_MAPURL) && \
                             WATCH_MODULE(WATCH_MOD__DETAIL))

/******************/
/* global storage */
/******************/

BOOL  MapUrlExtensionMethod,
      MapUrlPathOds5;

MAPPING_META  MappingMeta;
MAPPING_META  *MappingMetaPtr;

/********************/
/* external storage */
/********************/

extern BOOL  AuthPromiscuous,
             AuthProtectRule,
             AuthPolicySysUafRelaxed,
             CliOdsExtendedDisabled,
             CliOdsExtendedEnabled,
             OdsExtended;

extern int  EfnWait,
            ServerPort,
            ServerHostNameLength;

extern unsigned long  SysPrvMask[];

extern char  *FaoUrlEncodeTable[];

extern int  ToLowerCase[],
            ToUpperCase[];

extern char  ErrorSanityCheck[],
             ErrorWatchSysFao[],
             ServerHostName[],
             ServerHostPort[],
             SoftwareID[];

extern LIST_HEAD  MapUrlUserNameCacheList;
extern int  MapUrlUserNameCacheEntries;

extern CONFIG_STRUCT  Config;
extern HTTPD_GBLSEC  *HttpdGblSecPtr;
extern IPADDRESS  TcpIpEmptyAddress;
extern META_CONFIG  *MetaGlobalMappingPtr;
extern MSG_STRUCT  Msgs;
extern SYS_INFO  SysInfo;
extern WATCH_STRUCT  Watch;

/*****************************************************************************/
/*
This overly-long function (sorry) is not AST reentrant, but when executed 
within the context of the HTTPd, is executed either without the possibility of 
AST interruption, or during AST processing and so provides atomic 
functionality.  Pointers to any strings returned must be used immediately 
(before leaving AST context), or copied into storage maintained by the calling 
routine. 

Maps from URL format to VMS file specification, and REVERSE-MAPS from VMS file 
specification to URL format.  Maps scripts.

Always returns a pointer to char.  An error message is detected by being  
returned as a pointer to a string beginning with a null-character!  The 
message begins from character one. 

Call with 'PathPtr' pointing at a string containing the URL path and 'VmsPtr' 
pointing to the storage to contain the mapped VMS equivalent.

Call with 'VmsPtr' pointing to a VMS file specification (dev:[dir]file.ext) 
and 'PathPtr' pointing to an empty string, used as storage to contain the 
REVERSE-MAPPED file specification.

Call with 'PathPtr' pointing at a string containing the URL path, 'VmsPtr' 
pointing to storage to contain the mapped VMS equivalent, 'ScriptPtr' pointing 
to storage for the URL format script path and 'ScriptVmsPtr' pointing to 
storage to contain the VMS specification of the script procedure/image.

The MapUrl_Map() just wraps MapUrl__Map() so that it's function can be WATCHed.

See alter-egos defined in MapUrl.h
*/ 

char* MapUrl_Map
(
char *PathPtr,
int SizeOfPathPtr,
char *VmsPtr,
int SizeOfVmsPtr,
char *ScriptPtr,
int SizeOfScriptPtr,
char *ScriptVmsPtr,
int SizeOfScriptVmsPtr,
char *RunTimePtr,
int SizeOfRunTimePtr,
int *PathOdsPtr,
REQUEST_STRUCT *rqptr,
REQUEST_PATHSET *PathSetPtr
)
{
   BOOL  VmsToPath;
   char  *cptr, *sptr;
   char  OdsNote [32];

   /*********/
   /* begin */
   /*********/

   if (!rqptr || !WATCHPNT(rqptr))
   {
      cptr = MapUrl__Map (PathPtr, SizeOfPathPtr,
                          VmsPtr, SizeOfVmsPtr,
                          ScriptPtr, SizeOfScriptPtr,
                          ScriptVmsPtr, SizeOfScriptVmsPtr,
                          RunTimePtr, SizeOfRunTimePtr,
                          PathOdsPtr, rqptr, PathSetPtr);
      if (rqptr)
      {
         rqptr->MetaConPass = 0;
         rqptr->MetaConMappedPtr = rqptr->MetaConScriptPtr = NULL;
      }
      return (cptr);
   }

   if (!WATCH_CATEGORY(WATCH_MAPPING))
   {
      cptr = MapUrl__Map (PathPtr, SizeOfPathPtr,
                          VmsPtr, SizeOfVmsPtr,
                          ScriptPtr, SizeOfScriptPtr,
                          ScriptVmsPtr, SizeOfScriptVmsPtr,
                          RunTimePtr, SizeOfRunTimePtr,
                          PathOdsPtr, rqptr, PathSetPtr);
      if (rqptr)
      {
         rqptr->MetaConPass = 0;
         rqptr->MetaConMappedPtr = rqptr->MetaConScriptPtr = NULL;
      }
      return (cptr);
   }

#if WATCH_CAT

   VmsToPath = false;
   if (PathPtr && PathPtr[0])
      WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "PATH !AZ", PathPtr); 
   else
   if (VmsPtr && VmsPtr[0])
   {
      VmsToPath = true;
      WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "VMS !AZ", VmsPtr); 
   }

   cptr = MapUrl__Map (PathPtr, SizeOfPathPtr,
                       VmsPtr, SizeOfVmsPtr, 
                       ScriptPtr, SizeOfScriptPtr,
                       ScriptVmsPtr, SizeOfScriptVmsPtr,
                       RunTimePtr, SizeOfRunTimePtr,
                       PathOdsPtr, rqptr, PathSetPtr);

   if (VmsToPath)
      WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "RESULT !AZ", cptr);
   else
   {
      if (!PathPtr) PathPtr = "";
      if (!VmsPtr) VmsPtr = "";
      if (!ScriptPtr) ScriptPtr = "";
      if (!ScriptVmsPtr) ScriptVmsPtr = "";
      if (!RunTimePtr) RunTimePtr = "";

      WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "RESULT");
      if (!cptr[0] && cptr[1])
         WatchDataFormatted ("Error: !AZ\n", cptr+1);
      else
      if (cptr[0] == '\1')
         WatchDataFormatted ("Redirect: !AZ\n", cptr+1);
      else
      {
         switch (PathOdsPtr ? *PathOdsPtr : rqptr->PathOds)
         {
            case MAPURL_PATH_ODS_2 : sptr = " (ODS-2)"; break;
            case MAPURL_PATH_ODS_5 : sptr = " (ODS-5)"; break;
            case MAPURL_PATH_ODS_ADS : sptr = " (ADS)"; break;
            case MAPURL_PATH_ODS_PWK : sptr = " (PWK)"; break;
            case MAPURL_PATH_ODS_SMB : sptr = " (SMB)"; break;
            case MAPURL_PATH_ODS_SRI : sptr = " (SRI)"; break;
            default : sptr = " (ods-2)";
         }
         WatchDataFormatted ("\
     Mapped: !AZ\n\
 Translated: !AZ!AZ\n\
     Script: !AZ\n\
Script-File: !AZ\n\
   Run-Time: !AZ\n",
           cptr, VmsPtr, VmsPtr[0] ? sptr : "",
           ScriptPtr, ScriptVmsPtr, RunTimePtr);
      }
   }

   if (rqptr)
   {
      rqptr->MetaConPass = 0;
      rqptr->MetaConMappedPtr = rqptr->MetaConScriptPtr = NULL;
   }
   return (cptr);

#endif /* WATCH_CAT */
}

char* MapUrl__Map
(
char *PathPtr,
int SizeOfPathPtr,
char *VmsPtr,
int SizeOfVmsPtr,
char *ScriptPtr,
int SizeOfScriptPtr,
char *ScriptVmsPtr,
int SizeOfScriptVmsPtr,
char *RunTimePtr,
int SizeOfRunTimePtr,
int *PathOdsPtr,
REQUEST_STRUCT *rqptr,
REQUEST_PATHSET *PathSetPtr
)
{
   static char  DerivedPathBuffer [MAP_BUFFER_SIZE+128],
                PathBuffer [MAP_BUFFER_SIZE+128],
                VmsBuffer [MAP_BUFFER_SIZE+128];

   BOOL  EvanescentZeroed,
         ExecFileType,
         LoadingRules,
         MapEllipsis,
         MapPathToVms,
         StatusCodeMapping,
         SubstituteForUserName;
   int  idx, widx, status,
        MapRootLength,
        MetaConPass,
        MetaConRestartCount,
        PathOds,
        SetPathIgnore,
        TotalLength,
        WatchThisMatch,
        WatchThisOne,
        WildStringCount;
   unsigned short  Length;
   char  ch,
         RmsSubChar;
   char  *cptr, *eptr, *pptr, *rptr, *sptr, *tptr, *uptr, *zptr,
         *MapRootPtr;
   char  *WildString [REGEX_PMATCH_MAX];
   char  Scratch [MAP_BUFFER_SIZE+128],
         UserDefault [256],
         VmsUserMappedBuffer [256],
         WildBuffer [(MAP_BUFFER_SIZE*4)+128];
   MAP_RULE_META  *mrptr;
   MAP_SET_META  *mrpsptr;
   METACON_LINE  *mclptr;
   REQUEST_PATHSET  EvanescentPathSet;
   REQUEST_PATHSET  *rqpsptr;
   regex_t  *pregptr;
   regmatch_t  pmatch [REGEX_PMATCH_MAX];

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_MAPURL))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL,
                 "MapUrl_Map() !UL !&Z !UL !&Z !UL !&Z !UL",
                 SizeOfPathPtr, PathPtr, SizeOfVmsPtr, VmsPtr,
                 SizeOfScriptPtr, ScriptPtr, SizeOfScriptVmsPtr);

   /* if just loading the rules then free any previous set */
   if (LoadingRules = (!PathPtr && !VmsPtr && !ScriptPtr))
      MetaConUnload (&MetaGlobalMappingPtr, &MapUrl_ConfigUnloadLineData);

   if (!MetaGlobalMappingPtr)
   {
      /*********************/
      /* read mapping file */
      /*********************/

      MapUrl_ConfigLoad (&MetaGlobalMappingPtr);
   }

   /* if just (re)loading rules then return now */
   if (LoadingRules) return ("\0\0");

   /*****************************************/
   /* map from the URL or VMS to VMS or URL */
   /*****************************************/

   if (!PathPtr)
   {
      SET2(PathBuffer,'\0\0');
      PathPtr = PathBuffer + 1;
      SizeOfPathPtr = sizeof(PathBuffer);
   }
   if (!VmsPtr)
   {
      *(VmsPtr = VmsBuffer) = '\0';
      SizeOfVmsPtr = sizeof(VmsBuffer);
   }
   /* making 'ScriptPtr' non-empty means "exec" and "script" are ignored */
   if (!ScriptPtr) ScriptPtr = "?";

   if (PathPtr[0])
   {
      /* if the URL is not an empty string then force the conversion to VMS */
      MapPathToVms = true;
      *VmsPtr = '\0';
      MetaConPass = 1;
   }
   else
   {
      /* URL was an empty string, convert from VMS to URL */
      MapPathToVms = false;
      /* generate a URL-style version of the VMS specification */
      MapOdsVmsToUrl (PathPtr, VmsPtr, SizeOfPathPtr, true, rqptr->PathOds);
      MetaConPass = -1;
   }

   /***********************/
   /* loop thru the rules */
   /***********************/

   if (!MetaGlobalMappingPtr)
      return (MsgFor(rqptr,MSG_MAPPING_DENIED_NO_RULES)-1);

   WatchThisOne = WatchThisMatch = 0;
   if (WATCHPNT(rqptr))
   {
      if (WATCH_CATEGORY(WATCH_MAPPING)) WatchThisOne = WATCH_MAPPING;
      if (WATCH_CATEGORY(WATCH_MATCH)) WatchThisMatch = WATCH_MATCH;
   }

   MapRootLength = PathOds = 0;
   RmsSubChar = MAPURL_RMS_SUBSTITUTION_DEFAULT;
   EvanescentZeroed = MapEllipsis = SetPathIgnore = false;
   MapRootPtr = NULL;
   MetaConRestartCount = 0;

   if (rqptr)
   {
      rqptr->MetaConPass = MetaConPass;
      rqptr->MetaConRestartCount = MetaConRestartCount;
      rqptr->MetaConMappedPtr = rqptr->MetaConScriptPtr = NULL;
   }

   if (!PathSetPtr)
   {
      PathSetPtr = &EvanescentPathSet;
      memset (&EvanescentPathSet, 0, sizeof(EvanescentPathSet));
      EvanescentZeroed = true;
   }

   rqpsptr = PathSetPtr;

   MetaConParseReset (MetaGlobalMappingPtr, true);
   for (;;)
   {
      mclptr = MetaGlobalMappingPtr->ParsePtr =
         MetaGlobalMappingPtr->ParseNextPtr;

      if (WATCH_MODULE_DETAIL)
         WatchDataFormatted ("!&X !UL !UL !UL !UL !&X !&Z\n",
            mclptr, mclptr->Size, mclptr->Token, mclptr->Number,
            mclptr->Length, mclptr->LineDataPtr, mclptr->TextPtr);

      /* if terminating empty "line" */
      if (!mclptr->Size) break;
      if (mclptr->Token == METACON_TOKEN_TEXT)
      {
         /* get required data, adjust the parse context to the next "line" */
         cptr = mclptr->TextPtr;
         mrptr = mclptr->LineDataPtr;
         MetaGlobalMappingPtr->ParseNextPtr =
            (METACON_LINE*)((char*)mclptr + mclptr->Size);
      }
      else
      if (mclptr->Token != METACON_TOKEN_DIRECTORY)
      {
         /* not a simple text line, have meta-config parse this one for us */
         cptr = MetaConParse (rqptr, MetaGlobalMappingPtr, &mclptr,
                              WatchThisOne);
         /* if the end of rules */
         if (!cptr) break;

         /* if error string */
         if (!*cptr && *(cptr+1)) return (cptr);
         /* if inline rule and expression was false */
         if (SAME4 (cptr, '\0\0\0\1')) continue;
         mrptr = mclptr->LineDataPtr;
      }

      if (mclptr->Token == METACON_TOKEN_DIRECTORY)
      {
         /* get required data, adjust the parse context to the next "line" */
         cptr = mclptr->TextPtr + sizeof("[ConfigDirectory]");
         while (*cptr && ISLWS(*cptr)) cptr++;
         zptr = (sptr = rqptr->ConfigDirectory) +
                sizeof(rqptr->ConfigDirectory)-1;
         while (*cptr && sptr < zptr) *sptr++ = *cptr++;
         *sptr = '\0';
         rqptr->ConfigDirectoryLength = sptr - rqptr->ConfigDirectory;
         MetaGlobalMappingPtr->ParseNextPtr =
            (METACON_LINE*)((char*)mclptr + mclptr->Size);
         continue;
      }

      /* there was a problem in initializing the rule */
      if (!mrptr)
      {
         if (WatchThisOne) WatchDataFormatted ("?!AZ\n", cptr);
         continue;
      }

      if (WATCH_MODULE_DETAIL)
         WatchDataFormatted ("!&X !UL !&Z !&Z !&Z\n", 
            mrptr, mrptr->RuleType, mrptr->TemplatePtr,
            mrptr->ResultPtr, mrptr->ConditionalPtr);

      /* hmmm, endless loop!! */
      if (MetaConPass > 2)
         return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);

      if (rqptr->rqPathSet.MapUri)
      {
         /* substitute the request URI (raw path plus query) for rewriting */
         rqptr->rqPathSet.MapUri = false;
         zptr = (pptr = PathPtr = PathBuffer+1) + sizeof(PathBuffer)-1;
         for (cptr = rqptr->rqHeader.RequestUriPtr;
              *cptr && pptr < zptr;
              *pptr++ = *cptr++);
         if (pptr >= zptr)
            return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
         *pptr = '\0';
      }

      /* if 'ScriptPtr' is not to be used then ignore "exec" and "script" */
      if (ScriptPtr[0] &&
          (mrptr->RuleType == MAPURL_RULE_EXEC ||
           mrptr->RuleType == MAPURL_RULE_SCRIPT ||
           mrptr->RuleType == MAPURL_RULE_UXEC))
      {
         if (WatchThisOne)
            MapUrl_WatchRule (rqptr, mrptr, PathPtr, PathOds,
                              MAPURL_REPORT_MATCH_NOT, SetPathIgnore);
         continue;
      }

      if (cptr = MapUrl_GuaranteeAccess (rqptr, PathPtr))
         return (cptr);

      /*****************************************************************/
      /* compare the URL with the template/result in the mapping entry */
      /*****************************************************************/

      if (mrptr->RuleType == MAPURL_RULE_SET ||
          mrptr->RuleType == MAPURL_RULE_PROTECT)
      {
         /*********************/
         /* SET/PROTECT rules */
         /*********************/

         /* cheap check obvious non-/match before using expensive functions */
         tptr = mrptr->TemplatePtr;
         pregptr = &mrptr->RegexPregTemplate;
         pptr = PathPtr;
         if (Config.cfMisc.RegexSyntax && *tptr == REGEX_CHAR) tptr++;
         /* while matching vanilla characters */
         while ((isalnum(*tptr) || *tptr == '/' ||
                 *tptr == '-' || *tptr == '_') &&
                TOLO(*pptr) == TOLO(*tptr))
         {
            pptr++;
            tptr++;
         }
         /* if non-matching vanilla character */
         if (isalnum(*tptr) || *tptr == '/' || *tptr == '-' || *tptr == '_')
         {
            if (WatchThisMatch)
               WatchThis (WATCHITM(rqptr), WATCH_MATCH, "NO !&Z", tptr);
            if (WatchThisOne)
               MapUrl_WatchRule (rqptr, mrptr, PathPtr, PathOds,
                                 MAPURL_REPORT_MATCH_NOT, SetPathIgnore);
            continue;
         }

         /* if a trailing wildcard then it's a match already! */
         if (SAME2(tptr,'*\0'))
         {
            if (WatchThisMatch)
               WatchThis (WATCHITM(rqptr), WATCH_MATCH, "YES");
         }
         else
         {
            /* expensive comparison of path and template */
            if (!StringMatchAndRegex (rqptr, PathPtr, mrptr->TemplatePtr,
                                      SMATCH_STRING_REGEX, pregptr, NULL))
            {
               /* not matched then straight to next rule */
               if (!WatchThisOne) continue;
               MapUrl_WatchRule (rqptr, mrptr, PathPtr, PathOds,
                                 MAPURL_REPORT_MATCH_NOT, SetPathIgnore);
               continue;
            }
         }

         if (WATCH_MODULE(WATCH_MOD_MAPURL))
            WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MATCHED");
      }
      else
      {
         /***************/
         /* other rules */
         /***************/

         /* when mapping VMS to URL then ignore all BUT "pass" & "set" rules */
         if (!MapPathToVms && mrptr->RuleType != MAPURL_RULE_PASS) continue;

         /* if reverse-mapping VMS to URL use the result if available */
         if (!MapPathToVms && mrptr->ResultPtr[0])
         {
            if (MapRootPtr)
            {
               /* if a mapping root is set then eliminate this first */
               cptr = MapRootPtr;
               pptr = PathPtr;
               while (*cptr && *pptr && TOLO(*cptr) == TOLO(*pptr))
               {
                  cptr++;
                  pptr++;
               }
               if (*cptr || !*pptr)
               {
                  /* the path and root do not match */
                  if (!WatchThisMatch) continue;
                  WatchThis (WATCHITM(rqptr), WATCH_MATCH, "NO (root) !&Z", cptr);
                  continue;
                }
            }
            cptr = tptr = mrptr->ResultPtr;
            pregptr = NULL;
         }
         else
         {
            cptr = tptr = mrptr->TemplatePtr;
            pregptr = &mrptr->RegexPregTemplate;
         }

         /* cheap check obvious non-/match before using expensive functions */
         if (Config.cfMisc.RegexSyntax && *tptr == REGEX_CHAR) tptr++;
         pptr = PathPtr;
         /* while matching vanilla characters */
         while ((isalnum(*tptr) || *tptr == '/' ||
                 *tptr == '-' || *tptr == '_') &&
                TOLO(*pptr) == TOLO(*tptr))
         {
            pptr++;
            tptr++;
         }
         /* if non-matching vanilla character */
         if (isalnum(*tptr) || *tptr == '/' || *tptr == '-' || *tptr == '_')
         {
            if (WatchThisMatch)
               WatchThis (WATCHITM(rqptr), WATCH_MATCH, "NO !&Z", tptr);
            if (WatchThisOne)
               MapUrl_WatchRule (rqptr, mrptr, PathPtr, PathOds,
                                 MAPURL_REPORT_MATCH_NOT, SetPathIgnore);
            continue;
         }

         /* if a trailing wildcard then it's a match already! */
         if (SAME2(tptr,'*\0'))
         {
            if (WatchThisMatch)
               WatchThis (WATCHITM(rqptr), WATCH_MATCH, "YES");
            /* simulate the setting of the 'pmatch' data */
            pmatch[0].rm_so = 0;
            pmatch[1].rm_so = pptr - PathPtr;
            while (*pptr) pptr++;
            pmatch[0].rm_eo = pmatch[1].rm_eo = pptr - PathPtr;
            for (idx = 2; idx < REGEX_PMATCH_MAX; idx++)
                pmatch[idx].rm_so = pmatch[idx].rm_eo = -1;
         }
         else
         {
            /* expensive comparison of path and template */
            if (!StringMatchAndRegex (rqptr, PathPtr, cptr,
                                      SMATCH_STRING_REGEX, pregptr, &pmatch))
            {
               /* not matched then straight to next rule */
               if (!WatchThisOne) continue;
               MapUrl_WatchRule (rqptr, mrptr, PathPtr, PathOds,
                                 MAPURL_REPORT_MATCH_NOT, SetPathIgnore);
               continue;
            }
         }

         /************/
         /* matched! */
         /************/

         /* copy using 'regmatch_t' data, strings into 'WildBuffer' */
         zptr = (sptr = WildBuffer) + sizeof(WildBuffer);
         for (idx = WildStringCount = 0; idx < REGEX_PMATCH_MAX; idx++)
         {
            WildString[idx] = "";
            if (pmatch[idx].rm_so == -1 || pmatch[idx].rm_eo == -1) continue;
            WildString[WildStringCount++] = sptr;
            pptr = PathPtr + pmatch[idx].rm_so;
            cptr = PathPtr + pmatch[idx].rm_eo;
            while (pptr < cptr && sptr < zptr) *sptr++ = *pptr++;
            if (sptr >= zptr) break;
            *sptr++ = '\0';
         }
         if (sptr >= zptr) 
            return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
         /* the actual number of wildcard/group matched strings */
         if (WildStringCount) WildStringCount--;

         if (WATCH_MODULE(WATCH_MOD_MAPURL))
         {
            WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MATCHED");
            StringWatchPmatch (PathPtr, &pmatch);
            WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "WILDSTRING");
            for (idx = 1; idx <= WildStringCount; idx++)
               WatchDataFormatted ("!UL. !&Z\n", idx, WildString[idx]);
         }
      }

      if (mrptr->ConditionalPtr[0])
      {
         /***************/
         /* conditional */
         /***************/

         /* when not mapping using a request then ignore conditionals */
         if (rqptr)
         {
            /* if condition not met then continue */
            if (!MapConString ((REQUEST_STRUCT*)rqptr, mrptr, rqpsptr))
            {
               if (WatchThisOne)
                  MapUrl_WatchRule (rqptr, mrptr, PathPtr, PathOds,
                                    MAPURL_REPORT_MATCH_RULE, SetPathIgnore);
               continue;
            }

            /* report rule and conditional matched */
            if (WatchThisOne)
               MapUrl_WatchRule (rqptr, mrptr, PathPtr, PathOds,
                                 MAPURL_REPORT_MATCH_RULECOND, SetPathIgnore);
         }

         if (WATCH_MODULE_DETAIL)
            WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "CONDITIONAL MATCHED");
      }
      else
      {
         /* report rule, there was no conditional */
         if (WatchThisOne)
            MapUrl_WatchRule (rqptr, mrptr, PathPtr, PathOds,
                              MAPURL_REPORT_MATCH_RULENOCOND, SetPathIgnore);
      }

      if (mrptr->PathSet)
      {
         /****************/
         /* path SETings */
         /****************/

         mrpsptr = &mrptr->mpPathSet;

         if (mrpsptr->MapSetIgnore)
         {
            /* takes effect immediately */
            SetPathIgnore = true;
            continue;
         }
         else
         if (mrpsptr->MapSetNoIgnore)
            SetPathIgnore = false;
 
         /* ignoring path SETings (except for the above of course ;^) */
         if (SetPathIgnore) continue;

         /***********************/
         /* being ignored? - no */
         /***********************/

         if (mrpsptr->MapRestart)
         {
            MetaConRestartCount++;
            if (WatchThisOne)
               WatchThis (WATCHITM(rqptr), WATCH_MAPPING,
                          "RESTART !UL", MetaConRestartCount); 
            if (MetaConRestartCount > MAPURL_RESTART_MAX)
               return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
            if (rqptr) rqptr->MetaConRestartCount = MetaConRestartCount;
            MetaConParseReset (MetaGlobalMappingPtr, true);
            continue;
         }

         /* some SETings must be applied in this function! */

         if (!(rqptr->rqCgi.AgentScript ||
               rqptr->rqCgi.CalloutInProgress))
         {
            if (mrpsptr->MapSetRequest)
               rqpsptr = &PathSetPtr;
            else
            if (mrpsptr->MapSetNoRequest)
            {
               rqpsptr = &EvanescentPathSet;
               if (!EvanescentZeroed)
               {
                  memset (&EvanescentPathSet, 0, sizeof(EvanescentPathSet));
                  EvanescentZeroed = true;
               }
            }
         }

         if (mrpsptr->NoMapEllipsis)
            rqpsptr->MapEllipsis = MapEllipsis = false;
         else
         if (mrpsptr->MapEllipsis)
            rqpsptr->MapEllipsis = MapEllipsis = true;

         if (mrpsptr->MapRootPtr)
         {
            /* empty string resets */
            if (mrpsptr->MapRootLength)
            {
               MapRootPtr = mrpsptr->MapRootPtr;
               MapRootLength = mrpsptr->MapRootLength;
               rqpsptr->MapRootPtr =
                  VmGetHeap (rqptr, mrpsptr->MapRootLength+1);
               memcpy (rqpsptr->MapRootPtr,
                       mrpsptr->MapRootPtr,
                       mrpsptr->MapRootLength+1);
            }
            else
            {
               MapRootPtr = rqpsptr->MapRootPtr = NULL;
               MapRootLength = 0;
            }
         }

         if (mrpsptr->RmsSubChar)
            RmsSubChar = mrpsptr->RmsSubChar;

         if (rqptr->rqCgi.AgentScript)
         {
            /* bypass evanecsent structure by SETing directly into request! */
            if (MetaConPass == 1 &&
                mrpsptr->ScriptAgentAsPtr)
            {
               /* empty string resets */
               if (mrpsptr->ScriptAgentAsLength)
               {
                  rqptr->rqPathSet.ScriptAgentAsPtr =
                     VmGetHeap (rqptr, mrpsptr->ScriptAgentAsLength+1);
                  memcpy (rqptr->rqPathSet.ScriptAgentAsPtr,
                          mrpsptr->ScriptAgentAsPtr,
                          mrpsptr->ScriptAgentAsLength+1);
               }
               else
                  rqptr->rqPathSet.ScriptAgentAsPtr = NULL;
            }
         }

         if (MetaConPass == 1 &&
             mrpsptr->ScriptAsPtr)
         {
            /* empty string resets */
            if (mrpsptr->ScriptAsLength)
            {
               rqpsptr->ScriptAsPtr =
                  VmGetHeap (rqptr, mrpsptr->ScriptAsLength+1);
               memcpy (rqpsptr->ScriptAsPtr,
                       mrpsptr->ScriptAsPtr,
                       mrpsptr->ScriptAsLength+1);
            }
            else
               rqpsptr->ScriptAsPtr = NULL;
         }

         /* others can be applied in an independent function */

         MapUrl_PathSet (rqptr, rqpsptr, mrpsptr);

         /* when mapping VMS to URL then ignore all BUT "pass" rules */
         if (!MapPathToVms && mrptr->RuleType != MAPURL_RULE_PASS) continue;

         /* must be immediately detected and actioned by the calling routine */
         if (rqpsptr->Http2ToHttp11) return ("\0\0");
         
         if (rqpsptr->ClientAddress &&
             rqpsptr == &rqptr->rqPathSet)
         {
            if (rqpsptr->ClientAddress == MAPURL_CLIENT_RESET)
               MapUrl_ResetClientAddress (rqptr);
            else
            if (cptr = MapUrl_SetClientAddress (rqptr))
               return (cptr);
         }
      }

      /***************/
      /* end SETings */
      /***************/

      if (rqpsptr->PathOds)
      {
         /* path file-system has been set, perhaps to turn off ODS-5! */
         if (rqpsptr->PathOds == MAPURL_PATH_ODS_0)
            PathOds = rqptr->PathOds = rqpsptr->PathOds = 0;
         else
            PathOds = rqptr->PathOds = rqpsptr->PathOds; 
      }
      else
      {
         /* set from the $GETDVI volume file-system */
         PathOds = rqptr->PathOds = mrptr->ResultPathOds;
      }
      if (PathOdsPtr) *PathOdsPtr = PathOds;

      if (MapPathToVms)
      {
         /***************************/
         /* mapping from URL to VMS */
         /***************************/

         switch (mrptr->RuleType)
         {
         case MAPURL_RULE_EXEC :
         case MAPURL_RULE_UXEC :

            /*******************/
            /* EXEC/UXEC rule */
            /*******************/

            if (!WildStringCount)
               return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);

            /* first get the script component from the request */
            tptr = mrptr->TemplatePtr;
            zptr = (sptr = ScriptPtr) + SizeOfScriptPtr;
            if (mrptr->RuleType == MAPURL_RULE_UXEC)
            {
               if (!rqpsptr->ScriptAsPtr ||
                   rqpsptr->ScriptAsPtr[0] != '~')
               {
                  /* hmmm, not enabled with a mapping rule!! */
                  return (MsgFor(rqptr,MSG_GENERAL_DISABLED)-1);
               }
               /* first insert the /~username component */
               while (*tptr && *tptr != '*' && sptr < zptr) *sptr++ = *tptr++;
               if (*tptr) tptr++;
               cptr = WildString[1];
               while (*cptr && sptr < zptr) *sptr++ = *cptr++;
               cptr = WildString[2];
            }
            else
            if (SAME2(tptr,'/~'))
            {
               /* mapping a DECnet username based script */
               while (*tptr && *tptr != '*' && sptr < zptr) *sptr++ = *tptr++;
               while (*tptr == '*') tptr++;
               cptr = WildString[1];
               while (*cptr && sptr < zptr) *sptr++ = *cptr++;
               cptr = WildString[2];
            }
            else
            {
               /* mapping a vanilla exec rule */
               cptr = WildString[1];
            }

            while (*tptr && *tptr != '*' && sptr < zptr) *sptr++ = *tptr++;
            while (*tptr && SAME2(tptr,'**')) tptr++;
            if (SAME2(tptr,'*.'))
            {
               /* EXECing a file type here */
               ExecFileType = true;
               while (*cptr && sptr < zptr) *sptr++ = *cptr++;
               /* now get the script file type (e.g. ".cgi") */
               tptr++;
               while (*tptr && *tptr != '*' && sptr < zptr) *sptr++ = *tptr++;
            }
            else
            {
               /* EXECing a directory here */
               ExecFileType = false;
               while (*cptr && *cptr != '/' && sptr < zptr) *sptr++ = *cptr++;
            }

            if (sptr >= zptr)
               return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
            *sptr = '\0';

            if (mrptr->RuleType == MAPURL_RULE_UXEC)
            {
               /* "append" the username to the circumflex */
               rqpsptr->ScriptAsPtr = sptr =
                   VmGetHeap (rqptr, strlen(WildString[1])+2);
               *sptr++ = '~';
               for (cptr = WildString[1]; *cptr; *sptr++ = TOUP(*cptr++));
               *sptr = '\0';
            }

            /* now get what it's mapped into (the more complex bit ;^) */
            rptr = mrptr->ResultPtr;

            if (*rptr == '(')
            {
               /* run-time, e.g. exec /plbin/@ (ht_exe:pl.exe)/plbin/@ */
               rptr++;
               zptr = (sptr = RunTimePtr) + SizeOfRunTimePtr;
               while (*rptr && *rptr != ')' && sptr < zptr) *sptr++ = *rptr++;
               if (sptr >= zptr)
                  return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
               *sptr = '\0';
               if (*rptr) rptr++;
            }

            zptr = (sptr = Scratch) + sizeof(Scratch);

            if (mrptr->RuleType == MAPURL_RULE_UXEC)
            {
               /* begin with the mapped user instead of the /~username */
               if (rqpsptr->PathOds)
                  uptr = MapUserName (rqptr, WildString[1], NULL);
               else
               {
                  uptr = MapUserName (rqptr, WildString[1], &PathOds);
                  rqptr->PathOds = PathOds;
                  if (PathOdsPtr) *PathOdsPtr = PathOds;
               }
               if (!uptr[0]) return (uptr);
               /* copy in the now-mapped username */
               while (*uptr && sptr < zptr) *sptr++ = *uptr++;
               /* skip over the equivalent in the result (i.e. "/~*") */
               while (*rptr && *rptr != '*') rptr++;
               if (*rptr) rptr++;
               /* skip the first wildcarded string (username) */
               cptr = WildString[widx=2];
            }
            else
            if (SAME2(mrptr->TemplatePtr,'/~'))
            {
               /*
                  e.g. exec /~@/cgi-bin/@ /0""::/web/user/@/cgi-bin/@
                  i.e. the user part must be prepended to the script part.
               */
               while (*rptr && *rptr != '*' && sptr < zptr)
               {
                  if (*rptr != '\"' || !SAME4(rptr,'\"\"::'))
                  {
                     *sptr++ = *rptr++;
                     continue;
                  }
                  /* mapping a DECnet username based script */
                  *sptr++ = *rptr++;
                  cptr = WildString[1];
                  while (*cptr && sptr < zptr) *sptr++ = *cptr++;
               }
               while (*rptr == '*') rptr++;
               cptr = WildString[1];
               while (*cptr && sptr < zptr) *sptr++ = *cptr++;
               cptr = WildString[widx=2];
            }
            else
            {
               /* vanilla script exec */
               if (MapRootPtr)
               {
                  if (WATCHING (rqptr, WATCH_MAPPING))
                     WatchThis (WATCHITM(rqptr), WATCH_MAPPING,
                                "ROOT !AZ", MapRootPtr);
                  for (cptr = MapRootPtr;
                       *cptr && sptr < zptr;
                       *sptr++ = *cptr++);
               }
               cptr = WildString[widx=1];
            }

            /* now the leading script-location part of the result */
            while (*rptr && *rptr != '*' && sptr < zptr) *sptr++ = *rptr++;
            while (*rptr == '*') rptr++;

            if (WATCH_MODULE_DETAIL)
               WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "!&Z", cptr);
            if (ExecFileType)
            {
               /* EXECing a file type here, script name (full buffer) */
               while (*cptr && sptr < zptr) *sptr++ = *cptr++;
               cptr = WildString[++widx];
               /* now get the script file type (e.g. ".cgi") */
               while (*rptr && *rptr != '*' && sptr < zptr) *sptr++ = *rptr++;
            }
            else
            {
               /* EXECing a directory here, script name */
               while (*cptr && *cptr != '/' && sptr < zptr) *sptr++ = *cptr++;
            }

            if (sptr >= zptr)
               return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
            *sptr = '\0';

            /* generate the VMS script name */
            MapOdsUrlToVms (Scratch, ScriptVmsPtr, SizeOfScriptVmsPtr,
                            RmsSubChar, MapEllipsis, PathOds);

            /* indicate CGIplus script via leading '+' instead of '/' */
            if (mrptr->IsCgiPlusScript && (!RunTimePtr || !RunTimePtr[0]))
               ScriptPtr[0] = '+';

            /* get all including and after the first slash as the new path */
            zptr = (sptr = DerivedPathBuffer) + sizeof(DerivedPathBuffer);
            rqptr->MetaConMappedPtr = sptr;
            /* continue copying from the wildcard string */
            while (*cptr && sptr < zptr) *sptr++ = *cptr++;
            if (sptr >= zptr)
               return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
            *sptr++ = '\0';
            TotalLength = sptr - DerivedPathBuffer;

            if (WATCH_MODULE_DETAIL)
               WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "E/UXEC !&Z !&Z !&Z",
                          ScriptPtr, ScriptVmsPtr, DerivedPathBuffer);

            if (!rqpsptr->MapEmpty && !DerivedPathBuffer[0])
               return ("\0\0");

            if (rqpsptr->MapOnce)
            {
               /* convert the URL-style to a VMS-style specification */
               MapOdsUrlToVms (DerivedPathBuffer, VmsPtr, SizeOfVmsPtr,
                               RmsSubChar, MapEllipsis, PathOds);
               if (WATCH_MODULE_DETAIL)
                  WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL,
                             "E/UXEC !&Z !&Z", DerivedPathBuffer, VmsPtr);
               return (DerivedPathBuffer);
            }

            /* restarting at first rule map the path derived from the script */
            MetaConPass++;
            if (rqptr)
            {
               rqptr->MetaConPass = MetaConPass;
               rqptr->MetaConScriptPtr = ScriptPtr;
            }
            *VmsPtr = '\0';
            PathPtr = DerivedPathBuffer;
            RmsSubChar = MAPURL_RMS_SUBSTITUTION_DEFAULT;
            MapEllipsis = rqpsptr->MapEllipsis = false;
            MetaConParseReset (MetaGlobalMappingPtr, true);
            continue;

         case MAPURL_RULE_FAIL :

            /*************/
            /* FAIL rule */
            /*************/

            if (WATCH_MODULE_DETAIL)
               WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "FAIL !&Z", PathPtr);
            return (MsgFor(rqptr,MSG_MAPPING_DENIED_RULE)-1);

         case MAPURL_RULE_MAP :

            /************/
            /* MAP rule */
            /************/

            /* place this in a buffer so not to overwrite original path */
            zptr = (pptr = PathPtr = PathBuffer+1) + sizeof(PathBuffer)-1;
            rqptr->MetaConMappedPtr = pptr;
            widx = 1;
            /* scan through the result string */
            rptr = mrptr->ResultPtr;
            while (*rptr)
            {
               while (*rptr && *rptr != '*' && pptr < zptr) *pptr++ = *rptr++;
               if (!*rptr || pptr >= zptr) break;
               /* a wildcard asterisk, substitute from original path */
               while (*rptr == '*') rptr++;
               if (*rptr == '\'')
               {
                  /* specific wildcard component, e.g. "*'1" */
                  rptr++;
                  if (isdigit(*rptr))
                  {
                     idx = *rptr++ - '0';
                     cptr = WildString[idx];
                  }
                  else
                     cptr = "";
               }
               else
               if (widx < REGEX_PMATCH_MAX)
                  cptr = WildString[widx++];
               else
                  cptr = "";
               while (*cptr && pptr < zptr) *pptr++ = *cptr++;
            }
            if (pptr >= zptr)
               return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
            *pptr = '\0';

            /* continue with the substituted URL mapping */
            if (WATCH_MODULE_DETAIL)
               WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MAP !&Z", PathPtr);
            continue;

         case MAPURL_RULE_PASS :
         case MAPURL_RULE_REDIRECT :
         case MAPURL_RULE_USER :

            /****************************/
            /* PASS/REDIRECT/USER rules */
            /****************************/

            cptr = WildString[widx=1];
            rptr = mrptr->ResultPtr; 
            zptr = (pptr = PathBuffer+1) + sizeof(PathBuffer)-1;

            /* if there is no result to map just pass back the original path */
            if (!*rptr) 
            {
               if (mrptr->RuleType == MAPURL_RULE_PASS && MapRootPtr)
               {
                  if (WATCHING (rqptr, WATCH_MAPPING))
                     WatchThis (WATCHITM(rqptr), WATCH_MAPPING,
                                "ROOT !AZ", MapRootPtr);
                  for (cptr = MapRootPtr;
                       *cptr && pptr < zptr;
                       *pptr++ = *cptr++);
                  for (cptr = PathPtr; *cptr && pptr < zptr; *pptr++ = *cptr++);
                  if (pptr >= zptr)
                     return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
                  *pptr = '\0';
                  pptr = PathBuffer;
               }
               else
                  pptr = PathPtr;

               if (*pptr != '/')
               {
                  /* doesn't look like file-system, don't bother translating */
                  return (PathPtr);
               }

               /* convert the URL-style to a VMS-style specification */
               MapOdsUrlToVms (pptr, VmsPtr, SizeOfVmsPtr,
                               RmsSubChar, MapEllipsis, PathOds);
               if (WATCH_MODULE_DETAIL)
                  WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL,
                              "PASS !&Z !&Z", PathPtr, VmsPtr);
               return (PathPtr);
            }

            /* if redirect rule leave room for the redirection indicator */
            if (mrptr->RuleType == MAPURL_RULE_REDIRECT) pptr++;

            if (mrptr->RuleType == MAPURL_RULE_USER)
            {
               /* begin with the mapped user instead of the /~username */
               if (rqpsptr->PathOds)
                  uptr = MapUserName (rqptr, WildString[1], NULL);
               else
               {
                  uptr = MapUserName (rqptr, WildString[1], &PathOds);
                  rqptr->PathOds = PathOds;
                  if (PathOdsPtr) *PathOdsPtr = PathOds;
               }
               if (!uptr[0]) return (uptr);
               /* copy in the-now mapped username */
               while (*uptr && pptr < zptr) *pptr++ = *uptr++;
               /* skip over the equivalent in the result (i.e. "/~*") */
               while (*rptr && *rptr != '*') rptr++;
               if (*rptr) rptr++;
               /* skip first wildcard string (username) */
               cptr = WildString[widx=2];
            }

            if (mrptr->RuleType == MAPURL_RULE_PASS ||
                mrptr->RuleType == MAPURL_RULE_REDIRECT)
            {
               if (!WildStringCount)
               {
                  if (cptr = SysTrnLnm (mrptr->ResultPtr))
                  {
                     /****************************/
                     /* use logical name content */
                     /****************************/

                     if (WATCHING (rqptr, WATCH_MAPPING))
                        WatchThis (WATCHITM(rqptr), WATCH_MAPPING,
                                   "TRLNM !AZ \"!AZ\"", mrptr->ResultPtr, cptr);

                     if (mrptr->RuleType == MAPURL_RULE_PASS)
                     {
                        zptr = (sptr = VmsPtr) + SizeOfVmsPtr;
                        while (*cptr && sptr < zptr) *sptr++ = *cptr++;
                        if (sptr >= zptr)
                           return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
                        *sptr = '\0';

                        zptr = (sptr = PathBuffer+1) + sizeof(PathBuffer)-1;
                        cptr = mrptr->ResultPtr;
                     }
                     else
                     {
                        zptr = (sptr = PathBuffer+1) + sizeof(PathBuffer)-1;
                        /* the redirection indicator */
                        *sptr++ = '\1';
                     }

                     while (*cptr && sptr < zptr) *sptr++ = *cptr++;
                     if (sptr >= zptr)
                        return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
                     *sptr = '\0';
                     return (PathBuffer+1);
                  }
               }
            }

            if (mrptr->RuleType == MAPURL_RULE_PASS)
            {
               /* scan through the result string */
               if (rptr[0] == '\"' || rptr[0] == '\'')
                  if (isdigit(rptr[1]) && isdigit(rptr[2]) &&
                      isdigit(rptr[3]) && (!rptr[4] || rptr[4] == ' '))
                     StatusCodeMapping = true;
                  else
                     StatusCodeMapping = false;
               else
               if (isdigit(rptr[0]) && isdigit(rptr[1]) &&
                   isdigit(rptr[2]) && (!rptr[3] || rptr[3] == ' '))
                  StatusCodeMapping = true;
               else
                  StatusCodeMapping = false;

               if (StatusCodeMapping)
                  while (*rptr && pptr < zptr) *pptr++ = *rptr++;
            }
            else
               StatusCodeMapping = false;

            if (!StatusCodeMapping)
            {                                  
               if (mrptr->RuleType == MAPURL_RULE_PASS && MapRootPtr)
               {
                  if (WATCHING (rqptr, WATCH_MAPPING))
                     WatchThis (WATCHITM(rqptr), WATCH_MAPPING,
                                "ROOT !AZ", MapRootPtr);
                  for (cptr = MapRootPtr;
                       *cptr && pptr < zptr;
                       *pptr++ = *cptr++);
               }
               /* map wildcard asterisks */
               while (*rptr)
               {
                  while (*rptr && *rptr != '*' && pptr < zptr)
                     *pptr++ = *rptr++;
                  if (!*rptr) break;
                  /* wildcard asterisk, substitute from the wildcard buffer */
                  while (*rptr == '*') rptr++;
                  if (*rptr == '\'')
                  {
                     /* specific wildcard component, e.g. "*'1" */
                     rptr++;
                     if (isdigit(*rptr))
                     {
                        idx = *rptr++ - '0';
                        cptr = WildString[idx];
                     }
                     else
                        cptr = "";
                  }
                  else
                  if (widx < REGEX_PMATCH_MAX)
                     cptr = WildString[widx++];
                  else
                     cptr = "";
                  if (mrptr->RuleType == MAPURL_RULE_REDIRECT)
                  {
                     /* URL-encode wildcard portions from the path */
                     while (*cptr && pptr < zptr)
                     {
                        for (eptr = FaoUrlEncodeTable[*(unsigned char*)cptr];
                             *eptr && pptr < zptr;
                             *pptr++ = *eptr++);
                        cptr++;
                     }
                  }
                  else
                     while (*cptr && pptr < zptr) *pptr++ = *cptr++;
                  if (pptr >= zptr) break;
               }
            }
            if (pptr >= zptr)
               return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
            *pptr = '\0';

            if (mrptr->RuleType == MAPURL_RULE_PASS ||
                mrptr->RuleType == MAPURL_RULE_USER)
            {
               /* if status code mapping return leading null char */
               if (StatusCodeMapping)
               {
                  PathBuffer[0] = '\0';
                  return (PathBuffer);
               }

               if (PathBuffer[1] != '/')
               {
                  /* doesn't look like file-system, don't bother translating */
                  return (PathBuffer+1);
               }

               /* convert the URL-style to a VMS-style specification */
               MapOdsUrlToVms (PathBuffer+1, VmsPtr, SizeOfVmsPtr,
                               RmsSubChar, MapEllipsis, PathOds);
               if (WATCH_MODULE_DETAIL)
                  WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL,
                             "PASS !&Z !&Z", PathPtr, VmsPtr);
               return (PathBuffer+1);
            }
            else
            {
               if (WATCH_MODULE_DETAIL)
                  WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL,
                             "REDIRECT !&Z !&Z", PathPtr, VmsPtr);
               /* indicate it's a redirect */
               PathBuffer[1] = '\1';
               return (PathBuffer+1);
            }

         case MAPURL_RULE_SCRIPT :

            /***************/
            /* SCRIPT rule */
            /***************/

            tptr = mrptr->TemplatePtr;
            zptr = (pptr = ScriptPtr) + SizeOfScriptPtr;
            while (*tptr && *tptr != '*' && pptr < zptr) *pptr++ = *tptr++;
            if (pptr > ScriptPtr && pptr[-1] == '/') pptr--;
            if (pptr >= zptr)
               return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
            *pptr = '\0';

            rptr = mrptr->ResultPtr;

            if (*rptr == '(')
            {
               /* run-time, e.g. exec /plbin/@ (ht_exe:pl.exe)/plbin/@ */
               rptr++;
               zptr = (sptr = RunTimePtr) + SizeOfRunTimePtr;
               while (*rptr && *rptr != ')' && sptr < zptr) *sptr++ = *rptr++;
               if (sptr >= zptr)
                  return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
               *sptr = '\0';
               if (*rptr) rptr++;
            }

            zptr = (sptr = Scratch) + sizeof(Scratch);
            if (MapRootPtr)
            {
               if (WATCHING (rqptr, WATCH_MAPPING))
                  WatchThis (WATCHITM(rqptr), WATCH_MAPPING,
                             "ROOT !AZ", MapRootPtr);
               for (cptr = MapRootPtr; *cptr && sptr < zptr; *sptr++ = *cptr++);
            }
            while (*rptr &&
                   *rptr != '*' &&
                   !SAME2(rptr,'/*') &&
                   sptr < zptr) *sptr++ = *rptr++;
            if (sptr >= zptr)
               return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
            *sptr = '\0';

            /* convert the URL-format script to VMS specification */
            MapOdsUrlToVms (Scratch, ScriptVmsPtr, SizeOfScriptVmsPtr,
                            RmsSubChar, MapEllipsis, PathOds);

            /* indicate CGIplus script via leading '+' instead of '/' */
            if (mrptr->IsCgiPlusScript && (!RunTimePtr || !RunTimePtr[0]))
               ScriptPtr[0] = '+';

            /* get wildcard matched second section of path as new path */
            zptr = (sptr = DerivedPathBuffer) + sizeof(DerivedPathBuffer);
            rqptr->MetaConMappedPtr = sptr;
            if (rqpsptr->MapOnce && MapRootPtr)
            {
               if (WATCHING (rqptr, WATCH_MAPPING))
                  WatchThis (WATCHITM(rqptr), WATCH_MAPPING,
                             "ROOT !AZ", MapRootPtr);
               for (cptr = MapRootPtr; *cptr && sptr < zptr; *sptr++ = *cptr++);
            }
            while (*rptr && *rptr != '*' && sptr < zptr) *sptr++ = *rptr++;
            cptr = WildString[1];
            while (*cptr && sptr < zptr) *sptr++ = *cptr++;
            if (sptr < zptr) *sptr++ = '\0';
            if (sptr >= zptr)
               return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
            TotalLength = sptr - DerivedPathBuffer;

            if (WATCH_MODULE_DETAIL)
               WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL,
                          "SCRIPT !&Z !&Z !&Z",
                          ScriptPtr, ScriptVmsPtr, DerivedPathBuffer);

            if (!rqpsptr->MapEmpty && !DerivedPathBuffer[0])
               return ("\0\0");

            if (rqpsptr->MapOnce)
            {
               /* convert the URL-style to a VMS-style specification */
               MapOdsUrlToVms (DerivedPathBuffer, VmsPtr, SizeOfVmsPtr,
                               RmsSubChar, MapEllipsis, PathOds);
               if (WATCH_MODULE_DETAIL)
                  WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL,
                             "SCRIPT !&Z !&Z", DerivedPathBuffer, VmsPtr);
               return (DerivedPathBuffer);
            }

            /* restarting at first rule map the path derived from the script */
            MetaConPass++;
            if (rqptr)
            {
               rqptr->MetaConPass = MetaConPass;
               rqptr->MetaConScriptPtr = ScriptPtr;
            }
            *VmsPtr = '\0';
            PathPtr = DerivedPathBuffer;
            RmsSubChar = MAPURL_RMS_SUBSTITUTION_DEFAULT;
            MapEllipsis = rqpsptr->MapEllipsis = false;
            MetaConParseReset (MetaGlobalMappingPtr, true);
            continue;

         case MAPURL_RULE_PROTECT :

            /****************/
            /* PROTECT rule */
            /****************/

            if (MetaConPass == 1)
            {
               /* pass one protects the full path or script component */
               if (mrptr->ResultLength)
               {
                  rqptr->rqAuth.Protect1Ptr =
                     VmGetHeap (rqptr, mrptr->ResultLength+1);
                  rqptr->rqAuth.Protect1Length = mrptr->ResultLength;
                  memcpy (rqptr->rqAuth.Protect1Ptr,
                          mrptr->ResultPtr,
                          mrptr->ResultLength+1);
               }
               else
               {
                  /* empty result string cancels any previous authorization */
                  rqptr->rqAuth.Protect1Ptr = NULL;
                  rqptr->rqAuth.Protect1Length = 0;
               }
            }
            else
            {
               /* pass two protects any path following the script component */
               if (mrptr->ResultLength)
               {
                  rqptr->rqAuth.Protect2Ptr =
                     VmGetHeap (rqptr, mrptr->ResultLength+1);
                  rqptr->rqAuth.Protect2Length = mrptr->ResultLength;
                  memcpy (rqptr->rqAuth.Protect2Ptr,
                          mrptr->ResultPtr,
                          mrptr->ResultLength+1);
               }
               else
               {
                  /* empty result string cancels any previous authorization */
                  rqptr->rqAuth.Protect1Ptr = NULL;
                  rqptr->rqAuth.Protect1Length = 0;
               }
            }

            continue;

         case MAPURL_RULE_SET :

            /************/
            /* SET rule */
            /************/

            /* path SETings have already been applied */
            continue;
         }
      }
      else
      {
         /***************************/
         /* mapping from VMS to URL */
         /***************************/

         /* REVERSE maps a VMS "result" to a "template" :^) */

         cptr = WildString[idx = 1];
         if (MapRootPtr)
         {
            if (WATCHING (rqptr, WATCH_MAPPING))
               WatchThis (WATCHITM(rqptr), WATCH_MAPPING,
                          "ROOT !AZ", MapRootPtr);
            while (MapRootLength-- && *cptr) cptr++;
         }
         zptr = (pptr = PathPtr) + SizeOfPathPtr;
         tptr = mrptr->TemplatePtr;
         /* scan through the template string */
         while (*tptr)
         {
            while (*tptr && *tptr != '*' && pptr < zptr) *pptr++ = *tptr++;
            if (!*tptr) break;
            /* a wildcard asterisk, substitute from result path */
            while (*tptr == '*') tptr++;
            if (*cptr) while (*cptr && pptr < zptr) *pptr++ = *cptr++;
            /* three lines fix 05-APR-2018 longstanding bug */
            if (!*tptr) break;
            if (idx < REGEX_PMATCH_MAX) idx++;
            cptr = WildString[idx];
         }
         if (pptr >= zptr)
            return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
         *pptr = '\0';

         return (PathPtr);
      }
   }

   /***********************/
   /* a mapping not found */
   /***********************/

   if (WatchThisOne)
      WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "END of mapping rules"); 

   if (MapPathToVms)
      return (MsgFor(rqptr,MSG_MAPPING_DENIED_DEFAULT)-1);
   else
      return (MAPURL_NO_REVERSE_PATH);
}

/*****************************************************************************/
/*
The path has a setting.  Map that onto the request path settings.
*/ 
 
MapUrl_PathSet
(
REQUEST_STRUCT *rqptr,
REQUEST_PATHSET *rqpsptr,
MAP_SET_META *mrpsptr
)
{
   int  length;
   char  *cptr, *sptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_MAPURL))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MapUrl_PathSet()");

   if (mrpsptr->NoAcceptLang)
   {
      rqpsptr->AcceptLangChar = '\0';
      rqpsptr->AcceptLangWildcard =
         rqpsptr->AcceptLangTypeVariant = false;
      rqpsptr->AcceptLangPtr = NULL;
   }
   else
   if (mrpsptr->AcceptLangChar)
   {
      rqpsptr->AcceptLangChar = mrpsptr->AcceptLangChar;
      rqpsptr->AcceptLangWildcard = mrpsptr->AcceptLangWildcard;
      rqpsptr->AcceptLangTypeVariant = mrpsptr->AcceptLangTypeVariant;
      if (mrpsptr->AcceptLangLength)
      {
         rqpsptr->AcceptLangPtr =
            VmGetHeap (rqptr, mrpsptr->AcceptLangLength+1);
         memcpy (rqpsptr->AcceptLangPtr,
                 mrpsptr->AcceptLangPtr,
                 mrpsptr->AcceptLangLength+1);
      }
   }

   if (mrpsptr->NoAlert)
      rqpsptr->Alert = 0;
   else
   if (mrpsptr->Alert)
      rqpsptr->Alert = mrpsptr->Alert;

   if (mrpsptr->NoAuthAll)
      rqpsptr->AuthorizeAll = false;
   else
   if (mrpsptr->AuthAll)
      rqpsptr->AuthorizeAll = true;

   if (mrpsptr->NoAuthMapped)
      rqpsptr->AuthorizeMapped = false;
   else
   if (mrpsptr->AuthMapped)
      rqpsptr->AuthorizeMapped = true;

   if (mrpsptr->NoAuthOnce)
      rqpsptr->AuthorizeOnce = false;
   else
   if (mrpsptr->AuthOnce)
      rqpsptr->AuthorizeOnce = true;

   /* only if they haven't been set in any previous pass */
   if (!rqpsptr->AuthRevalidateTimeout)
      rqpsptr->AuthRevalidateTimeout = mrpsptr->AuthRevalidateTimeout;

   if (mrpsptr->AuthSysUafPwdExpUrlLength)
   {
      rqpsptr->AuthSysUafPwdExpUrlPtr =
         VmGetHeap (rqptr, mrpsptr->AuthSysUafPwdExpUrlLength+1);
      memcpy (rqpsptr->AuthSysUafPwdExpUrlPtr,
              mrpsptr->AuthSysUafPwdExpUrlPtr,
              mrpsptr->AuthSysUafPwdExpUrlLength+1);
   }

   if (mrpsptr->NoCache)
   {
      rqpsptr->NoCache = true;
      rqpsptr->CacheCGI =
         rqpsptr->CacheCookie =
         rqpsptr->CacheNoFile =
         rqpsptr->CacheNet =
         rqpsptr->CacheNPH =
         rqpsptr->CachePermanent =
         rqpsptr->CacheQuery = false;
      rqpsptr->CacheExpiresAfter =
         rqpsptr->CacheGuardSeconds =
         rqpsptr->CacheMaxKBytes = 0;
   }
   else
   if (mrpsptr->CacheSetting)
   {
      if (mrpsptr->Cache)
         rqpsptr->NoCache = rqpsptr->CacheNoFile = false;

      if (mrpsptr->CacheNoCGI)
         rqpsptr->CacheCGI = false;
      else
      if (mrpsptr->CacheCGI)
         rqpsptr->CacheCGI = true;

      if (mrpsptr->CacheNoCookie)
         rqpsptr->CacheCookie = false;
      else
      if (mrpsptr->CacheCookie)
         rqpsptr->CacheCookie = true;

      if (mrpsptr->CacheNoFile)
         rqpsptr->CacheNoFile = true;
      else
      if (mrpsptr->CacheFile)
         rqpsptr->CacheNoFile = false;

      if (mrpsptr->CacheNoNet)
         rqpsptr->CacheNet = false;
      else
      if (mrpsptr->CacheNet)
         rqpsptr->CacheNet = true;

      if (mrpsptr->CacheNoNPH)
         rqpsptr->CacheNPH = false;
      else
      if (mrpsptr->CacheNPH)
         rqpsptr->CacheNPH = true;

      if (mrpsptr->CacheNoPermanent)
         rqpsptr->CachePermanent = false;
      else
      if (mrpsptr->CachePermanent)
         rqpsptr->CachePermanent = true;

      if (mrpsptr->CacheNoQuery)
         rqpsptr->CacheQuery = false;
      else
      if (mrpsptr->CacheQuery)
         rqpsptr->CacheQuery = true;

      if (mrpsptr->CacheNoScript)
         rqpsptr->CacheCGI = rqpsptr->CacheNPH = false;
      else
      if (mrpsptr->CacheScript)
         rqpsptr->CacheCGI = rqpsptr->CacheNPH = true;

      if (mrpsptr->CacheNoSSI)
         rqpsptr->CacheSSI = false;
      else
      if (mrpsptr->CacheSSI)
         rqpsptr->CacheSSI = true;

      if (mrpsptr->CacheExpiresAfter)
      {
         if (mrpsptr->CacheExpiresAfter == CACHE_EXPIRES_NONE)
            rqpsptr->CacheExpiresAfter = 0;
         else
            rqpsptr->CacheExpiresAfter = mrpsptr->CacheExpiresAfter;
      }
      if (mrpsptr->CacheGuardSeconds)
      {
         /* negative 1 indicates to revert to configuration maximum */
         if (mrpsptr->CacheGuardSeconds == -1)
            rqpsptr->CacheGuardSeconds = 0;
          else
            rqpsptr->CacheGuardSeconds = mrpsptr->CacheGuardSeconds;
      }
      if (mrpsptr->CacheMaxKBytes)
      {
         /* negative 1 indicates to revert to configuration maximum */
         if (mrpsptr->CacheMaxKBytes == -1)
            rqpsptr->CacheMaxKBytes = 0;
         else
            rqpsptr->CacheMaxKBytes = mrpsptr->CacheMaxKBytes;
      }
   }

   if (mrpsptr->CgiPlusInCC[0] || mrpsptr->CgiPlusInCC[1])
   {
      if (mrpsptr->CgiPlusInCC[0])
         memcpy (rqpsptr->CgiPlusInCC, mrpsptr->CgiPlusInCC,
                 sizeof(rqpsptr->CgiPlusInCC));
      else
         memset (rqpsptr->CgiPlusInCC, 0,
                 sizeof(rqpsptr->CgiPlusInCC));
   }

   if (mrpsptr->CgiPlusInNoWriteof)
      rqpsptr->CgiPlusInWriteof = false;
   else
   if (mrpsptr->CgiPlusInWriteof)
      rqpsptr->CgiPlusInWriteof = true;

   if (mrpsptr->NoDefaultSearch)
      rqpsptr->NoDefaultSearch = true;
   else
   if (mrpsptr->DefaultSearch)
      rqpsptr->NoDefaultSearch = false;

   if (mrpsptr->ClientAddress)
   {
      rqpsptr->ClientAddress = mrpsptr->ClientAddress;
      if (mrpsptr->ClientAddress == MAPURL_CLIENT_LITERAL)
      {
         rqpsptr->ClientAddressLiteralPtr =
               VmGetHeap (rqptr, mrpsptr->ClientAddressLiteralLength+1);
         memcpy (rqpsptr->ClientAddressLiteralPtr,
                 mrpsptr->ClientAddressLiteralPtr,
                 mrpsptr->ClientAddressLiteralLength+1);
      }
   }

   if (mrpsptr->DirNoAccess)
   {
      rqpsptr->DirNoAccess = true;
      rqpsptr->DirAccess = rqpsptr->DirAccessSelective = false;
   }
   else
   if (mrpsptr->DirAccess)
   {
      rqpsptr->DirAccess = true;
      rqpsptr->DirNoAccess = rqpsptr->DirAccessSelective = false;
   }
   else
   if (mrpsptr->DirAccessSelective)
   {
      rqpsptr->DirAccessSelective = true;
      rqpsptr->DirAccess = rqpsptr->DirNoAccess = false;
   }

   if (mrpsptr->DirFont == MAPURL_DIR_FONT_INHERIT)
      rqpsptr->DirFont = MAPURL_DIR_FONT_INHERIT;
   else
   if (mrpsptr->DirFont == MAPURL_DIR_FONT_MONOSPACE)
      rqpsptr->DirFont = 0;

   if (mrpsptr->DirNoIconLink)
      rqpsptr->DirNoIconLink = true;
   else
   if (mrpsptr->DirIconLink)
      rqpsptr->DirNoIconLink = false;

   if (mrpsptr->DirNoImpliedWildcard)
      rqpsptr->DirNoImpliedWildcard = true;
   else
   if (mrpsptr->DirImpliedWildcard)
      rqpsptr->DirNoImpliedWildcard = false;

   if (mrpsptr->DirSort[0])
   {
      rqpsptr->DirSort[0] = mrpsptr->DirSort[0];
      rqpsptr->DirSort[1] = mrpsptr->DirSort[1];
   }

   if (mrpsptr->DirStyle == MAPURL_DIR_STYLE_DEFAULT ||
       mrpsptr->DirStyle == MAPURL_DIR_STYLE_ANCHOR)
      rqpsptr->DirStyle = 0;
   else
   if (mrpsptr->DirStyle)
      rqpsptr->DirStyle = mrpsptr->DirStyle;

   if (mrpsptr->DirTargetPtr)
   {
      /* empty string resets */
      if (mrpsptr->DirTargetLength)
      {
         rqpsptr->DirTargetPtr =
            VmGetHeap (rqptr, mrpsptr->DirTargetLength+1);
         memcpy (rqpsptr->DirTargetPtr,
                 mrpsptr->DirTargetPtr,
                 mrpsptr->DirTargetLength+1);
      }
      else
         rqpsptr->DirTargetPtr = NULL;
   }

   if (mrpsptr->DirThesePtr)
   {
      /* empty string resets */
      if (mrpsptr->DirTheseLength)
      {
         rqpsptr->DirThesePtr =
            VmGetHeap (rqptr, mrpsptr->DirTheseLength+1);
         memcpy (rqpsptr->DirThesePtr,
                 mrpsptr->DirThesePtr,
                 mrpsptr->DirTheseLength+1);
      }
      else
         rqpsptr->DirThesePtr = NULL;
   }

   if (mrpsptr->DirTitle == MAPURL_DIR_TITLE_THIS)
   {
      /* empty string resets */
      if (mrpsptr->DirTitleLength)
      {
         rqpsptr->DirTitle = mrpsptr->DirTitle;
         rqpsptr->DirTitlePtr =
            VmGetHeap (rqptr, mrpsptr->DirTitleLength+1);
         memcpy (rqpsptr->DirTitlePtr,
                 mrpsptr->DirTitlePtr,
                 mrpsptr->DirTitleLength+1);
      }
      else
      {
         rqpsptr->DirTitle = 0;
         rqpsptr->DirTitlePtr = NULL;
      }
   }
   else
   if (mrpsptr->DirTitle)
      rqpsptr->DirTitle = mrpsptr->DirTitle;

   if (mrpsptr->DirNoWildcard)
   {
      rqpsptr->DirNoWildcard = true;
      rqpsptr->DirWildcard = false;
   }
   else
   if (mrpsptr->DirWildcard)
   {
      rqpsptr->DirNoWildcard = false;
      rqpsptr->DirWildcard = true;
   }

   /* negative 1 indicates to revert to zero */
   if (mrpsptr->DirVersionsOf)
      rqpsptr->DirVersionsOf = mrpsptr->DirVersionsOf;

   if (mrpsptr->DirNoWildcard)
   {
      rqpsptr->DirNoWildcard = true;
      rqpsptr->DirWildcard = false;
   }
   else
   if (mrpsptr->DirWildcard)
   {
      rqpsptr->DirNoWildcard = false;
      rqpsptr->DirWildcard = true;
   }

   if (mrpsptr->NoExpired)
      rqpsptr->Expired = false;
   else
   if (mrpsptr->Expired)
      rqpsptr->Expired = true;

   if (mrpsptr->NoHtmlEquals)
   {
      rqpsptr->HtmlBodyTagPtr =
         rqpsptr->HtmlFooterPtr =
         rqpsptr->HtmlFooterTagPtr =
         rqpsptr->HtmlHeaderPtr =
         rqpsptr->HtmlHeaderTagPtr = NULL;
   }
   if (mrpsptr->HtmlBodyTagPtr)
   {
      /* empty string resets */
      if (mrpsptr->HtmlBodyTagLength)
      {
         rqpsptr->HtmlBodyTagPtr =
            VmGetHeap (rqptr, mrpsptr->HtmlBodyTagLength+1);
         memcpy (rqpsptr->HtmlBodyTagPtr,
                 mrpsptr->HtmlBodyTagPtr,
                 mrpsptr->HtmlBodyTagLength+1);
      }
      else
         rqpsptr->HtmlBodyTagPtr = NULL;
   }
   if (mrpsptr->HtmlFooterPtr)
   {
      /* empty string resets */
      if (mrpsptr->HtmlFooterLength)
      {
         rqpsptr->HtmlFooterPtr =
            VmGetHeap (rqptr, mrpsptr->HtmlFooterLength+1);
         memcpy (rqpsptr->HtmlFooterPtr,
                 mrpsptr->HtmlFooterPtr,
                 mrpsptr->HtmlFooterLength+1);
      }
      else
         rqpsptr->HtmlFooterPtr = NULL;
   }
   if (mrpsptr->HtmlFooterTagPtr)
   {
      /* empty string resets */
      if (mrpsptr->HtmlFooterTagLength)
      {
         rqpsptr->HtmlFooterTagPtr =
            VmGetHeap (rqptr, mrpsptr->HtmlFooterTagLength+1);
         memcpy (rqpsptr->HtmlFooterTagPtr,
                 mrpsptr->HtmlFooterTagPtr,
                 mrpsptr->HtmlFooterTagLength+1);
      }
      else
         rqpsptr->HtmlFooterTagPtr = NULL;
   }
   if (mrpsptr->HtmlHeaderPtr)
   {
      /* empty string resets */
      if (mrpsptr->HtmlHeaderLength)
      {
         rqpsptr->HtmlHeaderPtr =
            VmGetHeap (rqptr, mrpsptr->HtmlHeaderLength+1);
         memcpy (rqpsptr->HtmlHeaderPtr,
                 mrpsptr->HtmlHeaderPtr,
                 mrpsptr->HtmlHeaderLength+1);
      }
      else
         rqpsptr->HtmlHeaderPtr = NULL;
   }
   if (mrpsptr->HtmlHeaderTagPtr)
   {
      /* empty string resets */
      if (mrpsptr->HtmlHeaderTagLength)
      {
         rqpsptr->HtmlHeaderTagPtr =
            VmGetHeap (rqptr, mrpsptr->HtmlHeaderTagLength+1);
         memcpy (rqpsptr->HtmlHeaderTagPtr,
                 mrpsptr->HtmlHeaderTagPtr,
                 mrpsptr->HtmlHeaderTagLength+1);
      }
      else
         rqpsptr->HtmlHeaderTagPtr = NULL;
   }

   if (HTTP2_REQUEST(rqptr))
   {
      /* if HTTP/2 request */
      if (mrpsptr->Http2MaxStreams)
         rqpsptr->Http2MaxStreams = mrpsptr->Http2MaxStreams;
      if (mrpsptr->Http2SendGoAway)
         rqpsptr->Http2SendGoAway = mrpsptr->Http2SendGoAway;
      if (mrpsptr->Http2SendPing) rqpsptr->Http2SendPing = true;
      if (mrpsptr->Http2SendReset)
         rqpsptr->Http2SendReset = mrpsptr->Http2SendReset;
      if (mrpsptr->Http2ToHttp11) rqpsptr->Http2ToHttp11 = true;
      if (mrpsptr->Http2WriteQueue)
         rqpsptr->Http2WriteQueue = mrpsptr->Http2WriteQueue;
   }

   if (mrpsptr->NoLog)
      rqpsptr->NoLog = true;
   else
   if (mrpsptr->Log)
      rqpsptr->NoLog = false;

   if (mrpsptr->MapNonEmpty)
      rqpsptr->MapEmpty = false;
   else
   if (mrpsptr->MapEmpty)
      rqpsptr->MapEmpty = true;

   if (mrpsptr->MapExtensionMethod)
      rqpsptr->MapExtensionMethod = true;
   else
   if (mrpsptr->MapExtensionMethod)
      rqpsptr->MapExtensionMethod = false;

   if (mrpsptr->MapOnce)
      rqpsptr->MapOnce = true;
   else
   if (mrpsptr->NoMapOnce)
      rqpsptr->MapOnce = false;

   if (mrpsptr->MapUri) rqpsptr->MapUri = true;

   if (mrpsptr->PrivSsi)
      rqpsptr->PrivSsi = true;
   else
   if (mrpsptr->NoPrivSsi)
      rqpsptr->PrivSsi = false;

   if (mrpsptr->NoProfile)
      rqpsptr->NoProfile = true;
   else
   if (mrpsptr->Profile)
      rqpsptr->NoProfile = false;

   if (mrpsptr->ProxyNoAffinity)
      rqpsptr->ProxyAffinity = false;
   else
   if (mrpsptr->ProxyAffinity)
      rqpsptr->ProxyAffinity = true;

   if (mrpsptr->ProxyForwardedBy)
      rqpsptr->ProxyForwardedBy = mrpsptr->ProxyForwardedBy;

   if (mrpsptr->ProxyXForwardedFor)
      rqpsptr->ProxyXForwardedFor = mrpsptr->ProxyXForwardedFor;

   if (mrpsptr->NoProxyReverseVerify)
      rqpsptr->ProxyReverseVerify = false;
   else
   if (mrpsptr->ProxyReverseVerify)
      rqpsptr->ProxyReverseVerify = true;

   if (mrpsptr->NoProxyReverseAuthHeader)
      rqpsptr->ProxyReverseNoAuthHeader = true;
   else
   if (mrpsptr->ProxyReverseAuthHeader)
      rqpsptr->ProxyReverseNoAuthHeader = false;

   if (mrpsptr->ProxyTunnelRequestLength)
   {
      rqpsptr->ProxyTunnelRequestPtr =
         VmGetHeap (rqptr, mrpsptr->ProxyTunnelRequestLength);
      memcpy (rqpsptr->ProxyTunnelRequestPtr,
              mrpsptr->ProxyTunnelRequestPtr,
              mrpsptr->ProxyTunnelRequestLength);
      rqpsptr->ProxyTunnelRequestLength = mrpsptr->ProxyTunnelRequestLength;
   }

   if (mrpsptr->NoProxyUnknownRequestFields)
      rqpsptr->ProxyUnknownRequestFields = false;
   else
   if (mrpsptr->ProxyUnknownRequestFields)
      rqpsptr->ProxyUnknownRequestFields = true;

   if (mrpsptr->ProxyHeaderCount)
   {
      rqpsptr->ProxyHeaderCount = mrpsptr->ProxyHeaderCount;
      for (int idx = 0; idx < mrpsptr->ProxyHeaderCount; idx++)
      {
         rqpsptr->ProxyHeader[idx] =
            VmGetHeap (rqptr, strlen(mrpsptr->ProxyHeader[idx]));
         strcpy (rqpsptr->ProxyHeader[idx], mrpsptr->ProxyHeader[idx]);
      }
   }

   if (mrpsptr->PutRFM) rqpsptr->PutRFM = mrpsptr->PutRFM; 

   if (mrpsptr->PutMaxKbytes) rqpsptr->PutMaxKbytes = mrpsptr->PutMaxKbytes; 

   if (mrpsptr->RegexSyntax)
      if ((rqpsptr->RegexSyntax = mrpsptr->RegexSyntax) == -1)
         rqpsptr->RegexSyntax = 0;

   if (mrpsptr->ReportType) rqpsptr->ReportType = mrpsptr->ReportType;

   if (mrpsptr->Report400as)
      rqpsptr->Report400as = mrpsptr->Report400as;
   if (mrpsptr->Report403as)
      rqpsptr->Report403as = mrpsptr->Report403as;
   if (mrpsptr->Report404as)
      rqpsptr->Report404as = mrpsptr->Report404as;

   if (mrpsptr->ResponseGzip)
      rqpsptr->ResponseGzip = mrpsptr->ResponseGzip;

   if (mrpsptr->Response200is203)
      rqpsptr->Response200is203 = mrpsptr->Response200is203;

   if (mrpsptr->ResponseNoChunked)
      rqpsptr->ResponseNoChunked = true;
   else
   if (mrpsptr->ResponseChunked)
      rqpsptr->ResponseNoChunked = false;

   if (mrpsptr->ResponseHeaderNone)
      rqpsptr->ResponseHeaderNone = true;
   else
   if (mrpsptr->ResponseHeaderBegin)
      rqpsptr->ResponseHeaderBegin = true;
   else
   if (mrpsptr->ResponseHeaderFull)
      rqpsptr->ResponseHeaderBegin =
         rqpsptr->ResponseHeaderNone = false;

   if (mrpsptr->ResponseHttpNoOriginal)
      rqpsptr->ResponseHttpOriginal = false;
   else
   if (mrpsptr->ResponseHttpOriginal)
      rqpsptr->ResponseHttpOriginal = true;

   if (mrpsptr->ScriptBodyNoDecode)
      rqpsptr->ScriptBodyDecode = false;
   else
   if (mrpsptr->ScriptBodyDecode)
      rqpsptr->ScriptBodyDecode = true;

   if (mrpsptr->ScriptSymbolNoTruncate)
      rqpsptr->ScriptSymbolTruncate = false;
   else
   if (mrpsptr->ScriptSymbolTruncate)
      rqpsptr->ScriptSymbolTruncate = true;

   if (mrpsptr->ScriptSyntaxNoUnix)
      rqpsptr->ScriptSyntaxUnix = false;
   else
   if (mrpsptr->ScriptSyntaxUnix)
      rqpsptr->ScriptSyntaxUnix = true;

   /* careful! this one's a little back-to-front */
   if (mrpsptr->ScriptFind)
      rqpsptr->ScriptNoFind = false;
   else
   if (mrpsptr->ScriptNoFind)
      rqpsptr->ScriptNoFind = true;

   if (mrpsptr->ScriptNoPathFind)
      rqpsptr->ScriptPathFind = false;
   else
   if (mrpsptr->ScriptPathFind)
      rqpsptr->ScriptPathFind = true;

   if (mrpsptr->ScriptNoQueryNone)
      rqpsptr->ScriptQueryNone = false;
   else
   if (mrpsptr->ScriptQueryNone)
      rqpsptr->ScriptQueryNone = true;

   if (mrpsptr->ScriptNoQueryRelaxed)
      rqpsptr->ScriptQueryRelaxed = false;
   else
   if (mrpsptr->ScriptQueryRelaxed)
      rqpsptr->ScriptQueryRelaxed = true;

   if (mrpsptr->AccessNoProfile)
      rqpsptr->AccessProfile = false;
   else
   if (mrpsptr->AccessProfile)
      rqpsptr->AccessProfile = true;

   if (mrpsptr->AccessNoRead)
      rqpsptr->AccessRead = false;
   else
   if (mrpsptr->AccessRead)
      rqpsptr->AccessRead = true;

   if (mrpsptr->AccessNoServer)
      rqpsptr->AccessServer = false;
   else
   if (mrpsptr->AccessServer)
      rqpsptr->AccessServer = true;

   if (mrpsptr->AccessNoWrite)
      rqpsptr->AccessWrite = false;
   else
   if (mrpsptr->AccessWrite)
      rqpsptr->AccessWrite = true;

   if (mrpsptr->WebDavNoHidden)
      rqpsptr->WebDavNoHidden = true;
   else
   if (mrpsptr->WebDavHidden)
      rqpsptr->WebDavNoHidden = false;

   if (mrpsptr->WebDavNoLock)
      rqpsptr->WebDavNoLock = true;
   else
   if (mrpsptr->WebDavLock)
      rqpsptr->WebDavNoLock = false;

   /* allowed to be an empty string (suppresses directory) */
   if (mrpsptr->WebDavMetaDirPtr)
   {
      rqpsptr->WebDavMetaDirPtr =
         VmGetHeap (rqptr, mrpsptr->WebDavMetaDirLength+1);
      memcpy (rqpsptr->WebDavMetaDirPtr,
              mrpsptr->WebDavMetaDirPtr,
              mrpsptr->WebDavMetaDirLength+1);
   }

   if (mrpsptr->WebDavNoProfile)
      rqpsptr->WebDavProfile = false;
   else
   if (mrpsptr->WebDavProfile)
      rqpsptr->WebDavProfile = true;

   if (mrpsptr->WebDavNoProp)
      rqpsptr->WebDavNoProp = true;
   else
   if (mrpsptr->WebDavProp)
      rqpsptr->WebDavNoProp = false;

   if (mrpsptr->WebDavNoPutLock)
      rqpsptr->WebDavPutLock = false;
   else
   if (mrpsptr->WebDavPutLock)
      rqpsptr->WebDavPutLock = true;

   if (mrpsptr->WebDavNoRead)
      rqpsptr->WebDavRead = false;
   else
   if (mrpsptr->WebDavRead)
      rqpsptr->WebDavRead = true;

   if (mrpsptr->WebDavNoServer)
      rqpsptr->WebDavServer = false;
   else
   if (mrpsptr->WebDavServer)
      rqpsptr->WebDavServer = true;

   if (mrpsptr->WebDavNoWinProp)
      rqpsptr->WebDavNoWinProp = true;
   else
   if (mrpsptr->WebDavWinProp)
      rqpsptr->WebDavNoWinProp = false;

   if (mrpsptr->WebDavNoWrite)
      rqpsptr->WebDavWrite = false;
   else
   if (mrpsptr->WebDavWrite)
      rqpsptr->WebDavWrite = true;

   if (mrpsptr->WebDavLockTimeoutDefault)
      rqpsptr->WebDavLockTimeoutDefault = mrpsptr->WebDavLockTimeoutDefault;

   if (mrpsptr->WebDavLockTimeoutMax)
      rqpsptr->WebDavLockTimeoutMax = mrpsptr->WebDavLockTimeoutMax;

   if (mrpsptr->WebSocketInputSize)
      rqpsptr->WebSocketInputSize = mrpsptr->WebSocketInputSize;

   if (mrpsptr->WebSocketOutputSize)
      rqpsptr->WebSocketOutputSize = mrpsptr->WebSocketOutputSize;

   if (rqpsptr->ThrottleSet &&
       mrpsptr->ThrottleNoSet)
   {
      /* reset a previously mapped throttle */
      rqpsptr->ThrottleSet = false;
      rqpsptr->ThrottleBusy =
         rqpsptr->ThrottleFrom =
         rqpsptr->ThrottleIndex =
         rqpsptr->ThrottlePerUser = 
         rqpsptr->ThrottleResume =
         rqpsptr->ThrottleTo =
         rqpsptr->ThrottleTimeoutBusy =
         rqpsptr->ThrottleTimeoutQueue = 0;
   }
   else
   if (!rqpsptr->ThrottleSet &&
       mrpsptr->ThrottleSet)
   {
      /* only if it hasn't been set in any previous pass */
      rqpsptr->ThrottleSet = true;
      rqpsptr->ThrottleBusy = mrpsptr->ThrottleBusy;
      rqpsptr->ThrottleFrom = mrpsptr->ThrottleFrom;
      rqpsptr->ThrottleIndex = mrpsptr->ThrottleIndex;
      rqpsptr->ThrottlePerUser = mrpsptr->ThrottlePerUser;
      rqpsptr->ThrottleResume = mrpsptr->ThrottleResume;
      rqpsptr->ThrottleTo = mrpsptr->ThrottleTo;
      rqpsptr->ThrottleTimeoutBusy = mrpsptr->ThrottleTimeoutBusy;
      rqpsptr->ThrottleTimeoutQueue = mrpsptr->ThrottleTimeoutQueue;
   }

   /* only if they haven't been set in any previous pass */
   if (!rqpsptr->ScriptBitBucketTimeout)
      rqpsptr->ScriptBitBucketTimeout = mrpsptr->ScriptBitBucketTimeout;

   if (!rqpsptr->ScriptCpuMax)
      rqpsptr->ScriptCpuMax = mrpsptr->ScriptCpuMax;

   if (!rqpsptr->ScriptLifeTime)
      rqpsptr->ScriptLifeTime = mrpsptr->ScriptLifeTime;

   /* hmmm, bit of a kludge (but easy) */
   if (mrpsptr->TimeoutPersistent < 0)
      rqptr->PersistentRequest = false;
   else
      rqpsptr->TimeoutPersistent = mrpsptr->TimeoutPersistent;

   if (!rqpsptr->TimeoutNoProgress)
      rqpsptr->TimeoutNoProgress = mrpsptr->TimeoutNoProgress;

   if (!rqpsptr->TimeoutOutput)
      rqpsptr->TimeoutOutput = mrpsptr->TimeoutOutput;

   if (mrpsptr->NoStmLF)
      rqpsptr->StmLF = false;
   else
   if (mrpsptr->StmLF)
      rqpsptr->StmLF = true;

   if (mrpsptr->SSLCGIvar)
   {
      if (rqpsptr->SSLCGIvar == SESOLA_CGI_VAR_NONE)
         rqpsptr->SSLCGIvar = 0;
      else
         rqpsptr->SSLCGIvar = mrpsptr->SSLCGIvar;
   }

   if (mrpsptr->PathOds) rqpsptr->PathOds = mrpsptr->PathOds;

   if (mrpsptr->OdsName)
      if (mrpsptr->OdsName == MAPURL_ODS_DEFAULT)
         rqpsptr->OdsName = 0;
      else
         rqpsptr->OdsName = mrpsptr->OdsName;
 
   /* allowed to be an empty string */
   if (mrpsptr->CgiPrefixPtr)
   {
      rqpsptr->CgiPrefixPtr =
         VmGetHeap (rqptr, mrpsptr->CgiPrefixLength+1);
      memcpy (rqpsptr->CgiPrefixPtr,
              mrpsptr->CgiPrefixPtr,
              mrpsptr->CgiPrefixLength+1);
   }

   /* allowed to be an empty string (suppresses charset) */
   if (mrpsptr->CharsetPtr)
   {
      rqpsptr->CharsetPtr =
         VmGetHeap (rqptr, mrpsptr->CharsetLength+1);
      memcpy (rqpsptr->CharsetPtr,
              mrpsptr->CharsetPtr,
              mrpsptr->CharsetLength+1);
   }

   /* empty string resets (reverts type, must have a content type) */
   if (mrpsptr->ContentTypePtr)
   {
      if (mrpsptr->ContentTypeLength)
      {
         rqpsptr->ContentTypePtr =
            VmGetHeap (rqptr, mrpsptr->ContentTypeLength+1);
         memcpy (rqpsptr->ContentTypePtr,
                 mrpsptr->ContentTypePtr,
                 mrpsptr->ContentTypeLength+1);
      }
      else
         rqpsptr->ContentTypePtr = NULL;
   }

   /* allowed to be an empty string (suppresses charset) */
   if (mrpsptr->DirCharsetPtr)
   {
      rqpsptr->DirCharsetPtr =
         VmGetHeap (rqptr, mrpsptr->DirCharsetLength+1);
      memcpy (rqpsptr->DirCharsetPtr,
              mrpsptr->DirCharsetPtr,
              mrpsptr->DirCharsetLength+1);
   }

   /* allowed to be an empty string */
   if (mrpsptr->HttpAcceptCharsetPtr)
   {
      if (mrpsptr->HttpAcceptCharsetPtr[0])
      {
         rqptr->rqHeader.AcceptCharsetPtr =
            VmGetHeap (rqptr, mrpsptr->HttpAcceptCharsetLength+1);
         memcpy (rqptr->rqHeader.AcceptCharsetPtr,
                 mrpsptr->HttpAcceptCharsetPtr,
                 mrpsptr->HttpAcceptCharsetLength+1);
      }
      else
      {
         /* empty, as if it had never been part of the request header */
         rqptr->rqHeader.AcceptCharsetPtr = NULL;
      }
   }

   /* allowed to be an empty string */
   if (mrpsptr->HttpAcceptLangPtr)
   {
      if (mrpsptr->HttpAcceptLangPtr[0])
      {
         rqptr->rqHeader.AcceptLangPtr =
            VmGetHeap (rqptr, mrpsptr->HttpAcceptLangLength+1);
         memcpy (rqptr->rqHeader.AcceptLangPtr,
                 mrpsptr->HttpAcceptLangPtr,
                 mrpsptr->HttpAcceptLangLength+1);
      }
      else
      {
         /* empty, as if it had never been part of the request header */
         rqptr->rqHeader.AcceptLangPtr = NULL;
      }
   }

   if (mrpsptr->IndexPtr)
   {
      /* empty string resets */
      if (mrpsptr->IndexLength)
      {
         rqpsptr->IndexPtr =
            VmGetHeap (rqptr, mrpsptr->IndexLength+1);
         memcpy (rqpsptr->IndexPtr,
                 mrpsptr->IndexPtr,
                 mrpsptr->IndexLength+1);
      }
      else
         rqpsptr->IndexPtr = NULL;
   }

   if (mrpsptr->DictionaryPtr)
   {
      MetaConDictionary (rqptr, mrpsptr->DictionaryPtr);
      if (WATCHPNT(rqptr) && (WATCH_CATEGORY(WATCH_MAPPING) &&
                              WATCH_CATEGORY(WATCH_INTERNAL)))
         DictWatch (rqptr->rqDictPtr, DICT_TYPE_CONFIG, "*");
   }

   if (mrpsptr->NotePadPtr)
   {
      if (WATCHING (rqptr, WATCH_MAPPING))
      {
         MetaConNotePad (rqptr, mrpsptr->NotePadPtr, true);
         WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "NOTEPAD !AZ",
                    rqptr->NotePadPtr != NULL ? rqptr->NotePadPtr : "(empty)");
      }
      else
         MetaConNotePad (rqptr, mrpsptr->NotePadPtr, false);
   }

   if (mrpsptr->ProxyReverseLocationPtr)
   {
      /* empty string resets */
      if (mrpsptr->ProxyReverseLocationLength)
      {
         rqpsptr->ProxyReverseLocationPtr =
            VmGetHeap (rqptr, mrpsptr->ProxyReverseLocationLength+1);
         memcpy (rqpsptr->ProxyReverseLocationPtr,
                 mrpsptr->ProxyReverseLocationPtr,
                 mrpsptr->ProxyReverseLocationLength+1);
      }
      else
         rqpsptr->ProxyReverseLocationPtr = NULL;
   }

   /* query string is allowed to be set empty */
   if (mrpsptr->QueryStringPtr)
   {
      if (rqptr->rqHeader.QueryStringLength < mrpsptr->QueryStringLength)
         rqptr->rqHeader.QueryStringPtr =
            VmGetHeap (rqptr, mrpsptr->QueryStringLength+1);
      memcpy (rqptr->rqHeader.QueryStringPtr,
              mrpsptr->QueryStringPtr,
              mrpsptr->QueryStringLength+1);
      rqptr->rqHeader.QueryStringLength = mrpsptr->QueryStringLength;
   }

   /* also see DCL callout "CSP:" */
   if (mrpsptr->ResponseCspLength < 0)
   {
      rqpsptr->ResponseCspPtr = NULL;
      rqpsptr->ResponseCspLength = 0;
   }
   else
   if (length = mrpsptr->ResponseCspLength)
   {
      cptr = mrpsptr->ResponseCspPtr;
      rqpsptr->ResponseCspPtr = sptr =
         VmReallocHeap (rqptr, rqpsptr->ResponseCspPtr,
                        rqpsptr->ResponseCspLength + length+2, FI_LI);
      if (rqpsptr->ResponseCspLength)
      {
         sptr += rqpsptr->ResponseCspLength;
         *sptr++ = ' ';
      }
      memcpy (sptr, cptr, length);
      sptr += length;
      rqpsptr->ResponseCspLength = sptr - rqpsptr->ResponseCspPtr;
   }

   /* also see DCL callout "CSPRO:" */
   if (mrpsptr->ResponseCsproLength < 0)
   {
      rqpsptr->ResponseCsproPtr = NULL;
      rqpsptr->ResponseCsproLength = 0;
   }
   else
   if (length = mrpsptr->ResponseCsproLength)
   {
      cptr = mrpsptr->ResponseCsproPtr;
      rqpsptr->ResponseCsproPtr = sptr =
         VmReallocHeap (rqptr, rqpsptr->ResponseCsproPtr,
                        rqpsptr->ResponseCsproLength + length+2, FI_LI);
      if (rqpsptr->ResponseCsproLength)
      {
         sptr += rqpsptr->ResponseCsproLength;
         *sptr++ = ' ';
      }
      memcpy (sptr, cptr, length);
      sptr += length;
      rqpsptr->ResponseCsproLength = sptr - rqpsptr->ResponseCsproPtr;
   }

   /* leave this in for backward compatibility (but use empty string) */
   if (mrpsptr->ResponseHeaderNoAdd)
   {
      rqpsptr->ResponseHeaderAddPtr = NULL;
      rqpsptr->ResponseHeaderAddLength = 0;
   }

   if (mrpsptr->ResponseHeaderAddPtr &&
       !mrpsptr->ResponseHeaderAddLength)
   {
      /* empty string resets */
      rqpsptr->ResponseHeaderAddPtr = NULL;
      rqpsptr->ResponseHeaderAddLength = 0;
   }
   else
   if (length = mrpsptr->ResponseHeaderAddLength)
   {
      cptr = mrpsptr->ResponseHeaderAddPtr;
      rqpsptr->ResponseHeaderAddPtr = sptr =
         VmReallocHeap (rqptr, rqpsptr->ResponseHeaderAddPtr,
                        rqpsptr->ResponseHeaderAddLength +
                        mrpsptr->ResponseHeaderAddLength+2, FI_LI);
      sptr += rqpsptr->ResponseHeaderAddLength;
      memcpy (sptr, cptr, length);
      sptr += length;
      SET2(sptr,'\n\0');
      sptr++;
      rqpsptr->ResponseHeaderAddLength = sptr - rqpsptr->ResponseHeaderAddPtr;
   }

   if (mrpsptr->ResponseVarRecord)
   {
      if (mrpsptr->ResponseVarRecord == FILE_VAR_DEFAULT)
         rqpsptr->ResponseVarRecord = 0;
      else
         rqpsptr->ResponseVarRecord = mrpsptr->ResponseVarRecord;
   }

   if (mrpsptr->ResponseStrictTransSecPtr)
   {
      /* must start with an integer or suppresses header */
      if (mrpsptr->ResponseStrictTransSecLength)
      {
         rqpsptr->ResponseStrictTransSecPtr =
            VmGetHeap (rqptr, mrpsptr->ResponseStrictTransSecLength+1);
         memcpy (rqpsptr->ResponseStrictTransSecPtr,
                 mrpsptr->ResponseStrictTransSecPtr,
                 mrpsptr->ResponseStrictTransSecLength+1);
      }
      else
         rqpsptr->ResponseStrictTransSecPtr = NULL;
   }

   if (mrpsptr->ScriptCommandPtr)
   {
      /* empty string resets */
      if (mrpsptr->ScriptCommandLength)
      {
         rqpsptr->ScriptCommandPtr =
            VmGetHeap (rqptr, mrpsptr->ScriptCommandLength+1);
         memcpy (rqpsptr->ScriptCommandPtr,
                 mrpsptr->ScriptCommandPtr,
                 mrpsptr->ScriptCommandLength+1);
      }
      else
         rqpsptr->ScriptCommandPtr = NULL;
   }

   if (mrpsptr->ScriptControlPtr)
   {
      /* empty string resets */
      if (mrpsptr->ScriptControlLength)
      {
         rqpsptr->ScriptControlPtr =
            VmGetHeap (rqptr, mrpsptr->ScriptControlLength+1);
         memcpy (rqpsptr->ScriptControlPtr,
                 mrpsptr->ScriptControlPtr,
                 mrpsptr->ScriptControlLength+1);
      }
      else
         rqpsptr->ScriptControlPtr = NULL;
   }

   if (mrpsptr->ScriptDefaultPtr)
   {
      /* empty string resets */
      if (mrpsptr->ScriptDefaultLength)
      {
         rqpsptr->ScriptDefaultPtr =
            VmGetHeap (rqptr, mrpsptr->ScriptDefaultLength+1);
         memcpy (rqpsptr->ScriptDefaultPtr,
                 mrpsptr->ScriptDefaultPtr,
                 mrpsptr->ScriptDefaultLength+1);
      }
      else
         rqpsptr->ScriptDefaultPtr = NULL;
   }

   if (mrpsptr->ScriptParamsPtr &&
       !mrpsptr->ScriptParamsLength)
   {
      /* empty string resets */
      mrpsptr->ScriptParamsPtr = NULL;
      mrpsptr->ScriptParamsLength = 0;
   }
   else
   if (mrpsptr->ScriptParamsPtr)
   {
      rqpsptr->ScriptParamsPtr = sptr =
         VmReallocHeap (rqptr, rqpsptr->ScriptParamsPtr,
                        rqpsptr->ScriptParamsLength +
                        mrpsptr->ScriptParamsLength+1, FI_LI);
      sptr += rqpsptr->ScriptParamsLength;
      cptr = mrpsptr->ScriptParamsPtr;
      length = mrpsptr->ScriptParamsLength;
      if (*cptr == '+')
      {
         cptr++;
         length--;
      }
      memcpy (sptr, cptr, length);
      rqpsptr->ScriptParamsLength += length;
   }

   if (mrpsptr->ChangeServicePtr)
   {
      /* empty string resets */
      if (mrpsptr->ChangeServiceLength)
      {
         rqpsptr->ChangeServicePtr =
            VmGetHeap (rqptr, mrpsptr->ChangeServiceLength+1);
         memcpy (rqpsptr->ChangeServicePtr,
                 mrpsptr->ChangeServicePtr,
                 mrpsptr->ChangeServiceLength+1);
      }
      else
         rqpsptr->ChangeServicePtr = NULL;
   }

   if (mrpsptr->SsiExecPtr)
   {
      /* empty string resets */
      if (mrpsptr->SsiExecLength)
      {
         rqpsptr->SsiExecPtr =
            VmGetHeap (rqptr, mrpsptr->SsiExecLength+1);
         memcpy (rqpsptr->SsiExecPtr,
                 mrpsptr->SsiExecPtr,
                 mrpsptr->SsiExecLength+1);
      }
      else
         rqpsptr->SsiExecPtr = NULL;
   }

   if (mrpsptr->StyleSheetPtr)
   {
      /* empty string resets */
      if (mrpsptr->StyleSheetLength)
      {
         rqpsptr->StyleSheetPtr =
            VmGetHeap (rqptr, mrpsptr->StyleSheetLength+1);
         memcpy (rqpsptr->StyleSheetPtr,
                 mrpsptr->StyleSheetPtr,
                 mrpsptr->StyleSheetLength+1);
      }
      else
         rqpsptr->StyleSheetPtr = NULL;
   }

   if (mrpsptr->CorsNoAllowCredentials)
      rqpsptr->CorsAllowCredentials = false;
   else
   if (mrpsptr->CorsAllowCredentials)
      rqpsptr->CorsAllowCredentials = true;

   if (mrpsptr->CorsAllowHeadersPtr)
   {
      /* empty string resets */
      if (mrpsptr->CorsAllowHeadersLength)
      {
         rqpsptr->CorsAllowHeadersPtr =
            VmGetHeap (rqptr, mrpsptr->CorsAllowHeadersLength+1);
         memcpy (rqpsptr->CorsAllowHeadersPtr,
                 mrpsptr->CorsAllowHeadersPtr,
                 mrpsptr->CorsAllowHeadersLength+1);
      }
      else
         rqpsptr->CorsAllowHeadersPtr = NULL;
   }

   if (mrpsptr->CorsAllowMethodsPtr)
   {
      /* empty string resets */
      if (mrpsptr->CorsAllowMethodsLength)
      {
         rqpsptr->CorsAllowMethodsPtr =
            VmGetHeap (rqptr, mrpsptr->CorsAllowMethodsLength+1);
         memcpy (rqpsptr->CorsAllowMethodsPtr,
                 mrpsptr->CorsAllowMethodsPtr,
                 mrpsptr->CorsAllowMethodsLength+1);
      }
      else
         rqpsptr->CorsAllowMethodsPtr = NULL;
   }

   if (mrpsptr->CorsAllowOriginPtr)
   {
      /* empty string resets */
      if (mrpsptr->CorsAllowOriginLength)
      {
         rqpsptr->CorsAllowOriginPtr =
            VmGetHeap (rqptr, mrpsptr->CorsAllowOriginLength+1);
         memcpy (rqpsptr->CorsAllowOriginPtr,
                 mrpsptr->CorsAllowOriginPtr,
                 mrpsptr->CorsAllowOriginLength+1);
      }
      else
         rqpsptr->CorsAllowOriginPtr = NULL;
   }

   if (mrpsptr->CorsExposeHeadersPtr)
   {
      /* empty string resets */
      if (mrpsptr->CorsExposeHeadersLength)
      {
         rqpsptr->CorsExposeHeadersPtr =
            VmGetHeap (rqptr, mrpsptr->CorsExposeHeadersLength+1);
         memcpy (rqpsptr->CorsExposeHeadersPtr,
                 mrpsptr->CorsExposeHeadersPtr,
                 mrpsptr->CorsExposeHeadersLength+1);
      }
      else
         rqpsptr->CorsExposeHeadersPtr = NULL;
   }

   if (mrpsptr->CorsMaxAge)
   {
      /* negative 1 indicates to revert to configuration maximum */
      if (mrpsptr->CorsMaxAge == -1)
         rqpsptr->CorsMaxAge = 0;
      else
         rqpsptr->CorsMaxAge = mrpsptr->CorsMaxAge;
   }

   if (IPADDRESS_IS_RESET(&rqpsptr->ProxyBindIpAddress) &&
       IPADDRESS_IS_SET(&mrpsptr->ProxyBindIpAddress))
      rqpsptr->ProxyBindIpAddress = mrpsptr->ProxyBindIpAddress;

   if (IPADDRESS_IS_RESET(&rqpsptr->ProxyChainIpAddress) &&
       IPADDRESS_IS_SET(&mrpsptr->ProxyChainIpAddress))
   {
      IPADDRESS_COPY (&rqpsptr->ProxyChainIpAddress,
                      &mrpsptr->ProxyChainIpAddress);
      rqpsptr->ProxyChainPort = mrpsptr->ProxyChainPort;
      if (mrpsptr->ProxyChainHostPortPtr &&
          mrpsptr->ProxyChainHostPortPtr[0])
      {
         rqpsptr->ProxyChainHostPortPtr =
            VmGetHeap (rqptr, mrpsptr->ProxyChainHostPortLength+1);
         memcpy (rqpsptr->ProxyChainHostPortPtr,
                 mrpsptr->ProxyChainHostPortPtr,
                 mrpsptr->ProxyChainHostPortLength+1);
      }
   }

   if (mrpsptr->ProxyChainCredPtr)
   {
      /* empty string resets */
      if (mrpsptr->ProxyChainCredLength)
      {
         rqpsptr->ProxyChainCredPtr =
            VmGetHeap (rqptr, mrpsptr->ProxyChainCredLength+1);
         memcpy (rqpsptr->ProxyChainCredPtr,
                 mrpsptr->ProxyChainCredPtr,
                 mrpsptr->ProxyChainCredLength+1);
      }
      else
         rqpsptr->ProxyChainCredPtr = NULL;
   }
}

/*****************************************************************************/
/*
Modify the client's IP address data using the source indicated by the path
setting.  Used to support a transparent load balancer/accelerator/proxy
upstream from the server while retaining the original client address data. 
Return a NULL pointer to indicate success.  Return an error string if it fails
for some reason (e.g. header is not present).  A fail should abort mapping and
report the error.
*/ 
 
char* MapUrl_SetClientAddress (REQUEST_STRUCT *rqptr)

{
   int  ClientAddress;
   char  *cptr, *hptr, *sptr, *zptr;
   char  strbuf [256];
   CLIENT_STRUCT  *clptr;
   IPADDRESS  IpAddress;
   REQUEST_PATHSET  *rqpsptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_MAPURL))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL,
                 "MapUrl_SetClientAddress()");

   /* do not reproduce again if multiple client data mappings */
   if (rqptr->ClientPtr->SetClientAddress)
      clptr = rqptr->ClientPtr;
   else
   {
      /* reproduce the existing client data and then use that */
      clptr = (CLIENT_STRUCT*)VmGetHeap (rqptr, sizeof(CLIENT_STRUCT));
      memcpy (clptr, &rqptr->ClientPtr, sizeof(CLIENT_STRUCT));
      rqptr->ClientResetPtr = rqptr->ClientPtr;
      rqptr->ClientPtr = clptr;
   }

   rqpsptr = &rqptr->rqPathSet;

   ClientAddress = rqpsptr->ClientAddress;
   rqpsptr->ClientAddress = 0;

   if (ClientAddress == clptr->SetClientAddress &&
       ClientAddress != MAPURL_CLIENT_LITERAL)
   {
      if (WATCHING (rqptr, WATCH_MAPPING))
         WatchThis (WATCHITM(rqptr), WATCH_MAPPING,
                    "CLIENT already set to this!!");
      return (NULL);
   }

   if (ClientAddress == MAPURL_CLIENT_XFORWARDEDFOR ||
       ClientAddress == MAPURL_CLIENT_IF_XFORWARDEDFOR)
   {
      cptr = rqptr->rqHeader.XForwardedForPtr;
      hptr = "X-Forwarded-For:";
   }
   else
   if (ClientAddress == MAPURL_CLIENT_FORWARDED ||
       ClientAddress == MAPURL_CLIENT_IF_FORWARDED)
   {
      cptr = rqptr->rqHeader.ForwardedPtr;
      hptr = "Forwarded:";
   }
   else
   if (ClientAddress == MAPURL_CLIENT_LITERAL)
   {
      /* really just for ease of testing */
      cptr = rqpsptr->ClientAddressLiteralPtr;
      hptr = "X-Literal:";
   }
   else
   {
      ErrorNoticed (rqptr, SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
      return (MsgFor(rqptr,MSG_MAPPING_DENIED_INTERNAL)-1);
   }

   if (!cptr)
   {
      if (WATCHING (rqptr, WATCH_MAPPING))
         WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "CLIENT no !AZ", hptr);
      if (ClientAddress == MAPURL_CLIENT_IF_XFORWARDEDFOR ||
          ClientAddress == MAPURL_CLIENT_IF_FORWARDED) return (NULL);
      return (MsgFor(rqptr,MSG_MAPPING_DENIED_RULE)-1);
   }

   /* parse out the leading (IP address) string representing the client */
   zptr = (sptr = strbuf) + sizeof(strbuf)-1;
   while (ISLWS(*cptr)) cptr++;
   if (MATCH4(cptr,"for=")) cptr += 4;
   if (*cptr == '\"')
      for (cptr++; *cptr && *cptr != '\"' && sptr < zptr; *sptr++ = *cptr++);
   else
      while (*cptr && *cptr != ',' && !ISLWS(*cptr) && sptr < zptr)
         *sptr++ = *cptr++;
   *sptr = '\0';

   if (VMSnok (TcpIpStringToAddress (strbuf, &IpAddress)))
   {
      if (WATCHING (rqptr, WATCH_MAPPING))
         WatchThis (WATCHITM(rqptr), WATCH_MAPPING,
                    "CLIENT address error !AZ !AZ", hptr, strbuf);
      return (MsgFor(rqptr,MSG_MAPPING_DENIED_RULE)-1);
   }

   /* when not multiple client data mapping */
   if (!clptr->SetClientAddress)
   {
      /* buffer the TCP connection data (see MapUrl_ResetClientData()) */
      IPADDRESS_COPY (&clptr->UpstreamIpAddress, &clptr->IpAddress);
      strcpy (clptr->UpstreamIpAddressString, &clptr->IpAddressString);
      strcpy (clptr->UpstreamHostName, clptr->Lookup.HostName);
      clptr->UpstreamHostNameLength = clptr->Lookup.HostNameLength;
   }

   /* rewrite the client address data */
   clptr->SetClientAddress = ClientAddress;
   IPADDRESS_COPY (&clptr->IpAddress, &IpAddress);
   /* recreate a "clean" IP address string from the IP address above */
   cptr = TcpIpAddressToString (&IpAddress, 0);
   strcpy (&clptr->IpAddressString, cptr);
   strcpy (clptr->Lookup.HostName, cptr);
   clptr->Lookup.HostNameLength = strlen(cptr);

   if (WATCHMOD (rqptr, WATCH_MOD_MAPURL))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL,
                 "!AZ !&I !AZ !AZ -> !&I !AZ !AZ",
                 hptr, &clptr->UpstreamIpAddress,
                       clptr->UpstreamIpAddressString,
                       clptr->UpstreamHostName,
                       &clptr->IpAddress,
                       &clptr->IpAddressString,
                       clptr->Lookup.HostName);

   if (WATCHING (rqptr, WATCH_MAPPING))
      WatchThis (WATCHITM(rqptr), WATCH_MAPPING, "CLIENT !AZ !AZ", hptr, cptr);

   if (Watch.Category && Watch.Category != WATCH_ONE_SHOT_CAT)
      WatchFilterClientService (rqptr);

   return (NULL);
}

/*****************************************************************************/
/*
Undo the client address mapping performed by MapUrl_SetClientAddress() above by
simply pointing back using the saved original.
*/ 
 
void MapUrl_ResetClientAddress (REQUEST_STRUCT *rqptr)

{
   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_MAPURL))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL,
                 "MapUrl_ResetClientAddress()");

   rqptr->ClientPtr = rqptr->ClientResetPtr;
}

/*****************************************************************************/
/*
Load mapping rules into meta-config structure.
*/ 
 
int MapUrl_ConfigLoad (META_CONFIG **MetaConPtrPtr)

{
   int  status;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_MAPURL))
      WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapUrl_ConfigLoad()");

   status = MetaConLoad (MetaConPtrPtr,
                         v10orPrev10(CONFIG_MAP_FILE_NAME,-1),
                         &MapUrl_ConfigLoadCallBack, true, true);
   if (*MetaConPtrPtr == MetaGlobalMappingPtr)
   {
      /* server startup/reload */
      MetaConStartupReport (MetaGlobalMappingPtr, "MAP");
      if (VMSnok (status)) exit (status);
   }
   return (status);
}

/*****************************************************************************/
/*
Called by MetaConUnload() to free resources allocated during mapping rule
configuration.
*/ 
 
MapUrl_ConfigUnload (META_CONFIG *mcptr)

{
   int  status;
   MAPPING_META  *mmptr;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_MAPURL))
      WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapUrl_ConfigUnload()");

   if (mcptr->MappingMetaPtr)
   {
      if (mcptr->MappingMetaPtr == MappingMetaPtr)
      {
         memset (MappingMetaPtr, 0, sizeof(MAPPING_META));
         MappingMetaPtr = NULL;
      }
      else
         VmFree (mcptr->MappingMetaPtr, FI_LI);
      mcptr->MappingMetaPtr = NULL;
   }
}

/*****************************************************************************/
/*
Called by MetaConUnload() callback for each line's associated data, basically
to check for a regular expression structure and free it if present, then just
dispose of the line data itself.
*/ 
 
MapUrl_ConfigUnloadLineData (void *LineDataPtr)

{
   int  status;
   MAP_RULE_META  *mrptr;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_MAPURL))
      WatchThis (WATCHALL, WATCH_MOD_MAPURL,
                 "MapUrl_ConfigUnloadLineData()");

   mrptr = (MAP_RULE_META*)LineDataPtr;
   if (mrptr->RegexPregTemplate.buffer) regfree (&mrptr->RegexPregTemplate);
   VmFree (mrptr, FI_LI);
}

/*****************************************************************************/
/*
For each non-meta-config directive line read by MetaConLoad() this function is
called to parse the line text's contents and to configure the private data
structure associated with each rule.
*/ 

BOOL MapUrl_ConfigLoadCallBack (META_CONFIG *mcptr)

{
   static char  Ods5Disabled [] = "ODS-5 processing CLI disabled",
                Ods5Enabled [] = "ODS-5 processing enabled",
                Ods5NotSupported [] = "ODS-5 not supported",
                ProblemAcceptLang [] =
                   "Set path \'accept=lang\' parameter problem",
                ProblemConditional [] = "Conditional problem",
                ProblemConfused [] = "Cannot understand this one at all",
                ProblemHostNameLookup [] = "Cannot resolve host name !AZ, !&m",
                ProblemNameValue [] = "Set path \'name=value\' pair problem",
                ProblemNotAllowed [] = "Rule cannot be used in [ConfigFile]",
                ProblemOverflow [] = "Storage overflow",
                ProblemProtect [] = "Protect path problem",
                ProblemProxyHeader [] = "Proxy header exceeded maximum",
                ProblemRegex [] = "Regex: !AZ",
                ProblemResultPath [] = "Problem with \'result\' path",
                ProblemResultRequired [] = "Requires a \'result\'",
                ProblemResultStatus [] = "Rule \'result\' cannot be a status",
                ProblemRmsSubstitution [] = "RMS substitution character",
                ProblemSetPath [] = "Set path problem",
                ProblemSpecifiedWildcardCannot [] =
                   "Cannot use \'specified\' wildcards with rule",
                ProblemSpecifiedWildcardMix [] =
                   "Mix of \'specified\' and non-\'specified\' wildcards",
                ProblemSpecifiedWildcardRange [] =
                   "Value of \'specified\' wildcard out-of-range",
                ProblemTemplateRequired [] = "Requires a \'template\'",
                ProblemThrottleValues [] =
                   "Successive dependent throttle values must be larger",
                ProblemWildcardMapping [] = "Wildcard mapping problem",
                ProblemTooManyWildcard [] = "Too many wildcards";

   BOOL  AllowedInConfigFile,
         ConfigProblem,
         IsCgiPlusScript,
         PathSet,
         VirtualServerUnknown;
   int  length, status,
        ConfigFileIndex,
        ResultPathOds,
        RuleType,
        TemplateWildcardCount,
        ResultWildcardCount,
        ResultSpecifiedWildcardCount;
   unsigned short  Length;
   char  *cptr, *sptr, *zptr,
         *ConditionalPtr,
         *ResultPtr,
         *RulePtr,
         *SetPathPtr,
         *TemplatePtr;
   char  Name [256],
         DiskDevice [64+1],
         Value [1024];
   MAPPING_META  *mmptr;
   MAP_RULE_META  *mrptr;
   MAP_SET_META  *mrpsptr;
   METACON_LINE  *mclptr;
   regex_t  RegexPreg;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_MAPURL))
   {
      WatchThis (WATCHALL, WATCH_MOD_MAPURL,
                 "MapUrl_ConfigLoadCallBack() !&F !&X",
                 &MapUrl_ConfigLoadCallBack, mcptr);
      if (WATCH_MODULE(WATCH_MOD__DETAIL))
      {
         mclptr = mcptr->ParsePtr;
         WatchDataFormatted ("!&X !UL !UL !UL !UL !&X !&Z !&Z\n",
            mclptr, mclptr->Size, mclptr->Token, mclptr->Number,
            mclptr->Length, mclptr->LineDataPtr, mclptr->TextPtr,
            mclptr->InlineTextPtr);
      }
   }

   /* get a pointer to the current "line" */
   mclptr = mcptr->ParsePtr;

   /* if this is during server startup/reload set the global service pointer */
   if (mcptr == MetaGlobalMappingPtr)
      mmptr = mcptr->MappingMetaPtr = MappingMetaPtr = &MappingMeta;
   else
   /* if a report then conjure one up through quantum mechanics */
   if (!mcptr->MappingMetaPtr)
      mmptr = mcptr->MappingMetaPtr = VmGet (sizeof(MAPPING_META));
   else
      /* not the first time through */
      mmptr = mcptr->MappingMetaPtr;

   if (mclptr->Token == METACON_TOKEN_PRE)
   {
      /******************/
      /* pre-initialize */
      /******************/

      MapUrlPathOds5 = OdsExtended = false;

      return (true);
   }

   if (mclptr->Token == METACON_TOKEN_POST)
   {
      /****************/
      /* post-process */
      /****************/

      /* if global service pointer, during server startup or reload */
      if (mcptr == MetaGlobalMappingPtr)
      {
         /* if no ODS5 paths then EFS can be disabled */
#ifdef ODS_EXTENDED
         if (CliOdsExtendedDisabled)
         {
            OdsExtended = false;
            MetaConReport (mcptr, METACON_REPORT_INFORM, Ods5Disabled);
         }
         else
#endif /* ODS_EXTENDED */
         if (CliOdsExtendedEnabled || MapUrlPathOds5)
         {
#ifdef ODS_EXTENDED
            if (SysInfo.VersionInteger >= 720)
            {
               OdsExtended = true;
               MetaConReport (mcptr, METACON_REPORT_INFORM, Ods5Enabled);
            }
            else
#endif /* ODS_EXTENDED */
            {
               OdsExtended = false;
               MetaConReport (mcptr, METACON_REPORT_INFORM, Ods5NotSupported);
            }
         }
         else
            OdsExtended = false;

         /* reset the username cache */
         MapUserNameCache (NULL, NULL, NULL, NULL);

         /* initialize the throttled paths */
         ThrottleInit ();
      }

      return (true);
   }

   /***********/
   /* process */
   /***********/

   /* if it's not text/inline then mapping's not interested in it */
   if (mclptr->Token != METACON_TOKEN_TEXT && !mclptr->InlineTextPtr)
      return (true);

   if (mclptr->InlineTextPtr)
      cptr = mclptr->InlineTextPtr;
   else
      cptr = mclptr->TextPtr;
   for (sptr = cptr; *sptr; sptr++);

   /* allocate memory for the structure plus the null terminated strings */
   mrptr = (MAP_RULE_META*)VmGet (sizeof(MAP_RULE_META)+(sptr-cptr+1));

   /* copy to storage the configuration string */
   sptr = &mrptr->Storage;
   while (*cptr) *sptr++ = *cptr++;
   *sptr = '\0';

   /* point to the MAP_RULE_META */
   mrpsptr = &mrptr->mpPathSet;

   AllowedInConfigFile = ConfigProblem = IsCgiPlusScript = PathSet = false;

   /********/
   /* rule */
   /********/

   for (cptr = &mrptr->Storage; *cptr && ISLWS(*cptr); cptr++);

   RulePtr = cptr;
   if (StringSliceValue (&cptr) <= 0)
   {
      MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemConfused);
      VmFree (mrptr, FI_LI);
      return (true);
   }

   IsCgiPlusScript = false;
   if (strsame (RulePtr, "EXEC", -1))
      RuleType = MAPURL_RULE_EXEC;
   else
   if (strsame (RulePtr, "EXEC+", -1))
   {
      RuleType = MAPURL_RULE_EXEC;
      IsCgiPlusScript = true;
   }
   else
   if (strsame (RulePtr, "FAIL", -1))
   {
      RuleType = MAPURL_RULE_FAIL;
      AllowedInConfigFile = true;
   }
   else
   if (strsame (RulePtr, "MAP", -1))
   {
      RuleType = MAPURL_RULE_MAP;
      AllowedInConfigFile = true;
   }
   else
   if (strsame (RulePtr, "PASS", -1))
   {
      RuleType = MAPURL_RULE_PASS;
      AllowedInConfigFile = true;
   }
   else
   if (strsame (RulePtr, "REDIRECT", -1))
   {
      RuleType = MAPURL_RULE_REDIRECT;
      AllowedInConfigFile = true;
   }
   else
   if (strsame (RulePtr, "SCRIPT", -1))
      RuleType = MAPURL_RULE_SCRIPT;
   else
   if (strsame (RulePtr, "SCRIPT+", -1))
   {
      RuleType = MAPURL_RULE_SCRIPT;
      IsCgiPlusScript = true;
   }
   else
   if (strsame (RulePtr, "SET", -1))
      RuleType = MAPURL_RULE_SET;
   else
   if (strsame (RulePtr, "UXEC", -1) ||
       strsame (RulePtr, "UEXEC", -1))
      RuleType = MAPURL_RULE_UXEC;
   else
   if (strsame (RulePtr, "UXEC+", -1) ||
       strsame (RulePtr, "UEXEC+", -1))
   {
      RuleType = MAPURL_RULE_UXEC;
      IsCgiPlusScript = true;
   }
   else
   if (strsame (RulePtr, "USER", -1))
      RuleType = MAPURL_RULE_USER;
   else
   if (strsame (RulePtr, "PROTECT", -1))
   {
      RuleType = MAPURL_RULE_PROTECT;
      AllowedInConfigFile = true;
   }
   else
   {
      MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemConfused);
      VmFree (mrptr, FI_LI);
      return (true);
   }

   /* ensure a conditional is not mistaken for a missing template */
   if (!*cptr || *cptr == '[' || SAME2(cptr,'![')) 
   {
      /* there must be a "template" to map from */
      MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemTemplateRequired);
      VmFree (mrptr, FI_LI);
      return (true);
   }

   /* extract template (the path the rule is applied to) */
   TemplatePtr = cptr;
   if (StringSliceValue (&cptr) <= 0)
   {
      MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemTemplateRequired);
      VmFree (mrptr, FI_LI);
      return (true);
   }

   /* note the position of an empty string in case none of these! */
   ResultPtr = SetPathPtr = ConditionalPtr = RulePtr + strlen(RulePtr);

   if (RuleType != MAPURL_RULE_SET)
   {
      /*********************/
      /* path mapping rule */
      /*********************/

      /* if this doesn't look like a conditional */
      if (*cptr && *cptr != '[' && !SAME2(cptr,'!['))
      {
         ResultPtr = cptr;
         if (StringSliceValue (&cptr) <= 0)
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemConfused);
            VmFree (mrptr, FI_LI);
            return (true);
         }
      }
   }

   /* if it doesn't look like a conditional then assume it's SET directives */
   if (*cptr && *cptr != '[' && !SAME2(cptr,'!['))
   {
      /****************/
      /* path SETings */
      /****************/

      while (*cptr)
      {
         /* break if this looks like a conditional */
         if (*cptr == '[' || SAME2(cptr,'![')) break;

         SetPathPtr = cptr;
         if ((length = StringSliceValue (&cptr)) <= 0)
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemSetPath);
            VmFree (mrptr, FI_LI);
            return (true);
         }

         PathSet = true;
         if (strsame (SetPathPtr, "ACCEPT=LANG", -1))
            mrpsptr->AcceptLangChar = '_';
         else
         if (strsame (SetPathPtr, "NOACCEPT=LANG", -1) ||
             strsame (SetPathPtr, "ACCEPT=NOLANG", -1))
            mrpsptr->NoAcceptLang = true;
         else
         if (strsame (SetPathPtr, "ACCEPT=LANG=", 12))
         {
            mrpsptr->AcceptLangChar = '_';
            sptr = SetPathPtr + 12;
            if (*sptr == '(') sptr++;
            while (*sptr)
            {
               if (strsame (sptr, "CHAR=", 5))
               {
                  mrpsptr->AcceptLangChar = sptr[5];
                  sptr += 6;
               }
               else
               if (strsame (sptr, "DEFAULT=", 8))
               {
                  sptr += 8;
                  mrpsptr->AcceptLangPtr = sptr;
                  while (*sptr && *sptr != ',' && *sptr != ')') sptr++;
                  if (*sptr) *sptr++ = '\0';
                  mrpsptr->AcceptLangLength = strlen(mrpsptr->AcceptLangPtr);
               }
               else
               if (strsame (sptr, "VARIANT=", 8))
               {
                  sptr += 8;
                  if (strsame (sptr, "NAME", 4))
                     mrpsptr->AcceptLangTypeVariant = false;
                  else
                  if (strsame (sptr, "TYPE", 4))
                     mrpsptr->AcceptLangTypeVariant = true;
                  else
                  {
                     MetaConReport (mcptr, METACON_REPORT_ERROR,
                                    ProblemAcceptLang);
                     VmFree (mrptr, FI_LI);
                     return (true);
                  }
                  while (*sptr && *sptr != ',' && *sptr != ')') sptr++;
               }
               else
               {
                  MetaConReport (mcptr, METACON_REPORT_ERROR,
                                 ProblemAcceptLang);
                  VmFree (mrptr, FI_LI);
                  return (true);
               }
               if (*sptr == ',' || *sptr == ')') sptr++;
            }
            if (mrpsptr->AcceptLangChar != '_' &&
                mrpsptr->AcceptLangChar != '-' &&
                mrpsptr->AcceptLangChar != '.' &&
                mrpsptr->AcceptLangChar != '$')
            {
               MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemAcceptLang);
               VmFree (mrptr, FI_LI);
               return (true);
            }
            /* check if there is a wildcard file type */
            for (sptr = TemplatePtr; *sptr; sptr++);
            while (sptr > TemplatePtr && *sptr != '.' && *sptr != '/') sptr--;
            if (*sptr == '.') mrpsptr->AcceptLangWildcard = true;
         }
         else
         if (strsame (SetPathPtr, "ALERT", -1))
            mrpsptr->Alert = MAPURL_PATH_ALERT_END;
         else
         if (strsame (SetPathPtr, "ALERT=END", -1))
            mrpsptr->Alert = MAPURL_PATH_ALERT_END;
         else
         if (strsame (SetPathPtr, "ALERT=AUTH", -1))
            mrpsptr->Alert = MAPURL_PATH_ALERT_AUTH;
         else
         if (strsame (SetPathPtr, "ALERT=MAP", -1))
            mrpsptr->Alert = MAPURL_PATH_ALERT_MAP;
         else
         if (strsame (SetPathPtr, "ALERT=", 6) &&
             isdigit(SetPathPtr[6]))
         {
            mrpsptr->Alert = atoi(SetPathPtr+6);
            if (!mrpsptr->Alert) mrpsptr->NoAlert = true;
         }
         else
         if (strsame (SetPathPtr, "NOALERT", -1))
            mrpsptr->NoAlert = true;
         else
         if (strsame (SetPathPtr, "AUTH=ALL", -1))
            mrpsptr->AuthAll = true;
         else
         if (strsame (SetPathPtr, "NOAUTH=ALL", -1))
            mrpsptr->NoAuthAll = true;
         else
         if (strsame (SetPathPtr, "AUTH=MAPPED", -1))
            mrpsptr->AuthMapped = true;
         else
         if (strsame (SetPathPtr, "NOAUTH=MAPPED", -1) ||
             strsame (SetPathPtr, "AUTH=REQUEST", -1))
            mrpsptr->NoAuthMapped = true;
         else
         if (strsame (SetPathPtr, "AUTH=ONCE", -1) ||
             strsame (SetPathPtr, "AUTHONCE", -1))
            mrpsptr->AuthOnce = true;
         else
         if (strsame (SetPathPtr, "AUTH=NOONCE", -1) ||
             strsame (SetPathPtr, "NOAUTH=ONCE", -1) ||
             strsame (SetPathPtr, "NOAUTHONCE", -1))
            mrpsptr->NoAuthOnce = true;
         else
         if (strsame (SetPathPtr, "AUTH=REVALIDATE=", 16))
            mrpsptr->AuthRevalidateTimeout =
               MetaConSetSeconds (mcptr, SetPathPtr+16, 60);
         else
         if (strsame (SetPathPtr, "AUTH=SYSUAF=PWDEXPURL=", 22))
         {
            mrpsptr->AuthSysUafPwdExpUrlPtr = SetPathPtr + 22;
            mrpsptr->AuthSysUafPwdExpUrlLength =
               strlen(mrpsptr->AuthSysUafPwdExpUrlPtr);
         }
         else
         if (strsame (SetPathPtr, "CACHE", -1))
            mrpsptr->CacheSetting = mrpsptr->Cache = true;
         else
         if (strsame (SetPathPtr, "NOCACHE", -1) ||
             strsame (SetPathPtr, "CACHE=NONE", -1))
            mrpsptr->CacheSetting = mrpsptr->NoCache = true;
         else
         if (strsame (SetPathPtr, "CACHE=CGI", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheCGI = true;
         else
         if (strsame (SetPathPtr, "CACHE=NOCGI", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheNoCGI = true;
         else
         if (strsame (SetPathPtr, "CACHE=COOKIE", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheCookie = true;
         else
         if (strsame (SetPathPtr, "CACHE=NOCOOKIE", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheNoCookie = true;
         else
         if (strsame (SetPathPtr, "CACHE=FILE", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheFile = true;
         else
         if (strsame (SetPathPtr, "CACHE=NOFILE", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheNoFile = true;
         else
         if (strsame (SetPathPtr, "CACHE=NET", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheNet = true;
         else
         if (strsame (SetPathPtr, "CACHE=NONET", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheNoNet = true;
         else
         if (strsame (SetPathPtr, "CACHE=NPH", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheNPH = true;
         else
         if (strsame (SetPathPtr, "CACHE=NONPH", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheNoNPH = true;
         else
         if (strsame (SetPathPtr, "CACHE=PERM", 10))
            mrpsptr->CacheSetting = mrpsptr->CachePermanent = true;
         else
         if (strsame (SetPathPtr, "CACHE=NOPERM", 12))
            mrpsptr->CacheSetting = mrpsptr->CacheNoPermanent = true;
         else
         if (strsame (SetPathPtr, "CACHE=QUERY", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheQuery = true;
         else
         if (strsame (SetPathPtr, "CACHE=NOQUERY", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheNoQuery = true;
         else
         if (strsame (SetPathPtr, "CACHE=SCRIPT", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheScript = true;
         else
         if (strsame (SetPathPtr, "CACHE=NOSCRIPT", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheNoScript = true;
         else
         if (strsame (SetPathPtr, "CACHE=SSI", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheSSI = true;
         else
         if (strsame (SetPathPtr, "CACHE=NOSSI", -1))
            mrpsptr->CacheSetting = mrpsptr->CacheNoSSI = true;
         else
         if (strsame (SetPathPtr, "CACHE=EXPIRES=", 14))
         {
            sptr = SetPathPtr + 14;
            if (strsame (sptr, "DAY", -1))
               mrpsptr->CacheExpiresAfter = CACHE_EXPIRES_DAY;
            else
            if (strsame (sptr, "HOUR", -1))
               mrpsptr->CacheExpiresAfter = CACHE_EXPIRES_HOUR;
            else
            if (strsame (sptr, "MINUTE", -1))
               mrpsptr->CacheExpiresAfter = CACHE_EXPIRES_MINUTE;
            else
            if (strsame (sptr, "0", -1))
               mrpsptr->CacheExpiresAfter = CACHE_EXPIRES_NONE;
            else
               mrpsptr->CacheExpiresAfter =
                  MetaConSetSeconds (mcptr, sptr, 1);
            mrpsptr->CacheSetting = true;
         }
         else
         if (strsame (SetPathPtr, "CACHE=GUARD=", 12))
         {
            sptr = SetPathPtr + 12;
            mrpsptr->CacheGuardSeconds = MetaConSetSeconds (mcptr, sptr, 1);
            /* negative 1 indicates to revert to configuration maximum */
            if (!mrpsptr->CacheGuardSeconds) mrpsptr->CacheGuardSeconds = -1;
            mrpsptr->CacheSetting = true;
         }
         else
         if (strsame (SetPathPtr, "CACHE=MAX=", 10))
         {
            sptr = SetPathPtr + 10;
            mrpsptr->CacheMaxKBytes = atoi(sptr);
            while (isdigit(*sptr)) sptr++;
            if (strsame (sptr, "MB", 2))
               mrpsptr->CacheMaxKBytes = mrpsptr->CacheMaxKBytes << 10;
            /* negative 1 indicates to revert to configuration maximum */
            if (!mrpsptr->CacheMaxKBytes) mrpsptr->CacheMaxKBytes = -1;
            mrpsptr->CacheSetting = true;
         }
         else
         if (strsame (SetPathPtr, "CGI=PREFIX=", 11) ||
             strsame (SetPathPtr, "CGIPREFIX=", 10))
         {
            mrpsptr->CgiPrefixPtr = SetPathPtr + 10;
            if (*mrpsptr->CgiPrefixPtr == '=') mrpsptr->CgiPrefixPtr++;
            mrpsptr->CgiPrefixLength = strlen(mrpsptr->CgiPrefixPtr);
         }
         else
         if (strsame (SetPathPtr, "CGIPLUSIN=EOF", -1))
            mrpsptr->CgiPlusInWriteof = true;
         else
         if (strsame (SetPathPtr, "CGIPLUSIN=NOEOF", -1) ||
             strsame (SetPathPtr, "NOCGIPLUSIN=EOF", -1))
            mrpsptr->CgiPlusInNoWriteof = true;
         else
         if (strsame (SetPathPtr, "CGIPLUSIN=CC=NONE", -1))
            strcpy (mrpsptr->CgiPlusInCC, "\0**");
         else
         if (strsame (SetPathPtr, "CGIPLUSIN=CC=LF", -1))
            strcpy (mrpsptr->CgiPlusInCC, "\n\0*");
         else
         if (strsame (SetPathPtr, "CGIPLUSIN=CC=CR", -1))
            strcpy (mrpsptr->CgiPlusInCC, "\r\0*");
         else
         if (strsame (SetPathPtr, "CGIPLUSIN=CC=CRLF", -1))
            strcpy (mrpsptr->CgiPlusInCC, "\r\n\0");
         else
         if (strsame (SetPathPtr, "CHARSET=", 8))
         {
            mrpsptr->CharsetPtr = SetPathPtr + 8;
            mrpsptr->CharsetLength = strlen(mrpsptr->CharsetPtr);
            AllowedInConfigFile = true;
         }
         else
         if (strsame (SetPathPtr, "CLIENT=FORWARDED", -1))
            mrpsptr->ClientAddress = MAPURL_CLIENT_FORWARDED;
         else
         if (strsame (SetPathPtr, "CLIENT=IF=FORWARDED", -1))
            mrpsptr->ClientAddress = MAPURL_CLIENT_IF_FORWARDED;
         else
         if (strsame (SetPathPtr, "CLIENT=LITERAL=", 15))
         {
            /* really just for ease of testing */
            mrpsptr->ClientAddress = MAPURL_CLIENT_LITERAL;
            mrpsptr->ClientAddressLiteralPtr = SetPathPtr + 15;
            mrpsptr->ClientAddressLiteralLength =
               strlen(mrpsptr->ClientAddressLiteralPtr);
         }
         else
         if (strsame (SetPathPtr, "CLIENT=XFORWARDEDFOR", -1))
            mrpsptr->ClientAddress = MAPURL_CLIENT_XFORWARDEDFOR;
         else
         if (strsame (SetPathPtr, "CLIENT=IF=XFORWARDEDFOR", -1))
            mrpsptr->ClientAddress = MAPURL_CLIENT_IF_XFORWARDEDFOR;
         else
         if (strsame (SetPathPtr, "CLIENT=RESET", -1))
            mrpsptr->ClientAddress = MAPURL_CLIENT_RESET;
         else
         if (strsame (SetPathPtr, "CONTENT=", 8))
         {
            mrpsptr->ContentTypePtr = SetPathPtr + 8;
            mrpsptr->ContentTypeLength = strlen(mrpsptr->ContentTypePtr);
            AllowedInConfigFile = true;
         }
         else
         if (strsame (SetPathPtr, "CONTENT-TYPE=", 13))
         {
            /* just a synonym for "content=", bet someone uses it! */
            mrpsptr->ContentTypePtr = SetPathPtr + 13;
            mrpsptr->ContentTypeLength = strlen(mrpsptr->ContentTypePtr);
            AllowedInConfigFile = true;
         }
         else
         if (strsame (SetPathPtr, "CSS=", 4))
         {
            mrpsptr->StyleSheetPtr = SetPathPtr + 4;
            mrpsptr->StyleSheetLength = strlen(mrpsptr->StyleSheetPtr);
         }
         else
         if (strsame (SetPathPtr, "CORS=AGE=", 9))
         {
            sptr = SetPathPtr + 9;
            mrpsptr->CorsMaxAge = MetaConSetSeconds (mcptr, sptr, 1);
            /* negative 1 indicates to revert to configuration maximum */
            if (!mrpsptr->CorsMaxAge) mrpsptr->CorsMaxAge = -1;
         }
         else
         if (strsame (SetPathPtr, "CORS=CRED=TRUE", -1) ||
             strsame (SetPathPtr, "CORS=CRED=YES", -1))
            mrpsptr->CorsAllowCredentials = true;
         else
         if (strsame (SetPathPtr, "CORS=CRED=FALSE", -1) ||
             strsame (SetPathPtr, "CORS=CRED=NO", -1))
            mrpsptr->CorsNoAllowCredentials = true;
         else
         if (strsame (SetPathPtr, "CORS=EXPOSE=", 12))
         {
            mrpsptr->CorsExposeHeadersPtr = SetPathPtr + 12;
            mrpsptr->CorsExposeHeadersLength =
               strlen(mrpsptr->CorsExposeHeadersPtr);
         }
         else
         if (strsame (SetPathPtr, "CORS=HEADERS=", 13))
         {
            mrpsptr->CorsAllowHeadersPtr = SetPathPtr + 13;
            mrpsptr->CorsAllowHeadersLength =
               strlen(mrpsptr->CorsAllowHeadersPtr);
         }
         else
         if (strsame (SetPathPtr, "CORS=METHODS=", 13))
         {
            mrpsptr->CorsAllowMethodsPtr = SetPathPtr + 13;
            mrpsptr->CorsAllowMethodsLength =
               strlen(mrpsptr->CorsAllowMethodsPtr);
         }
         else
         if (strsame (SetPathPtr, "CORS=ORIGIN=", 12))
         {
            mrpsptr->CorsAllowOriginPtr = SetPathPtr + 12;
            mrpsptr->CorsAllowOriginLength =
               strlen(mrpsptr->CorsAllowOriginPtr);
         }
         else
         if (strsame (SetPathPtr, "DICT=", 5))
            mrpsptr->DictionaryPtr = SetPathPtr + 5;
         else
         if (strsame (SetPathPtr, "DIR=ACCESS=SELECTIVE", -1))
            mrpsptr->DirAccessSelective = true;
         else
         if (strsame (SetPathPtr, "DIR=ACCESS", -1))
            mrpsptr->DirAccess = true;
         else
         if (strsame (SetPathPtr, "DIR=NOACCESS", -1))
            mrpsptr->DirNoAccess = true;
         else
         if (strsame (SetPathPtr, "DIR=CHARSET=", 12))
         {
            mrpsptr->DirCharsetPtr = SetPathPtr + 12;
            mrpsptr->DirCharsetLength = strlen(mrpsptr->DirCharsetPtr);
         }
         else
         if (strsame (SetPathPtr, "DIR=DELIMIT", -1) ||
             strsame (SetPathPtr, "DIR=DELIMIT=BOTH", -1))
            mrpsptr->DirDelimit = MAPURL_DIR_DELIMIT_BOTH;
         else
         if (strsame (SetPathPtr, "DIR=DELIMIT=HEADER", -1))
            mrpsptr->DirDelimit = MAPURL_DIR_DELIMIT_HEADER;
         else
         if (strsame (SetPathPtr, "DIR=DELIMIT=FOOTER", -1))
            mrpsptr->DirDelimit = MAPURL_DIR_DELIMIT_FOOTER;
         else
         if (strsame (SetPathPtr, "DIR=DELIMIT=NONE", -1))
            mrpsptr->DirDelimit = MAPURL_DIR_DELIMIT_NONE;
         else
         if (strsame (SetPathPtr, "DIR=FONT", -1) ||
             strsame (SetPathPtr, "DIR=font=MONOSPACE", -1))
            mrpsptr->DirFont = MAPURL_DIR_FONT_MONOSPACE;
         else
         if (strsame (SetPathPtr, "DIR=font=INHERIT", -1))
            mrpsptr->DirFont = MAPURL_DIR_FONT_INHERIT;
         else
         if (strsame (SetPathPtr, "DIR=LAYOUT=", 11))
         {
            mrpsptr->IndexPtr = SetPathPtr + 11;
            mrpsptr->IndexLength = strlen(mrpsptr->IndexPtr);
         }
         else
         if (strsame (SetPathPtr, "DIR=ILINK", -1))
            mrpsptr->DirIconLink = true;
         else
         if (strsame (SetPathPtr, "DIR=NOILINK", -1))
            mrpsptr->DirNoIconLink = true;
         else
         if (strsame (SetPathPtr, "DIR=IMPLIEDWILDCARD", -1))
            mrpsptr->DirImpliedWildcard = true;
         else
         if (strsame (SetPathPtr, "DIR=NOIMPLIEDWILDCARD", -1))
            mrpsptr->DirNoImpliedWildcard = true;
         else
         if (strsame (SetPathPtr, "DIR=SORT=", 9))
         {
            mrpsptr->DirSort[0] = TOUP(SetPathPtr[9]);
            mrpsptr->DirSort[1] = SetPathPtr[10];
         }
         else
         if (strsame (SetPathPtr, "DIR=STYLE", -1) ||
             strsame (SetPathPtr, "DIR=style=DEFAULT", -1))
            mrpsptr->DirStyle = MAPURL_DIR_STYLE_DEFAULT;
         else
         if (strsame (SetPathPtr, "DIR=style=DEFAULT2", -1))
            mrpsptr->DirStyle = MAPURL_DIR_STYLE_DEFAULT2;
         else
         if (strsame (SetPathPtr, "DIR=style=ANCHOR", -1))
            mrpsptr->DirStyle = MAPURL_DIR_STYLE_ANCHOR;
         else
         if (strsame (SetPathPtr, "DIR=style=ANCHOR2", -1))
            mrpsptr->DirStyle = MAPURL_DIR_STYLE_ANCHOR2;
         else
         if (strsame (SetPathPtr, "DIR=style=HTDIR", -1))
            mrpsptr->DirStyle = MAPURL_DIR_STYLE_HTDIR;
         else
         if (strsame (SetPathPtr, "DIR=style=HTDIR2", -1))
            mrpsptr->DirStyle = MAPURL_DIR_STYLE_HTDIR2;
         else
         if (strsame (SetPathPtr, "DIR=style=ORIGINAL", -1))
            mrpsptr->DirStyle = MAPURL_DIR_STYLE_ORIGINAL;
         else
         if (strsame (SetPathPtr, "DIR=style=ORIGINAL2", -1))
            mrpsptr->DirStyle = MAPURL_DIR_STYLE_ORIGINAL2;
         else
         if (strsame (SetPathPtr, "DIR=style=SORT", -1))
            mrpsptr->DirStyle = MAPURL_DIR_STYLE_SORT;
         else
         if (strsame (SetPathPtr, "DIR=style=SORT2", -1))
            mrpsptr->DirStyle = MAPURL_DIR_STYLE_SORT2;
         else
         if (strsame (SetPathPtr, "DIR=TARGET=", 11))
         {
            mrpsptr->DirTargetPtr = SetPathPtr + 11;
            mrpsptr->DirTargetLength = strlen(mrpsptr->DirTargetPtr);
         }
         else
         if (strsame (SetPathPtr, "DIR=title=THIS=", 15))
         {
            mrpsptr->DirTitle = MAPURL_DIR_TITLE_THIS;
            mrpsptr->DirTitlePtr = SetPathPtr + 15;
            mrpsptr->DirTitleLength = strlen(mrpsptr->DirTitlePtr);
         }
         else
         if (strsame (SetPathPtr, "DIR=title=DEFAULT", -1))
            mrpsptr->DirTitle = MAPURL_DIR_TITLE_DEFAULT;
         else
         if (strsame (SetPathPtr, "DIR=title=OWNER", -1))
            mrpsptr->DirTitle = MAPURL_DIR_TITLE_OWNER;
         else
         if (strsame (SetPathPtr, "DIR=title=REMOTE",- 1))
            mrpsptr->DirTitle = MAPURL_DIR_TITLE_REMOTE;
         else
         if (strsame (SetPathPtr, "DIR=title=0", -1))
            mrpsptr->DirTitle = MAPURL_DIR_TITLE_NONE;
         else
         if (strsame (SetPathPtr, "DIR=title=", 10) &&
             isdigit(*(SetPathPtr+10)))
            mrpsptr->DirTitle = atol(SetPathPtr+10);
         else
         if (strsame (SetPathPtr, "DIR=THESE=", 10))
         {
            mrpsptr->DirThesePtr = SetPathPtr + 10;
            mrpsptr->DirTheseLength = strlen(mrpsptr->DirThesePtr);
         }
         else
         if (strsame (SetPathPtr, "DIR=VERSIONS=", 13))
         {
            sptr = SetPathPtr + 13;
            if (*sptr == '*')
               mrpsptr->DirVersionsOf = 65536;
            else
            if (*sptr == '0')
               /* negative one means reset to zero */
               mrpsptr->DirVersionsOf = -1;
            else
               mrpsptr->DirVersionsOf = atoi(sptr);
         }
         else
         if (strsame (SetPathPtr, "DIR=WILDCARD", -1))
            mrpsptr->DirWildcard = true;
         else
         if (strsame (SetPathPtr, "DIR=NOWILDCARD", -1))
            mrpsptr->DirNoWildcard = true;
         else
         if (strsame (SetPathPtr, "NOHTML", 6))
         {
            mrpsptr->NoHtmlEquals = true;
            AllowedInConfigFile = true;
         }
         else
         if (strsame (SetPathPtr, "HTML=BODYTAG=", 13))
         {
            mrpsptr->HtmlBodyTagPtr = SetPathPtr + 13;
            mrpsptr->HtmlBodyTagLength = strlen(mrpsptr->HtmlBodyTagPtr);
            AllowedInConfigFile = true;
         }
         else
         if (strsame (SetPathPtr, "HTML=FOOTER=", 12))
         {
            mrpsptr->HtmlFooterPtr = SetPathPtr + 12;
            mrpsptr->HtmlFooterLength = strlen(mrpsptr->HtmlFooterPtr);
            AllowedInConfigFile = true;
         }
         else
         if (strsame (SetPathPtr, "HTML=FOOTERTAG=", 15))
         {
            mrpsptr->HtmlFooterTagPtr = SetPathPtr + 15;
            mrpsptr->HtmlFooterTagLength = strlen(mrpsptr->HtmlFooterTagPtr);
            AllowedInConfigFile = true;
         }
         else
         if (strsame (SetPathPtr, "HTML=HEADER=", 12))
         {
            mrpsptr->HtmlHeaderPtr = SetPathPtr + 12;
            mrpsptr->HtmlHeaderLength = strlen(mrpsptr->HtmlHeaderPtr);
            AllowedInConfigFile = true;
         }
         else
         if (strsame (SetPathPtr, "HTML=HEADERTAG=", 15))
         {
            mrpsptr->HtmlHeaderTagPtr = SetPathPtr + 15;
            mrpsptr->HtmlHeaderTagLength = strlen(mrpsptr->HtmlHeaderTagPtr);
            AllowedInConfigFile = true;
         }
         else
         if (strsame (SetPathPtr, "HTTP2=PROTOCOL=1.1", -1))
            mrpsptr->Http2ToHttp11 = true;
         else
         if (strsame (SetPathPtr, "HTTP2=STREAMS=MAX=", 18))
            mrpsptr->Http2MaxStreams = atoi(SetPathPtr+18);
         else
         if (strsame (SetPathPtr, "HTTP2=WRITE=HIGH", -1))
            mrpsptr->Http2WriteQueue = HTTP2_WRITE_QUEUE_HIGH;
         else
         if (strsame (SetPathPtr, "HTTP2=WRITE=NORMAL", -1))
            mrpsptr->Http2WriteQueue = HTTP2_WRITE_QUEUE_NORMAL;
         else
         if (strsame (SetPathPtr, "HTTP2=WRITE=LOW", -1))
            mrpsptr->Http2WriteQueue = HTTP2_WRITE_QUEUE_LOW;
         else
         if (strsame (SetPathPtr, "HTTP2=SEND=GOAWAY", 17))
         {
            /* the optional error code is incremented to allow for zero */
            if (*(sptr = SetPathPtr + 17) == '=')
               mrpsptr->Http2SendGoAway = atoi(sptr+1) + 1;
            else
               mrpsptr->Http2SendGoAway = 1;
         }
         else
         if (strsame (SetPathPtr, "HTTP2=SEND=PING", -1))
            mrpsptr->Http2SendPing = true;
         else
         if (strsame (SetPathPtr, "HTTP2=SEND=RESET", 16))
         {
            /* the optional error code is incremented to allow for zero */
            if (*(sptr = SetPathPtr + 16) == '=')
               mrpsptr->Http2SendReset = atoi(sptr+1) + 1;
            else
               mrpsptr->Http2SendReset = 1;
         }
         else
         if (strsame (SetPathPtr, "HTTP=ACCEPT-CHARSET=", 20))
         {
            mrpsptr->HttpAcceptCharsetPtr = SetPathPtr + 20;
            mrpsptr->HttpAcceptCharsetLength =
               strlen(mrpsptr->HttpAcceptCharsetPtr);
            AllowedInConfigFile = true;
         }
         else
         if (strsame (SetPathPtr, "HTTP=ACCEPT-LANGUAGE=", 21))
         {
            mrpsptr->HttpAcceptLangPtr = SetPathPtr + 21;
            mrpsptr->HttpAcceptLangLength = strlen(mrpsptr->HttpAcceptLangPtr);
            AllowedInConfigFile = true;
         }
         else
         if (strsame (SetPathPtr, "INDEX=", 6))
         {
            mrpsptr->IndexPtr = SetPathPtr + 6;
            mrpsptr->IndexLength = strlen(mrpsptr->IndexPtr);
            AllowedInConfigFile = true;
         }
         else
         if (strsame (SetPathPtr, "EXPIRED", -1))
         {
            mrpsptr->Expired = true;
            AllowedInConfigFile = true;
         }
         else
         if (strsame (SetPathPtr, "NOEXPIRED", -1))
         {
            mrpsptr->NoExpired = true;
            AllowedInConfigFile = true;
         }
         else
         if (strsame (SetPathPtr, "LOG", -1))
            mrpsptr->Log = true;
         else
         if (strsame (SetPathPtr, "NOLOG", -1))
            mrpsptr->NoLog = true;
         else
         if (strsame (SetPathPtr, "MAP=ELLIPSIS", -1))
            mrpsptr->MapEllipsis = true;
         else
         if (strsame (SetPathPtr, "NOMAP=ELLIPSIS", -1) ||
             strsame (SetPathPtr, "MAP=NOELLIPSIS", -1))
            mrpsptr->NoMapEllipsis = false;
         else
         if (strsame (SetPathPtr, "MAPEMPTY", -1) ||
             strsame (SetPathPtr, "MAP=EMPTY", -1))
            mrpsptr->MapEmpty = true;
         else
         if (strsame (SetPathPtr, "NOMAPEMPTY", -1) ||
             strsame (SetPathPtr, "NOMAP=EMPTY", -1) ||
             strsame (SetPathPtr, "MAP=NONEMPTY", -1))
            mrpsptr->MapNonEmpty = true;
         else
         if (strsame (SetPathPtr, "MAP=METHOD", -1))
            mrpsptr->MapExtensionMethod = true;
         else
         if (strsame (SetPathPtr, "MAP=NOMETHOD", -1) ||
             strsame (SetPathPtr, "NOMAP=METHOD", -1))
            mrpsptr->NoMapExtensionMethod = true;
         else
         if (strsame (SetPathPtr, "MAP=ONCE", -1) ||
             strsame (SetPathPtr, "MAPONCE", -1))
            mrpsptr->MapOnce = true;
         else
         if (strsame (SetPathPtr, "MAP=NOONCE", -1) ||
             strsame (SetPathPtr, "NOMAP=ONCE", -1) ||
             strsame (SetPathPtr, "NOMAPONCE", -1))
            mrpsptr->NoMapOnce = true;
         else
         if (strsame (SetPathPtr, "MAP=RESTART", -1))
            mrpsptr->MapRestart = true;
         else
         if (strsame (SetPathPtr, "MAP=ROOT=", 9))
         {
            mrpsptr->MapRootPtr = SetPathPtr + 9;
            mrpsptr->MapRootLength = strlen(mrpsptr->MapRootPtr);
         }
         else
         if (strsame (SetPathPtr, "MAP=SET=REQUEST", -1))
            mrpsptr->MapSetRequest = true;
         else
         if (strsame (SetPathPtr, "NOMAP=SET=REQUEST", -1) ||
             strsame (SetPathPtr, "MAP=SET=NOREQUEST", -1))
            mrpsptr->MapSetNoRequest = true;
         else
         if (strsame (SetPathPtr, "MAP=SET=IGNORE", -1))
            mrpsptr->MapSetIgnore = true;
         else
         if (strsame (SetPathPtr, "NOMAP=SET=IGNORE", -1) ||
             strsame (SetPathPtr, "MAP=SET=NOIGNORE", -1))
            mrpsptr->MapSetNoIgnore = true;
         else
         if (strsame (SetPathPtr, "MAP=URI", -1))
            mrpsptr->MapUri = true;
         else
         if (strsame (SetPathPtr, "NOTEPAD=", 8))
            mrpsptr->NotePadPtr = SetPathPtr + 8;
         else
         if (strsame (SetPathPtr, "ODS=0", -1) ||
             strsame (SetPathPtr, "NOODS", -1))
            mrpsptr->PathOds = MAPURL_PATH_ODS_0;
         else
         if (strsame (SetPathPtr, "ODS=2", -1) ||
             strsame (SetPathPtr, "ODS-2", -1))
            mrpsptr->PathOds = MAPURL_PATH_ODS_2;
         else
         if (strsame (SetPathPtr, "ODS=5", -1) ||
             strsame (SetPathPtr, "ODS-5", -1))
         {
            mrpsptr->PathOds = MAPURL_PATH_ODS_5;
#ifdef ODS_EXTENDED
            /* indicate that at least one mapping is set ODS-5 */
            MapUrlPathOds5 = true;
#endif /* ODS_EXTENDED */
         }
         else
         if (strsame (SetPathPtr, "ODS=ADS", -1))
            mrpsptr->PathOds = MAPURL_PATH_ODS_ADS;
         else
         if (strsame (SetPathPtr, "ODS=PWK", -1))
            mrpsptr->PathOds = MAPURL_PATH_ODS_PWK;
         else
         if (strsame (SetPathPtr, "ODS=SMB", -1))
            mrpsptr->PathOds = MAPURL_PATH_ODS_SMB;
         else
         if (strsame (SetPathPtr, "ODS=SRI", -1))
            mrpsptr->PathOds = MAPURL_PATH_ODS_SRI;
         else
         if (strsame (SetPathPtr, "ODS=NAME=8BIT", -1))
            mrpsptr->OdsName = MAPURL_ODS_8BIT;
         else
         if (strsame (SetPathPtr, "ODS=NAME=UTF8", -1))
            mrpsptr->OdsName = MAPURL_ODS_UTF8;
         else
         if (strsame (SetPathPtr, "ODS=NAME=DEFAULT", -1))
            mrpsptr->OdsName = MAPURL_ODS_DEFAULT;
         else
         if (strsame (SetPathPtr, "PROFILE", -1))
            mrpsptr->Profile = true;
         else
         if (strsame (SetPathPtr, "NOPROFILE", -1))
            mrpsptr->NoProfile = true;
         else
         if (strsame (SetPathPtr, "PROXY=AFFINITY", 14))
            mrpsptr->ProxyAffinity = true;
         else
         if (strsame (SetPathPtr, "PROXY=NOAFFINITY", 16) ||
             strsame (SetPathPtr, "NOPROXY=AFFINITY", 16))
            mrpsptr->ProxyNoAffinity = true;
         else
         if (strsame (SetPathPtr, "PROXY=BIND=", 11))
         {
            mrpsptr->ProxyBindIpAddressPtr = SetPathPtr + 11;
            mrpsptr->ProxyBindIpAddressLength =
               strlen(mrpsptr->ProxyBindIpAddressPtr);
         }
         else
         if (strsame (SetPathPtr, "PROXY=CHAIN=CRED=", 17))
         {
            mrpsptr->ProxyChainCredPtr = SetPathPtr + 17;
            mrpsptr->ProxyChainCredLength =
               strlen(mrpsptr->ProxyChainCredPtr);
         }
         else
         if (strsame (SetPathPtr, "PROXY=CHAIN=", 12))
         {
            mrpsptr->ProxyChainHostPortPtr = SetPathPtr + 12;
            mrpsptr->ProxyChainHostPortLength =
               strlen(mrpsptr->ProxyChainHostPortPtr);
         }
         else
         if (strsame (SetPathPtr, "PROXY=NOFORWARDED", -1))
            mrpsptr->ProxyForwardedBy = PROXY_FORWARDED_NONE;
         else
         if (strsame (SetPathPtr, "PROXY=FORWARDED", -1) ||
             strsame (SetPathPtr, "PROXY=FORWARDED=BY", -1))
            mrpsptr->ProxyForwardedBy = PROXY_FORWARDED_BY;
         else
         if (strsame (SetPathPtr, "PROXY=FORWARDED=FOR", -1))
            mrpsptr->ProxyForwardedBy = PROXY_FORWARDED_FOR;
         else
         if (strsame (SetPathPtr, "PROXY=FORWARDED=ADDRESS", -1))
            mrpsptr->ProxyForwardedBy = PROXY_FORWARDED_ADDRESS;
         else
         if (strsame (SetPathPtr, "PROXY=NOXFORWARDEDFOR", -1))
            mrpsptr->ProxyXForwardedFor = PROXY_XFORWARDEDFOR_NONE;
         else
         if (strsame (SetPathPtr, "PROXY=XFORWARDEDFOR", -1) ||
             strsame (SetPathPtr, "PROXY=XFORWARDEDFOR=ENABLED", -1))
            mrpsptr->ProxyXForwardedFor = PROXY_XFORWARDEDFOR_ENABLED;
         else
         if (strsame (SetPathPtr, "PROXY=XFORWARDEDFOR=ADDRESS", -1))
            mrpsptr->ProxyXForwardedFor = PROXY_XFORWARDEDFOR_ADDRESS;
         else
         if (strsame (SetPathPtr, "PROXY=XFORWARDEDFOR=UNKNOWN", -1))
            mrpsptr->ProxyXForwardedFor = PROXY_XFORWARDEDFOR_UNKNOWN;
         else
         if (strsame (SetPathPtr, "PROXY=HEADER=", 13))
         {
            if (mrpsptr->ProxyHeaderCount < MAPURL_PROXY_HEADER_MAX)
               mrpsptr->ProxyHeader[mrpsptr->ProxyHeaderCount++] =
                  SetPathPtr + 13;
            else
               MetaConReport (mcptr, METACON_REPORT_ERROR,
                              ProblemProxyHeader);
         }
         else
         if (strsame (SetPathPtr, "PROXY=REVERSE=LOCATION=", 23))
         {
            mrpsptr->ProxyReverseLocationPtr = SetPathPtr + 23;
            mrpsptr->ProxyReverseLocationLength =
               strlen(mrpsptr->ProxyReverseLocationPtr);
         }
         else
         if (strsame (SetPathPtr, "PROXY=REVERSE=VERIFY", -1))
            mrpsptr->ProxyReverseVerify = true;
         else
         if (strsame (SetPathPtr, "PROXY=REVERSE=NOVERIFY", -1) ||
             strsame (SetPathPtr, "NOPROXY=REVERSE=VERIFY", -1))
            mrpsptr->NoProxyReverseVerify = true;
         else
         if (strsame (SetPathPtr, "PROXY=REVERSE=AUTH", -1))
            mrpsptr->ProxyReverseAuthHeader = true;
         else
         if (strsame (SetPathPtr, "PROXY=REVERSE=NOAUTH", -1) ||
             strsame (SetPathPtr, "NOPROXY=REVERSE=AUTH", -1))
            mrpsptr->NoProxyReverseAuthHeader = true;
         else
         if (strsame (SetPathPtr, "PROXY=TUNNEL=REQUEST=", 21))
         {
            mrpsptr->ProxyTunnelRequestPtr = SetPathPtr + 21;
            mrpsptr->ProxyTunnelRequestLength =
               strlen(mrpsptr->ProxyTunnelRequestPtr);
         }
         else
         if (strsame (SetPathPtr, "PROXY=UNKNOWN", -1))
            mrpsptr->ProxyUnknownRequestFields = true;
         else
         if (strsame (SetPathPtr, "PROXY=NOUNKNOWN", -1) ||
             strsame (SetPathPtr, "NOPROXY=UNKNOWN", -1))
            mrpsptr->NoProxyUnknownRequestFields = true;
         else
         if (strsame (SetPathPtr, "PUT=RFM=FIX512", -1))
            mrpsptr->PutRFM = PUT_RFM_FIX512;
         else
         if (strsame (SetPathPtr, "PUT=RFM=STM", -1))
            mrpsptr->PutRFM = PUT_RFM_STM;
         else
         if (strsame (SetPathPtr, "PUT=RFM=STMCR", -1))
            mrpsptr->PutRFM = PUT_RFM_STMCR;
         else
         if (strsame (SetPathPtr, "PUT=RFM=STMLF", -1))
            mrpsptr->PutRFM = PUT_RFM_STMLF;
         else
         if (strsame (SetPathPtr, "PUT=RFM=UDF", -1))
            mrpsptr->PutRFM = PUT_RFM_UDF;
         else
         if (strsame (SetPathPtr, "PUT=MAX=", 8))
         {
            if (*(SetPathPtr+8) == '*')
               /* no limit is actually 4GB - current HTTP atol() constraint */
               mrpsptr->PutMaxKbytes = 999999999;
            else
               mrpsptr->PutMaxKbytes = atoi(SetPathPtr+8);
         }
         else
         if (strsame (SetPathPtr, "QUERY-STRING=", 13))
         {
            mrpsptr->QueryStringPtr = SetPathPtr + 13;
            mrpsptr->QueryStringLength = strlen(mrpsptr->QueryStringPtr);
         }
         else
         if (strsame (SetPathPtr, "REGEX=AWK", 9))
            mrpsptr->RegexSyntax = RE_SYNTAX_AWK;
         else
         if (strsame (SetPathPtr, "DEFAULT", 7))
            mrpsptr->RegexSyntax = -1;
         else
         if (strsame (SetPathPtr, "REGEX=ED", 8))
            mrpsptr->RegexSyntax = RE_SYNTAX_ED;
         else
         if (strsame (SetPathPtr, "REGEX=EGREP", 11))
            mrpsptr->RegexSyntax = RE_SYNTAX_EGREP;
         else
         if (strsame (SetPathPtr, "REGEX=GREP", 10))
            mrpsptr->RegexSyntax = RE_SYNTAX_GREP;
         else
         if (strsame (SetPathPtr, "REGEX=POSIX_AWK", 15))
            mrpsptr->RegexSyntax = RE_SYNTAX_POSIX_AWK;
         else
         if (strsame (SetPathPtr, "REGEX=POSIX_BASIC", 17))
            mrpsptr->RegexSyntax = RE_SYNTAX_POSIX_BASIC;
         else
         if (strsame (SetPathPtr, "REGEX=POSIX_EGREP", 17))
            mrpsptr->RegexSyntax = RE_SYNTAX_POSIX_EGREP;
         else
         if (strsame (SetPathPtr, "REGEX=POSIX_EXTENDED", 20))
            mrpsptr->RegexSyntax = RE_SYNTAX_POSIX_EXTENDED;
         else
         if (strsame (SetPathPtr, "REGEX=POSIX_MINIMAL_BASIC", 25))
            mrpsptr->RegexSyntax = RE_SYNTAX_POSIX_MINIMAL_BASIC;
         else
         if (strsame (SetPathPtr, "REGEX=POSIX_MINIMAL_EXTENDED", 28))
            mrpsptr->RegexSyntax = RE_SYNTAX_POSIX_MINIMAL_EXTENDED;
         else
         if (strsame (SetPathPtr, "REGEX=SED", 9))
            mrpsptr->RegexSyntax = RE_SYNTAX_AWK;
         else
         if (strsame (SetPathPtr, "REGEX=WASD", 10))
            mrpsptr->RegexSyntax = REGEX_C_FLAGS;
         else
         if (strsame (SetPathPtr, "REGEX=", 6) && isdigit(SetPathPtr[6]))
            mrpsptr->RegexSyntax = atoi(SetPathPtr+6);
         else
         if (strsame (SetPathPtr, "REPORT=BASIC", 10))
            mrpsptr->ReportType = ERROR_REPORT_BASIC;
         else
         if (strsame (SetPathPtr, "REPORT=DETAILED", 10))
            mrpsptr->ReportType = ERROR_REPORT_DETAILED;
         else
         if (strsame (SetPathPtr, "REPORT=TUNNEL", 10))
            mrpsptr->ReportType = ERROR_REPORT_TUNNEL;
         else
         if (strsame (SetPathPtr, "REPORT=400=", 11))
            mrpsptr->Report400as = atoi(SetPathPtr+11);
         else
         if (strsame (SetPathPtr, "REPORT=403=", 11))
            mrpsptr->Report403as = atoi(SetPathPtr+11);
         else
         if (strsame (SetPathPtr, "REPORT=404=", 11))
            mrpsptr->Report404as = atoi(SetPathPtr+11);
         else
         if (strsame (SetPathPtr, "RMSCHAR=", 8))
            mrpsptr->RmsSubChar = SetPathPtr[8];
         else
         if (strsame (SetPathPtr, "RESPONSE=200=203", -1))
            mrpsptr->Response200is203 = 203;
         else
         if (strsame (SetPathPtr, "RESPONSE=203=200", -1))
            mrpsptr->Response200is203 = 200;
         else
         if (strsame (SetPathPtr, "RESPONSE=CSP=", 13))
         {
            mrpsptr->ResponseCspPtr = SetPathPtr + 13;
            if (!(mrpsptr->ResponseCspLength =
                  strlen(mrpsptr->ResponseCspPtr)))
            {
               mrpsptr->ResponseCspPtr = NULL;
               mrpsptr->ResponseCspLength = -1;
            }
         }
         else
         if (strsame (SetPathPtr, "NORESPONSE=CSP", -1) ||
             strsame (SetPathPtr, "RESPONSE=NOCSP", -1))
         {
            mrpsptr->ResponseCspPtr = NULL;
            mrpsptr->ResponseCspLength = -1;
         }
         else
         if (strsame (SetPathPtr, "RESPONSE=CSPRO=", 15))
         {
            mrpsptr->ResponseCsproPtr = SetPathPtr + 15;
            if (!(mrpsptr->ResponseCsproLength =
                  strlen(mrpsptr->ResponseCsproPtr)))
            {
               mrpsptr->ResponseCspPtr = NULL;
               mrpsptr->ResponseCspLength = -1;
            }
         }
         else
         if (strsame (SetPathPtr, "NORESPONSE=CSPRO", -1) ||
             strsame (SetPathPtr, "RESPONSE=NOCSPRO", -1))
         {
            mrpsptr->ResponseCsproPtr = NULL;
            mrpsptr->ResponseCsproLength = -1;
         }
         else
         if (strsame (SetPathPtr, "RESPONSE=GZIP=ALL", -1) ||
             strsame (SetPathPtr, "RESPONSE=GZIP=0", -1))
            mrpsptr->ResponseGzip = MAPURL_RESPONSE_GZIP_ALL;
         else
         if (strsame (SetPathPtr, "RESPONSE=GZIP=NONE", -1))
            mrpsptr->ResponseGzip = MAPURL_RESPONSE_GZIP_NONE;
         else
         if (strsame (SetPathPtr, "RESPONSE=GZIP=", 14))
            mrpsptr->ResponseGzip = atoi(SetPathPtr+14);
         else
         if (strsame (SetPathPtr, "RESPONSE=CHUNKED", 14))
            mrpsptr->ResponseChunked = true;
         else
         if (strsame (SetPathPtr, "NORESPONSE=CHUNKED", 16) ||
             strsame (SetPathPtr, "RESPONSE=NOCHUNKED", 16))
            mrpsptr->ResponseNoChunked = true;
         else
         if (strsame (SetPathPtr, "RESPONSE=HEADER=BEGIN", -1))
            mrpsptr->ResponseHeaderBegin = true;
         else
         if (strsame (SetPathPtr, "RESPONSE=HEADER=FULL", -1))
            mrpsptr->ResponseHeaderFull = true;
         else
         if (strsame (SetPathPtr, "RESPONSE=HEADER=NONE", -1))
            mrpsptr->ResponseHeaderNone = true;
         else
         if (strsame (SetPathPtr, "NORESPONSE=HEADER=ADD", -1) ||
             strsame (SetPathPtr, "RESPONSE=HEADER=NOADD", -1))
            mrpsptr->ResponseHeaderNoAdd = true;
         else
         if (strsame (SetPathPtr, "RESPONSE=HEADER=ADD=", 20))
         {
            mrpsptr->ResponseHeaderAddPtr = SetPathPtr + 20;
            mrpsptr->ResponseHeaderAddLength =
               strlen(mrpsptr->ResponseHeaderAddPtr);
         }
         else
         if (strsame (SetPathPtr, "RESPONSE=HTTP=ORIGINAL", -1))
            mrpsptr->ResponseHttpOriginal = true;
         else
         if (strsame (SetPathPtr, "NORESPONSE=HTTP=ORIGINAL", -1) ||
             strsame (SetPathPtr, "RESPONSE=HTTP=NOORIGINAL", -1))
            mrpsptr->ResponseHttpNoOriginal = true;
         else
         if (strsame (SetPathPtr, "RESPONSE=STS=", 13))
         {
            mrpsptr->ResponseStrictTransSecPtr = SetPathPtr+13;
            mrpsptr->ResponseStrictTransSecLength =
               strlen(mrpsptr->ResponseStrictTransSecPtr);
         }
         else
         if (strsame (SetPathPtr, "RESPONSE=VAR", -1))
            mrpsptr->ResponseVarRecord = FILE_VAR_DEFAULT;
         else
         if (strsame (SetPathPtr, "RESPONSE=VAR=CRLF", -1))
            mrpsptr->ResponseVarRecord = FILE_VAR_CRLF;
         else
         if (strsame (SetPathPtr, "RESPONSE=VAR=LF", -1))
            mrpsptr->ResponseVarRecord = FILE_VAR_LF;
         else
         if (strsame (SetPathPtr, "RESPONSE=VAR=NONE", -1))
            mrpsptr->ResponseVarRecord = FILE_VAR_NONE;
         else
         if (strsame (SetPathPtr, "SCRIPT=AGENT=AS=", 16))
         {
            /* while setting the required pointers force to upper-case */
            for (sptr = mrpsptr->ScriptAgentAsPtr = SetPathPtr+16;
                *sptr;
                sptr++)
               *sptr = TOUP(*sptr);
            mrpsptr->ScriptAgentAsLength = strlen(mrpsptr->ScriptAgentAsPtr);
         }
         else
         if (strsame (SetPathPtr, "SCRIPT=AS=", 10))
         {
            /* while setting the required pointers force to upper-case */
            for (sptr = mrpsptr->ScriptAsPtr = SetPathPtr+10; *sptr; sptr++)
               *sptr = TOUP(*sptr);
            mrpsptr->ScriptAsLength = strlen(mrpsptr->ScriptAsPtr);
         }
         else
         if (strsame (SetPathPtr, "SCRIPT=BIT-BUCKET=", 18))
            mrpsptr->ScriptBitBucketTimeout =
               MetaConSetSeconds (mcptr, SetPathPtr+18, 1);
         else
         if (strsame (SetPathPtr, "SCRIPT=BODY=DECODE", -1))
            mrpsptr->ScriptBodyDecode = true;
         else
         if (strsame (SetPathPtr, "SCRIPT=BODY=NODECODE", -1) ||
             strsame (SetPathPtr, "SCRIPT=NOBODY=DECODE", -1) ||
             strsame (SetPathPtr, "NOSCRIPT=BODY=DECODE", -1))
            mrpsptr->ScriptBodyNoDecode = true;
         else
         if (strsame (SetPathPtr, "SCRIPT=CPU=", 11))
            mrpsptr->ScriptCpuMax = MetaConSetSeconds (mcptr, SetPathPtr+11, 1);
         else
         if (strsame (SetPathPtr, "SCRIPT=COMMAND=", 15))
         {
            mrpsptr->ScriptCommandPtr = SetPathPtr + 15;
            mrpsptr->ScriptCommandLength = strlen(mrpsptr->ScriptCommandPtr);
         }
         else
         if (strsame (SetPathPtr, "SCRIPT=CONTROL=", 15))
         {
            mrpsptr->ScriptControlPtr = SetPathPtr + 15;
            mrpsptr->ScriptControlLength = strlen(mrpsptr->ScriptControlPtr);
         }
         else
         if (strsame (SetPathPtr, "SCRIPT=DEFAULT=", 15))
         {
            mrpsptr->ScriptDefaultPtr = SetPathPtr + 15;
            mrpsptr->ScriptDefaultLength = strlen(mrpsptr->ScriptDefaultPtr);
         }
         else
         if (strsame (SetPathPtr, "SCRIPT=FIND", -1))
            mrpsptr->ScriptFind = true;
         else
         if (strsame (SetPathPtr, "SCRIPT=NOFIND", -1) ||
             strsame (SetPathPtr, "NOSCRIPT=FIND", -1))
            mrpsptr->ScriptNoFind = true;
         else
         if (strsame (SetPathPtr, "SCRIPT=LIFETIME=", 16))
            mrpsptr->ScriptLifeTime =
               MetaConSetSeconds (mcptr, SetPathPtr+16, 1);
         else
         if (strsame (SetPathPtr, "SCRIPT=SYMBOL=TRUNCATE", -1))
            mrpsptr->ScriptSymbolTruncate = true;
         else
         if (strsame (SetPathPtr, "SCRIPT=SYMBOL=NOTRUNCATE", -1) ||
             strsame (SetPathPtr, "NOSCRIPT=SYMBOL=TRUNCATE", -1))
            mrpsptr->ScriptSymbolNoTruncate = true;
         else
         if (strsame (SetPathPtr, "SCRIPT=SYNTAX=UNIX", -1))
            mrpsptr->ScriptSyntaxUnix = true;
         else
         if (strsame (SetPathPtr, "SCRIPT=SYNTAX=NOUNIX", -1) ||
             strsame (SetPathPtr, "NOSCRIPT=SYNTAX=UNIX", -1))
            mrpsptr->ScriptSyntaxNoUnix = true;
         else
         if (strsame (SetPathPtr, "SCRIPT=PARAM=", 13))
         {
            mrpsptr->ScriptParamsPtr = SetPathPtr + 13;
            mrpsptr->ScriptParamsLength =
               strlen(mrpsptr->ScriptParamsPtr);
         }
         else
         if (strsame (SetPathPtr, "SCRIPT=PARAMS=", 14))
         {
            mrpsptr->ScriptParamsPtr = SetPathPtr + 14;
            mrpsptr->ScriptParamsLength =
               strlen(mrpsptr->ScriptParamsPtr);
         }
         else
         if (strsame (SetPathPtr, "SCRIPT=PATH=FIND", -1))
            mrpsptr->ScriptPathFind = true;
         else
         if (strsame (SetPathPtr, "SCRIPT=NOPATH=FIND", -1) ||
             strsame (SetPathPtr, "NOSCRIPT=PATH=FIND", -1))
            mrpsptr->ScriptNoPathFind = true;
         else
         if (strsame (SetPathPtr, "SCRIPT=QUERY=NONE", -1))
            mrpsptr->ScriptQueryNone = true;
         else
         if (strsame (SetPathPtr, "SCRIPT=NOQUERY=NONE", -1) ||
             strsame (SetPathPtr, "NOSCRIPT=QUERY=NONE", -1))
            mrpsptr->ScriptNoQueryNone = true;
         else
         if (strsame (SetPathPtr, "SCRIPT=QUERY=RELAXED", -1))
            mrpsptr->ScriptQueryRelaxed = true;
         else
         if (strsame (SetPathPtr, "SCRIPT=NOQUERY=RELAXED", -1) ||
             strsame (SetPathPtr, "NOSCRIPT=QUERY=RELAXED", -1))
            mrpsptr->ScriptNoQueryRelaxed = true;
         else
         if (strsame (SetPathPtr, "SEARCH=NONE", -1))
            mrpsptr->NoDefaultSearch = true;
         else
         if (strsame (SetPathPtr, "NOSEARCH=NONE", -1) ||
             strsame (SetPathPtr, "SEARCH=NONONE", -1))
            mrpsptr->DefaultSearch = true;
         else
         if (strsame (SetPathPtr, "SERVICE=", 8))
         {
            mrpsptr->ChangeServicePtr = SetPathPtr + 8;
            mrpsptr->ChangeServiceLength =
               strlen(mrpsptr->ChangeServicePtr);
         }
         else
         if (strsame (SetPathPtr, "SSI=PRIV", 8))
            mrpsptr->PrivSsi = true;
         else
         if (strsame (SetPathPtr, "SSI=NOPRIV", -1) ||
             strsame (SetPathPtr, "NOSSI=PRIV", -1))
            mrpsptr->NoPrivSsi = true;
         else
         if (strsame (SetPathPtr, "SSI=EXEC=", 9))
         {
            mrpsptr->SsiExecPtr = SetPathPtr + 9;
            mrpsptr->SsiExecLength =
               strlen(mrpsptr->SsiExecPtr);
         }
         else
         if (strsame (SetPathPtr, "SSI=DCL=", 8))
         {
            /* synonym for 'ssi=exec=' */
            mrpsptr->SsiExecPtr = SetPathPtr + 8;
            mrpsptr->SsiExecLength =
               strlen(mrpsptr->SsiExecPtr);
         }
         else
         if (strsame (SetPathPtr, "NOSSLCGI", -1))
            mrpsptr->SSLCGIvar = SESOLA_CGI_VAR_NONE;
         else
         if (strsame (SetPathPtr, "SSLCGI=NONE", -1))
            mrpsptr->SSLCGIvar = SESOLA_CGI_VAR_NONE;
         else
         if (strsame (SetPathPtr, "SSLCGI=APACHE_MOD_SSL", -1))
            mrpsptr->SSLCGIvar = SESOLA_CGI_VAR_APACHE_MOD_SSL;
         else
         if (strsame (SetPathPtr, "SSLCGI=APACHE_MOD_SSL_CLIENT", -1))
            mrpsptr->SSLCGIvar = SESOLA_CGI_VAR_APACHE_MOD_SSL_CLIENT;
         else
         if (strsame (SetPathPtr, "SSLCGI=APACHE_MOD_SSL_EXTENS", -1))
            mrpsptr->SSLCGIvar = SESOLA_CGI_VAR_APACHE_MOD_SSL_EXTENS;
         else
         if (strsame (SetPathPtr, "SSLCGI=APACHE_MOD_SSL_OID", -1))
            mrpsptr->SSLCGIvar = SESOLA_CGI_VAR_APACHE_MOD_SSL_OID;
         else
         if (strsame (SetPathPtr, "SSLCGI=PURVEYOR", -1))
            mrpsptr->SSLCGIvar = SESOLA_CGI_VAR_PURVEYOR;
         else
         if (strsame (SetPathPtr, "STMLF", -1))
            mrpsptr->StmLF = true;
         else
         if (strsame (SetPathPtr, "NOSTMLF", -1))
            mrpsptr->NoStmLF = true;
         else
         if (strsame (SetPathPtr, "THROTTLE=", 9))
         {
            mrpsptr->ThrottleSet = true;
            SetPathPtr += 9;
            if (strsame (SetPathPtr, "BUSY=", 5))
               mrpsptr->ThrottleBusy = atoi(SetPathPtr+5);
            else
            if (strsame (SetPathPtr, "FROM=", 5))
            {
               sptr = SetPathPtr + 5;
               mrpsptr->ThrottleFrom = atoi(sptr);
               while (isdigit(*sptr)) sptr++;
               if (*sptr == '$') mrpsptr->ThrottlePerUser = atoi(sptr+1);
            }
            else
            if (strsame (SetPathPtr, "RESUME=", 7))
               mrpsptr->ThrottleResume = atoi(SetPathPtr+7);
            else
            if (strsame (SetPathPtr, "TO=", 3))
               mrpsptr->ThrottleTo = atoi(SetPathPtr+3);
            else
            if (strsame (SetPathPtr, "TIMEOUT=BUSY=", 13))
               mrpsptr->ThrottleTimeoutBusy =
                  MetaConSetSeconds (mcptr, SetPathPtr+13, 1);
            else
            if (strsame (SetPathPtr, "TIMEOUT=QUEUE=", 14))
               mrpsptr->ThrottleTimeoutQueue =
                  MetaConSetSeconds (mcptr, SetPathPtr+14, 1);
            else
            if (strsame (SetPathPtr, "USER=", 5))
               mrpsptr->ThrottlePerUser = atoi(SetPathPtr+5);
            else
            if (strsame (SetPathPtr, "NONE", 4))
               mrpsptr->ThrottleNoSet = true;
            else
            {
               /* throttle=from[/user],to,resume,busy,t/o-queue,t/o-busy */
               sptr = SetPathPtr;
               if (*sptr == '(') sptr++;
               if (isdigit(*sptr)) mrpsptr->ThrottleFrom = atoi(sptr);
               while (isdigit(*sptr)) *sptr++;
               if (*sptr == '/')
               {
                  sptr++;
                  mrpsptr->ThrottlePerUser = atoi(sptr);
                  while (isdigit(*sptr)) *sptr++;
               }
               if (*sptr == ',') *sptr++;
               if (isdigit(*sptr)) mrpsptr->ThrottleTo = atoi(sptr);
               while (isdigit(*sptr)) *sptr++;
               if (*sptr == ',') *sptr++;
               if (isdigit(*sptr)) mrpsptr->ThrottleResume = atoi(sptr);
               while (isdigit(*sptr)) *sptr++;
               if (*sptr == ',') *sptr++;
               if (isdigit(*sptr)) mrpsptr->ThrottleBusy = atoi(sptr);
               while (isdigit(*sptr)) *sptr++;
               if (*sptr == ',') *sptr++;
               if (isdigit(*sptr))
                  mrpsptr->ThrottleTimeoutQueue =
                     MetaConSetSeconds (mcptr, sptr, 1);
               while (*sptr && (isdigit(*sptr) || *sptr == ':')) *sptr++;
               if (*sptr == ',') *sptr++;
               if (isdigit(*sptr))
                  mrpsptr->ThrottleTimeoutBusy =
                     MetaConSetSeconds (mcptr, sptr, 1);
               while (*sptr && (isdigit(*sptr) || *sptr == ':')) *sptr++;
            }
         }
         else
         if (strsame (SetPathPtr, "TIMEOUT=PERSISTENT=", 19))
            mrpsptr->TimeoutPersistent =
               MetaConSetSeconds (mcptr, SetPathPtr+19, 1);
         else
         /* for backward compatibility against the above */
         if (strsame (SetPathPtr, "TIMEOUT=KEEPALIVE=", 18))
            mrpsptr->TimeoutPersistent =
               MetaConSetSeconds (mcptr, SetPathPtr+18, 1);
         else
         if (strsame (SetPathPtr, "TIMEOUT=NOPROGRESS=", 19))
            mrpsptr->TimeoutNoProgress =
               MetaConSetSeconds (mcptr, SetPathPtr+19, 1);
         else
         if (strsame (SetPathPtr, "TIMEOUT=OUTPUT=", 15))
            mrpsptr->TimeoutOutput =
               MetaConSetSeconds (mcptr, SetPathPtr+15, 1);
         else
         if (strsame (SetPathPtr, "TIMEOUT=", 8))
         {
            sptr = SetPathPtr+8;
            mrpsptr->TimeoutPersistent = MetaConSetSeconds (mcptr, sptr, 1);
            while (*sptr && !ISLWS(*sptr) && *sptr != ',') *sptr++;
            if (*sptr == ',') *sptr++;
            mrpsptr->TimeoutNoProgress = MetaConSetSeconds (mcptr, sptr, 1);
            while (*sptr && !ISLWS(*sptr) && *sptr != ',') *sptr++;
            if (*sptr == ',') *sptr++;
            mrpsptr->TimeoutOutput = MetaConSetSeconds (mcptr, sptr, 1);
         }
         else
         if (strsame (SetPathPtr, "NOTIMEOUT", -1))
            mrpsptr->TimeoutNoProgress =
              mrpsptr->TimeoutPersistent =
              mrpsptr->TimeoutOutput = -1;
         else
         if (strsame (SetPathPtr, "ACCESS=PROFILE", -1))
            mrpsptr->AccessProfile = true;
         else
         if (strsame (SetPathPtr, "ACCESS=NOPROFILE", -1) ||
             strsame (SetPathPtr, "NOACCESS=PROFILE", -1))
            mrpsptr->AccessNoProfile = true;
         else
         if (strsame (SetPathPtr, "ACCESS=READ", -1))
            mrpsptr->AccessRead = true;
         else
         if (strsame (SetPathPtr, "ACCESS=NOREAD", -1) ||
             strsame (SetPathPtr, "NOACCESS=READ", -1))
            mrpsptr->AccessNoRead = true;
         else
         if (strsame (SetPathPtr, "ACCESS=SERVER", -1))
            mrpsptr->AccessServer = true;
         else
         if (strsame (SetPathPtr, "ACCESS=NOSERVER", -1) ||
             strsame (SetPathPtr, "NOACCESS=SERVER", -1))
            mrpsptr->AccessNoServer = true;
         else
         if (strsame (SetPathPtr, "ACCESS=WRITE", -1))
            mrpsptr->AccessWrite = true;
         else
         if (strsame (SetPathPtr, "ACCESS=NOWRITE", -1) ||
             strsame (SetPathPtr, "NOACCESS=WRITE", -1))
            mrpsptr->AccessNoWrite = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=HIDDEN", -1))
            mrpsptr->WebDavHidden = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=NOHIDDEN", -1) ||
             strsame (SetPathPtr, "NOWEBDAV=HIDDEN", -1))
            mrpsptr->WebDavNoHidden = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=LOCKING", 11))
            mrpsptr->WebDavLock = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=NOLOCKING", 13) ||
             strsame (SetPathPtr, "NOWEBDAV=LOCKING", 13))
            mrpsptr->WebDavNoLock = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=META=DIR=", 16))
         {
            mrpsptr->WebDavMetaDirPtr = SetPathPtr + 16;
            mrpsptr->WebDavMetaDirLength = strlen(mrpsptr->WebDavMetaDirPtr);
         }
         else
         if (strsame (SetPathPtr, "WEBDAV=PROFILE", -1))
            mrpsptr->WebDavProfile = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=NOPROFILE", -1) ||
             strsame (SetPathPtr, "NOWEBDAV=PROFILE", -1))
            mrpsptr->WebDavNoProfile = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=PROP", -1))
            mrpsptr->WebDavProp = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=NOPROP", -1) ||
             strsame (SetPathPtr, "NOWEBDAV=PROP", -1))
            mrpsptr->WebDavNoProp = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=PUT=LOCK", -1))
            mrpsptr->WebDavPutLock = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=NOPUT=LOCK", -1) ||
             strsame (SetPathPtr, "NOWEBDAV=PUT=LOCK", -1))
            mrpsptr->WebDavNoPutLock = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=READ", -1))
            mrpsptr->WebDavRead = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=NOREAD", -1) ||
             strsame (SetPathPtr, "NOWEBDAV=READ", -1))
            mrpsptr->WebDavNoRead = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=SERVER", -1))
            mrpsptr->WebDavServer = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=NOSERVER", -1) ||
             strsame (SetPathPtr, "NOWEBDAV=SERVER", -1))
            mrpsptr->WebDavNoServer = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=WINPROP", -1))
            mrpsptr->WebDavWinProp = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=NOWINPROP", -1) ||
             strsame (SetPathPtr, "NOWEBDAV=WINPROP", -1))
            mrpsptr->WebDavNoWinProp = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=WRITE", -1))
            mrpsptr->WebDavWrite = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=NOWRITE", -1) ||
             strsame (SetPathPtr, "NOWEBDAV=WRITE", -1))
            mrpsptr->WebDavNoWrite = true;
         else
         if (strsame (SetPathPtr, "WEBDAV=LOCK=TIMEOUT=DEFAULT=", 28))
            mrpsptr->WebDavLockTimeoutDefault =
               MetaConSetSeconds (mcptr, SetPathPtr+28, 1);
         else
         if (strsame (SetPathPtr, "WEBDAV=LOCK=TIMEOUT=MAX=", 24))
            mrpsptr->WebDavLockTimeoutMax =
               MetaConSetSeconds (mcptr, SetPathPtr+24, 1);
         else
         if (strsame (SetPathPtr, "WEBSOCKET=INPUT=", 16))
            mrpsptr->WebSocketInputSize = atoi(SetPathPtr+16);
         else
         if (strsame (SetPathPtr, "WEBSOCKET=OUTPUT=", 17))
            mrpsptr->WebSocketOutputSize = atoi(SetPathPtr+17);
         else
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemSetPath);
            VmFree (mrptr, FI_LI);
            return (true);
         }
      }
   }

   if (RuleType == MAPURL_RULE_SET && !PathSet)
   {
      MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemSetPath);
      VmFree (mrptr, FI_LI);
      return (true);
   }

   if (RuleType == MAPURL_RULE_PROTECT)
   {
      /****************/
      /* protect rule */
      /****************/

      /*
         PROTECT rules are always added to the mapping database, whether
         correct or not, so that even when incorrect the path is still
         access controlled (and in an incorrect case always denied!)
      */
      sptr = AuthConfigParseProtectRule (NULL, ResultPtr, strlen(ResultPtr));
      if (sptr)
      {
         MetaConReport (mcptr, METACON_REPORT_ERROR, sptr);
         ConfigProblem = true;
      }
      AuthProtectRule = true;
   }

   /* check for optional mapping conditional(s) */
   if (*cptr == '[' || SAME2(cptr,'!['))
   {
      /***************/
      /* conditional */
      /***************/

      ConditionalPtr = cptr;
      while (*cptr == '[' || SAME2(cptr,'!['))
      {
         while (*cptr == '[' || *cptr == '!') cptr++;
         while (*cptr && *cptr != ']')
         {
            while (ISLWS(*cptr)) cptr++;
            sptr = cptr;
            while (*cptr && *cptr != ':' && !ISLWS(*cptr) && *cptr != ']')
            {
               if (*cptr == '\\') cptr++;
               *cptr = TOUP(*cptr);
               if (*cptr) cptr++;
            }
            /* basic check of conditional rule */
            if (*sptr == '!') sptr++;
            if ((!SAME2(sptr,'AC') &&
                 !SAME2(sptr,'AL') &&
                 !SAME2(sptr,'AS') &&
                 !SAME2(sptr,'CA') &&
                 !SAME2(sptr,'CK') &&
                 !SAME2(sptr,'DR') &&
                 !SAME2(sptr,'EX') &&
                 !SAME2(sptr,'FO') &&
                 !SAME2(sptr,'HH') &&
                 !SAME2(sptr,'HM') &&
                 !SAME2(sptr,'HO') &&
                 !SAME2(sptr,'ME') &&
                 !SAME2(sptr,'MP') &&
                 !SAME2(sptr,'NO') &&
                 !SAME2(sptr,'PA') &&
                 !SAME2(sptr,'PI') &&
                 !SAME2(sptr,'QS') &&
                 !SAME2(sptr,'RC') &&
                 !SAME2(sptr,'RF') &&
                 !SAME2(sptr,'RQ') &&
                 !SAME2(sptr,'RU') &&
                 !SAME2(sptr,'SC') &&
                 !SAME2(sptr,'SN') &&
                 !SAME2(sptr,'SP') &&
                 !SAME2(sptr,'ST') &&
                 !SAME2(sptr,'UA') &&
                 !SAME2(sptr,'VS') &&
                 !SAME2(sptr,'XF')) ||
                sptr[2] != ':')
            {
               /***********************/
               /* conditional problem */
               /***********************/

               MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemConditional);
               VmFree (mrptr, FI_LI);
               return (true);
            }

            while (*cptr && !ISLWS(*cptr) && *cptr != ']')
            {
               if (*cptr == '\\') cptr++;
               if (*cptr) cptr++;
            }
            while (ISLWS(*cptr)) cptr++;
         }
         if (*cptr == ']') cptr++;
         while (ISLWS(*cptr)) cptr++;
      }
      /* terminate in the white-space following the conditional(s) */
      if (*cptr) *cptr = '\0';
   }

   /**********************/
   /* allowed rule check */
   /**********************/

   if (mclptr->MetaFileType == METACON_TYPE_CONFIG)
   {
      if (!AllowedInConfigFile)
      {
         MetaConReport (mcptr, METACON_REPORT_WARNING, ProblemNotAllowed);
         VmFree (mrptr, FI_LI);
         return (true);
      }
   }

   /***************************/
   /* rule consistency checks */
   /***************************/

   if (mrpsptr->RmsSubChar &&
       !isalnum(mrpsptr->RmsSubChar) && mrpsptr->RmsSubChar != '$' &&
       mrpsptr->RmsSubChar != '-' && mrpsptr->RmsSubChar != '_')
   {
      MetaConReport (mcptr, METACON_REPORT_WARNING, ProblemRmsSubstitution);
      VmFree (mrptr, FI_LI);
      return (true);
   }

   TemplateWildcardCount = 0;
   cptr = TemplatePtr;
   while (*cptr)
   {
      if (*cptr++ != '*') continue;
      TemplateWildcardCount++;
      /* contiguous count as one! */
      while (*cptr == '*') cptr++;
   }

   if (TemplateWildcardCount > REGEX_PMATCH_MAX)
   {
      MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemTooManyWildcard);
      VmFree (mrptr, FI_LI);
      return (true);
   }

   memset (&RegexPreg, 0, sizeof(RegexPreg));
   if (Config.cfMisc.RegexSyntax && *TemplatePtr == REGEX_CHAR)
   {
      cptr = StringRegexCompile (TemplatePtr+1, &RegexPreg);
      if (cptr)
      {
         MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemRegex, cptr);
         VmFree (mrptr, FI_LI);
         return (true);
      }
   }

   ResultWildcardCount = ResultSpecifiedWildcardCount = 0;
   cptr = ResultPtr;
   while (*cptr)
   {
      if (*cptr++ != '*') continue;
      ResultWildcardCount++;
      /* contiguous count as one! */
      while (*cptr == '*') cptr++;
      if (*cptr != '\'') continue;
      ResultSpecifiedWildcardCount++;
      cptr++;
      if (*cptr < '1' || *cptr > '9')
      {
         MetaConReport (mcptr, METACON_REPORT_ERROR,
                        ProblemSpecifiedWildcardRange);
         VmFree (mrptr, FI_LI);
         return (true);
      }
   }

   if (WATCH_MODULE(WATCH_MOD_MAPURL))
      WatchThis (WATCHALL, WATCH_MOD_MAPURL,
                 "!&Z !&Z !&Z !UL !UL !UL",
                 TemplatePtr, ResultPtr, ConditionalPtr,
                 TemplateWildcardCount, ResultWildcardCount,
                 ResultSpecifiedWildcardCount);

   ResultPathOds = 0;

   switch (RuleType)
   {
      case MAPURL_RULE_SCRIPT :

         if (!ResultPtr[0])
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemResultRequired);
            VmFree (mrptr, FI_LI);
            return (true);
         }
         if (isdigit(ResultPtr[0]))
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemResultStatus);
            VmFree (mrptr, FI_LI);
            return (true);
         }
         if (ResultSpecifiedWildcardCount)
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR,
                           ProblemSpecifiedWildcardCannot);
            VmFree (mrptr, FI_LI);
            return (true);
         }
         if (ResultWildcardCount != TemplateWildcardCount)
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR,
                           ProblemWildcardMapping);
            VmFree (mrptr, FI_LI);
            return (true);
         }
         break;

      case MAPURL_RULE_EXEC :
      case MAPURL_RULE_UXEC :

         if (!ResultPtr[0])
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemResultRequired);
            VmFree (mrptr, FI_LI);
            return (true);
         }
         if (isdigit(ResultPtr[0]))
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemResultStatus);
            VmFree (mrptr, FI_LI);
            return (true);
         }
         if (ResultSpecifiedWildcardCount)
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR,
                           ProblemSpecifiedWildcardCannot);
            VmFree (mrptr, FI_LI);
            return (true);
         }
         if (!ResultWildcardCount ||
             !TemplateWildcardCount ||
             ResultWildcardCount != TemplateWildcardCount)
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR,
                           ProblemWildcardMapping);
            VmFree (mrptr, FI_LI);
            return (true);
         }
         break;

      case MAPURL_RULE_FAIL :
         break;

      case MAPURL_RULE_PASS :

         if (ResultSpecifiedWildcardCount &&
             ResultSpecifiedWildcardCount != ResultWildcardCount)
         {
            MetaConReport (mcptr, METACON_REPORT_WARNING,
                           ProblemSpecifiedWildcardMix);
            VmFree (mrptr, FI_LI);
            return (true);
         }
         if (((ResultPtr[0] == '\"' || ResultPtr[0] == '\'') &&
              !isdigit(ResultPtr[0]) &&
              ResultWildcardCount > TemplateWildcardCount) ||
             ((ResultPtr[0] != '\"' && ResultPtr[0] != '\'') &&
              !isdigit(ResultPtr[0]) &&
              ResultWildcardCount > TemplateWildcardCount))
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR,
                           ProblemWildcardMapping);
            VmFree (mrptr, FI_LI);
            return (true);
         }

#ifdef ODS_EXTENDED

         /* determine the on-disk structure */
         zptr = (sptr = DiskDevice) + sizeof(DiskDevice)-1;
         if (ResultPtr[0])
            cptr = ResultPtr;
         else
            cptr = TemplatePtr;
         if (*cptr == '/') cptr++;
         while (*cptr && *cptr != '/' && sptr < zptr) *sptr++ = *cptr++;
         if (sptr < zptr) *sptr++ = ':';
         *sptr = '\0';
         ResultPathOds = OdsVolumeStructure (DiskDevice);

#endif /* ODS_EXTENDED */

         break;

      case MAPURL_RULE_MAP :
      case MAPURL_RULE_REDIRECT :
      case MAPURL_RULE_USER :

         if (!ResultPtr[0])
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemResultRequired);
            VmFree (mrptr, FI_LI);
            return (true);
         }
         if (isdigit(ResultPtr[0]))
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemResultStatus);
            VmFree (mrptr, FI_LI);
            return (true);
         }
         if (ResultSpecifiedWildcardCount &&
             ResultSpecifiedWildcardCount != ResultWildcardCount)
         {
            MetaConReport (mcptr, METACON_REPORT_WARNING,
                           ProblemSpecifiedWildcardMix);
            VmFree (mrptr, FI_LI);
            return (true);
         }
         if (ResultWildcardCount > TemplateWildcardCount)
         {
            MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemWildcardMapping);
            VmFree (mrptr, FI_LI);
            return (true);
         }
         break;

      case MAPURL_RULE_PROTECT :
      case MAPURL_RULE_SET :

         break;

      default :
         MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemConfused);
         VmFree (mrptr, FI_LI);
         return (true);
   }

   /*****************************/
   /* throttle integrity checks */
   /*****************************/

   if (mrpsptr->ThrottleSet &&
       !mrpsptr->ThrottleNoSet)
   {
      /* throttle=from,to,resume,busy,t/o-resume,t/o-busy */
      if ((mrpsptr->ThrottleTo &&
           mrpsptr->ThrottleTo <= mrpsptr->ThrottleFrom) ||
          (mrpsptr->ThrottleResume && !mrpsptr->ThrottleTo) ||
          (mrpsptr->ThrottleResume &&
           mrpsptr->ThrottleResume <= mrpsptr->ThrottleTo) ||
          (mrpsptr->ThrottleBusy && mrpsptr->ThrottleTo &&
           mrpsptr->ThrottleBusy <= mrpsptr->ThrottleTo) ||
          (mrpsptr->ThrottleBusy && mrpsptr->ThrottleResume &&
           mrpsptr->ThrottleBusy <= mrpsptr->ThrottleResume))
      {
         MetaConReport (mcptr, METACON_REPORT_WARNING, ProblemThrottleValues);
         VmFree (mrptr, FI_LI);
         return (true);
      }
   }

   /************************************/
   /* script=param=(name=value) checks */
   /************************************/

   if (mrpsptr->ScriptParamsPtr)
   {
      /* these will generally end up as DCL symbols for a script */
      cptr = mrpsptr->ScriptParamsPtr;
      for (;;)
      {
         status = StringParseNameValue (&cptr, true,
                                        Name, sizeof(Name),
                                        Value, sizeof(Value));
         if (VMSnok (status)) break;
      }
      if (status != SS$_ENDOFFILE)
      {
         MetaConReport (mcptr, METACON_REPORT_WARNING, ProblemNameValue);
         VmFree (mrptr, FI_LI);
         return (true);
      }
   }

   /*********************************/
   /* add the rule as the line data */
   /*********************************/

   /* set the meta-config data */
   mclptr->LineDataPtr = mrptr;
   mclptr->ConfigProblem = ConfigProblem;

   /* set the rule data */
   mrptr->MetaConNumber = mclptr->Number;
   mrptr->RuleType = RuleType;
   mrptr->TemplatePtr = TemplatePtr;
   mrptr->ResultPtr = ResultPtr;
   mrptr->ResultLength = strlen(ResultPtr);
   mrptr->ConditionalPtr = ConditionalPtr;
   mrptr->PathSet = PathSet;
   mrptr->IsCgiPlusScript = IsCgiPlusScript;

#ifdef ODS_EXTENDED

   if (ResultPathOds)
   {
      /* indicate that this path is located on an ODS-5 volume */
      mrptr->ResultPathOds = ResultPathOds;
      /* indicate that at least one mapped volume is ODS-5 */
      if (ResultPathOds == MAPURL_PATH_ODS_5) MapUrlPathOds5 = true;
   }

#endif /* ODS_EXTENDED */

   if (RegexPreg.buffer)
   {
      /* the template was a regular expression, create a compiled version */
      StringRegexCompile (TemplatePtr+1, &mrptr->RegexPregTemplate);
      /* free the temporary compiled version */
      regfree (&RegexPreg);
   }

   if (mrpsptr->ThrottleSet &&
       !mrpsptr->ThrottleNoSet)
   {
      mrpsptr->ThrottleIndex = mmptr->ThrottleIndex++;
      if (mmptr->ThrottleIndex > mmptr->ThrottleTotal)
         mmptr->ThrottleTotal = mmptr->ThrottleIndex;
   }

   if (mrpsptr->ProxyBindIpAddressLength)
   {
      status = TcpIpStringToAddress (mrpsptr->ProxyBindIpAddressPtr,
                                     &mrpsptr->ProxyBindIpAddress);
      if (VMSnok (status))
         MetaConReport (mcptr, METACON_REPORT_ERROR,
                        "Could not get bind address for !AZ",
                        mrpsptr->ProxyBindIpAddressPtr);
   }

   if (mrpsptr->ProxyChainHostPortLength)
   {
      IPADDRESS_ZERO4 (&mrpsptr->ProxyChainIpAddress) 
      status = NetHostNameLookup (mrpsptr->ProxyChainHostPortPtr,
                                  0, NULL, NULL, NULL,
                                  &mrpsptr->ProxyChainIpAddress,
                                  &mrpsptr->ProxyChainPort);
      if (!mrpsptr->ProxyChainPort)
         mrpsptr->ProxyChainPort = DEFAULT_HTTP_PROXY_PORT;
      if (VMSnok (status))
      {
         MetaConReport (mcptr, METACON_REPORT_ERROR, ProblemHostNameLookup, 
                        mrpsptr->ProxyChainHostPortPtr, status);
         /* make sure the connection can't succeed */
         IPADDRESS_SET_UNUSABLE (&mrpsptr->ProxyChainIpAddress)
         mrpsptr->ProxyChainPort = 0;
      }
   }

   /* note that we've got an extension-method rule */
   if (mrpsptr->MapExtensionMethod) MapUrlExtensionMethod = true;

   if (WATCH_MODULE_DETAIL)
      WatchDataFormatted ("!&X !UL !&Z !&Z (!UL) !&B !&Z\n",
         mrptr, mrptr->RuleType, mrptr->TemplatePtr,
         mrptr->ResultPtr, mrptr->ResultPathOds, mrptr->PathSet,
         mrptr->ConditionalPtr);

   return (true);
}

/*****************************************************************************/
/*
Given the current document's URL path and the specified document's URL path, 
generate a resultant URL path based on whether the specified document's path 
is absolute or relative the the current document's path.  Insufficient space to
contain the result path will not crash the function and the result will be set
to an empty string.
*/ 

int MapUrl_VirtualPath
(
char *CurrentPath,
char *DocumentPath,
char *ResultPath,
int SizeOfResultPath
)
{
   char  *cptr, *dptr, *sptr, *tptr, *zptr;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_MAPURL))
      WatchThis (WATCHALL, WATCH_MOD_MAPURL,
                 "MapUrl_VirtualPath() !&Z !&Z !UL",
                 CurrentPath, DocumentPath, SizeOfResultPath);

   if (*(dptr = DocumentPath) == '/' || strstr (DocumentPath, "//")) 
   {
      /*****************/
      /* absolute path */
      /*****************/

      strcpy (ResultPath, dptr);
      zptr = (sptr = ResultPath) + SizeOfResultPath;
      while (*dptr && sptr < zptr) *sptr++ = *dptr++;
      if (sptr >= zptr) return (-1);
      *sptr = '\0';
      return (sptr - ResultPath);
   }

   /* step over any "relative to this directory" syntax ("./") */
   if (dptr[0] == '.' && dptr[1] == '/') dptr += 2;
   if (*dptr != '.')
   {
      /*****************/
      /* inferior path */
      /*****************/

      zptr = (sptr = tptr = ResultPath) + SizeOfResultPath;
      cptr = CurrentPath;
      while (*cptr && sptr < zptr)
      {
         if (*cptr == '/') tptr = sptr+1;
         *sptr++ = *cptr++;
      }
      sptr = tptr;
      while (*dptr && sptr < zptr) *sptr++ = *dptr++;
      if (sptr >= zptr) return (-1);
      *sptr = '\0';
      return (sptr - ResultPath);
   }

   /*****************/
   /* relative path */
   /*****************/

   zptr = (sptr = tptr = ResultPath) + SizeOfResultPath;
   cptr = CurrentPath;
   while (*cptr && sptr < zptr)
   {
      if (*cptr == '/') tptr = sptr;
      *sptr++ = *cptr++;
   }
   sptr = tptr;
   /* loop, stepping back one level for each "../" in the document path */
   while (dptr[0] == '.' && dptr[1] == '.' && dptr[2] == '/')
   {
      dptr += 3;
      if (sptr > ResultPath) sptr--;
      while (sptr > ResultPath && *sptr != '/') sptr--;
   }
   if (sptr > ResultPath && sptr < zptr)
      sptr++;
   else
   if (sptr < zptr)
      *sptr++ = '/';
   while (*dptr && sptr < zptr) *sptr++ = *dptr++;
   if (sptr >= zptr) return (-1);
   *sptr = '\0';
   return (sptr - ResultPath);
}

/*****************************************************************************/
/*
Guarantee access to certain paths under certain conditions.
*/ 

char* MapUrl_GuaranteeAccess 
(
REQUEST_STRUCT *rqptr,
char *PathBeingMapped
)
{
   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_MAPURL))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL,
                 "MapUrl_GuaranteeAccess() !&Z", PathBeingMapped);

   if (AuthPromiscuous ||
       HttpdGblSecPtr->AuthSkelKeyHttpdTickSecond)
   {
      /* guarantee access to the administration menu */
      if (!strsame (PathBeingMapped, HTTPD_ADMIN,
                    sizeof(HTTPD_ADMIN)-1) &&
          !strsame (PathBeingMapped, WASD_ROOT_LOCAL,
                    sizeof(WASD_ROOT_LOCAL)-1) &&
          !strsame (PathBeingMapped, HT_ROOT_LOCAL,
                    sizeof(HT_ROOT_LOCAL)-1))
         return (NULL);

      if (WATCHING (rqptr, WATCH_MAPPING))
          WatchThis (WATCHITM(rqptr), WATCH_MAPPING,
                     "!AZ guarantees access to !AZ",
                     AuthPromiscuous ? "PROMISCUOUS" : "SKELKEY",
                     PathBeingMapped);

      return (PathBeingMapped);
   }

   return (NULL);
}

/*****************************************************************************/
/*
A server administration report on the server's mapping rules. This function
just wraps the reporting function, loading a temporary database if necessary
for reporting from the rule file.
*/

MapUrl_Report
(
REQUEST_STRUCT *rqptr,
BOOL UseServerDatabase,
char *VirtualService
)
{
   int  status;
   META_CONFIG  *mcptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_MAPURL))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL,
                 "MapUrl_Report() !UL !&Z",
                 UseServerDatabase, VirtualService);

   if (UseServerDatabase)
   {
      /* use mapping rules already loaded into the server */
      MapUrl_ReportNow (rqptr, true, MetaGlobalMappingPtr, VirtualService);
   }
   else
   {
      /* load temporary set of rules from mapping file */
      status = MapUrl_ConfigLoad (&mcptr);
      if (VMSnok (status))
      {
         /* severe error reported */
         rqptr->rqResponse.HttpStatus = 403;
         ErrorGeneral (rqptr, mcptr->LoadReport.TextPtr, FI_LI);
      }
      else
         MapUrl_ReportNow (rqptr, false, mcptr, VirtualService);
      MetaConUnload (&mcptr, &MapUrl_ConfigUnloadLineData);
   }

   AdminEnd (rqptr);
}

/*****************************************************************************/
/*
Return a report on the HTTPd server's mapping rules ... now!
*/

MapUrl_ReportNow
(
REQUEST_STRUCT *rqptr,
BOOL UseServerDatabase,
META_CONFIG *mcptr,
char *VirtualService
)
{
   static char  BeginRules [] =
"<p><h3><u>Mapping Rules</u></h3>\n";

   static char  BeginTable [] =
"<p><table class=\"lftlft\">\n\
<tr><td><pre>";

   static char  CommentFao [] = "<b>!4ZL</b>  !#* !AZ\n";

   static char  RuleFao [] =
"<b>!4ZL</b>  !#* !&?<strike>\r\r<b>!AZ</b>  !&;AZ!&@!&@!&@!&?</strike>\r\r\n";

   static char  NonRuleFao [] =
"<b>!4ZL</b>  !#* !&?<strike>\r\r<b>!#AZ</b>  !&;AZ!&?</strike>\r\r\n";

   static char  ProblemFao [] = "<b>!4ZL</b>  !#* <strike>!&;AZ</strike>\n";

   static char  RuleVirtualFao [] =
"<b>!4ZL</b>  !#* !&?<strike>\r\r<b>[[!&;AZ]]</b>!&?</strike>\r\r\n";

   static char  RuleImplicitVirtualFao [] =
"<b>0000</b>  <b>[[*:*]]</b>\n";

   static char  VersionFao [] = "<b>!4ZL  [[!AZ]]</b>\n";

   static char  UserMappingFao [] =
"!AZ\
<b>----</b>\
</pre>\n\
</td></tr>\n\
</table>\n\
<p><h3><u>User Mapping Cache</u></h3>\n\
<p><table class=\"lftlft\">\n\
<tr>\
<th></th>\
<th class=\"sbttl\">Name</th>\
<th class=\"sbttl\">Path</th>\
<th class=\"sbttl\">ODS</th>\
<th class=\"sbttl\">Hits</th>\
<th class=\"sbttl\">Last</th>\
<th class=\"sbttl\">Reuse</th>\
</tr>\n\
<tr HEIGHT=5></tr>\n";

   static char  EntryFao [] =
"<tr!AZ>\
<th class=\"targht\">!4ZL</th>\
<td>!AZ&nbsp;&nbsp;</td>\
<td>!AZ&nbsp;&nbsp;</td>\
<td>!AZ&nbsp;&nbsp;</td>\
<td>!UL&nbsp;&nbsp;</td>\
<td><nobr>!20%D&nbsp;&nbsp;</nobr></td>\
<td>!UL</td>\
</tr>\n";

   static char EmptyUserMappingFao [] =
"<tr class=\"hlght\"><th>0000</th><td colspan=\"6\"><i>empty</i></td><tr>\n";

   static char  EndPageFao [] =
"</table>\n\
</div>\n\
</body>\n\
</html>\n";

   BOOL  FinalRule,
         ThisVirtualService;
   int  status,
        EntryCount;
   unsigned long  FaoVector [32];
   unsigned long  *vecptr;
   unsigned short  Length;
   char  *cptr;
   char  Buffer [MAP_BUFFER_SIZE],
         RmsSubChar [2],
         TimeString [32];
   LIST_ENTRY  *leptr;
   MAP_RULE_META  *mrptr;
   MAP_URL_USER_ENTRY  *ucptr;
   METACON_LINE  *mclptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_MAPURL))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MapUrl_ReportNow() !&B !&Z",
                 UseServerDatabase, VirtualService);

   AdminPageTitle (rqptr, "Path Mapping");
   AdminMetaConReport (rqptr, mcptr, MetaGlobalMappingPtr);
   AdminMetaConSource (rqptr, mcptr, MetaGlobalMappingPtr);

   /*****************/
   /* mapping rules */
   /*****************/

   status = FaolToNet (rqptr, BeginRules, NULL);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   AdminVirtualServiceForm (rqptr, ADMIN_REPORT_MAPPING,
                            VirtualService, UseServerDatabase);

   status = FaolToNet (rqptr, BeginTable, NULL);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   status = SS$_NORMAL;
   EntryCount = 0;
   FinalRule = false;
   ThisVirtualService = true;

   MetaConParseReset (mcptr, true);
   while (mclptr = MetaConParseRaw (mcptr))
   {
      if (mclptr->Token == METACON_TOKEN_SERVICE)
      {
         /* if filtering on a virtual service */
         if (VirtualService[0] &&
             !StringMatch (rqptr, VirtualService, mclptr->TextPtr))
         {
            ThisVirtualService = false;
            continue;
         }
         ThisVirtualService = true;
         vecptr = FaoVector;
         *vecptr++ = mclptr->Number;
         *vecptr++ = (mclptr->MetaFileLevel+mclptr->FlowControlLevel) * 3;
         *vecptr++ = mclptr->ConfigProblem || FinalRule;
         *vecptr++ = mclptr->TextPtr;
         *vecptr++ = mclptr->ConfigProblem || FinalRule;
         status = FaolToNet (rqptr, RuleVirtualFao, &FaoVector);
         if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
         continue;
      }
      else
      if (mclptr->Number == 1)
      {
         status = FaolToNet (rqptr, RuleImplicitVirtualFao, NULL);
         if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
      }

      if (!ThisVirtualService) continue;
      EntryCount++;

      if (mclptr->Token != METACON_TOKEN_TEXT)
      {
         for (cptr = mclptr->TextPtr; *cptr && !ISLWS(*cptr); cptr++);
         vecptr = FaoVector;
         *vecptr++ = mclptr->Number;

         if (mclptr->Token == METACON_TOKEN_COMMENT)
         {
            *vecptr++ = (mclptr->MetaFileLevel+
                         mclptr->FlowControlLevel+1) * 3;
            *vecptr++ = mclptr->TextPtr;
            status = FaolToNet (rqptr, CommentFao, &FaoVector);
            if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
            continue;
         }

         if (mclptr->Token == METACON_TOKEN_VERSION)
         {
            *vecptr++ = mclptr->TextPtr;
            status = FaolToNet (rqptr, VersionFao, &FaoVector);
            if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
            continue;
         }

         switch (mclptr->Token)
         {
            case METACON_TOKEN_ELIF :
            case METACON_TOKEN_ELSE :
            case METACON_TOKEN_UNIF :
            case METACON_TOKEN_IFIF :
               if (mclptr->FlowControlLevel)
                  *vecptr++ = (mclptr->MetaFileLevel+
                               mclptr->FlowControlLevel) * 3;
               else
                  *vecptr++ = (mclptr->MetaFileLevel+
                               mclptr->FlowControlLevel+1) * 3;
               break;
            default:
               *vecptr++ = (mclptr->MetaFileLevel+
                            mclptr->FlowControlLevel+1) * 3;
         }
         *vecptr++ = mclptr->ConfigProblem || FinalRule ||
                     (mclptr->InlineTextPtr && !mclptr->LineDataPtr);
         *vecptr++ = cptr - mclptr->TextPtr;
         *vecptr++ = mclptr->TextPtr;
         while (*cptr && ISLWS(*cptr)) cptr++;
         *vecptr++ = cptr;
         *vecptr++ = mclptr->ConfigProblem || FinalRule ||
                     (mclptr->InlineTextPtr && !mclptr->LineDataPtr);
         status = FaolToNet (rqptr, NonRuleFao, &FaoVector);
         if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
         continue;
      }

      vecptr = FaoVector;
      *vecptr++ = mclptr->Number;
      *vecptr++ = (mclptr->MetaFileLevel+
                   mclptr->FlowControlLevel) * 3 + 3;

      if (!(mrptr = mclptr->LineDataPtr))
      {
         *vecptr++ = mclptr->TextPtr;
         status = FaolToNet (rqptr, ProblemFao, &FaoVector);
         if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
         continue;
      }

      if (WATCHMOD (rqptr, WATCH_MODULE_DETAIL))
         WatchDataFormatted ("!&X !UL !&Z !&Z !&Z\n",
            mrptr, mrptr->RuleType, mrptr->TemplatePtr,
            mrptr->ResultPtr, mrptr->ConditionalPtr);

      *vecptr++ = mclptr->ConfigProblem || FinalRule;

      if (mrptr->RuleType == MAPURL_RULE_EXEC)
      {
         if (mrptr->IsCgiPlusScript)
            *vecptr++ = "exec+";
         else
            *vecptr++ = "exec";
      }
      else
      if (mrptr->RuleType == MAPURL_RULE_FAIL)
         *vecptr++ = "fail";
      else
      if (mrptr->RuleType == MAPURL_RULE_MAP)
         *vecptr++ = "map";
      else
      if (mrptr->RuleType == MAPURL_RULE_PASS)
         *vecptr++ = "pass";
      else
      if (mrptr->RuleType == MAPURL_RULE_PROTECT)
         *vecptr++ = "protect";
      else
      if (mrptr->RuleType == MAPURL_RULE_REDIRECT)
         *vecptr++ = "redirect";
      else
      if (mrptr->RuleType == MAPURL_RULE_SCRIPT)
      {
         if (mrptr->IsCgiPlusScript)
            *vecptr++ = "script+";
         else
            *vecptr++ = "script";
      }
      else
      if (mrptr->RuleType == MAPURL_RULE_SET)
         *vecptr++ = "set";
      else
      if (mrptr->RuleType == MAPURL_RULE_UXEC)
         *vecptr++ = "uxec";
      else
      if (mrptr->RuleType == MAPURL_RULE_USER)
         *vecptr++ = "user";
      else
         *vecptr++ = "*ERROR*";

      *vecptr++ = mrptr->TemplatePtr;
      if (isdigit(*mrptr->ResultPtr))
      {
         *vecptr++ = "  &quot;!&;AZ&quot;";
         *vecptr++ = mrptr->ResultPtr;
      }
      else
      if (mrptr->ResultPtr[0])
      {
         *vecptr++ = "  !&;AZ";
         *vecptr++ = mrptr->ResultPtr;
      }
      else
         *vecptr++ = "";

      if (mrptr->PathSet)
      {
         /* just one space, ExplainPathSet() adds it's own leading space */
         *vecptr++ = " !&;AZ";
         *vecptr++ = MapUrl_ExplainPathSet (rqptr, mrptr);
      }
      else
         *vecptr++ = "";

      if (mrptr->ConditionalPtr[0])
      {
         *vecptr++ = "  !&;AZ";
         *vecptr++ = mrptr->ConditionalPtr;
      }
      else
         *vecptr++ = "";

      *vecptr++ = mclptr->ConfigProblem || FinalRule;

      status = FaolToNet (rqptr, RuleFao, &FaoVector);
      if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

      /* stop reporting a specific virtual service if terminating rule */
      if (!VirtualService[0]) continue;
      if ((mrptr->RuleType == MAPURL_RULE_FAIL ||
           mrptr->RuleType == MAPURL_RULE_PASS) &&
           SAME2(mrptr->TemplatePtr,'*\0'))
         FinalRule = true;
   }

   vecptr = FaoVector;
   if (EntryCount)
      *vecptr++ = "";
   else
      *vecptr++ = "<i>(none)</i>\n";
   *vecptr++ = MapUrlUserNameCacheEntries;

   status = FaolToNet (rqptr, UserMappingFao, &FaoVector);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   /*****************/
   /* user mappings */
   /*****************/

   EntryCount = 0;

   /* process the cache entry list from most to least recent */
   for (leptr = MapUrlUserNameCacheList.HeadPtr;
        leptr;
        leptr = leptr->NextPtr)
   {
      ucptr = (MAP_URL_USER_ENTRY*)leptr;

      /* if empty name, must have been reset, no point in going any further */
      if (!ucptr->UserName[0]) break;

      vecptr = FaoVector;

      EntryCount++;
      if (EntryCount % 2)
         *vecptr++ = " class=\"hlght\"";
      else
         *vecptr++ = " class=\"hlght\"";
      *vecptr++ = EntryCount;
      *vecptr++ = ucptr->UserName;
      *vecptr++ = ucptr->UserPath;

      if (ucptr->PathOds == MAPURL_PATH_ODS_2)
         *vecptr++ = "ODS-2";
      else
      if (ucptr->PathOds == MAPURL_PATH_ODS_5)
         *vecptr++ = "ODS-5";
      else
         *vecptr++ = "ods-2";

      *vecptr++ = ucptr->HitCount;
      *vecptr++ = &ucptr->LastBinaryTime;
      *vecptr++ = ucptr->ReuseCount;

      status = FaolToNet (rqptr, EntryFao, &FaoVector);
      if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
   }
   if (!MapUrlUserNameCacheList.HeadPtr)
   {
      status = FaolToNet (rqptr, EmptyUserMappingFao, NULL);
      if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);
   }

   status = FaolToNet (rqptr, EndPageFao, NULL);
   if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI);

   rqptr->rqResponse.PreExpired = PRE_EXPIRE_MAPPING_RULES;
   ResponseHeader200 (rqptr, "text/html", &rqptr->NetWriteBufferDsc);
}

/*****************************************************************************/
/*
Report an individual rule during processing of a mapping rule check.
*/

MapUrl_WatchRule
(
REQUEST_STRUCT *rqptr,
MAP_RULE_META  *mrptr,
char *PathPtr,
int PathOds,
int Match,
BOOL SetPathIgnore
)
{
   static char  RuleFao [] = "!4ZL !AZ!AZ  !AZ  !8AZ  !AZ  !&@!&@!&@\n";

   int  status;
   unsigned short  Length;
   unsigned long  FaoVector [32];
   unsigned long  *vecptr;
   char  Buffer [8192],
         RmsSubChar [2];

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_MAPURL))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL,
                 "MapUrl_WatchRule() !&Z !UL !UL", PathPtr, PathOds, Match);

   vecptr = FaoVector;
   *vecptr++ = mrptr->MetaConNumber;
   *vecptr++ = PathPtr;

   if (PathOds == MAPURL_PATH_ODS_0)
      *vecptr++ = "  NOODS";
   else
   if (PathOds == MAPURL_PATH_ODS_2)
      *vecptr++ = "  ODS=2";
   else
   if (PathOds == MAPURL_PATH_ODS_5)
      *vecptr++ = "  ODS=5";
   else
   if (PathOds == MAPURL_PATH_ODS_PWK)
      *vecptr++ = "  ODS=PWK";
   else
   if (PathOds == MAPURL_PATH_ODS_SMB)
      *vecptr++ = "  ODS=SMB";
   else
   if (PathOds == MAPURL_PATH_ODS_SRI)
      *vecptr++ = "  ODS=SRI";
   else
      *vecptr++ = "";
 
   if (Match == MAPURL_REPORT_MATCH_NOT)
      *vecptr++ = "..";
   else
   if (Match == MAPURL_REPORT_MATCH_RULE)
      *vecptr++ = "YN";
   else
   if (Match == MAPURL_REPORT_MATCH_RULECOND)
      *vecptr++ = "YY";
   else
   if (Match ==  MAPURL_REPORT_MATCH_RULENOCOND)
      *vecptr++ = "Y-";
   else
      *vecptr++ = "??";

   switch (mrptr->RuleType)
   {
      case MAPURL_RULE_EXEC :
         if (mrptr->IsCgiPlusScript)
            *vecptr++ = "EXEC+";
         else
            *vecptr++ = "EXEC";
         break;
      case MAPURL_RULE_FAIL :
         *vecptr++ = "FAIL";
         break;
      case MAPURL_RULE_MAP :
         *vecptr++ = "MAP";
         break;
      case MAPURL_RULE_PASS :     
         *vecptr++ = "PASS";
         break;
      case MAPURL_RULE_PROTECT :
         *vecptr++ = "PROTECT";
         break;
      case MAPURL_RULE_REDIRECT :
         *vecptr++ = "REDIRECT";
         break;
      case MAPURL_RULE_SCRIPT :
         if (mrptr->IsCgiPlusScript)
            *vecptr++ = "SCRIPT+";
         else
            *vecptr++ = "SCRIPT";
         break;
      case MAPURL_RULE_SET :
         if (SetPathIgnore)
            *vecptr++ = "(SET)";
         else
            *vecptr++ = "SET";
         break;
      case MAPURL_RULE_USER :
         *vecptr++ = "USER";
         break;
      case MAPURL_RULE_UXEC :
         if (mrptr->IsCgiPlusScript)
            *vecptr++ = "UXEC+";
         else
            *vecptr++ = "UXEC";
         break;
      default :
         *vecptr++ = "*ERROR*";
   }

   *vecptr++ = mrptr->TemplatePtr;

   if (isdigit(*mrptr->ResultPtr))
   {
      *vecptr++ = "\"!AZ\"";
      *vecptr++ = mrptr->ResultPtr;
   }
   else
   {
      *vecptr++ = "!AZ";
      *vecptr++ = mrptr->ResultPtr;
   }

   if (mrptr->PathSet)
   {
      *vecptr++ = " !AZ";
      *vecptr++ = MapUrl_ExplainPathSet (rqptr, mrptr);
   }
   else
      *vecptr++ = "";

   if (mrptr->ConditionalPtr[0])
   {
      *vecptr++ = " !AZ";
      *vecptr++ = mrptr->ConditionalPtr;
   }
   else
      *vecptr++ = "";

   status = FaolToBuffer (Buffer, sizeof(Buffer), &Length, RuleFao, &FaoVector);
   if (VMSnok (status) || status == SS$_BUFFEROVF)
      ErrorNoticed (rqptr, status, NULL, FI_LI);

   if (WATCHING (rqptr, WATCH_MAPPING))
      WatchData (Buffer, Length); 
}

/*****************************************************************************/
/*
Display mapping rules used to resolve the path in the query string.  Called
from the Request.c modules.  For HTTP report, uses blocking network write.
There are a number of intermediate FaolToBuffer()s.  This was done only to make
the format strings a bit easier to manage.
*/

char* MapUrl_ExplainPathSet
(
REQUEST_STRUCT *rqptr,
MAP_RULE_META *mrptr
)
{
   static char  Buffer [MAP_BUFFER_SIZE];

   int  blen, status;
   unsigned short  slen;
   unsigned long  FaoVector [32];
   unsigned long  *vecptr;
   char  *bptr;
   char  FaoString [1024],
         RegexNumString [32];
   MAP_SET_META  *mrpsptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_MAPURL))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MapUrl_ExplainPathSet()");

   bptr = Buffer;
   blen = sizeof(Buffer);
   mrpsptr = &mrptr->mpPathSet;

   vecptr = FaoVector;
   FaoString[0] = 0;

   if (mrpsptr->NoAcceptLang)
      strcat (FaoString, " NOaccept=LANG");
   else
   if (mrpsptr->AcceptLangChar)
   {
      strcat (FaoString, " accept=LANG=(default=!AZ,char=!&C,!&?type\rname\r)");
      if (mrpsptr->AcceptLangPtr)
         *vecptr++ = mrpsptr->AcceptLangPtr;
      else
         *vecptr++ = "";
      *vecptr++ = mrpsptr->AcceptLangChar;
      *vecptr++ = mrpsptr->AcceptLangTypeVariant;
   }

   if (mrpsptr->NoAlert)
      strcat (FaoString, " NOALERT");
   else
   if (mrpsptr->Alert == MAPURL_PATH_ALERT_END)
      strcat (FaoString, " ALERT=end");
   else
   if (mrpsptr->Alert == MAPURL_PATH_ALERT_AUTH)
      strcat (FaoString, " ALERT=auth");
   else
   if (mrpsptr->Alert == MAPURL_PATH_ALERT_MAP)
      strcat (FaoString, " ALERT=map");
   else
   if (mrpsptr->Alert)
   {
      strcat (FaoString, " ALERT=!UL");
      *vecptr++ = mrpsptr->Alert;
   }

   if (mrpsptr->NoAuthAll)
      strcat (FaoString, " NOauth=ALL");
   else
   if (mrpsptr->AuthAll)
      strcat (FaoString, " auth=ALL");

   if (mrpsptr->NoAuthMapped)
      strcat (FaoString, " NOauth=MAPPED");
   else
   if (mrpsptr->AuthMapped)
      strcat (FaoString, " auth=MAPPED");

   if (mrpsptr->NoAuthOnce)
      strcat (FaoString, " NOauth=ONCE");
   else
   if (mrpsptr->AuthOnce)
      strcat (FaoString, " auth=ONCE");

   if (mrpsptr->AuthSysUafPwdExpUrlPtr)
   {
      strcat (FaoString, " auth=SYSUAF=pwdexpURL=!&\"AZ");
      *vecptr++ = mrpsptr->AuthSysUafPwdExpUrlPtr;
   }

   if (mrpsptr->AuthRevalidateTimeout)
   {
      strcat (FaoString, " auth=REVALIDATE=!AZ");
      *vecptr++ = MetaConShowSeconds (rqptr, mrpsptr->AuthRevalidateTimeout);
   }

   if (FaoString[0])
   {
      status = FaolToBuffer (bptr, blen, &slen, FaoString, &FaoVector);
      if (VMSnok (status) || status == SS$_BUFFEROVF)
      {
         ErrorNoticed (rqptr, status, NULL, FI_LI);
         return ("*ERROR*");
      }
      bptr += slen;
      blen -= slen;
      vecptr = FaoVector;
      FaoString[0] = 0;
   }

   if (mrpsptr->NoCache)
      strcat (FaoString, " NOcache");
   else
   if (mrpsptr->Cache)
      strcat (FaoString, " cache");

   if (mrpsptr->CacheNoCGI)
      strcat (FaoString, " cache=noCGI");
   else
   if (mrpsptr->CacheCGI)
      strcat (FaoString, " cache=CGI");

   if (mrpsptr->CacheNoCookie)
      strcat (FaoString, " cache=nocookie");
   else
   if (mrpsptr->CacheCookie)
      strcat (FaoString, " cache=cookie");

   if (mrpsptr->CacheNoFile)
      strcat (FaoString, " cache=NOfile");
   else
   if (mrpsptr->CacheFile)
      strcat (FaoString, " cache=file");

   if (mrpsptr->CacheNoNet)
      strcat (FaoString, " cache=NOnet");
   else
   if (mrpsptr->CacheNet)
      strcat (FaoString, " cache=net");

   if (mrpsptr->CacheNoNPH)
      strcat (FaoString, " cache=noNPH");
   else
   if (mrpsptr->CacheNPH)
      strcat (FaoString, " cache=NPH");

   if (mrpsptr->CacheNoPermanent)
      strcat (FaoString, " cache=NOpermanent");
   else
   if (mrpsptr->CachePermanent)
      strcat (FaoString, " cache=permanent");

   if (mrpsptr->CacheNoQuery)
      strcat (FaoString, " cache=NOquery");
   else
   if (mrpsptr->CacheQuery)
      strcat (FaoString, " cache=query");

   if (mrpsptr->CacheNoScript)
      strcat (FaoString, " cache=NOscript");
   else
   if (mrpsptr->CacheScript)
      strcat (FaoString, " cache=script");

   if (mrpsptr->CacheNoSSI)
      strcat (FaoString, " cache=noSSI");
   else
   if (mrpsptr->CacheSSI)
      strcat (FaoString, " cache=SSI");

   if (mrpsptr->CacheExpiresAfter == CACHE_EXPIRES_NONE)
      strcat (FaoString, " cache=expires=none");
   else
   if (mrpsptr->CacheExpiresAfter == CACHE_EXPIRES_DAY)
      strcat (FaoString, " cache=expires=day");
   else
   if (mrpsptr->CacheExpiresAfter == CACHE_EXPIRES_HOUR)
      strcat (FaoString, " cache=expires=hour");
   else
   if (mrpsptr->CacheExpiresAfter == CACHE_EXPIRES_MINUTE)
      strcat (FaoString, " cache=expires=minute");
   else
   if (mrpsptr->CacheExpiresAfter)
   {
      strcat (FaoString, " cache=expires=!AZ");
      *vecptr++ = MetaConShowSeconds (rqptr, mrpsptr->CacheExpiresAfter);
   }

   if (mrpsptr->CacheGuardSeconds)
   {
      strcat (FaoString, " cache=guard=!UL");
      if (mrpsptr->CacheGuardSeconds == -1)
         *vecptr++ = 0;
      else
         *vecptr++ = mrpsptr->CacheGuardSeconds;
   }

   if (mrpsptr->CacheMaxKBytes)
   {
      strcat (FaoString, " cache=max=!UL");
      if (mrpsptr->CacheMaxKBytes == -1)
         *vecptr++ = 0;
      else
         *vecptr++ = mrpsptr->CacheMaxKBytes;
   }

   if (mrpsptr->CgiPlusInNoWriteof)
      strcat (FaoString, " cgiplusin=NOeof");
   else
   if (mrpsptr->CgiPlusInWriteof)
      strcat (FaoString, " cgiplusin=EOF");

   if (mrpsptr->CgiPlusInCC[0] || mrpsptr->CgiPlusInCC[1])
   {
      if (mrpsptr->CgiPlusInCC[0] == '\r' &&
          mrpsptr->CgiPlusInCC[1] == '\n')
         strcat (FaoString, " cgiplusin=cc=CRLF");
      else
      if (mrpsptr->CgiPlusInCC[0] == '\n')
         strcat (FaoString, " cgiplusin=cc=LF");
      else
      if (mrpsptr->CgiPlusInCC[0] == '\r')
         strcat (FaoString, " cgiplusin=cc=CR");
      else
         strcat (FaoString, " cgiplusin=cc=NONE");
   }

   if (mrpsptr->CgiPrefixPtr)
   {
      strcat (FaoString, " CGI=prefix=!AZ");
      *vecptr++ = mrpsptr->CgiPrefixPtr;
   }

   if (mrpsptr->CharsetPtr)
   {
      strcat (FaoString, " charset=!&\"AZ");
      *vecptr++ = mrpsptr->CharsetPtr;
   }

   if (mrpsptr->ClientAddress == MAPURL_CLIENT_FORWARDED)
      strcat (FaoString, " client=FORWARDED");
   else
   if (mrpsptr->ClientAddress == MAPURL_CLIENT_IF_FORWARDED)
      strcat (FaoString, " client=IF=FORWARDED");
   else
   if (mrpsptr->ClientAddress == MAPURL_CLIENT_IF_XFORWARDEDFOR)
      strcat (FaoString, " client=IF=XFORWARDEDFOR");
   else
   if (mrpsptr->ClientAddress == MAPURL_CLIENT_LITERAL)
   {
      strcat (FaoString, " client=LITERAL=\"!AZ\"");
      *vecptr++ = mrpsptr->ClientAddressLiteralPtr;
   }
   else
   if (mrpsptr->ClientAddress == MAPURL_CLIENT_RESET)
      strcat (FaoString, " client=RESET");
   else
   if (mrpsptr->ClientAddress == MAPURL_CLIENT_XFORWARDEDFOR)
      strcat (FaoString, " client=XFORWARDEDFOR");

   if (mrpsptr->ContentTypePtr)
   {
      strcat (FaoString, " content=!&\"AZ");
      *vecptr++ = mrpsptr->ContentTypePtr;
   }

   if (mrpsptr->CorsMaxAge)
   {
      strcat (FaoString, " cors=AGE=!UL");
      *vecptr++ = mrpsptr->CorsMaxAge;
   }

   if (mrpsptr->CorsAllowCredentials)
      strcat (FaoString, " cors=CRED=true");
   else
   if (mrpsptr->CorsNoAllowCredentials)
      strcat (FaoString, " cors=CRED=false");

   if (mrpsptr->CorsExposeHeadersPtr)
   {
      strcat (FaoString, " cors=EXPOSE=!&\"AZ");
      *vecptr++ = mrpsptr->CorsExposeHeadersPtr;
   }

   if (mrpsptr->CorsAllowHeadersPtr)
   {
      strcat (FaoString, " cors=HEADERS=!&\"AZ");
      *vecptr++ = mrpsptr->CorsAllowHeadersPtr;
   }

   if (mrpsptr->CorsAllowMethodsPtr)
   {
      strcat (FaoString, " cors=METHODS=!&\"AZ");
      *vecptr++ = mrpsptr->CorsAllowMethodsPtr;
   }

   if (mrpsptr->CorsAllowOriginPtr)
   {
      strcat (FaoString, " cors=ORIGIN=!&\"AZ");
      *vecptr++ = mrpsptr->CorsAllowOriginPtr;
   }

   if (FaoString[0])
   {
      status = FaolToBuffer (bptr, blen, &slen, FaoString, &FaoVector);
      if (VMSnok (status) || status == SS$_BUFFEROVF)
      {
         ErrorNoticed (rqptr, status, NULL, FI_LI);
         return ("*ERROR*");
      }
      bptr += slen;
      blen -= slen;
      vecptr = FaoVector;
      FaoString[0] = 0;
   }

   if (mrpsptr->DictionaryPtr)
   {
      strcat (FaoString, " dict=!&\"AZ");
      *vecptr++ = mrpsptr->DictionaryPtr;
   }

   if (mrpsptr->DirNoAccess)
      strcat (FaoString, " dir=NOaccess");
   else
   if (mrpsptr->DirAccess)
      strcat (FaoString, " dir=access");
   else
   if (mrpsptr->DirAccessSelective)
      strcat (FaoString, " dir=access=selective");

   if (mrpsptr->DirCharsetPtr)
   {
      strcat (FaoString, " dir=charset=!&\"AZ");
      *vecptr++ = mrpsptr->DirCharsetPtr;
   }

   if (mrpsptr->DirDelimit == MAPURL_DIR_DELIMIT_BOTH)
      strcat (FaoString, " dir=DELIMIT=both");
   else
   if (mrpsptr->DirDelimit == MAPURL_DIR_DELIMIT_HEADER)
      strcat (FaoString, " dir=DELIMIT=header");
   else
   if (mrpsptr->DirDelimit == MAPURL_DIR_DELIMIT_FOOTER)
      strcat (FaoString, " dir=DELIMIT=footer");
   else
   if (mrpsptr->DirDelimit == MAPURL_DIR_DELIMIT_NONE)
      strcat (FaoString, " dir=DELIMIT=none");

   if (mrpsptr->DirFont == MAPURL_DIR_FONT_INHERIT)
      strcat (FaoString, " dir=FONT=inherit");
   else
   if (mrpsptr->DirFont == MAPURL_DIR_FONT_MONOSPACE)
      strcat (FaoString, " dir=FONT=monospace");

   if (mrpsptr->DirNoIconLink)
      strcat (FaoString, " dir=NOilink");
   else
   if (mrpsptr->DirIconLink)
      strcat (FaoString, " dir=ilink");

   if (mrpsptr->DirNoImpliedWildcard)
      strcat (FaoString, " dir=NOimpliedwildcard");
   else
   if (mrpsptr->DirImpliedWildcard)
      strcat (FaoString, " dir=impliedwildcard");

   if (mrpsptr->DirSort[0])
   {
      strcat (FaoString, " dir=SORT=!AZ");
      *vecptr++ = mrpsptr->DirSort;
   }

   if (mrpsptr->DirStyle == MAPURL_DIR_STYLE_DEFAULT)
      strcat (FaoString, " dir=style=default");
   else
   if (mrpsptr->DirStyle == MAPURL_DIR_STYLE_DEFAULT2)
      strcat (FaoString, " dir=style=default2");
   else
   if (mrpsptr->DirStyle == MAPURL_DIR_STYLE_ANCHOR)
      strcat (FaoString, " dir=style=anchor");
   else
   if (mrpsptr->DirStyle == MAPURL_DIR_STYLE_ANCHOR2)
      strcat (FaoString, " dir=style=anchor2");
   else
   if (mrpsptr->DirStyle == MAPURL_DIR_STYLE_HTDIR)
      strcat (FaoString, " dir=style=htdir");
   else
   if (mrpsptr->DirStyle == MAPURL_DIR_STYLE_HTDIR2)
      strcat (FaoString, " dir=style=htdir2");
   else
   if (mrpsptr->DirStyle == MAPURL_DIR_STYLE_ORIGINAL)
      strcat (FaoString, " dir=style=original");
   else
   if (mrpsptr->DirStyle == MAPURL_DIR_STYLE_ORIGINAL2)
      strcat (FaoString, " dir=style=original2");
   else
   if (mrpsptr->DirStyle == MAPURL_DIR_STYLE_SORT)
      strcat (FaoString, " dir=style=sort");
   else
   if (mrpsptr->DirStyle == MAPURL_DIR_STYLE_SORT2)
      strcat (FaoString, " dir=style=sort2");

   if (mrpsptr->DirTargetPtr)
   {
      strcat (FaoString, " dir=TARGET=\"!AZ\"");
      *vecptr++ = mrpsptr->DirTargetPtr;
   }

   if (mrpsptr->DirThesePtr)
   {
      strcat (FaoString, " dir=THESE=\"!AZ\"");
      *vecptr++ = mrpsptr->DirThesePtr;
   }

   if (mrpsptr->DirTitle == MAPURL_DIR_TITLE_THIS)
   {
      strcat (FaoString, " dir=TITLE=this=\"!AZ\"");
      *vecptr++ = mrpsptr->DirTitlePtr;
   }
   else
   if (mrpsptr->DirTitle == MAPURL_DIR_TITLE_DEFAULT)
      strcat (FaoString, " dir=TITLE=default");
   else
   if (mrpsptr->DirTitle == MAPURL_DIR_TITLE_OWNER)
      strcat (FaoString, " dir=TITLE=owner");
   else
   if (mrpsptr->DirTitle == MAPURL_DIR_TITLE_REMOTE)
      strcat (FaoString, " dir=TITLE=remote");
   else
   if (mrpsptr->DirTitle == MAPURL_DIR_TITLE_NONE)
      strcat (FaoString, " dir=TITLE=0");
   else
   if (mrpsptr->DirTitle)
   {
      strcat (FaoString, " dir=TITLE=!UL");
      *vecptr++ = mrpsptr->DirTitle;
   }

   if (mrpsptr->DirVersionsOf)
   {
      if (mrpsptr->DirVersionsOf == 65536)
         strcat (FaoString, " dir=VERSIONS=*");
      else
      if (mrpsptr->DirVersionsOf == -1)
         strcat (FaoString, " dir=VERSIONS=0");
      else
      {
         strcat (FaoString, " dir=VERSIONS=!UL");
         *vecptr++ = mrpsptr->DirVersionsOf;
      }
   }

   if (mrpsptr->DirNoWildcard)
      strcat (FaoString, " dir=NOwildcard");
   else
   if (mrpsptr->DirWildcard)
      strcat (FaoString, " dir=wildcard");

   if (mrpsptr->NoExpired)
      strcat (FaoString, " NOexpired");
   else
   if (mrpsptr->Expired)
      strcat (FaoString, " expired");

   if (mrpsptr->Http2ToHttp11)
      strcat (FaoString, " HTTP2=protocol=1.1");

   if (mrpsptr->Http2MaxStreams)
   {
      strcat (FaoString, " HTTP2=streams=max=!UL");
      *vecptr++ = mrpsptr->Http2MaxStreams;
   }

   if (mrpsptr->Http2WriteQueue == HTTP2_WRITE_QUEUE_HIGH)
      strcat (FaoString, " HTTP2=write=high");
   else
   if (mrpsptr->Http2WriteQueue == HTTP2_WRITE_QUEUE_NORMAL)
      strcat (FaoString, " HTTP2=write=normal");
   else
   if (mrpsptr->Http2WriteQueue == HTTP2_WRITE_QUEUE_LOW)
      strcat (FaoString, " HTTP2=write=low");

   if (mrpsptr->Http2SendGoAway)
   {
      strcat (FaoString, " HTTP2=send=goaway=!UL");
      *vecptr++ = mrpsptr->Http2SendGoAway - 1;
   }

   if (mrpsptr->Http2SendPing)
      strcat (FaoString, " HTTP2=send=ping");

   if (mrpsptr->Http2SendReset)
   {
      strcat (FaoString, " HTTP2=send=reset=!UL");
      *vecptr++ = mrpsptr->Http2SendReset - 1;
   }

   if (mrpsptr->HttpAcceptCharsetPtr)
   {
      strcat (FaoString, " http=Accept-Charset=!&\"AZ");
      *vecptr++ = mrpsptr->HttpAcceptCharsetPtr;
   }

   if (mrpsptr->HtmlBodyTagPtr)
   {
      strcat (FaoString, " html=bodytag=!&\"AZ");
      *vecptr++ = mrpsptr->HtmlBodyTagPtr;
   }

   if (mrpsptr->HtmlHeaderTagPtr)
   {
      strcat (FaoString, " html=headertag=!&\"AZ");
      *vecptr++ = mrpsptr->HtmlHeaderTagPtr;
   }

   if (mrpsptr->HtmlHeaderPtr)
   {
      strcat (FaoString, " html=header=!&\"AZ");
      *vecptr++ = mrpsptr->HtmlHeaderPtr;
   }

   if (mrpsptr->HtmlFooterTagPtr)
   {
      strcat (FaoString, " html=footertag=!&\"AZ");
      *vecptr++ = mrpsptr->HtmlFooterTagPtr;
   }

   if (mrpsptr->HtmlFooterPtr)
   {
      strcat (FaoString, " html=footer=!&\"AZ");
      *vecptr++ = mrpsptr->HtmlFooterPtr;
   }

   if (mrpsptr->HttpAcceptLangPtr)
   {
      strcat (FaoString, " http=Accept-Language=!&\"AZ");
      *vecptr++ = mrpsptr->HttpAcceptLangPtr;
   }

   if (mrpsptr->IndexPtr)
   {
      strcat (FaoString, " index=!AZ");
      *vecptr++ = mrpsptr->IndexPtr;
   }

   if (mrpsptr->NoLog)
      strcat (FaoString, " NOlog");
   else
   if (mrpsptr->Log)
      strcat (FaoString, " log");

   if (FaoString[0])
   {
      status = FaolToBuffer (bptr, blen, &slen, FaoString, &FaoVector);
      if (VMSnok (status) || status == SS$_BUFFEROVF)
      {
         ErrorNoticed (rqptr, status, NULL, FI_LI);
         return ("*ERROR*");
      }
      bptr += slen;
      blen -= slen;
      vecptr = FaoVector;
      FaoString[0] = '\0';
   }

   if (mrpsptr->NoMapEllipsis)
      strcat (FaoString, " map=NOELLIPSIS");
   else
   if (mrpsptr->MapEllipsis)
      strcat (FaoString, " map=ELLIPSIS");

   if (mrpsptr->MapNonEmpty)
      strcat (FaoString, " map=NOEMPTY");
   else
   if (mrpsptr->MapEmpty)
      strcat (FaoString, " map=EMPTY");

   if (mrpsptr->NoMapExtensionMethod)
      strcat (FaoString, " map=NOMETHOD");
   else
   if (mrpsptr->MapExtensionMethod)
      strcat (FaoString, " map=METHOD");

   if (mrpsptr->NoMapOnce)
      strcat (FaoString, " map=NOONCE");
   else
   if (mrpsptr->MapOnce)
      strcat (FaoString, " map=ONCE");

   if (mrpsptr->MapRestart)
      strcat (FaoString, " map=RESTART");

   if (mrpsptr->MapSetNoIgnore)
      strcat (FaoString, " map=SET=noignore");
   else
   if (mrpsptr->MapSetIgnore)
      strcat (FaoString, " map=SET=ignore");

   if (mrpsptr->MapSetNoRequest)
      strcat (FaoString, " map=SET=norequest");
   else
   if (mrpsptr->MapSetRequest)
      strcat (FaoString, " map=SET=request");

   if (mrpsptr->MapUri)
      strcat (FaoString, " map=URI");

   if (mrpsptr->MapRootPtr)
   {
      strcat (FaoString, " map=ROOT=!AZ");
      *vecptr++ = mrpsptr->MapRootPtr;
   }

   if (mrpsptr->NotePadPtr)
   {
      strcat (FaoString, " notepad=!&\"AZ");
      *vecptr++ = mrpsptr->NotePadPtr;
   }

   if (mrpsptr->PathOds == MAPURL_PATH_ODS_0)
      strcat (FaoString, " NOods");
   else
   if (mrpsptr->PathOds == MAPURL_PATH_ODS_2)
      strcat (FaoString, " ods=2");
   else
   if (mrpsptr->PathOds == MAPURL_PATH_ODS_5)
      strcat (FaoString, " ods=5");
   else
   if (mrpsptr->PathOds == MAPURL_PATH_ODS_PWK)
      strcat (FaoString, " ods=PWK");
   else
   if (mrpsptr->PathOds == MAPURL_PATH_ODS_SMB)
      strcat (FaoString, " ods=SMB");
   else
   if (mrpsptr->PathOds == MAPURL_PATH_ODS_SRI)
      strcat (FaoString, " ods=SRI");

   if (mrpsptr->OdsName == MAPURL_ODS_8BIT)
      strcat (FaoString, " ods=name=8BIT");
   else
   if (mrpsptr->OdsName == MAPURL_ODS_UTF8)
      strcat (FaoString, " ods=name=UTF8");
   else
   if (mrpsptr->OdsName == MAPURL_ODS_DEFAULT)
      strcat (FaoString, " ods=name=DEFAULT");

   if (mrpsptr->NoProfile)
      strcat (FaoString, " NOprofile");
   else
   if (mrpsptr->Profile)
      strcat (FaoString, " profile");

   if (mrpsptr->ProxyNoAffinity)
      strcat (FaoString, " proxy=NOAFFINITY");
   else
   if (mrpsptr->ProxyAffinity)
      strcat (FaoString, " proxy=AFFINITY");

   if (mrpsptr->ProxyBindIpAddressPtr)
   {
      strcat (FaoString, " proxy=BIND=!AZ");
      *vecptr++ = mrpsptr->ProxyBindIpAddressPtr;
   }

   if (mrpsptr->ProxyChainHostPortPtr)
   {
      strcat (FaoString, " proxy=CHAIN=!AZ");
      *vecptr++ = mrpsptr->ProxyChainHostPortPtr;
   }

   if (mrpsptr->ProxyChainCredPtr)
   {
      strcat (FaoString, " proxy=CHAIN=CRED=!AZ");
      *vecptr++ = mrpsptr->ProxyChainCredPtr;
   }

   if (mrpsptr->ProxyForwardedBy == PROXY_FORWARDED_NONE)
      strcat (FaoString, " proxy=NOforwarded");
   else
   if (mrpsptr->ProxyForwardedBy == PROXY_FORWARDED_BY)
      strcat (FaoString, " proxy=FORWARDED=by");
   else
   if (mrpsptr->ProxyForwardedBy == PROXY_FORWARDED_FOR)
      strcat (FaoString, " proxy=FORWARDED=for");
   else
   if (mrpsptr->ProxyForwardedBy == PROXY_FORWARDED_ADDRESS)
      strcat (FaoString, " proxy=FORWARDED=address");

   if (mrpsptr->ProxyReverseLocationLength)
   {
      strcat (FaoString, " proxy=REVERSE=location=!AZ");
      *vecptr++ = mrpsptr->ProxyReverseLocationPtr;
   }

   if (mrpsptr->ProxyXForwardedFor == PROXY_XFORWARDEDFOR_NONE)
      strcat (FaoString, " proxy=NOxforwardedfor");
   else
   if (mrpsptr->ProxyXForwardedFor == PROXY_XFORWARDEDFOR_ENABLED)
      strcat (FaoString, " proxy=XFORWARDEDFOR=enabled");
   else
   if (mrpsptr->ProxyXForwardedFor == PROXY_XFORWARDEDFOR_ADDRESS)
      strcat (FaoString, " proxy=XFORWARDEDFOR=address");
   else
   if (mrpsptr->ProxyXForwardedFor == PROXY_XFORWARDEDFOR_UNKNOWN)
      strcat (FaoString, " proxy=XFORWARDEDFOR=unknown");

   if (mrpsptr->NoProxyReverseVerify)
      strcat (FaoString, " proxy=REVERSE=NOverify");
   else
   if (mrpsptr->ProxyReverseVerify)
      strcat (FaoString, " proxy=REVERSE=verify");

   if (mrpsptr->NoProxyReverseAuthHeader)
      strcat (FaoString, " proxy=REVERSE=NOauth");
   else
   if (mrpsptr->ProxyReverseAuthHeader)
      strcat (FaoString, " proxy=REVERSE=auth");

   if (mrpsptr->ProxyTunnelRequestPtr)
   {
      strcat (FaoString, " proxy=tunnel=request=!&\\&\"AZ");
      *vecptr++ = mrpsptr->ProxyTunnelRequestPtr;
   }

   if (mrpsptr->NoProxyUnknownRequestFields)
      strcat (FaoString, " proxy=NOUNKNOWN");
   else
   if (mrpsptr->ProxyUnknownRequestFields)
      strcat (FaoString, " proxy=UNKNOWN");

   if (FaoString[0])
   {
      status = FaolToBuffer (bptr, blen, &slen, FaoString, &FaoVector);
      if (VMSnok (status) || status == SS$_BUFFEROVF)
      {
         ErrorNoticed (rqptr, status, NULL, FI_LI);
         return ("*ERROR*");
      }
      bptr += slen;
      blen -= slen;
      vecptr = FaoVector;
      FaoString[0] = '\0';
   }

   if (mrpsptr->ProxyHeaderCount)
   {
      for (int idx = 0; idx < mrpsptr->ProxyHeaderCount; idx++)
      {
         *vecptr++ = mrpsptr->ProxyHeader[idx];
         status = FaolToBuffer (bptr, blen, &slen, " proxy=HEADER=!AZ",
                                &FaoVector);
         if (VMSnok (status) || status == SS$_BUFFEROVF)
         {
            ErrorNoticed (rqptr, status, NULL, FI_LI);
            return ("*ERROR*");
         }
         bptr += slen;
         blen -= slen;
         vecptr = FaoVector;
         FaoString[0] = '\0';
      }
   }

   if (mrpsptr->PutRFM == PUT_RFM_FIX512)
      strcat (FaoString, " put=FIX512");
   else
   if (mrpsptr->PutRFM == PUT_RFM_STM)
      strcat (FaoString, " put=STM");
   else
   if (mrpsptr->PutRFM == PUT_RFM_STMCR)
      strcat (FaoString, " put=STMCR");
   else
   if (mrpsptr->PutRFM == PUT_RFM_STMLF)
      strcat (FaoString, " put=STMLF");
   else
   if (mrpsptr->PutRFM == PUT_RFM_UDF)
      strcat (FaoString, " put=UDF");

   if (mrpsptr->PutMaxKbytes)
   {
      strcat (FaoString, " put=MAX=!UL");
      *vecptr++ = mrpsptr->PutMaxKbytes;
   }

   if (mrpsptr->RegexSyntax)
   {
      strcat (FaoString, " regex=!AZ");
      switch (mrpsptr->RegexSyntax)
      {
         case RE_SYNTAX_AWK :
              *vecptr++ = "AWK";
              break; 
         case RE_SYNTAX_EGREP :
              *vecptr++ = "EGREP";
              break; 
         case RE_SYNTAX_GREP :
              *vecptr++ = "GREP";
              break; 
         case RE_SYNTAX_POSIX_AWK :
              *vecptr++ = "POSIX_AWK";
              break; 
         case RE_SYNTAX_POSIX_BASIC :
              *vecptr++ = "POSIX_BASIC/ED/SED";
              break; 
         case RE_SYNTAX_POSIX_EGREP :
              *vecptr++ = "POSIX_EGREP";
              break; 
         case RE_SYNTAX_POSIX_EXTENDED :
              *vecptr++ = "POSIX_EXTENDED";
              break; 
         case RE_SYNTAX_POSIX_MINIMAL_BASIC :
              *vecptr++ = "POSIX_MINIMAL_BASIC";
              break; 
         case RE_SYNTAX_POSIX_MINIMAL_EXTENDED :
              *vecptr++ = "POSIX_MINIMAL_EXTENDED";
              break; 
         case REGEX_C_FLAGS :
              *vecptr++ = "WASD";
              break; 
         case -1 :
              *vecptr++ = "default";
              break; 
         default :
              vecptr--;  /** jump back one! **/
              *vecptr++ = " regex=!8XL";
              *vecptr = mrpsptr->RegexSyntax;
      }
   }

   if (mrpsptr->ReportType == ERROR_REPORT_BASIC)
      strcat (FaoString, " report=BASIC");
   else
   if (mrpsptr->ReportType == ERROR_REPORT_DETAILED)
      strcat (FaoString, " report=DETAILED");
   else
   if (mrpsptr->ReportType == ERROR_REPORT_TUNNEL)
      strcat (FaoString, " report=TUNNEL");

   if (mrpsptr->Report400as)
   {
      strcat (FaoString, " report=400=!UL");
      *vecptr++ = mrpsptr->Report400as;
   }

   if (mrpsptr->Report403as)
   {
      strcat (FaoString, " report=403=!UL");
      *vecptr++ = mrpsptr->Report403as;
   }

   if (mrpsptr->Report404as)
   {
      strcat (FaoString, " report=404=!UL");
      *vecptr++ = mrpsptr->Report404as;
   }

   if (mrpsptr->Response200is203 == 203)
      strcat (FaoString, " response=200=203");
   else
   if (mrpsptr->Response200is203 == 200)
      strcat (FaoString, " response=203=200");

   if (mrpsptr->ResponseGzip == MAPURL_RESPONSE_GZIP_ALL)
      strcat (FaoString, " response=GZIP=all");
   else
   if (mrpsptr->ResponseGzip == MAPURL_RESPONSE_GZIP_NONE)
      strcat (FaoString, " response=GZIP=none");
   else
   if (mrpsptr->ResponseGzip)
   {
      strcat (FaoString, " response=GZIP=!UL");
      *vecptr++ = mrpsptr->ResponseGzip;
   }

   if (mrpsptr->ResponseNoChunked)
      strcat (FaoString, " response=NOchunked");
   else
   if (mrpsptr->ResponseChunked)
      strcat (FaoString, " response=chunked");

   if (mrpsptr->ResponseCspLength)
   {
      if (mrpsptr->ResponseCspLength < 0)
         strcat (FaoString, " response=NOcsp");
      else
      {
         strcat (FaoString, " response=csp=!&\"AZ");
         *vecptr++ = mrpsptr->ResponseCspPtr;
      }
   }

   if (mrpsptr->ResponseCsproLength)
   {
      if (mrpsptr->ResponseCsproLength < 0)
         strcat (FaoString, " response=NOcspro");
      else
      {
         strcat (FaoString, " response=cspro=!&\"AZ");
         *vecptr++ = mrpsptr->ResponseCsproPtr;
      }
   }

   if (mrpsptr->ResponseHeaderNone)
      strcat (FaoString, " response=header=none");
   else
   if (mrpsptr->ResponseHeaderBegin)
      strcat (FaoString, " response=header=begin");
   else
   if (mrpsptr->ResponseHeaderFull)
      strcat (FaoString, " response=header=full");

   if (mrpsptr->ResponseHeaderNoAdd)
      strcat (FaoString, " response=header=NOadd");

   if (mrpsptr->ResponseHeaderAddLength)
   {
      strcat (FaoString, " response=header=add=!&\"AZ");
      *vecptr++ = mrpsptr->ResponseHeaderAddPtr;
   }

   if (mrpsptr->ResponseHttpNoOriginal)
      strcat (FaoString, " response=HTTP=NOoriginal");
   else
   if (mrpsptr->ResponseHttpOriginal)
      strcat (FaoString, " response=HTTP=original");

   if (mrpsptr->ResponseStrictTransSecPtr)
   {
      strcat (FaoString, " response=STS=\"!AZ\"");
      *vecptr++ = mrpsptr->ResponseStrictTransSecPtr;
   }

   if (mrpsptr->ResponseVarRecord == FILE_VAR_CRLF)
      strcat (FaoString, " response=VAR=CRLF");
   else
   if (mrpsptr->ResponseVarRecord == FILE_VAR_LF)
      strcat (FaoString, " response=VAR=LF");
   else
   if (mrpsptr->ResponseVarRecord == FILE_VAR_NONE)
      strcat (FaoString, " response=VAR=NONE");

   if (FaoString[0])
   {
      status = FaolToBuffer (bptr, blen, &slen, FaoString, &FaoVector);
      if (VMSnok (status) || status == SS$_BUFFEROVF)
      {
         ErrorNoticed (rqptr, status, NULL, FI_LI);
         return ("*ERROR*");
      }
      bptr += slen;
      blen -= slen;
      vecptr = FaoVector;
      FaoString[0] = '\0';
   }

   if (mrpsptr->ScriptBodyNoDecode)
      strcat (FaoString, " script=BODY=NOdecode");
   else
   if (mrpsptr->ScriptBodyDecode)
      strcat (FaoString, " script=BODY=decode");

   if (mrpsptr->ScriptSymbolNoTruncate)
      strcat (FaoString, " script=SYMBOL=NOtruncate");
   else
   if (mrpsptr->ScriptSymbolTruncate)
      strcat (FaoString, " script=SYMBOL=truncate");

   if (mrpsptr->ScriptSyntaxNoUnix)
      strcat (FaoString, " script=SYNTAX=NOunix");
   else
   if (mrpsptr->ScriptSyntaxUnix)
      strcat (FaoString, " script=SYNTAX=unix");

   if (mrpsptr->ScriptNoFind)
      strcat (FaoString, " script=NOfind");
   else
   if (mrpsptr->ScriptFind)
      strcat (FaoString, " script=FIND");

   if (mrpsptr->ScriptNoPathFind)
      strcat (FaoString, " NOscript=PATH=find");
   else
   if (mrpsptr->ScriptPathFind)
      strcat (FaoString, " script=PATH=find");

   if (mrpsptr->ScriptNoQueryNone)
      strcat (FaoString, " NOscript=QUERY=none");
   else
   if (mrpsptr->ScriptQueryNone)
      strcat (FaoString, " script=QUERY=none");

   if (mrpsptr->ScriptNoQueryRelaxed)
      strcat (FaoString, " NOscript=QUERY=relaxed");
   else
   if (mrpsptr->ScriptQueryRelaxed)
      strcat (FaoString, " script=QUERY=relaxed");

   if (mrpsptr->NoDefaultSearch)
      strcat (FaoString, " search=none");
   else
   if (mrpsptr->DefaultSearch)
      strcat (FaoString, " NOsearch=none");

   if (mrpsptr->ChangeServicePtr)
   {
      strcat (FaoString, " service=!AZ");
      *vecptr++ = mrpsptr->ChangeServicePtr;
   }

   if (mrpsptr->NoPrivSsi)
      strcat (FaoString, " NOssi=PRIV");
   else
   if (mrpsptr->PrivSsi)
      strcat (FaoString, " ssi=PRIV");

   if (mrpsptr->SsiExecPtr)
   {
      strcat (FaoString, " ssi=EXEC=!AZ");
      *vecptr++ = mrpsptr->SsiExecPtr;
   }

   if (mrpsptr->StyleSheetPtr)
   {
      strcat (FaoString, " css=!AZ");
      *vecptr++ = mrpsptr->StyleSheetPtr;
   }

   if (mrpsptr->SSLCGIvar == SESOLA_CGI_VAR_APACHE_MOD_SSL)
      strcat (FaoString, " SSLCGI=Apache_mod_SSL");
   else
   if (mrpsptr->SSLCGIvar == SESOLA_CGI_VAR_APACHE_MOD_SSL_CLIENT)
      strcat (FaoString, " SSLCGI=Apache_mod_SSL_client");
   else
   if (mrpsptr->SSLCGIvar == SESOLA_CGI_VAR_APACHE_MOD_SSL_EXTENS)
      strcat (FaoString, " SSLCGI=Apache_mod_SSL_extens");
   else
   if (mrpsptr->SSLCGIvar == SESOLA_CGI_VAR_APACHE_MOD_SSL_OID)
      strcat (FaoString, " SSLCGI=Apache_mod_SSL_OID");
   else
   if (mrpsptr->SSLCGIvar == SESOLA_CGI_VAR_PURVEYOR)
      strcat (FaoString, " SSLCGI=Purveyor");

   if (mrpsptr->NoStmLF)
      strcat (FaoString, " NOstmLF");
   else
   if (mrpsptr->StmLF)
      strcat (FaoString, " stmLF");

   if (FaoString[0])
   {
      status = FaolToBuffer (bptr, blen, &slen, FaoString, &FaoVector);
      if (VMSnok (status) || status == SS$_BUFFEROVF)
      {
         ErrorNoticed (rqptr, status, NULL, FI_LI);
         return ("*ERROR*");
      }
      bptr += slen;
      blen -= slen;
      vecptr = FaoVector;
      FaoString[0] = '\0';
   }

   if (mrpsptr->QueryStringPtr)
   {
      strcat (FaoString, " query-string=!&\"AZ");
      *vecptr++ = mrpsptr->QueryStringPtr;
   }

   if (mrpsptr->RmsSubChar)
   {
      strcat (FaoString, " RMSchar=!&C");
      *vecptr++ = mrpsptr->RmsSubChar;
   }

   if (mrpsptr->ScriptAgentAsPtr)
   {
      strcat (FaoString, " script=AGENT=AS=!AZ");
      *vecptr++ = mrpsptr->ScriptAgentAsPtr;
   }

   if (mrpsptr->ScriptAsPtr)
   {
      strcat (FaoString, " script=AS=!AZ");
      *vecptr++ = mrpsptr->ScriptAsPtr;
   }

   if (mrpsptr->ScriptCommandPtr)
   {
      strcat (FaoString, " script=COMMAND=!&\"AZ");
      *vecptr++ = mrpsptr->ScriptCommandPtr;
   }

   if (mrpsptr->ScriptControlPtr)
   {
      strcat (FaoString, " script=CONTROL=!&\"AZ");
      *vecptr++ = mrpsptr->ScriptControlPtr;
   }

   if (mrpsptr->ScriptDefaultPtr)
   {
      strcat (FaoString, " script=DEFAULT=!AZ");
      *vecptr++ = mrpsptr->ScriptDefaultPtr;
   }

   if (mrpsptr->ScriptBitBucketTimeout)
   {
      strcat (FaoString, " script=BIT-BUCKET=!AZ");
      *vecptr++ = MetaConShowSeconds (rqptr, mrpsptr->ScriptBitBucketTimeout);
   }

   if (mrpsptr->ScriptCpuMax)
   {
      strcat (FaoString, " script=CPU=!UL");
      *vecptr++ = mrpsptr->ScriptCpuMax;
   }

   if (mrpsptr->ScriptLifeTime)
   {
      strcat (FaoString, " script=lifetime=!AZ");
      *vecptr++ = MetaConShowSeconds (rqptr, mrpsptr->ScriptLifeTime);
   }

   if (mrpsptr->ScriptParamsPtr)
   {
      strcat (FaoString, " script=PARAMS=!&\"AZ");
      *vecptr++ = mrpsptr->ScriptParamsPtr;
   }

   if (mrpsptr->TimeoutPersistent > 0 ||
       mrpsptr->TimeoutNoProgress > 0 ||
       mrpsptr->TimeoutOutput > 0)
   {
      strcat (FaoString, " timeout=!AZ(p),!AZ(np),!AZ(o)");
      *vecptr++ = MetaConShowSeconds (rqptr, mrpsptr->TimeoutPersistent);
      *vecptr++ = MetaConShowSeconds (rqptr, mrpsptr->TimeoutNoProgress);
      *vecptr++ = MetaConShowSeconds (rqptr, mrpsptr->TimeoutOutput);
   }
   else
   if (mrpsptr->TimeoutPersistent < 0 &&
       mrpsptr->TimeoutNoProgress < 0 &&
       mrpsptr->TimeoutOutput < 0)
      strcat (FaoString, " notimeout");

   if (mrpsptr->ThrottleNoSet)
      strcat (FaoString, " throttle=NONE");
   else
   if (mrpsptr->ThrottleSet)
   {
      if (mrpsptr->ThrottlePerUser)
      {
         strcat (FaoString, " throttle=!UL$!UL,!UL,!UL,!UL,!AZ,!AZ");
         *vecptr++ = mrpsptr->ThrottleFrom;
         *vecptr++ = mrpsptr->ThrottlePerUser;
      }
      else
      {
         strcat (FaoString, " throttle=!UL,!UL,!UL,!UL,!AZ,!AZ");
         *vecptr++ = mrpsptr->ThrottleFrom;
      }
      *vecptr++ = mrpsptr->ThrottleTo;
      *vecptr++ = mrpsptr->ThrottleResume;
      *vecptr++ = mrpsptr->ThrottleBusy;
      *vecptr++ = MetaConShowSeconds (rqptr, mrpsptr->ThrottleTimeoutQueue);
      *vecptr++ = MetaConShowSeconds (rqptr, mrpsptr->ThrottleTimeoutBusy);
   }

   if (mrpsptr->AccessNoProfile)
      strcat (FaoString, " access=NOprofile");
   else
   if (mrpsptr->AccessProfile)
      strcat (FaoString, " access=profile");

   if (mrpsptr->AccessNoRead)
      strcat (FaoString, " access=NOread");
   else
   if (mrpsptr->AccessRead)
      strcat (FaoString, " access=read");

   if (mrpsptr->AccessNoServer)
      strcat (FaoString, " access=NOserver");
   else
   if (mrpsptr->AccessServer)
      strcat (FaoString, " access=server");

   if (mrpsptr->AccessNoWrite)
      strcat (FaoString, " access=NOwrite");
   else
   if (mrpsptr->AccessWrite)
      strcat (FaoString, " access=write");

   if (FaoString[0])
   {
      status = FaolToBuffer (bptr, blen, &slen, FaoString, &FaoVector);
      if (VMSnok (status) || status == SS$_BUFFEROVF)
      {
         ErrorNoticed (rqptr, status, NULL, FI_LI);
         return ("*ERROR*");
      }
      bptr += slen;
      blen -= slen;
      vecptr = FaoVector;
      FaoString[0] = '\0';
   }

   if (mrpsptr->WebDavNoServer)
      strcat (FaoString, " WebDAV=NOserver");
   else
   if (mrpsptr->WebDavServer)
      strcat (FaoString, " WebDAV=server");

   if (mrpsptr->WebDavNoHidden)
      strcat (FaoString, " WebDAV=NOhidden");
   else
   if (mrpsptr->WebDavHidden)
      strcat (FaoString, " WebDAV=hidden");

   if (mrpsptr->WebDavNoLock)
      strcat (FaoString, " WebDAV=NOlock");
   else
   if (mrpsptr->WebDavLock)
      strcat (FaoString, " WebDAV=lock");

   if (mrpsptr->WebDavMetaDirPtr)
   {
      strcat (FaoString, " webdav=META=dir=!&\"AZ");
      *vecptr++ = mrpsptr->WebDavMetaDirPtr;
   }

   if (mrpsptr->WebDavNoProfile)
      strcat (FaoString, " WebDAV=NOprofile");
   else
   if (mrpsptr->WebDavProfile)
      strcat (FaoString, " WebDAV=profile");

   if (mrpsptr->WebDavNoProp)
      strcat (FaoString, " WebDAV=NOprop");
   else
   if (mrpsptr->WebDavProp)
      strcat (FaoString, " WebDAV=prop");

   if (mrpsptr->WebDavNoPutLock)
      strcat (FaoString, " WebDAV=NOPUT=lock");
   else
   if (mrpsptr->WebDavPutLock)
      strcat (FaoString, " WebDAV=PUT=lock");

   if (mrpsptr->WebDavNoRead)
      strcat (FaoString, " WebDAV=NOread");
   else
   if (mrpsptr->WebDavRead)
      strcat (FaoString, " WebDAV=read");

   if (mrpsptr->WebDavNoWinProp)
      strcat (FaoString, " WebDAV=NOwinprop");
   else
   if (mrpsptr->WebDavWinProp)
      strcat (FaoString, " WebDAV=winprop");

   if (mrpsptr->WebDavNoWrite)
      strcat (FaoString, " WebDAV=NOwrite");
   else
   if (mrpsptr->WebDavWrite)
      strcat (FaoString, " WebDAV=write");

   if (mrpsptr->WebDavLockTimeoutDefault)
   {
      strcat (FaoString, " WebDAV=lock=timeout=default=!AZ");
      *vecptr++ = MetaConShowSeconds (rqptr, mrpsptr->WebDavLockTimeoutDefault);
   }

   if (mrpsptr->WebDavLockTimeoutMax)
   {
      strcat (FaoString, " WebDAV=lock=timeout=max=!AZ");
      *vecptr++ = MetaConShowSeconds (rqptr, mrpsptr->WebDavLockTimeoutMax);
   }

   if (mrpsptr->WebSocketInputSize)
   {
      strcat (FaoString, " WebSocket=input=!UL");
      *vecptr++ = mrpsptr->WebSocketInputSize;
   }

   if (mrpsptr->WebSocketOutputSize)
   {
      strcat (FaoString, " WebSocket=output=!UL");
      *vecptr++ = mrpsptr->WebSocketOutputSize;
   }

   if (FaoString[0])
   {
      status = FaolToBuffer (bptr, blen, &slen, FaoString, &FaoVector);
      if (VMSnok (status) || status == SS$_BUFFEROVF)
      {
         ErrorNoticed (rqptr, status, NULL, FI_LI);
         return ("*ERROR*");
      }
   }

   return (Buffer);
}

/*****************************************************************************/
/*
Supplied with an integer in 'ThrottleIndex' representing the element number of
the 'ThrottleArray' path, scan through all the paths in the rule list
noting all SET THROTTLE= paths until the count equals that of the integer
supplied.  This is the rule representing that index.  Return a pointer to the
rule, or NULL.
*/ 
 
MAP_RULE_META* MapUrl_ThrottleRule (int ThrottleIndex)

{
   int  RuleCount;
   MAP_RULE_META  *mrptr;
   MAP_SET_META  *mrpsptr;
   METACON_LINE  *mclptr;

   /*********/
   /* begin */
   /*********/

   if (WATCH_MODULE(WATCH_MOD_MAPURL))
      WatchThis (WATCHALL, WATCH_MOD_MAPURL,
                 "MapUrl_ThrottleRule() !UL", ThrottleIndex);

   RuleCount = 0;
   MetaConParseReset (MetaGlobalMappingPtr, true);
   while (mclptr = MetaConParseRaw (MetaGlobalMappingPtr))
   {
      if (!(mrptr = mclptr->LineDataPtr)) continue;
      mrpsptr = &mrptr->mpPathSet;
      if (!mrpsptr->ThrottleSet) continue;
      if (ThrottleIndex != RuleCount++) continue;
      return (mrptr);
   }
   return (NULL);
}

/*****************************************************************************/
/*
Reload the mapping rules.
*/

MapUrl_ControlReload ()

{
   /*********/
   /* begin */
   /*********/

   if ((WATCH_MODULE(WATCH_MOD_MAPURL)))
      WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapUrl_ControlReload()");

   MetaConUnload (&MetaGlobalMappingPtr, &MapUrl_ConfigUnloadLineData);

   MapUrl_ConfigLoad (&MetaGlobalMappingPtr);

   /* purge DCL module script task list and script name cache */
   DclLoadedMappingRules();
}

/*****************************************************************************/

