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