1 /*
2  * Part of tivars_lib_cpp
3  * (C) 2015-2019 Adrien "Adriweb" Bertrand
4  * https://github.com/adriweb/tivars_lib_cpp
5  * License: MIT
6  */
7 
8 #include "tivarslib_utils.h"
9 #include <sstream>
10 #include <cmath>
11 
has_option(const options_t & m,const std::string & element)12 bool has_option(const options_t& m, const std::string& element)
13 {
14     return m.find(element) != m.end();
15 }
16 
hexdec(const std::string & str)17 unsigned char hexdec(const std::string& str)
18 {
19     return (unsigned char) stoul(str, nullptr, 16);
20 }
21 
dechex(unsigned char i,bool zeropad)22 std::string dechex(unsigned char i, bool zeropad)
23 {
24     std::string str = "00";
25     sprintf(&str[0], zeropad ? "%02X" : "%X", i);
26     return str;
27 }
28 
strtoupper(const std::string & str)29 std::string strtoupper(const std::string& str)
30 {
31     std::string newStr(str);
32     for (char& c : newStr)
33     {
34         c = (char) toupper(c);
35     }
36     return newStr;
37 }
38 
explode(const std::string & str,const std::string & delim)39 std::vector<std::string> explode(const std::string& str, const std::string& delim)
40 {
41     std::vector<std::string> result;
42 
43     size_t last = 0;
44     size_t next = 0;
45     while ((next = str.find(delim, last)) != std::string::npos)
46     {
47         result.push_back(str.substr(last, next - last));
48         last = next + delim.length();
49     }
50     result.push_back(str.substr(last));
51 
52     return result;
53 }
54 
explode(const std::string & str,char delim)55 std::vector<std::string> explode(const std::string& str, char delim)
56 {
57     return explode(str, std::string(1, delim));
58 }
59 
60 // trim from start
ltrim(std::string s,const char * t)61 std::string ltrim(std::string s, const char* t)
62 {
63     s.erase(0, s.find_first_not_of(t));
64     return s;
65 }
66 
67 // trim from end
rtrim(std::string s,const char * t)68 std::string rtrim(std::string s, const char* t)
69 {
70     s.erase(s.find_last_not_of(t) + 1);
71     return s;
72 }
73 
74 // trim from both ends
trim(const std::string & s,const char * t)75 std::string trim(const std::string& s, const char* t)
76 {
77     return ltrim(rtrim(s, t), t);
78 }
79 
str_repeat(const std::string & str,unsigned int times)80 std::string str_repeat(const std::string& str, unsigned int times)
81 {
82     std::string result;
83     result.reserve(times * str.length()); // avoid repeated reallocation
84     for (unsigned char i = 0; i < times; i++)
85     {
86         result += str;
87     }
88     return result;
89 }
90 
91 // From http://stackoverflow.com/a/2481126/378298
ParseCSV(const std::string & csvSource,std::vector<std::vector<std::string>> & lines)92 void ParseCSV(const std::string& csvSource, std::vector<std::vector<std::string>>& lines)
93 {
94     bool inQuote(false);
95     bool newLine(false);
96     std::string field;
97     lines.clear();
98     std::vector<std::string> line;
99 
100     std::string::const_iterator aChar = csvSource.begin();
101     std::string::const_iterator tmp;
102 
103     while (aChar != csvSource.end())
104     {
105         tmp = aChar;
106         switch (*aChar)
107         {
108             case '"':
109                 newLine = false;
110                 // Handle weird escaping of quotes ("""" => ")
111                 if (*(tmp+1) == '"' && *(tmp+2) == '"' && *(tmp+3) == '"') {
112                     field.push_back(*aChar);
113                     aChar += 3;
114                 } else {
115                     inQuote = !inQuote;
116                 }
117                 break;
118 
119             case ',':
120                 newLine = false;
121                 if (inQuote)
122                 {
123                     field += *aChar;
124                 }
125                 else
126                 {
127                     line.push_back(field);
128                     field.clear();
129                 }
130                 break;
131 
132             case '\n':
133             case '\r':
134                 if (inQuote)
135                 {
136                     field += *aChar;
137                 }
138                 else
139                 {
140                     if (!newLine)
141                     {
142                         line.push_back(field);
143                         lines.push_back(line);
144                         field.clear();
145                         line.clear();
146                         newLine = true;
147                     }
148                 }
149                 break;
150 
151             default:
152                 newLine = false;
153                 field.push_back(*aChar);
154                 break;
155         }
156 
157         ++aChar;
158     }
159 
160     if (!field.empty())
161         line.push_back(field);
162 
163     if (!line.empty())
164         lines.push_back(line);
165 }
166 
is_numeric(const std::string & str)167 bool is_numeric(const std::string& str)
168 {
169     char* p;
170     double ignored = ::strtod(str.c_str(), &p);
171     (void)ignored;
172     return (bool)!*p;
173 }
174 
file_exists(const std::string & path)175 bool file_exists(const std::string& path) {
176     if (path.empty()) {
177         return false;
178     }
179     if (FILE *file = fopen(path.c_str(), "r")) {
180         fclose(file);
181         return true;
182     } else {
183         return false;
184     }
185 }
186 
str_pad(const std::string & str,unsigned long pad_length,std::string pad_string)187 std::string str_pad(const std::string& str, unsigned long pad_length, std::string pad_string)
188 {
189     unsigned long i, j, x;
190     unsigned long str_size = str.size();
191     unsigned long pad_size = pad_string.size();
192 
193     if (pad_length <= str_size || pad_size < 1)
194     {
195         return str;
196     }
197 
198     std::string o;
199     o.reserve(pad_length);
200 
201     for (i = 0, x = str_size; i < x; i++)
202     {
203         o.push_back(str[i]);
204     }
205     for (i = str_size; i < pad_length;)
206     {
207         for (j = 0; j < pad_size && i < pad_length; j++, i++)
208         {
209             o.push_back(pad_string[j]);
210         }
211     }
212 
213     return o;
214 }
215 
multiple(int num,const std::string & var)216 std::string multiple(int num, const std::string &var) {
217     const std::string unit = var.empty() ? "1" : var;
218     switch (num) {
219         case 0:
220             return "0";
221         case 1:
222             return unit;
223         case -1:
224             return "-" + unit;
225         default:
226             return std::to_string(num) + var;
227     }
228 }
229 
230 // Adapted from http://stackoverflow.com/a/32903747/378298
dec2frac(double num,const std::string & var,double err)231 std::string dec2frac(double num, const std::string& var, double err)
232 {
233     if (err <= 0.0 || err >= 1.0)
234     {
235         err = 0.001;
236     }
237 
238     int sign = ( num > 0 ) ? 1 : ( ( num < 0 ) ? -1 : 0 );
239 
240     if (sign == -1)
241     {
242         num = std::abs(num);
243     }
244 
245     if (sign != 0)
246     {
247         // err is the maximum relative err; convert to absolute
248         err *= num;
249     }
250 
251     int n = (int) std::floor(num);
252     num -= n;
253 
254     if (num < err)
255     {
256         return multiple(sign * n, var);
257     }
258 
259     if (1 - err < num)
260     {
261         return multiple(sign * (n + 1), var);
262     }
263 
264     // The lower fraction is 0/1
265     int lower_n = 0;
266     int lower_d = 1;
267 
268     // The upper fraction is 1/1
269     int upper_n = 1;
270     int upper_d = 1;
271 
272     while (true)
273     {
274         // The middle fraction is (lower_n + upper_n) / (lower_d + upper_d)
275         int middle_n = lower_n + upper_n;
276         int middle_d = lower_d + upper_d;
277 
278         if (middle_d * (num + err) < middle_n)
279         {
280             // real + err < middle : middle is our new upper
281             upper_n = middle_n;
282             upper_d = middle_d;
283         }
284         else if (middle_n < (num - err) * middle_d)
285         {
286             // middle < real - err : middle is our new lower
287             lower_n = middle_n;
288             lower_d = middle_d;
289         } else {
290             // Middle is our best fraction
291             return multiple((n * middle_d + middle_n) * sign, var) + "/" + std::to_string(middle_d);
292         }
293     }
294 }
295 
trimZeros(const std::string & str)296 std::string trimZeros(const std::string& str)
297 {
298     return std::to_string(std::stoi(str));
299 }
300