• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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
24licensecheck - simple license checker for source files
25
26=head1 SYNOPSIS
27
28B<licensecheck> B<--help>|B<--version>
29
30B<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>]
33I<list of files and directories to check>
34
35=head1 DESCRIPTION
36
37B<licensecheck> attempts to determine the license that applies to each file
38passed to it, by searching the start of the file for text belonging to
39various licenses.
40
41If any of the arguments passed are directories, B<licensecheck> will add
42the 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
50Specify whether to output the text being processed from each file before
51the corresponding license information.
52
53Default is to be quiet.
54
55=item B<-l=>I<N>, B<--lines=>I<N>
56
57Specify the number of lines of each file's header which should be parsed
58for license information. (Default is 60).
59
60=item B<-i=>I<regex>, B<--ignore=>I<regex>
61
62When processing the list of files and directories, the regular
63expression specified by this option will be used to indicate those which
64should not be considered (e.g. backup files, VCS metadata).
65
66=item B<-r>, B<--recursive>
67
68Specify that the contents of directories should be added
69recursively.
70
71=item B<-c=>I<regex>, B<--check=>I<regex>
72
73Specify a pattern against which filenames will be matched in order to
74decide which files to check the license of.
75
76The default includes common source files.
77
78=item B<--copyright>
79
80Also display copyright text found within the file
81
82=item B<-m>, B<--machine>
83
84Display 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
86and/or filtered, e.g. with the B<awk> and B<sort> commands.
87Note that using the B<--verbose> option will kill the readability.
88
89=item B<--no-conf>, B<--noconf>
90
91Do not read any configuration files. This can only be used as the first
92option given on the command-line.
93
94=back
95
96=head1 CONFIGURATION VARIABLES
97
98The two configuration files F</etc/devscripts.conf> and
99F<~/.devscripts> are sourced by a shell in that order to set
100configuration variables.  Command line options can be used to override
101configuration file settings.  Environment variable settings are
102ignored for this purpose.  The currently recognised variables are:
103
104=over 4
105
106=item B<LICENSECHECK_VERBOSE>
107
108If this is set to I<yes>, then it is the same as the B<--verbose> command
109line parameter being used. The default is I<no>.
110
111=item B<LICENSECHECK_PARSELINES>
112
113If this is set to a positive number then the specified number of lines
114at the start of each file will be read whilst attempting to determine
115the license(s) in use.  This is equivalent to the B<--lines> command line
116option.
117
118=back
119
120=head1 LICENSE
121
122This code is copyright by Adam D. Barratt <I<adam@adam-barratt.org.uk>>,
123all rights reserved; based on a script of the same name from the KDE
124SDK, which is copyright by <I<dfaure@kde.org>>.
125This program comes with ABSOLUTELY NO WARRANTY.
126You are free to redistribute this code under the terms of the GNU
127General Public License, version 2 or later.
128
129=head1 AUTHOR
130
131Adam D. Barratt <adam@adam-barratt.org.uk>
132
133=cut
134
135use strict;
136use warnings;
137use Getopt::Long qw(:config gnu_getopt);
138use File::Basename;
139use Tie::File;
140use Fcntl 'O_RDONLY';
141
142sub fatal($);
143sub parse_copyright($);
144sub parselicense($);
145sub remove_comments($);
146
147my $progname = basename($0);
148
149# From dpkg-source
150my $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
170my $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
172my $modified_conf_msg;
173
174my ($opt_verbose, $opt_lines, $opt_noconf) = ('', '', '');
175my $opt_ignore_regex = $default_ignore_regex;
176my $opt_check_regex = $default_check_regex;
177my $opt_recursive = 0;
178my $opt_copyright = 0;
179my $opt_machine = 0;
180my ($opt_help, $opt_version);
181my $def_lines = 60;
182
183# Read configuration files and then command line
184# This is boilerplate
185
186if (@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
227GetOptions("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
243if ($opt_noconf) {
244    fatal "--no-conf is only acceptable as the first command-line option!";
245}
246if ($opt_help) { help(); exit 0; }
247if ($opt_version) { version(); exit 0; }
248
249die "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
253my @files = ();
254my @find_args = ();
255my $files_count = @ARGV;
256
257push @find_args, qw(-not ( -path */LayoutTests/* -prune ) );
258push @find_args, qw(-not ( -path */out/Debug/* -prune ) );
259push @find_args, qw(-not ( -path */out/Release/* -prune ) );
260push @find_args, qw(-not ( -path .git* -prune ) );
261push @find_args, qw(-not ( -path .svn* -prune ) );
262
263push @find_args, qw(-maxdepth 1) unless $opt_recursive;
264push @find_args, qw(-follow -type f -print);
265
266while (@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
287while (@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
341sub 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
360sub 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
395sub help {
396   print <<"EOF";
397Usage: $progname [options] filename [filename ...]
398Valid 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
419Default settings modified by devscripts configuration files:
420$modified_conf_msg
421EOF
422}
423
424sub version {
425    print <<"EOF";
426This is $progname, from the Debian devscripts package, version ###VERSION###
427Copyright (C) 2007, 2008 by Adam D. Barratt <adam\@adam-barratt.org.uk>; based
428on a script of the same name from the KDE SDK by <dfaure\@kde.org>.
429
430This program comes with ABSOLUTELY NO WARRANTY.
431You are free to redistribute this code under the terms of the
432GNU General Public License, version 2, or (at your option) any
433later version.
434EOF
435}
436
437sub 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
665sub 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