mirror of
https://github.com/radareorg/radare2.git
synced 2024-11-25 06:09:50 +00:00
581 lines
13 KiB
C
581 lines
13 KiB
C
/* radare - LGPL - Copyright 2021 - pancake */
|
|
|
|
#include <r_main.h>
|
|
|
|
static int r_main_r2pm_sh(int argc, const char **argv) {
|
|
int i;
|
|
RStrBuf *sb = r_strbuf_new ("r2pm.sh");
|
|
for (i = 1; i < argc; i++) {
|
|
r_strbuf_appendf (sb, " %s", argv[i]);
|
|
}
|
|
char *cmd = r_strbuf_drain (sb);
|
|
int res = r_sandbox_system (cmd, 1);
|
|
free (cmd);
|
|
return res;
|
|
}
|
|
|
|
static const char *helpmsg = \
|
|
"Usage: r2pm [-flags] [pkgs...]\n"\
|
|
"Commands:\n"\
|
|
" -c ([git/dir]) clear source cache (GITDIR)\n"\
|
|
" -ci <pkgname> clean + install\n"\
|
|
" -cp clean the user's home plugin directory\n"\
|
|
" -d,doc [pkgname] show documentation for given package\n"\
|
|
" -f force operation (install, uninstall, ..)\n"\
|
|
" -gi <pkg> global install (system-wide)\n"\
|
|
" -h show this message\n"\
|
|
" -H variable show value of given variable\n"\
|
|
" -I information about repository and installed packages\n"\
|
|
" -i <pkgname> install or update package in your home (pkgname=all)\n"\
|
|
" -l list installed pkgs\n"\
|
|
" -r [cmd ...args] run shell command with R2PM_BINDIR in PATH\n"\
|
|
" -s [<keyword>] search in database\n"\
|
|
" -uci <pkgname> uninstall + clean + install\n"\
|
|
" -ui <pkgname> uninstall + install\n"\
|
|
" -u <pkgname> r2pm -u baleful (See -f to force uninstall)\n"\
|
|
" -U r2pm -U (upgrade all outdated packages)\n"\
|
|
" -v show version\n"\
|
|
"Environment:\n"\
|
|
" SUDO=sudo use this tool as sudo\n"\
|
|
" R2PM_PLUGDIR=${R2PM_PLUGDIR}\n"\
|
|
" R2PM_BINDIR=${R2PM_BINDIR}\n"\
|
|
" R2PM_OFFLINE=0 disabled by default, avoid init/update calls if set to !=0\n"\
|
|
" R2PM_NATIVE=0 set to 1 to use the native C codepath for r2pm\n"\
|
|
" R2PM_DBDIR=${R2PM_DBDIR}\n"\
|
|
" R2PM_GITDIR=${R2PM_GITDIR}\n"\
|
|
" R2PM_GITSKIP=${R2PM_GITSKIP}\n";
|
|
|
|
typedef struct r_r2pm_t {
|
|
bool help;
|
|
bool clean;
|
|
bool force;
|
|
bool global;
|
|
bool list;
|
|
bool init;
|
|
bool run;
|
|
bool doc;
|
|
bool search;
|
|
bool version;
|
|
bool info;
|
|
bool install;
|
|
bool uninstall;
|
|
} R2Pm;
|
|
|
|
static int git_pull(const char *dir) {
|
|
char *s = r_str_newf ("cd %s\ngit pull", dir);
|
|
int rc = r_sandbox_system (s, 1);
|
|
free (s);
|
|
return rc;
|
|
}
|
|
|
|
static int git_clone(const char *dir, const char *url) {
|
|
char *cmd = r_str_newf ("git clone --depth=10 --recursive %s %s", url, dir);
|
|
int rc = r_sandbox_system (cmd, 1);
|
|
free (cmd);
|
|
return rc;
|
|
}
|
|
|
|
static bool r2pm_actionword(R2Pm *r2pm, const char *action) {
|
|
if (!strcmp (action, "init") || !strcmp (action, "update")) {
|
|
r2pm->init = true;
|
|
} else if (!strcmp (action, "help")) {
|
|
r2pm->help = true;
|
|
} else if (!strcmp (action, "info")) {
|
|
r2pm->info = true;
|
|
} else if (!strcmp (action, "help")) {
|
|
r2pm->help = true;
|
|
} else {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static char *r2pm_bindir(void) {
|
|
return r_str_home (".local/share/radare2/prefix/bin");
|
|
}
|
|
|
|
static char *r2pm_gitdir(void) {
|
|
return r_str_home (".local/share/radare2/r2pm/git");
|
|
}
|
|
|
|
static char *r2pm_dbdir(void) {
|
|
return r_str_home (".local/share/radare2/r2pm/db");
|
|
}
|
|
|
|
static char *r2pm_pkgdir(void) {
|
|
return r_str_home (".local/share/radare2/r2pm/pkg");
|
|
}
|
|
|
|
typedef enum {
|
|
TT_OPT_QUOTED_LINE,
|
|
TT_CODEBLOCK,
|
|
TT_ENDQUOTE,
|
|
} R2pmTokenType;
|
|
|
|
static char *r2pm_get(const char *file, const char *token, R2pmTokenType type) {
|
|
char *res = NULL;
|
|
char *dbdir = r2pm_dbdir ();
|
|
char *path = r_str_newf ("%s/%s", dbdir, file);
|
|
free (dbdir);
|
|
char *data = r_file_slurp (path, NULL);
|
|
if (!data) {
|
|
free (path);
|
|
return NULL;
|
|
}
|
|
const char *needle = token; // "\nR2PM_DESC ";
|
|
char *descptr = strstr (data, needle);
|
|
if (descptr) {
|
|
char *nl = NULL;
|
|
switch (type) {
|
|
case TT_OPT_QUOTED_LINE:
|
|
descptr += strlen (needle);
|
|
nl = strchr (descptr, '\n');
|
|
if (nl) {
|
|
*nl = 0;
|
|
nl--;
|
|
if (*nl == '"') {
|
|
*nl = 0;
|
|
}
|
|
}
|
|
if (*descptr == '"') {
|
|
descptr++;
|
|
}
|
|
res = strdup (descptr);
|
|
break;
|
|
case TT_ENDQUOTE:
|
|
nl = strchr (descptr + strlen (token), '\n');
|
|
if (nl) {
|
|
char *begin = nl + 1;
|
|
char *eoc = strstr (begin, "\n\"\n");
|
|
if (eoc) {
|
|
return r_str_ndup (begin, eoc-begin);
|
|
} else {
|
|
eprintf ("Cannot find end of thing\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
break;
|
|
case TT_CODEBLOCK:
|
|
nl = strchr (descptr + strlen (token), '\n');
|
|
if (nl) {
|
|
char *begin = nl + 1;
|
|
char *eoc = strstr (begin, "\n}\n");
|
|
if (eoc) {
|
|
return r_str_ndup (begin, eoc-begin);
|
|
} else {
|
|
eprintf ("Cannot find end of thing\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
free (data);
|
|
return res;
|
|
}
|
|
|
|
static char *r2pm_desc(const char *file) {
|
|
return r2pm_get (file, "\nR2PM_DESC ", TT_OPT_QUOTED_LINE);
|
|
}
|
|
|
|
static int r2pm_list(void) {
|
|
char *path = r2pm_pkgdir ();
|
|
RList *files = r_sys_dir (path);
|
|
free (path);
|
|
if (!files) {
|
|
return 1;
|
|
}
|
|
RListIter *iter;
|
|
const char *file;
|
|
r_list_foreach (files, iter, file) {
|
|
if (*file != '.') {
|
|
printf ("%s\n", file);
|
|
}
|
|
}
|
|
r_list_free (files);
|
|
return 0;
|
|
}
|
|
|
|
static int r2pm_update(void) {
|
|
char *gpath = r2pm_gitdir ();
|
|
char *pmpath = r_str_newf ("%s/%s", gpath, "radare2-pm");
|
|
r_sys_mkdirp (gpath);
|
|
if (r_file_exists (pmpath)) {
|
|
if (git_pull (pmpath) != 0) {
|
|
eprintf ("Error\n");
|
|
free (pmpath);
|
|
free (gpath);
|
|
return 1;
|
|
}
|
|
} else {
|
|
const char *giturl = "https://github.com/radareorg/radare2-pm";
|
|
git_clone (pmpath, giturl);
|
|
}
|
|
|
|
// copy files from git into db
|
|
char *dbpath = r2pm_dbdir ();
|
|
r_sys_mkdirp (dbpath);
|
|
RList *files = r_sys_dir (pmpath);
|
|
if (files) {
|
|
RListIter *iter;
|
|
const char *file;
|
|
r_list_foreach (files, iter, file) {
|
|
if (*file != '.') {
|
|
char *src = r_str_newf ("%s/%s", pmpath, file);
|
|
char *dst = r_str_newf ("%s/%s", dbpath, file);
|
|
if (!r_file_copy (src, dst)) {
|
|
eprintf ("Warning: Cannot copy '%s' into '%s'.\n", file, dbpath);
|
|
}
|
|
free (src);
|
|
free (dst);
|
|
}
|
|
}
|
|
r_list_free (files);
|
|
}
|
|
free (pmpath);
|
|
free (gpath);
|
|
free (dbpath);
|
|
return 0;
|
|
}
|
|
|
|
static void r2pm_setenv(void) {
|
|
r_sys_setenv ("MAKE", "make");
|
|
char *r2_plugdir = r_str_home (R2_HOME_PLUGINS);
|
|
r_sys_setenv ("R2PM_PLUGDIR", r2_plugdir);
|
|
free (r2_plugdir);
|
|
|
|
char *r2_prefix = r_str_home (R2_HOME_DATADIR "/prefix");
|
|
r_sys_setenv ("R2PM_PREFIX", r2_prefix);
|
|
free (r2_prefix);
|
|
}
|
|
|
|
static int r2pm_install_pkg(const char *pkg) {
|
|
printf ("Installing %s ...\n", pkg);
|
|
char *script = r2pm_get (pkg, "\nR2PM_INSTALL() {", TT_CODEBLOCK);
|
|
if (!script) {
|
|
eprintf ("Cannot parse package\n");
|
|
return 1;
|
|
}
|
|
r2pm_setenv ();
|
|
char *srcdir = r2pm_gitdir ();
|
|
char *s = r_str_newf ("cd '%s/%s'\nexport MAKE=make\nR2PM_FAIL(){\n echo $@\n}\n%s", srcdir, pkg, script);
|
|
int res = r_sandbox_system (s, 1);
|
|
free (s);
|
|
free (srcdir);
|
|
return res;
|
|
}
|
|
|
|
static int r2pm_doc_pkg(const char *pkg) {
|
|
char *docstr = r2pm_get (pkg, "\nR2PM_DOC=\"", TT_ENDQUOTE);
|
|
if (docstr) {
|
|
printf ("%s\n", docstr);
|
|
free (docstr);
|
|
return 0;
|
|
}
|
|
eprintf ("Cannot find documentation for '%s'\n", pkg);
|
|
return 1;
|
|
}
|
|
|
|
static int r2pm_clean_pkg(const char *pkg) {
|
|
printf ("Cleaning %s ...\n", pkg);
|
|
// TODO. make clean/mrproper instead maybe better?
|
|
char *srcdir = r2pm_gitdir ();
|
|
if (R_STR_ISNOTEMPTY (srcdir)) {
|
|
char *d = r_file_new (srcdir, pkg, NULL);
|
|
if (d && r_file_exists (d)) {
|
|
eprintf ("rm -rf '%s'\n", d);
|
|
r_file_rm_rf (d);
|
|
}
|
|
free (d);
|
|
}
|
|
free (srcdir);
|
|
return 0;
|
|
}
|
|
|
|
static int r2pm_uninstall_pkg(const char *pkg) {
|
|
printf ("Uninstalling %s ...\n", pkg);
|
|
char *script = r2pm_get (pkg, "\nR2PM_UNINSTALL() {", TT_CODEBLOCK);
|
|
if (!script) {
|
|
eprintf ("Cannot parse package\n");
|
|
return 1;
|
|
}
|
|
r2pm_setenv ();
|
|
char *srcdir = r2pm_gitdir ();
|
|
char *s = r_str_newf ("cd %s/%s\nexport MAKE=make\nR2PM_FAIL(){\n echo $@\n}\n%s",
|
|
srcdir, pkg, script);
|
|
int res = r_sandbox_system (s, 1);
|
|
free (s);
|
|
free (srcdir);
|
|
return res;
|
|
}
|
|
|
|
static int r2pm_clone(const char *pkg) {
|
|
char *pkgdir = r2pm_gitdir ();
|
|
char *srcdir = r_file_new (pkgdir, pkg, NULL);
|
|
free (pkgdir);
|
|
if (r_file_is_directory (srcdir)) {
|
|
git_pull (srcdir);
|
|
} else {
|
|
char *url = r2pm_get (pkg, "\nR2PM_GIT ", TT_OPT_QUOTED_LINE);
|
|
if (url) {
|
|
git_clone (srcdir, url);
|
|
free (url);
|
|
} else {
|
|
char *url = r2pm_get (pkg, "\nR2PM_TGZ", TT_OPT_QUOTED_LINE);
|
|
eprintf ("TODO: wget tarball from '%s'\n", url);
|
|
free (srcdir);
|
|
free (url);
|
|
return 1;
|
|
}
|
|
}
|
|
free (srcdir);
|
|
return 0;
|
|
}
|
|
|
|
static int r2pm_install(RList *targets, bool uninstall, bool clean) {
|
|
RListIter *iter;
|
|
const char *t;
|
|
int rc = 0;
|
|
r_list_foreach (targets, iter, t) {
|
|
if (uninstall) {
|
|
r2pm_uninstall_pkg (t);
|
|
}
|
|
if (clean) {
|
|
r2pm_clean_pkg (t);
|
|
}
|
|
r2pm_clone (t);
|
|
rc |= r2pm_install_pkg (t);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int r2pm_doc(RList *targets) {
|
|
RListIter *iter;
|
|
const char *t;
|
|
int rc = 0;
|
|
r_list_foreach (targets, iter, t) {
|
|
rc |= r2pm_doc_pkg (t);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int r2pm_clean(RList *targets) {
|
|
RListIter *iter;
|
|
const char *t;
|
|
int rc = 0;
|
|
r_list_foreach (targets, iter, t) {
|
|
rc |= r2pm_clean_pkg (t);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int r2pm_uninstall(RList *targets) {
|
|
RListIter *iter;
|
|
const char *t;
|
|
int rc = 0;
|
|
r_list_foreach (targets, iter, t) {
|
|
rc |= r2pm_uninstall_pkg (t);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static bool is_valid_package(const char *dbdir, const char *pkg) {
|
|
if (*pkg == '.') {
|
|
return false;
|
|
}
|
|
char *script = r2pm_get (pkg, "\nR2PM_INSTALL() {", TT_CODEBLOCK);
|
|
if (!script) {
|
|
eprintf ("Warning: Unable to find R2PM_INSTALL script in '%s'\n", pkg);
|
|
return false;
|
|
}
|
|
free (script);
|
|
return true;
|
|
}
|
|
|
|
static int count_available(void) {
|
|
char *dbdir = r2pm_dbdir ();
|
|
RListIter *iter;
|
|
const char *c;
|
|
RList *dbfiles = r_sys_dir (dbdir);
|
|
int count = 0;
|
|
r_list_foreach (dbfiles, iter, c) {
|
|
if (is_valid_package (dbdir, c)) {
|
|
count ++;
|
|
}
|
|
}
|
|
r_list_free (dbfiles);
|
|
free (dbdir);
|
|
return count;
|
|
}
|
|
|
|
static int count_installed(void) {
|
|
char *dbdir = r2pm_pkgdir ();
|
|
RListIter *iter;
|
|
const char *c;
|
|
RList *dbfiles = r_sys_dir (dbdir);
|
|
int count = 0;
|
|
r_list_foreach (dbfiles, iter, c) {
|
|
if (*c != '.') {
|
|
count ++;
|
|
}
|
|
}
|
|
r_list_free (dbfiles);
|
|
free (dbdir);
|
|
return count;
|
|
}
|
|
|
|
static int r2pm_info(void) {
|
|
const int installed_packages = count_installed ();
|
|
const int available_packages = count_available ();
|
|
printf ("Installed %d packages of %d in database\n",
|
|
installed_packages, available_packages);
|
|
return 0;
|
|
}
|
|
|
|
static int r2pm_search(const char *grep) {
|
|
char *path = r2pm_dbdir ();
|
|
RList *files = r_sys_dir (path);
|
|
free (path);
|
|
if (!files) {
|
|
return 1;
|
|
}
|
|
RListIter *iter;
|
|
const char *file;
|
|
r_list_foreach (files, iter, file) {
|
|
if (*file != '.') {
|
|
char *desc = r2pm_desc (file);
|
|
if (!grep || (strstr (desc, grep) || strstr (file, grep))) {
|
|
eprintf ("%s%s%s\n", file, r_str_pad (' ', 20 - strlen (file)), desc);
|
|
}
|
|
free (desc);
|
|
}
|
|
}
|
|
r_list_free (files);
|
|
return 0;
|
|
}
|
|
|
|
static int r_main_r2pm_c(int argc, const char **argv) {
|
|
R2Pm r2pm = {0};
|
|
RGetopt opt;
|
|
r_getopt_init (&opt, argc, argv, "cdiIhflgrsu");
|
|
if (opt.ind < argc) {
|
|
// TODO: deprecate, only use flags imho
|
|
r2pm_actionword (&r2pm, argv[opt.ind]);
|
|
}
|
|
int i, c;
|
|
while ((c = r_getopt_next (&opt)) != -1) {
|
|
switch (c) {
|
|
case 'c':
|
|
r2pm.clean = true;
|
|
break;
|
|
case 'i':
|
|
r2pm.install = true;
|
|
break;
|
|
case 'd':
|
|
r2pm.doc = true;
|
|
break;
|
|
case 'I':
|
|
r2pm.info = true;
|
|
break;
|
|
case 'u':
|
|
r2pm.uninstall = true;
|
|
break;
|
|
case 'f':
|
|
r2pm.force = true;
|
|
break;
|
|
case 'l':
|
|
r2pm.list = true;
|
|
break;
|
|
case 's':
|
|
r2pm.search = true;
|
|
break;
|
|
case 'r':
|
|
r2pm.run = true;
|
|
break;
|
|
case 'g':
|
|
r2pm.global = true;
|
|
break;
|
|
case 'h':
|
|
r2pm.help = true;
|
|
break;
|
|
}
|
|
}
|
|
if (r2pm.help || argc == 1) {
|
|
printf ("%s", helpmsg);
|
|
return 0;
|
|
}
|
|
if (r2pm.init) {
|
|
return r2pm_update ();
|
|
}
|
|
if (r2pm.run) {
|
|
char *opath = r_sys_getenv ("PATH");
|
|
if (opath) {
|
|
char *bindir = r2pm_bindir ();
|
|
const char *sep = R_SYS_ENVSEP;
|
|
char *newpath = r_str_newf ("%s%s%s", bindir, sep, opath);
|
|
r_sys_setenv ("PATH", newpath);
|
|
free (newpath);
|
|
free (opath);
|
|
free (bindir);
|
|
}
|
|
int i;
|
|
RStrBuf *sb = r_strbuf_new ("");
|
|
for (i = opt.ind; i < argc; i++) {
|
|
r_strbuf_appendf (sb, " %s", argv[i]);
|
|
}
|
|
char *cmd = r_strbuf_drain (sb);
|
|
int res = r_sandbox_system (cmd, 1);
|
|
free (cmd);
|
|
return res;
|
|
}
|
|
RList *targets = r_list_newf (free);
|
|
for (i = opt.ind; i < argc; i++) {
|
|
r_list_append (targets, strdup (argv[i]));
|
|
}
|
|
int res = -1;
|
|
if (r2pm.search) {
|
|
res = r2pm_search (argv[opt.ind]);
|
|
} else if (r2pm.info) {
|
|
res = r2pm_info ();
|
|
} else if (r2pm.doc) {
|
|
res = r2pm_doc (targets);
|
|
} else if (r2pm.install) {
|
|
res = r2pm_install (targets, r2pm.uninstall, r2pm.clean);
|
|
} else if (r2pm.uninstall) {
|
|
res = r2pm_uninstall (targets);
|
|
} else if (r2pm.clean) {
|
|
res = r2pm_clean (targets);
|
|
} else if (r2pm.list) {
|
|
res = r2pm_list ();
|
|
}
|
|
r_list_free (targets);
|
|
if (res != -1) {
|
|
return res;
|
|
}
|
|
if (opt.ind == 1) {
|
|
return 0;
|
|
}
|
|
eprintf ("TODO\n");
|
|
#if __WINDOWS__
|
|
bool use_c_impl = true;
|
|
#else
|
|
bool use_c_impl = r_sys_getenv_asbool ("R2PM_NATIVE");
|
|
#endif
|
|
if (use_c_impl) {
|
|
return 1;
|
|
}
|
|
return r_main_r2pm_sh (argc, argv);
|
|
}
|
|
|
|
R_API int r_main_r2pm(int argc, const char **argv) {
|
|
#if __WINDOWS__
|
|
bool use_c_impl = true;
|
|
#else
|
|
bool use_c_impl = r_sys_getenv_asbool ("R2PM_NATIVE");
|
|
#endif
|
|
if (use_c_impl) {
|
|
return r_main_r2pm_c (argc, argv);
|
|
}
|
|
return r_main_r2pm_sh (argc, argv);
|
|
}
|