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