1 /* vi:ai:et:ts=8 sw=2
2  */
3 /*
4  * wzdftpd - a modular and cool ftp server
5  * Copyright (C) 2002-2004  Pierre Chifflier
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * As a special exemption, Pierre Chifflier
22  * and other respective copyright holders give permission to link this program
23  * with OpenSSL, and distribute the resulting executable, without including
24  * the source code for OpenSSL in the source distribution.
25  */
26 
27 #include "wzd_all.h"
28 
29 #ifndef WZD_USE_PCH
30 
31 #include <stdio.h>
32 #include <string.h>
33 
34 #include <ctype.h> /* isspace */
35 
36 #include "libwzd-base/list.h"
37 
38 #include "wzd_string.h"
39 
40 #include "wzd_structs.h"
41 #include "wzd_log.h"
42 #include "wzd_misc.h" /* ascii_lower */
43 
44 #ifdef HAVE_UTF8
45 # include "wzd_utf8.h"
46 #endif
47 
48 #ifndef HAVE_STRTOK_R
49 # include "libwzd-base/wzd_strtok_r.h"
50 #endif
51 
52 #include "wzd_debug.h"
53 
54 #endif /* WZD_USE_PCH */
55 
56 struct wzd_string_t {
57   char * buffer;
58   size_t length;
59   size_t allocated;
60 };
61 
62 static inline void _str_set_min_size(wzd_string_t *str, size_t length);
63 
64 
65 
str_allocate(void)66 wzd_string_t * str_allocate(void)
67 {
68   wzd_string_t * str;
69 
70   str = wzd_malloc(sizeof(wzd_string_t));
71   str->buffer = NULL;
72   str->length = 0;
73   str->allocated = 0;
74 
75   return str;
76 }
77 
str_deallocate(wzd_string_t * st)78 void str_deallocate(wzd_string_t *st)
79 {
80   if (st) {
81     wzd_free(st->buffer);
82 #ifdef DEBUG
83     memset(st,0xab,sizeof(wzd_string_t));
84 #endif
85     wzd_free(st);
86   }
87 }
88 
89 /** \brief Deallocates a NULL-terminated string list
90  */
str_deallocate_array(wzd_string_t ** array)91 void str_deallocate_array(wzd_string_t **array)
92 {
93   wzd_string_t ** iterator = array;
94 
95   if (!iterator) return;
96 
97   while ( (*iterator) ) {
98     str_deallocate(*iterator);
99     iterator++;
100   }
101   wzd_free(array);
102 }
103 
str_fromchar(const char * str)104 wzd_string_t * str_fromchar(const char *str)
105 {
106   wzd_string_t * s;
107   size_t length;
108 
109   s = str_allocate();
110 
111   if (s && str) {
112     length = strlen(str);
113     _str_set_min_size(s,length+1);
114     memcpy(s->buffer,str,length);
115     s->buffer[length] = '\0';
116     s->length = length;
117   }
118 
119   return s;
120 }
121 
122 /** returns a pointer to a new string pointing to \a str
123  *
124  * \note \a str must not be freed, you must use str_deallocate() on the result
125  */
str_fromchar_raw(char * str)126 wzd_string_t * str_fromchar_raw(char *str)
127 {
128   wzd_string_t * s;
129 
130   s = str_allocate();
131 
132   if (s && str) {
133     s->buffer = str;
134     s->length = strlen(str);
135     s->allocated = s->length;
136   }
137 
138   return s;
139 }
140 
141 /* str_tochar
142  * returns a pointer to the data contained in the string str.
143  * These data must NOT be modified !
144  */
str_tochar(const wzd_string_t * str)145 const char * str_tochar(const wzd_string_t *str)
146 {
147   return (str)?str->buffer:NULL;
148 }
149 
150 /** returns 1 if string exists and length is inside min and max (included)
151  */
str_checklength(const wzd_string_t * str,size_t min,size_t max)152 unsigned int str_checklength(const wzd_string_t *str, size_t min, size_t max)
153 {
154   if (!str || !str->buffer) return 0;
155   if (strlen(str->buffer) < min || strlen(str->buffer) > max) return 0;
156   return 1;
157 }
158 
159 /** Get the length of the given string, or -1 if error
160  */
str_length(const wzd_string_t * str)161 size_t str_length(const wzd_string_t *str)
162 {
163   if (!str || !str->buffer) return -1;
164   return str->length;
165 }
166 
str_store(wzd_string_t * str,const char * s)167 wzd_string_t * str_store(wzd_string_t * str, const char * s)
168 {
169   size_t length;
170 
171   if (!str) return NULL;
172 
173   length = strlen(s);
174   _str_set_min_size(str,length+1);
175 
176   wzd_strncpy(str->buffer, s, length+1);
177   str->length = length;
178 
179   return str;
180 }
181 
str_dup(const wzd_string_t * src)182 wzd_string_t * str_dup(const wzd_string_t *src)
183 {
184   wzd_string_t * dst;
185 
186   if (!src) return NULL;
187 
188 #if DEBUG
189   if ( (src->length >= src->allocated) ||
190        (src->length != strlen(src->buffer)) )
191   {
192     out_err(LEVEL_CRITICAL,"invalid string (%s) at %s:%d\n",src->buffer,__FILE__,__LINE__);
193     return NULL;
194   }
195 #endif
196 
197   dst = str_allocate();
198   _str_set_min_size(dst,src->allocated);
199   if (src->buffer) {
200     memcpy(dst->buffer,src->buffer,src->length);
201     dst->buffer[src->length] = '\0';
202     dst->length = src->length;
203   }
204 
205   return dst;
206 }
207 
str_copy(wzd_string_t * dst,const wzd_string_t * src)208 wzd_string_t * str_copy(wzd_string_t *dst, const wzd_string_t *src)
209 {
210   if (!src || !dst) return NULL;
211 
212 #if DEBUG
213   if ( (src->length >= src->allocated) ||
214        (src->length != strlen(src->buffer)) )
215   {
216     out_err(LEVEL_CRITICAL,"invalid string (%s) at %s:%d\n",src->buffer,__FILE__,__LINE__);
217     return NULL;
218   }
219   if ( dst->buffer &&
220       ((dst->length >= dst->allocated) ||
221        (dst->length != strlen(dst->buffer))) )
222   {
223     out_err(LEVEL_CRITICAL,"invalid string (%s) at %s:%d\n",dst->buffer,__FILE__,__LINE__);
224     return NULL;
225   }
226 #endif
227 
228   _str_set_min_size(dst,src->allocated);
229   if (src->buffer) {
230     memcpy(dst->buffer,src->buffer,src->length);
231     dst->buffer[src->length] = '\0';
232     dst->length = src->length;
233   }
234 
235   return dst;
236 }
237 
238 /** \brief append \a tail to string pointed to by \a str
239  */
str_append(wzd_string_t * str,const char * tail)240 wzd_string_t * str_append(wzd_string_t * str, const char *tail)
241 {
242   size_t length;
243 
244   if (!str) return NULL;
245   if (!tail) return str;
246 
247   length = strlen(tail);
248 
249   _str_set_min_size(str,str->length + length + 1);
250   if (str->buffer) {
251     strlcat(str->buffer,tail,str->length+length+1);
252     str->length += length;
253   }
254 
255   return str;
256 }
257 
258 /** \brief append character \a c to string pointed to by str
259  */
str_append_c(wzd_string_t * str,const char c)260 wzd_string_t * str_append_c(wzd_string_t * str, const char c)
261 {
262   if (!str) return NULL;
263 
264   _str_set_min_size(str,str->length + 2);
265   if (str->buffer) {
266     str->buffer[str->length] = c;
267     str->length ++;
268     str->buffer[str->length] = '\0';
269   }
270 
271   return str;
272 }
273 
274 /** \brief prepend 'head' to string pointed to by str
275  */
str_prepend(wzd_string_t * str,const char * head)276 wzd_string_t * str_prepend(wzd_string_t * str, const char *head)
277 {
278   size_t length;
279   char * buf;
280 
281   if (!str) return NULL;
282   if (!head) return str;
283 
284   length = strlen(head);
285 
286   if (length + str->length >= str->allocated)
287     str->allocated = length + str->length + 1;
288   buf = wzd_malloc(str->allocated);
289   wzd_strncpy(buf, head, length);
290   if (str->buffer) {
291     memcpy(buf + length, str->buffer, str->length);
292     length += str->length;
293     wzd_free(str->buffer);
294   }
295   buf[length] = '\0';
296   str->buffer = buf;
297   str->length = length;
298 
299   return str;
300 }
301 
302 
303 /** \brief remove all leading and trailing spaces from input string
304  */
str_trim(wzd_string_t * str)305 wzd_string_t * str_trim(wzd_string_t * str)
306 {
307   return str_trim_left(str_trim_right(str));
308 }
309 
str_trim_left(wzd_string_t * str)310 wzd_string_t * str_trim_left(wzd_string_t *str)
311 {
312   unsigned int i=0;
313 
314   if (!str || !str->buffer)
315     return NULL;
316 
317   while (isspace(str->buffer[i])) {
318     ++i;
319   }
320 
321   if (i!=0) {
322     unsigned int j=0;
323     for (;i!=str->length;i++)
324     {
325       str->buffer[j++] = str->buffer[i];
326     }
327     str->length = j;
328     str->buffer[j] = '\0';
329   }
330 
331   return str;
332 }
333 
str_trim_right(wzd_string_t * str)334 wzd_string_t * str_trim_right(wzd_string_t *str)
335 {
336   size_t len;
337 
338   if (!str || !str->buffer)
339     return NULL;
340 
341   if (str->length == 0) return str;
342 
343   len = str->length;
344 
345   while ((--len > 0) &&
346       (isspace(str->buffer[len]) ||
347        str->buffer[len] == '\n')) {
348     str->buffer[len] = '\0';
349     str->length--;
350   }
351   return str;
352 }
353 
354 /** \brief Removes \a len characters from a wzd_string_t, starting at position \a pos.
355  *
356  * The rest of the wzd_string_t is shifted down to fill the gap.
357  */
str_erase(wzd_string_t * str,size_t pos,int len)358 wzd_string_t * str_erase(wzd_string_t * str, size_t pos, int len)
359 {
360   if (!str || !str->buffer) return NULL;
361   if (pos > str->length) return NULL;
362 
363   if (len < 0)
364     len = str->length - pos;
365   else {
366     if (pos + len > str->length) return NULL;
367 
368     if (pos + len < str->length)
369       wzd_memmove (str->buffer + pos, str->buffer + pos + len, str->length - (pos + len));
370   }
371 
372   str->length -= len;
373 
374   str->buffer[str->length] = 0;
375 
376   return str;
377 }
378 
379 /** \brief Convert string to lower case
380  * \note
381  * This function modifies its input string
382  */
str_tolower(wzd_string_t * str)383 wzd_string_t * str_tolower(wzd_string_t *str)
384 {
385   if (str && str->buffer)
386     ascii_lower(str->buffer, str->length);
387 
388   return str;
389 }
390 
391 
392 /** \brief Extract token from string str
393  * \note
394  * This function modifies its input string
395  */
str_tok(wzd_string_t * str,const char * delim)396 wzd_string_t * str_tok(wzd_string_t *str, const char *delim)
397 {
398   wzd_string_t * token;
399   char *ptr, *t;
400   char * buffer;
401 
402 #ifdef DEBUG
403   if (!str)
404     out_log(LEVEL_HIGH,"str_tok called with NULL argument !\n");
405 #endif
406 
407   if (!str || !str->buffer || str->length == 0) return NULL;
408   if (!delim) return NULL;
409 
410   buffer = wzd_strdup(str->buffer);
411   t = strtok_r(buffer, delim, &ptr);
412 
413   token = STR(t);
414   if (t) {
415     if (ptr) {
416       str->length = strlen(ptr);
417       wzd_strncpy(str->buffer, ptr, str->length+1);
418     } else {
419       str->length = 0;
420       str->buffer[0] = '\0';
421     }
422   }
423   wzd_free(buffer);
424 
425   return token;
426 }
427 
428 /** \brief str_read next token
429  * \return a pointer to the next token, or NULL if not found, or if there is
430  * only whitespaces, or if quotes are unbalanced
431  *
432  * Read next token separated by a whitespace, except if string begins
433  * with a ' or ", in this case it searches the matching character.
434  * Note: input string is modified as a \\0 is written.
435  */
str_read_token(wzd_string_t * str)436 wzd_string_t * str_read_token(wzd_string_t *str)
437 {
438   char *tok, c;
439   char sep[2];
440   char *s;
441   char *ptr, *endptr;
442   wzd_string_t * str_ret=NULL;
443 
444   if (!str || !str->buffer || str->length == 0) return NULL;
445 
446   s = str->buffer;
447 
448   if (s == NULL)
449   {
450     return NULL;
451   }
452 
453   /* skip leading spaces */
454   while ( (c = *s) && isspace(c) ) s++;
455   if (*s == '\0') /* only whitespaces */
456   { return NULL; }
457 
458   /* search for any whitespace or quote */
459   tok = strpbrk(s, " \t\r\n\"'");
460 
461   if (!tok) {
462     str_ret = STR(str->buffer);
463     /* nothing, we return string */
464     str->length = 0;
465     str->buffer[0] = '\0';
466     return str_ret;
467   }
468 
469   /* the first char is a quote ? */
470   if (*tok == '"' || *tok == '\'') {
471     sep[0] = *tok;
472     sep[1] = '\0';
473     if (!strchr(tok+1,*tok)) { /* unbalanced quotes */
474       return NULL;
475     }
476     /** \bug we can't have escaped characters */
477     ptr = strtok_r(tok, sep, &endptr);
478     str_ret = STR(ptr);
479     str->length = strlen(str->buffer);
480     return str_ret;
481   }
482 
483   /* normal case, we search a whitespace */
484   return str_tok(str, " \t\r\n");
485 }
486 
487 
488 
489 /** \brief Produce output according to \a format and variable number of arguments,
490  * and write output to \a str.
491  */
str_vsprintf(wzd_string_t * str,const char * format,va_list ap)492 int str_vsprintf(wzd_string_t *str, const char *format, va_list ap)
493 {
494   va_list ap2;
495   int result;
496 
497   if (!str) return -1;
498   if (!format) return -1;
499 
500   if (!str->buffer)
501     _str_set_min_size(str,strlen(format)+1);
502 
503   /** vsnprintf modifies its last argument on some archs, we have to
504    * work on a copy of the va_list
505    */
506 #ifdef va_copy
507   va_copy(ap2,ap);
508 #else
509   memcpy (&ap2,&ap, sizeof(va_list));
510 #endif
511 
512   result = vsnprintf(str->buffer, str->allocated, format, ap2);
513 #ifndef WIN32
514   if (result < 0) return result;
515   if ((unsigned int)result >= str->allocated)
516   {
517     _str_set_min_size(str, result+1);
518     va_end(ap2);
519 #ifdef va_copy
520   va_copy(ap2,ap);
521 #else
522     memcpy (&ap2,&ap, sizeof(va_list));
523 #endif
524     result = vsnprintf(str->buffer, str->allocated, format, ap2);
525   }
526   str->length = result;
527 #else /* WIN32 */
528   /* windows is crap, once again
529    * vsnprintf does not return the number that should be been allocated,
530    * it always return -1 if the buffer is not large enough
531    */
532    while (result < 0)
533    {
534      if (str->allocated >= 1024000) {
535        return -1;
536      }
537      _str_set_min_size(str,str->allocated + (str->allocated >> 2) + 20);
538      va_end(ap2);
539 #ifdef va_copy
540      va_copy(ap2,ap);
541 #else
542      memcpy (&ap2,&ap, sizeof(va_list));
543 #endif
544      result = vsnprintf(str->buffer, str->allocated-1, format, ap2);
545    }
546    str->length = result;
547    str->buffer[str->length] = '\0';
548    if ((u32_t)result == str->allocated) {
549     _str_set_min_size(str, result+1);
550    }
551 #endif
552 
553   va_end (ap2);
554 
555   return result;
556 }
557 
558 
559 
560 /* str_sprintf
561  * Produce output according to format and variable number of arguments,
562  * and write output to str.
563  */
str_sprintf(wzd_string_t * str,const char * format,...)564 int str_sprintf(wzd_string_t *str, const char *format, ...)
565 {
566   va_list argptr;
567   int result;
568 
569   if (!str) return -1;
570   if (!format) return -1;
571 
572   va_start(argptr,format); /* note: ansi compatible version of va_start */
573 
574   result = str_vsprintf(str, format, argptr);
575 
576   va_end (argptr);
577 
578   return result;
579 }
580 
581 /** \brief Prepend formatted output to string
582  */
str_prepend_printf(wzd_string_t * str,const char * format,...)583 int str_prepend_printf(wzd_string_t *str, const char *format, ...)
584 {
585   va_list argptr;
586   int result;
587   char * buffer = NULL;
588   size_t length = 0;
589 
590   if (!str) return -1;
591   if (!format) return -1;
592 
593   if (!str->buffer)
594     _str_set_min_size(str,str->length + strlen(format)+1);
595 
596   va_start(argptr,format); /* note: ansi compatible version of va_start */
597 
598 #ifndef WIN32
599   result = vsnprintf(buffer, 0, format, argptr);
600   if (result < 0) return result;
601   result++;
602   if ((unsigned int)result >= length)
603   {
604     buffer = wzd_malloc( result + 1 );
605     va_end(argptr);
606     va_start(argptr,format); /* note: ansi compatible version of va_start */
607     result = vsnprintf(buffer, result, format, argptr);
608   }
609   length = result;
610 #else /* WIN32 */
611   /* windows is crap, once again
612    * vsnprintf does not return the number that should be been allocated,
613    * it always return -1 if the buffer is not large enough
614    */
615   length = strlen(format)+1;
616   buffer = wzd_malloc(length);
617   result = vsnprintf(buffer, length, format, argptr);
618   while (result < 0)
619   {
620     if (length >= 1024000) {
621       return -1;
622     }
623     wzd_free(buffer);
624     length = length + (length >> 2) + 20;
625     buffer = wzd_malloc(length);
626     va_end(argptr);
627     va_start(argptr,format); /* note: ansi compatible version of va_start */
628     result = vsnprintf(buffer, length-1, format, argptr);
629   }
630   length = result;
631   buffer[length] = '\0';
632   if ((u32_t)result == length) {
633    _str_set_min_size(str, result+1);
634   }
635 #endif
636 
637   va_end (argptr);
638 
639   str_prepend(str, buffer);
640   if (buffer) wzd_free(buffer);
641 
642   return str->length;
643 }
644 
645 
646 /** \brief Append formatted output to string
647  */
str_append_printf(wzd_string_t * str,const char * format,...)648 int str_append_printf(wzd_string_t *str, const char *format, ...)
649 {
650   va_list argptr;
651   int result;
652   char * buffer = NULL;
653   size_t length = 0;
654 
655   if (!str) return -1;
656   if (!format) return -1;
657 
658   if (!str->buffer)
659     _str_set_min_size(str,str->length + strlen(format)+1);
660 
661   va_start(argptr,format); /* note: ansi compatible version of va_start */
662 
663 #ifndef WIN32
664   result = vsnprintf(buffer, 0, format, argptr);
665   if (result < 0) return result;
666   result++;
667   if ((unsigned int)result >= length)
668   {
669     buffer = wzd_malloc( result + 1 );
670     va_end(argptr);
671     va_start(argptr,format); /* note: ansi compatible version of va_start */
672     result = vsnprintf(buffer, result, format, argptr);
673   }
674   length = result;
675 #else /* WIN32 */
676   /* windows is crap, once again
677    * vsnprintf does not return the number that should be been allocated,
678    * it always return -1 if the buffer is not large enough
679    */
680   length = strlen(format)+1;
681   buffer = wzd_malloc(length);
682   result = vsnprintf(buffer, length, format, argptr);
683   while (result < 0)
684   {
685     if (length >= 1024000) {
686       return -1;
687     }
688     wzd_free(buffer);
689     length = length + (length >> 2) + 20;
690     buffer = wzd_malloc(length);
691     va_end(argptr);
692     va_start(argptr,format); /* note: ansi compatible version of va_start */
693     result = vsnprintf(buffer, length-1, format, argptr);
694   }
695   length = result;
696   buffer[length] = '\0';
697   if ((u32_t)result == length) {
698    _str_set_min_size(str, result+1);
699   }
700 #endif
701 
702   va_end (argptr);
703 
704   str_append(str, buffer);
705   if (buffer) wzd_free(buffer);
706 
707   return str->length;
708 }
709 
710 /** \brief Split \a str into a maximum of \a max_tokens pieces, separated by \a sep.
711  *
712  * If \a max_tokens is reached, the remainder of \a str is appended to the last token.
713  *
714  * \return a NULL-terminated string array, or NULL. The array must be freed using
715  * str_deallocate_array().
716  */
str_split(wzd_string_t * str,const char * sep,int max_tokens)717 wzd_string_t ** str_split(wzd_string_t * str, const char * sep, int max_tokens)
718 {
719   List string_list;
720   ListElmt * elmnt;
721   const char *remainder = NULL;
722   char * s;
723   wzd_string_t * token;
724   wzd_string_t ** str_array;
725   unsigned int i;
726 
727   if (!str || !sep || sep[0]=='\0') return NULL;
728 
729   if (max_tokens < 1) max_tokens = (unsigned int)-1;
730 
731   list_init(&string_list,NULL);
732 
733   remainder = str->buffer;
734   s = strstr(remainder, sep);
735   if (s) {
736     size_t len;
737     size_t delimiter_len = strlen(sep);
738 
739     while (--max_tokens && s) {
740       len = s - remainder;
741       token = str_allocate();
742       _str_set_min_size(token, len + 1);
743       strncpy(token->buffer, remainder, len);
744       token->buffer[len] = '\0';
745       token->length = len;
746 
747       list_ins_next(&string_list, list_tail(&string_list), token);
748 
749       remainder = s + delimiter_len;
750 
751       s = strstr(remainder, sep);
752     }
753   }
754 
755   if (remainder && remainder[0] != '\0')
756     list_ins_next(&string_list, list_tail(&string_list), STR(remainder));
757 
758   str_array = wzd_malloc( (list_size(&string_list)+1) * sizeof(wzd_string_t*) );
759   i = 0;
760   for (elmnt = list_head(&string_list); elmnt; elmnt = list_next(elmnt)) {
761     str_array[i++] = list_data(elmnt);
762   }
763   str_array[i] = NULL;
764 
765   list_destroy(&string_list);
766 
767   return str_array;
768 }
769 
770 #ifdef HAVE_UTF8
771 /** \brief Convert utf8 string to other charset
772  * \note
773  * Require unicode support
774  */
str_utf8_to_local(wzd_string_t * str,const char * charset)775 int str_utf8_to_local(wzd_string_t *str, const char * charset)
776 {
777   char * utf_buf;
778   size_t length;
779 
780   if (!utf8_valid(str->buffer, str->length)) {
781     return -1;
782   }
783 
784   length = strlen(str->buffer) + 10; /* we allocate more, small security */
785   utf_buf = wzd_malloc(length);
786 
787   if (utf8_to_local_charset(str->buffer, utf_buf, length, charset))
788   {
789     /* error during conversion */
790     wzd_free(utf_buf);
791     return -1;
792   }
793 
794   wzd_free(str->buffer);
795   str->buffer = utf_buf;
796   str->allocated = length;
797   str->length = strlen(utf_buf);
798 
799   return 0;
800 }
801 
802 /** \brief Convert charset to utf8 string
803  * \note
804  * Require unicode support
805  */
str_local_to_utf8(wzd_string_t * str,const char * charset)806 int str_local_to_utf8(wzd_string_t *str, const char * charset)
807 {
808   char * utf_buf;
809   size_t length;
810 
811   /** \bug testing if a strin to be converted to UTF-8 is already
812   valid UTF-8 is a bit stupid */
813 /*  if (!utf8_valid(str->buffer, str->length)) {
814     return -1;
815   } */
816 
817   length = strlen(str->buffer) + 10; /* we allocate more, small security */
818   utf_buf = wzd_malloc(length);
819 
820   if (local_charset_to_utf8(str->buffer, utf_buf, length, charset))
821   {
822     /* error during conversion */
823     wzd_free(utf_buf);
824     return -1;
825   }
826 
827   wzd_free(str->buffer);
828   str->buffer = utf_buf;
829   str->allocated = length;
830   str->length = strlen(utf_buf);
831 
832   return 0;
833 }
834 
835 /** \brief test if string is valid utf8
836  * \note
837  * require unicode support
838  */
str_is_valid_utf8(wzd_string_t * str)839 int str_is_valid_utf8(wzd_string_t *str)
840 {
841   return utf8_valid(str->buffer,str->length);
842 }
843 
844 #else
str_utf8_to_local(wzd_string_t * str,const char * charset)845 int str_utf8_to_local(wzd_string_t *str, const char * charset)
846 {
847   return -1;
848 }
849 
str_local_to_utf8(wzd_string_t * str,const char * charset)850 int str_local_to_utf8(wzd_string_t *str, const char * charset)
851 {
852   return -1;
853 }
854 
str_is_valid_utf8(wzd_string_t * str)855 int str_is_valid_utf8(wzd_string_t *str)
856 {
857   return -1;
858 }
859 
860 #endif /* HAVE_UTF8 */
861 
862 
863 
864 
865 
_str_set_min_size(wzd_string_t * str,size_t length)866 static inline void _str_set_min_size(wzd_string_t *str, size_t length)
867 {
868   void * ptr;
869 
870   if (str) {
871     if (length > str->allocated) {
872       /* allocate a bit more than requested */
873       if (length < 200) length += 20;
874       else length = (size_t)(length * 1.3);
875 
876       if (!str->buffer) {
877         str->buffer = wzd_malloc(length);
878         str->buffer[0] = '\0';
879       } else {
880         if ( (ptr = wzd_realloc(str->buffer,length)) ) {
881           str->buffer = ptr;
882           str->buffer[str->length] = '\0';
883         } else {
884           ptr = wzd_malloc(length);
885           memcpy(ptr,str->buffer,str->length);
886           wzd_free(str->buffer);
887           str->buffer = ptr;
888         }
889       }
890       str->allocated = length;
891     }
892   }
893 }
894 
895