diff options
Diffstat (limited to 'src/txtacc.c')
-rw-r--r-- | src/txtacc.c | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/src/txtacc.c b/src/txtacc.c new file mode 100644 index 0000000..c094c22 --- /dev/null +++ b/src/txtacc.c @@ -0,0 +1,177 @@ +/* wydawca - automatic release submission daemon + Copyright (C) 2007, 2009, 2010 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 <http://www.gnu.org/licenses/>. */ + +#include "wydawca.h" + +struct 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 txtacc_entry_freesize(e) ((e)->size - (e)->len) + +struct txtacc +{ + struct grecs_list *cur; /* Current build list */ + struct grecs_list *mem; /* List of already allocated elements */ +}; + +static struct txtacc_entry * +txtacc_alloc_entry (struct grecs_list *list, size_t size) +{ + struct txtacc_entry *p = xmalloc (sizeof (*p)); + p->buf = xmalloc (size); + p->size = size; + p->len = 0; + grecs_list_append (list, p); + return p; +} + +static struct txtacc_entry * +txtacc_cur_entry (struct txtacc *acc) +{ + struct txtacc_entry *ent; + + if (grecs_list_size (acc->cur) == 0) + return txtacc_alloc_entry (acc->cur, TXTACC_BUFSIZE); + ent = acc->cur->tail->data; + if (txtacc_entry_freesize (ent) == 0) + ent = txtacc_alloc_entry (acc->cur, TXTACC_BUFSIZE); + return ent; +} + +static void +txtacc_entry_append (struct txtacc_entry *ent, const char *p, size_t size) +{ + memcpy (ent->buf + ent->len, p, size); + ent->len += size; +} + +static void +txtacc_entry_tailor (struct txtacc_entry *ent) +{ + if (ent->size > ent->len) + { + char *p = realloc (ent->buf, ent->len); + if (!p) + return; + ent->buf = p; + ent->size = ent->len; + } +} + +static void +txtacc_entry_free (void *p) +{ + if (p) + { + struct txtacc_entry *ent = p; + free (ent->buf); + free (ent); + } +} + +struct txtacc * +txtacc_create () +{ + struct txtacc *acc = xmalloc (sizeof (*acc)); + acc->cur = grecs_list_create (); + acc->cur->free_entry = txtacc_entry_free; + acc->mem = grecs_list_create (); + acc->mem->free_entry = txtacc_entry_free; + return acc; +} + +void +txtacc_free (struct txtacc *acc) +{ + grecs_list_free (acc->cur); + grecs_list_free (acc->mem); + free (acc); +} + +void +txtacc_grow (struct txtacc *acc, const char *buf, size_t size) +{ + while (size) + { + struct txtacc_entry *ent = txtacc_cur_entry (acc); + size_t rest = txtacc_entry_freesize (ent); + if (rest > size) + rest = size; + txtacc_entry_append (ent, buf, rest); + buf += rest; + size -= rest; + } +} + +char * +txtacc_finish (struct txtacc *acc) +{ + struct grecs_list_entry *ep; + struct txtacc_entry *txtent; + size_t size; + + switch (grecs_list_size (acc->cur)) + { + case 0: + return NULL; + + case 1: + txtent = acc->cur->head->data; + acc->cur->head->data = NULL; + grecs_list_append (acc->mem, txtent); + txtacc_entry_tailor (txtent); + break; + + default: + size = 0; + for (ep = acc->cur->head; ep; ep = ep->next) + { + txtent = ep->data; + size += txtent->len; + } + + txtent = txtacc_alloc_entry (acc->mem, size); + for (ep = acc->cur->head; ep; ep = ep->next) + { + struct txtacc_entry *tp = ep->data; + txtacc_entry_append (txtent, tp->buf, tp->len); + } + } + + grecs_list_clear (acc->cur); + return txtent->buf; +} + +void +txtacc_free_string (struct txtacc *acc, char *str) +{ + struct grecs_list_entry *ep; + for (ep = acc->mem->head; ep; ep = ep->next) + { + struct txtacc_entry *tp = ep->data; + if (tp->buf == str) + { + grecs_list_remove_entry(acc->mem, ep); + free (tp->buf); + return; + } + } +} + |