From 29b362ae5b8914730d6100647b02ff9519b3914a Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Thu, 10 Feb 2022 04:17:23 +0900 Subject: [PATCH] readtags: add -C/--canonicalize-input option Partially close #3168. TODO: * revise code, Signed-off-by: Masatake YAMATO --- .../drive-letter0.tags | 12 ++ .../drive-letter1.tags | 12 ++ .../exit-expected.txt | 1 + .../good0.tags | 34 +++++ .../good1.tags | 34 +++++ .../good2.tags | 34 +++++ .../run.sh | 34 +++++ .../stderr-expected.txt | 7 + .../stdout-expected.txt | 74 ++++++++++ extra-cmds/readtags-cmd.c | 132 +++++++++++++++--- 10 files changed, 357 insertions(+), 17 deletions(-) create mode 100644 Tmain/readtags-canonicalize-input-names.d/drive-letter0.tags create mode 100644 Tmain/readtags-canonicalize-input-names.d/drive-letter1.tags create mode 100644 Tmain/readtags-canonicalize-input-names.d/exit-expected.txt create mode 100644 Tmain/readtags-canonicalize-input-names.d/good0.tags create mode 100644 Tmain/readtags-canonicalize-input-names.d/good1.tags create mode 100644 Tmain/readtags-canonicalize-input-names.d/good2.tags create mode 100644 Tmain/readtags-canonicalize-input-names.d/run.sh create mode 100644 Tmain/readtags-canonicalize-input-names.d/stderr-expected.txt create mode 100644 Tmain/readtags-canonicalize-input-names.d/stdout-expected.txt diff --git a/Tmain/readtags-canonicalize-input-names.d/drive-letter0.tags b/Tmain/readtags-canonicalize-input-names.d/drive-letter0.tags new file mode 100644 index 0000000000..a53494250c --- /dev/null +++ b/Tmain/readtags-canonicalize-input-names.d/drive-letter0.tags @@ -0,0 +1,12 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/ +!_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_PROC_CWD C:\tmp // +!_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 5.9.0 /e08db39a3/ +a_fn xyz/a.c /^static void a_fn(void) {}$/;" f typeref:typename:void file: diff --git a/Tmain/readtags-canonicalize-input-names.d/drive-letter1.tags b/Tmain/readtags-canonicalize-input-names.d/drive-letter1.tags new file mode 100644 index 0000000000..80d490162e --- /dev/null +++ b/Tmain/readtags-canonicalize-input-names.d/drive-letter1.tags @@ -0,0 +1,12 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/ +!_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_PROC_CWD D:/tmp // +!_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 5.9.0 /e08db39a3/ +a_fn xyz/a.c /^static void a_fn(void) {}$/;" f typeref:typename:void file: diff --git a/Tmain/readtags-canonicalize-input-names.d/exit-expected.txt b/Tmain/readtags-canonicalize-input-names.d/exit-expected.txt new file mode 100644 index 0000000000..573541ac97 --- /dev/null +++ b/Tmain/readtags-canonicalize-input-names.d/exit-expected.txt @@ -0,0 +1 @@ +0 diff --git a/Tmain/readtags-canonicalize-input-names.d/good0.tags b/Tmain/readtags-canonicalize-input-names.d/good0.tags new file mode 100644 index 0000000000..ce9edc1792 --- /dev/null +++ b/Tmain/readtags-canonicalize-input-names.d/good0.tags @@ -0,0 +1,34 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/ +!_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_PROC_CWD /tmp/abc/ // +!_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 5.9.0 /e08db39a3/ +a_fn xyz/a.c /^static void a_fn(void) {}$/;" f typeref:typename:void file: +b_fn b.c /^static void b_fn(void) {}$/;" f typeref:typename:void file: +c_fn ./c.c /^static void c_fn(void) {}$/;" f typeref:typename:void file: +d_fn ./../d.c /^static void d_fn(void) {}$/;" f typeref:typename:void file: +e_fn ../e.c /^static void e_fn(void) {}$/;" f typeref:typename:void file: +f_fn ../../f.c /^static void f_fn(void) {}$/;" f typeref:typename:void file: +g_fn ../../../g.c /^static void g_fn(void) {}$/;" f typeref:typename:void file: +h_fn ../../../../h.c /^static void h_fn(void) {}$/;" f typeref:typename:void file: +i_fn .././../../../i.c /^static void i_fn(void) {}$/;" f typeref:typename:void file: +j_fn .././../j.c /^static void j_fn(void) {}$/;" f typeref:typename:void file: +k_fn .././.././././k.c /^static void k_fn(void) {}$/;" f typeref:typename:void file: +l_fn ././././././l.c /^static void l_fn(void) {}$/;" f typeref:typename:void file: +m_fn ./././../abc/./m.c /^static void m_fn(void) {}$/;" f typeref:typename:void file: +n_fn ./././../abc/X/.././n.c /^static void n_fn(void) {}$/;" f typeref:typename:void file: +o_fn .////.///////./////..//abc//X//..///.///o.c /^static void o_fn(void) {}$/;" f typeref:typename:void file: +p_fn ./../xyz/p.c /^static void p_fn(void) {}$/;" f typeref:typename:void file: +q_fn ./../xyz/../././xyz/q.c /^static void q_fn(void) {}$/;" f typeref:typename:void file: +r_fn /r.c /^static void r_fn(void) {}$/;" f typeref:typename:void file: +s_fn /../s.c /^static void s_fn(void) {}$/;" f typeref:typename:void file: +t_fn /../././t.c /^static void t_fn(void) {}$/;" f typeref:typename:void file: +u_fn /../././tmp/./u.c /^static void u_fn(void) {}$/;" f typeref:typename:void file: +v_fn /../././tmp/./../tmp/abc/./v.c /^static void v_fn(void) {}$/;" f typeref:typename:void file: +w_fn /../././tmp/./../tmp/../abc/./w.c /^static void w_fn(void) {}$/;" f typeref:typename:void file: diff --git a/Tmain/readtags-canonicalize-input-names.d/good1.tags b/Tmain/readtags-canonicalize-input-names.d/good1.tags new file mode 100644 index 0000000000..c642be4196 --- /dev/null +++ b/Tmain/readtags-canonicalize-input-names.d/good1.tags @@ -0,0 +1,34 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/ +!_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_PROC_CWD /tmp/abc/../././abc/../abc // +!_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 5.9.0 /e08db39a3/ +a_fn xyz/a.c /^static void a_fn(void) {}$/;" f typeref:typename:void file: +b_fn b.c /^static void b_fn(void) {}$/;" f typeref:typename:void file: +c_fn ./c.c /^static void c_fn(void) {}$/;" f typeref:typename:void file: +d_fn ./../d.c /^static void d_fn(void) {}$/;" f typeref:typename:void file: +e_fn ../e.c /^static void e_fn(void) {}$/;" f typeref:typename:void file: +f_fn ../../f.c /^static void f_fn(void) {}$/;" f typeref:typename:void file: +g_fn ../../../g.c /^static void g_fn(void) {}$/;" f typeref:typename:void file: +h_fn ../../../../h.c /^static void h_fn(void) {}$/;" f typeref:typename:void file: +i_fn .././../../../i.c /^static void i_fn(void) {}$/;" f typeref:typename:void file: +j_fn .././../j.c /^static void j_fn(void) {}$/;" f typeref:typename:void file: +k_fn .././.././././k.c /^static void k_fn(void) {}$/;" f typeref:typename:void file: +l_fn ././././././l.c /^static void l_fn(void) {}$/;" f typeref:typename:void file: +m_fn ./././../abc/./m.c /^static void m_fn(void) {}$/;" f typeref:typename:void file: +n_fn ./././../abc/X/.././n.c /^static void n_fn(void) {}$/;" f typeref:typename:void file: +o_fn .////.///////./////..//abc//X//..///.///o.c /^static void o_fn(void) {}$/;" f typeref:typename:void file: +p_fn ./../xyz/p.c /^static void p_fn(void) {}$/;" f typeref:typename:void file: +q_fn ./../xyz/../././xyz/q.c /^static void q_fn(void) {}$/;" f typeref:typename:void file: +r_fn /r.c /^static void r_fn(void) {}$/;" f typeref:typename:void file: +s_fn /../s.c /^static void s_fn(void) {}$/;" f typeref:typename:void file: +t_fn /../././t.c /^static void t_fn(void) {}$/;" f typeref:typename:void file: +u_fn /../././tmp/./u.c /^static void u_fn(void) {}$/;" f typeref:typename:void file: +v_fn /../././tmp/./../tmp/abc/./v.c /^static void v_fn(void) {}$/;" f typeref:typename:void file: +w_fn /../././tmp/./../tmp/../abc/./w.c /^static void w_fn(void) {}$/;" f typeref:typename:void file: diff --git a/Tmain/readtags-canonicalize-input-names.d/good2.tags b/Tmain/readtags-canonicalize-input-names.d/good2.tags new file mode 100644 index 0000000000..b64e588bd0 --- /dev/null +++ b/Tmain/readtags-canonicalize-input-names.d/good2.tags @@ -0,0 +1,34 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/ +!_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_PROC_CWD /tmp/abc/../././abc/../abc/ // +!_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 5.9.0 /e08db39a3/ +a_fn xyz/a.c /^static void a_fn(void) {}$/;" f typeref:typename:void file: +b_fn b.c /^static void b_fn(void) {}$/;" f typeref:typename:void file: +c_fn ./c.c /^static void c_fn(void) {}$/;" f typeref:typename:void file: +d_fn ./../d.c /^static void d_fn(void) {}$/;" f typeref:typename:void file: +e_fn ../e.c /^static void e_fn(void) {}$/;" f typeref:typename:void file: +f_fn ../../f.c /^static void f_fn(void) {}$/;" f typeref:typename:void file: +g_fn ../../../g.c /^static void g_fn(void) {}$/;" f typeref:typename:void file: +h_fn ../../../../h.c /^static void h_fn(void) {}$/;" f typeref:typename:void file: +i_fn .././../../../i.c /^static void i_fn(void) {}$/;" f typeref:typename:void file: +j_fn .././../j.c /^static void j_fn(void) {}$/;" f typeref:typename:void file: +k_fn .././.././././k.c /^static void k_fn(void) {}$/;" f typeref:typename:void file: +l_fn ././././././l.c /^static void l_fn(void) {}$/;" f typeref:typename:void file: +m_fn ./././../abc/./m.c /^static void m_fn(void) {}$/;" f typeref:typename:void file: +n_fn ./././../abc/X/.././n.c /^static void n_fn(void) {}$/;" f typeref:typename:void file: +o_fn .////.///////./////..//abc//X//..///.///o.c /^static void o_fn(void) {}$/;" f typeref:typename:void file: +p_fn ./../xyz/p.c /^static void p_fn(void) {}$/;" f typeref:typename:void file: +q_fn ./../xyz/../././xyz/q.c /^static void q_fn(void) {}$/;" f typeref:typename:void file: +r_fn /r.c /^static void r_fn(void) {}$/;" f typeref:typename:void file: +s_fn /../s.c /^static void s_fn(void) {}$/;" f typeref:typename:void file: +t_fn /../././t.c /^static void t_fn(void) {}$/;" f typeref:typename:void file: +u_fn /../././tmp/./u.c /^static void u_fn(void) {}$/;" f typeref:typename:void file: +v_fn /../././tmp/./../tmp/abc/./v.c /^static void v_fn(void) {}$/;" f typeref:typename:void file: +w_fn /../././tmp/./../tmp/../abc/./w.c /^static void w_fn(void) {}$/;" f typeref:typename:void file: diff --git a/Tmain/readtags-canonicalize-input-names.d/run.sh b/Tmain/readtags-canonicalize-input-names.d/run.sh new file mode 100644 index 0000000000..f62059fd27 --- /dev/null +++ b/Tmain/readtags-canonicalize-input-names.d/run.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +# Copyright: 2021 Masatake YAMATO +# License: GPL-2 + +READTAGS=$3 + +. ../utils.sh + +#V="valgrind --leak-check=full -v" +V= + +if ! [ -x "${READTAGS}" ]; then + skip "no readtags" +fi + + +echo2() +{ + echo "$@" + echo "$@" 1>&2 +} + +run_test() +{ + echo2 "# $1" + "${READTAGS}" -F '(list $name "\t" $input "\n")' $2 -t $1 -l +} + +run_test good0.tags -C && +run_test good1.tags --canonicalize-input && +run_test good2.tags -C && +! run_test drive-letter0.tags --canonicalize-input && +! run_test drive-letter1.tags -C diff --git a/Tmain/readtags-canonicalize-input-names.d/stderr-expected.txt b/Tmain/readtags-canonicalize-input-names.d/stderr-expected.txt new file mode 100644 index 0000000000..4547f02c7d --- /dev/null +++ b/Tmain/readtags-canonicalize-input-names.d/stderr-expected.txt @@ -0,0 +1,7 @@ +# good0.tags +# good1.tags +# good2.tags +# drive-letter0.tags +!_TAG_PROC_CWD must start with '/': C:\tmp +# drive-letter1.tags +!_TAG_PROC_CWD must start with '/': D:/tmp diff --git a/Tmain/readtags-canonicalize-input-names.d/stdout-expected.txt b/Tmain/readtags-canonicalize-input-names.d/stdout-expected.txt new file mode 100644 index 0000000000..1846bd40ac --- /dev/null +++ b/Tmain/readtags-canonicalize-input-names.d/stdout-expected.txt @@ -0,0 +1,74 @@ +# good0.tags +a_fn xyz/a.c +b_fn b.c +c_fn c.c +d_fn /tmp/d.c +e_fn /tmp/e.c +f_fn /f.c +g_fn /g.c +h_fn /h.c +i_fn /i.c +j_fn /j.c +k_fn /k.c +l_fn l.c +m_fn m.c +n_fn n.c +o_fn o.c +p_fn /tmp/xyz/p.c +q_fn /tmp/xyz/q.c +r_fn /r.c +s_fn /s.c +t_fn /t.c +u_fn /tmp/u.c +v_fn /tmp/abc/v.c +w_fn /abc/w.c +# good1.tags +a_fn xyz/a.c +b_fn b.c +c_fn c.c +d_fn /tmp/d.c +e_fn /tmp/e.c +f_fn /f.c +g_fn /g.c +h_fn /h.c +i_fn /i.c +j_fn /j.c +k_fn /k.c +l_fn l.c +m_fn m.c +n_fn n.c +o_fn o.c +p_fn /tmp/xyz/p.c +q_fn /tmp/xyz/q.c +r_fn /r.c +s_fn /s.c +t_fn /t.c +u_fn /tmp/u.c +v_fn /tmp/abc/v.c +w_fn /abc/w.c +# good2.tags +a_fn xyz/a.c +b_fn b.c +c_fn c.c +d_fn /tmp/d.c +e_fn /tmp/e.c +f_fn /f.c +g_fn /g.c +h_fn /h.c +i_fn /i.c +j_fn /j.c +k_fn /k.c +l_fn l.c +m_fn m.c +n_fn n.c +o_fn o.c +p_fn /tmp/xyz/p.c +q_fn /tmp/xyz/q.c +r_fn /r.c +s_fn /s.c +t_fn /t.c +u_fn /tmp/u.c +v_fn /tmp/abc/v.c +w_fn /abc/w.c +# drive-letter0.tags +# drive-letter1.tags diff --git a/extra-cmds/readtags-cmd.c b/extra-cmds/readtags-cmd.c index 15a416223e..d013f6a018 100644 --- a/extra-cmds/readtags-cmd.c +++ b/extra-cmds/readtags-cmd.c @@ -13,6 +13,11 @@ #include "printtags.h" #include "routines.h" #include "routines_p.h" + +#include "vstring.h" +#include "htable.h" +#include "fname.h" + #include /* strerror */ #include /* exit */ #include /* stderr */ @@ -24,6 +29,10 @@ typedef struct sReadOption { int matchOpts; } readOptions; +struct canonWorkArea { + struct canonFnameCacheTable *cacheTable; +}; + static const char *TagFileName = "tags"; static const char *ProgramName; static int debugMode; @@ -187,7 +196,8 @@ static int compareTagEntry (const void *a, const void *b) static void walkTags (tagFile *const file, tagEntry *first_entry, tagResult (* nextfn) (tagFile *const, tagEntry *), - void (* actionfn) (const tagEntry *, void *), void *data) + void (* actionfn) (const tagEntry *, void *), void *data, + struct canonWorkArea *canon) { struct tagEntryArray *a = NULL; @@ -196,9 +206,19 @@ static void walkTags (tagFile *const file, tagEntry *first_entry, do { + tagEntry *shadow = first_entry; + tagEntry shadowRec; + if (canon) + { + shadowRec = *first_entry; + shadow = &shadowRec; + shadow->file = canonicalizeRelativeFileName (canon->cacheTable, + first_entry->file); + } + if (Qualifier) { - int i = q_is_acceptable (Qualifier, first_entry); + int i = q_is_acceptable (Qualifier, shadow); switch (i) { case Q_REJECT: @@ -210,11 +230,11 @@ static void walkTags (tagFile *const file, tagEntry *first_entry, if (a) { - tagEntry *e = copyTag (first_entry); + tagEntry *e = copyTag (shadow); tagEntryArrayPush (a, e); } else - (* actionfn) (first_entry, data); + (* actionfn) (shadow, data); } while ( (*nextfn) (file, first_entry) == TagSuccess); int err = tagsGetErrno (file); @@ -237,10 +257,23 @@ static void walkTags (tagFile *const file, tagEntry *first_entry, #else static void walkTags (tagFile *const file, tagEntry *first_entry, tagResult (* nextfn) (tagFile *const, tagEntry *), - void (* actionfn) (const tagEntry *, void *), void *data) + void (* actionfn) (const tagEntry *, void *), void *data, + struct canonWorkArea *canon) { do - (* actionfn) (first_entry, data); + { + tagEntry *shadow = first_entry; + tagEntry shadowRec; + if (canon) + { + shadow = &shadowRec; + shadowRec = *first_entry; + shadow->file = canonicalizeRelativeFileName (canon->cacheTable, + first_entry->file); + } + + (* actionfn) (shadow, data); + } while ( (*nextfn) (file, first_entry) == TagSuccess); int err = tagsGetErrno (file); @@ -290,6 +323,42 @@ static void removeTagFile (void) eFree ((char *)TagFileName); } +static const char *loadCtagsCWD (tagFile *const file, tagEntry *pentry) +{ + if (tagsFindPseudoTag (file, pentry, "!_TAG_PROC_CWD", + TAG_FULLMATCH) != TagSuccess) + { + int err = tagsGetErrno (file); + if (!err) + { + fprintf (stderr, "%s: no !_TAG_PROC_CWD in %s\n", + ProgramName, TagFileName); + exit (1); + } + + fprintf (stderr, "%s: cannot find !_TAG_PROC_CWD in %s: %s\n", + ProgramName, TagFileName, tagsStrerror (err)); + exit (1); + } + + if (pentry->file[0] != '/') + { + fputs ("!_TAG_PROC_CWD must start with '/': ", stderr); + tagsPrintValue (pentry->file, 1, NULL, stderr); + fputc ('\n', stderr); + exit (1); + } + + return pentry->file; +} + +static struct canonFnameCacheTable *makeCanonFnameCacheTable (tagFile *const file) +{ + tagEntry pentry; + const char *cwd = loadCtagsCWD (file, &pentry); + return canonFnameCacheTableNew (cwd); +} + static tagFile *openTags (const char *const filePath, tagFileInfo *const info) { if (strcmp (filePath, "-") == 0) @@ -336,7 +405,7 @@ static int hasPsuedoTag (tagFile *const file, } static void findTag (const char *const name, readOptions *readOpts, - tagPrintOptions *printOpts) + tagPrintOptions *printOpts, struct canonWorkArea *canon) { tagFileInfo info; tagEntry entry; @@ -351,6 +420,9 @@ static void findTag (const char *const name, readOptions *readOpts, exit (1); } + if (canon && canon->cacheTable == NULL) + canon->cacheTable = makeCanonFnameCacheTable (file); + if (printOpts->escaping) { printOpts->escapingInputField = 0; @@ -379,7 +451,8 @@ static void findTag (const char *const name, readOptions *readOpts, #ifdef READTAGS_DSL Formatter? printTagWithFormatter: #endif - printTag, printOpts); + printTag, printOpts, + canon); else if ((err = tagsGetErrno (file)) != 0) { fprintf (stderr, "%s: error in tagsFind(): %s\n", @@ -390,7 +463,8 @@ static void findTag (const char *const name, readOptions *readOpts, tagsClose (file); } -static void listTags (int pseudoTags, tagPrintOptions *printOpts) +static void listTags (int pseudoTags, tagPrintOptions *printOpts, + struct canonWorkArea *canon) { tagFileInfo info; tagEntry entry; @@ -407,6 +481,9 @@ static void listTags (int pseudoTags, tagPrintOptions *printOpts) exit (1); } + if (pseudoTags == 0 && canon && canon->cacheTable == NULL) + canon->cacheTable = makeCanonFnameCacheTable (file); + if (printOpts->escaping) { printOpts->escapingInputField = 0; @@ -418,8 +495,8 @@ static void listTags (int pseudoTags, tagPrintOptions *printOpts) if (pseudoTags) { if (tagsFirstPseudoTag (file, &entry) == TagSuccess) - walkTags (file, &entry, tagsNextPseudoTag, printPseudoTag, - printOpts); + walkTags (file, &entry, tagsNextPseudoTag, printPseudoTag, printOpts, + canon); else if ((err = tagsGetErrno (file)) != 0) { fprintf (stderr, "%s: error in tagsFirstPseudoTag(): %s\n", @@ -435,7 +512,8 @@ static void listTags (int pseudoTags, tagPrintOptions *printOpts) #ifdef READTAGS_DSL Formatter? printTagWithFormatter: #endif - printTag, printOpts); + printTag, printOpts, + canon); else if ((err = tagsGetErrno (file)) != 0) { fprintf (stderr, "%s: error in tagsFirst(): %s\n", @@ -488,6 +566,8 @@ static const char *const Usage = " -s[0|1|2] | --override-sort-detection METHOD\n" " Override sort detection of tag file.\n" " METHOD: unsorted|sorted|foldcase\n" + " -C | --canonicalize-input\n" + " Reduct '..' and '.' in input fields.\n" #ifdef READTAGS_DSL " -F EXP | --formatter EXP\n" " Format the tags listed by ACTION with EXP when printing.\n" @@ -571,12 +651,19 @@ extern int main (int argc, char **argv) int actionSupplied = 0; int i; int ignore_prefix = 0; + tagPrintOptions printOpts; readOptions readOpts; memset (&printOpts, 0, sizeof (printOpts)); memset (&readOpts, 0, sizeof (readOpts)); + struct canonWorkArea canonWorkArea = { + .cacheTable = NULL, + }; + struct canonWorkArea *canon = NULL; + + ProgramName = argv [0]; setExecutableName (ProgramName); if (argc == 1) @@ -586,7 +673,7 @@ extern int main (int argc, char **argv) const char *const arg = argv [i]; if (ignore_prefix || arg [0] != '-') { - findTag (arg, &readOpts, &printOpts); + findTag (arg, &readOpts, &printOpts, canon); actionSupplied = 1; } else if (arg [0] == '-' && arg [1] == '\0') @@ -598,7 +685,7 @@ extern int main (int argc, char **argv) debugMode++; else if (strcmp (optname, "list-pseudo-tags") == 0) { - listTags (1, &printOpts); + listTags (1, &printOpts, NULL); actionSupplied = 1; } else if (strcmp (optname, "help") == 0) @@ -643,7 +730,7 @@ extern int main (int argc, char **argv) readOpts.matchOpts |= TAG_PARTIALMATCH; else if (strcmp (optname, "list") == 0) { - listTags (0, &printOpts); + listTags (0, &printOpts, canon); actionSupplied = 1; } else if (strcmp (optname, "line-number") == 0) @@ -683,6 +770,8 @@ extern int main (int argc, char **argv) exit (1); } } + else if (strcmp (optname, "canonicalize-input") == 0) + canon = &canonWorkArea; #ifdef READTAGS_DSL else if (strcmp (optname, "filter") == 0) { @@ -740,7 +829,7 @@ extern int main (int argc, char **argv) switch (arg [j]) { case 'd': debugMode++; break; - case 'D': listTags (1, &printOpts); actionSupplied = 1; break; + case 'D': listTags (1, &printOpts, NULL); actionSupplied = 1; break; case 'h': printUsage (stdout, 0); break; #ifdef READTAGS_DSL case 'H': @@ -764,7 +853,7 @@ extern int main (int argc, char **argv) case 'e': printOpts.extensionFields = 1; break; case 'i': readOpts.matchOpts |= TAG_IGNORECASE; break; case 'p': readOpts.matchOpts |= TAG_PARTIALMATCH; break; - case 'l': listTags (0, &printOpts); actionSupplied = 1; break; + case 'l': listTags (0, &printOpts, canon); actionSupplied = 1; break; case 'n': printOpts.lineNumber = 1; break; case 't': if (arg [j+1] != '\0') @@ -787,6 +876,9 @@ extern int main (int argc, char **argv) else printUsage(stderr, 1); break; + case 'C': + canon = &canonWorkArea; + break; #ifdef READTAGS_DSL case 'Q': if (i + 1 == argc) @@ -834,5 +926,11 @@ extern int main (int argc, char **argv) if (Formatter) f_destroy (Formatter); #endif + + if (canon) + { + if (canon->cacheTable) + canonFnameCacheTableDelete (canon->cacheTable); + } return 0; }