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