Example #1
0
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);
}