/* * For an xtended filemark: set the fnum from the fname. * This is used for marks obtained from the .viminfo file. It's postponed * until the mark is used to avoid a long startup delay. */ static void fname2fnum(xfmark_T *fm) { char_u *p; if (fm->fname != NULL) { /* * First expand "~/" in the file name to the home directory. * Don't expand the whole name, it may contain other '~' chars. */ if (fm->fname[0] == '~' && (fm->fname[1] == '/' #ifdef BACKSLASH_IN_FILENAME || fm->fname[1] == '\\' #endif )) { int len; expand_env((char_u *)"~/", NameBuff, MAXPATHL); len = (int)STRLEN(NameBuff); vim_strncpy(NameBuff + len, fm->fname + 2, MAXPATHL - len - 1); } else vim_strncpy(NameBuff, fm->fname, MAXPATHL - 1); /* Try to shorten the file name. */ mch_dirname(IObuff, IOSIZE); p = shorten_fname(NameBuff, IObuff); /* buflist_new() will call fmarks_check_names() */ (void)buflist_new(NameBuff, p, (linenr_T)1, 0); } }
/* * Get the absolute name of the given relative directory. * * parameter directory: Directory name, relative to current directory. * return FAIL for failure, OK for success */ int mch_full_dir_name(char *directory, char *buffer, int len) { int retval = OK; if(STRLEN(directory) == 0) { return mch_dirname((char_u *) buffer, len); } char old_dir[MAXPATHL]; /* Get current directory name. */ if (mch_dirname((char_u *) old_dir, MAXPATHL) == FAIL) { return FAIL; } /* We have to get back to the current dir at the end, check if that works. */ if (mch_chdir(old_dir) != 0) { return FAIL; } if (mch_chdir(directory) != 0) { /* Do not return immediatly since we may be in the wrong directory. */ retval = FAIL; } if (retval == FAIL || mch_dirname((char_u *) buffer, len) == FAIL) { /* Do not return immediatly since we are in the wrong directory. */ retval = FAIL; } if (mch_chdir(old_dir) != 0) { /* That shouldn't happen, since we've tested if it works. */ retval = FAIL; EMSG(_(e_prev_dir)); } return retval; }
/* * Find a file in a search context. * The search context was created with vim_findfile_init() above. * Return a pointer to an allocated file name or NULL if nothing found. * To get all matching files call this function until you get NULL. * * If the passed search_context is NULL, NULL is returned. * * The search algorithm is depth first. To change this replace the * stack with a list (don't forget to leave partly searched directories on the * top of the list). */ char_u *vim_findfile(void *search_ctx_arg) { char_u *file_path; char_u *rest_of_wildcards; char_u *path_end = NULL; ff_stack_T *stackp; int len; int i; char_u *p; char_u *suf; ff_search_ctx_T *search_ctx; if (search_ctx_arg == NULL) return NULL; search_ctx = (ff_search_ctx_T *)search_ctx_arg; /* * filepath is used as buffer for various actions and as the storage to * return a found filename. */ if ((file_path = alloc((int)MAXPATHL)) == NULL) return NULL; /* store the end of the start dir -- needed for upward search */ if (search_ctx->ffsc_start_dir != NULL) path_end = &search_ctx->ffsc_start_dir[ STRLEN(search_ctx->ffsc_start_dir)]; /* upward search loop */ for (;; ) { /* downward search loop */ for (;; ) { /* check if user user wants to stop the search*/ ui_breakcheck(); if (got_int) break; /* get directory to work on from stack */ stackp = ff_pop(search_ctx); if (stackp == NULL) break; /* * TODO: decide if we leave this test in * * GOOD: don't search a directory(-tree) twice. * BAD: - check linked list for every new directory entered. * - check for double files also done below * * Here we check if we already searched this directory. * We already searched a directory if: * 1) The directory is the same. * 2) We would use the same wildcard string. * * Good if you have links on same directory via several ways * or you have selfreferences in directories (e.g. SuSE Linux 6.3: * /etc/rc.d/init.d is linked to /etc/rc.d -> endless loop) * * This check is only needed for directories we work on for the * first time (hence stackp->ff_filearray == NULL) */ if (stackp->ffs_filearray == NULL && ff_check_visited(&search_ctx->ffsc_dir_visited_list ->ffvl_visited_list, stackp->ffs_fix_path , stackp->ffs_wc_path ) == FAIL) { #ifdef FF_VERBOSE if (p_verbose >= 5) { verbose_enter_scroll(); smsg((char_u *)"Already Searched: %s (%s)", stackp->ffs_fix_path, stackp->ffs_wc_path); /* don't overwrite this either */ msg_puts((char_u *)"\n"); verbose_leave_scroll(); } #endif ff_free_stack_element(stackp); continue; } #ifdef FF_VERBOSE else if (p_verbose >= 5) { verbose_enter_scroll(); smsg((char_u *)"Searching: %s (%s)", stackp->ffs_fix_path, stackp->ffs_wc_path); /* don't overwrite this either */ msg_puts((char_u *)"\n"); verbose_leave_scroll(); } #endif /* check depth */ if (stackp->ffs_level <= 0) { ff_free_stack_element(stackp); continue; } file_path[0] = NUL; /* * If no filearray till now expand wildcards * The function expand_wildcards() can handle an array of paths * and all possible expands are returned in one array. We use this * to handle the expansion of '**' into an empty string. */ if (stackp->ffs_filearray == NULL) { char_u *dirptrs[2]; /* we use filepath to build the path expand_wildcards() should * expand. */ dirptrs[0] = file_path; dirptrs[1] = NULL; /* if we have a start dir copy it in */ if (!vim_isAbsName(stackp->ffs_fix_path) && search_ctx->ffsc_start_dir) { STRCPY(file_path, search_ctx->ffsc_start_dir); add_pathsep(file_path); } /* append the fix part of the search path */ STRCAT(file_path, stackp->ffs_fix_path); add_pathsep(file_path); rest_of_wildcards = stackp->ffs_wc_path; if (*rest_of_wildcards != NUL) { len = (int)STRLEN(file_path); if (STRNCMP(rest_of_wildcards, "**", 2) == 0) { /* pointer to the restrict byte * The restrict byte is not a character! */ p = rest_of_wildcards + 2; if (*p > 0) { (*p)--; file_path[len++] = '*'; } if (*p == 0) { /* remove '**<numb> from wildcards */ STRMOVE(rest_of_wildcards, rest_of_wildcards + 3); } else rest_of_wildcards += 3; if (stackp->ffs_star_star_empty == 0) { /* if not done before, expand '**' to empty */ stackp->ffs_star_star_empty = 1; dirptrs[1] = stackp->ffs_fix_path; } } /* * Here we copy until the next path separator or the end of * the path. If we stop at a path separator, there is * still something else left. This is handled below by * pushing every directory returned from expand_wildcards() * on the stack again for further search. */ while (*rest_of_wildcards && !vim_ispathsep(*rest_of_wildcards)) file_path[len++] = *rest_of_wildcards++; file_path[len] = NUL; if (vim_ispathsep(*rest_of_wildcards)) rest_of_wildcards++; } /* * Expand wildcards like "*" and "$VAR". * If the path is a URL don't try this. */ if (path_with_url(dirptrs[0])) { stackp->ffs_filearray = (char_u **) alloc((unsigned)sizeof(char *)); if (stackp->ffs_filearray != NULL && (stackp->ffs_filearray[0] = vim_strsave(dirptrs[0])) != NULL) stackp->ffs_filearray_size = 1; else stackp->ffs_filearray_size = 0; } else /* Add EW_NOTWILD because the expanded path may contain * wildcard characters that are to be taken literally. * This is a bit of a hack. */ expand_wildcards((dirptrs[1] == NULL) ? 1 : 2, dirptrs, &stackp->ffs_filearray_size, &stackp->ffs_filearray, EW_DIR|EW_ADDSLASH|EW_SILENT|EW_NOTWILD); stackp->ffs_filearray_cur = 0; stackp->ffs_stage = 0; } else rest_of_wildcards = &stackp->ffs_wc_path[ STRLEN(stackp->ffs_wc_path)]; if (stackp->ffs_stage == 0) { /* this is the first time we work on this directory */ if (*rest_of_wildcards == NUL) { /* * We don't have further wildcards to expand, so we have to * check for the final file now. */ for (i = stackp->ffs_filearray_cur; i < stackp->ffs_filearray_size; ++i) { if (!path_with_url(stackp->ffs_filearray[i]) && !mch_isdir(stackp->ffs_filearray[i])) continue; /* not a directory */ /* prepare the filename to be checked for existence * below */ STRCPY(file_path, stackp->ffs_filearray[i]); add_pathsep(file_path); STRCAT(file_path, search_ctx->ffsc_file_to_search); /* * Try without extra suffix and then with suffixes * from 'suffixesadd'. */ len = (int)STRLEN(file_path); if (search_ctx->ffsc_tagfile) suf = (char_u *)""; else suf = curbuf->b_p_sua; for (;; ) { /* if file exists and we didn't already find it */ if ((path_with_url(file_path) || (os_file_exists(file_path) && (search_ctx->ffsc_find_what == FINDFILE_BOTH || ((search_ctx->ffsc_find_what == FINDFILE_DIR) == mch_isdir(file_path))))) #ifndef FF_VERBOSE && (ff_check_visited( &search_ctx->ffsc_visited_list->ffvl_visited_list, file_path , (char_u *)"" ) == OK) #endif ) { #ifdef FF_VERBOSE if (ff_check_visited( &search_ctx->ffsc_visited_list->ffvl_visited_list, file_path , (char_u *)"" ) == FAIL) { if (p_verbose >= 5) { verbose_enter_scroll(); smsg((char_u *)"Already: %s", file_path); /* don't overwrite this either */ msg_puts((char_u *)"\n"); verbose_leave_scroll(); } continue; } #endif /* push dir to examine rest of subdirs later */ stackp->ffs_filearray_cur = i + 1; ff_push(search_ctx, stackp); if (!path_with_url(file_path)) simplify_filename(file_path); if (mch_dirname(ff_expand_buffer, MAXPATHL) == OK) { p = shorten_fname(file_path, ff_expand_buffer); if (p != NULL) STRMOVE(file_path, p); } #ifdef FF_VERBOSE if (p_verbose >= 5) { verbose_enter_scroll(); smsg((char_u *)"HIT: %s", file_path); /* don't overwrite this either */ msg_puts((char_u *)"\n"); verbose_leave_scroll(); } #endif return file_path; } /* Not found or found already, try next suffix. */ if (*suf == NUL) break; copy_option_part(&suf, file_path + len, MAXPATHL - len, ","); } } } else { /* * still wildcards left, push the directories for further * search */ for (i = stackp->ffs_filearray_cur; i < stackp->ffs_filearray_size; ++i) { if (!mch_isdir(stackp->ffs_filearray[i])) continue; /* not a directory */ ff_push(search_ctx, ff_create_stack_element( stackp->ffs_filearray[i], rest_of_wildcards, stackp->ffs_level - 1, 0)); } } stackp->ffs_filearray_cur = 0; stackp->ffs_stage = 1; } /* * if wildcards contains '**' we have to descent till we reach the * leaves of the directory tree. */ if (STRNCMP(stackp->ffs_wc_path, "**", 2) == 0) { for (i = stackp->ffs_filearray_cur; i < stackp->ffs_filearray_size; ++i) { if (fnamecmp(stackp->ffs_filearray[i], stackp->ffs_fix_path) == 0) continue; /* don't repush same directory */ if (!mch_isdir(stackp->ffs_filearray[i])) continue; /* not a directory */ ff_push(search_ctx, ff_create_stack_element(stackp->ffs_filearray[i], stackp->ffs_wc_path, stackp->ffs_level - 1, 1)); } } /* we are done with the current directory */ ff_free_stack_element(stackp); } /* If we reached this, we didn't find anything downwards. * Let's check if we should do an upward search. */ if (search_ctx->ffsc_start_dir && search_ctx->ffsc_stopdirs_v != NULL && !got_int) { ff_stack_T *sptr; /* is the last starting directory in the stop list? */ if (ff_path_in_stoplist(search_ctx->ffsc_start_dir, (int)(path_end - search_ctx->ffsc_start_dir), search_ctx->ffsc_stopdirs_v) == TRUE) break; /* cut of last dir */ while (path_end > search_ctx->ffsc_start_dir && vim_ispathsep(*path_end)) path_end--; while (path_end > search_ctx->ffsc_start_dir && !vim_ispathsep(path_end[-1])) path_end--; *path_end = 0; path_end--; if (*search_ctx->ffsc_start_dir == 0) break; STRCPY(file_path, search_ctx->ffsc_start_dir); add_pathsep(file_path); STRCAT(file_path, search_ctx->ffsc_fix_path); /* create a new stack entry */ sptr = ff_create_stack_element(file_path, search_ctx->ffsc_wc_path, search_ctx->ffsc_level, 0); if (sptr == NULL) break; ff_push(search_ctx, sptr); } else break; } vim_free(file_path); return NULL; }
/* * Initialization routine for vim_findfile(). * * Returns the newly allocated search context or NULL if an error occurred. * * Don't forget to clean up by calling vim_findfile_cleanup() if you are done * with the search context. * * Find the file 'filename' in the directory 'path'. * The parameter 'path' may contain wildcards. If so only search 'level' * directories deep. The parameter 'level' is the absolute maximum and is * not related to restricts given to the '**' wildcard. If 'level' is 100 * and you use '**200' vim_findfile() will stop after 100 levels. * * 'filename' cannot contain wildcards! It is used as-is, no backslashes to * escape special characters. * * If 'stopdirs' is not NULL and nothing is found downward, the search is * restarted on the next higher directory level. This is repeated until the * start-directory of a search is contained in 'stopdirs'. 'stopdirs' has the * format ";*<dirname>*\(;<dirname>\)*;\=$". * * If the 'path' is relative, the starting dir for the search is either VIM's * current dir or if the path starts with "./" the current files dir. * If the 'path' is absolute, the starting dir is that part of the path before * the first wildcard. * * Upward search is only done on the starting dir. * * If 'free_visited' is TRUE the list of already visited files/directories is * cleared. Set this to FALSE if you just want to search from another * directory, but want to be sure that no directory from a previous search is * searched again. This is useful if you search for a file at different places. * The list of visited files/dirs can also be cleared with the function * vim_findfile_free_visited(). * * Set the parameter 'find_what' to FINDFILE_DIR if you want to search for * directories only, FINDFILE_FILE for files only, FINDFILE_BOTH for both. * * A search context returned by a previous call to vim_findfile_init() can be * passed in the parameter "search_ctx_arg". This context is reused and * reinitialized with the new parameters. The list of already visited * directories from this context is only deleted if the parameter * "free_visited" is true. Be aware that the passed "search_ctx_arg" is freed * if the reinitialization fails. * * If you don't have a search context from a previous call "search_ctx_arg" * must be NULL. * * This function silently ignores a few errors, vim_findfile() will have * limited functionality then. */ void * vim_findfile_init ( char_u *path, char_u *filename, char_u *stopdirs, int level, int free_visited, int find_what, void *search_ctx_arg, int tagfile, /* expanding names of tags files */ char_u *rel_fname /* file name to use for "." */ ) { char_u *wc_part; ff_stack_T *sptr; ff_search_ctx_T *search_ctx; /* If a search context is given by the caller, reuse it, else allocate a * new one. */ if (search_ctx_arg != NULL) search_ctx = search_ctx_arg; else { search_ctx = (ff_search_ctx_T*)alloc((unsigned)sizeof(ff_search_ctx_T)); if (search_ctx == NULL) goto error_return; vim_memset(search_ctx, 0, sizeof(ff_search_ctx_T)); } search_ctx->ffsc_find_what = find_what; search_ctx->ffsc_tagfile = tagfile; /* clear the search context, but NOT the visited lists */ ff_clear(search_ctx); /* clear visited list if wanted */ if (free_visited == TRUE) vim_findfile_free_visited(search_ctx); else { /* Reuse old visited lists. Get the visited list for the given * filename. If no list for the current filename exists, creates a new * one. */ search_ctx->ffsc_visited_list = ff_get_visited_list(filename, &search_ctx->ffsc_visited_lists_list); if (search_ctx->ffsc_visited_list == NULL) goto error_return; search_ctx->ffsc_dir_visited_list = ff_get_visited_list(filename, &search_ctx->ffsc_dir_visited_lists_list); if (search_ctx->ffsc_dir_visited_list == NULL) goto error_return; } if (ff_expand_buffer == NULL) { ff_expand_buffer = (char_u*)alloc(MAXPATHL); if (ff_expand_buffer == NULL) goto error_return; } /* Store information on starting dir now if path is relative. * If path is absolute, we do that later. */ if (path[0] == '.' && (vim_ispathsep(path[1]) || path[1] == NUL) && (!tagfile || vim_strchr(p_cpo, CPO_DOTTAG) == NULL) && rel_fname != NULL) { int len = (int)(gettail(rel_fname) - rel_fname); if (!vim_isAbsName(rel_fname) && len + 1 < MAXPATHL) { /* Make the start dir an absolute path name. */ vim_strncpy(ff_expand_buffer, rel_fname, len); search_ctx->ffsc_start_dir = FullName_save(ff_expand_buffer, FALSE); } else search_ctx->ffsc_start_dir = vim_strnsave(rel_fname, len); if (search_ctx->ffsc_start_dir == NULL) goto error_return; if (*++path != NUL) ++path; } else if (*path == NUL || !vim_isAbsName(path)) { #ifdef BACKSLASH_IN_FILENAME /* "c:dir" needs "c:" to be expanded, otherwise use current dir */ if (*path != NUL && path[1] == ':') { char_u drive[3]; drive[0] = path[0]; drive[1] = ':'; drive[2] = NUL; if (vim_FullName(drive, ff_expand_buffer, MAXPATHL, TRUE) == FAIL) goto error_return; path += 2; } else #endif if (mch_dirname(ff_expand_buffer, MAXPATHL) == FAIL) goto error_return; search_ctx->ffsc_start_dir = vim_strsave(ff_expand_buffer); if (search_ctx->ffsc_start_dir == NULL) goto error_return; #ifdef BACKSLASH_IN_FILENAME /* A path that starts with "/dir" is relative to the drive, not to the * directory (but not for "//machine/dir"). Only use the drive name. */ if ((*path == '/' || *path == '\\') && path[1] != path[0] && search_ctx->ffsc_start_dir[1] == ':') search_ctx->ffsc_start_dir[2] = NUL; #endif } /* * If stopdirs are given, split them into an array of pointers. * If this fails (mem allocation), there is no upward search at all or a * stop directory is not recognized -> continue silently. * If stopdirs just contains a ";" or is empty, * search_ctx->ffsc_stopdirs_v will only contain a NULL pointer. This * is handled as unlimited upward search. See function * ff_path_in_stoplist() for details. */ if (stopdirs != NULL) { char_u *walker = stopdirs; int dircount; while (*walker == ';') walker++; dircount = 1; search_ctx->ffsc_stopdirs_v = (char_u **)alloc((unsigned)sizeof(char_u *)); if (search_ctx->ffsc_stopdirs_v != NULL) { do { char_u *helper; void *ptr; helper = walker; ptr = vim_realloc(search_ctx->ffsc_stopdirs_v, (dircount + 1) * sizeof(char_u *)); if (ptr) search_ctx->ffsc_stopdirs_v = ptr; else /* ignore, keep what we have and continue */ break; walker = vim_strchr(walker, ';'); if (walker) { search_ctx->ffsc_stopdirs_v[dircount-1] = vim_strnsave(helper, (int)(walker - helper)); walker++; } else /* this might be "", which means ascent till top * of directory tree. */ search_ctx->ffsc_stopdirs_v[dircount-1] = vim_strsave(helper); dircount++; } while (walker != NULL); search_ctx->ffsc_stopdirs_v[dircount-1] = NULL; } } search_ctx->ffsc_level = level; /* split into: * -fix path * -wildcard_stuff (might be NULL) */ wc_part = vim_strchr(path, '*'); if (wc_part != NULL) { int llevel; int len; char *errpt; /* save the fix part of the path */ search_ctx->ffsc_fix_path = vim_strnsave(path, (int)(wc_part - path)); /* * copy wc_path and add restricts to the '**' wildcard. * The octet after a '**' is used as a (binary) counter. * So '**3' is transposed to '**^C' ('^C' is ASCII value 3) * or '**76' is transposed to '**N'( 'N' is ASCII value 76). * For EBCDIC you get different character values. * If no restrict is given after '**' the default is used. * Due to this technique the path looks awful if you print it as a * string. */ len = 0; while (*wc_part != NUL) { if (len + 5 >= MAXPATHL) { EMSG(_(e_pathtoolong)); break; } if (STRNCMP(wc_part, "**", 2) == 0) { ff_expand_buffer[len++] = *wc_part++; ff_expand_buffer[len++] = *wc_part++; llevel = strtol((char *)wc_part, &errpt, 10); if ((char_u *)errpt != wc_part && llevel > 0 && llevel < 255) ff_expand_buffer[len++] = llevel; else if ((char_u *)errpt != wc_part && llevel == 0) /* restrict is 0 -> remove already added '**' */ len -= 2; else ff_expand_buffer[len++] = FF_MAX_STAR_STAR_EXPAND; wc_part = (char_u *)errpt; if (*wc_part != NUL && !vim_ispathsep(*wc_part)) { EMSG2(_( "E343: Invalid path: '**[number]' must be at the end of the path or be followed by '%s'."), PATHSEPSTR); goto error_return; } } else ff_expand_buffer[len++] = *wc_part++; } ff_expand_buffer[len] = NUL; search_ctx->ffsc_wc_path = vim_strsave(ff_expand_buffer); if (search_ctx->ffsc_wc_path == NULL) goto error_return; } else search_ctx->ffsc_fix_path = vim_strsave(path); if (search_ctx->ffsc_start_dir == NULL) { /* store the fix part as startdir. * This is needed if the parameter path is fully qualified. */ search_ctx->ffsc_start_dir = vim_strsave(search_ctx->ffsc_fix_path); if (search_ctx->ffsc_start_dir == NULL) goto error_return; search_ctx->ffsc_fix_path[0] = NUL; } /* create an absolute path */ if (STRLEN(search_ctx->ffsc_start_dir) + STRLEN(search_ctx->ffsc_fix_path) + 3 >= MAXPATHL) { EMSG(_(e_pathtoolong)); goto error_return; } STRCPY(ff_expand_buffer, search_ctx->ffsc_start_dir); add_pathsep(ff_expand_buffer); { int eb_len = (int)STRLEN(ff_expand_buffer); char_u *buf = alloc(eb_len + (int)STRLEN(search_ctx->ffsc_fix_path) + 1); STRCPY(buf, ff_expand_buffer); STRCPY(buf + eb_len, search_ctx->ffsc_fix_path); if (mch_isdir(buf)) { STRCAT(ff_expand_buffer, search_ctx->ffsc_fix_path); add_pathsep(ff_expand_buffer); } else { char_u *p = gettail(search_ctx->ffsc_fix_path); char_u *wc_path = NULL; char_u *temp = NULL; int len = 0; if (p > search_ctx->ffsc_fix_path) { len = (int)(p - search_ctx->ffsc_fix_path) - 1; STRNCAT(ff_expand_buffer, search_ctx->ffsc_fix_path, len); add_pathsep(ff_expand_buffer); } else len = (int)STRLEN(search_ctx->ffsc_fix_path); if (search_ctx->ffsc_wc_path != NULL) { wc_path = vim_strsave(search_ctx->ffsc_wc_path); temp = alloc((int)(STRLEN(search_ctx->ffsc_wc_path) + STRLEN(search_ctx->ffsc_fix_path + len) + 1)); } if (temp == NULL || wc_path == NULL) { vim_free(buf); vim_free(temp); vim_free(wc_path); goto error_return; } STRCPY(temp, search_ctx->ffsc_fix_path + len); STRCAT(temp, search_ctx->ffsc_wc_path); vim_free(search_ctx->ffsc_wc_path); vim_free(wc_path); search_ctx->ffsc_wc_path = temp; } vim_free(buf); } sptr = ff_create_stack_element(ff_expand_buffer, search_ctx->ffsc_wc_path, level, 0); if (sptr == NULL) goto error_return; ff_push(search_ctx, sptr); search_ctx->ffsc_file_to_search = vim_strsave(filename); if (search_ctx->ffsc_file_to_search == NULL) goto error_return; return search_ctx; error_return: /* * We clear the search context now! * Even when the caller gave us a (perhaps valid) context we free it here, * as we might have already destroyed it. */ vim_findfile_cleanup(search_ctx); return NULL; }
/* * Get absolute file name into "buf[len]". * * return FAIL for failure, OK for success */ int mch_FullName( char_u *fname, char_u *buf, int len, int force /* also expand when already absolute path */ ) { int l; char_u olddir[MAXPATHL]; char_u *p; int retval = OK; /* expand it if forced or not an absolute path */ if (force || !mch_isFullName(fname)) { /* * If the file name has a path, change to that directory for a moment, * and then do the getwd() (and get back to where we were). * This will get the correct path name with "../" things. */ if ((p = vim_strrchr(fname, '/')) != NULL) { /* Only change directory when we are sure we can return to where * we are now. After doing "su" chdir(".") might not work. */ if ((mch_dirname(olddir, MAXPATHL) == FAIL || mch_chdir((char *)olddir) != 0)) { p = NULL; /* can't get current dir: don't chdir */ retval = FAIL; } else { /* The directory is copied into buf[], to be able to remove * the file name without changing it (could be a string in * read-only memory) */ if (p - fname >= len) retval = FAIL; else { vim_strncpy(buf, fname, p - fname); if (mch_chdir((char *)buf)) retval = FAIL; else fname = p + 1; *buf = NUL; } } } if (mch_dirname(buf, len) == FAIL) { retval = FAIL; *buf = NUL; } if (p != NULL) { l = mch_chdir((char *)olddir); if (l != 0) EMSG(_(e_prev_dir)); } l = STRLEN(buf); if (l >= len - 1) retval = FAIL; /* no space for trailing "/" */ else if (l > 0 && buf[l - 1] != '/' && *fname != NUL && STRCMP(fname, ".") != 0) STRCAT(buf, "/"); } /* Catch file names which are too long. */ if (retval == FAIL || (int)(STRLEN(buf) + STRLEN(fname)) >= len) return FAIL; /* Do not append ".", "/dir/." is equal to "/dir". */ if (STRCMP(fname, ".") != 0) STRCAT(buf, fname); return OK; }