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