]> git.saurik.com Git - apple/mdnsresponder.git/blob - Clients/dnssdutil/dns-rr-func-autogen
mDNSResponder-1310.80.1.tar.gz
[apple/mdnsresponder.git] / Clients / dnssdutil / dns-rr-func-autogen
1 #! /bin/bash
2 #
3 # Copyright (c) 2019-2020 Apple Inc. All rights reserved.
4 #
5
6 declare -r version=1.3
7 declare -r script=${BASH_SOURCE[0]}
8 declare -r recordTypesURL='https://www.iana.org/assignments/dns-parameters/dns-parameters-4.csv'
9
10 #============================================================================================================================
11
12 PrintHelp()
13 {
14 echo ""
15 echo "Usage: $( basename "${script}" ) [options]"
16 echo ""
17 echo "Options:"
18 echo " -O '<name>,<value>' Specifies a record name-value pair override. Can be used more than once."
19 echo " -h Display script usage."
20 echo " -V Display version of this script and exit."
21 echo ""
22 echo "This script writes C functions to convert DNS resource record type values to strings and vice versa to stdout"
23 echo "based on the latest DNS resource record type data available at"
24 echo ""
25 echo " ${recordTypesURL}"
26 echo ""
27 }
28
29 #============================================================================================================================
30
31 ErrQuit()
32 {
33 echo "error: $*" 1>&2
34 exit 1
35 }
36
37 #============================================================================================================================
38
39 StripLeadingTrailingWhitespace()
40 {
41 sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
42 }
43
44 #============================================================================================================================
45
46 GetNamesAndValues()
47 {
48 local -r isOverride=${1}
49 shopt -s nocasematch
50 while IFS=',' read name value others; do
51 name=$( StripLeadingTrailingWhitespace <<< "${name}" )
52 [[ ${name} =~ ^unassigned$ ]] && continue
53
54 value=$( StripLeadingTrailingWhitespace <<< "${value}" )
55 [[ ${value} =~ ^[0-9]+$ ]] || continue
56 [ "${value}" -le 65535 ] || continue
57
58 if [ "${value}" -eq 255 ]; then
59 name=ANY
60 fi
61 echo "${name},${value},${isOverride}"
62 done
63 shopt -u nocasematch
64 }
65
66 #============================================================================================================================
67
68 PrintRecordTypesEnum()
69 {
70 local -r inputFile=${1}
71 printf "typedef enum\n"
72 printf "{\n"
73 < "${inputFile}" sort --field-separator=, --key=2,2 --numeric-sort --unique |
74 while IFS=',' read name value override; do
75 name="${name//[^A-Za-z0-9_]/_}" # Only allow alphanumeric and underscore characters.
76 printf "\tkDNSRecordType_%-10s = %d," "${name}" "${value}"
77 if [ "${override}" -ne 0 ]; then
78 printf " // OVERRIDE"
79 fi
80 printf "\n"
81 done
82 printf "\t\n"
83 printf "}\tDNSRecordType;\n"
84 }
85
86 #============================================================================================================================
87
88 PrintValueToStringElseIf()
89 {
90 local -r first=${1}
91 local -r last=${2}
92 [ "${first}" -le "${last}" ] || ErrQuit "${first} > ${last}"
93 shift 2
94 local stringArray=( "$@" )
95
96 if [ "${last}" -ne "${first}" ]; then
97 printf "\telse if( ( inValue >= ${first} ) && ( inValue <= ${last} ) )\n"
98 local -r arrayVarName="sNames_${first}_${last}"
99 else
100 printf "\telse if( inValue == ${first} )\n"
101 local -r arrayVarName="sNames_${first}"
102 fi
103 printf "\t{\n"
104 printf "\t\tstatic const char * const\t\t${arrayVarName}[] =\n"
105 printf "\t\t{\n"
106 local value=${first}
107 for string in "${stringArray[@]}"; do
108 printf "\t\t\t%-15s // %3d\n" "\"${string}\"," "${value}"
109 value=$(( value + 1 ))
110 done
111 local -r stringCount=$(( value - first ))
112 local -r expectedCount=$(( last - first + 1 ))
113 [ "${stringCount}" -eq "${expectedCount}" ] || ErrQuit "${stringCount} != ${expectedCount}"
114 printf "\t\t};\n"
115 printf "\t\tstring = ${arrayVarName}[ inValue - ${first} ];\n"
116 printf "\t}\n"
117 }
118
119 #============================================================================================================================
120
121 PrintValueToStringFunction()
122 {
123 local -r inputFile=${1}
124 printf "const char *\tDNSRecordTypeValueToString( int inValue )\n"
125 printf "{\n"
126 printf "\tconst char *\t\tstring;\n"
127 printf "\t\n"
128 printf "\tif( 0 ) {}\n"
129 < "${inputFile}" sort --field-separator=, --key=2,2 --numeric-sort --unique |
130 {
131 local first=-1
132 local last=-1
133 local next=-1
134 local stringArray=()
135 while IFS=',' read name value override; do
136 if [ "${value}" -ne "${next}" ]; then
137 [ "${first}" -ge 0 ] && PrintValueToStringElseIf "${first}" "${last}" "${stringArray[@]}"
138 first=${value}
139 stringArray=()
140 fi
141 stringArray+=( "${name}" )
142 last=${value}
143 next=$(( value + 1 ))
144 done
145 [ "${first}" -ge 0 ] && PrintValueToStringElseIf "${first}" "${last}" "${stringArray[@]}"
146 }
147 printf "\telse\n"
148 printf "\t{\n"
149 printf "\t\tstring = NULL;\n"
150 printf "\t}\n"
151 printf "\treturn( string );\n"
152 printf "}\n"
153 }
154
155 #============================================================================================================================
156
157 PrintStringToValueFunction()
158 {
159 local -r inputFile=${1}
160 printf "#include <stdlib.h>\n"
161 printf "\n"
162 printf "typedef struct\n"
163 printf "{\n"
164 printf "\tconst char *\t\tname;\n"
165 printf "\tuint16_t\t\t\tvalue;\n"
166 printf "\t\n"
167 printf "}\t_DNSRecordTypeItem;\n"
168 printf "\n"
169 printf "static int\t_DNSRecordTypeStringToValueCmp( const void *inKey, const void *inElement );\n"
170 printf "\n"
171 printf "uint16_t\tDNSRecordTypeStringToValue( const char *inString )\n"
172 printf "{\n"
173 printf "\t// The name-value table is sorted by name in ascending lexicographical order to allow going from name to\n"
174 printf "\t// value in logarithmic time via a binary search.\n"
175 printf "\t\n"
176 printf "\tstatic const _DNSRecordTypeItem\t\tsTable[] =\n"
177 printf "\t{\n"
178
179 < "${inputFile}" sort --field-separator=, --key=1,1 --ignore-case --unique |
180 while IFS=',' read name value override; do
181 printf "\t\t{ %-13s %5d }," "\"${name}\"," "${value}"
182 if [ "${override}" -ne 0 ]; then
183 printf " // OVERRIDE"
184 fi
185 printf "\n"
186 done
187 printf "\t};\n"
188 printf "\tconst _DNSRecordTypeItem *\t\t\titem;\n"
189 printf "\t\n"
190 printf "\titem = (_DNSRecordTypeItem *) bsearch( inString, sTable, sizeof( sTable ) / sizeof( sTable[ 0 ] ),\n"
191 printf "\t\tsizeof( sTable[ 0 ] ), _DNSRecordTypeStringToValueCmp );\n"
192 printf "\treturn( item ? item->value : 0 );\n"
193 printf "}\n"
194 printf "\n"
195 printf "static int\t_DNSRecordTypeStringToValueCmp( const void *inKey, const void *inElement )\n"
196 printf "{\n"
197 printf "\tconst _DNSRecordTypeItem * const\t\titem = (const _DNSRecordTypeItem *) inElement;\n"
198 printf "\treturn( strcasecmp( (const char *) inKey, item->name ) );\n"
199 printf "}\n"
200 }
201
202 #============================================================================================================================
203
204 ExitHandler()
205 {
206 if [ -d "${tempDir}" ]; then
207 rm -fr "${tempDir}"
208 fi
209 }
210
211 #============================================================================================================================
212
213 PrintAutoGenNote()
214 {
215 printf "// This code was autogenerated on $( date -u '+%Y-%m-%d' ) by $( basename ${script} ) version ${version}\n"
216 printf "// Data source URL: ${recordTypesURL}\n"
217 printf "// Overrides: "
218 if [ "${#}" -gt 0 ]; then
219 local separator=""
220 for override in "${@}"; do
221 printf "%s'%s'" "${separator}" "${override}"
222 separator=", "
223 done
224 printf "\n"
225 else
226 printf "none\n"
227 fi
228 printf "\n"
229 }
230
231 #============================================================================================================================
232
233 main()
234 {
235 local -a overrides
236 while getopts ":hO:V" option; do
237 case "${option}" in
238 h)
239 PrintHelp
240 exit 0
241 ;;
242 O)
243 overrides+=( "${OPTARG}" )
244 ;;
245 V)
246 echo "$( basename "${script}" ) version ${version}"
247 exit 0
248 ;;
249 :)
250 ErrQuit "option '${OPTARG}' requires an argument."
251 ;;
252 *)
253 ErrQuit "unknown option '${OPTARG}'."
254 ;;
255 esac
256 done
257
258 [ "${OPTIND}" -gt "$#" ] || ErrQuit "unexpected argument \"${!OPTIND}\"."
259
260 trap ExitHandler EXIT
261 tempDir=$( mktemp -d ) || ErrQuit "Failed to make temporary directory."
262 declare -r originalRecordTypesFile="${tempDir}/recordTypesOriginal.csv"
263 curl --output "${originalRecordTypesFile}" "${recordTypesURL}" || ErrQuit "Failed to download CSV file."
264
265 declare -r overridesFile="${tempDir}/overrides.csv"
266 for override in "${overrides[@]}"; do
267 echo "${override}"
268 done | GetNamesAndValues 1 > "${overridesFile}"
269
270 declare -r recordTypesFile="${tempDir}/recordTypes.csv"
271 < "${originalRecordTypesFile}" GetNamesAndValues 0 > "${recordTypesFile}"
272
273 declare -r tempFile="${tempDir}/temp.csv"
274 cat "${overridesFile}" "${recordTypesFile}" | sort --field-separator=, --key=2,2 --unique --numeric-sort > "${tempFile}"
275 cat "${overridesFile}" "${tempFile}" | sort --field-separator=, --key=1,1 --unique --ignore-case > "${recordTypesFile}"
276
277 PrintAutoGenNote "${overrides[@]}"
278 PrintRecordTypesEnum "${recordTypesFile}"
279 printf "\n"
280 PrintAutoGenNote "${overrides[@]}"
281 PrintValueToStringFunction "${recordTypesFile}"
282 printf "\n"
283 PrintAutoGenNote "${overrides[@]}"
284 PrintStringToValueFunction "${recordTypesFile}"
285 }
286
287 main "$@"