/* wydawca - automatic release submission daemon Copyright (C) 2007-2016 Sergey Poznyakoff Wydawca is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Wydawca 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 wydawca. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include "grecs.h" struct grecs_txtacc_entry { char *buf; /* Text buffer */ size_t size; /* Buffer size */ size_t len; /* Actual number of bytes in buffer */ }; #define TXTACC_BUFSIZE 1024 #define grecs_txtacc_entry_freesize(e) ((e)->size - (e)->len) struct grecs_txtacc { struct grecs_list *cur; /* Current build list */ struct grecs_list *mem; /* List of already allocated elements */ }; static struct grecs_txtacc_entry * grecs_txtacc_alloc_entry(struct grecs_list *list, size_t size) { struct grecs_txtacc_entry *p = grecs_malloc(sizeof (*p)); p->buf = grecs_malloc(size); p->size = size; p->len = 0; grecs_list_append(list, p); return p; } static struct grecs_txtacc_entry * grecs_txtacc_cur_entry(struct grecs_txtacc *acc) { struct grecs_txtacc_entry *ent; if (grecs_list_size(acc->cur) == 0) return grecs_txtacc_alloc_entry(acc->cur, GRECS_TXTACC_BUFSIZE); ent = acc->cur->tail->data; if (grecs_txtacc_entry_freesize(ent) == 0) ent = grecs_txtacc_alloc_entry(acc->cur, GRECS_TXTACC_BUFSIZE); return ent; } static void grecs_txtacc_entry_append(struct grecs_txtacc_entry *ent, const char *p, size_t size) { memcpy(ent->buf + ent->len, p, size); ent->len += size; } static void grecs_txtacc_entry_tailor(struct grecs_txtacc_entry *ent) { if (ent->size > ent->len) { char *p = grecs_realloc(ent->buf, ent->len); if (!p) return; ent->buf = p; ent->size = ent->len; } } static void grecs_txtacc_entry_free(void *p) { if (p) { struct grecs_txtacc_entry *ent = p; free(ent->buf); free(ent); } } struct grecs_txtacc * grecs_txtacc_create() { struct grecs_txtacc *acc = grecs_malloc(sizeof (*acc)); acc->cur = grecs_list_create(); acc->cur->free_entry = grecs_txtacc_entry_free; acc->mem = grecs_list_create(); acc->mem->free_entry = grecs_txtacc_entry_free; return acc; } void grecs_txtacc_free(struct grecs_txtacc *acc) { if (acc) { grecs_list_free(acc->cur); grecs_list_free(acc->mem); free(acc); } } void grecs_txtacc_grow(struct grecs_txtacc *acc, const char *buf, size_t size) { while (size) { struct grecs_txtacc_entry *ent = grecs_txtacc_cur_entry(acc); size_t rest = grecs_txtacc_entry_freesize(ent); if (rest > size) rest = size; grecs_txtacc_entry_append(ent, buf, rest); buf += rest; size -= rest; } } void grecs_txtacc_grow_string(struct grecs_txtacc *acc, const char *buf) { grecs_txtacc_grow(acc, buf, strlen(buf)); } void grecs_txtacc_grow_string_escape(struct grecs_txtacc *acc, const char *buf) { for (; *buf; buf++) { if (strchr(" \t\n\"\'\\", *buf)) grecs_txtacc_grow_char(acc, '\\'); grecs_txtacc_grow_char(acc, *buf); } } char * grecs_txtacc_finish(struct grecs_txtacc *acc, int steal) { struct grecs_list_entry *ep; struct grecs_txtacc_entry *txtent; size_t size; char *p; switch (grecs_list_size(acc->cur)) { case 0: return NULL; case 1: txtent = acc->cur->head->data; acc->cur->head->data = NULL; grecs_txtacc_entry_tailor(txtent); grecs_list_append(acc->mem, txtent); break; default: size = 0; for (ep = acc->cur->head; ep; ep = ep->next) { txtent = ep->data; size += txtent->len; } txtent = grecs_txtacc_alloc_entry(acc->mem, size); for (ep = acc->cur->head; ep; ep = ep->next) { struct grecs_txtacc_entry *tp = ep->data; grecs_txtacc_entry_append(txtent, tp->buf, tp->len); } } grecs_list_clear(acc->cur); p = txtent->buf; if (steal) { grecs_list_remove_tail(acc->mem); free(txtent); } return p; } void grecs_txtacc_free_string(struct grecs_txtacc *acc, char *str) { struct grecs_list_entry *ep; for (ep = acc->mem->head; ep; ep = ep->next) { struct grecs_txtacc_entry *tp = ep->data; if (tp->buf == str) { grecs_list_remove_entry(acc->mem, ep); grecs_txtacc_entry_free(tp); return; } } } void grecs_txtacc_clear(struct grecs_txtacc *acc) { grecs_list_clear(acc->cur); }