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