Subversion Repositories checksum-tools

Rev

Rev 4 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 daniel-mar 1
#!/usr/bin/php
2
<?php
3
 
4
/*
5
   Copyright 2020 Daniel Marschall, ViaThinkSoft
6
 
7
   Licensed under the Apache License, Version 2.0 (the "License");
8
   you may not use this file except in compliance with the License.
9
   You may obtain a copy of the License at
10
 
11
       http://www.apache.org/licenses/LICENSE-2.0
12
 
13
   Unless required by applicable law or agreed to in writing, software
14
   distributed under the License is distributed on an "AS IS" BASIS,
15
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
   See the License for the specific language governing permissions and
17
   limitations under the License.
18
*/
19
 
4 daniel-mar 20
// TODO: make use of STDERR and return different exit codes
5 daniel-mar 21
// TODO: On Windows file systems, accept file names case insensitively
4 daniel-mar 22
 
5 daniel-mar 23
function utf8_normalize($str) {
24
        // This helps to handle decomposite Unicode endpoints (E.g. German Umlauts have different representations)
25
        // Requires php-intl
26
        if (!class_exists('Normalizer')) return $str;
27
        return Normalizer::normalize($str);
28
}
29
 
30
function convertToUTF8($str) {
31
        $enc = mb_detect_encoding($str);
32
        if ($enc && $enc != 'UTF-8') {
33
                return iconv($enc, 'UTF-8', $str);
34
        } else {
35
                return $str;
36
        }
37
}
38
 
2 daniel-mar 39
function testsfv($file) {
40
        // TODO: warn if an entry is multiple times (with different checksums) in a single file
41
        if (!file_exists($file)) {
42
                echo "ERROR: File $file does not exist.\n";
43
                return;
44
        }
45
 
5 daniel-mar 46
        $files_checked = array();
47
 
2 daniel-mar 48
        $lines = file($file);
4 daniel-mar 49
        $is_first_line = true;
50
        $force_utf8 = false;
2 daniel-mar 51
        foreach ($lines as $line) {
4 daniel-mar 52
                if ($is_first_line) {
53
                        $tmp = 0;
54
                        $line = str_replace("\xEF\xBB\xBF",'',$line,$tmp);
55
                        if ($tmp > 0) $force_utf8 = true;
56
                        $is_first_line = false;
57
                }
5 daniel-mar 58
                if (!$force_utf8) $line = convertToUTF8($line);
4 daniel-mar 59
 
5 daniel-mar 60
                if (substr(trim($line),0,1) == ';') continue;
61
 
2 daniel-mar 62
                $line = rtrim($line);
63
                if ($line == '') continue;
64
                $checksum = substr($line,-8);
65
                $origname = rtrim(substr($line,0,strlen($line)-8));
5 daniel-mar 66
                $origname = dirname($file) . '/' . rtrim($origname);
2 daniel-mar 67
                if (!file_exists($origname)) {
68
                        echo "WARNING: File vanished : $origname\n";
69
                } else {
5 daniel-mar 70
                        if (is_file($origname)) {
71
                                $checksum2 = crc32_file($origname);
72
                                if (strtolower($checksum) != strtolower($checksum2)) {
73
                                        echo "CHECKSUM FAIL: $origname (expected $checksum, but is $checksum2)\n";
74
                                } else {
75
                                        global $show_verbose;
76
                                        if ($show_verbose) echo "OK: $origname\n";
77
                                }
2 daniel-mar 78
                        } else {
5 daniel-mar 79
                                // For some reason, some files on a NTFS volume are "FIFO" pipe files?!
80
                                echo "Warning: $origname is not a regular file!\n";
2 daniel-mar 81
                        }
82
                }
5 daniel-mar 83
 
84
                $origname = utf8_normalize(basename($origname));
85
                $files_checked[] = dirname($file) . '/' . $origname;
2 daniel-mar 86
        }
5 daniel-mar 87
 
88
        // Now check if files have vanished!
89
        $directory = dirname($file);
90
        $sd = @scandir($directory);
91
        if ($sd === false) {
92
                echo "Error: Cannot scan directory $directory\n";
93
        } else {
94
                foreach ($sd as $file) {
95
                        if ($file === '.') continue;
96
                        if ($file === '..') continue;
97
                        if (strtolower($file) === 'thumbs.db') continue;
98
                        if (strtolower(substr($file, -4)) === '.md5') continue;
99
                        if (strtolower(substr($file, -4)) === '.sfv') continue;
100
                        $fullpath = $directory . '/' . $file;
101
                        if (!is_dir($fullpath)) {
102
                                $fullpath = utf8_normalize($fullpath);
103
                                if (!in_array($fullpath,$files_checked)) {
104
                                        echo "Warning: File not in SFV checksum file: $fullpath\n";
105
                                }
106
                        }
107
                }
108
        }
2 daniel-mar 109
}
110
 
111
function swapEndianness($hex) {
112
        return implode('', array_reverse(str_split($hex, 2)));
113
}
114
 
115
function crc32_file($filename, $rawOutput = false) {
116
        $out = bin2hex(hash_file ('crc32b', $filename , true));
117
        if (hash('crc32b', 'TEST') == 'b893eaee') {
118
                // hash_file() in PHP 5.2 has the wrong Endianess!
119
                // https://bugs.php.net/bug.php?id=47467
120
                $out = swapEndianness($out);
121
        }
122
        return $out;
123
}
124
 
125
function _rec($directory) {
4 daniel-mar 126
        $directory = rtrim($directory, '/\\');
127
 
2 daniel-mar 128
        if (!is_dir($directory)) {
129
                exit("Invalid directory path $directory\n");
130
        }
131
 
132
        if ($dont_add_files = count(glob("$directory/*.sfv")) == 0) {
4 daniel-mar 133
                global $show_verbose;
134
                if ($show_verbose) echo "Directory $directory has no SFV file. Skipping.\n";
2 daniel-mar 135
        } else {
136
                $out = array();
137
 
4 daniel-mar 138
                global $show_verbose;
139
                if ($show_verbose) echo "Check directory $directory\n";
140
                $sfvfiles = glob($directory.'/*.sfv');
2 daniel-mar 141
                foreach ($sfvfiles as $sfvfile) {
142
                        testsfv($sfvfile);
143
                }
144
        }
145
 
4 daniel-mar 146
        $sd = @scandir($directory);
147
        if ($sd === false) {
148
                echo "Error: Cannot scan directory $directory\n";
149
                return;
150
        }
151
 
152
        foreach ($sd as $file) {
2 daniel-mar 153
                if ($file !== '.' && $file !== '..') {
154
                        $file = $directory . '/' . $file;
155
                        if (is_dir($file)) {
156
                                _rec($file);
157
                        }
158
                }
159
        }
160
}
161
 
162
 
163
# ---
164
 
4 daniel-mar 165
$show_verbose = false;
166
$dir = '';
167
 
168
for ($i=1; $i<$argc; $i++) {
169
        if ($argv[$i] == '-v') {
170
                $show_verbose = true;
171
        } else {
172
                $dir = $argv[$i];
173
        }
174
}
175
 
176
if (empty($dir)) {
177
        echo "Syntax: $argv[0] [-v] <directory>\n";
2 daniel-mar 178
        exit(2);
179
}
180
 
4 daniel-mar 181
if (!is_dir($dir)) {
2 daniel-mar 182
        echo "Directory not found\n";
183
        exit(1);
184
}
185
 
4 daniel-mar 186
_rec($dir);
2 daniel-mar 187
 
4 daniel-mar 188
if ($show_verbose) echo "Done.\n";