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