Current Version: 1.0.32
Project Name: csspp
csspp.cpp
Go to the documentation of this file.
1// Copyright (c) 2015-2024 Made to Order Software Corp. All Rights Reserved
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation; either version 2 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License along
14// with this program; if not, write to the Free Software Foundation, Inc.,
15// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16
227// csspp
228//
229#include <csspp/assembler.h>
230#include <csspp/compiler.h>
231#include <csspp/exception.h>
232#include <csspp/parser.h>
233
234
235// advgetopt
236//
237#include <advgetopt/advgetopt.h>
238#include <advgetopt/exception.h>
239
240
241// boost
242//
243#include <boost/preprocessor/stringize.hpp>
244
245
246// C++
247//
248#include <cstdlib>
249#include <fstream>
250#include <iostream>
251
252
253// C
254//
255#include <unistd.h>
256
257
258// last include
259//
260#include <snapdev/poison.h>
261
262
263
264namespace
265{
266
267void free_char(char * ptr)
268{
269 free(ptr);
270}
271
272// TODO: add support for configuration files & the variable
273
274constexpr advgetopt::option g_options[] =
275{
276 advgetopt::define_option(
277 advgetopt::Name("args")
278 , advgetopt::ShortName('a')
279 , advgetopt::Flags(advgetopt::command_flags<
280 advgetopt::GETOPT_FLAG_REQUIRED
281 , advgetopt::GETOPT_FLAG_MULTIPLE>())
282 , nullptr
283 , "define values in the $_csspp_args variable map"
284 , nullptr
285 ),
286 advgetopt::define_option(
287 advgetopt::Name("debug")
288 , advgetopt::ShortName('d')
289 , advgetopt::Flags(advgetopt::standalone_command_flags<>())
290 , advgetopt::Help("show all messages, including @debug messages")
291 ),
292 advgetopt::define_option(
293 advgetopt::Name("include")
294 , advgetopt::ShortName('I')
295 , advgetopt::Flags(advgetopt::command_flags<
296 advgetopt::GETOPT_FLAG_REQUIRED
297 , advgetopt::GETOPT_FLAG_MULTIPLE>())
298 , advgetopt::Help("specify one or more paths to various user defined CSS files; \"-\" to clear the list (i.e. \"-I -\")")
299 ),
300 advgetopt::define_option(
301 advgetopt::Name("no-logo")
302 , advgetopt::ShortName('\0')
303 , advgetopt::Flags(advgetopt::standalone_command_flags<>())
304 , advgetopt::Help("prevent the \"logo\" from appearing in the output file")
305 ),
306 advgetopt::define_option(
307 advgetopt::Name("empty-on-undefined-variable")
308 , advgetopt::ShortName('\0')
309 , advgetopt::Flags(advgetopt::standalone_command_flags<>())
310 , advgetopt::Help("if accessing an undefined variable, return an empty string, otherwise generate an error.")
311 ),
312 advgetopt::define_option(
313 advgetopt::Name("output")
314 , advgetopt::ShortName('o')
315 , advgetopt::Flags(advgetopt::command_flags<
316 advgetopt::GETOPT_FLAG_REQUIRED>())
317 , advgetopt::Help("save the results in the specified file if specified; otherwise send output to stdout.")
318 ),
319 advgetopt::define_option(
320 advgetopt::Name("precision")
321 , advgetopt::ShortName('p')
322 , advgetopt::Flags(advgetopt::command_flags<
323 advgetopt::GETOPT_FLAG_REQUIRED>())
324 , advgetopt::Help("define the number of digits to use after the decimal point, defaults to 3; note that for percent values, the precision is always 2.")
325 ),
326 advgetopt::define_option(
327 advgetopt::Name("quiet")
328 , advgetopt::ShortName('q')
329 , advgetopt::Flags(advgetopt::standalone_command_flags<>())
330 , advgetopt::Help("suppress @info and @warning messages.")
331 ),
332 advgetopt::define_option(
333 advgetopt::Name("style")
334 , advgetopt::ShortName('s')
335 , advgetopt::Flags(advgetopt::command_flags<
336 advgetopt::GETOPT_FLAG_REQUIRED>())
337 , advgetopt::Help("output style: compressed, tidy, compact, expanded.")
338 ),
339 advgetopt::define_option(
340 advgetopt::Name("Werror")
341 , advgetopt::Flags(advgetopt::standalone_command_flags<>())
342 , advgetopt::Help("make warnings count as errors.")
343 ),
344 advgetopt::define_option(
345 advgetopt::Name("--")
346 , advgetopt::Flags(advgetopt::command_flags<
347 advgetopt::GETOPT_FLAG_MULTIPLE
348 , advgetopt::GETOPT_FLAG_DEFAULT_OPTION
349 , advgetopt::GETOPT_FLAG_SHOW_USAGE_ON_ERROR>())
350 , advgetopt::Help("[file.css ...]; use stdin if no filename specified.")
351 ),
352 advgetopt::end_options()
353};
354
355// TODO: once we have stdc++20, remove all defaults
356#pragma GCC diagnostic ignored "-Wpedantic"
357advgetopt::options_environment const g_options_environment =
358{
359 .f_project_name = "csspp",
360 .f_group_name = nullptr,
361 .f_options = g_options,
362 .f_options_files_directory = nullptr,
363 .f_environment_variable_name = "CSSPPFLAGS",
364 .f_environment_variable_intro = nullptr,
365 .f_section_variables_name = nullptr,
366 .f_configuration_files = nullptr,
367 .f_configuration_filename = nullptr,
368 .f_configuration_directories = nullptr,
369 .f_environment_flags = advgetopt::GETOPT_ENVIRONMENT_FLAG_PROCESS_SYSTEM_PARAMETERS,
370 .f_help_header = "Usage: %p [-<opt>] [file.css ...] [-o out.css]\n"
371 "where -<opt> is one or more of:",
372 .f_help_footer = "%c",
373 .f_version = CSSPP_VERSION,
374 .f_license = "GNU GPL v2",
375 .f_copyright = "Copyright (c) 2015-"
376 BOOST_PP_STRINGIZE(UTC_BUILD_YEAR)
377 " by Made to Order Software Corporation -- All Rights Reserved",
378 //.f_build_date = UTC_BUILD_DATE,
379 //.f_build_time = UTC_BUILD_TIME
380};
381
382
383
384class pp
385{
386public:
387 pp(int argc, char * argv[]);
388
389 int compile();
390
391private:
392 advgetopt::getopt f_opt;
393 int f_precision = 3;
394};
395
396
397pp::pp(int argc, char * argv[])
398 : f_opt(g_options_environment, argc, argv)
399{
400 if(f_opt.is_defined("quiet"))
401 {
402 csspp::error::instance().set_hide_all(true);
403 }
404
405 if(f_opt.is_defined("debug"))
406 {
407 csspp::error::instance().set_show_debug(true);
408 }
409
410 if(f_opt.is_defined("Werror"))
411 {
412 csspp::error::instance().set_count_warnings_as_errors(true);
413 }
414
415 if(f_opt.is_defined("precision"))
416 {
417 f_precision = f_opt.get_long("precision");
418 }
419}
420
422{
423 csspp::lexer::pointer_t l;
424 csspp::position::pointer_t pos;
425 std::unique_ptr<std::stringstream> ss;
426
427 csspp::safe_precision_t safe_precision(f_precision);
428
429 if(f_opt.is_defined("--"))
430 {
431 // one or more filename specified
432 int const arg_count(f_opt.size("--"));
433 if(arg_count == 1
434 && f_opt.get_string("--") == "-")
435 {
436 // user asked for stdin
437 pos.reset(new csspp::position("-"));
438 l.reset(new csspp::lexer(std::cin, *pos));
439 }
440 else
441 {
442 std::unique_ptr<char, void (*)(char *)> cwd(get_current_dir_name(), free_char);
443 ss.reset(new std::stringstream);
444 pos.reset(new csspp::position("csspp.css"));
445 for(int idx(0); idx < arg_count; ++idx)
446 {
447 // full paths so the -I have no effects on those files
448 std::string filename(f_opt.get_string("--", idx));
449 if(filename.empty())
450 {
451 csspp::error::instance() << *pos
452 << "You cannot include a file with an empty name."
453 << csspp::error_mode_t::ERROR_WARNING;
454 return 1;
455 }
456 if(filename == "-")
457 {
458 csspp::error::instance() << *pos
459 << "You cannot currently mix files and stdin. You may use @import \"filename\"; in your stdin data though."
460 << csspp::error_mode_t::ERROR_WARNING;
461 return 1;
462 }
463 if(filename[0] == '/')
464 {
465 // already absolute
466 *ss << "@import \"" << filename << "\";\n";
467 }
468 else
469 {
470 // make absolute so we do not need to have a "." path
471 *ss << "@import \"" << cwd.get() << "/" << filename << "\";\n";
472 }
473 }
474 l.reset(new csspp::lexer(*ss, *pos));
475 }
476 }
477 else
478 {
479 // default to stdin
480 pos.reset(new csspp::position("-"));
481 l.reset(new csspp::lexer(std::cin, *pos));
482 }
483
484 // run the lexer and parser
485 csspp::error_happened_t error_tracker;
486 csspp::parser p(l);
487 csspp::node::pointer_t root(p.stylesheet());
488 if(error_tracker.error_happened())
489 {
490 return 1;
491 }
492
493 csspp::node::pointer_t csspp_args(new csspp::node(csspp::node_type_t::LIST, root->get_position()));
494 csspp::node::pointer_t args_var(new csspp::node(csspp::node_type_t::VARIABLE, root->get_position()));
495 args_var->set_string("_csspp_args");
496 csspp::node::pointer_t wrapper(new csspp::node(csspp::node_type_t::LIST, root->get_position()));
497 csspp::node::pointer_t array(new csspp::node(csspp::node_type_t::ARRAY, root->get_position()));
498 wrapper->add_child(array);
499 csspp_args->add_child(args_var);
500 csspp_args->add_child(wrapper);
501 if(f_opt.is_defined("args"))
502 {
503 int const count(f_opt.size("args"));
504 for(int idx(0); idx < count; ++idx)
505 {
506 csspp::node::pointer_t arg(new csspp::node(csspp::node_type_t::STRING, root->get_position()));
507 arg->set_string(f_opt.get_string("args", idx));
508 array->add_child(arg);
509 }
510 }
511 root->set_variable("_csspp_args", csspp_args);
512
513 // run the compiler
514 csspp::compiler c;
515 c.set_root(root);
516 c.set_date_time_variables(time(nullptr));
517
518 // add paths to the compiler (i.e. for the user and system @imports)
519 if(f_opt.is_defined("include"))
520 {
521 std::size_t const count(f_opt.size("include"));
522 for(std::size_t idx(0); idx < count; ++idx)
523 {
524 std::string const path(f_opt.get_string("include", idx));
525 if(path == "-")
526 {
527 c.clear_paths();
528 }
529 else
530 {
531 c.add_path(path);
532 }
533 }
534 }
535
536 if(f_opt.is_defined("no-logo"))
537 {
538 c.set_no_logo();
539 }
540
541 if(f_opt.is_defined("empty-on-undefined-variable"))
542 {
543 c.set_empty_on_undefined_variable(true);
544 }
545
546 c.compile(false);
547 if(error_tracker.error_happened())
548 {
549 return 1;
550 }
551
552 csspp::output_mode_t output_mode(csspp::output_mode_t::COMPRESSED);
553 if(f_opt.is_defined("style"))
554 {
555 std::string const mode(f_opt.get_string("style"));
556 if(mode == "compressed")
557 {
558 output_mode = csspp::output_mode_t::COMPRESSED;
559 }
560 else if(mode == "tidy")
561 {
562 output_mode = csspp::output_mode_t::TIDY;
563 }
564 else if(mode == "compact")
565 {
566 output_mode = csspp::output_mode_t::COMPACT;
567 }
568 else if(mode == "expanded")
569 {
570 output_mode = csspp::output_mode_t::EXPANDED;
571 }
572 else
573 {
574 csspp::error::instance() << root->get_position()
575 << "The output mode \""
576 << mode
577 << "\" is not supported. Try one of: compressed, tidy, compact, expanded instead."
578 << csspp::error_mode_t::ERROR_WARNING;
579 return 1;
580 }
581 }
582
583 std::ostream * out(nullptr);
584 bool user_output(false);
585 std::string output_filename;
586 if(f_opt.is_defined("output"))
587 {
588 output_filename = f_opt.get_string("output");
589 user_output = output_filename != "-";
590 }
591 if(user_output)
592 {
593 out = new std::ofstream(output_filename);
594 }
595 else
596 {
597 out = &std::cout;
598 }
599 csspp::assembler a(*out);
600 a.output(c.get_root(), output_mode);
601 if(user_output)
602 {
603 delete out;
604 }
605 if(error_tracker.error_happened())
606 {
607 // this should be rare as the assembler generally does not generate
608 // errors (it may throw though.)
609 return 1;
610 }
611
612 return 0;
613}
614
615} // no name namespace
616
617int main(int argc, char *argv[])
618{
619 try
620 {
621 pp preprocessor(argc, argv);
622 return preprocessor.compile();
623 }
624 catch(advgetopt::getopt_exit const & except)
625 {
626 return except.code();
627 }
628 catch(csspp::csspp_exception_exit const & e)
629 {
630 // something went wrong in the library
631 return e.exit_code();
632 }
633 catch(csspp::csspp_exception_logic const & e)
634 {
635 std::cerr << "fatal error: a logic exception, which should NEVER occur, occurred: " << e.what() << std::endl;
636 exit(1);
637 }
638 catch(csspp::csspp_exception_overflow const & e)
639 {
640 std::cerr << "fatal error: an overflow exception occurred: " << e.what() << std::endl;
641 exit(1);
642 }
643 catch(csspp::csspp_exception_runtime const & e)
644 {
645 std::cerr << "fatal error: a runtime exception occurred: " << e.what() << std::endl;
646 exit(1);
647 }
648 catch(advgetopt::getopt_undefined const & e)
649 {
650 std::cerr << "fatal error: an undefined exception occurred because of your command line: " << e.what() << std::endl;
651 exit(1);
652 }
653 catch(advgetopt::getopt_invalid const & e)
654 {
655 std::cerr << "fatal error: there is an error on your command line, an exception occurred: " << e.what() << std::endl;
656 exit(1);
657 }
658 catch(advgetopt::getopt_invalid_default const & e)
659 {
660 std::cerr << "fatal error: there is an error on your command line, you used a parameter without a value and there is no default. The exception says: " << e.what() << std::endl;
661 exit(1);
662 }
663}
664
665// Local Variables:
666// mode: cpp
667// indent-tabs-mode: nil
668// c-basic-offset: 4
669// tab-width: 4
670// End:
671
672// vim: ts=4 sw=4 et
pp(int argc, char *argv[])
Definition csspp.cpp:397
int main(int argc, char *argv[])
Definition csspp.cpp:617
void free_char(char *ptr)
Definition csspp.cpp:267
constexpr advgetopt::option g_options[]
Definition csspp.cpp:274
advgetopt::options_environment const g_options_environment
Definition csspp.cpp:357

Documentation of CSS Preprocessor.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.