#include <stic.h>

#include <sys/stat.h> /* chmod() */
#include <unistd.h> /* chdir() rmdir() usleep() */

#include <stddef.h> /* NULL */
#include <stdio.h> /* FILE fclose() fopen() fprintf() remove() snprintf() */
#include <string.h> /* strdup() */

#include <test-utils.h>

#include "../../src/compat/fs_limits.h"
#include "../../src/compat/os.h"
#include "../../src/cfg/config.h"
#include "../../src/lua/vlua.h"
#include "../../src/ui/statusbar.h"
#include "../../src/utils/dynarray.h"
#include "../../src/utils/fs.h"
#include "../../src/utils/macros.h"
#include "../../src/utils/matchers.h"
#include "../../src/utils/path.h"
#include "../../src/utils/str.h"
#include "../../src/filelist.h"
#include "../../src/filetype.h"
#include "../../src/running.h"
#include "../../src/status.h"
#include "../lua/asserts.h"

static int prog_exists(const char name[]);
static int prog_does_not_exist(const char name[]);
static void start_use_script(void);
static void stop_use_script(void);
static void assoc_a(char macro);
static void assoc_b(char macro);
static void assoc_common(void);
static void assoc(const char pattern[], const char cmd[]);

static char cwd[PATH_MAX + 1];
static char script_path[PATH_MAX + 1];

SETUP_ONCE()
{
	assert_non_null(get_cwd(cwd, sizeof(cwd)));
}

SETUP()
{
#ifndef _WIN32
	update_string(&cfg.shell, "/bin/sh");
	update_string(&cfg.shell_cmd_flag, "-c");
#else
	update_string(&cfg.shell, "cmd");
	update_string(&cfg.shell_cmd_flag, "/C");
#endif

	update_string(&cfg.vi_command, "echo");
	update_string(&cfg.slow_fs_list, "");
	update_string(&cfg.fuse_home, "");

	stats_update_shell_type(cfg.shell);

	view_setup(&lwin);

	lwin.list_rows = 2;
	lwin.list_pos = 0;
	lwin.dir_entry = dynarray_cextend(NULL,
			lwin.list_rows*sizeof(*lwin.dir_entry));
	lwin.dir_entry[0].name = strdup("a");
	lwin.dir_entry[0].origin = &lwin.curr_dir[0];
	lwin.dir_entry[0].selected = 1;
	lwin.dir_entry[1].name = strdup("b");
	lwin.dir_entry[1].origin = &lwin.curr_dir[0];
	lwin.dir_entry[1].selected = 1;
	lwin.selected_files = 2;

	curr_view = &lwin;
	other_view = &rwin;

	ft_init(&prog_exists);

	make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH,
			"existing-files", cwd);
}

TEARDOWN()
{
	update_string(&cfg.shell, NULL);
	update_string(&cfg.shell_cmd_flag, NULL);
	stats_update_shell_type("/bin/sh");

	view_teardown(&lwin);

	ft_reset(0);
	update_string(&cfg.vi_command, NULL);
	update_string(&cfg.slow_fs_list, NULL);
	update_string(&cfg.fuse_home, NULL);
}

TEST(full_path_regexps_are_handled_for_selection)
{
	matchers_t *ms;
	char pattern[PATH_MAX + 16];
	char *error;

	/* Mind that there is no chdir(), this additionally checks that origins are
	 * being used by the code. */

	snprintf(pattern, sizeof(pattern), "//%s/*//", lwin.curr_dir);
	ms = matchers_alloc(pattern, 0, 1, "", &error);
	assert_non_null(ms);
	ft_set_programs(ms, "echo %f >> " SANDBOX_PATH "/run", 0, 1);

	rn_open(&lwin, FHE_NO_RUN);

	/* Checking for file existence on its removal. */
	assert_success(remove(SANDBOX_PATH "/run"));
}

TEST(full_path_regexps_are_handled_for_selection2)
{
	matchers_t *ms;
	char pattern[PATH_MAX + 16];
	char *error;

	/* Mind that there is no chdir(), this additionally checks that origins are
	 * being used by the code. */

	snprintf(pattern, sizeof(pattern), "//%s/*//", lwin.curr_dir);
	ms = matchers_alloc(pattern, 0, 1, "", &error);
	assert_non_null(ms);
#ifndef _WIN32
	ft_set_programs(ms, "echo > /dev/null %c &", 0, 1);
#else
	ft_set_programs(ms, "echo > NUL %c &", 0, 1);
#endif

	rn_open(&lwin, FHE_NO_RUN);

	/* If we don't crash, then everything is fine. */
}

TEST(can_open_via_plugin)
{
	curr_stats.vlua = vlua_init();

	GLUA_EQ(curr_stats.vlua, "",
			"function open(info) name = info.entry.name end");
	GLUA_EQ(curr_stats.vlua, "",
			"vifm.addhandler{ name = 'open', handler = open }");

	char *error;
	matchers_t *ms = matchers_alloc("*", 0, 1, "", &error);
	assert_non_null(ms);
	ft_set_programs(ms, "#vifmtest#open", /*for_x=*/0, /*in_x=*/1);

	rn_open(&lwin, FHE_NO_RUN);

	GLUA_EQ(curr_stats.vlua, "a", "print(name)");

	vlua_finish(curr_stats.vlua);
	curr_stats.vlua = NULL;
}

TEST(can_open_symlinked_directory, IF(not_windows))
{
	ft_init(&prog_does_not_exist);

	create_dir(SANDBOX_PATH "/dir");
	assert_success(make_symlink("dir", SANDBOX_PATH "/link"));

	make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), SANDBOX_PATH, "", cwd);
	populate_dir_list(&lwin, /*reload=*/0);

	assert_int_equal(2, lwin.list_rows);
	lwin.list_pos = 1;
	lwin.dir_entry[lwin.list_pos].marked = 1;
	lwin.pending_marking = 1;
	assert_string_equal("link", lwin.dir_entry[lwin.list_pos].name);

	rn_open(&lwin, FHE_RUN);
	assert_success(chdir(cwd));

	assert_true(paths_are_same(lwin.curr_dir, SANDBOX_PATH "/dir"));

	remove_file(SANDBOX_PATH "/link");
	remove_dir(SANDBOX_PATH "/dir");
}

TEST(following_resolves_links_in_origin, IF(not_windows))
{
	assert_success(os_mkdir(SANDBOX_PATH "/A", 0700));
	assert_success(os_mkdir(SANDBOX_PATH "/A/B", 0700));
	assert_success(os_mkdir(SANDBOX_PATH "/A/C", 0700));
	assert_success(make_symlink("../C", SANDBOX_PATH "/A/B/c"));
	assert_success(make_symlink("A/B", SANDBOX_PATH "/B"));

	make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), SANDBOX_PATH, "B", cwd);
	populate_dir_list(&lwin, 0);

	assert_int_equal(1, lwin.list_rows);
	assert_string_equal("c", lwin.dir_entry[0].name);
	lwin.list_pos = 0;

	char *saved_cwd = save_cwd();
	rn_follow(&lwin, 0);
	restore_cwd(saved_cwd);

	assert_true(paths_are_same(lwin.curr_dir, SANDBOX_PATH "/A"));
	assert_string_equal("C", lwin.dir_entry[lwin.list_pos].name);

	assert_success(remove(SANDBOX_PATH "/B"));
	assert_success(remove(SANDBOX_PATH "/A/B/c"));
	assert_success(rmdir(SANDBOX_PATH "/A/C"));
	assert_success(rmdir(SANDBOX_PATH "/A/B"));
	assert_success(rmdir(SANDBOX_PATH "/A"));
}

TEST(following_to_a_broken_symlink_is_possible, IF(not_windows))
{
	assert_success(make_symlink("no-file", SANDBOX_PATH "/bad"));
	assert_success(make_symlink("bad", SANDBOX_PATH "/to-bad"));

	make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), SANDBOX_PATH, "", cwd);
	populate_dir_list(&lwin, 0);

	assert_int_equal(2, lwin.list_rows);
	assert_string_equal("to-bad", lwin.dir_entry[1].name);
	lwin.list_pos = 1;

	char *saved_cwd = save_cwd();
	rn_follow(&lwin, 0);
	restore_cwd(saved_cwd);

	assert_true(paths_are_same(lwin.curr_dir, SANDBOX_PATH));
	assert_string_equal("bad", lwin.dir_entry[lwin.list_pos].name);

	assert_success(remove(SANDBOX_PATH "/bad"));
	assert_success(remove(SANDBOX_PATH "/to-bad"));
}

TEST(selection_uses_vim_on_all_undefs, IF(not_windows))
{
	start_use_script();

	rn_open(&lwin, FHE_NO_RUN);

	const char *lines[] = { "a", "b" };
	file_is(SANDBOX_PATH "/vi-list", lines, ARRAY_LEN(lines));

	assert_success(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(selection_uses_vim_on_at_least_one_undef_non_current, IF(not_windows))
{
	start_use_script();
	assoc_a('c');

	rn_open(&lwin, FHE_NO_RUN);

	const char *lines[] = { "a", "b" };
	file_is(SANDBOX_PATH "/vi-list", lines, ARRAY_LEN(lines));

	assert_failure(remove(SANDBOX_PATH "/a-list"));
	assert_success(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(selection_uses_vim_on_at_least_one_undef_current, IF(not_windows))
{
	start_use_script();
	assoc_b('c');

	rn_open(&lwin, FHE_NO_RUN);

	const char *lines[] = { "a", "b" };
	file_is(SANDBOX_PATH "/vi-list", lines, ARRAY_LEN(lines));

	assert_failure(remove(SANDBOX_PATH "/b-list"));
	assert_success(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(selection_uses_common_handler, IF(not_windows))
{
	start_use_script();
	assoc_common();

	rn_open(&lwin, FHE_NO_RUN);

	const char *lines[] = { "a", "b" };
	file_is(SANDBOX_PATH "/common-list", lines, ARRAY_LEN(lines));

	assert_success(remove(SANDBOX_PATH "/common-list"));
	assert_failure(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(selection_is_incompatible, IF(not_windows))
{
	start_use_script();
	assoc_a('c');
	assoc_b('f');

	rn_open(&lwin, FHE_NO_RUN);

	assert_failure(remove(SANDBOX_PATH "/a-list"));
	assert_failure(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(selection_is_compatible, IF(not_windows))
{
	start_use_script();
	assoc("{a}", "echo > /dev/null %c &");
	assoc("{b}", "echo > /dev/null %c &");

	rn_open(&lwin, FHE_NO_RUN);

	assert_failure(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(inconsistent_selection, IF(not_windows))
{
	start_use_script();
	lwin.dir_entry[0].type = FT_DIR;

	rn_open(&lwin, FHE_NO_RUN);

	assert_failure(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(selection_with_broken_link, IF(not_windows))
{
	start_use_script();
	/* Simulate broken link using incorrect name of a file. */
	update_string(&lwin.dir_entry[0].name, "no-such-file");

	rn_open(&lwin, FHE_NO_RUN);

	assert_failure(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(current_file_not_part_of_selection, IF(not_windows))
{
	start_use_script();
	lwin.dir_entry[0].selected = 0;
	--lwin.selected_files;

	rn_open(&lwin, FHE_NO_RUN);

	const char *lines[] = { "a" };
	file_is(SANDBOX_PATH "/vi-list", lines, ARRAY_LEN(lines));

	assert_success(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(entering_parent_directory, IF(not_windows))
{
	start_use_script();
	update_string(&lwin.dir_entry[0].name, "..");
	lwin.dir_entry[0].type = FT_DIR;

	rn_open(&lwin, FHE_NO_RUN);

	char path[PATH_MAX + 1];
	make_abs_path(path, sizeof(path), TEST_DATA_PATH, "", cwd);
	assert_true(paths_are_same(lwin.curr_dir, path));

	assert_failure(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(entering_a_selected_directory, IF(not_windows))
{
	start_use_script();
	update_string(&lwin.dir_entry[0].name, "existing-files");
	make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH, "", cwd);

	lwin.dir_entry[0].type = FT_DIR;
	lwin.dir_entry[1].selected = 0;
	--lwin.selected_files;

	rn_open(&lwin, FHE_NO_RUN);

	char path[PATH_MAX + 1];
	make_abs_path(path, sizeof(path), TEST_DATA_PATH, "existing-files", cwd);
	assert_true(paths_are_same(lwin.curr_dir, path));

	assert_failure(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(entering_an_unselected_directory, IF(not_windows))
{
	view_teardown(&lwin);
	view_setup(&lwin);
	make_abs_path(lwin.curr_dir, sizeof(lwin.curr_dir), TEST_DATA_PATH, "", cwd);

	lwin.list_rows = 3;
	lwin.list_pos = 0;
	lwin.dir_entry = dynarray_cextend(NULL,
			lwin.list_rows*sizeof(*lwin.dir_entry));
	lwin.dir_entry[0].name = strdup("existing-files");
	lwin.dir_entry[0].origin = &lwin.curr_dir[0];
	lwin.dir_entry[0].selected = 0;
	lwin.dir_entry[0].type = FT_DIR;
	lwin.dir_entry[1].name = strdup("b");
	lwin.dir_entry[1].origin = &lwin.curr_dir[0];
	lwin.dir_entry[1].selected = 1;
	lwin.dir_entry[2].name = strdup("c");
	lwin.dir_entry[2].origin = &lwin.curr_dir[0];
	lwin.dir_entry[2].selected = 1;
	lwin.selected_files = 3;

	char out_file[PATH_MAX + 1];
	make_abs_path(out_file, sizeof(out_file), SANDBOX_PATH, "chosen", cwd);
	curr_stats.chosen_files_out = out_file;

	start_use_script();

	rn_open(&lwin, FHE_NO_RUN);

	char path[PATH_MAX + 1];
	make_abs_path(path, sizeof(path), TEST_DATA_PATH, "existing-files", cwd);
	assert_true(paths_are_same(lwin.curr_dir, path));

	curr_stats.chosen_files_out = NULL;
	assert_failure(remove(SANDBOX_PATH "/chosen"));
	assert_failure(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(macro_can_be_added_implicitly, IF(not_windows))
{
	start_use_script();
	lwin.dir_entry[1].selected = 0;
	--lwin.selected_files;

	char *error;
	matchers_t *ms = matchers_alloc("{a}", 0, 1, "", &error);
	assert_non_null(ms);

	char cmd[PATH_MAX + 1];
	snprintf(cmd, sizeof(cmd), "%s a", script_path);
	ft_set_programs(ms, cmd, 0, 0);

	rn_open(&lwin, FHE_NO_RUN);

	const char *lines[] = { "a" };
	file_is(SANDBOX_PATH "/a-list", lines, ARRAY_LEN(lines));

	assert_success(remove(SANDBOX_PATH "/a-list"));
	assert_failure(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(handler_can_be_matched_by_a_prefix, IF(not_windows))
{
	start_use_script();
	lwin.dir_entry[1].selected = 0;
	--lwin.selected_files;

	char *error;
	matchers_t *ms = matchers_alloc("{a}", 0, 1, "", &error);
	assert_non_null(ms);

	char cmd[PATH_MAX + 1];
	snprintf(cmd, sizeof(cmd), "{wrong}no-such-cmd a, {right}%s a", script_path);
	ft_set_programs(ms, cmd, 0, 0);

	rn_open_with_match(&lwin, script_path, 0);

	const char *lines[] = { "a" };
	file_is(SANDBOX_PATH "/a-list", lines, ARRAY_LEN(lines));

	assert_success(remove(SANDBOX_PATH "/a-list"));
	assert_failure(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(selection_multi_run, IF(not_windows))
{
	start_use_script();

	char *error;
	matchers_t *ms;
	char cmd[PATH_MAX + 1];

	ms = matchers_alloc("{a}", 0, 1, "", &error);
	assert_non_null(ms);
	snprintf(cmd, sizeof(cmd), "%s a %%c &", script_path);
	ft_set_programs(ms, cmd, /*for_x=*/0, /*in_x=*/0);

	ms = matchers_alloc("{b}", 0, 1, "", &error);
	assert_non_null(ms);
	snprintf(cmd, sizeof(cmd), "%s b %%\"c &", script_path);
	ft_set_programs(ms, cmd, /*for_x=*/0, /*in_x=*/0);

	rn_open(&lwin, FHE_NO_RUN);

	wait_for_all_bg();

	const char *lines_a[] = { "a" };
	file_is(SANDBOX_PATH "/a-list", lines_a, ARRAY_LEN(lines_a));

	const char *lines_b[] = { "b" };
	file_is(SANDBOX_PATH "/b-list", lines_b, ARRAY_LEN(lines_b));

	assert_success(remove(SANDBOX_PATH "/a-list"));
	assert_success(remove(SANDBOX_PATH "/b-list"));
	assert_failure(remove(SANDBOX_PATH "/vi-list"));

	stop_use_script();
}

TEST(provide_input_on_statusbar_redirection, IF(have_cat))
{
	view_setup(&rwin);

	make_abs_path(rwin.curr_dir, sizeof(rwin.curr_dir), SANDBOX_PATH, "", cwd);
	create_file(SANDBOX_PATH "/a");
	create_file(SANDBOX_PATH "/b");

	setup_grid(&rwin, 20, 2, /*init=*/1);
	replace_string(&rwin.dir_entry[0].name, "a");
	replace_string(&rwin.dir_entry[1].name, "b");

	rwin.dir_entry[0].marked = 1;
	rwin.dir_entry[1].marked = 1;
	rwin.pending_marking = 1;

	rn_open_with(&rwin, "cat %Pl%S", /*dont_execute=*/0, /*force_bg=*/0);

	char a[PATH_MAX + 1];
	char b[PATH_MAX + 1];
	get_full_path_of(&rwin.dir_entry[0], sizeof(a), a);
	get_full_path_of(&rwin.dir_entry[1], sizeof(b), b);

	char buf[PATH_MAX*2 + 16];
	snprintf(buf, sizeof(buf), "%s\n%s", a, b);

	assert_string_equal(buf, ui_sb_last());

	remove_file(SANDBOX_PATH "/a");
	remove_file(SANDBOX_PATH "/b");

	view_teardown(&rwin);
}

TEST(provide_input_on_nowhere_redirection, IF(have_cat))
{
	view_setup(&rwin);

	make_abs_path(rwin.curr_dir, sizeof(rwin.curr_dir), SANDBOX_PATH, "", cwd);
	create_file(SANDBOX_PATH "/a");
	create_file(SANDBOX_PATH "/b");

	setup_grid(&rwin, 20, 2, /*init=*/1);
	replace_string(&rwin.dir_entry[0].name, "a");
	replace_string(&rwin.dir_entry[1].name, "b");

	rwin.dir_entry[0].marked = 1;
	rwin.dir_entry[1].marked = 1;
	rwin.pending_marking = 1;

	char *saved_dir = save_cwd();
	assert_success(chdir(SANDBOX_PATH));

	rn_open_with(&rwin, "cat > file && touch confirmwrite %Pl%i",
			/*dont_execute=*/0, /*force_bg=*/0);

	restore_cwd(saved_dir);

	char a[PATH_MAX + 1];
	char b[PATH_MAX + 1];
	make_abs_path(a, sizeof(a), SANDBOX_PATH, "a", cwd);
	make_abs_path(b, sizeof(b), SANDBOX_PATH, "b", cwd);

	int i;
	for(i = 0; i < 100; ++i)
	{
		usleep(5000);
		if(path_exists(SANDBOX_PATH "/confirmwrite", DEREF))
		{
			break;
		}
	}

	const char *lines[] = { a, b };
	file_is(SANDBOX_PATH "/file", lines, ARRAY_LEN(lines));

	remove_file(SANDBOX_PATH "/confirmwrite");
	remove_file(SANDBOX_PATH "/file");
	remove_file(SANDBOX_PATH "/a");
	remove_file(SANDBOX_PATH "/b");

	view_teardown(&rwin);
}

TEST(provide_input_to_fg_process, IF(have_cat))
{
	view_setup(&rwin);

	make_abs_path(rwin.curr_dir, sizeof(rwin.curr_dir), SANDBOX_PATH, "", cwd);
	create_file(SANDBOX_PATH "/a");
	create_file(SANDBOX_PATH "/b");

	setup_grid(&rwin, 20, 2, /*init=*/1);
	replace_string(&rwin.dir_entry[0].name, "a");
	replace_string(&rwin.dir_entry[1].name, "b");

	rwin.dir_entry[0].marked = 1;
	rwin.dir_entry[1].marked = 1;
	rwin.pending_marking = 1;

	char *saved_dir = save_cwd();
	assert_success(chdir(SANDBOX_PATH));

	rn_open_with(&rwin, "cat > file %Pl", /*dont_execute=*/0, /*force_bg=*/0);

	restore_cwd(saved_dir);

	char a[PATH_MAX + 1];
	char b[PATH_MAX + 1];
	make_abs_path(a, sizeof(a), SANDBOX_PATH, "a", cwd);
	make_abs_path(b, sizeof(b), SANDBOX_PATH, "b", cwd);

	const char *lines[] = { a, b };
	file_is(SANDBOX_PATH "/file", lines, ARRAY_LEN(lines));

	remove_file(SANDBOX_PATH "/file");
	remove_file(SANDBOX_PATH "/a");
	remove_file(SANDBOX_PATH "/b");

	view_teardown(&rwin);
}

TEST(provide_input_to_bg_process, IF(have_cat))
{
	view_setup(&rwin);

	make_abs_path(rwin.curr_dir, sizeof(rwin.curr_dir), SANDBOX_PATH, "", cwd);
	create_file(SANDBOX_PATH "/a");
	create_file(SANDBOX_PATH "/b");

	setup_grid(&rwin, 20, 2, /*init=*/1);
	replace_string(&rwin.dir_entry[0].name, "a");
	replace_string(&rwin.dir_entry[1].name, "b");

	rwin.dir_entry[0].marked = 1;
	rwin.dir_entry[1].marked = 1;
	rwin.pending_marking = 1;

	char *saved_dir = save_cwd();
	assert_success(chdir(SANDBOX_PATH));

	rn_open_with(&rwin, "cat > file %Pl &", /*dont_execute=*/0, /*force_bg=*/0);

	restore_cwd(saved_dir);

	wait_for_all_bg();

	char a[PATH_MAX + 1];
	char b[PATH_MAX + 1];
	make_abs_path(a, sizeof(a), SANDBOX_PATH, "a", cwd);
	make_abs_path(b, sizeof(b), SANDBOX_PATH, "b", cwd);

	const char *lines[] = { a, b };
	file_is(SANDBOX_PATH "/file", lines, ARRAY_LEN(lines));

	remove_file(SANDBOX_PATH "/file");
	remove_file(SANDBOX_PATH "/a");
	remove_file(SANDBOX_PATH "/b");

	view_teardown(&rwin);
}

static int
prog_exists(const char name[])
{
	return 1;
}

static int
prog_does_not_exist(const char name[])
{
	return 0;
}

static void
start_use_script(void)
{
	make_abs_path(script_path, sizeof(script_path), SANDBOX_PATH, "script", cwd);

	char vi_cmd[PATH_MAX + 1];
	snprintf(vi_cmd, sizeof(vi_cmd), "%s vi", script_path);
	update_string(&cfg.vi_command, vi_cmd);

	FILE *fp = fopen(SANDBOX_PATH "/script", "w");
	fprintf(fp, "#!/bin/sh\n");
	fprintf(fp, "prefix=$1\n");
	fprintf(fp, "shift\n");
	fprintf(fp, "for arg; do echo \"$arg\" >> %s/${prefix}-list; done\n",
			SANDBOX_PATH);
	fclose(fp);
	assert_success(chmod(SANDBOX_PATH "/script", 0777));
}

static void
stop_use_script(void)
{
	assert_success(remove(script_path));
}

static void
assoc_a(char macro)
{
	char *error;
	matchers_t *ms = matchers_alloc("{a}", 0, 1, "", &error);
	assert_non_null(ms);

	char cmd[PATH_MAX + 1];
	snprintf(cmd, sizeof(cmd), "%s a %%%c", script_path, macro);
	ft_set_programs(ms, cmd, 0, 0);
}

static void
assoc_b(char macro)
{
	char *error;
	matchers_t *ms = matchers_alloc("{b}", 0, 1, "", &error);
	assert_non_null(ms);

	char cmd[PATH_MAX + 1];
	snprintf(cmd, sizeof(cmd), "%s b %%%c", script_path, macro);
	ft_set_programs(ms, cmd, 0, 0);
}

static void
assoc_common(void)
{
	char *error;
	matchers_t *ms = matchers_alloc("{a,b}", 0, 1, "", &error);
	assert_non_null(ms);

	char cmd[PATH_MAX + 1];
	snprintf(cmd, sizeof(cmd), "%s common %%f", script_path);
	ft_set_programs(ms, cmd, 0, 0);
}

static void
assoc(const char pattern[], const char cmd[])
{
	char *error;
	matchers_t *ms = matchers_alloc(pattern, 0, 1, "", &error);
	assert_non_null(ms);

	ft_set_programs(ms, cmd, 0, 0);
}

/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
/* vim: set cinoptions+=t0 filetype=c : */
