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