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