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