Re: linux-next: manual merge of the notifications tree with the pidfd tree

From: David Howells
Date: Wed Jan 27 2021 - 06:15:52 EST


#!/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 $in_section = 0;
my $nr_in_section = 0;
my %conflict_list = ();

for ($i = 0; $i <= $#{$lines}; $i++) {
my $l = $lines->[$i];

if ($l =~ /^#define\s+__NR_syscalls\s+([0-9]+)/) {
if ($in_section == 0) {
# Before '<<<<<<<'
die "$f:$i: Multiple __NR_syscalls definitions\n[s=$in_section inr=$i_nr nis=$nr_in_section]\n"
unless ($i_nr == -1);
$nr = $1;
$i_nr = $i;
$nr_in_section = 4;
} elsif ($in_section == 1 && $nr_in_section == 0) {
# After '<<<<<<<'
$nr = $1;
$i_nr = $i;
$nr_in_section = 1;
} elsif ($in_section == 2 && $nr_in_section == 1) {
# After '======='
$i_nr = $i;
$nr_in_section = 2;
} elsif ($in_section == 3 && $nr_in_section == 0) {
# After '>>>>>>>'
$nr = $1;
$i_nr = $i;
$nr_in_section = 3;
} else {
die "$f:$i: Multiple __NR_syscalls definitions\n[s=$in_section inr=$i_nr nis=$nr_in_section]\n";
}
next;
}
next if ($in_section == 3);

if ($l =~ /^<<<<<<</) {
$begin = $i;
$in_section = 1;
next;
}
if ($l =~ /^=======/) {
$mid = $i;
$in_section = 2;
next;
}
if ($l =~ /^>>>>>>>/) {
$end = $i;
$in_section = 3;
next;
}
next if ($in_section == 0);
}

die "$f: error: Can't find __NR_syscalls\n" if ($i_nr == -1);

# Analyse the pre-merge syscalls.
my $top = -1;
my $stop = ($begin == -1) ? $#{$lines} : $begin;
for ($i = 0; $i < $stop; $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 ($name eq "syscalls");
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";
}
}

if ($in_section == 0) {
print "$f: Couldn't find section to be resolved\n";
return \%conflict_list;
}

# Analyse what we're merging into.
for ($i = $begin + 1; $i < $mid; $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 ($name eq "syscalls");
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];

if ($l =~ /^#define\s+__NR_([a-zA-Z0-9_]+)\s+([0-9]+)/) {
my $name = $1;
my $num = $2;
next if ($name eq "syscalls");
if (exists($conflict_list{$name})) {
warn "$f:$i: Definition of $name in both branches\n";
# Remove the duplicate
splice(@{$lines}, $i, 2, ());
$end -= 2;
$i--;
next;
}
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 $in_section = 0;

for ($i = 0; $i <= $#{$lines}; $i++) {
my $l = $lines->[$i];

if ($l =~ /^<<<<<<</) {
$begin = $i;
$in_section = 1;
next;
}
if ($l =~ /^=======/) {
$mid = $i;
$in_section = 2;
next;
}
if ($l =~ /^>>>>>>>/) {
$end = $i;
$in_section = 3;
last;
}
next if ($in_section == 0);
}

if ($in_section == 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 (my) 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 (my) 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 (other) syscall '", $name, "'\n"
unless (exists($conflict_list->{$name}));

my $new = $conflict_list->{$name};
$new += $table->{num_offset} ? $table->{num_offset} : 0;
if (exists($used{$name})) {
warn "$f:$i: Redefined (other) syscall '", $name, "'\n";
splice(@{$lines}, $i, 1, ());
$i--;
$end--;
next;
}
$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, ());

#print $_, "\n" foreach (@{$lines});
#exit(88);

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);
}

###############################################################################
#
# Change a syscall in a syscall.tbl file.
#
###############################################################################
sub change_in_table($$$)
{
my ($name, $to_id, $table) = @_;
my $f = $table->{file};
my $pattern = $table->{pattern};
my $widths = $table->{widths} ? $table->{widths} : [ 8, 8, 32, 32, 32 ];
my $old_id = "";
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);
next if ($#bits < 2);
my $num = $bits[0];

if ($bits[2] eq $name) {
print STDERR "$f:$i: Duplicate syscall ", $num, "\n" if ($j != -1);
$j = $i;
$old_id = $bits[0];
}
}

if ($j == -1) {
print STDERR "$f: error: Can't find syscall ", $name, "\n";
return;
}

if ($old_id eq "") {
print STDERR "$f: error: Can't find syscall number ", $name, "\n";
return;
}

# Rename the syscall entry
my $l = $lines->[$j];
$l =~ s/^$old_id/$to_id/g;
$lines->[$j] = $pattern = tabulate($l, $widths);

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(" --change <name> <to-id>\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 "--change") {
format_error() if ($#ARGV < 2);

my $name = $ARGV[1];
my $to_id = $ARGV[2];
foreach (@tables) {
change_in_table($name, $to_id, $_);
}
} 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();
}