Home | History | Annotate | Download | only in devscripts
      1 #!/usr/bin/perl -w
      2 # This script was originally based on the script of the same name from
      3 # the KDE SDK (by dfaure (at] kde.org)
      4 #
      5 # This version is
      6 #   Copyright (C) 2007, 2008 Adam D. Barratt
      7 #   Copyright (C) 2012 Francesco Poli
      8 #
      9 # This program is free software; you can redistribute it and/or modify
     10 # it under the terms of the GNU General Public License as published by
     11 # the Free Software Foundation; either version 2 of the License, or
     12 # (at your option) any later version.
     13 #
     14 # This program is distributed in the hope that it will be useful,
     15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 # GNU General Public License for more details.
     18 #
     19 # You should have received a copy of the GNU General Public License along
     20 # with this program. If not, see <http://www.gnu.org/licenses/>.
     21 
     22 =head1 NAME
     23 
     24 licensecheck - simple license checker for source files
     25 
     26 =head1 SYNOPSIS
     27 
     28 B<licensecheck> B<--help>|B<--version>
     29 
     30 B<licensecheck> [B<--no-conf>] [B<--verbose>] [B<--copyright>]
     31 [B<-l>|B<--lines=>I<N>] [B<-i>|B<--ignore=>I<regex>] [B<-c>|B<--check=>I<regex>]
     32 [B<-m>|B<--machine>] [B<-r>|B<--recursive>]
     33 I<list of files and directories to check>
     34 
     35 =head1 DESCRIPTION
     36 
     37 B<licensecheck> attempts to determine the license that applies to each file
     38 passed to it, by searching the start of the file for text belonging to
     39 various licenses.
     40 
     41 If any of the arguments passed are directories, B<licensecheck> will add
     42 the files contained within to the list of files to process.
     43 
     44 =head1 OPTIONS
     45 
     46 =over 4
     47 
     48 =item B<--verbose>, B<--no-verbose>
     49 
     50 Specify whether to output the text being processed from each file before
     51 the corresponding license information.
     52 
     53 Default is to be quiet.
     54 
     55 =item B<-l=>I<N>, B<--lines=>I<N>
     56 
     57 Specify the number of lines of each file's header which should be parsed
     58 for license information. (Default is 60).
     59 
     60 =item B<-i=>I<regex>, B<--ignore=>I<regex>
     61 
     62 When processing the list of files and directories, the regular
     63 expression specified by this option will be used to indicate those which
     64 should not be considered (e.g. backup files, VCS metadata).
     65 
     66 =item B<-r>, B<--recursive>
     67 
     68 Specify that the contents of directories should be added
     69 recursively.
     70 
     71 =item B<-c=>I<regex>, B<--check=>I<regex>
     72 
     73 Specify a pattern against which filenames will be matched in order to
     74 decide which files to check the license of.
     75 
     76 The default includes common source files.
     77 
     78 =item B<--copyright>
     79 
     80 Also display copyright text found within the file
     81 
     82 =item B<-m>, B<--machine>
     83 
     84 Display the information in a machine readable way, i.e. in the form
     85 <file><tab><license>[<tab><copyright>] so that it can be easily sorted
     86 and/or filtered, e.g. with the B<awk> and B<sort> commands.
     87 Note that using the B<--verbose> option will kill the readability.
     88 
     89 =item B<--no-conf>, B<--noconf>
     90 
     91 Do not read any configuration files. This can only be used as the first
     92 option given on the command-line.
     93 
     94 =back
     95 
     96 =head1 CONFIGURATION VARIABLES
     97 
     98 The two configuration files F</etc/devscripts.conf> and
     99 F<~/.devscripts> are sourced by a shell in that order to set
    100 configuration variables.  Command line options can be used to override
    101 configuration file settings.  Environment variable settings are
    102 ignored for this purpose.  The currently recognised variables are:
    103 
    104 =over 4
    105 
    106 =item B<LICENSECHECK_VERBOSE>
    107 
    108 If this is set to I<yes>, then it is the same as the B<--verbose> command
    109 line parameter being used. The default is I<no>.
    110 
    111 =item B<LICENSECHECK_PARSELINES>
    112 
    113 If this is set to a positive number then the specified number of lines
    114 at the start of each file will be read whilst attempting to determine
    115 the license(s) in use.  This is equivalent to the B<--lines> command line
    116 option.
    117 
    118 =back
    119 
    120 =head1 LICENSE
    121 
    122 This code is copyright by Adam D. Barratt <I<adam (at] adam-barratt.org.uk>>,
    123 all rights reserved; based on a script of the same name from the KDE
    124 SDK, which is copyright by <I<dfaure (at] kde.org>>.
    125 This program comes with ABSOLUTELY NO WARRANTY.
    126 You are free to redistribute this code under the terms of the GNU
    127 General Public License, version 2 or later.
    128 
    129 =head1 AUTHOR
    130 
    131 Adam D. Barratt <adam (at] adam-barratt.org.uk>
    132 
    133 =cut
    134 
    135 use strict;
    136 use warnings;
    137 use Getopt::Long qw(:config gnu_getopt);
    138 use File::Basename;
    139 use Tie::File;
    140 use Fcntl 'O_RDONLY';
    141 
    142 sub fatal($);
    143 sub parse_copyright($);
    144 sub parselicense($);
    145 sub remove_comments($);
    146 
    147 my $progname = basename($0);
    148 
    149 # From dpkg-source
    150 my $default_ignore_regex = '
    151 # Ignore general backup files
    152 (?:^|/).*~$|
    153 # Ignore emacs recovery files
    154 (?:^|/)\.#.*$|
    155 # Ignore vi swap files
    156 (?:^|/)\..*\.swp$|
    157 # Ignore baz-style junk files or directories
    158 (?:^|/),,.*(?:$|/.*$)|
    159 # File-names that should be ignored (never directories)
    160 (?:^|/)(?:DEADJOE|\.cvsignore|\.arch-inventory|\.bzrignore|\.gitignore)$|
    161 # File or directory names that should be ignored
    162 (?:^|/)(?:CVS|RCS|\.deps|\{arch\}|\.arch-ids|\.svn|\.hg|_darcs|\.git|
    163 \.shelf|_MTN|\.bzr(?:\.backup|tags)?)(?:$|/.*$)
    164 ';
    165 
    166 # Take out comments and newlines
    167 $default_ignore_regex =~ s/^#.*$//mg;
    168 $default_ignore_regex =~ s/\n//sg;
    169 
    170 my $default_check_regex = '\.(c(c|pp|xx)?|h(h|pp|xx)?|f(77|90)?|p(l|m)|xs|sh|php|py(|x)|rb|java|vala|el|sc(i|e)|cs|pas|inc|dtd|xsl|mod|m|tex|mli?)$';
    171 
    172 my $modified_conf_msg;
    173 
    174 my ($opt_verbose, $opt_lines, $opt_noconf) = ('', '', '');
    175 my $opt_ignore_regex = $default_ignore_regex;
    176 my $opt_check_regex = $default_check_regex;
    177 my $opt_recursive = 0;
    178 my $opt_copyright = 0;
    179 my $opt_machine = 0;
    180 my ($opt_help, $opt_version);
    181 my $def_lines = 60;
    182 
    183 # Read configuration files and then command line
    184 # This is boilerplate
    185 
    186 if (@ARGV and $ARGV[0] =~ /^--no-?conf$/) {
    187     $modified_conf_msg = "  (no configuration files read)";
    188     shift;
    189 } else {
    190     my @config_files = ('/etc/devscripts.conf', '~/.devscripts');
    191     my %config_vars = (
    192 		       'LICENSECHECK_VERBOSE' => 'no',
    193 		       'LICENSECHECK_PARSELINES' => $def_lines,
    194 		      );
    195     my %config_default = %config_vars;
    196 
    197     my $shell_cmd;
    198     # Set defaults
    199     foreach my $var (keys %config_vars) {
    200 	$shell_cmd .= qq[$var="$config_vars{$var}";\n];
    201     }
    202     $shell_cmd .= 'for file in ' . join(" ", @config_files) . "; do\n";
    203     $shell_cmd .= '[ -f $file ] && . $file; done;' . "\n";
    204     # Read back values
    205     foreach my $var (keys %config_vars) { $shell_cmd .= "echo \$$var;\n" }
    206     my $shell_out = `/bin/bash -c '$shell_cmd'`;
    207     @config_vars{keys %config_vars} = split /\n/, $shell_out, -1;
    208 
    209     # Check validity
    210     $config_vars{'LICENSECHECK_VERBOSE'} =~ /^(yes|no)$/
    211 	or $config_vars{'LICENSECHECK_VERBOSE'} = 'no';
    212     $config_vars{'LICENSECHECK_PARSELINES'} =~ /^[1-9][0-9]*$/
    213 	or $config_vars{'LICENSECHECK_PARSELINES'} = $def_lines;
    214 
    215     foreach my $var (sort keys %config_vars) {
    216 	if ($config_vars{$var} ne $config_default{$var}) {
    217 	    $modified_conf_msg .= "  $var=$config_vars{$var}\n";
    218 	}
    219     }
    220     $modified_conf_msg ||= "  (none)\n";
    221     chomp $modified_conf_msg;
    222 
    223     $opt_verbose = $config_vars{'LICENSECHECK_VERBOSE'} eq 'yes' ? 1 : 0;
    224     $opt_lines = $config_vars{'LICENSECHECK_PARSELINES'};
    225 }
    226 
    227 GetOptions("help|h" => \$opt_help,
    228 	   "version|v" => \$opt_version,
    229 	   "verbose!" => \$opt_verbose,
    230 	   "lines|l=i" => \$opt_lines,
    231 	   "ignore|i=s" => \$opt_ignore_regex,
    232 	   "recursive|r" => \$opt_recursive,
    233 	   "check|c=s" => \$opt_check_regex,
    234 	   "copyright" => \$opt_copyright,
    235 	   "machine|m" => \$opt_machine,
    236 	   "noconf" => \$opt_noconf,
    237 	   "no-conf" => \$opt_noconf,
    238 	   )
    239     or die "Usage: $progname [options] filelist\nRun $progname --help for more details\n";
    240 
    241 $opt_lines = $def_lines if $opt_lines !~ /^[1-9][0-9]*$/;
    242 
    243 if ($opt_noconf) {
    244     fatal "--no-conf is only acceptable as the first command-line option!";
    245 }
    246 if ($opt_help) { help(); exit 0; }
    247 if ($opt_version) { version(); exit 0; }
    248 
    249 die "Usage: $progname [options] filelist\nRun $progname --help for more details\n" unless @ARGV;
    250 
    251 $opt_lines = $def_lines if not defined $opt_lines;
    252 
    253 my @files = ();
    254 my @find_args = ();
    255 my $files_count = @ARGV;
    256 
    257 push @find_args, qw(-not ( -path */LayoutTests/* -prune ) );
    258 push @find_args, qw(-not ( -path */out/Debug/* -prune ) );
    259 push @find_args, qw(-not ( -path */out/Release/* -prune ) );
    260 push @find_args, qw(-not ( -path .git* -prune ) );
    261 push @find_args, qw(-not ( -path .svn* -prune ) );
    262 
    263 push @find_args, qw(-maxdepth 1) unless $opt_recursive;
    264 push @find_args, qw(-follow -type f -print);
    265 
    266 while (@ARGV) {
    267     my $file = shift @ARGV;
    268 
    269     if (-d $file) {
    270 	open FIND, '-|', 'find', $file, @find_args
    271 	    or die "$progname: couldn't exec find: $!\n";
    272 
    273 	while (<FIND>) {
    274 	    chomp;
    275 	    next unless m%$opt_check_regex%;
    276 	    # Skip empty files
    277 	    next if (-z $_);
    278 	    push @files, $_ unless m%$opt_ignore_regex%;
    279 	}
    280 	close FIND;
    281     } else {
    282 	next unless ($files_count == 1) or $file =~ m%$opt_check_regex%;
    283 	push @files, $file unless $file =~ m%$opt_ignore_regex%;
    284     }
    285 }
    286 
    287 while (@files) {
    288     my $file = shift @files;
    289     my $header = '';
    290     my $copyright_match;
    291     my $copyright = '';
    292     my $license = '';
    293     my %copyrights;
    294 
    295     open (F, "<$file") or die "Unable to access $file\n";
    296     while (<F>) {
    297         last if ($. > $opt_lines);
    298         $header .= $_;
    299     }
    300     close(F);
    301 
    302     $copyright = join(" / ", values %copyrights);
    303 
    304     print qq(----- $file header -----\n$header----- end header -----\n\n)
    305 	if $opt_verbose;
    306 
    307     remove_comments($header);
    308     $license = parselicense($header);
    309 
    310     # If no license in header, check footer (slow, because read file backwards)
    311     # Need for instance for Perl files, which often use the footer
    312     if ($license eq "UNKNOWN") {
    313         my $footer = '';
    314         tie(my @file_lines, "Tie::File", $file, autochomp => 0, mode => O_RDONLY) or die("Unable to access $file\n");
    315         # Avoid indexing error if header is entire file
    316         if ($#file_lines >= $opt_lines) {
    317             foreach (@file_lines[-$opt_lines .. -1]) {
    318                 $footer .= $_;
    319             }
    320         }
    321         print qq(----- $file footer -----\n$header----- end footer -----\n\n)
    322             if $opt_verbose;
    323         remove_comments($footer);
    324         $license = parselicense($footer);
    325     }
    326 
    327     if ($opt_machine) {
    328 	print "$file\t$license";
    329 	print "\t" . ($copyright or "*No copyright*") if $opt_copyright;
    330 	print "\n";
    331     } else {
    332 	print "$file: ";
    333 	print "*No copyright* " unless $copyright;
    334 	print $license . "\n";
    335 	print "  [Copyright: " . $copyright . "]\n"
    336 	  if $copyright and $opt_copyright;
    337 	print "\n" if $opt_copyright;
    338     }
    339 }
    340 
    341 sub remove_comments($) {
    342     $_ = $_[0];
    343     # Remove Fortran comments
    344     s/^[cC] //gm;
    345     # Remove .ASM comments
    346     s#^;\*?##gm;
    347     # Remove .S comments
    348     s#^@ ##gm;
    349     # Remove new lines
    350     tr/\t\r\n/ /;
    351     # Remove C / C++ comments
    352     s#(\*/|/[/*])##g;
    353     # Remove all characters not matching search
    354     tr% A-Za-z.,@;0-9\(\)/-%%cd;
    355     # Collapse multiple spaces into single space
    356     tr/ //s;
    357     $_[0] = $_;
    358 }
    359 
    360 sub parse_copyright($) {
    361     my $copyright = '';
    362     my $match;
    363 
    364     my $copyright_indicator_regex = '
    365 	(?:copyright	# The full word
    366 	|copr\.		# Legally-valid abbreviation
    367 	|\x{00a9}	# Unicode character COPYRIGHT SIGN
    368 	|\xc2\xa9	# Unicode copyright sign encoded in iso8859
    369 	|\(c\)		# Legally-null representation of sign
    370 	)';
    371     my $copyright_disindicator_regex = '
    372 	\b(?:info(?:rmation)?	# Discussing copyright information
    373 	|notice			# Discussing the notice
    374 	|and|or                 # Part of a sentence
    375 	)\b';
    376 
    377     if (m%$copyright_indicator_regex(?::\s*|\s+)(\S.*)$%ix) {
    378 	$match = $1;
    379 
    380 	# Ignore lines matching "see foo for copyright information" etc.
    381 	if ($match !~ m%^\s*$copyright_disindicator_regex%ix) {
    382 	    # De-cruft
    383 	    $match =~ s/([,.])?\s*$//;
    384 	    $match =~ s/$copyright_indicator_regex//igx;
    385 	    $match =~ s/^\s+//;
    386 	    $match =~ s/\s{2,}/ /g;
    387 	    $match =~ s/\\@/@/g;
    388 	    $copyright = $match;
    389 	}
    390     }
    391 
    392     return $copyright;
    393 }
    394 
    395 sub help {
    396    print <<"EOF";
    397 Usage: $progname [options] filename [filename ...]
    398 Valid options are:
    399    --help, -h             Display this message
    400    --version, -v          Display version and copyright info
    401    --no-conf, --noconf    Don't read devscripts config files; must be
    402                           the first option given
    403    --verbose              Display the header of each file before its
    404                             license information
    405    --lines, -l            Specify how many lines of the file header
    406                             should be parsed for license information
    407                             (Default: $def_lines)
    408    --check, -c            Specify a pattern indicating which files should
    409                              be checked
    410                              (Default: '$default_check_regex')
    411    --machine, -m          Display in a machine readable way (good for awk)
    412    --recursive, -r        Add the contents of directories recursively
    413    --copyright            Also display the file's copyright
    414    --ignore, -i           Specify that files / directories matching the
    415                             regular expression should be ignored when
    416                             checking files
    417                             (Default: '$default_ignore_regex')
    418 
    419 Default settings modified by devscripts configuration files:
    420 $modified_conf_msg
    421 EOF
    422 }
    423 
    424 sub version {
    425     print <<"EOF";
    426 This is $progname, from the Debian devscripts package, version ###VERSION###
    427 Copyright (C) 2007, 2008 by Adam D. Barratt <adam\@adam-barratt.org.uk>; based
    428 on a script of the same name from the KDE SDK by <dfaure\@kde.org>.
    429 
    430 This program comes with ABSOLUTELY NO WARRANTY.
    431 You are free to redistribute this code under the terms of the
    432 GNU General Public License, version 2, or (at your option) any
    433 later version.
    434 EOF
    435 }
    436 
    437 sub parselicense($) {
    438     my ($licensetext) = @_;
    439 
    440     my $gplver = "";
    441     my $lgplver = "";
    442     my $extrainfo = "";
    443     my $license = "";
    444 
    445     if ($licensetext =~ /version ([^, ]+?)[.,]? (?:\(?only\)?.? )?(?:of the GNU (Affero )?General Public License )?(as )?published by the Free Software Foundation/i or
    446 	$licensetext =~ /GNU (?:Affero )?General Public License (?:as )?published by the Free Software Foundation; version ([^, ]+?)[.,]? /i or
    447 	$licensetext =~ /GNU (?:Affero )?General Public License,? [Vv]ersion (\d+(?:\.\d+)?)[ \.]/) {
    448 	$gplver = " (v$1)";
    449     } elsif ($licensetext =~ /either version ([^ ]+)(?: of the License)?, or \(at your option\) any later version/) {
    450 	$gplver = " (v$1 or later)";
    451     }
    452 
    453     if ($licensetext =~ /version ([^, ]+?)[.,]? (?:or later|or any later version) (?:of the GNU (?:Lesser |Library )General Public License )(as )?published by the Free Software Foundation/i or
    454 	$licensetext =~ /(?:GNU (?:Lesser |Library )|(?:Lesser|Library) GNU )General Public License (?:(?:as )?published by the Free Software Foundation;)?,? (?:either )?[Vv]ersion ([^, ]+?)(?: of the license)?[.,]? (?:or later|or (?:\(at your option\) )?any later version)/i or
    455 	$licensetext =~ /GNU (?:Lesser |Library )General Public License(?: \(LGPL\))?,? [Vv]ersion (\d+(?:\.\d+)?)[ \.]/) {
    456 	$lgplver = " (v$1 or later)";
    457     }
    458 
    459     if ($licensetext =~ /permission (?:is (also granted|given))? to link (the code of )?this program with (any edition of )?(Qt|the Qt library)/i) {
    460 	$extrainfo = " (with Qt exception)$extrainfo"
    461     }
    462 
    463     if ($licensetext =~ /(All changes made in this file will be lost|DO NOT (EDIT|delete this file)|Generated (automatically|by|from)|generated.*file)/i) {
    464 	$license = "GENERATED FILE";
    465     }
    466 
    467     if ($licensetext =~ /is (free software.? you can redistribute it and\/or modify it|licensed) under the terms of (version [^ ]+ of )?the (GNU (Library |Lesser )General Public License|LGPL)/i or
    468         $licensetext =~ /(is distributed|may be used|can redistribute).*terms.*(LGPL|(Lesser|Library) GNU General Public License)/) {
    469         if ($lgplver) {
    470 	    $license = "LGPL$lgplver$extrainfo $license";
    471         } else {
    472 	    $license = "LGPL (unversioned/unknown version) $license";
    473         }
    474     }
    475 
    476     if ($licensetext =~ /is free software.? you (can|may) redistribute it and\/or modify it under the terms of (?:version [^ ]+ (?:\(?only\)? )?of )?the GNU General Public License/i) {
    477 	$license = "GPL$gplver$extrainfo $license";
    478     } elsif ($licensetext =~ /is distributed under the terms of the GNU General Public License,/
    479 	and $gplver) {
    480 	$license = "GPL$gplver$extrainfo $license";
    481     } elsif ($licensetext =~ /is distributed.*terms.*[^L]GPL/) {
    482         if ($gplver) {
    483 	    $license = "GPL$gplver$extrainfo $license";
    484         } else {
    485 	    $license = "GPL (unversioned/unknown version) $license";
    486         }
    487     }
    488 
    489     if ($licensetext =~ /This file is part of the .*Qt GUI Toolkit. This file may be distributed under the terms of the Q Public License as defined/) {
    490 	$license = "QPL (part of Qt) $license";
    491     } elsif ($licensetext =~ /may be distributed under the terms of the Q Public License as defined/) {
    492 	$license = "QPL $license";
    493     }
    494 
    495     if ($licensetext =~ /opensource\.org\/licenses\/mit/) {
    496 	$license = "MIT/X11 (BSD like) $license";
    497     } elsif ($licensetext =~ /Permission is hereby granted, free of charge, to any person obtaining a copy of this software and(\/or)? associated documentation files \(the (Software|Materials)\), to deal in the (Software|Materials)/) {
    498 	$license = "MIT/X11 (BSD like) $license";
    499     } elsif ($licensetext =~ /Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this software and its documentation for any purpose/) {
    500 	$license = "MIT/X11 (BSD like) $license";
    501     } elsif ($licensetext =~ /Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee/) {
    502 	$license = "MIT/X11 (BSD like) $license";
    503     } elsif ($licensetext  =~ /MIT .* License/) {
    504 	$license = "MIT/X11 (BSD like) $license";
    505     }
    506 
    507     if ($licensetext  =~ /This file is part of the Independent JPEG Group(')?s software.*For conditions of distribution and use, see the accompanying README file/i) {
    508 	$license = "Independent JPEG Group License $license";
    509     }
    510 
    511     if ($licensetext  =~ /the University of Illinois Open Source License/){
    512 	$license = "University of Illinois/NCSA Open Source License (BSD like) $license";
    513     }
    514 
    515     if ($licensetext  =~ /Permission to use, copy, modify, and(\/or)? distribute this software (and its documentation )?for any purpose (with or )?without fee is hereby granted, provided.*(copyright|entire) notice.*all copies/i) {
    516 	$license = "ISC $license";
    517     }
    518 
    519     if ($licensetext =~ /THIS SOFTWARE IS PROVIDED .*AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY/ ||
    520         $licensetext =~ /THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- ITY/) {
    521 	if ($licensetext =~ /All advertising materials mentioning features or use of this software must display the following/) {
    522 	    $license = "BSD (4 clause) $license";
    523 	} elsif ($licensetext =~ /be used to endorse or promote products derived from this software/) {
    524 	    $license = "BSD (3 clause) $license";
    525 	} elsif ($licensetext =~ /Redistributions of source code must retain the above copyright notice/) {
    526 	    $license = "BSD (2 clause) $license";
    527 	} else {
    528 	    $license = "BSD $license";
    529 	}
    530     } elsif ($licensetext =~ /Use of this source code is governed by a BSD-style license/) {
    531         $license = "BSD-like $license";
    532     } elsif ($licensetext =~ /BSD terms apply/) {
    533         $license = "BSD-like $license";
    534     } elsif ($licensetext =~ /subject to the BSD License/) {
    535         # TODO(sbc): remove this case once we fix: http://crbug.com/177268
    536         $license = "BSD-like $license";
    537     } elsif ($licensetext =~ /license BSD/) {
    538         $license = "BSD-like $license";
    539     } elsif ($licensetext =~ /GOVERNED BY A BSD-STYLE SOURCE LICENSE/) {
    540         $license = "BSD-like $license";
    541     } elsif ($licensetext =~ /BSD 3-Clause license/) {
    542         $license = "BSD (3 clause) $license";
    543     }
    544 
    545     if ($licensetext =~ /Mozilla Public License( Version|, v.) ([^ ]+[^., ]),?/) {
    546 	$license = "MPL (v$2) $license";
    547     }
    548 
    549     if ($licensetext =~ /Released under the terms of the Artistic License ([^ ]+)/) {
    550 	$license = "Artistic (v$1) $license";
    551     }
    552 
    553     if ($licensetext =~ /is free software under the Artistic [Ll]icense/) {
    554 	$license = "Artistic $license";
    555     }
    556 
    557     if ($licensetext =~ /This (program|library) is free software; you can redistribute it and\/or modify it under the same terms as Perl itself/) {
    558 	$license = "Perl $license";
    559     }
    560 
    561     if ($licensetext =~ /under the terms of the Apache ([^ ]+) License OR version 2 of the GNU/) {
    562 	$license = "Apache (v$1) GPL (v2) $license";
    563     } elsif ($licensetext =~ /under the Apache License, Version ([^ ]+)/) {
    564 	$license = "Apache (v$1) $license";
    565     }
    566 
    567     if ($licensetext =~ /(THE BEER-WARE LICENSE)/i) {
    568 	$license = "Beerware $license";
    569     }
    570 
    571     if ($licensetext =~ /This source file is subject to version ([^ ]+) of the PHP license/) {
    572 	$license = "PHP (v$1) $license";
    573     }
    574 
    575     if ($licensetext =~ /under the terms of the CeCILL /) {
    576 	$license = "CeCILL $license";
    577     }
    578 
    579     if ($licensetext =~ /under the terms of the CeCILL-([^ ]+) /) {
    580 	$license = "CeCILL-$1 $license";
    581     }
    582 
    583     if ($licensetext =~ /under the SGI Free Software (B License|License B)/) {
    584 	$license = "SGI Free Software License B $license";
    585     }
    586 
    587     if ($licensetext =~ /(in|into) the public domain/i) {
    588 	$license = "Public domain $license";
    589     }
    590 
    591     if ($licensetext =~ /terms of the Common Development and Distribution License(, Version ([^(]+))? \(the License\)/) {
    592 	$license = "CDDL " . ($1 ? "(v$2) " : '') . $license;
    593     }
    594 
    595     if ($licensetext =~ /Microsoft Permissive License \(Ms-PL\)/) {
    596         $license = "Ms-PL $license";
    597     }
    598 
    599     if ($licensetext =~ /as defined in and that are subject to the Apple Public Source License([ ,-]+Version ([^ ]+)?(\.))/) {
    600 	$license = "APSL " . ($1 ? "(v$2) " : '') . $license;
    601     } elsif ($licensetext =~ /provided that if you redistribute the Apple Software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the Apple Software/) {
    602 	# https://fedoraproject.org/wiki/Licensing/Apple_MIT_License
    603 	$license = "Apple MIT $license";
    604     }
    605 
    606     if ($licensetext =~ /Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license \([\"]?the Software[\"]?\)/ or
    607 	$licensetext =~ /Boost Software License([ ,-]+Version ([^ ]+)?(\.))/i) {
    608 	$license = "BSL " . ($1 ? "(v$2) " : '') . $license;
    609     }
    610 
    611     if ($licensetext =~ /PYTHON SOFTWARE FOUNDATION LICENSE (VERSION ([^ ]+))/i) {
    612 	$license = "PSF " . ($1 ? "(v$2) " : '') . $license;
    613     }
    614 
    615     if ($licensetext =~ /The origin of this software must not be misrepresented.*Altered source versions must be plainly marked as such.*This notice may not be removed or altered from any source distribution/ or
    616         $licensetext =~ /see copyright notice in zlib\.h/) {
    617 	$license = "zlib/libpng $license";
    618     } elsif ($licensetext =~ /This code is released under the libpng license/) {
    619         $license = "libpng $license";
    620     }
    621 
    622     if ($licensetext =~ /under MIT license/) {
    623         $license = "MIT/X11 (BSD like) $license";
    624     }
    625 
    626     if ($licensetext =~ /License MIT(-| )License/) {
    627         $license = "MIT/X11 (BSD like) $license";
    628     }
    629 
    630     if ($licensetext =~ /As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice/) {
    631         $license = $license . "with Bison parser exception";
    632     }
    633 
    634     if ($licensetext =~ /As a special exception to the GNU General Public License, if you distribute this file as part of a program or library that is built using GNU Libtool, you may include this file under the same distribution terms that you use for the rest of that program/) {
    635         $license = $license . "with libtool exception";
    636     }
    637 
    638     if ($licensetext =~ /These materials are protected by copyright laws and contain material proprietary to the Khronos Group, Inc\. You may use these materials for implementing Khronos specifications, without altering or removing any trademark, copyright or other notice from the specification/) {
    639         $license = $license . "Khronos Group";
    640     }
    641 
    642     if ($licensetext =~ /This file is part of the FreeType project, and may only be used(,)? modified(,)? and distributed under the terms of the FreeType project license, LICENSE\.TXT\. By continuing to use, modify, or distribute this file you indicate that you have read the license and understand and accept it fully/) {
    643         $license = "FreeType (BSD like) $license";
    644     }
    645     if ($licensetext =~ /This software, and all works of authorship, whether in source or object code form as indicated by the copyright notice.*is made available, and may only be used, modified, and distributed under the FreeType Project License, LICENSE\.TXT\. Additionally, subject to the terms and conditions of the FreeType Project License, each contributor to the Work hereby grants to any individual or legal entity exercising permissions granted by the FreeType Project License and this section.*a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable.*patent license to make/) {
    646         $license = "FreeType (BSD like) with patent clause $license";
    647     }
    648 
    649     if ($licensetext =~ /Anti-Grain Geometry.*Permission to copy, use, modify, sell and distribute this software is granted provided this copyright notice appears in all copies. This software is provided as is without express or impl/) {
    650         $license = "Anti-Grain Geometry $license";
    651     }
    652 
    653     if ($licensetext =~ /Developed at SunSoft, a Sun Microsystems, Inc\. business\. Permission to use, copy, modify, and distribute this software is freely granted, provided that this notice is preserved\./) {
    654         $license = "SunSoft (BSD like) $license";
    655     }
    656 
    657     $license = "UNKNOWN" unless $license;
    658 
    659     # Remove trailing spaces.
    660     $license =~ s/\s+$//;
    661 
    662     return $license;
    663 }
    664 
    665 sub fatal($) {
    666     my ($pack,$file,$line);
    667     ($pack,$file,$line) = caller();
    668     (my $msg = "$progname: fatal error at line $line:\n@_\n") =~ tr/\0//d;
    669     $msg =~ s/\n\n$/\n/;
    670     die $msg;
    671 }
    672