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