/* radare - LGPL - Copyright 2013-2022 - pancake */ #include #include #define FMT_NONE 0 #define FMT_RAW 1 #define FMT_JSON 'j' #define FMT_QUIET 'q' #define FMT_EMOJI 'e' R_TH_LOCAL RList *dirstack = NULL; static char *showfile(char *res, const int nth, const char *fpath, const char *name, int printfmt, bool needs_newline) { #if __UNIX__ struct stat sb; #endif const char *n = fpath; char *nn, *u_rwx = NULL; int sz = r_file_size (n); int perm, uid = 0, gid = 0; int fch = '-'; if (!strncmp (fpath, "./", 2)) { fpath = fpath + 2; } const bool isdir = r_file_is_directory (n); if (isdir) { nn = r_str_append (strdup (fpath), "/"); } else { nn = strdup (fpath); } if (!*nn) { free (nn); return res; } perm = isdir? 0755: 0644; if (!printfmt) { res = r_str_appendf (res, "%18s%s", nn, needs_newline? "\n": " "); free (nn); return res; } // TODO: escape non-printable chars in filenames // TODO: Implement more real info in ls -l // TODO: handle suid #if __UNIX__ if (lstat (n, &sb) != -1) { ut32 ifmt = sb.st_mode & S_IFMT; uid = sb.st_uid; gid = sb.st_gid; perm = sb.st_mode & 0777; if (!(u_rwx = strdup (r_str_rwx_i (perm >> 6)))) { free (nn); return res; } if (sb.st_mode & S_ISUID) { u_rwx[2] = (sb.st_mode & S_IXUSR) ? 's' : 'S'; } if (isdir) { fch = 'd'; } else { switch (ifmt) { case S_IFCHR: fch = 'c'; break; case S_IFBLK: fch = 'b'; break; case S_IFLNK: fch = 'l'; break; case S_IFIFO: fch = 'p'; break; #ifndef __wasi__ #ifdef S_IFSOCK case S_IFSOCK: fch = 's'; break; #endif #endif } } } #else u_rwx = strdup ("-"); fch = isdir? 'd': '-'; #endif if (printfmt == FMT_QUIET) { res = r_str_appendf (res, "%s\n", nn); } else if (printfmt == FMT_EMOJI) { const char *eDIR = "📁"; const char *eIMG = "🌅"; const char *eHID = "👀"; const char *eANY = " "; // -- const char *icon = eANY; if (isdir) { icon = eDIR; #if __UNIX__ } else if ((sb.st_mode & S_IFMT) == S_IFLNK) { const char *eLNK = "📎"; icon = eLNK; } else if (sb.st_mode & S_ISUID) { const char *eUID = "🔼"; icon = eUID; #endif } else if (r_str_casestr (nn, ".jpg") || r_str_casestr (nn, ".png") || r_str_casestr (nn, ".gif")) { icon = eIMG; } else if (*nn == '.') { icon = eHID; } res = r_str_appendf (res, "%s %s\n", icon, nn); } else if (printfmt == FMT_RAW) { res = r_str_appendf (res, "%c%s%s%s 1 %4d:%-4d %-10d %s\n", isdir?'d': fch, r_str_get_fail (u_rwx, "-"), r_str_rwx_i ((perm >> 3) & 7), r_str_rwx_i (perm & 7), uid, gid, sz, nn); } else if (printfmt == FMT_JSON) { if (nth > 0) { res = r_str_append (res, ","); } PJ *pj = pj_new (); pj_o (pj); pj_ks (pj, "name", name); pj_kn (pj, "size", sz); pj_kn (pj, "uid", uid); pj_kn (pj, "gid", gid); pj_kn (pj, "perm", perm); pj_ks (pj, "perm_root", r_str_rwx_i ((perm >> 6)&7)); pj_ks (pj, "perm_group", r_str_rwx_i ((perm >> 3)&7)); pj_ks (pj, "perm_other", r_str_rwx_i (perm & 7)); pj_kb (pj, "isdir", isdir); pj_end (pj); char *js = pj_drain (pj); res = r_str_append (res, js); free (js); } else { eprintf ("unknown format\n"); } free (nn); free (u_rwx); return res; } // TODO: Move into r_util .. r_print maybe? r_cons dep is annoying R_API char *r_syscmd_ls(const char *input, int cons_width) { char *res = NULL; const char *path = "."; char *d = NULL; char *p = NULL; char *homepath = NULL; char *pattern = NULL; int printfmt = 0; RListIter *iter; char *name; char *dir; int off; if (!input) { input = ""; path = "."; } if (*input == '?') { input = "-h"; } else if (*input == 'e') { printfmt = FMT_EMOJI; input++; } else if (*input == 'j') { printfmt = FMT_JSON; input++; } else if (*input == 'q') { printfmt = FMT_QUIET; input++; } if (r_sandbox_enable (0)) { eprintf ("Sandbox forbids listing directories\n"); return NULL; } if (*input && input[0] == ' ') { input++; } if (*input) { if (!strncmp (input, "-h", 2) || *input == '?') { eprintf ("Usage: ls [-e,-l,-j,-q] [path] # long, json, quiet\n"); return NULL; } if ((!strncmp (input, "-e", 2))) { printfmt = FMT_EMOJI; path = r_str_trim_head_ro (path + 1); } else if ((!strncmp (input, "-q", 2))) { printfmt = FMT_QUIET; path = r_str_trim_head_ro (path + 1); } else if ((!strncmp (input, "-l", 2)) || (!strncmp (input, "-j", 2))) { // mode = 'l'; if (input[2]) { printfmt = (input[2] == 'j') ? FMT_JSON : FMT_RAW; path = r_str_trim_head_ro (input + 2); if (!*path) { path = "."; } } else { printfmt = FMT_RAW; } } else { path = input; } } if (!path || !*path) { path = "."; } else if (!strncmp (path, "~/", 2)) { homepath = r_str_home (path + 2); if (homepath) { path = (const char *)homepath; } } else if (*path == '$') { if (!strncmp (path + 1, "home", 4) || !strncmp (path + 1, "HOME", 4)) { homepath = r_str_home ((strlen (path) > 5)? path + 6: NULL); if (homepath) { path = (const char *)homepath; } } } if (!r_file_is_directory (path)) { p = strrchr (path, '/'); if (p) { off = p - path; d = (char *) calloc (1, off + 1); if (!d) { free (homepath); return NULL; } memcpy (d, path, off); path = (const char *)d; pattern = strdup (p + 1); } else { pattern = strdup (path); path = "."; } } else { pattern = strdup ("*"); } if (r_file_is_regular (path)) { res = showfile (res, 0, path, path, printfmt, false); free (homepath); free (pattern); free (d); return res; } RList *files = r_sys_dir (path); if (!files) { free (homepath); free (pattern); free (d); return NULL; } r_list_sort (files, (RListComparator)strcmp); if (path[strlen (path) - 1] == '/') { dir = strdup (path); } else { dir = r_str_append (strdup (path), "/"); } int nth = 0; if (printfmt == FMT_JSON) { res = strdup ("["); } bool needs_newline = false; int linelen = 0; r_list_foreach (files, iter, name) { char *n = r_str_append (strdup (dir), name); if (!n) { break; } if (r_str_glob (name, pattern)) { if (*n) { int namelen = strlen (name); linelen += (namelen > 20)? namelen*2: 32; if (linelen > cons_width) { needs_newline = true; } res = showfile (res, nth, n, name, printfmt, needs_newline); if (needs_newline) { needs_newline = false; linelen = 0; } } nth++; } free (n); } if (printfmt == FMT_JSON) { res = r_str_append (res, "]"); } else { if (res) { char * last = res + strlen (res) - 1; if (*last != '\n') { res = r_str_append (res, "\n"); } } } free (dir); free (d); free (homepath); free (pattern); r_list_free (files); return res; } static ut64 valstr(const void *_a) { const char *a = _a; return r_str_hash64 (a); } static int cmpstr(const void *_a, const void *_b) { const char *a = _a, *b = _b; return (int)strcmp (a, b); } R_API char *r_syscmd_sort(const char *file) { const char *p = NULL; RList *list = NULL; if (file) { if ((p = strchr (file, ' '))) { p = p + 1; } else { p = file; } } if (p && *p) { char *filename = strdup (p); r_str_trim (filename); char *data = r_file_slurp (filename, NULL); if (!data) { eprintf ("No such file or directory\n"); } else { list = r_str_split_list (data, "\n", 0); r_list_sort (list, cmpstr); data = r_list_to_str (list, '\n'); r_list_free (list); } free (filename); return data; } else { eprintf ("Usage: sort [file]\n"); } return NULL; } R_API char *r_syscmd_head(const char *file, int count) { const char *p = NULL; if (file) { if ((p = strchr (file, ' '))) { p = p + 1; } else { p = file; } } if (p && *p) { char *filename = strdup (p); r_str_trim (filename); char *data = r_file_slurp_lines (filename, 1, count); if (!data) { eprintf ("No such file or directory\n"); } free (filename); return data; } else { eprintf ("Usage: head 7 [file]\n"); } return NULL; } R_API char *r_syscmd_tail(const char *file, int count) { const char *p = NULL; if (file) { if ((p = strchr (file, ' '))) { p = p + 1; } else { p = file; } } if (p && *p) { char *filename = strdup (p); r_str_trim (filename); char *data = r_file_slurp_lines_from_bottom (filename, count); if (!data) { eprintf ("No such file or directory\n"); } free (filename); return data; } else { eprintf ("Usage: tail 7 [file]\n"); } return NULL; } R_API char *r_syscmd_uniq(const char *file) { const char *p = NULL; RList *list = NULL; if (file) { if ((p = strchr (file, ' '))) { p = p + 1; } else { p = file; } } if (p && *p) { char *filename = strdup (p); r_str_trim (filename); char *data = r_file_slurp (filename, NULL); if (!data) { eprintf ("No such file or directory\n"); } else { list = r_str_split_list (data, "\n", 0); RList *uniq_list = r_list_uniq (list, valstr); data = r_list_to_str (uniq_list, '\n'); r_list_free (uniq_list); r_list_free (list); } free (filename); return data; } else { eprintf ("Usage: uniq [file]\n"); } return NULL; } R_API char *r_syscmd_join(const char *file1, const char *file2) { const char *p1 = NULL, *p2 = NULL; RList *list1, *list2, *list = r_list_newf (free); if (!list) { return NULL; } if (file1) { if ((p1 = strchr (file1, ' '))) { p1++; } else { p1 = file1; } } if (file2) { if ((p2 = strchr (file2, ' '))) { p2 = p2 + 1; } else { p2 = file2; } } if (p1 && *p1 && p2 && *p2 ) { char *filename1 = strdup (p1); char *filename2 = strdup (p2); r_str_trim (filename1); r_str_trim (filename2); char *data1 = r_file_slurp (filename1, NULL); char *data2 = r_file_slurp (filename2, NULL); char *data = NULL; RListIter *iter1, *iter2; if (!data1 || !data2) { R_LOG_ERROR ("No such files or directory"); } else { list1 = r_str_split_list (data1, "\n", 0); list2 = r_str_split_list (data2, "\n", 0); char *str1, *str2; r_list_foreach (list1, iter1, str1) { char *field = strdup (str1); // extract comman field char *end = strchr (field, ' '); if (end) { *end = '\0'; } else { free (field); continue; } r_list_foreach (list2, iter2, str2) { if (r_str_startswith (str2, field)) { char *out = r_str_new (field); char *first = strchr (str1, ' '); char *second = strchr (str2, ' '); out = r_str_append (out, r_str_get_fail (first, " ")); out = r_str_append (out, r_str_get_fail (second, " ")); r_list_append (list, out); } } free (field); } data = r_list_to_str (list, '\n'); r_list_free (list1); r_list_free (list2); } r_list_free (list); free (filename1); free (filename2); free (data1); free (data2); return data; } else { eprintf ("Usage: join file1 file2\n"); } return NULL; } R_API char *r_syscmd_cat(const char *file) { const char *p = NULL; if (file) { if ((p = strchr (file, ' '))) { p = p + 1; } else { p = file; } } if (p && *p) { char *filename = strdup (p); r_str_trim (filename); char *data = r_file_slurp (filename, NULL); if (!data) { eprintf ("No such file or directory\n"); } free (filename); return data; } else { eprintf ("Usage: cat [file]\n"); } return NULL; } R_API char *r_syscmd_mktemp(const char *dir) { const char *space = strchr (dir, ' '); const char *suffix = space? r_str_trim_head_ro (space): ""; if (!*suffix || (!strncmp (suffix, "-d ", 3) && strstr (suffix, " -"))) { eprintf ("Usage: mktemp [-d] [file|directory]\n"); return NULL; } bool dodir = (bool) strstr (suffix, "-d"); int ret; char *dirname = (!strncmp (suffix, "-d ", 3)) ? strdup (suffix + 3): strdup (suffix); r_str_trim (dirname); char *arg = NULL; if (!*dirname || *dirname == '-') { eprintf ("Usage: mktemp [-d] [file|directory]\n"); free (dirname); return NULL; } int fd = r_file_mkstemp (dirname, &arg); if (fd != -1) { ret = 1; close (fd); } else { ret = 0; } if (ret && dodir) { r_file_rm (arg); ret = r_sys_mkdirp (arg); } if (!ret) { eprintf ("Cannot create '%s'\n", dirname); free (dirname); return NULL; } return dirname; } R_API bool r_syscmd_mkdir(const char *dir) { const char *space = strchr (dir, ' '); const char *suffix = space? r_str_trim_head_ro (space): ""; if (!*suffix || (!strncmp (suffix, "-p ", 3) && strstr (suffix, " -"))) { eprintf ("Usage: mkdir [-p] [directory]\n"); return false; } char *dirname = (!strncmp (suffix, "-p ", 3)) ? strdup (suffix + 3): strdup (suffix); r_str_trim (dirname); if (!*dirname || *dirname == '-') { eprintf ("Usage: mkdir [-p] [directory]\n"); free (dirname); return false; } if (!r_sys_mkdirp (dirname)) { if (r_sys_mkdir_failed ()) { eprintf ("Cannot create '%s'\n", dirname); free (dirname); return false; } } free (dirname); return true; } R_API bool r_syscmd_pushd(const char *input) { if (!dirstack) { dirstack = r_list_newf (free); } char *cwd = r_sys_getdir (); if (!cwd) { eprintf ("Where am I?\n"); return false; } bool suc = r_sys_chdir (input); if (suc) { r_list_push (dirstack, cwd); } else { eprintf ("Cannot chdir\n"); } return suc; } R_API bool r_syscmd_popd(void) { if (!dirstack) { return false; } char *d = r_list_pop (dirstack); if (d) { r_sys_chdir (d); eprintf ("%s\n", d); free (d); } if (r_list_empty (dirstack)) { r_list_free (dirstack); dirstack = NULL; return false; } return true; } R_API bool r_syscmd_popalld(void) { if (!dirstack || r_list_empty (dirstack)) { return false; } while (r_syscmd_popd ()) { // wait for it } return true; } R_API bool r_syscmd_mv(const char *input) { if (strlen (input) < 3) { eprintf ("Usage: mv src dst\n"); return false; } char *inp = strdup (input + 2); char *arg = strchr (inp, ' '); bool rc = false; if (arg) { *arg++ = 0; if (!(rc = r_file_move (inp, arg))) { eprintf ("Cannot move file\n"); } } else { eprintf ("Usage: mv src dst\n"); } free (inp); return rc; }