1f560ef556da659874dabc56ba782def3136439d
[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   if (SCSI_EXEC_REQ_FIFO_DEFINED != "")
95   {
96     gsub("defined  *SCSI_EXEC_REQ_FIFO_DEFINED",
97          SCSI_EXEC_REQ_FIFO_DEFINED, stmnt)
98     gsub("defined *\\( *SCSI_EXEC_REQ_FIFO_DEFINED *\\)",
99          SCSI_EXEC_REQ_FIFO_DEFINED, stmnt)
100   }
101
102   if (SCST_IO_CONTEXT != "")
103   {
104     gsub("defined  *SCST_IO_CONTEXT", SCST_IO_CONTEXT, stmnt)
105     gsub("defined *\\( *SCST_IO_CONTEXT *\\)", SCST_IO_CONTEXT, stmnt)
106   }
107
108   do
109   {
110     last_stmnt = stmnt
111
112     pattern = "! *([0-9]+)"
113     while (match(stmnt, pattern, op) != 0)
114     {
115       sub(pattern, op[1] == 0, stmnt)
116     }
117     
118     pattern="KERNEL_VERSION\\( *([0-9]+) *, *([0-9]+) *, *([0-9]+) *\\)"
119     while (match(stmnt, pattern, op) != 0)
120     {
121       sub(pattern, op[1] * 65536 + op[2] * 256 + op[3], stmnt)
122     }
123   
124     pattern="(-*[0-9]+) *(\\*|/) *(-*[0-9]+)"
125     while (match(stmnt, pattern, op) != 0)
126     {
127       result="error"
128       if      (op[2] == "*") result = op[1] * op[3]
129       else if (op[2] == "/" && op[3] != 0) result = op[1] / op[3]
130       sub(pattern, result, stmnt)
131     }
132   
133     pattern="(-*[0-9]+) *(\\+|-) *(-*[0-9]+)"
134     while (match(stmnt, pattern, op) != 0)
135     {
136       result="error"
137       if      (op[2] == "+") result = op[1] * op[3]
138       else if (op[2] == "-") result = op[1] / op[3]
139       sub(pattern, result, stmnt)
140     }
141   
142     pattern="(-*[0-9]+) *(<|<=|>|>=|==) *(-*[0-9]+)"
143     while (match(stmnt, pattern, op) != 0)
144     {
145       result="error"
146       if      (op[2] == "<" ) result = op[1] <  op[3]
147       else if (op[2] == "<=") result = op[1] <= op[3]
148       else if (op[2] == ">" ) result = op[1] >  op[3]
149       else if (op[2] == ">=") result = op[1] >= op[3]
150       else if (op[2] == "==") result = op[1] == op[3]
151       sub(pattern, result, stmnt)
152     }
153   
154     pattern="(-*[0-9]+) *\\&\\& *(-*[0-9]+)"
155     while (match(stmnt, pattern, op) != 0)
156     {
157       sub(pattern, (op[1] != 0) && (op[2] != 0), stmnt)
158     }
159   
160     pattern="(-*[0-9]+) *\\|\\| *(-*[0-9]+)"
161     while (match(stmnt, pattern, op) != 0)
162     {
163       sub(pattern, (op[1] != 0) || (op[2] != 0), stmnt)
164     }
165   
166     pattern="\\((-*[0-9]+)\\)"
167     while (match(stmnt, pattern, op) != 0)
168     {
169       sub(pattern, op[1], stmnt)
170     }
171   } while (stmnt != last_stmnt)
172
173   return stmnt
174 }
175
176
177 # Evaluate ! stmnt
178 function invert(stmnt) {
179   sub("^+#if ", "+#if ! ", stmnt)
180   return evaluate(stmnt)
181 }
182
183
184 # Handle #if or #elif
185
186 function handle_if()
187 {
188   # Only act on preprocessor conditional expressions with regard to the Linux
189   # kernel version, and do not interpret other expressions.
190   if ($0 ~ "LINUX_VERSION_CODE"    \
191       || $0 ~ "INSIDE_KERNEL_TREE" \
192       || $0 ~ "RHEL_MAJOR"         \
193       || $0 ~ "RHEL_MINOR"         \
194       || $0 ~ "RHEL_RELEASE_CODE"  \
195       || $0 ~ "SCSI_EXEC_REQ_FIFO_DEFINED" \
196       || $0 ~ "SCST_IO_CONTEXT")
197   {
198     #print $0 " -> " evaluated
199     $0 = evaluated
200   }
201   else
202   {
203     evaluated = "+#if undecided"
204   }
205   #printf "%s -> %s\n", $0, evaluated
206   if (evaluated ~ "^+#if")
207   {
208     if_stmnt[if_nesting_level] = evaluated
209     any_section_output[if_nesting_level] = 0
210   }
211   else
212   {
213     sub("^+#elif ",
214         sprintf("+#if ! %d \\&\\& ", decision[if_nesting_level]),
215         evaluated)
216     evaluated = evaluate(evaluated)
217   }
218   decision[if_nesting_level] = evaluated
219   matching_if = if_stmnt[if_nesting_level]
220 }
221
222
223 # Decide whether or not to print the preprocessor statement $0.
224
225 function process_preprocessor_statement() {
226   last_if_nesting_level = if_nesting_level
227   orig_stmnt = $0
228   evaluated = evaluate($0)
229   condition = 1
230   delete_next_blank_line = 0
231   if (evaluated ~ "^+#if")
232   {
233     if_nesting_level++
234     handle_if()
235   }
236   else if (evaluated ~ "^+#elif")
237   {
238     handle_if()
239   }
240   else if (evaluated ~ "^+#else")
241   {
242     matching_if = if_stmnt[if_nesting_level]
243     decision[if_nesting_level] = invert(decision[if_nesting_level])
244   }
245   else if (evaluated ~ "^+#endif")
246   {
247     matching_if = if_stmnt[if_nesting_level]
248     delete_next_blank_line = ! any_section_output[if_nesting_level]
249     if_nesting_level--
250   }
251   else
252   {
253     condition = 0
254   }
255   if (condition)
256   {
257     output = 1
258     for (i = if_nesting_level; i >= 0; i--)
259     {
260       output = output && decision[i] != "+#if 0"
261     }
262     if (output)
263       any_section_output[if_nesting_level] = 1
264   }
265   if (delete_disabled_code                                      \
266       && (evaluated ~ "^+#define SCSI_EXEC_REQ_FIFO_DEFINED$"   \
267           || evaluated ~ "^+#define SCST_IO_CONTEXT$"))
268   {
269     lines_deleted++
270     delete_next_blank_line = 1
271   }
272   else if (delete_disabled_code == 0 || (output && (! condition || condition && matching_if !~ "^+#if [01]")))
273   {
274     line[lines++] = orig_stmnt
275   }
276   else
277   {
278     lines_deleted++
279   }
280 }
281
282 function reset_hunk_state_variables() {
283   lines = 0
284   lines_deleted = 0
285   output = 1
286   if_nesting_level = -1
287   delete_next_blank_line = 0
288 }
289
290 function dump_lines() {
291   # Detect empty hunks
292   first_modif = -1
293   for (i = 0; i < lines; i++)
294   {
295     if (line[i] ~ "^[+-]")
296     {
297       first_modif = i
298       break
299     }
300   }
301
302   # Dump line[] as a hunk, but only if the hunk is not empty.
303   if (first_modif >= 0)
304   {
305     if (h[0] != "")
306       printf "@@ -%d,%d +%d,%d @@%s\n",h[1],h[2],h[3],h[4]-lines_deleted,h[5]
307     for (i = 0; i < lines; i++)
308     print line[i]
309   }
310   reset_hunk_state_variables()
311 }
312
313 BEGIN {
314   # Verify arguments.
315   if (kernel_version == "")
316   {
317     printf "Error: kernel_version was not specified.\n"
318     exit 1
319   }
320   LINUX_VERSION_CODE = version_code(kernel_version)
321   if (LINUX_VERSION_CODE < 2*65536 || LINUX_VERSION_CODE > 3*65536)
322   {
323     printf "Error: kernel version (%s) is out of range.\n", kernel_version
324     exit 1
325   }
326   if (delete_disabled_code != 0 && delete_disabled_code != 1)
327     delete_disabled_code = 0
328
329   # Variable initialization.
330   reset_hunk_state_variables()
331 }
332
333
334 {
335   # Dump continued lines without trying to process these -- the parsing code
336   # in this script cannot handle continued lines yet.
337   if (match($0, "\\\\$"))
338   {
339     line[lines++]=$0
340     do
341     {
342       getline
343       line[lines++]=$0
344     } while (match($0, "\\\\$"))
345     getline
346   }
347
348   # If the line currently being processed is a hunk header, print all lines
349   # that were stored in the array line[] since the last hunk header was read.
350   if (match($0, "^@@ -([0-9]*),([0-9]*) \\+([0-9]*),([0-9]*) @@(.*)$"))
351   {
352     /* print h[1], h[2], h[3], h[4], h[5] */
353     dump_lines()
354     match($0, "^@@ -([0-9]*),([0-9]*) \\+([0-9]*),([0-9]*) @@(.*)$", h)
355   }
356   else if (delete_disabled_code && delete_next_blank_line && match($0, "^+$"))
357   {
358     lines_deleted++
359     delete_next_blank_line = 0
360   }
361   else
362   {
363     delete_next_blank_line = 0
364     if (match($0, "^+ *#"))
365     {
366       process_preprocessor_statement()
367     }
368     else if (delete_disabled_code == 0 || output)
369     {
370       # Store the line that was just read.
371       line[lines++]=$0
372     }
373     else
374     {
375       # Discard the last read line.
376       lines_deleted++
377     }
378   }
379 }
380
381 END {
382   # Dump processed contents of the last read hunk.
383   dump_lines()
384 }