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