adding new GNUNET_HELPER_ API for communication with (SUID) helper binaries via stdin...
[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
30 #include <iconv.h>
31 #endif
32 #include "gnunet_common.h"
33 #include "gnunet_strings_lib.h"
34
35 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
36
37 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
38
39
40 /**
41  * Fill a buffer of the given size with
42  * count 0-terminated strings (given as varargs).
43  * If "buffer" is NULL, only compute the amount of
44  * space required (sum of "strlen(arg)+1").
45  *
46  * Unlike using "snprintf" with "%s", this function
47  * will add 0-terminators after each string.  The
48  * "GNUNET_string_buffer_tokenize" function can be
49  * used to parse the buffer back into individual
50  * strings.
51  *
52  * @param buffer the buffer to fill with strings, can
53  *               be NULL in which case only the necessary
54  *               amount of space will be calculated
55  * @param size number of bytes available in buffer
56  * @param count number of strings that follow
57  * @param ... count 0-terminated strings to copy to buffer
58  * @return number of bytes written to the buffer
59  *         (or number of bytes that would have been written)
60  */
61 size_t
62 GNUNET_STRINGS_buffer_fill (char *buffer, size_t size, unsigned int count, ...)
63 {
64   size_t needed;
65   size_t slen;
66   const char *s;
67   va_list ap;
68
69   needed = 0;
70   va_start (ap, count);
71   while (count > 0)
72   {
73     s = va_arg (ap, const char *);
74
75     slen = strlen (s) + 1;
76     if (buffer != NULL)
77     {
78       GNUNET_assert (needed + slen <= size);
79       memcpy (&buffer[needed], s, slen);
80     }
81     needed += slen;
82     count--;
83   }
84   va_end (ap);
85   return needed;
86 }
87
88
89 /**
90  * Given a buffer of a given size, find "count"
91  * 0-terminated strings in the buffer and assign
92  * the count (varargs) of type "const char**" to the
93  * locations of the respective strings in the
94  * buffer.
95  *
96  * @param buffer the buffer to parse
97  * @param size size of the buffer
98  * @param count number of strings to locate
99  * @return offset of the character after the last 0-termination
100  *         in the buffer, or 0 on error.
101  */
102 unsigned int
103 GNUNET_STRINGS_buffer_tokenize (const char *buffer, size_t size,
104                                 unsigned int count, ...)
105 {
106   unsigned int start;
107   unsigned int needed;
108   const char **r;
109   va_list ap;
110
111   needed = 0;
112   va_start (ap, count);
113   while (count > 0)
114   {
115     r = va_arg (ap, const char **);
116
117     start = needed;
118     while ((needed < size) && (buffer[needed] != '\0'))
119       needed++;
120     if (needed == size)
121     {
122       va_end (ap);
123       return 0;                 /* error */
124     }
125     *r = &buffer[start];
126     needed++;                   /* skip 0-termination */
127     count--;
128   }
129   va_end (ap);
130   return needed;
131 }
132
133
134 /**
135  * Convert a given filesize into a fancy human-readable format.
136  *
137  * @param size number of bytes
138  * @return fancy representation of the size (possibly rounded) for humans
139  */
140 char *
141 GNUNET_STRINGS_byte_size_fancy (unsigned long long size)
142 {
143   const char *unit = _( /* size unit */ "b");
144   char *ret;
145
146   if (size > 5 * 1024)
147   {
148     size = size / 1024;
149     unit = "KiB";
150     if (size > 5 * 1024)
151     {
152       size = size / 1024;
153       unit = "MiB";
154       if (size > 5 * 1024)
155       {
156         size = size / 1024;
157         unit = "GiB";
158         if (size > 5 * 1024)
159         {
160           size = size / 1024;
161           unit = "TiB";
162         }
163       }
164     }
165   }
166   ret = GNUNET_malloc (32);
167   GNUNET_snprintf (ret, 32, "%llu %s", size, unit);
168   return ret;
169 }
170
171
172 /**
173  * Convert a given fancy human-readable size to bytes.
174  *
175  * @param fancy_size human readable string (i.e. 1 MB)
176  * @param size set to the size in bytes
177  * @return GNUNET_OK on success, GNUNET_SYSERR on error
178  */
179 int
180 GNUNET_STRINGS_fancy_size_to_bytes (const char *fancy_size,
181                                     unsigned long long *size)
182 {
183   struct
184   {
185     const char *name;
186     unsigned long long value;
187   } table[] =
188   {
189     {
190     "B", 1},
191     {
192     "KiB", 1024},
193     {
194     "kB", 1000},
195     {
196     "MiB", 1024 * 1024},
197     {
198     "MB", 1000 * 1000},
199     {
200     "GiB", 1024 * 1024 * 1024},
201     {
202     "GB", 1000 * 1000 * 1000},
203     {
204     "TiB", 1024LL * 1024LL * 1024LL * 1024LL},
205     {
206     "TB", 1000LL * 1000LL * 1000LL * 1024LL},
207     {
208     "PiB", 1024LL * 1024LL * 1024LL * 1024LL * 1024LL},
209     {
210     "PB", 1000LL * 1000LL * 1000LL * 1024LL * 1000LL},
211     {
212     "EiB", 1024LL * 1024LL * 1024LL * 1024LL * 1024LL * 1024LL},
213     {
214     "EB", 1000LL * 1000LL * 1000LL * 1024LL * 1000LL * 1000LL},
215     {
216     NULL, 0}
217   };
218   unsigned long long ret;
219   char *in;
220   const char *tok;
221   unsigned long long last;
222   unsigned int i;
223
224   ret = 0;
225   last = 0;
226   in = GNUNET_strdup (fancy_size);
227   for (tok = strtok (in, " "); tok != NULL; tok = strtok (NULL, " "))
228   {
229     i = 0;
230     while ((table[i].name != NULL) && (0 != strcasecmp (table[i].name, tok)))
231       i++;
232     if (table[i].name != NULL)
233       last *= table[i].value;
234     else
235     {
236       ret += last;
237       last = 0;
238       if (1 != sscanf (tok, "%llu", &last))
239       {
240         GNUNET_free (in);
241         return GNUNET_SYSERR;   /* expected number */
242       }
243     }
244   }
245   ret += last;
246   *size = ret;
247   GNUNET_free (in);
248   return GNUNET_OK;
249 }
250
251
252 /**
253  * Convert a given fancy human-readable time to our internal
254  * representation.
255  *
256  * @param fancy_size human readable string (i.e. 1 minute)
257  * @param rtime set to the relative time
258  * @return GNUNET_OK on success, GNUNET_SYSERR on error
259  */
260 int
261 GNUNET_STRINGS_fancy_time_to_relative (const char *fancy_size,
262                                        struct GNUNET_TIME_Relative *rtime)
263 {
264   struct
265   {
266     const char *name;
267     unsigned long long value;
268   } table[] =
269   {
270     {
271     "ms", 1},
272     {
273     "s", 1000},
274     {
275     "\"", 1000},
276     {
277     "min", 60 * 1000},
278     {
279     "minutes", 60 * 1000},
280     {
281     "'", 60 * 1000},
282     {
283     "h", 60 * 60 * 1000},
284     {
285     "d", 24 * 60 * 60 * 1000},
286     {
287     "a", 31557600 /* year */ },
288     {
289     NULL, 0}
290   };
291   unsigned long long ret;
292   char *in;
293   const char *tok;
294   unsigned long long last;
295   unsigned int i;
296
297   if ((0 == strcasecmp (fancy_size, "infinity")) ||
298       (0 == strcasecmp (fancy_size, "forever")))
299   {
300     *rtime = GNUNET_TIME_UNIT_FOREVER_REL;
301     return GNUNET_OK;
302   }
303   ret = 0;
304   last = 0;
305   in = GNUNET_strdup (fancy_size);
306   for (tok = strtok (in, " "); tok != NULL; tok = strtok (NULL, " "))
307   {
308     i = 0;
309     while ((table[i].name != NULL) && (0 != strcasecmp (table[i].name, tok)))
310       i++;
311     if (table[i].name != NULL)
312       last *= table[i].value;
313     else
314     {
315       ret += last;
316       last = 0;
317       if (1 != sscanf (tok, "%llu", &last))
318       {
319         GNUNET_free (in);
320         return GNUNET_SYSERR;   /* expected number */
321       }
322     }
323   }
324   ret += last;
325   rtime->rel_value = (uint64_t) ret;
326   GNUNET_free (in);
327   return GNUNET_OK;
328 }
329
330 /**
331  * Convert the len characters long character sequence
332  * given in input that is in the given input charset
333  * to a string in given output charset.
334  * @return the converted string (0-terminated),
335  *  if conversion fails, a copy of the orignal
336  *  string is returned.
337  */
338 char *
339 GNUNET_STRINGS_conv (const char *input, size_t len, const char *input_charset, const char *output_charset)
340 {
341   char *ret;
342
343 #if ENABLE_NLS && HAVE_ICONV
344   size_t tmpSize;
345   size_t finSize;
346   char *tmp;
347   char *itmp;
348   iconv_t cd;
349
350   cd = iconv_open (output_charset, input_charset);
351   if (cd == (iconv_t) - 1)
352   {
353     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "iconv_open");
354     LOG (GNUNET_ERROR_TYPE_WARNING, _("Character sets requested were `%s'->`%s'\n"),
355          input_charset, output_charset);
356     ret = GNUNET_malloc (len + 1);
357     memcpy (ret, input, len);
358     ret[len] = '\0';
359     return ret;
360   }
361   tmpSize = 3 * len + 4;
362   tmp = GNUNET_malloc (tmpSize);
363   itmp = tmp;
364   finSize = tmpSize;
365   if (iconv (cd,
366 #if FREEBSD || DARWIN || WINDOWS
367              (const char **) &input,
368 #else
369              (char **) &input,
370 #endif
371              &len, &itmp, &finSize) == SIZE_MAX)
372   {
373     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "iconv");
374     iconv_close (cd);
375     GNUNET_free (tmp);
376     ret = GNUNET_malloc (len + 1);
377     memcpy (ret, input, len);
378     ret[len] = '\0';
379     return ret;
380   }
381   ret = GNUNET_malloc (tmpSize - finSize + 1);
382   memcpy (ret, tmp, tmpSize - finSize);
383   ret[tmpSize - finSize] = '\0';
384   GNUNET_free (tmp);
385   if (0 != iconv_close (cd))
386     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "iconv_close");
387   return ret;
388 #else
389   ret = GNUNET_malloc (len + 1);
390   memcpy (ret, input, len);
391   ret[len] = '\0';
392   return ret;
393 #endif
394 }
395
396
397 /**
398  * Convert the len characters long character sequence
399  * given in input that is in the given charset
400  * to UTF-8.
401  * @return the converted string (0-terminated),
402  *  if conversion fails, a copy of the orignal
403  *  string is returned.
404  */
405 char *
406 GNUNET_STRINGS_to_utf8 (const char *input, size_t len, const char *charset)
407 {
408   return GNUNET_STRINGS_conv (input, len, charset, "UTF-8");
409 }
410
411 /**
412  * Convert the len bytes-long UTF-8 string
413  * given in input to the given charset.
414
415  * @return the converted string (0-terminated),
416  *  if conversion fails, a copy of the orignal
417  *  string is returned.
418  */
419 char *
420 GNUNET_STRINGS_from_utf8 (const char *input, size_t len, const char *charset)
421 {
422   return GNUNET_STRINGS_conv (input, len, "UTF-8", charset);
423 }
424
425
426
427 /**
428  * Complete filename (a la shell) from abbrevition.
429  * @param fil the name of the file, may contain ~/ or
430  *        be relative to the current directory
431  * @returns the full file name,
432  *          NULL is returned on error
433  */
434 char *
435 GNUNET_STRINGS_filename_expand (const char *fil)
436 {
437   char *buffer;
438
439 #ifndef MINGW
440   size_t len;
441   size_t n;
442   char *fm;
443   const char *fil_ptr;
444 #else
445   char *fn;
446   long lRet;
447 #endif
448
449   if (fil == NULL)
450     return NULL;
451
452 #ifndef MINGW
453   if (fil[0] == DIR_SEPARATOR)
454     /* absolute path, just copy */
455     return GNUNET_strdup (fil);
456   if (fil[0] == '~')
457   {
458     fm = getenv ("HOME");
459     if (fm == NULL)
460     {
461       LOG (GNUNET_ERROR_TYPE_WARNING,
462            _("Failed to expand `$HOME': environment variable `HOME' not set"));
463       return NULL;
464     }
465     fm = GNUNET_strdup (fm);
466     /* do not copy '~' */
467     fil_ptr = fil + 1;
468
469     /* skip over dir seperator to be consistent */
470     if (fil_ptr[0] == DIR_SEPARATOR)
471       fil_ptr++;
472   }
473   else
474   {
475     /* relative path */
476     fil_ptr = fil;
477     len = 512;
478     fm = NULL;
479     while (1)
480     {
481       buffer = GNUNET_malloc (len);
482       if (getcwd (buffer, len) != NULL)
483       {
484         fm = buffer;
485         break;
486       }
487       if ((errno == ERANGE) && (len < 1024 * 1024 * 4))
488       {
489         len *= 2;
490         GNUNET_free (buffer);
491         continue;
492       }
493       GNUNET_free (buffer);
494       break;
495     }
496     if (fm == NULL)
497     {
498       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "getcwd");
499       buffer = getenv ("PWD");  /* alternative */
500       if (buffer != NULL)
501         fm = GNUNET_strdup (buffer);
502     }
503     if (fm == NULL)
504       fm = GNUNET_strdup ("./");        /* give up */
505   }
506   n = strlen (fm) + 1 + strlen (fil_ptr) + 1;
507   buffer = GNUNET_malloc (n);
508   GNUNET_snprintf (buffer, n, "%s%s%s", fm,
509                    (fm[strlen (fm) - 1] ==
510                     DIR_SEPARATOR) ? "" : DIR_SEPARATOR_STR, fil_ptr);
511   GNUNET_free (fm);
512   return buffer;
513 #else
514   fn = GNUNET_malloc (MAX_PATH + 1);
515
516   if ((lRet = plibc_conv_to_win_path (fil, fn)) != ERROR_SUCCESS)
517   {
518     SetErrnoFromWinError (lRet);
519     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "plibc_conv_to_win_path");
520     return NULL;
521   }
522   /* is the path relative? */
523   if ((strncmp (fn + 1, ":\\", 2) != 0) && (strncmp (fn, "\\\\", 2) != 0))
524   {
525     char szCurDir[MAX_PATH + 1];
526
527     lRet = GetCurrentDirectory (MAX_PATH + 1, szCurDir);
528     if (lRet + strlen (fn) + 1 > (MAX_PATH + 1))
529     {
530       SetErrnoFromWinError (ERROR_BUFFER_OVERFLOW);
531       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetCurrentDirectory");
532       return NULL;
533     }
534     buffer = GNUNET_malloc (MAX_PATH + 1);
535     GNUNET_snprintf (buffer, MAX_PATH + 1, "%s\\%s", szCurDir, fn);
536     GNUNET_free (fn);
537     fn = buffer;
538   }
539
540   return fn;
541 #endif
542 }
543
544
545 /**
546  * Give relative time in human-readable fancy format.
547  *
548  * @param delta time in milli seconds
549  * @return time as human-readable string
550  */
551 char *
552 GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative delta)
553 {
554   const char *unit = _( /* time unit */ "ms");
555   char *ret;
556   uint64_t dval = delta.rel_value;
557
558   if (delta.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
559     return GNUNET_strdup (_("eternity"));
560   if (dval > 5 * 1000)
561   {
562     dval = dval / 1000;
563     unit = _( /* time unit */ "s");
564     if (dval > 5 * 60)
565     {
566       dval = dval / 60;
567       unit = _( /* time unit */ "m");
568       if (dval > 5 * 60)
569       {
570         dval = dval / 60;
571         unit = _( /* time unit */ "h");
572         if (dval > 5 * 24)
573         {
574           dval = dval / 24;
575           unit = _( /* time unit */ " days");
576         }
577       }
578     }
579   }
580   GNUNET_asprintf (&ret, "%llu %s", dval, unit);
581   return ret;
582 }
583
584
585 /**
586  * "man ctime_r", except for GNUnet time; also, unlike ctime, the
587  * return value does not include the newline character.
588  *
589  * @param t time to convert
590  * @return absolute time in human-readable format
591  */
592 char *
593 GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t)
594 {
595   time_t tt;
596   char *ret;
597
598   if (t.abs_value == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value)
599     return GNUNET_strdup (_("end of time"));
600   tt = t.abs_value / 1000;
601 #ifdef ctime_r
602   ret = ctime_r (&tt, GNUNET_malloc (32));
603 #else
604   ret = GNUNET_strdup (ctime (&tt));
605 #endif
606   ret[strlen (ret) - 1] = '\0';
607   return ret;
608 }
609
610
611
612 /* end of strings.c */