author | Michael Krelin <hacker@klever.net> | 2005-03-30 15:50:28 (UTC) |
---|---|---|
committer | Michael Krelin <hacker@klever.net> | 2005-03-30 15:50:28 (UTC) |
commit | 3ddbfeafde93d1aab16a710498d86eef4e787406 (patch) (side-by-side diff) | |
tree | d53ef21cf3b2bddfeb957c98344d0be8759ed555 | |
parent | 642dc685bd0a3f1526e22827a4539aa0e06aeff7 (diff) | |
download | sitecing-3ddbfeafde93d1aab16a710498d86eef4e787406.zip sitecing-3ddbfeafde93d1aab16a710498d86eef4e787406.tar.gz sitecing-3ddbfeafde93d1aab16a710498d86eef4e787406.tar.bz2 |
1. preprocessor doesn't touch unchanged files anymore
2. doublechedk on whether file is up to date when making
3. changed the way preprocessor targets depend on the timestamp file
4. a bugfix in strip_suffix/strip_prefix
-rw-r--r-- | include/sitecing/sitecing_enflesher.h | 13 | ||||
-rw-r--r-- | lib/component_factory.cc | 19 | ||||
-rw-r--r-- | lib/file_factory.cc | 1 | ||||
-rw-r--r-- | lib/sitecing_enflesher.ll | 76 | ||||
-rw-r--r-- | lib/sitecing_util.cc | 4 |
5 files changed, 93 insertions, 20 deletions
diff --git a/include/sitecing/sitecing_enflesher.h b/include/sitecing/sitecing_enflesher.h index 512a358..8bc43a0 100644 --- a/include/sitecing/sitecing_enflesher.h +++ b/include/sitecing/sitecing_enflesher.h @@ -1,66 +1,79 @@ #ifndef __SITECING_SITECING_ENFLESHER_H #define __SITECING_SITECING_ENFLESHER_H #include <fstream> #include <string> using namespace std; /** * @file * @brief The preprocessed source builder. */ #ifndef sitecing_enflesher_flexlexer_once #define sitecing_enflesher_flexlexer_once #undef yyFlexLexer #define yyFlexLexer sitecing_enflesherFlexLexer #include <FlexLexer.h> #undef yyFlexLexerOnce #endif class sitecing_parser; /** * The enfleshing of the skeleton file according to the in-memory parsed * component source. */ class sitecing_enflesher : public sitecing_enflesherFlexLexer { public: /** * It is time to anchor output with the #line directive. */ bool anchor_time; /** * The file currently being written is supposed to have #line * directives all around the place. */ bool anchoraged; /** * The reference to the parser object containg the parsed source. */ sitecing_parser& parser; /** * The output stream. */ ofstream outs; + /** + * the outs stream destination file. + * @see outs + */ + string outs_filename; /** * @param p The parser object containing preparsed data. */ sitecing_enflesher(sitecing_parser& p) : parser(p), anchor_time(true) { } /** * Do the job. */ void enflesh(); virtual void LexerOutput(const char *buf,int size); virtual int yylex(); /** * Put a #line anchor into output. */ void anchor(); + + /** + * Close previously opened output stream, rename to the 'correct' + * destination filename, if needed, and open new file. + * @see outs + * @see outs_filename + */ + void outs_open(const string& nfile); }; #endif /* __SITECING_SITECING_ENFLESHER_H */ diff --git a/lib/component_factory.cc b/lib/component_factory.cc index f8666dc..b8f5a16 100644 --- a/lib/component_factory.cc +++ b/lib/component_factory.cc @@ -1,325 +1,332 @@ #ifdef USE_PCH #include "pch.h" #else #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <sys/wait.h> #include <fcntl.h> #include <iostream> #include <fstream> #include <stdexcept> #include <vector> using namespace std; #include "sitecing/component_factory.h" #include "sitecing/sitecing_util.h" #include "sitecing/sitecing_parser.h" #include "sitecing/sitecing_exception.h" #endif namespace sitecing { - static const char *pp_targets[] = { ".cc", ".h", ".imports", ".classname", ".baseclassname", ".ancestors" }; + static const char *pp_targets[] = { ".cc", ".h", ".imports", ".classname", ".baseclassname", ".ancestors", ".pp_stamp" }; static const char *cc_targets[] = { ".o", ".d" }; component_factory::component_factory(configuration& c) : config(c), root_source(normalize_path(c.root_source,strip_trailing_slash)+'/'), root_intermediate(normalize_path(c.root_intermediate,strip_trailing_slash)+'/'), root_so(normalize_path(c.root_so,strip_trailing_slash)+'/') { } void component_factory::get_dependencies(const string& dst,file_list_t& deps) { deps.clear(); string dp = normalize_path(dst,strip_trailing_slash); // source documents try { // XXX: or just compare it off? string noro = strip_prefix(dp,root_source); return; }catch(utility_no_affix& una) { } // .so binaries try { string noso = strip_suffix(dp,".so"); string noro = strip_prefix(noso,root_so); deps.push_back(root_intermediate+noro+".o"); config_options *co_so_deps = config.lookup_config(noro,config_options::flag_so_deps); if(co_so_deps) { for(list<string>::const_iterator i=co_so_deps->so_deps.begin();i!=co_so_deps->so_deps.end();++i) deps.push_back(*i); } return; }catch(utility_no_prefix& unp) { throw konforka::exception(CODEPOINT,"component is outside of component root"); }catch(utility_no_suffix& uns) { } // preprocessor targets for(int ppt=0;ppt<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) { try { string nos = strip_suffix(dp,pp_targets[ppt]); string noro = strip_prefix(nos,root_intermediate); deps.push_back(root_source+noro); ifstream imports((root_intermediate+noro+".imports").c_str(),ios::in); if(imports.good()) { string str; while(!imports.eof()) { imports >> str; if(!str.empty()) deps.push_back(root_intermediate+str+".classname"); } } ifstream ancestors((root_intermediate+noro+".ancestors").c_str(),ios::in); if(ancestors.good()) { string str; while(!ancestors.eof()) { ancestors >> str; if(!str.empty()) deps.push_back(root_intermediate+str+".classname"); } } config_options *co_intermediate_deps = config.lookup_config(noro,config_options::flag_intermediate_deps); if(co_intermediate_deps) { for(list<string>::const_iterator i=co_intermediate_deps->intermediate_deps.begin();i!=co_intermediate_deps->intermediate_deps.end();++i) deps.push_back(*i); } return; }catch(utility_no_affix& una) { // do nothing. must be a cpp dependency. } } // compiler targets for(int cct=0;cct<sizeof(cc_targets)/sizeof(*cc_targets);cct++) { try { string nos = strip_suffix(dp,cc_targets[cct]); string noro = strip_prefix(nos,root_intermediate); deps.push_back(root_intermediate+noro+".cc"); config_options *co_cpp_deps = config.lookup_config(noro,config_options::flag_cpp_deps); if( (!co_cpp_deps) || co_cpp_deps->cpp_deps) { ifstream df((root_intermediate+noro+".d").c_str(),ios::in); if(df.good()) { string str; while(!df.eof()) { df >> str; if(str.find_first_of("\\:")==string::npos) deps.push_back(combine_path(config.root_source+noro,str)); } } } // XXX: extra deps like IntermediateDeps? }catch(utility_no_affix& una) { // do nothing. } } } bool component_factory::is_uptodate(const string& dst,file_list_t *deps) { string dp = normalize_path(dst,strip_trailing_slash); // XXX: or just compare it off, instead of throwing things around. try { - strip_prefix(dp,root_intermediate); - return file_factory::is_uptodate(dst,deps); - }catch(utility_no_prefix& unp) { + string noro = strip_prefix(dp,root_intermediate); + for(int ppt=0;(ppt+1)<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) { + try { + string nos = strip_suffix(noro,pp_targets[ppt]); + return file_factory::is_uptodate(root_intermediate+nos+".pp_stamp",deps); + }catch(utility_no_suffix& uns) { } } + bool rv = file_factory::is_uptodate(dst,deps); + return rv; + }catch(utility_no_prefix& unp) { } try { strip_prefix(dp,root_so); return file_factory::is_uptodate(dst,deps); - }catch(utility_no_prefix& unp) { - } + }catch(utility_no_prefix& unp) { } return true; } void component_factory::build(const string& dst) { string dp = normalize_path(dst,strip_trailing_slash); // sources try { string noro = strip_prefix(dp,root_source); // building the sources is left up to developer return; }catch(utility_no_prefix& unp) { } // .so files try { string noso = strip_suffix(dp,".so"); string noro = strip_prefix(noso,root_so); string o = root_intermediate+noro+".o"; if(access(o.c_str(),R_OK)) throw konforka::exception(CODEPOINT,string("can't access compiled component code (")+o+")"); make_path(dir_name(root_so+noro),0755); file_lock lock_cc(root_intermediate+noro+".o.lock"); file_lock lock_so(root_so+noro+".so.lock"); int stdO = open((root_intermediate+noro+".ld.stdout").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664); if(stdO<0) throw konforka::exception(CODEPOINT,"failed to open/create linker stdout"); int stdE = open((root_intermediate+noro+".ld.stderr").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664); if(stdE<0) { close(stdO); throw konforka::exception(CODEPOINT,"failed to open/create linker stderr"); } list<string> args; config_options *co_ld_flags = config.lookup_config(noro,config_options::flag_ld_flags); if(co_ld_flags) { args.insert(args.end(),co_ld_flags->ld_flags.begin(),co_ld_flags->ld_flags.end()); } args.push_back("-shared"); args.push_back(o); file_list_t ancestors; get_ancestors(noro,ancestors); for(file_list_t::const_iterator i=ancestors.begin();i!=ancestors.end();++i) { string aso=root_so+*i+".so"; make(aso); args.push_back(aso); } args.push_back("-o"); args.push_back(dp); // TODO: "g++" configurable int rv = execute("g++",args,stdO,stdE); if(! (WIFEXITED(rv) && !WEXITSTATUS(rv)) ) // TODO:TODO: linker_error throw compile_error(CODEPOINT,"failed to link component",noro); return; }catch(utility_no_prefix& unp) { throw konforka::exception(CODEPOINT,"component is outside of component root"); }catch(utility_no_suffix& uns) { } // compiler targets for(int cct=0;cct<sizeof(cc_targets)/sizeof(*cc_targets);cct++) { try { string nos = strip_suffix(dp,cc_targets[cct]); string noro = strip_prefix(nos,root_intermediate); string cc = root_intermediate+noro+".cc"; string o = root_intermediate+noro+".o"; if(access(cc.c_str(),R_OK)) throw konforka::exception(CODEPOINT,string("can't access preprocessed component code (")+cc+")"); make_path(dir_name(cc),0755); string pwd = dir_name(root_source+noro); auto_chdir dir_changer(pwd); file_lock lock_source(root_intermediate+noro+".lock"); file_lock lock_cc(root_intermediate+noro+".o.lock"); int stdO = open((root_intermediate+noro+".stdout").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664); if(stdO<0) throw konforka::exception(CODEPOINT,"failed to open/create compiler stdout"); int stdE = open((root_intermediate+noro+".stderr").c_str(),O_CREAT|O_TRUNC|O_WRONLY,0664); if(stdE<0) { close(stdO); throw konforka::exception(CODEPOINT,"failed to open/create compiler's stderr"); } list<string> args; config_options *co_cpp_flags = config.lookup_config(noro,config_options::flag_cpp_flags); if(co_cpp_flags) { args.insert(args.end(),co_cpp_flags->cpp_flags.begin(),co_cpp_flags->cpp_flags.end()); } // TODO: maybe move it to separare config option like CoreCPPFLags? args.push_back("-I"+root_intermediate); args.push_back("-I"+root_source); args.push_back("-MD"); args.push_back("-MF"); args.push_back(root_intermediate+noro+".d"); args.push_back("-c"); args.push_back(cc); args.push_back("-o"); args.push_back(o); // TODO: "g++" configurable int rv = execute("g++",args,stdO,stdE); if(! (WIFEXITED(rv) && !WEXITSTATUS(rv)) ) throw compile_error(CODEPOINT,"failed to compile component",noro); return; }catch(utility_no_affix& una) { // do nothing, not a compiler target } } // preprocessor targets for(int ppt=0;ppt<sizeof(pp_targets)/sizeof(*pp_targets);ppt++) { try { string nos = strip_suffix(dp,pp_targets[ppt]); string noro = strip_prefix(nos,root_intermediate); string src = root_source+noro; if(access(src.c_str(),R_OK)) throw konforka::exception(CODEPOINT,string("can't access component source (")+src+")"); make_path(dir_name(root_intermediate+noro),0755); file_lock lock(root_intermediate+noro+".lock"); sitecing_parser parser(*this); config_options *co_skeleton = config.lookup_config(noro,config_options::flag_skeleton); if(co_skeleton) parser.skeleton = co_skeleton->skeleton; static const char *id_chars = "abcdefghijklmnopqrstuvwxyz0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZ"; parser.class_name = normalize_path(noro,strip_leading_slash|strip_trailing_slash); for(string::size_type illc = parser.class_name.find_first_not_of(id_chars);illc!=string::npos;illc=parser.class_name.find_first_not_of(id_chars,illc)) { string::size_type lc = parser.class_name.find_first_of(id_chars,illc); int n = ((lc==string::npos)?parser.class_name.length():lc)-illc; parser.class_name.replace(illc,n,n,'_'); } parser.class_name = "_SCC_"+parser.class_name; parser.output_basename = nos; parser.component_basename = noro; try { parser.preprocess(src); + string sf = root_intermediate+noro+".pp_stamp"; + ofstream sfs(sf.c_str(),ios::trunc|ios::out); // touch .pp_stamp }catch(preprocessor_error& pe) { pe.component_name = noro; pe.see(CODEPOINT); throw; } return; }catch(utility_no_affix& una) { // must be a crap from .d file } } cerr << "ignoring build request for " << dp << endl; } int component_factory::execute(const string& cmd, const list<string>& args,int stdo,int stde) { // XXX: is it right that we do stdio/stderr tricks outside of the function? cerr << "executing: " << cmd; vector<const char*> argv(args.size()+2); argv[0]=cmd.c_str(); int an = 1; for(list<string>::const_iterator i=args.begin();i!=args.end();i++) { cerr << " " << *i ; argv[an++] = i->c_str(); } cerr << endl; argv[an++]=NULL; pid_t pid = vfork(); if(pid==-1) { close(stdo); close(stde); throw konforka::exception(CODEPOINT,"failed to vfork()"); } if(!pid) { // child if(dup2(stdo,1)!=1) _exit(-1); if(dup2(stde,2)!=2) _exit(-1); close(0); execvp(cmd.c_str(),(char**)&argv.front()); _exit(-1); } // parent close(stdo); close(stde); int rv; if(waitpid(pid,&rv,0)<0) throw konforka::exception(CODEPOINT,"failed to waitpid()"); return rv; } string component_factory::get_classname(const string& component) { string cn = root_intermediate+normalize_path(component,strip_trailing_slash|strip_leading_slash)+".classname"; make(cn); ifstream ifs(cn.c_str()); if(!ifs.good()) throw konforka::exception(CODEPOINT,"failed to access component .classname"); ifs >> cn; return cn; } void component_factory::get_ancestors(const string& component,file_list_t& rv) { string cn = root_intermediate+normalize_path(component,strip_trailing_slash|strip_leading_slash)+".ancestors"; make(cn); ifstream ifs(cn.c_str()); if(!ifs.good()) throw konforka::exception(CODEPOINT,"filed to access component .ancestors"); rv.clear(); while(!ifs.eof()) { string a; ifs >> a; if(!a.empty()) rv.push_back(a); } } } diff --git a/lib/file_factory.cc b/lib/file_factory.cc index c6b5748..7ca7b86 100644 --- a/lib/file_factory.cc +++ b/lib/file_factory.cc @@ -1,55 +1,56 @@ #ifdef USE_PCH #include "pch.h" #else #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <konforka/exception.h> using namespace std; #include "sitecing/file_factory.h" #endif namespace sitecing { bool file_factory::is_uptodate(const string& dst,file_list_t* deps) { file_list_t deplist; file_list_t *fl = deps?deps:&deplist; get_dependencies(dst,*fl); struct stat stdst; if(stat(dst.c_str(),&stdst)) return false; for(file_list_t::const_iterator i=fl->begin();i!=fl->end();i++) { struct stat stdep; if(stat(i->c_str(),&stdep)) return false; if(stdst.st_mtime<stdep.st_mtime) return false; if(!is_uptodate(*i)) return false; } return true; } void file_factory::make(const string& dst) { try { depth++; if(depth>25) throw konforka::exception(CODEPOINT,"recursed too deeply."); file_list_t deps; if(!is_uptodate(dst,&deps)) { for(file_list_t::const_iterator i=deps.begin();i!=deps.end();i++) make(*i); + if(!is_uptodate(dst,&deps)) build(dst); } depth--; }catch(konforka::exception& ke) { depth--; ke.see(CODEPOINT); throw; }catch(...) { depth--; throw; } } } diff --git a/lib/sitecing_enflesher.ll b/lib/sitecing_enflesher.ll index 5f631d7..46489c7 100644 --- a/lib/sitecing_enflesher.ll +++ b/lib/sitecing_enflesher.ll @@ -1,202 +1,254 @@ %{ #include <iostream> #include <fstream> #include <cassert> #include <stdexcept> using namespace std; #include "sitecing/sitecing_exception.h" using namespace sitecing; #define sitecing_enflesher_flexlexer_once #include "sitecing/sitecing_enflesher.h" #include "sitecing/sitecing_parser.h" #undef yyFlexLexer #define yyFlexLexer sitecing_enflesherFlexLexer %} %option 8bit c++ verbose noyywrap yyclass="sitecing_enflesher" yylineno prefix="sitecing_enflesher" stack debug ID [A-Za-z_][A-Za-z0-9_]* %% ^\%\%\#[^\n]+\n { string line = yytext; line.erase(0,3); line.erase(line.length()-1); - outs.flush(); - outs.close(); - outs.clear(); - outs.open((parser.output_basename+line).c_str(),ios::trunc); - if(!outs.good()) - throw preprocessor_error(CODEPOINT,"failed to write preprocessor output"); + outs_open(parser.output_basename+line); anchor(); anchoraged = true; } ^\%\%[^\n]+\n { string line = yytext; line.erase(0,2); line.erase(line.length()-1); - outs.flush(); - outs.close(); - outs.clear(); - outs.open((parser.output_basename+line).c_str(),ios::trunc); - if(!outs.good()) - throw preprocessor_error(CODEPOINT,"failed to write preprocessor output"); + outs_open(parser.output_basename+line); anchoraged = false; } \<\%component_basename\%\> outs << parser.component_basename; anchor_time = true; \<\%impl\%\> outs << parser.impl; anchor_time = true; \<\%member_functions:impl\%\> { for(sitecing_parser::member_functions_t::const_iterator i=parser.member_functions.begin();i!=parser.member_functions.end();i++) { outs << i->type << " " << parser.class_name << "::"; if(i->name.empty()) { outs << parser.class_name << "()"; bool first = true; for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) { if(i->initializer.empty()) continue; if(first) { outs << ":"; first=false; }else{ outs << ","; } if(i->bComponent) { outs << i->name << "(NULL)"; }else { outs << i->name << "(" << i->initializer << ")"; } } }else if(i->name == "~") outs << "~" << parser.class_name << "()"; else outs << i->name << i->args; outs << "{\n" << i->body << "\n}\n"; } anchor_time = true; } \<\%class_name\%\> outs << parser.class_name; anchor_time = true; \<\%baseclass_header\%\> outs << parser.base_header; anchor_time = true; \<\%decl\%\> outs << parser.decl; anchor_time = true; \<\%baseclass_name\%\> outs << parser.base_class; anchor_time = true; \<\%member_variables:decl\%\> { for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) { if(i->bComponent) { if(i->type.empty()) { i->type = parser.factory.get_classname(i->initializer); } if(i->bTypeOnly) { outs << "typedef " << i->type << " " << i->name << ";\n"; }else{ outs << "typedef " << i->type << " __type_" << i->name << ";\nsitecing::so_component __soc_" << i->name << ";\n__type_" << i->name << " *" << i->name << ";\n"; } }else{ outs << i->type << " " << i->name << ";\n"; } } anchor_time = true; } \<\%member_functions:decl\%\> { for(sitecing_parser::member_functions_t::const_iterator i=parser.member_functions.begin();i!=parser.member_functions.end();i++) { (i->name.empty()?outs:outs << "virtual ") << i->type << " "; if(i->name.empty()) { outs << parser.class_name << "()"; }else if(i->name == "~") outs << "~" << parser.class_name << "()"; else outs << i->name << i->args; outs << ";\n"; } anchor_time = true; } \<\%imports:list\%\> { for(sitecing_parser::member_variables_t::const_iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) { if(i->bComponent) outs << i->initializer << endl; } anchor_time = true; } \<\%imports:includes\%\> { for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) { if(i->bComponent) outs << "\n#include \"" << i->initializer << ".h\"\n"; } anchor_time = true; } \<\%imports:import\%\> { for(sitecing_parser::member_variables_t::iterator i=parser.member_variables.begin();i!=parser.member_variables.end();i++) { if(!i->bComponent) continue; if(i->bTypeOnly) continue; outs << "__soc_" << i->name << "=__SCIF->ss->fetch(\"" << i->initializer << "\",__SCIF); " << i->name << "=static_cast<__type_" << i->name << "*>(__soc_" << i->name << ".ac->__the_most_derived_this());\n"; } anchor_time = true; } \<\%base_component\%\> { // TODO: anchor_time = true; } \<\%ancestors:includes\%\> { for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) { outs << "#include \"" << i->path << ".h\"\n"; } anchor_time = true; } \<\%ancestors:component_list\%\> { for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) { outs << i->path << "\n"; } anchor_time = true; } \<\%ancestors:base_clause_part\%\> { for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) { outs << ", virtual public " << parser.factory.get_classname(i->path); } } \<\%ancestors:typedefs\%\> { for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) { outs << "typedef class " << parser.factory.get_classname(i->path) << " " << i->name << ";\n"; } anchor_time = true; } \<\%ancestors:import\%\> { for(sitecing_parser::ancestor_classes_t::const_iterator i=parser.ancestor_classes.begin();i!=parser.ancestor_classes.end();++i) { outs << i->name << "::__do_imports();\n"; } anchor_time = true; } \n { if(anchor_time) anchor(); ECHO; } . ECHO; %% void sitecing_enflesher::LexerOutput(const char *buf,int size) { outs.write(buf,size); } void sitecing_enflesher::enflesh() { ifstream ifs(parser.skeleton.c_str()); if(!ifs.good()) throw preprocessor_error(CODEPOINT,"failed to open skeleton file"); switch_streams(&ifs,NULL); yylex(); } void sitecing_enflesher::anchor() { if(!anchoraged) return; outs << "\n#line " << lineno() << " \"" << parser.skeleton << "\"\n"; anchor_time = false; } + +void sitecing_enflesher::outs_open(const string& nfile) { + if(!outs_filename.empty()) { + outs.flush(); + outs.close(); + outs.clear(); + /* + * compare source and destination files. + * + * one can also keep a hash for the old one and compute one for the + * output while writing, but I'm not sure if it's any better. Surely a + * bit less accurate, unless we're going to compare it in case of + * difference, anyway. + */ + bool overwrite = false; + struct stat st_s, st_d; + string fn_s = outs_filename+".new"; + string fn_d = outs_filename; + if(stat(fn_d.c_str(),&st_d)) { + overwrite = true; + }else{ + if(stat(fn_s.c_str(),&st_s)) + throw preprocessor_error(CODEPOINT,"failed to stat() supposedly created file"); + if(st_s.st_size!=st_d.st_size) { + overwrite = true; + }else{ + ifstream i_s(fn_s.c_str(),ios::in); + if(!i_s) + throw preprocessor_error(CODEPOINT,"failed to open supposedly created file"); + ifstream i_d(fn_d.c_str(),ios::in); + if(!i_d) + throw preprocessor_error(CODEPOINT,"failed to open the old preprocessed source"); + off_t remaining = st_s.st_size; + char t1[2048]; + char t2[sizeof(t1)]; + while(remaining) { + int rb = remaining; + if(rb>sizeof(t1)) + rb = sizeof(t1); + if(i_s.read(t1,rb).gcount()!=rb) + throw preprocessor_error(CODEPOINT,"error reading just created file"); + if(i_d.read(t2,rb).gcount()!=rb) + throw preprocessor_error(CODEPOINT,"error reading the old preprocessed source"); + if(memcmp(t1,t2,rb)) { + overwrite = true; + break; + } + remaining -= rb; + } + } + } + if(overwrite) { + cerr << "renaming '" << fn_s << "'" << endl; + if(rename(fn_s.c_str(),fn_d.c_str())) + throw preprocessor_error(CODEPOINT,"failed to rename() generated output"); + } + } + outs_filename = nfile; + outs.open((nfile+".new").c_str(),ios::trunc); + if(!outs.good()) + throw preprocessor_error(CODEPOINT,"failed to write preprocessor output"); +} /* * vim:set ft=lex: */ diff --git a/lib/sitecing_util.cc b/lib/sitecing_util.cc index 5466b28..f892a60 100644 --- a/lib/sitecing_util.cc +++ b/lib/sitecing_util.cc @@ -1,257 +1,257 @@ #ifdef USE_PCH #include "pch.h" #else #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <fcntl.h> #include <sys/ipc.h> #include <sys/sem.h> #include <errno.h> #include <iostream> #include <fstream> #include <cassert> #include "sitecing/sitecing_util.h" #endif namespace sitecing { /* * XXX: all of these utilities could be sheerly optimized. */ string normalize_path(const string& path,int opts) { const char *s = path.c_str(); string rv; string::size_type notslash = 0; if( (*s)=='.' && s[1]=='/' ) s+=2; if(opts&strip_leading_slash) for(;(*s) && (*s)=='/';s++); for(;*s;s++) { if( (*s)=='/' ) { if(s[1]=='/') continue; if(s[1]=='.' && s[2]=='/') { s+=2; continue; } } if(opts&restrict_dotdot) { if( ( rv.empty() && s[0]=='.' && s[1]=='.' && s[2]=='/' ) // "^../" || ( s[0]=='/' && s[1]=='.' && s[2]=='.' && (s[3]==0 || s[3]=='/') ) // "/..(/|$)" ) throw utility_restricted_sequence(CODEPOINT,"restricted updir sequence encountered"); } rv += *s; if( (*s) != '/' ) notslash=rv.length(); } if(!(opts&strip_trailing_slash)) notslash++; if(notslash<rv.length()) rv.erase(notslash); // XXX: check the logic of stripping/not strippling trailing slash return rv; } string strip_prefix(const string& str,const string& prefix) { - if(str.compare(0,prefix.length(),prefix)) + if( (str.length()<prefix.length()) || str.compare(0,prefix.length(),prefix)) throw utility_no_prefix(CODEPOINT,"no such prefix"); return str.substr(prefix.length()); } string strip_suffix(const string& str,const string& suffix) { - if(str.compare(str.length()-suffix.length(),suffix.length(),suffix)) + if( (str.length()<suffix.length()) || str.compare(str.length()-suffix.length(),suffix.length(),suffix)) throw utility_no_suffix(CODEPOINT,"no such suffix"); return str.substr(0,str.length()-suffix.length()); } string dir_name(const string& filename) { string::size_type sl = filename.find_last_of('/'); if(sl==string::npos) return ""; // no slashes -- no dir. string::size_type nosl = filename.find_last_not_of('/',sl); if(nosl==string::npos) return ""; // only slashes -- no dir. XXX: only slashes after the last slash... does it mean no dir? return filename.substr(0,nosl+1); } void make_path(const string& path,mode_t mode) { struct stat st; for(string::size_type sl=0;sl!=string::npos;sl=path.find('/',sl+1)) { if(!sl) continue; string p = path.substr(0,sl); if(stat(p.c_str(),&st) || !S_ISDIR(st.st_mode)) { if(mkdir(p.c_str(),mode)) throw konforka::exception(CODEPOINT,"failed to mkdir()"); } } if(stat(path.c_str(),&st) || !S_ISDIR(st.st_mode)) { if(mkdir(path.c_str(),mode)) throw konforka::exception(CODEPOINT,"failed to mkdir()"); } } void file_lock::lock(const string& f) { unlock(); fd = open(f.c_str(),O_CREAT|O_RDWR,S_IRUSR|S_IWUSR); if(fd<0) throw konforka::exception(CODEPOINT,"failed to open/create lockfile"); try { lock(); }catch(konforka::exception& ke) { ke.see(CODEPOINT); close(fd); fd=-1; throw; }catch(...) { close(fd); fd=-1; throw; } } void file_lock::lock() { assert(fd>=0); struct flock fl; fl.l_type = F_WRLCK; fl.l_whence=SEEK_SET; fl.l_start=fl.l_len=0; for(int tries=3;tries;tries--) { if(!fcntl(fd,F_SETLK,&fl)) return; sleep(8); } throw konforka::exception(CODEPOINT,"failed to obtain file lock"); } void file_lock::unlock() { if(fd<0) return; struct flock fl; fl.l_type = F_UNLCK; fl.l_whence=SEEK_SET; fl.l_start=fl.l_len=0; int rv = fcntl(fd,F_SETLK,&fl); close(fd); fd=-1; if(rv) throw konforka::exception(CODEPOINT,"failed to release file lock"); } void pid_file::set(const string& f,bool u) { ofstream of(f.c_str(),ios::trunc); if(!of) throw konforka::exception(CODEPOINT,"failed to open file for writing pid"); of << getpid() << endl; of.close(); file_name = f; unlink_pid = u; } void pid_file::unlink() { if(!unlink_pid) return; ::unlink(file_name.c_str()); } void semaphore::init() { deinit(); semid = semget(IPC_PRIVATE,1,IPC_CREAT|0600); if(semid<0) throw konforka::exception(CODEPOINT,"failed to semget()"); if(semctl(semid,0,SETVAL,1)) throw konforka::exception(CODEPOINT,"failed to semctl()"); } void semaphore::deinit() { if(semid<0) return; semctl(semid,0,IPC_RMID,0); } void semaphore::on() { assert(semid>=0); struct sembuf sb; sb.sem_num=0; sb.sem_op=-1; sb.sem_flg = SEM_UNDO; while(semop(semid,&sb,1)<0) { if(errno!=EINTR) throw konforka::exception(CODEPOINT,"failed to semop()"); } } void semaphore::off() { assert(semid>=0); struct sembuf sb; sb.sem_num=0; sb.sem_op=1; sb.sem_flg = SEM_UNDO; while(semop(semid,&sb,1)<0) { if(errno!=EINTR) throw konforka::exception(CODEPOINT,"failed to semop()"); } } void semaphore_lock::lock() { assert(sem); if(locked) return; sem->on(); locked = true; } void semaphore_lock::unlock() { if(!sem) return; if(!locked) return; sem->off(); locked=false; } string combine_path(const string& origin,const string& relative,int opts) { string r = normalize_path(relative,0); string rv; // XXX: what to do if relative is empty is a question, really. if(r.empty()) { return normalize_path( (opts&origin_is_file)?dir_name(origin):origin ,strip_leading_slash|restrict_dotdot|strip_trailing_slash); }else{ if(r[0]=='/') { r.erase(0,1); }else{ rv = normalize_path((opts&origin_is_file)?dir_name(origin):origin,restrict_dotdot|strip_trailing_slash); } } string::size_type lsl = rv.rfind('/'); for(string::size_type sl=r.find('/');sl!=string::npos;sl=r.find('/')) { assert(sl!=0); if(sl==1 && r[0]=='.') { // it's a "./" r.erase(0,2); }else if(sl==2 && r[0]=='.' && r[1]=='.') { // we have a "../" if(lsl==string::npos) { if(rv.empty() && (opts&fail_beyond_root)) throw utility_beyond_root(CODEPOINT,"went beyond root while combining path"); rv.clear(); }else{ rv.erase(lsl); lsl = rv.rfind('/'); } r.erase(0,3); }else{ // we have a "something/" lsl = rv.length(); rv += '/'; rv += r.substr(0,sl); r.erase(0,sl+1); } } if(r.empty()) return rv+'/'; if(r.length()==2 && r[0]=='.' && r[0]=='.') { if(lsl==string::npos) { if(rv.empty() & (opts&fail_beyond_root)) throw utility_beyond_root(CODEPOINT,"went beyond root while combining path"); return "/"; }else{ rv.erase(lsl+1); return rv; } } rv += '/'; |