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