summaryrefslogtreecommitdiffabout
path: root/lib
Side-by-side diff
Diffstat (limited to 'lib') (more/less context) (ignore whitespace changes)
-rw-r--r--lib/.gitignore9
-rw-r--r--lib/Makefile.am22
-rw-r--r--lib/acomponent.cc47
-rw-r--r--lib/cgi_component.cc30
-rw-r--r--lib/component_factory.cc279
-rw-r--r--lib/component_so.cc112
-rw-r--r--lib/configuration.cc474
-rw-r--r--lib/file_factory.cc55
-rw-r--r--lib/pch.h45
-rw-r--r--lib/process_manager.cc152
-rw-r--r--lib/scoreboard.cc71
-rw-r--r--lib/sitecing_enflesher.ll202
-rw-r--r--lib/sitecing_interface_cgi.cc25
-rw-r--r--lib/sitecing_parser.ll594
-rw-r--r--lib/sitecing_util.cc278
-rw-r--r--lib/sitespace.cc52
-rw-r--r--lib/util.cc83
17 files changed, 2530 insertions, 0 deletions
diff --git a/lib/.gitignore b/lib/.gitignore
new file mode 100644
index 0000000..8137060
--- a/dev/null
+++ b/lib/.gitignore
@@ -0,0 +1,9 @@
+Makefile.in
+sitecing_enflesher.cc
+sitecing_parser.cc
+.libs
+.deps
+Makefile
+*.o
+*.lo
+*.la
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..53d8182
--- a/dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,22 @@
+lib_LTLIBRARIES = libsitecing.la
+noinst_HEADERS = pch.h
+
+INCLUDES = -I${top_srcdir}/include -I${top_builddir} ${KINGATE_CFLAGS} ${DOTCONF_CFLAGS} \
+ ${PCREPP_CFLAGS}
+AM_CPPFLAGS = -D__SC_DEFAULT_SKELETON=\"${pkgdatadir}/component.skel\"
+AM_LFLAGS = -olex.yy.c
+
+libsitecing_la_SOURCES = \
+ sitecing_parser.ll sitecing_enflesher.ll \
+ sitecing_interface_cgi.cc \
+ acomponent.cc \
+ cgi_component.cc \
+ component_so.cc \
+ file_factory.cc component_factory.cc \
+ sitespace.cc \
+ configuration.cc \
+ util.cc sitecing_util.cc \
+ scoreboard.cc \
+ process_manager.cc
+libsitecing_la_LDFLAGS = \
+ -version-info 1:0:0
diff --git a/lib/acomponent.cc b/lib/acomponent.cc
new file mode 100644
index 0000000..8dfeee4
--- a/dev/null
+++ b/lib/acomponent.cc
@@ -0,0 +1,47 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include <cstdarg>
+ #include <fstream>
+ #include <konforka/exception.h>
+ using namespace std;
+ #include "sitecing/acomponent.h"
+#endif
+
+namespace sitecing {
+
+ acomponent::acomponent()
+ : __SCIF(NULL) {
+ }
+ acomponent::~acomponent() {
+ }
+
+ void acomponent::__set_interface(sitecing_interface* scif) {
+ sitecing_interface *o = __SCIF;
+ __SCIF = scif;
+ if(o!=scif) {
+ __on_change_interface(o);
+ __do_imports();
+ __on_imports();
+ }
+ }
+
+ void acomponent::__on_change_interface(sitecing_interface *oscif) { }
+ void acomponent::__do_imports() { }
+ void acomponent::__on_imports() { }
+
+ void acomponent::run(int _magic,...) {
+ va_list va;
+ va_start(va,_magic);
+ main(_magic,va);
+ va_end(va);
+ }
+
+
+ void acomponent::pass_file_through(const char *fn) {
+ ifstream ifs(fn,ios::in|ios::binary);
+ if(!ifs)
+ throw konforka::exception(CODEPOINT,"failed to open file");
+ (*(__SCIF->out)) << ifs.rdbuf();
+ }
+}
diff --git a/lib/cgi_component.cc b/lib/cgi_component.cc
new file mode 100644
index 0000000..b5c4bee
--- a/dev/null
+++ b/lib/cgi_component.cc
@@ -0,0 +1,30 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include "sitecing/cgi_component.h"
+#endif
+
+namespace sitecing {
+
+ cgi_component::cgi_component()
+ : __CGI(NULL) {
+ }
+ cgi_component::~cgi_component() {
+ }
+
+ void cgi_component::__set_interface(sitecing_interface* scif) {
+ acomponent::__set_interface(scif);
+ kingate::cgi_gateway *oc = __CGI;
+ __CGI = __SCIF?__SCIF->cgigw:NULL;
+ if(__CGI!=oc)
+ __on_change_CGI(oc);
+ }
+ void cgi_component::__on_change_interface(sitecing_interface *o) {
+ acomponent::__on_change_interface(o); // But it's a no-op
+ // TODO: do something about runtime type check, maybe?
+ __SCIF = (sitecing_interface_cgi*)acomponent::__SCIF;
+ }
+ void cgi_component::__on_change_CGI(kingate::cgi_gateway *o) { }
+ void cgi_component::__on_imports() { }
+
+}
diff --git a/lib/component_factory.cc b/lib/component_factory.cc
new file mode 100644
index 0000000..bcf19f2
--- a/dev/null
+++ b/lib/component_factory.cc
@@ -0,0 +1,279 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <unistd.h>
+ #include <sys/wait.h>
+ #include <fcntl.h>
+ #include <iostream>
+ #include <fstream>
+ #include <stdexcept>
+ #include <vector>
+ using namespace std;
+ #include "sitecing/component_factory.h"
+ #include "sitecing/sitecing_util.h"
+ #include "sitecing/sitecing_parser.h"
+ #include "sitecing/sitecing_exception.h"
+#endif
+
+namespace sitecing {
+
+ static const char *pp_targets[] = { ".cc", ".h", ".imports", ".classname", ".baseclassname", ".ancestors" };
+
+ component_factory::component_factory(configuration& c)
+ : config(c),
+ root_source(normalize_path(c.root_source,strip_trailing_slash)+'/'),
+ root_intermediate(normalize_path(c.root_intermediate,strip_trailing_slash)+'/'),
+ root_so(normalize_path(c.root_so,strip_trailing_slash)+'/') {
+ }
+
+ void component_factory::get_dependencies(const string& dst,file_list_t& deps) {
+ deps.clear();
+ string dp = normalize_path(dst,strip_trailing_slash);
+ // source documents
+ try { // XXX: or just compare it off?
+ string noro = strip_prefix(dp,root_source);
+ return;
+ }catch(utility_no_affix& una) {
+ }
+ // .so binaries
+ try {
+ string noso = strip_suffix(dp,".so");
+ string noro = strip_prefix(noso,root_so);
+ deps.push_back(root_intermediate+noro+".cc");
+ config_options *co_cpp_deps = config.lookup_config(noro,config_options::flag_cpp_deps);
+ if( (!co_cpp_deps) || co_cpp_deps->cpp_deps) {
+ ifstream df((root_intermediate+noro+".d").c_str(),ios::in);
+ if(df.good()) {
+ string str;
+ while(!df.eof()) {
+ df >> str;
+ if(str.find_first_of("\\:")==string::npos)
+ deps.push_back(combine_path(config.root_source+noro,str));
+ }
+ }
+ }
+ config_options *co_so_deps = config.lookup_config(noro,config_options::flag_so_deps);
+ if(co_so_deps) {
+ for(list<string>::const_iterator i=co_so_deps->so_deps.begin();i!=co_so_deps->so_deps.end();++i)
+ deps.push_back(*i);
+ }
+ return;
+ }catch(utility_no_prefix& unp) {
+ throw konforka::exception(CODEPOINT,"component is outside of component root");
+ }catch(utility_no_suffix& uns) {
+ }
+ // preprocessor targets
+ for(int ppt=0;ppt<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) {
+ try {
+ string nos = strip_suffix(dp,pp_targets[ppt]);
+ string noro = strip_prefix(nos,root_intermediate);
+ deps.push_back(root_source+noro);
+ ifstream imports((root_intermediate+noro+".imports").c_str(),ios::in);
+ if(imports.good()) {
+ string str;
+ while(!imports.eof()) {
+ imports >> str;
+ if(!str.empty())
+ deps.push_back(root_intermediate+str+".classname");
+ }
+ }
+ ifstream ancestors((root_intermediate+noro+".ancestors").c_str(),ios::in);
+ if(ancestors.good()) {
+ string str;
+ while(!ancestors.eof()) {
+ ancestors >> str;
+ if(!str.empty())
+ deps.push_back(root_intermediate+str+".classname");
+ }
+ }
+ config_options *co_intermediate_deps = config.lookup_config(noro,config_options::flag_intermediate_deps);
+ if(co_intermediate_deps) {
+ for(list<string>::const_iterator i=co_intermediate_deps->intermediate_deps.begin();i!=co_intermediate_deps->intermediate_deps.end();++i)
+ deps.push_back(*i);
+ }
+ return;
+ }catch(utility_no_affix& una) {
+ // do nothing. must be a cpp dependency.
+ }
+ }
+ }
+
+ bool component_factory::is_uptodate(const string& dst,file_list_t *deps) {
+ string dp = normalize_path(dst,strip_trailing_slash);
+ // XXX: or just compare it off, instead of throwing things around.
+ try {
+ strip_prefix(dp,root_intermediate);
+ return file_factory::is_uptodate(dst,deps);
+ }catch(utility_no_prefix& unp) {
+ }
+ try {
+ strip_prefix(dp,root_so);
+ return file_factory::is_uptodate(dst,deps);
+ }catch(utility_no_prefix& unp) {
+ }
+ return true;
+ }
+
+ void component_factory::build(const string& dst) {
+ string dp = normalize_path(dst,strip_trailing_slash);
+ // sources
+ try {
+ string noro = strip_prefix(dp,root_source);
+ // building the sources is left up to developer
+ return;
+ }catch(utility_no_prefix& unp) {
+ }
+ // .so files
+ try {
+ string noso = strip_suffix(dp,".so");
+ string noro = strip_prefix(noso,root_so);
+ string cc = root_intermediate+noro+".cc";
+ if(access(cc.c_str(),R_OK))
+ throw konforka::exception(CODEPOINT,string("can't access preprocessed component code (")+cc+")");
+ make_path(dir_name(root_so+noro),0755);
+ string pwd = dir_name(root_source+noro);
+ auto_chdir dir_changer(pwd);
+ file_lock lock_source(root_intermediate+noro+".lock");
+ file_lock lock_so(root_so+noro+".so.lock");
+ int stdO = open((root_intermediate+noro+".stdout").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664);
+ if(stdO<0)
+ throw konforka::exception(CODEPOINT,"failed to open/create compiler stdout");
+ int stdE = open((root_intermediate+noro+".stderr").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664);
+ if(stdE<0) {
+ close(stdO);
+ throw konforka::exception(CODEPOINT,"failed to open/create compiler's stderr");
+ }
+ list<string> args;
+ config_options *co_cpp_flags = config.lookup_config(noro,config_options::flag_cpp_flags);
+ if(co_cpp_flags) {
+ args.insert(args.end(),co_cpp_flags->cpp_flags.begin(),co_cpp_flags->cpp_flags.end());
+ }
+ config_options *co_ld_flags = config.lookup_config(noro,config_options::flag_ld_flags);
+ if(co_ld_flags) {
+ args.insert(args.end(),co_ld_flags->ld_flags.begin(),co_ld_flags->ld_flags.end());
+ }
+ // TODO: maybe move it to separare config option like CoreCPPFLags?
+ args.push_back("-I"+root_intermediate);
+ args.push_back("-I"+root_source);
+ args.push_back("-MD"); args.push_back("-MF"); args.push_back(root_intermediate+noro+".d");
+ args.push_back("-shared");
+ args.push_back("-o"); args.push_back(dp);
+ args.push_back(cc);
+ file_list_t ancestors;
+ get_ancestors(noro,ancestors);
+ for(file_list_t::const_iterator i=ancestors.begin();i!=ancestors.end();++i) {
+ string aso=root_so+*i+".so";
+ make(aso);
+ args.push_back(aso);
+ }
+ // TODO: "g++" configurable
+ int rv = execute("g++",args,stdO,stdE);
+ if(! (WIFEXITED(rv) && !WEXITSTATUS(rv)) )
+ throw compile_error(CODEPOINT,"failed to compile component",noro);
+ return;
+ }catch(utility_no_prefix& unp) {
+ throw konforka::exception(CODEPOINT,"component is outside of component root");
+ }catch(utility_no_suffix& uns) {
+ }
+ // preprocessor targets
+ for(int ppt=0;ppt<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) {
+ try {
+ string nos = strip_suffix(dp,pp_targets[ppt]);
+ string noro = strip_prefix(nos,root_intermediate);
+ string src = root_source+noro;
+ if(access(src.c_str(),R_OK))
+ throw konforka::exception(CODEPOINT,string("can't access component source (")+src+")");
+ make_path(dir_name(root_intermediate+noro),0755);
+ file_lock lock(root_intermediate+noro+".lock");
+ sitecing_parser parser(*this);
+ config_options *co_skeleton = config.lookup_config(noro,config_options::flag_skeleton);
+ if(co_skeleton)
+ parser.skeleton = co_skeleton->skeleton;
+ static const char *id_chars = "abcdefghijklmnopqrstuvwxyz0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ parser.class_name = normalize_path(noro,strip_leading_slash|strip_trailing_slash);
+ for(string::size_type illc = parser.class_name.find_first_not_of(id_chars);illc!=string::npos;illc=parser.class_name.find_first_not_of(id_chars,illc)) {
+ string::size_type lc = parser.class_name.find_first_of(id_chars,illc);
+ int n = ((lc==string::npos)?parser.class_name.length():lc)-illc;
+ parser.class_name.replace(illc,n,n,'_');
+ }
+ parser.class_name = "_SCC_"+parser.class_name;
+ parser.output_basename = nos;
+ parser.component_basename = noro;
+ try {
+ parser.preprocess(src);
+ }catch(preprocessor_error& pe) {
+ pe.component_name = noro;
+ pe.see(CODEPOINT);
+ throw;
+ }
+ return;
+ }catch(utility_no_affix& una) {
+ // must be a crap from .d file
+ }
+ }
+ cerr << "ignoring build request for " << dp << endl;
+ }
+
+ int component_factory::execute(const string& cmd, const list<string>& args,int stdo,int stde) {
+ // XXX: is it right that we do stdio/stderr tricks outside of the function?
+ cerr << "executing: " << cmd;
+ vector<const char*> argv(args.size()+2);
+ argv[0]=cmd.c_str();
+ int an = 1;
+ for(list<string>::const_iterator i=args.begin();i!=args.end();i++) {
+ cerr << " " << *i ;
+ argv[an++] = i->c_str();
+ }
+ cerr << endl;
+ argv[an++]=NULL;
+ pid_t pid = vfork();
+ if(pid==-1) {
+ close(stdo); close(stde);
+ throw konforka::exception(CODEPOINT,"failed to vfork()");
+ }
+ if(!pid) {
+ // child
+ if(dup2(stdo,1)!=1)
+ _exit(-1);
+ if(dup2(stde,2)!=2)
+ _exit(-1);
+ close(0);
+ execvp(cmd.c_str(),(char**)&argv.front());
+ _exit(-1);
+ }
+ // parent
+ close(stdo); close(stde);
+ int rv;
+ if(waitpid(pid,&rv,0)<0)
+ throw konforka::exception(CODEPOINT,"failed to waitpid()");
+ return rv;
+ }
+
+ string component_factory::get_classname(const string& component) {
+ string cn = root_intermediate+normalize_path(component,strip_trailing_slash|strip_leading_slash)+".classname";
+ make(cn);
+ ifstream ifs(cn.c_str());
+ if(!ifs.good())
+ throw konforka::exception(CODEPOINT,"failed to access component .classname");
+ ifs >> cn;
+ return cn;
+ }
+
+ void component_factory::get_ancestors(const string& component,file_list_t& rv) {
+ string cn = root_intermediate+normalize_path(component,strip_trailing_slash|strip_leading_slash)+".ancestors";
+ make(cn);
+ ifstream ifs(cn.c_str());
+ if(!ifs.good())
+ throw konforka::exception(CODEPOINT,"filed to access component .ancestors");
+ rv.clear();
+ while(!ifs.eof()) {
+ string a;
+ ifs >> a;
+ if(!a.empty())
+ rv.push_back(a);
+ }
+ }
+
+}
diff --git a/lib/component_so.cc b/lib/component_so.cc
new file mode 100644
index 0000000..57cce01
--- a/dev/null
+++ b/lib/component_so.cc
@@ -0,0 +1,112 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include <unistd.h>
+ #include <dlfcn.h>
+ #include <iostream>
+ #include <cassert>
+ #include <stdexcept>
+ using namespace std;
+ #include "sitecing/component_so.h"
+ #include "sitecing/sitecing_util.h"
+#endif
+
+namespace sitecing {
+
+ /*
+ * component_so
+ */
+
+ component_so::component_so(const string& soname)
+ : dl(NULL), sofile(soname) {
+ if(stat(sofile.c_str(),&stso))
+ throw konforka::exception(CODEPOINT,"failed to stat() shared object");
+ file_lock lock(sofile+".lock");
+ dl = dlopen(sofile.c_str(),RTLD_LAZY);
+ lock.unlock();
+ if(!dl)
+ throw konforka::exception(CODEPOINT,"failed to dlopen: "+string(dlerror()));
+ egg = (egg_t)dlsym(dl,"_egg");
+ if(!egg)
+ throw konforka::exception(CODEPOINT,"failed to dlsym: "+string(dlerror()));
+ }
+ component_so::~component_so() {
+ for(free_chickens_t::iterator i=chickens_free.begin();i!=chickens_free.end();i++)
+ delete *i;
+ chickens_free.clear();
+ if(!chickens_used.empty())
+ throw konforka::exception(CODEPOINT,"attempt to destroy the component in use");
+ dlclose(dl);
+ }
+
+ bool component_so::is_uptodate() const {
+ struct stat st;
+ if(stat(sofile.c_str(),&st))
+ throw konforka::exception(CODEPOINT,"failed to stat() shared object");
+ return stso.st_mtime==st.st_mtime;
+ }
+
+ acomponent* component_so::allocate_chicken() {
+ acomponent *rv;
+ if(!chickens_free.empty()) {
+ rv = chickens_free.front();
+ chickens_free.pop_front();
+ }else{
+ rv = (*egg)();
+ }
+ assert(chickens_used.find(rv)==chickens_used.end());
+ chickens_used[rv]=1;
+ return rv;
+ }
+
+ void component_so::allocate_chicken(acomponent* ac) {
+ used_chickens_t::iterator i = chickens_used.find(ac);
+ if(i!=chickens_used.end()) {
+ i->second++;
+ }else{
+ free_chickens_t::iterator i;
+ for(i=chickens_free.begin();*i!=ac && i!=chickens_free.end();i++);
+ if(i==chickens_free.end())
+ throw konforka::exception(CODEPOINT,"hens rarely adopt chickens");
+ chickens_free.erase(i);
+ chickens_used[ac]=1;
+ }
+ }
+
+ void component_so::deallocate_chicken(acomponent* ac) {
+ used_chickens_t::iterator i = chickens_used.find(ac);
+ if(i==chickens_used.end())
+ throw konforka::exception(CODEPOINT,"you can't deallocate what is not allocated");
+ i->second--;
+ if(i->second>0)
+ return;
+ chickens_used.erase(i);
+ chickens_free.push_front(ac);
+ }
+
+ /*
+ * so_component
+ */
+
+ so_component::so_component(component_so *h,sitecing_interface *scif)
+ : hen(h), ac(NULL) {
+ if(!hen)
+ throw konforka::exception(CODEPOINT,"can't get an egg from the null-hen");
+ ac = hen->allocate_chicken();
+ ac->__set_interface(scif);
+ }
+
+ void so_component::attach(component_so *h,acomponent *a) {
+ detach(); hen = h; ac = a;
+ if(!ac)
+ throw konforka::exception(CODEPOINT,"trying to clone null-chicken");
+ if(!hen)
+ throw konforka::exception(CODEPOINT,"trying to clone orphan chicken");
+ hen->allocate_chicken(ac);
+ }
+ void so_component::detach() {
+ if(hen && ac)
+ hen->deallocate_chicken(ac);
+ }
+
+}
diff --git a/lib/configuration.cc b/lib/configuration.cc
new file mode 100644
index 0000000..4ee1526
--- a/dev/null
+++ b/lib/configuration.cc
@@ -0,0 +1,474 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include <unistd.h>
+ #include <fnmatch.h>
+ #include <cassert>
+ #include <stdexcept>
+ using namespace std;
+ #include <dotconf.h>
+ #include "sitecing/configuration.h"
+ #include "sitecing/sitecing_util.h"
+ #include "sitecing/scoreboard.h"
+#endif
+
+namespace sitecing {
+
+ configuration::configuration()
+ : flags(0), autobuild(false) { }
+ configuration::configuration(const string& cfile,bool ab)
+ : flags(0), autobuild(ab) {
+ parse(cfile);
+ }
+
+ enum dc_ctx {
+ DCC_ROOT = 1,
+ DCC_PATH = 2,
+ DCC_SCRC = 4
+ };
+ struct dc_context {
+ dc_ctx ctx;
+ configuration* cf;
+ list<config_options*> co;
+ };
+
+ static DOTCONF_CB(dco_root_source) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->root_source = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_root_source;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_root_intermediate) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->root_intermediate = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_root_intermediate;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_root_so) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->root_so = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_root_so;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_listen_socket) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->listen_socket = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_listen_socket;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_rc_file_name) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->rc_file_name = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_rc_file_name;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_min_children) { dc_context *dcc = (dc_context*)ctx;
+ if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
+ return "MinChildren is too big";
+ dcc->cf->min_children = cmd->data.value;
+ dcc->cf->flags |= configuration::flag_min_children;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_max_children) { dc_context *dcc = (dc_context*)ctx;
+ if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
+ return "MaxChildren is too big";
+ dcc->cf->max_children = cmd->data.value;
+ dcc->cf->flags |= configuration::flag_max_children;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_min_spare_children) { dc_context *dcc = (dc_context*)ctx;
+ if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
+ return "MinSpareChildren is too big";
+ dcc->cf->min_spare_children = cmd->data.value;
+ dcc->cf->flags |= configuration::flag_min_spare_children;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_max_spare_children) { dc_context *dcc = (dc_context*)ctx;
+ if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
+ return "MaxSpareChildren is too big";
+ dcc->cf->max_spare_children = cmd->data.value;
+ dcc->cf->flags |= configuration::flag_max_spare_children;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_requests_per_child) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->requests_per_child = cmd->data.value;
+ dcc->cf->flags |= configuration::flag_requests_per_child;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_multi_process) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->multi_process = cmd->data.value;
+ dcc->cf->flags |= configuration::flag_multi_process;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_user) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->user = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_user;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_group) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->group = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_group;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_chroot) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->chroot = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_chroot;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_pid_file) { dc_context *dcc = (dc_context*)ctx;
+ dcc->cf->pid_file = cmd->data.str;
+ dcc->cf->flags |= configuration::flag_pid_file;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_daemonize) { dc_context *dcc = (dc_context*) ctx;
+ dcc->cf->daemonize = cmd->data.value;
+ dcc->cf->flags |= configuration::flag_daemonize;
+ return NULL;
+ }
+
+ static DOTCONF_CB(dco_path) { dc_context *dcc = (dc_context*)ctx;
+ string path = cmd->data.str;
+ if(path[path.length()-1]=='>')
+ path.erase(path.length()-1);
+ // TODO: normalize path
+ dcc->co.push_front(&(dcc->cf->specs[path]));
+ dcc->ctx = DCC_PATH; // TODO: stack it, instead
+ return NULL;
+ }
+ static DOTCONF_CB(dco__path) { dc_context *dcc = (dc_context*)ctx;
+ dcc->co.pop_front();
+ assert(dcc->co.size());
+ dcc->ctx = DCC_ROOT; // TODO: stack it, instead
+ return NULL;
+ }
+
+ static DOTCONF_CB(dco_skeleton) { dc_context *dcc = (dc_context*)ctx;
+ dcc->co.front()->skeleton = cmd->data.str;
+ dcc->co.front()->flags |= config_options::flag_skeleton;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_cpp_flags) { dc_context *dcc = (dc_context*)ctx;
+ for(char **arg=cmd->data.list;*arg;arg++)
+ dcc->co.front()->cpp_flags.push_back(*arg);
+ dcc->co.front()->flags |= config_options::flag_cpp_flags;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_ld_flags) { dc_context *dcc = (dc_context*)ctx;
+ for(char **arg=cmd->data.list;*arg;arg++)
+ dcc->co.front()->ld_flags.push_back(*arg);
+ dcc->co.front()->flags |= config_options::flag_ld_flags;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_intermediate_deps) { dc_context *dcc = (dc_context*) ctx;
+ for(char **arg=cmd->data.list;*arg;arg++)
+ dcc->co.front()->intermediate_deps.push_back(*arg);
+ dcc->co.front()->flags |= config_options::flag_intermediate_deps;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_so_deps) { dc_context *dcc = (dc_context*) ctx;
+ for(char **arg=cmd->data.list;*arg;arg++)
+ dcc->co.front()->so_deps.push_back(*arg);
+ dcc->co.front()->flags |= config_options::flag_so_deps;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_build) { dc_context *dcc = (dc_context*)ctx;
+ dcc->co.front()->build = cmd->data.value;
+ dcc->co.front()->flags |= config_options::flag_build;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_cpp_deps) { dc_context *dcc = (dc_context*)ctx;
+ dcc->co.front()->cpp_deps = cmd->data.value;
+ dcc->co.front()->flags |= config_options::flag_cpp_deps;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_exception_handler) { dc_context *dcc = (dc_context*)ctx;
+ dcc->co.front()->exception_handler = cmd->data.str;
+ dcc->co.front()->flags |= config_options::flag_exception_handler;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_http_status_handler) { dc_context *dcc = (dc_context*)ctx;
+ if(cmd->arg_count!=2)
+ return "Invalid number of arguments";
+ dcc->co.front()->http_status_handlers[cmd->data.list[0]] = cmd->data.list[1];
+ dcc->co.front()->flags |= config_options::flag_http_status_handlers;
+ return NULL;
+ }
+ static DOTCONF_CB(dco_action) { dc_context *dcc = (dc_context*)ctx;
+ if(cmd->arg_count<2)
+ return "Invalid number of arguments";
+ try {
+ char **arg=cmd->data.list;
+ dcc->co.front()->action_handlers.push_back(config_options::action_handler_t(arg[0],arg[1]));
+ for(arg+=2;*arg;arg++)
+ dcc->co.front()->action_handlers.back().args.push_back(*arg);
+ dcc->co.front()->flags |= config_options::flag_action_handlers;
+ }catch(exception& e) {
+ return "Error processing Action directive"; // XXX: could be done better
+ }
+ return NULL;
+ }
+ static DOTCONF_CB(dco_auto_build_files) { dc_context *dcc = (dc_context*)ctx;
+ if(!( dcc->cf && dcc->cf->autobuild))
+ return NULL;
+ for(char **arg=cmd->data.list;*arg;arg++)
+ dcc->co.front()->auto_build_files.push_back(*arg);
+ dcc->co.front()->flags |= config_options::flag_auto_build_files;
+ return NULL;
+ }
+
+ static const configoption_t dc_options[] = {
+ { "RootSource", ARG_STR, dco_root_source, NULL, DCC_ROOT },
+ { "RootIntermediate", ARG_STR, dco_root_intermediate, NULL, DCC_ROOT },
+ { "RootSO", ARG_STR, dco_root_so, NULL, DCC_ROOT },
+ { "ListenSocket", ARG_STR, dco_listen_socket, NULL, DCC_ROOT },
+ { "RCFileName", ARG_STR, dco_rc_file_name, NULL, DCC_ROOT },
+ { "MinChildren", ARG_INT, dco_min_children, NULL, DCC_ROOT },
+ { "MaxChildren", ARG_INT, dco_max_children, NULL, DCC_ROOT },
+ { "MinSpareChildren", ARG_INT, dco_min_spare_children, NULL, DCC_ROOT },
+ { "MaxSpareChildren", ARG_INT, dco_max_spare_children, NULL, DCC_ROOT },
+ { "RequestsPerChild", ARG_INT, dco_requests_per_child, NULL, DCC_ROOT },
+ { "MultiProcess", ARG_TOGGLE, dco_multi_process, NULL, DCC_ROOT },
+ { "User", ARG_STR, dco_user, NULL, DCC_ROOT },
+ { "Group", ARG_STR, dco_group, NULL, DCC_ROOT },
+ { "Chroot", ARG_STR, dco_chroot, NULL, DCC_ROOT },
+ { "PidFile", ARG_STR, dco_pid_file, NULL, DCC_ROOT },
+ { "Daemonize", ARG_TOGGLE, dco_daemonize, NULL, DCC_ROOT },
+ { "<Path", ARG_STR, dco_path, NULL, DCC_ROOT },
+ { "Skeleton", ARG_STR, dco_skeleton, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "CPPFLAGS", ARG_LIST, dco_cpp_flags, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "LDFLAGS", ARG_LIST, dco_ld_flags, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "Build", ARG_TOGGLE, dco_build, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "CPPDeps", ARG_TOGGLE, dco_cpp_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "ExceptionHandler", ARG_STR, dco_exception_handler, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "HTTPStatusHandler", ARG_LIST, dco_http_status_handler, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "IntermediateDeps", ARG_LIST, dco_intermediate_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "SODeps", ARG_LIST, dco_so_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "Action", ARG_LIST, dco_action, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "AutoBuildFiles", ARG_LIST, dco_auto_build_files, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
+ { "</Path>", ARG_NONE, dco__path, NULL, DCC_PATH },
+ LAST_OPTION
+ };
+
+ static const char *dc_context_checker(command_t *cmd,unsigned long mask) {
+ dc_context *dcc = (dc_context*)cmd->context;
+ if( (mask==CTX_ALL) || ((mask&dcc->ctx)==dcc->ctx) )
+ return NULL;
+ return "misplaced option";
+ }
+ static FUNC_ERRORHANDLER(dc_error_handler) {
+ throw konforka::exception(CODEPOINT,string("error parsing config file: ")+msg);
+ }
+
+ bool loaded_options::is_valid() {
+ struct stat nst;
+ if(stat(source_file.c_str(),&nst))
+ return false;
+ if(st.st_mtime!=nst.st_mtime)
+ return false;
+ return true;
+ }
+
+ loaded_options *configuration::lookup_loaded_options(const string& target) {
+ // we assume 'target' is a directory with trailing slash appended
+ string scrc = root_source+target;
+ if(flags&flag_rc_file_name)
+ scrc += rc_file_name;
+ else
+ scrc += ".scrc";
+ // TODO: normalize me, anyway.
+ if(access(scrc.c_str(),R_OK))
+ return 0; // TODO FIXME: this approach leaves already loaded .scrcs around in case of removal
+ loaded_specs_t::iterator i = loaded_specs.find(target);
+ if(i==loaded_specs.end() || !i->second.is_valid()) {
+ if(i!=loaded_specs.end())
+ loaded_specs.erase(i);
+ pair<loaded_specs_t::iterator,bool> ii = loaded_specs.insert(loaded_specs_t::value_type(target,loaded_options()));
+ assert(ii.first!=loaded_specs.end());
+ ii.first->second.parse(this,scrc);
+ i = ii.first;
+ }
+ assert(i!=loaded_specs.end());
+ return &(i->second);
+ }
+
+ config_options::action_handler_t *config_options::lookup_action_handler(const string& target) {
+ for(action_handlers_t::iterator i=action_handlers.begin();i!=action_handlers.end();++i) {
+ if(i->regex.search(target))
+ return &*i;
+ }
+ return NULL;
+ }
+
+ string config_options::lookup_http_status_handler(const string& status) {
+ http_status_handlers_t::const_iterator i = http_status_handlers.find(status);
+ string rv;
+ if(i!=http_status_handlers.end())
+ rv = i->second;
+ return rv;
+ }
+
+ string configuration::lookup_http_status_handler(const string& target,const string& status) {
+ string t = "/";
+ t += normalize_path(target,strip_leading_slash);
+ string rv;
+ for(;;) {
+ if(t[t.length()-1]=='/') {
+ loaded_options* lo = lookup_loaded_options(t);
+ if( lo && (lo->flags&config_options::flag_http_status_handlers) ) {
+ rv = lo->lookup_http_status_handler(status);
+ if(!rv.empty())
+ return rv;
+ }
+ }
+ specs_t::iterator i = specs.find(t);
+ if( i!=specs.end() && (i->second.flags&&config_options::flag_http_status_handlers) ) {
+ rv = i->second.lookup_http_status_handler(status);
+ if(!rv.empty())
+ return rv;
+ }
+ if(t.empty())
+ return rv;
+ string::size_type sl=t.rfind('/');
+ if(sl==string::npos) {
+ t.erase();
+ }else{
+ if(sl==(t.length()-1))
+ t.erase(sl);
+ else
+ t.erase(sl+1);
+ }
+ }
+ }
+
+ config_options::action_handler_t *configuration::lookup_action_handler(const string& target) {
+ string t = "/";
+ t += normalize_path(target,strip_leading_slash);
+ for(;;) {
+ if(t[t.length()-1]=='/') {
+ loaded_options* lo = lookup_loaded_options(t);
+ if( lo && (lo->flags&config_options::flag_action_handlers) ) {
+ config_options::action_handler_t *rv = lo->lookup_action_handler(target);
+ if(rv)
+ return rv;
+ }
+ }
+ specs_t::iterator i = specs.find(t);
+ if( i!=specs.end() && (i->second.flags&&config_options::flag_action_handlers) ) {
+ config_options::action_handler_t *rv = i->second.lookup_action_handler(target);
+ if(rv)
+ return rv;
+ }
+ if(t.empty())
+ return NULL;
+ string::size_type sl=t.rfind('/');
+ if(sl==string::npos) {
+ t.erase();
+ }else{
+ if(sl==(t.length()-1))
+ t.erase(sl);
+ else
+ t.erase(sl+1);
+ }
+ }
+ }
+
+ config_options* configuration::lookup_config(const string& target,int flag) {
+ string t = "/"; // always assume leading slash
+ t += normalize_path(target,strip_leading_slash);
+ // XXX: reconsider precedence
+ for(;;) {
+ if(t[t.length()-1]=='/') {
+ loaded_options* lo = lookup_loaded_options(t);
+ if( lo && (lo->flags&flag)==flag )
+ return lo;
+ }
+ specs_t::iterator i = specs.find(t);
+ if( i!=specs.end() && (i->second.flags&flag)==flag )
+ return &(i->second);
+ if(t.empty())
+ return NULL;
+ string::size_type sl=t.rfind('/');
+ if(sl==string::npos) {
+ t.erase();
+ }else{
+ if(sl==(t.length()-1))
+ t.erase(sl);
+ else
+ t.erase(sl+1);
+ }
+ }
+ }
+
+ bool config_options::match_autobuild_files(const char *fn,bool &rv) {
+ for(list<string>::reverse_iterator i=auto_build_files.rbegin();i!=auto_build_files.rend();++i) {
+ const char *pat = i->c_str();
+ bool plus = true;
+ if((*pat)=='+')
+ pat++;
+ else if((*pat)=='-') {
+ plus = false;
+ pat++;
+ }
+ if(!fnmatch(pat,fn,FNM_NOESCAPE|FNM_PATHNAME|FNM_PERIOD)) {
+ rv = plus;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool configuration::match_autobuild_files(const string& target,const char *fn) {
+ string t = "/";
+ t += normalize_path(target,strip_leading_slash|strip_trailing_slash);
+ t += "/";
+ bool rv = false;
+ for(;;) {
+ if(t[t.length()-1]=='/') {
+ loaded_options* lo = lookup_loaded_options(t);
+ if(lo && (lo->flags&config_options::flag_auto_build_files) && lo->match_autobuild_files(fn,rv) )
+ return rv;
+ }
+ specs_t::iterator i = specs.find(t);
+ if( i!=specs.end() && (i->second.flags&config_options::flag_auto_build_files) && i->second.match_autobuild_files(fn,rv) )
+ return rv;
+ if(t.empty())
+ return rv;
+ string::size_type sl=t.rfind('/');
+ if(sl==string::npos) {
+ t.erase();
+ }else{
+ if(sl==(t.length()-1))
+ t.erase(sl);
+ else
+ t.erase(sl+1);
+ }
+ }
+ }
+
+ void configuration::parse(const string& cfile) {
+ struct dc_context dcc;
+ dcc.cf = this;
+ dcc.ctx = DCC_ROOT;
+ dcc.co.push_front(&root_options());
+ configfile_t *cf = dotconf_create((char*)cfile.c_str(),dc_options,(context_t*)&dcc,CASE_INSENSITIVE);
+ if(!cf)
+ throw konforka::exception(CODEPOINT,"failed to dotconf_create()");
+ cf->errorhandler = (dotconf_errorhandler_t) dc_error_handler;
+ cf->contextchecker = (dotconf_contextchecker_t) dc_context_checker;
+ if(!dotconf_command_loop(cf))
+ throw konforka::exception(CODEPOINT,"failed to dotconf_command_loop()");
+ dotconf_cleanup(cf);
+ }
+
+ void loaded_options::parse(configuration *config,const string& cfile) {
+ struct dc_context dcc;
+ dcc.cf = config;
+ dcc.ctx = DCC_SCRC;
+ dcc.co.push_front(this);
+ configfile_t *cf = dotconf_create((char*)cfile.c_str(),dc_options,(context_t*)&dcc,CASE_INSENSITIVE);
+ if(!cf)
+ throw konforka::exception(CODEPOINT,"failed to dotconf_create()");
+ cf->errorhandler = (dotconf_errorhandler_t) dc_error_handler;
+ cf->contextchecker = (dotconf_contextchecker_t) dc_context_checker;
+ if(!dotconf_command_loop(cf))
+ throw konforka::exception(CODEPOINT,"failed to dotconf_command_loop()");
+ dotconf_cleanup(cf);
+ source_file = cfile;
+ stat(cfile.c_str(),&st); // TODO: handle errors?
+ }
+}
diff --git a/lib/file_factory.cc b/lib/file_factory.cc
new file mode 100644
index 0000000..c6b5748
--- a/dev/null
+++ b/lib/file_factory.cc
@@ -0,0 +1,55 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <unistd.h>
+ #include <konforka/exception.h>
+ using namespace std;
+ #include "sitecing/file_factory.h"
+#endif
+
+namespace sitecing {
+
+ bool file_factory::is_uptodate(const string& dst,file_list_t* deps) {
+ file_list_t deplist;
+ file_list_t *fl = deps?deps:&deplist;
+ get_dependencies(dst,*fl);
+ struct stat stdst;
+ if(stat(dst.c_str(),&stdst))
+ return false;
+ for(file_list_t::const_iterator i=fl->begin();i!=fl->end();i++) {
+ struct stat stdep;
+ if(stat(i->c_str(),&stdep))
+ return false;
+ if(stdst.st_mtime<stdep.st_mtime)
+ return false;
+ if(!is_uptodate(*i))
+ return false;
+ }
+ return true;
+ }
+
+ void file_factory::make(const string& dst) {
+ try {
+ depth++;
+ if(depth>25)
+ throw konforka::exception(CODEPOINT,"recursed too deeply.");
+ file_list_t deps;
+ if(!is_uptodate(dst,&deps)) {
+ for(file_list_t::const_iterator i=deps.begin();i!=deps.end();i++)
+ make(*i);
+ build(dst);
+ }
+ depth--;
+ }catch(konforka::exception& ke) {
+ depth--;
+ ke.see(CODEPOINT);
+ throw;
+ }catch(...) {
+ depth--;
+ throw;
+ }
+ }
+
+}
diff --git a/lib/pch.h b/lib/pch.h
new file mode 100644
index 0000000..67f9d6d
--- a/dev/null
+++ b/lib/pch.h
@@ -0,0 +1,45 @@
+#ifndef __PCH_H
+#define __PCH_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/sem.h>
+#include <errno.h>
+#include <dlfcn.h>
+#include <fnmatch.h>
+
+#include <dotconf.h>
+
+#include <cstdarg>
+#include <cassert>
+#include <iostream>
+#include <fstream>
+
+#include <vector>
+using namespace std;
+
+#include <konforka/exception.h>
+
+#include "sitecing/acomponent.h"
+#include "sitecing/cgi_component.h"
+#include "sitecing/component_factory.h"
+#include "sitecing/sitecing_util.h"
+#include "sitecing/sitecing_exception.h"
+#include "sitecing/component_so.h"
+#include "sitecing/configuration.h"
+#include "sitecing/file_factory.h"
+#include "sitecing/sitecing_interface_cgi.h"
+#include "sitecing/sitespace.h"
+#include "sitecing/util.h"
+#include "sitecing/scoreboard.h"
+#include "sitecing/process_manager.h"
+
+#include "sitecing/sitecing_parser.h"
+#include "sitecing/sitecing_enflesher.h"
+
+#endif /* __PCH_H */
diff --git a/lib/process_manager.cc b/lib/process_manager.cc
new file mode 100644
index 0000000..48bcb03
--- a/dev/null
+++ b/lib/process_manager.cc
@@ -0,0 +1,152 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <sys/wait.h>
+ #include <signal.h>
+ #include <errno.h>
+ #include <cassert>
+ #include <string>
+ #include <konforka/exception.h>
+ using namespace std;
+ #include "sitecing/process_manager.h"
+#endif
+
+namespace sitecing {
+
+ process_manager::process_manager()
+ : min_children(1), max_children(MAX_SITECING_SCOREBOARD_SLOTS),
+ min_spare_children(0), max_spare_children(-1), finishing(false),
+ die_humbly(false) {
+ }
+ process_manager::~process_manager() {
+ if(die_humbly)
+ return;
+ for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
+ scoreboard_slot *sslot = sboard.get_slot(tmp);
+ if((sslot->state!=scoreboard_slot::state_free) && (sslot->pid>0))
+ kill(sslot->pid,SIGTERM);
+ }
+ collect_dead_souls(true);
+ }
+
+ void process_manager::manage() {
+ while(!finishing) {
+ manage_children();
+ // XXX: is it the way it should be?
+ sleep(10);
+ wait_for_children();
+ }
+ collect_dead_souls(true);
+ }
+
+ void process_manager::collect_dead_souls(bool actively) {
+ for(int tries=5;(tries>0) && (sboard.count_slots(scoreboard_slot::state_free)!=MAX_SITECING_SCOREBOARD_SLOTS);tries--) {
+ if(actively) {
+ for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
+ scoreboard_slot *sslot = sboard.get_slot(tmp);
+ if((sslot->state!=scoreboard_slot::state_free) && (sslot->pid>0))
+ kill(sslot->pid,SIGTERM);
+ }
+ }
+ wait_for_children(false);
+ // XXX: again.. is it the right way?
+ sleep(1);
+ }
+ }
+
+ void process_manager::wait_for_children(bool hang) {
+ int status;
+ int o = WUNTRACED;
+ if(!hang)
+ o|=WNOHANG;
+ while(sboard.count_slots(scoreboard_slot::state_free)<MAX_SITECING_SCOREBOARD_SLOTS) {
+ pid_t pid = waitpid(-1,&status,o);
+ if(!pid)
+ return;
+ if(pid<0) {
+ if(errno==EINTR)
+ return;
+ throw konforka::exception(CODEPOINT,"failed to waitpid()");
+ }
+ assert(pid);
+ int slot = sboard.get_slot_by_pid(pid);
+ sboard.free_slot(slot);
+ if(hang)
+ return;
+ }
+ }
+
+ void process_manager::manage_children() {
+ if(!spawn_children())
+ kill_children();
+ else
+ sleep(1); // just to get some rest.
+ }
+
+ bool process_manager::spawn_children() {
+ int total_children = 0;
+ int idle_children = 0;
+ for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
+ switch(sboard.get_slot(tmp)->state) {
+ case scoreboard_slot::state_free:
+ break;
+ case scoreboard_slot::state_idle:
+ idle_children++;
+ default:
+ total_children++;
+ break;
+ }
+ }
+ int total_lack = 0;
+ if(total_children<min_children)
+ total_lack = min_children-total_children;
+ int idle_lack = 0;
+ if(idle_children<min_spare_children)
+ idle_lack = min_spare_children-idle_children;
+ bool rv = false;
+ for(;(idle_lack>0 || total_lack>0) && (total_children<max_children);idle_lack--,total_lack--,total_children++) {
+ spawn_child();
+ rv = true;
+ }
+ return rv;
+ }
+
+ bool process_manager::kill_children() {
+ int idle_children = 0;
+ for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
+ if(sboard.get_slot(tmp)->state==scoreboard_slot::state_idle)
+ idle_children++;
+ }
+ int idle_excess = idle_children-max_spare_children;
+ bool rv = false;
+ for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS && idle_excess>0;tmp++) {
+ scoreboard_slot *sslot = sboard.get_slot(tmp);
+ if((sslot->state==scoreboard_slot::state_idle) && (sslot->pid>0)) {
+ kill(sslot->pid,SIGTERM);
+ idle_excess--;
+ rv = true;
+ }
+ }
+ return rv;
+ }
+
+ void process_manager::spawn_child() {
+ int slot = sboard.allocate_slot();
+ pid_t pid = fork();
+ if(pid<0) {
+ sboard.free_slot(slot);
+ throw konforka::exception(CODEPOINT,"failed to fork()");
+ }
+ if(!pid) {
+ // child
+ sboard.get_slot(slot)->pid = getpid();
+ process(slot);
+ _exit(0);
+ }
+ // parent
+ sboard.get_slot(slot)->pid = pid;
+ }
+
+}
diff --git a/lib/scoreboard.cc b/lib/scoreboard.cc
new file mode 100644
index 0000000..370cd93
--- a/dev/null
+++ b/lib/scoreboard.cc
@@ -0,0 +1,71 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <sys/ipc.h>
+ #include <sys/shm.h>
+ #include <cassert>
+ #include <string>
+ #include <konforka/exception.h>
+ using namespace std;
+ #include "sitecing/scoreboard.h"
+#endif
+
+namespace sitecing {
+
+ scoreboard::scoreboard()
+ : shmid(-1), slots(NULL) {
+ shmid = shmget(IPC_PRIVATE,MAX_SITECING_SCOREBOARD_SLOTS*sizeof(scoreboard_slot),IPC_CREAT|0600);
+ if(shmid<0)
+ throw konforka::exception(CODEPOINT,"failed to shmget()");
+ slots = (scoreboard_slot*)shmat(shmid,NULL,0);
+ if(shmctl(shmid,IPC_RMID,NULL))
+ throw konforka::exception(CODEPOINT,"failed to shmctl()");
+ if(!slots)
+ throw konforka::exception(CODEPOINT,"failed to shmat()");
+ for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++)
+ slots[tmp].state=scoreboard_slot::state_free;
+ }
+ scoreboard::~scoreboard() {
+ shmdt(slots);
+ }
+
+ int scoreboard::allocate_slot() {
+ for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
+ if(slots[tmp].state==scoreboard_slot::state_free) {
+ slots[tmp].state=scoreboard_slot::state_allocated;
+ slots[tmp].pid=0;
+ return tmp;
+ }
+ }
+ throw konforka::exception(CODEPOINT,"out of scoreboard slots");
+ }
+ void scoreboard::free_slot(int slot) {
+ assert(slot>=0 && slot<MAX_SITECING_SCOREBOARD_SLOTS);
+ if(slots[slot].state==scoreboard_slot::state_free)
+ throw konforka::exception(CODEPOINT,"freeing unallocated slot");
+ slots[slot].state=scoreboard_slot::state_free;
+ }
+
+ scoreboard_slot *scoreboard::get_slot(int slot) {
+ assert(slot>=0 && slot<MAX_SITECING_SCOREBOARD_SLOTS);
+ return &slots[slot];
+ }
+ int scoreboard::get_slot_by_pid(pid_t pid) {
+ for(int rv=0;rv<MAX_SITECING_SCOREBOARD_SLOTS;rv++)
+ if( (slots[rv].state!=scoreboard_slot::state_free) && (slots[rv].pid == pid) )
+ return rv;
+ throw konforka::exception(CODEPOINT,"no such process");
+ }
+
+ int scoreboard::count_slots(enum scoreboard_slot::_state state) {
+ int rv = 0;
+ for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
+ if(slots[tmp].state==state)
+ rv++;
+ }
+ return rv;
+ }
+
+}
diff --git a/lib/sitecing_enflesher.ll b/lib/sitecing_enflesher.ll
new file mode 100644
index 0000000..5f631d7
--- a/dev/null
+++ b/lib/sitecing_enflesher.ll
@@ -0,0 +1,202 @@
+%{
+#include <iostream>
+#include <fstream>
+#include <cassert>
+#include <stdexcept>
+using namespace std;
+#include "sitecing/sitecing_exception.h"
+using namespace sitecing;
+#define sitecing_enflesher_flexlexer_once
+#include "sitecing/sitecing_enflesher.h"
+#include "sitecing/sitecing_parser.h"
+#undef yyFlexLexer
+#define yyFlexLexer sitecing_enflesherFlexLexer
+%}
+%option 8bit c++ verbose noyywrap yyclass="sitecing_enflesher" yylineno prefix="sitecing_enflesher" stack debug
+
+ID [A-Za-z_][A-Za-z0-9_]*
+
+%%
+
+^\%\%\#[^\n]+\n {
+ string line = yytext;
+ line.erase(0,3);
+ line.erase(line.length()-1);
+ outs.flush();
+ outs.close();
+ outs.clear();
+ outs.open((parser.output_basename+line).c_str(),ios::trunc);
+ if(!outs.good())
+ throw preprocessor_error(CODEPOINT,"failed to write preprocessor output");
+ anchor();
+ anchoraged = true;
+}
+^\%\%[^\n]+\n {
+ string line = yytext;
+ line.erase(0,2);
+ line.erase(line.length()-1);
+ outs.flush();
+ outs.close();
+ outs.clear();
+ outs.open((parser.output_basename+line).c_str(),ios::trunc);
+ if(!outs.good())
+ throw preprocessor_error(CODEPOINT,"failed to write preprocessor output");
+ anchoraged = false;
+}
+
+\<\%component_basename\%\> outs << parser.component_basename; anchor_time = true;
+\<\%impl\%\> outs << parser.impl; anchor_time = true;
+\<\%member_functions:impl\%\> {
+ for(sitecing_parser::member_functions_t::const_iterator i=parser.member_functions.begin();i!=parser.member_functions.end();i++) {
+ outs << i->type << " " << parser.class_name << "::";
+ if(i->name.empty()) {
+ outs << parser.class_name << "()";
+ bool first = true;
+ for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) {
+ if(i->initializer.empty())
+ continue;
+ if(first) {
+ outs << ":";
+ first=false;
+ }else{
+ outs << ",";
+ }
+ if(i->bComponent) {
+ outs << i->name << "(NULL)";
+ }else {
+ outs << i->name << "(" << i->initializer << ")";
+ }
+ }
+ }else if(i->name == "~")
+ outs << "~" << parser.class_name << "()";
+ else
+ outs << i->name << i->args;
+ outs << "{\n" << i->body << "\n}\n";
+ }
+ anchor_time = true;
+}
+\<\%class_name\%\> outs << parser.class_name; anchor_time = true;
+\<\%baseclass_header\%\> outs << parser.base_header; anchor_time = true;
+\<\%decl\%\> outs << parser.decl; anchor_time = true;
+\<\%baseclass_name\%\> outs << parser.base_class; anchor_time = true;
+\<\%member_variables:decl\%\> {
+ for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) {
+ if(i->bComponent) {
+ if(i->type.empty()) {
+ i->type = parser.factory.get_classname(i->initializer);
+ }
+ if(i->bTypeOnly) {
+ outs << "typedef " << i->type << " " << i->name << ";\n";
+ }else{
+ outs << "typedef " << i->type << " __type_" << i->name << ";\nsitecing::so_component __soc_" << i->name << ";\n__type_" << i->name << " *" << i->name << ";\n";
+ }
+ }else{
+ outs << i->type << " " << i->name << ";\n";
+ }
+ }
+ anchor_time = true;
+}
+\<\%member_functions:decl\%\> {
+ for(sitecing_parser::member_functions_t::const_iterator i=parser.member_functions.begin();i!=parser.member_functions.end();i++) {
+ (i->name.empty()?outs:outs << "virtual ")
+ << i->type << " ";
+ if(i->name.empty()) {
+ outs << parser.class_name << "()";
+ }else if(i->name == "~")
+ outs << "~" << parser.class_name << "()";
+ else
+ outs << i->name << i->args;
+ outs << ";\n";
+ }
+ anchor_time = true;
+}
+\<\%imports:list\%\> {
+ for(sitecing_parser::member_variables_t::const_iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) {
+ if(i->bComponent)
+ outs << i->initializer << endl;
+ }
+ anchor_time = true;
+}
+\<\%imports:includes\%\> {
+ for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) {
+ if(i->bComponent)
+ outs << "\n#include \"" << i->initializer << ".h\"\n";
+ }
+ anchor_time = true;
+}
+\<\%imports:import\%\> {
+ for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) {
+ if(!i->bComponent)
+ continue;
+ if(i->bTypeOnly)
+ continue;
+ outs << "__soc_" << i->name << "=__SCIF->ss->fetch(\"" << i->initializer << "\",__SCIF); " << i->name << "=static_cast<__type_" << i->name << "*>(__soc_" << i->name << ".ac->__the_most_derived_this());\n";
+ }
+ anchor_time = true;
+}
+
+\<\%base_component\%\> {
+ // TODO:
+ anchor_time = true;
+}
+
+\<\%ancestors:includes\%\> {
+ for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) {
+ outs << "#include \"" << i->path << ".h\"\n";
+ }
+ anchor_time = true;
+}
+\<\%ancestors:component_list\%\> {
+ for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) {
+ outs << i->path << "\n";
+ }
+ anchor_time = true;
+}
+\<\%ancestors:base_clause_part\%\> {
+ for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) {
+ outs << ", virtual public " << parser.factory.get_classname(i->path);
+ }
+}
+\<\%ancestors:typedefs\%\> {
+ for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) {
+ outs << "typedef class " << parser.factory.get_classname(i->path) << " " << i->name << ";\n";
+ }
+ anchor_time = true;
+}
+\<\%ancestors:import\%\> {
+ for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) {
+ outs << i->name << "::__do_imports();\n";
+ }
+ anchor_time = true;
+}
+
+\n {
+ if(anchor_time)
+ anchor();
+ ECHO;
+}
+. ECHO;
+
+%%
+
+void sitecing_enflesher::LexerOutput(const char *buf,int size) {
+ outs.write(buf,size);
+}
+
+void sitecing_enflesher::enflesh() {
+ ifstream ifs(parser.skeleton.c_str());
+ if(!ifs.good())
+ throw preprocessor_error(CODEPOINT,"failed to open skeleton file");
+ switch_streams(&ifs,NULL);
+ yylex();
+}
+
+void sitecing_enflesher::anchor() {
+ if(!anchoraged)
+ return;
+ outs << "\n#line " << lineno() << " \"" << parser.skeleton << "\"\n";
+ anchor_time = false;
+}
+/*
+ * vim:set ft=lex:
+ */
diff --git a/lib/sitecing_interface_cgi.cc b/lib/sitecing_interface_cgi.cc
new file mode 100644
index 0000000..5c3d295
--- a/dev/null
+++ b/lib/sitecing_interface_cgi.cc
@@ -0,0 +1,25 @@
+#include <cassert>
+#include "sitecing/sitecing_interface_cgi.h"
+
+namespace sitecing {
+
+ sitecing_interface_cgi::sitecing_interface_cgi(sitespace *s)
+ : sitecing_interface(&prebuffer), ss(s), cgigw(NULL) {
+ }
+
+ void sitecing_interface_cgi::prepare(kingate::cgi_gateway *cg) {
+ cgigw = cg;
+ headers.clear();
+ headers["Content-Type"] = "text/html";
+ prebuffer.str("");
+ }
+
+ void sitecing_interface_cgi::flush() {
+ assert(cgigw);
+ for(headers_t::const_iterator i=headers.begin();i!=headers.end();i++)
+ cgigw->out() << i->first << ": " << i->second << "\n";
+ (cgigw->out() << "\n").write(prebuffer.str().c_str(),prebuffer.tellp());
+ cgigw->out().flush();
+ }
+
+}
diff --git a/lib/sitecing_parser.ll b/lib/sitecing_parser.ll
new file mode 100644
index 0000000..6cb78f3
--- a/dev/null
+++ b/lib/sitecing_parser.ll
@@ -0,0 +1,594 @@
+%{
+ /*
+ * XXX: I have a strong feeling that this parser should be completely rewritten.
+ */
+#include <iostream>
+#include <fstream>
+#include <cassert>
+#include <stdexcept>
+using namespace std;
+#include "sitecing/sitecing_util.h"
+#include "sitecing/sitecing_exception.h"
+using namespace sitecing;
+#define sitecing_parser_flexlexer_once
+#include "sitecing/sitecing_parser.h"
+#include "sitecing/sitecing_enflesher.h"
+#undef yyFlexLexer
+#define yyFlexLexer sitecing_parserFlexLexer
+%}
+%x SLASHSTAR_COMMENT SLASHSLASH_COMMENT STRING
+%x CODELINE CLASSLINE DECLLINE IMPLLINE DECLBLOCK IMPLBLOCK VARLINE VARINIT
+%x IMPORTLINE IMPORTCOMPONENT
+%x IMPORTTYPELINE IMPORTTYPECOMPONENT
+%x DERIVELINE DERIVECOMPONENT
+%x CONSTRUCTOR DESTRUCTOR CODEMETHODLINE CODEMETHODARGS
+%x CODEMETHODBLOCK INLINE METHODLINE METHODARGS METHODBLOCK CODEBLOCK OUTPUTBLOCK
+%option 8bit c++ verbose noyywrap yyclass="sitecing_parser" prefix="sitecing_parser" stack yylineno
+
+WHITESPACE [ \t]
+ID [A-Za-z_][A-Za-z0-9_]*
+NOIDCHAR [^A-Za-z0-9_]
+
+%%
+
+<INITIAL>{
+ ^\%\%class{WHITESPACE}+ {
+ // TODO: signal error if we already have class name acquired from source.
+ modi.push_front(modus_operandi(modus_operandi::flag_devour_comments|modus_operandi::flag_devour_whitespace));
+ BEGIN(CLASSLINE);
+ }
+ ^\%\%decl{WHITESPACE}+ {
+ modi.push_front(modus_operandi(0));
+ anchor();
+ BEGIN(DECLLINE);
+ }
+ ^\%\%impl{WHITESPACE}+ {
+ modi.push_front(modus_operandi(0));
+ anchor();
+ BEGIN(IMPLLINE);
+ }
+ \<\%decl\> {
+ modi.push_front(modus_operandi(0));
+ anchor();
+ BEGIN(DECLBLOCK);
+ }
+ \<\%impl\> {
+ modi.push_front(modus_operandi(0));
+ anchor();
+ BEGIN(IMPLBLOCK);
+ }
+ ^\%\%var{WHITESPACE}+ {
+ modi.push_front(modus_operandi(modus_operandi::flag_devour_comments));
+ anchor();
+ BEGIN(VARLINE);
+ }
+ ^\%\%import{WHITESPACE}+ {
+ modi.push_front(modus_operandi(modus_operandi::flag_devour_comments));
+ BEGIN(IMPORTLINE);
+ }
+ ^\%\%import_type{WHITESPACE}+ {
+ modi.push_front(modus_operandi(modus_operandi::flag_devour_comments));
+ BEGIN(IMPORTTYPELINE);
+ }
+ ^\%\%derive{WHITESPACE}+ {
+ modi.push_front(modus_operandi(modus_operandi::flag_devour_comments));
+ BEGIN(DERIVELINE);
+ }
+ \<\%constructor\> {
+ modi.push_front(modus_operandi());
+ anchor();
+ BEGIN(CONSTRUCTOR);
+ }
+ \<\%destructor\> {
+ modi.push_front(modus_operandi());
+ anchor();
+ BEGIN(DESTRUCTOR);
+ }
+ \<\%codemethod{WHITESPACE}+ {
+ modi.push_front(modus_operandi(modus_operandi::flag_devour_comments));
+ anchor();
+ BEGIN(CODEMETHODLINE);
+ }
+ \<\%method{WHITESPACE}+ {
+ modi.push_front(modus_operandi(modus_operandi::flag_devour_comments));
+ anchor();
+ BEGIN(METHODLINE);
+ }
+ <<EOF>> {
+ assert(modi.size()==1);
+ M().modify(modus_operandi::modus_preop);
+ LexerOutput(";",1);
+ return 0;
+ }
+}
+<<EOF>> throw preprocessor_error(CODEPOINT,"unexpected end of file",lineno());
+
+<CODEBLOCK,CODEMETHODBLOCK>{
+ "<%output>" {
+ anchor();
+ yy_push_state(OUTPUTBLOCK);
+ }
+}
+
+<METHODLINE>{
+ {WHITESPACE}+ {
+ modus_operandi& m = modi.front();
+ if(!m.output.empty()) {
+ if(!m._lastid.empty()) {
+ if(!m._type.empty()) m._type += ' ';
+ m._type += m._lastid;
+ }
+ m._lastid = m.output;
+ m.output.clear();
+ }
+ }
+ \* {
+ modus_operandi& m = modi.front();
+ ECHO;
+ if(!m._lastid.empty()) {
+ if(!m._type.empty()) m._type += ' ';
+ m._type += m._lastid;
+ }
+ m._lastid = m.output;
+ m.output.clear();
+ }
+ \( {
+ modus_operandi& m = modi.front();
+ if(m.output.empty()) {
+ m._name=m._lastid;
+ }else{
+ if(!m._lastid.empty()) { // XXX: lastid, I believe should never be emtpy...
+ if(!m._type.empty()) m._type += ' ';
+ m._type += m._lastid;
+ }
+ m._name = m.output;
+ m.output.clear();
+ }
+ ECHO;
+ BEGIN(METHODARGS);
+ }
+}
+<METHODARGS>{
+ \%\> {
+ modus_operandi& m = modi.front();
+ m._args = m.output;
+ m.output.clear();
+ anchor();
+ BEGIN(METHODBLOCK);
+ }
+}
+
+<INITIAL,METHODBLOCK,OUTPUTBLOCK>{
+ \<\%{WHITESPACE}+ {
+ M().modify(modus_operandi::modus_postop);
+ anchor();
+ LexerOutput("(",1);
+ yy_push_state(INLINE);
+ }
+ ^\%{WHITESPACE} {
+ M().modify(modus_operandi::modus_code);
+ anchor();
+ yy_push_state(CODELINE);
+ }
+ \<\%code\> {
+ M().modify(modus_operandi::modus_code);
+ anchor();
+ yy_push_state(CODEBLOCK);
+ }
+ "</%output>" {
+ if(YY_START!=OUTPUTBLOCK) throw preprocessor_error(CODEPOINT,"unexpected tag",lineno());
+ M().modify(modus_operandi::modus_code);
+ anchor();
+ yy_pop_state();
+ }
+}
+
+<INLINE>\%\> LexerOutput(")",1); M().modus=modus_operandi::modus_preop; yy_pop_state();
+<CODELINE>\n yy_pop_state();
+
+<CODEMETHODLINE>{
+ {WHITESPACE}+ {
+ modus_operandi& m = modi.front();
+ if(!m.output.empty()) {
+ if(!m._lastid.empty()) {
+ if(!m._type.empty()) m._type += ' ';
+ m._type += m._lastid;
+ }
+ m._lastid = m.output;
+ m.output.clear();
+ }
+ }
+ \* {
+ modus_operandi& m = modi.front();
+ ECHO;
+ if(!m._lastid.empty()) {
+ if(!m._type.empty()) m._type += ' ';
+ m._type += m._lastid;
+ }
+ m._lastid = m.output;
+ m.output.clear();
+ }
+ \( {
+ modus_operandi& m = modi.front();
+ if(m.output.empty()) {
+ m._name=m._lastid;
+ }else{
+ if(!m._lastid.empty()) { // XXX: lastid, I believe should never be emtpy...
+ if(!m._type.empty()) m._type += ' ';
+ m._type += m._lastid;
+ }
+ m._name = m.output;
+ m.output.clear();
+ }
+ ECHO;
+ BEGIN(CODEMETHODARGS);
+ }
+}
+<CODEMETHODARGS>{
+ \%\> {
+ modus_operandi& m = modi.front();
+ m._args = m.output;
+ m.output.clear();
+ m.flags=0;
+ anchor();
+ BEGIN(CODEMETHODBLOCK);
+ }
+}
+
+<IMPORTLINE>{
+ {WHITESPACE}+ { }
+ {ID} {
+ if(!modi.front()._name.empty())
+ throw preprocessor_error(CODEPOINT,"syntax error",lineno());
+ modi.front()._name = yytext;
+ }
+ \= {
+ modi.front().output.clear();
+ BEGIN(IMPORTCOMPONENT);
+ }
+}
+<IMPORTCOMPONENT>{
+ {WHITESPACE}+ { }
+ \n {
+ modus_operandi& m = M();
+ string::size_type t = m.output.find_first_not_of(" \t");
+ if(t!=string::npos)
+ m.output.erase(0,t);
+ t = m.output.find_last_not_of(" \t;");
+ if(t!=string::npos)
+ m.output.erase(t+1);
+ if(m.output[0]=='"' && m.output[m.output.length()-1]=='"') {
+ m.output.erase(0,1);
+ m.output.erase(m.output.length()-1);
+ }
+ string c = combine_path(component_basename,m.output);
+ member_variables.push_back(member_variable(m._type,m._name,normalize_path(c,strip_leading_slash),true));
+ modi.pop_front();
+ BEGIN(INITIAL);
+ }
+}
+
+<IMPORTTYPELINE>{
+ {WHITESPACE}+ { }
+ {ID} {
+ if(!modi.front()._name.empty())
+ throw preprocessor_error(CODEPOINT,"syntax error",lineno());
+ modi.front()._name = yytext;
+ }
+ \= {
+ modi.front().output.clear();
+ BEGIN(IMPORTTYPECOMPONENT);
+ }
+}
+<IMPORTTYPECOMPONENT>{
+ {WHITESPACE}+ { }
+ \n {
+ modus_operandi& m = M();
+ string::size_type t = m.output.find_first_not_of(" \t");
+ if(t!=string::npos)
+ m.output.erase(0,t);
+ t = m.output.find_last_not_of(" \t;");
+ if(t!=string::npos)
+ m.output.erase(t+1);
+ if(m.output[0]=='"' && m.output[m.output.length()-1]=='"') {
+ m.output.erase(0,1);
+ m.output.erase(m.output.length()-1);
+ }
+ string c = combine_path(component_basename,m.output);
+ member_variables.push_back(member_variable(m._type,m._name,normalize_path(c,strip_leading_slash),true,true));
+ modi.pop_front();
+ BEGIN(INITIAL);
+ }
+}
+
+<DERIVELINE>{
+ {WHITESPACE}+ { }
+ {ID} {
+ if(!modi.front()._name.empty())
+ throw preprocessor_error(CODEPOINT,"syntax_error",lineno());
+ modi.front()._name = yytext;
+ }
+ \= {
+ modi.front().output.clear();
+ BEGIN(DERIVECOMPONENT);
+ }
+}
+<DERIVECOMPONENT>{
+ {WHITESPACE}+ { }
+ \n {
+ modus_operandi& m = M();
+ string::size_type t = m.output.find_first_not_of(" \t");
+ if(t!=string::npos)
+ m.output.erase(0,t);
+ t = m.output.find_last_not_of(" \t;");
+ if(t!=string::npos)
+ m.output.erase(t+1);
+ if(m.output[0]=='"' && m.output[m.output.length()-1]=='"') {
+ m.output.erase(0,1);
+ m.output.erase(m.output.length()-1);
+ }
+ string c = combine_path(component_basename,m.output);
+ ancestor_classes.push_back(ancestor_class(m._name,normalize_path(c,strip_leading_slash)));
+ modi.pop_front();
+ BEGIN(INITIAL);
+ }
+}
+
+<VARLINE>{
+ {WHITESPACE}+ {
+ modus_operandi& m = modi.front();
+ if(!m.output.empty()) {
+ if(!m._lastid.empty()) {
+ if(!m._type.empty()) m._type += ' ';
+ m._type += m._lastid;
+ }
+ m._lastid = m.output;
+ m.output.clear();
+ }
+ }
+ \* {
+ modus_operandi& m = modi.front();
+ ECHO;
+ if(!m._lastid.empty()) {
+ if(!m._type.empty()) m._type += ' ';
+ m._type += m._lastid;
+ }
+ m._lastid = m.output;
+ m.output.clear();
+ }
+ \;|\n|\= {
+ modus_operandi& m = modi.front();
+ if(m.output.empty()) {
+ m._name=m._lastid;
+ }else{
+ if(!m._lastid.empty()) { // XXX: lastid should never be emtpy, I believe?
+ if(!m._type.empty()) m._type += ' ';
+ m._type += m._lastid;
+ }
+ m._name=m.output;
+ m.output.clear();
+ }
+ BEGIN(VARINIT);
+ if(*yytext!='=')
+ unput('\n');
+ }
+}
+<VARINIT>{
+ \n {
+ modus_operandi& m = modi.front();
+ string::size_type t = m.output.find_first_not_of(" \t");
+ if(t!=string::npos)
+ m.output.erase(0,t);
+ t = m.output.find_last_not_of(" \t;");
+ if(t!=string::npos)
+ m.output.erase(t+1);
+ member_variables.push_back(member_variable(m._type,m._name,m.output));
+ if(!m.output.empty())
+ have_initializers=true;
+ modi.pop_front();
+ BEGIN(INITIAL);
+ }
+}
+<DECLLINE>\n {
+ ECHO;
+ decl += modi.front().output;
+ modi.pop_front();
+ BEGIN(INITIAL);
+}
+<IMPLLINE>\n {
+ ECHO;
+ impl += modi.front().output;
+ modi.pop_front();
+ BEGIN(INITIAL);
+}
+<CLASSLINE>\n {
+ class_name = modi.front().output;
+ modi.pop_front();
+ BEGIN(INITIAL);
+}
+<CLASSLINE,DECLLINE,IMPLLINE,VARLINE,VARINIT,IMPORTLINE,IMPORTCOMPONENT,CODEMETHODLINE,CODEMETHODARGS,INLINE,METHODLINE,METHODARGS,DECLBLOCK,IMPLBLOCK,CONSTRUCTOR,DESTRUCTOR,CODEMETHODBLOCK,CODELINE,CODEBLOCK>{
+ "/*" {
+ yy_push_state(SLASHSTAR_COMMENT);
+ if(!M().devour_comments()) {
+ ECHO;
+ }
+ }
+ "//" {
+ yy_push_state(SLASHSLASH_COMMENT);
+ if(!M().devour_comments()) {
+ ECHO;
+ }
+ }
+ \" {
+ yy_push_state(STRING);
+ ECHO;
+ }
+ \'\\.\' {
+ ECHO;
+ }
+}
+
+<INITIAL,METHODBLOCK,OUTPUTBLOCK>{
+ \" soft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\\"",2);
+ \n soft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\n",2);
+ \r soft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\r",2);
+ \t soft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\t",2);
+ \b soft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\b",2);
+ \a soft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\a",2);
+ . soft_anchor(); M().modify(modus_operandi::modus_text); ECHO;
+ {WHITESPACE}+ soft_anchor(); M().modify(modus_operandi::modus_text); ECHO;
+}
+
+<DECLBLOCK,IMPLBLOCK,CONSTRUCTOR,DESTRUCTOR,CODEMETHODBLOCK,METHODBLOCK,CODEBLOCK>{
+ \<\/\%decl\> {
+ if(YY_START!=DECLBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno());
+ decl += modi.front().output;
+ modi.pop_front();
+ BEGIN(INITIAL);
+ }
+ \<\/\%impl\> {
+ if(YY_START!=IMPLBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno());
+ impl += modi.front().output;
+ modi.pop_front();
+ BEGIN(INITIAL);
+ }
+ \<\/\%constructor\> {
+ if(YY_START!=CONSTRUCTOR) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno());
+ member_functions.push_back(member_function("","","",modi.front().output));
+ have_constructor = true;
+ modi.pop_front();
+ BEGIN(INITIAL);
+ }
+ \<\/\%destructor\> {
+ if(YY_START!=DESTRUCTOR) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno());
+ member_functions.push_back(member_function("","~","",modi.front().output));
+ modi.pop_front();
+ BEGIN(INITIAL);
+ }
+ \<\/\%codemethod\> {
+ if(YY_START!=CODEMETHODBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno());
+ modus_operandi& m = modi.front();
+ member_functions.push_back(member_function(m._type,m._name,m._args,m.output));
+ modi.pop_front();
+ BEGIN(INITIAL);
+ }
+ \<\/%method\> {
+ if(YY_START!=METHODBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno());
+ modus_operandi& m = modi.front();
+ m.modify(modus_operandi::modus_code);
+ member_functions.push_back(member_function(m._type,m._name,m._args,m.output));
+ modi.pop_front();
+ BEGIN(INITIAL);
+ }
+ \<\/%code\> {
+ if(YY_START!=CODEBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno());
+ yy_pop_state();
+ }
+ \n ECHO;
+}
+
+<SLASHSTAR_COMMENT>{
+ "*/" {
+ if(!M().devour_comments()) {
+ ECHO;
+ }
+ yy_pop_state();
+ unput(' ');
+ }
+ \n {
+ if(!M().devour_comments()) {
+ ECHO;
+ }
+ }
+}
+<SLASHSLASH_COMMENT>{
+ \n {
+ if(!M().devour_comments()) {
+ ECHO;
+ }
+ yy_pop_state();
+ if(YY_START!=CODEBLOCK && YY_START!=CODEMETHODBLOCK && YY_START!=IMPLBLOCK && YY_START!=DECLBLOCK)
+ unput('\n');
+ }
+}
+<SLASHSTAR_COMMENT,SLASHSLASH_COMMENT>. {
+ if(!M().devour_comments()) {
+ ECHO;
+ }
+}
+<STRING>{
+ \\. ECHO;
+ \" ECHO; yy_pop_state();
+ . ECHO;
+}
+
+{WHITESPACE}+ {
+ if(!(M().flags&modus_operandi::flag_devour_whitespace)) {
+ ECHO;
+ }
+}
+
+%%
+
+sitecing_parser::sitecing_parser(component_factory& f)
+ : factory(f), have_initializers(false), have_constructor(false),
+ base_class("sitecing::cgi_component"),
+ base_header("sitecing/cgi_component.h"),
+ skeleton(__SC_DEFAULT_SKELETON) {
+ }
+
+void sitecing_parser::preprocess(const string& in) {
+ ifstream ifs(in.c_str(),ios::in);
+ if(!ifs.good())
+ throw preprocessor_error(CODEPOINT,"failed to open input file");
+ input_file = in;
+ modi.push_front(modus_operandi(0));
+ switch_streams(&ifs,NULL);
+ if(yylex())
+ throw preprocessor_error(CODEPOINT,"unknown error");
+ member_functions.push_back(member_function("void","main","(int _magic,va_list _args)",M().output));
+ if(have_initializers && !have_constructor)
+ member_functions.push_back(member_function("","","",""));
+ sitecing_enflesher enflesher(*this);
+ enflesher.enflesh();
+}
+
+void sitecing_parser::LexerOutput(const char* buf,int size) {
+ assert(modi.size());
+ M().output.append(buf,size);
+}
+
+static const char *modus_transitions
+ [sitecing_parser::modus_operandi::modi]
+ [sitecing_parser::modus_operandi::modi] = {
+// To:
+// code preop postop text From:
+ { "", "(*(__SCIF->out))", "(*(__SCIF->out))<<", "(*(__SCIF->out))<<\"" }, // code
+ { ";", "", "<<", "<<\"" }, // preop
+ { NULL, NULL, "", "\"" }, // postop
+ { "\";", "\"", "\"<<", "" } // text
+};
+
+void sitecing_parser::modus_operandi::modify(modus_t m) {
+ const char * x = modus_transitions[modus][m];
+ assert(x);
+ output += x;
+ modus = m;
+}
+
+void sitecing_parser::soft_anchor() {
+ if(M().modus!=modus_operandi::modus_text)
+ anchor();
+}
+void sitecing_parser::anchor() {
+ if(M().modus==modus_operandi::modus_text)
+ M().modify(modus_operandi::modus_preop);
+ M().output += "\n#line ";
+ char tmp[7];
+ snprintf(tmp,sizeof(tmp),"%d",lineno());
+ M().output += tmp;
+ M().output += " \"";
+ M().output += input_file;
+ M().output += "\"\n";
+}
+/* vim:set ft=lex: */
diff --git a/lib/sitecing_util.cc b/lib/sitecing_util.cc
new file mode 100644
index 0000000..9b6c54e
--- a/dev/null
+++ b/lib/sitecing_util.cc
@@ -0,0 +1,278 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include <sys/stat.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <sys/ipc.h>
+ #include <sys/sem.h>
+ #include <errno.h>
+ #include <iostream>
+ #include <fstream>
+ #include <cassert>
+ #include "sitecing/sitecing_util.h"
+#endif
+
+namespace sitecing {
+
+ /*
+ * XXX: all of these utilities could be sheerly optimized.
+ */
+
+ string normalize_path(const string& path,int opts) {
+ const char *s = path.c_str();
+ string rv;
+ string::size_type notslash = 0;
+ if( (*s)=='.' && s[1]=='/' )
+ s+=2;
+ if(opts&strip_leading_slash)
+ for(;(*s) && (*s)=='/';s++);
+ for(;*s;s++) {
+ if( (*s)=='/' ) {
+ if(s[1]=='/')
+ continue;
+ if(s[1]=='.' && s[2]=='/') {
+ s+=2;
+ continue;
+ }
+ }
+ if(opts&restrict_dotdot) {
+ if(
+ ( rv.empty() && s[0]=='.' && s[1]=='.' && s[2]=='/' ) // "^../"
+ || ( s[0]=='/' && s[1]=='.' && s[2]=='.' && (s[3]==0 || s[3]=='/') ) // "/..(/|$)"
+ )
+ throw utility_restricted_sequence(CODEPOINT,"restricted updir sequence encountered");
+ }
+ rv += *s;
+ if( (*s) != '/' )
+ notslash=rv.length();
+ }
+ if(!(opts&strip_trailing_slash))
+ notslash++;
+ if(notslash<rv.length())
+ rv.erase(notslash); // XXX: check the logic of stripping/not strippling trailing slash
+ return rv;
+ }
+
+ string strip_prefix(const string& str,const string& prefix) {
+ if(str.compare(0,prefix.length(),prefix))
+ throw utility_no_prefix(CODEPOINT,"no such prefix");
+ return str.substr(prefix.length());
+ }
+
+ string strip_suffix(const string& str,const string& suffix) {
+ if(str.compare(str.length()-suffix.length(),suffix.length(),suffix))
+ throw utility_no_suffix(CODEPOINT,"no such suffix");
+ return str.substr(0,str.length()-suffix.length());
+ }
+
+ string dir_name(const string& filename) {
+ string::size_type sl = filename.find_last_of('/');
+ if(sl==string::npos)
+ return ""; // no slashes -- no dir.
+ string::size_type nosl = filename.find_last_not_of('/',sl);
+ if(nosl==string::npos)
+ return ""; // only slashes -- no dir. XXX: only slashes after the last slash... does it mean no dir?
+ return filename.substr(0,nosl+1);
+ }
+
+ void make_path(const string& path,mode_t mode) {
+ struct stat st;
+ for(string::size_type sl=0;sl!=string::npos;sl=path.find('/',sl+1)) {
+ if(!sl)
+ continue;
+ string p = path.substr(0,sl);
+ if(stat(p.c_str(),&st) || !S_ISDIR(st.st_mode)) {
+ if(mkdir(p.c_str(),mode))
+ throw konforka::exception(CODEPOINT,"failed to mkdir()");
+ }
+ }
+ if(stat(path.c_str(),&st) || !S_ISDIR(st.st_mode)) {
+ if(mkdir(path.c_str(),mode))
+ throw konforka::exception(CODEPOINT,"failed to mkdir()");
+ }
+ }
+
+ void file_lock::lock(const string& f) {
+ unlock();
+ fd = open(f.c_str(),O_CREAT|O_RDWR,S_IRUSR|S_IWUSR);
+ if(fd<0)
+ throw konforka::exception(CODEPOINT,"failed to open/create lockfile");
+ try {
+ lock();
+ }catch(konforka::exception& ke) {
+ ke.see(CODEPOINT);
+ close(fd); fd=-1;
+ throw;
+ }catch(...) {
+ close(fd); fd=-1;
+ throw;
+ }
+ }
+ void file_lock::lock() {
+ assert(fd>=0);
+ struct flock fl;
+ fl.l_type = F_WRLCK;
+ fl.l_whence=SEEK_SET;
+ fl.l_start=fl.l_len=0;
+ for(int tries=3;tries;tries--) {
+ if(!fcntl(fd,F_SETLK,&fl))
+ return;
+ sleep(8);
+ }
+ throw konforka::exception(CODEPOINT,"failed to obtain file lock");
+ }
+ void file_lock::unlock() {
+ if(fd<0)
+ return;
+ struct flock fl;
+ fl.l_type = F_UNLCK;
+ fl.l_whence=SEEK_SET;
+ fl.l_start=fl.l_len=0;
+ int rv = fcntl(fd,F_SETLK,&fl);
+ close(fd);
+ fd=-1;
+ if(rv)
+ throw konforka::exception(CODEPOINT,"failed to release file lock");
+ }
+
+ void pid_file::set(const string& f,bool u) {
+ ofstream of(f.c_str(),ios::trunc);
+ if(!of)
+ throw konforka::exception(CODEPOINT,"failed to open file for writing pid");
+ of << getpid() << endl;
+ of.close();
+ file_name = f;
+ unlink_pid = u;
+ }
+ void pid_file::unlink() {
+ if(!unlink_pid)
+ return;
+ ::unlink(file_name.c_str());
+ }
+
+ void semaphore::init() {
+ deinit();
+ semid = semget(IPC_PRIVATE,1,IPC_CREAT|0600);
+ if(semid<0)
+ throw konforka::exception(CODEPOINT,"failed to semget()");
+ if(semctl(semid,0,SETVAL,1))
+ throw konforka::exception(CODEPOINT,"failed to semctl()");
+ }
+ void semaphore::deinit() {
+ if(semid<0)
+ return;
+ semctl(semid,0,IPC_RMID,0);
+ }
+ void semaphore::on() {
+ assert(semid>=0);
+ struct sembuf sb;
+ sb.sem_num=0;
+ sb.sem_op=-1;
+ sb.sem_flg = SEM_UNDO;
+ while(semop(semid,&sb,1)<0) {
+ if(errno!=EINTR)
+ throw konforka::exception(CODEPOINT,"failed to semop()");
+ }
+ }
+ void semaphore::off() {
+ assert(semid>=0);
+ struct sembuf sb;
+ sb.sem_num=0;
+ sb.sem_op=1;
+ sb.sem_flg = SEM_UNDO;
+ while(semop(semid,&sb,1)<0) {
+ if(errno!=EINTR)
+ throw konforka::exception(CODEPOINT,"failed to semop()");
+ }
+ }
+
+ void semaphore_lock::lock() {
+ assert(sem);
+ if(locked)
+ return;
+ sem->on();
+ locked = true;
+ }
+ void semaphore_lock::unlock() {
+ if(!sem)
+ return;
+ if(!locked)
+ return;
+ sem->off();
+ locked=false;
+ }
+
+ string combine_path(const string& origin,const string& relative,int opts) {
+ string r = normalize_path(relative,0);
+ string rv;
+ // XXX: what to do if relative is empty is a question, really.
+ if(r.empty()) {
+ return normalize_path( (opts&origin_is_file)?dir_name(origin):origin ,strip_leading_slash|restrict_dotdot|strip_trailing_slash);
+ }else{
+ if(r[0]=='/') {
+ r.erase(0,1);
+ }else{
+ rv = normalize_path((opts&origin_is_file)?dir_name(origin):origin,restrict_dotdot|strip_trailing_slash);
+ }
+ }
+ string::size_type lsl = rv.rfind('/');
+ for(string::size_type sl=r.find('/');sl!=string::npos;sl=r.find('/')) {
+ assert(sl!=0);
+ if(sl==1 && r[0]=='.') {
+ // it's a "./"
+ r.erase(0,2);
+ }else if(sl==2 && r[0]=='.' && r[1]=='.') {
+ // we have a "../"
+ if(lsl==string::npos) {
+ if(rv.empty() && (opts&fail_beyond_root))
+ throw utility_beyond_root(CODEPOINT,"went beyond root while combining path");
+ rv.clear();
+ }else{
+ rv.erase(lsl);
+ lsl = rv.rfind('/');
+ }
+ r.erase(0,3);
+ }else{
+ // we have a "something/"
+ lsl = rv.length();
+ rv += '/';
+ rv += r.substr(0,sl);
+ r.erase(0,sl+1);
+ }
+ }
+ if(r.empty())
+ return rv+'/';
+ if(r.length()==2 && r[0]=='.' && r[0]=='.') {
+ if(lsl==string::npos) {
+ if(rv.empty() & (opts&fail_beyond_root))
+ throw utility_beyond_root(CODEPOINT,"went beyond root while combining path");
+ return "/";
+ }else{
+ rv.erase(lsl+1);
+ return rv;
+ }
+ }
+ rv += '/';
+ rv += r;
+ return rv;
+ }
+
+ void auto_chdir::pushdir(const string& td,bool ap) {
+ char *tmp = get_current_dir_name();
+ assert(tmp);
+ saved_pwd = tmp;
+ free(tmp);
+ autopop=ap;
+ if(chdir(td.c_str()))
+ throw konforka::exception(CODEPOINT,"failed to chdir()");
+ }
+ void auto_chdir::popdir() {
+ autopop=false;
+ if(chdir(saved_pwd.c_str()))
+ throw konforka::exception(CODEPOINT,"failed to chdir()");
+ // XXX: or should it be thrown? after all we call it from destructor...
+ }
+
+}
diff --git a/lib/sitespace.cc b/lib/sitespace.cc
new file mode 100644
index 0000000..0406d11
--- a/dev/null
+++ b/lib/sitespace.cc
@@ -0,0 +1,52 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include <cassert>
+ #include "sitecing/sitespace.h"
+ #include "sitecing/sitecing_util.h"
+#endif
+
+namespace sitecing {
+
+ sitespace::sitespace(configuration& c)
+ : config(c), factory(c) { }
+
+ sitespace::~sitespace() {
+ for(sentenced_t::iterator i = sentenced.begin();i!=sentenced.end();++i) {
+ assert((*i)->chickens_used.empty());
+ delete *i;
+ }
+ }
+
+ so_component sitespace::fetch(const string& c,sitecing_interface* scif) {
+ execute_sentenced();
+ string sobase = normalize_path(c);
+ string sopath = factory.root_so+sobase+".so";
+ config_options *co_build = config.lookup_config(sobase,config_options::flag_build);
+ if( (!co_build) || co_build->build )
+ factory.make(sopath);
+ components_t::iterator i = components.find(sopath);
+ if(i!=components.end()) {
+ if(i->second->is_uptodate())
+ return so_component(i->second,scif);
+ if(i->second->chickens_used.empty()) {
+ delete i->second;
+ }else{
+ sentenced.push_back(i->second);
+ }
+ components.erase(i);
+ }
+ pair<components_t::iterator,bool> ins = components.insert(components_t::value_type(sopath,new component_so(sopath)));
+ return so_component(ins.first->second,scif);
+ }
+
+ void sitespace::execute_sentenced() {
+ for(sentenced_t::iterator i = sentenced.begin();i!=sentenced.end();++i) {
+ if((*i)->chickens_used.empty()) {
+ delete *i;
+ sentenced.erase(i);
+ }
+ }
+ }
+
+}
diff --git a/lib/util.cc b/lib/util.cc
new file mode 100644
index 0000000..1a81c56
--- a/dev/null
+++ b/lib/util.cc
@@ -0,0 +1,83 @@
+#ifdef USE_PCH
+ #include "pch.h"
+#else
+ #include <cassert>
+ #include "sitecing/util.h"
+#endif
+
+namespace sitecing {
+
+ static const char *unsafeChars = "<>& \n\"";
+
+ string html_escape(const string& str,int flags) {
+ string rv = str;
+ string::size_type screwed = 0;
+ for(;;) {
+ screwed = rv.find_first_of(unsafeChars,screwed);
+ if(screwed == string::npos)
+ break;
+ while(screwed<rv.length() && strchr(unsafeChars,rv.at(screwed))) {
+ char danger = rv.at(screwed);
+ switch(danger) {
+ case '<':
+ rv.replace(screwed,1,"&lt;"); screwed+=4;
+ break;
+ case '>':
+ rv.replace(screwed,1,"&gt;"); screwed+=4;
+ break;
+ case '&':
+ rv.replace(screwed,1,"&amp;"); screwed+=5;
+ break;
+ case ' ':
+ if(flags&html_escape_nbsp) {
+ rv.replace(screwed,1,"&nbsp;"); screwed+=6;
+ }else
+ screwed++;
+ break;
+ case '\n':
+ if(flags&html_escape_br) {
+ if(flags&html_escape_br_noslash) {
+ rv.replace(screwed,1,"<br>\n"); screwed += 5;
+ }else{
+ rv.replace(screwed,1,"<br/>\n"); screwed += 6;
+ }
+ }else
+ screwed++;
+ break;
+ case '\"':
+ if(flags&html_escape_quot) {
+ rv.replace(screwed,1,"&quot;"); screwed+=6;
+ }else
+ screwed++;
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+ }
+ return rv;
+ }
+
+ void checkpoint::set() {
+ point = stream->tellp();
+ if(last_will==will_intestate)
+ last_will = will_rollback;
+ }
+
+ void checkpoint::make_will(will_t lw) {
+ last_will = lw;
+ }
+
+ void checkpoint::rollback() {
+ stream->seekp(point);
+ if(last_will == will_rollback)
+ last_will = will_intestate;
+ }
+ void checkpoint::commit() {
+ point = stream->tellp();
+ if(last_will == will_rollback)
+ last_will = will_intestate;
+ }
+
+}