1 /* wydawca - automatic release submission daemon
2 Copyright (C) 2007-2016 Sergey Poznyakoff
3
4 Wydawca is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 Wydawca is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with wydawca. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <string.h>
21 #include <stdlib.h>
22 #include "grecs.h"
23
24 struct grecs_txtacc_entry
25 {
26 char *buf; /* Text buffer */
27 size_t size; /* Buffer size */
28 size_t len; /* Actual number of bytes in buffer */
29 };
30 #define TXTACC_BUFSIZE 1024
31 #define grecs_txtacc_entry_freesize(e) ((e)->size - (e)->len)
32
33 struct grecs_txtacc
34 {
35 struct grecs_list *cur; /* Current build list */
36 struct grecs_list *mem; /* List of already allocated elements */
37 };
38
39 static struct grecs_txtacc_entry *
grecs_txtacc_alloc_entry(struct grecs_list * list,size_t size)40 grecs_txtacc_alloc_entry(struct grecs_list *list, size_t size)
41 {
42 struct grecs_txtacc_entry *p = grecs_malloc(sizeof (*p));
43 p->buf = grecs_malloc(size);
44 p->size = size;
45 p->len = 0;
46 grecs_list_append(list, p);
47 return p;
48 }
49
50 static struct grecs_txtacc_entry *
grecs_txtacc_cur_entry(struct grecs_txtacc * acc)51 grecs_txtacc_cur_entry(struct grecs_txtacc *acc)
52 {
53 struct grecs_txtacc_entry *ent;
54
55 if (grecs_list_size(acc->cur) == 0)
56 return grecs_txtacc_alloc_entry(acc->cur,
57 GRECS_TXTACC_BUFSIZE);
58 ent = acc->cur->tail->data;
59 if (grecs_txtacc_entry_freesize(ent) == 0)
60 ent = grecs_txtacc_alloc_entry(acc->cur,
61 GRECS_TXTACC_BUFSIZE);
62 return ent;
63 }
64
65 static void
grecs_txtacc_entry_append(struct grecs_txtacc_entry * ent,const char * p,size_t size)66 grecs_txtacc_entry_append(struct grecs_txtacc_entry *ent,
67 const char *p, size_t size)
68 {
69 memcpy(ent->buf + ent->len, p, size);
70 ent->len += size;
71 }
72
73 static void
grecs_txtacc_entry_tailor(struct grecs_txtacc_entry * ent)74 grecs_txtacc_entry_tailor(struct grecs_txtacc_entry *ent)
75 {
76 if (ent->size > ent->len) {
77 char *p = grecs_realloc(ent->buf, ent->len);
78 if (!p)
79 return;
80 ent->buf = p;
81 ent->size = ent->len;
82 }
83 }
84
85 static void
grecs_txtacc_entry_free(void * p)86 grecs_txtacc_entry_free(void *p)
87 {
88 if (p) {
89 struct grecs_txtacc_entry *ent = p;
90 free(ent->buf);
91 free(ent);
92 }
93 }
94
95 struct grecs_txtacc *
grecs_txtacc_create()96 grecs_txtacc_create()
97 {
98 struct grecs_txtacc *acc = grecs_malloc(sizeof (*acc));
99 acc->cur = grecs_list_create();
100 acc->cur->free_entry = grecs_txtacc_entry_free;
101 acc->mem = grecs_list_create();
102 acc->mem->free_entry = grecs_txtacc_entry_free;
103 return acc;
104 }
105
106 void
grecs_txtacc_free(struct grecs_txtacc * acc)107 grecs_txtacc_free(struct grecs_txtacc *acc)
108 {
109 if (acc) {
110 grecs_list_free(acc->cur);
111 grecs_list_free(acc->mem);
112 free(acc);
113 }
114 }
115
116 void
grecs_txtacc_grow(struct grecs_txtacc * acc,const char * buf,size_t size)117 grecs_txtacc_grow(struct grecs_txtacc *acc, const char *buf, size_t size)
118 {
119 while (size) {
120 struct grecs_txtacc_entry *ent = grecs_txtacc_cur_entry(acc);
121 size_t rest = grecs_txtacc_entry_freesize(ent);
122 if (rest > size)
123 rest = size;
124 grecs_txtacc_entry_append(ent, buf, rest);
125 buf += rest;
126 size -= rest;
127 }
128 }
129
130 void
grecs_txtacc_grow_string(struct grecs_txtacc * acc,const char * buf)131 grecs_txtacc_grow_string(struct grecs_txtacc *acc, const char *buf)
132 {
133 grecs_txtacc_grow(acc, buf, strlen(buf));
134 }
135
136 void
grecs_txtacc_grow_string_escape(struct grecs_txtacc * acc,const char * buf)137 grecs_txtacc_grow_string_escape(struct grecs_txtacc *acc, const char *buf)
138 {
139 for (; *buf; buf++) {
140 if (strchr(" \t\n\"\'\\", *buf))
141 grecs_txtacc_grow_char(acc, '\\');
142 grecs_txtacc_grow_char(acc, *buf);
143 }
144 }
145
146 char *
grecs_txtacc_finish(struct grecs_txtacc * acc,int steal)147 grecs_txtacc_finish(struct grecs_txtacc *acc, int steal)
148 {
149 struct grecs_list_entry *ep;
150 struct grecs_txtacc_entry *txtent;
151 size_t size;
152 char *p;
153
154 switch (grecs_list_size(acc->cur)) {
155 case 0:
156 return NULL;
157
158 case 1:
159 txtent = acc->cur->head->data;
160 acc->cur->head->data = NULL;
161 grecs_txtacc_entry_tailor(txtent);
162 grecs_list_append(acc->mem, txtent);
163 break;
164
165 default:
166 size = 0;
167 for (ep = acc->cur->head; ep; ep = ep->next) {
168 txtent = ep->data;
169 size += txtent->len;
170 }
171
172 txtent = grecs_txtacc_alloc_entry(acc->mem, size);
173 for (ep = acc->cur->head; ep; ep = ep->next) {
174 struct grecs_txtacc_entry *tp = ep->data;
175 grecs_txtacc_entry_append(txtent, tp->buf, tp->len);
176 }
177 }
178
179 grecs_list_clear(acc->cur);
180 p = txtent->buf;
181 if (steal) {
182 grecs_list_remove_tail(acc->mem);
183 free(txtent);
184 }
185 return p;
186 }
187
188 void
grecs_txtacc_free_string(struct grecs_txtacc * acc,char * str)189 grecs_txtacc_free_string(struct grecs_txtacc *acc, char *str)
190 {
191 struct grecs_list_entry *ep;
192 for (ep = acc->mem->head; ep; ep = ep->next) {
193 struct grecs_txtacc_entry *tp = ep->data;
194 if (tp->buf == str) {
195 grecs_list_remove_entry(acc->mem, ep);
196 grecs_txtacc_entry_free(tp);
197 return;
198 }
199 }
200 }
201
202 void
grecs_txtacc_clear(struct grecs_txtacc * acc)203 grecs_txtacc_clear(struct grecs_txtacc *acc)
204 {
205 grecs_list_clear(acc->cur);
206 }
207