Branch copied to trunk svn merge /framework/trunk@470 /framework/branches/new-templates@470master
@@ -1,17 +1,28 @@ | |||
SUBDIRS = ./transtext | |||
noinst_PROGRAMS = hello_world.fcgi | |||
dist_bin_SCRIPTS = cppcms_tmpl_cc | |||
hello_world_fcgi_SOURCES = hello_world.cpp hello_world_view1.cpp hello_world_view2.cpp | |||
hello_world_fcgi_LDADD = libcppcms.la transtext/libcppcmstranstext.la | |||
hello_world_fcgi_CXXFLAGS= -Wall -I./transtext | |||
hello_world_fcgi_SOURCES = hello_world.cpp | |||
hello_world_fcgi_LDADD = libcppcms.la | |||
hello_world_fcgi_CXXFLAGS=-Wall | |||
hello_world_view1.cpp: hello_world_skin1.tmpl hello_world_view1.tmpl | |||
./cppcms_tmpl_cc hello_world_skin1.tmpl hello_world_view1.tmpl -o hello_world_view1.cpp -n view1 -d test | |||
hello_world_view2.cpp: hello_world_skin2.tmpl hello_world_view1.tmpl | |||
./cppcms_tmpl_cc hello_world_skin2.tmpl hello_world_view1.tmpl -o hello_world_view2.cpp -n view2 -d test | |||
lib_LTLIBRARIES = libcppcms.la | |||
libcppcms_la_SOURCES = global_config.cpp manager.cpp \ | |||
url.cpp worker_thread.cpp text_tool.cpp\ | |||
cache_interface.cpp base_cache.cpp thread_cache.cpp scgi.cpp | |||
libcppcms_la_SOURCES = global_config.cpp manager.cpp url.cpp worker_thread.cpp \ | |||
text_tool.cpp cache_interface.cpp base_cache.cpp thread_cache.cpp scgi.cpp \ | |||
base_view.cpp | |||
libcppcms_la_LDFLAGS = -version-info 0:0:0 | |||
libcppcms_la_CXXFLAGS = -Wall | |||
libcppcms_la_LIBADD = @CPPCMS_LIBS@ | |||
libcppcms_la_LDFLAGS = -no-undefined -version-info 0:0:0 | |||
libcppcms_la_LIBADD = @CPPCMS_LIBS@ transtext/libcppcmstranstext.la | |||
libcppcms_la_CXXFLAGS = -Wall -I./transtext | |||
if EN_FORK_CACHE | |||
libcppcms_la_SOURCES += process_cache.cpp | |||
@@ -19,12 +30,12 @@ endif | |||
if EN_FCGI_BACKEND | |||
libcppcms_la_SOURCES += fcgi.cpp | |||
endif | |||
endif | |||
nobase_pkginclude_HEADERS = global_config.h text_tool.h url.h cppcms_error.h \ | |||
manager.h worker_thread.h fcgi.h cache_interface.h archive.h \ | |||
base_cache.h thread_cache.h cgicc_connection.h scgi.h cgi_api.h \ | |||
process_cache.h shmem_allocator.h posix_mutex.h config.h cgi.h | |||
process_cache.h shmem_allocator.h posix_mutex.h config.h cgi.h base_view.h | |||
if EN_TCP_CACHE | |||
libcppcms_la_SOURCES += tcp_cache.cpp | |||
@@ -0,0 +1,104 @@ | |||
#include "base_view.h" | |||
#include "worker_thread.h" | |||
namespace cppcms { | |||
string base_view::escape(string const &s) | |||
{ | |||
string content; | |||
unsigned i,len=s.size(); | |||
content.reserve(len*3/2); | |||
for(i=0;i<len;i++) { | |||
char c=s[i]; | |||
switch(c){ | |||
case '<': content+="<"; break; | |||
case '>': content+=">"; break; | |||
case '&': content+="&"; break; | |||
case '\"': content+="""; break; | |||
default: content+=c; | |||
} | |||
} | |||
return content; | |||
} | |||
string base_view::urlencode(string const &s) | |||
{ | |||
string content; | |||
unsigned i,len=s.size(); | |||
content.reserve(3*len); | |||
for(i=0;i<len;i++){ | |||
char c=s[i]; | |||
if( ('a'<=c && c<='z') | |||
|| ('A'<=c && c<='Z') | |||
|| ('0'<=c && c<='9')) | |||
{ | |||
content+=c; | |||
} | |||
else { | |||
switch(c) { | |||
case '-': | |||
case '_': | |||
case '.': | |||
case '~': | |||
case '!': | |||
case '*': | |||
case '\'': | |||
case '(': | |||
case ')': | |||
case ';': | |||
case ':': | |||
case '@': | |||
case '&': | |||
case '=': | |||
case '+': | |||
case '$': | |||
case ',': | |||
case '/': | |||
case '?': | |||
case '%': | |||
case '#': | |||
case '[': | |||
case ']': | |||
content+=c; | |||
break; | |||
default: | |||
{ | |||
char buf[4]; | |||
snprintf(buf,sizeof(buf),"%%%02x",(unsigned)(c)); | |||
content.append(buf,3); | |||
} | |||
}; | |||
} | |||
}; | |||
return content; | |||
} | |||
namespace details { | |||
views_storage &views_storage::instance() { | |||
static views_storage this_instance; | |||
return this_instance; | |||
}; | |||
void views_storage::add_view(string t,string v,view_factory_t f) | |||
{ | |||
storage[t][v]=f; | |||
} | |||
void views_storage::remove_views(string t) | |||
{ | |||
storage.erase(t); | |||
} | |||
base_view *views_storage::fetch_view(string t,string v,base_view::settings s,base_content *c) | |||
{ | |||
templates_t::iterator p=storage.find(t); | |||
if(p==storage.end()) return NULL; | |||
template_views_t::iterator p2=p->second.find(v); | |||
if(p2==p->second.end()) return NULL; | |||
view_factory_t &f=p2->second; | |||
return f(s,c); | |||
} | |||
}; | |||
}// CPPCMS |
@@ -0,0 +1,134 @@ | |||
#ifndef CPPCMS_BASEVIEW_H | |||
#define CPPCMS_BASEVIEW_H | |||
#include <boost/format.hpp> | |||
#include <boost/function.hpp> | |||
#include <ostream> | |||
#include <sstream> | |||
#include <string> | |||
#include <map> | |||
#include "worker_thread.h" | |||
#include "cppcms_error.h" | |||
#include "config.h" | |||
namespace cppcms { | |||
using namespace std; | |||
// Just simple polimorphic class | |||
class base_content { | |||
public: | |||
virtual ~base_content() {}; | |||
}; | |||
class base_view { | |||
public: | |||
struct settings { | |||
worker_thread *worker; | |||
ostream *output; | |||
settings(worker_thread *w) : worker(w) , output(&w->get_cout()) {}; | |||
settings(worker_thread *w,ostream *o) : worker(w), output(o) {}; | |||
}; | |||
protected: | |||
worker_thread &worker; | |||
ostream &cout; | |||
base_view(settings s) : | |||
worker(*s.worker), | |||
cout(*s.output) | |||
{ | |||
} | |||
template<typename T> | |||
string escape(T const &v) | |||
{ | |||
ostringstream s; | |||
s<<v; | |||
return s.str(); | |||
}; | |||
string escape(string const &s); | |||
inline string raw(string s) { return s; }; | |||
inline string intf(int val,string f){ | |||
return (format(f) % val).str(); | |||
}; | |||
string strftime(std::tm const &t,string f) | |||
{ | |||
char buf[128]; | |||
buf[0]=0; | |||
std::strftime(buf,sizeof(buf),f.c_str(),&t); | |||
return buf; | |||
}; | |||
string date(std::tm const &t) { return strftime(t,"%Y-%m-%d"); }; | |||
string time(std::tm const &t) { return strftime(t,"%H:%M"); }; | |||
string timesec(std::tm const &t) { return strftime(t,"%T"); }; | |||
string escape(std::tm const &t) { return strftime(t,"%Y-%m-%d %T"); } | |||
string urlencode(string const &s); | |||
inline boost::format format(string const &f){ | |||
boost::format frm(f); | |||
frm.exceptions(0); | |||
return frm; | |||
}; | |||
public: | |||
virtual void render() {}; | |||
virtual ~base_view() {}; | |||
}; | |||
namespace details { | |||
template<typename T,typename VT> | |||
struct view_builder { | |||
base_view *operator()(base_view::settings s,base_content *c) { | |||
VT *p=dynamic_cast<VT *>(c); | |||
if(!p) throw cppcms_error("Incorrect content type"); | |||
return new T(s,*p); | |||
}; | |||
}; | |||
class views_storage { | |||
public: | |||
typedef boost::function<base_view *(base_view::settings s,base_content *c)> view_factory_t; | |||
private: | |||
typedef map<string,view_factory_t> template_views_t; | |||
typedef map<string,template_views_t> templates_t; | |||
templates_t storage; | |||
public: | |||
void add_view( string template_name, | |||
string view_name, | |||
view_factory_t); | |||
void remove_views(string template_name); | |||
base_view *fetch_view(string template_name,string view_name,base_view::settings ,base_content *c); | |||
static views_storage &instance(); | |||
}; | |||
}; // DETAILS | |||
}; // CPPCMS | |||
#define cppcms_view(X) \ | |||
do { \ | |||
void X##_symbol(); \ | |||
X##_symbol(); \ | |||
} while(0) | |||
#if defined(HAVE_CPP_0X_AUTO) | |||
# define CPPCMS_TYPEOF(x) auto | |||
#elif defined(HAVE_CPP_0X_DECLTYPE) | |||
# define CPPCMS_TYPEOF(x) decltype(x) | |||
#elif defined(HAVE_GCC_TYPEOF) | |||
# define CPPCMS_TYPEOF(x) typeof(x) | |||
#elif defined(HAVE_WORKING_BOOST_TYPEOF) | |||
# include <boost/typeof/typeof.hpp> | |||
# define CPPCMS_TYPEOF(x) BOOST_TYPEOF(x) | |||
#else | |||
# error "No useful C++0x auto/decltype/typeof method for this compiler" | |||
#endif | |||
#endif |
@@ -41,9 +41,10 @@ string deflate(string const &text,long level,long length) | |||
bool cache_iface::fetch_page(string const &key) | |||
{ | |||
string tmp; | |||
if(!cms->caching_module) return false; | |||
if(cms->caching_module->fetch_page(key,cms->out,cms->gzip)) { | |||
string tmp; | |||
if(cms->caching_module->fetch_page(key,tmp,cms->gzip)) { | |||
cms->cout<<tmp; | |||
cms->gzip_done=true; | |||
return true; | |||
} | |||
@@ -56,10 +57,13 @@ void cache_iface::store_page(string const &key,time_t timeout) | |||
archive a; | |||
long level=cms->app.config.lval("gzip.level",-1); | |||
long length=cms->app.config.lval("gzip.buffer",-1); | |||
string compr=deflate(cms->out,level,length); | |||
a<<(cms->out)<<compr; | |||
string tmp=cms->out_buf.str(); | |||
cms->out_buf.str(""); | |||
string compr=deflate(tmp,level,length); | |||
a<<tmp<<compr; | |||
if(cms->gzip){ | |||
cms->out=compr; | |||
cms->cout<<compr; | |||
cms->gzip_done=true; | |||
} | |||
cms->caching_module->store(key,triggers,timeout,a); | |||
@@ -54,3 +54,11 @@ server.socket = "/tmp/hello-fastcgi.socket" # Default is "" -- use default s | |||
# cache.ports = { 12000 12000 } # The ports tcp_cache_servers listen on | |||
# Make sure all servers occure in SAME order in all clients | |||
# Localization | |||
locale.dir="./transtext/locale" # path to locale directory | |||
locale.lang_list = { "he" "en" } # list of supported languages | |||
locale.lang_default = "he" # default language (default first one) | |||
locale.domain_list = { "app" "test" } # list of supported domains | |||
locale.domain_default = "test" # default domain (default first one) | |||
@@ -11,7 +11,7 @@ AC_PROG_LIBTOOL | |||
AC_SUBST(LIBTOOL_DEPS) | |||
AC_LANG_CPLUSPLUS | |||
AC_CONFIG_FILES([Makefile]) | |||
AC_CONFIG_FILES([Makefile transtext/Makefile]) | |||
AC_ARG_ENABLE(forkcache,[AS_HELP_STRING([--disable-forkcache],[Disable shared memory cache])]) | |||
AC_ARG_ENABLE(fastcgi,[AS_HELP_STRING([--disable-fastcgi],[Disable fastcgi interface])]) | |||
@@ -40,7 +40,7 @@ fi | |||
if test "x$enable_fastcgi" != "xno" ; then | |||
AC_CHECK_LIB(fcgi++,main,[ | |||
have_fcgi=yes | |||
CPPCMS_LIBS="-lfcgi++ $CPPCMS_LIBS" | |||
CPPCMS_LIBS="-lfcgi++ -lfcgi $CPPCMS_LIBS" | |||
AC_DEFINE([EN_FCGI_BACKEND],[],["Enable fastcgi backend"]) | |||
], | |||
[ echo "======================================================================" | |||
@@ -49,6 +49,45 @@ if test "x$enable_fastcgi" != "xno" ; then | |||
echo "You still have scgi and cgi API" ]) | |||
fi | |||
have_auto_type_detection=no | |||
AC_TRY_COMPILE([#include <list> | |||
], | |||
[std::list<int> l; auto p=l.begin();], | |||
[AC_DEFINE([HAVE_CPP_0X_AUTO],[],["Have C++0x auto"]) | |||
have_auto_type_detection=yes | |||
echo "C++0x auto... ok" ],[echo "C++0x auto... not supported"]) | |||
AC_TRY_COMPILE([#include <list> | |||
], | |||
[std::list<int> l; decltype(l.begin()) p=l.begin();], | |||
[AC_DEFINE([HAVE_CPP_0X_DECLTYPE],[],["Have C++0x decltype"]) | |||
have_auto_type_detection=yes | |||
echo "C++0x decltype... ok"],[echo "C++0x decltype... not supported"]) | |||
AC_TRY_COMPILE([#include <list> | |||
], | |||
[std::list<int> l; typeof(l.begin()) p=l.begin();], | |||
[AC_DEFINE([HAVE_GCC_TYPEOF],[],["Have g++ typeof"]) | |||
have_auto_type_detection=yes | |||
echo "g++ typeof... ok"],[echo "g++ typeof... not supported"]) | |||
AC_TRY_COMPILE([#include <list> | |||
#include <vector> | |||
#include #include <boost/typeof/typeof.hpp> | |||
], | |||
[ {std::list<int> l; BOOST_TYPEOF(l.begin()) p=l.begin(); BOOST_TYPEOF(*p) &refp=*p;} | |||
{std::vector<int> l; BOOST_TYPEOF(l.begin()) p=l.begin(); BOOST_TYPEOF(*p) &refp=*p;}], | |||
[AC_DEFINE([HAVE_WORKING_BOOST_TYPEOF],[],["Have working BOOST_TYPEOF"]) | |||
have_auto_type_detection=yes | |||
echo "Useful BOOST_TYPEOF... ok"],[echo "Useful BOOST_TYPEOF... not supported"]) | |||
if test x$have_auto_type_detection != xyes ; then | |||
echo "No useful type detection method for this compiler found" | |||
echo "Supported methods: C++0x auto, C++0x decltype, Boost.Typeof, GCC typeof" | |||
exit -1 | |||
fi | |||
AM_CONDITIONAL(EN_FORK_CACHE,[test "x$have_mm" == "xyes" ]) | |||
AM_CONDITIONAL(EN_FCGI_BACKEND,[test "x$have_fcgi" == "xyes" ]) | |||
@@ -99,6 +138,7 @@ fi | |||
AM_CONDITIONAL(EN_TCP_CACHE,[test "x$have_asio" == "xyes" ]) | |||
AC_CHECK_LIB(cgicc,main,[CPPCMS_LIBS="-lcgicc $CPPCMS_LIBS"],[echo "cgicc not found" ; exit -1]) | |||
AC_CHECK_LIB(dl,dlopen,[CPPCMS_LIBS="-ldl $CPPCMS_LIBS"],[]) | |||
AC_CHECK_LIB(pthread,pthread_sigmask,[],[echo "Pthreads library not found" ; exit -1]) | |||
AC_CHECK_LIB(boost_regex,main,[ | |||
CPPCMS_LIBS="-lboost_regex $CPPCMS_LIBS" | |||
@@ -0,0 +1,663 @@ | |||
#!/usr/bin/env python | |||
import os | |||
import re | |||
import sys | |||
variable_match=r'([a-zA-Z]\w*)(((\.|->)([a-zA-Z]\w*))*)' | |||
str_match=r'"([^"\\]|\\[^"]|\\")*"' | |||
def interleave(*args): | |||
for idx in range(0, max(len(arg) for arg in args)): | |||
for arg in args: | |||
try: | |||
yield arg[idx] | |||
except IndexError: | |||
continue | |||
def output(s): | |||
global stack | |||
global file_name | |||
global line_number | |||
global output_fd | |||
output_fd.write('\t' * len(stack) + '#line %d "%s"' % (line_number,file_name)+'\n' + \ | |||
'\t'*len(stack) + s + '\n') | |||
class tmpl_descr: | |||
def __init__(self,start,size): | |||
self.start_id=start | |||
self.param_num=size | |||
class namespace_block: | |||
pattern=r'^<%\s*namespace\s+(\w+)\s*(at\s*(\w+)\s*)?%>$' | |||
type='namespace' | |||
def use(self,m): | |||
self.internal_namespace=0 | |||
global namespace_name | |||
if m.group(1)=='vary': | |||
if namespace_name!='': | |||
self.internal_namespace=0 | |||
else: | |||
error_exit("Can't use `vary' namespace without external one") | |||
else: # m.group(1)!='vary' | |||
if namespace_name=='': | |||
namespace_name=m.group(1) | |||
self.internal_namespace=1 | |||
else: | |||
error_exit("Can't define both external and non-vary namespace") | |||
output( "namespace %s {" % namespace_name) | |||
self.reset_gettext=0 | |||
if m.group(2): | |||
global spec_gettext | |||
if not spec_gettext: | |||
spec_gettext=m.group(3) | |||
self.reset_gettext=1 | |||
global stack | |||
stack.append(self) | |||
def on_end(self): | |||
global namespace_name | |||
output( "} // end of namespace %s" % namespace_name) | |||
if self.internal_namespace: | |||
namespace_name='' | |||
if self.reset_gettext: | |||
global spec_gettext | |||
spec_gettext='' | |||
def write_class_loader(): | |||
global class_list | |||
output( "namespace {") | |||
output( " struct loader {") | |||
output( " loader(){") | |||
output( " using namespace cppcms::details;") | |||
output( " views_storage &_VS_ref=views_storage::instance();") | |||
list_of_views={} | |||
for class_def in class_list: | |||
list_of_views[class_def.namespace]="" | |||
if class_def.content_name!='': | |||
output( ' _VS_ref.add_view("%s","%s",view_builder<%s::%s,%s>());' \ | |||
% (class_def.namespace,class_def.name,class_def.namespace,class_def.name,class_def.content_name)) | |||
else: | |||
output( ' _VS_ref.add_view("%s","%s",simple_view_builder<%s::%s>());' \ | |||
% (class_def.namespace,class_def.name,class_def.namespace,class_def.name)) | |||
output( " };") | |||
output( ' ~loader() { ') | |||
output( " using namespace cppcms::details;") | |||
output( " views_storage &_VS_ref=views_storage::instance();") | |||
for ns in list_of_views: | |||
output( ' _VS_ref.remove_views("%s");' % ns) | |||
output( ' }') | |||
output( " } loader_entry;") | |||
output( "} // empty namespace ") | |||
output( 'extern "C" { // Dummy extern symbols to insure full link' ) | |||
for ns in list_of_views: | |||
output( '\tvoid load_view_%s(){}' % ns ) | |||
output( '} // extern C' ) | |||
def gettext_domain(): | |||
global spec_gettext | |||
if spec_gettext: | |||
return spec_gettext | |||
global namespace_name | |||
return namespace_name | |||
class class_block: | |||
pattern=r'^<%\s*class\s+(\w+)\s+(uses\s+(\w+(::\w+)?))?\s+(extends\s+(\w+(::\w+)?))?\s*%>$' | |||
type='class' | |||
def declare(self): | |||
if self.extends=='' : | |||
constructor='cppcms::base_view(_s)' | |||
self.extends='cppcms::base_view' | |||
else: | |||
constructor='%s(_s,_content)' % self.extends; | |||
output( "struct %s :public %s" % (self.class_name , self.extends )) | |||
output( "{") | |||
if self.uses!='' : | |||
output( "\t%s &content;" % self.uses) | |||
output( "\tcppcms::transtext::trans const *tr;" ) | |||
output( "\t%s(cppcms::base_view::settings _s,%s &_content): %s,content(_content)" % ( self.class_name,self.uses,constructor )) | |||
else: | |||
output( "\t%s(cppcms::base_view::settings _s): %s" % ( self.class_name,constructor )) | |||
output("\t{") | |||
output('\t\ttr=_s.worker->domain_gettext("%s");' % gettext_domain() ) | |||
output("\t};") | |||
def use(self,m): | |||
self.class_name=m.group(1) | |||
if m.group(2): | |||
self.uses=m.group(3) | |||
else: | |||
self.uses='' | |||
if m.group(5): | |||
self.extends=m.group(6) | |||
else: | |||
self.extends='' | |||
self.declare(); | |||
global stack | |||
if len(stack)!=1 or stack[-1].type!='namespace': | |||
error_exit("You must define class inside namespace block only") | |||
stack.append(self) | |||
global class_list | |||
global namespace_name | |||
class information: | |||
content_name=self.uses | |||
name=self.class_name | |||
namespace=namespace_name | |||
class_list.append(information()) | |||
def on_end(self): | |||
output( "}; // end of class %s" % self.class_name) | |||
class template_block: | |||
pattern=r'^<%\s*template\s+([a-zA-Z]\w*)\s*\(([\w\s,:\&]*)\)\s*%>$' | |||
type='template' | |||
plist=[] | |||
def create_parameters(self,lst): | |||
pattern=r'^\s*((\w+(::\w+)*)\s*(const)?\s*(\&)?\s*(\w+))\s*(,(.*))?$' | |||
m=re.match(pattern,lst) | |||
res=[] | |||
while m: | |||
global tmpl_seq | |||
id=m.group(2) | |||
if tmpl_seq.has_key(id): | |||
error_exit("Duplicate definition of patameter %s" % id) | |||
for v in self.plist: | |||
del tmpl_seq[v] | |||
return "" | |||
tmpl_seq[id]='' | |||
res.append(m.group(1)) | |||
self.plist.append(id) | |||
if m.group(8): | |||
lst=m.group(8) | |||
m=re.match(pattern,lst) | |||
else: | |||
return ','.join(res) | |||
for v in self.plist: | |||
del tmpl_seq[v] | |||
error_exit("Wrong expression %s" % lst) | |||
def use(self,m): | |||
self.name=m.group(1) | |||
params="" | |||
if m.group(2) and not re.match('^\s*$',m.group(2)): | |||
params=self.create_parameters(m.group(2)) | |||
output( "virtual void %s(%s) {" % (self.name,params) ) | |||
global stack | |||
if len(stack)==0 or stack[-1].type!='class': | |||
error_exit("You must define template inside class block only") | |||
stack.append(self) | |||
global current_template | |||
current_template=self.name | |||
global ignore_inline | |||
ignore_inline=0 | |||
def on_end(self): | |||
output( "} // end of template %s" % self.name) | |||
global ignore_inline | |||
ignore_inline=1 | |||
def inline_content(s): | |||
global ignore_inline | |||
if not ignore_inline: | |||
output( 'cout<<"%s";' % to_string(s)) | |||
def error_exit(x): | |||
global exit_flag | |||
global file_name | |||
global line_number | |||
sys.stderr.write("Error: %s in file %s, line %d\n" % (x,file_name,line_number)) | |||
exit_flag=1 | |||
def to_string(s): | |||
res='' | |||
for c in s: | |||
global stack | |||
if c=='\n': | |||
res+="\\n\""+"\n"+"\t"*len(stack)+"\t\"" | |||
elif c=="\t": | |||
res+="\\t" | |||
elif c=="\v": | |||
res+="\\v" | |||
elif c=="\b": | |||
res+="\\b" | |||
elif c=="\r": | |||
res+="\\r" | |||
elif c=="\f": | |||
res+="\\f" | |||
elif c=="\a": | |||
res+="\\a" | |||
elif c=="\\": | |||
res+="\\\\" | |||
elif c=="\"": | |||
res+="\\\"" | |||
elif ord(c)>0 and ord(c)<32: | |||
res+="%03o" % ord(c) | |||
else: | |||
res+=c | |||
return res | |||
def make_ident(val): | |||
m=re.match('^'+variable_match+'$',val) | |||
global tmpl_seq | |||
if tmpl_seq.has_key(m.group(1)): | |||
return val | |||
return "content." + val | |||
class foreach_block: | |||
pattern=r'^<%\s*foreach\s+([a-zA-Z]\w*)\s+in\s+(' + variable_match +')\s*%>$' | |||
type='foreach' | |||
has_item=0 | |||
has_separator=0 | |||
separator_label='' | |||
on_first_label='' | |||
def use(self,m): | |||
self.ident=m.group(1) | |||
self.seq_name=make_ident(m.group(2)) | |||
global tmpl_seq | |||
if tmpl_seq.has_key(self.ident): | |||
error_exit("Nested sequences with same name") | |||
tmpl_seq[self.ident]=''; | |||
output( "if(%s.begin()!=%s.end()) {" % (self.seq_name,self.seq_name) ) | |||
global stack | |||
stack.append(self) | |||
def on_end(self): | |||
if not self.has_item: | |||
error_exit("foreach without item") | |||
global tmpl_seq | |||
del tmpl_seq[self.ident] | |||
output( "}" ) | |||
def prepare_foreach(seq,ident,has_separator): | |||
output( "for(CPPCMS_TYPEOF(%(s)s.begin()) %(i)s_ptr=%(s)s.begin(),%(i)s_ptr_end=%(s)s.end();%(i)s_ptr!=%(i)s_ptr_end;++%(i)s_ptr) {" \ | |||
% { 's' :seq, 'i' : ident }) | |||
output( "CPPCMS_TYPEOF(*%s_ptr) &%s=*%s_ptr;" % (ident,ident,ident)) | |||
if has_separator: | |||
output( "if(%s_ptr!=%s.begin()) {" % (ident,seq)) | |||
class separator_block: | |||
pattern=r'^<%\s*separator\s*%>' | |||
type='separator' | |||
def use(self,m): | |||
global stack | |||
if len(stack)==0 or stack[len(stack)-1].type!='foreach': | |||
error_exit("separator without foreach") | |||
return | |||
foreachb=stack[len(stack)-1] | |||
if foreachb.has_separator: | |||
error_exit("two separators for one foreach") | |||
foreachb.has_separator=1 | |||
prepare_foreach(foreachb.seq_name,foreachb.ident,1) | |||
class item_block: | |||
pattern=r'^<%\s*item\s*%>' | |||
type='item' | |||
def use(self,m): | |||
global stack | |||
if not stack or stack[-1].type!='foreach': | |||
error_exit("item without foreach") | |||
return | |||
foreachb=stack[-1] | |||
if foreachb.has_item: | |||
error_exit("Two items for one foreach"); | |||
if foreachb.has_separator: | |||
output( "} // end of separator") | |||
else: | |||
prepare_foreach(foreachb.seq_name,foreachb.ident,0) | |||
foreachb.has_item=1 | |||
stack.append(self) | |||
def on_end(self): | |||
output( "} // end of item" ) | |||
class empty_block: | |||
pattern=r'^<%\s*empty\s*%>' | |||
type='empty' | |||
def use(self,m): | |||
global stack | |||
if not stack or stack[-1].type!='foreach': | |||
error_exit("empty without foreach") | |||
return | |||
forb=stack.pop() | |||
if not forb.has_item: | |||
error_exit("Unexpected empty - item missed?") | |||
output( " } else {") | |||
self.ident=forb.ident | |||
stack.append(self) | |||
def on_end(self): | |||
output( "} // end of empty") | |||
global tmpl_seq | |||
del tmpl_seq[self.ident] | |||
class else_block: | |||
pattern=r'^<%\s*else\s*%>$' | |||
type='else' | |||
def on_end(self): | |||
output("}") | |||
def use(self,m): | |||
prev=stack.pop() | |||
if prev.type!='if' and prev.type!='elif': | |||
error_exit("elif without if"); | |||
output( "}else{") | |||
stack.append(self) | |||
class if_block: | |||
pattern=r'^<%\s*(if|elif)\s+((not\s+|not\s+empty\s+|empty\s+)?('+variable_match+')|\((.+)\)|)\s*%>$' | |||
type='if' | |||
def prepare(self): | |||
output( "if(%s) {" % self.ident) | |||
def on_end(self): | |||
output( "} // endif") | |||
def use(self,m): | |||
global stack | |||
self.type=m.group(1) | |||
if m.group(4): | |||
if m.group(4)=='rtl': | |||
self.ident='(std::strcmp(tr->gettext("LTR"),"RTL")==0)' | |||
else: | |||
self.ident=make_ident(m.group(4)) | |||
if m.group(3): | |||
if re.match('.*empty',m.group(3)): | |||
self.ident=self.ident + '.empty()' | |||
if re.match('not.*',m.group(3)): | |||
self.ident="!("+self.ident+")" | |||
else: | |||
self.ident=m.group(10) | |||
if self.type == 'if' : | |||
self.prepare() | |||
stack.append(self) | |||
else: # type == elif | |||
if stack : | |||
prev=stack.pop() | |||
if prev.type!='if' and prev.type!='elif': | |||
error_exit("elif without if"); | |||
output( "}") | |||
output( "else") | |||
self.prepare() | |||
stack.append(self) | |||
else: | |||
error_exit("Unexpeced elif"); | |||
# END ifop | |||
class end_block: | |||
pattern=r'^<%\s*end(\s+(\w+))?\s*%>'; | |||
def use(self,m): | |||
global stack | |||
if not stack: | |||
error_exit("Unexpeced 'end'"); | |||
else: | |||
obj=stack.pop(); | |||
if m.group(1): | |||
if obj.type!=m.group(2): | |||
error_exit("End of %s does not match block %s" % (m.group(2) , obj.type)); | |||
obj.on_end() | |||
class error_com: | |||
pattern=r'^<%(.*)%>$' | |||
def use(self,m): | |||
error_exit("unknown command `%s'" % m.group(1)) | |||
class cpp_include_block: | |||
pattern=r'^<%\s*c\+\+\s+(.*)%>$' | |||
def use(self,m): | |||
output( m.group(1)); | |||
class base_show: | |||
mark='('+variable_match+r')\s*(\|(.*))?' | |||
base_pattern='^\s*'+mark + '$' | |||
def get_params(self,s): | |||
pattern='^\s*(('+variable_match+')|('+str_match+')|(\-?\d+(\.\d*)?))\s*(,(.*))?$' | |||
res=[] | |||
m=re.match(pattern,s) | |||
while m: | |||
if m.group(2): | |||
res.append(make_ident(m.group(2))) | |||
elif m.group(8): | |||
res.append(m.group(8)) | |||
elif m.group(10): | |||
res.append(m.group(10)) | |||
if m.group(13): | |||
s=m.group(13) | |||
m=re.match(pattern,s) | |||
else: | |||
return res | |||
error_exit("Invalid parameters: `%s'" % s ) | |||
return [] | |||
def prepare(self,s): | |||
m=re.match(self.base_pattern,s) | |||
if not m: | |||
error_exit("No variable") | |||
return []; | |||
var=make_ident(m.group(1)) | |||
if not m.group(8): | |||
return "escape(%s)" % var | |||
filters=m.group(8) | |||
expr='^\s*(ext\s+)?(\w+)\s*(\((([^"\)]|'+str_match + ')*)\))?\s*(\|(.*))?$' | |||
m=re.match(expr,filters) | |||
while m: | |||
if m.group(1): | |||
func="content."+m.group(2) | |||
else: | |||
func=m.group(2) | |||
if m.group(3): | |||
params=','.join([var]+self.get_params(m.group(4))) | |||
else: | |||
params=var | |||
var=func+"("+params+")" | |||
if m.group(8): | |||
filters=m.group(8) | |||
m=re.match(expr,filters) | |||
else: | |||
return var | |||
error_exit("Seems to be a problem in expression %s" % filters) | |||
return ""; | |||
class filters_show_block(base_show): | |||
pattern=r'^<%\s*('+ variable_match + r'\s*(\|.*)?)%>$' | |||
def use(self,m): | |||
expr=self.prepare(m.group(1)); | |||
if expr!="": | |||
output('cout<<%s;' % expr) | |||
def make_format_params(s): | |||
pattern=r'^(([^,\("]|'+str_match+'|\(([^"\)]|'+str_match+')*\))+)(,(.*))?$' | |||
params=[] | |||
m=re.match(pattern,s) | |||
s_orig=s | |||
while m.group(1): | |||
res=base_show().prepare(m.group(1)) | |||
if res: | |||
params.append(res) | |||
if not m.group(6): | |||
return params | |||
s=m.group(7) | |||
m=re.match(pattern,s) | |||
error_exit("Seems to be wrong parameters list [%s]" % s_orig) | |||
return [] | |||
class ngettext_block: | |||
pattern=r'^<%\s*ngt\s*('+str_match+')\s*,\s*('+str_match+')\s*,\s*('+variable_match+')\s*(using(.*))?\s*%>$' | |||
def use(self,m): | |||
s1=m.group(1) | |||
s2=m.group(3) | |||
idt=make_ident(m.group(5)) | |||
params=[] | |||
if m.group(11): | |||
params=make_format_params(m.group(12)) | |||
if not params: | |||
output( "cout<<tr->ngettext(%s,%s,%s);" % (s1,s2,idt)) | |||
else: | |||
output( "cout<<(format(tr->ngettext(%s,%s,%s)) %% %s );" % (s1,s2,idt, ' % '.join(params))) | |||
class gettext_block: | |||
pattern=r'^<%\s*gt\s*('+str_match+')\s*(using(.*))?\s*%>$' | |||
def use(self,m): | |||
s=m.group(1) | |||
params=[] | |||
if m.group(3): | |||
params=make_format_params(m.group(4)) | |||
if not params: | |||
output( "cout<<tr->gettext(%s);" % s) | |||
else: | |||
output( "cout<<(format(tr->gettext(%s)) %% %s );" % (s , ' % '.join(params))) | |||
class include_block: | |||
pattern=r'^<%\s*include\s+([a-zA_Z]\w*(::\w+)?)\s*\(\s*(.*)\)\s*%>$'; | |||
def use(self,m): | |||
if m.group(3): | |||
params=base_show().get_params(m.group(3)) | |||
else: | |||
params=[] | |||
output( "%s(%s);" % (m.group(1) , ','.join(params))) | |||
def fetch_content(content): | |||
tmp='' | |||
for row in re.split('\n',content): | |||
global line_number | |||
global file_name | |||
line_number+=1 | |||
l1=re.split(r'<%([^"%]|"([^"\\]|\\[^"]|\\")*"|%[^>])*%>',row) | |||
n=0 | |||
for l2 in re.finditer(r'<%([^"%]|"([^"\\]|\\[^"]|\\")*"|%[^>])*%>',row): | |||
yield tmp+l1[n] | |||
tmp='' | |||
yield l2.group(0) | |||
n+=3 | |||
tmp+=l1[n]+'\n' | |||
yield tmp | |||
def main(): | |||
global stack | |||
all=[] | |||
indx=1 | |||
global namespace_name | |||
global output_file | |||
global exit_flag | |||
while indx < len(os.sys.argv): | |||
if os.sys.argv[indx]=='-n': | |||
if indx+1>=len(os.sys.argv): | |||
sys.stderr.write("-n should be followed by namespace name\n") | |||
exit_flag=1 | |||
return | |||
else: | |||
namespace_name=os.sys.argv[indx+1]; | |||
indx+=1 | |||
elif os.sys.argv[indx]=='-o': | |||
if indx+1>=len(os.sys.argv): | |||
sys.stderr.write("-o should be followed by output file name\n") | |||
exit_flag=1 | |||
return | |||
else: | |||
output_file=os.sys.argv[indx+1] | |||
indx+=1 | |||
elif os.sys.argv[indx]=='-d': | |||
if indx+1>=len(os.sys.argv): | |||
sys.stderr.write("-d should followed by gettext domain name\n") | |||
exit_flag=1 | |||
return | |||
else: | |||
global spec_gettext | |||
spec_gettext=os.sys.argv[indx+1] | |||
indx+=1 | |||
else: | |||
all.append(os.sys.argv[indx]) | |||
indx+=1 | |||
if not all: | |||
sys.stderr.write("No input file names given\n") | |||
exit_flag=1 | |||
return | |||
global output_fd | |||
if output_file!='': | |||
output_fd=open(output_file,"w") | |||
for file in all: | |||
global file_name | |||
global line_number | |||
line_number=0 | |||
file_name=file | |||
f=open(file,'r') | |||
content=f.read() | |||
f.close() | |||
for x in fetch_content(content): | |||
if x=='' : continue | |||
if len(stack)==0: | |||
if re.match(r"^\s*$",x): | |||
continue | |||
elif not re.match(r"<\%.*\%>",x): | |||
error_exit("Content is not allowed outside template blocks") | |||
continue | |||
matched=0 | |||
for c in [ namespace_block(), class_block(), if_block(), template_block(), end_block(), else_block(), \ | |||
cpp_include_block(),\ | |||
gettext_block(),ngettext_block(),\ | |||
foreach_block(), item_block(), empty_block(),separator_block(),\ | |||
include_block(),\ | |||
filters_show_block(), error_com()]: | |||
m = re.match(c.pattern,x) | |||
if m : | |||
c.use(m) | |||
matched=1 | |||
break | |||
if not matched: | |||
inline_content(x) | |||
if stack: | |||
error_exit("Unexpected end of file %s" % file) | |||
global class_list | |||
if class_list and exit_flag==0: | |||
write_class_loader() | |||
####################### | |||
# MAIN | |||
####################### | |||
output_file='' | |||
output_fd=sys.stdout | |||
namespace_name='' | |||
file_name='' | |||
labels_counter=0 | |||
tmpl_seq={} | |||
template_parameters={} | |||
templates_map={} | |||
parameters_counter=2 | |||
stack=[] | |||
class_list=[] | |||
exit_flag=0 | |||
current_template='' | |||
spec_gettext='' | |||
ignore_inline=1 | |||
################ | |||
main() | |||
################ | |||
if output_fd!=sys.stderr: | |||
output_fd.close() | |||
if exit_flag!=0 and output_file!='': | |||
try: | |||
os.unlink(output_file) | |||
except: | |||
pass | |||
sys.exit(exit_flag) |
@@ -0,0 +1,45 @@ | |||
#ifndef CPPCMS_FORM_H | |||
#define CPPCMS_FORM_H | |||
namespace cppcms { | |||
class base_form_entry : public boost::noncopyable { | |||
char const *id; | |||
char const *name | |||
public: | |||
base_form_entry(base_form *f,char const *_id,char const *_name) : id(_id),name(w->gettext(_name)) { f->add(this); } | |||
virtual string render_html_input() = 0; | |||
virtual string get_title() = 0; | |||
virtual bool validate() = 0 | |||
virtual ~base_form_entry(){}; | |||
}; | |||
class form_int : public base_form_entry { | |||
bool test; | |||
int low,high; | |||
string id,name; | |||
public: | |||
int value; | |||
Integer(char const *id,char const *name,int default_val=0) : test(false), value(default_val) {}; | |||
Integer(string id,string name,int l,int h) : test(true),low(l),high(h) {}; | |||
virtual bool validate(); | |||
private: | |||
}; | |||
class base_form : public boost::noncopyable { | |||
vector<base_form_entry *> this_methods | |||
protected: | |||
void add(base_form_entry *entry); | |||
base_form *operator &(base_form_entry *entry) { add(entry) }; | |||
}; | |||
struct String { | |||
std::string value; | |||
String(); | |||
String(int min_len); | |||
}; | |||
} | |||
#endif |
@@ -1,16 +1,30 @@ | |||
#include "worker_thread.h" | |||
#include "manager.h" | |||
#include "hello_world_view.h" | |||
using namespace cppcms; | |||
class my_hello_world : public worker_thread { | |||
public: | |||
my_hello_world(manager const &s) : worker_thread(s) {}; | |||
my_hello_world(manager const &s) : worker_thread(s) | |||
{ | |||
use_template("view2"); | |||
}; | |||
virtual void main(); | |||
}; | |||
void my_hello_world::main() | |||
{ | |||
out+="<h1>Simple Hello World test application.</h1>\n"; | |||
view::hello v; | |||
v.title="Cool"; | |||
v.msg=gettext("Hello World"); | |||
for(int i=0;i<15;i++) | |||
v.numbers.push_back(i); | |||
v.lst.push_back(view::data("Hello",10)); | |||
v.ok=true; | |||
render("hello",v); | |||
} | |||
int main(int argc,char ** argv) | |||
@@ -0,0 +1,16 @@ | |||
<% c++ #include "hello_world_view.h" %> | |||
<% namespace vary %> | |||
<% class master uses view::master %> | |||
<% template render() %> | |||
<html> | |||
<title>Skin1 :: <% title %></title> | |||
<body> | |||
<% include body() %> | |||
</body> | |||
</html> | |||
<% end template %> | |||
<% template body() %> | |||
<% end template %> | |||
<% end class %> | |||
<% end namespace %> | |||
@@ -0,0 +1,16 @@ | |||
<% c++ #include "hello_world_view.h" %> | |||
<% namespace vary %> | |||
<% class master uses view::master %> | |||
<% template render() %> | |||
<html> | |||
<title>Skin2:: <% title %></title> | |||
<body> | |||
<% include body() %> | |||
</body> | |||
</html> | |||
<% end template %> | |||
<% template body() %> | |||
<% end template %> | |||
<% end class %> | |||
<% end namespace %> | |||
@@ -0,0 +1,25 @@ | |||
#include "base_view.h" | |||
#include <list> | |||
class my_hello_world; | |||
namespace view { | |||
struct master : public cppcms::base_content { | |||
std::string title; | |||
bool ok; | |||
}; | |||
struct data { | |||
std::string name; | |||
int val; | |||
data(char const *n="",int v=0) : name(n),val(v){} | |||
}; | |||
struct hello : public master { | |||
std::string msg; | |||
std::list<int> numbers; | |||
std::list<data> lst; | |||
}; | |||
}; | |||
@@ -0,0 +1,16 @@ | |||
<% namespace vary %> | |||
<% class hello uses view::hello extends master %> | |||
<% template body() %> | |||
<% if rtl %><% end %> | |||
<% if not empty title %><h1><% title %></h1><% end %> | |||
<p><% msg %></p> | |||
<% foreach x in numbers %> | |||
<ul> | |||
<% item %> | |||
<li><% ngt "passed one day","passed %d days",x using x %></li> | |||
<% end %> | |||
</ul> | |||
<% end %> | |||
<% end template %> | |||
<% end class %> | |||
<% end namespace %> |
@@ -4,6 +4,8 @@ | |||
#include "manager.h" | |||
#include "cgicc_connection.h" | |||
#include <dlfcn.h> | |||
#include <dirent.h> | |||
#include <poll.h> | |||
#include <signal.h> | |||
#include <errno.h> | |||
@@ -13,10 +15,12 @@ | |||
#include <sys/mman.h> | |||
#include <sys/wait.h> | |||
#include <sys/types.h> | |||
#include <algorithm> | |||
#include "thread_cache.h" | |||
#include "scgi.h" | |||
#include "cgi.h" | |||
#ifdef EN_FORK_CACHE | |||
# include "process_cache.h" | |||
#endif | |||
@@ -511,12 +515,44 @@ void manager::execute() | |||
if(!web_app.get()) { | |||
set_mod(get_mod()); | |||
} | |||
if(!gettext.get()){ | |||
set_gettext(get_gettext()); | |||
} | |||
if(!workers.get()) { | |||
throw cppcms_error("No workers factory set up"); | |||
} | |||
load_templates(); | |||
web_app->execute(); | |||
} | |||
void manager::load_templates() | |||
{ | |||
string ext=config.sval("templates.ext",".so"); | |||
unsigned len=ext.size(); | |||
vector<string> const &dirs=config.slist("templates.dirs"); | |||
for(vector<string>::const_iterator dir=dirs.begin();dir!=dirs.end();++dir) { | |||
DIR *d=::opendir(dir->c_str()); | |||
if(!d) continue; | |||
for(struct dirent *entry=::readdir(d);entry;entry=::readdir(d)) { | |||
string filename=entry->d_name; | |||
if( filename.size()>len && | |||
filename.substr(filename.size()-len,len) == ext ) | |||
{ | |||
void *handler=::dlopen((*dir + "/" + filename).c_str(),RTLD_LAZY); | |||
if(handler) templates_list.push_back(handler); | |||
} | |||
} | |||
::closedir(d); | |||
} | |||
} | |||
manager::~manager() | |||
{ | |||
for_each(templates_list.begin(),templates_list.end(),::dlclose); | |||
} | |||
void manager::set_worker(base_factory *w) | |||
{ | |||
workers=auto_ptr<base_factory>(w); | |||
@@ -537,6 +573,30 @@ void manager::set_mod(web_application *m) | |||
web_app=auto_ptr<web_application>(m); | |||
} | |||
transtext::trans_factory *manager::get_gettext() | |||
{ | |||
transtext::trans_factory *tmp=NULL; | |||
try{ | |||
tmp=new transtext::trans_factory(); | |||
tmp->load( config.sval ("locale.dir",""), | |||
config.slist("locale.lang_list"), | |||
config.sval ("locale.lang_default",""), | |||
config.slist ("locale.domain_list"), | |||
config.sval ("locale.domain_default","")); | |||
return tmp; | |||
} | |||
catch(...){ | |||
delete tmp; | |||
throw; | |||
} | |||
} | |||
void manager::set_gettext(transtext::trans_factory *s) | |||
{ | |||
gettext=auto_ptr<transtext::trans_factory>(s); | |||
} | |||
manager::manager() | |||
{ | |||
config.load(0,NULL); | |||
@@ -1,5 +1,5 @@ | |||
#ifndef _THREAD_POOL_H | |||
#define _THREAD_POOL_H | |||
#ifndef CPPCMS_MANAGER_H | |||
#define CPPCMS_MANAGER_H | |||
#include <pthread.h> | |||
#include <string> | |||
@@ -11,6 +11,7 @@ | |||
#include "cache_interface.h" | |||
#include "cgi_api.h" | |||
#include "posix_mutex.h" | |||
#include "transtext.h" | |||
#include <boost/shared_ptr.hpp> | |||
@@ -209,6 +210,9 @@ class manager : private boost::noncopyable { | |||
cache_factory *get_cache_factory(); | |||
cgi_api *get_api(); | |||
web_application *get_mod(); | |||
transtext::trans_factory *get_gettext(); | |||
list<void *> templates_list; | |||
void load_templates(); | |||
public: | |||
cppcms_config config; | |||
auto_ptr<cache_factory> cache; | |||
@@ -216,16 +220,20 @@ public: | |||
auto_ptr<base_factory> workers; | |||
auto_ptr<web_application> web_app; | |||
auto_ptr<transtext::trans_factory> gettext; | |||
void set_worker(base_factory *w); | |||
void set_cache(cache_factory *c); | |||
void set_api(cgi_api *a); | |||
void set_mod(web_application *m); | |||
void set_gettext(transtext::trans_factory *); | |||
manager(); | |||
manager(char const *file); | |||
manager(int argc, char **argv); | |||
~manager(); | |||
void execute(); | |||
}; | |||
} | |||
#endif /* _THREAD_POOL_H */ | |||
#endif /* CPPCMS_MANAGER_H */ |
@@ -0,0 +1,8 @@ | |||
noinst_PROGRAMS = test | |||
test_SOURCES = test.cpp | |||
test_LDADD = libcppcmstranstext.la | |||
lib_LTLIBRARIES=libcppcmstranstext.la | |||
libcppcmstranstext_la_SOURCES = transtext.cpp lambda.cpp mo_file.cpp trans_factory.cpp | |||
libcppcmstranstext_la_LDFLAGS = -no-undefined | |||
noinst_HEADERS = mo_file.h | |||
nobase_pkginclude_HEADERS = transtext.h |
@@ -0,0 +1,399 @@ | |||
#include "transtext.h" | |||
#include <cstdlib> | |||
#include <cstring> | |||
#include <ctype.h> | |||
#include <cassert> | |||
#ifdef DEBUG_LAMBDA | |||
#include <iostream> | |||
using namespace std; | |||
#endif | |||
#ifdef DEBUG_LAMBDA | |||
#define LOG(x) x | |||
#else | |||
#define LOG(x) | |||
#endif | |||
namespace cppcms { | |||
namespace transtext { | |||
namespace lambda { | |||
struct identity : public plural { | |||
virtual int operator()(int n) const { LOG(cout<<"id("<<n<<")\n";) return n; }; | |||
}; | |||
struct unary : public plural { | |||
plural *op1; | |||
unary(plural *ptr): op1(ptr) {}; | |||
virtual ~unary() { delete op1; }; | |||
}; | |||
struct binary : public plural { | |||
plural *op1,*op2; | |||
binary(plural *p1,plural *p2): op1(p1),op2(p2) {}; | |||
virtual ~binary() { delete op1; delete op2; }; | |||
}; | |||
struct number : public plural { | |||
int val; | |||
number(int v) : val(v) {}; | |||
virtual int operator()(int n) const { LOG(cout<<"value="<<val<<endl;) return val; }; | |||
}; | |||
#ifdef DEBUG_LAMBDA | |||
#define UNOP(name,oper) \ | |||
struct name: public unary { name(plural *op) : unary(op) {};\ | |||
virtual int operator()(int n) const { int v=(*op1)(n); cerr<<#oper<<v<<endl; return oper(v); }; }; | |||
#define BINOP(name,oper) \ | |||
struct name : public binary { name(plural *p1,plural *p2) : binary(p1,p2) {}; virtual int operator()(int n) const \ | |||
{ int v1=(*op1)(n); int v2=(*op2)(n); cout<<v1<<#oper<<v2<<endl; return v1 oper v2; }; }; | |||
#define BINOPD(name,oper) \ | |||
struct name : public binary { \ | |||
name(plural *p1,plural *p2) : binary(p1,p2) {};\ | |||
virtual int operator()(int n) const { int v1=(*op1)(n); int v2=(*op2)(n); \ | |||
cout<<v1<<#oper<<v2<<endl; \ | |||
return v2==0 ? 0 : v1 oper v2; };\ | |||
}; | |||
#else | |||
#define UNOP(name,oper) \ | |||
struct name: public unary { name(plural *op) : unary(op) {}; virtual int operator()(int n) const { return oper (*op1)(n); }; }; | |||
#define BINOP(name,oper) \ | |||
struct name : public binary { name(plural *p1,plural *p2) : binary(p1,p2) {}; virtual int operator()(int n) const { return (*op1)(n) oper (*op2)(n); }; }; | |||
#define BINOPD(name,oper) \ | |||
struct name : public binary { \ | |||
name(plural *p1,plural *p2) : binary(p1,p2) {};\ | |||
virtual int operator()(int n) const { int v1=(*op1)(n); int v2=(*op2)(n); return v2==0 ? 0 : v1 oper v2; };\ | |||
}; | |||
#endif | |||
enum { END = 0 , SHL = 256, SHR, GTE,LTE, EQ, NEQ, AND, OR, NUM, VARIABLE }; | |||
UNOP(l_not,!) | |||
UNOP(minus,-) | |||
UNOP(bin_not,~) | |||
BINOP(mul,*) | |||
BINOPD(div,/) | |||
BINOPD(mod,%) | |||
static int level10[]={3,'*','/','%'}; | |||
BINOP(add,+) | |||
BINOP(sub,-) | |||
static int level9[]={2,'+','-'}; | |||
BINOP(shl,<<) | |||
BINOP(shr,>>) | |||
static int level8[]={2,SHL,SHR}; | |||
BINOP(gt,>) | |||
BINOP(lt,<) | |||
BINOP(gte,>=) | |||
BINOP(lte,<=) | |||
static int level7[]={4,'<','>',GTE,LTE}; | |||
BINOP(eq,==) | |||
BINOP(neq,!=) | |||
static int level6[]={2,EQ,NEQ}; | |||
BINOP(bin_and,&) | |||
static int level5[]={1,'&'}; | |||
BINOP(bin_xor,^) | |||
static int level4[]={1,'^'}; | |||
BINOP(bin_or,|) | |||
static int level3[]={1,'|'}; | |||
BINOP(l_and,&&) | |||
static int level2[]={1,AND}; | |||
BINOP(l_or,||) | |||
static int level1[]={1,OR}; | |||
struct conditional : public plural { | |||
plural *op1,*op2,*op3; | |||
conditional(plural *p1,plural *p2,plural *p3): op1(p1),op2(p2),op3(p3) {}; | |||
virtual ~conditional() { delete op1; delete op2; delete op3; }; | |||
virtual int operator()(int n) const { return (*op1)(n) ? (*op2)(n) : (*op3)(n); }; | |||
}; | |||
plural *bin_factory(int value,plural *left,plural *right) | |||
{ | |||
switch(value) { | |||
case '/': return new div(left,right); | |||
case '*': return new mul(left,right); | |||
case '%': return new mod(left,right); | |||
case '+': return new add(left,right); | |||
case '-': return new sub(left,right); | |||
case SHL: return new shl(left,right); | |||
case SHR: return new shr(left,right); | |||
case '>': return new gt(left,right); | |||
case '<': return new lt(left,right); | |||
case GTE: return new gte(left,right); | |||
case LTE: return new lte(left,right); | |||
case EQ: return new eq(left,right); | |||
case NEQ: return new neq(left,right); | |||
case '&': return new bin_and(left,right); | |||
case '^': return new bin_xor(left,right); | |||
case '|': return new bin_or (left,right); | |||
case AND: return new l_and(left,right); | |||
case OR: return new l_or(left,right); | |||
default: | |||
delete left; | |||
delete right; | |||
return NULL; | |||
} | |||
} | |||
plural *un_factory(int value,plural *op) | |||
{ | |||
switch(value) { | |||
case '!': return new l_not(op); | |||
case '~': return new bin_not(op); | |||
case '-': return new minus(op); | |||
default: | |||
delete op; | |||
return NULL; | |||
} | |||
} | |||
static inline bool is_in(int v,int *p) | |||
{ | |||
int len=*p; | |||
p++; | |||
while(len && *p!=v) { p++;len--; } | |||
return len; | |||
} | |||
class tockenizer { | |||
char const *text; | |||
int pos; | |||
int next_tocken; | |||
int int_value; | |||
void step() | |||
{ | |||
while(text[pos] && isblank(text[pos])) pos++; | |||
char const *ptr=text+pos; | |||
char *tmp_ptr; | |||
if(strncmp(ptr,"<<",2)==0) { pos+=2; next_tocken=SHL; } | |||
else if(strncmp(ptr,">>",2)==0) { pos+=2; next_tocken=SHR; } | |||
else if(strncmp(ptr,"&&",2)==0) { pos+=2; next_tocken=AND; } | |||
else if(strncmp(ptr,"||",2)==0) { pos+=2; next_tocken=OR; } | |||
else if(strncmp(ptr,"<=",2)==0) { pos+=2; next_tocken=LTE; } | |||
else if(strncmp(ptr,">=",2)==0) { pos+=2; next_tocken=GTE; } | |||
else if(strncmp(ptr,"==",2)==0) { pos+=2; next_tocken=EQ; } | |||
else if(strncmp(ptr,"!=",2)==0) { pos+=2; next_tocken=NEQ; } | |||
else if(*ptr=='n') { pos++; next_tocken=VARIABLE; } | |||
else if(isdigit(*ptr)) { int_value=strtol(text+pos,&tmp_ptr,0); pos=tmp_ptr-text; next_tocken=NUM; } | |||
else if(*ptr=='\0') { next_tocken=0; } | |||
else { next_tocken=*ptr; pos++; } | |||
#ifdef DEBUG_LAMBDA | |||
if(next_tocken>=' ' && next_tocken<=127) | |||
std::cout<<"Tocken:"<<(char)next_tocken<<'\n'; | |||
else if(next_tocken==NUM) | |||
std::cout<<"Number:"<<int_value<<"\n"; | |||
else if(next_tocken==VARIABLE) | |||
std::cout<<"Variale\n"; | |||
else | |||
std::cout<<"TockenID:"<<next_tocken<<'\n'; | |||
#endif | |||
} | |||
public: | |||
tockenizer(char const *s) { text=s; pos=0; step(); }; | |||
int get(int *val=NULL){ | |||
int iv=int_value; | |||
int res=next_tocken; | |||
step(); | |||
if(val && res==NUM){ | |||
*val=iv; | |||
} | |||
return res; | |||
}; | |||
int next(int *val=NULL) { | |||
if(val && next_tocken==NUM) { | |||
*val=int_value; | |||
return NUM; | |||
} | |||
return next_tocken; | |||
} | |||
}; | |||
#define BINARY_EXPR(expr,hexpr,list) \ | |||
plural *expr() \ | |||
{ \ | |||
LOG(std::cout<< #expr<<'\n';) \ | |||
plural *op1=NULL,*op2=NULL; \ | |||
if((op1=hexpr())==NULL) goto error; \ | |||
while(is_in(t.next(),list)) { \ | |||
LOG(cout<<"Concate with "<<(char)t.next()<<endl;) \ | |||
int o=t.get(); \ | |||
if((op2=hexpr())==NULL) goto error; \ | |||
op1=bin_factory(o,op1,op2); \ | |||
assert(op1); \ | |||
op2=NULL; \ | |||
} \ | |||
return op1; \ | |||
error: \ | |||
delete op1; \ | |||
delete op2; \ | |||
return NULL; \ | |||
} | |||
struct parser { | |||
tockenizer &t; | |||
parser(tockenizer &tin) : t(tin) {}; | |||
plural *value_expr() | |||
{ | |||
LOG(std::cout<<"Value\n";) | |||
plural *op=NULL; | |||
if(t.next()=='(') { | |||
LOG(std::cout<<"Value ()\n";) | |||
t.get(); | |||
if((op=cond_expr())==NULL) goto error; | |||
if(t.get()!=')') goto error; | |||
return op; | |||
} | |||
else if(t.next()==NUM) { | |||
int value; | |||
t.get(&value); | |||
LOG(std::cout<<"Value ("<<value<<")\n";) | |||
return new number(value); | |||
} | |||
else if(t.next()==VARIABLE) { | |||
t.get(); | |||
LOG(std::cout<<"Value (n)\n";) | |||
return new identity(); | |||
} | |||
return NULL; | |||
error: | |||
delete op; | |||
return NULL; | |||
}; | |||
plural *un_expr() | |||
{ | |||
plural *op1=NULL; | |||
static int level_unary[]={3,'-','!','~'}; | |||
if(is_in(t.next(),level_unary)) { | |||
int op=t.get(); | |||
if((op1=un_expr())==NULL) | |||
goto error; | |||
switch(op) { | |||
case '-': | |||
LOG(std::cout<<"Unary(-)\n";) | |||
return new minus(op1); | |||
case '!': | |||
LOG(std::cout<<"Unary(!)\n";) | |||
return new l_not(op1); | |||
case '~': | |||
LOG(std::cout<<"Unary(~)\n";) | |||
return new bin_not(op1); | |||
default: | |||
goto error; | |||
} | |||
} | |||
else { | |||
LOG(std::cout<<"Unary... none\n";) | |||
return value_expr(); | |||
} | |||
error: | |||
delete op1; | |||
return NULL; | |||
}; | |||
BINARY_EXPR(l10,un_expr,level10); | |||
BINARY_EXPR(l9,l10,level9); | |||
BINARY_EXPR(l8,l9,level8); | |||
BINARY_EXPR(l7,l8,level7); | |||
BINARY_EXPR(l6,l7,level6); | |||
BINARY_EXPR(l5,l6,level5); | |||
BINARY_EXPR(l4,l5,level4); | |||
BINARY_EXPR(l3,l4,level3); | |||
BINARY_EXPR(l2,l3,level2); | |||
BINARY_EXPR(l1,l2,level1); | |||
plural *cond_expr() | |||
{ | |||
plural *cond=NULL,*case1=NULL,*case2=NULL; | |||
if((cond=l1())==NULL) | |||
goto error; | |||
if(t.next()=='?') { | |||
LOG(std::cout<<"Condtion... make\n";) | |||
t.get(); | |||
if((case1=cond_expr())==NULL) | |||
goto error; | |||
if(t.get()!=':') | |||
goto error; | |||
if((case2=cond_expr())==NULL) | |||
goto error; | |||
} | |||
else { | |||
LOG(std::cout<<"Condtion... none\n";) | |||
return cond; | |||
} | |||
return new conditional(cond,case1,case2); | |||
error: | |||
delete cond; | |||
delete case1; | |||
delete case2; | |||
return NULL; | |||
} | |||
public: | |||
plural *compile() | |||
{ | |||
plural *res=cond_expr(); | |||
if(res && t.next()!=END) { | |||
delete res; | |||
return NULL; | |||
}; | |||
return res; | |||
} | |||
}; | |||
#ifdef DEBUG_LAMBDA | |||
string printtree(plural *p) | |||
{ | |||
identity *pi=dynamic_cast<identity *>(p); | |||
if(pi) { return string("n"); }; | |||
unary *pu=dynamic_cast<unary *>(p); | |||
if(pu) { return string(typeid(*pu).name())+"("+printtree(pu->op1)+")"; } | |||
binary *pb=dynamic_cast<binary *>(p); | |||
if(pb) { return string(typeid(*pb).name())+"("+printtree(pb->op1)+","+printtree(pb->op2)+")"; }; | |||
number *pn=dynamic_cast<number *>(p); | |||
if(pn) { char buf[32]; snprintf(buf,sizeof(buf),"(%d)",pn->val); return string(buf); }; | |||
conditional *pc=dynamic_cast<conditional *>(p); | |||
if(pc) { return "("+printtree(pc->op1)+")?("+printtree(pc->op2)+"):("+printtree(pc->op3)+")"; }; | |||
return string("PLURAL"); | |||
} | |||
#endif | |||
plural *compile(char const *str) | |||
{ | |||
LOG(cout<<"Compiling:["<<str<<"]\n";) | |||
tockenizer t(str); | |||
parser p(t); | |||
plural *tmp=p.compile(); | |||
#ifdef DEBUG_LAMBDA | |||
cout<<printtree(tmp)<<endl; | |||
for(int i=0;i<15;i++) { | |||
cout<<"f("<<i<<")="<<(*tmp)(i)<<endl; | |||
} | |||
#endif | |||
return tmp; | |||
} | |||
} // namespace lambda | |||
} //Öł Namespace gettext | |||
} // namespace cppcms | |||
@@ -0,0 +1,37 @@ | |||
# SOME DESCRIPTIVE TITLE. | |||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER | |||
# This file is distributed under the same license as the PACKAGE package. | |||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. | |||
# | |||
#, fuzzy | |||
msgid "" | |||
msgstr "" | |||
"Project-Id-Version: PACKAGE VERSION\n" | |||
"Report-Msgid-Bugs-To: \n" | |||
"POT-Creation-Date: 2008-04-25 23:28+0300\n" | |||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | |||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | |||
"Language-Team: LANGUAGE <LL@li.org>\n" | |||
"MIME-Version: 1.0\n" | |||
"Content-Type: text/plain; charset=utf-8\n" | |||
"Content-Transfer-Encoding: 8bit\n" | |||
"Plural-Forms: nplurals=4; plural= n!=1 ? n>10 ? 3 : n==2 ? 2 : 1 : 0 ;\n" | |||
msgid "hello\n" | |||
msgstr "shalom\n" | |||
msgid "LTR" | |||
msgstr "RTL" | |||
msgid "LANG" | |||
msgstr "Hebrew" | |||
msgid "Hello %1%, %2%" | |||
msgstr "Shalom %1%, %2%" | |||
msgid "passed one day" | |||
msgid_plural "passed %d days" | |||
msgstr[0] "avar yom ehad" | |||
msgstr[1] "avru %d yamim" | |||
msgstr[2] "avru yomaim" | |||
msgstr[3] "avru %d yom" |
@@ -0,0 +1,339 @@ | |||
#include "mo_file.h" | |||
/******************************************************** | |||
* This file was adopted by Artyom Tonkikh for * | |||
* purouses of thread safe gettext implementation * | |||
********************************************************/ | |||
/* | |||
The MIT License | |||
Copyright (c) 2007 Jonathan Blow (jon [at] number-none [dot] com) | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in | |||
all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
THE SOFTWARE. | |||
*/ | |||
/* | |||
This code is designed to be a very simple drop-in method for reading .mo | |||
files, which are a standard format for storing text strings to help with | |||
internationalization. | |||
For more information, see: http://www.gnu.org/software/gettext/manual/gettext.html | |||
Unfortunately the gettext code is unwieldy. For a simple program like | |||
a video game, we just want to look up some strings in a pre-baked file, | |||
which is what this code does. | |||
We assume that you started with a .po file (which is a human-readable format | |||
containing all the strings) and then compiled it into a .mo file (which is | |||
a binary format that can be efficiently read into memory all in one chunk). | |||
This code then reads straight from the .mo file, so there is no extra | |||
string allocation and corresponding memory fragmentation. | |||
You can generate a .mo file from a .po file using programs such as poedit | |||
or msgfmt. | |||
This code assumes you compiled the hash table into the .mo file. It also | |||
doesn't attempt to care about the encoding of your text; it assumes you | |||
already know what that is. (I use UTF-8 which seems like the only sane | |||
choice). | |||
There's no good reason that this is a C++ file, rather than a C file; I just | |||
wrote it that way originally. You can trivially get rid of the C++-ness if | |||
you want it to compile as straight C. | |||
Send questions to jon [at] number-none [dot] com. | |||
21 December 2007 | |||
*/ | |||
// From the header file: | |||
namespace thr_safe_gettext { | |||
struct Localization_Text { | |||
Localization_Text(char const*); | |||
~Localization_Text(); | |||
void abort(); | |||
void *mo_data; | |||
int reversed; | |||
int num_strings; | |||
int original_table_offset; | |||
int translated_table_offset; | |||
int hash_num_entries; | |||
int hash_offset; | |||
}; | |||
// From the .cpp fie: | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <assert.h> | |||
#include <sys/stat.h> | |||
#include <string.h> | |||
// This is just the common hashpjw routine, pasted in: | |||
#define HASHWORDBITS 32 | |||
inline unsigned long hashpjw(const char *str_param) { | |||
unsigned long hval = 0; | |||
unsigned long g; | |||
const char *s = str_param; | |||
while (*s) { | |||
hval <<= 4; | |||
hval += (unsigned char) *s++; | |||
g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4)); | |||
if (g != 0) { | |||
hval ^= g >> (HASHWORDBITS - 8); | |||
hval ^= g; | |||
} | |||
} | |||
return hval; | |||
} | |||
// Here is the actual code: | |||
// Read an entire file into memory. Replace this with any equivalent function. | |||
#ifdef WIN32 | |||
#define fileno _fileno | |||
#define fstat _fstat | |||
#define stat _stat | |||
#endif | |||
int os_read_entire_file(FILE *file, void **data_return) { | |||
assert(file); | |||
int descriptor = fileno(file); | |||
struct stat file_stats; | |||
int result = fstat(descriptor, &file_stats); | |||
if (result == -1) return -1; | |||
int length = file_stats.st_size; | |||
unsigned char *data = new unsigned char[length]; | |||
fseek(file, 0, SEEK_SET); | |||
int success = fread((void *)data, length, 1, file); | |||
if (success < 1) { | |||
delete [] data; | |||
return -1; | |||
} | |||
*data_return = data; | |||
return length; | |||
} | |||
// Swap the endianness of a 4-byte word. | |||
// On some architectures you can replace my_swap4 with an inlined instruction. | |||
inline unsigned long my_swap4(unsigned long result) { | |||
unsigned long c0 = (result >> 0) & 0xff; | |||
unsigned long c1 = (result >> 8) & 0xff; | |||
unsigned long c2 = (result >> 16) & 0xff; | |||
unsigned long c3 = (result >> 24) & 0xff; | |||
return (c0 << 24) | (c1 << 16) | (c2 << 8) | c3; | |||
} | |||
inline int read4_from_offset(Localization_Text *loc, int offset) { | |||
unsigned long *ptr = (unsigned long *)(((char *)loc->mo_data) + offset); | |||
if (loc->reversed) { | |||
return my_swap4(*ptr); | |||
} else { | |||
return *ptr; | |||
} | |||
} | |||
inline char *get_source_string(Localization_Text *loc, int index,int *len=NULL) { | |||
int addr_offset = loc->original_table_offset + 8 * index; | |||
int string_len = read4_from_offset(loc, addr_offset); | |||
int string_offset = read4_from_offset(loc, addr_offset+4); | |||
char *t = ((char *)loc->mo_data) + string_offset; | |||
if(len) *len=string_len; | |||
return t; | |||
} | |||
inline char *get_translated_string(Localization_Text *loc, int index, int *len=NULL) { | |||
int addr_offset = loc->translated_table_offset + 8 * index; | |||
if(len) *len=read4_from_offset(loc, addr_offset); | |||
int string_offset = read4_from_offset(loc, addr_offset+4); | |||
char *t = ((char *)loc->mo_data) + string_offset; | |||
return t; | |||
} | |||
static bool label_matches(Localization_Text *loc, char const *s, int index) { | |||
char *t = get_source_string(loc, index); | |||
if (strcmp(s, t) == 0) return true; | |||
return false; | |||
} | |||
inline int get_target_index(Localization_Text *loc, char const *s) { | |||
unsigned long V = hashpjw(s); | |||
int S = loc->hash_num_entries; | |||
int hash_cursor = V % S; | |||
int orig_hash_cursor = hash_cursor; | |||
int increment = 1 + (V % (S - 2)); | |||
while (1) { | |||
unsigned int index = read4_from_offset(loc, loc->hash_offset + 4 * hash_cursor); | |||
if (index == 0) break; | |||
index--; // Because entries in the table are stored +1 so that 0 means empty. | |||
if (label_matches(loc, s, index)) return index; | |||
// if (index_empty(loc, index)) break; | |||
hash_cursor += increment; | |||
hash_cursor %= S; | |||
if (hash_cursor == orig_hash_cursor) break; | |||
} | |||
return -1; | |||
} | |||
Localization_Text::Localization_Text(char const *filename) { | |||
mo_data = NULL; | |||
num_strings = 0; | |||
original_table_offset = 0; | |||
translated_table_offset = 0; | |||
hash_num_entries = 0; | |||
hash_offset = 0; | |||
FILE *f = fopen(filename, "rb"); | |||
if (!f) return; | |||
void *data; | |||
int length = os_read_entire_file(f, &data); // Replace this with any function that will read an entire file and return it in a block of newly-allocated memory. | |||
fclose(f); | |||
if (length < 0) return; // os_read_entire_file returns -1 on failure. | |||
if (length < 24) { // There has to be at least this much in the header... | |||
abort(); | |||
return; | |||
} | |||
mo_data = data; | |||
unsigned long *long_ptr = (unsigned long *)data; | |||
const unsigned long TARGET_MAGIC = 0x950412DE; | |||
const unsigned long TARGET_MAGIC_REVERSED = 0xDE120495; | |||
unsigned long magic = long_ptr[0]; | |||
if (magic == TARGET_MAGIC) { | |||
reversed = 0; | |||
} else if (magic == TARGET_MAGIC_REVERSED) { | |||
reversed = 1; | |||
} else { | |||
abort(); | |||
return; | |||
} | |||
num_strings = read4_from_offset(this, 8); | |||
original_table_offset = read4_from_offset(this, 12); | |||
translated_table_offset = read4_from_offset(this, 16); | |||
hash_num_entries = read4_from_offset(this, 20); | |||
hash_offset = read4_from_offset(this, 24); | |||
if (hash_num_entries == 0) { // We expect a hash table to be there; if it's not, bail. | |||
abort(); | |||
return; | |||
} | |||
} | |||
void Localization_Text::abort() { | |||
delete [] (char *)mo_data; | |||
mo_data = NULL; | |||
return; | |||
} | |||
Localization_Text::~Localization_Text() { | |||
if (mo_data) delete [] ((char *)mo_data); | |||
} | |||
// | |||
// Call text_lookup to go from a source string to a translated string. | |||
// This is the main entry point that you would use. (The only other | |||
// entry point is to construct a Localization_Text() to initialize | |||
// things.) | |||
// | |||
extern "C" char const *thr_safe_gettext_text_lookup(void *the_localization_text, char const *s,int std_id) | |||
{ | |||
Localization_Text *loc = (Localization_Text *)the_localization_text; | |||
if (!loc || !loc->mo_data) return NULL; | |||
int len; | |||
int target_index = get_target_index(loc, s); | |||
if (target_index == -1) return NULL; // Maybe we want to log an error? | |||
char *tmp=get_translated_string(loc, target_index, &len); | |||
char *p=tmp; | |||
while(std_id>0 && p-tmp<len) { | |||
if(*p) | |||
p++; | |||
else{ | |||
p++; | |||
std_id--; | |||
} | |||
} | |||
if(p-tmp<len) | |||
return p; | |||
else | |||
return NULL; | |||
} | |||
extern "C" void *thr_safe_gettext_load(char const *mo_file) | |||
{ | |||
return new Localization_Text(mo_file); | |||
} | |||
extern "C" void thr_safe_gettext_unload(void *ptr) | |||
{ | |||
delete (Localization_Text*)ptr; | |||
} | |||
} // END OF thr_safe_gettext namespace |
@@ -0,0 +1,12 @@ | |||
#ifndef MO_FILE | |||
#define MO_FILE | |||
extern "C" { | |||
void *thr_safe_gettext_load(char const *mo_file); | |||
void thr_safe_gettext_unload(void *ptr); | |||
char const *thr_safe_gettext_text_lookup(void *the_localization_text, char const *s,int std_id); | |||
}; | |||
#endif |
@@ -0,0 +1,28 @@ | |||
#include "transtext.h" | |||
#include <stdio.h> | |||
#include <cstdlib> | |||
using namespace cppcms::transtext; | |||
int main(int argc,char **argv) | |||
{ | |||
trans_factory tf; | |||
vector<string> domains; | |||
domains.push_back("test"); | |||
vector<string> langs; | |||
langs.push_back("en"); | |||
langs.push_back("he"); | |||
tf.load("./locale",langs,"",domains,""); | |||
int i; | |||
for(i=0;i<15;i++) { | |||
printf(tf.get("he","").ngettext("passed one day","passed %d days",i),i); | |||
putchar('\n'); | |||
printf(tf.get("en","").ngettext("passed one day","passed %d days",i),i); | |||
putchar('\n'); | |||
} | |||
return 0; | |||
} |
@@ -0,0 +1,67 @@ | |||
#include "transtext.h" | |||
using namespace std; | |||
namespace cppcms { | |||
namespace transtext { | |||
static const trans default_trans; | |||
trans const &trans_factory::get(string lang,string domain) const | |||
{ | |||
map<string,map<string,boost::shared_ptr<trans> > >::const_iterator p; | |||
if(lang.empty()) lang=default_lang; | |||
if(domain.empty()) domain=default_domain; | |||
if((p=langs.find(lang))==langs.end()) { | |||
return default_trans; | |||
} | |||
else { | |||
map<string,boost::shared_ptr<trans> >::const_iterator p2; | |||
if((p2=p->second.find(domain))==p->second.end()) { | |||
return default_trans; | |||
} | |||
return *(p2->second); | |||
} | |||
} | |||
void trans_factory::load( | |||
string const &locale_dir, | |||
vector<string> const &lang_list, | |||
string const &lang_def, | |||
vector<string> const &domain_list, | |||
string const &domain_def) | |||
{ | |||
if(lang_list.empty() || domain_list.empty()) { | |||
return; | |||
} | |||
default_lang=lang_def; | |||
default_domain=domain_def; | |||
if(default_lang.empty()) default_lang=lang_list[0]; | |||
if(default_domain.empty()) default_domain=domain_list[0]; | |||
typedef vector<string>::const_iterator it_t; | |||
for(it_t lang=lang_list.begin(),le=lang_list.end();lang!=le;++lang) | |||
{ | |||
for(it_t domain=domain_list.begin(),de=domain_list.end();de!=domain;++domain) { | |||
boost::shared_ptr<trans_thread_safe> tr(new trans_thread_safe()); | |||
tr->load(lang->c_str(),domain->c_str(),locale_dir.c_str()); | |||
langs[*lang][*domain]=tr; | |||
} | |||
} | |||
} | |||
trans_factory::~trans_factory() | |||
{ | |||
} | |||
} // namespace transtext | |||
} // namespace cppcms |
@@ -0,0 +1,93 @@ | |||
#include "transtext.h" | |||
#include "mo_file.h" | |||
#include <cstring> | |||
#include <cstdlib> | |||
#include <ctype.h> | |||
#include <string> | |||
#include <iostream> | |||
namespace cppcms { | |||
namespace transtext { | |||
using namespace lambda; | |||
using namespace std; | |||
struct default_plural : public plural { | |||
int operator()(int n) const { return n==1 ? 0 : 1; }; | |||
~default_plural() {}; | |||
}; | |||
trans_thread_safe::trans_thread_safe() | |||
{ | |||
data=NULL; | |||
converter=new default_plural; | |||
} | |||
trans_thread_safe::~trans_thread_safe() | |||
{ | |||
if(data){ | |||
thr_safe_gettext_unload(data); | |||
} | |||
delete converter; | |||
} | |||
static plural *get_converter(char const *header) | |||
{ | |||
char const *ptr,*ptr2; | |||
string tmp; | |||
plural *result; | |||
if(!header) goto error; | |||
ptr=strstr(header,"Plural-Forms:"); | |||
if(!ptr) goto error; | |||
ptr=strstr(ptr,"plural"); | |||
if(!ptr) goto error; | |||
if(ptr[6]=='s') { // Prevent detecting plurals as plural | |||
ptr=strstr(ptr+6,"plural"); | |||
} | |||
if(!ptr) goto error; | |||
ptr+=6; | |||
while(*ptr && isblank(*ptr)) ptr++; | |||
if(*ptr!='=') goto error; | |||
ptr++; | |||
ptr2=strstr(ptr,";"); | |||
if(!ptr2) goto error; | |||
tmp.append(ptr,ptr2-ptr); | |||
result=compile(tmp.c_str()); | |||
if(!result) goto error; | |||
return result; | |||
error: | |||
return new default_plural(); | |||
} | |||
void trans_thread_safe::load(char const * locale,char const *domain_name, char const * dirname) | |||
{ | |||
if(data) thr_safe_gettext_unload(data); | |||
string path_to_mo_file=string(dirname)+"/" + locale +"/LC_MESSAGES/" + domain_name + ".mo"; | |||
data=thr_safe_gettext_load(path_to_mo_file.c_str()); | |||
char const *header=thr_safe_gettext_text_lookup(data,"",0); | |||
delete converter; | |||
converter=get_converter(header); | |||
} | |||
char const *trans_thread_safe::gettext(char const *s) const | |||
{ | |||
char const *t=thr_safe_gettext_text_lookup(data,s,0); | |||
return t ? t : s; | |||
} | |||
char const *trans_thread_safe::ngettext(char const *s,char const *p,int n) const | |||
{ | |||
int idx=(*converter)(n); | |||
if(idx<0) idx=0; | |||
char const *t=thr_safe_gettext_text_lookup(data,s,idx); | |||
if(!t) { | |||
return n==1 ? s : p; | |||
} | |||
return t; | |||
} | |||
} // namespace transtex | |||
} // namespace cppcms |
@@ -0,0 +1,66 @@ | |||
#ifndef CPPCMS_TRANSTEXT_H | |||
#define CPPCMS_TRANSTEXT_H | |||
#include <map> | |||
#include <string> | |||
#include <vector> | |||
#include <boost/shared_ptr.hpp> | |||
namespace cppcms { | |||
namespace transtext { | |||
using namespace std; | |||
namespace lambda { | |||
struct plural { // INTERFACE | |||
virtual int operator()(int n) const = 0; | |||
virtual ~plural(){}; | |||
}; | |||
plural *compile(char const *expr); | |||
}; // END OF NAMESPACE LAMBDA | |||
class trans { | |||
public: | |||
trans() {}; | |||
virtual ~trans() {}; | |||
virtual void load(char const * locale,char const *domain_name, char const * dirname) {}; | |||
virtual char const *gettext(char const *s) const { return s; }; | |||
virtual char const *ngettext(char const *s,char const *p,int n) const { return n==1 ? s:p; }; | |||
char const *operator()(char const *str) const { return gettext(str); }; | |||
char const *operator()(char const *single,char const *plural,int n) const { return ngettext(single,plural,n); }; | |||
}; | |||
class trans_thread_safe : public trans { | |||
lambda::plural *converter; | |||
void *data; | |||
public: | |||
trans_thread_safe(); | |||
virtual ~trans_thread_safe(); | |||
virtual void load(char const * locale,char const *domain_name, char const * dirname); | |||
virtual char const *gettext(char const *s) const; | |||
virtual char const *ngettext(char const *single,char const *plural,int n) const; | |||
lambda::plural const & num2idx_conv() const { return *converter; }; | |||
int num2idx(int n) const { return (*converter)(n); }; | |||
}; | |||
class trans_factory { | |||
map<string,map<string,boost::shared_ptr<trans> > > langs; | |||
string default_lang; | |||
string default_domain; | |||
public: | |||
trans const &get(string lang,string domain) const; | |||
void load( string const &locale_dir, | |||
vector<string> const &lang_list, | |||
string const &lang_def, | |||
vector<string> const &domain_list, | |||
string const &domain_def); | |||
~trans_factory(); | |||
}; | |||
} // namespace transtext | |||
} // namespace cppcms | |||
#endif |
@@ -1,6 +1,7 @@ | |||
#include "worker_thread.h" | |||
#include "global_config.h" | |||
#include "thread_cache.h" | |||
#include "base_view.h" | |||
#include <boost/iostreams/filtering_stream.hpp> | |||
#include <boost/iostreams/filter/gzip.hpp> | |||
@@ -10,22 +11,63 @@ | |||
using namespace cgicc; | |||
namespace cppcms { | |||
worker_thread::worker_thread(manager const &s) : | |||
url(this), | |||
app(s), | |||
cache(this), | |||
cout(&(this->out_buf)) | |||
{ | |||
caching_module=app.cache->get(); | |||
static const transtext::trans tr; | |||
gt=&tr; | |||
} ; | |||
worker_thread::~worker_thread() | |||
{ | |||
app.cache->del(caching_module); | |||
} | |||
void worker_thread::main() | |||
{ | |||
out="<h1>Hello World</h2>\n"; | |||
cout<<"<h1>Hello World</h2>\n"; | |||
} | |||
void worker_thread::set_header(HTTPHeader *h) | |||
{ | |||
response_header=auto_ptr<HTTPHeader>(h); | |||
}; | |||
void worker_thread::add_header(string s) { | |||
other_headers.push_back(s); | |||
}; | |||
void worker_thread::set_cookie(cgicc::HTTPCookie const &c) | |||
{ | |||
response_header->setCookie(c); | |||
} | |||
void worker_thread::set_lang(string const &s) | |||
{ | |||
lang=s; | |||
gt=&app.gettext->get(s,""); | |||
} | |||
transtext::trans const *worker_thread::domain_gettext(string const &domain) | |||
{ | |||
return &app.gettext->get(lang,domain); | |||
} | |||
HTTPHeader &worker_thread::header() | |||
{ | |||
return *response_header; | |||
} | |||
void worker_thread::run(cgicc_connection &cgi_conn) | |||
{ | |||
cgi=&cgi_conn.cgi(); | |||
env=&(cgi->getEnvironment()); | |||
ostream &cout=cgi_conn.cout(); | |||
ostream &cgi_out=cgi_conn.cout(); | |||
other_headers.clear(); | |||
out.clear(); | |||
out.reserve(app.config.lval("server.buffer_reserve",16000)); | |||
cache.reset(); | |||
set_lang(""); | |||
out_buf.str(""); | |||
set_header(new HTTPHTMLHeader); | |||
@@ -48,10 +90,12 @@ void worker_thread::run(cgicc_connection &cgi_conn) | |||
} | |||
catch(std::exception const &e) { | |||
string msg=e.what(); | |||
set_header(new HTTPStatusHeader(500,msg)); | |||
out="<html><body><p>"+msg+"</p><body></html>"; | |||
cgi_out<<HTTPStatusHeader(500,msg); | |||
cgi_out<<"<html><body><p>"+msg+"</p><body></html>"; | |||
gzip=gzip_done=false; | |||
other_headers.clear(); | |||
out_buf.str(""); | |||
return; | |||
} | |||
if(app.config.lval("server.disable_xpowered_by",0)==0) { | |||
@@ -59,53 +103,62 @@ void worker_thread::run(cgicc_connection &cgi_conn) | |||
} | |||
for(list<string>::iterator h=other_headers.begin();h!=other_headers.end();h++) { | |||
cout<<*h<<"\n"; | |||
cgi_out<<*h<<"\n"; | |||
} | |||
string out=out_buf.str(); | |||
out_buf.str(""); | |||
if(gzip) { | |||
if(out.size()>0) { | |||
if(gzip_done){ | |||
cout<<"Content-Length: "<<out.size()<<"\n"; | |||
cgi_out<<"Content-Length: "<<out.size()<<"\n"; | |||
} | |||
cout<<"Content-Encoding: gzip\n"; | |||
cout<<*response_header; | |||
cgi_out<<"Content-Encoding: gzip\n"; | |||
cgi_out<<*response_header; | |||
if(gzip_done) { | |||
cout<<out; | |||
cgi_out<<out; | |||
} | |||
else{ | |||
long level=app.config.lval("gzip.level",-1); | |||
long length=app.config.lval("gzip.buffer",-1); | |||
deflate(out,cout,level,length); | |||
deflate(out,cgi_out,level,length); | |||
} | |||
} | |||
else { | |||
cout<<*response_header; | |||
cgi_out<<*response_header; | |||
} | |||
} | |||
else { | |||
cout<<"Content-Length: "<<out.size()<<"\n"; | |||
cout<<*response_header; | |||
cout<<out; | |||
cgi_out<<"Content-Length: "<<out.size()<<"\n"; | |||
cgi_out<<*response_header; | |||
cgi_out<<out; | |||
} | |||
// Clean Up | |||
out.clear(); | |||
other_headers.clear(); | |||
response_header.reset(); | |||
cgi=NULL; | |||
} | |||
void worker_thread::init_internal() | |||
void worker_thread::render(string tmpl,string name,base_content &content,ostream &out ) | |||
{ | |||
caching_module=app.cache->get(); | |||
} | |||
worker_thread::~worker_thread() | |||
using cppcms::details::views_storage; | |||
base_view::settings s(this,&out); | |||
auto_ptr<base_view> p(views_storage::instance().fetch_view(tmpl,name,s,&content)); | |||
if(!p.get()) throw cppcms_error("Template `"+name+"' not found in template set `" + tmpl +"'"); | |||
p->render(); | |||
}; | |||
void worker_thread::render(string tmpl,string name,base_content &content) | |||
{ | |||
app.cache->del(caching_module); | |||
} | |||
render(tmpl,name,content,cout); | |||
}; | |||
void worker_thread::render(string name,base_content &content,ostream &o) | |||
{ | |||
render(current_template,name,content,o); | |||
}; | |||
void worker_thread::render(string name,base_content &content) | |||
{ | |||
render(current_template,name,content,cout); | |||
}; | |||
} | |||
@@ -17,10 +17,12 @@ | |||
#include "cache_interface.h" | |||
#include "base_cache.h" | |||
#include "cgicc_connection.h" | |||
#include "transtext.h" | |||
namespace cppcms { | |||
class manager; | |||
class base_content; | |||
using namespace std; | |||
using cgicc::CgiEnvironment; | |||
@@ -28,41 +30,64 @@ using cgicc::Cgicc; | |||
using cgicc::HTTPHeader; | |||
class worker_thread: private boost::noncopyable { | |||
friend class url_parser; | |||
friend class cache_iface; | |||
friend class url_parser; | |||
friend class cache_iface; | |||
friend class base_view; | |||
list<string> other_headers; | |||
base_cache *caching_module; | |||
bool gzip; | |||
bool gzip_done; | |||
stringbuf out_buf; | |||
transtext::trans const *gt; | |||
string lang; | |||
auto_ptr<HTTPHeader> response_header; | |||
protected: | |||
url_parser url; | |||
manager const &app; | |||
Cgicc *cgi; | |||
CgiEnvironment const *env; | |||
auto_ptr<HTTPHeader> response_header; | |||
list<string> other_headers; | |||
void set_header(HTTPHeader*h){response_header=auto_ptr<HTTPHeader>(h);}; | |||
void add_header(string s) { other_headers.push_back(s); }; | |||
virtual void main(); | |||
cache_iface cache; | |||
ostream cout; | |||
// Output and Cahce | |||
void set_header(HTTPHeader *h); | |||
void add_header(string s); | |||
void set_cookie(cgicc::HTTPCookie const &c); | |||
cache_iface cache; | |||
base_cache *caching_module; | |||
bool gzip; | |||
bool gzip_done; | |||
string out; | |||
void init_internal(); | |||
HTTPHeader &header(); | |||
void set_lang(); | |||
void set_lang(string const &s); | |||
inline char const *gettext(char const *s) { return gt->gettext(s); }; | |||
inline char const *ngettext(char const *s,char const *p,int n) { return gt->ngettext(s,p,n); }; | |||
string current_template; | |||
inline void use_template(string s="") { current_template=s; }; | |||
void render(string name,base_content &content); | |||
void render(string templ,string name,base_content &content); | |||
void render(string name,base_content &content,ostream &); | |||
void render(string templ,string name,base_content &content,ostream &); | |||
virtual void main(); | |||
public: | |||
int id; | |||
pthread_t pid; | |||
ostream &get_cout() { return cout; } | |||
transtext::trans const *domain_gettext(string const &domain); | |||
void run(cgicc_connection &); | |||
worker_thread(manager const &s) : | |||
url(this), | |||
app(s), | |||
cache(this) | |||
{ | |||
init_internal(); | |||
} ; | |||
worker_thread(manager const &s); | |||
virtual ~worker_thread(); | |||
}; | |||