1 /*
2  * (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
3  *
4  * Licensed under the GPLv2.
5  */
6 
7 #include "strlist.h"
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 
str_node__new(const char * s,bool dupstr)13 static struct str_node *str_node__new(const char *s, bool dupstr)
14 {
15 	struct str_node *self = malloc(sizeof(*self));
16 
17 	if (self != NULL) {
18 		if (dupstr) {
19 			s = strdup(s);
20 			if (s == NULL)
21 				goto out_delete;
22 		}
23 		self->s = s;
24 	}
25 
26 	return self;
27 
28 out_delete:
29 	free(self);
30 	return NULL;
31 }
32 
str_node__delete(struct str_node * self,bool dupstr)33 static void str_node__delete(struct str_node *self, bool dupstr)
34 {
35 	if (dupstr)
36 		free((void *)self->s);
37 	free(self);
38 }
39 
strlist__add(struct strlist * self,const char * new_entry)40 int strlist__add(struct strlist *self, const char *new_entry)
41 {
42 	struct rb_node **p = &self->entries.rb_node;
43 	struct rb_node *parent = NULL;
44 	struct str_node *sn;
45 
46 	while (*p != NULL) {
47 		int rc;
48 
49 		parent = *p;
50 		sn = rb_entry(parent, struct str_node, rb_node);
51 		rc = strcmp(sn->s, new_entry);
52 
53 		if (rc > 0)
54 			p = &(*p)->rb_left;
55 		else if (rc < 0)
56 			p = &(*p)->rb_right;
57 		else
58 			return -EEXIST;
59 	}
60 
61 	sn = str_node__new(new_entry, self->dupstr);
62 	if (sn == NULL)
63 		return -ENOMEM;
64 
65 	rb_link_node(&sn->rb_node, parent, p);
66 	rb_insert_color(&sn->rb_node, &self->entries);
67 	++self->nr_entries;
68 
69 	return 0;
70 }
71 
strlist__load(struct strlist * self,const char * filename)72 int strlist__load(struct strlist *self, const char *filename)
73 {
74 	char entry[1024];
75 	int err;
76 	FILE *fp = fopen(filename, "r");
77 
78 	if (fp == NULL)
79 		return errno;
80 
81 	while (fgets(entry, sizeof(entry), fp) != NULL) {
82 		const size_t len = strlen(entry);
83 
84 		if (len == 0)
85 			continue;
86 		entry[len - 1] = '\0';
87 
88 		err = strlist__add(self, entry);
89 		if (err != 0)
90 			goto out;
91 	}
92 
93 	err = 0;
94 out:
95 	fclose(fp);
96 	return err;
97 }
98 
strlist__remove(struct strlist * self,struct str_node * sn)99 void strlist__remove(struct strlist *self, struct str_node *sn)
100 {
101 	rb_erase(&sn->rb_node, &self->entries);
102 	str_node__delete(sn, self->dupstr);
103 }
104 
strlist__find(struct strlist * self,const char * entry)105 struct str_node *strlist__find(struct strlist *self, const char *entry)
106 {
107 	struct rb_node **p = &self->entries.rb_node;
108 	struct rb_node *parent = NULL;
109 
110 	while (*p != NULL) {
111 		struct str_node *sn;
112 		int rc;
113 
114 		parent = *p;
115 		sn = rb_entry(parent, struct str_node, rb_node);
116 		rc = strcmp(sn->s, entry);
117 
118 		if (rc > 0)
119 			p = &(*p)->rb_left;
120 		else if (rc < 0)
121 			p = &(*p)->rb_right;
122 		else
123 			return sn;
124 	}
125 
126 	return NULL;
127 }
128 
strlist__parse_list_entry(struct strlist * self,const char * s)129 static int strlist__parse_list_entry(struct strlist *self, const char *s)
130 {
131 	if (strncmp(s, "file://", 7) == 0)
132 		return strlist__load(self, s + 7);
133 
134 	return strlist__add(self, s);
135 }
136 
strlist__parse_list(struct strlist * self,const char * s)137 int strlist__parse_list(struct strlist *self, const char *s)
138 {
139 	char *sep;
140 	int err;
141 
142 	while ((sep = strchr(s, ',')) != NULL) {
143 		*sep = '\0';
144 		err = strlist__parse_list_entry(self, s);
145 		*sep = ',';
146 		if (err != 0)
147 			return err;
148 		s = sep + 1;
149 	}
150 
151 	return *s ? strlist__parse_list_entry(self, s) : 0;
152 }
153 
strlist__new(bool dupstr,const char * slist)154 struct strlist *strlist__new(bool dupstr, const char *slist)
155 {
156 	struct strlist *self = malloc(sizeof(*self));
157 
158 	if (self != NULL) {
159 		self->entries	 = RB_ROOT;
160 		self->dupstr	 = dupstr;
161 		self->nr_entries = 0;
162 		if (slist && strlist__parse_list(self, slist) != 0)
163 			goto out_error;
164 	}
165 
166 	return self;
167 out_error:
168 	free(self);
169 	return NULL;
170 }
171 
strlist__delete(struct strlist * self)172 void strlist__delete(struct strlist *self)
173 {
174 	if (self != NULL) {
175 		struct str_node *pos;
176 		struct rb_node *next = rb_first(&self->entries);
177 
178 		while (next) {
179 			pos = rb_entry(next, struct str_node, rb_node);
180 			next = rb_next(&pos->rb_node);
181 			strlist__remove(self, pos);
182 		}
183 		self->entries = RB_ROOT;
184 		free(self);
185 	}
186 }
187 
strlist__entry(const struct strlist * self,unsigned int idx)188 struct str_node *strlist__entry(const struct strlist *self, unsigned int idx)
189 {
190 	struct rb_node *nd;
191 
192 	for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
193 		struct str_node *pos = rb_entry(nd, struct str_node, rb_node);
194 
195 		if (!idx--)
196 			return pos;
197 	}
198 
199 	return NULL;
200 }
201