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