#!/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 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) { gsub(" *\\/\\*[^*]*\\*\\/ *", "", stmnt) 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) if (RHEL_MAJOR == "") gsub("defined\\(RHEL_MAJOR\\)", "0", stmnt) else { gsub("defined\\(RHEL_MAJOR\\)", "1", stmnt) gsub("RHEL_MAJOR", RHEL_MAJOR, stmnt) } if (RHEL_MINOR == "") gsub("defined\\(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) } 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] == "<=") 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="([0-9]+) *\\|\\| *([0-9]+)" while (match(stmnt, pattern, op) != 0) { sub(pattern, (op[1] != 0) || (op[2] != 0), stmnt) } pattern="\\(([0-9]+)\\)" while (match(stmnt, pattern, op) != 0) { sub(pattern, op[1], 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") { #print $0 " -> " evaluated $0 = evaluated } else { evaluated = "+#if undecided" } #printf "%s -> %s\n", $0, evaluated if (evaluated ~ "^+#if") { if_stmnt[if_nesting_level] = evaluated } 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 evaluated = evaluate($0) condition = 1 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 && (! condition || condition && matching_if !~ "^+#if [01]")) { line[lines++]=$0 } else { lines_deleted++ } } function dump_lines() { 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] lines = 0 lines_deleted = 0 } 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 } # Variable initialization. lines = 0 lines_deleted = 0 output = 1 if_nesting_level = -1 } { # 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() match($0, "^@@ -([0-9]*),([0-9]*) \\+([0-9]*),([0-9]*) @@(.*)$", h) } else if (match($0, "^+ *#")) { process_preprocessor_statement() } else if (output) { # Store the line that was just read. line[lines++]=$0 } else { # Discard the last read line. lines_deleted++ } } END { # Dump processed contents of the last read hunk. dump_lines() }