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 | 19 | static size_t parse_ex_pattern(const char *buf, size_t size, char **escaped) | |
11 | { | ||
12 | 19 | BUG_ON(size == 0); | |
13 | 19 | 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 | 19 | char *pattern = xmalloc(size * 2); | |
18 | 19 | char open_delim = buf[0]; | |
19 |
2/2✓ Branch 0 (20→8) taken 890 times.
✓ Branch 1 (20→21) taken 1 times.
|
891 | for (size_t i = 1, j = 0; i < size; i++) { |
20 |
2/2✓ Branch 0 (8→9) taken 889 times.
✓ Branch 1 (8→21) taken 1 times.
|
890 | if (unlikely(buf[i] == '\0')) { |
21 | break; | ||
22 | } | ||
23 |
3/4✓ Branch 0 (9→10) taken 3 times.
✓ Branch 1 (9→14) taken 886 times.
✓ Branch 2 (10→11) taken 3 times.
✗ Branch 3 (10→14) not taken.
|
889 | if (buf[i] == '\\' && i + 1 < size) { |
24 | 3 | i++; | |
25 |
2/2✓ Branch 0 (11→12) taken 1 times.
✓ Branch 1 (11→13) 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 (14→15) taken 17 times.
✓ Branch 1 (14→16) taken 869 times.
|
886 | if (buf[i] == open_delim) { |
32 | 17 | pattern[j] = '\0'; | |
33 | 17 | *escaped = pattern; | |
34 | 17 | return i + 1; | |
35 | } | ||
36 | 869 | char c = buf[i]; | |
37 |
2/2✓ Branch 0 (16→17) taken 28 times.
✓ Branch 1 (16→18) taken 841 times.
|
869 | if (c == '*' || c == '[' || c == ']') { |
38 | 28 | pattern[j++] = '\\'; | |
39 | } | ||
40 | 869 | pattern[j++] = buf[i]; | |
41 | } | ||
42 | |||
43 | 2 | free(pattern); | |
44 | 2 | return 0; | |
45 | } | ||
46 | |||
47 | 21 | static size_t parse_ex_cmd(Tag *tag, const char *buf, size_t size) | |
48 | { | ||
49 |
1/2✓ Branch 0 (2→3) taken 21 times.
✗ Branch 1 (2→11) not taken.
|
21 | if (unlikely(size == 0)) { |
50 | return 0; | ||
51 | } | ||
52 | |||
53 | 21 | size_t n; | |
54 |
2/2✓ Branch 0 (3→4) taken 19 times.
✓ Branch 1 (3→5) taken 2 times.
|
21 | if (buf[0] == '/' || buf[0] == '?') { |
55 | 19 | 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 (6→7) taken 19 times.
✓ Branch 1 (6→11) taken 2 times.
|
21 | if (n == 0) { |
61 | return 0; | ||
62 | } | ||
63 | |||
64 |
4/4✓ Branch 0 (7→8) taken 17 times.
✓ Branch 1 (7→11) taken 2 times.
✓ Branch 2 (9→10) taken 16 times.
✓ Branch 3 (9→11) taken 1 times.
|
19 | if (n + 1 < size && mem_equal(buf + n, ";\"", 2)) { |
65 | 16 | n += 2; | |
66 | } | ||
67 | |||
68 | return n; | ||
69 | } | ||
70 | |||
71 | 21 | bool parse_ctags_line(Tag *tag, const char *line, size_t line_len) | |
72 | { | ||
73 | 21 | size_t pos = 0; | |
74 | 21 | *tag = (Tag){.name = get_delim(line, &pos, line_len, '\t')}; | |
75 |
2/4✓ Branch 0 (3→4) taken 21 times.
✗ Branch 1 (3→25) not taken.
✓ Branch 2 (4→5) taken 21 times.
✗ Branch 3 (4→25) not taken.
|
21 | if (tag->name.length == 0 || pos >= line_len) { |
76 | return false; | ||
77 | } | ||
78 | |||
79 | 21 | tag->filename = get_delim(line, &pos, line_len, '\t'); | |
80 |
2/4✓ Branch 0 (6→7) taken 21 times.
✗ Branch 1 (6→25) not taken.
✓ Branch 2 (7→8) taken 21 times.
✗ Branch 3 (7→25) not taken.
|
21 | if (tag->filename.length == 0 || pos >= line_len) { |
81 | return false; | ||
82 | } | ||
83 | |||
84 | 21 | size_t len = parse_ex_cmd(tag, line + pos, line_len - pos); | |
85 |
2/2✓ Branch 0 (9→10) taken 2 times.
✓ Branch 1 (9→12) taken 19 times.
|
21 | if (len == 0) { |
86 | 2 | BUG_ON(tag->pattern); | |
87 | return false; | ||
88 | } | ||
89 | |||
90 | 19 | pos += len; | |
91 |
2/2✓ Branch 0 (12→13) taken 18 times.
✓ Branch 1 (12→25) taken 1 times.
|
19 | 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 (13→14) taken 1 times.
✓ Branch 1 (13→24) taken 17 times.
|
18 | 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 (24→16) taken 38 times.
✓ Branch 1 (24→25) taken 17 times.
|
55 | while (pos < line_len) { |
111 | 38 | StringView field = get_delim(line, &pos, line_len, '\t'); | |
112 |
3/4✓ Branch 0 (17→18) taken 17 times.
✓ Branch 1 (17→20) taken 21 times.
✓ Branch 2 (18→19) taken 17 times.
✗ Branch 3 (18→20) not taken.
|
38 | if (field.length == 1 && ascii_isalpha(field.data[0])) { |
113 | 17 | tag->kind = field.data[0]; | |
114 |
2/2✓ Branch 0 (21→22) taken 7 times.
✓ Branch 1 (21→23) taken 14 times.
|
21 | } 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 | 18 | bool next_tag ( | |
124 | const char *buf, | ||
125 | size_t buf_len, | ||
126 | size_t *posp, // in-out param | ||
127 | const StringView *prefix, | ||
128 | bool exact, | ||
129 | Tag *tag // out param | ||
130 | ) { | ||
131 | 18 | const char *p = prefix->data; | |
132 | 18 | size_t plen = prefix->length; | |
133 |
2/2✓ Branch 0 (14→3) taken 55 times.
✓ Branch 1 (14→15) taken 4 times.
|
59 | for (size_t pos = *posp; pos < buf_len; ) { |
134 | 55 | StringView line = buf_slice_next_line(buf, &pos, buf_len); | |
135 | 55 | if ( | |
136 |
1/2✓ Branch 0 (4→5) taken 55 times.
✗ Branch 1 (4→13) not taken.
|
55 | line.length > 0 // Line is non-empty |
137 |
2/2✓ Branch 0 (5→6) taken 37 times.
✓ Branch 1 (5→13) taken 18 times.
|
55 | && line.data[0] != '!' // and not a comment |
138 |
2/2✓ Branch 0 (7→8) taken 15 times.
✓ Branch 1 (7→13) taken 22 times.
|
37 | && strview_has_strn_prefix(&line, p, plen) // and starts with `prefix` |
139 |
4/4✓ Branch 0 (8→9) taken 2 times.
✓ Branch 1 (8→10) taken 13 times.
✓ Branch 2 (9→10) taken 1 times.
✓ Branch 3 (9→13) taken 1 times.
|
15 | && (!exact || line.data[plen] == '\t') // or matches `prefix` exactly, if applicable |
140 |
1/2✓ Branch 0 (11→12) taken 14 times.
✗ Branch 1 (11→13) not taken.
|
14 | && parse_ctags_line(tag, line.data, line.length) // and is a valid tags(5) entry |
141 | ) { | ||
142 | // Advance the position; `tag` param has been filled by parse_ctags_line() | ||
143 | 14 | *posp = pos; | |
144 | 14 | return true; | |
145 | } | ||
146 | } | ||
147 | |||
148 | // No matching tags remaining | ||
149 | 4 | return false; | |
150 | } | ||
151 | |||
152 | // NOTE: tag itself is not freed | ||
153 | 22 | void free_tag(Tag *tag) | |
154 | { | ||
155 | 22 | free(tag->pattern); | |
156 | 22 | } | |
157 |