Subversion Repositories checksum-tools

Rev

Rev 5 | 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
/*
15 daniel-mar 5
   Copyright 2020-2022 Daniel Marschall, ViaThinkSoft
2 daniel-mar 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
 
5 daniel-mar 20
// TODO: On Windows file systems, accept file names case insensitively
4 daniel-mar 21
 
5 daniel-mar 22
function utf8_normalize($str) {
23
        // This helps to handle decomposite Unicode endpoints (E.g. German Umlauts have different representations)
24
        // Requires php-intl
25
        if (!class_exists('Normalizer')) return $str;
26
        return Normalizer::normalize($str);
27
}
28
 
29
function convertToUTF8($str) {
30
        $enc = mb_detect_encoding($str);
31
        if ($enc && $enc != 'UTF-8') {
32
                return iconv($enc, 'UTF-8', $str);
33
        } else {
34
                return $str;
35
        }
36
}
37
 
2 daniel-mar 38
function testsfv($file) {
39
        // TODO: warn if an entry is multiple times (with different checksums) in a single file
40
        if (!file_exists($file)) {
15 daniel-mar 41
                fwrite(STDERR, "ERROR: File $file does not exist.\n");
2 daniel-mar 42
                return;
43
        }
44
 
5 daniel-mar 45
        $files_checked = array();
46
 
2 daniel-mar 47
        $lines = file($file);
4 daniel-mar 48
        $is_first_line = true;
49
        $force_utf8 = false;
2 daniel-mar 50
        foreach ($lines as $line) {
4 daniel-mar 51
                if ($is_first_line) {
52
                        $tmp = 0;
53
                        $line = str_replace("\xEF\xBB\xBF",'',$line,$tmp);
54
                        if ($tmp > 0) $force_utf8 = true;
55
                        $is_first_line = false;
56
                }
5 daniel-mar 57
                if (!$force_utf8) $line = convertToUTF8($line);
4 daniel-mar 58
 
5 daniel-mar 59
                if (substr(trim($line),0,1) == ';') continue;
60
 
2 daniel-mar 61
                $line = rtrim($line);
62
                if ($line == '') continue;
63
                $checksum = substr($line,-8);
64
                $origname = rtrim(substr($line,0,strlen($line)-8));
5 daniel-mar 65
                $origname = dirname($file) . '/' . rtrim($origname);
2 daniel-mar 66
                if (!file_exists($origname)) {
15 daniel-mar 67
                        fwrite(STDERR, "WARNING: File vanished : $origname\n");
2 daniel-mar 68
                } else {
5 daniel-mar 69
                        if (is_file($origname)) {
70
                                $checksum2 = crc32_file($origname);
71
                                if (strtolower($checksum) != strtolower($checksum2)) {
15 daniel-mar 72
                                        fwrite(STDERR, "CHECKSUM FAIL: $origname (expected $checksum, but is $checksum2)\n");
5 daniel-mar 73
                                } else {
74
                                        global $show_verbose;
75
                                        if ($show_verbose) echo "OK: $origname\n";
76
                                }
2 daniel-mar 77
                        } else {
5 daniel-mar 78
                                // For some reason, some files on a NTFS volume are "FIFO" pipe files?!
15 daniel-mar 79
                                fwrite(STDERR, "Warning: $origname is not a regular file!\n");
2 daniel-mar 80
                        }
81
                }
5 daniel-mar 82
 
83
                $origname = utf8_normalize(basename($origname));
84
                $files_checked[] = dirname($file) . '/' . $origname;
2 daniel-mar 85
        }
5 daniel-mar 86
 
87
        // Now check if files have vanished!
88
        $directory = dirname($file);
89
        $sd = @scandir($directory);
90
        if ($sd === false) {
15 daniel-mar 91
                fwrite(STDERR, "Error: Cannot scan directory $directory\n");
5 daniel-mar 92
        } else {
93
                foreach ($sd as $file) {
94
                        if ($file === '.') continue;
95
                        if ($file === '..') continue;
15 daniel-mar 96
                        if (substr($file,0,1) === '.') continue;
5 daniel-mar 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)) {
15 daniel-mar 104
                                        fwrite(STDERR, "Warning: File not in SFV checksum file: $fullpath\n");
5 daniel-mar 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)) {
15 daniel-mar 129
                fwrite(STDERR, "Invalid directory path $directory\n");
130
                return false;
2 daniel-mar 131
        }
132
 
133
        if ($dont_add_files = count(glob("$directory/*.sfv")) == 0) {
4 daniel-mar 134
                global $show_verbose;
135
                if ($show_verbose) echo "Directory $directory has no SFV file. Skipping.\n";
2 daniel-mar 136
        } else {
137
                $out = array();
138
 
4 daniel-mar 139
                global $show_verbose;
140
                if ($show_verbose) echo "Check directory $directory\n";
141
                $sfvfiles = glob($directory.'/*.sfv');
2 daniel-mar 142
                foreach ($sfvfiles as $sfvfile) {
143
                        testsfv($sfvfile);
144
                }
145
        }
146
 
4 daniel-mar 147
        $sd = @scandir($directory);
148
        if ($sd === false) {
15 daniel-mar 149
                fwrite(STDERR, "Error: Cannot scan directory $directory\n");
150
                return false;
4 daniel-mar 151
        }
152
 
153
        foreach ($sd as $file) {
2 daniel-mar 154
                if ($file !== '.' && $file !== '..') {
155
                        $file = $directory . '/' . $file;
156
                        if (is_dir($file)) {
157
                                _rec($file);
158
                        }
159
                }
160
        }
15 daniel-mar 161
 
162
        return true;
2 daniel-mar 163
}
164
 
165
 
166
# ---
167
 
4 daniel-mar 168
$show_verbose = false;
15 daniel-mar 169
$dirs = array();
4 daniel-mar 170
 
171
for ($i=1; $i<$argc; $i++) {
172
        if ($argv[$i] == '-v') {
173
                $show_verbose = true;
174
        } else {
15 daniel-mar 175
                $dirs[] = $argv[$i];
4 daniel-mar 176
        }
177
}
178
 
15 daniel-mar 179
if (count($dirs) == 0) {
180
        echo "Syntax: $argv[0] [-v] <directory> [<directory> [...]]\n";
2 daniel-mar 181
        exit(2);
182
}
183
 
15 daniel-mar 184
$res = 0;
185
foreach ($dirs as $dir) {
186
        if (!_rec($dir)) $res = 1;
2 daniel-mar 187
}
15 daniel-mar 188
if ($show_verbose) echo "Done.\n";
189
exit($res);
2 daniel-mar 190