cvsimport master
authoradamch <adamch>
Sun, 16 Dec 2007 23:11:41 +0000 (23:11 +0000)
committeradamch <adamch>
Sun, 16 Dec 2007 23:11:41 +0000 (23:11 +0000)
53 files changed:
Makefile
elisp/domtool-mode-startup.el
elisp/domtool-mode.el
lib/alias.dtl
lib/apache.dtl
lib/apache_options.dtl
lib/bind.dtl
lib/domain.dtl
lib/easy_domain.dtl
lib/mailman.dtl
lib/mod_cache.dtl [new file with mode: 0644]
lib/mod_rewrite.dtl
pcre/mlton/FFI/.cvsignore [new file with mode: 0644]
pcre/mlton/libpcre-h.sml [new file with mode: 0644]
pcre/pcre_sml.c [new file with mode: 0644]
pcre/pcre_sml.h [new file with mode: 0644]
pcre/smlnj/.cvsignore [new file with mode: 0644]
pcre/smlnj/FFI/.cvsignore [new file with mode: 0644]
pcre/smlnj/libpcre-h.sml [new file with mode: 0644]
scripts/domtool-publish
src/ast.sml
src/autodoc.sml
src/client.sml
src/compat.sig
src/compat_mlton.sml
src/compat_smlnj.sml
src/describe.sig
src/describe.sml
src/domain.sml
src/domtool.grm
src/domtool.lex
src/env.sig
src/env.sml
src/errormsg.sig
src/errormsg.sml
src/eval.sml
src/main.sml
src/order.sml
src/pcre.sig [new file with mode: 0644]
src/pcre.sml [new file with mode: 0644]
src/plugins/alias.sml
src/plugins/apache.sml
src/plugins/bind.sml
src/plugins/domtool-mysql
src/plugins/easy_domain.sml
src/prefix.cm
src/prefix.mlb
src/printFn.sml
src/reduce.sml
src/sources
src/tycheck.sml
src/unused.sig [new file with mode: 0644]
src/unused.sml [new file with mode: 0644]

index 9396f38..b92bf32 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -4,20 +4,21 @@ bin/vmailpasswd: src/mail/vmailpasswd.c
        $(CC) -lcrypt -o $@ $<
 
 COMMON_DEPS := configDefault/config.sig configDefault/configDefault.sml \
-       openssl/openssl_sml.so config.sml
+       openssl/openssl_sml.so pcre/pcre_sml.so config.sml
 
 EMACS_DIR := /usr/local/share/emacs/site-lisp/domtool-mode
 
 config.sml:
        echo -e 'structure Config :> CONFIG = struct\nopen ConfigDefault\nend' > $@
 
-.PHONY: all mlton smlnj install
+.PHONY: all mlton smlnj install install_sos
 
 mlton: bin/domtool-server bin/domtool-client bin/domtool-slave \
        bin/domtool-admin bin/domtool-doc bin/dbtool bin/vmail \
        bin/smtplog bin/setsa bin/mysql-fixperms bin/webbw
 
-smlnj: $(COMMON_DEPS) openssl/smlnj/FFI/libssl.h.cm src/domtool.cm
+smlnj: $(COMMON_DEPS) openssl/smlnj/FFI/libssl.h.cm pcre/smlnj/FFI/libpcre.h.cm \
+       src/domtool.cm
 
 configDefault/config.sig: src/config.sig.header \
                configDefault/*.csg configDefault/*.cfs \
@@ -43,6 +44,14 @@ openssl/openssl_sml.so: openssl/openssl_sml.o
                -o openssl/openssl_sml.so \
                openssl/openssl_sml.o -lssl
 
+pcre/pcre_sml.o: pcre/pcre_sml.c
+       gcc -fPIC -c pcre/pcre_sml.c -o pcre/pcre_sml.o
+
+pcre/pcre_sml.so: pcre/pcre_sml.o
+       gcc -shared -Wl,-soname,pcre_sml.so \
+               -o pcre/pcre_sml.so \
+               pcre/pcre_sml.o -lpcre
+
 src/domtool.cm: src/prefix.cm src/sources
        cat src/prefix.cm src/sources >src/domtool.cm
 
@@ -100,6 +109,16 @@ openssl/mlton/FFI/libssl.h.mlb: openssl/openssl_sml.h
        -mlbfile libssl.h.mlb -cppopt -D__builtin_va_list="void*" \
        ../openssl_sml.h
 
+pcre/smlnj/FFI/libpcre.h.cm: pcre/pcre_sml.h
+       cd pcre/smlnj ; ml-nlffigen -d FFI -lh LibpcreH.libh -include ../libpcre-h.sml \
+       -cm libpcre.h.cm -D__builtin_va_list="void*" \
+       ../pcre_sml.h
+
+pcre/mlton/FFI/libpcre.h.mlb: pcre/pcre_sml.h
+       cd pcre/mlton ; mlnlffigen -dir FFI -libhandle LibpcreH.libh -include ../libpcre-h.sml \
+       -mlbfile libpcre.h.mlb -cppopt -D__builtin_va_list="void*" \
+       ../pcre_sml.h
+
 %.lex.sml: %.lex
        mllex $<
 
@@ -107,6 +126,7 @@ openssl/mlton/FFI/libssl.h.mlb: openssl/openssl_sml.h
        mlyacc $<
 
 COMMON_MLTON_DEPS := openssl/mlton/FFI/libssl.h.mlb \
+       pcre/mlton/FFI/libpcre.h.mlb \
        src/domtool.lex.sml \
        src/domtool.grm.sig src/domtool.grm.sml \
        $(COMMON_DEPS) src/*.sig src/*.sml \
@@ -155,7 +175,11 @@ bin/webbw: $(COMMON_MLTON_DEPS) src/stats/webbw.mlb
 elisp/domtool-tables.el: lib/*.dtl bin/domtool-doc
        bin/domtool-doc -basis -emacs >$@
 
-install:
+install_sos:
+       cp openssl/openssl_sml.so /usr/local/lib/
+       cp pcre/pcre_sml.so /usr/local/lib/
+
+install: install_sos
        cp scripts/domtool-publish /usr/local/sbin/
        cp scripts/domtool-reset-global /usr/local/sbin/
        cp scripts/domtool-reset-local /usr/local/sbin/
@@ -169,7 +193,6 @@ install:
        cp scripts/domtool-slave-logged /usr/local/bin/
        cp scripts/domtool-server /etc/init.d/
        cp scripts/domtool-slave /etc/init.d/
-       cp openssl/openssl_sml.so /usr/local/lib/
        -cp bin/domtool-server /usr/local/sbin/
        -cp bin/domtool-slave /usr/local/sbin/
        -cp bin/domtool-client /usr/local/bin/domtool
index fc63a0e..a909c3e 100644 (file)
@@ -2,3 +2,5 @@
 Major Mode for editing Domtool files." t nil)
 
 (add-to-list (quote auto-mode-alist) (quote ("\\.\\(dtl\\|com\\|net\\|org\\|edu\\|mil\\|biz\\|info\\|name\\|be\\|ca\\|cc\\|de\\|fr\\|in\\|mu\\|se\\|uk\\|us\\|ws\\)$" . domtool-mode)))
+
+(provide 'domtool-mode-startup)
index 292acfa..17c90c5 100644 (file)
 (defun domtool-syms-re (&rest syms)
   (concat "\\<" (regexp-opt syms t) "\\>"))
 
-(load-file "/usr/local/share/emacs/site-lisp/domtool-mode/domtool-tables.el")
+(require 'domtool-tables)
 
 (defvar domtool-font-lock-keywords
   `(,(concat
       "\\_<"
       (regexp-opt '("let" "in" "begin" "end" "with" "where" "extern" "type"
-                    "val" "context" "Root")
+                    "val" "context" "Root" "if" "then" "else")
                   t)
       "\\_>")
 
@@ -83,8 +83,8 @@
           . domtool-font-lock-syntactic-keywords)
          (font-lock-syntactic-face-function
           . domtool-font-lock-syntactic-face-function)))
-  (set (make-local-variable 'comment-start) "(* ")
-  (set (make-local-variable 'comment-end) " *)")
+  (set (make-local-variable 'comment-start-regexp) "(\\*\\|{{")
+  (set (make-local-variable 'comment-end-regexp) "\\*)\\|}}")
   (set (make-local-variable 'comment-nested) t)
 
   (set (make-local-variable 'compile-command)
 
 (defun until-closed-helper (level)
   (if
-      (re-search-backward "\\_<\\(with\\|where\\|begin\\|end\\|let\\)\\_>"
+      (re-search-backward "\\_<\\(with\\|where\\|begin\\|end\\|let\\|val\\|type\\|if\\)\\_>"
                          nil t)
       (cond
        ((string= (match-string 0) "end")
        (until-closed-helper (+ level 1)))
        ((= level 0)
        (current-indentation))
+       ((and
+        (string= (match-string 0) "with")
+        (save-excursion
+          (backward-char)
+          (looking-at "\\s-")))
+       (until-closed-helper level))
        (t
        (until-closed-helper (- level 1))))
 
     (back-to-indentation)
     (multiple-value-bind (previous-keyword base-indent)
         (save-excursion
-          (if (re-search-backward "\\_<\\(with\\|where\\|begin\\|end\\|let\\|in\\)\\_>"
+          (if (re-search-backward "\\_<\\(with\\|where\\|begin\\|end\\|let\\|in\\|val\\|type\\|if\\)\\_>\\|}}\\|{{"
                                   nil t)
               (values (match-string 0) (current-indentation))
             (values nil 0)))
           'noindent)
          ((nth 4 state)
           (domtool-calculate-comment-indent state))
-         ((looking-at "\\_<\\(with\\|end\\|in\\)\\_>")
+        ((looking-at "{{\\|\\_<\\(extern\\|val\\|type\\|context\\)\\_>")
+         0)
+         ((looking-at "\\_<\\(with\\|end\\|in\\|else\\)\\_>")
           (until-closed))
          ((not previous-keyword)
           base-indent)
          ((string= previous-keyword "end")
           base-indent)
+         ((looking-at "\\_<\\(val\\|extern\\|context\\)\\_>")
+          base-indent)
          (t
           (+ base-indent domtool-indent)))))))
 
                    (incf depth)
                  (decf depth)))
              (+ (current-indentation) depth)))))
+
+(provide 'domtool-mode)
index 113654b..e84d0a7 100644 (file)
@@ -13,11 +13,7 @@ extern type aliasSource;
 extern val userSource : emailUser -> aliasSource;
 {{The part appear before the "@" in your desired source address}}
 extern val defaultSource : aliasSource;
-{{Matches any mail to this domain that doesn't match any other rule, with the
-  exception of systemwide usernames like UNIX users.}}
-extern val catchAllSource : aliasSource;
-{{Matches any mail to this domain that doesn't match any other rule, even
-  for systemwide usernames.}}
+{{Matches any mail to this domain that doesn't match any other rule.}}
 
 extern type aliasTarget;
 {{A place to redirect messages}}
@@ -41,5 +37,5 @@ val aliasDrop = \user -> aliasPrim (userSource user) dropTarget;
 {{Silently delete mail to the user at the current domain.}}
 
 val defaultAlias = \email -> aliasPrim defaultSource (addressTarget email);
-{{When a message to the current domain doesn't match any other alias, send it to
+{{When a message to the current domain doesn't match any other rule, send it to
   this e-mail address.}}
index 1c3b1ad..ed6f8b1 100644 (file)
@@ -31,19 +31,30 @@ extern val no_ssl : ssl;
 extern val use_cert : ssl_cert_path -> ssl;
 
 extern val vhost : host -> Vhost => [Domain]
-       {WebPlaces : [web_place],
-        SSL : ssl,
-        User : your_user,
-        Group : your_group,
-        DocumentRoot : your_path,
-        ServerAdmin : email,
-        SuExec : suexec_flag};
+  {WebPlaces : [web_place],
+  SSL : ssl,
+  User : your_user,
+  Group : your_group,
+  DocumentRoot : your_path,
+  ServerAdmin : email,
+  SuExec : suexec_flag};
 {{Add a new named Apache virtual host, specifying which nodes' Apache servers
   should answer requests for this host, whether it should use SSL, what UNIX
   user and group dynamic content generators should be run as, the filesystem
   path to the static content root, and the e-mail address to which error pages
   should direct visitors.}}
 
+extern val vhostDefault : Vhost => [Domain]
+  {WebPlaces : [web_place],
+  SSL : ssl,
+  User : your_user,
+  Group : your_group,
+  DocumentRoot : your_path,
+  ServerAdmin : email,
+  SuExec : suexec_flag};
+{{Like <tt>vhost</tt>, but for, e.g., <tt>yourdomain.com</tt> instead of
+  <tt>www.yourdomain.com</tt>}}
+
 context Location;
 
 extern type location;
index 83f3465..7becc15 100644 (file)
@@ -5,6 +5,7 @@ extern type apache_option;
   Apache documentation</a> for what the options mean.}}
 
 extern val execCGI : apache_option;
+extern val followSymLinks : apache_option;
 extern val includesNOEXEC : apache_option;
 extern val indexes : apache_option;
 
@@ -31,3 +32,7 @@ extern val action : no_spaces -> location -> [^Vhost];
 extern val addDefaultCharset : no_spaces -> [^Vhost];
 {{See <a href="http://httpd.apache.org/docs/2.0/mod/core.html#adddefaultcharset">the
   Apache documentation</a>.}}
+
+extern type file_extension;
+extern val cgiExtension : file_extension -> [^Vhost];
+{{Ask for all files ending in a particular extension to be executed as CGI.}}
index 4f2c3c8..9cbc2fb 100644 (file)
@@ -3,10 +3,12 @@
 extern type dnsRecord;
 
 extern val dnsA : host -> ip -> dnsRecord;
+extern val dnsAAAA : host -> ipv6 -> dnsRecord;
 extern val dnsCNAME : host -> domain -> dnsRecord;
 extern val dnsMX : int -> domain -> dnsRecord;
 extern val dnsNS : domain -> dnsRecord;
 
 extern val dnsDefaultA : ip -> dnsRecord;
+extern val dnsDefaultAAAA : ipv6 -> dnsRecord;
 
 extern val dns : dnsRecord -> [Domain] {TTL : int};
index 3d9b088..8111c62 100644 (file)
@@ -8,6 +8,8 @@ extern type no_newlines;
 
 extern type ip;
 {{An IP address}}
+extern type ipv6;
+{{An IPv6 address}}
 
 extern type your_ip;
 extern val your_ip_to_ip : your_ip -> ip;
dissimilarity index 63%
index c4b3d4f..56bc092 100644 (file)
@@ -1,89 +1,82 @@
-{{The most common kinds of domain configuration}}
-
-val default_node : (node) = "mire";
-val web_node : (web_node) = "mire";
-
-val webAt =
-       \ n : (web_node) ->
-       \ host : (host) ->
-       \\ config : Vhost ->
-               (dns (dnsA host (ip_of_node (web_node_to_node n)));
-
-               vhost host where
-                       WebPlaces = [web_place_default n]
-               with
-                       config
-               end);
-
-val web = webAt web_node;
-
-val webAtIp =
-       \ ip : (your_ip) ->
-       \ host : (host) ->
-       \\ config : Vhost -> begin
-         dns (dnsA host (your_ip_to_ip ip));
-
-         vhost host where
-           WebPlaces = [web_place web_node ip]
-         with
-           config
-         end
-       end;
-
-val addDefaultAlias = begin
-  mailbox <- Mailbox;
-  defaultAlias mailbox
-end;
-
-val addWww = begin
-  web "www" with
-    serverAliasDefault;
-    www : [Vhost] <- WWW;
-    www
-  end
-end;
-
-val domNoWwwNoDefaultAlias =
-       \ d : (your_domain) ->
-       \\ config : Domain ->
-               domain d with
-                       dns (dnsNS "ns1.hcoop.net");
-                       dns (dnsNS "ns3.hcoop.net");
-
-                       dns (dnsDefaultA (ip_of_node (web_node_to_node web_node)));
-
-                       handleMail;
-                       dns (dnsMX 1 "deleuze.hcoop.net");
-
-                       config
-               end;
-
-val domNoDefaultAlias =
-       \ d : (your_domain) ->
-       \\ config : Domain ->
-               domNoWwwNoDefaultAlias d with
-                 addWww;
-                 config
-               end;
-
-val domNoWww =
-       \ d : (your_domain) ->
-       \\ config : Domain ->
-               domNoWwwNoDefaultAlias d with
-                 config;
-                 addDefaultAlias;
-               end;
-
-val dom =
-       \ d : (your_domain) ->
-       \\ config : Domain ->
-               domNoDefaultAlias d with
-                 config;
-                 addDefaultAlias;
-               end;
-
-val nameserver = \host -> dns (dnsNS host);
-val dnsIP = \from -> \to -> dns (dnsA from to);
-val dnsMail = \num -> \host -> dns (dnsMX num host);
-val dnsAlias = \from -> \to -> dns (dnsCNAME from to);
-val dnsDefault = \to -> dns (dnsDefaultA to);
+{{The most common kinds of domain configuration}}
+
+val default_node : (node) = "mire";
+val web_node : (web_node) = "mire";
+
+val webAt =
+  \ n : (web_node) ->
+  \ host : (host) ->
+  \\ config : Vhost -> begin
+    dns (dnsA host (ip_of_node (web_node_to_node n)));
+
+    vhost host where
+      WebPlaces = [web_place_default n]
+    with
+      config
+    end
+  end;
+
+val web = webAt web_node;
+
+val webAtIp =
+  \ ip : (your_ip) ->
+  \ host : (host) ->
+  \\ config : Vhost -> begin
+    dns (dnsA host (your_ip_to_ip ip));
+
+    vhost host where
+      WebPlaces = [web_place web_node ip]
+    with
+      config
+    end
+  end;
+
+val addDefaultAlias = begin
+  mailbox <- Mailbox;
+  defaultAlias mailbox
+end;
+
+val addWww = begin
+  web "www" with
+    serverAliasDefault;
+    www : [Vhost] <- WWW;
+    www
+  end
+end;
+
+val dom =
+  \ d : (your_domain) ->
+  \\ config : Domain ->
+  domain d with
+    dns (dnsNS "ns1.hcoop.net");
+    dns (dnsNS "ns3.hcoop.net");
+
+    dns (dnsDefaultA (ip_of_node (web_node_to_node web_node)));
+
+    handleMail;
+    dns (dnsMX 1 "deleuze.hcoop.net");
+
+    createWWW : bool <- CreateWWW;
+    if createWWW then
+      addWww
+    else
+      Skip
+    end;
+
+    defAl : bool <- DefaultAlias;
+    if defAl then
+      addDefaultAlias
+    else
+      Skip
+    end;
+
+    config
+  end;
+
+val nameserver = \host -> dns (dnsNS host);
+val dnsIP = \from -> \to -> dns (dnsA from to);
+val dnsIPv6 = \from -> \to -> dns (dnsAAAA from to);
+val dnsMail = \num -> \host -> dns (dnsMX num host);
+val dnsAlias = \from -> \to -> dns (dnsCNAME from to);
+val dnsDefault = \to -> dns (dnsDefaultA to);
+val dnsDefaultv6 = \to -> dns (dnsDefaultAAAA to);
index 31cb02e..f2158b7 100644 (file)
@@ -22,10 +22,10 @@ extern val mailman_place_to_ip : mailman_place -> ip;
 {{Analogous to <tt>web_place</tt>, but based on <tt>mailman_node</tt>s}}
 
 extern val mailmanVhost : host -> [Domain]
-       {MailmanPlaces : [mailman_place],
-        SSL : ssl,
-        User : your_user,
-         ServerAdmin : email};
+  {MailmanPlaces : [mailman_place],
+  SSL : ssl,
+  User : your_user,
+  ServerAdmin : email};
 {{Create an Apache virtual host to serve as the web interface for some Mailman
   lists.}}
 
diff --git a/lib/mod_cache.dtl b/lib/mod_cache.dtl
new file mode 100644 (file)
index 0000000..ed9bcfd
--- /dev/null
@@ -0,0 +1,6 @@
+{{Apache mod_cache}}
+
+extern val diskCache : location -> [Vhost];
+{{See <a href="http://httpd.apache.org/docs/2.2/mod/mod_cache.html#cacheenable">the
+  Apache documentation</a>; specifically, the case where the first parameter is
+  <tt>disk</tt>.}}
index 2e29877..71ced0e 100644 (file)
@@ -46,11 +46,14 @@ extern type mod_rewrite_cond_flag;
 extern val cond_nocase : mod_rewrite_cond_flag;
 extern val ornext : mod_rewrite_cond_flag;
 
-extern val rewriteRule : no_spaces -> no_spaces -> [mod_rewrite_flag] -> [^Vhost];
+extern type regexp;
+{{PCRE regular expression}}
+
+extern val rewriteRule : regexp -> no_spaces -> [mod_rewrite_flag] -> [^Vhost];
 {{See <a href="http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#rewriterule">Apache
   documentation for <tt>RewriteRule</tt></a>.}}
 
-extern val rewriteCond : no_spaces -> no_spaces -> [mod_rewrite_cond_flag] -> [^Vhost];
+extern val rewriteCond : no_spaces -> regexp -> [mod_rewrite_cond_flag] -> [^Vhost];
 {{See <a href="http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#rewritecond">Apache
   documentation for <tt>RewriteCond</tt></a>.}}
 
diff --git a/pcre/mlton/FFI/.cvsignore b/pcre/mlton/FFI/.cvsignore
new file mode 100644 (file)
index 0000000..73ea653
--- /dev/null
@@ -0,0 +1,2 @@
+*.sml
+*.mlb
diff --git a/pcre/mlton/libpcre-h.sml b/pcre/mlton/libpcre-h.sml
new file mode 100644 (file)
index 0000000..356df39
--- /dev/null
@@ -0,0 +1,13 @@
+structure LibpcreH = struct
+    local
+        val lh = DynLinkage.open_lib
+             { name = "/usr/local/lib/pcre_sml.so", global = true, lazy = true }
+           handle DynLinkage.DynLinkError s => raise Fail s
+    in
+        fun libh s = let
+            val sh = DynLinkage.lib_symbol (lh, s)
+        in
+            fn () => DynLinkage.addr sh
+        end
+    end
+end
diff --git a/pcre/pcre_sml.c b/pcre/pcre_sml.c
new file mode 100644 (file)
index 0000000..48998ba
--- /dev/null
@@ -0,0 +1,15 @@
+#include <pcre.h>
+
+int PCRE_SML_validRegexp(const char *s) {
+  pcre *re;
+  const char *error;
+  int erroffset;
+
+  re = pcre_compile(s, 0, &error, &erroffset, NULL);
+  
+  if (re) {
+    pcre_free(re);
+    return 1;
+  } else
+    return 0;
+}
diff --git a/pcre/pcre_sml.h b/pcre/pcre_sml.h
new file mode 100644 (file)
index 0000000..4d69ec1
--- /dev/null
@@ -0,0 +1 @@
+int PCRE_SML_validRegexp(const char *);
diff --git a/pcre/smlnj/.cvsignore b/pcre/smlnj/.cvsignore
new file mode 100644 (file)
index 0000000..6dc8e1a
--- /dev/null
@@ -0,0 +1 @@
+.cm
diff --git a/pcre/smlnj/FFI/.cvsignore b/pcre/smlnj/FFI/.cvsignore
new file mode 100644 (file)
index 0000000..526588f
--- /dev/null
@@ -0,0 +1,3 @@
+*.sml
+*.mlb
+*.cm
diff --git a/pcre/smlnj/libpcre-h.sml b/pcre/smlnj/libpcre-h.sml
new file mode 100644 (file)
index 0000000..356df39
--- /dev/null
@@ -0,0 +1,13 @@
+structure LibpcreH = struct
+    local
+        val lh = DynLinkage.open_lib
+             { name = "/usr/local/lib/pcre_sml.so", global = true, lazy = true }
+           handle DynLinkage.DynLinkError s => raise Fail s
+    in
+        fun libh s = let
+            val sh = DynLinkage.lib_symbol (lh, s)
+        in
+            fn () => DynLinkage.addr sh
+        end
+    end
+end
index 3ce7c7d..63c5312 100755 (executable)
@@ -46,7 +46,7 @@ case $1 in
        ;;
        exim)
                /bin/cp /var/domtool/aliases /etc/aliases.hosted
-               #/bin/cp /var/domtool/aliases.default /etc/
+               /bin/cp /var/domtool/aliases.default /etc/aliases.wildcard
                redo_exim
        ;;
        mailman)
index ead8531..d4c5727 100644 (file)
@@ -1,5 +1,5 @@
 (* HCoop Domtool (http://hcoop.sourceforge.net/)
- * Copyright (c) 2006, Adam Chlipala
+ * Copyright (c) 2006-2007, Adam Chlipala
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -91,6 +91,8 @@ datatype exp' =
        (* Apply a TNested to an action *)
        | EALam of string * pred * exp
        (* Abstraction for building TNested values *)
+       | EIf of exp * exp * exp
+       (* If..then..else *)
 withtype exp = exp' * position
 
 datatype decl' =
index 640012c..c64180d 100644 (file)
@@ -252,7 +252,8 @@ fun makeEmacsKeywords infiles =
        printKind ("contexts", contexts);
        printKind ("actions", actions);
        printKind ("vals", vals);
-       printKind ("env-vars", StringSet.listItems evs)
+       printKind ("env-vars", StringSet.listItems evs);
+       print "(provide 'domtool-tables)\n"
     end
 
 end
index 4d32f65..863c0c0 100644 (file)
@@ -28,7 +28,7 @@ datatype passwd_result =
 fun getpass () =
     let
        val tty = Posix.FileSys.stdin
-       val termios = Compat.getattr tty
+       val termios = Posix.TTY.TC.getattr tty
        val fields = Posix.TTY.fieldsOf termios
 
        val termios' = Posix.TTY.termios {iflag = #iflag fields,
@@ -41,11 +41,11 @@ fun getpass () =
                                          ispeed = #ispeed fields,
                                          ospeed = #ospeed fields}
 
-       fun reset () = Compat.setattr (tty, Posix.TTY.TC.sanow, termios)
+       fun reset () = Posix.TTY.TC.setattr (tty, Posix.TTY.TC.sanow, termios)
     in
        print "        Password: ";
        TextIO.flushOut TextIO.stdOut;
-       Compat.setattr (tty, Posix.TTY.TC.sanow, termios');
+       Posix.TTY.TC.setattr (tty, Posix.TTY.TC.sanow, termios');
        case TextIO.inputLine TextIO.stdIn of
            NONE => (reset ();
                     Aborted)
index 220bfb0..b54bc27 100644 (file)
@@ -18,7 +18,4 @@
 
 signature COMPAT = sig
     structure Char : WORD
-
-    val getattr : Posix.TTY.file_desc -> Posix.TTY.termios
-    val setattr : Posix.TTY.file_desc * Posix.TTY.TC.set_action * Posix.TTY.termios -> unit
 end
index 9a6b60f..06aa3a3 100644 (file)
@@ -18,9 +18,6 @@
 
 structure Compat : COMPAT = struct
     structure Char = MLRep.Char.Unsigned
-
-    val getattr = Posix.TTY.TC.getattr
-    val setattr = Posix.TTY.TC.setattr
 end
 
 val _ = let
index 01cfa24..ff493be 100644 (file)
@@ -18,7 +18,4 @@
 
 structure Compat : COMPAT = struct
     structure Char = Word32
-
-    val getattr = Posix.TTY.getattr
-    val setattr = Posix.TTY.setattr
 end
index 268a9e1..8333c26 100644 (file)
@@ -26,6 +26,6 @@ signature DESCRIBE = sig
 
     val describe_type_error : Ast.position -> Ast.type_error -> unit
 
-
+    val ununify : Ast.typ -> Ast.typ
 
 end
index a753ddb..1e81550 100644 (file)
@@ -116,7 +116,18 @@ fun get_first_arg (t, _) =
       | TUnif (_, ref (SOME t')) => get_first_arg t'
       | _ => raise Fail "get_first_arg failed!"
 
-fun describe_type_error loc te =
+fun hint te =
+    case te of
+       WrongType (_, _, (TBase "string", _), (TBase "your_domain", _), _) =>
+       SOME "Did you forget to request permission to configure this domain?  See:\n\thttps://members.hcoop.net/portal/domain"
+      | WrongType (_, (EString dom, _), (TBase "string", _), (TBase "domain", _), _) =>
+       if CharVector.exists Char.isUpper dom then
+           SOME "Uppercase letters aren't allowed in domain strings."
+       else
+           NONE
+      | _ => NONE
+
+fun describe_type_error' loc te =
     case te of
        WrongType (place, e, t1, t2, ueo) =>
        (ErrorMsg.error (SOME loc) (place ^ " has wrong type.");
@@ -141,4 +152,29 @@ fun describe_type_error loc te =
         preface ("Have:", p_pred p1);
         preface ("Need:", p_pred p2))
 
+fun ununify (tAll as (t, _)) =
+    case t of
+       TUnif (_, ref (SOME t)) => ununify t
+      | _ => tAll
+
+fun normalize_error err =
+    case err of
+       WrongType (s, e, t1, t2, ueo) =>
+       WrongType (s, e, ununify t1, ununify t2, ueo)
+      | WrongForm (s1, s2, e, t, ueo) =>
+       WrongForm (s1, s2, e, ununify t, ueo)
+      | UnboundVariable _ => err
+      | WrongPred _ => err
+
+fun describe_type_error loc te =
+    let
+       val te = normalize_error te
+    in
+       describe_type_error' loc te;
+       Option.app (fn s => (print "Hint Monster says:\n";
+                            print s;
+                            print "\n"))
+                  (hint te)
+    end
+
 end
index 94eb077..8384f85 100644 (file)
@@ -80,6 +80,36 @@ fun validIp s =
        n1 >= 0 andalso n1 < 256 andalso n2 >= 0 andalso n2 < 256 andalso n3 >= 0 andalso n3 < 256 andalso n4 >= 0 andalso n4 < 256
       | _ => false
 
+fun isHexDigit ch = Char.isDigit ch orelse (ord ch >= ord #"a" andalso ord ch <= ord #"f")
+
+fun validIpv6 s =
+    let
+       val fields = String.fields (fn ch => ch = #":") s
+
+       val empties = foldl (fn ("", n) => n + 1
+                             | (_, n) => n) 0 fields
+
+       fun noIpv4 maxLen =
+           length fields >= 2
+           andalso length fields <= maxLen
+           andalso empties <= 1
+           andalso List.all (fn "" => true
+                              | s => size s <= 4
+                                     andalso CharVector.all isHexDigit s) fields
+
+       fun hasIpv4 () =
+           length fields > 0
+           andalso
+           let
+               val maybeIpv4 = List.last fields
+               val theRest = List.take (fields, length fields - 1)
+           in
+               validIp maybeIpv4 andalso noIpv4 6
+           end
+    in
+       noIpv4 8 orelse hasIpv4 ()
+    end
+
 fun isIdent ch = Char.isLower ch orelse Char.isDigit ch
 
 fun validHost s =
@@ -143,6 +173,10 @@ val _ = Env.type_one "ip"
        Env.string
        validIp
 
+val _ = Env.type_one "ipv6"
+       Env.string
+       validIpv6
+
 val _ = Env.type_one "host"
        Env.string
        validHost
index 532dad9..0cc4fbd 100644 (file)
@@ -32,7 +32,8 @@ open Ast
  | COLON | CARET | BANG | AND
  | LPAREN | RPAREN | LBRACK | RBRACK | LBRACE | RBRACE
  | EQ | COMMA | BSLASH | BSLASHBSLASH | SEMI | LET | IN | BEGIN | END
- | ROOT
+ | IF | THEN | ELSE
+ | ROOT | SKIP
  | EXTERN | TYPE | VAL | WITH | WHERE | CONTEXT
 
 %nonterm 
@@ -64,6 +65,7 @@ open Ast
 
 %name Domtool
 
+%nonassoc THEN ELSE
 %right SEMI
 %nonassoc COLON
 %nonassoc IN
@@ -128,6 +130,7 @@ exp    : apps                              (apps)
        | exp SEMI                          (exp)
        | SYMBOL LARROW CSYMBOL SEMI exp    (EGet (SYMBOL, NONE, CSYMBOL, exp), (SYMBOLleft, expright))
        | SYMBOL COLON typ LARROW CSYMBOL SEMI exp (EGet (SYMBOL, SOME typ, CSYMBOL, exp), (SYMBOLleft, expright))
+       | IF exp THEN exp ELSE exp END      (EIf (exp1, exp2, exp3), (IFleft, ENDright))
 
 apps   : term                              (term)
        | apps term                         (EApp (apps, term), (appsleft, termright))
@@ -139,6 +142,7 @@ term   : LPAREN exp RPAREN                 (exp)
        | LBRACK elist RBRACK               (EList elist, (LBRACKleft, RBRACKright))
        | LET exp IN exp END                (ELocal (exp1, exp2), (LETleft, ENDright))
        | SYMBOL                            (EVar SYMBOL, (SYMBOLleft, SYMBOLright))
+       | SKIP                              (ESkip, (SKIPleft, SKIPright))
 
 sets   : CSYMBOL EQ apps SEMIopt           ([(ESet (CSYMBOL, apps), (CSYMBOLleft, appsright))])
        | CSYMBOL EQ apps SEMI sets         ((ESet (CSYMBOL, apps), (CSYMBOLleft, appsright))
index 99449ca..4f51821 100644 (file)
@@ -130,6 +130,11 @@ lineComment = #[^\n]*\n;
 <INITIAL> "with"      => (Tokens.WITH (yypos, yypos + size yytext));
 <INITIAL> "where"     => (Tokens.WHERE (yypos, yypos + size yytext));
 
+<INITIAL> "if"        => (Tokens.IF (yypos, yypos + size yytext));
+<INITIAL> "then"      => (Tokens.THEN (yypos, yypos + size yytext));
+<INITIAL> "else"      => (Tokens.ELSE (yypos, yypos + size yytext));
+<INITIAL> "Skip"      => (Tokens.SKIP (yypos, yypos + size yytext));
+
 <INITIAL> "extern"    => (Tokens.EXTERN (yypos, yypos + size yytext));
 <INITIAL> "type"      => (Tokens.TYPE (yypos, yypos + size yytext));
 <INITIAL> "val"       => (Tokens.VAL (yypos, yypos + size yytext));
index 023813a..0a50620 100644 (file)
@@ -59,6 +59,7 @@ signature ENV = sig
                -> string * 'a arg * string * 'b arg * string * 'c arg
                -> ('a * 'b * 'c -> unit) -> action
 
+    val noneV : string -> (env_vars -> unit) -> action
     val oneV : string -> string * 'a arg -> (env_vars * 'a -> unit) -> action
     val twoV : string -> string * 'a arg * string * 'b arg -> (env_vars * 'a * 'b -> unit) -> action
 
@@ -80,6 +81,7 @@ signature ENV = sig
     val container_none : string -> (unit -> unit) * (unit -> unit) -> unit
     val container_one : string -> string * 'a arg -> ('a -> unit) * (unit -> unit) -> unit
 
+    val containerV_none : string -> (env_vars -> unit) * (unit -> unit) -> unit
     val containerV_one : string -> string * 'a arg -> (env_vars * 'a -> unit) * (unit -> unit) -> unit
 
     val registerFunction : string * (Ast.exp list -> Ast.exp option) -> unit
index edb1ffd..ef710c1 100644 (file)
@@ -149,6 +149,10 @@ fun three func (name1, arg1, name2, arg2, name3, arg3) f (_, [e1, e2, e3]) =
                                         SM.empty))
   | three func _ _ (_, es) = badArgs (func, es)
 
+fun noneV func f (evs, []) = (f evs;
+                             SM.empty)
+  | noneV func _ (_, es) = badArgs (func, es)
+
 fun oneV func (name, arg) f (evs, [e]) =
     (case arg e of
         NONE => badArg (func, name, e)
@@ -193,6 +197,7 @@ fun actionV_two name args f = registerAction (name, twoV name args f)
 fun container_none name (f, g) = registerContainer (name, none name f, g)
 fun container_one name args (f, g) = registerContainer (name, one name args f, g)
 
+fun containerV_none name (f, g) = registerContainer (name, noneV name f, g)
 fun containerV_one name args (f, g) = registerContainer (name, oneV name args f, g)
 
 type env = SS.set * (typ * exp option) SM.map * SS.set
index 5404f2e..12dbb6e 100644 (file)
@@ -16,6 +16,7 @@ signature ERRORMSG =
     val linePos : int list ref
 
     val error : (int * int) option -> string -> unit
+    val warning : (int * int) option -> string -> unit
 
     val dummyLoc : int * int
 
index 25b7161..5c04589 100644 (file)
@@ -6,6 +6,7 @@ structure ErrorMsg :> ERRORMSG =
   struct
     (* Initial values of compiler state variables *)
     val anyErrors = ref false
+    val anyWarnings = ref false
     val errorText = ref ""
     val fileName = ref ""
     val lineNum = ref 1
@@ -17,14 +18,14 @@ structure ErrorMsg :> ERRORMSG =
 
     (* Reset compiler to initial state *)
     fun reset() = (anyErrors:=false;
+                  anyWarnings:=false;
                   errorText:="";
                    fileName:="";
                    lineNum:=1;
                    linePos:=[1];
                    sourceStream:=TextIO.stdIn)
 
-    (* Print the given error message *)
-    fun error posopt (msg:string) =
+    fun notify f prefix posopt (msg:string) =
       let
         val (startpos, endpos) = Option.getOpt (posopt, (0, 0))
         fun look(pos,a::rest,n) =
@@ -34,13 +35,16 @@ structure ErrorMsg :> ERRORMSG =
           else look(pos,rest,n-1)
           | look _ = print "0.0"
       in
-        anyErrors := true;
+        f ();
         print (!fileName); print ":";
         look(startpos, !linePos, !lineNum);
         if startpos=endpos then () else (print "-"; look(endpos, !linePos, !lineNum));
-        app print [":error: ", msg, "\n"]
+        app print [":", prefix, ": ", msg, "\n"]
       end
 
+    val error = notify (fn () => anyErrors := true) "error"
+    val warning = notify (fn () => anyWarnings := true) "warning"
+
       val dummyLoc = (0, 0)
 
       exception Error
index 1fec487..c41f796 100644 (file)
@@ -60,7 +60,12 @@ fun exec' evs (eAll as (e, _)) =
     case e of
        ESkip => SM.empty
       | ESet (ev, e) => SM.insert (SM.empty, ev, e)
-      | EGet (x, _, ev, e) => exec' evs (Reduce.subst x (lookup (evs, ev)) e)
+      | EGet (x, _, ev, e) =>
+       let
+           val e' = Reduce.subst x (lookup (evs, ev)) e
+       in
+           exec' evs (Reduce.reduceExp Env.empty e')
+       end
       | ESeq es =>
        let
            val (new, _) =
index feaab16..9bc87cd 100644 (file)
@@ -33,7 +33,8 @@ fun check' G fname =
        if !ErrorMsg.anyErrors then
            G
        else
-           Tycheck.checkFile G (Defaults.tInit ()) prog
+           (Option.app (Unused.check G) (#3 prog);
+            Tycheck.checkFile G (Defaults.tInit ()) prog)
     end
 
 fun basis () =
@@ -87,7 +88,8 @@ fun check fname =
                        if !ErrorMsg.anyErrors then
                            raise ErrorMsg.Error
                        else
-                           (G', #3 prog)
+                           (Option.app (Unused.check b) (#3 prog);
+                            (G', #3 prog))
                    end
            end
     end
index 08db062..599c62a 100644 (file)
@@ -120,6 +120,9 @@ fun expNeeded G (e, loc) =
                   empty es
       | ELocal (e1, e2) => unionCTE (expNeeded G e1, expNeeded G e2)
       | EWith (e1, e2) => unionCTE (expNeeded G e1, expNeeded G e2)
+      | EIf (e1, e2, e3) => unionCTE (expNeeded G e1,
+                                     unionCTE (expNeeded G e2,
+                                               expNeeded G e3))
 
 fun declNeeded G (d, _, _) =
     case d of
diff --git a/src/pcre.sig b/src/pcre.sig
new file mode 100644 (file)
index 0000000..695cd01
--- /dev/null
@@ -0,0 +1,25 @@
+(* HCoop Domtool (http://hcoop.sourceforge.net/)
+ * Copyright (c) 2006, Adam Chlipala
+ *
+ * This program 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+(* Perl Compatible Regular Expressions *)
+
+signature PCRE = sig
+
+    val validRegexp : string -> bool
+
+end
diff --git a/src/pcre.sml b/src/pcre.sml
new file mode 100644 (file)
index 0000000..279a5e4
--- /dev/null
@@ -0,0 +1,31 @@
+(* HCoop Domtool (http://hcoop.sourceforge.net/)
+ * Copyright (c) 2006, Adam Chlipala
+ *
+ * This program 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+(* Perl Compatible Regular Expressions *)
+
+structure Pcre :> PCRE = struct
+
+fun validRegexp s =
+    let
+       val buf = ZString.dupML' s
+    in
+       F_PCRE_SML_validRegexp.f' buf <> 0
+       before C.free' buf
+    end
+
+end
index 8ed2da6..b96d57d 100644 (file)
@@ -85,12 +85,10 @@ val _ = Env.type_one "email"
 datatype aliasSource =
         User of string
        | Default
-       | CatchAll
 
 val source = fn (EApp ((EVar "userSource", _), e), _) =>
                Option.map User (Env.string e)
              | (EVar "defaultSource", _) => SOME Default
-             | (EVar "catchAllSource", _) => SOME CatchAll
              | _ => NONE
 
 datatype aliasTarget =
@@ -136,16 +134,11 @@ fun writeSource (env, s, t) =
                       write ": ";
                       writeTarget (write, t);
                       write "\n")
-         | Default => (write "*@";
-                       writeDom ();
-                       write ": ";
-                       writeTarget (write, t);
-                       write "\n")
-         | CatchAll => (writeD "*@";
-                        writeDomD ();
-                        writeD ": ";
-                        writeTarget (writeD, t);
-                        writeD "\n")
+         | Default => (writeD "*@";
+                       writeDomD ();
+                       writeD ": ";
+                       writeTarget (writeD, t);
+                       writeD "\n")
     end
                        
 val _ = Env.actionV_two "aliasPrim"
index 7783d0d..300ffa4 100644 (file)
@@ -86,13 +86,18 @@ val _ = Env.type_one "suexec_flag"
        Env.bool
        (fn b => b orelse Domain.hasPriv "www")
 
+val _ = Env.type_one "regexp"
+       Env.string
+       Pcre.validRegexp
+
 fun validLocation s =
     size s > 0 andalso size s < 1000 andalso CharVector.all
                                                 (fn ch => Char.isAlphaNum ch
                                                           orelse ch = #"-"
                                                           orelse ch = #"_"
                                                           orelse ch = #"."
-                                                          orelse ch = #"/") s
+                                                          orelse ch = #"/"
+                                                          orelse ch = #"~") s
 
 val _ = Env.type_one "location"
        Env.string
@@ -111,6 +116,15 @@ fun ssl e = case e of
              | (EApp ((EVar "use_cert", _), s), _) => Option.map SOME (Env.string s)
              | _ => NONE
 
+fun validExtension s =
+    size s > 0
+    andalso size s < 20
+    andalso CharVector.all (fn ch => Char.isAlphaNum ch orelse ch = #"_") s
+
+val _ = Env.type_one "file_extension"
+       Env.string
+       validExtension
+
 val defaults = [("WebPlaces",
                 (TList (TBase "web_place", dl), dl),
                 (fn () => (EList (map webPlaceDefault Config.Apache.webNodes_default), dl))),
@@ -178,6 +192,7 @@ val cond_flag = fn (EVar "cond_nocase", _) => SOME "NC"
 val apache_option = fn (EVar "execCGI", _) => SOME "ExecCGI"
                     | (EVar "includesNOEXEC", _) => SOME "IncludesNOEXEC"
                     | (EVar "indexes", _) => SOME "Indexes"
+                    | (EVar "followSymLinks", _) => SOME "FollowSymLinks"
                     | _ => NONE
 
 val autoindex_width = fn (EVar "autofit", _) => SOME "*"
@@ -421,104 +436,110 @@ fun registerAliaser f =
        aliaser := (fn x => (old x; f x))
     end
 
-val () = Env.containerV_one "vhost"
-        ("host", Env.string)
-        (fn (env, host) =>
-            let
-                val places = Env.env (Env.list webPlace) (env, "WebPlaces")
-
-                val ssl = Env.env ssl (env, "SSL")
-                val user = Env.env Env.string (env, "User")
-                val group = Env.env Env.string (env, "Group")
-                val docroot = Env.env Env.string (env, "DocumentRoot")
-                val sadmin = Env.env Env.string (env, "ServerAdmin")
-                val suexec = Env.env Env.bool (env, "SuExec")
-
-                val fullHost = host ^ "." ^ Domain.currentDomain ()
-                val vhostId = fullHost ^ (if Option.isSome ssl then ".ssl" else "")
-                val confFile = fullHost ^ (if Option.isSome ssl then ".vhost_ssl" else ".vhost")
-            in
-                currentVhost := fullHost;
-                currentVhostId := vhostId;
-                sslEnabled := Option.isSome ssl;
-
-                rewriteEnabled := false;
-                localRewriteEnabled := false;
-                vhostFiles := map (fn (node, ip) =>
-                                      let
-                                          val file = Domain.domainFile {node = node,
-                                                                        name = confFile}
+fun vhostPost () = (!post ();
+                   write "</VirtualHost>\n";
+                   app (TextIO.closeOut o #2) (!vhostFiles))
 
-                                          val ld = logDir {user = user, node = node, vhostId = vhostId}
-                                      in
-                                          TextIO.output (file, "# Owner: ");
+fun vhostBody (env, makeFullHost) =
+    let
+       val places = Env.env (Env.list webPlace) (env, "WebPlaces")
+
+       val ssl = Env.env ssl (env, "SSL")
+       val user = Env.env Env.string (env, "User")
+       val group = Env.env Env.string (env, "Group")
+       val docroot = Env.env Env.string (env, "DocumentRoot")
+       val sadmin = Env.env Env.string (env, "ServerAdmin")
+       val suexec = Env.env Env.bool (env, "SuExec")
+
+       val fullHost = makeFullHost (Domain.currentDomain ())
+       val vhostId = fullHost ^ (if Option.isSome ssl then ".ssl" else "")
+       val confFile = fullHost ^ (if Option.isSome ssl then ".vhost_ssl" else ".vhost")
+    in
+       currentVhost := fullHost;
+       currentVhostId := vhostId;
+       sslEnabled := Option.isSome ssl;
+
+       rewriteEnabled := false;
+       localRewriteEnabled := false;
+       vhostFiles := map (fn (node, ip) =>
+                             let
+                                 val file = Domain.domainFile {node = node,
+                                                               name = confFile}
+
+                                 val ld = logDir {user = user, node = node, vhostId = vhostId}
+                             in
+                                 TextIO.output (file, "# Owner: ");
+                                 TextIO.output (file, user);
+                                 TextIO.output (file, "\n<VirtualHost ");
+                                 TextIO.output (file, ip);
+                                 TextIO.output (file, ":");
+                                 TextIO.output (file, case ssl of
+                                                          SOME _ => "443"
+                                                        | NONE => "80");
+                                 TextIO.output (file, ">\n");
+                                 TextIO.output (file, "\tErrorLog ");
+                                 TextIO.output (file, ld);
+                                 TextIO.output (file, "/error.log\n\tCustomLog ");
+                                 TextIO.output (file, ld);
+                                 TextIO.output (file, "/access.log combined\n");
+                                 TextIO.output (file, "\tServerName ");
+                                 TextIO.output (file, fullHost);
+                                 app
+                                     (fn dom => (TextIO.output (file, "\n\tServerAlias ");
+                                                 TextIO.output (file, makeFullHost dom)))
+                                     (Domain.currentAliasDomains ());
+
+                                 if suexec then
+                                     if isVersion1 node then
+                                         (TextIO.output (file, "\n\tUser ");
                                           TextIO.output (file, user);
-                                          TextIO.output (file, "\n<VirtualHost ");
-                                          TextIO.output (file, ip);
-                                          TextIO.output (file, ":");
-                                          TextIO.output (file, case ssl of
-                                                                   SOME _ => "443"
-                                                                 | NONE => "80");
-                                          TextIO.output (file, ">\n");
-                                          TextIO.output (file, "\tErrorLog ");
-                                          TextIO.output (file, ld);
-                                          TextIO.output (file, "/error.log\n\tCustomLog ");
-                                          TextIO.output (file, ld);
-                                          TextIO.output (file, "/access.log combined\n");
-                                          TextIO.output (file, "\tServerName ");
-                                          TextIO.output (file, fullHost);
-                                          app
-                                              (fn dom => (TextIO.output (file, "\n\tServerAlias ");
-                                                          TextIO.output (file, host);
-                                                          TextIO.output (file, ".");
-                                                          TextIO.output (file, dom)))
-                                              (Domain.currentAliasDomains ());
-
-                                          if suexec then
-                                              if isVersion1 node then
-                                                  (TextIO.output (file, "\n\tUser ");
-                                                   TextIO.output (file, user);
-                                                   TextIO.output (file, "\n\tGroup ");
-                                                   TextIO.output (file, group))
-                                              else
-                                                  (TextIO.output (file, "\n\tSuexecUserGroup ");
-                                                   TextIO.output (file, user);
-                                                   TextIO.output (file, " ");
-                                                   TextIO.output (file, group))
-                                          else
-                                              ();
-
-                                          if isWaklog node then
-                                              (TextIO.output (file, "\n\tWaklogEnabled on\n\tWaklogLocationPrincipal ");
-                                               TextIO.output (file, user);
-                                               TextIO.output (file, "/daemon@HCOOP.NET /etc/keytabs/user.daemon/");
-                                               TextIO.output (file, user))
-                                          else
-                                              ();
-
-                                          TextIO.output (file, "\n\tDAVLockDB /var/lock/apache2/dav/");
+                                          TextIO.output (file, "\n\tGroup ");
+                                          TextIO.output (file, group))
+                                     else
+                                         (TextIO.output (file, "\n\tSuexecUserGroup ");
                                           TextIO.output (file, user);
-                                          TextIO.output (file, "/DAVLock");
+                                          TextIO.output (file, " ");
+                                          TextIO.output (file, group))
+                                 else
+                                     ();
+
+                                 if isWaklog node then
+                                     (TextIO.output (file, "\n\tWaklogEnabled on\n\tWaklogLocationPrincipal ");
+                                      TextIO.output (file, user);
+                                      TextIO.output (file, "/daemon@HCOOP.NET /etc/keytabs/user.daemon/");
+                                      TextIO.output (file, user))
+                                 else
+                                     ();
+
+                                 TextIO.output (file, "\n\tDAVLockDB /var/lock/apache2/dav/");
+                                 TextIO.output (file, user);
+                                 TextIO.output (file, "/DAVLock");
+
+                                 (ld, file)
+                             end)
+                         places;
+       write "\n\tDocumentRoot ";
+       write docroot;
+       write "\n\tServerAdmin ";
+       write sadmin;
+       case ssl of
+           SOME cert =>
+           (write "\n\tSSLEngine on\n\tSSLCertificateFile ";
+            write cert)
+         | NONE => ();
+       write "\n";
+       !pre {user = user, nodes = map #1 places, id = vhostId, hostname = fullHost};
+       app (fn dom => !aliaser (makeFullHost dom)) (Domain.currentAliasDomains ())
+    end    
 
-                                          (ld, file)
-                                      end)
-                                  places;
-                write "\n\tDocumentRoot ";
-                write docroot;
-                write "\n\tServerAdmin ";
-                write sadmin;
-                case ssl of
-                    SOME cert =>
-                    (write "\n\tSSLEngine on\n\tSSLCertificateFile ";
-                     write cert)
-                  | NONE => ();
-                write "\n";
-                !pre {user = user, nodes = map #1 places, id = vhostId, hostname = fullHost};
-                app (fn dom => !aliaser (host ^ "." ^ dom)) (Domain.currentAliasDomains ())
-            end,
-         fn () => (!post ();
-                   write "</VirtualHost>\n";
-                   app (TextIO.closeOut o #2) (!vhostFiles)))
+val () = Env.containerV_one "vhost"
+        ("host", Env.string)
+        (fn (env, host) => vhostBody (env, fn dom => host ^ "." ^ dom),
+         vhostPost)
+
+val () = Env.containerV_none "vhostDefault"
+        (fn env => vhostBody (env, fn dom => dom),
+         vhostPost)
 
 val inLocal = ref false
 
@@ -705,6 +726,12 @@ val () = Env.action_one "unset_options"
                       app (fn opt => (write " -"; write opt)) opts;
                       write "\n"))
 
+val () = Env.action_one "cgiExtension"
+        ("extension", Env.string)
+        (fn ext => (write "\tAddHandler cgi-script ";
+                    write ext;
+                    write "\n"))
+
 val () = Env.action_one "directoryIndex"
         ("filenames", Env.list Env.string)
         (fn opts =>
@@ -945,6 +972,12 @@ val () = Env.action_two "setEnv"
                                                        | ch => str ch) value);
                              write "\"\n"))
 
+val () = Env.action_one "diskCache"
+        ("path", Env.string)
+        (fn path => (write "\tCacheEnable disk \"";
+                     write path;
+                     write "\"\n"))
+        
 val () = Domain.registerResetLocal (fn () =>
                                       ignore (OS.Process.system (Config.rm ^ " -rf /var/domtool/vhosts/*")))
 
index 20c3c10..89b3938 100644 (file)
@@ -50,6 +50,8 @@ datatype dns_record =
        | MX of int * string
        | NS of string
        | DefaultA of string
+       | AAAA of string * string
+       | DefaultAAAA of string
 
 val record = fn (EApp ((EApp ((EVar "dnsA", _), e1), _), e2), _) =>
                (case (Env.string e1, Domain.ip e2) of
@@ -66,7 +68,13 @@ val record = fn (EApp ((EApp ((EVar "dnsA", _), e1), _), e2), _) =>
              | (EApp ((EVar "dnsNS", _), e), _) =>
                Option.map NS (Env.string e)
              | (EApp ((EVar "dnsDefaultA", _), e), _) =>
-               Option.map DefaultA (Env.string e)
+               Option.map DefaultA (Domain.ip e)
+             | (EApp ((EApp ((EVar "dnsAAAA", _), e1), _), e2), _) =>
+               (case (Env.string e1, Env.string e2) of
+                    (SOME v1, SOME v2) => SOME (AAAA (v1, v2))
+                  | _ => NONE)
+             | (EApp ((EVar "dnsDefaultAAAA", _), e), _) =>
+               Option.map DefaultAAAA (Env.string e)
              | _ => NONE
 
 fun writeRecord (evs, r) =
@@ -116,6 +124,20 @@ fun writeRecord (evs, r) =
                            write "\tIN\tNS\t";
                            write host;
                            write ".\n")
+             | AAAA (from, to) => (write from;
+                                   write ".";
+                                   writeDom ();
+                                   write ".\t";
+                                   write (Int.toString ttl);
+                                   write "\tIN\tAAAA\t";
+                                   write to;
+                                   write "\n")
+             | DefaultAAAA to => (writeDom ();
+                                  write ".\t";
+                                  write (Int.toString ttl);
+                                  write "\tIN\tAAAA\t";
+                                  write to;
+                                  write "\n")
        end
 
 val () = Env.actionV_one "dns"
index 5d67086..49363b1 100755 (executable)
@@ -35,7 +35,7 @@ case $1 in
                chmod 770 $DIR/$DBNAME
                ln -sf $DIR/$DBNAME /var/lib/mysql/$DBNAME
                fs setacl -dir $DIR/$DBNAME/ -acl system:mysql all
-               sudo -H mysql -e "GRANT CREATE,SELECT,INSERT,UPDATE,DELETE,INDEX,ALTER,CREATE VIEW,SHOW VIEW,LOCK TABLES,GRANT OPTION ON TABLE * TO '$USERNAME'@$WHERE;" $DBNAME
+               sudo -H mysql -e "GRANT CREATE,CREATE TEMPORARY TABLES,SELECT,INSERT,UPDATE,DELETE,INDEX,ALTER,CREATE VIEW,SHOW VIEW,LOCK TABLES,GRANT OPTION ON TABLE * TO '$USERNAME'@$WHERE;" $DBNAME
 
                sudo -H mysql -e "FLUSH PRIVILEGES;"
        ;;
index 24aa83f..70a3601 100644 (file)
@@ -30,4 +30,12 @@ val _ = Defaults.registerDefault ("WWW",
                                            StringMap.empty), dl),
                                  (fn () => (ESkip, dl)))
 
+val _ = Defaults.registerDefault ("CreateWWW",
+                                 (TBase "bool", dl),
+                                 (fn () => (EVar "true", dl)))
+
+val _ = Defaults.registerDefault ("DefaultAlias",
+                                 (TBase "bool", dl),
+                                 (fn () => (EVar "true", dl)))
+
 end
index 79ba23d..b75f99a 100644 (file)
@@ -8,6 +8,7 @@ $/pp-lib.cm
 $c/internals/c-int.cm
 
 ../openssl/smlnj/FFI/libssl.h.cm
+../pcre/smlnj/FFI/libpcre.h.cm
 
 compat.sig
 compat_smlnj.sml
index 026d357..71e1e43 100644 (file)
@@ -8,6 +8,7 @@ $(SML_LIB)/smlnj-lib/PP/pp-lib.mlb
 $(SML_LIB)/mlnlffi-lib/internals/c-int.mlb
 
 ../openssl/mlton/FFI/libssl.h.mlb
+../pcre/mlton/FFI/libpcre.h.mlb
 
 compat.sig
 
index 05172da..42282aa 100644 (file)
@@ -132,6 +132,9 @@ fun p_exp' pn (e, _) =
                                 keyword "end"]
       | EWith (e1, (ESkip, _)) => dBox [p_exp e1, space 1, keyword "with", space 1, keyword "end"]
       | EWith (e1, e2) => dBox [p_exp e1, space 1, keyword "with", p_exp e2, space 1, keyword "end"]
+      | EIf (e1, e2, e3) => dBox [keyword "if", space 1, p_exp e1,
+                                 space 1, keyword "then", space 1, p_exp e2,
+                                 space 1, keyword "else", space 1, p_exp e3]
 and p_exp e = p_exp' false e
 
 fun p_decl d =
index 525d713..9e538d6 100644 (file)
@@ -42,6 +42,8 @@ fun freeIn x (b, _) =
       | EWith (e1, e2) => freeIn x e1 orelse freeIn x e2
       | EALam (x', _, b') => x <> x' andalso freeIn x b'
 
+      | EIf (e1, e2, e3) => freeIn x e1 orelse freeIn x e2 orelse freeIn x e3
+
 local
     val freshCount = ref 0
 in
@@ -106,6 +108,8 @@ fun subst x e (bAll as (b, loc)) =
        else
            (EALam (x', p, subst x e b'), loc)
 
+      | EIf (b1, b2, b3) => (EIf (subst x e b1, subst x e b2, subst x e b3), loc)
+
 fun findPrim (e, _) =
     case e of
        EApp (f, x) =>
@@ -179,4 +183,16 @@ fun reduceExp G (eAll as (e, loc)) =
            (EALam (x, p, reduceExp G' e), loc)
        end
 
+      | EIf (e1, e2, e3) =>
+       let
+           val e1' = reduceExp G e1
+           fun e2' () = reduceExp G e2
+           fun e3' () = reduceExp G e3
+       in
+           case e1' of
+               (EVar "true", _) => e2' ()
+             | (EVar "false", _) => e3' ()
+             | _ => (EIf (e1', e2' (), e3' ()), loc)
+       end
+
 end
index 4e4a836..1538dd4 100644 (file)
@@ -51,6 +51,9 @@ slave.sml
 defaults.sig
 defaults.sml
 
+pcre.sig
+pcre.sml
+
 openssl.sig
 openssl.sml
 
@@ -133,5 +136,8 @@ htmlPrint.sml
 autodoc.sig
 autodoc.sml
 
+unused.sig
+unused.sml
+
 main.sig
 main.sml
index def1463..d83a5c0 100644 (file)
@@ -258,10 +258,7 @@ fun envVarSetFrom v (e, _) =
 
       | _ => NONE
 
-fun ununify (tAll as (t, _)) =
-    case t of
-       TUnif (_, ref (SOME t)) => ununify t
-      | _ => tAll
+val ununify = Describe.ununify
 
 fun checkExp G (eAll as (e, loc)) =
     let
@@ -571,6 +568,32 @@ fun checkExp G (eAll as (e, loc)) =
 
          | ESkip => (TAction ((CPrefix (CRoot, loc), loc),
                               SM.empty, SM.empty), loc)
+
+         | EIf (e1, e2, e3) =>
+           let
+               val t1 = checkExp G e1
+               val t2 = checkExp G e2
+               val t3 = checkExp G e3
+               val bool = (TBase "bool", loc)
+           in
+               (subTyp (t1, bool))
+               handle Unify ue =>
+                      dte (WrongType ("\"if\" test",
+                                      e1,
+                                      t1,
+                                      bool,
+                                      SOME ue));
+               (subTyp (t2, t3); t3)
+                      handle Unify _ =>
+                             ((subTyp (t3, t2); t2)
+                              handle Unify ue =>
+                                     (dte (WrongType ("\"else\" case",
+                                                      eAll,
+                                                      t3,
+                                                      t2,
+                                                      SOME ue));
+                                      (TError, loc)))
+           end
     end
 
 exception Ununif
diff --git a/src/unused.sig b/src/unused.sig
new file mode 100644 (file)
index 0000000..f1a5180
--- /dev/null
@@ -0,0 +1,25 @@
+(* HCoop Domtool (http://hcoop.sourceforge.net/)
+ * Copyright (c) 2007, Adam Chlipala
+ *
+ * This program 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+(* Domtool configuration language unused environment variable setting analysis *)
+
+signature UNUSED = sig
+
+    val check : Env.env -> Ast.exp -> unit
+
+end
diff --git a/src/unused.sml b/src/unused.sml
new file mode 100644 (file)
index 0000000..ad8c1c4
--- /dev/null
@@ -0,0 +1,112 @@
+(* HCoop Domtool (http://hcoop.sourceforge.net/)
+ * Copyright (c) 2007, Adam Chlipala
+ *
+ * This program 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *)
+
+(* Domtool configuration language unused environment variable setting analysis *)
+
+structure Unused :> UNUSED = struct
+
+open Ast
+structure SM = StringMap
+structure SS = StringSet
+
+fun check G e =
+    let
+       fun used vars x =
+           (#1 (SM.remove (vars, x)))
+           handle NotFound => vars
+
+       fun unused loc x =
+           ErrorMsg.warning (SOME loc) ("Unused setting of environment variable " ^ x)
+
+       fun writing vars x loc =
+           (Option.app (fn loc' => unused loc' x) (SM.find (vars, x));
+            SM.insert (vars, x, loc))
+
+       fun findHead (e, _) =
+           case e of
+               EVar x => SOME x
+             | EApp (e, _) => findHead e
+             | _ => NONE
+
+       fun processTy f default loc t =
+           case #1 (Describe.ununify t) of
+               TArrow (_, t) => processTy f default loc t
+             | TNested (_, t) => processTy f default loc t
+
+             | TAction (_, reads, writes) => f (reads, writes)
+
+             | _ => default
+
+       fun writes (e, _) =
+           case e of
+               ESet (x, _) => SS.singleton x
+             | EGet (_, _, _, e) => writes e
+             | ESeq es => foldl (fn (e, s) => SS.union (writes e, s)) SS.empty es
+             | ELocal (_, e) => writes e
+             | EWith (e, _) => writes e
+             | _ => SS.empty
+
+       fun chk (eAll as (e, loc), vars) =
+           case e of
+               EInt _ => vars
+             | EString _ => vars
+             | EList es => vars
+             | ELam _ => vars
+             | ESkip => vars
+
+             | ESet (x, _) => writing vars x loc
+             | EGet (_, _, x, e) => chk (e, used vars x)
+             | ESeq es => foldl chk vars es
+             | ELocal (e1, e2) =>
+               let
+                   val vars = chk (e2, chk (e1, vars))
+                   val writes1 = writes e1
+                   val writes2 = writes e2
+               in
+                   SM.foldli (fn (x, _, vars') =>
+                                 if SS.member (writes1, x)
+                                    andalso not (SS.member (writes2, x)) then
+                                     SM.insert (vars', x, valOf (SM.find (vars, x)))
+                                 else
+                                     vars') vars vars
+               end
+             | EWith (e1, e2) => chk (e2, chk (e1, vars))
+             | EALam _ => vars
+             | EIf _ => vars
+
+             | _ =>
+               let
+                   val processTy = processTy (fn (reads, writes) =>
+                                                 let
+                                                     val vars = SM.foldli (fn (x, _, vars) => used vars x) vars reads
+                                                 in
+                                                     SM.foldli (fn (x, _, vars) => writing vars x loc) vars writes
+                                                 end)
+                                             vars
+               in
+                   case findHead eAll of
+                       NONE => raise Fail "Couldn't find the head"
+                     | SOME head =>
+                       case Env.lookupVal G head of
+                           NONE => vars
+                         | SOME t => processTy loc t
+               end
+    in
+       SM.appi (fn (x, loc) => unused loc x) (chk (e, SM.empty))
+    end
+end