Skip to content

Commit 4089cff

Browse files
authored
Merge pull request #3500 from masatake/update-libreadtags
Update libreadtags
2 parents 5d506a1 + 4b56af0 commit 4089cff

File tree

6 files changed

+317
-1
lines changed

6 files changed

+317
-1
lines changed

libreadtags/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ tests/*.trs
3434
tests/test-api-tagsClose
3535
tests/test-api-tagsFind
3636
tests/test-api-tagsFirst
37+
tests/test-api-tagsFindPseudoTag
3738
tests/test-api-tagsFirstPseudoTag
3839
tests/test-api-tagsOpen
3940
tests/test-api-tagsSetSortType

libreadtags/NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
used fseek and ftell. In this version, they are replaced with _fseeki64 and
1818
_ftelli64.
1919

20+
- add a new API function (tagsFindPseudoTag) for finding a pseudo tag for
21+
given name.
22+
2023
# Version 0.1.0
2124

2225
- propagate internal errors to caller

libreadtags/readtags.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,39 @@ extern tagResult tagsNextPseudoTag (tagFile *const file, tagEntry *const entry)
13341334
return findPseudoTag (file, 0, entry);
13351335
}
13361336

1337+
extern tagResult tagsFindPseudoTag (tagFile *const file, tagEntry *const entry,
1338+
const char *const name, const int match)
1339+
{
1340+
size_t len;
1341+
tagEntry entry0;
1342+
tagEntry *entryp = entry? entry: &entry0;
1343+
1344+
tagResult r = tagsFirstPseudoTag (file, entryp);
1345+
if (r != TagSuccess)
1346+
return r;
1347+
1348+
if (match & TAG_PARTIALMATCH)
1349+
len = strlen (name);
1350+
1351+
do
1352+
{
1353+
if (match & TAG_PARTIALMATCH)
1354+
{
1355+
if (strncmp (entryp->name, name, len) == 0)
1356+
return TagSuccess;
1357+
}
1358+
else
1359+
{
1360+
if (strcmp (entryp->name, name) == 0)
1361+
return TagSuccess;
1362+
}
1363+
r = tagsNextPseudoTag (file, entryp);
1364+
}
1365+
while (r == TagSuccess);
1366+
1367+
return r;
1368+
}
1369+
13371370
extern tagResult tagsClose (tagFile *const file)
13381371
{
13391372
tagResult result = TagFailure;

libreadtags/readtags.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ typedef enum {
3939
#define sortType tagSortType
4040
#endif
4141

42-
/* Options for tagsFind() */
42+
/* Options for tagsFind() and tagsFindPseudoTag() */
4343
#define TAG_FULLMATCH 0x0
4444
#define TAG_PARTIALMATCH 0x1
4545

@@ -270,6 +270,21 @@ extern tagResult tagsFirstPseudoTag (tagFile *const file, tagEntry *const entry)
270270
*/
271271
extern tagResult tagsNextPseudoTag (tagFile *const file, tagEntry *const entry);
272272

273+
/*
274+
* Does the same as tagsFind(), but is specialized to pseudo tags.
275+
* The available values for `match' are:
276+
*
277+
* TAG_PARTIALMATCH
278+
* Tags whose leading characters match `name' will qualify.
279+
*
280+
* TAG_FULLMATCH
281+
* Only tags whose full lengths match `name' will qualify.
282+
*
283+
* NOTE: unlike tagsFind(), this function uses liner-searching even if
284+
* the tags file is sorted.
285+
*/
286+
extern tagResult tagsFindPseudoTag (tagFile *const file, tagEntry *const entry, const char *const name, const int match);
287+
273288
/*
274289
* Call tagsClose() at completion of reading the tag file, which will
275290
* close the file and free any internal memory allocated. The function will

libreadtags/tests/Makefile.am

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ TESTS = \
22
\
33
test-api-tagsOpen \
44
test-api-tagsFind \
5+
test-api-tagsFindPseudoTag \
56
test-api-tagsFirstPseudoTag \
67
test-api-tagsFirst \
78
test-api-tagsClose \
@@ -17,6 +18,7 @@ check_PROGRAMS = \
1718
\
1819
test-api-tagsOpen \
1920
test-api-tagsFind \
21+
test-api-tagsFindPseudoTag \
2022
test-api-tagsFirstPseudoTag \
2123
test-api-tagsFirst \
2224
test-api-tagsClose \
@@ -57,6 +59,9 @@ EXTRA_DIST += duplicated-names--sorted-no.tags
5759
EXTRA_DIST += duplicated-names--sorted-foldcase.tags
5860
EXTRA_DIST += broken-line-field-in-middle.tags
5961

62+
test_api_tagsFindPseudoTag = test-api-tagsFindPseudoTag.c
63+
test_api_tagsFindPseudoTag_DEPENDENCIES = $(DEPS)
64+
6065
test_api_tagsFirstPseudoTag = test-api-tagsFirstPseudoTag.c
6166
test_api_tagsFirstPseudoTag_DEPENDENCIES = $(DEPS)
6267
EXTRA_DIST += ptag-sort-no.tags
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
/*
2+
* Copyright (c) 2022, Masatake YAMATO
3+
*
4+
* This source code is released into the public domain.
5+
*
6+
* Testing tagsFindPseudoTag() API function
7+
*/
8+
9+
#include "readtags.h"
10+
11+
#include <stdio.h>
12+
#include <stdlib.h>
13+
#include <string.h>
14+
#include <unistd.h>
15+
16+
17+
struct expectation {
18+
char *name;
19+
char *file;
20+
char *pattern;
21+
};
22+
23+
#define COUNT(x) (sizeof(x)/sizeof(x[0]))
24+
25+
static int
26+
check_finding0 (tagFile *t, struct expectation *expectations, int count, int match)
27+
{
28+
tagEntry e;
29+
struct expectation *x;
30+
int err;
31+
32+
for (int i = 0; i < count; i++)
33+
{
34+
x = expectations + i;
35+
fprintf (stderr, "[%d/%d] finding ptags in %s matching...", i + 1, count,
36+
match == TAG_FULLMATCH? "full": "partial");
37+
if (tagsFindPseudoTag (t, &e, x->name, match) != TagSuccess)
38+
{
39+
if ((err = tagsGetErrno (t)))
40+
fprintf (stderr, "error: %d\n", err);
41+
else
42+
fprintf (stderr, "cannot find: %s\n", x->name);
43+
return 1;
44+
}
45+
fprintf (stderr, "found\n");
46+
47+
fprintf (stderr, "checking name field...");
48+
if (match == TAG_FULLMATCH)
49+
{
50+
if (!(e.name && strcmp (x->name, e.name) == 0))
51+
{
52+
fprintf (stderr, "unexpected: %s\n", e.name? e.name: "<NULL>");
53+
return 1;
54+
}
55+
}
56+
else
57+
{
58+
if (!(e.name && strncmp (x->name, e.name, strlen(x->name)) == 0))
59+
{
60+
fprintf (stderr, "unexpected: strncmp(%s, %s)\n",
61+
x->name, e.name? e.name: "<NULL>");
62+
return 1;
63+
}
64+
}
65+
fprintf (stderr, "ok\n");
66+
67+
fprintf (stderr, "checking file field...");
68+
if (!(e.file && strcmp (x->file, e.file) == 0))
69+
{
70+
fprintf (stderr, "unexpected: %s\n", e.file? e.file: "<NULL>");
71+
return 1;
72+
}
73+
fprintf (stderr, "ok\n");
74+
75+
fprintf (stderr, "checking pattern field...");
76+
if (!(e.address.pattern && strcmp (x->pattern, e.address.pattern) == 0))
77+
{
78+
fprintf (stderr, "unexpected: %s\n",
79+
e.address.pattern? e.address.pattern: "<NULL>");
80+
return 1;
81+
}
82+
fprintf (stderr, "ok\n");
83+
}
84+
85+
return 0;
86+
}
87+
88+
static int
89+
check_finding(const char *tags, struct expectation *expectations, int count, int match)
90+
{
91+
tagFile *t;
92+
tagFileInfo info;
93+
94+
fprintf (stderr, "opening %s...", tags);
95+
t = tagsOpen (tags, &info);
96+
if (!t)
97+
{
98+
fprintf (stderr, "unexpected result (t: %p, opened: %d, error_number: %d)\n",
99+
t, info.status.opened, info.status.error_number);
100+
return 1;
101+
}
102+
fprintf (stderr, "ok\n");
103+
104+
if (check_finding0 (t, expectations, count, match) != 0)
105+
return 1;
106+
107+
fprintf (stderr, "closing the tag file...");
108+
if (tagsClose (t) != TagSuccess)
109+
{
110+
fprintf (stderr, "unexpected result\n");
111+
return 1;
112+
}
113+
fprintf (stderr, "ok\n");
114+
return 0;
115+
}
116+
117+
int
118+
main (void)
119+
{
120+
char *srcdir = getenv ("srcdir");
121+
if (srcdir)
122+
{
123+
if (chdir (srcdir) == -1)
124+
{
125+
perror ("chdir");
126+
return 99;
127+
}
128+
}
129+
130+
/*
131+
* sorted=yes
132+
*/
133+
const char *tags_sorted_yes = "./ptag-sort-yes.tags";
134+
struct expectation exp_sorted_yes [] = {
135+
{ "!_JSON_OUTPUT_VERSION", "0.0", "/in development/", },
136+
{ "!_TAG_FILE_FORMAT", "2", "/extended format; --format=1 will not append ;\" to lines/", },
137+
{ "!_TAG_FILE_SORTED", "1", "/0=unsorted, 1=sorted, 2=foldcase/", },
138+
{ "!_TAG_KIND_DESCRIPTION!C", "D,macroparam", "/parameters inside macro definitions/", },
139+
{ "!_TAG_KIND_DESCRIPTION!EmacsLisp", "C,custom", "/customizable variables/", },
140+
{ "!_TAG_OUTPUT_FILESEP", "slash", "/slash or backslash/", },
141+
{ "!_TAG_OUTPUT_MODE", "u-ctags", "/u-ctags or e-ctags/", },
142+
{ "!_TAG_PATTERN_LENGTH_LIMIT", "96", "/0 for no limit/", },
143+
{ "!_TAG_PROGRAM_AUTHOR", "Universal Ctags Team", "//", },
144+
{ "!_TAG_PROGRAM_NAME", "Universal Ctags", "/Derived from Exuberant Ctags/", },
145+
{ "!_TAG_PROGRAM_URL", "https://ctags.io/", "/official site/", },
146+
{ "!_TAG_PROGRAM_VERSION", "0.0.0", "/9b73623f/", },
147+
};
148+
149+
if (check_finding (tags_sorted_yes, exp_sorted_yes, COUNT(exp_sorted_yes),
150+
TAG_FULLMATCH) != 0)
151+
return 1;
152+
153+
struct expectation exp_sorted_yes_partial [] = {
154+
{ "!_JSON", "0.0", "/in development/", },
155+
{ "!_TAG_FILE", "2", "/extended format; --format=1 will not append ;\" to lines/", },
156+
{ "!_TAG_KIND_DESCRIPTION", "D,macroparam", "/parameters inside macro definitions/", },
157+
{ "!_TAG_OUTPUT_", "slash", "/slash or backslash/", },
158+
{ "!_TAG_PATT", "96", "/0 for no limit/", },
159+
{ "!_TAG_PROGRAM_AUTHOR", "Universal Ctags Team", "//", },
160+
{ "!_TAG_PROGRAM_N", "Universal Ctags", "/Derived from Exuberant Ctags/", },
161+
};
162+
163+
if (check_finding (tags_sorted_yes, exp_sorted_yes_partial, COUNT(exp_sorted_yes_partial),
164+
TAG_PARTIALMATCH) != 0)
165+
return 1;
166+
167+
/*
168+
* sorted=no
169+
*/
170+
const char *tags_sorted_no = "./ptag-sort-no.tags";
171+
struct expectation exp_sorted_no [] = {
172+
{ "!_JSON_OUTPUT_VERSION", "0.0", "/in development/", },
173+
{ "!_TAG_FILE_FORMAT", "2", "/extended format; --format=1 will not append ;\" to lines/", },
174+
{ "!_TAG_FILE_SORTED", "0", "/0=unsorted, 1=sorted, 2=foldcase/", },
175+
{ "!_TAG_PROGRAM_AUTHOR", "Universal Ctags Team", "//", },
176+
{ "!_TAG_PROGRAM_NAME", "Universal Ctags", "/Derived from Exuberant Ctags/", },
177+
{ "!_TAG_PROGRAM_URL", "https://ctags.io/", "/official site/", },
178+
{ "!_TAG_PROGRAM_VERSION", "0.0.0", "/9b73623f/", },
179+
{ "!_TAG_OUTPUT_MODE", "u-ctags", "/u-ctags or e-ctags/", },
180+
{ "!_TAG_OUTPUT_FILESEP", "slash", "/slash or backslash/", },
181+
{ "!_TAG_PATTERN_LENGTH_LIMIT", "96", "/0 for no limit/", },
182+
{ "!_TAG_KIND_DESCRIPTION!C", "d,macro", "/macro definitions/", },
183+
{ "!_TAG_KIND_DESCRIPTION!EmacsLisp", "u,unknown", "/unknown type of definitions/", },
184+
};
185+
186+
if (check_finding (tags_sorted_no, exp_sorted_no, COUNT(exp_sorted_no), TAG_FULLMATCH) != 0)
187+
return 1;
188+
189+
struct expectation exp_sorted_no_partial [] = {
190+
{ "!_JSON", "0.0", "/in development/", },
191+
{ "!_TAG_FILE", "2", "/extended format; --format=1 will not append ;\" to lines/", },
192+
{ "!_TAG_KIND_DESCRIPTION", "d,macro", "/macro definitions/", },
193+
{ "!_TAG_OUTPUT_", "u-ctags", "/u-ctags or e-ctags/", },
194+
{ "!_TAG_PATT", "96", "/0 for no limit/", },
195+
{ "!_TAG_PROGRAM_AUTHOR", "Universal Ctags Team", "//", },
196+
{ "!_TAG_PROGRAM_N", "Universal Ctags", "/Derived from Exuberant Ctags/", },
197+
};
198+
199+
if (check_finding (tags_sorted_no, exp_sorted_no_partial, COUNT(exp_sorted_no_partial),
200+
TAG_PARTIALMATCH) != 0)
201+
return 1;
202+
203+
/* No entry */
204+
const char *tags = "./ptag-sort-yes.tags";
205+
tagFileInfo info;
206+
207+
fprintf (stderr, "opening %s...", tags);
208+
tagFile *t = tagsOpen (tags, &info);
209+
if (!t)
210+
{
211+
fprintf (stderr, "unexpected result (t: %p, opened: %d, error_number: %d)\n",
212+
t, info.status.opened, info.status.error_number);
213+
return 1;
214+
}
215+
fprintf (stderr, "ok\n");
216+
217+
struct non_existing_testcase {
218+
const char *matchstr;
219+
int match;
220+
} ne_tcase [2] = {
221+
{
222+
.matchstr = "full",
223+
.match = TAG_FULLMATCH,
224+
},
225+
{
226+
.matchstr = "partial",
227+
.match = TAG_PARTIALMATCH,
228+
}
229+
};
230+
for (int i = 0; i < COUNT(ne_tcase); i++)
231+
{
232+
fprintf (stderr, "try to find non-existing tag in %s matching...",
233+
ne_tcase[i].matchstr);
234+
tagResult r = tagsFindPseudoTag (t, NULL, "!NO_SUCH_PTAG",
235+
ne_tcase[i].match);
236+
int err = tagsGetErrno (t);
237+
if (r == TagSuccess)
238+
{
239+
fprintf (stderr, "found one unexpectedly\n");
240+
return 1;
241+
}
242+
else if (err != 0)
243+
{
244+
fprintf (stderr, "unexpected errno: %d\n", err);
245+
return 1;
246+
}
247+
fprintf (stderr, "ok\n");
248+
}
249+
250+
fprintf (stderr, "closing the tag file...");
251+
if (tagsClose (t) != TagSuccess)
252+
{
253+
fprintf (stderr, "unexpected result\n");
254+
return 1;
255+
}
256+
fprintf (stderr, "ok\n");
257+
258+
return 0;
259+
}

0 commit comments

Comments
 (0)