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