static int compare_file_names(const char *s, const char *t, int ignore_case) { char s_buf[NAME_MAX]; char t_buf[NAME_MAX]; snprintf(s_buf, sizeof(s_buf), "%s", s); chosp(s_buf); s = s_buf; snprintf(t_buf, sizeof(t_buf), "%s", t); chosp(t_buf); t = t_buf; if(ignore_case) { strtolower(s_buf); strtolower(t_buf); } if(!cfg.sort_numbers) return strcmp(s, t); else #if defined(_WIN32) || defined(__APPLE__) return vercmp(s, t); #else return strverscmp(s, t); #endif }
const char * make_rel_path(const char path[], const char base[]) { static char buf[PATH_MAX]; const char *p = path, *b = base; int i; int nslashes; #ifdef _WIN32 if(path[1] == ':' && base[1] == ':' && path[0] != base[0]) { canonicalize_path(path, buf, sizeof(buf)); return buf; } #endif while(p[0] != '\0' && p[1] != '\0' && b[0] != '\0' && b[1] != '\0') { const char *op = p, *ob = b; if((p = strchr(p + 1, '/')) == NULL) p = path + strlen(path); if((b = strchr(b + 1, '/')) == NULL) b = base + strlen(base); if(p - path != b - base || strnoscmp(path, base, p - path) != 0) { p = op; b = ob; break; } } canonicalize_path(b, buf, sizeof(buf)); chosp(buf); nslashes = 0; for(i = 0; buf[i] != '\0'; i++) if(buf[i] == '/') nslashes++; buf[0] = '\0'; while(nslashes-- > 0) strcat(buf, "../"); if(*p == '/') p++; canonicalize_path(p, buf + strlen(buf), sizeof(buf) - strlen(buf)); chosp(buf); if(buf[0] == '\0') strcpy(buf, "."); return buf; }
int to_canonic_path(const char path[], char buf[], size_t buf_len) { if(!is_path_absolute(path)) { char cwd[PATH_MAX]; char full_path[PATH_MAX]; if(getcwd(cwd, sizeof(cwd)) == NULL) { /* getcwd() failed, we can't use relative path, so fail. */ LOG_SERROR_MSG(errno, "Can't get CWD"); return 1; } snprintf(full_path, sizeof(full_path), "%s/%s", cwd, path); canonicalize_path(full_path, buf, buf_len); } else { canonicalize_path(path, buf, buf_len); } chosp(buf); return 0; }
/* Checks whether path/file exists. If path is NULL, filename is assumed to * contain full path. */ static int path_exists_internal(const char path[], const char filename[], int deref) { char full[PATH_MAX]; if(path == NULL) { copy_str(full, sizeof(full), filename); } else { snprintf(full, sizeof(full), "%s/%s", path, filename); } /* At least on Windows extra trailing slash can mess up the check, so get rid * of it. */ if(!is_root_dir(full)) { chosp(full); } if(!deref) { struct stat st; return os_lstat(full, &st) == 0; } return os_access(full, F_OK) == 0; }
/* buf should be at least PATH_MAX characters length */ static void parse_path(const char *dir, const char *path, char *buf) { strcpy(buf, path); #ifdef _WIN32 to_forward_slash(buf); #endif if(is_path_absolute(buf)) { snprintf(buf, PATH_MAX, "%s", path); } #ifdef _WIN32 else if(buf[0] == '/') { snprintf(buf, PATH_MAX, "%c:%s", dir[0], path); } #endif else { char new_path[PATH_MAX]; snprintf(new_path, sizeof(new_path), "%s/%s", dir, path); canonicalize_path(new_path, buf, PATH_MAX); } if(!is_root_dir(buf)) chosp(buf); #ifdef _WIN32 to_forward_slash(buf); #endif }
/* Converts a path into canonic form. Mind that canonic paths are usually not * longer than the original one, but can be extended in corner cases so several * extra bytes (e.g. 16) in the buffer are needed. */ static void make_canonic(const char path[], char buf[], size_t buf_size) { canonicalize_path(path, buf, buf_size); if(!is_root_dir(buf) && !ends_with_slash(path)) { chosp(buf); } }
char * replace_home_part(const char path[]) { char *result = replace_home_part_strict(path); if(!is_root_dir(result)) { chosp(result); } return result; }
TEST(trailing_forward_slash_of_path_is_preserved, IF(windows)) { int line_num; char *path; strcat(test_data, "\\"); path = parse_file_spec(test_data, &line_num, "."); chosp(test_data); strcat(test_data, "/"); assert_string_equal(test_data, path); assert_int_equal(DEFAULT_LINENUM, line_num); free(path); chosp(test_data); }
/* Resolve link target and either navigate inside directory link points to or * navigate to directory where target is located pointing cursor on * it (the follow_dirs flag controls behaviour). */ static void follow_link(FileView *view, int follow_dirs) { char *dir, *file; char full_path[PATH_MAX]; char linkto[PATH_MAX + NAME_MAX]; dir_entry_t *const entry = &curr_view->dir_entry[curr_view->list_pos]; get_full_path_of(entry, sizeof(full_path), full_path); if(get_link_target_abs(full_path, entry->origin, linkto, sizeof(linkto)) != 0) { show_error_msg("Error", "Can't read link."); return; } if(!path_exists(linkto, DEREF)) { show_error_msg("Broken Link", "Can't access link destination. It might be broken."); return; } chosp(linkto); if(is_dir(linkto) && !follow_dirs) { dir = strdup(entry->name); file = NULL; } else { dir = strdup(linkto); remove_last_path_component(dir); file = get_last_path_component(linkto); } if(dir[0] != '\0') { navigate_to(view, dir); } if(file != NULL) { const int pos = find_file_pos_in_list(view, file); if(pos >= 0) { flist_set_pos(view, pos); } } free(dir); }
/* Registers action handler for a particular combination of event and path * pattern. Event name is case insensitive. Returns zero on successful * registration or non-zero on error. */ static int add_aucmd(const char event[], const char pattern[], int negated, const char action[], vle_aucmd_handler handler) { char canonic_path[PATH_MAX]; aucmd_info_t *autocmd; char *regexp; autocmd = DA_EXTEND(autocmds); if(autocmd == NULL) { return 1; } if(strchr(pattern, '/') != NULL) { canonicalize_path(pattern, canonic_path, sizeof(canonic_path)); if(!is_root_dir(canonic_path)) { chosp(canonic_path); } pattern = canonic_path; } regexp = glob_to_regex(pattern, 1); if(regexp == NULL) { return 1; } if(regcomp(&autocmd->regex, regexp, REG_EXTENDED | REG_ICASE) != 0) { free(regexp); return 1; } free(regexp); autocmd->event = strdup(event); autocmd->pattern = strdup(pattern); autocmd->negated = negated; autocmd->action = strdup(action); autocmd->handler = handler; if(autocmd->event == NULL || autocmd->pattern == NULL || autocmd->action == NULL) { free_autocmd_data(autocmd); return 1; } DA_COMMIT(autocmds); /* TODO: sort by event name (case insensitive) and then by pattern? */ return 0; }
static void chmod_file_in_list(FileView *view, int pos, const char *mode, const char *inv_mode, int recurse_dirs) { char *filename; char path_buf[PATH_MAX]; filename = view->dir_entry[pos].name; snprintf(path_buf, sizeof(path_buf), "%s/%s", view->curr_dir, filename); chosp(path_buf); file_chmod(path_buf, mode, inv_mode, recurse_dirs); }
/* Implementation of :p filename modifier. */ static int apply_p_mod(const char *path, const char *parent, char *buf, size_t buf_len) { size_t len; if(is_path_absolute(path)) { snprintf(buf, buf_len, "%s", path); return 0; } snprintf(buf, buf_len, "%s", parent); chosp(buf); len = strlen(buf); snprintf(buf + len, buf_len - len, "/%s", path); return 0; }
void remove_last_path_component(char *path) { char *slash; while(ends_with_slash(path)) { chosp(path); } if((slash = strrchr(path, '/')) != NULL) { int pos = is_root_dir(path) ? 1 : 0; slash[pos] = '\0'; } }
/* Tries to expands tilde in the front of the path. Returns the path or newly * allocated string without tilde. */ static char * try_replace_tilde(const char path[]) { #ifndef _WIN32 char name[NAME_MAX]; const char *p; char *result; struct passwd *pw; #endif if(path[0] != '~') { return (char *)path; } if(path[1] == '\0' || path[1] == '/') { char *const result = format_str("%s%s", cfg.home_dir, (path[1] == '/') ? (path + 2) : ""); return result; } #ifndef _WIN32 if((p = strchr(path, '/')) == NULL) { p = path + strlen(path); copy_str(name, sizeof(name), path + 1); } else { snprintf(name, p - (path + 1) + 1, "%s", path + 1); p++; } if((pw = getpwnam(name)) == NULL) { return (char *)path; } chosp(pw->pw_dir); result = format_str("%s/%s", pw->pw_dir, p); return result; #else return (char *)path; #endif }
void remove_last_path_component(char path[]) { char *slash; while(ends_with_slash(path)) { chosp(path); } slash = strrchr(path, '/'); if(slash != NULL) { const int offset = is_root_dir(path) ? 1 : 0; slash[offset] = '\0'; } }
SymLinkType get_symlink_type(const char path[]) { char cwd[PATH_MAX]; char linkto[PATH_MAX + NAME_MAX]; int saved_errno; char *filename_copy; char *p; if(get_cwd(cwd, sizeof(cwd)) == NULL) { /* getcwd() failed, just use "." rather than fail. */ strcpy(cwd, "."); } /* Use readlink() (in get_link_target_abs) before realpath() to check for * target at slow file system. realpath() doesn't fit in this case as it * resolves chains of symbolic links and we want to try only the first one. */ if(get_link_target_abs(path, cwd, linkto, sizeof(linkto)) != 0) { LOG_SERROR_MSG(errno, "Can't readlink \"%s\"", path); log_cwd(); return SLT_UNKNOWN; } if(refers_to_slower_fs(path, linkto)) { return SLT_SLOW; } filename_copy = strdup(path); chosp(filename_copy); p = os_realpath(filename_copy, linkto); saved_errno = errno; free(filename_copy); if(p == linkto) { return is_dir(linkto) ? SLT_DIR : SLT_UNKNOWN; } LOG_SERROR_MSG(saved_errno, "Can't realpath \"%s\"", path); log_cwd(); return SLT_UNKNOWN; }
int is_symlink(const char path[]) { #ifndef _WIN32 struct stat st; return os_lstat(path, &st) == 0 && S_ISLNK(st.st_mode); #else char filename[PATH_MAX]; DWORD attr; wchar_t *utf16_filename; HANDLE hfind; WIN32_FIND_DATAW ffd; attr = win_get_file_attrs(path); if(attr == INVALID_FILE_ATTRIBUTES) { LOG_WERROR(GetLastError()); return 0; } if(!(attr & FILE_ATTRIBUTE_REPARSE_POINT)) { return 0; } copy_str(filename, sizeof(filename), path); chosp(filename); utf16_filename = utf8_to_utf16(path); hfind = FindFirstFileW(utf16_filename, &ffd); free(utf16_filename); if(hfind == INVALID_HANDLE_VALUE) { LOG_WERROR(GetLastError()); return 0; } if(!FindClose(hfind)) { LOG_WERROR(GetLastError()); } return ffd.dwReserved0 == IO_REPARSE_TAG_SYMLINK; #endif }
/* Sets values of the mark. The force parameter controls whether mark is * updated even when it already points to the specified directory-file pair. */ static void set_mark(const char m, const char directory[], const char file[], time_t timestamp, int force) { mark_t *const mark = get_mark_by_name(m); if(mark != NULL && (force || !is_mark_points_to(mark, directory, file))) { reset_mark(mark); mark->directory = strdup(directory); mark->file = strdup(file); mark->timestamp = timestamp; /* Remove any trailing slashes, they might be convenient in configuration * file (hence they are permitted), but shouldn't be stored internally. */ chosp(mark->file); } }
void vle_aucmd_execute(const char event[], const char path[], void *arg) { size_t i; char canonic_path[PATH_MAX]; canonicalize_path(path, canonic_path, sizeof(canonic_path)); if(!is_root_dir(canonic_path)) { chosp(canonic_path); } for(i = 0U; i < DA_SIZE(autocmds); ++i) { if(strcasecmp(event, autocmds[i].event) == 0 && is_pattern_match(&autocmds[i], canonic_path)) { autocmds[i].handler(autocmds[i].action, arg); } } }
int ensure_file_is_selected(FileView *view, const char name[]) { int file_pos; char nm[NAME_MAX]; /* Don't reset filters to find "file with empty name". */ if(name[0] == '\0') { return 0; } /* This is for compatibility with paths loaded from vifminfo that have * trailing slash. */ copy_str(nm, sizeof(nm), name); chosp(nm); file_pos = find_file_pos_in_list(view, nm); if(file_pos < 0 && file_can_be_displayed(view->curr_dir, nm)) { if(nm[0] == '.') { set_dot_files_visible(view, 1); file_pos = find_file_pos_in_list(view, nm); } if(file_pos < 0) { remove_filename_filter(view); /* remove_filename_filter() postpones list of files reloading. */ populate_dir_list(view, 1); file_pos = find_file_pos_in_list(view, nm); } } flist_set_pos(view, (file_pos < 0) ? 0 : file_pos); return file_pos >= 0; }
char * replace_home_part(const char directory[]) { static char buf[PATH_MAX]; size_t len; len = strlen(cfg.home_dir) - 1; if(strnoscmp(directory, cfg.home_dir, len) == 0 && (directory[len] == '\0' || directory[len] == '/')) { strncat(strcpy(buf, "~"), directory + len, sizeof(buf) - strlen(buf) - 1); } else { copy_str(buf, sizeof(buf), directory); } if(!is_root_dir(buf)) chosp(buf); return buf; }
void make_abs_path(char buf[], size_t buf_len, const char base[], const char sub[], const char cwd[]) { char local_buf[buf_len]; if(is_path_absolute(base)) { snprintf(local_buf, buf_len, "%s%s%s", base, (sub[0] == '\0' ? "" : "/"), sub); } else { snprintf(local_buf, buf_len, "%s/%s%s%s", cwd, base, (sub[0] == '\0' ? "" : "/"), sub); } canonicalize_path(local_buf, buf, buf_len); if(!ends_with_slash(sub) && !is_root_dir(buf)) { chosp(buf); } }
/* * type: CT_* */ void filename_completion(const char *str, CompletionType type) { /* TODO refactor filename_completion(...) function */ DIR * dir; char * dirname; char * filename; char * temp; if(str[0] == '~' && strchr(str, '/') == NULL) { char *const tilde_expanded = expand_tilde(str); vle_compl_add_path_match(tilde_expanded); free(tilde_expanded); return; } dirname = expand_tilde(str); filename = strdup(dirname); temp = cmds_expand_envvars(dirname); free(dirname); dirname = temp; temp = strrchr(dirname, '/'); if(temp != NULL && type != CT_FILE && type != CT_FILE_WOE) { strcpy(filename, ++temp); *temp = '\0'; } else { dirname = realloc(dirname, 2); strcpy(dirname, "."); } #ifdef _WIN32 if(is_unc_root(dirname) || (stroscmp(dirname, ".") == 0 && is_unc_root(curr_view->curr_dir)) || (stroscmp(dirname, "/") == 0 && is_unc_path(curr_view->curr_dir))) { char buf[PATH_MAX]; if(!is_unc_root(dirname)) snprintf(buf, strchr(curr_view->curr_dir + 2, '/') - curr_view->curr_dir + 1, "%s", curr_view->curr_dir); else snprintf(buf, sizeof(buf), "%s", dirname); complete_with_shared(buf, filename); free(filename); free(dirname); return; } if(is_unc_path(curr_view->curr_dir)) { char buf[PATH_MAX]; if(is_path_absolute(dirname) && !is_unc_root(curr_view->curr_dir)) snprintf(buf, strchr(curr_view->curr_dir + 2, '/') - curr_view->curr_dir + 2, "%s", curr_view->curr_dir); else snprintf(buf, sizeof(buf), "%s", curr_view->curr_dir); strcat(buf, dirname); chosp(buf); (void)replace_string(&dirname, buf); } #endif dir = opendir(dirname); if(dir == NULL || vifm_chdir(dirname) != 0) { vle_compl_add_path_match(filename); } else { filename_completion_internal(dir, dirname, filename, type); (void)vifm_chdir(curr_view->curr_dir); } free(filename); free(dirname); if(dir != NULL) { closedir(dir); } }
int get_link_target(const char *link, char *buf, size_t buf_len) { LOG_FUNC_ENTER; #ifndef _WIN32 char *filename; ssize_t len; if(buf_len == 0) { return -1; } filename = strdup(link); chosp(filename); len = readlink(filename, buf, buf_len - 1); free(filename); if(len == -1) { return -1; } buf[len] = '\0'; return 0; #else char filename[PATH_MAX]; DWORD attr; wchar_t *utf16_filename; HANDLE hfile; char rdb[2048]; char *t; REPARSE_DATA_BUFFER *sbuf; WCHAR *path; if(!is_symlink(link)) { return -1; } copy_str(filename, sizeof(filename), link); chosp(filename); utf16_filename = utf8_to_utf16(filename); hfile = CreateFileW(utf16_filename, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); free(utf16_filename); if(hfile == INVALID_HANDLE_VALUE) { LOG_WERROR(GetLastError()); return -1; } if(!DeviceIoControl(hfile, FSCTL_GET_REPARSE_POINT, NULL, 0, rdb, sizeof(rdb), &attr, NULL)) { LOG_WERROR(GetLastError()); CloseHandle(hfile); return -1; } CloseHandle(hfile); sbuf = (REPARSE_DATA_BUFFER *)rdb; path = sbuf->SymbolicLinkReparseBuffer.PathBuffer; path[sbuf->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR) + sbuf->SymbolicLinkReparseBuffer.PrintNameLength/sizeof(WCHAR)] = L'\0'; t = to_multibyte(path + sbuf->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR)); if(strncmp(t, "\\??\\", 4) == 0) strncpy(buf, t + 4, buf_len); else strncpy(buf, t, buf_len); buf[buf_len - 1] = '\0'; free(t); to_forward_slash(buf); return 0; #endif }