OSDN Git Service

refresh.
authorvisor <visor@users.sourceforge.jp>
Mon, 19 Oct 2009 14:13:21 +0000 (23:13 +0900)
committervisor <visor@users.sourceforge.jp>
Mon, 19 Oct 2009 14:13:21 +0000 (23:13 +0900)
142 files changed:
Makefile [new file with mode: 0644]
Makefile.conf [new file with mode: 0644]
bin/MKID.pl [new file with mode: 0755]
bin/MKTABLE.pl [new file with mode: 0755]
bin/MKWTABLE.pl [new file with mode: 0755]
cgi/Makefile [new file with mode: 0644]
cgi/main.cc [new file with mode: 0644]
cgi_debug/Makefile [new file with mode: 0644]
config.h [new file with mode: 0644]
ext/diff-openbsd/diff.c [new file with mode: 0644]
ext/diff-openbsd/diff.h [new file with mode: 0644]
ext/diff-openbsd/diffreg.c [new file with mode: 0644]
ext/diff.cc [new file with mode: 0644]
ext/diff.h [new file with mode: 0644]
ext/ml-diff.cc [new file with mode: 0644]
ext/ml-diff.h [new file with mode: 0644]
lib/app.cc [new file with mode: 0644]
lib/app.h [new file with mode: 0644]
lib/bdbmacro.h [new file with mode: 0644]
lib/expr.cc [new file with mode: 0644]
lib/expr.h [new file with mode: 0644]
lib/filemacro.h [new file with mode: 0644]
lib/form.cc [new file with mode: 0644]
lib/form.h [new file with mode: 0644]
lib/form_utf8-jp.cc [new file with mode: 0644]
lib/form_utf8-jp.h [new file with mode: 0644]
lib/form_utf8.cc [new file with mode: 0644]
lib/form_utf8.h [new file with mode: 0644]
lib/formfile.cc [new file with mode: 0644]
lib/formfile.h [new file with mode: 0644]
lib/ftable.h [new file with mode: 0644]
lib/heapdebug.cc [new file with mode: 0644]
lib/heapdebug.h [new file with mode: 0644]
lib/http.cc [new file with mode: 0644]
lib/http.h [new file with mode: 0644]
lib/httpconst.h [new file with mode: 0644]
lib/iso2022jp.cc [new file with mode: 0644]
lib/iso2022jp.h [new file with mode: 0644]
lib/mftable.h [new file with mode: 0644]
lib/ml.cc [new file with mode: 0644]
lib/ml.h [new file with mode: 0644]
lib/mlenv.cc [new file with mode: 0644]
lib/mlenv.h [new file with mode: 0644]
lib/motor.cc [new file with mode: 0644]
lib/motor.h [new file with mode: 0644]
lib/motorconst.h [new file with mode: 0644]
lib/motorenv.cc [new file with mode: 0644]
lib/motorenv.h [new file with mode: 0644]
lib/motorfunc.cc [new file with mode: 0644]
lib/motorfunc.h [new file with mode: 0644]
lib/motoroutput-jp.cc [new file with mode: 0644]
lib/motoroutput-jp.h [new file with mode: 0644]
lib/motoroutput.cc [new file with mode: 0644]
lib/motoroutput.h [new file with mode: 0644]
lib/motorvar.cc [new file with mode: 0644]
lib/motorvar.h [new file with mode: 0644]
lib/sigsafe.cc [new file with mode: 0644]
lib/sigsafe.h [new file with mode: 0644]
lib/spair.h [new file with mode: 0644]
lib/ustring.h [new file with mode: 0644]
lib/utf16.cc [new file with mode: 0644]
lib/utf16.h [new file with mode: 0644]
lib/utf8-jp.cc [new file with mode: 0644]
lib/utf8-jp.h [new file with mode: 0644]
lib/utf8.cc [new file with mode: 0644]
lib/utf8.h [new file with mode: 0644]
lib/util_apache.cc [new file with mode: 0644]
lib/util_apache.h [new file with mode: 0644]
lib/util_check.cc [new file with mode: 0644]
lib/util_check.h [new file with mode: 0644]
lib/util_const.cc [new file with mode: 0644]
lib/util_const.h [new file with mode: 0644]
lib/util_file.cc [new file with mode: 0644]
lib/util_file.h [new file with mode: 0644]
lib/util_inet.cc [new file with mode: 0644]
lib/util_inet.h [new file with mode: 0644]
lib/util_mimetype.cc [new file with mode: 0644]
lib/util_mimetype.h [new file with mode: 0644]
lib/util_proc.cc [new file with mode: 0644]
lib/util_proc.h [new file with mode: 0644]
lib/util_random.cc [new file with mode: 0644]
lib/util_random.h [new file with mode: 0644]
lib/util_string.cc [new file with mode: 0644]
lib/util_string.h [new file with mode: 0644]
lib/util_time.cc [new file with mode: 0644]
lib/util_time.h [new file with mode: 0644]
ml/Makefile [new file with mode: 0644]
ml/main.cc [new file with mode: 0644]
ml_debug/Makefile [new file with mode: 0644]
modules/ml-addon.cc [new file with mode: 0644]
modules/ml-addon.h [new file with mode: 0644]
modules/ml-apache.cc [new file with mode: 0644]
modules/ml-apache.h [new file with mode: 0644]
modules/ml-bool.cc [new file with mode: 0644]
modules/ml-bool.h [new file with mode: 0644]
modules/ml-cookielogin.cc [new file with mode: 0644]
modules/ml-cookielogin.h [new file with mode: 0644]
modules/ml-db.cc [new file with mode: 0644]
modules/ml-db.h [new file with mode: 0644]
modules/ml-defun.cc [new file with mode: 0644]
modules/ml-defun.h [new file with mode: 0644]
modules/ml-encode.cc [new file with mode: 0644]
modules/ml-encode.h [new file with mode: 0644]
modules/ml-formvar.cc [new file with mode: 0644]
modules/ml-formvar.h [new file with mode: 0644]
modules/ml-include.cc [new file with mode: 0644]
modules/ml-include.h [new file with mode: 0644]
modules/ml-inet.cc [new file with mode: 0644]
modules/ml-inet.h [new file with mode: 0644]
modules/ml-math.cc [new file with mode: 0644]
modules/ml-math.h [new file with mode: 0644]
modules/ml-motor.cc [new file with mode: 0644]
modules/ml-motor.h [new file with mode: 0644]
modules/ml-sendmail.cc [new file with mode: 0644]
modules/ml-sendmail.h [new file with mode: 0644]
modules/ml-sqlite3.cc [new file with mode: 0644]
modules/ml-sqlite3.h [new file with mode: 0644]
modules/ml-store.cc [new file with mode: 0644]
modules/ml-store.h [new file with mode: 0644]
modules/ml-string.cc [new file with mode: 0644]
modules/ml-string.h [new file with mode: 0644]
modules/ml-struct.cc [new file with mode: 0644]
modules/ml-struct.h [new file with mode: 0644]
modules/ml-time.cc [new file with mode: 0644]
modules/ml-time.h [new file with mode: 0644]
modules/ml-variable.cc [new file with mode: 0644]
modules/ml-variable.h [new file with mode: 0644]
modules/ml-wiki.cc [new file with mode: 0644]
modules/ml-wiki.h [new file with mode: 0644]
modules/ml-xml.cc [new file with mode: 0644]
modules/ml-xml.h [new file with mode: 0644]
modules/motor-function.cc [new file with mode: 0644]
modules/motor-function.h [new file with mode: 0644]
wiki/wikicmd.cc [new file with mode: 0644]
wiki/wikicmd.h [new file with mode: 0644]
wiki/wikienv.cc [new file with mode: 0644]
wiki/wikienv.h [new file with mode: 0644]
wiki/wikiformat.cc [new file with mode: 0644]
wiki/wikiformat.h [new file with mode: 0644]
wiki/wikiline.cc [new file with mode: 0644]
wiki/wikiline.h [new file with mode: 0644]
wiki/wikitable.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..8d5dfb6
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+SUBDIR += ml_debug
+SUBDIR += ml
+SUBDIR += cgi_debug
+SUBDIR += cgi
+
+install: dist
+#      rsync -avH dist/* /usr/web/htcmd/i/
+
+.include <bsd.subdir.mk>
+
+.PHONY: dist
+
+dist: _SUBDIR
diff --git a/Makefile.conf b/Makefile.conf
new file mode 100644 (file)
index 0000000..3b5f8d1
--- /dev/null
@@ -0,0 +1,30 @@
+UTF8JP = 1
+BSD = 1
+
+MSRCS += diff.cc
+MSRCS += ml-db.cc
+MSRCS += ml-diff.cc
+MSRCS += ml-addon.cc
+MSRCS += ml-apache.cc
+MSRCS += ml-bool.cc
+MSRCS += ml-cookielogin.cc
+MSRCS += ml-defun.cc
+MSRCS += ml-encode.cc
+MSRCS += ml-formvar.cc
+MSRCS += ml-include.cc
+MSRCS += ml-inet.cc
+MSRCS += ml-math.cc
+MSRCS += ml-motor.cc
+MSRCS += ml-sendmail.cc
+MSRCS += ml-sqlite3.cc
+MSRCS += ml-store.cc
+MSRCS += ml-string.cc
+MSRCS += ml-struct.cc
+MSRCS += ml-time.cc
+MSRCS += ml-variable.cc
+MSRCS += ml-wiki.cc
+MSRCS += ml-xml.cc
+MSRCS += motor-function.cc
+
+MLDADD += -lsqlite3
+MLDADD += -lexpat
diff --git a/bin/MKID.pl b/bin/MKID.pl
new file mode 100755 (executable)
index 0000000..52a9acb
--- /dev/null
@@ -0,0 +1,64 @@
+#! /usr/bin/perl
+
+my @ID;
+my @SRCS;
+
+open (IN, "../Makefile.conf");
+while (<IN>) {
+    chomp;
+    if (/^MSRCS\s*\+?=\s*(.*\.cc)/) {
+       push (@SRCS, $1);
+    }
+}
+close (IN);
+
+foreach (@SRCS) {
+    if (-f "../ext/$_") {
+       &p ("../ext/$_");
+    } elsif (-f "../modules/$_") {
+       &p ("../modules/$_");
+    } else {
+       die "$_: not found\n";
+    }
+}
+
+open (OUT, "> ml-id.h");
+print OUT "#ifndef ML_ID_H\n";
+print OUT "#define ML_ID_H\n\n";
+my $c = 0;
+foreach (@ID) {
+    print OUT "#define $_      ", (sum ($_) * 389 + $c), "\n";
+    $c ++;
+}
+print OUT "\n#endif /* ML_ID_H */\n";
+close (OUT);
+
+exit;
+
+sub  p {
+    my ($file) = @_;
+    my ($hdr);
+
+    $hdr = $file;
+    $hdr =~ s/\.cc$/.h/;
+
+    open (IN, $hdr);
+    while (<IN>) {
+       chomp;
+       if (/class\s+([a-zA-Z0-9_]+)\s*:\s*(public)\s*MLFunc/) {
+           push (@ID, "c" . $1 . "ID");
+       }
+    }
+    close (IN);
+}
+
+sub  sum {
+    my ($text) = @_;
+    my ($sum);
+
+    foreach (unpack ("C*", $text)) {
+       $sum += $_;
+       $sum &= 0xffff;
+    }
+    $sum;
+}
diff --git a/bin/MKTABLE.pl b/bin/MKTABLE.pl
new file mode 100755 (executable)
index 0000000..499b374
--- /dev/null
@@ -0,0 +1,143 @@
+#! /usr/bin/perl
+
+my @FN;
+my @MD;
+my @SF;
+my @MTFN;
+my @WFN;
+my @HDR;
+my @SRCS;
+
+open (IN, "../Makefile.conf");
+while (<IN>) {
+    chomp;
+    if (/^MSRCS\s*\+?=\s*(.*\.cc)/) {
+       push (@SRCS, $1);
+    }
+}
+close (IN);
+
+foreach (@SRCS) {
+    if (-f "../ext/$_") {
+       &p ("../ext/$_");
+    } elsif (-f "../modules/$_") {
+       &p ("../modules/$_");
+    } else {
+       die "$_: not found\n";
+    }
+}
+
+open (OUT, "> ftable.cc");
+print OUT "#include \"ftable.h\"\n";
+print OUT "#include \"mftable.h\"\n";
+foreach (@HDR) {
+    print OUT "#include \"$_\"\n";
+}
+print OUT "//============================================================\n";
+&ftable;
+print OUT "//============================================================\n";
+&mftable;
+print OUT "//============================================================\n";
+&wikiftable;
+close (OUT);
+
+exit;
+
+sub  p {
+    my ($file) = @_;
+    my ($hdr, @a, $md, $ffn, $fmfn);
+
+    $hdr = $file;
+    $hdr =~ s/\.cc$/.h/;
+    $ffn = $fmfn = 0;
+
+    open (IN, $file);
+    while (<IN>) {
+       chomp;
+       if (/^\/\/\#AFUNC\s+/) {
+           @a = split (/\s+/, $');
+           push (@FN, [$a[0], $a[1]]);
+           $ffn = 1;
+       } elsif (/^\/\/\#MFUNC\s+/) {
+           @a = split (/\s+/, $');
+           push (@MD, [$a[0], $a[1]]);
+           $md = $a[0];
+           $ffn = 1;
+       } elsif (/^\/\/\#SFUNC\s+/) {
+           @a = split (/\s+/, $');
+           if ($a[2] ne '') {
+               push (@SF, [$a[0], $a[1], $a[2]]);
+           } else {
+               push (@SF, [$a[0], $a[1], $md]);
+           }
+           $ffn = 1;
+       } elsif (/^\/\/\#MTFUNC\s+/) {
+           @a = split (/\s+/, $');
+           push (@MTFN, [$a[0], $a[1]]);
+           $fmfn = 1;
+       } elsif (/^\/\/\#WIKIFUNC\s+/) {
+           @a = split (/\s+/, $');
+           push (@WFN, [$a[0], $a[1]]);
+           $ffn = 1;
+       }
+    }
+    close (IN);
+
+    if (-f $hdr) {
+       if ($ffn || $fmfn) {
+           push (@HDR, $hdr);
+       }
+#      if ($fmfn) {
+#          push (@MTHDR, $hdr);
+#      }
+    }
+}
+
+sub  ftable {
+
+    print OUT "static FTableSupport::ftable_t  ftable[] = {\n";
+    foreach (@FN) {
+       print OUT "    {CharConst (\"$_->[0]\"), NULL, 0, $_->[1]},\n";
+    }
+    print OUT "    {NULL, 0, NULL, 0, NULL},\n";
+    print OUT "};\n";
+
+    print OUT "static FTableSupport::ftable_t  mtable[] = {\n";
+    foreach (@MD) {
+       print OUT "    {CharConst (\"$_->[0]\"), NULL, 0, $_->[1]},\n";
+    }
+    print OUT "    {NULL, 0, NULL, 0, NULL},\n";
+    print OUT "};\n";
+
+    print OUT "static FTableSupport::ftable_t  stable[] = {\n";
+    foreach (@SF) {
+       print OUT "    {CharConst (\"$_->[0]\"), CharConst (\"$_->[2]\"), $_->[1]},\n";
+    }
+    print OUT "    {NULL, 0, NULL, 0, NULL},\n";
+    print OUT "};\n";
+    print OUT "FTable  GFTable (ftable);\nMTable  GMTable (mtable, stable);\n";
+
+}
+
+sub  mftable {
+
+    print OUT "static MFTableSupport::mftable_t  mftable[] = {\n";
+    foreach (@MTFN) {
+       print OUT "    {CharConst (\"$_->[0]\"), $_->[1]},\n";
+    }
+    print OUT "    {NULL, 0, NULL},\n";
+    print OUT "};\n";
+    print OUT "MFTable  IMFTable (mftable);\n";
+
+}
+
+sub  wikiftable {
+    print OUT "static FTableSupport::ftable_t  wikiftable[] = {\n";
+    foreach (@WFN) {
+       print OUT "    {CharConst (\"$_->[0]\"), NULL, 0, $_->[1]},\n";
+    }
+    print OUT "    {NULL, 0, NULL, 0, NULL},\n";
+    print OUT "};\n";
+    print OUT "FTable  GWikiFTable (wikiftable);\n";
+    
+}
diff --git a/bin/MKWTABLE.pl b/bin/MKWTABLE.pl
new file mode 100755 (executable)
index 0000000..7139c82
--- /dev/null
@@ -0,0 +1,125 @@
+#! /usr/bin/perl
+
+my @CMD;
+my @FN;
+my @FN2;
+my @HDR;
+my @SRCS;
+
+opendir (DIR, "../wiki");
+@a = readdir (DIR);
+closedir (DIR);
+foreach (@a) {
+    if (/^wiki.*\.cc$/) {
+       push (@SRCS, "../wiki/$_");
+    }
+}
+
+foreach (@SRCS) {
+    &p ($_);
+}
+
+open (OUT, "> wikitable.cc");
+print OUT "#include \"wikitable.h\"\n";
+foreach (@HDR) {
+    print OUT "#include \"$_\"\n";
+}
+print OUT "//============================================================\n";
+print OUT "static WikiCmdTableSupport::wikicmd_t  wikitable[] = {\n";
+foreach (@CMD) {
+    if ($#$_ == 1) {
+       print OUT "    {CharConst (\"$_->[0]\"), NULL, 0, NULL, 0, $_->[1]},\n";
+    } elsif ($#$_ == 2) {
+       print OUT "    {CharConst (\"$_->[0]\"), NULL, 0, CharConst (\"$_->[1]\"), $_->[2]},\n";
+    } else {
+       print OUT "    {CharConst (\"$_->[0]\"), CharConst (\"$_->[1]\"), CharConst (\"$_->[2]\"), $_->[3]},\n";
+    }
+}
+print OUT "    {NULL, 0, NULL, 0, NULL},\n";
+print OUT "};\n";
+print OUT "WikiCmdTable  GWikiCmdTable (wikitable);\n";
+print OUT "//============================================================\n";
+print OUT "static WikiCmdTableSupport::wikifunc_t  wftable[] = {\n";
+foreach (@FN) {
+    print OUT "    {CharConst (\"$_->[0]\"), $_->[2], (bool(*)())$_->[1]},\n";
+}
+print OUT "    {NULL, 0, WikiFormat::WikiArg1, NULL},\n";
+print OUT "};\n";
+print OUT "static WikiCmdTableSupport::wikifunc_t  wftable2[] = {\n";
+foreach (@FN2) {
+    print OUT "    {CharConst (\"$_->[0]\"), $_->[2], (bool(*)())$_->[1]},\n";
+}
+print OUT "    {NULL, 0, WikiFormat::WikiArg1, NULL},\n";
+print OUT "};\n";
+print OUT "WikiFuncTable  GWikiFuncTable (wftable);\n";
+print OUT "WikiFuncTable  GWikiFuncTable2 (wftable2);\n";
+print OUT "//============================================================\n";
+close (OUT);
+
+exit;
+
+sub  p {
+    my ($file) = @_;
+    my ($hdr, @a, $ffn, $x, $c, $name);
+
+    $hdr = $file;
+    $hdr =~ s/\.cc$/.h/;
+    $ffn = 0;
+
+    open (IN, $file);
+    while (<IN>) {
+       chomp;
+       if (/^\/\/\#WIKICMD\s+/) {
+           @a = split (/\s+/, $');
+           push (@CMD, [@a]);
+           $ffn = 1;
+       } elsif (/^\/\/\#WIKILINE\s+/) {
+           @a = split (/\s+/, $');
+           push (@FN, [$a[0], $a[1]]);
+           $ffn = 1;
+       } elsif (/^\/\/\#WIKILINE2\s+/) {
+           @a = split (/\s+/, $');
+           push (@FN2, [$a[0], $a[1]]);
+           $ffn = 1;
+       } elsif (/^bool\s+(wl_[^ \t\(]+)/) {
+           $name = $1;
+           $c = 0;
+           $x = $_;
+           while ($x !~ /\)/) {
+               chomp ($x .= <IN>);
+           }
+           if ($x =~ /\(\s*ustring/ && &countComma ($x) == 2) {
+               &setFN (\@FN, $name, WikiFormat::WikiArg1);
+               &setFN (\@FN2, $name, WikiFormat::WikiArg1);
+           } elsif ($x =~ /\(\s*std::vector/ && &countComma ($x) == 2) {
+               &setFN (\@FN, $name, WikiFormat::WikiArgM);
+               &setFN (\@FN2, $name, WikiFormat::WikiArgM);
+           } elsif ($x =~ /\(\s*std::vector/ && &countComma ($x) == 3) {
+               &setFN (\@FN, $name, WikiFormat::WikiArgM2);
+               &setFN (\@FN2, $name, WikiFormat::WikiArgM2);
+           }
+       }
+    }
+    close (IN);
+
+    if (-f $hdr) {
+       if ($ffn) {
+           push (@HDR, $hdr);
+       }
+    }
+}
+
+sub  countComma {
+    my ($str) = @_;
+    my (@a) = split (/,/, $str, -1);
+    return ($#a);
+}
+
+sub  setFN {
+    my ($rfn, $name, $val) = @_;
+    foreach (@$rfn) {
+       if ($_->[1] eq $name) {
+           $_->[2] = $val;
+       }
+    }
+}
diff --git a/cgi/Makefile b/cgi/Makefile
new file mode 100644 (file)
index 0000000..b95aed5
--- /dev/null
@@ -0,0 +1,5 @@
+.ifndef PROG
+PROG = ml.cgi
+.endif
+.PATH: ../cgi ../lib ../modules ../wiki ../ext
+.include "../ml/Makefile"
diff --git a/cgi/main.cc b/cgi/main.cc
new file mode 100644 (file)
index 0000000..a9a3503
--- /dev/null
@@ -0,0 +1,265 @@
+#include "config.h"
+#include "motorconst.h"
+#include "heapdebug.h"
+#include "httpconst.h"
+#include "app.h"
+#include "motorenv.h"
+#include "motor.h"
+#ifdef UTF8JP
+#include "form_utf8-jp.h"
+#include "motoroutput-jp.h"
+#else
+#include "form_utf8.h"
+#include "motoroutput.h"
+#endif
+#include "util_check.h"
+#include "util_string.h"
+#include "util_file.h"
+#include "util_mimetype.h"
+#include "ustring.h"
+#include <iostream>
+#include <stdlib.h>
+#include <unistd.h>
+
+using namespace  std;
+
+static ustring  glue1 () {
+    char*  cwd = getcwd (NULL, 0);
+    ustring  p;
+    std::vector<ustring>  a;
+    ustring  pathinfo;
+    ustring  scriptfilename;
+    ustring  scriptname;
+    ustring  pwd;
+    int  i;
+
+    p = getenvString (kPATH_INFO);
+    {
+       uiterator  b = p.begin ();
+       uiterator  e = p.end ();
+       umatch  m;
+       while (usearch (b, e, m, re_slash)) {
+           a.push_back (ustring (b, m[0].first));
+           b = m[0].second;
+       }
+       a.push_back (ustring (b, e));
+    }
+    if (cwd) {
+       scriptfilename = ustring (cwd);
+       free (cwd);
+       scriptfilename.append (CharConst ("/ml"));
+       scriptname = getenvString (kSCRIPT_NAME);
+       pwd = scriptfilename;
+       for (i = 1; i < a.size (); i ++) {
+           scriptfilename.append (uSlash).append (a[i]);
+           scriptname.append (uSlash).append (a[i]);
+           if (isPlainFile (scriptfilename)) {
+               break;
+           } else if (isDirectory (scriptfilename)) {
+           } else {
+               return uEmpty;
+           }
+           pwd = scriptfilename;
+       }
+       for (i ++; i < a.size (); i ++) {
+           pathinfo.append (uSlash).append (a[i]);
+       }
+       setenv (kPATH_INFO, pathinfo.c_str (), 1);
+       setenv (kSCRIPT_FILENAME, scriptfilename.c_str (), 1);
+       setenv (kSCRIPT_NAME, scriptname.c_str (), 1);
+       chdir (pwd.c_str ());
+    } else {
+    }
+    return scriptfilename;
+}
+
+void  glue2 (ustring scriptfilename, AppEnv* aenv, MotorEnv* env) {
+    ustring  hmltext;
+    int  argc = 0;
+    char** argv = NULL;
+
+    int  rc;
+    FileMacro  f;
+    off_t  s;
+    ustring::iterator  b, b0, e;
+    int  st;
+
+#define  MAXARGS       32
+
+    if (scriptfilename.length () == 0)
+       return;
+
+    if (f.openRead (scriptfilename.c_str ())) {
+       s = f.size ();
+       if (s > 2048) {
+           s = 2048;
+       }
+       if (s > 0) {
+           hmltext.resize (s + 1);
+           f.read (&hmltext.at (0), s);
+           hmltext.at (s) = '\0';
+       }
+       f.close ();
+
+       b = hmltext.begin ();
+       e = hmltext.end () - 1;
+       if (s >= 3 && memcmp (&b[0], "\xef\xbb\xbf", 3) == 0) {
+           b += 3;
+       }
+       b0 = b;
+       st = 0;
+       argc = 0;
+       for (; b < e; b ++) {
+           switch (st) {
+           case 0:             // init, space
+               if (isspace (*b)) {
+               } else if (' ' < *b && *b < 0x7f) {
+                   st = 2;
+                   argc ++;
+               } else {        // non printable
+                   *b = 0;
+                   b = e;
+               }
+               break;
+           case 2:             // ascii
+               if (isspace (*b)) {
+                   st = 0;
+                   if (argc == MAXARGS) {
+                       *b = 0;
+                       b = e;
+                   }
+               } else if (' ' < *b && *b < 0x7f) {
+               } else {
+                   *b = 0;
+                   b = e;
+               }
+               break;
+           }
+       }
+       argv = new char* [argc + 1];
+       b = b0;
+       st = 0;
+       argc = 0;
+       for (; b < e; b ++) {
+           switch (st) {
+           case 0:
+               if (isspace (*b)) {
+               } else if (' ' < *b && *b < 0x7f) {
+                   st = 2;
+                   argv[argc] = &b[0];
+                   argc ++;
+               } else {
+                   *b = 0;
+                   b = e;
+               }
+               break;
+           case 2:             // ascii
+               if (isspace (*b)) {
+                   st = 0;
+                   *b = 0;
+                   if (argc == MAXARGS) {
+                       b = e;
+                   }
+               } else if (' ' < *b && *b < 0x7f) {
+               } else {
+                   *b = 0;
+                   b = e;
+               }
+               break;
+           }
+       }
+       argv[argc] = NULL;
+    } else {
+       // open failed
+    }
+
+    if (argv)
+       aenv->readOption (argc, argv, env);
+
+    delete argv; 
+}
+
+int  main (int argc, char** argv) {
+    AppEnv  aenv;
+    CGIFormFile*  form = NULL;
+    HTMLMotor  motor;
+    MotorOutput*  out = NULL;
+    ustring  scriptfilename;
+
+#ifdef kErrorLog
+    close (2);
+    open (cDataTop kErrorLog, O_WRONLY | O_APPEND | O_CREAT, 0666);
+#endif
+
+#ifdef UTF8JP
+    if (checkAgent () & UA_Windows) {
+       form = new CGIFormUTF8JPMS;
+       out = new MotorOutputOStreamJPMS;
+    } else {
+//     form = new CGIFormUTF8;
+       form = new CGIFormUTF8JPMS;
+       out = new MotorOutputOStream;
+    }
+#else
+    form = new CGIFormUTF8;
+    out = new MotorOutputOStream;
+#endif
+    MotorEnv  env (&aenv, form, &motor, out);
+
+    try {
+       ustring  ext;
+
+//     aenv.readOption (argc, argv, &env);
+       scriptfilename = glue1 ();
+       if (! isPlainFile (scriptfilename) || isExecutableFile (scriptfilename)) {
+           env.forbiddenResponse ();
+           goto Ex1;
+       }
+
+       ext = getExt (scriptfilename);
+       if (match (ext, CharConst ("hml"))) {
+       } else if (match (ext, CharConst ("ml"))) {
+           env.noContentResponse ();
+           goto Ex1;
+       } else {
+           ustring  type = mimetype (ext);
+           if (type.empty ()) {
+               // not reached
+               type.assign (CharConst (kMIME_TEXT));
+           }
+           env.outputFile (scriptfilename, type);
+           goto Ex1;
+       }
+
+       glue2 (scriptfilename, &aenv, &env);
+
+#ifdef DEBUG
+       char*  em = getenv (kREQUEST_METHOD);
+       char*  en = getenv (kSCRIPT_NAME);
+       std::cerr << "============================================================\n";
+       if (em) {
+           std::cerr << em << " ";
+           if (en)
+               std::cerr << en;
+           std::cerr << "\n";
+       }
+       aenv.dump (std::cerr);
+       std::cerr << "--\n";
+#endif /* DEBUG */
+       aenv.setDefault ();
+
+       env.setDefault ();
+       env.readFormVar ();
+       env.cacheControl ();
+       env.doML ();
+       env.doMotor ();
+    }
+    catch (ustring& msg) {
+       std::cerr << msg << "\n";
+       goto Ex1;
+    }
+
+ Ex1:;
+    delete out;
+    delete form;
+}
diff --git a/cgi_debug/Makefile b/cgi_debug/Makefile
new file mode 100644 (file)
index 0000000..1f94478
--- /dev/null
@@ -0,0 +1,4 @@
+DEBUG = 1
+HEAPDEBUG = 1
+PROG = ml_debug.cgi
+.include "../cgi/Makefile"
diff --git a/config.h b/config.h
new file mode 100644 (file)
index 0000000..43b6c9b
--- /dev/null
+++ b/config.h
@@ -0,0 +1,19 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define  cDataTop              "/usr/web/htvar/i"
+#define  cDocTop               "/usr/web/htdocs/i"
+//#define  kErrorLog           "/logs/error_log"
+#define  cDEFAULTTIMELIMIT     300
+#define  cLOGSHORTSIZE         120
+#define  cPOSTLIMITDEFAULT     (1024*1024)
+#define  cPOSTLIMITHARD                (10*1024*1024)
+#define  cPOSTFILELIMITDEFAULT (3*1024*1024)
+#define  cPOSTFILELIMITHARD    (500*1024*1024)
+#define  cResourceFileMax      (1024*1024)
+#define  cmd_qmailinject       "/var/qmail/bin/qmail-inject"
+#define  cmd_rm                        "/bin/rm"
+#define  cmd_rmdir             "/bin/rmdir"
+#define  path_devnull          "/dev/null"
+
+#endif /* CONFIG_H */
diff --git a/ext/diff-openbsd/diff.c b/ext/diff-openbsd/diff.c
new file mode 100644 (file)
index 0000000..af77b37
--- /dev/null
@@ -0,0 +1,455 @@
+/*     $OpenBSD: diff.c,v 1.49 2007/03/01 21:48:32 jmc Exp $   */
+
+/*
+ * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+#ifndef lint
+static const char rcsid[] = "$OpenBSD: diff.c,v 1.49 2007/03/01 21:48:32 jmc Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "diff.h"
+
+int     aflag, bflag, dflag, iflag, lflag, Nflag, Pflag, pflag, rflag;
+int     sflag, tflag, Tflag, wflag;
+int     format, context, status;
+char   *start, *ifdefname, *diffargs, *label[2], *ignore_pats;
+struct stat stb1, stb2;
+struct excludes *excludes_list;
+regex_t         ignore_re;
+
+#define        OPTIONS "0123456789abC:cdD:efhI:iL:lnNPpqrS:sTtU:uwX:x:"
+static struct option longopts[] = {
+       { "text",                       no_argument,            0,      'a' },
+       { "ignore-space-change",        no_argument,            0,      'b' },
+       { "context",                    optional_argument,      0,      'C' },
+       { "ifdef",                      required_argument,      0,      'D' },
+       { "minimal",                    no_argument,            0,      'd' },
+       { "ed",                         no_argument,            0,      'e' },
+       { "forward-ed",                 no_argument,            0,      'f' },
+       { "ignore-matching-lines",      required_argument,      0,      'I' },
+       { "ignore-case",                no_argument,            0,      'i' },
+       { "paginate",                   no_argument,            0,      'l' },
+       { "label",                      required_argument,      0,      'L' },
+       { "new-file",                   no_argument,            0,      'N' },
+       { "rcs",                        no_argument,            0,      'n' },
+       { "unidirectional-new-file",    no_argument,            0,      'P' },
+       { "show-c-function",            no_argument,            0,      'p' },
+       { "brief",                      no_argument,            0,      'q' },
+       { "recursive",                  no_argument,            0,      'r' },
+       { "report-identical-files",     no_argument,            0,      's' },
+       { "starting-file",              required_argument,      0,      'S' },
+       { "expand-tabs",                no_argument,            0,      't' },
+       { "initial-tab",                no_argument,            0,      'T' },
+       { "unified",                    optional_argument,      0,      'U' },
+       { "ignore-all-space",           no_argument,            0,      'w' },
+       { "exclude",                    required_argument,      0,      'x' },
+       { "exclude-from",               required_argument,      0,      'X' },
+       { NULL,                         0,                      0,      '\0'}
+};
+
+__dead void usage(void);
+void push_excludes(char *);
+void push_ignore_pats(char *);
+void read_excludes_file(char *file);
+void set_argstr(char **, char **);
+
+int
+main(int argc, char **argv)
+{
+       char *ep, **oargv;
+       long  l;
+       int   ch, lastch, gotstdin, prevoptind, newarg;
+
+       oargv = argv;
+       gotstdin = 0;
+
+       lastch = '\0';
+       prevoptind = 1;
+       newarg = 1;
+       while ((ch = getopt_long(argc, argv, OPTIONS, longopts, NULL)) != -1) {
+               switch (ch) {
+               case '0': case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9':
+                       if (newarg)
+                               usage();        /* disallow -[0-9]+ */
+                       else if (lastch == 'c' || lastch == 'u')
+                               context = 0;
+                       else if (!isdigit(lastch) || context > INT_MAX / 10)
+                               usage();
+                       context = (context * 10) + (ch - '0');
+                       break;
+               case 'a':
+                       aflag = 1;
+                       break;
+               case 'b':
+                       bflag = 1;
+                       break;
+               case 'C':
+               case 'c':
+                       format = D_CONTEXT;
+                       if (optarg != NULL) {
+                               l = strtol(optarg, &ep, 10);
+                               if (*ep != '\0' || l < 0 || l >= INT_MAX)
+                                       usage();
+                               context = (int)l;
+                       } else
+                               context = 3;
+                       break;
+               case 'd':
+                       dflag = 1;
+                       break;
+               case 'D':
+                       format = D_IFDEF;
+                       ifdefname = optarg;
+                       break;
+               case 'e':
+                       format = D_EDIT;
+                       break;
+               case 'f':
+                       format = D_REVERSE;
+                       break;
+               case 'h':
+                       /* silently ignore for backwards compatibility */
+                       break;
+               case 'I':
+                       push_ignore_pats(optarg);
+                       break;
+               case 'i':
+                       iflag = 1;
+                       break;
+               case 'L':
+                       if (label[0] == NULL)
+                               label[0] = optarg;
+                       else if (label[1] == NULL)
+                               label[1] = optarg;
+                       else
+                               usage();
+                       break;
+               case 'l':
+                       lflag = 1;
+                       signal(SIGPIPE, SIG_IGN);
+                       break;
+               case 'N':
+                       Nflag = 1;
+                       break;
+               case 'n':
+                       format = D_NREVERSE;
+                       break;
+               case 'p':
+                       pflag = 1;
+                       break;
+               case 'P':
+                       Pflag = 1;
+                       break;
+               case 'r':
+                       rflag = 1;
+                       break;
+               case 'q':
+                       format = D_BRIEF;
+                       break;
+               case 'S':
+                       start = optarg;
+                       break;
+               case 's':
+                       sflag = 1;
+                       break;
+               case 'T':
+                       Tflag = 1;
+                       break;
+               case 't':
+                       tflag = 1;
+                       break;
+               case 'U':
+               case 'u':
+                       format = D_UNIFIED;
+                       if (optarg != NULL) {
+                               l = strtol(optarg, &ep, 10);
+                               if (*ep != '\0' || l < 0 || l >= INT_MAX)
+                                       usage();
+                               context = (int)l;
+                       } else
+                               context = 3;
+                       break;
+               case 'w':
+                       wflag = 1;
+                       break;
+               case 'X':
+                       read_excludes_file(optarg);
+                       break;
+               case 'x':
+                       push_excludes(optarg);
+                       break;
+               default:
+                       usage();
+                       break;
+               }
+               lastch = ch;
+               newarg = optind != prevoptind;
+               prevoptind = optind;
+       }
+       argc -= optind;
+       argv += optind;
+
+       /*
+        * Do sanity checks, fill in stb1 and stb2 and call the appropriate
+        * driver routine.  Both drivers use the contents of stb1 and stb2.
+        */
+       if (argc != 2)
+               usage();
+       if (ignore_pats != NULL) {
+               char buf[BUFSIZ];
+               int error;
+
+               if ((error = regcomp(&ignore_re, ignore_pats,
+                                    REG_NEWLINE | REG_EXTENDED)) != 0) {
+                       regerror(error, &ignore_re, buf, sizeof(buf));
+                       if (*ignore_pats != '\0')
+                               errx(2, "%s: %s", ignore_pats, buf);
+                       else
+                               errx(2, "%s", buf);
+               }
+       }
+       if (strcmp(argv[0], "-") == 0) {
+               fstat(STDIN_FILENO, &stb1);
+               gotstdin = 1;
+       } else if (stat(argv[0], &stb1) != 0)
+               err(2, "%s", argv[0]);
+       if (strcmp(argv[1], "-") == 0) {
+               fstat(STDIN_FILENO, &stb2);
+               gotstdin = 1;
+       } else if (stat(argv[1], &stb2) != 0)
+               err(2, "%s", argv[1]);
+       if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode)))
+               errx(2, "can't compare - to a directory");
+       set_argstr(oargv, argv);
+       if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) {
+               if (format == D_IFDEF)
+                       errx(2, "-D option not supported with directories");
+               diffdir(argv[0], argv[1]);
+       } else {
+               if (S_ISDIR(stb1.st_mode)) {
+                       argv[0] = splice(argv[0], argv[1]);
+                       if (stat(argv[0], &stb1) < 0)
+                               err(2, "%s", argv[0]);
+               }
+               if (S_ISDIR(stb2.st_mode)) {
+                       argv[1] = splice(argv[1], argv[0]);
+                       if (stat(argv[1], &stb2) < 0)
+                               err(2, "%s", argv[1]);
+               }
+               print_status(diffreg(argv[0], argv[1], 0), argv[0], argv[1],
+                   NULL);
+       }
+       exit(status);
+}
+
+void *
+emalloc(size_t n)
+{
+       void *p;
+
+       if ((p = malloc(n)) == NULL)
+               err(2, NULL);
+       return (p);
+}
+
+void *
+erealloc(void *p, size_t n)
+{
+       void *q;
+
+       if ((q = realloc(p, n)) == NULL)
+               err(2, NULL);
+       return (q);
+}
+
+int
+easprintf(char **ret, const char *fmt, ...)
+{
+       int len;
+       va_list ap;
+
+       va_start(ap, fmt);
+       len = vasprintf(ret, fmt, ap);
+       va_end(ap);
+
+       if (len == -1)
+               err(2, NULL);
+       return (len);
+}
+
+void
+set_argstr(char **av, char **ave)
+{
+       size_t argsize;
+       char **ap;
+
+       argsize = 4 + *ave - *av + 1;
+       diffargs = emalloc(argsize);
+       strlcpy(diffargs, "diff", argsize);
+       for (ap = av + 1; ap < ave; ap++) {
+               if (strcmp(*ap, "--") != 0) {
+                       strlcat(diffargs, " ", argsize);
+                       strlcat(diffargs, *ap, argsize);
+               }
+       }
+}
+
+/*
+ * Read in an excludes file and push each line.
+ */
+void
+read_excludes_file(char *file)
+{
+       FILE *fp;
+       char *buf, *pattern;
+       size_t len;
+
+       if (strcmp(file, "-") == 0)
+               fp = stdin;
+       else if ((fp = fopen(file, "r")) == NULL)
+               err(2, "%s", file);
+       while ((buf = fgetln(fp, &len)) != NULL) {
+               if (buf[len - 1] == '\n')
+                       len--;
+               pattern = emalloc(len + 1);
+               memcpy(pattern, buf, len);
+               pattern[len] = '\0';
+               push_excludes(pattern);
+       }
+       if (strcmp(file, "-") != 0)
+               fclose(fp);
+}
+
+/*
+ * Push a pattern onto the excludes list.
+ */
+void
+push_excludes(char *pattern)
+{
+       struct excludes *entry;
+
+       entry = emalloc(sizeof(*entry));
+       entry->pattern = pattern;
+       entry->next = excludes_list;
+       excludes_list = entry;
+}
+
+void
+push_ignore_pats(char *pattern)
+{
+       size_t len;
+
+       if (ignore_pats == NULL) {
+               /* XXX: estrdup */
+               len = strlen(pattern) + 1;
+               ignore_pats = emalloc(len);
+               strlcpy(ignore_pats, pattern, len);
+       } else {
+               /* old + "|" + new + NUL */
+               len = strlen(ignore_pats) + strlen(pattern) + 2;
+               ignore_pats = erealloc(ignore_pats, len);
+               strlcat(ignore_pats, "|", len);
+               strlcat(ignore_pats, pattern, len);
+       }
+}
+
+void
+print_only(const char *path, size_t dirlen, const char *entry)
+{
+       if (dirlen > 1)
+               dirlen--;
+       printf("Only in %.*s: %s\n", (int)dirlen, path, entry);
+}
+
+void
+print_status(int val, char *path1, char *path2, char *entry)
+{
+       switch (val) {
+       case D_ONLY:
+               print_only(path1, strlen(path1), entry);
+               break;
+       case D_COMMON:
+               printf("Common subdirectories: %s%s and %s%s\n",
+                   path1, entry ? entry : "", path2, entry ? entry : "");
+               break;
+       case D_BINARY:
+               printf("Binary files %s%s and %s%s differ\n",
+                   path1, entry ? entry : "", path2, entry ? entry : "");
+               break;
+       case D_DIFFER:
+               if (format == D_BRIEF)
+                       printf("Files %s%s and %s%s differ\n",
+                           path1, entry ? entry : "",
+                           path2, entry ? entry : "");
+               break;
+       case D_SAME:
+               if (sflag)
+                       printf("Files %s%s and %s%s are identical\n",
+                           path1, entry ? entry : "",
+                           path2, entry ? entry : "");
+               break;
+       case D_MISMATCH1:
+               printf("File %s%s is a directory while file %s%s is a regular file\n",
+                   path1, entry ? entry : "", path2, entry ? entry : "");
+               break;
+       case D_MISMATCH2:
+               printf("File %s%s is a regular file while file %s%s is a directory\n",
+                   path1, entry ? entry : "", path2, entry ? entry : "");
+               break;
+       case D_SKIPPED1:
+               printf("File %s%s is not a regular file or directory and was skipped\n",
+                   path1, entry ? entry : "");
+               break;
+       case D_SKIPPED2:
+               printf("File %s%s is not a regular file or directory and was skipped\n",
+                   path2, entry ? entry : "");
+               break;
+       }
+}
+
+__dead void
+usage(void)
+{
+       (void)fprintf(stderr,
+           "usage: diff [-abdilpqTtw] [-I pattern] [-c | -e | -f | -n | -u]\n"
+           "            [-L label] file1 file2\n"
+           "       diff [-abdilpqTtw] [-I pattern] [-L label] -C number file1 file2\n"
+           "       diff [-abdilqtw] [-I pattern] -D string file1 file2\n"
+           "       diff [-abdilpqTtw] [-I pattern] [-L label] -U number file1 file2\n"
+           "       diff [-abdilNPpqrsTtw] [-I pattern] [-c | -e | -f | -n | -u]\n"
+           "            [-L label] [-S name] [-X file] [-x pattern] dir1 dir2\n");
+
+       exit(2);
+}
diff --git a/ext/diff-openbsd/diff.h b/ext/diff-openbsd/diff.h
new file mode 100644 (file)
index 0000000..817057d
--- /dev/null
@@ -0,0 +1,91 @@
+/*     $OpenBSD: diff.h,v 1.29 2004/12/09 18:56:10 millert Exp $       */
+
+/*-
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)diff.h      8.1 (Berkeley) 6/6/93
+ */
+
+#include <sys/types.h>
+#include <regex.h>
+
+/*
+ * Output format options
+ */
+#define        D_NORMAL        0       /* Normal output */
+#define        D_EDIT          -1      /* Editor script out */
+#define        D_REVERSE       1       /* Reverse editor script */
+#define        D_CONTEXT       2       /* Diff with context */
+#define        D_UNIFIED       3       /* Unified context diff */
+#define        D_IFDEF         4       /* Diff with merged #ifdef's */
+#define        D_NREVERSE      5       /* Reverse ed script with numbered
+                                  lines and no trailing . */
+#define        D_BRIEF         6       /* Say if the files differ */
+
+/*
+ * Output flags
+ */
+#define        D_HEADER        1       /* Print a header/footer between files */
+#define        D_EMPTY1        2       /* Treat first file as empty (/dev/null) */
+#define        D_EMPTY2        4       /* Treat second file as empty (/dev/null) */
+
+/*
+ * Status values for print_status() and diffreg() return values
+ */
+#define        D_SAME          0       /* Files are the same */
+#define        D_DIFFER        1       /* Files are different */
+#define        D_BINARY        2       /* Binary files are different */
+#define        D_COMMON        3       /* Subdirectory common to both dirs */
+#define        D_ONLY          4       /* Only exists in one directory */
+#define        D_MISMATCH1     5       /* path1 was a dir, path2 a file */
+#define        D_MISMATCH2     6       /* path1 was a file, path2 a dir */
+#define        D_ERROR         7       /* An error occurred */
+#define        D_SKIPPED1      8       /* path1 was a special file */
+#define        D_SKIPPED2      9       /* path2 was a special file */
+
+struct excludes {
+       char *pattern;
+       struct excludes *next;
+};
+
+extern int     aflag, bflag, dflag, iflag, lflag, Nflag, Pflag, pflag, rflag,
+               sflag, tflag, Tflag, wflag;
+extern int     format, context, status;
+extern char    *start, *ifdefname, *diffargs, *label[2], *ignore_pats;
+extern struct  stat stb1, stb2;
+extern struct  excludes *excludes_list;
+extern regex_t ignore_re;
+
+char   *splice(char *, char *);
+int    diffreg(char *, char *, int);
+int    easprintf(char **, const char *, ...);
+void   *emalloc(size_t);
+void   *erealloc(void *, size_t);
+void   diffdir(char *, char *);
+void   print_only(const char *, size_t, const char *);
+void   print_status(int, char *, char *, char *);
diff --git a/ext/diff-openbsd/diffreg.c b/ext/diff-openbsd/diffreg.c
new file mode 100644 (file)
index 0000000..7dc19b6
--- /dev/null
@@ -0,0 +1,1549 @@
+/*     $OpenBSD: diffreg.c,v 1.67 2007/03/18 21:12:27 espie Exp $      */
+
+/*
+ * Copyright (C) Caldera International Inc.  2001-2002.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code and documentation must retain the above
+ *    copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed or owned by Caldera
+ *     International, Inc.
+ * 4. Neither the name of Caldera International, Inc. nor the names of other
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*-
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)diffreg.c   8.1 (Berkeley) 6/6/93
+ */
+
+#ifndef lint
+static const char rcsid[] = "$OpenBSD: diffreg.c,v 1.67 2007/03/18 21:12:27 espie Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "diff.h"
+#include "pathnames.h"
+
+/*
+ * diff - compare two files.
+ */
+
+/*
+ *     Uses an algorithm due to Harold Stone, which finds
+ *     a pair of longest identical subsequences in the two
+ *     files.
+ *
+ *     The major goal is to generate the match vector J.
+ *     J[i] is the index of the line in file1 corresponding
+ *     to line i file0. J[i] = 0 if there is no
+ *     such line in file1.
+ *
+ *     Lines are hashed so as to work in core. All potential
+ *     matches are located by sorting the lines of each file
+ *     on the hash (called ``value''). In particular, this
+ *     collects the equivalence classes in file1 together.
+ *     Subroutine equiv replaces the value of each line in
+ *     file0 by the index of the first element of its
+ *     matching equivalence in (the reordered) file1.
+ *     To save space equiv squeezes file1 into a single
+ *     array member in which the equivalence classes
+ *     are simply concatenated, except that their first
+ *     members are flagged by changing sign.
+ *
+ *     Next the indices that point into member are unsorted into
+ *     array class according to the original order of file0.
+ *
+ *     The cleverness lies in routine stone. This marches
+ *     through the lines of file0, developing a vector klist
+ *     of "k-candidates". At step i a k-candidate is a matched
+ *     pair of lines x,y (x in file0 y in file1) such that
+ *     there is a common subsequence of length k
+ *     between the first i lines of file0 and the first y
+ *     lines of file1, but there is no such subsequence for
+ *     any smaller y. x is the earliest possible mate to y
+ *     that occurs in such a subsequence.
+ *
+ *     Whenever any of the members of the equivalence class of
+ *     lines in file1 matable to a line in file0 has serial number
+ *     less than the y of some k-candidate, that k-candidate
+ *     with the smallest such y is replaced. The new
+ *     k-candidate is chained (via pred) to the current
+ *     k-1 candidate so that the actual subsequence can
+ *     be recovered. When a member has serial number greater
+ *     that the y of all k-candidates, the klist is extended.
+ *     At the end, the longest subsequence is pulled out
+ *     and placed in the array J by unravel
+ *
+ *     With J in hand, the matches there recorded are
+ *     check'ed against reality to assure that no spurious
+ *     matches have crept in due to hashing. If they have,
+ *     they are broken, and "jackpot" is recorded--a harmless
+ *     matter except that a true match for a spuriously
+ *     mated line may now be unnecessarily reported as a change.
+ *
+ *     Much of the complexity of the program comes simply
+ *     from trying to minimize core utilization and
+ *     maximize the range of doable problems by dynamically
+ *     allocating what is needed and reusing what is not.
+ *     The core requirements for problems larger than somewhat
+ *     are (in words) 2*length(file0) + length(file1) +
+ *     3*(number of k-candidates installed),  typically about
+ *     6n words for files of length n.
+ */
+
+struct cand {
+       int x;
+       int y;
+       int pred;
+};
+
+struct line {
+       int serial;
+       int value;
+} *file[2];
+
+/*
+ * The following struct is used to record change information when
+ * doing a "context" or "unified" diff.  (see routine "change" to
+ * understand the highly mnemonic field names)
+ */
+struct context_vec {
+       int a;                  /* start line in old file */
+       int b;                  /* end line in old file */
+       int c;                  /* start line in new file */
+       int d;                  /* end line in new file */
+};
+
+static int  *J;                        /* will be overlaid on class */
+static int  *class;            /* will be overlaid on file[0] */
+static int  *klist;            /* will be overlaid on file[0] after class */
+static int  *member;           /* will be overlaid on file[1] */
+static int   clen;
+static int   inifdef;          /* whether or not we are in a #ifdef block */
+static int   len[2];
+static int   pref, suff;       /* length of prefix and suffix */
+static int   slen[2];
+static int   anychange;
+static long *ixnew;            /* will be overlaid on file[1] */
+static long *ixold;            /* will be overlaid on klist */
+static struct cand *clist;     /* merely a free storage pot for candidates */
+static int   clistlen;         /* the length of clist */
+static struct line *sfile[2];  /* shortened by pruning common prefix/suffix */
+static u_char *chrtran;                /* translation table for case-folding */
+static struct context_vec *context_vec_start;
+static struct context_vec *context_vec_end;
+static struct context_vec *context_vec_ptr;
+
+#define FUNCTION_CONTEXT_SIZE  55
+static char lastbuf[FUNCTION_CONTEXT_SIZE];
+static int lastline;
+static int lastmatchline;
+
+static FILE *opentemp(const char *);
+static void output(char *, FILE *, char *, FILE *, int);
+static void check(char *, FILE *, char *, FILE *);
+static void range(int, int, char *);
+static void uni_range(int, int);
+static void dump_context_vec(FILE *, FILE *);
+static void dump_unified_vec(FILE *, FILE *);
+static void prepare(int, FILE *, off_t);
+static void prune(void);
+static void equiv(struct line *, int, struct line *, int, int *);
+static void unravel(int);
+static void unsort(struct line *, int, int *);
+static void change(char *, FILE *, char *, FILE *, int, int, int, int, int *);
+static void sort(struct line *, int);
+static void print_header(const char *, const char *);
+static int  ignoreline(char *);
+static int  asciifile(FILE *);
+static int  fetch(long *, int, int, FILE *, int, int);
+static int  newcand(int, int, int);
+static int  search(int *, int, int);
+static int  skipline(FILE *);
+static int  isqrt(int);
+static int  stone(int *, int, int *, int *);
+static int  readhash(FILE *);
+static int  files_differ(FILE *, FILE *, int);
+static __inline int min(int, int);
+static __inline int max(int, int);
+static char *match_function(const long *, int, FILE *);
+static char *preadline(int, size_t, off_t);
+
+
+/*
+ * chrtran points to one of 2 translation tables: cup2low if folding upper to
+ * lower case clow2low if not folding case
+ */
+u_char clow2low[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+       0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+       0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+       0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+       0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
+       0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
+       0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
+       0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
+       0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
+       0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+       0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+       0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
+       0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+       0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
+       0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+       0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
+       0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
+       0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
+       0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
+       0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+       0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
+       0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
+       0xfd, 0xfe, 0xff
+};
+
+u_char cup2low[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+       0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+       0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+       0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+       0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
+       0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
+       0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+       0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+       0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
+       0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
+       0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+       0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+       0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
+       0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+       0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
+       0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+       0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
+       0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
+       0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
+       0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
+       0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+       0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
+       0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
+       0xfd, 0xfe, 0xff
+};
+
+int
+diffreg(char *ofile1, char *ofile2, int flags)
+{
+       char *file1 = ofile1;
+       char *file2 = ofile2;
+       FILE *f1 = NULL;
+       FILE *f2 = NULL;
+       int rval = D_SAME;
+       int i, ostdout = -1;
+       pid_t pid = -1;
+
+       anychange = 0;
+       lastline = 0;
+       lastmatchline = 0;
+       context_vec_ptr = context_vec_start - 1;
+       chrtran = (iflag ? cup2low : clow2low);
+       if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
+               return (S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2);
+       if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0)
+               goto closem;
+
+       if (flags & D_EMPTY1)
+               f1 = fopen(_PATH_DEVNULL, "r");
+       else {
+               if (!S_ISREG(stb1.st_mode)) {
+                       if ((f1 = opentemp(file1)) == NULL ||
+                           fstat(fileno(f1), &stb1) < 0) {
+                               warn("%s", file1);
+                               status |= 2;
+                               goto closem;
+                       }
+               } else if (strcmp(file1, "-") == 0)
+                       f1 = stdin;
+               else
+                       f1 = fopen(file1, "r");
+       }
+       if (f1 == NULL) {
+               warn("%s", file1);
+               status |= 2;
+               goto closem;
+       }
+
+       if (flags & D_EMPTY2)
+               f2 = fopen(_PATH_DEVNULL, "r");
+       else {
+               if (!S_ISREG(stb2.st_mode)) {
+                       if ((f2 = opentemp(file2)) == NULL ||
+                           fstat(fileno(f2), &stb2) < 0) {
+                               warn("%s", file2);
+                               status |= 2;
+                               goto closem;
+                       }
+               } else if (strcmp(file2, "-") == 0)
+                       f2 = stdin;
+               else
+                       f2 = fopen(file2, "r");
+       }
+       if (f2 == NULL) {
+               warn("%s", file2);
+               status |= 2;
+               goto closem;
+       }
+
+       switch (files_differ(f1, f2, flags)) {
+       case 0:
+               goto closem;
+       case 1:
+               break;
+       default:
+               /* error */
+               status |= 2;
+               goto closem;
+       }
+
+       if (!asciifile(f1) || !asciifile(f2)) {
+               rval = D_BINARY;
+               status |= 1;
+               goto closem;
+       }
+       if (lflag) {
+               /* redirect stdout to pr */
+               int pfd[2];
+               char *header;
+               char *prargv[] = { "pr", "-h", NULL, "-f", NULL };
+
+               easprintf(&header, "%s %s %s", diffargs, file1, file2);
+               prargv[2] = header;
+               fflush(stdout);
+               rewind(stdout);
+               pipe(pfd);
+               switch ((pid = fork())) {
+               case -1:
+                       warnx("No more processes");
+                       status |= 2;
+                       free(header);
+                       return (D_ERROR);
+               case 0:
+                       /* child */
+                       if (pfd[0] != STDIN_FILENO) {
+                               dup2(pfd[0], STDIN_FILENO);
+                               close(pfd[0]);
+                       }
+                       close(pfd[1]);
+                       execv(_PATH_PR, prargv);
+                       _exit(127);
+               default:
+                       /* parent */
+                       if (pfd[1] != STDOUT_FILENO) {
+                               ostdout = dup(STDOUT_FILENO);
+                               dup2(pfd[1], STDOUT_FILENO);
+                               close(pfd[1]);
+                       }
+                       close(pfd[0]);
+                       rewind(stdout);
+                       free(header);
+               }
+       }
+       prepare(0, f1, stb1.st_size);
+       prepare(1, f2, stb2.st_size);
+       prune();
+       sort(sfile[0], slen[0]);
+       sort(sfile[1], slen[1]);
+
+       member = (int *)file[1];
+       equiv(sfile[0], slen[0], sfile[1], slen[1], member);
+       member = erealloc(member, (slen[1] + 2) * sizeof(int));
+
+       class = (int *)file[0];
+       unsort(sfile[0], slen[0], class);
+       class = erealloc(class, (slen[0] + 2) * sizeof(int));
+
+       klist = emalloc((slen[0] + 2) * sizeof(int));
+       clen = 0;
+       clistlen = 100;
+       clist = emalloc(clistlen * sizeof(struct cand));
+       i = stone(class, slen[0], member, klist);
+       free(member);
+       free(class);
+
+       J = erealloc(J, (len[0] + 2) * sizeof(int));
+       unravel(klist[i]);
+       free(clist);
+       free(klist);
+
+       ixold = erealloc(ixold, (len[0] + 2) * sizeof(long));
+       ixnew = erealloc(ixnew, (len[1] + 2) * sizeof(long));
+       check(file1, f1, file2, f2);
+       output(file1, f1, file2, f2, (flags & D_HEADER));
+       if (ostdout != -1) {
+               int wstatus;
+
+               /* close the pipe to pr and restore stdout */
+               fflush(stdout);
+               rewind(stdout);
+               if (ostdout != STDOUT_FILENO) {
+                       close(STDOUT_FILENO);
+                       dup2(ostdout, STDOUT_FILENO);
+                       close(ostdout);
+               }
+               waitpid(pid, &wstatus, 0);
+       }
+closem:
+       if (anychange) {
+               status |= 1;
+               if (rval == D_SAME)
+                       rval = D_DIFFER;
+       }
+       if (f1 != NULL)
+               fclose(f1);
+       if (f2 != NULL)
+               fclose(f2);
+       if (file1 != ofile1)
+               free(file1);
+       if (file2 != ofile2)
+               free(file2);
+       return (rval);
+}
+
+/*
+ * Check to see if the given files differ.
+ * Returns 0 if they are the same, 1 if different, and -1 on error.
+ * XXX - could use code from cmp(1) [faster]
+ */
+static int
+files_differ(FILE *f1, FILE *f2, int flags)
+{
+       char buf1[BUFSIZ], buf2[BUFSIZ];
+       size_t i, j;
+
+       if ((flags & (D_EMPTY1|D_EMPTY2)) || stb1.st_size != stb2.st_size ||
+           (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT))
+               return (1);
+       for (;;) {
+               i = fread(buf1, 1, sizeof(buf1), f1);
+               j = fread(buf2, 1, sizeof(buf2), f2);
+               if (i != j)
+                       return (1);
+               if (i == 0 && j == 0) {
+                       if (ferror(f1) || ferror(f2))
+                               return (1);
+                       return (0);
+               }
+               if (memcmp(buf1, buf2, i) != 0)
+                       return (1);
+       }
+}
+
+static FILE *
+opentemp(const char *file)
+{
+       char buf[BUFSIZ], *tempdir, tempfile[MAXPATHLEN];
+       ssize_t nread;
+       int ifd, ofd;
+
+       if (strcmp(file, "-") == 0)
+               ifd = STDIN_FILENO;
+       else if ((ifd = open(file, O_RDONLY, 0644)) < 0)
+               return (NULL);
+
+       if ((tempdir = getenv("TMPDIR")) == NULL)
+               tempdir = _PATH_TMP;
+       
+       if (strlcpy(tempfile, tempdir, sizeof(tempfile)) >= sizeof(tempfile) ||
+           strlcat(tempfile, "/diff.XXXXXXXX", sizeof(tempfile)) >=
+           sizeof(tempfile)) {
+               close(ifd);
+               errno = ENAMETOOLONG;
+               return (NULL);
+       }
+
+       if ((ofd = mkstemp(tempfile)) < 0)
+               return (NULL);
+       unlink(tempfile);
+       while ((nread = read(ifd, buf, BUFSIZ)) > 0) {
+               if (write(ofd, buf, nread) != nread) {
+                       close(ifd);
+                       close(ofd);
+                       return (NULL);
+               }
+       }
+       close(ifd);
+       lseek(ofd, (off_t)0, SEEK_SET);
+       return (fdopen(ofd, "r"));
+}
+
+char *
+splice(char *dir, char *file)
+{
+       char *tail, *buf;
+
+       if ((tail = strrchr(file, '/')) == NULL)
+               tail = file;
+       else
+               tail++;
+       easprintf(&buf, "%s/%s", dir, tail);
+       return (buf);
+}
+
+static void
+prepare(int i, FILE *fd, off_t filesize)
+{
+       struct line *p;
+       int j, h;
+       size_t sz;
+
+       rewind(fd);
+
+       sz = (filesize <= SIZE_MAX ? filesize : SIZE_MAX) / 25;
+       if (sz < 100)
+               sz = 100;
+
+       p = emalloc((sz + 3) * sizeof(struct line));
+       for (j = 0; (h = readhash(fd));) {
+               if (j == sz) {
+                       sz = sz * 3 / 2;
+                       p = erealloc(p, (sz + 3) * sizeof(struct line));
+               }
+               p[++j].value = h;
+       }
+       len[i] = j;
+       file[i] = p;
+}
+
+static void
+prune(void)
+{
+       int i, j;
+
+       for (pref = 0; pref < len[0] && pref < len[1] &&
+           file[0][pref + 1].value == file[1][pref + 1].value;
+           pref++)
+               ;
+       for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
+           file[0][len[0] - suff].value == file[1][len[1] - suff].value;
+           suff++)
+               ;
+       for (j = 0; j < 2; j++) {
+               sfile[j] = file[j] + pref;
+               slen[j] = len[j] - pref - suff;
+               for (i = 0; i <= slen[j]; i++)
+                       sfile[j][i].serial = i;
+       }
+}
+
+static void
+equiv(struct line *a, int n, struct line *b, int m, int *c)
+{
+       int i, j;
+
+       i = j = 1;
+       while (i <= n && j <= m) {
+               if (a[i].value < b[j].value)
+                       a[i++].value = 0;
+               else if (a[i].value == b[j].value)
+                       a[i++].value = j;
+               else
+                       j++;
+       }
+       while (i <= n)
+               a[i++].value = 0;
+       b[m + 1].value = 0;
+       j = 0;
+       while (++j <= m) {
+               c[j] = -b[j].serial;
+               while (b[j + 1].value == b[j].value) {
+                       j++;
+                       c[j] = b[j].serial;
+               }
+       }
+       c[j] = -1;
+}
+
+/* Code taken from ping.c */
+static int
+isqrt(int n)
+{
+       int y, x = 1;
+
+       if (n == 0)
+               return(0);
+
+       do { /* newton was a stinker */
+               y = x;
+               x = n / x;
+               x += y;
+               x /= 2;
+       } while ((x - y) > 1 || (x - y) < -1);
+
+       return (x);
+}
+
+static int
+stone(int *a, int n, int *b, int *c)
+{
+       int i, k, y, j, l;
+       int oldc, tc, oldl;
+       u_int numtries;
+
+       const u_int bound = dflag ? UINT_MAX : max(256, isqrt(n));
+
+       k = 0;
+       c[0] = newcand(0, 0, 0);
+       for (i = 1; i <= n; i++) {
+               j = a[i];
+               if (j == 0)
+                       continue;
+               y = -b[j];
+               oldl = 0;
+               oldc = c[0];
+               numtries = 0;
+               do {
+                       if (y <= clist[oldc].y)
+                               continue;
+                       l = search(c, k, y);
+                       if (l != oldl + 1)
+                               oldc = c[l - 1];
+                       if (l <= k) {
+                               if (clist[c[l]].y <= y)
+                                       continue;
+                               tc = c[l];
+                               c[l] = newcand(i, y, oldc);
+                               oldc = tc;
+                               oldl = l;
+                               numtries++;
+                       } else {
+                               c[l] = newcand(i, y, oldc);
+                               k++;
+                               break;
+                       }
+               } while ((y = b[++j]) > 0 && numtries < bound);
+       }
+       return (k);
+}
+
+static int
+newcand(int x, int y, int pred)
+{
+       struct cand *q;
+
+       if (clen == clistlen) {
+               clistlen = clistlen * 11 / 10;
+               clist = erealloc(clist, clistlen * sizeof(struct cand));
+       }
+       q = clist + clen;
+       q->x = x;
+       q->y = y;
+       q->pred = pred;
+       return (clen++);
+}
+
+static int
+search(int *c, int k, int y)
+{
+       int i, j, l, t;
+
+       if (clist[c[k]].y < y)  /* quick look for typical case */
+               return (k + 1);
+       i = 0;
+       j = k + 1;
+       while (1) {
+               l = i + j;
+               if ((l >>= 1) <= i)
+                       break;
+               t = clist[c[l]].y;
+               if (t > y)
+                       j = l;
+               else if (t < y)
+                       i = l;
+               else
+                       return (l);
+       }
+       return (l + 1);
+}
+
+static void
+unravel(int p)
+{
+       struct cand *q;
+       int i;
+
+       for (i = 0; i <= len[0]; i++)
+               J[i] = i <= pref ? i :
+                   i > len[0] - suff ? i + len[1] - len[0] : 0;
+       for (q = clist + p; q->y != 0; q = clist + q->pred)
+               J[q->x + pref] = q->y + pref;
+}
+
+/*
+ * Check does double duty:
+ *  1. ferret out any fortuitous correspondences due
+ *     to confounding by hashing (which result in "jackpot")
+ *  2.  collect random access indexes to the two files
+ */
+static void
+check(char *file1, FILE *f1, char *file2, FILE *f2)
+{
+       int i, j, jackpot, c, d;
+       long ctold, ctnew;
+
+       rewind(f1);
+       rewind(f2);
+       j = 1;
+       ixold[0] = ixnew[0] = 0;
+       jackpot = 0;
+       ctold = ctnew = 0;
+       for (i = 1; i <= len[0]; i++) {
+               if (J[i] == 0) {
+                       ixold[i] = ctold += skipline(f1);
+                       continue;
+               }
+               while (j < J[i]) {
+                       ixnew[j] = ctnew += skipline(f2);
+                       j++;
+               }
+               if (bflag || wflag || iflag) {
+                       for (;;) {
+                               c = getc(f1);
+                               d = getc(f2);
+                               /*
+                                * GNU diff ignores a missing newline
+                                * in one file if bflag || wflag.
+                                */
+                               if ((bflag || wflag) &&
+                                   ((c == EOF && d == '\n') ||
+                                   (c == '\n' && d == EOF))) {
+                                       break;
+                               }
+                               ctold++;
+                               ctnew++;
+                               if (bflag && isspace(c) && isspace(d)) {
+                                       do {
+                                               if (c == '\n')
+                                                       break;
+                                               ctold++;
+                                       } while (isspace(c = getc(f1)));
+                                       do {
+                                               if (d == '\n')
+                                                       break;
+                                               ctnew++;
+                                       } while (isspace(d = getc(f2)));
+                               } else if (wflag) {
+                                       while (isspace(c) && c != '\n') {
+                                               c = getc(f1);
+                                               ctold++;
+                                       }
+                                       while (isspace(d) && d != '\n') {
+                                               d = getc(f2);
+                                               ctnew++;
+                                       }
+                               }
+                               if (chrtran[c] != chrtran[d]) {
+                                       jackpot++;
+                                       J[i] = 0;
+                                       if (c != '\n' && c != EOF)
+                                               ctold += skipline(f1);
+                                       if (d != '\n' && c != EOF)
+                                               ctnew += skipline(f2);
+                                       break;
+                               }
+                               if (c == '\n' || c == EOF)
+                                       break;
+                       }
+               } else {
+                       for (;;) {
+                               ctold++;
+                               ctnew++;
+                               if ((c = getc(f1)) != (d = getc(f2))) {
+                                       /* jackpot++; */
+                                       J[i] = 0;
+                                       if (c != '\n' && c != EOF)
+                                               ctold += skipline(f1);
+                                       if (d != '\n' && c != EOF)
+                                               ctnew += skipline(f2);
+                                       break;
+                               }
+                               if (c == '\n' || c == EOF)
+                                       break;
+                       }
+               }
+               ixold[i] = ctold;
+               ixnew[j] = ctnew;
+               j++;
+       }
+       for (; j <= len[1]; j++)
+               ixnew[j] = ctnew += skipline(f2);
+       /*
+        * if (jackpot)
+        *      fprintf(stderr, "jackpot\n");
+        */
+}
+
+/* shellsort CACM #201 */
+static void
+sort(struct line *a, int n)
+{
+       struct line *ai, *aim, w;
+       int j, m = 0, k;
+
+       if (n == 0)
+               return;
+       for (j = 1; j <= n; j *= 2)
+               m = 2 * j - 1;
+       for (m /= 2; m != 0; m /= 2) {
+               k = n - m;
+               for (j = 1; j <= k; j++) {
+                       for (ai = &a[j]; ai > a; ai -= m) {
+                               aim = &ai[m];
+                               if (aim < ai)
+                                       break;  /* wraparound */
+                               if (aim->value > ai[0].value ||
+                                   (aim->value == ai[0].value &&
+                                       aim->serial > ai[0].serial))
+                                       break;
+                               w.value = ai[0].value;
+                               ai[0].value = aim->value;
+                               aim->value = w.value;
+                               w.serial = ai[0].serial;
+                               ai[0].serial = aim->serial;
+                               aim->serial = w.serial;
+                       }
+               }
+       }
+}
+
+static void
+unsort(struct line *f, int l, int *b)
+{
+       int *a, i;
+
+       a = emalloc((l + 1) * sizeof(int));
+       for (i = 1; i <= l; i++)
+               a[f[i].serial] = f[i].value;
+       for (i = 1; i <= l; i++)
+               b[i] = a[i];
+       free(a);
+}
+
+static int
+skipline(FILE *f)
+{
+       int i, c;
+
+       for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
+               continue;
+       return (i);
+}
+
+static void
+output(char *file1, FILE *f1, char *file2, FILE *f2, int flags)
+{
+       int m, i0, i1, j0, j1;
+
+       rewind(f1);
+       rewind(f2);
+       m = len[0];
+       J[0] = 0;
+       J[m + 1] = len[1] + 1;
+       if (format != D_EDIT) {
+               for (i0 = 1; i0 <= m; i0 = i1 + 1) {
+                       while (i0 <= m && J[i0] == J[i0 - 1] + 1)
+                               i0++;
+                       j0 = J[i0 - 1] + 1;
+                       i1 = i0 - 1;
+                       while (i1 < m && J[i1 + 1] == 0)
+                               i1++;
+                       j1 = J[i1 + 1] - 1;
+                       J[i1] = j1;
+                       change(file1, f1, file2, f2, i0, i1, j0, j1, &flags);
+               }
+       } else {
+               for (i0 = m; i0 >= 1; i0 = i1 - 1) {
+                       while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0)
+                               i0--;
+                       j0 = J[i0 + 1] - 1;
+                       i1 = i0 + 1;
+                       while (i1 > 1 && J[i1 - 1] == 0)
+                               i1--;
+                       j1 = J[i1 - 1] + 1;
+                       J[i1] = j1;
+                       change(file1, f1, file2, f2, i1, i0, j1, j0, &flags);
+               }
+       }
+       if (m == 0)
+               change(file1, f1, file2, f2, 1, 0, 1, len[1], &flags);
+       if (format == D_IFDEF) {
+               for (;;) {
+#define        c i0
+                       if ((c = getc(f1)) == EOF)
+                               return;
+                       putchar(c);
+               }
+#undef c
+       }
+       if (anychange != 0) {
+               if (format == D_CONTEXT)
+                       dump_context_vec(f1, f2);
+               else if (format == D_UNIFIED)
+                       dump_unified_vec(f1, f2);
+       }
+}
+
+static __inline void
+range(int a, int b, char *separator)
+{
+       printf("%d", a > b ? b : a);
+       if (a < b)
+               printf("%s%d", separator, b);
+}
+
+static __inline void
+uni_range(int a, int b)
+{
+       if (a < b)
+               printf("%d,%d", a, b - a + 1);
+       else if (a == b)
+               printf("%d", b);
+       else
+               printf("%d,0", b);
+}
+
+static char *
+preadline(int fd, size_t len, off_t off)
+{
+       char *line;
+       ssize_t nr;
+
+       line = emalloc(len + 1);
+       if ((nr = pread(fd, line, len, off)) < 0)
+               err(1, "preadline");
+       if (nr > 0 && line[nr-1] == '\n')
+               nr--;
+       line[nr] = '\0';
+       return (line);
+}
+
+static int
+ignoreline(char *line)
+{
+       int ret;
+
+       ret = regexec(&ignore_re, line, 0, NULL, 0);
+       free(line);
+       return (ret == 0);      /* if it matched, it should be ignored. */
+}
+
+/*
+ * Indicate that there is a difference between lines a and b of the from file
+ * to get to lines c to d of the to file.  If a is greater then b then there
+ * are no lines in the from file involved and this means that there were
+ * lines appended (beginning at b).  If c is greater than d then there are
+ * lines missing from the to file.
+ */
+static void
+change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d,
+    int *pflags)
+{
+       static size_t max_context = 64;
+       int i;
+
+restart:
+       if (format != D_IFDEF && a > b && c > d)
+               return;
+       if (ignore_pats != NULL) {
+               char *line;
+               /*
+                * All lines in the change, insert, or delete must
+                * match an ignore pattern for the change to be
+                * ignored.
+                */
+               if (a <= b) {           /* Changes and deletes. */
+                       for (i = a; i <= b; i++) {
+                               line = preadline(fileno(f1),
+                                   ixold[i] - ixold[i - 1], ixold[i - 1]);
+                               if (!ignoreline(line))
+                                       goto proceed;
+                       }
+               }
+               if (a > b || c <= d) {  /* Changes and inserts. */
+                       for (i = c; i <= d; i++) {
+                               line = preadline(fileno(f2),
+                                   ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
+                               if (!ignoreline(line))
+                                       goto proceed;
+                       }
+               }
+               return;
+       }
+proceed:
+       if (*pflags & D_HEADER) {
+               printf("%s %s %s\n", diffargs, file1, file2);
+               *pflags &= ~D_HEADER;
+       }
+       if (format == D_CONTEXT || format == D_UNIFIED) {
+               /*
+                * Allocate change records as needed.
+                */
+               if (context_vec_ptr == context_vec_end - 1) {
+                       ptrdiff_t offset = context_vec_ptr - context_vec_start;
+                       max_context <<= 1;
+                       context_vec_start = erealloc(context_vec_start,
+                           max_context * sizeof(struct context_vec));
+                       context_vec_end = context_vec_start + max_context;
+                       context_vec_ptr = context_vec_start + offset;
+               }
+               if (anychange == 0) {
+                       /*
+                        * Print the context/unidiff header first time through.
+                        */
+                       print_header(file1, file2);
+                       anychange = 1;
+               } else if (a > context_vec_ptr->b + (2 * context) + 1 &&
+                   c > context_vec_ptr->d + (2 * context) + 1) {
+                       /*
+                        * If this change is more than 'context' lines from the
+                        * previous change, dump the record and reset it.
+                        */
+                       if (format == D_CONTEXT)
+                               dump_context_vec(f1, f2);
+                       else
+                               dump_unified_vec(f1, f2);
+               }
+               context_vec_ptr++;
+               context_vec_ptr->a = a;
+               context_vec_ptr->b = b;
+               context_vec_ptr->c = c;
+               context_vec_ptr->d = d;
+               return;
+       }
+       if (anychange == 0)
+               anychange = 1;
+       switch (format) {
+
+       case D_BRIEF:
+               return;
+       case D_NORMAL:
+       case D_EDIT:
+               range(a, b, ",");
+               putchar(a > b ? 'a' : c > d ? 'd' : 'c');
+               if (format == D_NORMAL)
+                       range(c, d, ",");
+               putchar('\n');
+               break;
+       case D_REVERSE:
+               putchar(a > b ? 'a' : c > d ? 'd' : 'c');
+               range(a, b, " ");
+               putchar('\n');
+               break;
+       case D_NREVERSE:
+               if (a > b)
+                       printf("a%d %d\n", b, d - c + 1);
+               else {
+                       printf("d%d %d\n", a, b - a + 1);
+                       if (!(c > d))
+                               /* add changed lines */
+                               printf("a%d %d\n", b, d - c + 1);
+               }
+               break;
+       }
+       if (format == D_NORMAL || format == D_IFDEF) {
+               fetch(ixold, a, b, f1, '<', 1);
+               if (a <= b && c <= d && format == D_NORMAL)
+                       puts("---");
+       }
+       i = fetch(ixnew, c, d, f2, format == D_NORMAL ? '>' : '\0', 0);
+       if (i != 0 && format == D_EDIT) {
+               /*
+                * A non-zero return value for D_EDIT indicates that the
+                * last line printed was a bare dot (".") that has been
+                * escaped as ".." to prevent ed(1) from misinterpreting
+                * it.  We have to add a substitute command to change this
+                * back and restart where we left off.
+                */
+               puts(".");
+               printf("%ds/^\\.\\././\n", a);
+               a += i;
+               c += i;
+               goto restart;
+       }
+       if ((format == D_EDIT || format == D_REVERSE) && c <= d)
+               puts(".");
+       if (inifdef) {
+               printf("#endif /* %s */\n", ifdefname);
+               inifdef = 0;
+       }
+}
+
+static int
+fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
+{
+       int i, j, c, lastc, col, nc;
+
+       /*
+        * When doing #ifdef's, copy down to current line
+        * if this is the first file, so that stuff makes it to output.
+        */
+       if (format == D_IFDEF && oldfile) {
+               long curpos = ftell(lb);
+               /* print through if append (a>b), else to (nb: 0 vs 1 orig) */
+               nc = f[a > b ? b : a - 1] - curpos;
+               for (i = 0; i < nc; i++)
+                       putchar(getc(lb));
+       }
+       if (a > b)
+               return (0);
+       if (format == D_IFDEF) {
+               if (inifdef) {
+                       printf("#else /* %s%s */\n",
+                           oldfile == 1 ? "!" : "", ifdefname);
+               } else {
+                       if (oldfile)
+                               printf("#ifndef %s\n", ifdefname);
+                       else
+                               printf("#ifdef %s\n", ifdefname);
+               }
+               inifdef = 1 + oldfile;
+       }
+       for (i = a; i <= b; i++) {
+               fseek(lb, f[i - 1], SEEK_SET);
+               nc = f[i] - f[i - 1];
+               if (format != D_IFDEF && ch != '\0') {
+                       putchar(ch);
+                       if (Tflag && (format == D_NORMAL || format == D_CONTEXT
+                           || format == D_UNIFIED))
+                               putchar('\t');
+                       else if (format != D_UNIFIED)
+                               putchar(' ');
+               }
+               col = 0;
+               for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
+                       if ((c = getc(lb)) == EOF) {
+                               if (format == D_EDIT || format == D_REVERSE ||
+                                   format == D_NREVERSE)
+                                       warnx("No newline at end of file");
+                               else
+                                       puts("\n\\ No newline at end of file");
+                               return (0);
+                       }
+                       if (c == '\t' && tflag) {
+                               do {
+                                       putchar(' ');
+                               } while (++col & 7);
+                       } else {
+                               if (format == D_EDIT && j == 1 && c == '\n'
+                                   && lastc == '.') {
+                                       /*
+                                        * Don't print a bare "." line
+                                        * since that will confuse ed(1).
+                                        * Print ".." instead and return,
+                                        * giving the caller an offset
+                                        * from which to restart.
+                                        */
+                                       puts(".");
+                                       return (i - a + 1);
+                               }
+                               putchar(c);
+                               col++;
+                       }
+               }
+       }
+       return (0);
+}
+
+/*
+ * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
+ */
+static int
+readhash(FILE *f)
+{
+       int i, t, space;
+       int sum;
+
+       sum = 1;
+       space = 0;
+       if (!bflag && !wflag) {
+               if (iflag)
+                       for (i = 0; (t = getc(f)) != '\n'; i++) {
+                               if (t == EOF) {
+                                       if (i == 0)
+                                               return (0);
+                                       break;
+                               }
+                               sum = sum * 127 + chrtran[t];
+                       }
+               else
+                       for (i = 0; (t = getc(f)) != '\n'; i++) {
+                               if (t == EOF) {
+                                       if (i == 0)
+                                               return (0);
+                                       break;
+                               }
+                               sum = sum * 127 + t;
+                       }
+       } else {
+               for (i = 0;;) {
+                       switch (t = getc(f)) {
+                       case '\t':
+                       case '\r':
+                       case '\v':
+                       case '\f':
+                       case ' ':
+                               space++;
+                               continue;
+                       default:
+                               if (space && !wflag) {
+                                       i++;
+                                       space = 0;
+                               }
+                               sum = sum * 127 + chrtran[t];
+                               i++;
+                               continue;
+                       case EOF:
+                               if (i == 0)
+                                       return (0);
+                               /* FALLTHROUGH */
+                       case '\n':
+                               break;
+                       }
+                       break;
+               }
+       }
+       /*
+        * There is a remote possibility that we end up with a zero sum.
+        * Zero is used as an EOF marker, so return 1 instead.
+        */
+       return (sum == 0 ? 1 : sum);
+}
+
+static int
+asciifile(FILE *f)
+{
+       unsigned char buf[BUFSIZ];
+       int i, cnt;
+
+       if (aflag || f == NULL)
+               return (1);
+
+       rewind(f);
+       cnt = fread(buf, 1, sizeof(buf), f);
+       for (i = 0; i < cnt; i++)
+               if (!isprint(buf[i]) && !isspace(buf[i]))
+                       return (0);
+       return (1);
+}
+
+static __inline int min(int a, int b)
+{
+       return (a < b ? a : b);
+}
+
+static __inline int max(int a, int b)
+{
+       return (a > b ? a : b);
+}
+
+#define begins_with(s, pre) (strncmp(s, pre, sizeof(pre)-1) == 0)
+
+static char *
+match_function(const long *f, int pos, FILE *file)
+{
+       unsigned char buf[FUNCTION_CONTEXT_SIZE];
+       size_t nc;
+       int last = lastline;
+       char *p;
+       char *state = NULL;
+
+       lastline = pos;
+       while (pos > last) {
+               fseek(file, f[pos - 1], SEEK_SET);
+               nc = f[pos] - f[pos - 1];
+               if (nc >= sizeof(buf))
+                       nc = sizeof(buf) - 1;
+               nc = fread(buf, 1, nc, file);
+               if (nc > 0) {
+                       buf[nc] = '\0';
+                       p = strchr(buf, '\n');
+                       if (p != NULL)
+                               *p = '\0';
+                       if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
+                               if (begins_with(buf, "private:")) {
+                                       if (!state)
+                                               state = " (private)";
+                               } else if (begins_with(buf, "protected:")) {
+                                       if (!state)
+                                               state = " (protected)";
+                               } else if (begins_with(buf, "public:")) {
+                                       if (!state)
+                                               state = " (public)";
+                               } else {
+                                       strlcpy(lastbuf, buf, sizeof lastbuf);
+                                       if (state)
+                                               strlcat(lastbuf, state, 
+                                                   sizeof lastbuf);
+                                       lastmatchline = pos;
+                                       return lastbuf;
+                               }
+                       }
+               }
+               pos--;
+       }
+       return lastmatchline > 0 ? lastbuf : NULL;
+}
+
+/* dump accumulated "context" diff changes */
+static void
+dump_context_vec(FILE *f1, FILE *f2)
+{
+       struct context_vec *cvp = context_vec_start;
+       int lowa, upb, lowc, upd, do_output;
+       int a, b, c, d;
+       char ch, *f;
+
+       if (context_vec_start > context_vec_ptr)
+               return;
+
+       b = d = 0;              /* gcc */
+       lowa = max(1, cvp->a - context);
+       upb = min(len[0], context_vec_ptr->b + context);
+       lowc = max(1, cvp->c - context);
+       upd = min(len[1], context_vec_ptr->d + context);
+
+       printf("***************");
+       if (pflag) {
+               f = match_function(ixold, lowa-1, f1);
+               if (f != NULL) {
+                       putchar(' ');
+                       fputs(f, stdout);
+               }
+       }
+       printf("\n*** ");
+       range(lowa, upb, ",");
+       printf(" ****\n");
+
+       /*
+        * Output changes to the "old" file.  The first loop suppresses
+        * output if there were no changes to the "old" file (we'll see
+        * the "old" lines as context in the "new" list).
+        */
+       do_output = 0;
+       for (; cvp <= context_vec_ptr; cvp++)
+               if (cvp->a <= cvp->b) {
+                       cvp = context_vec_start;
+                       do_output++;
+                       break;
+               }
+       if (do_output) {
+               while (cvp <= context_vec_ptr) {
+                       a = cvp->a;
+                       b = cvp->b;
+                       c = cvp->c;
+                       d = cvp->d;
+
+                       if (a <= b && c <= d)
+                               ch = 'c';
+                       else
+                               ch = (a <= b) ? 'd' : 'a';
+
+                       if (ch == 'a')
+                               fetch(ixold, lowa, b, f1, ' ', 0);
+                       else {
+                               fetch(ixold, lowa, a - 1, f1, ' ', 0);
+                               fetch(ixold, a, b, f1,
+                                   ch == 'c' ? '!' : '-', 0);
+                       }
+                       lowa = b + 1;
+                       cvp++;
+               }
+               fetch(ixold, b + 1, upb, f1, ' ', 0);
+       }
+       /* output changes to the "new" file */
+       printf("--- ");
+       range(lowc, upd, ",");
+       printf(" ----\n");
+
+       do_output = 0;
+       for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
+               if (cvp->c <= cvp->d) {
+                       cvp = context_vec_start;
+                       do_output++;
+                       break;
+               }
+       if (do_output) {
+               while (cvp <= context_vec_ptr) {
+                       a = cvp->a;
+                       b = cvp->b;
+                       c = cvp->c;
+                       d = cvp->d;
+
+                       if (a <= b && c <= d)
+                               ch = 'c';
+                       else
+                               ch = (a <= b) ? 'd' : 'a';
+
+                       if (ch == 'd')
+                               fetch(ixnew, lowc, d, f2, ' ', 0);
+                       else {
+                               fetch(ixnew, lowc, c - 1, f2, ' ', 0);
+                               fetch(ixnew, c, d, f2,
+                                   ch == 'c' ? '!' : '+', 0);
+                       }
+                       lowc = d + 1;
+                       cvp++;
+               }
+               fetch(ixnew, d + 1, upd, f2, ' ', 0);
+       }
+       context_vec_ptr = context_vec_start - 1;
+}
+
+/* dump accumulated "unified" diff changes */
+static void
+dump_unified_vec(FILE *f1, FILE *f2)
+{
+       struct context_vec *cvp = context_vec_start;
+       int lowa, upb, lowc, upd;
+       int a, b, c, d;
+       char ch, *f;
+
+       if (context_vec_start > context_vec_ptr)
+               return;
+
+       b = d = 0;              /* gcc */
+       lowa = max(1, cvp->a - context);
+       upb = min(len[0], context_vec_ptr->b + context);
+       lowc = max(1, cvp->c - context);
+       upd = min(len[1], context_vec_ptr->d + context);
+
+       fputs("@@ -", stdout);
+       uni_range(lowa, upb);
+       fputs(" +", stdout);
+       uni_range(lowc, upd);
+       fputs(" @@", stdout);
+       if (pflag) {
+               f = match_function(ixold, lowa-1, f1);
+               if (f != NULL) {
+                       putchar(' ');
+                       fputs(f, stdout);
+               }
+       }
+       putchar('\n');
+
+       /*
+        * Output changes in "unified" diff format--the old and new lines
+        * are printed together.
+        */
+       for (; cvp <= context_vec_ptr; cvp++) {
+               a = cvp->a;
+               b = cvp->b;
+               c = cvp->c;
+               d = cvp->d;
+
+               /*
+                * c: both new and old changes
+                * d: only changes in the old file
+                * a: only changes in the new file
+                */
+               if (a <= b && c <= d)
+                       ch = 'c';
+               else
+                       ch = (a <= b) ? 'd' : 'a';
+
+               switch (ch) {
+               case 'c':
+                       fetch(ixold, lowa, a - 1, f1, ' ', 0);
+                       fetch(ixold, a, b, f1, '-', 0);
+                       fetch(ixnew, c, d, f2, '+', 0);
+                       break;
+               case 'd':
+                       fetch(ixold, lowa, a - 1, f1, ' ', 0);
+                       fetch(ixold, a, b, f1, '-', 0);
+                       break;
+               case 'a':
+                       fetch(ixnew, lowc, c - 1, f2, ' ', 0);
+                       fetch(ixnew, c, d, f2, '+', 0);
+                       break;
+               }
+               lowa = b + 1;
+               lowc = d + 1;
+       }
+       fetch(ixnew, d + 1, upd, f2, ' ', 0);
+
+       context_vec_ptr = context_vec_start - 1;
+}
+
+static void
+print_header(const char *file1, const char *file2)
+{
+       if (label[0] != NULL)
+               printf("%s %s\n", format == D_CONTEXT ? "***" : "---",
+                   label[0]);
+       else
+               printf("%s %s\t%s", format == D_CONTEXT ? "***" : "---",
+                   file1, ctime(&stb1.st_mtime));
+       if (label[1] != NULL)
+               printf("%s %s\n", format == D_CONTEXT ? "---" : "+++",
+                   label[1]);
+       else
+               printf("%s %s\t%s", format == D_CONTEXT ? "---" : "+++",
+                   file2, ctime(&stb2.st_mtime));
+}
diff --git a/ext/diff.cc b/ext/diff.cc
new file mode 100644 (file)
index 0000000..0346c5b
--- /dev/null
@@ -0,0 +1,2314 @@
+/*     $OpenBSD: diffreg.c,v 1.67 2007/03/18 21:12:27 espie Exp $      */
+
+/*
+ * Copyright (C) Caldera International Inc.  2001-2002.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code and documentation must retain the above
+ *    copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed or owned by Caldera
+ *     International, Inc.
+ * 4. Neither the name of Caldera International, Inc. nor the names of other
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+ * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
+ * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*-
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)diffreg.c   8.1 (Berkeley) 6/6/93
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+//#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include "diff.h"
+//#include "pathnames.h"
+//#include "variable.h"
+#include "mlenv.h"
+#include "ustring.h"
+//#ifdef HEAPDEBUG
+//#include "heapdebug.h"
+//#endif
+
+/*
+ * Output format options
+ */
+#define        D_NORMAL        0       /* Normal output */
+#define        D_EDIT          -1      /* Editor script out */
+#define        D_REVERSE       1       /* Reverse editor script */
+#define        D_CONTEXT       2       /* Diff with context */
+#define        D_UNIFIED       3       /* Unified context diff */
+#define        D_IFDEF         4       /* Diff with merged #ifdef's */
+#define        D_NREVERSE      5       /* Reverse ed script with numbered
+                                  lines and no trailing . */
+#define        D_BRIEF         6       /* Say if the files differ */
+
+/*
+ * Output flags
+ */
+#define        D_HEADER        1       /* Print a header/footer between files */
+#define        D_EMPTY1        2       /* Treat first file as empty (/dev/null) */
+#define        D_EMPTY2        4       /* Treat second file as empty (/dev/null) */
+
+/*
+ * Status values for print_status() and diffreg() return values
+ */
+#define        D_SAME          0       /* Files are the same */
+#define        D_DIFFER        1       /* Files are different */
+#define        D_BINARY        2       /* Binary files are different */
+#define        D_COMMON        3       /* Subdirectory common to both dirs */
+#define        D_ONLY          4       /* Only exists in one directory */
+#define        D_MISMATCH1     5       /* path1 was a dir, path2 a file */
+#define        D_MISMATCH2     6       /* path1 was a file, path2 a dir */
+#define        D_ERROR         7       /* An error occurred */
+#define        D_SKIPPED1      8       /* path1 was a special file */
+#define        D_SKIPPED2      9       /* path2 was a special file */
+
+
+static __inline int min(int, int);
+
+class  BBlockFILE {
+ public:
+//    BBlock*  text;
+//    BBlock  ptr;
+    ustring*  text;
+    uiterator  b;
+    uiterator  e;
+    int  err;
+
+//    BBlockFILE (BBlock* p): text (p) {
+    BBlockFILE (ustring* p): text (p) {
+//     ptr.assign (text);
+       b = text->begin ();
+       e = text->end ();
+       err = 0;
+    };
+    virtual  ~BBlockFILE () {};
+    virtual void  rewind () {
+//     ptr.assign (text);
+       b = text->begin ();
+       e = text->end ();
+    };
+    virtual int  mgetc () {
+//     if (ptr.length > 0) {
+//         return ptr.shift ();
+       if (b < e) {
+           uiterator  i = b;
+           b ++;
+           return *i;
+       } else {
+           return EOF;
+           err = 1;
+       }
+    };
+    virtual int  merror () {
+       return err;
+    };
+    virtual size_t  fread (char* p, size_t s, size_t n) {
+       size_t  ans = min (e - b, s * n) / s;
+       memcpy (p, &b[0], ans * s);
+       b += ans * s;
+       return ans;
+    };
+    virtual size_t  fread (u_char* p, size_t s, size_t n) {
+       return fread ((char*)p, s, n);
+    };
+    virtual int  fseek (long offset, int whence) {
+       switch (whence) {
+       case SEEK_SET:
+           if (offset <= text->length ()) {
+//             ptr.assign (text->start + offset, text->length - offset);
+               b = text->begin () + offset;
+               e = text->end ();
+               return 0;
+           } else {
+//             ptr.assign (text->start + text->length, 0);
+               b = text->end ();
+               e = text->end ();
+               err = 1;
+               return -1;
+           }
+           break;
+       case SEEK_CUR:
+           if (offset <= e - b) {
+//             ptr.skip (offset);
+               b += offset;
+               return 0;
+           } else {
+//             ptr.skip (ptr.length);
+               b = e;
+               err = 1;
+               return -1;
+           }
+           break;
+       case SEEK_END:
+           if (offset <= text->length ()) {
+//             ptr.assign (text->start + text->length - offset, offset);
+               e = text->end ();
+               b = e - offset;
+               return 0;
+           } else {
+//             ptr.assign (text);
+               b = text->begin ();
+               e = text->end ();
+               err = 1;
+               return -1;
+           }
+           break;
+       default:;
+           return -1;
+       }
+    };
+};
+
+#if 0
+class  OutputVar {
+public:
+    ustring  vlc;
+    ustring  vll;
+    ustring  vrc;
+    ustring  vrl;
+    MlEnv*  mlenv;
+    int  n;
+
+    OutputVar (ustring v1, ustring v2, ustring v3, ustring v4, MlEnv* e) {
+       vlc = v1;
+       vll = v2;
+       vrc = v3;
+       vrl = v4;
+       mlenv = e;
+       n = 0;
+    };
+    virtual void  out (char c1, ustring* v1, char c2, ustring* v2) {
+//     u_char  b[4];
+//     uiterator  p, q;
+
+       n ++;
+       mlenv->setAry (vlc, n, newMNode_str (new ustring (1, c1)));
+       mlenv->setAry (vll, n, newMNode_str (new ustring (*v1)));
+       mlenv->setAry (vrc, n, newMNode_str (new ustring (1, c2)));
+       mlenv->setAry (vrl, n, newMNode_str (new ustring (*v2)));
+    };
+    virtual void  linenum (int n1, int n2) {
+       static ustring  p (CharConst ("#"));
+
+       n ++;
+       mlenv->setAry (vlc, n, newMNode_str (new ustring (p)));
+       mlenv->setAry (vll, n, newMNode_num (n1));
+       mlenv->setAry (vrc, n, newMNode_str (new ustring (p)));
+       mlenv->setAry (vrl, n, newMNode_num (n2));
+    };
+    virtual void  finish () {
+       mlenv->setArySize (vlc, n);
+       mlenv->setArySize (vll, n);
+       mlenv->setArySize (vrc, n);
+       mlenv->setArySize (vrl, n);
+    };
+};
+#endif
+class  OutputList {
+public:
+    MNodeList  vlc;
+    MNodeList  vll;
+    MNodeList  vrc;
+    MNodeList  vrl;
+    MlEnv*  mlenv;
+
+    OutputList (MlEnv* e) {
+       mlenv = e;
+    };
+    virtual  ~OutputList () {};
+
+    virtual void  out (char c1, ustring* v1, char c2, ustring* v2) {
+       vlc.append (newMNode_str (new ustring (1, c1)));
+       vll.append (newMNode_str (new ustring (*v1)));
+       vrc.append (newMNode_str (new ustring (1, c2)));
+       vrl.append (newMNode_str (new ustring (*v2)));
+    };
+    virtual void  linenum (int n1, int n2) {
+       static ustring  p (CharConst ("#"));
+       vlc.append (newMNode_str (new ustring (p)));
+       vll.append (newMNode_num (n1));
+       vrc.append (newMNode_str (new ustring (p)));
+       vrl.append (newMNode_num (n2));
+    };
+};
+
+/*
+ * diff - compare two files.
+ */
+
+/*
+ *     Uses an algorithm due to Harold Stone, which finds
+ *     a pair of longest identical subsequences in the two
+ *     files.
+ *
+ *     The major goal is to generate the match vector J.
+ *     J[i] is the index of the line in file1 corresponding
+ *     to line i file0. J[i] = 0 if there is no
+ *     such line in file1.
+ *
+ *     Lines are hashed so as to work in core. All potential
+ *     matches are located by sorting the lines of each file
+ *     on the hash (called ``value''). In particular, this
+ *     collects the equivalence classes in file1 together.
+ *     Subroutine equiv replaces the value of each line in
+ *     file0 by the index of the first element of its
+ *     matching equivalence in (the reordered) file1.
+ *     To save space equiv squeezes file1 into a single
+ *     array member in which the equivalence classes
+ *     are simply concatenated, except that their first
+ *     members are flagged by changing sign.
+ *
+ *     Next the indices that point into member are unsorted into
+ *     array class according to the original order of file0.
+ *
+ *     The cleverness lies in routine stone. This marches
+ *     through the lines of file0, developing a vector klist
+ *     of "k-candidates". At step i a k-candidate is a matched
+ *     pair of lines x,y (x in file0 y in file1) such that
+ *     there is a common subsequence of length k
+ *     between the first i lines of file0 and the first y
+ *     lines of file1, but there is no such subsequence for
+ *     any smaller y. x is the earliest possible mate to y
+ *     that occurs in such a subsequence.
+ *
+ *     Whenever any of the members of the equivalence class of
+ *     lines in file1 matable to a line in file0 has serial number
+ *     less than the y of some k-candidate, that k-candidate
+ *     with the smallest such y is replaced. The new
+ *     k-candidate is chained (via pred) to the current
+ *     k-1 candidate so that the actual subsequence can
+ *     be recovered. When a member has serial number greater
+ *     that the y of all k-candidates, the klist is extended.
+ *     At the end, the longest subsequence is pulled out
+ *     and placed in the array J by unravel
+ *
+ *     With J in hand, the matches there recorded are
+ *     check'ed against reality to assure that no spurious
+ *     matches have crept in due to hashing. If they have,
+ *     they are broken, and "jackpot" is recorded--a harmless
+ *     matter except that a true match for a spuriously
+ *     mated line may now be unnecessarily reported as a change.
+ *
+ *     Much of the complexity of the program comes simply
+ *     from trying to minimize core utilization and
+ *     maximize the range of doable problems by dynamically
+ *     allocating what is needed and reusing what is not.
+ *     The core requirements for problems larger than somewhat
+ *     are (in words) 2*length(file0) + length(file1) +
+ *     3*(number of k-candidates installed),  typically about
+ *     6n words for files of length n.
+ */
+
+struct cand {
+       int x;
+       int y;
+       int pred;
+};
+
+struct line {
+       int serial;
+       int value;
+} *file[2];
+
+/*
+ * The following struct is used to record change information when
+ * doing a "context" or "unified" diff.  (see routine "change" to
+ * understand the highly mnemonic field names)
+ */
+struct context_vec {
+       int a;                  /* start line in old file */
+       int b;                  /* end line in old file */
+       int c;                  /* start line in new file */
+       int d;                  /* end line in new file */
+};
+
+static int  *J;                        /* will be overlaid on class */
+static int  *vclass;           /* will be overlaid on file[0] */
+static int  *klist;            /* will be overlaid on file[0] after class */
+static int  *member;           /* will be overlaid on file[1] */
+static int   clen;
+//static int   inifdef;                /* whether or not we are in a #ifdef block */
+static int   len[2];
+static int   pref, suff;       /* length of prefix and suffix */
+static int   slen[2];
+static int   anychange;
+static long *ixnew;            /* will be overlaid on file[1] */
+static long *ixold;            /* will be overlaid on klist */
+static struct cand *clist;     /* merely a free storage pot for candidates */
+static int   clistlen;         /* the length of clist */
+static struct line *sfile[2];  /* shortened by pruning common prefix/suffix */
+static u_char *chrtran;                /* translation table for case-folding */
+static struct context_vec *context_vec_start;
+static struct context_vec *context_vec_end;
+static struct context_vec *context_vec_ptr;
+
+#define FUNCTION_CONTEXT_SIZE  55
+static char lastbuf[FUNCTION_CONTEXT_SIZE];
+static int lastline;
+static int lastmatchline;
+
+static int     bflag, dflag, iflag, wflag;
+static int     format, context, status;
+
+//static FILE *opentemp(const char *);
+//static void output(char *, FILE *, char *, FILE *, int);
+//static void  output(BBlockFILE* f1, BBlockFILE* f2, int flags, OutputVar* out);
+static void  output(BBlockFILE* f1, BBlockFILE* f2, int flags, OutputList* out);
+//static void check(char *, FILE *, char *, FILE *);
+static void  check(BBlockFILE *f1, BBlockFILE *f2);
+//static void range(int, int, char *);
+//static void uni_range(int, int);
+//static void dump_context_vec(FILE *, FILE *);
+//static void  dump_context_vec(BBlockFILE*f1, BBlockFILE*f2);
+//static void  dump_context_vec(BBlockFILE*f1, BBlockFILE*f2, OutputVar* out);
+static void  dump_context_vec(BBlockFILE*f1, BBlockFILE*f2, OutputList* out);
+//static void dump_unified_vec(FILE *, FILE *);
+//static void  dump_unified_vec(BBlockFILE* f1, BBlockFILE* f2);
+//static void prepare(int, FILE *, off_t);
+static void prepare(int, BBlockFILE *);
+static void prune(void);
+static void equiv(struct line *, int, struct line *, int, int *);
+static void unravel(int);
+static void unsort(struct line *, int, int *);
+//static void change(char *, FILE *, char *, FILE *, int, int, int, int, int *);
+//static void  change(BBlockFILE* f1, BBlockFILE* f2, int a, int b, int c, int d, int *pflags, OutputVar* out);
+static void  change(BBlockFILE* f1, BBlockFILE* f2, int a, int b, int c, int d, int *pflags, OutputList* out);
+static void sort(struct line *, int);
+//static void print_header(const char *, const char *);
+//static void print_header();
+//static int  ignoreline(char *);
+//static int  asciifile(FILE *);
+//static int  fetch(long *, int, int, FILE *, int, int);
+//static int  fetch(long *f, int a, int b, BBlockFILE*lb, int ch);
+static int  newcand(int, int, int);
+static int  search(int *, int, int);
+//static int  skipline(FILE *);
+static int  skipline(BBlockFILE *);
+static int  isqrt(int);
+static int  stone(int *, int, int *, int *);
+//static int  readhash(FILE *);
+static int  readhash(BBlockFILE *);
+//static int  files_differ(FILE *, FILE *, int);
+static int  files_differ (BBlockFILE* f1, BBlockFILE* f2, int flags);
+static __inline int min(int, int);
+static __inline int max(int, int);
+//static char *match_function(const long *, int, FILE *);
+//static char* match_function(const long *f, int pos, BBlockFILE *file);
+//static char *preadline(int, size_t, off_t);
+
+
+/*
+ * chrtran points to one of 2 translation tables: cup2low if folding upper to
+ * lower case clow2low if not folding case
+ */
+u_char clow2low[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+       0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+       0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+       0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+       0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
+       0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
+       0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c,
+       0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+       0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
+       0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
+       0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+       0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+       0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
+       0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+       0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
+       0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+       0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
+       0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
+       0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
+       0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
+       0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+       0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
+       0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
+       0xfd, 0xfe, 0xff
+};
+
+u_char cup2low[256] = {
+       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+       0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+       0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+       0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+       0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
+       0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61,
+       0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c,
+       0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+       0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62,
+       0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d,
+       0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+       0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+       0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e,
+       0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+       0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
+       0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+       0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
+       0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
+       0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
+       0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb,
+       0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+       0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
+       0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc,
+       0xfd, 0xfe, 0xff
+};
+
+static void*  emalloc (size_t s) {
+#ifdef HEAPDEBUG0
+    void*  ans;
+    ans = malloc (s);
+    fmdbAdd (ans, s);
+    return ans;
+#else
+    return malloc (s);
+#endif
+}
+static void*  erealloc (void* p, size_t s) {
+#ifdef HEAPDEBUG0
+    void*  ans;
+    if (p)
+       mdbDelete (p);
+    ans = realloc (p, s);
+    fmdbAdd (ans, s);
+    return ans;
+#else
+    return realloc (p, s);
+#endif
+}
+static void  efree (void* p) {
+#ifdef HEAPDEBUG0
+    mdbDelete (p);
+#endif
+    free (p);
+}
+
+int
+//diffreg(char *ofile1, char *ofile2, int flags)
+//diffreg (ustring* text1, ustring* text2, ustring* varleftc, ustring* varleftline, ustring* varrightc, ustring* varrightline, MlEnv* mlenv)
+diffreg (ustring* text1, ustring* text2, MNodePtr* pleftc, MNodePtr* pleftline, MNodePtr* prightc, MNodePtr* prightline, MlEnv* mlenv)
+{
+//     char *file1 = ofile1;
+//     char *file2 = ofile2;
+//     FILE *f1 = NULL;
+//     FILE *f2 = NULL;
+       BBlockFILE  f1 (text1);
+       BBlockFILE  f2 (text2);
+       int rval = D_SAME;
+//     int i, ostdout = -1;
+       int i;
+//     pid_t pid = -1;
+       int  flags;
+//     OutputVar  out (*varleftc, *varleftline, *varrightc, *varrightline, mlenv);
+       OutputList  out (mlenv);
+
+       bflag = dflag = iflag = wflag = 0;
+//     format = D_UNIFIED;
+       format = D_CONTEXT;
+       context = 1;
+       status = 0;
+       flags = 0;
+       
+       len[0] = len[1] = 0;
+       pref = suff = 0;
+       slen[0] = slen[1] = 0;
+       anychange = 0;
+       context_vec_start = context_vec_end = NULL;
+       lastline = 0;
+       lastmatchline = 0;
+       context_vec_ptr = context_vec_start - 1;
+       chrtran = (iflag ? cup2low : clow2low);
+#if 0
+       if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode))
+               return (S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2);
+       if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0)
+               goto closem;
+#endif
+#if 0
+       if (flags & D_EMPTY1)
+               f1 = fopen(_PATH_DEVNULL, "r");
+       else {
+               if (!S_ISREG(stb1.st_mode)) {
+                       if ((f1 = opentemp(file1)) == NULL ||
+                           fstat(fileno(f1), &stb1) < 0) {
+                               warn("%s", file1);
+                               status |= 2;
+                               goto closem;
+                       }
+               } else if (strcmp(file1, "-") == 0)
+                       f1 = stdin;
+               else
+                       f1 = fopen(file1, "r");
+       }
+       if (f1 == NULL) {
+               warn("%s", file1);
+               status |= 2;
+               goto closem;
+       }
+#endif
+#if 0
+       if (flags & D_EMPTY2)
+               f2 = fopen(_PATH_DEVNULL, "r");
+       else {
+               if (!S_ISREG(stb2.st_mode)) {
+                       if ((f2 = opentemp(file2)) == NULL ||
+                           fstat(fileno(f2), &stb2) < 0) {
+                               warn("%s", file2);
+                               status |= 2;
+                               goto closem;
+                       }
+               } else if (strcmp(file2, "-") == 0)
+                       f2 = stdin;
+               else
+                       f2 = fopen(file2, "r");
+       }
+       if (f2 == NULL) {
+               warn("%s", file2);
+               status |= 2;
+               goto closem;
+       }
+#endif
+//     switch (files_differ(f1, f2, flags)) {
+       switch (files_differ(&f1, &f2, flags)) {
+       case 0:
+               goto closem;
+       case 1:
+               break;
+       default:
+               /* error */
+               status |= 2;
+               goto closem;
+       }
+
+#if 0
+       if (!asciifile(f1) || !asciifile(f2)) {
+               rval = D_BINARY;
+               status |= 1;
+               goto closem;
+       }
+#endif
+#if 0
+       if (lflag) {
+               /* redirect stdout to pr */
+               int pfd[2];
+               char *header;
+               char *prargv[] = { "pr", "-h", NULL, "-f", NULL };
+
+               easprintf(&header, "%s %s %s", diffargs, file1, file2);
+               prargv[2] = header;
+               fflush(stdout);
+               rewind(stdout);
+               pipe(pfd);
+               switch ((pid = fork())) {
+               case -1:
+                       warnx("No more processes");
+                       status |= 2;
+//                     free(header);
+                       efree(header);
+                       return (D_ERROR);
+               case 0:
+                       /* child */
+                       if (pfd[0] != STDIN_FILENO) {
+                               dup2(pfd[0], STDIN_FILENO);
+                               close(pfd[0]);
+                       }
+                       close(pfd[1]);
+                       execv(_PATH_PR, prargv);
+                       _exit(127);
+               default:
+                       /* parent */
+                       if (pfd[1] != STDOUT_FILENO) {
+                               ostdout = dup(STDOUT_FILENO);
+                               dup2(pfd[1], STDOUT_FILENO);
+                               close(pfd[1]);
+                       }
+                       close(pfd[0]);
+                       rewind(stdout);
+//                     free(header);
+                       efree(header);
+               }
+       }
+#endif
+//     prepare(0, f1, stb1.st_size);
+//     prepare(1, f2, stb2.st_size);
+       prepare(0, &f1);
+       prepare(1, &f2);
+       prune();
+       sort(sfile[0], slen[0]);
+       sort(sfile[1], slen[1]);
+
+       member = (int *)file[1];
+       equiv(sfile[0], slen[0], sfile[1], slen[1], member);
+       member = (int*)erealloc(member, (slen[1] + 2) * sizeof(int));
+
+       vclass = (int *)file[0];
+       unsort(sfile[0], slen[0], vclass);
+       vclass = (int*)erealloc(vclass, (slen[0] + 2) * sizeof(int));
+
+       klist = (int*)emalloc((slen[0] + 2) * sizeof(int));
+       clen = 0;
+       clistlen = 100;
+       clist = (struct cand*)emalloc(clistlen * sizeof(struct cand));
+       i = stone(vclass, slen[0], member, klist);
+//     free(member);
+//     free(vclass);
+       efree(member);
+       efree(vclass);
+
+       J = (int*)erealloc(J, (len[0] + 2) * sizeof(int));
+       unravel(klist[i]);
+//     free(clist);
+//     free(klist);
+       efree(clist);
+       efree(klist);
+
+       ixold = (long*)erealloc(ixold, (len[0] + 2) * sizeof(long));
+       ixnew = (long*)erealloc(ixnew, (len[1] + 2) * sizeof(long));
+//     check(file1, f1, file2, f2);
+       check (&f1, &f2);
+//     output(file1, f1, file2, f2, (flags & D_HEADER));
+       output(&f1, &f2, (flags & D_HEADER), &out);
+#if 0
+       if (ostdout != -1) {
+               int wstatus;
+
+               /* close the pipe to pr and restore stdout */
+               fflush(stdout);
+               rewind(stdout);
+               if (ostdout != STDOUT_FILENO) {
+                       close(STDOUT_FILENO);
+                       dup2(ostdout, STDOUT_FILENO);
+                       close(ostdout);
+               }
+               waitpid(pid, &wstatus, 0);
+       }
+#endif
+closem:
+//     out.finish ();
+       *pleftc = out.vlc.release ();
+       *pleftline = out.vll.release ();
+       *prightc = out.vrc.release ();
+       *prightline = out.vrl.release ();
+       if (anychange) {
+               status |= 1;
+               if (rval == D_SAME)
+                       rval = D_DIFFER;
+       }
+#if 0
+       if (f1 != NULL)
+               fclose(f1);
+       if (f2 != NULL)
+               fclose(f2);
+       if (file1 != ofile1)
+//             free(file1);
+               efree(file1);
+       if (file2 != ofile2)
+//             free(file2);
+               efree(file2);
+#endif
+       if (J) {
+           efree (J);
+           J = NULL;
+       }
+       if (ixold) {
+           efree (ixold);
+           efree (ixnew);
+           ixold = ixnew = NULL;
+       }
+       if (context_vec_start) {
+           efree (context_vec_start);
+           context_vec_start = NULL;
+       }
+       return (rval);
+}
+
+/*
+ * Check to see if the given files differ.
+ * Returns 0 if they are the same, 1 if different, and -1 on error.
+ * XXX - could use code from cmp(1) [faster]
+ */
+static int
+//files_differ(FILE *f1, FILE *f2, int flags)
+files_differ (BBlockFILE* f1, BBlockFILE* f2, int flags)
+{
+       char buf1[BUFSIZ], buf2[BUFSIZ];
+       size_t i, j;
+
+//     if ((flags & (D_EMPTY1|D_EMPTY2)) || stb1.st_size != stb2.st_size ||
+//         (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT))
+       if ((flags & (D_EMPTY1|D_EMPTY2))
+           || f1->text->length () != f2->text->length ())
+               return (1);
+       for (;;) {
+//             i = fread(buf1, 1, sizeof(buf1), f1);
+//             j = fread(buf2, 1, sizeof(buf2), f2);
+               i = f1->fread(buf1, 1, sizeof(buf1));
+               j = f2->fread(buf2, 1, sizeof(buf2));
+               if (i != j)
+                       return (1);
+               if (i == 0 && j == 0) {
+//                     if (ferror(f1) || ferror(f2))
+                       if (f1->merror() || f2->merror())
+                               return (1);
+                       return (0);
+               }
+               if (memcmp(buf1, buf2, i) != 0)
+                       return (1);
+       }
+}
+
+#if 0
+static FILE *
+opentemp(const char *file)
+{
+       char buf[BUFSIZ], *tempdir, tempfile[MAXPATHLEN];
+       ssize_t nread;
+       int ifd, ofd;
+
+       if (strcmp(file, "-") == 0)
+               ifd = STDIN_FILENO;
+       else if ((ifd = open(file, O_RDONLY, 0644)) < 0)
+               return (NULL);
+
+       if ((tempdir = getenv("TMPDIR")) == NULL)
+//             tempdir = _PATH_TMP;
+           tempdir = "/tmp";
+       
+       if (strlcpy(tempfile, tempdir, sizeof(tempfile)) >= sizeof(tempfile) ||
+           strlcat(tempfile, "/diff.XXXXXXXX", sizeof(tempfile)) >=
+           sizeof(tempfile)) {
+               close(ifd);
+               errno = ENAMETOOLONG;
+               return (NULL);
+       }
+
+       if ((ofd = mkstemp(tempfile)) < 0)
+               return (NULL);
+       unlink(tempfile);
+       while ((nread = read(ifd, buf, BUFSIZ)) > 0) {
+               if (write(ofd, buf, nread) != nread) {
+                       close(ifd);
+                       close(ofd);
+                       return (NULL);
+               }
+       }
+       close(ifd);
+       lseek(ofd, (off_t)0, SEEK_SET);
+       return (fdopen(ofd, "r"));
+}
+#endif
+
+#if 0
+char *
+splice(char *dir, char *file)
+{
+       char *tail, *buf;
+
+       if ((tail = strrchr(file, '/')) == NULL)
+               tail = file;
+       else
+               tail++;
+       easprintf(&buf, "%s/%s", dir, tail);
+       return (buf);
+}
+#endif
+
+static void
+//prepare(int i, FILE *fd, off_t filesize)
+prepare(int i, BBlockFILE *fd)
+{
+       struct line *p;
+       int j, h;
+       size_t sz;
+
+//     rewind(fd);
+       fd->rewind ();
+
+//     sz = (filesize <= SIZE_MAX ? filesize : SIZE_MAX) / 25;
+       sz = (fd->text->length () <= SSIZE_MAX ? fd->text->length () : SSIZE_MAX) / 25;
+       if (sz < 100)
+               sz = 100;
+
+       p = (line*)emalloc((sz + 3) * sizeof(struct line));
+       for (j = 0; (h = readhash(fd));) {
+               if (j == sz) {
+                       sz = sz * 3 / 2;
+                       p = (line*)erealloc(p, (sz + 3) * sizeof(struct line));
+               }
+               p[++j].value = h;
+       }
+       len[i] = j;
+       file[i] = p;
+}
+
+static void
+prune(void)
+{
+       int i, j;
+
+       for (pref = 0; pref < len[0] && pref < len[1] &&
+           file[0][pref + 1].value == file[1][pref + 1].value;
+           pref++)
+               ;
+       for (suff = 0; suff < len[0] - pref && suff < len[1] - pref &&
+           file[0][len[0] - suff].value == file[1][len[1] - suff].value;
+           suff++)
+               ;
+       for (j = 0; j < 2; j++) {
+               sfile[j] = file[j] + pref;
+               slen[j] = len[j] - pref - suff;
+               for (i = 0; i <= slen[j]; i++)
+                       sfile[j][i].serial = i;
+       }
+}
+
+static void
+equiv(struct line *a, int n, struct line *b, int m, int *c)
+{
+       int i, j;
+
+       i = j = 1;
+       while (i <= n && j <= m) {
+               if (a[i].value < b[j].value)
+                       a[i++].value = 0;
+               else if (a[i].value == b[j].value)
+                       a[i++].value = j;
+               else
+                       j++;
+       }
+       while (i <= n)
+               a[i++].value = 0;
+       b[m + 1].value = 0;
+       j = 0;
+       while (++j <= m) {
+               c[j] = -b[j].serial;
+               while (b[j + 1].value == b[j].value) {
+                       j++;
+                       c[j] = b[j].serial;
+               }
+       }
+       c[j] = -1;
+}
+
+/* Code taken from ping.c */
+static int
+isqrt(int n)
+{
+       int y, x = 1;
+
+       if (n == 0)
+               return(0);
+
+       do { /* newton was a stinker */
+               y = x;
+               x = n / x;
+               x += y;
+               x /= 2;
+       } while ((x - y) > 1 || (x - y) < -1);
+
+       return (x);
+}
+
+static int
+stone(int *a, int n, int *b, int *c)
+{
+       int i, k, y, j, l;
+       int oldc, tc, oldl;
+       u_int numtries;
+
+       const u_int bound = dflag ? UINT_MAX : max(256, isqrt(n));
+
+       k = 0;
+       c[0] = newcand(0, 0, 0);
+       for (i = 1; i <= n; i++) {
+               j = a[i];
+               if (j == 0)
+                       continue;
+               y = -b[j];
+               oldl = 0;
+               oldc = c[0];
+               numtries = 0;
+               do {
+                       if (y <= clist[oldc].y)
+                               continue;
+                       l = search(c, k, y);
+                       if (l != oldl + 1)
+                               oldc = c[l - 1];
+                       if (l <= k) {
+                               if (clist[c[l]].y <= y)
+                                       continue;
+                               tc = c[l];
+                               c[l] = newcand(i, y, oldc);
+                               oldc = tc;
+                               oldl = l;
+                               numtries++;
+                       } else {
+                               c[l] = newcand(i, y, oldc);
+                               k++;
+                               break;
+                       }
+               } while ((y = b[++j]) > 0 && numtries < bound);
+       }
+       return (k);
+}
+
+static int
+newcand(int x, int y, int pred)
+{
+       struct cand *q;
+
+       if (clen == clistlen) {
+               clistlen = clistlen * 11 / 10;
+               clist = (struct cand*)erealloc(clist, clistlen * sizeof(struct cand));
+       }
+       q = clist + clen;
+       q->x = x;
+       q->y = y;
+       q->pred = pred;
+       return (clen++);
+}
+
+static int
+search(int *c, int k, int y)
+{
+       int i, j, l, t;
+
+       if (clist[c[k]].y < y)  /* quick look for typical case */
+               return (k + 1);
+       i = 0;
+       j = k + 1;
+       while (1) {
+               l = i + j;
+               if ((l >>= 1) <= i)
+                       break;
+               t = clist[c[l]].y;
+               if (t > y)
+                       j = l;
+               else if (t < y)
+                       i = l;
+               else
+                       return (l);
+       }
+       return (l + 1);
+}
+
+static void
+unravel(int p)
+{
+       struct cand *q;
+       int i;
+
+       for (i = 0; i <= len[0]; i++)
+               J[i] = i <= pref ? i :
+                   i > len[0] - suff ? i + len[1] - len[0] : 0;
+       for (q = clist + p; q->y != 0; q = clist + q->pred)
+               J[q->x + pref] = q->y + pref;
+}
+
+/*
+ * Check does double duty:
+ *  1. ferret out any fortuitous correspondences due
+ *     to confounding by hashing (which result in "jackpot")
+ *  2.  collect random access indexes to the two files
+ */
+static void
+//check(char *file1, FILE *f1, char *file2, FILE *f2)
+check(BBlockFILE *f1, BBlockFILE *f2)
+{
+       int i, j, jackpot, c, d;
+       long ctold, ctnew;
+
+//     rewind(f1);
+//     rewind(f2);
+       f1->rewind ();
+       f2->rewind ();
+       j = 1;
+       ixold[0] = ixnew[0] = 0;
+       jackpot = 0;
+       ctold = ctnew = 0;
+       for (i = 1; i <= len[0]; i++) {
+               if (J[i] == 0) {
+                       ixold[i] = ctold += skipline(f1);
+                       continue;
+               }
+               while (j < J[i]) {
+                       ixnew[j] = ctnew += skipline(f2);
+                       j++;
+               }
+               if (bflag || wflag || iflag) {
+                       for (;;) {
+//                             c = getc(f1);
+//                             d = getc(f2);
+                               c = f1->mgetc ();
+                               d = f2->mgetc ();
+                               /*
+                                * GNU diff ignores a missing newline
+                                * in one file if bflag || wflag.
+                                */
+                               if ((bflag || wflag) &&
+                                   ((c == EOF && d == '\n') ||
+                                   (c == '\n' && d == EOF))) {
+                                       break;
+                               }
+                               ctold++;
+                               ctnew++;
+                               if (bflag && isspace(c) && isspace(d)) {
+                                       do {
+                                               if (c == '\n')
+                                                       break;
+                                               ctold++;
+//                                     } while (isspace(c = getc(f1)));
+                                       } while (isspace(c = f1-> mgetc()));
+                                       do {
+                                               if (d == '\n')
+                                                       break;
+                                               ctnew++;
+//                                     } while (isspace(d = getc(f2)));
+                                       } while (isspace(d = f2->mgetc()));
+                               } else if (wflag) {
+                                       while (isspace(c) && c != '\n') {
+//                                             c = getc(f1);
+                                               c = f1->mgetc();
+                                               ctold++;
+                                       }
+                                       while (isspace(d) && d != '\n') {
+//                                             d = getc(f2);
+                                               d = f2->mgetc();
+                                               ctnew++;
+                                       }
+                               }
+                               if (chrtran[c] != chrtran[d]) {
+                                       jackpot++;
+                                       J[i] = 0;
+                                       if (c != '\n' && c != EOF)
+                                               ctold += skipline(f1);
+                                       if (d != '\n' && c != EOF)
+                                               ctnew += skipline(f2);
+                                       break;
+                               }
+                               if (c == '\n' || c == EOF)
+                                       break;
+                       }
+               } else {
+                       for (;;) {
+                               ctold++;
+                               ctnew++;
+//                             if ((c = getc(f1)) != (d = getc(f2))) {
+                               if ((c = f1->mgetc()) != (d = f2->mgetc())) {
+                                       if ((c == EOF && d == '\n')
+                                           || (c == '\n' && d == EOF))
+                                               break;
+                                       /* jackpot++; */
+                                       J[i] = 0;
+                                       if (c != '\n' && c != EOF)
+                                               ctold += skipline(f1);
+                                       if (d != '\n' && c != EOF)
+                                               ctnew += skipline(f2);
+                                       break;
+                               }
+                               if (c == '\n' || c == EOF)
+                                       break;
+                       }
+               }
+               ixold[i] = ctold;
+               ixnew[j] = ctnew;
+               j++;
+       }
+       for (; j <= len[1]; j++)
+               ixnew[j] = ctnew += skipline(f2);
+       /*
+        * if (jackpot)
+        *      fprintf(stderr, "jackpot\n");
+        */
+}
+
+/* shellsort CACM #201 */
+static void
+sort(struct line *a, int n)
+{
+       struct line *ai, *aim, w;
+       int j, m = 0, k;
+
+       if (n == 0)
+               return;
+       for (j = 1; j <= n; j *= 2)
+               m = 2 * j - 1;
+       for (m /= 2; m != 0; m /= 2) {
+               k = n - m;
+               for (j = 1; j <= k; j++) {
+                       for (ai = &a[j]; ai > a; ai -= m) {
+                               aim = &ai[m];
+                               if (aim < ai)
+                                       break;  /* wraparound */
+                               if (aim->value > ai[0].value ||
+                                   (aim->value == ai[0].value &&
+                                       aim->serial > ai[0].serial))
+                                       break;
+                               w.value = ai[0].value;
+                               ai[0].value = aim->value;
+                               aim->value = w.value;
+                               w.serial = ai[0].serial;
+                               ai[0].serial = aim->serial;
+                               aim->serial = w.serial;
+                       }
+               }
+       }
+}
+
+static void
+unsort(struct line *f, int l, int *b)
+{
+       int *a, i;
+
+       a = (int*)emalloc((l + 1) * sizeof(int));
+       for (i = 1; i <= l; i++)
+               a[f[i].serial] = f[i].value;
+       for (i = 1; i <= l; i++)
+               b[i] = a[i];
+//     free(a);
+       efree(a);
+}
+
+static int
+//skipline(FILE *f)
+skipline(BBlockFILE *f)
+{
+       int i, c;
+
+//     for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++)
+       for (i = 1; (c = f->mgetc()) != '\n' && c != EOF; i++)
+               continue;
+       return (i);
+}
+
+static void
+//output(char *file1, FILE *f1, char *file2, FILE *f2, int flags)
+//output(BBlockFILE* f1, BBlockFILE* f2, int flags, OutputVar* out)
+output(BBlockFILE* f1, BBlockFILE* f2, int flags, OutputList* out)
+{
+       int m, i0, i1, j0, j1;
+
+//     rewind(f1);
+//     rewind(f2);
+       f1->rewind();
+       f2->rewind();
+       m = len[0];
+       J[0] = 0;
+       J[m + 1] = len[1] + 1;
+       if (format != D_EDIT) {
+               for (i0 = 1; i0 <= m; i0 = i1 + 1) {
+                       while (i0 <= m && J[i0] == J[i0 - 1] + 1)
+                               i0++;
+                       j0 = J[i0 - 1] + 1;
+                       i1 = i0 - 1;
+                       while (i1 < m && J[i1 + 1] == 0)
+                               i1++;
+                       j1 = J[i1 + 1] - 1;
+                       J[i1] = j1;
+//                     change(file1, f1, file2, f2, i0, i1, j0, j1, &flags);
+                       change(f1, f2, i0, i1, j0, j1, &flags, out);
+               }
+       } else {
+               for (i0 = m; i0 >= 1; i0 = i1 - 1) {
+                       while (i0 >= 1 && J[i0] == J[i0 + 1] - 1 && J[i0] != 0)
+                               i0--;
+                       j0 = J[i0 + 1] - 1;
+                       i1 = i0 + 1;
+                       while (i1 > 1 && J[i1 - 1] == 0)
+                               i1--;
+                       j1 = J[i1 - 1] + 1;
+                       J[i1] = j1;
+//                     change(file1, f1, file2, f2, i1, i0, j1, j0, &flags);
+                       change(f1, f2, i1, i0, j1, j0, &flags, out);
+               }
+       }
+       if (m == 0)
+//             change(file1, f1, file2, f2, 1, 0, 1, len[1], &flags);
+               change(f1, f2, 1, 0, 1, len[1], &flags, out);
+#if 0
+       if (format == D_IFDEF) {
+               for (;;) {
+#define        c i0
+                       if ((c = getc(f1)) == EOF)
+                               return;
+                       putc(c, stdout);
+               }
+#undef c
+       }
+#endif
+       if (anychange != 0) {
+               if (format == D_CONTEXT)
+                       dump_context_vec(f1, f2, out);
+#if 0
+               else if (format == D_UNIFIED)
+                       dump_unified_vec(f1, f2);
+#endif
+       }
+}
+
+#if 0
+static __inline void
+range(int a, int b, char *separator)
+{
+       fprintf(stdout, "%d", a > b ? b : a);
+//     if (a < b)
+               fprintf(stdout, "%s%d", separator, b);
+}
+#endif
+#if 0
+static __inline void
+uni_range(int a, int b)
+{
+       if (a < b)
+               fprintf(stdout, "%d,%d", a, b - a + 1);
+       else if (a == b)
+               fprintf(stdout, "%d", b);
+       else
+               fprintf(stdout, "%d,0", b);
+}
+#endif
+#if 0
+static char *
+preadline(int fd, size_t len, off_t off)
+{
+       char *line;
+       ssize_t nr;
+
+       line = (char*)emalloc(len + 1);
+       if ((nr = pread(fd, line, len, off)) < 0)
+               err(1, "preadline");
+       if (nr > 0 && line[nr-1] == '\n')
+               nr--;
+       line[nr] = '\0';
+       return (line);
+}
+#endif
+#if 0
+static int
+ignoreline(char *line)
+{
+       int ret;
+
+       ret = regexec(&ignore_re, line, 0, NULL, 0);
+//     free(line);
+       efree(line);
+       return (ret == 0);      /* if it matched, it should be ignored. */
+}
+#endif
+
+/*
+ * Indicate that there is a difference between lines a and b of the from file
+ * to get to lines c to d of the to file.  If a is greater then b then there
+ * are no lines in the from file involved and this means that there were
+ * lines appended (beginning at b).  If c is greater than d then there are
+ * lines missing from the to file.
+ */
+static void
+//change(char *file1, FILE *f1, char *file2, FILE *f2, int a, int b, int c, int d, int *pflags)
+//change(BBlockFILE* f1, BBlockFILE* f2, int a, int b, int c, int d, int *pflags, OutputVar* out)
+change(BBlockFILE* f1, BBlockFILE* f2, int a, int b, int c, int d, int *pflags, OutputList* out)
+{
+       static size_t max_context = 64;
+       int i;
+
+restart:
+       if (format != D_IFDEF && a > b && c > d)
+               return;
+#if 0
+       if (ignore_pats != NULL) {
+               char *line;
+               /*
+                * All lines in the change, insert, or delete must
+                * match an ignore pattern for the change to be
+                * ignored.
+                */
+               if (a <= b) {           /* Changes and deletes. */
+                       for (i = a; i <= b; i++) {
+                               line = preadline(fileno(f1),
+                                   ixold[i] - ixold[i - 1], ixold[i - 1]);
+                               if (!ignoreline(line))
+                                       goto proceed;
+                       }
+               }
+               if (a > b || c <= d) {  /* Changes and inserts. */
+                       for (i = c; i <= d; i++) {
+                               line = preadline(fileno(f2),
+                                   ixnew[i] - ixnew[i - 1], ixnew[i - 1]);
+                               if (!ignoreline(line))
+                                       goto proceed;
+                       }
+               }
+               return;
+       }
+#endif
+proceed:
+       if (*pflags & D_HEADER) {
+//             printf("%s %s %s\n", diffargs, file1, file2);
+//             fprintf (stdout, "\n");
+               *pflags &= ~D_HEADER;
+       }
+       if (format == D_CONTEXT || format == D_UNIFIED) {
+               /*
+                * Allocate change records as needed.
+                */
+               if (context_vec_ptr == context_vec_end - 1) {
+                       ptrdiff_t offset = context_vec_ptr - context_vec_start;
+                       max_context <<= 1;
+                       context_vec_start = (struct context_vec*)erealloc(context_vec_start,
+                           max_context * sizeof(struct context_vec));
+                       context_vec_end = context_vec_start + max_context;
+                       context_vec_ptr = context_vec_start + offset;
+               }
+               if (anychange == 0) {
+                       /*
+                        * Print the context/unidiff header first time through.
+                        */
+//                     print_header(file1, file2);
+//                     print_header();
+                       anychange = 1;
+               } else if (a > context_vec_ptr->b + (2 * context) + 1 &&
+                   c > context_vec_ptr->d + (2 * context) + 1) {
+                       /*
+                        * If this change is more than 'context' lines from the
+                        * previous change, dump the record and reset it.
+                        */
+                       if (format == D_CONTEXT)
+                               dump_context_vec(f1, f2, out);
+#if 0
+                       else
+                               dump_unified_vec(f1, f2);
+#endif
+               }
+               context_vec_ptr++;
+               context_vec_ptr->a = a;
+               context_vec_ptr->b = b;
+               context_vec_ptr->c = c;
+               context_vec_ptr->d = d;
+               return;
+       }
+#if 0
+       if (anychange == 0)
+               anychange = 1;
+#if 0
+       switch (format) {
+       case D_BRIEF:
+               return;
+       case D_NORMAL:
+       case D_EDIT:
+               range(a, b, ",");
+               putc(a > b ? 'a' : c > d ? 'd' : 'c', stdout);
+               if (format == D_NORMAL)
+                       range(c, d, ",");
+               putc('\n', stdout);
+               break;
+       case D_REVERSE:
+               putc(a > b ? 'a' : c > d ? 'd' : 'c', stdout);
+               range(a, b, " ");
+               putc('\n', stdout);
+               break;
+       case D_NREVERSE:
+               if (a > b)
+                       fprintf(stdout, "a%d %d\n", b, d - c + 1);
+               else {
+                       fprintf(stdout, "d%d %d\n", a, b - a + 1);
+                       if (!(c > d))
+                               /* add changed lines */
+                               fprintf(stdout, "a%d %d\n", b, d - c + 1);
+               }
+               break;
+       }
+#endif
+#if 0
+       if (format == D_NORMAL || format == D_IFDEF) {
+               fetch(ixold, a, b, f1, '<', 1);
+               if (a <= b && c <= d && format == D_NORMAL)
+                       fputs("---", stdout);
+       }
+#endif
+//     i = fetch(ixnew, c, d, f2, format == D_NORMAL ? '>' : '\0', 0);
+       i = fetch(ixnew, c, d, f2, format == D_NORMAL ? '>' : '\0');
+#if 0
+       if (i != 0 && format == D_EDIT) {
+               /*
+                * A non-zero return value for D_EDIT indicates that the
+                * last line printed was a bare dot (".") that has been
+                * escaped as ".." to prevent ed(1) from misinterpreting
+                * it.  We have to add a substitute command to change this
+                * back and restart where we left off.
+                */
+               fputs(".", stdout);
+               fprintf(stdout, "%ds/^\\.\\././\n", a);
+               a += i;
+               c += i;
+               goto restart;
+       }
+#endif
+#if 0
+       if ((format == D_EDIT || format == D_REVERSE) && c <= d)
+               fputs(".", stdout);
+#endif
+#if 0
+       if (inifdef) {
+//             fprintf(stdout, "#endif /* %s */\n", ifdefname);
+               inifdef = 0;
+       }
+#endif
+#endif
+}
+
+#if 0
+static int
+//fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile)
+fetch(long *f, int a, int b, BBlockFILE*lb, int ch, int oldfile)
+{
+//     int i, j, c, lastc, col, nc;
+       int i, j, c, nc;
+       BBlock  p;
+
+#if 0
+       /*
+        * When doing #ifdef's, copy down to current line
+        * if this is the first file, so that stuff makes it to output.
+        */
+       if (format == D_IFDEF && oldfile) {
+               long curpos = ftell(lb);
+               /* print through if append (a>b), else to (nb: 0 vs 1 orig) */
+               nc = f[a > b ? b : a - 1] - curpos;
+               for (i = 0; i < nc; i++)
+                       putc(getc(lb), stdout);
+       }
+#endif
+       if (a > b)
+               return (0);
+#if 0
+       if (format == D_IFDEF) {
+               if (inifdef) {
+                       fprintf(stdout, "#else /* %s%s */\n",
+                           oldfile == 1 ? "!" : "", ifdefname);
+               } else {
+                       if (oldfile)
+                               fprintf(stdout,"#ifndef %s\n", ifdefname);
+                       else
+                               fprintf(stdout,"#ifdef %s\n", ifdefname);
+               }
+               inifdef = 1 + oldfile;
+       }
+#endif
+       for (i = a; i <= b; i++) {
+#if 0
+//             fseek(lb, f[i - 1], SEEK_SET);
+               lb->fseek(f[i - 1], SEEK_SET);
+#endif
+               nc = f[i] - f[i - 1];
+               if (format != D_IFDEF && ch != '\0') {
+                       /* 1カラム目 */
+                       putc(ch, stdout);
+#if 0
+                       if (Tflag && (format == D_NORMAL || format == D_CONTEXT
+                           || format == D_UNIFIED))
+                               putc('\t', stdout);
+                       else if (format != D_UNIFIED)
+#endif
+                           /* 2カラム目の空白*/
+//                             putc(' ', stdout);
+               }
+#if 0
+               col = 0;
+               for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) {
+//                     if ((c = getc(lb)) == EOF) {
+                       if ((c = lb->mgetc()) == EOF) {
+#if 0
+                               if (format == D_EDIT
+                                   || format == D_REVERSE
+                                   || format == D_NREVERSE)
+                                       warnx("No newline at end of file");
+                               else
+#endif
+                                       fputs("\n\\ No newline at end of file", stdout);
+                               return (0);
+                       }
+#if 0
+                       if (c == '\t' && tflag) {
+                               do {
+                                       putc(' ', stdout);
+                               } while (++col & 7);
+                       } else {
+#endif
+#if 0
+                               if (format == D_EDIT && j == 1 && c == '\n'
+                                   && lastc == '.') {
+                                       /*
+                                        * Don't print a bare "." line
+                                        * since that will confuse ed(1).
+                                        * Print ".." instead and return,
+                                        * giving the caller an offset
+                                        * from which to restart.
+                                        */
+                                       fputs(".", stdout);
+                                       return (i - a + 1);
+                               }
+#endif
+                               putc(c, stdout);
+                               col++;
+#if 0
+                       }
+#endif
+               }
+#endif
+               /* 行末のLFが無いかもしれない */
+               p.assign (&(*lb->text)[f[i - 1]], min(lb->text->length - f[i - 1], nc));
+               pfprintf (stdout, "%B", &p);
+               if (p.length == 0 || p.last () != '\n')
+                       putc ('\n', stdout);
+       }
+       return (0);
+}
+#endif
+#if 0
+static int  fetch(long *f, int a, int b, BBlockFILE*lb, int ch) {
+       int i, j, c, nc;
+       BBlock  p;
+
+       for (i = a; i <= b; i++) {
+               nc = f[i] - f[i - 1];
+               if (ch != '\0') {
+                       /* 1カラム目 */
+                       putc(ch, stdout);
+               }
+               /* 行末のLFが無いかもしれない */
+               p.assign (&(*lb->text)[f[i - 1]], min(lb->text->length - f[i - 1], nc));
+               pfprintf (stdout, "%B", &p);
+               if (p.length == 0 || p.last () != '\n')
+                       putc ('\n', stdout);
+       }
+       return (0);
+}
+#endif
+
+//static void  fetchline (long* f, int i, BBlockFILE* lb, BBlock* ans) {
+static void  fetchline (long* f, int i, BBlockFILE* lb, ustring* ans) {
+    ans->assign (&(*lb->text)[f[i - 1]], min (lb->text->length () - f[i - 1], f[i] - f[i - 1]));
+//    if (ans->length > 0 && ans->last () == '\n')
+//     ans->pop ();
+    if (ans->length () > 0 && (*ans)[ans->length () - 1] == '\n')
+       ans->resize (ans->length () - 1);
+}
+
+/*
+ * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578.
+ */
+static int
+//readhash(FILE *f)
+readhash(BBlockFILE *f)
+{
+       int i, t, space;
+       int sum;
+
+       sum = 1;
+       space = 0;
+       if (!bflag && !wflag) {
+               if (iflag)
+//                     for (i = 0; (t = getc(f)) != '\n'; i++) {
+                       for (i = 0; (t = f->mgetc()) != '\n'; i++) {
+                               if (t == EOF) {
+                                       if (i == 0)
+                                               return (0);
+                                       break;
+                               }
+                               sum = sum * 127 + chrtran[t];
+                       }
+               else
+//                     for (i = 0; (t = getc(f)) != '\n'; i++) {
+                       for (i = 0; (t = f->mgetc()) != '\n'; i++) {
+                               if (t == EOF) {
+                                       if (i == 0)
+                                               return (0);
+                                       break;
+                               }
+                               sum = sum * 127 + t;
+                       }
+       } else {
+               for (i = 0;;) {
+//                     switch (t = getc(f)) {
+                       switch (t = f->mgetc()) {
+                       case '\t':
+                       case '\r':
+                       case '\v':
+                       case '\f':
+                       case ' ':
+                               space++;
+                               continue;
+                       default:
+                               if (space && !wflag) {
+                                       i++;
+                                       space = 0;
+                               }
+                               sum = sum * 127 + chrtran[t];
+                               i++;
+                               continue;
+                       case EOF:
+                               if (i == 0)
+                                       return (0);
+                               /* FALLTHROUGH */
+                       case '\n':
+                               break;
+                       }
+                       break;
+               }
+       }
+       /*
+        * There is a remote possibility that we end up with a zero sum.
+        * Zero is used as an EOF marker, so return 1 instead.
+        */
+       return (sum == 0 ? 1 : sum);
+}
+
+#if 0
+static int
+asciifile(FILE *f)
+{
+       unsigned char buf[BUFSIZ];
+       int i, cnt;
+
+       if (aflag || f == NULL)
+               return (1);
+
+       rewind(f);
+       cnt = fread(buf, 1, sizeof(buf), f);
+       for (i = 0; i < cnt; i++)
+               if (!isprint(buf[i]) && !isspace(buf[i]))
+                       return (0);
+       return (1);
+}
+#endif
+
+static __inline int min(int a, int b)
+{
+       return (a < b ? a : b);
+}
+
+static __inline int max(int a, int b)
+{
+       return (a > b ? a : b);
+}
+
+#define begins_with(s, pre) (strncmp(s, pre, sizeof(pre)-1) == 0)
+
+#if 0
+static char *
+//match_function(const long *f, int pos, FILE *file)
+match_function(const long *f, int pos, BBlockFILE *file)
+{
+       unsigned char buf[FUNCTION_CONTEXT_SIZE];
+       size_t nc;
+       int last = lastline;
+       char *p;
+       char *state = NULL;
+
+       lastline = pos;
+       while (pos > last) {
+//             fseek(file, f[pos - 1], SEEK_SET);
+               file->fseek(f[pos - 1], SEEK_SET);
+               nc = f[pos] - f[pos - 1];
+               if (nc >= sizeof(buf))
+                       nc = sizeof(buf) - 1;
+//             nc = fread(buf, 1, nc, file);
+               nc = file->fread(buf, 1, nc);
+               if (nc > 0) {
+                       buf[nc] = '\0';
+                       p = strchr(char_type (buf), '\n');
+                       if (p != NULL)
+                               *p = '\0';
+                       if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') {
+                               if (begins_with(char_type (buf), "private:")) {
+                                       if (!state)
+                                               state = " (private)";
+                               } else if (begins_with(char_type (buf), "protected:")) {
+                                       if (!state)
+                                               state = " (protected)";
+                               } else if (begins_with(char_type (buf), "public:")) {
+                                       if (!state)
+                                               state = " (public)";
+                               } else {
+                                       strlcpy(lastbuf, char_type (buf), sizeof lastbuf);
+                                       if (state)
+                                               strlcat(lastbuf, state, 
+                                                   sizeof lastbuf);
+                                       lastmatchline = pos;
+                                       return lastbuf;
+                               }
+                       }
+               }
+               pos--;
+       }
+       return lastmatchline > 0 ? lastbuf : NULL;
+}
+#endif
+
+#if 0
+/* dump accumulated "context" diff changes */
+static void
+//dump_context_vec(FILE *f1, FILE *f2)
+dump_context_vec(BBlockFILE*f1, BBlockFILE*f2)
+{
+       struct context_vec *cvp = context_vec_start;
+       int lowa, upb, lowc, upd, do_output;
+       int a, b, c, d;
+       char ch, *f;
+
+       if (context_vec_start > context_vec_ptr)
+               return;
+
+       b = d = 0;              /* gcc */
+       lowa = max(1, cvp->a - context);
+       upb = min(len[0], context_vec_ptr->b + context);
+       lowc = max(1, cvp->c - context);
+       upd = min(len[1], context_vec_ptr->d + context);
+
+//     fprintf(stdout,"***************");
+#if 0
+       if (pflag) {
+               f = match_function(ixold, lowa-1, f1);
+               if (f != NULL) {
+                       putc(' ', stdout);
+                       fputs(f, stdout);
+               }
+       }
+#endif
+    fprintf (stdout, "context:%d", context);
+       fprintf(stdout,"\n*** ");
+       range(lowa, upb, ",");
+       fprintf(stdout," ****(old)\n");
+
+       /*
+        * Output changes to the "old" file.  The first loop suppresses
+        * output if there were no changes to the "old" file (we'll see
+        * the "old" lines as context in the "new" list).
+        */
+       do_output = 0;
+       for (; cvp <= context_vec_ptr; cvp++)
+               if (cvp->a <= cvp->b) {
+                       cvp = context_vec_start;
+                       do_output++;
+                       break;
+               }
+       if (do_output) {
+               while (cvp <= context_vec_ptr) {
+                       a = cvp->a;
+                       b = cvp->b;
+                       c = cvp->c;
+                       d = cvp->d;
+
+                       if (a <= b && c <= d)
+                               ch = 'c';
+                       else
+                               ch = (a <= b) ? 'd' : 'a';
+
+                       if (ch == 'a')
+//                             fetch(ixold, lowa, b, f1, ' ', 0);
+                               fetch(ixold, lowa, b, f1, ' ');
+                       else {
+//                             fetch(ixold, lowa, a - 1, f1, ' ', 0);
+//                             fetch(ixold, a, b, f1, ch == 'c' ? '!' : '-', 0);
+                               fetch(ixold, lowa, a - 1, f1, ' ');
+                               fetch(ixold, a, b, f1, ch == 'c' ? '!' : '-');
+                       }
+                       lowa = b + 1;
+                       cvp++;
+               }
+//             fetch(ixold, b + 1, upb, f1, ' ', 0);
+               fetch(ixold, b + 1, upb, f1, ' ');
+       }
+       /* output changes to the "new" file */
+       fprintf(stdout,"--- ");
+       range(lowc, upd, ",");
+       fprintf(stdout," ----(new)\n");
+
+       do_output = 0;
+       for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++)
+               if (cvp->c <= cvp->d) {
+                       cvp = context_vec_start;
+                       do_output++;
+                       break;
+               }
+       if (do_output) {
+               while (cvp <= context_vec_ptr) {
+                       a = cvp->a;
+                       b = cvp->b;
+                       c = cvp->c;
+                       d = cvp->d;
+
+                       if (a <= b && c <= d)
+                               ch = 'c';
+                       else
+                               ch = (a <= b) ? 'd' : 'a';
+
+                       if (ch == 'd')
+//                             fetch(ixnew, lowc, d, f2, ' ', 0);
+                               fetch(ixnew, lowc, d, f2, ' ');
+                       else {
+//                             fetch(ixnew, lowc, c - 1, f2, ' ', 0);
+//                             fetch(ixnew, c, d, f2, ch == 'c' ? '!' : '+', 0);
+                               fetch(ixnew, lowc, c - 1, f2, ' ');
+                               fetch(ixnew, c, d, f2, ch == 'c' ? '!' : '+');
+                       }
+                       lowc = d + 1;
+                       cvp++;
+               }
+//             fetch(ixnew, d + 1, upd, f2, ' ', 0);
+               fetch(ixnew, d + 1, upd, f2, ' ');
+       }
+       context_vec_ptr = context_vec_start - 1;
+}
+#else
+#if 0
+static void  testout (char c1, BBlock* p1, char c2, BBlock* p2) {
+    int  i, col;
+    int  ch;
+
+    pfprintf (stdout, "%c", c1);
+    for (i = 0, col = 0; i < p1->length && col < 35; i ++, col ++) {
+       if ((ch = (*p1)[i]) == '\t') {
+           putc (' ', stdout);
+           for (col ++; col % 8 == 0; col ++)
+               putc (' ', stdout);
+           col --;
+       } else {
+           putc (ch, stdout);
+       }
+    }
+    for (; col < 35; col ++)
+       putc (' ', stdout);
+    pfprintf (stdout, " |%c", c2);
+    for (i = 0, col = 0; i < p2->length && col < 35; i ++, col ++) {
+       if ((ch = (*p2)[i]) == '\t') {
+           putc (' ', stdout);
+           for (col ++; col % 8 == 0; col ++)
+               putc (' ', stdout);
+           col --;
+       } else {
+           putc (ch, stdout);
+       }
+    }
+    putc ('\n', stdout);
+}
+#endif
+
+static void
+//dump_context_vec(FILE *f1, FILE *f2)
+//dump_context_vec(BBlockFILE*f1, BBlockFILE*f2, OutputVar* out)
+dump_context_vec(BBlockFILE*f1, BBlockFILE*f2, OutputList* out)
+{
+    struct context_vec *cvp = context_vec_start;
+    int lowa, upb, lowc, upd, do_output;
+    int a, b, c, d;
+    char ch;
+    int  i, n;
+//    BBlock  p1, p2;
+    ustring  p1, p2;
+    int  c1, c2;
+
+    if (context_vec_start > context_vec_ptr)
+       return;
+
+    b = d = 0;         /* gcc */
+    lowa = max(1, cvp->a - context);
+    upb = min(len[0], context_vec_ptr->b + context);
+    lowc = max(1, cvp->c - context);
+    upd = min(len[1], context_vec_ptr->d + context);
+    
+#if 0
+    fprintf(stdout,"***************");
+    fprintf(stdout,"\n*** ");
+    range(lowa, upb, ",");
+    fprintf (stdout, "  ");
+    range(lowc, upd, ",");
+    fprintf(stdout," ****\n");
+#endif
+    out->linenum (lowa, lowc);
+
+    do_output = 0;
+    for (; cvp <= context_vec_ptr; cvp++)
+       if (cvp->a <= cvp->b || cvp->c <= cvp->d) {
+           cvp = context_vec_start;
+           do_output++;
+           break;
+       }
+    if (do_output) {
+       while (cvp <= context_vec_ptr) {
+           a = cvp->a;
+           b = cvp->b;
+           c = cvp->c;
+           d = cvp->d;
+
+           if (a <= b && c <= d)
+               ch = 'c';
+           else
+               ch = (a <= b) ? 'd' : 'a';
+           
+           switch (ch) {
+           case 'a':
+#if 0
+               printf ("<<<\n");
+               fetch(ixold, lowa, b, f1, ' ');
+               printf (">>>\n");
+               fetch(ixnew, lowc, c - 1, f2, ' ');
+               fetch(ixnew, c, d, f2, ch == 'c' ? '!' : '+');
+#endif
+               n = max (b - lowa, c - 1 - lowc);
+               for (i = 0; i <= n; i ++) {
+                   if (lowa + i <= b) {
+                       fetchline (ixold, lowa + i, f1, &p1);
+                       c1 = 1;
+                   } else {
+//                     p1.init ();
+                       p1.resize (0);
+                       c1 = 0;
+                   }
+                   if (lowc + i <= c - 1) {
+                       fetchline (ixnew, lowc + i, f2, &p2);
+                       c2 = 1;
+                   } else {
+//                     p2.init ();
+                       p2.resize (0);
+                       c2 = 0;
+                   }
+//                 pfprintf (stdout, "< %B\n", &p1);
+//                 pfprintf (stdout, "> %B\n", &p2);
+//                 testout (' ', &p1, ' ', &p2);
+                   out->out (c1?' ':'*', &p1, c2?' ':'*', &p2);
+               }
+               n = d - c;
+               for (i = 0; i <= n; i ++) {
+//                 p1.init ();
+                   p1.resize (0);
+                   fetchline (ixnew, c + i, f2, &p2);
+                   if (ch == 'c') {
+//                     pfprintf (stdout, "<*\n");
+//                     pfprintf (stdout, ">!%B\n", &p2);
+//                     testout ('*', &p1, '!', &p2);
+//                     out->out ('*', &p1, '!', &p2);
+                       out->out ('*', &p1, '+', &p2);
+                   } else {
+//                     pfprintf (stdout, "<*\n");
+//                     pfprintf (stdout, ">+%B\n", &p2);
+//                     testout ('*', &p1, '+', &p2);
+                       out->out ('*', &p1, '+', &p2);
+                   }
+               }
+               break;
+           case 'c':
+#if 0
+               printf ("<<<\n");
+               fetch(ixold, lowa, a - 1, f1, ' ');
+               fetch(ixold, a, b, f1, ch == 'c' ? '!' : '-');
+               printf (">>>\n");
+               fetch(ixnew, lowc, c - 1, f2, ' ');
+               fetch(ixnew, c, d, f2, ch == 'c' ? '!' : '+');
+#endif
+               n = max (a - 1 - lowa, c - 1 - lowc);
+               for (i = 0; i <= n; i ++) {
+                   if (lowa + i <= a - 1) {
+                       fetchline (ixold, lowa + i, f1, &p1);
+                       c1 = 1;
+                   } else {
+//                     p1.init ();
+                       p1.resize (0);
+                       c1 = 0;
+                   }
+                   if (lowc + i <= c - 1) {
+                       fetchline (ixnew, lowc + i, f2, &p2);
+                       c2 = 1;
+                   } else {
+//                     p2.init ();
+                       p2.resize (0);
+                       c2 = 0;
+                   }
+//                 pfprintf (stdout, "> %B\n", &p1);
+//                 pfprintf (stdout, "< %B\n", &p2);
+//                 testout (' ', &p1, ' ', &p2);
+                   out->out (c1?' ':'*', &p1, c2?' ':'*', &p2);
+               }
+               n = max (b - a, d - c);
+               for (i = 0; i <= n; i ++) {
+                   if (a + i <= b) {
+                       fetchline (ixold, a + i, f1, &p1);
+                       c1 = 1;
+                   } else {
+//                     p1.init ();
+                       p1.resize (0);
+                       c1 = 0;
+                   }
+                   if (c + i <= d) {
+                       fetchline (ixnew, c + i, f2, &p2);
+                       c2 = 1;
+                   } else {
+//                     p2.init ();
+                       p2.resize (0);
+                       c2 = 0;
+                   }
+                   if (ch == 'c') {
+//                     pfprintf (stdout, ">!%B\n", &p1);
+//                     pfprintf (stdout, "<!%B\n", &p2);
+//                     testout ('!', &p1, '!', &p2);
+//                     out->out (c1?'!':'*', &p1, c2?'!':'*', &p2);
+                       out->out (c1?'-':'*', &p1, c2?'+':'*', &p2);
+                   } else {
+//                     pfprintf (stdout, ">-%B\n", &p1);
+//                     pfprintf (stdout, "<+%B\n", &p2);
+//                     testout ('-', &p1, '+', &p2);
+                       out->out (c1?'-':'*', &p1, c2?'+':'*', &p2);
+                   }
+               }
+               break;
+           case 'd':
+#if 0
+               printf ("<<<\n");
+               fetch(ixold, lowa, a - 1, f1, ' ');
+               fetch(ixold, a, b, f1, ch == 'c' ? '!' : '-');
+               printf (">>>\n");
+               fetch(ixnew, lowc, d, f2, ' ');
+#endif
+               n = max (a - 1 - lowa, d - lowc);
+               for (i = 0; i <= n; i ++) {
+                   if (lowa + i <= a - 1) {
+                       fetchline (ixold, lowa + i, f1, &p1);
+                       c1 = 1;
+                   } else {
+//                     p1.init ();
+                       p1.resize (0);
+                       c1 =0;
+                   }
+                   if (lowc + i <= d) {
+                       fetchline (ixnew, lowc + i, f2, &p2);
+                       c2 = 1;
+                   } else {
+//                     p2.init ();
+                       p2.resize (0);
+                       c2 = 0;
+                   }
+//                 pfprintf (stdout, "> %B\n", &p1);
+//                 pfprintf (stdout, "< %B\n", &p2);
+//                 testout (' ', &p1, ' ', &p2);
+                   out->out (c1?' ':'*', &p1, c2?' ':'*', &p2);
+               }
+               n = b - a;
+               for (i = 0; i <= n; i ++) {
+                   fetchline (ixold, a + i, f1, &p1);
+//                 p2.init ();
+                   p2.resize (0);
+                   if (ch == 'c') {
+//                     pfprintf (stdout, ">!%B\n", &p1);
+//                     pfprintf (stdout, "<*\n");
+//                     testout ('!', &p1, '*', &p2);
+//                     out->out ('!', &p1, '*', &p2);
+                       out->out ('-', &p1, '*', &p2);
+                   } else {
+//                     pfprintf (stdout, ">-%B\n", &p1);
+//                     pfprintf (stdout, "<*\n");
+//                     testout ('-', &p1, '*', &p2);
+                       out->out ('-', &p1, '*', &p2);
+                   }
+               }
+               break;
+           }
+           lowa = b + 1;
+           lowc = d + 1;
+           cvp++;
+       }
+//     printf ("<<<\n");
+//     fetch(ixold, b + 1, upb, f1, ' ');
+//     printf (">>>\n");
+//     fetch(ixnew, d + 1, upd, f2, ' ');
+       n = max (upb - b - 1, upd - d - 1);
+       for (i = 0; i <= n; i ++) {
+           if (b + 1 + i <= upb) {
+               fetchline (ixold, b + 1 + i, f1, &p1);
+               c1 = 1;
+           } else {
+//             p1.init ();
+               p1.resize (0);
+               c1 = 0;
+           }
+           if (d + 1 + i <= upd) {
+               fetchline (ixnew, d + 1 + i, f2, &p2);
+               c2 = 1;
+           } else {
+//             p2.init ();
+               p2.resize (0);
+               c2 = 0;
+           }
+//         testout (' ', &p1, ' ', &p2);
+           out->out (c1?' ':'*', &p1, c2?' ':'*', &p2);
+       }
+    }
+    context_vec_ptr = context_vec_start - 1;
+}
+#endif
+
+#if 0
+/* dump accumulated "unified" diff changes */
+static void
+//dump_unified_vec(FILE *f1, FILE *f2)
+dump_unified_vec(BBlockFILE* f1, BBlockFILE* f2)
+{
+       struct context_vec *cvp = context_vec_start;
+       int lowa, upb, lowc, upd;
+       int a, b, c, d;
+       char ch, *f;
+
+       if (context_vec_start > context_vec_ptr)
+               return;
+
+       b = d = 0;              /* gcc */
+       lowa = max(1, cvp->a - context);
+       upb = min(len[0], context_vec_ptr->b + context);
+       lowc = max(1, cvp->c - context);
+       upd = min(len[1], context_vec_ptr->d + context);
+
+       fputs("@@ -", stdout);
+       uni_range(lowa, upb);
+       fputs(" +", stdout);
+       uni_range(lowc, upd);
+       fputs(" @@", stdout);
+#if 0
+       if (pflag) {
+               f = match_function(ixold, lowa-1, f1);
+               if (f != NULL) {
+                       putc(' ', stdout);
+                       fputs(f, stdout);
+               }
+       }
+#endif
+       putc('\n', stdout);
+
+       /*
+        * Output changes in "unified" diff format--the old and new lines
+        * are printed together.
+        */
+       for (; cvp <= context_vec_ptr; cvp++) {
+               a = cvp->a;
+               b = cvp->b;
+               c = cvp->c;
+               d = cvp->d;
+
+               /*
+                * c: both new and old changes
+                * d: only changes in the old file
+                * a: only changes in the new file
+                */
+               if (a <= b && c <= d)
+                       ch = 'c';
+               else
+                       ch = (a <= b) ? 'd' : 'a';
+
+               switch (ch) {
+               case 'c':
+                       fetch(ixold, lowa, a - 1, f1, ' ', 0);
+                       fetch(ixold, a, b, f1, '-', 0);
+                       fetch(ixnew, c, d, f2, '+', 0);
+                       break;
+               case 'd':
+                       fetch(ixold, lowa, a - 1, f1, ' ', 0);
+                       fetch(ixold, a, b, f1, '-', 0);
+                       break;
+               case 'a':
+                       fetch(ixnew, lowc, c - 1, f2, ' ', 0);
+                       fetch(ixnew, c, d, f2, '+', 0);
+                       break;
+               }
+               lowa = b + 1;
+               lowc = d + 1;
+       }
+       fetch(ixnew, d + 1, upd, f2, ' ', 0);
+
+       context_vec_ptr = context_vec_start - 1;
+}
+#endif
+
+#if 0
+static void
+//print_header(const char *file1, const char *file2)
+print_header ()
+{
+#if 0
+       if (label[0] != NULL)
+               fprintf(stdout,"%s %s\n", format == D_CONTEXT ? "***" : "---",
+                   label[0]);
+       else
+#endif
+//             fprintf(stdout,"%s %s\t%s", format == D_CONTEXT ? "***" : "---",
+//                 file1, ctime(&stb1.st_mtime));
+               fprintf(stdout,"%s\n", format == D_CONTEXT ? "***" : "---");
+#if 0
+       if (label[1] != NULL)
+               fprintf(stdout,"%s %s\n", format == D_CONTEXT ? "---" : "+++",
+                   label[1]);
+       else
+#endif
+//             fprintf(stdout,"%s %s\t%s", format == D_CONTEXT ? "---" : "+++",
+//                 file2, ctime(&stb2.st_mtime));
+               fprintf(stdout,"%s\n", format == D_CONTEXT ? "---" : "+++");
+}
+#endif
diff --git a/ext/diff.h b/ext/diff.h
new file mode 100644 (file)
index 0000000..37956d7
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef DIFF_H
+#define DIFF_H
+
+#include "ustring.h"
+#include "ml.h"
+class  MlEnv;
+
+int  diffreg (ustring* text1, ustring* text2, MNodePtr* pleftc, MNodePtr* pleftline, MNodePtr* prightc, MNodePtr* prightline, MlEnv* mlenv);
+
+#endif /* DIFF_H */
diff --git a/ext/ml-diff.cc b/ext/ml-diff.cc
new file mode 100644 (file)
index 0000000..6bf1580
--- /dev/null
@@ -0,0 +1,42 @@
+#include "ml-diff.h"
+#include "diff.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "expr.h"
+#include "ustring.h"
+#include <exception>
+
+/*DOC:
+==diff function==
+
+*/
+/*DOC:
+===diff===
+// (diff TEXT1 TEXT2 VAR_LEFT1 VAR_LEFT2 VAR_RIGHT1 VAR_RIGHT2) -> NIL
+ (diff LEFT_TEXT RIGHT_TEXT) -> (LEFT_OP LEFT_LINE RIGHT_OP RIGHT_LINE)
+
+*/
+//#AFUNC       diff    ml_diff
+MNode*  ml_diff (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  t1, t2;
+    MNodePtr  l1, l2, r1, r2;
+    MNodeList  ans;
+
+    if (!arg)
+       throw (uErrorWrongNumber);
+    t1 = eval_str (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    t2 = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    diffreg (&t1, &t2, &l1, &l2, &r1, &r2, mlenv);
+    ans.append (l1.release ());
+    ans.append (l2.release ());
+    ans.append (r1.release ());
+    ans.append (r2.release ());
+
+    return ans.release ();
+}
diff --git a/ext/ml-diff.h b/ext/ml-diff.h
new file mode 100644 (file)
index 0000000..315ce8f
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef ML_DIFF_H
+#define ML_DIFF_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_diff (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_DIFF_H */
diff --git a/lib/app.cc b/lib/app.cc
new file mode 100644 (file)
index 0000000..8ea81b2
--- /dev/null
@@ -0,0 +1,193 @@
+#include "app.h"
+#include "motorenv.h"
+#include "util_const.h"
+#include "util_check.h"
+#include "util_string.h"
+#include <string.h>
+#include <iostream>
+#include <stdlib.h>
+
+static bool  pcmp (char** a, const char* b, int n) {
+    int  rc = strncmp (*a, b, n);
+    if (rc == 0) {
+       *a += n;
+       return true;
+    } else {
+       return false;
+    }
+}
+#define cmp(a,b)       pcmp (&(a), (b), sizeof (b) - 1)
+#define zcmp(a,b)      (strcmp ((a), (b)) == 0)
+
+/*DOC:
+=ml=
+
+|table:|
+|datastore:NAME|
+|get-html:FILENAME|
+|post-html:FILENAME|
+|html:FILENAME|
+|error-html:FILENAME|
+|get-ml:FILENAME|
+|post-ml:FILENAME|
+|ml:FILENAME|
+|type:MIME-TYPE|
+|post-limit:DATASIZE|
+|post-file-limit:DATASIZE|
+|post-file|
+|random-cookie|
+|random-url|
+|no-cache|
+|dump|
+
+*/
+
+void  AppEnv::readOption (int argc, char** argv, MotorEnv* env) {
+    int  i;
+    char*  p;
+
+    arg0 = ustring (argv[0]);
+    for (i = 1; i < argc; i ++) {
+       p = argv[i];
+       if (cmp (p, "datastore:")) {
+           datastore = ustring (p);
+           if (! checkName (datastore))
+               throw (datastore + uErrorBadDatastore);
+       } else if (cmp (p, "get-html:")) {
+           getHtml = ustring (p);
+           if (getHtml != uDash && ! checkResourceName (getHtml))
+               throw (getHtml + uErrorBadFile);
+       } else if (cmp (p, "post-html:")) {
+           postHtml = ustring (p);
+           if (postHtml != uDash && ! checkResourceName (postHtml))
+               throw (postHtml + uErrorBadFile);
+       } else if (cmp (p, "html:")) {
+           getHtml = ustring (p);
+           postHtml = ustring (p);
+           if (getHtml != uDash && ! checkResourceName (getHtml))
+               throw (getHtml + uErrorBadFile);
+       } else if (cmp (p, "error-html:")) {
+           errorHtml = ustring (p);
+           if (errorHtml != uDash && ! checkResourceName (errorHtml))
+               throw (errorHtml + uErrorBadFile);
+       } else if (cmp (p, "get-ml:")) {
+           getML = ustring (p);
+           if (getML != uDash && ! checkResourceName (getML))
+               throw (getML + uErrorBadFile);
+       } else if (cmp (p, "post-ml:")) {
+           postML = ustring (p);
+           if (postML != uDash && ! checkResourceName (postML))
+               throw (postML + uErrorBadFile);
+       } else if (cmp (p, "ml:")) {
+           getML = ustring (p);
+           postML = ustring (p);
+           if (getML != uDash && ! checkResourceName (getML))
+               throw (getML + uErrorBadFile);
+       } else if (cmp (p, "type:")) {
+           mimetype = ustring (p);
+           if (! checkMimeType (mimetype))
+               throw (mimetype + ": bad mime type.");
+       } else if (cmp (p, "post-limit:")) {
+           postlimit = boost::lexical_cast <size_t> (p);
+           if (postlimit < 0) {
+               postlimit = cPOSTLIMITDEFAULT;
+           } else if (postlimit > cPOSTLIMITHARD) {
+               *env->log << "post-limit:" << postlimit << ": limit exceeded.\n";
+               postlimit = cPOSTLIMITHARD;
+           }
+       } else if (cmp (p, "postfile-limit:")
+                  || cmp (p, "post-file-limit:")) {
+           postfilelimit = boost::lexical_cast <size_t> (p);
+           if (postfilelimit < 0) {
+               postfilelimit = cPOSTFILELIMITDEFAULT;
+           } else if (postfilelimit > cPOSTFILELIMITHARD) {
+               *env->log << "post-file-limit:" << postfilelimit << ": limit exceeded.\n";
+               postfilelimit = cPOSTFILELIMITHARD;
+           }
+       } else if (zcmp (p, "postfile")
+                  || zcmp (p, "post-file")) {
+           postfile = true;
+       } else if (zcmp (p, "random-cookie")) {
+           cacheControl = CC_COOKIE;
+       } else if (zcmp (p, "random-url")) {
+           cacheControl = CC_URL;
+       } else if (zcmp (p, "no-cache")) {
+           cacheControl = CC_NOCACHE;
+       } else if (zcmp (p, "dump")) {
+           debugDump = true;
+       } else if (p[0] == ';' || p[0] == '(' || p[0] == '<') {
+           break;
+       } else {
+           throw (ustring (p) + ustring (": unrecognized parameter"));
+       }
+    }
+}
+
+void  AppEnv::setDefault () {
+    if (mimetype.size () == 0)
+       mimetype = ustring (CharConst (kMIME_HTML));
+    if (cacheControl == CC_NONE)
+       cacheControl = CC_COOKIE;
+}
+
+ustring  AppEnv::scriptName () {
+#if 0
+    char*  e = getenv (kSCRIPT_FILENAME);
+
+    if (e) {
+       return ustring (e);
+    } else {
+       return uEmpty;
+    }
+#endif
+    return getenvString (kSCRIPT_FILENAME);
+}
+
+void  AppEnv::dump (std::ostream& out) {
+    out << arg0 << "\n";
+    if (datastore.size () > 0)
+       out << "        datastore:" << datastore << "\n";
+    if (getML.size () > 0) {
+       if (getML == postML) {
+           out << "    ml:" << getML << "\n";
+       } else {
+           out << "    get-ml:" << getML << "\n";
+           if (postML.size () > 0)
+               out << "        post-ml:" << postML << "\n";
+       }
+    } else if (postML.size () > 0) {
+       out << "        post-ml:" << postML << "\n";
+    }
+    if (getHtml.size () > 0) {
+       if (getHtml == postHtml) {
+           out << "    html:" << getHtml << "\n";
+       } else {
+           out << "    get-html:" << getHtml << "\n";
+           if (postHtml.size () > 0)
+               out << "        post-html:" << postHtml << "\n";
+       }
+    } else if (postHtml.size () > 0) {
+       out << "        post-html:" << postHtml << "\n";
+    }
+    if (errorHtml.size () > 0)
+       out << "        error-html:" << errorHtml << "\n";
+    if (mimetype.size () > 0)
+       out << "        type:" << mimetype << "\n";
+    if (postlimit != cPOSTLIMITDEFAULT)
+       out << "        post-limit:" << postlimit << "\n";
+    if (postfile)
+       out << "        post-file\n";
+    if (postfilelimit != cPOSTFILELIMITDEFAULT)
+       out << "        post-file-limit:" << postfilelimit << "\n";
+    switch (cacheControl) {
+    case CC_COOKIE:
+       out << "        random-cookie\n";
+       break;
+    case CC_URL:
+       out << "        random-url\n";
+       break;
+    case CC_NOCACHE:
+       out << "        no-cache\n";
+       break;
+    }
+}
diff --git a/lib/app.h b/lib/app.h
new file mode 100644 (file)
index 0000000..96db032
--- /dev/null
+++ b/lib/app.h
@@ -0,0 +1,46 @@
+#ifndef APP_H
+#define APP_H
+
+#include "config.h"
+#include "httpconst.h"
+#include "ustring.h"
+#include <iostream>
+
+class  MotorEnv;
+class  AppEnv {
+ public:
+    ustring  arg0;
+    ustring  datastore;
+    ustring  getHtml;
+    ustring  postHtml;
+    ustring  errorHtml;
+    ustring  getML;
+    ustring  postML;
+    size_t  postlimit;
+    size_t  postfilelimit;
+    ustring  mimetype;
+    bool  postfile;
+    enum {
+       CC_NONE,
+       CC_COOKIE,
+       CC_URL,
+       CC_NOCACHE,
+    }  cacheControl;
+    bool  debugDump;
+
+    AppEnv () {
+       cacheControl = CC_NONE;
+       postlimit = cPOSTLIMITDEFAULT;
+       postfilelimit = cPOSTFILELIMITDEFAULT;
+       postfile = false;
+       debugDump = false;
+    };
+    virtual  ~AppEnv () {};
+
+    virtual void  readOption (int argc, char** argv, MotorEnv* env);
+    virtual void  setDefault ();
+    virtual ustring  scriptName ();
+    virtual void  dump (std::ostream& out);
+};
+
+#endif /* APP_H */
diff --git a/lib/bdbmacro.h b/lib/bdbmacro.h
new file mode 100644 (file)
index 0000000..92d6193
--- /dev/null
@@ -0,0 +1,187 @@
+#ifndef BDBMACRO_H
+#define BDBMACRO_H
+
+#include "ustring.h"
+#ifdef Linux
+#include <db_185.h>
+#else
+#include <db.h>
+#endif
+#include <fcntl.h>
+
+class  BDB {
+ public:
+    DB*  db;
+    DBT  key1, val1;
+    u_int  seqflag;
+
+    BDB (): db (NULL) {};
+    virtual  ~BDB () {
+       close ();
+    };
+    virtual void  close () {
+       if (db) {
+           (*db->close) (db);
+           db = NULL;
+       }
+    };
+    virtual void  put (DBT* key, DBT* val) {
+       if (!db || !key)
+           return;
+       if (val) {
+           (*db->put) (db, key, val, 0);
+       } else {
+           (*db->del) (db, key, 0);
+       }
+    };
+    virtual void  put (const u_char* key, size_t skey, const u_char* val, size_t sval) {
+       if (!db)
+           return;
+       key1.data = (void*)key;
+       key1.size = skey;
+       if (val) {
+           val1.data = (void*)val;
+           val1.size = sval;
+           (*db->put) (db, &key1, &val1, 0);
+       } else {
+           (*db->del) (db, &key1, 0);
+       }
+    };
+    virtual void  put (const ustring& key, const ustring& val) {
+       if (!db)
+           return;
+       key1.data = (void*)key.data ();
+       key1.size = key.size ();
+       val1.data = (void*)val.data ();
+       val1.size = val.size ();
+       (*db->put) (db, &key1, &val1, 0);
+    };
+    virtual void  del (DBT* key) {
+       if (!db || !key)
+           return;
+       (*db->del) (db, key, 0);
+    };
+    virtual void  del (const u_char* key, size_t skey) {
+       if (!db)
+           return;
+       key1.data = (void*)key;
+       key1.size = skey;
+       (*db->del) (db, &key1, 0);
+    };
+    virtual void  del (const ustring& key) {
+       if (!db)
+           return;
+       key1.data = (void*)key.data ();
+       key1.size = key.size ();
+       (*db->del) (db, &key1, 0);
+    };
+    virtual int  get (DBT* key, DBT* val) {
+       int  rc;
+       if (db && key) {
+           rc = (*db->get) (db, key, val, 0);
+           if (rc) {
+               val->data = NULL;
+               val->size = 0;
+           }
+           return (rc == 0);           // success
+       } else {
+           val->data = NULL;
+           val->size = 0;
+           return 0;                   // failure
+       }
+    };
+    virtual int  get (const ustring& key, ustring& val) {
+       int  rc;
+       if (db) {
+           key1.data = (void*)key.data ();
+           key1.size = key.size ();
+           rc = (*db->get) (db, &key1, &val1, 0);
+           if (rc) {
+               val.resize (0);
+           } else {
+               val.assign ((char*)val1.data, val1.size);
+           }
+           return (rc == 0);           // success
+       } else {
+           val.resize (0);
+           return 0;                   // failure
+       }
+    };
+    virtual void  flush () {
+       if (db)
+           (*db->sync) (db, 0);
+    };
+    virtual int  isempty () {
+       if (db && (*db->seq) (db, &key1, &val1, R_FIRST) == 0) {
+           return 0;
+       }else{
+           return 1;                   // empty
+       }
+    };
+    virtual void  initeach () {
+       seqflag = R_FIRST;
+    };
+    virtual int  each (DBT* key, DBT* val) {
+       int  rc;
+       if (!db)
+           return (0);
+       rc = (*db->seq) (db, key, val, seqflag);
+       seqflag = R_NEXT;
+       if (rc == 0) {
+           return 1;
+       }else{
+           return 0;
+       }
+    };
+    virtual int  each (ustring& key, ustring& val) {
+       int  rc;
+       if (!db)
+           return (0);
+       rc = (*db->seq) (db, &key1, &val1, seqflag);
+       seqflag = R_NEXT;
+       if (rc == 0) {
+           key.assign ((char*)key1.data, key1.size);
+           val.assign ((char*)val1.data, val1.size);
+           return 1;
+       }else{
+           return 0;
+       }
+    };
+};
+
+class  BDBHash: public BDB {
+ public:
+    void  open (const char* path) {
+#if 0
+       HASHINFO  info;
+
+       info.bsize = 1024;
+       info.ffactor = 0;
+       info.nelem = 1;
+       info.cachesize = 0;
+       info.hash = NULL;
+       info.lorder = 0;
+       db = dbopen (path, O_RDWR | O_CREAT, 0666, DB_HASH, &info);
+#endif
+       db = dbopen (path, O_RDWR | O_CREAT, 0666, DB_HASH, NULL);
+    };
+};
+
+class  BDBHashRDOnly: public BDB {
+ public:
+    void  open (const char* path) {
+       db = dbopen (path, O_RDONLY, 0666, DB_HASH, NULL);
+    };
+};
+
+class  BDBBtree: public BDB {
+ public:
+    void  openRead (const char* path) {
+       db = dbopen (path, O_RDONLY, 0666, DB_BTREE, NULL);
+    };
+    void  openRW (const char* path) {
+       db = dbopen (path, O_RDWR | O_CREAT, 0666, DB_BTREE, NULL);
+    };
+};
+
+#endif /* BDBMACRO_H */
diff --git a/lib/expr.cc b/lib/expr.cc
new file mode 100644 (file)
index 0000000..c223949
--- /dev/null
@@ -0,0 +1,555 @@
+#include "expr.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "ftable.h"
+#include "motorvar.h"
+#include "util_const.h"
+#include "util_string.h"
+#include "util_check.h"
+#include "ustring.h"
+#include <boost/unordered_map.hpp>
+#include <boost/lexical_cast.hpp>
+#include <iostream>
+#include <stdlib.h>
+#include <assert.h>
+
+using namespace  std;
+
+static MNode*  call_func (MNode* cell, MlEnv* mlenv) {
+    MNodePtr  ans;
+    FTable::iterator  it;
+    ustring*  c = cell->car ()->sym;
+    MNodePtr  bcell;
+    MNode*  sexp;
+
+    if (mlenv->ftable && (it = mlenv->ftable->find (*c)) != mlenv->ftable->end ()) {
+       // normal function.
+       bcell = mlenv->currentCell;
+       mlenv->currentCell = cell;
+       ans = it->second->fn (cell, mlenv);
+       mlenv->currentCell = bcell;
+    } else if (mlenv->moduleStatus == MlEnv::M_NONE && mlenv->mtable && (it = mlenv->mtable->find (*c)) != mlenv->mtable->end ()) {
+       // module function.
+       boost::unordered_map<ustring, FTable*>::iterator  it2;
+       mlenv->moduleStatus = MlEnv::M_START;
+       if ((it2 = mlenv->mtable->iSTableM.find (*c)) != mlenv->mtable->iSTableM.end ()) {
+           mlenv->stable = it2->second;
+       } else {
+           mlenv->stable = NULL;
+       }
+       bcell = mlenv->currentCell;
+       mlenv->currentCell = cell;
+       ans = it->second->fn (cell, mlenv);
+       mlenv->currentCell = bcell;
+       mlenv->moduleStatus = MlEnv::M_NONE;
+       mlenv->stable = NULL;
+       mlenv->module = NULL;
+    } else if (mlenv->moduleStatus == MlEnv::M_EXEC && mlenv->stable && (it = mlenv->stable->find (*c)) != mlenv->stable->end ()) {
+       // subfunction.
+       bcell = mlenv->currentCell;
+       mlenv->currentCell = cell;
+       ans = it->second->fn (cell, mlenv);
+       mlenv->currentCell = bcell;
+    } else if ((sexp = mlenv->getVar (*c))) {
+       if (isLambda (sexp)) {
+           MNodeList  list;
+           MNode*  a = cell->cdr ();
+           while (a) {
+               list.append (eval (a->car (), mlenv));
+               nextNode (a);
+           }
+           bcell = mlenv->currentCell;
+           mlenv->currentCell = cell;
+           ans = execDefun (mlenv, sexp, list ());
+           mlenv->currentCell = bcell;
+       }
+    } else {
+       // not found
+       mlenv->currentCell = cell;
+       throw (ustring ("undefined function"));
+    }
+    return ans.release ();
+}
+
+MNode*  eval (MNode* cell, MlEnv* mlenv) {
+    if (! cell)
+       return NULL;
+    switch (cell->type) {
+    case MNode::MC_NIL:
+       return NULL;
+    case MNode::MC_CONS:
+       if (cell->car ()
+           && cell->car ()->isSym ()
+           && (cell->cdr () == NULL || cell->cdr ()->isCons ())) {
+           return call_func (cell, mlenv);
+       } else {
+           throw (cell->dump_string_short () + ustring (": error"));
+       }
+       break;
+    case MNode::MC_STR:
+//     return newMNode_str (new ustring (*cell->str));
+       return cell;
+    case MNode::MC_SYM:
+       if (cell->sym->length () > 0) {
+           switch ((*cell->sym)[0]) {
+           case '#':
+           case ':':
+               return cell;
+           }
+       }
+       return mlenv->getVar (*cell->sym);
+    case MNode::MC_DOUBLE:
+//     return newMNode_num (cell->real);
+       return cell;
+    default:
+       assert (0);
+    }
+    return NULL;                       // not reached
+}
+
+double  eval_double (MNode* cell, MlEnv* mlenv) {
+    MNodePtr  p;
+
+    p = eval (cell, mlenv);
+    if (p ())
+       return p ()->to_double ();
+    else
+       return 0.;
+}
+
+int  eval_int (MNode* cell, MlEnv* mlenv) {
+    return (int) eval_double (cell, mlenv);
+}
+
+ustring  eval_str (MNode* cell, MlEnv* mlenv) {
+    MNodePtr  p;
+
+    p = eval (cell, mlenv);
+    if (p ()) {
+       switch (p ()->type) {
+       case MNode::MC_NIL:
+       case MNode::MC_STR:
+       case MNode::MC_SYM:
+       case MNode::MC_DOUBLE:
+           return p ()->to_string ();
+       case MNode::MC_CONS:
+           return p ()->to_string (false, true);
+       default:
+           throw (p ()->dump_string () + uErrorBadType);
+       }
+    } else {
+       return uEmpty;
+    }
+}
+
+ustring  eval_text1 (MNode* cell, MlEnv* mlenv) {
+    ustring  ans = eval_str (cell, mlenv);
+//    clipCtrl (ans);
+//    return ans;
+    return omitCtrl (ans);
+}
+
+ustring  eval_asciiword (MNode* cell, MlEnv* mlenv) {
+    ustring  ans = eval_str (cell, mlenv);
+//    clipNonAsciiWord (ans);
+//    return ans;
+    return omitNonAsciiWord (ans);
+}
+
+ustring*  eval_str_new (MNode* cell, MlEnv* mlenv) {
+    MNodePtr  p;
+    ustring*  ans = new ustring;
+
+    p = eval (cell, mlenv);
+    if (p ())
+       *ans = p ()->to_string ();
+    return ans;
+}
+
+bool  eval_bool (MNode* cell, MlEnv* mlenv) {
+    MNodePtr  p;
+
+    p = eval (cell, mlenv);
+    return to_bool (p ());
+}
+
+ustring  eval_file (MNode* cell, MlEnv* mlenv) {
+    ustring  ans = eval_str (cell, mlenv);
+    if (! checkFilename (ans)) // XXX dummy
+       ans.resize (0);
+    return ans;
+}
+
+//MNode*  progn (MNode* arg, MlEnv* mlenv, MNode* sym) {
+MNode*  progn (MNode* arg, MlEnv* mlenv) {
+    MNodePtr  ans;
+
+    if (! arg)
+       return NULL;
+
+    assert (arg->isCons ());
+    while (arg && ! mlenv->noprog) {
+       if (arg->car ()) {
+#ifdef DEBUG
+           mlenv->logSexp (arg->car ());
+#endif /* DEBUG */
+           ans = eval (arg->car (), mlenv);
+#ifdef DEBUG2
+           cerr << "progn:";
+           if (ans ())
+               ans ()->dump (cerr);
+           else
+               cerr << "nil";
+           cerr << "\n";
+#endif /* DEBUG */
+           if (mlenv->breaksym ()) {
+#if 0
+               if (!sym || mlenv->breaksym ()->isNil ()) {
+                   mlenv->breaksym = NULL;
+                   return mlenv->breakval ();
+               } else {
+                   if (eq (mlenv->breaksym (), sym)) {
+                       mlenv->breaksym = NULL;
+                       return mlenv->breakval ();
+                   } else {
+                       return NULL;
+                   }
+               }
+#endif
+               return mlenv->breakval ();
+           }
+       } else {
+           ans = NULL;
+       }
+       nextNode (arg);
+    }
+    return ans.release ();
+}
+
+void  progn_ex (MNode* arg, MlEnv* mlenv) {
+    MNodePtr  ans;
+
+    assert (arg && arg->isCons ());
+    if (arg->cdr () && arg->cdr ()->isCons ()) {
+       arg = arg->cdr ();
+       mlenv->resetProg ();
+       while (arg && ! mlenv->noprog) {
+           if (arg->car () && arg->car ()->isCons ()) {
+#ifdef DEBUG
+               mlenv->logSexp (arg->car ());
+#endif /* DEBUG */
+               ans = eval (arg->car (), mlenv);
+#ifdef DEBUG2
+               cerr << "progn:";
+               if (ans ())
+                   ans ()->dump (cerr);
+               else
+                   cerr << "nil";
+               cerr << "\n";
+#endif /* DEBUG */
+               if (mlenv->breaksym ()) {
+                   mlenv->breaksym = NULL;
+                   return;
+               }
+           }
+           nextNode (arg);
+       }
+    }
+}
+
+void  checkDefun (MNode* arg, ustring& name, MNode*& sexp) {
+    MNode*  param;
+    MNode*  body;
+    MNode*  a;
+    
+    if (! arg)
+       throw (uErrorWrongNumber);
+    if (! arg->car ())
+       throw ("bad name.");
+
+    name = arg->car ()->to_string ();
+    nextNodeNonNil (arg);
+    sexp = arg;
+
+    param = arg->car ();
+    body = arg->cdr ();
+    if ((param && ! param->isNil () && ! param->isCons ())
+       || (body && ! body->isNil () && ! body->isCons ())) {
+       throw (uErrorWrongType);
+    }
+    for (a = param; a && a->isCons (); nextNode (a)) {
+       if (! a->car () || ! a->car ()->isSym ())
+           throw (param->dump_string () + uErrorBadType);
+    }
+}
+
+MNode*  newLambda (MNode* cell) {
+    if (! cell || ! cell->isCons ())
+       throw (uErrorSyntax);
+
+    MNode*  ans = new MNode;
+       
+    ans->set_car (newMNode_sym (new ustring (uLambda)));
+    ans->set_cdr (cell);
+    
+    return ans;
+}
+
+bool  isLambda (MNode* sexp) {
+    return (sexp && sexp->isCons () && sexp->car () && sexp->car ()->isSym () && (*sexp->car ()->sym) == uLambda);
+}
+
+MNode*  buildArgs (int start, std::vector<ustring>& args) {
+    MNode*  ans = NULL;
+    MNode*  a;
+    int  i;
+
+    if (start < args.size ()) {
+       ans = a = new MNode;
+       a->set_car (newMNode_str (new ustring (args[start])));
+       for (i = start + 1; i < args.size (); i ++) {
+           newMNodeCdr (a);
+           a->set_car (newMNode_str (new ustring (args[i])));
+       }
+    }
+    return ans;
+}
+
+MNode*  buildArgs (int start, std::vector<ustring>& args, const ustring& arg2) {
+    MNode*  ans = NULL;
+    MNode*  a;
+    int  i;
+
+    ans = a = new MNode;
+    a->set_car (newMNode_str (new ustring (arg2)));
+    for (i = start; i < args.size (); i ++) {
+       newMNodeCdr (a);
+       a->set_car (newMNode_str (new ustring (args[i])));
+    }
+    return ans;
+}
+
+class  KwList: public boost::unordered_map<ustring, std::pair<bool,MNode*> > {
+public:
+    KwList () {};
+    ~KwList () {};
+    void  insertVar (const ustring& name, bool f) {
+       erase (name);
+       insert (KwList::value_type (name, std::pair<bool,MNode*> (f, NULL)));
+    };
+    void  setVar (const ustring& name, MNode* val) {
+       KwList::iterator  it = find (name);
+       if (it == end ()) {
+       } else {
+           it->second.second = val;
+       }
+    };
+    MNode*  getVar (const ustring& name) {
+       KwList::iterator  it = find (name);
+       if (it == end ()) {
+           return NULL;
+       } else {
+           return it->second.second;
+       }
+    };
+    bool  defined (const ustring& name) {
+       KwList::iterator  it = find (name);
+       return (it != end ());
+    };
+    bool  definedBoolType (const ustring& name) {
+       KwList::iterator  it = find (name);
+       return (it != end () && it->second.first);
+    };
+};
+
+MNode*  execDefun (MlEnv* mlenv, MotorVar* pool, const ustring& name, MNode* values) {
+    MNode*  sexp = pool->getVar (name);
+    if (isLambda (sexp)) {
+       return execDefun (mlenv, sexp, values);
+    }
+    return NULL;
+}
+
+MNode*  execDefun (MlEnv* mlenv, MNode* lambda, MNode* values) {
+    assert (isLambda (lambda));
+    MNode*  sexp = lambda->cdr ();
+    MNode*  param;
+    MNode*  body = sexp->cdr ();
+    MNodePtr  ans;
+    KwList*  kwlist = NULL;
+    ustring*  u;
+    ustring  k;
+    bool  skip;
+
+    mlenv->beginLocal ();
+    for (param = sexp->car (); param; nextNode (param)) {
+       u = param->car ()->sym;
+       if (match (*u, CharConst ("&rest"))) {
+           break;
+       } else if (match (*u, CharConst ("&key"))) {
+           if (kwlist) {
+               throw (sexp->car ()->dump_string_short () + uErrorBadParam);
+           } else {
+               kwlist = new KwList;
+           }
+       } else {
+           if (kwlist) {
+               kwlist->insertVar (*u, true);
+               mlenv->setLocalVar (*u, NULL);
+           } else {
+           }
+       }
+    }
+
+    skip = false;
+    for (param = sexp->car (); param;) {
+       u = NULL;
+       if (kwlist && values && values->car ()->isSym ()
+           && (u = values->car ()->sym)->length () > 0
+           && (*u)[0] == '#'
+           && kwlist->defined (k = ustring (u->begin () + 1, u->end ()))) {
+           mlenv->setLocalVar (k, mlTrue);
+           nextNode (values);
+       } else if (u && u->length () > 0
+                  && (*u)[0] == ':'
+                  && kwlist->defined (k = ustring (u->begin () + 1, u->end ()))) {
+           nextNode (values);
+           if (values)
+               mlenv->setLocalVar (k, values->car ());
+           else
+               mlenv->setLocalVar (k, NULL);
+           nextNode (values);
+       } else if (match (*(u = param->car ()->sym), CharConst ("&rest"))) {
+           nextNode (param);
+           if (param) {
+               mlenv->setLocalVar (*param->car ()->sym, values);
+               values = NULL;
+           } else {
+               throw (sexp->car ()->dump_string_short () + uErrorBadParam);
+           }
+           break;
+       } else if (match (*u, CharConst ("&key"))) {
+           skip = true;
+           nextNode (param);
+       } else {
+           if (skip) {
+               nextNode (param);
+           } else {
+               if (values)
+                   mlenv->setLocalVar (*u, values->car ());
+               else
+                   mlenv->setLocalVar (*u, NULL);
+               nextNode (param);
+               nextNode (values);
+           }
+       }
+    }
+    for (; values;) {
+       u = NULL;
+       if (kwlist && values && values->car ()->isSym ()
+           && (u = values->car ()->sym)->length () > 0
+           && (*u)[0] == '#'
+           && kwlist->defined (k = ustring (u->begin () + 1, u->end ()))) {
+           mlenv->setLocalVar (k, mlTrue);
+           nextNode (values);
+       } else if (u && u->length () > 0
+                  && (*u)[0] == ':'
+                  && kwlist->defined (ustring (u->begin () + 1, u->end ()))) {
+           nextNode (values);
+           if (values)
+               mlenv->setLocalVar (k, values->car ());
+           else
+               mlenv->setLocalVar (k, NULL);
+           nextNode (values);
+       } else {
+           throw (uErrorWrongNumber);
+       }
+    }
+
+    ans = progn (body, mlenv);
+    if (mlenv->breaksym ()
+       && (mlenv->breaksym ()->isNil () || eq (mlenv->breaksym (), lambda->car ()))) {
+       mlenv->breaksym = NULL;
+    }
+    mlenv->endLocal ();
+
+    return ans.release ();
+}
+
+void  setParams (MNode* list, int nparam, std::vector<MNode*>* params, paramList *kwlist, std::vector<MNode*>* keywords, MNode** rest) {
+    KwList*  kw = NULL;
+    ustring*  u;
+    MNode*  a;
+    int  i;
+    ustring  name;
+
+    if (kwlist) {
+       kw = new KwList;
+       for (i = 0; kwlist[i].name; i ++) {
+           kw->insertVar (ustring (kwlist[i].name, kwlist[i].namelen), kwlist[i].fbool);
+       }
+    }
+
+    while (list) {
+       a = list->car ();
+       if (a && kw && a->isSym () && (u = a->sym) && u->size () > 0) {
+           switch ((*u)[0]) {
+           case ':':
+               name = ustring (u->begin () + 1, u->end ());
+               if (kw->defined (name)) {
+                   nextNode (list);
+                   if (list) {
+                       kw->setVar (name, list->car ());
+                       nextNode (list);
+                   } else {
+                       delete kw;
+                       throw (uErrorWrongNumber);
+                   }
+               } else {
+                   goto Bp1;
+               }
+               break;
+           case '#':
+               name = ustring (u->begin () + 1, u->end ());
+               if (kw->definedBoolType (name)) {
+                   nextNode (list);
+                   kw->setVar (name, mlTrue);
+               } else {
+                   goto Bp1;
+               }
+               break;
+           default:
+               goto Bp1;
+           }
+       } else {
+       Bp1:;
+           if (params && params->size () < nparam) {
+               nextNode (list);
+               params->push_back (a);
+           } else {
+               break;
+           }
+       }
+    }
+
+    if (rest) {
+       *rest = list;
+    } else if (list) {
+       delete kw;
+       throw (uErrorWrongNumber);
+    }
+
+    if (params) {
+       while (params->size () < nparam) {
+           params->push_back (NULL);
+       }
+    }
+
+    if (kwlist && keywords) {
+       for (i = 0; kwlist[i].name; i ++) {
+           keywords->push_back (kw->getVar (ustring (kwlist[i].name, kwlist[i].namelen)));
+       }
+    }
+
+    delete kw;
+}
diff --git a/lib/expr.h b/lib/expr.h
new file mode 100644 (file)
index 0000000..17d7e4b
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef EXPR_H
+#define EXPR_H
+
+#include "ml.h"
+#include "ustring.h"
+#include <vector>
+
+class  MlEnv;
+class  MotorVar;
+
+typedef struct {
+    const char*  name;
+    size_t  namelen;
+    bool  fbool;
+}  paramList;
+
+MNode*  eval (MNode* ptr, MlEnv* mlenv);
+double  eval_double (MNode* ptr, MlEnv* mlenv);
+int  eval_int (MNode* ptr, MlEnv* mlenv);
+ustring  eval_str (MNode* ptr, MlEnv* mlenv);
+ustring  eval_text1 (MNode* cell, MlEnv* mlenv);
+ustring  eval_asciiword (MNode* cell, MlEnv* mlenv);
+ustring*  eval_str_new (MNode* ptr, MlEnv* mlenv);
+bool  eval_bool (MNode* ptr, MlEnv* mlenv);
+ustring  eval_file (MNode* cell, MlEnv* mlenv);
+MNode*  progn (MNode* arg, MlEnv* mlenv);
+void  progn_ex (MNode* arg, MlEnv* mlenv);
+void  checkDefun (MNode* cell, ustring& rname, MNode*& sexp);
+MNode*  newLambda (MNode* cell);
+bool  isLambda (MNode* sexp);
+MNode*  buildArgs (int start, std::vector<ustring>& args);
+MNode*  buildArgs (int start, std::vector<ustring>& args, const ustring& arg2);
+MNode*  execDefun (MlEnv* mlenv, MotorVar* pool, const ustring& name, MNode* values);
+MNode*  execDefun (MlEnv* mlenv, MNode* sexp, MNode* values);
+void  setParams (MNode* list, int nparam, std::vector<MNode*>* params, paramList *kwlist, std::vector<MNode*>* keywords, MNode** rest);
+
+#endif /* EXPR_H */
diff --git a/lib/filemacro.h b/lib/filemacro.h
new file mode 100644 (file)
index 0000000..fb6df83
--- /dev/null
@@ -0,0 +1,89 @@
+#ifndef FILEMACRO_H
+#define FILEMACRO_H
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <assert.h>
+#ifndef  HAVE_OPENLOCK
+#include <sys/file.h>
+#endif
+
+class  FileMacro {
+ public:
+    int  fd;
+
+    FileMacro (): fd (-1) {};
+    virtual  ~FileMacro () {
+       close ();
+    };
+
+    virtual bool  open (const char* path, int flags) {
+       assert (fd < 0);
+       fd = ::open (path, flags);
+       return (fd >= 0);
+    };
+    virtual bool  open (const char* path, int flags, mode_t mode) {
+       assert (fd < 0);
+       fd = ::open (path, flags, mode);
+       return (fd >= 0);
+    };
+    virtual bool  openRead (const char* path) {
+       return open (path, O_RDONLY);
+    };
+    virtual bool  openReadLock (const char* path) {
+#ifdef  HAVE_OPENLOCK
+       return open (path, O_RDONLY | O_SHLOCK);
+#else
+//     assert (0);
+       int  fd;
+       fd = open (path, O_RDONLY);
+       flock (fd, LOCK_SH);
+       return fd;
+#endif
+    };
+    virtual bool  openWrite (const char* path) {
+       return open (path, O_WRONLY | O_TRUNC | O_CREAT, 0666);
+    };
+    virtual bool  openReadWrite (const char* path) {
+       return open (path, O_RDWR | O_CREAT, 0666);
+    };
+    virtual bool  openAppendLock (const char* path) {
+#ifdef  HAVE_OPENLOCK
+       return open (path, O_WRONLY | O_CREAT | O_APPEND | O_EXLOCK, 0666);
+#else
+//     assert (0);
+       int  fd;
+       fd = open (path, O_WRONLY | O_CREAT | O_APPEND, 0666);
+       flock (fd, LOCK_EX);
+       return fd;
+#endif
+    };
+    virtual void  close () {
+       if (fd >= 0)
+           ::close (fd);
+       fd = -1;
+    };
+    virtual int  isOpen () {
+       return (fd != -1);
+    };
+    virtual off_t  size () {
+       struct stat  sb;
+
+       if (fstat (fd, &sb) == 0) {
+           return sb.st_size;
+       } else {
+           return 0;
+       }
+    };
+    virtual ssize_t  read (void* buf, size_t n) {
+       return ::read (fd, buf, n);
+    };
+    virtual ssize_t  write (const void* buf, size_t n) {
+       return ::write (fd, buf, n);
+    };
+};
+
+#endif /* FILEMACRO_H */
diff --git a/lib/form.cc b/lib/form.cc
new file mode 100644 (file)
index 0000000..32627fd
--- /dev/null
@@ -0,0 +1,203 @@
+#include "form.h"
+#include "app.h"
+#include "motorenv.h"
+#include "httpconst.h"
+#include "util_const.h"
+#include "util_check.h"
+#include "util_string.h"
+#include "ustring.h"
+#include <iostream>
+#include <stdlib.h>
+#include <string.h>
+
+void  CGIForm::read (const ustring& query) {
+    ustring::size_type  s, e, t;
+    uiterator  ib, ie;
+    uiterator  is = query.begin ();
+
+    s = 0;
+    while (1) {
+       ib = is + s;
+       e = query.find ('&', s);
+       if (e == ustring::npos) {
+           ie = query.end ();
+           insertNameValue (ib, ie);
+           break;
+       } else {
+           ie = is + e;
+           insertNameValue (ib, ie);
+           s = e + 1;
+       }
+    }
+}
+
+void  CGIForm::read_get () {
+    ustring  q = getenvString (kQUERY_STRING);
+    if (q.length () > 0) {
+       read (q);
+    }
+}
+
+void  CGIForm::read_post (MotorEnv* env) {
+    size_t  size;
+    ustring  q;
+
+    size = postSize ();
+    if (size == 0 || size > env->appenv->postlimit)
+       return;
+    q.reserve (size);
+    q.resize (size);
+    assert (&q[0] == q.data ());
+    std::cin.read (&q[0], size);
+    q.resize (std::cin.gcount ());
+    if (q.length () == size)
+       read (q);
+}
+
+CGIForm::method_type  CGIForm::methodType () {
+    char*  e = getenv (kREQUEST_METHOD);
+
+    if (!e) {
+       return M_NONE;
+    } else if (strcmp (e, kMETHOD_GET) == 0) {
+       return M_GET;
+    } else if (strcmp (e, kMETHOD_POST) == 0) {
+       return M_POST;
+    } else {
+       return M_OTHER;
+    }
+}
+
+bool  CGIForm::isMultipart () {
+    char*  e = getenv (kCONTENT_TYPE);
+
+    return (e && strcmp (e, kMIME_FORMDATA) == 0);
+}
+
+size_t  CGIForm::postSize () {
+    char*  e = getenv (kCONTENT_LENGTH);
+
+    if (e == NULL)
+       return 0;
+    return strtoul (e, NULL, 10);
+}
+
+void  CGIForm::at (const ustring& name, ustring& ans) {
+    at (name, 0, ans);
+}
+
+void  CGIForm::at (const ustring& name, size_t i, ustring& ans) {
+    map_t::iterator  it;
+
+    it = iarg.find (name);
+    if (it == iarg.end ()) {
+       ans = uEmpty;
+    } else {
+       if (it->second < index.size ()) {
+           std::vector<int>*  v = &index [it->second];
+           if (v->size () > i) {
+               ans = values[(*v)[i]];
+           } else {
+               ans = uEmpty;
+           }
+#ifdef DEBUG
+//         std::cerr << "at:" << name << " i=" << it->second << "\n";
+#endif /* DEBUG */
+       } else {
+           ans = uEmpty;
+       }
+    }
+}
+
+size_t  CGIForm::atSize (const ustring& name) {
+    map_t::iterator  it;
+
+    it = iarg.find (name);
+    if (it == iarg.end ()) {
+    } else {
+       if (it->second < index.size ()) {
+           std::vector<int>*  v = &index [it->second];
+           return v->size ();
+       }
+    }
+    return 0;
+}
+
+void  CGIForm::insert (const ustring& name, const ustring& value) {
+    if (checkName (name)) {
+       map_t::iterator  it = iarg.find (name);
+       std::vector<int>*  v;
+       
+       if (it == iarg.end ()) {
+           v = insertName (name);
+       } else {
+           v = &index.at (it->second);
+       }
+       if (v) {
+//         values.push_back (new ustring(value));
+           values.push_back (value);
+           v->push_back (values.size () - 1);
+       }
+    }
+}
+
+#ifdef DEBUG2
+void  CGIForm::dump (std::ostream& o) {
+    map_t::iterator  it;
+    std::vector<int>*  t;
+    std::vector<int>::iterator  iu;
+    int  c;
+
+    for (it = iarg.begin (); it != iarg.end (); it ++) {
+       o << it->first << ":\t";
+       t = &index.at (it->second);
+       if (t->size () == 0) {
+           o << "(empty)\n";
+       } else {
+           c = 0;
+           for (iu = t->begin (); iu != t->end (); iu ++) {
+               if (c == 0) {
+                   o << values.at (*iu) << "\n";
+               } else {
+                   o << std::string ((it->first.size () + 1) / 8 + 1 , '\t') << values.at (*iu) << "\n";
+               }
+               c ++;
+           }
+       }
+    }
+}
+#endif /* DEBUG */
+
+void  CGIForm::insertNameValue (uiterator& b, uiterator& e) {
+    uiterator  it;
+
+    if (b == e)
+       return;
+
+    for (it = b; it != e; it ++) {
+       if (*it == '=') {
+           ustring  name (b, it);
+           ustring  value (it + 1, e);
+
+           decode (name);
+           decode (value);
+           insert (name, value);
+           return;
+       }
+    }
+    {
+       ustring  name (b, e);
+       ustring  value;
+
+       decode (name);
+       insert (name, value);
+    }
+}
+
+std::vector<int>*  CGIForm::insertName (const ustring& name) {
+    std::vector<int>*  ans = new std::vector<int>;
+    index.push_back (ans);
+    iarg.insert (map_t::value_type (name, index.size () - 1));
+    return ans;
+}
+
diff --git a/lib/form.h b/lib/form.h
new file mode 100644 (file)
index 0000000..c0cf273
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef FORM_H
+#define FORM_H
+
+#include "ustring.h"
+#include <boost/unordered_map.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <vector>
+#include <iostream>
+
+class  MotorEnv;
+class  CGIForm {
+ public:
+    typedef boost::unordered_map<ustring, int>  map_t;
+    typedef boost::ptr_vector<std::vector<int> >  indexary;
+//    typedef boost::ptr_vector<ustring>  pool_t;
+    typedef  std::vector<ustring>  pool_t;
+    typedef enum {
+       M_NONE,
+       M_GET,
+       M_POST,
+       M_OTHER,
+    }  method_type;
+
+    map_t  iarg;
+    indexary  index;
+    pool_t  values;
+    method_type  method;
+    bool  multipart;
+
+    CGIForm () {
+       method = methodType ();
+       multipart = isMultipart ();
+    };
+    virtual  ~CGIForm () {};
+    virtual void  read (const ustring& query);
+    virtual void  read_get ();
+    virtual void  read_post (MotorEnv* env);
+    virtual method_type  methodType ();
+    virtual bool  isMultipart ();
+    virtual size_t  postSize ();
+    virtual void  at (const ustring& name, ustring& ans);
+    virtual void  at (const ustring& name, size_t i, ustring& ans);
+    virtual size_t  atSize (const ustring& name);
+#ifdef DEBUG2
+    virtual void  dump (std::ostream& o);
+#endif /* DEBUG */
+    virtual void  insert (const ustring& name, const ustring& value);
+    virtual void  decode (ustring& key) = 0;
+    virtual void  fix (ustring& key) = 0;
+    virtual void  insertNameValue (uiterator& b, uiterator& e);
+    virtual std::vector<int>*  insertName (const ustring& name);
+};
+
+#endif /* FORM_H */
diff --git a/lib/form_utf8-jp.cc b/lib/form_utf8-jp.cc
new file mode 100644 (file)
index 0000000..6c2240c
--- /dev/null
@@ -0,0 +1,16 @@
+#include "form_utf8-jp.h"
+#include "util_string.h"
+#include "utf8.h"
+#include "utf8-jp.h"
+
+void  CGIFormUTF8JPMS::decode (ustring& key) {
+//    key = urldecode_nonul (key);
+//    fixUTF8 (key);
+//    key = fixFromMS (key);
+    key = fixFromMS (fixUTF8 (urldecode_nonul (key)));
+}
+
+void  CGIFormUTF8JPMS::fix (ustring& key) {
+    key = fixFromMS (fixUTF8 (key));
+}
+
diff --git a/lib/form_utf8-jp.h b/lib/form_utf8-jp.h
new file mode 100644 (file)
index 0000000..0b699a1
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef FORM_UTF8_JP_H
+#define FORM_UTF8_JP_H
+
+#include "form_utf8.h"
+
+class  CGIFormUTF8JPMS: public CGIFormUTF8 {
+ public:
+    CGIFormUTF8JPMS () {};
+    virtual  ~CGIFormUTF8JPMS () {};
+    virtual void  decode (ustring& key);
+    virtual void  fix (ustring& key);
+};
+
+#endif /* FORM_UTF8_JP_H */
diff --git a/lib/form_utf8.cc b/lib/form_utf8.cc
new file mode 100644 (file)
index 0000000..271460c
--- /dev/null
@@ -0,0 +1,18 @@
+#include "form_utf8.h"
+#include "util_string.h"
+#include "utf8.h"
+
+/*
+  Decode both Name and Value in FORM value.
+  Delete NUL chars.
+  Delete char which is not a valid UTF-8 text.
+*/
+
+void  CGIFormUTF8::decode (ustring& key) {
+    key = fixUTF8 (urldecode_nonul (key));
+}
+
+void  CGIFormUTF8::fix (ustring& key) {
+    key = fixUTF8 (key);
+}
+
diff --git a/lib/form_utf8.h b/lib/form_utf8.h
new file mode 100644 (file)
index 0000000..e727320
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef FORM_UTF8_H
+#define FORM_UTF8_H
+
+#include "formfile.h"
+#include "ustring.h"
+
+class  CGIFormUTF8: public CGIFormFile {
+ public:
+    CGIFormUTF8 () {};
+    virtual  ~CGIFormUTF8 () {};
+    virtual void  decode (ustring& key);
+    virtual void  fix (ustring& key);
+};
+
+#endif /* FORM_UTF8_H */
diff --git a/lib/formfile.cc b/lib/formfile.cc
new file mode 100644 (file)
index 0000000..288cde9
--- /dev/null
@@ -0,0 +1,253 @@
+#include "formfile.h"
+#include "motorenv.h"
+#include "util_const.h"
+#include "util_string.h"
+#include "ustring.h"
+#include "filemacro.h"
+#include <boost/lexical_cast.hpp>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <assert.h>
+
+void  CGIFormFile::read_multipart (MotorEnv* env) {
+#ifdef DEBUG2
+    std::cerr << "boundary:" << boundary << "\n";
+#endif /* DEBUG */
+    compileReg ();
+    if (saveData (env)) {
+       searchPart (env);
+#ifdef DEBUG2
+       dump (std::cerr);
+       for (int i = 0; i < parts.size (); i ++) {
+           std::cerr << "file:" << i << " (" << (void*)parts[i].first << ", " << (void*)parts[i].second << ") filename:" << filenames[i] << "\n";
+       }
+#endif /* DEBUG */
+    }
+}
+
+bool  CGIFormFile::saveData (MotorEnv* env) {
+    static const int  bsize = 65536 * 4;
+    size_t  size = postSize ();
+    ustring  b;
+    size_t  s;
+
+    b.resize (bsize);
+    if (size == 0 || size > env->appenv->postfilelimit)
+       return false;           // NG
+    tmpfile = env->path_to_posttemp ();
+    mapsize = 0;
+    if (fp.openReadWrite (tmpfile.c_str ())) {
+       while (size > 0) {
+           s = (size < bsize) ? size : bsize;
+           s = std::cin.read (&b[0], s).gcount ();
+           if (s <= 0)
+               break;
+           ::write (fp.fd, &b[0], s);
+           size -= s;
+           mapsize += s;
+       }
+//     fp.close ();
+       mapdata = (char*)mmap (NULL, mapsize, PROT_READ, MAP_PRIVATE, fp.fd, 0);
+#ifdef DEBUG2
+       std::cerr << (void*) mapdata << ": " << mapsize << "\n";
+#endif /* DEBUG */
+    } else {
+       throw (ustring (CharConst ("configuration error: can't open temporary file.")));
+    }
+    return true;
+}
+
+void  CGIFormFile::unlinkTmpFile () {
+    if (mapdata > 0) {
+       munmap (mapdata, mapsize);
+       mapdata = NULL;
+    }
+    if (tmpfile.size () > 0) {
+       unlink (tmpfile.c_str ());
+       tmpfile.resize (0);
+    }
+}
+
+bool  CGIFormFile::isMultipart () {
+//    char*  e = getenv (kCONTENT_TYPE);
+    ustring  e = getenvString (kCONTENT_TYPE);
+    umatch  m;
+//    ustring  t;
+    static uregex  re ("^" kMIME_FORMDATA "\\s*;\\s*boundary=\"?([ '()+,./0-9:=?A-Z_a-z-]*['()+,./0-9:=?A-Z_a-z-])");
+
+    if (usearch (e, m, re)) {
+       boundary = ustring (m[1]);
+       return true;
+    } else {
+       return false;
+    }
+}
+
+void  CGIFormFile::searchPart (MotorEnv* env) {
+    char*  b = mapdata;
+    char*  e = mapdata + mapsize;
+    char*  x;
+    boost::match_results<char*>  m;
+    ustring  disp;
+    ustring  name;
+    ustring  filename;
+    ustring  type;
+    ustring  v;
+    size_t  size;
+
+#ifdef DEBUG2
+    std::cerr << "b:" << (void*)b << " e:" << (void*)e << "\n";
+    std::cerr << "mapdata:" << ustring (b, b + 40) << "\n";
+#endif /* DEBUG */
+    if (b != e && regex_search (b, e, m, re1, boost::regex_constants::match_single_line)) {
+#ifdef DEBUG2
+       std::cerr << "match:" << ustring (m[0].first, m[0].second) << "\n";
+#endif /* DEBUG */
+       b = m[0].second;
+       while (b != e && regex_search (b, e, m, reN, boost::regex_constants::match_single_line)) {
+           x = m[0].first;
+#ifdef DEBUG2
+           std::cerr << "match:" << ustring (m[0].first, m[0].second) << "\n";
+#endif /* DEBUG */
+           readMimeHead (b, x, disp, name, filename, type);
+#ifdef DEBUG2
+           std::cerr << "disp:" << disp << " name:" << name << " filename:" << filename << " type:" << type << "\n";
+#endif /* DEBUG */
+           if (filename.size () > 0) {
+               v = boost::lexical_cast<ustring> (parts.size ());
+//             decode (name);
+               fix (name);
+               insert (name, v);
+               parts.push_back (part (b, x));
+//             decode (filename);
+               fix (filename);
+               filenames.push_back (filePartOSSafe (filename));
+           } else {
+               size = x - b;
+               if (size < env->appenv->postlimit) {
+                   v = ustring (b, x);
+//                 decode (name);
+                   fix (name);
+//                 decode (v);
+                   fix (v);
+                   insert (name, v);
+               } else {
+                   *env->log << "form variable '" << name << "': size limit.\n";
+               }
+           }
+
+           b = m[0].second;
+           if (e - b < 2) {
+               break;
+           } else if (b[0] == '-' && b[1] == '-') {
+#ifdef DEBUG2
+               std::cerr << "break\n";
+#endif /* DEBUG */
+               break;
+           } else if (b[0] == '\r' && b[1] == '\n') {
+               b += 2;
+           } else {
+               break;          // format error;
+           }
+       }
+    }
+}
+
+void  CGIFormFile::compileReg () {
+    ustring  a;
+    ustring  t = escape_re (boundary);
+
+    a.append (CharConst ("^--"));
+    a.append (t);
+    a.append (uCRLF);
+#ifdef DEBUG2
+    std::cerr << "re1:" << a << "\n";
+#endif /* DEBUG */
+    re1.assign (a);
+    a = uCRLF;
+    a.append (CharConst ("--"));
+    a.append (t);
+#ifdef DEBUG2
+    std::cerr << "reN:" << a << "\n";
+#endif /* DEBUG */
+    reN.assign (a);
+}
+
+void  CGIFormFile::readMimeHead (char*& b, char* e, ustring& disp, ustring& name, ustring& filename, ustring& type) {
+    boost::match_results<char*>  m;
+    boost::match_results<char*>  m2;
+    char*  x;
+    static uregex  re_disp1 ("^Content-Disposition:\\s*(.*);\\s*name=\"(.*)\";\\s*filename=\"(.*)\"$");
+    static uregex  re_disp2 ("^Content-Disposition:\\s*(.*);\\s*name=\"(.*)\"$");
+    static uregex  re_type ("^Content-Type:\\s*([a-zA-Z_0-9/.+-]*)(;\\s*(.*))?$");
+
+    disp.resize (0);
+    name.resize (0);
+    filename.resize (0);
+    type.resize (0);
+    while (b != e && regex_search (b, e, m, re_nl, boost::regex_constants::match_single_line)) {
+       x = m[0].first;
+#ifdef DEBUG2
+       std::cerr << "line:" << ustring (b, x) << "\n";
+#endif /* DEBUG */
+       if (b == x) {           // empty line
+           b = m[0].second;
+           break;
+       }
+       if (regex_search (b, x, m2, re_disp1, boost::regex_constants::match_single_line)) {
+           disp.assign (m2[1].first, m2[1].second - m2[1].first);
+           name.assign (m2[2].first, m2[2].second - m2[2].first);
+           filename.assign (m2[3].first, m2[3].second - m2[3].first);
+       } else if (regex_search (b, x, m2, re_disp2, boost::regex_constants::match_single_line)) {
+           disp.assign (m2[1].first, m2[1].second - m2[1].first);
+           name.assign (m2[2].first, m2[2].second - m2[2].first);
+       } else if (regex_search (b, x, m2, re_type, boost::regex_constants::match_single_line)) {
+           type.assign (m2[1].first, m2[1].second - m2[1].first);
+       } else {
+#ifdef DEBUG2
+           std::cerr << "not match:" << ustring (b, x) << "\n";
+#endif /* DEBUG */
+       }
+       b = m[0].second;
+    }
+}
+
+bool  CGIFormFile::readFilename (int i, ustring& filename) {
+    if (0 <= i && i < parts.size ()) {
+       filename = filenames[i];
+       return true;
+    }
+    return false;
+}
+
+bool  CGIFormFile::saveFile (int i, const ustring& path, size_t max) {
+    static size_t  bsize = 65536;
+    part  p;
+    char*  b;
+    size_t  s, size;
+    FileMacro  f;
+
+    if (0 <= i && i < parts.size ()) {
+       p = parts[i];
+       assert (mapdata <= p.first && p.first <= p.second && p.second < mapdata + mapsize);
+       b = p.first;
+       size = p.second - p.first;
+       if (max > 0 && size > max)
+           return false;
+
+       f.openWrite (path.c_str ());
+#ifdef DEBUG2
+       std::cerr << "write:" << path << "\n";
+#endif /* DEBUG */
+       while (size > 0) {
+           s = (size < bsize) ? size : bsize;
+           ::write (f.fd, b, s);
+           size -= s;
+           b += s;
+       }
+       f.close ();
+       return true;
+    }
+    return false;
+}
+
diff --git a/lib/formfile.h b/lib/formfile.h
new file mode 100644 (file)
index 0000000..ec7e3cf
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef FORMFILE_H
+#define FORMFILE_H
+
+#include "form.h"
+#include "motorenv.h"
+#include "filemacro.h"
+#include <vector>
+#include <utility>
+
+class  CGIFormFile: public CGIForm {
+ public:
+    typedef std::pair<char*,char*>  part;
+
+    ustring  tmpfile;
+    ustring  boundary;
+    uregex  re1;
+    uregex  reN;
+    FileMacro  fp;
+    char*  mapdata;
+    size_t  mapsize;
+    std::vector<part>  parts;
+    std::vector<ustring>  filenames;
+
+    CGIFormFile () {
+       mapdata = NULL;
+    };
+    virtual  ~CGIFormFile () {
+       unlinkTmpFile ();
+    };
+
+    virtual void  read_multipart (MotorEnv* env);
+    virtual bool  saveData (MotorEnv* env);
+    virtual void  unlinkTmpFile ();
+    virtual bool  isMultipart ();
+    virtual void  searchPart (MotorEnv* env);
+    virtual void  compileReg ();
+    virtual void  readMimeHead (char*& b, char* e, ustring& disp, ustring& name, ustring& filename, ustring& type);
+    virtual bool  readFilename (int i, ustring& filename);
+    virtual bool  saveFile (int i, const ustring& path, size_t max);
+};
+
+#endif /* FORMFILE_H */
diff --git a/lib/ftable.h b/lib/ftable.h
new file mode 100644 (file)
index 0000000..f413fae
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef FTABLE_H
+#define FTABLE_H
+
+#include "ustring.h"
+#include <boost/unordered_map.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+
+class  MNode;
+class  MlEnv;
+
+namespace FTableSupport {
+    typedef struct {
+       const char*  name;
+       size_t  namelen;
+       const char*  module;
+       size_t  modlen;
+       MNode*  (*fn) (MNode* arg, MlEnv* mlenv);
+    }  ftable_t;
+}
+
+class  FTable: public boost::unordered_map<ustring, FTableSupport::ftable_t*> {
+public:
+
+    FTable () {};
+    FTable (FTableSupport::ftable_t* t) {
+       int  i;
+       for (i = 0; t[i].name; i ++) {
+           insert (FTable::value_type (ustring (t[i].name, t[i].namelen), &t[i]));
+       }
+    };
+    virtual  ~FTable () {};
+};
+
+class  MTable: public FTable {
+public:
+    boost::ptr_vector<FTable>  iSTableV;
+    boost::unordered_map<ustring, FTable*>  iSTableM;
+
+    MTable (FTableSupport::ftable_t* t, FTableSupport::ftable_t* s): FTable (t) {
+       int  i;
+       FTable*  f;
+       ustring  p;
+       boost::unordered_map<ustring, FTable*>::iterator  it;
+       
+       for (i = 0; t[i].name; i ++) {
+           f = new FTable;
+           iSTableV.push_back (f);
+           iSTableM.insert (boost::unordered_map<ustring, FTable*>::value_type (ustring (t[i].name, t[i].namelen), f));
+       }
+       for (i = 0; s[i].name; i ++) {
+           p = ustring (s[i].module, s[i].modlen);
+           it = iSTableM.find (p);
+           if (it != iSTableM.end ()) {
+               it->second->insert (FTable::value_type (ustring (s[i].name, s[i].namelen), &s[i]));
+           }
+       }
+    };
+    virtual  ~MTable () {};
+};
+
+extern FTable  GFTable;
+extern MTable  GMTable;
+extern FTable  GWikiFTable;
+
+#endif /* FTABLE_H */
diff --git a/lib/heapdebug.cc b/lib/heapdebug.cc
new file mode 100644 (file)
index 0000000..dc809e3
--- /dev/null
@@ -0,0 +1,150 @@
+#include "heapdebug.h"
+#include <iostream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+extern "C"{
+#ifdef Linux
+#include <db_185.h>
+#else
+#include <db.h>
+#endif
+}
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+static void  ex ();
+static void  initDB ();
+class  HdInit {
+public:
+    HdInit () {
+       initDB ();
+    };
+    virtual  ~HdInit () {
+       ex ();
+    };
+};
+
+static HdInit init;
+static int Inited = 0;
+static int Exited = 0;
+static DB*  db;
+
+static void  initDB () {
+    db = dbopen (NULL, O_RDWR, 0, DB_BTREE, NULL);
+    Inited = 1;
+}
+
+static void  ex () {
+    std::cout.flush ();
+    std::cerr.flush ();
+    if (db) {
+#ifdef DEBUG2
+       fprintf (stderr, "atexit()\n");
+       fprintf (stderr, "pid:%d\n", getpid ());
+#endif /* DEBUG */
+       DBT  key, val;
+       int  rc;
+       u_int  seqflag;
+       seqflag = R_FIRST;
+       while ((rc = (*db->seq) (db, &key, &val, seqflag)), (seqflag = R_NEXT), (rc == 0)) {
+           fprintf (stderr, "lost:   %.8x(%.6x|%d)\n",
+                    *(u_int*)key.data, *(u_int*)val.data, *(size_t*)val.data);
+       }
+       (*db->close) (db);
+       db = NULL;
+       Inited = 0;
+       Exited = 1;
+    }
+}
+
+inline void  mdbAdd (void* ptr, size_t sz) {
+    DBT  key, val;
+
+    if (!Inited)
+       initDB ();
+    key.data = &ptr;
+    key.size = sizeof (void*);
+    val.data = &sz;
+    val.size = sizeof (size_t);
+    (*db->put) (db, &key, &val, 0);
+#ifdef HEAPDEBUG_VERBOSE
+    fprintf (stderr, "malloc: %.8x(%.6x|%d)\n", ptr, sz, sz);
+    fflush (stderr);
+#endif
+}
+
+void  mdbDelete (void* ptr) {
+    DBT  key, val;
+
+    if (! db)
+       return;
+
+    key.data = &ptr;
+    key.size = sizeof (void*);
+    if ((*db->get) (db, &key, &val, 0) == 0) {
+#ifdef    HEAPDEBUG_VERBOSE
+       fprintf (stderr, "free  : %.8x(%.6x|%d)\n", ptr, *(size_t*)val.data, *(size_t*)val.data);
+       fflush (stderr);
+#endif /* HEAPDEBUG_VERBOSE */
+       (*db->del) (db, &key, 0);
+    }else{
+       fprintf (stderr, "no matching ptr: %.8x\n", ptr);
+       fflush (stderr);
+    }
+}
+
+void  mdbClose () {
+    (*db->close) (db);
+    db = NULL;
+    Inited = 0;
+    Exited = 1;
+#ifdef DEBUG2
+    fprintf (stderr, "mdbClose()\n");
+#endif /* DEBUG */
+}
+
+#ifdef GCC2
+void*  __builtin_new (size_t sz) {
+    void*  p;
+
+    if (sz == 0) {
+       sz = 1;
+    }
+    p = (void*)malloc (sz);
+    mdbAdd (p, sz);
+    return p;
+}
+
+void  __builtin_delete (void* ptr) {
+    if (ptr) {
+       free (ptr);
+       mdbDelete (ptr);
+    }
+}
+#else
+void*  operator new (size_t sz) //throw (bad_alloc)
+{
+  void *p;
+
+  /* malloc (0) is unpredictable; avoid it.  */
+  if (sz == 0)
+    sz = 1;
+  p = (void *) malloc (sz);
+  mdbAdd (p, sz);
+  return p;
+}
+
+void  operator delete (void *ptr) //throw ()
+{
+    if (ptr) {
+       free (ptr);
+       mdbDelete (ptr);
+    }
+}
+#endif
diff --git a/lib/heapdebug.h b/lib/heapdebug.h
new file mode 100644 (file)
index 0000000..70f24b7
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef HEAPDEBUG_H
+#define HEAPDEBUG_H
+
+#if 0
+class  HeapDebug {
+ public:
+    HeapDebug ();
+    virtual  ~HeapDebug ();
+    virtual void  mdbDump ();
+};
+void  mdbInit ();
+#endif
+
+#endif /* HEAPDEBUG_H */
diff --git a/lib/http.cc b/lib/http.cc
new file mode 100644 (file)
index 0000000..8691bca
--- /dev/null
@@ -0,0 +1,263 @@
+#include "http.h"
+#include "httpconst.h"
+#include "motorenv.h"
+#include "motoroutput.h"
+#include "util_string.h"
+#include "util_time.h"
+#include "util_check.h"
+#include "util_const.h"
+#include "util_random.h"
+#include "ustring.h"
+#include "utf8.h"
+#include <vector>
+#include <iostream>
+#include <stdlib.h>
+
+void  HTTPResponse::printNoCache (std::ostream& o) {
+    o << "Cache-Control: no-cache\n"
+       "Pragma: no-cache\n";
+}
+
+void  HTTPResponse::printCookie (std::ostream& o, MotorEnv* env) {
+    std::vector<ustring>::iterator  it;
+    
+    for (it = setcookie.begin (); it != setcookie.end (); it ++) {
+       o << "Set-Cookie: " << *it << "\n";
+#ifdef DEBUG
+       *env->log << "Set-Cookie: " << *it << "\n";
+#endif /* DEBUG */
+    }
+}
+
+void  HTTPResponse::printLocation (std::ostream& o, MotorEnv* env) {
+    throw (uErrorNotImpl);
+    /* *** */
+}
+
+void  HTTPResponse::setCookie (const ustring& key, const ustring& val, const ustring& path, time_t span, const ustring& domain, bool fsecure) {
+    ustring  ck;
+    umatch  m;
+    static uregex  re ("//|/\\.|\\.\\.|[\\x00-\\x20\\x7f-\\xff]");
+    
+    if (key.size () < 128 && val.size () < 512) {
+       ck = cookieEncode (key);
+       ck.append (CharConst ("="));
+       ck.append (cookieEncode (val));
+       ck.append (CharConst ("; path="));
+       if (path.size () == 0) {
+           char*  e = getenv (kSCRIPT_NAME);
+           if (e) {
+               ck.append (dirPart (e));
+           } else {
+               ck.append (uSlash);
+           }
+       } else {
+           if (usearch (path, m, re)) {
+               if (path.begin () == m[0].first) {
+                   ck.append (uSlash);
+               } else {
+                   ustring::const_iterator  b = path.begin ();
+                   if (path[0] != '/')
+                       ck.append (uSlash);
+                   ck.append (ustring (b, m[0].first));
+               }
+           } else {
+               if (path[0] != '/')
+                   ck.append (uSlash);
+               ck.append (path);
+           }
+       }
+       if (span > 0) {
+           time_t  limit = now () + span;
+           ck.append (CharConst ("; expires="));
+           ck.append (dateCookie (limit));
+       }
+       if (domain.size () > 0 && checkDomainDot (domain)) { // ???
+           ck.append (CharConst ("; domain="));
+           ck.append (domain);
+       }
+       if (fsecure) {
+           ck.append (CharConst ("; secure"));
+       }
+
+       setcookie.push_back (ck);
+    }
+}
+
+void  HTTPResponse::setCookiePair (uiterator& b, const uiterator& e) {
+    umatch  m;
+    ustring  key, val;
+    static uregex  re ("=");
+
+    if (usearch (b, e, m, re)) {
+       if (b != m[0].first) {
+           key = ustring (b, m[0].first);
+           key = omitNonAsciiWord (key);
+           val = ustring (m[0].second, e);
+           val = omitNonAsciiWord (val);
+           val = cookieDecode (val);
+//         fixUTF8 (val);
+           val = fixUTF8 (val);
+           cookiemap.insert (boost::unordered_map<ustring, ustring>::value_type (key, val));
+#ifdef DEBUG2
+           std::cerr << "cookie: " << key << "=" << val << "\n";
+#endif /* DEBUG */
+       }
+    }
+}
+
+void  HTTPResponse::parseCookie () {
+    uiterator  b, e;
+    umatch  m;
+    static uregex  re (" *; *");
+
+    cookieDone = true;
+    cookie = getenvString (kHTTP_COOKIE);
+    b = cookie.begin ();
+    e = cookie.end ();
+    while (usearch (b, e, m, re)) {
+       if (b != m[0].first)
+           setCookiePair (b, m[0].first);
+       b = m[0].second;
+    }
+    if (b != e)
+       setCookiePair (b, e);
+}
+
+ustring  HTTPResponse::readCookie (const ustring& key) {
+    boost::unordered_map<ustring, ustring>::iterator  it;
+
+    if (! cookieDone)
+       parseCookie ();
+
+    it = cookiemap.find (key);
+    if (it != cookiemap.end ()) {
+       return it->second;
+    } else {
+       return uEmpty;
+    }
+}
+
+void  HTTPResponse::setRandomCookie () {
+    ustring  u = smallRandomKey ();
+    setCookie (uR, u, uSlash, 0, uEmpty, false);
+}
+
+void  HTTPResponse::standardResponse (std::ostream& o, const ustring& type, MotorEnv* env) {
+    if (env) {
+       standardResponse (o, type, env->output->encoding (), env);
+    } else {
+       standardResponse (o, type, ustring (), env);
+    }
+}
+
+void  HTTPResponse::standardResponse (std::ostream& o, const ustring& type, const ustring& encoding, MotorEnv* env) {
+    static uregex  re ("^text/");
+    umatch  m;
+
+    if (usearch (type, m, re)) {
+       o << kRES_TYPE << type << "; " kCHARSET << encoding << "\n";
+    } else {
+       o << kRES_TYPE << type << "\n";
+    }
+    if (fNoCache)
+       printNoCache (o);
+    printCookie (o, env);
+    o << "\n";
+}
+
+void  HTTPResponse::standardResponse_html (std::ostream& o, MotorEnv* env) {
+    o << kRES_TYPE kMIME_HTML;
+    if (env && env->output) {
+       o << "; " kCHARSET << env->output->encoding () << "\n";
+    }
+    if (fNoCache)
+       printNoCache (o);
+    printCookie (o, env);
+    o << "\n";
+}
+
+void  HTTPResponse::disposition (std::ostream& o, bool finline, const ustring& name) {
+    ustring  n2;
+    Splitter  sp (name, re_q);
+
+    if (sp.next ()) {
+       if (sp.match (0)) {
+           n2.reserve (name.length ());
+           n2.append (sp.begin (), sp.end ());
+           n2.append (uUScore);
+           while (sp.next ()) {
+               n2.append (sp.begin (), sp.end ());
+               if (sp.match (0)) {
+                   n2.append (uUScore);
+               }
+           }
+       } else {
+           n2 = name;
+       }
+    }
+    if (finline) {
+       o << kRES_DISP kINLINE;
+    } else {
+       o << kRES_DISP kATTACHMENT;
+    }
+    if (n2.length () > 0) {
+       o << "; filename=\"" << n2 << "\"";
+    }
+    o << "\n";
+}
+
+void  HTTPResponse::location (std::ostream& o, const ustring& url, MotorEnv* env) {
+    o << "Status: 302 Moved Temporarily\n";
+    printNoCache (o);
+    o << "Location: " << url << "\n"
+       kRES_TYPE kMIME_TEXT "; " kCHARSET kCODE_UTF8 "\n";
+    printCookie (o, env);
+    o << "\n"
+       "Location: " << url << "\n";
+}
+
+void  HTTPResponse::location_html (std::ostream& o, const ustring& url, MotorEnv* env) {
+    o << kRES_TYPE kMIME_HTML "; " kCHARSET kCODE_UTF8 "\n";
+    printNoCache (o);
+    printCookie (o, env);
+    o << "\n";
+    o << "<html>\n"
+       "<head>\n"
+       "<title></title>\n";
+    o << "<meta http-equiv=\"refresh\" content=\"0;URL="
+      << url
+      << "\">\n"
+       "</head>\n";
+    o << "<body>\n"
+       "<script language=\"JavaScript\" type=\"text/javascript\">\n"
+       "location.href='"
+      << url
+      << "';\n"
+       "</script>\n"
+       "<noscript>go to <a href=\""
+      << url
+      << "\">here</a>.</noscript>\n"
+       "</body>\n"
+       "</html>\n";
+}
+
+void  HTTPResponse::noContentResponse (std::ostream& o, MotorEnv* env) {
+//    o << "Status: 204 No Content\n"
+    o << "Status: 200 No Content\n"
+       kRES_TYPE kMIME_TEXT "; " kCHARSET kCODE_UTF8 "\n";
+    printNoCache (o);
+    printCookie (o, env);
+    o << "\n"
+       "No Content.\n";
+}
+
+void  HTTPResponse::forbiddenResponse (std::ostream& o, MotorEnv* env) {
+    o << "Status: 403 Forbidden\n"
+       kRES_TYPE kMIME_TEXT "; " kCHARSET kCODE_UTF8 "\n";
+    printNoCache (o);
+    printCookie (o, env);
+    o << "\n"
+       "Forbidden.\n";
+}
+
diff --git a/lib/http.h b/lib/http.h
new file mode 100644 (file)
index 0000000..febf631
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef HTTP_H
+#define HTTP_H
+
+#include "ustring.h"
+#include <boost/unordered_map.hpp>
+#include <iostream>
+
+class  MotorEnv;
+class  HTTPResponse {
+ public:
+    std::vector<ustring>  setcookie;
+    bool  cookieDone;
+    ustring  cookie;
+    boost::unordered_map<ustring, ustring>  cookiemap;
+    bool  fNoCache;
+
+    HTTPResponse (): cookieDone (false), fNoCache (false) {};
+    virtual  ~HTTPResponse () {};
+    virtual void  printNoCache (std::ostream& o);
+    virtual void  printCookie (std::ostream& o, MotorEnv* env);
+    virtual void  printLocation (std::ostream& o, MotorEnv* env);
+    virtual void  setCookie (const ustring& key, const ustring& val, const ustring& path, time_t span, const ustring& domain, bool fsecure);
+    virtual void  setCookiePair (uiterator& b, const uiterator& e);
+    virtual void  parseCookie ();
+    virtual ustring  readCookie (const ustring& key);
+    virtual void  setRandomCookie ();
+    virtual void  standardResponse (std::ostream& o, const ustring& type, MotorEnv* env);
+    virtual void  standardResponse (std::ostream& o, const ustring& type, const ustring& encoding, MotorEnv* env);
+    virtual void  standardResponse_html (std::ostream& o, MotorEnv* env);
+    virtual void  disposition (std::ostream& o, bool finline, const ustring& name);
+    virtual void  location (std::ostream& o, const ustring& url, MotorEnv* env);
+    virtual void  location_html (std::ostream& o, const ustring& url, MotorEnv* env);
+    virtual void  noContentResponse (std::ostream& o, MotorEnv* env);
+    virtual void  forbiddenResponse (std::ostream& o, MotorEnv* env);
+};
+
+#endif /* HTTP_H */
diff --git a/lib/httpconst.h b/lib/httpconst.h
new file mode 100644 (file)
index 0000000..e06947e
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef HTTPCONST_H
+#define HTTPCONST_H
+
+#define kHTTP_COOKIE           "HTTP_COOKIE"
+#define kHTTP_HOST             "HTTP_HOST"
+#define kHTTP_REFERER          "HTTP_REFERER"
+#define kHTTP_USER_AGENT       "HTTP_USER_AGENT"
+
+#define kCONTENT_LENGTH                "CONTENT_LENGTH"
+#define kCONTENT_TYPE          "CONTENT_TYPE"
+#define kDOCUMENT_ROOT         "DOCUMENT_ROOT"
+#define kHTTPS                 "HTTPS"
+#define kPATH_INFO             "PATH_INFO"
+#define kQUERY_STRING          "QUERY_STRING"
+#define kREDIRECT_URL          "REDIRECT_URL"
+#define kREMOTE_ADDR           "REMOTE_ADDR"
+#define kREMOTE_USER           "REMOTE_USER"
+#define kREQUEST_METHOD                "REQUEST_METHOD"
+#define kREQUEST_URI           "REQUEST_URI"
+#define kSCRIPT_FILENAME       "SCRIPT_FILENAME"
+#define kSCRIPT_NAME           "SCRIPT_NAME"
+#define kSERVER_ADDR           "SERVER_ADDR"
+#define kSERVER_NAME           "SERVER_NAME"
+#define kSERVER_PORT           "SERVER_PORT"
+#define kSERVER_PROTOCOL       "SERVER_PROTOCOL"
+
+#define kSSL_CLIENT_M_SERIAL   "SSL_CLIENT_M_SERIAL"
+#define kSSL_CLIENT_S_DN       "SSL_CLIENT_S_DN"
+#define kSSL_CLIENT_I_DN       "SSL_CLIENT_I_DN"
+#define SSL_CIPHER             "SSL_CIPHER"
+#define SSL_CIPHER_ALGKEYSIZE  "SSL_CIPHER_ALGKEYSIZE"
+#define SSL_CIPHER_EXPORT      "SSL_CIPHER_EXPORT"
+#define SSL_CIPHER_USEKEYSIZE  "SSL_CIPHER_USEKEYSIZE"
+#define SSL_CLIENT_VERIFY      "SSL_CLIENT_VERIFY"
+#define SSL_COMPRESS_METHOD    "SSL_COMPRESS_METHOD"
+#define SSL_PROTOCOL           "SSL_PROTOCOL"
+#define SSL_SERVER_A_KEY       "SSL_SERVER_A_KEY"
+#define SSL_SERVER_A_SIG       "SSL_SERVER_A_SIG"
+#define SSL_SERVER_I_DN                "SSL_SERVER_I_DN"
+#define SSL_SERVER_I_DN_C      "SSL_SERVER_I_DN_C"
+#define SSL_SERVER_I_DN_O      "SSL_SERVER_I_DN_O"
+#define SSL_SERVER_I_DN_OU     "SSL_SERVER_I_DN_OU"
+#define SSL_SERVER_M_SERIAL    "SSL_SERVER_M_SERIAL"
+#define SSL_SERVER_M_VERSION   "SSL_SERVER_M_VERSION"
+#define SSL_SERVER_S_DN                "SSL_SERVER_S_DN"
+#define SSL_SERVER_S_DN_C      "SSL_SERVER_S_DN_C"
+#define SSL_SERVER_S_DN_CN     "SSL_SERVER_S_DN_CN"
+#define SSL_SERVER_S_DN_L      "SSL_SERVER_S_DN_L"
+#define SSL_SERVER_S_DN_O      "SSL_SERVER_S_DN_O"
+#define SSL_SERVER_S_DN_ST     "SSL_SERVER_S_DN_ST"
+#define SSL_SERVER_V_END       "SSL_SERVER_V_END"
+#define SSL_SERVER_V_START     "SSL_SERVER_V_START"
+#define SSL_SESSION_ID         "SSL_SESSION_ID"
+#define kMETHOD_GET            "GET"
+#define kMETHOD_POST           "POST"
+#define kMIME_FORMDATA         "multipart/form-data"
+#define kMIME_HTML             "text/html"
+#define kMIME_TEXT             "text/plain"
+#define kMIME_XML              "text/xml"
+#define kMIME_OCTET            "application/octet-stream"
+#define kRES_TYPE              "Content-type: "
+#define kRES_DISP              "Content-Disposition: "
+#define kCHARSET               "charset="
+#define kATTACHMENT            "attachment"
+#define kINLINE                        "inline"
+#define kCODE_UTF8             "UTF-8"
+
+#endif /* HTTPCONST_H */
diff --git a/lib/iso2022jp.cc b/lib/iso2022jp.cc
new file mode 100644 (file)
index 0000000..1d838fc
--- /dev/null
@@ -0,0 +1,26 @@
+#include "iso2022jp.h"
+#include "util_string.h"
+#include "ustring.h"
+
+ustring  mimeEncodeJP (const ustring& text) {
+    ustring  ans;
+    uiterator  b, e;
+    umatch  m;
+    ustring  t;
+    static uregex  re ("(\\033\\$[@B][^\\033]*\\033\\([BJ])");
+    
+    b = text.begin ();
+    e = text.end ();
+    while (usearch (b, e, m, re)) {
+       if (b != m[0].first)
+           ans.append (b, m[0].first);
+       ans.append (CharConst ("=?ISO-2022-JP?B?"));
+       t = base64encode (m[0].first, m[0].second);
+       ans.append (t);
+       ans.append (CharConst ("?="));
+       b = m[0].second;
+    }
+    if (b != e)
+       ans.append (b, e);
+    return ans;
+}
diff --git a/lib/iso2022jp.h b/lib/iso2022jp.h
new file mode 100644 (file)
index 0000000..e0f5821
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef ISO2022JP_H
+#define ISO2022JP_H
+
+#include "ustring.h"
+
+ustring  mimeEncodeJP (const ustring& text);
+
+#endif /* ISO2022JP_H */
diff --git a/lib/mftable.h b/lib/mftable.h
new file mode 100644 (file)
index 0000000..2d6e57b
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef MFTABLE_H
+#define MFTABLE_H
+
+#include "ustring.h"
+#include <boost/unordered_map.hpp>
+//#include <boost/ptr_container/ptr_vector.hpp>
+#include <vector>
+
+class  MlEnv;
+
+namespace MFTableSupport {
+    typedef  struct {
+       const char*  name;
+       int  namelen;
+       void  (*fn) (std::vector<ustring>& args, MlEnv* mlenv);
+    }  mftable_t;
+}
+
+class  MFTable: public boost::unordered_map<ustring, MFTableSupport::mftable_t*> {
+public:
+    boost::ptr_vector<MFTable>  iSTableV;
+    boost::unordered_map<ustring, MFTable*>  iSTableM;
+
+    MFTable () {};
+    MFTable (MFTableSupport::mftable_t* t) {
+       int  i;
+       for (i = 0; t[i].name; i ++) {
+           insert (MFTable::value_type (ustring (t[i].name, t[i].namelen), &t[i]));
+       }
+    };
+    virtual  ~MFTable () {};
+};
+
+extern MFTable  IMFTable;
+
+#endif /* MFTABLE_H */
diff --git a/lib/ml.cc b/lib/ml.cc
new file mode 100644 (file)
index 0000000..36e156e
--- /dev/null
+++ b/lib/ml.cc
@@ -0,0 +1,548 @@
+#include "ml.h"
+#include "config.h"
+#include "mlenv.h"
+#include "expr.h"
+#include "utf8.h"
+#include "ustring.h"
+#include "util_string.h"
+#include "util_const.h"
+#include <iostream>
+#include <boost/lexical_cast.hpp>
+#include <exception>
+#include <assert.h>
+#include <stdlib.h>
+
+void  MNode::fdelete () {
+#ifdef DEBUG2
+    std::cerr << "fdelete:" << std::hex << this << std::dec << ":";
+    this->dump (std::cerr);
+    std::cerr << "\n";
+#endif /* DEBUG */
+    assert (type != MC_DELETED);
+    switch (type) {
+    case MC_CONS:
+       if (cons.car) {
+           if (cons.car->refcount <= 1) {
+               delete cons.car;
+           } else {
+               cons.car->refcount --;
+           }
+           cons.car = NULL;
+       }
+       if (cons.cdr) {
+           if (cons.cdr->refcount <= 1) {
+               delete cons.cdr;
+           } else {
+               cons.cdr->refcount --;
+           }
+       }
+       break;
+    case MC_STR:
+       delete str;
+       str = NULL;
+       break;
+    case MC_SYM:
+       delete sym;
+       sym = NULL;
+       break;
+    }
+    type = MC_DELETED;
+}
+
+void  MNode::swap (MNode& b) {
+    char  u[sizeof (MNode)];
+    memcpy (u, &b, sizeof (MNode));
+    memcpy (&b, this, sizeof (MNode));
+    memcpy (this, u, sizeof (MNode));
+}
+
+double  MNode::to_double () {
+    switch (type) {
+    case MNode::MC_NIL:
+       return 0.;
+    case MNode::MC_CONS:
+       return 0.;
+    case MNode::MC_STR:
+       return atof (str->c_str ());
+    case MNode::MC_SYM:
+       return atof (sym->c_str ());
+    case MNode::MC_DOUBLE:
+       return real;
+    default:
+       assert (0);
+    }
+}
+
+bool  MNode::to_bool () {
+    switch (type) {
+    case MNode::MC_NIL:
+       return false;
+    case MNode::MC_CONS:
+       return true;
+    case MNode::MC_STR:
+       return ::to_bool (*str);
+    case MNode::MC_SYM:
+       if (sym->length () > 0) {
+           return true;
+       } else {
+           return false;
+       }
+    case MNode::MC_DOUBLE:
+       if (real == 0.) {
+           return false;
+       } else {
+           return true;
+       }
+    default:
+       assert (0);
+    }
+}
+
+ustring  MNode::to_string (bool c, bool listdump) {
+    switch (type) {
+    case MNode::MC_NIL:
+       return uEmpty;
+    case MNode::MC_CONS:
+       {
+           ustring  ans;
+           if (! c)
+               ans.append (CharConst ("("));
+           if (cons.car)
+               ans.append (cons.car->to_string (false, listdump));
+           else
+               ans.append (uNil);
+           if (cons.cdr) {
+               switch (cons.cdr->type) {
+               case MC_NIL:
+                   ans.append (CharConst (")"));
+                   break;
+               case MC_CONS:
+                   ans.append (uSPC);
+                   ans.append (cons.cdr->to_string (true, listdump));
+                   break;
+               default:
+                   ans.append (CharConst (" . "));
+                   ans.append (cons.cdr->to_string (true, listdump));
+                   ans.append (CharConst (")"));
+               }               
+           } else {
+               ans.append (CharConst (")"));
+           }
+           return ans;
+       }
+    case MNode::MC_STR:
+       if (listdump) {
+           return ustring (CharConst ("\"")).append (slashEncode (*str)).append (CharConst ("\""));
+       } else {
+           return ustring (*str);
+//     return *str;
+       }
+    case MNode::MC_SYM:
+       return ustring (*sym);
+//     return *sym;
+    case MNode::MC_DOUBLE:
+       return boost::lexical_cast<ustring> (real);
+    default:
+       assert (0);
+    }
+}
+
+void  MNode::dump (std::ostream& o, bool c) {
+    switch (type) {
+    case MC_NIL:
+//     o << uNil;
+       o << uNil2;
+       break;
+    case MC_SYM:
+       o << logText (*sym);
+       break;
+    case MC_STR:
+       o << "\"" << logText (*str) << "\"";
+       break;
+    case MC_CONS:
+       if (! c && car () && car ()->type == MC_SYM && car ()->str->compare ("quote") == 0 && cdr () && cdr ()->type == MC_CONS && cdr ()->car ()) {
+           o << "'";
+           cdr ()->car ()->dump (o);
+       } else {
+           if (! c)
+               o << "(";
+           if (car ())
+               car ()->dump (o);
+           else
+               o << uNil;
+           if (cdr ()) {
+               switch (cdr ()->type) {
+               case MC_NIL:
+                   o << ")";
+                   break;
+               case MC_CONS:
+                   o << " ";
+                   cdr ()->dump (o, true);
+                   break;
+               default:
+                   o << " . ";
+                   cdr ()->dump (o, true);
+                   o << ")";
+               }
+           } else {
+               o << ")";
+           }
+       }
+       break;
+    case MC_DOUBLE:
+       o << boost::lexical_cast<ustring> (real);
+       break;
+    default:
+       assert (0);
+    }
+}
+
+ustring  MNode::dump_string (bool c) {
+    switch (type) {
+    case MC_NIL:
+//     return uNil;
+       return uNil2;
+    case MC_SYM:
+       return logText (*sym);
+    case MC_STR:
+       return ustring (CharConst ("\"")) + logText (*str) + "\"";
+    case MC_CONS:
+       {
+           ustring  ans;
+           if (! c && car () && car ()->type == MC_SYM && car ()->str->compare ("quote") == 0 && cdr () && cdr ()->type == MC_CONS && cdr ()->car ()) {
+               ans.append (CharConst ("'"));
+               ans.append (cdr ()->car ()->dump_string ());
+           } else {
+               if (! c)
+                   ans.append (CharConst ("("));
+               if (car ())
+                   ans.append (car ()->dump_string ());
+               else
+                   ans.append (uNil);
+               if (cdr ()) {
+                   switch (cdr ()->type) {
+                   case MC_NIL:
+                       ans.append (CharConst (")"));
+                       break;
+                   case MC_CONS:
+                       ans.append (uSPC);
+                       ans.append (cdr ()->dump_string (true));
+                       break;
+                   default:
+                       ans.append (CharConst (" . "));
+                       ans.append (cdr ()->dump_string (true));
+                       ans.append (CharConst (")"));
+                   }
+               } else {
+                   ans.append (CharConst (")"));
+               }
+           }
+           return ans;
+       }
+    case MC_DOUBLE:
+       return boost::lexical_cast<ustring> (real);
+    default:
+       if (type == MC_DELETED)
+           std::cerr << "type:DELETED\n";
+       else
+           std::cerr << "type:" << type << "\n";
+       assert (0);
+    }
+}
+
+ustring  MNode::dump_string_short () {
+    ustring  u = dump_string ();
+
+    return ellipsis (u, cLOGSHORTSIZE);
+}
+
+void  MotorSexp::scan (const ustring& text, bool skip) {
+    uiterator  b = text.begin ();
+    uiterator  e = text.end ();
+    word_type  type;
+    ustring*  s;
+    MNode*  cell = &top;
+    int  linenum = 1;
+
+    if (skip) {
+       skipHead (b, e, linenum);
+    }
+    while (b != e) {
+       if (skip) {
+           skipWhite (linenum, b, e);
+           if (*b == '<') {
+#ifdef DEBUG
+               std::cerr << "---:" << ustring (b, b + 20) << "\n";
+#endif /* DEBUG */
+               return;
+           }
+       }
+#if 0
+       newMNodeCdr (cell);
+       if (scanCar (linenum, b, e, cell)) {
+           status = S_SURPLUS_PAREN;
+           return;
+       }
+#endif
+       if (scanSexp (linenum, b, e, cell, true)) {
+           status = S_SURPLUS_PAREN;
+           return;
+       }
+    }
+}
+
+void  MotorSexp::skipHead (uiterator& b, uiterator& e, int& linenum) {
+    umatch  m;
+    while (b != e && (*b != '(' && *b != ';')) {
+       if (usearch (b, e, m, re_nl)) {
+           b = m[0].second;
+           linenum ++;
+       } else {
+           b = e;
+       }
+    }
+}
+
+void  MotorSexp::scanWord (int& linenum, uiterator& b, uiterator& e, word_type& type, ustring*& ans) {
+    ustring::value_type  c;
+    umatch  m;
+    static uregex  re ("^[^\\x00- ()\";]+");
+    static uregex  re_num ("[\\-+]?[0-9]+(\\.[0-9]*)?");
+ Ep1:;
+    skipWhite (linenum, b, e);
+    if (b != e) {
+       c = *b;
+       switch (c) {
+       case '(':
+           b ++;
+           type = YYPAR;
+           break;
+       case ')':
+           b ++;
+           type = YYREN;
+           break;
+       case '\"':
+           b ++;
+           type = YYTEXT;
+           ans = scanText (linenum, b, e);
+           break;
+       case ';':
+           b ++;
+           if (usearch (b, e, m, re_nl)) {
+               b = m[0].second;
+               linenum ++;
+               goto Ep1;
+           } else {
+               // end of comment but without nl.
+               b = e;
+               type = YYNONE;
+           }
+           break;
+       default:
+           if (usearch (b, e, m, re)) {
+               assert (b == m[0].first);
+               if (*b == '\'') {
+                   b ++;
+                   type = YYQUOTE;
+                   ans = NULL;
+               } else {
+                   umatch  m2;
+                   if (regex_match (m[0].first, m[0].second, m2, re_num)) {
+                       type = YYNUM;
+                       ans = new ustring (m[0]);
+                       b = m[0].second;
+                   } else {
+                       type = YYSIM;
+                       ans = new ustring (m[0]);
+                       b = m[0].second;
+                   }
+               }
+               skipWhite (linenum, b, e);
+           } else {
+               assert (0);
+           }
+       }
+    } else {
+       type = YYNONE;
+    }
+}
+
+ustring*  MotorSexp::scanText (int& linenum, uiterator& b, uiterator& e) {
+    ustring::value_type  c;
+    uiterator  p = b;
+    ustring*  ans = new ustring;
+
+    ans->reserve (128);
+    while (b != e) {
+       c = *b;
+       if (c == '\"') {
+           b ++;
+           return ans;
+       } else if (c == '\\') {
+           b ++;
+           if (b != e) {
+               c = *(b ++);
+               switch (c) {
+               case 't':
+                   (*ans) += '\t';
+                   break;
+               case 'n':
+                   (*ans) += '\n';
+                   break;
+               case 'r':
+                   (*ans) += '\r';
+                   break;
+               case '\n':
+                   break;
+               default:
+                   (*ans) += c;
+               }
+           }
+       } else if (c == '\n') {
+           linenum ++;
+           nextChar (b, e, *ans);
+       } else {
+           nextChar (b, e, *ans);
+       }
+    }
+
+    // XXX no matching quote
+    return ans;
+}
+
+void  MotorSexp::skipWhite (int& linenum, uiterator& b, uiterator& e) {
+    ustring::value_type  c;
+
+    for (; b != e; b ++) {     // hack
+       c = *b;
+       if (0 <= c && c <= ' ') {
+           if (c == '\n') {
+               linenum ++;
+           }
+       } else {
+           break;
+       }
+    }
+}
+
+bool  MotorSexp::scanCar (int& linenum, uiterator& b, uiterator& e, MNode* cell) {
+    if (b != e) {
+       if (scanSexp (linenum, b, e, cell, false))
+           return true;
+    }
+    while (b != e) {
+       if (scanSexp (linenum, b, e, cell, true))
+           return true;
+    }
+    // reached at the end of the text.
+    return false;
+}
+
+void  MotorSexp::scanQuote (int& linenum, uiterator& b, uiterator& e, MNode* cell) {
+    cell->set_car (newMNode_sym (new ustring (CharConst ("quote"))));
+    newMNodeCdr (cell);
+
+    if (b != e) {
+       if (scanSexp (linenum, b, e, cell, false))
+           throw (uErrorSyntax);       // ')
+    }
+}
+
+bool  MotorSexp::scanSexp (int& linenum, uiterator& b, uiterator& e, MNode*& cell, bool qcdr) {
+    word_type  type;
+    ustring*  s;
+    MNode*  x;
+
+    scanWord (linenum, b, e, type, s);
+    switch (type) {
+    case YYNONE:
+       break;
+    case YYPAR:
+       if (qcdr)
+           newMNodeCdr (cell);
+       x = new MNode;
+       mlenv->push_linenum (x, linenum);
+       if (! scanCar (linenum, b, e, x)) {
+           status = S_MISSING_PAREN;
+       }
+       if (x->isNil ()) {
+           delete x;
+           cell->set_car (NULL);
+       } else {
+           cell->set_car (x);
+       }
+#if 0
+       {
+           MNode  l;
+           MNode*  lp = &l;
+           int  n = linenum;
+           while (b != e) {
+               if (scanSexp (linenum, b, e, lp, true)) {
+                   goto Ep1;
+               }
+           }
+           status = S_MISSING_PAREN;
+       Ep1:
+           if (l.isCons ()) {
+               mlenv->push_linenum (l.cdr (), n);
+               cell->set_car (l.cdr ());
+           }
+       }
+#endif
+       break;
+    case YYQUOTE:
+       if (qcdr)
+           newMNodeCdr (cell);
+       x = new MNode;
+       cell->set_car (x);
+       mlenv->push_linenum (x, linenum);
+       scanQuote (linenum, b, e, x);
+       break;
+    case YYREN:
+       return true;            // end with )
+    case YYTEXT:
+       if (qcdr)
+           newMNodeCdr (cell);
+       cell->set_car (newMNode_str (s));
+       break;
+    case YYSIM:
+       if (qcdr)
+           newMNodeCdr (cell);
+       cell->set_car (newMNode_sym (s));
+       break;
+    case YYNUM:
+       if (qcdr)
+           newMNodeCdr (cell);
+       cell->set_car (newMNode_num (atof (s->c_str ())));
+       delete s;
+       break;
+    default:;
+       assert (0);
+    }
+    return false;
+}
+
+void  nextNode (MNode*& arg) {
+    if (arg) {
+       switch (arg->type) {
+       case MNode::MC_NIL:
+           arg = NULL;
+           break;
+       case MNode::MC_CONS:
+           arg = arg->cdr ();
+           if (arg && ! arg->isCons ()) {
+               throw (uErrorWrongType);
+           }
+           break;
+       default:
+           throw (uErrorWrongType);
+       }
+    }
+}
+
+void  nextNodeNonNil (MNode*& arg) {
+    nextNode (arg);
+    if (! arg)
+       throw (uErrorWrongNumber);
+}
+
diff --git a/lib/ml.h b/lib/ml.h
new file mode 100644 (file)
index 0000000..86b4821
--- /dev/null
+++ b/lib/ml.h
@@ -0,0 +1,386 @@
+#ifndef ML_H
+#define ML_H
+
+#include "util_const.h"
+#include "ustring.h"
+#include <iostream>
+#include <assert.h>
+
+class  MlEnv;
+class  MNode {
+ public:
+    typedef enum {
+       MC_NIL,
+       MC_CONS,
+       MC_STR,
+       MC_SYM,
+       MC_DOUBLE,
+//     MC_INT,
+       MC_DELETED,
+    }  ptr_type;
+
+    ptr_type  type;
+    int  refcount;
+    union {
+       struct {
+           MNode*  car;
+           MNode*  cdr;
+       }  cons;
+       ustring*  str;
+       ustring*  sym;
+       double  real;
+       // long long  num;
+    };
+    MNode () {
+       type = MC_NIL;
+       cons.car = NULL;
+       cons.cdr = NULL;
+       refcount = 0;
+    };
+    MNode (bool v) {
+    };
+    ~MNode () {
+       fdelete ();
+    };
+    
+    void  fdelete ();
+    MNode*  refinc () {
+       refcount ++;
+       return this;
+    };
+    void  swap (MNode& b);
+    void  set_car (MNode* v) {
+       assert (type == MC_NIL || (type == MC_CONS || cons.car == NULL));
+       type = MC_CONS;
+       cons.car = v;
+       if (v)
+           cons.car->refcount ++;
+    };
+    void  unset_car () {
+       assert (type == MC_CONS);
+       if (cons.car) {
+           if (cons.car->refcount <= 1) {
+               delete cons.car;
+           } else {
+               cons.car->refcount --;
+           }
+           cons.car = NULL;
+       }
+    };
+    void  unset_cdr () {
+       assert (type == MC_CONS);
+       if (cons.cdr) {
+           if (cons.cdr->refcount <= 1) {
+               delete cons.cdr;
+           } else {
+               cons.cdr->refcount --;
+           }
+           cons.cdr = NULL;
+       }
+    };
+    void  set_cdr (MNode* v) {
+       assert (type == MC_NIL || (type == MC_CONS || cons.cdr == NULL));
+       type = MC_CONS;
+       cons.cdr = v;
+       if (cons.cdr)
+           cons.cdr->refcount ++;
+    };
+    MNode*  release_cdr () {
+       MNode*  ans = cdr ();
+       set_cdr (NULL);
+       if (ans)
+           ans->refcount --;
+       return ans;
+    };
+    void  set_str (ustring* v) {
+       assert (type == MC_NIL);
+       type = MC_STR;
+       str = v;
+    };
+    void  set_sym (ustring* v) {
+       assert (type == MC_NIL);
+       type = MC_SYM;
+       sym = v;
+    };
+    void  set (double v) {
+       assert (type == MC_NIL);
+       type = MC_DOUBLE;
+       real = v;
+    };
+    void  set_nil () {
+       assert (type == MC_NIL);
+    };
+    void  set_bool (bool v) {
+       if (v)
+           set (1.);
+       else
+           set_nil ();
+    };
+    // void  set (int v);
+    inline bool  isNil () {
+       return type == MC_NIL;
+    };
+    inline bool  isCons () {
+       return type == MC_CONS;
+    };
+    inline bool  isStr () {
+       return type == MC_STR;
+    };
+    inline bool  isSym () {
+       return type == MC_SYM;
+    };
+    inline bool  isReal () {
+       return type == MC_DOUBLE;
+    };
+    inline MNode*  car () {
+//     assert (type == MC_CONS);
+       if (type != MC_CONS)
+           throw (uErrorWrongType);
+       return cons.car;
+    };
+    inline MNode*  cdr () {
+//     assert (type == MC_CONS);
+       if (type != MC_CONS)
+           throw (uErrorWrongType);
+       return cons.cdr;
+    };
+    double  to_double ();
+    int  to_int () {
+       return (int)to_double ();
+    };
+    bool  to_bool ();
+    ustring  to_string (bool c = false, bool listdump = false);
+    void  dump (std::ostream& o, bool c = false);
+    ustring  dump_string (bool c = false);
+    ustring  dump_string_short ();
+};
+
+inline bool  isNil (MNode* a) {
+    return (a == NULL || a->isNil ());
+}
+
+inline bool  eq (MNode* a, MNode* b) {
+    if (a && b) {
+       if (a->type == b->type) {
+           switch (a->type) {
+           case MNode::MC_NIL:
+               return true;
+           case MNode::MC_CONS:
+               if (a->car () == b->car () && a->cdr () == b->cdr ())
+                   return true;
+               else
+                   return false;
+           case MNode::MC_STR:
+               if (*a->str == *b->str)
+                   return true;
+               else
+                   return false;
+           case MNode::MC_SYM:
+               if (*a->sym == *b->sym)
+                   return true;
+               else
+                   return false;
+           case MNode::MC_DOUBLE:
+               if (a->real == b->real)
+                   return true;
+               else
+                   return false;
+           default:
+               assert (0);
+           }
+       } else {
+           return false;
+       }
+    } else if (a == NULL && b == NULL) {
+       return true;
+    } else {
+       return false;
+    }
+};
+
+inline double  to_double (MNode* c) {
+    if (c)
+       return c->to_double ();
+    else
+       return 0.;
+}
+
+inline int  to_int (MNode* c) {
+    return (int)to_double (c);
+}
+
+inline bool  to_bool (MNode* c) {
+    if (c)
+       return c->to_bool ();
+    else
+       return false;
+}
+
+inline ustring  to_string (MNode* c) {
+    if (c)
+       return c->to_string ();
+    else
+       return uEmpty;
+}
+
+inline MNode*  newMNode_sym (ustring* v) {
+    MNode*  ans = new MNode;
+    ans->set_sym (v);
+    return ans;
+}
+
+inline MNode*  newMNode_str (ustring* v) {
+    MNode*  ans = new MNode;
+    ans->set_str (v);
+    return ans;
+}
+
+inline MNode*  newMNode_num (double v) {
+    MNode*  ans = new MNode;
+    ans->set (v);
+    return ans;
+}
+
+inline MNode*  newMNode_bool (bool v) {
+    if (v) {
+       MNode*  ans = new MNode;
+       ans->set_bool (v);
+       return ans;
+    } else {
+       return NULL;
+    }
+}
+
+inline void  newMNodeCdr (MNode*& cell) {
+    MNode*  x = new MNode;
+    cell->set_cdr (x);
+    cell = x;
+}
+
+class  MNodePtr {
+ public:
+    MNode*  p;
+
+    MNodePtr (): p (NULL) {};
+    ~MNodePtr () {
+       fdelete (p);
+       p = NULL;
+    };
+    void  fdelete (MNode* x) {
+       if (x) {
+           if (x->refcount <= 1)
+               delete x;
+           else
+               x->refcount --;
+       }
+    };
+    MNode*  operator () () {
+       return p;
+    };
+    MNode*  operator = (MNode* b) {
+       MNode*  x = p;
+       p = b;
+       if (p)
+           p->refcount ++;
+       fdelete (x);
+       return p;
+    };
+    MNode*  operator = (MNodePtr& b) {
+       MNode*  x = p;
+       p = b ();
+       if (p)
+           p->refcount ++;
+       fdelete (x);
+       return p;
+    };
+    MNode*  release () {
+       MNode*  ans = p;
+       p = NULL;
+       if (ans)
+           ans->refcount --;
+       return ans;
+    };
+};
+
+class  MNodeList {
+ public:
+    MNodePtr  ans;
+    MNode*  a;
+
+    MNodeList () {
+       a = NULL;
+    };
+    virtual  ~MNodeList () {};
+
+    virtual void  append (MNode* v) {
+       if (a) {
+           newMNodeCdr (a);
+       } else {
+           ans = a = new MNode;
+       }
+       a->set_car (v);
+    };
+    virtual void  set_cdr_cut (MNode* v) {
+       if (a) {
+           a->set_cdr (v);
+       } else {
+           ans = v;
+       }
+    };
+#if 0 // not tested
+    virtual void  set_cdr (MNode* v) {
+       set_cdr_cur (v);
+       for (a = v; a && a->isCons () && a->cdr (); a = a->cdr ()) {}
+    };
+#endif
+    virtual MNode*  operator () () {
+       return ans ();
+    };
+    virtual MNode*  release () {
+       return ans.release ();
+    };
+};
+
+class  MotorSexp {
+    typedef enum {
+       YYNONE, YYPAR, YYREN, YYTEXT, YYSIM, YYNUM, YYQUOTE,
+    }  word_type;
+
+ public:
+    MNode  top;
+    MlEnv*  mlenv;
+    enum {
+       S_NORMAL,
+       S_MISSING_PAREN,
+       S_SURPLUS_PAREN,
+    }  status;
+
+    MotorSexp (MlEnv* e) {
+       mlenv = e;
+       status = S_NORMAL;
+    };
+    virtual  ~MotorSexp () {};
+
+    virtual void  scan (const ustring& text, bool skip = false);
+    virtual void  skipHead (uiterator& b, uiterator& e, int& linenum);
+    virtual void  scanWord (int& linenum, uiterator& b, uiterator& e, word_type& type, ustring*& ans);
+    virtual ustring*  scanText (int& linenum, uiterator& b, uiterator& e);
+    virtual void  skipWhite (int& linenum, uiterator& b, uiterator& e);
+    virtual bool  scanCar (int& linenum, uiterator& b, uiterator& e, MNode* cell);
+    virtual void  scanQuote (int& linenum, uiterator& b, uiterator& e, MNode* cell);
+    virtual bool  scanSexp (int& linenum, uiterator& b, uiterator& e, MNode*& cell, bool qcdr);
+};
+
+class  MLFunc {
+ public:
+    int  id;
+
+    MLFunc (int v): id (v) {};
+    virtual  ~MLFunc () {};
+    
+};
+
+void  nextNode (MNode*& arg);
+void  nextNodeNonNil (MNode*& arg);
+
+#endif /* ML_H */
diff --git a/lib/mlenv.cc b/lib/mlenv.cc
new file mode 100644 (file)
index 0000000..38aa379
--- /dev/null
@@ -0,0 +1,413 @@
+#include "mlenv.h"
+#include "ml.h"
+#include "util_check.h"
+#include "util_time.h"
+#include "util_string.h"
+#include "config.h"
+#include "motorconst.h"
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <boost/unordered_map.hpp>
+
+void  MlEnv::setStartTime () {
+    starttime = now ();
+    limittime = starttime + cDEFAULTTIMELIMIT;
+}
+
+bool  MlEnv::qtimeup () {
+    if (now () > limittime) {
+       *log << "timeup\n";
+       return true;
+    } else {
+       return false;
+    }
+}
+
+void  MlEnv::inclIncCount () {
+    if (includeCount == kIncludeMax)
+       throw (uErrorInclNest);
+    includeCount ++;
+}
+
+void  MlEnv::declIncCount () {
+    assert (includeCount > 0);
+    includeCount --;
+}
+
+bool  MlEnv::validName (const ustring& name) {
+    umatch  m;
+    static uregex  re_var (rMOTORVAR);
+
+    return (regex_match (name, m, re_var));
+}
+
+void  MlEnv::setGlobalAry (const ustring& name, size_t i, MNode* val) {
+    ustring  a (name);
+    a.append (uUScore);
+    a.append (boost::lexical_cast<ustring> (i));
+    globalVar.setVar (a, val);
+#ifdef DEBUG
+    logSetVar (a, val);
+#endif /* DEBUG */
+}
+
+void  MlEnv::setGlobalArySize (const ustring& name, size_t n) {
+    ustring  a (name);
+    MNode*  val;
+    a.append (CharConst ("_n"));
+    val = newMNode_num (n);
+    globalVar.setVar (a, val);
+#ifdef DEBUG
+    logSetVar (a, val);
+#endif /* DEBUG */
+}
+
+void  MlEnv::setLocalAry (MotorVar* pool, const ustring& name, size_t i, MNode* val) {
+    ustring  a (name);
+    a.append (uUScore);
+    a.append (boost::lexical_cast<ustring> (i));
+    pool->setVar (a, val);
+#ifdef DEBUG
+    logSetVar (a, val, true);
+#endif /* DEBUG */
+}
+
+void  MlEnv::setLocalArySize (MotorVar* pool, const ustring& name, size_t n) {
+    ustring  a (name);
+    MNode*  val;
+    a.append (CharConst ("_n"));
+    val = newMNode_num (n);
+    pool->setVar (a, val);
+#ifdef DEBUG
+    logSetVar (a, val, true);
+#endif /* DEBUG */
+}
+
+MNode*  MlEnv::getGlobalAry (const ustring& name, size_t i) {
+    ustring  a (name);
+    a.append (uUScore);
+    a.append (boost::lexical_cast<ustring> (i));
+    return globalVar.getVar (a);
+}
+
+size_t  MlEnv::getGlobalArySize (const ustring& name) {
+    MNode*  v;
+    ustring  a (name);
+    a.append (CharConst ("_n"));
+    v = globalVar.getVar (a);
+    if (v && v->isReal ()) {
+       return (size_t)v->real;
+    } else {
+       return 0;
+    }
+}
+
+MNode*  MlEnv::getLocalAry (MotorVar* pool, const ustring& name, size_t i) {
+    ustring  a (name);
+    a.append (uUScore);
+    a.append (boost::lexical_cast<ustring> (i));
+    return pool->getVar (a);
+}
+
+size_t  MlEnv::getLocalArySize (MotorVar* pool, const ustring& name) {
+    MNode*  v;
+    ustring  a (name);
+
+    a.append (CharConst ("_n"));
+    v = pool->getVar (a);
+    if (v && v->isReal ()) {
+       return (size_t)v->real;
+    } else {
+       return 0;
+    }
+}
+
+void  MlEnv::setVar (const ustring& name, MNode* val) {
+    MotorVar*  pool;
+
+    if (validName (name)) {
+       pool = findLocal (name);
+       if (pool) {
+           pool->setVar (name, val);
+#ifdef DEBUG
+           logSetVar (name, val, true);
+#endif /* DEBUG */
+       } else {
+           globalVar.setVar (name, val);
+#ifdef DEBUG
+           logSetVar (name, val);
+#endif /* DEBUG */
+       }
+    } else {
+       MNodePtr  p;
+       p = val;
+#ifdef DEBUG
+       logSetVarError (name, val);
+#endif /* DEBUG */
+    }
+}
+
+void  MlEnv::setVar2 (const ustring& name, MNode* val) {
+    if (checkAry (name)) {
+       setAry (ustring (name.begin () + 1, name.end ()), val);
+    } else {
+       setVar (name, val);
+    }
+}
+
+void  MlEnv::setAry (const ustring& name, size_t i, MNode* val) {
+    MotorVar*  pool;
+
+    if (validName (name) && i >= 0) {
+       pool = findLocal (name);
+       if (pool) {
+           setLocalAry (pool, name, i, val);
+       } else {
+           setGlobalAry (name, i, val);
+       }
+    } else {
+       MNodePtr  p;
+       p = val;
+#ifdef DEBUG
+       logSetAryError (name, i, val);
+#endif /* DEBUG */
+    }
+}
+
+void  MlEnv::setArySize (const ustring& name, size_t n) {
+    MotorVar*  pool;
+
+    if (validName (name)) {
+       pool = findLocal (name);
+       if (pool) {
+           setLocalArySize (pool, name, n);
+       } else {
+           setGlobalArySize (name, n);
+       }
+    } else {
+       throw (padEmpty (name) + uErrorBadName);
+#ifdef DEBUG
+//     logSetArySizeError (name, n);
+#endif /* DEBUG */
+    }
+}
+
+void  MlEnv::setAry (const ustring& name, MNode* list) {
+    MotorVar*  pool;
+    size_t  n = 0;
+
+    if (validName (name)) {
+       pool = findLocal (name);
+       if (pool) {
+           if (isNil (list)) {
+           } else if (list->isCons ()) {
+               while (list && list->isCons ()) {
+                   n ++;
+                   setLocalAry (pool, name, n, list->car ());
+                   nextNode (list);
+               }
+           } else {
+//             n ++;
+//             setLocalAry (pool, name, n, list);
+               throw (ustring (CharConst ("setting a scalar value to an array.")));
+           }
+           setLocalArySize (pool, name, n);
+       } else {
+           if (isNil (list)) {
+           } else if (list->isCons ()) {
+               while (list && list->isCons ()) {
+                   n ++;
+                   setGlobalAry (name, n, list->car ());
+                   nextNode (list);
+               }
+           } else {
+//             n ++;
+//             setGlobalAry (name, n, list);
+               throw (ustring (CharConst ("setting a scalar value to an array.")));
+           }
+           setGlobalArySize (name, n);
+       }
+    } else {
+       throw (padEmpty (name) + uErrorBadName);
+#ifdef DEBUG
+//     logSetVarError (name, NULL);
+#endif /* DEBUG */
+    }
+}
+
+MNode*  MlEnv::getVar (const ustring& name) {
+    MotorVar*  pool;
+
+    if (validName (name)) {
+       pool = findLocal (name);
+       if (pool) {
+           return (pool->getVar (name));
+       } else {
+           return (globalVar.getVar (name));
+       }
+    } else {
+       throw (padEmpty (name) + uErrorBadName);
+    }
+    return NULL;
+}
+
+ustring  MlEnv::getVar_string (const ustring& name) {
+    MNode*  v = getVar (name);
+    if (v)
+       return v->to_string ();
+    else
+       return uEmpty;
+}
+
+MNode*  MlEnv::getAry (const ustring& name, size_t i) {
+    MotorVar*  pool;
+
+    if (validName (name)) {
+       if (i >= 0) {
+           pool = findLocal (name);
+           if (pool) {
+               return getLocalAry (pool, name, i);
+           } else {
+               return getGlobalAry (name, i);
+           }
+       }
+    } else {
+       throw (padEmpty (name) + uErrorBadName);
+    }
+    return NULL;
+}
+
+ustring  MlEnv::getAry_string (const ustring& name, size_t i) {
+    MNode*  v = getAry (name, i);
+    if (v)
+       return v->to_string ();
+    else
+       return uEmpty;
+}
+
+size_t  MlEnv::getArySize (const ustring& name) {
+    MotorVar*  pool;
+
+    if (validName (name)) {
+       pool = findLocal (name);
+       if (pool) {
+           return getLocalArySize (pool, name);
+       } else {
+           return getGlobalArySize (name);
+       }
+    } else {
+       throw (padEmpty (name) + uErrorBadName);
+    }
+    return 0;
+}
+
+void  MlEnv::beginLocal () {
+    MotorVar*  v = new MotorVar;
+    localVar.push_back (v);
+}
+
+void  MlEnv::setLocalVar (const ustring& name, MNode* val) {
+    MotorVar*  pool = &localVar.back ();
+    if (validName (name)) {
+       pool->setVar (name, val);
+#ifdef DEBUG
+       logSetVar (name, val, true);
+#endif /* DEBUG */
+    } else {
+       MNodePtr  p;
+       p = val;
+#ifdef DEBUG
+       logSetVarError (name, val);
+#endif /* DEBUG */
+    }
+}
+
+void  MlEnv::defineLocalVar (const ustring& name) {
+    MotorVar*  pool = &localVar.back ();
+    if (validName (name)) {
+       pool->setVar (name, NULL);
+    }
+}
+
+void  MlEnv::endLocal () {
+    localVar.pop_back ();
+}
+
+MotorVar*  MlEnv::findLocal (const ustring& name) {
+    boost::ptr_vector<MotorVar>::reverse_iterator  t;
+    MotorVar::iterator  it;
+
+    for (t = localVar.rbegin (); t != localVar.rend (); t ++) {
+       it = t->find (name);
+       if (it != t->end ()) {
+           return &*t;
+       }
+    }
+    return NULL;
+}
+
+#ifdef DEBUG
+void  MlEnv::logSetVar (const ustring& name, MNode* val, bool flocal) {
+    if (! log)
+       return;
+    *log << "  [";
+    if (flocal)
+       *log << "*";
+    *log <<  name << " <= ";
+    if (val)
+       *log << val->dump_string_short ();
+    else
+       *log << uNil;
+    *log << "]\n";
+}
+
+void  MlEnv::logSetVarError (const ustring& name, MNode* val) {
+    if (! log)
+       return;
+    *log << "  error: [";
+    *log <<  name << " <= ";
+    if (val)
+       *log << val->dump_string_short ();
+    *log << "]\n";
+}
+
+void  MlEnv::logSetAryError (const ustring& name, size_t i, MNode* val) {
+    if (! log)
+       return;
+    *log << "  error: [" <<  name << uUScore << i << " <= ";
+    if (val)
+       *log << val->dump_string_short ();
+    *log << "]\n";
+}
+
+void  MlEnv::logSetArySizeError (const ustring& name, size_t n) {
+    if (! log)
+       return;
+    *log << "  [" <<  name << "_n" << " <= " << n << "]\n";
+}
+#endif
+
+void  MlEnv::push_linenum (MNode* c, int ln) {
+    linenum.insert (std::pair<MNode*, int> (c, ln));
+}
+
+void  MlEnv::logLinenum (MNode* c) {
+    boost::unordered_map<MNode*, int>::iterator  i;
+
+    i = linenum.find (c);
+    if (i == linenum.end ()) {
+       *log << "<none>: ";
+    } else {
+       *log << i->second << ": ";
+    }
+}
+
+void  MlEnv::logSexp (MNode* c) {
+    int  i;
+    if (log && c && c->isCons ()) {
+       for (i = 0; i < includeCount; i ++)
+           *log << ":";
+       logLinenum (c);
+       *log << c->dump_string_short () << "\n";
+    }
+}
+
diff --git a/lib/mlenv.h b/lib/mlenv.h
new file mode 100644 (file)
index 0000000..43f0835
--- /dev/null
@@ -0,0 +1,113 @@
+#ifndef MLENV_H
+#define MLENV_H
+
+#include "ftable.h"
+#include "motorvar.h"
+#include <boost/unordered_map.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <iostream>
+
+class  MNode;
+class  MotorEnv;
+
+class  MlEnv {
+ public:
+    boost::unordered_map<MNode*, int>  linenum;
+    MotorVar  globalVar;
+    boost::ptr_vector<MotorVar>  localVar;
+    MNodePtr  retval;
+    MNodePtr  currentCell;
+    FTable*  ftable;
+    MTable*  mtable;
+//    MotorVar*  detable;
+    enum {
+       M_NONE,
+       M_START,
+       M_EXEC,
+    }  moduleStatus;
+    FTable*  stable;
+    MLFunc*  module;
+    time_t  starttime;
+    time_t  limittime;
+    int  includeCount;
+//    int  nbreak;
+    MNodePtr  breaksym;
+    MNodePtr  breakval;
+    bool  noprog;
+    MotorEnv*  env;
+    std::ostream*  log;
+
+    MlEnv () {
+       ftable = NULL;
+       mtable = NULL;
+//     detable = NULL;
+       moduleStatus = M_NONE;
+       stable = NULL;
+       module = NULL;
+       includeCount = 0;
+//     nbreak = 0;
+       env = NULL;
+       log = NULL;
+    };
+    virtual  ~MlEnv () {};
+
+//    virtual void  setFTable (FTable* ft, MTable* mt, MotorVar* dt) {
+    virtual void  setFTable (FTable* ft, MTable* mt) {
+       ftable = ft;
+       mtable = mt;
+//     detable = dt;
+    };
+    virtual void  setStartTime ();
+    virtual bool  qtimeup ();
+    virtual void  resetProg () {
+       noprog = false;
+//     nbreak = 0;
+       breaksym = NULL;
+       breakval = NULL;
+    };
+    virtual void  breakProg () {
+       noprog = true;
+//     nbreak = 0;
+       breaksym = NULL;
+       breakval = NULL;
+    };
+    virtual void  inclIncCount ();
+    virtual void  declIncCount ();
+
+    virtual bool  validName (const ustring& name);
+    virtual void  setGlobalAry (const ustring& name, size_t i, MNode* val);
+    virtual void  setGlobalArySize (const ustring& name, size_t n);
+    virtual void  setLocalAry (MotorVar* pool, const ustring& name, size_t i, MNode* val);
+    virtual void  setLocalArySize (MotorVar* pool, const ustring& name, size_t n);
+    virtual MNode*  getGlobalAry (const ustring& name, size_t i);
+    virtual size_t  getGlobalArySize (const ustring& name);
+    virtual MNode*  getLocalAry (MotorVar* pool, const ustring& name, size_t i);
+    virtual size_t  getLocalArySize (MotorVar* pool, const ustring& name);
+    virtual void  setVar (const ustring& name, MNode* val);
+    virtual void  setVar2 (const ustring& name, MNode* val);
+    virtual void  setAry (const ustring& name, size_t i, MNode* val);
+    virtual void  setArySize (const ustring& name, size_t n);
+    virtual void  setAry (const ustring& name, MNode* list);
+    virtual MNode*  getVar (const ustring& name);
+    virtual ustring  getVar_string (const ustring& name);
+    virtual MNode*  getAry (const ustring& name, size_t i);
+    virtual ustring  getAry_string (const ustring& name, size_t i);
+    virtual size_t  getArySize (const ustring& name);
+    virtual void  beginLocal ();
+    virtual void  setLocalVar (const ustring& name, MNode* val);
+    virtual void  defineLocalVar (const ustring& name);
+    virtual void  endLocal ();
+    virtual MotorVar*  findLocal (const ustring& name);
+#ifdef DEBUG
+    virtual void  logSetVar (const ustring& name, MNode* val, bool flocal = false);
+    virtual void  logSetVarError (const ustring& name, MNode* val);
+    virtual void  logSetAryError (const ustring& name, size_t i, MNode* val);
+    virtual void  logSetArySizeError (const ustring& name, size_t n);
+#endif /* DEBUG */
+
+    virtual void  push_linenum (MNode* c, int ln);
+    virtual void  logLinenum (MNode* c);
+    virtual void  logSexp (MNode* c);
+};
+
+#endif /* MLENV_H */
diff --git a/lib/motor.cc b/lib/motor.cc
new file mode 100644 (file)
index 0000000..ca61a63
--- /dev/null
@@ -0,0 +1,660 @@
+#include "motor.h"
+#include "motoroutput.h"
+#include "motorfunc.h"
+#include "motorenv.h"
+#include "mlenv.h"
+#include "util_const.h"
+#include "util_string.h"
+#include "util_file.h"
+#include "ustring.h"
+#include "motorconst.h"
+#include <iostream>
+#include <assert.h>
+
+/*DOC:
+==HTMLMotor==
+
+*/
+
+using namespace  std;
+
+#define cSCOM  "(?:<!--)?"
+#define cECOM  "(?: *-->)?"
+
+// ============================================================
+#ifdef DEBUG2
+void  MotorObj::dumpIndent (std::ostream& o, int indent) {
+    for (int i = 0; i < indent; i ++) {
+       o << "\t";
+    }
+}
+
+#endif /* DEBUG */
+
+#ifdef DEBUG2
+void  MotorObj::dumpObjs (std::ostream& o, int indent, MotorObjVec* objs) {
+    for (int i = 0; i < objs->size (); i ++) {
+       if (i == 0)
+           (*objs)[i].dump (o, 0);
+       else
+           (*objs)[i].dump (o, indent);
+    }
+    if (objs->size () == 0)
+       o << "\n";
+}
+#endif /* DEBUG */
+
+// ============================================================
+void  MotorObjText::out (MotorOutput* o, MotorEnv* env) {
+    o->outTemplate (text);
+}
+
+#ifdef DEBUG2
+void  MotorObjText::dump (std::ostream& o, int indent) {
+    dumpIndent (o, indent);
+    o << "MotorObjText(\"" << text << "\")\n";
+}
+#endif /* DEBUG */
+
+// ============================================================
+void  MotorObjVar::out (MotorOutput* o, MotorEnv* env) {
+    ustring  val;
+
+    val = env->mlenv->getVar_string (variable);
+    switch (vopt) {
+    case opt_normal:
+       o->outHTML (val);
+       break;
+    case opt_nbsp:
+       if (val.size () > 0)
+           o->outHTML (val);
+       else
+           o->outPadding ();
+       break;
+    case opt_c3:
+       val = c3 (val);
+       o->outHTML (val);
+       break;
+    case opt_br:
+       o->outHTML_br (val);
+       break;
+    case opt_wbr:
+       o->outHTML_wbr (val);
+       break;
+    default:;
+       assert (0);
+    }
+}
+
+#ifdef DEBUG2
+void  MotorObjVar::dump (std::ostream& o, int indent) {
+    dumpIndent (o, indent);
+    o << "MotorObjVar(" << variable << ":" << vopt << ")\n";
+}
+#endif /* DEBUG */
+// ============================================================
+void  MotorObjTempl::out (MotorOutput* o, MotorEnv* env) {
+    motor->setTemplate (num, &objs);
+}
+
+#ifdef DEBUG2
+void  MotorObjTempl::dump (std::ostream& o, int indent) {
+    dumpIndent (o, indent);
+    o << "MotorObjTempl(" << num << ":\n";
+    dumpIndent (o, indent + 1);
+    dumpObjs (o, indent + 1, &objs);
+    dumpIndent (o, indent);
+    o << ")\n";
+}
+#endif /* DEBUG */
+// ============================================================
+void  MotorObjSelect::out (MotorOutput* o, MotorEnv* env) {
+    ustring  val;
+    MotorOutputString  m;
+
+    val = env->mlenv->getVar_string (variable);
+    motor->output (value, &m, env); // XXX: must apply rconv().
+    if (val == m.ans) {
+       if (fbang) {
+           motor->output (falseobjs, o, env);
+       } else {
+           motor->output (trueobjs, o, env);
+       }
+    } else {
+       if (fbang) {
+           motor->output (trueobjs, o, env);
+       } else {
+           motor->output (falseobjs, o, env);
+       }
+    }
+}
+
+#ifdef DEBUG2
+void  MotorObjSelect::dump (std::ostream& o, int indent) {
+    dumpIndent (o, indent);
+    o << "MotorObjSelect(";
+    o << variable;
+    if (fbang)
+       o << "!";
+//    o << ":" << value << "\n";
+    o << ":\n";
+    dumpIndent (o, indent + 2);
+    dumpObjs (o, indent + 2, &value);
+    dumpIndent (o, indent + 1);
+    o << "true:\t";
+    dumpObjs (o, indent + 2, &trueobjs);
+    dumpIndent (o, indent + 1);
+    o << "false:\t";
+    dumpObjs (o, indent + 2, &falseobjs);
+    dumpIndent (o, indent);
+    o << ")\n";
+}
+#endif /* DEBUG */
+// ============================================================
+void  MotorObjError::out (MotorOutput* o, MotorEnv* env) {
+    if (variable.empty ()) {
+//     if (env->errorvar.empty ())
+       if (env->errflag)
+           motor->output (trueobjs, o, env);
+       else
+           motor->output (falseobjs, o, env);
+    } else {
+       if (env->getErrorVar (variable))
+           motor->output (trueobjs, o, env);
+       else
+           motor->output (falseobjs, o, env);
+    }
+}
+
+#ifdef DEBUG2
+void  MotorObjError::dump (std::ostream& o, int indent) {
+    dumpIndent (o, indent);
+    o << "MotorObjError(" << variable << ":\n";
+    dumpIndent (o, indent + 1);
+    o << "true:\t";
+    dumpObjs (o, indent + 2, &trueobjs);
+    dumpIndent (o, indent + 1);
+    o << "false:\t";
+    dumpObjs (o, indent + 2, &falseobjs);
+    dumpIndent (o, indent);
+    o << ")\n";
+}
+#endif /* DEBUG */
+// ============================================================
+void  MotorObjFunc::out (MotorOutput* o, MotorEnv* env) {
+    execMotorFunc (name, args, env->mlenv);
+}
+
+#ifdef DEBUG2
+void  MotorObjFunc::dump (std::ostream& o, int indent) {
+    dumpIndent (o, indent);
+    o << "MotorObjFunc(" << name;
+    for (int i = 0; i < args.size (); i ++) {
+       o << ":" << args[i];
+    }
+    o << ")\n";
+}
+#endif /* DEBUG */
+// ============================================================
+#if 0
+void  MotorObjWiki::out (MotorOutput* o, MotorEnv* env) {
+}
+
+#ifdef DEBUG2
+void  MotorObjWiki::dump (std::ostream& o, int indent) {
+    dumpIndent (o, indent);
+    o << "MotorObjWiki()\n";
+}
+#endif /* DEBUG */
+#endif
+// ============================================================
+#if 0
+void  MotorObjML::out (MotorOutput* o, MotorEnv* env) {
+    /* *** */
+    assert (0);
+}
+
+#ifdef DEBUG2
+void  MotorObjML::dump (std::ostream& o, int indent) {
+    dumpIndent (o, indent);
+    o << "MotorObjML()\n";
+}
+#endif /* DEBUG */
+#endif
+// ============================================================
+/*DOC:
+===variable interpolation===
+|h:Tag|h:Note|
+|&[;[''VAR'']]|variable expansion|
+|&[;[''VAR''~]]|if empty, padding with &&;nbsp;|
+|&[;[''VAR'',]]|comma placement|
+//|&[;[''VAR''!]]|
+|&[;[''VAR''/]]|replacing NL with &<;br>|
+//|&[;[''VAR''/~]]|
+//|&[;[''VAR''//]]|
+|&[;[''VAR''^]]| &<;wbr> output|
+//|&[;[''VAR''%]]|
+|&[;[raw:''VAR'']]|no '&' encoding|
+|&[;[url:''VAR'']]|url encoding|
+|&[;[js:''VAR'']]|encoding for javascript string literal|
+|&[;[pad0:''NUM'':''VAR'']]|zero-padding|
+|&[;[text_nbsp:''VAR'']]| &<;br> and empty padding|
+|&[;[text_url:''VAR'']]|replacing NL with &<;br> and url link|
+|&[;[wiki:''VAR'']]|wiki output|
+
+===conditional component===
+|h:Tag|h:Note|
+|&[;[''VAR''?''VALUE'':''TEXT1'']]|output ''TEXT1'' if ''VAR'' is ''VALUE''|
+|&[;[''VAR''?''VALUE'':''TEXT1''&|;&|;''TEXT2'']]|output ''TEXT1'' if ''VAR'' is ''VALUE'', otherwise ''TEXT2''|
+|&[;[''VAR''!?''VALUE'':''TEXT1'']]|output ''TEXT1'' unless ''VAR'' is ''VALUE''|
+|&[;[''VAR''!?''VALUE'':''TEXT1''&|;&|;''TEXT2'']]|output ''TEXT1'' unless ''VAR'' is ''VALUE'', otherwise ''TEXT2''|
+|&[;[error:''VAR'':''TEXT1'']]|output ''TEXT1'' if error variable ''VAR'' is true|
+|&[;[error:''VAR'':''TEXT1''&|;&|;''TEXT2'']]|output ''TEXT1'' if error variable ''VAR'' is true, otherwise ''TEXT2''|
+|&[;[error::''TEXT1'']]|output ''TEXT1'' if error variable is set|
+|&[;[error::''TEXT1''&|;&|;''TEXT2'']]|output ''TEXT1'' if error variable is set, otherwise ''TEXT2''|
+
+===template fraction and function===
+|h:Tag|h:Note|
+|&[;[''NUM'':''TEXT1'']]|fraction definition|
+|&[;[eval:''NAME'']]|motor function execution|
+|&[;[eval:''NAME'':''PARAM1'':''PARAM2''...]]|motor function execution with parameters|
+
+*/
+void  HTMLMotor::compile (const ustring& text, bool skipHead) {
+    begin = text.begin ();
+    end = text.end ();
+    if (skipHead) {
+       umatch  m;
+       while (begin != end && *begin != '<') {
+           if (usearch (begin, end, m, re_nl)) {
+               begin = m[0].second;
+           } else {
+               begin = end;
+           }
+       }
+    }
+    s1 (&objs, term_none, NULL);
+}
+
+void  HTMLMotor::compile_file (const ustring& path, bool skipHead) {
+    ustring  b;
+
+    if (readFile (path, b)) {
+       compile (b, skipHead);
+    }
+}
+
+void  HTMLMotor::output (MotorObj::MotorObjVec& v, MotorOutput* o, MotorEnv* env) {
+#if 0
+    MotorObj::MotorObjVec::iterator  it;
+
+    for (it = v.begin (); it != v.end (); it ++) {
+       it->out (o, env);
+    }
+#endif
+    int  i, n;
+
+    n = v.size ();
+    for (i = 0; i < n; i ++) {
+       v[i].out (o, env);
+    }
+}
+
+void  HTMLMotor::output (MotorOutput* o, MotorEnv* env) {
+    output (objs, o, env);
+}
+
+void  HTMLMotor::outputTemp (const ustring& text, MotorOutput* o, MotorEnv* env) {
+    int  i, n;
+
+    n = objs.size ();
+#ifdef DEBUG2
+    std::cerr << "objs.size ():" << objs.size () << "\n";
+#endif /* DEBUG */
+    compile (text);
+    for (i = n; i < objs.size (); i ++) {
+       objs[i].out (o, env);
+    }
+    while (objs.size () > n) {
+#ifdef DEBUG2
+       std::cerr << "objs.size ():" << objs.size () << "\n";
+#endif /* DEBUG */
+       objs.pop_back ();
+    }
+}
+
+void  HTMLMotor::setTemplate (const ustring& name, MotorObj::MotorObjVec* value) {
+    templates.erase (name);
+    templates.insert (boost::unordered_map<ustring, MotorObj::MotorObjVec*>::value_type (name, value));
+}
+
+MotorObj::MotorObjVec*  HTMLMotor::getTemplate (const ustring& name) {
+    boost::unordered_map<ustring, MotorObj::MotorObjVec*>::iterator  it = templates.find (name);
+    if (it == templates.end ()) {
+       return NULL;
+    } else {
+       return it->second;
+    }
+}
+
+#ifdef DEBUG2
+void  HTMLMotor::dump (std::ostream& o) {
+    int  i;
+
+    for (i = 0; i < objs.size (); i ++) {
+       objs[i].dump (o, 0);
+    }
+}
+#endif /* DEBUG */
+
+void  HTMLMotor::s1 (MotorObj::MotorObjVec* sobjs, term_type tt, end_type* type) {
+    umatch  m;
+    uiterator  start = begin;
+    static uregex  re1 ("(" cSCOM "\\[\\[)|&(.);|(" cSCOM "\\|\\|" cECOM ")|(" cSCOM "\\]\\]" cECOM ")|(:)");
+
+#ifdef DEBUG2
+    cerr << "reg\n";
+#endif /* DEBUG */
+    while (start != end) {
+       if (usearch (start, end, m, re1)) {
+#ifdef DEBUG2
+           cerr << "start:" << ustring (start, start + 8) << "...  match:" << ustring (m[0].first, m[0].second) << "\n";
+#endif /* DEBUG */
+           if (m[1].matched) {                 // [[
+               shiftPrec (sobjs, &m);
+               if (s2 (sobjs, m[0].second)) {
+                   start = begin;
+               } else {
+                   start ++;
+               }
+           } else if (m[2].matched) {          // &.;
+               shiftPrec (sobjs, &m);
+               sobjs->push_back (new MotorObjText (this, m[2].first, m[2].second));
+               start = begin = m[0].second;
+           } else if (m[3].matched) {          // ||
+               if (tt == term_else_bra) {
+                   if (begin != m[0].first) {
+                       sobjs->push_back (new MotorObjText (this, begin, m[0].first));
+                   }
+                   begin = m[0].second;
+                   if (type)
+                       *type = end_else;
+                   return;
+               } else {
+                   start = m[0].second;
+               }
+           } else if (m[4].matched) {          // ]]
+               if (tt == term_else_bra || tt == term_bra) {
+                   if (begin != m[0].first) {
+                       sobjs->push_back (new MotorObjText (this, begin, m[0].first));
+                   }
+                   begin = m[0].second;
+                   if (type)
+                       *type = end_bra;
+                   return;
+               } else {
+                   start = m[0].second;
+               }
+           } else if (m[5].matched) {          // :
+               if (tt == term_colon) {
+                   if (begin != m[0].first) {
+                       sobjs->push_back (new MotorObjText (this, begin, m[0].first));
+                   }
+                   begin = m[0].second;
+                   if (type)
+                       *type = end_colon;
+                   return;
+               } else {
+                   start = m[0].second;
+               }
+           } else {
+               assert (0);     // Program error
+           }
+       } else {                // !match
+#ifdef DEBUG2
+           cerr << "regend\n";
+#endif /* DEBUG */
+           break;
+       }
+    }
+
+    if (type)
+       *type = end_none;
+
+    if (tt == term_none) {
+       if (begin != end) {
+           sobjs->push_back (new MotorObjText (this, begin, end));
+           begin = end;
+       }
+    }
+}
+
+int  HTMLMotor::s2 (MotorObj::MotorObjVec* sobjs, uiterator start) { // [[
+    umatch  m;
+    static uregex
+       re2 ("^"
+            "(" rMOTORVAR ")"          /* 1 */
+            "("        "((/|~|,|\\^)?\\]\\]" cECOM ")" /* 2, 3, 4 */
+            "|"        "((!)?\\?)"     /* 5, 6 */
+            "|"        "(:" cECOM ")"          /* 7 */
+            ")"
+            );
+
+#ifdef DEBUG2
+    cerr << "s2()  start:" << ustring (start, start + 8) << "...\n";
+#endif /* DEBUG */
+    if (usearch (start, end, m, re2)) {
+       if (m[3].matched) {                     // ]]
+           if (m[4].matched) {                 // option
+               ustring  s = m[4];
+               upair  p = m[4];
+               if (match (p, UCharConst ("/"))) {
+                   sobjs->push_back (new MotorObjVar (this, m[1], MotorObjVar::opt_br));
+               } else if (match (p, UCharConst ("~"))) {
+                   sobjs->push_back (new MotorObjVar (this, m[1], MotorObjVar::opt_nbsp));
+               } else if (match (p, UCharConst (","))) {
+                   sobjs->push_back (new MotorObjVar (this, m[1], MotorObjVar::opt_c3));
+//             } else if (match (p, UCharConst ("!"))) {
+//                 sobjs->push_back (new MotorObjVar (this, m[1], MotorObjVar::opt_raw));
+//             } else if (match (p, UCharConst ("~/")) || match (p, UCharConst ("/~"))) {
+//                 sobjs->push_back (new MotorObjVar (this, m[1], MotorObjVar::opt_br_nbsp));
+//             } else if (match (p, UCharConst ("//"))) {
+//                 sobjs->push_back (new MotorObjVar (this, m[1], MotorObjVar::opt_br_a));
+//             } else if (match (p, UCharConst ("//~")) || match (p, UCharConst ("~//"))) {
+//                 sobjs->push_back (new MotorObjVar (this, m[1], MotorObjVar::opt_br_a_nbsp));
+//             } else if (match (p, UCharConst ("+"))) {
+//                 sobjs->push_back (new MotorObjVar (this, m[1], MotorObjVar::opt_wiki));
+               } else if (match (p, UCharConst ("^"))) {
+                   sobjs->push_back (new MotorObjVar (this, m[1], MotorObjVar::opt_wbr));
+//             } else if (match (p, UCharConst ("%"))) {
+//                 sobjs->push_back (new MotorObjVar (this, m[1], MotorObjVar::opt_url));
+               } else {
+                   assert (0);
+               }
+           } else {            // no option
+               sobjs->push_back (new MotorObjVar (this, m[1], MotorObjVar::opt_normal));
+           }
+           begin = m[0].second;
+       } else if (m[5].matched) {              // ?
+           if (! s3 (sobjs, m[5].second, m[6].matched, m[1])) {
+               return 0;               // NG
+           }
+       } else if (m[7].matched) {              // :
+           if (! s4 (sobjs, m[7].second, m[1])) {
+               return 0;               // NG
+           }
+       } else {
+           assert (0);                 // Program error
+       }
+       return 1;               // OK
+    } else {                   // invalid command
+       return 0;               // NG
+    }
+}
+
+int  HTMLMotor::s3 (MotorObj::MotorObjVec* sobjs, uiterator start, bool fbang, upair var) {// [[VAR?
+    MotorObjSelect*  o;
+    end_type  type;
+    uiterator  ob = begin;
+
+#ifdef DEBUG2
+    cerr << "s3 ()\n";
+#endif /* DEBUG */
+    begin = start;
+    o = new MotorObjSelect (this, var, fbang);
+    s1 (&o->value, term_colon, &type);
+    if (type == end_colon) {
+       if (s6 (&o->trueobjs, &o->falseobjs)) {
+           sobjs->push_back (o);
+       } else {
+           delete o;
+           begin = ob;
+           return 0;                   // NG
+       }
+    } else {
+       delete o;
+       begin = ob;
+       return 0;                       // NG
+    }
+    return 1;
+}
+
+int  HTMLMotor::s4 (MotorObj::MotorObjVec* sobjs, uiterator start, upair name) { // [[NAME:
+    // start points after the colon.
+    umatch  m;
+    static uregex  re_num ("^[0-9]+$");
+
+#ifdef DEBUG2
+    cerr << "s4 ()  start:" << ustring (start, start + 8) << "...  name:" << ustring (name.first, name.second) << "\n";
+#endif /* DEBUG */
+    if (match (name, UCharConst ("error"))) { // [[error:
+       if (! s5 (sobjs, start)) {
+           return 0;                   // NG
+       }
+    } else if (usearch (name.first, name.second, m, re_num)) {// [[NUM:
+       MotorObjTempl*  o = new MotorObjTempl (this, name);
+       uiterator  b = begin;
+
+       begin = start;
+       if (! s7 (&o->objs)) {
+           delete o;
+           begin = b;
+           return 0;                   // NG
+       }
+       sobjs->push_back (o);
+    } else {                           // [[FUNC:
+       if (! s8 (sobjs, name, start)) {
+           return 0;
+       }
+    }
+    return 1;
+}
+
+int  HTMLMotor::s5 (MotorObj::MotorObjVec* sobjs, uiterator start) { // [[error:
+    umatch  m;
+    end_type  type;
+    static uregex  re_var ("^(" rMOTORVAR ")?:");
+
+#ifdef DEBUG2
+    cerr << "s5 ()\n";
+#endif /* DEBUG */
+    if (usearch (start, end, m, re_var)) {     // VAR:
+       MotorObjError*  o;
+
+       o = new MotorObjError (this, m[1]);
+       begin = m[0].second;
+       if (s6 (&o->trueobjs, &o->falseobjs)) {
+           sobjs->push_back (o);
+       } else {
+           delete o;
+           return 0;                   // NG
+       }
+    } else {
+       return 0;                       // NG
+    }
+    return 1;
+}
+
+int  HTMLMotor::s6 (MotorObj::MotorObjVec* trueobjs, MotorObj::MotorObjVec* falseobjs) {
+    end_type  type;
+
+#ifdef DEBUG2
+    cerr << "s6 ()\n";
+#endif /* DEBUG */
+    s1 (trueobjs, term_else_bra, &type);
+    switch (type) {
+    case end_none:                     // no matching ]].
+       return 0;                       // NG
+       break;
+    case end_else:                     // ||
+       if (! s7 (falseobjs)) {
+           return 0;                   // NG
+       }
+       break;
+    case end_bra:                      // closing.
+       break;
+    default:
+       assert (0);                     // Program error
+    }
+    return 1;
+}
+
+int  HTMLMotor::s7 (MotorObj::MotorObjVec* sobjs) {
+    end_type  type;
+
+#ifdef DEBUG2
+    cerr << "s7 ()\n";
+#endif /* DEBUG */
+    s1 (sobjs, term_bra, &type);
+    if (type == end_bra)
+       return 1;                       // OK
+    else
+       return 0;                       // NG
+}
+
+int  HTMLMotor::s8 (MotorObj::MotorObjVec* sobjs, upair name, uiterator start) { // [[FUNC:
+    MotorObjFunc*  o = new MotorObjFunc (this, name);
+    ustring  s;
+    static uregex  re_colon_bra ("(:)|(\\]\\]" cECOM ")|&(.);");
+    Splitter  sp (start, end, re_colon_bra);
+
+#ifdef DEBUG2
+    std::cerr << "s8  start:" << *start << "\n";
+#endif /* DEBUG */
+    while (sp.next ()) {
+       s.append (sp.begin (), sp.end ());
+       if (sp.match (1)) {             // :
+           o->args.push_back (s);
+           s.resize (0);
+       } else if (sp.match (2)) {      // ]]
+           o->args.push_back (s);
+           s.resize (0);
+           begin = sp.matchEnd ();
+           goto Ex1;
+       } else if (sp.match (3)) {      // &x;
+           s.append (sp.matchBegin (3), sp.matchEnd (3));
+       } else {
+           assert (0);
+       }
+    }
+    // unexpected end
+#ifdef DEBUG2
+    std::cerr << "unexpected end\n";
+#endif /* DEBUG */
+    delete o;
+    return 0;                          // NG
+    
+ Ex1:;
+    sobjs->push_back (o);
+
+    return 1;
+}
+
+void  HTMLMotor::shiftPrec (MotorObj::MotorObjVec* sobjs, umatch* m) {
+    if (begin != (*m)[0].first) {
+       sobjs->push_back (new MotorObjText (this, begin, (*m)[0].first));
+       begin = (*m)[0].first;
+    }
+}
+
+// ============================================================
diff --git a/lib/motor.h b/lib/motor.h
new file mode 100644 (file)
index 0000000..5d2a529
--- /dev/null
@@ -0,0 +1,203 @@
+#ifndef MOTOR_H
+#define MOTOR_H
+
+#include "motoroutput.h"
+#include "motorenv.h"
+#include "ustring.h"
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <boost/unordered_map.hpp>
+#include <vector>
+
+class  HTMLMotor;
+class  MotorObj {
+ public:
+    typedef  boost::ptr_vector<MotorObj>  MotorObjVec;
+
+    enum objtype {
+       motor_text,
+       motor_var,
+       motor_templ,
+       motor_select,
+       motor_error,
+       motor_ext,
+       motor_wiki,
+       motor_ml,
+    }  type;
+    HTMLMotor*  motor;
+
+    MotorObj (objtype t, HTMLMotor* owner): type (t), motor (owner) {};
+    virtual  ~MotorObj () {};
+    virtual void  out (MotorOutput* o, MotorEnv* env) = 0;
+#ifdef DEBUG2
+    virtual void  dump (std::ostream& o, int indent) = 0;
+    virtual void  dumpIndent (std::ostream& o, int indent);
+    virtual void  dumpObjs (std::ostream& o, int indent, MotorObjVec* objs);
+#endif /* DEBUG */
+};
+
+class  MotorObjText: public MotorObj {
+ public:
+    ustring  text;
+
+    MotorObjText (HTMLMotor* owner, uiterator begin, uiterator end): MotorObj (motor_text, owner) {
+       text.assign (begin, end);
+    };
+    virtual  ~MotorObjText () {};
+    virtual void  out (MotorOutput* o, MotorEnv* env);
+#ifdef DEBUG2
+    virtual void  dump (std::ostream& o, int indent);
+#endif /* DEBUG */
+};
+
+class  MotorObjVar: public MotorObj {
+ public:
+    ustring  variable;
+    enum  opt {
+       opt_normal,
+       opt_nbsp,
+       opt_c3,
+//     opt_raw,
+       opt_br,
+//     opt_br_nbsp,
+//     opt_br_a,
+//     opt_br_a_nbsp,
+//     opt_wiki,
+       opt_wbr,
+//     opt_url,
+    }  vopt;
+
+    MotorObjVar (HTMLMotor* owner, upair var, opt p2): MotorObj (motor_var, owner) {
+       variable.assign (var.first, var.second);
+       vopt = p2;
+    };
+    virtual  ~MotorObjVar () {};
+    virtual void  out (MotorOutput* o, MotorEnv* env);
+#ifdef DEBUG2
+    virtual void  dump (std::ostream& o, int indent);
+#endif /* DEBUG */
+};
+
+class  MotorObjTempl: public MotorObj {
+ public:
+    ustring  num;
+    MotorObjVec  objs;
+
+    MotorObjTempl (HTMLMotor* owner, upair n): MotorObj (motor_templ, owner) {
+       num.assign (n.first, n.second);
+    };
+    virtual  ~MotorObjTempl () {};
+    virtual void  out (MotorOutput* o, MotorEnv* env);
+#ifdef DEBUG2
+    virtual void  dump (std::ostream& o, int indent);
+#endif /* DEBUG */
+};
+
+class  MotorObjSelect: public MotorObj {
+ public:
+    ustring  variable;
+    MotorObjVec  value;
+    bool  fbang;
+    MotorObjVec  trueobjs;
+    MotorObjVec  falseobjs;
+
+    MotorObjSelect (HTMLMotor* owner, upair var, bool fb): MotorObj (motor_select, owner) {
+       variable.assign (var.first, var.second);
+       fbang = fb;
+    };
+    virtual  ~MotorObjSelect () {};
+    virtual void  out (MotorOutput* o, MotorEnv* env);
+#ifdef DEBUG2
+    virtual void  dump (std::ostream& o, int indent);
+#endif /* DEBUG */
+};
+
+class  MotorObjError: public MotorObj {
+ public:
+    ustring  variable;
+    MotorObjVec  trueobjs;
+    MotorObjVec  falseobjs;
+
+    MotorObjError (HTMLMotor* owner, upair var): MotorObj (motor_error, owner) {
+       variable.assign (var.first, var.second);
+    };
+    virtual  ~MotorObjError () {};
+    virtual void  out (MotorOutput* o, MotorEnv* env);
+#ifdef DEBUG2
+    virtual void  dump (std::ostream& o, int indent);
+#endif /* DEBUG */
+};
+
+class  MotorObjFunc: public MotorObj {
+ public:
+    ustring  name;
+    std::vector<ustring>  args;
+
+    MotorObjFunc (HTMLMotor* owner, upair n): MotorObj (motor_ext, owner) {
+       name.assign (n.first, n.second);
+    };
+    virtual  ~MotorObjFunc () {};
+    virtual void  out (MotorOutput* o, MotorEnv* env);
+#ifdef DEBUG2
+    virtual void  dump (std::ostream& o, int indent);
+#endif /* DEBUG */
+};
+
+#if 0
+class  MotorObjWiki: public MotorObj {
+ public:
+    MotorObjWiki (HTMLMotor* owner): MotorObj (motor_wiki, owner) {};
+    virtual  ~MotorObjWiki () {};
+    virtual void  out (MotorOutput* o, MotorEnv* env);
+#ifdef DEBUG2
+    virtual void  dump (std::ostream& o, int indent);
+#endif /* DEBUG */
+};
+#endif
+
+#if 0
+class  MotorObjML: public MotorObj {
+ public:
+    MotorObjML (HTMLMotor* owner): MotorObj (motor_ml, owner) {};
+    virtual  ~MotorObjML () {};
+    virtual void  out (MotorOutput* o, MotorEnv* env);
+#ifdef DEBUG2
+    virtual void  dump (std::ostream& o, int indent);
+#endif /* DEBUG */
+};
+#endif
+
+class  HTMLMotor {
+ public:
+    MotorObj::MotorObjVec  objs;
+    uiterator  begin;
+    uiterator  end;
+    boost::unordered_map<ustring, MotorObj::MotorObjVec*>  templates;
+
+    HTMLMotor () {};
+    virtual  ~HTMLMotor () {};
+    virtual void  compile (const ustring& text, bool skipHead = false);
+    virtual void  compile_file (const ustring& path, bool skipHead = false);
+    virtual void  output (MotorObj::MotorObjVec& v, MotorOutput* o, MotorEnv* env);
+    virtual void  output (MotorOutput* o, MotorEnv* env);
+    virtual void  outputTemp (const ustring& text, MotorOutput* o, MotorEnv* env);
+    virtual void  setTemplate (const ustring& name, MotorObj::MotorObjVec* value);
+    virtual MotorObj::MotorObjVec*  getTemplate (const ustring& name);
+#ifdef DEBUG2
+    virtual void  dump (std::ostream& o);
+#endif /* DEBUG */
+    
+ private:
+    typedef enum {term_none, term_else_bra, term_bra, term_colon}  term_type;
+    typedef enum {end_none, end_else, end_bra, end_colon}  end_type;
+    virtual void  s1 (MotorObj::MotorObjVec* sobjs, term_type tt, end_type* type);
+    virtual int  s2 (MotorObj::MotorObjVec* sobjs, uiterator start);
+    virtual int  s3 (MotorObj::MotorObjVec* sobjs, uiterator start, bool fbang, upair var);
+    virtual int  s4 (MotorObj::MotorObjVec* sobjs, uiterator start, upair name);
+    virtual int  s5 (MotorObj::MotorObjVec* sobjs, uiterator start);
+    virtual int  s6 (MotorObj::MotorObjVec* trueobjs, MotorObj::MotorObjVec* falseobjs);
+    virtual int  s7 (MotorObj::MotorObjVec* sobjs);
+    virtual int  s8 (MotorObj::MotorObjVec* sobjs, upair name, uiterator start);
+    virtual void  shiftPrec (MotorObj::MotorObjVec* sobjs, umatch* m);
+};
+
+#endif /* MOTOR_H */
diff --git a/lib/motorconst.h b/lib/motorconst.h
new file mode 100644 (file)
index 0000000..9041f7a
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef MOTORCONST_H
+#define MOTORCONST_H
+
+#define  rMOTORVAR     "[a-zA-Z_0-9][a-zA-Z_0-9-]*"
+#define  rWIKIVAR      "[a-zA-Z_][a-zA-Z_0-9-]*"
+#define  rWikiID       "[a-zA-Z_][a-zA-Z_0-9-]*"
+#define  kWORD         "[a-zA-Z0-9_]"
+#define  kWNAME                "[a-zA-Z_][a-zA-Z0-9_-]"
+#define  rHTML         "https?://[a-zA-Z\\-]+(\\.[a-zA-Z\\-]+)*(:[0-9]+)?(/[!#%&+,\\-.0-9:<=>?@A-Z_a-z~]*)*"
+#define  kDS           "/"
+#define  kSubDB                "/db/"
+#define  kSubStore     "/store/"
+#define  kSubStorage   "/save/"
+#define  kSubTemp      "/tmp/"
+#define  kSubAuth      "/auth/"
+#define  kBin          "/bin/"
+#define  kEtc          "/etc/"
+#define  kStoreSerial  "serial"
+#define  kEXT_SQLITE3  ".sqlite3"
+#define  kEXT_HASH     ".db"
+#define  kEXT_BTREE    ".btree"
+#define  kEXT_LOCK     ".lock"
+#define  kMimeTypeDB   "/etc/mimetype.db"
+#define  kARRAYMAX     10000
+#define  kIncludeMax   8
+#define  kSearchName   "HMLSEARCHPATH"
+
+#endif /* MOTORCONST_H */
diff --git a/lib/motorenv.cc b/lib/motorenv.cc
new file mode 100644 (file)
index 0000000..e371c81
--- /dev/null
@@ -0,0 +1,476 @@
+#include "config.h"
+#include "motorconst.h"
+#include "motor.h"
+#include "motorenv.h"
+#include "wikienv.h"
+#include "mlenv.h"
+#include "formfile.h"
+#include "ml-defun.h"
+#include "expr.h"
+#include "utf8.h"
+#include "util_check.h"
+#include "util_const.h"
+#include "util_file.h"
+#include "util_string.h"
+#include "filemacro.h"
+#include "ustring.h"
+#include <boost/lexical_cast.hpp>
+#include <stdlib.h>
+#include <unistd.h>
+
+//MotorVar  GDeTable;
+
+MotorEnv::MotorEnv (AppEnv* ae, CGIFormFile* fm, HTMLMotor* m, MotorOutput* out) {
+    appenv = ae;
+    form = fm;
+    motor = m;
+    output = out;
+    log = &std::cerr;
+    mlenv = new MlEnv;
+    mlenv->env = this;
+    mlenv->log = log;
+    mlenv->setStartTime ();
+    mlenv->resetProg ();
+//    mlenv->setFTable (&GFTable, &GMTable, &GDeTable);
+    mlenv->setFTable (&GFTable, &GMTable);
+    wikienv = new WikiEnv;
+    errflag = false;
+    responseDone = false;
+}
+
+MotorEnv::~MotorEnv () {
+    delete mlenv;
+    delete wikienv;
+}
+
+void  MotorEnv::setErrorFlag () {
+    mlenv->breakProg ();
+    errflag = true;
+}
+
+void  MotorEnv::setErrorVar (const ustring& name) {
+    errflag = true;
+    if (mlenv->validName (name)) {
+       errorvar.setVar (name);
+#ifdef DEBUG
+       if (log) {
+           *log << "   [!" << name << "]\n";
+       }
+#endif /* DEBUG */
+    }
+}
+
+bool  MotorEnv::getErrorVar (const ustring& name) {
+    if (mlenv->validName (name))
+       return errorvar.getVar (name);
+}
+
+void  MotorEnv::motorItem (const ustring& name) {
+    MotorObj::MotorObjVec*  itemobj;
+
+    if (motor) {
+       itemobj = motor->getTemplate (name);
+       if (itemobj)
+           motor->output (*itemobj, output, this);
+    }
+}
+
+void  MotorEnv::motorText (const ustring& text) {
+    motor->outputTemp (text, output, this);
+}
+
+bool  MotorEnv::path_resource (const ustring& name, ustring& ans) {
+    ustring  r;
+
+#ifdef cDocTop
+    if (checkAbsoluteResourceName (name)) {
+       r.assign (CharConst (cDocTop)).append (name);
+//     if (fileExists (r)) {
+       if (isPlainFile (r)) {
+           ans = r;
+           return true;
+       } else {
+           return false;
+       }
+    }
+#endif
+    if (! checkResourceName (name))
+       return false;
+//    if (fileExists (name)) {
+    if (isPlainFile (name)) {
+       ans = name;
+       return true;
+    }
+    r = getenvString (kSearchName);
+    if (r.length () > 0) {
+       static uregex  re ("[^\x21-\x7e]|//|\\.\\.|/$");
+       umatch  m;
+       if (! usearch (r, m, re)) {
+           ans.assign (getenvString (kDOCUMENT_ROOT)).append (r).append (CharConst ("/")).append (name);
+           return true;
+       }
+    }
+    return false;
+}
+
+ustring  MotorEnv::path_to_auth (const ustring& name) {
+    ustring  ans;
+
+//    if (! checkName (name))
+//     throw (name + uErrorBadName);
+    ans.reserve (128);
+    ans.append (CharConst (cDataTop kSubAuth));
+    ans.append (name);
+
+    return ans;
+}
+
+ustring  MotorEnv::path_db (const ustring& name, const char* suffix) {
+    ustring  ans;
+    static uregex  re ("^[a-zA-Z][a-zA-Z0-9_\\-]*$");
+    umatch  m;
+
+    assert (suffix);
+    ans.reserve (128);
+    if (datastore.length () > 0) {
+       if (usearch (name, m, re)) {
+           ans.append (CharConst (cDataTop kDS));
+           ans.append (datastore);
+           ans.append (CharConst (kSubDB));
+           ans.append (name);
+           ans.append (suffix);
+#ifdef DEBUG2
+           std::cerr << "datastore:" << ans << "\n";
+#endif /* DEBUG */
+       } else {
+           throw (padEmpty (name) + ustring (CharConst (": bad datafile name.")));
+       }
+    } else {
+       throw (uErrorMissingDatastore);
+    }
+    return ans;
+}
+
+ustring  MotorEnv::path_store_file (const ustring& name, const char* suffix) {
+    if (storedir.empty ())
+       throw (uErrorNoStore);
+    if (suffix)
+       return storedir + filenameEncode (name) + suffix;
+    else
+       return storedir + filenameEncode (name);
+}
+
+ustring  MotorEnv::path_storage_file (const ustring& name, const char* suffix) {
+    if (storagedir.empty ())
+       throw (uErrorNoStorage);
+    if (suffix)
+       return storagedir + filenameEncode (name) + suffix;
+    else
+       return storagedir + filenameEncode (name);
+}
+
+ustring  MotorEnv::path_to_posttemp () {
+    ustring  ans;
+
+    ans.reserve (64);
+    ans.append (CharConst (cDataTop kSubTemp "post-"));
+    ans.append (boost::lexical_cast<ustring> (getpid ()));
+    return ans;
+}
+
+ustring  MotorEnv::path_to_store () {
+    if (datastore.length () == 0)
+       throw (uErrorMissingDatastore);
+    return ustring (CharConst (cDataTop kDS)) + datastore + ustring (CharConst (kSubStore));
+}
+
+ustring  MotorEnv::path_to_store_index () {
+    if (datastore.length () == 0)
+       throw (uErrorMissingDatastore);
+    return ustring (CharConst (cDataTop kDS)) + datastore + ustring (CharConst (kSubStore kStoreSerial));
+}
+
+ustring  MotorEnv::path_to_storage () {
+    if (datastore.length () == 0)
+       throw (uErrorMissingDatastore);
+    return ustring (CharConst (cDataTop kDS)) + datastore + ustring (CharConst (kSubStorage));
+}
+
+ustring  MotorEnv::path_to_storage_index () {
+    if (datastore.length () == 0)
+       throw (uErrorMissingDatastore);
+    return ustring (CharConst (cDataTop kDS)) + datastore + ustring (CharConst (kSubStorage kStoreSerial));
+}
+
+void  MotorEnv::setDefault () {
+    datastore = appenv->datastore;
+    switch (form->method) {
+    case CGIForm::M_GET:
+       htmlFile = appenv->getHtml;
+       break;
+    case CGIForm::M_POST:
+       htmlFile = appenv->postHtml;
+       break;
+    case CGIForm::M_NONE:
+       htmlFile = appenv->getHtml;
+//     *log << "REQUEST_METHOD not defined.\n";
+       break;
+    }
+    errorHtmlFile = appenv->errorHtml;
+    mimetype = appenv->mimetype;
+}
+
+void  MotorEnv::setDefaultDatastore () {
+    datastore = appenv->datastore;
+}
+
+void  MotorEnv::setDatastore (const ustring& name) {
+    if (name.size () == 0) {
+       datastore = appenv->datastore;
+    } else {
+       if (! checkName (name))
+           throw (name + uErrorBadDatastore);
+       datastore = name;
+    }
+}
+
+void  MotorEnv::readFormVar () {
+    switch (form->method) {
+    case CGIForm::M_GET:
+       form->read_get ();
+       break;
+    case CGIForm::M_POST:
+       if (appenv->postfile) {
+           if (form->isMultipart ())
+               form->read_multipart (this);
+           else
+               ::close (0);
+       } else {
+           if (form->isMultipart ())
+               ::close (0);
+           else
+               form->read_post (this);
+       }
+       break;
+    }
+//    form->dump (cerr);
+}
+
+void  MotorEnv::cacheControl () {
+    if (form->method == CGIForm::M_GET) {
+       switch (appenv->cacheControl) {
+       case AppEnv::CC_COOKIE:
+           http.setRandomCookie ();
+           break;
+       case AppEnv::CC_URL:
+           throw (uErrorNotImpl);
+           break;
+       case AppEnv::CC_NOCACHE:
+           http.fNoCache = true;
+           break;
+       }
+    }
+}
+
+void  MotorEnv::doML () {
+    switch (form->method) {
+    case CGIForm::M_GET:
+       doML (appenv->getML);
+       break;
+    case CGIForm::M_POST:
+       doML (appenv->postML);
+       break;
+    default:
+       *log << "REQUEST_METHOD not defined.\n";
+       doML (appenv->getML);
+    }
+}
+
+void  MotorEnv::doML (const ustring& file) {
+    ustring  b;
+    ustring  f;
+    bool  rc;
+    bool  dash;
+
+    if (file.size () == 0)
+       return;
+
+    if (file == uDash) {
+       f = appenv->scriptName ();
+       if (f.size () == 0)
+           return;
+       rc = readFile (f, b);
+       dash = true;
+    } else {
+       if (! path_resource (file, f))
+           return;
+       rc = readFile (f, b);
+       dash = false;
+    }
+    if (rc) {
+       MotorSexp  ml (mlenv);
+       ml.scan (b, dash);
+#ifdef DEBUG2
+       ml.top.dump (std::cerr, false);
+       std::cerr << "\n";
+       std::cerr.flush ();
+#endif /* DEBUG */
+       if (ml.top.isCons ()) {
+           try {
+               progn_ex (&ml.top, mlenv);
+           }
+           catch (ustring& msg) {
+               if (log) {
+                   if (mlenv->currentCell ()) {
+                       mlenv->logLinenum (mlenv->currentCell ());
+                       *log << mlenv->currentCell ()->dump_string_short () << ": ";
+                   }
+                   *log << logText (msg) << "\n";
+               }
+               mlenv->currentCell = NULL;
+           }
+       }
+    } else {
+       *log << file << uErrorCantOpen << "\n";
+    }
+}
+
+void  MotorEnv::doMotor () {
+    if (responseDone)
+       return;
+
+    if (errflag) {
+       if (errorHtmlFile.size () > 0)
+           doMotor (errorHtmlFile);
+       else if (htmlFile.size () > 0)
+           doMotor (htmlFile);
+    } else if (htmlFile.size () > 0) {
+       doMotor (htmlFile);
+    }
+    if (!responseDone) {
+       noContentResponse ();
+    }
+}
+
+void  MotorEnv::doMotor (const ustring& file, const ustring& type) {
+    ustring  f;
+
+    if (file == uDash) {
+       f = appenv->scriptName ();
+       if (f.size () == 0)
+           return;
+       standardResponse (type);
+       motor->compile_file (f, true);
+    } else if (path_resource (file, f)) {
+       standardResponse (type);
+       if (f.size () > 0) {
+           motor->compile_file (f);
+#ifdef DEBUG2
+           motor->dump (std::cerr);
+           std::cerr.flush ();
+#endif /* DEBUG */
+       } else {
+           return;
+       }
+    } else {
+       return;
+    }
+
+    try {
+       motor->output (output, this);
+    }
+    catch (ustring& msg) {
+       if (log) {
+           if (mlenv->currentCell ()) {
+               mlenv->logLinenum (mlenv->currentCell ());
+               *log << mlenv->currentCell ()->dump_string_short () << ": ";
+           }
+           *log << logText (msg) << "\n";
+       }
+       mlenv->currentCell = NULL;
+    }
+}
+
+void  MotorEnv::outputFile (const ustring& src, const ustring& type) {
+    FileMacro  f;
+    char*  b;
+    ssize_t  s;
+
+    if (responseDone)
+       return;
+
+    if (f.openRead (src.c_str ())) {
+       standardResponse (type);
+       b = new char[65536];
+       while ((s = f.read (b, 65536)) > 0) {
+           std::cout.write (b, s);
+       }
+       delete b;
+    } else {
+       noContentResponse ();
+    }
+}
+
+void  MotorEnv::outputFile (const ustring& src, const ustring& type, bool finline, const ustring& dispname) {
+    FileMacro  f;
+    char*  b;
+    ssize_t  s;
+
+    if (responseDone)
+       return;
+
+    if (f.openRead (src.c_str ())) {
+       standardResponse (type, finline, dispname);
+       b = new char[65536];
+       while ((s = f.read (b, 65536)) > 0) {
+           std::cout.write (b, s);
+       }
+       delete b;
+    } else {
+       noContentResponse ();
+    }
+}
+
+void  MotorEnv::standardResponse (const ustring& type) {
+    http.standardResponse (std::cout, type, this);
+    responseDone = true;
+}
+
+void  MotorEnv::standardResponse (const ustring& type, const ustring& encoding) {
+    http.standardResponse (std::cout, type, encoding, this);
+    responseDone = true;
+}
+
+void  MotorEnv::standardResponse (const ustring& type, bool finline, const ustring& dispname) {
+    http.disposition (std::cout, finline, dispname);
+    http.standardResponse (std::cout, type, this);
+    responseDone = true;
+}
+
+void  MotorEnv::standardResponse_html () {
+    http.standardResponse_html (std::cout, this);
+    responseDone = true;
+}
+
+void  MotorEnv::noContentResponse () {
+    http.noContentResponse (std::cout, this);
+    responseDone = true;
+}
+
+void  MotorEnv::forbiddenResponse () {
+    http.forbiddenResponse (std::cout, this);
+    responseDone = true;
+}
+
+void  MotorEnv::location (const ustring& url) {
+    http.location (std::cout, url, this);
+    mlenv->breakProg ();
+    responseDone = true;
+}
+
+void  MotorEnv::location_html (const ustring& url) {
+    http.location_html (std::cout, url, this);
+    mlenv->breakProg ();
+    responseDone = true;
+}
+
diff --git a/lib/motorenv.h b/lib/motorenv.h
new file mode 100644 (file)
index 0000000..d521814
--- /dev/null
@@ -0,0 +1,98 @@
+#ifndef MOTORENV_H
+#define MOTORENV_H
+
+#include "app.h"
+#include "motorvar.h"
+#include "motorfunc.h"
+#include "ml-defun.h"
+#include "http.h"
+#include "ustring.h"
+#include "util_time.h"
+#include <iostream>
+
+class  FTable;
+class  CGIFormFile;
+class  HTMLMotor;
+class  MotorOutput;
+class  MLFunc;
+class  MNode;
+class  MlEnv;
+class  WikiEnv;
+class  MotorEnv {
+ public:
+    AppEnv*  appenv;
+    CGIFormFile*  form;
+    HTMLMotor*  motor;
+    MotorOutput*  output;
+    HTTPResponse  http;
+
+    ustring  datastore;
+    ustring  htmlFile;
+    ustring  errorHtmlFile;
+    ustring  mimetype;
+    MlEnv*  mlenv;
+    WikiEnv*  wikienv;
+    MotorErrorVar  errorvar;
+    ustring  storedir;
+    ustring  storagedir;
+    std::wstring  regtext;
+    boost::wsmatch  regmatch;
+    MotorVar  motorCall;
+    bool  errflag;
+    bool  responseDone;
+    std::ostream*  log;
+
+    MotorEnv (AppEnv* ae, CGIFormFile* fm, HTMLMotor* m, MotorOutput* out);
+    virtual  ~MotorEnv ();
+
+    virtual void  setErrorFlag ();
+    virtual void  setErrorVar (const ustring& name);
+    virtual bool  getErrorVar (const ustring& name);
+    virtual void  motorItem (const ustring& name);
+    virtual void  motorText (const ustring& text);
+
+    virtual bool  path_resource (const ustring& name, ustring& ans);
+    virtual ustring  path_to_auth (const ustring& name);
+    virtual ustring  path_db (const ustring& name, const char* suffix);
+    virtual ustring  path_store_file (const ustring& name, const char* suffix);
+    virtual ustring  path_store_file (const ustring& name) {
+       return path_store_file (name, NULL);
+    };
+    virtual ustring  path_storage_file (const ustring& name, const char* suffix);
+    virtual ustring  path_storage_file (const ustring& name) {
+       return path_storage_file (name, NULL);
+    };
+    virtual ustring  path_to_posttemp ();
+    virtual ustring  path_to_store ();
+    virtual ustring  path_to_store_index ();
+    virtual ustring  path_to_storage ();
+    virtual ustring  path_to_storage_index ();
+
+    virtual void  setDefault ();
+    virtual void  setDefaultDatastore ();
+    virtual void  setDatastore (const ustring& name);
+    virtual void  readFormVar ();
+    virtual void  cacheControl ();
+    virtual void  doML ();
+    virtual void  doML (const ustring& file);
+    virtual void  doMotor ();
+    virtual void  doMotor (const ustring& file) {
+       doMotor (file, mimetype);
+    };
+    virtual void  doMotor (const ustring& file, const ustring& type);
+    virtual void  outputFile (const ustring& src, const ustring& type);
+    virtual void  outputFile (const ustring& src, const ustring& type, bool finline, const ustring& dispname);
+    virtual void  standardResponse () {
+       standardResponse (mimetype);
+    };
+    virtual void  standardResponse (const ustring& type);
+    virtual void  standardResponse (const ustring& type, const ustring& encoding);
+    virtual void  standardResponse (const ustring& type, bool finline, const ustring& dispname);
+    virtual void  standardResponse_html ();
+    virtual void  noContentResponse ();
+    virtual void  forbiddenResponse ();
+    virtual void  location (const ustring& url);
+    virtual void  location_html (const ustring& url);
+};
+
+#endif /* MOTORENV_H */
diff --git a/lib/motorfunc.cc b/lib/motorfunc.cc
new file mode 100644 (file)
index 0000000..c59064a
--- /dev/null
@@ -0,0 +1,39 @@
+#include "motorfunc.h"
+#include "motorenv.h"
+#include "mlenv.h"
+#include "ml-defun.h"
+#include "mftable.h"
+#include "expr.h"
+#include "utf8.h"
+#include "ustring.h"
+
+
+void  execMotorFunc (const ustring& name, std::vector<ustring>& args, MlEnv* mlenv) {
+    MFTable::iterator  it;
+
+    mlenv->inclIncCount ();
+    if ((it = IMFTable.find (name)) != IMFTable.end ()) {
+       try {
+           it->second->fn (args, mlenv);
+       } catch (ustring& msg) {
+           ustring amsg;
+           amsg.append (CharConst ("[[")).append (name);
+           for (int i = 0; i < args.size (); i ++) {
+               amsg.append (uColon).append (args[i]);
+           }
+           amsg.append (CharConst ("]]: ")).append (msg);
+           amsg = ellipsis (logText (amsg), cLOGSHORTSIZE);
+           throw (amsg);
+       }
+    } else {
+//#ifdef DEBUG2
+//     std::cerr << name << ": not found.\n";
+//#endif /* DEBUG */
+       MNodePtr  vargs;
+       MNodePtr  ans;
+
+       vargs = buildArgs (0, args);
+       ans = execDefun (mlenv, &mlenv->env->motorCall, name, vargs ());
+    }
+    mlenv->declIncCount ();
+}
diff --git a/lib/motorfunc.h b/lib/motorfunc.h
new file mode 100644 (file)
index 0000000..083fa01
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef MOTORFUNC_H
+#define MOTORFUNC_H
+
+#include "ustring.h"
+#include <vector>
+
+class  MlEnv;
+
+void  execMotorFunc (const ustring& name, std::vector<ustring>& args, MlEnv* mlenv);
+
+#endif /* MOTORFUNC_H */
diff --git a/lib/motoroutput-jp.cc b/lib/motoroutput-jp.cc
new file mode 100644 (file)
index 0000000..0038c3a
--- /dev/null
@@ -0,0 +1,61 @@
+#include "motoroutput-jp.h"
+#include "utf8-jp.h"
+#include <iconv.h>
+#include <iostream>
+
+using namespace  std;
+
+/* ============================================================ */
+MotorOutput*  MotorOutputOStreamJPMS::out (const ustring::value_type* s, size_t len) {
+    ustring  a (s, len);
+    cout << fixToMS (a);
+    return this;
+}
+
+MotorOutput*  MotorOutputOStreamJPMS::out (uiterator s, uiterator e) {
+    ustring  a (s, e);
+    cout << fixToMS (a);
+    return this;
+}
+
+MotorOutput*  MotorOutputOStreamJPMS::out (const ustring& text) {
+    cout << fixToMS (text);
+    return this;
+}
+
+/* ============================================================ */
+MotorOutput*  MotorOutputISO2022JP::out (const ustring::value_type* s, size_t len) {
+    char*  buf = new char[4096];
+    const char*  ibuf;
+    char*  obuf;
+    size_t  isize, osize, rsize;
+
+    ibuf = s;
+    isize = len;
+    while (isize > 0) {
+       obuf = buf;
+       osize = 4096;
+       rsize = iconv (cd, &ibuf, &isize, &obuf, &osize);
+//     if (rsize < 0)
+       if (obuf - buf <= 0)
+           break;
+//     cout.write (buf, obuf - buf);
+       basic_out (buf, obuf - buf);
+    }
+    delete buf;
+    return this;
+}
+
+/* ============================================================ */
+MotorOutput*  MotorOutputISO2022JPOStream::basic_out (const ustring::value_type* s, size_t len) {
+    cout.write (s, len);
+    return this;
+}
+
+/* ============================================================ */
+MotorOutput*  MotorOutputISO2022JPString::basic_out (const ustring::value_type* s, size_t len) {
+    ans.append (ustring (s, len));
+    return this;
+}
+
+/* ============================================================ */
diff --git a/lib/motoroutput-jp.h b/lib/motoroutput-jp.h
new file mode 100644 (file)
index 0000000..844de3b
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef MOTOROUTPUT_JP_H
+#define MOTOROUTPUT_JP_H
+
+#include "motoroutput.h"
+#include "utf8-jp.h"
+#include <iconv.h>
+
+class  MotorOutputOStreamJPMS: public MotorOutput {
+ public:
+    MotorOutputOStreamJPMS () {};
+    virtual  ~MotorOutputOStreamJPMS () {};    
+
+    virtual MotorOutput*  out (const ustring::value_type* s, size_t len);
+    virtual MotorOutput*  out (uiterator s, uiterator e);
+    virtual MotorOutput*  out (const ustring& text);
+};
+
+class  MotorOutputISO2022JP: public MotorOutput {
+ public:
+    iconv_t  cd;
+
+    MotorOutputISO2022JP () {
+       cd = iconv_open ("ISO-2022-JP", "UTF-8");
+    };
+    virtual  ~MotorOutputISO2022JP () {
+       iconv_close (cd);
+    };
+
+    virtual MotorOutput*  out (const ustring::value_type* s, size_t len);
+    virtual MotorOutput*  basic_out (const ustring::value_type* s, size_t len) = 0;
+    virtual const char*  encoding () {
+       return "ISO-2022-JP";
+    };
+};
+
+class  MotorOutputISO2022JPOStream: public MotorOutputISO2022JP {
+ public:
+    MotorOutputISO2022JPOStream () {};
+    virtual  ~MotorOutputISO2022JPOStream () {};
+
+    virtual MotorOutput*  basic_out (const ustring::value_type* s, size_t len);
+};
+
+class  MotorOutputISO2022JPString: public MotorOutputISO2022JP {
+ public:
+    ustring  ans;
+
+    MotorOutputISO2022JPString () {};
+    virtual  ~MotorOutputISO2022JPString () {};
+
+    virtual MotorOutput*  basic_out (const ustring::value_type* s, size_t len);
+};
+
+#endif /* MOTOROUTPUT_JP_H */
diff --git a/lib/motoroutput.cc b/lib/motoroutput.cc
new file mode 100644 (file)
index 0000000..85f758a
--- /dev/null
@@ -0,0 +1,104 @@
+#include "motoroutput.h"
+#include "util_string.h"
+#include "ustring.h"
+#include "utf8.h"
+#include "motorconst.h"
+#include <iostream>
+
+using namespace  std;
+
+/* ============================================================ */
+static uregex  re_encode ("(<)|(>)|(&)|(\")|(')");
+static uregex  re_encode_br ("(<)|(>)|(&)|(\")|(')|(\n)");
+static uregex  re_encode_br_a ("(<)|(>)|(&)|(\")|(')|(\n)|(" rHTML ")");
+
+MotorOutput*  MotorOutput::outamp (ustring::const_iterator b, ustring::const_iterator e, uregex* re) {
+    umatch  m;
+
+    while (b != e && usearch (b, e, m, *re)) {
+       if (b != m[0].first) {
+           ustring  t (b, m[0].first);
+           outText (t);
+       }
+       b = m[0].second;
+
+       if (m[1].matched) {             // <
+           out (CharConst ("&lt;"));
+       } else if (m[2].matched) {      // >
+           out (CharConst ("&gt;"));
+       } else if (m[3].matched) {      // &
+           out (CharConst ("&amp;"));
+       } else if (m[4].matched) {      // "
+           out (CharConst ("&quot;"));
+       } else if (m[5].matched) {      // '
+           out (CharConst ("&#39;"));
+       } else if (m[6].matched) {      // \n
+           out (CharConst ("<br />\n"));
+       } else if (m[7].matched) {      // http...
+           out (CharConst ("<a href=\""));
+           outamp (m[7].first, m[7].second, &re_encode);
+           out (CharConst ("\">"));
+           outamp (m[7].first, m[7].second, &re_encode);
+           out (CharConst ("</a>"));
+       } else {
+           assert (0);
+       }
+    }
+    if (b != e) {
+       ustring  t (b, e);
+       outText (t);
+    }
+    return this;
+}
+
+MotorOutput*  MotorOutput::outText (const ustring& str) {
+    // text code conversion.
+    return out (ocode (str));
+}
+
+MotorOutput*  MotorOutput::outTemplate (const ustring& str) {
+    return out (fixUTF8 (str));
+}
+
+MotorOutput*  MotorOutput::outHTML (const ustring& str) {
+    ustring  t = fixUTF8 (str);
+    return outamp (t.begin (), t.end (), &re_encode);
+}
+
+MotorOutput*  MotorOutput::outHTML_br (const ustring& str) {
+    ustring  t = fixUTF8 (str);
+    return outamp (t.begin (), t.end (), &re_encode_br);
+}
+
+MotorOutput*  MotorOutput::outHTML_br_a (const ustring& str) {
+    ustring  t = fixUTF8 (str);
+    return outamp (t.begin (), t.end (), &re_encode_br_a);
+}
+
+MotorOutput*  MotorOutput::outHTML_wbr (const ustring& str) {
+    ustring  t = fixUTF8 (str);
+    /* *** */
+    assert (0);
+}
+
+MotorOutput*  MotorOutput::outHTMLnoCtrl (const ustring& str) {
+    return outHTML (omitCtrl (str));
+}
+
+MotorOutput*  MotorOutput::outPadding () {
+    return out (CharConst ("&nbsp;"));
+}
+
+/* ============================================================ */
+MotorOutput*  MotorOutputOStream::out (const ustring::value_type* s, size_t len) {
+    cout.write (s, len);
+    return this;
+}
+
+/* ============================================================ */
+MotorOutput*  MotorOutputString::out (const ustring::value_type* s, size_t len) {
+    ans.append (s, len);
+    return this;
+}
+
+/* ============================================================ */
diff --git a/lib/motoroutput.h b/lib/motoroutput.h
new file mode 100644 (file)
index 0000000..103cde7
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef MOTOROUTPUT_H
+#define MOTOROUTPUT_H
+
+#include "ustring.h"
+class  MotorOutput {
+ public:
+    MotorOutput () {};
+    virtual  ~MotorOutput () {};
+
+ private:
+    virtual MotorOutput*  outamp (uiterator b, uiterator e, uregex* re);
+
+ public:    
+    virtual void  init () {};
+    virtual MotorOutput*  out (const ustring::value_type* s, size_t len) = 0; /* raw output. */
+    virtual MotorOutput*  out (uiterator s, uiterator e) {
+       return out (s.base (), e - s);
+    };
+    virtual MotorOutput*  out (const ustring& text) {
+       return out (text.begin (), text.end ());
+    };
+    virtual ustring  ocode (const ustring& str) {
+       return str;
+    };
+    virtual ustring  rcode (const ustring& str) {
+       return str;
+    };
+    virtual MotorOutput*  outText (const ustring& str);        /* output with a text code convertion. */
+    virtual MotorOutput*  outTemplate (const ustring& str);    /* fix the charactor set. */
+    virtual MotorOutput*  outHTML (const ustring& str);        /* amp encode */
+    virtual MotorOutput*  outHTML_br (const ustring& str);
+    virtual MotorOutput*  outHTML_br_a (const ustring& str);
+    virtual MotorOutput*  outHTML_wbr (const ustring& str);
+    virtual MotorOutput*  outHTMLnoCtrl (const ustring& str);
+    virtual MotorOutput*  outPadding ();
+    virtual const char*  encoding () {
+       return "UTF-8";
+    };
+};
+
+class  MotorOutputOStream: public MotorOutput {
+ public:
+    MotorOutputOStream () {};
+    virtual  ~MotorOutputOStream () {};
+
+    virtual MotorOutput*  out (const ustring::value_type* s, size_t len);
+};
+
+class  MotorOutputString: public MotorOutput {
+ public:
+    ustring  ans;              /* XXX: customize the allocator. */
+    
+    MotorOutputString () {};
+    virtual  ~MotorOutputString () {};
+    virtual void  init () {
+       ans.resize (0);
+    };
+    virtual MotorOutput*  out (const ustring::value_type* s, size_t len);
+    virtual MotorOutput*  out (ustring::const_iterator s, ustring::const_iterator e) {
+       return out (s.base (), e - s);
+    };
+    virtual MotorOutput*  out (const ustring& text) {
+       return out (text.begin (), text.end ());
+    };
+};
+
+#endif /* MOTOROUTPUT_H */
diff --git a/lib/motorvar.cc b/lib/motorvar.cc
new file mode 100644 (file)
index 0000000..66dc0a1
--- /dev/null
@@ -0,0 +1,57 @@
+#include "motorvar.h"
+#include "ustring.h"
+#include <boost/unordered_map.hpp>
+#include <iostream>
+
+using namespace  std;
+
+void  MotorSet::set (const ustring& name) {
+    insert (name);
+}
+
+void  MotorSet::unset (const ustring& name) {
+    MotorSet::iterator  it = find (name);
+    if (it != end ())
+       erase (it);
+}
+
+bool  MotorSet::get (const ustring& name) {
+    MotorSet::iterator  it = find (name);
+    return (it != end ());
+}
+
+void  MotorVar::setVar (const ustring& name, MNode* val) {
+    std::pair<MotorVar::iterator, bool>  x;
+    erase (name);
+    x = insert (MotorVar::value_type (name, MNodePtr ()));
+    x.first->second = val;
+}
+
+MNode*  MotorVar::getVar (const ustring& name) {
+    MotorVar::iterator  it = find (name);
+    if (it == end ()) {
+       return NULL;
+    } else {
+       return it->second ();
+    }
+}
+
+bool  MotorVar::defined (const ustring& name) {
+    MotorVar::iterator  it = find (name);
+    return (it != end ());
+}
+
+void  MotorErrorVar::setVar (const ustring& name) {
+    erase (name);
+    insert (boost::unordered_map<ustring, bool>::value_type (name, true));
+}
+
+bool  MotorErrorVar::getVar (const ustring& name) {
+    boost::unordered_map<ustring, bool>::iterator  it = find (name);
+    if (it == end ()) {
+       return false;
+    } else {
+       return true;
+    }
+}
+
diff --git a/lib/motorvar.h b/lib/motorvar.h
new file mode 100644 (file)
index 0000000..e6bf567
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef MOTORVAR_H
+#define MOTORVAR_H
+
+#include "ml.h"
+#include "ustring.h"
+#include <boost/unordered_set.hpp>
+#include <boost/unordered_map.hpp>
+
+class  MotorSet: public boost::unordered_set<ustring> {
+ public:
+    MotorSet () {};
+    virtual  ~MotorSet () {};
+    virtual void  set (const ustring& name);
+    virtual void  unset (const ustring& name);
+    virtual bool  get (const ustring& name);
+};
+
+class  MotorVar: public boost::unordered_map<ustring, MNodePtr> {
+ public:
+    MotorVar () {};
+    virtual  ~MotorVar () {};
+    virtual void  setVar (const ustring& name, MNode* val);
+    virtual MNode*  getVar (const ustring& name);
+    virtual void  eraseVar (const ustring& name) {
+       erase (name);
+    };
+    virtual bool  defined (const ustring& name);
+};
+
+class  MotorErrorVar: public boost::unordered_map<ustring, bool> { /* XXX: use set. */
+ public:
+    MotorErrorVar () {};
+    virtual  ~MotorErrorVar () {};
+    virtual void  setVar (const ustring& name);
+    virtual bool  getVar (const ustring& name);
+};
+
+#endif /* MOTORVAR_H */
diff --git a/lib/sigsafe.cc b/lib/sigsafe.cc
new file mode 100644 (file)
index 0000000..b0047ea
--- /dev/null
@@ -0,0 +1,4 @@
+#include "sigsafe.h"
+
+int  SigSafeFlag = 0;
+
diff --git a/lib/sigsafe.h b/lib/sigsafe.h
new file mode 100644 (file)
index 0000000..ab30877
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef SIGSAFE_H
+#define SIGSAFE_H
+
+#include <signal.h>
+#include <assert.h>
+#include <stdlib.h>
+
+extern int  SigSafeFlag;
+class  SigSafe {
+ public:
+    void  (*sighup)(int);
+    void  (*sigint)(int);
+    void  (*sigquit)(int);
+    void  (*sigpipe)(int);
+    void  (*sigterm)(int);
+
+    SigSafe () {
+       sighup = signal (SIGHUP, handler);
+       sigint = signal (SIGINT, handler);
+       sigquit = signal (SIGQUIT, handler);
+       sigpipe = signal (SIGPIPE, handler);
+       sigterm = signal (SIGTERM, handler);
+    };
+    ~SigSafe () {
+       signal (SIGHUP, sighup);
+       signal (SIGINT, sigint);
+       signal (SIGQUIT, sigquit);
+       signal (SIGPIPE, sigpipe);
+       signal (SIGTERM, sigterm);
+
+       if (SigSafeFlag) {
+           void  (*fn)(int);
+           int  s = SigSafeFlag;
+
+           switch (s) {
+           case SIGHUP:
+               fn = sighup;
+               break;
+           case SIGINT:
+               fn = sigint;
+               break;
+           case SIGQUIT:
+               fn = sigquit;
+               break;
+           case SIGPIPE:
+               fn = sigpipe;
+               break;
+           case SIGTERM:
+               fn = sigterm;
+               break;
+           default:
+               assert (0);
+           }
+           if (fn == SIG_DFL) {
+               exit (1);
+           } else {
+               if (fn != handler)
+                   fn (s);
+           }
+       }
+    };
+
+    static void  handler (int sig) {
+       SigSafeFlag = sig;
+    };
+};
+
+#endif /* SIGSAFE_H */
diff --git a/lib/spair.h b/lib/spair.h
new file mode 100644 (file)
index 0000000..f232c13
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef SPAIR_H
+#define SPAIR_H
+
+#include "ustring.h"
+#include <string>
+#include <iterator>
+#include <utility>
+#include <string.h>
+
+#define  CharConst(s)          (s), (sizeof (s) - 1)
+
+typedef std::pair<std::string::const_iterator, std::string::const_iterator>  spair;
+
+inline int  match (spair& p, char* s, int len) {
+    int  n = p.second - p.first;
+    return (n == len && memcmp (p.first.base (), s, n) == 0);
+}
+
+#endif /* SPAIR_H */
diff --git a/lib/ustring.h b/lib/ustring.h
new file mode 100644 (file)
index 0000000..ffabdb6
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef USTRING_H
+#define USTRING_H
+
+#include <string>
+#include <utility>
+#include <boost/regex.hpp>
+#include <boost/lexical_cast.hpp>
+
+inline char*  char_type (u_char* v) {return (char*)v;}
+inline char*  char_type (char* v) {return v;}
+inline const char*  char_type (const u_char* v) {return (const char*)v;}
+//inline u_char*  uchar_type (char* v) {return (u_char*)v;}
+inline const u_char*  uchar_type (const char* v) {return (const u_char*)v;}
+inline char*  noconst_char (const char* v) {return (char*)v;}
+#define UCharConst(s)          (uchar_type(s)), (sizeof (s) - 1)
+#define CharConst(s)           (s), (sizeof (s) - 1)
+#define comapreHead(s)         compare(0, sizeof(s) - 1, (s), sizeof(s) - 1)
+
+typedef std::basic_string<char>                ustring;
+typedef ustring::const_iterator                uiterator;
+typedef std::pair<ustring::const_iterator, ustring::const_iterator>  upair;
+typedef boost::match_results<ustring::const_iterator>  umatch;
+typedef boost::basic_regex<char, boost::regex_traits<char> >  uregex;
+
+inline bool  usearch (ustring::const_iterator first, ustring::const_iterator last, umatch& m, const uregex& re, boost::match_flag_type flags = boost::regex_constants::match_single_line) {
+    return regex_search (first, last, m, re, flags);
+}
+inline bool  usearch (const ustring& s, umatch& m, const uregex& re, boost::match_flag_type flags = boost::regex_constants::match_single_line) {
+    return regex_search (s.begin (), s.end (), m, re, flags);
+}
+
+class  ustringPtr {
+ public:
+    ustring*  p;
+    ustringPtr (): p (NULL) {};
+    virtual  ~ustringPtr () {
+       delete p;
+       p = NULL;
+    };
+    ustring*  operator = (ustring* b) {
+       delete p;
+       p = b;
+    };
+    ustring*  release () {
+       ustring*  ans = p;
+       p = NULL;
+       return ans;
+    };
+};
+
+inline int  match (upair& p, const u_char* s, ustring::size_type len) {
+    ustring::size_type  n = p.second - p.first;
+    return (n == len && memcmp (p.first.base (), s, n) == 0);
+}
+
+#endif /* USTRING_H */
diff --git a/lib/utf16.cc b/lib/utf16.cc
new file mode 100644 (file)
index 0000000..7918c90
--- /dev/null
@@ -0,0 +1,122 @@
+#include "utf16.h"
+#include "ustring.h"
+
+ustring  utf8to16 (const ustring& src) {
+    ustring  ans;
+    int  i;
+    u_char  c, d, e;
+    u_int  x;
+
+    ans.reserve (src.size ());
+    for (i = 0; i < src.size ();) {
+       c = src[i ++];
+       if (c < 0x80) {
+           ans.append (1, 0);
+           ans.append (1, c);
+       } else if (0xc2 <= c && c <= 0xdf) {
+           d = src[i ++];
+           if (0x80 <= d && d <= 0xbf) {
+               x = ((c & 0x1f) << 6) + (d & 0x3f);
+               ans.append (1, x >> 8);
+               ans.append (1, x);
+           }
+       } else if (c == 0xe0) {
+           d = src[i ++];
+           e = src[i ++];
+           if (0xa0 <= d && d <= 0xbf && 0x80 <= e && e <= 0xbf) {
+               x = ((c & 0x1f) << 12) + ((d & 0x3f) << 6) + (e & 0x3f);
+               ans.append (1, x >> 8);
+               ans.append (1, x);
+           }
+       } else if (0xe1 <= c && c <= 0xef) {
+           d = src[i ++];
+           e = src[i ++];
+           if (0x80 <= d && d <= 0xbf && 0x80 <= e && e <= 0xbf) {
+               x = ((c & 0x1f) << 12) + ((d & 0x3f) << 6) + (e & 0x3f);
+               ans.append (1, x >> 8);
+               ans.append (1, x);
+           }
+       } else if (0xf0 <= c && c <= 0xf4) {
+           // skip surrogate pair
+           i ++;
+           i ++;
+           i ++;
+       } else {
+           // ignore illegal byte
+       }
+    }
+    return ans;
+}
+
+std::wstring  utow (const ustring& src) {
+    std::wstring  ans;
+    int  i;
+    u_char  c, d, e, f;
+    u_int  x;
+
+    ans.reserve (src.size ());
+    for (i = 0; i < src.size ();) {
+       c = src[i ++];
+       if (c < 0x80) {
+           ans.append (1, c);
+       } else if (0xc2 <= c && c <= 0xdf) {
+           d = src[i ++];
+           if (0x80 <= d && d <= 0xbf) {
+               x = ((c & 0x1f) << 6) + (d & 0x3f);
+               ans.append (1, x);
+           }
+       } else if (c == 0xe0) {
+           d = src[i ++];
+           e = src[i ++];
+           if (0xa0 <= d && d <= 0xbf && 0x80 <= e && e <= 0xbf) {
+               x = ((c & 0x1f) << 12) + ((d & 0x3f) << 6) + (e & 0x3f);
+               ans.append (1, x);
+           }
+       } else if (0xe1 <= c && c <= 0xef) {
+           d = src[i ++];
+           e = src[i ++];
+           if (0x80 <= d && d <= 0xbf && 0x80 <= e && e <= 0xbf) {
+               x = ((c & 0x1f) << 12) + ((d & 0x3f) << 6) + (e & 0x3f);
+               ans.append (1, x);
+           }
+       } else if (0xf0 <= c && c <= 0xf4) {
+           d = src[i ++];
+           e = src[i ++];
+           f = src[i ++];
+           if (0x80 <= d && d <= 0xbf && 0x80 <= e && e <= 0xbf && 0x80 <= f && f <= 0xbf) {
+               x = ((c & 0x07) << 18) + ((d & 0x3f) << 12) + ((e & 0x3f) << 6) + (f & 0x3f);
+               ans.append (1, x);
+           }
+       } else {
+           // ignore
+       }
+    }
+    return ans;
+}
+
+ustring  wtou (const std::wstring& src) {
+    ustring  ans;
+    u_int  x;
+    int  i;
+
+    ans.reserve (src.size () * 3);
+    for (i = 0; i < src.size (); i ++) {
+       x = src[i];
+       if (x < 0x0080) {
+           ans.append (1, x);
+       } else if (x < 0x0800) {
+           ans.append (1, 0xc0 | ((x >> 6) & 0x3f));
+           ans.append (1, 0x80 | (x & 0x3f));
+       } else if (x < 0x10000) {
+           ans.append (1, 0xe0 | ((x >> 12) & 0x0f));
+           ans.append (1, 0x80 | ((x >> 6) & 0x3f));
+           ans.append (1, 0x80 | (x & 0x3f));
+       } else {
+           ans.append (1, 0xf0 | ((x >> 18) & 0x07));
+           ans.append (1, 0x80 | ((x >> 12) & 0x3f));
+           ans.append (1, 0x80 | ((x >> 6) & 0x3f));
+           ans.append (1, 0x80 | (x & 0x3f));
+       }
+    }
+    return ans;
+}
diff --git a/lib/utf16.h b/lib/utf16.h
new file mode 100644 (file)
index 0000000..f3f392e
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef UTF16_H
+#define UTF16_H
+
+#include "ustring.h"
+
+ustring  utf8to16 (const ustring& src);
+std::wstring  utow (const ustring& src);
+ustring  wtou (const std::wstring& src);
+
+#endif /* UTF16_H */
diff --git a/lib/utf8-jp.cc b/lib/utf8-jp.cc
new file mode 100644 (file)
index 0000000..18026b1
--- /dev/null
@@ -0,0 +1,63 @@
+#include "utf8-jp.h"
+#include "ustring.h"
+
+typedef struct {
+    const char*  c;
+    size_t  s;
+}  CS;
+
+static uregex  re_js ("(\xe2\x80\x94)|(\xe3\x80\x9c)|(\xe2\x80\x96)|(\xe2\x88\x92)|(\xc2\xa2)|(\xc2\xa3)|(\xc2\xac)");
+static CS  t_ms[] = {
+       CharConst ("\xe2\x80\x95"),
+       CharConst ("\xef\xbd\x9e"),
+       CharConst ("\xe2\x88\xa5"),
+       CharConst ("\xef\xbc\x8d"),
+       CharConst ("\xef\xbf\xa0"),
+       CharConst ("\xef\xbf\xa1"),
+       CharConst ("\xef\xbf\xa2"),
+};
+static uregex  re_ms ("(\xe2\x80\x95)|(\xef\xbd\x9e)|(\xe2\x88\xa5)|(\xef\xbc\x8d)|(\xef\xbf\xa0)|(\xef\xbf\xa1)|(\xef\xbf\xa2)");
+static CS  t_js[] = {
+       CharConst ("\xe2\x80\x94"),
+       CharConst ("\xe3\x80\x9c"),
+       CharConst ("\xe2\x80\x96"),
+       CharConst ("\xe2\x88\x92"),
+       CharConst ("\xc2\xa2"),
+       CharConst ("\xc2\xa3"),
+       CharConst ("\xc2\xac"),
+};
+
+
+static ustring  fix (const ustring& text, uregex& re, CS* t) {
+    uiterator  b, e;
+    umatch  m;
+    ustring  ans;
+    int  i;
+
+    ans.reserve (text.size () + 32);
+    b = text.begin ();
+    e = text.end ();
+    while (usearch (b, e, m, re)) {
+       if (b != m[0].first)
+           ans.append (b, m[0].first);
+       for (i = 0; i < 7; i ++) {
+           if (m[i + 1].matched) {
+               ans.append (t[i].c, t[i].s);
+               break;
+           }
+       }
+       b = m[0].second;
+    }
+    if (b != e)
+       ans.append (b, e);
+
+    return ans;
+}
+
+ustring  fixFromMS (const ustring& text) {
+    return fix (text, re_ms, t_js);
+}
+
+ustring  fixToMS (const ustring& text) {
+    return fix (text, re_js, t_ms);
+}
diff --git a/lib/utf8-jp.h b/lib/utf8-jp.h
new file mode 100644 (file)
index 0000000..c642215
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef UTF8_JP_H
+#define UTF8_JP_H
+
+#include "ustring.h"
+
+ustring  fixFromMS (const ustring& text);
+ustring  fixToMS (const ustring& text);
+
+#endif /* UTF8_JP_H */
diff --git a/lib/utf8.cc b/lib/utf8.cc
new file mode 100644 (file)
index 0000000..1065a34
--- /dev/null
@@ -0,0 +1,260 @@
+#include "utf8.h"
+#include "ustring.h"
+#include <iostream>
+
+using namespace  std;
+
+/*
+  Clip char which is not a valid UTF-8 text.
+*/
+ustring  fixUTF8 (const ustring& str) {
+    ustring  ans;
+    size_t  i, n;
+    u_char  c, d, e, f;
+
+    n = str.length ();
+    ans.reserve (n);
+    for (i = 0; i < n;) {
+       c = str[i ++];
+       if (c <= 0x7f) {
+           if (c == '\r') {
+               ans.append (1, '\n');
+               if (i < n && str[i] == '\n') {
+                   i ++;
+               }
+           } else {
+               ans.append (1, c);
+           }
+       } else if (0xc2 <= c && c <= 0xdf && i < n) {
+           d = str[i];
+           if (0x80 <= d && d <= 0xbf) {
+               i ++;
+               ans.append (1, c);
+               ans.append (1, d);
+           }
+       } else if (c == 0xe0 && i + 1 < n) {
+           d = str[i];
+           e = str[i + 1];
+           if (0xa0 <= d && d <= 0xbf && 0x80 <= e && e <= 0xbf) {
+               i += 2;
+               ans.append (1, c);
+               ans.append (1, d);
+               ans.append (1, e);
+           }
+       } else if (0xe1 <= c && c <= 0xef && i + 1 < n) {
+           d = str[i];
+           e = str[i + 1];
+           if (0x80 <= d && d <= 0xbf && 0x80 <= e && e <= 0xbf) {
+               i += 2;
+               ans.append (1, c);
+               ans.append (1, d);
+               ans.append (1, e);
+           }
+       } else if (c == 0xf0 && i + 2 < n) {
+           d = str[i];
+           e = str[i + 1];
+           f = str[i + 2];
+           if (0x90 <= d && d <= 0xbf && 0x80 <= e && e <= 0xbf && 0x80 <= f && f <= 0xbf) {
+               i += 3;
+               ans.append (1, c);
+               ans.append (1, d);
+               ans.append (1, e);
+               ans.append (1, f);
+           }
+       } else if (0xf1 <= c && c <= 0xf3 && i + 2 < n) {
+           d = str[i];
+           e = str[i + 1];
+           f = str[i + 2];
+           if (0x80 <= d && d <= 0xbf && 0x80 <= e && e <= 0xbf && 0x80 <= f && f <= 0xbf) {
+               i += 3;
+               ans.append (1, c);
+               ans.append (1, d);
+               ans.append (1, e);
+               ans.append (1, f);
+           }
+       } else if (c == 0xf4 && i + 2 < n) {
+           d = str[i];
+           e = str[i + 1];
+           f = str[i + 2];
+           if (0x80 <= d && d <= 0x8f && 0x80 <= e && e <= 0xbf && 0x80 <= f && f <= 0xbf) {
+               i += 3;
+               ans.append (1, c);
+               ans.append (1, d);
+               ans.append (1, e);
+               ans.append (1, f);
+           }
+       }
+    }
+    return ans;
+}
+
+void  nextChar (uiterator& it, const uiterator& end) {
+    ustring::value_type  c;
+
+    c = *(it ++);
+    if ((c & 0x80) == 0) {
+    } else if ((c & 0xe0) == 0xc0) {
+       if (it != end) it ++;
+    } else if ((c & 0xf0) == 0xe0) {
+       if (it != end) it ++;
+       if (it != end) it ++;
+    } else if ((c & 0xf8) == 0xf0) {
+       if (it != end) it ++;
+       if (it != end) it ++;
+       if (it != end) it ++;
+    } else if ((c & 0xfc) == 0xf8) {
+       if (it != end) it ++;
+       if (it != end) it ++;
+       if (it != end) it ++;
+       if (it != end) it ++;
+    } else if ((c & 0xfe) == 0xfc) {
+       if (it != end) it ++;
+       if (it != end) it ++;
+       if (it != end) it ++;
+       if (it != end) it ++;
+       if (it != end) it ++;
+    } else {
+       // error
+    }
+}
+
+void  nextChar (uiterator& it, const uiterator& end, ustring& target) {
+    ustring::value_type  c;
+
+    c = *(it ++);
+    if ((c & 0x80) == 0) {
+       target += c;
+    } else if ((c & 0xe0) == 0xc0) {
+       if (it < end) {
+           target += c;
+           target += (*it ++);
+       } else {
+           it = end;
+       }
+    } else if ((c & 0xf0) == 0xe0) {
+       if (it + 1 < end) {
+           target += c;
+           target += (*it ++);
+           target += (*it ++);
+       } else {
+           it = end;
+       }
+    } else if ((c & 0xf8) == 0xf0) {
+       if (it + 2 < end) {
+           target += c;
+           target += (*it ++);
+           target += (*it ++);
+           target += (*it ++);
+       } else {
+           it = end;
+       }
+    } else if ((c & 0xfc) == 0xf8) {
+       if (it + 3 < end) {
+           target += c;
+           target += (*it ++);
+           target += (*it ++);
+           target += (*it ++);
+           target += (*it ++);
+       } else {
+           it = end;
+       }
+    } else if ((c & 0xfe) == 0xfc) {
+       if (it + 4 < end) {
+           target += c;
+           target += (*it ++);
+           target += (*it ++);
+           target += (*it ++);
+           target += (*it ++);
+           target += (*it ++);
+       } else {
+           it = end;
+       }
+    } else {
+       // error
+    }
+}
+
+void  lastChar (const ustring& text, uiterator& ans) {
+    uiterator  b = text.begin ();
+    uiterator  e = text.end ();
+
+    assert (b != e);
+    e --;
+    while ((*e & 0xc0) == 0x80) {
+       e --;
+    }
+    ans = e;
+}
+
+ustring  ellipsis (const ustring& text, int limit) {
+    uiterator  b, e;
+    ustring  u;
+
+    u.reserve (256);
+    for (b = text.begin (), e = text.end (); limit > 0 && b != e; nextChar (b, e, u)) {
+       limit --;
+    }
+    if (b != e) {
+       u.append (CharConst ("..."));
+    }
+    return u;
+}
+
+ustring  logText (const ustring& text) {
+    uiterator  b = text.begin ();
+    uiterator  e = text.end ();
+    umatch  m;
+    ustring  u;
+    static uregex  re ("[\\000-\\037\\0177]");
+
+    u.reserve (256);
+    while (usearch (b, e, m, re)) {
+       if (b != m[0].first)
+           u += ustring (b, m[0].first);
+       if (*m[0].first == '\n') {
+           u += "//";
+       } else {
+           u += '_';
+       }
+       b = m[0].second;
+    }
+    if (b != e) {
+       u += ustring (b, e);
+    }
+    return u;
+}
+
+void  clipEnd (ustring& val, uregex& re1, uregex& re2) {
+    uiterator  b, i1, i2, e;
+    umatch  m;
+
+    b = val.begin ();
+    e = val.end ();
+    if (usearch (b, e, m, re1)) {
+       i1 = m[0].second;
+    } else {
+       i1 = b;
+    }
+    if (usearch (i1, e, m, re2)) {
+       i2 = m[0].first;
+    } else {
+       i2 = e;
+    }
+    if (i1 != b || i2 != e) {
+       val = ustring (i1, i2);
+    }
+}
+
+void  clipWhiteEnd (ustring& val) {
+    static uregex  re1 ("^(" UTF8_SPACE "|" UTF8_ZWSPACE "|" UTF8_IDEOSPACE ")+");
+    static uregex  re2 ("(" UTF8_SPACE "|" UTF8_ZWSPACE "|" UTF8_IDEOSPACE ")+$");
+
+    clipEnd (val, re1, re2);
+}
+
+void  clipNLEnd (ustring& val) {
+    static uregex  re1 ("^\\n+");
+    static uregex  re2 ("\\n+$");
+
+    clipEnd (val, re1, re2);
+}
diff --git a/lib/utf8.h b/lib/utf8.h
new file mode 100644 (file)
index 0000000..e3477b9
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef UTF8_H
+#define UTF8_H
+
+#include "ustring.h"
+
+#define  UTF8_SPACE            "\x20"
+#define  UTF8_NBSPACE          "\xc2\xa0"
+#define  UTF8_ZWSPACE          "\xe2\x80\x8b"
+#define  UTF8_WJOINERSPACE     "\xe2\x81\xa0"
+#define  UTF8_IDEOSPACE                "\xe3\x80\x80"
+#define  UTF8_ZWNBSPACE                "\xef\xbb\xbf"
+
+
+ustring  fixUTF8 (const ustring& str);
+void  nextChar (uiterator& it, const uiterator& end);
+void  nextChar (uiterator& it, const uiterator& end, ustring& target);
+void  lastChar (const ustring& text, uiterator& ans);
+ustring  ellipsis (const ustring& text, int limit);
+ustring  logText (const ustring& text);
+void  clipEnd (ustring& val, uregex& re1, uregex& re2);
+void  clipWhiteEnd (ustring& val);
+void  clipNLEnd (ustring& val);
+
+#endif /* UTF8_H */
diff --git a/lib/util_apache.cc b/lib/util_apache.cc
new file mode 100644 (file)
index 0000000..8092c40
--- /dev/null
@@ -0,0 +1,54 @@
+#include "util_apache.h"
+#include "util_const.h"
+#include "util_string.h"
+#include "httpconst.h"
+#include "ustring.h"
+#include <vector>
+
+ustring  apacheAbsolutePath (const ustring& url) {
+    ustring  ans;
+    std::vector<ustring>  ary;
+    std::vector<ustring>::iterator  it;
+    Splitter  sp (url.begin (), url.end (), re_slash);
+    uiterator  b, e;
+    bool  fdirpath;
+    size_t  len = url.length ();
+
+    if (len > 0 && url[len - 1] == '/') {
+       fdirpath = true;
+    } else {
+       fdirpath = false;
+    }
+    if (len > 0 && url[0] == '/') {
+       sp.next ();
+    } else {
+       ustring  e = getenvString (kSCRIPT_NAME);
+       split (e.begin (), e.end (), re_slash, ary);
+       if (ary.size () > 0 && ary.back ().length () > 0) {
+           ary.pop_back();
+       }
+    }
+    while (sp.next ()) {
+       b = sp.begin ();
+       e = sp.end ();
+       if (b == e) {
+       } else if (match (b, e, CharConst ("."))) {
+       } else if (match (b, e, CharConst (".."))) {
+           if (ary.size () > 0)
+               ary.pop_back ();
+       } else {
+           ary.push_back (ustring (b, e));
+       }
+    }
+
+    for (it = ary.begin (); it != ary.end (); it ++) {
+       if ((*it).length () > 0) {
+           ans.append (uSlash).append (*it);
+       }
+    }
+    if (fdirpath) {
+       ans.append (uSlash);
+    }
+
+    return ans;
+}
diff --git a/lib/util_apache.h b/lib/util_apache.h
new file mode 100644 (file)
index 0000000..861857a
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef UTIL_APACHE_H
+#define UTIL_APACHE_H
+
+#include "ustring.h"
+
+ustring  apacheAbsolutePath (const ustring& url);
+
+#endif /* UTIL_APACHE_H */
diff --git a/lib/util_check.cc b/lib/util_check.cc
new file mode 100644 (file)
index 0000000..3c06365
--- /dev/null
@@ -0,0 +1,171 @@
+#include "util_check.h"
+#include "util_const.h"
+#include "util_string.h"
+#include "httpconst.h"
+#include "motorconst.h"
+#include "ustring.h"
+
+static bool  checkname (const ustring& name, uregex& re) {
+    umatch  m;
+
+    if (usearch (name, m, re)) {
+       return true;            // OK
+    } else {
+       return false;           // NG
+    }
+}
+
+static bool  checkname (uiterator& b, uiterator& e, uregex& re) {
+    umatch  m;
+
+    if (usearch (b, e, m, re)) {
+       return true;            // OK
+    } else {
+       return false;           // NG
+    }
+}
+
+bool  checkName (const ustring& name) {
+    static uregex  re ("^" kWNAME "{0,31}$");
+
+    return (checkname (name, re));
+}
+
+bool  checkFilename (const ustring& name) {
+    static uregex  re ("^" kWNAME "{1,127}(\\." kWORD "{1,16})?$");
+
+    return (checkname (name, re));
+}
+
+bool  checkResourceName (const ustring& name) {
+    static uregex  re ("^(" kWNAME "{0,127}/)*" kWNAME "{0,127}(\\." kWORD "{1,16})?$");
+
+    return (checkname (name, re));
+}
+
+bool  checkAbsoluteResourceName (const ustring& name) {
+    static uregex  re ("^/(" kWNAME "{0,127}/)*" kWNAME "{0,127}(\\." kWORD "{1,16})?$");
+
+    return (checkname (name, re));
+}
+
+bool  checkIP (const ustring& name) {
+    static uregex  re ("^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$");
+
+    return (checkname (name, re));
+}
+
+bool  checkDomainDot (const ustring& name) {
+    static uregex  re ("^\\.?([a-zA-Z0-9-]+\\.)*([a-zA-Z0-9-]+)$");
+
+    return (checkname (name, re));
+}
+
+bool  checkHostname (const ustring& name) {
+    static uregex  re ("^[a-zA-Z0-9][a-zA-Z0-9\\-]*(\\.[a-zA-Z0-9][a-zA-Z0-9\\-]*)*$");
+
+    return (checkname (name, re));
+}
+
+bool  checkMimeType (const ustring& name) {
+    static  uregex re ("^[a-z_0-9-]+/[a-z_0-9.+-]+$");
+
+    return (checkname (name, re));
+}
+
+bool  checkMailAddr (const ustring& name) {
+    static  uregex re ("^[^\\x00- @\\x7f-\\xff]+@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*$");
+
+    return (checkname (name, re));
+}
+
+bool  checkNum (const ustring& text) {
+    return (checkname (text, re_num));
+}
+
+bool  checkNum (uiterator& b, uiterator& e) {
+    return (checkname (b, e, re_num));
+}
+
+static uregex  re_width ("^[0-9]+%?$");
+bool  checkWidth (uiterator& b, uiterator& e) {
+    return (checkname (b, e, re_width));
+}
+
+bool  checkWidth (const ustring& text) {
+    return (checkname (text, re_width));
+}
+
+static uregex  re_color ("^#([0-9a-fA-F]{3}){1,2}$");
+bool  checkColor (uiterator& b, uiterator& e) {
+    return (checkname (b, e, re_color));
+}
+
+bool  checkColor (const ustring& text) {
+    return (checkname (text, re_color));
+}
+
+static uregex  re_wikiid ("^" rWikiID "$");
+bool  checkWikiID (uiterator& b, uiterator& e) {
+    return (checkname (b, e, re_wikiid));
+}
+
+bool  checkWikiID (const ustring& text) {
+    return (checkname (text, re_wikiid));
+}
+
+bool  checkAry (const ustring& name) {
+    return (name.length () > 0 && name[0] == '@');
+}
+
+bool  isHTTPS () {
+    char*  e = getenv (kHTTPS);
+
+    if (e && *e == 'o') {
+       return true;
+    } else {
+       return false;
+    }
+}
+
+int  checkAgent () {
+    umatch  m;
+    int  ans = 0;
+    static uregex  re_os ("Macintosh|Mac_|Windows|iPhone|iPod");
+    static uregex  re_ua ("Safari|MSIE|Gecko|Opera|NetFront");
+
+    ustring  agent = getenvString (kHTTP_USER_AGENT);
+    if (usearch (agent, m, re_os)) {
+       switch (*m[0].first) {
+       case 'M':
+           ans |= UA_Mac;
+           break;
+       case 'W':
+           ans |= UA_Windows;
+           break;
+       case 'i':
+           ans |= UA_iPhone;
+           break;
+       }
+    }
+    if (usearch (agent, m, re_ua)) {
+       switch (*m[0].first) {
+       case 'S':
+           ans |= UA_Safari;
+           break;
+       case 'M':
+           ans |= UA_Mozilla;
+           break;
+       case 'G':
+           ans |= UA_Mozilla;
+           break;
+       case 'O':
+           ans |= UA_Opera;
+           break;
+       case 'N':
+           ans |= UA_NetFront;
+           break;
+       }
+    }
+    return ans;
+}
diff --git a/lib/util_check.h b/lib/util_check.h
new file mode 100644 (file)
index 0000000..a8f5e21
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef UTIL_CHECK_H
+#define UTIL_CHECK_H
+
+#include "ustring.h"
+
+#define  UA_Windows    0x00000001
+#define  UA_Mac                0x00000002
+#define  UA_iPhone     0x00000004
+#define  UA_IE         0x00000100
+#define  UA_Mozilla    0x00000200
+#define  UA_Safari     0x00000400
+#define  UA_Opera      0x00000800
+#define  UA_NetFront   0x00001000
+
+bool  checkName (const ustring& name);
+bool  checkFilename (const ustring& name);
+bool  checkResourceName (const ustring& name);
+bool  checkAbsoluteResourceName (const ustring& name);
+bool  checkIP (const ustring& name);
+bool  checkDomainDot (const ustring& name);
+bool  checkHostname (const ustring& name);
+bool  checkMimeType (const ustring& name);
+bool  checkMailAddr (const ustring& name);
+bool  checkNum (const ustring& text);
+bool  checkNum (uiterator& b, uiterator& e);
+bool  checkWidth (uiterator& b, uiterator& e);
+bool  checkWidth (const ustring& text);
+bool  checkColor (uiterator& b, uiterator& e);
+bool  checkColor (const ustring& text);
+bool  checkWikiID (uiterator& b, uiterator& e);
+bool  checkWikiID (const ustring& text);
+bool  checkAry (const ustring& name);
+bool  isHTTPS ();
+int  checkAgent ();
+
+#endif /* UTIL_CHECK_H */
diff --git a/lib/util_const.cc b/lib/util_const.cc
new file mode 100644 (file)
index 0000000..8c94e38
--- /dev/null
@@ -0,0 +1,64 @@
+#include "ml.h"
+#include "util_const.h"
+#include "motorconst.h"
+#include "ustring.h"
+
+ustring  uErrorWrongNumber (CharConst ("wrong number of argument."));
+ustring  uErrorWrongType (CharConst ("wrong type of argument."));
+ustring  uErrorDiv0 (CharConst ("divided by 0."));
+ustring  uErrorBadArg (CharConst ("bad argument."));
+ustring  uErrorSyntax (CharConst ("syntax error."));
+ustring  uErrorNotImpl (CharConst ("not implemented."));
+ustring  uErrorFilenameEmpty (CharConst ("file name is empty."));
+ustring  uErrorCmdNameEmpty (CharConst ("command name is empty."));
+ustring  uErrorVarNameEmpty (CharConst ("variable name is empty."));
+ustring  uErrorNoStore (CharConst ("no serial store directory."));
+ustring  uErrorNoStorage (CharConst ("no named store directory."));
+ustring  uErrorInclNest (CharConst ("deeply nested."));
+ustring  uErrorBadName (CharConst (": bad name."));
+ustring  uErrorNotFound (CharConst (": not found."));
+ustring  uErrorMissingDatastore (CharConst ("missing datastore parameter."));
+ustring  uErrorBadParam (CharConst (": bad parameter definition."));
+const char*  uErrorBadType = ": bad type.";
+const char*  uErrorBadCmd = ": bad command.";
+const char*  uErrorCantOpen = ": can't open.";
+const char*  uErrorBadFile = ": bad filename.";
+const char*  uErrorBadMailAddr = ": bad mail address.";
+const char*  uErrorBadDatastore = ": bad datastore name.";
+const char*  uErrorFileSize = ": file size too large.";
+
+ustring  uOne (CharConst ("1"));
+ustring  uNil (CharConst ("nil"));
+ustring  uNil2 (CharConst ("()"));
+ustring  uLF (CharConst ("\n"));
+ustring  uCRLF (CharConst ("\r\n"));
+ustring  uSPC (CharConst (" "));
+ustring  uR (CharConst ("R"));
+ustring  uDash (CharConst ("-"));
+ustring  uSlash (CharConst ("/"));
+ustring  uColon (CharConst (":"));
+ustring  uUScore (CharConst ("_"));
+ustring  uEmpty;
+ustring  uXSerial (CharConst ("XSerial"));
+ustring  uLambda (CharConst ("lambda"));
+uregex  re_tab ("\t");
+uregex  re_lf ("\\n");
+uregex  re_nl ("\\r\\n?|\\n");
+uregex  re_slash ("/");
+uregex  re_colon (":");
+uregex  re_comma (",");
+//uregex  re_amp ("&");
+//uregex  re_eq ("=");
+uregex  re_num ("^[0-9]+$");
+uregex  re_q ("\"");
+MNode*  mlTrue = NULL;
+static MNodePtr  trueHolder;
+
+class  ConstInit {
+public:
+    ConstInit () {
+       trueHolder = mlTrue = newMNode_bool (true);
+    };
+    ~ConstInit () {};
+};
+static ConstInit  init;
diff --git a/lib/util_const.h b/lib/util_const.h
new file mode 100644 (file)
index 0000000..93c39f4
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef UTIL_CONST_H
+#define UTIL_CONST_H
+
+#include "ustring.h"
+class  MNode;
+
+extern ustring  uErrorWrongNumber;
+extern ustring  uErrorWrongType;
+extern ustring  uErrorDiv0;
+extern ustring  uErrorBadArg;
+extern ustring  uErrorSyntax;
+extern ustring  uErrorNotImpl;
+extern ustring  uErrorFilenameEmpty;
+extern ustring  uErrorCmdNameEmpty;
+extern ustring  uErrorVarNameEmpty;
+extern ustring  uErrorNoStore;
+extern ustring  uErrorNoStorage;
+extern ustring  uErrorInclNest;
+extern ustring  uErrorBadName;
+extern ustring  uErrorNotFound;
+extern ustring  uErrorMissingDatastore;
+extern ustring  uErrorBadParam;
+
+extern const char*  uErrorBadType;
+extern const char*  uErrorBadCmd;
+extern const char*  uErrorCantOpen;
+extern const char*  uErrorBadFile;
+extern const char*  uErrorBadMailAddr;
+extern const char*  uErrorBadDatastore;
+extern const char*  uErrorFileSize;
+
+extern ustring  uOne;
+extern ustring  uNil;
+extern ustring  uNil2;
+extern ustring  uLF;
+extern ustring  uCRLF;
+extern ustring  uSPC;
+extern ustring  uR;
+extern ustring  uDash;
+extern ustring  uSlash;
+extern ustring  uColon;
+extern ustring  uUScore;
+extern ustring  uEmpty;
+extern ustring  uXSerial;
+extern ustring  uLambda;
+extern uregex  re_tab;
+extern uregex  re_lf;
+extern uregex  re_nl;
+extern uregex  re_slash;
+extern uregex  re_colon;
+extern uregex  re_comma;
+//extern uregex  re_amp;
+//extern uregex  re_eq;
+extern uregex  re_num;
+extern uregex  re_q;
+extern MNode*  mlTrue;
+
+#endif /* UTIL_CONST_H */
diff --git a/lib/util_file.cc b/lib/util_file.cc
new file mode 100644 (file)
index 0000000..264cc66
--- /dev/null
@@ -0,0 +1,110 @@
+#include "util_file.h"
+#include "config.h"
+#include "ustring.h"
+#include "util_const.h"
+#include "filemacro.h"
+#include <iostream>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+//bool  fileExists (const ustring& name) {
+bool  isPlainFile (const ustring& name) {
+    struct stat  sb;
+
+//    if (name.size () > 0 && stat (name.c_str (), &sb) == 0 && (sb.st_mode & S_IFREG)) {
+    if (name.size () > 0 && stat (name.c_str (), &sb) == 0 && S_ISREG (sb.st_mode)) {
+       return true;
+    } else {
+       return false;
+    }
+}
+
+bool  isExecutableFile (const ustring& name) {
+    struct stat  sb;
+
+    if (name.size () > 0 && stat (name.c_str (), &sb) == 0 && (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
+       return true;
+    } else {
+       return false;
+    }
+}
+
+bool  isDirectory (const ustring& name) {
+    struct stat  sb;
+
+//    if (name.size () > 0 && stat (name.c_str (), &sb) == 0 && (sb.st_mode & S_IFREG)) {
+    if (name.size () > 0 && stat (name.c_str (), &sb) == 0 && S_ISDIR (sb.st_mode)) {
+       return true;
+    } else {
+       return false;
+    }
+}
+
+bool  fileSize (const ustring& name, off_t& size) {
+    struct stat  sb;
+
+    if (name.size () > 0 && stat (name.c_str (), &sb) == 0 && S_ISREG (sb.st_mode)) {
+       size = sb.st_size;
+       return true;
+    } else {
+       return false;
+    }
+}
+
+bool  readFile (const ustring& filename, ustring& ans, size_t max) {
+    if (filename.size () > 0) {
+       FileMacro  f;
+       off_t  s;
+
+       if (f.openRead (filename.c_str ())) {
+           s = f.size ();
+           if (max > 0 && s > max)
+               throw (filename + uErrorFileSize);
+           ans.resize (s);
+           if (s > 0)
+               f.read (&ans.at (0), s);
+           f.close ();
+       } else {
+           return false;
+       }
+    } else {
+       return false;           // NG
+    }
+    return true;               // OK
+}
+
+void  writeFile (const ustring& filename, ustring& data) {
+    if (filename.size () > 0) {
+       FileMacro  f;
+       if (f.openWrite (filename.c_str ())) {
+           if (data.size () > 0)
+               f.write (&data.at (0), data.size ());
+       }
+    }
+}
+
+/*
+  top must end with slash.
+*/
+void  makeSubDir (ustring& top, const ustring& sub) {
+    uiterator  b, e;
+    umatch  m;
+
+    b = sub.begin ();
+    e = sub.end ();
+    while (b != e && usearch (b, e, m, re_slash)) {
+       top.append (b, m[0].second);
+       mkdir (top.c_str (), 0777);
+#ifdef DEBUG2
+       std::cerr << "mkdir:" << top << "\n";
+#endif /* DEBUG */
+       b = m[0].second;
+    }
+    if (b != e) {
+       top.append (b, e);
+       mkdir (top.c_str (), 0777);
+#ifdef DEBUG2
+       std::cerr << "mkdir:" << top << "\n";
+#endif /* DEBUG */
+    }
+}
diff --git a/lib/util_file.h b/lib/util_file.h
new file mode 100644 (file)
index 0000000..fad2572
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef UTIL_FILE_H
+#define UTIL_FILE_H
+
+#include "config.h"
+#include "ustring.h"
+
+bool  isPlainFile (const ustring& name);
+bool  isExecutableFile (const ustring& name);
+bool  isDirectory (const ustring& name);
+bool  fileSize (const ustring& name, off_t& size);
+bool  readFile (const ustring& filename, ustring& ans, size_t max = cResourceFileMax);
+void  writeFile (const ustring& filename, ustring& data);
+void  makeSubDir (ustring& top, const ustring& sub);
+
+#endif /* UTIL_FILE_H */
diff --git a/lib/util_inet.cc b/lib/util_inet.cc
new file mode 100644 (file)
index 0000000..004f599
--- /dev/null
@@ -0,0 +1,29 @@
+#include "util_inet.h"
+#include "util_string.h"
+#include "ustring.h"
+#include <netinet/in.h>
+#include <netdb.h>
+#include <string.h>
+#include <sys/socket.h>
+
+ustring  getnameinfo (const ustring& ip) {
+    struct sockaddr_in  sa;
+    char  host[NI_MAXHOST + 4];
+    char*  p;
+    umatch  m;
+    static uregex  re ("^([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$");
+
+    if (regex_search (ip, m, re)) {
+       memset (&sa, 0, sizeof (sa));
+       sa.sin_family = AF_INET;
+       p = (char*)&sa.sin_addr;
+       p[0] = strtoul(ustring (m[1].first, m[1].second));
+       p[1] = strtoul(ustring (m[2].first, m[2].second));
+       p[2] = strtoul(ustring (m[3].first, m[3].second));
+       p[3] = strtoul(ustring (m[4].first, m[4].second));
+       if (getnameinfo ((struct sockaddr*)&sa, sizeof (sa), host, NI_MAXHOST, NULL, 0, 0) == 0) {
+           return ustring (host);
+       }
+    }
+    return ip;
+}
diff --git a/lib/util_inet.h b/lib/util_inet.h
new file mode 100644 (file)
index 0000000..70a6a83
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef UTIL_INET_H
+#define UTIL_INET_H
+
+#include "ustring.h"
+
+ustring  getnameinfo (const ustring& ip);
+
+#endif /* UTIL_INET_H */
diff --git a/lib/util_mimetype.cc b/lib/util_mimetype.cc
new file mode 100644 (file)
index 0000000..5cd5815
--- /dev/null
@@ -0,0 +1,102 @@
+#include "util_mimetype.h"
+#include "config.h"
+#include "util_const.h"
+#include "util_string.h"
+#include "motorconst.h"
+#include "httpconst.h"
+#include "bdbmacro.h"
+#include "ustring.h"
+#include <sys/stat.h>
+
+static struct {
+    const char*  ext;
+    size_t  extlen;
+    const char*  type;
+    size_t  typelen;
+}  typelist [] = {
+    {CharConst ("js"), CharConst ("application/javascript")},
+    {CharConst ("doc"), CharConst ("application/msword")},
+    {CharConst ("pdf"), CharConst ("application/pdf")},
+    {CharConst ("xls"), CharConst ("application/vnd.ms-excel")},
+    {CharConst ("ppt"), CharConst ("application/vnd.ms-powerpoint")},
+    {CharConst ("swf"), CharConst ("application/x-shockwave-flash")},
+    {CharConst ("gif"), CharConst ("image/gif")},
+    {CharConst ("jpeg"), CharConst ("image/jpeg")},
+    {CharConst ("jpg"), CharConst ("image/jpeg")},
+    {CharConst ("png"), CharConst ("image/png")},
+    {CharConst ("tiff"), CharConst ("image/tiff")},
+    {CharConst ("tif"), CharConst ("image/tiff")},
+    {CharConst ("css"), CharConst ("text/css")},
+    {CharConst ("csv"), CharConst ("text/csv")},
+    {CharConst ("html"), CharConst ("text/html")},
+    {CharConst ("htm"), CharConst ("text/html")},
+    {CharConst ("txt"), CharConst ("text/plain")},
+    {CharConst ("text"), CharConst ("text/plain")},
+    {NULL, 0, NULL, 0}
+};
+
+static ustring  mimetype_db (const ustring& ext, bool f) {
+    BDBHashRDOnly  bdb;
+    ustring  ans;
+
+    bdb.open (cDataTop kMimeTypeDB);
+    if (! bdb.get (ext, ans)) {
+       if (f)
+           ans.assign (CharConst (kMIME_OCTET));
+       else
+           ans.resize (0);
+    }
+    bdb.close ();
+    return ans;
+}
+
+static ustring  mimetype_static (const ustring& ext, bool f) {
+    int  i;
+
+    for (i = 0; typelist[i].ext; i ++) {
+       if (match (ext, typelist[i].ext, typelist[i].extlen))
+           return ustring (typelist[i].type, typelist[i].typelen);
+    }
+    if (f)
+       return ustring (CharConst (kMIME_OCTET));
+    else
+       return uEmpty;
+}
+
+ustring  getExt (const ustring& name) {
+    static uregex  re ("\\.([a-zA-Z0-9]{1,16})$");
+    umatch  m;
+
+    if (usearch (name, m, re)) {
+       ustring  ans (m[1].second - m[1].first, ' ');
+       uiterator  b;
+       ustring::iterator  it;
+       for (it = ans.begin (), b = m[1].first; b != m[1].second; it ++, b ++) {
+           if ('A' <= *b && *b <= 'Z') {
+               *it = *b + ('a' - 'A');
+           } else {
+               *it = *b;
+           }
+       }
+       return ans;
+    } else {
+       return uEmpty;
+    }
+}
+
+ustring  mimetype (const ustring& ext, bool f) {
+    struct stat  sb;
+
+    if (ext.length () == 0) {
+       if (f)
+           return ustring (CharConst (kMIME_OCTET));
+       else
+           return uEmpty;
+    } else {
+       if (stat (cDataTop kMimeTypeDB, &sb) == 0) {
+           return mimetype_db (ext, f);
+       } else {
+           return mimetype_static (ext, f);
+       }
+    }
+}
diff --git a/lib/util_mimetype.h b/lib/util_mimetype.h
new file mode 100644 (file)
index 0000000..7b9cecb
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef UTIL_MIMETYPE_H
+#define UTIL_MIMETYPE_H
+
+#include "ustring.h"
+
+ustring  getExt (const ustring& name);
+ustring  mimetype (const ustring& ext, bool f = true);
+
+#endif /* UTIL_MIMETYPE_H */
diff --git a/lib/util_proc.cc b/lib/util_proc.cc
new file mode 100644 (file)
index 0000000..ba03b45
--- /dev/null
@@ -0,0 +1,149 @@
+#include "util_proc.h"
+#include "config.h"
+#include "ustring.h"
+#include <iostream>
+#include <strings.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#ifdef HEAPDEBUG
+void  mdbClose ();
+#endif
+
+int  exec_cmd (char* const argv[]) {
+    pid_t  pid;
+    int  status;
+
+    pid = fork ();
+    if (pid < 0) {             // error
+       throw (ustring (CharConst ("fork failed.")));
+    }
+    if (pid == 0) {
+       // child
+       close (0);
+       open (path_devnull, O_RDONLY);
+       close (1);
+       open (path_devnull, O_WRONLY);
+       close (2);
+       open (path_devnull, O_WRONLY);
+       execv (argv[0], argv);
+#ifdef HEAPDEBUG
+       mdbClose ();
+#endif
+       exit (1);               // heap lost
+    } else {
+       // parent
+       waitpid (pid, &status, 0);
+    }
+    return (status >> 8);
+}
+
+
+int  ProcRW::open (char* const argv[], ustring* dir) {
+    int  fd1[2];
+    int  fd2[2];
+
+    pipe (fd1);
+    pipe (fd2);
+    pid = fork ();
+    if (pid < 0) {             // error
+       ::close (fd1[0]);
+       ::close (fd1[1]);
+       ::close (fd2[0]);
+       ::close (fd2[1]);
+       throw (ustring (CharConst ("fork failed.")));
+//     return 0;               // NG
+    }
+    if (pid == 0) {
+       // child
+#ifdef DEBUG2
+       std::cerr << "fork child pid:" << getpid () << "\n";
+       atexit (NULL);
+#endif /* DEBUG */
+       ::close (fd1[1]);
+       ::close (fd2[0]);
+       ::close (0);            // stdin
+       dup (fd1[0]);           // --stdin
+       ::close (fd1[0]);
+       ::close (1);            // stdout
+       dup (fd2[1]);           // --stdout
+       ::close (fd2[1]);
+       if (dir) {
+           chdir (dir->c_str ());
+       }
+#ifdef DEBUG2
+       fprintf (stderr, "exec %s\n", argv[0]);
+#endif /* DEBUG */
+       execv (argv[0], argv);
+#ifdef DEBUG
+       char*  p = rindex (argv[0], '/');
+       if (p)
+           std::cerr << (++ p) << ": can't execute.\n";
+       else
+           std::cerr << argv[0] << ": can't execute.\n";
+#endif /* DEBUG */
+#ifdef HEAPDEBUG
+       mdbClose ();
+#endif
+       exit (1);
+    } else {
+       // parent
+       ::close (fd1[0]);
+       ::close (fd2[1]);
+       wfd = fd1[1];
+       rfd = fd2[0];
+    }
+    return 1;                  // OK
+}
+
+int  ProcRW::close () {
+    int  status = 0;
+
+    if (wfd >= 0)
+       ::close (wfd);
+    if (rfd >= 0)
+       ::close (rfd);
+    if (pid > 0)
+       waitpid (pid, &status, 0);
+    rfd = wfd = -1;
+    pid = 0;
+
+    return (status >> 8);
+}
+
+void  ProcRW::closeWriter () {
+    if (wfd >= 0)
+       ::close (wfd);
+    wfd = -1;
+}
+
+ssize_t  ProcRW::read (char* b, size_t n) {
+    return ::read (rfd, b, n);
+}
+
+void  ProcRW::read (ustring& ans) {
+    char*  b = (char*)malloc (4096);
+    ssize_t  s;
+
+//    while ((s = ::read (rfd, b, 4096)) > 0) {
+    while ((s = read (b, 4096)) > 0) {
+       ans.append (b, s);
+    }
+
+    free (b);
+}
+
+void  ProcRW::writeln (const ustring& text) {
+    ustring  a (text);
+
+    a.append (CharConst ("\n"));
+    ::write (wfd, a.data (), a.size ());
+}
+
+void  ProcRW::write (const char* v, ssize_t size) {
+    ::write (wfd, v, size);
+}
+
diff --git a/lib/util_proc.h b/lib/util_proc.h
new file mode 100644 (file)
index 0000000..4b50ebf
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef UTIL_PROC_H
+#define UTIL_PROC_H
+
+#include "ustring.h"
+//#include <boost/circular_buffer.hpp>
+#include <unistd.h>
+
+class  ProcRW {
+ public:
+    pid_t  pid;
+    int  rfd;
+    int  wfd;
+//    boost::circular_buffer<int> cb(3);
+//    boost::circular_buffer<char>  buf(4096);
+
+    ProcRW () {
+       pid = 0;
+       rfd = -1;
+       wfd = -1;
+    };
+    virtual  ~ProcRW () {
+       close ();
+    };
+    virtual int  open (char* const argv[], ustring* dir = NULL);
+    virtual int  close ();
+    virtual void  closeWriter ();
+    virtual ssize_t  read (char* b, size_t n);
+    virtual void  read (ustring& ans);
+    virtual void  writeln (const ustring& text);
+    virtual void  write (const char* v, ssize_t size);
+};
+
+int  exec_cmd (char* const argv[]);
+
+#endif /* UTIL_PROC_H */
diff --git a/lib/util_random.cc b/lib/util_random.cc
new file mode 100644 (file)
index 0000000..dc80947
--- /dev/null
@@ -0,0 +1,116 @@
+#include "util_random.h"
+#include "ustring.h"
+#include <boost/lexical_cast.hpp>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+static int  Inited = 0;
+static unsigned long  Seed;
+static ustring  RChar (CharConst ("ABCDEFGHJKLMNPQRTUVWXYZabcdefghijkmnopqrstuvwxyz23456789"));
+static ustring  SaltChar (CharConst ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"));
+
+static void  init () {
+    int  fd;
+
+    fd = open ("/dev/urandom", O_RDONLY);
+    if (fd < 0)
+       assert (0);
+    read (fd, &Seed, sizeof (Seed));
+    srandom (Seed);
+    Inited = 1;
+}
+
+static void  enc (unsigned long v, char* b) {
+    int  i;
+    int  n = RChar.size ();
+
+    for (i = 0; i < 5; i ++) {
+       b[i] = RChar[v % n];
+       v /= n;
+    }
+}
+
+#if 0
+ustring  randomKey () {
+    char  b[128];
+    ustring  ans;
+    ssize_t  s;
+
+    if (! Inited)
+       init ();
+    s = snprintf (b, 128, "%.8x%.8x%.8x%.8x", random (), random (), random (), random ());
+    ans = ustring (b, s);
+    return ans;
+}
+#endif
+#if 0
+ustring  smallRandomKey () {
+    char  b[128];
+    ustring  ans;
+    ssize_t  s;
+
+    if (! Inited)
+       init ();
+    s = snprintf (b, 128, "%.8x%.8x", random (), random ());
+    ans = ustring (b, s);
+    return ans;
+}
+#endif
+
+ustring  randomKey () {
+    char  b[32];
+
+    if (! Inited)
+       init ();
+    enc (random (), b);
+    enc (random (), b + 5);
+    enc (random (), b + 10);
+    enc (random (), b + 15);
+    return ustring (b, 20);
+}
+
+ustring  smallRandomKey () {
+    char  b[32];
+
+    if (! Inited)
+       init ();
+    enc (random (), b);
+    enc (random (), b + 5);
+    return ustring (b, 10);
+}
+
+ustring  randomKey (unsigned long n) {
+    char  b[32];
+    size_t  s;
+
+    s = snprintf (b, 32, "%.10lu", n);
+    return ustring (b, s) + randomKey ();
+}
+
+static void  encSalt (unsigned long v, char* b) {
+    int  i;
+    int  n = SaltChar.size ();
+
+    for (i = 0; i < 4; i ++) {
+       b[i] = SaltChar[v % n];
+       v /= n;
+    }
+}
+
+ustring  makeSalt () {
+    char  b[16];
+
+    if (! Inited)
+       init ();
+    b[0] = '$';
+    b[1] = '1';
+    b[2] = '$';
+    enc (random (), b + 3);
+    enc (random (), b + 7);
+    b[11] = '$';
+    return ustring (b, 12);
+}
diff --git a/lib/util_random.h b/lib/util_random.h
new file mode 100644 (file)
index 0000000..a57c5e6
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef UTIL_RANDOM_H
+#define UTIL_RANDOM_H
+
+#include "ustring.h"
+
+ustring  randomKey ();
+ustring  randomKey (unsigned long n);
+ustring  smallRandomKey ();
+ustring  makeSalt ();
+
+#endif /* UTIL_RANDOM_H */
diff --git a/lib/util_string.cc b/lib/util_string.cc
new file mode 100644 (file)
index 0000000..df88c95
--- /dev/null
@@ -0,0 +1,691 @@
+#include "util_string.h"
+#include "util_const.h"
+#include "util_random.h"
+#include "ustring.h"
+#include "utf8.h"
+#include "utf16.h"
+#include <boost/regex.hpp>
+#include <iconv.h>
+#include <vector>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+ustring  c3 (const ustring& str) {
+    bool  qsign = false;
+    static uregex  re ("^[0-9]+");
+    uiterator  b, e;
+    umatch  m;
+
+    b = str.begin ();
+    e = str.end ();
+    if (str[0] == '-' || str[0] == '+') {
+       qsign = true;
+       b = b + 1;
+    }
+    if (usearch (b, e, m, re)) {
+       int  n = m[0].second - m[0].first;
+       int  l = str.size () + n / 3;
+       ustring  ans;
+
+       ans.reserve (l);
+       if (qsign) {
+           ans.append (1, str[0]);
+       }
+       for (; b != m[0].second; b ++) {
+           ans.append (1, *b);
+           if (n > 1 && n % 3 == 1) {
+               ans.append (CharConst (","));
+           }
+           n --;
+       }
+       for (; b != e; b ++) {
+           ans.append (1, *b);
+       }
+       return ans;
+    } else {
+       return str;
+    }
+}
+
+static int  hex (char c) {
+    if ('0' <= c && c <= '9') {
+       return (c - '0');
+    } else if ('a' <= c && c <= 'f') {
+       return (c -  'a' + 10);
+    } else if ('A' <= c && c <= 'F') {
+       return (c - 'A' + 10);
+    } else {
+       return 0;
+    }
+}
+
+static int  hex (char c1, char c2) {
+    return (hex (c1) * 16 + hex (c2));
+}
+
+static char  hexchar (int c) {
+    if (0 <= c && c <= 9)
+       return '0' + c;
+    else if (10 <= c <= 15)
+       return 'a' - 10 + c;
+    else
+       return '0';
+}
+
+static ustring  percentHex (int c) {
+    ustring  ans (3, '%');
+
+    ans[1] = hexchar ((c >> 4) & 0x0f);
+    ans[2] = hexchar (c & 0x0f);
+    return ans;
+}
+
+ustring  urldecode_nonul (const ustring& str) {
+    ustring  ans;
+    static uregex  re ("(\\+)|%([0-9a-fA-F][0-9a-fA-F])|\\x00");
+    umatch  m;
+    uiterator  b, e;
+
+    ans.reserve (str.size ());
+    b = str.begin ();
+    e = str.end ();
+    while (usearch (b, e, m, re)) {
+       if (b != m[0].first) {
+           ans.append (b, m[0].first);
+       }
+       if (m[1].matched) {
+           ans.append (1, ' ');
+       } else if (m[2].matched) {
+           int  v = hex (*(m[2].first), *(m[2].first + 1));
+           if (v != 0) 
+               ans.append (1, v);
+       } else {
+       }
+       b = m[0].second;
+    }
+    if (b != e) {
+       ans.append (b, e);
+    }
+
+    return ans;
+}
+
+static ustring  omitPattern (const ustring& text, uregex& re) {
+    Splitter  sp (text, re);
+
+    if (sp.next ()) {
+       if (sp.match (0)) {
+           ustring  ans;
+           ans.reserve (text.length ());
+           if (sp.begin () != sp.end ())
+               ans.append (sp.begin (), sp.end ());
+           while (sp.next ()) {
+               if (sp.begin () != sp.end ())
+                   ans.append (sp.begin (), sp.end ());
+           }
+           return ans;
+       } else {
+           return text;
+       }
+    } else {
+       return text;
+    }
+}
+
+ustring  omitCtrl (const ustring& str) {
+    static uregex  re ("[\\x00-\\x1f\\x7f]+");
+    return omitPattern (str, re);
+}
+
+ustring  omitNL (const ustring& str) {
+    return omitPattern (str, re_nl);
+}
+
+ustring  omitNonAscii (const ustring& str) {
+    static uregex  re ("[^ -\\x7e]+");
+    return omitPattern (str, re);
+}
+
+ustring  omitNonAsciiWord (const ustring& str) {
+    static uregex  re ("[^\\x21-\\x7e]+");
+    return omitPattern (str, re);
+}
+
+bool  to_bool (const ustring& v) {
+    if (v.length () == 0 || (v.length () == 1 && v[0] == '0')) {
+       return false;
+    } else {
+       return true;
+    }
+}
+
+static ustring  percentEncode (const ustring& text, uregex& re) {
+    /* $1 -> _
+       $2 -> %HEX
+    */
+    umatch  m;
+    uiterator  b, e;
+    ustring  ans;
+
+    b = text.begin ();
+    e = text.end ();
+    if (b != e && usearch (b, e, m, re)) {
+       if (b != m[0].first) {
+           ans.append (ustring (b, m[0].first));
+       }
+       if (m[1].matched) {
+           ans.append (uUScore);
+       } else if (m[2].matched) {
+           ans.append (percentHex (*m[2].first));
+       } else {
+           assert (0);
+       }
+       b = m[0].second;
+       while (b != e && usearch (b, e, m, re)) {
+           if (b != m[0].first) {
+               ans.append (ustring (b, m[0].first));
+           }
+           if (m[1].matched) {
+               ans.append (uUScore);
+           } else if (m[2].matched) {
+               ans.append (percentHex (*m[2].first));
+           } else {
+               assert (0);
+           }
+           b = m[0].second;
+       }
+       if (b != e) {
+           ans.append (ustring (b, e));
+       }
+       return ans;
+    } else {
+       return text;
+    }
+}
+
+ustring  cookieEncode (const ustring& text) {
+    static uregex  re ("([\\x00-\\x1f\\x7f])|([ ,;%\\x80-\\xff])");
+
+    return percentEncode (text, re);
+}
+
+ustring  urlEncode (const ustring& url) {
+    static uregex  re ("(\\x00)|([^a-zA-Z0-9_.,/-])");
+    
+    return percentEncode (url, re);
+}
+
+ustring  cookieDecode (const ustring& text) {
+    umatch  m;
+    uiterator  b, e;
+    ustring  ans;
+    int  a;
+    static uregex  re ("%([0-9a-fA-F])([0-9a-fA-F])");
+
+    b = text.begin ();
+    e = text.end ();
+    while (usearch (b, e, m, re)) {
+       if (b != m[0].first)
+           ans.append (ustring (b, m[0].first));
+       a = hex (*m[1].first, *m[2].first);
+       ans.append (1, a);
+       b = m[0].second;
+    }
+    if (b != e)
+       ans.append (ustring (b, e));
+
+    return ans;
+}
+
+ustring  clipColon (const ustring& text) {
+    int  i;
+    ustring  ans (text);
+
+    for (i = 0; i < ans.size (); i ++) {
+       if (ans[i] == ':')
+           ans[i] = '_';
+    }
+    return ans;
+}
+
+ustring  dirPart (char* path) {
+    char*  e = rindex (path, '/');
+
+    if (e && e != path) {
+       return ustring (path, e - path);
+    } else {
+       return uSlash;
+    }
+}
+
+ustring  filePartOSSafe (const ustring& path) {
+    umatch  m;
+    static uregex  re ("[^\\\\/]+$");
+
+    if (usearch (path, m, re)) {
+       return ustring (m[0].first, m[0].second);
+    } else {
+       return uEmpty;
+    }
+}
+
+void  split (uiterator b, uiterator e, uregex& re, std::vector<ustring>& ans) {
+    Splitter  sp (b, e, re);
+
+    while (sp.next ()) {
+       ans.push_back (sp.cur ());
+    }
+}
+
+/*
+bool  splitChar (uiterator b, uiterator e, uiterator::value_type ch, uiterator& m1, uiterator& m2) {
+    for (; b < e; b ++) {
+       if (*b == ch) {
+           m2 = m1 = b;
+           m2 ++;
+           return true;
+       }
+    }
+    m1 = m2 = e;
+    return false;
+}
+*/
+
+static char  Base64Char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ustring  base64encode (uiterator b, uiterator e) {
+    ustring  ans;
+    size_t  size;
+    int  c0, c1, c2;
+
+    while (b != e) {
+       size = e - b;
+       if (size >= 3) {
+           c0 = *b ++;
+           c1 = *b ++;
+           c2 = *b ++;
+           ans.append (1, Base64Char[(c0 >> 2) & 0x3f]);
+           ans.append (1, Base64Char[((c0 & 0x03) << 4) | ((c1 >> 4) & 0x0f)]);
+           ans.append (1, Base64Char[((c1 & 0x0f) << 2) | ((c2 >> 6) & 0x03)]);
+           ans.append (1, Base64Char[c2 & 0x3f]);
+       } else if (size == 2) {
+           c0 = *b ++;
+           c1 = *b ++;
+           ans.append (1, Base64Char[(c0 >> 2) & 0x3f]);
+           ans.append (1, Base64Char[((c0 & 0x03) << 4) | ((c1 >> 4) & 0x0f)]);
+           ans.append (1, Base64Char[((c1 & 0x0f) << 2)]);
+           ans.append (1, '=');
+       } else if (size == 1) {
+           c0 = *b ++;
+           ans.append (1, Base64Char[(c0 >> 2) & 0x3f]);
+           ans.append (1, Base64Char[((c0 & 0x03) << 4)]);
+           ans.append (1, '=');
+           ans.append (1, '=');
+       } else {
+           break;
+       }
+    }
+    return ans;
+}
+
+ustring  escape_re (const ustring& text) {
+    ustring::const_iterator  b, e;
+    umatch  m;
+    ustring  ans;
+    int  c;
+    char  buf[4];
+    static uregex  re ("[^\\x01- !\"#%',/0-9:;<=>@A-Z_`a-z~\\x7f-\\xff-]");
+
+    buf[0] = '\\';
+    buf[1] = 'x';
+    ans.reserve (text.size () + 16);
+    b = text.begin ();
+    e = text.end ();
+    while (b != e && usearch (b, e, m, re)) {
+       if (b != m[0].first)
+           ans.append (b, m[0].first);
+       c = *m[0].first;
+       buf[2] = hexchar ((c >> 4) & 0x0f);
+       buf[3] = hexchar (c & 0x0f);
+       ans.append (buf, 4);
+       b = m[0].second;
+    }
+    if (b != e)
+       ans.append (b, e);
+    return ans;
+}
+
+ustring  slashEncode (const ustring& text) {
+    ustring::const_iterator  b, e;
+    umatch  m;
+    ustring  ans;
+    int  c;
+    char  buf[4];
+    static uregex  re ("([\\x00-\\x1f\\x7f])|(\\\\)|(\")");
+
+    buf[0] = '\\';
+    buf[1] = 'x';
+    b = text.begin ();
+    e = text.end ();
+    while (b != e && usearch (b, e, m, re)) {
+       if (b != m[0].first)
+           ans.append (b, m[0].first);
+       if (m[1].matched) {
+           c = *m[0].first;
+           switch (c) {
+           case '\t':
+               ans.append (CharConst ("\\t"));
+               break;
+           case '\r':
+               ans.append (CharConst ("\\r"));
+               break;
+           case '\n':
+               ans.append (CharConst ("\\n"));
+               break;
+           default:
+               buf[2] = hexchar ((c >> 4) & 0x0f);
+               buf[3] = hexchar (c & 0x0f);
+               ans.append (buf, 4);
+           }
+       } else if (m[2].matched) {
+           ans.append (CharConst ("\\\\"));
+       } else if (m[3].matched) {
+           ans.append (CharConst ("\\\""));
+       } else {
+           assert (0);
+       }
+       b = m[0].second;
+    }
+    if (b != e)
+       ans.append (b, e);
+    return ans;
+}
+
+ustring  slashDecode (const ustring& text) {
+    ustring::const_iterator  b, e;
+    umatch  m;
+    ustring  ans;
+    int  c;
+    static uregex  re ("\\\\([0-7][0-7][0-7]|[\\x00-\\x7f])");
+
+    b = text.begin ();
+    e = text.end ();
+    while (b != e && usearch (b, e, m, re)) {
+       if (b != m[0].first)
+           ans.append (b, m[0].first);
+       b = m[0].first + 1;
+       c = *b;
+       switch (c) {
+       case 't':
+           ans.append (CharConst ("\t"));
+           break;
+       case 'r':
+           ans.append (CharConst ("\r"));
+           break;
+       case 'n':
+           ans.append (CharConst ("\n"));
+           break;
+       default:
+           if (m[0].second - m[0].first == 4) {
+               c = (c - '0') * 64;
+               b ++;
+               c += (*b - '0') * 8;
+               b ++;
+               c += *b - '0';
+               if (0 < c && c < 0x20)
+                   ans.append (1, c);
+           } else {
+               ans.append (1, c);
+           }
+       }
+       b = m[0].second;
+    }
+    if (b != e)
+       ans.append (b, e);
+    return ans;
+}
+
+unsigned long  strtoul (const ustring& str) {
+    return strtoul (str.c_str (), NULL, 10);
+}
+
+unsigned long  strtoul (const uiterator& b) {
+    return strtoul (&*b, NULL, 10);
+}
+
+long  strtol (const ustring& str) {
+    return strtol (str.c_str (), NULL, 10);
+}
+
+bool  passMatch (const ustring& pass, const ustring& cpass) {
+    if (pass.length () == 0 || cpass.length () == 0)
+       return false;
+    return (strcmp (crypt (pass.c_str (), cpass.c_str ()), cpass.c_str ()) == 0);
+}
+
+ustring  passCrypt (const ustring& pass) {
+    ustring  salt = makeSalt ();
+    return ustring (crypt (pass.c_str (), salt.c_str ()));
+}
+
+size_t  strlength (const ustring& src) {
+    uiterator  b, e;
+    size_t  n = 0;
+    b = src.begin ();
+    e = src.end ();
+    while (b < e) {
+       n ++;
+       nextChar (b, e);
+    }
+    return n;
+}
+
+void  substring (const ustring& src, size_t idx, size_t len, int flen, ustring& ans) {
+    uiterator  b, e, t;
+    size_t  i;
+
+    b = src.begin ();
+    e = src.end ();
+    for (i = 0; i < idx && b < e; i ++)
+       nextChar (b, e);
+    if (flen) {
+       t = b;
+       for (i = 0; i < len && t < e; i ++)
+           nextChar (t, e);
+       ans.assign (b, t);
+    } else {
+       ans.assign (b, e);
+    }
+}
+
+ustring  utf16Encode (const ustring& str) {
+    int  i;
+    ustring  u, ans;
+    int  c;
+    char  b[8];
+
+    u = utf8to16 (str);
+    ans.reserve (u.size () * 3);
+    b[0] = '\\';
+    b[1] = 'u';
+    for (i = 0; i < u.size (); i += 2) {
+       c = u[i];
+       b[2] = hexchar ((c >> 4) & 0x0f);
+       b[3] = hexchar (c & 0x0f);
+       c = u[i + 1];
+       b[4] = hexchar ((c >> 4) & 0x0f);
+       b[5] = hexchar (c & 0x0f);
+       ans.append (b, 6);
+    }
+    return ans;
+}
+
+ustring  filenameEncode (const ustring& text) {
+    static uregex  re ("([\\x00-\\x1f\\x7f])|([^a-zA-Z0-9._-])|(^\\.+)");
+    Splitter  sp (text, re);
+    ustring  ans;
+    int  c;
+
+    if (text.length () == 0) {
+       throw (ustring (text).append (uErrorBadName));
+    }
+    ans.reserve (text.length () + 16);
+    while (sp.next ()) {
+       if (sp.begin () < sp.end ())
+           ans.append (sp.begin (), sp.end ());
+       if (sp.match (1)) {
+       } else if (sp.match (2)) {
+           c = *sp.matchBegin (2);
+           ans.append (1, ':');
+           ans.append (1, hexchar ((c >> 4) & 0x0f));
+           ans.append (1, hexchar (c & 0x0f));
+       } else if (sp.match (3)) {
+           for (c = sp.matchEnd (3) - sp.matchBegin (3); c > 0; c --) {
+               ans.append (CharConst (":2e"));
+           }
+       }
+    }
+    if (ans.length () > 250)
+       ans.resize (250);
+    return ans;
+}
+
+bool  matchSkip (uiterator& b, uiterator e, const char* t, size_t s) {
+    if (e - b >= s && memcmp (t, &b[0], s) == 0) {
+       b += s;
+       return true;
+    } else {
+       return false;
+    }
+}
+
+bool  matchHead (uiterator& b, uiterator e, const char* t, size_t s) {
+    if (e - b >= s && memcmp (t, &b[0], s) == 0) {
+       return true;
+    } else {
+       return false;
+    }
+}
+
+bool  match (uiterator b, uiterator e, const char* t, size_t s) {
+    if (e - b == s && memcmp (t, &b[0], s) == 0) {
+       return true;
+    } else {
+       return false;
+    }
+}
+
+bool  match (const ustring& str, const char* t, size_t s) {
+    if (str.length () == s && memcmp (t, str.data (), s) == 0) {
+       return true;
+    } else {
+       return false;
+    }
+}
+
+bool  match (uiterator b, uiterator e, const ustring& str) {
+    if (e - b == str.length () && memcmp (str.data (), &b[0], str.length ()) == 0) {
+       return true;
+    } else {
+       return false;
+    }
+}
+
+ustring  clipWhite (uiterator b, uiterator e) {
+    while (b < e)
+       if (isblank (*b)) {
+           b ++;
+       } else {
+           break;
+       }
+    while (b < e)
+       if (isblank (*(e - 1))) {
+           e --;
+       } else {
+           break;
+       }
+    return ustring (b, e);
+}
+
+ustring  getenvString (const char* key) {
+    char*  e = getenv (key);
+    if (e) {
+       return ustring (e);
+    } else {
+       return uEmpty;
+    }
+}
+
+ustring  zeroPad (int n, const ustring& src) {
+    int  m;
+
+    n = std::min (32, n);
+    m = n - src.length ();
+    if (m > 0) {
+       ustring  ans;
+       ans.reserve (m);
+       ans.append (m, '0');
+       ans.append (src);
+       return ans;
+    } else {
+       return src;
+    }
+}
+
+bool  wsearch (const ustring& text, boost::wsmatch& m, const ustring& reg, boost::wregex::flag_type reg_flags, boost::match_flag_type search_flags) {
+    std::wstring  wtext = utow (text);
+    std::wstring  wreg = utow (reg);
+    boost::wregex  re (wreg, reg_flags);
+    return regex_search (wtext, m, re, search_flags);
+}
+
+#if 0
+void  ellipsis (const ustring& src, size_t num, ustring& ans) {
+    size_t  i;
+    uiterator  b, e;
+
+    b = src.begin ();
+    e = src.end ();
+    for (i = 0; i < num && b < e; i ++) {
+       nextChar (b, e);
+    }
+    ans.assign (src.begin (), b);
+    if (b != e) {
+       ans.append (CharConst ("..."));
+    }
+}
+#endif
+
+ustring  uiconv (const ustring& src, const char* tocode, const char* fromcode) {
+    iconv_t  cd;
+    char  buf[4096];
+    const char*  ibuf;
+    char*  obuf;
+    size_t  isize, osize, rsize;
+    ustring  ans;
+
+    cd = iconv_open (tocode, fromcode);
+    if (cd == (iconv_t)(-1))
+       throw (ustring ("bad encoding name."));
+    ibuf = &src.at (0);
+    isize = src.size ();
+    while (isize > 0) {
+       obuf = buf;
+       osize = 4096;
+       rsize = iconv (cd, &ibuf, &isize, &obuf, &osize);
+//     if (rsize < 0)
+       if (obuf - buf <= 0)
+           break;
+       ans.append (buf, obuf - buf);
+    }
+    iconv_close (cd);
+    return ans;
+}
+
+ustring  padEmpty (const ustring& name) {
+    if (name.empty ())
+       return ustring (CharConst ("(null)"));
+    else
+       return name;
+}
diff --git a/lib/util_string.h b/lib/util_string.h
new file mode 100644 (file)
index 0000000..cc3f72d
--- /dev/null
@@ -0,0 +1,176 @@
+#ifndef UTIL_STRING_H
+#define UTIL_STRING_H
+
+#include "ustring.h"
+#include "utf16.h"
+#include <vector>
+
+class  Splitter {
+ public:
+    uregex*  re;
+    uiterator  b;
+    uiterator  t;
+    uiterator  u;
+    uiterator  e;
+    umatch  m;
+
+    Splitter (const ustring& text, uregex& r) {
+       b = t = u = text.begin ();
+       e = text.end ();
+       re = &r;
+    };
+    Splitter (uiterator pb, uiterator pe, uregex& r) {
+       b = t = u = pb;
+       e = pe;
+       re = &r;
+    };
+    virtual  ~Splitter () {};
+    virtual void  init (uiterator pb, uiterator pe) {
+       b = t = u = pb;
+       e = pe;
+    };
+    virtual bool  isEnd () {
+       return b == e;
+    };
+    virtual bool  next () {
+       b = u;
+       if (b != e) {
+           if (usearch (b, e, m, *re)) {
+               t = m[0].first;
+               u = m[0].second;
+           } else {
+               t = e;
+               u = e;
+           }
+           return true;
+       } else {
+           return false;
+       }
+    };
+    virtual bool  nextSep () {
+       b = u;
+       if (b != e) {
+           if (usearch (b, e, m, *re)) {
+               t = m[0].first;
+               u = m[0].second;
+               return true;
+           } else {
+               t = e;
+               u = e;
+               return false;
+           }
+       } else {
+           return false;
+       }
+    };
+    virtual uiterator  begin () {
+       return b;
+    };
+    virtual uiterator  end () {
+       return t;
+    };
+    virtual ustring  cur () {
+       return ustring (b, t);
+    };
+    virtual bool  match (int index) {
+       return (t != u && m[index].matched);
+    }
+    virtual uiterator  matchBegin () {
+       return t;
+    };
+    virtual uiterator  matchBegin (int index) {
+       return m[index].first;
+    };
+    virtual uiterator  matchEnd () {
+       return u;
+    };
+    virtual uiterator  matchEnd (int index) {
+       return m[index].second;
+    };
+    virtual uiterator  eol () {
+       return e;
+    };
+    virtual void  rewind (int i) {
+       int  n = u - t;
+       if (n > i) {
+           u -= i;
+       } else {
+           u -= n;
+       }
+    };
+};
+
+class  WSplitter {
+ public:
+    boost::wregex*  re;
+    std::wstring::const_iterator  b, t, u, e;
+    boost::wsmatch  m;
+
+    WSplitter (const std::wstring& text, boost::wregex& r) {
+       b = t = u = text.begin ();
+       e = text.end ();
+       re = &r;
+    };
+    virtual  ~WSplitter () {};
+    virtual bool  next () {
+       b = u;
+       if (b != e) {
+           if (regex_search (b, e, m, *re, boost::regex_constants::match_single_line)) {
+               t = m[0].first;
+               u = m[0].second;
+           } else {
+               t = e;
+               u = e;
+           }
+           return true;
+       } else {
+           return false;
+       }
+    };
+    virtual ustring  cur () {
+       std::wstring  x (b, t);
+       return wtou (x);
+    };
+};
+
+ustring  c3 (const ustring& str);
+ustring  urldecode_nonul (const ustring& str);
+ustring  omitCtrl (const ustring& str);
+ustring  omitNL (const ustring& str);
+ustring  omitNonAscii (const ustring& str);
+ustring  omitNonAsciiWord (const ustring& str);
+bool  to_bool (const ustring& v);
+ustring  urlEncode (const ustring& url);
+ustring  cookieEncode (const ustring& text);
+ustring  cookieDecode (const ustring& text);
+ustring  clipColon (const ustring& text);
+ustring  dirPart (char* path);
+ustring  filePartOSSafe (const ustring& path);
+void  split (uiterator b, uiterator e, uregex& re, std::vector<ustring>& ans);
+//bool  splitChar (uiterator& b, uiterator& e, uiterator::value_type ch, uiterator& m1, uiterator& m2);
+ustring  base64encode (uiterator b, uiterator e);
+ustring  filenameEncode (const ustring& text);
+ustring  escape_re (const ustring& text);
+ustring  slashEncode (const ustring& text);
+ustring  slashDecode (const ustring& text);
+unsigned long  strtoul (const ustring& str);
+unsigned long  strtoul (const uiterator& b);
+long  strtol (const ustring& str);
+bool  passMatch (const ustring& pass, const ustring& cpass);
+ustring  passCrypt (const ustring& pass);
+size_t  strlength (const ustring& src);
+void  substring (const ustring& src, size_t idx, size_t len, int flen, ustring& ans);
+ustring  utf16Encode (const ustring& str);
+bool  matchSkip (uiterator& b, uiterator e, const char* t, size_t s);
+bool  matchHead (uiterator& b, uiterator e, const char* t, size_t s);
+bool  match (uiterator b, uiterator e, const char* t, size_t s);
+bool  match (const ustring& str, const char* t, size_t s);
+bool  match (uiterator b, uiterator e, const ustring& str);
+ustring  clipWhite (uiterator b, uiterator e);
+ustring  getenvString (const char* key);
+ustring  zeroPad (int n, const ustring& src);
+bool  wsearch (const ustring& text, boost::wsmatch& m, const ustring& reg, boost::wregex::flag_type reg_flags = boost::regex_constants::normal, boost::match_flag_type search_flags = boost::regex_constants::match_single_line);
+ustring  uiconv (const ustring& src, const char* tocode, const char* fromcode);
+ustring  padEmpty (const ustring& name);
+
+#endif /* UTIL_STRING_H */
diff --git a/lib/util_time.cc b/lib/util_time.cc
new file mode 100644 (file)
index 0000000..3259a7f
--- /dev/null
@@ -0,0 +1,51 @@
+#include "util_time.h"
+#include "ustring.h"
+#include <time.h>
+
+ustring  strYMD (time_t t) {
+    struct tm  tv;
+
+    localtime_r (&t, &tv);
+    char  b[64];
+    size_t  s;
+
+    s = snprintf (b, 64, "%.4d%.2d%.2d", tv.tm_year + 1900, tv.tm_mon + 1, tv.tm_mday);
+    return ustring (b, s);
+}
+
+ustring  mailDate () {
+    size_t  s;
+    char  b[64];
+    time_t  t;
+    struct tm  tv;
+
+    t = now ();
+    localtime_r (&t, &tv);
+// Date: Sun, 04 Jan 2009 17:32:11 +0900
+    s = strftime (b, 64, "Date: %a, %d %b %Y %T %z\n", &tv);
+    return ustring (b, s);
+}
+
+time_t  now () {
+    return time (NULL);
+}
+
+ustring  dateCookie (time_t clock) {
+    /* Netscape Cookie expireフォーマット */
+    ustring  ans;
+    struct tm  t;
+    int  s;
+    static const char* const  w[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+    static const char* const  m[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+    gmtime_r (&clock, &t);
+//    t = *gmtime( &clock );
+    ans.reserve (64);
+    ans.resize (64);
+    s = snprintf (&ans[0], 64,
+                 "%s, %.2d-%s-%.4d %.2d:%.2d:%.2d GMT",
+                 w[t.tm_wday], t.tm_mday, m[t.tm_mon], t.tm_year + 1900,
+                 t.tm_hour, t.tm_min, t.tm_sec);
+    ans.resize (s);
+    return ans;
+}
diff --git a/lib/util_time.h b/lib/util_time.h
new file mode 100644 (file)
index 0000000..73cdc67
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef UTIL_TIME_H
+#define UTIL_TIME_H
+
+#include "ustring.h"
+#include <time.h>
+
+ustring  strYMD (time_t t);
+ustring  mailDate ();
+time_t  now ();
+ustring  dateCookie (time_t clock);
+
+#endif /* UTIL_TIME_H */
diff --git a/ml/Makefile b/ml/Makefile
new file mode 100644 (file)
index 0000000..265cc18
--- /dev/null
@@ -0,0 +1,120 @@
+.include "../Makefile.conf"
+
+.ifdef UTF8JP
+SRCS += form_utf8-jp.cc
+SRCS += iso2022jp.cc
+SRCS += motoroutput-jp.cc
+SRCS += utf8-jp.cc
+.endif
+
+SRCS += app.cc
+SRCS += expr.cc
+SRCS += form.cc
+SRCS += form_utf8.cc
+SRCS += formfile.cc
+SRCS += ftable.cc
+SRCS += http.cc
+SRCS += main.cc
+SRCS += ml.cc
+SRCS += mlenv.cc
+SRCS += ${MSRCS}
+SRCS += motor.cc
+SRCS += motorenv.cc
+SRCS += motorfunc.cc
+SRCS += motoroutput.cc
+SRCS += motorvar.cc
+SRCS += sigsafe.cc
+SRCS += utf8.cc
+SRCS += utf16.cc
+SRCS += util_apache.cc
+SRCS += util_check.cc
+SRCS += util_const.cc
+SRCS += util_file.cc
+SRCS += util_inet.cc
+SRCS += util_mimetype.cc
+SRCS += util_proc.cc
+SRCS += util_random.cc
+SRCS += util_string.cc
+SRCS += util_time.cc
+SRCS += wikicmd.cc
+SRCS += wikienv.cc
+SRCS += wikiformat.cc
+SRCS += wikiline.cc
+SRCS += wikitable.cc
+
+.PATH: ../ml ../lib ../modules ../wiki ../ext
+
+.ifdef DEBUG
+CFLAGS = -g
+CFLAGS += -DDEBUG
+.ifdef DEBUG2
+CFLAGS += -DDEBUG2
+.endif
+.ifdef HEAPDEBUG
+CFLAGS += -DHEAPDEBUG
+.ifdef HEAPDEBUG_VERBOSE
+CFLAGS += -DHEAPDEBUG_VERBOSE
+.endif
+.endif
+.else
+CFLAGS += -O2
+.endif
+#CFLAGS += -DGCC2
+.if defined (BSD)
+CFLAGS += -DHAVE_OPENLOCK
+.endif
+.ifdef UTF8JP
+CFLAGS += -DUTF8JP
+.endif
+CFLAGS += -I/usr/local/include
+.ifdef DEBUG
+CFLAGS += -I../ml_debug
+.endif
+CFLAGS += -I. -I.. -I../ml -I../lib -I../wiki -I../modules -I../ext
+
+LDADD += -L/usr/local/lib
+.ifdef HEAPDEBUG
+LDADD += /usr/local/lib/libboost_regex.a
+LDADD += heapdebug.o
+.else
+LDADD += -lboost_regex
+.endif
+LDADD += ${MLDADD}
+LDADD += -liconv
+LDADD += -lcrypt
+LDADD += -lstdc++
+LDADD += -pthread
+.ifndef PROG
+PROG = ml
+.endif
+NO_MAN = 1
+CLEANFILES += ftable.cc ml-id.h wikitable.cc heapdebug.o
+
+${PROG}: ftable.cc ml-id.h wikitable.cc
+.ifdef HEAPDEBUG
+${PROG}: heapdebug.o
+.endif
+
+dist: ${PROG}
+       rsync -avH ${PROG} ../dist/
+
+.PHONY: dist
+
+beforedepend:
+       ../bin/MKTABLE.pl
+       ../bin/MKWTABLE.pl
+       ../bin/MKID.pl
+
+ftable.cc: ${MSRCS}
+#ftable.cc:
+       ../bin/MKTABLE.pl
+
+wikitable.cc: wikicmd.cc wikiline.cc
+#wikitable.cc:
+       ../bin/MKWTABLE.pl
+
+ml-id.h: ${MSRCS}
+#ml-id.h:
+       ../bin/MKID.pl
+
+.include <bsd.prog.mk>
diff --git a/ml/main.cc b/ml/main.cc
new file mode 100644 (file)
index 0000000..e7ed0b4
--- /dev/null
@@ -0,0 +1,81 @@
+#include "heapdebug.h"
+#include "httpconst.h"
+#include "app.h"
+#include "motorenv.h"
+#include "motor.h"
+#ifdef UTF8JP
+#include "form_utf8-jp.h"
+#include "motoroutput-jp.h"
+#else
+#include "form_utf8.h"
+#include "motoroutput.h"
+#endif
+#include "util_check.h"
+#include "ustring.h"
+#include <iostream>
+#include <stdlib.h>
+#include <unistd.h>
+
+using namespace  std;
+
+int  main (int argc, char** argv) {
+    AppEnv  aenv;
+    CGIFormFile*  form = NULL;
+    HTMLMotor  motor;
+    MotorOutput*  out = NULL;
+#ifdef UTF8JP
+    if (checkAgent () & UA_Windows) {
+       form = new CGIFormUTF8JPMS;
+       out = new MotorOutputOStreamJPMS;
+    } else {
+//     form = new CGIFormUTF8;
+       form = new CGIFormUTF8JPMS;
+       out = new MotorOutputOStream;
+    }
+#else
+    form = new CGIFormUTF8;
+    out = new MotorOutputOStream;
+#endif
+    MotorEnv  env (&aenv, form, &motor, out);
+
+    try {
+       aenv.readOption (argc, argv, &env);
+
+       if (aenv.debugDump) {
+           close (2);
+           dup (1);
+           env.standardResponse (ustring (kMIME_TEXT), ustring (kCHARSET kCODE_UTF8));
+           cout.flush ();
+           cerr.flush ();
+       }
+
+#ifdef DEBUG
+       char*  em = getenv (kREQUEST_METHOD);
+       char*  en = getenv (kSCRIPT_NAME);
+       std::cerr << "============================================================\n";
+       if (em) {
+           std::cerr << em << " ";
+           if (en)
+               std::cerr << en;
+           std::cerr << "\n";
+       }
+       aenv.dump (std::cerr);
+       std::cerr << "--\n";
+#endif /* DEBUG */
+       aenv.setDefault ();
+
+       env.setDefault ();
+       env.readFormVar ();
+       env.cacheControl ();
+       env.doML ();
+       env.doMotor ();
+    }
+    catch (ustring& msg) {
+       std::cerr << msg << "\n";
+       goto Ex1;
+    }
+
+ Ex1:;
+    delete out;
+    delete form;
+}
diff --git a/ml_debug/Makefile b/ml_debug/Makefile
new file mode 100644 (file)
index 0000000..257c4bf
--- /dev/null
@@ -0,0 +1,6 @@
+DEBUG = 1
+#DEBUG2 = 1
+HEAPDEBUG = 1
+#HEAPDEBUG_VERBOSE = 1
+PROG = ml_debug
+.include "../ml/Makefile"
diff --git a/modules/ml-addon.cc b/modules/ml-addon.cc
new file mode 100644 (file)
index 0000000..e357aba
--- /dev/null
@@ -0,0 +1,254 @@
+#include "ml-addon.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "config.h"
+#include "motorconst.h"
+#include "motorenv.h"
+#include "util_const.h"
+#include "util_check.h"
+#include "util_string.h"
+#include "util_proc.h"
+#include "ustring.h"
+#include "expr.h"
+#include <vector>
+#include <exception>
+
+/*DOC:
+==add-on command==
+外部プログラムを呼び出す。
+
+*/
+
+typedef struct {
+    ProcRW  proc;
+    ustring  cmd;
+    std::vector<ustring>  par;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    MNode*  rest;
+    bool  xs;
+    ustring  type;
+}  AddonParams;
+
+static paramList  addonKwList[] = {
+    {CharConst ("xserial"), true},
+    {NULL, 0, 0}
+};
+
+static void  addon_sub2 (MlEnv* mlenv, AddonParams& o) {
+    int  i;
+
+    while (o.rest) {
+       o.par.push_back (omitNL (eval_str (o.rest->car (), mlenv)));
+       nextNode (o.rest);
+    }
+
+    if (o.cmd.size () == 0)
+       throw (uErrorCmdNameEmpty);
+    if (! checkFilename (o.cmd))
+       throw (o.cmd + uErrorBadCmd);
+    if (o.xs && mlenv->env->storedir.length () == 0)
+       throw (uErrorNoStore);
+
+    {
+       char*  argv[4];
+       ustring  cmd0 (CharConst (cDataTop kBin));
+
+       cmd0.append (o.cmd);
+       argv[0] = (char*)cmd0.c_str ();
+       argv[1] = NULL;
+
+       if (o.xs) {
+           argv[1] = (char*)mlenv->env->storedir.c_str ();
+           argv[2] = NULL;
+           o.proc.open (argv, &mlenv->env->storedir);
+       } else {
+           o.proc.open (argv);
+       }
+
+       for (i = 0; i < o.par.size (); i ++) {
+           o.proc.writeln (o.par[i]);
+#ifdef DEBUG2
+           std::cerr << i << ":" << o.par[i] << "\n";
+#endif /* DEBUG */
+       }
+       o.proc.closeWriter ();
+    }
+}
+
+/*DOC:
+===add-on===
+ (add-on [#xserial | :xserial BOOL] COMMAND [ARG1 ARG2...]) -> STRING
+
+===add-on-tab===
+ (add-on-tab [#xserial | :xserial BOOL] COMMAND [ARG1 ARG2...]) -> STRING-LIST
+
+*/
+
+//#AFUNC       add-on  ml_addon
+MNode*  ml_addon (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    AddonParams  o;
+
+    setParams (arg, 1, &o.params, addonKwList, &o.keywords, &o.rest);
+    o.cmd = eval_str (o.params[0], mlenv);
+    o.xs = eval_bool (o.keywords[0], mlenv);
+    addon_sub2 (mlenv, o);
+    ustring*  a = new ustring;
+    o.proc.read (*a);
+
+    return newMNode_str (a);
+}
+
+//#AFUNC       add-on-tab      ml_addon_tab
+MNode*  ml_addon_tab (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    AddonParams  o;
+
+    setParams (arg, 1, &o.params, addonKwList, &o.keywords, &o.rest);
+    o.cmd = eval_str (o.params[0], mlenv);
+    o.xs = eval_bool (o.keywords[0], mlenv);
+    addon_sub2 (mlenv, o);
+    {
+       ustring  a;
+       uiterator  b, e;
+       umatch  m;
+       MNodeList  ans;
+
+       o.proc.read (a);
+       b = a.begin ();
+       e = a.end ();
+       while (usearch (b, e, m, re_tab)) {
+           ans.append (newMNode_str (new ustring (b, m[0].first)));
+           b = m[0].second;
+       }
+       if (b != e) {
+           ans.append (newMNode_str (new ustring (b, e)));
+       }
+       return ans.release ();
+    }
+}
+
+/*DOC:
+===add-on-array-tab-nl===
+ (add-on-array-tab-nl VARIABLE_OR_ARRAY_LIST [#serial | :serial BOOL] COMMAND [ARG1 ARG2...]) -> NIL
+
+*/
+//#AFUNC       add-on-array-tab-nl     ml_addon_array_tab_nl
+MNode*  ml_addon_array_tab_nl (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  varlist;
+    std::vector<ustring>  vars;
+    AddonParams  o;
+    MNodePtr  h;
+    ustring  v;
+
+    setParams (arg, 2, &o.params, addonKwList, &o.keywords, &o.rest);
+    varlist = eval (o.params[0], mlenv);
+    o.cmd = eval_str (o.params[1], mlenv);
+    o.xs = eval_bool (o.keywords[0], mlenv);
+    if (! varlist ()->isCons ()) {
+       throw (varlist ()->dump_string () + uErrorBadType);
+    }
+    for (MNode* a = varlist (); a && a->isCons () && a->car (); nextNode (a)) {
+       v = a->car ()->to_string ();
+       if (checkAry (v))
+           vars.push_back (ustring (v.begin () + 1, v.end ()));
+       else
+           vars.push_back (v);
+    }
+    addon_sub2 (mlenv, o);
+    {
+       ustring  a, u;
+       uiterator  b, e;
+       umatch  m, m2;
+       size_t  n = 0;
+       int  i;
+
+       o.proc.read (a);
+       b = a.begin ();
+       e = a.end ();
+       while (usearch (b, e, m, re_lf)) {
+           n ++;
+           i = 0;
+           while (i < vars.size () && usearch (b, m[0].first, m2, re_tab)) {
+               h = newMNode_str (new ustring (b, m2[0].first));
+               mlenv->setAry (vars[i], n, h.p);
+               i ++;
+               b = m2[0].second;
+           }
+           if (i < vars.size () && b != m[0].first) {
+               h = newMNode_str (new ustring (b, m[0].first));
+               mlenv->setAry (vars[i], n, h.p);
+               i ++;
+           }
+           for (; i < vars.size (); i ++) {
+               mlenv->setAry (vars[i], n, NULL);
+           }
+           b = m[0].second;
+       }
+       if (b != e) {
+           n ++;
+           i = 0;
+           while (i < vars.size () && usearch (b, e, m2, re_tab)) {
+               h = newMNode_str (new ustring (b, m2[0].first));
+               mlenv->setAry (vars[i], n, h.p);
+               i ++;
+               b = m2[0].second;
+           }
+           if (i < vars.size () && b != e) {
+               h = newMNode_str (new ustring (b, e));
+               mlenv->setAry (vars[i], n, h.p);
+               i ++;
+           }
+           for (; i < vars.size (); i ++) {
+               mlenv->setAry (vars[i], n, NULL);
+           }
+
+       }
+       for (i = 0; i < vars.size (); i ++) {
+           mlenv->setArySize (vars[i], n);
+       }
+    }
+    return NULL;
+}
+
+/*DOC:
+===add-on-output===
+ (add-on-output [#xserial | :xserial BOOL] [:type MIME_TYPE] COMMAND [ARG1 ARG2...]) -> NIL
+
+*/
+//#AFUNC       add-on-output   ml_addon_output
+MNode*  ml_addon_output (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    AddonParams  o;
+    char*  b;
+    ssize_t  s;
+    static paramList  kwlist[] = {
+       {CharConst ("xserial"), true},
+       {CharConst ("type"), false},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 1, &o.params, kwlist, &o.keywords, &o.rest);
+    o.cmd = eval_str (o.params[0], mlenv);
+    o.xs = eval_bool (o.keywords[0], mlenv);
+    o.type = eval_asciiword (o.keywords[1], mlenv);
+    if (! checkMimeType (o.type))
+       o.type.resize (0);
+    addon_sub2 (mlenv, o);
+
+    if (o.type.length () > 0) {
+       mlenv->env->standardResponse (o.type);
+    } else {
+       static ustring  k (CharConst (kMIME_OCTET));
+       mlenv->env->standardResponse (k);
+    }
+    b = (char*)malloc (4096);
+    while ((s = o.proc.read (b, 4096)) > 0) {
+       std::cout.write (b, s);
+    }
+    free (b);
+
+    return NULL;
+}
diff --git a/modules/ml-addon.h b/modules/ml-addon.h
new file mode 100644 (file)
index 0000000..22dd367
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef ML_ADDON_H
+#define ML_ADDON_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_addon (MNode* cell, MlEnv* mlenv);
+MNode*  ml_addon_tab (MNode* cell, MlEnv* mlenv);
+MNode*  ml_addon_array_tab_nl (MNode* cell, MlEnv* mlenv);
+MNode*  ml_addon_output (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_ADDON_H */
diff --git a/modules/ml-apache.cc b/modules/ml-apache.cc
new file mode 100644 (file)
index 0000000..fc2bc0e
--- /dev/null
@@ -0,0 +1,211 @@
+#include "ml-apache.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "util_apache.h"
+#include "util_check.h"
+#include "util_const.h"
+#include "util_string.h"
+#include "httpconst.h"
+#include "expr.h"
+#include "ustring.h"
+#include <assert.h>
+#include <stdlib.h>
+
+/*DOC:
+==apache library==
+
+*/
+
+static MNode*  env_sub (MNode* cell, const char* name) {
+    if (cell->cdr ()) {
+       throw (uErrorWrongNumber);
+    } else {
+       char*  e = getenv (name);
+       
+       if (e) {
+           return newMNode_str (new ustring (e));
+       } else {
+           return NULL;
+       }
+    }
+}
+
+/*DOC:
+===server-name===
+ (server-name) -> STRING
+
+===remote-user===
+ (remote-user) -> STRING
+
+===referer===
+ (referer) -> STRING
+
+===apache-path-info===
+ (apache-path-info) -> STRING
+
+===apache-redirect-url===
+ (apache-redirect-url) -> STRING
+
+===remote-ip===
+ (remote-ip) -> STRING
+
+===request-method===
+ (request-method) -> STRING
+
+===apache-https===
+ (apache-https) -> STRING
+
+===ssl-client-m-serial===
+ (ssl-client-m-serial) -> STRING
+
+===ssl-client-s-dn===
+ (ssl-client-s-dn) -> STRING
+
+===ssl-client-i-dn===
+ (ssl-client-i-dn) -> STRING
+
+*/
+//#AFUNC       server-name     ml_server_name
+//#WIKIFUNC    server-name     ml_server_name
+//#AFUNC       remote-user     ml_remote_user
+//#WIKIFUNC    remote-user     ml_remote_user
+//#AFUNC       referer         ml_referer
+//#AFUNC       user-agent      ml_user_agent
+//#AFUNC       apache-path-info        ml_apache_path_info
+//#AFUNC       apache-script-name      ml_apache_script_name
+//#AFUNC       apache-redirect-url     ml_apache_redirect_url
+//#AFUNC       remote-ip       ml_remote_ip
+//#WIKIFUNC    remote-ip       ml_remote_ip
+//#AFUNC       request-method  ml_request_method
+//#WIKIFUNC    request-method  ml_request_method
+//#AFUNC       apache-https    ml_apache_https
+//#WIKIFUNC    apache-https    ml_apache_https
+//#AFUNC       ssl-client-m-serial     ml_ssl_client_m_serial
+//#AFUNC       ssl-client-s-dn         ml_ssl_client_s_dn
+//#AFUNC       ssl-client-i-dn         ml_ssl_client_i_dn
+MNode*  ml_server_name (MNode* cell, MlEnv* mlenv) {
+    return env_sub (cell, kSERVER_NAME);
+}
+MNode*  ml_remote_user (MNode* cell, MlEnv* mlenv) {
+    return env_sub (cell, kREMOTE_USER);
+}
+MNode*  ml_referer (MNode* cell, MlEnv* mlenv) {
+    return env_sub (cell, kHTTP_REFERER);
+}
+MNode*  ml_user_agent (MNode* cell, MlEnv* mlenv) {
+    return env_sub (cell, kHTTP_USER_AGENT);
+}
+MNode*  ml_apache_path_info (MNode* cell, MlEnv* mlenv) {
+    return env_sub (cell, kPATH_INFO);
+}
+MNode*  ml_apache_script_name (MNode* cell, MlEnv* mlenv) {
+    return env_sub (cell, kSCRIPT_NAME);
+}
+MNode*  ml_apache_redirect_url (MNode* cell, MlEnv* mlenv) {
+    return env_sub (cell, kREDIRECT_URL);
+}
+MNode*  ml_remote_ip (MNode* cell, MlEnv* mlenv) {
+    return env_sub (cell, kREMOTE_ADDR);
+}
+MNode*  ml_request_method (MNode* cell, MlEnv* mlenv) {
+    return env_sub (cell, kREQUEST_METHOD);
+}
+MNode*  ml_apache_https (MNode* cell, MlEnv* mlenv) {
+    return env_sub (cell, kHTTPS);
+}
+MNode*  ml_ssl_client_m_serial (MNode* cell, MlEnv* mlenv) {
+    return env_sub (cell, kSSL_CLIENT_M_SERIAL);
+}
+MNode*  ml_ssl_client_s_dn (MNode* cell, MlEnv* mlenv) {
+    return env_sub (cell, kSSL_CLIENT_S_DN);
+}
+MNode*  ml_ssl_client_i_dn (MNode* cell, MlEnv* mlenv) {
+    return env_sub (cell, kSSL_CLIENT_I_DN);
+}
+
+/*DOC:
+===absolute-url===
+ (absolute-url URL [#http | :http BOOL] [#https | :https BOOL] [:port NUM]) -> STRING
+
+*/
+//#AFUNC       absolute-url    ml_absolute_url
+MNode*  ml_absolute_url (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    ustring  url;
+    enum {
+       PROTO_HTTP,
+       PROTO_HTTPS,
+    }  proto;
+    int  port = 0;
+    ustring*  u;
+    int  n;
+    ustring*  ans;
+    static paramList  kwlist[] = {
+       {CharConst ("http"), true},
+       {CharConst ("https"), true},
+       {CharConst ("port"), false},
+       {NULL, 0, 0}
+    };
+
+    if (isHTTPS ())
+       proto = PROTO_HTTPS;
+    else
+       proto = PROTO_HTTP;
+
+    setParams (arg, 1, &params, kwlist, &keywords, NULL);
+    url = eval_text1 (params[0], mlenv);
+    if (eval_bool (keywords[0], mlenv))
+       proto = PROTO_HTTP;
+    if (eval_bool (keywords[1], mlenv))
+       proto = PROTO_HTTPS;
+    if (eval_bool (keywords[2], mlenv)) {
+       n = eval_int (keywords[2], mlenv);
+       if (1 <= n && n <= 65535)
+           port = n;
+    }
+
+    switch (proto) {
+    case PROTO_HTTP:
+       ans = new ustring (CharConst ("http://"));
+       break;
+    case PROTO_HTTPS:
+       ans = new ustring (CharConst ("https://"));
+       break;
+    default:
+       assert (0);
+    }
+    ans->append (getenvString (kSERVER_NAME));
+    if (port > 0) {
+       ans->append (uColon).append (boost::lexical_cast<ustring> (port));
+    }
+    ans->append (apacheAbsolutePath (url));
+
+    return newMNode_str (ans);
+}
+
+/*DOC:
+===read-cookie===
+ (read-cookie NAME) -> STRING
+
+*/
+//#AFUNC       read-cookie     ml_read_cookie
+MNode*  ml_read_cookie (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+    ustring  val;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    name = eval_text1 (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (mlenv->env && name.length () > 0) {
+       val = mlenv->env->http.readCookie (name);
+    }
+    return newMNode_str (new ustring (val));
+}
diff --git a/modules/ml-apache.h b/modules/ml-apache.h
new file mode 100644 (file)
index 0000000..1d69aaa
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef ML_APACHE_H
+#define ML_APACHE_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_server_name (MNode* cell, MlEnv* mlenv);
+MNode*  ml_remote_user (MNode* cell, MlEnv* mlenv);
+MNode*  ml_referer (MNode* cell, MlEnv* mlenv);
+MNode*  ml_user_agent (MNode* cell, MlEnv* mlenv);
+MNode*  ml_apache_path_info (MNode* cell, MlEnv* mlenv);
+MNode*  ml_apache_script_name (MNode* cell, MlEnv* mlenv);
+MNode*  ml_apache_redirect_url (MNode* cell, MlEnv* mlenv);
+MNode*  ml_remote_ip (MNode* cell, MlEnv* mlenv);
+MNode*  ml_request_method (MNode* cell, MlEnv* mlenv);
+MNode*  ml_apache_https (MNode* cell, MlEnv* mlenv);
+MNode*  ml_ssl_client_m_serial (MNode* cell, MlEnv* mlenv);
+MNode*  ml_ssl_client_s_dn (MNode* cell, MlEnv* mlenv);
+MNode*  ml_ssl_client_i_dn (MNode* cell, MlEnv* mlenv);
+MNode*  ml_absolute_url (MNode* cell, MlEnv* mlenv);
+MNode*  ml_read_cookie (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_APACHE_H */
diff --git a/modules/ml-bool.cc b/modules/ml-bool.cc
new file mode 100644 (file)
index 0000000..e91606b
--- /dev/null
@@ -0,0 +1,484 @@
+#include "ml-bool.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "util_const.h"
+#include "expr.h"
+#include <exception>
+
+/*DOC:
+==boolean operations==
+
+値をブール値として評価するとき,空文字列,文字列の「0」,数値の0,NILはFALSE,それ以外をTRUEとする。
+
+*/
+
+static inline bool  nullp (MNode* p) {
+    return (p == NULL || p->isNil ());
+}
+
+/*DOC:
+===null===
+ (null VALUE) -> 1 or NIL
+
+*/
+//#AFUNC       null    ml_null
+//#WIKIFUNC    null    ml_null
+MNode*  ml_null (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  a1;
+    bool  ans;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    a1 = eval (arg->car (), mlenv);
+    ans = nullp (a1 ());
+    nextNode (arg);
+    while (arg && ans) {
+       a1 = eval (arg->car (), mlenv);
+       nextNode (arg);
+       ans = nullp (a1 ());
+    }
+    return newMNode_bool (ans);
+}
+
+/*DOC:
+===not===
+ (not BOOL) -> 1 or NIL
+
+*/
+//#AFUNC       not     ml_not
+//#WIKIFUNC    not     ml_not
+MNode*  ml_not (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    bool  a1;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    a1 = eval_bool (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+    return newMNode_bool (! a1);
+}
+
+/*DOC:
+===and===
+ (and BOOL BOOL...) -> 1 or NIL
+
+パラメータを左から順に評価し,FALSEになると,以降は評価しない。
+
+*/
+//#AFUNC       and     ml_and
+//#WIKIFUNC    and     ml_and
+MNode*  ml_and (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    bool  a1;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    a1 = eval_bool (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg && a1) {
+       a1 &= eval_bool (arg->car (), mlenv);
+       nextNode (arg);
+    }
+    return newMNode_bool (a1);
+}
+
+/*DOC:
+===or===
+ (or BOOL BOOL...) -> 1 or NIL
+
+パラメータを左から順に評価し,TRUEになると,以降は評価しない。
+
+*/
+//#AFUNC       or      ml_or
+//#WIKIFUNC    or      ml_or
+MNode*  ml_or (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    bool  a1;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    a1 = eval_bool (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg && ! a1) {
+       a1 |= eval_bool (arg->car (), mlenv);
+       nextNode (arg);
+    }
+    return newMNode_bool (a1);
+}
+
+/*DOC:
+===xor===
+ (xor BOOL BOOL...) -> 1 or NIL
+
+*/
+//#AFUNC       xor     ml_xor
+//#WIKIFUNC    xor     ml_xor
+MNode*  ml_xor (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    bool  a1;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    a1 = eval_bool (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       a1 ^= eval_bool (arg->car (), mlenv);
+       nextNode (arg);
+    }
+    return newMNode_bool (a1);
+}
+
+/*DOC:
+=== = ===
+ (= NUMBER NUMBER...) -> 1 or NIL
+
+パラメータを数値として比較し,すべて一致すれば,1を返す。左から順に評価し,一致しないものがあれば,以降は評価しない。
+
+*/
+//#AFUNC       =       ml_neq
+//#WIKIFUNC    =       ml_neq
+MNode*  ml_neq (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  v1, v2;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    v1 = eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       v2 = eval_double (arg->car (), mlenv);
+       if (v1 == v2) {
+       } else {
+           return newMNode_bool (false);
+       }
+       nextNode (arg);
+    }
+    return newMNode_bool (true);
+}
+
+/*DOC:
+=== != ===
+ (!= NUMBER NUMBER) -> 1 or NIL
+
+numerical not equal
+
+*/
+//#AFUNC       !=      ml_nne
+//#WIKIFUNC    !=      ml_nne
+MNode*  ml_nne (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  v1, v2;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    v1 = eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    v2 = eval_double (arg->car (), mlenv);
+    nextNode (arg);
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    return newMNode_bool (v1 != v2);
+}
+
+/*DOC:
+=== > ===
+ (> NUMBER NUMBER...) -> 1 or NIL
+
+パラメータを数値として比較し,すべて右側の値が小さいとき,1を返す。左から順に評価し,右側が小さくないとき,以降は評価しない。
+
+*/
+//#AFUNC       >       ml_gt
+//#WIKIFUNC    >       ml_gt
+MNode*  ml_gt (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  v1, v2;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    v1 = eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       v2 = eval_double (arg->car (), mlenv);
+       if (v1 > v2) {
+           v1 = v2;
+       } else {
+           return newMNode_bool (false);
+       }
+       nextNode (arg);
+    }
+    return newMNode_bool (true);
+}
+
+/*DOC:
+=== < ===
+ (< NUMBER NUMBER...) -> 1 or NIL
+
+*/
+//#AFUNC       <       ml_lt
+//#WIKIFUNC    <       ml_lt
+MNode*  ml_lt (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  v1, v2;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    v1 = eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       v2 = eval_double (arg->car (), mlenv);
+       if (v1 < v2) {
+           v1 = v2;
+       } else {
+           return newMNode_bool (false);
+       }
+       nextNode (arg);
+    }
+    return newMNode_bool (true);
+}
+
+/*DOC:
+=== >= ===
+ (>= NUMBER NUMBER...) -> 1 or NIL
+
+*/
+//#AFUNC       >=      ml_ge
+//#WIKIFUNC    >=      ml_ge
+MNode*  ml_ge (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  v1, v2;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    v1 = eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       v2 = eval_double (arg->car (), mlenv);
+       if (v1 >= v2) {
+           v1 = v2;
+       } else {
+           return newMNode_bool (false);
+       }
+       nextNode (arg);
+    }
+    return newMNode_bool (true);
+}
+
+/*DOC:
+=== <= ===
+ (<= NUMBER NUMBER...) -> 1 or NIL
+
+*/
+//#AFUNC       <=      ml_le
+//#WIKIFUNC    <=      ml_le
+MNode*  ml_le (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  v1, v2;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    v1 = eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       v2 = eval_double (arg->car (), mlenv);
+       if (v1 <= v2) {
+           v1 = v2;
+       } else {
+           return newMNode_bool (false);
+       }
+       nextNode (arg);
+    }
+    return newMNode_bool (true);
+}
+
+/*DOC:
+===eq===
+ (eq TEXT TEXT...) -> 1 or NIL
+
+string equal
+
+*/
+//#AFUNC       eq      ml_eq
+//#WIKIFUNC    eq      ml_eq
+MNode*  ml_eq (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  v1, v2;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    v1 = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       v2 = eval_str (arg->car (), mlenv);
+       if (v1 == v2) {
+           v1 = v2;
+       } else {
+           return newMNode_bool (false);
+       }
+       nextNode (arg);
+    }
+    return newMNode_bool (true);
+}
+
+/*DOC:
+===ne===
+ (ne TEXT TEXT) -> 1 or NIL
+
+string not equal
+
+*/
+//#AFUNC       ne      ml_ne
+//#WIKIFUNC    ne      ml_ne
+MNode*  ml_ne (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  v1, v2;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    v1 = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (! arg)
+       throw (uErrorWrongNumber);
+    v2 = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    return newMNode_bool (v1 != v2);
+}
+
+/*DOC:
+===emptyp===
+ (emptyp TEXT...) -> 1 or NIL
+
+0-length string
+
+*/
+//#AFUNC       emptyp  ml_emptyp
+//#WIKIFUNC    emptyp  ml_emptyp
+MNode*  ml_emptyp (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  u;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    while (arg) {
+       u = eval_str (arg->car (), mlenv);
+       nextNode (arg);
+       if (u.size () > 0)
+           return newMNode_bool (false);
+    }
+    return newMNode_bool (true);
+}
+
+/*DOC:
+===not-emptyp===
+ (not-emptyp TEXT...) -> 1 or NIL
+
+non 0-length string
+
+*/
+//#AFUNC       not-emptyp      ml_not_emptyp
+//#WIKIFUNC    not-emptyp      ml_not_emptyp
+MNode*  ml_not_emptyp (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  u;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    while (arg) {
+       u = eval_str (arg->car (), mlenv);
+       nextNode (arg);
+       if (u.size () == 0)
+           return newMNode_bool (false);
+    }
+    return newMNode_bool (true);
+}
+
+/*DOC:
+===min===
+ (min NUMBER ...) -> NUMBER
+
+smallest number
+
+===max===
+ (max NUMBER ...) -> NUMBER
+
+largest number
+
+*/
+//#AFUNC       min     ml_min
+//#AFUNC       max     ml_max
+//#WIKIFUNC    min     ml_min
+//#WIKIFUNC    max     ml_max
+MNode*  ml_min (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  v;
+    double  ans;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    ans = eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       v = eval_double (arg->car (), mlenv);
+       nextNode (arg);
+       if (v < ans)
+           ans = v;
+    }
+    return newMNode_num (ans);
+}
+
+MNode*  ml_max (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  v;
+    double  ans;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    ans = eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       v = eval_double (arg->car (), mlenv);
+       nextNode (arg);
+       if (v > ans)
+           ans = v;
+    }
+    return newMNode_num (ans);
+}
+
+/*DOC:
+===error-flag===
+ (error-flag) -> BOOL
+
+*/
+//#AFUNC       error-flag      ml_error_flag
+//#WIKIFUNC    error-flag      ml_error_flag
+MNode*  ml_error_flag (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+
+    if (arg)
+       throw (uErrorWrongNumber);
+    return newMNode_bool (mlenv->env->errflag);
+}
diff --git a/modules/ml-bool.h b/modules/ml-bool.h
new file mode 100644 (file)
index 0000000..7b506cd
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef ML_BOOL_H
+#define ML_BOOL_H
+
+class MNode;
+class MlEnv;
+
+MNode*  ml_null (MNode* cell, MlEnv* mlenv);
+MNode*  ml_not (MNode* cell, MlEnv* mlenv);
+MNode*  ml_and (MNode* cell, MlEnv* mlenv);
+MNode*  ml_or (MNode* cell, MlEnv* mlenv);
+MNode*  ml_xor (MNode* cell, MlEnv* mlenv);
+MNode*  ml_neq (MNode* cell, MlEnv* mlenv);
+MNode*  ml_nne (MNode* cell, MlEnv* mlenv);
+MNode*  ml_gt (MNode* cell, MlEnv* mlenv);
+MNode*  ml_lt (MNode* cell, MlEnv* mlenv);
+MNode*  ml_ge (MNode* cell, MlEnv* mlenv);
+MNode*  ml_le (MNode* cell, MlEnv* mlenv);
+MNode*  ml_eq (MNode* cell, MlEnv* mlenv);
+MNode*  ml_ne (MNode* cell, MlEnv* mlenv);
+MNode*  ml_emptyp (MNode* cell, MlEnv* mlenv);
+MNode*  ml_not_emptyp (MNode* cell, MlEnv* mlenv);
+MNode*  ml_min (MNode* cell, MlEnv* mlenv);
+MNode*  ml_max (MNode* cell, MlEnv* mlenv);
+MNode*  ml_error_flag (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_BOOL_H */
diff --git a/modules/ml-cookielogin.cc b/modules/ml-cookielogin.cc
new file mode 100644 (file)
index 0000000..cc7e8dd
--- /dev/null
@@ -0,0 +1,441 @@
+#include "ml-cookielogin.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorconst.h"
+#include "motorenv.h"
+#include "expr.h"
+#include "http.h"
+#include "util_const.h"
+#include "util_check.h"
+#include "util_random.h"
+#include "util_string.h"
+#include "util_time.h"
+#include "sigsafe.h"
+#include "bdbmacro.h"
+#include "filemacro.h"
+#include <exception>
+
+/*DOC:
+==cookie authentication module==
+
+*/
+
+static MLCookieLogin*  objref (MlEnv* mlenv) {
+    assert (mlenv->module->id == cMLCookieLoginID);
+    return (MLCookieLogin*)mlenv->module;
+}
+
+/*
+  session record:
+  key: <SessionKey>
+  val: <ID>:<Limit>:<Avail>:<Group>:<IP>
+*/
+
+static void  splitRec (ustring& rec, ustring& id, ustring& limit, ustring& avail, ustring& group, ustring& ip) {
+    uiterator  b = rec.begin ();
+    uiterator  e = rec.end ();
+    umatch  m;
+
+    if (b == e || ! usearch (b, e, m, re_colon)) {
+       id.assign (b, e);
+       goto Ex2;
+    }
+    id.assign (b, m[0].first);
+    b = m[0].second;
+    if (b == e || ! usearch (b, e, m, re_colon)) {
+       limit.assign (b, e);
+       goto Ex3;
+    }
+    limit.assign (b, m[0].first);
+    b = m[0].second;
+    if (b == e || ! usearch (b, e, m, re_colon)) {
+       avail.assign (b, e);
+       goto Ex4;
+    }
+    avail.assign (b, m[0].first);
+    b = m[0].second;
+    if (b == e || ! usearch (b, e, m, re_colon)) {
+       group.assign (b, e);
+       goto Ex5;
+    }
+    group.assign (b, m[0].first);
+    b = m[0].second;
+    if (b == e || ! usearch (b, e, m, re_colon)) {
+       ip.assign (b, e);
+    } else {
+       ip.assign (b, m[0].first);
+    }
+    return;
+
+// Ex1:
+//    id.resize (0);
+ Ex2:
+    limit.resize (0);
+ Ex3:
+    avail.resize (0);
+ Ex4:
+    group.resize (0);
+ Ex5:
+    ip.resize (0);
+
+    return;
+}
+
+static int  login_check (MLCookieLogin* obj, ustring& key, ustring& rid) {
+    ustring  rec;
+    ustring  id, limit, avail, group, ip;
+    time_t  t;
+
+    if (key.length () == 0)
+       return 0;
+
+    if (obj->db.get (key, rec)) {
+       splitRec (rec, id, limit, avail, group, ip);
+       if (limit.size () > 0) { // type 1 record
+           t = strtol (limit);
+           if (id.size () > 0 && t >= now ()) {
+               rec = id;
+               rec.append (uColon).append (boost::lexical_cast<ustring> (now () + strtol (avail))).append (uColon).append (avail).append (uColon).append (group).append (uColon).append (ip);
+               obj->db.put (key, rec);
+               rid = id;
+               return 1;       // type 1 record
+           }
+       } else {
+           rid = id;
+           return 2;           // type 2 record
+       }
+    }
+    return 0;
+}
+
+void  MLCookieLogin::opendb () {
+    ustring  dbfile, lockfile;
+    
+    dbfile = ustring (dbpath) + kEXT_HASH;
+    lockfile = ustring (dbpath) + kEXT_LOCK;
+
+    lock.openAppendLock (lockfile.c_str ());
+    db.open (dbfile.c_str ());
+}
+
+void  MLCookieLogin::closedb () {
+    if (lock.isOpen ()) {
+       db.close ();
+       lock.close ();
+    }
+}
+
+
+/*DOC:
+===$login-cookie===
+ ($login-cookie DB SESSIONKEY SUBFUNCTION...) -> VALUE of last SUBFUNCTION
+
+SESSIONKEY, SESSIONKEY+"N"
+*/
+
+//#MFUNC       $login-cookie   ml_cookielogin
+MNode*  ml_cookielogin (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MLCookieLogin  obj;
+    ustring  name;
+    MNodePtr  ans;
+
+    mlenv->module = &obj;
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    name = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+
+    obj.sessionkey = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+
+    if (name.size () == 0)
+       throw (uErrorFilenameEmpty);
+    if (! checkName (name))
+       throw (name + uErrorBadName);
+    if (mlenv->env) {
+       SigSafe  sig;
+       obj.dbpath = mlenv->env->path_to_auth (name);
+
+       mlenv->moduleStatus = MlEnv::M_EXEC;
+//     obj.opendb (dbpath);
+       ans = progn (arg, mlenv);
+       if (mlenv->breaksym ()
+           && (mlenv->breaksym ()->isNil () || eq (mlenv->breaksym (), cell->car ()))) {
+           mlenv->breaksym = NULL;
+       }
+//     obj.closedb ();
+    }
+
+    return ans.release ();
+}
+
+/*DOC:
+===subfunctions of $login-cookie===
+
+*/
+/*DOC:
+====login====
+ (login UID AVAIL_SEC [:group GROUP] [:ip REMOTE] [:path PATH] [:domain DOMAIN] [:span TIME] [#secure | :secure BOOL]) -> NIL
+
+*/
+//#SFUNC       login   ml_cookielogin_login
+MNode*  ml_cookielogin_login (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MLCookieLogin*  obj = objref (mlenv);
+    ustring  id;
+    ustring  sessionval;
+    time_t  avail = 0;
+    time_t  limit;
+    ustring  group;
+    ustring  ip;
+    ustring  path;
+    ustring  domain;
+    time_t  span = 0;
+    bool  fsecure = false;
+    ustring  r;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    static paramList  kwlist[] = {
+       {CharConst ("group"), false},
+       {CharConst ("ip"), false},
+       {CharConst ("path"), false},
+       {CharConst ("domain"), false},
+       {CharConst ("span"), false},
+       {CharConst ("secure"), true},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 2, &params, kwlist, &keywords, NULL);
+    id = eval_text1 (params[0], mlenv);
+    avail = eval_int (params[1], mlenv);
+    if (avail < 0)
+       avail = 0;
+    group = eval_text1 (keywords[0], mlenv);
+    ip = eval_asciiword (keywords[1], mlenv);
+    path = eval_asciiword (keywords[2], mlenv);
+    domain = eval_asciiword (keywords[3], mlenv);
+    span = eval_int (keywords[4], mlenv);
+    if (span < 0)
+       span = 0;
+    fsecure = eval_bool (keywords[5], mlenv);
+
+    if (id.size () > 0) {
+       // write to the db
+       obj->opendb ();
+       r.append (clipColon (id)).append (uColon);
+       limit = now () + avail;
+       r.append (boost::lexical_cast<ustring> (limit)).append (uColon).append (boost::lexical_cast<ustring> (avail)).append (uColon).append (clipColon (group));
+       if (ip.size () > 0 && checkIP (ip)) {
+           r.append (uColon).append (ip);      // IP
+       } else {
+           r.append (uColon);
+       }
+       sessionval = randomKey ();
+       obj->db.put (sessionval, r);
+
+       // write a cookie
+       if (obj->sessionkey.size () > 0) {
+           if (fsecure) {
+               if (isHTTPS ()) {
+                   ustring  sessionval2 (randomKey ());
+                   ustring  key2 (obj->sessionkey);
+
+                   obj->db.put (sessionval2, sessionval);
+                   key2.append (CharConst ("N"));
+                   mlenv->env->http.setCookie (obj->sessionkey, sessionval, path, span, domain, true);
+                   mlenv->env->http.setCookie (key2, sessionval2, path, span, domain, false);
+               } else {
+                   mlenv->env->http.setCookie (obj->sessionkey, sessionval, path, span, domain, false);
+               }
+           } else {
+               mlenv->env->http.setCookie (obj->sessionkey, sessionval, path, span, domain, false);
+           }
+       }
+       obj->closedb ();
+    }
+
+    return NULL;
+}
+
+/*DOC:
+====logout====
+ (logout) -> NIL
+
+*/
+//#SFUNC       logout  ml_cookielogin_logout
+MNode*  ml_cookielogin_logout (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MLCookieLogin*  obj = objref (mlenv);
+    ustring  key, id;
+    int  rc;
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    key = mlenv->env->http.readCookie (obj->sessionkey);
+    obj->opendb ();
+    obj->db.del (key);
+    rc = login_check (obj, key, id);
+    if (rc == 2) {
+       obj->db.del (id);
+    }
+    obj->closedb ();
+
+    return NULL;
+}
+
+/*DOC:
+====check====
+ (check [#secure | :secure BOOL]) -> ID or NIL
+
+*/
+//#SFUNC       check   ml_cookielogin_check
+MNode*  ml_cookielogin_check (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MLCookieLogin*  obj = objref (mlenv);
+    bool  fsecure = false;
+    ustring*  u;
+    ustring  key;
+    ustring  id;
+    int  rc;
+    std::vector<MNode*>  keywords;
+    static paramList  kwlist[] = {
+       {CharConst ("secure"), true},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 0, NULL, kwlist, &keywords, NULL);
+    fsecure = eval_bool (keywords[0], mlenv);
+
+    key = mlenv->env->http.readCookie (obj->sessionkey);
+    obj->opendb ();
+    rc = login_check (obj, key, id);
+    switch (rc) {
+    case 1:
+       obj->closedb ();
+       return newMNode_str (new ustring (id));
+       break;
+    case 2:
+       rc = login_check (obj, id, id);
+       if (rc == 1) {
+           obj->closedb ();
+           return newMNode_str (new ustring (id));
+       } else {
+           obj->closedb ();
+           return NULL;
+       }
+       break;
+    }
+    if (fsecure && ! isHTTPS ()) {
+       ustring  key2 (obj->sessionkey);
+       key2.append (CharConst ("N"));
+       key = mlenv->env->http.readCookie (key2);
+       rc = login_check (obj, key, id);
+       if (rc == 2) {
+           rc = login_check (obj, id, id);
+           if (rc == 1) {
+               obj->closedb ();
+               return newMNode_str (new ustring (id));
+           } else {
+               obj->closedb ();
+               return NULL;
+           }
+       }
+    }
+    obj->closedb ();
+
+    return NULL;
+}
+
+/*DOC:
+====delete====
+ (delete ID) -> NIL
+
+*/
+//#SFUNC       delete  ml_cookielogin_delete
+MNode*  ml_cookielogin_delete (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MLCookieLogin*  obj = objref (mlenv);
+    ustring  key, val;
+    umatch  m;
+    ustring  id;
+    std::vector<ustring>  keys;
+    std::vector<ustring>::iterator  it;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    id = eval_text1 (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    obj->opendb ();
+    obj->db.initeach ();
+    while (obj->db.each (key, val)) {
+       if (usearch (val, m, re_colon)) {
+           if (match (val.begin (), m[0].first, id)) {
+               keys.push_back (key);
+           }
+       }
+    }
+    for (it = keys.begin (); it != keys.end (); it ++) {
+       obj->db.del (*it);
+    }
+    obj->closedb ();
+
+    return NULL;
+}
+
+/*DOC:
+====clear====
+ (clear) -> NIL
+
+*/
+//#SFUNC       clear   ml_cookielogin_clear
+MNode*  ml_cookielogin_clear (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MLCookieLogin*  obj = objref (mlenv);
+    ustring  key, val;
+    ustring  id, limit, avail, group, ip;
+    time_t  t;
+    time_t  tn = now ();
+    std::vector<ustring>  keys;
+    std::vector<ustring>::iterator  it;
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    obj->opendb ();
+    obj->db.initeach ();
+    while (obj->db.each (key, val)) {
+       splitRec (val, id, limit, avail, group, ip);
+       if (limit.length () > 0) {
+           t = strtol (limit);
+           if (t > 0 && t < tn) {
+               keys.push_back (key);
+           }
+       } else {
+           if (obj->db.get (id, val)) {
+               splitRec (val, val, limit, avail, group, ip);
+               if (limit.length () > 0) {
+                   t = strtol (limit);
+                   if (t > 0 && t < tn) {
+                       keys.push_back (key);
+                   }
+               }
+           } else {
+               keys.push_back (key);
+           }
+       }
+    }
+
+    for (it = keys.begin (); it != keys.end (); it ++) {
+       obj->db.del (*it);
+    }
+    obj->closedb ();
+
+    return NULL;
+}
+
diff --git a/modules/ml-cookielogin.h b/modules/ml-cookielogin.h
new file mode 100644 (file)
index 0000000..37a4e89
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef ML_COOKIELOGIN_H
+#define ML_COOKIELOGIN_H
+
+#include "ml.h"
+#include "ml-id.h"
+#include "ustring.h"
+#include "bdbmacro.h"
+#include "filemacro.h"
+
+class  MlEnv;
+class  MLCookieLogin: public MLFunc {
+ public:
+    BDBHash  db;
+    FileMacro  lock;
+    ustring  dbpath;
+    ustring  sessionkey;
+
+    MLCookieLogin (): MLFunc (cMLCookieLoginID) {};
+    virtual  ~MLCookieLogin () {
+       closedb ();
+    };
+
+//    virtual void  opendb (ustring& path);
+    virtual void  opendb ();
+    virtual void  closedb ();
+};
+
+MNode*  ml_cookielogin (MNode* cell, MlEnv* mlenv);
+MNode*  ml_cookielogin_login (MNode* cell, MlEnv* mlenv);
+MNode*  ml_cookielogin_login_dual (MNode* cell, MlEnv* mlenv);
+MNode*  ml_cookielogin_logout (MNode* cell, MlEnv* mlenv);
+MNode*  ml_cookielogin_check (MNode* cell, MlEnv* mlenv);
+MNode*  ml_cookielogin_delete (MNode* cell, MlEnv* mlenv);
+MNode*  ml_cookielogin_clear (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_COOKIELOGIN_H */
diff --git a/modules/ml-db.cc b/modules/ml-db.cc
new file mode 100644 (file)
index 0000000..ac68283
--- /dev/null
@@ -0,0 +1,482 @@
+#include "ml-db.h"
+#include "motorconst.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "ustring.h"
+#include "expr.h"
+#include "util_const.h"
+#include "util_check.h"
+#include "util_string.h"
+#include <vector>
+#include <exception>
+
+/*DOC:
+==db module==
+
+*/
+
+static MLDb*  objref (MlEnv* mlenv) {
+    assert (mlenv->module->id == cMLDbID);
+    return (MLDb*)mlenv->module;
+}
+
+void  MLDb::dbPath (const ustring& name, bool fxserial, ustring& dbpath, ustring& lockpath, MlEnv* mlenv) {
+    if (fxserial) {
+       dbpath = mlenv->env->path_store_file (name, kEXT_BTREE);
+       lockpath = mlenv->env->path_store_file (name, kEXT_LOCK);
+    } else {
+       dbpath = mlenv->env->path_db (name, kEXT_BTREE);
+       lockpath = mlenv->env->path_db (name, kEXT_LOCK);
+    }
+}
+
+void  MLDb::openRead (const ustring& name, bool fxserial, MlEnv* mlenv) {
+    ustring  dbpath;
+    ustring  lockpath;
+
+    dbPath (name, fxserial, dbpath, lockpath, mlenv);
+    lock.openReadLock (lockpath.c_str ());
+    db.openRead (dbpath.c_str ());
+}
+
+void  MLDb::openRW (const ustring& name, bool fxserial, MlEnv* mlenv) {
+    ustring  dbpath;
+    ustring  lockpath;
+
+    dbPath (name, fxserial, dbpath, lockpath, mlenv);
+    lock.openAppendLock (lockpath.c_str ());
+    db.openRW (dbpath.c_str ());
+}
+
+void  MLDb::closedb () {
+    db.close ();
+    lock.close ();
+}
+
+typedef enum {
+    FN_READ,
+    FN_RW,
+}  DBFUNC;
+
+static MNode*  ml_db_sub (MNode* cell, MlEnv* mlenv, DBFUNC fn) {
+    MNode*  arg = cell->cdr ();
+    MLDb  obj;
+    MNodePtr  ans;
+    ustring  name;
+    bool  fxserial = false;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    MNode*  rest;
+    static paramList  kwlist[] = {
+       {CharConst ("limit"), false},
+       {CharConst ("xserial"), true},
+       {NULL, 0, 0}
+    };
+
+    mlenv->module = &obj;
+    setParams (arg, 1, &params, kwlist, &keywords, &rest);
+    name = eval_str (params[0], mlenv);
+    obj.limit = eval_int (keywords[0], mlenv);
+    if (obj.limit <= 0)
+       obj.limit = 1;
+    if (obj.limit > kARRAYMAX)
+       obj.limit = kARRAYMAX;
+    fxserial = eval_bool (keywords[1], mlenv);
+
+    if (name.size () == 0)
+       throw (uErrorFilenameEmpty);
+    if (fxserial && mlenv->env->storedir.empty ())
+       throw (uErrorNoStore);
+
+    switch (fn) {
+    case FN_READ:
+       obj.openRead (name, fxserial, mlenv);
+       break;
+    case FN_RW:
+       obj.openRW (name, fxserial, mlenv);
+       break;
+    default:
+       assert (0);
+    }
+
+    mlenv->moduleStatus = MlEnv::M_EXEC;
+    ans = progn (rest, mlenv);
+    if (mlenv->breaksym ()
+       && (mlenv->breaksym ()->isNil () || eq (mlenv->breaksym (), cell->car ()))) {
+       mlenv->breaksym = NULL;
+    }
+    return mlenv->retval = ans ();
+}
+
+/*DOC:
+===$db-read===
+ ($db-read NAME [:limit NUM] [#xserial | :xserial BOOL] [SUBFUNCTION...])
+
+*/
+//#MFUNC       $db-read        ml_db_read
+MNode*  ml_db_read (MNode* cell, MlEnv* mlenv) {
+    return ml_db_sub (cell, mlenv, FN_READ);
+}
+
+/*DOC:
+===$db-rw===
+ ($db-rw NAME [:limit NUM] [:xserial] [SUBFUNCTION...])
+
+*/
+//#MFUNC       $db-rw  ml_db_rw
+MNode*  ml_db_rw (MNode* cell, MlEnv* mlenv) {
+    return ml_db_sub (cell, mlenv, FN_RW);
+}
+
+/*DOC:
+===subfunctions of $db-read and $db-rw===
+
+*/
+/*DOC:
+====read====
+ (read KEY) -> STRING-LIST or NIL
+
+*/
+//#SFUNC       read    ml_db_x_read    $db-read
+//#SFUNC       read    ml_db_x_read    $db-rw
+MNode*  ml_db_x_read (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MLDb*  obj = objref (mlenv);
+    ustring  key;
+    ustring  val;
+
+    if (!arg) 
+       throw (uErrorWrongNumber);
+    key = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg) 
+       throw (uErrorWrongNumber);
+
+    if (obj->db.get (key, val)) {
+       uiterator  b = val.begin ();
+       uiterator  e = val.end ();
+       uiterator  it;
+       MNodeList  ans;
+
+       for (it = b; ; it ++) {
+           if (*it == 0 || it == e) {
+               ans.append (newMNode_str (new ustring (b, it)));
+               b = it + 1;
+               if (it == e)
+                   break;
+           }
+       }
+       return ans.release ();
+    } else {
+       return NULL;
+    }
+}
+
+/*DOC:
+====write====
+ (write KEY VALUES...) -> NIL
+
+*/
+//#SFUNC       write   ml_db_x_write   $db-read
+//#SFUNC       write   ml_db_x_write   $db-rw
+MNode*  ml_db_x_write (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MLDb*  obj = objref (mlenv);
+    ustring  key;
+    std::vector<ustring>  values;
+    ustring  v;
+    size_t  size;
+    int  i;
+
+    if (!arg) 
+       throw (uErrorWrongNumber);
+    key = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    size = 0;
+    while (arg) {
+       values.push_back (eval_str (arg->car (), mlenv));
+       nextNode (arg);
+       size += values.back ().length () + 1;
+    }
+    v.reserve (size);
+    if (values.size () > 0) {
+       v.append (values[0]);
+       for (i = 1; i < values.size (); i ++) {
+           v.append (CharConst ("\0"));
+           v.append (values[i]);
+       }
+    }
+    obj->db.put (key, v);
+
+    return NULL;
+}
+
+static void  set_read_vars (ustring& val, std::vector<ustring>& vars, std::vector<int>& type, MlEnv* mlenv) {
+    if (vars.size () > 0) {
+       uiterator  b = val.begin ();
+       uiterator  e = val.end ();
+       uiterator  it;
+       int  i = 0;
+       for (it = b; ; it ++) {
+           if (it == e || *it == 0) {
+               mlenv->setVar (vars[i], newMNode_str (new ustring (b, it)));
+               b = it + 1;
+               i ++;
+               if (i == vars.size ())
+                   break;
+               if (it == e) {
+                   for (; i < vars.size (); i ++) {
+                       mlenv->setVar (vars[i], NULL);
+                   }
+                   break;
+               }
+           }
+       }
+    }
+}
+
+static void  set_read_arys (ustring& val, std::vector<ustring>& vars, std::vector<int>& type, MlEnv* mlenv) {
+    if (vars.size () > 0) {
+       uiterator  b = val.begin ();
+       uiterator  e = val.end ();
+       uiterator  it;
+       int  i = 0;
+       int  n = 0;
+       bool  af = false;
+       for (it = b; ; it ++) {
+           if (it == e || *it == 0) {
+               switch (type[i]) {
+               case 1: // variable
+                   mlenv->setVar (vars[i], newMNode_str (new ustring (b, it)));
+                   type[i] = 0;
+                   break;
+               case 2: // array
+                   if (! af) {
+                       n ++;
+                       af = true;
+                   }
+                   mlenv->setAry (vars[i], n, newMNode_str (new ustring (b, it)));
+                   break;
+               }
+               b = it + 1;
+               do {
+                   i ++;
+                   if (i == type.size ()) {
+                       i = 0;
+                       af = false;
+                   }
+               } while (type[i] == 0);
+               if (it == e)
+                   break;
+           }
+       }
+       if (af) {
+           for (; i < vars.size (); i ++) {
+               switch (type[i]) {
+               case 1: // variable
+                   mlenv->setVar (vars[i], NULL);
+                   break;
+               case 2: // array
+                   mlenv->setAry (vars[i], n, NULL);
+                   break;
+               }
+           }
+       }
+       for (i = 0; i < vars.size (); i ++) {
+           if (type[i] == 2) // array
+               mlenv->setArySize (vars[i], n);
+       }
+    }
+}
+
+/*DOC:
+====read-array====
+ (read-array KEY VARIABLE_OR_ARRAYVARIABLE...) -> BOOL
+
+*/
+//#SFUNC       read-array      ml_db_x_read_array      $db-read
+//#SFUNC       read-array      ml_db_x_read_array      $db-rw
+MNode*  ml_db_x_read_array (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MLDb*  obj = objref (mlenv);
+    ustring  key;
+    ustring  val;
+    ustring  v;
+    std::vector<ustring>  vars;
+    std::vector<int>  type;
+    int  s = 0;
+    bool  ans = false;
+
+    if (!arg) 
+       throw (uErrorWrongNumber);
+    key = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       v = eval_str (arg->car (), mlenv);
+       if (checkAry (v)) {     // array
+           vars.push_back (ustring (v.begin () + 1, v.end ()));
+           type.push_back (2); // array
+           s ++;
+       } else {
+           vars.push_back (v);
+           type.push_back (1); // variable
+       }
+       nextNode (arg);
+    }
+
+    if (obj->db.get (key, val))
+       ans = true;
+    if (s == 0) {
+       set_read_vars (val, vars, type, mlenv);
+    } else {
+       set_read_arys (val, vars, type, mlenv);
+    }
+
+    return newMNode_bool (ans);
+}
+
+/*DOC:
+====write-array====
+ (write-array KEY VARIABLE_OR_ARRAYVARIABLE...) -> NIL
+
+*/
+//#SFUNC       write-array     ml_db_x_write_array     $db-read
+//#SFUNC       write-array     ml_db_x_write_array     $db-rw
+MNode*  ml_db_x_write_array (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MLDb*  obj = objref (mlenv);
+    ustring  key;
+    ustring  val;
+    std::vector<ustring>  vars;
+    std::vector<int>  type;
+    int  i, j, n;
+    ustring  v;
+    int  s = 0;
+    bool  af = false;
+
+    if (!arg) 
+       throw (uErrorWrongNumber);
+    key = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       v = eval_str (arg->car (), mlenv);
+       if (checkAry (v)) {     // array
+           if (! af) {
+               i = vars.size ();
+               af = true;
+           }
+           vars.push_back (ustring (v.begin () + 1, v.end ()));
+           type.push_back (2); // array
+           s ++;
+       } else {                // variable
+           vars.push_back (v);
+           type.push_back (1); // variable
+       }
+       nextNode (arg);
+    }
+
+    if (vars.size () > 0) {
+       af = false;
+       if (s == 0)
+           n = 1;
+       else
+           n = mlenv->getArySize (vars[i]);
+       for (i = 1; i <= n; i ++) {
+           for (j = 0; j < vars.size (); j ++) {
+               switch (type[j]) {
+               case 1: // variable
+                   if (af)
+                       val.append (CharConst ("\0"));
+                   else
+                       af = true;
+                   val.append (to_string (mlenv->getVar (vars[j])));
+                   type[j] = 0;
+                   break;
+               case 2: // array
+                   if (af)
+                       val.append (CharConst ("\0"));
+                   else
+                       af = true;
+                   val.append (to_string (mlenv->getAry (vars[j], i)));
+                   break;
+               }
+           }
+       }
+    }
+    obj->db.put (key, val);
+
+    return NULL;
+}
+
+/*DOC:
+====select====
+ (select LAMBDA ARRAYVARIABLE...) -> BOOL
+
+*/
+//#SFUNC       select  ml_db_x_select  $db-read
+//#SFUNC       select  ml_db_x_select  $db-rw
+MNode*  ml_db_x_select (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MLDb*  obj = objref (mlenv);
+    MNodePtr  lambda;
+    std::vector<ustring>  vars;
+    ustring  v;
+    bool  ans = false;
+    ustring  key, val;
+    MNodePtr  h;
+    MNodePtr  ag;
+    int  i, n;
+
+    if (!arg) 
+       throw (uErrorWrongNumber);
+    lambda = eval (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       v = eval_str (arg->car (), mlenv);
+       if (checkAry (v)) {     // array
+           vars.push_back (ustring (v.begin () + 1, v.end ()));
+       } else {
+           vars.push_back (v);
+       }
+       nextNode (arg);
+    }
+
+    obj->db.initeach ();
+    ag = new MNode;
+    n = 0;
+    while (obj->db.each (key, val)) {
+       ag ()->set_car (newMNode_str (new ustring (key)));
+       h = execDefun (mlenv, lambda (), ag ());
+       ag ()->unset_car ();
+       if (to_bool (h ())) {
+           ans = true;
+           if (vars.size () > 0) {
+               uiterator  b = val.begin ();
+               uiterator  e = val.end ();
+               uiterator  it;
+               
+               i = 0;
+               n ++;
+               mlenv->setAry (vars[i], n, newMNode_str (new ustring (key)));
+               i ++;
+               if (i < vars.size ()) {
+                   for (it = b; ; it ++) {
+                       if (*it == 0 || it == e) {
+                           mlenv->setAry (vars[i], n, newMNode_str (new ustring (b, it)));
+                           b = it + 1;
+                           i ++;
+                           if (it == e || i == vars.size ())
+                               break;
+                       }
+                   }
+                   for (; i < vars.size (); i ++) {
+                       mlenv->setAry (vars[i], n, NULL);
+                   }
+               }
+           }
+       }
+    }
+
+    return newMNode_bool (ans);
+}
diff --git a/modules/ml-db.h b/modules/ml-db.h
new file mode 100644 (file)
index 0000000..4224d09
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef ML_DB_H
+#define ML_DB_H
+
+#include "ml.h"
+#include "ml-id.h"
+#include "ustring.h"
+#include "bdbmacro.h"
+#include "filemacro.h"
+
+class  MNode;
+class  MlEnv;
+
+class  MLDb: public MLFunc {
+ public:
+    BDBBtree  db;
+    FileMacro  lock;
+//    ustring  dbpath;
+    int  limit;
+
+    MLDb (): MLFunc (cMLDbID) {
+       limit = 10000;
+    };
+    virtual  ~MLDb () {
+       closedb ();
+    };
+    virtual void  dbPath (const ustring& name, bool fxserial, ustring& dbpath, ustring& lockpath, MlEnv* mlenv);
+    virtual void  openRead (const ustring& name, bool fxserial, MlEnv* mlenv);
+    virtual void  openRW (const ustring& name, bool fxserial, MlEnv* mlenv);
+    virtual void  closedb ();
+};
+
+MNode*  ml_db_read (MNode* cell, MlEnv* mlenv);
+MNode*  ml_db_rw (MNode* cell, MlEnv* mlenv);
+MNode*  ml_db_x_read (MNode* cell, MlEnv* mlenv);
+MNode*  ml_db_x_write (MNode* cell, MlEnv* mlenv);
+MNode*  ml_db_x_read_array (MNode* cell, MlEnv* mlenv);
+MNode*  ml_db_x_write_array (MNode* cell, MlEnv* mlenv);
+MNode*  ml_db_x_select (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_DB_H */
diff --git a/modules/ml-defun.cc b/modules/ml-defun.cc
new file mode 100644 (file)
index 0000000..3afb779
--- /dev/null
@@ -0,0 +1,60 @@
+#include "ml-defun.h"
+#include "motorconst.h"
+#include "expr.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "util_const.h"
+#include "ustring.h"
+#include <vector>
+#include <exception>
+
+/*DOC:
+==function definition==
+
+*/
+/*DOC:
+===defun===
+ (defun NAME LIST BLOCK...) -> NIL
+
+*/
+//#AFUNC       defun   ml_defun
+MNode*  ml_defun (MNode* cell, MlEnv* mlenv) {
+    MNode*  sexp = NULL;
+    ustring  name;
+
+    checkDefun (cell->cdr (), name, sexp);
+    mlenv->setVar (name, newLambda (sexp));
+    return NULL;
+}
+
+/*DOC:
+===defun-motor===
+ (defun-motor NAME LIST BLOCK...) -> NIL
+
+*/
+//#AFUNC       defun-motor     ml_defun_motor
+MNode*  ml_defun_motor (MNode* cell, MlEnv* mlenv) {
+    MNode*  sexp = NULL;
+    ustring  name;
+
+    checkDefun (cell->cdr (), name, sexp);
+    mlenv->env->motorCall.setVar (name, newLambda (sexp));
+    return NULL;
+}
+
+/*DOC:
+===lambda===
+ (lambda LIST BLOCK...) -> LAMBDA
+
+*/
+//#AFUNC       lambda  ml_lambda
+//#WIKIFUNC    lambda  ml_lambda
+MNode*  ml_lambda (MNode* cell, MlEnv* mlenv) {
+    MNode*  sexp = NULL;
+    ustring  name;
+
+    checkDefun (cell, name, sexp);
+    return cell;
+}
+
diff --git a/modules/ml-defun.h b/modules/ml-defun.h
new file mode 100644 (file)
index 0000000..c14f508
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef ML_DEFUN_H
+#define ML_DEFUN_H
+
+class  MlEnv;
+class  MNode;
+
+MNode*  ml_defun (MNode* cell, MlEnv* mlenv);
+MNode*  ml_defun_motor (MNode* cell, MlEnv* mlenv);
+MNode*  ml_lambda (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_DEFUN_H */
diff --git a/modules/ml-encode.cc b/modules/ml-encode.cc
new file mode 100644 (file)
index 0000000..e09d5cd
--- /dev/null
@@ -0,0 +1,98 @@
+#include "ml-encode.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "motoroutput.h"
+#include "expr.h"
+#include "util_const.h"
+#include "util_string.h"
+#include "utf8.h"
+#include "ustring.h"
+//#include <boost/ptr_container/ptr_vector.hpp>
+#include <vector>
+#include <exception>
+
+/*DOC:
+==encoding functions==
+
+*/
+/*DOC:
+===js===
+ (js STRING...) -> STRING
+
+*/
+//#AFUNC       js      ml_js
+//#WIKIFUNC    js      ml_js
+MNode*  ml_js (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  str;
+
+    while (arg) {
+       str.append (eval_str (arg->car (), mlenv));
+       nextNode (arg);
+    }
+
+    str = utf16Encode (str);
+    return newMNode_str (new ustring (str));
+}
+
+/*DOC:
+===escape-regexp===
+ (escape-regexp STRING...) -> STRING
+
+*/
+//#AFUNC       escape-regexp   ml_escape_regexp
+//#WIKIFUNC    escape-regexp   ml_escape_regexp
+MNode*  ml_escape_regexp (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  str;
+    ustring  ans;
+
+    while (arg) {
+       str.append (eval_str (arg->car (), mlenv));
+       nextNode (arg);
+    }
+
+    ans = escape_re (str);
+    return newMNode_str (new ustring (ans));
+}
+
+/*DOC:
+===encode-control-char===
+ (encode-control-char STRING...) -> STRING
+
+制御文字,「\(バックスラッシュ)」,「"(ダブルクオート)」をバックスラッシュでエンコードする。
+
+*/
+//#AFUNC       encode-control-char     ml_encode_control_char
+//#WIKIFUNC    encode-control-char     ml_encode_control_char
+MNode*  ml_encode_control_char (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  str;
+
+    while (arg) {
+       str.append (eval_str (arg->car (), mlenv));
+       nextNode (arg);
+    }
+
+    return newMNode_str (new ustring (slashEncode (str)));
+}
+
+/*DOC:
+===decode-control-char===
+ (decode-control-char STRING...) -> STRING
+
+*/
+//#AFUNC       decode-control-char     ml_decode_control_char
+//#WIKIFUNC    decode-control-char     ml_decode_control_char
+MNode*  ml_decode_control_char (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  str;
+
+    while (arg) {
+       str.append (eval_str (arg->car (), mlenv));
+       nextNode (arg);
+    }
+
+    return newMNode_str (new ustring (slashDecode (str)));
+}
diff --git a/modules/ml-encode.h b/modules/ml-encode.h
new file mode 100644 (file)
index 0000000..4e686dc
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef ML_ENCODE_H
+#define ML_ENCODE_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_js (MNode* cell, MlEnv* mlenv);
+MNode*  ml_escape_regexp (MNode* cell, MlEnv* mlenv);
+MNode*  ml_encode_control_char (MNode* cell, MlEnv* mlenv);
+MNode*  ml_decode_control_char (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_ENCODE_H */
diff --git a/modules/ml-formvar.cc b/modules/ml-formvar.cc
new file mode 100644 (file)
index 0000000..e1ab238
--- /dev/null
@@ -0,0 +1,448 @@
+#include "ml-formvar.h"
+#include "ml-store.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "formfile.h"
+#include "motorenv.h"
+#include "util_string.h"
+#include "util_const.h"
+#include "util_check.h"
+#include "expr.h"
+#include "utf8.h"
+#include <boost/lexical_cast.hpp>
+#include <exception>
+#include <iostream>
+#include <assert.h>
+
+/*DOC:
+==form variable manipulation==
+
+*/
+/*DOC:
+===input-text===
+ (input-text VARIABLE_or_ARRAY [:max NUM] [#nowhite | #nw | :nowhite BOOL | :nw BOOL] [:filter REGEX] [:error-filter LAMBDA]) -> NIL
+
+===input-textarea===
+ (input-textarea VARIABLE_or_ARRAY [:max NUM] [#nowhite | #nw | :nowhite BOOL | :nw BOOL] [:filter REGEX] [:error-filter LAMBDA]) -> NIL
+
+===input-int===
+ (input-int VARIABLE_or_ARRAY [:max NUM] [#nowhite | #nw | :nowhite BOOL | :nw BOOL] [:filter REGEX] [:error-filter LAMBDA]) -> NIL
+
+===input-real===
+ (input-real VARIABLE_or_ARRAY [:max NUM] [#nowhite | #nw | :nowhite BOOL | :nw BOOL] [:filter REGEX] [:error-filter LAMBDA]) -> NIL
+
+===input-int-or-blank===
+ (input-int-or-blank VARIABLE_or_ARRAY [:max NUM] [#nowhite | #nw | :nowhite BOOL | :nw BOOL] [:filter REGEX] [:error-filter LAMBDA]) -> NIL
+
+===input-real-or-blank===
+ (input-real-or-blank VARIABLE_or_ARRAY [:max NUM] [#nowhite | #nw | :nowhite BOOL | :nw BOOL] [:filter REGEX] [:error-filter LAMBDA]) -> NIL
+
+===input-ascii===
+ (input-ascii VARIABLE_or_ARRAY [:max NUM] [#nowhite | #nw | :nowhite BOOL | :nw BOOL] [:filter REGEX] [:error-filter LAMBDA]) -> NIL
+
+===input-bool===
+ (input-bool VARIABLE_or_ARRAY [:max NUM] [#nowhite | #nw | :nowhite BOOL | :nw BOOL] [:filter REGEX] [:error-filter LAMBDA]) -> NIL
+
+*/
+
+class  opt_t {
+  public:
+    bool  ftextarea;
+    bool  nowhite;
+    size_t  max;
+    ustring  filter;
+    MNodePtr  errFilter;
+
+    opt_t () {
+       ftextarea = false;
+       nowhite = false;
+       max = 0;
+    };
+    virtual  ~opt_t () {};
+};
+
+static MNode*  formvar_to_text1 (ustring& val) {
+    return newMNode_str (new ustring (omitCtrl (val)));
+}
+
+static MNode*  formvar_to_ascii (ustring& val) {
+    return newMNode_str (new ustring (omitNonAscii (val)));
+}
+
+static MNode*  formvar_to_text (ustring& val) {
+    return newMNode_str (new ustring (val));
+}
+
+static MNode*  formvar_to_int (ustring& val) {
+    long  v = atol (val.c_str ());
+    return newMNode_num (v);
+}
+
+static MNode*  formvar_to_int_blank (ustring& val) {
+    if (val.length () > 0) {
+       long  v = atol (val.c_str ());
+       return newMNode_num (v);
+    } else {
+       return NULL;
+    }
+}
+
+static MNode*  formvar_to_double (ustring& val) {
+    double  v = atof (val.c_str ());
+    return newMNode_num (v);
+}
+
+static MNode*  formvar_to_double_blank (ustring& val) {
+    if (val.length () > 0) {
+       double  v = atof (val.c_str ());
+       return newMNode_num (v);
+    } else {
+       return NULL;
+    }
+}
+
+static MNode*  formvar_to_bool (ustring& val) {
+    return newMNode_bool (to_bool (val));
+}
+
+static void  opt_filter (const ustring& name, ustring& val, opt_t& opt, MlEnv* mlenv) {
+    if (opt.filter.size () > 0) {
+       boost::wsmatch  m;
+       if (wsearch (val, m, opt.filter)) {
+           val = wtou (std::wstring (m[0].first, m[0].second));
+       } else {
+           val.resize (0);
+       }
+    }
+    if (opt.nowhite) {
+       if (opt.ftextarea) {
+           clipNLEnd (val);
+       } else {
+           clipWhiteEnd (val);
+       }
+    }
+    if (opt.max > 0) {
+       substring (val, 0, opt.max, true, val);
+    }
+    if (opt.errFilter ()) {
+       MNodePtr  ag;
+       MNodePtr  v;
+       ag = new MNode;
+       ag ()->set_car (newMNode_str (new ustring (val)));
+       v = execDefun (mlenv, opt.errFilter (), ag ());
+       if (to_bool (v ())) {
+           mlenv->env->setErrorVar (name);
+       }
+    }
+}
+
+#if 0
+static void  formvar_input_readopt_opt (MNode*& arg, MlEnv* mlenv, opt_t& opt) {
+    ustring*  u;
+
+    for (; arg; nextNode (arg)) {
+       if (! arg->car ()->isSym ())
+           break;
+       u = arg->car ()->sym;
+       if (match (*u, CharConst (":max"))) {
+           nextNodeNonNil (arg);
+           opt.max = eval_int (arg->car (), mlenv);
+       } else if (match (*u, CharConst (":nowhite")) || match (*u, CharConst (":nw"))) {
+           opt.nowhite = true;
+       } else if (match (*u, CharConst (":filter"))) {
+           nextNodeNonNil (arg);
+           opt.filter.assign (eval_str (arg->car (), mlenv));
+       } else if (match (*u, CharConst (":error-filter"))) {
+           MNodePtr  f;
+           ustring  n;
+           MNode*  p = NULL;
+           nextNodeNonNil (arg);
+           f = eval (arg->car (), mlenv);
+           nextNode (arg);
+           checkDefun (f (), n, p);
+           if (n == uLambda) {
+//             opt.errFilter = p;
+               opt.errFilter = f ();
+           }
+       } else {
+           break;
+       }
+    }
+
+    if (arg)
+       throw (uErrorWrongNumber);
+}
+#endif
+
+static void  formvar_input_readopt (MNode*& arg, MlEnv* mlenv, ustring& name, opt_t& opt) {
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    static paramList  kwlist[] = {
+       {CharConst ("max"), false},
+       {CharConst ("nowhite"), true},
+       {CharConst ("nw"), true},
+       {CharConst ("filter"), false},
+       {CharConst ("error-filter"), false},
+       {NULL, 0, 0}
+    };
+    
+    setParams (arg, 1, &params, kwlist, &keywords, NULL);
+    name = eval_str (params[0], mlenv);
+    opt.max = eval_int (keywords[0], mlenv);
+    if (opt.max < 0)
+       opt.max = 0;
+    opt.nowhite = eval_bool (keywords[1], mlenv) || eval_bool (keywords[2], mlenv);
+    if (keywords[3])
+       opt.filter.assign (eval_str (keywords[3], mlenv));
+    if (keywords[4]) {
+       MNodePtr  f;
+       ustring  n;
+       MNode*  p = NULL;
+
+       f = eval (keywords[4], mlenv);
+       checkDefun (f (), n, p);
+       if (n == uLambda) {
+           opt.errFilter = f ();
+       }
+    }
+
+    if (name.size () == 0)
+       throw (uErrorVarNameEmpty);
+}
+
+static void  formvar_input_do (MlEnv* mlenv, ustring& name, opt_t& opt, MNode* (*fn)(ustring&)) {
+    MNodePtr  h;
+    ustring  val;
+
+    if (checkAry (name)) {
+       ustring  aname;
+       size_t  i, n;
+
+       aname = ustring (name.begin () + 1, name.end ());
+       n = mlenv->env->form->atSize (aname);
+       for (i = 0; i < n; i ++) {
+           mlenv->env->form->at (aname, i, val);
+           opt_filter (aname, val, opt, mlenv);
+           mlenv->setAry (aname, i + 1, (*fn) (val));
+       }
+       mlenv->setArySize (aname, n);
+    } else {
+       mlenv->env->form->at (name, val);
+       opt_filter (name, val, opt, mlenv);
+       mlenv->setVar (name, (*fn) (val));
+    }
+}
+
+//#AFUNC       input-text      ml_formvar_input_text
+//#WIKIFUNC    input-text      ml_formvar_input_text
+MNode*  ml_formvar_input_text (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+    opt_t  opt;
+
+    formvar_input_readopt (arg, mlenv, name, opt);
+    formvar_input_do (mlenv, name, opt, formvar_to_text1);
+
+    return NULL;
+}
+
+//#AFUNC       input-textarea  ml_formvar_input_textarea
+//#WIKIFUNC    input-textarea  ml_formvar_input_textarea
+MNode*  ml_formvar_input_textarea (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+    opt_t  opt;
+
+    formvar_input_readopt (arg, mlenv, name, opt);
+    opt.ftextarea = true;
+    formvar_input_do (mlenv, name, opt, formvar_to_text);
+
+    return NULL;
+}
+
+//#AFUNC       input-int       ml_formvar_input_int
+//#WIKIFUNC    input-int       ml_formvar_input_int
+MNode*  ml_formvar_input_int (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+    opt_t  opt;
+
+    formvar_input_readopt (arg, mlenv, name, opt);
+    formvar_input_do (mlenv, name, opt, formvar_to_int);
+
+    return NULL;
+}
+
+//#AFUNC       input-real      ml_formvar_input_real
+//#WIKIFUNC    input-real      ml_formvar_input_real
+MNode*  ml_formvar_input_real (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+    opt_t  opt;
+
+    formvar_input_readopt (arg, mlenv, name, opt);
+    formvar_input_do (mlenv, name, opt, formvar_to_double);
+
+    return NULL;
+}
+
+//#AFUNC       input-int-or-blank      ml_formvar_input_int_blank
+//#WIKIFUNC    input-int-or-blank      ml_formvar_input_int_blank
+MNode*  ml_formvar_input_int_blank (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+    opt_t  opt;
+
+    formvar_input_readopt (arg, mlenv, name, opt);
+    formvar_input_do (mlenv, name, opt, formvar_to_int_blank);
+
+    return NULL;
+}
+
+//#AFUNC       input-real-or-blank     ml_formvar_input_real_blank
+//#WIKIFUNC    input-real-or-blank     ml_formvar_input_real_blank
+MNode*  ml_formvar_input_real_blank (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+    opt_t  opt;
+
+    formvar_input_readopt (arg, mlenv, name, opt);
+    formvar_input_do (mlenv, name, opt, formvar_to_double_blank);
+
+    return NULL;
+}
+
+//#AFUNC       input-ascii     ml_formvar_input_ascii
+//#WIKIFUNC    input-ascii     ml_formvar_input_ascii
+MNode*  ml_formvar_input_ascii (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+    opt_t  opt;
+
+    formvar_input_readopt (arg, mlenv, name, opt);
+    formvar_input_do (mlenv, name, opt, formvar_to_ascii);
+
+    return NULL;
+}
+
+//#AFUNC       input-bool      ml_formvar_input_bool
+//#WIKIFUNC    input-bool      ml_formvar_input_bool
+MNode*  ml_formvar_input_bool (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+    opt_t  opt;
+
+    formvar_input_readopt (arg, mlenv, name, opt);
+    formvar_input_do (mlenv, name, opt, formvar_to_bool);
+
+    return NULL;
+}
+
+/*DOC:
+===input-file===
+ (input-file VARIABLE STORE-FILENAME [:max NUM] [:filter REGEX] [:error-filter LAMBDA]) -> NIL
+
+*/
+//#AFUNC       input-file      ml_formvar_input_file
+MNode*  ml_formvar_input_file (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  var;
+    ustring  fname;
+    opt_t  opt;
+    ustring  val;
+    ustring  tgt;
+    ustring  filename;
+    int  i;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    static paramList  kwlist[] = {
+       {CharConst ("max"), false},
+       {CharConst ("filter"), false},
+       {CharConst ("error-filter"), false},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 2, &params, kwlist, &keywords, NULL);
+    var = eval_str (params[0], mlenv);
+    fname = eval_str (params[1], mlenv);
+    opt.max = eval_int (keywords[0], mlenv);
+    if (opt.max < 0)
+       opt.max = 0;
+    if (keywords[1])
+       opt.filter.assign (eval_str (keywords[1], mlenv));
+    if (keywords[2]) {
+       MNodePtr  f;
+       ustring  n;
+       MNode*  p = NULL;
+
+       f = eval (keywords[2], mlenv);
+       checkDefun (f (), n, p);
+       if (n == uLambda) {
+           opt.errFilter = f ();
+       }
+    }
+
+    if (mlenv->env->storedir.empty ()) {
+       newStoreSerial (mlenv);
+    }
+    mlenv->env->form->at (var, val);
+    if (val.size () > 0) {
+       i = strtoul (val);
+       tgt = mlenv->env->path_store_file (fname);
+       if (mlenv->env->form->readFilename (i, filename)) {
+           if (opt.filter.size () > 0) {
+               boost::wsmatch  m;
+               if (wsearch (filename, m, opt.filter)) {
+                   filename.assign (m[0].first, m[0].second);
+               } else {
+                   filename.resize (0);
+               }
+           }
+           if (opt.errFilter ()) {
+               MNodePtr  ag;
+               MNodePtr  v;
+               ag = new MNode;
+               ag ()->set_car (newMNode_str (new ustring (filename)));
+               v = execDefun (mlenv, opt.errFilter (), ag ());
+               if (to_bool (v ())) {
+                   mlenv->env->setErrorVar (var);
+                   filename.resize (0);
+               }
+           }
+
+           if (filename.length () > 0) {
+               mlenv->env->form->saveFile (i, tgt, opt.max);
+#ifdef DEBUG
+               if (mlenv->log) {
+                   *mlenv->log << "    [file: \"" << fname << "\"]\n";
+               }
+#endif /* DEBUG */
+               mlenv->setVar (var, newMNode_str (new ustring (filename)));
+           }
+       }
+    } else {
+       mlenv->setVar (var, NULL);
+    }
+    return NULL;
+}
+
+/*DOC:
+===read-query-string===
+ (read-query-string) -> NIL
+
+*/
+//#AFUNC       read-query-string       ml_read_query_string
+MNode*  ml_read_query_string (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (mlenv->env && mlenv->env->form) {
+       if (mlenv->env->form->method != CGIForm::M_GET) {
+           mlenv->env->form->read_get ();
+       }
+    }
+
+    return NULL;
+}
diff --git a/modules/ml-formvar.h b/modules/ml-formvar.h
new file mode 100644 (file)
index 0000000..0523be2
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef ML_FORMVAR_H
+#define ML_FORMVAR_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_formvar_input_text (MNode* cell, MlEnv* mlenv);
+MNode*  ml_formvar_input_textarea (MNode* cell, MlEnv* mlenv);
+MNode*  ml_formvar_input_int (MNode* cell, MlEnv* mlenv);
+MNode*  ml_formvar_input_real (MNode* cell, MlEnv* mlenv);
+MNode*  ml_formvar_input_int_blank (MNode* cell, MlEnv* mlenv);
+MNode*  ml_formvar_input_real_blank (MNode* cell, MlEnv* mlenv);
+MNode*  ml_formvar_input_ascii (MNode* cell, MlEnv* mlenv);
+MNode*  ml_formvar_input_bool (MNode* cell, MlEnv* mlenv);
+MNode*  ml_formvar_input_file (MNode* cell, MlEnv* mlenv);
+MNode*  ml_read_query_string (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_FORMVAR_H */
diff --git a/modules/ml-include.cc b/modules/ml-include.cc
new file mode 100644 (file)
index 0000000..bb399f7
--- /dev/null
@@ -0,0 +1,61 @@
+#include "ml-include.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "expr.h"
+#include "util_const.h"
+#include "util_check.h"
+#include "util_file.h"
+#include "ustring.h"
+#include <exception>
+
+/*DOC:
+==MiniLisp file reading function==
+
+*/
+/*DOC:
+===include===
+ (include FILENAME...)
+
+*/
+//#AFUNC       include ml_include
+MNode*  ml_include (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  file, f;
+    MNodePtr  ans;
+
+    while (arg) {
+       file = eval_str (arg->car (), mlenv);
+       nextNode (arg);
+       if (mlenv->env->path_resource (file, f)) {
+           ustring  b;
+           bool  rc;
+
+           rc = readFile (f, b);
+           if (rc) {
+               MotorSexp  ml (mlenv);
+               ml.scan (b);
+#ifdef DEBUG2
+               ml.top.dump (std::cerr, false);
+               std::cerr << "\n";
+#endif /* DEBUG */
+               mlenv->inclIncCount ();
+               if (ml.top.isCons ()) {
+                   ans = progn (&ml.top, mlenv);
+                   if (mlenv->breaksym ()) {
+                       if (mlenv->breaksym ()->isNil () || eq (mlenv->breaksym (), cell->car ()))
+                           mlenv->breaksym = NULL;
+                       arg = NULL;
+                   }
+               }
+               mlenv->declIncCount ();
+           } else {
+               throw (file + uErrorNotFound);
+           }
+       } else {
+           throw (file + uErrorNotFound);
+       }
+    }
+
+    return ans.release ();
+}
diff --git a/modules/ml-include.h b/modules/ml-include.h
new file mode 100644 (file)
index 0000000..9a01a62
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef ML_INCLUDE_H
+#define ML_INCLUDE_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_include (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_INCLUDE_H */
diff --git a/modules/ml-inet.cc b/modules/ml-inet.cc
new file mode 100644 (file)
index 0000000..6dd90d4
--- /dev/null
@@ -0,0 +1,33 @@
+#include "ml-inet.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "expr.h"
+#include "util_const.h"
+#include "util_inet.h"
+#include "ustring.h"
+#include <exception>
+
+/*DOC:
+==INET function==
+
+*/
+/*DOC:
+===gethostname===
+ (gethostname IPADDR) -> HOSTNAME
+
+*/
+//#AFUNC       gethostname     ml_gethostname
+MNode*  ml_gethostname (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  ip;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    ip = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    return newMNode_str (new ustring (getnameinfo (ip)));
+}
diff --git a/modules/ml-inet.h b/modules/ml-inet.h
new file mode 100644 (file)
index 0000000..d47a641
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef ML_INET_H
+#define ML_INET_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_gethostname (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_INET_H */
diff --git a/modules/ml-math.cc b/modules/ml-math.cc
new file mode 100644 (file)
index 0000000..cc87250
--- /dev/null
@@ -0,0 +1,182 @@
+#include "ml-math.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "expr.h"
+#include "util_const.h"
+#include <exception>
+#include <math.h>
+
+/*DOC:
+==mathematical function==
+
+*/
+
+static double  arg1_double (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  ans;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    ans = eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+    return ans;
+}
+
+/*DOC:
+=== + ===
+ (+ NUMBER NUMBER...) -> NUMBER
+
+*/
+//#AFUNC       +       ml_add
+//#WIKIFUNC    +       ml_add
+MNode*  ml_add (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  a1 = 0.;
+
+    while (arg) {
+       a1 += eval_double (arg->car (), mlenv);
+       nextNode (arg);
+    }
+    return newMNode_num (a1);
+}
+
+/*DOC:
+=== - ===
+ (- NUMBER) -> NUMBER
+ (- NUMBER NUMBER) -> NUMBER
+
+*/
+//#AFUNC       -       ml_sub
+//#WIKIFUNC    -       ml_sub
+MNode*  ml_sub (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  a1;
+    double  a2;
+
+    if (arg == NULL)
+       throw (uErrorWrongNumber);
+
+    a1 = eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg) {                 // (- NUM1 NUM2)
+       a2 = eval_double (arg->car (), mlenv);
+       nextNode (arg);
+       if (arg)
+           throw (uErrorWrongNumber);
+       return newMNode_num (a1 - a2);
+    } else {                   // (- NUM1)
+       return newMNode_num (- a1);
+    }
+}
+
+/*DOC:
+=== * ===
+ (* NUMBER NUMBER...) -> NUMBER
+
+*/
+//#AFUNC       *       ml_mult
+//#WIKIFUNC    *       ml_mult
+MNode*  ml_mult (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  a1 = 1.;
+
+    if (arg == NULL)
+       throw (uErrorWrongNumber);
+
+    a1 = eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       a1 *= eval_double (arg->car (), mlenv);
+       nextNode (arg);
+    }
+    return newMNode_num (a1);
+}
+
+/*DOC:
+=== / ===
+ (/ NUMBER NUMBER) -> NUMBER
+
+*/
+//#AFUNC       /       ml_div
+//#WIKIFUNC    /       ml_div
+MNode*  ml_div (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  a1;
+    double  a2;
+
+    if (arg == NULL)
+       throw (uErrorWrongNumber);
+
+    a1 = eval_double (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    a2 = eval_double (arg->car (), mlenv);
+    if (a2 == 0.) {
+       throw (uErrorDiv0);
+    }
+
+    return newMNode_num (a1 / a2);
+}
+
+/*DOC:
+=== % ===
+ (% NUMBER NUMBER) -> NUMBER
+
+*/
+//#AFUNC       %       ml_mod
+//#WIKIFUNC    %       ml_mod
+MNode*  ml_mod (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  a1;
+    double  a2;
+
+    if (arg == NULL)
+       throw (uErrorWrongNumber);
+
+    a1 = eval_double (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    a2 = eval_double (arg->car (), mlenv);
+    if (a2 == 0.) {
+       throw (uErrorDiv0);
+    }
+
+    return newMNode_num (fmod (a1, a2));
+}
+
+/*DOC:
+===ceil===
+ (ceil NUMBER) -> NUMBER
+
+*/
+//#AFUNC       ceil    ml_ceil
+//#WIKIFUNC    ceil    ml_ceil
+MNode*  ml_ceil (MNode* cell, MlEnv* mlenv) {
+    double  v = arg1_double (cell, mlenv);
+    return newMNode_num (ceil (v));
+}
+
+/*DOC:
+===floor===
+ (floor NUMBER) -> NUMBER
+
+*/
+//#AFUNC       floor   ml_floor
+//#WIKIFUNC    floor   ml_floor
+MNode*  ml_floor (MNode* cell, MlEnv* mlenv) {
+    double  v = arg1_double (cell, mlenv);
+    return newMNode_num (floor (v));
+}
+
+/*DOC:
+===fraction===
+ (fraction NUMBER) -> NUMBER
+
+*/
+//#AFUNC       fraction        ml_fraction
+//#WIKIFUNC    fraction        ml_fraction
+MNode*  ml_fraction (MNode* cell, MlEnv* mlenv) {
+    double  v = arg1_double (cell, mlenv);
+    return newMNode_num (v - floor (v));
+}
diff --git a/modules/ml-math.h b/modules/ml-math.h
new file mode 100644 (file)
index 0000000..31f99a4
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef ML_MATH_H
+#define ML_MATH_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_add (MNode* cell, MlEnv* mlenv);
+MNode*  ml_sub (MNode* cell, MlEnv* mlenv);
+MNode*  ml_mult (MNode* cell, MlEnv* mlenv);
+MNode*  ml_div (MNode* cell, MlEnv* mlenv);
+MNode*  ml_mod (MNode* cell, MlEnv* mlenv);
+MNode*  ml_ceil (MNode* cell, MlEnv* mlenv);
+MNode*  ml_floor (MNode* cell, MlEnv* mlenv);
+MNode*  ml_fraction (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_MATH_H */
diff --git a/modules/ml-motor.cc b/modules/ml-motor.cc
new file mode 100644 (file)
index 0000000..b661e7f
--- /dev/null
@@ -0,0 +1,280 @@
+#include "ml-motor.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "motoroutput.h"
+#include "expr.h"
+#include "util_const.h"
+#include "util_check.h"
+#include "util_string.h"
+#include "ustring.h"
+
+/*DOC:
+==HTMLMotor function==
+
+*/
+
+static void  location_sub (MNode* arg, ustring& url, MlEnv* mlenv) {
+    MNodePtr  p;
+    MNode*  a;
+    ustring  str;
+    int  c;
+    umatch  m;
+    static uregex  re ("^[a-z]{1,6}://[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*(:[0-9]{1,5})?/");
+
+    if (!arg)
+       throw (uErrorWrongNumber);
+
+    url = eval_text1 (arg->car (), mlenv);
+    if (usearch (url, m, re)) {
+       uiterator  e = url.end ();
+       str = ustring (m[0].second, e);
+       url = ustring (m[0].first, m[0].second) + urlEncode (str);
+    } else {
+       url = urlEncode (url);
+    }
+    nextNode (arg);
+
+    c = 0;
+    while (arg) {
+       p = eval (arg->car (), mlenv);
+
+       if (p ()) {
+           if (! p ()->isCons ()) {
+               if (arg->car ())
+                   throw (arg->car ()->dump_string () + uErrorBadType);
+               else
+                   throw (uNil + uErrorBadType);
+           }
+           if (c == 0)
+               url.append (CharConst ("?"));
+           else
+               url.append (CharConst ("&amp;"));
+           c ++;
+       
+           str = to_string (p ()->car ());
+           url.append (urlEncode (str));
+           url.append (CharConst ("="));
+           a = p ()->cdr ();
+           if (a && a->isCons ()) {
+               str = to_string (a->car ());
+               url.append (urlEncode (str));
+           }
+       }
+       nextNode (arg);
+    }
+}
+
+/*DOC:
+===motor-file===
+ (motor-file HTMLFILE [:type MIMETYPE] [#error | :error BOOL]) -> NIL
+
+*/
+//#AFUNC       motor-file      ml_motor_file
+MNode*  ml_motor_file (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  file;
+    ustring  type;
+    bool  ferr = false;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    static paramList  kwlist[] = {
+       {CharConst ("type"), false},
+       {CharConst ("error"), true},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 1, &params, kwlist, &keywords, NULL);
+    file = eval_str (params[0], mlenv);
+    if (keywords[0]) {
+       type = eval_asciiword (keywords[0], mlenv);
+       if (! checkMimeType (type))
+           type.resize (0);
+    }
+    ferr = eval_bool (keywords[1], mlenv);
+
+    if (file.size () == 0)
+       throw (uErrorFilenameEmpty);
+    if (!checkResourceName (file))
+       throw (file + uErrorBadFile);
+
+    if (ferr)
+       mlenv->env->setErrorFlag ();
+    if (type.size () == 0)
+       mlenv->env->doMotor (file);
+    else
+       mlenv->env->doMotor (file, type);
+    mlenv->breakProg ();
+
+    return NULL;
+}
+
+/*DOC:
+===motor-item===
+ (motor-item NUM...) -> NIL
+
+*/
+//#AFUNC       motor-item      ml_motor_item
+MNode*  ml_motor_item (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  num;
+
+    if (!arg)
+       throw (uErrorWrongNumber);
+
+    if (! mlenv->env->responseDone)
+       mlenv->env->standardResponse ();
+    while (arg) {
+       num = eval_str (arg->car (), mlenv);
+       nextNode (arg);
+       mlenv->env->motorItem (num);
+    }
+
+    return NULL;
+}
+
+/*DOC:
+===motor-output===
+ (motor-output TEXT...) -> NIL
+
+*/
+//#AFUNC       motor-output    ml_motor_output
+MNode*  ml_motor_output (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  text;
+
+    if (!arg)
+       throw (uErrorWrongNumber);
+
+    if (! mlenv->env->responseDone)
+       mlenv->env->standardResponse ();
+    while (arg) {
+       text = eval_str (arg->car (), mlenv);
+       nextNode (arg);
+       mlenv->env->motorText (text);
+    }
+
+    return NULL;
+}
+
+/*DOC:
+===motor-output-html===
+ (motor-output-html TEXT...) -> NIL
+
+*/
+//#AFUNC       motor-output-html       ml_motor_output_html
+MNode*  ml_motor_output_html (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  text;
+
+    if (!arg)
+       throw (uErrorWrongNumber);
+
+    if (! mlenv->env->responseDone)
+       mlenv->env->standardResponse ();
+    {
+       MotorOutput*  back = mlenv->env->output;
+       MotorOutputString  out2;
+       mlenv->env->output = &out2;
+       while (arg) {
+           text = eval_str (arg->car (), mlenv);
+           nextNode (arg);
+           mlenv->env->motorText (text);
+       }
+       mlenv->env->output = back;
+       mlenv->env->output->outHTML (out2.ans);
+    }
+
+    return NULL;
+}
+
+/*DOC:
+===motor-raw===
+ (motor-raw TEXT...) -> NIL
+
+*/
+//#AFUNC       motor-raw       ml_motor_raw
+MNode*  ml_motor_raw (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  text;
+
+    if (!arg)
+       throw (uErrorWrongNumber);
+
+    if (! mlenv->env->responseDone)
+       mlenv->env->standardResponse ();
+    while (arg) {
+       text = eval_str (arg->car (), mlenv);
+       nextNode (arg);
+       mlenv->env->output->outText (text);
+    }
+
+    return NULL;
+}
+
+/*DOC:
+===error===
+ (error) -> NIL
+
+*/
+//#AFUNC       error   ml_error
+MNode*  ml_error (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    mlenv->env->setErrorFlag ();
+
+    return NULL;
+}
+
+/*DOC:
+===forbidden===
+ (forbidden) -> NIL
+
+*/
+//#AFUNC       forbidden       ml_forbidden
+MNode*  ml_forbidden (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (! mlenv->env->responseDone)
+       mlenv->env->forbiddenResponse ();
+    mlenv->env->setErrorFlag ();
+
+    return NULL;
+}
+
+/*DOC:
+===location===
+ (location URL ['(NAME VALUE)...]) -> NIL
+
+*/
+//#AFUNC       location        ml_location
+MNode*  ml_location (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  url;
+
+    location_sub (arg, url, mlenv);
+    mlenv->env->location (url);
+    return NULL;
+}
+
+/*DOC:
+===location-html===
+ (location-html URL ['(NAME VALUE)...]) -> NIL
+
+*/
+//#AFUNC       location-html   ml_location_html
+MNode*  ml_location_html (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  url;
+
+    location_sub (arg, url, mlenv);
+    mlenv->env->location_html (url);
+    return NULL;
+}
+
diff --git a/modules/ml-motor.h b/modules/ml-motor.h
new file mode 100644 (file)
index 0000000..b5c7cab
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef ML_MOTOR_H
+#define ML_MOTOR_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_motor_file (MNode* cell, MlEnv* mlenv);
+MNode*  ml_motor_item (MNode* cell, MlEnv* mlenv);
+MNode*  ml_motor_output (MNode* cell, MlEnv* mlenv);
+MNode*  ml_motor_output_html (MNode* cell, MlEnv* mlenv);
+MNode*  ml_motor_raw (MNode* cell, MlEnv* mlenv);
+MNode*  ml_error (MNode* cell, MlEnv* mlenv);
+MNode*  ml_forbidden (MNode* cell, MlEnv* mlenv);
+MNode*  ml_location (MNode* cell, MlEnv* mlenv);
+MNode*  ml_location_html (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_MOTOR_H */
diff --git a/modules/ml-sendmail.cc b/modules/ml-sendmail.cc
new file mode 100644 (file)
index 0000000..a2fc073
--- /dev/null
@@ -0,0 +1,256 @@
+#include "ml-sendmail.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "config.h"
+#include "motorconst.h"
+#include "motorenv.h"
+#include "motor.h"
+#ifdef UTF8JP
+#include "motoroutput-jp.h"
+#include "iso2022jp.h"
+#else
+#include "motoroutput.h"
+#endif
+#include "util_const.h"
+#include "util_check.h"
+#include "util_string.h"
+#include "util_proc.h"
+#include "util_time.h"
+#include "ustring.h"
+#include "expr.h"
+#include <vector>
+#include <exception>
+#include <stdio.h>
+
+#define  TO_LIST_MAX   100
+
+/*DOC:
+==mail module==
+
+*/
+class  SendmailHdr {
+public:
+    ustring  fsubj;
+    ustring  ffrom;
+    ustring  fto;
+    ustring  fcc;
+
+    SendmailHdr () {};
+    ~SendmailHdr () {};
+};
+
+static void  phdr (SendmailHdr& hdr, ustring& line) {
+    uiterator  b, e;
+    umatch  m;
+    static uregex  re_head ("^((subject:\\s*)|(from:\\s*)|(to:\\s*)|(cc:\\s*))", boost::regex_constants::normal | boost::regex_constants::icase);
+
+    b = line.begin ();
+    e = line.end ();
+    if (usearch (b, e, m, re_head)) {
+       if (m[2].matched) {     // subject
+           hdr.fsubj = ustring (m[0].second, e);
+       } else if (m[3].matched) { // from
+           hdr.ffrom = ustring (m[0].second, e);
+       } else if (m[4].matched) { // to
+           hdr.fto = ustring (m[0].second, e);
+       } else if (m[5].matched) { // cc
+           hdr.fcc = ustring (m[0].second, e);
+       }
+    }
+}
+
+static void  sendmail (ustring& faddr, std::vector<ustring>& taddr, ustring& file, MlEnv* mlenv) {
+#ifdef UTF8JP
+    MotorOutputISO2022JPString  out;
+#else
+    MotorOutputString  out;
+#endif
+    HTMLMotor  motor;
+    uiterator  b, e;
+    umatch  m, m2;
+    SendmailHdr  hdr;
+    ustring  line;
+    ustring  t;
+    ProcRW  proc;
+    char**  argv;
+    int  i;
+    FILE*  fd;
+    static uregex  re_white ("^[ \\t]+");
+
+    if (file.size () == 0) 
+       return;
+
+    motor.compile_file (file);
+    motor.output (&out, mlenv->env);
+    b = out.ans.begin ();
+    e = out.ans.end ();
+    line.resize (0);
+    while (usearch (b, e, m, re_nl)) {
+       if (b == m[0].first) {
+           b = m[0].second;
+           break;
+       }
+       if (usearch (b, m[0].first, m2, re_white)) {
+           line.append (uLF);
+           line.append (b, m[0].first);
+       } else {
+           if (line.size () > 0)
+               phdr (hdr, line);
+           line = ustring (b, m[0].first);
+       }
+       b = m[0].second;
+    }
+    if (line.size () > 0)
+       phdr (hdr, line);
+
+    argv = (char**)new char* [taddr.size () + 4];
+    argv[0] = (char*)cmd_qmailinject;
+    argv[1] = (char*)"-f";
+    argv[2] = noconst_char (faddr.c_str ());
+    for (i = 0; i < taddr.size (); i ++) {
+       argv[3 + i] = noconst_char (taddr[i].c_str ());
+    }
+    argv[3 + i] = NULL;
+    proc.open (argv);
+    delete argv;
+    fd = fdopen (proc.wfd, "w");
+
+    if (hdr.ffrom.size () > 0)
+       t = ustring (CharConst ("From: ")) + mimeEncodeJP (hdr.ffrom);
+    else
+       t = ustring (CharConst ("From: ")) + faddr;
+    fwrite (t.data (), sizeof (ustring::value_type), t.size (), fd);
+    fwrite ("\n", 1, 1, fd);
+    if (hdr.fto.size () > 0) {
+       t = ustring (CharConst ("To: ")) + mimeEncodeJP (hdr.fto);
+       fwrite (t.data (), sizeof (ustring::value_type), t.size (), fd);
+       fwrite ("\n", 1, 1, fd);
+    }
+    if (hdr.fcc.size () > 0) {
+       t = ustring (CharConst ("Cc: ")) + mimeEncodeJP (hdr.fcc);
+       fwrite (t.data (), sizeof (ustring::value_type), t.size (), fd);
+       fwrite ("\n", 1, 1, fd);
+    }
+    if (hdr.fsubj.size () > 0) {
+       t = ustring (CharConst ("Subject: ")) + mimeEncodeJP (hdr.fsubj);
+       fwrite (t.data (), sizeof (ustring::value_type), t.size (), fd);
+       fwrite ("\n", 1, 1, fd);
+    }
+#ifdef UTF8JP
+    fwrite (CharConst ("MIME-Version: 1.0\n"), 1, fd);
+    fwrite (CharConst ("Content-Type: text/plain; charset=ISO-2022-JP\n"), 1, fd);
+    fwrite (CharConst ("Content-Transfer-Encoding: 7bit\n"), 1, fd);
+#endif
+// Date: Sun, 04 Jan 2009 17:32:11 +0900
+    t = mailDate ();
+    fwrite (t.data (), sizeof (ustring::value_type), t.size (), fd);
+
+    fwrite ("\n", 1, 1, fd);
+
+    while (usearch (b, e, m, re_nl)) {
+       if (b != m[0].first)
+           fwrite (&b[0], sizeof (ustring::value_type), m[0].first - b, fd);
+       fwrite ("\n", 1, 1, fd);
+       b = m[0].second;
+    }
+    if (b != e) {
+       fwrite (&b[0], sizeof (ustring::value_type), e - b, fd);
+
+    }
+    fclose (fd);
+}
+
+/*DOC:
+===mail===
+ (mail FILENAME :from ADDR :to ADDR_or_LIST) -> NIL
+
+送り先の最大数は,100。
+
+*/
+//#AFUNC       mail    ml_mail
+MNode*  ml_mail (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  file;
+    ustring  faddr;
+    std::vector<ustring>  taddr;
+    ustring  subject;
+    MNodePtr  h;
+    ustring  t;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    static paramList  kwlist[] = {
+       {CharConst ("from"), false},
+       {CharConst ("to"), false},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 1, &params, kwlist, &keywords, NULL);
+    file = eval_str (params[0], mlenv);
+    if (file.size () == 0)
+       throw (uErrorFilenameEmpty);
+    if (! checkResourceName (file)) {
+       throw (file + uErrorBadFile);
+    }
+    faddr = eval_str (keywords[0], mlenv);
+    if (! checkMailAddr (faddr)) {
+       if (mlenv->log)
+           *mlenv->log << faddr << uErrorBadMailAddr << "\n";
+       faddr.resize (0);
+    }
+    h = eval (keywords[1], mlenv);
+    if (h () && h ()->isCons ()) {
+       MNode*  a = h ();
+       while (a) {
+           t = to_string (a->car ());
+           if (checkMailAddr (t)) {
+               taddr.push_back (t);
+           }
+           nextNode (a);                   
+       }
+    } else {
+       t = to_string (h ());
+       if (! checkMailAddr (t)) {
+           if (mlenv->log)
+               *mlenv->log << t << uErrorBadMailAddr << "\n";
+       } else {
+           taddr.push_back (t);
+       }
+    }
+    if (taddr.size () > TO_LIST_MAX)
+       throw (ustring ("too many addresses"));
+
+    if (faddr.size () > 0 && taddr.size () > 0) {
+       ustring  f;
+       if (mlenv->env->path_resource (file, f))
+           sendmail (faddr, taddr, f, mlenv);
+    } else {
+       if (faddr.size () == 0)
+           if (mlenv->log)
+               *mlenv->log << "From address is undefined or illegal.\n";
+       if (taddr.size () == 0)
+           if (mlenv->log)
+               *mlenv->log << "To address is undefined or illegal.\n";
+    }
+
+    return NULL;
+}
+
+/*DOC:
+===mail-address-p===
+ (mail-address-p STRING) -> BOOL
+
+*/
+//#AFUNC       mail-address-p  ml_mail_address_p
+MNode*  ml_mail_address_p (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  str;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    str = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    return newMNode_bool (checkMailAddr (str));
+}
diff --git a/modules/ml-sendmail.h b/modules/ml-sendmail.h
new file mode 100644 (file)
index 0000000..643350e
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef ML_SENDMAIL_H
+#define ML_SENDMAIL_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_mail (MNode* cell, MlEnv* mlenv);
+MNode*  ml_mail_address_p (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_SENDMAIL_H */
diff --git a/modules/ml-sqlite3.cc b/modules/ml-sqlite3.cc
new file mode 100644 (file)
index 0000000..55e770a
--- /dev/null
@@ -0,0 +1,535 @@
+#include "ml-sqlite3.h"
+#include "motorconst.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "ustring.h"
+#include "expr.h"
+#include "util_const.h"
+#include "util_string.h"
+#include "utf8.h"
+#include <exception>
+
+/*DOC:
+==sqlite3 module==
+
+*/
+
+static MLSqlite3*  objref (MlEnv* mlenv) {
+    assert (mlenv->module->id == cMLSqlite3ID);
+    return (MLSqlite3*)mlenv->module;
+}
+
+int  MLSqlite3::open (ustring& name) {
+    int  rc;
+
+    if (fcreate) {
+       rc = sqlite3_open_v2 (name.c_str (), &dbh, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
+    } else {
+       rc = sqlite3_open_v2 (name.c_str (), &dbh, SQLITE_OPEN_READWRITE, NULL);
+    }
+    if (! rc) {
+       sqlite3_busy_timeout (dbh, 60000);
+       dbst = NULL;
+       return 1;               // 1: OK
+    } else {
+       return 0;               // 0: NG
+    }
+}
+
+void  MLSqlite3::close () {
+    if (dbh) {
+       finalize ();
+       sqlite3_close (dbh);
+       dbh = NULL;
+    }
+}
+
+void  MLSqlite3::finalize () {
+    if (dbst) {
+       sqlite3_finalize (dbst);
+       dbst = NULL;
+    }
+}
+
+int  MLSqlite3::prepare (ustring& sql) {
+    int  rc;
+
+    rc = sqlite3_prepare_v2 (dbh, sql.c_str (), sql.length (), &dbst, NULL);    
+    return rc;
+}
+
+void  MLSqlite3::bind (namearray& name, namearray& value, MlEnv* mlenv) {
+    namearray::iterator  i1, i2;
+    int  n;
+
+    for (i1 =name.begin (), i2 = value.begin ();
+        i1 != name.end ();
+        i1 ++, i2 ++) {
+#ifdef DEBUG
+       if (mlenv->log) {
+           *mlenv->log << "\tbind " << *i1 << " <= \"" << ellipsis (logText (*i2), cLOGSHORTSIZE) << "\"\n";
+       }
+#endif /* DEBUG */
+       n = sqlite3_bind_parameter_index (dbst, (*i1).c_str ());
+       if (n == 0) {
+           throw (ustring ("\"") + *i1 + ustring ("\": named parameter not found."));
+       } else {
+           sqlite3_bind_text (dbst, n, (*i2).c_str (), (*i2).length (), NULL);
+       }
+    }
+}
+
+void  MLSqlite3::bind (namearray& value, MlEnv* mlenv) {
+    namearray::iterator  i2;
+    int  n;
+
+    for (n = 1, i2 = value.begin (); i2 != value.end (); n ++, i2 ++) {
+#ifdef DEBUG
+       if (mlenv->log) {
+           *mlenv->log << "\tbind[" << n << "] <= \"" << ellipsis (logText (*i2), cLOGSHORTSIZE) << "\"\n";
+       }
+#endif /* DEBUG */
+       sqlite3_bind_text (dbst, n, (*i2).c_str (), (*i2).length (), NULL);
+    }
+}
+
+void  MLSqlite3::exec (MlEnv* mlenv) {
+    int  rc;
+
+    rc = sqlite3_step (dbst);
+    switch (rc) {
+    case SQLITE_DONE:
+       finalize ();
+       break;
+    case SQLITE_ROW:
+       ncols = sqlite3_column_count (dbst);
+       break;
+    case SQLITE_BUSY:
+       if (mlenv->log)
+           *mlenv->log << "SQL timeout.\n";
+       mlenv->breakProg ();
+       break;
+    default:                   // error
+       finalize ();
+    }
+}
+
+void  MLSqlite3::step (MlEnv* mlenv) {
+    int  rc;
+
+    rc = sqlite3_step (dbst);
+    switch (rc) {
+    case SQLITE_DONE:
+       finalize ();
+       break;
+    case SQLITE_ROW:
+       break;
+    case SQLITE_BUSY:
+       if (mlenv->log)
+           *mlenv->log << "SQL timeout.\n";
+       mlenv->breakProg ();
+       break;
+    }
+}
+
+void  MLSqlite3::answer (namearray& vars, MlEnv* mlenv) {
+    int  i;
+    int  n = vars.size ();
+    MNodePtr  h;
+
+    if (isReady ()) {
+       for (i = 0; i < n; i ++) {
+           if (i < ncols) {
+               ustring*  v = new ustring (char_type (sqlite3_column_text (dbst, i)), sqlite3_column_bytes (dbst, i));
+               h = newMNode_str (v);
+               mlenv->setVar (vars[i], h.p);
+           } else {
+               mlenv->setVar (vars[i], NULL);
+           }
+       }
+    }
+}
+
+void  MLSqlite3::answer (namearray& vars, int idx, MlEnv* mlenv) {
+    int  i;
+    int  n = vars.size ();
+    MNodePtr  h;
+
+    if (isReady ()) {
+       for (i = 0; i < n; i ++) {
+           if (i < ncols) {
+               ustring*  v = new ustring (char_type (sqlite3_column_text (dbst, i)), sqlite3_column_bytes (dbst, i));
+               h = newMNode_str (v);
+               mlenv->setAry (vars[i], idx, h.p);
+           } else {
+               mlenv->setAry (vars[i], idx, NULL);
+           }
+       }
+    }
+}
+
+void  MLSqlite3::answer_ary (MlEnv* mlenv, MLSqlite3::fsqlParam& par) {
+    int  n = 0;
+
+    while (isReady ()) {
+       n ++;
+       answer (par.answer, n, mlenv);
+       if (n >= limit && limit > 0)
+           break;
+       step (mlenv);
+    }
+    for (int i = 0; i < par.answer.size (); i ++) {
+       mlenv->setArySize (par.answer[i], n);
+    }
+}
+
+MNode*  MLSqlite3::answer_list (MlEnv* mlenv) {
+    MNodeList  ans;
+    int  i;
+
+    if (isReady ()) {
+       for (i = 0; i < ncols; i ++) {
+           ans.append (newMNode_str (new ustring (char_type (sqlite3_column_text (dbst, i)), sqlite3_column_bytes (dbst, i))));
+       }
+    }
+    finalize ();
+    return ans.release ();
+}
+
+MNode*  MLSqlite3::answer_list_ary (MlEnv* mlenv) {
+    boost::ptr_vector<MNodeList>  list;
+    MNodeList  ans;
+    int  i, n;
+
+    for (i = 0; i < ncols; i ++) {
+       list.push_back (new MNodeList);
+    }
+
+    n = 0;
+    while (isReady ()) {
+       n ++;
+       for (i = 0; i < ncols; i ++) {
+           list[i].append (newMNode_str (new ustring (char_type (sqlite3_column_text (dbst, i)), sqlite3_column_bytes (dbst, i))));
+       }
+       if (n >= limit && limit > 0)
+           break;
+       step (mlenv);
+    }
+
+    for (i = 0; i < ncols; i ++) {
+       ans.append (list[i].release ());
+    }
+
+    return ans.release ();
+}
+
+sqlite3_int64  MLSqlite3::rowid () {
+    return sqlite3_last_insert_rowid (dbh);
+}
+
+/*DOC:
+===$sqlite3===
+ ($sqlite3 DB [:limit NUM] [#create | :create BOOL] [SUBFUNCTION...])
+
+*/
+//#MFUNC       $sqlite3        ml_sqlite3
+MNode*  ml_sqlite3 (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MLSqlite3  obj;
+    ustring  db;
+    ustring  dbfile;
+    MNodePtr  ans;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    MNode*  rest;
+    static paramList  kwlist[] = {
+       {CharConst ("limit"), false},
+       {CharConst ("create"), true},
+       {NULL, 0, 0}
+    };
+
+    mlenv->module = &obj;
+    setParams (arg, 1, &params, kwlist, &keywords, &rest);
+    db = eval_str (params[0], mlenv);
+    if (keywords[0]) {
+       obj.limit = eval_int (keywords[0], mlenv);
+       if (obj.limit <= 0)
+           obj.limit = 1;
+       if (obj.limit > kARRAYMAX)
+           obj.limit = kARRAYMAX;
+    }
+    obj.fcreate = eval_bool (keywords[1], mlenv);
+    
+    if (db.size () == 0)
+       throw (uErrorFilenameEmpty);
+    dbfile = mlenv->env->path_db (db, kEXT_SQLITE3);
+    if (! obj.open (dbfile))
+       throw (ustring ("unable to open database file."));
+
+    mlenv->moduleStatus = MlEnv::M_EXEC;
+    ans = progn (rest, mlenv);
+    if (mlenv->breaksym ()
+       && (mlenv->breaksym ()->isNil () || eq (mlenv->breaksym (), cell->car ()))) {
+       mlenv->breaksym = NULL;
+    }
+
+    return mlenv->retval = ans ();
+}
+
+static void  pushAnswer (MlEnv* mlenv, MNode* cell, MNode*& arg, MLSqlite3::namearray& answer) {
+    ustring*  u;
+    MNodePtr  v;
+    MNode*  c;
+
+    v = eval (arg, mlenv);
+    answer.clear ();
+    if (v () && v ()->isCons()) {
+       c = v ();
+       while (c) {
+           u = new ustring ();
+           if (c->car ())
+               *u = c->car ()->to_string ();
+           answer.push_back (u);
+           nextNode (c);
+       }
+    } else {
+       u = new ustring;
+       if (v ())
+           *u = v ()->to_string ();
+       answer.push_back (u);
+    }
+}
+
+/*DOC:
+===subfunctions of $sqlite3===
+
+*/
+static MNode*  ml_sqlite3_sql_main (MNode* cell, MlEnv* mlenv, MLSqlite3::fsqlParam& par) {
+    MNode*  arg = cell->cdr ();
+    MLSqlite3*  obj = objref (mlenv);
+    int  rc;
+    MNodePtr  ans;
+    int  mode;
+    ustring*  u;
+    ustring*  u2;
+    umatch  m;
+    static uregex  re ("^:[a-zA-Z0-9_]{1,32}$");
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    MNode*  rest;
+    static paramList  kwlist[] = {
+       {CharConst ("bind"), false},
+       {CharConst ("answer"), false},
+       {CharConst ("@answer"), false},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 1, &params, kwlist, &keywords, &rest);
+    par.sql = eval_str (params[0], mlenv);
+    mode = 0;
+    if (keywords[0]) {         // bind
+       MNodePtr  p;
+
+       mode = 1;
+       p = eval (keywords[0], mlenv);
+       if (! p () || p ()->isCons ()) {
+           MNode*  v;
+
+           v = p ();
+           while (v) {
+               u = new ustring (to_string (v->car ()));
+               nextNodeNonNil (v);
+               u2 = new ustring (to_string (v->car ()));
+               nextNode (v);
+               if (usearch (*u, m, re)) {
+                   par.bindName.push_back (u);
+                   par.bindValue.push_back (u2);
+               } else {
+                   ustring  t (*u);
+                   delete u;
+                   delete u2;
+                   throw (ustring ("\"") + t + ustring ("\": bad bind name."));
+               }
+           }
+       } else {
+           if (p ())
+               throw (p ()->dump_string_short () + ustring (": bad bind parameter."));
+           else
+               throw (ustring ("nil: bad bind parameter."));
+       }
+    }
+    if (keywords[1]) {         // answer
+       mode = 1;
+       par.answerisary = false;
+       pushAnswer (mlenv, cell, keywords[1], par.answer);
+    }
+    if (keywords[2]) {         // @answer
+       mode = 1;
+       par.answerisary = true;
+       pushAnswer (mlenv, cell, keywords[2], par.answer);
+    }
+
+    if (mode == 0) {
+       while (rest) {
+           u2 = eval_str_new (rest->car (), mlenv);
+           nextNode (rest);
+           par.bindValue.push_back (u2);
+       }
+       mode = 2;
+    }
+    if (rest)
+       throw (uErrorBadArg);
+    
+#ifdef DEBUG2
+    {
+       MLSqlite3::namearray::iterator  it1, it2;
+       int  c;
+
+       std::cerr << "sql       " << sql << "\n";
+       if (mode == 1) {
+           for (it1 = bindName.begin (), it2 = bindValue.begin (); it1 != bindName.end (); it1 ++, it2 ++) {
+               std::cerr << "  :bind " << *it1 << " " << *it2 << "\n";
+           }
+       } else {
+           for (it2 = bindValue.begin (); it2 != bindValue.end (); it2 ++) {
+               std::cerr << "  " << *it2 << "\n";
+           }
+       }
+       if (answer.size () > 0) {
+           if (answerisary)
+               std::cerr << "  :@answer '(";
+           else
+               std::cerr << "  :answer '(";
+           for (c = 0, it1 = answer.begin (); it1 != answer.end (); it1 ++) {
+               if (c > 0)
+                   std::cerr << " ";
+               c ++;
+               std::cerr << *it1;
+           }
+           std::cerr << ")\n";
+       }
+    }
+#endif /* DEBUG */
+
+    rc = obj->prepare (par.sql);
+    if (rc == SQLITE_OK) {
+       if (mode == 2) {
+           obj->bind (par.bindValue, mlenv);
+       } else {
+           obj->bind (par.bindName, par.bindValue, mlenv);
+       }
+       obj->exec (mlenv);
+       if (par.answer.size () > 0) {
+           if (par.answerisary) {
+               if (obj->isReady ()) {
+                   obj->answer_ary (mlenv, par);
+               } else {
+                   for (int i = 0; i < par.answer.size (); i ++) {
+                       mlenv->setArySize (par.answer[i], 0);
+                   }
+               }
+           } else {
+               if (obj->isReady ()) {
+                   obj->answer (par.answer, mlenv);
+                   obj->finalize ();
+               } else {
+                   for (int i = 0; i < par.answer.size (); i ++) {
+                       mlenv->setVar (par.answer[i], NULL);
+                   }
+               }
+           }
+       } else {
+           if (obj->isReady ()) {
+               if (par.answerisary) {
+                   ans = obj->answer_list_ary (mlenv);
+               } else {
+                   ans = obj->answer_list (mlenv);
+               }
+           }
+       }
+    } else {                   // ! SQLITE_OK
+       throw (ustring ("SQL error: ") + sqlite3_errmsg (obj->dbh));
+    }
+
+    return ans.release ();
+}
+
+/*DOC:
+====sql====
+ (sql SQL [:bind NAME-VALUE-LIST] [:answer '(VARIABLES...) | :@answer '(VARIABLES...)]) -> NIL or STRING-LIST
+ (sql@ SQL [:bind NAME-VALUE-LIST] [:answer '(VARIABLES...) | :@answer '(VARIABLES...)]) -> NIL or LIST-OF-LIST-OF-STRING
+
+*/
+//#SFUNC       sql     ml_sqlite3_sql
+//#SFUNC       sql@    ml_sqlite3_sql_ary
+MNode*  ml_sqlite3_sql (MNode* cell, MlEnv* mlenv) {
+    MLSqlite3::fsqlParam  par;
+
+    return ml_sqlite3_sql_main (cell, mlenv, par);
+}
+MNode*  ml_sqlite3_sql_ary (MNode* cell, MlEnv* mlenv) {
+    MLSqlite3::fsqlParam  par;
+
+    par.answerisary = 1;
+    return ml_sqlite3_sql_main (cell, mlenv, par);
+}
+
+/*DOC:
+====rowid====
+ (rowid) -> STRING
+
+*/
+//#SFUNC       rowid   ml_sqlite3_rowid
+MNode*  ml_sqlite3_rowid (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MLSqlite3*  obj = objref (mlenv);
+
+    if (arg)
+       throw (uErrorWrongNumber);
+    return newMNode_str (new ustring (boost::lexical_cast<ustring> (obj->rowid ())));
+}
+
+static ustring  escape_like (const ustring& str) {
+    static uregex  re ("[%_\\\\]");
+    Splitter  sp (str, re);
+    ustring  ans;
+
+    while (sp.next ()) {
+       ans.append (sp.begin (), sp.end ());
+       switch (*sp.matchBegin ()) {
+       case '%':
+       case '_':
+       case '\\':
+           ans.append (CharConst ("\\"));
+           ans.append (sp.matchBegin (), sp.matchEnd ());
+           break;
+       default:;
+       }
+    }
+    return ans;
+}
+
+/*DOC:
+====escape-like====
+ (escape-like STRING) -> STRING
+
+*/
+//#SFUNC       escape-like     ml_sqlite3_escape_like
+MNode*  ml_sqlite3_escape_like (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MLSqlite3*  obj = objref (mlenv);
+    ustring  str;
+    ustring  ans;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    str = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    ans = escape_like (str);
+
+    return newMNode_str (new ustring (ans));
+}
diff --git a/modules/ml-sqlite3.h b/modules/ml-sqlite3.h
new file mode 100644 (file)
index 0000000..36081e2
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef ML_SQLITE3_H
+#define ML_SQLITE3_H
+
+#include "ml.h"
+#include "ml-id.h"
+#include "ustring.h"
+#include "motorconst.h"
+#include <boost/ptr_container/ptr_vector.hpp>
+extern "C" {
+#include "sqlite3.h"
+}
+
+class  MNode;
+class  MlEnv;
+
+class  MLSqlite3: public MLFunc {
+ public:
+    typedef boost::ptr_vector<ustring>  namearray;
+    class  fsqlParam {
+    public:
+       ustring  sql;
+       MLSqlite3::namearray  bindName;
+       MLSqlite3::namearray  bindValue;
+       MLSqlite3::namearray  answer;
+       bool  answerisary;
+       
+       fsqlParam () {
+           answerisary = false;
+       };
+       virtual  ~fsqlParam () {};
+    };
+
+    sqlite3*  dbh;
+    int  limit;
+    ustring  voverflow;
+    sqlite3_stmt*  dbst;
+    int  ncols;
+    bool  fcreate;
+
+    MLSqlite3 (): MLFunc (cMLSqlite3ID) {
+       dbh = NULL;
+       limit = kARRAYMAX;
+       dbst = NULL;
+       fcreate = false;
+    };
+    virtual  ~MLSqlite3 () {
+       close ();
+    };
+
+    virtual int  open (ustring& name);
+    virtual void  close ();
+    virtual void  finalize ();
+    virtual int  prepare (ustring& sql);
+    virtual void  bind (namearray& name, namearray& value, MlEnv* mlenv);
+    virtual void  bind (namearray& value, MlEnv* mlenv);
+    virtual void  exec (MlEnv* mlenv);
+    virtual void  step (MlEnv* mlenv);
+    virtual bool  isReady () {
+       return (dbst != NULL);
+    };
+    virtual void  answer (namearray& vars, MlEnv* mlenv);
+    virtual void  answer (namearray& vars, int idx, MlEnv* mlenv);
+    virtual void  answer_ary (MlEnv* mlenv, fsqlParam& par);
+    virtual MNode*  answer_list (MlEnv* mlenv);
+    virtual MNode*  answer_list_ary (MlEnv* mlenv);
+    virtual sqlite3_int64  rowid ();
+};
+
+MNode*  ml_sqlite3 (MNode* cell, MlEnv* mlenv);
+MNode*  ml_sqlite3_sql (MNode* cell, MlEnv* mlenv);
+MNode*  ml_sqlite3_sql_ary (MNode* cell, MlEnv* mlenv);
+MNode*  ml_sqlite3_rowid (MNode* cell, MlEnv* mlenv);
+MNode*  ml_sqlite3_escape_like (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_SQLITE3_H */
diff --git a/modules/ml-store.cc b/modules/ml-store.cc
new file mode 100644 (file)
index 0000000..79bf0b2
--- /dev/null
@@ -0,0 +1,814 @@
+#include "ml-store.h"
+#include "config.h"
+#include "motorconst.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "expr.h"
+#include "motorenv.h"
+#include "util_const.h"
+#include "util_check.h"
+#include "util_file.h"
+#include "util_proc.h"
+#include "util_time.h"
+#include "util_random.h"
+#include "util_string.h"
+#include "util_mimetype.h"
+#include "utf8.h"
+#include "ustring.h"
+#include "bdbmacro.h"
+#include "filemacro.h"
+#include <boost/lexical_cast.hpp>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+#define  StoreFileNameMax      48
+
+/*DOC:
+==data store module==
+
+*/
+/*
+  *record of serial.db
+  key: <xserial>
+  val: <creation_time>:<last_access_time>:<serial>:<subdir>:<savedir>
+*/
+static void  openDB (BDBHash& db, FileMacro& lock, MlEnv* mlenv) {
+    ustring  t = mlenv->env->path_to_store_index ();
+    ustring  dbfile = t + kEXT_HASH;
+    ustring  lockfile = t + kEXT_LOCK;
+
+    lock.openAppendLock (lockfile.c_str ());
+    db.open (dbfile.c_str ());
+}
+
+static bool  splitRec (ustring& rec, ustring& uctime, ustring& uatime, ustring& sr, ustring& subdir, ustring& savedir) {
+    uiterator  b = rec.begin ();
+    uiterator  e = rec.end ();
+    umatch  m;
+    static uregex  re (":");
+
+    if (b == e || ! usearch (b, e, m, re))
+       return false;
+    uctime.assign (b, m[0].first);
+    b = m[0].second;
+    if (b == e || ! usearch (b, e, m, re))
+       return false;
+    uatime.assign (b, m[0].first);
+    b = m[0].second;
+    if (b == e || ! usearch (b, e, m, re))
+       return false;
+    sr.assign (b, m[0].first);
+    b = m[0].second;
+    if (b == e || ! usearch (b, e, m, re))
+       return false;
+    subdir.assign (b, m[0].first);
+    b = m[0].second;
+    savedir.assign (b, e);
+
+    return true;
+}
+
+void  newStoreSerial (MlEnv* mlenv) {
+    BDBHash  db;
+    FileMacro  lock;
+    unsigned long  srl;
+    ustring  v, x, subdir, nw;
+    time_t  tm;
+    unsigned long  n;
+    ustring  xs;
+
+    tm = now ();
+    nw = boost::lexical_cast<ustring> (tm);
+    subdir = strYMD (tm);
+    subdir.append (uSlash);
+
+    openDB (db, lock, mlenv);
+
+    if (db.get (uDash, x)) {
+       srl = boost::lexical_cast<unsigned long> (x) + 1;
+    } else {
+       srl = 1;
+    }
+
+    n = srl;
+    xs = randomKey (srl);
+
+    x = boost::lexical_cast<ustring> (srl);
+    db.put (uDash, x);
+
+    subdir.append (x);
+    v = nw;
+    v.append (uColon).append (nw).append (uColon).append (x).append (uColon).append (subdir).append (uColon);
+    db.put (xs, v);
+
+    db.close ();
+    lock.close ();
+
+    v = mlenv->env->path_to_store ();
+    makeSubDir (v, subdir);
+    mlenv->env->storedir = v + uSlash;
+
+    mlenv->setVar (uXSerial, newMNode_str (new ustring (xs)));
+#ifdef DEBUG2
+    std::cerr << "storedir:" << mlenv->env->storedir << "\n";
+#endif /* DEBUG */
+}
+
+static bool  setSerial (MlEnv* mlenv, ustring& xs, ustring& path) {
+    BDBHash  db;
+    FileMacro  lock;
+    ustring  nw, v, uctime, uatime, sr, subdir, savedir;
+    time_t  tm;
+
+    tm = now ();
+    nw = boost::lexical_cast<ustring> (tm);
+
+    openDB (db, lock, mlenv);
+
+    if (! db.get (xs, v) || ! splitRec (v, uctime, uatime, sr, subdir, savedir)) {
+       db.close ();
+       lock.close ();
+       return false;
+    }
+
+    v = nw;
+    v.append (uColon).append (nw).append (uColon).append (sr).append (uColon).append (subdir).append (uColon).append (savedir);
+    db.put (xs, v);
+
+    db.close ();
+    lock.close ();
+
+    if (uctime.size () == 0) {
+       v = mlenv->env->path_to_store ();
+       makeSubDir (v, subdir);
+       path = v;
+       path.append (uSlash);
+    } else {
+       path = mlenv->env->path_to_store ();
+       path.append (subdir).append (uSlash);
+    }
+
+    return true;
+}
+
+static ustring*  nextRec (char*& b, char* e, bool fans) {
+    char*  p = b;
+    ustring*  ans = NULL;
+
+    while (b < e) {
+       if (*b == ':') {
+           if (fans)
+               ans = new ustring (p, b);
+           b ++;
+           return ans;
+       }
+       b ++;
+    }
+    if (p < e && fans)
+       ans = new ustring (p, e);
+    return ans;
+}
+
+static void  pathRec (DBT* val, ustring*& subdir, ustring*& savedir) {
+    char*  b;
+    char*  e;
+    ustring*  u;
+
+    b = (char*)val->data;
+    e = b + val->size;
+    nextRec (b, e, false);
+    nextRec (b, e, false);
+    nextRec (b, e, false);
+    u = nextRec (b, e, true);
+    if (u)
+       subdir = u;
+    else
+       subdir = NULL;
+    u = nextRec (b, e, true);
+    if (u)
+       savedir = u;
+    else
+       savedir = NULL;
+}
+
+static void  pdir (ustring& path) {
+    int  i;
+
+    for (i = path.size () - 1; i > sizeof (cDataTop); i --) {
+       if (path[i] == '/') {
+           path.resize (i);
+           return;
+       }
+    }
+}
+
+static void  cleanStore (int span, MlEnv* mlenv) {
+    BDBHash  db;
+    FileMacro  lock;
+    time_t  limit, t;
+    DBT  key, val;
+    boost::ptr_vector<ustring>  xs;
+    boost::ptr_vector<ustring>  path;
+    ustring*  subdir = NULL;
+    ustring*  savedir = NULL;
+    int  i;
+    ustring  u;
+    char*  argv1[4];
+    char*  argv2[4];
+
+    limit = now () - span;
+#if 0
+    std::cerr << "limit:" << strYMD (limit) << "\n";
+#endif /* DEBUG */
+    openDB (db, lock, mlenv);
+
+    db.initeach ();
+    while (db.each (&key, &val)) {
+       if (key.size > 1) {
+           t = strtol ((char*)val.data, NULL, 10);
+           if (t > 0 && t < limit) {
+#if 0
+               std::cerr << strYMD (t) << "\n";
+#endif /* DEBUG */
+               xs.push_back (new ustring ((char*)key.data, key.size));
+               pathRec (&val, subdir, savedir);
+               if (subdir)
+                   path.push_back (subdir);
+               if (savedir)
+                   path.push_back (savedir);
+           }
+       }
+    }
+    for (i = 0; i < xs.size (); i ++) {
+       db.del (xs[i]);
+//     std::cerr << "db.del(" << xs[i] << ")\n";
+    }
+
+    db.close ();
+    lock.close ();
+
+    argv1[0] = (char*)cmd_rm;
+    argv1[1] = (char*)"-fr";
+    argv1[3] = NULL;
+    argv2[0] = (char*)cmd_rmdir;
+    argv2[2] = NULL;
+    for (i = 0; i < path.size (); i ++) {
+       assert (path[i].size () > 0);
+       u = mlenv->env->path_to_store () + path[i];
+       argv1[2] = noconst_char (u.c_str ());
+       exec_cmd (argv1);
+//     std::cerr << argv1[0] << " " << argv1[1] << " " << argv1[2] << "\n";
+       pdir (u);
+       argv2[1] = noconst_char (u.c_str ());
+       exec_cmd (argv2);
+//     std::cerr << argv2[0] << " " << argv2[1] << "\n";
+    }
+}
+
+static void  openStorageDB (BDBHash& db, FileMacro& lock, MlEnv* mlenv) {
+    ustring  t = mlenv->env->path_to_storage_index ();
+    ustring  dbfile = t + kEXT_HASH;
+    ustring  lockfile = t + kEXT_LOCK;
+
+    lock.openAppendLock (lockfile.c_str ());
+    db.open (dbfile.c_str ());
+}
+
+#if 0
+static ustring  storageName (const ustring& name) {
+    if (name.length () == 0 || name.length () > StoreFileNameMax) {
+       throw (ustring (name).append (uErrorBadName));
+    }
+    ustring  ans (filenameEncode (name));
+    if (ans.length () > 128)
+       ans.resize (128);
+    return ans;
+}
+#endif
+
+static void  newStorage (MlEnv* mlenv, const ustring& name) {
+    BDBHash  db;
+    FileMacro  lock;
+    ustring  ename;
+    ustring  dir;
+    time_t  tm = now ();
+    int  sum = 0;
+    int  i;
+    char  b[4];
+    size_t  s;
+
+    for (i = 0; i < name.length (); i ++) {
+       sum += name[i];
+    }
+    sum = (sum & 65535) % 1000;
+    mlenv->env->storagedir = mlenv->env->path_to_storage ();
+    s = snprintf (b, 4, "%.3d", sum);
+    mlenv->env->storagedir.append (b, s);
+    mkdir (mlenv->env->storagedir.c_str (), 0777);
+    ename = filenameEncode (name);
+    mlenv->env->storagedir.append (uSlash).append (ename);
+    mkdir (mlenv->env->storagedir.c_str (), 0777);
+    mlenv->env->storagedir.append (uSlash);
+    openStorageDB (db, lock, mlenv);
+    dir = boost::lexical_cast<ustring> (tm);
+    dir.append (uColon).append (b, s).append (uSlash).append (ename);
+    db.put (name, dir);
+    db.close ();
+    lock.close ();
+}
+
+/*DOC:
+===new-xserial===
+ (new-xserial) -> NIL
+motor変数「XSerial」にアクセスキーが保存される。
+
+*/
+//#AFUNC       new-xserial     ml_new_xserial
+MNode*  ml_new_xserial (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    unsigned long  n;
+    ustring  xs;
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    newStoreSerial (mlenv);
+
+    return NULL;
+}
+
+/*DOC:
+===set-xserial===
+ (set-xserial XSERIAL) -> BOOL
+アクセスキーが正しければ,motor変数「XSerial」にアクセスキーが保存され,trueを返す。
+
+*/
+//#AFUNC       set-xserial     ml_set_xserial
+MNode*  ml_set_xserial (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  val;
+    MNodePtr  h;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    val = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (setSerial (mlenv, val, mlenv->env->storedir)) {
+       h = newMNode_str (new ustring (val));
+       mlenv->setVar (uXSerial, h.p);
+
+       return newMNode_bool (true);
+    } else {
+       mlenv->env->storedir.resize (0);
+       mlenv->setVar (uXSerial, NULL);
+
+       return newMNode_bool (false);
+    }
+}
+
+/*DOC:
+===read-file===
+ (read-file FILENAME [:code ENCODING]) -> STRING
+
+*/
+//#AFUNC       read-file       ml_read_file
+MNode*  ml_read_file (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+    ustring  src;
+    ustring  encoding;
+    ustring*  data = NULL;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    static paramList  kwlist[] = {
+       {CharConst ("code"), false},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 1, &params, kwlist, &keywords, NULL);
+    name = eval_str (params[0], mlenv);
+    if (keywords[0])
+       encoding = eval_str (keywords[0], mlenv);
+
+    if (mlenv->env->storedir.empty ())
+       throw (uErrorNoStore);
+    src = mlenv->env->path_store_file (name);
+
+    try {
+       data = new ustring;
+       if (readFile (src, *data, cPOSTLIMITHARD)) {
+           if (encoding.size () > 0 && data->size () > 0) {
+               *data = uiconv (*data, kCODE_UTF8, encoding.c_str ());
+           }
+           *data = fixUTF8 (*data);
+       } else {
+           delete data;
+           data = NULL;
+       }
+    } catch (ustring& msg) {
+       delete data;
+       data = NULL;
+       throw (msg);
+    }
+
+    if (data)
+       return newMNode_str (data);
+    else
+       return NULL;
+}
+
+/*DOC:
+===write-file===
+ (write-file FILENAME STRING [:code ENCODING]) -> NIL
+
+*/
+//#AFUNC       write-file      ml_write_file
+MNode*  ml_write_file (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+    ustring  tgt;
+    ustring  encoding;
+    ustring  data;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    static paramList  kwlist[] = {
+       {CharConst ("code"), false},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 2, &params, kwlist, &keywords, NULL);
+    name = eval_str (params[0], mlenv);
+    data = eval_str (params[1], mlenv);
+    if (keywords[0])
+       encoding = eval_str (keywords[0], mlenv);
+
+    if (mlenv->env->storedir.empty ())
+       throw (uErrorNoStore);
+
+    tgt = mlenv->env->path_store_file (name);
+    if (encoding.size () > 0 && data.size () > 0) {
+       data = uiconv (data, encoding.c_str (), kCODE_UTF8);
+    }
+    writeFile (tgt, data);
+
+    return NULL;
+}
+
+/*DOC:
+===clean-store===
+ (clean-store) -> NIL
+1日以上経過したデータストアを消去する。
+
+*/
+//#AFUNC       clean-store     ml_clean_store
+MNode*  ml_clean_store (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    int  span = 86400;
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    cleanStore (span, mlenv);
+    return NULL;
+}
+
+/*DOC:
+===datastore===
+ (datastore) -> NIL
+ (datastore NAME) -> NIL
+
+NAME must be less than 32 bytes.
+
+*/
+//#AFUNC       datastore       ml_datastore
+MNode*  ml_datastore (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+
+    if (! arg) {
+       mlenv->env->setDefaultDatastore ();
+    } else {
+       name = eval_str (arg->car (), mlenv);
+       nextNode (arg);
+
+       if (arg)
+           throw (uErrorWrongNumber);
+
+       mlenv->env->setDatastore (name);
+    }
+
+    return NULL;
+}
+
+/*DOC:
+===set-storage===
+ (set-storage NAME) -> NIL
+
+*/
+//#AFUNC       set-storage     ml_set_storage
+MNode*  ml_set_storage (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    name = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    newStorage (mlenv, name);
+
+    return NULL;
+}
+
+/*DOC:
+===save-file===
+ (save-file NAME_STORE NAME_STORAGE) -> NIL
+
+*/
+//#AFUNC       save-file       ml_save_file
+MNode*  ml_save_file (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  namestore, namestorage;
+    ustring  src, tgt;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    namestore = eval_str (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    namestorage = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (mlenv->env->storedir.empty ())
+       throw (uErrorNoStore);
+    if (mlenv->env->storagedir.empty ())
+       throw (uErrorNoStorage);
+
+    src = mlenv->env->path_store_file (namestore);
+    tgt = mlenv->env->path_storage_file (namestorage);
+    rename (src.c_str (), tgt.c_str ());
+    symlink (tgt.c_str (), src.c_str ());
+
+    return NULL;
+}
+
+/*DOC:
+===restore-file===
+ (restore-file NAME_STORAGE NAME_STORE) -> NIL
+
+*/
+//#AFUNC       restore-file    ml_restore_file
+MNode*  ml_restore_file (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  namestore, namestorage;
+    ustring  src, tgt;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    namestorage = eval_str (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    namestore = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (mlenv->env->storedir.empty ())
+       throw (uErrorNoStore);
+    if (mlenv->env->storagedir.empty ())
+       throw (uErrorNoStorage);
+
+    src = mlenv->env->path_storage_file (namestorage);
+    tgt = mlenv->env->path_store_file (namestore);
+    unlink (tgt.c_str ());
+    symlink (src.c_str (), tgt.c_str ());
+
+    return NULL;
+}
+
+/*DOC:
+===delete-file===
+ (delete-file NAME_STORAGE) -> NIL
+
+*/
+//#AFUNC       delete-file     ml_delete_file
+MNode*  ml_delete_file (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  namestorage;
+    ustring  tgt;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    namestorage = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (mlenv->env->storagedir.empty ())
+       throw (uErrorNoStorage);
+
+    tgt = mlenv->env->path_storage_file (namestorage);
+    unlink (tgt.c_str ());
+
+    return NULL;
+}
+
+class  ResponseParam {
+public:
+    ustring  src;
+    ustring  type;
+    enum {
+       F_NONE,
+       F_SERIAL,
+       F_NAMED,
+       F_STATIC,
+    }  storetype;
+    bool  finline;
+    ustring  dispname;
+
+    ResponseParam () {
+       storetype = F_STATIC;
+       finline = false;
+    };
+    ResponseParam (MlEnv* mlenv) {
+       if (mlenv->env->storagedir.length () > 0) {
+           storetype = F_NAMED;
+       } else if (mlenv->env->storedir.length () > 0) {
+           storetype = F_SERIAL;
+       } else {
+           storetype = F_NONE;
+       }
+       finline = false;
+    };
+    virtual  ~ResponseParam () {};
+    void  proc (MNode* cell, MlEnv* mlenv, bool fmotor) {
+       MNode*  arg = cell->cdr ();
+       ustring  filename;
+       std::vector<MNode*>  params;
+       std::vector<MNode*>  keywords;
+       static paramList  kwlist_motor[] = {
+           {CharConst ("serial"), true},
+           {CharConst ("named"), true},
+           {CharConst ("static"), true},
+           {CharConst ("type"), false},
+           {NULL, 0, 0}
+       };
+       static paramList  kwlist_file[] = {
+           {CharConst ("serial"), true},
+           {CharConst ("named"), true},
+           {CharConst ("static"), true},
+           {CharConst ("type"), false},
+           {CharConst ("inline"), true},
+           {CharConst ("name"), false},
+           {NULL, 0, 0}
+       };
+
+       if (fmotor)
+           setParams (arg, 1, &params, kwlist_motor, &keywords, NULL);
+       else
+           setParams (arg, 1, &params, kwlist_file, &keywords, NULL);
+       filename = eval_str (params[0], mlenv);
+       if (eval_bool (keywords[0], mlenv))     // serial
+           storetype = F_SERIAL;
+       if (eval_bool (keywords[1], mlenv))     // named
+           storetype = F_NAMED;
+       if (eval_bool (keywords[2], mlenv))     // static
+           storetype = F_STATIC;
+       if (eval_bool (keywords[3], mlenv))     // type
+           type = eval_str (keywords[3], mlenv);
+       if (! fmotor) {
+           finline = eval_bool (keywords[4], mlenv); // inline
+           dispname = omitCtrl (eval_str (keywords[5], mlenv)); // name
+       }
+
+       if (type.empty ()) {
+           type = mimetype (getExt (filename));
+       } else if (! checkMimeType (type)) {
+           type = mimetype (type);
+       }
+
+       switch (storetype) {
+       case F_SERIAL:
+           src = mlenv->env->path_store_file (filename);
+           break;
+       case F_NAMED:
+           src = mlenv->env->path_storage_file (filename);
+           break;
+       case F_STATIC:
+           if (mlenv->env->path_resource (filename, src)) {
+           } else {
+               throw (filename + uErrorNotFound);
+           }
+           break;
+       default:
+           throw (uErrorNoStore);
+       }
+    };
+};
+
+/*DOC:
+===response-motor===
+ (response-motor HTMLFILE [#serial | #named | #static | :serial BOOL | :named BOOL | :static BOOL] [:type MIME_TYPE]) -> NIL
+
+*/
+//#AFUNC       response-motor  ml_response_motor
+MNode*  ml_response_motor (MNode* cell, MlEnv* mlenv) {
+    ResponseParam  par;
+
+    par.proc (cell, mlenv, true);
+
+    if (par.type.size () == 0) {
+       mlenv->env->doMotor (par.src);
+    } else {
+       mlenv->env->doMotor (par.src, par.type);
+    }
+    mlenv->breakProg ();
+    
+    return NULL;
+}
+/*DOC:
+===response-file===
+ (response-file FILENAME [#serial | #named | #static | :serial BOOL | :named BOOL | :static BOOL] [:type MIME_TYPE] [#inline | :inline BOOL] [:name NAME]) -> NIL
+
+*/
+//#AFUNC       response-file   ml_response_file
+MNode*  ml_response_file (MNode* cell, MlEnv* mlenv) {
+    ResponseParam  par (mlenv);
+
+    par.proc (cell, mlenv, false);
+
+    if (par.finline || par.dispname.length () > 0) {
+       mlenv->env->outputFile (par.src, par.type, par.finline, par.dispname);
+    } else {
+       mlenv->env->outputFile (par.src, par.type);
+    }
+    mlenv->breakProg ();
+    
+    return NULL;
+}
+
+/*DOC:
+===filesize===
+ (filesize FILENAME [#serial | #named | :serial BOOL | :named BOOL]) -> INTEGER or NIL
+
+*/
+//#AFUNC       filesize        ml_filesize
+MNode*  ml_filesize (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  filename;
+    ustring  type;
+    enum {
+       F_NONE,
+       F_SERIAL,
+       F_NAMED,
+    }  storetype;
+    ustring  src;
+    off_t  size;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    static paramList  kwlist[] = {
+       {CharConst ("serial"), true},
+       {CharConst ("named"), true},
+       {NULL, 0, 0}
+    };
+
+    if (mlenv->env->storagedir.length () > 0) {
+       storetype = F_NAMED;
+    } else if (mlenv->env->storedir.length () > 0) {
+       storetype = F_SERIAL;
+    } else {
+       storetype = F_NONE;
+    }
+    
+    setParams (arg, 1, &params, kwlist, &keywords, NULL);
+    filename = eval_str (params[0], mlenv);
+    if (eval_bool (keywords[0], mlenv))        // serial
+       storetype = F_SERIAL;
+    if (eval_bool (keywords[1], mlenv))        // named
+       storetype = F_NAMED;
+
+    switch (storetype) {
+    case F_SERIAL:
+       src = mlenv->env->path_store_file (filename);
+       break;
+    case F_NAMED:
+       src = mlenv->env->path_storage_file (filename);
+       break;
+    default:
+       throw (uErrorNoStore);
+    }
+
+    if (fileSize (src, size)) {
+       return newMNode_num (size);
+    } else {
+       return NULL;
+    }
+}
diff --git a/modules/ml-store.h b/modules/ml-store.h
new file mode 100644 (file)
index 0000000..ac98135
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef ML_STORE_H
+#define ML_STORE_H
+
+class  MNode;
+class  MlEnv;
+
+void  newStoreSerial (MlEnv* mlenv);
+
+MNode*  ml_new_xserial (MNode* cell, MlEnv* mlenv);
+MNode*  ml_set_xserial (MNode* cell, MlEnv* mlenv);
+MNode*  ml_read_file (MNode* cell, MlEnv* mlenv);
+MNode*  ml_write_file (MNode* cell, MlEnv* mlenv);
+MNode*  ml_clean_store (MNode* cell, MlEnv* mlenv);
+MNode*  ml_datastore (MNode* cell, MlEnv* mlenv);
+MNode*  ml_set_storage (MNode* cell, MlEnv* mlenv);
+MNode*  ml_save_file (MNode* cell, MlEnv* mlenv);
+MNode*  ml_restore_file (MNode* cell, MlEnv* mlenv);
+MNode*  ml_delete_file (MNode* cell, MlEnv* mlenv);
+MNode*  ml_response_motor (MNode* cell, MlEnv* mlenv);
+MNode*  ml_response_file (MNode* cell, MlEnv* mlenv);
+MNode*  ml_filesize (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_STORE_H */
diff --git a/modules/ml-string.cc b/modules/ml-string.cc
new file mode 100644 (file)
index 0000000..19d4e9f
--- /dev/null
@@ -0,0 +1,515 @@
+#include "ml-string.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "motoroutput.h"
+#include "util_const.h"
+#include "util_string.h"
+#include "expr.h"
+#include "utf8.h"
+#include "utf16.h"
+#include "ustring.h"
+#include <boost/lexical_cast.hpp>
+#include <exception>
+
+/*DOC:
+==string function==
+
+*/
+/*DOC:
+===concat===
+ (concat STRING...) -> STRING
+
+*/
+//#AFUNC       concat  ml_concat
+//#WIKIFUNC    concat  ml_concat
+MNode*  ml_concat (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustringPtr  a1;
+
+    a1 = new ustring;
+    a1.p->reserve (256);
+
+    while (arg) {
+       a1.p->append (eval_str (arg->car (), mlenv));
+       nextNode (arg);
+    }
+    return newMNode_str (a1.release ());
+}
+
+/*DOC:
+===megabyte===
+ (megabyte NUMBER) -> STRING
+
+*/
+//#AFUNC       megabyte        ml_megabyte
+//#WIKIFUNC    megabyte        ml_megabyte
+MNode*  ml_megabyte (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    double  val;
+    ustring  u;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    val = eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (val < 900) {
+       u = boost::lexical_cast<ustring> (val);
+       return newMNode_str (new ustring (u));
+    }
+    val = floor (val / 1024. * 10.) / 10.;
+    if (val < 900) {
+       u = boost::lexical_cast<ustring> (val);
+       u.append (CharConst ("K"));
+       return newMNode_str (new ustring (u));
+    }
+    val = floor (val / 1024. * 10. ) / 10.;
+    if (val < 900) {
+       u = boost::lexical_cast<ustring> (val);
+       u.append (CharConst ("M"));
+       return newMNode_str (new ustring (u));
+    }
+    val = floor (val / 1024. * 10.) / 10.;
+    if (val < 900) {
+       u = boost::lexical_cast<ustring> (val);
+       u.append (CharConst ("G"));
+       return newMNode_str (new ustring (u));
+    }
+    val = floor (val / 1024. * 10.) / 10.;
+    if (val < 900) {
+       u = boost::lexical_cast<ustring> (val);
+       u.append (CharConst ("T"));
+       return newMNode_str (new ustring (u));
+    }
+    val = floor (val / 1024. * 10.) / 10.;
+    u = boost::lexical_cast<ustring> (val);
+    u.append (CharConst ("P"));
+    return newMNode_str (new ustring (u));
+}
+
+/*DOC:
+===c3===
+ (c3 INTEGER) -> STRING
+
+*/
+//#AFUNC       c3      ml_c3
+//#WIKIFUNC    c3      ml_c3
+MNode*  ml_c3 (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  u;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    u = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    return newMNode_str (new ustring (c3 (u)));
+}
+
+/*DOC:
+===regexp-match===
+ (regexp-match REGEX TEXT [#i | :i BOOL]) -> BOOL
+
+*/
+//#AFUNC       regexp-match    ml_regexp_match
+//#WIKIFUNC    regexp-match    ml_regexp_match
+MNode*  ml_regexp_match (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  reg;
+    ustring  t;
+    boost::wregex::flag_type  f = boost::regex_constants::normal;
+    bool  ans;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    static paramList  kwlist[] = {
+       {CharConst ("i"), true},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 2, &params, kwlist, &keywords, NULL);
+    reg = eval_str (params[0], mlenv);
+    t = eval_str (params[1], mlenv);
+    if (eval_bool (keywords[0], mlenv))
+       f |= boost::regex_constants::icase;
+
+    mlenv->env->regtext = utow (t);
+    std::wstring  wreg = utow (reg);
+    boost::wregex  wre (wreg, f);
+    ans = regex_search (mlenv->env->regtext, mlenv->env->regmatch, wre, boost::regex_constants::match_single_line);
+
+    return newMNode_bool (ans);
+}
+
+/*DOC:
+===match-string===
+ (match-string NUM) -> STRING
+
+*/
+//#AFUNC       match-string    ml_match_string
+//#WIKIFUNC    match-string    ml_match_string
+MNode*  ml_match_string (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    int  n;
+    MNode*  ans = NULL;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    n = eval_int (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (0 <= n && n < mlenv->env->regmatch.size ()) {
+       ans = newMNode_str (new ustring (wtou (std::wstring (mlenv->env->regmatch[n].first, mlenv->env->regmatch[n].second))));
+    }
+
+    return ans;
+}
+
+/*DOC:
+===prematch===
+ (prematch) -> STRING
+
+*/
+//#AFUNC       prematch        ml_prematch
+//#WIKIFUNC    prematch        ml_prematch
+MNode*  ml_prematch (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNode*  ans = NULL;
+    std::wstring::const_iterator  b = mlenv->env->regtext.begin ();
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    ans = newMNode_str (new ustring (wtou (std::wstring (b, mlenv->env->regmatch[0].first))));
+
+    return ans;
+}
+
+/*DOC:
+===postmatch===
+ (postmatch) -> STRING
+
+*/
+//#AFUNC       postmatch       ml_postmatch
+//#WIKIFUNC    postmatch       ml_postmatch
+MNode*  ml_postmatch (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNode*  ans = NULL;
+    std::wstring::const_iterator  e = mlenv->env->regtext.end ();
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    ans = newMNode_str (new ustring (wtou (std::wstring (mlenv->env->regmatch[0].second, e))));
+
+    return ans;
+}
+
+/*DOC:
+===string-filter===
+ (string-filter REGEX STRING [#i | :i BOOL]) -> STRING
+
+*/
+//#AFUNC       string-filter   ml_string_filter
+//#WIKIFUNC    string-filter   ml_string_filter
+MNode*  ml_string_filter (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  reg;
+    ustring  t;
+    boost::wregex::flag_type  f = boost::regex_constants::normal;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    static paramList  kwlist[] = {
+       {CharConst ("i"), true},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 2, &params, kwlist, &keywords, NULL);
+    reg = eval_str (params[0], mlenv);
+    t = eval_str (params[1], mlenv);
+    if (eval_bool (keywords[0], mlenv))
+       f |= boost::regex_constants::icase;
+
+    mlenv->env->regtext = utow (t);
+    std::wstring  wreg = utow (reg);
+    boost::wregex  wre (wreg, f);
+    if (regex_search (mlenv->env->regtext, mlenv->env->regmatch, wre, boost::regex_constants::match_single_line)) {
+       return newMNode_str (new ustring (wtou (std::wstring (mlenv->env->regmatch[0].first, mlenv->env->regmatch[0].second))));
+    } else {
+       return newMNode_str (new ustring);
+    }
+}
+
+/*DOC:
+===split===
+ (split REGEX STRING) -> STRING_LIST
+
+*/
+//#AFUNC       split   ml_split
+//#WIKIFUNC    split   ml_split
+MNode*  ml_split (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  reg;
+    ustring  t;
+    MNodeList  ans;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    reg = eval_str (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    t = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    {
+       std::wstring  wt = utow (t);
+       std::wstring  wreg = utow (reg);
+       boost::wregex  wre (wreg);
+       WSplitter  sp (wt, wre);
+
+       while (sp.next ()) {
+           ans.append (newMNode_str (new ustring (sp.cur ())));
+       }
+    }
+    return ans.release ();
+}
+
+/*DOC:
+===array-join===
+ (array-join VARIABLE TEXT) -> STRING
+
+*/
+//#AFUNC       array-join      ml_array_join
+//#WIKIFUNC    array-join      ml_array_join
+MNode*  ml_array_join (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  var;
+    ustring  sep;
+    ustring  u;
+    ustring*  ans;
+    int  i, n;
+    MNode*  v;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    var = eval_str (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    sep = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    n = mlenv->getArySize (var);
+    ans = new ustring;
+    for (i = 1; i <= n; i ++) {
+       if (i > 1)
+           ans->append (sep);
+       v = mlenv->getAry (var, i);
+       if (v)
+           ans->append (v->to_string ());
+    }
+    return newMNode_str (ans);
+}
+
+/*DOC:
+===password-match===
+ (password-match PASSWORD CRYPT) -> BOOL
+
+*/
+//#AFUNC       password-match  ml_password_match
+//#WIKIFUNC    password-match  ml_password_match
+MNode*  ml_password_match (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  pass;
+    ustring  cpass;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    pass = eval_str (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    cpass = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    return newMNode_bool (passMatch (pass, cpass));
+}
+
+/*DOC:
+===password-crypt===
+ (password-crypt PASSWORD) -> STRING
+
+*/
+//#AFUNC       password-crypt  ml_password_crypt
+//#WIKIFUNC    password-crypt  ml_password_crypt
+MNode*  ml_password_crypt (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  pass;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    pass = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    return newMNode_str (new ustring (passCrypt (pass)));
+}
+
+/*DOC:
+===substring===
+ (substring STR INDEX LENGTH) -> STRING
+ (substring STR INDEX) -> STRING
+
+*/
+//#AFUNC       substring       ml_substring
+//#WIKIFUNC    substring       ml_substring
+MNode*  ml_substring (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  str;
+    size_t  index;
+    size_t  length;
+    int  mode;
+    ustring*  ans;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    str = eval_str (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    index = eval_int (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg) {
+       mode = 3;
+       length = eval_int (arg->car (), mlenv);
+       nextNode (arg);
+    } else {
+       mode = 2;
+    }
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    ans = new ustring;
+    substring (str, index, length, mode == 3, *ans);
+    return newMNode_str (ans);
+}
+    
+/*DOC:
+===length===
+ (length STR) -> NUMBER
+
+*/
+//#AFUNC       length  ml_length
+//#WIKIFUNC    length  ml_length
+MNode*  ml_length (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  str;
+    size_t  ans;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    str = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    ans = strlength (str);
+    return newMNode_num (ans);
+}
+
+/*DOC:
+===pad0===
+ (pad0 NUMBER STRING) -> STRING
+ (pad0 NUMBER STRING_LIST) -> STRING_LIST
+ (pad0 NUMBER_LIST STRING_LIST) -> STRING_LIST
+
+*/
+//#AFUNC       pad0    ml_pad0
+//#WIKIFUNC    pad0    ml_pad0
+MNode*  ml_pad0 (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  num;
+    MNodePtr  val;
+    int  n;
+    MNode*  np;
+    MNode*  vp;
+    MNodeList  ans;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    num = np = eval (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    val = vp = eval (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    n = 0;
+    if (vp) {
+       if (vp->isCons ()) {
+           while (vp) {
+               if (np) {
+                   if (np->isCons ()) {
+                       n = to_int (np->car ());
+                       np = np->cdr ();
+                   } else {
+                       n = to_int (np);
+                   }
+               }
+               ans.append (newMNode_str (new ustring (zeroPad (n, to_string (vp->car ())))));
+               vp = vp->cdr ();
+               if (vp && ! vp->isCons ())
+                   vp = NULL;
+           }
+           return ans.release ();
+       } else {
+           if (np) {
+               if (np->isCons ())
+                   n = to_int (np->car ());
+               else
+                   n = to_int (np);
+           }
+           return newMNode_str (new ustring (zeroPad (n, to_string (vp))));
+       }
+    }
+
+    return NULL;
+}
+
+/*DOC:
+===ellipsis===
+ (ellipsis NUM STRING) -> STRING
+
+*/
+//#AFUNC       ellipsis        ml_ellipsis
+//#WIKIFUNC    ellipsis        ml_ellipsis
+MNode*  ml_ellipsis (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    int  num;
+    ustring  str;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    num = eval_int (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    str = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    str = ellipsis (str, num);
+    return newMNode_str (new ustring (str));
+}
+
diff --git a/modules/ml-string.h b/modules/ml-string.h
new file mode 100644 (file)
index 0000000..0d8cad5
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef ML_STRING_H
+#define ML_STRING_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_concat (MNode* cell, MlEnv* mlenv);
+MNode*  ml_megabyte (MNode* cell, MlEnv* mlenv);
+MNode*  ml_c3 (MNode* cell, MlEnv* mlenv);
+MNode*  ml_regexp_match (MNode* cell, MlEnv* mlenv);
+MNode*  ml_string_filter (MNode* cell, MlEnv* mlenv);
+MNode*  ml_match_string (MNode* cell, MlEnv* mlenv);
+MNode*  ml_prematch (MNode* cell, MlEnv* mlenv);
+MNode*  ml_postmatch (MNode* cell, MlEnv* mlenv);
+MNode*  ml_split (MNode* cell, MlEnv* mlenv);
+MNode*  ml_array_join (MNode* cell, MlEnv* mlenv);
+MNode*  ml_password_match (MNode* cell, MlEnv* mlenv);
+MNode*  ml_password_crypt (MNode* cell, MlEnv* mlenv);
+MNode*  ml_substring (MNode* cell, MlEnv* mlenv);
+MNode*  ml_length (MNode* cell, MlEnv* mlenv);
+MNode*  ml_pad0 (MNode* cell, MlEnv* mlenv);
+MNode*  ml_ellipsis (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_STRING_H */
diff --git a/modules/ml-struct.cc b/modules/ml-struct.cc
new file mode 100644 (file)
index 0000000..b23f641
--- /dev/null
@@ -0,0 +1,782 @@
+#include "ml-struct.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "expr.h"
+#include "util_const.h"
+#include "util_string.h"
+#include "ustring.h"
+#include <exception>
+
+/*DOC:
+==lisp structure==
+
+*/
+/*DOC:
+===quote===
+ (quote LIST...) -> LIST
+
+*/
+//#AFUNC       quote   ml_quote
+//#WIKIFUNC    quote   ml_quote
+MNode*  ml_quote (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    
+    if (arg->car ()) {
+       switch (arg->car ()->type) {
+       case MNode::MC_NIL:
+           return NULL;
+       default:
+           return arg->car ();
+       }
+    }
+    return NULL;
+}
+
+/*DOC:
+===list===
+ (list LIST...) -> LIST
+
+*/
+//#AFUNC       list    ml_list
+//#WIKIFUNC    list    ml_list
+MNode*  ml_list (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodeList  ans;
+
+    while (arg) {
+       ans.append (eval (arg->car (), mlenv));
+       nextNode (arg);
+    }
+    return ans.release ();
+}
+
+/*DOC:
+===if===
+ (if EXPR THEN_FUNCTION ELSE_FUNCTION_BODY...) -> LAST VALUE
+
+*/
+//#AFUNC       if      ml_if
+//#WIKIFUNC    if      ml_if
+MNode*  ml_if (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  ans;
+    bool  r;
+
+    if (arg) {
+       r = eval_bool (arg->car (), mlenv);
+       nextNode (arg);
+       if (r) {
+           if (arg) {
+#ifdef DEBUG
+               mlenv->logSexp (arg->car ());
+#endif /* DEBUG */
+               ans = eval (arg->car (), mlenv);
+           }
+       } else {
+           nextNode (arg);
+           if (arg) {
+//             ans = progn (arg, mlenv, cell->car ());
+               ans = progn (arg, mlenv);
+               if (mlenv->breaksym ()
+                   && (mlenv->breaksym ()->isNil () || eq (mlenv->breaksym (), cell->car ()))) {
+                   mlenv->breaksym = NULL;
+               }
+           }
+       }
+    }
+    return ans.release ();
+}
+
+/*DOC:
+===cond===
+ (cond (EXPR BODY...)...) -> LAST VALUE
+
+*/
+//#AFUNC       cond    ml_cond
+//#WIKIFUNC    cond    ml_cond
+MNode*  ml_cond (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  ans;
+    MNode*  a;
+
+    while (arg) {
+       a = arg->car ();
+       if (a == NULL)
+           throw (uNil + uErrorBadType);
+       if (! a->isCons ())
+           throw (a->dump_string () + uErrorBadType);
+       if (eval_bool (a->car (), mlenv)) {
+//         ans = progn (a->cdr (), mlenv, cell->car ());
+           ans = progn (a->cdr (), mlenv);
+           if (mlenv->breaksym ()
+               && (mlenv->breaksym ()->isNil () || eq (mlenv->breaksym (), cell->car ()))) {
+               mlenv->breaksym = NULL;
+           }
+           break;
+       }
+       nextNode (arg);
+    }
+
+    return ans.release ();
+}
+
+/*DOC:
+===progn===
+ (progn BODY...) -> LAST VALUE
+
+*/
+//#AFUNC       progn   ml_progn
+//#WIKIFUNC    progn   ml_progn
+MNode*  ml_progn (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  ans;
+
+//    ans =  progn (arg, mlenv, cell->car ());
+    ans =  progn (arg, mlenv);
+    if (mlenv->breaksym ()
+       && (mlenv->breaksym ()->isNil () || eq (mlenv->breaksym (), cell->car ()))) {
+       mlenv->breaksym = NULL;
+    }
+    return ans.release ();
+}
+
+/*DOC:
+===repeat===
+ (repeat VARIABLE FROM TO [:step ADD] [:array VARLIST] BODY...) -> LAST VALUE
+
+*/
+//#AFUNC       repeat  ml_repeat
+MNode*  ml_repeat (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  iv;
+    double  from;
+    double  to;
+    double  step = 1.;
+    std::vector<ustring>  lv;
+    double  i;
+    MNodePtr  h;
+    MNodePtr  ans;
+    int  j;
+    bool  kp;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    MNode*  rest;
+    static paramList  kwlist[] = {
+       {CharConst ("step"), false},
+       {CharConst ("array"), false},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 3, &params, kwlist, &keywords, &rest);
+    iv = eval_str (params[0], mlenv);
+    from = eval_double (params[1], mlenv);
+    to = eval_double (params[2], mlenv);
+    if (keywords[0])
+       step = eval_double (keywords[0], mlenv);
+    if (keywords[1]) {
+       MNode*  a;
+       h = eval (keywords[1], mlenv);
+       a = h ();
+       if (a && a->isSym ()) {
+           lv.push_back (a->to_string ());
+       } else if (a && a->isCons ()) {
+           while (a) {
+               lv.push_back (to_string (a->car ()));
+               nextNode (a);
+           }
+       } else {
+           throw (to_string (h ()) + ustring (": bad argument."));
+       }
+    }
+
+    if (step > 0) {
+       kp = true;
+       for (i = from; kp && i <= to; i += step) {
+           h = newMNode_num (i);
+           mlenv->setVar (iv, h.p);
+           if (i > 0)
+               for (j = 0; j < lv.size (); j ++)
+                   mlenv->setVar (lv[j], mlenv->getAry (lv[j], (size_t)i));
+           ans = progn (rest, mlenv);
+           if (mlenv->breaksym ()) {
+               if (mlenv->breaksym ()->isNil () || eq (mlenv->breaksym (), cell->car ()))
+                   mlenv->breaksym = NULL;
+               kp = false;
+           }
+           if (i > 0)
+               for (j = 0; j < lv.size (); j ++)
+                   mlenv->setAry (lv[j], (size_t)i, mlenv->getVar (lv[j]));
+           if (mlenv->qtimeup ())
+               break;
+       }
+    } else if (step < 0) {
+       kp = true;
+       for (i = from; kp && i >= to; i += step) {
+           h = newMNode_num (i);
+           mlenv->setVar (iv, h.p);
+           if (i > 0)
+               for (j = 0; j < lv.size (); j ++)
+                   mlenv->setVar (lv[j], mlenv->getAry (lv[j], (size_t)i));
+           ans = progn (rest, mlenv);
+           if (mlenv->breaksym ()) {
+               if (mlenv->breaksym ()->isNil () || eq (mlenv->breaksym (), cell->car ()))
+                   mlenv->breaksym = NULL;
+               kp = false;
+           }
+           if (i > 0)
+               for (j = 0; j < lv.size (); j ++)
+                   mlenv->setAry (lv[j], (size_t)i, mlenv->getVar (lv[j]));
+           if (mlenv->qtimeup ())
+               break;
+       }
+    }
+
+    return mlenv->retval = ans ();
+}
+
+/*DOC:
+===doarray===
+ (doarray '(ARRAY_VARIABLE ...) [:index VARIABLE] [:setvar '(VARIABLE...)] BODY...) -> LAST VALUE
+
+*/
+//#AFUNC       doarray ml_doarray
+MNode*  ml_doarray (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    std::vector<ustring>  lv;
+    std::vector<ustring>  setvar;
+    MNodePtr  vlist;
+    ustring  iv;
+    size_t  i, n;
+    int  it, nlv, nsv;
+    ustring  val;
+    MNodePtr  ans;
+    MNodePtr  h;
+    bool  kp;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    MNode*  rest;
+    static paramList  kwlist[] = {
+       {CharConst ("index"), false},
+       {CharConst ("setvar"), false},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 1, &params, kwlist, &keywords, &rest);
+    vlist = eval (params[0], mlenv);
+    if (vlist () && vlist ()->isSym ()) {
+       lv.push_back (vlist ()->to_string ());
+    } else if (vlist () && vlist ()->isCons ()) {
+       MNode*  a = vlist ();
+       while (a && a->isCons ()) {
+           lv.push_back (to_string (a->car ()));
+           nextNode (a);
+       }
+    } else {
+       throw (to_string (vlist ()) + ustring (": bad argument."));
+    }
+    if (keywords[0])
+       iv = eval_str (keywords[0], mlenv);
+    if (keywords[1]) {
+       vlist = eval (keywords[1], mlenv);
+       if (vlist () && vlist ()->isSym ()) {
+           setvar.push_back (to_string (vlist ()));
+       } else if (vlist () && vlist ()->isCons ()) {
+           MNode*  a = vlist ();
+           while (a && a->isCons ()) {
+               setvar.push_back (to_string (a->car ()));
+               nextNode (a);
+           }
+       } else {
+           throw (ustring (CharConst (":setvar ")) + to_string (vlist ()) + ustring (": bad argument."));
+       }
+    }
+
+    nlv = lv.size ();
+    nsv = setvar.size ();
+    if (nsv > 0 && nlv > nsv)
+       nlv = nsv;
+
+    if (nlv > 0) {
+       kp = true;
+       n = mlenv->getArySize (lv[0]);
+       for (i = 1; kp && i <= n; i ++) {
+           if (nsv == 0) {
+               for (it = 0; it < nlv; it ++) {
+                   mlenv->setVar (lv[it], mlenv->getAry (lv[it], i));
+               }
+           } else {
+               for (it = 0; it < nlv; it ++) {
+                   mlenv->setVar (setvar[it], mlenv->getAry (lv[it], i));
+               }
+               for (; it < nsv; it ++) {
+                   mlenv->setVar (setvar[it], NULL);
+               }
+           }
+           if (iv.size () > 0) {
+               h = newMNode_num (i);
+               mlenv->setVar (iv, h ());
+           }
+           ans = progn (rest, mlenv);
+           if (mlenv->breaksym ()) {
+               if (mlenv->breaksym ()->isNil () || eq (mlenv->breaksym (), cell->car ()))
+                   mlenv->breaksym = NULL;
+               kp = false;
+           }
+           if (nsv == 0) {
+               for (it = 0; it < nlv; it ++) {
+                   mlenv->setAry (lv[it], i, mlenv->getVar (lv[it]));
+               }
+           } else {
+               for (it = 0; it < nlv; it ++) {
+                   mlenv->setAry (lv[it], i, mlenv->getVar (setvar[it]));
+               }
+           }
+       }
+       for (it = 0; it < nlv; it ++) {
+           mlenv->setArySize (lv[it], n);
+       }
+    }
+
+    return ans.release ();
+}
+
+/*DOC:
+===dolist===
+ (dolist '(VARIABLE...) '(LIST...) [:index VAR] BODY...) -> LAST VALUE
+
+*/
+//#AFUNC       dolist  ml_dolist
+MNode*  ml_dolist (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    std::vector<ustring>  lv;
+    int  it, iu;
+    MNodePtr  vlist;
+    MNodePtr  list;
+    ustring  iv;
+    std::vector<MNode*>  llv;
+    MNode*  a;
+    MNodePtr  ans;
+    MNodePtr  h;
+    int  i, n;
+    bool  kp;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    MNode*  rest;
+    static paramList  kwlist[] = {
+       {CharConst ("index"), false},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 2, &params, kwlist, &keywords, &rest);
+    vlist = eval (params[0], mlenv);
+    if (vlist () && vlist ()->isSym ()) {
+       lv.push_back (vlist ()->to_string ());
+    } else if (vlist () && vlist ()->isCons ()) {
+       a = vlist ();
+       while (a && a->isCons ()) {
+           lv.push_back (to_string (a->car ()));
+           nextNode (a);
+       }
+    } else {
+       throw (to_string (vlist ()) + ustring (": bad argument."));
+    }
+    list = eval (params[1], mlenv);
+    if (keywords[0])
+       iv = eval_str (keywords[0], mlenv);
+    
+    a = list ();
+    iu = lv.size ();
+    for (i = 0; i < iu; i ++) {
+       if (a)
+           llv.push_back (a->car ());
+       else
+           llv.push_back (NULL);
+       nextNode (a);
+    }
+
+    if (iu > 0 && llv.size () > 0) {
+       kp = true;
+       for (i = 1; kp && llv[0]; i ++) {
+           for (it = 0; it < iu; it ++) {
+               if (llv[it])
+                   mlenv->setVar (lv[it], llv[it]->car ());
+               else
+                   mlenv->setVar (lv[it], NULL);
+               nextNode (llv[it]);
+           }
+           ans = progn (rest, mlenv);
+           if (mlenv->breaksym ()) {
+               if (mlenv->breaksym ()->isNil () || eq (mlenv->breaksym (), cell->car ()))
+                   mlenv->breaksym = NULL;
+               kp = false;
+           }
+       }
+    }
+
+    return ans.release ();
+}
+
+/*DOC:
+===while===
+ (while EXPR BODY...) -> LAST VALUE
+
+*/
+//#AFUNC       while   ml_while
+MNode*  ml_while (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  ans;
+    MNode*  exp;
+
+    if (arg) {
+       exp = arg->car ();
+       nextNode (arg);
+       while (eval_bool (exp, mlenv)) {
+//         ans = progn (arg, mlenv, cell->car ());
+           ans = progn (arg, mlenv);
+           if (mlenv->breaksym ()) {
+               if (mlenv->breaksym ()->isNil () || eq (mlenv->breaksym (), cell->car ()))
+                   mlenv->breaksym = NULL;
+               break;
+           }
+       }
+    }
+    return ans.release ();
+}
+
+/*DOC:
+===break===
+ (break FUNCTION-NAME VALUE) -> NULL
+
+*/
+//#AFUNC       break   ml_break
+MNode*  ml_break (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  sym;
+    MNodePtr  val;
+
+    if (!arg) {
+       sym = new MNode;
+    } else {
+       sym = eval (arg->car (), mlenv);
+       nextNode (arg);
+       if (arg) {
+           val = eval (arg->car (), mlenv);
+           nextNode (arg);
+           if (arg)
+               throw (uErrorWrongNumber);
+       }
+    }
+    mlenv->breaksym = sym.release ();
+    mlenv->breakval = val.release ();
+
+    return NULL;
+}
+
+/*DOC:
+===apply===
+ (apply FUNCTION-NAME VALUE... LIST) -> ANY
+
+*/
+//#AFUNC       apply   ml_apply
+MNode*  ml_apply (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  fn;
+    MNodeList  list;
+    MNodePtr  h;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    fn = eval (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       h = eval (arg->car (), mlenv);
+       nextNode (arg);
+       if (arg) {
+           list.append (h.release ());
+       } else {
+           MNode*  a = h ();
+           while (a && a->isCons ()) {
+               list.append (a->car ());
+               nextNode (a);
+           }
+           if (a) {
+               throw (uErrorSyntax);
+           }
+       }
+    }
+    if (! fn ()) {
+       throw (uErrorSyntax);
+    } else if (isLambda (fn ())) {
+       return execDefun (mlenv, fn (), list ());
+    } else if (fn ()->isSym ()) {
+       MNodePtr  f;
+       f = new MNode ();
+       f ()->set_car (fn ());
+       f ()->set_cdr (list ());
+       return eval (f (), mlenv);
+    } else {
+       throw (uErrorSyntax);
+    }
+    return NULL;
+}
+
+/*DOC:
+===funcall===
+ (funcall LAMBDA PARAMS...)
+
+*/
+//#AFUNC       funcall ml_funcall
+MNode*  ml_funcall (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  fn;
+    MNodeList  list;
+    MNodePtr  h;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    fn = eval (arg->car (), mlenv);
+    nextNode (arg);
+    while (arg) {
+       h = eval (arg->car (), mlenv);
+       nextNode (arg);
+       list.append (h.release ());
+    }
+
+    if (! fn ()) {
+       throw (uErrorSyntax);
+    } else if (isLambda (fn ())) {
+       return execDefun (mlenv, fn (), list ());
+    } else if (fn ()->isSym ()) {
+       MNodePtr  f;
+       f = new MNode ();
+       f ()->set_car (fn ());
+       f ()->set_cdr (list ());
+       return eval (f (), mlenv);
+    } else {
+       throw (uErrorSyntax);
+    }
+    return NULL;
+}
+
+/*DOC:
+===eval===
+ (eval ANY) -> ANY
+
+*/
+//#AFUNC       eval    ml_eval
+MNode*  ml_eval (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  h;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    h = eval (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+    return eval (h (), mlenv);
+}
+
+/*DOC:
+===car===
+ (car LIST) -> LIST
+
+*/
+//#AFUNC       car     ml_car
+//#WIKIFUNC    car     ml_car
+MNode*  ml_car (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  h;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    h = eval (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (h () && h ()->isCons ()) {
+       return mlenv->retval = h ()->car ();
+    } else {
+       return NULL;
+    }
+}
+
+/*DOC:
+===cdr===
+ (cdr LIST) -> LIST
+
+*/
+//#AFUNC       cdr     ml_cdr
+//#WIKIFUNC    cdr     ml_cdr
+MNode*  ml_cdr (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  h;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    h = eval (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (h () && h ()->isCons ()) {
+       return mlenv->retval = h ()->cdr ();
+    } else {
+       return NULL;
+    }
+}
+
+/*DOC:
+===nth===
+ (nth N LIST) -> LIST
+
+*/
+//#AFUNC       nth     ml_nth
+//#WIKIFUNC    nth     ml_nth
+MNode*  ml_nth (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    int  n;
+    MNodePtr  h;
+    MNode*  a;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    n = eval_int (arg->car (), mlenv);
+    nextNode (arg);
+    h = eval (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    a = h ();
+    while (n > 0 && a && a->isCons ()) {
+       n --;
+       nextNode (a);
+    }
+
+    if (a && a->isCons ())
+       return mlenv->retval = a->car ();
+    else
+       return NULL;
+}
+
+
+/*DOC:
+===setcar===
+ (setcar CELL NEWCAR) -> NEWCAR
+
+*/
+//#AFUNC       setcar  ml_setcar
+MNode*  ml_setcar (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  c;
+    MNodePtr  newcar;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    c = eval (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    newcar = eval (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (! c ()->isCons ())
+       throw (c ()->dump_string () + uErrorBadType);
+    c ()->unset_car ();
+    c ()->set_car (newcar ());
+
+    return mlenv->retval = newcar ();
+}
+
+/*DOC:
+===setcdr===
+ (setcdr CELL NEWCDR) -> NEWCDR
+
+*/
+//#AFUNC       setcdr  ml_setcdr
+MNode*  ml_setcdr (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  c;
+    MNodePtr  newcdr;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    c = eval (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    newcdr = eval (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (! c ()->isCons ())
+       throw (c ()->dump_string () + uErrorBadType);
+    c ()->unset_cdr ();
+    c ()->set_cdr (newcdr ());
+
+    return mlenv->retval = newcdr ();
+}
+
+/*DOC:
+===cons===
+ (cons CAR CDR) -> CONS
+
+*/
+//#AFUNC       cons    ml_cons
+MNode*  ml_cons (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  car;
+    MNodePtr  cdr;
+    MNodePtr  ans;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    car = eval (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    cdr = eval (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    ans = new MNode;
+    ans ()->set_car (car ());
+    ans ()->set_cdr (cdr ());
+
+    return mlenv->retval = ans ();
+}
+
+/*DOC:
+===append===
+ (append LIST...) -> LIST
+
+*/
+//#AFUNC       append  ml_append
+//#WIKIFUNC    append  ml_append
+MNode*  ml_append (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  h;
+    MNodeList  list;
+    MNode*  a;
+
+    while (arg) {
+       h = eval (arg->car (), mlenv);
+       nextNode (arg);
+       if (arg) {
+           a = h ();
+           while (a && a->isCons ()) {
+               list.append (a->car ());
+               nextNode (a);
+           }
+       } else {
+           list.set_cdr_cut (h ());
+       }
+    }
+
+    return mlenv->retval = list ();
+}
+
diff --git a/modules/ml-struct.h b/modules/ml-struct.h
new file mode 100644 (file)
index 0000000..36e0160
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef ML_STRUCT_H
+#define ML_STRUCT_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_quote (MNode* cell, MlEnv* mlenv);
+MNode*  ml_list (MNode* cell, MlEnv* mlenv);
+MNode*  ml_if (MNode* cell, MlEnv* mlenv);
+MNode*  ml_cond (MNode* cell, MlEnv* mlenv);
+MNode*  ml_progn (MNode* cell, MlEnv* mlenv);
+MNode*  ml_repeat (MNode* cell, MlEnv* mlenv);
+MNode*  ml_doarray (MNode* cell, MlEnv* mlenv);
+MNode*  ml_dolist (MNode* cell, MlEnv* mlenv);
+MNode*  ml_while (MNode* cell, MlEnv* mlenv);
+MNode*  ml_break (MNode* cell, MlEnv* mlenv);
+MNode*  ml_apply (MNode* cell, MlEnv* mlenv);
+MNode*  ml_funcall (MNode* cell, MlEnv* mlenv);
+MNode*  ml_eval (MNode* cell, MlEnv* mlenv);
+MNode*  ml_car (MNode* cell, MlEnv* mlenv);
+MNode*  ml_cdr (MNode* cell, MlEnv* mlenv);
+MNode*  ml_nth (MNode* cell, MlEnv* mlenv);
+MNode*  ml_setcar (MNode* cell, MlEnv* mlenv);
+MNode*  ml_setcdr (MNode* cell, MlEnv* mlenv);
+MNode*  ml_cons (MNode* cell, MlEnv* mlenv);
+MNode*  ml_append (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_STRUCT_H */
diff --git a/modules/ml-time.cc b/modules/ml-time.cc
new file mode 100644 (file)
index 0000000..eb277c6
--- /dev/null
@@ -0,0 +1,196 @@
+#include "ml-time.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "expr.h"
+#include "util_const.h"
+#include "util_time.h"
+#include <exception>
+#include <time.h>
+#include <string.h>
+
+/*DOC:
+==time function==
+
+*/
+/*DOC:
+===now===
+ (now) -> INTEGER
+
+*/
+//#AFUNC       now     ml_now
+//#WIKIFUNC    now     ml_now
+MNode*  ml_now (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    time_t  tm;
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    tm = now ();
+    return newMNode_num (tm);
+}
+
+/*DOC:
+===to-date===
+ (to-date INTEGER) -> (YEAR MONTH DAY HOUR MINUTE SECOND)
+
+*/
+//#AFUNC       to-date ml_datetime3
+//#WIKIFUNC    to-date ml_datetime3
+MNode*  ml_datetime3 (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  ans;
+    time_t  tm;
+    struct tm  v;
+    MNode*  c;
+
+    if (!arg)
+       throw (uErrorWrongNumber);
+    tm = (time_t)eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    localtime_r (&tm, &v);
+    
+    ans = new MNode;
+    c = ans ();
+    c->set_car (newMNode_num (v.tm_year + 1900));
+    newMNodeCdr (c);
+    c->set_car (newMNode_num (v.tm_mon + 1));
+    newMNodeCdr (c);
+    c->set_car (newMNode_num (v.tm_mday));
+    newMNodeCdr (c);
+    c->set_car (newMNode_num (v.tm_hour));
+    newMNodeCdr (c);
+    c->set_car (newMNode_num (v.tm_min));
+    newMNodeCdr (c);
+    c->set_car (newMNode_num (v.tm_sec));
+
+    return ans.release ();
+}
+
+/*DOC:
+===to-date4===
+ (to-date4 INTEGER) -> (YEAR MONTH DAY WEEK)
+
+*/
+//#AFUNC       to-date4        ml_date4
+//#WIKIFUNC    to-date4        ml_date4
+MNode*  ml_date4 (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  ans;
+    time_t  tm;
+    struct tm  v;
+    MNode*  c;
+
+    if (!arg)
+       throw (uErrorWrongNumber);
+    tm = (time_t)eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    localtime_r (&tm, &v);
+    
+    ans = new MNode;
+    c = ans ();
+    c->set_car (newMNode_num (v.tm_year + 1900));
+    newMNodeCdr (c);
+    c->set_car (newMNode_num (v.tm_mon + 1));
+    newMNodeCdr (c);
+    c->set_car (newMNode_num (v.tm_mday));
+    newMNodeCdr (c);
+    c->set_car (newMNode_num (v.tm_wday));
+
+    return ans.release ();
+}
+
+/*DOC:
+===to-time===
+ (to-time INTEGER) -> (HOUR MINUTE SECOND)
+
+*/
+//#AFUNC       to-time ml_time3
+//#WIKIFUNC    to-time ml_time3
+MNode*  ml_time3 (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  ans;
+    time_t  tm;
+    struct tm  v;
+    MNode*  c;
+
+    if (!arg)
+       throw (uErrorWrongNumber);
+    tm = (time_t)eval_double (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    localtime_r (&tm, &v);
+    
+    ans = new MNode;
+    c = ans ();
+    c->set_car (newMNode_num (v.tm_hour));
+    newMNodeCdr (c);
+    c->set_car (newMNode_num (v.tm_min));
+    newMNodeCdr (c);
+    c->set_car (newMNode_num (v.tm_sec));
+
+    return ans.release ();
+}
+
+/*DOC:
+===date-to-time===
+ (date-to-time YEAR MONTH DAY [HOUR [MINUTE [SECOND]]]) -> NUMBER
+ (date-to-time (YEAR MONTH DAY [HOUR [MINUTE [SECOND]]])) -> NUMBER
+
+*/
+//#AFUNC       date-to-time    ml_datetotime
+//#WIKIFUNC    date-to-time    ml_datetotime
+MNode*  ml_datetotime (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    struct tm  tm;
+    MNodePtr  v;
+
+    memset (&tm, 0, sizeof (tm));
+
+    if (!arg)
+       throw (uErrorWrongNumber);
+
+    v = eval (arg->car (), mlenv);
+    nextNode (arg);
+    if (v () && v ()->isCons ()) {
+       if (arg)
+           throw (uErrorWrongNumber);
+       arg = v ();
+       tm.tm_year = eval_int (arg->car (), mlenv) - 1900;
+       nextNodeNonNil (arg);
+    } else {
+       tm.tm_year = to_int (v ()) - 1900;
+       if (!arg)
+           throw (uErrorWrongNumber);
+    }
+    tm.tm_mon = eval_int (arg->car (), mlenv) - 1;
+    nextNodeNonNil (arg);
+    tm.tm_mday = eval_int (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg) {
+       tm.tm_hour = eval_int (arg->car (), mlenv);
+       nextNode (arg);
+    }
+    if (arg) {
+       tm.tm_min = eval_int (arg->car (), mlenv);
+       nextNode (arg);
+    }
+    if (arg) {
+       tm.tm_sec = eval_int (arg->car (), mlenv);
+       nextNode (arg);
+    }
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    return newMNode_num (mktime (&tm));
+}
+
diff --git a/modules/ml-time.h b/modules/ml-time.h
new file mode 100644 (file)
index 0000000..47a8c07
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef ML_TIME_H
+#define ML_TIME_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_now (MNode* cell, MlEnv* mlenv);
+MNode*  ml_datetime3 (MNode* cell, MlEnv* mlenv);
+MNode*  ml_date4 (MNode* cell, MlEnv* mlenv);
+MNode*  ml_time3 (MNode* cell, MlEnv* mlenv);
+MNode*  ml_datetotime (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_TIME_H */
diff --git a/modules/ml-variable.cc b/modules/ml-variable.cc
new file mode 100644 (file)
index 0000000..efa74f0
--- /dev/null
@@ -0,0 +1,406 @@
+#include "ml-variable.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "util_const.h"
+#include "util_check.h"
+#include "ustring.h"
+#include "expr.h"
+#include <exception>
+
+/*DOC:
+==variable accessing==
+
+*/
+
+static void  setvar (MNode* name, MNode* val, MlEnv* mlenv) {
+    if (name) {
+       ustring  a = name->to_string ();
+       mlenv->setVar2 (a, val);
+    }
+}
+
+static void  setLocalVar (ustring& name, MNode* val, MlEnv* mlenv) {
+    if (checkAry (name)) {
+       ustring  na (name.begin () + 1, name.end ());
+       mlenv->defineLocalVar (na);
+       mlenv->setAry (na, val);
+    } else {
+       mlenv->setLocalVar (name, val);
+    }
+}
+
+/*DOC:
+===setvar===
+ (setvar VARIABLE_or_ARRAY VALUE [VARIABLE_or_ARRAY VALUE]...) -> LAST_VALUE
+
+*/
+//#AFUNC       setvar  ml_setvar
+//#WIKIFUNC    setvar  ml_setvar
+MNode*  ml_setvar (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  ans;
+
+    while (arg) {
+       MNodePtr  v1;
+       MNodePtr  v2;
+       MNode*  a1;
+       MNode*  a2;
+
+       v1 = eval (arg->car (), mlenv);
+       nextNode (arg);
+       if (arg) {
+           v2 = eval (arg->car (), mlenv);
+           nextNode (arg);
+       } else {
+           v2 = NULL;
+       }
+       if (v1 ()) {
+           if (v1 ()->isCons ()) {
+               a1 = v1 ();
+               a2 = v2 ();
+               if (isNil (a2)) {
+               } else if (a2->isCons ()) {
+                   while (a1 && a1->isCons () && a2 && a2->isCons ()) {
+                       setvar (a1->car (), a2->car (), mlenv);
+                       nextNode (a1);
+                       nextNode (a2);
+                   }
+               } else {
+                   throw (ustring (CharConst ("setting a scalar value to a list of variables.")));
+               }
+               while (a1 && a1->isCons ()) {
+                   setvar (a1->car (), NULL, mlenv);
+                   nextNode (a1);
+               }
+           } else {            // v1() is atom
+               setvar (v1 (), v2 (), mlenv);
+           }
+           ans = v2;
+       } else {                // v1() == NULL
+           // nothing
+       }
+    }
+    return ans.release ();
+}
+
+/*DOC:
+===setevar===
+ (setevar VARIABLE ...) -> VARIABLE
+
+*/
+//#AFUNC       setevar ml_setevar
+MNode*  ml_setevar (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  a;
+    MNodePtr  p;
+
+    while (arg) {
+//     a = eval_str (arg->car (), mlenv);
+       p = eval (arg->car (), mlenv);
+       a = to_string (p ());
+       nextNode (arg);
+       if (mlenv->env) {
+           mlenv->env->setErrorVar (a);
+       }
+    }
+    return NULL;
+}
+
+/*DOC:
+===let===
+ (let VARLIST BODY ...) -> LAST VALUE
+
+*/
+//#AFUNC       let     ml_let
+MNode*  ml_let (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNode*  varlist;
+    MNodePtr  ans;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    varlist = arg->car ();
+    nextNode (arg);
+
+    mlenv->beginLocal ();
+    if (varlist && varlist->isCons ()) {
+       MNode*  v;
+       MNodePtr  p;
+       ustring*  name;
+
+       while (varlist) {
+           v = varlist->car ();
+           if (v) {
+               switch (v->type) {
+               case MNode::MC_CONS:
+                   if (v->car ()->isSym ()) {
+                       name = v->car ()->sym;
+                       nextNode (v);
+                       p = eval (v->car (), mlenv);
+//                     mlenv->setLocalVar (*name, p ());
+                       setLocalVar (*name, p (), mlenv);
+                   }
+                   break;
+               case MNode::MC_SYM:
+                   name = v->sym;
+                   mlenv->setLocalVar (*name, NULL);
+                   break;
+               default:
+                   throw (v->dump_string_short () + ": bad type.");
+               }
+           }
+           nextNode (varlist);
+       }
+    }
+
+//    ans = progn (arg, mlenv, cell->car ());
+    ans = progn (arg, mlenv);
+    if (mlenv->breaksym ()
+       && (mlenv->breaksym ()->isNil () || eq (mlenv->breaksym (), cell->car ()))) {
+       mlenv->breaksym = NULL;
+    }
+    mlenv->endLocal ();
+
+    return ans.release ();
+}
+
+/*DOC:
+===getvar===
+ (getvar VARIABLE) -> VALUE
+
+*/
+//#AFUNC       getvar  ml_getvar
+//#WIKIFUNC    getvar  ml_getvar
+MNode*  ml_getvar (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+    ustring  val;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    name = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+    return mlenv->getVar (name);
+}
+
+/*DOC:
+===push===
+ (push ARRAY VALUE...) -> NIL
+
+*/
+//#AFUNC       push    ml_push
+//#WIKIFUNC    push    ml_push
+MNode*  ml_push (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  var;
+    int  n;
+    MNodePtr  v;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    var = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (checkAry (var))
+       var = ustring (var.begin () + 1, var.end ());
+    n = mlenv->getArySize (var);
+    while (arg) {
+       v = eval (arg->car (), mlenv);
+       nextNode (arg);
+       n ++;
+       mlenv->setAry (var, n, v ());
+    }
+    mlenv->setArySize (var, n);
+
+    return NULL;
+}
+
+/*DOC:
+===pop===
+ (pop ARRAY) -> VALUE
+
+*/
+//#AFUNC       pop     ml_pop
+//#WIKIFUNC    pop     ml_pop
+MNode*  ml_pop (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  var;
+    int  n;
+    MNodePtr  ans;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    var = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (checkAry (var))
+       var = ustring (var.begin () + 1, var.end ());
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    n = mlenv->getArySize (var);
+    if (n > 0) {
+       ans = mlenv->getAry (var, n);
+       mlenv->setAry (var, n, NULL);
+       mlenv->setArySize (var, n - 1);
+    }
+    return ans.release ();
+}
+
+/*DOC:
+===shift===
+ (shift ARRAY) -> VALUE
+
+*/
+//#AFUNC       shift   ml_shift
+//#WIKIFUNC    shift   ml_shift
+MNode*  ml_shift (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  var;
+    MNode*  ans = NULL;
+    int  i, n;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    var = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (checkAry (var))
+       var = ustring (var.begin () + 1, var.end ());
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    n = mlenv->getArySize (var);
+    if (n > 0) {
+       ans = mlenv->getAry (var, 1);
+       for (i = 2; i <= n; i ++) {
+           mlenv->setAry (var, i - 1, mlenv->getAry (var, i));
+       }
+       mlenv->setAry (var, n, NULL);
+       mlenv->setArySize (var, n - 1);
+    }
+    return ans;
+}
+
+/*DOC:
+===unshift===
+ (unshift ARRAY VALUE...) -> NIL
+
+*/
+//#AFUNC       unshift ml_unshift
+//#WIKIFUNC    unshift ml_unshift
+MNode*  ml_unshift (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNode*  a;
+    ustring  var;
+    int  i, n, n0;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    var = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (checkAry (var))
+       var = ustring (var.begin () + 1, var.end ());
+    n0 = 0;
+    a = arg;
+    while (a) {
+       n0 ++;
+       nextNode (a);
+    }
+
+    n = mlenv->getArySize (var);
+    for (i = n; i >= 1; i --) {
+       mlenv->setAry (var, i + n0, mlenv->getAry (var, i));
+    }
+    mlenv->setArySize (var, n + n0);
+    
+    i = 0;
+    while (arg) {
+       i ++;
+       mlenv->setAry (var, i, eval (arg->car (), mlenv));
+       nextNode (arg);
+    }
+
+    return NULL;
+}
+
+/*DOC:
+===array===
+ (array ARRAY)
+
+*/
+//#AFUNC       array   ml_array
+//#WIKIFUNC    array   ml_array
+MNode*  ml_array (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  var;
+//    MNode  ans;
+//    MNode*  y = &ans;
+    MNodeList  ans;
+    int  i, n;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    var = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (checkAry (var))
+       var = ustring (var.begin () + 1, var.end ());
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    n = mlenv->getArySize (var);
+    if (n > 0) {
+       for (i = 1; i <= n; i ++) {
+//         newMNodeCdr (y);
+//         y->set_car (mlenv->getAry (var, i));
+           ans.append (mlenv->getAry (var, i));
+       }
+    }
+//    if (ans.isCons ())
+//     return ans.release_cdr ();
+//    else
+//     return NULL;
+    return ans.release ();
+}
+
+/*DOC:
+===array-index===
+ (array-index ARRAY VALUE)
+
+*/
+//#AFUNC       array-index     ml_array_index
+//#WIKIFUNC    array-index     ml_array_index
+MNode*  ml_array_index (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  var;
+    MNodePtr  value;
+    int  i, n;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    var = eval_str (arg->car (), mlenv);
+    nextNodeNonNil (arg);
+    if (checkAry (var))
+       var = ustring (var.begin () + 1, var.end ());
+    value = eval (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    n = mlenv->getArySize (var);
+    if (n > 0) {
+       for (i = 1; i <= n; i ++) {
+           if (eq (mlenv->getAry (var, i), value ())) {
+               return newMNode_num (i);
+           }
+       }
+    }
+    return newMNode_num (0.);
+}
diff --git a/modules/ml-variable.h b/modules/ml-variable.h
new file mode 100644 (file)
index 0000000..81309fd
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef ML_VARIABLE_H
+#define ML_VARIABLE_H
+
+#include "ml.h"
+class  MlEnv;
+
+MNode*  ml_setvar (MNode* cell, MlEnv* mlenv);
+MNode*  ml_setevar (MNode* cell, MlEnv* mlenv);
+MNode*  ml_let (MNode* cell, MlEnv* mlenv);
+MNode*  ml_getvar (MNode* cell, MlEnv* mlenv);
+MNode*  ml_push (MNode* cell, MlEnv* mlenv);
+MNode*  ml_pop (MNode* cell, MlEnv* mlenv);
+MNode*  ml_shift (MNode* cell, MlEnv* mlenv);
+MNode*  ml_unshift (MNode* cell, MlEnv* mlenv);
+MNode*  ml_array (MNode* cell, MlEnv* mlenv);
+MNode*  ml_array_index (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_VARIABLE_H */
diff --git a/modules/ml-wiki.cc b/modules/ml-wiki.cc
new file mode 100644 (file)
index 0000000..9fb178d
--- /dev/null
@@ -0,0 +1,257 @@
+#include "ml-wiki.h"
+#include "mlenv.h"
+#include "wikiformat.h"
+#include "wikienv.h"
+#include "motorenv.h"
+#include "expr.h"
+#include "util_const.h"
+#include "util_check.h"
+#include "ustring.h"
+#include <exception>
+
+/*DOC:
+==wiki processing==
+
+*/
+/*DOC:
+===wiki===
+ (wiki [#superuser | :superuser BOOL] [#protect | :protect BOOL] TEXT...) -> NIL
+
+*/
+//#AFUNC       wiki    ml_wiki
+MNode*  ml_wiki (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  text;
+    bool  super = false;
+    bool  protect = false;
+    std::vector<MNode*>  params;
+    std::vector<MNode*>  keywords;
+    MNode*  rest;
+    static paramList  kwlist[] = {
+       {CharConst ("superuser"), true},
+       {CharConst ("protect"), true},
+       {NULL, 0, 0}
+    };
+
+    setParams (arg, 0, &params, kwlist, &keywords, &rest);
+    if (keywords[0])
+       super = eval_bool (keywords[0], mlenv);
+    if (keywords[1])
+       protect = eval_bool (keywords[1], mlenv);
+
+    while (rest) {
+       text.append (eval_str (rest->car (), mlenv));
+       nextNode (rest);
+    }
+
+    if (! mlenv->env->responseDone)
+       mlenv->env->standardResponse_html ();
+
+    {
+       WikiFormat  w (mlenv->env, protect);
+       w.compile (text, super);
+       w.output ();
+    }
+
+    return NULL;
+}
+
+static ustring*  wikivar (const ustring& name) {
+    ustring*  ans = new ustring;
+
+    ans->reserve (uWiki.length () + name.length ());
+    ans->append (uWiki);
+    ans->append (name);
+
+    return ans;
+}
+
+static ustring*  wikivar_ary (const ustring& name) {
+    ustring*  ans = new ustring;
+
+    ans->reserve (uWiki.length () + name.length () + 1);
+    ans->append (CharConst ("@"));
+    ans->append (uWiki);
+    ans->append (name);
+
+    return ans;
+}
+
+/*DOC:
+===wikivar===
+ (wikivar NAME) -> STRING
+ (wikivar@ NAME) -> STRING
+
+Return string prefixed with 'wiki_'.
+Return string prefixed with '@wiki_'.
+
+*/
+//#AFUNC       wikivar ml_wikivar
+//#AFUNC       wikivar@        ml_wikivar_ary
+MNode*  ml_wikivar (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    name = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    return newMNode_str (wikivar (name));
+}
+
+MNode*  ml_wikivar_ary (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  name;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+
+    name = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    return newMNode_str (wikivar_ary (name));
+}
+
+static void  setWikivar (const ustring& name, MlEnv* mlenv) {
+    ustring*  var;
+
+//    if (name.length () > 0 && name[0] == '@') {
+    if (checkAry (name)) {
+       int  i, n;
+       ustring  s (name.begin () + 1, name.end ());
+       var = wikivar (s);
+       n = mlenv->getArySize (s);
+       for (i = 1; i <= n; i ++)
+           mlenv->setAry (*var, i, mlenv->getAry (s, i));
+       mlenv->setArySize (*var, n);
+    } else {
+       var = wikivar (name);
+       mlenv->setVar (*var, mlenv->getVar (name));
+    }
+    delete var;
+}
+
+/*DOC:
+===set-wikivar===
+ (set-wikivar [VARIABLE|@ARRAY|LIST] ...) -> NIL
+
+*/
+//#AFUNC       set-wikivar     ml_set_wikivar
+MNode*  ml_set_wikivar (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  x;
+    ustring  name;
+
+    while (arg) {
+       x = eval (arg->car (), mlenv);
+       nextNode (arg);
+       if (x ()) {
+           if (x ()->isCons ()) {
+               MNode*  a = x ();
+               while (a && a->isCons ()) {
+                   setWikivar (to_string (a->car ()), mlenv);
+                   nextNode (a);
+               }
+           } else {
+               setWikivar (to_string (x ()), mlenv);
+           }
+       }
+    }
+
+    return NULL;
+}
+
+/*DOC:
+===defun-wiki-inline===
+ (defun-wiki-inline FNAME (ARGS...) BLOCK...) -> NIL
+ &[;[FNAME:ARGS:...]]
+
+===defun-wiki-inline2===
+ (defun-wiki-inline2 FNAME (ARG2 ARGS...) BLOCK...) -> NIL
+ &[;[FNAME:ARGS:... ARGS2]]
+
+*/
+//#AFUNC       defun-wiki-inline       ml_defun_wiki_inline
+//#AFUNC       defun-wiki-inline2      ml_defun_wiki_inline2
+MNode*  ml_defun_wiki_inline (MNode* cell, MlEnv* mlenv) {
+    ustring  name;
+    MNode*  sexp = NULL;
+
+    checkDefun (cell->cdr (), name, sexp);
+    mlenv->env->wikienv->wikiFunc_setVar (name, newLambda (sexp));
+
+    return NULL;
+}
+
+MNode*  ml_defun_wiki_inline2 (MNode* cell, MlEnv* mlenv) {
+    ustring  name;
+    MNode*  sexp = NULL;
+
+    checkDefun (cell->cdr (), name, sexp);
+    mlenv->env->wikienv->wikiFunc2_setVar (name, newLambda (sexp));
+
+    return NULL;
+}
+
+/*DOC:
+===defun-wiki-link===
+ (defun-wiki-link FNAME (ARGS...) BLOCK...) -> NIL
+ |select:NAME:FNAME:ARGS:...|
+ {form:POST:FNAME:ARGS:...
+
+*/
+//#AFUNC       defun-wiki-link ml_defun_wiki_link
+MNode*  ml_defun_wiki_link (MNode* cell, MlEnv* mlenv) {
+    ustring  name;
+    MNode*  sexp = NULL;
+
+    checkDefun (cell->cdr (), name, sexp);
+    mlenv->env->wikienv->wikiLink_setVar (name, newLambda (sexp));
+
+    return NULL;
+}
+
+/*DOC:
+===defun-wiki-command===
+ (defun-wiki-command FNAME (ARGS...) BLOCK...) -> NIL
+ $FNAME:ARGS:...
+
+*/
+//#AFUNC       defun-wiki-command      ml_defun_wiki_command
+MNode*  ml_defun_wiki_command (MNode* cell, MlEnv* mlenv) {
+    ustring  name;
+    MNode*  sexp = NULL;
+
+    checkDefun (cell->cdr (), name, sexp);
+    mlenv->env->wikienv->wikiCmd_setVar (name, newLambda (sexp));
+
+    return NULL;
+}
+
+/*DOC:
+===wiki-guestuser-function===
+ (wiki-guestuser-function FNAME...) -> NIL
+
+*/
+//#AFUNC       wiki-guestuser-function ml_wiki_guestuser_function
+MNode*  ml_wiki_guestuser_function (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  v;
+
+    while (arg) {
+       v = eval_str (arg->car (), mlenv);
+       nextNode (arg);
+       if (mlenv->validName (v))
+           mlenv->env->wikienv->wikiGuestFunc.set (v);
+    }
+
+    return NULL;
+}
diff --git a/modules/ml-wiki.h b/modules/ml-wiki.h
new file mode 100644 (file)
index 0000000..e557787
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef ML_WIKI_H
+#define ML_WIKI_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_wiki (MNode* cell, MlEnv* mlenv);
+MNode*  ml_wikivar (MNode* cell, MlEnv* mlenv);
+MNode*  ml_wikivar_ary (MNode* cell, MlEnv* mlenv);
+MNode*  ml_set_wikivar (MNode* cell, MlEnv* mlenv);
+MNode*  ml_defun_wiki_inline (MNode* cell, MlEnv* mlenv);
+MNode*  ml_defun_wiki_inline2 (MNode* cell, MlEnv* mlenv);
+MNode*  ml_defun_wiki_link (MNode* cell, MlEnv* mlenv);
+MNode*  ml_defun_wiki_command (MNode* cell, MlEnv* mlenv);
+MNode*  ml_set_wiki_special_prefix (MNode* cell, MlEnv* mlenv);
+MNode*  ml_wiki_guestuser_function (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_WIKI_H */
diff --git a/modules/ml-xml.cc b/modules/ml-xml.cc
new file mode 100644 (file)
index 0000000..ca6d151
--- /dev/null
@@ -0,0 +1,245 @@
+#include "ml-xml.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "motoroutput.h"
+#include "ustring.h"
+#include "expr.h"
+#include "utf8.h"
+#include "filemacro.h"
+#include <expat.h>
+#include <exception>
+#include <vector>
+
+/*DOC:
+==xml module==
+
+*/
+
+class  expat {
+public:
+    XML_Parser  xp;
+    void*  buff;
+    static const size_t  buffsize = 65536;
+
+    expat () {
+       xp = XML_ParserCreate (NULL);
+       buff = XML_GetBuffer (xp, buffsize);
+    };
+    virtual  ~expat () {
+       XML_ParserFree (xp);
+       xp = NULL;
+    };
+    virtual int  parse (size_t len, int isfinal) {
+       return XML_ParseBuffer (xp, len, isfinal);
+    };
+    virtual void  setUserData (void* userData) {
+       XML_SetUserData(xp, userData);
+    };
+    virtual void  setElementHandler (XML_StartElementHandler start, XML_EndElementHandler end, XML_CharacterDataHandler text) {
+       XML_SetElementHandler (xp, start, end);
+       XML_SetCharacterDataHandler (xp, text);
+    }
+};
+
+class  xmlReaderData {
+public:
+    MNodePtr  ans;
+    typedef  enum {
+       M_START,
+       M_TEXT,
+       M_CHILD,
+    }  mode_t;
+    std::vector<MNode*>  ptr;
+    std::vector<mode_t>  mode;
+    ustring  text;
+
+    xmlReaderData () {};
+    virtual  ~xmlReaderData () {
+       while (ptr.size () > 0) {
+           delete ptr.back ();
+           ptr.pop_back ();
+       }
+    };
+    virtual void  newNode (const XML_Char* name, const XML_Char** atts) {
+       MNodeList  a;
+       a.append (newMNode_sym (new ustring (fixUTF8 (ustring (name)))));
+       a.append (NULL);
+       for (; *atts; ) {
+           a.append (newMNode_sym (new ustring (fixUTF8 (ustring (*atts)))));
+           atts ++;
+           a.append (newMNode_str (new ustring (fixUTF8 (ustring (*atts)))));
+           atts ++;
+       }
+       ptr.push_back (a.release ());
+       mode.push_back (M_START);
+       text.resize (0);
+    };
+    virtual void  addText (const ustring& s) {
+       mode_t  m = mode.back ();
+
+       if (m == M_START || m == M_TEXT) {
+           text.append (s);
+           mode.back () = M_TEXT;
+       }
+    };
+    virtual void  endNode () {
+       MNode*  a = ptr.back ();
+       mode_t  m = mode.back ();
+
+       ptr.pop_back ();
+       mode.pop_back ();
+
+       switch (m) {
+       case M_START:
+           break;
+       case M_TEXT:
+           a->cdr ()->set_car (newMNode_str (new ustring (text)));
+           break;
+       case M_CHILD:
+           break;
+       default:;
+       }
+       if (ptr.size () == 0) {
+           ans = a;
+       } else {
+           if (! ptr.back ()->cdr ()->car ()) {
+               MNode*  c = new MNode;
+               c->set_car (a);
+               ptr.back ()->cdr ()->set_car (c);
+           } else if (ptr.back ()->cdr ()->car ()->isCons ()) {
+               MNode*  c = ptr.back ()->cdr ()->car ();
+               MNode*  d = new MNode;
+
+               while (c->cdr ()) {
+                   c = c->cdr ();
+               }
+               c->set_cdr (d);
+               d->set_car (a);
+           } else {
+               assert (0);
+           }
+           mode.back () = M_CHILD;
+       }
+    };
+};
+
+static void  xmlstart (void* data, const XML_Char* name, const XML_Char** atts) {
+    xmlReaderData*  reader = (xmlReaderData*)data;
+    reader->newNode (name, atts);
+}
+
+static void  xmlend (void* data, const XML_Char* name) {
+    xmlReaderData*  reader = (xmlReaderData*)data;
+    reader->endNode ();
+}
+
+static void  xmltext (void *data, const XML_Char *s, int len) {
+    xmlReaderData*  reader = (xmlReaderData*)data;
+    reader->addText (ustring (s, len));
+}
+
+/*DOC:
+===xml-read-file===
+ (xml-read-file FILENAME) -> LIST
+
+*/
+//#AFUNC       xml-read-file   ml_xml_read_file
+MNode*  ml_xml_read_file (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    ustring  filename;
+    xmlReaderData  data;
+    expat  exp;
+    FileMacro  fp;
+    size_t  s;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    filename = eval_str (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (mlenv->env->storedir.empty ())
+       throw (uErrorNoStore);
+    filename = mlenv->env->path_store_file (filename);
+
+    if (fp.openRead (filename.c_str ())) {
+       exp.setElementHandler (xmlstart, xmlend, xmltext);
+       exp.setUserData (&data);
+       while (1) {
+           s = fp.read (exp.buff, exp.buffsize);
+           exp.parse (s, (s == 0));
+           if (s == 0)
+               break;
+       }
+       fp.close ();
+    }
+
+#ifdef DEBUG2
+    std::cerr << data.ans ()->dump_string () << "\n";
+#endif /* DEBUG */
+
+    return data.ans.release ();
+}
+
+static void  xmlWrite (MotorOutput* out, MNode* list) {
+    if (list && list->isCons () && list->car ()->isSym ()) {
+       MNode*  c;
+       MNode*  d;
+       ustring*  name = list->car ()->sym;
+       list = list->cdr ();
+       if (list && list->isCons ()) {
+           c = list->car ();
+           d = list->cdr ();
+           out->out (CharConst ("<"))->outHTML (*name);
+           while (d && d->isCons () && d->car ()->isSym () && d->cdr ()->isCons () && d->cdr ()->car ()->isStr ()) {
+               out->out (CharConst (" "))->outHTML (*d->car ()->sym)->out (CharConst ("=\""))->outHTML (*d->cdr ()->car ()->str)->out (CharConst ("\""));
+               d = d->cdr ()->cdr ();
+           }
+           if (c) {
+               out->out (CharConst (">"));
+               if (c->isStr ()) {
+                   out->outHTML (*c->str);
+               } else {
+                   out->out (CharConst ("\n"));
+                   while (c && c->isCons ()) {
+                       xmlWrite (out, c->car ());
+                       c = c->cdr ();
+                   }
+               }
+               out->out (CharConst ("</"))->out (*name)->out (CharConst (">\n"));
+           } else {
+               out->out (CharConst ("/>\n"));
+           }
+       }
+    }
+}
+
+/*DOC:
+===xml-output===
+ (xml-output LIST) -> NIL
+
+*/
+//#AFUNC       xml-output      ml_xml_output
+MNode*  ml_xml_output (MNode* cell, MlEnv* mlenv) {
+    MNode*  arg = cell->cdr ();
+    MNodePtr  xml;
+
+    if (! arg)
+       throw (uErrorWrongNumber);
+    xml = eval (arg->car (), mlenv);
+    nextNode (arg);
+    if (arg)
+       throw (uErrorWrongNumber);
+
+    if (! mlenv->env->responseDone)
+       mlenv->env->standardResponse (ustring (CharConst (kMIME_XML)), ustring (CharConst (kCODE_UTF8)));
+
+    MotorOutputOStream  out;
+
+    out.out (CharConst ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"));
+    xmlWrite (&out, xml ());
+
+    return NULL;
+}
diff --git a/modules/ml-xml.h b/modules/ml-xml.h
new file mode 100644 (file)
index 0000000..6b3ce6e
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef ML_XML_H
+#define ML_XML_H
+
+class  MNode;
+class  MlEnv;
+
+MNode*  ml_xml_read_file (MNode* cell, MlEnv* mlenv);
+MNode*  ml_xml_output (MNode* cell, MlEnv* mlenv);
+
+#endif /* ML_XML_H */
diff --git a/modules/motor-function.cc b/modules/motor-function.cc
new file mode 100644 (file)
index 0000000..f776725
--- /dev/null
@@ -0,0 +1,126 @@
+#include "motor-function.h"
+#include "expr.h"
+#include "ml.h"
+#include "mlenv.h"
+#include "motorenv.h"
+#include "wikiformat.h"
+#include "ustring.h"
+#include <vector>
+
+/*DOC:
+===eval===
+ &[;[eval:''NAME'']]
+ &[;[eval:''NAME'':''ARGS'':...]]
+
+*/
+//#MTFUNC      eval    mf_eval
+void  mf_eval (std::vector<ustring>& args, MlEnv* mlenv) {
+    MNodePtr  vargs;
+    MNodePtr  ans;
+
+    if (args.size () > 0) {
+       vargs = buildArgs (1, args);
+       MNode*  sexp = mlenv->env->motorCall.getVar (args[0]);
+       if (isLambda (sexp)) {
+           ans = execDefun (mlenv, sexp, vargs ());
+       }
+    }
+}
+
+/*DOC:
+===js===
+ &[;[js:''VAR'']]
+
+*/
+//#MTFUNC      js      mf_js
+void  mf_js (std::vector<ustring>& args, MlEnv* mlenv) {
+    ustring  u;
+    MNode*  v;
+
+    if (args.size () == 1) {
+       v = mlenv->getVar (args[0]);
+       if (v) {
+           u = v->to_string ();
+           u = utf16Encode (u);
+           mlenv->env->output->out (u);
+       }
+    } else {
+       throw (uErrorWrongNumber);              // ***
+    }
+}
+
+/*DOC:
+===url===
+ &[;[url:''VAR'']]
+
+*/
+//#MTFUNC      url     mf_url
+void  mf_url (std::vector<ustring>& args, MlEnv* mlenv) {
+    MNode*  v;
+    ustring  u;
+
+    if (args.size () == 1) {
+       v = mlenv->getVar (args[0]);
+       if (v) {
+           u = v->to_string ();
+           u = urlEncode (u);
+           mlenv->env->output->out (u);
+       }
+    } else {
+       throw (uErrorWrongNumber);              // ***
+    }
+}
+
+/*DOC:
+===pad0===
+ &[;[pad0:''NUM'':''VAR'']]
+
+*/
+//#MTFUNC      pad0    mf_pad0
+void  mf_pad0 (std::vector<ustring>& args, MlEnv* mlenv) {
+    int  n;
+    ustring  t;
+
+    if (args.size () > 1) {
+       n = strtol (args[0]);
+       t = to_string (mlenv->getVar (args[1]));
+       mlenv->env->output->outHTML (zeroPad (n, t));
+    }
+}
+
+/*DOC:
+===wiki===
+ &[;[wiki:''VAR'']]
+ &[;[wiki:''VAR'':superuser]]
+ &[;[wiki:''VAR'':protect]]
+
+*/
+//#MTFUNC      wiki    mf_wiki
+void  mf_wiki (std::vector<ustring>& args, MlEnv* mlenv) {
+    ustring  t;
+    bool  super = false;
+    bool  protect = false;
+    MNode*  v;
+    int  i;
+
+    if (args.size () < 1)
+       return;
+    
+    v = mlenv->getVar (args[0]);
+    for (i = 1; i < args.size (); i ++) {
+       if (match (args[i], CharConst ("superuser"))) {
+           super = true;
+       } else if (match (args[i], CharConst ("protect"))) {
+           protect = true;
+       } else {
+           throw (uErrorBadArg);
+       }
+    }
+    if (v) {
+       WikiFormat  w (mlenv->env, protect);
+       t = v->to_string ();
+       w.compile (t, super);
+       w.output ();
+    }
+}
+
diff --git a/modules/motor-function.h b/modules/motor-function.h
new file mode 100644 (file)
index 0000000..6a2533f
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef MOTOR_FUNCTION_H
+#define MOTOR_FUNCTION_H
+
+#include "ustring.h"
+#include <vector>
+
+class  MlEnv;
+
+void  mf_eval (std::vector<ustring>& args, MlEnv* mlenv);
+void  mf_js (std::vector<ustring>& args, MlEnv* mlenv);
+void  mf_url (std::vector<ustring>& args, MlEnv* mlenv);
+void  mf_pad0 (std::vector<ustring>& args, MlEnv* mlenv);
+void  mf_wiki (std::vector<ustring>& args, MlEnv* mlenv);
+
+#endif /* MOTOR_FUNCTION_H */
diff --git a/wiki/wikicmd.cc b/wiki/wikicmd.cc
new file mode 100644 (file)
index 0000000..e54de44
--- /dev/null
@@ -0,0 +1,418 @@
+#include "wikicmd.h"
+#include "wikitable.h"
+#include "wikiformat.h"
+#include "wikienv.h"
+#include "ml.h"
+#include "expr.h"
+#include "motorenv.h"
+#include "motoroutput.h"
+#include "util_const.h"
+#include "util_check.h"
+#include "util_string.h"
+#include "ustring.h"
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <assert.h>
+#include <stdlib.h>
+
+/*DOC:
+==行コマンド==
+
+*/
+/* ============================================================ */
+static void  do_linevec (WikiLine::linevec* block, WikiFormat* wiki) {
+    if (block) {
+       WikiLineScanner  scanner (block);
+       wiki->compileLines (scanner);
+    }
+}
+
+/* ============================================================ */
+/*DOC:
+===$repeat===
+ $repeat:''VAR'':''FROM'':''TO''[:''STEP'']
+ ...
+ $endrepeat
+
+*/
+//#WIKICMD     $repeat $endrepeat      wc_repeat
+void  wc_repeat (WikiLine* wl, WikiFormat* wiki) {
+    uiterator  b = wl->begin;
+    uiterator  e = wl->end;
+    std::vector<ustring>  args;
+    ustring  iv;
+    double  from = 0.;
+    double  to = 0.;
+    double  step = 1.;
+    double  i;
+    MNodePtr  h;
+
+    if (wiki->protectMode && ! wl->fsuper)
+       return;
+
+    wiki->wikiParamRaw (b, e, false, args);
+    wiki->eval (args);
+    if (args.size () == 3 || args.size () == 4) {
+       iv = args[0];
+       from = atof (args[1].c_str ());
+       to = atof (args[2].c_str ());
+       if (args.size () == 4)
+           step = atof (args[3].c_str ());
+    } else {
+       wiki->errorMsg.append (CharConst ("$repeat: wrong number of parameters.\n"));
+    }
+
+    if (step > 0.) {
+       for (i = from; i <= to; i += step) {
+           h = newMNode_num (i);
+           wiki->mlenv->setVar (iv, h ());
+           do_linevec (wl->block, wiki);
+//         if (mlenv->qtimeup ())
+//             break;
+       }
+    } else if (step < 0.) {
+       for (i = from; i >= to; i += step) {
+           h = newMNode_num (i);
+           wiki->mlenv->setVar (iv, h ());
+           do_linevec (wl->block, wiki);
+//         if (wiki->mlenv->qtimeup ())
+//             break;
+       }
+    }
+}
+
+/* ============================================================ */
+/*DOC:
+===$doarray===
+ $doarray:''VAR'',''VAR'',...:''INDEX_VAR''
+ ...
+ $enddoarray
+
+*/
+//#WIKICMD     $doarray        $enddoarray     wc_doarray
+void  wc_doarray (WikiLine* wl, WikiFormat* wiki) {
+    uiterator  b = wl->begin;
+    uiterator  e = wl->end;
+    std::vector<ustring>  args;
+    std::vector<ustring>  lv;
+    ustring  iv;
+    int  i, n, iu, it;
+    MNodePtr  h;
+    static uregex  re (",");
+
+    if (wiki->protectMode && ! wl->fsuper)
+       return;
+
+    wiki->wikiParamRaw (b, e, false, args);
+    wiki->eval (args);
+    if (args.size () == 1 || args.size () == 2) {
+       Splitter  sp (args[0], re);
+       while (sp.next ()) {
+           lv.push_back (clipWhite (sp.begin (), sp.end ()));
+       }
+       if (args.size () == 2) {
+           iv = clipWhite (args[1].begin (), args[1].end ());
+       }
+    } else {
+       wiki->errorMsg.append (CharConst ("$repeat: wrong number of parameters.\n"));
+    }
+    iu = lv.size ();
+    if (iu > 0) {
+       n = wiki->mlenv->getArySize (lv[0]);
+       for (i = 1; i <= n; i ++) {
+           for (it = 0; it < iu; it ++) {
+               wiki->mlenv->setVar (lv[it], wiki->mlenv->getAry (lv[it], i));
+           }
+           if (iv.size () > 0) {
+               h = newMNode_num (i);
+               wiki->mlenv->setVar (iv, h ());
+           }
+           do_linevec (wl->block, wiki);
+           for (it = 0; it < iu; it ++) {
+               wiki->mlenv->setAry (lv[it], i, wiki->mlenv->getVar (lv[it]));
+           }
+       }
+       for (it = 0; it < iu; it ++) {
+           wiki->mlenv->setArySize (lv[it], n);
+       }
+    }
+}
+
+/* ============================================================ */
+/*DOC:
+===$block===
+ $block:''EXPR''
+ ...
+ $elseblock:''EXPR''
+ ...
+ $elseblock
+ ...
+ $endblock
+
+*/
+//#WIKICMD     $block  $elseblock      $endblock       wc_block
+void  wc_block (WikiLine* wl, WikiFormat* wiki) {
+    MotorSexp  ml (wiki->mlenv);
+    ustring  sexp (wl->begin, wl->end);
+    MNodePtr  v;
+    bool  q;
+
+    if (wiki->protectMode && ! wl->fsuper)
+       return;
+
+    if (sexp.empty ()) {
+       q = true;
+    } else {
+       ml.scan (sexp);
+       if (ml.top.isCons ()) {
+           MNode*  arg = ml.top.cdr ();
+           if (arg && arg->isCons ()) {
+               try {
+                   v = eval (arg->car (), wiki->mlenv);
+               } catch (ustring& msg) {
+                   if (wiki->mlenv->currentCell ()) {
+                       wiki->errorMsg.append (wiki->mlenv->currentCell ()->dump_string_short ()).append (CharConst (": "));
+                   }
+                   wiki->errorMsg.append (msg).append (uLF);
+               }
+           }
+           if (v ()) {
+               q = v ()->to_bool ();
+           } else {
+               q = false;
+           }
+       } else {
+           q = true;
+       }
+    }
+    if (q) {
+       do_linevec (wl->block, wiki);
+    } else {
+       WikiLine*  w2 = wl->block2;
+       if (w2) {
+           assert (w2->fn);
+           wl->fn (w2, wiki);
+       }
+    }
+}
+/* ============================================================ */
+//#WIKICMD     $setvar wc_setvar
+void  wc_setvar (WikiLine* wl, WikiFormat* wiki) {
+    MotorSexp  ml (wiki->mlenv);
+    ustring  sexp (wl->begin, wl->end);
+    MNode*  arg;
+    ustring  var;
+    MNodePtr  v;
+
+    if (wiki->protectMode && ! wl->fsuper)
+       return;
+
+    if (! sexp.empty ()) {
+       ml.scan (sexp);
+       if (ml.top.isCons ()) {
+           arg = ml.top.cdr ();
+           if (arg && arg->isCons ()) {
+               var = to_string (arg->car ());
+           }
+//         nextNodeNonNil (arg);
+           nextNode (arg);
+           if (! arg) {
+               wiki->errorMsg.append (CharConst ("$setvar: wrong number of parameters.\n"));
+               return;
+           }
+           try {
+               v = eval (arg->car (), wiki->mlenv);
+           } catch (ustring& msg) {
+               if (wiki->mlenv->currentCell ()) {
+                   wiki->errorMsg.append (wiki->mlenv->currentCell ()->dump_string_short ()).append (CharConst (": "));
+               }
+               wiki->errorMsg.append (msg).append (uLF);
+           }
+       }
+       if (var.size () > 0) {
+//         if (var[0] == '@') {
+           if (checkAry (var)) {
+               ustring  na (var.begin () + 1, var.end ());
+               wiki->mlenv->setAry (na, v ());
+           } else {
+               wiki->mlenv->setVar (var, v ());
+           }
+       }
+    }
+}
+
+/* ============================================================ */
+/*DOC:
+===$eval===
+ $eval:''EXPR''
+
+*/
+//#WIKICMD     $eval   wc_eval
+void  wc_eval (WikiLine* wl, WikiFormat* wiki) {
+    MotorSexp  ml (wiki->mlenv);
+    ustring  sexp (wl->begin, wl->end);
+    MNodePtr  v;
+
+    if (wiki->protectMode && ! wl->fsuper)
+       return;
+
+    if (! sexp.empty ()) {
+       ml.scan (sexp);
+       if (ml.top.isCons ()) {
+           MNode*  arg = ml.top.cdr ();
+           if (arg && arg->isCons ()) {
+               try {
+//                 v = progn (arg, wiki->mlenv, NULL);
+                   v = eval (arg->car (), wiki->mlenv);
+               } catch (ustring& msg) {
+                   if (wiki->mlenv->currentCell ()) {
+                       wiki->errorMsg.append (wiki->mlenv->currentCell ()->dump_string_short ()).append (CharConst (": "));
+                   }
+                   wiki->errorMsg.append (msg).append (uLF);
+               }
+           }
+       }
+    }
+}
+
+/* ============================================================ */
+/*DOC:
+===$insert===
+ $insert:VARIABLE
+ $insert:VARIABLE:==
+ $insert:VARIABLE:superuser
+ $insert:VARIABLE:superuser:==
+// $insert:VARIABLE:guestuser
+
+*/
+//#WIKICMD     $insert wc_insert
+void  wc_insert (WikiLine* wl, WikiFormat* wiki) {
+    uiterator  b = wl->begin;
+    uiterator  e = wl->end;
+    std::vector<ustring>  args;
+    int  hn;
+    bool  super = false;
+    bool  protect;
+    int  i;
+    static uregex  re_eq ("=+");
+    umatch  m;
+
+    if (wiki->env->mlenv) {
+       try {
+           wiki->env->mlenv->inclIncCount ();
+       } catch (ustring& msg) {
+           if (wiki->mlenv->currentCell ()) {
+               wiki->errorMsg.append (wiki->mlenv->currentCell ()->dump_string_short ()).append (CharConst (": "));
+           }
+           wiki->errorMsg.append (msg).append (uLF);
+           return;
+       }
+    }
+
+    wiki->wikiParamRaw (b, e, false, args);
+/*
+    if (args.size () >= 2) {
+       if (usearch (args[1], m, re_eq)) {
+           hn = m[0].second - m[0].first;
+       } else {
+           hn = 0;
+       }
+    } else {
+       hn = 0;
+    }
+*/
+    protect = wiki->protectMode && ! wl->fsuper;
+    hn = 0;
+    for (i = 1; i < args.size (); i ++) {
+       if (! protect && match (args[i], CharConst ("superuser"))) {
+           super = true;
+//     } else if (! protect && match (args[i], CharConst ("guestuser"))) {
+//         super = false;
+//         guest = true;
+       } else if (usearch (args[i], m, re_eq)) {
+           hn = m[0].second - m[0].first;
+           if (hn < 0)
+               hn = 0;
+           if (hn > 5)
+               hn = 5;
+       } else {
+           // bad parameter
+       }
+    }
+    if (args.size () >= 1) {
+       ustring  text = wiki->getVar (args[0]);
+       WikiLine::linevec  bl;
+       wiki->headbase += hn;
+       wiki->pass1 (text, &bl, super);
+       do_linevec (&bl, wiki);
+       wiki->headbase -= hn;
+    }
+    if (wiki->env->mlenv)
+       wiki->env->mlenv->declIncCount ();
+}
+
+/* ============================================================ */
+void  wc_call_defun (WikiLine* wl, WikiFormat* wiki) {
+    umatch  m;
+    ustring  name;
+    uiterator  b, e;
+    MNode*  wf;
+    bool  fx = true;
+
+    b = wl->begin + 1;
+    if (usearch (b, wl->end, m, re_wikicmdsep)) {
+       name = ustring (b, m[0].first);
+       b = m[0].second;
+       e = wl->end;
+    } else {
+       name = ustring (b, wl->end);
+       b = wl->end;
+       e = wl->end;
+    }
+    if (wiki->protectMode && ! wl->fsuper && ! wiki->env->wikienv->wikiGuestFunc.get (name))
+       fx = false;
+
+    if (fx && (wf = wiki->env->wikienv->wikiCmd_getVar (name))) {
+       std::vector<ustring>  args;
+       MNodePtr  vargs;
+       wiki->wikiParamRaw (b, e, false, args);
+       wiki->eval (args);
+       vargs = buildArgs (0, args);
+       {
+           MotorOutputString  out;
+           MotorOutput*  back = wiki->env->output;
+           WikiLine::linevec  bl;
+           MNodePtr  node;
+           MNodePtr  bcell;
+
+           wiki->env->output = &out;
+           bcell = wiki->env->mlenv->currentCell;
+           wiki->env->mlenv->currentCell = NULL;
+           try {
+               node = execDefun (wiki->env->mlenv, wf, vargs ());
+           } catch (ustring& msg) {
+               wiki->errorMsg.append (ustring (wl->begin, wl->end)).append (CharConst (": "));
+               if (wiki->env->mlenv->currentCell ()) {
+                   wiki->errorMsg.append (wiki->env->mlenv->currentCell ()->dump_string_short ()).append (CharConst (": "));
+               }
+               wiki->errorMsg.append (msg).append (uLF);
+           }
+           wiki->env->mlenv->currentCell = bcell;
+           if (out.ans.length () > 0) {
+               wiki->pass1 (out.ans, &bl, wl->fsuper);
+               do_linevec (&bl, wiki);
+           }
+           wiki->env->output = back;
+       }
+    } else {
+       WikiLine::linevec  bl;
+       WikiLine*  wl2;
+       ustring  u (CharConst ("^"));
+
+       u.append (wl->begin, wl->end);
+       wl2 = new WikiLine (u.begin (), u.end (), wl->fsuper);
+       bl.push_back (wl2);
+       do_linevec (&bl, wiki);
+    }
+}
+
+/* ============================================================ */
diff --git a/wiki/wikicmd.h b/wiki/wikicmd.h
new file mode 100644 (file)
index 0000000..9a03eb1
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef WIKICMD_H
+#define WIKICMD_H
+
+#include "ustring.h"
+
+class  WikiLine;
+class  WikiBlock;
+class  WikiFormat;
+
+void  wc_repeat (WikiLine* wl, WikiFormat* wiki);
+void  wc_doarray (WikiLine* wl, WikiFormat* wiki);
+void  wc_block (WikiLine* wl, WikiFormat* wiki);
+void  wc_setvar (WikiLine* wl, WikiFormat* wiki);
+void  wc_eval (WikiLine* wl, WikiFormat* wiki);
+void  wc_insert (WikiLine* wl, WikiFormat* wiki);
+void  wc_call_defun (WikiLine* wl, WikiFormat* wiki);
+
+#endif /* WIKICMD_H */
diff --git a/wiki/wikienv.cc b/wiki/wikienv.cc
new file mode 100644 (file)
index 0000000..f14e5d2
--- /dev/null
@@ -0,0 +1,88 @@
+#include "wikienv.h"
+#include "motorconst.h"
+#include "ml.h"
+#include "ustring.h"
+#include <boost/unordered_map.hpp>
+
+ustring  uHttp (CharConst ("http"));
+ustring  uHttps (CharConst ("https"));
+ustring  uDefault (CharConst ("default"));
+
+uregex  re_wiki1 (
+"("
+       "\\[\\["
+       "(\\+?" rWIKIVAR ")"
+       "("
+               "(/|~|,|\\^)?"
+               "\\]\\]"
+       "|"
+               "(:)"
+       ")"
+")|("
+       "\\]\\]"
+")|("
+       "\\|"
+")|"
+       "&"
+       "("
+               ";"
+       "|"
+               "([^;]);"
+       "|"
+               "([a-zA-Z][a-zA-Z0-9_]*);"
+       "|"
+               "#([0-9]{1,5});"
+       "|"
+               "#[xX]([0-9a-h]{1,4});"
+       ")"
+"|"
+       "(:)"
+"|"
+       "( )"
+"|"
+       "(<br>)"
+"|"
+       "(''+)"
+);
+uregex  re_wikicmdsep ("(:)|([ \t]+$)");
+
+void  WikiEnv::wikiFunc2_setVar (ustring name, MNode* val) {
+    wikiFunc2a.setVar (name, val);
+}
+
+void  WikiEnv::wikiFunc_setVar (ustring name, MNode* val) {
+    wikiFunca.setVar (name, val);
+}
+
+void  WikiEnv::wikiLink_setVar (ustring name, MNode* val) {
+    wikiLinka.setVar (name, val);
+}
+
+void  WikiEnv::wikiCmd_setVar (ustring name, MNode* val) {
+    wikiCmda.setVar (name, val);
+}
+
+MNode*  WikiEnv::wikiFunc2_getVar (ustring name) {
+    if (enableSpecial && special.size () > 0 && name.compare (0, special.size (), special) == 0)
+       return NULL;
+    return wikiFunc2a.getVar (name);
+}
+
+MNode*  WikiEnv::wikiFunc_getVar (ustring name) {
+    if (enableSpecial && special.size () > 0 && name.compare (0, special.size (), special) == 0)
+       return NULL;
+    return wikiFunca.getVar (name);
+}
+
+MNode*  WikiEnv::wikiLink_getVar (ustring name) {
+    if (enableSpecial && special.size () > 0 && name.compare (0, special.size (), special) == 0)
+       return NULL;
+    return wikiLinka.getVar (name);
+}
+
+MNode*  WikiEnv::wikiCmd_getVar (ustring name) {
+    if (enableSpecial && special.size () > 0 && name.compare (0, special.size (), special) == 0)
+       return NULL;
+    return wikiCmda.getVar (name);
+}
+
diff --git a/wiki/wikienv.h b/wiki/wikienv.h
new file mode 100644 (file)
index 0000000..f749e81
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef WIKIENV_H
+#define WIKIENV_H
+
+#include "motorvar.h"
+#include "ustring.h"
+
+class  WikiEnv {
+ public:
+    MotorVar  wikiFunc2a;
+    MotorVar  wikiFunca;
+    MotorVar  wikiLinka;
+    MotorVar  wikiCmda;
+    MotorSet  wikiGuestFunc;
+    ustring  special;
+    bool  enableSpecial;
+
+    WikiEnv () {
+       enableSpecial = false;
+    };
+    virtual  ~WikiEnv () {};
+
+    virtual void  wikiFunc2_setVar (ustring name, MNode* val);
+    virtual void  wikiFunc_setVar (ustring name, MNode* val);
+    virtual void  wikiLink_setVar (ustring name, MNode* val);
+    virtual void  wikiCmd_setVar (ustring name, MNode* val);
+    virtual MNode*  wikiFunc2_getVar (ustring name);
+    virtual MNode*  wikiFunc_getVar (ustring name);
+    virtual MNode*  wikiLink_getVar (ustring name);
+    virtual MNode*  wikiCmd_getVar (ustring name);
+};
+
+extern ustring  uHttp;
+extern ustring  uHttps;
+extern ustring  uDefault;
+
+extern uregex  re_wiki1;
+extern uregex  re_wikicmdsep;
+
+#endif /* WIKIENV_H */
diff --git a/wiki/wikiformat.cc b/wiki/wikiformat.cc
new file mode 100644 (file)
index 0000000..f9df8ce
--- /dev/null
@@ -0,0 +1,2724 @@
+#include "wikiformat.h"
+#include "wikiline.h"
+#include "wikitable.h"
+#include "wikicmd.h"
+#include "wikienv.h"
+#include "ml.h"
+#include "expr.h"
+#include "motorconst.h"
+#include "motorenv.h"
+#include "motoroutput.h"
+#include "util_const.h"
+#include "util_check.h"
+#include "util_string.h"
+#include "utf8.h"
+#include "ustring.h"
+#include <boost/ptr_container/ptr_vector.hpp>
+
+#define  kComment      "//"
+#define  kWikiP                '^'
+#define  kWikiH                '='
+#define  kWikiPRE      ' '
+#define  kWikiPRE2     '\t'
+#define  kWikiUL       '*'
+#define  kWikiOL       '#'
+#define  kWikiNL       ']'
+#define  kWikiDL       ';'
+#define  kWikiTABLE    '|'
+#define  kWikiQUOTE    '>'
+#define  kWikiQUOTE_e  '<'
+#define  kWikiDIV      '{'
+#define  kWikiDIV_e    '}'
+#define  kWikiHR       '-'
+#define  kWikiCmd      '$'
+
+#define  uP    "<p>"
+#define  uPe   "</p>\n"
+#define  uH1   "<h1>"
+#define  uH2   "<h2>"
+#define  uH3   "<h3>"
+#define  uH4   "<h4>"
+#define  uH5   "<h5>"
+#define  uH6   "<h6>"
+#define  uH1e  "</h1>\n"
+#define  uH2e  "</h2>\n"
+#define  uH3e  "</h3>\n"
+#define  uH4e  "</h4>\n"
+#define  uH5e  "</h5>\n"
+#define  uH6e  "</h6>\n"
+#define  uHR   "<hr />\n"
+#define  uID   " id="
+#define  uClass        " class="
+#define  uQ    "\""
+#define  unbsp "&nbsp;"
+
+ustring  uWiki (CharConst ("wiki_"));
+
+/*DOC:
+==テキストの整形ルール==
+
+*/
+/* ============================================================ */
+void  WikiMlEnv::setVar (const ustring& name, MNode* val) {
+    ustring  na (uWiki);
+    na.append (name);
+    parent->setVar (na, val);
+}
+
+void  WikiMlEnv::setAry (const ustring& name, size_t i, MNode* val) {
+    ustring  na (uWiki);
+    na.append (name);
+    parent->setAry (na, i, val);
+}
+
+void  WikiMlEnv::setArySize (const ustring& name, size_t n) {
+    ustring  na (uWiki);
+    na.append (name);
+    parent->setArySize (na, n);
+}
+
+void  WikiMlEnv::setAry (const ustring& name, MNode* list) {
+    ustring  na (uWiki);
+    na.append (name);
+    parent->setAry (na, list);
+}
+
+MNode*  WikiMlEnv::getVar (const ustring& name) {
+    ustring  na (uWiki);
+    na.append (name);
+    return parent->getVar (na);
+}
+
+MNode*  WikiMlEnv::getAry (const ustring& name, size_t i) {
+    ustring  na (uWiki);
+    na.append (name);
+    return parent->getAry (na, i);
+}
+
+size_t  WikiMlEnv::getArySize (const ustring& name) {
+    ustring  na (uWiki);
+    na.append (name);
+    return parent->getArySize (na);
+}
+
+/* ============================================================ */
+bool  WikiTableSplitter::next () {
+    int  nest = 0;
+    uiterator  ab;
+       
+    b = u;
+    if (b == e)
+       return false;
+    while (u != e && usearch (u, e, m, *re)) {
+       t = m[0].first;
+       u = m[0].second;
+       if (m[5].matched) {     // [[...:
+           nest ++;
+       } else if (m[6].matched) { // ]]
+           if (nest > 0)
+               nest --;
+       } else if (m[7].matched) { // |
+           if (nest == 0) {
+               return true;
+           }
+       }
+    }
+    t = u = e;
+    return true;
+}
+
+/* ============================================================ */
+void  WikiBlockComplex::outputBlock (MotorOutput* out) {
+    for (int i = 0; i < block.size (); i ++) {
+       block[i].output (out);
+    }
+}
+
+/* ============================================================ */
+/*DOC:
+===段落===
+ マークアップ文字以外の文字で書き出すと,段落になる。
+ ^行頭が他のマークアップ文字になるときは,行頭に^を書く。
+ 空行は,段落を分ける。
+
+*/
+bool  WikiBlockParagraph::nextLine (uiterator b, uiterator e) {
+    if (*b == kWikiP) {
+       addLine (b, e);
+       return true;
+    } else {
+       return false;
+    }
+}
+
+void  WikiBlockParagraph::addLine (uiterator b, uiterator e) {
+    uiterator  it;
+    int  n;
+
+    if (b[0] == kWikiP)
+       b ++;
+    if (b == e)
+       return;
+    if (html.size () == 0) {
+    } else {
+       lastChar (html, it);
+       n = html.end () - it;
+       it = b;
+       nextChar (it, e);
+#ifdef UTF8JP
+       if (n == 3 && it - b == 3) {
+       } else {
+           html.append (uSPC);
+       }
+#else
+       html.append (uSPC);
+#endif
+    }
+    html.append (wiki->wikiLine (b, e));
+}
+
+void  WikiBlockParagraph::addHtml (const ustring& ht) {
+    html.append (ht);
+}
+
+void  WikiBlockParagraph::output (MotorOutput* out) {
+    out->out (CharConst (uP))->outText (html)->out (CharConst (uPe));
+}
+
+/* ============================================================ */
+/*DOC:
+===タイトル===
+ =タイトル1=
+ ==タイトル2==
+ ===タイトル3===#anchor1
+
+行頭に=を書くと,タイトルになる。行末の=は,省略可。
+
+*/
+
+bool  WikiBlockH::nextLine (uiterator b, uiterator e) {
+    return false;
+}
+
+void  WikiBlockH::addLine (uiterator b, uiterator e) {
+    uiterator  u;
+    umatch  m;
+    int  n, cn;
+    static uregex  re ("(=+)#([a-zA-Z0-9_-]+)$");
+
+    cn = wiki->countWikiH (b, e);
+    level = cn + wiki->headbase;
+    if (level > 6)
+       level = 6;
+    level0 = wiki->hlevel;
+
+    for (; b < e && *b == ' '; b ++) {}
+    if (usearch (b, e, m, re)) {
+       u = m[0].first;
+       n = m[1].second - m[1].first - cn;
+       if (n > 0) {
+           u += n;
+       }
+       for (; b < u && u[-1] == ' '; u --) {}
+       title = wiki->wikiLine (b, u);
+       anchor = ustring (m[2].first, m[2].second);
+    } else {
+       u = e;
+       for (n = cn; n > 0 && b < u && u[-1] == '='; u --, n --) {}
+       for (; b < u && u[-1] == ' '; u --) {}
+       title = wiki->wikiLine (b, u);
+    }
+    wiki->hlevel = level;
+}
+
+WikiBlock::closeType  WikiBlockH::closeLine (uiterator b, uiterator e) {
+    if (e - b == 1 && *b == kWikiDIV_e) {
+       int  i;
+       for (i = wiki->bstack.size () - 1; i >= 0; i --) {
+           switch (wiki->bstack[i]->type) {
+           case BlockDiv:
+           case BlockForm:
+               for (i = wiki->bstack.size () - 1; i >= 0; i --) {
+                   wiki->pop_block ();
+                   switch (wiki->bstack[i]->type) {
+                   case BlockDiv:
+                   case BlockForm:
+                       if (wiki->cur)
+                           wiki->cur->close ();
+                       wiki->cur = NULL;
+                       return CloseTrue;
+                   }
+               }
+           }
+       }
+       return CloseFalse;
+    } else if (b != e && *b == kWikiH) {
+       int  l = wiki->countWikiH (b, e) + wiki->headbase;
+       int  i;
+       if (l > 6)
+           l = 6;
+       WikiBlockH*  obj;
+       for (i = wiki->bstack.size () - 1; i >= 0; i --) {
+           if (wiki->bstack[i]->type == BlockH) {
+               obj = (WikiBlockH*)wiki->bstack[i];
+               if (obj->level >= l) {
+                   wiki->pop_block ();
+               } else {
+                   break;
+               }
+           } else {
+               break;
+           }
+       }
+       if (wiki->cur)
+           wiki->cur->close ();
+       wiki->cur = NULL;
+       return CloseFalse;
+    } else {
+       return CloseFalse;
+    }
+}
+
+void  WikiBlockH::close () {
+    wiki->hlevel = level0;
+}
+
+void  WikiBlockH::output (MotorOutput* out) {
+    int  i;
+
+    assert (0 < level && level <= 6);
+    for (i = level0 + 1; i <= level; i ++) {
+       outputBeginDiv (i, out);
+    }
+    switch (level) {
+    case 1: out->out (CharConst (uH1)); break;
+    case 2: out->out (CharConst (uH2)); break;
+    case 3: out->out (CharConst (uH3)); break;
+    case 4: out->out (CharConst (uH4)); break;
+    case 5: out->out (CharConst (uH5)); break;
+    case 6: out->out (CharConst (uH6)); break;
+    }
+    if (anchor.size () > 0) {
+       out->out (CharConst ("<a name=\""))->outHTMLnoCtrl (anchor)->out (CharConst ("\">"))->outText (title)->out (CharConst ("</a>"));
+    } else {
+       out->outText (title);
+    }
+    switch (level) {
+    case 1: out->out (CharConst (uH1e)); break;
+    case 2: out->out (CharConst (uH2e)); break;
+    case 3: out->out (CharConst (uH3e)); break;
+    case 4: out->out (CharConst (uH4e)); break;
+    case 5: out->out (CharConst (uH5e)); break;
+    case 6: out->out (CharConst (uH6e)); break;
+    }
+    outputBlock (out);
+    for (i = level; i > level0; i --) {
+#ifdef DEBUG
+       out->out (CharConst ("</div><!-- hh"))->out (boost::lexical_cast<ustring> (i))->out (CharConst (" -->\n"));
+#else
+       outputEndDiv (out);
+#endif /* DEBUG */
+    }
+}
+
+void  WikiBlockH::outputBeginDiv (int lv, MotorOutput* out) {
+    switch (lv) {
+    case 1: out->out (CharConst ("<div class=\"hh1\">\n")); break;
+    case 2: out->out (CharConst ("<div class=\"hh2\">\n")); break;
+    case 3: out->out (CharConst ("<div class=\"hh3\">\n")); break;
+    case 4: out->out (CharConst ("<div class=\"hh4\">\n")); break;
+    case 5: out->out (CharConst ("<div class=\"hh5\">\n")); break;
+    case 6: out->out (CharConst ("<div class=\"hh6\">\n")); break;
+    }
+}
+
+void  WikiBlockH::outputEndDiv (MotorOutput* out) {
+    out->out (CharConst ("</div>\n"));
+}
+
+/* ============================================================ */
+/*DOC:
+===フォーマット済みテキストブロック===
+ 行頭に空白文字を書くと,<pre>〜</pre>タグで囲まれます。
+
+*/
+bool  WikiBlockPreformat::nextLine (uiterator b, uiterator e) {
+    if (*b == markup) {
+       addLine (b, e);
+       return true;
+    } else {
+       return false;
+    }
+}
+
+void  WikiBlockPreformat::addLine (uiterator b, uiterator e) {
+    markup = *b;
+    if (markup == ' ') {
+       html.append (wiki->wikiLine (b + 1, e));
+    } else {
+       html.append (wiki->wikiLine (b, e));
+    }
+    html.append (uLF);
+}
+
+void  WikiBlockPreformat::output (MotorOutput* out) {
+    out->out (CharConst ("<pre>"))->outText (html)->out (CharConst ("</pre>\n"));
+}
+
+/* ============================================================ */
+bool  WikiBlockItemText::nextLine (uiterator b, uiterator e) {
+    return false;
+}
+
+void  WikiBlockItemText::addLine (uiterator b, uiterator e) {
+    html.append (wiki->wikiLine (b, e));
+}
+
+bool  WikiBlockItemText::checkAddLine (uiterator b, uiterator e) {
+    int  ch;
+    WikiBlock*  obj;
+    WikiBlockItem*  wbi;
+
+    if (b == e) {
+       html.append (wiki->wikiLine (b, e));
+    } else {
+       ch = *b;
+       if (block.size () > 0) {
+           obj = &block.back ();
+           switch (obj->type) {
+           case BlockItemUL:
+           case BlockItemOL:
+           case BlockItemNL:
+               wbi = (WikiBlockItem*)obj;
+               if (wbi->ch == ch) {
+                   wbi->addLine (b, e);
+                   return true;
+               }
+               break;
+           default:;
+           }
+       }
+    }
+    return false;
+}
+
+void  WikiBlockItemText::output (MotorOutput* out) {
+    if (indentHack) {
+       outputBlock (out);
+    } else {
+       out->out (CharConst ("<li>"));
+       out->outText (html);
+       outputBlock (out);
+       out->out (CharConst ("</li>\n"));
+    }
+}
+
+/* ============================================================ */
+/*DOC:
+===箇条書き===
+ *項目1
+ **項目1-1
+ #ナンバリング1
+ ##ナンバリング1-2
+ ]インデント1
+ ]]インデント1-1
+
+*/
+bool  WikiBlockItem::nextLine (uiterator b, uiterator e) {
+    if (*b == ch) {
+       addLine (b, e);
+       return true;
+    } else {
+       return false;
+    }
+}
+
+void  WikiBlockItem::setChar (char c) {
+    assert (ch == 0);
+    ch = c;
+}
+
+void  WikiBlockItem::addLine (uiterator b, uiterator e) {
+    WikiBlock*  wb;
+    WikiBlockItem*  wbi;
+    WikiBlockItemText*  wbt;
+    int  c;
+
+    if (ch == 0)
+       setChar (b[0]);
+    assert (b != e && b[0] == ch);
+    b ++;
+    if (b == e) {
+       wbt = new WikiBlockItemText (wiki);
+       block.push_back (wbt);
+       wbt->addLine (b, e);
+    } else {
+       c = *b;
+       if (block.size () > 0 && block.back ().checkAddLine (b, e)) {
+       } else {
+           switch (c) {
+           case kWikiUL:       // *
+               wbi = new WikiBlockItem (WikiBlock::BlockItemUL, wiki);
+               break;
+           case kWikiOL:       // #
+               wbi = new WikiBlockItem (WikiBlock::BlockItemOL, wiki);
+               break;
+           case kWikiNL:       // ]
+               wbi = new WikiBlockItem (WikiBlock::BlockItemNL, wiki);
+               break;
+           default:
+               wbi = NULL;
+           }
+           if (wbi) {
+               if (block.size () > 0) {
+                   wbt = &block.back ();
+                   wbi->addLine (b, e);
+                   wbt->block.push_back (wbi);
+               } else {
+                   wbt = new WikiBlockItemText (wiki);
+                   block.push_back (wbt);
+                   wbi->addLine (b, e);
+                   wbt->block.push_back (wbi);
+                   wbt->indentHack = true;
+               }
+           } else {
+               wbt = new WikiBlockItemText (wiki);
+               block.push_back (wbt);
+               wbt->addLine (b, e);
+           }
+       }
+    }
+}
+
+void  WikiBlockItem::output (MotorOutput* out) {
+    int  i;
+
+    switch (type) {
+    case BlockItemUL:
+       out->out (CharConst ("<ul>\n"));
+       outputBlock (out);
+       out->out (CharConst ("</ul>\n"));
+       break;
+    case BlockItemOL:
+       out->out (CharConst ("<ol>\n"));
+       outputBlock (out);
+       out->out (CharConst ("</ol>\n"));
+       break;
+    case BlockItemNL:
+       out->out (CharConst ("<ul class=\"nobull\">\n"));
+       outputBlock (out);
+       out->out (CharConst ("</ul>\n"));
+       break;
+    default:
+       std::cerr << "type:" << type << uLF;
+       assert (0);
+    }
+}
+
+void  WikiBlockItem::outputBlock (MotorOutput* out) {
+    for (int i = 0; i < block.size (); i ++) {
+       block[i].output (out);
+    }
+}
+
+/* ============================================================ */
+/*DOC:
+===定義リスト===
+ ;見出し:説明文
+
+*/
+bool  WikiBlockItemDL::nextLine (uiterator b, uiterator e) {
+    if (*b == kWikiDL) {
+       addLine (b, e);
+       return true;
+    } else {
+       return false;
+    }
+}
+
+void  WikiBlockItemDL::addLine (uiterator b, uiterator e) {
+    uiterator  s = b + 1;
+    Splitter  sp (s, e, re_wiki1);
+    int  n = 0;
+
+    while (sp.nextSep ()) {
+       if (sp.match (1)) {     // [[
+           n ++;
+       } else if (sp.match (6)) { // ]]
+           if (n > 0)
+               n --;
+       } else if (sp.match (13)) { // :
+           break;
+       }
+    }
+    html1.push_back (wiki->wikiLine (s, sp.end ()));
+    html2.push_back (wiki->wikiLine (sp.matchEnd (), e));
+}
+
+void  WikiBlockItemDL::output (MotorOutput* out) {
+    int  i;
+
+    out->out (CharConst ("<dl>\n"));
+    for (i = 0; i < html1.size (); i ++) {
+       out->out (CharConst ("<dt>"))->outText (html1[i])->out (CharConst ("</dt>\n"));
+       out->out (CharConst ("<dd>"))->outText (html2[i])->out (CharConst ("</dd>\n"));
+    }
+    out->out (CharConst ("</dl>\n"));
+}
+
+/* ============================================================ */
+/*DOC:
+===表===
+ |table:w=100%|head:right:||right:|
+ |*:|head:品名|head:数量|
+ |1|テレビ|2|
+ |2|空気清浄機|3|
+
+]・テーブルセルオプション
+|table:||c:||
+|h:名前|h:省略形|h:意味|
+|noborder|nb|border="0"を出力する。|
+|cellspacing=''integer''|spc=|cellspacing属性を出力する。|
+|cellpadding=''integer''||cellpadding属性を出力する。|
+|padding|pad|テーブルの最大のカラム数よりカラムが少ない行に,空のカラムを追加する。|
+|expanding|expand|テーブルの最大のカラム数よりカラムが少ない行の最後のカラムを引き延ばす。|
+|center|c|テーブルをセンタリングする。|
+|left|l|テーブルを左寄せする。|
+|right|r|テーブルを右寄せする。|
+|id=''name''||id属性を出力する。|
+|class=''name''||class属性を出力する。|
+|width=''number'', width=''number''%|w=|width属性を出力する。|
+|bgcolor=#''color''|bg=|bgcolor属性を出力する。|
+|nowhite|nw||
+|turn||セルの並びの横方向と縦方向を交換する。|
+
+]・セルオプション
+|table:||c:||
+|h:名前|h:省略形|h:意味|
+|header|h|ヘッダタグ(<th>)を出力する。|
+|left|l|セル内で左寄せする。|
+|right|r|右寄せする。|
+|center|c|横方向で中央あわせする。|
+|top|t|セル内で上寄せする。|
+|middle|m|縦方向で中央あわせする。|
+|bottom|b|下寄せする。|
+|nowrap|nw|セル内での折り返しを禁止する。|
+|*||これ以前にtable:行などで指定したセルオプションを破棄する。|
+|id=''name''||セルタグにid属性を付加する。|
+|class=''name''||セルタグにclass属性を付加する。|
+|width=''number''&|;''number''%||セルタグにwidth属性を付加する。|
+|height=''number''&|;''number''%||セルタグにheight属性を付加する。|
+|bgcolor=#''hex''|bg=|セルタグにbg属性を付加する。|
+
+|h:継続記号|h:意味|
+| <|左のセルと結合する。|
+| <''text''|左のセルと内容が一致するとき,結合する。|
+| ^|上のセルと結合する。|
+| ^''text''|上のセルと内容が一致するとき,結合する。|
+
+*/
+void  WikiBlockTable::TableCell::init () {
+    fheader = false;
+    halign = HAlignNone;
+    valign = VAlignNone;
+    id.resize (0);
+    while (! classlist.empty ())
+       classlist.pop_back ();
+    width.resize (0);
+    height.resize (0);
+    bgcolor.resize (0);
+    fnowrap = false;
+}
+
+void  WikiBlockTable::TableCell::copyFrom (WikiBlockTable::TableCell& b) {
+    int  i;
+
+    fheader = b.fheader;
+    halign = b.halign;
+    valign = b.valign;
+    id = b.id;
+    for (i = 0; i < b.classlist.size (); i ++)
+       classlist.push_back (b.classlist[i]);
+    width = b.width;
+    height = b.height;
+    bgcolor = b.bgcolor;
+    fnowrap = b.fnowrap;
+}
+
+void  WikiBlockTable::TableCell::cellAttrib (WikiFormat* wiki, uiterator& b, uiterator e, bool inHeader) {
+    Splitter  sp (b, e, re_colon);
+    uiterator  t, u;
+    ustring  v;
+
+    while (inHeader ? sp.next () : sp.nextSep ()) {
+       t = sp.begin ();
+       u = sp.end ();
+       if (match (t, u, CharConst ("header")) || match (t, u, CharConst ("h"))) {
+           fheader = true;
+       } else if (match (t, u, CharConst ("left")) || match (t, u, CharConst ("l"))) {
+           halign = HAlignLeft;
+       } else if (match (t, u, CharConst ("right")) || match (t, u, CharConst ("r"))) {
+           halign = HAlignRight;
+       } else if (match (t, u, CharConst ("center")) || match (t, u, CharConst ("c"))) {
+           halign = HAlignCenter;
+       } else if (match (t, u, CharConst ("top")) || match (t, u, CharConst ("t"))) {
+           valign = VAlignTop;
+       } else if (match (t, u, CharConst ("middle")) || match (t, u, CharConst ("m"))) {
+           valign = VAlignMiddle;
+       } else if (match (t, u, CharConst ("bottom")) || match (t, u, CharConst ("b"))) {
+           valign = VAlignBottom;
+       } else if (match (t, u, CharConst ("nowrap")) || match (t, u, CharConst ("nw"))) {
+           fnowrap = true;
+       } else if (match (t, u, CharConst ("*"))) {
+           init ();
+       } else if (wiki->paramIDClass (t, u, id, classlist)) {
+       } else if (wiki->paramWidth (t, u, width)) {
+       } else if (wiki->paramHeight (t, u, height)) {
+       } else if (matchSkip (t, u, CharConst ("bgcolor=")) || matchSkip (t, u, CharConst ("bg="))) {
+           if (checkColor (t, u)) {
+               bgcolor = ustring (t, u);
+           } else {
+               break;
+           }
+       } else {
+           break;
+       }
+    }
+    t = sp.begin ();
+    if (t < e && *t == '<') {
+       t ++;
+       if (t < e && *t == '^') {
+           t ++;
+           frowspan = true;
+           fcolspan = true;
+       } else {
+           fcolspan = true;
+       }
+    } else if (t < e && *t == '^') {
+       t ++;
+       if (t < e && *t == '<') {
+           t ++;
+           fcolspan = true;
+           frowspan = true;
+       } else {
+           frowspan = true;
+       }
+    }
+    b = t;
+}
+
+void  WikiBlockTable::TableCell::cellBody (uiterator b, uiterator e, WikiBlockTable* block, int idx) {
+    int  i;
+    CellList_t*  col;
+
+    text = block->wiki->wikiLine (b, e);
+
+    if (idx > 0) {
+       if (fcolspan) {
+           col = &block->ary.back ();
+           for (i = idx - 1; i >= 0; i --) {
+               if ((*col)[i].colspan > 0) {
+                   if (text.length () == 0 || (*col)[i].text == text) {
+                       colspan = 0;
+                       (*col)[i].colspan ++;
+                   }
+                   break;
+               }
+           }
+       }
+    }
+    if (frowspan) {
+       for (i = block->ary.size () - 2; i >= 0; i --) {
+           col = &block->ary[i];
+           if (idx < col->size () && (*col)[idx].rowspan > 0) {
+               if (text.length () == 0 || (*col)[idx].text == text) {
+                   rowspan = 0;
+                   (*col)[idx].rowspan ++;
+               }
+               break;
+           }
+       }
+    }
+}
+
+
+void  WikiBlockTable::TableCell::outputTD (MotorOutput* out, bool fturn) {
+    int  i;
+
+    if (fheader)
+       out->out (CharConst ("<th"));
+    else
+       out->out (CharConst ("<td"));
+
+    switch (halign) {
+    case HAlignLeft:
+       out->out (CharConst (" align=\"left\""));
+       break;
+    case HAlignCenter:
+       out->out (CharConst (" align=\"center\""));
+       break;
+    case HAlignRight:
+       out->out (CharConst (" align=\"right\""));
+       break;
+    }
+    switch (valign) {
+    case VAlignTop:
+       out->out (CharConst (" valign=\"top\""));
+       break;
+    case VAlignMiddle:
+       out->out (CharConst (" valign=\"middle\""));
+       break;
+    case VAlignBottom:
+       out->out (CharConst (" valign=\"bottom\""));
+       break;
+    }
+    if (id.size () > 0)
+       out->out (CharConst (" id=" uQ))->outHTMLnoCtrl (id)->out (CharConst (uQ));
+    if (classlist.size () > 0) {
+       out->out (CharConst (" class=" uQ));
+       for (i = 0; i < classlist.size (); i ++) {
+           if (i > 0)
+               out->out (uSPC);
+           out->outHTMLnoCtrl (classlist[i]);
+       }
+       out->out (CharConst (uQ));
+    }
+    if (width.size () > 0)
+       out->out (CharConst (" width=" uQ))->outHTMLnoCtrl (width)->out (CharConst (uQ));
+    if (height.size () > 0)
+       out->out (CharConst (" height=" uQ))->outHTMLnoCtrl (height)->out (CharConst (uQ));
+    if (bgcolor.size () > 0)
+       out->out (CharConst (" bgcolor=" uQ))->outHTMLnoCtrl (bgcolor)->out (CharConst (uQ));
+    if (fnowrap)
+       out->out (CharConst (" nowrap=\"nowrap\""));
+    if (fturn) {
+       if (colspan > 1)
+           out->out (CharConst (" rowspan=" uQ))->out (boost::lexical_cast<ustring> (colspan))->out (CharConst (uQ));
+       if (rowspan > 1)
+           out->out (CharConst (" colspan=" uQ))->out (boost::lexical_cast<ustring> (rowspan))->out (CharConst (uQ));
+    } else {
+       if (colspan > 1)
+           out->out (CharConst (" colspan=" uQ))->out (boost::lexical_cast<ustring> (colspan))->out (CharConst (uQ));
+       if (rowspan > 1)
+           out->out (CharConst (" rowspan=" uQ))->out (boost::lexical_cast<ustring> (rowspan))->out (CharConst (uQ));
+    }
+    out->out (CharConst (">"));
+}
+
+void  WikiBlockTable::TableCell::outputTDe (MotorOutput* out) {
+    if (fheader)
+       out->out (CharConst ("</th>\n"));
+    else
+       out->out (CharConst ("</td>\n"));
+}
+
+/* ============================================================ */
+bool  WikiBlockTable::nextLine (uiterator b, uiterator e) {
+    if (*b == kWikiTABLE) {
+       addLine (b, e);
+       return true;
+    } else {
+       return false;
+    }
+}
+
+void  WikiBlockTable::addLine (uiterator b, uiterator e) {
+    assert (b[0] == '|');
+    b ++;
+    if (n == 0 && matchSkip (b, e, CharConst ("table:"))) {
+       addLineHead (b, e);
+    } else {
+       addLineBody (b, e);
+    }
+    n ++;
+}
+
+WikiBlock::closeType  WikiBlockTable::closeLine (uiterator b, uiterator e) {
+    static uregex  re ("^\\}\\}(($)|(\\|)|((!([1-9][0-9]*))?\\\\$))");
+    umatch  m;
+
+    if (usearch (b, e, m, re)) {
+       if (m[2].matched) {     // }}
+           wiki->pop_block ();
+           return CloseTrue;
+       } else if (m[3].matched) { // }}|...
+           wiki->pop_block ();
+           if (wiki->cur && wiki->cur->type == BlockTable) {
+               WikiBlockTable*  obj = (WikiBlockTable*)wiki->cur;
+               obj->cont = true;
+               addLineBody (m[3].second, e);
+           } else {
+               assert (0);
+           }
+           return CloseTrue;
+       } else if (m[4].matched) {              // }}\     .
+           wiki->pop_block ();
+           if (wiki->cur && wiki->cur->type == BlockTable) {
+               WikiBlockTable*  obj = (WikiBlockTable*)wiki->cur;
+               obj->cont = true;
+               addLineBody (m[4].first, e);
+           } else {
+               assert (0);
+           }
+           return CloseTrue;
+       }
+    }
+    return CloseFalse;
+}
+
+void  WikiBlockTable::output (MotorOutput* out) {
+    normalize ();
+    outputTableTag (out);
+    outputTBody (out);
+    out->out (CharConst ("</table>\n"));
+}
+
+void  WikiBlockTable::addLineHead (uiterator b, uiterator e) {
+    TableCell*  cell;
+    WikiTableSplitter  sp (b, e);
+    uiterator  t, u;
+
+    if (sp.next ()) {          // table:
+       t = sp.begin ();
+       u = sp.end ();
+       addLineHeadTable (t, u);
+    }
+    while (sp.next ()) {
+       cell = newCell (defaultList.size ());
+       t = sp.begin ();
+       u = sp.end ();
+       cell->cellAttrib (wiki, t, u, true);
+       defaultList.push_back (cell);
+    }
+}
+
+void  WikiBlockTable::addLineHeadTable (uiterator b, uiterator e) {
+    Splitter  ag (b, e, re_colon);
+    uiterator  t, u;
+
+    while (ag.next ()) {
+       t = ag.begin ();
+       u = ag.end ();
+       if (match (t, u, CharConst ("noborder")) || match (t, u, CharConst ("nb"))) {
+           fnoborder = true;
+       } else if (matchSkip (t, u, CharConst ("cellspacing=")) || matchSkip (t, u, CharConst ("spc="))) {
+           if (checkNum (t, u)) {
+               cellspacing = strtoul (t);
+           } else {
+               wiki->errorMsg.append (ag.begin (), u).append (CharConst (": bad spacing.\n"));
+           }
+       } else if (matchSkip (t, u, CharConst ("cellpadding="))) {
+           if (checkNum (t, u)) {
+               cellpadding = strtoul (t);
+           } else {
+               wiki->errorMsg.append (ag.begin (), u).append (CharConst (": bad padding.\n"));
+           }
+       } else if (match (t, u, CharConst ("padding")) || match (t, u, CharConst ("pad"))) {
+           fpadding = true;
+       } else if (match (t, u, CharConst ("expanding")) || match (t, u, CharConst ("expand"))) {
+           fpadding = false;
+       } else if (match (t, u, CharConst ("center")) || match (t, u, CharConst ("c"))) {
+           halign = HAlignCenter;
+       } else if (match (t, u, CharConst ("left")) || match (t, u, CharConst ("l"))) {
+           halign = HAlignLeft;
+       } else if (match (t, u, CharConst ("right")) || match (t, u, CharConst ("r"))) {
+           halign = HAlignRight;
+       } else if (wiki->paramIDClass (t, u, id, classlist)) {
+       } else if (wiki->paramWidth (t, u, width)) {
+       } else if (matchSkip (t, u, CharConst ("bgcolor=")) || matchSkip (t, u, CharConst ("bg="))) {
+           if (checkColor (t, u)) {
+               bgcolor = ustring (t, u);
+           } else {
+               wiki->errorMsg.append (ag.begin (), u).append (CharConst (": bad color.\n"));
+           }
+       } else if (match (t, u, CharConst ("nowhite")) || match (t, u, CharConst ("nw"))) {
+           fnowhite = true;
+       } else if (match (t, u, CharConst ("turn"))) {
+           fturn = true;
+       } else {
+           wiki->errorMsg.append (t, u).append (CharConst (": error.\n"));
+       }
+    }
+}
+
+void  WikiBlockTable::addLineBody (uiterator b, uiterator e) {
+    CellList_t*  cols;
+    TableCell*  cell;
+    uiterator  t, u;
+    bool  fmorecell = false;
+    umatch  m;
+    static uregex  re ("(!([1-9][0-9]*))?\\\\$");
+
+    if (cont) {
+       cont = false;
+       cols = &ary.back ();
+    } else {
+       cols = new CellList_t;
+       ary.push_back (cols);
+    }
+
+    if (usearch (b, e, m, re)) { // 
+       e = m[0].first;
+       if (m[2].matched) {     // ...!NUM\         .
+           int  v = strtoul (m[2].first);
+           ccont ++;
+           if (ccont >= v) {
+               cont = false;
+               ccont = 0;
+           } else {
+               cont = true;
+           }
+       } else {                // ...\             .
+           cont = true;
+       }
+       if (b < e && e[-1] == '|') { // ...|\       .
+           fmorecell = true;
+       }
+    } else {
+       ccont = 0;
+    }
+
+    {
+       WikiTableSplitter  spl (b, e);
+       while (spl.next ()) {
+           t = spl.begin ();
+           u = spl.end ();
+           cell = newCell (cols->size ());
+           cell->cellAttrib (wiki, t, u);
+           if (spl.matchBegin () == spl.eol () && match (t, u, CharConst ("{{"))) {
+               cols->push_back (cell);
+               wiki->push_block (&cell->block);
+               return;
+           } else {
+               cell->cellBody (t, u, this, cols->size ());
+               cols->push_back (cell);
+           }
+       }
+       if (fmorecell) {
+           cell = newCell (cols->size ());
+           cols->push_back (cell);
+       }
+    }
+}
+
+WikiBlockTable::TableCell*  WikiBlockTable::newCell (int idx) {
+    TableCell*  ans = new TableCell;
+
+    if (idx < defaultList.size ())
+       ans->copyFrom (defaultList[idx]);
+    return ans;
+}
+
+void  WikiBlockTable::normalize () {
+    int  maxcol = 0;
+    int  i, j;
+    CellList_t*  col;
+    TableCell*  cell;
+
+    for (i = 0; i < ary.size (); i ++) {
+       col = &ary[i];
+       if (col->size () > maxcol)
+           maxcol = col->size ();
+    }
+    if (fpadding) {
+       for (i = 0; i < ary.size (); i ++) {
+           col = &ary[i];
+           if (col->size () < maxcol) {
+               cell = newCell (col->size ());
+               cell->colspan = maxcol - col->size ();
+               col->push_back (cell);
+               while (col->size () < maxcol) {
+                   cell = newCell (col->size ());
+                   cell->colspan = 0;
+                   col->push_back (cell);
+               }
+           }
+       }
+    } else {                   // expand
+       for (i = 0; i < ary.size (); i ++) {
+           col = &ary[i];
+           for (j = col->size () - 1; j >= 0; j --) {
+               cell = &(*col)[j];
+               if (cell->colspan > 0) {
+                   cell->colspan += maxcol - col->size ();
+                   while (col->size () < maxcol) {
+                       cell = newCell (col->size ());
+                       cell->colspan = 0;
+                       col->push_back (cell);
+                   }
+                   break;
+               }
+           }
+       }
+    }
+}
+
+void  WikiBlockTable::outputTableTag (MotorOutput* out) {
+    int  i;
+
+    out->out (CharConst ("<table"));
+    if (fnoborder)
+       out->out (CharConst (" border=\"0\""));
+    else
+       out->out (CharConst (" border=\"1\""));
+    if (cellspacing >= 0)
+       out->out (CharConst (" cellspacing=\""))->out (boost::lexical_cast<ustring> (cellspacing))->out (CharConst ("\""));
+    else
+       out->out (CharConst (" cellspacing=\"0\""));
+    if (cellpadding >= 0)
+       out->out (CharConst (" cellpadding=\""))->out (boost::lexical_cast<ustring> (cellpadding))->out (CharConst ("\""));
+    else
+       out->out (CharConst (" cellpadding=\"0\""));
+    switch (halign) {
+    case HAlignLeft:
+       out->out (CharConst (" align=\"left\""));
+       break;
+    case HAlignCenter:
+       out->out (CharConst (" align=\"center\""));
+       break;
+    case HAlignRight:
+       out->out (CharConst (" align=\"right\""));
+       break;
+    }
+    if (id.size () > 0)
+       out->out (CharConst (" id=\""))->outHTMLnoCtrl (id)->out (CharConst (uQ));
+    if (classlist.size () > 0) {
+       out->out (CharConst (" class=\""));
+       for (i = 0; i < classlist.size (); i ++) {
+           if (i > 0)
+               out->out (uSPC);
+           out->outHTMLnoCtrl (classlist[i]);
+       }
+       out->out (CharConst (uQ));
+    }
+    if (width.size () > 0)
+       out->out (CharConst (" width=" uQ))->outHTMLnoCtrl (width)->out (CharConst (uQ));
+    if (bgcolor.size () > 0)
+       out->out (CharConst (" bgcolor=" uQ))->outHTMLnoCtrl (bgcolor)->out (CharConst (uQ));
+    out->out (CharConst (">\n"));
+}
+
+void  WikiBlockTable::outputTBody (MotorOutput* out) {
+    int  i, j, n;
+    CellList_t*  col;
+    TableCell*  cell;
+
+    if (fturn) {
+       if (ary.size () > 0) {
+           n = ary[0].size ();
+           for (j = 0; j < n; j ++) {
+               out->out (CharConst ("<tr>\n"));
+               for (i = 0; i < ary.size (); i ++) {
+                   col = &ary[i];
+                   cell = &(*col)[j];
+                   outputTBodyCell (out, cell);
+               }
+               out->out (CharConst ("</tr>\n"));
+           }
+       }
+    } else {
+       for (i = 0; i < ary.size (); i ++) {
+           col = &ary[i];
+           out->out (CharConst ("<tr>\n"));
+           for (j = 0; j < col->size (); j ++) {
+               cell = &(*col)[j];
+               outputTBodyCell (out, cell);
+           }
+           out->out (CharConst ("</tr>\n"));
+       }
+    }
+}
+
+void  WikiBlockTable::outputTBodyCell (MotorOutput* out, TableCell* cell) {
+    if (cell->colspan > 0 && cell->rowspan > 0) {
+       cell->outputTD (out, fturn);
+       if (cell->block.size () > 0) {
+           wiki->output (cell->block);
+       } else {
+           if (cell->text.size () > 0) {
+               out->outText (cell->text);
+           } else {
+               if (! fnowhite)
+                   out->out (CharConst (unbsp));
+           }
+       }
+       cell->outputTDe (out);
+    }
+}
+
+/* ============================================================ */
+/*DOC:
+===selectタグ===
+ |select:NAME:function:default:size=NUM:multiple|
+ |LABEL|VALUE|selected|
+
+*/
+bool  WikiBlockSelect::nextLine (uiterator b, uiterator e) {
+    if (*b == kWikiTABLE) {
+       addLine (b, e);
+       return true;
+    } else {
+       close ();
+       return false;
+    }
+}
+
+void  WikiBlockSelect::addLine (uiterator b, uiterator e) {
+    assert (b[0] == '|');
+    b ++;
+    if (n == 0 && matchSkip (b, e, CharConst ("select:"))) {
+       addLineHead (b, e);
+    } else {
+       addLineBody (b, e);
+    }
+    n ++;
+}
+
+void  WikiBlockSelect::addLineHead (uiterator b, uiterator e) {
+    WikiTableSplitter  ag (b, e);
+    uiterator  t, u;
+
+    ag.next ();
+    t = ag.begin ();
+    wiki->wikiParamRaw (t, ag.end (), false, args);
+    if (args.size () > 0) {
+       name = wiki->wikiLine (args[0].begin (), args[0].end ());
+       args.erase (args.begin ());
+       while (args.size () > 0) {
+           t = args[0].begin ();
+           u = args[0].end ();
+           if (match (t, u, CharConst ("default"))) {
+               fdefault = true;
+               args.erase (args.begin ());
+           } else if (match (t, u, CharConst ("multiple"))) {
+               fmultiple = true;
+               args.erase (args.begin ());
+           } else if (matchSkip (t, u, CharConst ("size="))) {
+               ustring  v = ustring (t, u);
+               if (match (v, CharConst ("*"))) {
+                   elsize = 1;
+               } else if (checkNum (v)) {
+                   elsize = boost::lexical_cast<int> (v);
+                   if (elsize < 0 || elsize > 999) {
+                       elsize = 1;
+                   }
+               } else {
+                   elsize = 1;
+               }
+               args.erase (args.begin ());
+           } else if (wiki->paramIDClass (t, u, id, classlist)) {
+               args.erase (args.begin ());
+           } else {
+               break;
+           }
+       }
+       wiki->eval (args);
+    }
+
+    if (fmultiple && elsize == 0) {
+       elsize = 1;
+    }
+
+#ifdef DEBUG2
+    for (int i = 0; i < args.size (); i ++) {
+       std::cerr << args[i] << uLF;
+    }
+#endif /* DEBUG */
+}
+
+void  WikiBlockSelect::addLineBody (uiterator b, uiterator e) {
+    WikiTableSplitter  ag (b, e);
+    uiterator  t, u;
+    SelectItem  v;
+
+    if (ag.next ())
+       v.label = omitCtrl (wiki->wikiLine (ag.begin (), ag.end ()));
+    else
+       v.label.resize (0);
+    if (ag.next ()) {
+       v.value = omitCtrl (wiki->wikiLine (ag.begin (), ag.end ()));
+       v.fvalue = true;
+    } else {
+       v.value.resize (0);
+       v.fvalue = false;
+    }
+    v.fselect = false;
+    if (ag.next ())
+       if (match (ag.begin (), ag.end (), CharConst ("select"))) {
+           v.fselect = true;
+       }
+    item.push_back (v);
+}
+
+void  WikiBlockSelect::close () {
+    wiki->cur = NULL;
+    wiki->pop_block ();
+    assert (wiki->cur->type == BlockParagraph);
+
+    MotorOutputString  out;
+    output (&out);
+    wiki->cur->addHtml (out.ans);
+    delete this;
+}
+
+void  WikiBlockSelect::output (MotorOutput* out) {
+    int  i;
+    ustring  u;
+
+    out->out (CharConst ("<select name=\""))->outHTMLnoCtrl (name)->out (CharConst (uQ));
+    if (elsize > 0) {
+       if (elsize == 1) {
+           out->out (CharConst (" size=" uQ))->out (boost::lexical_cast<ustring> (item.size ()))->out (CharConst (uQ));
+       } else {
+           out->out (CharConst (" size=" uQ))->out (boost::lexical_cast<ustring> (elsize))->out (CharConst (uQ));
+       }
+    }
+    if (fmultiple)
+       out->out (CharConst (" multiple=\"multiple\""));
+    wiki->outIDClass (out, id, classlist);
+    if (args.size () > 0) {
+       out->out (CharConst (" onChange=\""));
+       wiki->wikiLinkParam (0, args, u);
+       out->outHTMLnoCtrl (u);
+       out->out (CharConst (uQ));
+    }
+    out->out (CharConst (">\n"));
+
+    if (fdefault)
+       u = wiki->getVar (name);
+#ifdef DEBUG2
+    std::cerr << "u:" << u << uLF;
+#endif /* DEBUG */
+
+    for (i = 0; i < item.size (); i ++) {
+       SelectItem*  v = &item[i];
+       out->out (CharConst ("<option"));
+       if (v->fvalue) {
+           out->out (CharConst (" value="uQ))->outHTMLnoCtrl (v->value)->out (CharConst (uQ));
+#ifdef DEBUG2
+           std::cerr << "u:" << u << " v:" << v->value << " ::" << (u == v->value) << uLF;
+#endif /* DEBUG */
+           if (v->fselect || (fdefault && u == v->value)) {
+               out->out (CharConst (" selected"));
+           }
+       } else {
+           if (v->fselect || (fdefault && u == v->label)) {
+               out->out (CharConst (" selected"));
+           }
+       }
+       out->out (CharConst (">"));
+       out->outHTML (v->label)->out (CharConst ("</option>\n"));
+    }
+    out->out (CharConst ("</select>\n"));
+}
+
+/* ============================================================ */
+/*DOC:
+===引用===
+ >
+ この部分は,引用です。
+ <
+
+*/
+bool  WikiBlockQuote::nextLine (uiterator b, uiterator e) {
+    return false;
+}
+
+WikiBlock::closeType  WikiBlockQuote::closeLine (uiterator b, uiterator e) {
+    if (e - b == 1 && *b == kWikiQUOTE_e) {
+       wiki->pop_block ();
+       wiki->cur = NULL;
+       return CloseTrue;
+    } else {
+       return CloseFalse;
+    }
+}
+
+void  WikiBlockQuote::addLine (uiterator b, uiterator e) {
+    // do nothing
+}
+
+void  WikiBlockQuote::output (MotorOutput* out) {
+    out->out (CharConst ("<blockquote>\n"));
+    outputBlock (out);
+    out->out (CharConst ("</blockquote>\n"));
+}
+
+/* ============================================================ */
+/*DOC:
+===DIVタグ===
+ {div:SideMenu
+ ...
+ }
+ {div:id=Name:SideMenu
+ ...
+ }
+
+*/
+bool  WikiBlockDiv::nextLine (uiterator b, uiterator e) {
+    return false;
+}
+
+void  WikiBlockDiv::addLine (uiterator b, uiterator e) {
+    Splitter  spl (b, e, re_colon);
+    umatch  m;
+    uiterator  t, u;
+
+    spl.next ();
+    while (spl.next ()) {
+       t = spl.begin ();
+       u = spl.end ();
+       if (wiki->paramIDClass (t, u, id, classlist)) {
+       } else if (checkWikiID (t, u)) {
+           Splitter  sp (t, u, re_comma);
+           ustring  v;
+           while (sp.next ()) {
+               v = wiki->eval (sp.begin (), sp.end ());
+               if (checkWikiID (v)) {
+                   classlist.push_back (v);
+               } else {
+                   wiki->errorMsg.append (v).append (CharConst (": bad class name\n"));
+               }
+           }
+       } else {
+           wiki->errorMsg.append (t, u).append (CharConst (": bad name\n"));
+       }
+    }
+}
+
+WikiBlock::closeType  WikiBlockDiv::closeLine (uiterator b, uiterator e) {
+    if (e - b == 1 && *b == kWikiDIV_e) {
+       wiki->pop_block ();
+       wiki->cur = NULL;
+       return CloseTrue;
+    } else {
+       return CloseFalse;
+    }
+}
+
+void  WikiBlockDiv::output (MotorOutput* out) {
+    int  i;
+
+    out->out (CharConst ("<div"));
+    if (classlist.size () > 0) {
+       out->out (CharConst (uClass uQ));
+       for (i = 0; i < classlist.size (); i ++) {
+           if (i > 0)
+               out->out (uSPC);
+           out->outHTMLnoCtrl (classlist[i]);
+       }
+       out->out (CharConst (uQ));
+    }
+    if (id.size () > 0) {
+       out->out (CharConst (uID uQ))->outHTMLnoCtrl (id)->out (CharConst (uQ));
+    }
+    out->out (CharConst (">\n"));
+    outputBlock (out);
+    out->out (CharConst ("</div>\n"));
+}
+
+/* ============================================================ */
+/*DOC:
+===FORMタグ===
+ {form:POST:post.hgh
+ ...
+ }
+// {form:POST:id=form1:class=formclass:post.hgh
+// {form:POST:post.hgh:id=form1:class=formclass
+ {form:id=form1:class=formclass:POST:post.hgh
+ ...
+ }
+
+*/
+bool  WikiBlockForm::nextLine (uiterator b, uiterator e) {
+    return false;
+}
+
+void  WikiBlockForm::addLine (uiterator b, uiterator e) {
+    uiterator  t, u;
+
+    wiki->wikiParamRaw (b, e, false, args);
+    args.erase (args.begin ());
+    while (args.size () > 0) {
+       t = args[0].begin ();
+       u = args[0].end ();
+       if (wiki->paramIDClass (t, u, id, classlist)) {
+           args.erase (args.begin ());
+       } else {
+           break;
+       }
+    }
+    if (args.size () > 0) {
+       t = args[0].begin ();
+       u = args[0].end ();
+       if (match (t, u, CharConst ("get")) || match (t, u, CharConst ("GET"))) {
+           method = M_GET;
+           args.erase (args.begin ());
+       } else if (match (t, u, CharConst ("post")) || match (t, u, CharConst ("POST"))) {
+           method = M_POST;
+           args.erase (args.begin ());
+       }
+       wiki->eval (args);
+    }
+}
+
+WikiBlock::closeType  WikiBlockForm::closeLine (uiterator b, uiterator e) {
+    if (e - b == 1 && *b == kWikiDIV_e) {
+       wiki->pop_block ();
+       wiki->cur = NULL;
+       wiki->curform = NULL;
+       return CloseTrue;
+    } else {
+       return CloseFalse;
+    }
+}
+
+void  WikiBlockForm::output (MotorOutput* out) {
+    ustring  url;
+    ustring  target;
+    bool  fscript;
+
+    out->out (CharConst ("<form"));
+    switch (method) {
+    case M_NONE:
+    case M_GET:
+       out->out (CharConst (" method=\"get\""));
+       break;
+    case M_POST:
+       out->out (CharConst (" method=\"post\""));
+       break;
+    default:
+       assert (0);
+    }
+    wiki->wikiUrlParam (args, fscript, url, target);
+    if (fscript) {
+       out->out (CharConst (" onSubmit=\""))-> outHTMLnoCtrl (url)->out (CharConst (uQ));
+    } else {
+       out->out (CharConst (" action=\""))-> outHTMLnoCtrl (url)->out (CharConst (uQ));
+    }
+    if (qfileform)
+       out->out (CharConst (" enctype=\"multipart/form-data\""));
+    if (target.length () > 0)
+       out->out (CharConst (" target=\""))->outHTMLnoCtrl (target)->out (CharConst (uQ));
+    wiki->outIDClass (out, id, classlist);
+    out->out (CharConst (">\n"));
+    outputBlock (out);
+    out->out (CharConst ("</form>\n"));
+}
+
+/* ============================================================ */
+/*DOC:
+===水平線===
+ ----
+
+*/
+bool  WikiBlockHR::nextLine (uiterator b, uiterator e) {
+    return false;
+}
+
+void  WikiBlockHR::addLine (uiterator b, uiterator e) {
+    // do nothing
+}
+
+void  WikiBlockHR::output (MotorOutput* out) {
+    out->out (CharConst (uHR));
+}
+
+/* ============================================================ */
+void  WikiFormat::pass1 (const ustring& text, WikiLine::linevec* block, bool fsuper) {
+    Splitter  sp (text, re_nl);
+
+    pass1_1 (sp, NULL, NULL, block, NULL, NULL, fsuper);
+}
+
+int  WikiFormat::pass1_1 (Splitter& sp, ustring* elseword, ustring* endword, WikiLine::linevec* block, uiterator* elsebegin, uiterator* elseend, bool fsuper) {
+    uiterator  b, e, t, u, v;
+    umatch  m;
+
+    while (sp.next ()) {
+       b = sp.begin ();
+       e = sp.end ();
+       while (b < e && b[0] == '\t')
+           b ++;
+       if (matchSkip (b, e, CharConst (kComment))) {
+           // comment
+       } else if (b != e && b[0] == kWikiCmd) {
+           if (usearch (b, e, m, re_wikicmdsep)) {
+               t = b;
+               u = m[0].first;
+               v = m[0].second;
+           } else {
+               t = b;
+               u = e;
+               v = e;
+           }
+           if (endword && match (t, u, *endword)) { 
+               return 2;       // endword
+           } else if (elseword && match (t, u, *elseword)) {
+               if (elsebegin)
+                   *elsebegin = v;
+               if (elseend)
+                   *elseend = e;
+               return 1;       // elseword
+           } else if (pass1_2 (sp, b, e, t, u, v, block, fsuper)) {
+           } else {
+               WikiLine*  wl;
+               block->push_back (wl = new WikiLine (b, e, fsuper));
+               wl->fn = wc_call_defun;
+           }
+       } else {
+           block->push_back (new WikiLine (b, e, fsuper));
+       }
+    }
+    return 0;                  // end of line
+}
+
+bool  WikiFormat::pass1_2 (Splitter& sp, uiterator& b, uiterator& e, uiterator& t, uiterator& u, uiterator& v, WikiLine::linevec* block, bool fsuper) {
+    WikiCmdTable::iterator  it;
+    ustring  elseword, endword;
+    WikiLine*  wl;
+    WikiLine*  wl2;
+    int  rc;
+
+    if ((it = GWikiCmdTable.find (ustring (t, u))) != GWikiCmdTable.end ()) {
+       block->push_back (wl = new WikiLine (b, v, e, fsuper));
+       wl->fn = it->second->fn;
+       if (it->second->endname) {
+           endword.assign (it->second->endname, it->second->endnamelen);
+           if (it->second->elsename) {
+               elseword.assign (it->second->elsename, it->second->elsenamelen);
+               // if-then-else block
+               wl->block = new WikiLine::linevec;
+               rc = pass1_1 (sp, &elseword, &endword, wl->block, &b, &e, fsuper);
+               switch (rc) {
+               case 0: // eot
+                       // no end line
+                   if (endword.length () > 0) {
+                       errorMsg.append (CharConst ("no matcing \"")).append (endword).append (CharConst ("\".\n"));
+                   }
+                   break;
+               case 1: // else
+                   do {
+                       wl2 = new WikiLine (b, e, fsuper);
+                       wl2->fn = wl->fn;
+                       wl->block2 = wl2;
+                       wl = wl2;
+                       wl->block = new WikiLine::linevec;
+                       rc = pass1_1 (sp, &elseword, &endword, wl->block, &b, &e, fsuper);
+                       if (rc == 0) {
+                           // no end line
+                           if (endword.length () > 0) {
+                               errorMsg.append (CharConst ("no matcing \"")).append (endword).append (CharConst ("\".\n"));
+                           }
+                       }
+                   } while (rc == 1);
+                   break;
+               case 2: // end
+                   break;
+               default:
+                   assert (0);
+               }
+           } else {
+               // block
+               wl->block = new WikiLine::linevec;
+               rc = pass1_1 (sp, NULL, &endword, wl->block, NULL, NULL, fsuper);
+               if (rc == 0) {
+                   // no end line error
+                   if (endword.length () > 0) {
+                       errorMsg.append (CharConst ("no matcing \"")).append (endword).append (CharConst ("\".\n"));
+                   }
+               }
+           }
+       } else {
+           // nonblock
+       }
+       return true;
+    }
+
+    return false;
+}
+
+#ifdef DEBUG2
+static void  dump (WikiLine::linevec* block, int indent = 0) {
+    int  i, j;
+    if (! block) {
+       for (j = 0; j < indent; j ++) std::cerr << "    ";
+       std::cerr << "(NULL)\n";
+       return;
+    }
+    for (i = 0; i < block->size (); i ++) {
+       WikiLine*  wl = &(*block)[i];
+       for (j = 0; j < indent; j ++) std::cerr << "    ";
+       if (wl->fn)
+           std::cerr << (void*)wl->fn << ":";
+       std::cerr << ustring (wl->begin, wl->end) << uLF;
+       while (1) {
+           if (wl->block) {
+               dump (wl->block, indent + 1);
+           }
+           if (wl->block2) {
+               wl = wl->block2;
+               for (j = 0; j < indent; j ++) std::cerr << "    ";
+               std::cerr << "else:" << (void*)wl->fn << ":" << ustring (wl->begin, wl->end) << uLF;
+               //          dump (wl->block, indent + 1);
+           } else {
+               break;
+           }
+       }
+    }
+}
+#endif /* DEBUG */
+
+void  WikiFormat::compile (const ustring& text, bool fsuper) {
+    try {
+       WikiLine::linevec  block;
+       pass1 (text, &block, fsuper);
+#ifdef DEBUG2
+       dump (&block);
+#endif /* DEBUG */
+
+       {
+           WikiLineScanner  scanner (&block);
+           compileLines (scanner);
+           while (1) {
+               if (cur)
+                   cur->close ();
+               cur = NULL;
+               if (bstack.size () > 0)
+                   pop_block ();
+               else
+                   break;
+           }
+           cur = NULL;
+       }
+    } catch (ustring& msg) {
+       if (mlenv->env->mlenv->currentCell ())
+           errorMsg.append (mlenv->env->mlenv->currentCell ()->dump_string_short ()).append (CharConst (": "));
+       errorMsg.append (msg).append (uLF);
+    }
+}
+
+void  WikiFormat::output (boost::ptr_vector<WikiBlock>& ary) {
+    int  i;
+
+    for (i = 0; i < ary.size (); i ++) {
+       ary[i].output (env->output);
+    }
+    errorOutput ();
+}
+
+void  WikiFormat::errorOutput () {
+    if (errorMsg.size () > 0) {
+       env->output->out (CharConst (uP));
+       env->output->outHTML_br (errorMsg);
+       env->output->out (CharConst (uPe));
+       errorMsg.resize (0);
+    }
+}
+
+bool  WikiFormat::checkClose (uiterator b, uiterator e) {
+    int  n = bstack.size ();
+    WikiBlock::closeType  rc;
+
+    while (n > 0) {
+       n --;
+       rc = bstack[n]->closeLine (b, e);
+       switch (rc) {
+       case WikiBlock::CloseTrue:
+           return true;
+       case WikiBlock::CloseFalse:
+           return false;
+       }
+    }
+    return false;
+}
+
+void  WikiFormat::compileLine (WikiLineScanner& scanner) {
+    WikiLine*  wl = scanner.cur ();
+    if (! wl)
+       return;
+
+    uiterator  b = wl->begin;
+    uiterator  e = wl->end;
+
+    if (wl->fn) {
+#ifdef DEBUG
+       std::cerr << ustring (wl->begin0, wl->end) << "\n";
+#endif /* DEBUG */
+       wl->fn (wl, this);
+    } else {
+       if (b == e) {           // empty line
+           if (cur) {
+               cur->close ();
+               cur = NULL;
+           }
+       } else if (matchSkip (b, e, CharConst ("//"))) {        // comment
+       } else if (checkClose (b, e)) {
+       } else if (cur && cur->nextLine (b, e)) {
+       } else if (b[0] == kWikiP) { // ^
+           if (cur)
+               cur->close ();
+           cur = new WikiBlockParagraph (this);
+           blockp->push_back (cur);
+           cur->addLine (b, e);
+       } else if (b[0] == kWikiH) {    // =
+           WikiBlockH*  obj;
+           if (cur)
+               cur->close ();
+           cur = obj = new WikiBlockH (this);
+           blockp->push_back (cur);
+           cur->addLine (b, e);
+           push_block (&obj->block);
+       } else if (b[0] == kWikiPRE     // SPC
+                  || b[0] == kWikiPRE2) {      // TAB
+           if (cur)
+               cur->close ();
+           cur = new WikiBlockPreformat (this);
+           blockp->push_back (cur);
+           cur->addLine (b, e);
+       } else if (b[0] == kWikiUL) {   // *
+           WikiBlockItem*  obj;
+           if (cur)
+               cur->close ();
+           cur = obj = new WikiBlockItem (WikiBlock::BlockItemUL, this);
+           blockp->push_back (cur);
+           obj->addLine (b, e);
+       } else if (b[0] == kWikiOL) {   // #
+           WikiBlockItem*  obj;
+           if (cur)
+               cur->close ();
+           cur = obj = new WikiBlockItem (WikiBlock::BlockItemOL, this);
+           blockp->push_back (cur);
+           obj->addLine (b, e);
+       } else if (b[0] == kWikiNL) {   // ]
+           WikiBlockItem*  obj;
+           if (cur)
+               cur->close ();
+           cur = obj = new WikiBlockItem (WikiBlock::BlockItemNL, this);
+           blockp->push_back (cur);
+           obj->addLine (b, e);
+       } else if (b[0] == kWikiDL) {   // ;
+           if (cur)
+               cur->close ();
+           cur = new WikiBlockItemDL (this);
+           blockp->push_back (cur);
+           cur->addLine (b, e);
+       } else if (matchHead (b, e, CharConst ("|select:"))) {  // |select:
+           if (cur && cur->type == WikiBlock::BlockParagraph) {
+           } else {
+               if (cur)
+                   cur->close ();
+               cur = new WikiBlockParagraph (this);
+               blockp->push_back (cur);
+           }
+           push_block (NULL);
+           cur = new WikiBlockSelect (this);
+           cur->addLine (b, e);
+       } else if (b[0] == kWikiTABLE) {        // |
+           WikiBlockTable*  obj;
+           if (cur)
+               cur->close ();
+           cur = obj = new WikiBlockTable (this);
+           blockp->push_back (cur);
+           cur->addLine (b, e);
+       } else if (b[0] == kWikiQUOTE && e - b == 1) {  // >
+           WikiBlockQuote*  obj;
+           if (cur)
+               cur->close ();
+           cur = obj = new WikiBlockQuote (this);
+           blockp->push_back (cur);
+           push_block (&obj->block);
+       } else if (matchHead (b, e, CharConst ("{div:"))) {
+           WikiBlockComplex*  obj;
+           if (cur)
+               cur->close ();
+           cur = obj = new WikiBlockDiv (this);
+           blockp->push_back (cur);
+           cur->addLine (b, e);
+           push_block (&obj->block);
+       } else if (curform == NULL && matchHead (b, e, CharConst ("{form:"))) {
+           WikiBlockComplex*  obj;
+           if (cur)
+               cur->close ();
+           cur = obj = curform = new WikiBlockForm (this);
+           blockp->push_back (cur);
+           cur->addLine (b, e);
+           push_block (&obj->block);
+       } else if (matchHead (b, e, CharConst ("----"))) { // ----
+           if (cur)
+               cur->close ();
+           cur = new WikiBlockHR (this);
+           blockp->push_back (cur);
+           cur->addLine (b, e);
+       } else {
+       Bp1:
+           if (cur && cur->type != WikiBlock::BlockParagraph) {
+               cur->close ();
+               cur = NULL;
+           }
+           if (! cur) {
+               cur = new WikiBlockParagraph (this);
+               blockp->push_back (cur);
+           }
+           cur->addLine (b, e);
+       }
+    }
+}
+
+void  WikiFormat::compileLines (WikiLineScanner& scanner, bool (*fn)(WikiLine& spp, WikiLineScanner& scanner, WikiFormat* wiki, void* par), void* par) {
+    WikiLine*  wl;
+
+    while (wl = scanner.next ()) {
+       if (fn && fn (*scanner.cur (), scanner, this, par)) {
+           break;
+       } else {
+           compileLine (scanner);
+       }
+    }
+}
+
+int  WikiFormat::countWikiH (uiterator& b, uiterator e) {
+    int  ans = 0;
+    while (b != e && *b == kWikiH) {
+       ans ++;
+       b ++;
+    }
+    if (ans > 6)
+       ans = 6;
+    return ans;
+}
+
+ustring  WikiFormat::wikiLink (const ustring& url) {
+    ustring  ans;
+    uiterator  b, e;
+    ustring  host, port, v;
+    umatch  m;
+    enum {
+       MHOST,
+       MPORT,
+       MPATH,
+       MFORM,
+       MFRAG,
+       MNAME,
+       MVALUE,
+    }  mode;
+    static uregex  re ("\\[\\[(" rWIKIVAR ")\\]\\]|(:)|(/)|(\\?)|(#)");
+
+    b = url.begin ();
+    e = url.end ();
+    if (matchSkip (b, e, CharConst ("http://")) || matchSkip (b, e, CharConst ("https://"))) {
+       Splitter  sp (b, e, re);
+
+       mode = MHOST;
+       ans.assign (url.begin (), b);   // http://
+       while (sp.next ()) {
+           if (sp.begin () < sp.end ())
+               host.append (sp.begin (), sp.end ());
+           if (! sp.match (0)) {
+               break;
+           } else if (sp.match (1)) {          // [[VAR]]
+               host.append (getVar (ustring (sp.matchBegin (1), sp.matchEnd (1))));
+           } else if (sp.match (2)) {  // :
+               mode = MPORT;
+               break;
+           } else if (sp.match (3)) {  // /
+               mode = MPATH;
+               break;
+           } else {
+               mode = MPATH;
+               goto Bp1;
+           }
+       }
+       if (mode == MPORT) {
+           while (sp.next ()) {
+               if (sp.begin () < sp.end ())
+                   port.append (sp.begin (), sp.end ());
+               if (! sp.match (0)) {
+                   break;
+               } else if (sp.match (1)) {      // [[VAR]]
+                   port.append (getVar (ustring (sp.matchBegin (1), sp.matchEnd (1))));
+               } else if (sp.match (3)) {      // /
+                   mode = MPATH;
+                   break;
+               } else {
+                   mode = MPATH;
+                   goto Bp1;
+               }
+           }
+       }
+       b = sp.matchEnd ();
+    Bp3:;
+       if (! checkHostname (host)) 
+           goto Bp1;
+       ans.append (host);
+       if (port.length () > 0) {
+           if (! checkNum (port))
+               goto Bp1;
+           ans.append (CharConst (":")).append (port);
+       }
+       if (! sp.isEnd ()) {
+           ans.append (CharConst ("/"));
+       }
+       goto Bp2;
+    Bp1:;
+       ans.resize (0);
+       b = url.begin ();
+    }
+ Bp2:;
+    {
+       // path
+       Splitter  sp (b, e, re);
+       int  n = 0;
+
+       mode = MPATH;
+       while (sp.next ()) {
+           if (sp.begin () < sp.end ()) {
+               v = sp.cur ();
+               v = urldecode_nonul (v);
+               v = urlEncode (v);
+               ans.append (v);
+           }
+           if (! sp.match (0)) {
+               break;
+           } else if (sp.match (1)) {          // [[VAR]]
+               v = getVar (ustring (sp.matchBegin (1), sp.matchEnd (1)));
+               ans.append (urlEncode (v));
+           } else if (sp.match (3)) {  // /
+               ans.append (CharConst ("/"));
+           } else if (sp.match (4)) {  // ?
+               ans.append (CharConst ("?"));
+               mode = MFORM;
+               break;
+           } else if (sp.match (5)) {  // #
+               mode = MFRAG;
+               ans.append (CharConst ("#"));
+               v = ustring (sp.matchEnd (), e);
+               v = urldecode_nonul (v);
+               v = urlEncode (v);
+               ans.append (v);
+               goto Bp5;
+           }
+       }
+       b = sp.matchEnd ();
+    }
+ Bp4:;
+    // form variable
+    if (b != e) {
+       static uregex  re ("\\[\\[(" rWIKIVAR ")\\]\\]|(=)|(&)|(#)");
+       Splitter  sp (b, e, re);
+       ustring  name, value;
+       int  n = 0;
+
+       while (1) {
+           name.resize (0);
+           value.resize (0);
+           mode = MNAME;
+           while (mode == MNAME && sp.next ()) {
+               if (sp.begin () < sp.end ()) {
+                   v = sp.cur ();
+                   v = urldecode_nonul (v);
+                   name.append (v);
+               }
+               if (! sp.match (0)) {
+                   break;
+               } else if (sp.match (1)) { // [[VAR]]
+                   name.append (getVar (ustring (sp.matchBegin (1), sp.matchEnd (1))));
+               } else if (sp.match (2)) { // =
+                   mode = MVALUE;
+                   break;
+               } else if (sp.match (3)) { // &
+                   mode = MNAME;
+                   break;
+               } else if (sp.match (4)) { // #
+                   mode = MFRAG;
+                   break;
+               }
+           }
+           while (mode == MVALUE && sp.next ()) {
+               if (sp.begin () < sp.end ()) {
+                   v = sp.cur ();
+                   v = urldecode_nonul (v);
+                   value.append (v);
+               }
+               if (! sp.match (0)) {
+                   break;
+               } else if (sp.match (1)) { // [[VAR]]
+                   value.append (getVar (ustring (sp.matchBegin (1), sp.matchEnd (1))));
+               } else if (sp.match (2)) { // =
+                   value.append (CharConst ("="));
+               } else if (sp.match (3)) { // &
+                   mode = MNAME;
+                   break;
+               } else if (sp.match (4)) { // #
+                   mode = MFRAG;
+                   break;
+               }
+           }
+
+           if (n > 0)
+               ans.append (CharConst ("&"));
+           if (name.length () > 0) {
+               ans.append (urlEncode (name)).append (CharConst ("=")).append (urlEncode (value));
+               n ++;
+           }
+           if (mode != MNAME)
+               break;
+       }
+       if (mode == MFRAG) {
+           ans.append (CharConst ("#"));
+           v = ustring (sp.matchEnd (), e);
+           v = urldecode_nonul (v);
+           v = urlEncode (v);
+           ans.append (v);
+       }
+    }
+ Bp5:;
+
+    return ans;
+}
+
+void  WikiFormat::wikiUrlParam (std::vector<ustring>& args, bool& fscript, ustring& url, ustring& target) {
+    if (args.size () > 0) {
+       url = args[0];
+       if (args.size () > 1 && (url == uHttp || url == uHttps)) {
+           url.append (CharConst (":")).append (args[1]);
+           url = wikiLink (url);
+           fscript = false;
+           if (args.size () > 2) {
+               target = eval (args[2].begin (), args[2].end ());
+           }
+       } else if (wikiLinkParam (0, args, url)) {
+           fscript = true;
+       } else {
+           url = wikiLink (url);
+           fscript = false;
+           if (args.size () > 1) {
+               target = eval (args[1].begin (), args[1].end ());
+           }
+       }
+    }
+}
+
+bool  WikiFormat::wikiLinkParam (int start, std::vector<ustring>& args, ustring& ans) {
+    MNode*  wf;
+    MNodePtr  vargs;
+    MNodePtr  node;
+    MNode*  a;
+
+    if (args.size () <= start)
+       return false;
+
+    if (wf = env->wikienv->wikiLink_getVar (args[start])) {
+       vargs = buildArgs (start + 1, args);
+       {
+           MotorOutputString  out2;
+           MotorOutput*  back = env->output;
+           env->output = &out2;
+           try {
+               node = execDefun (env->mlenv, wf, vargs ());
+           } catch (ustring& msg) {
+               if (mlenv->env->mlenv->currentCell ())
+                   errorMsg.append (mlenv->env->mlenv->currentCell ()->dump_string_short ()).append (CharConst (": "));
+               errorMsg.append (msg).append (uLF);
+           }
+           env->output = back;
+           ans = omitCtrl (out2.ans);
+       }
+       return true;
+    }
+    return false;
+}
+
+bool  WikiFormat::wikiParamRaw (uiterator& b, uiterator e, bool fstop, ustring& arg2) {
+    // ([[NAME:)VALUE]]
+    // fstop == true => end with ']]'
+    // fstop == false => end with EOL
+    Splitter  sp (b, e, re_wiki1);
+    int  n = 0;
+
+    while (sp.next ()) {
+       if (sp.match (5)) {             // [[
+           n ++;
+       } else if (sp.match (6)) {      // ]]
+           if (fstop && n == 0) {
+               arg2 = ustring (b, sp.end ());
+               b = sp.matchEnd ();
+               return true;
+           }
+           if (n > 0)
+               n --;
+       }
+    }
+    arg2 = ustring (b, e);
+    b = e;
+    if (fstop)
+       return false;
+    else
+       return true;
+}
+
+bool  WikiFormat::wikiParamRaw (uiterator& b, uiterator e, bool fstop, std::vector<ustring>& args) {
+    // ([[NAME:)ARGS:ARGS:...]]
+    Splitter  sp (b, e, re_wiki1);
+    int  n = 0;
+
+    while (sp.next ()) {
+       if (sp.match (5)) {             // [[
+           n ++;
+       } else if (sp.match (6)) {      // ]]
+           if (fstop && n == 0) {
+               args.push_back (ustring (b, sp.end ()));
+               b = sp.matchEnd ();
+               return true;
+           }
+           if (n > 0)
+               n --;
+       } else if (sp.match (13)) {     // :
+           if (n == 0) {
+               args.push_back (ustring (b, sp.end ()));
+               b = sp.matchEnd ();
+           }
+       }
+    }
+    args.push_back (ustring (b, e));
+    b = e;
+    if (fstop)
+       return false;
+    else
+       return true;
+}
+
+bool  WikiFormat::wikiParamRaw (uiterator& b, uiterator e, bool fstop, std::vector<ustring>& args, ustring& arg2) {
+    // ([[NAME:)ARGS:ARGS:... ARG2]]
+    Splitter  sp (b, e, re_wiki1);
+    int  n = 0;
+
+    while (sp.next ()) {
+       if (sp.match (5)) {             // [[
+           n ++;
+       } else if (sp.match (6)) {      // ]]
+           if (fstop && n == 0) {
+               args.push_back (ustring (b, sp.end ()));
+               b = sp.matchEnd ();
+               return true;
+           }
+           if (n > 0)
+               n --;
+       } else if (sp.match (13)) {     // :
+           if (n == 0) {
+               args.push_back (ustring (b, sp.end ()));
+               b = sp.matchEnd ();
+           }
+       } else if (sp.match (14)) {     // SPC
+           if (n == 0) {
+               args.push_back (ustring (b, sp.end ()));
+               b = sp.matchEnd ();
+               return wikiParamRaw (b, e, fstop, arg2);
+           }
+       }
+    }
+    args.push_back (ustring (b, e));
+    b = e;
+    if (fstop)
+       return false;
+    else
+       return true;
+}
+
+bool  WikiFormat::paramIDClass (uiterator b, uiterator e, ustring& id, std::vector<ustring>& classes) {
+    ustring  v;
+
+    if (matchSkip (b, e, CharConst ("id="))) {
+       v = eval (b, e);
+       if (checkWikiID (v)) {
+           id = v;
+       } else {
+           errorMsg.append (v).append (CharConst (": bad id\n"));
+       }
+       return true;
+    } else if (matchSkip (b, e, CharConst ("class="))) {
+       Splitter  sp (b, e, re_comma);
+       while (sp.next ()) {
+           v = eval (sp.begin (), sp.end ());
+           if (checkWikiID (v)) {
+               classes.push_back (v);
+           } else {
+               errorMsg.append (v).append (CharConst (": bad class name\n"));
+           }
+       }
+       return true;
+    }
+    return false;
+}
+
+bool  WikiFormat::paramSize (const char* m, size_t len, uiterator b, uiterator e, ustring& ans) {
+    if (matchSkip (b, e, m, len)) {
+       ustring  v (b, e);
+       if (checkNum (v)) {
+           ans = v;
+           return true;
+       } else {
+//         errorBadParam (s, e, wiki);
+       }
+    }
+    return false;
+}
+
+bool  WikiFormat::paramWidth (uiterator b, uiterator e, ustring& width) {
+    if (matchSkip (b, e, CharConst ("width=")) || matchSkip (b, e, CharConst ("w="))) {
+       ustring  v = eval (b, e);
+       if (checkWidth (v)) {
+           width = v;
+       } else {
+//         errorBadParam (s, e, wiki);
+       }
+       return true;
+    }
+    return false;
+}
+
+bool  WikiFormat::paramHeight (uiterator b, uiterator e, ustring& height) {
+    if (matchSkip (b, e, CharConst ("height=")) || matchSkip (b, e, CharConst ("h="))) {
+       ustring  v = eval (b, e);
+       if (checkWidth (v)) {
+           height = v;
+       } else {
+//         errorBadParam (s, e, wiki);
+       }
+       return true;
+    }
+    return false;
+}
+
+bool  WikiFormat::outIDClass (MotorOutput* out, const ustring& id, std::vector<ustring>& classes) {
+    if (id.length () > 0)
+       out->out (CharConst (" id=\""))->outHTMLnoCtrl (id)->out (CharConst (uQ));
+    if (classes.size () > 0) {
+       out->out (CharConst (" class=\""));
+       for (int i = 0; i < classes.size (); i ++) {
+           if (i > 0)
+               out->out (uSPC);
+           out->outHTMLnoCtrl (classes[i]);
+       }
+       out->out (CharConst (uQ));
+    }
+}
+
+ustring  WikiFormat::eval (uiterator b, uiterator e) {
+    ustring  ans, u;
+    umatch  m;
+    static uregex  re ("\\[\\[(" rWIKIVAR ")\\]\\]");
+
+    while (usearch (b, e, m, re)) {
+       ans.append (b, m[0].first);
+       ans.append (getVar (ustring (m[1].first, m[1].second)));
+       b = m[0].second;
+    }
+    ans.append (b, e);
+    return ans;
+}
+
+void  WikiFormat::eval (std::vector<ustring>& args) {
+    int  i;
+
+    for (i = 0; i < args.size (); i ++) {
+       args[i] = eval (args[i].begin (), args[i].end ());
+    }
+}
+
+ustring  WikiFormat::wikiLine (uiterator b, uiterator e) {
+    Splitter  sp (b, e, re_wiki1);
+    MotorOutputString  out;
+
+    wikiLine_1 (sp, &out, 0);
+    return out.ans;
+}
+
+void  WikiFormat::wikiLine_1 (Splitter& sp, MotorOutputString* out, int tmatch) {
+    /*
+      tmatch:
+       bit 1: return with ']]'
+       bit 2: return with ':'
+       bit 3: return with SPC
+       bit 4: return with ''
+    */
+
+    out->init ();
+    while (sp.nextSep ()) {
+       if (sp.begin () < sp.end ())
+           out->outHTML (sp.cur ());
+       if (sp.match (1)) {     // [[
+           wikiLine_2 (sp, out);
+       } else if (sp.match (6)) { // ]]
+           if (tmatch & 0x01)
+               return;
+           out->outHTML (ustring (sp.matchBegin (), sp.matchEnd ()));
+       } else if (sp.match (7)) { // |
+           out->outHTML (ustring (sp.matchBegin (), sp.matchEnd ()));
+       } else if (sp.match (8)) { // &
+           if (sp.match (9)) { // &.;
+               out->outHTML (ustring (sp.matchBegin (9), sp.matchEnd (9)));
+           } else if (sp.match (10)) { // &.+;
+               out->out (ustring (sp.matchBegin (), sp.matchEnd ()));
+           } else if (sp.match (11)) { // &#0000;
+               out->out (ustring (sp.matchBegin (), sp.matchEnd ()));
+           } else if (sp.match (12)) { // &#x0000;
+               out->out (ustring (sp.matchBegin (), sp.matchEnd ()));
+           } else {            // &;
+           }
+       } else if (sp.match (13)) { // :
+           if (tmatch & 0x02)
+               return;
+           out->outHTML (ustring (sp.matchBegin (), sp.matchEnd ()));
+       } else if (sp.match (14)) { // SPC
+           if (tmatch & 0x04)
+               return;
+           out->outHTML (ustring (sp.matchBegin (), sp.matchEnd ()));
+       } else if (sp.match (15)) { // <br>
+           out->out (CharConst ("<br />"));
+       } else if (sp.match (16)) { // ''
+           if (tmatch & 0x08)
+               return;
+           else
+               wikiLine_11 (sp, out, sp.matchEnd () - sp.matchBegin ());
+       } else {
+           assert (0);
+       }
+    }
+    if (sp.begin () < sp.end ())
+       out->outHTML (sp.cur ());
+}
+
+void  WikiFormat::wikiLine_2 (Splitter& sp, MotorOutputString* out) {
+    // [[NAME[/|~|,|^]]]
+    // [[NAME:
+    ustring  name (sp.matchBegin (2), sp.matchEnd (2));
+
+    if (sp.match (5)) {                // :
+       wikiLine_3 (sp, name, out);
+    } else {
+       ustring  val = getVar (name);
+       if (sp.match (4)) {
+           uiterator  b = sp.matchBegin (4);
+           switch (*b) {
+           case '/':
+               out->outHTML_br (val);
+               break;
+           case '~':
+               if (val.size () > 0)
+                   out->outHTML (val);
+               else
+                   out->outPadding ();
+               break;
+           case ',':
+               val = c3 (val);
+               out->outHTML (val);
+               break;
+           case '^':
+               out->outHTML_wbr (val);
+               break;
+           default:
+               assert (0);
+           }
+       } else {
+           out->outHTML (val);
+       }
+    }
+}
+
+void  WikiFormat::wikiLine_3 (Splitter& sp, const ustring& name, MotorOutputString* out) {
+    // [[NAME:
+    WikiFuncTable::iterator  it;
+    WikiCmdTableSupport::wikifunc_t*  wf;
+    Splitter  backup = sp;
+
+    if ((it = GWikiFuncTable.find (name)) != GWikiFuncTable.end ()) {
+       wf = it->second;
+       switch (wf->atype) {
+       case WikiArg1:
+           {
+               ustring  arg2;
+               wikiLine_4 (sp, arg2);
+               if (wf->fn1 (arg2, out, this))
+                   return;
+           }
+           break;
+       case WikiArgM:
+           {
+               std::vector<ustring>  args;
+               wikiLine_5 (sp, args);
+               if (wf->fnM (args, out, this))
+                   return;
+           }
+           break;
+       case WikiArgM2:
+           {
+               std::vector<ustring>  args;
+               ustring  arg2;
+               wikiLine_6 (sp, args, arg2);
+               if (wf->fnM2 (args, arg2, out, this))
+                   return;
+           }
+           break;
+       default:
+           assert (0);
+       }
+    } else if ((it = GWikiFuncTable2.find (name)) != GWikiFuncTable2.end ()) {
+       wf = it->second;
+       switch (wf->atype) {
+       case WikiArg1:
+           {
+               ustring  arg2;
+               wikiLine_7 (sp, arg2);
+               if (wf->fn1 (arg2, out, this))
+                   return;
+           }
+           break;
+       case WikiArgM:
+           {
+               std::vector<ustring>  args;
+               wikiLine_8 (sp, args);
+               if (wf->fnM (args, out, this))
+                   return;
+           }
+           break;
+       case WikiArgM2:
+           {
+               std::vector<ustring>  args;
+               ustring  arg2;
+               wikiLine_9 (sp, args, arg2);
+               if (wf->fnM2 (args, arg2, out, this))
+                   return;
+           }
+           break;
+       default:
+           assert (0);
+       }
+    } else if (wikiLine_10 (sp, name, out)) {
+       return;
+    } else {
+       out->outHTML (ustring (sp.matchBegin (), sp.matchEnd ()));
+       return;
+    }
+
+    {
+       sp = backup;
+       out->outHTML (ustring (sp.matchBegin (), sp.matchEnd ()));
+    }
+}
+
+void  WikiFormat::wikiLine_4 (Splitter& sp, ustring& arg2) {
+    // ([[NAME:)VALUE...]]
+    MotorOutputString  out;
+
+    wikiLine_1 (sp, &out, 0x01);
+    arg2 = out.ans;
+}
+
+void  WikiFormat::wikiLine_5 (Splitter& sp, std::vector<ustring>& args) {
+    // ([[NAME:)ARGS:ARGS:...]]
+    while (! sp.isEnd ()) {
+       MotorOutputString  out;
+       wikiLine_1 (sp, &out, 0x03);
+       args.push_back (out.ans);
+       if (sp.match (6))       // ]]
+           return;
+    }
+}
+
+void  WikiFormat::wikiLine_6 (Splitter& sp, std::vector<ustring>& args, ustring& arg2) {
+    // ([[NAME:)ARGS:ARGS:... ARG2]]
+    while (! sp.isEnd ()) {
+       MotorOutputString  out;
+       wikiLine_1 (sp, &out, 0x07);
+       args.push_back (out.ans);
+       if (sp.match (6))       // ]]
+           return;
+       if (sp.match (14))      // SPC
+           break;
+    }
+    {
+       MotorOutputString  out;
+       wikiLine_1 (sp, &out, 0x01);
+       arg2 = out.ans;
+    }
+}
+
+void  WikiFormat::wikiLine_7 (Splitter& sp, ustring& arg2) {
+    // ([[NAME:)VALUE...]]
+    uiterator  b = sp.matchEnd ();
+    int  n = 0;
+
+    while (sp.nextSep ()) {
+       if (sp.match (5)) {             // [[...:
+           n ++;
+       } else if (sp.match (6)) {      // ]]
+           if (n > 0) {
+               n --;
+           } else {
+               arg2.assign (b, sp.end ());
+               return;
+           }
+       }
+    }
+}
+
+void  WikiFormat::wikiLine_8 (Splitter& sp, std::vector<ustring>& args) {
+    // ([[NAME:)ARGS:ARGS:...]]
+    uiterator  b = sp.matchEnd ();
+    int  n = 0;
+
+    while (sp.nextSep ()) {
+       if (sp.match (5)) {             // [[...:
+           n ++;
+       } else if (sp.match (6)) {      // ]]
+           if (n > 0) {
+               n --;
+           } else {
+               args.push_back (ustring (b, sp.end ()));
+               return;
+           }
+       } else if (sp.match (13)) {     // :
+           if (n == 0) {
+               args.push_back (ustring (b, sp.end ()));
+               b = sp.matchEnd ();
+           }
+       }
+    }
+}
+
+void  WikiFormat::wikiLine_9 (Splitter& sp, std::vector<ustring>& args, ustring& arg2) {
+    // ([[NAME:)ARGS:ARGS:... ARG2]]
+    uiterator  b = sp.matchEnd ();
+    int  n = 0;
+
+    while (sp.nextSep ()) {
+       if (sp.match (5)) {             // [[...:
+           n ++;
+       } else if (sp.match (6)) {      // ]]
+           if (n > 0) {
+               n --;
+           } else {
+               args.push_back (ustring (b, sp.end ()));
+               return;
+           }
+       } else if (sp.match (13)) {     // :
+           if (n == 0) {
+               args.push_back (ustring (b, sp.end ()));
+               b = sp.matchEnd ();
+           }
+       } else if (sp.match (14)) {     // SPC
+           if (n == 0) {
+               args.push_back (ustring (b, sp.end ()));
+               wikiLine_7 (sp, arg2);
+               return;
+           }
+       }
+    }
+}
+
+// interpolate the defined function.
+bool  WikiFormat::wikiLine_10 (Splitter& sp, const ustring& name, MotorOutputString* out) {
+    MNode*  wd;
+    std::vector<ustring>  args;
+    ustring  arg2;
+    MNodePtr  vargs;
+    MNodePtr  node;
+    MotorOutput*  back;
+    ustring  v;
+
+    if ((wd = env->wikienv->wikiFunc2_getVar (name))) {
+       MotorOutputString  out2;
+       
+       wikiLine_9 (sp, args, arg2);
+       eval (args);
+       arg2 = eval (arg2.begin (), arg2.end ());
+       vargs = buildArgs (0, args, arg2);
+       back = env->output;
+       env->output = &out2;
+       try {
+           node = execDefun (env->mlenv, wd, vargs ());
+       } catch (ustring& msg) {
+           if (mlenv->env->mlenv->currentCell ())
+               errorMsg.append (mlenv->env->mlenv->currentCell ()->dump_string_short ()).append (CharConst (": "));
+           errorMsg.append (msg).append (uLF);
+       }
+       env->output = back;
+       v = omitCtrl (out2.ans);
+       v = wikiLine (v.begin (), v.end ());
+       out->outText (v);
+
+       return true;
+    } else if ((wd = env->wikienv->wikiFunc_getVar (name))) {
+       MotorOutputString  out2;
+       
+       wikiLine_8 (sp, args);
+       eval (args);
+       vargs = buildArgs (0, args);
+       back = env->output;
+       env->output = &out2;
+       try {
+           node = execDefun (env->mlenv, wd, vargs ());
+       } catch (ustring& msg) {
+           if (mlenv->env->mlenv->currentCell ())
+               errorMsg.append (mlenv->env->mlenv->currentCell ()->dump_string_short ()).append (CharConst (": "));
+           errorMsg.append (msg).append (uLF);
+       }
+       env->output = back;
+       v = wikiLine (out2.ans.begin (), out2.ans.end ());
+       v = omitCtrl (v);
+       out->outText (v);
+
+       return true;
+    } else {
+       return false;
+    }
+}
+
+static void  outQuot (MotorOutput* out, int n) {
+    static ustring  t (CharConst ("'"));
+
+    for (; n > 0; n --) {
+       out->outHTML (t);
+    }
+}
+
+void  WikiFormat::wikiLine_11 (Splitter& sp, MotorOutput* out, int n1) {
+    MotorOutputString  out2;
+    int  n, n2;
+
+    if (sp.matchEnd () == sp.eol ()) {
+       outQuot (out, n1);
+       return;
+    }
+    wikiLine_1 (sp, &out2, 0x08);
+    if (sp.match (16)) {               // ''
+       n2 = sp.matchEnd () - sp.matchBegin ();
+       n = n1 < n2 ? n1 : n2;
+       if (n >= 5) {
+           outQuot (out, n1 - 5);
+           out->out (CharConst ("<i><b>"))->outText (out2.ans)->out (CharConst ("</b></i>"));
+//         outQuot (out, n2 - 5);
+           sp.rewind (n2 - 5);
+       } else if (n >= 3) {
+           outQuot (out, n1 - 3);
+           out->out (CharConst ("<b>"))->outText (out2.ans)->out (CharConst ("</b>"));
+//         outQuot (out, n2 - 3);
+           sp.rewind (n2 - 3);
+       } else if (n >= 2) {
+           outQuot (out, n1 - 2);
+           out->out (CharConst ("<i>"))->outText (out2.ans)->out (CharConst ("</i>"));
+//         outQuot (out, n2 - 2);
+           sp.rewind (n2 - 2);
+       } else {
+           outQuot (out, n1);
+           out->outText (out2.ans);
+           outQuot (out, n2);
+       }
+    } else {
+       outQuot (out, n1);
+       out->outText (out2.ans);
+    }
+}
+/* ============================================================ */
diff --git a/wiki/wikiformat.h b/wiki/wikiformat.h
new file mode 100644 (file)
index 0000000..ea10b59
--- /dev/null
@@ -0,0 +1,605 @@
+#ifndef WIKIFORMAT_H
+#define WIKIFORMAT_H
+
+#include "wikienv.h"
+#include "motoroutput.h"
+#include "motorenv.h"
+#include "mlenv.h"
+#include "ftable.h"
+#include "ustring.h"
+#include "util_string.h"
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <vector>
+#include <iostream>
+#include <assert.h>
+
+class  WikiBlock;
+class  WikiFormat;
+
+class  WikiMlEnv: public MlEnv {
+ public:
+    MlEnv*  parent;
+
+    WikiMlEnv (MlEnv* p) {
+       parent = p;
+    };
+    virtual  ~WikiMlEnv () {};
+
+    virtual void  setVar (const ustring& name, MNode* val);
+    virtual void  setAry (const ustring& name, size_t i, MNode* val);
+    virtual void  setArySize (const ustring& name, size_t n);
+    virtual void  setAry (const ustring& name, MNode* list);
+    virtual MNode*  getVar (const ustring& name);
+    virtual MNode*  getAry (const ustring& name, size_t i);
+    virtual size_t  getArySize (const ustring& name);
+};
+
+class  WikiLine {
+ public:
+    typedef boost::ptr_vector<WikiLine>  linevec;
+
+    uiterator  begin0;
+    uiterator  begin;
+    uiterator  end;
+    void  (*fn) (WikiLine* wl, WikiFormat* wiki);
+    linevec*  block;
+    WikiLine*  block2;
+    bool  fsuper;
+
+    WikiLine (uiterator b, uiterator e, bool f) {
+       begin0 = begin = b;
+       end = e;
+       fn = NULL;
+       block = NULL;
+       block2 = NULL;
+       fsuper = f;
+    };
+    WikiLine (uiterator b0, uiterator b, uiterator e, bool f) {
+       begin0 = b0;
+       begin = b;
+       end = e;
+       fn = NULL;
+       block = NULL;
+       block2 = NULL;
+       fsuper = f;
+    };
+    virtual  ~WikiLine () {
+       delete block;
+       delete block2;
+    };
+    
+};
+
+class  WikiLineScanner {
+ public:
+    WikiLine::linevec*  wlv;
+    int  idx;
+
+    WikiLineScanner (WikiLine::linevec* v) {
+       assert (v);
+       wlv = v;
+       idx = -1;
+    };
+    virtual  ~WikiLineScanner () {};
+
+    virtual WikiLine*  cur () {
+       if (0 <= idx && idx < wlv->size ()) {
+           return &(*wlv)[idx];
+       } else {
+           return NULL;
+       }
+    };
+    virtual WikiLine*  next () {
+       idx ++;
+       return cur ();
+    };
+    virtual void  rollback () {
+       if (idx >= 0)
+           idx --;
+    };
+    virtual bool  isEnd () {
+       return idx >= wlv->size ();
+    };
+};
+
+class  WikiTableSplitter {
+ public:
+    uregex*  re;
+    uiterator  b;
+    uiterator  t;
+    uiterator  u;
+    uiterator  e;
+    umatch  m;
+    
+    WikiTableSplitter (uiterator pb, uiterator pe) {
+       re = &re_wiki1;
+       b = t = u = pb;
+       e = pe;
+    };
+    virtual  ~WikiTableSplitter () {};
+    virtual void  init (uiterator pb, uiterator pe) {
+       b = t = u = pb;
+       e = pe;
+    };
+    virtual uiterator  begin () {
+       return b;
+    };
+    virtual uiterator  end () {
+       return t;
+    };
+    virtual uiterator  eol () {
+       return e;
+    };
+    virtual uiterator  matchBegin () {
+       return t;
+    };
+    virtual bool  next ();
+};
+
+class  WikiBlock {
+ public:
+    typedef enum {
+       BlockNone,
+       BlockParagraph,
+       BlockH,
+       BlockPreformat,
+       BlockItemText,
+       BlockItemUL,
+       BlockItemOL,
+       BlockItemNL,
+       BlockItemDL,
+       BlockTS,
+       BlockTable,
+       BlockSelect,
+       BlockQuote,
+       BlockDF,
+       BlockDiv,
+       BlockForm,
+       BlockHR,
+    }  blockType;
+    typedef enum {
+       CloseNA,
+       CloseFalse,
+       CloseTrue,
+    }  closeType;
+
+    blockType  type;
+    WikiFormat*  wiki;
+
+    WikiBlock (blockType t, WikiFormat* w): type (t), wiki (w) {};
+    virtual  ~WikiBlock () {};
+
+    virtual void  addLine (uiterator b, uiterator e) {
+       assert (0);
+    };
+    virtual bool  nextLine (uiterator b, uiterator e) = 0;
+    virtual closeType  closeLine (uiterator b, uiterator e) {
+       return CloseNA;
+    };
+    virtual void  addLines (uiterator b, uiterator e) {
+       assert (0);
+    };
+    virtual void  addHtml (const ustring& ht) {
+       assert (0);
+    };
+    virtual void  close () {};
+    virtual void  output (MotorOutput* out) = 0;
+};
+
+class  WikiBlockComplex: public WikiBlock {
+ public:
+    boost::ptr_vector<WikiBlock>  block;
+
+    WikiBlockComplex (blockType t, WikiFormat* w): WikiBlock (t, w) {};
+    virtual  ~WikiBlockComplex () {};
+    virtual void  outputBlock (MotorOutput* out);
+};
+
+class  WikiBlockParagraph: public WikiBlock {
+ public:
+    ustring  html;
+
+    WikiBlockParagraph (WikiFormat* w): WikiBlock (BlockParagraph, w) {};
+    virtual  ~WikiBlockParagraph () {};
+
+    virtual bool  nextLine (uiterator b, uiterator e);
+    virtual void  addLine (uiterator b, uiterator e);
+    virtual void  addHtml (const ustring& ht);
+    virtual void  output (MotorOutput* out);
+};
+
+class  WikiBlockH: public WikiBlockComplex {
+ public:
+    ustring  title;
+    ustring  anchor;
+    int  level0;
+    int  level;
+
+    WikiBlockH (WikiFormat* w): WikiBlockComplex (BlockH, w) {
+       level0 = level = 0;
+    };
+    virtual  ~WikiBlockH () {};
+
+    virtual bool  nextLine (uiterator b, uiterator e);
+    virtual void  addLine (uiterator b, uiterator e);
+    virtual closeType  closeLine (uiterator b, uiterator e);
+    virtual void  close ();
+    virtual void  output (MotorOutput* out);
+    virtual void  outputBeginDiv (int lv, MotorOutput* out);
+    virtual void  outputEndDiv (MotorOutput* out);
+};
+
+class  WikiBlockPreformat: public WikiBlock {
+ public:
+    ustring  html;
+    char  markup;
+
+    WikiBlockPreformat (WikiFormat* w): WikiBlock (BlockPreformat, w) {
+       markup = 0;
+    };
+    virtual  ~WikiBlockPreformat () {};
+
+    virtual bool  nextLine (uiterator b, uiterator e);
+    virtual void  addLine (uiterator b, uiterator e);
+    virtual void  output (MotorOutput* out);
+};
+
+class  WikiBlockItemText: public WikiBlockComplex {
+ public:
+    ustring  html;
+    bool  indentHack;
+
+    WikiBlockItemText (WikiFormat* w): WikiBlockComplex (BlockItemText, w) {
+       indentHack = false;
+    };
+    virtual  ~WikiBlockItemText () {};
+    virtual bool  nextLine (uiterator b, uiterator e);
+    virtual void  addLine (uiterator b, uiterator e);
+    virtual bool  checkAddLine (uiterator b, uiterator e);
+    virtual void  output (MotorOutput* out);
+};
+
+class  WikiBlockItem: public WikiBlock {
+ public:
+    int  ch;
+    boost::ptr_vector<WikiBlockItemText>  block;
+
+    WikiBlockItem (blockType t, WikiFormat* w): WikiBlock (t, w) {
+       assert (t == BlockItemUL || t == BlockItemOL || t == BlockItemNL);
+       ch = 0;
+    };
+    virtual  ~WikiBlockItem () {};
+
+    virtual bool  nextLine (uiterator b, uiterator e);
+    virtual void  setChar (char c);
+    virtual void  addLine (uiterator b, uiterator e);
+    virtual void  output (MotorOutput* out);
+    virtual void  outputBlock (MotorOutput* out);
+};
+
+class  WikiBlockItemDL: public WikiBlock {
+ public:
+    std::vector<ustring>  html1;
+    std::vector<ustring>  html2;
+
+    WikiBlockItemDL (WikiFormat* w): WikiBlock (BlockItemDL, w) {};
+    virtual  ~WikiBlockItemDL () {};
+
+    virtual bool  nextLine (uiterator b, uiterator e);
+    virtual void  addLine (uiterator b, uiterator e);
+    virtual void  output (MotorOutput* out);
+};
+
+class  WikiBlockTable: public WikiBlock {
+ public:
+    typedef enum {
+       HAlignNone,
+       HAlignLeft,
+       HAlignRight,
+       HAlignCenter,
+    }  halign_t;
+    typedef enum {
+       VAlignNone,
+       VAlignTop,
+       VAlignBottom,
+       VAlignMiddle,
+    }  valign_t;
+
+    class  TableCell {
+    public:
+       ustring  text;
+       boost::ptr_vector<WikiBlock>  block;
+       bool  fheader;
+       halign_t  halign;
+       valign_t  valign;
+       ustring  id;
+       std::vector<ustring>  classlist;
+       ustring  width;
+       ustring  height;
+       ustring  bgcolor;
+       bool  fnowrap;
+       bool  fcolspan;
+       bool  frowspan;
+       int  rowspan;
+       int  colspan;
+       bool  rowspanmatch;
+       bool  colspanmatch;
+
+       TableCell () {
+           fheader = false;
+           halign = HAlignNone;
+           valign = VAlignNone;
+           fnowrap = false;
+           fcolspan = false;
+           frowspan = false;
+           rowspan = 1;
+           colspan = 1;
+           rowspanmatch = false;
+           colspanmatch = false;
+       };
+       virtual  ~TableCell () {};
+       virtual void  init ();
+       virtual void  copyFrom (TableCell& b);
+       virtual void  cellAttrib (WikiFormat* wiki, uiterator& b, uiterator e, bool inHeader = false);
+       virtual void  cellBody (uiterator b, uiterator e, WikiBlockTable* block, int idx);
+       virtual void  outputTD (MotorOutput* out, bool fturn);
+       virtual void  outputTDe (MotorOutput* out);
+    };
+
+    typedef boost::ptr_vector<TableCell>  CellList_t;
+    typedef boost::ptr_vector<CellList_t>  TableAry_t;
+
+    int  n;
+    bool  fnoborder;
+    int  cellspacing;
+    int  cellpadding;
+    bool  fpadding;
+    halign_t  halign;
+    ustring  id;
+    std::vector<ustring>  classlist;
+    ustring  width;
+    ustring  bgcolor;
+    bool  fnowhite;
+    bool  fturn;
+    bool  cont;
+    int  ccont;
+    CellList_t  defaultList;
+    TableAry_t  ary;
+
+    WikiBlockTable (WikiFormat* w): WikiBlock (BlockTable, w) {
+       n = 0;
+       fnoborder = false;
+       cellspacing = -1;
+       cellpadding = -1;
+       fpadding = false;
+       halign = HAlignNone;
+       fnowhite = false;
+       fturn = false;
+       cont = false;
+       ccont = 0;
+    };
+    virtual  ~WikiBlockTable () {};
+
+    virtual bool  nextLine (uiterator b, uiterator e);
+    virtual void  addLine (uiterator b, uiterator e);
+    virtual closeType  closeLine (uiterator b, uiterator e);
+    virtual void  output (MotorOutput* out);
+    virtual void  addLineHead (uiterator b, uiterator e);
+    virtual void  addLineHeadTable (uiterator b, uiterator e);
+    virtual void  addLineBody (uiterator b, uiterator e);
+    virtual TableCell*  newCell (int idx);
+    virtual void  normalize ();
+    virtual void  outputTableTag (MotorOutput* out);
+    virtual void  outputTBody (MotorOutput* out);
+    virtual void  outputTBodyCell (MotorOutput* out, TableCell* cell);
+};
+
+class  WikiBlockSelect: public WikiBlock {
+ public:
+    class  SelectItem {
+    public:
+       ustring  label;
+       ustring  value;
+       bool  fvalue;
+       bool  fselect;
+
+       SelectItem (): fvalue (false), fselect (false) {};
+       virtual  ~SelectItem () {};
+    };
+
+    int  n;
+    ustring  name;
+    std::vector<ustring>  args;
+    bool  fdefault;
+    bool  fmultiple;
+    int  elsize;
+    std::vector<ustring>  classlist;
+    ustring  id;
+    std::vector<SelectItem>  item;
+
+    WikiBlockSelect (WikiFormat* w): WikiBlock (BlockSelect, w) {
+       n = 0;
+       fdefault = false;
+       fmultiple = false;
+       elsize = 0;
+    };
+    virtual  ~WikiBlockSelect () {};
+
+    virtual bool  nextLine (uiterator b, uiterator e);
+    virtual void  addLine (uiterator b, uiterator e);
+    virtual void  addLineHead (uiterator b, uiterator e);
+    virtual void  addLineBody (uiterator b, uiterator e);
+    virtual void  close ();
+    virtual void  output (MotorOutput* out);
+};
+
+class  WikiBlockQuote: public WikiBlockComplex {
+ public:
+
+    WikiBlockQuote (WikiFormat* w): WikiBlockComplex (BlockQuote, w) {};
+    virtual  ~WikiBlockQuote () {};
+
+    virtual bool  nextLine (uiterator b, uiterator e);
+    virtual closeType  closeLine (uiterator b, uiterator e);
+    virtual void  addLine (uiterator b, uiterator e);
+    virtual void  output (MotorOutput* out);
+};
+
+class  WikiBlockDiv: public WikiBlockComplex {
+ public:
+    std::vector<ustring>  classlist;
+    ustring  id;
+
+    WikiBlockDiv (WikiFormat* w): WikiBlockComplex (BlockDiv, w) {};
+    virtual  ~WikiBlockDiv () {};
+
+    virtual bool  nextLine (uiterator b, uiterator e);
+    virtual void  addLine (uiterator b, uiterator e);
+    virtual closeType  closeLine (uiterator b, uiterator e);
+    virtual void  output (MotorOutput* out);
+};
+
+class  WikiBlockForm: public WikiBlockComplex {
+ public:
+    enum {
+       M_NONE,
+       M_GET,
+       M_POST,
+    }  method;
+    std::vector<ustring>  args;
+    ustring  id;
+    std::vector<ustring>  classlist;
+    bool  qfileform;
+
+    WikiBlockForm (WikiFormat* w): WikiBlockComplex (BlockForm, w) {
+       method = M_NONE;
+       qfileform = false;
+    };
+    virtual  ~WikiBlockForm () {};
+
+    virtual bool  nextLine (uiterator b, uiterator e);
+    virtual void  addLine (uiterator b, uiterator e);
+    virtual closeType  closeLine (uiterator b, uiterator e);
+    virtual void  output (MotorOutput* out);
+};
+
+class  WikiBlockHR: public WikiBlock {
+ public:
+    WikiBlockHR (WikiFormat* w): WikiBlock (BlockHR, w) {};
+    virtual  ~WikiBlockHR () {};
+
+    virtual bool  nextLine (uiterator b, uiterator e);
+    virtual void  addLine (uiterator b, uiterator e);
+    virtual void  output (MotorOutput* out);
+};
+
+class  WikiFormat {
+ public:
+    typedef enum {
+       WikiArg1,
+       WikiArgM,
+       WikiArgM2,
+    }  wikifuncarg_t;
+
+    WikiBlock*  cur;
+    WikiBlockForm*  curform;
+    int  hlevel;
+    int  headbase;
+    MotorEnv*  env;
+    MlEnv*  mlenv;
+    ustring  errorMsg;
+    boost::ptr_vector<WikiBlock>  block;
+    boost::ptr_vector<WikiBlock>*  blockp;
+    std::vector<WikiBlock*>  bstack;
+    std::vector<boost::ptr_vector<WikiBlock>*>  pstack;
+    bool  protectMode;
+
+    WikiFormat (MotorEnv* e, bool mode) {
+       cur = NULL;
+       curform = NULL;
+       hlevel = 0;
+       headbase = 0;
+       env = e;
+       mlenv = new WikiMlEnv (env->mlenv);
+       mlenv->env = env;
+       if (env->log)
+           mlenv->log = env->log;
+       else
+           mlenv->log = &std::cerr;
+       mlenv->setStartTime ();
+       mlenv->resetProg ();
+       mlenv->setFTable (&GWikiFTable, NULL);
+       blockp = &block;
+       protectMode = mode;
+    };
+    virtual  ~WikiFormat () {
+       delete mlenv;
+    };
+
+    virtual void  compile (const ustring& text, bool fsuper);
+    virtual void  output (boost::ptr_vector<WikiBlock>& ary);
+    virtual void  output () {
+       output (block);
+    };
+    virtual void  pass1 (const ustring& text, WikiLine::linevec* block, bool fsuper);
+    virtual int  pass1_1 (Splitter& sp, ustring* elseword, ustring* endword, WikiLine::linevec* block, uiterator* elsebegin, uiterator* elseend, bool fsuper);
+    virtual bool  pass1_2 (Splitter& sp, uiterator& b, uiterator& e, uiterator& t, uiterator& u, uiterator& v, WikiLine::linevec* block, bool fsuper);
+    virtual void  errorOutput ();
+
+    virtual bool  checkClose (uiterator b, uiterator e);
+    virtual void  compileLine (WikiLineScanner& scanner);
+    virtual void  compileLines (WikiLineScanner& scanner, bool (*fn)(WikiLine& spp, WikiLineScanner& scanner, WikiFormat* wiki, void* par) = false, void* par = false);
+    virtual void  push_block (boost::ptr_vector<WikiBlock>* b) {
+       bstack.push_back (cur);
+       cur = NULL;
+       pstack.push_back (blockp);
+       blockp = b;
+    };
+    virtual void  pop_block () {
+       if (cur)
+           cur->close ();
+       cur = bstack.back ();
+       bstack.pop_back ();
+       blockp = pstack.back ();
+       pstack.pop_back ();
+    };
+    virtual int  countWikiH (uiterator& b, uiterator e);
+    virtual ustring  wikiLine (uiterator b, uiterator e);
+    virtual void  wikiLine_1 (Splitter& sp, MotorOutputString* out, int tmatch);
+    virtual void  wikiLine_2 (Splitter& sp, MotorOutputString* out);
+    virtual void  wikiLine_3 (Splitter& sp, const ustring& name, MotorOutputString* out);
+    virtual void  wikiLine_4 (Splitter& sp, ustring& arg2);
+    virtual void  wikiLine_5 (Splitter& sp, std::vector<ustring>& args);
+    virtual void  wikiLine_6 (Splitter& sp, std::vector<ustring>& args, ustring& arg2);
+    virtual void  wikiLine_7 (Splitter& sp, ustring& arg2);
+    virtual void  wikiLine_8 (Splitter& sp, std::vector<ustring>& args);
+    virtual void  wikiLine_9 (Splitter& sp, std::vector<ustring>& args, ustring& arg2);
+    virtual bool  wikiLine_10 (Splitter& sp, const ustring& name, MotorOutputString* out);
+    virtual void  wikiLine_11 (Splitter& sp, MotorOutput* out, int n);
+    virtual ustring  wikiLink (const ustring& url);
+    virtual void  wikiUrlParam (std::vector<ustring>& args, bool& fscript, ustring& url, ustring& target);
+    virtual bool  wikiLinkParam (int start, std::vector<ustring>& args, ustring& ans);
+    virtual bool  wikiParamRaw (uiterator& b, uiterator e, bool fstop, ustring& arg2);
+    virtual bool  wikiParamRaw (uiterator& b, uiterator e, bool fstop, std::vector<ustring>& args);
+    virtual bool  wikiParamRaw (uiterator& b, uiterator e, bool fstop, std::vector<ustring>& args, ustring& arg2);
+    virtual bool  paramIDClass (uiterator b, uiterator e, ustring& id, std::vector<ustring>& classes);
+    virtual bool  paramSize (const char* m, size_t len, uiterator b, uiterator e, ustring& ans);
+    virtual bool  paramWidth (uiterator b, uiterator e, ustring& width);
+    virtual bool  paramHeight (uiterator b, uiterator e, ustring& height);
+    virtual bool  outIDClass (MotorOutput* out, const ustring& id, std::vector<ustring>& classes);
+    virtual ustring  eval (uiterator b, uiterator e);
+    virtual void  eval (std::vector<ustring>& args);
+    virtual ustring  getVar (const ustring& name) {
+       return mlenv->getVar_string (name);
+    };
+    virtual ustring  getAry (const ustring& name, size_t i) {
+       return mlenv->getAry_string (name, i);
+    };
+    virtual size_t  getArySize (const ustring& name) {
+       return getArySize (name);
+    };
+};
+
+extern ustring  uWiki;
+
+#endif /* WIKIFORMAT_H */
diff --git a/wiki/wikiline.cc b/wiki/wikiline.cc
new file mode 100644 (file)
index 0000000..03f1481
--- /dev/null
@@ -0,0 +1,581 @@
+#include "wikiline.h"
+#include "wikienv.h"
+#include "wikitable.h"
+#include "wikiformat.h"
+#include "motorconst.h"
+#include "motoroutput.h"
+#include "motorenv.h"
+#include "ml.h"
+#include "expr.h"
+#include "util_const.h"
+#include "util_check.h"
+#include "util_string.h"
+#include "ustring.h"
+#include <vector>
+#include <assert.h>
+
+/*DOC:
+==インライン要素==
+
+*/
+/* ============================================================ */
+static void  errorBadParam (uiterator b, uiterator e, WikiFormat* wiki) {
+    wiki->errorMsg.append (b, e).append (CharConst (": bad parameter.\n"));
+}
+
+/*DOC:
+===強制改行===
+ &<;br>
+
+*/
+/* ============================================================ */
+/*DOC:
+===強調===
+ &';&';これはイタリック&';&';
+ &';&';&';これはボールド&';&';&';
+ &';&';&';&';&';イタリック+ボールド&';&';&';&';&';
+ &[;[i:wiki function形式のイタリック]]
+ &[;[b:wiki function形式のボールド]]
+ &[;[bi:ボールド+イタリック]]
+ &[;[ib:ボールド+イタリック]]
+
+*/
+//#WIKILINE    i       wl_italic
+bool  wl_italic (ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    out->out (CharConst ("<i>"))->outText (arg2)->out (CharConst ("</i>"));
+    return true;
+}
+
+//#WIKILINE    b       wl_bold
+bool  wl_bold (ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    out->out (CharConst ("<b>"))->outText (arg2)->out (CharConst ("</b>"));
+    return true;
+}
+
+//#WIKILINE    bi      wl_bolditalic
+//#WIKILINE    ib      wl_bolditalic
+bool  wl_bolditalic (ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    out->out (CharConst ("<b><i>"))->outText (arg2)->out (CharConst ("</i></b>"));
+    return true;
+}
+
+/* ============================================================ */
+/*DOC:
+===リンク===
+ &[;[http://www.yahoo.co.jp]]
+ &[;[+http://www.google.co.jp Googleトップ]]
+ &[;[https://ab.com/ HTTPSリンク]]
+ &[;[+https://ab.com/ HTTPSリンク]]
+ &[;[link:new.hgh?Name=n2&Value=&[;[v1]] 次]]
+ &[;[link:new.hgh?Name=n2&Value=&[;[v1]]:MainFrame 次]]
+ &[;[+link:profile.hgh?ID=&[;[id]] プロフィール]]
+ &[;[link:edit:&[;[id]] 編集]]
+
+*/
+static bool  wl_http_sub (ustring& proto, bool newwin, std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    ustring  url;
+    bool  fscript = false;
+    ustring  target;
+    static uregex  re ("^[1-9][0-9]*/");
+
+    if (args.size () > 0) {
+       if (proto.length () > 0) {
+           url = proto;
+           url.append (CharConst (":"));
+           url.append (args[0]);
+           if (args.size () > 1) {
+               url.append (CharConst (":")).append (args[1]);
+           }
+           url = wiki->wikiLink (url);
+           if (args.size () > 2) {
+               target = args[2]; // no LF
+           }
+       } else {
+           wiki->wikiUrlParam (args, fscript, url, target);
+       }
+       if (fscript) {
+           out->out (CharConst ("<a href=\"javascript:"))->outHTMLnoCtrl (url);
+       } else {
+           out->out (CharConst ("<a href=\""))->outHTMLnoCtrl (url);
+       }
+       if (newwin) {
+           out->out (CharConst ("\" target=\"_blank\">"));
+       } else if (target.length () > 0) {
+           out->out (CharConst ("\" target=\""))->outHTMLnoCtrl (target)->out (CharConst ("\">"));
+       } else {
+           out->out (CharConst ("\">"));
+       }
+       if (arg2.length () > 0) {
+           ustring  v = wiki->wikiLine (arg2.begin (), arg2.end ());
+           out->outText (v);
+       } else {
+           out->outHTML (url);
+       }
+       out->out (CharConst ("</a>"));
+    }
+    return true;
+}
+
+//#WIKILINE2   http    wl_http
+bool  wl_http (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    return wl_http_sub (uHttp, false, args, arg2, out, wiki);
+}
+
+//#WIKILINE2   +http   wl_http_new
+bool  wl_http_new (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    return wl_http_sub (uHttp, true, args, arg2, out, wiki);
+}
+
+//#WIKILINE2   https   wl_https
+bool  wl_https (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    return wl_http_sub (uHttps, false, args, arg2, out, wiki);
+}
+
+//#WIKILINE2   +https  wl_https_new
+bool  wl_https_new (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    return wl_http_sub (uHttps, true, args, arg2, out, wiki);
+}
+
+//#WIKILINE2   link    wl_link
+bool  wl_link (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    return wl_http_sub (uEmpty, false, args, arg2, out, wiki);
+}
+
+//#WIKILINE2   +link   wl_link_new
+bool  wl_link_new (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    return wl_http_sub (uEmpty, true, args, arg2, out, wiki);
+}
+
+/* ============================================================ */
+/*DOC:
+===IMGタグ===
+ [[image&:;images/logo.gif]]
+ [[image&:;images/logo.gif Logo]]
+ [[image&:;images/logo.gif:width=120:height=48 Logo]]
+
+|table:||c:|top:|
+|h:attribute|h:abbrev.|h:note|
+|width=''number''|w|width attribute|
+|width=''number''%|w|^|
+|height=''number''|h|height attribute|
+|height=''number''%|h|^|
+|id=''name''||id attribute|
+|class=''name''[,''name'']||class attribute|
+
+*/
+//#WIKILINE2   image   wl_image
+bool  wl_image (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    ustring  url, width, height, id;
+    std::vector<ustring>  classlist;
+    int  i = 0;
+    uiterator  b, e;
+    ustring  v;
+
+    if (args.size () > 0) {
+       url = args[i ++];
+       if (args.size () > 1 && (url == uHttp || url == uHttps)) {
+           url.append (CharConst (":")).append (args[i ++]);
+       }
+    }
+    url = wiki->wikiLink (url);
+    for (; i < args.size (); i ++) {
+       b = args[i].begin ();
+       e = args[i].end ();
+       if (matchSkip (b, e, CharConst ("width=")) || matchSkip (b, e, CharConst ("w="))) {
+           width = wiki->eval (b, e);
+           if (! checkWidth (width))
+               return false;
+       } else if (matchSkip (b, e, CharConst ("height=")) || matchSkip (b, e, CharConst ("h="))) {
+           height = wiki->eval (b, e);
+           if (! checkWidth (height))
+               return false;
+       } else if (wiki->paramIDClass (b, e, id, classlist)) {
+       } else {
+           return false;
+       }
+    }
+
+    out->out (CharConst ("<img src=\""))->outHTMLnoCtrl (url)->out (CharConst ("\""));
+    wiki->outIDClass (out, id, classlist);
+    if (width.length () > 0)
+       out->out (CharConst (" width=\""))->outHTMLnoCtrl (width)->out (CharConst ("\""));
+    if (height.length () > 0)
+       out->out (CharConst (" height=\""))->outHTMLnoCtrl (height)->out (CharConst ("\""));
+    v = wiki->eval (arg2.begin (), arg2.end ());
+    out->out (CharConst (" alt=\""))->outText (v)->out (CharConst ("\""));
+    out->out (CharConst (" />"));
+
+    return true;
+}
+
+/*DOC:
+===テキストカラー===
+ &[;[color:#ff0000 赤色]]
+
+*/
+//#WIKILINE    color   wl_color
+bool  wl_color (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    ustring  color, v;
+
+    if (args.size () == 1) {
+       color = args[0];
+       if (! checkColor (color))
+           return false;
+    } else {
+       return false;
+    }
+
+    out->out (CharConst ("<span style=\"color:"))->outHTMLnoCtrl (color)->out (CharConst (";\">"))->outText (arg2)->out (CharConst ("</span>"));
+
+    return true;
+}
+
+/*DOC:
+===アンカー===
+ &[;[anchor:Fig1]]
+
+*/
+//#WIKILINE    anchor  wl_anchor
+bool  wl_anchor (ustring& arg, MotorOutput* out, WikiFormat* wiki) {
+    ustring  name = wiki->eval (arg.begin (), arg.end ());
+    umatch  m;
+    static uregex  re ("^[a-zA-Z0-9_-]+$");
+
+    if (! usearch (name, m, re))
+       return false;
+    out->out (CharConst ("<a name=\""))->outHTMLnoCtrl (name)->out (CharConst ("\"></a>"));
+
+    return true;
+}
+
+/*DOC:
+===SPANタグ===
+ &[;[span:red クラス指定]]
+ &[;[span:id=Field1 ID指定]]
+
+*/
+//#WIKILINE2   span    wl_span
+bool  wl_span (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    ustring  v, id;
+    std::vector<ustring>  classlist;
+    uiterator  b, e;
+    int  i;
+
+    for (i = 0; i < args.size (); i ++) {
+       b = args[i].begin ();
+       e = args[i].end ();
+       if (wiki->paramIDClass (b, e, id, classlist)) {
+       } else {
+           if (checkWikiID (b, e)) {
+               classlist.push_back (ustring (b, e));
+           } else {
+               return false;
+           }
+       }
+    }
+
+    out->out (CharConst ("<span"));
+    wiki->outIDClass (out, id, classlist);
+    v = wiki->wikiLine (arg2.begin (), arg2.end ());
+    out->out (CharConst (">"))->outText (v)->out (CharConst ("</span>"));
+
+    return true;
+}
+
+/* ============================================================ */
+/*DOC:
+===フォームエレメント===
+ &[;[input:Name:size=24 初期値]]
+ &[;[input:Name:default]]
+ &[;[password:PW]]
+ &[;[hidden:Name 値]]
+ &[;[file:Name]]
+ &[;[submit:送信]]
+ &[;[button:Name ボタン]]
+ &[;[radio:Name:Value:id=Radio1 ラベル]]
+ &[;[checkbox:Name:Value ラベル]]
+ &[;[textarea:Text:cols=40:rows=6:wrap=soft テキストを入力してください。]]
+
+cols=, rows=, wrap=, width=, default
+
+*/
+static bool  wl_input_sub (const char* type, size_t typelen, std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki, bool novalue = false) {
+    int  i;
+    ustring  name, val;
+    ustring  psize;
+    ustring  pwidth;
+    bool  pdefault = false;
+    uiterator  b, e;
+    ustring  id;
+    std::vector<ustring>  classlist;
+
+    if (args.size () > 0)
+       name = wiki->eval (args[0].begin (), args[0].end ());
+    for (i = 1; i < args.size (); i ++) {
+       b = args[i].begin ();
+       e = args[i].end ();
+       if (wiki->paramSize (CharConst ("size="), b, e, psize)) {
+       } else if (wiki->paramWidth (b, e, pwidth)) {
+       } else if (match (b, e, uDefault)) {
+           pdefault = true;
+       } else if (wiki->paramIDClass (b, e, id, classlist)) {
+       } else {
+//         errorBadParam (b, e, wiki);
+           return false;
+       }
+    }
+
+    out->out (CharConst ("<input type=\""))->out (type, typelen)->out (CharConst ("\""));
+    out->out (CharConst (" name=\""))->outHTMLnoCtrl (name)->out (CharConst ("\""));
+    if (! novalue) {
+       if (pdefault) {
+           ustring t;
+           out->out (CharConst (" value=\""));
+           t = wiki->getVar (name);
+           out->outHTMLnoCtrl (t);
+           out->out (CharConst ("\""));
+       } else {
+           val = wiki->eval (arg2.begin (), arg2.end ());
+           out->out (CharConst (" value=\""))->outHTMLnoCtrl (val)->out (CharConst ("\""));
+       }
+    }
+    if (psize.size () > 0)
+       out->out (CharConst (" size=\""))->outHTMLnoCtrl (psize)->out (CharConst ("\""));
+    if (pwidth.size () > 0)
+       out->out (CharConst (" style=\"width: "))->outHTMLnoCtrl (pwidth)->out (CharConst (";\""));
+    wiki->outIDClass (out, id, classlist);
+    out->out (CharConst (" />"));
+    return true;
+}
+
+bool  wl_input_radiocheck (const char* type, size_t typelen, std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    int  i;
+    ustring  name, val;
+    bool  pchecked = false;
+    uiterator  b, e;
+    ustring  id;
+    std::vector<ustring>  classlist;
+    ustring  script;
+
+    if (args.size () > 0)
+       name = wiki->eval (args[0].begin (), args[0].end ());
+    if (args.size () > 1)
+       val = wiki->eval (args[1].begin (), args[1].end ());
+    for (i = 2; i < args.size (); i ++) {
+       b = args[i].begin ();
+       e = args[i].end ();
+       if (match (b, e, uDefault)) {
+           if (wiki->getVar (name) == val)
+               pchecked = true;
+       } else if (match (b, e, CharConst ("checked"))) {
+           pchecked = true;
+       } else if (wiki->paramIDClass (b, e, id, classlist)) {
+       } else {
+           break;
+       }
+    }
+    if (i < args.size () && wiki->wikiLinkParam (i, args, script)) {}
+
+    if (arg2.length () > 0)
+       out->out (CharConst ("<label>"));
+    out->out (CharConst ("<input type=\""))->out (type, typelen)->out (CharConst ("\""));
+    if (args.size () > 0)
+       out->out (CharConst (" name=\""))->outHTMLnoCtrl (name)->out (CharConst ("\""));
+    if (args.size () > 1)
+       out->out (CharConst (" value=\""))->outHTMLnoCtrl (val)->out (CharConst ("\""));
+    wiki->outIDClass (out, id, classlist);
+    if (pchecked)
+       out->out (CharConst (" checked=\"checked\""));
+    if (script.length () > 0)
+       out->out (CharConst (" onClick=\""))->outHTMLnoCtrl (script)->out (CharConst ("\""));
+    out->out (CharConst (" />"));
+    if (arg2.length () > 0)
+       out->outHTML (arg2)->out (CharConst ("</label>"));
+
+    return true;
+}
+
+//#WIKILINE2   input   wl_input
+bool  wl_input (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    return wl_input_sub (CharConst ("text"), args, arg2, out, wiki);
+}
+
+//#WIKILINE2   password        wl_password
+bool  wl_password (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    return wl_input_sub (CharConst ("password"), args, arg2, out, wiki);
+}
+
+//#WIKILINE2   hidden  wl_hidden
+bool  wl_hidden (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    return wl_input_sub (CharConst ("hidden"), args, arg2, out, wiki);
+}
+
+//#WIKILINE2   file    wl_file
+bool  wl_file (std::vector<ustring>& args, MotorOutput* out, WikiFormat* wiki) {
+    ustring  arg2;
+    if (wl_input_sub (CharConst ("file"), args, arg2, out, wiki, true)) {
+       if (wiki->curform)
+           wiki->curform->qfileform = true;
+       return true;
+    } else {
+       return false;
+    }
+}
+
+//#WIKILINE    submit  wl_submit
+bool  wl_submit (ustring& arg, MotorOutput* out, WikiFormat* wiki) {
+    out->out (CharConst ("<input type=\"submit\" value=\""))->outText (arg)->out (CharConst ("\" />"));
+    return true;
+}
+
+//#WIKILINE2   button  wl_button
+bool  wl_button (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    int  i;
+    std::vector<ustring>::iterator  it;
+    ustring  name, val;
+    uiterator  b, e;
+    ustring  id;
+    std::vector<ustring>  classlist;
+    ustring  script;
+
+    if (args.size () > 0)
+       name = wiki->eval (args[0].begin (), args[0].end ());
+    for (i = 1; i < args.size (); i ++) {
+       b = args[i].begin ();
+       e = args[i].end ();
+       if (wiki->paramIDClass (b, e, id, classlist)) {
+       } else {
+           break;
+       }
+    }
+    if (i < args.size () && wiki->wikiLinkParam (i, args, script)) {}
+
+    out->out (CharConst ("<input type=\"button\""));
+    out->out (CharConst (" name=\""))->outHTMLnoCtrl (name)->out (CharConst ("\""));
+    val = wiki->eval (arg2.begin (), arg2.end ());
+    out->out (CharConst (" value=\""))->outHTMLnoCtrl (val)->out (CharConst ("\""));
+    wiki->outIDClass (out, id, classlist);
+    if (script.length () > 0)
+       out->out (CharConst (" onClick=\""))->outHTMLnoCtrl (script)->out (CharConst ("\""));
+    out->out (CharConst (" />"));
+
+    return true;
+}
+
+//#WIKILINE2   radio   wl_radio
+bool  wl_radio (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+//     <input name="radio" type="radio" value="radio" checked="checked" />
+    return wl_input_radiocheck (CharConst ("radio"), args, arg2, out, wiki);
+}
+
+//#WIKILINE2   checkbox        wl_checkbox
+bool  wl_checkbox (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+//     <input name="checkbox" type="checkbox" value="1" checked="checked" />
+    return wl_input_radiocheck (CharConst ("checkbox"), args, arg2, out, wiki);
+}
+
+//#WIKILINE2   textarea        wl_textarea
+bool  wl_textarea (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    int  i;
+    ustring  name;
+    ustring  pcols, prows;
+    ustring  pwidth;
+    enum {
+       W_OFF, W_SOFT, W_HARD,
+    }  pwrap = W_SOFT;
+    bool  pdefault = false;
+    uiterator  b, e;
+
+    if (args.size () > 0)
+       name = wiki->eval (args[0].begin (), args[0].end ());
+    for (i = 1; i < args.size (); i ++) {
+       b = args[i].begin ();
+       e = args[i].end ();
+       if (wiki->paramSize (CharConst ("cols="), b, e, pcols)) {
+       } else if (wiki->paramSize (CharConst ("rows="), b, e, prows)) {
+       } else if (matchSkip (b, e, CharConst ("wrap="))) {
+           if (match (b, e, CharConst ("off"))) {
+               pwrap = W_OFF;
+           } else if (match (b, e, CharConst ("soft"))) {
+               pwrap = W_SOFT;
+           } else if (match (b, e, CharConst ("hard"))) {
+               pwrap = W_HARD;
+           } else {
+               return false;
+           }
+       } else if (wiki->paramWidth (b, e, pwidth)) {
+       } else if (match (b, e, uDefault)) {
+           pdefault = true;
+       } else {
+           return false;
+       }
+    }
+    
+    out->out (CharConst ("<textarea"));
+    if (args.size () >= 1)
+       out->out (CharConst (" name=\""))->outHTMLnoCtrl (name)->out (CharConst ("\""));
+    if (pcols.size () > 0)
+       out->out (CharConst (" cols=\""))->outHTMLnoCtrl (pcols)->out (CharConst ("\""));
+    else
+       out->out (CharConst (" cols=\"80\""));
+    if (prows.size () > 0)
+       out->out (CharConst (" rows=\""))->outHTMLnoCtrl (prows)->out (CharConst ("\""));
+    switch (pwrap) {
+    case W_OFF:
+       out->out (CharConst (" wrap=\"off\""));
+       break;
+    case W_SOFT:
+       out->out (CharConst (" wrap=\"soft\""));
+//     out->out (CharConst (" wrap=\"virtual\""));
+       break;
+    case W_HARD:
+       out->out (CharConst (" wrap=\"hard\""));
+//     out->out (CharConst (" wrap=\"physical\""));
+       break;
+    }
+    if (pwidth.size () > 0)
+       out->out (CharConst (" style=\"width: "))->outHTMLnoCtrl (pwidth)->out (CharConst (";\""));
+    out->out (CharConst (">"));
+    if (pdefault) {
+       ustring  t = wiki->getVar (name);
+       out->outHTML (t);
+    } else {
+       out->outHTML (arg2);
+    }
+    out->out (CharConst ("</textarea>"));
+    return true;
+}
+
+/* ============================================================ */
+/*DOC:
+===数値フォーマット===
+ &[;[pad0:NUMBER:VALUE]]
+
+*/
+//#WIKILINE    pad0    wl_pad0
+bool  wl_pad0 (std::vector<ustring>& args, MotorOutput* out, WikiFormat* wiki) {
+    int  n;
+    ustring  t;
+
+    if (args.size () == 2) {
+       n = strtol (args[0]);
+       out->outHTMLnoCtrl (zeroPad (n, wiki->getVar (args[1])));
+       return true;
+    } else {
+       return false;
+    }
+}
+/* ============================================================ */
+#if 0
+bool  wl_M2 (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) {
+    out->out (CharConst ("{{"));
+    for (int i = 0; i < args.size (); i ++) {
+       if (i > 0)
+           out->out (CharConst (":"));
+       out->outText (args[i]);
+    }
+    out->out (uSPC)->outText (arg2)->out (CharConst ("}}"));
+    return true;
+}
+#endif
+/* ============================================================ */
diff --git a/wiki/wikiline.h b/wiki/wikiline.h
new file mode 100644 (file)
index 0000000..ced9987
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef WIKILINE_H
+#define WIKILINE_H
+
+#include "ustring.h"
+class  MotorOutput;
+class  WikiFormat;
+
+bool  wl_italic (ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_bold (ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_bolditalic (ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_http (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_http_new (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_https (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_https_new (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_link (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_link_new (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_image (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_color (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_anchor (ustring& arg, MotorOutput* out, WikiFormat* wiki);
+bool  wl_span (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_input (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_password (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_hidden (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_file (std::vector<ustring>& args, MotorOutput* out, WikiFormat* wiki);
+bool  wl_submit (ustring& arg, MotorOutput* out, WikiFormat* wiki);
+bool  wl_button (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_radio (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_checkbox (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_textarea (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+bool  wl_pad0 (std::vector<ustring>& args, MotorOutput* out, WikiFormat* wiki);
+
+#endif /* WIKILINE_H */
diff --git a/wiki/wikitable.h b/wiki/wikitable.h
new file mode 100644 (file)
index 0000000..e1d46ca
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef WIKITABLE_H
+#define WIKITABLE_H
+
+#include "wikiformat.h"
+#include "ustring.h"
+#include <boost/unordered_map.hpp>
+#include <vector>
+
+class  MotorOutput;
+
+namespace WikiCmdTableSupport {
+    typedef struct {
+       const char*  name;
+       size_t  namelen;
+       const char*  elsename;
+       size_t  elsenamelen;
+       const char*  endname;
+       size_t  endnamelen;
+       void  (*fn) (WikiLine* wl, WikiFormat* wiki);
+    }  wikicmd_t;
+
+    typedef struct {
+       const char*  name;
+       size_t  namelen;
+       WikiFormat::wikifuncarg_t  atype;
+       union {
+           bool  (*fn) ();
+           bool  (*fn1) (ustring& arg, MotorOutput* out, WikiFormat* wiki);
+           bool  (*fnM) (std::vector<ustring>& args, MotorOutput* out, WikiFormat* wiki);
+           bool  (*fnM2) (std::vector<ustring>& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki);
+       };
+    }  wikifunc_t;
+}
+
+class  WikiCmdTable: public boost::unordered_map<ustring, WikiCmdTableSupport::wikicmd_t*> {
+ public:
+    WikiCmdTable (WikiCmdTableSupport::wikicmd_t* t) {
+       int  i;
+       for (i = 0; t[i].name; i ++) {
+           insert (value_type (ustring (t[i].name, t[i].namelen), &t[i]));
+       }
+    };
+    virtual  ~WikiCmdTable () {};
+};
+
+class  WikiFuncTable: public boost::unordered_map<ustring, WikiCmdTableSupport::wikifunc_t*> {
+public:
+    WikiFuncTable (WikiCmdTableSupport::wikifunc_t* t) {
+       int  i;
+       for (i = 0; t[i].name; i ++) {
+           insert (value_type (ustring (t[i].name, t[i].namelen), &t[i]));
+       }
+    };
+    virtual  ~WikiFuncTable () {};
+};
+
+extern WikiCmdTable  GWikiCmdTable;
+extern WikiFuncTable  GWikiFuncTable;
+extern WikiFuncTable  GWikiFuncTable2;
+
+#endif /* WIKITABLE_H */