Update head to match stable 1.0
[gnupg.git] / util / strgutil.c
1 /* strgutil.c -  string utilities
2  * Copyright (C) 1994, 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include "types.h"
26 #include "util.h"
27 #include "memory.h"
28
29
30 static ushort koi8_unicode[128] = {
31     0x2500,0x2502,0x250c,0x2510,0x2514,0x2518,0x251c,0x2524,
32     0x252c,0x2534,0x253c,0x2580,0x2584,0x2588,0x258c,0x2590,
33     0x2591,0x2592,0x2593,0x2320,0x25a0,0x2219,0x221a,0x2248,
34     0x2264,0x2265,0x00a0,0x2321,0x00b0,0x00b2,0x00b7,0x00f7,
35     0x2550,0x2551,0x2552,0x0451,0x2553,0x2554,0x2555,0x2556,
36     0x2557,0x2558,0x2559,0x255a,0x255b,0x255c,0x255d,0x255e,
37     0x255f,0x2560,0x2561,0x0401,0x2562,0x2563,0x2564,0x2565,
38     0x2566,0x2567,0x2568,0x2569,0x256a,0x256b,0x256c,0x00a9,
39     0x044e,0x0430,0x0431,0x0446,0x0434,0x0435,0x0444,0x0433,
40     0x0445,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,
41     0x043f,0x044f,0x0440,0x0441,0x0442,0x0443,0x0436,0x0432,
42     0x044c,0x044b,0x0437,0x0448,0x044d,0x0449,0x0447,0x044a,
43     0x042e,0x0410,0x0411,0x0426,0x0414,0x0415,0x0424,0x0413,
44     0x0425,0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,
45     0x041f,0x042f,0x0420,0x0421,0x0422,0x0423,0x0416,0x0412,
46     0x042c,0x042b,0x0417,0x0428,0x042d,0x0429,0x0427,0x042a
47 };
48
49 static ushort latin2_unicode[128] = {
50     0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
51     0x0088,0x0089,0x008A,0x008B,0x008C,0x008D,0x008E,0x008F,
52     0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
53     0x0098,0x0099,0x009A,0x009B,0x009C,0x009D,0x009E,0x009F,
54     0x00A0,0x0104,0x02D8,0x0141,0x00A4,0x013D,0x015A,0x00A7,
55     0x00A8,0x0160,0x015E,0x0164,0x0179,0x00AD,0x017D,0x017B,
56     0x00B0,0x0105,0x02DB,0x0142,0x00B4,0x013E,0x015B,0x02C7,
57     0x00B8,0x0161,0x015F,0x0165,0x017A,0x02DD,0x017E,0x017C,
58     0x0154,0x00C1,0x00C2,0x0102,0x00C4,0x0139,0x0106,0x00C7,
59     0x010C,0x00C9,0x0118,0x00CB,0x011A,0x00CD,0x00CE,0x010E,
60     0x0110,0x0143,0x0147,0x00D3,0x00D4,0x0150,0x00D6,0x00D7,
61     0x0158,0x016E,0x00DA,0x0170,0x00DC,0x00DD,0x0162,0x00DF,
62     0x0155,0x00E1,0x00E2,0x0103,0x00E4,0x013A,0x0107,0x00E7,
63     0x010D,0x00E9,0x0119,0x00EB,0x011B,0x00ED,0x00EE,0x010F,
64     0x0111,0x0144,0x0148,0x00F3,0x00F4,0x0151,0x00F6,0x00F7,
65     0x0159,0x016F,0x00FA,0x0171,0x00FC,0x00FD,0x0163,0x02D9
66 };
67
68
69 static const char *active_charset_name = "iso-8859-1";
70 static ushort *active_charset = NULL;
71 static int no_translation = 0;
72
73 void
74 free_strlist( STRLIST sl )
75 {
76     STRLIST sl2;
77
78     for(; sl; sl = sl2 ) {
79         sl2 = sl->next;
80         m_free(sl);
81     }
82 }
83
84
85 STRLIST
86 add_to_strlist( STRLIST *list, const char *string )
87 {
88     STRLIST sl;
89
90     sl = m_alloc( sizeof *sl + strlen(string));
91     sl->flags = 0;
92     strcpy(sl->d, string);
93     sl->next = *list;
94     *list = sl;
95     return sl;
96 }
97
98 /****************
99  * ame as add_to_strlist() but if is_utf8 is *not* set a conversion
100  * to UTF8 is done
101  */
102 STRLIST
103 add_to_strlist2( STRLIST *list, const char *string, int is_utf8 )
104 {
105     STRLIST sl;
106
107     if( is_utf8 )
108         sl = add_to_strlist( list, string );
109     else {
110         char *p = native_to_utf8( string );
111         sl = add_to_strlist( list, p );
112         m_free( p );
113     }
114     return sl;
115 }
116
117 STRLIST
118 append_to_strlist( STRLIST *list, const char *string )
119 {
120     STRLIST r, sl;
121
122     sl = m_alloc( sizeof *sl + strlen(string));
123     sl->flags = 0;
124     strcpy(sl->d, string);
125     sl->next = NULL;
126     if( !*list )
127         *list = sl;
128     else {
129         for( r = *list; r->next; r = r->next )
130             ;
131         r->next = sl;
132     }
133     return sl;
134 }
135
136 STRLIST
137 append_to_strlist2( STRLIST *list, const char *string, int is_utf8 )
138 {
139     STRLIST sl;
140
141     if( is_utf8 )
142         sl = append_to_strlist( list, string );
143     else {
144         char *p = native_to_utf8( string );
145         sl = append_to_strlist( list, p );
146         m_free( p );
147     }
148     return sl;
149 }
150
151
152 STRLIST
153 strlist_prev( STRLIST head, STRLIST node )
154 {
155     STRLIST n;
156
157     for(n=NULL; head && head != node; head = head->next )
158         n = head;
159     return n;
160 }
161
162 STRLIST
163 strlist_last( STRLIST node )
164 {
165     if( node )
166         for( ; node->next ; node = node->next )
167             ;
168     return node;
169 }
170
171 char *
172 pop_strlist( STRLIST *list )
173 {
174   char *str=NULL;
175   STRLIST sl=*list;
176
177   if(sl)
178     {
179       str=m_alloc(strlen(sl->d)+1);
180       strcpy(str,sl->d);
181
182       *list=sl->next;
183       m_free(sl);
184     }
185
186   return str;
187 }
188
189 /****************
190  * look for the substring SUB in buffer and return a pointer to that
191  * substring in BUF or NULL if not found.
192  * Comparison is case-insensitive.
193  */
194 const char *
195 memistr( const char *buf, size_t buflen, const char *sub )
196 {
197     const byte *t, *s ;
198     size_t n;
199
200     for( t=buf, n=buflen, s=sub ; n ; t++, n-- )
201         if( toupper(*t) == toupper(*s) ) {
202             for( buf=t++, buflen = n--, s++;
203                  n && toupper(*t) == toupper(*s); t++, s++, n-- )
204                 ;
205             if( !*s )
206                 return buf;
207             t = buf; n = buflen; s = sub ;
208         }
209
210     return NULL ;
211 }
212
213 const char *
214 ascii_memistr( const char *buf, size_t buflen, const char *sub )
215 {
216     const byte *t, *s ;
217     size_t n;
218
219     for( t=buf, n=buflen, s=sub ; n ; t++, n-- )
220         if( ascii_toupper(*t) == ascii_toupper(*s) ) {
221             for( buf=t++, buflen = n--, s++;
222                  n && ascii_toupper(*t) == ascii_toupper(*s); t++, s++, n-- )
223                 ;
224             if( !*s )
225                 return buf;
226             t = buf; n = buflen; s = sub ;
227         }
228
229     return NULL ;
230 }
231
232 /****************
233  * Wie strncpy(), aber es werden maximal n-1 zeichen kopiert und ein
234  * '\0' angehängt. Ist n = 0, so geschieht nichts, ist Destination
235  * gleich NULL, so wird via m_alloc Speicher besorgt, ist dann nicht
236  * genügend Speicher vorhanden, so bricht die funktion ab.
237  */
238 char *
239 mem2str( char *dest , const void *src , size_t n )
240 {
241     char *d;
242     const char *s;
243
244     if( n ) {
245         if( !dest )
246             dest = m_alloc( n ) ;
247         d = dest;
248         s = src ;
249         for(n--; n && *s; n-- )
250             *d++ = *s++;
251         *d = '\0' ;
252     }
253
254     return dest ;
255 }
256
257
258 /****************
259  * remove leading and trailing white spaces
260  */
261 char *
262 trim_spaces( char *str )
263 {
264     char *string, *p, *mark;
265
266     string = str;
267     /* find first non space character */
268     for( p=string; *p && isspace( *(byte*)p ) ; p++ )
269         ;
270     /* move characters */
271     for( (mark = NULL); (*string = *p); string++, p++ )
272         if( isspace( *(byte*)p ) ) {
273             if( !mark )
274                 mark = string ;
275         }
276         else
277             mark = NULL ;
278     if( mark )
279         *mark = '\0' ;  /* remove trailing spaces */
280
281     return str ;
282 }
283
284
285
286 unsigned int
287 trim_trailing_chars( byte *line, unsigned len, const char *trimchars )
288 {
289     byte *p, *mark;
290     unsigned n;
291
292     for(mark=NULL, p=line, n=0; n < len; n++, p++ ) {
293         if( strchr(trimchars, *p ) ) {
294             if( !mark )
295                 mark = p;
296         }
297         else
298             mark = NULL;
299     }
300
301     if( mark ) {
302         *mark = 0;
303         return mark - line;
304     }
305     return len;
306 }
307
308 /****************
309  * remove trailing white spaces and return the length of the buffer
310  */
311 unsigned
312 trim_trailing_ws( byte *line, unsigned len )
313 {
314     return trim_trailing_chars( line, len, " \t\r\n" );
315 }
316
317 unsigned int
318 check_trailing_chars( const byte *line, unsigned int len,
319                       const char *trimchars )
320 {
321     const byte *p, *mark;
322     unsigned int n;
323
324     for(mark=NULL, p=line, n=0; n < len; n++, p++ ) {
325         if( strchr(trimchars, *p ) ) {
326             if( !mark )
327                 mark = p;
328         }
329         else
330             mark = NULL;
331     }
332
333     if( mark ) {
334         return mark - line;
335     }
336     return len;
337 }
338
339 /****************
340  * remove trailing white spaces and return the length of the buffer
341  */
342 unsigned int
343 check_trailing_ws( const byte *line, unsigned int len )
344 {
345     return check_trailing_chars( line, len, " \t\r\n" );
346 }
347
348
349
350 int
351 string_count_chr( const char *string, int c )
352 {
353     int count;
354     for(count=0; *string; string++ )
355         if( *string == c )
356             count++;
357     return count;
358 }
359
360
361 int
362 set_native_charset( const char *newset )
363 {
364     if( !ascii_strcasecmp( newset, "iso-8859-1" ) ) {
365         active_charset_name = "iso-8859-1";
366         no_translation = 0;
367         active_charset = NULL;
368     }
369     else if( !ascii_strcasecmp( newset, "iso-8859-2" ) ) {
370         active_charset_name = "iso-8859-2";
371         no_translation = 0;
372         active_charset = latin2_unicode;
373     }
374     else if( !ascii_strcasecmp( newset, "koi8-r" ) ) {
375         active_charset_name = "koi8-r";
376         no_translation = 0;
377         active_charset = koi8_unicode;
378     }
379     else if( !ascii_strcasecmp (newset, "utf8" )
380              || !ascii_strcasecmp(newset, "utf-8") ) {
381         active_charset_name = "utf-8";
382         no_translation = 1;
383         active_charset = NULL;
384     }
385     else
386         return G10ERR_GENERAL;
387     return 0;
388 }
389
390 const char*
391 get_native_charset()
392 {
393     return active_charset_name;
394 }
395
396 /****************
397  * Convert string, which is in native encoding to UTF8 and return the
398  * new allocated UTF8 string.
399  */
400 char *
401 native_to_utf8( const char *string )
402 {
403     const byte *s;
404     char *buffer;
405     byte *p;
406     size_t length=0;
407
408     if (no_translation) {
409         buffer = m_strdup (string);
410     }
411     else if( active_charset ) {
412         for(s=string; *s; s++ ) {
413             length++;
414             if( *s & 0x80 )
415                 length += 2; /* we may need 3 bytes */
416         }
417         buffer = m_alloc( length + 1 );
418         for(p=buffer, s=string; *s; s++ ) {
419             if( *s & 0x80 ) {
420                 ushort val = active_charset[ *s & 0x7f ];
421                 if( val < 0x0800 ) {
422                     *p++ = 0xc0 | ( (val >> 6) & 0x1f );
423                     *p++ = 0x80 | (  val & 0x3f );
424                 }
425                 else {
426                     *p++ = 0xe0 | ( (val >> 12) & 0x0f );
427                     *p++ = 0x80 | ( (val >>  6) & 0x3f );
428                     *p++ = 0x80 | (  val & 0x3f );
429                 }
430             }
431             else
432                 *p++ = *s;
433         }
434         *p = 0;
435     }
436     else {
437         for(s=string; *s; s++ ) {
438             length++;
439             if( *s & 0x80 )
440                 length++;
441         }
442         buffer = m_alloc( length + 1 );
443         for(p=buffer, s=string; *s; s++ ) {
444             if( *s & 0x80 ) {
445                 *p++ = 0xc0 | ((*s >> 6) & 3);
446                 *p++ = 0x80 | ( *s & 0x3f );
447             }
448             else
449                 *p++ = *s;
450         }
451         *p = 0;
452     }
453     return buffer;
454 }
455
456
457 /****************
458  * Convert string, which is in UTF8 to native encoding.  illegal
459  * encodings by some "\xnn" and quote all control characters. A
460  * character with value DELIM will always be quoted, it must be a
461  * vanilla ASCII character.  
462   */
463 char *
464 utf8_to_native( const char *string, size_t length, int delim )
465 {
466     int nleft;
467     int i;
468     byte encbuf[8];
469     int encidx;
470     const byte *s;
471     size_t n;
472     byte *buffer = NULL, *p = NULL;
473     unsigned long val = 0;
474     size_t slen;
475     int resync = 0;
476
477     /* 1. pass (p==NULL): count the extended utf-8 characters */
478     /* 2. pass (p!=NULL): create string */
479     for( ;; ) {
480         for( slen=length, nleft=encidx=0, n=0, s=string; slen; s++, slen-- ) {
481             if( resync ) {
482                 if( !(*s < 128 || (*s >= 0xc0 && *s <= 0xfd)) ) {
483                     /* still invalid */
484                     if( p ) {
485                         sprintf(p, "\\x%02x", *s );
486                         p += 4;
487                     }
488                     n += 4;
489                     continue;
490                 }
491                 resync = 0;
492             }
493             if( !nleft ) {
494                 if( !(*s & 0x80) ) { /* plain ascii */
495                     if( *s < 0x20 || *s == 0x7f || *s == delim ||
496                         (delim && *s=='\\')) {
497                         n++;
498                         if( p )
499                             *p++ = '\\';
500                         switch( *s ) {
501                           case '\n': n++; if( p ) *p++ = 'n'; break;
502                           case '\r': n++; if( p ) *p++ = 'r'; break;
503                           case '\f': n++; if( p ) *p++ = 'f'; break;
504                           case '\v': n++; if( p ) *p++ = 'v'; break;
505                           case '\b': n++; if( p ) *p++ = 'b'; break;
506                           case   0 : n++; if( p ) *p++ = '0'; break;
507                           default:
508                             n += 3;
509                             if ( p ) {
510                                 sprintf( p, "x%02x", *s );
511                                 p += 3;
512                             }
513                             break;
514                         }
515                     }
516                     else {
517                         if( p ) *p++ = *s;
518                         n++;
519                     }
520                 }
521                 else if( (*s & 0xe0) == 0xc0 ) { /* 110x xxxx */
522                     val = *s & 0x1f;
523                     nleft = 1;
524                     encidx = 0;
525                     encbuf[encidx++] = *s;
526                 }
527                 else if( (*s & 0xf0) == 0xe0 ) { /* 1110 xxxx */
528                     val = *s & 0x0f;
529                     nleft = 2;
530                     encidx = 0;
531                     encbuf[encidx++] = *s;
532                 }
533                 else if( (*s & 0xf8) == 0xf0 ) { /* 1111 0xxx */
534                     val = *s & 0x07;
535                     nleft = 3;
536                     encidx = 0;
537                     encbuf[encidx++] = *s;
538                 }
539                 else if( (*s & 0xfc) == 0xf8 ) { /* 1111 10xx */
540                     val = *s & 0x03;
541                     nleft = 4;
542                     encidx = 0;
543                     encbuf[encidx++] = *s;
544                 }
545                 else if( (*s & 0xfe) == 0xfc ) { /* 1111 110x */
546                     val = *s & 0x01;
547                     nleft = 5;
548                     encidx = 0;
549                     encbuf[encidx++] = *s;
550                 }
551                 else {  /* invalid encoding: print as \xnn */
552                     if( p ) {
553                         sprintf(p, "\\x%02x", *s );
554                         p += 4;
555                     }
556                     n += 4;
557                     resync = 1;
558                 }
559             }
560             else if( *s < 0x80 || *s >= 0xc0 ) { /* invalid */
561                 if( p ) {
562                     for(i=0; i < encidx; i++ ) {
563                         sprintf(p, "\\x%02x", encbuf[i] );
564                         p += 4;
565                     }
566                     sprintf(p, "\\x%02x", *s );
567                     p += 4;
568                 }
569                 n += 4 + 4*encidx;
570                 nleft = 0;
571                 encidx = 0;
572                 resync = 1;
573             }
574             else {
575                 encbuf[encidx++] = *s;
576                 val <<= 6;
577                 val |= *s & 0x3f;
578                 if( !--nleft ) { /* ready */
579                     if (no_translation) {
580                         if( p ) {
581                             for(i=0; i < encidx; i++ )
582                                 *p++ = encbuf[i];
583                         }
584                         n += encidx;
585                         encidx = 0;
586                     }
587                     else if( active_charset ) { /* table lookup */
588                         for(i=0; i < 128; i++ ) {
589                             if( active_charset[i] == val )
590                                 break;
591                         }
592                         if( i < 128 ) { /* we can print this one */
593                             if( p ) *p++ = i+128;
594                             n++;
595                         }
596                         else { /* we do not have a translation: print utf8 */
597                             if( p ) {
598                                 for(i=0; i < encidx; i++ ) {
599                                     sprintf(p, "\\x%02x", encbuf[i] );
600                                     p += 4;
601                                 }
602                             }
603                             n += encidx*4;
604                             encidx = 0;
605                         }
606                     }
607                     else { /* native set */
608                         if( val >= 0x80 && val < 256 ) {
609                             n++;    /* we can simply print this character */
610                             if( p ) *p++ = val;
611                         }
612                         else { /* we do not have a translation: print utf8 */
613                             if( p ) {
614                                 for(i=0; i < encidx; i++ ) {
615                                     sprintf(p, "\\x%02x", encbuf[i] );
616                                     p += 4;
617                                 }
618                             }
619                             n += encidx*4;
620                             encidx = 0;
621                         }
622                     }
623                 }
624
625             }
626         }
627         if( !buffer ) { /* allocate the buffer after the first pass */
628             buffer = p = m_alloc( n + 1 );
629         }
630         else {
631             *p = 0; /* make a string */
632             return buffer;
633         }
634     }
635 }
636
637 /****************************************************
638  ******** locale insensitive ctype functions ********
639  ****************************************************/
640 /* FIXME: replace them by a table lookup and macros */
641 int
642 ascii_isupper (int c)
643 {
644     return c >= 'A' && c <= 'Z';
645 }
646
647 int
648 ascii_islower (int c)
649 {
650     return c >= 'a' && c <= 'z';
651 }
652
653 int 
654 ascii_toupper (int c)
655 {
656     if (c >= 'a' && c <= 'z')
657         c &= ~0x20;
658     return c;
659 }
660
661 int 
662 ascii_tolower (int c)
663 {
664     if (c >= 'A' && c <= 'Z')
665         c |= 0x20;
666     return c;
667 }
668
669
670 int
671 ascii_strcasecmp( const char *a, const char *b )
672 {
673     if (a == b)
674         return 0;
675
676     for (; *a && *b; a++, b++) {
677         if (*a != *b && ascii_toupper(*a) != ascii_toupper(*b))
678             break;
679     }
680     return *a == *b? 0 : (ascii_toupper (*a) - ascii_toupper (*b));
681 }
682
683 int
684 ascii_memcasecmp( const char *a, const char *b, size_t n )
685 {
686     if (a == b)
687         return 0;
688     for ( ; n; n--, a++, b++ ) {
689         if( *a != *b  && ascii_toupper (*a) != ascii_toupper (*b) )
690             return *a == *b? 0 : (ascii_toupper (*a) - ascii_toupper (*b));
691     }
692     return 0;
693 }
694
695
696 /*********************************************
697  ********** missing string functions *********
698  *********************************************/
699
700 #ifndef HAVE_STPCPY
701 char *
702 stpcpy(char *a,const char *b)
703 {
704     while( *b )
705         *a++ = *b++;
706     *a = 0;
707
708     return (char*)a;
709 }
710 #endif
711
712
713 #ifndef HAVE_STRSEP
714 /* code taken from glibc-2.2.1/sysdeps/generic/strsep.c */
715 char *
716 strsep (char **stringp, const char *delim)
717 {
718   char *begin, *end;
719
720   begin = *stringp;
721   if (begin == NULL)
722     return NULL;
723
724   /* A frequent case is when the delimiter string contains only one
725      character.  Here we don't need to call the expensive `strpbrk'
726      function and instead work using `strchr'.  */
727   if (delim[0] == '\0' || delim[1] == '\0')
728     {
729       char ch = delim[0];
730
731       if (ch == '\0')
732         end = NULL;
733       else
734         {
735           if (*begin == ch)
736             end = begin;
737           else if (*begin == '\0')
738             end = NULL;
739           else
740             end = strchr (begin + 1, ch);
741         }
742     }
743   else
744     /* Find the end of the token.  */
745     end = strpbrk (begin, delim);
746
747   if (end)
748     {
749       /* Terminate the token and set *STRINGP past NUL character.  */
750       *end++ = '\0';
751       *stringp = end;
752     }
753   else
754     /* No more delimiters; this is the last token.  */
755     *stringp = NULL;
756
757   return begin;
758 }
759 #endif /*HAVE_STRSEP*/
760
761
762 #ifndef HAVE_STRLWR
763 char *
764 strlwr(char *s)
765 {
766     char *p;
767     for(p=s; *p; p++ )
768         *p = tolower(*p);
769     return s;
770 }
771 #endif
772
773 #ifndef HAVE_STRCASECMP
774 int
775 strcasecmp( const char *a, const char *b )
776 {
777     for( ; *a && *b; a++, b++ ) {
778         if( *a != *b && toupper(*a) != toupper(*b) )
779             break;
780     }
781     return *(const byte*)a - *(const byte*)b;
782 }
783 #endif
784
785 #ifndef HAVE_STRNCASECMP
786 int
787 strncasecmp( const char *a, const char *b, size_t n )
788 {
789     for( ; n && *a && *b; a++, b++, n--) {
790         if( *a != *b && toupper(*a) != toupper(*b) )
791             break;
792     }
793     if (!n)
794       return 0;
795     return *(const byte*)a - *(const byte*)b;
796 }
797 #endif
798
799
800 #ifdef __MINGW32__
801 /* 
802  * Like vsprintf but provides a pointer to malloc'd storage, which
803  * must be freed by the caller (m_free).  Taken from libiberty as
804  * found in gcc-2.95.2 and a little bit modernized.
805  * FIXME: Write a new CRT for W32.
806  */
807 int
808 vasprintf ( char **result, const char *format, va_list args)
809 {
810   const char *p = format;
811   /* Add one to make sure that it is never zero, which might cause malloc
812      to return NULL.  */
813   int total_width = strlen (format) + 1;
814   va_list ap;
815
816   /* this is not really portable but works under Windows */
817   memcpy ( &ap, &args, sizeof (va_list));
818
819   while (*p != '\0')
820     {
821       if (*p++ == '%')
822         {
823           while (strchr ("-+ #0", *p))
824             ++p;
825           if (*p == '*')
826             {
827               ++p;
828               total_width += abs (va_arg (ap, int));
829             }
830           else
831             {
832               char *endp;  
833               total_width += strtoul (p, &endp, 10);
834               p = endp;
835             }
836           if (*p == '.')
837             {
838               ++p;
839               if (*p == '*')
840                 {
841                   ++p;
842                   total_width += abs (va_arg (ap, int));
843                 }
844               else
845                 {
846                   char *endp;
847                   total_width += strtoul (p, &endp, 10);
848                   p = endp;
849                 }
850             }
851           while (strchr ("hlL", *p))
852             ++p;
853           /* Should be big enough for any format specifier except %s
854              and floats.  */
855           total_width += 30;
856           switch (*p)
857             {
858             case 'd':
859             case 'i':
860             case 'o':
861             case 'u':
862             case 'x':
863             case 'X':
864             case 'c':
865               (void) va_arg (ap, int);
866               break;
867             case 'f':
868             case 'e':
869             case 'E':
870             case 'g':
871             case 'G':
872               (void) va_arg (ap, double);
873               /* Since an ieee double can have an exponent of 307, we'll
874                  make the buffer wide enough to cover the gross case. */
875               total_width += 307;
876             
877             case 's':
878               total_width += strlen (va_arg (ap, char *));
879               break;
880             case 'p':
881             case 'n':
882               (void) va_arg (ap, char *);
883               break;
884             }
885         }
886     }
887   *result = m_alloc (total_width);
888   if (*result != NULL)
889     return vsprintf (*result, format, args);
890   else
891     return 0;
892 }
893
894 #endif /*__MINGW32__*/
895