Subversion Repositories checksum-tools

Rev

Rev 5 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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