- Fixed bug in evaluation of expressions containing '&&' or '||'.
[mirror/scst/.git] / scripts / specialize-patch
1 #!/usr/bin/gawk -f
2
3 ############################################################################
4 #
5 # Script that removes preprocessor checks on the kernel version. Somewhat
6 # related to the v4l-scripts-gentree.pl script.
7 #
8 # Copyright (C) 2008 Bart Van Assche <bart.vanassche@gmail.com>
9 #
10 # This program is free software; you can redistribute it and/or
11 # modify it under the terms of the GNU General Public License
12 # as published by the Free Software Foundation, version 2
13 # of the License.
14 #
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
19 #
20 ############################################################################
21
22 # Usage:
23 # * Specify the kernel version code as follows: -v kernel_version=...
24 # * Provide the patch to be processed to stdin.
25 #
26 # The output of this script will be a patch that is specialized for the
27 # specified kernel version.
28
29
30 # Convert a kernel version in the x.y.z format into numeric form, just like
31 # the KERNEL_VERSION() macro.
32
33 function version_code(kver) {
34   match(kver, "([0-9]+).([0-9]+).([0-9]+)", array)
35   return 65536*array[1] + 256*array[2] + array[3]
36 }
37
38
39 # Evaluate a preprocessor statement via repeated substitutions.
40 # Mathematicians call this algorithm 'term rewriting'.
41 # Note: the order in which the substitutions appear below is important --
42 # it is the same order as the order of operators in C.
43
44 function evaluate(stmnt) {
45   gsub(" *\\/\\*[^*]*\\*\\/ *", "", stmnt)
46
47   gsub("^+ *# *", "+#", stmnt)
48
49   if (match(stmnt, "^+#ifdef (.*)$", arg))
50   {
51     stmnt = "+#if defined(" arg[1] ")"
52   }
53
54   if (match(stmnt, "^+#ifndef (.*)$", arg))
55   {
56     stmnt = "+#if ! defined(" arg[1] ")"
57   }
58
59   gsub("LINUX_VERSION_CODE", LINUX_VERSION_CODE, stmnt)
60
61   gsub("defined\\(INSIDE_KERNEL_TREE\\)", "1", stmnt)
62
63   if (RHEL_MAJOR == "")
64     gsub("defined\\(RHEL_MAJOR\\)", "0", stmnt)
65   else
66   {
67     gsub("defined\\(RHEL_MAJOR\\)", "1", stmnt)
68     gsub("RHEL_MAJOR", RHEL_MAJOR, stmnt)
69   }
70
71   if (RHEL_MINOR == "")
72     gsub("defined\\(RHEL_MINOR\\)", "0", stmnt)
73   else
74   {
75     gsub("defined\\(RHEL_MINOR\\)", "1", stmnt)
76     gsub("RHEL_MINOR", RHEL_MINOR, stmnt)
77   }
78
79   if (RHEL_MAJOR == "" || RHEL_MINOR == "")
80     gsub("defined\\(RHEL_RELEASE_CODE\\)", "0", stmnt)
81   else
82   {
83     gsub("defined\\(RHEL_RELEASE_CODE\\)", "1", stmnt)
84     gsub("RHEL_RELEASE_CODE", RHEL_MAJOR * 256 + RHEL_MINOR, stmnt)
85   }
86
87   do
88   {
89     last_stmnt = stmnt
90
91     pattern = "! *([0-9]+)"
92     while (match(stmnt, pattern, op) != 0)
93     {
94       sub(pattern, op[1] == 0, stmnt)
95     }
96     
97     pattern="KERNEL_VERSION\\(([0-9]+) *, *([0-9]+) *, *([0-9]+) *\\)"
98     while (match(stmnt, pattern, op) != 0)
99     {
100       sub(pattern, op[1] * 65536 + op[2] * 256 + op[3], stmnt)
101     }
102   
103     pattern="([0-9]+) *(<|<=|>|>=|==) *([0-9]+)"
104     while (match(stmnt, pattern, op) != 0)
105     {
106       result="error"
107       if      (op[2] == "<" ) result = op[1] <  op[3]
108       else if (op[2] == "<=") result = op[1] <= op[3]
109       else if (op[2] == ">" ) result = op[1] >  op[3]
110       else if (op[2] == ">=") result = op[1] >= op[3]
111       else if (op[2] == "==") result = op[1] == op[3]
112       sub(pattern, result, stmnt)
113     }
114   
115     pattern="([0-9]+) *\\&\\& *([0-9]+)"
116     while (match(stmnt, pattern, op) != 0)
117     {
118       sub(pattern, (op[1] != 0) && (op[2] != 0), stmnt)
119     }
120   
121     pattern="([0-9]+) *\\|\\| *([0-9]+)"
122     while (match(stmnt, pattern, op) != 0)
123     {
124       sub(pattern, (op[1] != 0) || (op[2] != 0), stmnt)
125     }
126   
127     pattern="\\(([0-9]+)\\)"
128     while (match(stmnt, pattern, op) != 0)
129     {
130       sub(pattern, op[1], stmnt)
131     }
132   } while (stmnt != last_stmnt)
133
134   return stmnt
135 }
136
137
138 # Evaluate ! stmnt
139 function invert(stmnt) {
140   sub("^+#if ", "+#if ! ", stmnt)
141   return evaluate(stmnt)
142 }
143
144
145 # Handle #if or #elif
146
147 function handle_if()
148 {
149   # Only act on preprocessor conditional expressions with regard to the Linux
150   # kernel version, and do not interpret other expressions.
151   if ($0 ~ "LINUX_VERSION_CODE"    \
152       || $0 ~ "INSIDE_KERNEL_TREE" \
153       || $0 ~ "RHEL_MAJOR"         \
154       || $0 ~ "RHEL_MINOR"         \
155       || $0 ~ "RHEL_RELEASE_CODE")
156   {
157     #print $0 " -> " evaluated
158     $0 = evaluated
159   }
160   else
161   {
162     evaluated = "+#if undecided"
163   }
164   #printf "%s -> %s\n", $0, evaluated
165   if (evaluated ~ "^+#if")
166   {
167     if_stmnt[if_nesting_level] = evaluated
168   }
169   else
170   {
171     sub("^+#elif ",
172         sprintf("+#if ! %d \\&\\& ", decision[if_nesting_level]),
173         evaluated)
174     evaluated = evaluate(evaluated)
175   }
176   decision[if_nesting_level] = evaluated
177   matching_if = if_stmnt[if_nesting_level]
178 }
179
180
181 # Decide whether or not to print the preprocessor statement $0.
182
183 function process_preprocessor_statement() {
184   last_if_nesting_level = if_nesting_level
185   evaluated = evaluate($0)
186   condition = 1
187   if (evaluated ~ "^+#if")
188   {
189     if_nesting_level++
190     handle_if()
191   }
192   else if (evaluated ~ "^+#elif")
193   {
194     handle_if()
195   }
196   else if (evaluated ~ "^+#else")
197   {
198     matching_if = if_stmnt[if_nesting_level]
199     decision[if_nesting_level] = invert(decision[if_nesting_level])
200   }
201   else if (evaluated ~ "^+#endif")
202   {
203     matching_if = if_stmnt[if_nesting_level]
204     if_nesting_level--
205   }
206   else
207   {
208     condition = 0
209   }
210   if (condition)
211   {
212     output = 1
213     for (i = if_nesting_level; i >= 0; i--)
214     {
215       output = output && decision[i] != "+#if 0"
216     }
217   }
218   if (output && (! condition || condition && matching_if !~ "^+#if [01]"))
219   {
220     line[lines++]=$0
221   }
222   else
223   {
224     lines_deleted++
225   }
226 }
227
228 function dump_lines() {
229   if (h[0] != "")
230     printf "@@ -%d,%d +%d,%d @@%s\n",h[1],h[2],h[3],h[4]-lines_deleted,h[5]
231   for (i = 0; i < lines; i++)
232     print line[i]
233   lines = 0
234   lines_deleted = 0
235 }
236
237 BEGIN {
238   # Verify arguments.
239   if (kernel_version == "")
240   {
241     printf "Error: kernel_version was not specified.\n"
242     exit 1
243   }
244   LINUX_VERSION_CODE = version_code(kernel_version)
245   if (LINUX_VERSION_CODE < 2*65536 || LINUX_VERSION_CODE > 3*65536)
246   {
247     printf "Error: kernel version (%s) is out of range.\n", kernel_version
248     exit 1
249   }
250
251   # Variable initialization.
252   lines = 0
253   lines_deleted = 0
254   output = 1
255   if_nesting_level = -1
256 }
257
258
259 {
260   # If the line currently being processed is a hunk header, print all lines
261   # that were stored in the array line[] since the last hunk header was read.
262   if (match($0, "^@@ -([0-9]*),([0-9]*) \\+([0-9]*),([0-9]*) @@(.*)$"))
263   {
264     /* print h[1], h[2], h[3], h[4], h[5] */
265     dump_lines()
266     match($0, "^@@ -([0-9]*),([0-9]*) \\+([0-9]*),([0-9]*) @@(.*)$", h)
267   }
268   else if (match($0, "^+ *#"))
269   {
270     process_preprocessor_statement()
271   }
272   else if (output)
273   {
274     # Store the line that was just read.
275     line[lines++]=$0
276   }
277   else
278   {
279     # Discard the last read line.
280     lines_deleted++
281   }
282 }
283
284 END {
285   # Dump processed contents of the last read hunk.
286   dump_lines()
287 }