In-line run of subs_in_out so that it gets the environment more easily.
[libdcp.git] / tools / dcpverify.cc
1 /*
2     Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
6     libdcp is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     libdcp is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with libdcp.  If not, see <http://www.gnu.org/licenses/>.
18
19     In addition, as a special exception, the copyright holders give
20     permission to link the code of portions of this program with the
21     OpenSSL library under certain conditions as described in each
22     individual source file, and distribute linked combinations
23     including the two.
24
25     You must obey the GNU General Public License in all respects
26     for all of the code used other than OpenSSL.  If you modify
27     file(s) with this exception, you may extend this exception to your
28     version of the file(s), but you are not obligated to do so.  If you
29     do not wish to do so, delete this exception statement from your
30     version.  If you delete this exception statement from all source
31     files in the program, then also delete it here.
32 */
33
34
35 #include "common.h"
36 #include "compose.hpp"
37 #include "filesystem.h"
38 #include "raw_convert.h"
39 #include "verify.h"
40 #include "version.h"
41 #include <boost/bind/bind.hpp>
42 #include <boost/filesystem.hpp>
43 #include <boost/optional.hpp>
44 #include <getopt.h>
45 #include <iostream>
46 #include <cstdlib>
47
48
49 using std::cerr;
50 using std::cout;
51 using std::list;
52 using std::string;
53 using std::vector;
54 using boost::bind;
55 using boost::optional;
56 #if BOOST_VERSION >= 106100
57 using namespace boost::placeholders;
58 #endif
59
60
61 static void
62 help (string n)
63 {
64         cerr << "Syntax: " << n << " [OPTION] <DCP>\n"
65              << "  -V, --version                                show libdcp version\n"
66              << "  -h, --help                                   show this help\n"
67              << "  --ignore-missing-assets                      don't give errors about missing assets\n"
68              << "  --ignore-bv21-smpte                          don't give the SMPTE Bv2.1 error about a DCP not being SMPTE\n"
69              << "  --no-asset-hash-check                        don't check asset hashes\n"
70              << "  --asset-hash-check-maximum-size <size-in-MB> only check hashes for assets smaller than this size (in MB)\n"
71              << "  -q, --quiet                                  don't report progress\n";
72 }
73
74
75 int
76 main (int argc, char* argv[])
77 {
78         dcp::init ();
79
80         bool ignore_missing_assets = false;
81         bool ignore_bv21_smpte = false;
82         bool quiet = false;
83
84         dcp::VerificationOptions verification_options;
85
86         int option_index = 0;
87         while (true) {
88                 static struct option long_options[] = {
89                         { "version", no_argument, 0, 'V' },
90                         { "help", no_argument, 0, 'h' },
91                         { "ignore-missing-assets", no_argument, 0, 'A' },
92                         { "ignore-bv21-smpte", no_argument, 0, 'B' },
93                         { "no-asset-hash-check", no_argument, 0, 'C' },
94                         { "asset-hash-check-maximum-size", required_argument, 0, 'D' },
95                         { "quiet", no_argument, 0, 'q' },
96                         { 0, 0, 0, 0 }
97                 };
98
99                 int c = getopt_long (argc, argv, "VhABCD:q", long_options, &option_index);
100
101                 if (c == -1) {
102                         break;
103                 } else if (c == '?' || c == ':') {
104                         exit(EXIT_FAILURE);
105                 }
106
107                 switch (c) {
108                 case 'V':
109                         cout << "dcpverify version " << dcp::version << "\n";
110                         exit (EXIT_SUCCESS);
111                 case 'h':
112                         help(boost::filesystem::path(argv[0]).filename().string());
113                         exit (EXIT_SUCCESS);
114                 case 'A':
115                         ignore_missing_assets = true;
116                         break;
117                 case 'B':
118                         ignore_bv21_smpte = true;
119                         break;
120                 case 'C':
121                         verification_options.check_asset_hashes = false;
122                         break;
123                 case 'D':
124                         verification_options.maximum_asset_size_for_hash_check = dcp::raw_convert<int>(optarg) * 1000000LL;
125                         break;
126                 case 'q':
127                         quiet = true;
128                         break;
129                 }
130         }
131
132         if (argc <= optind) {
133                 help (argv[0]);
134                 exit (EXIT_FAILURE);
135         }
136
137         if (!dcp::filesystem::exists(argv[optind])) {
138                 cerr << argv[0] << ": DCP " << argv[optind] << " not found.\n";
139                 exit (EXIT_FAILURE);
140         }
141
142         auto stage = [quiet](string s, optional<boost::filesystem::path> path) {
143                 if (quiet) {
144                         return;
145                 }
146
147                 if (path) {
148                         cout << s << ": " << path->string() << "\n";
149                 } else {
150                         cout << s << "\n";
151                 }
152         };
153
154         auto progress = [quiet](float amount) {
155                 if (quiet) {
156                         return;
157                 }
158                 int const width = 60;
159                 int const index = std::rint(amount * width);
160                 cout << "[";
161                 for (int i = 0; i < width; ++i) {
162                         if (i < index) {
163                                 std::cout << "=";
164                         } else if (i == index) {
165                                 std::cout << ">";
166                         } else {
167                                 std::cout << " ";
168                         }
169                 }
170                 cout << "] " << std::rint(amount * 100) << "%\r";
171                 cout.flush();
172         };
173
174         vector<boost::filesystem::path> directories;
175         directories.push_back (argv[optind]);
176         auto notes = dcp::verify(directories, {}, stage, progress, verification_options);
177         dcp::filter_notes (notes, ignore_missing_assets);
178
179         if (!quiet) {
180                 cout << "\n";
181         }
182
183         bool failed = false;
184         bool bv21_failed = false;
185         bool warned = false;
186         for (auto i: notes) {
187                 if (ignore_bv21_smpte && i.code() == dcp::VerificationNote::Code::INVALID_STANDARD) {
188                         continue;
189                 }
190                 switch (i.type()) {
191                 case dcp::VerificationNote::Type::ERROR:
192                         cout << "Error: " << note_to_string(i) << "\n";
193                         failed = true;
194                         break;
195                 case dcp::VerificationNote::Type::BV21_ERROR:
196                         cout << "Bv2.1 error: " << note_to_string(i) << "\n";
197                         bv21_failed = true;
198                         break;
199                 case dcp::VerificationNote::Type::WARNING:
200                         cout << "Warning: " << note_to_string(i) << "\n";
201                         warned = true;
202                         break;
203                 }
204         }
205
206         if (!failed && !quiet) {
207                 if (bv21_failed && warned) {
208                         cout << "\nDCP verified OK (but with Bv2.1 errors and warnings).\n";
209                 } else if (bv21_failed) {
210                         cout << "\nDCP verified OK (but with Bv2.1 errors).\n";
211                 } else if (warned) {
212                         cout << "\nDCP verified OK (but with warnings).\n";
213                 } else {
214                         cout << "DCP verified OK.\n";
215                 }
216         }
217
218         exit (failed ? EXIT_FAILURE : EXIT_SUCCESS);
219 }