/* 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 #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 = _( /* size unit */ "KiB"); if (size > 5 * 1024) { size = size / 1024; unit = _( /* size unit */ "MiB"); if (size > 5 * 1024) { size = size / 1024; unit = _( /* size unit */ "GiB"); if (size > 5 * 1024) { size = size / 1024; unit = _( /* size unit */ "TiB"); } } } } ret = GNUNET_malloc (32); GNUNET_snprintf (ret, 32, "%llu %s", size, unit); return ret; } /** * 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) { char *ret; #if ENABLE_NLS && HAVE_ICONV size_t tmpSize; size_t finSize; char *tmp; char *itmp; iconv_t cd; cd = iconv_open ("UTF-8", charset); if (cd == (iconv_t) - 1) { LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "iconv_open"); LOG (GNUNET_ERROR_TYPE_WARNING, _("Character set requested was `%s'\n"), 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 } /** * 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; } /* end of strings.c */