strings.cpp
1 //------------------------------------------------------------------------------
2 // strings.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (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, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301, USA.
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2010 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "strings.h"
24 
25 #include "random.h"
26 
27 #include "plugins/engineplugin.h"
28 #include "plugins/pluginloader.h"
29 
30 #include <cassert>
31 #include <cmath>
32 
33 #include <QDateTime>
34 #include <QDir>
35 #include <QStringList>
36 #include <QUrl>
37 
38 const char Strings::RANDOM_CHAR_POOL[RANDOM_CHAR_POOL_SIZE] =
39 {
40  'a', 'b', 'c', 'd', 'e', 'f', 'g',
41  'h', 'i', 'j', 'k', 'l', 'm', 'n',
42  'o', 'p', 'q', 'r', 's', 't', 'u',
43  'v', 'w', 'x', 'y', 'z', '0', '1',
44  '2', '3', '4', '5', '6', '7', '8',
45  '9'
46 };
47 
48 QString Strings::colorizeString(const QString &str, int current)
49 {
50  static const char colorChart[22][7] =
51  {
52  "FF91A4", //a
53  "D2B48C", //b
54  "808080", //c
55  "32CD32", //d
56  "918151", //e
57  "F4C430", //f
58  "E32636", //g
59  "0000FF", //h
60  "FF8C00", //i
61  "C0C0C0", //j
62  "FFD700", //k
63  "E34234", //l
64  "000000", //m
65  "4169E1", //n
66  "FFDEAD", //o
67  "465945", //p
68  "228b22", //q
69  "800000", //r
70  "704214", //s
71  "A020F0", //t
72  "404040", //u
73  "007F7F", //v
74  };
75 
76  QString ret;
77  bool colored = false;
78  for(int i = 0;i < str.length();i++)
79  {
80  if(str[i] == ESCAPE_COLOR_CHAR)
81  {
82  i++;
83  if(i >= str.length())
84  break;
85  QChar colorChar = str[i].toLower();
86  int color = colorChar.toAscii() - 97;
87 
88  // special cases
89  if(colorChar == '+')
90  color = current == 0 ? 19 : current-1; // + is the current minus one, wrap if needed.
91  else if(colorChar == '*')
92  color = 3; // Chat color which is usally green
93  else if(colorChar == '!')
94  color = 16; // Team char (usually green, but made dark green here for distinction)
95  else if(colorChar == '[') // Named!
96  {
97  int end = str.indexOf(']', i);
98  if(end == -1)
99  break;
100  QString colorName = str.mid(i+1, end-i-1);
101  if(colorName.indexOf('"') == -1) // Just in case there's a security problem.
102  ret += QString("<span style=\"color: " + colorName + "\">");
103  i += colorName.length()+1;
104  colored = true;
105  continue;
106  }
107  else if(colorChar == '-')
108  {
109  if(colored)
110  ret += "</span>";
111  colored = false;
112  continue;
113  }
114 
115  if(colored)
116  {
117  ret += "</span>";
118  colored = false;
119  }
120 
121  if(color >= 0 && color < 22)
122  {
123  ret += QString("<span style=\"color: #") + colorChart[color] + "\">";
124  colored = true;
125  }
126  continue;
127  }
128  ret += str[i];
129  }
130  if(colored)
131  ret += "</span>";
132  return ret;
133 }
134 
135 QStringList Strings::combineManyPaths(const QStringList &fronts, const QString &pathEnd)
136 {
137  QStringList result;
138  foreach (const QString &s, fronts)
139  {
140  result << combinePaths(s, pathEnd);
141  }
142  return result;
143 }
144 
145 QString Strings::combinePaths(QString pathFront, QString pathEnd)
146 {
147  QString combinedPath;
148 
149  // One of them is NULL
150  if (pathFront.isEmpty())
151  {
152  return pathEnd;
153  }
154 
155  if (pathEnd.isEmpty())
156  {
157  return pathFront;
158  }
159 
160  pathFront = Strings::trimr(pathFront, "/\\");
161  pathEnd = Strings::triml(pathEnd, "/\\");
162 
163  combinedPath = pathFront + "/" + pathEnd;
164  combinedPath = normalizePath(combinedPath);
165 
166  return combinedPath;
167 }
168 
169 QString Strings::createRandomAlphaNumericString(unsigned numChars)
170 {
171  QString generatedString = "";
172  for (unsigned i = 0; i < numChars; ++i)
173  {
174  unsigned index = (unsigned) Random::nextUShort(RANDOM_CHAR_POOL_SIZE);
175  generatedString += RANDOM_CHAR_POOL[index];
176  }
177 
178  return generatedString;
179 }
180 
181 QString Strings::createRandomAlphaNumericStringWithNewLines(unsigned numCharsPerLine, unsigned numLines)
182 {
183  QString generatedString = "";
184  for (unsigned i = 0; i < numLines; ++i)
185  {
186  generatedString += createRandomAlphaNumericString(numCharsPerLine) + "\n";
187  }
188 
189  return generatedString;
190 }
191 
192 // NOTE: Be sure that '\\' is the first thing in the array otherwise it will re-escape.
193 static char escapeCharacters[] = {'\\', '"', 0};
194 const QString& Strings::escape(QString &str)
195 {
196  for(unsigned int i = 0;escapeCharacters[i] != 0;i++)
197  {
198  // += 2 because we'll be inserting 1 character.
199  for(int p = 0;p < str.length() && (p = str.indexOf(escapeCharacters[i], p)) != -1;p += 2)
200  {
201  str.insert(p, '\\');
202  }
203  }
204  return str;
205 }
206 const QString& Strings::unescape(QString &str)
207 {
208  for(unsigned int i = 0;escapeCharacters[i] != 0;i++)
209  {
210  QString sequence = "\\" + QString(escapeCharacters[i]);
211  for(int p = 0;p < str.length() && (p = str.indexOf(sequence, p)) != -1;p++)
212  str.replace(str.indexOf(sequence, p), 2, escapeCharacters[i]);
213  }
214  return str;
215 }
216 
217 QString Strings::formatDataAmount(qint64 bytes)
218 {
219  DataUnit dataUnit;
220 
221  float fBytes = (float)bytes;
222  fBytes = scaleDataUnit(fBytes, dataUnit);
223 
224  QString formattedString = QString("%1 ").arg(fBytes, 0, 'f', 2);
225  switch(dataUnit)
226  {
227  case Byte:
228  formattedString += "B";
229  break;
230 
231  case Kilobyte:
232  formattedString += "kB";
233  break;
234 
235  case Megabyte:
236  formattedString += "MB";
237  break;
238 
239  case Gigabyte:
240  formattedString += "GB";
241  break;
242 
243  default:
244  // Shouldn't really happen.
245  return "#ERR: Formatting data amount error.";
246  }
247 
248  return formattedString;
249 }
250 
251 QString Strings::formatDataSpeed(float speedInBytesPerSecond)
252 {
253  DataUnit dataUnit;
254 
255  speedInBytesPerSecond = scaleDataUnit(speedInBytesPerSecond, dataUnit);
256 
257  QString formattedString = QString("%1 ").arg(speedInBytesPerSecond, 0, 'f', 2);
258  switch(dataUnit)
259  {
260  case Byte:
261  formattedString += "B/s";
262  break;
263 
264  case Kilobyte:
265  formattedString += "kB/s";
266  break;
267 
268  case Megabyte:
269  formattedString += "MB/s";
270  break;
271 
272  case Gigabyte:
273  formattedString += "GB/s";
274  break;
275 
276  default:
277  // Shouldn't really happen.
278  return "#ERR: Formatting speed error.";
279  }
280 
281  return formattedString;
282 }
283 
284 QString Strings::formatTime(float seconds)
285 {
286  if (seconds < 0.0f)
287  {
288  return "#ERR: Formatting time error.";
289  }
290 
291  seconds = ceil(seconds);
292 
293  // QTime is a 24-hour clock. It cannot be used here since seconds input
294  // can be larger than that.
295 
296  int hours = 0;
297  int minutes = 0;
298  int remainingSeconds = 0;
299 
300  if (seconds >= 3600.0f)
301  {
302  // An hour or more.
303  hours = seconds / 3600.0f;
304  seconds -= hours * 3600.0f;
305  }
306 
307  if (seconds >= 60.0f)
308  {
309  // A minute or more.
310  minutes = seconds / 60.0f;
311  seconds -= minutes * 60.0f;
312  }
313 
314  remainingSeconds = (int)seconds;
315 
316  QString formattedString;
317  if (hours > 0)
318  {
319  formattedString += QString("%1h ").arg(hours);
320  }
321 
322  if (hours > 0 || minutes > 0)
323  {
324  formattedString += QString("%1min. ").arg(minutes);
325  }
326 
327  formattedString += QString("%1s").arg(remainingSeconds);
328 
329  return formattedString;
330 }
331 
332 bool Strings::isCharOnCharList(char c, const QString& charList)
333 {
334  for (int i = 0; i < charList.length(); ++i)
335  {
336  if (charList[i] == c)
337  return true;
338  }
339 
340  return false;
341 }
342 
343 bool Strings::isUrlSafe(const QString& url)
344 {
345  QUrl urlObject = url;
346 
347  QString scheme = urlObject.scheme();
348 
349  bool bIsSafe1 = scheme.isEmpty();
350  bool bIsSafe2 = (scheme.compare("http", Qt::CaseInsensitive) == 0);
351  bool bIsSafe3 = (scheme.compare("ftp", Qt::CaseInsensitive) == 0);
352 
353 
354  return bIsSafe1 || bIsSafe2 || bIsSafe3;
355 }
356 
357 QString Strings::normalizePath(QString path)
358 {
359  path = QDir::fromNativeSeparators(path);
360  path = QDir::cleanPath(path);
361 
362  return path;
363 }
364 
365 QByteArray Strings::readUntilByte(QDataStream& stream, unsigned char stopByte)
366 {
367  QByteArray result;
368  bool bStopByteEncountered = false;
369 
370  while (!stream.atEnd() && !bStopByteEncountered)
371  {
372  quint8 rByte;
373  stream >> rByte;
374  result += rByte;
375 
376  if (rByte == stopByte)
377  {
378  bStopByteEncountered = true;
379  }
380  }
381 
382  return result;
383 }
384 
385 float Strings::scaleDataUnit(float bytes, DataUnit& outUnit)
386 {
387  const static float UPPER_BOUNDARY = 900.0f;
388  outUnit = Byte;
389 
390  while (bytes > UPPER_BOUNDARY && outUnit != Gigabyte)
391  {
392  bytes /= 1024.0f;
393  outUnit = (DataUnit)((int)outUnit + 1);
394  }
395 
396  return bytes;
397 }
398 
399 QString Strings::timestamp(const QString& format)
400 {
401  return QDateTime::currentDateTime().toString(format);
402 }
403 
404 void Strings::translateServerAddress(const QString& addressString, QString& hostname, unsigned short& port, const QString& defaultAddress)
405 {
406  port = 0;
407  QStringList addressAndPort = addressString.split(":");
408  QStringList defaultAddressAndPort = defaultAddress.split(":");
409 
410  if (addressAndPort.size() >= 1 && addressAndPort.size() <= 2)
411  {
412  hostname = addressAndPort[0];
413  if (addressAndPort.size() == 2)
414  {
415  port = addressAndPort[1].toUShort();
416  }
417  }
418  else
419  {
420  // if something is not right set default settings
421  if (defaultAddressAndPort.size() >= 1)
422  {
423  hostname = defaultAddressAndPort[0];
424  }
425  }
426 
427  if (port == 0 && defaultAddressAndPort.size() >= 2)
428  {
429  port = defaultAddressAndPort[1].toUShort();
430  }
431 }
432 
433 QString& Strings::trimr(QString& str, const QString& charList)
434 {
435  int i;
436  for (i = str.length() - 1; i >= 0; --i)
437  {
438  if (!isCharOnCharList(str[i].toAscii(), charList))
439  break;
440  }
441  ++i;
442 
443  return str.remove(i, str.length() - i);
444 }
445 
446 QString& Strings::triml(QString& str, const QString& charList)
447 {
448  int i;
449  for (i = 0; i < str.length(); ++i)
450  {
451  if (!isCharOnCharList(str[i].toAscii(), charList))
452  break;
453  }
454 
455  return str.remove(0, i);
456 }
457 
458 QString Strings::wrapUrlsWithHtmlATags(const QString& str)
459 {
460  static QString pluginSchemes;
461  if(pluginSchemes.isEmpty())
462  {
463  pluginSchemes = "zds";
464 
465  for(unsigned int i = 0;i < gPlugins->numPlugins();++i)
466  pluginSchemes = QString("%1|%2").arg(pluginSchemes)
467  .arg(gPlugins->plugin(i)->info()->data()->scheme);
468  }
469 
470  QRegExp pattern(QString("("
471  "("
472  "(http|https|ftp|%1)://"
473  "|(www\\.)"
474  ")[\\w\\-\\.,@?^=%&amp;:/~\\+#\\(\\)]+"
475  ")").arg(pluginSchemes), Qt::CaseInsensitive);
476  QString newString = str;
477 
478  int offset = 0;
479  int index = -1;
480  while ((index = pattern.indexIn(newString, offset)) >= 0)
481  {
482  QString cap = pattern.cap(1);
483  int capLength = cap.length();
484 
485  QString replacement = cap;
486  if (cap.startsWith("www.", Qt::CaseInsensitive))
487  {
488  replacement = "http://" + cap;
489  }
490 
491  replacement = QString("<a href=\"%1\">%2</a>").arg(replacement, cap);
492 
493  newString.replace(index, capLength, replacement);
494  offset = index + replacement.length();
495  }
496 
497  return newString;
498 }
static unsigned short nextUShort(unsigned short max)
Generates a new random unsigned short.
Definition: random.cpp:48
static QString combinePaths(QString pathFront, QString pathEnd)
Definition: strings.cpp:145
static void translateServerAddress(const QString &addressString, QString &hostname, unsigned short &port, const QString &defaultAddress)
Translates string in format "hostname:port" to atomic values.
Definition: strings.cpp:404
static QString formatTime(float seconds)
Formats a numerical time value into a string.
Definition: strings.cpp:284
static QString normalizePath(QString path)
Creates a clean path.
Definition: strings.cpp:357
static QString wrapUrlsWithHtmlATags(const QString &str)
Detects all links within a given string and wraps them in <a href> tags.
Definition: strings.cpp:458
static bool isUrlSafe(const QString &url)
Unsafe URLs begin with file:// and this functions returns false for such URLs.
Definition: strings.cpp:343
static QString formatDataAmount(qint64 bytes)
Similar to formatDataSpeed().
Definition: strings.cpp:217
static QString formatDataSpeed(float speedInBytesPerSecond)
Formats a numerical speed value into a string.
Definition: strings.cpp:251
static QByteArray readUntilByte(QDataStream &stream, unsigned char stopByte)
Reads raw data from the current position of passed QDataStream until a specified byte is encountered...
Definition: strings.cpp:365
static const QString & escape(QString &str)
Adds escape characters to a string.
Definition: strings.cpp:194
static QStringList combineManyPaths(const QStringList &fronts, const QString &pathEnd)
Combines path suffix with all fronts, returns new list.
Definition: strings.cpp:135
static QString colorizeString(const QString &str, int def=4)
Definition: strings.cpp:48
static QString createRandomAlphaNumericString(unsigned numChars)
Creates a random string with specified length.
Definition: strings.cpp:169