Skip to content

Commit

Permalink
Merge pull request #3500 from masatake/update-libreadtags
Browse files Browse the repository at this point in the history
Update libreadtags
  • Loading branch information
masatake authored Oct 9, 2022
2 parents 5d506a1 + 4b56af0 commit 4089cff
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 1 deletion.
1 change: 1 addition & 0 deletions libreadtags/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ tests/*.trs
tests/test-api-tagsClose
tests/test-api-tagsFind
tests/test-api-tagsFirst
tests/test-api-tagsFindPseudoTag
tests/test-api-tagsFirstPseudoTag
tests/test-api-tagsOpen
tests/test-api-tagsSetSortType
Expand Down
3 changes: 3 additions & 0 deletions libreadtags/NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
used fseek and ftell. In this version, they are replaced with _fseeki64 and
_ftelli64.

- add a new API function (tagsFindPseudoTag) for finding a pseudo tag for
given name.

# Version 0.1.0

- propagate internal errors to caller
Expand Down
33 changes: 33 additions & 0 deletions libreadtags/readtags.c
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,39 @@ extern tagResult tagsNextPseudoTag (tagFile *const file, tagEntry *const entry)
return findPseudoTag (file, 0, entry);
}

extern tagResult tagsFindPseudoTag (tagFile *const file, tagEntry *const entry,
const char *const name, const int match)
{
size_t len;
tagEntry entry0;
tagEntry *entryp = entry? entry: &entry0;

tagResult r = tagsFirstPseudoTag (file, entryp);
if (r != TagSuccess)
return r;

if (match & TAG_PARTIALMATCH)
len = strlen (name);

do
{
if (match & TAG_PARTIALMATCH)
{
if (strncmp (entryp->name, name, len) == 0)
return TagSuccess;
}
else
{
if (strcmp (entryp->name, name) == 0)
return TagSuccess;
}
r = tagsNextPseudoTag (file, entryp);
}
while (r == TagSuccess);

return r;
}

extern tagResult tagsClose (tagFile *const file)
{
tagResult result = TagFailure;
Expand Down
17 changes: 16 additions & 1 deletion libreadtags/readtags.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ typedef enum {
#define sortType tagSortType
#endif

/* Options for tagsFind() */
/* Options for tagsFind() and tagsFindPseudoTag() */
#define TAG_FULLMATCH 0x0
#define TAG_PARTIALMATCH 0x1

Expand Down Expand Up @@ -270,6 +270,21 @@ extern tagResult tagsFirstPseudoTag (tagFile *const file, tagEntry *const entry)
*/
extern tagResult tagsNextPseudoTag (tagFile *const file, tagEntry *const entry);

/*
* Does the same as tagsFind(), but is specialized to pseudo tags.
* The available values for `match' are:
*
* TAG_PARTIALMATCH
* Tags whose leading characters match `name' will qualify.
*
* TAG_FULLMATCH
* Only tags whose full lengths match `name' will qualify.
*
* NOTE: unlike tagsFind(), this function uses liner-searching even if
* the tags file is sorted.
*/
extern tagResult tagsFindPseudoTag (tagFile *const file, tagEntry *const entry, const char *const name, const int match);

/*
* Call tagsClose() at completion of reading the tag file, which will
* close the file and free any internal memory allocated. The function will
Expand Down
5 changes: 5 additions & 0 deletions libreadtags/tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ TESTS = \
\
test-api-tagsOpen \
test-api-tagsFind \
test-api-tagsFindPseudoTag \
test-api-tagsFirstPseudoTag \
test-api-tagsFirst \
test-api-tagsClose \
Expand All @@ -17,6 +18,7 @@ check_PROGRAMS = \
\
test-api-tagsOpen \
test-api-tagsFind \
test-api-tagsFindPseudoTag \
test-api-tagsFirstPseudoTag \
test-api-tagsFirst \
test-api-tagsClose \
Expand Down Expand Up @@ -57,6 +59,9 @@ EXTRA_DIST += duplicated-names--sorted-no.tags
EXTRA_DIST += duplicated-names--sorted-foldcase.tags
EXTRA_DIST += broken-line-field-in-middle.tags

test_api_tagsFindPseudoTag = test-api-tagsFindPseudoTag.c
test_api_tagsFindPseudoTag_DEPENDENCIES = $(DEPS)

test_api_tagsFirstPseudoTag = test-api-tagsFirstPseudoTag.c
test_api_tagsFirstPseudoTag_DEPENDENCIES = $(DEPS)
EXTRA_DIST += ptag-sort-no.tags
Expand Down
259 changes: 259 additions & 0 deletions libreadtags/tests/test-api-tagsFindPseudoTag.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
/*
* Copyright (c) 2022, Masatake YAMATO
*
* This source code is released into the public domain.
*
* Testing tagsFindPseudoTag() API function
*/

#include "readtags.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


struct expectation {
char *name;
char *file;
char *pattern;
};

#define COUNT(x) (sizeof(x)/sizeof(x[0]))

static int
check_finding0 (tagFile *t, struct expectation *expectations, int count, int match)
{
tagEntry e;
struct expectation *x;
int err;

for (int i = 0; i < count; i++)
{
x = expectations + i;
fprintf (stderr, "[%d/%d] finding ptags in %s matching...", i + 1, count,
match == TAG_FULLMATCH? "full": "partial");
if (tagsFindPseudoTag (t, &e, x->name, match) != TagSuccess)
{
if ((err = tagsGetErrno (t)))
fprintf (stderr, "error: %d\n", err);
else
fprintf (stderr, "cannot find: %s\n", x->name);
return 1;
}
fprintf (stderr, "found\n");

fprintf (stderr, "checking name field...");
if (match == TAG_FULLMATCH)
{
if (!(e.name && strcmp (x->name, e.name) == 0))
{
fprintf (stderr, "unexpected: %s\n", e.name? e.name: "<NULL>");
return 1;
}
}
else
{
if (!(e.name && strncmp (x->name, e.name, strlen(x->name)) == 0))
{
fprintf (stderr, "unexpected: strncmp(%s, %s)\n",
x->name, e.name? e.name: "<NULL>");
return 1;
}
}
fprintf (stderr, "ok\n");

fprintf (stderr, "checking file field...");
if (!(e.file && strcmp (x->file, e.file) == 0))
{
fprintf (stderr, "unexpected: %s\n", e.file? e.file: "<NULL>");
return 1;
}
fprintf (stderr, "ok\n");

fprintf (stderr, "checking pattern field...");
if (!(e.address.pattern && strcmp (x->pattern, e.address.pattern) == 0))
{
fprintf (stderr, "unexpected: %s\n",
e.address.pattern? e.address.pattern: "<NULL>");
return 1;
}
fprintf (stderr, "ok\n");
}

return 0;
}

static int
check_finding(const char *tags, struct expectation *expectations, int count, int match)
{
tagFile *t;
tagFileInfo info;

fprintf (stderr, "opening %s...", tags);
t = tagsOpen (tags, &info);
if (!t)
{
fprintf (stderr, "unexpected result (t: %p, opened: %d, error_number: %d)\n",
t, info.status.opened, info.status.error_number);
return 1;
}
fprintf (stderr, "ok\n");

if (check_finding0 (t, expectations, count, match) != 0)
return 1;

fprintf (stderr, "closing the tag file...");
if (tagsClose (t) != TagSuccess)
{
fprintf (stderr, "unexpected result\n");
return 1;
}
fprintf (stderr, "ok\n");
return 0;
}

int
main (void)
{
char *srcdir = getenv ("srcdir");
if (srcdir)
{
if (chdir (srcdir) == -1)
{
perror ("chdir");
return 99;
}
}

/*
* sorted=yes
*/
const char *tags_sorted_yes = "./ptag-sort-yes.tags";
struct expectation exp_sorted_yes [] = {
{ "!_JSON_OUTPUT_VERSION", "0.0", "/in development/", },
{ "!_TAG_FILE_FORMAT", "2", "/extended format; --format=1 will not append ;\" to lines/", },
{ "!_TAG_FILE_SORTED", "1", "/0=unsorted, 1=sorted, 2=foldcase/", },
{ "!_TAG_KIND_DESCRIPTION!C", "D,macroparam", "/parameters inside macro definitions/", },
{ "!_TAG_KIND_DESCRIPTION!EmacsLisp", "C,custom", "/customizable variables/", },
{ "!_TAG_OUTPUT_FILESEP", "slash", "/slash or backslash/", },
{ "!_TAG_OUTPUT_MODE", "u-ctags", "/u-ctags or e-ctags/", },
{ "!_TAG_PATTERN_LENGTH_LIMIT", "96", "/0 for no limit/", },
{ "!_TAG_PROGRAM_AUTHOR", "Universal Ctags Team", "//", },
{ "!_TAG_PROGRAM_NAME", "Universal Ctags", "/Derived from Exuberant Ctags/", },
{ "!_TAG_PROGRAM_URL", "https://ctags.io/", "/official site/", },
{ "!_TAG_PROGRAM_VERSION", "0.0.0", "/9b73623f/", },
};

if (check_finding (tags_sorted_yes, exp_sorted_yes, COUNT(exp_sorted_yes),
TAG_FULLMATCH) != 0)
return 1;

struct expectation exp_sorted_yes_partial [] = {
{ "!_JSON", "0.0", "/in development/", },
{ "!_TAG_FILE", "2", "/extended format; --format=1 will not append ;\" to lines/", },
{ "!_TAG_KIND_DESCRIPTION", "D,macroparam", "/parameters inside macro definitions/", },
{ "!_TAG_OUTPUT_", "slash", "/slash or backslash/", },
{ "!_TAG_PATT", "96", "/0 for no limit/", },
{ "!_TAG_PROGRAM_AUTHOR", "Universal Ctags Team", "//", },
{ "!_TAG_PROGRAM_N", "Universal Ctags", "/Derived from Exuberant Ctags/", },
};

if (check_finding (tags_sorted_yes, exp_sorted_yes_partial, COUNT(exp_sorted_yes_partial),
TAG_PARTIALMATCH) != 0)
return 1;

/*
* sorted=no
*/
const char *tags_sorted_no = "./ptag-sort-no.tags";
struct expectation exp_sorted_no [] = {
{ "!_JSON_OUTPUT_VERSION", "0.0", "/in development/", },
{ "!_TAG_FILE_FORMAT", "2", "/extended format; --format=1 will not append ;\" to lines/", },
{ "!_TAG_FILE_SORTED", "0", "/0=unsorted, 1=sorted, 2=foldcase/", },
{ "!_TAG_PROGRAM_AUTHOR", "Universal Ctags Team", "//", },
{ "!_TAG_PROGRAM_NAME", "Universal Ctags", "/Derived from Exuberant Ctags/", },
{ "!_TAG_PROGRAM_URL", "https://ctags.io/", "/official site/", },
{ "!_TAG_PROGRAM_VERSION", "0.0.0", "/9b73623f/", },
{ "!_TAG_OUTPUT_MODE", "u-ctags", "/u-ctags or e-ctags/", },
{ "!_TAG_OUTPUT_FILESEP", "slash", "/slash or backslash/", },
{ "!_TAG_PATTERN_LENGTH_LIMIT", "96", "/0 for no limit/", },
{ "!_TAG_KIND_DESCRIPTION!C", "d,macro", "/macro definitions/", },
{ "!_TAG_KIND_DESCRIPTION!EmacsLisp", "u,unknown", "/unknown type of definitions/", },
};

if (check_finding (tags_sorted_no, exp_sorted_no, COUNT(exp_sorted_no), TAG_FULLMATCH) != 0)
return 1;

struct expectation exp_sorted_no_partial [] = {
{ "!_JSON", "0.0", "/in development/", },
{ "!_TAG_FILE", "2", "/extended format; --format=1 will not append ;\" to lines/", },
{ "!_TAG_KIND_DESCRIPTION", "d,macro", "/macro definitions/", },
{ "!_TAG_OUTPUT_", "u-ctags", "/u-ctags or e-ctags/", },
{ "!_TAG_PATT", "96", "/0 for no limit/", },
{ "!_TAG_PROGRAM_AUTHOR", "Universal Ctags Team", "//", },
{ "!_TAG_PROGRAM_N", "Universal Ctags", "/Derived from Exuberant Ctags/", },
};

if (check_finding (tags_sorted_no, exp_sorted_no_partial, COUNT(exp_sorted_no_partial),
TAG_PARTIALMATCH) != 0)
return 1;

/* No entry */
const char *tags = "./ptag-sort-yes.tags";
tagFileInfo info;

fprintf (stderr, "opening %s...", tags);
tagFile *t = tagsOpen (tags, &info);
if (!t)
{
fprintf (stderr, "unexpected result (t: %p, opened: %d, error_number: %d)\n",
t, info.status.opened, info.status.error_number);
return 1;
}
fprintf (stderr, "ok\n");

struct non_existing_testcase {
const char *matchstr;
int match;
} ne_tcase [2] = {
{
.matchstr = "full",
.match = TAG_FULLMATCH,
},
{
.matchstr = "partial",
.match = TAG_PARTIALMATCH,
}
};
for (int i = 0; i < COUNT(ne_tcase); i++)
{
fprintf (stderr, "try to find non-existing tag in %s matching...",
ne_tcase[i].matchstr);
tagResult r = tagsFindPseudoTag (t, NULL, "!NO_SUCH_PTAG",
ne_tcase[i].match);
int err = tagsGetErrno (t);
if (r == TagSuccess)
{
fprintf (stderr, "found one unexpectedly\n");
return 1;
}
else if (err != 0)
{
fprintf (stderr, "unexpected errno: %d\n", err);
return 1;
}
fprintf (stderr, "ok\n");
}

fprintf (stderr, "closing the tag file...");
if (tagsClose (t) != TagSuccess)
{
fprintf (stderr, "unexpected result\n");
return 1;
}
fprintf (stderr, "ok\n");

return 0;
}

0 comments on commit 4089cff

Please sign in to comment.