#!/usr/bin/gawk -f ############################################################################ # # Script that removes preprocessor checks on the kernel version. Somewhat # related to the v4l-scripts-gentree.pl script. # # Copyright (C) 2008-2009 Bart Van Assche # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # ############################################################################ # Usage: # * Specify the kernel version code as follows: -v kernel_version=... # * Provide the patch to be processed to stdin. # # The output of this script will be a patch that is specialized for the # specified kernel version. # Convert a kernel version in the x.y.z format into numeric form, just like # the KERNEL_VERSION() macro. function version_code(kver) { match(kver, "([0-9]+).([0-9]+).([0-9]+)", array) return 65536*array[1] + 256*array[2] + array[3] } # Evaluate a preprocessor statement via repeated substitutions. # Mathematicians call this algorithm 'term rewriting'. # Note: the order in which the substitutions appear below is important -- # it is the same order as the order of operators in C. function evaluate(stmnt) { if (debug) printf "/* debug specialize-patch: (a) %s */\n", stmnt # Remove C-style comments. gsub(" *\\/\\*[^*]*\\*\\/ *", "", stmnt) # Remove the spaces before the #-sign. gsub("^+ *# *", "+#", stmnt) if (match(stmnt, "^+#ifdef (.*)$", arg)) { stmnt = "+#if defined(" arg[1] ")" } if (match(stmnt, "^+#ifndef (.*)$", arg)) { stmnt = "+#if ! defined(" arg[1] ")" } gsub("LINUX_VERSION_CODE", LINUX_VERSION_CODE, stmnt) gsub("defined\\(INSIDE_KERNEL_TREE\\)", "1", stmnt) gsub("defined\\(BACKPORT_LINUX_WORKQUEUE_TO_2_6_19\\)", "0", stmnt) gsub("defined\\(CONFIG_SUSE_KERNEL\\)", "0", stmnt) if (RHEL_MAJOR == "") { gsub("defined\\(RHEL_MAJOR\\)", "0", stmnt) gsub("RHEL_MAJOR", "", stmnt) } else { gsub("defined\\(RHEL_MAJOR\\)", "1", stmnt) gsub("RHEL_MAJOR", RHEL_MAJOR, stmnt) } if (RHEL_MINOR == "") { gsub("defined\\(RHEL_MINOR\\)", "0", stmnt) gsub("RHEL_MINOR", "0", stmnt) } else { gsub("defined\\(RHEL_MINOR\\)", "1", stmnt) gsub("RHEL_MINOR", RHEL_MINOR, stmnt) } if (RHEL_MAJOR == "" || RHEL_MINOR == "") { gsub("defined\\(RHEL_RELEASE_CODE\\)", "0", stmnt) gsub("RHEL_RELEASE_CODE", "", stmnt) } else { gsub("defined\\(RHEL_RELEASE_CODE\\)", "1", stmnt) gsub("RHEL_RELEASE_CODE", RHEL_MAJOR * 256 + RHEL_MINOR, stmnt) } if (SCSI_EXEC_REQ_FIFO_DEFINED != "") { gsub("defined *SCSI_EXEC_REQ_FIFO_DEFINED", SCSI_EXEC_REQ_FIFO_DEFINED, stmnt) gsub("defined *\\( *SCSI_EXEC_REQ_FIFO_DEFINED *\\)", SCSI_EXEC_REQ_FIFO_DEFINED, stmnt) } if (SCST_IO_CONTEXT != "") { gsub("defined *SCST_IO_CONTEXT", SCST_IO_CONTEXT, stmnt) gsub("defined *\\( *SCST_IO_CONTEXT *\\)", SCST_IO_CONTEXT, stmnt) } if (generating_upstream_patch_defined) { gsub("defined *GENERATING_UPSTREAM_PATCH", 1, stmnt) gsub("defined *\\( *GENERATING_UPSTREAM_PATCH *\\)", 1, stmnt) } if (config_tcp_zero_copy_transfer_completion_notification_undefined) { gsub("defined *CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION", 0, stmnt) gsub("defined *\\( *CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION *\\)", 0, stmnt) } gsub("defined *CONFIG_SCST_PROC", !config_scst_proc_undefined, stmnt) gsub("defined *\\( *CONFIG_SCST_PROC *\\)", !config_scst_proc_undefined, stmnt) if (debug) printf "/* debug specialize-patch: (b) %s */\n", stmnt do { last_stmnt = stmnt pattern = "! *([0-9]+)" while (match(stmnt, pattern, op) != 0) { sub(pattern, op[1] == 0, stmnt) } pattern="KERNEL_VERSION\\( *([0-9]+) *, *([0-9]+) *, *([0-9]+) *\\)" while (match(stmnt, pattern, op) != 0) { sub(pattern, op[1] * 65536 + op[2] * 256 + op[3], stmnt) } pattern="(-*[0-9]+) *(\\*|/) *(-*[0-9]+)" while (match(stmnt, pattern, op) != 0) { result="error" if (op[2] == "*") result = op[1] * op[3] else if (op[2] == "/" && op[3] != 0) result = op[1] / op[3] sub(pattern, result, stmnt) } pattern="(-*[0-9]+) *(\\+|-) *(-*[0-9]+)" while (match(stmnt, pattern, op) != 0) { result="error" if (op[2] == "+") result = op[1] * op[3] else if (op[2] == "-") result = op[1] / op[3] sub(pattern, result, stmnt) } pattern="(-*[0-9]+) *(<|<=|>|>=|==|!=) *(-*[0-9]+)" while (match(stmnt, pattern, op) != 0) { result="error" if (op[2] == "<" ) result = op[1] < op[3] else if (op[2] == "<=") result = op[1] <= op[3] else if (op[2] == ">" ) result = op[1] > op[3] else if (op[2] == ">=") result = op[1] >= op[3] else if (op[2] == "==") result = op[1] == op[3] else if (op[2] == "!=") result = op[1] != op[3] sub(pattern, result, stmnt) } pattern="(-*[0-9]+) *\\&\\& *(-*[0-9]+)" while (match(stmnt, pattern, op) != 0) { sub(pattern, (op[1] != 0) && (op[2] != 0), stmnt) } pattern="([01]) *\\&\\& *(!* *defined *\\( *[A-Za-z_]* *\\))" while (match(stmnt, pattern, op) != 0) { sub(pattern, op[1] != 0 ? op[2] : op[1], stmnt) } pattern="(-*[0-9]+) *\\|\\| *(-*[0-9]+)" while (match(stmnt, pattern, op) != 0) { sub(pattern, (op[1] != 0) || (op[2] != 0), stmnt) } pattern="([01]) *\\|\\| *(!* *defined *\\( *[A-Za-z_]* *\\))" while (match(stmnt, pattern, op) != 0) { sub(pattern, op[1] == 0 ? op[2] : op[1], stmnt) } pattern="\\((-*[0-9]+)\\)" while (match(stmnt, pattern, op) != 0) { sub(pattern, op[1], stmnt) } if (debug) printf "/* debug specialize-patch: (c) %s -> %s */\n", last_stmnt, stmnt } while (stmnt != last_stmnt) return stmnt } # Evaluate ! stmnt function invert(stmnt) { sub("^+#if ", "+#if ! ", stmnt) return evaluate(stmnt) } # Handle #if or #elif function handle_if() { # Only act on preprocessor conditional expressions with regard to the Linux # kernel version, and do not interpret other expressions. if ($0 ~ "LINUX_VERSION_CODE" \ || $0 ~ "INSIDE_KERNEL_TREE" \ || $0 ~ "RHEL_MAJOR" \ || $0 ~ "RHEL_MINOR" \ || $0 ~ "RHEL_RELEASE_CODE" \ || $0 ~ "GENERATING_UPSTREAM_PATCH" \ || $0 ~ "CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION" \ || ($0 ~ "CONFIG_SCST_PROC" && config_scst_proc_undefined) \ || ($0 ~ "SCSI_EXEC_REQ_FIFO_DEFINED" && SCSI_EXEC_REQ_FIFO_DEFINED!="") \ || ($0 ~ "SCST_IO_CONTEXT" && SCST_IO_CONTEXT != "")) { #print $0 " -> " evaluated $0 = evaluated } else { evaluated = "+#if undecided" } if (debug) printf "/* debug specialize-patch: (d) %s -> %s */\n", $0, evaluated if (evaluated ~ "^+#if") { if_stmnt[if_nesting_level] = evaluated any_section_output[if_nesting_level] = 0 } else { sub("^+#elif ", sprintf("+#if ! %d \\&\\& ", decision[if_nesting_level]), evaluated) evaluated = evaluate(evaluated) } decision[if_nesting_level] = evaluated matching_if = if_stmnt[if_nesting_level] } # Decide whether or not to print the preprocessor statement $0. function process_preprocessor_statement() { last_if_nesting_level = if_nesting_level orig_stmnt = $0 evaluated = evaluate($0) condition = 1 delete_next_blank_line = 0 if (evaluated ~ "^+#if") { if_nesting_level++ handle_if() } else if (evaluated ~ "^+#elif") { handle_if() } else if (evaluated ~ "^+#else") { matching_if = if_stmnt[if_nesting_level] decision[if_nesting_level] = invert(decision[if_nesting_level]) } else if (evaluated ~ "^+#endif") { matching_if = if_stmnt[if_nesting_level] if_nesting_level-- } else { condition = 0 } if (condition) { output = 1 for (i = if_nesting_level; i >= 0; i--) { output = output && decision[i] != "+#if 0" } if (output) any_section_output[if_nesting_level] = 1 } if (evaluated ~ "^+#define SCSI_EXEC_REQ_FIFO_DEFINED$" \ && SCSI_EXEC_REQ_FIFO_DEFINED != "" \ || evaluated ~ "^+#define SCST_IO_CONTEXT$" \ && SCST_IO_CONTEXT != "" \ || (evaluated ~ "^+#define CONFIG_SCST_PROC$" \ && config_scst_proc_undefined)) { if (blank_deleted_code) for (i = 0; i < input_line_count; i++) line[lines++] = "+" else { lines_deleted += input_line_count } delete_next_blank_line = 1 } else if (output && (! condition || condition && matching_if !~ "^+#if [01]")) { for (i = 0; i < input_line_count; i++) line[lines++] = input_line[i] } else { if (blank_deleted_code) for (i = 0; i < input_line_count; i++) line[lines++] = "+" else lines_deleted += input_line_count if (lines >= 1 && line[lines - 1] == "+") delete_next_blank_line = 1 } } function reset_hunk_state_variables() { lines = 0 lines_deleted = 0 output = 1 if_nesting_level = -1 delete_next_blank_line = 0 } function dump_lines() { # Detect empty hunks first_modif = -1 for (i = 0; i < lines; i++) { if (line[i] ~ "^[+-]") { first_modif = i break } } # Dump line[] as a hunk, but only if the hunk is not empty. if (first_modif >= 0) { if (h[0] != "") printf "@@ -%d,%d +%d,%d @@%s\n",h[1],h[2],h[3],h[4]-lines_deleted,h[5] for (i = 0; i < lines; i++) print line[i] } } BEGIN { # Verify arguments. if (kernel_version == "") { printf "Error: kernel_version was not specified.\n" exit 1 } LINUX_VERSION_CODE = version_code(kernel_version) if (LINUX_VERSION_CODE < 2*65536 || LINUX_VERSION_CODE > 3*65536) { printf "Error: kernel version (%s) is out of range.\n", kernel_version exit 1 } if (blank_deleted_code != 0 && blank_deleted_code != 1) blank_deleted_code = 0 if (generating_upstream_patch_defined != 0 && generating_upstream_patch_defined != 1) generating_upstream_patch_defined = 0 if (config_tcp_zero_copy_transfer_completion_notification_undefined != 0 && config_tcp_zero_copy_transfer_completion_notification_undefined != 1) config_tcp_zero_copy_transfer_completion_notification_undefined = 0 if (config_scst_proc_undefined != 0 && config_scst_proc_undefined != 1) config_scst_proc_undefined = 0 # Variable initialization. reset_hunk_state_variables() } { if (!config_scst_proc_undefined) { gsub("^+/\\* #define CONFIG_SCST_PROC \\*/$", "+#define CONFIG_SCST_PROC") } else { gsub("^+/\\* #define CONFIG_SCST_PROC \\*/$", "+") } input_line[0] = $0 input_line_count = 1 # Join continued lines before processing these. while (match($0, "\\\\$")) { previous_line = $0 sub("\\\\$", "", previous_line) getline input_line[input_line_count++] = $0 sub("^+", "", $0) $0 = previous_line $0 } # If the line currently being processed is a hunk header, print all lines # that were stored in the array line[] since the last hunk header was read. if (match($0, "^@@ -([0-9]*),([0-9]*) \\+([0-9]*),([0-9]*) @@(.*)$")) { /* print h[1], h[2], h[3], h[4], h[5] */ dump_lines() reset_hunk_state_variables() match($0, "^@@ -([0-9]*),([0-9]*) \\+([0-9]*),([0-9]*) @@(.*)$", h) } else if (delete_next_blank_line && match($0, "^+$")) { if (blank_deleted_code) for (i = 0; i < input_line_count; i++) line[lines++] = "+" else { lines_deleted += input_line_count } delete_next_blank_line = 0 } else { delete_next_blank_line = 0 if (match($0, "^+ *#")) { process_preprocessor_statement() } else if (output) { # Store the lines that were just read. for (i = 0; i < input_line_count; i++) line[lines++] = input_line[i] # Convert double blank lines into a single blank line. if (line[lines-1] == "+") delete_next_blank_line = 1 } else { # Discard the last read lines. if (blank_deleted_code) { for (i = 0; i < input_line_count; i++) line[lines++] = "+" } else lines_deleted += input_line_count if (lines >= 1 && line[lines-1] == "+") delete_next_blank_line = 1 } } } END { # Dump processed contents of the last read hunk. dump_lines() }