/* * maintains the list of already visited files and dirs * returns FAIL if the given file/dir is already in the list * returns OK if it is newly added */ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *wc_path) { ff_visited_T *vp; bool url = false; FileInfo file_info; // For a URL we only compare the name, otherwise we compare the // device/inode. if (path_with_url(fname)) { STRLCPY(ff_expand_buffer, fname, MAXPATHL); url = true; } else { ff_expand_buffer[0] = NUL; if (!os_get_file_info((char *)fname, &file_info)) { return FAIL; } } /* check against list of already visited files */ for (vp = *visited_list; vp != NULL; vp = vp->ffv_next) { if ((url && fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0) || (!url && vp->ffv_dev_valid && vp->ffv_dev == file_info.stat.st_dev && vp->ffv_ino == file_info.stat.st_ino)) { /* are the wildcard parts equal */ if (ff_wc_equal(vp->ffv_wc_path, wc_path) == TRUE) /* already visited */ return FAIL; } } /* * New file/dir. Add it to the list of visited files/dirs. */ vp = xmalloc(sizeof(ff_visited_T) + STRLEN(ff_expand_buffer)); if (!url) { vp->ffv_dev_valid = TRUE; vp->ffv_ino = file_info.stat.st_ino; vp->ffv_dev = file_info.stat.st_dev; vp->ffv_fname[0] = NUL; } else { vp->ffv_dev_valid = FALSE; STRCPY(vp->ffv_fname, ff_expand_buffer); } if (wc_path != NULL) vp->ffv_wc_path = vim_strsave(wc_path); else vp->ffv_wc_path = NULL; vp->ffv_next = *visited_list; *visited_list = vp; return OK; }
/* * maintains the list of already visited files and dirs * returns FAIL if the given file/dir is already in the list * returns OK if it is newly added */ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *wc_path) { ff_visited_T *vp; bool url = false; FileID file_id; // For an URL we only compare the name, otherwise we compare the // device/inode. if (path_with_url((char *)fname)) { STRLCPY(ff_expand_buffer, fname, MAXPATHL); url = true; } else { ff_expand_buffer[0] = NUL; if (!os_fileid((char *)fname, &file_id)) { return FAIL; } } /* check against list of already visited files */ for (vp = *visited_list; vp != NULL; vp = vp->ffv_next) { if ((url && fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0) || (!url && vp->file_id_valid && os_fileid_equal(&(vp->file_id), &file_id))) { // are the wildcard parts equal if (ff_wc_equal(vp->ffv_wc_path, wc_path)) { // already visited return FAIL; } } } /* * New file/dir. Add it to the list of visited files/dirs. */ vp = xmalloc(sizeof(ff_visited_T) + STRLEN(ff_expand_buffer)); if (!url) { vp->file_id_valid = true; vp->file_id = file_id; vp->ffv_fname[0] = NUL; } else { vp->file_id_valid = false; STRCPY(vp->ffv_fname, ff_expand_buffer); } if (wc_path != NULL) vp->ffv_wc_path = vim_strsave(wc_path); else vp->ffv_wc_path = NULL; vp->ffv_next = *visited_list; *visited_list = vp; return OK; }
/* * 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. */ file_path = xmalloc(MAXPATHL); /* 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 **)xmalloc(sizeof(char *)); stackp->ffs_filearray[0] = vim_strsave(dirptrs[0]); stackp->ffs_filearray_size = 1; } 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]) && !os_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) == os_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 (os_dirname(ff_expand_buffer, MAXPATHL) == OK) { p = path_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 (!os_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 (!os_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); ff_push(search_ctx, sptr); } else break; } free(file_path); return NULL; }
/* * maintains the list of already visited files and dirs * returns FAIL if the given file/dir is already in the list * returns OK if it is newly added * * TODO: What to do on memory allocation problems? * -> return TRUE - Better the file is found several times instead of * never. */ static int ff_check_visited(ff_visited_T **visited_list, char_u *fname, char_u *wc_path) { ff_visited_T *vp; #ifdef UNIX struct stat st; int url = FALSE; #endif /* For an URL we only compare the name, otherwise we compare the * device/inode (unix) or the full path name (not Unix). */ if (path_with_url(fname)) { vim_strncpy(ff_expand_buffer, fname, MAXPATHL - 1); #ifdef UNIX url = TRUE; #endif } else { ff_expand_buffer[0] = NUL; #ifdef UNIX if (mch_stat((char *)fname, &st) < 0) #else if (vim_FullName(fname, ff_expand_buffer, MAXPATHL, TRUE) == FAIL) #endif return FAIL; } /* check against list of already visited files */ for (vp = *visited_list; vp != NULL; vp = vp->ffv_next) { if ( #ifdef UNIX !url ? (vp->ffv_dev_valid && vp->ffv_dev == st.st_dev && vp->ffv_ino == st.st_ino) : #endif fnamecmp(vp->ffv_fname, ff_expand_buffer) == 0 ) { /* are the wildcard parts equal */ if (ff_wc_equal(vp->ffv_wc_path, wc_path) == TRUE) /* already visited */ return FAIL; } } /* * New file/dir. Add it to the list of visited files/dirs. */ vp = (ff_visited_T *)alloc((unsigned)(sizeof(ff_visited_T) + STRLEN(ff_expand_buffer))); if (vp != NULL) { #ifdef UNIX if (!url) { vp->ffv_dev_valid = TRUE; vp->ffv_ino = st.st_ino; vp->ffv_dev = st.st_dev; vp->ffv_fname[0] = NUL; } else { vp->ffv_dev_valid = FALSE; #endif STRCPY(vp->ffv_fname, ff_expand_buffer); #ifdef UNIX } #endif if (wc_path != NULL) vp->ffv_wc_path = vim_strsave(wc_path); else vp->ffv_wc_path = NULL; vp->ffv_next = *visited_list; *visited_list = vp; } return OK; }