summaryrefslogtreecommitdiffabout
authorMichael Krelin <hacker@klever.net>2005-03-30 15:50:28 (UTC)
committer Michael Krelin <hacker@klever.net>2005-03-30 15:50:28 (UTC)
commit3ddbfeafde93d1aab16a710498d86eef4e787406 (patch) (side-by-side diff)
treed53ef21cf3b2bddfeb957c98344d0be8759ed555
parent642dc685bd0a3f1526e22827a4539aa0e06aeff7 (diff)
downloadsitecing-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
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--include/sitecing/sitecing_enflesher.h13
-rw-r--r--lib/component_factory.cc19
-rw-r--r--lib/file_factory.cc1
-rw-r--r--lib/sitecing_enflesher.ll76
-rw-r--r--lib/sitecing_util.cc4
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 += '/';