1 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
2  * All rights reserved.
3  *
4  * This package is an SSL implementation written
5  * by Eric Young (eay@cryptsoft.com).
6  * The implementation was written so as to conform with Netscapes SSL.
7  *
8  * This library is free for commercial and non-commercial use as long as
9  * the following conditions are aheared to.  The following conditions
10  * apply to all code found in this distribution, be it the RC4, RSA,
11  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
12  * included with this distribution is covered by the same copyright terms
13  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
14  *
15  * Copyright remains Eric Young's, and as such any Copyright notices in
16  * the code are not to be removed.
17  * If this package is used in a product, Eric Young should be given attribution
18  * as the author of the parts of the library used.
19  * This can be in the form of a textual message at program startup or
20  * in documentation (online or textual) provided with the package.
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions
24  * are met:
25  * 1. Redistributions of source code must retain the copyright
26  *    notice, this list of conditions and the following disclaimer.
27  * 2. Redistributions in binary form must reproduce the above copyright
28  *    notice, this list of conditions and the following disclaimer in the
29  *    documentation and/or other materials provided with the distribution.
30  * 3. All advertising materials mentioning features or use of this software
31  *    must display the following acknowledgement:
32  *    "This product includes cryptographic software written by
33  *     Eric Young (eay@cryptsoft.com)"
34  *    The word 'cryptographic' can be left out if the rouines from the library
35  *    being used are not cryptographic related :-).
36  * 4. If you include any Windows specific code (or a derivative thereof) from
37  *    the apps directory (application code) you must include an acknowledgement:
38  *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
39  *
40  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
41  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
44  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50  * SUCH DAMAGE.
51  *
52  * The licence and distribution terms for any publically available version or
53  * derivative of this code cannot be changed.  i.e. this code cannot simply be
54  * copied and put under another distribution licence
55  * [including the GNU Public Licence.] */
56 
57 #include <openssl/conf.h>
58 
59 #include <string.h>
60 #include <ctype.h>
61 
62 #include <openssl/bio.h>
63 #include <openssl/buf.h>
64 #include <openssl/err.h>
65 #include <openssl/mem.h>
66 
67 #include "conf_def.h"
68 
69 
conf_value_hash(const CONF_VALUE * v)70 static uint32_t conf_value_hash(const CONF_VALUE *v) {
71   return (lh_strhash(v->section) << 2) ^ lh_strhash(v->name);
72 }
73 
conf_value_cmp(const CONF_VALUE * a,const CONF_VALUE * b)74 static int conf_value_cmp(const CONF_VALUE *a, const CONF_VALUE *b) {
75   int i;
76 
77   if (a->section != b->section) {
78     i = strcmp(a->section, b->section);
79     if (i) {
80       return i;
81     }
82   }
83 
84   if (a->name != NULL && b->name != NULL) {
85     return strcmp(a->name, b->name);
86   } else if (a->name == b->name) {
87     return 0;
88   } else {
89     return (a->name == NULL) ? -1 : 1;
90   }
91 }
92 
NCONF_new(void * method)93 CONF *NCONF_new(void *method) {
94   CONF *conf;
95 
96   if (method != NULL) {
97     return NULL;
98   }
99 
100   conf = OPENSSL_malloc(sizeof(CONF));
101   if (conf == NULL) {
102     return NULL;
103   }
104 
105   conf->data = lh_CONF_VALUE_new(conf_value_hash, conf_value_cmp);
106   if (conf->data == NULL) {
107     OPENSSL_free(conf);
108     return NULL;
109   }
110 
111   return conf;
112 }
113 
value_free_contents(CONF_VALUE * value)114 static void value_free_contents(CONF_VALUE *value) {
115   if (value->section) {
116     OPENSSL_free(value->section);
117   }
118   if (value->name) {
119     OPENSSL_free(value->name);
120     if (value->value) {
121       OPENSSL_free(value->value);
122     }
123   } else {
124     if (value->value) {
125       sk_CONF_VALUE_free((STACK_OF(CONF_VALUE)*)value->value);
126     }
127   }
128 }
129 
value_free(CONF_VALUE * value)130 static void value_free(CONF_VALUE *value) {
131   value_free_contents(value);
132   OPENSSL_free(value);
133 }
134 
NCONF_free(CONF * conf)135 void NCONF_free(CONF *conf) {
136   if (conf == NULL || conf->data == NULL) {
137     return;
138   }
139 
140   lh_CONF_VALUE_doall(conf->data, value_free_contents);
141   lh_CONF_VALUE_free(conf->data);
142   OPENSSL_free(conf);
143 }
144 
NCONF_new_section(const CONF * conf,const char * section)145 CONF_VALUE *NCONF_new_section(const CONF *conf, const char *section) {
146   STACK_OF(CONF_VALUE) *sk = NULL;
147   int ok = 0, i;
148   CONF_VALUE *v = NULL, *old_value;
149 
150   sk = sk_CONF_VALUE_new_null();
151   v = OPENSSL_malloc(sizeof(CONF_VALUE));
152   if (sk == NULL || v == NULL) {
153     goto err;
154   }
155   i = strlen(section) + 1;
156   v->section = OPENSSL_malloc(i);
157   if (v->section == NULL) {
158     goto err;
159   }
160 
161   memcpy(v->section, section, i);
162   v->section[i-1] = 0;
163   v->name = NULL;
164   v->value = (char *)sk;
165 
166   if (!lh_CONF_VALUE_insert(conf->data, &old_value, v)) {
167     goto err;
168   }
169   if (old_value) {
170     value_free(old_value);
171   }
172   ok = 1;
173 
174 err:
175   if (!ok) {
176     if (sk != NULL) {
177       sk_CONF_VALUE_free(sk);
178     }
179     if (v != NULL) {
180       OPENSSL_free(v);
181     }
182     v = NULL;
183   }
184   return v;
185 }
186 
str_copy(CONF * conf,char * section,char ** pto,char * from)187 static int str_copy(CONF *conf, char *section, char **pto, char *from) {
188   int q, r, rr = 0, to = 0, len = 0;
189   char *s, *e, *rp, *rrp, *np, *cp, v;
190   const char *p;
191   BUF_MEM *buf;
192 
193   buf = BUF_MEM_new();
194   if (buf == NULL) {
195     return 0;
196   }
197 
198   len = strlen(from) + 1;
199   if (!BUF_MEM_grow(buf, len)) {
200     goto err;
201   }
202 
203   for (;;) {
204     if (IS_QUOTE(conf, *from)) {
205       q = *from;
206       from++;
207       while (!IS_EOF(conf, *from) && (*from != q)) {
208         if (IS_ESC(conf, *from)) {
209           from++;
210           if (IS_EOF(conf, *from)) {
211             break;
212           }
213         }
214         buf->data[to++] = *(from++);
215       }
216       if (*from == q) {
217         from++;
218       }
219     } else if (IS_DQUOTE(conf, *from)) {
220       q = *from;
221       from++;
222       while (!IS_EOF(conf, *from)) {
223         if (*from == q) {
224           if (*(from + 1) == q) {
225             from++;
226           } else {
227             break;
228           }
229         }
230         buf->data[to++] = *(from++);
231       }
232       if (*from == q) {
233         from++;
234       }
235     } else if (IS_ESC(conf, *from)) {
236       from++;
237       v = *(from++);
238       if (IS_EOF(conf, v)) {
239         break;
240       } else if (v == 'r') {
241         v = '\r';
242       } else if (v == 'n') {
243         v = '\n';
244       } else if (v == 'b') {
245         v = '\b';
246       } else if (v == 't') {
247         v = '\t';
248       }
249       buf->data[to++] = v;
250     } else if (IS_EOF(conf, *from)) {
251       break;
252     } else if (*from == '$') {
253       /* try to expand it */
254       rrp = NULL;
255       s = &(from[1]);
256       if (*s == '{') {
257         q = '}';
258       } else if (*s == '(') {
259         q = ')';
260       } else {
261         q = 0;
262       }
263 
264       if (q) {
265         s++;
266       }
267       cp = section;
268       e = np = s;
269       while (IS_ALPHA_NUMERIC(conf, *e)) {
270         e++;
271       }
272       if (e[0] == ':' && e[1] == ':') {
273         cp = np;
274         rrp = e;
275         rr = *e;
276         *rrp = '\0';
277         e += 2;
278         np = e;
279         while (IS_ALPHA_NUMERIC(conf, *e)) {
280           e++;
281         }
282       }
283       r = *e;
284       *e = '\0';
285       rp = e;
286       if (q) {
287         if (r != q) {
288           OPENSSL_PUT_ERROR(CONF, str_copy, CONF_R_NO_CLOSE_BRACE);
289           goto err;
290         }
291         e++;
292       }
293       /* So at this point we have
294        * np which is the start of the name string which is
295        *   '\0' terminated.
296        * cp which is the start of the section string which is
297        *   '\0' terminated.
298        * e is the 'next point after'.
299        * r and rr are the chars replaced by the '\0'
300        * rp and rrp is where 'r' and 'rr' came from. */
301       p = NCONF_get_string(conf, cp, np);
302       if (rrp != NULL) {
303         *rrp = rr;
304       }
305       *rp = r;
306       if (p == NULL) {
307         OPENSSL_PUT_ERROR(CONF, str_copy, CONF_R_VARIABLE_HAS_NO_VALUE);
308         goto err;
309       }
310       BUF_MEM_grow_clean(buf, (strlen(p) + buf->length - (e - from)));
311       while (*p) {
312         buf->data[to++] = *(p++);
313       }
314 
315       /* Since we change the pointer 'from', we also have
316          to change the perceived length of the string it
317          points at.  /RL */
318       len -= e - from;
319       from = e;
320 
321       /* In case there were no braces or parenthesis around
322          the variable reference, we have to put back the
323          character that was replaced with a '\0'.  /RL */
324       *rp = r;
325     } else {
326       buf->data[to++] = *(from++);
327     }
328   }
329 
330   buf->data[to] = '\0';
331   if (*pto != NULL) {
332     OPENSSL_free(*pto);
333   }
334   *pto = buf->data;
335   OPENSSL_free(buf);
336   return 1;
337 
338 err:
339   if (buf != NULL) {
340     BUF_MEM_free(buf);
341   }
342   return 0;
343 }
344 
get_section(const CONF * conf,const char * section)345 static CONF_VALUE *get_section(const CONF *conf, const char *section) {
346   CONF_VALUE template;
347 
348   memset(&template, 0, sizeof(template));
349   template.section = (char *) section;
350   return lh_CONF_VALUE_retrieve(conf->data, &template);
351 }
352 
STACK_OF(CONF_VALUE)353 STACK_OF(CONF_VALUE) *NCONF_get_section(const CONF *conf, const char *section) {
354   CONF_VALUE *section_value = get_section(conf, section);
355   if (section_value == NULL) {
356     return NULL;
357   }
358   return (STACK_OF(CONF_VALUE)*) section_value->value;
359 }
360 
NCONF_get_string(const CONF * conf,const char * section,const char * name)361 const char *NCONF_get_string(const CONF *conf, const char *section,
362                              const char *name) {
363   CONF_VALUE template, *value;
364 
365   memset(&template, 0, sizeof(template));
366   template.section = (char *) section;
367   template.name = (char *) name;
368   value = lh_CONF_VALUE_retrieve(conf->data, &template);
369   if (value == NULL) {
370     return NULL;
371   }
372   return value->value;
373 }
374 
add_string(const CONF * conf,CONF_VALUE * section,CONF_VALUE * value)375 int add_string(const CONF *conf, CONF_VALUE *section, CONF_VALUE *value) {
376   STACK_OF(CONF_VALUE) *section_stack = (STACK_OF(CONF_VALUE)*) section->value;
377   CONF_VALUE *old_value;
378 
379   value->section = section->section;
380   if (!sk_CONF_VALUE_push(section_stack, value)) {
381     return 0;
382   }
383 
384   if (!lh_CONF_VALUE_insert(conf->data, &old_value, value)) {
385     return 0;
386   }
387   if (old_value != NULL) {
388     (void)sk_CONF_VALUE_delete_ptr(section_stack, old_value);
389     value_free(old_value);
390   }
391 
392   return 1;
393 }
394 
eat_ws(CONF * conf,char * p)395 static char *eat_ws(CONF *conf, char *p) {
396   while (IS_WS(conf, *p) && !IS_EOF(conf, *p)) {
397     p++;
398   }
399   return p;
400 }
401 
402 #define scan_esc(conf, p) (((IS_EOF((conf), (p)[1])) ? ((p) + 1) : ((p) + 2)))
403 
eat_alpha_numeric(CONF * conf,char * p)404 static char *eat_alpha_numeric(CONF *conf, char *p) {
405   for (;;) {
406     if (IS_ESC(conf, *p)) {
407       p = scan_esc(conf, p);
408       continue;
409     }
410     if (!IS_ALPHA_NUMERIC_PUNCT(conf, *p)) {
411       return p;
412     }
413     p++;
414   }
415 }
416 
scan_quote(CONF * conf,char * p)417 static char *scan_quote(CONF *conf, char *p) {
418   int q = *p;
419 
420   p++;
421   while (!IS_EOF(conf, *p) && *p != q) {
422     if (IS_ESC(conf, *p)) {
423       p++;
424       if (IS_EOF(conf, *p)) {
425         return p;
426       }
427     }
428     p++;
429   }
430   if (*p == q) {
431     p++;
432   }
433   return p;
434 }
435 
436 
scan_dquote(CONF * conf,char * p)437 static char *scan_dquote(CONF *conf, char *p) {
438   int q = *p;
439 
440   p++;
441   while (!(IS_EOF(conf, *p))) {
442     if (*p == q) {
443       if (*(p + 1) == q) {
444         p++;
445       } else {
446         break;
447       }
448     }
449     p++;
450   }
451   if (*p == q) {
452     p++;
453   }
454   return p;
455 }
456 
clear_comments(CONF * conf,char * p)457 static void clear_comments(CONF *conf, char *p) {
458   for (;;) {
459     if (IS_FCOMMENT(conf, *p)) {
460       *p = '\0';
461       return;
462     }
463     if (!IS_WS(conf, *p)) {
464       break;
465     }
466     p++;
467   }
468 
469   for (;;) {
470     if (IS_COMMENT(conf, *p)) {
471       *p = '\0';
472       return;
473     }
474     if (IS_DQUOTE(conf, *p)) {
475       p = scan_dquote(conf, p);
476       continue;
477     }
478     if (IS_QUOTE(conf, *p)) {
479       p = scan_quote(conf, p);
480       continue;
481     }
482     if (IS_ESC(conf, *p)) {
483       p = scan_esc(conf, p);
484       continue;
485     }
486     if (IS_EOF(conf, *p)) {
487       return;
488     } else {
489       p++;
490     }
491   }
492 }
493 
def_load_bio(CONF * conf,BIO * in,long * out_error_line)494 static int def_load_bio(CONF *conf, BIO *in, long *out_error_line) {
495   static const size_t CONFBUFSIZE = 512;
496   int bufnum = 0, i, ii;
497   BUF_MEM *buff = NULL;
498   char *s, *p, *end;
499   int again;
500   long eline = 0;
501   char btmp[DECIMAL_SIZE(eline) + 1];
502   CONF_VALUE *v = NULL, *tv;
503   CONF_VALUE *sv = NULL;
504   char *section = NULL, *buf;
505   char *start, *psection, *pname;
506 
507   if ((buff = BUF_MEM_new()) == NULL) {
508     OPENSSL_PUT_ERROR(CONF, def_load_bio, ERR_R_BUF_LIB);
509     goto err;
510   }
511 
512   section = (char *)OPENSSL_malloc(10);
513   if (section == NULL) {
514     OPENSSL_PUT_ERROR(CONF, def_load_bio, ERR_R_MALLOC_FAILURE);
515     goto err;
516   }
517   BUF_strlcpy(section, "default", 10);
518 
519   sv = NCONF_new_section(conf, section);
520   if (sv == NULL) {
521     OPENSSL_PUT_ERROR(CONF, def_load_bio, CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
522     goto err;
523   }
524 
525   bufnum = 0;
526   again = 0;
527   for (;;) {
528     if (!BUF_MEM_grow(buff, bufnum + CONFBUFSIZE)) {
529       OPENSSL_PUT_ERROR(CONF, def_load_bio, ERR_R_BUF_LIB);
530       goto err;
531     }
532     p = &(buff->data[bufnum]);
533     *p = '\0';
534     BIO_gets(in, p, CONFBUFSIZE - 1);
535     p[CONFBUFSIZE - 1] = '\0';
536     ii = i = strlen(p);
537     if (i == 0 && !again) {
538       break;
539     }
540     again = 0;
541     while (i > 0) {
542       if ((p[i - 1] != '\r') && (p[i - 1] != '\n')) {
543         break;
544       } else {
545         i--;
546       }
547     }
548     /* we removed some trailing stuff so there is a new
549      * line on the end. */
550     if (ii && i == ii) {
551       again = 1; /* long line */
552     } else {
553       p[i] = '\0';
554       eline++; /* another input line */
555     }
556 
557     /* we now have a line with trailing \r\n removed */
558 
559     /* i is the number of bytes */
560     bufnum += i;
561 
562     v = NULL;
563     /* check for line continuation */
564     if (bufnum >= 1) {
565       /* If we have bytes and the last char '\\' and
566        * second last char is not '\\' */
567       p = &(buff->data[bufnum - 1]);
568       if (IS_ESC(conf, p[0]) && ((bufnum <= 1) || !IS_ESC(conf, p[-1]))) {
569         bufnum--;
570         again = 1;
571       }
572     }
573     if (again) {
574       continue;
575     }
576     bufnum = 0;
577     buf = buff->data;
578 
579     clear_comments(conf, buf);
580     s = eat_ws(conf, buf);
581     if (IS_EOF(conf, *s)) {
582       continue; /* blank line */
583     }
584     if (*s == '[') {
585       char *ss;
586 
587       s++;
588       start = eat_ws(conf, s);
589       ss = start;
590     again:
591       end = eat_alpha_numeric(conf, ss);
592       p = eat_ws(conf, end);
593       if (*p != ']') {
594         if (*p != '\0' && ss != p) {
595           ss = p;
596           goto again;
597         }
598         OPENSSL_PUT_ERROR(CONF, def_load_bio, CONF_R_MISSING_CLOSE_SQUARE_BRACKET);
599         goto err;
600       }
601       *end = '\0';
602       if (!str_copy(conf, NULL, &section, start)) {
603         goto err;
604       }
605       if ((sv = get_section(conf, section)) == NULL) {
606         sv = NCONF_new_section(conf, section);
607       }
608       if (sv == NULL) {
609         OPENSSL_PUT_ERROR(CONF, def_load_bio, CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
610         goto err;
611       }
612       continue;
613     } else {
614       pname = s;
615       psection = NULL;
616       end = eat_alpha_numeric(conf, s);
617       if ((end[0] == ':') && (end[1] == ':')) {
618         *end = '\0';
619         end += 2;
620         psection = pname;
621         pname = end;
622         end = eat_alpha_numeric(conf, end);
623       }
624       p = eat_ws(conf, end);
625       if (*p != '=') {
626         OPENSSL_PUT_ERROR(CONF, def_load_bio, CONF_R_MISSING_EQUAL_SIGN);
627         goto err;
628       }
629       *end = '\0';
630       p++;
631       start = eat_ws(conf, p);
632       while (!IS_EOF(conf, *p)) {
633         p++;
634       }
635       p--;
636       while ((p != start) && (IS_WS(conf, *p))) {
637         p--;
638       }
639       p++;
640       *p = '\0';
641 
642       if (!(v = (CONF_VALUE *)OPENSSL_malloc(sizeof(CONF_VALUE)))) {
643         OPENSSL_PUT_ERROR(CONF, def_load_bio, ERR_R_MALLOC_FAILURE);
644         goto err;
645       }
646       if (psection == NULL) {
647         psection = section;
648       }
649       v->name = (char *)OPENSSL_malloc(strlen(pname) + 1);
650       v->value = NULL;
651       if (v->name == NULL) {
652         OPENSSL_PUT_ERROR(CONF, def_load_bio, ERR_R_MALLOC_FAILURE);
653         goto err;
654       }
655       BUF_strlcpy(v->name, pname, strlen(pname) + 1);
656       if (!str_copy(conf, psection, &(v->value), start)) {
657         goto err;
658       }
659 
660       if (strcmp(psection, section) != 0) {
661         if ((tv = get_section(conf, psection)) == NULL) {
662           tv = NCONF_new_section(conf, psection);
663         }
664         if (tv == NULL) {
665           OPENSSL_PUT_ERROR(CONF, def_load_bio, CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
666           goto err;
667         }
668       } else {
669         tv = sv;
670       }
671       if (add_string(conf, tv, v) == 0) {
672         OPENSSL_PUT_ERROR(CONF, def_load_bio, ERR_R_MALLOC_FAILURE);
673         goto err;
674       }
675       v = NULL;
676     }
677   }
678   if (buff != NULL) {
679     BUF_MEM_free(buff);
680   }
681   if (section != NULL) {
682     OPENSSL_free(section);
683   }
684   return 1;
685 
686 err:
687   if (buff != NULL) {
688     BUF_MEM_free(buff);
689   }
690   if (section != NULL) {
691     OPENSSL_free(section);
692   }
693   if (out_error_line != NULL) {
694     *out_error_line = eline;
695   }
696   BIO_snprintf(btmp, sizeof btmp, "%ld", eline);
697   ERR_add_error_data(2, "line ", btmp);
698 
699   if (v != NULL) {
700     if (v->name != NULL) {
701       OPENSSL_free(v->name);
702     }
703     if (v->value != NULL) {
704       OPENSSL_free(v->value);
705     }
706     if (v != NULL) {
707       OPENSSL_free(v);
708     }
709   }
710   return 0;
711 }
712 
NCONF_load(CONF * conf,const char * filename,long * out_error_line)713 int NCONF_load(CONF *conf, const char *filename, long *out_error_line) {
714   BIO *in = BIO_new_file(filename, "rb");
715   int ret;
716 
717   if (in == NULL) {
718     OPENSSL_PUT_ERROR(CONF, NCONF_load, ERR_R_SYS_LIB);
719     return 0;
720   }
721 
722   ret = def_load_bio(conf, in, out_error_line);
723   BIO_free(in);
724 
725   return ret;
726 }
727 
NCONF_load_bio(CONF * conf,BIO * bio,long * out_error_line)728 int NCONF_load_bio(CONF *conf, BIO *bio, long *out_error_line) {
729   return def_load_bio(conf, bio, out_error_line);
730 }
731 
CONF_parse_list(const char * list,char sep,int remove_whitespace,int (* list_cb)(const char * elem,int len,void * usr),void * arg)732 int CONF_parse_list(const char *list, char sep, int remove_whitespace,
733                     int (*list_cb)(const char *elem, int len, void *usr),
734                     void *arg) {
735   int ret;
736   const char *lstart, *tmpend, *p;
737 
738   if (list == NULL) {
739     OPENSSL_PUT_ERROR(CONF, CONF_parse_list, CONF_R_LIST_CANNOT_BE_NULL);
740     return 0;
741   }
742 
743   lstart = list;
744   for (;;) {
745     if (remove_whitespace) {
746       while (*lstart && isspace((unsigned char)*lstart)) {
747         lstart++;
748       }
749     }
750     p = strchr(lstart, sep);
751     if (p == lstart || !*lstart) {
752       ret = list_cb(NULL, 0, arg);
753     } else {
754       if (p) {
755         tmpend = p - 1;
756       } else {
757         tmpend = lstart + strlen(lstart) - 1;
758       }
759       if (remove_whitespace) {
760         while (isspace((unsigned char)*tmpend)) {
761           tmpend--;
762         }
763       }
764       ret = list_cb(lstart, tmpend - lstart + 1, arg);
765     }
766     if (ret <= 0) {
767       return ret;
768     }
769     if (p == NULL) {
770       return 1;
771     }
772     lstart = p + 1;
773   }
774 }
775