# -*- mode: shell-script -*-
# Shell functions for parsing the Linux kernel version and for downloading
# from kernel.org.

kernel_mirror="ftp://ftp.kernel.org/pub/linux/kernel"
kernel_longterm="http://www.kernel.org/pub/linux/kernel"
kernel_sources="$HOME/software/downloads"

# Kernel version number.
function kernel_version {
  if [ "${1#2.}" != "$1" ]; then
    echo "$1" | sed -n 's/^\([0-9]*\.[0-9]*\.[0-9]*\).*$/\1/p'
  else
    echo "$1" | sed -n 's/^\([0-9]*\.[0-9]*\).*$/\1/p'
  fi
}

# Last component of the kernel version, or the empty string if $1 does
# not contain a patchlevel.
function patchlevel {
  if [ "${1#2.}" != "$1" ]; then
    echo "$1" | sed -n 's/^\([0-9]*\.[0-9]*\.[0-9]*\)[.-]\(.*\)$/\2/p'
  else
    echo "$1" | sed -n 's/^\([0-9]*\.[0-9]*\)[.-]\(.*\)$/\2/p'
  fi
}

# Download the file from URL $1 and save it in the current directory.
function download_file {
  if [ ! -e "$(basename "$1")" ]; then
    if [ "${quiet_download}" = "false" ]; then
      { wget -q -nc -O- "$1" 2>/dev/null | grep -q .; } \
        && echo "Downloading $1 ..."
    fi
    wget -q -nc "$1"
  fi
  [ -e "$(basename "$1")" ]
}

# Make sure the kernel tarball and patch file are present in directory
# ${kernel_sources}. Download any missing files from ${kernel_mirror}.
function download_kernel {
  local kver="$(kernel_version $1)"
  local plevel="$(patchlevel $1)"
  local series="$1"

  if [ "${series:0:2}" = "3." ]; then
    series="3.x"
  else
    series="${series:0:3}"
  fi
  mkdir -p "${kernel_sources}" || return $?
  test -w "${kernel_sources}" || return $?
  (
    cd "${kernel_sources}" || return $?
    if [ "$plevel" = "" ] \
       || download_file "${kernel_mirror}/v$series/patch-$1.xz" \
       || download_file "${kernel_mirror}/v$series/longterm/v${kver}/patch-$1.xz"
    then
      download_file "${kernel_mirror}/v$series/linux-${kver}.tar.xz" \
        || download_file "${kernel_mirror}/v$series/longterm/v${kver}/linux-${kver}.tar.xz" \
        || return $?
    else
      download_file "${kernel_mirror}/v$series/linux-$1.tar.xz" \
        || download_file "${kernel_mirror}/v$series/longterm/v${kver}/linux-$1.tar.xz" \
        || return $?
    fi
  )
}

function extract_kernel_archive {
  if [ -e "${kernel_sources}/linux-$1.tar.xz" ]; then
    xz -cd "${kernel_sources}/linux-$1.tar.xz" | tar xf -
  else
    tar xjf "${kernel_sources}/linux-$1.tar.bz2"
  fi
}

# Create a linux-$1 tree in the current directory, where $1 is a kernel
# version number with either three or four components.
function extract_kernel_tree {
  local kver="$(kernel_version $1)"
  local plevel="$(patchlevel $1)"
  local tmpdir=kernel-tree-tmp-$$

  rm -rf "linux-$1" "${tmpdir}"
  mkdir "${tmpdir}" || return $?
  (
    cd "${tmpdir}" || return $?
    if [ "$plevel" != "" -a -e "${kernel_sources}/patch-$1.xz" ]; then
      extract_kernel_archive $kver || return $?
      mv linux-$kver linux-$1
      ( cd linux-$1 && xz -cd "${kernel_sources}/patch-$1.xz" \
        | patch -p1 -f -s; ) \
        || return $?
    else
      extract_kernel_archive $1 || return $?
    fi
    mv "linux-$1" ".." || return $?
    cd "../linux-$1" || return $?
    if [ "$1" = "2.6.29" -o "$1" = "2.6.29.1" -o "$1" = "2.6.29.2" -o "$1" = "2.6.29.3" ]
    then
      patch -f -s -p1 <<'EOF'
Make sure that branch profiling does not trigger sparse warnings.
See also http://bugzilla.kernel.org/show_bug.cgi?id=12925
---
See also http://lkml.org/lkml/2009/4/5/120
--- orig/linux-2.6.29/include/linux/compiler.h	2009-03-23 19:12:14.000000000 -0400
+++ linux-2.6.29/include/linux/compiler.h	2009-03-24 08:46:46.000000000 -0400
@@ -75,7 +75,8 @@ struct ftrace_branch_data {
  * Note: DISABLE_BRANCH_PROFILING can be used by special lowlevel code
  * to disable branch tracing on a per file basis.
  */
-#if defined(CONFIG_TRACE_BRANCH_PROFILING) && !defined(DISABLE_BRANCH_PROFILING)
+#if defined(CONFIG_TRACE_BRANCH_PROFILING) \
+    && !defined(DISABLE_BRANCH_PROFILING) && !defined(__CHECKER__)
 void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
 
 #define likely_notrace(x)	__builtin_expect(!!(x), 1)
EOF
    fi
    if [ "${1#2.6.31}" != "$1" ]
    then
      patch -f -s -p1 <<'EOF'
Checking a 2.6.31.1 kernel configured with allyesconfig/allmodconfig
with sparse (make C=2) triggers a sparse warning on code that uses the
kmemcheck_annotate_bitfield() macro. An example of such a warning:

include/net/inet_sock.h:208:17: warning: do-while statement is not a compound statement

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Cc: Vegard Nossum <vegardno@ifi.uio.no>
Cc: Andrew Morton <akpm@linux-foundation.org>

---
See also http://lkml.org/lkml/2009/9/26/51

--- linux-2.6.31.1/include/linux/kmemcheck-orig.h	2009-09-26 13:53:44.000000000 +0200
+++ linux-2.6.31.1/include/linux/kmemcheck.h	2009-09-26 13:53:56.000000000 +0200
@@ -137,13 +137,13 @@ static inline void kmemcheck_mark_initia
 	int name##_end[0];
 
 #define kmemcheck_annotate_bitfield(ptr, name)				\
-	do if (ptr) {							\
+	do { if (ptr) {							\
 		int _n = (long) &((ptr)->name##_end)			\
 			- (long) &((ptr)->name##_begin);		\
 		BUILD_BUG_ON(_n < 0);					\
 									\
 		kmemcheck_mark_initialized(&((ptr)->name##_begin), _n);	\
-	} while (0)
+	} } while (0)
 
 #define kmemcheck_annotate_variable(var)				\
 	do {								\
EOF
    fi
    if [ "${1#2.6.32}" != "$1" -o "${1#2.6.33}" != "$1" ]
    then
      patch -f -s -p1 <<'EOF'
Get rid of sparse errors on sk_buff.protocol.

--- linux/include/linux/skbuff-orig.h	2010-12-07 13:40:51.000000000 -0500
+++ linux/include/linux/skbuff.h	2010-12-07 13:41:05.000000000 -0500
@@ -349,8 +349,8 @@ struct sk_buff {
 				ipvs_property:1,
 				peeked:1,
 				nf_trace:1;
-	__be16			protocol:16;
 	kmemcheck_bitfield_end(flags1);
+	__be16			protocol;
 
 	void			(*destructor)(struct sk_buff *skb);
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
EOF
    fi
  )
  rmdir "${tmpdir}"
}

