]> git.armaanb.net Git - gen-shell.git/blob - src/libshared/src/format.cpp
added install instructions
[gen-shell.git] / src / libshared / src / format.cpp
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 //
23 // http://www.opensource.org/licenses/mit-license.php
24 //
25 ////////////////////////////////////////////////////////////////////////////////
26
27 #include <cmake.h>
28 #include <format.h>
29 #include <utf8.h>
30 #include <algorithm>
31 #include <sstream>
32 #include <iostream>
33 #include <iomanip>
34 #include <cctype>
35 #include <strings.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <sys/select.h>
39 #include <time.h>
40 #include <csignal>
41 #include <cmath>
42
43 ////////////////////////////////////////////////////////////////////////////////
44 const std::string format (std::string& value)
45 {
46   return value;
47 }
48
49 ////////////////////////////////////////////////////////////////////////////////
50 const std::string format (const char* value)
51 {
52   std::string s (value);
53   return s;
54 }
55
56 ////////////////////////////////////////////////////////////////////////////////
57 const std::string formatHex (int value)
58 {
59   std::stringstream s;
60   s.setf (std::ios::hex, std::ios::basefield);
61   s << value;
62   return s.str ();
63 }
64
65 ////////////////////////////////////////////////////////////////////////////////
66 const std::string format (float value, int width, int precision)
67 {
68   std::stringstream s;
69   s.width (width);
70   s.precision (precision);
71   if (0 < value && value < 1)
72   {
73     // For value close to zero, width - 2 (2 accounts for the first zero and
74     // the dot) is the number of digits after zero that are significant
75     double factor = 1;
76     for (int i = 2; i < width; i++)
77       factor *= 10;
78     value = roundf (value * factor) / factor;
79   }
80   s << value;
81   return s.str ();
82 }
83
84 ////////////////////////////////////////////////////////////////////////////////
85 const std::string format (double value, int width, int precision)
86 {
87   std::stringstream s;
88   s.width (width);
89   s.precision (precision);
90   if (0 < value && value < 1)
91   {
92     // For value close to zero, width - 2 (2 accounts for the first zero and
93     // the dot) is the number of digits after zero that are significant
94     double factor = 1;
95     for (int i = 2; i < width; i++)
96       factor *= 10;
97     value = round (value * factor) / factor;
98   }
99   s << value;
100   return s.str ();
101 }
102
103 ////////////////////////////////////////////////////////////////////////////////
104 const std::string format (double value)
105 {
106   std::stringstream s;
107   s << std::fixed << value;
108   return s.str ();
109 }
110
111 ////////////////////////////////////////////////////////////////////////////////
112 void replace_positional (
113   std::string& fmt,
114   const std::string& from,
115   const std::string& to)
116 {
117   std::string::size_type pos = 0;
118   while ((pos = fmt.find (from, pos)) != std::string::npos)
119   {
120     fmt.replace (pos, from.length (), to);
121     pos += to.length ();
122   }
123 }
124
125 ////////////////////////////////////////////////////////////////////////////////
126 std::string leftJustify (const int input, const int width)
127 {
128   std::stringstream s;
129   s << input;
130   std::string output = s.str ();
131   return output + std::string (width - output.length (), ' ');
132 }
133
134 ////////////////////////////////////////////////////////////////////////////////
135 std::string leftJustify (const std::string& input, const int width)
136 {
137   return input + std::string (width - utf8_text_width (input), ' ');
138 }
139
140 ////////////////////////////////////////////////////////////////////////////////
141 std::string rightJustifyZero (const int input, const int width)
142 {
143   std::stringstream s;
144   s << std::setw (width) << std::setfill ('0') << input;
145   return s.str ();
146 }
147
148 ////////////////////////////////////////////////////////////////////////////////
149 std::string rightJustify (const int input, const int width)
150 {
151   std::stringstream s;
152   s << std::setw (width) << std::setfill (' ') << input;
153   return s.str ();
154 }
155
156 ////////////////////////////////////////////////////////////////////////////////
157 std::string rightJustify (const std::string& input, const int width)
158 {
159   unsigned int len = utf8_text_width (input);
160   return (((unsigned int) width > len)
161            ? std::string (width - len, ' ')
162            : "")
163          + input;
164 }
165
166 ////////////////////////////////////////////////////////////////////////////////
167 std::string commify (const std::string& data)
168 {
169   // First scan for decimal point and end of digits.
170   int decimalPoint = -1;
171   int end          = -1;
172
173   int i;
174   for (int i = 0; i < (int) data.length (); ++i)
175   {
176     if (isdigit (data[i]))
177       end = i;
178
179     if (data[i] == '.')
180       decimalPoint = i;
181   }
182
183   std::string result;
184   if (decimalPoint != -1)
185   {
186     // In reverse order, transfer all digits up to, and including the decimal
187     // point.
188     for (i = (int) data.length () - 1; i >= decimalPoint; --i)
189       result += data[i];
190
191     int consecutiveDigits = 0;
192     for (; i >= 0; --i)
193     {
194       if (isdigit (data[i]))
195       {
196         result += data[i];
197
198         if (++consecutiveDigits == 3 && i && isdigit (data[i - 1]))
199         {
200           result += ',';
201           consecutiveDigits = 0;
202         }
203       }
204       else
205         result += data[i];
206     }
207   }
208   else
209   {
210     // In reverse order, transfer all digits up to, but not including the last
211     // digit.
212     for (i = (int) data.length () - 1; i > end; --i)
213       result += data[i];
214
215     int consecutiveDigits = 0;
216     for (; i >= 0; --i)
217     {
218       if (isdigit (data[i]))
219       {
220         result += data[i];
221
222         if (++consecutiveDigits == 3 && i && isdigit (data[i - 1]))
223         {
224           result += ',';
225           consecutiveDigits = 0;
226         }
227       }
228       else
229         result += data[i];
230     }
231   }
232
233   // reverse result into data.
234   std::string done;
235   for (int i = (int) result.length () - 1; i >= 0; --i)
236     done += result[i];
237
238   return done;
239 }
240
241 ////////////////////////////////////////////////////////////////////////////////
242 // Convert a quantity in bytes to a more readable format.
243 std::string formatBytes (size_t bytes)
244 {
245   char formatted[24];
246
247        if (bytes >=  995000000) sprintf (formatted, "%.1f GiB", bytes / 1000000000.0);
248   else if (bytes >=     995000) sprintf (formatted, "%.1f MiB", bytes /    1000000.0);
249   else if (bytes >=        995) sprintf (formatted, "%.1f KiB", bytes /       1000.0);
250   else                          sprintf (formatted, "%d B",     (int)bytes);
251
252   return commify (formatted);
253 }
254
255 ////////////////////////////////////////////////////////////////////////////////
256 // Convert a quantity in seconds to a more readable format.
257 std::string formatTime (time_t seconds)
258 {
259   char formatted[24];
260   float days = (float) seconds / 86400.0;
261
262        if (seconds >= 86400 * 365) sprintf (formatted, "%.1f y", (days / 365.0));
263   else if (seconds >= 86400 * 84)  sprintf (formatted, "%1d mo", (int) (days / 30));
264   else if (seconds >= 86400 * 13)  sprintf (formatted, "%d wk",  (int) (float) (days / 7.0));
265   else if (seconds >= 86400)       sprintf (formatted, "%d d",   (int) days);
266   else if (seconds >= 3600)        sprintf (formatted, "%d h",   (int) (seconds / 3600));
267   else if (seconds >= 60)          sprintf (formatted, "%d m",   (int) (seconds / 60));
268   else if (seconds >= 1)           sprintf (formatted, "%d s",   (int) seconds);
269   else                             strcpy (formatted, "-");
270
271   return std::string (formatted);
272 }
273
274 ////////////////////////////////////////////////////////////////////////////////
275 std::string printable (const std::string& input)
276 {
277   // Sanitize 'message'.
278   std::string sanitized = input;
279   std::string::size_type bad;
280   while ((bad = sanitized.find ("\r")) != std::string::npos)
281     sanitized.replace (bad, 1, "\\r");
282
283   while ((bad = sanitized.find ("\n")) != std::string::npos)
284     sanitized.replace (bad, 1, "\\n");
285
286   while ((bad = sanitized.find ("\f")) != std::string::npos)
287     sanitized.replace (bad, 1, "\\f");
288
289   while ((bad = sanitized.find ("\t")) != std::string::npos)
290     sanitized.replace (bad, 1, "\\t");
291
292   while ((bad = sanitized.find ("\v")) != std::string::npos)
293     sanitized.replace (bad, 1, "\\v");
294
295   return sanitized;
296 }
297
298 ////////////////////////////////////////////////////////////////////////////////
299 std::string printable (char input)
300 {
301   // Sanitize 'message'.
302   char stringized[2] = {0};
303   stringized[0] = input;
304
305   std::string sanitized = stringized;
306   switch (input)
307   {
308   case '\r': sanitized = "\\r"; break;
309   case '\n': sanitized = "\\n"; break;
310   case '\f': sanitized = "\\f"; break;
311   case '\t': sanitized = "\\t"; break;
312   case '\v': sanitized = "\\v"; break;
313   default:   sanitized = input; break;
314   }
315
316   return sanitized;
317 }
318
319 ////////////////////////////////////////////////////////////////////////////////
320 // Iterate over the input, converting text to 'x'.
321 // Does not modify color codes.
322 std::string obfuscateText (const std::string& input)
323 {
324   std::stringstream output;
325   std::string::size_type i = 0;
326   int character;
327   bool inside = false;
328
329   while ((character = utf8_next_char (input, i)))
330   {
331     if (inside)
332     {
333       output << (char) character;
334
335       if (character == 'm')
336         inside = false;
337     }
338     else
339     {
340       if (character == 033)
341         inside = true;
342
343       if (inside || character == ' ')
344         output << (char) character;
345       else
346         output << 'x';
347     }
348   }
349
350   return output.str ();
351 }
352
353 ////////////////////////////////////////////////////////////////////////////////