Corrected the changelog format.
[doldaconnect.git] / common / utils.c
1 /*
2  *  Dolda Connect - Modular multiuser Direct Connect-style client
3  *  Copyright (C) 2004 Fredrik Tolf <fredrik@dolda2000.com>
4  *  
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *  
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *  
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include <stdio.h>
22 #include <wchar.h>
23 #include <iconv.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <wctype.h>
27 #include <langinfo.h>
28 #include <pwd.h>
29 #include <unistd.h>
30 #include <sys/time.h>
31 #include <netinet/in.h>
32 #include <alloca.h>
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37 #include <utils.h>
38 #include <log.h>
39
40 static char *base64set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
41 static int base64rev[] = {
42     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
43     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
44     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
45     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
46     -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
47     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
48     -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
49     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
50     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
51     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
52     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
53     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
54     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
55     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
56     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
57     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
58 };
59 static char *base32set = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
60 static int base32rev[] = {
61     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
62     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
63     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
64     -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, -1,
65     -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
66     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
67     -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
68     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
69     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
70     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
71     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
72     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
73     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
74     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
75     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
76     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
77 };
78
79 char *vsprintf2(char *format, va_list al)
80 {
81     int ret;
82     char *buf;
83     va_list al2;
84     
85     va_copy(al2, al);
86     ret = vsnprintf(NULL, 0, format, al2);
87     va_end(al2);
88     if((buf = malloc(ret + 1)) == NULL)
89     {
90         LOGOOM(ret + 1);
91         return(NULL);
92     }
93     va_copy(al2, al);
94     vsnprintf(buf, ret + 1, format, al2);
95     va_end(al2);
96     return(buf);
97 }
98
99 char *sprintf2(char *format, ...)
100 {
101     va_list args;
102     char *buf;
103     
104     va_start(args, format);
105     buf = vsprintf2(format, args);
106     va_end(args);
107     return(buf);
108 }
109
110 wchar_t *vswprintf2(wchar_t *format, va_list al)
111 {
112     int ret;
113     wchar_t *buf;
114     size_t bufsize;
115     va_list al2;
116     
117     buf = smalloc(sizeof(wchar_t) * (bufsize = 1024));
118     while(1)
119     {
120         va_copy(al2, al);
121         ret = vswprintf(buf, bufsize, format, al2);
122         va_end(al2);
123         if(ret >= 0)
124             break;
125         buf = srealloc(buf, sizeof(wchar_t) * (bufsize *= 2));
126     }
127     if(bufsize > ret + 1)
128         buf = srealloc(buf, sizeof(wchar_t) * (ret + 1));
129     return(buf);
130 }
131
132 wchar_t *swprintf2(wchar_t *format, ...)
133 {
134     va_list args;
135     wchar_t *buf;
136     
137     va_start(args, format);
138     buf = vswprintf2(format, args);
139     va_end(args);
140     return(buf);
141 }
142
143 int havecharset(char *charset)
144 {
145     iconv_t cd;
146     
147     if((cd = iconv_open("wchar_t", charset)) == (iconv_t)-1)
148         return(0);
149     iconv_close(cd);
150     if((cd = iconv_open(charset, "wchar_t")) == (iconv_t)-1)
151         return(0);
152     iconv_close(cd);
153     return(1);
154 }
155
156 wchar_t *icmbstowcs(char *mbs, char *charset)
157 {
158     int ret;
159     char *buf;
160     char *p, *p2;
161     size_t len1, len2, bufsize, data;
162     iconv_t cd;
163     
164     len1 = strlen(mbs) + 1;
165     bufsize = len2 = len1 * sizeof(wchar_t);
166     if((buf = malloc(bufsize)) == NULL)
167     {
168         LOGOOM(bufsize);
169         return(NULL);
170     }
171     if(charset == NULL)
172         charset = nl_langinfo(CODESET);
173     if((cd = iconv_open("wchar_t", charset)) == (iconv_t)-1)
174     {
175 #ifdef DAEMON
176         flog(LOG_ERR, "icmbstowcs: could not open iconv structure for %s: %s", charset, strerror(errno));
177 #endif
178         free(buf);
179         return(NULL);
180     }
181     p = buf;
182     while(len1 > 0)
183     {
184         ret = iconv(cd, &mbs, &len1, &p, &len2);
185         if(ret < 0)
186         {
187             if(errno == E2BIG)
188             {
189                 data = p - buf;
190                 len2 += 128;
191                 bufsize += 128;
192                 if((p2 = realloc(buf, bufsize)) == NULL)
193                 {
194                     LOGOOM(bufsize);
195                     free(buf);
196                     iconv_close(cd);
197                     return(NULL);
198                 }
199                 buf = p2;
200                 p = buf + data;
201             } else {
202                 free(buf);
203                 iconv_close(cd);
204                 return(NULL);
205             }
206         }
207     }
208     if(len2 > 0)
209         buf = realloc(buf, p - buf);
210     iconv_close(cd);
211     return((wchar_t *)buf);
212 }
213
214 wchar_t *icsmbstowcs(char *mbs, char *charset, wchar_t *def)
215 {
216     static wchar_t *buf = NULL;
217     
218     if(buf != NULL)
219         free(buf);
220     if((buf = icmbstowcs(mbs, charset)) == NULL)
221     {
222         if((def != NULL) && (*def == L'~'))
223         {
224 #ifdef DAEMON
225             flog(LOG_WARNING, "icsmbstowcs: could not convert wcs string into charset %s: %s", charset, strerror(errno));
226 #endif
227             def++;
228         }
229         return(def);
230     }
231     return(buf);
232 }
233
234 char *icwcstombs(wchar_t *wcs, char *charset)
235 {
236     int ret;
237     char *buf;
238     char *p, *p2;
239     size_t len1, len2, bufsize, data;
240     iconv_t cd;
241     
242     len1 = sizeof(wchar_t) * (wcslen(wcs) + 1);
243     bufsize = len2 = len1;
244     if((buf = malloc(bufsize)) == NULL)
245     {
246 #ifdef DAEMON
247         LOGOOM(bufsize);
248 #endif
249         return(NULL);
250     }
251     if(charset == NULL)
252         charset = nl_langinfo(CODESET);
253     if((cd = iconv_open(charset, "wchar_t")) == (iconv_t)-1)
254     {
255 #ifdef DAEMON
256         flog(LOG_ERR, "icwcstombs: could not open iconv structure for %s: %s", charset, strerror(errno));
257 #endif
258         free(buf);
259         return(NULL);
260     }
261     p = buf;
262     while(len1 > 0)
263     {
264         ret = iconv(cd, (char **)&wcs, &len1, &p, &len2);
265         if(ret < 0)
266         {
267             if(errno == E2BIG)
268             {
269                 data = p - buf;
270                 len2 += 128;
271                 bufsize += 128;
272                 if((p2 = realloc(buf, bufsize)) == NULL)
273                 {
274                     LOGOOM(bufsize);
275                     free(buf);
276                     iconv_close(cd);
277                     return(NULL);
278                 }
279                 buf = p2;
280                 p = buf + data;
281             } else {
282                 free(buf);
283                 iconv_close(cd);
284                 return(NULL);
285             }
286         }
287     }
288     if(len2 > 0)
289         buf = realloc(buf, p - buf);
290     iconv_close(cd);
291     return(buf);
292 }
293
294 char *icswcstombs(wchar_t *wcs, char *charset, char *def)
295 {
296     static char *buf = NULL;
297     
298     if(buf != NULL)
299         free(buf);
300     if((buf = icwcstombs(wcs, charset)) == NULL)
301     {
302         if((def != NULL) && (*def == '~'))
303         {
304 #ifdef DAEMON
305             flog(LOG_WARNING, "icswcstombs: could not convert mbs string from charset %s: %s", charset, strerror(errno));
306 #endif
307             def++;
308         }
309         return(def);
310     }
311     return(buf);
312 }
313
314 wchar_t *wcstolower(wchar_t *wcs)
315 {
316     wchar_t *p;
317     
318     for(p = wcs; *p != L'\0'; p++)
319         *p = towlower(*p);
320     return(wcs);
321 }
322
323 wchar_t ucptowc(int ucp)
324 {
325     int ret;
326     unsigned long ucpbuf;
327     char *buf;
328     char *mbsp, *p, *p2;
329     wchar_t res;
330     size_t len1, len2, bufsize, data;
331     iconv_t cd;
332     
333     ucpbuf = htonl(ucp);
334     mbsp = (char *)&ucpbuf;
335     len1 = 4;
336     bufsize = len2 = len1 * sizeof(wchar_t);
337     if((buf = malloc(bufsize)) == NULL)
338     {
339         LOGOOM(bufsize);
340         return(L'\0');
341     }
342     if((cd = iconv_open("wchar_t", "UCS-4BE")) == (iconv_t)-1)
343     {
344 #ifdef DAEMON
345         flog(LOG_ERR, "ucptowc: could not open iconv structure for UCS-4BE: %s", strerror(errno));
346 #endif
347         free(buf);
348         return(L'\0');
349     }
350     p = buf;
351     while(len1 > 0)
352     {
353         ret = iconv(cd, &mbsp, &len1, &p, &len2);
354         if(ret < 0)
355         {
356             if(errno == E2BIG)
357             {
358                 data = p - buf;
359                 len2 += 128;
360                 bufsize += 128;
361                 if((p2 = realloc(buf, bufsize)) == NULL)
362                 {
363                     LOGOOM(bufsize);
364                     free(buf);
365                     iconv_close(cd);
366                     return(L'\0');
367                 }
368                 buf = p2;
369                 p = buf + data;
370             } else {
371                 free(buf);
372                 iconv_close(cd);
373                 return(L'\0');
374             }
375         }
376     }
377     if(len2 > 0)
378         buf = realloc(buf, p - buf);
379     iconv_close(cd);
380     res = *(wchar_t *)buf;
381     free(buf);
382     return(res);
383 }
384
385 void _sizebuf(void **buf, size_t *bufsize, size_t reqsize, size_t elsize, int algo)
386 {
387     if(*bufsize >= reqsize)
388         return;
389     switch(algo)
390     {
391     case 0:
392         *buf = srealloc(*buf, elsize * ((*bufsize) = reqsize));
393         break;
394     case 1:
395         if(*bufsize == 0)
396             *bufsize = 1;
397         while(*bufsize < reqsize)
398             *bufsize <<= 1;
399         *buf = srealloc(*buf, elsize * (*bufsize));
400         break;
401     }
402 }
403
404 double ntime(void)
405 {
406     struct timeval tv;
407     
408     gettimeofday(&tv, NULL);
409     return((double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0));
410 }
411
412 int wcsexists(wchar_t *h, wchar_t *n)
413 {
414     int i, o, nl, hl;
415     wchar_t *ln, *lh;
416     
417     ln = alloca(sizeof(*ln) * (nl = wcslen(n)));
418     for(i = 0; i < nl; i++)
419         ln[i] = towlower(n[i]);
420     lh = alloca(sizeof(*lh) * (hl = wcslen(h)));
421     if(nl > hl)
422         return(0);
423     for(i = 0; i < nl; i++)
424         lh[i] = towlower(h[i]);
425     i = 0;
426     while(1)
427     {
428         for(o = 0; o < nl; o++)
429         {
430             if(lh[i + o] != ln[o])
431                 break;
432         }
433         if(o == nl)
434             return(1);
435         if(i == hl - nl)
436             return(0);
437         lh[i + nl] = towlower(h[i + nl]);
438         i++;
439     }
440 }
441
442 #ifndef HAVE_WCSCASECMP
443 int wcscasecmp(const wchar_t *s1, const wchar_t *s2)
444 {
445     for(; (towlower(*s1) == towlower(*s2)) && (*s1 != L'\0'); s1++, s2++);
446     return(towlower(*s1) - towlower(*s2));
447 }
448 #endif
449
450 char *hexencode(char *data, size_t datalen)
451 {
452     char *buf, this;
453     size_t bufsize, bufdata;
454     int dig;
455     
456     buf = NULL;
457     bufsize = bufdata = 0;
458     for(; datalen > 0; datalen--, data++)
459     {
460         dig = (*data & 0xF0) >> 4;
461         if(dig > 9)
462             this = 'A' + dig - 10;
463         else
464             this = dig + '0';
465         addtobuf(buf, this);
466         dig = *data & 0x0F;
467         if(dig > 9)
468             this = 'A' + dig - 10;
469         else
470             this = dig + '0';
471         addtobuf(buf, this);
472     }
473     addtobuf(buf, 0);
474     return(buf);
475 }
476
477 char *hexdecode(char *data, size_t *len)
478 {
479     char *buf, this, bit;
480     size_t bufsize, bufdata;
481     
482     buf = NULL;
483     bufsize = bufdata = 0;
484     for(bit = 4, this = 0; *data; data++)
485     {
486         if((*data >= 'A') && (*data <= 'F'))
487         {
488             this |= (this & 0x0F) | ((*data - 'A' + 10) << bit);
489         } else if((*data >= 'a') && (*data <= 'f')) {
490             this |= (this & 0x0F) | ((*data - 'a' + 10) << bit);
491         } else if((*data >= '0') && (*data <= '9')) {
492             this |= (this & 0x0F) | ((*data - '0') << bit);
493         } else if(*data == '\n') {
494             continue;
495         } else {
496             if(buf != NULL)
497                 free(buf);
498             return(NULL);
499         }
500         if(bit == 4) {
501             bit = 0;
502         } else {
503             bit = 4;
504             addtobuf(buf, this);
505             this = 0;
506         }
507     }
508     if(bit != 4) {
509         if(buf != NULL)
510             free(buf);
511         return(NULL);
512     }
513     addtobuf(buf, 0);
514     if(len != NULL)
515         *len = bufdata - 1;
516     return(buf);
517 }
518
519 char *base64encode(char *data, size_t datalen)
520 {
521     char *buf;
522     size_t bufsize, bufdata;
523     
524     if(datalen == 0)
525         return(sstrdup(""));
526     buf = NULL;
527     bufsize = bufdata = 0;
528     while(datalen >= 3)
529     {
530         addtobuf(buf, base64set[(data[0] & 0xfc) >> 2]);
531         addtobuf(buf, base64set[((data[0] & 0x03) << 4) | ((data[1] & 0xf0) >> 4)]);
532         addtobuf(buf, base64set[((data[1] & 0x0f) << 2) | ((data[2] & 0xc0) >> 6)]);
533         addtobuf(buf, base64set[data[2] & 0x3f]);
534         datalen -= 3;
535         data += 3;
536     }
537     if(datalen == 1)
538     {
539         addtobuf(buf, base64set[(data[0] & 0xfc) >> 2]);
540         addtobuf(buf, base64set[(data[0] & 0x03) << 4]);
541         bufcat(buf, "==", 2);
542     }
543     if(datalen == 2)
544     {
545         addtobuf(buf, base64set[(data[0] & 0xfc) >> 2]);
546         addtobuf(buf, base64set[((data[0] & 0x03) << 4) | ((data[1] & 0xf0) >> 4)]);
547         addtobuf(buf, base64set[(data[1] & 0x0f) << 2]);
548         addtobuf(buf, '=');
549     }
550     addtobuf(buf, 0);
551     return(buf);
552 }
553
554 char *base64decode(char *data, size_t *datalen)
555 {
556     int b, c;
557     char *buf, cur;
558     size_t bufsize, bufdata;
559     
560     buf = NULL;
561     bufsize = bufdata = 0;
562     cur = 0;
563     b = 8;
564     for(; *data > 0; data++)
565     {
566         c = (int)(unsigned char)*data;
567         if(c == '=')
568             break;
569         if(c == '\n')
570             continue;
571         if(base64rev[c] == -1)
572         {
573             if(buf != NULL)
574                 free(buf);
575             return(NULL);
576         }
577         b -= 6;
578         if(b <= 0)
579         {
580             cur |= base64rev[c] >> -b;
581             addtobuf(buf, cur);
582             b += 8;
583             cur = 0;
584         }
585         cur |= base64rev[c] << b;
586     }
587     if(datalen != NULL)
588         *datalen = bufdata;
589     addtobuf(buf, 0);
590     return(buf);
591 }
592
593 char *base32encode(char *data, size_t datalen)
594 {
595     char *buf;
596     size_t bufsize, bufdata;
597     
598     if(datalen == 0)
599         return(sstrdup(""));
600     buf = NULL;
601     bufsize = bufdata = 0;
602     while(datalen >= 5)
603     {
604         addtobuf(buf, base32set[((data[0] & 0xf8) >> 3)]);
605         addtobuf(buf, base32set[((data[0] & 0x07) << 2) | ((data[1] & 0xc0) >> 6)]);
606         addtobuf(buf, base32set[((data[1] & 0x3e) >> 1)]);
607         addtobuf(buf, base32set[((data[1] & 0x01) << 4) | ((data[2] & 0xf0) >> 4)]);
608         addtobuf(buf, base32set[((data[2] & 0x0f) << 1) | ((data[3] & 0x80) >> 7)]);
609         addtobuf(buf, base32set[((data[3] & 0x7c) >> 2)]);
610         addtobuf(buf, base32set[((data[3] & 0x03) << 3) | ((data[4] & 0xe0) >> 5)]);
611         addtobuf(buf, base32set[data[4] & 0x1f]);
612         datalen -= 5;
613         data += 5;
614     }
615     if(datalen == 1)
616     {
617         addtobuf(buf, base32set[((data[0] & 0xf8) >> 3)]);
618         addtobuf(buf, base32set[((data[0] & 0x07) << 2)]);
619         bufcat(buf, "======", 6);
620     }
621     if(datalen == 2)
622     {
623         addtobuf(buf, base32set[((data[0] & 0xf8) >> 3)]);
624         addtobuf(buf, base32set[((data[0] & 0x07) << 2) | ((data[1] & 0xc0) >> 6)]);
625         addtobuf(buf, base32set[((data[1] & 0x3e) >> 1)]);
626         addtobuf(buf, base32set[((data[1] & 0x01) << 4)]);
627         bufcat(buf, "====", 4);
628     }
629     if(datalen == 3)
630     {
631         addtobuf(buf, base32set[((data[0] & 0xf8) >> 3)]);
632         addtobuf(buf, base32set[((data[0] & 0x07) << 2) | ((data[1] & 0xc0) >> 6)]);
633         addtobuf(buf, base32set[((data[1] & 0x3e) >> 1)]);
634         addtobuf(buf, base32set[((data[1] & 0x01) << 4) | ((data[2] & 0xf0) >> 4)]);
635         addtobuf(buf, base32set[((data[2] & 0x0f) << 1)]);
636         bufcat(buf, "===", 3);
637     }
638     if(datalen == 4)
639     {
640         addtobuf(buf, base32set[((data[0] & 0xf8) >> 3)]);
641         addtobuf(buf, base32set[((data[0] & 0x07) << 2) | ((data[1] & 0xc0) >> 6)]);
642         addtobuf(buf, base32set[((data[1] & 0x3e) >> 1)]);
643         addtobuf(buf, base32set[((data[1] & 0x01) << 4) | ((data[2] & 0xf0) >> 4)]);
644         addtobuf(buf, base32set[((data[2] & 0x0f) << 1) | ((data[3] & 0x80) >> 7)]);
645         addtobuf(buf, base32set[((data[3] & 0x7c) >> 2)]);
646         addtobuf(buf, base32set[((data[3] & 0x03) << 3)]);
647         bufcat(buf, "=", 1);
648     }
649     addtobuf(buf, 0);
650     return(buf);
651 }
652
653 char *base32decode(char *data, size_t *datalen)
654 {
655     int b, c;
656     char *buf, cur;
657     size_t bufsize, bufdata;
658     
659     buf = NULL;
660     bufsize = bufdata = 0;
661     cur = 0;
662     b = 8;
663     for(; *data > 0; data++)
664     {
665         c = (int)(unsigned char)*data;
666         if(c == '=')
667             break;
668         if(c == '\n')
669             continue;
670         if(base32rev[c] == -1)
671         {
672             if(buf != NULL)
673                 free(buf);
674             return(NULL);
675         }
676         b -= 5;
677         if(b <= 0)
678         {
679             cur |= base32rev[c] >> -b;
680             addtobuf(buf, cur);
681             b += 8;
682             cur = 0;
683         }
684         cur |= base32rev[c] << b;
685     }
686     if(datalen != NULL)
687         *datalen = bufdata;
688     addtobuf(buf, 0);
689     return(buf);
690 }
691
692 void _freeparr(void **arr)
693 {
694     void **buf;
695     
696     if(arr == NULL)
697         return;
698     for(buf = arr; *buf != NULL; buf++)
699         free(*buf);
700     free(arr);
701 }
702
703 int _parrlen(void **arr)
704 {
705     int i;
706     
707     if(arr == NULL)
708         return(0);
709     for(i = 0; *arr != NULL; arr++)
710         i++;
711     return(i);
712 }
713
714 char *getetcpath(char *binpath)
715 {
716     int f;
717     char *etcpath, *p;
718     size_t etcpathsize, etcpathdata;
719
720     etcpath = NULL;
721     etcpathsize = etcpathdata = 0;
722     f = 1;
723     do
724     {
725         if(f)
726             f = 0;
727         else
728             binpath++;
729         for(p = binpath; *p && (*p != ':'); p++);
730         for(; (p >= binpath) && (*p != '/'); p--);
731         if(p >= binpath)
732         {
733             if(etcpathdata > 0)
734                 addtobuf(etcpath, ':');
735             bufcat(etcpath, binpath, p - binpath + 1);
736             bufcat(etcpath, "etc", 3);
737         }
738     } while((binpath = strchr(binpath, ':')) != NULL);
739     addtobuf(etcpath, 0);
740     return(etcpath);
741 }
742
743 char *findfile(char *name, char *homedir, int filldef)
744 {
745     char *path, *binpath, *etcpath, *p;
746     struct passwd *pw;
747     int mode, homeonly;
748     
749     if(name == NULL)
750         return(NULL);
751
752     mode = R_OK | (filldef ? W_OK : 0);
753     homeonly = homedir != NULL;
754
755     if(!strchr(name, '/'))
756     {
757         if(homedir == NULL)
758             homedir = getenv("HOME");
759         if((homedir == NULL) && ((pw = getpwuid(getuid())) != NULL))
760             homedir = pw->pw_dir;
761         if((homedir != NULL) && ((path = sprintf2("%s/.%s", homedir, name)) != NULL))
762         {
763             if(!access(path, mode))
764                 return(path);
765             free(path);
766         }
767     }
768     
769     if(!homeonly)
770     {
771         if(strchr(name, '/') != NULL)
772         {
773             if(!access(name, mode))
774                 return(sstrdup(name));
775         } else {
776             if((binpath = getenv("PATH")) == NULL)
777                 etcpath = sstrdup("/usr/local/etc:/etc:/usr/etc");
778             else
779                 etcpath = getetcpath(binpath);
780             for(p = strtok(etcpath, ":"); p != NULL; p = strtok(NULL, ":"))
781             {
782                 if((path = sprintf2("%s/%s", p, name)) != NULL)
783                 {
784                     if(!access(path, mode))
785                     {
786                         free(etcpath);
787                         return(path);
788                     }
789                     free(path);
790                 }
791             }
792             free(etcpath);
793         }
794     }
795     
796     if(filldef) {
797         if(homedir)
798             return(sprintf2("%s/.%s", homedir, name));
799         return(sprintf2("/etc/%s", name));
800     } else {
801         return(NULL);
802     }
803 }
804
805 struct strpair *newstrpair(char *key, char *val, struct strpair **list)
806 {
807     struct strpair *pair;
808     
809     pair = smalloc(sizeof(*pair));
810     memset(pair, 0, sizeof(*pair));
811     if(key != NULL)
812         pair->key = sstrdup(key);
813     if(val != NULL)
814         pair->val = sstrdup(val);
815     if(list != NULL)
816     {
817         pair->next = *list;
818         *list = pair;
819     }
820     return(pair);
821 }
822
823 void freestrpair(struct strpair *pair, struct strpair **list)
824 {
825     struct strpair *cur;
826     
827     for(cur = *list; cur != NULL; list = &(cur->next), cur = cur->next)
828     {
829         if(cur == pair)
830         {
831             *list = cur->next;
832             break;
833         }
834     }
835     free(pair->key);
836     free(pair->val);
837     free(pair);
838 }
839
840 char *spfind(struct strpair *list, char *key)
841 {
842     for(; list != NULL; list = list->next)
843     {
844         if(!strcmp(list->key, key))
845             return(list->val);
846     }
847     return(NULL);
848 }
849
850 struct wcspair *newwcspair(wchar_t *key, wchar_t *val, struct wcspair **list)
851 {
852     struct wcspair *pair;
853     
854     pair = smalloc(sizeof(*pair));
855     memset(pair, 0, sizeof(*pair));
856     if(key != NULL)
857         pair->key = swcsdup(key);
858     if(val != NULL)
859         pair->val = swcsdup(val);
860     if(list != NULL)
861     {
862         pair->next = *list;
863         *list = pair;
864     }
865     return(pair);
866 }
867
868 void freewcspair(struct wcspair *pair, struct wcspair **list)
869 {
870     struct wcspair *cur;
871     
872     for(cur = *list; cur != NULL; list = &(cur->next), cur = cur->next)
873     {
874         if(cur == pair)
875         {
876             *list = cur->next;
877             break;
878         }
879     }
880     free(pair->key);
881     free(pair->val);
882     free(pair);
883 }
884
885 wchar_t *wpfind(struct wcspair *list, wchar_t *key)
886 {
887     for(; list != NULL; list = list->next)
888     {
889         if(!wcscmp(list->key, key))
890             return(list->val);
891     }
892     return(NULL);
893 }