comparison src/tag.c @ 28027:d59552ad3f36 v8.2.4538

patch 8.2.4538: the find_tags_in_file() function is too long Commit: https://github.com/vim/vim/commit/bf40e90dfeb1d3d0280077e65782beb3fee31c9f Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Thu Mar 10 18:36:54 2022 +0000 patch 8.2.4538: the find_tags_in_file() function is too long Problem: The find_tags_in_file() function is too long. Solution: Refactor into smaller functions. (Yegappan Lakshmanan, closes #9920)
author Bram Moolenaar <Bram@vim.org>
date Thu, 10 Mar 2022 19:45:02 +0100
parents c724906134a3
children 12a256140887
comparison
equal deleted inserted replaced
28026:09c8bfcf2761 28027:d59552ad3f36
35 char_u *tagkind_end; // end of tagkind 35 char_u *tagkind_end; // end of tagkind
36 char_u *user_data; // user_data string 36 char_u *user_data; // user_data string
37 char_u *user_data_end; // end of user_data 37 char_u *user_data_end; // end of user_data
38 linenr_T tagline; // "line:" value 38 linenr_T tagline; // "line:" value
39 } tagptrs_T; 39 } tagptrs_T;
40
41 /*
42 * Return values used when reading lines from a tags file.
43 */
44 typedef enum
45 {
46 TAGS_READ_SUCCESS = 1,
47 TAGS_READ_EOF,
48 TAGS_READ_IGNORE,
49 } tags_read_status_T;
50
51 /*
52 * States used during a tags search
53 */
54 typedef enum
55 {
56 TS_START, // at start of file
57 TS_LINEAR, // linear searching forward, till EOF
58 TS_BINARY, // binary searching
59 TS_SKIP_BACK, // skipping backwards
60 TS_STEP_FORWARD // stepping forwards
61 } tagsearch_state_T; // Current search state
62
63 /*
64 * Binary search file offsets in a tags file
65 */
66 typedef struct
67 {
68 off_T low_offset; // offset for first char of first line that
69 // could match
70 off_T high_offset; // offset of char after last line that could
71 // match
72 off_T curr_offset; // Current file offset in search range
73 off_T curr_offset_used; // curr_offset used when skipping back
74 off_T match_offset; // Where the binary search found a tag
75 int low_char; // first char at low_offset
76 int high_char; // first char at high_offset
77 } tagsearch_info_T;
78
79 /*
80 * Return values used when matching tags against a pattern.
81 */
82 typedef enum
83 {
84 TAG_MATCH_SUCCESS = 1,
85 TAG_MATCH_FAIL,
86 TAG_MATCH_STOP,
87 TAG_MATCH_NEXT
88 } tagmatch_status_T;
89
90 /*
91 * Arguments used for matching tags read from a tags file against a pattern.
92 */
93 typedef struct
94 {
95 int matchoff; // tag match offset
96 int match_re; // TRUE if the tag matches a regexp
97 int match_no_ic; // TRUE if the tag matches with case
98 int has_re; // regular expression used
99 int sortic; // tags file sorted ignoring case (foldcase)
100 int sort_error; // tags file not sorted
101 } findtags_match_args_T;
40 102
41 /* 103 /*
42 * The matching tags are first stored in one of the hash tables. In 104 * The matching tags are first stored in one of the hash tables. In
43 * which one depends on the priority of the match. 105 * which one depends on the priority of the match.
44 * ht_match[] is used to find duplicates, ga_match[] to keep them in sequence. 106 * ht_match[] is used to find duplicates, ga_match[] to keep them in sequence.
1575 #endif 1637 #endif
1576 1638
1577 /* 1639 /*
1578 * State information used during a tag search 1640 * State information used during a tag search
1579 */ 1641 */
1580 typedef struct { 1642 typedef struct
1643 {
1644 tagsearch_state_T state; // tag search state
1581 char_u *tag_fname; // name of the tag file 1645 char_u *tag_fname; // name of the tag file
1646 FILE *fp; // current tags file pointer
1582 pat_T orgpat; // holds unconverted pattern info 1647 pat_T orgpat; // holds unconverted pattern info
1583 int name_only; // get only tag names 1648 int flags; // flags used for tag search
1584 int get_searchpat; // used for 'showfulltag' 1649 int tag_file_sorted; // !_TAG_FILE_SORTED value
1650 int get_searchpat; // used for 'showfulltag'
1585 int help_only; // only search for help tags 1651 int help_only; // only search for help tags
1652 vimconv_T vimconv;
1586 #ifdef FEAT_MULTI_LANG 1653 #ifdef FEAT_MULTI_LANG
1654 char_u help_lang[3]; // lang of current tags file
1655 int help_pri; // help language priority
1587 char_u *help_lang_find; // lang to be found 1656 char_u *help_lang_find; // lang to be found
1588 int is_txt; // flag of file extension 1657 int is_txt; // flag of file extension
1589 #endif 1658 #endif
1590 int did_open; // did open a tag file 1659 int did_open; // did open a tag file
1591 int mincount; // MAXCOL: find all matches 1660 int mincount; // MAXCOL: find all matches
1592 // other: minimal number of matches 1661 // other: minimal number of matches
1593 int linear; // do a linear search 1662 int linear; // do a linear search
1594 char_u *lbuf; // line buffer 1663 char_u *lbuf; // line buffer
1595 int lbuf_size; // length of lbuf 1664 int lbuf_size; // length of lbuf
1596 #ifdef FEAT_EMACS_TAGS 1665 #ifdef FEAT_EMACS_TAGS
1666 int is_etag; // current file is emaces style
1597 char_u *ebuf; // additional buffer for etag fname 1667 char_u *ebuf; // additional buffer for etag fname
1598 #endif 1668 #endif
1599 int match_count; // number of matches found 1669 int match_count; // number of matches found
1600 garray_T ga_match[MT_COUNT]; // stores matches in sequence 1670 garray_T ga_match[MT_COUNT]; // stores matches in sequence
1601 hashtab_T ht_match[MT_COUNT]; // stores matches by key 1671 hashtab_T ht_match[MT_COUNT]; // stores matches by key
1613 int mincount) 1683 int mincount)
1614 { 1684 {
1615 int mtt; 1685 int mtt;
1616 1686
1617 st->tag_fname = alloc(MAXPATHL + 1); 1687 st->tag_fname = alloc(MAXPATHL + 1);
1688 st->fp = NULL;
1618 st->orgpat.pat = pat; 1689 st->orgpat.pat = pat;
1619 st->orgpat.len = (int)STRLEN(pat); 1690 st->orgpat.len = (int)STRLEN(pat);
1620 st->orgpat.regmatch.regprog = NULL; 1691 st->orgpat.regmatch.regprog = NULL;
1692 st->flags = flags;
1693 st->tag_file_sorted = NUL;
1621 st->help_only = (flags & TAG_HELP); 1694 st->help_only = (flags & TAG_HELP);
1622 st->name_only = (flags & TAG_NAMES);
1623 st->get_searchpat = FALSE; 1695 st->get_searchpat = FALSE;
1624 #ifdef FEAT_MULTI_LANG 1696 #ifdef FEAT_MULTI_LANG
1697 st->help_lang[0] = NUL;
1698 st->help_pri = 0;
1625 st->help_lang_find = NULL; 1699 st->help_lang_find = NULL;
1626 st->is_txt = FALSE; 1700 st->is_txt = FALSE;
1627 #endif 1701 #endif
1628 st->did_open = FALSE; 1702 st->did_open = FALSE;
1629 st->mincount = mincount; 1703 st->mincount = mincount;
1667 #endif 1741 #endif
1668 } 1742 }
1669 1743
1670 #ifdef FEAT_MULTI_LANG 1744 #ifdef FEAT_MULTI_LANG
1671 /* 1745 /*
1672 * Initialize the state for searching tags in a Vim help file. 1746 * Initialize the language and priority used for searching tags in a Vim help
1747 * file.
1673 * Returns TRUE to process the help file and FALSE to skip the file. 1748 * Returns TRUE to process the help file and FALSE to skip the file.
1674 */ 1749 */
1675 static int 1750 static int
1676 findtags_in_help_init( 1751 findtags_in_help_init(findtags_state_T *st)
1677 findtags_state_T *st,
1678 int flags,
1679 char_u *help_lang,
1680 int *help_pri)
1681 { 1752 {
1682 int i; 1753 int i;
1683 char_u *s; 1754 char_u *s;
1684 1755
1685 // Keep en if the file extension is .txt 1756 // Keep 'en' as the language if the file extension is '.txt'
1686 if (st->is_txt) 1757 if (st->is_txt)
1687 STRCPY(help_lang, "en"); 1758 STRCPY(st->help_lang, "en");
1688 else 1759 else
1689 { 1760 {
1690 // Prefer help tags according to 'helplang'. Put the 1761 // Prefer help tags according to 'helplang'. Put the two-letter
1691 // two-letter language name in help_lang[]. 1762 // language name in help_lang[].
1692 i = (int)STRLEN(st->tag_fname); 1763 i = (int)STRLEN(st->tag_fname);
1693 if (i > 3 && st->tag_fname[i - 3] == '-') 1764 if (i > 3 && st->tag_fname[i - 3] == '-')
1694 STRCPY(help_lang, st->tag_fname + i - 2); 1765 STRCPY(st->help_lang, st->tag_fname + i - 2);
1695 else 1766 else
1696 STRCPY(help_lang, "en"); 1767 STRCPY(st->help_lang, "en");
1697 } 1768 }
1698 // When searching for a specific language skip tags files 1769 // When searching for a specific language skip tags files for other
1699 // for other languages. 1770 // languages.
1700 if (st->help_lang_find != NULL 1771 if (st->help_lang_find != NULL
1701 && STRICMP(help_lang, st->help_lang_find) != 0) 1772 && STRICMP(st->help_lang, st->help_lang_find) != 0)
1702 return FALSE; 1773 return FALSE;
1703 1774
1704 // For CTRL-] in a help file prefer a match with the same 1775 // For CTRL-] in a help file prefer a match with the same language.
1705 // language. 1776 if ((st->flags & TAG_KEEP_LANG)
1706 if ((flags & TAG_KEEP_LANG)
1707 && st->help_lang_find == NULL 1777 && st->help_lang_find == NULL
1708 && curbuf->b_fname != NULL 1778 && curbuf->b_fname != NULL
1709 && (i = (int)STRLEN(curbuf->b_fname)) > 4 1779 && (i = (int)STRLEN(curbuf->b_fname)) > 4
1710 && curbuf->b_fname[i - 1] == 'x' 1780 && curbuf->b_fname[i - 1] == 'x'
1711 && curbuf->b_fname[i - 4] == '.' 1781 && curbuf->b_fname[i - 4] == '.'
1712 && STRNICMP(curbuf->b_fname + i - 3, help_lang, 2) == 0) 1782 && STRNICMP(curbuf->b_fname + i - 3, st->help_lang, 2) == 0)
1713 *help_pri = 0; 1783 st->help_pri = 0;
1714 else 1784 else
1715 { 1785 {
1716 *help_pri = 1; 1786 // search for the language in 'helplang'
1787 st->help_pri = 1;
1717 for (s = p_hlg; *s != NUL; ++s) 1788 for (s = p_hlg; *s != NUL; ++s)
1718 { 1789 {
1719 if (STRNICMP(s, help_lang, 2) == 0) 1790 if (STRNICMP(s, st->help_lang, 2) == 0)
1720 break; 1791 break;
1721 ++*help_pri; 1792 ++st->help_pri;
1722 if ((s = vim_strchr(s, ',')) == NULL) 1793 if ((s = vim_strchr(s, ',')) == NULL)
1723 break; 1794 break;
1724 } 1795 }
1725 if (s == NULL || *s == NUL) 1796 if (s == NULL || *s == NUL)
1726 { 1797 {
1727 // Language not in 'helplang': use last, prefer English, 1798 // Language not in 'helplang': use last, prefer English, unless
1728 // unless found already. 1799 // found already.
1729 ++*help_pri; 1800 ++st->help_pri;
1730 if (STRICMP(help_lang, "en") != 0) 1801 if (STRICMP(st->help_lang, "en") != 0)
1731 ++*help_pri; 1802 ++st->help_pri;
1732 } 1803 }
1733 } 1804 }
1734 1805
1735 return TRUE; 1806 return TRUE;
1736 } 1807 }
1737 #endif 1808 #endif
1738 1809
1739 #ifdef FEAT_EVAL 1810 #ifdef FEAT_EVAL
1740 /* 1811 /*
1741 * Use the 'tagfunc' (if configured and enabled) to get the tags. 1812 * Use the function set in 'tagfunc' (if configured and enabled) to get the
1813 * tags.
1742 * Return OK if at least 1 tag has been successfully found, NOTDONE if the 1814 * Return OK if at least 1 tag has been successfully found, NOTDONE if the
1743 * 'tagfunc' is not used or the 'tagfunc' returns v:null and FAIL otherwise. 1815 * 'tagfunc' is not used or the 'tagfunc' returns v:null and FAIL otherwise.
1744 */ 1816 */
1745 static int 1817 static int
1746 findtags_apply_tfu( 1818 findtags_apply_tfu(findtags_state_T *st, char_u *pat, char_u *buf_ffname)
1747 char_u *pat, 1819 {
1748 findtags_state_T *st, 1820 int use_tfu = ((st->flags & TAG_NO_TAGFUNC) == 0);
1749 int flags,
1750 char_u *buf_ffname)
1751 {
1752 int use_tfu = ((flags & TAG_NO_TAGFUNC) == 0);
1753 int retval; 1821 int retval;
1754 1822
1755 if (!use_tfu || tfu_in_use || *curbuf->b_p_tfu == NUL) 1823 if (!use_tfu || tfu_in_use || *curbuf->b_p_tfu == NUL)
1756 return NOTDONE; 1824 return NOTDONE;
1757 1825
1758 tfu_in_use = TRUE; 1826 tfu_in_use = TRUE;
1759 retval = find_tagfunc_tags(pat, st->ga_match, &st->match_count, 1827 retval = find_tagfunc_tags(pat, st->ga_match, &st->match_count,
1760 flags, buf_ffname); 1828 st->flags, buf_ffname);
1761 tfu_in_use = FALSE; 1829 tfu_in_use = FALSE;
1762 1830
1763 return retval; 1831 return retval;
1764 } 1832 }
1765 #endif 1833 #endif
1797 * The file name is followed by a ','. Remember etag file name in ebuf. 1865 * The file name is followed by a ','. Remember etag file name in ebuf.
1798 * Returns a FILE pointer to the tags file. If another tags file is included, 1866 * Returns a FILE pointer to the tags file. If another tags file is included,
1799 * then returns a pointer to the new tags file. The old file pointer is saved 1867 * then returns a pointer to the new tags file. The old file pointer is saved
1800 * in incstack. 1868 * in incstack.
1801 */ 1869 */
1802 static FILE * 1870 static void
1803 emacs_tags_new_filename(findtags_state_T *st, FILE *fp, int *is_etag) 1871 emacs_tags_new_filename(findtags_state_T *st)
1804 { 1872 {
1805 char_u *p; 1873 char_u *p;
1806 char_u *fullpath_ebuf; 1874 char_u *fullpath_ebuf;
1807 1875
1808 if (vim_fgets(st->ebuf, LSIZE, fp)) 1876 if (vim_fgets(st->ebuf, LSIZE, st->fp))
1809 return fp; 1877 return;
1810 1878
1811 for (p = st->ebuf; *p && *p != ','; p++) 1879 for (p = st->ebuf; *p && *p != ','; p++)
1812 ; 1880 ;
1813 *p = NUL; 1881 *p = NUL;
1814 1882
1815 // check for an included tags file. 1883 // check for an included tags file.
1816 // atoi(p+1) is the number of bytes before the next ^L unless it is an 1884 // atoi(p+1) is the number of bytes before the next ^L unless it is an
1817 // include statement. Skip the included tags file if it exceeds the 1885 // include statement. Skip the included tags file if it exceeds the
1818 // maximum. 1886 // maximum.
1819 if (STRNCMP(p + 1, "include", 7) != 0 || incstack_idx >= INCSTACK_SIZE) 1887 if (STRNCMP(p + 1, "include", 7) != 0 || incstack_idx >= INCSTACK_SIZE)
1820 return fp; 1888 return;
1821 1889
1822 // Save current "fp" and "tag_fname" in the stack. 1890 // Save current "fp" and "tag_fname" in the stack.
1823 incstack[incstack_idx].etag_fname = vim_strsave(st->tag_fname); 1891 incstack[incstack_idx].etag_fname = vim_strsave(st->tag_fname);
1824 if (incstack[incstack_idx].etag_fname == NULL) 1892 if (incstack[incstack_idx].etag_fname == NULL)
1825 return fp; 1893 return;
1826 1894
1827 incstack[incstack_idx].fp = fp; 1895 incstack[incstack_idx].fp = st->fp;
1828 fp = NULL; 1896 st->fp = NULL;
1829 1897
1830 // Figure out "tag_fname" and "fp" to use for 1898 // Figure out "tag_fname" and "fp" to use for
1831 // included file. 1899 // included file.
1832 fullpath_ebuf = expand_tag_fname(st->ebuf, st->tag_fname, FALSE); 1900 fullpath_ebuf = expand_tag_fname(st->ebuf, st->tag_fname, FALSE);
1833 if (fullpath_ebuf != NULL) 1901 if (fullpath_ebuf != NULL)
1834 { 1902 {
1835 fp = mch_fopen((char *)fullpath_ebuf, "r"); 1903 st->fp = mch_fopen((char *)fullpath_ebuf, "r");
1836 if (fp != NULL) 1904 if (st->fp != NULL)
1837 { 1905 {
1838 if (STRLEN(fullpath_ebuf) > LSIZE) 1906 if (STRLEN(fullpath_ebuf) > LSIZE)
1839 semsg(_(e_tag_file_path_truncated_for_str), st->ebuf); 1907 semsg(_(e_tag_file_path_truncated_for_str), st->ebuf);
1840 vim_strncpy(st->tag_fname, fullpath_ebuf, MAXPATHL); 1908 vim_strncpy(st->tag_fname, fullpath_ebuf, MAXPATHL);
1841 ++incstack_idx; 1909 ++incstack_idx;
1842 *is_etag = 0; // we can include anything 1910 st->is_etag = FALSE; // we can include anything
1843 } 1911 }
1844 vim_free(fullpath_ebuf); 1912 vim_free(fullpath_ebuf);
1845 } 1913 }
1846 if (fp == NULL) 1914 if (st->fp == NULL)
1847 { 1915 {
1848 // Can't open the included file, skip it and 1916 // Can't open the included file, skip it and
1849 // restore old value of "fp". 1917 // restore old value of "fp".
1850 fp = incstack[incstack_idx].fp; 1918 st->fp = incstack[incstack_idx].fp;
1851 vim_free(incstack[incstack_idx].etag_fname); 1919 vim_free(incstack[incstack_idx].etag_fname);
1852 } 1920 }
1853 1921
1854 return fp; 1922 return;
1855 } 1923 }
1856 1924
1857 /* 1925 /*
1858 * Reached the end of an emacs-style tags file. If this is an included tags 1926 * Reached the end of an emacs-style tags file. If this is an included tags
1859 * file, then pop it from the incstack and continue processing the parent tags 1927 * file, then pop it from the incstack and continue processing the parent tags
1860 * file. Otherwise, processed all the tags. 1928 * file. Otherwise, processed all the tags.
1861 * Returns TRUE if an included tags file is popped and processing should 1929 * Returns TRUE if an included tags file is popped and processing should
1862 * continue with the parent tags file. Otherwise returns FALSE. 1930 * continue with the parent tags file. Otherwise returns FALSE.
1863 */ 1931 */
1864 static int 1932 static int
1865 emacs_tags_file_eof(findtags_state_T *st, FILE **fp) 1933 emacs_tags_file_eof(findtags_state_T *st)
1866 { 1934 {
1867 if (!incstack_idx) // reached end of file. stop processing. 1935 if (!incstack_idx) // reached end of file. stop processing.
1868 return FALSE; 1936 return FALSE;
1869 1937
1870 // reached the end of an included tags file. pop it. 1938 // reached the end of an included tags file. pop it.
1871 --incstack_idx; 1939 --incstack_idx;
1872 fclose(*fp); // end of this file ... 1940 fclose(st->fp); // end of this file ...
1873 *fp = incstack[incstack_idx].fp; 1941 st->fp = incstack[incstack_idx].fp;
1874 STRCPY(st->tag_fname, incstack[incstack_idx].etag_fname); 1942 STRCPY(st->tag_fname, incstack[incstack_idx].etag_fname);
1875 vim_free(incstack[incstack_idx].etag_fname); 1943 vim_free(incstack[incstack_idx].etag_fname);
1876 1944
1877 return TRUE; 1945 return TRUE;
1878 } 1946 }
1941 return OK; 2009 return OK;
1942 } 2010 }
1943 #endif 2011 #endif
1944 2012
1945 /* 2013 /*
2014 * Read the next line from a tags file.
2015 * Returns TAGS_READ_SUCCESS if a tags line is successfully read and should be
2016 * processed.
2017 * Returns TAGS_READ_EOF if the end of file is reached.
2018 * Returns TAGS_READ_IGNORE if the current line should be ignored (used when
2019 * reached end of a emacs included tags file)
2020 */
2021 static tags_read_status_T
2022 findtags_get_next_line(findtags_state_T *st, tagsearch_info_T *sinfo_p)
2023 {
2024 int eof;
2025 off_T offset;
2026
2027 // For binary search: compute the next offset to use.
2028 if (st->state == TS_BINARY)
2029 {
2030 offset = sinfo_p->low_offset + ((sinfo_p->high_offset
2031 - sinfo_p->low_offset) / 2);
2032 if (offset == sinfo_p->curr_offset)
2033 return TAGS_READ_EOF; // End the binary search without a match.
2034 else
2035 sinfo_p->curr_offset = offset;
2036 }
2037
2038 // Skipping back (after a match during binary search).
2039 else if (st->state == TS_SKIP_BACK)
2040 {
2041 sinfo_p->curr_offset -= st->lbuf_size * 2;
2042 if (sinfo_p->curr_offset < 0)
2043 {
2044 sinfo_p->curr_offset = 0;
2045 rewind(st->fp);
2046 st->state = TS_STEP_FORWARD;
2047 }
2048 }
2049
2050 // When jumping around in the file, first read a line to find the
2051 // start of the next line.
2052 if (st->state == TS_BINARY || st->state == TS_SKIP_BACK)
2053 {
2054 // Adjust the search file offset to the correct position
2055 sinfo_p->curr_offset_used = sinfo_p->curr_offset;
2056 vim_fseek(st->fp, sinfo_p->curr_offset, SEEK_SET);
2057 eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
2058 if (!eof && sinfo_p->curr_offset != 0)
2059 {
2060 sinfo_p->curr_offset = vim_ftell(st->fp);
2061 if (sinfo_p->curr_offset == sinfo_p->high_offset)
2062 {
2063 // oops, gone a bit too far; try from low offset
2064 vim_fseek(st->fp, sinfo_p->low_offset, SEEK_SET);
2065 sinfo_p->curr_offset = sinfo_p->low_offset;
2066 }
2067 eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
2068 }
2069 // skip empty and blank lines
2070 while (!eof && vim_isblankline(st->lbuf))
2071 {
2072 sinfo_p->curr_offset = vim_ftell(st->fp);
2073 eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
2074 }
2075 if (eof)
2076 {
2077 // Hit end of file. Skip backwards.
2078 st->state = TS_SKIP_BACK;
2079 sinfo_p->match_offset = vim_ftell(st->fp);
2080 sinfo_p->curr_offset = sinfo_p->curr_offset_used;
2081 return TAGS_READ_IGNORE;
2082 }
2083 }
2084 // Not jumping around in the file: Read the next line.
2085 else
2086 {
2087 // skip empty and blank lines
2088 do
2089 {
2090 #ifdef FEAT_CSCOPE
2091 if (st->flags & TAG_CSCOPE)
2092 eof = cs_fgets(st->lbuf, st->lbuf_size);
2093 else
2094 #endif
2095 {
2096 sinfo_p->curr_offset = vim_ftell(st->fp);
2097 eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
2098 }
2099 } while (!eof && vim_isblankline(st->lbuf));
2100
2101 if (eof)
2102 {
2103 #ifdef FEAT_EMACS_TAGS
2104 if (emacs_tags_file_eof(st) == TRUE)
2105 {
2106 // an included tags file. Continue processing the parent
2107 // tags file.
2108 st->is_etag = TRUE; // (only etags can include)
2109 return TAGS_READ_IGNORE;
2110 }
2111 #endif
2112 return TAGS_READ_EOF;
2113 }
2114 }
2115
2116 return TAGS_READ_SUCCESS;
2117 }
2118
2119 /*
1946 * Parse a tags file header line in 'st->lbuf'. 2120 * Parse a tags file header line in 'st->lbuf'.
1947 * Returns TRUE to read the next header line and FALSE to process the line. 2121 * Returns TRUE if the current line in st->lbuf is not a tags header line and
2122 * should be parsed as a regular tag line. Returns FALSE if the line is a
2123 * header line and the next header line should be read.
1948 */ 2124 */
1949 static int 2125 static int
1950 tags_file_hdr_parse(findtags_state_T *st, vimconv_T *vcp, int *sorted_file) 2126 findtags_hdr_parse(findtags_state_T *st)
1951 { 2127 {
1952 char_u *p; 2128 char_u *p;
1953 2129
2130 // Header lines in a tags file start with "!_TAG_"
1954 if (STRNCMP(st->lbuf, "!_TAG_", 6) != 0) 2131 if (STRNCMP(st->lbuf, "!_TAG_", 6) != 0)
1955 // Non-header item before the header, e.g. "!" itself. 2132 // Non-header item before the header, e.g. "!" itself.
1956 return FALSE; 2133 return TRUE;
1957 2134
1958 // Read header line. 2135 // Process the header line.
1959 if (STRNCMP(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) 2136 if (STRNCMP(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0)
1960 *sorted_file = st->lbuf[18]; 2137 st->tag_file_sorted = st->lbuf[18];
1961 if (STRNCMP(st->lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0) 2138 if (STRNCMP(st->lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0)
1962 { 2139 {
1963 // Prepare to convert every line from the specified 2140 // Prepare to convert every line from the specified encoding to
1964 // encoding to 'encoding'. 2141 // 'encoding'.
1965 for (p = st->lbuf + 20; *p > ' ' && *p < 127; ++p) 2142 for (p = st->lbuf + 20; *p > ' ' && *p < 127; ++p)
1966 ; 2143 ;
1967 *p = NUL; 2144 *p = NUL;
1968 convert_setup(vcp, st->lbuf + 20, p_enc); 2145 convert_setup(&st->vimconv, st->lbuf + 20, p_enc);
1969 } 2146 }
1970 2147
1971 // Read the next line. Unrecognized flags are ignored. 2148 // Read the next line. Unrecognized flags are ignored.
2149 return FALSE;
2150 }
2151
2152 /*
2153 * Handler to initialize the state when starting to process a new tags file.
2154 * Called in the TS_START state when finding tags from a tags file.
2155 * Returns TRUE if the line read from the tags file should be parsed and
2156 * FALSE if the line should be ignored.
2157 */
2158 static int
2159 findtags_start_state_handler(
2160 findtags_state_T *st,
2161 int *sortic,
2162 tagsearch_info_T *sinfo_p)
2163 {
2164 #ifdef FEAT_CSCOPE
2165 int use_cscope = (st->flags & TAG_CSCOPE);
2166 #endif
2167 int noic = (st->flags & TAG_NOIC);
2168 off_T filesize;
2169
2170 // The header ends when the line sorts below "!_TAG_". When
2171 // case is folded lower case letters sort before "_".
2172 if (STRNCMP(st->lbuf, "!_TAG_", 6) <= 0
2173 || (st->lbuf[0] == '!' && ASCII_ISLOWER(st->lbuf[1])))
2174 return findtags_hdr_parse(st);
2175
2176 // Headers ends.
2177
2178 // When there is no tag head, or ignoring case, need to do a
2179 // linear search.
2180 // When no "!_TAG_" is found, default to binary search. If
2181 // the tag file isn't sorted, the second loop will find it.
2182 // When "!_TAG_FILE_SORTED" found: start binary search if
2183 // flag set.
2184 // For cscope, it's always linear.
2185 # ifdef FEAT_CSCOPE
2186 if (st->linear || use_cscope)
2187 # else
2188 if (st->linear)
2189 # endif
2190 st->state = TS_LINEAR;
2191 else if (st->tag_file_sorted == NUL)
2192 st->state = TS_BINARY;
2193 else if (st->tag_file_sorted == '1')
2194 st->state = TS_BINARY;
2195 else if (st->tag_file_sorted == '2')
2196 {
2197 st->state = TS_BINARY;
2198 *sortic = TRUE;
2199 st->orgpat.regmatch.rm_ic = (p_ic || !noic);
2200 }
2201 else
2202 st->state = TS_LINEAR;
2203
2204 if (st->state == TS_BINARY && st->orgpat.regmatch.rm_ic && !*sortic)
2205 {
2206 // Binary search won't work for ignoring case, use linear
2207 // search.
2208 st->linear = TRUE;
2209 st->state = TS_LINEAR;
2210 }
2211
2212 // When starting a binary search, get the size of the file and
2213 // compute the first offset.
2214 if (st->state == TS_BINARY)
2215 {
2216 if (vim_fseek(st->fp, 0L, SEEK_END) != 0)
2217 // can't seek, don't use binary search
2218 st->state = TS_LINEAR;
2219 else
2220 {
2221 // Get the tag file size (don't use mch_fstat(), it's
2222 // not portable). Don't use lseek(), it doesn't work
2223 // properly on MacOS Catalina.
2224 filesize = vim_ftell(st->fp);
2225 vim_fseek(st->fp, 0L, SEEK_SET);
2226
2227 // Calculate the first read offset in the file. Start
2228 // the search in the middle of the file.
2229 sinfo_p->low_offset = 0;
2230 sinfo_p->low_char = 0;
2231 sinfo_p->high_offset = filesize;
2232 sinfo_p->curr_offset = 0;
2233 sinfo_p->high_char = 0xff;
2234 }
2235 return FALSE;
2236 }
2237
1972 return TRUE; 2238 return TRUE;
2239 }
2240
2241 /*
2242 * Parse a tag line read from a tags file.
2243 * Returns OK if a tags line is successfully parsed.
2244 * Returns FAIL if an error is encountered.
2245 */
2246 static int
2247 findtags_parse_line(findtags_state_T *st, tagptrs_T *tagpp)
2248 {
2249 int status;
2250
2251 // Figure out where the different strings are in this line.
2252 // For "normal" tags: Do a quick check if the tag matches.
2253 // This speeds up tag searching a lot!
2254 if (st->orgpat.headlen
2255 #ifdef FEAT_EMACS_TAGS
2256 && !st->is_etag
2257 #endif
2258 )
2259 {
2260 CLEAR_FIELD(*tagpp);
2261 tagpp->tagname = st->lbuf;
2262 tagpp->tagname_end = vim_strchr(st->lbuf, TAB);
2263 if (tagpp->tagname_end == NULL)
2264 {
2265 // Corrupted tag line.
2266 return FAIL;
2267 }
2268
2269 // Can be a matching tag, isolate the file name and command.
2270 tagpp->fname = tagpp->tagname_end + 1;
2271 tagpp->fname_end = vim_strchr(tagpp->fname, TAB);
2272 if (tagpp->fname_end == NULL)
2273 status = FAIL;
2274 else
2275 {
2276 tagpp->command = tagpp->fname_end + 1;
2277 status = OK;
2278 }
2279 }
2280 else
2281 status = parse_tag_line(st->lbuf,
2282 #ifdef FEAT_EMACS_TAGS
2283 st->is_etag,
2284 #endif
2285 tagpp);
2286
2287 if (status == FAIL)
2288 return FAIL;
2289
2290 #ifdef FEAT_EMACS_TAGS
2291 if (st->is_etag)
2292 tagpp->fname = st->ebuf;
2293 #endif
2294
2295 return OK;
2296 }
2297
2298 /*
2299 * Initialize the structure used for tag matching.
2300 */
2301 static void
2302 findtags_matchargs_init(findtags_match_args_T *margs, int flags)
2303 {
2304 margs->matchoff = 0; // match offset
2305 margs->match_re = FALSE; // match with regexp
2306 margs->match_no_ic = FALSE; // matches with case
2307 margs->has_re = (flags & TAG_REGEXP); // regexp used
2308 margs->sortic = FALSE; // tag file sorted in nocase
2309 margs->sort_error = FALSE; // tags file not sorted
2310 }
2311
2312 /*
2313 * Compares the tag name in 'tagpp->tagname' with a search pattern in
2314 * 'st->orgpat.head'.
2315 * Returns TAG_MATCH_SUCCESS if the tag matches, TAG_MATCH_FAIL if the tag
2316 * doesn't match, TAG_MATCH_NEXT to look for the next matching tag (used in a
2317 * binary search) and TAG_MATCH_STOP if all the tags are processed without a
2318 * match. Uses the values in 'margs' for doing the comparison.
2319 */
2320 static tagmatch_status_T
2321 findtags_match_tag(
2322 findtags_state_T *st,
2323 tagptrs_T *tagpp,
2324 findtags_match_args_T *margs,
2325 tagsearch_info_T *sinfo_p)
2326 {
2327 int match = FALSE;
2328 int cmplen;
2329 int i;
2330 int tagcmp;
2331
2332 // Skip this line if the length of the tag is different and
2333 // there is no regexp, or the tag is too short.
2334 if (st->orgpat.headlen
2335 #ifdef FEAT_EMACS_TAGS
2336 && !st->is_etag
2337 #endif
2338 )
2339 {
2340 cmplen = (int)(tagpp->tagname_end - tagpp->tagname);
2341 if (p_tl != 0 && cmplen > p_tl) // adjust for 'taglength'
2342 cmplen = p_tl;
2343 if (margs->has_re && st->orgpat.headlen < cmplen)
2344 cmplen = st->orgpat.headlen;
2345 else if (st->state == TS_LINEAR && st->orgpat.headlen != cmplen)
2346 return TAG_MATCH_FAIL;
2347
2348 if (st->state == TS_BINARY)
2349 {
2350 // Simplistic check for unsorted tags file.
2351 i = (int)tagpp->tagname[0];
2352 if (margs->sortic)
2353 i = (int)TOUPPER_ASC(tagpp->tagname[0]);
2354 if (i < sinfo_p->low_char || i > sinfo_p->high_char)
2355 margs->sort_error = TRUE;
2356
2357 // Compare the current tag with the searched tag.
2358 if (margs->sortic)
2359 tagcmp = tag_strnicmp(tagpp->tagname, st->orgpat.head,
2360 (size_t)cmplen);
2361 else
2362 tagcmp = STRNCMP(tagpp->tagname, st->orgpat.head, cmplen);
2363
2364 // A match with a shorter tag means to search forward.
2365 // A match with a longer tag means to search backward.
2366 if (tagcmp == 0)
2367 {
2368 if (cmplen < st->orgpat.headlen)
2369 tagcmp = -1;
2370 else if (cmplen > st->orgpat.headlen)
2371 tagcmp = 1;
2372 }
2373
2374 if (tagcmp == 0)
2375 {
2376 // We've located the tag, now skip back and search
2377 // forward until the first matching tag is found.
2378 st->state = TS_SKIP_BACK;
2379 sinfo_p->match_offset = sinfo_p->curr_offset;
2380 return TAG_MATCH_NEXT;
2381 }
2382 if (tagcmp < 0)
2383 {
2384 sinfo_p->curr_offset = vim_ftell(st->fp);
2385 if (sinfo_p->curr_offset < sinfo_p->high_offset)
2386 {
2387 sinfo_p->low_offset = sinfo_p->curr_offset;
2388 if (margs->sortic)
2389 sinfo_p->low_char = TOUPPER_ASC(tagpp->tagname[0]);
2390 else
2391 sinfo_p->low_char = tagpp->tagname[0];
2392 return TAG_MATCH_NEXT;
2393 }
2394 }
2395 if (tagcmp > 0 && sinfo_p->curr_offset != sinfo_p->high_offset)
2396 {
2397 sinfo_p->high_offset = sinfo_p->curr_offset;
2398 if (margs->sortic)
2399 sinfo_p->high_char = TOUPPER_ASC(tagpp->tagname[0]);
2400 else
2401 sinfo_p->high_char = tagpp->tagname[0];
2402 return TAG_MATCH_NEXT;
2403 }
2404
2405 // No match yet and are at the end of the binary search.
2406 return TAG_MATCH_STOP;
2407 }
2408 else if (st->state == TS_SKIP_BACK)
2409 {
2410 if (MB_STRNICMP(tagpp->tagname, st->orgpat.head, cmplen) != 0)
2411 st->state = TS_STEP_FORWARD;
2412 else
2413 // Have to skip back more. Restore the curr_offset
2414 // used, otherwise we get stuck at a long line.
2415 sinfo_p->curr_offset = sinfo_p->curr_offset_used;
2416 return TAG_MATCH_NEXT;
2417 }
2418 else if (st->state == TS_STEP_FORWARD)
2419 {
2420 if (MB_STRNICMP(tagpp->tagname, st->orgpat.head, cmplen) != 0)
2421 {
2422 if ((off_T)vim_ftell(st->fp) > sinfo_p->match_offset)
2423 return TAG_MATCH_STOP; // past last match
2424 else
2425 return TAG_MATCH_NEXT; // before first match
2426 }
2427 }
2428 else
2429 // skip this match if it can't match
2430 if (MB_STRNICMP(tagpp->tagname, st->orgpat.head, cmplen) != 0)
2431 return TAG_MATCH_FAIL;
2432 }
2433
2434 // First try matching with the pattern literally (also when it is
2435 // a regexp).
2436 cmplen = (int)(tagpp->tagname_end - tagpp->tagname);
2437 if (p_tl != 0 && cmplen > p_tl) // adjust for 'taglength'
2438 cmplen = p_tl;
2439 // if tag length does not match, don't try comparing
2440 if (st->orgpat.len != cmplen)
2441 match = FALSE;
2442 else
2443 {
2444 if (st->orgpat.regmatch.rm_ic)
2445 {
2446 match =
2447 (MB_STRNICMP(tagpp->tagname, st->orgpat.pat, cmplen) == 0);
2448 if (match)
2449 margs->match_no_ic =
2450 (STRNCMP(tagpp->tagname, st->orgpat.pat, cmplen) == 0);
2451 }
2452 else
2453 match = (STRNCMP(tagpp->tagname, st->orgpat.pat, cmplen) == 0);
2454 }
2455
2456 // Has a regexp: Also find tags matching regexp.
2457 margs->match_re = FALSE;
2458 if (!match && st->orgpat.regmatch.regprog != NULL)
2459 {
2460 int cc;
2461
2462 cc = *tagpp->tagname_end;
2463 *tagpp->tagname_end = NUL;
2464 match = vim_regexec(&st->orgpat.regmatch, tagpp->tagname, (colnr_T)0);
2465 if (match)
2466 {
2467 margs->matchoff = (int)(st->orgpat.regmatch.startp[0] -
2468 tagpp->tagname);
2469 if (st->orgpat.regmatch.rm_ic)
2470 {
2471 st->orgpat.regmatch.rm_ic = FALSE;
2472 margs->match_no_ic = vim_regexec(&st->orgpat.regmatch,
2473 tagpp->tagname, (colnr_T)0);
2474 st->orgpat.regmatch.rm_ic = TRUE;
2475 }
2476 }
2477 *tagpp->tagname_end = cc;
2478 margs->match_re = TRUE;
2479 }
2480
2481 return match ? TAG_MATCH_SUCCESS : TAG_MATCH_FAIL;
1973 } 2482 }
1974 2483
1975 /* 2484 /*
1976 * Convert the encoding of a line read from a tags file in 'st->lbuf'. 2485 * Convert the encoding of a line read from a tags file in 'st->lbuf'.
1977 * Converting the pattern from 'enc' to the tags file encoding doesn't work, 2486 * Converting the pattern from 'enc' to the tags file encoding doesn't work,
1978 * because characters are not recognized. 2487 * because characters are not recognized. The converted line is saved in
2488 * st->lbuf.
1979 */ 2489 */
1980 static void 2490 static void
1981 findtags_string_convert(findtags_state_T *st, vimconv_T *vcp) 2491 findtags_string_convert(findtags_state_T *st)
1982 { 2492 {
1983 char_u *conv_line; 2493 char_u *conv_line;
1984 int len; 2494 int len;
1985 2495
1986 conv_line = string_convert(vcp, st->lbuf, NULL); 2496 conv_line = string_convert(&st->vimconv, st->lbuf, NULL);
1987 if (conv_line == NULL) 2497 if (conv_line == NULL)
1988 return; 2498 return;
1989 2499
1990 // Copy or swap lbuf and conv_line. 2500 // Copy or swap lbuf and conv_line.
1991 len = (int)STRLEN(conv_line) + 1; 2501 len = (int)STRLEN(conv_line) + 1;
2000 STRCPY(st->lbuf, conv_line); 2510 STRCPY(st->lbuf, conv_line);
2001 vim_free(conv_line); 2511 vim_free(conv_line);
2002 } 2512 }
2003 } 2513 }
2004 2514
2515 /*
2516 * Add a matching tag found in a tags file to st->ht_match and st->ga_match.
2517 * Returns OK if successfully added the match and FAIL on memory allocation
2518 * failure.
2519 */
2005 static int 2520 static int
2006 findtags_add_match( 2521 findtags_add_match(
2007 findtags_state_T *st, 2522 findtags_state_T *st,
2008 tagptrs_T *tagp, 2523 tagptrs_T *tagpp,
2524 findtags_match_args_T *margs,
2009 char_u *buf_ffname, 2525 char_u *buf_ffname,
2010 int flags UNUSED, 2526 hash_T *hash)
2011 hash_T *hash,
2012 int match_re,
2013 int match_no_ic,
2014 int matchoff,
2015 int is_etag UNUSED,
2016 char_u *help_lang UNUSED,
2017 int help_pri UNUSED)
2018 { 2527 {
2019 #ifdef FEAT_CSCOPE 2528 #ifdef FEAT_CSCOPE
2020 int use_cscope = (flags & TAG_CSCOPE); 2529 int use_cscope = (st->flags & TAG_CSCOPE);
2021 #endif 2530 #endif
2531 int name_only = (st->flags & TAG_NAMES);
2022 int mtt; 2532 int mtt;
2023 int len = 0; 2533 int len = 0;
2024 int is_current; // file name matches 2534 int is_current; // file name matches
2025 int is_static; // current tag line is static 2535 int is_static; // current tag line is static
2026 char_u *mfp; 2536 char_u *mfp;
2037 #endif 2547 #endif
2038 { 2548 {
2039 // Decide in which array to store this match. 2549 // Decide in which array to store this match.
2040 is_current = test_for_current( 2550 is_current = test_for_current(
2041 #ifdef FEAT_EMACS_TAGS 2551 #ifdef FEAT_EMACS_TAGS
2042 is_etag, 2552 st->is_etag,
2043 #endif 2553 #endif
2044 tagp->fname, tagp->fname_end, st->tag_fname, 2554 tagpp->fname, tagpp->fname_end, st->tag_fname, buf_ffname);
2045 buf_ffname);
2046 #ifdef FEAT_EMACS_TAGS 2555 #ifdef FEAT_EMACS_TAGS
2047 is_static = FALSE; 2556 is_static = FALSE;
2048 if (!is_etag) // emacs tags are never static 2557 if (!st->is_etag) // emacs tags are never static
2049 #endif 2558 #endif
2050 is_static = test_for_static(tagp); 2559 is_static = test_for_static(tagpp);
2051 2560
2052 // decide in which of the sixteen tables to store this 2561 // decide in which of the sixteen tables to store this
2053 // match 2562 // match
2054 if (is_static) 2563 if (is_static)
2055 { 2564 {
2063 if (is_current) 2572 if (is_current)
2064 mtt = MT_GL_CUR; 2573 mtt = MT_GL_CUR;
2065 else 2574 else
2066 mtt = MT_GL_OTH; 2575 mtt = MT_GL_OTH;
2067 } 2576 }
2068 if (st->orgpat.regmatch.rm_ic && !match_no_ic) 2577 if (st->orgpat.regmatch.rm_ic && !margs->match_no_ic)
2069 mtt += MT_IC_OFF; 2578 mtt += MT_IC_OFF;
2070 if (match_re) 2579 if (margs->match_re)
2071 mtt += MT_RE_OFF; 2580 mtt += MT_RE_OFF;
2072 } 2581 }
2073 2582
2074 // Add the found match in ht_match[mtt] and ga_match[mtt]. 2583 // Add the found match in ht_match[mtt] and ga_match[mtt].
2075 // Store the info we need later, which depends on the kind of 2584 // Store the info we need later, which depends on the kind of
2083 #endif 2592 #endif
2084 // Append the help-heuristic number after the tagname, for 2593 // Append the help-heuristic number after the tagname, for
2085 // sorting it later. The heuristic is ignored for 2594 // sorting it later. The heuristic is ignored for
2086 // detecting duplicates. 2595 // detecting duplicates.
2087 // The format is {tagname}@{lang}NUL{heuristic}NUL 2596 // The format is {tagname}@{lang}NUL{heuristic}NUL
2088 *tagp->tagname_end = NUL; 2597 *tagpp->tagname_end = NUL;
2089 len = (int)(tagp->tagname_end - tagp->tagname); 2598 len = (int)(tagpp->tagname_end - tagpp->tagname);
2090 mfp = alloc(sizeof(char_u) + len + 10 + ML_EXTRA + 1); 2599 mfp = alloc(sizeof(char_u) + len + 10 + ML_EXTRA + 1);
2091 if (mfp != NULL) 2600 if (mfp != NULL)
2092 { 2601 {
2093 int heuristic; 2602 int heuristic;
2094 2603
2095 p = mfp; 2604 p = mfp;
2096 STRCPY(p, tagp->tagname); 2605 STRCPY(p, tagpp->tagname);
2097 #ifdef FEAT_MULTI_LANG 2606 #ifdef FEAT_MULTI_LANG
2098 p[len] = '@'; 2607 p[len] = '@';
2099 STRCPY(p + len + 1, help_lang); 2608 STRCPY(p + len + 1, st->help_lang);
2100 #endif 2609 #endif
2101 2610
2102 heuristic = help_heuristic(tagp->tagname, 2611 heuristic = help_heuristic(tagpp->tagname,
2103 match_re ? matchoff : 0, !match_no_ic); 2612 margs->match_re ? margs->matchoff : 0,
2613 !margs->match_no_ic);
2104 #ifdef FEAT_MULTI_LANG 2614 #ifdef FEAT_MULTI_LANG
2105 heuristic += help_pri; 2615 heuristic += st->help_pri;
2106 #endif 2616 #endif
2107 sprintf((char *)p + len + 1 + ML_EXTRA, "%06d", 2617 sprintf((char *)p + len + 1 + ML_EXTRA, "%06d",
2108 heuristic); 2618 heuristic);
2109 } 2619 }
2110 *tagp->tagname_end = TAB; 2620 *tagpp->tagname_end = TAB;
2111 } 2621 }
2112 else if (st->name_only) 2622 else if (name_only)
2113 { 2623 {
2114 if (st->get_searchpat) 2624 if (st->get_searchpat)
2115 { 2625 {
2116 char_u *temp_end = tagp->command; 2626 char_u *temp_end = tagpp->command;
2117 2627
2118 if (*temp_end == '/') 2628 if (*temp_end == '/')
2119 while (*temp_end && *temp_end != '\r' 2629 while (*temp_end && *temp_end != '\r'
2120 && *temp_end != '\n' 2630 && *temp_end != '\n'
2121 && *temp_end != '$') 2631 && *temp_end != '$')
2122 temp_end++; 2632 temp_end++;
2123 2633
2124 if (tagp->command + 2 < temp_end) 2634 if (tagpp->command + 2 < temp_end)
2125 { 2635 {
2126 len = (int)(temp_end - tagp->command - 2); 2636 len = (int)(temp_end - tagpp->command - 2);
2127 mfp = alloc(len + 2); 2637 mfp = alloc(len + 2);
2128 if (mfp != NULL) 2638 if (mfp != NULL)
2129 vim_strncpy(mfp, tagp->command + 2, len); 2639 vim_strncpy(mfp, tagpp->command + 2, len);
2130 } 2640 }
2131 else 2641 else
2132 mfp = NULL; 2642 mfp = NULL;
2133 st->get_searchpat = FALSE; 2643 st->get_searchpat = FALSE;
2134 } 2644 }
2135 else 2645 else
2136 { 2646 {
2137 len = (int)(tagp->tagname_end - tagp->tagname); 2647 len = (int)(tagpp->tagname_end - tagpp->tagname);
2138 mfp = alloc(sizeof(char_u) + len + 1); 2648 mfp = alloc(sizeof(char_u) + len + 1);
2139 if (mfp != NULL) 2649 if (mfp != NULL)
2140 vim_strncpy(mfp, tagp->tagname, len); 2650 vim_strncpy(mfp, tagpp->tagname, len);
2141 2651
2142 // if wanted, re-read line to get long form too 2652 // if wanted, re-read line to get long form too
2143 if (State & INSERT) 2653 if (State & INSERT)
2144 st->get_searchpat = p_sft; 2654 st->get_searchpat = p_sft;
2145 } 2655 }
2159 // other tag: <mtt><tag_fname><0x02><0x02><lbuf><NUL> 2669 // other tag: <mtt><tag_fname><0x02><0x02><lbuf><NUL>
2160 // without Emacs tags: <mtt><tag_fname><0x02><lbuf><NUL> 2670 // without Emacs tags: <mtt><tag_fname><0x02><lbuf><NUL>
2161 // Here <mtt> is the "mtt" value plus 1 to avoid NUL. 2671 // Here <mtt> is the "mtt" value plus 1 to avoid NUL.
2162 len = (int)tag_fname_len + (int)STRLEN(st->lbuf) + 3; 2672 len = (int)tag_fname_len + (int)STRLEN(st->lbuf) + 3;
2163 #ifdef FEAT_EMACS_TAGS 2673 #ifdef FEAT_EMACS_TAGS
2164 if (is_etag) 2674 if (st->is_etag)
2165 { 2675 {
2166 ebuf_len = STRLEN(st->ebuf); 2676 ebuf_len = STRLEN(st->ebuf);
2167 len += (int)ebuf_len + 1; 2677 len += (int)ebuf_len + 1;
2168 } 2678 }
2169 else 2679 else
2181 slash_adjust(p + 1); 2691 slash_adjust(p + 1);
2182 #endif 2692 #endif
2183 p[tag_fname_len + 1] = TAG_SEP; 2693 p[tag_fname_len + 1] = TAG_SEP;
2184 s = p + 1 + tag_fname_len + 1; 2694 s = p + 1 + tag_fname_len + 1;
2185 #ifdef FEAT_EMACS_TAGS 2695 #ifdef FEAT_EMACS_TAGS
2186 if (is_etag) 2696 if (st->is_etag)
2187 { 2697 {
2188 STRCPY(s, st->ebuf); 2698 STRCPY(s, st->ebuf);
2189 s[ebuf_len] = TAG_SEP; 2699 s[ebuf_len] = TAG_SEP;
2190 s += ebuf_len + 1; 2700 s += ebuf_len + 1;
2191 } 2701 }
2220 { 2730 {
2221 // Out of memory! Just forget about the rest. 2731 // Out of memory! Just forget about the rest.
2222 st->stop_searching = TRUE; 2732 st->stop_searching = TRUE;
2223 return FAIL; 2733 return FAIL;
2224 } 2734 }
2225 else 2735
2226 { 2736 ((char_u **)(st->ga_match[mtt].ga_data))
2227 ((char_u **)(st->ga_match[mtt].ga_data)) 2737 [st->ga_match[mtt].ga_len++] = mfp;
2228 [st->ga_match[mtt].ga_len++] = mfp; 2738 st->match_count++;
2229 st->match_count++;
2230 }
2231 } 2739 }
2232 else 2740 else
2233 // duplicate tag, drop it 2741 // duplicate tag, drop it
2234 vim_free(mfp); 2742 vim_free(mfp);
2235 } 2743 }
2236 2744
2237 return OK; 2745 return OK;
2238 } 2746 }
2239 2747
2240 /* 2748 /*
2241 * Search for tags matching 'st->orgpat.pat' in the 'st->tag_fname' tags file. 2749 * Read and get all the tags from file st->tag_fname.
2242 * Information needed to search for the tags is in the 'st' state structure. 2750 * Returns OK if all the tags are processed successfully and FAIL is a tag
2243 * The matching tags are returned in 'st'. 2751 * format error is encountered.
2244 * Returns OK if successfully processed the file and FAIL on memory allocation
2245 * failure.
2246 */ 2752 */
2247 static int 2753 static int
2248 find_tags_in_file( 2754 findtags_get_all_tags(
2249 findtags_state_T *st, 2755 findtags_state_T *st,
2250 int flags, 2756 findtags_match_args_T *margs,
2251 char_u *buf_ffname) 2757 char_u *buf_ffname)
2252 { 2758 {
2253 FILE *fp = NULL; 2759 tagptrs_T tagp;
2254 tagptrs_T tagp; 2760 tagsearch_info_T search_info;
2255 int eof = FALSE; // found end-of-file 2761 int retval;
2256 int i;
2257 #ifdef FEAT_MULTI_LANG
2258 int help_pri = 0;
2259 char_u help_lang[3] = ""; // lang of current tags file
2260 #endif
2261 int tag_file_sorted = NUL; // !_TAG_FILE_SORTED value
2262 off_T filesize;
2263 int tagcmp;
2264 off_T offset;
2265 enum
2266 {
2267 TS_START, // at start of file
2268 TS_LINEAR, // linear searching forward, till EOF
2269 TS_BINARY, // binary searching
2270 TS_SKIP_BACK, // skipping backwards
2271 TS_STEP_FORWARD // stepping forwards
2272 } state; // Current search state
2273 struct tag_search_info // Binary search file offsets
2274 {
2275 off_T low_offset; // offset for first char of first line that
2276 // could match
2277 off_T high_offset; // offset of char after last line that could
2278 // match
2279 off_T curr_offset; // Current file offset in search range
2280 off_T curr_offset_used; // curr_offset used when skipping back
2281 off_T match_offset; // Where the binary search found a tag
2282 int low_char; // first char at low_offset
2283 int high_char; // first char at high_offset
2284 } search_info;
2285
2286 int cmplen;
2287 int match; // matches
2288 int match_no_ic = 0;// matches with rm_ic == FALSE
2289 int match_re; // match with regexp
2290 int matchoff = 0;
2291
2292 int is_etag; // current file is emaces style
2293
2294 hash_T hash = 0;
2295
2296 int sort_error = FALSE; // tags file not sorted
2297 int sortic = FALSE; // tag file sorted in nocase
2298 int noic = (flags & TAG_NOIC);
2299 int line_error = FALSE; // syntax error
2300 int has_re = (flags & TAG_REGEXP); // regexp used
2301 #ifdef FEAT_CSCOPE 2762 #ifdef FEAT_CSCOPE
2302 int use_cscope = (flags & TAG_CSCOPE); 2763 int use_cscope = (st->flags & TAG_CSCOPE);
2303 #endif 2764 #endif
2304 vimconv_T vimconv; 2765 hash_T hash = 0;
2305
2306 vimconv.vc_type = CONV_NONE;
2307 2766
2308 // This is only to avoid a compiler warning for using search_info 2767 // This is only to avoid a compiler warning for using search_info
2309 // uninitialised. 2768 // uninitialised.
2310 CLEAR_FIELD(search_info); 2769 CLEAR_FIELD(search_info);
2311 2770
2312 // A file that doesn't exist is silently ignored. Only when not a
2313 // single file is found, an error message is given (further on).
2314 #ifdef FEAT_CSCOPE
2315 if (use_cscope)
2316 fp = NULL; // avoid GCC warning
2317 else
2318 #endif
2319 {
2320 #ifdef FEAT_MULTI_LANG
2321 if (curbuf->b_help)
2322 {
2323 if (!findtags_in_help_init(st, flags, help_lang, &help_pri))
2324 return OK;
2325 }
2326 #endif
2327
2328 if ((fp = mch_fopen((char *)st->tag_fname, "r")) == NULL)
2329 return OK;
2330
2331 if (p_verbose >= 5)
2332 {
2333 verbose_enter();
2334 smsg(_("Searching tags file %s"), st->tag_fname);
2335 verbose_leave();
2336 }
2337 }
2338 st->did_open = TRUE; // remember that we found at least one file
2339
2340 state = TS_START; // we're at the start of the file
2341 is_etag = 0; // default is: not emacs style
2342
2343 // Read and parse the lines in the file one by one 2771 // Read and parse the lines in the file one by one
2344 for (;;) 2772 for (;;)
2345 { 2773 {
2346 // check for CTRL-C typed, more often when jumping around 2774 // check for CTRL-C typed, more often when jumping around
2347 if (state == TS_BINARY || state == TS_SKIP_BACK) 2775 if (st->state == TS_BINARY || st->state == TS_SKIP_BACK)
2348 line_breakcheck(); 2776 line_breakcheck();
2349 else 2777 else
2350 fast_breakcheck(); 2778 fast_breakcheck();
2351 if ((flags & TAG_INS_COMP)) // Double brackets for gcc 2779 if ((st->flags & TAG_INS_COMP)) // Double brackets for gcc
2352 ins_compl_check_keys(30, FALSE); 2780 ins_compl_check_keys(30, FALSE);
2353 if (got_int || ins_compl_interrupted()) 2781 if (got_int || ins_compl_interrupted())
2354 { 2782 {
2355 st->stop_searching = TRUE; 2783 st->stop_searching = TRUE;
2356 break; 2784 break;
2362 st->stop_searching = TRUE; 2790 st->stop_searching = TRUE;
2363 break; 2791 break;
2364 } 2792 }
2365 if (st->get_searchpat) 2793 if (st->get_searchpat)
2366 goto line_read_in; 2794 goto line_read_in;
2367 // For binary search: compute the next offset to use. 2795
2368 if (state == TS_BINARY) 2796 retval = findtags_get_next_line(st, &search_info);
2369 { 2797 if (retval == TAGS_READ_IGNORE)
2370 offset = search_info.low_offset + ((search_info.high_offset 2798 continue;
2371 - search_info.low_offset) / 2); 2799 if (retval == TAGS_READ_EOF)
2372 if (offset == search_info.curr_offset) 2800 break;
2373 break; // End the binary search without a match. 2801
2374 else 2802 line_read_in:
2375 search_info.curr_offset = offset; 2803
2376 } 2804 if (st->vimconv.vc_type != CONV_NONE)
2377 2805 findtags_string_convert(st);
2378 /* 2806
2379 * Skipping back (after a match during binary search).
2380 */
2381 else if (state == TS_SKIP_BACK)
2382 {
2383 search_info.curr_offset -= st->lbuf_size * 2;
2384 if (search_info.curr_offset < 0)
2385 {
2386 search_info.curr_offset = 0;
2387 rewind(fp);
2388 state = TS_STEP_FORWARD;
2389 }
2390 }
2391
2392 /*
2393 * When jumping around in the file, first read a line to find the
2394 * start of the next line.
2395 */
2396 if (state == TS_BINARY || state == TS_SKIP_BACK)
2397 {
2398 // Adjust the search file offset to the correct position
2399 search_info.curr_offset_used = search_info.curr_offset;
2400 vim_fseek(fp, search_info.curr_offset, SEEK_SET);
2401 eof = vim_fgets(st->lbuf, st->lbuf_size, fp);
2402 if (!eof && search_info.curr_offset != 0)
2403 {
2404 search_info.curr_offset = vim_ftell(fp);
2405 if (search_info.curr_offset == search_info.high_offset)
2406 {
2407 // oops, gone a bit too far; try from low offset
2408 vim_fseek(fp, search_info.low_offset, SEEK_SET);
2409 search_info.curr_offset = search_info.low_offset;
2410 }
2411 eof = vim_fgets(st->lbuf, st->lbuf_size, fp);
2412 }
2413 // skip empty and blank lines
2414 while (!eof && vim_isblankline(st->lbuf))
2415 {
2416 search_info.curr_offset = vim_ftell(fp);
2417 eof = vim_fgets(st->lbuf, st->lbuf_size, fp);
2418 }
2419 if (eof)
2420 {
2421 // Hit end of file. Skip backwards.
2422 state = TS_SKIP_BACK;
2423 search_info.match_offset = vim_ftell(fp);
2424 search_info.curr_offset = search_info.curr_offset_used;
2425 continue;
2426 }
2427 }
2428
2429 /*
2430 * Not jumping around in the file: Read the next line.
2431 */
2432 else
2433 {
2434 // skip empty and blank lines
2435 do
2436 {
2437 #ifdef FEAT_CSCOPE
2438 if (use_cscope)
2439 eof = cs_fgets(st->lbuf, st->lbuf_size);
2440 else
2441 #endif
2442 {
2443 search_info.curr_offset = vim_ftell(fp);
2444 eof = vim_fgets(st->lbuf, st->lbuf_size, fp);
2445 }
2446 } while (!eof && vim_isblankline(st->lbuf));
2447
2448 if (eof)
2449 {
2450 #ifdef FEAT_EMACS_TAGS 2807 #ifdef FEAT_EMACS_TAGS
2451 if (emacs_tags_file_eof(st, &fp) == TRUE) 2808 // Emacs tags line with CTRL-L: New file name on next line.
2452 { 2809 // The file name is followed by a ','.
2453 // an included tags file. Continue processing the parent 2810 // Remember etag file name in ebuf.
2454 // tags file.
2455 is_etag = 1; // (only etags can include)
2456 continue;
2457 }
2458 #endif
2459 break; // end of file
2460 }
2461 }
2462 line_read_in:
2463
2464 if (vimconv.vc_type != CONV_NONE)
2465 findtags_string_convert(st, &vimconv);
2466
2467 #ifdef FEAT_EMACS_TAGS
2468 /*
2469 * Emacs tags line with CTRL-L: New file name on next line.
2470 * The file name is followed by a ','.
2471 * Remember etag file name in ebuf.
2472 */
2473 if (*st->lbuf == Ctrl_L 2811 if (*st->lbuf == Ctrl_L
2474 # ifdef FEAT_CSCOPE 2812 # ifdef FEAT_CSCOPE
2475 && !use_cscope 2813 && !use_cscope
2476 # endif 2814 # endif
2477 ) 2815 )
2478 { 2816 {
2479 is_etag = 1; // in case at the start 2817 st->is_etag = TRUE; // in case at the start
2480 state = TS_LINEAR; 2818 st->state = TS_LINEAR;
2481 fp = emacs_tags_new_filename(st, fp, &is_etag); 2819 emacs_tags_new_filename(st);
2482 continue; 2820 continue;
2483 } 2821 }
2484 #endif 2822 #endif
2485 2823
2486 /* 2824 // When still at the start of the file, check for Emacs tags file
2487 * When still at the start of the file, check for Emacs tags file 2825 // format, and for "not sorted" flag.
2488 * format, and for "not sorted" flag. 2826 if (st->state == TS_START)
2489 */ 2827 {
2490 if (state == TS_START) 2828 if (findtags_start_state_handler(st, &margs->sortic, &search_info) == FALSE)
2491 {
2492 // The header ends when the line sorts below "!_TAG_". When
2493 // case is folded lower case letters sort before "_".
2494 if (STRNCMP(st->lbuf, "!_TAG_", 6) <= 0
2495 || (st->lbuf[0] == '!' && ASCII_ISLOWER(st->lbuf[1])))
2496 {
2497 if (tags_file_hdr_parse(st, &vimconv, &tag_file_sorted))
2498 // Read the next line. Unrecognized flags are ignored.
2499 continue;
2500
2501 goto parse_line;
2502 }
2503
2504 // Headers ends.
2505
2506 /*
2507 * When there is no tag head, or ignoring case, need to do a
2508 * linear search.
2509 * When no "!_TAG_" is found, default to binary search. If
2510 * the tag file isn't sorted, the second loop will find it.
2511 * When "!_TAG_FILE_SORTED" found: start binary search if
2512 * flag set.
2513 * For cscope, it's always linear.
2514 */
2515 # ifdef FEAT_CSCOPE
2516 if (st->linear || use_cscope)
2517 # else
2518 if (st->linear)
2519 # endif
2520 state = TS_LINEAR;
2521 else if (tag_file_sorted == NUL)
2522 state = TS_BINARY;
2523 else if (tag_file_sorted == '1')
2524 state = TS_BINARY;
2525 else if (tag_file_sorted == '2')
2526 {
2527 state = TS_BINARY;
2528 sortic = TRUE;
2529 st->orgpat.regmatch.rm_ic = (p_ic || !noic);
2530 }
2531 else
2532 state = TS_LINEAR;
2533
2534 if (state == TS_BINARY && st->orgpat.regmatch.rm_ic && !sortic)
2535 {
2536 // Binary search won't work for ignoring case, use linear
2537 // search.
2538 st->linear = TRUE;
2539 state = TS_LINEAR;
2540 }
2541
2542 // When starting a binary search, get the size of the file and
2543 // compute the first offset.
2544 if (state == TS_BINARY)
2545 {
2546 if (vim_fseek(fp, 0L, SEEK_END) != 0)
2547 // can't seek, don't use binary search
2548 state = TS_LINEAR;
2549 else
2550 {
2551 // Get the tag file size (don't use mch_fstat(), it's
2552 // not portable). Don't use lseek(), it doesn't work
2553 // properly on MacOS Catalina.
2554 filesize = vim_ftell(fp);
2555 vim_fseek(fp, 0L, SEEK_SET);
2556
2557 // Calculate the first read offset in the file. Start
2558 // the search in the middle of the file.
2559 search_info.low_offset = 0;
2560 search_info.low_char = 0;
2561 search_info.high_offset = filesize;
2562 search_info.curr_offset = 0;
2563 search_info.high_char = 0xff;
2564 }
2565 continue; 2829 continue;
2566 } 2830 }
2567 } 2831
2568
2569 parse_line:
2570 // When the line is too long the NUL will not be in the 2832 // When the line is too long the NUL will not be in the
2571 // last-but-one byte (see vim_fgets()). 2833 // last-but-one byte (see vim_fgets()).
2572 // Has been reported for Mozilla JS with extremely long names. 2834 // Has been reported for Mozilla JS with extremely long names.
2573 // In that case we need to increase lbuf_size. 2835 // In that case we need to increase lbuf_size.
2574 if (st->lbuf[st->lbuf_size - 2] != NUL 2836 if (st->lbuf[st->lbuf_size - 2] != NUL
2580 st->lbuf_size *= 2; 2842 st->lbuf_size *= 2;
2581 vim_free(st->lbuf); 2843 vim_free(st->lbuf);
2582 st->lbuf = alloc(st->lbuf_size); 2844 st->lbuf = alloc(st->lbuf_size);
2583 if (st->lbuf == NULL) 2845 if (st->lbuf == NULL)
2584 { 2846 {
2585 if (fp != NULL) 2847 if (st->fp != NULL)
2586 fclose(fp); 2848 fclose(st->fp);
2587 return FAIL; 2849 break;
2588 } 2850 }
2589 2851
2590 if (state == TS_STEP_FORWARD) 2852 if (st->state == TS_STEP_FORWARD)
2591 // Seek to the same position to read the same line again 2853 // Seek to the same position to read the same line again
2592 vim_fseek(fp, search_info.curr_offset, SEEK_SET); 2854 vim_fseek(st->fp, search_info.curr_offset, SEEK_SET);
2593 // this will try the same thing again, make sure the offset is 2855 // this will try the same thing again, make sure the offset is
2594 // different 2856 // different
2595 search_info.curr_offset = 0; 2857 search_info.curr_offset = 0;
2596 continue; 2858 continue;
2597 } 2859 }
2598 2860
2599 /* 2861 if (findtags_parse_line(st, &tagp) == FAIL)
2600 * Figure out where the different strings are in this line. 2862 return FAIL;
2601 * For "normal" tags: Do a quick check if the tag matches. 2863
2602 * This speeds up tag searching a lot! 2864 retval = findtags_match_tag(st, &tagp, margs, &search_info);
2603 */ 2865 if (retval == TAG_MATCH_NEXT)
2604 if (st->orgpat.headlen 2866 continue;
2867 if (retval == TAG_MATCH_STOP)
2868 break;
2869
2870 // If a match is found, add it to ht_match[] and ga_match[].
2871 if (retval == TAG_MATCH_SUCCESS)
2872 {
2873 if (findtags_add_match(st, &tagp, margs, buf_ffname, &hash)
2874 == FAIL)
2875 break;
2876 }
2877 } // forever
2878
2879 return OK;
2880 }
2881
2882 /*
2883 * Search for tags matching 'st->orgpat.pat' in the 'st->tag_fname' tags file.
2884 * Information needed to search for the tags is in the 'st' state structure.
2885 * The matching tags are returned in 'st'.
2886 * Returns OK if successfully processed the file and FAIL on memory allocation
2887 * failure.
2888 */
2889 static int
2890 findtags_in_file(findtags_state_T *st, char_u *buf_ffname)
2891 {
2892 findtags_match_args_T margs;
2893 int line_error = FALSE; // syntax error
2894 #ifdef FEAT_CSCOPE
2895 int use_cscope = FALSE;
2896 #endif
2897
2898 st->vimconv.vc_type = CONV_NONE;
2899 st->tag_file_sorted = NUL;
2900 st->fp = NULL;
2901 findtags_matchargs_init(&margs, st->flags);
2902
2903 // A file that doesn't exist is silently ignored. Only when not a
2904 // single file is found, an error message is given (further on).
2905 #ifdef FEAT_CSCOPE
2906 use_cscope = (st->flags & TAG_CSCOPE);
2907 if (use_cscope)
2908 st->fp = NULL; // avoid GCC warning
2909 else
2910 #endif
2911 {
2912 #ifdef FEAT_MULTI_LANG
2913 if (curbuf->b_help)
2914 {
2915 if (!findtags_in_help_init(st))
2916 return OK;
2917 }
2918 #endif
2919
2920 st->fp = mch_fopen((char *)st->tag_fname, "r");
2921 if (st->fp == NULL)
2922 return OK;
2923
2924 if (p_verbose >= 5)
2925 {
2926 verbose_enter();
2927 smsg(_("Searching tags file %s"), st->tag_fname);
2928 verbose_leave();
2929 }
2930 }
2931 st->did_open = TRUE; // remember that we found at least one file
2932
2933 st->state = TS_START; // we're at the start of the file
2605 #ifdef FEAT_EMACS_TAGS 2934 #ifdef FEAT_EMACS_TAGS
2606 && !is_etag 2935 st->is_etag = FALSE; // default is: not emacs style
2607 #endif 2936 #endif
2608 ) 2937
2609 { 2938 // Read and parse the lines in the file one by one
2610 CLEAR_FIELD(tagp); 2939 if (findtags_get_all_tags(st, &margs, buf_ffname) == FAIL)
2611 tagp.tagname = st->lbuf; 2940 line_error = TRUE;
2612 tagp.tagname_end = vim_strchr(st->lbuf, TAB);
2613 if (tagp.tagname_end == NULL)
2614 {
2615 // Corrupted tag line.
2616 line_error = TRUE;
2617 break;
2618 }
2619
2620 /*
2621 * Skip this line if the length of the tag is different and
2622 * there is no regexp, or the tag is too short.
2623 */
2624 cmplen = (int)(tagp.tagname_end - tagp.tagname);
2625 if (p_tl != 0 && cmplen > p_tl) // adjust for 'taglength'
2626 cmplen = p_tl;
2627 if (has_re && st->orgpat.headlen < cmplen)
2628 cmplen = st->orgpat.headlen;
2629 else if (state == TS_LINEAR && st->orgpat.headlen != cmplen)
2630 continue;
2631
2632 if (state == TS_BINARY)
2633 {
2634 /*
2635 * Simplistic check for unsorted tags file.
2636 */
2637 i = (int)tagp.tagname[0];
2638 if (sortic)
2639 i = (int)TOUPPER_ASC(tagp.tagname[0]);
2640 if (i < search_info.low_char || i > search_info.high_char)
2641 sort_error = TRUE;
2642
2643 /*
2644 * Compare the current tag with the searched tag.
2645 */
2646 if (sortic)
2647 tagcmp = tag_strnicmp(tagp.tagname, st->orgpat.head,
2648 (size_t)cmplen);
2649 else
2650 tagcmp = STRNCMP(tagp.tagname, st->orgpat.head, cmplen);
2651
2652 /*
2653 * A match with a shorter tag means to search forward.
2654 * A match with a longer tag means to search backward.
2655 */
2656 if (tagcmp == 0)
2657 {
2658 if (cmplen < st->orgpat.headlen)
2659 tagcmp = -1;
2660 else if (cmplen > st->orgpat.headlen)
2661 tagcmp = 1;
2662 }
2663
2664 if (tagcmp == 0)
2665 {
2666 // We've located the tag, now skip back and search
2667 // forward until the first matching tag is found.
2668 state = TS_SKIP_BACK;
2669 search_info.match_offset = search_info.curr_offset;
2670 continue;
2671 }
2672 if (tagcmp < 0)
2673 {
2674 search_info.curr_offset = vim_ftell(fp);
2675 if (search_info.curr_offset < search_info.high_offset)
2676 {
2677 search_info.low_offset = search_info.curr_offset;
2678 if (sortic)
2679 search_info.low_char =
2680 TOUPPER_ASC(tagp.tagname[0]);
2681 else
2682 search_info.low_char = tagp.tagname[0];
2683 continue;
2684 }
2685 }
2686 if (tagcmp > 0
2687 && search_info.curr_offset != search_info.high_offset)
2688 {
2689 search_info.high_offset = search_info.curr_offset;
2690 if (sortic)
2691 search_info.high_char =
2692 TOUPPER_ASC(tagp.tagname[0]);
2693 else
2694 search_info.high_char = tagp.tagname[0];
2695 continue;
2696 }
2697
2698 // No match yet and are at the end of the binary search.
2699 break;
2700 }
2701 else if (state == TS_SKIP_BACK)
2702 {
2703 if (MB_STRNICMP(tagp.tagname, st->orgpat.head, cmplen) != 0)
2704 state = TS_STEP_FORWARD;
2705 else
2706 // Have to skip back more. Restore the curr_offset
2707 // used, otherwise we get stuck at a long line.
2708 search_info.curr_offset = search_info.curr_offset_used;
2709 continue;
2710 }
2711 else if (state == TS_STEP_FORWARD)
2712 {
2713 if (MB_STRNICMP(tagp.tagname, st->orgpat.head, cmplen) != 0)
2714 {
2715 if ((off_T)vim_ftell(fp) > search_info.match_offset)
2716 break; // past last match
2717 else
2718 continue; // before first match
2719 }
2720 }
2721 else
2722 // skip this match if it can't match
2723 if (MB_STRNICMP(tagp.tagname, st->orgpat.head, cmplen) != 0)
2724 continue;
2725
2726 /*
2727 * Can be a matching tag, isolate the file name and command.
2728 */
2729 tagp.fname = tagp.tagname_end + 1;
2730 tagp.fname_end = vim_strchr(tagp.fname, TAB);
2731 if (tagp.fname_end == NULL)
2732 i = FAIL;
2733 else
2734 {
2735 tagp.command = tagp.fname_end + 1;
2736 i = OK;
2737 }
2738 }
2739 else
2740 i = parse_tag_line(st->lbuf,
2741 #ifdef FEAT_EMACS_TAGS
2742 is_etag,
2743 #endif
2744 &tagp);
2745 if (i == FAIL)
2746 {
2747 line_error = TRUE;
2748 break;
2749 }
2750
2751 #ifdef FEAT_EMACS_TAGS
2752 if (is_etag)
2753 tagp.fname = st->ebuf;
2754 #endif
2755 /*
2756 * First try matching with the pattern literally (also when it is
2757 * a regexp).
2758 */
2759 cmplen = (int)(tagp.tagname_end - tagp.tagname);
2760 if (p_tl != 0 && cmplen > p_tl) // adjust for 'taglength'
2761 cmplen = p_tl;
2762 // if tag length does not match, don't try comparing
2763 if (st->orgpat.len != cmplen)
2764 match = FALSE;
2765 else
2766 {
2767 if (st->orgpat.regmatch.rm_ic)
2768 {
2769 match = (MB_STRNICMP(tagp.tagname, st->orgpat.pat, cmplen) == 0);
2770 if (match)
2771 match_no_ic = (STRNCMP(tagp.tagname, st->orgpat.pat,
2772 cmplen) == 0);
2773 }
2774 else
2775 match = (STRNCMP(tagp.tagname, st->orgpat.pat, cmplen) == 0);
2776 }
2777
2778 /*
2779 * Has a regexp: Also find tags matching regexp.
2780 */
2781 match_re = FALSE;
2782 if (!match && st->orgpat.regmatch.regprog != NULL)
2783 {
2784 int cc;
2785
2786 cc = *tagp.tagname_end;
2787 *tagp.tagname_end = NUL;
2788 match = vim_regexec(&st->orgpat.regmatch, tagp.tagname, (colnr_T)0);
2789 if (match)
2790 {
2791 matchoff = (int)(st->orgpat.regmatch.startp[0] - tagp.tagname);
2792 if (st->orgpat.regmatch.rm_ic)
2793 {
2794 st->orgpat.regmatch.rm_ic = FALSE;
2795 match_no_ic = vim_regexec(&st->orgpat.regmatch, tagp.tagname,
2796 (colnr_T)0);
2797 st->orgpat.regmatch.rm_ic = TRUE;
2798 }
2799 }
2800 *tagp.tagname_end = cc;
2801 match_re = TRUE;
2802 }
2803
2804 // If a match is found, add it to ht_match[] and ga_match[].
2805 if (match)
2806 {
2807 if (findtags_add_match(st, &tagp, buf_ffname, flags, &hash,
2808 match_re, match_no_ic, matchoff,
2809 is_etag,
2810 #ifdef FEAT_MULTI_LANG
2811 help_lang, help_pri
2812 #else
2813 (char_u *)"", 0
2814 #endif
2815 ) == FAIL)
2816 break;
2817 }
2818 #ifdef FEAT_CSCOPE
2819 if (use_cscope && eof)
2820 break;
2821 #endif
2822 } // forever
2823 2941
2824 if (line_error) 2942 if (line_error)
2825 { 2943 {
2826 semsg(_(e_format_error_in_tags_file_str), st->tag_fname); 2944 semsg(_(e_format_error_in_tags_file_str), st->tag_fname);
2827 #ifdef FEAT_CSCOPE 2945 #ifdef FEAT_CSCOPE
2828 if (!use_cscope) 2946 if (!use_cscope)
2829 #endif 2947 #endif
2830 semsg(_("Before byte %ld"), (long)vim_ftell(fp)); 2948 semsg(_("Before byte %ld"), (long)vim_ftell(st->fp));
2831 st->stop_searching = TRUE; 2949 st->stop_searching = TRUE;
2832 line_error = FALSE; 2950 line_error = FALSE;
2833 } 2951 }
2834 2952
2835 #ifdef FEAT_CSCOPE 2953 if (st->fp != NULL)
2836 if (!use_cscope) 2954 fclose(st->fp);
2837 #endif
2838 fclose(fp);
2839 #ifdef FEAT_EMACS_TAGS 2955 #ifdef FEAT_EMACS_TAGS
2840 emacs_tags_incstack_free(); 2956 emacs_tags_incstack_free();
2841 #endif 2957 #endif
2842 if (vimconv.vc_type != CONV_NONE) 2958 if (st->vimconv.vc_type != CONV_NONE)
2843 convert_setup(&vimconv, NULL, NULL); 2959 convert_setup(&st->vimconv, NULL, NULL);
2844 2960
2845 tag_file_sorted = NUL; 2961 if (margs.sort_error)
2846 if (sort_error)
2847 {
2848 semsg(_(e_tags_file_not_sorted_str), st->tag_fname); 2962 semsg(_(e_tags_file_not_sorted_str), st->tag_fname);
2849 sort_error = FALSE; 2963
2850 } 2964 // Stop searching if sufficient tags have been found.
2851
2852 /*
2853 * Stop searching if sufficient tags have been found.
2854 */
2855 if (st->match_count >= st->mincount) 2965 if (st->match_count >= st->mincount)
2856 st->stop_searching = TRUE; 2966 st->stop_searching = TRUE;
2857 2967
2858 return OK; 2968 return OK;
2859 } 2969 }
2860 2970
2861 /* 2971 /*
2862 * Copy the tags found by find_tags() to 'matchesp'. 2972 * Copy the tags found by find_tags() to 'matchesp'.
2863 */ 2973 * Returns the number of matches copied.
2864 static void 2974 */
2865 findtags_copy_matches( 2975 static int
2866 findtags_state_T *st, 2976 findtags_copy_matches(findtags_state_T *st, char_u ***matchesp)
2867 char_u ***matchesp, 2977 {
2868 int *num_matches) 2978 int name_only = (st->flags & TAG_NAMES);
2869 {
2870 char_u **matches; 2979 char_u **matches;
2871 int mtt; 2980 int mtt;
2872 int i; 2981 int i;
2873 char_u *mfp; 2982 char_u *mfp;
2874 char_u *p; 2983 char_u *p;
2885 mfp = ((char_u **)(st->ga_match[mtt].ga_data))[i]; 2994 mfp = ((char_u **)(st->ga_match[mtt].ga_data))[i];
2886 if (matches == NULL) 2995 if (matches == NULL)
2887 vim_free(mfp); 2996 vim_free(mfp);
2888 else 2997 else
2889 { 2998 {
2890 if (!st->name_only) 2999 if (!name_only)
2891 { 3000 {
2892 // Change mtt back to zero-based. 3001 // Change mtt back to zero-based.
2893 *mfp = *mfp - 1; 3002 *mfp = *mfp - 1;
2894 3003
2895 // change the TAG_SEP back to NUL 3004 // change the TAG_SEP back to NUL
2904 ga_clear(&st->ga_match[mtt]); 3013 ga_clear(&st->ga_match[mtt]);
2905 hash_clear(&st->ht_match[mtt]); 3014 hash_clear(&st->ht_match[mtt]);
2906 } 3015 }
2907 3016
2908 *matchesp = matches; 3017 *matchesp = matches;
2909 *num_matches = st->match_count; 3018 return st->match_count;
2910 } 3019 }
2911 3020
2912 /* 3021 /*
2913 * find_tags() - search for tags in tags files 3022 * find_tags() - search for tags in tags files
2914 * 3023 *
2939 find_tags( 3048 find_tags(
2940 char_u *pat, // pattern to search for 3049 char_u *pat, // pattern to search for
2941 int *num_matches, // return: number of matches found 3050 int *num_matches, // return: number of matches found
2942 char_u ***matchesp, // return: array of matches found 3051 char_u ***matchesp, // return: array of matches found
2943 int flags, 3052 int flags,
2944 int mincount, // MAXCOL: find all matches 3053 int mincount, // MAXCOL: find all matches
2945 // other: minimal number of matches 3054 // other: minimal number of matches
2946 char_u *buf_ffname) // name of buffer for priority 3055 char_u *buf_ffname) // name of buffer for priority
2947 { 3056 {
2948 findtags_state_T st; 3057 findtags_state_T st;
2949 tagname_T tn; // info for get_tagfname() 3058 tagname_T tn; // info for get_tagfname()
3033 emsg_off = save_emsg_off; 3142 emsg_off = save_emsg_off;
3034 if (has_re && st.orgpat.regmatch.regprog == NULL) 3143 if (has_re && st.orgpat.regmatch.regprog == NULL)
3035 goto findtag_end; 3144 goto findtag_end;
3036 3145
3037 #ifdef FEAT_EVAL 3146 #ifdef FEAT_EVAL
3038 retval = findtags_apply_tfu(pat, &st, flags, buf_ffname); 3147 retval = findtags_apply_tfu(&st, pat, buf_ffname);
3039 if (retval != NOTDONE) 3148 if (retval != NOTDONE)
3040 goto findtag_end; 3149 goto findtag_end;
3041 3150
3042 // re-initialize the default return value 3151 // re-initialize the default return value
3043 retval = FAIL; 3152 retval = FAIL;
3077 use_cscope || 3186 use_cscope ||
3078 #endif 3187 #endif
3079 get_tagfname(&tn, first_file, st.tag_fname) == OK; 3188 get_tagfname(&tn, first_file, st.tag_fname) == OK;
3080 first_file = FALSE) 3189 first_file = FALSE)
3081 { 3190 {
3082 if (find_tags_in_file(&st, flags, buf_ffname) == FAIL) 3191 if (findtags_in_file(&st, buf_ffname) == FAIL)
3083 goto findtag_end; 3192 goto findtag_end;
3084 if (st.stop_searching 3193 if (st.stop_searching
3085 #ifdef FEAT_CSCOPE 3194 #ifdef FEAT_CSCOPE
3086 || use_cscope 3195 || use_cscope
3087 #endif 3196 #endif
3126 * matches. When retval == FAIL, free the matches. 3235 * matches. When retval == FAIL, free the matches.
3127 */ 3236 */
3128 if (retval == FAIL) 3237 if (retval == FAIL)
3129 st.match_count = 0; 3238 st.match_count = 0;
3130 3239
3131 findtags_copy_matches(&st, matchesp, num_matches); 3240 *num_matches = findtags_copy_matches(&st, matchesp);
3132 3241
3133 curbuf->b_help = help_save; 3242 curbuf->b_help = help_save;
3134 #ifdef FEAT_MULTI_LANG 3243 #ifdef FEAT_MULTI_LANG
3135 vim_free(saved_pat); 3244 vim_free(saved_pat);
3136 #endif 3245 #endif