// SPDX-FileCopyrightText: 2013-2020 pancake // SPDX-License-Identifier: LGPL-3.0-only #include #include #define FMT_NONE 0 #define FMT_RAW 1 #define FMT_JSON 2 static int needs_newline = 0; static char *showfile(char *res, const int nth, const char *fpath, const char *name, int printfmt) { #if __UNIX__ struct stat sb; #endif const char *n = fpath; char *nn, *u_rwx = NULL; int sz = rz_file_size(n); int perm, uid = 0, gid = 0; int fch = '-'; if (!strncmp(fpath, "./", 2)) { fpath = fpath + 2; } const bool isdir = rz_file_is_directory(n); if (isdir) { nn = rz_str_append(strdup(fpath), "/"); } else { nn = strdup(fpath); } if (!*nn) { free(nn); return res; } perm = isdir ? 0755 : 0644; if (!printfmt) { needs_newline = ((nth + 1) % 4) ? 1 : 0; res = rz_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(rz_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; #ifdef S_IFSOCK case S_IFSOCK: fch = 's'; break; #endif } } } #else u_rwx = strdup("-"); fch = isdir ? 'd' : '-'; #endif if (printfmt == 'q') { res = rz_str_appendf(res, "%s\n", nn); } else if (printfmt == 'e') { 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 (rz_str_casestr(nn, ".jpg") || rz_str_casestr(nn, ".png") || rz_str_casestr(nn, ".gif")) { icon = eIMG; } else if (*nn == '.') { icon = eHID; } res = rz_str_appendf(res, "%s %s\n", icon, nn); } else if (printfmt == FMT_RAW) { res = rz_str_appendf(res, "%c%s%s%s 1 %4d:%-4d %-10d %s\n", isdir ? 'd' : fch, u_rwx ? u_rwx : "-", rz_str_rwx_i((perm >> 3) & 7), rz_str_rwx_i(perm & 7), uid, gid, sz, nn); } else if (printfmt == FMT_JSON) { if (nth > 0) { res = rz_str_append(res, ","); } res = rz_str_appendf(res, "{\"name\":\"%s\",\"size\":%d,\"uid\":%d," "\"gid\":%d,\"perm\":%d,\"isdir\":%s}", name, sz, uid, gid, perm, isdir ? "true" : "false"); } free(nn); free(u_rwx); return res; } RZ_API RZ_OWN char *rz_syscmd_ls(RZ_NONNULL const char *input) { char *res = NULL; const char *path = "."; char *d = NULL; char *p = NULL; char *homepath = NULL; char *pattern = NULL; int printfmt = 0; RzListIter *iter; RzList *files; char *name; char *dir; int off; if (!input) { input = ""; path = "."; } if (*input == 'q') { printfmt = 'q'; input++; } if (*input && input[0] == ' ') { input++; } if (*input) { if ((!strncmp(input, "-h", 2))) { eprintf("Usage: ls ([-e,-l,-j,-q]) ([path]) # long, json, quiet\n"); } else if ((!strncmp(input, "-e", 2))) { printfmt = 'e'; path = rz_str_trim_head_ro(path + 1); } else if ((!strncmp(input, "-q", 2))) { printfmt = 'q'; path = rz_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 = rz_str_trim_head_ro(input + 2); if (!*path) { path = "."; } } else { printfmt = FMT_RAW; } } else { path = input; } } if (!path || !*path) { path = "."; } else if (rz_str_startswith(path, "~")) { homepath = rz_path_home_expand(path); if (homepath) { path = (const char *)homepath; } } else if (*path == '$') { if (!strncmp(path + 1, "home", 4) || !strncmp(path + 1, "HOME", 4)) { homepath = rz_str_home((strlen(path) > 5) ? path + 6 : NULL); if (homepath) { path = (const char *)homepath; } } } if (!rz_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 (rz_file_is_regular(path)) { res = showfile(res, 0, path, path, printfmt); free(homepath); free(pattern); free(d); return res; } files = rz_sys_dir(path); if (path[strlen(path) - 1] == '/') { dir = strdup(path); } else { dir = rz_str_append(strdup(path), "/"); } int nth = 0; if (printfmt == FMT_JSON) { res = strdup("["); } needs_newline = 0; rz_list_foreach (files, iter, name) { char *n = rz_str_append(strdup(dir), name); if (!n) { break; } if (rz_str_glob(name, pattern)) { if (*n) { res = showfile(res, nth, n, name, printfmt); } nth++; } free(n); } if (printfmt == FMT_JSON) { res = rz_str_append(res, "]"); } if (needs_newline) { res = rz_str_append(res, "\n"); } free(dir); free(d); free(homepath); free(pattern); rz_list_free(files); return res; } static int cmpstr(const void *_a, const void *_b) { const char *a = _a, *b = _b; return (int)strcmp(a, b); } RZ_API RZ_OWN char *rz_syscmd_sort(RZ_NONNULL const char *file) { rz_return_val_if_fail(file, NULL); const char *p = NULL; RzList *list = NULL; if ((p = strchr(file, ' '))) { p = p + 1; } else { p = file; } if (p && *p) { char *filename = strdup(p); rz_str_trim(filename); char *data = rz_file_slurp(filename, NULL); if (!data) { eprintf("No such file or directory\n"); } else { list = rz_str_split_list(data, "\n", 0); rz_list_sort(list, cmpstr); data = rz_list_to_str(list, '\n'); rz_list_free(list); } free(filename); return data; } else { eprintf("Usage: sort [file]\n"); } return NULL; } RZ_API RZ_OWN char *rz_syscmd_head(RZ_NONNULL const char *file, int count) { rz_return_val_if_fail(file, NULL); const char *p = NULL; if ((p = strchr(file, ' '))) { p = p + 1; } else { p = file; } if (p && *p) { char *filename = strdup(p); rz_str_trim(filename); char *data = rz_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; } RZ_API RZ_OWN char *rz_syscmd_tail(RZ_NONNULL const char *file, int count) { rz_return_val_if_fail(file, NULL); const char *p = NULL; if (file) { if ((p = strchr(file, ' '))) { p = p + 1; } else { p = file; } } if (p && *p) { char *filename = strdup(p); rz_str_trim(filename); char *data = rz_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; } RZ_API RZ_OWN char *rz_syscmd_uniq(RZ_NONNULL const char *file) { rz_return_val_if_fail(file, NULL); const char *p = NULL; RzList *list = NULL; if (file) { if ((p = strchr(file, ' '))) { p = p + 1; } else { p = file; } } if (p && *p) { char *filename = strdup(p); rz_str_trim(filename); char *data = rz_file_slurp(filename, NULL); if (!data) { eprintf("No such file or directory\n"); } else { list = rz_str_split_list(data, "\n", 0); RzList *uniq_list = rz_list_uniq(list, cmpstr); data = rz_list_to_str(uniq_list, '\n'); rz_list_free(uniq_list); rz_list_free(list); } free(filename); return data; } else { eprintf("Usage: uniq [file]\n"); } return NULL; } RZ_API RZ_OWN char *rz_syscmd_join(RZ_NONNULL const char *file1, RZ_NONNULL const char *file2) { rz_return_val_if_fail(file1 && file2, NULL); const char *p1 = NULL, *p2 = NULL; if (file1) { if ((p1 = strchr(file1, ' '))) { p1 = p1 + 1; } 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); rz_str_trim(filename1); rz_str_trim(filename2); char *data1 = rz_file_slurp(filename1, NULL); char *data2 = rz_file_slurp(filename2, NULL); char *data = NULL; if (!data1 && !data2) { eprintf("No such files or directory\n"); } else { RzList *list = rz_list_newf(NULL); RzList *list1 = rz_str_split_list(data1, "\n", 0); RzList *list2 = rz_str_split_list(data2, "\n", 0); if (!list || !list1 || !list2) { rz_list_free(list2); rz_list_free(list1); rz_list_free(list); return NULL; } char *str1, *str2; RzListIter *iter1, *iter2; rz_list_foreach (list1, iter1, str1) { char *field = strdup(str1); // extract comman field char *end = strchr(field, ' '); if (end) { *end = '\0'; } else { free(field); continue; } rz_list_foreach (list2, iter2, str2) { if (rz_str_startswith(str2, field)) { char *out = rz_str_new(field); char *first = strchr(str1, ' '); char *second = strchr(str2, ' '); rz_str_append(out, first ? first : " "); rz_str_append(out, second ? second : " "); rz_list_append(list, out); } } free(field); } data = rz_list_to_str(list, '\n'); rz_list_free(list2); rz_list_free(list1); rz_list_free(list); } free(filename1); free(filename2); return data; } else { eprintf("Usage: join file1 file2\n"); } return NULL; } RZ_API RZ_OWN char *rz_syscmd_cat(RZ_NONNULL const char *file) { rz_return_val_if_fail(file, NULL); const char *p = NULL; if (file) { if ((p = strchr(file, ' '))) { p = p + 1; } else { p = file; } } if (p && *p) { char *filename = strdup(p); rz_str_trim(filename); char *data = rz_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; } RZ_API RZ_OWN char *rz_syscmd_mkdir(RZ_NONNULL const char *dir) { rz_return_val_if_fail(dir, NULL); const char *suffix = rz_str_trim_head_ro(strchr(dir, ' ')); if (!suffix || !strncmp(suffix, "-p", 3)) { return rz_str_dup(NULL, "Usage: mkdir [-p] [directory]\n"); } int ret; char *dirname = (!strncmp(suffix, "-p ", 3)) ? strdup(suffix + 3) : strdup(suffix); rz_str_trim(dirname); ret = rz_sys_mkdirp(dirname); if (!ret && rz_sys_mkdir_failed()) { char *res = rz_str_newf("Cannot create \"%s\"\n", dirname); free(dirname); return res; } free(dirname); return NULL; }