]> git.deb.at Git - pkg/abook.git/blob - misc.c
Fixed snprintf related bugs
[pkg/abook.git] / misc.c
1
2 /*
3  * $Id$
4  *
5  * by JH <jheinonen@bigfoot.com>
6  *
7  * Copyright (C) Jaakko Heinonen
8  */
9
10 #define ABOOK_SRC       1
11 /*#undef ABOOK_SRC*/
12
13 #include <stdio.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <ctype.h>
17 #include <unistd.h>
18 #include <errno.h>
19 #include "misc.h"
20 #ifdef ABOOK_SRC
21 #       include "abook.h"
22 #endif
23
24 #ifndef DEBUG
25 #       define NDEBUG   1
26 #else
27 #       undef NDEBUG
28 #endif
29
30 #include <assert.h>
31
32 char *
33 revstr(char *str)
34 {
35         char *s, *s2;
36
37         s = s2 = strdup(str);
38
39         while( *str )
40                 str++;
41
42         while( *s )
43                 *--str = *s++;
44
45         free(s2);
46         return str;
47 }
48
49 char *
50 strupper(char *str)
51 {
52         char *tmp = str;
53
54         while( ( *str = toupper( *str ) ) )
55                 str++;
56         
57         return tmp;
58 }
59
60 char *
61 strlower(char *str)
62 {
63         char *tmp = str;
64
65         while( ( *str = tolower ( *str ) ) )
66                 str++;
67
68         return tmp;
69 }
70
71 char *
72 strtrim(char *s)
73 {
74         char *t, *tt;
75
76         assert(s != NULL);
77
78         for(t = s; ISSPACE(*t); t++);
79
80         memmove(s, t, strlen(t)+1);
81
82         for (tt = t = s; *t != '\0'; t++)
83                 if(!ISSPACE(*t))
84                         tt = t+1;
85
86         *tt = '\0';
87
88         return s;
89 }
90
91
92 #ifdef HAVE_CONFIG_H
93 #       include "config.h"
94 #endif
95
96 /* varargs declarations: */
97
98 #ifdef HAVE_STDARG_H
99 #       define MY_VA_LOCAL_DECL   va_list ap
100 #       define MY_VA_START(f)     va_start(ap, f)
101 #       define MY_VA_SHIFT(v,t)   v = va_arg(ap, t)
102 #       define MY_VA_END          va_end(ap)
103 #else
104 #       error HAVE_STDARG_H not defined
105 #endif
106
107 char *
108 mkstr (const char *format, ... )
109 {
110         MY_VA_LOCAL_DECL;
111         int size = 100;
112         char *buffer =
113 #ifdef ABOOK_SRC
114                 (char *) abook_malloc (size);
115 #else
116                 (char *) malloc (size);
117 #endif
118         
119         for(;;) {
120                 int n;
121                 MY_VA_START(format);
122                 n = vsnprintf (buffer, size,
123                                 format, ap);
124                 MY_VA_END;
125
126                 if (n > -1 && n < size)
127                         return buffer;
128
129                 if (n > -1)
130                         size = n + 1;
131                 else
132                         size *= 2;
133                 
134                 buffer =
135 #ifdef ABOOK_SRC
136                         (char *) abook_realloc (buffer, size);
137 #else
138                         (char *) realloc (buffer, size);
139 #endif
140         }
141 }
142
143
144 char*
145 strconcat (const char *str, ...)
146 {
147         int   l;
148         MY_VA_LOCAL_DECL;
149         char *s, *concat;
150
151         if(str == NULL)
152                 return NULL;
153
154         l = 1 + strlen (str);
155         MY_VA_START(str);
156         MY_VA_SHIFT(s, char*);
157         while (s) {
158                 l += strlen (s);
159                 MY_VA_SHIFT(s, char*);
160         }
161         MY_VA_END;
162         
163         concat = (char *)
164 #ifdef ABOOK_SRC
165         abook_malloc(l);
166 #else
167         malloc(l);
168 #endif
169
170         strcpy (concat, str);
171         MY_VA_START(str);
172         MY_VA_SHIFT(s, char*);
173         while (s) {
174                 strcat (concat, s);
175                 MY_VA_SHIFT(s, char*);
176         }
177         MY_VA_END;
178
179         return concat;
180 }
181
182
183 int
184 safe_strcmp(const char *s1, const char * s2)
185 {
186         if (s1 == NULL && s2 == NULL) return 0;
187         if (s1 == NULL) return -1;
188         if (s2 == NULL) return 1;
189
190         return strcmp(s1, s2);
191 }
192
193 char *
194 my_getcwd()
195 {
196         char *dir = NULL;
197         int size = 100;
198
199         dir = malloc(size);
200         
201         while( getcwd(dir, size) == NULL && errno == ERANGE )
202                 dir = realloc(dir, size *=2);
203
204         return dir;
205 }
206
207 #define INITIAL_SIZE    128
208 #ifndef ABOOK_SRC
209 #       define abook_malloc(X) malloc(X)
210 #       define abook_realloc(X, XX) realloc(X, XX)
211 #endif
212
213 char *
214 getaline(FILE *f)
215 {
216         char *buf;              /* buffer for line */
217         size_t size;            /* size of buffer */
218         size_t inc;             /* how much to enlarge buffer */
219         size_t len;             /* # of chars stored into buf before '\0' */
220         char *p;
221         const size_t thres = 128; /* initial buffer size (most lines should
222                                      fit into this size, so think of this as
223                                      the "long line threshold").  */
224         const size_t mucho = 128; /* if there is at least this much wasted
225                                      space when the whole buffer has been
226                                      read, try to reclaim it.  Don't make
227                                      this too small, else there is too much
228                                      time wasted trying to reclaim a couple
229                                      of bytes.  */
230         const size_t mininc = 64; /* minimum number of bytes by which
231                                      to increase the allocated memory */
232
233         len = 0;
234         size = thres;
235         buf = abook_malloc(size);
236
237         while (fgets(buf+len, size-len, f) != NULL) {
238                 len += strlen(buf+len);
239                 if (len > 0 && buf[len-1] == '\n')
240                         break;          /* the whole line has been read */
241
242                 for (inc = size, p = NULL; inc > mininc; inc /= 2)
243                         if ((p = abook_realloc(buf, size + inc)) != NULL)
244                                 break;
245
246                 size += inc;
247                 buf = p;
248         }
249
250         if (len == 0) {
251                 free(buf);
252                 return NULL;    /* nothing read (eof or error) */
253         }
254
255         if (buf[len-1] == '\n') /* remove newline, if there */
256                 buf[--len] = '\0';
257
258         if (size - len > mucho) { /* a plenitude of unused memory? */
259                 p = abook_realloc(buf, len+1);
260                 if (p != NULL) {
261                         buf = p;
262                         size = len+1;
263                 }
264         }
265
266         return buf;
267 }
268
269 /**************************************************************
270  * Original:
271  * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
272  * A bombproof version of doprnt (dopr) included.
273  * Sigh.  This sort of thing is always nasty do deal with.  Note that
274  * the version here does not include floating point...
275  *
276  * snprintf() is used instead of sprintf() as it does limit checks
277  * for string length.  This covers a nasty loophole.
278  *
279  * The other functions are there to prevent NULL pointers from
280  * causing nast effects.
281  *
282  * More Recently:
283  *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
284  *  This was ugly.  It is still ugly.  I opted out of floating point
285  *  numbers, but the formatter understands just about everything
286  *  from the normal C string format, at least as far as I can tell from
287  *  the Solaris 2.5 printf(3S) man page.
288  *
289  *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
290  *    Ok, added some minimal floating point support, which means this
291  *    probably requires libm on most operating systems.  Don't yet
292  *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
293  *    was pretty badly broken, it just wasn't being exercised in ways
294  *    which showed it, so that's been fixed.  Also, formated the code
295  *    to mutt conventions, and removed dead code left over from the
296  *    original.  Also, there is now a builtin-test, just compile with:
297  *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
298  *    and run snprintf for results.
299  * 
300  *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
301  *    The PGP code was using unsigned hexadecimal formats. 
302  *    Unfortunately, unsigned formats simply didn't work.
303  *
304  *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
305  *    The original code assumed that both snprintf() and vsnprintf() were
306  *    missing.  Some systems only have snprintf() but not vsnprintf(), so
307  *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
308  *
309  **************************************************************/
310
311 #include "config.h"
312
313 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
314
315 #include <string.h>
316 # include <ctype.h>
317 #include <sys/types.h>
318
319 /* Define this as a fall through, HAVE_STDARG_H is probably already set */
320
321 #if !defined(HAVE_STDARG_H) && !defined(HAVE_VARARGS_H)
322 #       define HAVE_VARARGS_H   1
323 #endif
324
325 /* varargs declarations: */
326
327 #if defined(HAVE_STDARG_H)
328 /*# include <stdarg.h>*/
329 # define HAVE_STDARGS    /* let's hope that works everywhere (mj) */
330 # define VA_LOCAL_DECL   va_list ap
331 # define VA_START(f)     va_start(ap, f)
332 # define VA_SHIFT(v,t)  ;   /* no-op for ANSI */
333 # define VA_END          va_end(ap)
334 #else
335 # if defined(HAVE_VARARGS_H)
336 #  include <varargs.h>
337 #  undef HAVE_STDARGS
338 #  define VA_LOCAL_DECL   va_list ap
339 #  define VA_START(f)     va_start(ap)      /* f is ignored! */
340 #  define VA_SHIFT(v,t) v = va_arg(ap,t)
341 #  define VA_END        va_end(ap)
342 # else
343 /*XX ** NO VARARGS ** XX*/
344 # endif
345 #endif
346
347 /*int snprintf (char *str, size_t count, const char *fmt, ...);*/
348 /*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/
349
350 static void dopr (char *buffer, size_t maxlen, const char *format, 
351                   va_list args);
352 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
353                     char *value, int flags, int min, int max);
354 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
355                     long value, int base, int min, int max, int flags);
356 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
357                    long double fvalue, int min, int max, int flags);
358 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
359
360 /*
361  * dopr(): poor man's version of doprintf
362  */
363
364 /* format read states */
365 #define DP_S_DEFAULT 0
366 #define DP_S_FLAGS   1
367 #define DP_S_MIN     2
368 #define DP_S_DOT     3
369 #define DP_S_MAX     4
370 #define DP_S_MOD     5
371 #define DP_S_CONV    6
372 #define DP_S_DONE    7
373
374 /* format flags - Bits */
375 #define DP_F_MINUS      (1 << 0)
376 #define DP_F_PLUS       (1 << 1)
377 #define DP_F_SPACE      (1 << 2)
378 #define DP_F_NUM        (1 << 3)
379 #define DP_F_ZERO       (1 << 4)
380 #define DP_F_UP         (1 << 5)
381 #define DP_F_UNSIGNED   (1 << 6)
382
383 /* Conversion Flags */
384 #define DP_C_SHORT   1
385 #define DP_C_LONG    2
386 #define DP_C_LDOUBLE 3
387
388 #define char_to_int(p) (p - '0')
389 #define MAX(p,q) ((p >= q) ? p : q)
390
391 static void dopr (char *buffer, size_t maxlen, const char *format, va_list args)
392 {
393   char ch;
394   long value;
395   long double fvalue;
396   char *strvalue;
397   int min;
398   int max;
399   int state;
400   int flags;
401   int cflags;
402   size_t currlen;
403   
404   state = DP_S_DEFAULT;
405   currlen = flags = cflags = min = 0;
406   max = -1;
407   ch = *format++;
408
409   while (state != DP_S_DONE)
410   {
411     if ((ch == '\0') || (currlen >= maxlen)) 
412       state = DP_S_DONE;
413
414     switch(state) 
415     {
416     case DP_S_DEFAULT:
417       if (ch == '%') 
418         state = DP_S_FLAGS;
419       else 
420         dopr_outch (buffer, &currlen, maxlen, ch);
421       ch = *format++;
422       break;
423     case DP_S_FLAGS:
424       switch (ch) 
425       {
426       case '-':
427         flags |= DP_F_MINUS;
428         ch = *format++;
429         break;
430       case '+':
431         flags |= DP_F_PLUS;
432         ch = *format++;
433         break;
434       case ' ':
435         flags |= DP_F_SPACE;
436         ch = *format++;
437         break;
438       case '#':
439         flags |= DP_F_NUM;
440         ch = *format++;
441         break;
442       case '0':
443         flags |= DP_F_ZERO;
444         ch = *format++;
445         break;
446       default:
447         state = DP_S_MIN;
448         break;
449       }
450       break;
451     case DP_S_MIN:
452       if (isdigit((unsigned char)ch)) 
453       {
454         min = 10*min + char_to_int (ch);
455         ch = *format++;
456       } 
457       else if (ch == '*') 
458       {
459         min = va_arg (args, int);
460         ch = *format++;
461         state = DP_S_DOT;
462       } 
463       else 
464         state = DP_S_DOT;
465       break;
466     case DP_S_DOT:
467       if (ch == '.') 
468       {
469         state = DP_S_MAX;
470         ch = *format++;
471       } 
472       else 
473         state = DP_S_MOD;
474       break;
475     case DP_S_MAX:
476       if (isdigit((unsigned char)ch)) 
477       {
478         if (max < 0)
479           max = 0;
480         max = 10*max + char_to_int (ch);
481         ch = *format++;
482       } 
483       else if (ch == '*') 
484       {
485         max = va_arg (args, int);
486         ch = *format++;
487         state = DP_S_MOD;
488       } 
489       else 
490         state = DP_S_MOD;
491       break;
492     case DP_S_MOD:
493       /* Currently, we don't support Long Long, bummer */
494       switch (ch) 
495       {
496       case 'h':
497         cflags = DP_C_SHORT;
498         ch = *format++;
499         break;
500       case 'l':
501         cflags = DP_C_LONG;
502         ch = *format++;
503         break;
504       case 'L':
505         cflags = DP_C_LDOUBLE;
506         ch = *format++;
507         break;
508       default:
509         break;
510       }
511       state = DP_S_CONV;
512       break;
513     case DP_S_CONV:
514       switch (ch) 
515       {
516       case 'd':
517       case 'i':
518         if (cflags == DP_C_SHORT) 
519           value = va_arg (args, short int);
520         else if (cflags == DP_C_LONG)
521           value = va_arg (args, long int);
522         else
523           value = va_arg (args, int);
524         fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
525         break;
526       case 'o':
527         flags |= DP_F_UNSIGNED;
528         if (cflags == DP_C_SHORT)
529           value = va_arg (args, unsigned short int);
530         else if (cflags == DP_C_LONG)
531           value = va_arg (args, unsigned long int);
532         else
533           value = va_arg (args, unsigned int);
534         fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
535         break;
536       case 'u':
537         flags |= DP_F_UNSIGNED;
538         if (cflags == DP_C_SHORT)
539           value = va_arg (args, unsigned short int);
540         else if (cflags == DP_C_LONG)
541           value = va_arg (args, unsigned long int);
542         else
543           value = va_arg (args, unsigned int);
544         fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
545         break;
546       case 'X':
547         flags |= DP_F_UP;
548       case 'x':
549         flags |= DP_F_UNSIGNED;
550         if (cflags == DP_C_SHORT)
551           value = va_arg (args, unsigned short int);
552         else if (cflags == DP_C_LONG)
553           value = va_arg (args, unsigned long int);
554         else
555           value = va_arg (args, unsigned int);
556         fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
557         break;
558       case 'f':
559         if (cflags == DP_C_LDOUBLE)
560           fvalue = va_arg (args, long double);
561         else
562           fvalue = va_arg (args, double);
563         /* um, floating point? */
564         fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
565         break;
566       case 'E':
567         flags |= DP_F_UP;
568       case 'e':
569         if (cflags == DP_C_LDOUBLE)
570           fvalue = va_arg (args, long double);
571         else
572           fvalue = va_arg (args, double);
573         break;
574       case 'G':
575         flags |= DP_F_UP;
576       case 'g':
577         if (cflags == DP_C_LDOUBLE)
578           fvalue = va_arg (args, long double);
579         else
580           fvalue = va_arg (args, double);
581         break;
582       case 'c':
583         dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
584         break;
585       case 's':
586         strvalue = va_arg (args, char *);
587         if (max < 0) 
588           max = maxlen; /* ie, no max */
589         fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
590         break;
591       case 'p':
592         strvalue = va_arg (args, void *);
593         fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
594         break;
595       case 'n':
596         if (cflags == DP_C_SHORT) 
597         {
598           short int *num;
599           num = va_arg (args, short int *);
600           *num = currlen;
601         } 
602         else if (cflags == DP_C_LONG) 
603         {
604           long int *num;
605           num = va_arg (args, long int *);
606           *num = currlen;
607         } 
608         else 
609         {
610           int *num;
611           num = va_arg (args, int *);
612           *num = currlen;
613         }
614         break;
615       case '%':
616         dopr_outch (buffer, &currlen, maxlen, ch);
617         break;
618       case 'w':
619         /* not supported yet, treat as next char */
620         ch = *format++;
621         break;
622       default:
623         /* Unknown, skip */
624         break;
625       }
626       ch = *format++;
627       state = DP_S_DEFAULT;
628       flags = cflags = min = 0;
629       max = -1;
630       break;
631     case DP_S_DONE:
632       break;
633     default:
634       /* hmm? */
635       break; /* some picky compilers need this */
636     }
637   }
638   if (currlen < maxlen - 1) 
639     buffer[currlen] = '\0';
640   else 
641     buffer[maxlen - 1] = '\0';
642 }
643
644 static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
645                     char *value, int flags, int min, int max)
646 {
647   int padlen, strln;     /* amount to pad */
648   int cnt = 0;
649   
650   if (value == 0)
651   {
652     value = "<NULL>";
653   }
654
655   for (strln = 0; value[strln]; ++strln); /* strlen */
656   padlen = min - strln;
657   if (padlen < 0) 
658     padlen = 0;
659   if (flags & DP_F_MINUS) 
660     padlen = -padlen; /* Left Justify */
661
662   while ((padlen > 0) && (cnt < max)) 
663   {
664     dopr_outch (buffer, currlen, maxlen, ' ');
665     --padlen;
666     ++cnt;
667   }
668   while (*value && (cnt < max)) 
669   {
670     dopr_outch (buffer, currlen, maxlen, *value++);
671     ++cnt;
672   }
673   while ((padlen < 0) && (cnt < max)) 
674   {
675     dopr_outch (buffer, currlen, maxlen, ' ');
676     ++padlen;
677     ++cnt;
678   }
679 }
680
681 /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
682
683 static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
684                     long value, int base, int min, int max, int flags)
685 {
686   int signvalue = 0;
687   unsigned long uvalue;
688   char convert[20];
689   int place = 0;
690   int spadlen = 0; /* amount to space pad */
691   int zpadlen = 0; /* amount to zero pad */
692   int caps = 0;
693   
694   if (max < 0)
695     max = 0;
696
697   uvalue = value;
698
699   if(!(flags & DP_F_UNSIGNED))
700   {
701     if( value < 0 ) {
702       signvalue = '-';
703       uvalue = -value;
704     }
705     else
706       if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
707         signvalue = '+';
708     else
709       if (flags & DP_F_SPACE)
710         signvalue = ' ';
711   }
712   
713   if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
714
715   do {
716     convert[place++] =
717       (caps? "0123456789ABCDEF":"0123456789abcdef")
718       [uvalue % (unsigned)base  ];
719     uvalue = (uvalue / (unsigned)base );
720   } while(uvalue && (place < 20));
721   if (place == 20) place--;
722   convert[place] = 0;
723
724   zpadlen = max - place;
725   spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
726   if (zpadlen < 0) zpadlen = 0;
727   if (spadlen < 0) spadlen = 0;
728   if (flags & DP_F_ZERO)
729   {
730     zpadlen = MAX(zpadlen, spadlen);
731     spadlen = 0;
732   }
733   if (flags & DP_F_MINUS) 
734     spadlen = -spadlen; /* Left Justifty */
735
736 #ifdef DEBUG_SNPRINTF
737   dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
738       zpadlen, spadlen, min, max, place));
739 #endif
740
741   /* Spaces */
742   while (spadlen > 0) 
743   {
744     dopr_outch (buffer, currlen, maxlen, ' ');
745     --spadlen;
746   }
747
748   /* Sign */
749   if (signvalue) 
750     dopr_outch (buffer, currlen, maxlen, signvalue);
751
752   /* Zeros */
753   if (zpadlen > 0) 
754   {
755     while (zpadlen > 0)
756     {
757       dopr_outch (buffer, currlen, maxlen, '0');
758       --zpadlen;
759     }
760   }
761
762   /* Digits */
763   while (place > 0) 
764     dopr_outch (buffer, currlen, maxlen, convert[--place]);
765   
766   /* Left Justified spaces */
767   while (spadlen < 0) {
768     dopr_outch (buffer, currlen, maxlen, ' ');
769     ++spadlen;
770   }
771 }
772
773 static long double abs_val (long double value)
774 {
775   long double result = value;
776
777   if (value < 0)
778     result = -value;
779
780   return result;
781 }
782
783 static long double pow10 (int exp)
784 {
785   long double result = 1;
786
787   while (exp)
788   {
789     result *= 10;
790     exp--;
791   }
792   
793   return result;
794 }
795
796 static long round (long double value)
797 {
798   long intpart;
799
800   intpart = value;
801   value = value - intpart;
802   if (value >= 0.5)
803     intpart++;
804
805   return intpart;
806 }
807
808 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
809                    long double fvalue, int min, int max, int flags)
810 {
811   int signvalue = 0;
812   long double ufvalue;
813   char iconvert[20];
814   char fconvert[20];
815   int iplace = 0;
816   int fplace = 0;
817   int padlen = 0; /* amount to pad */
818   int zpadlen = 0; 
819   int caps = 0;
820   long intpart;
821   long fracpart;
822   
823   /* 
824    * AIX manpage says the default is 0, but Solaris says the default
825    * is 6, and sprintf on AIX defaults to 6
826    */
827   if (max < 0)
828     max = 6;
829
830   ufvalue = abs_val (fvalue);
831
832   if (fvalue < 0)
833     signvalue = '-';
834   else
835     if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
836       signvalue = '+';
837     else
838       if (flags & DP_F_SPACE)
839         signvalue = ' ';
840
841 #if 0
842   if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
843 #endif
844
845   intpart = ufvalue;
846
847   /* 
848    * Sorry, we only support 9 digits past the decimal because of our 
849    * conversion method
850    */
851   if (max > 9)
852     max = 9;
853
854   /* We "cheat" by converting the fractional part to integer by
855    * multiplying by a factor of 10
856    */
857   fracpart = round ((pow10 (max)) * (ufvalue - intpart));
858
859   if (fracpart >= pow10 (max))
860   {
861     intpart++;
862     fracpart -= pow10 (max);
863   }
864
865 #ifdef DEBUG_SNPRINTF
866   dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
867 #endif
868
869   /* Convert integer part */
870   do {
871     iconvert[iplace++] =
872       (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
873     intpart = (intpart / 10);
874   } while(intpart && (iplace < 20));
875   if (iplace == 20) iplace--;
876   iconvert[iplace] = 0;
877
878   /* Convert fractional part */
879   do {
880     fconvert[fplace++] =
881       (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
882     fracpart = (fracpart / 10);
883   } while(fracpart && (fplace < 20));
884   if (fplace == 20) fplace--;
885   fconvert[fplace] = 0;
886
887   /* -1 for decimal point, another -1 if we are printing a sign */
888   padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
889   zpadlen = max - fplace;
890   if (zpadlen < 0)
891     zpadlen = 0;
892   if (padlen < 0) 
893     padlen = 0;
894   if (flags & DP_F_MINUS) 
895     padlen = -padlen; /* Left Justifty */
896
897   if ((flags & DP_F_ZERO) && (padlen > 0)) 
898   {
899     if (signvalue) 
900     {
901       dopr_outch (buffer, currlen, maxlen, signvalue);
902       --padlen;
903       signvalue = 0;
904     }
905     while (padlen > 0)
906     {
907       dopr_outch (buffer, currlen, maxlen, '0');
908       --padlen;
909     }
910   }
911   while (padlen > 0)
912   {
913     dopr_outch (buffer, currlen, maxlen, ' ');
914     --padlen;
915   }
916   if (signvalue) 
917     dopr_outch (buffer, currlen, maxlen, signvalue);
918
919   while (iplace > 0) 
920     dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
921
922   /*
923    * Decimal point.  This should probably use locale to find the correct
924    * char to print out.
925    */
926   dopr_outch (buffer, currlen, maxlen, '.');
927
928   while (fplace > 0) 
929     dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
930
931   while (zpadlen > 0)
932   {
933     dopr_outch (buffer, currlen, maxlen, '0');
934     --zpadlen;
935   }
936
937   while (padlen < 0) 
938   {
939     dopr_outch (buffer, currlen, maxlen, ' ');
940     ++padlen;
941   }
942 }
943
944 static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
945 {
946   if (*currlen < maxlen)
947     buffer[(*currlen)++] = c;
948 }
949 #endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
950
951 #ifndef HAVE_VSNPRINTF
952 int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
953 {
954   str[0] = 0;
955   dopr(str, count, fmt, args);
956   return(strlen(str));
957 }
958 #endif /* !HAVE_VSNPRINTF */
959
960 #ifndef HAVE_SNPRINTF
961 /* VARARGS3 */
962 #ifdef HAVE_STDARGS
963 int snprintf (char *str,size_t count,const char *fmt,...)
964 #else
965 int snprintf (va_alist) va_dcl
966 #endif
967 {
968 #ifndef HAVE_STDARGS
969   char *str;
970   size_t count;
971   char *fmt;
972 #endif
973   VA_LOCAL_DECL;
974     
975   VA_START (fmt);
976   VA_SHIFT (str, char *);
977   VA_SHIFT (count, size_t );
978   VA_SHIFT (fmt, char *);
979   (void) vsnprintf(str, count, fmt, ap);
980   VA_END;
981   return(strlen(str));
982 }
983
984 #ifdef TEST_SNPRINTF
985 #ifndef LONG_STRING
986 #define LONG_STRING 1024
987 #endif
988 int main (void)
989 {
990   char buf1[LONG_STRING];
991   char buf2[LONG_STRING];
992   char *fp_fmt[] = {
993     "%-1.5f",
994     "%1.5f",
995     "%123.9f",
996     "%10.5f",
997     "% 10.5f",
998     "%+22.9f",
999     "%+4.9f",
1000     "%01.3f",
1001     "%4f",
1002     "%3.1f",
1003     "%3.2f",
1004     NULL
1005   };
1006   double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, 
1007     0.9996, 1.996, 4.136, 0};
1008   char *int_fmt[] = {
1009     "%-1.5d",
1010     "%1.5d",
1011     "%123.9d",
1012     "%5.5d",
1013     "%10.5d",
1014     "% 10.5d",
1015     "%+22.33d",
1016     "%01.3d",
1017     "%4d",
1018     NULL
1019   };
1020   long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
1021   int x, y;
1022   int fail = 0;
1023   int num = 0;
1024
1025   printf ("Testing snprintf format codes against system sprintf...\n");
1026
1027   for (x = 0; fp_fmt[x] != NULL ; x++)
1028     for (y = 0; fp_nums[y] != 0 ; y++)
1029     {
1030       snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
1031       sprintf (buf2, fp_fmt[x], fp_nums[y]);
1032       if (strcmp (buf1, buf2))
1033       {
1034         printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
1035             fp_fmt[x], buf1, buf2);
1036         fail++;
1037       }
1038       num++;
1039     }
1040
1041   for (x = 0; int_fmt[x] != NULL ; x++)
1042     for (y = 0; int_nums[y] != 0 ; y++)
1043     {
1044       snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
1045       sprintf (buf2, int_fmt[x], int_nums[y]);
1046       if (strcmp (buf1, buf2))
1047       {
1048         printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
1049             int_fmt[x], buf1, buf2);
1050         fail++;
1051       }
1052       num++;
1053     }
1054   printf ("%d tests failed out of %d.\n", fail, num);
1055 }
1056 #endif /* SNPRINTF_TEST */
1057
1058 #endif /* !HAVE_SNPRINTF */