/* * palm-db-tools: Support Library: String Parsing Utility Functions * Copyright (C) 1999-2000 by Tom Dyas (tdyas@users.sourceforge.net) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifh Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "strop.h" #include extern std::ostream* err; void StrOps::lower(std::string& str) { for (std::string::iterator p = str.begin(); p != str.end(); ++p) { if (isupper(*p)) *p = tolower(*p); } } bool StrOps::string2boolean(const std::string& str) { std::string value(str); StrOps::lower(value); if (value == "on") return true; else if (str == "off") return false; else if (str == "true") return true; else if (str == "t") return true; else if (str == "false") return false; else if (str == "f") return false; else { int num = 0; std::istringstream(str.c_str()) >> num; return num != 0 ? true : false; } } std::string StrOps::type2string(PalmLib::FlatFile::Field::FieldType t) { switch (t) { case PalmLib::FlatFile::Field::STRING: return "string"; case PalmLib::FlatFile::Field::BOOLEAN: return "boolean"; case PalmLib::FlatFile::Field::INTEGER: return "integer"; case PalmLib::FlatFile::Field::FLOAT: return "float"; case PalmLib::FlatFile::Field::DATE: return "date"; case PalmLib::FlatFile::Field::TIME: return "time"; case PalmLib::FlatFile::Field::DATETIME: return "datetime"; case PalmLib::FlatFile::Field::NOTE: return "note"; case PalmLib::FlatFile::Field::LIST: return "list"; case PalmLib::FlatFile::Field::LINK: return "link"; case PalmLib::FlatFile::Field::CALCULATED: return "calculated"; case PalmLib::FlatFile::Field::LINKED: return "linked"; default: // If we don't support the field type, then fake it as a string. return "string"; } } PalmLib::FlatFile::Field::FieldType StrOps::string2type(std::string typestr) { StrOps::lower(typestr); if (typestr == "string") return PalmLib::FlatFile::Field::STRING; else if (typestr == "str") return PalmLib::FlatFile::Field::STRING; else if (typestr == "note") return PalmLib::FlatFile::Field::NOTE; else if (typestr == "bool") return PalmLib::FlatFile::Field::BOOLEAN; else if (typestr == "boolean") return PalmLib::FlatFile::Field::BOOLEAN; else if (typestr == "integer") return PalmLib::FlatFile::Field::INTEGER; else if (typestr == "int") return PalmLib::FlatFile::Field::INTEGER; else if (typestr == "float") return PalmLib::FlatFile::Field::FLOAT; else if (typestr == "date") return PalmLib::FlatFile::Field::DATE; else if (typestr == "time") return PalmLib::FlatFile::Field::TIME; else if (typestr == "datetime") return PalmLib::FlatFile::Field::DATETIME; else if (typestr == "list") return PalmLib::FlatFile::Field::LIST; else if (typestr == "link") return PalmLib::FlatFile::Field::LINK; else if (typestr == "linked") return PalmLib::FlatFile::Field::LINKED; else if (typestr == "calculated") return PalmLib::FlatFile::Field::CALCULATED; else kdDebug() << "unknown field type" << endl; return PalmLib::FlatFile::Field::STRING; } std::string StrOps::strip_back(const std::string& str, const std::string& what) { std::string result(str); std::string::reverse_iterator p = result.rbegin(); while (p != result.rend() && (std::find(what.begin(), what.end(), *p) != what.end())) ++p; result.erase(p.base(), result.end()); return result; } std::string StrOps::strip_front(const std::string& str,const std::string& what) { std::string result(str); std::string::iterator p = result.begin(); while (p != result.end() && (std::find(what.begin(), what.end(), *p) != what.end())) ++p; result.erase(result.begin(), p); return result; } StrOps::string_list_t StrOps::csv_to_array(const std::string& str, char delim, bool quoted_string) { enum { STATE_NORMAL, STATE_TQUOTES } state; StrOps::string_list_t result; std::string elem; state = STATE_NORMAL; for (std::string::const_iterator p = str.begin(); p != str.end(); ++p) { switch (state) { case STATE_NORMAL: if (quoted_string && *p == '"') { state = STATE_TQUOTES; } else if (*p == delim) { result.push_back(elem); elem = ""; } else { elem += *p; } break; case STATE_TQUOTES: if (quoted_string && *p == '"') { if ((p + 1) != str.end() && *(p+1) == '"') { ++p; elem += '"'; } else { state = STATE_NORMAL; } } else { elem += *p; } break; } } switch (state) { case STATE_NORMAL: result.push_back(elem); break; case STATE_TQUOTES: kdDebug() << "unterminated quotes" << endl; break; } return result; } StrOps::string_list_t StrOps::str_to_array(const std::string& str, const std::string& delim, bool multiple_delim, bool handle_comments) { enum { STATE_NORMAL, STATE_COMMENT, STATE_TQUOTE_DOUBLE, STATE_TQUOTE_SINGLE, STATE_BACKSLASH, STATE_BACKSLASH_DOUBLETQUOTE } state; StrOps::string_list_t result; std::string elem; state = STATE_NORMAL; for (std::string::const_iterator p = str.begin(); p != str.end(); ++p) { switch (state) { case STATE_NORMAL: if (*p == '"') { state = STATE_TQUOTE_DOUBLE; } else if (*p == '\'') { state = STATE_TQUOTE_SINGLE; } else if (std::find(delim.begin(), delim.end(), *p) != delim.end()) { if (multiple_delim) { ++p; while (p != str.end() && std::find(delim.begin(), delim.end(), *p) != delim.end()) { ++p; } --p; } result.push_back(elem); elem = ""; } else if (*p == '\\') { state = STATE_BACKSLASH; } else if (handle_comments && *p == '#') { state = STATE_COMMENT; } else { elem += *p; } break; case STATE_COMMENT: break; case STATE_TQUOTE_DOUBLE: if (*p == '"') state = STATE_NORMAL; else if (*p == '\\') state = STATE_BACKSLASH_DOUBLETQUOTE; else elem += *p; break; case STATE_TQUOTE_SINGLE: if (*p == '\'') state = STATE_NORMAL; else elem += *p; break; case STATE_BACKSLASH: elem += *p; state = STATE_NORMAL; break; case STATE_BACKSLASH_DOUBLETQUOTE: switch (*p) { case '\\': elem += '\\'; break; case 'n': elem += '\n'; break; case 'r': elem += '\r'; break; case 't': elem += '\t'; break; case 'v': elem += '\v'; break; case '"': elem += '"'; break; case 'x': { char buf[3]; // Extract and check the first hexadecimal character. if ((p + 1) == str.end()) kdDebug() << "truncated escape" << endl; if (! isxdigit(*(p + 1))) kdDebug() << "invalid hex character" << endl; buf[0] = *++p; // Extract and check the second (if any) hex character. if ((p + 1) != str.end() && isxdigit(*(p + 1))) { buf[1] = *++p; buf[2] = '\0'; } else { buf[1] = buf[2] = '\0'; } std::istringstream stream(buf); stream.setf(std::ios::hex, std::ios::basefield); unsigned value; stream >> value; elem += static_cast (value & 0xFFu); } break; } // Escape is done. Go back to the normal double quote state. state = STATE_TQUOTE_DOUBLE; break; } } switch (state) { case STATE_NORMAL: result.push_back(elem); break; case STATE_TQUOTE_DOUBLE: kdDebug() << "unterminated double quotes" << endl; break; case STATE_TQUOTE_SINGLE: kdDebug() << "unterminated single quotes" << endl; break; case STATE_BACKSLASH: case STATE_BACKSLASH_DOUBLETQUOTE: kdDebug() << "an escape character must follow a backslash" << endl; break; default: break; } return result; } PalmLib::pi_uint32_t StrOps::get_current_time(void) { time_t now; time(&now); return static_cast (now) + PalmLib::pi_uint32_t(2082844800); } char * StrOps::strptime(const char *s, const char *format, struct tm *tm) { char *data = (char *)s; char option = false; while (*format != 0) { if (*data == 0) return NULL; switch (*format) { case '%': option = true; format++; break; case 'd': if (option) { tm->tm_mday = strtol(data, &data, 10); if (tm->tm_mday < 1 || tm->tm_mday > 31) return NULL; } else if (*data != 'd') { return data; } option = false; format++; break; case 'm': if (option) { /* tm_mon between 0 and 11 */ tm->tm_mon = strtol(data, &data, 10) - 1; if (tm->tm_mon < 0 || tm->tm_mon > 11) return NULL; } else if (*data != 'm') { return data; } option = false; format++; break; case 'y': if (option) { tm->tm_year = strtol(data, &data, 10); if (tm->tm_year < 60) tm->tm_year += 100; } else if (*data != 'y') { return data; } option = false; format++; break; case 'Y': if (option) { tm->tm_year = strtol(data, &data, 10) - 1900; } else if (*data != 'Y') { return data; } option = false; format++; break; case 'H': if (option) { tm->tm_hour = strtol(data, &data, 10); if (tm->tm_hour < 0 || tm->tm_hour > 23) return NULL; } else if (*data != 'H') { return data; } option = false; format++; break; case 'M': if (option) { tm->tm_min = strtol(data, &data, 10); if (tm->tm_min < 0 || tm->tm_min > 59) return NULL; } else if (*data != 'M') { return data; } option = false; format++; break; default: if (option) return data; if (*data != *format) return data; format++; data++; } } return data; } // Read a line from an istream w/o concern for buffer limitations. std::string StrOps::readline(std::istream& stream) { std::string line; char buf[1024]; while (1) { // Read the next line (or part thereof) from the stream. stream.getline(buf, sizeof(buf)); // Bail out of the loop if the stream has reached end-of-file. if ((stream.eof() && !buf[0]) || stream.bad()) break; // Append the data read to the result string. line.append(buf); // If the stream is good, then stop. Otherwise, clear the // error indicator so that getline will work again. if ((stream.eof() && buf[0]) || stream.good()) break; stream.clear(); } return line; } std::string StrOps::quote_string(std::string str, bool extended_mode) { std::string result; std::ostringstream error; if (extended_mode) { result += '"'; for (std::string::iterator c = str.begin(); c != str.end(); ++c) { switch (*c) { case '\\': result += '\\'; result += '\\'; break; case '\r': result += '\\'; result += 'r'; break; case '\n': result += '\\'; result += 'n'; break; case '\t': result += '\\'; result += 't'; break; case '\v': result += '\\'; result += 'v'; break; case '"': result += '\\'; result += '"'; break; default: if (isprint(*c)) { result += *c; } else { std::ostringstream buf; buf.width(2); buf.setf(std::ios::hex, std::ios::basefield); buf.setf(std::ios::left); buf << ((static_cast (*c)) & 0xFF) << std::ends; result += "\\x"; result += buf.str(); } break; } } result += '"'; } else { result += '"'; for (std::string::iterator c = str.begin(); c != str.end(); ++c) { if (*c == '"') { result += "\"\""; } else if (*c == '\n' || *c == '\r') { error << "use extended csv mode for newlines\n"; *err << error.str(); kdDebug() << error.str().c_str() << endl; } else { result += *c; } } result += '"'; } return result; } std::string StrOps::concatenatepath(std::string p_Path, std::string p_FileName, std::string p_Ext) { std::string l_FilePath; #ifdef WIN32 if (p_FileName[1] == ':' || p_FileName[0] == '\\') return p_FileName; else if (p_Path.empty()) l_FilePath = p_FileName; else l_FilePath = p_Path + std::string("\\") + p_FileName; #else if (p_FileName[0] == '/') return p_FileName; else if (p_Path.empty()) l_FilePath = p_FileName; else l_FilePath = p_Path + std::string("/") + p_FileName; #endif if (!p_Ext.empty() && (p_FileName.rfind(p_Ext) == std::string::npos)) l_FilePath += p_Ext; return l_FilePath; }