Subversion Repositories checksum-tools

Rev

Rev 4 | Rev 14 | 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 testmd5($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
 
46
        $lines = file($file);
4 daniel-mar 47
        $is_first_line = true;
48
        $force_utf8 = false;
2 daniel-mar 49
        foreach ($lines as $line) {
4 daniel-mar 50
                if ($is_first_line) {
51
                        $tmp = 0;
52
                        $line = str_replace("\xEF\xBB\xBF",'',$line,$tmp);
53
                        if ($tmp > 0) $force_utf8 = true;
54
                        $is_first_line = false;
55
                }
5 daniel-mar 56
                if (!$force_utf8) $line = convertToUTF8($line);
4 daniel-mar 57
 
5 daniel-mar 58
                if (substr(trim($line),0,1) == ';') continue;
59
 
2 daniel-mar 60
                $line = trim($line);
61
                if ($line == '') continue;
62
                $line = str_replace('*', ' ', $line);
63
                $line = str_replace("\t", ' ', $line);
64
                list($checksum, $origname) = explode(' ', $line, 2);
65
                $origname = dirname($file) . '/' . trim($origname);
66
                $checksum = trim($checksum);
67
                if (!file_exists($origname)) {
68
                        echo "WARNING: File vanished : $origname\n";
69
                } else {
5 daniel-mar 70
                        if (is_file($origname)) {
71
                                $checksum2 = md5_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 _rec($directory) {
4 daniel-mar 112
        $directory = rtrim($directory, '/\\');
113
 
2 daniel-mar 114
        if (!is_dir($directory)) {
115
                exit("Invalid directory path $directory\n");
116
        }
117
 
118
        if ($dont_add_files = count(glob("$directory/*.md5")) == 0) {
4 daniel-mar 119
                global $show_verbose;
120
                if ($show_verbose) echo "Directory $directory has no MD5 file. Skipping.\n";
2 daniel-mar 121
        } else {
122
                $out = array();
123
 
4 daniel-mar 124
                global $show_verbose;
125
                if ($show_verbose) echo "Check directory $directory\n";
126
                $md5files = glob($directory.'/*.md5');
2 daniel-mar 127
                foreach ($md5files as $md5file) {
128
                        testmd5($md5file);
129
                }
130
        }
131
 
4 daniel-mar 132
        $sd = @scandir($directory);
133
        if ($sd === false) {
134
                echo "Error: Cannot scan directory $directory\n";
135
                return;
136
        }
137
 
138
        foreach ($sd as $file) {
2 daniel-mar 139
                if ($file !== '.' && $file !== '..') {
140
                        $file = $directory . '/' . $file;
141
                        if (is_dir($file)) {
142
                                _rec($file);
143
                        }
144
                }
145
        }
146
}
147
 
148
 
149
# ---
150
 
4 daniel-mar 151
$show_verbose = false;
152
$dir = '';
153
 
154
for ($i=1; $i<$argc; $i++) {
155
        if ($argv[$i] == '-v') {
156
                $show_verbose = true;
157
        } else {
158
                $dir = $argv[$i];
159
        }
160
}
161
 
162
if (empty($dir)) {
163
        echo "Syntax: $argv[0] [-v] <directory>\n";
2 daniel-mar 164
        exit(2);
165
}
166
 
4 daniel-mar 167
if (!is_dir($dir)) {
2 daniel-mar 168
        echo "Directory not found\n";
169
        exit(1);
170
}
171
 
4 daniel-mar 172
_rec($dir);
2 daniel-mar 173
 
4 daniel-mar 174
if ($show_verbose) echo "Done.\n";