3 # Copyright (c) 2019-2020 Apple Inc. All rights reserved.
7 declare -r script=${BASH_SOURCE[0]}
8 declare -r recordTypesURL
='https://www.iana.org/assignments/dns-parameters/dns-parameters-4.csv'
10 #============================================================================================================================
15 echo "Usage: $( basename "${script}" ) [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."
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"
25 echo " ${recordTypesURL}"
29 #============================================================================================================================
37 #============================================================================================================================
39 StripLeadingTrailingWhitespace
()
41 sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
44 #============================================================================================================================
48 local -r isOverride
=${1}
50 while IFS
=',' read name value others
; do
51 name
=$( StripLeadingTrailingWhitespace <<< "${name}" )
52 [[ ${name} =~ ^unassigned$
]] && continue
54 value
=$( StripLeadingTrailingWhitespace <<< "${value}" )
55 [[ ${value} =~ ^
[0-9]+$
]] || continue
56 [ "${value}" -le 65535 ] || continue
58 if [ "${value}" -eq 255 ]; then
61 echo "${name},${value},${isOverride}"
66 #============================================================================================================================
68 PrintRecordTypesEnum
()
70 local -r inputFile
=${1}
71 printf "typedef enum\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
83 printf "}\tDNSRecordType;\n"
86 #============================================================================================================================
88 PrintValueToStringElseIf
()
92 [ "${first}" -le "${last}" ] || ErrQuit
"${first} > ${last}"
94 local stringArray
=( "$@" )
96 if [ "${last}" -ne "${first}" ]; then
97 printf "\telse if( ( inValue >= ${first} ) && ( inValue <= ${last} ) )\n"
98 local -r arrayVarName
="sNames_${first}_${last}"
100 printf "\telse if( inValue == ${first} )\n"
101 local -r arrayVarName
="sNames_${first}"
104 printf "\t\tstatic const char * const\t\t${arrayVarName}[] =\n"
107 for string
in "${stringArray[@]}"; do
108 printf "\t\t\t%-15s // %3d\n" "\"${string}\"," "${value}"
109 value
=$(( value + 1 ))
111 local -r stringCount
=$(( value - first ))
112 local -r expectedCount
=$(( last - first + 1 ))
113 [ "${stringCount}" -eq "${expectedCount}" ] || ErrQuit
"${stringCount} != ${expectedCount}"
115 printf "\t\tstring = ${arrayVarName}[ inValue - ${first} ];\n"
119 #============================================================================================================================
121 PrintValueToStringFunction
()
123 local -r inputFile
=${1}
124 printf "const char *\tDNSRecordTypeValueToString( int inValue )\n"
126 printf "\tconst char *\t\tstring;\n"
128 printf "\tif( 0 ) {}\n"
129 < "${inputFile}" sort --field-separator=, --key=2,2 --numeric-sort --unique |
135 while IFS
=',' read name value override
; do
136 if [ "${value}" -ne "${next}" ]; then
137 [ "${first}" -ge 0 ] && PrintValueToStringElseIf
"${first}" "${last}" "${stringArray[@]}"
141 stringArray
+=( "${name}" )
143 next
=$(( value + 1 ))
145 [ "${first}" -ge 0 ] && PrintValueToStringElseIf
"${first}" "${last}" "${stringArray[@]}"
149 printf "\t\tstring = NULL;\n"
151 printf "\treturn( string );\n"
155 #============================================================================================================================
157 PrintStringToValueFunction
()
159 local -r inputFile
=${1}
160 printf "#include <stdlib.h>\n"
162 printf "typedef struct\n"
164 printf "\tconst char *\t\tname;\n"
165 printf "\tuint16_t\t\t\tvalue;\n"
167 printf "}\t_DNSRecordTypeItem;\n"
169 printf "static int\t_DNSRecordTypeStringToValueCmp( const void *inKey, const void *inElement );\n"
171 printf "uint16_t\tDNSRecordTypeStringToValue( const char *inString )\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"
176 printf "\tstatic const _DNSRecordTypeItem\t\tsTable[] =\n"
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"
188 printf "\tconst _DNSRecordTypeItem *\t\t\titem;\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"
195 printf "static int\t_DNSRecordTypeStringToValueCmp( const void *inKey, const void *inElement )\n"
197 printf "\tconst _DNSRecordTypeItem * const\t\titem = (const _DNSRecordTypeItem *) inElement;\n"
198 printf "\treturn( strcasecmp( (const char *) inKey, item->name ) );\n"
202 #============================================================================================================================
206 if [ -d "${tempDir}" ]; then
211 #============================================================================================================================
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
220 for override
in "${@}"; do
221 printf "%s'%s'" "${separator}" "${override}"
231 #============================================================================================================================
236 while getopts ":hO:V" option
; do
243 overrides
+=( "${OPTARG}" )
246 echo "$( basename "${script}" ) version ${version}"
250 ErrQuit
"option '${OPTARG}' requires an argument."
253 ErrQuit
"unknown option '${OPTARG}'."
258 [ "${OPTIND}" -gt "$#" ] || ErrQuit
"unexpected argument \"${!OPTIND}\"."
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."
265 declare -r overridesFile
="${tempDir}/overrides.csv"
266 for override
in "${overrides[@]}"; do
268 done | GetNamesAndValues
1 > "${overridesFile}"
270 declare -r recordTypesFile
="${tempDir}/recordTypes.csv"
271 < "${originalRecordTypesFile}" GetNamesAndValues
0 > "${recordTypesFile}"
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}"
277 PrintAutoGenNote
"${overrides[@]}"
278 PrintRecordTypesEnum
"${recordTypesFile}"
280 PrintAutoGenNote
"${overrides[@]}"
281 PrintValueToStringFunction
"${recordTypesFile}"
283 PrintAutoGenNote
"${overrides[@]}"
284 PrintStringToValueFunction
"${recordTypesFile}"