#!/usr/bin/env perl

# convert linux-kernel __section uses from unquoted string to quoted string
# convert __attribute__((section("foo"))) to __section("foo")
# convert __attribute__((foo, section=("bar"), baz))
#      to __section("bar") attribute((foo, baz))
# convert __attribute__

use strict;

# patch compiler_attributes.h to remove quoting of section name

my $result = `patch -p1 <<"EOF"
 include/linux/compiler_attributes.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/compiler_attributes.h b/include/linux/compiler_attributes.h
index ea7b756b1c8f..b2a3f4f641a7 100644
--- a/include/linux/compiler_attributes.h
+++ b/include/linux/compiler_attributes.h
@@ -254,7 +254,7 @@
  *   gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-section-variable-attribute
  * clang: https://clang.llvm.org/docs/AttributeReference.html#section-declspec-allocate
  */
-#define __section(S)                    __attribute__((__section__(#S)))
+#define __section(section)              __attribute__((__section__(section)))
 
 /*
  *   gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-unused-function-attribute
EOF`;

# patch scripts/mod/modpost.c to add quoting of section name

my $result = `patch -p1 <<"EOF"
 scripts/mod/modpost.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 69341b36f271..f882ce0d9327 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -2254,7 +2254,7 @@ static void add_header(struct buffer *b, struct module *mod)
 	buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\\n");
 	buf_printf(b, "\\n");
 	buf_printf(b, "__visible struct module __this_module\\n");
-	buf_printf(b, "__section(.gnu.linkonce.this_module) = {\\n");
+	buf_printf(b, "__section(\\".gnu.linkonce.this_module\\") = {\\n");
 	buf_printf(b, "\\t.name = KBUILD_MODNAME,\\n");
 	if (mod->has_init)
 		buf_printf(b, "\\t.init = init_module,\\n");
@@ -2308,7 +2308,7 @@ static int add_versions(struct buffer *b, struct module *mod)
 
 	buf_printf(b, "\\n");
 	buf_printf(b, "static const struct modversion_info ____versions[]\\n");
-	buf_printf(b, "__used __section(__versions) = {\\n");
+	buf_printf(b, "__used __section(\\"__versions\\") = {\\n");
 
 	for (s = mod->unres; s; s = s->next) {
 		if (!s->module)
EOF`;

# Get the list of files to modify (contains __section or __attribute__.*section
# (ignore scripts, tools, uapi, arch/powerpc/boot and compiler_attributes.h)

my $output = `git grep --name-only -P "(?:\\b__section\\b\|\\b__attribute__\\b.*section)" | grep -vP '^(?:include/linux/compiler_attributes\\.h|scripts/|tools/|/uapi/|arch/powerpc/boot/)'`;
my @files = split("\n", $output);
# Add the only file in tools that needs conversion
push (@files, "tools/include/linux/objtool.h");

# Modify each possible file
foreach (@files) {
    chomp;
    my $file = $_;

    # read the original file
    open(FH, '<', $file) or die $!;
    my @lines = <FH>;
    close FH;

    # write the modified file line by line
    open (FH, '>', $file) or die $!;
    foreach my $line (@lines) {
	chomp $line;
	my $newline = $line;

	# Convert __section(foo) to __section("foo")
	# if "foo" uses multiple token pasting,
	#   pre and post tokens removed and a single # is used then " " added
	#   e.g.:  foo ## bar ## baz becomes "foo" #bar "baz"
	if ($line =~ m/\b__section\s*\(\s*(?!")([^\)]+)\)/) {
	    my $oldsection = $1;
	    my $newsection = $1;
	    if ($oldsection =~ /(.*)##(.*)##(.*)/) {
		$newsection = '"' . trim($1) . '" #' . trim($2) . ' "' . trim($3) . '"';
	    } else {
		$newsection = '"' . trim($oldsection) . '"';
	    }
	    $newline =~ s/__section\s*\(\s*\Q$oldsection\E\s*\)/__section($newsection)/;
	}

	# convert __attribute__((section("foo"))) to __section("foo")
	$newline =~ s/\b__attribute__\s*\(\s*\(\s*_*section_*\s*\(\s*("[^"]+")\s*\)\s*\)\s*\)/__section($1)/;

	# convert __attribute__((foo, section=("bar"), baz))
	# to __section("bar") attribute((foo, baz))
	if ($newline =~ /(\b__attribute__\s*\(\s*\(([^,_]+)?(\s*,?\s*_*section_*\s*\(\s*("[^"]+")\s*\)\s*,?\s*)(.*)\s*\)\s*\))/) {
	    my $section = $3;
	    my $comma = "";
	    $comma = ", " if ($section =~ /^\s*,/ && $section =~ /,\s*$/);
	    $newline =~ s/\Q$section\E/$comma/;
	    $section =~ s/^[^"]*//;
	    $section =~ s/^("[^"]*").*/$1/;
	    $newline =~ s/\b__attribute__/__section($section) __attribute__/;
	}

	# if the line ended with a line continuation \, try to move the
	# continuation to the same location by removing or adding tabs
	if ($line =~ /\\$/) {
	    my $olen = length(expand_tabs($line));
	    my $nlen = length(expand_tabs($newline));
	    if ($newline =~ /\t\\$/) {
		if ($nlen > $olen) {
		    $newline =~ s/\t\\$/\\/;
		} else {
		    while ($nlen < $olen) {
			$newline =~ s/\\$/\t\\/;
			$nlen = length(expand_tabs($newline));
		    }
		}
	    }
	}
	print FH "$newline\n";
    }
    close FH;
}

# And git commit the changes
$result = qx{git commit -a --author='Joe Perches <joe\@perches.com>' -F- <<"EOF"
treewide: Convert macro and uses of __section(foo) to __section("foo")

Use a more generic form for __section that requires quotes to avoid
complications with clang and gcc differences.

Remove the quote operator # from compiler_attributes.h __section macro.

Convert all unquoted __section(foo) uses to quoted __section("foo").
Also convert __attribute__((section("foo"))) uses to __section("foo")
even if the __attribute__ has multiple list entry forms.

Signed-off-by: Joe Perches <joe\@perches.com>
EOF
};

# utility subroutines
sub trim {
    my ($string) = @_;
    $string =~ s/^\s+|\s+$//g;
    return $string;
}

sub expand_tabs {
    my ($str) = @_;

    my $res = '';
    my $n = 0;
    for my $c (split(//, $str)) {
	if ($c eq "\t") {
	    $res .= ' ';
	    $n++;
	    for (; ($n % 8) != 0; $n++) {
		$res .= ' ';
	    }
	    next;
	}
	$res .= $c;
	$n++;
    }

    return $res;
}
