Skip to content

Commit 5592e4a

Browse files
committed
main: add --oneshot=<filename> option
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
1 parent aec47d2 commit 5592e4a

File tree

7 files changed

+165
-0
lines changed

7 files changed

+165
-0
lines changed

Tmain/oneshot.d/run.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright: 2026 Masatake YAMATO
2+
# License: GPL-2
3+
4+
CTAGS=$1
5+
6+
gen()
7+
{
8+
cat <<EOF
9+
int v;
10+
int f(void)
11+
{
12+
return 0;
13+
}
14+
EOF
15+
}
16+
17+
gen | ${CTAGS} --quiet --options=NONE --oneshot=sort-no.c --sort=no
18+
gen | ${CTAGS} --quiet --options=NONE --oneshot=sort-yes.c --sort=yes
19+
gen | ${CTAGS} --quiet --options=NONE --oneshot=x.c -x

Tmain/oneshot.d/stderr-expected.txt

Whitespace-only changes.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
v sort-no.c /^int v;$/;" v typeref:typename:int
2+
f sort-no.c /^int f(void)$/;" f typeref:typename:int
3+
f sort-yes.c /^int f(void)$/;" f typeref:typename:int
4+
v sort-yes.c /^int v;$/;" v typeref:typename:int
5+
f function 2 x.c int f(void)
6+
v variable 1 x.c int v;

docs/man/ctags.1.rst

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,48 @@ Input/Output File Options
221221

222222
This option is quite esoteric and is empty by default.
223223

224+
``--oneshot=<filename>``
225+
Makes ctags behave as a filter, reading source
226+
file contents from standard input and printing their tags to
227+
standard output. When this option is enabled, the options ``-L``,
228+
``-f``, ``-o``, and ``--totals`` are ignored.
229+
230+
ctags assumes *<filename>* is the input file
231+
name of the contents. It affects language selection.
232+
*<filename>* appears as the input file name in the tags output.
233+
234+
This option is useful for extracting interesting names in command
235+
output.
236+
237+
For example you cat extract C preprocessor macro names in the source
238+
code specified by a URL:
239+
240+
.. code-block:: console
241+
242+
$ curl -s -o - \
243+
'https://raw.githubusercontent.com/torvalds/linux/refs/heads/master/fs/buffer.c' \
244+
| ./ctags --oneshot=buf.c --kinds-C='{macro}'
245+
BH_ENTRY buf.c /^#define BH_ENTRY(/;" d file:
246+
...
247+
248+
Make the output in JSON format:
249+
250+
.. code-block:: console
251+
252+
$ curl -s -o - \
253+
'https://raw.githubusercontent.com/torvalds/linux/refs/heads/master/fs/buffer.c' \
254+
| ./ctags --oneshot=buf.c --kinds-C='{macro}' --output-format=json \
255+
| jq .
256+
{
257+
"_type": "tag",
258+
"name": "BH_ENTRY",
259+
"path": "buf.c",
260+
"pattern": "/^#define BH_ENTRY(/",
261+
"file": true,
262+
"kind": "macro"
263+
}
264+
...
265+
224266
``--links[=(yes|no)]``
225267
Indicates whether symbolic links (if supported) should be followed.
226268
When disabled, symbolic links are ignored. This option is on by default.

docs/news/HEAD.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ Extend ``--map-<LANG>=`` and ``--langmap=`` options to choose a parser using reg
2727

2828
``--list-maps`` has also been extended to include regular-expression-based mappings.
2929

30+
New option: ``--oneshot=<filename>``
31+
32+
Makes @CTAGS_NAME_EXECUTABLE@ behave as a filter, reading source
33+
file contents from standard input and printing their tags to
34+
standard output.
35+
3036
Incompatible changes
3137
---------------------------------------------------------------------
3238
Messages for broken symlinks are now emitted at NOTICE level instead of

main/options.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,9 @@ static optionDescription LongOptionDescription [] = {
227227
{1,0," --filter-terminator=<string>"},
228228
{1,0," Specify <string> to print to stdout following the tags for each file"},
229229
{1,0," parsed when --filter is enabled."},
230+
{1,0," --oneshot=<filename>"},
231+
{1,0," Behave as a filter, reading file contents from standard input and"},
232+
{1,0," writing tags to standard output. <filename> is used as the input field of tags."},
230233
{1,0," --links[=(yes|no)]"},
231234
{1,0," Indicate whether symbolic links should be followed [yes]."},
232235
{1,0," --maxdepth=<N>"},
@@ -496,6 +499,8 @@ static optionDescription LongOptionDescription [] = {
496499
{1,0," Print this option summary including experimental features."},
497500
{1,0," --license"},
498501
{1,0," Print details of software license."},
502+
{1,0," --oneshot-limit=<bytes>"},
503+
{1,0," Limit the input size in the oneshot mode, in bytes (default is 32MB)."},
499504
{0,0," --print-language"},
500505
{0,0," Don't make tags file but just print the guessed language name for"},
501506
{0,0," input file."},
@@ -1675,6 +1680,27 @@ static void processHelpFullOption (
16751680
exit (0);
16761681
}
16771682

1683+
static void processOneshot (
1684+
const char *const option,
1685+
const char *const parameter)
1686+
{
1687+
if (!parameter || parameter[0] == '\0')
1688+
error (FATAL, "No input file name given to --%s=%s", option, parameter);
1689+
1690+
static struct interactiveModeArgs args = { .sandbox = false, };
1691+
Option.interactive |= INTERACTIVE_MODE|INTERACTIVE_ONESHOT;
1692+
1693+
#ifdef HAVE_SECCOMP
1694+
Option.interactive |= INTERACTIVE_WITH_SANDBOX;
1695+
args.sandbox = true;
1696+
#endif
1697+
1698+
args.fname = parameter;
1699+
args.limit = oneshotLimit;
1700+
1701+
setMainLoop (interactiveOneshot, &args);
1702+
}
1703+
16781704
#ifdef HAVE_JANSSON
16791705
static void processInteractiveOption (
16801706
const char *const option,
@@ -2723,6 +2749,28 @@ static bool inOneshotMode (void)
27232749
return (Option.interactive & INTERACTIVE_ONESHOT);
27242750
}
27252751

2752+
static void oneshotSetLimit (size_t limit)
2753+
{
2754+
verbose ("adjust input limit of oneshot mode: %lu", (unsigned long)limit);
2755+
oneshotLimit = limit;
2756+
}
2757+
2758+
static void processOneshotLimit (
2759+
const char *const option, const char *const parameter)
2760+
{
2761+
if (parameter == NULL || parameter[0] == '\0')
2762+
error (FATAL, "A positive number or 0 is needed after --%s option", option);
2763+
2764+
unsigned long limit = 0;
2765+
if (!strToULong(parameter, 0, &limit))
2766+
error (FATAL, "Invalid oneshot limit: %s", parameter);
2767+
if (limit > SIZE_MAX)
2768+
error (FATAL, "Too large limit: %s (> %lu)",
2769+
parameter, (unsigned long)SIZE_MAX);
2770+
2771+
oneshotSetLimit ((size_t)limit);
2772+
}
2773+
27262774
static void processSortOption (
27272775
const char *const option, const char *const parameter)
27282776
{
@@ -3125,6 +3173,8 @@ static parametricOption ParametricOptions [] = {
31253173
{ "list-roles", processListRolesOption, true, STAGE_ANY },
31263174
{ "list-subparsers", processListSubparsersOption, true, STAGE_ANY },
31273175
{ "maxdepth", processMaxRecursionDepthOption, true, STAGE_ANY },
3176+
{ "oneshot", processOneshot, true, STAGE_ANY },
3177+
{ "oneshot-limit", processOneshotLimit, true, STAGE_ANY },
31283178
{ "optlib-dir", processOptlibDir, false, STAGE_ANY },
31293179
{ "options", processOptionFile, false, STAGE_ANY },
31303180
{ "options-maybe", processOptionFileMaybe, false, STAGE_ANY },

man/ctags.1.rst.in

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,48 @@ Input/Output File Options
221221

222222
This option is quite esoteric and is empty by default.
223223

224+
``--oneshot=<filename>``
225+
Makes @CTAGS_NAME_EXECUTABLE@ behave as a filter, reading source
226+
file contents from standard input and printing their tags to
227+
standard output. When this option is enabled, the options ``-L``,
228+
``-f``, ``-o``, and ``--totals`` are ignored.
229+
230+
@CTAGS_NAME_EXECUTABLE@ assumes *<filename>* is the input file
231+
name of the contents. It affects language selection.
232+
*<filename>* appears as the input file name in the tags output.
233+
234+
This option is useful for extracting interesting names in command
235+
output.
236+
237+
For example you cat extract C preprocessor macro names in the source
238+
code specified by a URL:
239+
240+
.. code-block:: console
241+
242+
$ curl -s -o - \
243+
'https://raw.githubusercontent.com/torvalds/linux/refs/heads/master/fs/buffer.c' \
244+
| ./ctags --oneshot=buf.c --kinds-C='{macro}'
245+
BH_ENTRY buf.c /^#define BH_ENTRY(/;" d file:
246+
...
247+
248+
Make the output in JSON format:
249+
250+
.. code-block:: console
251+
252+
$ curl -s -o - \
253+
'https://raw.githubusercontent.com/torvalds/linux/refs/heads/master/fs/buffer.c' \
254+
| ./ctags --oneshot=buf.c --kinds-C='{macro}' --output-format=json \
255+
| jq .
256+
{
257+
"_type": "tag",
258+
"name": "BH_ENTRY",
259+
"path": "buf.c",
260+
"pattern": "/^#define BH_ENTRY(/",
261+
"file": true,
262+
"kind": "macro"
263+
}
264+
...
265+
224266
``--links[=(yes|no)]``
225267
Indicates whether symbolic links (if supported) should be followed.
226268
When disabled, symbolic links are ignored. This option is on by default.

0 commit comments

Comments
 (0)