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, §ion, 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