To: vim_dev@googlegroups.com Subject: Patch 8.1.1099 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.1099 Problem: The do_tag() function is too long. Solution: Factor parts out to separate functions. Move simplify_filename() to a file where it fits better. (Andy Massimino, closes #4195) Files: src/tag.c, src/proto/tag.pro, src/findfile.c, src/proto/findfile.pro *** ../vim-8.1.1098/src/tag.c 2019-03-30 21:41:44.218279831 +0100 --- src/tag.c 2019-03-31 19:14:18.988597395 +0200 *************** *** 74,79 **** --- 74,83 ---- static int test_for_current(char_u *, char_u *, char_u *, char_u *); #endif static int find_extra(char_u **pp); + static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_u **matches); + #if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL) + static int add_llist_tags(char_u *tag, int num_matches, char_u **matches); + #endif static char_u *bottommsg = (char_u *)N_("E555: at bottom of tag stack"); static char_u *topmsg = (char_u *)N_("E556: at top of tag stack"); *************** *** 125,149 **** int prevtagstackidx = tagstackidx; int prev_num_matches; int new_tag = FALSE; ! int other_name; ! int i, j, k; ! int idx; int ic; - char_u *p; - char_u *name; int no_regexp = FALSE; int error_cur_match = 0; - char_u *command_end; int save_pos = FALSE; fmark_T saved_fmark; - int taglen; #ifdef FEAT_CSCOPE int jumped_to_tag = FALSE; #endif - tagptrs_T tagp, tagp2; int new_num_matches; char_u **new_matches; - int attr; int use_tagstack; int skip_msg = FALSE; char_u *buf_ffname = curbuf->b_ffname; /* name to use for --- 129,145 ---- int prevtagstackidx = tagstackidx; int prev_num_matches; int new_tag = FALSE; ! int i; int ic; int no_regexp = FALSE; int error_cur_match = 0; int save_pos = FALSE; fmark_T saved_fmark; #ifdef FEAT_CSCOPE int jumped_to_tag = FALSE; #endif int new_num_matches; char_u **new_matches; int use_tagstack; int skip_msg = FALSE; char_u *buf_ffname = curbuf->b_ffname; /* name to use for *************** *** 482,487 **** --- 478,486 ---- */ for (;;) { + int other_name; + char_u *name; + /* * When desired match not found yet, try to find it (and others). */ *************** *** 541,549 **** * ":tnext" and jumping to another file. */ if (!new_tag && !other_name) { /* Find the position of each old match in the new list. Need * to use parse_match() to find the tag line. */ - idx = 0; for (j = 0; j < num_matches; ++j) { parse_match(matches[j], &tagp); --- 540,551 ---- * ":tnext" and jumping to another file. */ if (!new_tag && !other_name) { + int j, k; + int idx = 0; + tagptrs_T tagp, tagp2; + /* Find the position of each old match in the new list. Need * to use parse_match() to find the tag line. */ for (j = 0; j < num_matches; ++j) { parse_match(matches[j], &tagp); *************** *** 552,558 **** parse_match(new_matches[i], &tagp2); if (STRCMP(tagp.tagname, tagp2.tagname) == 0) { ! p = new_matches[i]; for (k = i; k > idx; --k) new_matches[k] = new_matches[k - 1]; new_matches[idx++] = p; --- 554,560 ---- parse_match(new_matches[i], &tagp2); if (STRCMP(tagp.tagname, tagp2.tagname) == 0) { ! char_u *p = new_matches[i]; for (k = i; k > idx; --k) new_matches[k] = new_matches[k - 1]; new_matches[idx++] = p; *************** *** 587,927 **** else #endif if (type == DT_TAG && *tag != NUL) ! /* ! * If a count is supplied to the ":tag " command, then ! * jump to count'th matching tag. ! */ cur_match = count > 0 ? count - 1 : 0; else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1)) { ! /* ! * List all the matching tags. ! * Assume that the first match indicates how long the tags can ! * be, and align the file names to that. ! */ ! parse_match(matches[0], &tagp); ! taglen = (int)(tagp.tagname_end - tagp.tagname + 2); ! if (taglen < 18) ! taglen = 18; ! if (taglen > Columns - 25) ! taglen = MAXCOL; ! if (msg_col == 0) ! msg_didout = FALSE; /* overwrite previous message */ ! msg_start(); ! msg_puts_attr(_(" # pri kind tag"), HL_ATTR(HLF_T)); ! msg_clr_eos(); ! taglen_advance(taglen); ! msg_puts_attr(_("file\n"), HL_ATTR(HLF_T)); ! ! for (i = 0; i < num_matches && !got_int; ++i) ! { ! parse_match(matches[i], &tagp); ! if (!new_tag && ( ! #if defined(FEAT_QUICKFIX) ! (g_do_tagpreview != 0 ! && i == ptag_entry.cur_match) || ! #endif ! (use_tagstack ! && i == tagstack[tagstackidx].cur_match))) ! *IObuff = '>'; ! else ! *IObuff = ' '; ! vim_snprintf((char *)IObuff + 1, IOSIZE - 1, ! "%2d %s ", i + 1, ! mt_names[matches[i][0] & MT_MASK]); ! msg_puts((char *)IObuff); ! if (tagp.tagkind != NULL) ! msg_outtrans_len(tagp.tagkind, ! (int)(tagp.tagkind_end - tagp.tagkind)); ! msg_advance(13); ! msg_outtrans_len_attr(tagp.tagname, ! (int)(tagp.tagname_end - tagp.tagname), ! HL_ATTR(HLF_T)); ! msg_putchar(' '); ! taglen_advance(taglen); ! ! /* Find out the actual file name. If it is long, truncate ! * it and put "..." in the middle */ ! p = tag_full_fname(&tagp); ! if (p != NULL) ! { ! msg_outtrans_long_attr(p, HL_ATTR(HLF_D)); ! vim_free(p); ! } ! if (msg_col > 0) ! msg_putchar('\n'); ! if (got_int) ! break; ! msg_advance(15); ! ! /* print any extra fields */ ! command_end = tagp.command_end; ! if (command_end != NULL) ! { ! p = command_end + 3; ! while (*p && *p != '\r' && *p != '\n') ! { ! while (*p == TAB) ! ++p; ! ! /* skip "file:" without a value (static tag) */ ! if (STRNCMP(p, "file:", 5) == 0 ! && vim_isspace(p[5])) ! { ! p += 5; ! continue; ! } ! /* skip "kind:" and "" */ ! if (p == tagp.tagkind ! || (p + 5 == tagp.tagkind ! && STRNCMP(p, "kind:", 5) == 0)) ! { ! p = tagp.tagkind_end; ! continue; ! } ! /* print all other extra fields */ ! attr = HL_ATTR(HLF_CM); ! while (*p && *p != '\r' && *p != '\n') ! { ! if (msg_col + ptr2cells(p) >= Columns) ! { ! msg_putchar('\n'); ! if (got_int) ! break; ! msg_advance(15); ! } ! p = msg_outtrans_one(p, attr); ! if (*p == TAB) ! { ! msg_puts_attr(" ", attr); ! break; ! } ! if (*p == ':') ! attr = 0; ! } ! } ! if (msg_col > 15) ! { ! msg_putchar('\n'); ! if (got_int) ! break; ! msg_advance(15); ! } ! } ! else ! { ! for (p = tagp.command; ! *p && *p != '\r' && *p != '\n'; ++p) ! ; ! command_end = p; ! } ! ! /* ! * Put the info (in several lines) at column 15. ! * Don't display "/^" and "?^". ! */ ! p = tagp.command; ! if (*p == '/' || *p == '?') ! { ! ++p; ! if (*p == '^') ! ++p; ! } ! /* Remove leading whitespace from pattern */ ! while (p != command_end && vim_isspace(*p)) ! ++p; ! ! while (p != command_end) ! { ! if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns) ! msg_putchar('\n'); ! if (got_int) ! break; ! msg_advance(15); ! ! /* skip backslash used for escaping a command char or ! * a backslash */ ! if (*p == '\\' && (*(p + 1) == *tagp.command ! || *(p + 1) == '\\')) ! ++p; ! ! if (*p == TAB) ! { ! msg_putchar(' '); ! ++p; ! } ! else ! p = msg_outtrans_one(p, 0); ! ! /* don't display the "$/;\"" and "$?;\"" */ ! if (p == command_end - 2 && *p == '$' ! && *(p + 1) == *tagp.command) ! break; ! /* don't display matching '/' or '?' */ ! if (p == command_end - 1 && *p == *tagp.command ! && (*p == '/' || *p == '?')) ! break; ! } ! if (msg_col) ! msg_putchar('\n'); ! ui_breakcheck(); ! } ! if (got_int) ! got_int = FALSE; /* only stop the listing */ ask_for_selection = TRUE; } #if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL) else if (type == DT_LTAG) { ! list_T *list; ! char_u tag_name[128 + 1]; ! char_u *fname; ! char_u *cmd; ! ! /* ! * Add the matching tags to the location list for the current ! * window. ! */ ! ! fname = alloc(MAXPATHL + 1); ! cmd = alloc(CMDBUFFSIZE + 1); ! list = list_alloc(); ! if (list == NULL || fname == NULL || cmd == NULL) ! { ! vim_free(cmd); ! vim_free(fname); ! if (list != NULL) ! list_free(list); goto end_do_tag; - } - - for (i = 0; i < num_matches; ++i) - { - int len, cmd_len; - long lnum; - dict_T *dict; - - parse_match(matches[i], &tagp); - - /* Save the tag name */ - len = (int)(tagp.tagname_end - tagp.tagname); - if (len > 128) - len = 128; - vim_strncpy(tag_name, tagp.tagname, len); - tag_name[len] = NUL; - - /* Save the tag file name */ - p = tag_full_fname(&tagp); - if (p == NULL) - continue; - vim_strncpy(fname, p, MAXPATHL); - vim_free(p); - - /* - * Get the line number or the search pattern used to locate - * the tag. - */ - lnum = 0; - if (isdigit(*tagp.command)) - /* Line number is used to locate the tag */ - lnum = atol((char *)tagp.command); - else - { - char_u *cmd_start, *cmd_end; - - /* Search pattern is used to locate the tag */ - - /* Locate the end of the command */ - cmd_start = tagp.command; - cmd_end = tagp.command_end; - if (cmd_end == NULL) - { - for (p = tagp.command; - *p && *p != '\r' && *p != '\n'; ++p) - ; - cmd_end = p; - } - - /* - * Now, cmd_end points to the character after the - * command. Adjust it to point to the last - * character of the command. - */ - cmd_end--; - - /* - * Skip the '/' and '?' characters at the - * beginning and end of the search pattern. - */ - if (*cmd_start == '/' || *cmd_start == '?') - cmd_start++; - - if (*cmd_end == '/' || *cmd_end == '?') - cmd_end--; - - len = 0; - cmd[0] = NUL; - - /* - * If "^" is present in the tag search pattern, then - * copy it first. - */ - if (*cmd_start == '^') - { - STRCPY(cmd, "^"); - cmd_start++; - len++; - } - - /* - * Precede the tag pattern with \V to make it very - * nomagic. - */ - STRCAT(cmd, "\\V"); - len += 2; - - cmd_len = (int)(cmd_end - cmd_start + 1); - if (cmd_len > (CMDBUFFSIZE - 5)) - cmd_len = CMDBUFFSIZE - 5; - STRNCAT(cmd, cmd_start, cmd_len); - len += cmd_len; - - if (cmd[len - 1] == '$') - { - /* - * Replace '$' at the end of the search pattern - * with '\$' - */ - cmd[len - 1] = '\\'; - cmd[len] = '$'; - len++; - } - - cmd[len] = NUL; - } - - if ((dict = dict_alloc()) == NULL) - continue; - if (list_append_dict(list, dict) == FAIL) - { - vim_free(dict); - continue; - } - - dict_add_string(dict, "text", tag_name); - dict_add_string(dict, "filename", fname); - dict_add_number(dict, "lnum", lnum); - if (lnum == 0) - dict_add_string(dict, "pattern", cmd); - } - - vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag); - set_errorlist(curwin, list, ' ', IObuff, NULL); - - list_free(list); - vim_free(fname); - vim_free(cmd); - cur_match = 0; /* Jump to the first tag */ } #endif --- 589,607 ---- else #endif if (type == DT_TAG && *tag != NUL) ! // If a count is supplied to the ":tag " command, then ! // jump to count'th matching tag. cur_match = count > 0 ? count - 1 : 0; else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1)) { ! print_tag_list(new_tag, use_tagstack, num_matches, matches); ask_for_selection = TRUE; } #if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL) else if (type == DT_LTAG) { ! if (add_llist_tags(tag, num_matches, matches) == FAIL) goto end_do_tag; cur_match = 0; /* Jump to the first tag */ } #endif *************** *** 1089,1094 **** --- 769,1116 ---- } /* + * List all the matching tags. + */ + static void + print_tag_list( + int new_tag, + int use_tagstack, + int num_matches, + char_u **matches) + { + taggy_T *tagstack = curwin->w_tagstack; + int tagstackidx = curwin->w_tagstackidx; + int i; + char_u *p; + char_u *command_end; + tagptrs_T tagp; + int taglen; + int attr; + + /* + * Assume that the first match indicates how long the tags can + * be, and align the file names to that. + */ + parse_match(matches[0], &tagp); + taglen = (int)(tagp.tagname_end - tagp.tagname + 2); + if (taglen < 18) + taglen = 18; + if (taglen > Columns - 25) + taglen = MAXCOL; + if (msg_col == 0) + msg_didout = FALSE; // overwrite previous message + msg_start(); + msg_puts_attr(_(" # pri kind tag"), HL_ATTR(HLF_T)); + msg_clr_eos(); + taglen_advance(taglen); + msg_puts_attr(_("file\n"), HL_ATTR(HLF_T)); + + for (i = 0; i < num_matches && !got_int; ++i) + { + parse_match(matches[i], &tagp); + if (!new_tag && ( + #if defined(FEAT_QUICKFIX) + (g_do_tagpreview != 0 + && i == ptag_entry.cur_match) || + #endif + (use_tagstack + && i == tagstack[tagstackidx].cur_match))) + *IObuff = '>'; + else + *IObuff = ' '; + vim_snprintf((char *)IObuff + 1, IOSIZE - 1, + "%2d %s ", i + 1, + mt_names[matches[i][0] & MT_MASK]); + msg_puts((char *)IObuff); + if (tagp.tagkind != NULL) + msg_outtrans_len(tagp.tagkind, + (int)(tagp.tagkind_end - tagp.tagkind)); + msg_advance(13); + msg_outtrans_len_attr(tagp.tagname, + (int)(tagp.tagname_end - tagp.tagname), + HL_ATTR(HLF_T)); + msg_putchar(' '); + taglen_advance(taglen); + + // Find out the actual file name. If it is long, truncate + // it and put "..." in the middle + p = tag_full_fname(&tagp); + if (p != NULL) + { + msg_outtrans_long_attr(p, HL_ATTR(HLF_D)); + vim_free(p); + } + if (msg_col > 0) + msg_putchar('\n'); + if (got_int) + break; + msg_advance(15); + + // print any extra fields + command_end = tagp.command_end; + if (command_end != NULL) + { + p = command_end + 3; + while (*p && *p != '\r' && *p != '\n') + { + while (*p == TAB) + ++p; + + // skip "file:" without a value (static tag) + if (STRNCMP(p, "file:", 5) == 0 + && vim_isspace(p[5])) + { + p += 5; + continue; + } + // skip "kind:" and "" + if (p == tagp.tagkind + || (p + 5 == tagp.tagkind + && STRNCMP(p, "kind:", 5) == 0)) + { + p = tagp.tagkind_end; + continue; + } + // print all other extra fields + attr = HL_ATTR(HLF_CM); + while (*p && *p != '\r' && *p != '\n') + { + if (msg_col + ptr2cells(p) >= Columns) + { + msg_putchar('\n'); + if (got_int) + break; + msg_advance(15); + } + p = msg_outtrans_one(p, attr); + if (*p == TAB) + { + msg_puts_attr(" ", attr); + break; + } + if (*p == ':') + attr = 0; + } + } + if (msg_col > 15) + { + msg_putchar('\n'); + if (got_int) + break; + msg_advance(15); + } + } + else + { + for (p = tagp.command; + *p && *p != '\r' && *p != '\n'; ++p) + ; + command_end = p; + } + + // Put the info (in several lines) at column 15. + // Don't display "/^" and "?^". + p = tagp.command; + if (*p == '/' || *p == '?') + { + ++p; + if (*p == '^') + ++p; + } + // Remove leading whitespace from pattern + while (p != command_end && vim_isspace(*p)) + ++p; + + while (p != command_end) + { + if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns) + msg_putchar('\n'); + if (got_int) + break; + msg_advance(15); + + // skip backslash used for escaping a command char or + // a backslash + if (*p == '\\' && (*(p + 1) == *tagp.command + || *(p + 1) == '\\')) + ++p; + + if (*p == TAB) + { + msg_putchar(' '); + ++p; + } + else + p = msg_outtrans_one(p, 0); + + // don't display the "$/;\"" and "$?;\"" + if (p == command_end - 2 && *p == '$' + && *(p + 1) == *tagp.command) + break; + // don't display matching '/' or '?' + if (p == command_end - 1 && *p == *tagp.command + && (*p == '/' || *p == '?')) + break; + } + if (msg_col) + msg_putchar('\n'); + ui_breakcheck(); + } + if (got_int) + got_int = FALSE; // only stop the listing + } + + #if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL) + /* + * Add the matching tags to the location list for the current + * window. + */ + static int + add_llist_tags( + char_u *tag, + int num_matches, + char_u **matches) + { + list_T *list; + char_u tag_name[128 + 1]; + char_u *fname; + char_u *cmd; + int i; + char_u *p; + tagptrs_T tagp; + + fname = alloc(MAXPATHL + 1); + cmd = alloc(CMDBUFFSIZE + 1); + list = list_alloc(); + if (list == NULL || fname == NULL || cmd == NULL) + { + vim_free(cmd); + vim_free(fname); + if (list != NULL) + list_free(list); + return FAIL; + } + + for (i = 0; i < num_matches; ++i) + { + int len, cmd_len; + long lnum; + dict_T *dict; + + parse_match(matches[i], &tagp); + + /* Save the tag name */ + len = (int)(tagp.tagname_end - tagp.tagname); + if (len > 128) + len = 128; + vim_strncpy(tag_name, tagp.tagname, len); + tag_name[len] = NUL; + + // Save the tag file name + p = tag_full_fname(&tagp); + if (p == NULL) + continue; + vim_strncpy(fname, p, MAXPATHL); + vim_free(p); + + // Get the line number or the search pattern used to locate + // the tag. + lnum = 0; + if (isdigit(*tagp.command)) + // Line number is used to locate the tag + lnum = atol((char *)tagp.command); + else + { + char_u *cmd_start, *cmd_end; + + // Search pattern is used to locate the tag + + // Locate the end of the command + cmd_start = tagp.command; + cmd_end = tagp.command_end; + if (cmd_end == NULL) + { + for (p = tagp.command; + *p && *p != '\r' && *p != '\n'; ++p) + ; + cmd_end = p; + } + + // Now, cmd_end points to the character after the + // command. Adjust it to point to the last + // character of the command. + cmd_end--; + + // Skip the '/' and '?' characters at the + // beginning and end of the search pattern. + if (*cmd_start == '/' || *cmd_start == '?') + cmd_start++; + + if (*cmd_end == '/' || *cmd_end == '?') + cmd_end--; + + len = 0; + cmd[0] = NUL; + + // If "^" is present in the tag search pattern, then + // copy it first. + if (*cmd_start == '^') + { + STRCPY(cmd, "^"); + cmd_start++; + len++; + } + + // Precede the tag pattern with \V to make it very + // nomagic. + STRCAT(cmd, "\\V"); + len += 2; + + cmd_len = (int)(cmd_end - cmd_start + 1); + if (cmd_len > (CMDBUFFSIZE - 5)) + cmd_len = CMDBUFFSIZE - 5; + STRNCAT(cmd, cmd_start, cmd_len); + len += cmd_len; + + if (cmd[len - 1] == '$') + { + // Replace '$' at the end of the search pattern + // with '\$' + cmd[len - 1] = '\\'; + cmd[len] = '$'; + len++; + } + + cmd[len] = NUL; + } + + if ((dict = dict_alloc()) == NULL) + continue; + if (list_append_dict(list, dict) == FAIL) + { + vim_free(dict); + continue; + } + + dict_add_string(dict, "text", tag_name); + dict_add_string(dict, "filename", fname); + dict_add_number(dict, "lnum", lnum); + if (lnum == 0) + dict_add_string(dict, "pattern", cmd); + } + + vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag); + set_errorlist(curwin, list, ' ', IObuff, NULL); + + list_free(list); + vim_free(fname); + vim_free(cmd); + + return OK; + } + #endif + + /* * Free cached tags. */ void *************** *** 3431,3648 **** } /* - * Converts a file name into a canonical form. It simplifies a file name into - * its simplest form by stripping out unneeded components, if any. The - * resulting file name is simplified in place and will either be the same - * length as that supplied, or shorter. - */ - void - simplify_filename(char_u *filename) - { - #ifndef AMIGA /* Amiga doesn't have "..", it uses "/" */ - int components = 0; - char_u *p, *tail, *start; - int stripping_disabled = FALSE; - int relative = TRUE; - - p = filename; - #ifdef BACKSLASH_IN_FILENAME - if (p[1] == ':') /* skip "x:" */ - p += 2; - #endif - - if (vim_ispathsep(*p)) - { - relative = FALSE; - do - ++p; - while (vim_ispathsep(*p)); - } - start = p; /* remember start after "c:/" or "/" or "///" */ - - do - { - /* At this point "p" is pointing to the char following a single "/" - * or "p" is at the "start" of the (absolute or relative) path name. */ - #ifdef VMS - /* VMS allows device:[path] - don't strip the [ in directory */ - if ((*p == '[' || *p == '<') && p > filename && p[-1] == ':') - { - /* :[ or :< composition: vms directory component */ - ++components; - p = getnextcomp(p + 1); - } - /* allow remote calls as host"user passwd"::device:[path] */ - else if (p[0] == ':' && p[1] == ':' && p > filename && p[-1] == '"' ) - { - /* ":: composition: vms host/passwd component */ - ++components; - p = getnextcomp(p + 2); - } - else - #endif - if (vim_ispathsep(*p)) - STRMOVE(p, p + 1); /* remove duplicate "/" */ - else if (p[0] == '.' && (vim_ispathsep(p[1]) || p[1] == NUL)) - { - if (p == start && relative) - p += 1 + (p[1] != NUL); /* keep single "." or leading "./" */ - else - { - /* Strip "./" or ".///". If we are at the end of the file name - * and there is no trailing path separator, either strip "/." if - * we are after "start", or strip "." if we are at the beginning - * of an absolute path name . */ - tail = p + 1; - if (p[1] != NUL) - while (vim_ispathsep(*tail)) - MB_PTR_ADV(tail); - else if (p > start) - --p; /* strip preceding path separator */ - STRMOVE(p, tail); - } - } - else if (p[0] == '.' && p[1] == '.' && - (vim_ispathsep(p[2]) || p[2] == NUL)) - { - /* Skip to after ".." or "../" or "..///". */ - tail = p + 2; - while (vim_ispathsep(*tail)) - MB_PTR_ADV(tail); - - if (components > 0) /* strip one preceding component */ - { - int do_strip = FALSE; - char_u saved_char; - stat_T st; - - /* Don't strip for an erroneous file name. */ - if (!stripping_disabled) - { - /* If the preceding component does not exist in the file - * system, we strip it. On Unix, we don't accept a symbolic - * link that refers to a non-existent file. */ - saved_char = p[-1]; - p[-1] = NUL; - #ifdef UNIX - if (mch_lstat((char *)filename, &st) < 0) - #else - if (mch_stat((char *)filename, &st) < 0) - #endif - do_strip = TRUE; - p[-1] = saved_char; - - --p; - /* Skip back to after previous '/'. */ - while (p > start && !after_pathsep(start, p)) - MB_PTR_BACK(start, p); - - if (!do_strip) - { - /* If the component exists in the file system, check - * that stripping it won't change the meaning of the - * file name. First get information about the - * unstripped file name. This may fail if the component - * to strip is not a searchable directory (but a regular - * file, for instance), since the trailing "/.." cannot - * be applied then. We don't strip it then since we - * don't want to replace an erroneous file name by - * a valid one, and we disable stripping of later - * components. */ - saved_char = *tail; - *tail = NUL; - if (mch_stat((char *)filename, &st) >= 0) - do_strip = TRUE; - else - stripping_disabled = TRUE; - *tail = saved_char; - #ifdef UNIX - if (do_strip) - { - stat_T new_st; - - /* On Unix, the check for the unstripped file name - * above works also for a symbolic link pointing to - * a searchable directory. But then the parent of - * the directory pointed to by the link must be the - * same as the stripped file name. (The latter - * exists in the file system since it is the - * component's parent directory.) */ - if (p == start && relative) - (void)mch_stat(".", &new_st); - else - { - saved_char = *p; - *p = NUL; - (void)mch_stat((char *)filename, &new_st); - *p = saved_char; - } - - if (new_st.st_ino != st.st_ino || - new_st.st_dev != st.st_dev) - { - do_strip = FALSE; - /* We don't disable stripping of later - * components since the unstripped path name is - * still valid. */ - } - } - #endif - } - } - - if (!do_strip) - { - /* Skip the ".." or "../" and reset the counter for the - * components that might be stripped later on. */ - p = tail; - components = 0; - } - else - { - /* Strip previous component. If the result would get empty - * and there is no trailing path separator, leave a single - * "." instead. If we are at the end of the file name and - * there is no trailing path separator and a preceding - * component is left after stripping, strip its trailing - * path separator as well. */ - if (p == start && relative && tail[-1] == '.') - { - *p++ = '.'; - *p = NUL; - } - else - { - if (p > start && tail[-1] == '.') - --p; - STRMOVE(p, tail); /* strip previous component */ - } - - --components; - } - } - else if (p == start && !relative) /* leading "/.." or "/../" */ - STRMOVE(p, tail); /* strip ".." or "../" */ - else - { - if (p == start + 2 && p[-2] == '.') /* leading "./../" */ - { - STRMOVE(p - 2, p); /* strip leading "./" */ - tail -= 2; - } - p = tail; /* skip to char after ".." or "../" */ - } - } - else - { - ++components; /* simple path component */ - p = getnextcomp(p); - } - } while (*p != NUL); - #endif /* !AMIGA */ - } - - /* * Check if we have a tag for the buffer with name "buf_ffname". * This is a bit slow, because of the full path compare in fullpathcmp(). * Return TRUE if tag for file "fname" if tag file "tag_fname" is for current --- 3453,3458 ---- *** ../vim-8.1.1098/src/proto/tag.pro 2018-11-11 15:20:32.436704418 +0100 --- src/proto/tag.pro 2019-03-31 19:09:23.798893253 +0200 *************** *** 6,12 **** void free_tag_stuff(void); int get_tagfname(tagname_T *tnp, int first, char_u *buf); void tagname_free(tagname_T *tnp); - void simplify_filename(char_u *filename); int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file); int get_tags(list_T *list, char_u *pat, char_u *buf_fname); void get_tagstack(win_T *wp, dict_T *retdict); --- 6,11 ---- *** ../vim-8.1.1098/src/findfile.c 2019-03-09 12:32:50.673562149 +0100 --- src/findfile.c 2019-03-31 19:09:15.586959263 +0200 *************** *** 2605,2607 **** --- 2605,2819 ---- } #endif // FEAT_SEARCHPATH + + /* + * Converts a file name into a canonical form. It simplifies a file name into + * its simplest form by stripping out unneeded components, if any. The + * resulting file name is simplified in place and will either be the same + * length as that supplied, or shorter. + */ + void + simplify_filename(char_u *filename) + { + #ifndef AMIGA // Amiga doesn't have "..", it uses "/" + int components = 0; + char_u *p, *tail, *start; + int stripping_disabled = FALSE; + int relative = TRUE; + + p = filename; + # ifdef BACKSLASH_IN_FILENAME + if (p[1] == ':') // skip "x:" + p += 2; + # endif + + if (vim_ispathsep(*p)) + { + relative = FALSE; + do + ++p; + while (vim_ispathsep(*p)); + } + start = p; // remember start after "c:/" or "/" or "///" + + do + { + // At this point "p" is pointing to the char following a single "/" + // or "p" is at the "start" of the (absolute or relative) path name. + # ifdef VMS + // VMS allows device:[path] - don't strip the [ in directory + if ((*p == '[' || *p == '<') && p > filename && p[-1] == ':') + { + // :[ or :< composition: vms directory component + ++components; + p = getnextcomp(p + 1); + } + // allow remote calls as host"user passwd"::device:[path] + else if (p[0] == ':' && p[1] == ':' && p > filename && p[-1] == '"' ) + { + // ":: composition: vms host/passwd component + ++components; + p = getnextcomp(p + 2); + } + else + # endif + if (vim_ispathsep(*p)) + STRMOVE(p, p + 1); // remove duplicate "/" + else if (p[0] == '.' && (vim_ispathsep(p[1]) || p[1] == NUL)) + { + if (p == start && relative) + p += 1 + (p[1] != NUL); // keep single "." or leading "./" + else + { + // Strip "./" or ".///". If we are at the end of the file name + // and there is no trailing path separator, either strip "/." if + // we are after "start", or strip "." if we are at the beginning + // of an absolute path name . + tail = p + 1; + if (p[1] != NUL) + while (vim_ispathsep(*tail)) + MB_PTR_ADV(tail); + else if (p > start) + --p; // strip preceding path separator + STRMOVE(p, tail); + } + } + else if (p[0] == '.' && p[1] == '.' && + (vim_ispathsep(p[2]) || p[2] == NUL)) + { + // Skip to after ".." or "../" or "..///". + tail = p + 2; + while (vim_ispathsep(*tail)) + MB_PTR_ADV(tail); + + if (components > 0) // strip one preceding component + { + int do_strip = FALSE; + char_u saved_char; + stat_T st; + + /* Don't strip for an erroneous file name. */ + if (!stripping_disabled) + { + // If the preceding component does not exist in the file + // system, we strip it. On Unix, we don't accept a symbolic + // link that refers to a non-existent file. + saved_char = p[-1]; + p[-1] = NUL; + # ifdef UNIX + if (mch_lstat((char *)filename, &st) < 0) + # else + if (mch_stat((char *)filename, &st) < 0) + # endif + do_strip = TRUE; + p[-1] = saved_char; + + --p; + // Skip back to after previous '/'. + while (p > start && !after_pathsep(start, p)) + MB_PTR_BACK(start, p); + + if (!do_strip) + { + // If the component exists in the file system, check + // that stripping it won't change the meaning of the + // file name. First get information about the + // unstripped file name. This may fail if the component + // to strip is not a searchable directory (but a regular + // file, for instance), since the trailing "/.." cannot + // be applied then. We don't strip it then since we + // don't want to replace an erroneous file name by + // a valid one, and we disable stripping of later + // components. + saved_char = *tail; + *tail = NUL; + if (mch_stat((char *)filename, &st) >= 0) + do_strip = TRUE; + else + stripping_disabled = TRUE; + *tail = saved_char; + # ifdef UNIX + if (do_strip) + { + stat_T new_st; + + // On Unix, the check for the unstripped file name + // above works also for a symbolic link pointing to + // a searchable directory. But then the parent of + // the directory pointed to by the link must be the + // same as the stripped file name. (The latter + // exists in the file system since it is the + // component's parent directory.) + if (p == start && relative) + (void)mch_stat(".", &new_st); + else + { + saved_char = *p; + *p = NUL; + (void)mch_stat((char *)filename, &new_st); + *p = saved_char; + } + + if (new_st.st_ino != st.st_ino || + new_st.st_dev != st.st_dev) + { + do_strip = FALSE; + // We don't disable stripping of later + // components since the unstripped path name is + // still valid. + } + } + # endif + } + } + + if (!do_strip) + { + // Skip the ".." or "../" and reset the counter for the + // components that might be stripped later on. + p = tail; + components = 0; + } + else + { + // Strip previous component. If the result would get empty + // and there is no trailing path separator, leave a single + // "." instead. If we are at the end of the file name and + // there is no trailing path separator and a preceding + // component is left after stripping, strip its trailing + // path separator as well. + if (p == start && relative && tail[-1] == '.') + { + *p++ = '.'; + *p = NUL; + } + else + { + if (p > start && tail[-1] == '.') + --p; + STRMOVE(p, tail); // strip previous component + } + + --components; + } + } + else if (p == start && !relative) // leading "/.." or "/../" + STRMOVE(p, tail); // strip ".." or "../" + else + { + if (p == start + 2 && p[-2] == '.') // leading "./../" + { + STRMOVE(p - 2, p); // strip leading "./" + tail -= 2; + } + p = tail; // skip to char after ".." or "../" + } + } + else + { + ++components; // simple path component + p = getnextcomp(p); + } + } while (*p != NUL); + #endif // !AMIGA + } *** ../vim-8.1.1098/src/proto/findfile.pro 2019-02-13 22:45:21.512636158 +0100 --- src/proto/findfile.pro 2019-03-31 19:09:20.870916772 +0200 *************** *** 15,18 **** --- 15,19 ---- int vim_ispathlistsep(int c); void uniquefy_paths(garray_T *gap, char_u *pattern); int expand_in_path(garray_T *gap, char_u *pattern, int flags); + void simplify_filename(char_u *filename); /* vim: set ft=c : */ *** ../vim-8.1.1098/src/version.c 2019-03-31 15:31:54.592053004 +0200 --- src/version.c 2019-03-31 19:39:26.094371299 +0200 *************** *** 773,774 **** --- 773,776 ---- { /* Add new patch number below this line */ + /**/ + 1099, /**/ -- hundred-and-one symptoms of being an internet addict: 175. You send yourself e-mail before you go to bed to remind you what to do when you wake up. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///