/* radare - LGPL - Copyright 2010-2019 - pancake, maijin */

#include <r_types.h>
#include <r_list.h>
#include <r_flag.h>
#include <r_core.h>
#define USE_R2 1
#include <spp/spp.h>

static bool is_valid_project_name(const char *name) {
	int i;
	if (r_str_endswith (name, ".zip")) {
		return false;
	}
	for (i = 0; name[i]; i++) {
		switch (name[i]) {
		case '\\': // for w32
		case '.':
		case '_':
		case ':':
		case '-':
			continue;
		}
		if (name[i] >= 'a' && name[i] <= 'z') {
			continue;
		}
		if (name[i] >= 'A' && name[i] <= 'Z') {
			continue;
		}
		if (IS_DIGIT (name[i])) {
			continue;
		}
		return false;
	}
	return true;
}

static char *projectScriptPath(RCore *core, const char *file) {
	const char *magic = "# r2 rdb project file";
	char *data, *prjfile;
	if (r_file_is_abspath (file)) {
		prjfile = strdup (file);
	} else {
		if (!is_valid_project_name (file)) {
			return NULL;
		}
		prjfile = r_file_abspath (r_config_get (core->config, "dir.projects"));
		prjfile = r_str_append (prjfile, R_SYS_DIR);
		prjfile = r_str_append (prjfile, file);
		if (!r_file_exists (prjfile) || r_file_is_directory (prjfile)) {
			prjfile = r_str_append (prjfile, R_SYS_DIR "rc");
		}
	}
	data = r_file_slurp (prjfile, NULL);
	if (data) {
		if (strncmp (data, magic, strlen (magic))) {
			R_FREE (prjfile);
		}
	}
	free (data);
	return prjfile;
}

static int projectInit(RCore *core) {
	char *prjdir = r_file_abspath (r_config_get (core->config, "dir.projects"));
	int ret = r_sys_mkdirp (prjdir);
	if (!ret) {
		eprintf ("Cannot mkdir dir.projects\n");
	}
	free (prjdir);
	return ret;
}

R_API bool r_core_is_project(RCore *core, const char *name) {
	bool ret = false;
	if (name && *name && *name != '.') {
		char *path = projectScriptPath (core, name);
		if (!path) {
			return false;
		}
		if (r_str_endswith (path, R_SYS_DIR "rc") && r_file_exists (path)) {
			ret = true;
		} else {
			path = r_str_append (path, ".d");
			if (r_file_is_directory (path)) {
				ret = true;
			}
		}
		free (path);
	}
	return ret;
}

R_API int r_core_project_cat(RCore *core, const char *name) {
	char *path = projectScriptPath (core, name);
	if (path) {
		char *data = r_file_slurp (path, NULL);
		if (data) {
			r_cons_println (data);
			free (data);
		}
	}
	free (path);
	return 0;
}

R_API int r_core_project_list(RCore *core, int mode) {
	PJ *pj = NULL;
	RListIter *iter;
	RList *list;

	char *foo, *path = r_file_abspath (r_config_get (core->config, "dir.projects"));
	if (!path) {
		return 0;
	}
	list = r_sys_dir (path);
	switch (mode) {
	case 'j':
		pj = pj_new ();
		if (!pj) {
			break;
		}
		pj_a (pj);
		r_list_foreach (list, iter, foo) {
			// todo. escape string
			if (r_core_is_project (core, foo)) {
				pj_s (pj, foo);
			}
		}
		pj_end (pj);
		r_cons_printf ("%s\n", pj_string (pj));
		pj_free (pj);
		break;
	default:
		r_list_foreach (list, iter, foo) {
			if (r_core_is_project (core, foo)) {
				r_cons_println (foo);
			}
		}
		break;
	}
	r_list_free (list);
	free (path);
	return 0;
}

R_API int r_core_project_delete(RCore *core, const char *prjfile) {
	if (r_sandbox_enable (0)) {
		eprintf ("Cannot delete project in sandbox mode\n");
		return 0;
	}
	char *path = projectScriptPath (core, prjfile);
	if (!path) {
		eprintf ("Invalid project name '%s'\n", prjfile);
		return false;
	}
	if (r_core_is_project (core, prjfile)) {
		char *prjDir = r_file_dirname (path);
		if (!prjDir) {
			eprintf ("Cannot resolve directory\n");
			free (path);
			return false;
		}
		// rm project file
		if (r_file_exists (path)) {
			r_file_rm (path);
			eprintf ("rm %s\n", path);
		}

		//rm notes.txt file
		char *notes_txt = r_str_newf ("%s%s%s", prjDir, R_SYS_DIR, "notes.txt");
		if (r_file_exists (notes_txt)) {
			r_file_rm (notes_txt);
			eprintf ("rm %s\n", notes_txt);
		}
		free(notes_txt);

		char *rop_d = r_str_newf ("%s%s%s", prjDir, R_SYS_DIR, "rop.d");

		if (r_file_is_directory (rop_d)) {
			char *f;
			RListIter *iter;
			RList *files = r_sys_dir (rop_d);
			r_list_foreach (files, iter, f) {
				char *filepath = r_str_append (strdup (rop_d), R_SYS_DIR);
				filepath = r_str_append (filepath, f);
				if (!r_file_is_directory (filepath)) {
					eprintf ("rm %s\n", filepath);
					r_file_rm (filepath);
				}
				free (filepath);
			}
			r_file_rm (rop_d);
			eprintf ("rm %s\n", rop_d);
			r_list_free (files);
		}
		free (rop_d);
		// remove directory only if it's empty
		r_file_rm (prjDir);
		free (prjDir);
	}
	free (path);
	return 0;
}

static bool projectLoadRop(RCore *core, const char *prjfile) {
	char *path, *db = NULL, *path_ns;
	bool found = 0;
	SdbListIter *it;
	SdbNs *ns;

	if (!prjfile || !*prjfile) {
		return false;
	}

	Sdb *rop_db = sdb_ns (core->sdb, "rop", false);
	Sdb *nop_db = sdb_ns (rop_db, "nop", false);
	Sdb *mov_db = sdb_ns (rop_db, "mov", false);
	Sdb *const_db = sdb_ns (rop_db, "const", false);
	Sdb *arithm_db = sdb_ns (rop_db, "arithm", false);
	Sdb *arithmct_db = sdb_ns (rop_db, "arithm_ct", false);

	char *rcPath = projectScriptPath (core, prjfile);
	char *prjDir = r_file_dirname (rcPath);

	if (r_str_endswith (prjfile, R_SYS_DIR "rc")) {
		// XXX
		eprintf ("ENDS WITH\n");
		path = strdup (prjfile);
		path[strlen (path) - 3] = 0;
	} else if (r_file_fexists ("%s" R_SYS_DIR "rc", prjDir, prjfile)) {
		path = r_str_newf ("%s" R_SYS_DIR, prjDir, prjfile);
	} else {
		if (*prjfile == R_SYS_DIR[0]) {
			db = r_str_newf ("%s.d", prjfile);
			if (!db) {
				free (prjDir);
				free (rcPath);
				return false;
			}
			path = strdup (db);
		} else {
			db = r_str_newf ("%s" R_SYS_DIR "%s.d", prjDir, prjfile);
			if (!db) {
				free (prjDir);
				free (rcPath);
				return false;
			}
			path = r_file_abspath (db);
		}
	}
	if (!path) {
		free (db);
		free (prjDir);
		free (rcPath);
		return false;
	}
	if (rop_db) {
		ls_foreach (core->sdb->ns, it, ns){
			if (ns->sdb == rop_db) {
				ls_delete (core->sdb->ns, it);
				found = true;
				break;
			}
		}
	}
	if (!found) {
		sdb_free (rop_db);
	}
	rop_db = sdb_new (path, "rop", 0);
	if (!rop_db) {
		free (db);
		free (path);
		free (prjDir);
		free (rcPath);
		return false;
	}
	sdb_ns_set (core->sdb, "rop", rop_db);

	path_ns = r_str_newf ("%s" R_SYS_DIR "rop", prjDir);
	if (!r_file_exists (path_ns)) {
		path_ns = r_str_append (path_ns, ".sdb");
	}
	nop_db = sdb_new (path_ns, "nop", 0);
	sdb_ns_set (rop_db, "nop", nop_db);

	mov_db = sdb_new (path_ns, "mov", 0);
	sdb_ns_set (rop_db, "mov", mov_db);

	const_db = sdb_new (path_ns, "const", 0);
	sdb_ns_set (rop_db, "const", const_db);

	arithm_db = sdb_new (path_ns, "arithm", 0);
	sdb_ns_set (rop_db, "arithm", arithm_db);

	arithmct_db = sdb_new (path_ns, "arithm_ct", 0);
	sdb_ns_set (rop_db, "arithm_ct", arithmct_db);

	free (path);
	free (path_ns);
	free (db);
	free (prjDir);
	free (rcPath);
	return true;
}

R_API void r_core_project_execute_cmds(RCore *core, const char *prjfile) {
	char *str = r_core_project_notes_file (core, prjfile);
	char *data = r_file_slurp (str, NULL);
	if (!data) {
		free (str);
		return;
	}
	Output out;
	out.fout = NULL;
	out.cout = r_strbuf_new (NULL);
	r_strbuf_init (out.cout);
	struct Proc proc;
	spp_proc_set (&proc, "spp", 1);
	spp_eval (data, &out);
	free (data);
	data = strdup (r_strbuf_get (out.cout));
	char *bol = strtok (data, "\n");
	while (bol) {
		if (bol[0] == ':') {
			r_core_cmd0 (core, bol + 1);
		}
		bol = strtok (NULL, "\n");
	}
	free (data);
	free (str);
}

/*** vvv thready ***/

typedef struct {
	RCore *core;
	char *prjName;
	char *rcPath;
} ProjectState;

static RThreadFunctionRet projectLoadBackground(RThread *th) {
	ProjectState *ps = th->user;
	r_core_project_load (ps->core, ps->prjName, ps->rcPath);
	free (ps->prjName);
	free (ps->rcPath);
	free (ps);
	return R_TH_STOP;
}

R_API RThread *r_core_project_load_bg(RCore *core, const char *prjName, const char *rcPath) {
	ProjectState *ps = R_NEW (ProjectState);
	ps->core = core;
	ps->prjName = strdup (prjName);
	ps->rcPath = strdup (rcPath);
	RThread *th = r_th_new (projectLoadBackground, ps, false);
	if (th) {
		r_th_start (th, true);
		char thname[16] = {0};
		size_t thlen = R_MIN (strlen(prjName), sizeof(thname) - 1);
		strncpy (thname, prjName, thlen);
		thname[15] = 0;
		r_th_setname (th, thname);
	}
	return th;
}

/*** ^^^ thready ***/

static ut64 getProjectLaddr(RCore *core, const char *prjfile) {
	ut64 laddr = 0;
	char *buf = r_file_slurp (prjfile, NULL);
	char *pos;
	if (buf) {
		if ((pos = strstr(buf, "\"e bin.laddr = "))) {
			laddr = r_num_math (NULL, pos + 15);
		}
		free (buf);
	}
	return laddr;
}

R_API bool r_core_project_open(RCore *core, const char *prjfile, bool thready) {
	bool askuser = true;
	int ret, close_current_session = 1;
	char *oldbin;
	const char *newbin;
	ut64 mapaddr = 0;
	if (!prjfile || !*prjfile) {
		return false;
	}
	if (thready) {
		eprintf ("Loading projects in a thread has been deprecated. Use tasks\n");
		return false;
	}
	char *prj = projectScriptPath (core, prjfile);
	if (!prj) {
		eprintf ("Invalid project name '%s'\n", prjfile);
		return false;
	}
	char *filepath = r_core_project_info (core, prj);
	// eprintf ("OPENING (%s) from %s\n", prj, r_config_get (core->config, "file.path"));
	/* if it is not an URI */
	if (!filepath) {
		eprintf ("Cannot retrieve information for project '%s'\n", prj);
		free (prj);
		return false;
	}

	if (!filepath[0]) {
		goto cookiefactory;
	}
	if (!strstr (filepath, "://")) {
		/* check if path exists */
		if (!r_file_exists (filepath)) {
			eprintf ("Cannot find file '%s'\n", filepath);
			free (prj);
			free (filepath);
			return false;
		}
	}
 cookiefactory:
	;
	const char *file_path = r_config_get (core->config, "file.path");
	if (!file_path || !*file_path) {
		file_path = r_config_get (core->config, "file.lastpath");
	}
	oldbin = strdup (file_path);
	if (!strcmp (prjfile, r_config_get (core->config, "prj.name"))) {
		// eprintf ("Reloading project\n");
		askuser = false;
#if 0
		free (prj);
		free (filepath);
		return false;
#endif
	}
	if (askuser) {
		if (r_cons_is_interactive ()) {
			close_current_session = r_cons_yesno ('y', "Close current session? (Y/n)");
		}
	}
	if (close_current_session) {
		// delete
		r_core_file_close_fd (core, -1);
		r_io_close_all (core->io);
		r_anal_purge (core->anal);
		r_flag_unset_all (core->flags);
		r_bin_file_delete_all (core->bin);
		// open new file
		// TODO: handle read/read-write mode
		if (filepath[0]) {
			/* Old-style project without embedded on commands to open all files.  */
			if (!r_core_file_open (core, filepath, 0, UT64_MAX)) {
				eprintf ("Cannot open file '%s'\n", filepath);
				ret = false;
				goto beach;
			}
		}
	}
	mapaddr = getProjectLaddr (core, prj);
	if (mapaddr) {
		r_config_set_i (core->config, "bin.laddr", mapaddr);
	}
	if (filepath[0] && close_current_session && r_config_get_i (core->config, "file.info")) {
		mapaddr = r_config_get_i (core->config, "file.offset");
		(void)r_core_bin_load (core, filepath, mapaddr? mapaddr: UT64_MAX);
	}
	/* load sdb stuff in here */
	ret = r_core_project_load (core, prjfile, prj);
	if (filepath[0]) {
		newbin = r_config_get (core->config, "file.path");
		if (!newbin || !*newbin) {
			newbin = r_config_get (core->config, "file.lastpath");
		}
		if (strcmp (oldbin, newbin)) {
			eprintf ("WARNING: file.path changed: %s => %s\n", oldbin, newbin);
		}
	}
beach:
	free (oldbin);
	free (filepath);
	free (prj);
	return ret;
}

R_API char *r_core_project_info(RCore *core, const char *prjfile) {
	FILE *fd;
	char buf[256], *file = NULL;
	char *prj = projectScriptPath (core, prjfile);
	if (!prj) {
		eprintf ("Invalid project name '%s'\n", prjfile);
		return NULL;
	}
	fd = r_sandbox_fopen (prj, "r");
	if (fd) {
		for (;;) {
			if (!fgets (buf, sizeof (buf), fd)) {
				break;
			}
			if (feof (fd)) {
				break;
			}
			if (!strncmp (buf, "\"e file.path = ", 15)) {
				buf[strlen (buf) - 2] = 0;
				file = r_str_new (buf + 15);
				break;
			}
			if (!strncmp (buf, "\"e file.lastpath = ", 19)) {
				buf[strlen (buf) - 2] = 0;
				file = r_str_new (buf + 19);
				break;
			}
			// TODO: deprecate before 1.0
			if (!strncmp (buf, "e file.path = ", 14)) {
				buf[strlen (buf) - 1] = 0;
				file = r_str_new (buf + 14);
				break;
			}
		}
		fclose (fd);
	} else {
		eprintf ("Cannot open project info (%s)\n", prj);
	}
#if 0
	if (file) {
		r_cons_printf ("Project: %s\n", prj);
		r_cons_printf ("FilePath: %s\n", file);
	}
#endif
	free (prj);
	return file;
}

static int fdc;		//this is a ugly, remove it, when we have $fd

static bool store_files_and_maps (RCore *core, RIODesc *desc, ut32 id) {
	RList *maps = NULL;
	RListIter *iter;
	RIOMap *map;
	if (desc) {
		// reload bin info
		r_cons_printf ("\"obf %s\"\n", desc->uri);
		r_cons_printf ("\"ofs \\\"%s\\\" %s\"\n", desc->uri, r_str_rwx_i (desc->perm));
		if ((maps = r_io_map_get_for_fd (core->io, id))) {
			r_list_foreach (maps, iter, map) {
				r_cons_printf ("om %d 0x%"PFMT64x" 0x%"PFMT64x" 0x%"PFMT64x" %s%s%s\n", fdc,
					map->itv.addr, map->itv.size, map->delta, r_str_rwx_i (map->perm),
					map->name ? " " : "", map->name ? map->name : "");
			}
			r_list_free (maps);
		}
		fdc++;
	}
	return true;
}

static bool simpleProjectSaveScript(RCore *core, const char *file, int opts) {
	char *filename, *hl, *ohl = NULL;
	int fd, fdold;

	if (!file || * file == '\0') {
		return false;
	}

	filename = r_str_word_get_first (file);
	fd = r_sandbox_open (file, O_BINARY | O_RDWR | O_CREAT | O_TRUNC, 0644);
	if (fd == -1) {
		free (filename);
		return false;
	}

	hl = r_cons_singleton ()->highlight;
	if (hl) {
		ohl = strdup (hl);
		r_cons_highlight (NULL);
	}

	fdold = r_cons_singleton ()->fdout;
	r_cons_singleton ()->fdout = fd;
	r_cons_singleton ()->context->is_interactive = false; // NOES must use api

	r_str_write (fd, "# r2 rdb project file\n");

	if (opts & R_CORE_PRJ_EVAL) {
		r_str_write (fd, "# eval\n");
		r_config_list (core->config, NULL, true);
		r_cons_flush ();
	}

	if (opts & R_CORE_PRJ_FCNS) {
		r_str_write (fd, "# functions\n");
		r_core_cmd (core, "afl*", 0);
		r_cons_flush ();
	}

	if (opts & R_CORE_PRJ_FLAGS) {
		r_str_write (fd, "# flags\n");
		r_core_cmd (core, "f.**", 0);
		r_cons_flush ();
	}
	if (opts & R_CORE_PRJ_META) {
		r_str_write (fd, "# meta\n");
		r_meta_list (core->anal, R_META_TYPE_ANY, 1);
		r_cons_flush ();
		r_core_cmd (core, "fV*", 0);
		r_cons_flush ();
	}
	if (opts & R_CORE_PRJ_XREFS) {
		r_str_write (fd, "# xrefs\n");
		r_core_cmd (core, "ax*", 0);
		r_cons_flush ();
	}


	r_cons_singleton ()->fdout = fdold;
	r_cons_singleton ()->context->is_interactive = true;

	if (ohl) {
		r_cons_highlight (ohl);
		free (ohl);
	}

	close (fd);
	free (filename);

	return true;
}

static bool projectSaveScript(RCore *core, const char *file, int opts) {
	char *filename, *hl, *ohl = NULL;
	int fd, fdold;

	if (!file || *file == '\0') {
		return false;
	}

	filename = r_str_word_get_first (file);
	fd = r_sandbox_open (file, O_BINARY | O_RDWR | O_CREAT | O_TRUNC, 0644);
	if (fd == -1) {
		free (filename);
		return false;
	}

	hl = r_cons_singleton ()->highlight;
	if (hl) {
		ohl = strdup (hl);
		r_cons_highlight (NULL);
	}

	fdold = r_cons_singleton ()->fdout;
	r_cons_singleton ()->fdout = fd;
	r_cons_singleton ()->context->is_interactive = false;
	r_str_write (fd, "# r2 rdb project file\n");
	// Set file.path and file.lastpath to empty string to signal
	// new behaviour to project load routine (see io maps below).
	r_config_set (core->config, "file.path", "");
	r_config_set (core->config, "file.lastpath", "");
	if (opts & R_CORE_PRJ_EVAL) {
		r_str_write (fd, "# eval\n");
		r_config_list (core->config, NULL, true);
		r_cons_flush ();
	}

	if (opts & R_CORE_PRJ_FCNS) {
		r_core_cmd (core, "afl*", 0);
		r_cons_flush ();
	}

	if (opts & R_CORE_PRJ_FLAGS) {
		r_str_write (fd, "# flags\n");
		r_flag_space_push (core->flags, NULL);
		r_flag_list (core->flags, true, NULL);
		r_flag_space_pop (core->flags);
		r_cons_flush ();
	}
	if (opts & R_CORE_PRJ_IO_MAPS && core->io && core->io->files) {
		fdc = 3;
		r_id_storage_foreach (core->io->files, (RIDStorageForeachCb)store_files_and_maps, core);
		r_cons_flush ();
	}
	{
		r_core_cmd (core, "fz*", 0);
		r_cons_flush ();
	}
	if (opts & R_CORE_PRJ_META) {
		r_str_write (fd, "# meta\n");
		r_meta_list (core->anal, R_META_TYPE_ANY, 1);
		r_cons_flush ();
		r_core_cmd (core, "fV*", 0);
		r_cons_flush ();
	}
	if (opts & R_CORE_PRJ_XREFS) {
		r_core_cmd (core, "ax*", 0);
		r_cons_flush ();
	}
	if (opts & R_CORE_PRJ_FLAGS) {
		r_core_cmd (core, "f.**", 0);
		r_cons_flush ();
	}
	if (opts & R_CORE_PRJ_DBG_BREAK) {
		r_core_cmd (core, "db*", 0);
		r_cons_flush ();
	}
	if (opts & R_CORE_PRJ_ANAL_HINTS) {
		r_core_cmd (core, "ah*", 0);
		r_cons_flush ();
	}
	if (opts & R_CORE_PRJ_ANAL_TYPES) {
		r_str_write (fd, "# types\n");
		r_core_cmd (core, "t*", 0);
		r_cons_flush ();
	}
	if (opts & R_CORE_PRJ_ANAL_MACROS) {
		r_str_write (fd, "# macros\n");
		r_core_cmd (core, "(*", 0);
		r_str_write (fd, "# aliases\n");
		r_core_cmd (core, "$*", 0);
		r_cons_flush ();
	}
	if (opts & R_CORE_PRJ_ANAL_SEEK) {
		r_cons_printf ("# seek\n"
			"s 0x%08"PFMT64x "\n", core->offset);
		r_cons_flush ();
	}

	r_cons_singleton ()->fdout = fdold;
	r_cons_singleton ()->context->is_interactive = true;

	if (ohl) {
		r_cons_highlight (ohl);
		free (ohl);
	}

	close (fd);
	free (filename);

	return true;
}

// TODO: rename to r_core_project_save_script
R_API bool r_core_project_save_rdb(RCore *core, const char *file, int opts) {
	return projectSaveScript (core, file, opts);
}

#define TRANSITION 1

R_API bool r_core_project_save(RCore *core, const char *prjName) {
	bool scr_null = false;
	bool ret = true;
	char *scriptPath, *prjDir;
	SdbListIter *it;
	SdbNs *ns;
	char *oldPrjName = NULL;

	if (!prjName || !*prjName) {
		return false;
	}
	scriptPath = projectScriptPath (core, prjName);
	if (!scriptPath) {
		eprintf ("Invalid project name '%s'\n", prjName);
		return false;
	}
	if (r_str_endswith (scriptPath, R_SYS_DIR "rc")) {
		/* new project format */
		prjDir = r_file_dirname (scriptPath);
	} else {
		prjDir = r_str_newf ("%s.d", scriptPath);
	}
	if (r_file_exists (scriptPath)) {
		if (r_file_is_directory (scriptPath)) {
			eprintf ("WTF. rc is a directory?\n");
		}
		if (r_str_endswith (prjDir, ".d")) {
			eprintf ("Upgrading project...\n");
#if TRANSITION
			r_file_rm (scriptPath);
			r_sys_mkdirp (prjDir);
			eprintf ("Please remove: rm -rf %s %s.d\n", prjName, prjName);
			char *rc = r_str_newf ("%s" R_SYS_DIR "rc", prjDir);
			if (!rc) {
				free (prjDir);
				free (scriptPath);
				return false;
			}
			free (scriptPath);
			scriptPath = rc;
			free (prjDir);
			prjDir = r_file_dirname (scriptPath);
#endif
		}
	}
	if (!prjDir) {
		prjDir = strdup (prjName);
	}
	if (!r_file_exists (prjDir)) {
		r_sys_mkdirp (prjDir);
	}
	if (r_config_get_i (core->config, "scr.null")) {
		r_config_set_i (core->config, "scr.null", false);
		scr_null = true;
	}
	projectInit (core);

	Sdb *rop_db = sdb_ns (core->sdb, "rop", false);
	if (rop_db) {
		/* set filepath for all the rop sub-dbs */
		ls_foreach (rop_db->ns, it, ns) {
			char *rop_path = r_str_newf ("%s" R_SYS_DIR "rop.d" R_SYS_DIR "%s", prjDir, ns->name);
			sdb_file (ns->sdb, rop_path);
			sdb_sync (ns->sdb);
			free (rop_path);
		}
	}

	const char *oldPrjNameC = r_config_get (core->config, "prj.name");
	if (oldPrjNameC) {
		oldPrjName = strdup (oldPrjNameC);
	}
	r_config_set (core->config, "prj.name", prjName);
	if (r_config_get_i (core->config, "prj.simple")) {
		if (!simpleProjectSaveScript (core, scriptPath, R_CORE_PRJ_ALL)) {
			eprintf ("Cannot open '%s' for writing\n", prjName);
			ret = false;
		}
	} else {
		if (!projectSaveScript (core, scriptPath, R_CORE_PRJ_ALL)) {
			eprintf ("Cannot open '%s' for writing\n", prjName);
			ret = false;
		}
	}

	if (r_config_get_i (core->config, "prj.files")) {
		eprintf ("TODO: prj.files: support copying more than one file into the project directory\n");
		char *binFile = r_core_project_info (core, prjName);
		const char *binFileName = r_file_basename (binFile);
		char *prjBinDir = r_str_newf ("%s" R_SYS_DIR "bin", prjDir);
		char *prjBinFile = r_str_newf ("%s" R_SYS_DIR "%s", prjBinDir, binFileName);
		r_sys_mkdirp (prjBinDir);
		if (!r_file_copy (binFile, prjBinFile)) {
			eprintf ("Warning: Cannot copy '%s' into '%s'\n", binFile, prjBinFile);
		}
		free (prjBinFile);
		free (prjBinDir);
		free (binFile);
	}
	if (r_config_get_i (core->config, "prj.git")) {
		char *cwd = r_sys_getdir ();
		char *gitDir = r_str_newf ("%s" R_SYS_DIR ".git", prjDir);
		if (r_sys_chdir (prjDir)) {
			if (!r_file_is_directory (gitDir)) {
				r_sys_cmd ("git init");
			}
			r_sys_cmd ("git add * ; git commit -a");
		} else {
			eprintf ("Cannot chdir %s\n", prjDir);
		}
		r_sys_chdir (cwd);
		free (gitDir);
		free (cwd);
	}
	if (r_config_get_i (core->config, "prj.zip")) {
		char *cwd = r_sys_getdir ();
		const char *prjName = r_file_basename (prjDir);
		if (r_sys_chdir (prjDir)) {
			if (!strchr (prjName, '\'')) {
				r_sys_chdir ("..");
				r_sys_cmdf ("rm -f '%s.zip'; zip -r '%s'.zip '%s'",
					prjName, prjName, prjName);
			} else {
				eprintf ("Command injection attempt?\n");
			}
		} else {
			eprintf ("Cannot chdir %s\n", prjDir);
		}
		r_sys_chdir (cwd);
		free (cwd);
	}
	// LEAK : not always in heap free (prjName);
	free (prjDir);
	if (scr_null) {
		r_config_set_i (core->config, "scr.null", true);
	}
	if (!ret && oldPrjName) {
		// reset prj.name on fail
		r_config_set (core->config, "prj.name", oldPrjName);
	}
	free (scriptPath);
	free (oldPrjName);
	return ret;
}

R_API char *r_core_project_notes_file(RCore *core, const char *prjName) {
	char *notes_txt;
	const char *prjdir = r_config_get (core->config, "dir.projects");
	char *prjpath = r_file_abspath (prjdir);
	notes_txt = r_str_newf ("%s"R_SYS_DIR "%s"R_SYS_DIR "notes.txt", prjpath, prjName);
	free (prjpath);
	return notes_txt;
}

R_API bool r_core_project_load(RCore *core, const char *prjName, const char *rcpath) {
	const bool cfg_fortunes = r_config_get_i (core->config, "cfg.fortunes");
	const bool scr_interactive = r_cons_is_interactive ();
	const bool scr_prompt = r_config_get_i (core->config, "scr.prompt");
	(void) projectLoadRop (core, prjName);
	bool ret = r_core_cmd_file (core, rcpath);
	r_config_set_i (core->config, "cfg.fortunes", cfg_fortunes);
	r_config_set_i (core->config, "scr.interactive", scr_interactive);
	r_config_set_i (core->config, "scr.prompt", scr_prompt);
	r_config_bump (core->config, "asm.arch");
	return ret;
}
