From 1aa0d1c267737b86e14e00dccc7f1386fd41caef Mon Sep 17 00:00:00 2001 From: visor Date: Mon, 19 Oct 2009 23:13:21 +0900 Subject: [PATCH] refresh. --- Makefile | 13 + Makefile.conf | 30 + bin/MKID.pl | 64 ++ bin/MKTABLE.pl | 143 +++ bin/MKWTABLE.pl | 125 ++ cgi/Makefile | 5 + cgi/main.cc | 265 +++++ cgi_debug/Makefile | 4 + config.h | 19 + ext/diff-openbsd/diff.c | 455 ++++++++ ext/diff-openbsd/diff.h | 91 ++ ext/diff-openbsd/diffreg.c | 1549 +++++++++++++++++++++++++ ext/diff.cc | 2314 +++++++++++++++++++++++++++++++++++++ ext/diff.h | 10 + ext/ml-diff.cc | 42 + ext/ml-diff.h | 9 + lib/app.cc | 193 ++++ lib/app.h | 46 + lib/bdbmacro.h | 187 +++ lib/expr.cc | 555 +++++++++ lib/expr.h | 37 + lib/filemacro.h | 89 ++ lib/form.cc | 203 ++++ lib/form.h | 54 + lib/form_utf8-jp.cc | 16 + lib/form_utf8-jp.h | 14 + lib/form_utf8.cc | 18 + lib/form_utf8.h | 15 + lib/formfile.cc | 253 ++++ lib/formfile.h | 42 + lib/ftable.h | 65 ++ lib/heapdebug.cc | 150 +++ lib/heapdebug.h | 14 + lib/http.cc | 263 +++++ lib/http.h | 37 + lib/httpconst.h | 68 ++ lib/iso2022jp.cc | 26 + lib/iso2022jp.h | 8 + lib/mftable.h | 36 + lib/ml.cc | 548 +++++++++ lib/ml.h | 386 +++++++ lib/mlenv.cc | 413 +++++++ lib/mlenv.h | 113 ++ lib/motor.cc | 660 +++++++++++ lib/motor.h | 203 ++++ lib/motorconst.h | 28 + lib/motorenv.cc | 476 ++++++++ lib/motorenv.h | 98 ++ lib/motorfunc.cc | 39 + lib/motorfunc.h | 11 + lib/motoroutput-jp.cc | 61 + lib/motoroutput-jp.h | 54 + lib/motoroutput.cc | 104 ++ lib/motoroutput.h | 67 ++ lib/motorvar.cc | 57 + lib/motorvar.h | 38 + lib/sigsafe.cc | 4 + lib/sigsafe.h | 68 ++ lib/spair.h | 19 + lib/ustring.h | 56 + lib/utf16.cc | 122 ++ lib/utf16.h | 10 + lib/utf8-jp.cc | 63 + lib/utf8-jp.h | 9 + lib/utf8.cc | 260 +++++ lib/utf8.h | 24 + lib/util_apache.cc | 54 + lib/util_apache.h | 8 + lib/util_check.cc | 171 +++ lib/util_check.h | 36 + lib/util_const.cc | 64 ++ lib/util_const.h | 58 + lib/util_file.cc | 110 ++ lib/util_file.h | 15 + lib/util_inet.cc | 29 + lib/util_inet.h | 8 + lib/util_mimetype.cc | 102 ++ lib/util_mimetype.h | 9 + lib/util_proc.cc | 149 +++ lib/util_proc.h | 35 + lib/util_random.cc | 116 ++ lib/util_random.h | 11 + lib/util_string.cc | 691 +++++++++++ lib/util_string.h | 176 +++ lib/util_time.cc | 51 + lib/util_time.h | 12 + ml/Makefile | 120 ++ ml/main.cc | 81 ++ ml_debug/Makefile | 6 + modules/ml-addon.cc | 254 +++++ modules/ml-addon.h | 12 + modules/ml-apache.cc | 211 ++++ modules/ml-apache.h | 23 + modules/ml-bool.cc | 484 ++++++++ modules/ml-bool.h | 26 + modules/ml-cookielogin.cc | 441 +++++++ modules/ml-cookielogin.h | 36 + modules/ml-db.cc | 482 ++++++++ modules/ml-db.h | 40 + modules/ml-defun.cc | 60 + modules/ml-defun.h | 11 + modules/ml-encode.cc | 98 ++ modules/ml-encode.h | 12 + modules/ml-formvar.cc | 448 ++++++++ modules/ml-formvar.h | 18 + modules/ml-include.cc | 61 + modules/ml-include.h | 9 + modules/ml-inet.cc | 33 + modules/ml-inet.h | 9 + modules/ml-math.cc | 182 +++ modules/ml-math.h | 16 + modules/ml-motor.cc | 280 +++++ modules/ml-motor.h | 17 + modules/ml-sendmail.cc | 256 +++++ modules/ml-sendmail.h | 10 + modules/ml-sqlite3.cc | 535 +++++++++ modules/ml-sqlite3.h | 75 ++ modules/ml-store.cc | 814 +++++++++++++ modules/ml-store.h | 23 + modules/ml-string.cc | 515 +++++++++ modules/ml-string.h | 24 + modules/ml-struct.cc | 782 +++++++++++++ modules/ml-struct.h | 28 + modules/ml-time.cc | 196 ++++ modules/ml-time.h | 13 + modules/ml-variable.cc | 406 +++++++ modules/ml-variable.h | 18 + modules/ml-wiki.cc | 257 +++++ modules/ml-wiki.h | 18 + modules/ml-xml.cc | 245 ++++ modules/ml-xml.h | 10 + modules/motor-function.cc | 126 ++ modules/motor-function.h | 15 + wiki/wikicmd.cc | 418 +++++++ wiki/wikicmd.h | 18 + wiki/wikienv.cc | 88 ++ wiki/wikienv.h | 39 + wiki/wikiformat.cc | 2724 ++++++++++++++++++++++++++++++++++++++++++++ wiki/wikiformat.h | 605 ++++++++++ wiki/wikiline.cc | 581 ++++++++++ wiki/wikiline.h | 32 + wiki/wikitable.h | 61 + 142 files changed, 25825 insertions(+) create mode 100644 Makefile create mode 100644 Makefile.conf create mode 100755 bin/MKID.pl create mode 100755 bin/MKTABLE.pl create mode 100755 bin/MKWTABLE.pl create mode 100644 cgi/Makefile create mode 100644 cgi/main.cc create mode 100644 cgi_debug/Makefile create mode 100644 config.h create mode 100644 ext/diff-openbsd/diff.c create mode 100644 ext/diff-openbsd/diff.h create mode 100644 ext/diff-openbsd/diffreg.c create mode 100644 ext/diff.cc create mode 100644 ext/diff.h create mode 100644 ext/ml-diff.cc create mode 100644 ext/ml-diff.h create mode 100644 lib/app.cc create mode 100644 lib/app.h create mode 100644 lib/bdbmacro.h create mode 100644 lib/expr.cc create mode 100644 lib/expr.h create mode 100644 lib/filemacro.h create mode 100644 lib/form.cc create mode 100644 lib/form.h create mode 100644 lib/form_utf8-jp.cc create mode 100644 lib/form_utf8-jp.h create mode 100644 lib/form_utf8.cc create mode 100644 lib/form_utf8.h create mode 100644 lib/formfile.cc create mode 100644 lib/formfile.h create mode 100644 lib/ftable.h create mode 100644 lib/heapdebug.cc create mode 100644 lib/heapdebug.h create mode 100644 lib/http.cc create mode 100644 lib/http.h create mode 100644 lib/httpconst.h create mode 100644 lib/iso2022jp.cc create mode 100644 lib/iso2022jp.h create mode 100644 lib/mftable.h create mode 100644 lib/ml.cc create mode 100644 lib/ml.h create mode 100644 lib/mlenv.cc create mode 100644 lib/mlenv.h create mode 100644 lib/motor.cc create mode 100644 lib/motor.h create mode 100644 lib/motorconst.h create mode 100644 lib/motorenv.cc create mode 100644 lib/motorenv.h create mode 100644 lib/motorfunc.cc create mode 100644 lib/motorfunc.h create mode 100644 lib/motoroutput-jp.cc create mode 100644 lib/motoroutput-jp.h create mode 100644 lib/motoroutput.cc create mode 100644 lib/motoroutput.h create mode 100644 lib/motorvar.cc create mode 100644 lib/motorvar.h create mode 100644 lib/sigsafe.cc create mode 100644 lib/sigsafe.h create mode 100644 lib/spair.h create mode 100644 lib/ustring.h create mode 100644 lib/utf16.cc create mode 100644 lib/utf16.h create mode 100644 lib/utf8-jp.cc create mode 100644 lib/utf8-jp.h create mode 100644 lib/utf8.cc create mode 100644 lib/utf8.h create mode 100644 lib/util_apache.cc create mode 100644 lib/util_apache.h create mode 100644 lib/util_check.cc create mode 100644 lib/util_check.h create mode 100644 lib/util_const.cc create mode 100644 lib/util_const.h create mode 100644 lib/util_file.cc create mode 100644 lib/util_file.h create mode 100644 lib/util_inet.cc create mode 100644 lib/util_inet.h create mode 100644 lib/util_mimetype.cc create mode 100644 lib/util_mimetype.h create mode 100644 lib/util_proc.cc create mode 100644 lib/util_proc.h create mode 100644 lib/util_random.cc create mode 100644 lib/util_random.h create mode 100644 lib/util_string.cc create mode 100644 lib/util_string.h create mode 100644 lib/util_time.cc create mode 100644 lib/util_time.h create mode 100644 ml/Makefile create mode 100644 ml/main.cc create mode 100644 ml_debug/Makefile create mode 100644 modules/ml-addon.cc create mode 100644 modules/ml-addon.h create mode 100644 modules/ml-apache.cc create mode 100644 modules/ml-apache.h create mode 100644 modules/ml-bool.cc create mode 100644 modules/ml-bool.h create mode 100644 modules/ml-cookielogin.cc create mode 100644 modules/ml-cookielogin.h create mode 100644 modules/ml-db.cc create mode 100644 modules/ml-db.h create mode 100644 modules/ml-defun.cc create mode 100644 modules/ml-defun.h create mode 100644 modules/ml-encode.cc create mode 100644 modules/ml-encode.h create mode 100644 modules/ml-formvar.cc create mode 100644 modules/ml-formvar.h create mode 100644 modules/ml-include.cc create mode 100644 modules/ml-include.h create mode 100644 modules/ml-inet.cc create mode 100644 modules/ml-inet.h create mode 100644 modules/ml-math.cc create mode 100644 modules/ml-math.h create mode 100644 modules/ml-motor.cc create mode 100644 modules/ml-motor.h create mode 100644 modules/ml-sendmail.cc create mode 100644 modules/ml-sendmail.h create mode 100644 modules/ml-sqlite3.cc create mode 100644 modules/ml-sqlite3.h create mode 100644 modules/ml-store.cc create mode 100644 modules/ml-store.h create mode 100644 modules/ml-string.cc create mode 100644 modules/ml-string.h create mode 100644 modules/ml-struct.cc create mode 100644 modules/ml-struct.h create mode 100644 modules/ml-time.cc create mode 100644 modules/ml-time.h create mode 100644 modules/ml-variable.cc create mode 100644 modules/ml-variable.h create mode 100644 modules/ml-wiki.cc create mode 100644 modules/ml-wiki.h create mode 100644 modules/ml-xml.cc create mode 100644 modules/ml-xml.h create mode 100644 modules/motor-function.cc create mode 100644 modules/motor-function.h create mode 100644 wiki/wikicmd.cc create mode 100644 wiki/wikicmd.h create mode 100644 wiki/wikienv.cc create mode 100644 wiki/wikienv.h create mode 100644 wiki/wikiformat.cc create mode 100644 wiki/wikiformat.h create mode 100644 wiki/wikiline.cc create mode 100644 wiki/wikiline.h create mode 100644 wiki/wikitable.h diff --git a/Makefile b/Makefile new file mode 100644 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 + +.PHONY: dist + +dist: _SUBDIR diff --git a/Makefile.conf b/Makefile.conf new file mode 100644 index 0000000..3b5f8d1 --- /dev/null +++ b/Makefile.conf @@ -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 index 0000000..52a9acb --- /dev/null +++ b/bin/MKID.pl @@ -0,0 +1,64 @@ +#! /usr/bin/perl + +my @ID; +my @SRCS; + +open (IN, "../Makefile.conf"); +while () { + 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 () { + 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 index 0000000..499b374 --- /dev/null +++ b/bin/MKTABLE.pl @@ -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 () { + 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 () { + 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 index 0000000..7139c82 --- /dev/null +++ b/bin/MKWTABLE.pl @@ -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 () { + 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 .= ); + } + 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 index 0000000..b95aed5 --- /dev/null +++ b/cgi/Makefile @@ -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 index 0000000..a9a3503 --- /dev/null +++ b/cgi/main.cc @@ -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 +#include +#include + +using namespace std; + +static ustring glue1 () { + char* cwd = getcwd (NULL, 0); + ustring p; + std::vector 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 index 0000000..1f94478 --- /dev/null +++ b/cgi_debug/Makefile @@ -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 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 index 0000000..af77b37 --- /dev/null +++ b/ext/diff-openbsd/diff.c @@ -0,0 +1,455 @@ +/* $OpenBSD: diff.c,v 1.49 2007/03/01 21:48:32 jmc Exp $ */ + +/* + * Copyright (c) 2003 Todd C. Miller + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..817057d --- /dev/null +++ b/ext/diff-openbsd/diff.h @@ -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 +#include + +/* + * 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 index 0000000..7dc19b6 --- /dev/null +++ b/ext/diff-openbsd/diffreg.c @@ -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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..0346c5b --- /dev/null +++ b/ext/diff.cc @@ -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 +#include +#include + +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, "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 index 0000000..37956d7 --- /dev/null +++ b/ext/diff.h @@ -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 index 0000000..6bf1580 --- /dev/null +++ b/ext/ml-diff.cc @@ -0,0 +1,42 @@ +#include "ml-diff.h" +#include "diff.h" +#include "ml.h" +#include "mlenv.h" +#include "expr.h" +#include "ustring.h" +#include + +/*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 index 0000000..315ce8f --- /dev/null +++ b/ext/ml-diff.h @@ -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 index 0000000..8ea81b2 --- /dev/null +++ b/lib/app.cc @@ -0,0 +1,193 @@ +#include "app.h" +#include "motorenv.h" +#include "util_const.h" +#include "util_check.h" +#include "util_string.h" +#include +#include +#include + +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 (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 (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 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 + +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 index 0000000..92d6193 --- /dev/null +++ b/lib/bdbmacro.h @@ -0,0 +1,187 @@ +#ifndef BDBMACRO_H +#define BDBMACRO_H + +#include "ustring.h" +#ifdef Linux +#include +#else +#include +#endif +#include + +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 index 0000000..c223949 --- /dev/null +++ b/lib/expr.cc @@ -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 +#include +#include +#include +#include + +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::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& 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& 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 > { +public: + KwList () {}; + ~KwList () {}; + void insertVar (const ustring& name, bool f) { + erase (name); + insert (KwList::value_type (name, std::pair (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* params, paramList *kwlist, std::vector* 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 index 0000000..17d7e4b --- /dev/null +++ b/lib/expr.h @@ -0,0 +1,37 @@ +#ifndef EXPR_H +#define EXPR_H + +#include "ml.h" +#include "ustring.h" +#include + +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& args); +MNode* buildArgs (int start, std::vector& 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* params, paramList *kwlist, std::vector* keywords, MNode** rest); + +#endif /* EXPR_H */ diff --git a/lib/filemacro.h b/lib/filemacro.h new file mode 100644 index 0000000..fb6df83 --- /dev/null +++ b/lib/filemacro.h @@ -0,0 +1,89 @@ +#ifndef FILEMACRO_H +#define FILEMACRO_H + +#include +#include +#include +#include +#include +#include +#ifndef HAVE_OPENLOCK +#include +#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 index 0000000..32627fd --- /dev/null +++ b/lib/form.cc @@ -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 +#include +#include + +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* 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* 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* 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* t; + std::vector::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* CGIForm::insertName (const ustring& name) { + std::vector* ans = new std::vector; + 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 index 0000000..c0cf273 --- /dev/null +++ b/lib/form.h @@ -0,0 +1,54 @@ +#ifndef FORM_H +#define FORM_H + +#include "ustring.h" +#include +#include +#include +#include + +class MotorEnv; +class CGIForm { + public: + typedef boost::unordered_map map_t; + typedef boost::ptr_vector > indexary; +// typedef boost::ptr_vector pool_t; + typedef std::vector 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* 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 index 0000000..6c2240c --- /dev/null +++ b/lib/form_utf8-jp.cc @@ -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 index 0000000..0b699a1 --- /dev/null +++ b/lib/form_utf8-jp.h @@ -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 index 0000000..271460c --- /dev/null +++ b/lib/form_utf8.cc @@ -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 index 0000000..e727320 --- /dev/null +++ b/lib/form_utf8.h @@ -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 index 0000000..288cde9 --- /dev/null +++ b/lib/formfile.cc @@ -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 +#include +#include +#include + +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 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 (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 m; + boost::match_results 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 index 0000000..ec7e3cf --- /dev/null +++ b/lib/formfile.h @@ -0,0 +1,42 @@ +#ifndef FORMFILE_H +#define FORMFILE_H + +#include "form.h" +#include "motorenv.h" +#include "filemacro.h" +#include +#include + +class CGIFormFile: public CGIForm { + public: + typedef std::pair part; + + ustring tmpfile; + ustring boundary; + uregex re1; + uregex reN; + FileMacro fp; + char* mapdata; + size_t mapsize; + std::vector parts; + std::vector 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 index 0000000..f413fae --- /dev/null +++ b/lib/ftable.h @@ -0,0 +1,65 @@ +#ifndef FTABLE_H +#define FTABLE_H + +#include "ustring.h" +#include +#include + +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 { +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 iSTableV; + boost::unordered_map iSTableM; + + MTable (FTableSupport::ftable_t* t, FTableSupport::ftable_t* s): FTable (t) { + int i; + FTable* f; + ustring p; + boost::unordered_map::iterator it; + + for (i = 0; t[i].name; i ++) { + f = new FTable; + iSTableV.push_back (f); + iSTableM.insert (boost::unordered_map::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 index 0000000..dc809e3 --- /dev/null +++ b/lib/heapdebug.cc @@ -0,0 +1,150 @@ +#include "heapdebug.h" +#include +#include +#include +#include +#include +#include + +extern "C"{ +#ifdef Linux +#include +#else +#include +#endif +} +#include +#include +#include +#include + +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 index 0000000..70f24b7 --- /dev/null +++ b/lib/heapdebug.h @@ -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 index 0000000..8691bca --- /dev/null +++ b/lib/http.cc @@ -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 +#include +#include + +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::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::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::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 << "\n" + "\n" + "\n"; + o << "\n" + "\n"; + o << "\n" + "\n" + "\n" + "\n" + "\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 index 0000000..febf631 --- /dev/null +++ b/lib/http.h @@ -0,0 +1,37 @@ +#ifndef HTTP_H +#define HTTP_H + +#include "ustring.h" +#include +#include + +class MotorEnv; +class HTTPResponse { + public: + std::vector setcookie; + bool cookieDone; + ustring cookie; + boost::unordered_map 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 index 0000000..e06947e --- /dev/null +++ b/lib/httpconst.h @@ -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 index 0000000..1d838fc --- /dev/null +++ b/lib/iso2022jp.cc @@ -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 index 0000000..e0f5821 --- /dev/null +++ b/lib/iso2022jp.h @@ -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 index 0000000..2d6e57b --- /dev/null +++ b/lib/mftable.h @@ -0,0 +1,36 @@ +#ifndef MFTABLE_H +#define MFTABLE_H + +#include "ustring.h" +#include +//#include +#include + +class MlEnv; + +namespace MFTableSupport { + typedef struct { + const char* name; + int namelen; + void (*fn) (std::vector& args, MlEnv* mlenv); + } mftable_t; +} + +class MFTable: public boost::unordered_map { +public: + boost::ptr_vector iSTableV; + boost::unordered_map 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 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 +#include +#include +#include +#include + +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 (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 (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 (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 = ⊤ + 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 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 +#include + +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 index 0000000..38aa379 --- /dev/null +++ b/lib/mlenv.cc @@ -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 +#include + +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 (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 (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 (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 (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::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 (c, ln)); +} + +void MlEnv::logLinenum (MNode* c) { + boost::unordered_map::iterator i; + + i = linenum.find (c); + if (i == linenum.end ()) { + *log << ": "; + } 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 index 0000000..43f0835 --- /dev/null +++ b/lib/mlenv.h @@ -0,0 +1,113 @@ +#ifndef MLENV_H +#define MLENV_H + +#include "ftable.h" +#include "motorvar.h" +#include +#include +#include + +class MNode; +class MotorEnv; + +class MlEnv { + public: + boost::unordered_map linenum; + MotorVar globalVar; + boost::ptr_vector 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 index 0000000..ca61a63 --- /dev/null +++ b/lib/motor.cc @@ -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 +#include + +/*DOC: +==HTMLMotor== + +*/ + +using namespace std; + +#define cSCOM "(?:)?" + +// ============================================================ +#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::value_type (name, value)); +} + +MotorObj::MotorObjVec* HTMLMotor::getTemplate (const ustring& name) { + boost::unordered_map::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 index 0000000..5d2a529 --- /dev/null +++ b/lib/motor.h @@ -0,0 +1,203 @@ +#ifndef MOTOR_H +#define MOTOR_H + +#include "motoroutput.h" +#include "motorenv.h" +#include "ustring.h" +#include +#include +#include + +class HTMLMotor; +class MotorObj { + public: + typedef boost::ptr_vector 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 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 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 index 0000000..9041f7a --- /dev/null +++ b/lib/motorconst.h @@ -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 index 0000000..e371c81 --- /dev/null +++ b/lib/motorenv.cc @@ -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 +#include +#include + +//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 (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 index 0000000..d521814 --- /dev/null +++ b/lib/motorenv.h @@ -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 + +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 index 0000000..c59064a --- /dev/null +++ b/lib/motorfunc.cc @@ -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& 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 index 0000000..083fa01 --- /dev/null +++ b/lib/motorfunc.h @@ -0,0 +1,11 @@ +#ifndef MOTORFUNC_H +#define MOTORFUNC_H + +#include "ustring.h" +#include + +class MlEnv; + +void execMotorFunc (const ustring& name, std::vector& args, MlEnv* mlenv); + +#endif /* MOTORFUNC_H */ diff --git a/lib/motoroutput-jp.cc b/lib/motoroutput-jp.cc new file mode 100644 index 0000000..0038c3a --- /dev/null +++ b/lib/motoroutput-jp.cc @@ -0,0 +1,61 @@ +#include "motoroutput-jp.h" +#include "utf8-jp.h" +#include +#include + +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 index 0000000..844de3b --- /dev/null +++ b/lib/motoroutput-jp.h @@ -0,0 +1,54 @@ +#ifndef MOTOROUTPUT_JP_H +#define MOTOROUTPUT_JP_H + +#include "motoroutput.h" +#include "utf8-jp.h" +#include + +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 index 0000000..85f758a --- /dev/null +++ b/lib/motoroutput.cc @@ -0,0 +1,104 @@ +#include "motoroutput.h" +#include "util_string.h" +#include "ustring.h" +#include "utf8.h" +#include "motorconst.h" +#include + +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 ("<")); + } else if (m[2].matched) { // > + out (CharConst (">")); + } else if (m[3].matched) { // & + out (CharConst ("&")); + } else if (m[4].matched) { // " + out (CharConst (""")); + } else if (m[5].matched) { // ' + out (CharConst ("'")); + } else if (m[6].matched) { // \n + out (CharConst ("
\n")); + } else if (m[7].matched) { // http... + out (CharConst ("")); + outamp (m[7].first, m[7].second, &re_encode); + out (CharConst ("")); + } 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 (" ")); +} + +/* ============================================================ */ +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 index 0000000..103cde7 --- /dev/null +++ b/lib/motoroutput.h @@ -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 index 0000000..66dc0a1 --- /dev/null +++ b/lib/motorvar.cc @@ -0,0 +1,57 @@ +#include "motorvar.h" +#include "ustring.h" +#include +#include + +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 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::value_type (name, true)); +} + +bool MotorErrorVar::getVar (const ustring& name) { + boost::unordered_map::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 index 0000000..e6bf567 --- /dev/null +++ b/lib/motorvar.h @@ -0,0 +1,38 @@ +#ifndef MOTORVAR_H +#define MOTORVAR_H + +#include "ml.h" +#include "ustring.h" +#include +#include + +class MotorSet: public boost::unordered_set { + 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 { + 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 { /* 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 index 0000000..b0047ea --- /dev/null +++ b/lib/sigsafe.cc @@ -0,0 +1,4 @@ +#include "sigsafe.h" + +int SigSafeFlag = 0; + diff --git a/lib/sigsafe.h b/lib/sigsafe.h new file mode 100644 index 0000000..ab30877 --- /dev/null +++ b/lib/sigsafe.h @@ -0,0 +1,68 @@ +#ifndef SIGSAFE_H +#define SIGSAFE_H + +#include +#include +#include + +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 index 0000000..f232c13 --- /dev/null +++ b/lib/spair.h @@ -0,0 +1,19 @@ +#ifndef SPAIR_H +#define SPAIR_H + +#include "ustring.h" +#include +#include +#include +#include + +#define CharConst(s) (s), (sizeof (s) - 1) + +typedef std::pair 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 index 0000000..ffabdb6 --- /dev/null +++ b/lib/ustring.h @@ -0,0 +1,56 @@ +#ifndef USTRING_H +#define USTRING_H + +#include +#include +#include +#include + +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 ustring; +typedef ustring::const_iterator uiterator; +typedef std::pair upair; +typedef boost::match_results umatch; +typedef boost::basic_regex > 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 index 0000000..7918c90 --- /dev/null +++ b/lib/utf16.cc @@ -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 index 0000000..f3f392e --- /dev/null +++ b/lib/utf16.h @@ -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 index 0000000..18026b1 --- /dev/null +++ b/lib/utf8-jp.cc @@ -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 index 0000000..c642215 --- /dev/null +++ b/lib/utf8-jp.h @@ -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 index 0000000..1065a34 --- /dev/null +++ b/lib/utf8.cc @@ -0,0 +1,260 @@ +#include "utf8.h" +#include "ustring.h" +#include + +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 index 0000000..e3477b9 --- /dev/null +++ b/lib/utf8.h @@ -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 index 0000000..8092c40 --- /dev/null +++ b/lib/util_apache.cc @@ -0,0 +1,54 @@ +#include "util_apache.h" +#include "util_const.h" +#include "util_string.h" +#include "httpconst.h" +#include "ustring.h" +#include + +ustring apacheAbsolutePath (const ustring& url) { + ustring ans; + std::vector ary; + std::vector::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 index 0000000..861857a --- /dev/null +++ b/lib/util_apache.h @@ -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 index 0000000..3c06365 --- /dev/null +++ b/lib/util_check.cc @@ -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 index 0000000..a8f5e21 --- /dev/null +++ b/lib/util_check.h @@ -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 index 0000000..8c94e38 --- /dev/null +++ b/lib/util_const.cc @@ -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 index 0000000..93c39f4 --- /dev/null +++ b/lib/util_const.h @@ -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 index 0000000..264cc66 --- /dev/null +++ b/lib/util_file.cc @@ -0,0 +1,110 @@ +#include "util_file.h" +#include "config.h" +#include "ustring.h" +#include "util_const.h" +#include "filemacro.h" +#include +#include +#include + +//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 index 0000000..fad2572 --- /dev/null +++ b/lib/util_file.h @@ -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 index 0000000..004f599 --- /dev/null +++ b/lib/util_inet.cc @@ -0,0 +1,29 @@ +#include "util_inet.h" +#include "util_string.h" +#include "ustring.h" +#include +#include +#include +#include + +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 index 0000000..70a6a83 --- /dev/null +++ b/lib/util_inet.h @@ -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 index 0000000..5cd5815 --- /dev/null +++ b/lib/util_mimetype.cc @@ -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 + +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 index 0000000..7b9cecb --- /dev/null +++ b/lib/util_mimetype.h @@ -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 index 0000000..ba03b45 --- /dev/null +++ b/lib/util_proc.cc @@ -0,0 +1,149 @@ +#include "util_proc.h" +#include "config.h" +#include "ustring.h" +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..4b50ebf --- /dev/null +++ b/lib/util_proc.h @@ -0,0 +1,35 @@ +#ifndef UTIL_PROC_H +#define UTIL_PROC_H + +#include "ustring.h" +//#include +#include + +class ProcRW { + public: + pid_t pid; + int rfd; + int wfd; +// boost::circular_buffer cb(3); +// boost::circular_buffer 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 index 0000000..dc80947 --- /dev/null +++ b/lib/util_random.cc @@ -0,0 +1,116 @@ +#include "util_random.h" +#include "ustring.h" +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000..a57c5e6 --- /dev/null +++ b/lib/util_random.h @@ -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 index 0000000..df88c95 --- /dev/null +++ b/lib/util_string.cc @@ -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 +#include +#include +#include +#include +#include +#include + +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& 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 index 0000000..cc3f72d --- /dev/null +++ b/lib/util_string.h @@ -0,0 +1,176 @@ +#ifndef UTIL_STRING_H +#define UTIL_STRING_H + +#include "ustring.h" +#include "utf16.h" +#include + +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& 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 index 0000000..3259a7f --- /dev/null +++ b/lib/util_time.cc @@ -0,0 +1,51 @@ +#include "util_time.h" +#include "ustring.h" +#include + +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 index 0000000..73cdc67 --- /dev/null +++ b/lib/util_time.h @@ -0,0 +1,12 @@ +#ifndef UTIL_TIME_H +#define UTIL_TIME_H + +#include "ustring.h" +#include + +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 index 0000000..265cc18 --- /dev/null +++ b/ml/Makefile @@ -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 diff --git a/ml/main.cc b/ml/main.cc new file mode 100644 index 0000000..e7ed0b4 --- /dev/null +++ b/ml/main.cc @@ -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 +#include +#include + +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 index 0000000..257c4bf --- /dev/null +++ b/ml_debug/Makefile @@ -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 index 0000000..e357aba --- /dev/null +++ b/modules/ml-addon.cc @@ -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 +#include + +/*DOC: +==add-on command== +外部プログラムを呼び出す。 + +*/ + +typedef struct { + ProcRW proc; + ustring cmd; + std::vector par; + std::vector params; + std::vector 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 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 index 0000000..22dd367 --- /dev/null +++ b/modules/ml-addon.h @@ -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 index 0000000..fc2bc0e --- /dev/null +++ b/modules/ml-apache.cc @@ -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 +#include + +/*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 params; + std::vector 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, ¶ms, 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 (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 index 0000000..1d69aaa --- /dev/null +++ b/modules/ml-apache.h @@ -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 index 0000000..e91606b --- /dev/null +++ b/modules/ml-bool.cc @@ -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 + +/*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 index 0000000..7b506cd --- /dev/null +++ b/modules/ml-bool.h @@ -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 index 0000000..cc7e8dd --- /dev/null +++ b/modules/ml-cookielogin.cc @@ -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 + +/*DOC: +==cookie authentication module== + +*/ + +static MLCookieLogin* objref (MlEnv* mlenv) { + assert (mlenv->module->id == cMLCookieLoginID); + return (MLCookieLogin*)mlenv->module; +} + +/* + session record: + key: + val: :::: +*/ + +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 (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 params; + std::vector 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, ¶ms, 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 (limit)).append (uColon).append (boost::lexical_cast (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 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 keys; + std::vector::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 keys; + std::vector::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 index 0000000..37a4e89 --- /dev/null +++ b/modules/ml-cookielogin.h @@ -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 index 0000000..ac68283 --- /dev/null +++ b/modules/ml-db.cc @@ -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 +#include + +/*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 params; + std::vector keywords; + MNode* rest; + static paramList kwlist[] = { + {CharConst ("limit"), false}, + {CharConst ("xserial"), true}, + {NULL, 0, 0} + }; + + mlenv->module = &obj; + setParams (arg, 1, ¶ms, 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 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& vars, std::vector& 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& vars, std::vector& 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 vars; + std::vector 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 vars; + std::vector 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 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 index 0000000..4224d09 --- /dev/null +++ b/modules/ml-db.h @@ -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 index 0000000..3afb779 --- /dev/null +++ b/modules/ml-defun.cc @@ -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 +#include + +/*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 index 0000000..c14f508 --- /dev/null +++ b/modules/ml-defun.h @@ -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 index 0000000..e09d5cd --- /dev/null +++ b/modules/ml-encode.cc @@ -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 +#include +#include + +/*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 index 0000000..4e686dc --- /dev/null +++ b/modules/ml-encode.h @@ -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 index 0000000..e1ab238 --- /dev/null +++ b/modules/ml-formvar.cc @@ -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 +#include +#include +#include + +/*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 params; + std::vector 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, ¶ms, 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 params; + std::vector keywords; + static paramList kwlist[] = { + {CharConst ("max"), false}, + {CharConst ("filter"), false}, + {CharConst ("error-filter"), false}, + {NULL, 0, 0} + }; + + setParams (arg, 2, ¶ms, 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 index 0000000..0523be2 --- /dev/null +++ b/modules/ml-formvar.h @@ -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 index 0000000..bb399f7 --- /dev/null +++ b/modules/ml-include.cc @@ -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 + +/*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 index 0000000..9a01a62 --- /dev/null +++ b/modules/ml-include.h @@ -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 index 0000000..6dd90d4 --- /dev/null +++ b/modules/ml-inet.cc @@ -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 + +/*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 index 0000000..d47a641 --- /dev/null +++ b/modules/ml-inet.h @@ -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 index 0000000..cc87250 --- /dev/null +++ b/modules/ml-math.cc @@ -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 +#include + +/*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 index 0000000..31f99a4 --- /dev/null +++ b/modules/ml-math.h @@ -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 index 0000000..b661e7f --- /dev/null +++ b/modules/ml-motor.cc @@ -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 ("&")); + 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 params; + std::vector keywords; + static paramList kwlist[] = { + {CharConst ("type"), false}, + {CharConst ("error"), true}, + {NULL, 0, 0} + }; + + setParams (arg, 1, ¶ms, 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 index 0000000..b5c7cab --- /dev/null +++ b/modules/ml-motor.h @@ -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 index 0000000..a2fc073 --- /dev/null +++ b/modules/ml-sendmail.cc @@ -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 +#include +#include + +#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& 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 taddr; + ustring subject; + MNodePtr h; + ustring t; + std::vector params; + std::vector keywords; + static paramList kwlist[] = { + {CharConst ("from"), false}, + {CharConst ("to"), false}, + {NULL, 0, 0} + }; + + setParams (arg, 1, ¶ms, 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 index 0000000..643350e --- /dev/null +++ b/modules/ml-sendmail.h @@ -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 index 0000000..55e770a --- /dev/null +++ b/modules/ml-sqlite3.cc @@ -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 + +/*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 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 params; + std::vector keywords; + MNode* rest; + static paramList kwlist[] = { + {CharConst ("limit"), false}, + {CharConst ("create"), true}, + {NULL, 0, 0} + }; + + mlenv->module = &obj; + setParams (arg, 1, ¶ms, 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 params; + std::vector keywords; + MNode* rest; + static paramList kwlist[] = { + {CharConst ("bind"), false}, + {CharConst ("answer"), false}, + {CharConst ("@answer"), false}, + {NULL, 0, 0} + }; + + setParams (arg, 1, ¶ms, 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 (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 index 0000000..36081e2 --- /dev/null +++ b/modules/ml-sqlite3.h @@ -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 +extern "C" { +#include "sqlite3.h" +} + +class MNode; +class MlEnv; + +class MLSqlite3: public MLFunc { + public: + typedef boost::ptr_vector 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 index 0000000..79bf0b2 --- /dev/null +++ b/modules/ml-store.cc @@ -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 +#include +#include +#include +#include +#include + +#define StoreFileNameMax 48 + +/*DOC: +==data store module== + +*/ +/* + *record of serial.db + key: + val: :::: +*/ +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 (tm); + subdir = strYMD (tm); + subdir.append (uSlash); + + openDB (db, lock, mlenv); + + if (db.get (uDash, x)) { + srl = boost::lexical_cast (x) + 1; + } else { + srl = 1; + } + + n = srl; + xs = randomKey (srl); + + x = boost::lexical_cast (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 (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 xs; + boost::ptr_vector 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 (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 params; + std::vector keywords; + static paramList kwlist[] = { + {CharConst ("code"), false}, + {NULL, 0, 0} + }; + + setParams (arg, 1, ¶ms, 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 params; + std::vector keywords; + static paramList kwlist[] = { + {CharConst ("code"), false}, + {NULL, 0, 0} + }; + + setParams (arg, 2, ¶ms, 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 params; + std::vector 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, ¶ms, kwlist_motor, &keywords, NULL); + else + setParams (arg, 1, ¶ms, 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 params; + std::vector 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, ¶ms, 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 index 0000000..ac98135 --- /dev/null +++ b/modules/ml-store.h @@ -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 index 0000000..19d4e9f --- /dev/null +++ b/modules/ml-string.cc @@ -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 +#include + +/*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 (val); + return newMNode_str (new ustring (u)); + } + val = floor (val / 1024. * 10.) / 10.; + if (val < 900) { + u = boost::lexical_cast (val); + u.append (CharConst ("K")); + return newMNode_str (new ustring (u)); + } + val = floor (val / 1024. * 10. ) / 10.; + if (val < 900) { + u = boost::lexical_cast (val); + u.append (CharConst ("M")); + return newMNode_str (new ustring (u)); + } + val = floor (val / 1024. * 10.) / 10.; + if (val < 900) { + u = boost::lexical_cast (val); + u.append (CharConst ("G")); + return newMNode_str (new ustring (u)); + } + val = floor (val / 1024. * 10.) / 10.; + if (val < 900) { + u = boost::lexical_cast (val); + u.append (CharConst ("T")); + return newMNode_str (new ustring (u)); + } + val = floor (val / 1024. * 10.) / 10.; + u = boost::lexical_cast (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 params; + std::vector keywords; + static paramList kwlist[] = { + {CharConst ("i"), true}, + {NULL, 0, 0} + }; + + setParams (arg, 2, ¶ms, 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 params; + std::vector keywords; + static paramList kwlist[] = { + {CharConst ("i"), true}, + {NULL, 0, 0} + }; + + setParams (arg, 2, ¶ms, 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 index 0000000..0d8cad5 --- /dev/null +++ b/modules/ml-string.h @@ -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 index 0000000..b23f641 --- /dev/null +++ b/modules/ml-struct.cc @@ -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 + +/*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 lv; + double i; + MNodePtr h; + MNodePtr ans; + int j; + bool kp; + std::vector params; + std::vector keywords; + MNode* rest; + static paramList kwlist[] = { + {CharConst ("step"), false}, + {CharConst ("array"), false}, + {NULL, 0, 0} + }; + + setParams (arg, 3, ¶ms, 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 lv; + std::vector setvar; + MNodePtr vlist; + ustring iv; + size_t i, n; + int it, nlv, nsv; + ustring val; + MNodePtr ans; + MNodePtr h; + bool kp; + std::vector params; + std::vector keywords; + MNode* rest; + static paramList kwlist[] = { + {CharConst ("index"), false}, + {CharConst ("setvar"), false}, + {NULL, 0, 0} + }; + + setParams (arg, 1, ¶ms, 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 lv; + int it, iu; + MNodePtr vlist; + MNodePtr list; + ustring iv; + std::vector llv; + MNode* a; + MNodePtr ans; + MNodePtr h; + int i, n; + bool kp; + std::vector params; + std::vector keywords; + MNode* rest; + static paramList kwlist[] = { + {CharConst ("index"), false}, + {NULL, 0, 0} + }; + + setParams (arg, 2, ¶ms, 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 index 0000000..36e0160 --- /dev/null +++ b/modules/ml-struct.h @@ -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 index 0000000..eb277c6 --- /dev/null +++ b/modules/ml-time.cc @@ -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 +#include +#include + +/*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 index 0000000..47a8c07 --- /dev/null +++ b/modules/ml-time.h @@ -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 index 0000000..efa74f0 --- /dev/null +++ b/modules/ml-variable.cc @@ -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 + +/*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 index 0000000..81309fd --- /dev/null +++ b/modules/ml-variable.h @@ -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 index 0000000..9fb178d --- /dev/null +++ b/modules/ml-wiki.cc @@ -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 + +/*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 params; + std::vector keywords; + MNode* rest; + static paramList kwlist[] = { + {CharConst ("superuser"), true}, + {CharConst ("protect"), true}, + {NULL, 0, 0} + }; + + setParams (arg, 0, ¶ms, 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 index 0000000..e557787 --- /dev/null +++ b/modules/ml-wiki.h @@ -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 index 0000000..ca6d151 --- /dev/null +++ b/modules/ml-xml.cc @@ -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 +#include +#include + +/*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 ptr; + std::vector 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 ("\n")); + xmlWrite (&out, xml ()); + + return NULL; +} diff --git a/modules/ml-xml.h b/modules/ml-xml.h new file mode 100644 index 0000000..6b3ce6e --- /dev/null +++ b/modules/ml-xml.h @@ -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 index 0000000..f776725 --- /dev/null +++ b/modules/motor-function.cc @@ -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 + +/*DOC: +===eval=== + &[;[eval:''NAME'']] + &[;[eval:''NAME'':''ARGS'':...]] + +*/ +//#MTFUNC eval mf_eval +void mf_eval (std::vector& 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& 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& 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& 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& 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 index 0000000..6a2533f --- /dev/null +++ b/modules/motor-function.h @@ -0,0 +1,15 @@ +#ifndef MOTOR_FUNCTION_H +#define MOTOR_FUNCTION_H + +#include "ustring.h" +#include + +class MlEnv; + +void mf_eval (std::vector& args, MlEnv* mlenv); +void mf_js (std::vector& args, MlEnv* mlenv); +void mf_url (std::vector& args, MlEnv* mlenv); +void mf_pad0 (std::vector& args, MlEnv* mlenv); +void mf_wiki (std::vector& args, MlEnv* mlenv); + +#endif /* MOTOR_FUNCTION_H */ diff --git a/wiki/wikicmd.cc b/wiki/wikicmd.cc new file mode 100644 index 0000000..e54de44 --- /dev/null +++ b/wiki/wikicmd.cc @@ -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 +#include +#include + +/*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 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 args; + std::vector 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 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 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 index 0000000..9a03eb1 --- /dev/null +++ b/wiki/wikicmd.h @@ -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 index 0000000..f14e5d2 --- /dev/null +++ b/wiki/wikienv.cc @@ -0,0 +1,88 @@ +#include "wikienv.h" +#include "motorconst.h" +#include "ml.h" +#include "ustring.h" +#include + +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});" + ")" +"|" + "(:)" +"|" + "( )" +"|" + "(
)" +"|" + "(''+)" +); +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 index 0000000..f749e81 --- /dev/null +++ b/wiki/wikienv.h @@ -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 index 0000000..f9df8ce --- /dev/null +++ b/wiki/wikiformat.cc @@ -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 + +#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 "

" +#define uPe "

\n" +#define uH1 "

" +#define uH2 "

" +#define uH3 "

" +#define uH4 "

" +#define uH5 "

" +#define uH6 "
" +#define uH1e "
\n" +#define uH2e "\n" +#define uH3e "\n" +#define uH4e "\n" +#define uH5e "\n" +#define uH6e "\n" +#define uHR "
\n" +#define uID " id=" +#define uClass " class=" +#define uQ "\"" +#define unbsp " " + +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 ("outHTMLnoCtrl (anchor)->out (CharConst ("\">"))->outText (title)->out (CharConst ("")); + } 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 ("\n")); +#else + outputEndDiv (out); +#endif /* DEBUG */ + } +} + +void WikiBlockH::outputBeginDiv (int lv, MotorOutput* out) { + switch (lv) { + case 1: out->out (CharConst ("
\n")); break; + case 2: out->out (CharConst ("
\n")); break; + case 3: out->out (CharConst ("
\n")); break; + case 4: out->out (CharConst ("
\n")); break; + case 5: out->out (CharConst ("
\n")); break; + case 6: out->out (CharConst ("
\n")); break; + } +} + +void WikiBlockH::outputEndDiv (MotorOutput* out) { + out->out (CharConst ("
\n")); +} + +/* ============================================================ */ +/*DOC: +===フォーマット済みテキストブロック=== + 行頭に空白文字を書くと,
〜
タグで囲まれます。 + +*/ +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 ("
"))->outText (html)->out (CharConst ("
\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 ("
  • ")); + out->outText (html); + outputBlock (out); + out->out (CharConst ("
  • \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 ("
      \n")); + outputBlock (out); + out->out (CharConst ("
    \n")); + break; + case BlockItemOL: + out->out (CharConst ("
      \n")); + outputBlock (out); + out->out (CharConst ("
    \n")); + break; + case BlockItemNL: + out->out (CharConst ("
      \n")); + outputBlock (out); + out->out (CharConst ("
    \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 ("
    \n")); + for (i = 0; i < html1.size (); i ++) { + out->out (CharConst ("
    "))->outText (html1[i])->out (CharConst ("
    \n")); + out->out (CharConst ("
    "))->outText (html2[i])->out (CharConst ("
    \n")); + } + out->out (CharConst ("
    \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|ヘッダタグ()を出力する。| +|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 ("out (CharConst ("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 (colspan))->out (CharConst (uQ)); + if (rowspan > 1) + out->out (CharConst (" colspan=" uQ))->out (boost::lexical_cast (rowspan))->out (CharConst (uQ)); + } else { + if (colspan > 1) + out->out (CharConst (" colspan=" uQ))->out (boost::lexical_cast (colspan))->out (CharConst (uQ)); + if (rowspan > 1) + out->out (CharConst (" rowspan=" uQ))->out (boost::lexical_cast (rowspan))->out (CharConst (uQ)); + } + out->out (CharConst (">")); +} + +void WikiBlockTable::TableCell::outputTDe (MotorOutput* out) { + if (fheader) + out->out (CharConst ("\n")); + else + out->out (CharConst ("\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 ("\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 ("out (CharConst (" border=\"0\"")); + else + out->out (CharConst (" border=\"1\"")); + if (cellspacing >= 0) + out->out (CharConst (" cellspacing=\""))->out (boost::lexical_cast (cellspacing))->out (CharConst ("\"")); + else + out->out (CharConst (" cellspacing=\"0\"")); + if (cellpadding >= 0) + out->out (CharConst (" cellpadding=\""))->out (boost::lexical_cast (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 ("\n")); + for (i = 0; i < ary.size (); i ++) { + col = &ary[i]; + cell = &(*col)[j]; + outputTBodyCell (out, cell); + } + out->out (CharConst ("\n")); + } + } + } else { + for (i = 0; i < ary.size (); i ++) { + col = &ary[i]; + out->out (CharConst ("\n")); + for (j = 0; j < col->size (); j ++) { + cell = &(*col)[j]; + outputTBodyCell (out, cell); + } + out->out (CharConst ("\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 (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 ("\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 ("
    \n")); + outputBlock (out); + out->out (CharConst ("
    \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 (" 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 ("
    \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 ("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 ("\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& 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& 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& 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& 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& 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& 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& 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& 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)) { // � + out->out (ustring (sp.matchBegin (), sp.matchEnd ())); + } else if (sp.match (12)) { // � + 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)) { //
    + out->out (CharConst ("
    ")); + } 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 args; + wikiLine_5 (sp, args); + if (wf->fnM (args, out, this)) + return; + } + break; + case WikiArgM2: + { + std::vector 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 args; + wikiLine_8 (sp, args); + if (wf->fnM (args, out, this)) + return; + } + break; + case WikiArgM2: + { + std::vector 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& 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& 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& 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& 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 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 (""))->outText (out2.ans)->out (CharConst ("")); +// outQuot (out, n2 - 5); + sp.rewind (n2 - 5); + } else if (n >= 3) { + outQuot (out, n1 - 3); + out->out (CharConst (""))->outText (out2.ans)->out (CharConst ("")); +// outQuot (out, n2 - 3); + sp.rewind (n2 - 3); + } else if (n >= 2) { + outQuot (out, n1 - 2); + out->out (CharConst (""))->outText (out2.ans)->out (CharConst ("")); +// 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 index 0000000..ea10b59 --- /dev/null +++ b/wiki/wikiformat.h @@ -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 +#include +#include +#include + +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 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 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 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 html1; + std::vector 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 block; + bool fheader; + halign_t halign; + valign_t valign; + ustring id; + std::vector 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 CellList_t; + typedef boost::ptr_vector TableAry_t; + + int n; + bool fnoborder; + int cellspacing; + int cellpadding; + bool fpadding; + halign_t halign; + ustring id; + std::vector 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 args; + bool fdefault; + bool fmultiple; + int elsize; + std::vector classlist; + ustring id; + std::vector 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 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 args; + ustring id; + std::vector 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 block; + boost::ptr_vector* blockp; + std::vector bstack; + std::vector*> 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 = █ + protectMode = mode; + }; + virtual ~WikiFormat () { + delete mlenv; + }; + + virtual void compile (const ustring& text, bool fsuper); + virtual void output (boost::ptr_vector& 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* 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& args); + virtual void wikiLine_6 (Splitter& sp, std::vector& args, ustring& arg2); + virtual void wikiLine_7 (Splitter& sp, ustring& arg2); + virtual void wikiLine_8 (Splitter& sp, std::vector& args); + virtual void wikiLine_9 (Splitter& sp, std::vector& 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& args, bool& fscript, ustring& url, ustring& target); + virtual bool wikiLinkParam (int start, std::vector& 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& args); + virtual bool wikiParamRaw (uiterator& b, uiterator e, bool fstop, std::vector& args, ustring& arg2); + virtual bool paramIDClass (uiterator b, uiterator e, ustring& id, std::vector& 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& classes); + virtual ustring eval (uiterator b, uiterator e); + virtual void eval (std::vector& 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 index 0000000..03f1481 --- /dev/null +++ b/wiki/wikiline.cc @@ -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 +#include + +/*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 (""))->outText (arg2)->out (CharConst ("")); + return true; +} + +//#WIKILINE b wl_bold +bool wl_bold (ustring& arg2, MotorOutput* out, WikiFormat* wiki) { + out->out (CharConst (""))->outText (arg2)->out (CharConst ("")); + return true; +} + +//#WIKILINE bi wl_bolditalic +//#WIKILINE ib wl_bolditalic +bool wl_bolditalic (ustring& arg2, MotorOutput* out, WikiFormat* wiki) { + out->out (CharConst (""))->outText (arg2)->out (CharConst ("")); + 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& 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 ("outHTMLnoCtrl (url); + } else { + out->out (CharConst ("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 ("")); + } + return true; +} + +//#WIKILINE2 http wl_http +bool wl_http (std::vector& 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& 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& 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& 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& 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& 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& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) { + ustring url, width, height, id; + std::vector 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 ("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& 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 ("outHTMLnoCtrl (color)->out (CharConst (";\">"))->outText (arg2)->out (CharConst ("")); + + 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 ("outHTMLnoCtrl (name)->out (CharConst ("\">")); + + return true; +} + +/*DOC: +===SPANタグ=== + &[;[span:red クラス指定]] + &[;[span:id=Field1 ID指定]] + +*/ +//#WIKILINE2 span wl_span +bool wl_span (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) { + ustring v, id; + std::vector 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 ("outIDClass (out, id, classlist); + v = wiki->wikiLine (arg2.begin (), arg2.end ()); + out->out (CharConst (">"))->outText (v)->out (CharConst ("")); + + 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& 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 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 ("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& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) { + int i; + ustring name, val; + bool pchecked = false; + uiterator b, e; + ustring id; + std::vector 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 ("")); + + return true; +} + +//#WIKILINE2 input wl_input +bool wl_input (std::vector& 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& 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& 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& 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 ("outText (arg)->out (CharConst ("\" />")); + return true; +} + +//#WIKILINE2 button wl_button +bool wl_button (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) { + int i; + std::vector::iterator it; + ustring name, val; + uiterator b, e; + ustring id; + std::vector 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 ("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& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) { +// + return wl_input_radiocheck (CharConst ("radio"), args, arg2, out, wiki); +} + +//#WIKILINE2 checkbox wl_checkbox +bool wl_checkbox (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki) { +// + return wl_input_radiocheck (CharConst ("checkbox"), args, arg2, out, wiki); +} + +//#WIKILINE2 textarea wl_textarea +bool wl_textarea (std::vector& 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 ("= 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 ("")); + return true; +} + +/* ============================================================ */ +/*DOC: +===数値フォーマット=== + &[;[pad0:NUMBER:VALUE]] + +*/ +//#WIKILINE pad0 wl_pad0 +bool wl_pad0 (std::vector& 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& 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 index 0000000..ced9987 --- /dev/null +++ b/wiki/wikiline.h @@ -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& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_http_new (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_https (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_https_new (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_link (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_link_new (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_image (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_color (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_anchor (ustring& arg, MotorOutput* out, WikiFormat* wiki); +bool wl_span (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_input (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_password (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_hidden (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_file (std::vector& args, MotorOutput* out, WikiFormat* wiki); +bool wl_submit (ustring& arg, MotorOutput* out, WikiFormat* wiki); +bool wl_button (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_radio (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_checkbox (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_textarea (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); +bool wl_pad0 (std::vector& args, MotorOutput* out, WikiFormat* wiki); + +#endif /* WIKILINE_H */ diff --git a/wiki/wikitable.h b/wiki/wikitable.h new file mode 100644 index 0000000..e1d46ca --- /dev/null +++ b/wiki/wikitable.h @@ -0,0 +1,61 @@ +#ifndef WIKITABLE_H +#define WIKITABLE_H + +#include "wikiformat.h" +#include "ustring.h" +#include +#include + +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& args, MotorOutput* out, WikiFormat* wiki); + bool (*fnM2) (std::vector& args, ustring& arg2, MotorOutput* out, WikiFormat* wiki); + }; + } wikifunc_t; +} + +class WikiCmdTable: public boost::unordered_map { + 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 { +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 */ -- 2.11.0