--- parser3/src/main/pa_string.C 2004/02/27 15:24:03 1.196 +++ parser3/src/main/pa_string.C 2008/07/17 09:11:25 1.211 @@ -1,11 +1,11 @@ /** @file Parser: string class. @see untalength_t.C. - Copyright (c) 2001-2004 ArtLebedev Group (http://www.artlebedev.com) + Copyright (c) 2001-2005 ArtLebedev Group (http://www.artlebedev.com) Author: Alexandr Petrosian (http://paf.design.ru) */ -static const char * const IDENT_STRING_C="$Date: 2004/02/27 15:24:03 $"; +static const char * const IDENT_STRING_C="$Date: 2008/07/17 09:11:25 $"; #include "pcre.h" @@ -21,24 +21,36 @@ int pa_atoi(const char* str, const Strin if(!str) return 0; - while(*str && isspace(*str)) + while(*str && isspace((unsigned char)*str)) str++; if(!*str) return 0; int result; char *error_pos; + bool negative=false; + if(str[0]=='-') { + negative=true; + str++; + } else if(str[0]=='+') { + str++; + } // 0xABC if(str[0]=='0') if(str[1]=='x' || str[1]=='X') result=(int)(unsigned long)strtol(str, &error_pos, 0); - else - result=(int)strtol(str+1/*skip leading 0*/, &error_pos, 0); + else { + // skip leading 0000, to disable octal interpretation + do str++; while(*str=='0'); + result=(int)strtol(str, &error_pos, 0); + } else result=(int)strtol(str, &error_pos, 0); + if(negative) + result=-result; while(char c=*error_pos++) - if(!isspace(c)) + if(!isspace((unsigned char)c)) throw Exception("number.format", problem_source, problem_source?"invalid number (int)": "'%s' is invalid number (int)", str); @@ -50,24 +62,36 @@ double pa_atod(const char* str, const St if(!str) return 0; - while(*str && isspace(*str)) + while(*str && isspace((unsigned char)*str)) str++; if(!*str) return 0; double result; char *error_pos; + bool negative=false; + if(str[0]=='-') { + negative=true; + str++; + } else if(str[0]=='+') { + str++; + } // 0xABC if(str[0]=='0') if(str[1]=='x' || str[1]=='X') result=(double)(unsigned long)strtol(str, &error_pos, 0); - else - result=(double)strtod(str+1/*skip leading 0*/, &error_pos); + else { + // skip leading 0000, to disable octal interpretation + do str++; while(*str=='0'); + result=(double)strtod(str, &error_pos); + } else result=(double)strtod(str, &error_pos); + if(negative) + result=-result; while(char c=*error_pos++) - if(!isspace(c)) + if(!isspace((unsigned char)c)) throw Exception("number.format", problem_source, problem_source?"invalid number (double)": "'%s' is invalid number (double)", str); @@ -80,7 +104,7 @@ double pa_atod(const char* str, const St #ifndef DOXYGEN typedef struct { ssize_t countdown; - char target; /* Character we're looking for */ + int target; /* Character we're looking for */ } chr_data; #endif static int CORD_range_contains_chr_greater_then_proc(char c, size_t size, void* client_data) @@ -149,7 +173,6 @@ String::Body String::Body::trim(String:: return *this; if(!chars) chars=" \t\n"; // white space - Body result=*this; size_t start=0; size_t end=our_length; @@ -256,6 +279,14 @@ String& String::append_strdup(const char return *this; } +size_t String::length(Charset& charset) const { + if(charset.isUTF8()){ + const XMLByte* srcPtr=(const XMLByte*)cstrm(); + return lengthUTF8(srcPtr, srcPtr+body.length()); + } else + return body.length(); +} + /// @todo check in doc: whether it documents NOW bad situation "abc".mid(-1, 3) =were?="ab" String& String::mid(size_t substr_begin, size_t substr_end) const { String& result=*new String; @@ -272,8 +303,43 @@ String& String::mid(size_t substr_begin, // next: letters themselves result.body=body.mid(substr_begin, substr_length); -// SAPI::log("piece of '%s' from %d to %d is '%s'", - //cstr(), substr_begin, substr_end, result.cstr()); + ASSERT_STRING_INVARIANT(result); + return result; +} + +// from, to and helper_length in characters, not in bytes (it's important for utf-8) +String& String::mid(Charset& charset, size_t from, size_t to, size_t helper_length) const { + String& result=*new String; + + size_t self_length=(helper_length)?helper_length:length(charset); + + if(!self_length) + return result; + + from=min(min(to, from), self_length); + to=min(max(to, from), self_length); + + size_t substr_length=to-from; + + if(!substr_length) + return result; + + if(charset.isUTF8()){ + const XMLByte* srcPtr=(const XMLByte*)cstrm(); + const XMLByte* srcEnd=srcPtr+body.length(); + + // convert from and substr_length from 'characters' to 'bytes' + from=getUTF8BytePos(srcPtr, srcEnd, from); + substr_length=getUTF8BytePos(srcPtr+from, srcEnd, substr_length); + if(!substr_length) + return result; + } + + // first: their langs + result.langs.append(result.body, langs, from, substr_length); + // next: letters themselves + result.body=body.mid(from, substr_length); + ASSERT_STRING_INVARIANT(result); return result; } @@ -298,6 +364,21 @@ size_t String::pos(const String& substr, return pos(substr.body, this_offset, lang); } +size_t String::pos(Charset& charset, const String& substr, + size_t this_offset, Language lang) const { + + size_t result=pos(substr.body, this_offset, lang); + if(result==CORD_NOT_FOUND) + return STRING_NOT_FOUND; + + if(charset.isUTF8()){ + const XMLByte* srcPtr=(const XMLByte*)cstrm(); + result=getUTF8CharPos(srcPtr, srcPtr+body.length(), result); + } + + return result; +} + void String::split(ArrayString& result, size_t& pos_after, const char* delim, @@ -343,20 +424,27 @@ void String::split(ArrayString& result, } } -static void regex_options(const String* options, int *result, bool& need_pre_post_match){ +enum Match_feature { + MF_NEED_PRE_POST_MATCH = 0x01, + MF_JUST_COUNT_MATCHES = 0x02 +}; + +static void regex_options(const String* options, int* result, int* match_features){ struct Regex_option { const char* keyL; const char* keyU; - int clear, set; + int clear; + int set; int *result; - bool *flag; + int flag; } regex_option[]={ {"i", "I", 0, PCRE_CASELESS, result, 0}, // a=A {"s", "S", 0, PCRE_DOTALL, result, 0}, // \n\n$ [default] {"x", "U", 0, PCRE_EXTENDED, result, 0}, // whitespace in regex ignored {"m", "M", PCRE_DOTALL, PCRE_MULTILINE, result, 0}, // ^aaa\n$^bbb\n$ {"g", "G", 0, 1, result+1, 0}, // many rows - {"'", 0, 0, 0, 0, &need_pre_post_match}, + {"'", 0, 0, 0, 0, MF_NEED_PRE_POST_MATCH}, + {"n", "N", 0, 0, 0, MF_JUST_COUNT_MATCHES}, {0, 0, 0, 0, 0, 0} }; result[0]=PCRE_EXTRA | PCRE_DOTALL | PCRE_DOLLAR_ENDONLY; @@ -364,11 +452,13 @@ static void regex_options(const String* if(options && !options->is_empty()) for(Regex_option *o=regex_option; o->keyL; o++) - if(options->pos(o->keyL)!=STRING_NOT_FOUND - || (o->keyU && options->pos(o->keyU)!=STRING_NOT_FOUND)) { - if(o->flag) - *o->flag=true; - else { // result + if( + options->pos(o->keyL)!=STRING_NOT_FOUND + || (o->keyU && options->pos(o->keyU)!=STRING_NOT_FOUND) + ){ + if(o->flag){ + (*match_features) |= o->flag; + } else { *o->result &= ~o->clear; *o->result |= o->set; } @@ -379,17 +469,20 @@ Table* String::match(Charset& source_cha const String& regexp, const String* options, Row_action row_action, void *info, - bool& just_matched) const { + int& matches_count) const { if(regexp.is_empty()) throw Exception(0, 0, "regexp is empty"); - const char* pattern=regexp.cstr(); + const char* pattern=regexp.cstr(String::L_UNSPECIFIED); // fix any tainted with L_REGEX const char* errptr; int erroffset; - bool need_pre_post_match=false; - int option_bits[2]={0}; regex_options(options, option_bits, need_pre_post_match); + int option_bits[2]={0}; + int match_features=0; + regex_options(options, option_bits, &match_features); + bool need_pre_post_match=(match_features & MF_NEED_PRE_POST_MATCH) != 0; + bool just_count_matches=(match_features & MF_JUST_COUNT_MATCHES) != 0; bool global=option_bits[1]!=0; pcre *code=pcre_compile(pattern, option_bits[0], &errptr, &erroffset, @@ -430,11 +523,12 @@ Table* String::match(Charset& source_cha if(exec_substrings==PCRE_ERROR_NOMATCH) { pcre_free(code); row_action(table, 0/*last time, no raw*/, 0, 0, poststart, postfinish, info); - if(global || subpatterns) - return &table; // global or with subpatterns=true+result - else { - just_matched=false; return 0; // not global=no result - } + // if(global || subpatterns) + // return &table; // global or with subpatterns=true+result + // else { + // just_matched=false; return 0; // not global=no result + // } + return just_count_matches ? 0 : &table; } if(exec_substrings<0) { @@ -463,12 +557,14 @@ Table* String::match(Charset& source_cha *row+=&mid(ovector[i*2+0], ovector[i*2+1]); // .i column value } + matches_count++; row_action(table, row, prestart, prefinish, poststart, postfinish, info); if(!global || prestart==poststart) { // not global | going to hang pcre_free(code); row_action(table, 0/*last time, no row*/, 0, 0, poststart, postfinish, info); - return &table; + return just_count_matches ? 0 : &table; + // return &table; } prestart=poststart; @@ -588,52 +684,75 @@ static int serialize_lang_piece(char ala } String::Cm String::serialize(size_t prolog_length) const { size_t fragments_count=langs.count(); + size_t body_length=body.length(); size_t buf_length= prolog_length //1 +sizeof(size_t) //2 - +fragments_count*(sizeof(char)+sizeof(size_t)) //3 - +body.length() //4 - +1; // for zero terminator used in deserialize + +body_length //3 + +1 // 4 for zero terminator used in deserialize + +sizeof(size_t) //5 + +fragments_count*(sizeof(char)+sizeof(size_t)); //6 + String::Cm result(new(PointerFreeGC) char[buf_length], buf_length); // 1: prolog char *cur=result.str+prolog_length; - // 2: langs.count [WARNING: not cast, addresses must be %4=0 on sparc] + // 2: chars.count [WARNING: not cast, addresses must be %4=0 on sparc] + memcpy(cur, &body_length, sizeof(body_length)); cur+=sizeof(body_length); + // 3: letters + body.for_each(serialize_body_char, serialize_body_piece, &cur); + // 4: zero terminator + *cur++=0; + // 5: langs.count [WARNING: not cast, addresses must be %4=0 on sparc] memcpy(cur, &fragments_count, sizeof(fragments_count)); cur+=sizeof(fragments_count); - // 3: lang info + // 6: lang info langs.for_each(body, serialize_lang_piece, &cur); - // 4: letters - body.for_each(serialize_body_char, serialize_body_piece, &cur); - // 5: zero terminator - *cur=0; return result; } -bool String::deserialize(size_t prolog_length, void *buf, size_t buf_length) { - if(buf_length<=prolog_length) +bool String::deserialize(size_t prolog_size, void *buf, size_t buf_size) { + size_t in_buf=buf_size; + if(in_buf<=prolog_size) return false; - buf_length-=prolog_length; - buf_length-=1; // 5: zero terminator + in_buf-=prolog_size; // 1: prolog - const char* cur=(const char* )buf+prolog_length; + const char* cur=(const char* )buf+prolog_size; + + // 2: chars.count + size_t body_length; + if(in_bufbody_length) + return false; // file curruption // uchar needed to prevent propagating 0x80 bit to upper bytes langs.append(total_length, (String::Language)(uchar)lang, fragment_length); - total_length+=fragment_length; - - buf_length-=piece_length; + total_length=combined_length; + in_buf-=piece_length; } - // 4: letters - if(buf_length!=total_length) + if(total_length!=body_length) // length(all language fragments) vs length(letters) return false; - - // serialize wrote extra zero byte there, we can rely on that - body=String::Body(cur, buf_length); } + if(in_buf!=0) // some strange extra bytes + return false; ASSERT_STRING_INVARIANT(*this); return true; @@ -663,14 +782,24 @@ bool String::deserialize(size_t prolog_l const char* String::Body::v() const { return CORD_to_const_char_star(body); } +void String::Body::dump() const { + CORD_dump(body); +} + const char* String::Languages::v() const { if(opt.is_not_just_lang) return CORD_to_const_char_star(langs); else return (const char*)&langs; } +void String::Languages::dump() const { + if(opt.is_not_just_lang) + CORD_dump(langs); + else + puts((const char*)&langs); +} const char* String::v() const { - const int LIMIT_VIEW=20; + const uint LIMIT_VIEW=20; char* buf=(char*)malloc(MAX_STRING); const char*body_view=body.v(); const char*langs_view=langs.v(); @@ -683,7 +812,10 @@ const char* String::v() const { return buf; } - +void String::dump() const { + body.dump(); + langs.dump(); +} const String& String::trim(String::Trim_kind kind, const char* chars) const { if(!length()) return *this; @@ -706,4 +838,4 @@ const String& String::trim(String::Trim_ ASSERT_STRING_INVARIANT(result); return result; -} \ No newline at end of file +}