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