diff options
Diffstat (limited to '')
-rw-r--r-- | chunkedit.c | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/chunkedit.c b/chunkedit.c new file mode 100644 index 0000000..9bf5630 --- /dev/null +++ b/chunkedit.c @@ -0,0 +1,452 @@ +/* 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> + +#include <sys/types.h> +#include <errno.h> +#include <stdlib.h> +#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) { + while (f != NULL) { + int i; + struct chunkeditfield *p = f; + f = f->next; + + for (i = 0 ; i < p->linecount ; i++) { + 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; + + n = calloc(1, sizeof(struct chunkeditfield) + + linecount * sizeof(struct cef_line)); + if (FAILEDTOALLOC(n)) { + 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) { + assert (data != NULL || len == 0); + + assert (cef->len_all_data >= cef->len_data); + 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) { + cef_setdatalen(cef, data, strlen(data)); +} + +void cef_setwordlist(struct chunkeditfield *cef, const struct strlist *words) { + int i; size_t len = 0; + + for (i = 0 ; i < words->count ; i++) { + len += 1+strlen(words->values[i]); + } + if (len > 0) + len--; + assert (cef->len_all_data >= cef->len_data); + 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; + + assert (line < cef->linecount); + assert (wordcount > 0); + + l = &cef->lines[line]; + assert (l->wordcount == 0 && l->words == NULL && l->wordlen == NULL); + + l->wordcount = wordcount; + l->words = nzNEW(wordcount, const char*); + if (FAILEDTOALLOC(l->words)) + return RET_ERROR_OOM; + l->wordlen = nzNEW(wordcount, size_t); + if (FAILEDTOALLOC(l->wordlen)) { + free(l->words);l->words = NULL; + return RET_ERROR_OOM; + } + va_start(ap, wordcount); + len = 1; /* newline */ + for (i = 0 ; i < wordcount; i++) { + word = va_arg(ap, const char*); + assert(word != NULL); + + l->words[i] = word; + l->wordlen[i] = strlen(word); + len += 1 + l->wordlen[i]; + } + word = va_arg(ap, const char*); + assert (word == NULL); + + va_end(ap); + cef->len_all_data += len; + return RET_OK; +} + +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; + + assert (line < cef->linecount); + assert (wordcount >= 0); + + l = &cef->lines[line]; + assert (l->wordcount == 0 && l->words == NULL && l->wordlen == NULL); + + l->wordcount = wordcount + 2; + l->words = nzNEW(wordcount + 2, const char *); + if (FAILEDTOALLOC(l->words)) + return RET_ERROR_OOM; + l->wordlen = nzNEW(wordcount + 2, size_t); + if (FAILEDTOALLOC(l->wordlen)) { + free(l->words); l->words = NULL; + return RET_ERROR_OOM; + } + va_start(ap, wordcount); + 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; + for (i = 0 ; i < wordcount; i++) { + word = va_arg(ap, const char*); + assert(word != NULL); + + l->words[i + 2] = word; + l->wordlen[i + 2] = strlen(word); + len += 1 + l->wordlen[i + 2]; + } + word = va_arg(ap, const char*); + assert (word == NULL); + + va_end(ap); + cef->len_all_data += len; + return RET_OK; +} + +static inline int findcef(const struct chunkeditfield *cef, const char *p, size_t len) { + int result = 0; + while (cef != NULL) { + if (cef->len_field == len && + strncasecmp(p, cef->field, len) == 0) { + return result; + } + cef = cef->next; + result++; + } + return -1; +} + +retvalue chunk_edit(const char *chunk, char **result, size_t *rlen, const struct chunkeditfield *cefs) { + size_t maxlen; + 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 */ + for (cef = cefs ; cef != NULL ; cef=cef->next) { + maxlen += cef->len_field + cef->len_all_data + 3; /* ': \n' */ + count ++; + } + fields = nzNEW(count, struct field); + if (FAILEDTOALLOC(fields)) + return RET_ERROR_OOM; + i = 0; + for (cef = cefs ; cef != NULL ; cef=cef->next) { + assert (i < count); + fields[i++].cef = cef; + } + assert (i == count); + + /* get rid of empty or strange lines at the beginning: */ + while (*chunk == ' ' || *chunk == '\t') { + while (*chunk != '\0' && *chunk != '\n') + chunk++; + if (*chunk == '\n') + chunk++; + } + p = chunk; + + while (true) { + q = p; + while (*q != '\0' && *q != '\n' && *q != ':') + q++; + if (*q == '\0') + break; + if (*q == '\n') { + /* header without colon? what kind of junk is this? */ + q++; + while (*q == ' ' || *q == '\t') { + while (*q != '\0' && *q != '\n') + q++; + if (*q == '\n') + q++; + + } + if (p == chunk) + chunk = q; + p = q; + continue; + } + i = findcef(cefs, p, q-p); + /* find begin and end of data */ + q++; + while (*q == ' ') + q++; + e = q; + while (*e != '\0' && *e != '\n') + e++; + while (e[0] == '\n' && (e[1] == ' ' || e[1] == '\t')) { + e++; + while (*e != '\0' && *e != '\n') + e++; + } + if (i < 0) { + /* not known, we'll have to copy it */ + maxlen += 1+e-p; + if (*e == '\0') + break; + p = e+1; + continue; + } + if (fields[i].endofs == 0) { + fields[i].startofs = p-chunk; + fields[i].endofs = e-chunk; + if (fields[i].cef->action == CEF_KEEP || + fields[i].cef->action == CEF_ADDMISSED) + maxlen += 1+e-q; + } + if (*e == '\0') + break; + p = e+1; + } + n = malloc(maxlen + 1); + if (FAILEDTOALLOC(n)) { + free(fields); + return RET_ERROR_OOM; + } + len = 0; + for (processed = 0; + processed < count && fields[processed].cef->when == CEF_EARLY; + processed++) { + struct field *f = &fields[processed]; + const struct chunkeditfield *ef = f->cef; + if (ef->action == CEF_DELETE) + continue; + if (ef->action == CEF_REPLACE && f->endofs == 0) + continue; + if (f->endofs != 0 && + (ef->action == CEF_KEEP || + ef->action == CEF_ADDMISSED)) { + size_t l = f->endofs - f->startofs; + assert (maxlen >= len + l); + memcpy(n+len, chunk + f->startofs, l); + len +=l; + n[len++] = '\n'; + continue; + } + if (ef->action == CEF_KEEP) + continue; + assert (maxlen >= len+ 3+ ef->len_field); + memcpy(n+len, ef->field, ef->len_field); + len += ef->len_field; + n[len++] = ':'; + n[len++] = ' '; + if (ef->data != NULL) { + assert (maxlen >= len+1+ef->len_data); + memcpy(n+len, ef->data, ef->len_data); + len += ef->len_data; + } else if (ef->words != NULL) { + int j; + for (j = 0 ; j < ef->words->count ; j++) { + const char *v = ef->words->values[j]; + size_t l = strlen(v); + if (j > 0) + n[len++] = ' '; + memcpy(n+len, v, l); + len += l; + } + } + for (i = 0 ; i < ef->linecount ; i++) { + int j; + n[len++] = '\n'; + for (j = 0 ; j < ef->lines[i].wordcount ; j++) { + n[len++] = ' '; + memcpy(n+len, ef->lines[i].words[j], + 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 */ + while (true) { + q = p; + while (*q != '\0' && *q != '\n' && *q != ':') + q++; + if (*q == '\0') + break; + if (*q == '\n') { + /* header without colon? what kind of junk is this? */ + q++; + while (*q == ' ' || *q == '\t') { + while (*q != '\0' && *q != '\n') + q++; + if (*q == '\n') + q++; + + } + p = q; + continue; + } + i = findcef(cefs, p, q-p); + /* find begin and end of data */ + q++; + while (*q == ' ') + q++; + e = q; + while (*e != '\0' && *e != '\n') + e++; + while (e[0] == '\n' && (e[1] == ' ' || e[1] == '\t')) { + e++; + while (*e != '\0' && *e != '\n') + e++; + } + if (i < 0) { + /* not known, copy it */ + size_t l = e - p; + assert (maxlen >= len + l); + memcpy(n+len, p, l); + len += l; + n[len++] = '\n'; + if (*e == '\0') + break; + p = e+1; + continue; + } + if (*e == '\0') + break; + p = e+1; + } + for (; processed < count ; processed++) { + struct field *f = &fields[processed]; + const struct chunkeditfield *ef = f->cef; + if (ef->action == CEF_DELETE) + continue; + if (ef->action == CEF_REPLACE && f->endofs == 0) + continue; + if (f->endofs != 0 && + (ef->action == CEF_KEEP || + ef->action == CEF_ADDMISSED)) { + size_t l = f->endofs - f->startofs; + assert (maxlen >= len + l); + memcpy(n+len, chunk + f->startofs, l); + len +=l; + n[len++] = '\n'; + continue; + } + if (ef->action == CEF_KEEP) + continue; + assert (maxlen >= len+ 3+ ef->len_field); + memcpy(n+len, ef->field, ef->len_field); + len += ef->len_field; + n[len++] = ':'; + n[len++] = ' '; + if (ef->data != NULL) { + assert (maxlen >= len+1+ef->len_data); + memcpy(n+len, ef->data, ef->len_data); + len += ef->len_data; + } else if (ef->words != NULL) { + int j; + for (j = 0 ; j < ef->words->count ; j++) { + const char *v = ef->words->values[j]; + size_t l = strlen(v); + if (j > 0) + n[len++] = ' '; + memcpy(n+len, v, l); + len += l; + } + } + for (i = 0 ; i < ef->linecount ; i++) { + int j; + n[len++] = '\n'; + for (j = 0 ; j < ef->lines[i].wordcount ; j++) { + n[len++] = ' '; + memcpy(n+len, ef->lines[i].words[j], + 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); + if (*result == NULL) + *result = n; + *rlen = len; + return RET_OK; +} |