// @file str.h /* Copyright 2010 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #pragma once /** * String utilities. * * TODO: De-inline. * TODO: Retire the mongoutils namespace, and move str under the mongo namespace. */ #include #include #include "mongo/bson/util/builder.h" namespace mongoutils { namespace str { /** the idea here is to make one liners easy. e.g.: return str::stream() << 1 << ' ' << 2; since the following doesn't work: (std::stringstream() << 1).str(); */ class stream { public: mongo::StringBuilder ss; template stream& operator<<(const T& v) { ss << v; return *this; } operator std::string() const { return ss.str(); } }; inline bool startsWith(const char* str, const char* prefix) { const char* s = str; const char* p = prefix; while (*p) { if (*p != *s) return false; p++; s++; } return true; } inline bool startsWith(const std::string& s, const std::string& p) { return startsWith(s.c_str(), p.c_str()); } // while these are trivial today use in case we do different wide char things later inline bool startsWith(const char* p, char ch) { return *p == ch; } inline bool startsWith(const std::string& s, char ch) { return startsWith(s.c_str(), ch); } inline bool endsWith(const std::string& s, const std::string& p) { int l = p.size(); int x = s.size(); if (x < l) return false; return strncmp(s.c_str() + x - l, p.c_str(), l) == 0; } inline bool endsWith(const char* s, char p) { size_t len = strlen(s); return len && s[len - 1] == p; } inline bool endsWith(const char* p, const char* suffix) { size_t a = strlen(p); size_t b = strlen(suffix); if (b > a) return false; return strcmp(p + a - b, suffix) == 0; } inline bool equals(const char* a, const char* b) { return strcmp(a, b) == 0; } /** find char x, and return rest of std::string thereafter, or "" if not found */ inline const char* after(const char* s, char x) { const char* p = strchr(s, x); return (p != 0) ? p + 1 : ""; } inline std::string after(const std::string& s, char x) { const char* p = strchr(s.c_str(), x); return (p != 0) ? std::string(p + 1) : ""; } /** find std::string x, and return rest of std::string thereafter, or "" if not found */ inline const char* after(const char* s, const char* x) { const char* p = strstr(s, x); return (p != 0) ? p + strlen(x) : ""; } inline std::string after(const std::string& s, const std::string& x) { const char* p = strstr(s.c_str(), x.c_str()); return (p != 0) ? std::string(p + x.size()) : ""; } /** @return true if s contains x * These should not be used with strings containing NUL bytes */ inline bool contains(const std::string& s, const std::string& x) { return strstr(s.c_str(), x.c_str()) != 0; } inline bool contains(const std::string& s, char x) { verify(x != '\0'); // this expects c-strings so don't use when looking for NUL bytes return strchr(s.c_str(), x) != 0; } /** @return everything before the character x, else entire std::string */ inline std::string before(const std::string& s, char x) { const char* p = strchr(s.c_str(), x); return (p != 0) ? s.substr(0, p - s.c_str()) : s; } /** @return everything before the std::string x, else entire std::string */ inline std::string before(const std::string& s, const std::string& x) { const char* p = strstr(s.c_str(), x.c_str()); return (p != 0) ? s.substr(0, p - s.c_str()) : s; } /** check if if strings share a common starting prefix @return offset of divergence (or length if equal). 0=nothing in common. */ inline int shareCommonPrefix(const char* p, const char* q) { int ofs = 0; while (1) { if (*p == 0 || *q == 0) break; if (*p != *q) break; p++; q++; ofs++; } return ofs; } inline int shareCommonPrefix(const std::string& a, const std::string& b) { return shareCommonPrefix(a.c_str(), b.c_str()); } /** std::string to unsigned. zero if not a number. can end with non-num chars */ inline unsigned toUnsigned(const std::string& a) { unsigned x = 0; const char* p = a.c_str(); while (1) { if (!isdigit(*p)) break; x = x * 10 + (*p - '0'); p++; } return x; } /** split a std::string on a specific char. We don't split N times, just once on the first occurrence. If char not present entire std::string is in L and R is empty. @return true if char found */ inline bool splitOn(const std::string& s, char c, std::string& L, std::string& R) { const char* start = s.c_str(); const char* p = strchr(start, c); if (p == 0) { L = s; R.clear(); return false; } L = std::string(start, p - start); R = std::string(p + 1); return true; } /** split scanning reverse direction. Splits ONCE ONLY. */ inline bool rSplitOn(const std::string& s, char c, std::string& L, std::string& R) { const char* start = s.c_str(); const char* p = strrchr(start, c); if (p == 0) { L = s; R.clear(); return false; } L = std::string(start, p - start); R = std::string(p + 1); return true; } /** @return number of occurrences of c in s */ inline unsigned count(const std::string& s, char c) { unsigned n = 0; for (unsigned i = 0; i < s.size(); i++) if (s[i] == c) n++; return n; } /** trim leading spaces. spaces only, not tabs etc. */ inline std::string ltrim(const std::string& s) { const char* p = s.c_str(); while (*p == ' ') p++; return p; } } // namespace str } // namespace mongoutils namespace mongo { using namespace mongoutils; #if defined(_WIN32) inline int strcasecmp(const char* s1, const char* s2) { return _stricmp(s1, s2); } #endif } // namespace mongo