• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/perl
2#***************************************************************************
3#                                  _   _ ____  _
4#  Project                     ___| | | |  _ \| |
5#                             / __| | | | |_) | |
6#                            | (__| |_| |  _ <| |___
7#                             \___|\___/|_| \_\_____|
8#
9# Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
10#
11# This software is licensed as described in the file COPYING, which
12# you should have received as part of this distribution. The terms
13# are also available at https://curl.se/docs/copyright.html.
14#
15# You may opt to use, copy, modify, merge, publish, distribute and/or sell
16# copies of the Software, and permit persons to whom the Software is
17# furnished to do so, under the terms of the COPYING file.
18#
19# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20# KIND, either express or implied.
21#
22###########################################################################
23#
24# Invoke script in the root of the git checkout. Scans all files in git unless
25# given a specific single file.
26#
27# Usage: copyright.pl [file]
28#
29
30# regexes of files to not scan
31my @skiplist=(
32    '^tests\/data\/test(\d+)$', # test case data
33    '^docs\/cmdline-opts\/[a-z]+(.*)\.d$', # curl.1 pieces
34    '(\/|^)[A-Z0-9_.-]+$', # all uppercase file name, possibly with dot and dash
35    '(\/|^)[A-Z0-9_-]+\.md$', # all uppercase file name with .md extension
36    '.gitignore', # wherever they are
37    '.gitattributes', # wherever they are
38    '^tests/certs/.*', # generated certs
39    '^tests/stunnel.pem', # generated cert
40    '^tests/valgrind.supp', # valgrind suppressions
41    '^projects/Windows/.*.dsw$', # generated MSVC file
42    '^projects/Windows/.*.sln$', # generated MSVC file
43    '^projects/Windows/.*.tmpl$', # generated MSVC file
44    '^projects/Windows/.*.vcxproj.filters$', # generated MSVC file
45    '^m4/ax_compile_check_sizeof.m4$', # imported, leave be
46    '^.mailmap', # git control file
47    '\/readme',
48    '^.github/', # github instruction files
49    '^.dcignore', # deepcode.ai instruction file
50    '^.muse/', # muse-CI control files
51    "buildconf", # its nothing to copyright
52
53    # docs/ files we're okay with without copyright
54    'INSTALL.cmake',
55    'TheArtOfHttpScripting',
56    'page-footer',
57    'curl_multi_socket_all.3',
58    'curl_strnequal.3',
59    'symbols-in-versions',
60    'options-in-versions',
61
62    # macos-framework files
63    '^lib\/libcurl.plist',
64    '^lib\/libcurl.vers.in',
65
66    # vms files
67    '^packages\/vms\/build_vms.com',
68    '^packages\/vms\/curl_release_note_start.txt',
69    '^packages\/vms\/curlmsg.sdl',
70    '^packages\/vms\/macro32_exactcase.patch',
71
72    # XML junk
73    '^projects\/wolfssl_override.props',
74
75    # macos framework generated files
76    '^src\/macos\/curl.mcp.xml.sit.hqx',
77    '^src\/macos\/src\/curl_GUSIConfig.cpp',
78
79    # checksrc control files
80    '\.checksrc$',
81
82    # an empty control file
83    "^zuul.d/playbooks/.zuul.ignore",
84
85    );
86
87sub scanfile {
88    my ($f) = @_;
89    my $line=1;
90    my $found = 0;
91    open(F, "<$f") ||
92        print ERROR "can't open $f\n";
93    while (<F>) {
94        chomp;
95        my $l = $_;
96        # check for a copyright statement and save the years
97        if($l =~ /.* +copyright .* *\d\d\d\d/i) {
98            while($l =~ /([\d]{4})/g) {
99                push @copyright, {
100                  year => $1,
101                  line => $line,
102                  col => index($l, $1),
103                  code => $l
104                };
105                $found++;
106            }
107        }
108        # allow within the first 100 lines
109        if(++$line > 100) {
110            last;
111        }
112    }
113    close(F);
114    return $found;
115}
116
117sub checkfile {
118    my ($file) = @_;
119    my $fine = 0;
120    @copyright=();
121    my $found = scanfile($file);
122
123    if(!$found) {
124        print "$file:1: missing copyright range\n";
125        return 2;
126    }
127
128    my $commityear = undef;
129    @copyright = sort {$$b{year} cmp $$a{year}} @copyright;
130
131    # if the file is modified, assume commit year this year
132    if(`git status -s -- $file` =~ /^ [MARCU]/) {
133        $commityear = (localtime(time))[5] + 1900;
134    }
135    else {
136        # min-parents=1 to ignore wrong initial commit in truncated repos
137        my $grl = `git rev-list --max-count=1 --min-parents=1 --timestamp HEAD -- $file`;
138        if($grl) {
139            chomp $grl;
140            $commityear = (localtime((split(/ /, $grl))[0]))[5] + 1900;
141        }
142    }
143
144    if(defined($commityear) && scalar(@copyright) &&
145       $copyright[0]{year} != $commityear) {
146        printf "$file:%d: copyright year out of date, should be $commityear, " .
147            "is $copyright[0]{year}\n",
148            $copyright[0]{line};
149    }
150    else {
151        $fine = 1;
152    }
153    return $fine;
154}
155
156my @all;
157if($ARGV[0]) {
158    push @all, $ARGV[0];
159}
160else {
161    @all = `git ls-files`;
162}
163for my $f (@all) {
164    chomp $f;
165    my $skipped = 0;
166    for my $skip (@skiplist) {
167        #print "$f matches $skip ?\n";
168        if($f =~ /$skip/) {
169            $skiplisted++;
170            $skipped = 1;
171            #print "$f: SKIPPED ($skip)\n";
172            last;
173        }
174    }
175    if(!$skipped) {
176        my $r = checkfile($f);
177        $missing++ if($r == 2);
178        $wrong++ if(!$r);
179    }
180}
181
182print STDERR "$missing files have no copyright\n" if($missing);
183print STDERR "$wrong files have wrong copyright year\n" if ($wrong);
184print STDERR "$skiplisted files are skipped\n" if ($skiplisted);
185
186exit 1 if($missing || $wrong);
187