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 &params)
70 {
71 std::ostringstream oss;
72
73 #if defined(HAVE_STD_PUT_TIME)
74 oss << std::put_time(std::localtime(&params.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(&params.time));
82
83 oss << buffer;
84 #endif
85
86 return oss.str();
87 }
88
89 std::string substituteKeywords(const std::string &content, const Substitution &params)
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 &params)
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 &params)
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