Add a script that simplifies the process of altering system call tables in
the kernel sources.  It has five functions available:

 (1) Add a system call or compat system call:

        ./scripts/syscall-manage.pl --add <name> [--compat]

 (2) Remove a system call:

        ./scripts/syscall-manage.pl --rm <name>

 (3) Rename a system call:

        ./scripts/syscall-manage.pl --rename <name> <new_name>

 (4) Renumber the system calls from 424 to __NR_syscalls to erase gaps:

        ./scripts/syscall-manage.pl --renumber

 (5) Resolve simple git conflicts between two branches that both add system
     calls and renumber the conflicting calls.

        ./scripts/syscall-manage.pl --resolve

The script alters the master uapi unistd.h, sys_ni.c and the system call
.tbl files.

Signed-off-by: David Howells <dhowe...@redhat.com>
---

 Documentation/process/adding-syscalls.rst |   53 ++
 scripts/syscall-manage.pl                 |  929 +++++++++++++++++++++++++++++
 2 files changed, 982 insertions(+)
 create mode 100755 scripts/syscall-manage.pl

diff --git a/Documentation/process/adding-syscalls.rst 
b/Documentation/process/adding-syscalls.rst
index 1c3a840d06b9..56515cc6d55f 100644
--- a/Documentation/process/adding-syscalls.rst
+++ b/Documentation/process/adding-syscalls.rst
@@ -8,6 +8,14 @@ This document describes what's involved in adding a new system 
call to the
 Linux kernel, over and above the normal submission advice in
 :ref:`Documentation/process/submitting-patches.rst <submittingpatches>`.
 
+A script is available to alter all the system call tables in one go::
+
+       ./scripts/syscall-manage.pl --add <name> [--compat]
+       ./scripts/syscall-manage.pl --rm <name>
+       ./scripts/syscall-manage.pl --rename <name> <new_name>
+       ./scripts/syscall-manage.pl --renumber
+       ./scripts/syscall-manage.pl --resolve
+
 
 System Call Alternatives
 ------------------------
@@ -247,6 +255,10 @@ To summarize, you need a commit that includes:
  - generic table entry in ``include/uapi/asm-generic/unistd.h``
  - fallback stub in ``kernel/sys_ni.c``
 
+A system call named xyzzy can be added by::
+
+    ./scripts/syscall-manage.pl --add xyzzy
+
 
 x86 System Call Implementation
 ------------------------------
@@ -352,6 +364,10 @@ To summarize, you need:
  - instance of ``__SC_COMP`` not ``__SYSCALL`` in
    ``include/uapi/asm-generic/unistd.h``
 
+A compatibility system call named xyzzy can be added by::
+
+    ./scripts/syscall-manage.pl --add xyzzy --compat
+
 
 Compatibility System Calls (x86)
 --------------------------------
@@ -491,6 +507,43 @@ The man page should be cc'ed to linux-...@vger.kernel.org
 For more details, see https://www.kernel.org/doc/man-pages/patches.html
 
 
+System Call Management Script
+-----------------------------
+
+A script is available to alter all the system call tables in one go.  It has
+five functions available:
+
+ * Add a new system call, giving it the specified name and allocating it the
+   next number and bumping __NR_syscalls.  If --compat is specified, then
+   __SC_COMP() will be used in lieu of __SYSCALL() and the script will attempt
+   to emit appropriate compatibility lines into the tables.  The command is::
+
+     ./scripts/syscall-manage.pl --add <name> [--compat]
+
+ * Remove the system call with the specified name, decrementing __NR_syscalls
+   if it was the final one.  The command is::
+
+     ./scripts/syscall-manage.pl --rm <name>
+
+ * Rename the system call with the specified name to the new name.  The command
+   is::
+
+     ./scripts/syscall-manage.pl --rename <name> <new_name>
+
+ * Renumber the system calls between 424 and __NR_syscalls to remove any
+   holes and update __NR_syscalls.  The command is::
+
+     ./scripts/syscall-manage.pl --renumber
+
+ * Resolve simple git conflicts across all system call table files resulting
+   from one branch being merged into another where both branches add system
+   calls with conflicting numbers.  The new syscalls are renumbered,
+   __NR_syscalls is updated and the conflict markers and any extra definition
+   of __NR_syscalls are removed.  The command is::
+
+     ./scripts/syscall-manage.pl --resolve
+
+
 Do not call System Calls in the Kernel
 --------------------------------------
 
diff --git a/scripts/syscall-manage.pl b/scripts/syscall-manage.pl
new file mode 100755
index 000000000000..3a0cf76d5c15
--- /dev/null
+++ b/scripts/syscall-manage.pl
@@ -0,0 +1,929 @@
+#!/usr/bin/perl -w
+#
+# This script can be used to add, remove, rename and renumber system calls in
+# the kernel sources and resolve simple git conflicts when a branch carrying
+# new system calls is merged into another that also has new system calls with
+# conflicting numbers.
+#
+# Usage:
+#
+#  ./scripts/syscall-manage.pl --add <name> [--compat]
+#
+#      Add a new system call, giving it the specified name and allocating it
+#      the next number, bumping __NR_syscalls.  If --compat is specified, then
+#      __SC_COMP() will be used in lieu of __SYSCALL() and the script will
+#      attempt to emit appropriate compatibility lines into the tables.
+#
+#  ./scripts/syscall-manage.pl --rm <name>
+#
+#      Remove the system call with the specified name, decrementing
+#      __NR_syscalls if it was the final one.
+#
+#  ./scripts/syscall-manage.pl --rename <name> <new_name>
+#
+#      Rename the system call with the specified name to the new name.
+#
+#  ./scripts/syscall-manage.pl --renumber
+#
+#      Renumber the system calls between 424 and __NR_syscalls to remove any
+#      holes and update __NR_syscalls.
+#
+#  ./scripts/syscall-manage.pl --resolve
+#
+#      Resolve simple git conflicts across all system call table files
+#      resulting from one branch being merged into another where both branches
+#      add system calls with conflicting numbers.  The new syscalls are
+#      renumbered, __NR_syscalls is updated and the conflict markers and any
+#      extra definition of __NR_syscalls are removed.
+#
+use strict;
+
+#
+# List of files that need to be altered and their insertion patterns
+#
+my $master = "include/uapi/asm-generic/unistd.h";
+my $sys_ni = "kernel/sys_ni.c";
+my @tables = (
+    { file     => "arch/alpha/kernel/syscalls/syscall.tbl",
+      pattern  => "%NUM common %NAME sys_%NAME",
+      num_offset => 110 },
+    { file     => "arch/arm/tools/syscall.tbl",
+      pattern  => "%NUM common %NAME sys_%NAME" },
+    { file     => "arch/ia64/kernel/syscalls/syscall.tbl",
+      pattern  => "%NUM common %NAME sys_%NAME" },
+    { file     => "arch/m68k/kernel/syscalls/syscall.tbl",
+      pattern  => "%NUM common %NAME sys_%NAME" },
+    { file     => "arch/microblaze/kernel/syscalls/syscall.tbl",
+      pattern  => "%NUM common %NAME sys_%NAME" },
+    { file     => "arch/mips/kernel/syscalls/syscall_n32.tbl",
+      pattern  => "%NUM n32 %NAME sys_%NAME",
+      compat   => 0 },
+    { file     => "arch/mips/kernel/syscalls/syscall_n32.tbl",
+      pattern  => "%NUM n32 %NAME compat_sys_%NAME",
+      compat   => 1 },
+    { file     => "arch/mips/kernel/syscalls/syscall_n64.tbl",
+      pattern  => "%NUM n64 %NAME sys_%NAME" },
+    { file     => "arch/mips/kernel/syscalls/syscall_o32.tbl",
+      pattern  => "%NUM o32 %NAME sys_%NAME",
+      compat   => 0 },
+    { file     => "arch/mips/kernel/syscalls/syscall_o32.tbl",
+      pattern  => "%NUM o32 %NAME sys_%NAME compat_sys_%NAME",
+      compat   => 1 },
+    { file     => "arch/parisc/kernel/syscalls/syscall.tbl",
+      pattern  => "%NUM common %NAME sys_%NAME",
+      compat   => 0 },
+    { file     => "arch/parisc/kernel/syscalls/syscall.tbl",
+      pattern  => "%NUM common %NAME compat_sys_%NAME",
+      compat   => 1 },
+    { file     => "arch/powerpc/kernel/syscalls/syscall.tbl",
+      pattern  => "%NUM common %NAME sys_%NAME",
+      compat   => 0 },
+    { file     => "arch/powerpc/kernel/syscalls/syscall.tbl",
+      pattern  => "%NUM common %NAME sys_%NAME compat_sys_%NAME",
+      compat   => 1 },
+    { file     => "arch/s390/kernel/syscalls/syscall.tbl",
+      pattern  => "%NUM common %NAME sys_%NAME sys_%NAME",
+      widths   => [ 8, 8, 24, 32, 32],
+      compat   => 0 },
+    { file     => "arch/s390/kernel/syscalls/syscall.tbl",
+      pattern  => "%NUM common %NAME sys_%NAME compat_sys_%NAME",
+      widths   => [ 8, 8, 24, 32, 32],
+      compat   => 1 },
+    { file     => "arch/sh/kernel/syscalls/syscall.tbl",
+      pattern  => "%NUM common %NAME sys_%NAME" },
+    { file     => "arch/sparc/kernel/syscalls/syscall.tbl",
+      pattern  => "%NUM common %NAME sys_%NAME",
+      compat   => 0 },
+    { file     => "arch/sparc/kernel/syscalls/syscall.tbl",
+      pattern  => "%NUM common %NAME sys_%NAME compat_sys_%NAME",
+      compat   => 1 },
+    { file     => "arch/x86/entry/syscalls/syscall_32.tbl",
+      pattern  => "%NUM i386 %NAME sys_%NAME __ia32_sys_%NAME",
+      widths   => [ 8, 8, 24, 32, 32],
+      compat   => 0 },
+    { file     => "arch/x86/entry/syscalls/syscall_32.tbl",
+      pattern  => "%NUM i386 %NAME sys_%NAME __ia32_compat_sys_%NAME",
+      widths   => [ 8, 8, 24, 32, 32],
+      compat   => 1 },
+    { file     => "arch/x86/entry/syscalls/syscall_64.tbl",
+      pattern  => "%NUM common %NAME __x64_sys_%NAME",
+      widths   => [ 8, 8, 24, 32, 32] },
+    { file     => "arch/xtensa/kernel/syscalls/syscall.tbl",
+      pattern  => "%NUM common %NAME sys_%NAME" },
+    #{ file    => "tools/perf/arch/powerpc/entry/syscalls/syscall.tbl",
+    #  pattern => "%NUM common %NAME sys_%NAME" },
+    #{ file    => "tools/perf/arch/s390/entry/syscalls/syscall.tbl",
+    #  pattern => "%NUM common %NAME sys_%NAME sys_%NAME" },
+    #{ file    => "tools/perf/arch/x86/entry/syscalls/syscall_64.tbl",
+    #  pattern => "%NUM common %NAME __x64_sys_%NAME" },
+    );
+
+my $common_base = 424;
+
+#
+# Helpers
+#
+sub read_file($)
+{
+    my ($file) = @_;
+    my @lines;
+
+    open(FD, "<" . "$file") || die $file;
+    while (<FD>) {
+       chomp($_);
+       push @lines, $_;
+    }
+    close(FD) || die $file;
+    return \@lines;
+}
+
+sub write_file($$)
+{
+    my ($file, $lines) = @_;
+
+    print "Writing $file\n";
+    open(FD, ">" . "$file") || die $file;
+    print FD $_, "\n" foreach(@{$lines});
+    close(FD) || die $file;
+}
+
+###############################################################################
+#
+# Add a new syscall to the master list and return the syscall number allocated.
+#
+###############################################################################
+sub add_to_master($$)
+{
+    my ($name, $compat) = @_;
+    my $f = $master;
+    my $lines = read_file($f);
+    my $num = -1;
+    my $nr = -1;
+    my $i;
+    my $j = -1;
+
+    for ($i = 0; $i <= $#{$lines}; $i++) {
+       my $l = $lines->[$i];
+       if ($l =~ /^#define\s+__NR_syscalls\s+([0-9]+)/) {
+           die "$f:$i: Multiple __NR_syscalls definitions\n" if ($nr != -1);
+           $nr = $1;
+           $j = $i;
+       }
+
+       if ($l =~ /^#define\s+__NR_([a-zA-Z0-9_]+)\s+([0-9]+)/) {
+           if ($1 eq $name) {
+               die "$f:$i: Syscall multiply defined (", $num, ")\n" if ($num 
!= -1);
+               print STDERR "$f:$i: Syscall already exists (", $2, ")\n";
+               $num = $2;
+           }
+       }
+    }
+
+    die "$f: error: Can't find __NR_syscalls\n" if ($nr == -1);
+
+    if ($num == -1) {
+       # Update the last syscall number
+       $num = $nr;
+       print "Allocating syscall number ", $num, "\n";
+       $lines->[$j] = "#define __NR_syscalls " . ($nr + 1);
+
+       # Rewind to the last syscall number definition
+       while ($j--, $j >= 0 && $lines->[$j] eq "") {}
+       die "$f:$j: error: Expecting #undef __NR_syscalls\n"
+           unless ($lines->[$j] =~ /^#undef\s+__NR_syscalls/);
+       while ($j--, $j >= 0 && $lines->[$j] eq "") {}
+       die "$f:%j: error: Expecting __SYSCALL or __SC_COMP\n"
+           unless ($lines->[$j] =~ 
/^(__SYSCALL|__SC_COMP|__SC_3264|__SC_COMP_3264)/);
+       if ($lines->[$j - 1] =~ /^#define __NR_([a-zA-Z0-9_]+) ([0-9]+)/) {
+           die "$f:$j: error: Incorrect syscall number ($2 != $num)\n"
+               if ($2 != $num - 1);
+       } else {
+           die "$f:$j: error: Expecting #define __NR_*\n";
+       }
+       $j++;
+
+       # Insert the new syscall number
+       if ($compat == 0) {
+           splice(@{$lines}, $j, 0,
+                  ( "#define __NR_$name $num",
+                    "__SYSCALL(__NR_$name, sys_$name)" ));
+       } elsif ($compat == 1) {
+           splice(@{$lines}, $j, 0,
+                  ( "#define __NR_$name $num",
+                    "__SC_COMP(__NR_$name, sys_$name, compat_sys_$name)" ));
+       } else {
+           die;
+       }
+
+       write_file($f, $lines);
+    }
+
+    return $num;
+}
+
+###############################################################################
+#
+# Add tabs to a string to pad it out
+#
+###############################################################################
+sub tab_to($$)
+{
+    my ($s, $width) = @_;
+
+    if ($width == 8) {
+       return $s . "\t";
+    } elsif ($width == 16) {
+       return $s . "\t" if (length($s) > 7);
+       return $s . "\t\t";
+    } elsif ($width == 24) {
+       return $s . "\t" if (length($s) > 15);
+       return $s . "\t\t" if (length($s) > 7);
+       return $s . "\t\t\t";
+    } elsif ($width == 32) {
+       return $s . "\t" if (length($s) > 23);
+       return $s . "\t\t" if (length($s) > 15);
+       return $s . "\t\t\t" if (length($s) > 7);
+       return $s . "\t\t\t\t";
+    } else {
+       die "Width $width\n";
+    }
+}
+
+###############################################################################
+#
+# Tabulate a table line appropriately.
+#
+###############################################################################
+sub tabulate($$)
+{
+    my ($l, $widths) = @_;
+    my @bits = split(/\s+/, $l);
+
+    my $rl = tab_to($bits[0], $widths->[0]);   # Syscall number
+    $rl .= tab_to($bits[1], $widths->[1]);     # Syscall type
+    $rl .= tab_to($bits[2], $widths->[2]);     # Syscall name
+
+    # Add the syscall handlers
+    if ($#bits == 3) {
+       $rl .= $bits[3];
+    } elsif ($#bits == 4) {
+       $rl .= tab_to($bits[3], $widths->[3]);
+       $rl .= $bits[4];
+    } elsif ($#bits == 5) {
+       $rl .= tab_to($bits[3], $widths->[4]);
+       $rl .= tab_to($bits[4], $widths->[5]);
+       $rl .= $bits[5];
+    } else {
+       die "Too many handlers\n";
+    }
+}
+
+###############################################################################
+#
+# Add a new syscall to a syscall.tbl file.
+#
+###############################################################################
+sub add_to_table($$$)
+{
+    my ($name, $num, $table) = @_;
+    my $f = $table->{file};
+    my $pattern = $table->{pattern};
+    my $widths = $table->{widths} ? $table->{widths} : [ 8, 8, 32, 32, 32 ];
+    my $lines = read_file($f);
+    my $i;
+    my $j = -1;
+
+    $num += $table->{num_offset} if (exists $table->{num_offset});
+
+    for ($i = 0; $i <= $#{$lines}; $i++) {
+       my $l = $lines->[$i];
+       my @bits = split(/\s+/, $l);
+       next if ($#bits == -1);
+       if ($bits[0] eq $num - 1) {
+           die "$f:$i: Duplicate syscall ", $num - 1, "\n" if $j != -1;
+           $j = $i;
+       }
+       if ($bits[0] eq $num) {
+           if ($bits[2] eq $name) {
+               print STDERR "$f:$i: Ignoring already-added syscall ", $num, 
"\n";
+               return;
+           }
+           die "$f:$i: Conflicting syscall ", $num, "\n";
+       }
+    }
+
+    die "$f: error: Can't find syscall ", $num - 1, "\n" if ($j == -1);
+
+    $pattern =~ s/%NAME/$name/g;
+    $pattern =~ s/%NUM/$num/g;
+    $pattern = tabulate($pattern, $widths);
+
+    # Insert the new syscall entry after the preceding one.
+    splice(@{$lines}, $j + 1, 0, ( $pattern ));
+
+    write_file($f, $lines);
+}
+
+###############################################################################
+#
+# Remove a syscall from the master list.
+#
+###############################################################################
+sub remove_from_master($)
+{
+    my ($name) = @_;
+    my $f = $master;
+    my $lines = read_file($f);
+    my $num = -1;
+    my $nr = -1;
+    my $i;
+    my $i_nr = -1;
+    my $i_num = -1;
+    my $c = 1;
+
+    for ($i = 0; $i <= $#{$lines}; $i++) {
+       my $l = $lines->[$i];
+       if ($l =~ /^#define\s+__NR_syscalls\s+([0-9]+)/) {
+           die "$f:$i: Multiple __NR_syscalls definitions\n" if ($nr != -1);
+           $nr = $1;
+           $i_nr = $i;
+       }
+
+       if ($l =~ /^#define\s+__NR_([a-zA-Z0-9_]+)\s+([0-9]+)/) {
+           if ($1 eq $name) {
+               if ($num != -1) {
+                   print STDERR "$f:$i: Syscall multiply defined (", $num, 
")\n"
+               }
+               $num = $2; # Remove the last instance only
+               $i_num = $i;
+           }
+       }
+    }
+
+    die "$f: error: Can't find __NR_syscalls\n" if ($i_nr == -1);
+
+    if ($i_num == -1) {
+       print "Syscall not found in unistd.h\n";
+       return;
+    }
+
+    # If the syscall number is the last one, deallocate it
+    if ($nr == $num + 1) {
+       print "Deallocating syscall number ", $num, "\n";
+       $lines->[$i_nr] = "#define __NR_syscalls " . ($nr - 1);
+    }
+
+    # Remove the __SYSCALL or __SC_COMP line also
+    if ($lines->[$i_num + 1] =~ 
/^(__SYSCALL|__SC_COMP|__SC_3264|__SC_COMP_3264)[(]__NR_$name,/) {
+       $c++;
+       $c++ if ($lines->[$i_num + 1] =~ /\\$/);
+    }
+
+    splice(@{$lines}, $i_num, $c, ());
+    write_file($f, $lines);
+}
+
+###############################################################################
+#
+# Remove a syscall from a syscall.tbl file.
+#
+###############################################################################
+sub remove_from_table($$)
+{
+    my ($name, $table) = @_;
+    my $f = $table->{file};
+    my $lines = read_file($f);
+    my $i;
+    my $j = -1;
+
+    for ($i = 0; $i <= $#{$lines}; $i++) {
+       my $l = $lines->[$i];
+       my @bits = split(/\s+/, $l);
+       my $num = $bits[0];
+       next if ($#bits < 2);
+
+       if ($bits[2] eq $name) {
+           print STDERR "$f:$i: Duplicate syscall ", $num, "\n" if ($j != -1);
+           $j = $i;
+       }
+    }
+
+    if ($j == -1) {
+       print STDERR "$f: error: Can't find syscall ", $name, "\n";
+       return;
+    }
+
+    # Remove the syscall entry
+    splice(@{$lines}, $j, 1, ());
+
+    write_file($f, $lines);
+}
+
+###############################################################################
+#
+# Remove a syscall from kernel/sys_ni.c
+#
+###############################################################################
+sub remove_from_sys_ni($)
+{
+    my ($name) = @_;
+    my $f = $sys_ni;
+    my $lines = read_file($f);
+    my $i;
+    my $j = -1;
+
+    for ($i = 0; $i <= $#{$lines}; $i++) {
+       my $l = $lines->[$i];
+
+       if ($l =~ /COND_SYSCALL[_A-Z]*[(]$name[)]/) {
+           if ($j == -1) {
+               $j = $i;
+           } else {
+               print STDERR "$f:$i: Multiple COND_SYSCALLs\n";
+           }
+       }
+    }
+
+    return if ($j == -1);
+
+    # Remove the COND_SYSCALL entry
+    splice(@{$lines}, $j, 1, ());
+
+    write_file($f, $lines);
+}
+
+###############################################################################
+#
+# Rename a syscall in the master list.
+#
+###############################################################################
+sub rename_in_master($$)
+{
+    my ($name, $name2) = @_;
+    my $f = $master;
+    my $lines = read_file($f);
+    my $num = -1;
+    my $i;
+    my $i_num = -1;
+
+    for ($i = 0; $i <= $#{$lines}; $i++) {
+       my $l = $lines->[$i];
+       if ($l =~ /^#define\s+__NR_([a-zA-Z0-9_]+)\s+([0-9]+)/) {
+           if ($1 eq $name) {
+               if ($num != -1) {
+                   print STDERR "$f:$i: Syscall multiply defined (", $num, 
")\n"
+               }
+               $num = $2; # Rename the last instance only
+               $i_num = $i;
+           }
+       }
+    }
+
+    if ($i_num == -1) {
+       print "Syscall not found in unistd.h\n";
+       return;
+    }
+
+    # Rename the __SYSCALL or __SC_COMP line also
+    $lines->[$i_num] =~ s/$name/$name2/g;
+    if ($lines->[$i_num + 1] =~ 
/^(__SYSCALL|__SC_COMP|__SC_3264|__SC_COMP_3264)[(]__NR_$name,/) {
+       $lines->[$i_num + 1] =~ s/$name/$name2/g;
+       $lines->[$i_num + 2] =~ s/$name/$name2/g if ($lines->[$i_num + 1] =~ 
/\\$/);
+    }
+
+    write_file($f, $lines);
+}
+
+###############################################################################
+#
+# Rename a syscall in a syscall.tbl file.
+#
+###############################################################################
+sub rename_in_table($$$)
+{
+    my ($name, $name2, $table) = @_;
+    my $f = $table->{file};
+    my $pattern = $table->{pattern};
+    my $widths = $table->{widths} ? $table->{widths} : [ 8, 8, 32, 32, 32 ];
+    my $lines = read_file($f);
+    my $i;
+    my $j = -1;
+
+    for ($i = 0; $i <= $#{$lines}; $i++) {
+       my $l = $lines->[$i];
+       my @bits = split(/\s+/, $l);
+       my $num = $bits[0];
+       next if ($#bits < 2);
+
+       if ($bits[2] eq $name) {
+           print STDERR "$f:$i: Duplicate syscall ", $num, "\n" if ($j != -1);
+           $j = $i;
+       }
+    }
+
+    if ($j == -1) {
+       print STDERR "$f: error: Can't find syscall ", $name, "\n";
+       return;
+    }
+
+    # Rename the syscall entry
+    my $l = $lines->[$j];
+    $l =~ s/$name/$name2/g;
+    $lines->[$j] = $pattern = tabulate($l, $widths);
+
+    write_file($f, $lines);
+}
+
+###############################################################################
+#
+# Rename a syscall in kernel/sys_ni.c
+#
+###############################################################################
+sub rename_in_sys_ni($$)
+{
+    my ($name, $name2) = @_;
+    my $f = $sys_ni;
+    my $lines = read_file($f);
+    my $changed = 0;
+    my $i;
+
+    for ($i = 0; $i <= $#{$lines}; $i++) {
+       my $l = $lines->[$i];
+
+       if ($l =~ /COND_SYSCALL[_A-Z]*[(]$name[)]/) {
+           $lines->[$i] =~ s/$name/$name2/;
+           $changed = 1;
+       }
+    }
+
+    write_file($f, $lines) if ($changed);
+}
+
+###############################################################################
+#
+# Resolve git-conflicted syscalls in the master list.
+#
+###############################################################################
+sub resolve_conflicts_in_master()
+{
+    my $f = $master;
+    my $lines = read_file($f);
+    my $nr = -1;
+    my $i;
+    my $i_nr = -1;
+    my $begin = -1;
+    my $mid = -1;
+    my $end = -1;
+    my $mode = 0;
+    my $nr_mode = 0;
+    my %conflict_list = ();
+
+    for ($i = 0; $i <= $#{$lines}; $i++) {
+       my $l = $lines->[$i];
+
+       if ($l =~ /^#define\s+__NR_syscalls\s+([0-9]+)/) {
+           if ($mode == 0 && $i_nr != -1) {
+               $nr_mode = 4;
+               $nr = $1;
+               $i_nr = $i;
+           } elsif ($mode == 1 && $nr_mode == 0) {
+               $nr_mode = 1;
+               $nr = $1;
+               $i_nr = $i;
+           } elsif ($mode == 2 && $nr_mode == 1) {
+               $nr_mode = 2;
+               $i_nr = $i;
+           } elsif ($mode == 3 && $nr_mode == 0) {
+               $nr_mode = 3;
+               $nr = $1;
+               $i_nr = $i;
+           } else {
+               die "$f:$i: Multiple __NR_syscalls definitions\n";
+           }
+           next;
+       }
+       next if ($mode == 3);
+
+       if ($l =~ /^<<<<<<</) {
+           $begin = $i;
+           $mode = 1;
+           next;
+       }
+       if ($l =~ /^=======/) {
+           $mid = $i;
+           $mode = 2;
+           next;
+       }
+       if ($l =~ /^>>>>>>>/) {
+           $end = $i;
+           $mode = 3;
+           next;
+       }
+       next if ($mode == 0);
+    }
+
+    if ($mode == 0) {
+       print "$f: Couldn't find section to be resolved\n";
+       return;
+    }
+
+    die "$f: error: Can't find __NR_syscalls\n" if ($i_nr == -1);
+
+    # Analyse what we're merging into.
+    my $top = -1;
+    for ($i = $begin + 1; $i < $mid; $i++) {
+       my $l = $lines->[$i];
+
+       next if ($l =~ /^#define\s+__NR_syscalls\s+[0-9]+/);
+
+       if ($l =~ /^#define\s+__NR_([a-zA-Z0-9_]+)\s+([0-9]+)/) {
+           my $name = $1;
+           my $num = $2;
+           die "$f:$i: Redefinition of $name\n" if 
(exists($conflict_list{$name}));
+           die "$f:$i: Number regression\n" if ($num < $top);
+           $top = $num;
+           $conflict_list{$name} = $num;
+           print "Keep __NR_", $name, " as ", $num, "\n";
+       }
+    }
+
+    die "$f: Last number (", $top, ") different to limit-1 (", $nr - 1, ")\n"
+       if ($top != -1 && $top != $nr - 1);
+
+    # Renumber what we're merging in.
+    for ($i = $mid + 1; $i < $end; $i++) {
+       my $l = $lines->[$i];
+
+       next if ($l =~ /^#define\s+__NR_syscalls\s+[0-9]+/);
+
+       if ($l =~ /^#define\s+__NR_([a-zA-Z0-9_]+)\s+([0-9]+)/) {
+           my $name = $1;
+           my $num = $2;
+           die "$f:$i: Definition of $name in both branches\n"
+               if (exists($conflict_list{$name}));
+           my $new = $nr;
+           $conflict_list{$name} = $new;
+           $l =~ s/(\s)$num/${1}$new/;
+           print "Reassign __NR_", $name, " to ", $new, "\n";
+           $lines->[$i] = $l;
+           $nr++;
+       }
+    }
+
+    # Adjust __NR_syscalls
+    if ($lines->[$i_nr] =~ /^#define\s+__NR_syscalls\s+([0-9]+)/) {
+       my $num = $1;
+       $lines->[$i_nr] =~ s/(\s)$num/${1}$nr/;
+       print "__NR_syscalls set to $nr\n";
+    }
+
+    # Delete various bits, starting with the highest index and working towards
+    # the lowest so as not to displace the higher indices.
+    splice(@{$lines}, $end, 1, ());
+    splice(@{$lines}, $mid, 1, ());
+    for ($i = $mid - 1; $i > $begin; $i--) {
+       my $l = $lines->[$i];
+
+       splice(@{$lines}, $i, 1, ()) if ($l =~ /^#undef\s+__NR_syscalls\s*$/);
+       splice(@{$lines}, $i, 1, ()) if ($l =~ 
/^#define\s+__NR_syscalls\s+([0-9]+)/);
+       splice(@{$lines}, $i, 1, ()) if ($l =~ /^$/);
+    }
+    splice(@{$lines}, $begin, 1, ());
+
+    write_file($f, $lines);
+    return \%conflict_list;
+}
+
+###############################################################################
+#
+# Resolve git-conflicted syscalls in a syscall.tbl file.
+#
+###############################################################################
+sub resolve_conflicts_in_table($$)
+{
+    my ($conflict_list, $table) = @_;
+    my $f = $table->{file};
+    my $lines = read_file($f);
+    my $i;
+    my $begin = -1;
+    my $mid = -1;
+    my $end = -1;
+    my $mode = 0;
+
+    for ($i = 0; $i <= $#{$lines}; $i++) {
+       my $l = $lines->[$i];
+
+       if ($l =~ /^<<<<<<</) {
+           $begin = $i;
+           $mode = 1;
+           next;
+       }
+       if ($l =~ /^=======/) {
+           $mid = $i;
+           $mode = 2;
+           next;
+       }
+       if ($l =~ /^>>>>>>>/) {
+           $end = $i;
+           $mode = 3;
+           last;
+       }
+       next if ($mode == 0);
+    }
+
+    if ($mode == 0) {
+       print "$f: Couldn't find section to be resolved\n";
+       return;
+    }
+
+    my %used = ();
+    for ($i = $begin + 1; $i < $mid; $i++) {
+       my $l = $lines->[$i];
+       next unless ($l =~ /^[0-9]/);
+       my @bits = split(/\s+/, $l);
+       my $num = $bits[0];
+       my $name = $bits[2];
+       next if ($#bits < 2);
+
+       die "$f:$i: Undefined syscall '", $name, "'\n"
+           unless (exists($conflict_list->{$name}));
+
+       my $new = $conflict_list->{$name};
+       $new += $table->{num_offset} ? $table->{num_offset} : 0;
+       die "$f:$i: Redefined syscall '", $name, "'\n" if 
(exists($used{$name}));
+       $used{$name} = 1;
+       next if ($num == $new);
+    }
+
+    for ($i = $mid + 1; $i < $end; $i++) {
+       my $l = $lines->[$i];
+       next unless ($l =~ /^[0-9]/);
+       my @bits = split(/\s+/, $l);
+       my $num = $bits[0];
+       next if ($#bits < 2);
+       my $name = $bits[2];
+
+       die "$f:$i: Undefined syscall '", $name, "'\n"
+           unless (exists($conflict_list->{$name}));
+
+       my $new = $conflict_list->{$name};
+       $new += $table->{num_offset} ? $table->{num_offset} : 0;
+       die "$f:$i: Redefined syscall '", $name, "'\n" if 
(exists($used{$name}));
+       $used{$name} = 1;
+       next if ($num == $new);
+       $lines->[$i] =~ s/^$num/$new/;
+    }
+
+    # Delete the git markers, starting with the highest index and working
+    # towards the lowest so as not to displace the higher indices.
+    splice(@{$lines}, $end, 1, ());
+    splice(@{$lines}, $mid, 1, ());
+    splice(@{$lines}, $begin, 1, ());
+
+    write_file($f, $lines);
+}
+
+###############################################################################
+#
+# Renumber the syscall numbers in the master list that are between 424 and
+# __NR_syscalls and reduce __NR_syscalls.
+#
+###############################################################################
+sub renumber_master()
+{
+    my $f = $master;
+    my $lines = read_file($f);
+    my $nr = -1;
+    my $next = $common_base;
+    my $i;
+    my $i_nr = -1;
+    my %num_list = ();
+
+    # Find the __NR_syscalls value.
+    for ($i = 0; $i <= $#{$lines}; $i++) {
+       my $l = $lines->[$i];
+       if ($l =~ /^#define\s+__NR_syscalls\s+([0-9]+)/) {
+           die "$f:$i: Redefinition of __NR_syscalls\n" if ($i_nr != -1);
+           $nr = $1;
+           $i_nr = $i;
+       }
+    }
+    die "$f: error: Can't find __NR_syscalls\n" if ($i_nr == -1);
+
+    # Renumber the definitions.
+    for ($i = 0; $i <= $#{$lines}; $i++) {
+       my $l = $lines->[$i];
+       if ($l =~ /^#define\s+__NR_([a-zA-Z0-9_]+)\s+([0-9]+)/) {
+           my $name = $1;
+           my $num = $2;
+
+           next if ($num < $common_base || $num >= $nr);
+           if ($num != $next) {
+               print "Renumber ", $name, " from ", $num, " to ", $next, "\n";
+               $lines->[$i] =~ s/(\s)$num/${1}$next/;
+               $num_list{$name} = $next;
+           }
+
+           $next++;
+       }
+    }
+
+    # Adjust __NR_syscalls
+    $lines->[$i_nr] =~ s/(\s)$nr/${1}$next/;
+    print "__NR_syscalls set to $next\n";
+
+    write_file($f, $lines);
+    return \%num_list;
+}
+
+###############################################################################
+#
+# Renumber the syscall numbers in a syscall.tbl file to match the master.
+#
+###############################################################################
+sub renumber_table($$)
+{
+    my ($num_list, $table) = @_;
+    my $f = $table->{file};
+    my $lines = read_file($f);
+    my $i;
+
+    for ($i = 0; $i <= $#{$lines}; $i++) {
+       my $l = $lines->[$i];
+       my @bits = split(/\s+/, $l);
+       next if ($#bits < 2);
+       my $num = $bits[0];
+       my $name = $bits[2];
+
+       next unless (exists($num_list->{$name}));
+       my $new = $num_list->{$name};
+       $new += $table->{num_offset} ? $table->{num_offset} : 0;
+       next if ($num eq $new);
+
+       $lines->[$i] =~ s/^$num/$new/;
+    }
+
+    write_file($f, $lines);
+}
+
+###############################################################################
+#
+# Decide what to do based on the script parameters
+#
+###############################################################################
+sub format_error()
+{
+    print("Format: syscall-manage.pl --add <name> [--compat]\n");
+    print("                          --rm <name>\n");
+    print("                          --rename <name>\n");
+    print("                          --renumber\n");
+    print("                          --resolve\n");
+    exit(2);
+}
+
+format_error() if ($#ARGV < 0);
+
+if ($ARGV[0] eq "--add") {
+    format_error() if ($#ARGV < 1);
+
+    my $name = $ARGV[1];
+    my $compat = 0;
+    $compat = 1 if ($#ARGV == 2 && $ARGV[2] eq "--compat");
+
+    my $num = add_to_master($name, $compat);
+    foreach my $table (@tables) {
+       next if (exists($table->{compat}) && $compat != $table->{compat});
+       add_to_table($name, $num, $table);
+    }
+} elsif ($ARGV[0] eq "--rm") {
+    format_error() if ($#ARGV < 1);
+
+    my $name = $ARGV[1];
+    remove_from_master($name);
+    foreach (@tables) {
+       remove_from_table($name, $_);
+    }
+    remove_from_sys_ni($name);
+} elsif ($ARGV[0] eq "--rename") {
+    format_error() if ($#ARGV < 2);
+
+    my $name = $ARGV[1];
+    my $name2 = $ARGV[2];
+    rename_in_master($name, $name2);
+    foreach (@tables) {
+       rename_in_table($name, $name2, $_);
+    }
+    rename_in_sys_ni($name, $name2);
+} elsif ($ARGV[0] eq "--resolve") {
+    my $conflict_list = resolve_conflicts_in_master();
+    foreach (@tables) {
+       resolve_conflicts_in_table($conflict_list, $_);
+    }
+} elsif ($ARGV[0] eq "--renumber") {
+    my $num_list = renumber_master();
+    foreach (@tables) {
+       renumber_table($num_list, $_);
+    }
+} else {
+    format_error();
+}

Reply via email to