trans-coord-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

trans-coord/gnun/server/gnun configure.ac Makef...


From: Pavel Kharitonov
Subject: trans-coord/gnun/server/gnun configure.ac Makef...
Date: Fri, 13 Mar 2015 18:02:49 +0000

CVSROOT:        /sources/trans-coord
Module name:    trans-coord
Changes by:     Pavel Kharitonov <ineiev>       15/03/13 18:02:49

Modified files:
        gnun/server/gnun: configure.ac Makefile.am NEWS 
Added files:
        gnun/server/gnun: expand-ssi.awk.in 
        gnun/server/gnun/tests/validate: 5 5.0.html 5.1.html match3 
                                         match3.1.html match3.0.html 
Removed files:
        gnun/server/gnun: expand-ssi.awk 

Log message:
        Fix some SSI expansion bugs; support regexp groups
        as Apache variables.
        
        * expand-ssi.awk: Replace with `expand-ssi.awk.in'.
        * expand-ssi.awk.in:
        * tests/validate/5:
        * tests/validate/5.0.html:
        * tests/validate/5.1.html:
        * tests/validate/match3:
        * tests/validate/match3.0.html:
        * tests/validate/match3.1.html: New files.
        * configure.ac: Check if awk supports third argument
        in match(); make `expand-ssi.awk' of `expand-ssi.awk.in'.
        Remove conftest files.  Mention installcheck in the final
        message.
        * expand-ssi.awk.in: Support regexp groups as Apache
        variables (when awk is gawk); don't unquote double
        quotes in #if directives; don't unquote regular expressions
        in directives like <!--#if expr="a = /'a'/" -->.
        * Makefile.am: Add new tests; replace `expand-ssi.awk'
        with `expand-ssi.awk.in' and its derivation.
        * NEWS: Update.

CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/trans-coord/gnun/server/gnun/configure.ac?cvsroot=trans-coord&r1=1.44&r2=1.45
http://cvs.savannah.gnu.org/viewcvs/trans-coord/gnun/server/gnun/Makefile.am?cvsroot=trans-coord&r1=1.40&r2=1.41
http://cvs.savannah.gnu.org/viewcvs/trans-coord/gnun/server/gnun/NEWS?cvsroot=trans-coord&r1=1.112&r2=1.113
http://cvs.savannah.gnu.org/viewcvs/trans-coord/gnun/server/gnun/expand-ssi.awk.in?cvsroot=trans-coord&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/trans-coord/gnun/server/gnun/expand-ssi.awk?cvsroot=trans-coord&r1=1.6&r2=0
http://cvs.savannah.gnu.org/viewcvs/trans-coord/gnun/server/gnun/tests/validate/5?cvsroot=trans-coord&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/trans-coord/gnun/server/gnun/tests/validate/5.0.html?cvsroot=trans-coord&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/trans-coord/gnun/server/gnun/tests/validate/5.1.html?cvsroot=trans-coord&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/trans-coord/gnun/server/gnun/tests/validate/match3?cvsroot=trans-coord&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/trans-coord/gnun/server/gnun/tests/validate/match3.1.html?cvsroot=trans-coord&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/trans-coord/gnun/server/gnun/tests/validate/match3.0.html?cvsroot=trans-coord&rev=1.1

Patches:
Index: configure.ac
===================================================================
RCS file: /sources/trans-coord/trans-coord/gnun/server/gnun/configure.ac,v
retrieving revision 1.44
retrieving revision 1.45
diff -u -b -r1.44 -r1.45
--- configure.ac        22 May 2014 11:17:06 -0000      1.44
+++ configure.ac        13 Mar 2015 18:02:47 -0000      1.45
@@ -1,7 +1,7 @@
 # Process this file with autoconf to produce a configure script.
 
 # Copyright (C) 2008, 2009, 2010, 2011, 2012,
-#   2013, 2014 Free Software Foundation, Inc.
+#   2013, 2014, 2015 Free Software Foundation, Inc.
 
 # This file is part of GNUnited Nations.
 
@@ -69,6 +69,17 @@
 
 AC_ARG_VAR([AWK], [the `awk' program])
 AX_CHECK_AWK_GSUB([decent_awk=yes], [decent_awk=no])
+AX_TRY_AWK_EXPOUT([match(a,b,c)],[],
+[
+  if(!match("abcdefg", /b((c)d)e(f)/, arr))
+    print "no match"
+  if (arr[1] != "cd" || arr[2] != "c" || arr[3] != "f"\
+      || arr[0] != "bcdef" || 4 in arr)
+    print "wrong grouping"
+  print "OK"
+], [OK], [match3=match], [match3=match_fallback])
+AC_SUBST([MATCH3], [$match3])
+AM_CONDITIONAL([HAVE_MATCH3], [test $match3 = match])
 
 AC_ARG_VAR([SED], [the `sed' program])
 AC_PROG_SED
@@ -89,6 +100,7 @@
 Unlike GNU sed, the `sed' program doesn't support --in-place option.
 ])
 ])
+rm -f conftest0.sed confest.sed
 
 AC_MSG_CHECKING([whether sed supports the `e' option of the `s' command])
 test="$(echo false | ${SED} 's/.*/true/e')"
@@ -262,7 +274,7 @@
 [have_xmllint=yes
 AC_MSG_CHECKING([if XHTML DTDs are installed])
 have_dtds=no
-cat > conftest.dtd <<'EOF'
+cat > conftest.html <<'EOF'
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
 <html xmlns="http://www.w3.org/1999/xhtml"; xml:lang="en" lang="en">
@@ -274,14 +286,15 @@
 </body>
 </html>
 EOF
-if $XMLLINT --valid --nonet --noout conftest.dtd 2> /dev/null ; then
+if $XMLLINT --valid --nonet --noout conftest.html 2> /dev/null ; then
   have_dtds=yes;
 fi
-  if test "$have_dtds" = no; then
+rm -f conftest.html
+if test "$have_dtds" = no; then
     AC_MSG_RESULT([$have_dtds (will use our copy)])
-  else
+else
     AC_MSG_RESULT([$have_dtds])
-  fi
+fi
 ])
 
 # Our validation scripts depend on awk and xmllint.
@@ -298,6 +311,8 @@
       ])
 AS_CASE(["$decent_gettext-$gnun_cv_recent_po4a"], [yes-yes],
         [AC_CONFIG_FILES([gnun-preconvert])])
+AS_CASE([$have_validation], [yes],
+  [AC_CONFIG_FILES([expand-ssi.awk])])
 
 AC_OUTPUT
 
@@ -314,5 +329,6 @@
 echo "VCS support...                          $have_vcs $cvs $svn $bzr"
 echo "Use Multiviews naming conventions...    $enable_multiviews"
 echo
-echo "Type \"make\" to build the scripts and \"make install\" to install
-them along with the manuals."
+echo \
+"Type \"make\" to build the scripts, \"make install\" to install them
+along with the manuals, and \"make installcheck\" to run tests."

Index: Makefile.am
===================================================================
RCS file: /sources/trans-coord/trans-coord/gnun/server/gnun/Makefile.am,v
retrieving revision 1.40
retrieving revision 1.41
diff -u -b -r1.40 -r1.41
--- Makefile.am 28 Jun 2014 05:57:15 -0000      1.40
+++ Makefile.am 13 Mar 2015 18:02:47 -0000      1.41
@@ -41,12 +41,23 @@
 endif
 endif
 
-validation_tests = validate/1 validate/2 validate/3 validate/4
+validation_tests = validate/1 validate/2 validate/3 validate/4 validate/5
 tests_available += $(validation_tests)
 
+if HAVE_MATCH3
+validation_tests += validate/match3
+else
+tests_available += validate/match3
+endif
+
+noinst_SCRIPTS = stamp-config.mk
+pkglibexec_SCRIPTS = copy-msgid mailfail update-localized-urls \
+ validate-html-notify
+
 if HAVE_VALIDATION
 bin_SCRIPTS += gnun-validate-html
 tests_enabled += $(validation_tests)
+pkglibexec_SCRIPTS += expand-ssi.awk
 endif
 
 tests_available += clear-previous
@@ -60,10 +71,6 @@
 tests_enabled += copy-msgid
 endif
 
-noinst_SCRIPTS = stamp-config.mk
-pkglibexec_SCRIPTS = copy-msgid mailfail update-localized-urls \
- validate-html-notify
-
 tests_available += diff-po add-fuzzy-diff
 
 if HAVE_WDIFF
@@ -79,7 +86,7 @@
 
 tests_available += make-prototype
 tests_enabled += make-prototype
-dist_pkglibexec_SCRIPTS = expand-ssi.awk make-prototype.awk
+dist_pkglibexec_SCRIPTS = make-prototype.awk
 
 installcheck-local:
        @failed=0; pass=0; skip=0; \
@@ -169,7 +176,7 @@
 dist_pkgdata_DATA = GNUmakefile
 
 EXTRA_DIST = add-fuzzy-diff.in copy-msgid.in diff-po.awk.in \
-            gnun-add-fuzzy-diff.in gnun-diff-po.in \
+            expand-ssi.awk.in gnun-add-fuzzy-diff.in gnun-diff-po.in \
             gnun-init-po.in gnun-merge-preconverted.in \
             gnun-report.in gnun-validate-html.in \
             mailfail.in update-localized-urls.in \
@@ -188,7 +195,10 @@
             tests/validate/1 tests/validate/1.html \
             tests/validate/2 tests/validate/html5.html \
             tests/validate/3 tests/validate/3.0.html tests/validate/3.1.html \
-            tests/validate/4 tests/validate/4.0.html tests/validate/4.1.html
+            tests/validate/4 tests/validate/4.0.html tests/validate/4.1.html \
+            tests/validate/5 tests/validate/5.0.html tests/validate/5.1.html \
+            tests/validate/match3 tests/validate/match3.0.html \
+              tests/validate/match3.1.html
 
 CLEANFILES = add-fuzzy-diff copy-msgid diff-po.awk gnun-add-fuzzy-diff \
             gnun-diff-po gnun-init-po gnun-merge-preconverted gnun-report \

Index: NEWS
===================================================================
RCS file: /sources/trans-coord/trans-coord/gnun/server/gnun/NEWS,v
retrieving revision 1.112
retrieving revision 1.113
diff -u -b -r1.112 -r1.113
--- NEWS        5 Nov 2014 14:52:22 -0000       1.112
+++ NEWS        13 Mar 2015 18:02:47 -0000      1.113
@@ -2,12 +2,23 @@
 
 * Changes in GNUnited Nations 0.10 (????-??-??)
 
+** `gnun-validate-html' supports groups of regular expressions
+   as Apache variables when expanding SSI (when gawk is used).
+
+** GNUmakefile.team is made more suitable for parallel builds;
+
 ** In GNUmakefile.team, the `update' was split into
    `update-www' and `update-team'; the repositories are updated
    unconditionally (previously, they were only updated when VCS=yes).
 
 ** Bugs fixed in 0.10.
 
+*** In SSI expansion, `gnun-validate-html' wrongly assumed that double
+    quotes were removed when evaluating #if directives.
+
+*** In SSI expansion, `gnun-validate-html' wrongly unquoted regular
+    expressions in directives like <!--#if expr="a = /'a'/" -->
+
 *** `gnun-add-fuzzy-diff' didn't work correctly when no input file
     was given.
 

Index: expand-ssi.awk.in
===================================================================
RCS file: expand-ssi.awk.in
diff -N expand-ssi.awk.in
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ expand-ssi.awk.in   13 Mar 2015 18:02:46 -0000      1.1
@@ -0,0 +1,547 @@
+# expand-ssi.awk: process Apache SSI directives.
+
+# Copyright (C) 2015 Free Software Foundation, Inc.
+
+# This file is part of GNUnited Nations.
+
+# GNUnited Nations is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# GNUnited Nations is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNUnited Nations.  If not, see <http://www.gnu.org/licenses/>.
+
+# The implementation is very limited,
+# e.g. `&&' and `||' in `if' expressions are not supported, as well as
+# many cases of quoting; CGI includes would be expanded in a wrong way.
+
+# Fallback for awks who don't support RT.
+BEGIN { RT = "\n" }
+
+# Read whole file to `text'.
+{ text = text $0 RT }
+
+# Look for the variable first in the list of the named ones,
+# then in the numbered parts of the latest regex.
+function get_apache_var(var)
+{
+  if(var in apache_variables)
+    return apache_variables[var]
+  if(var in matched_array && var ~ /^[0-9]$/)
+    return matched_array[var]
+  return val = ""
+}
+
+# Substitute the variables in `str' with their values;
+# return the result.
+function expand_var_value(str,
+  vars, n, i, begin, end, rest, var, val)
+{
+  n = split(str, vars, /\$/)
+  if(!n)
+    return ""
+  str = vars[1];
+  for(i = 2; i <= n; i++)
+    {
+      if(substr(vars[i], 1, 1) == "{")
+        {
+          begin = 2; end = index(vars[i], "}"); rest = end + 1
+        }
+      else
+        {
+          begin = 1; end = match(vars[i] " ", /[^0-9a-zA-Z_]/); rest = end
+        }
+      if(end <= begin)
+        {
+          str = str "$" vars[i]; continue
+        }
+      var = substr(vars[i],begin,end - begin)
+      val = get_apache_var(var)
+      str = str val (rest <= length(vars[i])? substr(vars[i], rest): "")
+    }
+  return str
+}
+
+# Remove the first and the last character.
+function unquote(str)
+{
+  return substr(str, 2, length(str) - 2)
+}
+
+# Process `<!--#set ... -->' directive.
+function assign_var(var, val,
+  i, q, n, expanded_val)
+{
+  # Extract variable name and value.
+  var = unquote(var)
+  q = substr(val, 1, 1)
+  val = unquote(val)
+  if(q == "\"")
+    gsub(/\\"/, q, val)
+  if(q == "'")
+    gsub(/\\'/, q, val)
+  # Handle escaped `$'s when expanding val.
+  # Note: the unescaping is done in a different way when
+  # expanding `if' expressions in eval_expression().
+  n = split(val, arr, /\\\$/)
+  if(!n)
+    expanded_val = ""
+  else
+    {
+      expanded_val = expand_var_value(arr[1])
+      for(i = 2; i <= n; i++)
+        expanded_val = expanded_val "$" expand_var_value(arr[i])
+    }
+  apache_variables[var] = expanded_val
+}
+
+# URL encoding.
+function encode_url(str)
+{
+  gsub(/%/, "%25", str); # This substitution must be applied first.
+  gsub(/\t/, "%09", str); gsub(/\n/, "%0a", str);  gsub(/\r/, "%0d", str)
+  gsub(/ /, "%20", str);  gsub(/"/, "%22", str);   gsub(/#/, "%23", str)
+  gsub(/;/, "%3b", str);  gsub(/</, "%3c", str);   gsub(/>/, "%3e", str)
+  gsub(/\?/, "%3f", str); gsub(/\[/, "%5b", str);  gsub(/\]/, "%5d", str)
+  gsub(/\^/, "%5e", str); gsub(/`/, "%60", str);
+  gsub(/\{/, "%7b", str);  gsub(/[|]/, "%7c", str); gsub(/}/, "%7d", str)
+  return str
+}
+
+# Entity encoding.
+function encode_entity(str)
+{
+  gsub(/&/, "\\&amp;", str); gsub(/"/, "\\&quot;", str)
+  gsub(/</, "\\&lt;", str);  gsub(/>/, "\\&gt;", str)
+  return str;
+}
+
+# Process `<!--#echo ... -->' directive.
+function echo_var(str, arr,
+  var, val, enc)
+{
+  if(length(arr[2]) > 2)
+    {
+      var = unquote(arr[2])
+      val = get_apache_var(var)
+      if(1 in arr)
+        {
+          enc = arr[1]
+          if(enc == "entity")
+            val = encode_entity(val)
+          else if(enc == "url")
+            val = encode_url(val)
+          else if(enc != "none")
+            {
+              print my_name "unknown encoding `" enc \
+                    "' in echo directive" > "/dev/stderr"
+              exit 1
+            }
+        }
+      else
+        val = encode_entity(val)
+      printf("%s", val)
+    }
+  else
+    {
+      val = substr(str,  1, match(str, /[ \t\r\n]-->/))
+      print my_name "couldn't process echo directive `" val "'" > "/dev/stderr"
+      exit 1
+    }
+}
+
+# Remove leading and trailing spaces, unquote, expand variables.
+function unquoted_var(str,
+  val)
+{
+  val = str
+  sub(/^[ \t]*/, "", val)
+  sub(/[ \t]*$/, "", val)
+  if(val ~ /^'.*'$/)
+    val = unquote(val)
+  return expand_var_value(val)
+}
+
+# Substituted in MATCH3 when match doesn't support third argument.
+function match_fallback(arg1, arg2, arg3)
+{
+  return match(arg1, arg2)
+}
+
+# Evaluate condition from `<!--#if ... -->' directive.
+# Supported forms are "string", "!string", "string1 = string2",
+# "string1 == string2", "string1 != string2".
+# Other forms ("-A string", "string1 > string2", "expr1 && expr2" etc.)
+# are not supported.
+# Advanced quoting patterns like  "' '${test}' ' = '   '"
+# are also not supported (use "' ${test} ' = '   '").
+function eval_expression(str,
+  arr, lval, rval, negate, pattern_match, res, idx)
+{
+  # Escaped `$'s are treated the same way as unescaped in `if' expressions.
+  gsub(/\\\$/, "$", str)
+  idx = index(str, "=")
+  if(!idx)
+    {
+      # No `=': we've got either "!string" or "string" kind of expression.
+      if(str ~ /^[ \t]*!/)
+        return !length(unquoted_var(substr(str, index(str, "!") + 1)))
+      return length(unquoted_var(str))
+    }
+  if(idx > 1)
+    arr[1] = substr(str, 1, idx - 1)
+  else
+    arr[1] = ""
+  arr[2] = substr(str, idx + 1)
+  if(str ~ /!==/)
+    {
+      print my_name "bad expression `" str "'" > "/dev/stderr"
+      exit 1
+    }
+  # "string1 == string2" or "string1 != string2" or "string1 = string2" case.
+  pattern_match = 0
+  negate = 0
+  if(arr[2] ~ /^=/)
+    {
+      arr[2] = substr(arr[2], 2)
+      negate = -1
+    }
+  sub(/^[ \t]*/, "", arr[2])
+  sub(/[ \t]*$/, "", arr[2])
+  if(arr[2] ~ /^\/.*\/$/)
+    {
+      pattern_match = 1
+      arr[2] = unquote(arr[2])
+    }
+  if(!negate && (arr[1] ~ /!$/))
+    {
+      negate = 1
+      sub(/.$/, "", arr[1])
+    }
+  lval = unquoted_var(arr[1])
+  rval = arr[2]
+  if(rval ~ /^'.*'$/ && !pattern_match)
+    rval = unquote(rval)
+  rval = expand_var_value(rval)
+  if(pattern_match)
+    {
+      # Note: Apache manual says you can only capture parts of regexp
+      # with positive matching, but in fact the same is true for negative one.
+      res = @MATCH3@(lval, rval, matched_array)
+    }
+  else
+    res = (lval == rval)
+  if(negate > 0)
+    return !res
+  return res
+}
+
+# Find the end of the directive and check that
+# no comment begins before the directive ends.
+function directive_idx(str,
+  i, idx1)
+{
+  i = match(str, /[ \t\r\n]-->/)
+  if(i == 0)
+    {
+      print my_name "broken chunk `<!--#" str "': directive has no end" \
+            > "/dev/stderr"
+      exit 1
+    }
+  idx1 = index(str, "<!--")
+  if(idx1 && i >= idx1)
+    {
+      print my_name "broken chunk `<!--#" str "' (first ` -->' at " i \
+            " comes after next `<!--' at " idx1 ")" > "/dev/stderr"
+      exit 1
+    }
+  return i + 4
+}
+
+function process_endif()
+{
+  if(--depth < 0)
+    {
+      print my_name "unexpected endif directive in `" \
+            chunks[ch_i] "'" > "/dev/stderr"
+      exit 1
+    }
+  printf("%s", substr(chunks[ch_i], idx) "")
+}
+
+# Main program.
+END {
+  # Don't bother about empty files.
+  if(!length(text))
+    exit 0
+# Assign initial values.
+  # Assign prefix for error messages.
+  my_name = "expand-ssi.awk: "
+  # Default root directory is "../.."
+  if(root == "")
+    root = "../.."
+
+  # The passed_vars variable are expected to come from the command line.
+  ch_n = split(passed_vars, split_vars, /;/)
+  for(ch_i = 1; ch_i <= ch_n; ch_i++)
+    {
+      m = index(split_vars[ch_i], "=")
+      value = ""
+      if(m)
+        {
+          var_name = substr(split_vars[ch_i], 1, m - 1)
+          if(m + 1 <= length(split_vars[ch_i]))
+            value = substr(split_vars[ch_i], m + 1)
+        }
+      else
+        var_name = split_vars[ch_i]
+      if(length(var_name))
+        apache_variables[var_name] = value
+    }
+
+  # Relative directory name for the processed file
+  # (FILENAME must be relative).
+  relative_dir_name = FILENAME
+  # Assume that stdin comes from the current directory,
+  # although it is not very probable.
+  if (relative_dir_name == "-")
+    relative_dir_name = "./foo.html"
+  if (relative_dir_name ~ /\//)
+    sub (/[^\/]*$/, "", relative_dir_name)
+  else
+    relative_dir_name = root
+  sub (/\/*$/, "/", relative_dir_name)
+
+# Split the text by Apache directives.
+  ch_n = split(text, chunks, "<!--#");
+  depth = 0 # The current depth of `if' nesting.
+# Process directives.
+  if(ch_n > 0)
+    printf("%s", chunks[1])
+  for(ch_i = 2; ch_i <= ch_n; ch_i++)
+    {
+      idx = directive_idx(chunks[ch_i])
+# `Set' directive: assign a variable.
+      directive = substr(chunks[ch_i], 1, idx - 1)
+      if(directive \
+~ /^set[ \t\r\n]+var=('.+'|".+")[ \t\r\n]+value=('.*'|".*")[ \t\r\n]+-->/)
+        {
+          pattern[1] = directive
+          sub(/^set[ \t\r\n]+var=/, "", pattern[1])
+          sub(/[ \t\r\n]+value=('.*'|".*")[ \t\r\n]+-->.*/,
+              "", pattern[1])
+          pattern[2] = directive
+          sub(/^set[ \t\r\n]+var=('.+'|".+")[ \t\r\n]+value=/, \
+              "", pattern[2])
+          sub(/[ \t\r\n]+-->.*/, "", pattern[2])
+          assign_var(pattern[1], pattern[2])
+          if(idx <= length(chunks[ch_i]))
+            printf("%s", substr(chunks[ch_i], idx) "")
+          continue
+        }
+# `Set' directive (different order of attributes).
+      if(directive \
+~ /^set[ \t\r\n]+value=('.*'|".*")[ \t\r\n]+var=('.+'|".+")[ \t\r\n]+-->/)
+        {
+          pattern[1] = directive
+          sub(/^set[ \t\r\n]+value=/, "", pattern[1])
+          sub(/[ \t\r\n]+var=('.*'|".*")[ \t\r\n]+-->.*/,
+              "", pattern[1])
+          pattern[2] = directive
+          sub(/^set[ \t\r\n]+value=('.+'|".+")[ \t\r\n]+var=/, \
+              "", pattern[2])
+          sub(/[ \t\r\n]+-->.*/, "", pattern[2])
+          assign_var(pattern[2], pattern[1])
+          if(idx <= length(chunks[ch_i]))
+            printf("%s", substr(chunks[ch_i], idx) "")
+          continue
+        }
+# `Echo' directive: output a variable.
+      if(chunks[ch_i] \
+~ /^echo[ \t\r\n]+(encoding=("[^\"]*"|'[^']*')[ 
\t\r\n]+)?var=('[^']+'|"[^\"]+")[ \t\r\n]+-->/)
+        {
+          if(chunks[ch_i] \
+~ /^echo[ \t\r\n]+(encoding=("[^\"]*"|'[^']*')[ \t\r\n]+)/)
+            {
+              pattern[1] = chunks[ch_i]
+              sub(/^echo[ \t\r\n]+encoding=/, "", pattern[1])
+              idx1 = index(substr(pattern[1], 2), substr(pattern[1], 1, 1))
+              pattern[1] = substr(pattern[1], 2, idx1 - 1)
+            }
+          else
+            delete pattern[1]
+          pattern[2] = chunks[ch_i]
+          sub(\
+/^echo[ \t\r\n]+(encoding=("[^\"]*"|'[^']*')[ \t\r\n]+)?var=/, \
+"", pattern[2])
+          idx1 = index(substr(pattern[2], 2), substr(pattern[2], 1, 1))
+          pattern[2] = substr(pattern[2], 1, idx1 + 1)
+          echo_var(chunks[ch_i], pattern)
+          if(idx <= length(chunks[ch_i]))
+            printf("%s", substr(chunks[ch_i], idx) "")
+          continue
+        }
+# `Include' directive: expand the file and rearrange chunks.
+      if(chunks[ch_i] ~ \
+/^include[ \t\r\n]+(file|virtual)=("[^"]*"|'[^']*')[ \t\r\n]+-->/)
+        {
+          pattern[2] = chunks[ch_i]
+          sub(/^include[ \t\r\n]+(file|virtual)=/, "", pattern[2])
+          pattern[1] = substr(pattern[2], 1, 1)
+          idx1 = index(substr(pattern[2], 2), pattern[1])
+          if(idx1 > 1)
+            name = substr(pattern[2], 2, idx1 - 1)
+          else
+            name = ""
+          # Construct the real path to the file.
+          # Note: relative paths actually won't work in nested includes;
+          # we could track the changes of the path through assigning a path
+          # to every chunk, but it doesn't seem to have much sense, since
+          # it is documented that we should only use absolute paths.
+          if(name ~ /^\//)
+            name = root name
+          else
+            name = relative_dir_name name
+          # Load the file.
+          text = ""
+          while(1)
+            {
+              m = getline < name
+              if(m < 0)
+                {
+                  print my_name "can't read file `" name "': " ERRNO \
+                        > "/dev/stderr"
+                  exit 1
+                }
+              if(!m)
+                break
+              text = text $0 RT
+            }
+          close(name)
+          if(!length(text))
+            {
+              printf("%s", substr(chunks[ch_i], idx) "")
+              continue
+            }
+          m = split(text, next_chunks, "<!--#")
+          # Append the tail of current chunk to the last chunk
+          # of the included file.
+          next_chunks[m] = next_chunks[m] substr(chunks[ch_i], idx)
+          # Move the remaining chunks and put the included file
+          # in freed space.
+          if(m > 1)
+            {
+              for(j = ch_n; j > ch_i; j--)
+                chunks[j + m - 1] = chunks[j]
+              for(j = 2; j <= m ; j++)
+                chunks[j + ch_i - 1] = next_chunks[j]
+              ch_n += m - 1
+            }
+          printf("%s", next_chunks[1] "")
+          continue
+        } # if(match(chunks[ch_i], ...
+# `If' directive: skip all branches with false conditions;
+# output the branch with true condition.
+      if(chunks[ch_i] ~ /^if[ \t\r\n]+expr=('.*'|".*")[ \t\r\n]+-->/)
+        {
+          depth++
+          finish = 0
+          while(ch_i <= ch_n && !finish)
+            {
+              exp_val = 1
+              # `Else' branches are output unconditionally
+              # (no matching `if'/`elif' found).
+              if(chunks[ch_i] !~ /^else[ \t\r\n]+-->/)
+                {
+                  exp_l = index(chunks[ch_i], "expr=") + length("expr=")
+                  qu = substr(chunks[ch_i], exp_l, 1)
+                  expr = substr(chunks[ch_i], exp_l + 1)
+                  expression = substr(expr, 1, match(expr, "[^\\\\]" qu))
+                  exp_val = eval_expression(expression)
+                }
+              if(exp_val)
+                {
+                  if(idx <= length(chunks[ch_i]))
+                    printf("%s", substr(chunks[ch_i], idx) "")
+                  break
+                }
+              # Skip to next `elif', `else' or `endif'
+              d = 0;
+              while (++ch_i <= ch_n)
+                {
+                  idx = directive_idx(chunks[ch_i])
+                  # Next if: increase depth.
+                  if(chunks[ch_i] \
+                     ~ /^if[ \t\r\n]+expr=('.*'|".*")[ \t\r\n]+-->/)
+                    {
+                      d++; continue
+                    }
+                  # `Endif': break if we are at the initial depth.
+                  if(chunks[ch_i] ~ /^endif[ \t\r\n]+-->/)
+                    {
+                      if(d-- > 0)
+                        continue
+                      finish = !0; process_endif(); break
+                    }
+                  # `Else' or `elif': break if we are at the initial depth.
+                  if(d == 0 && (chunks[ch_i] ~ /^else[ \t\r\n]+-->/ \
+                     || chunks[ch_i] \
+                          ~ /^elif[ \t\r\n]+expr=('.*'|".*")[ \t\r\n]+-->/))
+                    break
+                } # while (++ch_i <= ch_n)
+            } # while(ch_i <= ch_n && !finish)
+          continue
+        } # if(chunks[ch_i] ~ /^if[ \t\r\n]+ ...
+# `Elif' or `else' directive: skip up to endif (since the branch with
+# matching expression was output when processing the `if' directive).
+      if((chunks[ch_i] ~ /^else[ \t\r\n]+-->/) \
+          || (chunks[ch_i] ~ /^elif[ \t\r\n]+expr=('.*'|".*")[ \t\r\n]+-->/))
+        {
+          d = 0; chunk_i = ch_i
+          while (++ch_i <= ch_n)
+            {
+              idx = directive_idx(chunks[ch_i])
+              if(chunks[ch_i] ~ /^if[ \t\r\n]+expr=('.*'|".*")[ \t\r\n]+-->/)
+                d++
+              if(chunks[ch_i] ~ /^endif[ \t\r\n]+-->/)
+                if(d-- == 0)
+                  {
+                    process_endif(); break
+                  }
+            }
+          if(chunks[ch_i] !~ /^endif[ \t\r\n]+-->/)
+            {
+              print my_name "couldn't find matching endif for `<!--#" \
+                chunks[chunk_i] "'" > "/dev/stderr"
+              exit 1
+            }
+          continue
+        } # if((chunks[ch_i] ~ /^else[ \t\r\n]+-->/...
+# `Endif' directive: decrease depth and output the rest of the chunk.
+      if(chunks[ch_i] ~ /^endif[ \t\r\n]+-->/)
+        {
+          process_endif(); continue
+        }
+# A directive that must be parsed, but didn't match against its pattern.
+      if(chunks[ch_i] ~ /^(if|elif|else|endif|include|set|echo)\>/)
+        {
+          print my_name "couldn't parse directive `<!--#" \
+            substr(chunks[ch_i], 1, idx - 1) "'" > "/dev/stderr"
+          exit 1
+        }
+      # Unknown directive: hopefully it won't influence the validity.
+      printf("<!--#%s", chunks[ch_i] "")
+    } # for(ch_i = 2; ch_i <= ch_n; ch_i++)
+  if(depth > 0)
+    {
+      print my_name "some if directives are matched with no endif." \
+            > "/dev/stderr"
+      exit 1
+    }
+}

Index: tests/validate/5
===================================================================
RCS file: tests/validate/5
diff -N tests/validate/5
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/validate/5    13 Mar 2015 18:02:48 -0000      1.1
@@ -0,0 +1,24 @@
+#! /bin/sh
+# Copyright (C) 2015 Free Software Foundation, Inc.
+
+# This file is part of GNUnited Nations.
+
+# GNUnited Nations is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# GNUnited Nations is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNUnited Nations.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test unquoting in expressions.
+
+"${bindir}/gnun-validate-html" --expand-to=5.1.html \
+  "${srcdir}/validate/5.0.html" || exit 1
+diff "${srcdir}/validate/5.1.html" 5.1.html || exit 1
+rm 5.1.html

Index: tests/validate/5.0.html
===================================================================
RCS file: tests/validate/5.0.html
diff -N tests/validate/5.0.html
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/validate/5.0.html     13 Mar 2015 18:02:48 -0000      1.1
@@ -0,0 +1,38 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
+<html xmlns="http://www.w3.org/1999/xhtml"; xml:lang="en" lang="en">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<!-- Copyright (C) 2015 Free Software Foundation, Inc.
+
+This file is a part of GNUN testsuite.
+
+GNUnited Nations is free software: you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+GNUnited Nations is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNUnited Nations.  If not, see <http://www.gnu.org/licenses/>. -->
+<title>title</title>
+</head>
+<body>
+<h2>Title</h2><!--#if expr="a = /'a'/" -->
+wrong "a = /'a'/" expression evaluation</p><!--#endif
+ --><!--#if expr="'a' != /^a$/" -->
+wrong "a = /'a'/" expression evaluation</p><!--#endif
+ --><!--#if expr="a != 'a'" -->
+wrong "a != 'a'" expression evaluation</p><!--#endif
+ --><!--#if expr="'a' != a" -->
+wrong "'a' != a" expression evaluation</p><!--#endif
+ --><!--#if expr='a == "a"' -->
+wrong 'a == "a"' expression evaluation</p><!--#endif
+ --><!--#if expr='"a" == a' -->
+wrong '"a" == a' expression evaluation</p><!--#endif
+ --></body>
+</html>

Index: tests/validate/5.1.html
===================================================================
RCS file: tests/validate/5.1.html
diff -N tests/validate/5.1.html
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/validate/5.1.html     13 Mar 2015 18:02:48 -0000      1.1
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
+<html xmlns="http://www.w3.org/1999/xhtml"; xml:lang="en" lang="en">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<!-- Copyright (C) 2015 Free Software Foundation, Inc.
+
+This file is a part of GNUN testsuite.
+
+GNUnited Nations is free software: you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+GNUnited Nations is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNUnited Nations.  If not, see <http://www.gnu.org/licenses/>. -->
+<title>title</title>
+</head>
+<body>
+<h2>Title</h2></body>
+</html>

Index: tests/validate/match3
===================================================================
RCS file: tests/validate/match3
diff -N tests/validate/match3
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/validate/match3       13 Mar 2015 18:02:48 -0000      1.1
@@ -0,0 +1,24 @@
+#! /bin/sh
+# Copyright (C) 2015 Free Software Foundation, Inc.
+
+# This file is part of GNUnited Nations.
+
+# GNUnited Nations is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# GNUnited Nations is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GNUnited Nations.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test groups in regexes (only works with gawk).
+
+"${bindir}/gnun-validate-html" --expand-to=match3.1.html \
+  "${srcdir}/validate/match3.0.html" || exit 1
+diff "${srcdir}/validate/match3.1.html" match3.1.html || exit 1
+rm match3.1.html

Index: tests/validate/match3.1.html
===================================================================
RCS file: tests/validate/match3.1.html
diff -N tests/validate/match3.1.html
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/validate/match3.1.html        13 Mar 2015 18:02:49 -0000      1.1
@@ -0,0 +1,26 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
+<html xmlns="http://www.w3.org/1999/xhtml"; xml:lang="en" lang="en">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<!-- Copyright (C) 2015 Free Software Foundation, Inc.
+
+This file is a part of GNUN testsuite.
+
+GNUnited Nations is free software: you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+GNUnited Nations is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNUnited Nations.  If not, see <http://www.gnu.org/licenses/>. -->
+<title>title</title>
+</head>
+<body>
+<h2>Title</h2></body>
+</html>

Index: tests/validate/match3.0.html
===================================================================
RCS file: tests/validate/match3.0.html
diff -N tests/validate/match3.0.html
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/validate/match3.0.html        13 Mar 2015 18:02:49 -0000      1.1
@@ -0,0 +1,56 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
+<html xmlns="http://www.w3.org/1999/xhtml"; xml:lang="en" lang="en">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<!-- Copyright (C) 2015 Free Software Foundation, Inc.
+
+This file is a part of GNUN testsuite.
+
+GNUnited Nations is free software: you can redistribute it and/or
+modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+GNUnited Nations is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNUnited Nations.  If not, see <http://www.gnu.org/licenses/>. -->
+<title>title</title>
+</head>
+<body>
+<h2>Title</h2><!--#if expr="ab != /(a(b))/" -->
+wrong 'ab != /(a(b))/' expression evaluation</p>
+<!--#else
+ --><!--#if expr="$1 != ab" -->
+'ab != /(a(b))/'; $1 expression evaluation: 
+$1='<!--#echo encoding="none" var="1" -->'
+</p><!--#endif
+ --><!--#if expr="$2 != b" -->
+'ab != /(a(b))/'; $2 expression evaluation: 
+$2='<!--#echo encoding="none" var="2" -->'
+</p><!--#endif
+ --><!--#endif
+ --><!--#if expr="c1234567890ab = /(1)(2)(3)(4)(5)(6)(7)(8)((9)(0))/"
+ --><!--#if expr="$10" -->
+wrong evaluation of "c1234567890ab = /(1)(2)(3)(4)(5)(6)(7)(8)((9)(0))/";
+$10=<!--#echo encoding="none" var="10" -->
+is assigned, unlike in Apache</p><!--#endif
+ --><!--#if expr="$0 != 1234567890" -->
+wrong evaluation of "c1234567890ab = /(1)(2)(3)(4)(5)(6)(7)(8)((9)(0))/";
+$0=<!--#echo encoding="none" var="0" --></p>
+<!--#endif
+ --><!--#else -->
+wrong $1234567890 = /(1)(2)(3)(4)(5)(6)(7)(8)((9)(0))/ evaluation</p>
+<!--#endif
+ --><!--#if expr="abd != /(a(b))c/"
+ --><!--#if expr="$1" -->
+wrong evaluation of abd != /(a(b))c/: $1=<!--#echo encoding="none" var="1"
+ --></p><!--#endif
+ --><!--#if expr="$2" -->
+wrong evaluation of abd != /(a(b))c/: $2=<!--#echo encoding="none" var="2"
+ --></p><!--#endif --><!--#endif --></body>
+</html>

Index: expand-ssi.awk
===================================================================
RCS file: expand-ssi.awk
diff -N expand-ssi.awk
--- expand-ssi.awk      15 Mar 2014 07:58:23 -0000      1.6
+++ /dev/null   1 Jan 1970 00:00:00 -0000
@@ -1,529 +0,0 @@
-# expand-ssi.awk: process Apache SSI directives.
-
-# Copyright (C) 2014 Free Software Foundation, Inc.
-
-# This file is part of GNUnited Nations.
-
-# GNUnited Nations is free software: you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-
-# GNUnited Nations is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with GNUnited Nations.  If not, see <http://www.gnu.org/licenses/>.
-
-# The implementation is very limited,
-# e.g. `&&' and `||' in `if' expressions are not supported, as well as
-# many cases of quoting; CGI includes would be expanded in a wrong way.
-
-# Fallback for awks who don't support RT.
-BEGIN { RT = "\n" }
-
-# Read whole file to `text'.
-{ text = text $0 RT }
-
-# Substitute the variables in `str' with their values;
-# return the result.
-function expand_var_value(str,
-  vars, n, i, begin, end, rest, var, val)
-{
-  n = split(str, vars, /\$/)
-  if(!n)
-    return ""
-  str = vars[1];
-  for(i = 2; i <= n; i++)
-    {
-      if(substr(vars[i], 1, 1) == "{")
-        {
-          begin = 2; end = index(vars[i], "}"); rest = end + 1
-        }
-      else
-        {
-          begin = 1; end = match(vars[i] " ", /[^0-9a-zA-Z_]/); rest = end
-        }
-      if(end <= begin)
-        {
-          str = str "$" vars[i]; continue
-        }
-      var = substr(vars[i],begin,end - begin)
-      if(var in apache_variables)
-        val = apache_variables[var]
-      else
-        val = ""
-      str = str val (rest <= length(vars[i])? substr(vars[i], rest): "")
-    }
-  return str
-}
-
-# Remove the first and the last character.
-function unquote(str)
-{
-  return substr(str, 2, length(str) - 2)
-}
-
-# Process `<!--#set ... -->' directive.
-function assign_var(var, val,
-  i, q, n, expanded_val)
-{
-  # Extract variable name and value.
-  var = unquote(var)
-  q = substr(val, 1, 1)
-  val = unquote(val)
-  if(q == "\"")
-    gsub(/\\"/, q, val)
-  if(q == "'")
-    gsub(/\\'/, q, val)
-  # Handle escaped `$'s when expanding val.
-  # Note: the unescaping is done in a different way when
-  # expanding `if' expressions in eval_expression().
-  n = split(val, arr, /\\\$/)
-  if(!n)
-    expanded_val = ""
-  else
-    {
-      expanded_val = expand_var_value(arr[1])
-      for(i = 2; i <= n; i++)
-        expanded_val = expanded_val "$" expand_var_value(arr[i])
-    }
-  apache_variables[var] = expanded_val
-}
-
-# URL encoding.
-function encode_url(str)
-{
-  gsub(/%/, "%25", str); # This substitution must be applied first.
-  gsub(/\t/, "%09", str); gsub(/\n/, "%0a", str);  gsub(/\r/, "%0d", str)
-  gsub(/ /, "%20", str);  gsub(/"/, "%22", str);   gsub(/#/, "%23", str)
-  gsub(/;/, "%3b", str);  gsub(/</, "%3c", str);   gsub(/>/, "%3e", str)
-  gsub(/\?/, "%3f", str); gsub(/\[/, "%5b", str);  gsub(/\]/, "%5d", str)
-  gsub(/\^/, "%5e", str); gsub(/`/, "%60", str);
-  gsub(/\{/, "%7b", str);  gsub(/[|]/, "%7c", str); gsub(/}/, "%7d", str)
-  return str
-}
-
-# Entity encoding.
-function encode_entity(str)
-{
-  gsub(/&/, "\\&amp;", str); gsub(/"/, "\\&quot;", str)
-  gsub(/</, "\\&lt;", str);  gsub(/>/, "\\&gt;", str)
-  return str;
-}
-
-# Process `<!--#echo ... -->' directive.
-function echo_var(str, arr,
-  var, val, enc)
-{
-  if(length(arr[2]) > 2)
-    {
-      var = unquote(arr[2])
-      if(var in apache_variables)
-        val = apache_variables[var]
-      else
-        val = ""
-      if(1 in arr)
-        {
-          enc = arr[1]
-          if(enc == "entity")
-            val = encode_entity(val)
-          else if(enc == "url")
-            val = encode_url(val)
-          else if(enc != "none")
-            {
-              print my_name "unknown encoding `" enc \
-                    "' in echo directive" > "/dev/stderr"
-              exit 1
-            }
-        }
-      else
-        val = encode_entity(val)
-      printf("%s", val)
-    }
-  else
-    {
-      val = substr(str,  1, match(str, /[ \t\r\n]-->/))
-      print my_name "couldn't process echo directive `" val "'" > "/dev/stderr"
-      exit 1
-    }
-}
-
-# Remove leading and trailing spaces, unquote, expand variables.
-function unquoted_var(str,
-  val)
-{
-  val = str
-  sub(/^[ \t]*/, "", val)
-  sub(/[ \t]*$/, "", val)
-  if(val ~ /^'.*'$/ || val ~ /^".*"$/)
-    val = unquote(val)
-  return expand_var_value(val)
-}
-
-# Evaluate condition from `<!--#if ... -->' directive
-# Supported forms are "string", "!string", "string1 = string2",
-# "string1 == string2", "string1 != string2".
-# Other forms ("-A string", "string1 > string2", "expr1 && expr2" etc.)
-# are not supported.
-# Advanced quoting patterns like  "' '${test}' ' = '   '"
-# are also not supported (use "' ${test} ' = '   '").
-function eval_expression(str,
-  arr, lval, rval, negate, pattern_match, res, idx)
-{
-  # Escaped `$'s are treated the same way as unescaped in `if' expressions.
-  gsub(/\\\$/, "$", str)
-  idx = index(str, "=")
-  if(!idx)
-    {
-      # No `=': we've got either "!string" or "string" kind of expression.
-      if(str ~ /^[ \t]*!/)
-        return !length(unquoted_var(substr(str, index(str, "!") + 1)))
-      return length(unquoted_var(str))
-    }
-  if(idx > 1)
-    arr[1] = substr(str, 1, idx - 1)
-  else
-    arr[1] = ""
-  arr[2] = substr(str, idx + 1)
-  if(str ~ /!==/)
-    {
-      print my_name "bad expression `" str "'" > "/dev/stderr"
-      exit 1
-    }
-  # "string1 == string2" or "string1 != string2" or "string1 = string2" case.
-  pattern_match = 0
-  negate = 0
-  if(arr[2] ~ /^=/)
-    {
-      arr[2] = substr(arr[2], 2)
-      negate = -1
-    }
-  sub(/^[ \t]*/, "", arr[2])
-  sub(/[ \t]*$/, "", arr[2])
-  if(arr[2] ~ /^\/.*\/$/)
-    {
-      pattern_match = 1
-      arr[2] = unquote(arr[2])
-    }
-  if(!negate && (arr[1] ~ /!$/))
-    {
-      negate = 1
-      sub(/.$/, "", arr[1])
-    }
-  lval = unquoted_var(arr[1])
-  rval = arr[2]
-  if(rval ~ /^'.*'$/ || rval ~ /^".*"$/ && !pattern_match)
-    rval = unquote(rval)
-  rval = expand_var_value(rval)
-  res = pattern_match? (lval ~ rval): (lval == rval)
-  if(negate > 0)
-    return !res
-  return res
-}
-
-# Find the end of the directive and check that
-# no comment begins before the directive ends.
-function directive_idx(str,
-  i, idx1)
-{
-  i = match(str, /[ \t\r\n]-->/)
-  if(i == 0)
-    {
-      print my_name "broken chunk `<!--#" str "': directive has no end" \
-            > "/dev/stderr"
-      exit 1
-    }
-  idx1 = index(str, "<!--")
-  if(idx1 && i >= idx1)
-    {
-      print my_name "broken chunk `<!--#" str "' (first ` -->' at " i \
-            " comes after next `<!--' at " idx1 ")" > "/dev/stderr"
-      exit 1
-    }
-  return i + 4
-}
-
-function process_endif()
-{
-  if(--depth < 0)
-    {
-      print my_name "unexpected endif directive in `" \
-            chunks[ch_i] "'" > "/dev/stderr"
-      exit 1
-    }
-  printf("%s", substr(chunks[ch_i], idx) "")
-}
-
-# Main program.
-END {
-  # Don't bother about empty files.
-  if(!length(text))
-    exit 0
-# Assign initial values.
-  # Assign prefix for error messages.
-  my_name = "expand-ssi.awk: "
-  # Default root directory is "../.."
-  if(root == "")
-    root = "../.."
-
-  # The passed_vars variable are expected to come from the command line.
-  ch_n = split(passed_vars, split_vars, /;/)
-  for(ch_i = 1; ch_i <= ch_n; ch_i++)
-    {
-      m = index(split_vars[ch_i], "=")
-      value = ""
-      if(m)
-        {
-          var_name = substr(split_vars[ch_i], 1, m - 1)
-          if(m + 1 <= length(split_vars[ch_i]))
-            value = substr(split_vars[ch_i], m + 1)
-        }
-      else
-        var_name = split_vars[ch_i]
-      if(length(var_name))
-        apache_variables[var_name] = value
-    }
-
-  # Relative directory name for the processed file
-  # (FILENAME must be relative).
-  relative_dir_name = FILENAME
-  # Assume that stdin comes from the current directory,
-  # although it is not very probable.
-  if (relative_dir_name == "-")
-    relative_dir_name = "./foo.html"
-  if (relative_dir_name ~ /\//)
-    sub (/[^\/]*$/, "", relative_dir_name)
-  else
-    relative_dir_name = root
-  sub (/\/*$/, "/", relative_dir_name)
-
-# Split the text by Apache directives.
-  ch_n = split(text, chunks, "<!--#");
-  depth = 0 # The current depth of `if' nesting.
-# Process directives.
-  if(ch_n > 0)
-    printf("%s", chunks[1])
-  for(ch_i = 2; ch_i <= ch_n; ch_i++)
-    {
-      idx = directive_idx(chunks[ch_i])
-# `Set' directive: assign a variable.
-      directive = substr(chunks[ch_i], 1, idx - 1)
-      if(directive \
-~ /^set[ \t\r\n]+var=('.+'|".+")[ \t\r\n]+value=('.*'|".*")[ \t\r\n]+-->/)
-        {
-          pattern[1] = directive
-          sub(/^set[ \t\r\n]+var=/, "", pattern[1])
-          sub(/[ \t\r\n]+value=('.*'|".*")[ \t\r\n]+-->.*/,
-              "", pattern[1])
-          pattern[2] = directive
-          sub(/^set[ \t\r\n]+var=('.+'|".+")[ \t\r\n]+value=/, \
-              "", pattern[2])
-          sub(/[ \t\r\n]+-->.*/, "", pattern[2])
-          assign_var(pattern[1], pattern[2])
-          if(idx <= length(chunks[ch_i]))
-            printf("%s", substr(chunks[ch_i], idx) "")
-          continue
-        }
-# `Set' directive (different order of attributes).
-      if(directive \
-~ /^set[ \t\r\n]+value=('.*'|".*")[ \t\r\n]+var=('.+'|".+")[ \t\r\n]+-->/)
-        {
-          pattern[1] = directive
-          sub(/^set[ \t\r\n]+value=/, "", pattern[1])
-          sub(/[ \t\r\n]+var=('.*'|".*")[ \t\r\n]+-->.*/,
-              "", pattern[1])
-          pattern[2] = directive
-          sub(/^set[ \t\r\n]+value=('.+'|".+")[ \t\r\n]+var=/, \
-              "", pattern[2])
-          sub(/[ \t\r\n]+-->.*/, "", pattern[2])
-          assign_var(pattern[2], pattern[1])
-          if(idx <= length(chunks[ch_i]))
-            printf("%s", substr(chunks[ch_i], idx) "")
-          continue
-        }
-# `Echo' directive: output a variable.
-      if(chunks[ch_i] \
-~ /^echo[ \t\r\n]+(encoding=("[^\"]*"|'[^']*')[ 
\t\r\n]+)?var=('[^']+'|"[^\"]+")[ \t\r\n]+-->/)
-        {
-          if(chunks[ch_i] \
-~ /^echo[ \t\r\n]+(encoding=("[^\"]*"|'[^']*')[ \t\r\n]+)/)
-            {
-              pattern[1] = chunks[ch_i]
-              sub(/^echo[ \t\r\n]+encoding=/, "", pattern[1])
-              idx1 = index(substr(pattern[1], 2), substr(pattern[1], 1, 1))
-              pattern[1] = substr(pattern[1], 2, idx1 - 1)
-            }
-          else
-            delete pattern[1]
-          pattern[2] = chunks[ch_i]
-          sub(\
-/^echo[ \t\r\n]+(encoding=("[^\"]*"|'[^']*')[ \t\r\n]+)?var=/, \
-"", pattern[2])
-          idx1 = index(substr(pattern[2], 2), substr(pattern[2], 1, 1))
-          pattern[2] = substr(pattern[2], 1, idx1 + 1)
-          echo_var(chunks[ch_i], pattern)
-          if(idx <= length(chunks[ch_i]))
-            printf("%s", substr(chunks[ch_i], idx) "")
-          continue
-        }
-# `Include' directive: expand the file and rearrange chunks.
-      if(chunks[ch_i] ~ \
-/^include[ \t\r\n]+(file|virtual)=("[^"]*"|'[^']*')[ \t\r\n]+-->/)
-        {
-          pattern[2] = chunks[ch_i]
-          sub(/^include[ \t\r\n]+(file|virtual)=/, "", pattern[2])
-          pattern[1] = substr(pattern[2], 1, 1)
-          idx1 = index(substr(pattern[2], 2), pattern[1])
-          if(idx1 > 1)
-            name = substr(pattern[2], 2, idx1 - 1)
-          else
-            name = ""
-          # Construct the real path to the file.
-          # Note: relative paths actually won't work in nested includes;
-          # we could track the changes of the path through assigning a path
-          # to every chunk, but it doesn't seem to have much sense, since
-          # it is documented that we should only use absolute paths.
-          if(name ~ /^\//)
-            name = root name
-          else
-            name = relative_dir_name name
-          # Load the file.
-          text = ""
-          while(1)
-            {
-              m = getline < name
-              if(m < 0)
-                {
-                  print my_name "can't read file `" name "': " ERRNO \
-                        > "/dev/stderr"
-                  exit 1
-                }
-              if(!m)
-                break
-              text = text $0 RT
-            }
-          close(name)
-          if(!length(text))
-            {
-              printf("%s", substr(chunks[ch_i], idx) "")
-              continue
-            }
-          m = split(text, next_chunks, "<!--#")
-          # Append the tail of current chunk to the last chunk
-          # of the included file.
-          next_chunks[m] = next_chunks[m] substr(chunks[ch_i], idx)
-          # Move the remaining chunks and put the included file
-          # in freed space.
-          if(m > 1)
-            {
-              for(j = ch_n; j > ch_i; j--)
-                chunks[j + m - 1] = chunks[j]
-              for(j = 2; j <= m ; j++)
-                chunks[j + ch_i - 1] = next_chunks[j]
-              ch_n += m - 1
-            }
-          printf("%s", next_chunks[1] "")
-          continue
-        } # if(match(chunks[ch_i], ...
-# `If' directive: skip all branches with false conditions;
-# output the branch with true condition.
-      if(chunks[ch_i] ~ /^if[ \t\r\n]+expr=('.*'|".*")[ \t\r\n]+-->/)
-        {
-          depth++
-          finish = 0
-          while(ch_i <= ch_n && !finish)
-            {
-              exp_val = 1
-              # `Else' branches are output unconditionally
-              # (no matching `if'/`elif' found).
-              if(chunks[ch_i] !~ /^else[ \t\r\n]+-->/)
-                {
-                  exp_l = index(chunks[ch_i], "expr=") + length("expr=")
-                  qu = substr(chunks[ch_i], exp_l, 1)
-                  expr = substr(chunks[ch_i], exp_l + 1)
-                  expression = substr(expr, 1, match(expr, "[^\\\\]" qu))
-                  exp_val = eval_expression(expression)
-                }
-              if(exp_val)
-                {
-                  if(idx <= length(chunks[ch_i]))
-                    printf("%s", substr(chunks[ch_i], idx) "")
-                  break
-                }
-              # Skip to next `elif', `else' or `endif'
-              d = 0;
-              while (++ch_i <= ch_n)
-                {
-                  idx = directive_idx(chunks[ch_i])
-                  # Next if: increase depth.
-                  if(chunks[ch_i] \
-                     ~ /^if[ \t\r\n]+expr=('.*'|".*")[ \t\r\n]+-->/)
-                    {
-                      d++; continue
-                    }
-                  # `Endif': break if we are at the initial depth.
-                  if(chunks[ch_i] ~ /^endif[ \t\r\n]+-->/)
-                    {
-                      if(d-- > 0)
-                        continue
-                      finish = !0; process_endif(); break
-                    }
-                  # `Else' or `elif': break if we are at the initial depth.
-                  if(d == 0 && (chunks[ch_i] ~ /^else[ \t\r\n]+-->/ \
-                     || chunks[ch_i] \
-                          ~ /^elif[ \t\r\n]+expr=('.*'|".*")[ \t\r\n]+-->/))
-                    break
-                } # while (++ch_i <= ch_n)
-            } # while(ch_i <= ch_n && !finish)
-          continue
-        } # if(chunks[ch_i] ~ /^if[ \t\r\n]+ ...
-# `Elif' or `else' directive: skip up to endif (since the branch with
-# matching expression was output when processing the `if' directive).
-      if((chunks[ch_i] ~ /^else[ \t\r\n]+-->/) \
-          || (chunks[ch_i] ~ /^elif[ \t\r\n]+expr=('.*'|".*")[ \t\r\n]+-->/))
-        {
-          d = 0; chunk_i = ch_i
-          while (++ch_i <= ch_n)
-            {
-              idx = directive_idx(chunks[ch_i])
-              if(chunks[ch_i] ~ /^if[ \t\r\n]+expr=('.*'|".*")[ \t\r\n]+-->/)
-                d++
-              if(chunks[ch_i] ~ /^endif[ \t\r\n]+-->/)
-                if(d-- == 0)
-                  {
-                    process_endif(); break
-                  }
-            }
-          if(chunks[ch_i] !~ /^endif[ \t\r\n]+-->/)
-            {
-              print my_name "couldn't find matching endif for `<!--#" \
-                chunks[chunk_i] "'" > "/dev/stderr"
-              exit 1
-            }
-          continue
-        } # if((chunks[ch_i] ~ /^else[ \t\r\n]+-->/...
-# `Endif' directive: decrease depth and output the rest of the chunk.
-      if(chunks[ch_i] ~ /^endif[ \t\r\n]+-->/)
-        {
-          process_endif(); continue
-        }
-# A directive that must be parsed, but didn't match against its pattern.
-      if(chunks[ch_i] ~ /^(if|elif|else|endif|include|set|echo)\>/)
-        {
-          print my_name "couldn't parse directive `<!--#" \
-            substr(chunks[ch_i], 1, idx - 1) "'" > "/dev/stderr"
-          exit 1
-        }
-      # Unknown directive: hopefully it won't influence the validity.
-      printf("<!--#%s", chunks[ch_i] "")
-    } # for(ch_i = 2; ch_i <= ch_n; ch_i++)
-  if(depth > 0)
-    {
-      print my_name "some if directives are matched with no endif." \
-            > "/dev/stderr"
-      exit 1
-    }
-}



reply via email to

[Prev in Thread] Current Thread [Next in Thread]