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