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