summaryrefslogtreecommitdiffabout
path: root/lib
authorMichael Krelin <hacker@klever.net>2005-01-29 21:21:05 (UTC)
committer Michael Krelin <hacker@klever.net>2005-01-29 21:21:05 (UTC)
commitce1f37aae46ea95020d7b865f7a80e8abdfad0d8 (patch) (unidiff)
tree4964383ab8cd7e6d8ea821f1a615d1bbcf98dad8 /lib
parent3c75c860fc1ad5b3f5185e23ec6f438dd2528958 (diff)
downloadsitecing-0.0.zip
sitecing-0.0.tar.gz
sitecing-0.0.tar.bz2
initial commit into repository0.0
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 @@
1Makefile.in
2sitecing_enflesher.cc
3sitecing_parser.cc
4.libs
5.deps
6Makefile
7*.o
8*.lo
9*.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 @@
1lib_LTLIBRARIES = libsitecing.la
2noinst_HEADERS = pch.h
3
4INCLUDES = -I${top_srcdir}/include -I${top_builddir} ${KINGATE_CFLAGS} ${DOTCONF_CFLAGS} \
5 ${PCREPP_CFLAGS}
6AM_CPPFLAGS = -D__SC_DEFAULT_SKELETON=\"${pkgdatadir}/component.skel\"
7AM_LFLAGS = -olex.yy.c
8
9libsitecing_la_SOURCES = \
10 sitecing_parser.ll sitecing_enflesher.ll \
11 sitecing_interface_cgi.cc \
12 acomponent.cc \
13 cgi_component.cc \
14 component_so.cc \
15 file_factory.cc component_factory.cc \
16 sitespace.cc \
17 configuration.cc \
18 util.cc sitecing_util.cc \
19 scoreboard.cc \
20 process_manager.cc
21libsitecing_la_LDFLAGS = \
22 -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 @@
1#ifdef USE_PCH
2 #include "pch.h"
3#else
4 #include <cstdarg>
5 #include <fstream>
6 #include <konforka/exception.h>
7 using namespace std;
8 #include "sitecing/acomponent.h"
9#endif
10
11namespace sitecing {
12
13 acomponent::acomponent()
14 : __SCIF(NULL) {
15 }
16 acomponent::~acomponent() {
17 }
18
19 void acomponent::__set_interface(sitecing_interface* scif) {
20 sitecing_interface *o = __SCIF;
21 __SCIF = scif;
22 if(o!=scif) {
23 __on_change_interface(o);
24 __do_imports();
25 __on_imports();
26 }
27 }
28
29 void acomponent::__on_change_interface(sitecing_interface *oscif) { }
30 void acomponent::__do_imports() { }
31 void acomponent::__on_imports() { }
32
33 void acomponent::run(int _magic,...) {
34 va_list va;
35 va_start(va,_magic);
36 main(_magic,va);
37 va_end(va);
38 }
39
40
41 void acomponent::pass_file_through(const char *fn) {
42 ifstream ifs(fn,ios::in|ios::binary);
43 if(!ifs)
44 throw konforka::exception(CODEPOINT,"failed to open file");
45 (*(__SCIF->out)) << ifs.rdbuf();
46 }
47}
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 @@
1#ifdef USE_PCH
2 #include "pch.h"
3#else
4 #include "sitecing/cgi_component.h"
5#endif
6
7namespace sitecing {
8
9 cgi_component::cgi_component()
10 : __CGI(NULL) {
11 }
12 cgi_component::~cgi_component() {
13 }
14
15 void cgi_component::__set_interface(sitecing_interface* scif) {
16 acomponent::__set_interface(scif);
17 kingate::cgi_gateway *oc = __CGI;
18 __CGI = __SCIF?__SCIF->cgigw:NULL;
19 if(__CGI!=oc)
20 __on_change_CGI(oc);
21 }
22 void cgi_component::__on_change_interface(sitecing_interface *o) {
23 acomponent::__on_change_interface(o); // But it's a no-op
24 // TODO: do something about runtime type check, maybe?
25 __SCIF = (sitecing_interface_cgi*)acomponent::__SCIF;
26 }
27 void cgi_component::__on_change_CGI(kingate::cgi_gateway *o) { }
28 void cgi_component::__on_imports() { }
29
30}
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 @@
1#ifdef USE_PCH
2 #include "pch.h"
3#else
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include <sys/wait.h>
8 #include <fcntl.h>
9 #include <iostream>
10 #include <fstream>
11 #include <stdexcept>
12 #include <vector>
13 using namespace std;
14 #include "sitecing/component_factory.h"
15 #include "sitecing/sitecing_util.h"
16 #include "sitecing/sitecing_parser.h"
17 #include "sitecing/sitecing_exception.h"
18#endif
19
20namespace sitecing {
21
22 static const char *pp_targets[] = { ".cc", ".h", ".imports", ".classname", ".baseclassname", ".ancestors" };
23
24 component_factory::component_factory(configuration& c)
25 : config(c),
26 root_source(normalize_path(c.root_source,strip_trailing_slash)+'/'),
27 root_intermediate(normalize_path(c.root_intermediate,strip_trailing_slash)+'/'),
28 root_so(normalize_path(c.root_so,strip_trailing_slash)+'/') {
29 }
30
31 void component_factory::get_dependencies(const string& dst,file_list_t& deps) {
32 deps.clear();
33 string dp = normalize_path(dst,strip_trailing_slash);
34 // source documents
35 try { // XXX: or just compare it off?
36 string noro = strip_prefix(dp,root_source);
37 return;
38 }catch(utility_no_affix& una) {
39 }
40 // .so binaries
41 try {
42 string noso = strip_suffix(dp,".so");
43 string noro = strip_prefix(noso,root_so);
44 deps.push_back(root_intermediate+noro+".cc");
45 config_options *co_cpp_deps = config.lookup_config(noro,config_options::flag_cpp_deps);
46 if( (!co_cpp_deps) || co_cpp_deps->cpp_deps) {
47 ifstream df((root_intermediate+noro+".d").c_str(),ios::in);
48 if(df.good()) {
49 string str;
50 while(!df.eof()) {
51 df >> str;
52 if(str.find_first_of("\\:")==string::npos)
53 deps.push_back(combine_path(config.root_source+noro,str));
54 }
55 }
56 }
57 config_options *co_so_deps = config.lookup_config(noro,config_options::flag_so_deps);
58 if(co_so_deps) {
59 for(list<string>::const_iterator i=co_so_deps->so_deps.begin();i!=co_so_deps->so_deps.end();++i)
60 deps.push_back(*i);
61 }
62 return;
63 }catch(utility_no_prefix& unp) {
64 throw konforka::exception(CODEPOINT,"component is outside of component root");
65 }catch(utility_no_suffix& uns) {
66 }
67 // preprocessor targets
68 for(int ppt=0;ppt<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) {
69 try {
70 string nos = strip_suffix(dp,pp_targets[ppt]);
71 string noro = strip_prefix(nos,root_intermediate);
72 deps.push_back(root_source+noro);
73 ifstream imports((root_intermediate+noro+".imports").c_str(),ios::in);
74 if(imports.good()) {
75 string str;
76 while(!imports.eof()) {
77 imports >> str;
78 if(!str.empty())
79 deps.push_back(root_intermediate+str+".classname");
80 }
81 }
82 ifstream ancestors((root_intermediate+noro+".ancestors").c_str(),ios::in);
83 if(ancestors.good()) {
84 string str;
85 while(!ancestors.eof()) {
86 ancestors >> str;
87 if(!str.empty())
88 deps.push_back(root_intermediate+str+".classname");
89 }
90 }
91 config_options *co_intermediate_deps = config.lookup_config(noro,config_options::flag_intermediate_deps);
92 if(co_intermediate_deps) {
93 for(list<string>::const_iterator i=co_intermediate_deps->intermediate_deps.begin();i!=co_intermediate_deps->intermediate_deps.end();++i)
94 deps.push_back(*i);
95 }
96 return;
97 }catch(utility_no_affix& una) {
98 // do nothing. must be a cpp dependency.
99 }
100 }
101 }
102
103 bool component_factory::is_uptodate(const string& dst,file_list_t *deps) {
104 string dp = normalize_path(dst,strip_trailing_slash);
105 // XXX: or just compare it off, instead of throwing things around.
106 try {
107 strip_prefix(dp,root_intermediate);
108 return file_factory::is_uptodate(dst,deps);
109 }catch(utility_no_prefix& unp) {
110 }
111 try {
112 strip_prefix(dp,root_so);
113 return file_factory::is_uptodate(dst,deps);
114 }catch(utility_no_prefix& unp) {
115 }
116 return true;
117 }
118
119 void component_factory::build(const string& dst) {
120 string dp = normalize_path(dst,strip_trailing_slash);
121 // sources
122 try {
123 string noro = strip_prefix(dp,root_source);
124 // building the sources is left up to developer
125 return;
126 }catch(utility_no_prefix& unp) {
127 }
128 // .so files
129 try {
130 string noso = strip_suffix(dp,".so");
131 string noro = strip_prefix(noso,root_so);
132 string cc = root_intermediate+noro+".cc";
133 if(access(cc.c_str(),R_OK))
134 throw konforka::exception(CODEPOINT,string("can't access preprocessed component code (")+cc+")");
135 make_path(dir_name(root_so+noro),0755);
136 string pwd = dir_name(root_source+noro);
137 auto_chdir dir_changer(pwd);
138 file_lock lock_source(root_intermediate+noro+".lock");
139 file_lock lock_so(root_so+noro+".so.lock");
140 int stdO = open((root_intermediate+noro+".stdout").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664);
141 if(stdO<0)
142 throw konforka::exception(CODEPOINT,"failed to open/create compiler stdout");
143 int stdE = open((root_intermediate+noro+".stderr").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664);
144 if(stdE<0) {
145 close(stdO);
146 throw konforka::exception(CODEPOINT,"failed to open/create compiler's stderr");
147 }
148 list<string> args;
149 config_options *co_cpp_flags = config.lookup_config(noro,config_options::flag_cpp_flags);
150 if(co_cpp_flags) {
151 args.insert(args.end(),co_cpp_flags->cpp_flags.begin(),co_cpp_flags->cpp_flags.end());
152 }
153 config_options *co_ld_flags = config.lookup_config(noro,config_options::flag_ld_flags);
154 if(co_ld_flags) {
155 args.insert(args.end(),co_ld_flags->ld_flags.begin(),co_ld_flags->ld_flags.end());
156 }
157 // TODO: maybe move it to separare config option like CoreCPPFLags?
158 args.push_back("-I"+root_intermediate);
159 args.push_back("-I"+root_source);
160 args.push_back("-MD"); args.push_back("-MF"); args.push_back(root_intermediate+noro+".d");
161 args.push_back("-shared");
162 args.push_back("-o"); args.push_back(dp);
163 args.push_back(cc);
164 file_list_t ancestors;
165 get_ancestors(noro,ancestors);
166 for(file_list_t::const_iterator i=ancestors.begin();i!=ancestors.end();++i) {
167 string aso=root_so+*i+".so";
168 make(aso);
169 args.push_back(aso);
170 }
171 // TODO: "g++" configurable
172 int rv = execute("g++",args,stdO,stdE);
173 if(! (WIFEXITED(rv) && !WEXITSTATUS(rv)) )
174 throw compile_error(CODEPOINT,"failed to compile component",noro);
175 return;
176 }catch(utility_no_prefix& unp) {
177 throw konforka::exception(CODEPOINT,"component is outside of component root");
178 }catch(utility_no_suffix& uns) {
179 }
180 // preprocessor targets
181 for(int ppt=0;ppt<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) {
182 try {
183 string nos = strip_suffix(dp,pp_targets[ppt]);
184 string noro = strip_prefix(nos,root_intermediate);
185 string src = root_source+noro;
186 if(access(src.c_str(),R_OK))
187 throw konforka::exception(CODEPOINT,string("can't access component source (")+src+")");
188 make_path(dir_name(root_intermediate+noro),0755);
189 file_lock lock(root_intermediate+noro+".lock");
190 sitecing_parser parser(*this);
191 config_options *co_skeleton = config.lookup_config(noro,config_options::flag_skeleton);
192 if(co_skeleton)
193 parser.skeleton = co_skeleton->skeleton;
194 static const char *id_chars = "abcdefghijklmnopqrstuvwxyz0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ";
195 parser.class_name = normalize_path(noro,strip_leading_slash|strip_trailing_slash);
196 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)) {
197 string::size_type lc = parser.class_name.find_first_of(id_chars,illc);
198 int n = ((lc==string::npos)?parser.class_name.length():lc)-illc;
199 parser.class_name.replace(illc,n,n,'_');
200 }
201 parser.class_name = "_SCC_"+parser.class_name;
202 parser.output_basename = nos;
203 parser.component_basename = noro;
204 try {
205 parser.preprocess(src);
206 }catch(preprocessor_error& pe) {
207 pe.component_name = noro;
208 pe.see(CODEPOINT);
209 throw;
210 }
211 return;
212 }catch(utility_no_affix& una) {
213 // must be a crap from .d file
214 }
215 }
216 cerr << "ignoring build request for " << dp << endl;
217 }
218
219 int component_factory::execute(const string& cmd, const list<string>& args,int stdo,int stde) {
220 // XXX: is it right that we do stdio/stderr tricks outside of the function?
221 cerr << "executing: " << cmd;
222 vector<const char*> argv(args.size()+2);
223 argv[0]=cmd.c_str();
224 int an = 1;
225 for(list<string>::const_iterator i=args.begin();i!=args.end();i++) {
226 cerr << " " << *i ;
227 argv[an++] = i->c_str();
228 }
229 cerr << endl;
230 argv[an++]=NULL;
231 pid_t pid = vfork();
232 if(pid==-1) {
233 close(stdo); close(stde);
234 throw konforka::exception(CODEPOINT,"failed to vfork()");
235 }
236 if(!pid) {
237 // child
238 if(dup2(stdo,1)!=1)
239 _exit(-1);
240 if(dup2(stde,2)!=2)
241 _exit(-1);
242 close(0);
243 execvp(cmd.c_str(),(char**)&argv.front());
244 _exit(-1);
245 }
246 // parent
247 close(stdo); close(stde);
248 int rv;
249 if(waitpid(pid,&rv,0)<0)
250 throw konforka::exception(CODEPOINT,"failed to waitpid()");
251 return rv;
252 }
253
254 string component_factory::get_classname(const string& component) {
255 string cn = root_intermediate+normalize_path(component,strip_trailing_slash|strip_leading_slash)+".classname";
256 make(cn);
257 ifstream ifs(cn.c_str());
258 if(!ifs.good())
259 throw konforka::exception(CODEPOINT,"failed to access component .classname");
260 ifs >> cn;
261 return cn;
262 }
263
264 void component_factory::get_ancestors(const string& component,file_list_t& rv) {
265 string cn = root_intermediate+normalize_path(component,strip_trailing_slash|strip_leading_slash)+".ancestors";
266 make(cn);
267 ifstream ifs(cn.c_str());
268 if(!ifs.good())
269 throw konforka::exception(CODEPOINT,"filed to access component .ancestors");
270 rv.clear();
271 while(!ifs.eof()) {
272 string a;
273 ifs >> a;
274 if(!a.empty())
275 rv.push_back(a);
276 }
277 }
278
279}
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 @@
1#ifdef USE_PCH
2 #include "pch.h"
3#else
4 #include <unistd.h>
5 #include <dlfcn.h>
6 #include <iostream>
7 #include <cassert>
8 #include <stdexcept>
9 using namespace std;
10 #include "sitecing/component_so.h"
11 #include "sitecing/sitecing_util.h"
12#endif
13
14namespace sitecing {
15
16 /*
17 * component_so
18 */
19
20 component_so::component_so(const string& soname)
21 : dl(NULL), sofile(soname) {
22 if(stat(sofile.c_str(),&stso))
23 throw konforka::exception(CODEPOINT,"failed to stat() shared object");
24 file_lock lock(sofile+".lock");
25 dl = dlopen(sofile.c_str(),RTLD_LAZY);
26 lock.unlock();
27 if(!dl)
28 throw konforka::exception(CODEPOINT,"failed to dlopen: "+string(dlerror()));
29 egg = (egg_t)dlsym(dl,"_egg");
30 if(!egg)
31 throw konforka::exception(CODEPOINT,"failed to dlsym: "+string(dlerror()));
32 }
33 component_so::~component_so() {
34 for(free_chickens_t::iterator i=chickens_free.begin();i!=chickens_free.end();i++)
35 delete *i;
36 chickens_free.clear();
37 if(!chickens_used.empty())
38 throw konforka::exception(CODEPOINT,"attempt to destroy the component in use");
39 dlclose(dl);
40 }
41
42 bool component_so::is_uptodate() const {
43 struct stat st;
44 if(stat(sofile.c_str(),&st))
45 throw konforka::exception(CODEPOINT,"failed to stat() shared object");
46 return stso.st_mtime==st.st_mtime;
47 }
48
49 acomponent* component_so::allocate_chicken() {
50 acomponent *rv;
51 if(!chickens_free.empty()) {
52 rv = chickens_free.front();
53 chickens_free.pop_front();
54 }else{
55 rv = (*egg)();
56 }
57 assert(chickens_used.find(rv)==chickens_used.end());
58 chickens_used[rv]=1;
59 return rv;
60 }
61
62 void component_so::allocate_chicken(acomponent* ac) {
63 used_chickens_t::iterator i = chickens_used.find(ac);
64 if(i!=chickens_used.end()) {
65 i->second++;
66 }else{
67 free_chickens_t::iterator i;
68 for(i=chickens_free.begin();*i!=ac && i!=chickens_free.end();i++);
69 if(i==chickens_free.end())
70 throw konforka::exception(CODEPOINT,"hens rarely adopt chickens");
71 chickens_free.erase(i);
72 chickens_used[ac]=1;
73 }
74 }
75
76 void component_so::deallocate_chicken(acomponent* ac) {
77 used_chickens_t::iterator i = chickens_used.find(ac);
78 if(i==chickens_used.end())
79 throw konforka::exception(CODEPOINT,"you can't deallocate what is not allocated");
80 i->second--;
81 if(i->second>0)
82 return;
83 chickens_used.erase(i);
84 chickens_free.push_front(ac);
85 }
86
87 /*
88 * so_component
89 */
90
91 so_component::so_component(component_so *h,sitecing_interface *scif)
92 : hen(h), ac(NULL) {
93 if(!hen)
94 throw konforka::exception(CODEPOINT,"can't get an egg from the null-hen");
95 ac = hen->allocate_chicken();
96 ac->__set_interface(scif);
97 }
98
99 void so_component::attach(component_so *h,acomponent *a) {
100 detach(); hen = h; ac = a;
101 if(!ac)
102 throw konforka::exception(CODEPOINT,"trying to clone null-chicken");
103 if(!hen)
104 throw konforka::exception(CODEPOINT,"trying to clone orphan chicken");
105 hen->allocate_chicken(ac);
106 }
107 void so_component::detach() {
108 if(hen && ac)
109 hen->deallocate_chicken(ac);
110 }
111
112}
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 @@
1#ifdef USE_PCH
2 #include "pch.h"
3#else
4 #include <unistd.h>
5 #include <fnmatch.h>
6 #include <cassert>
7 #include <stdexcept>
8 using namespace std;
9 #include <dotconf.h>
10 #include "sitecing/configuration.h"
11 #include "sitecing/sitecing_util.h"
12 #include "sitecing/scoreboard.h"
13#endif
14
15namespace sitecing {
16
17 configuration::configuration()
18 : flags(0), autobuild(false) { }
19 configuration::configuration(const string& cfile,bool ab)
20 : flags(0), autobuild(ab) {
21 parse(cfile);
22 }
23
24 enum dc_ctx {
25 DCC_ROOT = 1,
26 DCC_PATH = 2,
27 DCC_SCRC = 4
28 };
29 struct dc_context {
30 dc_ctx ctx;
31 configuration* cf;
32 list<config_options*> co;
33 };
34
35 static DOTCONF_CB(dco_root_source) { dc_context *dcc = (dc_context*)ctx;
36 dcc->cf->root_source = cmd->data.str;
37 dcc->cf->flags |= configuration::flag_root_source;
38 return NULL;
39 }
40 static DOTCONF_CB(dco_root_intermediate) { dc_context *dcc = (dc_context*)ctx;
41 dcc->cf->root_intermediate = cmd->data.str;
42 dcc->cf->flags |= configuration::flag_root_intermediate;
43 return NULL;
44 }
45 static DOTCONF_CB(dco_root_so) { dc_context *dcc = (dc_context*)ctx;
46 dcc->cf->root_so = cmd->data.str;
47 dcc->cf->flags |= configuration::flag_root_so;
48 return NULL;
49 }
50 static DOTCONF_CB(dco_listen_socket) { dc_context *dcc = (dc_context*)ctx;
51 dcc->cf->listen_socket = cmd->data.str;
52 dcc->cf->flags |= configuration::flag_listen_socket;
53 return NULL;
54 }
55 static DOTCONF_CB(dco_rc_file_name) { dc_context *dcc = (dc_context*)ctx;
56 dcc->cf->rc_file_name = cmd->data.str;
57 dcc->cf->flags |= configuration::flag_rc_file_name;
58 return NULL;
59 }
60 static DOTCONF_CB(dco_min_children) { dc_context *dcc = (dc_context*)ctx;
61 if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
62 return "MinChildren is too big";
63 dcc->cf->min_children = cmd->data.value;
64 dcc->cf->flags |= configuration::flag_min_children;
65 return NULL;
66 }
67 static DOTCONF_CB(dco_max_children) { dc_context *dcc = (dc_context*)ctx;
68 if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
69 return "MaxChildren is too big";
70 dcc->cf->max_children = cmd->data.value;
71 dcc->cf->flags |= configuration::flag_max_children;
72 return NULL;
73 }
74 static DOTCONF_CB(dco_min_spare_children) { dc_context *dcc = (dc_context*)ctx;
75 if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
76 return "MinSpareChildren is too big";
77 dcc->cf->min_spare_children = cmd->data.value;
78 dcc->cf->flags |= configuration::flag_min_spare_children;
79 return NULL;
80 }
81 static DOTCONF_CB(dco_max_spare_children) { dc_context *dcc = (dc_context*)ctx;
82 if(cmd->data.value>=MAX_SITECING_SCOREBOARD_SLOTS)
83 return "MaxSpareChildren is too big";
84 dcc->cf->max_spare_children = cmd->data.value;
85 dcc->cf->flags |= configuration::flag_max_spare_children;
86 return NULL;
87 }
88 static DOTCONF_CB(dco_requests_per_child) { dc_context *dcc = (dc_context*)ctx;
89 dcc->cf->requests_per_child = cmd->data.value;
90 dcc->cf->flags |= configuration::flag_requests_per_child;
91 return NULL;
92 }
93 static DOTCONF_CB(dco_multi_process) { dc_context *dcc = (dc_context*)ctx;
94 dcc->cf->multi_process = cmd->data.value;
95 dcc->cf->flags |= configuration::flag_multi_process;
96 return NULL;
97 }
98 static DOTCONF_CB(dco_user) { dc_context *dcc = (dc_context*)ctx;
99 dcc->cf->user = cmd->data.str;
100 dcc->cf->flags |= configuration::flag_user;
101 return NULL;
102 }
103 static DOTCONF_CB(dco_group) { dc_context *dcc = (dc_context*)ctx;
104 dcc->cf->group = cmd->data.str;
105 dcc->cf->flags |= configuration::flag_group;
106 return NULL;
107 }
108 static DOTCONF_CB(dco_chroot) { dc_context *dcc = (dc_context*)ctx;
109 dcc->cf->chroot = cmd->data.str;
110 dcc->cf->flags |= configuration::flag_chroot;
111 return NULL;
112 }
113 static DOTCONF_CB(dco_pid_file) { dc_context *dcc = (dc_context*)ctx;
114 dcc->cf->pid_file = cmd->data.str;
115 dcc->cf->flags |= configuration::flag_pid_file;
116 return NULL;
117 }
118 static DOTCONF_CB(dco_daemonize) { dc_context *dcc = (dc_context*) ctx;
119 dcc->cf->daemonize = cmd->data.value;
120 dcc->cf->flags |= configuration::flag_daemonize;
121 return NULL;
122 }
123
124 static DOTCONF_CB(dco_path) { dc_context *dcc = (dc_context*)ctx;
125 string path = cmd->data.str;
126 if(path[path.length()-1]=='>')
127 path.erase(path.length()-1);
128 // TODO: normalize path
129 dcc->co.push_front(&(dcc->cf->specs[path]));
130 dcc->ctx = DCC_PATH; // TODO: stack it, instead
131 return NULL;
132 }
133 static DOTCONF_CB(dco__path) { dc_context *dcc = (dc_context*)ctx;
134 dcc->co.pop_front();
135 assert(dcc->co.size());
136 dcc->ctx = DCC_ROOT; // TODO: stack it, instead
137 return NULL;
138 }
139
140 static DOTCONF_CB(dco_skeleton) { dc_context *dcc = (dc_context*)ctx;
141 dcc->co.front()->skeleton = cmd->data.str;
142 dcc->co.front()->flags |= config_options::flag_skeleton;
143 return NULL;
144 }
145 static DOTCONF_CB(dco_cpp_flags) { dc_context *dcc = (dc_context*)ctx;
146 for(char **arg=cmd->data.list;*arg;arg++)
147 dcc->co.front()->cpp_flags.push_back(*arg);
148 dcc->co.front()->flags |= config_options::flag_cpp_flags;
149 return NULL;
150 }
151 static DOTCONF_CB(dco_ld_flags) { dc_context *dcc = (dc_context*)ctx;
152 for(char **arg=cmd->data.list;*arg;arg++)
153 dcc->co.front()->ld_flags.push_back(*arg);
154 dcc->co.front()->flags |= config_options::flag_ld_flags;
155 return NULL;
156 }
157 static DOTCONF_CB(dco_intermediate_deps) { dc_context *dcc = (dc_context*) ctx;
158 for(char **arg=cmd->data.list;*arg;arg++)
159 dcc->co.front()->intermediate_deps.push_back(*arg);
160 dcc->co.front()->flags |= config_options::flag_intermediate_deps;
161 return NULL;
162 }
163 static DOTCONF_CB(dco_so_deps) { dc_context *dcc = (dc_context*) ctx;
164 for(char **arg=cmd->data.list;*arg;arg++)
165 dcc->co.front()->so_deps.push_back(*arg);
166 dcc->co.front()->flags |= config_options::flag_so_deps;
167 return NULL;
168 }
169 static DOTCONF_CB(dco_build) { dc_context *dcc = (dc_context*)ctx;
170 dcc->co.front()->build = cmd->data.value;
171 dcc->co.front()->flags |= config_options::flag_build;
172 return NULL;
173 }
174 static DOTCONF_CB(dco_cpp_deps) { dc_context *dcc = (dc_context*)ctx;
175 dcc->co.front()->cpp_deps = cmd->data.value;
176 dcc->co.front()->flags |= config_options::flag_cpp_deps;
177 return NULL;
178 }
179 static DOTCONF_CB(dco_exception_handler) { dc_context *dcc = (dc_context*)ctx;
180 dcc->co.front()->exception_handler = cmd->data.str;
181 dcc->co.front()->flags |= config_options::flag_exception_handler;
182 return NULL;
183 }
184 static DOTCONF_CB(dco_http_status_handler) { dc_context *dcc = (dc_context*)ctx;
185 if(cmd->arg_count!=2)
186 return "Invalid number of arguments";
187 dcc->co.front()->http_status_handlers[cmd->data.list[0]] = cmd->data.list[1];
188 dcc->co.front()->flags |= config_options::flag_http_status_handlers;
189 return NULL;
190 }
191 static DOTCONF_CB(dco_action) { dc_context *dcc = (dc_context*)ctx;
192 if(cmd->arg_count<2)
193 return "Invalid number of arguments";
194 try {
195 char **arg=cmd->data.list;
196 dcc->co.front()->action_handlers.push_back(config_options::action_handler_t(arg[0],arg[1]));
197 for(arg+=2;*arg;arg++)
198 dcc->co.front()->action_handlers.back().args.push_back(*arg);
199 dcc->co.front()->flags |= config_options::flag_action_handlers;
200 }catch(exception& e) {
201 return "Error processing Action directive"; // XXX: could be done better
202 }
203 return NULL;
204 }
205 static DOTCONF_CB(dco_auto_build_files) { dc_context *dcc = (dc_context*)ctx;
206 if(!( dcc->cf && dcc->cf->autobuild))
207 return NULL;
208 for(char **arg=cmd->data.list;*arg;arg++)
209 dcc->co.front()->auto_build_files.push_back(*arg);
210 dcc->co.front()->flags |= config_options::flag_auto_build_files;
211 return NULL;
212 }
213
214 static const configoption_t dc_options[] = {
215 { "RootSource", ARG_STR, dco_root_source, NULL, DCC_ROOT },
216 { "RootIntermediate", ARG_STR, dco_root_intermediate, NULL, DCC_ROOT },
217 { "RootSO", ARG_STR, dco_root_so, NULL, DCC_ROOT },
218 { "ListenSocket", ARG_STR, dco_listen_socket, NULL, DCC_ROOT },
219 { "RCFileName", ARG_STR, dco_rc_file_name, NULL, DCC_ROOT },
220 { "MinChildren", ARG_INT, dco_min_children, NULL, DCC_ROOT },
221 { "MaxChildren", ARG_INT, dco_max_children, NULL, DCC_ROOT },
222 { "MinSpareChildren", ARG_INT, dco_min_spare_children, NULL, DCC_ROOT },
223 { "MaxSpareChildren", ARG_INT, dco_max_spare_children, NULL, DCC_ROOT },
224 { "RequestsPerChild", ARG_INT, dco_requests_per_child, NULL, DCC_ROOT },
225 { "MultiProcess", ARG_TOGGLE, dco_multi_process, NULL, DCC_ROOT },
226 { "User", ARG_STR, dco_user, NULL, DCC_ROOT },
227 { "Group", ARG_STR, dco_group, NULL, DCC_ROOT },
228 { "Chroot", ARG_STR, dco_chroot, NULL, DCC_ROOT },
229 { "PidFile", ARG_STR, dco_pid_file, NULL, DCC_ROOT },
230 { "Daemonize", ARG_TOGGLE, dco_daemonize, NULL, DCC_ROOT },
231 { "<Path", ARG_STR, dco_path, NULL, DCC_ROOT },
232 { "Skeleton", ARG_STR, dco_skeleton, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
233 { "CPPFLAGS", ARG_LIST, dco_cpp_flags, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
234 { "LDFLAGS", ARG_LIST, dco_ld_flags, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
235 { "Build", ARG_TOGGLE, dco_build, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
236 { "CPPDeps", ARG_TOGGLE, dco_cpp_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
237 { "ExceptionHandler", ARG_STR, dco_exception_handler, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
238 { "HTTPStatusHandler", ARG_LIST, dco_http_status_handler, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
239 { "IntermediateDeps", ARG_LIST, dco_intermediate_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
240 { "SODeps", ARG_LIST, dco_so_deps, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
241 { "Action", ARG_LIST, dco_action, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
242 { "AutoBuildFiles", ARG_LIST, dco_auto_build_files, NULL, DCC_ROOT|DCC_PATH|DCC_SCRC },
243 { "</Path>", ARG_NONE, dco__path, NULL, DCC_PATH },
244 LAST_OPTION
245 };
246
247 static const char *dc_context_checker(command_t *cmd,unsigned long mask) {
248 dc_context *dcc = (dc_context*)cmd->context;
249 if( (mask==CTX_ALL) || ((mask&dcc->ctx)==dcc->ctx) )
250 return NULL;
251 return "misplaced option";
252 }
253 static FUNC_ERRORHANDLER(dc_error_handler) {
254 throw konforka::exception(CODEPOINT,string("error parsing config file: ")+msg);
255 }
256
257 bool loaded_options::is_valid() {
258 struct stat nst;
259 if(stat(source_file.c_str(),&nst))
260 return false;
261 if(st.st_mtime!=nst.st_mtime)
262 return false;
263 return true;
264 }
265
266 loaded_options *configuration::lookup_loaded_options(const string& target) {
267 // we assume 'target' is a directory with trailing slash appended
268 string scrc = root_source+target;
269 if(flags&flag_rc_file_name)
270 scrc += rc_file_name;
271 else
272 scrc += ".scrc";
273 // TODO: normalize me, anyway.
274 if(access(scrc.c_str(),R_OK))
275 return 0; // TODO FIXME: this approach leaves already loaded .scrcs around in case of removal
276 loaded_specs_t::iterator i = loaded_specs.find(target);
277 if(i==loaded_specs.end() || !i->second.is_valid()) {
278 if(i!=loaded_specs.end())
279 loaded_specs.erase(i);
280 pair<loaded_specs_t::iterator,bool> ii = loaded_specs.insert(loaded_specs_t::value_type(target,loaded_options()));
281 assert(ii.first!=loaded_specs.end());
282 ii.first->second.parse(this,scrc);
283 i = ii.first;
284 }
285 assert(i!=loaded_specs.end());
286 return &(i->second);
287 }
288
289 config_options::action_handler_t *config_options::lookup_action_handler(const string& target) {
290 for(action_handlers_t::iterator i=action_handlers.begin();i!=action_handlers.end();++i) {
291 if(i->regex.search(target))
292 return &*i;
293 }
294 return NULL;
295 }
296
297 string config_options::lookup_http_status_handler(const string& status) {
298 http_status_handlers_t::const_iterator i = http_status_handlers.find(status);
299 string rv;
300 if(i!=http_status_handlers.end())
301 rv = i->second;
302 return rv;
303 }
304
305 string configuration::lookup_http_status_handler(const string& target,const string& status) {
306 string t = "/";
307 t += normalize_path(target,strip_leading_slash);
308 string rv;
309 for(;;) {
310 if(t[t.length()-1]=='/') {
311 loaded_options* lo = lookup_loaded_options(t);
312 if( lo && (lo->flags&config_options::flag_http_status_handlers) ) {
313 rv = lo->lookup_http_status_handler(status);
314 if(!rv.empty())
315 return rv;
316 }
317 }
318 specs_t::iterator i = specs.find(t);
319 if( i!=specs.end() && (i->second.flags&&config_options::flag_http_status_handlers) ) {
320 rv = i->second.lookup_http_status_handler(status);
321 if(!rv.empty())
322 return rv;
323 }
324 if(t.empty())
325 return rv;
326 string::size_type sl=t.rfind('/');
327 if(sl==string::npos) {
328 t.erase();
329 }else{
330 if(sl==(t.length()-1))
331 t.erase(sl);
332 else
333 t.erase(sl+1);
334 }
335 }
336 }
337
338 config_options::action_handler_t *configuration::lookup_action_handler(const string& target) {
339 string t = "/";
340 t += normalize_path(target,strip_leading_slash);
341 for(;;) {
342 if(t[t.length()-1]=='/') {
343 loaded_options* lo = lookup_loaded_options(t);
344 if( lo && (lo->flags&config_options::flag_action_handlers) ) {
345 config_options::action_handler_t *rv = lo->lookup_action_handler(target);
346 if(rv)
347 return rv;
348 }
349 }
350 specs_t::iterator i = specs.find(t);
351 if( i!=specs.end() && (i->second.flags&&config_options::flag_action_handlers) ) {
352 config_options::action_handler_t *rv = i->second.lookup_action_handler(target);
353 if(rv)
354 return rv;
355 }
356 if(t.empty())
357 return NULL;
358 string::size_type sl=t.rfind('/');
359 if(sl==string::npos) {
360 t.erase();
361 }else{
362 if(sl==(t.length()-1))
363 t.erase(sl);
364 else
365 t.erase(sl+1);
366 }
367 }
368 }
369
370 config_options* configuration::lookup_config(const string& target,int flag) {
371 string t = "/"; // always assume leading slash
372 t += normalize_path(target,strip_leading_slash);
373 // XXX: reconsider precedence
374 for(;;) {
375 if(t[t.length()-1]=='/') {
376 loaded_options* lo = lookup_loaded_options(t);
377 if( lo && (lo->flags&flag)==flag )
378 return lo;
379 }
380 specs_t::iterator i = specs.find(t);
381 if( i!=specs.end() && (i->second.flags&flag)==flag )
382 return &(i->second);
383 if(t.empty())
384 return NULL;
385 string::size_type sl=t.rfind('/');
386 if(sl==string::npos) {
387 t.erase();
388 }else{
389 if(sl==(t.length()-1))
390 t.erase(sl);
391 else
392 t.erase(sl+1);
393 }
394 }
395 }
396
397 bool config_options::match_autobuild_files(const char *fn,bool &rv) {
398 for(list<string>::reverse_iterator i=auto_build_files.rbegin();i!=auto_build_files.rend();++i) {
399 const char *pat = i->c_str();
400 bool plus = true;
401 if((*pat)=='+')
402 pat++;
403 else if((*pat)=='-') {
404 plus = false;
405 pat++;
406 }
407 if(!fnmatch(pat,fn,FNM_NOESCAPE|FNM_PATHNAME|FNM_PERIOD)) {
408 rv = plus;
409 return true;
410 }
411 }
412 return false;
413 }
414
415 bool configuration::match_autobuild_files(const string& target,const char *fn) {
416 string t = "/";
417 t += normalize_path(target,strip_leading_slash|strip_trailing_slash);
418 t += "/";
419 bool rv = false;
420 for(;;) {
421 if(t[t.length()-1]=='/') {
422 loaded_options* lo = lookup_loaded_options(t);
423 if(lo && (lo->flags&config_options::flag_auto_build_files) && lo->match_autobuild_files(fn,rv) )
424 return rv;
425 }
426 specs_t::iterator i = specs.find(t);
427 if( i!=specs.end() && (i->second.flags&config_options::flag_auto_build_files) && i->second.match_autobuild_files(fn,rv) )
428 return rv;
429 if(t.empty())
430 return rv;
431 string::size_type sl=t.rfind('/');
432 if(sl==string::npos) {
433 t.erase();
434 }else{
435 if(sl==(t.length()-1))
436 t.erase(sl);
437 else
438 t.erase(sl+1);
439 }
440 }
441 }
442
443 void configuration::parse(const string& cfile) {
444 struct dc_context dcc;
445 dcc.cf = this;
446 dcc.ctx = DCC_ROOT;
447 dcc.co.push_front(&root_options());
448 configfile_t *cf = dotconf_create((char*)cfile.c_str(),dc_options,(context_t*)&dcc,CASE_INSENSITIVE);
449 if(!cf)
450 throw konforka::exception(CODEPOINT,"failed to dotconf_create()");
451 cf->errorhandler = (dotconf_errorhandler_t) dc_error_handler;
452 cf->contextchecker = (dotconf_contextchecker_t) dc_context_checker;
453 if(!dotconf_command_loop(cf))
454 throw konforka::exception(CODEPOINT,"failed to dotconf_command_loop()");
455 dotconf_cleanup(cf);
456 }
457
458 void loaded_options::parse(configuration *config,const string& cfile) {
459 struct dc_context dcc;
460 dcc.cf = config;
461 dcc.ctx = DCC_SCRC;
462 dcc.co.push_front(this);
463 configfile_t *cf = dotconf_create((char*)cfile.c_str(),dc_options,(context_t*)&dcc,CASE_INSENSITIVE);
464 if(!cf)
465 throw konforka::exception(CODEPOINT,"failed to dotconf_create()");
466 cf->errorhandler = (dotconf_errorhandler_t) dc_error_handler;
467 cf->contextchecker = (dotconf_contextchecker_t) dc_context_checker;
468 if(!dotconf_command_loop(cf))
469 throw konforka::exception(CODEPOINT,"failed to dotconf_command_loop()");
470 dotconf_cleanup(cf);
471 source_file = cfile;
472 stat(cfile.c_str(),&st); // TODO: handle errors?
473 }
474}
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 @@
1#ifdef USE_PCH
2 #include "pch.h"
3#else
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include <konforka/exception.h>
8 using namespace std;
9 #include "sitecing/file_factory.h"
10#endif
11
12namespace sitecing {
13
14 bool file_factory::is_uptodate(const string& dst,file_list_t* deps) {
15 file_list_t deplist;
16 file_list_t *fl = deps?deps:&deplist;
17 get_dependencies(dst,*fl);
18 struct stat stdst;
19 if(stat(dst.c_str(),&stdst))
20 return false;
21 for(file_list_t::const_iterator i=fl->begin();i!=fl->end();i++) {
22 struct stat stdep;
23 if(stat(i->c_str(),&stdep))
24 return false;
25 if(stdst.st_mtime<stdep.st_mtime)
26 return false;
27 if(!is_uptodate(*i))
28 return false;
29 }
30 return true;
31 }
32
33 void file_factory::make(const string& dst) {
34 try {
35 depth++;
36 if(depth>25)
37 throw konforka::exception(CODEPOINT,"recursed too deeply.");
38 file_list_t deps;
39 if(!is_uptodate(dst,&deps)) {
40 for(file_list_t::const_iterator i=deps.begin();i!=deps.end();i++)
41 make(*i);
42 build(dst);
43 }
44 depth--;
45 }catch(konforka::exception& ke) {
46 depth--;
47 ke.see(CODEPOINT);
48 throw;
49 }catch(...) {
50 depth--;
51 throw;
52 }
53 }
54
55}
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 @@
1#ifndef __PCH_H
2#define __PCH_H
3
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <sys/wait.h>
7#include <unistd.h>
8#include <fcntl.h>
9#include <sys/ipc.h>
10#include <sys/shm.h>
11#include <sys/sem.h>
12#include <errno.h>
13#include <dlfcn.h>
14#include <fnmatch.h>
15
16#include <dotconf.h>
17
18#include <cstdarg>
19#include <cassert>
20#include <iostream>
21#include <fstream>
22
23#include <vector>
24using namespace std;
25
26#include <konforka/exception.h>
27
28#include "sitecing/acomponent.h"
29#include "sitecing/cgi_component.h"
30#include "sitecing/component_factory.h"
31#include "sitecing/sitecing_util.h"
32#include "sitecing/sitecing_exception.h"
33#include "sitecing/component_so.h"
34#include "sitecing/configuration.h"
35#include "sitecing/file_factory.h"
36#include "sitecing/sitecing_interface_cgi.h"
37#include "sitecing/sitespace.h"
38#include "sitecing/util.h"
39#include "sitecing/scoreboard.h"
40#include "sitecing/process_manager.h"
41
42#include "sitecing/sitecing_parser.h"
43#include "sitecing/sitecing_enflesher.h"
44
45#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 @@
1#ifdef USE_PCH
2 #include "pch.h"
3#else
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include <sys/wait.h>
7 #include <signal.h>
8 #include <errno.h>
9 #include <cassert>
10 #include <string>
11 #include <konforka/exception.h>
12 using namespace std;
13 #include "sitecing/process_manager.h"
14#endif
15
16namespace sitecing {
17
18 process_manager::process_manager()
19 : min_children(1), max_children(MAX_SITECING_SCOREBOARD_SLOTS),
20 min_spare_children(0), max_spare_children(-1), finishing(false),
21 die_humbly(false) {
22 }
23 process_manager::~process_manager() {
24 if(die_humbly)
25 return;
26 for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
27 scoreboard_slot *sslot = sboard.get_slot(tmp);
28 if((sslot->state!=scoreboard_slot::state_free) && (sslot->pid>0))
29 kill(sslot->pid,SIGTERM);
30 }
31 collect_dead_souls(true);
32 }
33
34 void process_manager::manage() {
35 while(!finishing) {
36 manage_children();
37 // XXX: is it the way it should be?
38 sleep(10);
39 wait_for_children();
40 }
41 collect_dead_souls(true);
42 }
43
44 void process_manager::collect_dead_souls(bool actively) {
45 for(int tries=5;(tries>0) && (sboard.count_slots(scoreboard_slot::state_free)!=MAX_SITECING_SCOREBOARD_SLOTS);tries--) {
46 if(actively) {
47 for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
48 scoreboard_slot *sslot = sboard.get_slot(tmp);
49 if((sslot->state!=scoreboard_slot::state_free) && (sslot->pid>0))
50 kill(sslot->pid,SIGTERM);
51 }
52 }
53 wait_for_children(false);
54 // XXX: again.. is it the right way?
55 sleep(1);
56 }
57 }
58
59 void process_manager::wait_for_children(bool hang) {
60 int status;
61 int o = WUNTRACED;
62 if(!hang)
63 o|=WNOHANG;
64 while(sboard.count_slots(scoreboard_slot::state_free)<MAX_SITECING_SCOREBOARD_SLOTS) {
65 pid_t pid = waitpid(-1,&status,o);
66 if(!pid)
67 return;
68 if(pid<0) {
69 if(errno==EINTR)
70 return;
71 throw konforka::exception(CODEPOINT,"failed to waitpid()");
72 }
73 assert(pid);
74 int slot = sboard.get_slot_by_pid(pid);
75 sboard.free_slot(slot);
76 if(hang)
77 return;
78 }
79 }
80
81 void process_manager::manage_children() {
82 if(!spawn_children())
83 kill_children();
84 else
85 sleep(1); // just to get some rest.
86 }
87
88 bool process_manager::spawn_children() {
89 int total_children = 0;
90 int idle_children = 0;
91 for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
92 switch(sboard.get_slot(tmp)->state) {
93 case scoreboard_slot::state_free:
94 break;
95 case scoreboard_slot::state_idle:
96 idle_children++;
97 default:
98 total_children++;
99 break;
100 }
101 }
102 int total_lack = 0;
103 if(total_children<min_children)
104 total_lack = min_children-total_children;
105 int idle_lack = 0;
106 if(idle_children<min_spare_children)
107 idle_lack = min_spare_children-idle_children;
108 bool rv = false;
109 for(;(idle_lack>0 || total_lack>0) && (total_children<max_children);idle_lack--,total_lack--,total_children++) {
110 spawn_child();
111 rv = true;
112 }
113 return rv;
114 }
115
116 bool process_manager::kill_children() {
117 int idle_children = 0;
118 for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
119 if(sboard.get_slot(tmp)->state==scoreboard_slot::state_idle)
120 idle_children++;
121 }
122 int idle_excess = idle_children-max_spare_children;
123 bool rv = false;
124 for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS && idle_excess>0;tmp++) {
125 scoreboard_slot *sslot = sboard.get_slot(tmp);
126 if((sslot->state==scoreboard_slot::state_idle) && (sslot->pid>0)) {
127 kill(sslot->pid,SIGTERM);
128 idle_excess--;
129 rv = true;
130 }
131 }
132 return rv;
133 }
134
135 void process_manager::spawn_child() {
136 int slot = sboard.allocate_slot();
137 pid_t pid = fork();
138 if(pid<0) {
139 sboard.free_slot(slot);
140 throw konforka::exception(CODEPOINT,"failed to fork()");
141 }
142 if(!pid) {
143 // child
144 sboard.get_slot(slot)->pid = getpid();
145 process(slot);
146 _exit(0);
147 }
148 // parent
149 sboard.get_slot(slot)->pid = pid;
150 }
151
152}
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 @@
1#ifdef USE_PCH
2 #include "pch.h"
3#else
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include <sys/ipc.h>
7 #include <sys/shm.h>
8 #include <cassert>
9 #include <string>
10 #include <konforka/exception.h>
11 using namespace std;
12 #include "sitecing/scoreboard.h"
13#endif
14
15namespace sitecing {
16
17 scoreboard::scoreboard()
18 : shmid(-1), slots(NULL) {
19 shmid = shmget(IPC_PRIVATE,MAX_SITECING_SCOREBOARD_SLOTS*sizeof(scoreboard_slot),IPC_CREAT|0600);
20 if(shmid<0)
21 throw konforka::exception(CODEPOINT,"failed to shmget()");
22 slots = (scoreboard_slot*)shmat(shmid,NULL,0);
23 if(shmctl(shmid,IPC_RMID,NULL))
24 throw konforka::exception(CODEPOINT,"failed to shmctl()");
25 if(!slots)
26 throw konforka::exception(CODEPOINT,"failed to shmat()");
27 for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++)
28 slots[tmp].state=scoreboard_slot::state_free;
29 }
30 scoreboard::~scoreboard() {
31 shmdt(slots);
32 }
33
34 int scoreboard::allocate_slot() {
35 for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
36 if(slots[tmp].state==scoreboard_slot::state_free) {
37 slots[tmp].state=scoreboard_slot::state_allocated;
38 slots[tmp].pid=0;
39 return tmp;
40 }
41 }
42 throw konforka::exception(CODEPOINT,"out of scoreboard slots");
43 }
44 void scoreboard::free_slot(int slot) {
45 assert(slot>=0 && slot<MAX_SITECING_SCOREBOARD_SLOTS);
46 if(slots[slot].state==scoreboard_slot::state_free)
47 throw konforka::exception(CODEPOINT,"freeing unallocated slot");
48 slots[slot].state=scoreboard_slot::state_free;
49 }
50
51 scoreboard_slot *scoreboard::get_slot(int slot) {
52 assert(slot>=0 && slot<MAX_SITECING_SCOREBOARD_SLOTS);
53 return &slots[slot];
54 }
55 int scoreboard::get_slot_by_pid(pid_t pid) {
56 for(int rv=0;rv<MAX_SITECING_SCOREBOARD_SLOTS;rv++)
57 if( (slots[rv].state!=scoreboard_slot::state_free) && (slots[rv].pid == pid) )
58 return rv;
59 throw konforka::exception(CODEPOINT,"no such process");
60 }
61
62 int scoreboard::count_slots(enum scoreboard_slot::_state state) {
63 int rv = 0;
64 for(int tmp=0;tmp<MAX_SITECING_SCOREBOARD_SLOTS;tmp++) {
65 if(slots[tmp].state==state)
66 rv++;
67 }
68 return rv;
69 }
70
71}
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 @@
1%{
2#include <iostream>
3#include <fstream>
4#include <cassert>
5#include <stdexcept>
6using namespace std;
7#include "sitecing/sitecing_exception.h"
8using namespace sitecing;
9#define sitecing_enflesher_flexlexer_once
10#include "sitecing/sitecing_enflesher.h"
11#include "sitecing/sitecing_parser.h"
12#undef yyFlexLexer
13#define yyFlexLexer sitecing_enflesherFlexLexer
14%}
15%option 8bit c++ verbose noyywrap yyclass="sitecing_enflesher" yylineno prefix="sitecing_enflesher" stack debug
16
17 ID[A-Za-z_][A-Za-z0-9_]*
18
19%%
20
21 ^\%\%\#[^\n]+\n{
22 string line = yytext;
23 line.erase(0,3);
24 line.erase(line.length()-1);
25 outs.flush();
26 outs.close();
27 outs.clear();
28 outs.open((parser.output_basename+line).c_str(),ios::trunc);
29 if(!outs.good())
30 throw preprocessor_error(CODEPOINT,"failed to write preprocessor output");
31 anchor();
32 anchoraged = true;
33}
34 ^\%\%[^\n]+\n{
35 string line = yytext;
36 line.erase(0,2);
37 line.erase(line.length()-1);
38 outs.flush();
39 outs.close();
40 outs.clear();
41 outs.open((parser.output_basename+line).c_str(),ios::trunc);
42 if(!outs.good())
43 throw preprocessor_error(CODEPOINT,"failed to write preprocessor output");
44 anchoraged = false;
45}
46
47 \<\%component_basename\%\>outs << parser.component_basename; anchor_time = true;
48 \<\%impl\%\> outs << parser.impl; anchor_time = true;
49 \<\%member_functions:impl\%\>{
50 for(sitecing_parser::member_functions_t::const_iterator i=parser.member_functions.begin();i!=parser.member_functions.end();i++) {
51 outs << i->type << " " << parser.class_name << "::";
52 if(i->name.empty()) {
53 outs << parser.class_name << "()";
54 bool first = true;
55 for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) {
56 if(i->initializer.empty())
57 continue;
58 if(first) {
59 outs << ":";
60 first=false;
61 }else{
62 outs << ",";
63 }
64 if(i->bComponent) {
65 outs << i->name << "(NULL)";
66 }else {
67 outs << i->name << "(" << i->initializer << ")";
68 }
69 }
70 }else if(i->name == "~")
71 outs << "~" << parser.class_name << "()";
72 else
73 outs << i->name << i->args;
74 outs << "{\n" << i->body << "\n}\n";
75 }
76 anchor_time = true;
77}
78 \<\%class_name\%\> outs << parser.class_name; anchor_time = true;
79 \<\%baseclass_header\%\>outs << parser.base_header; anchor_time = true;
80 \<\%decl\%\> outs << parser.decl; anchor_time = true;
81 \<\%baseclass_name\%\> outs << parser.base_class; anchor_time = true;
82 \<\%member_variables:decl\%\>{
83 for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) {
84 if(i->bComponent) {
85 if(i->type.empty()) {
86 i->type = parser.factory.get_classname(i->initializer);
87 }
88 if(i->bTypeOnly) {
89 outs << "typedef " << i->type << " " << i->name << ";\n";
90 }else{
91 outs << "typedef " << i->type << " __type_" << i->name << ";\nsitecing::so_component __soc_" << i->name << ";\n__type_" << i->name << " *" << i->name << ";\n";
92 }
93 }else{
94 outs << i->type << " " << i->name << ";\n";
95 }
96 }
97 anchor_time = true;
98}
99 \<\%member_functions:decl\%\>{
100 for(sitecing_parser::member_functions_t::const_iterator i=parser.member_functions.begin();i!=parser.member_functions.end();i++) {
101 (i->name.empty()?outs:outs << "virtual ")
102 << i->type << " ";
103 if(i->name.empty()) {
104 outs << parser.class_name << "()";
105 }else if(i->name == "~")
106 outs << "~" << parser.class_name << "()";
107 else
108 outs << i->name << i->args;
109 outs << ";\n";
110 }
111 anchor_time = true;
112}
113 \<\%imports:list\%\> {
114 for(sitecing_parser::member_variables_t::const_iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) {
115 if(i->bComponent)
116 outs << i->initializer << endl;
117 }
118 anchor_time = true;
119}
120 \<\%imports:includes\%\>{
121 for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) {
122 if(i->bComponent)
123 outs << "\n#include \"" << i->initializer << ".h\"\n";
124 }
125 anchor_time = true;
126}
127 \<\%imports:import\%\>{
128 for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) {
129 if(!i->bComponent)
130 continue;
131 if(i->bTypeOnly)
132 continue;
133 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";
134 }
135 anchor_time = true;
136}
137
138 \<\%base_component\%\> {
139 // TODO:
140 anchor_time = true;
141}
142
143 \<\%ancestors:includes\%\>{
144 for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) {
145 outs << "#include \"" << i->path << ".h\"\n";
146 }
147 anchor_time = true;
148}
149 \<\%ancestors:component_list\%\>{
150 for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) {
151 outs << i->path << "\n";
152 }
153 anchor_time = true;
154}
155 \<\%ancestors:base_clause_part\%\>{
156 for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) {
157 outs << ", virtual public " << parser.factory.get_classname(i->path);
158 }
159}
160 \<\%ancestors:typedefs\%\> {
161 for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) {
162 outs << "typedef class " << parser.factory.get_classname(i->path) << " " << i->name << ";\n";
163 }
164 anchor_time = true;
165}
166 \<\%ancestors:import\%\> {
167 for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) {
168 outs << i->name << "::__do_imports();\n";
169 }
170 anchor_time = true;
171}
172
173 \n {
174 if(anchor_time)
175 anchor();
176 ECHO;
177}
178 . ECHO;
179
180%%
181
182void sitecing_enflesher::LexerOutput(const char *buf,int size) {
183 outs.write(buf,size);
184}
185
186void sitecing_enflesher::enflesh() {
187 ifstream ifs(parser.skeleton.c_str());
188 if(!ifs.good())
189 throw preprocessor_error(CODEPOINT,"failed to open skeleton file");
190 switch_streams(&ifs,NULL);
191 yylex();
192}
193
194void sitecing_enflesher::anchor() {
195 if(!anchoraged)
196 return;
197 outs << "\n#line " << lineno() << " \"" << parser.skeleton << "\"\n";
198 anchor_time = false;
199}
200/*
201 * vim:set ft=lex:
202 */
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 @@
1#include <cassert>
2#include "sitecing/sitecing_interface_cgi.h"
3
4namespace sitecing {
5
6 sitecing_interface_cgi::sitecing_interface_cgi(sitespace *s)
7 : sitecing_interface(&prebuffer), ss(s), cgigw(NULL) {
8 }
9
10 void sitecing_interface_cgi::prepare(kingate::cgi_gateway *cg) {
11 cgigw = cg;
12 headers.clear();
13 headers["Content-Type"] = "text/html";
14 prebuffer.str("");
15 }
16
17 void sitecing_interface_cgi::flush() {
18 assert(cgigw);
19 for(headers_t::const_iterator i=headers.begin();i!=headers.end();i++)
20 cgigw->out() << i->first << ": " << i->second << "\n";
21 (cgigw->out() << "\n").write(prebuffer.str().c_str(),prebuffer.tellp());
22 cgigw->out().flush();
23 }
24
25}
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 @@
1%{
2 /*
3 * XXX: I have a strong feeling that this parser should be completely rewritten.
4 */
5#include <iostream>
6#include <fstream>
7#include <cassert>
8#include <stdexcept>
9using namespace std;
10#include "sitecing/sitecing_util.h"
11#include "sitecing/sitecing_exception.h"
12using namespace sitecing;
13#define sitecing_parser_flexlexer_once
14#include "sitecing/sitecing_parser.h"
15#include "sitecing/sitecing_enflesher.h"
16#undef yyFlexLexer
17#define yyFlexLexer sitecing_parserFlexLexer
18%}
19%x SLASHSTAR_COMMENT SLASHSLASH_COMMENT STRING
20%x CODELINE CLASSLINE DECLLINE IMPLLINE DECLBLOCK IMPLBLOCK VARLINE VARINIT
21%x IMPORTLINE IMPORTCOMPONENT
22%x IMPORTTYPELINE IMPORTTYPECOMPONENT
23%x DERIVELINE DERIVECOMPONENT
24%x CONSTRUCTOR DESTRUCTOR CODEMETHODLINE CODEMETHODARGS
25%x CODEMETHODBLOCK INLINE METHODLINE METHODARGS METHODBLOCK CODEBLOCK OUTPUTBLOCK
26%option 8bit c++ verbose noyywrap yyclass="sitecing_parser" prefix="sitecing_parser" stack yylineno
27
28 WHITESPACE[ \t]
29 ID [A-Za-z_][A-Za-z0-9_]*
30 NOIDCHAR[^A-Za-z0-9_]
31
32%%
33
34<INITIAL>{
35 ^\%\%class{WHITESPACE}+{
36 // TODO: signal error if we already have class name acquired from source.
37 modi.push_front(modus_operandi(modus_operandi::flag_devour_comments|modus_operandi::flag_devour_whitespace));
38 BEGIN(CLASSLINE);
39 }
40 ^\%\%decl{WHITESPACE}+{
41 modi.push_front(modus_operandi(0));
42 anchor();
43 BEGIN(DECLLINE);
44 }
45 ^\%\%impl{WHITESPACE}+{
46 modi.push_front(modus_operandi(0));
47 anchor();
48 BEGIN(IMPLLINE);
49 }
50 \<\%decl\> {
51 modi.push_front(modus_operandi(0));
52 anchor();
53 BEGIN(DECLBLOCK);
54 }
55 \<\%impl\> {
56 modi.push_front(modus_operandi(0));
57 anchor();
58 BEGIN(IMPLBLOCK);
59 }
60 ^\%\%var{WHITESPACE}+{
61 modi.push_front(modus_operandi(modus_operandi::flag_devour_comments));
62 anchor();
63 BEGIN(VARLINE);
64 }
65 ^\%\%import{WHITESPACE}+{
66 modi.push_front(modus_operandi(modus_operandi::flag_devour_comments));
67 BEGIN(IMPORTLINE);
68 }
69 ^\%\%import_type{WHITESPACE}+ {
70 modi.push_front(modus_operandi(modus_operandi::flag_devour_comments));
71 BEGIN(IMPORTTYPELINE);
72 }
73 ^\%\%derive{WHITESPACE}+{
74 modi.push_front(modus_operandi(modus_operandi::flag_devour_comments));
75 BEGIN(DERIVELINE);
76 }
77 \<\%constructor\>{
78 modi.push_front(modus_operandi());
79 anchor();
80 BEGIN(CONSTRUCTOR);
81 }
82 \<\%destructor\>{
83 modi.push_front(modus_operandi());
84 anchor();
85 BEGIN(DESTRUCTOR);
86 }
87 \<\%codemethod{WHITESPACE}+{
88 modi.push_front(modus_operandi(modus_operandi::flag_devour_comments));
89 anchor();
90 BEGIN(CODEMETHODLINE);
91 }
92 \<\%method{WHITESPACE}+ {
93 modi.push_front(modus_operandi(modus_operandi::flag_devour_comments));
94 anchor();
95 BEGIN(METHODLINE);
96 }
97 <<EOF>>{
98 assert(modi.size()==1);
99 M().modify(modus_operandi::modus_preop);
100 LexerOutput(";",1);
101 return 0;
102 }
103}
104 <<EOF>>throw preprocessor_error(CODEPOINT,"unexpected end of file",lineno());
105
106<CODEBLOCK,CODEMETHODBLOCK>{
107 "<%output>"{
108 anchor();
109 yy_push_state(OUTPUTBLOCK);
110 }
111}
112
113<METHODLINE>{
114 {WHITESPACE}+{
115 modus_operandi& m = modi.front();
116 if(!m.output.empty()) {
117 if(!m._lastid.empty()) {
118 if(!m._type.empty()) m._type += ' ';
119 m._type += m._lastid;
120 }
121 m._lastid = m.output;
122 m.output.clear();
123 }
124 }
125 \*{
126 modus_operandi& m = modi.front();
127 ECHO;
128 if(!m._lastid.empty()) {
129 if(!m._type.empty()) m._type += ' ';
130 m._type += m._lastid;
131 }
132 m._lastid = m.output;
133 m.output.clear();
134 }
135 \({
136 modus_operandi& m = modi.front();
137 if(m.output.empty()) {
138 m._name=m._lastid;
139 }else{
140 if(!m._lastid.empty()) { // XXX: lastid, I believe should never be emtpy...
141 if(!m._type.empty()) m._type += ' ';
142 m._type += m._lastid;
143 }
144 m._name = m.output;
145 m.output.clear();
146 }
147 ECHO;
148 BEGIN(METHODARGS);
149 }
150}
151<METHODARGS>{
152 \%\>{
153 modus_operandi& m = modi.front();
154 m._args = m.output;
155 m.output.clear();
156 anchor();
157 BEGIN(METHODBLOCK);
158 }
159}
160
161<INITIAL,METHODBLOCK,OUTPUTBLOCK>{
162 \<\%{WHITESPACE}+{
163 M().modify(modus_operandi::modus_postop);
164 anchor();
165 LexerOutput("(",1);
166 yy_push_state(INLINE);
167 }
168 ^\%{WHITESPACE}{
169 M().modify(modus_operandi::modus_code);
170 anchor();
171 yy_push_state(CODELINE);
172 }
173 \<\%code\>{
174 M().modify(modus_operandi::modus_code);
175 anchor();
176 yy_push_state(CODEBLOCK);
177 }
178 "</%output>" {
179 if(YY_START!=OUTPUTBLOCK) throw preprocessor_error(CODEPOINT,"unexpected tag",lineno());
180 M().modify(modus_operandi::modus_code);
181 anchor();
182 yy_pop_state();
183 }
184}
185
186 <INLINE>\%\>LexerOutput(")",1); M().modus=modus_operandi::modus_preop; yy_pop_state();
187 <CODELINE>\nyy_pop_state();
188
189<CODEMETHODLINE>{
190 {WHITESPACE}+{
191 modus_operandi& m = modi.front();
192 if(!m.output.empty()) {
193 if(!m._lastid.empty()) {
194 if(!m._type.empty()) m._type += ' ';
195 m._type += m._lastid;
196 }
197 m._lastid = m.output;
198 m.output.clear();
199 }
200 }
201 \*{
202 modus_operandi& m = modi.front();
203 ECHO;
204 if(!m._lastid.empty()) {
205 if(!m._type.empty()) m._type += ' ';
206 m._type += m._lastid;
207 }
208 m._lastid = m.output;
209 m.output.clear();
210 }
211 \({
212 modus_operandi& m = modi.front();
213 if(m.output.empty()) {
214 m._name=m._lastid;
215 }else{
216 if(!m._lastid.empty()) { // XXX: lastid, I believe should never be emtpy...
217 if(!m._type.empty()) m._type += ' ';
218 m._type += m._lastid;
219 }
220 m._name = m.output;
221 m.output.clear();
222 }
223 ECHO;
224 BEGIN(CODEMETHODARGS);
225 }
226}
227<CODEMETHODARGS>{
228 \%\>{
229 modus_operandi& m = modi.front();
230 m._args = m.output;
231 m.output.clear();
232 m.flags=0;
233 anchor();
234 BEGIN(CODEMETHODBLOCK);
235 }
236}
237
238<IMPORTLINE>{
239 {WHITESPACE}+{ }
240 {ID}{
241 if(!modi.front()._name.empty())
242 throw preprocessor_error(CODEPOINT,"syntax error",lineno());
243 modi.front()._name = yytext;
244 }
245 \= {
246 modi.front().output.clear();
247 BEGIN(IMPORTCOMPONENT);
248 }
249}
250<IMPORTCOMPONENT>{
251 {WHITESPACE}+{ }
252 \n{
253 modus_operandi& m = M();
254 string::size_type t = m.output.find_first_not_of(" \t");
255 if(t!=string::npos)
256 m.output.erase(0,t);
257 t = m.output.find_last_not_of(" \t;");
258 if(t!=string::npos)
259 m.output.erase(t+1);
260 if(m.output[0]=='"' && m.output[m.output.length()-1]=='"') {
261 m.output.erase(0,1);
262 m.output.erase(m.output.length()-1);
263 }
264 string c = combine_path(component_basename,m.output);
265 member_variables.push_back(member_variable(m._type,m._name,normalize_path(c,strip_leading_slash),true));
266 modi.pop_front();
267 BEGIN(INITIAL);
268 }
269}
270
271<IMPORTTYPELINE>{
272 {WHITESPACE}+{ }
273 {ID}{
274 if(!modi.front()._name.empty())
275 throw preprocessor_error(CODEPOINT,"syntax error",lineno());
276 modi.front()._name = yytext;
277 }
278 \= {
279 modi.front().output.clear();
280 BEGIN(IMPORTTYPECOMPONENT);
281 }
282}
283<IMPORTTYPECOMPONENT>{
284 {WHITESPACE}+{ }
285 \n{
286 modus_operandi& m = M();
287 string::size_type t = m.output.find_first_not_of(" \t");
288 if(t!=string::npos)
289 m.output.erase(0,t);
290 t = m.output.find_last_not_of(" \t;");
291 if(t!=string::npos)
292 m.output.erase(t+1);
293 if(m.output[0]=='"' && m.output[m.output.length()-1]=='"') {
294 m.output.erase(0,1);
295 m.output.erase(m.output.length()-1);
296 }
297 string c = combine_path(component_basename,m.output);
298 member_variables.push_back(member_variable(m._type,m._name,normalize_path(c,strip_leading_slash),true,true));
299 modi.pop_front();
300 BEGIN(INITIAL);
301 }
302}
303
304<DERIVELINE>{
305 {WHITESPACE}+{ }
306 {ID}{
307 if(!modi.front()._name.empty())
308 throw preprocessor_error(CODEPOINT,"syntax_error",lineno());
309 modi.front()._name = yytext;
310 }
311 \= {
312 modi.front().output.clear();
313 BEGIN(DERIVECOMPONENT);
314 }
315}
316<DERIVECOMPONENT>{
317 {WHITESPACE}+{ }
318 \n {
319 modus_operandi& m = M();
320 string::size_type t = m.output.find_first_not_of(" \t");
321 if(t!=string::npos)
322 m.output.erase(0,t);
323 t = m.output.find_last_not_of(" \t;");
324 if(t!=string::npos)
325 m.output.erase(t+1);
326 if(m.output[0]=='"' && m.output[m.output.length()-1]=='"') {
327 m.output.erase(0,1);
328 m.output.erase(m.output.length()-1);
329 }
330 string c = combine_path(component_basename,m.output);
331 ancestor_classes.push_back(ancestor_class(m._name,normalize_path(c,strip_leading_slash)));
332 modi.pop_front();
333 BEGIN(INITIAL);
334 }
335}
336
337<VARLINE>{
338 {WHITESPACE}+{
339 modus_operandi& m = modi.front();
340 if(!m.output.empty()) {
341 if(!m._lastid.empty()) {
342 if(!m._type.empty()) m._type += ' ';
343 m._type += m._lastid;
344 }
345 m._lastid = m.output;
346 m.output.clear();
347 }
348 }
349 \*{
350 modus_operandi& m = modi.front();
351 ECHO;
352 if(!m._lastid.empty()) {
353 if(!m._type.empty()) m._type += ' ';
354 m._type += m._lastid;
355 }
356 m._lastid = m.output;
357 m.output.clear();
358 }
359 \;|\n|\={
360 modus_operandi& m = modi.front();
361 if(m.output.empty()) {
362 m._name=m._lastid;
363 }else{
364 if(!m._lastid.empty()) { // XXX: lastid should never be emtpy, I believe?
365 if(!m._type.empty()) m._type += ' ';
366 m._type += m._lastid;
367 }
368 m._name=m.output;
369 m.output.clear();
370 }
371 BEGIN(VARINIT);
372 if(*yytext!='=')
373 unput('\n');
374 }
375}
376<VARINIT>{
377 \n{
378 modus_operandi& m = modi.front();
379 string::size_type t = m.output.find_first_not_of(" \t");
380 if(t!=string::npos)
381 m.output.erase(0,t);
382 t = m.output.find_last_not_of(" \t;");
383 if(t!=string::npos)
384 m.output.erase(t+1);
385 member_variables.push_back(member_variable(m._type,m._name,m.output));
386 if(!m.output.empty())
387 have_initializers=true;
388 modi.pop_front();
389 BEGIN(INITIAL);
390 }
391}
392 <DECLLINE>\n{
393 ECHO;
394 decl += modi.front().output;
395 modi.pop_front();
396 BEGIN(INITIAL);
397}
398 <IMPLLINE>\n{
399 ECHO;
400 impl += modi.front().output;
401 modi.pop_front();
402 BEGIN(INITIAL);
403}
404 <CLASSLINE>\n{
405 class_name = modi.front().output;
406 modi.pop_front();
407 BEGIN(INITIAL);
408}
409<CLASSLINE,DECLLINE,IMPLLINE,VARLINE,VARINIT,IMPORTLINE,IMPORTCOMPONENT,CODEMETHODLINE,CODEMETHODARGS,INLINE,METHODLINE,METHODARGS,DECLBLOCK,IMPLBLOCK,CONSTRUCTOR,DESTRUCTOR,CODEMETHODBLOCK,CODELINE,CODEBLOCK>{
410 "/*"{
411 yy_push_state(SLASHSTAR_COMMENT);
412 if(!M().devour_comments()) {
413 ECHO;
414 }
415 }
416 "//"{
417 yy_push_state(SLASHSLASH_COMMENT);
418 if(!M().devour_comments()) {
419 ECHO;
420 }
421 }
422 \" {
423 yy_push_state(STRING);
424 ECHO;
425 }
426 \'\\.\'{
427 ECHO;
428 }
429}
430
431<INITIAL,METHODBLOCK,OUTPUTBLOCK>{
432 \"soft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\\"",2);
433 \nsoft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\n",2);
434 \rsoft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\r",2);
435 \tsoft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\t",2);
436 \bsoft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\b",2);
437 \asoft_anchor(); M().modify(modus_operandi::modus_text); LexerOutput("\\a",2);
438 .soft_anchor(); M().modify(modus_operandi::modus_text); ECHO;
439 {WHITESPACE}+soft_anchor(); M().modify(modus_operandi::modus_text); ECHO;
440}
441
442<DECLBLOCK,IMPLBLOCK,CONSTRUCTOR,DESTRUCTOR,CODEMETHODBLOCK,METHODBLOCK,CODEBLOCK>{
443 \<\/\%decl\>{
444 if(YY_START!=DECLBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno());
445 decl += modi.front().output;
446 modi.pop_front();
447 BEGIN(INITIAL);
448 }
449 \<\/\%impl\>{
450 if(YY_START!=IMPLBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno());
451 impl += modi.front().output;
452 modi.pop_front();
453 BEGIN(INITIAL);
454 }
455 \<\/\%constructor\>{
456 if(YY_START!=CONSTRUCTOR) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno());
457 member_functions.push_back(member_function("","","",modi.front().output));
458 have_constructor = true;
459 modi.pop_front();
460 BEGIN(INITIAL);
461 }
462 \<\/\%destructor\>{
463 if(YY_START!=DESTRUCTOR) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno());
464 member_functions.push_back(member_function("","~","",modi.front().output));
465 modi.pop_front();
466 BEGIN(INITIAL);
467 }
468 \<\/\%codemethod\>{
469 if(YY_START!=CODEMETHODBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno());
470 modus_operandi& m = modi.front();
471 member_functions.push_back(member_function(m._type,m._name,m._args,m.output));
472 modi.pop_front();
473 BEGIN(INITIAL);
474 }
475 \<\/%method\> {
476 if(YY_START!=METHODBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno());
477 modus_operandi& m = modi.front();
478 m.modify(modus_operandi::modus_code);
479 member_functions.push_back(member_function(m._type,m._name,m._args,m.output));
480 modi.pop_front();
481 BEGIN(INITIAL);
482 }
483 \<\/%code\> {
484 if(YY_START!=CODEBLOCK) throw preprocessor_error(CODEPOINT,"tags mismatch",lineno());
485 yy_pop_state();
486 }
487 \n ECHO;
488}
489
490<SLASHSTAR_COMMENT>{
491 "*/"{
492 if(!M().devour_comments()) {
493 ECHO;
494 }
495 yy_pop_state();
496 unput(' ');
497 }
498 \n{
499 if(!M().devour_comments()) {
500 ECHO;
501 }
502 }
503}
504<SLASHSLASH_COMMENT>{
505 \n{
506 if(!M().devour_comments()) {
507 ECHO;
508 }
509 yy_pop_state();
510 if(YY_START!=CODEBLOCK && YY_START!=CODEMETHODBLOCK && YY_START!=IMPLBLOCK && YY_START!=DECLBLOCK)
511 unput('\n');
512 }
513}
514 <SLASHSTAR_COMMENT,SLASHSLASH_COMMENT>.{
515 if(!M().devour_comments()) {
516 ECHO;
517 }
518}
519<STRING>{
520 \\.ECHO;
521 \"ECHO; yy_pop_state();
522 .ECHO;
523}
524
525 {WHITESPACE}+{
526 if(!(M().flags&modus_operandi::flag_devour_whitespace)) {
527 ECHO;
528 }
529}
530
531%%
532
533sitecing_parser::sitecing_parser(component_factory& f)
534 : factory(f), have_initializers(false), have_constructor(false),
535 base_class("sitecing::cgi_component"),
536 base_header("sitecing/cgi_component.h"),
537 skeleton(__SC_DEFAULT_SKELETON) {
538 }
539
540void sitecing_parser::preprocess(const string& in) {
541 ifstream ifs(in.c_str(),ios::in);
542 if(!ifs.good())
543 throw preprocessor_error(CODEPOINT,"failed to open input file");
544 input_file = in;
545 modi.push_front(modus_operandi(0));
546 switch_streams(&ifs,NULL);
547 if(yylex())
548 throw preprocessor_error(CODEPOINT,"unknown error");
549 member_functions.push_back(member_function("void","main","(int _magic,va_list _args)",M().output));
550 if(have_initializers && !have_constructor)
551 member_functions.push_back(member_function("","","",""));
552 sitecing_enflesher enflesher(*this);
553 enflesher.enflesh();
554}
555
556void sitecing_parser::LexerOutput(const char* buf,int size) {
557 assert(modi.size());
558 M().output.append(buf,size);
559}
560
561static const char *modus_transitions
562 [sitecing_parser::modus_operandi::modi]
563 [sitecing_parser::modus_operandi::modi] = {
564// To:
565// code preop postop text From:
566 { "", "(*(__SCIF->out))", "(*(__SCIF->out))<<", "(*(__SCIF->out))<<\"" }, // code
567 { ";", "", "<<", "<<\"" }, // preop
568 { NULL, NULL, "", "\"" }, // postop
569 { "\";", "\"", "\"<<", "" } // text
570};
571
572void sitecing_parser::modus_operandi::modify(modus_t m) {
573 const char * x = modus_transitions[modus][m];
574 assert(x);
575 output += x;
576 modus = m;
577}
578
579void sitecing_parser::soft_anchor() {
580 if(M().modus!=modus_operandi::modus_text)
581 anchor();
582}
583void sitecing_parser::anchor() {
584 if(M().modus==modus_operandi::modus_text)
585 M().modify(modus_operandi::modus_preop);
586 M().output += "\n#line ";
587 char tmp[7];
588 snprintf(tmp,sizeof(tmp),"%d",lineno());
589 M().output += tmp;
590 M().output += " \"";
591 M().output += input_file;
592 M().output += "\"\n";
593}
594/* 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 @@
1#ifdef USE_PCH
2 #include "pch.h"
3#else
4 #include <sys/stat.h>
5 #include <sys/types.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <sys/ipc.h>
9 #include <sys/sem.h>
10 #include <errno.h>
11 #include <iostream>
12 #include <fstream>
13 #include <cassert>
14 #include "sitecing/sitecing_util.h"
15#endif
16
17namespace sitecing {
18
19 /*
20 * XXX: all of these utilities could be sheerly optimized.
21 */
22
23 string normalize_path(const string& path,int opts) {
24 const char *s = path.c_str();
25 string rv;
26 string::size_type notslash = 0;
27 if( (*s)=='.' && s[1]=='/' )
28 s+=2;
29 if(opts&strip_leading_slash)
30 for(;(*s) && (*s)=='/';s++);
31 for(;*s;s++) {
32 if( (*s)=='/' ) {
33 if(s[1]=='/')
34 continue;
35 if(s[1]=='.' && s[2]=='/') {
36 s+=2;
37 continue;
38 }
39 }
40 if(opts&restrict_dotdot) {
41 if(
42 ( rv.empty() && s[0]=='.' && s[1]=='.' && s[2]=='/' )// "^../"
43 || ( s[0]=='/' && s[1]=='.' && s[2]=='.' && (s[3]==0 || s[3]=='/') ) // "/..(/|$)"
44 )
45 throw utility_restricted_sequence(CODEPOINT,"restricted updir sequence encountered");
46 }
47 rv += *s;
48 if( (*s) != '/' )
49 notslash=rv.length();
50 }
51 if(!(opts&strip_trailing_slash))
52 notslash++;
53 if(notslash<rv.length())
54 rv.erase(notslash); // XXX: check the logic of stripping/not strippling trailing slash
55 return rv;
56 }
57
58 string strip_prefix(const string& str,const string& prefix) {
59 if(str.compare(0,prefix.length(),prefix))
60 throw utility_no_prefix(CODEPOINT,"no such prefix");
61 return str.substr(prefix.length());
62 }
63
64 string strip_suffix(const string& str,const string& suffix) {
65 if(str.compare(str.length()-suffix.length(),suffix.length(),suffix))
66 throw utility_no_suffix(CODEPOINT,"no such suffix");
67 return str.substr(0,str.length()-suffix.length());
68 }
69
70 string dir_name(const string& filename) {
71 string::size_type sl = filename.find_last_of('/');
72 if(sl==string::npos)
73 return ""; // no slashes -- no dir.
74 string::size_type nosl = filename.find_last_not_of('/',sl);
75 if(nosl==string::npos)
76 return ""; // only slashes -- no dir. XXX: only slashes after the last slash... does it mean no dir?
77 return filename.substr(0,nosl+1);
78 }
79
80 void make_path(const string& path,mode_t mode) {
81 struct stat st;
82 for(string::size_type sl=0;sl!=string::npos;sl=path.find('/',sl+1)) {
83 if(!sl)
84 continue;
85 string p = path.substr(0,sl);
86 if(stat(p.c_str(),&st) || !S_ISDIR(st.st_mode)) {
87 if(mkdir(p.c_str(),mode))
88 throw konforka::exception(CODEPOINT,"failed to mkdir()");
89 }
90 }
91 if(stat(path.c_str(),&st) || !S_ISDIR(st.st_mode)) {
92 if(mkdir(path.c_str(),mode))
93 throw konforka::exception(CODEPOINT,"failed to mkdir()");
94 }
95 }
96
97 void file_lock::lock(const string& f) {
98 unlock();
99 fd = open(f.c_str(),O_CREAT|O_RDWR,S_IRUSR|S_IWUSR);
100 if(fd<0)
101 throw konforka::exception(CODEPOINT,"failed to open/create lockfile");
102 try {
103 lock();
104 }catch(konforka::exception& ke) {
105 ke.see(CODEPOINT);
106 close(fd); fd=-1;
107 throw;
108 }catch(...) {
109 close(fd); fd=-1;
110 throw;
111 }
112 }
113 void file_lock::lock() {
114 assert(fd>=0);
115 struct flock fl;
116 fl.l_type = F_WRLCK;
117 fl.l_whence=SEEK_SET;
118 fl.l_start=fl.l_len=0;
119 for(int tries=3;tries;tries--) {
120 if(!fcntl(fd,F_SETLK,&fl))
121 return;
122 sleep(8);
123 }
124 throw konforka::exception(CODEPOINT,"failed to obtain file lock");
125 }
126 void file_lock::unlock() {
127 if(fd<0)
128 return;
129 struct flock fl;
130 fl.l_type = F_UNLCK;
131 fl.l_whence=SEEK_SET;
132 fl.l_start=fl.l_len=0;
133 int rv = fcntl(fd,F_SETLK,&fl);
134 close(fd);
135 fd=-1;
136 if(rv)
137 throw konforka::exception(CODEPOINT,"failed to release file lock");
138 }
139
140 void pid_file::set(const string& f,bool u) {
141 ofstream of(f.c_str(),ios::trunc);
142 if(!of)
143 throw konforka::exception(CODEPOINT,"failed to open file for writing pid");
144 of << getpid() << endl;
145 of.close();
146 file_name = f;
147 unlink_pid = u;
148 }
149 void pid_file::unlink() {
150 if(!unlink_pid)
151 return;
152 ::unlink(file_name.c_str());
153 }
154
155 void semaphore::init() {
156 deinit();
157 semid = semget(IPC_PRIVATE,1,IPC_CREAT|0600);
158 if(semid<0)
159 throw konforka::exception(CODEPOINT,"failed to semget()");
160 if(semctl(semid,0,SETVAL,1))
161 throw konforka::exception(CODEPOINT,"failed to semctl()");
162 }
163 void semaphore::deinit() {
164 if(semid<0)
165 return;
166 semctl(semid,0,IPC_RMID,0);
167 }
168 void semaphore::on() {
169 assert(semid>=0);
170 struct sembuf sb;
171 sb.sem_num=0;
172 sb.sem_op=-1;
173 sb.sem_flg = SEM_UNDO;
174 while(semop(semid,&sb,1)<0) {
175 if(errno!=EINTR)
176 throw konforka::exception(CODEPOINT,"failed to semop()");
177 }
178 }
179 void semaphore::off() {
180 assert(semid>=0);
181 struct sembuf sb;
182 sb.sem_num=0;
183 sb.sem_op=1;
184 sb.sem_flg = SEM_UNDO;
185 while(semop(semid,&sb,1)<0) {
186 if(errno!=EINTR)
187 throw konforka::exception(CODEPOINT,"failed to semop()");
188 }
189 }
190
191 void semaphore_lock::lock() {
192 assert(sem);
193 if(locked)
194 return;
195 sem->on();
196 locked = true;
197 }
198 void semaphore_lock::unlock() {
199 if(!sem)
200 return;
201 if(!locked)
202 return;
203 sem->off();
204 locked=false;
205 }
206
207 string combine_path(const string& origin,const string& relative,int opts) {
208 string r = normalize_path(relative,0);
209 string rv;
210 // XXX: what to do if relative is empty is a question, really.
211 if(r.empty()) {
212 return normalize_path( (opts&origin_is_file)?dir_name(origin):origin ,strip_leading_slash|restrict_dotdot|strip_trailing_slash);
213 }else{
214 if(r[0]=='/') {
215 r.erase(0,1);
216 }else{
217 rv = normalize_path((opts&origin_is_file)?dir_name(origin):origin,restrict_dotdot|strip_trailing_slash);
218 }
219 }
220 string::size_type lsl = rv.rfind('/');
221 for(string::size_type sl=r.find('/');sl!=string::npos;sl=r.find('/')) {
222 assert(sl!=0);
223 if(sl==1 && r[0]=='.') {
224 // it's a "./"
225 r.erase(0,2);
226 }else if(sl==2 && r[0]=='.' && r[1]=='.') {
227 // we have a "../"
228 if(lsl==string::npos) {
229 if(rv.empty() && (opts&fail_beyond_root))
230 throw utility_beyond_root(CODEPOINT,"went beyond root while combining path");
231 rv.clear();
232 }else{
233 rv.erase(lsl);
234 lsl = rv.rfind('/');
235 }
236 r.erase(0,3);
237 }else{
238 // we have a "something/"
239 lsl = rv.length();
240 rv += '/';
241 rv += r.substr(0,sl);
242 r.erase(0,sl+1);
243 }
244 }
245 if(r.empty())
246 return rv+'/';
247 if(r.length()==2 && r[0]=='.' && r[0]=='.') {
248 if(lsl==string::npos) {
249 if(rv.empty() & (opts&fail_beyond_root))
250 throw utility_beyond_root(CODEPOINT,"went beyond root while combining path");
251 return "/";
252 }else{
253 rv.erase(lsl+1);
254 return rv;
255 }
256 }
257 rv += '/';
258 rv += r;
259 return rv;
260 }
261
262 void auto_chdir::pushdir(const string& td,bool ap) {
263 char *tmp = get_current_dir_name();
264 assert(tmp);
265 saved_pwd = tmp;
266 free(tmp);
267 autopop=ap;
268 if(chdir(td.c_str()))
269 throw konforka::exception(CODEPOINT,"failed to chdir()");
270 }
271 void auto_chdir::popdir() {
272 autopop=false;
273 if(chdir(saved_pwd.c_str()))
274 throw konforka::exception(CODEPOINT,"failed to chdir()");
275 // XXX: or should it be thrown? after all we call it from destructor...
276 }
277
278}
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 @@
1#ifdef USE_PCH
2 #include "pch.h"
3#else
4 #include <cassert>
5 #include "sitecing/sitespace.h"
6 #include "sitecing/sitecing_util.h"
7#endif
8
9namespace sitecing {
10
11 sitespace::sitespace(configuration& c)
12 : config(c), factory(c) { }
13
14 sitespace::~sitespace() {
15 for(sentenced_t::iterator i = sentenced.begin();i!=sentenced.end();++i) {
16 assert((*i)->chickens_used.empty());
17 delete *i;
18 }
19 }
20
21 so_component sitespace::fetch(const string& c,sitecing_interface* scif) {
22 execute_sentenced();
23 string sobase = normalize_path(c);
24 string sopath = factory.root_so+sobase+".so";
25 config_options *co_build = config.lookup_config(sobase,config_options::flag_build);
26 if( (!co_build) || co_build->build )
27 factory.make(sopath);
28 components_t::iterator i = components.find(sopath);
29 if(i!=components.end()) {
30 if(i->second->is_uptodate())
31 return so_component(i->second,scif);
32 if(i->second->chickens_used.empty()) {
33 delete i->second;
34 }else{
35 sentenced.push_back(i->second);
36 }
37 components.erase(i);
38 }
39 pair<components_t::iterator,bool> ins = components.insert(components_t::value_type(sopath,new component_so(sopath)));
40 return so_component(ins.first->second,scif);
41 }
42
43 void sitespace::execute_sentenced() {
44 for(sentenced_t::iterator i = sentenced.begin();i!=sentenced.end();++i) {
45 if((*i)->chickens_used.empty()) {
46 delete *i;
47 sentenced.erase(i);
48 }
49 }
50 }
51
52}
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 @@
1#ifdef USE_PCH
2 #include "pch.h"
3#else
4 #include <cassert>
5 #include "sitecing/util.h"
6#endif
7
8namespace sitecing {
9
10 static const char *unsafeChars = "<>& \n\"";
11
12 string html_escape(const string& str,int flags) {
13 string rv = str;
14 string::size_type screwed = 0;
15 for(;;) {
16 screwed = rv.find_first_of(unsafeChars,screwed);
17 if(screwed == string::npos)
18 break;
19 while(screwed<rv.length() && strchr(unsafeChars,rv.at(screwed))) {
20 char danger = rv.at(screwed);
21 switch(danger) {
22 case '<':
23 rv.replace(screwed,1,"&lt;"); screwed+=4;
24 break;
25 case '>':
26 rv.replace(screwed,1,"&gt;"); screwed+=4;
27 break;
28 case '&':
29 rv.replace(screwed,1,"&amp;"); screwed+=5;
30 break;
31 case ' ':
32 if(flags&html_escape_nbsp) {
33 rv.replace(screwed,1,"&nbsp;"); screwed+=6;
34 }else
35 screwed++;
36 break;
37 case '\n':
38 if(flags&html_escape_br) {
39 if(flags&html_escape_br_noslash) {
40 rv.replace(screwed,1,"<br>\n"); screwed += 5;
41 }else{
42 rv.replace(screwed,1,"<br/>\n"); screwed += 6;
43 }
44 }else
45 screwed++;
46 break;
47 case '\"':
48 if(flags&html_escape_quot) {
49 rv.replace(screwed,1,"&quot;"); screwed+=6;
50 }else
51 screwed++;
52 break;
53 default:
54 assert(false);
55 break;
56 }
57 }
58 }
59 return rv;
60 }
61
62 void checkpoint::set() {
63 point = stream->tellp();
64 if(last_will==will_intestate)
65 last_will = will_rollback;
66 }
67
68 void checkpoint::make_will(will_t lw) {
69 last_will = lw;
70 }
71
72 void checkpoint::rollback() {
73 stream->seekp(point);
74 if(last_will == will_rollback)
75 last_will = will_intestate;
76 }
77 void checkpoint::commit() {
78 point = stream->tellp();
79 if(last_will == will_rollback)
80 last_will = will_intestate;
81 }
82
83}