1 // Copyright 2008-present Contributors to the OpenImageIO project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/OpenImageIO/oiio/blob/master/LICENSE.md
4
5
6 #include <cmath>
7 #include <cstdio>
8 #include <cstdlib>
9 #include <ctime>
10 #include <iostream>
11 #include <iterator>
12 #include <memory>
13
14 #include <OpenImageIO/argparse.h>
15 #include <OpenImageIO/filesystem.h>
16 #include <OpenImageIO/imageio.h>
17 #include <OpenImageIO/strutil.h>
18 #include <OpenImageIO/sysutil.h>
19
20 #ifdef USE_BOOST_REGEX
21 # include <boost/regex.hpp>
22 using boost::regex;
23 using boost::regex_search;
24 using namespace boost::regex_constants;
25 #else
26 # include <regex>
27 using std::regex;
28 using std::regex_search;
29 using namespace std::regex_constants;
30 #endif
31
32 using namespace OIIO;
33
34 static bool help = false;
35 static bool invert_match = false;
36 static bool list_files = false;
37 static bool recursive = false;
38 static bool file_match = false;
39 static bool print_dirs = false;
40 static bool all_subimages = false;
41 static std::string pattern;
42 static std::vector<std::string> filenames;
43
44
45
46 static bool
grep_file(const std::string & filename,regex & re,bool ignore_nonimage_files=false)47 grep_file(const std::string& filename, regex& re,
48 bool ignore_nonimage_files = false)
49 {
50 if (!Filesystem::exists(filename)) {
51 std::cerr << "igrep: " << filename << ": No such file or directory\n";
52 return false;
53 }
54
55 if (Filesystem::is_directory(filename)) {
56 if (!recursive)
57 return false;
58 if (print_dirs) {
59 std::cout << "(" << filename << "/)\n";
60 std::cout.flush();
61 }
62 bool r = false;
63 std::vector<std::string> directory_entries;
64 Filesystem::get_directory_entries(filename, directory_entries);
65 for (const auto& d : directory_entries)
66 r |= grep_file(d, re, true);
67 return r;
68 }
69
70 auto in = ImageInput::open(filename);
71 if (!in.get()) {
72 if (!ignore_nonimage_files)
73 std::cerr << geterror() << "\n";
74 return false;
75 }
76 ImageSpec spec = in->spec();
77
78 if (file_match) {
79 bool match = regex_search(filename, re);
80 if (match && !invert_match) {
81 std::cout << filename << "\n";
82 return true;
83 }
84 }
85
86 bool found = false;
87 int subimage = 0;
88 do {
89 if (!all_subimages && subimage > 0)
90 break;
91 for (auto&& p : spec.extra_attribs) {
92 TypeDesc t = p.type();
93 if (t.elementtype() == TypeDesc::STRING) {
94 int n = t.numelements();
95 for (int i = 0; i < n; ++i) {
96 bool match = regex_search(((const char**)p.data())[i], re);
97 found |= match;
98 if (match && !invert_match) {
99 if (list_files) {
100 std::cout << filename << "\n";
101 return found;
102 }
103 std::cout << filename << ": " << p.name() << " = "
104 << ((const char**)p.data())[i] << "\n";
105 }
106 }
107 }
108 }
109 } while (in->seek_subimage(++subimage, 0, spec));
110
111 if (invert_match) {
112 found = !found;
113 if (found)
114 std::cout << filename << "\n";
115 }
116 return found;
117 }
118
119
120
121 static int
parse_files(int argc,const char * argv[])122 parse_files(int argc, const char* argv[])
123 {
124 for (int i = 0; i < argc; i++) {
125 if (pattern.empty())
126 pattern = argv[i];
127 else
128 filenames.emplace_back(argv[i]);
129 }
130 return 0;
131 }
132
133
134
135 int
main(int argc,const char * argv[])136 main(int argc, const char* argv[])
137 {
138 // Helpful for debugging to make sure that any crashes dump a stack
139 // trace.
140 Sysutil::setup_crash_stacktrace("stdout");
141
142 Filesystem::convert_native_arguments(argc, argv);
143 // clang-format off
144 ArgParse ap;
145 ap.intro("igrep -- search images for matching metadata\n"
146 OIIO_INTRO_STRING)
147 .usage("igrep [options] pattern filename...");
148 ap.arg("filename")
149 .hidden()
150 .action(parse_files);
151 ap.arg("-i")
152 .help("Ignore upper/lower case distinctions");
153 ap.arg("-v", &invert_match)
154 .help("Invert match (select non-matching files)");
155 ap.arg("-E")
156 .help( "Pattern is an extended regular expression");
157 ap.arg("-f", &file_match)
158 .help("Match against file name as well as metadata");
159 ap.arg("-l", &list_files)
160 .help("List the matching files (no detail)");
161 ap.arg("-r", &recursive)
162 .help("Recurse into directories");
163 ap.arg("-d", &print_dirs)
164 .help("Print directories (when recursive)");
165 ap.arg("-a", &all_subimages)
166 .help("Search all subimages of each file");
167
168 // clang-format on
169 ap.parse(argc, argv);
170 if (pattern.empty() || filenames.empty()) {
171 std::cerr << ap.geterror() << std::endl;
172 ap.usage();
173 return help ? EXIT_SUCCESS : EXIT_FAILURE;
174 }
175
176 #if USE_BOOST_REGEX
177 boost::regex_constants::syntax_option_type flag
178 = boost::regex_constants::grep;
179 if (ap["E"].get<int>())
180 flag = boost::regex::extended;
181 if (ap["i"].get<int>())
182 flag |= boost::regex_constants::icase;
183 #else
184 auto flag = std::regex_constants::grep;
185 if (ap["E"].get<int>())
186 flag = std::regex_constants::extended;
187 if (ap["i"].get<int>())
188 flag |= std::regex_constants::icase;
189 #endif
190 regex re(pattern, flag);
191 for (auto&& s : filenames)
192 grep_file(s, re);
193
194 return 0;
195 }
196