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