1 /* grecs - Gray's Extensible Configuration System
2    Copyright (C) 2007-2016 Sergey Poznyakoff
3 
4    Grecs 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    Grecs 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 Grecs. If not, see <http://www.gnu.org/licenses/>. */
16 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <grecs.h>
21 #include <grecs-gram.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 struct grecs_list *
grecs_list_create()26 grecs_list_create()
27 {
28 	struct grecs_list *lp = grecs_malloc(sizeof(*lp));
29 	memset(lp, 0, sizeof(*lp));
30 	return lp;
31 }
32 
33 size_t
grecs_list_size(struct grecs_list * lp)34 grecs_list_size(struct grecs_list *lp)
35 {
36 	return lp ? lp->count : 0;
37 }
38 
39 void
grecs_list_insert_entry(struct grecs_list * lp,struct grecs_list_entry * anchor,struct grecs_list_entry * ent,int before)40 grecs_list_insert_entry(struct grecs_list *lp,
41 			struct grecs_list_entry *anchor,
42 			struct grecs_list_entry *ent, int before)
43 {
44 	struct grecs_list_entry *p;
45 
46 	if (!anchor) {
47 		ent->prev = NULL;
48 		ent->next = lp->head;
49 		if (lp->head)
50 			lp->head->prev = ent;
51 		else
52 			lp->tail = ent;
53 		lp->head = ent;
54 		lp->count++;
55 		return;
56 	}
57 
58 	if (before) {
59 		grecs_list_insert_entry(lp, anchor->prev, ent, 0);
60 		return;
61 	}
62 
63 	ent->prev = anchor;
64 	if ((p = anchor->next))
65 		p->prev = ent;
66 	else
67 		lp->tail = ent;
68 	ent->next = p;
69 	anchor->next = ent;
70 	lp->count++;
71 }
72 
73 void
grecs_list_remove_entry(struct grecs_list * lp,struct grecs_list_entry * ent)74 grecs_list_remove_entry(struct grecs_list *lp, struct grecs_list_entry *ent)
75 {
76 	struct grecs_list_entry *p;
77 	if ((p = ent->prev))
78 		p->next = ent->next;
79 	else
80 		lp->head = ent->next;
81 	if ((p = ent->next))
82 		p->prev = ent->prev;
83 	else
84 		lp->tail = ent->prev;
85 	grecs_free(ent);
86 	lp->count--;
87 }
88 
89 void *
grecs_list_remove_tail(struct grecs_list * lp)90 grecs_list_remove_tail(struct grecs_list *lp)
91 {
92 	void *data;
93 	struct grecs_list_entry *ep;
94 
95 	if (!lp || !lp->tail)
96 		return NULL;
97 	ep = lp->tail;
98 	data = lp->tail->data;
99 	grecs_list_remove_entry(lp, ep);
100 	return data;
101 }
102 
103 static int
_ptrcmp(const void * a,const void * b)104 _ptrcmp(const void *a, const void *b)
105 {
106 	return a != b;
107 }
108 
109 int
grecs_list_remove(struct grecs_list * lp,void * data)110 grecs_list_remove(struct grecs_list *lp, void *data)
111 {
112 	struct grecs_list_entry *ep;
113 	int (*cmp)(const void *, const void *);
114 
115 
116 	if (!lp)
117 		return 1;
118 
119 	cmp = lp->cmp ? lp->cmp : _ptrcmp;
120 	for (ep = lp->head; ep; ep = ep->next) {
121 		if (cmp(ep->data, data) == 0) {
122 			grecs_list_remove_entry(lp, ep);
123 			return 0;
124 		}
125 	}
126 	return 1;
127 }
128 
129 void
grecs_list_append(struct grecs_list * lp,void * val)130 grecs_list_append(struct grecs_list *lp, void *val)
131 {
132 	struct grecs_list_entry *ep = grecs_malloc(sizeof(*ep));
133 	ep->data = val;
134 	grecs_list_insert_entry(lp, lp->tail, ep, 0);
135 }
136 
137 void
grecs_list_add(struct grecs_list * dst,struct grecs_list * src)138 grecs_list_add(struct grecs_list *dst, struct grecs_list *src)
139 {
140 	if (!src->head)
141 		return;
142 
143 	src->head->prev = dst->tail;
144 	if (dst->tail)
145 		dst->tail->next = src->head;
146 	else
147 		dst->head = src->head;
148 	dst->tail = src->tail;
149 	dst->count += src->count;
150 	src->head = src->tail = NULL;
151 	src->count = 0;
152 }
153 
154 void
grecs_list_push(struct grecs_list * lp,void * val)155 grecs_list_push(struct grecs_list *lp, void *val)
156 {
157 	struct grecs_list_entry *ep = grecs_malloc(sizeof(*ep));
158 	ep->data = val;
159 	grecs_list_insert_entry(lp, NULL, ep, 0);
160 }
161 
162 void *
grecs_list_pop(struct grecs_list * lp)163 grecs_list_pop(struct grecs_list *lp)
164 {
165 	void *data;
166 	struct grecs_list_entry *ep;
167 
168 	if (!lp)
169 		return NULL;
170 	ep = lp->head;
171 	if (ep)	{
172 		data = ep->data;
173 		grecs_list_remove_entry(lp, ep);
174 	} else
175 		data = NULL;
176 	return data;
177 }
178 
179 void
grecs_list_clear(struct grecs_list * lp)180 grecs_list_clear(struct grecs_list *lp)
181 {
182 	struct grecs_list_entry *ep;
183 
184 	if (!lp)
185 		return;
186 	ep = lp->head;
187 	while (ep) {
188 		struct grecs_list_entry *next = ep->next;
189 		if (lp->free_entry)
190 			lp->free_entry(ep->data);
191 		grecs_free(ep);
192 		ep = next;
193 	}
194 	lp->head = lp->tail = NULL;
195 	lp->count = 0;
196 }
197 
198 void
grecs_list_free(struct grecs_list * lp)199 grecs_list_free(struct grecs_list *lp)
200 {
201 	if (lp) {
202 		grecs_list_clear(lp);
203 		grecs_free(lp);
204 	}
205 }
206 
207 void *
grecs_list_locate(struct grecs_list * lp,void * data)208 grecs_list_locate(struct grecs_list *lp, void *data)
209 {
210 	struct grecs_list_entry *ep;
211 	int (*cmp)(const void *, const void *);
212 
213 	if (!lp)
214 		return NULL;
215 
216 	cmp = lp->cmp ? lp->cmp : _ptrcmp;
217 
218 	for (ep = lp->head; ep; ep = ep->next) {
219 		if (cmp(ep->data, data) == 0)
220 			return ep->data;
221 	}
222 	return NULL;
223 }
224 
225 void *
grecs_list_index(struct grecs_list * lp,size_t idx)226 grecs_list_index(struct grecs_list *lp, size_t idx)
227 {
228 	struct grecs_list_entry *ep;
229 
230 	if (!lp)
231 		return NULL;
232 	for (ep = lp->head; ep && idx; ep = ep->next, idx--)
233 		;
234 	return ep ? ep->data : NULL;
235 }
236 
237 int
grecs_list_compare(struct grecs_list * a,struct grecs_list * b)238 grecs_list_compare(struct grecs_list *a, struct grecs_list *b)
239 {
240 	struct grecs_list_entry *ap, *bp;
241 	int (*cmp)(const void *, const void *);
242 
243 	if (!a)
244 		return !!b;
245 	else if (!b)
246 		return 1;
247 
248 	if (grecs_list_size(a) != grecs_list_size(b))
249 		return 1;
250 	if (a->cmp != b->cmp)
251 		return 1;
252 
253 	cmp = a->cmp ? a->cmp : _ptrcmp;
254 
255 	for (ap = a->head, bp = b->head; ap; ap = ap->next, bp = bp->next)
256 		if (cmp(ap->data, bp->data))
257 			return 1;
258 
259 	return 0;
260 }
261 
262