Subversion Repositories checksum-tools

Rev

Rev 4 | Rev 14 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 4 Rev 5
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 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
20
// TODO: make use of STDERR and return different exit codes
-
 
21
// TODO: On Windows file systems, accept file names case insensitively
-
 
22
 
-
 
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
}
21
 
38
 
22
function testmd5($file) {
39
function testmd5($file) {
23
        // TODO: warn if an entry is multiple times (with different checksums) in a single file
40
        // TODO: warn if an entry is multiple times (with different checksums) in a single file
24
        if (!file_exists($file)) {
41
        if (!file_exists($file)) {
25
                echo "ERROR: File $file does not exist.\n";
42
                echo "ERROR: File $file does not exist.\n";
26
                return;
43
                return;
27
        }
44
        }
28
 
45
 
29
        $lines = file($file);
46
        $lines = file($file);
30
        $is_first_line = true;
47
        $is_first_line = true;
31
        $force_utf8 = false;
48
        $force_utf8 = false;
32
        foreach ($lines as $line) {
49
        foreach ($lines as $line) {
33
                if ($is_first_line) {
50
                if ($is_first_line) {
34
                        $tmp = 0;
51
                        $tmp = 0;
35
                        $line = str_replace("\xEF\xBB\xBF",'',$line,$tmp);
52
                        $line = str_replace("\xEF\xBB\xBF",'',$line,$tmp);
36
                        if ($tmp > 0) $force_utf8 = true;
53
                        if ($tmp > 0) $force_utf8 = true;
37
                        $is_first_line = false;
54
                        $is_first_line = false;
38
                }
55
                }
39
                $is_ansi = strstr(utf8_decode($line),'?') !== false; // Attention: This assumes that '?' is not part of the line!
56
                if (!$force_utf8) $line = convertToUTF8($line);
-
 
57
 
40
                if (!$force_utf8 && $is_ansi) $line = utf8_encode($line);
58
                if (substr(trim($line),0,1) == ';') continue;
41
 
59
 
42
                $line = trim($line);
60
                $line = trim($line);
43
                if ($line == '') continue;
61
                if ($line == '') continue;
44
                $line = str_replace('*', ' ', $line);
62
                $line = str_replace('*', ' ', $line);
45
                $line = str_replace("\t", ' ', $line);
63
                $line = str_replace("\t", ' ', $line);
46
                list($checksum, $origname) = explode(' ', $line, 2);
64
                list($checksum, $origname) = explode(' ', $line, 2);
47
                $origname = dirname($file) . '/' . trim($origname);
65
                $origname = dirname($file) . '/' . trim($origname);
48
                $checksum = trim($checksum);
66
                $checksum = trim($checksum);
49
                if (!file_exists($origname)) {
67
                if (!file_exists($origname)) {
50
                        echo "WARNING: File vanished : $origname\n";
68
                        echo "WARNING: File vanished : $origname\n";
51
                } else {
69
                } else {
-
 
70
                        if (is_file($origname)) {
52
                        $checksum2 = md5_file($origname);
71
                                $checksum2 = md5_file($origname);
53
                        if (strtolower($checksum) != strtolower($checksum2)) {
72
                                if (strtolower($checksum) != strtolower($checksum2)) {
54
                                echo "CHECKSUM FAIL: $origname (expected $checksum, but is $checksum2)\n";
73
                                        echo "CHECKSUM FAIL: $origname (expected $checksum, but is $checksum2)\n";
55
                        } else {
74
                                } else {
56
                                global $show_verbose;
75
                                        global $show_verbose;
57
                                if ($show_verbose) echo "OK: $origname\n";
76
                                        if ($show_verbose) echo "OK: $origname\n";
58
                        }
77
                                }
-
 
78
                        } else {
-
 
79
                                // For some reason, some files on a NTFS volume are "FIFO" pipe files?!
-
 
80
                                echo "Warning: $origname is not a regular file!\n";
-
 
81
                        }
-
 
82
                }
-
 
83
 
-
 
84
                $origname = utf8_normalize(basename($origname));
-
 
85
                $files_checked[] = dirname($file) . '/' . $origname;
-
 
86
        }
-
 
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
                        }
59
                }
107
                }
60
                // TODO: Also warn about extra files which are not indexed
-
 
61
        }
108
        }
62
}
109
}
63
 
110
 
64
function _rec($directory) {
111
function _rec($directory) {
65
        $directory = rtrim($directory, '/\\');
112
        $directory = rtrim($directory, '/\\');
66
 
113
 
67
        if (!is_dir($directory)) {
114
        if (!is_dir($directory)) {
68
                exit("Invalid directory path $directory\n");
115
                exit("Invalid directory path $directory\n");
69
        }
116
        }
70
 
117
 
71
        if ($dont_add_files = count(glob("$directory/*.md5")) == 0) {
118
        if ($dont_add_files = count(glob("$directory/*.md5")) == 0) {
72
                global $show_verbose;
119
                global $show_verbose;
73
                if ($show_verbose) echo "Directory $directory has no MD5 file. Skipping.\n";
120
                if ($show_verbose) echo "Directory $directory has no MD5 file. Skipping.\n";
74
        } else {
121
        } else {
75
                $out = array();
122
                $out = array();
76
 
123
 
77
                global $show_verbose;
124
                global $show_verbose;
78
                if ($show_verbose) echo "Check directory $directory\n";
125
                if ($show_verbose) echo "Check directory $directory\n";
79
                $md5files = glob($directory.'/*.md5');
126
                $md5files = glob($directory.'/*.md5');
80
                foreach ($md5files as $md5file) {
127
                foreach ($md5files as $md5file) {
81
                        testmd5($md5file);
128
                        testmd5($md5file);
82
                }
129
                }
83
        }
130
        }
84
 
131
 
85
        $sd = @scandir($directory);
132
        $sd = @scandir($directory);
86
        if ($sd === false) {
133
        if ($sd === false) {
87
                echo "Error: Cannot scan directory $directory\n";
134
                echo "Error: Cannot scan directory $directory\n";
88
                return;
135
                return;
89
        }
136
        }
90
 
137
 
91
        foreach ($sd as $file) {
138
        foreach ($sd as $file) {
92
                if ($file !== '.' && $file !== '..') {
139
                if ($file !== '.' && $file !== '..') {
93
                        $file = $directory . '/' . $file;
140
                        $file = $directory . '/' . $file;
94
                        if (is_dir($file)) {
141
                        if (is_dir($file)) {
95
                                _rec($file);
142
                                _rec($file);
96
                        }
143
                        }
97
                }
144
                }
98
        }
145
        }
99
}
146
}
100
 
147
 
101
 
148
 
102
# ---
149
# ---
103
 
150
 
104
$show_verbose = false;
151
$show_verbose = false;
105
$dir = '';
152
$dir = '';
106
 
153
 
107
for ($i=1; $i<$argc; $i++) {
154
for ($i=1; $i<$argc; $i++) {
108
        if ($argv[$i] == '-v') {
155
        if ($argv[$i] == '-v') {
109
                $show_verbose = true;
156
                $show_verbose = true;
110
        } else {
157
        } else {
111
                $dir = $argv[$i];
158
                $dir = $argv[$i];
112
        }
159
        }
113
}
160
}
114
 
161
 
115
if (empty($dir)) {
162
if (empty($dir)) {
116
        echo "Syntax: $argv[0] [-v] <directory>\n";
163
        echo "Syntax: $argv[0] [-v] <directory>\n";
117
        exit(2);
164
        exit(2);
118
}
165
}
119
 
166
 
120
if (!is_dir($dir)) {
167
if (!is_dir($dir)) {
121
        echo "Directory not found\n";
168
        echo "Directory not found\n";
122
        exit(1);
169
        exit(1);
123
}
170
}
124
 
171
 
125
_rec($dir);
172
_rec($dir);
126
 
173
 
127
if ($show_verbose) echo "Done.\n";
174
if ($show_verbose) echo "Done.\n";
128
 
175