diff options
Diffstat (limited to '')
-rw-r--r-- | chunks.c | 798 |
1 files changed, 798 insertions, 0 deletions
diff --git a/chunks.c b/chunks.c new file mode 100644 index 0000000..41f5f2c --- /dev/null +++ b/chunks.c @@ -0,0 +1,798 @@ +/* This file is part of "reprepro" + * Copyright (C) 2003,2004,2005,2007 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 <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <assert.h> +#include "error.h" +#include "chunks.h" +#include "names.h" + +/* point to a specified field in a chunk */ +static const char *chunk_getfield(const char *name, const char *chunk) { + size_t l; + + if (chunk == NULL) + return NULL; + l = strlen(name); + while (*chunk != '\0') { + if (strncasecmp(name, chunk, l) == 0 && chunk[l] == ':') { + chunk += l+1; + return chunk; + } + while (*chunk != '\n' && *chunk != '\0') + chunk++; + if (*chunk == '\0') + return NULL; + chunk++; + } + return NULL; +} + +/* get the content of the given field, including all following lines, in a format + * that may be put into chunk_replacefields +static retvalue chunk_getcontent(const char *chunk, const char *name, char **value) { + const char *field; + char *val; + const char *b, *e; + + assert(value != NULL); + field = chunk_getfield(name, chunk); + if (field == NULL) + return RET_NOTHING; + + b = field; + * jump over spaces at the beginning * + if (xisspace(*b)) + b++; + + * search for the end * + e = b; + do { + while (*e != '\n' && *e != '\0') + e++; + if (*e != '\0') + e++; + } while (*e != ' ' && *e != '\t' && *e != '\0'); + + if (e > b && *e == '\0') + e--; + * remove trailing newline * + if (e > b && *e == '\n') + e--; + if (e > b) + val = strndup(b, e - b + 1); + else + val = strdup(""); + if (FAILEDTOALLOC(val)) + return RET_ERROR_OOM; + *value = val; + return RET_OK; +} +*/ + +/* look for name in chunk. returns RET_NOTHING if not found */ +retvalue chunk_getvalue(const char *chunk, const char *name, char **value) { + const char *field; + char *val; + const char *b, *e; + + assert(value != NULL); + field = chunk_getfield(name, chunk); + if (field == NULL) + return RET_NOTHING; + + b = field; + /* jump over spaces at the beginning */ + while (*b != '\0' && (*b == ' ' || *b == '\t')) + b++; + /* search for the end */ + e = b; + while (*e != '\n' && *e != '\0') + e++; + /* remove trailing spaces */ + while (e > b && xisspace(*e)) + e--; + if (!xisspace(*e)) + val = strndup(b, e - b + 1); + else + val = strdup(""); + if (FAILEDTOALLOC(val)) + return RET_ERROR_OOM; + *value = val; + return RET_OK; +} + +retvalue chunk_getextralinelist(const char *chunk, const char *name, struct strlist *strlist) { + retvalue r; + const char *f, *b, *e; + char *v; + + f = chunk_getfield(name, chunk); + if (f == NULL) + return RET_NOTHING; + strlist_init(strlist); + /* walk over the first line */ + while (*f != '\0' && *f != '\n') + f++; + /* nothing there is an empty list */ + if (*f == '\0') + return RET_OK; + f++; + /* while lines begin with ' ' or '\t', add them */ + while (*f == ' ' || *f == '\t') { + while (*f != '\0' && xisblank(*f)) + f++; + b = f; + while (*f != '\0' && *f != '\n') + f++; + e = f; + while (e > b && *e != '\0' && xisspace(*e)) + e--; + if (!xisspace(*e)) + v = strndup(b, e - b + 1); + else + v = strdup(""); + if (FAILEDTOALLOC(v)) { + strlist_done(strlist); + return RET_ERROR_OOM; + } + r = strlist_add(strlist, v); + if (!RET_IS_OK(r)) { + strlist_done(strlist); + return r; + } + if (*f == '\0') + return RET_OK; + f++; + } + return RET_OK; +} + +retvalue chunk_getwholedata(const char *chunk, const char *name, char **value) { + const char *f, *p, *e; + bool afternewline = false; + char *v; + + f = chunk_getfield(name, chunk); + if (f == NULL) + return RET_NOTHING; + while (*f == ' ') + f++; + for (e = p = f ; *p != '\0' ; p++) { + if (afternewline) { + if (*p == ' ' || *p == '\t') + afternewline = false; + else if (*p != '\r') + break; + } else { + if (*p == '\n') { + e = p; + afternewline = true; + } + } + } + if (!afternewline && *p == '\0') + e = p; + v = strndup(f, e - f); + if (FAILEDTOALLOC(v)) + return RET_ERROR_OOM; + *value = v; + return RET_OK; +} + +retvalue chunk_getwordlist(const char *chunk, const char *name, struct strlist *strlist) { + retvalue r; + const char *f, *b; + char *v; + + f = chunk_getfield(name, chunk); + if (f == NULL) + return RET_NOTHING; + strlist_init(strlist); + while (*f != '\0') { + /* walk over spaces */ + while (*f != '\0' && xisspace(*f)) { + if (*f == '\n') { + f++; + if (*f != ' ' && *f != '\t') + return RET_OK; + } else + f++; + } + if (*f == '\0') + return RET_OK; + b = f; + /* search for end of word */ + while (*f != '\0' && !xisspace(*f)) + f++; + v = strndup(b, f - b); + if (FAILEDTOALLOC(v)) { + strlist_done(strlist); + return RET_ERROR_OOM; + } + r = strlist_add(strlist, v); + if (!RET_IS_OK(r)) { + strlist_done(strlist); + return r; + } + } + return RET_OK; +} + +retvalue chunk_getuniqwordlist(const char *chunk, const char *name, struct strlist *strlist) { + retvalue r; + const char *f, *b; + char *v; + + f = chunk_getfield(name, chunk); + if (f == NULL) + return RET_NOTHING; + strlist_init(strlist); + while (*f != '\0') { + /* walk over spaces */ + while (*f != '\0' && xisspace(*f)) { + if (*f == '\n') { + f++; + if (*f != ' ' && *f != '\t') + return RET_OK; + } else + f++; + } + if (*f == '\0') + return RET_OK; + b = f; + /* search for end of word */ + while (*f != '\0' && !xisspace(*f)) + f++; + v = strndup(b, f - b); + if (FAILEDTOALLOC(v)) { + strlist_done(strlist); + return RET_ERROR_OOM; + } + r = strlist_adduniq(strlist, v); + if (!RET_IS_OK(r)) { + strlist_done(strlist); + return r; + } + } + return RET_OK; +} + +retvalue chunk_gettruth(const char *chunk, const char *name) { + const char *field; + + field = chunk_getfield(name, chunk); + if (field == NULL) + return RET_NOTHING; + while (*field == ' ' || *field == '\t') + field++; + if ((field[0] == 'f' || field[0] == 'F') && + (field[1] == 'a' || field[1] == 'A') && + (field[2] == 'l' || field[2] == 'L') && + (field[3] == 's' || field[3] == 'S') && + (field[4] == 'e' || field[4] == 'E')) { + return RET_NOTHING; + } + if ((field[0] == 'n' || field[0] == 'N') && + (field[1] == 'o' || field[1] == 'O')) { + return RET_NOTHING; + } + // TODO: strict check? + return RET_OK; +} +/* return RET_OK, if field is found, RET_NOTHING, if not */ +retvalue chunk_checkfield(const char *chunk, const char *name){ + const char *field; + + field = chunk_getfield(name, chunk); + if (field == NULL) + return RET_NOTHING; + + return RET_OK; +} + +/* Parse a package/source-field: ' *value( ?\(version\))? *' */ +retvalue chunk_getname(const char *chunk, const char *name, char **pkgname, bool allowversion) { + const char *field, *name_end, *p; + + field = chunk_getfield(name, chunk); + if (field == NULL) + return RET_NOTHING; + while (*field != '\0' && *field != '\n' && xisspace(*field)) + field++; + name_end = field; + /* this has now checked somewhere else for correctness and + * is only a pure separation process: + * (as package(version) is possible, '(' must be checked) */ + while (*name_end != '\0' && *name_end != '\n' && *name_end != '(' + && !xisspace(*name_end)) + name_end++; + p = name_end; + while (*p != '\0' && *p != '\n' && xisspace(*p)) + p++; + if (name_end == field || + (*p != '\0' && *p != '\n' && + (!allowversion || *p != '('))) { + if (*field == '\n' || *field == '\0') { + fprintf(stderr, "Error: Field '%s' is empty!\n", name); + } else { + fprintf(stderr, +"Error: Field '%s' contains unexpected character '%c'!\n", + name, *p); + } + return RET_ERROR; + } + if (*p == '(') { + while (*p != '\0' && *p != '\n' && *p != ')') + // TODO: perhaps check for wellformed version + p++; + if (*p != ')') { + fprintf(stderr, +"Error: Field '%s' misses closing parenthesis!\n", name); + return RET_ERROR; + } + p++; + } + while (*p != '\0' && *p != '\n' && xisspace(*p)) + p++; + if (*p != '\0' && *p != '\n') { + fprintf(stderr, +"Error: Field '%s' contains trailing junk starting with '%c'!\n", name, *p); + return RET_ERROR; + } + + *pkgname = strndup(field, name_end - field); + if (FAILEDTOALLOC(*pkgname)) + return RET_ERROR_OOM; + return RET_OK; + +} + +/* Parse a package/source-field: ' *value( ?\(version\))? *' */ +retvalue chunk_getnameandversion(const char *chunk, const char *name, char **pkgname, char **version) { + const char *field, *name_end, *p; + char *v; + + field = chunk_getfield(name, chunk); + if (field == NULL) + return RET_NOTHING; + while (*field != '\0' && *field != '\n' && xisspace(*field)) + field++; + name_end = field; + /* this has now checked somewhere else for correctness and + * is only a pure separation process: + * (as package(version) is possible, '(' must be checked) */ + while (*name_end != '\0' && *name_end != '\n' && *name_end != '(' + && !xisspace(*name_end)) + name_end++; + p = name_end; + while (*p != '\0' && *p != '\n' && xisspace(*p)) + p++; + if (name_end == field || (*p != '\0' && *p != '\n' && *p != '(')) { + if (*field == '\n' || *field == '\0') { + fprintf(stderr, "Error: Field '%s' is empty!\n", name); + } else { + fprintf(stderr, +"Error: Field '%s' contains unexpected character '%c'!\n", name, *p); + } + return RET_ERROR; + } + if (*p == '(') { + const char *version_begin; + + p++; + while (*p != '\0' && *p != '\n' && xisspace(*p)) + p++; + version_begin = p; + while (*p != '\0' && *p != '\n' && *p != ')' && !xisspace(*p)) + // TODO: perhaps check for wellformed version + p++; + v = strndup(version_begin, p - version_begin); + if (FAILEDTOALLOC(v)) + return RET_ERROR_OOM; + while (*p != '\0' && *p != '\n' && *p != ')' && xisspace(*p)) + p++; + if (*p != ')') { + free(v); + if (*p == '\0' || *p == '\n') + fprintf(stderr, +"Error: Field '%s' misses closing parenthesis!\n", + name); + else + fprintf(stderr, +"Error: Field '%s' has multiple words after '('!\n", + name); + return RET_ERROR; + } + p++; + } else { + v = NULL; + } + while (*p != '\0' && *p != '\n' && xisspace(*p)) + p++; + if (*p != '\0' && *p != '\n') { + free(v); + fprintf(stderr, +"Error: Field '%s' contains trailing junk starting with '%c'!\n", + name, *p); + return RET_ERROR; + } + + *pkgname = strndup(field, name_end - field); + if (FAILEDTOALLOC(*pkgname)) { + free(v); + return RET_ERROR_OOM; + } + *version = v; + return RET_OK; + +} + +/* Add this the <fields to add> to <chunk> before <beforethis> field, + * replacing older fields of this name, if they are already there. */ + +char *chunk_replacefields(const char *chunk, const struct fieldtoadd *toadd, const char *beforethis, bool maybemissing) { + const char *c, *ce; + char *newchunk, *n; + size_t size, len_beforethis; + const struct fieldtoadd *f; + retvalue result; + bool fieldsadded = false; + + assert (chunk != NULL && beforethis != NULL); + + if (toadd == NULL) + return NULL; + + c = chunk; + + /* calculate the maximal size we might end up with */ + size = 2 + strlen(c); + f = toadd; + while (f != NULL) { + if (f->data != NULL) + size += 3 + f->len_field + f->len_data; + f = f->next; + } + + newchunk = n = malloc(size); + if (FAILEDTOALLOC(n)) + return NULL; + + len_beforethis = strlen(beforethis); + + result = RET_NOTHING; + do { + /* are we at the place to add the fields yet? */ + if (!fieldsadded && strncasecmp(c, beforethis, len_beforethis) == 0 + && c[len_beforethis] == ':') { + /* add them now: */ + f = toadd; + while (f != NULL) { + if (f->data != NULL) { + memcpy(n, f->field, f->len_field); + n += f->len_field; + *n = ':'; n++; + *n = ' '; n++; + memcpy(n, f->data, f->len_data); + n += f->len_data; + *n = '\n'; n++; + } + f = f->next; + } + result = RET_OK; + fieldsadded = true; + } + /* is this one of the fields we added/will add? */ + f = toadd; + while (f != NULL) { + if (strncasecmp(c, f->field, f->len_field) == 0 + && c[f->len_field] == ':') + break; + f = f->next; + } + /* 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 (f == NULL && 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'; + if (maybemissing && !fieldsadded) { + /* add them now, if they are allowed to come later */ + f = toadd; + while (f != NULL) { + if (f->data != NULL) { + memcpy(n, f->field, f->len_field); + n += f->len_field; + *n = ':'; n++; + *n = ' '; n++; + memcpy(n, f->data, f->len_data); + n += f->len_data; + *n = '\n'; n++; + } + f = f->next; + } + result = RET_OK; + fieldsadded = true; + } + *n = '\0'; + + assert (n-newchunk < 0 || (size_t)(n-newchunk) <= size-1); + + if (result == RET_NOTHING) { + fprintf(stderr, +"Could not find field '%s' in chunk '%s'!!!\n", + beforethis, chunk); + assert(false); + } + + return newchunk; +} + +struct fieldtoadd *aodfield_new(const char *field, const char *data, struct fieldtoadd *next) { + struct fieldtoadd *n; + + assert(field != NULL); + + n = NEW(struct fieldtoadd); + if (FAILEDTOALLOC(n)) { + addfield_free(next); + return NULL; + } + n->field = field; + n->len_field = strlen(field); + n->data = data; + if (data != NULL) + n->len_data = strlen(data); + else + n->len_data = 0; + n->next = next; + return n; +} +struct fieldtoadd *addfield_new(const char *field, const char *data, struct fieldtoadd *next) { + struct fieldtoadd *n; + + assert(field != NULL && data != NULL); + + n = NEW(struct fieldtoadd); + if (FAILEDTOALLOC(n)) { + addfield_free(next); + return NULL; + } + n->field = field; + n->len_field = strlen(field); + n->data = data; + n->len_data = strlen(data); + n->next = next; + return n; +} +struct fieldtoadd *deletefield_new(const char *field, struct fieldtoadd *next) { + struct fieldtoadd *n; + + assert(field != NULL); + + n = NEW(struct fieldtoadd); + if (FAILEDTOALLOC(n)) { + addfield_free(next); + return NULL; + } + n->field = field; + n->len_field = strlen(field); + n->data = NULL; + n->len_data = 0; + n->next = next; + return n; +} +struct fieldtoadd *addfield_newn(const char *field, const char *data, size_t len, struct fieldtoadd *next) { + struct fieldtoadd *n; + + n = NEW(struct fieldtoadd); + if (FAILEDTOALLOC(n)) { + addfield_free(next); + return NULL; + } + 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; + + while (f != NULL) { + g = f->next; + free(f); + f = g; + } +} + +char *chunk_replacefield(const char *chunk, const char *fieldname, const char *data, bool maybemissing) { + struct fieldtoadd toadd; + + toadd.field = fieldname; + toadd.len_field = strlen(fieldname); + toadd.data = data; + toadd.len_data = strlen(data); + toadd.next = NULL; + return chunk_replacefields(chunk, &toadd, fieldname, maybemissing); +} + +/* 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; +} + +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; +} + +/* this is a bit wastefull, as with normally perfect formatted input, it just + * 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. */ +/* 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; + char *p; + + p = buffer; + 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') + break; + e = n; + n = NULL; + } else { + *(p++) = *(e++); + } + } + + if (n == NULL) { + n = e; + assert (n == l || *n == '\0'); + assert ((p - buffer) <= (n - start)); + *p = '\0'; + } else { + assert (n < l && *n == '\n'); + n++; + assert (p - buffer < n - start); + *p = '\0'; + while (n < l && (*n == '\n' || *n =='\r')) + n++; + } + *next = n; + return p - buffer; +} + |