Subversion Repositories checksum-tools

Rev

Rev 14 | 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 testmd5($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
 
14 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 = trim($line);
62
                if ($line == '') continue;
63
                $line = str_replace('*', ' ', $line);
64
                $line = str_replace("\t", ' ', $line);
65
                list($checksum, $origname) = explode(' ', $line, 2);
66
                $origname = dirname($file) . '/' . trim($origname);
67
                $checksum = trim($checksum);
68
                if (!file_exists($origname)) {
15 daniel-mar 69
                        fwrite(STDERR, "WARNING: File vanished : $origname\n");
2 daniel-mar 70
                } else {
5 daniel-mar 71
                        if (is_file($origname)) {
72
                                $checksum2 = md5_file($origname);
73
                                if (strtolower($checksum) != strtolower($checksum2)) {
15 daniel-mar 74
                                        fwrite(STDERR, "CHECKSUM FAIL: $origname (expected $checksum, but is $checksum2)\n");
5 daniel-mar 75
                                } else {
76
                                        global $show_verbose;
77
                                        if ($show_verbose) echo "OK: $origname\n";
78
                                }
2 daniel-mar 79
                        } else {
5 daniel-mar 80
                                // For some reason, some files on a NTFS volume are "FIFO" pipe files?!
15 daniel-mar 81
                                fwrite(STDERR, "Warning: $origname is not a regular file!\n");
2 daniel-mar 82
                        }
83
                }
5 daniel-mar 84
 
85
                $origname = utf8_normalize(basename($origname));
86
                $files_checked[] = dirname($file) . '/' . $origname;
2 daniel-mar 87
        }
5 daniel-mar 88
 
89
        // Now check if files have vanished!
90
        $directory = dirname($file);
91
        $sd = @scandir($directory);
92
        if ($sd === false) {
15 daniel-mar 93
                fwrite(STDERR, "Error: Cannot scan directory $directory\n");
5 daniel-mar 94
        } else {
95
                foreach ($sd as $file) {
96
                        if ($file === '.') continue;
97
                        if ($file === '..') continue;
15 daniel-mar 98
                        if (substr($file,0,1) === '.') continue;
5 daniel-mar 99
                        if (strtolower($file) === 'thumbs.db') continue;
100
                        if (strtolower(substr($file, -4)) === '.md5') continue;
101
                        if (strtolower(substr($file, -4)) === '.sfv') continue;
102
                        $fullpath = $directory . '/' . $file;
103
                        if (!is_dir($fullpath)) {
104
                                $fullpath = utf8_normalize($fullpath);
105
                                if (!in_array($fullpath,$files_checked)) {
15 daniel-mar 106
                                        fwrite(STDERR, "Warning: File not in SFV checksum file: $fullpath\n");
5 daniel-mar 107
                                }
108
                        }
109
                }
110
        }
2 daniel-mar 111
}
112
 
113
function _rec($directory) {
4 daniel-mar 114
        $directory = rtrim($directory, '/\\');
115
 
2 daniel-mar 116
        if (!is_dir($directory)) {
15 daniel-mar 117
                fwrite(STDERR, "Invalid directory path $directory\n");
118
                return false;
2 daniel-mar 119
        }
120
 
121
        if ($dont_add_files = count(glob("$directory/*.md5")) == 0) {
4 daniel-mar 122
                global $show_verbose;
123
                if ($show_verbose) echo "Directory $directory has no MD5 file. Skipping.\n";
2 daniel-mar 124
        } else {
125
                $out = array();
126
 
4 daniel-mar 127
                global $show_verbose;
128
                if ($show_verbose) echo "Check directory $directory\n";
129
                $md5files = glob($directory.'/*.md5');
2 daniel-mar 130
                foreach ($md5files as $md5file) {
131
                        testmd5($md5file);
132
                }
133
        }
134
 
4 daniel-mar 135
        $sd = @scandir($directory);
136
        if ($sd === false) {
15 daniel-mar 137
                fwrite(STDERR, "Error: Cannot scan directory $directory\n");
138
                return false;
4 daniel-mar 139
        }
140
 
141
        foreach ($sd as $file) {
2 daniel-mar 142
                if ($file !== '.' && $file !== '..') {
143
                        $file = $directory . '/' . $file;
144
                        if (is_dir($file)) {
145
                                _rec($file);
146
                        }
147
                }
148
        }
15 daniel-mar 149
 
150
        return true;
2 daniel-mar 151
}
152
 
153
 
154
# ---
155
 
4 daniel-mar 156
$show_verbose = false;
15 daniel-mar 157
$dirs = array();
4 daniel-mar 158
 
159
for ($i=1; $i<$argc; $i++) {
160
        if ($argv[$i] == '-v') {
161
                $show_verbose = true;
162
        } else {
15 daniel-mar 163
                $dirs[] = $argv[$i];
4 daniel-mar 164
        }
165
}
166
 
15 daniel-mar 167
if (count($dirs) == 0) {
168
        echo "Syntax: $argv[0] [-v] <directory> [<directory> [...]]\n";
2 daniel-mar 169
        exit(2);
170
}
171
 
15 daniel-mar 172
$res = 0;
173
foreach ($dirs as $dir) {
174
        if (!_rec($dir)) $res = 1;
2 daniel-mar 175
}
15 daniel-mar 176
if ($show_verbose) echo "Done.\n";
177
exit($res);
2 daniel-mar 178