comparison src/tag.c @ 16188:848d4c6e391e v8.1.1099

patch 8.1.1099: the do_tag() function is too long commit https://github.com/vim/vim/commit/b4a6020ac6a0638167013f1e45ff440ddc8a1671 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Mar 31 19:40:07 2019 +0200 patch 8.1.1099: the do_tag() function is too long 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)
author Bram Moolenaar <Bram@vim.org>
date Sun, 31 Mar 2019 19:45:05 +0200
parents a8689ea2e869
children d863beec391a
comparison
equal deleted inserted replaced
16187:97b99ffd8aeb 16188:848d4c6e391e
72 static int test_for_current(int, char_u *, char_u *, char_u *, char_u *); 72 static int test_for_current(int, char_u *, char_u *, char_u *, char_u *);
73 #else 73 #else
74 static int test_for_current(char_u *, char_u *, char_u *, char_u *); 74 static int test_for_current(char_u *, char_u *, char_u *, char_u *);
75 #endif 75 #endif
76 static int find_extra(char_u **pp); 76 static int find_extra(char_u **pp);
77 static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_u **matches);
78 #if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL)
79 static int add_llist_tags(char_u *tag, int num_matches, char_u **matches);
80 #endif
77 81
78 static char_u *bottommsg = (char_u *)N_("E555: at bottom of tag stack"); 82 static char_u *bottommsg = (char_u *)N_("E555: at bottom of tag stack");
79 static char_u *topmsg = (char_u *)N_("E556: at top of tag stack"); 83 static char_u *topmsg = (char_u *)N_("E556: at top of tag stack");
80 84
81 static char_u *tagmatchname = NULL; /* name of last used tag */ 85 static char_u *tagmatchname = NULL; /* name of last used tag */
123 int cur_fnum = curbuf->b_fnum; 127 int cur_fnum = curbuf->b_fnum;
124 int oldtagstackidx = tagstackidx; 128 int oldtagstackidx = tagstackidx;
125 int prevtagstackidx = tagstackidx; 129 int prevtagstackidx = tagstackidx;
126 int prev_num_matches; 130 int prev_num_matches;
127 int new_tag = FALSE; 131 int new_tag = FALSE;
128 int other_name; 132 int i;
129 int i, j, k;
130 int idx;
131 int ic; 133 int ic;
132 char_u *p;
133 char_u *name;
134 int no_regexp = FALSE; 134 int no_regexp = FALSE;
135 int error_cur_match = 0; 135 int error_cur_match = 0;
136 char_u *command_end;
137 int save_pos = FALSE; 136 int save_pos = FALSE;
138 fmark_T saved_fmark; 137 fmark_T saved_fmark;
139 int taglen;
140 #ifdef FEAT_CSCOPE 138 #ifdef FEAT_CSCOPE
141 int jumped_to_tag = FALSE; 139 int jumped_to_tag = FALSE;
142 #endif 140 #endif
143 tagptrs_T tagp, tagp2;
144 int new_num_matches; 141 int new_num_matches;
145 char_u **new_matches; 142 char_u **new_matches;
146 int attr;
147 int use_tagstack; 143 int use_tagstack;
148 int skip_msg = FALSE; 144 int skip_msg = FALSE;
149 char_u *buf_ffname = curbuf->b_ffname; /* name to use for 145 char_u *buf_ffname = curbuf->b_ffname; /* name to use for
150 priority computation */ 146 priority computation */
151 147
480 /* 476 /*
481 * Repeat searching for tags, when a file has not been found. 477 * Repeat searching for tags, when a file has not been found.
482 */ 478 */
483 for (;;) 479 for (;;)
484 { 480 {
481 int other_name;
482 char_u *name;
483
485 /* 484 /*
486 * When desired match not found yet, try to find it (and others). 485 * When desired match not found yet, try to find it (and others).
487 */ 486 */
488 if (use_tagstack) 487 if (use_tagstack)
489 name = tagstack[tagstackidx].tagname; 488 name = tagstack[tagstackidx].tagname;
539 /* If there already were some matches for the same name, move them 538 /* If there already were some matches for the same name, move them
540 * to the start. Avoids that the order changes when using 539 * to the start. Avoids that the order changes when using
541 * ":tnext" and jumping to another file. */ 540 * ":tnext" and jumping to another file. */
542 if (!new_tag && !other_name) 541 if (!new_tag && !other_name)
543 { 542 {
543 int j, k;
544 int idx = 0;
545 tagptrs_T tagp, tagp2;
546
544 /* Find the position of each old match in the new list. Need 547 /* Find the position of each old match in the new list. Need
545 * to use parse_match() to find the tag line. */ 548 * to use parse_match() to find the tag line. */
546 idx = 0;
547 for (j = 0; j < num_matches; ++j) 549 for (j = 0; j < num_matches; ++j)
548 { 550 {
549 parse_match(matches[j], &tagp); 551 parse_match(matches[j], &tagp);
550 for (i = idx; i < new_num_matches; ++i) 552 for (i = idx; i < new_num_matches; ++i)
551 { 553 {
552 parse_match(new_matches[i], &tagp2); 554 parse_match(new_matches[i], &tagp2);
553 if (STRCMP(tagp.tagname, tagp2.tagname) == 0) 555 if (STRCMP(tagp.tagname, tagp2.tagname) == 0)
554 { 556 {
555 p = new_matches[i]; 557 char_u *p = new_matches[i];
556 for (k = i; k > idx; --k) 558 for (k = i; k > idx; --k)
557 new_matches[k] = new_matches[k - 1]; 559 new_matches[k] = new_matches[k - 1];
558 new_matches[idx++] = p; 560 new_matches[idx++] = p;
559 break; 561 break;
560 } 562 }
585 ask_for_selection = TRUE; 587 ask_for_selection = TRUE;
586 } 588 }
587 else 589 else
588 #endif 590 #endif
589 if (type == DT_TAG && *tag != NUL) 591 if (type == DT_TAG && *tag != NUL)
590 /* 592 // If a count is supplied to the ":tag <name>" command, then
591 * If a count is supplied to the ":tag <name>" command, then 593 // jump to count'th matching tag.
592 * jump to count'th matching tag.
593 */
594 cur_match = count > 0 ? count - 1 : 0; 594 cur_match = count > 0 ? count - 1 : 0;
595 else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1)) 595 else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1))
596 { 596 {
597 /* 597 print_tag_list(new_tag, use_tagstack, num_matches, matches);
598 * List all the matching tags.
599 * Assume that the first match indicates how long the tags can
600 * be, and align the file names to that.
601 */
602 parse_match(matches[0], &tagp);
603 taglen = (int)(tagp.tagname_end - tagp.tagname + 2);
604 if (taglen < 18)
605 taglen = 18;
606 if (taglen > Columns - 25)
607 taglen = MAXCOL;
608 if (msg_col == 0)
609 msg_didout = FALSE; /* overwrite previous message */
610 msg_start();
611 msg_puts_attr(_(" # pri kind tag"), HL_ATTR(HLF_T));
612 msg_clr_eos();
613 taglen_advance(taglen);
614 msg_puts_attr(_("file\n"), HL_ATTR(HLF_T));
615
616 for (i = 0; i < num_matches && !got_int; ++i)
617 {
618 parse_match(matches[i], &tagp);
619 if (!new_tag && (
620 #if defined(FEAT_QUICKFIX)
621 (g_do_tagpreview != 0
622 && i == ptag_entry.cur_match) ||
623 #endif
624 (use_tagstack
625 && i == tagstack[tagstackidx].cur_match)))
626 *IObuff = '>';
627 else
628 *IObuff = ' ';
629 vim_snprintf((char *)IObuff + 1, IOSIZE - 1,
630 "%2d %s ", i + 1,
631 mt_names[matches[i][0] & MT_MASK]);
632 msg_puts((char *)IObuff);
633 if (tagp.tagkind != NULL)
634 msg_outtrans_len(tagp.tagkind,
635 (int)(tagp.tagkind_end - tagp.tagkind));
636 msg_advance(13);
637 msg_outtrans_len_attr(tagp.tagname,
638 (int)(tagp.tagname_end - tagp.tagname),
639 HL_ATTR(HLF_T));
640 msg_putchar(' ');
641 taglen_advance(taglen);
642
643 /* Find out the actual file name. If it is long, truncate
644 * it and put "..." in the middle */
645 p = tag_full_fname(&tagp);
646 if (p != NULL)
647 {
648 msg_outtrans_long_attr(p, HL_ATTR(HLF_D));
649 vim_free(p);
650 }
651 if (msg_col > 0)
652 msg_putchar('\n');
653 if (got_int)
654 break;
655 msg_advance(15);
656
657 /* print any extra fields */
658 command_end = tagp.command_end;
659 if (command_end != NULL)
660 {
661 p = command_end + 3;
662 while (*p && *p != '\r' && *p != '\n')
663 {
664 while (*p == TAB)
665 ++p;
666
667 /* skip "file:" without a value (static tag) */
668 if (STRNCMP(p, "file:", 5) == 0
669 && vim_isspace(p[5]))
670 {
671 p += 5;
672 continue;
673 }
674 /* skip "kind:<kind>" and "<kind>" */
675 if (p == tagp.tagkind
676 || (p + 5 == tagp.tagkind
677 && STRNCMP(p, "kind:", 5) == 0))
678 {
679 p = tagp.tagkind_end;
680 continue;
681 }
682 /* print all other extra fields */
683 attr = HL_ATTR(HLF_CM);
684 while (*p && *p != '\r' && *p != '\n')
685 {
686 if (msg_col + ptr2cells(p) >= Columns)
687 {
688 msg_putchar('\n');
689 if (got_int)
690 break;
691 msg_advance(15);
692 }
693 p = msg_outtrans_one(p, attr);
694 if (*p == TAB)
695 {
696 msg_puts_attr(" ", attr);
697 break;
698 }
699 if (*p == ':')
700 attr = 0;
701 }
702 }
703 if (msg_col > 15)
704 {
705 msg_putchar('\n');
706 if (got_int)
707 break;
708 msg_advance(15);
709 }
710 }
711 else
712 {
713 for (p = tagp.command;
714 *p && *p != '\r' && *p != '\n'; ++p)
715 ;
716 command_end = p;
717 }
718
719 /*
720 * Put the info (in several lines) at column 15.
721 * Don't display "/^" and "?^".
722 */
723 p = tagp.command;
724 if (*p == '/' || *p == '?')
725 {
726 ++p;
727 if (*p == '^')
728 ++p;
729 }
730 /* Remove leading whitespace from pattern */
731 while (p != command_end && vim_isspace(*p))
732 ++p;
733
734 while (p != command_end)
735 {
736 if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns)
737 msg_putchar('\n');
738 if (got_int)
739 break;
740 msg_advance(15);
741
742 /* skip backslash used for escaping a command char or
743 * a backslash */
744 if (*p == '\\' && (*(p + 1) == *tagp.command
745 || *(p + 1) == '\\'))
746 ++p;
747
748 if (*p == TAB)
749 {
750 msg_putchar(' ');
751 ++p;
752 }
753 else
754 p = msg_outtrans_one(p, 0);
755
756 /* don't display the "$/;\"" and "$?;\"" */
757 if (p == command_end - 2 && *p == '$'
758 && *(p + 1) == *tagp.command)
759 break;
760 /* don't display matching '/' or '?' */
761 if (p == command_end - 1 && *p == *tagp.command
762 && (*p == '/' || *p == '?'))
763 break;
764 }
765 if (msg_col)
766 msg_putchar('\n');
767 ui_breakcheck();
768 }
769 if (got_int)
770 got_int = FALSE; /* only stop the listing */
771 ask_for_selection = TRUE; 598 ask_for_selection = TRUE;
772 } 599 }
773 #if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL) 600 #if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL)
774 else if (type == DT_LTAG) 601 else if (type == DT_LTAG)
775 { 602 {
776 list_T *list; 603 if (add_llist_tags(tag, num_matches, matches) == FAIL)
777 char_u tag_name[128 + 1];
778 char_u *fname;
779 char_u *cmd;
780
781 /*
782 * Add the matching tags to the location list for the current
783 * window.
784 */
785
786 fname = alloc(MAXPATHL + 1);
787 cmd = alloc(CMDBUFFSIZE + 1);
788 list = list_alloc();
789 if (list == NULL || fname == NULL || cmd == NULL)
790 {
791 vim_free(cmd);
792 vim_free(fname);
793 if (list != NULL)
794 list_free(list);
795 goto end_do_tag; 604 goto end_do_tag;
796 }
797
798 for (i = 0; i < num_matches; ++i)
799 {
800 int len, cmd_len;
801 long lnum;
802 dict_T *dict;
803
804 parse_match(matches[i], &tagp);
805
806 /* Save the tag name */
807 len = (int)(tagp.tagname_end - tagp.tagname);
808 if (len > 128)
809 len = 128;
810 vim_strncpy(tag_name, tagp.tagname, len);
811 tag_name[len] = NUL;
812
813 /* Save the tag file name */
814 p = tag_full_fname(&tagp);
815 if (p == NULL)
816 continue;
817 vim_strncpy(fname, p, MAXPATHL);
818 vim_free(p);
819
820 /*
821 * Get the line number or the search pattern used to locate
822 * the tag.
823 */
824 lnum = 0;
825 if (isdigit(*tagp.command))
826 /* Line number is used to locate the tag */
827 lnum = atol((char *)tagp.command);
828 else
829 {
830 char_u *cmd_start, *cmd_end;
831
832 /* Search pattern is used to locate the tag */
833
834 /* Locate the end of the command */
835 cmd_start = tagp.command;
836 cmd_end = tagp.command_end;
837 if (cmd_end == NULL)
838 {
839 for (p = tagp.command;
840 *p && *p != '\r' && *p != '\n'; ++p)
841 ;
842 cmd_end = p;
843 }
844
845 /*
846 * Now, cmd_end points to the character after the
847 * command. Adjust it to point to the last
848 * character of the command.
849 */
850 cmd_end--;
851
852 /*
853 * Skip the '/' and '?' characters at the
854 * beginning and end of the search pattern.
855 */
856 if (*cmd_start == '/' || *cmd_start == '?')
857 cmd_start++;
858
859 if (*cmd_end == '/' || *cmd_end == '?')
860 cmd_end--;
861
862 len = 0;
863 cmd[0] = NUL;
864
865 /*
866 * If "^" is present in the tag search pattern, then
867 * copy it first.
868 */
869 if (*cmd_start == '^')
870 {
871 STRCPY(cmd, "^");
872 cmd_start++;
873 len++;
874 }
875
876 /*
877 * Precede the tag pattern with \V to make it very
878 * nomagic.
879 */
880 STRCAT(cmd, "\\V");
881 len += 2;
882
883 cmd_len = (int)(cmd_end - cmd_start + 1);
884 if (cmd_len > (CMDBUFFSIZE - 5))
885 cmd_len = CMDBUFFSIZE - 5;
886 STRNCAT(cmd, cmd_start, cmd_len);
887 len += cmd_len;
888
889 if (cmd[len - 1] == '$')
890 {
891 /*
892 * Replace '$' at the end of the search pattern
893 * with '\$'
894 */
895 cmd[len - 1] = '\\';
896 cmd[len] = '$';
897 len++;
898 }
899
900 cmd[len] = NUL;
901 }
902
903 if ((dict = dict_alloc()) == NULL)
904 continue;
905 if (list_append_dict(list, dict) == FAIL)
906 {
907 vim_free(dict);
908 continue;
909 }
910
911 dict_add_string(dict, "text", tag_name);
912 dict_add_string(dict, "filename", fname);
913 dict_add_number(dict, "lnum", lnum);
914 if (lnum == 0)
915 dict_add_string(dict, "pattern", cmd);
916 }
917
918 vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag);
919 set_errorlist(curwin, list, ' ', IObuff, NULL);
920
921 list_free(list);
922 vim_free(fname);
923 vim_free(cmd);
924
925 cur_match = 0; /* Jump to the first tag */ 605 cur_match = 0; /* Jump to the first tag */
926 } 606 }
927 #endif 607 #endif
928 608
929 if (ask_for_selection == TRUE) 609 if (ask_for_selection == TRUE)
1085 return jumped_to_tag; 765 return jumped_to_tag;
1086 #else 766 #else
1087 return FALSE; 767 return FALSE;
1088 #endif 768 #endif
1089 } 769 }
770
771 /*
772 * List all the matching tags.
773 */
774 static void
775 print_tag_list(
776 int new_tag,
777 int use_tagstack,
778 int num_matches,
779 char_u **matches)
780 {
781 taggy_T *tagstack = curwin->w_tagstack;
782 int tagstackidx = curwin->w_tagstackidx;
783 int i;
784 char_u *p;
785 char_u *command_end;
786 tagptrs_T tagp;
787 int taglen;
788 int attr;
789
790 /*
791 * Assume that the first match indicates how long the tags can
792 * be, and align the file names to that.
793 */
794 parse_match(matches[0], &tagp);
795 taglen = (int)(tagp.tagname_end - tagp.tagname + 2);
796 if (taglen < 18)
797 taglen = 18;
798 if (taglen > Columns - 25)
799 taglen = MAXCOL;
800 if (msg_col == 0)
801 msg_didout = FALSE; // overwrite previous message
802 msg_start();
803 msg_puts_attr(_(" # pri kind tag"), HL_ATTR(HLF_T));
804 msg_clr_eos();
805 taglen_advance(taglen);
806 msg_puts_attr(_("file\n"), HL_ATTR(HLF_T));
807
808 for (i = 0; i < num_matches && !got_int; ++i)
809 {
810 parse_match(matches[i], &tagp);
811 if (!new_tag && (
812 #if defined(FEAT_QUICKFIX)
813 (g_do_tagpreview != 0
814 && i == ptag_entry.cur_match) ||
815 #endif
816 (use_tagstack
817 && i == tagstack[tagstackidx].cur_match)))
818 *IObuff = '>';
819 else
820 *IObuff = ' ';
821 vim_snprintf((char *)IObuff + 1, IOSIZE - 1,
822 "%2d %s ", i + 1,
823 mt_names[matches[i][0] & MT_MASK]);
824 msg_puts((char *)IObuff);
825 if (tagp.tagkind != NULL)
826 msg_outtrans_len(tagp.tagkind,
827 (int)(tagp.tagkind_end - tagp.tagkind));
828 msg_advance(13);
829 msg_outtrans_len_attr(tagp.tagname,
830 (int)(tagp.tagname_end - tagp.tagname),
831 HL_ATTR(HLF_T));
832 msg_putchar(' ');
833 taglen_advance(taglen);
834
835 // Find out the actual file name. If it is long, truncate
836 // it and put "..." in the middle
837 p = tag_full_fname(&tagp);
838 if (p != NULL)
839 {
840 msg_outtrans_long_attr(p, HL_ATTR(HLF_D));
841 vim_free(p);
842 }
843 if (msg_col > 0)
844 msg_putchar('\n');
845 if (got_int)
846 break;
847 msg_advance(15);
848
849 // print any extra fields
850 command_end = tagp.command_end;
851 if (command_end != NULL)
852 {
853 p = command_end + 3;
854 while (*p && *p != '\r' && *p != '\n')
855 {
856 while (*p == TAB)
857 ++p;
858
859 // skip "file:" without a value (static tag)
860 if (STRNCMP(p, "file:", 5) == 0
861 && vim_isspace(p[5]))
862 {
863 p += 5;
864 continue;
865 }
866 // skip "kind:<kind>" and "<kind>"
867 if (p == tagp.tagkind
868 || (p + 5 == tagp.tagkind
869 && STRNCMP(p, "kind:", 5) == 0))
870 {
871 p = tagp.tagkind_end;
872 continue;
873 }
874 // print all other extra fields
875 attr = HL_ATTR(HLF_CM);
876 while (*p && *p != '\r' && *p != '\n')
877 {
878 if (msg_col + ptr2cells(p) >= Columns)
879 {
880 msg_putchar('\n');
881 if (got_int)
882 break;
883 msg_advance(15);
884 }
885 p = msg_outtrans_one(p, attr);
886 if (*p == TAB)
887 {
888 msg_puts_attr(" ", attr);
889 break;
890 }
891 if (*p == ':')
892 attr = 0;
893 }
894 }
895 if (msg_col > 15)
896 {
897 msg_putchar('\n');
898 if (got_int)
899 break;
900 msg_advance(15);
901 }
902 }
903 else
904 {
905 for (p = tagp.command;
906 *p && *p != '\r' && *p != '\n'; ++p)
907 ;
908 command_end = p;
909 }
910
911 // Put the info (in several lines) at column 15.
912 // Don't display "/^" and "?^".
913 p = tagp.command;
914 if (*p == '/' || *p == '?')
915 {
916 ++p;
917 if (*p == '^')
918 ++p;
919 }
920 // Remove leading whitespace from pattern
921 while (p != command_end && vim_isspace(*p))
922 ++p;
923
924 while (p != command_end)
925 {
926 if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns)
927 msg_putchar('\n');
928 if (got_int)
929 break;
930 msg_advance(15);
931
932 // skip backslash used for escaping a command char or
933 // a backslash
934 if (*p == '\\' && (*(p + 1) == *tagp.command
935 || *(p + 1) == '\\'))
936 ++p;
937
938 if (*p == TAB)
939 {
940 msg_putchar(' ');
941 ++p;
942 }
943 else
944 p = msg_outtrans_one(p, 0);
945
946 // don't display the "$/;\"" and "$?;\""
947 if (p == command_end - 2 && *p == '$'
948 && *(p + 1) == *tagp.command)
949 break;
950 // don't display matching '/' or '?'
951 if (p == command_end - 1 && *p == *tagp.command
952 && (*p == '/' || *p == '?'))
953 break;
954 }
955 if (msg_col)
956 msg_putchar('\n');
957 ui_breakcheck();
958 }
959 if (got_int)
960 got_int = FALSE; // only stop the listing
961 }
962
963 #if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL)
964 /*
965 * Add the matching tags to the location list for the current
966 * window.
967 */
968 static int
969 add_llist_tags(
970 char_u *tag,
971 int num_matches,
972 char_u **matches)
973 {
974 list_T *list;
975 char_u tag_name[128 + 1];
976 char_u *fname;
977 char_u *cmd;
978 int i;
979 char_u *p;
980 tagptrs_T tagp;
981
982 fname = alloc(MAXPATHL + 1);
983 cmd = alloc(CMDBUFFSIZE + 1);
984 list = list_alloc();
985 if (list == NULL || fname == NULL || cmd == NULL)
986 {
987 vim_free(cmd);
988 vim_free(fname);
989 if (list != NULL)
990 list_free(list);
991 return FAIL;
992 }
993
994 for (i = 0; i < num_matches; ++i)
995 {
996 int len, cmd_len;
997 long lnum;
998 dict_T *dict;
999
1000 parse_match(matches[i], &tagp);
1001
1002 /* Save the tag name */
1003 len = (int)(tagp.tagname_end - tagp.tagname);
1004 if (len > 128)
1005 len = 128;
1006 vim_strncpy(tag_name, tagp.tagname, len);
1007 tag_name[len] = NUL;
1008
1009 // Save the tag file name
1010 p = tag_full_fname(&tagp);
1011 if (p == NULL)
1012 continue;
1013 vim_strncpy(fname, p, MAXPATHL);
1014 vim_free(p);
1015
1016 // Get the line number or the search pattern used to locate
1017 // the tag.
1018 lnum = 0;
1019 if (isdigit(*tagp.command))
1020 // Line number is used to locate the tag
1021 lnum = atol((char *)tagp.command);
1022 else
1023 {
1024 char_u *cmd_start, *cmd_end;
1025
1026 // Search pattern is used to locate the tag
1027
1028 // Locate the end of the command
1029 cmd_start = tagp.command;
1030 cmd_end = tagp.command_end;
1031 if (cmd_end == NULL)
1032 {
1033 for (p = tagp.command;
1034 *p && *p != '\r' && *p != '\n'; ++p)
1035 ;
1036 cmd_end = p;
1037 }
1038
1039 // Now, cmd_end points to the character after the
1040 // command. Adjust it to point to the last
1041 // character of the command.
1042 cmd_end--;
1043
1044 // Skip the '/' and '?' characters at the
1045 // beginning and end of the search pattern.
1046 if (*cmd_start == '/' || *cmd_start == '?')
1047 cmd_start++;
1048
1049 if (*cmd_end == '/' || *cmd_end == '?')
1050 cmd_end--;
1051
1052 len = 0;
1053 cmd[0] = NUL;
1054
1055 // If "^" is present in the tag search pattern, then
1056 // copy it first.
1057 if (*cmd_start == '^')
1058 {
1059 STRCPY(cmd, "^");
1060 cmd_start++;
1061 len++;
1062 }
1063
1064 // Precede the tag pattern with \V to make it very
1065 // nomagic.
1066 STRCAT(cmd, "\\V");
1067 len += 2;
1068
1069 cmd_len = (int)(cmd_end - cmd_start + 1);
1070 if (cmd_len > (CMDBUFFSIZE - 5))
1071 cmd_len = CMDBUFFSIZE - 5;
1072 STRNCAT(cmd, cmd_start, cmd_len);
1073 len += cmd_len;
1074
1075 if (cmd[len - 1] == '$')
1076 {
1077 // Replace '$' at the end of the search pattern
1078 // with '\$'
1079 cmd[len - 1] = '\\';
1080 cmd[len] = '$';
1081 len++;
1082 }
1083
1084 cmd[len] = NUL;
1085 }
1086
1087 if ((dict = dict_alloc()) == NULL)
1088 continue;
1089 if (list_append_dict(list, dict) == FAIL)
1090 {
1091 vim_free(dict);
1092 continue;
1093 }
1094
1095 dict_add_string(dict, "text", tag_name);
1096 dict_add_string(dict, "filename", fname);
1097 dict_add_number(dict, "lnum", lnum);
1098 if (lnum == 0)
1099 dict_add_string(dict, "pattern", cmd);
1100 }
1101
1102 vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag);
1103 set_errorlist(curwin, list, ' ', IObuff, NULL);
1104
1105 list_free(list);
1106 vim_free(fname);
1107 vim_free(cmd);
1108
1109 return OK;
1110 }
1111 #endif
1090 1112
1091 /* 1113 /*
1092 * Free cached tags. 1114 * Free cached tags.
1093 */ 1115 */
1094 void 1116 void
3429 3451
3430 return retval; 3452 return retval;
3431 } 3453 }
3432 3454
3433 /* 3455 /*
3434 * Converts a file name into a canonical form. It simplifies a file name into
3435 * its simplest form by stripping out unneeded components, if any. The
3436 * resulting file name is simplified in place and will either be the same
3437 * length as that supplied, or shorter.
3438 */
3439 void
3440 simplify_filename(char_u *filename)
3441 {
3442 #ifndef AMIGA /* Amiga doesn't have "..", it uses "/" */
3443 int components = 0;
3444 char_u *p, *tail, *start;
3445 int stripping_disabled = FALSE;
3446 int relative = TRUE;
3447
3448 p = filename;
3449 #ifdef BACKSLASH_IN_FILENAME
3450 if (p[1] == ':') /* skip "x:" */
3451 p += 2;
3452 #endif
3453
3454 if (vim_ispathsep(*p))
3455 {
3456 relative = FALSE;
3457 do
3458 ++p;
3459 while (vim_ispathsep(*p));
3460 }
3461 start = p; /* remember start after "c:/" or "/" or "///" */
3462
3463 do
3464 {
3465 /* At this point "p" is pointing to the char following a single "/"
3466 * or "p" is at the "start" of the (absolute or relative) path name. */
3467 #ifdef VMS
3468 /* VMS allows device:[path] - don't strip the [ in directory */
3469 if ((*p == '[' || *p == '<') && p > filename && p[-1] == ':')
3470 {
3471 /* :[ or :< composition: vms directory component */
3472 ++components;
3473 p = getnextcomp(p + 1);
3474 }
3475 /* allow remote calls as host"user passwd"::device:[path] */
3476 else if (p[0] == ':' && p[1] == ':' && p > filename && p[-1] == '"' )
3477 {
3478 /* ":: composition: vms host/passwd component */
3479 ++components;
3480 p = getnextcomp(p + 2);
3481 }
3482 else
3483 #endif
3484 if (vim_ispathsep(*p))
3485 STRMOVE(p, p + 1); /* remove duplicate "/" */
3486 else if (p[0] == '.' && (vim_ispathsep(p[1]) || p[1] == NUL))
3487 {
3488 if (p == start && relative)
3489 p += 1 + (p[1] != NUL); /* keep single "." or leading "./" */
3490 else
3491 {
3492 /* Strip "./" or ".///". If we are at the end of the file name
3493 * and there is no trailing path separator, either strip "/." if
3494 * we are after "start", or strip "." if we are at the beginning
3495 * of an absolute path name . */
3496 tail = p + 1;
3497 if (p[1] != NUL)
3498 while (vim_ispathsep(*tail))
3499 MB_PTR_ADV(tail);
3500 else if (p > start)
3501 --p; /* strip preceding path separator */
3502 STRMOVE(p, tail);
3503 }
3504 }
3505 else if (p[0] == '.' && p[1] == '.' &&
3506 (vim_ispathsep(p[2]) || p[2] == NUL))
3507 {
3508 /* Skip to after ".." or "../" or "..///". */
3509 tail = p + 2;
3510 while (vim_ispathsep(*tail))
3511 MB_PTR_ADV(tail);
3512
3513 if (components > 0) /* strip one preceding component */
3514 {
3515 int do_strip = FALSE;
3516 char_u saved_char;
3517 stat_T st;
3518
3519 /* Don't strip for an erroneous file name. */
3520 if (!stripping_disabled)
3521 {
3522 /* If the preceding component does not exist in the file
3523 * system, we strip it. On Unix, we don't accept a symbolic
3524 * link that refers to a non-existent file. */
3525 saved_char = p[-1];
3526 p[-1] = NUL;
3527 #ifdef UNIX
3528 if (mch_lstat((char *)filename, &st) < 0)
3529 #else
3530 if (mch_stat((char *)filename, &st) < 0)
3531 #endif
3532 do_strip = TRUE;
3533 p[-1] = saved_char;
3534
3535 --p;
3536 /* Skip back to after previous '/'. */
3537 while (p > start && !after_pathsep(start, p))
3538 MB_PTR_BACK(start, p);
3539
3540 if (!do_strip)
3541 {
3542 /* If the component exists in the file system, check
3543 * that stripping it won't change the meaning of the
3544 * file name. First get information about the
3545 * unstripped file name. This may fail if the component
3546 * to strip is not a searchable directory (but a regular
3547 * file, for instance), since the trailing "/.." cannot
3548 * be applied then. We don't strip it then since we
3549 * don't want to replace an erroneous file name by
3550 * a valid one, and we disable stripping of later
3551 * components. */
3552 saved_char = *tail;
3553 *tail = NUL;
3554 if (mch_stat((char *)filename, &st) >= 0)
3555 do_strip = TRUE;
3556 else
3557 stripping_disabled = TRUE;
3558 *tail = saved_char;
3559 #ifdef UNIX
3560 if (do_strip)
3561 {
3562 stat_T new_st;
3563
3564 /* On Unix, the check for the unstripped file name
3565 * above works also for a symbolic link pointing to
3566 * a searchable directory. But then the parent of
3567 * the directory pointed to by the link must be the
3568 * same as the stripped file name. (The latter
3569 * exists in the file system since it is the
3570 * component's parent directory.) */
3571 if (p == start && relative)
3572 (void)mch_stat(".", &new_st);
3573 else
3574 {
3575 saved_char = *p;
3576 *p = NUL;
3577 (void)mch_stat((char *)filename, &new_st);
3578 *p = saved_char;
3579 }
3580
3581 if (new_st.st_ino != st.st_ino ||
3582 new_st.st_dev != st.st_dev)
3583 {
3584 do_strip = FALSE;
3585 /* We don't disable stripping of later
3586 * components since the unstripped path name is
3587 * still valid. */
3588 }
3589 }
3590 #endif
3591 }
3592 }
3593
3594 if (!do_strip)
3595 {
3596 /* Skip the ".." or "../" and reset the counter for the
3597 * components that might be stripped later on. */
3598 p = tail;
3599 components = 0;
3600 }
3601 else
3602 {
3603 /* Strip previous component. If the result would get empty
3604 * and there is no trailing path separator, leave a single
3605 * "." instead. If we are at the end of the file name and
3606 * there is no trailing path separator and a preceding
3607 * component is left after stripping, strip its trailing
3608 * path separator as well. */
3609 if (p == start && relative && tail[-1] == '.')
3610 {
3611 *p++ = '.';
3612 *p = NUL;
3613 }
3614 else
3615 {
3616 if (p > start && tail[-1] == '.')
3617 --p;
3618 STRMOVE(p, tail); /* strip previous component */
3619 }
3620
3621 --components;
3622 }
3623 }
3624 else if (p == start && !relative) /* leading "/.." or "/../" */
3625 STRMOVE(p, tail); /* strip ".." or "../" */
3626 else
3627 {
3628 if (p == start + 2 && p[-2] == '.') /* leading "./../" */
3629 {
3630 STRMOVE(p - 2, p); /* strip leading "./" */
3631 tail -= 2;
3632 }
3633 p = tail; /* skip to char after ".." or "../" */
3634 }
3635 }
3636 else
3637 {
3638 ++components; /* simple path component */
3639 p = getnextcomp(p);
3640 }
3641 } while (*p != NUL);
3642 #endif /* !AMIGA */
3643 }
3644
3645 /*
3646 * Check if we have a tag for the buffer with name "buf_ffname". 3456 * Check if we have a tag for the buffer with name "buf_ffname".
3647 * This is a bit slow, because of the full path compare in fullpathcmp(). 3457 * This is a bit slow, because of the full path compare in fullpathcmp().
3648 * Return TRUE if tag for file "fname" if tag file "tag_fname" is for current 3458 * Return TRUE if tag for file "fname" if tag file "tag_fname" is for current
3649 * file. 3459 * file.
3650 */ 3460 */