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