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

Documentation of CSS Preprocessor.

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.