Line | Branch | Exec | Source |
---|---|---|---|
1 | #include <stdlib.h> | ||
2 | #include "ctags.h" | ||
3 | #include "util/ascii.h" | ||
4 | #include "util/debug.h" | ||
5 | #include "util/str-util.h" | ||
6 | #include "util/strtonum.h" | ||
7 | #include "util/xmalloc.h" | ||
8 | #include "util/xstring.h" | ||
9 | |||
10 | 18 | static size_t parse_ex_pattern(const char *buf, size_t size, char **escaped) | |
11 | { | ||
12 | 18 | BUG_ON(size == 0); | |
13 | 18 | BUG_ON(buf[0] != '/' && buf[0] != '?'); | |
14 | |||
15 | // The search pattern is not a real regular expression; special characters | ||
16 | // need to be escaped | ||
17 | 18 | char *pattern = xmalloc(size * 2); | |
18 | 18 | char open_delim = buf[0]; | |
19 |
2/2✓ Branch 0 taken 846 times.
✓ Branch 1 taken 1 times.
|
847 | for (size_t i = 1, j = 0; i < size; i++) { |
20 |
2/2✓ Branch 0 taken 845 times.
✓ Branch 1 taken 1 times.
|
846 | if (unlikely(buf[i] == '\0')) { |
21 | break; | ||
22 | } | ||
23 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 842 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
845 | if (buf[i] == '\\' && i + 1 < size) { |
24 | 3 | i++; | |
25 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
|
3 | if (buf[i] == '\\') { |
26 | 1 | pattern[j++] = '\\'; | |
27 | } | ||
28 | 3 | pattern[j++] = buf[i]; | |
29 | 3 | continue; | |
30 | } | ||
31 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 826 times.
|
842 | if (buf[i] == open_delim) { |
32 | 16 | pattern[j] = '\0'; | |
33 | 16 | *escaped = pattern; | |
34 | 16 | return i + 1; | |
35 | } | ||
36 | 826 | char c = buf[i]; | |
37 |
2/2✓ Branch 0 taken 25 times.
✓ Branch 1 taken 801 times.
|
826 | if (c == '*' || c == '[' || c == ']') { |
38 | 25 | pattern[j++] = '\\'; | |
39 | } | ||
40 | 826 | pattern[j++] = buf[i]; | |
41 | } | ||
42 | |||
43 | 2 | free(pattern); | |
44 | 2 | return 0; | |
45 | } | ||
46 | |||
47 | 20 | static size_t parse_ex_cmd(Tag *tag, const char *buf, size_t size) | |
48 | { | ||
49 |
1/2✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
|
20 | if (unlikely(size == 0)) { |
50 | return 0; | ||
51 | } | ||
52 | |||
53 | 20 | size_t n; | |
54 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 2 times.
|
20 | if (buf[0] == '/' || buf[0] == '?') { |
55 | 18 | n = parse_ex_pattern(buf, size, &tag->pattern); | |
56 | } else { | ||
57 | 2 | n = buf_parse_ulong(buf, size, &tag->lineno); | |
58 | } | ||
59 | |||
60 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 2 times.
|
20 | if (n == 0) { |
61 | return 0; | ||
62 | } | ||
63 | |||
64 |
4/4✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 1 times.
|
18 | if (n + 1 < size && mem_equal(buf + n, ";\"", 2)) { |
65 | 15 | n += 2; | |
66 | } | ||
67 | |||
68 | return n; | ||
69 | } | ||
70 | |||
71 | 20 | bool parse_ctags_line(Tag *tag, const char *line, size_t line_len) | |
72 | { | ||
73 | 20 | size_t pos = 0; | |
74 | 20 | *tag = (Tag){.name = get_delim(line, &pos, line_len, '\t')}; | |
75 |
2/4✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
|
20 | if (tag->name.length == 0 || pos >= line_len) { |
76 | return false; | ||
77 | } | ||
78 | |||
79 | 20 | tag->filename = get_delim(line, &pos, line_len, '\t'); | |
80 |
2/4✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
|
20 | if (tag->filename.length == 0 || pos >= line_len) { |
81 | return false; | ||
82 | } | ||
83 | |||
84 | 20 | size_t len = parse_ex_cmd(tag, line + pos, line_len - pos); | |
85 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 18 times.
|
20 | if (len == 0) { |
86 | 2 | BUG_ON(tag->pattern); | |
87 | return false; | ||
88 | } | ||
89 | |||
90 | 18 | pos += len; | |
91 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 1 times.
|
18 | if (pos >= line_len) { |
92 | return true; | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Extension fields (key:[value]): | ||
97 | * | ||
98 | * file: visibility limited to this file | ||
99 | * struct:NAME tag is member of struct NAME | ||
100 | * union:NAME tag is member of union NAME | ||
101 | * typeref:struct:NAME::MEMBER_TYPE MEMBER_TYPE is type of the tag | ||
102 | */ | ||
103 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 16 times.
|
17 | if (line[pos++] != '\t') { |
104 | // free `pattern` allocated by parse_ex_cmd() | ||
105 | 1 | free_tag(tag); | |
106 | 1 | tag->pattern = NULL; | |
107 | 1 | return false; | |
108 | } | ||
109 | |||
110 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 16 times.
|
52 | while (pos < line_len) { |
111 | 36 | StringView field = get_delim(line, &pos, line_len, '\t'); | |
112 |
3/4✓ Branch 0 taken 16 times.
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 16 times.
✗ Branch 3 not taken.
|
36 | if (field.length == 1 && ascii_isalpha(field.data[0])) { |
113 | 16 | tag->kind = field.data[0]; | |
114 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 13 times.
|
20 | } else if (strview_equal_cstring(&field, "file:")) { |
115 | 7 | tag->local = true; | |
116 | } | ||
117 | // TODO: struct/union/typeref | ||
118 | } | ||
119 | |||
120 | return true; | ||
121 | } | ||
122 | |||
123 | 16 | bool next_tag ( | |
124 | const char *buf, | ||
125 | size_t buf_len, | ||
126 | size_t *posp, | ||
127 | const StringView *prefix, | ||
128 | bool exact, | ||
129 | Tag *tag | ||
130 | ) { | ||
131 | 16 | const char *p = prefix->data; | |
132 | 16 | size_t plen = prefix->length; | |
133 |
2/2✓ Branch 0 taken 54 times.
✓ Branch 1 taken 3 times.
|
57 | for (size_t pos = *posp; pos < buf_len; ) { |
134 | 54 | StringView line = buf_slice_next_line(buf, &pos, buf_len); | |
135 |
3/4✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 36 times.
|
54 | if (line.length == 0 || line.data[0] == '!') { |
136 | 41 | continue; | |
137 | } | ||
138 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 14 times.
|
36 | if (!strview_has_strn_prefix(&line, p, plen)) { |
139 | 22 | continue; | |
140 | } | ||
141 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
14 | if (exact && line.data[plen] != '\t') { |
142 | 1 | continue; | |
143 | } | ||
144 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
|
13 | if (!parse_ctags_line(tag, line.data, line.length)) { |
145 | ✗ | continue; | |
146 | } | ||
147 | 13 | *posp = pos; | |
148 | 13 | return true; | |
149 | } | ||
150 | 3 | return false; | |
151 | } | ||
152 | |||
153 | // NOTE: tag itself is not freed | ||
154 | 21 | void free_tag(Tag *tag) | |
155 | { | ||
156 | 21 | free(tag->pattern); | |
157 | 21 | } | |
158 |