chunkedit.c 10.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*  This file is part of "reprepro"
 *  Copyright (C) 2006 Bernhard R. Link
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  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
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1301  USA
 */
#include <config.h>

18
#include <sys/types.h>
19
#include <errno.h>
20
#include <stdlib.h>
21 22 23 24 25 26 27 28 29 30
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <assert.h>
#include "error.h"
#include "chunkedit.h"
#include "names.h"

void cef_free(struct chunkeditfield *f) {
31
	while (f != NULL) {
32 33 34 35
		int i;
		struct chunkeditfield *p = f;
		f = f->next;

36
		for (i = 0 ; i < p->linecount ; i++) {
37 38 39 40 41 42 43 44 45 46
			free(p->lines[i].words);
			free(p->lines[i].wordlen);
		}
		free(p);
	}
}

struct chunkeditfield *cef_newfield(const char *field, enum cefaction action, enum cefwhen when, unsigned int linecount, struct chunkeditfield *next) {
	struct chunkeditfield *n;

47 48 49
	n = calloc(1, sizeof(struct chunkeditfield) +
			linecount * sizeof(struct cef_line));
	if (FAILEDTOALLOC(n)) {
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
		cef_free(next);
		return NULL;
	}
	assert(field != NULL);
	n->field = field;
	n->len_field = strlen(field);
	n->action = action;
	n->when = when;
	n->linecount = linecount;
	n->next = next;
	return n;
}


void cef_setdatalen(struct chunkeditfield *cef, const char *data, size_t len) {
65
	assert (data != NULL || len == 0);
66

67
	assert (cef->len_all_data >= cef->len_data);
68 69 70 71 72 73 74 75
	cef->len_all_data -= cef->len_data;
	cef->len_all_data += len;
	cef->data = data;
	cef->len_data = len;
	cef->words = NULL;
}

void cef_setdata(struct chunkeditfield *cef, const char *data) {
76
	cef_setdatalen(cef, data, strlen(data));
77 78 79 80 81
}

void cef_setwordlist(struct chunkeditfield *cef, const struct strlist *words) {
	int i; size_t len = 0;

82
	for (i = 0 ; i < words->count ; i++) {
83 84
		len += 1+strlen(words->values[i]);
	}
85
	if (len > 0)
86
		len--;
87
	assert (cef->len_all_data >= cef->len_data);
88 89 90 91 92 93 94 95 96 97 98 99 100
	cef->len_all_data -= cef->len_data;
	cef->len_all_data += len;
	cef->data = NULL;
	cef->len_data = len;
	cef->words = words;
}

retvalue cef_setline(struct chunkeditfield *cef, int line, int wordcount, ...) {
	va_list ap; int i;
	struct cef_line *l;
	const char *word;
	size_t len;

101 102
	assert (line < cef->linecount);
	assert (wordcount > 0);
103 104

	l = &cef->lines[line];
105
	assert (l->wordcount == 0 && l->words == NULL && l->wordlen == NULL);
106 107

	l->wordcount = wordcount;
108 109
	l->words = nzNEW(wordcount, const char*);
	if (FAILEDTOALLOC(l->words))
110
		return RET_ERROR_OOM;
111 112
	l->wordlen = nzNEW(wordcount, size_t);
	if (FAILEDTOALLOC(l->wordlen)) {
113 114 115
		free(l->words);l->words = NULL;
		return RET_ERROR_OOM;
	}
116
	va_start(ap, wordcount);
117
	len = 1; /* newline */
118 119
	for (i = 0 ; i < wordcount; i++) {
		word = va_arg(ap, const char*);
120 121 122 123 124 125
		assert(word != NULL);

		l->words[i] = word;
		l->wordlen[i] = strlen(word);
		len += 1 + l->wordlen[i];
	}
126 127
	word = va_arg(ap, const char*);
	assert (word == NULL);
128 129 130 131 132 133

	va_end(ap);
	cef->len_all_data += len;
	return RET_OK;
}

134 135 136 137 138 139
retvalue cef_setline2(struct chunkeditfield *cef, int line, const char *hash, size_t hashlen, const char *size, size_t sizelen, int wordcount, ...) {
	va_list ap; int i;
	struct cef_line *l;
	const char *word;
	size_t len;

140 141
	assert (line < cef->linecount);
	assert (wordcount >= 0);
142 143

	l = &cef->lines[line];
144
	assert (l->wordcount == 0 && l->words == NULL && l->wordlen == NULL);
145 146

	l->wordcount = wordcount + 2;
147 148
	l->words = nzNEW(wordcount + 2, const char *);
	if (FAILEDTOALLOC(l->words))
149
		return RET_ERROR_OOM;
150 151 152
	l->wordlen = nzNEW(wordcount + 2, size_t);
	if (FAILEDTOALLOC(l->wordlen)) {
		free(l->words); l->words = NULL;
153 154
		return RET_ERROR_OOM;
	}
155
	va_start(ap, wordcount);
156 157 158 159 160 161 162
	len = 1; /* newline */
	l->words[0] = hash;
	l->wordlen[0] = hashlen;
	len += 1 + hashlen;
	l->words[1] = size;
	l->wordlen[1] = sizelen;
	len += 1 + sizelen;
163 164
	for (i = 0 ; i < wordcount; i++) {
		word = va_arg(ap, const char*);
165 166 167 168 169 170
		assert(word != NULL);

		l->words[i + 2] = word;
		l->wordlen[i + 2] = strlen(word);
		len += 1 + l->wordlen[i + 2];
	}
171 172
	word = va_arg(ap, const char*);
	assert (word == NULL);
173 174 175 176 177 178

	va_end(ap);
	cef->len_all_data += len;
	return RET_OK;
}

179 180
static inline int findcef(const struct chunkeditfield *cef, const char *p, size_t len) {
	int result = 0;
181 182 183
	while (cef != NULL) {
		if (cef->len_field == len &&
				strncasecmp(p, cef->field, len) == 0) {
184 185 186 187 188 189 190 191 192
			return result;
		}
		cef = cef->next;
		result++;
	}
	return -1;
}

retvalue chunk_edit(const char *chunk, char **result, size_t *rlen, const struct chunkeditfield *cefs) {
193
	size_t maxlen;
194 195 196 197 198 199 200 201 202 203 204 205
	int i, processed, count = 0;
	const struct chunkeditfield *cef;
	struct field {
		const struct chunkeditfield *cef;
		size_t startofs, endofs;
		/* next in original chunk */
		int next;
	} *fields;
	const char *p, *q, *e;
	char *n; size_t len;

	maxlen = 1; /* a newline might get missed */
206
	for (cef = cefs ; cef != NULL ; cef=cef->next) {
207 208 209
		maxlen += cef->len_field + cef->len_all_data + 3; /* ': \n' */
		count ++;
	}
210 211
	fields = nzNEW(count, struct field);
	if (FAILEDTOALLOC(fields))
212 213
		return RET_ERROR_OOM;
	i = 0;
214 215
	for (cef = cefs ; cef != NULL ; cef=cef->next) {
		assert (i < count);
216 217
		fields[i++].cef = cef;
	}
218
	assert (i == count);
219 220

	/* get rid of empty or strange lines at the beginning: */
221 222
	while (*chunk == ' ' || *chunk == '\t') {
		while (*chunk != '\0' && *chunk != '\n')
223
			chunk++;
224
		if (*chunk == '\n')
225 226 227 228
			chunk++;
	}
	p = chunk;

229
	while (true) {
230
		q = p;
231
		while (*q != '\0' && *q != '\n' && *q != ':')
232
			q++;
233
		if (*q == '\0')
234
			break;
235
		if (*q == '\n') {
236 237
			/* header without colon? what kind of junk is this? */
			q++;
238 239
			while (*q == ' ' || *q == '\t') {
				while (*q != '\0' && *q != '\n')
240
					q++;
241
				if (*q == '\n')
242
					q++;
243

244
			}
245
			if (p == chunk)
246 247 248 249 250 251 252
				chunk = q;
			p = q;
			continue;
		}
		i = findcef(cefs, p, q-p);
		/* find begin and end of data */
		q++;
253
		while (*q == ' ')
254 255
			q++;
		e = q;
256
		while (*e != '\0' && *e != '\n')
257
			e++;
258
		while (e[0] == '\n' && (e[1] == ' ' || e[1] == '\t')) {
259
			e++;
260
			while (*e != '\0' && *e != '\n')
261 262
				e++;
		}
263
		if (i < 0) {
264 265
			/* not known, we'll have to copy it */
			maxlen += 1+e-p;
266
			if (*e == '\0')
267 268 269 270
				break;
			p = e+1;
			continue;
		}
271
		if (fields[i].endofs == 0) {
272 273
			fields[i].startofs = p-chunk;
			fields[i].endofs = e-chunk;
274 275
			if (fields[i].cef->action == CEF_KEEP ||
			    fields[i].cef->action == CEF_ADDMISSED)
276 277
				maxlen += 1+e-q;
		}
278
		if (*e == '\0')
279 280 281
			break;
		p = e+1;
	}
282 283
	n = malloc(maxlen + 1);
	if (FAILEDTOALLOC(n)) {
284 285 286 287
		free(fields);
		return RET_ERROR_OOM;
	}
	len = 0;
288
	for (processed = 0;
289 290 291 292
	     processed < count && fields[processed].cef->when == CEF_EARLY;
	     processed++) {
		struct field *f = &fields[processed];
		const struct chunkeditfield *ef = f->cef;
293
		if (ef->action == CEF_DELETE)
294
			continue;
295
		if (ef->action == CEF_REPLACE && f->endofs == 0)
296
			continue;
297 298 299
		if (f->endofs != 0 &&
		    (ef->action == CEF_KEEP ||
		     ef->action == CEF_ADDMISSED)) {
300
			size_t l = f->endofs - f->startofs;
301 302
			assert (maxlen >= len + l);
			memcpy(n+len, chunk + f->startofs, l);
303 304 305 306
			len +=l;
			n[len++] = '\n';
			continue;
		}
307
		if (ef->action == CEF_KEEP)
308
			continue;
309 310
		assert (maxlen >= len+ 3+ ef->len_field);
		memcpy(n+len, ef->field, ef->len_field);
311 312 313
		len += ef->len_field;
		n[len++] = ':';
		n[len++] = ' ';
314 315
		if (ef->data != NULL) {
			assert (maxlen >= len+1+ef->len_data);
316 317
			memcpy(n+len, ef->data, ef->len_data);
			len += ef->len_data;
318
		} else if (ef->words != NULL) {
319
			int j;
320
			for (j = 0 ; j < ef->words->count ; j++) {
321 322
				const char *v = ef->words->values[j];
				size_t l = strlen(v);
323
				if (j > 0)
324
					n[len++] = ' ';
325
				memcpy(n+len, v, l);
326 327 328
				len += l;
			}
		}
329
		for (i = 0 ; i < ef->linecount ; i++) {
330 331
			int j;
			n[len++] = '\n';
332
			for (j = 0 ; j < ef->lines[i].wordcount ; j++) {
333
				n[len++] = ' ';
334
				memcpy(n+len, ef->lines[i].words[j],
335 336 337 338 339 340 341 342 343
				               ef->lines[i].wordlen[j]);
				len += ef->lines[i].wordlen[j];
			}
		}
		assert(maxlen > len);
		n[len++] = '\n';
	}
	p = chunk;
	/* now add all headers in between */
344
	while (true) {
345
		q = p;
346
		while (*q != '\0' && *q != '\n' && *q != ':')
347
			q++;
348
		if (*q == '\0')
349
			break;
350
		if (*q == '\n') {
351 352
			/* header without colon? what kind of junk is this? */
			q++;
353 354
			while (*q == ' ' || *q == '\t') {
				while (*q != '\0' && *q != '\n')
355
					q++;
356
				if (*q == '\n')
357
					q++;
358

359 360 361 362 363 364 365
			}
			p = q;
			continue;
		}
		i = findcef(cefs, p, q-p);
		/* find begin and end of data */
		q++;
366
		while (*q == ' ')
367 368
			q++;
		e = q;
369
		while (*e != '\0' && *e != '\n')
370
			e++;
371
		while (e[0] == '\n' && (e[1] == ' ' || e[1] == '\t')) {
372
			e++;
373
			while (*e != '\0' && *e != '\n')
374 375
				e++;
		}
376
		if (i < 0) {
377 378
			/* not known, copy it */
			size_t l = e - p;
379 380
			assert (maxlen >= len + l);
			memcpy(n+len, p, l);
381 382
			len += l;
			n[len++] = '\n';
383
			if (*e == '\0')
384 385 386 387
				break;
			p = e+1;
			continue;
		}
388
		if (*e == '\0')
389 390 391
			break;
		p = e+1;
	}
392
	for (; processed < count ; processed++) {
393 394
		struct field *f = &fields[processed];
		const struct chunkeditfield *ef = f->cef;
395
		if (ef->action == CEF_DELETE)
396
			continue;
397
		if (ef->action == CEF_REPLACE && f->endofs == 0)
398
			continue;
399 400 401
		if (f->endofs != 0 &&
		    (ef->action == CEF_KEEP ||
		     ef->action == CEF_ADDMISSED)) {
402
			size_t l = f->endofs - f->startofs;
403 404
			assert (maxlen >= len + l);
			memcpy(n+len, chunk + f->startofs, l);
405 406 407 408
			len +=l;
			n[len++] = '\n';
			continue;
		}
409
		if (ef->action == CEF_KEEP)
410
			continue;
411 412
		assert (maxlen >= len+ 3+ ef->len_field);
		memcpy(n+len, ef->field, ef->len_field);
413 414 415
		len += ef->len_field;
		n[len++] = ':';
		n[len++] = ' ';
416 417
		if (ef->data != NULL) {
			assert (maxlen >= len+1+ef->len_data);
418 419
			memcpy(n+len, ef->data, ef->len_data);
			len += ef->len_data;
420
		} else if (ef->words != NULL) {
421
			int j;
422
			for (j = 0 ; j < ef->words->count ; j++) {
423 424
				const char *v = ef->words->values[j];
				size_t l = strlen(v);
425
				if (j > 0)
426
					n[len++] = ' ';
427
				memcpy(n+len, v, l);
428 429 430
				len += l;
			}
		}
431
		for (i = 0 ; i < ef->linecount ; i++) {
432 433
			int j;
			n[len++] = '\n';
434
			for (j = 0 ; j < ef->lines[i].wordcount ; j++) {
435
				n[len++] = ' ';
436
				memcpy(n+len, ef->lines[i].words[j],
437 438 439 440 441 442 443 444 445 446 447
				               ef->lines[i].wordlen[j]);
				len += ef->lines[i].wordlen[j];
			}
		}
		assert(maxlen > len);
		n[len++] = '\n';
	}
	assert(maxlen >= len);
	n[len] = '\0';
	free(fields);
	*result = realloc(n, len+1);
448
	if (*result == NULL)
449 450 451 452
		*result = n;
	*rlen = len;
	return RET_OK;
}