1 /***
2     This file is part of snapcast
3     Copyright (C) 2014-2020  Johannes Pohl
4 
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 ***/
18 
19 #ifndef STRING_UTILS_H
20 #define STRING_UTILS_H
21 
22 #include <algorithm>
23 #include <map>
24 #include <sstream>
25 #include <stdio.h>
26 #include <string>
27 #include <vector>
28 #ifdef WINDOWS
29 #include <cctype>
30 #endif
31 
32 namespace utils
33 {
34 namespace string
35 {
36 
37 // trim from start
ltrim(std::string & s)38 static inline std::string& ltrim(std::string& s)
39 {
40     s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { return !std::isspace(ch); }));
41     return s;
42 }
43 
44 // trim from end
rtrim(std::string & s)45 static inline std::string& rtrim(std::string& s)
46 {
47     s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end());
48     return s;
49 }
50 
51 // trim from both ends
trim(std::string & s)52 static inline std::string& trim(std::string& s)
53 {
54     return ltrim(rtrim(s));
55 }
56 
57 // trim from start
ltrim_copy(const std::string & s)58 static inline std::string ltrim_copy(const std::string& s)
59 {
60     std::string str(s);
61     return ltrim(str);
62 }
63 
64 // trim from end
rtrim_copy(const std::string & s)65 static inline std::string rtrim_copy(const std::string& s)
66 {
67     std::string str(s);
68     return rtrim(str);
69 }
70 
71 // trim from both ends
trim_copy(const std::string & s)72 static inline std::string trim_copy(const std::string& s)
73 {
74     std::string str(s);
75     return trim(str);
76 }
77 
78 // decode %xx to char
uriDecode(const std::string & src)79 static std::string uriDecode(const std::string& src)
80 {
81     std::string ret;
82     char ch;
83     for (size_t i = 0; i < src.length(); i++)
84     {
85         if (int(src[i]) == 37)
86         {
87             unsigned int ii;
88             sscanf(src.substr(i + 1, 2).c_str(), "%x", &ii);
89             ch = static_cast<char>(ii);
90             ret += ch;
91             i += 2;
92         }
93         else
94         {
95             ret += src[i];
96         }
97     }
98     return (ret);
99 }
100 
101 
split_left(const std::string & s,char delim,std::string & left,std::string & right)102 static void split_left(const std::string& s, char delim, std::string& left, std::string& right)
103 {
104     auto pos = s.find(delim);
105     if (pos != std::string::npos)
106     {
107         left = s.substr(0, pos);
108         right = s.substr(pos + 1);
109     }
110     else
111     {
112         left = s;
113         right = "";
114     }
115 }
116 
117 
split_left(const std::string & s,char delim,std::string & right)118 static std::string split_left(const std::string& s, char delim, std::string& right)
119 {
120     std::string left;
121     split_left(s, delim, left, right);
122     return left;
123 }
124 
125 
126 
split(const std::string & s,char delim,std::vector<std::string> & elems)127 static std::vector<std::string>& split(const std::string& s, char delim, std::vector<std::string>& elems)
128 {
129     std::stringstream ss(s);
130     std::string item;
131     while (std::getline(ss, item, delim))
132     {
133         elems.push_back(item);
134     }
135     return elems;
136 }
137 
138 
split(const std::string & s,char delim)139 static std::vector<std::string> split(const std::string& s, char delim)
140 {
141     std::vector<std::string> elems;
142     split(s, delim, elems);
143     return elems;
144 }
145 
146 template <typename T>
split_pairs_to_container(const std::string & s,char pair_delim,char key_value_delim)147 static std::map<std::string, T> split_pairs_to_container(const std::string& s, char pair_delim, char key_value_delim)
148 {
149     std::map<std::string, T> result;
150     auto keyValueList = split(s, pair_delim);
151     for (auto& kv : keyValueList)
152     {
153         auto pos = kv.find(key_value_delim);
154         if (pos != std::string::npos)
155         {
156             std::string key = trim_copy(kv.substr(0, pos));
157             std::string value = trim_copy(kv.substr(pos + 1));
158             result[key].push_back(std::move(value));
159         }
160     }
161     return result;
162 }
163 
164 
split_pairs(const std::string & s,char pair_delim,char key_value_delim)165 static std::map<std::string, std::string> split_pairs(const std::string& s, char pair_delim, char key_value_delim)
166 {
167     std::map<std::string, std::string> result;
168     auto pairs = split_pairs_to_container<std::vector<std::string>>(s, pair_delim, key_value_delim);
169     for (auto& pair : pairs)
170         result[pair.first] = *pair.second.begin();
171     return result;
172 }
173 
174 
175 } // namespace string
176 } // namespace utils
177 
178 #endif
179