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