leakfix
[oweals/gnunet.git] / src / util / strings.c
1 /*
2      This file is part of GNUnet.
3      (C) 2005, 2006 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file util/strings.c
23  * @brief string functions
24  * @author Nils Durner
25  * @author Christian Grothoff
26  */
27
28 #include "platform.h"
29 #if HAVE_ICONV_H
30 #include <iconv.h>
31 #endif
32 #include "gnunet_common.h"
33 #include "gnunet_strings_lib.h"
34
35
36 /**
37  * Fill a buffer of the given size with
38  * count 0-terminated strings (given as varargs).
39  * If "buffer" is NULL, only compute the amount of
40  * space required (sum of "strlen(arg)+1").
41  *
42  * Unlike using "snprintf" with "%s", this function
43  * will add 0-terminators after each string.  The
44  * "GNUNET_string_buffer_tokenize" function can be
45  * used to parse the buffer back into individual
46  * strings.
47  *
48  * @return number of bytes written to the buffer
49  *         (or number of bytes that would have been written)
50  */
51 unsigned int
52 GNUNET_STRINGS_buffer_fill (char *buffer,
53                             unsigned int size, unsigned int count, ...)
54 {
55   unsigned int needed;
56   unsigned int slen;
57   const char *s;
58   va_list ap;
59
60   needed = 0;
61   va_start (ap, count);
62   while (count > 0)
63     {
64       s = va_arg (ap, const char *);
65       slen = strlen (s) + 1;
66       if (buffer != NULL)
67         {
68           GNUNET_assert (needed + slen <= size);
69           memcpy (&buffer[needed], s, slen);
70         }
71       needed += slen;
72       count--;
73     }
74   va_end (ap);
75   return needed;
76 }
77
78
79 /**
80  * Given a buffer of a given size, find "count"
81  * 0-terminated strings in the buffer and assign
82  * the count (varargs) of type "const char**" to the
83  * locations of the respective strings in the
84  * buffer.
85  *
86  * @param buffer the buffer to parse
87  * @param size size of the buffer
88  * @param count number of strings to locate
89  * @return offset of the character after the last 0-termination
90  *         in the buffer, or 0 on error.
91  */
92 unsigned int
93 GNUNET_STRINGS_buffer_tokenize (const char *buffer,
94                                 unsigned int size, unsigned int count, ...)
95 {
96   unsigned int start;
97   unsigned int needed;
98   const char **r;
99   va_list ap;
100
101   needed = 0;
102   va_start (ap, count);
103   while (count > 0)
104     {
105       r = va_arg (ap, const char **);
106       start = needed;
107       while ((needed < size) && (buffer[needed] != '\0'))
108         needed++;
109       if (needed == size)
110         {
111           va_end (ap);
112           return 0;             /* error */
113         }
114       *r = &buffer[start];
115       needed++;                 /* skip 0-termination */
116       count--;
117     }
118   va_end (ap);
119   return needed;
120 }
121
122
123 /**
124  * Convert a given filesize into a fancy human-readable format.
125  */
126 char *
127 GNUNET_STRINGS_byte_size_fancy (unsigned long long size)
128 {
129   const char *unit = _( /* size unit */ "b");
130   char *ret;
131
132   if (size > 5 * 1024)
133     {
134       size = size / 1024;
135       unit = _( /* size unit */ "KiB");
136       if (size > 5 * 1024)
137         {
138           size = size / 1024;
139           unit = _( /* size unit */ "MiB");
140           if (size > 5 * 1024)
141             {
142               size = size / 1024;
143               unit = _( /* size unit */ "GiB");
144               if (size > 5 * 1024)
145                 {
146                   size = size / 1024;
147                   unit = _( /* size unit */ "TiB");
148                 }
149             }
150         }
151     }
152   ret = GNUNET_malloc (32);
153   GNUNET_snprintf (ret, 32, "%llu%s", size, unit);
154   return ret;
155 }
156
157
158 /**
159  * Convert the len characters long character sequence
160  * given in input that is in the given charset
161  * to UTF-8.
162  * @return the converted string (0-terminated),
163  *  if conversion fails, a copy of the orignal
164  *  string is returned.
165  */
166 char *
167 GNUNET_STRINGS_to_utf8 (const char *input, size_t len, const char *charset)
168 {
169   char *ret;
170 #if ENABLE_NLS && HAVE_ICONV
171   size_t tmpSize;
172   size_t finSize;
173   char *tmp;
174   char *itmp;
175   iconv_t cd;
176
177   cd = iconv_open ("UTF-8", charset);
178   if (cd == (iconv_t) - 1)
179     {
180       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv_open");
181       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
182                   _("Character set requested was `%s'\n"), charset);
183       ret = GNUNET_malloc (len + 1);
184       memcpy (ret, input, len);
185       ret[len] = '\0';
186       return ret;
187     }
188   tmpSize = 3 * len + 4;
189   tmp = GNUNET_malloc (tmpSize);
190   itmp = tmp;
191   finSize = tmpSize;
192   if (iconv (cd, (char **) &input, &len, &itmp, &finSize) == (size_t) - 1)
193     {
194       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv");
195       iconv_close (cd);
196       GNUNET_free (tmp);
197       ret = GNUNET_malloc (len + 1);
198       memcpy (ret, input, len);
199       ret[len] = '\0';
200       return ret;
201     }
202   ret = GNUNET_malloc (tmpSize - finSize + 1);
203   memcpy (ret, tmp, tmpSize - finSize);
204   ret[tmpSize - finSize] = '\0';
205   GNUNET_free (tmp);
206   if (0 != iconv_close (cd))
207     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv_close");
208   return ret;
209 #else
210   ret = GNUNET_malloc (len + 1);
211   memcpy (ret, input, len);
212   ret[len] = '\0';
213   return ret;
214 #endif
215 }
216
217
218 /**
219  * Complete filename (a la shell) from abbrevition.
220  * @param fil the name of the file, may contain ~/ or
221  *        be relative to the current directory
222  * @returns the full file name,
223  *          NULL is returned on error
224  */
225 char *
226 GNUNET_STRINGS_filename_expand (const char *fil)
227 {
228   char *buffer;
229 #ifndef MINGW
230   size_t len;
231   size_t n;
232   char *fm;
233   const char *fil_ptr;
234 #else
235   char *fn;
236   long lRet;
237 #endif
238
239   if (fil == NULL)
240     return NULL;
241
242 #ifndef MINGW
243   if (fil[0] == DIR_SEPARATOR)
244     /* absolute path, just copy */
245     return GNUNET_strdup (fil);
246   if (fil[0] == '~')
247     {
248       fm = getenv ("HOME");
249       if (fm == NULL)
250         {
251           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
252                       _
253                       ("Failed to expand `$HOME': environment variable `HOME' not set"));
254           return NULL;
255         }
256       fm = GNUNET_strdup (fm);
257       /* do not copy '~' */
258       fil_ptr = fil + 1;
259
260       /* skip over dir seperator to be consistent */
261       if (fil_ptr[0] == DIR_SEPARATOR)
262         fil_ptr++;
263     }
264   else
265     {
266       /* relative path */
267       fil_ptr = fil;
268       len = 512;
269       fm = NULL;
270       while (1)
271         {
272           buffer = GNUNET_malloc (len);
273           if (getcwd (buffer, len) != NULL)
274             {
275               fm = buffer;
276               break;
277             }
278           if ((errno == ERANGE) && (len < 1024 * 1024 * 4))
279             {
280               len *= 2;
281               GNUNET_free (buffer);
282               continue;
283             }
284           GNUNET_free (buffer);
285           break;
286         }
287       if (fm == NULL)
288         {
289           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "getcwd");
290           buffer = getenv ("PWD");      /* alternative */
291           if (buffer != NULL)
292             fm = GNUNET_strdup (buffer);
293         }
294       if (fm == NULL)
295         fm = GNUNET_strdup ("./");      /* give up */
296     }
297   n = strlen (fm) + 1 + strlen (fil_ptr) + 1;
298   buffer = GNUNET_malloc (n);
299   GNUNET_snprintf (buffer, n, "%s%s%s",
300                    fm,
301                    (fm[strlen (fm) - 1] ==
302                     DIR_SEPARATOR) ? "" : DIR_SEPARATOR_STR, fil_ptr);
303   GNUNET_free (fm);
304   return buffer;
305 #else
306   fn = GNUNET_malloc (MAX_PATH + 1);
307
308   if ((lRet = plibc_conv_to_win_path (fil, fn)) != ERROR_SUCCESS)
309     {
310       SetErrnoFromWinError (lRet);
311       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
312                            "plibc_conv_to_win_path");
313       return NULL;
314     }
315   /* is the path relative? */
316   if ((strncmp (fn + 1, ":\\", 2) != 0) && (strncmp (fn, "\\\\", 2) != 0))
317     {
318       char szCurDir[MAX_PATH + 1];
319       lRet = GetCurrentDirectory (MAX_PATH + 1, szCurDir);
320       if (lRet + strlen (fn) + 1 > (MAX_PATH + 1))
321         {
322           SetErrnoFromWinError (ERROR_BUFFER_OVERFLOW);
323           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
324                                "GetCurrentDirectory");
325           return NULL;
326         }
327       buffer = GNUNET_malloc (MAX_PATH + 1);
328       GNUNET_snprintf (buffer, MAX_PATH + 1, "%s\\%s", szCurDir, fn);
329       GNUNET_free (fn);
330       fn = buffer;
331     }
332
333   return fn;
334 #endif
335 }
336
337
338 /**
339  * Give relative time in human-readable fancy format.
340  * @param delta time in milli seconds
341  */
342 char *
343 GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative del)
344 {
345   const char *unit = _( /* time unit */ "ms");
346   char *ret;
347   uint64_t delta = del.value;
348
349   if (delta > 5 * 1000)
350     {
351       delta = delta / 1000;
352       unit = _( /* time unit */ "s");
353       if (delta > 5 * 60)
354         {
355           delta = delta / 60;
356           unit = _( /* time unit */ "m");
357           if (delta > 5 * 60)
358             {
359               delta = delta / 60;
360               unit = _( /* time unit */ "h");
361               if (delta > 5 * 24)
362                 {
363                   delta = delta / 24;
364                   unit = _( /* time unit */ " days");
365                 }
366             }
367         }
368     }
369   GNUNET_asprintf (&ret, "%llu%s", delta, unit);
370   return ret;
371 }
372
373
374 /**
375  * "man ctime_r", except for GNUnet time; also, unlike ctime, the
376  * return value does not include the newline character.
377  */
378 char *
379 GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t)
380 {
381   time_t tt;
382   char *ret;
383
384   tt = t.value / 1000;
385 #ifdef ctime_r
386   ret = ctime_r (&tt, GNUNET_malloc (32));
387 #else
388   ret = GNUNET_strdup (ctime (&tt));
389 #endif
390   ret[strlen (ret) - 1] = '\0';
391   return ret;
392 }
393
394
395
396 /* end of strings.c */