Line data Source code
1 : /* TLD tools -- TLD, domain name, and sub-domain extraction
2 : * Copyright (c) 2011-2022 Made to Order Software Corp. All Rights Reserved
3 : *
4 : * Permission is hereby granted, free of charge, to any person obtaining a
5 : * copy of this software and associated documentation files (the
6 : * "Software"), to deal in the Software without restriction, including
7 : * without limitation the rights to use, copy, modify, merge, publish,
8 : * distribute, sublicense, and/or sell copies of the Software, and to
9 : * permit persons to whom the Software is furnished to do so, subject to
10 : * the following conditions:
11 : *
12 : * The above copyright notice and this permission notice shall be included
13 : * in all copies or substantial portions of the Software.
14 : *
15 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 : * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 : * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 : * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 : * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 : * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 : */
23 :
24 : // libtld lib
25 : //
26 : #include <libtld/tld_compiler.h>
27 : #include <libtld/tld_file.h>
28 :
29 :
30 : // C++ lib
31 : //
32 : #include <fstream>
33 : #include <iostream>
34 : #include <sstream>
35 :
36 :
37 : // C lib
38 : //
39 : #include <string.h>
40 :
41 :
42 2 : class compiler
43 : {
44 : public:
45 : std::ostream & error();
46 : int exit_code() const;
47 : void set_input_path(std::string const & path);
48 : void set_output(std::string const & output);
49 : void set_c_file(std::string const & c);
50 : void set_verify(bool verify);
51 : void set_output_json(bool verify);
52 : void set_include_offsets(bool include_offsets);
53 : void set_verbose(bool verbose);
54 :
55 : void run();
56 :
57 : private:
58 : void verify_output(tld_compiler & c);
59 :
60 : int f_errcnt = 0;
61 : std::string f_input_path = std::string();
62 : std::string f_output = std::string();
63 : std::string f_c_file = std::string();
64 : bool f_verify = false;
65 : bool f_output_json = false;
66 : bool f_include_offsets = false;
67 : bool f_verbose = false;
68 : };
69 :
70 :
71 0 : std::ostream & compiler::error()
72 : {
73 0 : ++f_errcnt;
74 0 : return std::cerr;
75 : }
76 :
77 :
78 1 : int compiler::exit_code() const
79 : {
80 1 : return f_errcnt == 0 ? 0 : 1;
81 : }
82 :
83 :
84 1 : void compiler::set_input_path(std::string const & path)
85 : {
86 1 : f_input_path = path;
87 1 : }
88 :
89 :
90 1 : void compiler::set_output(std::string const & output)
91 : {
92 1 : f_output = output;
93 1 : }
94 :
95 :
96 1 : void compiler::set_c_file(std::string const & c_file)
97 : {
98 1 : f_c_file = c_file;
99 1 : }
100 :
101 :
102 1 : void compiler::set_verify(bool verify)
103 : {
104 1 : f_verify = verify;
105 1 : }
106 :
107 :
108 1 : void compiler::set_output_json(bool output_json)
109 : {
110 1 : f_output_json = output_json;
111 1 : }
112 :
113 :
114 1 : void compiler::set_include_offsets(bool include_offsets)
115 : {
116 1 : f_include_offsets = include_offsets;
117 1 : }
118 :
119 :
120 0 : void compiler::set_verbose(bool verbose)
121 : {
122 0 : f_verbose = verbose;
123 0 : }
124 :
125 :
126 1 : void compiler::run()
127 : {
128 1 : if(f_errcnt != 0)
129 : {
130 : // command line found errors, return immediately
131 0 : return;
132 : }
133 :
134 1 : if(f_input_path.empty())
135 : {
136 0 : ++f_errcnt;
137 0 : std::cerr << "error: an input path is required.\n";
138 0 : return;
139 : }
140 :
141 1 : if(f_output.empty())
142 : {
143 0 : ++f_errcnt;
144 0 : std::cerr << "error: an output filename is required.\n";
145 0 : return;
146 : }
147 :
148 1 : std::cout << "Compiling TLDs from \"" << f_input_path << "\"..." << std::endl;
149 :
150 2 : tld_compiler c;
151 1 : c.set_input_folder(f_input_path);
152 1 : c.set_output(f_output);
153 1 : c.set_c_file(f_c_file);
154 1 : if(!c.compile())
155 : {
156 0 : ++f_errcnt;
157 : std::cerr
158 : << "error:"
159 0 : << c.get_filename()
160 0 : << ":"
161 : << c.get_line()
162 : << ": "
163 0 : << c.get_errmsg()
164 0 : << " (errno: "
165 : << c.get_errno()
166 : << ", "
167 0 : << strerror(c.get_errno())
168 0 : << ")\n";
169 0 : return;
170 : }
171 :
172 1 : std::cout << "Number of strings: " << c.get_string_manager().size() << "\n";
173 1 : std::cout << "Longest string: " << c.get_string_manager().max_length() << "\n";
174 1 : std::cout << "Total string length: " << c.get_string_manager().total_length() << "\n";
175 1 : std::cout << "Included strings: " << c.get_string_manager().included_count() << " (saved length: " << c.get_string_manager().included_length() << ")\n";
176 1 : std::cout << "Mergeable strings: " << c.get_string_manager().merged_count() << " (saved length: " << c.get_string_manager().merged_length() << ")\n";
177 1 : std::cout << "Compressed string length: " << c.get_string_manager().compressed_length() << std::endl;
178 : // TODO: add info about tags
179 :
180 1 : if(f_output_json)
181 : {
182 2 : std::string filename;
183 1 : std::string::size_type const dot(f_output.rfind('.'));
184 1 : if(dot != std::string::npos
185 1 : && dot > 0
186 2 : && f_output[dot - 1] != '/')
187 : {
188 1 : filename = f_output.substr(0, dot) + ".json";
189 : }
190 : else
191 : {
192 0 : filename = f_output + ".json";
193 : }
194 2 : std::ofstream out;
195 1 : out.open(filename);
196 1 : if(out.is_open())
197 : {
198 1 : c.output_to_json(out, f_include_offsets);
199 : }
200 : else
201 : {
202 0 : ++f_errcnt;
203 : std::cerr
204 : << "error: could not open JSON output file: \""
205 : << filename
206 0 : << "\".\n";
207 0 : return;
208 : }
209 : }
210 :
211 1 : if(f_verify)
212 : {
213 1 : verify_output(c);
214 : }
215 : }
216 :
217 :
218 1 : void compiler::verify_output(tld_compiler & c)
219 : {
220 1 : tld_file * file(nullptr);
221 2 : auto_free_tld_file auto_free(&file);
222 1 : tld_file_error err(tld_file_load(f_output.c_str(), &file));
223 1 : if(err != TLD_FILE_ERROR_NONE)
224 : {
225 0 : ++f_errcnt;
226 : std::cerr << "error: could not load output file \""
227 : << f_output
228 : << "\" -- err: "
229 : << tld_file_errstr(err)
230 0 : << " ("
231 : << static_cast<int>(err)
232 0 : << ").\n";
233 0 : return;
234 : }
235 :
236 : // generate a JSON from what was just loaded
237 : // and it has to match the compiler's JSON
238 : //
239 1 : char * json(tld_file_to_json(file));
240 1 : if(json == nullptr)
241 : {
242 0 : ++f_errcnt;
243 0 : std::cerr << "error: conversion of file to JSON failed.\n";
244 0 : return;
245 : }
246 2 : auto_free_string auto_delete(json);
247 :
248 : // save the verification JSON to a file if we also saved the
249 : // JSON of the compiler to a file
250 : //
251 1 : if(f_output_json)
252 : {
253 2 : std::string filename;
254 1 : std::string::size_type const dot(f_output.rfind('.'));
255 1 : if(dot != std::string::npos
256 1 : && dot > 0
257 2 : && f_output[dot - 1] != '/')
258 : {
259 1 : filename = f_output.substr(0, dot) + "-verify.json";
260 : }
261 : else
262 : {
263 0 : filename = f_output + "-verify.json";
264 : }
265 2 : std::ofstream out;
266 1 : out.open(filename);
267 1 : if(out.is_open())
268 : {
269 1 : out.write(json, strlen(json));
270 : }
271 : else
272 : {
273 0 : ++f_errcnt;
274 : std::cerr
275 : << "error: could not open JSON output file: \""
276 : << filename
277 0 : << "\".\n";
278 0 : return;
279 : }
280 : }
281 :
282 2 : std::stringstream compiler_json;
283 1 : c.output_to_json(compiler_json, false);
284 :
285 1 : if(compiler_json.str() != json)
286 : {
287 0 : ++f_errcnt;
288 : std::cerr
289 : << "error: compiler & verification JSON differ."
290 0 : << (f_output_json
291 : ? " Check the two .json output files to see the differences."
292 : : " Try using the --output-json command line option to get the .json files to find the differences.")
293 0 : << "\n";
294 0 : return;
295 : }
296 : }
297 :
298 :
299 :
300 :
301 0 : void usage(char * argv0)
302 : {
303 0 : std::string progname(argv0);
304 0 : std::string::size_type pos(progname.rfind('/'));
305 0 : if(pos != std::string::npos)
306 : {
307 0 : progname = progname.substr(pos + 1);
308 : }
309 0 : std::cout << progname << " v" << LIBTLD_VERSION << "\n";
310 0 : std::cout << "Usage: " << progname << " [--opts] [<output>]\n";
311 0 : std::cout << "Where --opts is one or more of the following:\n";
312 0 : std::cout << " --help | -h prints out this help screen and exit\n";
313 0 : std::cout << " --c-file path and filename to the \"tld_data.c\" file\n";
314 0 : std::cout << " --include-offsets print offset in comment in .json file\n";
315 0 : std::cout << " --output-json also save to a .json file\n";
316 0 : std::cout << " --source | -s <folder> define the source (input) folder\n";
317 0 : std::cout << " --verify verify loading results and compare against sources\n";
318 0 : std::cout << " --verbose print out more information about what is happening\n";
319 0 : std::cout << " --version | -V print out the version and exit\n";
320 0 : std::cout << "\n";
321 0 : std::cout << "The default source is \"/usr/share/libtld/tlds\".\n";
322 0 : std::cout << "The default output is \"/var/lib/libtld/tlds.tld\".\n";
323 0 : std::cout << progname << " will not output a C-file or JSON by default.\n";
324 0 : }
325 :
326 :
327 1 : int main(int argc, char * argv[])
328 : {
329 2 : compiler tldc;
330 :
331 7 : for(int i(1); i < argc; ++i)
332 : {
333 6 : if(argv[i][0] == '-')
334 : {
335 5 : if(strcmp(argv[i], "-h") == 0
336 5 : || strcmp(argv[i], "--help") == 0)
337 : {
338 0 : usage(argv[0]);
339 0 : return 1;
340 : }
341 5 : else if(strcmp(argv[i], "-V") == 0
342 5 : || strcmp(argv[i], "--version") == 0)
343 : {
344 0 : std::cout << LIBTLD_VERSION << std::endl;
345 0 : return 1;
346 : }
347 5 : else if(strcmp(argv[i], "-s") == 0
348 5 : || strcmp(argv[i], "--source") == 0)
349 : {
350 1 : ++i;
351 2 : if(i >= argc)
352 : {
353 0 : tldc.error()
354 0 : << "error: argument missing for --source.\n";
355 : }
356 : else
357 : {
358 1 : tldc.set_input_path(argv[i]);
359 : }
360 : }
361 4 : else if(strcmp(argv[i], "--verify") == 0)
362 : {
363 1 : tldc.set_verify(true);
364 : }
365 3 : else if(strcmp(argv[i], "--c-file") == 0)
366 : {
367 1 : ++i;
368 1 : if(i >= argc)
369 : {
370 0 : tldc.error()
371 0 : << "error: argument missing for --output-c-file.\n";
372 : }
373 : else
374 : {
375 1 : tldc.set_c_file(argv[i]);
376 : }
377 : }
378 2 : else if(strcmp(argv[i], "--output-json") == 0)
379 : {
380 1 : tldc.set_output_json(true);
381 : }
382 1 : else if(strcmp(argv[i], "--include-offsets") == 0)
383 : {
384 1 : tldc.set_include_offsets(true);
385 : }
386 0 : else if(strcmp(argv[i], "--verbose") == 0)
387 : {
388 0 : tldc.set_verbose(true);
389 : }
390 : else
391 : {
392 0 : tldc.error()
393 : << "error: unknown command line option \""
394 0 : << argv[i]
395 0 : << "\".\n";
396 : }
397 : }
398 : else
399 : {
400 1 : tldc.set_output(argv[i]);
401 : }
402 : }
403 :
404 1 : tldc.run();
405 :
406 1 : return tldc.exit_code();
407 3 : }
408 :
409 : // vim: ts=4 sw=4 et
|