Mercurial > vim
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 |