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