~ubuntu-branches/ubuntu/saucy/devscripts/saucy

« back to all changes in this revision

Viewing changes to scripts/licensecheck.pl

  • Committer: Package Import Robot
  • Author(s): Benjamin Drung, Christoph Berg, James McCoy, Dmitry Smirnov, Paul Wise, Benjamin Drung, Cyril Brulebois, Tony Mancill, David Prévot, Josselin Mouette, Raphael Geissert, Regid Ichira, Colin Watson
  • Date: 2013-02-18 21:50:11 UTC
  • mfrom: (10.7.8 squeeze)
  • mto: (10.10.1 sid)
  • mto: This revision was merged to the branch mainline in revision 132.
  • Revision ID: package-import@ubuntu.com-20130218215011-efervwilveqwzzzx
Tags: 2.13.0
[ Christoph Berg ]
* origtargz: New script: fetch the orig tarball of a Debian package from
  various sources, and unpack it
* debcommit: --changelog-info will pass --author and --date for git commits.

[ James McCoy ]
* licensecheck: Recognize MPL 2.0 licenses.  Thanks to Ryan Pavlik for the
  patch.  (Closes: #687664)
* namecheck: Check Apache's projects page for names.  (Closes: #686862)
* debcommit:
  + Drop checks for old dpkg versions and always use the necessary Perl
    modules (Dpkg::Changelog::Parse, Dpkg::Vendor::Ubuntu,
    Dpkg::Changelog::Entry::Debian).
  + Add changelog info support for hg and bzr.
* annotate-output:
  + Handle an incomplete line of output.  (Closes: #695609)
  + Don't treat backslashes in the command's output as an escape.  (Closes:
    #695613)
  + Don't swallow leading whitespace.  (Closes: #695612, LP: #1120917)
* dscverify: Use "gpg --status-fd" to determine if a valid signature is
  found and only use the signed content.  (Closes: #695914)
* wrap-and-sort: Fix repeated word in man page.  (Closes: #696363)

[ Dmitry Smirnov ]
* licensecheck:
  + Remove any regular comments pattern. (Closes: #526698)
  + Improve command line parsing.
  + Fix GPL license version detection bug.
  + Fix BSD-3-clause detection.

[ Paul Wise ]
* checkbashisms: When examining a bash script, indicate the lack of use of
  bashisms.
* uscan:
  + Access GitHub directly instead of using githubredir.debian.net in
    example GitHub watch URL.
  + Add an example watch URL that matches the various file extensions used
    by common archive formats.
  + Add an example watch URL for Google Code projects.

[ Benjamin Drung ]
* Add bashism test cases from Raphael Geissert.
* Add autopkgtest support. (LP: #1073330)
* suspicious-source: Add inode/symlink and image/x-xpmi to whitelisted
  mime-types.
* wrap-and-sort:
  + Put special entries (variables and placeholders) at the end of the list.
  + Sort debian/control*.in files too.
* licensecheck: detect (L)GPL licenses more permissively. Thanks to
  Laurent Rineau for the patch. (Closes: #659231)
* Bump Standards-Version to 3.9.4 (no changes needed).

[ Cyril Brulebois ]
* Don't auto reverse diffs when DEBDIFF_AUTO_VER_SORT is set to yes, and
  when the version in both packages is the same. (Closes: #650732)

[ Tony Mancill ]
* debsnap: Escape the package name when used in regex.  (Closes: #696018)

[ David Prévot ]
* Minor manpages convention fix: do not terminate the SEE ALSO lists with a
  period. (Closes: #696416)
* French translation update.

[ Josselin Mouette ]
* nmudiff: Use dpkg-parsechangelog to fix manual parsing bug (Closes: #700584)

[ Raphael Geissert ]
* checkbashisms:
  + allow -FOO- as heredoc delimiter
  + simplify mixed single/double balanced quotes correctly
  + correct description of 'setvar'
  + detect traps for DEBUG, ERRNO, or RETURN
  + check for incorrect args. to 'exit' (Closes: #687450)
  + fix handling of # characters in quoted strings
  + detect use of $FUNCNAME, $TMOUT, and $TIMEFORMAT
  + detect uses of sleep with anything other than an int.
  + detect the use of the hash utility and $_
  + check for other forms of brace expansion
  + check for use of non-standard tilde expansion
  + check for case modification expansions
  + check for the use of $GLOBIGNORE

[ Regid Ichira ]
* rc-alert: Support using curl as alternative to wget. (Closes: #690024)
* wnpp-alert: Support using curl as alternative to wget. (Closes: #690056)
* wnpp-check: Support using curl as alternative to wget. (Closes: #690059)

[ Colin Watson ]
* debian/control: Mark devscripts Multi-Arch: foreign. (Closes: #694760)
* debchange, debcommit: Set the timestamp of temporary editor files back
  one second, to make modification detection more reliable in the absence
  of subsecond granularity. (Closes: #697923)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/perl -w
 
1
#!/usr/bin/perl
2
2
# This script was originally based on the script of the same name from
3
3
# the KDE SDK (by dfaure@kde.org)
4
4
#
89
89
=item B<--no-conf>, B<--noconf>
90
90
 
91
91
Do not read any configuration files. This can only be used as the first
92
 
option given on the command-line.
 
92
option given on the command line.
93
93
 
94
94
=back
95
95
 
137
137
use Getopt::Long qw(:config gnu_getopt);
138
138
use File::Basename;
139
139
 
140
 
sub fatal($);
141
 
sub parse_copyright($);
142
 
sub parselicense($);
143
 
 
144
140
my $progname = basename($0);
145
141
 
146
142
# From dpkg-source
168
164
 
169
165
my $modified_conf_msg;
170
166
 
171
 
my ($opt_verbose, $opt_lines, $opt_noconf, $opt_ignore_regex, $opt_check_regex)
172
 
  = ('', '', '', '', '');
173
 
my $opt_recursive = 0;
174
 
my $opt_copyright = 0;
175
 
my $opt_machine = 0;
176
 
my ($opt_help, $opt_version);
 
167
my %OPT=(
 
168
    verbose        => '',
 
169
    lines          => '',
 
170
    noconf         => '',
 
171
    ignore         => '',
 
172
    check          => '',
 
173
    recursive      => 0,
 
174
    copyright      => 0,
 
175
    machine        => 0,
 
176
);
 
177
 
177
178
my $def_lines = 60;
178
179
 
179
180
# Read configuration files and then command line
216
217
    $modified_conf_msg ||= "  (none)\n";
217
218
    chomp $modified_conf_msg;
218
219
 
219
 
    $opt_verbose = $config_vars{'LICENSECHECK_VERBOSE'} eq 'yes' ? 1 : 0;
220
 
    $opt_lines = $config_vars{'LICENSECHECK_PARSELINES'};
221
 
}
222
 
 
223
 
GetOptions("help|h" => \$opt_help,
224
 
           "version|v" => \$opt_version,
225
 
           "verbose!" => \$opt_verbose,
226
 
           "lines|l=i" => \$opt_lines,
227
 
           "ignore|i=s" => \$opt_ignore_regex,
228
 
           "recursive|r" => \$opt_recursive,
229
 
           "check|c=s" => \$opt_check_regex,
230
 
           "copyright" => \$opt_copyright,
231
 
           "machine|m" => \$opt_machine,
232
 
           "noconf" => \$opt_noconf,
233
 
           "no-conf" => \$opt_noconf,
234
 
           )
235
 
    or die "Usage: $progname [options] filelist\nRun $progname --help for more details\n";
236
 
 
237
 
$opt_lines = $def_lines if $opt_lines !~ /^[1-9][0-9]*$/;
238
 
$opt_ignore_regex = $default_ignore_regex if ! length $opt_ignore_regex;
239
 
$opt_check_regex = $default_check_regex if ! length $opt_check_regex;
240
 
 
241
 
if ($opt_noconf) {
242
 
    fatal "--no-conf is only acceptable as the first command-line option!";
243
 
}
244
 
if ($opt_help) { help(); exit 0; }
245
 
if ($opt_version) { version(); exit 0; }
 
220
    $OPT{'verbose'} = $config_vars{'LICENSECHECK_VERBOSE'} eq 'yes' ? 1 : 0;
 
221
    $OPT{'lines'} = $config_vars{'LICENSECHECK_PARSELINES'};
 
222
}
 
223
 
 
224
GetOptions(\%OPT,
 
225
           "help|h",
 
226
           "check|c=s",
 
227
           "copyright",
 
228
           "ignore|i=s",
 
229
           "lines|l=i",
 
230
           "machine|m",
 
231
           "noconf|no-conf",
 
232
           "recursive|r",
 
233
           "verbose!",
 
234
           "version|v",
 
235
) or die "Usage: $progname [options] filelist\nRun $progname --help for more details\n";
 
236
 
 
237
$OPT{'lines'} = $def_lines if $OPT{'lines'} !~ /^[1-9][0-9]*$/;
 
238
$OPT{'ignore'} = $default_ignore_regex if ! length $OPT{'ignore'};
 
239
$OPT{'check'} = $default_check_regex if ! length $OPT{'check'};
 
240
 
 
241
if ($OPT{'noconf'}) {
 
242
    fatal("--no-conf is only acceptable as the first command-line option!");
 
243
}
 
244
if ($OPT{'help'}) { help(); exit 0; }
 
245
if ($OPT{'version'}) { version(); exit 0; }
246
246
 
247
247
die "Usage: $progname [options] filelist\nRun $progname --help for more details\n" unless @ARGV;
248
248
 
249
 
$opt_lines = $def_lines if not defined $opt_lines;
 
249
$OPT{'lines'} = $def_lines if not defined $OPT{'lines'};
250
250
 
251
251
my @files = ();
252
252
my @find_args = ();
253
253
my $files_count = @ARGV;
254
254
 
255
 
push @find_args, qw(-maxdepth 1) unless $opt_recursive;
 
255
push @find_args, qw(-maxdepth 1) unless $OPT{'recursive'};
256
256
push @find_args, qw(-follow -type f -print);
257
257
 
258
258
while (@ARGV) {
259
259
    my $file = shift @ARGV;
260
260
 
261
261
    if (-d $file) {
262
 
        open FIND, '-|', 'find', $file, @find_args
 
262
        open my $FIND, '-|', 'find', $file, @find_args
263
263
            or die "$progname: couldn't exec find: $!\n";
264
264
 
265
 
        while (<FIND>) {
 
265
        while (<$FIND>) {
266
266
            chomp;
267
 
            next unless m%$opt_check_regex%;
 
267
            next unless m%$OPT{'check'}%;
268
268
            # Skip empty files
269
269
            next if (-z $_);
270
 
            push @files, $_ unless m%$opt_ignore_regex%;
 
270
            push @files, $_ unless m%$OPT{'ignore'}%;
271
271
        }
272
 
        close FIND;
 
272
        close $FIND;
273
273
    } else {
274
 
        next unless ($files_count == 1) or $file =~ m%$opt_check_regex%;
275
 
        push @files, $file unless $file =~ m%$opt_ignore_regex%;
 
274
        next unless ($files_count == 1) or $file =~ m%$OPT{'check'}%;
 
275
        push @files, $file unless $file =~ m%$OPT{'ignore'}%;
276
276
    }
277
277
}
278
278
 
284
284
    my $license = '';
285
285
    my %copyrights;
286
286
 
287
 
    open (F, "<$file") or die "Unable to access $file\n";
288
 
    while (<F>) {
289
 
        last if ($. > $opt_lines);
 
287
    open (my $F, '<' ,$file) or die "Unable to access $file\n";
 
288
    while (<$F>) {
 
289
        last if ($. > $OPT{'lines'});
290
290
        $content .= $_;
291
291
        $copyright_match = parse_copyright($_);
292
292
        if ($copyright_match) {
293
293
            $copyrights{lc("$copyright_match")} = "$copyright_match";
294
294
        }
295
295
    }
296
 
    close(F);
 
296
    close($F);
297
297
 
298
298
    $copyright = join(" / ", values %copyrights);
299
299
 
300
300
    print qq(----- $file header -----\n$content----- end header -----\n\n)
301
 
        if $opt_verbose;
302
 
 
303
 
    # Remove Fortran comments
304
 
    $content =~ s/^[cC] //gm;
305
 
    $content =~ tr/\t\r\n/ /;
306
 
    # Remove C / C++ comments
307
 
    $content =~ s#(\*/|/[/*])##g;
308
 
    $content =~ tr% A-Za-z.,@;0-9\(\)/-%%cd;
309
 
    $content =~ tr/ //s;
310
 
 
311
 
    $license = parselicense($content);
312
 
    if ($opt_machine) {
 
301
        if $OPT{'verbose'};
 
302
 
 
303
    $license = parselicense(clean_comments($content));
 
304
 
 
305
    if ($OPT{'machine'}) {
313
306
        print "$file\t$license";
314
 
        print "\t" . ($copyright or "*No copyright*") if $opt_copyright;
 
307
        print "\t" . ($copyright or "*No copyright*") if $OPT{'copyright'};
315
308
        print "\n";
316
309
    } else {
317
310
        print "$file: ";
318
311
        print "*No copyright* " unless $copyright;
319
312
        print $license . "\n";
320
313
        print "  [Copyright: " . $copyright . "]\n"
321
 
          if $copyright and $opt_copyright;
322
 
        print "\n" if $opt_copyright;
 
314
          if $copyright and $OPT{'copyright'};
 
315
        print "\n" if $OPT{'copyright'};
323
316
    }
324
317
}
325
318
 
326
 
sub parse_copyright($) {
 
319
sub parse_copyright {
327
320
    my $copyright = '';
328
321
    my $match;
329
322
 
358
351
    return $copyright;
359
352
}
360
353
 
 
354
sub clean_comments {
 
355
    local $_ = shift or return q{};
 
356
 
 
357
    # Remove generic comments: look for 4 or more lines beginning with
 
358
    # regular comment pattern and trim it. Fall back to old algorithm
 
359
    # if no such pattern found.
 
360
    if( 4 <= scalar(()=m{ ^\s*
 
361
                           ([^a-zA-Z0-9\s]{1,3})
 
362
                           \s\w
 
363
                       }xmg)
 
364
    ){
 
365
        my $comment_length=length($1);
 
366
        my $comment_re=qr{\s*  [$1]{${comment_length}}  \s*}x;
 
367
        s/^$comment_re//mg;
 
368
    }
 
369
 
 
370
    # Remove Fortran comments
 
371
    s/^[cC] //gm;
 
372
    tr/\t\r\n/ /;
 
373
 
 
374
    # Remove C / C++ comments
 
375
    s#(\*/|/[/*])##g;
 
376
    tr% A-Za-z.,@;0-9\(\)/-%%cd;
 
377
    tr/ //s;
 
378
 
 
379
    return $_;
 
380
}
 
381
 
361
382
sub help {
362
383
   print <<"EOF";
363
384
Usage: $progname [options] filename [filename ...]
400
421
EOF
401
422
}
402
423
 
403
 
sub parselicense($) {
 
424
sub parselicense {
404
425
    my ($licensetext) = @_;
405
426
 
406
427
    my $gplver = "";
408
429
    my $license = "";
409
430
 
410
431
    if ($licensetext =~ /version ([^, ]+?)[.,]? (?:\(?only\)?.? )?(?:of the GNU (Affero )?(Lesser |Library )?General Public License )?(as )?published by the Free Software Foundation/i or
411
 
        $licensetext =~ /GNU (?:Affero )?(?:Lesser |Library )?General Public License (?:as )?published by the Free Software Foundation; version ([^, ]+?)[.,]? /i) {
 
432
        $licensetext =~ /GNU (?:Affero )?(?:Lesser |Library )?General Public License (?:as )?published by the Free Software Foundation[;,] version ([^, ]+?)[.,]? /i) {
412
433
 
413
434
        $gplver = " (v$1)";
414
435
    } elsif ($licensetext =~ /GNU (?:Affero )?(?:Lesser |Library )?General Public License, version (\d+(?:\.\d+)?)[ \.]/) {
429
450
        $license = "GENERATED FILE";
430
451
    }
431
452
 
432
 
    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) {
 
453
    if ($licensetext =~ /((is free software.? )?you can redistribute it and\/or modify it|is licensed) under the terms of (version [^ ]+ of )?the (GNU (Library |Lesser )General Public License|LGPL)/i) {
433
454
        $license = "LGPL$gplver$extrainfo $license";
434
455
    }
435
456
 
437
458
        $license = "AGPL$gplver$extrainfo $license";
438
459
    }
439
460
 
440
 
    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) {
 
461
    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) {
441
462
        $license = "GPL$gplver$extrainfo $license";
442
463
    }
443
464
 
452
473
 
453
474
    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/) {
454
475
        $license = "QPL (part of Qt) $license";
455
 
    } elsif ($licensetext =~ /may be distributed under the terms of the Q Public License as defined/) {
 
476
    } elsif ($licensetext =~ /may (be distributed|redistribute it) under the terms of the Q Public License/) {
456
477
        $license = "QPL $license";
457
478
    }
458
479
 
471
492
    if ($licensetext =~ /THIS SOFTWARE IS PROVIDED .*AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY/) {
472
493
        if ($licensetext =~ /All advertising materials mentioning features or use of this software must display the following acknowledge?ment.*This product includes software developed by/i) {
473
494
            $license = "BSD (4 clause) $license";
474
 
        } elsif ($licensetext =~ /(The name .*? may not|Neither the names? .*? nor the names of (its|their) contributors may) be used to endorse or promote products derived from this software/i) {
 
495
        } elsif ($licensetext =~ /(The name .*? may not|Neither the names? .*? nor the names of (its|their|other) contributors may) be used to endorse or promote products derived from this software/i) {
475
496
            $license = "BSD (3 clause) $license";
476
497
        } elsif ($licensetext =~ /Redistributions of source code must retain the above copyright notice/i) {
477
498
            $license = "BSD (2 clause) $license";
480
501
        }
481
502
    }
482
503
 
483
 
    if ($licensetext =~ /Mozilla Public License Version ([^ ]+)/) {
484
 
        $license = "MPL (v$1) $license";
 
504
    if ($licensetext =~ /Mozilla Public License,? (Version|v\.) (\d+(?:\.\d+)?)/) {
 
505
        $license = "MPL (v$2) $license";
485
506
    }
486
507
 
487
508
    if ($licensetext =~ /Released under the terms of the Artistic License ([^ ]+)/) {
568
589
    return $license;
569
590
}
570
591
 
571
 
sub fatal($) {
 
592
sub fatal {
572
593
    my ($pack,$file,$line);
573
594
    ($pack,$file,$line) = caller();
574
595
    (my $msg = "$progname: fatal error at line $line:\n@_\n") =~ tr/\0//d;