static void test_trie_strings(void) { struct mtree_trie *trie; char buf[16]; char *s; size_t i; int ret; trie = mtree_trie_create(); TEST_ASSERT_ERRNO(trie != NULL); if (trie == NULL) return; TEST_ASSERT(mtree_trie_count(trie) == 0); for (i = 0; i < TRIE_STRINGS; i++) { snprintf(buf, sizeof(buf), "%zu", i); s = strdup(buf); TEST_ASSERT_ERRNO(s != NULL); if (s == NULL) continue; ret = mtree_trie_insert(trie, buf, s); TEST_ASSERT_ERRNO(ret != -1); if (ret != -1) TEST_ASSERT_MSG(ret == 0, "key: %s", buf); } TEST_ASSERT(mtree_trie_count(trie) == TRIE_STRINGS); /* * Make sure all the items can be found. */ for (i = 0; i < TRIE_STRINGS + 1; i++) { snprintf(buf, sizeof(buf), "%zu", i); if (i < TRIE_STRINGS) { s = mtree_trie_find(trie, buf); TEST_ASSERT_MSG(s != NULL, "key: %s", buf); } else TEST_ASSERT(mtree_trie_find(trie, buf) == NULL); } /* * Replace the first item, the function returns 1 on successful * replacement. */ // XXX leaks the old 0 ret = mtree_trie_insert(trie, "0", strdup("xy")); TEST_ASSERT_ERRNO(ret == 0 || ret == 1); if (ret != 1) { TEST_ASSERT(ret == 1); s = mtree_trie_find(trie, "0"); TEST_ASSERT(s != NULL); if (s != NULL) TEST_ASSERT_STRCMP(s, "xy"); } mtree_trie_free(trie, free); }
static int read_spec(struct mtree_reader *r, char *s) { struct mtree_entry *entry; char name[MAXPATHLEN]; char *slash, *file, *word, *next; int ret; int skip = 0; /* Try to detect the format if it isn't known yet. */ if (r->path_last == -1 && detect_format(r, s) == -1) return (-1); if (r->path_last != 1) { /* * Path is surely in the first field, either because we know * the format or there is just one single field. */ read_word(s, &file, &next); assert(file != NULL); if (IS_DOTDOT(file)) { /* Only change the parent, keywords are ignored. */ if (r->parent == NULL) { mtree_reader_set_error(r, EINVAL, "`..' not allowed, no parent directory"); return (-1); } r->parent = r->parent->parent; return (0); } } entry = mtree_entry_create_empty(); if (entry == NULL) { mtree_reader_set_error(r, errno, NULL); return (-1); } if (r->path_last != 1) { if (next != NULL) { /* Read keyword that follow the path. */ ret = read_keywords(r, next, &entry->data, 1); if (ret == -1) { mtree_entry_free(entry); return (-1); } } } else { /* Path is at the end, read the keywords first. */ for (file = NULL; file == NULL;) { read_word(s, &word, &next); assert(word != NULL); if (next != NULL) { ret = read_keyword(r, word, &entry->data, 1); if (ret == -1) { mtree_entry_free(entry); return (-1); } s = next; } else file = word; } assert(file != NULL); if (IS_DOTDOT(file)) { mtree_reader_set_error(r, EINVAL, "`..' not allowed in " "this format"); mtree_entry_free(entry); return (-1); } } /* Copy /set values to the entry. */ mtree_entry_data_copy_keywords(&entry->data, &r->defaults, r->defaults.keywords, 0); /* * See if we should skip this file. */ if (SKIP_TYPE(r->options, entry->data.type)) { /* * With directories in version 1.0 format, we'll have to worry * about potentially setting the directory as the current * parent, other types can be skipped immediately. */ if (entry->data.type != MTREE_ENTRY_DIR || r->path_last == 1) goto skip; else skip = 1; } /* Save the file path and name. */ if (strnunvis(name, sizeof(name), file) == -1) { mtree_reader_set_error(r, ENAMETOOLONG, "File name too long: `%s'", file); mtree_entry_free(entry); return (-1); } if ((slash = strchr(name, '/')) != NULL) { /* * If the name contains a slash, it is a relative path. * These do not change the working directory. */ if (skip) goto skip; ret = mtree_cleanup_path(name, &entry->path, &entry->name); if (ret == -1) { mtree_reader_set_error(r, errno, NULL); mtree_entry_free(entry); return (-1); } } else { entry->name = strdup(name); if (entry->name == NULL) { mtree_reader_set_error(r, errno, NULL); mtree_entry_free(entry); return (-1); } entry->parent = r->parent; entry->path = create_v1_path(entry); if (entry->path == NULL) { mtree_reader_set_error(r, errno, NULL); mtree_entry_free(entry); return (-1); } } /* * We may want to skip this entry if a filter told us to skip * children of a directory, which is a parent of this entry. * * There is also no need to worry about changing the parent if * this happens. */ if (!skip && r->skip_trie != NULL) { char *tok; char *endptr; for (tok = strtok_r(name, "/", &endptr); tok != NULL; tok = strtok_r(NULL, "/", &endptr)) { if (mtree_trie_find(r->skip_trie, tok) != NULL) { mtree_entry_free(entry); return (0); } } } /* * Mark the current entry as the current directory. This only applies * to classic (1.0) entries and must be done after keywords are read. */ if (slash == NULL && entry->data.type == MTREE_ENTRY_DIR) r->parent = entry; if (skip) goto skip; if (r->filter != NULL) { int result; /* Apply filter. */ result = r->filter(entry, r->filter_data); if (result & MTREE_ENTRY_SKIP_CHILDREN) { if (entry->data.type == MTREE_ENTRY_DIR) { struct mtree_entry *found, *start; if (r->skip_trie == NULL) { r->skip_trie = mtree_trie_create(NULL); if (r->skip_trie == NULL) { mtree_reader_set_error(r, errno, NULL); return (-1); } } if (mtree_trie_insert(r->skip_trie, entry->path, TRIE_ITEM) == -1) { mtree_reader_set_error(r, errno, NULL); return (-1); } strcpy(name, entry->path); strcat(name, "/"); /* * Remove children that are already in the * list. */ start = r->entries; for (;;) { found = mtree_entry_find_prefix(start, name); if (found == NULL) break; start = found->next; r->entries = mtree_entry_unlink( r->entries, found); mtree_entry_free(found); } } } if (result & MTREE_ENTRY_SKIP) goto skip; } r->entries = mtree_entry_prepend(r->entries, entry); return (0); skip: /* * Skipping directory which is the current parent would make its files * end up in a wrong directory. * * Keep such entries in a list of `loose' entries instead, these will * be deleted when all the work is done. */ if (entry == r->parent) r->loose = mtree_entry_prepend(r->loose, entry); else mtree_entry_free(entry); return (0); }