]> git.ipfire.org Git - thirdparty/man-pages.git/commitdiff
bin/grepc: Optimize
authorAlejandro Colomar <alx@kernel.org>
Fri, 3 Nov 2023 16:41:22 +0000 (17:41 +0100)
committerAlejandro Colomar <alx@kernel.org>
Wed, 29 Oct 2025 20:29:16 +0000 (21:29 +0100)
Call pcre2grep(1) only once, which allows removing named pipes.  This is
an important optimization, and will also allow accepting several file
names in the command line (in a future commit).  The source code is also
significantly simplified.

It has a caveat: this single call to pcre2grep(1) will consume more
resources, and will crash on certain input files.  The workaround is to
restrict the search to just one or a few types of code.  Document this
in the manual page.  This caveat only applies to very specific files,
which so far I've only found in the Linux kernel source tree.

bin/grepc
share/man/man1/grepc.1

index 05eeee46fb1dd7f87823cb36c1882f937217878a..1a7b5191d02ddff6123f8f631588b99fee38182d 100755 (executable)
--- a/bin/grepc
+++ b/bin/grepc
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 
 
 # Defaults:
@@ -183,26 +183,60 @@ grepc_parse_cmd()
 }
 
 
-grepc_helper()
+grepc_e()              { echo '(?s)^([\w[]+[\w\s]*)?\benum\b[ \t]*([\w \t[\]]|::)*\n*([ \t]*){[^}]*^[ \t]*'"$1"'\b\s*[=,].*?^\3}.*?;'; }
+grepc_fp()             { echo '(?s)^[\w[](?:[\w\s\(,\)[\]*]|::)+[\w\s\)*\]]\s+\**\(?'"$1"'\)?\s*(\((?:[\w\s,[\]*]|::|(?1))*(?:\.\.\.)?\))(?:[\w\s\(,\)[\]]|::)*;'; }
+grepc_fd()             { echo '(?s)^[\w[](?:[\w\s\(,\)[\]*]|::)*[\w\s\)*\]]\s+\**\(?'"$1"'\)?\s*(\((?:[\w\s,[\]*]|::|(?1))*(?:\.\.\.)?\))[ \t]*\n([ \t]*){.*?^\2}'; }
+grepc_fgd_libm()       { grepc_fd "M_DECL_FUNC \(__$1\)"; }
+grepc_fgp_libio()      { grepc_fp "_IO_$1"; }
+grepc_fgd_libio()      { grepc_fd "_IO_$1"; }
+grepc_fgp()            { grepc_fgp_libio "$1"; }
+grepc_fsp()            { echo '(?s)^asmlinkage\s+[\w\s]+\**sys_'"$1"'\s*\(.*?\)'; }
+grepc_fsd()            { echo '(?s)^(COMPAT_)?SYSCALL_DEFINE.\('"$1"'\b.*?^}'; }
+grepc_mf()             { echo '(?s)^[ \t]*#\s*define\s[\s\\]*'"$1"'\(.*?[^\\]$'; }
+grepc_mo()             { echo '(?s)^[ \t]*#\s*define\s[\s\\]*'"$1"'\b(?!\().*?(?<!\\)$'; }
+grepc_t_braced()       { echo '(?s)^([\w[]([\w\s\(,\)[\]*]|::)*[\w\s\)*\]]\s+)?\b(struct|union|enum)\b([\w \t[\]]|::)+\b'"$1"'\b[ \t]*\n*([ \t]*){.*?^\5}.*?;'; }
+grepc_t_td_simple()    { echo '(?s)^[ \t]*typedef\s+[^{};]+'"$1"';'; }
+grepc_t_td_braced()    { echo '(?s)^[ \t]*typedef\s+(struct|union|enum)\b(?:(?!\W'"$1"'\W)([\w \t[\]]|::))*\n*([ \t]*){(?:(?!^\3?}).)*?^\3}\s*'"$1"'(\[[\w\(,\)]\])*;'; }
+grepc_t_td_func()      { echo '(?s)^[ \t]*typedef\s+[^{};]+\(\**'"$1"'\)\s*\([^{};]+;'; }
+grepc_ue()             { echo '(?s)^([\w[]+[\w\s]*)?\benum\b([\w \t[\]]|::)*\n*([ \t]*){[^}]*^\s*\w+[\w\s[\]=]*'"$1"'.*?^\3}.*?;'; }
+grepc_uf_def()         { echo '(?s)^[\w[]([\w\s\(,\)[\]*]|::)*[\w\s\)*\]]\s+\**\w+\s*\(([\w\s\(,\)[\]*]|::)+?(\.\.\.)?\)[ \t]*\n*([ \t]*){(?:(?!^\4?}).)*'"$1"'.*?^\4}'; }
+grepc_linux_use_func_syscall_def() { echo '(?s)^(COMPAT_)?SYSCALL_DEFINE.\(\w+\b(?:(?!^}).)*'"$1"'.?^}'; }
+grepc_uf_linux_def()   { grepc_linux_use_func_syscall_def "$1"; }
+grepc_um()             { echo '(?s)^[ \t]*#\s*define\s[\s\\]*\w+\b(\([^\)]*\))?(?:(?![^\\]$).)*'"$1"'.*?[^\\]$'; }
+grepc_ut_su()          { echo '(?s)^(?!^[ \t]*typedef\b)([\w[]([\w\s\(,\)[\]*]|::)*[\w\s\)*\]]\s+)?\b(struct|union)\b([\w \t[\]]|::)*\w+[ \t]*\n*([ \t]*){(?:(?!^\5?}).)*?'"$1"'.*?^\5}.*?;'; }
+grepc_ut_td_simple()   { echo '(?s)^[ \t]*typedef\s+[^{};]*'"$1"'[^{};]+;'; }
+grepc_ut_td_su()       { echo '(?s)^[ \t]*typedef\s+(struct|union)\b([\w \t[\]]|::)*\n*([ \t]*){(?:(?!^\3?}|^\s*typedef).)*'"$1"'(?:(?!^\3?}|^\s*typedef).)*^\3}\s*\w+;'; }
+
+
+grepc_patterns()
 {
-       grep -${iflag}zP -- "$1" \
-       | grep -${iflag}zP -- "$2" \
-       | head -c-1 \
-       | pcre2grep -${iflag}${lflag}HMn --label="$file" -- "$3" \
-       | if [ "$kflag" = 'no' ]; then
-               sed -E 's/^[^: ]+:[0-9]+:/\n\n&\n/';
-       else
-               cat;
-       fi;
+       test $t_e = yes && grepc_e "$1";
+       test $t_fp = yes && grepc_fp "$1";
+       test $t_fd = yes && grepc_fd "$1";
+       test $t_fsp = yes && grepc_fsp "$1";
+       test $t_fsd = yes && grepc_fsd "$1";
+       test $t_fgp = yes && grepc_fgp "$1";
+       test $t_fgd_libm = yes && grepc_fgd_libm "$1";
+       test $t_fgd_libio = yes && grepc_fgd_libio "$1";
+       test $t_mf = yes && grepc_mf "$1";
+       test $t_mo = yes && grepc_mo "$1";
+       test $t_t_braced = yes && grepc_t_braced "$1";
+       test $t_t_td_simple = yes && grepc_t_td_simple "$1";
+       test $t_t_td_braced = yes && grepc_t_td_braced "$1";
+       test $t_t_td_func = yes && grepc_t_td_func "$1";
+       test $t_ue = yes && grepc_ue "$1";
+       test $t_uf_def = yes && grepc_uf_def "$1";
+       test $t_uf_linux_def = yes && grepc_uf_linux_def "$1";
+       test $t_um = yes && grepc_um "$1";
+       test $t_ut_su = yes && grepc_ut_su "$1";
+       test $t_ut_td_simple = yes && grepc_ut_td_simple "$1";
+       test $t_ut_td_su = yes && grepc_ut_td_su "$1";
 }
 
 
-grepc_helper_use()
+grepc_search()
 {
-       grep -${iflag}zP -- "$1" \
-       | grep -${iflag}zP -- "$2" \
-       | head -c-1 \
-       | pcre2grep -${iflag}${lflag}HMn --label="$file" -- "$3" \
+       pcre2grep -${iflag}${lflag}HMn --label="$file" -f <(grepc_patterns "$identifier") \
        | if [ "$kflag" = 'no' ]; then
                sed -E 's/^[^: ]+:[0-9]+:/\n\n&\n/';
        else
@@ -216,226 +250,6 @@ grepc_helper_use()
 }
 
 
-grepc_e()
-{
-       grepc_helper \
-         '\benum\b' \
-         "^[ \t]*$1\b\s*[,=]" \
-         '(?s)^([\w[]+[\w\s]*)?\benum\b[ \t]*([\w \t[\]]|::)*\n*([ \t]*){[^}]*^[ \t]*'"$1"'\b\s*[=,].*?^\3}.*?;';
-}
-
-
-grepc_fp()
-{
-       grepc_helper \
-         "(\($1\)|\b$1)\s*\(" \
-         '.' \
-         '(?s)^[\w[](?:[\w\s\(,\)[\]*]|::)+[\w\s\)*\]]\s+\**\(?'"$1"'\)?\s*(\((?:[\w\s,[\]*]|::|(?1))*(?:\.\.\.)?\))(?:[\w\s\(,\)[\]]|::)*;';
-}
-
-
-grepc_fd()
-{
-       grepc_helper \
-         "(\($1\)|\b$1)\s*\(" \
-         '.' \
-         '(?s)^[\w[](?:[\w\s\(,\)[\]*]|::)*[\w\s\)*\]]\s+\**\(?'"$1"'\)?\s*(\((?:[\w\s,[\]*]|::|(?1))*(?:\.\.\.)?\))[ \t]*\n([ \t]*){.*?^\2}';
-}
-
-
-grepc_fgd_libm()  { grepc_fd "M_DECL_FUNC \(__$1\)"; }
-grepc_fgp_libio() { grepc_fp "_IO_$1"; }
-grepc_fgd_libio() { grepc_fd "_IO_$1"; }
-grepc_fgp()       { grepc_fgp_libio "$1"; }
-
-
-grepc_fsp()
-{
-       grepc_helper \
-         "^asmlinkage\s+[\w\s]+\**sys_$1\s*\(" \
-         '.' \
-         '(?s)^asmlinkage\s+[\w\s]+\**'"sys_$1"'\s*\(.*?\)';
-}
-
-
-grepc_fsd()
-{
-       grepc_helper \
-         "SYSCALL_DEFINE.\($1\b" \
-         '.' \
-         '(?s)^(COMPAT_)?SYSCALL_DEFINE.\('"$1"'\b.*?^}';
-}
-
-
-grepc_mf()
-{
-       grepc_helper \
-         "#\s*define\s+$1\(" \
-         '.' \
-         '(?s)^[ \t]*#\s*define\s[\s\\]*'"$1"'\(.*?[^\\]$';
-}
-
-
-grepc_mo()
-{
-       grepc_helper \
-         "#\s*define\s+$1\b(?!\()" \
-         '.' \
-         '(?s)^[ \t]*#\s*define\s[\s\\]*'"$1"'\b(?!\().*?(?<!\\)$';
-}
-
-
-grepc_t_braced()
-{
-       grepc_helper \
-         '\b(struct|union|enum)\b([\w \t[\]]|::)+\b'"$1"'\b' \
-         '.' \
-         '(?s)^([\w[]([\w\s\(,\)[\]*]|::)*[\w\s\)*\]]\s+)?\b(struct|union|enum)\b([\w \t[\]]|::)+\b'"$1"'\b[ \t]*\n*([ \t]*){.*?^\5}.*?;';
-}
-
-
-grepc_t_td_simple()
-{
-       grepc_helper \
-         '^[ \t]*typedef\s' \
-         "\b$1;" \
-         '(?s)^[ \t]*typedef\s+[^{};]+'"$1"';';
-}
-
-
-grepc_t_td_braced()
-{
-       grepc_helper \
-         '^[ \t]*typedef\s+(struct|union|enum)\b[^;]*$' \
-         "^[ \t]*}\s*$1(\[[\w\(,\)]\])*;" \
-         '(?s)^[ \t]*typedef\s+(struct|union|enum)\b(?:(?!\W'"$1"'\W)([\w \t[\]]|::))*\n*([ \t]*){(?:(?!^\3?}).)*?^\3}\s*'"$1"'(\[[\w\(,\)]\])*;';
-}
-
-
-grepc_t_td_func()
-{
-       grepc_helper \
-         '^[ \t]*typedef\s' \
-         "\(\**$1\)\s*\(" \
-         '(?s)^[ \t]*typedef\s+[^{};]+\(\**'"$1"'\)\s*\([^{};]+;';
-}
-
-
-grepc_ue()
-{
-       grepc_helper_use \
-         '\benum\b' \
-         "\b$1\b" \
-         '(?s)^([\w[]+[\w\s]*)?\benum\b([\w \t[\]]|::)*\n*([ \t]*){[^}]*^\s*\w+[\w\s[\]=]*'"$1"'.*?^\3}.*?;';
-}
-
-
-grepc_uf_def()
-{
-       grepc_helper_use \
-         "\b$1\b" \
-         '.' \
-         '(?s)^[\w[]([\w\s\(,\)[\]*]|::)*[\w\s\)*\]]\s+\**\w+\s*\(([\w\s\(,\)[\]*]|::)+?(\.\.\.)?\)[ \t]*\n*([ \t]*){(?:(?!^\4?}).)*'"$1"'.*?^\4}';
-}
-
-
-grepc_linux_use_func_syscall_def()
-{
-       grepc_helper_use \
-         "SYSCALL_DEFINE.\(" \
-         "\b$1\b" \
-         '(?s)^(COMPAT_)?SYSCALL_DEFINE.\(\w+\b(?:(?!^}).)*'"$1"'.?^}';
-}
-
-
-grepc_uf_linux_def() { grepc_linux_use_func_syscall_def "$1"; }
-
-
-grepc_um()
-{
-       grepc_helper_use \
-         "\b$1\b" \
-         'define' \
-         '(?s)^[ \t]*#\s*define\s[\s\\]*\w+\b(\([^\)]*\))?(?:(?![^\\]$).)*'"$1"'.*?[^\\]$';
-}
-
-
-grepc_ut_su()
-{
-       grepc_helper_use \
-         "\b(struct|union)\b" \
-         "\b$1\b" \
-         '(?s)^(?!^[ \t]*typedef\b)([\w[]([\w\s\(,\)[\]*]|::)*[\w\s\)*\]]\s+)?\b(struct|union)\b([\w \t[\]]|::)*\w+[ \t]*\n*([ \t]*){(?:(?!^\5?}).)*?'"$1"'.*?^\5}.*?;';
-}
-
-
-grepc_ut_td_simple()
-{
-       grepc_helper_use \
-         '^[ \t]*typedef\s' \
-         "\b$1\b" \
-         '(?s)^[ \t]*typedef\s+[^{};]*'"$1"'[^{};]+;';
-}
-
-
-grepc_ut_td_su()
-{
-       grepc_helper_use \
-         '^[ \t]*typedef\s+(struct|union)\b[^;]*$' \
-         "\b$1\b" \
-         '(?s)^[ \t]*typedef\s+(struct|union)\b([\w \t[\]]|::)*\n*([ \t]*){(?:(?!^\3?}|^\s*typedef).)*'"$1"'(?:(?!^\3?}|^\s*typedef).)*^\3}\s*\w+;';
-}
-
-
-grepc_search()
-{
-       local t="";
-       local f="$(mktemp -u -t grepc.XXXXXX)";
-       local fi="";
-       local fo="";
-
-       t="$t e";
-       t="$t fp fd fsp fsd fgp fgd_libm fgd_libio";
-       t="$t mf mo";
-       t="$t t_braced t_td_simple t_td_braced t_td_func";
-       t="$t ue uf_def uf_linux_def um ut_su ut_td_simple ut_td_su";
-
-       for ti in $t; do
-               fi="$fi $f.$ti.i";
-               fo="$fo $f.$ti.o";
-       done;
-
-       mkfifo -m600 $fi $fo;
-       cat $fo &
-
-       if test $t_e = yes; then grepc_e "$1"; else cat >/dev/null & printf ''; fi <$f.e.i >$f.e.o &
-       if test $t_fp = yes; then grepc_fp "$1"; else cat >/dev/null & printf ''; fi <$f.fp.i >$f.fp.o &
-       if test $t_fd = yes; then grepc_fd "$1"; else cat >/dev/null & printf ''; fi <$f.fd.i >$f.fd.o &
-       if test $t_fsp = yes; then grepc_fsp "$1"; else cat >/dev/null & printf ''; fi <$f.fsp.i >$f.fsp.o &
-       if test $t_fsd = yes; then grepc_fsd "$1"; else cat >/dev/null & printf ''; fi <$f.fsd.i >$f.fsd.o &
-       if test $t_fgp = yes; then grepc_fgp "$1"; else cat >/dev/null & printf ''; fi <$f.fgp.i >$f.fgp.o &
-       if test $t_fgd_libm = yes; then grepc_fgd_libm "$1"; else cat >/dev/null & printf ''; fi <$f.fgd_libm.i >$f.fgd_libm.o &
-       if test $t_fgd_libio = yes; then grepc_fgd_libio "$1"; else cat >/dev/null & printf ''; fi <$f.fgd_libio.i >$f.fgd_libio.o &
-       if test $t_mf = yes; then grepc_mf "$1"; else cat >/dev/null & printf ''; fi <$f.mf.i >$f.mf.o &
-       if test $t_mo = yes; then grepc_mo "$1"; else cat >/dev/null & printf ''; fi <$f.mo.i >$f.mo.o &
-       if test $t_t_braced = yes; then grepc_t_braced "$1"; else cat >/dev/null & printf ''; fi <$f.t_braced.i >$f.t_braced.o &
-       if test $t_t_td_simple = yes; then grepc_t_td_simple "$1"; else cat >/dev/null & printf ''; fi <$f.t_td_simple.i >$f.t_td_simple.o &
-       if test $t_t_td_braced = yes; then grepc_t_td_braced "$1"; else cat >/dev/null & printf ''; fi <$f.t_td_braced.i >$f.t_td_braced.o &
-       if test $t_t_td_func = yes; then grepc_t_td_func "$1"; else cat >/dev/null & printf ''; fi <$f.t_td_func.i >$f.t_td_func.o &
-       if test $t_ue = yes; then grepc_ue "$1"; else cat >/dev/null & printf ''; fi <$f.ue.i >$f.ue.o &
-       if test $t_uf_def = yes; then grepc_uf_def "$1"; else cat >/dev/null & printf ''; fi <$f.uf_def.i >$f.uf_def.o &
-       if test $t_uf_linux_def = yes; then grepc_uf_linux_def "$1"; else cat >/dev/null & printf ''; fi <$f.uf_linux_def.i >$f.uf_linux_def.o &
-       if test $t_um = yes; then grepc_um "$1"; else cat >/dev/null & printf ''; fi <$f.um.i >$f.um.o &
-       if test $t_ut_su = yes; then grepc_ut_su "$1"; else cat >/dev/null & printf ''; fi <$f.ut_su.i >$f.ut_su.o &
-       if test $t_ut_td_simple = yes; then grepc_ut_td_simple "$1"; else cat >/dev/null & printf ''; fi <$f.ut_td_simple.i >$f.ut_td_simple.o &
-       if test $t_ut_td_su = yes; then grepc_ut_td_su "$1"; else cat >/dev/null & printf ''; fi <$f.ut_td_su.i >$f.ut_td_su.o &
-
-       tee $fi >/dev/null;
-       wait;
-       rm $fi $fo;
-}
-
-
 main()
 {
        grepc_parse_cmd "$@" </dev/null;
index 2d9cde5c82de4045beffb6004ab76aa4d701f295..e80c648f079794ff435ff59ed418954b596fbcb7 100644 (file)
@@ -151,6 +151,13 @@ This option can be passed multiple times
 to search for various types of code.
 Default:
 .BR "e f m t" .
+.SH CAVEATS
+In some cases,
+internal calls to
+.MR pcre2grep 1
+may fail after consuming too much resources.
+To solve that,
+restrict the "types of code" of your search.
 .SH EXAMPLES
 .EX
 .RB \(ti/src/nginx/unit$ " grepc nxt_sprintf;"