#!/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 <bvanassche@acm.org>
#
# 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 and optionally the RHEL version as follows:
#   -v kernel_version=... -vRHEL_MAJOR=... -vRHEL_MINOR=...
# * 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_number(a, b, c) {
  return a * 65536 + b * 256 + (c > 255 ? 255 : c);
}

function version_code(kver, array) {
  if (!match(kver, "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$", array))
    match(kver, "^([0-9]+)\\.([0-9]+)$", array)
  return version_number(array[1], 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, pattern, arg, op, result) {

  if (debug)
    printf "/* debug specialize-patch: (a) %s */\n", stmnt

  # Remove C-style comments.
  gsub("[[:blank:]]*\\/\\*[^*]*\\*\\/[[:blank:]]*", "", stmnt)

  # Remove the spaces before the #-sign.
  gsub("^+[[:blank:]]*#[[:blank:]]*", "+#", stmnt)

  if (match(stmnt, "^+#ifdef (.*)$", arg))
  {
    stmnt = "+#if defined(" arg[1] ")"
  }

  if (match(stmnt, "^+#ifndef (.*)$", arg))
  {
    stmnt = "+#if !defined(" arg[1] ")"
  }

  # See also commit c9c9762d4d44 ("block: return the correct bvec when checking
  # for gaps"; v5.14-rc1).
  gsub("defined\\(bio_multiple_segments\\)",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0))", stmnt)

  gsub("defined\\(REGISTER_MAD_AGENT_HAS_FLAGS_ARG\\)",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 6 * 256 + 9)", stmnt)

  gsub("SOCK_RECVMSG_HAS_FOUR_ARGS",
       "(LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) \\&\\& " \
       "!defined(UEK_KABI_RENAME))", stmnt)

  gsub("defined\\(USE_PRE_440_WR_STRUCTURE\\)",
       "(LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) \\&\\& " \
       "RHEL_RELEASE_CODE -0 < 7 * 256 + 5)", stmnt)

  gsub("defined\\(IB_CREATE_CQ_HAS_INIT_ATTR\\)",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 7 * 256 + 5)", stmnt)

  gsub("defined\\(CREATE_SEND_MAD_HAS_AH_ARG\\)",
       "(LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16))", stmnt)

  gsub("defined\\(CREATE_SEND_MAD_HAS_BASE_ARG\\)",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 7 * 256 + 5)", stmnt)

  gsub("defined\\(IB_CM_LISTEN_TAKES_FOURTH_ARG\\)",
       "(LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) \\&\\& " \
       "RHEL_RELEASE_CODE -0 < 7 * 256 + 5)", stmnt)

  gsub("defined\\(IB_CM_LISTEN_TAKES_THIRD_ARG\\)",
       "(LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))", stmnt)

  gsub("IB_CLIENT_ADD_ONE_RETURNS_INT",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 8 * 256 + 4)", stmnt)

  gsub("defined\\(IB_CLIENT_REMOVE_TAKES_TWO_ARGS\\)",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 7 * 256 + 5)", stmnt)

  gsub("defined\\(IB_QUERY_GID_HAS_ATTR_ARG\\)",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 7 * 256 + 5)", stmnt)

  gsub("RDMA_CREATE_ID_TAKES_NET_ARG",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 7 * 256 + 5)", stmnt)

  gsub("defined\\(HAVE_DEV_ATTR_MAX_RECV_SGE\\)",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 7 * 256 + 7)", stmnt)

  gsub("HAVE_DEV_ATTR_MAX_SEND_SGE",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 7 * 256 + 7)", stmnt)

  gsub("HAVE_RDMA_DESTROY_AH_WITH_FLAGS",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 21, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 8 * 256 + 1)", stmnt)

  gsub("HAVE_RDMA_DESTROY_AH",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 7 * 256 + 5)", stmnt)

  gsub("HAVE_RDMA_QUERY_GID",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 8 * 256 + 1)", stmnt)

  gsub("HAVE_SRP_DATA_DESC_IMM",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 21, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 8 * 256 + 1)", stmnt)

  gsub("HAVE_STRUCT_SRP_LOGIN_REQ_RDMA",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0))", stmnt)

  gsub("defined\\(HAVE_IB_EVENT_GID_CHANGE\\)",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 6 * 256 + 9)", stmnt)

  gsub("defined\\(HAVE_IB_QUERY_DEVICE\\)",
       "(LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) \\&\\& " \
       "RHEL_RELEASE_CODE -0 < 7 * 256 + 5)", stmnt)

  gsub("defined\\(MAD_HANDLER_TAKES_SEND_BUF\\)",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 7 * 256 + 5)", stmnt)

  gsub("defined\\(HAVE_IB_SET_CPI_RESP_TIME\\)",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 7 * 256 + 5)", stmnt)

  gsub("defined\\(IB_PD_HAS_LOCAL_DMA_LKEY\\)",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 7 * 256 + 5)", stmnt)

  gsub("defined\\(HAVE_IB_DMA_MAP_OPS\\)",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(0, 0, 0))", stmnt)

  gsub("HAVE_IB_SG_DMA_LEN",
       "(LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0) \\&\\& " \
       "RHEL_RELEASE_CODE -0 < 8 * 256 + 2)", stmnt)

  gsub("RDMA_REJECT_HAS_FOUR_ARGS",
       "(LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || " \
       "RHEL_RELEASE_CODE -0 >= 8 * 256 + 4)", stmnt)

  gsub("defined(ENABLE_NPIV)", 0, stmnt)

  gsub("defined(FC_VPORT_CREATE_DEFINED)", 0, stmnt)

  gsub("LINUX_VERSION_CODE", LINUX_VERSION_CODE, stmnt)

  pattern="KERNEL_VERSION\\([[:blank:]]*([0-9]+)[[:blank:]]*,[[:blank:]]*([0-9]+)[[:blank:]]*,[[:blank:]]*([0-9]+)[[:blank:]]*\\)"
  while (match(stmnt, pattern, op) != 0)
  {
    sub(pattern, version_number(op[1], op[2], op[3]), 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)

  gsub("defined\\(CONFIG_SCST_STRICT_SERIALIZING\\)", "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)
  }

  gsub("defined\\(RHEL_RELEASE_VERSION\\)", "1", stmnt)
  if (RHEL_MAJOR == "" || RHEL_MINOR == "")
  {
    gsub("defined\\(RHEL_RELEASE_CODE\\)", "0", stmnt)
    gsub("RHEL_RELEASE_CODE", "", stmnt)
    gsub("RHEL_RELEASE_VERSION\\([^,)]*,[ 	]*[^,)]*\\)", "0", stmnt)
  }
  else
  {
    gsub("defined\\(RHEL_RELEASE_CODE\\)", "1", stmnt)
    gsub("RHEL_RELEASE_CODE", RHEL_MAJOR * 256 + RHEL_MINOR, stmnt)
    stmnt = gensub("RHEL_RELEASE_VERSION\\(([^,)]*),[ 	]*([^,)]*)\\)",
                   "(\\1) * 256 + (\\2)", "g", stmnt)
  }

  if (MOFED_MAJOR == "") {
    gsub("defined\\(MOFED_MAJOR\\)", "0", stmnt)
    gsub("MOFED_MAJOR", "", stmnt)
  } else {
    gsub("defined\\(MOFED_MAJOR\\)", "1", stmnt)
    gsub("MOFED_MAJOR", MOFED_MAJOR, stmnt)
  }

  if (MOFED_MINOR == "") {
    gsub("defined\\(MOFED_MINOR\\)", "0", stmnt)
    gsub("MOFED_MINOR", "", stmnt)
  } else {
    gsub("defined\\(MOFED_MINOR\\)", "1", stmnt)
    gsub("MOFED_MINOR", MOFED_MINOR, stmnt)
  }

  gsub("defined\\(_COMPAT_LINUX_MM_H\\)", "0", stmnt)
  gsub("defined\\(UEK_KABI_RENAME\\)", UEK_KABI_RENAME ? "1" : "0", stmnt)
  gsub("defined\\(UEK_RELEASE\\)", UEK_RELEASE ? "1" : "0", stmnt)
  gsub("UEK_RELEASE", UEK_RELEASE ? UEK_RELEASE : "0", stmnt)

  if (SCST_IO_CONTEXT != "")
  {
    gsub("defined[[:blank:]]+SCST_IO_CONTEXT", SCST_IO_CONTEXT, stmnt)
    gsub("defined[[:blank:]]*\\([[:blank:]]*SCST_IO_CONTEXT[[:blank:]]*\\)", SCST_IO_CONTEXT, stmnt)
  }

  if (generating_upstream_patch_defined)
  {
    gsub("defined[[:blank:]]+GENERATING_UPSTREAM_PATCH", 1, stmnt)
    gsub("defined[[:blank:]]*\\([[:blank:]]*GENERATING_UPSTREAM_PATCH[[:blank:]]*\\)", 1, stmnt)
  }

  if (config_tcp_zero_copy_transfer_completion_notification_undefined)
  {
    gsub("defined[[:blank:]]+CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION", 0, stmnt)
    gsub("defined[[:blank:]]*\\([[:blank:]]*CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION[[:blank:]]*\\)", 0, stmnt)
  }

  gsub("defined[[:blank:]]+CONFIG_SCST_PROC", !config_scst_proc_undefined, stmnt)
  gsub("defined[[:blank:]]*\\([[:blank:]]*CONFIG_SCST_PROC[[:blank:]]*\\)", !config_scst_proc_undefined,
       stmnt)

  if (debug)
    printf "/* debug specialize-patch: (b) %s */\n", stmnt

  do
  {
    last_stmnt = stmnt

    pattern = "![[:blank:]]*(-*[0-9]+)"
    while (match(stmnt, pattern, op) != 0)
    {
      sub(pattern, op[1] == 0, stmnt)
    }

    pattern = "![[:blank:]]*\\([[:blank:]]*(-*[0-9]+)[[:blank:]]*\\)"
    while (match(stmnt, pattern, op) != 0)
    {
      sub(pattern, op[1] == 0, stmnt)
    }

    pattern = "\\([[:blank:]]*(-*[0-9]+)[[:blank:]]*\\)"
    while (match(stmnt, pattern, op) != 0)
    {
      sub(pattern, op[1], stmnt)
    }

    pattern="(-*[0-9]+)[[:blank:]]*(\\*|/)[[:blank:]]*(-*[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]+)[[:blank:]]*(\\+|-)[[:blank:]]*(-*[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]+)[[:blank:]]*(<<|>>)[[:blank:]]*(-*[0-9]+)"
    while (match(stmnt, pattern, op) != 0)
    {
      result="error"
      if      (op[2] == "<<") result = int(op[1] * (2**op[3]))
      else if (op[2] == ">>") result = int(op[1] / (2**op[3]))
      sub(pattern, result, stmnt)
    }

    pattern="(-*[0-9]+)[[:blank:]]*(<|<=|>|>=|==|!=)[[:blank:]]*(-*[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]+)[[:blank:]]*&&[[:blank:]]*\\([[:blank:]]*(-*[0-9]+)[[:blank:]]*\\)"
    while (match(stmnt, pattern, op) != 0)
    {
      sub(pattern, (op[1] != 0) && (op[2] != 0), stmnt)
    }

    pattern="(-*[0-9]+)[[:blank:]]*&&[[:blank:]]*(-*[0-9]+)"
    while (match(stmnt, pattern, op) != 0)
    {
      sub(pattern, (op[1] != 0) && (op[2] != 0), stmnt)
    }

    pattern="0[[:blank:]]*&&[[:blank:]]*defined\\([A-Za-z0-9_]*\\)"
    while (match(stmnt, pattern, op) != 0)
    {
      sub(pattern, 0, stmnt)
    }

    pattern="^+#(if|elif)[[:blank:]]*([01])[[:blank:]]*&&[[:blank:]]*(!*[[:blank:]]*defined[[:blank:]]*\\([[:blank:]]*[A-Za-z0-9_]*[[:blank:]]*\\))$"
    while (match(stmnt, pattern, op) != 0)
    {
      stmnt = "+#" op[1] " " (op[2] != 0 ? op[3] : op[2])
    }

    pattern="^+#(if|elif)[[:blank:]]*(!*[[:blank:]]*defined[[:blank:]]*\\([[:blank:]]*[A-Za-z0-9_]*[[:blank:]]*\\))&&[[:blank:]]*([01])[[:blank:]]*$"
    while (match(stmnt, pattern, op) != 0)
    {
      stmnt = "+#" op[1] " " (op[3] != 0 ? op[2] : op[3])
    }

    pattern="^+#(if|elif)[[:blank:]]*(!*[[:blank:]]*[A-Za-z0-9_]*)[[:blank:]]*&&[[:blank:]]*([01])[[:blank:]]*$"
    while (match(stmnt, pattern, op) != 0)
    {
      stmnt = "+#" op[1] " " (op[3] != 0 ? op[2] : op[3])
    }

    pattern="(-*[0-9]+)[[:blank:]]*\\|\\|[[:blank:]]*(-*[0-9]+)"
    while (match(stmnt, pattern, op) != 0)
    {
      sub(pattern, (op[1] != 0) || (op[2] != 0), stmnt)
    }

    pattern="^+#(if|elif)[[:blank:]]*([01])[[:blank:]]*\\|\\|[[:blank:]]*(!*[[:blank:]]*defined[[:blank:]]*\\([[:blank:]]*[A-Za-z0-9_]*[[:blank:]]*\\))$"
    while (match(stmnt, pattern, op) != 0)
    {
      stmnt = "+#" op[1] " " (op[2] == 0 ? op[3] : op[2])
    }

    pattern="\\(([01])[[:blank:]]*\\|\\|[[:blank:]]*(!*[[:blank:]]*defined[[:blank:]]*\\([[:blank:]]*[A-Za-z0-9_]*[[:blank:]]*\\))\\)"
    while (match(stmnt, pattern, op) != 0)
    {
      sub(pattern, op[1] == 0 ? op[2] : op[1], stmnt)
    }

    pattern="\\([[:blank:]]*(-*[0-9]+)[[:blank:]]*\\)"
    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) {
  return evaluate(gensub("^+#if (.*)$", "+#if !(\\1)", "g", stmnt))
}


# Handle #if or #elif

function handle_if(evaluated)
{
  # 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 ~ "CONFIG_SCST_PROC"					\
      || $0 ~ "CONFIG_SCST_STRICT_SERIALIZING"				\
      || $0 ~ "CREATE_SEND_MAD_HAS_AH_ARG"				\
      || $0 ~ "CREATE_SEND_MAD_HAS_BASE_ARG"				\
      || $0 ~ "ENABLE_NPIV"						\
      || $0 ~ "FC_VPORT_CREATE_DEFINED"					\
      || $0 ~ "HAVE_DEV_ATTR_MAX_RECV_SGE"				\
      || $0 ~ "HAVE_DEV_ATTR_MAX_SEND_SGE"				\
      || $0 ~ "HAVE_IB_DMA_MAP_OPS"					\
      || $0 ~ "HAVE_IB_QUERY_DEVICE"					\
      || $0 ~ "HAVE_IB_SET_CPI_RESP_TIME"				\
      || $0 ~ "HAVE_IB_SG_DMA_LEN"					\
      || $0 ~ "HAVE_RDMA_DESTROY_AH"					\
      || $0 ~ "HAVE_RDMA_DESTROY_AH_WITH_FLAGS"				\
      || $0 ~ "HAVE_RDMA_QUERY_GID"					\
      || $0 ~ "HAVE_SRP_DATA_DESC_IMM"					\
      || $0 ~ "HAVE_STRUCT_SRP_LOGIN_REQ_RDMA"				\
      || $0 ~ "IB_CLIENT_ADD_ONE_RETURNS_INT"                           \
      || $0 ~ "IB_CLIENT_REMOVE_TAKES_TWO_ARGS"				\
      || $0 ~ "IB_CM_LISTEN_TAKES_FOURTH_ARG"				\
      || $0 ~ "IB_CM_LISTEN_TAKES_THIRD_ARG"				\
      || $0 ~ "IB_CREATE_CQ_HAS_INIT_ATTR"				\
      || $0 ~ "IB_PD_HAS_LOCAL_DMA_LKEY"				\
      || $0 ~ "IB_QUERY_GID_HAS_ATTR_ARG"				\
      || $0 ~ "INSIDE_KERNEL_TREE"					\
      || $0 ~ "MAD_HANDLER_TAKES_SEND_BUF"				\
      || $0 ~ "MOFED_MAJOR"						\
      || $0 ~ "MOFED_MINOR"						\
      || $0 ~ "RDMA_CREATE_ID_TAKES_NET_ARG"				\
      || $0 ~ "RDMA_REJECT_HAS_FOUR_ARGS"                               \
      || $0 ~ "REGISTER_MAD_AGENT_HAS_FLAGS_ARG"			\
      || $0 ~ "RHEL_MAJOR"						\
      || $0 ~ "RHEL_MINOR"						\
      || $0 ~ "RHEL_RELEASE_CODE"					\
      || $0 ~ "SOCK_RECVMSG_HAS_FOUR_ARGS"				\
      || $0 ~ "UEK_KABI_RENAME"						\
      || $0 ~ "UEK_RELEASE"						\
      || $0 ~ "USE_PRE_440_WR_STRUCTURE"				\
      || $0 ~ "_COMPAT_LINUX_MM_H"					\
      || $0 ~ "bio_multiple_segments"                                   \
      || generating_upstream_patch_defined				\
         && $0 ~ "GENERATING_UPSTREAM_PATCH"				\
      || $0 ~ "CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION"	\
      || ($0 ~ "CONFIG_SCST_PROC" && config_scst_proc_undefined)	\
      || ($0 ~ "SCST_IO_CONTEXT" && SCST_IO_CONTEXT != ""))
  {
  }
  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
    decision[if_nesting_level] = evaluated
    inv_decision[if_nesting_level] = evaluate(sprintf("+#if !%s", substr(evaluated, 6)))
    if (debug)
      printf "/* debug specialize-patch: (f) %s / %s */\n", \
        decision[if_nesting_level], inv_decision[if_nesting_level]
  }
  else
  {
    sub("^+#elif ",
        sprintf("+#if %s \\&\\& ", substr(inv_decision[if_nesting_level], 6)),
        evaluated)
    if (debug)
      printf "/* debug specialize-patch: (e) %s */\n", evaluated
    evaluated = evaluate(evaluated)
    sub("^+#if", "+#elif", evaluated);
    decision[if_nesting_level] = evaluated
    inv_decision[if_nesting_level] \
      = evaluate(sprintf("+#if %s && !%s", \
                         substr(inv_decision[if_nesting_level], 6), \
                         substr(evaluated, 8)))
    if (debug)
      printf "/* debug specialize-patch: (f) %s / %s */\n", \
        decision[if_nesting_level], inv_decision[if_nesting_level]
  }
  return evaluated
}


# Decide whether or not to print the preprocessor statement $0.

function process_preprocessor_statement(evaluated, condition) {
  evaluated = evaluate($0)
  condition = 1
  ei = evaluated_if_stmnt[if_nesting_level];
  if (evaluated ~ "^+#if")
  {
    if_nesting_level++
    evaluated_if_stmnt[if_nesting_level] = evaluated
    evaluated = handle_if(evaluated)
  }
  else if (evaluated ~ "^+#elif")
  {
    if (if_discarded[if_nesting_level]) {
      sub("^+#elif", "+#if", input_line[0])
      if (debug)
        printf "/* debug specialize-patch: (g0) -> %s */\n", evaluated
    }
    evaluated = handle_if(evaluated)
  }
  else if (evaluated ~ "^+#else")
  {
    decision[if_nesting_level] = inv_decision[if_nesting_level]
  }
  else if (evaluated ~ "^+#endif")
  {
    if_nesting_level--
  }
  else
  {
    condition = 0
  }
  if (condition)
  {
    output = 1
    if (debug)
      printf "/* debug specialize-patch: (g1a) if_nesting_level = %d */\n", \
          if_nesting_level
    for (i = if_nesting_level; i >= 0; i--) {
      if (debug)
        printf "/* debug specialize-patch: (g1b) %s: decision[%d] = %s */\n", \
          evaluated, i, decision[i]
      output = output && decision[i] != "+#if 0" && decision[i] != "+#elif 0"
    }
    if (output)
      any_section_output[if_nesting_level] = 1
    if (debug)
      printf "/* debug specialize-patch: (g2) %s: output = %d */\n", \
        evaluated, output
  }
  if (output && (!condition || \
                 decision[if_nesting_level + (evaluated ~ "^+#endif$")] \
                 !~ "^+#(el|)if [01]"))
  {
    if (evaluated == "+#if undecided"                           \
        || (evaluated !~ "^+#if"                                \
            && evaluated !~ "^+#elif"                           \
            && evaluated !~ "^+#endif"))
    {
      for (i = 0; i < input_line_count; i++)
        line[lines++] = input_line[i]
    } else {
      # If the statement being processed is an #else or #endif that is not a
      # header file include guard and if that statement has a trailing comment,
      # replace that comment by the partially evaluated matching #if expression.
      if (evaluated ~ "^+#else" || evaluated ~ "^+#endif") {
        if ($0 ~ "\\*/") {
          if ($0 ~ "_H_* \\*/$") {
            evaluated = $0
          } else {
            if (ei ~ "^+#if ")
              ei = substr(ei, 6)
            else if (ei ~ "^+#ifdef")
              ei = substr(ei, 9)
            match($0, "([^/ ]*)[[:blank:]]*/\\*[^*]*\\*/[[:blank:]]*", arg)
            evaluated = sprintf("%s /* %s */", arg[1], ei);
            if (match(evaluated, "([^/ ]*) */\\* defined\\(([^()]*)\\) \\*/",
                      arg))
            {
              evaluated = sprintf("%s /* %s */", arg[1], arg[2])
            }
          }
        } else {
          evaluated = $0
        }
        if (debug)
          printf "/* debug specialize-patch: (h) %s */\n", evaluated
      }
      line[lines++] = evaluated
      lines_less_added += input_line_count - 1
    }
  }
  else
  {
    discard = 1
    if (lines >= 1 && line[lines - 1] == "+")
      delete_next_blank_line = 1
  }

  if (evaluated ~ "^+#if")
    if_discarded[if_nesting_level] = discard
}

function reset_hunk_state_variables() {
  lines                  =  0
  lines_less_added       =  0
  lines_less_deleted     =  0
  output                 =  1
  if_nesting_level       = -1
  delete_next_blank_line =  0
  h[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]-lines_less_deleted,h[3],h[4]-lines_less_added,h[5]
    for (i = 0; i < lines; i++) {
      modifier = (LINUX_VERSION_CODE < version_code("4.19.0") &&
                  (RHEL_MAJOR == "" ||
                   RHEL_MAJOR * 256 + RHEL_MINOR < 7 * 256 + 7 ||
                   RHEL_MAJOR * 256 + RHEL_MINOR == 8 * 256 + 0) ?
                  "" : "const");
      gsub("BAD_WR_MODIFIER", modifier, line[i])
      gsub("CM_HANDLER_EVENT_MODIFIER", modifier, line[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)
  {
    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.
  process_file = 0
  reset_hunk_state_variables()
}


{
  if (match($0, "^diff[ \t]+[^ \t]+[ \t]+[^ \t]+[ \t]+([^ \t]+)$", filename) \
      || match($0, "^\\+\\+\\+[ \t]+([^ \t]+)[ \t]+", filename))
  {
    # Start of new file.
    dump_lines()
    reset_hunk_state_variables()
    process_file = match(filename[1], "\\.[ch]$") != 0 &&
      match(filename[1], "drivers/scsi/qla2xxx/") == 0
  }

  if (!process_file)
  {
    sub("^+#ifdef INSIDE_KERNEL_TREE$", "+#if 1")
    print
    next
  }

  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("[ \t]*\\\\$", "", previous_line)
    getline
    input_line[input_line_count++] = $0
    sub("^+[ \t]*", "", $0)
    $0 = previous_line " " $0
  }

  discard = 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 && $0 == "+")
  {
    discard = 1
    delete_next_blank_line = 0
  }
  else if (lines >= 2 && !match(line[lines-2], ":$") &&
           line[lines-1] == "+\x9return;" && $0 == "+}")
  {
    # Delete "return;" statements at the end of a function that are not
    # preceded by a label.
    line[lines-1] = $0
    lines_less_added++
  }
  else
  {
    delete_next_blank_line = 0
    if (match($0, "^+ *#"))
    {
      if (debug)
        printf "/* debug specialize-patch: line %d: %s*/\n", NR, $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 = 1
      if (lines >= 1 && line[lines-1] == "+")
        delete_next_blank_line = 1
    }
  }
  if (discard)
  {
    for (i = 0; i < input_line_count; i++)
    {
      if (blank_deleted_code)
      {
        if (input_line[i] ~ "^+")
          line[lines++] = "+"
        else
          line[lines++] = input_line[i]
      }
      else
      {
        if (input_line[i] ~ "^+")
          lines_less_added++
        else if (input_line[i] ~ "^-")
          lines_less_deleted++
      }
    }
  }
}

END {
  # Dump processed contents of the last read hunk.
  dump_lines()
}

# Local variables:
# indent-tabs-mode: nil
# c-basic-offset: 2
# End:
