void files_chmod(FileView *view, const char *mode, int recurse_dirs) { int i; ui_cancellation_reset(); i = 0; while(i < view->list_rows && !view->dir_entry[i].selected) i++; if(i == view->list_rows) { char buf[COMMAND_GROUP_INFO_LEN]; char inv[16]; snprintf(buf, sizeof(buf), "chmod in %s: %s", replace_home_part(flist_get_dir(view)), view->dir_entry[view->list_pos].name); cmd_group_begin(buf); snprintf(inv, sizeof(inv), "0%o", view->dir_entry[view->list_pos].mode & 0xff); chmod_file_in_list(view, view->list_pos, mode, inv, recurse_dirs); } else { char buf[COMMAND_GROUP_INFO_LEN]; size_t len; int j = i; len = snprintf(buf, sizeof(buf), "chmod in %s: ", replace_home_part(flist_get_dir(view))); while(i < view->list_rows && len < sizeof(buf)) { if(view->dir_entry[i].selected) { if(len >= 2 && buf[len - 2] != ':') { strncat(buf + len, ", ", sizeof(buf) - len - 1); len += strlen(buf + len); } strncat(buf + len, view->dir_entry[i].name, sizeof(buf) - len - 1); len += strlen(buf + len); } i++; } cmd_group_begin(buf); while(j < view->list_rows && !ui_cancellation_requested()) { if(view->dir_entry[j].selected) { char inv[16]; snprintf(inv, sizeof(inv), "0%o", view->dir_entry[j].mode & 0xff); chmod_file_in_list(view, j, mode, inv, recurse_dirs); } j++; } } cmd_group_end(); }
/* changes attributes of files in the view */ static void files_attrib(FileView *view, DWORD add, DWORD sub, int recurse_dirs) { int i; ui_cancellation_reset(); i = 0; while(i < view->list_rows && !view->dir_entry[i].selected) i++; if(i == view->list_rows) { char buf[COMMAND_GROUP_INFO_LEN]; snprintf(buf, sizeof(buf), "chmod in %s: %s", replace_home_part(view->curr_dir), view->dir_entry[view->list_pos].name); cmd_group_begin(buf); attrib_file_in_list(view, view->list_pos, add, sub, recurse_dirs); } else { char buf[COMMAND_GROUP_INFO_LEN]; size_t len; int j = i; len = snprintf(buf, sizeof(buf), "chmod in %s: ", replace_home_part(view->curr_dir)); while(i < view->list_rows && len < sizeof(buf)) { if(view->dir_entry[i].selected) { if(len >= 2 && buf[len - 2] != ':') { strncat(buf + len, ", ", sizeof(buf) - len - 1); len += strlen(buf + len); } strncat(buf + len, view->dir_entry[i].name, sizeof(buf) - len - 1); len += strlen(buf + len); } i++; } cmd_group_begin(buf); while(j < view->list_rows && !ui_cancellation_requested()) { if(view->dir_entry[j].selected) { attrib_file_in_list(view, j, add, sub, recurse_dirs); } j++; } } cmd_group_end(); }
/* Draws box and title of the menu. */ static void draw_menu_frame(const menu_state_t *m) { const size_t title_len = getmaxx(menu_win) - 2*4; const char *const suffix = menu_and_view_are_in_sync(m->d, m->view) ? "" : replace_home_part(m->d->cwd); const char *const at = (suffix[0] == '\0' ? "" : " @ "); char *const title = format_str("%s%s%s", m->d->title, at, suffix); if(utf8_strsw(title) > title_len) { const size_t len = utf8_nstrsnlen(title, title_len - 3); strcpy(title + len, "..."); } box(menu_win, 0, 0); wattron(menu_win, A_BOLD); checked_wmove(menu_win, 0, 3); wprint(menu_win, " "); wprint(menu_win, title); wprint(menu_win, " "); wattroff(menu_win, A_BOLD); free(title); }
/* Removes all files inside given trash directory (even those that this instance * of vifm is not aware of). */ static void empty_trash_dir(const char trash_dir[]) { char *const task_desc = format_str("Empty trash: %s", trash_dir); char *const op_desc = format_str("Emptying %s", replace_home_part(trash_dir)); char *const trash_dir_copy = strdup(trash_dir); if(bg_execute(task_desc, op_desc, BG_UNDEFINED_TOTAL, 1, &empty_trash_in_bg, trash_dir_copy) != 0) { free(trash_dir_copy); } free(op_desc); free(task_desc); }
void files_chmod(FileView *view, const char *mode, int recurse_dirs) { char undo_msg[COMMAND_GROUP_INFO_LEN]; dir_entry_t *entry; size_t len; snprintf(undo_msg, sizeof(undo_msg), "chmod in %s: ", replace_home_part(flist_get_dir(view))); len = strlen(undo_msg); ui_cancellation_reset(); entry = NULL; while(iter_selection_or_current(view, &entry) && !ui_cancellation_requested()) { if(len >= 2U && undo_msg[len - 2U] != ':') { strncat(undo_msg + len, ", ", sizeof(undo_msg) - len - 1); len += strlen(undo_msg + len); } strncat(undo_msg + len, entry->name, sizeof(undo_msg) - len - 1); len += strlen(undo_msg + len); } cmd_group_begin(undo_msg); entry = NULL; while(iter_selection_or_current(view, &entry) && !ui_cancellation_requested()) { char inv[16]; snprintf(inv, sizeof(inv), "0%o", entry->mode & 0xff); chmod_file_in_list(view, entry_to_pos(view, entry), mode, inv, recurse_dirs); } cmd_group_end(); }
/* Changes attributes of files in the view. */ static void files_attrib(FileView *view, DWORD add, DWORD sub, int recurse_dirs) { char undo_msg[COMMAND_GROUP_INFO_LEN]; dir_entry_t *entry; size_t len; snprintf(undo_msg, sizeof(undo_msg), "chmod in %s: ", replace_home_part(flist_get_dir(view))); len = strlen(undo_msg); ui_cancellation_reset(); entry = NULL; while(iter_selection_or_current(view, &entry) && !ui_cancellation_requested()) { if(len >= 2U && undo_msg[len - 2U] != ':') { strncat(undo_msg + len, ", ", sizeof(undo_msg) - len - 1); len += strlen(undo_msg + len); } strncat(undo_msg + len, entry->name, sizeof(undo_msg) - len - 1); len += strlen(undo_msg + len); } cmd_group_begin(undo_msg); entry = NULL; while(iter_selection_or_current(view, &entry) && !ui_cancellation_requested()) { attrib_file_in_list(view, entry_to_pos(view, entry), add, sub, recurse_dirs); } cmd_group_end(); }
/* Returns negative value in case of error */ static int execute_command(FileView *view, const char command[], int menu) { int id; int result; if(command == NULL) { flist_sel_stash_if_nonempty(view); return 0; } command = skip_to_cmd_name(command); if(command[0] == '"') return 0; if(command[0] == '\0' && !menu) { flist_sel_stash_if_nonempty(view); return 0; } if(!menu) { init_cmds(1, &cmds_conf); cmds_conf.begin = 0; cmds_conf.current = view->list_pos; cmds_conf.end = view->list_rows - 1; } id = get_cmd_id(command); if(!cmd_should_be_processed(id)) { return 0; } if(id == USER_CMD_ID) { char undo_msg[COMMAND_GROUP_INFO_LEN]; snprintf(undo_msg, sizeof(undo_msg), "in %s: %s", replace_home_part(flist_get_dir(view)), command); cmd_group_begin(undo_msg); cmd_group_end(); } keep_view_selection = 0; result = execute_cmd(command); if(result >= 0) return result; switch(result) { case CMDS_ERR_LOOP: status_bar_error("Loop in commands"); break; case CMDS_ERR_NO_MEM: status_bar_error("Unable to allocate enough memory"); break; case CMDS_ERR_TOO_FEW_ARGS: status_bar_error("Too few arguments"); break; case CMDS_ERR_TRAILING_CHARS: status_bar_error("Trailing characters"); break; case CMDS_ERR_INCORRECT_NAME: status_bar_error("Incorrect command name"); break; case CMDS_ERR_NEED_BANG: status_bar_error("Add bang to force"); break; case CMDS_ERR_NO_BUILTIN_REDEFINE: status_bar_error("Can't redefine builtin command"); break; case CMDS_ERR_INVALID_CMD: status_bar_error("Invalid command name"); break; case CMDS_ERR_NO_BANG_ALLOWED: status_bar_error("No ! is allowed"); break; case CMDS_ERR_NO_RANGE_ALLOWED: status_bar_error("No range is allowed"); break; case CMDS_ERR_NO_QMARK_ALLOWED: status_bar_error("No ? is allowed"); break; case CMDS_ERR_INVALID_RANGE: /* message dialog is enough */ break; case CMDS_ERR_NO_SUCH_UDF: status_bar_error("No such user defined command"); break; case CMDS_ERR_UDF_IS_AMBIGUOUS: status_bar_error("Ambiguous use of user-defined command"); break; case CMDS_ERR_ZERO_COUNT: status_bar_error("Zero count"); break; case CMDS_ERR_INVALID_ARG: status_bar_error("Invalid argument"); break; case CMDS_ERR_CUSTOM: /* error message is posted by command handler */ break; default: status_bar_error("Unknown error"); break; } if(!menu && vle_mode_is(NORMAL_MODE)) { flist_sel_stash_if_nonempty(view); } return -1; }
/* Expands macros in the *format string advancing the pointer as it goes. The * opt represents conditional expression state, should be zero for non-recursive * calls. Returns newly allocated string, which should be freed by the * caller. */ static LineWithAttrs parse_view_macros(view_t *view, const char **format, const char macros[], int opt) { const dir_entry_t *const curr = get_current_entry(view); LineWithAttrs result = { .line = strdup(""), .attrs = strdup("") }; char c; int nexpansions = 0; int has_expander = 0; if(curr == NULL) { return result; } while((c = **format) != '\0') { size_t width = 0; int left_align = 0; char buf[PATH_MAX + 1]; const char *const next = ++*format; int skip, ok; if(c != '%' || (!char_is_one_of(macros, *next) && !isdigit(*next) && (*next != '=' || has_expander))) { if(strappendch(&result.line, &result.line_len, c) != 0) { break; } continue; } if(*next == '=') { (void)sync_attrs(&result, 0); if(strappend(&result.line, &result.line_len, "%=") != 0 || strappendch(&result.attrs, &result.attrs_len, '=') != 0) { break; } ++*format; has_expander = 1; continue; } if(*next == '-') { left_align = 1; ++*format; } while(isdigit(**format)) { width = width*10 + *(*format)++ - '0'; } c = *(*format)++; skip = 0; ok = 1; buf[0] = '\0'; switch(c) { case 'a': friendly_size_notation(get_free_space(curr_view->curr_dir), sizeof(buf), buf); break; case 't': format_entry_name(curr, NF_FULL, sizeof(buf), buf); break; case 'T': if(curr->type == FT_LINK) { char full_path[PATH_MAX + 1]; char link_path[PATH_MAX + 1]; //add by sim1 get_full_path_of(curr, sizeof(full_path), full_path); //mod by sim1 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< if(get_link_target(full_path, link_path, sizeof(link_path)) != 0) { copy_str(buf, sizeof(buf), "Failed to resolve link"); } else { snprintf(buf, sizeof(buf), " -> %s", link_path); } //mod by sim1 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> } break; case 'f': get_short_path_of(view, curr, NF_FULL, 0, sizeof(buf), buf); break; case 'A': #ifndef _WIN32 get_perm_string(buf, sizeof(buf), curr->mode); #else copy_str(buf, sizeof(buf), attr_str_long(curr->attrs)); #endif break; case 'u': get_uid_string(curr, 0, sizeof(buf), buf); break; case 'g': get_gid_string(curr, 0, sizeof(buf), buf); break; case 's': friendly_size_notation(fentry_get_size(view, curr), sizeof(buf), buf); break; //add by sim1 ************************************************ case 'r': { char path[PATH_MAX] = {0}; get_full_path_at(view, view->list_pos, sizeof(path), path); (void)get_rating_string(buf, sizeof(buf), path); } break; case 'n': { int nitems = !fentry_is_dir(curr) ? 0 : (int)fentry_get_nitems(view, curr); snprintf(buf, sizeof(buf), "%d", nitems); } break; //add by sim1 ************************************************ case 'E': { uint64_t size = 0U; typedef int (*iter_f)(view_t *view, dir_entry_t **entry); /* No current element for visual mode, since it can contain truly * empty selection when cursor is on ../ directory. */ iter_f iter = vle_mode_is(VISUAL_MODE) ? &iter_selected_entries : &iter_selection_or_current; dir_entry_t *entry = NULL; while(iter(view, &entry)) { size += fentry_get_size(view, entry); } friendly_size_notation(size, sizeof(buf), buf); } break; case 'd': { struct tm *tm_ptr = localtime(&curr->mtime); strftime(buf, sizeof(buf), cfg.time_format, tm_ptr); } break; case '-': case 'x': skip = expand_num(buf, sizeof(buf), view->filtered); break; case 'l': skip = expand_num(buf, sizeof(buf), view->list_pos + 1); break; case 'L': skip = expand_num(buf, sizeof(buf), view->list_rows + view->filtered); break; case 'S': skip = expand_num(buf, sizeof(buf), view->list_rows); break; case '%': copy_str(buf, sizeof(buf), "%"); break; case 'z': copy_str(buf, sizeof(buf), get_tip()); break; case 'D': if(curr_stats.number_of_windows == 1) { view_t *const other = (view == curr_view) ? other_view : curr_view; //mod by sim1 //copy_str(buf, sizeof(buf), replace_home_part(other->curr_dir)); snprintf(buf, sizeof(buf), " ‖ %s", replace_home_part(other->curr_dir)); } break; case '[': { LineWithAttrs opt = parse_view_macros(view, format, macros, 1); copy_str(buf, sizeof(buf), opt.line); free(opt.line); char *attrs = opt.attrs; if(sync_attrs(&result, 0) && opt.attrs_len > 0U) { if(*attrs != ' ') { result.attrs[result.attrs_len - 1U] = *attrs; } ++attrs; } strappend(&result.attrs, &result.attrs_len, attrs); free(opt.attrs); break; } case ']': if(opt) { if(nexpansions == 0) { replace_string(&result.line, ""); replace_string(&result.attrs, ""); result.line_len = 0U; result.attrs_len = 0U; } if(sync_attrs(&result, 0)) { result.attrs[--result.attrs_len] = '\0'; } return result; } LOG_INFO_MSG("Unmatched %%]"); ok = 0; break; case '{': { /* Try to find matching closing bracket * TODO: implement the way to escape it, so that the expr may contain * closing brackets */ const char *e = strchr(*format, '}'); char *expr = NULL, *resstr = NULL; var_t res = var_false(); ParsingErrors parsing_error; /* If there's no matching closing bracket, just add the opening one * literally */ if(e == NULL) { ok = 0; break; } /* Create a NULL-terminated copy of the given expr. * TODO: we could temporarily use buf for that, to avoid extra * allocation, but explicitly named variable reads better. */ expr = calloc(e - (*format) + 1 /* NUL-term */, 1); memcpy(expr, *format, e - (*format)); /* Try to parse expr, and convert the res to string if succeed. */ parsing_error = parse(expr, 0, &res); if(parsing_error == PE_NO_ERROR) { resstr = var_to_str(res); } if(resstr != NULL) { copy_str(buf, sizeof(buf), resstr); } else { copy_str(buf, sizeof(buf), "<Invalid expr>"); } var_free(res); free(resstr); free(expr); *format = e + 1 /* closing bracket */; } break; case '*': if(width > 9) { snprintf(buf, sizeof(buf), "%%%d*", (int)width); width = 0; break; } (void)sync_attrs(&result, 1); result.attrs[result.attrs_len - 1] = '0' + width; width = 0; break; default: LOG_INFO_MSG("Unexpected %%-sequence: %%%c", c); ok = 0; break; } if(char_is_one_of("tTAugsEd", c) && fentry_is_fake(curr)) { buf[0] = '\0'; } if(!ok) { *format = next; if(strappendch(&result.line, &result.line_len, '%') != 0) { break; } continue; } check_expanded_str(buf, skip, &nexpansions); stralign(buf, width, ' ', left_align); if(strappend(&result.line, &result.line_len, buf) != 0) { break; } } /* Unmatched %[. */ if(opt) { (void)strprepend(&result.line, &result.line_len, "%["); } if(sync_attrs(&result, 0)) { result.attrs[--result.attrs_len] = '\0'; } return result; } /* Makes sure that result->attrs has at least as many elements as result->line * contains characters + extra_width. Returns non-zero if result->attrs has * extra characters compared to result->line. */ static int sync_attrs(LineWithAttrs *result, int extra_width) { const size_t nchars = utf8_strsw(result->line) + extra_width; if(result->attrs_len < nchars) { char *const new_attrs = format_str("%s%*s", result->attrs, (int)(nchars - result->attrs_len), ""); free(result->attrs); result->attrs = new_attrs; result->attrs_len = nchars; } return (result->attrs_len > nchars); } /* Prints number into the buffer. Returns non-zero if numeric value is * "empty" (zero). */ static int expand_num(char buf[], size_t buf_len, int val) { snprintf(buf, buf_len, "%d", val); return (val == 0); }