Jean Delvare 591eab
#!/usr/bin/perl -w
Jean Delvare 591eab
#
Jean Delvare 591eab
#############################################################################
Jean Delvare 591eab
# Copyright (c) 2014 Jean Delvare <jdelvare@suse.de>
Jean Delvare 591eab
# All Rights Reserved.
Jean Delvare 591eab
#
Jean Delvare 591eab
# This program is free software; you can redistribute it and/or
Jean Delvare 591eab
# modify it under the terms of version 2 of the GNU General Public License as
Jean Delvare 591eab
# published by the Free Software Foundation.
Jean Delvare 591eab
#
Jean Delvare 591eab
# This program is distributed in the hope that it will be useful,
Jean Delvare 591eab
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean Delvare 591eab
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the
Jean Delvare 591eab
# GNU General Public License for more details.
Jean Delvare 591eab
#############################################################################
Jean Delvare 591eab
#
Jean Delvare 591eab
# The purpose of this script is to catch, and in most cases fix,
Jean Delvare 591eab
# inconsistencies between supported.conf and the set of modules being actually
Jean Delvare 591eab
# installed. It is recommended to run it on a complete set of installed modules
Jean Delvare 591eab
# (all flavors and architectures) otherwise you may get false positives. Note
Jean Delvare 591eab
# that you can pass as many modules directories as you want.
Jean Delvare 591eab
#
Jean Delvare 591eab
# Usage: supported-conf-fixup [OPTIONS] supported.conf [/lib/modules/x.x-flavor ...]
Jean Delvare 591eab
# Options:
Jean Delvare 591eab
#  -h, --help		Print usage and exit
Jean Delvare 591eab
#      --mod-path	Report wrong module paths
Jean Delvare 591eab
#      --mod-missing	Report missing modules
Jean Delvare 591eab
#      --mask-path	Report wrong mask paths
Jean Delvare 591eab
#      --mask-missing	Report missing mask paths
Jean Delvare 6a5951
#      --not-listed	Report unlisted modules
Jean Delvare 591eab
#  -v, --verbose	Be verbose
Jean Delvare 591eab
#  -o, --output file	Write updated supported.conf to file
Jean Delvare 430664
#      --sort		Sort the output file
Jean Delvare 591eab
#
Jean Delvare 591eab
# To do: better handling of _ vs -
Jean Delvare 591eab
# To do: better handling of .ko being optional
Jean Delvare 591eab
Jean Delvare 591eab
use strict;
Jean Delvare 591eab
use vars qw($sup_conf_file @sup_conf_data @supported_mod @supported_mask %supported
Jean Delvare 591eab
	    %installed $n_ins %installed_paths %builtin %not_listed $verbose
Jean Delvare 6a5951
	    $check_mod_path $check_mod_missing $check_mask_path $check_mask_missing
Jean Delvare 6a5951
	    $check_not_listed $output $n_err $n_corrected $usage $sort);
Jean Delvare 591eab
use File::Find;
Jean Delvare 591eab
use Getopt::Long;
Jean Delvare 591eab
use Fcntl;
Jean Delvare 591eab
Jean Delvare 591eab
# Data structures:
Jean Delvare 591eab
# @sup_conf_data contains raw lines from supported.conf
Jean Delvare 591eab
# @supported_mod contains hashes with the following keys:
Jean Delvare 591eab
#   - line: line number in supported.conf
Jean Delvare 591eab
#   - module path: relative path + module name
Jean Delvare 591eab
# @supported_mask contains hashes with the following keys:
Jean Delvare 591eab
#   - line: line number in supported.conf
Jean Delvare 591eab
#   - mask path: relative path + *
Jean Delvare 591eab
# %supported contains the same information as @supported_mod + @supported_mask,
Jean Delvare 591eab
#   but as a module/mask path => line mapping
Jean Delvare 591eab
# %installed contains the list of installed modules, as a
Jean Delvare 591eab
#   module name => module paths mapping
Jean Delvare 591eab
# %installed_paths records paths which contain installed modules (only
Jean Delvare 591eab
#   keys are meaningful)
Jean Delvare 591eab
# %not_listed keys list installed modules which aren't listed in supported.conf
Jean Delvare 591eab
#   (so their support status is unknown)
Jean Delvare 591eab
# %builtin contains the list of built-in modules, as a
Jean Delvare 591eab
#   module name => module paths mapping
Jean Delvare 591eab
Jean Delvare 591eab
Getopt::Long::Configure("bundling");
Jean Delvare 77722e
$verbose = 0;
Jean Delvare 591eab
GetOptions(
Jean Delvare 591eab
	"help|h"	=> \$usage,
Jean Delvare 591eab
	"mod-path"	=> \$check_mod_path,
Jean Delvare 591eab
	"mod-missing"	=> \$check_mod_missing,
Jean Delvare 591eab
	"mask-path"	=> \$check_mask_path,
Jean Delvare 591eab
	"mask-missing"	=> \$check_mask_missing,
Jean Delvare 591eab
	"not-listed"	=> \$check_not_listed,
Jean Delvare 591eab
	"verbose|v+"	=> \$verbose,
Jean Delvare 591eab
	"output|o=s"	=> \$output,
Jean Delvare 430664
	"sort"		=> \$sort,
Jean Delvare 591eab
) or exit 1;
Jean Delvare 591eab
Jean Delvare 591eab
if ($usage || @ARGV < 1) {
Jean Delvare 591eab
	print STDERR
Jean Delvare 591eab
"Usage: $0 [OPTIONS] supported.conf [/lib/modules/x.x-flavor ...]
Jean Delvare 591eab
Options:
Jean Delvare 591eab
 -h, --help		Print usage and exit
Jean Delvare 591eab
     --mod-path		Report wrong module paths
Jean Delvare 591eab
     --mod-missing	Report missing modules
Jean Delvare 591eab
     --mask-path	Report wrong mask paths
Jean Delvare 591eab
     --mask-missing	Report missing mask paths
Jean Delvare 591eab
     --not-listed	Report unlisted modules
Jean Delvare 591eab
 -v, --verbose		Be verbose
Jean Delvare 591eab
 -o, --output file	Write updated supported.conf to file
Jean Delvare 430664
     --sort		Sort the output file
Jean Delvare 591eab
";
Jean Delvare 591eab
	exit 1;
Jean Delvare 591eab
}
Jean Delvare 591eab
Jean Delvare 591eab
$sup_conf_file = shift @ARGV;
Jean Delvare 591eab
Jean Delvare 591eab
sub gather_path
Jean Delvare 591eab
{
Jean Delvare 591eab
	my $file_or_dir = $_;
Jean Delvare 591eab
	my $module_path = $File::Find::name;
Jean Delvare 591eab
	my ($path, $module);
Jean Delvare 591eab
Jean Delvare 591eab
	# Special case for modules.builtin
Jean Delvare 591eab
	if ($file_or_dir eq "modules.builtin") {
Jean Delvare 591eab
		open(BUILTIN, "<$file_or_dir") or die;
Jean Delvare 591eab
		while (<BUILTIN>) {
deab24
			next unless m,^kernel/(.*)/([^/]+)\.ko(\.xz|\.gz|\.zst)?$,;
Jean Delvare 591eab
			$path = $1;
Jean Delvare 591eab
			$module = $2;
Jean Delvare 591eab
Jean Delvare 591eab
			if (exists $builtin{$module}) {
Jean Delvare 591eab
				next if grep { $_ eq $path } @{$builtin{$module}};
Jean Delvare 591eab
				# There can be duplicates with different paths across architectures
Jean Delvare 591eab
				push @{$builtin{$module}}, $path;
Jean Delvare 591eab
			} else {
Jean Delvare 591eab
				$builtin{$module} = [ $path ];
Jean Delvare 591eab
			}
Jean Delvare 591eab
		}
Jean Delvare 591eab
		close(BUILTIN);
Jean Delvare 591eab
		return;
Jean Delvare 591eab
	}
Jean Delvare 591eab
Jean Delvare 591eab
	return unless $module_path =~ s,^(.*?/)?kernel/,,;
Jean Delvare 591eab
	if (-f $file_or_dir) {
deab24
		return unless $module_path =~ m,^(.*)/([^/]+)\.ko(\.xz|\.gz|\.zst)?$,;
Jean Delvare 591eab
		$path = $1;
Jean Delvare 591eab
		$module = $2;
Jean Delvare 591eab
Jean Delvare 591eab
		if (exists $installed{$module}) {
Jean Delvare 591eab
			return if grep { $_ eq $path } @{$installed{$module}};
Jean Delvare 591eab
			# There can be duplicates with different paths across architectures
Jean Delvare 591eab
			push @{$installed{$module}}, $path;
Jean Delvare 591eab
		} else {
Jean Delvare 591eab
			$installed{$module} = [ $path ];
Jean Delvare 591eab
		}
Jean Delvare 591eab
		$n_ins++;
Jean Delvare 591eab
Jean Delvare 591eab
		$not_listed{"$path/$module"} = 1;
Jean Delvare 591eab
	} elsif (-d $file_or_dir) {
Jean Delvare 591eab
		$installed_paths{$module_path}++;
Jean Delvare 591eab
	}
Jean Delvare 591eab
}
Jean Delvare 591eab
Jean Delvare 591eab
# First, gather all installed modules with their path
Jean Delvare 591eab
$n_ins = 0;
Jean Delvare 591eab
if (@ARGV) {
Jean Delvare 591eab
	find(\&gather_path, @ARGV);
Jean Delvare 591eab
	print "Found $n_ins installed modules\n";
Jean Delvare 591eab
}
Jean Delvare 591eab
Jean Delvare 591eab
sub matching_mask
Jean Delvare 591eab
{
Jean Delvare 591eab
	my $module_path = shift;
Jean Delvare 591eab
Jean Delvare 591eab
	foreach my $mask (@supported_mask) {
Jean Delvare 591eab
		my $prefix = $mask->{module_path};
Jean Delvare 591eab
		$prefix =~ s,/*,,;
Jean Delvare 591eab
		return "$mask->{module_path} at line $mask->{line}"
Jean Delvare 591eab
			if $module_path =~ m,^$prefix,;
Jean Delvare 591eab
	}
Jean Delvare 591eab
}
Jean Delvare 591eab
Jean Delvare 591eab
# Then, parse supported.conf
Jean Delvare 591eab
open(SUP, $sup_conf_file) || die;
Jean Delvare 591eab
while (<SUP>) {
Jean Delvare 591eab
	# Store raw data, needed to generate the updated file
Jean Delvare 591eab
	push @sup_conf_data, $_;
Jean Delvare 591eab
Jean Delvare 591eab
	chomp;
Jean Delvare 591eab
	s/#.*$//;				# Strip comments
Jean Delvare 591eab
	s/\s+$//;				# Strip trailing whitespace
Jean Delvare 591eab
	next if m/^$/;				# Skip blank lines
Michal Kubecek ce984f
	s/^([+-]([\w\d-]+)?\s)*\s*//;		# Strip guards
Jean Delvare 591eab
Jean Delvare 591eab
	my $new_entry = {
Jean Delvare 591eab
		'line' => $.,
Jean Delvare 591eab
		'module_path' => $_,
Jean Delvare 591eab
	};
Jean Delvare 591eab
Jean Delvare 591eab
	# Check for masks that come too early
Jean Delvare 591eab
	my $matching_mask = matching_mask($_);
Jean Delvare 591eab
Jean Delvare 591eab
	if (m/\/\*$/) {
Jean Delvare 591eab
		print "WARNING: Mask $_ at line $. is shadowed by $matching_mask\n"
Jean Delvare 591eab
			if $matching_mask;
Jean Delvare 591eab
		push @supported_mask, $new_entry;
Jean Delvare 591eab
	} else {
Jean Delvare 591eab
		print "WARNING: Module $_ at line $. is shadowed by $matching_mask\n"
Jean Delvare 591eab
			if $matching_mask;
Jean Delvare 591eab
		push @supported_mod, $new_entry;
Jean Delvare 591eab
	}
Jean Delvare 591eab
Jean Delvare 591eab
	# Check for duplicate entries
Jean Delvare 591eab
	if (exists $supported{$_}) {
Jean Delvare 591eab
		print "WARNING: Duplicate entry $_, lines $supported{$_} and $.\n";
Jean Delvare 591eab
	} else {
Jean Delvare 591eab
		$supported{$_} = $.;
Jean Delvare 591eab
	}
Jean Delvare 591eab
}
Jean Delvare 591eab
close(SUP);
Jean Delvare 591eab
print "Gathered ", scalar @supported_mod, " modules and ",
Jean Delvare 591eab
      scalar @supported_mask, " masks from $sup_conf_file\n";
Jean Delvare 591eab
Jean Delvare 591eab
# Amongst all paths containing modules, list the ones which end the same as the parameter
Jean Delvare 591eab
sub candidate_paths
Jean Delvare 591eab
{
Jean Delvare 591eab
	my $path = shift;
Jean Delvare 591eab
	(my $last = $path) =~ s,.*/,,;
Jean Delvare 591eab
Jean Delvare 591eab
	return grep { $_ =~ m,[\s/]$last$, } keys %installed_paths;
Jean Delvare 591eab
}
Jean Delvare 591eab
Jean Delvare 591eab
sub check_path
Jean Delvare 591eab
{
Jean Delvare 591eab
	my $entry = $_;
Jean Delvare 591eab
	my $module_path = $entry->{module_path};
Jean Delvare 591eab
	my $line = $entry->{line};
Jean Delvare 591eab
	my ($path, $module, $ext, $module_alt);
Jean Delvare 591eab
	
Jean Delvare 591eab
	if ($module_path =~ m,^(.*)/\*$,) {
Jean Delvare 591eab
		$path = $1;
Jean Delvare 591eab
		# Mask, check that this directory exists
Jean Delvare 591eab
		if (exists $installed_paths{$path}) {
Jean Delvare 591eab
			# Mark all matching modules as listed
Jean Delvare 591eab
			foreach $module (keys %not_listed) {
Jean Delvare 591eab
				delete $not_listed{$module} if $module =~ m,^$path/,;
Jean Delvare 591eab
			}
Jean Delvare 591eab
		} else {
Jean Delvare 591eab
			# Check if other installed paths end with the same component
Jean Delvare 591eab
			my @candidate = candidate_paths($path);
Jean Delvare 591eab
Jean Delvare 591eab
			if (0 == scalar @candidate) {
Jean Delvare 591eab
				if ($check_mask_missing) {
Jean Delvare 591eab
					print "$sup_conf_file: $line: Referenced path $path doesn't exist, no candidate\n"
Jean Delvare 591eab
						if $verbose;
Jean Delvare 591eab
					$n_err++;
Jean Delvare 591eab
					# Delete that line
Jean Delvare 591eab
					$sup_conf_data[$line - 1] = '';	
Jean Delvare 591eab
					$n_corrected++;
Jean Delvare 591eab
				}
Jean Delvare 591eab
			} elsif (1 == scalar @candidate) {
Jean Delvare 591eab
				if ($check_mask_path) {
Jean Delvare 591eab
					print "$sup_conf_file: $line: Mask path $path is wrong, correct is $candidate[0]\n"
Jean Delvare 591eab
						if $verbose;
Jean Delvare 591eab
					$n_err++;
Jean Delvare 591eab
					# Update the path
Jean Delvare 591eab
					$sup_conf_data[$line - 1] =~ s,(\s)$path/,$1$candidate[0]/,;
Jean Delvare 591eab
					$n_corrected++;
Jean Delvare 591eab
Jean Delvare 591eab
					# Strip trailing whitespace
Jean Delvare 591eab
					$sup_conf_data[$line - 1] =~ s,\s*\n$,\n,;
Jean Delvare 591eab
				}
Jean Delvare 591eab
			} else {
Jean Delvare 591eab
				if ($check_mask_path) {
Jean Delvare 591eab
					$n_err++;
Jean Delvare 591eab
					print "$sup_conf_file: $line: Referenced path $path doesn't exist, too many candidates (",
Jean Delvare 591eab
					      join(", ", @candidate), ")\n"
Jean Delvare 591eab
						if $verbose;
Jean Delvare 591eab
				}
Jean Delvare 591eab
			}
Jean Delvare 591eab
		}
Jean Delvare 591eab
		return;
Jean Delvare 591eab
	}
Jean Delvare 591eab
deab24
	unless ($module_path =~ m,^(.*)/([^/]+)(\.ko)(\.xz|\.gz|\.zst)?$, ||
Jean Delvare 591eab
		$module_path =~ m,^(.*)/([^/]+)$,) {
Jean Delvare 591eab
		print STDERR "$sup_conf_file: $line: Unparsable module path: $module_path\n";
Jean Delvare 591eab
		return;
Jean Delvare 591eab
	}
Jean Delvare 591eab
	$path = $1;
Jean Delvare 591eab
	$module = $2;
Jean Delvare 591eab
	$ext = defined $3 ? $3 : '';
Jean Delvare 591eab
Jean Delvare 591eab
	# Exact name may be different so try all variants
Jean Delvare 591eab
	if (!exists $installed{$module}) {
Jean Delvare 591eab
		($module_alt = $module) =~ tr/-/_/;
Jean Delvare 591eab
		if (exists $installed{$module_alt}) {
Jean Delvare 591eab
			$module = $module_alt;
Jean Delvare 591eab
		} else {
Jean Delvare 591eab
			($module_alt = $module) =~ tr/_/-/;
Jean Delvare 591eab
			if (exists $installed{$module_alt}) {
Jean Delvare 591eab
				$module = $module_alt;
Jean Delvare 591eab
			}
Jean Delvare 591eab
		}
Jean Delvare 591eab
	}
Jean Delvare 591eab
Jean Delvare 591eab
	# Module, check if it exists
Jean Delvare 591eab
	if (!exists $installed{$module}) {
Jean Delvare 591eab
		if ($check_mod_missing) {
Jean Delvare 591eab
			if (exists $builtin{$module}) {
Jean Delvare 591eab
				print "$sup_conf_file: $line: Referenced module $module is built-in\n"
Jean Delvare 591eab
					if $verbose >= 2;
Jean Delvare 591eab
			} else {
Jean Delvare 591eab
				print "$sup_conf_file: $line: Referenced module $module doesn't exist\n"
Jean Delvare 591eab
					if $verbose;
Jean Delvare 591eab
				$n_err++;
Jean Delvare 591eab
				# Delete that line
Jean Delvare 591eab
				$sup_conf_data[$line - 1] = '';
Jean Delvare 591eab
				$n_corrected++;
Jean Delvare 591eab
			}
Jean Delvare 591eab
		}
Jean Delvare 591eab
		if ($check_mod_path) {
Jean Delvare 591eab
			if (exists $builtin{$module} && !grep { $_ eq $path } @{$builtin{$module}}) {
Jean Delvare 591eab
				print "$sup_conf_file: $line: Referenced module $module may be built-in but path doesn't match (",
Jean Delvare 591eab
				      join(" and ", @{$builtin{$module}}),
Jean Delvare 591eab
				      ", supported.conf says $path\n" if $verbose;
Jean Delvare 591eab
			}
Jean Delvare 591eab
		}
Jean Delvare 591eab
	} elsif (!grep { $_ eq $path } @{$installed{$module}}) {
Jean Delvare 591eab
		if ($check_mod_path) {
Jean Delvare 591eab
			print "$sup_conf_file: $line: Path for module $module is wrong: installed at ",
Jean Delvare 591eab
			      join(" and ", @{$installed{$module}}),
Jean Delvare 591eab
			      ", supported.conf says $path\n" if $verbose;
Jean Delvare 591eab
			$n_err++;
Jean Delvare 591eab
Jean Delvare 591eab
			# If there is only one candidate, assume it is right
Jean Delvare 591eab
			if (1 == @{$installed{$module}}) {
Jean Delvare 591eab
				# Update the path
Jean Delvare 591eab
				$sup_conf_data[$line - 1] =~ s,(\s)$path/,$1$installed{$module}->[0]/,;
Jean Delvare 591eab
				$n_corrected++;
Jean Delvare 591eab
Jean Delvare 591eab
				# Attempt to preserve tab-based comment alignment
Jean Delvare 591eab
				if ($sup_conf_data[$line - 1] =~ m,(\t+)#,) {
Jean Delvare 591eab
					my $old_len = length "$path/$module$ext";
Jean Delvare 591eab
					my $new_len = length "$installed{$module}->[0]/$module$ext";
Jean Delvare 591eab
					my $align = (int($old_len / 8) + length $1) * 8;
Jean Delvare 591eab
					my $new_spacing = "\t" x ($align / 8 - int($new_len / 8));
Jean Delvare 591eab
					$sup_conf_data[$line - 1] =~ s,(\t+)#,$new_spacing#,;
Jean Delvare 591eab
				}
Jean Delvare 591eab
				# Strip trailing whitespace
Jean Delvare 591eab
				$sup_conf_data[$line - 1] =~ s,\s*\n$,\n,;
Jean Delvare 591eab
			}
Jean Delvare 591eab
		}
Jean Delvare 591eab
	}
Jean Delvare 591eab
Jean Delvare 591eab
	# Mark as listed
Jean Delvare 591eab
	delete $not_listed{"$path/$module"};
Jean Delvare 591eab
	# Exact name may be different so try all variants
Jean Delvare 591eab
	$module =~ tr/-/_/;
Jean Delvare 591eab
	delete $not_listed{"$path/$module"};
Jean Delvare 591eab
	$module =~ tr/_/-/;
Jean Delvare 591eab
	delete $not_listed{"$path/$module"};
Jean Delvare 591eab
}
Jean Delvare 591eab
Jean Delvare 591eab
# Finally, compare both sets and report and/or correct inconsistencies
Jean Delvare 591eab
$n_err = $n_corrected = 0;
Jean Delvare 591eab
print "\n";
Jean Delvare 591eab
check_path($_) foreach (@supported_mod);
Jean Delvare 591eab
check_path($_) foreach (@supported_mask);
Jean Delvare 591eab
print "\nFound $n_err ", $n_err > 1 ? "errors" : "error", " (corrected $n_corrected)\n"
Jean Delvare 591eab
	if ($check_mod_path || $check_mod_missing || $check_mask_path || $check_mask_missing);
Jean Delvare 591eab
Jean Delvare 591eab
# If requested, print unlisted modules
Jean Delvare 591eab
if ($check_not_listed) {
Jean Delvare 591eab
	print "Modules installed but not listed in supported.conf:\n";
Jean Delvare 591eab
	print "$_\n" foreach (sort keys %not_listed);
Jean Delvare 591eab
}
Jean Delvare 591eab
Jean Delvare 430664
# Module name comparison function for sorting
Jean Delvare 430664
sub modcmp
Jean Delvare 430664
{
Jean Delvare 430664
	my $mod1 = $a;
Jean Delvare 430664
	my $mod2 = $b;
Jean Delvare 430664
Jean Delvare 430664
	$mod1 =~ s/\s*#.*$//mg;				# Strip comments
Jean Delvare 430664
	$mod2 =~ s/\s*#.*$//mg;				# Strip comments
Jean Delvare 430664
	$mod1 =~ s/\n//g;				# Strip empty lines
Jean Delvare 430664
	$mod2 =~ s/\n//g;				# Strip empty lines
Michal Kubecek ce984f
	$mod1 =~ s/^([+-]([\w\d-]+)?\s)*\s*//;		# Strip guards
Michal Kubecek ce984f
	$mod2 =~ s/^([+-]([\w\d-]+)?\s)*\s*//;		# Strip guards
Jean Delvare 430664
Jean Delvare 5e526c
	# Masks must always go after explict module names they match
Jean Delvare 5e526c
	return  1 if $mod1 =~ m/^(.*)\/\*$/ && substr($mod2, 0, length($1)) eq $1;
Jean Delvare 5e526c
	return -1 if $mod2 =~ m/^(.*)\/\*$/ && substr($mod1, 0, length($1)) eq $1;
Jean Delvare 5e526c
Jean Delvare 430664
	return $mod1 cmp $mod2;
Jean Delvare 430664
}
Jean Delvare 430664
Jean Delvare 430664
sub sort_data
Jean Delvare 430664
{
Jean Delvare 430664
	my (@header, @sorted, $n, $comment);
Jean Delvare 430664
Jean Delvare 430664
	# Preserve comments and blank lines at the top of supported.conf
Jean Delvare 430664
	for ($n = 0; $n < @sup_conf_data; $n++) {
Jean Delvare 430664
		last unless $sup_conf_data[$n] =~ m/^\s*(#|$)/;
Jean Delvare 430664
		push @header, $sup_conf_data[$n];
Jean Delvare 430664
	}
Jean Delvare 430664
Jean Delvare 430664
	# Store all the rest in an array, except blank lines
Jean Delvare 430664
	for ($comment = ""; $n < @sup_conf_data; $n++) {
Jean Delvare 430664
		next if $sup_conf_data[$n] =~ m/^\s*$/;
Jean Delvare 430664
Jean Delvare 430664
		# Comments are attached to the module which follows or precedes
Jean Delvare 430664
		# them. We use a heuristic to distinguish between the two
Jean Delvare 430664
		# cases: comments which start before column 24 are attached to
Jean Delvare 430664
		# the module which follows them, while comments which start
Jean Delvare 430664
		# after column 24 are attached to teh module which precedes
Jean Delvare 430664
		# them.
Jean Delvare 430664
		if ($sup_conf_data[$n] =~ m/^(\s*)#/) {
Jean Delvare 430664
			my $leading = $1;
Jean Delvare 430664
			$leading =~ s/ {1,7}\t/\t/g;
Jean Delvare 430664
			$leading =~ s/ {8}/\t/g;
Jean Delvare 430664
			if (length($leading) <= 3) {
Jean Delvare 430664
				$comment .= $sup_conf_data[$n];
Jean Delvare 430664
			} else {
Jean Delvare 430664
				$sorted[@sorted - 1] .= $sup_conf_data[$n];
Jean Delvare 430664
			}
Jean Delvare 430664
			next;
Jean Delvare 430664
		}
Jean Delvare 430664
Jean Delvare 430664
		push @sorted, $comment . $sup_conf_data[$n];
Jean Delvare 430664
		$comment = "";
Jean Delvare 430664
	}
Jean Delvare 430664
Jean Delvare 430664
	# Sort the module list
Jean Delvare 430664
	@sorted = sort modcmp @sorted;
Jean Delvare 430664
Jean Delvare 430664
	# Merge the header and the module list
Jean Delvare 430664
	@sup_conf_data = (@header, @sorted);
Jean Delvare 430664
}
Jean Delvare 430664
Jean Delvare 591eab
# If requested, write a fixed version of supported.conf
Jean Delvare 591eab
if ($output) {
Jean Delvare 430664
	sort_data() if $sort;
Jean Delvare 591eab
	open(OUTPUT, ">$output") || die;
Jean Delvare 591eab
	for (my $n = 0; $n < @sup_conf_data; $n++) {
Jean Delvare 591eab
		# Merge blank lines
Jean Delvare 591eab
		print OUTPUT $sup_conf_data[$n]
Jean Delvare 591eab
			unless $sup_conf_data[$n] eq "\n" && $sup_conf_data[$n - 1] eq "\n";
Jean Delvare 591eab
	}
Jean Delvare 591eab
	close(OUTPUT);
Jean Delvare 591eab
	print "Fixed supported.conf saved as $output\n";
Jean Delvare 591eab
} elsif ($n_corrected) {
Jean Delvare 591eab
	print "Use option -o to write out the corrected file\n";
Jean Delvare 430664
} elsif ($sort) {
Jean Delvare 430664
	print "Use option -o to write out the sorted file\n";
Jean Delvare 591eab
}