-eliminating duplicate code
[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  * Unit conversion table entry for 'convert_with_table'.
174  */
175 struct ConversionTable
176 {
177   /**
178    * Name of the unit (or NULL for end of table).
179    */
180   const char *name;
181
182   /**
183    * Factor to apply for this unit.
184    */
185   unsigned long long value;
186 };
187
188
189 /**
190  * Convert a string of the form "4 X 5 Y" into a numeric value
191  * by interpreting "X" and "Y" as units and then multiplying
192  * the numbers with the values associated with the respective
193  * unit from the conversion table.
194  *
195  * @param input input string to parse
196  * @param table table with the conversion of unit names to numbers
197  * @param output where to store the result
198  * @return GNUNET_OK on success, GNUNET_SYSERR on error
199  */
200 static int
201 convert_with_table (const char *input,
202                     const struct ConversionTable *table,
203                     unsigned long long *output)
204 {
205   unsigned long long ret;
206   char *in;
207   const char *tok;
208   unsigned long long last;
209   unsigned int i;
210
211   ret = 0;
212   last = 0;
213   in = GNUNET_strdup (input);
214   for (tok = strtok (in, " "); tok != NULL; tok = strtok (NULL, " "))
215   {
216     i = 0;
217     while ((table[i].name != NULL) && (0 != strcasecmp (table[i].name, tok)))
218       i++;
219     if (table[i].name != NULL)
220       last *= table[i].value;
221     else
222     {
223       ret += last;
224       last = 0;
225       if (1 != SSCANF (tok, "%llu", &last))
226       {
227         GNUNET_free (in);
228         return GNUNET_SYSERR;   /* expected number */
229       }
230     }
231   }
232   ret += last;
233   *output = ret;
234   GNUNET_free (in);
235   return GNUNET_OK;
236 }
237
238
239 /**
240  * Convert a given fancy human-readable size to bytes.
241  *
242  * @param fancy_size human readable string (i.e. 1 MB)
243  * @param size set to the size in bytes
244  * @return GNUNET_OK on success, GNUNET_SYSERR on error
245  */
246 int
247 GNUNET_STRINGS_fancy_size_to_bytes (const char *fancy_size,
248                                     unsigned long long *size)
249 {
250   static const struct ConversionTable table[] =
251   {
252     { "B", 1},
253     { "KiB", 1024},
254     { "kB", 1000},
255     { "MiB", 1024 * 1024},
256     { "MB", 1000 * 1000},
257     { "GiB", 1024 * 1024 * 1024},
258     { "GB", 1000 * 1000 * 1000},
259     { "TiB", 1024LL * 1024LL * 1024LL * 1024LL},
260     { "TB", 1000LL * 1000LL * 1000LL * 1024LL},
261     { "PiB", 1024LL * 1024LL * 1024LL * 1024LL * 1024LL},
262     { "PB", 1000LL * 1000LL * 1000LL * 1024LL * 1000LL},
263     { "EiB", 1024LL * 1024LL * 1024LL * 1024LL * 1024LL * 1024LL},
264     { "EB", 1000LL * 1000LL * 1000LL * 1024LL * 1000LL * 1000LL},
265     { NULL, 0}
266   };
267
268   return convert_with_table (fancy_size,
269                              table,
270                              size);
271 }
272
273
274 /**
275  * Convert a given fancy human-readable time to our internal
276  * representation.
277  *
278  * @param fancy_time human readable string (i.e. 1 minute)
279  * @param rtime set to the relative time
280  * @return GNUNET_OK on success, GNUNET_SYSERR on error
281  */
282 int
283 GNUNET_STRINGS_fancy_time_to_relative (const char *fancy_time,
284                                        struct GNUNET_TIME_Relative *rtime)
285 {
286   static const struct ConversionTable table[] =
287   {
288     { "ms", 1},
289     { "s", 1000},
290     { "\"", 1000},
291     { "min", 60 * 1000},
292     { "minutes", 60 * 1000},
293     { "'", 60 * 1000},
294     { "h", 60 * 60 * 1000},
295     { "d", 24 * 60 * 60 * 1000},
296     { "a", 31536000000LL /* year */ },
297     { NULL, 0}
298   };
299   int ret;
300   unsigned long long val;
301
302   ret = convert_with_table (fancy_time,
303                             table,
304                             &val);
305   rtime->rel_value = (uint64_t) val;
306   return ret;
307 }
308
309 /**
310  * Convert the len characters long character sequence
311  * given in input that is in the given input charset
312  * to a string in given output charset.
313  * @return the converted string (0-terminated),
314  *  if conversion fails, a copy of the orignal
315  *  string is returned.
316  */
317 char *
318 GNUNET_STRINGS_conv (const char *input, size_t len, const char *input_charset, const char *output_charset)
319 {
320   char *ret;
321
322 #if ENABLE_NLS && HAVE_ICONV
323   size_t tmpSize;
324   size_t finSize;
325   char *tmp;
326   char *itmp;
327   iconv_t cd;
328
329   cd = iconv_open (output_charset, input_charset);
330   if (cd == (iconv_t) - 1)
331   {
332     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "iconv_open");
333     LOG (GNUNET_ERROR_TYPE_WARNING, _("Character sets requested were `%s'->`%s'\n"),
334          input_charset, output_charset);
335     ret = GNUNET_malloc (len + 1);
336     memcpy (ret, input, len);
337     ret[len] = '\0';
338     return ret;
339   }
340   tmpSize = 3 * len + 4;
341   tmp = GNUNET_malloc (tmpSize);
342   itmp = tmp;
343   finSize = tmpSize;
344   if (iconv (cd,
345 #if FREEBSD || DARWIN || WINDOWS
346              (const char **) &input,
347 #else
348              (char **) &input,
349 #endif
350              &len, &itmp, &finSize) == SIZE_MAX)
351   {
352     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "iconv");
353     iconv_close (cd);
354     GNUNET_free (tmp);
355     ret = GNUNET_malloc (len + 1);
356     memcpy (ret, input, len);
357     ret[len] = '\0';
358     return ret;
359   }
360   ret = GNUNET_malloc (tmpSize - finSize + 1);
361   memcpy (ret, tmp, tmpSize - finSize);
362   ret[tmpSize - finSize] = '\0';
363   GNUNET_free (tmp);
364   if (0 != iconv_close (cd))
365     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "iconv_close");
366   return ret;
367 #else
368   ret = GNUNET_malloc (len + 1);
369   memcpy (ret, input, len);
370   ret[len] = '\0';
371   return ret;
372 #endif
373 }
374
375
376 /**
377  * Convert the len characters long character sequence
378  * given in input that is in the given charset
379  * to UTF-8.
380  * @return the converted string (0-terminated),
381  *  if conversion fails, a copy of the orignal
382  *  string is returned.
383  */
384 char *
385 GNUNET_STRINGS_to_utf8 (const char *input, size_t len, const char *charset)
386 {
387   return GNUNET_STRINGS_conv (input, len, charset, "UTF-8");
388 }
389
390 /**
391  * Convert the len bytes-long UTF-8 string
392  * given in input to the given charset.
393
394  * @return the converted string (0-terminated),
395  *  if conversion fails, a copy of the orignal
396  *  string is returned.
397  */
398 char *
399 GNUNET_STRINGS_from_utf8 (const char *input, size_t len, const char *charset)
400 {
401   return GNUNET_STRINGS_conv (input, len, "UTF-8", charset);
402 }
403
404
405
406 /**
407  * Complete filename (a la shell) from abbrevition.
408  * @param fil the name of the file, may contain ~/ or
409  *        be relative to the current directory
410  * @returns the full file name,
411  *          NULL is returned on error
412  */
413 char *
414 GNUNET_STRINGS_filename_expand (const char *fil)
415 {
416   char *buffer;
417
418 #ifndef MINGW
419   size_t len;
420   size_t n;
421   char *fm;
422   const char *fil_ptr;
423 #else
424   char *fn;
425   long lRet;
426 #endif
427
428   if (fil == NULL)
429     return NULL;
430
431 #ifndef MINGW
432   if (fil[0] == DIR_SEPARATOR)
433     /* absolute path, just copy */
434     return GNUNET_strdup (fil);
435   if (fil[0] == '~')
436   {
437     fm = getenv ("HOME");
438     if (fm == NULL)
439     {
440       LOG (GNUNET_ERROR_TYPE_WARNING,
441            _("Failed to expand `$HOME': environment variable `HOME' not set"));
442       return NULL;
443     }
444     fm = GNUNET_strdup (fm);
445     /* do not copy '~' */
446     fil_ptr = fil + 1;
447
448     /* skip over dir seperator to be consistent */
449     if (fil_ptr[0] == DIR_SEPARATOR)
450       fil_ptr++;
451   }
452   else
453   {
454     /* relative path */
455     fil_ptr = fil;
456     len = 512;
457     fm = NULL;
458     while (1)
459     {
460       buffer = GNUNET_malloc (len);
461       if (getcwd (buffer, len) != NULL)
462       {
463         fm = buffer;
464         break;
465       }
466       if ((errno == ERANGE) && (len < 1024 * 1024 * 4))
467       {
468         len *= 2;
469         GNUNET_free (buffer);
470         continue;
471       }
472       GNUNET_free (buffer);
473       break;
474     }
475     if (fm == NULL)
476     {
477       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "getcwd");
478       buffer = getenv ("PWD");  /* alternative */
479       if (buffer != NULL)
480         fm = GNUNET_strdup (buffer);
481     }
482     if (fm == NULL)
483       fm = GNUNET_strdup ("./");        /* give up */
484   }
485   n = strlen (fm) + 1 + strlen (fil_ptr) + 1;
486   buffer = GNUNET_malloc (n);
487   GNUNET_snprintf (buffer, n, "%s%s%s", fm,
488                    (fm[strlen (fm) - 1] ==
489                     DIR_SEPARATOR) ? "" : DIR_SEPARATOR_STR, fil_ptr);
490   GNUNET_free (fm);
491   return buffer;
492 #else
493   fn = GNUNET_malloc (MAX_PATH + 1);
494
495   if ((lRet = plibc_conv_to_win_path (fil, fn)) != ERROR_SUCCESS)
496   {
497     SetErrnoFromWinError (lRet);
498     LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "plibc_conv_to_win_path");
499     return NULL;
500   }
501   /* is the path relative? */
502   if ((strncmp (fn + 1, ":\\", 2) != 0) && (strncmp (fn, "\\\\", 2) != 0))
503   {
504     char szCurDir[MAX_PATH + 1];
505
506     lRet = GetCurrentDirectory (MAX_PATH + 1, szCurDir);
507     if (lRet + strlen (fn) + 1 > (MAX_PATH + 1))
508     {
509       SetErrnoFromWinError (ERROR_BUFFER_OVERFLOW);
510       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetCurrentDirectory");
511       return NULL;
512     }
513     buffer = GNUNET_malloc (MAX_PATH + 1);
514     GNUNET_snprintf (buffer, MAX_PATH + 1, "%s\\%s", szCurDir, fn);
515     GNUNET_free (fn);
516     fn = buffer;
517   }
518
519   return fn;
520 #endif
521 }
522
523
524 /**
525  * Give relative time in human-readable fancy format.
526  *
527  * @param delta time in milli seconds
528  * @return time as human-readable string
529  */
530 char *
531 GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative delta)
532 {
533   const char *unit = _( /* time unit */ "ms");
534   char *ret;
535   uint64_t dval = delta.rel_value;
536
537   if (delta.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
538     return GNUNET_strdup (_("eternity"));
539   if (dval > 5 * 1000)
540   {
541     dval = dval / 1000;
542     unit = _( /* time unit */ "s");
543     if (dval > 5 * 60)
544     {
545       dval = dval / 60;
546       unit = _( /* time unit */ "m");
547       if (dval > 5 * 60)
548       {
549         dval = dval / 60;
550         unit = _( /* time unit */ "h");
551         if (dval > 5 * 24)
552         {
553           dval = dval / 24;
554           unit = _( /* time unit */ " days");
555         }
556       }
557     }
558   }
559   GNUNET_asprintf (&ret, "%llu %s", dval, unit);
560   return ret;
561 }
562
563
564 /**
565  * "man ctime_r", except for GNUnet time; also, unlike ctime, the
566  * return value does not include the newline character.
567  *
568  * @param t time to convert
569  * @return absolute time in human-readable format
570  */
571 char *
572 GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t)
573 {
574   time_t tt;
575   char *ret;
576
577   if (t.abs_value == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value)
578     return GNUNET_strdup (_("end of time"));
579   tt = t.abs_value / 1000;
580 #ifdef ctime_r
581   ret = ctime_r (&tt, GNUNET_malloc (32));
582 #else
583   ret = GNUNET_strdup (ctime (&tt));
584 #endif
585   ret[strlen (ret) - 1] = '\0';
586   return ret;
587 }
588
589
590 /**
591  * "man basename"
592  * Returns a pointer to a part of filename (allocates nothing)!
593  *
594  * @param filename filename to extract basename from
595  * @return short (base) name of the file (that is, everything following the
596  *         last directory separator in filename. If filename ends with a
597  *         directory separator, the result will be a zero-length string.
598  *         If filename has no directory separators, the result is filename
599  *         itself.
600  */
601 const char *
602 GNUNET_STRINGS_get_short_name (const char *filename)
603 {
604   const char *short_fn = filename;
605   const char *ss;
606   while (NULL != (ss = strstr (short_fn, DIR_SEPARATOR_STR))
607       && (ss[1] != '\0'))
608     short_fn = 1 + ss;
609   return short_fn;
610 }
611
612
613 /**
614  * Get the numeric value corresponding to a character.
615  *
616  * @param a a character
617  * @return corresponding numeric value
618  */
619 static unsigned int
620 getValue__ (unsigned char a)
621 {
622   if ((a >= '0') && (a <= '9'))
623     return a - '0';
624   if ((a >= 'A') && (a <= 'V'))
625     return (a - 'A' + 10);
626   return -1;
627 }
628
629
630 /**
631  * Convert binary data to ASCII encoding.  The ASCII encoding is rather
632  * GNUnet specific.  It was chosen such that it only uses characters
633  * in [0-9A-V], can be produced without complex arithmetics and uses a
634  * small number of characters.  
635  * Does not append 0-terminator, but returns a pointer to the place where
636  * it should be placed, if needed.
637  *
638  * @param data data to encode
639  * @param size size of data (in bytes)
640  * @param out buffer to fill
641  * @param out_size size of the buffer. Must be large enough to hold
642  * ((size*8) + (((size*8) % 5) > 0 ? 5 - ((size*8) % 5) : 0)) / 5 bytes
643  * @return pointer to the next byte in 'out' or NULL on error.
644  */
645 char *
646 GNUNET_STRINGS_data_to_string (const unsigned char *data, size_t size, char *out, size_t out_size)
647 {
648   /**
649    * 32 characters for encoding 
650    */
651   static char *encTable__ = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
652   unsigned int wpos;
653   unsigned int rpos;
654   unsigned int bits;
655   unsigned int vbit;
656
657   GNUNET_assert (data != NULL);
658   GNUNET_assert (out != NULL);
659   if (out_size < (((size*8) + ((size*8) % 5)) % 5))
660   {
661     GNUNET_break (0);
662     return NULL;
663   }
664   vbit = 0;
665   wpos = 0;
666   rpos = 0;
667   bits = 0;
668   while ((rpos < size) || (vbit > 0))
669   {
670     if ((rpos < size) && (vbit < 5))
671     {
672       bits = (bits << 8) | data[rpos++];   /* eat 8 more bits */
673       vbit += 8;
674     }
675     if (vbit < 5)
676     {
677       bits <<= (5 - vbit);      /* zero-padding */
678       GNUNET_assert (vbit == ((size * 8) % 5));
679       vbit = 5;
680     }
681     if (wpos >= out_size)
682     {
683       GNUNET_break (0);
684       return NULL;
685     }
686     out[wpos++] = encTable__[(bits >> (vbit - 5)) & 31];
687     vbit -= 5;
688   }
689   if (wpos != out_size)
690   {
691     GNUNET_break (0);
692     return NULL;
693   }
694   GNUNET_assert (vbit == 0);
695   return &out[wpos];
696 }
697
698
699 /**
700  * Convert ASCII encoding back to data
701  * out_size must match exactly the size of the data before it was encoded.
702  *
703  * @param enc the encoding
704  * @param enclen number of characters in 'enc' (without 0-terminator, which can be missing)
705  * @param out location where to store the decoded data
706  * @param out_size sizeof the output buffer
707  * @return GNUNET_OK on success, GNUNET_SYSERR if result has the wrong encoding
708  */
709 int
710 GNUNET_STRINGS_string_to_data (const char *enc, size_t enclen,
711                               unsigned char *out, size_t out_size)
712 {
713   unsigned int rpos;
714   unsigned int wpos;
715   unsigned int bits;
716   unsigned int vbit;
717   int ret;
718   int shift;
719   int encoded_len = out_size * 8;
720   if (encoded_len % 5 > 0)
721   {
722     vbit = encoded_len % 5; /* padding! */
723     shift = 5 - vbit;
724   }
725   else
726   {
727     vbit = 0;
728     shift = 0;
729   }
730   if ((encoded_len + shift) / 5 != enclen)
731     return GNUNET_SYSERR;
732
733   wpos = out_size;
734   rpos = enclen;
735   bits = (ret = getValue__ (enc[--rpos])) >> (5 - encoded_len % 5);
736   if (-1 == ret)
737     return GNUNET_SYSERR;
738   while (wpos > 0)
739   {
740     GNUNET_assert (rpos > 0);
741     bits = ((ret = getValue__ (enc[--rpos])) << vbit) | bits;
742     if (-1 == ret)
743       return GNUNET_SYSERR;
744     vbit += 5;
745     if (vbit >= 8)
746     {
747       out[--wpos] = (unsigned char) bits;
748       bits >>= 8;
749       vbit -= 8;
750     }
751   }
752   GNUNET_assert (rpos == 0);
753   GNUNET_assert (vbit == 0);
754   return GNUNET_OK;
755 }
756
757
758 /**
759  * Parse a path that might be an URI.
760  *
761  * @param path path to parse. Must be NULL-terminated.
762  * @param scheme_part a pointer to 'char *' where a pointer to a string that
763  *        represents the URI scheme will be stored. Can be NULL. The string is
764  *        allocated by the function, and should be freed by GNUNET_free() when
765  *        it is no longer needed.
766  * @param path_part a pointer to 'const char *' where a pointer to the path
767  *        part of the URI will be stored. Can be NULL. Points to the same block
768  *        of memory as 'path', and thus must not be freed. Might point to '\0',
769  *        if path part is zero-length.
770  * @return GNUNET_YES if it's an URI, GNUNET_NO otherwise. If 'path' is not
771  *         an URI, '* scheme_part' and '*path_part' will remain unchanged
772  *         (if they weren't NULL).
773  */
774 int
775 GNUNET_STRINGS_parse_uri (const char *path, char **scheme_part,
776     const char **path_part)
777 {
778   size_t len;
779   int i, end;
780   int pp_state = 0;
781   const char *post_scheme_part = NULL;
782   len = strlen (path);
783   for (end = 0, i = 0; !end && i < len; i++)
784   {
785     switch (pp_state)
786     {
787     case 0:
788       if (path[i] == ':' && i > 0)
789       {
790         pp_state += 1;
791         continue;
792       }
793       if (!((path[i] >= 'A' && path[i] <= 'Z') || (path[i] >= 'a' && path[i] <= 'z')
794           || (path[i] >= '0' && path[i] <= '9') || path[i] == '+' || path[i] == '-'
795           || (path[i] == '.')))
796         end = 1;
797       break;
798     case 1:
799     case 2:
800       if (path[i] == '/')
801       {
802         pp_state += 1;
803         continue;
804       }
805       end = 1;
806       break;
807     case 3:
808       post_scheme_part = &path[i];
809       end = 1;
810       break;
811     default:
812       end = 1;
813     }
814   }
815   if (post_scheme_part == NULL)
816     return GNUNET_NO;
817   if (scheme_part)
818   {
819     *scheme_part = GNUNET_malloc (post_scheme_part - path + 1);
820     memcpy (*scheme_part, path, post_scheme_part - path);
821     (*scheme_part)[post_scheme_part - path] = '\0';
822   }
823   if (path_part)
824     *path_part = post_scheme_part;
825   return GNUNET_YES;
826 }
827
828
829 /**
830  * Check whether 'filename' is absolute or not, and if it's an URI
831  *
832  * @param filename filename to check
833  * @param can_be_uri GNUNET_YES to check for being URI, GNUNET_NO - to
834  *        assume it's not URI
835  * @param r_is_uri a pointer to an int that is set to GNUNET_YES if 'filename'
836  *        is URI and to GNUNET_NO otherwise. Can be NULL. If 'can_be_uri' is
837  *        not GNUNET_YES, *r_is_uri is set to GNUNET_NO.
838  * @param r_uri_scheme a pointer to a char * that is set to a pointer to URI scheme.
839  *        The string is allocated by the function, and should be freed with
840  *        GNUNET_free (). Can be NULL.
841  * @return GNUNET_YES if 'filename' is absolute, GNUNET_NO otherwise.
842  */
843 int
844 GNUNET_STRINGS_path_is_absolute (const char *filename, int can_be_uri,
845     int *r_is_uri, char **r_uri_scheme)
846 {
847 #if WINDOWS
848   size_t len;
849 #endif
850   const char *post_scheme_path;
851   int is_uri;
852   char * uri;
853   /* consider POSIX paths to be absolute too, even on W32,
854    * as plibc expansion will fix them for us.
855    */
856   if (filename[0] == '/')
857     return GNUNET_YES;
858   if (can_be_uri)
859   {
860     is_uri = GNUNET_STRINGS_parse_uri (filename, &uri, &post_scheme_path);
861     if (r_is_uri)
862       *r_is_uri = is_uri;
863     if (is_uri)
864     {
865       if (r_uri_scheme)
866         *r_uri_scheme = uri;
867       else
868         GNUNET_free_non_null (uri);
869 #if WINDOWS
870       len = strlen(post_scheme_path);
871       /* Special check for file:///c:/blah
872        * We want to parse 'c:/', not '/c:/'
873        */
874       if (post_scheme_path[0] == '/' && len >= 3 && post_scheme_path[2] == ':')
875         post_scheme_path = &post_scheme_path[1];
876 #endif
877       return GNUNET_STRINGS_path_is_absolute (post_scheme_path, GNUNET_NO, NULL, NULL);
878     }
879   }
880   else
881   {
882     is_uri = GNUNET_NO;
883     if (r_is_uri)
884       *r_is_uri = GNUNET_NO;
885   }
886 #if WINDOWS
887   len = strlen (filename);
888   if (len >= 3 &&
889       ((filename[0] >= 'A' && filename[0] <= 'Z')
890       || (filename[0] >= 'a' && filename[0] <= 'z'))
891       && filename[1] == ':' && (filename[2] == '/' || filename[2] == '\\'))
892     return GNUNET_YES;
893 #endif
894   return GNUNET_NO;
895 }
896
897 #if MINGW
898 #define         _IFMT           0170000 /* type of file */
899 #define         _IFLNK          0120000 /* symbolic link */
900 #define  S_ISLNK(m)     (((m)&_IFMT) == _IFLNK)
901 #endif
902
903 /**
904  * Perform 'checks' on 'filename'
905  * 
906  * @param filename file to check
907  * @param checks checks to perform
908  * @return GNUNET_YES if all checks pass, GNUNET_NO if at least one of them
909  *         fails, GNUNET_SYSERR when a check can't be performed
910  */
911 int
912 GNUNET_STRINGS_check_filename (const char *filename,
913                                enum GNUNET_STRINGS_FilenameCheck checks)
914 {
915   struct stat st;
916   if (filename == NULL || filename[0] == '\0')
917     return GNUNET_SYSERR;
918   if (checks & GNUNET_STRINGS_CHECK_IS_ABSOLUTE)
919     if (!GNUNET_STRINGS_path_is_absolute (filename, GNUNET_NO, NULL, NULL))
920       return GNUNET_NO;
921   if (checks & (GNUNET_STRINGS_CHECK_EXISTS
922       | GNUNET_STRINGS_CHECK_IS_DIRECTORY
923       | GNUNET_STRINGS_CHECK_IS_LINK))
924   {
925     if (STAT (filename, &st))
926     {
927       if (checks & GNUNET_STRINGS_CHECK_EXISTS)
928         return GNUNET_NO;
929       else
930         return GNUNET_SYSERR;
931     }
932   }
933   if (checks & GNUNET_STRINGS_CHECK_IS_DIRECTORY)
934     if (!S_ISDIR (st.st_mode))
935       return GNUNET_NO;
936   if (checks & GNUNET_STRINGS_CHECK_IS_LINK)
937     if (!S_ISLNK (st.st_mode))
938       return GNUNET_NO;
939   return GNUNET_YES;
940 }
941
942 #define MAX_IPV6_ADDRLEN 47
943 #define MAX_IPV4_ADDRLEN 21
944 #define MAX_IP_ADDRLEN MAX_IPV6_ADDRLEN
945
946
947 /**
948  * Tries to convert 'zt_addr' string to an IPv6 address.
949  * 
950  * @param zt_addr 0-terminated string. May be mangled by the function.
951  * @param addrlen length of zt_addr (not counting 0-terminator).
952  * @param r_buf a buffer to fill. Initially gets filled with zeroes,
953  *        then its sin6_port, sin6_family and sin6_addr are set appropriately.
954  * @return GNUNET_OK if conversion succeded. GNUNET_SYSERR otherwise, in which
955  *         case the contents of r_buf are undefined.
956  */
957 int
958 GNUNET_STRINGS_to_address_ipv6 (const char *zt_addr, 
959                                 uint16_t addrlen,
960                                 struct sockaddr_in6 *r_buf)
961 {
962   int ret;
963   char *port_colon;
964   unsigned int port;
965
966   if (addrlen < 6)
967     return GNUNET_SYSERR;
968
969   port_colon = strrchr (zt_addr, ':');
970   if (port_colon == NULL)
971     return GNUNET_SYSERR;
972   ret = SSCANF (port_colon, ":%u", &port);
973   if (ret != 1 || port > 65535)
974     return GNUNET_SYSERR;
975   port_colon[0] = '\0';
976   memset (r_buf, 0, sizeof (struct sockaddr_in6));
977   ret = inet_pton (AF_INET6, zt_addr, &r_buf->sin6_addr);
978   if (ret <= 0)
979     return GNUNET_SYSERR;
980   r_buf->sin6_port = htonl (port);
981   r_buf->sin6_family = AF_INET6;
982   return GNUNET_OK;
983 }
984
985
986 /**
987  * Tries to convert 'zt_addr' string to an IPv4 address.
988  * 
989  * @param zt_addr 0-terminated string. May be mangled by the function.
990  * @param addrlen length of zt_addr (not counting 0-terminator).
991  * @param r_buf a buffer to fill.
992  * @return GNUNET_OK if conversion succeded. GNUNET_SYSERR otherwise, in which case
993  *         the contents of r_buf are undefined.
994  */
995 int
996 GNUNET_STRINGS_to_address_ipv4 (const char *zt_addr, uint16_t addrlen,
997                                 struct sockaddr_in *r_buf)
998 {
999   unsigned int temps[5];
1000   unsigned int port;
1001   int cnt;
1002
1003   if (addrlen < 9)
1004     return GNUNET_SYSERR;
1005
1006   cnt = SSCANF (zt_addr, "%u.%u.%u.%u:%u", &temps[0], &temps[1], &temps[2], &temps[3], &port);
1007   if (cnt != 5)
1008     return GNUNET_SYSERR;
1009
1010   for (cnt = 0; cnt < 4; cnt++)
1011     if (temps[cnt] > 0xFF)
1012       return GNUNET_SYSERR;
1013   if (port > 65535)
1014     return GNUNET_SYSERR;
1015
1016   r_buf->sin_family = AF_INET;
1017   r_buf->sin_port = htonl (port);
1018   r_buf->sin_addr.s_addr = htonl ((temps[0] << 24) + (temps[1] << 16) +
1019       (temps[2] << 8) + temps[3]);
1020   return GNUNET_OK;
1021 }
1022
1023 /**
1024  * Tries to convert 'addr' string to an IP (v4 or v6) address.
1025  * IPv6 address must have its address part enclosed in '()' parens
1026  * instead of '[]'.
1027  * Will automatically decide whether to treat 'addr' as v4 or v6 address.
1028  * 
1029  * @param addr a string, may not be 0-terminated.
1030  * @param addrlen number of bytes in addr (if addr is 0-terminated,
1031  *        0-terminator should not be counted towards addrlen).
1032  * @param r_buf a buffer to fill.
1033  * @return GNUNET_OK if conversion succeded. GNUNET_SYSERR otherwise, in which
1034  *         case the contents of r_buf are undefined.
1035  */
1036 int
1037 GNUNET_STRINGS_to_address_ip (const char *addr, 
1038                               uint16_t addrlen,
1039                               struct sockaddr_storage *r_buf)
1040 {
1041   uint16_t i;
1042   char zt_addr[MAX_IP_ADDRLEN + 1];
1043   uint16_t zt_len = addrlen <= MAX_IP_ADDRLEN ? addrlen : MAX_IP_ADDRLEN;
1044
1045   if (addrlen < 1)
1046     return GNUNET_SYSERR;
1047
1048   memset (zt_addr, 0, MAX_IP_ADDRLEN + 1);
1049   strncpy (zt_addr, addr, zt_len);
1050
1051   /* For URIs we use '(' and ')' instead of '[' and ']'. Do the substitution
1052    * now, as GNUNET_STRINGS_to_address_ipv6() takes a proper []-enclosed IPv6
1053    * address.
1054    */
1055   if (zt_addr[0] == '(')
1056   {
1057     for (i = 0; i < zt_len; i++)
1058     {
1059       switch (zt_addr[i])
1060       {
1061       case '(':
1062         zt_addr[i] = '[';
1063         break;
1064       case ')':
1065         zt_addr[i] = ']';
1066         break;
1067       default:
1068         break;
1069       }
1070     }
1071     return GNUNET_STRINGS_to_address_ipv6 (zt_addr, zt_len, (struct sockaddr_in6 *) r_buf);
1072   }
1073   return GNUNET_STRINGS_to_address_ipv4 (zt_addr, zt_len, (struct sockaddr_in *) r_buf);
1074 }
1075
1076 /* end of strings.c */