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