See readtags(1) to know how to use readtags. This section is for discussing some notable topics for
client tools.
BuildFilter/SorterExpressions
Certain escape sequences in expressions are recognized by readtags. For example, when searching for a tag
that matches a\?b, if using a filter expression like '(eq?$name"a\?b")', since \? is translated into a
single ? by readtags, it actually searches for a?b.
Another problem is if a single quote appear in filter expressions (which is also wrapped by single
quotes), it terminates the expression, producing broken expressions, and may even cause unintended shell
injection. Single quotes can be escaped using '"'"'.
So, client tools need to:
• Replace \ by \\
• Replace ' by '"'"'
inside the expressions. If the expression also contains strings, " in the strings needs to be replaced by
\".
Client tools written in Lisp could build the expression using lists. prin1 (in Common Lisp style Lisps)
and write (in Scheme style Lisps) can translate the list into a string that can be directly used. For
example, in EmacsLisp:
(let ((name "hi"))
(prin1 `(eq? $name ,name)))
=> "(eq\\? $name "hi")"
The "?" is escaped, and readtags can handle it. Scheme style Lisps should do proper escaping so the
expression readtags gets is just the expression passed into write. Common Lisp style Lisps may produce
unrecognized escape sequences by readtags, like \#. Readtags provides some aliases for these Lisps:
• Use true for #t.
• Use false for #f.
• Use nil or () for ().
• Use (string->regexp"PATTERN") for #/PATTERN/. Use (string->regexp"PATTERN":case-foldtrue) for
#/PATTERN/i. Notice that string->regexp doesn't require escaping "/" in the pattern.
Notice that even when the client tool uses this method, ' still needs to be replaced by '"'"' to prevent
broken expressions and shell injection.
Another thing to notice is that missing fields are represented by #f, and applying string operators to
them will produce an error. You should always check if a field is missing before applying string
operators. See the "Filtering" section in readtags(1) to know how to do this. Run "readtags -H filter" to
see which operators take string arguments.
ParseReadtagsOutput
In the output of readtags, tabs can appear in all field values (e.g., the tag name itself could contain
tabs), which makes it hard to split the line into fields. Client tools should use the -E option, which
keeps the escape sequences in the tags file, so the only field that could contain tabs is the pattern
field.
The pattern field could:
• Use a line number. It will look like number;" (e.g. 10;").
• Use a search pattern. It will look like /pattern/;" or ?pattern?;". Notice that the search pattern
could contain tabs.
• Combine these two, like number;/pattern/;" or number;?pattern?;".
These are true for tags files using extended format, which is the default one. The legacy format (i.e.
--format=1) doesn't include the semicolons. It's old and barely used, so we won't discuss it here.
Client tools could split the line using the following steps:
• Find the first 2 tabs in the line, so we get the name and input field.
• From the 2nd tab:
• If a / follows, then the pattern delimiter is /.
• If a ? follows, then the pattern delimiter is ?.
• If a number follows, then:
• If a ;/ follows the number, then the delimiter is /.
• If a ;? follows the number, then the delimiter is ?.
• If a ;" follows the number, then the field uses only line number, and there's no pattern delimiter
(since there's no regex pattern). In this case the pattern field ends at the 3rd tab.
• After the opening delimiter, find the next unescaped pattern delimiter, and that's the closing
delimiter. It will be followed by ;" and then a tab. That's the end of the pattern field. By
"unescaped pattern delimiter", we mean there's an even number (including 0) of backslashes before it.
• From here, split the rest of the line into fields by tabs.
Then, the escape sequences in fields other than the pattern field should be translated. See "Proposal" in
tags(5) to know about all the escape sequences.
MakeUseofthePatternField
The pattern field specifies how to find a tag in its source file. The code generating this field seems to
have a long history, so there are some pitfalls and it's a bit hard to handle. A client tool could simply
require the line: field and jump to the line it specifies, to avoid using the pattern field. But anyway,
we'll discuss how to make the best use of it here.
You should take the words here merely as suggestions, and not standards. A client tool could definitely
develop better (or simpler) ways to use the pattern field.
From the last section, we know the pattern field could contain a line number and a search pattern. When
it only contains the line number, handling it is easy: you simply go to that line.
The search pattern resembles an EX command, but as we'll see later, it's actually not a valid one, so
some manual work are required to process it.
The search pattern could look like /pat/, called "forward search pattern", or ?pat?, called "backward
search pattern". Using a search pattern means even if the source file is updated, as long as the part
containing the tag doesn't change, we could still locate the tag correctly by searching.
When the pattern field only contains the search pattern, you just search for it. The search direction
(forward/backward) doesn't matter, as it's decided solely by whether the -B option is enabled, and not
the actual context. You could always start the search from say the beginning of the file.
When both the search pattern and the line number are presented, you could make good use of the line
number, by going to the line first, then searching for the nearest occurrence of the pattern. A way to do
this is to search both forward and backward for the pattern, and when there is a occurrence on both
sides, go to the nearer one.
What's good about this is when there are multiple identical lines in the source file (e.g. the COMMON
block in Fortran), this could help us find the correct one, even after the source file is updated and the
tag position is shifted by a few lines.
Now let's discuss how to search for the pattern. After you trim the / or ? around it, the pattern
resembles a regex pattern. It should be a regex pattern, as required by being a valid EX command, but
it's actually not, as you'll see below.
It could begin with a ^, which means the pattern starts from the beginning of a line. It could also end
with an unescaped$ which means the pattern ends at the end of a line. Let's keep this information, and
trim them too.
Now the remaining part is the actual string containing the tag. Some characters are escaped:
• \.
• $, but only at the end of the string.
• /, but only in forward search patterns.
• ?, but only in backward search patterns.
You need to unescape these to get the literal string. Now you could convert this literal string to a
regexp that matches it (by escaping, like re.escape in Python or regexp-quote in Elisp), and assemble it
with ^ or $ if the pattern originally has it, and finally search for the tag using this regexp.
Remark:AboutaPreviousFormatofthePatternField
In some earlier versions of Universal Ctags, the line number in the pattern field is the actual line
number minus one, for forward search patterns; or plus one, for backward search patterns. The idea is to
resemble an EX command: you go to the line, then search forward/backward for the pattern, and you can
always find the correct one. But this denies the purpose of using a search pattern: to tolerate file
updates. For example, the tag is at line 50, according to this scheme, the pattern field should be:
49;/pat/;"
Then let's assume that some code above are removed, and the tag is now at line 45. Now you can't find it
if you search forward from line 49.
Due to this reason, Universal Ctags turns to use the actual line number. A client tool could distinguish
them by the TAG_OUTPUT_EXCMD pseudo tag, it's "combine" for the old scheme, and "combineV2" for the
present scheme. But probably there's no need to treat them differently, since "search for the nearest
occurrence from the line" gives good result on both schemes.