Mercurial > irccd
comparison common/util.cpp @ 0:1158cffe5a5e
Initial import
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 08 Feb 2016 16:43:14 +0100 |
parents | |
children | 03068f5ed79d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:1158cffe5a5e |
---|---|
1 /* | |
2 * util.cpp -- some utilities | |
3 * | |
4 * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 #include <algorithm> | |
20 #include <cctype> | |
21 #include <cstdlib> | |
22 #include <ctime> | |
23 #include <iomanip> | |
24 #include <sstream> | |
25 #include <stdexcept> | |
26 | |
27 #include <irccd-config.h> | |
28 | |
29 #include "util.h" | |
30 #include "unicode.h" | |
31 | |
32 using namespace std::string_literals; | |
33 | |
34 namespace irccd { | |
35 | |
36 namespace util { | |
37 | |
38 namespace { | |
39 | |
40 const std::unordered_map<std::string, int> colorTable{ | |
41 { "white", 0 }, | |
42 { "black", 1 }, | |
43 { "blue", 2 }, | |
44 { "green", 3 }, | |
45 { "red", 4 }, | |
46 { "brown", 5 }, | |
47 { "purple", 6 }, | |
48 { "orange", 7 }, | |
49 { "yellow", 8 }, | |
50 { "lightgreen", 9 }, | |
51 { "cyan", 10 }, | |
52 { "lightcyan", 11 }, | |
53 { "lightblue", 12 }, | |
54 { "pink", 13 }, | |
55 { "grey", 14 }, | |
56 { "lightgrey", 15 } | |
57 }; | |
58 | |
59 const std::unordered_map<std::string, char> attributesTable{ | |
60 { "bold", '\x02' }, | |
61 { "italic", '\x09' }, | |
62 { "strike", '\x13' }, | |
63 { "reset", '\x0f' }, | |
64 { "underline", '\x15' }, | |
65 { "underline2", '\x1f' }, | |
66 { "reverse", '\x16' } | |
67 }; | |
68 | |
69 std::string substituteDate(const std::string &text, const Substitution ¶ms) | |
70 { | |
71 std::ostringstream oss; | |
72 | |
73 #if defined(HAVE_STD_PUT_TIME) | |
74 oss << std::put_time(std::localtime(¶ms.time), text.c_str()); | |
75 #else | |
76 /* | |
77 * Quick and dirty hack because GCC does not have this function. | |
78 */ | |
79 char buffer[4096]; | |
80 | |
81 std::strftime(buffer, sizeof (buffer) - 1, text.c_str(), std::localtime(¶ms.time)); | |
82 | |
83 oss << buffer; | |
84 #endif | |
85 | |
86 return oss.str(); | |
87 } | |
88 | |
89 std::string substituteKeywords(const std::string &content, const Substitution ¶ms) | |
90 { | |
91 auto value = params.keywords.find(content); | |
92 | |
93 if (value != params.keywords.end()) | |
94 return value->second; | |
95 | |
96 return ""; | |
97 } | |
98 | |
99 std::string substituteEnv(const std::string &content) | |
100 { | |
101 auto value = std::getenv(content.c_str()); | |
102 | |
103 if (value != nullptr) | |
104 return value; | |
105 | |
106 return ""; | |
107 } | |
108 | |
109 std::string substituteAttributes(const std::string &content) | |
110 { | |
111 std::stringstream oss; | |
112 std::vector<std::string> list = split(content, ","); | |
113 | |
114 /* @{} means reset */ | |
115 if (list.empty()) { | |
116 oss << attributesTable.at("reset"); | |
117 } else { | |
118 /* Remove useless spaces */ | |
119 for (auto &a : list) | |
120 a = strip(a); | |
121 | |
122 /* | |
123 * 0: foreground | |
124 * 1: background | |
125 * 2-n: attributes | |
126 */ | |
127 auto foreground = list[0]; | |
128 if (!foreground.empty() || list.size() >= 2) { | |
129 /* Color sequence */ | |
130 oss << '\x03'; | |
131 | |
132 /* Foreground */ | |
133 auto it = colorTable.find(foreground); | |
134 if (it != colorTable.end()) | |
135 oss << it->second; | |
136 | |
137 /* Background */ | |
138 if (list.size() >= 2 && (it = colorTable.find(list[1])) != colorTable.end()) | |
139 oss << "," << it->second; | |
140 | |
141 /* Attributes */ | |
142 for (std::size_t i = 2; i < list.size(); ++i) { | |
143 auto attribute = attributesTable.find(list[i]); | |
144 | |
145 if (attribute != attributesTable.end()) | |
146 oss << attribute->second; | |
147 } | |
148 } | |
149 } | |
150 | |
151 return oss.str(); | |
152 } | |
153 | |
154 std::string substitute(std::string::const_iterator &it, std::string::const_iterator &end, char token, const Substitution ¶ms) | |
155 { | |
156 std::string content, value; | |
157 | |
158 if (it == end) | |
159 return ""; | |
160 | |
161 while (it != end && *it != '}') | |
162 content += *it++; | |
163 | |
164 if (*it != '}' || it == end) | |
165 throw std::invalid_argument("unclosed "s + token + " construct"s); | |
166 | |
167 it++; | |
168 | |
169 switch (token) { | |
170 case '#': | |
171 value = substituteKeywords(content, params); | |
172 break; | |
173 case '$': | |
174 value = substituteEnv(content); | |
175 break; | |
176 case '@': | |
177 value = substituteAttributes(content); | |
178 break; | |
179 default: | |
180 throw std::invalid_argument("unknown "s + token + " construct"); | |
181 } | |
182 | |
183 return value; | |
184 } | |
185 | |
186 } // !namespace | |
187 | |
188 std::string format(std::string text, const Substitution ¶ms) | |
189 { | |
190 std::ostringstream oss; | |
191 | |
192 /* | |
193 * Change the date format before anything else to avoid interpolation with keywords and | |
194 * user input. | |
195 */ | |
196 text = substituteDate(text, params); | |
197 | |
198 std::string::const_iterator it = text.begin(); | |
199 std::string::const_iterator end = text.end(); | |
200 | |
201 while (it != end) { | |
202 auto token = *it; | |
203 | |
204 if (token == '#' || token == '@' || token == '$') { | |
205 ++ it; | |
206 | |
207 if (it == end) { | |
208 oss << token; | |
209 continue; | |
210 } | |
211 | |
212 if (*it == '{') { | |
213 /* Do we have a variable? */ | |
214 oss << substitute(++it, end, token, params); | |
215 } else if (*it == token) { | |
216 /* Need one for sure */ | |
217 oss << token; | |
218 | |
219 /* Do we have a double token followed by a { for escaping? */ | |
220 if (++it == end) | |
221 continue; | |
222 | |
223 if (*it != '{') | |
224 oss << token; | |
225 } else { | |
226 oss << *it++; | |
227 } | |
228 } else { | |
229 oss << *it++; | |
230 } | |
231 } | |
232 | |
233 return oss.str(); | |
234 } | |
235 | |
236 std::string strip(std::string str) | |
237 { | |
238 auto test = [] (char c) { return !std::isspace(c); }; | |
239 | |
240 str.erase(str.begin(), std::find_if(str.begin(), str.end(), test)); | |
241 str.erase(std::find_if(str.rbegin(), str.rend(), test).base(), str.end()); | |
242 | |
243 return str; | |
244 } | |
245 | |
246 std::vector<std::string> split(const std::string &list, const std::string &delimiters, int max) | |
247 { | |
248 std::vector<std::string> result; | |
249 size_t next = -1, current; | |
250 int count = 1; | |
251 bool finished = false; | |
252 | |
253 if (list.empty()) | |
254 return result; | |
255 | |
256 do { | |
257 std::string val; | |
258 | |
259 current = next + 1; | |
260 next = list.find_first_of(delimiters, current); | |
261 | |
262 // split max, get until the end | |
263 if (max >= 0 && count++ >= max) { | |
264 val = list.substr(current, std::string::npos); | |
265 finished = true; | |
266 } else { | |
267 val = list.substr(current, next - current); | |
268 finished = next == std::string::npos; | |
269 } | |
270 | |
271 result.push_back(val); | |
272 } while (!finished); | |
273 | |
274 return result; | |
275 } | |
276 | |
277 MessagePair parseMessage(std::string message, const std::string &cc, const std::string &name) | |
278 { | |
279 std::string result = message; | |
280 bool iscommand = false; | |
281 | |
282 // handle special commands "!<plugin> command" | |
283 if (cc.length() > 0) { | |
284 auto pos = result.find_first_of(" \t"); | |
285 auto fullcommand = cc + name; | |
286 | |
287 /* | |
288 * If the message that comes is "!foo" without spaces we | |
289 * compare the command char + the plugin name. If there | |
290 * is a space, we check until we find a space, if not | |
291 * typing "!foo123123" will trigger foo plugin. | |
292 */ | |
293 if (pos == std::string::npos) | |
294 iscommand = result == fullcommand; | |
295 else | |
296 iscommand = result.length() >= fullcommand.length() && result.compare(0, pos, fullcommand) == 0; | |
297 | |
298 if (iscommand) { | |
299 /* | |
300 * If no space is found we just set the message to "" otherwise | |
301 * the plugin name will be passed through onCommand | |
302 */ | |
303 if (pos == std::string::npos) | |
304 result = ""; | |
305 else | |
306 result = message.substr(pos + 1); | |
307 } | |
308 } | |
309 | |
310 return MessagePair{result, ((iscommand) ? MessageType::Command : MessageType::Message)}; | |
311 } | |
312 | |
313 bool isBoolean(std::string value) noexcept | |
314 { | |
315 return (value = unicode::toupper(value)) == "1" || value == "YES" || value == "TRUE" || value == "ON"; | |
316 } | |
317 | |
318 bool isInt(const std::string &str, int base) noexcept | |
319 { | |
320 if (str.empty()) | |
321 return false; | |
322 | |
323 char *ptr; | |
324 | |
325 std::strtol(str.c_str(), &ptr, base); | |
326 | |
327 return *ptr == 0; | |
328 } | |
329 | |
330 bool isReal(const std::string &str) noexcept | |
331 { | |
332 if (str.empty()) | |
333 return false; | |
334 | |
335 char *ptr; | |
336 | |
337 std::strtod(str.c_str(), &ptr); | |
338 | |
339 return *ptr == 0; | |
340 } | |
341 | |
342 std::string nextNetwork(std::string &input) | |
343 { | |
344 std::string result; | |
345 std::string::size_type pos = input.find("\r\n\r\n"); | |
346 | |
347 if ((pos = input.find("\r\n\r\n")) != std::string::npos) { | |
348 result = input.substr(0, pos); | |
349 input.erase(input.begin(), input.begin() + pos + 4); | |
350 } | |
351 | |
352 return result; | |
353 } | |
354 | |
355 } // util | |
356 | |
357 } // !irccd |