-fixes
[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  * "man basename"
613  * Returns a pointer to a part of filename (allocates nothing)!
614  *
615  * @param filename filename to extract basename from
616  * @return short (base) name of the file (that is, everything following the
617  *         last directory separator in filename. If filename ends with a
618  *         directory separator, the result will be a zero-length string.
619  *         If filename has no directory separators, the result is filename
620  *         itself.
621  */
622 const char *
623 GNUNET_STRINGS_get_short_name (const char *filename)
624 {
625   const char *short_fn = filename;
626   const char *ss;
627   while (NULL != (ss = strstr (short_fn, DIR_SEPARATOR_STR))
628       && (ss[1] != '\0'))
629     short_fn = 1 + ss;
630   return short_fn;
631 }
632
633
634 /**
635  * Get the numeric value corresponding to a character.
636  *
637  * @param a a character
638  * @return corresponding numeric value
639  */
640 static unsigned int
641 getValue__ (unsigned char a)
642 {
643   if ((a >= '0') && (a <= '9'))
644     return a - '0';
645   if ((a >= 'A') && (a <= 'V'))
646     return (a - 'A' + 10);
647   return -1;
648 }
649
650
651 /**
652  * Convert binary data to ASCII encoding.  The ASCII encoding is rather
653  * GNUnet specific.  It was chosen such that it only uses characters
654  * in [0-9A-V], can be produced without complex arithmetics and uses a
655  * small number of characters.  
656  * Does not append 0-terminator, but returns a pointer to the place where
657  * it should be placed, if needed.
658  *
659  * @param data data to encode
660  * @param size size of data (in bytes)
661  * @param out buffer to fill
662  * @param out_size size of the buffer. Must be large enough to hold
663  * ((size*8) + (((size*8) % 5) > 0 ? 5 - ((size*8) % 5) : 0)) / 5 bytes
664  * @return pointer to the next byte in 'out' or NULL on error.
665  */
666 char *
667 GNUNET_STRINGS_data_to_string (unsigned char *data, size_t size, char *out, size_t out_size)
668 {
669   /**
670    * 32 characters for encoding (GNUNET_CRYPTO_hash => 32 characters)
671    */
672   static char *encTable__ = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
673   unsigned int wpos;
674   unsigned int rpos;
675   unsigned int bits;
676   unsigned int vbit;
677
678   GNUNET_assert (data != NULL);
679   GNUNET_assert (out != NULL);
680   GNUNET_assert (out_size >= (((size*8) + ((size*8) % 5)) % 5));
681   vbit = 0;
682   wpos = 0;
683   rpos = 0;
684   bits = 0;
685   while ((rpos < size) || (vbit > 0))
686   {
687     if ((rpos < size) && (vbit < 5))
688     {
689       bits = (bits << 8) | data[rpos++];   /* eat 8 more bits */
690       vbit += 8;
691     }
692     if (vbit < 5)
693     {
694       bits <<= (5 - vbit);      /* zero-padding */
695       GNUNET_assert (vbit == ((size * 8) % 5));
696       vbit = 5;
697     }
698     if (wpos >= out_size)
699       return NULL;
700     out[wpos++] = encTable__[(bits >> (vbit - 5)) & 31];
701     vbit -= 5;
702   }
703   if (wpos != out_size)
704     return NULL;
705   GNUNET_assert (vbit == 0);
706   return &out[wpos];
707 }
708
709
710 /**
711  * Convert ASCII encoding back to data
712  * out_size must match exactly the size of the data before it was encoded.
713  *
714  * @param enc the encoding
715  * @param enclen number of characters in 'enc' (without 0-terminator, which can be missing)
716  * @param out location where to store the decoded data
717  * @param out_size sizeof the output buffer
718  * @return GNUNET_OK on success, GNUNET_SYSERR if result has the wrong encoding
719  */
720 int
721 GNUNET_STRINGS_string_to_data (const char *enc, size_t enclen,
722                               unsigned char *out, size_t out_size)
723 {
724   unsigned int rpos;
725   unsigned int wpos;
726   unsigned int bits;
727   unsigned int vbit;
728   int ret;
729   int shift;
730   int encoded_len = out_size * 8;
731   if (encoded_len % 5 > 0)
732   {
733     vbit = encoded_len % 5; /* padding! */
734     shift = 5 - vbit;
735   }
736   else
737   {
738     vbit = 0;
739     shift = 0;
740   }
741   if ((encoded_len + shift) / 5 != enclen)
742     return GNUNET_SYSERR;
743
744   wpos = out_size;
745   rpos = enclen;
746   bits = (ret = getValue__ (enc[--rpos])) >> (5 - encoded_len % 5);
747   if (-1 == ret)
748     return GNUNET_SYSERR;
749   while (wpos > 0)
750   {
751     GNUNET_assert (rpos > 0);
752     bits = ((ret = getValue__ (enc[--rpos])) << vbit) | bits;
753     if (-1 == ret)
754       return GNUNET_SYSERR;
755     vbit += 5;
756     if (vbit >= 8)
757     {
758       out[--wpos] = (unsigned char) bits;
759       bits >>= 8;
760       vbit -= 8;
761     }
762   }
763   GNUNET_assert (rpos == 0);
764   GNUNET_assert (vbit == 0);
765   return GNUNET_OK;
766 }
767
768
769 /**
770  * Parse a path that might be an URI.
771  *
772  * @param path path to parse. Must be NULL-terminated.
773  * @param scheme_part a pointer to 'char *' where a pointer to a string that
774  *        represents the URI scheme will be stored. Can be NULL. The string is
775  *        allocated by the function, and should be freed by GNUNET_free() when
776  *        it is no longer needed.
777  * @param path_part a pointer to 'const char *' where a pointer to the path
778  *        part of the URI will be stored. Can be NULL. Points to the same block
779  *        of memory as 'path', and thus must not be freed. Might point to '\0',
780  *        if path part is zero-length.
781  * @return GNUNET_YES if it's an URI, GNUNET_NO otherwise. If 'path' is not
782  *         an URI, '* scheme_part' and '*path_part' will remain unchanged
783  *         (if they weren't NULL).
784  */
785 int
786 GNUNET_STRINGS_parse_uri (const char *path, char **scheme_part,
787     const char **path_part)
788 {
789   size_t len;
790   int i, end;
791   int pp_state = 0;
792   const char *post_scheme_part = NULL;
793   len = strlen (path);
794   for (end = 0, i = 0; !end && i < len; i++)
795   {
796     switch (pp_state)
797     {
798     case 0:
799       if (path[i] == ':' && i > 0)
800       {
801         pp_state += 1;
802         continue;
803       }
804       if (!((path[i] >= 'A' && path[i] <= 'Z') || (path[i] >= 'a' && path[i] <= 'z')
805           || (path[i] >= '0' && path[i] <= '9') || path[i] == '+' || path[i] == '-'
806           || (path[i] == '.')))
807         end = 1;
808       break;
809     case 1:
810     case 2:
811       if (path[i] == '/')
812       {
813         pp_state += 1;
814         continue;
815       }
816       end = 1;
817       break;
818     case 3:
819       post_scheme_part = &path[i];
820       end = 1;
821       break;
822     default:
823       end = 1;
824     }
825   }
826   if (post_scheme_part == NULL)
827     return GNUNET_NO;
828   if (scheme_part)
829   {
830     *scheme_part = GNUNET_malloc (post_scheme_part - path + 1);
831     memcpy (*scheme_part, path, post_scheme_part - path);
832     (*scheme_part)[post_scheme_part - path] = '\0';
833   }
834   if (path_part)
835     *path_part = post_scheme_part;
836   return GNUNET_YES;
837 }
838
839
840 /**
841  * Check whether @filename is absolute or not, and if it's an URI
842  *
843  * @param filename filename to check
844  * @param can_be_uri GNUNET_YES to check for being URI, GNUNET_NO - to
845  *        assume it's not URI
846  * @param r_is_uri a pointer to an int that is set to GNUNET_YES if @filename
847  *        is URI and to GNUNET_NO otherwise. Can be NULL. If @can_be_uri is
848  *        not GNUNET_YES, *r_is_uri is set to GNUNET_NO.
849  * @param r_uri a pointer to a char * that is set to a pointer to URI scheme.
850  *        The string is allocated by the function, and should be freed with
851  *        GNUNET_free (). Can be NULL.
852  * @return GNUNET_YES if @filaneme is absolute, GNUNET_NO otherwise.
853  */
854 int
855 GNUNET_STRINGS_path_is_absolute (const char *filename, int can_be_uri,
856     int *r_is_uri, char **r_uri_scheme)
857 {
858 #if WINDOWS
859   size_t len;
860 #endif
861   const char *post_scheme_path;
862   int is_uri;
863   char * uri;
864   /* consider POSIX paths to be absolute too, even on W32,
865    * as plibc expansion will fix them for us.
866    */
867   if (filename[0] == '/')
868     return GNUNET_YES;
869   if (can_be_uri)
870   {
871     is_uri = GNUNET_STRINGS_parse_uri (filename, &uri, &post_scheme_path);
872     if (r_is_uri)
873       *r_is_uri = is_uri;
874     if (is_uri)
875     {
876       if (r_uri_scheme)
877         *r_uri_scheme = uri;
878       else
879         GNUNET_free_non_null (uri);
880 #if WINDOWS
881       len = strlen(post_scheme_path);
882       /* Special check for file:///c:/blah
883        * We want to parse 'c:/', not '/c:/'
884        */
885       if (post_scheme_path[0] == '/' && len >= 3 && post_scheme_path[2] == ':')
886         post_scheme_path = &post_scheme_path[1];
887 #endif
888       return GNUNET_STRINGS_path_is_absolute (post_scheme_path, GNUNET_NO, NULL, NULL);
889     }
890   }
891   else
892   {
893     is_uri = GNUNET_NO;
894     if (r_is_uri)
895       *r_is_uri = GNUNET_NO;
896   }
897 #if WINDOWS
898   len = strlen (filename);
899   if (len >= 3 &&
900       ((filename[0] >= 'A' && filename[0] <= 'Z')
901       || (filename[0] >= 'a' && filename[0] <= 'z'))
902       && filename[1] == ':' && (filename[2] == '/' || filename[2] == '\\'))
903     return GNUNET_YES;
904 #endif
905   return GNUNET_NO;
906 }
907
908 #if MINGW
909 #define         _IFMT           0170000 /* type of file */
910 #define         _IFLNK          0120000 /* symbolic link */
911 #define  S_ISLNK(m)     (((m)&_IFMT) == _IFLNK)
912 #endif
913
914 /**
915  * Perform @checks on @filename
916  * 
917  * @param filename file to check
918  * @param checks checks to perform
919  * @return GNUNET_YES if all @checks pass, GNUNET_NO if at least one of them
920  *         fails, GNUNET_SYSERR when a check can't be performed
921  */
922 int
923 GNUNET_STRINGS_check_filename (const char *filename,
924                                enum GNUNET_STRINGS_FilenameCheck checks)
925 {
926   struct stat st;
927   if (filename == NULL || filename[0] == '\0')
928     return GNUNET_SYSERR;
929   if (checks & GNUNET_STRINGS_CHECK_IS_ABSOLUTE)
930     if (!GNUNET_STRINGS_path_is_absolute (filename, GNUNET_NO, NULL, NULL))
931       return GNUNET_NO;
932   if (checks & (GNUNET_STRINGS_CHECK_EXISTS
933       | GNUNET_STRINGS_CHECK_IS_DIRECTORY
934       | GNUNET_STRINGS_CHECK_IS_LINK))
935   {
936     if (STAT (filename, &st))
937     {
938       if (checks & GNUNET_STRINGS_CHECK_EXISTS)
939         return GNUNET_NO;
940       else
941         return GNUNET_SYSERR;
942     }
943   }
944   if (checks & GNUNET_STRINGS_CHECK_IS_DIRECTORY)
945     if (!S_ISDIR (st.st_mode))
946       return GNUNET_NO;
947   if (checks & GNUNET_STRINGS_CHECK_IS_LINK)
948     if (!S_ISLNK (st.st_mode))
949       return GNUNET_NO;
950   return GNUNET_YES;
951 }
952
953 #define MAX_IPV6_ADDRLEN 47
954 #define MAX_IPV4_ADDRLEN 21
955 #define MAX_IP_ADDRLEN MAX_IPV6_ADDRLEN
956
957
958 /**
959  * Tries to convert 'zt_addr' string to an IPv6 address.
960  * 
961  * @param zt_addr 0-terminated string. May be mangled by the function.
962  * @param addrlen length of zt_addr (not counting 0-terminator).
963  * @param r_buf a buffer to fill. Initially gets filled with zeroes,
964  *        then its sin6_port, sin6_family and sin6_addr are set appropriately.
965  * @return GNUNET_OK if conversion succeded. GNUNET_SYSERR otherwise, in which
966  *         case the contents of r_buf are undefined.
967  */
968 int
969 GNUNET_STRINGS_to_address_ipv6 (const char *zt_addr, 
970                                 uint16_t addrlen,
971                                 struct sockaddr_in6 *r_buf)
972 {
973   int ret;
974   char *port_colon;
975   unsigned int port;
976
977   if (addrlen < 6)
978     return GNUNET_SYSERR;
979
980   port_colon = strrchr (zt_addr, ':');
981   if (port_colon == NULL)
982     return GNUNET_SYSERR;
983   ret = SSCANF (port_colon, ":%u", &port);
984   if (ret != 1 || port > 65535)
985     return GNUNET_SYSERR;
986   port_colon[0] = '\0';
987   memset (r_buf, 0, sizeof (struct sockaddr_in6));
988   ret = inet_pton (AF_INET6, zt_addr, &r_buf->sin6_addr);
989   if (ret <= 0)
990     return GNUNET_SYSERR;
991   r_buf->sin6_port = htonl (port);
992   r_buf->sin6_family = AF_INET6;
993   return GNUNET_OK;
994 }
995
996
997 /**
998  * Tries to convert 'zt_addr' string to an IPv4 address.
999  * 
1000  * @param zt_addr 0-terminated string. May be mangled by the function.
1001  * @param addrlen length of zt_addr (not counting 0-terminator).
1002  * @param r_buf a buffer to fill.
1003  * @return GNUNET_OK if conversion succeded. GNUNET_SYSERR otherwise, in which case
1004  *         the contents of r_buf are undefined.
1005  */
1006 int
1007 GNUNET_STRINGS_to_address_ipv4 (const char *zt_addr, uint16_t addrlen,
1008                                 struct sockaddr_in *r_buf)
1009 {
1010   unsigned int temps[5];
1011   unsigned int port;
1012   int cnt;
1013
1014   if (addrlen < 9)
1015     return GNUNET_SYSERR;
1016
1017   cnt = SSCANF (zt_addr, "%u.%u.%u.%u:%u", &temps[0], &temps[1], &temps[2], &temps[3], &port);
1018   if (cnt != 5)
1019     return GNUNET_SYSERR;
1020
1021   for (cnt = 0; cnt < 4; cnt++)
1022     if (temps[cnt] > 0xFF)
1023       return GNUNET_SYSERR;
1024   if (port > 65535)
1025     return GNUNET_SYSERR;
1026
1027   r_buf->sin_family = AF_INET;
1028   r_buf->sin_port = htonl (port);
1029   r_buf->sin_addr.s_addr = htonl ((temps[0] << 24) + (temps[1] << 16) +
1030       (temps[2] << 8) + temps[3]);
1031   return GNUNET_OK;
1032 }
1033
1034 /**
1035  * Tries to convert 'addr' string to an IP (v4 or v6) address.
1036  * IPv6 address must have its address part enclosed in '()' parens
1037  * instead of '[]'.
1038  * Will automatically decide whether to treat 'addr' as v4 or v6 address.
1039  * 
1040  * @param addr a string, may not be 0-terminated.
1041  * @param addrlen number of bytes in addr (if addr is 0-terminated,
1042  *        0-terminator should not be counted towards addrlen).
1043  * @param r_buf a buffer to fill.
1044  * @return GNUNET_OK if conversion succeded. GNUNET_SYSERR otherwise, in which
1045  *         case the contents of r_buf are undefined.
1046  */
1047 int
1048 GNUNET_STRINGS_to_address_ip (const char *addr, 
1049                               uint16_t addrlen,
1050                               struct sockaddr_storage *r_buf)
1051 {
1052   uint16_t i;
1053   char zt_addr[MAX_IP_ADDRLEN + 1];
1054   uint16_t zt_len = addrlen <= MAX_IP_ADDRLEN ? addrlen : MAX_IP_ADDRLEN;
1055
1056   if (addrlen < 1)
1057     return GNUNET_SYSERR;
1058
1059   memset (zt_addr, 0, MAX_IP_ADDRLEN + 1);
1060   strncpy (zt_addr, addr, zt_len);
1061
1062   /* For URIs we use '(' and ')' instead of '[' and ']'. Do the substitution
1063    * now, as GNUNET_STRINGS_to_address_ipv6() takes a proper []-enclosed IPv6
1064    * address.
1065    */
1066   if (zt_addr[0] == '(')
1067   {
1068     for (i = 0; i < zt_len; i++)
1069     {
1070       switch (zt_addr[i])
1071       {
1072       case '(':
1073         zt_addr[i] = '[';
1074         break;
1075       case ')':
1076         zt_addr[i] = ']';
1077         break;
1078       default:
1079         break;
1080       }
1081     }
1082     return GNUNET_STRINGS_to_address_ipv6 (zt_addr, zt_len, (struct sockaddr_in6 *) r_buf);
1083   }
1084   return GNUNET_STRINGS_to_address_ipv4 (zt_addr, zt_len, (struct sockaddr_in *) r_buf);
1085 }
1086
1087 /* end of strings.c */