chunks.c 18.2 KB
Newer Older
Bernhard Link's avatar
Bernhard Link committed
1
/*  This file is part of "reprepro"
2
 *  Copyright (C) 2003,2004,2005,2007 Bernhard R. Link
3
 *  This program is free software; you can redistribute it and/or modify
4
 *  it under the terms of the GNU General Public License version 2 as
Bernhard Link's avatar
Bernhard Link committed
5
 *  published by the Free Software Foundation.
6 7 8 9 10 11 12 13
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
Bernhard Link's avatar
Bernhard Link committed
14
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1301  USA
15
 */
Bernhard Link's avatar
Bernhard Link committed
16 17
#include <config.h>

Bernhard Link's avatar
Bernhard Link committed
18
#include <errno.h>
19
#include <stdlib.h>
Bernhard Link's avatar
Bernhard Link committed
20 21 22 23
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
Bernhard Link's avatar
Bernhard Link committed
24
#include "error.h"
Bernhard Link's avatar
Bernhard Link committed
25
#include "chunks.h"
26
#include "names.h"
Bernhard Link's avatar
Bernhard Link committed
27 28

/* point to a specified field in a chunk */
29
static const char *chunk_getfield(const char *name, const char *chunk) {
30
	size_t l;
Bernhard Link's avatar
Bernhard Link committed
31

32
	if (chunk == NULL)
Bernhard Link's avatar
Bernhard Link committed
33 34
		return NULL;
	l = strlen(name);
35 36
	while (*chunk != '\0') {
		if (strncasecmp(name, chunk, l) == 0 && chunk[l] == ':') {
Bernhard Link's avatar
Bernhard Link committed
37 38 39
			chunk += l+1;
			return chunk;
		}
40
		while (*chunk != '\n' && *chunk != '\0')
Bernhard Link's avatar
Bernhard Link committed
41
			chunk++;
42
		if (*chunk == '\0')
Bernhard Link's avatar
Bernhard Link committed
43
			return NULL;
44
		chunk++;
Bernhard Link's avatar
Bernhard Link committed
45 46 47 48
	}
	return NULL;
}

49
/* get the content of the given field, including all following lines, in a format
50
 * that may be put into chunk_replacefields
51
static retvalue chunk_getcontent(const char *chunk, const char *name, char **value) {
52 53
	const char *field;
	char *val;
54
	const char *b, *e;
55

56
	assert(value != NULL);
57 58
	field = chunk_getfield(name, chunk);
	if (field == NULL)
59
		return RET_NOTHING;
60

61
	b = field;
Bernhard Link's avatar
Bernhard Link committed
62
	* jump over spaces at the beginning *
63
	if (xisspace(*b))
64
		b++;
Bernhard Link's avatar
Bernhard Link committed
65

Bernhard Link's avatar
Bernhard Link committed
66
	* search for the end *
67 68
	e = b;
	do {
69
		while (*e != '\n' && *e != '\0')
70
			e++;
71
		if (*e != '\0')
72
			e++;
73
	} while (*e != ' ' && *e != '\t' && *e != '\0');
Bernhard Link's avatar
Bernhard Link committed
74

75
	if (e > b && *e == '\0')
76
		e--;
Bernhard Link's avatar
Bernhard Link committed
77
	* remove trailing newline *
78
	if (e > b && *e == '\n')
79
		e--;
80 81
	if (e > b)
		val = strndup(b, e - b + 1);
82
	else
83
		val = strdup("");
84
	if (FAILEDTOALLOC(val))
85 86 87
		return RET_ERROR_OOM;
	*value = val;
	return RET_OK;
Bernhard Link's avatar
Bernhard Link committed
88
}
Bernhard Link's avatar
Bernhard Link committed
89
*/
Bernhard Link's avatar
Bernhard Link committed
90

91
/* look for name in chunk. returns RET_NOTHING if not found */
92
retvalue chunk_getvalue(const char *chunk, const char *name, char **value) {
93 94
	const char *field;
	char *val;
95
	const char *b, *e;
96

97
	assert(value != NULL);
98 99
	field = chunk_getfield(name, chunk);
	if (field == NULL)
100
		return RET_NOTHING;
101 102

	b = field;
103
	/* jump over spaces at the beginning */
104
	while (*b != '\0' && (*b == ' ' || *b == '\t'))
105
		b++;
106
	/* search for the end */
107
	e = b;
108
	while (*e != '\n' && *e != '\0')
109
		e++;
110
	/* remove trailing spaces */
111
	while (e > b && xisspace(*e))
112
		e--;
113 114
	if (!xisspace(*e))
		val = strndup(b, e - b + 1);
115
	else
116
		val = strdup("");
117
	if (FAILEDTOALLOC(val))
118 119 120 121
		return RET_ERROR_OOM;
	*value = val;
	return RET_OK;
}
122

123
retvalue chunk_getextralinelist(const char *chunk, const char *name, struct strlist *strlist) {
124
	retvalue r;
125
	const char *f, *b, *e;
126 127
	char *v;

128 129
	f = chunk_getfield(name, chunk);
	if (f == NULL)
130
		return RET_NOTHING;
Bernhard Link's avatar
Bernhard Link committed
131
	strlist_init(strlist);
132
	/* walk over the first line */
133
	while (*f != '\0' && *f != '\n')
134
		f++;
135
	/* nothing there is an empty list */
136
	if (*f == '\0')
137 138
		return RET_OK;
	f++;
139
	/* while lines begin with ' ' or '\t', add them */
140 141
	while (*f == ' ' || *f == '\t') {
		while (*f != '\0' && xisblank(*f))
142 143
			f++;
		b = f;
144
		while (*f != '\0' && *f != '\n')
145 146
			f++;
		e = f;
147
		while (e > b && *e != '\0' && xisspace(*e))
148
			e--;
149 150
		if (!xisspace(*e))
			v = strndup(b, e - b + 1);
151
		else
152
			v = strdup("");
153
		if (FAILEDTOALLOC(v)) {
154
			strlist_done(strlist);
155 156
			return RET_ERROR_OOM;
		}
157 158
		r = strlist_add(strlist, v);
		if (!RET_IS_OK(r)) {
159
			strlist_done(strlist);
160 161
			return r;
		}
162
		if (*f == '\0')
163 164 165 166 167 168
			return RET_OK;
		f++;
	}
	return RET_OK;
}

169 170
retvalue chunk_getwholedata(const char *chunk, const char *name, char **value) {
	const char *f, *p, *e;
171
	bool afternewline = false;
172 173
	char *v;

174 175
	f = chunk_getfield(name, chunk);
	if (f == NULL)
176
		return RET_NOTHING;
177 178
	while (*f == ' ')
		f++;
179 180 181
	for (e = p = f ; *p != '\0' ; p++) {
		if (afternewline) {
			if (*p == ' ' || *p == '\t')
182
				afternewline = false;
183
			else if (*p != '\r')
184 185
				break;
		} else {
186
			if (*p == '\n') {
187
				e = p;
188
				afternewline = true;
189 190 191
			}
		}
	}
192
	if (!afternewline && *p == '\0')
193
		e = p;
194 195
	v = strndup(f, e - f);
	if (FAILEDTOALLOC(v))
196 197 198 199 200
		return RET_ERROR_OOM;
	*value = v;
	return RET_OK;
}

201
retvalue chunk_getwordlist(const char *chunk, const char *name, struct strlist *strlist) {
202
	retvalue r;
203
	const char *f, *b;
204
	char *v;
205

206 207
	f = chunk_getfield(name, chunk);
	if (f == NULL)
208
		return RET_NOTHING;
Bernhard Link's avatar
Bernhard Link committed
209
	strlist_init(strlist);
210
	while (*f != '\0') {
211
		/* walk over spaces */
212 213
		while (*f != '\0' && xisspace(*f)) {
			if (*f == '\n') {
214
				f++;
215
				if (*f != ' ' && *f != '\t')
216 217 218 219
					return RET_OK;
			} else
				f++;
		}
220
		if (*f == '\0')
221 222 223
			return RET_OK;
		b = f;
		/* search for end of word */
224
		while (*f != '\0' && !xisspace(*f))
225
			f++;
226 227
		v = strndup(b, f - b);
		if (FAILEDTOALLOC(v)) {
228
			strlist_done(strlist);
229 230
			return RET_ERROR_OOM;
		}
231 232
		r = strlist_add(strlist, v);
		if (!RET_IS_OK(r)) {
233
			strlist_done(strlist);
234 235
			return r;
		}
236 237 238
	}
	return RET_OK;
}
Bernhard Link's avatar
Bernhard Link committed
239

240
retvalue chunk_getuniqwordlist(const char *chunk, const char *name, struct strlist *strlist) {
241
	retvalue r;
242
	const char *f, *b;
243 244
	char *v;

245 246
	f = chunk_getfield(name, chunk);
	if (f == NULL)
247 248
		return RET_NOTHING;
	strlist_init(strlist);
249
	while (*f != '\0') {
250
		/* walk over spaces */
251 252
		while (*f != '\0' && xisspace(*f)) {
			if (*f == '\n') {
253
				f++;
254
				if (*f != ' ' && *f != '\t')
255 256 257 258
					return RET_OK;
			} else
				f++;
		}
259
		if (*f == '\0')
260 261 262
			return RET_OK;
		b = f;
		/* search for end of word */
263
		while (*f != '\0' && !xisspace(*f))
264
			f++;
265 266
		v = strndup(b, f - b);
		if (FAILEDTOALLOC(v)) {
267 268 269
			strlist_done(strlist);
			return RET_ERROR_OOM;
		}
270 271
		r = strlist_adduniq(strlist, v);
		if (!RET_IS_OK(r)) {
272 273 274 275 276 277 278
			strlist_done(strlist);
			return r;
		}
	}
	return RET_OK;
}

279
retvalue chunk_gettruth(const char *chunk, const char *name) {
Bernhard Link's avatar
Bernhard Link committed
280 281
	const char *field;

282 283
	field = chunk_getfield(name, chunk);
	if (field == NULL)
Bernhard Link's avatar
Bernhard Link committed
284
		return RET_NOTHING;
285
	while (*field == ' ' || *field == '\t')
286
		field++;
287
	if ((field[0] == 'f' || field[0] == 'F') &&
288 289 290
			(field[1] == 'a' || field[1] == 'A') &&
			(field[2] == 'l' || field[2] == 'L') &&
			(field[3] == 's' || field[3] == 'S') &&
291
			(field[4] == 'e' || field[4] == 'E')) {
292 293
		return RET_NOTHING;
	}
294 295
	if ((field[0] == 'n' || field[0] == 'N') &&
			(field[1] == 'o' || field[1] == 'O')) {
296 297 298
		return RET_NOTHING;
	}
	// TODO: strict check?
Bernhard Link's avatar
Bernhard Link committed
299 300
	return RET_OK;
}
301
/* return RET_OK, if field is found, RET_NOTHING, if not */
302
retvalue chunk_checkfield(const char *chunk, const char *name){
303 304
	const char *field;

305 306
	field = chunk_getfield(name, chunk);
	if (field == NULL)
307 308 309 310 311
		return RET_NOTHING;

	return RET_OK;
}

312
/* Parse a package/source-field: ' *value( ?\(version\))? *' */
313
retvalue chunk_getname(const char *chunk, const char *name, char **pkgname, bool allowversion) {
314
	const char *field, *name_end, *p;
315

316 317
	field = chunk_getfield(name, chunk);
	if (field == NULL)
318
		return RET_NOTHING;
319
	while (*field != '\0' && *field != '\n' && xisspace(*field))
320 321
		field++;
	name_end = field;
322
	/* this has now checked somewhere else for correctness and
323
	 * is only a pure separation process:
324
	 * (as package(version) is possible, '(' must be checked) */
325 326
	while (*name_end != '\0' && *name_end != '\n' && *name_end != '('
			&& !xisspace(*name_end))
327
		name_end++;
328
	p = name_end;
329
	while (*p != '\0' && *p != '\n' && xisspace(*p))
330
		p++;
331 332 333 334 335
	if (name_end == field ||
		(*p != '\0' && *p != '\n' &&
		  (!allowversion || *p != '('))) {
		if (*field == '\n' || *field == '\0') {
			fprintf(stderr, "Error: Field '%s' is empty!\n", name);
336
		} else {
337 338 339
			fprintf(stderr,
"Error: Field '%s' contains unexpected character '%c'!\n",
					name, *p);
340 341 342
		}
		return RET_ERROR;
	}
343 344
	if (*p == '(') {
		while (*p != '\0' && *p != '\n' && *p != ')')
345 346
			// TODO: perhaps check for wellformed version
			p++;
347
		if (*p != ')') {
Bernhard Link's avatar
Bernhard Link committed
348 349
			fprintf(stderr,
"Error: Field '%s' misses closing parenthesis!\n", name);
350 351
			return RET_ERROR;
		}
352
		p++;
353
	}
354
	while (*p != '\0' && *p != '\n' && xisspace(*p))
355
		p++;
356
	if (*p != '\0' && *p != '\n') {
Bernhard Link's avatar
Bernhard Link committed
357 358
		fprintf(stderr,
"Error: Field '%s' contains trailing junk starting with '%c'!\n", name, *p);
359 360 361
		return RET_ERROR;
	}

362 363
	*pkgname = strndup(field, name_end - field);
	if (FAILEDTOALLOC(*pkgname))
364 365 366 367 368
		return RET_ERROR_OOM;
	return RET_OK;

}

369
/* Parse a package/source-field: ' *value( ?\(version\))? *' */
370 371
retvalue chunk_getnameandversion(const char *chunk, const char *name, char **pkgname, char **version) {
	const char *field, *name_end, *p;
372 373
	char *v;

374 375
	field = chunk_getfield(name, chunk);
	if (field == NULL)
376
		return RET_NOTHING;
377
	while (*field != '\0' && *field != '\n' && xisspace(*field))
378 379 380
		field++;
	name_end = field;
	/* this has now checked somewhere else for correctness and
381
	 * is only a pure separation process:
382
	 * (as package(version) is possible, '(' must be checked) */
383 384
	while (*name_end != '\0' && *name_end != '\n' && *name_end != '('
			&& !xisspace(*name_end))
385 386
		name_end++;
	p = name_end;
387
	while (*p != '\0' && *p != '\n' && xisspace(*p))
388
		p++;
389 390 391
	if (name_end == field || (*p != '\0' && *p != '\n' && *p != '(')) {
		if (*field == '\n' || *field == '\0') {
			fprintf(stderr, "Error: Field '%s' is empty!\n", name);
392
		} else {
393 394
			fprintf(stderr,
"Error: Field '%s' contains unexpected character '%c'!\n", name, *p);
395 396 397
		}
		return RET_ERROR;
	}
398
	if (*p == '(') {
399 400 401
		const char *version_begin;

		p++;
402
		while (*p != '\0' && *p != '\n' && xisspace(*p))
403 404
			p++;
		version_begin = p;
405
		while (*p != '\0' && *p != '\n' && *p != ')'  && !xisspace(*p))
406 407
			// TODO: perhaps check for wellformed version
			p++;
408 409
		v = strndup(version_begin, p - version_begin);
		if (FAILEDTOALLOC(v))
410
			return RET_ERROR_OOM;
411
		while (*p != '\0' && *p != '\n' && *p != ')'  && xisspace(*p))
412
			p++;
413
		if (*p != ')') {
414
			free(v);
415
			if (*p == '\0' || *p == '\n')
Bernhard Link's avatar
Bernhard Link committed
416
				fprintf(stderr,
417 418
"Error: Field '%s' misses closing parenthesis!\n",
						name);
419
			else
Bernhard Link's avatar
Bernhard Link committed
420
				fprintf(stderr,
421
"Error: Field '%s' has multiple words after '('!\n",
422
						name);
423 424 425 426 427 428
			return RET_ERROR;
		}
		p++;
	} else {
		v = NULL;
	}
429
	while (*p != '\0' && *p != '\n' && xisspace(*p))
430
		p++;
431
	if (*p != '\0' && *p != '\n') {
432
		free(v);
433 434 435
		fprintf(stderr,
"Error: Field '%s' contains trailing junk starting with '%c'!\n",
				name, *p);
436 437 438
		return RET_ERROR;
	}

439 440
	*pkgname = strndup(field, name_end - field);
	if (FAILEDTOALLOC(*pkgname)) {
441 442 443 444 445 446 447 448
		free(v);
		return RET_ERROR_OOM;
	}
	*version = v;
	return RET_OK;

}

Bernhard Link's avatar
Bernhard Link committed
449 450 451
/* Add this the <fields to add> to <chunk> before <beforethis> field,
 * replacing older fields of this name, if they are already there. */

452
char *chunk_replacefields(const char *chunk, const struct fieldtoadd *toadd, const char *beforethis, bool maybemissing) {
453 454 455
	const char *c, *ce;
	char *newchunk, *n;
	size_t size, len_beforethis;
Bernhard Link's avatar
Bernhard Link committed
456 457
	const struct fieldtoadd *f;
	retvalue result;
458
	bool fieldsadded = false;
Bernhard Link's avatar
Bernhard Link committed
459

460
	assert (chunk != NULL && beforethis != NULL);
Bernhard Link's avatar
Bernhard Link committed
461

462
	if (toadd == NULL)
463
		return NULL;
Bernhard Link's avatar
Bernhard Link committed
464

Bernhard Link's avatar
Bernhard Link committed
465
	c = chunk;
Bernhard Link's avatar
Bernhard Link committed
466 467

	/* calculate the maximal size we might end up with */
468
	size = 2 + strlen(c);
Bernhard Link's avatar
Bernhard Link committed
469
	f = toadd;
470 471
	while (f != NULL) {
		if (f->data != NULL)
Bernhard Link's avatar
Bernhard Link committed
472
			size += 3 + f->len_field + f->len_data;
Bernhard Link's avatar
Bernhard Link committed
473 474 475
		f = f->next;
	}

Bernhard Link's avatar
Bernhard Link committed
476
	newchunk = n = malloc(size);
477
	if (FAILEDTOALLOC(n))
Bernhard Link's avatar
Bernhard Link committed
478
		return NULL;
Bernhard Link's avatar
Bernhard Link committed
479 480 481 482 483 484

	len_beforethis = strlen(beforethis);

	result = RET_NOTHING;
	do {
		/* are we at the place to add the fields yet? */
485 486
		if (!fieldsadded && strncasecmp(c, beforethis, len_beforethis) == 0
				&& c[len_beforethis] == ':') {
Bernhard Link's avatar
Bernhard Link committed
487 488
			/* add them now: */
			f = toadd;
489 490 491
			while (f != NULL) {
				if (f->data != NULL) {
					memcpy(n, f->field, f->len_field);
492 493 494
					n += f->len_field;
					*n = ':'; n++;
					*n = ' '; n++;
495
					memcpy(n, f->data, f->len_data);
496 497 498
					n += f->len_data;
					*n = '\n'; n++;
				}
Bernhard Link's avatar
Bernhard Link committed
499 500 501
				f = f->next;
			}
			result = RET_OK;
502
			fieldsadded = true;
Bernhard Link's avatar
Bernhard Link committed
503 504 505
		}
		/* is this one of the fields we added/will add? */
		f = toadd;
506 507 508
		while (f != NULL) {
			if (strncasecmp(c, f->field, f->len_field) == 0
					&& c[f->len_field] == ':')
Bernhard Link's avatar
Bernhard Link committed
509 510 511 512 513 514
				break;
			f = f->next;
		}
		/* search the end of the field */
		ce = c;
		do {
515
			while (*ce != '\n' && *ce != '\0')
Bernhard Link's avatar
Bernhard Link committed
516
				ce++;
517
			if (*ce == '\0')
Bernhard Link's avatar
Bernhard Link committed
518 519
				break;
			ce++;
520
		} while (*ce == ' ' || *ce == '\t');
Bernhard Link's avatar
Bernhard Link committed
521 522 523

		/* copy it, if it is not to be ignored */

524 525
		if (f == NULL && ce-c > 0) {
			memcpy(n, c, ce -c);
Bernhard Link's avatar
Bernhard Link committed
526 527 528 529 530 531
			n += ce-c;
		}

		/* and proceed with the next */
		c = ce;

532
	} while (*c != '\0' && *c != '\n');
Bernhard Link's avatar
Bernhard Link committed
533

534
	if (n > newchunk && *(n-1) != '\n')
535
		*(n++) = '\n';
536
	if (maybemissing && !fieldsadded) {
537 538
		/* add them now, if they are allowed to come later */
		f = toadd;
539 540 541
		while (f != NULL) {
			if (f->data != NULL) {
				memcpy(n, f->field, f->len_field);
542 543 544
				n += f->len_field;
				*n = ':'; n++;
				*n = ' '; n++;
545
				memcpy(n, f->data, f->len_data);
546 547 548 549 550 551 552 553
				n += f->len_data;
				*n = '\n'; n++;
			}
			f = f->next;
		}
		result = RET_OK;
		fieldsadded = true;
	}
Bernhard Link's avatar
Bernhard Link committed
554 555
	*n = '\0';

556
	assert (n-newchunk < 0 || (size_t)(n-newchunk) <= size-1);
Bernhard Link's avatar
Bernhard Link committed
557

558 559 560 561
	if (result == RET_NOTHING) {
		fprintf(stderr,
"Could not find field '%s' in chunk '%s'!!!\n",
				beforethis, chunk);
562
		assert(false);
Bernhard Link's avatar
Bernhard Link committed
563 564
	}

Bernhard Link's avatar
Bernhard Link committed
565
	return newchunk;
Bernhard Link's avatar
Bernhard Link committed
566 567
}

568 569 570 571 572
struct fieldtoadd *aodfield_new(const char *field, const char *data, struct fieldtoadd *next) {
	struct fieldtoadd *n;

	assert(field != NULL);

573 574 575 576 577
	n = NEW(struct fieldtoadd);
	if (FAILEDTOALLOC(n)) {
		addfield_free(next);
		return NULL;
	}
578 579 580
	n->field = field;
	n->len_field = strlen(field);
	n->data = data;
581
	if (data != NULL)
582 583 584 585 586 587
		n->len_data = strlen(data);
	else
		n->len_data = 0;
	n->next = next;
	return n;
}
588
struct fieldtoadd *addfield_new(const char *field, const char *data, struct fieldtoadd *next) {
Bernhard Link's avatar
Bernhard Link committed
589 590
	struct fieldtoadd *n;

591 592
	assert(field != NULL && data != NULL);

593 594 595 596
	n = NEW(struct fieldtoadd);
	if (FAILEDTOALLOC(n)) {
		addfield_free(next);
		return NULL;
Bernhard Link's avatar
Bernhard Link committed
597
 	}
Bernhard Link's avatar
Bernhard Link committed
598 599 600 601 602 603 604
	n->field = field;
	n->len_field = strlen(field);
	n->data = data;
	n->len_data = strlen(data);
	n->next = next;
	return n;
}
605
struct fieldtoadd *deletefield_new(const char *field, struct fieldtoadd *next) {
606 607 608 609
	struct fieldtoadd *n;

	assert(field != NULL);

610 611 612 613 614
	n = NEW(struct fieldtoadd);
	if (FAILEDTOALLOC(n)) {
		addfield_free(next);
		return NULL;
	}
615 616 617 618 619 620 621
	n->field = field;
	n->len_field = strlen(field);
	n->data = NULL;
	n->len_data = 0;
	n->next = next;
	return n;
}
622
struct fieldtoadd *addfield_newn(const char *field, const char *data, size_t len, struct fieldtoadd *next) {
Bernhard Link's avatar
Bernhard Link committed
623 624
	struct fieldtoadd *n;

625 626 627 628 629
	n = NEW(struct fieldtoadd);
	if (FAILEDTOALLOC(n)) {
		addfield_free(next);
		return NULL;
	}
Bernhard Link's avatar
Bernhard Link committed
630 631 632 633 634 635 636 637 638 639
	n->field = field;
	n->len_field = strlen(field);
	n->data = data;
	n->len_data = len;
	n->next = next;
	return n;
}
void addfield_free(struct fieldtoadd *f) {
	struct fieldtoadd *g;

640
	while (f != NULL) {
Bernhard Link's avatar
Bernhard Link committed
641 642 643 644 645 646
		g = f->next;
		free(f);
		f = g;
	}
}

647
char *chunk_replacefield(const char *chunk, const char *fieldname, const char *data, bool maybemissing) {
Bernhard Link's avatar
Bernhard Link committed
648
	struct fieldtoadd toadd;
Bernhard Link's avatar
Bernhard Link committed
649

Bernhard Link's avatar
Bernhard Link committed
650 651 652 653 654
	toadd.field = fieldname;
	toadd.len_field = strlen(fieldname);
	toadd.data = data;
	toadd.len_data = strlen(data);
	toadd.next = NULL;
655
	return chunk_replacefields(chunk, &toadd, fieldname, maybemissing);
Bernhard Link's avatar
Bernhard Link committed
656
}
657

658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
/* Add field <firstfieldname> as first field with value data, and remove
 * all other fields of that name (and of name alsoremove if that is != NULL), */

char *chunk_normalize(const char *chunk, const char *firstfieldname, const char *data) {
	const char *c, *ce;
	char *newchunk, *n;
	size_t size;
	size_t data_len, field_len;

	assert (chunk != NULL && firstfieldname != NULL && data != NULL);
	data_len = strlen(data);
	field_len = strlen(firstfieldname);
	c = chunk;

	/* calculate the maximal size we might end up with */
	size = 2 + strlen(c) + 3 + data_len + field_len;

	newchunk = n = malloc(size);
	if (FAILEDTOALLOC(n))
		return NULL;

	memcpy(n, firstfieldname, field_len); n += field_len;
	*(n++) = ':';
	*(n++) = ' ';
	memcpy(n, data, data_len); n += data_len;
	*(n++) = '\n';
	do {
		bool toremove;

		if (strncasecmp(c, firstfieldname, field_len) == 0
				&& c[field_len] == ':')
			toremove = true;
		else
			toremove = false;
		/* search the end of the field */
		ce = c;
		do {
			while (*ce != '\n' && *ce != '\0')
				ce++;
			if (*ce == '\0')
				break;
			ce++;
		} while (*ce == ' ' || *ce == '\t');

		/* copy it, if it is not to be ignored */

		if (!toremove && ce-c > 0) {
			memcpy(n, c, ce-c);
			n += ce-c;
		}
		/* and proceed with the next */
		c = ce;
	} while (*c != '\0' && *c != '\n');
	if (n > newchunk && *(n-1) != '\n')
		*(n++) = '\n';
	*n = '\0';
	return newchunk;
}

Bernhard Link's avatar
Bernhard Link committed
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
const char *chunk_getstart(const char *start, size_t len, bool commentsallowed) {
	const char *s, *l;

	s = start; l = start + len;
	while (s < l && (*s == ' ' || *s == '\t' ||
			*s == '\r' || *s =='\n'))
		s++;
	/* ignore leading comments (even full paragraphs of them) */
	while (commentsallowed && s < l && *s == '#') {
		while (s < l && *s != '\n')
			s++;
		while (s < l && (*s == ' ' || *s == '\t' ||
				*s == '\r' ||
				*s =='\n'))
			s++;
	}
	return s;
}

const char *chunk_over(const char *e) {
	while (*e != '\0') {
		if (*(e++) == '\n') {
			while (*e =='\r')
				e++;
			if (*e == '\n')
				return e+1;
		}
	}
	return e;
}

748
/* this is a bit wastefull, as with normally perfect formatted input, it just
749 750 751
 * writes everything to itself in a inefficent way. But when there are \r
 * in it or spaces before it or stuff like that, it will be in perfect
 * form afterwards. */
Bernhard Link's avatar
Bernhard Link committed
752 753 754 755 756 757 758
/* Write the first chunk found in the first len bytes after start
 * to buffer and set next to the next data found after it.
 * buffer can be a different buffer may be the buffer start is in
 * (as long as start is bigger than buffer).
 * buffer must be big enough to store up to len+1 bytes */
size_t chunk_extract(char *buffer, const char *start, size_t len, bool commentsallowed, const char **next) {
	const char *e, *n, *l;
759 760 761
	char *p;

	p = buffer;
Bernhard Link's avatar
Bernhard Link committed
762 763 764 765 766 767 768 769 770 771 772 773
	l = start + len;
	e = chunk_getstart(start, len, commentsallowed);
	n = NULL;
	while (e < l && *e != '\0') {
		if (*e == '\r') {
			e++;
		} else if (*e == '\n') {
			*(p++) = *(e++);
			n = e;
			while (n < l && *n =='\r')
				n++;
			if (n < l && *n == '\n')
774
				break;
Bernhard Link's avatar
Bernhard Link committed
775 776
			e = n;
			n = NULL;
777
		} else {
Bernhard Link's avatar
Bernhard Link committed
778
			*(p++) = *(e++);
779 780 781
		}
	}

Bernhard Link's avatar
Bernhard Link committed
782 783 784 785
	if (n == NULL) {
		n = e;
		assert (n == l || *n == '\0');
		assert ((p - buffer) <= (n - start));
786 787
		*p = '\0';
	} else {
Bernhard Link's avatar
Bernhard Link committed
788 789 790
		assert (n < l && *n == '\n');
		n++;
		assert (p - buffer < n - start);
791
		*p = '\0';
Bernhard Link's avatar
Bernhard Link committed
792 793
		while (n < l && (*n == '\n' || *n =='\r'))
			n++;
794
	}
Bernhard Link's avatar
Bernhard Link committed
795 796
	*next = n;
	return p - buffer;
797 798
}