diff options
Diffstat (limited to 'src/util/strings.c')
-rw-r--r-- | src/util/strings.c | 633 |
1 files changed, 633 insertions, 0 deletions
diff --git a/src/util/strings.c b/src/util/strings.c new file mode 100644 index 0000000..8000a93 --- /dev/null +++ b/src/util/strings.c @@ -0,0 +1,633 @@ +/* + This file is part of GNUnet. + (C) 2005, 2006 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/strings.c + * @brief string functions + * @author Nils Durner + * @author Christian Grothoff + */ + +#include "platform.h" +#if HAVE_ICONV +#include <iconv.h> +#endif +#include "gnunet_common.h" +#include "gnunet_strings_lib.h" + +#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__) + +#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall) + + +/** + * Fill a buffer of the given size with + * count 0-terminated strings (given as varargs). + * If "buffer" is NULL, only compute the amount of + * space required (sum of "strlen(arg)+1"). + * + * Unlike using "snprintf" with "%s", this function + * will add 0-terminators after each string. The + * "GNUNET_string_buffer_tokenize" function can be + * used to parse the buffer back into individual + * strings. + * + * @param buffer the buffer to fill with strings, can + * be NULL in which case only the necessary + * amount of space will be calculated + * @param size number of bytes available in buffer + * @param count number of strings that follow + * @param ... count 0-terminated strings to copy to buffer + * @return number of bytes written to the buffer + * (or number of bytes that would have been written) + */ +size_t +GNUNET_STRINGS_buffer_fill (char *buffer, size_t size, unsigned int count, ...) +{ + size_t needed; + size_t slen; + const char *s; + va_list ap; + + needed = 0; + va_start (ap, count); + while (count > 0) + { + s = va_arg (ap, const char *); + + slen = strlen (s) + 1; + if (buffer != NULL) + { + GNUNET_assert (needed + slen <= size); + memcpy (&buffer[needed], s, slen); + } + needed += slen; + count--; + } + va_end (ap); + return needed; +} + + +/** + * Given a buffer of a given size, find "count" + * 0-terminated strings in the buffer and assign + * the count (varargs) of type "const char**" to the + * locations of the respective strings in the + * buffer. + * + * @param buffer the buffer to parse + * @param size size of the buffer + * @param count number of strings to locate + * @return offset of the character after the last 0-termination + * in the buffer, or 0 on error. + */ +unsigned int +GNUNET_STRINGS_buffer_tokenize (const char *buffer, size_t size, + unsigned int count, ...) +{ + unsigned int start; + unsigned int needed; + const char **r; + va_list ap; + + needed = 0; + va_start (ap, count); + while (count > 0) + { + r = va_arg (ap, const char **); + + start = needed; + while ((needed < size) && (buffer[needed] != '\0')) + needed++; + if (needed == size) + { + va_end (ap); + return 0; /* error */ + } + *r = &buffer[start]; + needed++; /* skip 0-termination */ + count--; + } + va_end (ap); + return needed; +} + + +/** + * Convert a given filesize into a fancy human-readable format. + * + * @param size number of bytes + * @return fancy representation of the size (possibly rounded) for humans + */ +char * +GNUNET_STRINGS_byte_size_fancy (unsigned long long size) +{ + const char *unit = _( /* size unit */ "b"); + char *ret; + + if (size > 5 * 1024) + { + size = size / 1024; + unit = "KiB"; + if (size > 5 * 1024) + { + size = size / 1024; + unit = "MiB"; + if (size > 5 * 1024) + { + size = size / 1024; + unit = "GiB"; + if (size > 5 * 1024) + { + size = size / 1024; + unit = "TiB"; + } + } + } + } + ret = GNUNET_malloc (32); + GNUNET_snprintf (ret, 32, "%llu %s", size, unit); + return ret; +} + + +/** + * Convert a given fancy human-readable size to bytes. + * + * @param fancy_size human readable string (i.e. 1 MB) + * @param size set to the size in bytes + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int +GNUNET_STRINGS_fancy_size_to_bytes (const char *fancy_size, + unsigned long long *size) +{ + struct + { + const char *name; + unsigned long long value; + } table[] = + { + { + "B", 1}, + { + "KiB", 1024}, + { + "kB", 1000}, + { + "MiB", 1024 * 1024}, + { + "MB", 1000 * 1000}, + { + "GiB", 1024 * 1024 * 1024}, + { + "GB", 1000 * 1000 * 1000}, + { + "TiB", 1024LL * 1024LL * 1024LL * 1024LL}, + { + "TB", 1000LL * 1000LL * 1000LL * 1024LL}, + { + "PiB", 1024LL * 1024LL * 1024LL * 1024LL * 1024LL}, + { + "PB", 1000LL * 1000LL * 1000LL * 1024LL * 1000LL}, + { + "EiB", 1024LL * 1024LL * 1024LL * 1024LL * 1024LL * 1024LL}, + { + "EB", 1000LL * 1000LL * 1000LL * 1024LL * 1000LL * 1000LL}, + { + NULL, 0} + }; + unsigned long long ret; + char *in; + const char *tok; + unsigned long long last; + unsigned int i; + + ret = 0; + last = 0; + in = GNUNET_strdup (fancy_size); + for (tok = strtok (in, " "); tok != NULL; tok = strtok (NULL, " ")) + { + i = 0; + while ((table[i].name != NULL) && (0 != strcasecmp (table[i].name, tok))) + i++; + if (table[i].name != NULL) + last *= table[i].value; + else + { + ret += last; + last = 0; + if (1 != sscanf (tok, "%llu", &last)) + { + GNUNET_free (in); + return GNUNET_SYSERR; /* expected number */ + } + } + } + ret += last; + *size = ret; + GNUNET_free (in); + return GNUNET_OK; +} + + +/** + * Convert a given fancy human-readable time to our internal + * representation. + * + * @param fancy_size human readable string (i.e. 1 minute) + * @param rtime set to the relative time + * @return GNUNET_OK on success, GNUNET_SYSERR on error + */ +int +GNUNET_STRINGS_fancy_time_to_relative (const char *fancy_size, + struct GNUNET_TIME_Relative *rtime) +{ + struct + { + const char *name; + unsigned long long value; + } table[] = + { + { + "ms", 1}, + { + "s", 1000}, + { + "\"", 1000}, + { + "min", 60 * 1000}, + { + "minutes", 60 * 1000}, + { + "'", 60 * 1000}, + { + "h", 60 * 60 * 1000}, + { + "d", 24 * 60 * 60 * 1000}, + { + "a", 31557600 /* year */ }, + { + NULL, 0} + }; + unsigned long long ret; + char *in; + const char *tok; + unsigned long long last; + unsigned int i; + + if ((0 == strcasecmp (fancy_size, "infinity")) || + (0 == strcasecmp (fancy_size, "forever"))) + { + *rtime = GNUNET_TIME_UNIT_FOREVER_REL; + return GNUNET_OK; + } + ret = 0; + last = 0; + in = GNUNET_strdup (fancy_size); + for (tok = strtok (in, " "); tok != NULL; tok = strtok (NULL, " ")) + { + i = 0; + while ((table[i].name != NULL) && (0 != strcasecmp (table[i].name, tok))) + i++; + if (table[i].name != NULL) + last *= table[i].value; + else + { + ret += last; + last = 0; + if (1 != sscanf (tok, "%llu", &last)) + { + GNUNET_free (in); + return GNUNET_SYSERR; /* expected number */ + } + } + } + ret += last; + rtime->rel_value = (uint64_t) ret; + GNUNET_free (in); + return GNUNET_OK; +} + +/** + * Convert the len characters long character sequence + * given in input that is in the given input charset + * to a string in given output charset. + * @return the converted string (0-terminated), + * if conversion fails, a copy of the orignal + * string is returned. + */ +char * +GNUNET_STRINGS_conv (const char *input, size_t len, const char *input_charset, const char *output_charset) +{ + char *ret; + +#if ENABLE_NLS && HAVE_ICONV + size_t tmpSize; + size_t finSize; + char *tmp; + char *itmp; + iconv_t cd; + + cd = iconv_open (output_charset, input_charset); + if (cd == (iconv_t) - 1) + { + LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "iconv_open"); + LOG (GNUNET_ERROR_TYPE_WARNING, _("Character sets requested were `%s'->`%s'\n"), + input_charset, output_charset); + ret = GNUNET_malloc (len + 1); + memcpy (ret, input, len); + ret[len] = '\0'; + return ret; + } + tmpSize = 3 * len + 4; + tmp = GNUNET_malloc (tmpSize); + itmp = tmp; + finSize = tmpSize; + if (iconv (cd, +#if FREEBSD || DARWIN || WINDOWS + (const char **) &input, +#else + (char **) &input, +#endif + &len, &itmp, &finSize) == SIZE_MAX) + { + LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "iconv"); + iconv_close (cd); + GNUNET_free (tmp); + ret = GNUNET_malloc (len + 1); + memcpy (ret, input, len); + ret[len] = '\0'; + return ret; + } + ret = GNUNET_malloc (tmpSize - finSize + 1); + memcpy (ret, tmp, tmpSize - finSize); + ret[tmpSize - finSize] = '\0'; + GNUNET_free (tmp); + if (0 != iconv_close (cd)) + LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "iconv_close"); + return ret; +#else + ret = GNUNET_malloc (len + 1); + memcpy (ret, input, len); + ret[len] = '\0'; + return ret; +#endif +} + + +/** + * Convert the len characters long character sequence + * given in input that is in the given charset + * to UTF-8. + * @return the converted string (0-terminated), + * if conversion fails, a copy of the orignal + * string is returned. + */ +char * +GNUNET_STRINGS_to_utf8 (const char *input, size_t len, const char *charset) +{ + return GNUNET_STRINGS_conv (input, len, charset, "UTF-8"); +} + +/** + * Convert the len bytes-long UTF-8 string + * given in input to the given charset. + + * @return the converted string (0-terminated), + * if conversion fails, a copy of the orignal + * string is returned. + */ +char * +GNUNET_STRINGS_from_utf8 (const char *input, size_t len, const char *charset) +{ + return GNUNET_STRINGS_conv (input, len, "UTF-8", charset); +} + + + +/** + * Complete filename (a la shell) from abbrevition. + * @param fil the name of the file, may contain ~/ or + * be relative to the current directory + * @returns the full file name, + * NULL is returned on error + */ +char * +GNUNET_STRINGS_filename_expand (const char *fil) +{ + char *buffer; + +#ifndef MINGW + size_t len; + size_t n; + char *fm; + const char *fil_ptr; +#else + char *fn; + long lRet; +#endif + + if (fil == NULL) + return NULL; + +#ifndef MINGW + if (fil[0] == DIR_SEPARATOR) + /* absolute path, just copy */ + return GNUNET_strdup (fil); + if (fil[0] == '~') + { + fm = getenv ("HOME"); + if (fm == NULL) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("Failed to expand `$HOME': environment variable `HOME' not set")); + return NULL; + } + fm = GNUNET_strdup (fm); + /* do not copy '~' */ + fil_ptr = fil + 1; + + /* skip over dir seperator to be consistent */ + if (fil_ptr[0] == DIR_SEPARATOR) + fil_ptr++; + } + else + { + /* relative path */ + fil_ptr = fil; + len = 512; + fm = NULL; + while (1) + { + buffer = GNUNET_malloc (len); + if (getcwd (buffer, len) != NULL) + { + fm = buffer; + break; + } + if ((errno == ERANGE) && (len < 1024 * 1024 * 4)) + { + len *= 2; + GNUNET_free (buffer); + continue; + } + GNUNET_free (buffer); + break; + } + if (fm == NULL) + { + LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "getcwd"); + buffer = getenv ("PWD"); /* alternative */ + if (buffer != NULL) + fm = GNUNET_strdup (buffer); + } + if (fm == NULL) + fm = GNUNET_strdup ("./"); /* give up */ + } + n = strlen (fm) + 1 + strlen (fil_ptr) + 1; + buffer = GNUNET_malloc (n); + GNUNET_snprintf (buffer, n, "%s%s%s", fm, + (fm[strlen (fm) - 1] == + DIR_SEPARATOR) ? "" : DIR_SEPARATOR_STR, fil_ptr); + GNUNET_free (fm); + return buffer; +#else + fn = GNUNET_malloc (MAX_PATH + 1); + + if ((lRet = plibc_conv_to_win_path (fil, fn)) != ERROR_SUCCESS) + { + SetErrnoFromWinError (lRet); + LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "plibc_conv_to_win_path"); + return NULL; + } + /* is the path relative? */ + if ((strncmp (fn + 1, ":\\", 2) != 0) && (strncmp (fn, "\\\\", 2) != 0)) + { + char szCurDir[MAX_PATH + 1]; + + lRet = GetCurrentDirectory (MAX_PATH + 1, szCurDir); + if (lRet + strlen (fn) + 1 > (MAX_PATH + 1)) + { + SetErrnoFromWinError (ERROR_BUFFER_OVERFLOW); + LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetCurrentDirectory"); + return NULL; + } + buffer = GNUNET_malloc (MAX_PATH + 1); + GNUNET_snprintf (buffer, MAX_PATH + 1, "%s\\%s", szCurDir, fn); + GNUNET_free (fn); + fn = buffer; + } + + return fn; +#endif +} + + +/** + * Give relative time in human-readable fancy format. + * + * @param delta time in milli seconds + * @return time as human-readable string + */ +char * +GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative delta) +{ + const char *unit = _( /* time unit */ "ms"); + char *ret; + uint64_t dval = delta.rel_value; + + if (delta.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value) + return GNUNET_strdup (_("eternity")); + if (dval > 5 * 1000) + { + dval = dval / 1000; + unit = _( /* time unit */ "s"); + if (dval > 5 * 60) + { + dval = dval / 60; + unit = _( /* time unit */ "m"); + if (dval > 5 * 60) + { + dval = dval / 60; + unit = _( /* time unit */ "h"); + if (dval > 5 * 24) + { + dval = dval / 24; + unit = _( /* time unit */ " days"); + } + } + } + } + GNUNET_asprintf (&ret, "%llu %s", dval, unit); + return ret; +} + + +/** + * "man ctime_r", except for GNUnet time; also, unlike ctime, the + * return value does not include the newline character. + * + * @param t time to convert + * @return absolute time in human-readable format + */ +char * +GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t) +{ + time_t tt; + char *ret; + + if (t.abs_value == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value) + return GNUNET_strdup (_("end of time")); + tt = t.abs_value / 1000; +#ifdef ctime_r + ret = ctime_r (&tt, GNUNET_malloc (32)); +#else + ret = GNUNET_strdup (ctime (&tt)); +#endif + ret[strlen (ret) - 1] = '\0'; + return ret; +} + + +/** + * "man basename" + * Returns a pointer to a part of filename (allocates nothing)! + * + * @param filename filename to extract basename from + * @return short (base) name of the file (that is, everything following the + * last directory separator in filename. If filename ends with a + * directory separator, the result will be a zero-length string. + * If filename has no directory separators, the result is filename + * itself. + */ +const char * +GNUNET_STRINGS_get_short_name (const char *filename) +{ + const char *short_fn = filename; + const char *ss; + while (NULL != (ss = strstr (short_fn, DIR_SEPARATOR_STR)) + && (ss[1] != '\0')) + short_fn = 1 + ss; + return short_fn; +} + +/* end of strings.c */ |