fce6920a7cde488b31b7f7a1e3a37df3672c5376
[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   # Remove C-style comments.
46   gsub(" *\\/\\*[^*]*\\*\\/ *", "", stmnt)
47
48   # Remove the spaces before the #-sign.
49   gsub("^+ *# *", "+#", stmnt)
50
51   if (match(stmnt, "^+#ifdef (.*)$", arg))
52   {
53     stmnt = "+#if defined(" arg[1] ")"
54   }
55
56   if (match(stmnt, "^+#ifndef (.*)$", arg))
57   {
58     stmnt = "+#if ! defined(" arg[1] ")"
59   }
60
61   gsub("LINUX_VERSION_CODE", LINUX_VERSION_CODE, stmnt)
62
63   gsub("defined\\(INSIDE_KERNEL_TREE\\)", "1", stmnt)
64
65   gsub("defined\\(BACKPORT_LINUX_WORKQUEUE_TO_2_6_19\\)", "0", stmnt)
66
67   if (RHEL_MAJOR == "")
68     gsub("defined\\(RHEL_MAJOR\\)", "0", stmnt)
69   else
70   {
71     gsub("defined\\(RHEL_MAJOR\\)", "1", stmnt)
72     gsub("RHEL_MAJOR", RHEL_MAJOR, stmnt)
73   }
74
75   if (RHEL_MINOR == "")
76     gsub("defined\\(RHEL_MINOR\\)", "0", stmnt)
77   else
78   {
79     gsub("defined\\(RHEL_MINOR\\)", "1", stmnt)
80     gsub("RHEL_MINOR", RHEL_MINOR, stmnt)
81   }
82
83   if (RHEL_MAJOR == "" || RHEL_MINOR == "")
84   {
85     gsub("defined\\(RHEL_RELEASE_CODE\\)", "0", stmnt)
86     gsub("RHEL_RELEASE_CODE", "", stmnt)
87   }
88   else
89   {
90     gsub("defined\\(RHEL_RELEASE_CODE\\)", "1", stmnt)
91     gsub("RHEL_RELEASE_CODE", RHEL_MAJOR * 256 + RHEL_MINOR, stmnt)
92   }
93
94   gsub("defined  *SCSI_EXEC_REQ_FIFO_DEFINED", "1", stmnt)
95   gsub("defined *\\( *SCSI_EXEC_REQ_FIFO_DEFINED *\\)", "1", stmnt)
96
97   do
98   {
99     last_stmnt = stmnt
100
101     pattern = "! *([0-9]+)"
102     while (match(stmnt, pattern, op) != 0)
103     {
104       sub(pattern, op[1] == 0, stmnt)
105     }
106     
107     pattern="KERNEL_VERSION\\( *([0-9]+) *, *([0-9]+) *, *([0-9]+) *\\)"
108     while (match(stmnt, pattern, op) != 0)
109     {
110       sub(pattern, op[1] * 65536 + op[2] * 256 + op[3], stmnt)
111     }
112   
113     pattern="([0-9]+) *(<|<=|>|>=|==) *([0-9]+)"
114     while (match(stmnt, pattern, op) != 0)
115     {
116       result="error"
117       if      (op[2] == "<" ) result = op[1] <  op[3]
118       else if (op[2] == "<=") result = op[1] <= op[3]
119       else if (op[2] == ">" ) result = op[1] >  op[3]
120       else if (op[2] == ">=") result = op[1] >= op[3]
121       else if (op[2] == "==") result = op[1] == op[3]
122       sub(pattern, result, stmnt)
123     }
124   
125     pattern="([0-9]+) *\\&\\& *([0-9]+)"
126     while (match(stmnt, pattern, op) != 0)
127     {
128       sub(pattern, (op[1] != 0) && (op[2] != 0), stmnt)
129     }
130   
131     pattern="([0-9]+) *\\&\\& *(!* *defined\\([^)]*\\))"
132     while (match(stmnt, pattern, op) != 0)
133     {
134       sub(pattern, (op[1] != 0) ? op[2] : "0", stmnt)
135     }
136   
137     pattern="([0-9]+) *\\|\\| *([0-9]+)"
138     while (match(stmnt, pattern, op) != 0)
139     {
140       sub(pattern, (op[1] != 0) || (op[2] != 0), stmnt)
141     }
142   
143     pattern="([0-9]+) *\\|\\| *(!* *defined\\([^)]*\\))"
144     while (match(stmnt, pattern, op) != 0)
145     {
146       sub(pattern, (op[1] != 0) ? "1" : op[2], stmnt)
147     }
148   
149     pattern="\\(([0-9]+)\\)"
150     while (match(stmnt, pattern, op) != 0)
151     {
152       sub(pattern, op[1], stmnt)
153     }
154   } while (stmnt != last_stmnt)
155
156   return stmnt
157 }
158
159
160 # Evaluate ! stmnt
161 function invert(stmnt) {
162   sub("^+#if ", "+#if ! ", stmnt)
163   return evaluate(stmnt)
164 }
165
166
167 # Handle #if or #elif
168
169 function handle_if()
170 {
171   # Only act on preprocessor conditional expressions with regard to the Linux
172   # kernel version, and do not interpret other expressions.
173   if ($0 ~ "LINUX_VERSION_CODE"    \
174       || $0 ~ "INSIDE_KERNEL_TREE" \
175       || $0 ~ "RHEL_MAJOR"         \
176       || $0 ~ "RHEL_MINOR"         \
177       || $0 ~ "RHEL_RELEASE_CODE")
178   {
179     #print $0 " -> " evaluated
180     $0 = evaluated
181   }
182   else
183   {
184     evaluated = "+#if undecided"
185   }
186   #printf "%s -> %s\n", $0, evaluated
187   if (evaluated ~ "^+#if")
188   {
189     if_stmnt[if_nesting_level] = evaluated
190   }
191   else
192   {
193     sub("^+#elif ",
194         sprintf("+#if ! %d \\&\\& ", decision[if_nesting_level]),
195         evaluated)
196     evaluated = evaluate(evaluated)
197   }
198   decision[if_nesting_level] = evaluated
199   matching_if = if_stmnt[if_nesting_level]
200 }
201
202
203 # Decide whether or not to print the preprocessor statement $0.
204
205 function process_preprocessor_statement() {
206   last_if_nesting_level = if_nesting_level
207   evaluated = evaluate($0)
208   condition = 1
209   if (evaluated ~ "^+#if")
210   {
211     if_nesting_level++
212     handle_if()
213   }
214   else if (evaluated ~ "^+#elif")
215   {
216     handle_if()
217   }
218   else if (evaluated ~ "^+#else")
219   {
220     matching_if = if_stmnt[if_nesting_level]
221     decision[if_nesting_level] = invert(decision[if_nesting_level])
222   }
223   else if (evaluated ~ "^+#endif")
224   {
225     matching_if = if_stmnt[if_nesting_level]
226     if_nesting_level--
227   }
228   else
229   {
230     condition = 0
231   }
232   if (condition)
233   {
234     output = 1
235     for (i = if_nesting_level; i >= 0; i--)
236     {
237       output = output && decision[i] != "+#if 0"
238     }
239   }
240   if (output && (! condition || condition && matching_if !~ "^+#if [01]") \
241       && ! (evaluated ~ "^+#define SCSI_EXEC_REQ_FIFO_DEFINED$"))
242   {
243     line[lines++]=$0
244   }
245   else
246   {
247     lines_deleted++
248   }
249 }
250
251 function dump_lines() {
252   if (h[0] != "")
253     printf "@@ -%d,%d +%d,%d @@%s\n",h[1],h[2],h[3],h[4]-lines_deleted,h[5]
254   for (i = 0; i < lines; i++)
255     print line[i]
256   lines = 0
257   lines_deleted = 0
258 }
259
260 BEGIN {
261   # Verify arguments.
262   if (kernel_version == "")
263   {
264     printf "Error: kernel_version was not specified.\n"
265     exit 1
266   }
267   LINUX_VERSION_CODE = version_code(kernel_version)
268   if (LINUX_VERSION_CODE < 2*65536 || LINUX_VERSION_CODE > 3*65536)
269   {
270     printf "Error: kernel version (%s) is out of range.\n", kernel_version
271     exit 1
272   }
273
274   # Variable initialization.
275   lines = 0
276   lines_deleted = 0
277   output = 1
278   if_nesting_level = -1
279 }
280
281
282 {
283   # If the line currently being processed is a hunk header, print all lines
284   # that were stored in the array line[] since the last hunk header was read.
285   if (match($0, "^@@ -([0-9]*),([0-9]*) \\+([0-9]*),([0-9]*) @@(.*)$"))
286   {
287     /* print h[1], h[2], h[3], h[4], h[5] */
288     dump_lines()
289     match($0, "^@@ -([0-9]*),([0-9]*) \\+([0-9]*),([0-9]*) @@(.*)$", h)
290   }
291   else if (match($0, "^+ *#"))
292   {
293     process_preprocessor_statement()
294   }
295   else if (output)
296   {
297     # Store the line that was just read.
298     line[lines++]=$0
299   }
300   else
301   {
302     # Discard the last read line.
303     lines_deleted++
304   }
305 }
306
307 END {
308   # Dump processed contents of the last read hunk.
309   dump_lines()
310 }