Vidalia 0.2.10

stringutil.cpp

Go to the documentation of this file.
00001 /*
00002 **  This file is part of Vidalia, and is subject to the license terms in the
00003 **  LICENSE file, found in the top level directory of this distribution. If you
00004 **  did not receive the LICENSE file with this file, you may obtain it from the
00005 **  Vidalia source package distributed by the Vidalia Project at
00006 **  http://www.vidalia-project.net/. No part of Vidalia, including this file,
00007 **  may be copied, modified, propagated, or distributed except according to the
00008 **  terms described in the LICENSE file.
00009 */
00010 
00011 /*
00012 ** \file stringutil.cpp
00013 ** \version $Id: stringutil.cpp 4350 2010-07-14 15:43:39Z edmanm $
00014 ** \brief Common string manipulation functions
00015 */
00016 
00017 #include "stringutil.h"
00018 
00019 #include <QCoreApplication>
00020 #include <QApplication>
00021 
00022 
00023 /** Create a QStringList from the array of C-style strings. */
00024 QStringList
00025 char_array_to_stringlist(char **arr, int len)
00026 {
00027   QStringList list;
00028   for (int i = 0; i < len; i++) {
00029     list << QString(arr[i]);
00030   }
00031   return list;
00032 }
00033 
00034 /** Conditionally assigns errmsg to str if str is not null and returns false.
00035  * This is a seemingly pointless function, but it saves some messiness in
00036  * methods whose QString *errmsg parameter is optional. */
00037 bool
00038 err(QString *str, const QString &errmsg)
00039 {
00040   if (str) {
00041     *str = errmsg;
00042   }
00043   return false;
00044 }
00045 
00046 /** Ensures all characters in str are in validChars. If a character appears
00047  * in str but not in validChars, it will be removed and the resulting
00048  * string returned. */
00049 QString
00050 ensure_valid_chars(const QString &str, const QString &validChars)
00051 {
00052   QString out = str;
00053   for (int i = 0; i < str.length(); i++) {
00054     QChar c = str.at(i);
00055     if (validChars.indexOf(c) < 0) {
00056       out.remove(c);
00057     }
00058   }
00059   return out;
00060 }
00061 
00062 /** Scrubs an email address by replacing "@" with " at " and "." with " dot ". */
00063 QString
00064 scrub_email_addr(const QString &email)
00065 {
00066   QString scrubbed = email;
00067   scrubbed = scrubbed.replace("@", " at ");
00068   scrubbed = scrubbed.replace(".", " dot ");
00069   return scrubbed;
00070 }
00071 
00072 /** Wraps <b>str</b> at <b>width</b> characters wide, using <b>sep</b> as the
00073  * word separator (" ", for example), and placing the line ending <b>le</b> at
00074  * the end of each line, except the last. */
00075 QString
00076 string_wrap(const QString &str, int width,
00077             const QString &sep, const QString &le)
00078 {
00079   QString wrapped;
00080   int pos, nextsep, wordlen, n;
00081   int seplen = sep.length();
00082  
00083   if (str.length() < width) {
00084     return str;
00085   }
00086 
00087   pos = 0; 
00088   n = width;
00089   while (pos < str.length()) {
00090     /* Get the length of a "word" */
00091     nextsep = str.indexOf(sep, pos);
00092     if (nextsep < 0) {
00093       nextsep = str.length();
00094     }
00095     wordlen = nextsep-pos;
00096 
00097     /* Check if there is room for the word on this line */
00098     if (wordlen > n) {
00099       /* Create a new line */
00100       wrapped.append(le);
00101       n = width;
00102     }
00103 
00104     /* Add the word to the current line */
00105     wrapped.append(str.mid(pos, wordlen+seplen));
00106     n = n - wordlen - seplen;
00107     pos += wordlen + seplen;
00108   }
00109   return wrapped.trimmed();
00110 }
00111 
00112 /** Encodes the bytes in <b>buf</b> as an uppercase hexadecimal string and
00113  * returns the result. This function is derived from base16_encode() in Tor's
00114  * util.c. See LICENSE for details on Tor's license. */
00115 QString
00116 base16_encode(const QByteArray &buf)
00117 {
00118   QString hex;
00119   for (int i = 0; i < buf.size(); i++) {
00120     hex += "0123456789ABCDEF"[((quint8)buf[i]) >>  4];
00121     hex += "0123456789ABCDEF"[((quint8)buf[i]) & 0xf];
00122   }
00123   return hex;
00124 }
00125 
00126 /** Given an ASCII string <b>str</b>, this function returns a quoted string
00127  * with all escaped characters unescaped. Non-ASCII characters in the string
00128  * will be converted to the local 8-bit character encoding and encoded using
00129  * an escaped octal sequence. The returned string will thus contain only
00130  * printable ASCII characters. */
00131 QString
00132 string_escape(const QString &str)
00133 {
00134   QByteArray in;
00135   QByteArray out;
00136   char c;
00137 
00138   in = str.toLocal8Bit();
00139   out.append('\"');
00140   for (int i = 0; i < in.length(); i++) {
00141     c = in[i];
00142     switch (c) {
00143       case '\"':
00144         out.append("\\\"");
00145         break;
00146       case '\\':
00147         out.append("\\\\");
00148         break;
00149       case '\n':
00150         out.append("\\n");
00151         break;
00152       case '\r':
00153         out.append("\\r");
00154         break;
00155       case '\t':
00156         out.append("\\t");
00157         break;
00158       default:
00159         if (QChar(c).isPrint() && c < 127) {
00160           out.append(c);
00161         } else {
00162           out.append('\\');
00163           out.append(QString::number(c, 8).toAscii());
00164         }
00165     }
00166   }
00167   out.append('\"');
00168   return QString::fromAscii(out);
00169 }
00170 
00171 /** Given a quoted string <b>str</b>, this function returns an unquoted,
00172  * unescaped string. <b>str</b> must start and end with an unescaped DQUOTE,
00173  * The input string must contain only ASCII characters; however, non-ASCII
00174  * characters can be included by encoding their byte sequences in either
00175  * escaped hexadecimal (e.g., "\xFF") or octal (e.g., "\301"). The result
00176  * will be converted to a QString using the local 8-bit encoding. */
00177 QString
00178 string_unescape(const QString &str, bool *ok)
00179 {
00180   QByteArray out;
00181   int i;
00182 
00183   /* The string must start and end with an unescaped dquote */
00184   if (str.length() < 2)
00185     goto err;
00186   if (! str.startsWith("\"") || ! str.endsWith("\""))
00187     goto err;
00188   if (str.endsWith("\\\"") && ! str.endsWith("\\\\\""))
00189     goto err;
00190 
00191   i = 1;
00192   while (i < str.length()-1) {
00193     if (str[i] == QLatin1Char('\\')) {
00194       QChar c = str[++i];
00195       if (c == QLatin1Char('n')) {
00196         out.append('\n');
00197       } else if (c == QLatin1Char('r')) {
00198         out.append('\r');
00199       } else if (c == QLatin1Char('t')) {
00200         out.append('\t');
00201       } else if (c == QLatin1Char('x')) {
00202         if (i + 2 >= str.length())
00203           goto err;
00204         bool isHex;
00205         char val = static_cast<char>(str.mid(i+1, 2).toUInt(&isHex, 16));
00206         if (! isHex)
00207           goto err;
00208         out.append(val);
00209         i = i + 2;
00210       } else if (c.isDigit()) {
00211         if (i + 2 >= str.length())
00212           goto err;
00213         bool isOctal;
00214         uint val = str.mid(i, 3).toUInt(&isOctal, 8);
00215         if (! isOctal || val > 255)
00216           goto err;
00217         out.append(static_cast<char>(val));
00218         i = i + 2;
00219       } else {
00220         out.append(str[i].toLatin1());
00221       }
00222     } else if (str[i] == QLatin1Char('\"')) {
00223       /* Unescaped DQUOTE in the middle of the string, so terminate
00224        * processing and return a failure. */
00225       goto err;
00226     } else {
00227       out.append(str[i].toLatin1());
00228     }
00229     i++;
00230   }
00231   if (ok)
00232     *ok = true;
00233   return QString::fromLocal8Bit(out.data());
00234 
00235 err:
00236   if (ok)
00237     *ok = false;
00238   return QString();
00239 }
00240 
00241 /** Parses a series of space-separated key[=value|="value"] tokens from
00242  * <b>str</b> and returns the mappings in a QHash. If <b>str</b> was unable
00243  * to be parsed, <b>ok</b> is set to false. */
00244 QHash<QString,QString>
00245 string_parse_keyvals(const QString &str, bool *ok)
00246 {
00247   int i, len;
00248   bool tmp_ok;
00249   QHash<QString,QString> keyvals;
00250   
00251   i = 0;
00252   len = str.length();
00253   while (i < len && str[i].isSpace())
00254     i++; /* Skip initial whitespace */
00255   while (i < len) {
00256     QString key, val;
00257     
00258     while (i < len && !str[i].isSpace() && str[i] != '=')
00259       key.append(str[i++]);
00260       
00261     if (i < len && str[i] == '=') {
00262       if (++i < len && str[i] == '\"') {
00263         /* The value is wrapped in quotes */
00264         val.append(str[i]);
00265         while (++i < len) {
00266           val.append(str[i]);
00267           if (str[i] == '\\') {
00268             if (++i == len)
00269               goto error;
00270             val.append(str[i]);
00271           } else if (str[i] == '\"') {
00272             i++;
00273             break;
00274           } 
00275         }
00276         val = string_unescape(val, &tmp_ok);
00277         if (!tmp_ok)
00278           goto error;
00279         keyvals.insert(key, val);
00280       } else {
00281         /* The value was not wrapped in quotes */
00282         while (i < len && !str[i].isSpace())
00283           val.append(str[i++]);
00284         keyvals.insert(key, val);
00285       }
00286     } else {
00287       /* The key had no value */
00288       keyvals.insert(key, QString(""));
00289     }
00290     while (i < len && str[i].isSpace())
00291       i++;
00292   }
00293   if (ok)
00294     *ok = true;
00295   return keyvals;
00296 
00297 error:
00298   if (ok)
00299     *ok = false;
00300   return QHash<QString,QString>();
00301 }
00302 
00303 /** Parses a series of command line arguments from <b>str</b>. If <b>str</b>
00304  * was unable to be parsed, <b>ok</b> is set to false. */
00305 QStringList
00306 string_parse_arguments(const QString &str, bool *ok)
00307 {
00308   QStringList args;
00309   int i, len;
00310   bool tmp_ok;
00311 
00312   i = 0;
00313   len = str.length();
00314   while (i < len && str[i].isSpace())
00315     i++; /* Skip initial whitespace */
00316   while (i < len) {
00317     QString arg;
00318     
00319     if (str[i] == '\"') {
00320       /* The value is wrapped in quotes */
00321       arg.append(str[i]);
00322       while (++i < len) {
00323         arg.append(str[i]);
00324         if (str[i] == '\\') {
00325           if (++i == len)
00326             goto error;
00327           arg.append(str[i]);
00328         } else if (str[i] == '\"') {
00329           i++;
00330           break;
00331         } 
00332       }
00333       arg = string_unescape(arg, &tmp_ok);
00334       if (!tmp_ok)
00335         goto error;
00336       args << arg;
00337     } else {
00338       /* The value was not wrapped in quotes */
00339       while (i < len && !str[i].isSpace())
00340         arg.append(str[i++]);
00341       args << arg;
00342     }
00343     while (i < len && str[i].isSpace())
00344       i++;
00345   }
00346 
00347   if (ok)
00348     *ok = true;
00349   return args;
00350 
00351 error:
00352   if (ok)
00353     *ok = false;
00354   return QStringList();
00355 }
00356 
00357 /** Formats the list of command line arguments in <b>args</b> as a string.
00358  * Arguments that contain ' ', '\', or '"' tokens will be escaped and
00359  * wrapped in double quotes. */
00360 QString
00361 string_format_arguments(const QStringList &args)
00362 {
00363   QStringList out;
00364   foreach (QString arg, args) {
00365     if (arg.contains("\"") || arg.contains("\\") || arg.contains(" "))
00366       out << string_escape(arg);
00367     else 
00368       out << arg;
00369   }
00370   return out.join(" ");
00371 }
00372 
00373 /** Returns true if <b>str</b> is a valid hexademical string. Returns false
00374  * otherwise. */
00375 bool
00376 string_is_hex(const QString &str)
00377 {
00378   for (int i = 0; i < str.length(); i++) {
00379     char c = str[i].toUpper().toAscii();
00380     if ((c < 'A' || c > 'F') && (c < '0' || c > '9'))
00381       return false;
00382   }
00383   return true;
00384 }
00385 
00386 /** Returns a human-readable description of the time elapsed given by
00387  * <b>seconds</b>, broken down into days, hours, minutes and seconds. */
00388 QString
00389 string_format_uptime(quint64 seconds)
00390 {
00391   QString uptime;
00392   int secs  = (seconds % 60);
00393   int mins  = (seconds / 60 % 60);
00394   int hours = (seconds / 3600 % 24);
00395   int days  = (seconds / 86400);
00396 
00397   if (days)
00398     uptime += qApp->translate("stringutil.h", "%1 days ").arg(days);
00399   if (hours)
00400     uptime += qApp->translate("stringutil.h", "%1 hours ").arg(hours);
00401   if (mins)
00402     uptime += qApp->translate("stringutil.h", "%1 mins ").arg(mins);
00403   if (secs)
00404     uptime += qApp->translate("stringutil.h", "%1 secs").arg(secs);
00405 
00406   return uptime;
00407 }
00408 
00409 /** Returns a string representation of <b>date</b> formatted according to
00410  * "yyyy-MM-dd HH:mm:ss". */
00411 QString
00412 string_format_datetime(const QDateTime &date)
00413 {
00414   return date.toString("yyyy-MM-dd HH:mm:ss");
00415 }
00416 
00417 /** Returns a string representation of <b>bytes</b> with the appropriate
00418  * suffix of either "B/s", "KB/s", "MB/s" or "GB/s". */
00419 QString
00420 string_format_bandwidth(quint64 bytes)
00421 {
00422   if (bytes < 1024)
00423     return qApp->translate("stringutil.h", "%1 B/s").arg(bytes);
00424   if (bytes < 1048576)
00425     return qApp->translate("stringutil.h", "%1 KB/s").arg(bytes/1024.0, 0, 'f', 2);
00426   if (bytes < 1073741824)
00427     return qApp->translate("stringutil.h", "%1 MB/s").arg(bytes/1048576.0, 0, 'f', 2);
00428 
00429   return qApp->translate("stringutil.h", "%1 GB/s").arg(bytes/1073741824.0, 0, 'f', 2);
00430 }
00431