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