| /* | 
 |  * (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com> | 
 |  * | 
 |  * Licensed under the GPLv2. | 
 |  */ | 
 |  | 
 | #include "strlist.h" | 
 | #include <errno.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 |  | 
 | static struct str_node *str_node__new(const char *s, bool dupstr) | 
 | { | 
 | 	struct str_node *self = malloc(sizeof(*self)); | 
 |  | 
 | 	if (self != NULL) { | 
 | 		if (dupstr) { | 
 | 			s = strdup(s); | 
 | 			if (s == NULL) | 
 | 				goto out_delete; | 
 | 		} | 
 | 		self->s = s; | 
 | 	} | 
 |  | 
 | 	return self; | 
 |  | 
 | out_delete: | 
 | 	free(self); | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static void str_node__delete(struct str_node *self, bool dupstr) | 
 | { | 
 | 	if (dupstr) | 
 | 		free((void *)self->s); | 
 | 	free(self); | 
 | } | 
 |  | 
 | int strlist__add(struct strlist *self, const char *new_entry) | 
 | { | 
 | 	struct rb_node **p = &self->entries.rb_node; | 
 | 	struct rb_node *parent = NULL; | 
 | 	struct str_node *sn; | 
 |  | 
 | 	while (*p != NULL) { | 
 | 		int rc; | 
 |  | 
 | 		parent = *p; | 
 | 		sn = rb_entry(parent, struct str_node, rb_node); | 
 | 		rc = strcmp(sn->s, new_entry); | 
 |  | 
 | 		if (rc > 0) | 
 | 			p = &(*p)->rb_left; | 
 | 		else if (rc < 0) | 
 | 			p = &(*p)->rb_right; | 
 | 		else | 
 | 			return -EEXIST; | 
 | 	} | 
 |  | 
 | 	sn = str_node__new(new_entry, self->dupstr); | 
 | 	if (sn == NULL) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	rb_link_node(&sn->rb_node, parent, p); | 
 | 	rb_insert_color(&sn->rb_node, &self->entries); | 
 | 	++self->nr_entries; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int strlist__load(struct strlist *self, const char *filename) | 
 | { | 
 | 	char entry[1024]; | 
 | 	int err; | 
 | 	FILE *fp = fopen(filename, "r"); | 
 |  | 
 | 	if (fp == NULL) | 
 | 		return errno; | 
 |  | 
 | 	while (fgets(entry, sizeof(entry), fp) != NULL) { | 
 | 		const size_t len = strlen(entry); | 
 |  | 
 | 		if (len == 0) | 
 | 			continue; | 
 | 		entry[len - 1] = '\0'; | 
 |  | 
 | 		err = strlist__add(self, entry); | 
 | 		if (err != 0) | 
 | 			goto out; | 
 | 	} | 
 |  | 
 | 	err = 0; | 
 | out: | 
 | 	fclose(fp); | 
 | 	return err; | 
 | } | 
 |  | 
 | void strlist__remove(struct strlist *self, struct str_node *sn) | 
 | { | 
 | 	rb_erase(&sn->rb_node, &self->entries); | 
 | 	str_node__delete(sn, self->dupstr); | 
 | } | 
 |  | 
 | struct str_node *strlist__find(struct strlist *self, const char *entry) | 
 | { | 
 | 	struct rb_node **p = &self->entries.rb_node; | 
 | 	struct rb_node *parent = NULL; | 
 |  | 
 | 	while (*p != NULL) { | 
 | 		struct str_node *sn; | 
 | 		int rc; | 
 |  | 
 | 		parent = *p; | 
 | 		sn = rb_entry(parent, struct str_node, rb_node); | 
 | 		rc = strcmp(sn->s, entry); | 
 |  | 
 | 		if (rc > 0) | 
 | 			p = &(*p)->rb_left; | 
 | 		else if (rc < 0) | 
 | 			p = &(*p)->rb_right; | 
 | 		else | 
 | 			return sn; | 
 | 	} | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static int strlist__parse_list_entry(struct strlist *self, const char *s) | 
 | { | 
 | 	if (strncmp(s, "file://", 7) == 0) | 
 | 		return strlist__load(self, s + 7); | 
 |  | 
 | 	return strlist__add(self, s); | 
 | } | 
 |  | 
 | int strlist__parse_list(struct strlist *self, const char *s) | 
 | { | 
 | 	char *sep; | 
 | 	int err; | 
 |  | 
 | 	while ((sep = strchr(s, ',')) != NULL) { | 
 | 		*sep = '\0'; | 
 | 		err = strlist__parse_list_entry(self, s); | 
 | 		*sep = ','; | 
 | 		if (err != 0) | 
 | 			return err; | 
 | 		s = sep + 1; | 
 | 	} | 
 |  | 
 | 	return *s ? strlist__parse_list_entry(self, s) : 0; | 
 | } | 
 |  | 
 | struct strlist *strlist__new(bool dupstr, const char *slist) | 
 | { | 
 | 	struct strlist *self = malloc(sizeof(*self)); | 
 |  | 
 | 	if (self != NULL) { | 
 | 		self->entries	 = RB_ROOT; | 
 | 		self->dupstr	 = dupstr; | 
 | 		self->nr_entries = 0; | 
 | 		if (slist && strlist__parse_list(self, slist) != 0) | 
 | 			goto out_error; | 
 | 	} | 
 |  | 
 | 	return self; | 
 | out_error: | 
 | 	free(self); | 
 | 	return NULL; | 
 | } | 
 |  | 
 | void strlist__delete(struct strlist *self) | 
 | { | 
 | 	if (self != NULL) { | 
 | 		struct str_node *pos; | 
 | 		struct rb_node *next = rb_first(&self->entries); | 
 |  | 
 | 		while (next) { | 
 | 			pos = rb_entry(next, struct str_node, rb_node); | 
 | 			next = rb_next(&pos->rb_node); | 
 | 			strlist__remove(self, pos); | 
 | 		} | 
 | 		self->entries = RB_ROOT; | 
 | 		free(self); | 
 | 	} | 
 | } | 
 |  | 
 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx) | 
 | { | 
 | 	struct rb_node *nd; | 
 |  | 
 | 	for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 
 | 		struct str_node *pos = rb_entry(nd, struct str_node, rb_node); | 
 |  | 
 | 		if (!idx--) | 
 | 			return pos; | 
 | 	} | 
 |  | 
 | 	return NULL; | 
 | } |