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