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