d53bcd4826d43f43c31c46e3a37af33737993509
[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  * @param buffer the buffer to fill with strings, can
49  *               be NULL in which case only the necessary
50  *               amount of space will be calculated
51  * @param size number of bytes available in buffer
52  * @param count number of strings that follow
53  * @param ... count 0-terminated strings to copy to buffer
54  * @return number of bytes written to the buffer
55  *         (or number of bytes that would have been written)
56  */
57 size_t
58 GNUNET_STRINGS_buffer_fill (char *buffer,
59                             size_t size, unsigned int count, ...)
60 {
61   size_t needed;
62   size_t slen;
63   const char *s;
64   va_list ap;
65
66   needed = 0;
67   va_start (ap, count);
68   while (count > 0)
69     {
70       s = va_arg (ap, const char *);
71       slen = strlen (s) + 1;
72       if (buffer != NULL)
73         {
74           GNUNET_assert (needed + slen <= size);
75           memcpy (&buffer[needed], s, slen);
76         }
77       needed += slen;
78       count--;
79     }
80   va_end (ap);
81   return needed;
82 }
83
84
85 /**
86  * Given a buffer of a given size, find "count"
87  * 0-terminated strings in the buffer and assign
88  * the count (varargs) of type "const char**" to the
89  * locations of the respective strings in the
90  * buffer.
91  *
92  * @param buffer the buffer to parse
93  * @param size size of the buffer
94  * @param count number of strings to locate
95  * @return offset of the character after the last 0-termination
96  *         in the buffer, or 0 on error.
97  */
98 unsigned int
99 GNUNET_STRINGS_buffer_tokenize (const char *buffer,
100                                 size_t size, unsigned int count, ...)
101 {
102   unsigned int start;
103   unsigned int needed;
104   const char **r;
105   va_list ap;
106
107   needed = 0;
108   va_start (ap, count);
109   while (count > 0)
110     {
111       r = va_arg (ap, const char **);
112       start = needed;
113       while ((needed < size) && (buffer[needed] != '\0'))
114         needed++;
115       if (needed == size)
116         {
117           va_end (ap);
118           return 0;             /* error */
119         }
120       *r = &buffer[start];
121       needed++;                 /* skip 0-termination */
122       count--;
123     }
124   va_end (ap);
125   return needed;
126 }
127
128
129 /**
130  * Convert a given filesize into a fancy human-readable format.
131  *
132  * @param size number of bytes
133  * @return fancy representation of the size (possibly rounded) for humans
134  */
135 char *
136 GNUNET_STRINGS_byte_size_fancy (unsigned long long size)
137 {
138   const char *unit = _( /* size unit */ "b");
139   char *ret;
140
141   if (size > 5 * 1024)
142     {
143       size = size / 1024;
144       unit = _( /* size unit */ "KiB");
145       if (size > 5 * 1024)
146         {
147           size = size / 1024;
148           unit = _( /* size unit */ "MiB");
149           if (size > 5 * 1024)
150             {
151               size = size / 1024;
152               unit = _( /* size unit */ "GiB");
153               if (size > 5 * 1024)
154                 {
155                   size = size / 1024;
156                   unit = _( /* size unit */ "TiB");
157                 }
158             }
159         }
160     }
161   ret = GNUNET_malloc (32);
162   GNUNET_snprintf (ret, 32, "%llu%s", size, unit);
163   return ret;
164 }
165
166
167 /**
168  * Convert the len characters long character sequence
169  * given in input that is in the given charset
170  * to UTF-8.
171  * @return the converted string (0-terminated),
172  *  if conversion fails, a copy of the orignal
173  *  string is returned.
174  */
175 char *
176 GNUNET_STRINGS_to_utf8 (const char *input, size_t len, const char *charset)
177 {
178   char *ret;
179 #if ENABLE_NLS && HAVE_ICONV
180   size_t tmpSize;
181   size_t finSize;
182   char *tmp;
183   char *itmp;
184   iconv_t cd;
185
186   cd = iconv_open ("UTF-8", charset);
187   if (cd == (iconv_t) - 1)
188     {
189       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv_open");
190       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
191                   _("Character set requested was `%s'\n"), charset);
192       ret = GNUNET_malloc (len + 1);
193       memcpy (ret, input, len);
194       ret[len] = '\0';
195       return ret;
196     }
197   tmpSize = 3 * len + 4;
198   tmp = GNUNET_malloc (tmpSize);
199   itmp = tmp;
200   finSize = tmpSize;
201   if (iconv (cd,
202 #if FREEBSD
203              (const char **) &input,
204 #else
205              (char **) &input,
206 #endif
207              &len, &itmp, &finSize) == (size_t) - 1)
208     {
209       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv");
210       iconv_close (cd);
211       GNUNET_free (tmp);
212       ret = GNUNET_malloc (len + 1);
213       memcpy (ret, input, len);
214       ret[len] = '\0';
215       return ret;
216     }
217   ret = GNUNET_malloc (tmpSize - finSize + 1);
218   memcpy (ret, tmp, tmpSize - finSize);
219   ret[tmpSize - finSize] = '\0';
220   GNUNET_free (tmp);
221   if (0 != iconv_close (cd))
222     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "iconv_close");
223   return ret;
224 #else
225   ret = GNUNET_malloc (len + 1);
226   memcpy (ret, input, len);
227   ret[len] = '\0';
228   return ret;
229 #endif
230 }
231
232
233 /**
234  * Complete filename (a la shell) from abbrevition.
235  * @param fil the name of the file, may contain ~/ or
236  *        be relative to the current directory
237  * @returns the full file name,
238  *          NULL is returned on error
239  */
240 char *
241 GNUNET_STRINGS_filename_expand (const char *fil)
242 {
243   char *buffer;
244 #ifndef MINGW
245   size_t len;
246   size_t n;
247   char *fm;
248   const char *fil_ptr;
249 #else
250   char *fn;
251   long lRet;
252 #endif
253
254   if (fil == NULL)
255     return NULL;
256
257 #ifndef MINGW
258   if (fil[0] == DIR_SEPARATOR)
259     /* absolute path, just copy */
260     return GNUNET_strdup (fil);
261   if (fil[0] == '~')
262     {
263       fm = getenv ("HOME");
264       if (fm == NULL)
265         {
266           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
267                       _
268                       ("Failed to expand `$HOME': environment variable `HOME' not set"));
269           return NULL;
270         }
271       fm = GNUNET_strdup (fm);
272       /* do not copy '~' */
273       fil_ptr = fil + 1;
274
275       /* skip over dir seperator to be consistent */
276       if (fil_ptr[0] == DIR_SEPARATOR)
277         fil_ptr++;
278     }
279   else
280     {
281       /* relative path */
282       fil_ptr = fil;
283       len = 512;
284       fm = NULL;
285       while (1)
286         {
287           buffer = GNUNET_malloc (len);
288           if (getcwd (buffer, len) != NULL)
289             {
290               fm = buffer;
291               break;
292             }
293           if ((errno == ERANGE) && (len < 1024 * 1024 * 4))
294             {
295               len *= 2;
296               GNUNET_free (buffer);
297               continue;
298             }
299           GNUNET_free (buffer);
300           break;
301         }
302       if (fm == NULL)
303         {
304           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "getcwd");
305           buffer = getenv ("PWD");      /* alternative */
306           if (buffer != NULL)
307             fm = GNUNET_strdup (buffer);
308         }
309       if (fm == NULL)
310         fm = GNUNET_strdup ("./");      /* give up */
311     }
312   n = strlen (fm) + 1 + strlen (fil_ptr) + 1;
313   buffer = GNUNET_malloc (n);
314   GNUNET_snprintf (buffer, n, "%s%s%s",
315                    fm,
316                    (fm[strlen (fm) - 1] ==
317                     DIR_SEPARATOR) ? "" : DIR_SEPARATOR_STR, fil_ptr);
318   GNUNET_free (fm);
319   return buffer;
320 #else
321   fn = GNUNET_malloc (MAX_PATH + 1);
322
323   if ((lRet = plibc_conv_to_win_path (fil, fn)) != ERROR_SUCCESS)
324     {
325       SetErrnoFromWinError (lRet);
326       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
327                            "plibc_conv_to_win_path");
328       return NULL;
329     }
330   /* is the path relative? */
331   if ((strncmp (fn + 1, ":\\", 2) != 0) && (strncmp (fn, "\\\\", 2) != 0))
332     {
333       char szCurDir[MAX_PATH + 1];
334       lRet = GetCurrentDirectory (MAX_PATH + 1, szCurDir);
335       if (lRet + strlen (fn) + 1 > (MAX_PATH + 1))
336         {
337           SetErrnoFromWinError (ERROR_BUFFER_OVERFLOW);
338           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
339                                "GetCurrentDirectory");
340           return NULL;
341         }
342       buffer = GNUNET_malloc (MAX_PATH + 1);
343       GNUNET_snprintf (buffer, MAX_PATH + 1, "%s\\%s", szCurDir, fn);
344       GNUNET_free (fn);
345       fn = buffer;
346     }
347
348   return fn;
349 #endif
350 }
351
352
353 /**
354  * Give relative time in human-readable fancy format.
355  *
356  * @param delta time in milli seconds
357  * @return time as human-readable string
358  */
359 char *
360 GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative delta)
361 {
362   const char *unit = _( /* time unit */ "ms");
363   char *ret;
364   uint64_t dval = delta.value;
365
366   if (dval > 5 * 1000)
367     {
368       dval = dval / 1000;
369       unit = _( /* time unit */ "s");
370       if (dval > 5 * 60)
371         {
372           dval = dval / 60;
373           unit = _( /* time unit */ "m");
374           if (dval > 5 * 60)
375             {
376               dval = dval / 60;
377               unit = _( /* time unit */ "h");
378               if (dval > 5 * 24)
379                 {
380                   dval = dval / 24;
381                   unit = _( /* time unit */ " days");
382                 }
383             }
384         }
385     }
386   GNUNET_asprintf (&ret, "%llu%s", dval, unit);
387   return ret;
388 }
389
390
391 /**
392  * "man ctime_r", except for GNUnet time; also, unlike ctime, the
393  * return value does not include the newline character.
394  *
395  * @param t time to convert
396  * @return absolute time in human-readable format
397  */
398 char *
399 GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t)
400 {
401   time_t tt;
402   char *ret;
403
404   tt = t.value / 1000;
405 #ifdef ctime_r
406   ret = ctime_r (&tt, GNUNET_malloc (32));
407 #else
408   ret = GNUNET_strdup (ctime (&tt));
409 #endif
410   ret[strlen (ret) - 1] = '\0';
411   return ret;
412 }
413
414
415
416 /* end of strings.c */