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