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