-rw-r--r-- | lib/.gitignore | 9 | ||||
-rw-r--r-- | lib/Makefile.am | 22 | ||||
-rw-r--r-- | lib/acomponent.cc | 47 | ||||
-rw-r--r-- | lib/cgi_component.cc | 30 | ||||
-rw-r--r-- | lib/component_factory.cc | 279 | ||||
-rw-r--r-- | lib/component_so.cc | 112 | ||||
-rw-r--r-- | lib/configuration.cc | 474 | ||||
-rw-r--r-- | lib/file_factory.cc | 55 | ||||
-rw-r--r-- | lib/pch.h | 45 | ||||
-rw-r--r-- | lib/process_manager.cc | 152 | ||||
-rw-r--r-- | lib/scoreboard.cc | 71 | ||||
-rw-r--r-- | lib/sitecing_enflesher.ll | 202 | ||||
-rw-r--r-- | lib/sitecing_interface_cgi.cc | 25 | ||||
-rw-r--r-- | lib/sitecing_parser.ll | 594 | ||||
-rw-r--r-- | lib/sitecing_util.cc | 278 | ||||
-rw-r--r-- | lib/sitespace.cc | 52 | ||||
-rw-r--r-- | lib/util.cc | 83 |
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 @@ | |||
1 | Makefile.in | ||
2 | sitecing_enflesher.cc | ||
3 | sitecing_parser.cc | ||
4 | .libs | ||
5 | .deps | ||
6 | Makefile | ||
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 @@ | |||
1 | lib_LTLIBRARIES = libsitecing.la | ||
2 | noinst_HEADERS = pch.h | ||
3 | |||
4 | INCLUDES = -I${top_srcdir}/include -I${top_builddir} ${KINGATE_CFLAGS} ${DOTCONF_CFLAGS} \ | ||
5 | ${PCREPP_CFLAGS} | ||
6 | AM_CPPFLAGS = -D__SC_DEFAULT_SKELETON=\"${pkgdatadir}/component.skel\" | ||
7 | AM_LFLAGS = -olex.yy.c | ||
8 | |||
9 | libsitecing_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 | ||
21 | libsitecing_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 | |||
11 | namespace 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 | |||
7 | namespace 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 | |||
20 | namespace 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 | |||
14 | namespace 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 | |||
15 | namespace 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 | |||
12 | namespace 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> | ||
24 | using 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 | |||
16 | namespace 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 | |||
15 | namespace 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> | ||
6 | using namespace std; | ||
7 | #include "sitecing/sitecing_exception.h" | ||
8 | using 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 | |||
182 | void sitecing_enflesher::LexerOutput(const char *buf,int size) { | ||
183 | outs.write(buf,size); | ||
184 | } | ||
185 | |||
186 | void 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 | |||
194 | void 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 | |||
4 | namespace 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> | ||
9 | using namespace std; | ||
10 | #include "sitecing/sitecing_util.h" | ||
11 | #include "sitecing/sitecing_exception.h" | ||
12 | using 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 | |||
533 | sitecing_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 | |||
540 | void 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 | |||
556 | void sitecing_parser::LexerOutput(const char* buf,int size) { | ||
557 | assert(modi.size()); | ||
558 | M().output.append(buf,size); | ||
559 | } | ||
560 | |||
561 | static 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 | |||
572 | void 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 | |||
579 | void sitecing_parser::soft_anchor() { | ||
580 | if(M().modus!=modus_operandi::modus_text) | ||
581 | anchor(); | ||
582 | } | ||
583 | void 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 | |||
17 | namespace 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 | |||
9 | namespace 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 | |||
8 | namespace 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,"<"); screwed+=4; | ||
24 | break; | ||
25 | case '>': | ||
26 | rv.replace(screwed,1,">"); screwed+=4; | ||
27 | break; | ||
28 | case '&': | ||
29 | rv.replace(screwed,1,"&"); screwed+=5; | ||
30 | break; | ||
31 | case ' ': | ||
32 | if(flags&html_escape_nbsp) { | ||
33 | rv.replace(screwed,1," "); 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,"""); 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 | } | ||