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