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 :
54 : void run();
55 :
56 : private:
57 : void verify_output(tld_compiler & c);
58 :
59 : int f_errcnt = 0;
60 : std::string f_input_path = std::string();
61 : std::string f_output = std::string();
62 : std::string f_c_file = std::string();
63 : bool f_verify = false;
64 : bool f_output_json = false;
65 : bool f_include_offsets = false;
66 : };
67 :
68 :
69 0 : std::ostream & compiler::error()
70 : {
71 0 : ++f_errcnt;
72 0 : return std::cerr;
73 : }
74 :
75 :
76 1 : int compiler::exit_code() const
77 : {
78 1 : return f_errcnt == 0 ? 0 : 1;
79 : }
80 :
81 :
82 1 : void compiler::set_input_path(std::string const & path)
83 : {
84 1 : f_input_path = path;
85 1 : }
86 :
87 :
88 1 : void compiler::set_output(std::string const & output)
89 : {
90 1 : f_output = output;
91 1 : }
92 :
93 :
94 1 : void compiler::set_c_file(std::string const & c_file)
95 : {
96 1 : f_c_file = c_file;
97 1 : }
98 :
99 :
100 1 : void compiler::set_verify(bool verify)
101 : {
102 1 : f_verify = verify;
103 1 : }
104 :
105 :
106 1 : void compiler::set_output_json(bool output_json)
107 : {
108 1 : f_output_json = output_json;
109 1 : }
110 :
111 :
112 1 : void compiler::set_include_offsets(bool include_offsets)
113 : {
114 1 : f_include_offsets = include_offsets;
115 1 : }
116 :
117 :
118 1 : void compiler::run()
119 : {
120 1 : if(f_errcnt != 0)
121 : {
122 : // command line found errors, return immediately
123 0 : return;
124 : }
125 :
126 1 : if(f_input_path.empty())
127 : {
128 0 : ++f_errcnt;
129 0 : std::cerr << "error: an input path is required.\n";
130 0 : return;
131 : }
132 :
133 1 : if(f_output.empty())
134 : {
135 0 : ++f_errcnt;
136 0 : std::cerr << "error: an output filename is required.\n";
137 0 : return;
138 : }
139 :
140 1 : std::cout << "Compiling TLDs from \"" << f_input_path << "\"...\n";
141 :
142 2 : tld_compiler c;
143 1 : c.set_input_folder(f_input_path);
144 1 : c.set_output(f_output);
145 1 : c.set_c_file(f_c_file);
146 1 : if(!c.compile())
147 : {
148 0 : ++f_errcnt;
149 : std::cerr
150 : << "error:"
151 0 : << c.get_filename()
152 0 : << ":"
153 : << c.get_line()
154 : << ": "
155 0 : << c.get_errmsg()
156 0 : << " (errno: "
157 : << c.get_errno()
158 : << ", "
159 0 : << strerror(c.get_errno())
160 0 : << ")\n";
161 0 : return;
162 : }
163 :
164 1 : std::cout << "Number of strings: " << c.get_string_manager().size() << "\n";
165 1 : std::cout << "Longest string: " << c.get_string_manager().max_length() << "\n";
166 1 : std::cout << "Total string length: " << c.get_string_manager().total_length() << "\n";
167 1 : std::cout << "Included strings: " << c.get_string_manager().included_count() << " (saved length: " << c.get_string_manager().included_length() << ")\n";
168 1 : std::cout << "Mergeable strings: " << c.get_string_manager().merged_count() << " (saved length: " << c.get_string_manager().merged_length() << ")\n";
169 1 : std::cout << "Compressed string length: " << c.get_string_manager().compressed_length() << "\n";
170 : // TODO: add info about tags
171 :
172 1 : if(f_output_json)
173 : {
174 2 : std::string filename;
175 1 : std::string::size_type const dot(f_output.rfind('.'));
176 1 : if(dot != std::string::npos
177 1 : && dot > 0
178 2 : && f_output[dot - 1] != '/')
179 : {
180 1 : filename = f_output.substr(0, dot) + ".json";
181 : }
182 : else
183 : {
184 0 : filename = f_output + ".json";
185 : }
186 2 : std::ofstream out;
187 1 : out.open(filename);
188 1 : if(out.is_open())
189 : {
190 1 : c.output_to_json(out, f_include_offsets);
191 : }
192 : else
193 : {
194 0 : ++f_errcnt;
195 : std::cerr
196 : << "error: could not open JSON output file: \""
197 : << filename
198 0 : << "\".\n";
199 0 : return;
200 : }
201 : }
202 :
203 1 : if(f_verify)
204 : {
205 1 : verify_output(c);
206 : }
207 : }
208 :
209 :
210 1 : void compiler::verify_output(tld_compiler & c)
211 : {
212 1 : tld_file * file(nullptr);
213 2 : auto_free_tld_file auto_free(&file);
214 1 : tld_file_error err(tld_file_load(f_output.c_str(), &file));
215 1 : if(err != TLD_FILE_ERROR_NONE)
216 : {
217 0 : ++f_errcnt;
218 : std::cerr << "error: could not load output file \""
219 : << f_output
220 : << "\" -- err: "
221 : << tld_file_errstr(err)
222 0 : << " ("
223 : << static_cast<int>(err)
224 0 : << ").\n";
225 0 : return;
226 : }
227 :
228 : // generate a JSON from what was just loaded
229 : // and it has to match the compiler's JSON
230 : //
231 1 : char * json(tld_file_to_json(file));
232 1 : if(json == nullptr)
233 : {
234 0 : ++f_errcnt;
235 0 : std::cerr << "error: conversion of file to JSON failed.\n";
236 0 : return;
237 : }
238 2 : auto_free_string auto_delete(json);
239 :
240 : // save the verification JSON to a file if we also saved the
241 : // JSON of the compiler to a file
242 : //
243 1 : if(f_output_json)
244 : {
245 2 : std::string filename;
246 1 : std::string::size_type const dot(f_output.rfind('.'));
247 1 : if(dot != std::string::npos
248 1 : && dot > 0
249 2 : && f_output[dot - 1] != '/')
250 : {
251 1 : filename = f_output.substr(0, dot) + "-verify.json";
252 : }
253 : else
254 : {
255 0 : filename = f_output + "-verify.json";
256 : }
257 2 : std::ofstream out;
258 1 : out.open(filename);
259 1 : if(out.is_open())
260 : {
261 1 : out.write(json, strlen(json));
262 : }
263 : else
264 : {
265 0 : ++f_errcnt;
266 : std::cerr
267 : << "error: could not open JSON output file: \""
268 : << filename
269 0 : << "\".\n";
270 0 : return;
271 : }
272 : }
273 :
274 2 : std::stringstream compiler_json;
275 1 : c.output_to_json(compiler_json, false);
276 :
277 1 : if(compiler_json.str() != json)
278 : {
279 0 : ++f_errcnt;
280 : std::cerr
281 : << "error: compiler & verification JSON differ."
282 0 : << (f_output_json
283 : ? " Check the two .json output files to see the differences."
284 : : " Try using the --output-json command line option to get the .json files to find the differences.")
285 0 : << "\n";
286 0 : return;
287 : }
288 : }
289 :
290 :
291 :
292 :
293 0 : void usage(char * argv0)
294 : {
295 0 : std::string progname(argv0);
296 0 : std::string::size_type pos(progname.rfind('/'));
297 0 : if(pos != std::string::npos)
298 : {
299 0 : progname = progname.substr(pos + 1);
300 : }
301 0 : std::cout << progname << " v" << LIBTLD_VERSION << "\n";
302 0 : std::cout << "Usage: " << progname << " [--opts] [<output>]\n";
303 0 : std::cout << "Where --opts is one or more of the following:\n";
304 0 : std::cout << " --help | -h prints out this help screen and exit\n";
305 0 : std::cout << " --c-file path and filename to the \"tld_data.c\" file\n";
306 0 : std::cout << " --include-offsets print offset in comment in .json file\n";
307 0 : std::cout << " --output-json also save to a .json file\n";
308 0 : std::cout << " --source | -s <folder> define the source (input) folder\n";
309 0 : std::cout << " --verify verify loading results and compare against sources\n";
310 0 : std::cout << " --version | -V print out the version and exit\n";
311 0 : std::cout << "\n";
312 0 : std::cout << "The default source is \"/usr/share/libtld/tlds\".\n";
313 0 : std::cout << "The default output is \"/var/lib/libtld/tlds.tld\".\n";
314 0 : std::cout << progname << " will not output a C-file or JSON by default.\n";
315 0 : }
316 :
317 :
318 1 : int main(int argc, char * argv[])
319 : {
320 2 : compiler tldc;
321 :
322 7 : for(int i(1); i < argc; ++i)
323 : {
324 6 : if(argv[i][0] == '-')
325 : {
326 5 : if(strcmp(argv[i], "-h") == 0
327 5 : || strcmp(argv[i], "--help") == 0)
328 : {
329 0 : usage(argv[0]);
330 0 : return 1;
331 : }
332 5 : else if(strcmp(argv[i], "-V") == 0
333 5 : || strcmp(argv[i], "--version") == 0)
334 : {
335 0 : std::cout << LIBTLD_VERSION << std::endl;
336 0 : return 1;
337 : }
338 5 : else if(strcmp(argv[i], "-s") == 0
339 5 : || strcmp(argv[i], "--source") == 0)
340 : {
341 1 : ++i;
342 2 : if(i >= argc)
343 : {
344 0 : tldc.error()
345 0 : << "error: argument missing for --source.\n";
346 : }
347 : else
348 : {
349 1 : tldc.set_input_path(argv[i]);
350 : }
351 : }
352 4 : else if(strcmp(argv[i], "--verify") == 0)
353 : {
354 1 : tldc.set_verify(true);
355 : }
356 3 : else if(strcmp(argv[i], "--c-file") == 0)
357 : {
358 1 : ++i;
359 1 : if(i >= argc)
360 : {
361 0 : tldc.error()
362 0 : << "error: argument missing for --output-c-file.\n";
363 : }
364 : else
365 : {
366 1 : tldc.set_c_file(argv[i]);
367 : }
368 : }
369 2 : else if(strcmp(argv[i], "--output-json") == 0)
370 : {
371 1 : tldc.set_output_json(true);
372 : }
373 1 : else if(strcmp(argv[i], "--include-offsets") == 0)
374 : {
375 1 : tldc.set_include_offsets(true);
376 : }
377 : else
378 : {
379 0 : tldc.error()
380 : << "error: unknown command line option \""
381 0 : << argv[i]
382 0 : << "\".\n";
383 : }
384 : }
385 : else
386 : {
387 1 : tldc.set_output(argv[i]);
388 : }
389 : }
390 :
391 1 : tldc.run();
392 :
393 1 : return tldc.exit_code();
394 3 : }
395 :
396 : // vim: ts=4 sw=4 et
|