-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathcwhere
executable file
·152 lines (136 loc) · 4.13 KB
/
cwhere
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#!/bin/bash
#
# cwhere
#
# find which C header file defines the requested macro/identifier
#
# Would normally do this in Perl, but doing it in pure bash for
# something different.
#
# Mikel Ward <[email protected]>
#
# TODO move cleanup trap inside print_definitions()
name=cwhere
trap cleanup EXIT
# remove any temporary files, etc.
# Usage: cleanup
cleanup()
{
test -f "$sourcefile" && rm "$sourcefile"
}
get_cpp_output()
{
local headers="$@"
sourcefile=$(mktemp)
if test $? -ne 0; then
echo "Error: Cannot create source file" 1>&2
exit 1
fi
for header in $headers; do
echo "#include <$header>" >> "$sourcefile"
done
# TODO: Do we need -D_GNU_SOURCE?
cpp -dD "$sourcefile"
if test $? -ne 0; then
cat "$sourcefile"
echo "Error: Cannot run cpp" 1>&2
exit 1
fi
}
# Print the definition of <identifier> and which header file it appears in
# Usage: print_definitions <identifier> <header>...
# Example: print_definitions O_WRONLY sys/types.h sys/stat.h fcntl.h
print_definitions()
{
local identifier="$1"
shift
local headers="$@"
local filenamepattern definepattern typedefpattern declarepattern
filenamepattern='^# [0-9]+ "([^"]*)"'
definepattern='^[[:space:]]*#define[[:space:]][[:space:]]*'"$identifier"
typedefpattern='^[[:space:]]*typedef[[:space:]].*'"$identifier"
declarepattern='^[[:space:]]*(extern[[:space:]]*)?(_|int|void|char|short).*[[:space:]]'"$identifier"
get_cpp_output "$@" |
while read -r line; do
# this is evil, just doing it to learn something new
# put the pattern in a variable to avoid bash treating pattern as a string :-(
if [[ $line =~ $filenamepattern ]]; then
file=${BASH_REMATCH[1]}
if [[ $identifier == "INCLUDES" ]]; then
echo "$file"
else
# just save it in case there's a symbol match
:
fi
elif [[ $line =~ $typedefpattern ]] ||
[[ $line =~ $definepattern ]] ||
[[ $line =~ $declarepattern ]]; then
if [[ $identifier != "INCLUDES" ]]; then
echo "$file: $line"
fi
fi
done | grep -v '^/tmp/' | sort | uniq
}
# Usage: get_headers <function|syscall>
# Example: get_headers select
# Example: get_headers 'open(2)'
get_headers()
{
desc "$@" | sed -ne '/^[[:space:]]*#include/{s/^[[:space:]]*#include <\(.*\)>.*/\1/;p}'
}
# print a message saying how to run this program
usage()
{
cat <<EOF 1>&2
Usage: $name IDENTIFIER HEADER|FUNCTION [HEADER|FUNCTION]...
Examples:
$name O_RDONLY sys/stat.h sys/types.h fcntl.h
$name O_RDONLY 'open(2)'
$name 'struct sockaddr(|_in)?' sys/types.h sys/socket.h netinet/in.h
$name 'struct sockaddr(|_in)?' 'socket(2)' 'ip(7)'
EOF
#Arguments:
# IDENTIFIER is regex matching a C identifier
# HEADER is a C include file, without the < >
# FUNCTION is a function with an optional man section in parens
}
main()
{
if test $# -lt 2; then
usage
exit 2
fi
# identifier is always the first argument...
local args=("$1")
# now look at the remaining arguments
# they are either headers, which should be passed on verbatim
# or are function/syscall names, in which case we have to find
# which headers should be included according to the man pages
shift
i=1
for arg in "$@"; do
# accept open() or open(2), convert to open or open(2)
if [[ $arg =~ (.*)\((.*)\) ]]; then
page=${BASH_REMATCH[1]}
section=${BASH_REMATCH[2]}
section="${section:+($section)}"
headers=$(get_headers "$page$section")
for header in $headers; do
args[$i]=$header
i=$((i+1))
done
else
args[$i]=$arg
i=$((i+1))
fi
done
print_definitions "${args[@]}"
}
# only run the main script if it was executed
# allows sourcing of the script to get function definitions
# works out of the box in bash and ksh
# zsh requires unsetopt functionargzero in ~/.zshenv
case "$0" in *$name)
main "$@"
;;
esac