{"thread":{"id":"18757","subject":"[PATCH v2] prints elements of C code in the git repository","startedAt":"2009-04-06T22:09:09Z","lastAt":"2009-04-06T22:09:09Z","messageCount":1,"participants":["Roel Kluin"],"isPatch":true,"patchVersion":2,"patchTotal":null},"messages":[{"id":"110636","messageId":"49DA7D85.5080206@gmail.com","threadId":"18757","inReplyTo":null,"subject":"[PATCH v2] prints elements of C code in the git repository","fromName":"Roel Kluin","fromEmail":"roel.kluin@gmail.com","sentAt":"2009-04-06T22:09:09Z","receivedAt":"2009-04-06T22:09:09Z","isPatch":true,"sender":{"key":"roel.kluin@gmail.com","avatar":null},"body":"This script searches the definition of elements of C and dumps them to\nstdout. It now uses the ctags tags file. Thanks for previous comments,\nIs this better?\n\nUsage examples:\n\nIn your kernel source directory run to create your tags file:\nscripts/tags.sh tags\n\nSay the get-def.sh script lives in `../git/contrib/'.\nTo print the function definitions of kmalloc, do:\n../git/contrib/get-def.sh -f kmalloc\n\nYou can search for a function with a specific multiline pattern:\n../git/contrib/get-def.sh -f kmalloc \"/size.*SLUB_DMA/p\"\n\nMaybe you want to search for any function within a directory\nwith a pattern. You may want to use definitions in get-def.sh:\nsource ../git/contrib/get-def.sh\n\nbad_unsigned=\"`simple_sed \\\n\t\"unsigned ([^;{}()]*, )*($V)[#...#]\\2 (<|>=) 0[^x]\"`\"\nbad_unsigned_test=\"/$bad_unsigned/{\np\ns/^.*$bad_unsigned.*$/Due to variable \\2/p\n}\"\n\n../git/contrib/get-def.sh -f \"$V\" kernel/ \"$bad_unsigned_test\" |\nless\n\nSince frequently searching for functions is slow you may want\nto convert all functions to single lines and pipe that to a file\nthat can subsequently be parsed more quickly.\n\nwarning: this command may take a long time (once):\n../git/contrib/get-def.sh -f \"$V\" -- git-ls-files \"*.[ch]\" \\\n\"s/($S|$comm1|$comm2)*([\\\\]?\\n|$comm1|$S$S)($S|$comm1)*/ /g;\np;\" > ../allfuncs\n\nAnd then for instance do:\nsed -n -r \"/$bad_unsigned_test/{\np\ns/^.*$bad_unsigned_test.*$/Due to variable \\2/p\n}\" ../allfuncs | less\n------------------------------>8-------------8<---------------------------------\ncommit e61ffb20dffa95025e61e2706603a7c72fcad37f\nAuthor: Roel Kluin <roel.kluin@gmail.com>\nDate:   Mon Apr 6 23:58:28 2009 +0200\n\n    This script searches the definition of elements of C and dumps them to\n    stdout. It requires ctags and bash.\n    \n    As invoking it with -? will tell:\n    \n    USAGE: git get-def [OPTION]... PATTERN [FILE|DIRECTORY]... [SEDSCRIPT]\n    print elements of C code with name PATTERN, an extended regular expression. If\n    SEDSCRIPT is provided, it puts the entire element in the hold space before the\n    execution of SEDSCRIPT.\n    \n    \n    Options to specify which element(s) should be printed:\n            -c      class name\n            -d      define\n            -f      function or method name\n            -g      enumeration\n            -m      member (of structure or class data)\n            -s      structure\n            -t      typedef\n            -u      union\n            -v      variable\n            -D      only non-macro defines\n            -M      only macro defines\n    \n    Signed-off-by: Roel Kluin <roel.kluin@gmail.com>\n---\ndiff --git a/contrib/get-def.sh b/contrib/get-def.sh\nnew file mode 100755\nindex 0000000..9428003\n--- /dev/null\n+++ b/contrib/get-def.sh\n@@ -0,0 +1,254 @@\n+#!/bin/bash\n+# FIXME: make C++ style members\n+\n+int=\"[0-9]\"\n+hex=\"[a-f0-9]\"\n+hEx=\"[A-Fa-f0-9]\"\n+HEX=\"[A-F0-9]\"\n+upp=\"[A-Z]\"\n+up_=\"[A-Z_]\"\n+low=\"[a-z]\"\n+lo_=\"[a-z_]\"\n+alp=\"[A-Za-z]\"\n+al_=\"[A-Za-z_]\"\n+ALN=\"[A-Z0-9]\"\n+AN_=\"[A-Z0-9_]\"\n+aln=\"[A-Za-z0-9]\"\n+an_=\"[A-Za-z0-9_]\"\n+\n+em='!'\t\t\t# because of bash bang\n+\n+D=\"$int*\\.?$int+x?$hex*[uUlL]{0,3}[fF]?\"\t\t# a number, float or hex\n+# more strict and catches it (costs one backreference for (git )grep)\n+SD=\"($int+[uUlLfF]?|$int+[uU]?[lL][lL]?|0x$hex+|0x$HEX+|$int+[lL][lL][uU]|$int*\\.$int+[fF]?)\"\n+\n+V=\"$al_+$an_*\"\t\t# variable/function name (or definition)\n+K=\"$up_+$AN_*\"\t\t# definition (in capitals)\n+\n+# to catch variables that are members or arrays:\n+W=\"[a-zA-Z0-9_>.-]*\"\n+SW=\"$V(\\[[^][]*\\]|\\[[^][]*\\[[^][]*\\][^][]*\\]|\\.$V|->$V)*\"\t # more strict, 1 backref\n+\n+s=\"[[:space:]]*\"\n+S=\"[[:space:]]+\"\n+\n+# useful to ensure the end of a variable name:\n+Q=\"[^[:alnum:]_]\"\n+Q2=\"[^[:alnum:]_>.]\" # the '>' is tricky, it's an operator as well\n+\n+# match comments\n+comm1=\"\\/\\*([^*]+|\\**[^*/])*\\*+\\/\"\t\t\t# 1 backref\n+comm2=\"\\/\\/([^\\n]+|[n\\\\]+)*\"\t\t\t\t# 1 backref\n+\n+# match the end of the line, including comments:\n+cendl=\"$s($comm1|$comm2|$s)*$\"\t\t\t \t# 3 backrefs\n+cendln=\"$s($comm1|$comm2|$s)*($|\\n)\"\t\t\t# 4 backrefs\n+\n+# strings and characters can contain things we want to match\n+str=\"\\\"([^\\\\\\\"]+|\\\\\\\\.)*\\\"\"\t\t\t\t# 1 backref\n+ch1=\"'[^\\\\']'\"\n+ch2=\"'\\\\\\\\.[^']*'\"\n+ch=\"$ch1|$ch2\"\n+\n+# match something that is not comment, string or character (c-code):\n+ccode=\"([^\\\"'/]+|\\/[^*\\\"'/]|\\/?$comm1|\\/?$ch1|\\/?$ch2|\\/?$str)*\"\t\t# 3 backrefs\n+ccoden=\"([^\\\"'/]+|\\/[^*\\\"'/]|\\/?$comm1|\\/?$ch1|\\/?$ch2|\\/?$str|\\/?$comm2)*\"\t# 4 backrefs\n+\n+nps=\"[^()]*\"\n+nstdps=\"(\\($nps(\\($nps(\\($nps(\\($nps(\\($nps\\)$nps)*\\)$nps)*\\)$nps)*\\)$nps)*\\)$nps)*\"\n+npz=\"$nps$nstdps\"\n+nnps=\"\\($npz\\)\"\n+\n+ncs=\"[^}{]*\"\n+nstdcs=\"(\\{$ncs(\\{$ncs(\\{$ncs(\\{$ncs(\\{$ncs\\}$ncs)*\\}$ncs)*\\}$ncs)*\\}$ncs)*\\}$ncs)*\"\n+ncz=\"$ncs$nstdcs\"\n+nncs=\"\\{$ncz\\}\"\n+\n+delimitstr=\"s/([][{}(|)+*?\\\\/.^])/\\\\\\\\\\1/g\"\n+delimit()\n+{\n+\tsed -r \"$delimitstr\"\n+}\n+\n+# excludes testing in strings, chars and comment\n+excl_code()\n+{\n+\tlocal incl=\"\"\n+\tfor f in \"${@:3}\"; do\n+\t\tincl=\"$incl|\\/?$f\";\n+\tdone\n+\techo \"([^$1\\\"'/$2]*$incl|\\/[^$1\\\"*'/$2]*|\\/?$comm1|\\/?$ch1|\\/?$ch2|\\/?$str|\\/?$comm2)*\"\n+}\n+\n+# usage: nestc \"(\" \")\" [number]\n+nestc()\n+{\n+\tlocal i;\n+\t[ $# -eq 1 ] && i=5 || i=$3;\n+\t# first and 2nd are flipped to enable matching\n+\t# square brackets \"][\"\n+\tlocal p=\"$(excl_code \"$2$1\" \"${@:4}\")\"\n+\tlocal ret=\"$p\"\n+\twhile [ $i -gt 0 ]; do\n+\t\tret=\"${p}([$1]${ret}[$2]${p})*\"\n+\t\ti=$(($i-1));\n+\tdone\n+\techo \"$ret\"\n+}\n+\n+simple_sed()\n+{\n+\tl=\"${1//\\(...\\)/\\($(nestc \"(\" \")\" 8 | delimit)\\)}\"\n+\tl=\"${1//\\{...\\}/\\($(nestc \"{\" \"}\" 12 | delimit)\\)}\"\n+\tl=\"${l//[[]#...#[]]/$Q$ccode$Q2}\"\n+\tl=\"${l//[[]...[&|][&|][]]/$s($(nestc \"(\" \")\" 5 | delimit)[&|][&|])?$s}\"\n+\tl=\"${l//[[][&|][&|]...[]]/$s([&|][&|]$(nestc \"(\" \")\" 5 | delimit))?$s}\"\n+\tl=\"${l//[[]...[]]/$ccode}\"\n+\tl=\"${l//[[](\\{...)\\*[]]/(\\{$(nestc \"{\" \"}\" 12))*}\"\n+\tl=\"${l//[[](...\\})\\*[]]/($(nestc \"{\" \"}\" 12)\\})*}\"\n+\techo \"$l\" | sed -r \"\n+\t\t:a\n+\t\ts/([[:alnum:]])[[:space:]]+([[:alnum:]])/\\1[[:space:]]+\\2/g\n+\t\ts/[[:space:]]+/[[:space:]]*/g\n+\t\t$!{\n+\t\t\tN; ba\n+\t\t}\"\n+}\n+\n+usage()\n+{\n+cat << EOF\n+USAGE: git get-def [OPTION]... PATTERN [FILE|DIRECTORY]... [SEDSCRIPT]\n+\n+print elements of C code with name PATTERN, an extended regular expression. If\n+SEDSCRIPT is provided, it puts the entire element in the hold space before the\n+execution of SEDSCRIPT.\n+\n+Options to specify which element(s) should be printed:\n+\t-c\tclass name\n+\t-d\tdefine\n+\t-f\tfunction or method name\n+\t-g\tenumeration\n+\t-m\tmember (of structure or class data)\n+\t-s\tstructure\n+\t-t\ttypedef\n+\t-u\tunion\n+\t-v\tvariable\n+\t-D\tonly non-macro defines\n+\t-M\tonly macro defines\n+\t-?\tprint this help\n+\n+EOF\n+}\n+\n+sedstr_matches()\n+{\n+\t# TODO: distinction between simple defines and macros\n+\tlocal shead=\"$(nestc \"(\" \")\" 5)\"\n+\tlocal head=\"$(nestc \"(\" \")\" 10)\"\n+\tlocal body=\"$(nestc \"{\" \"}\" 10)\"\n+\tlocal SP=\"$S|$comm1|[\\\\]\"\n+\n+\tlocal wrd=\"$V(($SP)?\\($shead\\))?\"\n+\tlocal fret=\"($SP)*($wrd($SP|\\*)+)+\"\n+\tlocal A=\"($SP)*($wrd($SP)+)*\"\n+\tlocal B=\"($SP)+($wrd($SP))*\"\n+\tlocal C=\"(($SP)+$wrd)*($SP)*\"\n+\tlocal t=\n+\tlocal match=\n+\tlocal pr=\":__print;${3}\"\n+\tfor t in ${1//|/ }; do\n+\t\tcase \"$t\" in\n+\t\t\t\"c\") match=\"^${A}class($B$2($C)?\\{$body\\}|($C)?\\{$body\\}$A$2)($C)?;\";;\n+\t\t\t\"d\") match=\"^$s#(${SP})*define(${SP})+$2(($S|\\().*[^\\\\])?$\" ;;\n+\t\t\t\"D\") match=\"^$s#(${SP})*define(${SP})+$2($S.*[^\\\\])?$\" ;;\n+\t\t\t\"M\") match=\"^$s#(${SP})*define(${SP})+$2\\(.*[^\\\\]$\" ;;\n+\t\t\t\"f\") match=\"^$fret$2($SP)*\\($shead\\)($SP)*\\{$body\\}\" ;;\n+\t\t\t\"g\") match=\"^${A}enum($B$2($C)?\\{$body\\}|($C)?\\{$body\\}$A$2)($C)?;\";;\n+\t\t\t\"m\") match=\"^$fret$2($SP)*[;=]\";;\n+\t\t\t\"s\") match=\"^${A}struct($B$2($C)?\\{$body\\}|($C)?\\{$body\\}$A$2)($C)?;\";;\n+\t\t\t\"t\") match=\"^($SP)*typedef(($B)?\\{$body\\}|($SP)+)$A$2($C)?;$cendl\" ;;\n+\t\t\t\"u\") match=\"^${A}union($B$2($C)?\\{$body\\}|($C)?\\{$body\\}$A$2)($C)?;\";;\n+\t\t\t\"v\") match=\"^$fret$2($SP)*[;=]\";;\n+\t\tesac\n+\t\tt=\"${t//[DM]/d}\"\n+\t\tpr=\":__$t;/$match/${em}{H;N;b__$t};b__print;\n+$pr\"\n+\tdone\n+\techo \"$pr\"\n+}\n+\n+parse_tags()\n+{\n+\t# TODO: other limits, e.g. limit=\"\\tfile:\" for local functions\n+\tlocal limit=\"(\t.*)?\"\n+\tlocal l=\n+\t#\t\t  name\tfile\tquery\t\ttype\n+\tfor l in $(grep -E \"^$1\t($2)\t[^\t]+\t(${3//[DM]/d})$limit$\" \"tags\" |\n+\t\t\tsort -k2 | tr \"\\t \" \"@\\`\"); do\n+\t\tlocal n=\"${l%%@*}\"\n+\t\tl=\"${l#*@}\"\n+\t\tlocal f=\"${l%%@*}\"\n+\t\tlocal t=\"${l#*;\\\"@}\"\n+\t\tif [ \"$f\" != \"$oldf\" ]; then\n+\t\t\tif [ -n \"$oldf\" ]; then\n+\t\t\t\tsed -r -n \"${sedstr}b;$4\" \"$oldf\"\n+\t\t\t\t[ $? -ne 0 ] && echo \"Error in $oldf\"\n+\t\t\tfi\n+\t\t\tlocal sedstr=\n+\t\t\tlocal oldf=\"$f\"\n+\t\tfi\n+\t\tl=\"${l%;\\\"*}\"\n+\t\tl=\"${l#*@}\"\n+\t\t[ \"${l:0:2}\" = \"/^\" ] && l=\"/^`echo \"${l:2:$((${#l}-3))}\" | delimit`/\"\n+\t\tsedstr=\"${l//\\`/ }b__${t:0:1};$sedstr\"\n+\tdone\n+\t[ -n \"$sedstr\" ] && sed -r -n \"${sedstr}b;$4\" \"$oldf\"\n+}\n+\n+parse_opts()\n+{\n+\tlocal fl=\t\t\t# file list\n+\tlocal name=\n+\tlocal getstr=\n+\tlocal script=\n+\n+\twhile [ $# -ne 0 ]; do\n+\t\twhile getopts \"cdDMfgmstuv\" optname; do\n+\t\t\tcase \"$optname\" in\n+\t\t\t\tc|d|D|M|f|g|m|s|t|u|v) getstr=\"${getstr:+$getstr|}$optname\" ;;\n+\t\t\t\t\"?\") usage; exit 0; ;;\n+\t\t\tesac\n+\t\tdone\n+\t\tshift $((OPTIND-1))\n+\t\t[ $# -eq 0 ] && break;\n+\t\tOPTIND=0\n+\t\tif [ -f \"$1\" ]; then\n+\t\t\tfl=\"${fl:+$fl|}$1\";\n+\t\telif [ -d \"$1\" ]; then\n+\t\t\tfl=\"${fl:+$fl|}${1}[^\t]+\";\n+\t\telse\n+\t\t\tif [ -z \"$name\" ]; then\n+\t\t\t\tname=\"$1\";\n+\t\t\telse\n+\t\t\t\tscript=\"$1\"\n+\t\t\tfi\n+\t\tfi\n+\t\tshift\n+\tdone\n+\t[ -z \"$name\" ] && usage;\n+\tgetstr=\"${getstr:=c|d|f|g|m|s|t|u|v}\"\n+\tlocal pr=\"`sedstr_matches \"$getstr\" \"$name\" \"${script:=p;}\"`\"\n+\tfl=\"${fl:=[^\t]+}\"\n+\tparse_tags \"$name\" \"${fl//\\//\\\\/}\" \"$getstr\" \"$pr\"\n+}\n+\n+\n+#main\n+if [ \"$0\" != \"-bash\" ]; then\n+\tif [ -f \"tags\" ]; then\n+\t\tparse_opts \"$@\"\n+\telse\n+\t\techo \"No tags file found\";\n+\tfi\n+fi\n"}]}