1 /*
2  * File: dlib.c
3  *
4  * Copyright (C) 2006-2007 Jorge Arellano Cid <jcid@dillo.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  */
11 
12 /* Memory allocation, Simple dynamic strings, Lists (simple and sorted),
13  * and a few utility functions
14  */
15 
16 /*
17  * TODO: vsnprintf() is in C99, maybe a simple replacement if necessary.
18  */
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <ctype.h>
27 
28 #include "dlib.h"
29 
30 static bool_t dLib_show_msg = TRUE;
31 
32 /* dlib msgs go to stderr to avoid problems with filter dpis */
33 #define DLIB_MSG(...)                              \
34    D_STMT_START {                                  \
35       if (dLib_show_msg)                           \
36          fprintf(stderr, __VA_ARGS__);             \
37    } D_STMT_END
38 
39 /*
40  *- Memory --------------------------------------------------------------------
41  */
42 
dMalloc(size_t size)43 void *dMalloc (size_t size)
44 {
45   void *value = malloc (size);
46   if (value == 0)
47      exit(1);
48   return value;
49 }
50 
dRealloc(void * mem,size_t size)51 void *dRealloc (void *mem, size_t size)
52 {
53   void *value = realloc (mem, size);
54   if (value == 0)
55      exit(1);
56   return value;
57 }
58 
dMalloc0(size_t size)59 void *dMalloc0 (size_t size)
60 {
61   void *value = dMalloc (size);
62   memset (value, 0, size);
63   return value;
64 }
65 
dFree(void * mem)66 void dFree (void *mem)
67 {
68    free(mem);
69 }
70 
71 /*
72  *- strings (char *) ----------------------------------------------------------
73  */
74 
dStrdup(const char * s)75 char *dStrdup(const char *s)
76 {
77    if (s) {
78       int len = strlen(s)+1;
79       char *ns = dNew(char, len);
80       memcpy(ns, s, len);
81       return ns;
82    }
83    return NULL;
84 }
85 
dStrndup(const char * s,size_t sz)86 char *dStrndup(const char *s, size_t sz)
87 {
88    if (s) {
89       char *ns = dNew(char, sz+1);
90       memcpy(ns, s, sz);
91       ns[sz] = 0;
92       return ns;
93    }
94    return NULL;
95 }
96 
97 /*
98  * Concatenate a NULL-terminated list of strings
99  */
dStrconcat(const char * s1,...)100 char *dStrconcat(const char *s1, ...)
101 {
102    va_list args;
103    char *s, *ns = NULL;
104 
105    if (s1) {
106       Dstr *dstr = dStr_sized_new(64);
107       va_start(args, s1);
108       for (s = (char*)s1; s; s = va_arg(args, char*))
109          dStr_append(dstr, s);
110       va_end(args);
111       ns = dstr->str;
112       dStr_free(dstr, 0);
113    }
114    return ns;
115 }
116 
117 /*
118  * Remove leading and trailing whitespace
119  */
dStrstrip(char * s)120 char *dStrstrip(char *s)
121 {
122    char *p;
123    int len;
124 
125    if (s && *s) {
126       for (p = s; dIsspace(*p); ++p);
127       for (len = strlen(p); len && dIsspace(p[len-1]); --len);
128       if (p > s)
129          memmove(s, p, len);
130       s[len] = 0;
131    }
132    return s;
133 }
134 
135 /*
136  * Clear the contents of the string
137  */
dStrshred(char * s)138 void dStrshred(char *s)
139 {
140    if (s)
141       memset(s, 0, strlen(s));
142 }
143 
144 /*
145  * Return a new string of length 'len' filled with 'c' characters
146  */
dStrnfill(size_t len,char c)147 char *dStrnfill(size_t len, char c)
148 {
149    char *ret = dNew(char, len+1);
150    for (ret[len] = 0; len > 0; ret[--len] = c);
151    return ret;
152 }
153 
154 /*
155  * strsep() implementation
156  */
dStrsep(char ** orig,const char * delim)157 char *dStrsep(char **orig, const char *delim)
158 {
159    char *str, *p;
160 
161    if (!(str = *orig))
162       return NULL;
163 
164    p = strpbrk(str, delim);
165    if (p) {
166       *p++ = 0;
167       *orig = p;
168    } else {
169       *orig = NULL;
170    }
171    return str;
172 }
173 
174 /*
175  * ASCII functions to avoid the case difficulties introduced by I/i in
176  * Turkic locales.
177  */
178 
179 /*
180  * Case insensitive strstr
181  */
dStriAsciiStr(const char * haystack,const char * needle)182 char *dStriAsciiStr(const char *haystack, const char *needle)
183 {
184    int i, j;
185    char *ret = NULL;
186 
187    if (haystack && needle) {
188       for (i = 0, j = 0; haystack[i] && needle[j]; ++i)
189          if (D_ASCII_TOLOWER(haystack[i]) == D_ASCII_TOLOWER(needle[j])) {
190             ++j;
191          } else if (j) {
192             i -= j;
193             j = 0;
194          }
195       if (!needle[j])                 /* Got all */
196          ret = (char *)(haystack + i - j);
197    }
198    return ret;
199 }
200 
dStrAsciiCasecmp(const char * s1,const char * s2)201 int dStrAsciiCasecmp(const char *s1, const char *s2)
202 {
203    int ret = 0;
204 
205    while ((*s1 || *s2) &&
206           !(ret = D_ASCII_TOLOWER(*s1) - D_ASCII_TOLOWER(*s2))) {
207       s1++;
208       s2++;
209    }
210    return ret;
211 }
212 
dStrnAsciiCasecmp(const char * s1,const char * s2,size_t n)213 int dStrnAsciiCasecmp(const char *s1, const char *s2, size_t n)
214 {
215    int ret = 0;
216 
217    while (n-- && (*s1 || *s2) &&
218           !(ret = D_ASCII_TOLOWER(*s1) - D_ASCII_TOLOWER(*s2))) {
219       s1++;
220       s2++;
221    }
222    return ret;
223 }
224 
225 /*
226  *- dStr ----------------------------------------------------------------------
227  */
228 
229 /*
230  * Private allocator
231  */
dStr_resize(Dstr * ds,int n_sz,int keep)232 static void dStr_resize(Dstr *ds, int n_sz, int keep)
233 {
234    if (n_sz >= 0) {
235       if (keep && n_sz > ds->len) {
236          ds->str = (Dstr_char_t*) dRealloc (ds->str, n_sz*sizeof(Dstr_char_t));
237          ds->sz = n_sz;
238       } else {
239          dFree(ds->str);
240          ds->str = dNew(Dstr_char_t, n_sz);
241          ds->sz = n_sz;
242          ds->len = 0;
243          ds->str[0] = 0;
244       }
245    }
246 }
247 
248 /*
249  * Create a new string with a given size.
250  * Initialized to ""
251  */
dStr_sized_new(int sz)252 Dstr *dStr_sized_new (int sz)
253 {
254    Dstr *ds;
255    if (sz < 2)
256       sz = 2;
257 
258    ds = dNew(Dstr, 1);
259    ds->str = NULL;
260    dStr_resize(ds, sz + 1, 0); /* (sz + 1) for the extra '\0' */
261    return ds;
262 }
263 
264 /*
265  * Return memory if there's too much allocated
266  */
dStr_fit(Dstr * ds)267 void dStr_fit (Dstr *ds)
268 {
269    dStr_resize(ds, ds->len + 1, 1);
270 }
271 
272 /*
273  * Insert a C string, at a given position, into a Dstr (providing length).
274  * Note: It also works with embedded nil characters.
275  */
dStr_insert_l(Dstr * ds,int pos_0,const char * s,int l)276 void dStr_insert_l (Dstr *ds, int pos_0, const char *s, int l)
277 {
278    int n_sz;
279 
280    if (ds && s && l && pos_0 >= 0 && pos_0 <= ds->len) {
281       for (n_sz = ds->sz; ds->len + l >= n_sz; n_sz *= 2);
282       if (n_sz > ds->sz) {
283          dStr_resize(ds, n_sz, (ds->len > 0) ? 1 : 0);
284       }
285       if (pos_0 < ds->len)
286          memmove(ds->str+pos_0+l, ds->str+pos_0, ds->len-pos_0);
287       memcpy(ds->str+pos_0, s, l);
288       ds->len += l;
289       ds->str[ds->len] = 0;
290    }
291 }
292 
293 /*
294  * Insert a C string, at a given position, into a Dstr
295  */
dStr_insert(Dstr * ds,int pos_0,const char * s)296 void dStr_insert (Dstr *ds, int pos_0, const char *s)
297 {
298    if (s)
299       dStr_insert_l(ds, pos_0, s, strlen(s));
300 }
301 
302 /*
303  * Append a C string to a Dstr (providing length).
304  * Note: It also works with embedded nil characters.
305  */
dStr_append_l(Dstr * ds,const char * s,int l)306 void dStr_append_l (Dstr *ds, const char *s, int l)
307 {
308    dStr_insert_l(ds, ds->len, s, l);
309 }
310 
311 /*
312  * Append a C string to a Dstr.
313  */
dStr_append(Dstr * ds,const char * s)314 void dStr_append (Dstr *ds, const char *s)
315 {
316    dStr_append_l(ds, s, strlen(s));
317 }
318 
319 /*
320  * Create a new string.
321  * Initialized to 's' or empty if 's == NULL'
322  */
dStr_new(const char * s)323 Dstr *dStr_new (const char *s)
324 {
325    Dstr *ds = dStr_sized_new(0);
326    if (s && *s)
327       dStr_append(ds, s);
328    return ds;
329 }
330 
331 /*
332  * Free a dillo string.
333  * if 'all' free everything, else free the structure only.
334  */
dStr_free(Dstr * ds,int all)335 void dStr_free (Dstr *ds, int all)
336 {
337    if (ds) {
338       if (all)
339          dFree(ds->str);
340       dFree(ds);
341    }
342 }
343 
344 /*
345  * Append one character.
346  */
dStr_append_c(Dstr * ds,int c)347 void dStr_append_c (Dstr *ds, int c)
348 {
349    char cs[2];
350 
351    if (ds) {
352       if (ds->sz > ds->len + 1) {
353          ds->str[ds->len++] = (Dstr_char_t)c;
354          ds->str[ds->len] = 0;
355       } else {
356          cs[0] = (Dstr_char_t)c;
357          cs[1] = 0;
358          dStr_append_l (ds, cs, 1);
359       }
360    }
361 }
362 
363 /*
364  * Truncate a Dstr to be 'len' bytes long.
365  */
dStr_truncate(Dstr * ds,int len)366 void dStr_truncate (Dstr *ds, int len)
367 {
368    if (ds && len < ds->len) {
369       ds->str[len] = 0;
370       ds->len = len;
371    }
372 }
373 
374 /*
375  * Clear a Dstr.
376  */
dStr_shred(Dstr * ds)377 void dStr_shred (Dstr *ds)
378 {
379    if (ds && ds->sz > 0)
380       memset(ds->str, '\0', ds->sz);
381 }
382 
383 /*
384  * Erase a substring.
385  */
dStr_erase(Dstr * ds,int pos_0,int len)386 void dStr_erase (Dstr *ds, int pos_0, int len)
387 {
388    if (ds && pos_0 >= 0 && len > 0 && pos_0 + len <= ds->len) {
389       memmove(ds->str + pos_0, ds->str + pos_0 + len, ds->len - pos_0 - len);
390       ds->len -= len;
391       ds->str[ds->len] = 0;
392    }
393 }
394 
395 /*
396  * vsprintf-like function that appends.
397  * Used by: dStr_vsprintf(), dStr_sprintf() and dStr_sprintfa().
398  */
dStr_vsprintfa(Dstr * ds,const char * format,va_list argp)399 void dStr_vsprintfa (Dstr *ds, const char *format, va_list argp)
400 {
401    int n, n_sz;
402 
403    if (ds && format) {
404       va_list argp2;         /* Needed in case of looping on non-32bit arch */
405       while (1) {
406          va_copy(argp2, argp);
407          n = vsnprintf(ds->str + ds->len, ds->sz - ds->len, format, argp2);
408          va_end(argp2);
409 #if defined(__sgi)
410          /* IRIX does not conform to C99; if the entire argument did not fit
411           * into the buffer, n = buffer space used (minus 1 for terminator)
412           */
413          if (n > -1 && n + 1 < ds->sz - ds->len) {
414             ds->len += n;      /* Success! */
415             break;
416          } else {
417             n_sz = ds->sz * 2;
418          }
419 #else
420          if (n > -1 && n < ds->sz - ds->len) {
421             ds->len += n;      /* Success! */
422             break;
423          } else if (n > -1) {  /* glibc >= 2.1 */
424             n_sz = ds->len + n + 1;
425          } else {              /* old glibc */
426             n_sz = ds->sz * 2;
427          }
428 #endif
429          dStr_resize(ds, n_sz, (ds->len > 0) ? 1 : 0);
430       }
431    }
432 }
433 
434 /*
435  * vsprintf-like function.
436  */
dStr_vsprintf(Dstr * ds,const char * format,va_list argp)437 void dStr_vsprintf (Dstr *ds, const char *format, va_list argp)
438 {
439    if (ds) {
440       dStr_truncate(ds, 0);
441       dStr_vsprintfa(ds, format, argp);
442    }
443 }
444 
445 /*
446  * Printf-like function
447  */
dStr_sprintf(Dstr * ds,const char * format,...)448 void dStr_sprintf (Dstr *ds, const char *format, ...)
449 {
450    va_list argp;
451 
452    if (ds && format) {
453       va_start(argp, format);
454       dStr_vsprintf(ds, format, argp);
455       va_end(argp);
456    }
457 }
458 
459 /*
460  * Printf-like function that appends.
461  */
dStr_sprintfa(Dstr * ds,const char * format,...)462 void dStr_sprintfa (Dstr *ds, const char *format, ...)
463 {
464    va_list argp;
465 
466    if (ds && format) {
467       va_start(argp, format);
468       dStr_vsprintfa(ds, format, argp);
469       va_end(argp);
470    }
471 }
472 
473 /*
474  * Compare two dStrs.
475  */
dStr_cmp(Dstr * ds1,Dstr * ds2)476 int dStr_cmp(Dstr *ds1, Dstr *ds2)
477 {
478    int ret = 0;
479 
480    if (ds1 && ds2)
481       ret = memcmp(ds1->str, ds2->str, MIN(ds1->len+1, ds2->len+1));
482    return ret;
483 }
484 
485 /*
486  * Return a pointer to the first occurrence of needle in haystack.
487  */
dStr_memmem(Dstr * haystack,Dstr * needle)488 char *dStr_memmem(Dstr *haystack, Dstr *needle)
489 {
490    int i;
491 
492    if (needle && haystack) {
493       if (needle->len == 0)
494          return haystack->str;
495 
496       for (i = 0; i <= (haystack->len - needle->len); i++) {
497          if (haystack->str[i] == needle->str[0] &&
498              !memcmp(haystack->str + i, needle->str, needle->len))
499             return haystack->str + i;
500       }
501    }
502    return NULL;
503 }
504 
505 /*
506  * Return a printable representation of the provided Dstr, limited to a length
507  * of roughly maxlen.
508  *
509  * This is NOT threadsafe.
510  */
dStr_printable(Dstr * in,int maxlen)511 const char *dStr_printable(Dstr *in, int maxlen)
512 {
513    int i;
514    static const char *const HEX = "0123456789ABCDEF";
515    static Dstr *out = NULL;
516 
517    if (in == NULL)
518       return NULL;
519 
520    if (out)
521       dStr_truncate(out, 0);
522    else
523       out = dStr_sized_new(in->len);
524 
525    for (i = 0; (i < in->len) && (out->len < maxlen); ++i) {
526       if (isprint(in->str[i]) || (in->str[i] == '\n')) {
527          dStr_append_c(out, in->str[i]);
528       } else {
529          dStr_append_l(out, "\\x", 2);
530          dStr_append_c(out, HEX[(in->str[i] >> 4) & 15]);
531          dStr_append_c(out, HEX[in->str[i] & 15]);
532       }
533    }
534    if (out->len >= maxlen)
535       dStr_append(out, "...");
536    return out->str;
537 }
538 
539 /*
540  *- dList ---------------------------------------------------------------------
541  */
542 
543 /*
544  * Create a new empty list
545  */
dList_new(int size)546 Dlist *dList_new(int size)
547 {
548    Dlist *l;
549    if (size <= 0)
550       return NULL;
551 
552    l = dNew(Dlist, 1);
553    l->len = 0;
554    l->sz = size;
555    l->list = dNew(void*, l->sz);
556    return l;
557 }
558 
559 /*
560  * Free a list (not its elements)
561  */
dList_free(Dlist * lp)562 void dList_free (Dlist *lp)
563 {
564    if (!lp)
565       return;
566 
567    dFree(lp->list);
568    dFree(lp);
569 }
570 
571 /*
572  * Insert an element at a given position [0 based]
573  */
dList_insert_pos(Dlist * lp,void * data,int pos0)574 void dList_insert_pos (Dlist *lp, void *data, int pos0)
575 {
576    int i;
577 
578    if (!lp || pos0 < 0 || pos0 > lp->len)
579       return;
580 
581    if (lp->sz == lp->len) {
582       lp->sz *= 2;
583       lp->list = (void**) dRealloc (lp->list, lp->sz*sizeof(void*));
584    }
585    ++lp->len;
586 
587    for (i = lp->len - 1; i > pos0; --i)
588       lp->list[i] = lp->list[i - 1];
589    lp->list[pos0] = data;
590 }
591 
592 /*
593  * Append a data item to the list
594  */
dList_append(Dlist * lp,void * data)595 void dList_append (Dlist *lp, void *data)
596 {
597    dList_insert_pos(lp, data, lp->len);
598 }
599 
600 /*
601  * Prepend a data item to the list
602  */
dList_prepend(Dlist * lp,void * data)603 void dList_prepend (Dlist *lp, void *data)
604 {
605    dList_insert_pos(lp, data, 0);
606 }
607 
608 /*
609  * For completing the ADT.
610  */
dList_length(Dlist * lp)611 int dList_length (Dlist *lp)
612 {
613    if (!lp)
614       return 0;
615    return lp->len;
616 }
617 
618 /*
619  * Remove a data item without preserving order.
620  */
dList_remove_fast(Dlist * lp,const void * data)621 void dList_remove_fast (Dlist *lp, const void *data)
622 {
623    int i;
624 
625    if (!lp)
626       return;
627 
628    for (i = 0; i < lp->len; ++i) {
629       if (lp->list[i] == data) {
630          lp->list[i] = lp->list[--lp->len];
631          break;
632       }
633    }
634 }
635 
636 /*
637  * Remove a data item preserving order.
638  */
dList_remove(Dlist * lp,const void * data)639 void dList_remove (Dlist *lp, const void *data)
640 {
641    int i, j;
642 
643    if (!lp)
644       return;
645 
646    for (i = 0; i < lp->len; ++i) {
647       if (lp->list[i] == data) {
648          --lp->len;
649          for (j = i; j < lp->len; ++j)
650             lp->list[j] = lp->list[j + 1];
651          break;
652       }
653    }
654 }
655 
656 /*
657  * Return the nth data item,
658  * NULL when not found or 'n0' is out of range
659  */
dList_nth_data(Dlist * lp,int n0)660 void *dList_nth_data (Dlist *lp, int n0)
661 {
662    if (!lp || n0 < 0 || n0 >= lp->len)
663       return NULL;
664    return lp->list[n0];
665 }
666 
667 /*
668  * Return the found data item, or NULL if not present.
669  */
dList_find(Dlist * lp,const void * data)670 void *dList_find (Dlist *lp, const void *data)
671 {
672    int i = dList_find_idx(lp, data);
673    return (i >= 0) ? lp->list[i] : NULL;
674 }
675 
676 /*
677  * Search a data item.
678  * Return value: its index if found, -1 if not present.
679  * (this is useful for a list of integers, for finding number zero).
680  */
dList_find_idx(Dlist * lp,const void * data)681 int dList_find_idx (Dlist *lp, const void *data)
682 {
683    int i, ret = -1;
684 
685    if (!lp)
686       return ret;
687 
688    for (i = 0; i < lp->len; ++i) {
689       if (lp->list[i] == data) {
690          ret = i;
691          break;
692       }
693    }
694    return ret;
695 }
696 
697 /*
698  * Search a data item using a custom function.
699  * func() is given the list item and the user data as parameters.
700  * Return: data item when found, NULL otherwise.
701  */
dList_find_custom(Dlist * lp,const void * data,dCompareFunc func)702 void *dList_find_custom (Dlist *lp, const void *data, dCompareFunc func)
703 {
704    int i;
705    void *ret = NULL;
706 
707    if (!lp)
708       return ret;
709 
710    for (i = 0; i < lp->len; ++i) {
711       if (func(lp->list[i], data) == 0) {
712          ret = lp->list[i];
713          break;
714       }
715    }
716    return ret;
717 }
718 
719 /*
720  * QuickSort implementation.
721  * This allows for a simple compare function for all the ADT.
722  */
QuickSort(void ** left,void ** right,dCompareFunc compare)723 static void QuickSort(void **left, void **right, dCompareFunc compare)
724 {
725   void **p = left, **q = right, **t = left;
726 
727   while (1) {
728      while (p != t && compare(*p, *t) < 0)
729         ++p;
730      while (q != t && compare(*q, *t) > 0)
731         --q;
732      if (p > q)
733         break;
734      if (p < q) {
735         void *tmp = *p;
736         *p = *q;
737         *q = tmp;
738         if (t == p)
739            t = q;
740         else if (t == q)
741            t = p;
742      }
743      if (++p > --q)
744         break;
745   }
746 
747   if (left < q)
748      QuickSort(left, q, compare);
749   if (p < right)
750      QuickSort(p, right, compare);
751 }
752 
753 /*
754  * Sort the list using a custom function
755  */
dList_sort(Dlist * lp,dCompareFunc func)756 void dList_sort (Dlist *lp, dCompareFunc func)
757 {
758    if (lp && lp->len > 1) {
759       QuickSort(lp->list, lp->list + lp->len - 1, func);
760    }
761 }
762 
763 /*
764  * Insert an element into a sorted list.
765  * The comparison function receives two list elements.
766  */
dList_insert_sorted(Dlist * lp,void * data,dCompareFunc func)767 void dList_insert_sorted (Dlist *lp, void *data, dCompareFunc func)
768 {
769    int i, st, min, max;
770 
771    if (lp) {
772       min = st = i = 0;
773       max = lp->len - 1;
774       while (min <= max) {
775          i = (min + max) / 2;
776          st = func(lp->list[i], data);
777          if (st < 0) {
778             min = i + 1;
779          } else if (st > 0) {
780             max = i - 1;
781          } else {
782             break;
783          }
784       }
785       dList_insert_pos(lp, data, (st >= 0) ? i : i+1);
786    }
787 }
788 
789 /*
790  * Search a sorted list.
791  * Return the found data item, or NULL if not present.
792  * func() is given the list item and the user data as parameters.
793  */
dList_find_sorted(Dlist * lp,const void * data,dCompareFunc func)794 void *dList_find_sorted (Dlist *lp, const void *data, dCompareFunc func)
795 {
796    int i, st, min, max;
797    void *ret = NULL;
798 
799    if (lp && lp->len) {
800       min = 0;
801       max = lp->len - 1;
802       while (min <= max) {
803          i = (min + max) / 2;
804          st = func(lp->list[i], data);
805          if (st < 0) {
806             min = i + 1;
807          } else if (st > 0) {
808             max = i - 1;
809          } else {
810             ret = lp->list[i];
811             break;
812          }
813       }
814    }
815 
816    return ret;
817 }
818 
819 /*
820  *- Parse function ------------------------------------------------------------
821  */
822 
823 /*
824  * Take a dillo rc line and return 'name' and 'value' pointers to it.
825  * Notes:
826  *    - line is modified!
827  *    - it skips blank lines and lines starting with '#'
828  *
829  * Return value: 1 on blank line or comment, 0 on successful value/pair,
830  *              -1 otherwise.
831  */
dParser_parse_rc_line(char ** line,char ** name,char ** value)832 int dParser_parse_rc_line(char **line, char **name, char **value)
833 {
834    char *eq, *p;
835    int len, ret = -1;
836 
837    dReturn_val_if_fail(*line, ret);
838 
839    *name = NULL;
840    *value = NULL;
841    dStrstrip(*line);
842    if (!*line[0] || *line[0] == '#') {
843       /* blank line or comment */
844       ret = 1;
845    } else if ((eq = strchr(*line, '='))) {
846       /* get name */
847       for (p = *line; *p && *p != '=' && !dIsspace(*p); ++p);
848       *p = 0;
849       *name = *line;
850 
851       /* skip whitespace */
852       if (p < eq)
853          for (++p; dIsspace(*p); ++p);
854 
855       /* get value */
856       if (p == eq) {
857          for (++p; dIsspace(*p); ++p);
858          len = strlen(p);
859          if (len >= 2 && *p == '"' && p[len-1] == '"') {
860             p[len-1] = 0;
861             ++p;
862          }
863          *value = p;
864          ret = 0;
865       }
866    }
867 
868    return ret;
869 }
870 
871 /*
872  *- Dlib messages -------------------------------------------------------------
873  */
dLib_show_messages(bool_t show)874 void dLib_show_messages(bool_t show)
875 {
876    dLib_show_msg = show;
877 }
878 
879 /*
880  *- Misc utility functions ----------------------------------------------------
881  */
882 
883 /*
884  * Return the current working directory in a new string
885  */
dGetcwd()886 char *dGetcwd ()
887 {
888   size_t size = 128;
889 
890   while (1) {
891       char *buffer = dNew(char, size);
892       if (getcwd (buffer, size) == buffer)
893         return buffer;
894       dFree (buffer);
895       if (errno != ERANGE)
896         return 0;
897       size *= 2;
898   }
899 }
900 
901 /*
902  * Return the home directory in a static string (don't free)
903  */
dGethomedir()904 char *dGethomedir ()
905 {
906    static char *homedir = NULL;
907 
908    if (!homedir) {
909       if (getenv("HOME")) {
910          homedir = dStrdup(getenv("HOME"));
911 
912       } else if (getenv("HOMEDRIVE") && getenv("HOMEPATH")) {
913          homedir = dStrconcat(getenv("HOMEDRIVE"), getenv("HOMEPATH"), NULL);
914       } else {
915          DLIB_MSG("dGethomedir: $HOME not set, using '/'.\n");
916          homedir = dStrdup("/");
917       }
918    }
919    return homedir;
920 }
921 
922 /*
923  * Get a line from a FILE stream.
924  * Return value: read line on success, NULL on EOF.
925  */
dGetline(FILE * stream)926 char *dGetline (FILE *stream)
927 {
928    int ch;
929    Dstr *dstr;
930    char *line;
931 
932    dReturn_val_if_fail (stream, 0);
933 
934    dstr = dStr_sized_new(64);
935    while ((ch = fgetc(stream)) != EOF) {
936       dStr_append_c(dstr, ch);
937       if (ch == '\n')
938          break;
939    }
940 
941    line = (dstr->len) ? dstr->str : NULL;
942    dStr_free(dstr, (line) ? 0 : 1);
943    return line;
944 }
945 
946 /*
947  * Close a FD handling EINTR.
948  */
dClose(int fd)949 int dClose(int fd)
950 {
951    int st;
952 
953    do
954       st = close(fd);
955    while (st == -1 && errno == EINTR);
956    return st;
957 }
958