diff --git a/cppcms/json.h b/cppcms/json.h index 647f59d..a498d37 100644 --- a/cppcms/json.h +++ b/cppcms/json.h @@ -20,6 +20,7 @@ #define CPPCMS_JSON_H #include +#include #include #include #include @@ -60,7 +61,7 @@ namespace json { /// /// The json::object - std::map of json::value's /// - typedef std::map object; + typedef std::map object; #ifdef CPPCMS_DOXYGEN_DOCS @@ -263,6 +264,14 @@ namespace json { /// that holds an object { "y" : 10 } and find("foo") would return value of undefined type. /// value const &find(std::string const &path) const; + /// + /// Searches a value in the path \a path + /// + /// For example if the json::value represents { "x" : { "y" : 10 } }, then find("x.y") would return + /// a reference to value that hold a number 10, find("x") returns a reference to a value + /// that holds an object { "y" : 10 } and find("foo") would return value of undefined type. + /// + value const &find(char const *path) const; /// /// Searches a value in the path \a path, if not found throw bad_value_cast. @@ -279,12 +288,32 @@ namespace json { /// a reference to value that hold a number 10, find("x") returns a reference to a value /// that holds an object { "y" : 10 } and find("foo") throws /// + value const &at(char const *path) const; + /// + /// Searches a value in the path \a path, if not found throw bad_value_cast. + /// + /// For example if the json::value represents { "x" : { "y" : 10 } }, then find("x.y") would return + /// a reference to value that hold a number 10, find("x") returns a reference to a value + /// that holds an object { "y" : 10 } and find("foo") throws + /// value &at(std::string const &path); + /// + /// Searches a value in the path \a path, if not found throw bad_value_cast. + /// + /// For example if the json::value represents { "x" : { "y" : 10 } }, then find("x.y") would return + /// a reference to value that hold a number 10, find("x") returns a reference to a value + /// that holds an object { "y" : 10 } and find("foo") throws + /// + value &at(char const *path); /// /// Sets the value \a v at the path \a path, if the path invalid, creates it. /// void at(std::string const &path,value const &v); + /// + /// Sets the value \a v at the path \a path, if the path invalid, creates it. + /// + void at(char const *path,value const &v); /// @@ -305,6 +334,15 @@ namespace json { { return find(path).type(); } + /// + /// Returns the type of variable in path, if not found returns undefined + /// + /// Same as find(path).type() + /// + json_type type(char const *path) const + { + return find(path).type(); + } /// /// Convert an object \a v of type T to a value at specific path, same as at(path,value(v)) @@ -314,6 +352,14 @@ namespace json { { at(path,value(v)); } + /// + /// Convert an object \a v of type T to a value at specific path, same as at(path,value(v)) + /// + template + void set(char const *path,T const &v) + { + at(path,value(v)); + } /// /// Get a string value from a path \a path. If the path is not invalid or the object @@ -331,6 +377,22 @@ namespace json { return def; } } + /// + /// Get a string value from a path \a path. If the path is not invalid or the object + /// is not of type string at this path, returns \a def instead + /// + std::string get(char const *path,char const *def) const + { + value const &v=find(path); + if(v.is_undefined()) + return def; + try { + return v.get_value(); + } + catch(std::bad_cast const &e) { + return def; + } + } /// /// Get an object of type T from the path \a path. Throws bad_value_cast if such path does not @@ -341,12 +403,38 @@ namespace json { { return at(path).get_value(); } + /// + /// Get an object of type T from the path \a path. Throws bad_value_cast if such path does not + /// exists of conversion can't be done + /// + template + T get(char const *path) const + { + return at(path).get_value(); + } /// /// Get an object of type T from the path \a path. Returns \a def if such path does not /// exists of conversion can't be done /// template + T get(char const *path,T const &def) const + { + value const &v=find(path); + if(v.is_undefined()) + return def; + try { + return v.get_value(); + } + catch(std::bad_cast const &e) { + return def; + } + } + /// + /// Get an object of type T from the path \a path. Returns \a def if such path does not + /// exists of conversion can't be done + /// + template T get(std::string const &path,T const &def) const { value const &v=find(path); diff --git a/cppcms/steal_buf.h b/cppcms/steal_buf.h new file mode 100644 index 0000000..1fa4f3b --- /dev/null +++ b/cppcms/steal_buf.h @@ -0,0 +1,117 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2008-2010 Artyom Beilis (Tonkikh) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef CPPCMS_STEAL_BUF_H +#define CPPCMS_STEAL_BUF_H + +#include +#include +#include +#include +#include +#include + +namespace cppcms { +namespace util { + + template + class steal_buffer : public std::streambuf { + steal_buffer(steal_buffer const &); + void operator=(steal_buffer const &); + public: + char *begin() + { + return pbase(); + } + char *end() + { + return pptr(); + } + steal_buffer(std::ostream &out) + { + init(); + steal(out); + } + steal_buffer() + { + init(); + } + void steal(std::ostream &out) + { + release(); + stolen_ = out.rdbuf(this); + stream_ = &out; + } + void release() + { + if(stream_ && stolen_) { + stream_->rdbuf(stolen_); + } + stream_ = 0; + stolen_ = 0; + } + ~steal_buffer() + { + release(); + free(on_heap_); + } + int overflow(int c) + { + size_t current_size; + size_t new_size; + if(pbase() == on_stack_) { + current_size = on_stack_size; + new_size = on_stack_size * 2; + on_heap_ = (char *)malloc(new_size); + if(!on_heap_) + throw std::bad_alloc(); + memcpy(on_heap_,on_stack_,current_size); + } + else { + current_size = pptr() - pbase(); + new_size = current_size * 2; + char *new_ptr = (char *)realloc(on_heap_,new_size); + if(!new_ptr) + throw std::bad_alloc(); + on_heap_ = new_ptr; + + } + setp(on_heap_,on_heap_ + new_size); + pbump(current_size); + if(c!=EOF) + sputc(c); + return 0; + } + private: + void init() + { + on_heap_ = 0; + stolen_ = 0; + stream_ = 0; + setp(on_stack_,on_stack_+on_stack_size); + } + char *on_heap_; + std::streambuf *stolen_; + std::ostream *stream_; + char on_stack_[on_stack_size]; + }; + +} // util +} // cppcms + +#endif diff --git a/cppcms/string_key.h b/cppcms/string_key.h new file mode 100644 index 0000000..6de1f58 --- /dev/null +++ b/cppcms/string_key.h @@ -0,0 +1,330 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2008-2010 Artyom Beilis (Tonkikh) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef CPPCMS_STRING_KEY_H +#define CPPCMS_STRING_KEY_H + +#include +#include +#include +#include +#include + +namespace cppcms { + class string_key { + public: + + typedef char const *const_iterator; + + static const size_t npos = std::string::npos; + + string_key() : + begin_(0), + end_(0) + { + } + + string_key(char const *key) : + begin_(0), + end_(0), + key_(key) + { + } + string_key(std::string const &key) : + begin_(0), + end_(0), + key_(key) + { + } + + size_t size() const + { + return end() - begin(); + } + size_t length() const + { + return size(); + } + void clear() + { + begin_ = end_ = 0; + key_.clear(); + } + bool empty() const + { + return end() == begin(); + } + + size_t find(char c,size_t pos = 0) const + { + size_t s = size(); + if(pos >= s) + return npos; + char const *p=begin() + pos; + while(pos <= s && *p!=c) { + pos++; + p++; + } + if(pos >= s) + return npos; + return pos; + } + + string_key substr(size_t pos = 0,size_t n=npos) const + { + string_key tmp = unowned_substr(pos,n); + return string_key(std::string(tmp.begin(),tmp.end())); + } + string_key unowned_substr(size_t pos = 0,size_t n=npos) const + { + if(pos >= size()) { + return string_key(); + } + char const *p=begin() + pos; + char const *e=end(); + if(n > size_t(e-p)) { + return string_key(p,e); + } + else { + return string_key(p,p+n); + } + } + + char const &operator[](size_t n) const + { + return *(begin() + n); + } + char const &at(size_t n) const + { + if(n > size()) + throw std::out_of_range("cppcms::string_key::at() range error"); + return *(begin() + n); + } + + + static string_key unowned(std::string const &v) + { + return string_key(v.c_str(),v.c_str()+v.size()); + } + static string_key unowned(char const *str) + { + char const *end = str; + while(*end) + end++; + return string_key(str,end); + } + static string_key unowned(char const *begin,char const *end) + { + return string_key(begin,end); + } + + char const *begin() const + { + if(begin_) + return begin_; + return key_.c_str(); + } + char const *end() const + { + if(begin_) + return end_; + return key_.c_str() + key_.size(); + } + bool operator<(string_key const &other) const + { + return std::lexicographical_compare( begin(),end(), + other.begin(),other.end(), + std::char_traits::lt); + } + bool operator>(string_key const &other) const + { + return other < *this; + } + bool operator>=(string_key const &other) const + { + return !(*this < other); + } + bool operator<=(string_key const &other) const + { + return !(*this > other); + } + bool operator==(string_key const &other) const + { + return (end() - begin() == other.end() - other.begin()) + && memcmp(begin(),other.begin(),end()-begin()) == 0; + } + bool operator!=(string_key const &other) const + { + return !(*this!=other); + } + + char const *data() const + { + return begin(); + } + + std::string str() const + { + if(begin_) + return std::string(begin_,end_-begin_); + else + return key_; + } + operator std::string() const + { + return str(); + } + private: + string_key(char const *b,char const *e) : + begin_(b), + end_(e) + { + } + + char const *begin_; + char const *end_; + std::string key_; + }; + + inline std::ostream &operator<<(std::ostream &out,string_key const &s) + { + out.write(s.data(),s.size()); + return out; + } + + inline bool operator==(string_key const &l,char const *r) + { + return l==string_key::unowned(r); + } + + inline bool operator==(char const *l,string_key const &r) + { + return string_key::unowned(l) == r; + } + + inline bool operator==(string_key const &l,std::string const &r) + { + return l==string_key::unowned(r); + } + + inline bool operator==(std::string const &l,string_key const &r) + { + return string_key::unowned(l) == r; + } + + inline bool operator!=(string_key const &l,char const *r) + { + return l!=string_key::unowned(r); + } + + inline bool operator!=(char const *l,string_key const &r) + { + return string_key::unowned(l) != r; + } + + inline bool operator!=(string_key const &l,std::string const &r) + { + return l!=string_key::unowned(r); + } + + inline bool operator!=(std::string const &l,string_key const &r) + { + return string_key::unowned(l) != r; + } + inline bool operator<=(string_key const &l,char const *r) + { + return l<=string_key::unowned(r); + } + + inline bool operator<=(char const *l,string_key const &r) + { + return string_key::unowned(l) <= r; + } + + inline bool operator<=(string_key const &l,std::string const &r) + { + return l<=string_key::unowned(r); + } + + inline bool operator<=(std::string const &l,string_key const &r) + { + return string_key::unowned(l) <= r; + } + inline bool operator>=(string_key const &l,char const *r) + { + return l>=string_key::unowned(r); + } + + inline bool operator>=(char const *l,string_key const &r) + { + return string_key::unowned(l) >= r; + } + + inline bool operator>=(string_key const &l,std::string const &r) + { + return l>=string_key::unowned(r); + } + + inline bool operator>=(std::string const &l,string_key const &r) + { + return string_key::unowned(l) >= r; + } + + + inline bool operator<(string_key const &l,char const *r) + { + return l(string_key const &l,char const *r) + { + return l>string_key::unowned(r); + } + + inline bool operator>(char const *l,string_key const &r) + { + return string_key::unowned(l) > r; + } + + inline bool operator>(string_key const &l,std::string const &r) + { + return l>string_key::unowned(r); + } + + inline bool operator>(std::string const &l,string_key const &r) + { + return string_key::unowned(l) > r; + } + +} + +#endif diff --git a/cppcms/url_mapper.h b/cppcms/url_mapper.h index 6349c10..edec884 100644 --- a/cppcms/url_mapper.h +++ b/cppcms/url_mapper.h @@ -20,6 +20,7 @@ #define CPPCMS_URL_MAPPER_H #include +#include #include #include #include @@ -233,6 +234,66 @@ namespace cppcms { /// Write the URL to output stream \a out for the URL \a path with 0 parameters /// void map( std::ostream &out, + char const *path); + + /// + /// Write the URL to output stream \a out for the URL \a path with 1 parameters + /// + void map( std::ostream &out, + char const *path, + filters::streamable const &p1); + + /// + /// Write the URL to output stream \a out for the URL \a path with 2 parameters + /// + void map( std::ostream &out, + char const *path, + filters::streamable const &p1, + filters::streamable const &p2); + + /// + /// Write the URL to output stream \a out for the URL \a path with 3 parameters + /// + void map( std::ostream &out, + char const *path, + filters::streamable const &p1, + filters::streamable const &p2, + filters::streamable const &p3); + + /// + /// Write the URL to output stream \a out for the URL \a path with 4 parameters + /// + void map( std::ostream &out, + char const *path, + filters::streamable const &p1, + filters::streamable const &p2, + filters::streamable const &p3, + filters::streamable const &p4); + /// + /// Write the URL to output stream \a out for the URL \a path with 5 parameters + /// + void map( std::ostream &out, + char const *path, + filters::streamable const &p1, + filters::streamable const &p2, + filters::streamable const &p3, + filters::streamable const &p4, + filters::streamable const &p5); + /// + /// Write the URL to output stream \a out for the URL \a path with 6 parameters + /// + void map( std::ostream &out, + char const *path, + filters::streamable const &p1, + filters::streamable const &p2, + filters::streamable const &p3, + filters::streamable const &p4, + filters::streamable const &p5, + filters::streamable const &p6); + /// + /// Write the URL to output stream \a out for the URL \a path with 0 parameters + /// + void map( std::ostream &out, std::string const &path); /// @@ -314,9 +375,9 @@ namespace cppcms { private: void real_assign(std::string const &key,std::string const &url,application *child = 0); - url_mapper &get_mapper_for_key(std::string const &key,std::string &real_key,std::vector &direct); + url_mapper &get_mapper_for_key(string_key const &key,string_key &real_key,std::vector &direct); url_mapper *root_mapper(); - void real_map( std::string const &key, + void real_map( char const *key, filters::streamable const *const *params, size_t params_no, std::ostream &output); diff --git a/cppcms/util.h b/cppcms/util.h index 0c25699..ae93ce6 100644 --- a/cppcms/util.h +++ b/cppcms/util.h @@ -42,10 +42,26 @@ namespace cppcms { /// std::string CPPCMS_API escape(std::string const &s); /// + /// Escape string for inclusion in HTML page, i.e. + /// + /// - < - \< + /// - > - \> + /// - \& - \& + /// - " - \" + /// + /// Note, this function does not deal with encodings, so it's up to you to + /// provide valid text encoding + /// + void CPPCMS_API escape(char const *begin,char const *end,std::ostream &output); + /// /// Encode string for URL (percent encoding) /// std::string CPPCMS_API urlencode(std::string const &s); /// + /// Encode string for URL (percent encoding) + /// + void CPPCMS_API urlencode(char const *begin,char const *end,std::ostream &output); + /// /// Decode string from URL-encoding (percent-encoding) /// std::string CPPCMS_API urldecode(std::string const &s); diff --git a/src/filters.cpp b/src/filters.cpp index b83c24d..58a81a5 100644 --- a/src/filters.cpp +++ b/src/filters.cpp @@ -18,6 +18,7 @@ /////////////////////////////////////////////////////////////////////////////// #define CPPCMS_SOURCE #include +#include #include #include #include @@ -135,7 +136,10 @@ namespace cppcms { namespace filters { escape const &escape::operator=(escape const &other){ obj_ = other.obj_; return *this; } void escape::operator()(std::ostream &out) const { - out << util::escape(obj_.get(out)); + util::steal_buffer<> sb(out); + obj_(out); + sb.release(); + util::escape(sb.begin(),sb.end(),out); } struct urlencode::_data {}; @@ -146,7 +150,10 @@ namespace cppcms { namespace filters { urlencode const &urlencode::operator=(urlencode const &other){ obj_ = other.obj_; return *this; } void urlencode::operator()(std::ostream &out) const { - out << util::urlencode(obj_.get(out)); + util::steal_buffer<> sb(out); + obj_(out); + sb.release(); + util::urlencode(sb.begin(),sb.end(),out); } struct raw::_data {}; diff --git a/src/json.cpp b/src/json.cpp index 13030f6..a12edd8 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -402,15 +402,16 @@ namespace json { // returns empty if not found - value const &value::find(std::string const &path) const + value const &value::find(char const *cpath) const { + string_key path=string_key::unowned(cpath); static value const empty; value const *ptr=this; size_t pos=0; size_t new_pos; do { new_pos=path.find('.',pos); - std::string const part=path.substr(pos,new_pos - pos); + string_key const part=path.unowned_substr(pos,new_pos - pos); if(new_pos!=std::string::npos) new_pos++; if(part.empty()) @@ -427,23 +428,37 @@ namespace json { } while(new_pos < path.size()); return *ptr; } + // returns empty if not found + value const &value::find(std::string const &path) const + { + return find(path.c_str()); + } // throws if not found value const &value::at(std::string const &path) const { + return at(path.c_str()); + } + value const &value::at(char const *path) const + { value const &v=find(path); if(v.is_undefined()) - throw bad_value_cast("Value not found at "+path ); + throw bad_value_cast(std::string("Value not found at ")+path ); return v; } value &value::at(std::string const &path) { + return at(path.c_str()); + } + value &value::at(char const *cpath) + { + string_key path=string_key::unowned(cpath); value *ptr=this; size_t pos=0; size_t new_pos; do { new_pos=path.find('.',pos); - std::string part=path.substr(pos,new_pos - pos); + string_key part=path.unowned_substr(pos,new_pos - pos); if(new_pos!=std::string::npos) new_pos++; if(part.empty()) @@ -453,7 +468,7 @@ namespace json { json::object &obj=ptr->object(); json::object::iterator p; if((p=obj.find(part))==obj.end()) - throw bad_value_cast("Member "+part+" not found"); + throw bad_value_cast("Member "+part.str()+" not found"); ptr=&p->second; pos=new_pos; @@ -462,12 +477,17 @@ namespace json { } void value::at(std::string const &path,value const &v) { + at(path.c_str(),v); + } + void value::at(char const *cpath,value const &v) + { + string_key path=string_key::unowned(cpath); value *ptr=this; size_t pos=0; size_t new_pos; do { new_pos=path.find('.',pos); - std::string part=path.substr(pos,new_pos - pos); + string_key part=path.unowned_substr(pos,new_pos - pos); if(new_pos!=std::string::npos) @@ -480,7 +500,7 @@ namespace json { json::object &obj=ptr->object(); json::object::iterator p; if((p=obj.find(part))==obj.end()) { - ptr=&obj.insert(std::make_pair(part,json::value())).first->second; + ptr=&obj.insert(std::make_pair(part.str(),json::value())).first->second; } else ptr=&p->second; @@ -497,7 +517,7 @@ namespace json { json::object &self=object(); - json::object::iterator p=self.find(name); + json::object::iterator p=self.find(string_key::unowned(name)); if(p==self.end()) return self.insert(std::make_pair(name,value())).first->second; return p->second; @@ -508,7 +528,7 @@ namespace json { if(type()!=json::is_object) throw bad_value_cast("",type(),json::is_object); json::object const &self=object(); - json::object::const_iterator p=self.find(name); + json::object::const_iterator p=self.find(string_key::unowned(name)); if(p==self.end()) throw bad_value_cast("Member "+name+" not found"); return p->second; diff --git a/src/url_mapper.cpp b/src/url_mapper.cpp index 1f081c8..8664f53 100644 --- a/src/url_mapper.cpp +++ b/src/url_mapper.cpp @@ -27,8 +27,32 @@ #include namespace cppcms { + + template + struct stream_it { + Data const *self; + Entry const &formatting; + filters::streamable const * const *params; + size_t params_no; + std::map const &data_helpers_default; + std::map const &data_helpers_override; + void write(std::ostream &out) const + { + self->write(formatting,params,params_no,data_helpers_default,data_helpers_override,out); + } + }; + + template + std::ostream &operator<<(std::ostream &out,stream_it const &wr) + { + wr.write(out); + return out; + } + + struct url_mapper::data { + data() : parent(0),this_application(0) {} std::string this_name; application *parent; @@ -41,29 +65,32 @@ namespace cppcms { application *child; entry() : child(0) {} }; + + template + friend struct stream_it; typedef std::map by_size_type; - typedef std::map by_key_type; - typedef std::map helpers_type; + typedef std::map by_key_type; + typedef std::map helpers_type; by_key_type by_key; helpers_type helpers; std::string root; - entry const &get_entry(std::string const &key,size_t params_no,std::string const &full_url) const + entry const &get_entry(string_key const &key,size_t params_no,string_key const &full_url) const { by_key_type::const_iterator kp = by_key.find(key); if(kp == by_key.end()) - throw cppcms_error("url_mapper: key `" + key + "' not found for " - "url `" + full_url + "'"); + throw cppcms_error("url_mapper: key `" + key.str() + "' not found for " + "url `" + full_url.str() + "'"); by_size_type::const_iterator sp = kp->second.find(params_no); if(sp == kp->second.end()) - throw cppcms_error("url_mapper: invalid number of parameters for " + key + - "in url `" + full_url + "'"); + throw cppcms_error("url_mapper: invalid number of parameters for " + key.str() + + "in url `" + full_url.str() + "'"); return sp->second; } - url_mapper *is_app(std::string const &key) const + url_mapper *is_app(string_key const &key) const { by_key_type::const_iterator kp = by_key.find(key); if(kp == by_key.end()) @@ -76,52 +103,44 @@ namespace cppcms { return 0; } - url_mapper &child(std::string const &name,std::string const &full_url) + url_mapper &child(string_key const &name,string_key const &full_url) { entry const &fmt = get_entry(name,1,full_url); if(!fmt.child) { - throw cppcms_error("url_mapper: the key " + name + " is not child application key" - " in url `" + full_url + "'"); + throw cppcms_error("url_mapper: the key " + name.str() + " is not child application key" + " in url `" + full_url.str() + "'"); } return fmt.child->mapper(); } - void map( std::string const &key, - std::string const &full_url, + struct map_stream { + filters::streamable const * const *params; + size_t params_no; + std::map const &data_helpers_default; + std::map const &data_helpers_override; + }; + + void write( entry const &formatting, filters::streamable const * const *params, size_t params_no, - std::map const &data_helpers_default, - std::map const &data_helpers_override, - std::ostream &output) const + std::map const &data_helpers_default, + std::map const &data_helpers_override, + std::ostream &out) const { - entry const &formatting = get_entry(key,params_no,full_url); - - std::ostringstream ss; - std::ostream *out = 0; - - if(parent) { - ss.copyfmt(output); - out = &ss; - } - else { - out = &output; - *out << root; - } - for(size_t i=0;i::const_iterator p; - p = data_helpers_override.find(hkey); + string_key hkey = string_key::unowned(formatting.keys[i]); + std::map::const_iterator p; + p = data_helpers_override.find(string_key::unowned(hkey)); if(p != data_helpers_override.end()) { - *out << p->second; + out << p->second; } else { p = data_helpers_default.find(hkey); if(p!=data_helpers_default.end()) - *out << p->second; + out << p->second; } } else { @@ -129,21 +148,46 @@ namespace cppcms { if(index >= params_no) { throw cppcms_error("url_mapper: Index of parameter out of range"); } - (*params[index])(*out); + (*params[index])(out); } } } + } + void map( string_key const &key, + string_key const &full_url, + filters::streamable const * const *params, + size_t params_no, + std::map const &data_helpers_default, + std::map const &data_helpers_override, + std::ostream &out) const + { + entry const &formatting = get_entry(key,params_no,full_url); if(parent) { - std::string url = ss.str(); + stream_it url = + { + this, + formatting, + params, + params_no, + data_helpers_default, + data_helpers_override + }; + filters::streamable stream_url(url); filters::streamable const *par_ptr=&stream_url; - parent->mapper().d->map(this_name,full_url,&par_ptr,1,data_helpers_default,data_helpers_override,output); + parent->mapper().d->map(this_name,full_url,&par_ptr,1, data_helpers_default, + data_helpers_override,out); + } + else { + out << root; + write(formatting,params,params_no,data_helpers_default,data_helpers_override,out); } } + }; void url_mapper::assign(std::string const &key,std::string const &url) @@ -268,8 +312,8 @@ namespace cppcms { app.mapper().d->this_name = name; real_assign(name,url,&app); // Copy all values to root most one - std::map &values = app.mapper().d->helpers; - std::map::const_iterator p; + std::map &values = app.mapper().d->helpers; + std::map::const_iterator p; for(p=values.begin();p!=values.end();++p) { set_value(p->first,p->second); } @@ -295,7 +339,7 @@ namespace cppcms { } - url_mapper &url_mapper::get_mapper_for_key(std::string const &key,std::string &real_key,std::vector &keywords) + url_mapper &url_mapper::get_mapper_for_key(string_key const &key,string_key &real_key,std::vector &keywords) { url_mapper *mapper = this; size_t pos = 0; @@ -312,15 +356,16 @@ namespace cppcms { if(end == std::string::npos) { size_t separator = key.find(';',pos); if(separator == std::string::npos) { - real_key = key.substr(pos); + real_key = key.unowned_substr(pos); } else { - real_key = key.substr(pos,separator - pos); + keywords.reserve(6); + real_key = key.unowned_substr(pos,separator - pos); pos = separator + 1; for(;;){ end = key.find(',',pos); size_t size = end - pos; - keywords.push_back(key.substr(pos,size)); + keywords.push_back(key.unowned_substr(pos,size)); if(end == std::string::npos) break; pos = end + 1; @@ -343,13 +388,13 @@ namespace cppcms { return *mapper; } size_t chunk_size = end - pos; - if(key.compare(pos,chunk_size,".") == 0) + string_key subapp = key.unowned_substr(pos,chunk_size); + if(subapp == ".") ; // Just continue where we are - else if(key.compare(pos,chunk_size,"..")==0) { + else if(subapp == "..") { mapper = &mapper->parent(); } else { - std::string subapp = key.substr(pos,chunk_size); mapper = &mapper->d->child(subapp,key); } pos = end + 1; @@ -357,17 +402,18 @@ namespace cppcms { } - void url_mapper::real_map( std::string const &key, + void url_mapper::real_map( char const *ckey, filters::streamable const * const *params, size_t params_no, std::ostream &output) { - std::string real_key; - std::vector direct; + string_key key=string_key::unowned(ckey); + string_key real_key; + std::vector direct; url_mapper &mp = get_mapper_for_key(key,real_key,direct); if(params_no < direct.size()) throw cppcms_error("url_mapper: number of keywords is larger then number of actual parameters"); - std::map mappings; + std::map mappings; if(direct.size() == 0) { mp.d->map(real_key,key,params,params_no,topmost().d->helpers,mappings,output); } @@ -385,23 +431,86 @@ namespace cppcms { mp.d->map(real_key,key,params,params_no,topmost().d->helpers,mappings,output); } } - + void url_mapper::map( std::ostream &out, std::string const &key) { - real_map(key,0,0,out); + map(out,key.c_str()); } void url_mapper::map( std::ostream &out, std::string const &key, filters::streamable const &p1) { + map(out,key.c_str(),p1); + } + + void url_mapper::map( std::ostream &out, + std::string const &key, + filters::streamable const &p1, + filters::streamable const &p2) + { + map(out,key.c_str(),p1,p2); + } + + void url_mapper::map( std::ostream &out, + std::string const &key, + filters::streamable const &p1, + filters::streamable const &p2, + filters::streamable const &p3) + { + map(out,key.c_str(),p1,p2,p3); + } + + void url_mapper::map( std::ostream &out, + std::string const &key, + filters::streamable const &p1, + filters::streamable const &p2, + filters::streamable const &p3, + filters::streamable const &p4) + { + map(out,key.c_str(),p1,p2,p3,p4); + } + + void url_mapper::map( std::ostream &out, + std::string const &key, + filters::streamable const &p1, + filters::streamable const &p2, + filters::streamable const &p3, + filters::streamable const &p4, + filters::streamable const &p5) + { + map(out,key.c_str(),p1,p2,p3,p4,p5); + } + + void url_mapper::map( std::ostream &out, + std::string const &key, + filters::streamable const &p1, + filters::streamable const &p2, + filters::streamable const &p3, + filters::streamable const &p4, + filters::streamable const &p5, + filters::streamable const &p6) + { + map(out,key.c_str(),p1,p2,p3,p4,p5,p6); + } + + void url_mapper::map( std::ostream &out, + char const *key) + { + real_map(key,0,0,out); + } + + void url_mapper::map( std::ostream &out, + char const *key, + filters::streamable const &p1) + { filters::streamable const *params[1]= { &p1 }; real_map(key,params,1,out); } void url_mapper::map( std::ostream &out, - std::string const &key, + char const *key, filters::streamable const &p1, filters::streamable const &p2) { @@ -410,7 +519,7 @@ namespace cppcms { } void url_mapper::map( std::ostream &out, - std::string const &key, + char const *key, filters::streamable const &p1, filters::streamable const &p2, filters::streamable const &p3) @@ -420,7 +529,7 @@ namespace cppcms { } void url_mapper::map( std::ostream &out, - std::string const &key, + char const *key, filters::streamable const &p1, filters::streamable const &p2, filters::streamable const &p3, @@ -431,7 +540,7 @@ namespace cppcms { } void url_mapper::map( std::ostream &out, - std::string const &key, + char const *key, filters::streamable const &p1, filters::streamable const &p2, filters::streamable const &p3, @@ -443,7 +552,7 @@ namespace cppcms { } void url_mapper::map( std::ostream &out, - std::string const &key, + char const *key, filters::streamable const &p1, filters::streamable const &p2, filters::streamable const &p3, diff --git a/src/util.cpp b/src/util.cpp index ac9a68a..ec54373 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -20,6 +20,7 @@ #include #include "http_protocol.h" #include +#include #include "md5.h" namespace cppcms { @@ -43,18 +44,31 @@ std::string escape(std::string const &s) return content; } -std::string urlencode(std::string const &s) + +void escape(char const *begin,char const *end,std::ostream &output) { - std::string content; - unsigned i,len=s.size(); - content.reserve(3*len); - for(i=0;i': output << ">"; break; + case '&': output << "&"; break; + case '\"': output<<"""; break; + default: output << c; + } + } +} + +template +void urlencode_impl(char const *b,char const *e,Iterator out) +{ + while(b!=e){ + char c=*b++; if( ('a'<=c && c<='z') || ('A'<=c && c<='Z') || ('0'<=c && c<='9')) { - content+=c; + *out++ = c; } else { switch(c) { @@ -62,20 +76,33 @@ std::string urlencode(std::string const &s) case '_': case '.': case '~': - content+=c; + *out++ = c; break; default: { static char const hex[]="0123456789abcdef"; unsigned char uc = c; - content += '%'; - content += hex[(uc >> 4) & 0xF]; - content += hex[ uc & 0xF]; + *out++ = '%'; + *out++ = hex[(uc >> 4) & 0xF]; + *out++ = hex[ uc & 0xF]; } }; } }; +} + +void urlencode(char const *b,char const *e,std::ostream &out) +{ + std::ostream_iterator it(out); + urlencode_impl(b,e,it); +} +std::string urlencode(std::string const &s) +{ + std::string content; + content.reserve(3*s.size()); + std::back_insert_iterator out(content); + urlencode_impl(s.c_str(),s.c_str()+s.size(),out); return content; }