1 /* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
4 * csslint.c : a small tester program for libcroco.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2.1 of the GNU Lesser General Public
8 * License as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * Initial Author: Gael Chamoulaud.
21 * Main programmer: Dodji Seketeli
22 * Contributor: Rob Buis
23 * See COPYRGIGHTS file for copyright information.
27 #include <libxml/xpath.h>
33 *The options data structure.
34 *The variable of this data structure are set
35 *during the parsing the command line by the
36 *parse_command_line() function.
39 gboolean show_version;
41 gboolean display_help;
43 gboolean dump_location;
44 gchar *author_sheet_path;
45 gchar *user_sheet_path;
49 gchar **css_files_list;
57 static enum CRStatus sac_parse_and_display_locations (guchar * a_file_uri);
59 static void parse_cmd_line (int a_argc, char **a_argv,
60 struct Options *a_options);
62 static void display_version (void);
64 static void display_usage (void);
66 static enum CRStatus cssom_parse (guchar * a_file_uri);
68 static enum CRStatus get_and_dump_node_style (xmlNode * a_node,
70 CRCascade * a_cascade);
72 static enum CRStatus evaluate_selectors (gchar * a_xml_path,
73 gchar * a_author_sheet_path,
74 gchar * a_user_sheet_path,
75 gchar * a_ua_sheet_path,
79 *Parses the command line.
80 *@param a_argc the argc parameter of the main routine.
81 *@param the argv parameter of the main routine.
82 *@param a_options out parameter the parsed options.
85 parse_cmd_line (int a_argc, char **a_argv, struct Options *a_options)
89 g_return_if_fail (a_options);
95 for (i = 1; i < a_argc; i++) {
96 if (a_argv[i][0] != '-')
99 if ((!strcmp (a_argv[i], "-version")) ||
100 (!strcmp (a_argv[i], "-v"))) {
101 a_options->show_version = TRUE;
102 } else if (!strcmp (a_argv[i], "--evaluate") ||
103 !strcmp (a_argv[i], "-e")) {
104 for (i++; i < a_argc; i++) {
105 if (!strcmp (a_argv[i], "--author-sheet")) {
106 if (a_options->author_sheet_path) {
112 || a_argv[i][0] == '-') {
113 g_print ("--author-sheet should be followed by a path to the sheet\n");
117 a_options->author_sheet_path =
120 (a_argv[i], "--user-sheet")) {
121 if (a_options->user_sheet_path) {
127 || a_argv[i][0] == '-') {
128 g_print ("--user-sheet should be followed by a path to the sheet\n");
132 a_options->user_sheet_path =
134 } else if (!strcmp (a_argv[i], "--ua-sheet")) {
135 if (a_options->ua_sheet_path) {
141 || a_argv[i][0] == '-') {
142 g_print ("--ua-sheet should be followed by a path to the sheet\n");
146 a_options->ua_sheet_path = a_argv[i];
147 } else if (!strcmp (a_argv[i], "--xml")) {
150 || a_argv[i][0] == '-') {
151 g_print ("--xml should be followed by a path to the xml document\n");
155 a_options->xml_path = a_argv[i];
156 } else if (!strcmp (a_argv[i], "--xpath")) {
159 || a_argv[i][0] == '-') {
160 g_print ("--xpath should be followed by an xpath expresion\n");
164 a_options->xpath = a_argv[i];
169 if (!a_options->author_sheet_path
170 && !a_options->user_sheet_path &&
171 !a_options->ua_sheet_path) {
172 g_print ("Error: you must specify at least one stylesheet\n");
177 if (!a_options->xpath) {
179 ("Error: you must specify an xpath expression using the --xpath option\n");
184 a_options->evaluate = TRUE;
185 } else if (!strcmp (a_argv[i], "--dump-location")) {
186 a_options->dump_location = TRUE;
187 a_options->use_cssom = FALSE;
188 } else if (!strcmp (a_argv[i], "--help") ||
189 !strcmp (a_argv[i], "-h")) {
190 a_options->display_help = TRUE;
198 a_options->css_files_list = NULL;
200 if (a_argv[i][0] == '-') {
204 a_options->css_files_list = &a_argv[i];
209 *Displays the version text.
210 *@param a_argc the argc variable passed to the main function.
211 *@param a_argv the argv variable passed to the main function.
214 display_version (void)
216 g_print ("%s\n", LIBCROCO_VERSION);
220 *Displays the usage text.
221 *@param a_argc the argc variable passed to the main function.
222 *@param a_argv the argv variable passed to the main function.
227 g_print ("Usage: csslint <path to a css file>\n");
228 g_print ("\t| csslint -v|--version\n");
229 g_print ("\t| csslint --dump-location <path to a css file>\n");
230 g_print ("\t| csslint <--evaluate | -e> [--author-sheet <path> --user-sheet <path> --ua-sheet <path>\n\t ] --xml <path> --xpath <xpath expression>\n");
234 *The test of the cr_input_read_byte() method.
235 *Reads the each byte of a_file_uri using the
236 *cr_input_read_byte() method. Each byte is send to
238 *@param a_file_uri the file to read.
239 *@return CR_OK upon successfull completion of the
240 *function, an error code otherwise.
243 cssom_parse (guchar * a_file_uri)
245 enum CRStatus status = CR_OK;
246 CROMParser *parser = NULL;
247 CRStyleSheet *stylesheet = NULL;
249 g_return_val_if_fail (a_file_uri, CR_BAD_PARAM_ERROR);
251 parser = cr_om_parser_new (NULL);
252 status = cr_om_parser_parse_file (parser,
253 a_file_uri, CR_ASCII, &stylesheet);
254 if (status == CR_OK && stylesheet) {
255 cr_stylesheet_dump (stylesheet, stdout);
257 cr_stylesheet_destroy (stylesheet);
259 cr_om_parser_destroy (parser);
265 get_and_dump_node_style (xmlNode * a_node,
266 CRSelEng * a_sel_eng, CRCascade * a_cascade)
268 CRPropList *prop_list = NULL,
271 enum CRStatus status = CR_OK;
273 g_return_val_if_fail (a_node && a_sel_eng && a_cascade,
276 status = cr_sel_eng_get_matched_properties_from_cascade
277 (a_sel_eng, a_cascade, a_node, &prop_list);
278 if (status != CR_OK) {
279 g_printerr ("Error: unable to run the selection engine\n");
282 g_print ("Properties of xml element %s are:\n", a_node->name);
283 for (pair = prop_list; pair; pair = cr_prop_list_get_next (pair)) {
284 CRDeclaration *decl = NULL;
286 cr_prop_list_get_decl (pair, &decl);
288 prev_pair = cr_prop_list_get_prev (pair);
293 cr_declaration_dump_one (decl, stdout, 2);
297 g_print ("\n=====================\n\n");
300 cr_prop_list_destroy (prop_list);
308 evaluate_selectors (gchar * a_xml_path,
309 gchar * a_author_sheet_path,
310 gchar * a_user_sheet_path,
311 gchar * a_ua_sheet_path, gchar * a_xpath)
313 CRSelEng *sel_eng = NULL;
314 xmlDoc *xml_doc = NULL;
315 xmlXPathContext *xpath_context = NULL;
316 xmlXPathObject *xpath_object = NULL;
317 CRStyleSheet *author_sheet = NULL,
320 CRCascade *cascade = NULL;
321 xmlNode *cur_node = NULL;
323 enum CRStatus status = CR_OK;
325 g_return_val_if_fail (a_xml_path && a_xpath, CR_BAD_PARAM_ERROR);
327 xml_doc = xmlParseFile (a_xml_path);
329 g_printerr ("Error: Could not parse file %s\n", a_xml_path);
332 if (a_author_sheet_path) {
333 status = cr_om_parser_simply_parse_file
334 (a_author_sheet_path, CR_ASCII, &author_sheet);
336 g_printerr ("Error: Could not parse author sheet\n");
339 if (a_user_sheet_path) {
340 status = cr_om_parser_simply_parse_file
341 (a_user_sheet_path, CR_ASCII, &user_sheet);
343 g_printerr ("Error: Could not parse author sheet\n");
346 if (a_ua_sheet_path) {
347 status = cr_om_parser_simply_parse_file
348 (a_ua_sheet_path, CR_ASCII, &ua_sheet);
350 g_printerr ("Error: Could not parse ua sheet\n");
353 cascade = cr_cascade_new (author_sheet, user_sheet, ua_sheet);
355 g_printerr ("Could not instanciate the cascade\n");
358 sel_eng = cr_sel_eng_new ();
361 ("Error: Could not instanciate the selection engine\n");
364 xpath_context = xmlXPathNewContext (xml_doc);
365 if (!xpath_context) {
367 ("Error: Could not instanciate the xpath context\n");
370 xpath_object = xmlXPathEvalExpression (a_xpath, xpath_context);
372 g_printerr ("Error: Could not evaluate xpath expression\n");
375 if (xpath_object->type != XPATH_NODESET || !xpath_object->nodesetval) {
377 ("Error: xpath does not evalualuate to a node set\n");
381 for (i = 0; i < xpath_object->nodesetval->nodeNr; i++) {
382 cur_node = xpath_object->nodesetval->nodeTab[i];
383 if (cur_node->type == XML_ELEMENT_NODE) {
384 status = get_and_dump_node_style (cur_node, sel_eng,
390 xmlXPathFreeContext (xpath_context);
391 xpath_context = NULL;
394 xmlXPathFreeObject (xpath_object);
398 cr_sel_eng_destroy (sel_eng);
402 cr_cascade_destroy (cascade);
407 /***************************
408 *SAC related stuff for the
409 *line/col annotation stylesheet
411 ***************************/
413 start_document (CRDocHandler * a_this)
415 struct SacContext *context = NULL;
417 g_return_if_fail (a_this);
419 context = g_try_malloc (sizeof (struct SacContext));
421 cr_utils_trace_info ("instanciation of sac context failed");
424 cr_doc_handler_set_ctxt (a_this, context);
428 end_document (CRDocHandler * a_this)
430 struct SacContext *context = NULL;
432 g_return_if_fail (a_this);
434 cr_doc_handler_get_ctxt (a_this, (gpointer *) (gpointer) & context);
442 charset (CRDocHandler *a_this,
444 CRParsingLocation *a_charset_sym_location)
448 g_return_if_fail (a_this && a_charset && a_charset_sym_location) ;
450 str = (gchar*)cr_string_peek_raw_str (a_charset) ;
452 g_print ("\n\n@charset \"%s\";\n\n", str) ;
457 g_print ("/********************************************\n") ;
458 g_print (" * Parsing location information of the @charset rule\n") ;
459 g_print (" ********************************************/\n") ;
460 if (a_charset_sym_location) {
461 str = cr_parsing_location_to_string (a_charset_sym_location, 0) ;
463 g_print (" /*@charset*/\n") ;
464 g_print (" /*%s*/\n", str) ;
469 str = (gchar*) cr_string_peek_raw_str (a_charset) ;
471 g_print (" /*%s*/\n", str) ;
474 str = cr_parsing_location_to_string (&a_charset->location, 0) ;
476 g_print (" /*%s*/\n\n", str) ;
483 import_style (CRDocHandler *a_this,
486 CRString *a_uri_default_ns,
487 CRParsingLocation *a_location)
490 GString *gstr = NULL ;
491 GList *cur_media = NULL ;
493 g_return_if_fail (a_this && a_location) ;
495 gstr = g_string_new (NULL) ;
497 cr_utils_trace_info ("Out of memory error") ;
500 for (cur_media = a_media_list ;
502 cur_media = g_list_next (cur_media)) {
503 str = (gchar*)cr_string_peek_raw_str
504 ((CRString*)cur_media->data) ;
506 if (g_list_previous (cur_media)) {
507 g_string_append_printf (gstr, ", %s",
510 g_string_append_printf (gstr, "%s",
515 str = (gchar*)cr_string_peek_raw_str (a_uri) ;
517 g_print ("@import url(\"%s\") ", str) ;
519 g_print ("%s;\n\n", gstr->str) ;
523 str = cr_parsing_location_to_string (a_location, 0) ;
525 g_print ("/*****************************************************\n") ;
526 g_print (" *Parsing location inforamtion for the @import rule\n") ;
527 g_print (" ******************************************************/\n\n") ;
528 g_print (" /*@import*/\n") ;
529 g_print (" /*%s*/\n\n", str) ;
534 str = (gchar*)cr_string_peek_raw_str (a_uri) ;
536 g_print (" /*%s*/\n", str) ;
537 str = cr_parsing_location_to_string
538 (&a_uri->location, 0) ;
540 g_print (" /*%s*/\n\n", str) ;
546 for (cur_media = a_media_list ;
548 cur_media = g_list_next (cur_media)) {
549 str = (gchar*)cr_string_peek_raw_str
550 ((CRString*)cur_media->data) ;
552 g_print (" /*%s*/\n", str) ;
554 str = cr_parsing_location_to_string
555 (&((CRString*)cur_media->data)->location, 0) ;
557 g_print (" /*%s*/\n\n", str) ;
564 g_string_free (gstr, TRUE) ;
570 start_font_face (CRDocHandler *a_this,
571 CRParsingLocation *a_location)
575 g_print ("@font-face {\n") ;
576 g_print ("/******************************************************\n") ;
577 g_print (" Parsing location information for the @font-face rule\n") ;
578 g_print (" ******************************************************/\n\n") ;
580 g_print (" /*@font-face*/\n") ;
582 str = cr_parsing_location_to_string (a_location, 0) ;
585 g_print (" /*%s*/\n", str) ;
592 end_font_face (CRDocHandler *a_this)
598 start_media (CRDocHandler *a_this,
600 CRParsingLocation *a_location)
602 GList *cur_media = NULL ;
605 g_print ("@media ") ;
606 for (cur_media = a_media_list ;
608 cur_media = g_list_next (cur_media)) {
609 CRString *crstr = cur_media->data ;
611 str = (gchar*)cr_string_peek_raw_str (crstr) ;
613 if (cur_media->prev) {
614 g_print (", %s", str) ;
616 g_print ("%s", str) ;
621 /*****************************
622 *new print parsing locations
623 *****************************/
624 g_print ("\n /*@media*/\n") ;
626 str = cr_parsing_location_to_string (a_location,
629 g_print (" /*%s*/\n", str) ;
633 for (cur_media = a_media_list ;
635 cur_media = g_list_next (cur_media)) {
636 CRString *crstr = cur_media->data ;
638 str = (gchar*)cr_string_peek_raw_str (crstr) ;
640 g_print (" /*%s*/\n", str) ;
641 str = cr_parsing_location_to_string
642 (&crstr->location, 0) ;
644 g_print (" /*%s*/\n", str) ;
655 end_media (CRDocHandler *a_this,
662 start_page (CRDocHandler *a_this,
664 CRString *a_pseudo_page,
665 CRParsingLocation *a_location)
671 str = (gchar*)cr_string_peek_raw_str (a_name) ;
673 g_print ("%s ", str) ;
676 str = (gchar*) cr_string_peek_raw_str (a_pseudo_page) ;
678 g_print (":%s", str) ;
681 /*************************************
682 *print parsing location informations
683 ************************************/
684 g_print ("\n\n /*@page*/\n") ;
686 str = cr_parsing_location_to_string
689 g_print (" /*%s*/\n", str) ;
694 str = (gchar*)cr_string_peek_raw_str (a_name) ;
696 g_print (" /*%s*/\n", str) ;
697 str = cr_parsing_location_to_string
698 (&a_name->location, 0) ;
700 g_print (" /*%s*/\n", str) ;
706 str = (gchar*) cr_string_peek_raw_str
709 g_print (" /*%s*/\n", str) ;
710 str = cr_parsing_location_to_string
711 (&a_pseudo_page->location, 0) ;
713 g_print (" /*%s*/\n", str) ;
722 end_page (CRDocHandler *a_this,
724 CRString *pseudo_page)
730 dump_location_annotated_simple_sel (CRSimpleSel *a_this)
732 CRSimpleSel *cur_simple_sel = a_this ;
733 CRAdditionalSel *cur_add_sel = NULL ;
736 g_return_if_fail (a_this) ;
738 /*first, display the entire simple sel*/
739 str0 = cr_simple_sel_one_to_string
742 g_print ("/*%s*/\n", str0) ;
747 cr_parsing_location_dump
748 (&cur_simple_sel->location, 0,
752 /*now display the details of the simple sel*/
753 if (cur_simple_sel->name) {
754 str0 = (gchar*) cr_string_peek_raw_str
755 (cur_simple_sel->name) ;
757 g_print (" /*%s*/\n", str0) ;
760 str0 = cr_parsing_location_to_string
761 (&cur_simple_sel->name->location,
764 g_print (" /*%s*/\n", str0) ;
769 for (cur_add_sel = cur_simple_sel->add_sel;
771 cur_add_sel = cur_add_sel->next) {
772 str0 = cr_additional_sel_one_to_string
775 g_print ("\n /*%s*/\n", str0) ;
779 str0 = cr_parsing_location_to_string
780 (&cur_add_sel->location, 0) ;
782 g_print (" /*%s*/\n", str0) ;
790 start_selector (CRDocHandler * a_this, CRSelector * a_selector_list)
792 struct SacContext *context = NULL;
793 CRSelector *cur_sel = NULL ;
794 CRSimpleSel *cur_simple_sel = NULL ;
796 g_return_if_fail (a_this);
798 cr_doc_handler_get_ctxt (a_this, (gpointer *) (gpointer) & context);
802 cr_selector_dump (a_selector_list, stdout);
804 g_print ("/************************************************\n") ;
805 g_print (" *Parsing location information of the selector\n") ;
806 g_print (" ************************************************/\n") ;
808 for (cur_sel = a_selector_list;
810 cur_sel = cur_sel->next) {
811 for (cur_simple_sel = cur_sel->simple_sel ;
813 cur_simple_sel = cur_simple_sel->next) {
815 dump_location_annotated_simple_sel
822 end_selector (CRDocHandler * a_this,
823 CRSelector *a_selector_list)
825 struct SacContext *context = NULL;
827 g_return_if_fail (a_this);
829 cr_doc_handler_get_ctxt (a_this, (gpointer *) (gpointer) & context);
837 property (CRDocHandler *a_this,
840 gboolean a_is_important)
843 CRTerm *cur_term = NULL ;
845 str = (gchar*) cr_string_peek_raw_str (a_name) ;
848 g_print ("%s", str) ;
851 str = cr_term_to_string (a_expr) ;
853 g_print (" : %s;\n\n", str) ;
862 /*Now dump each part of the property declaration*/
865 g_print ("/************************************************\n") ;
866 g_print (" *Parsing location information of the property\n") ;
867 g_print (" ************************************************/\n") ;
869 str = (gchar*) cr_string_peek_raw_str (a_name) ;
871 g_print (" /*%s*/\n", str) ;
874 str = cr_parsing_location_to_string (&a_name->location, 0) ;
876 g_print (" /*%s*/\n", str) ;
881 for (cur_term = a_expr ;
883 cur_term = cur_term->next) {
884 str = cr_term_one_to_string (cur_term) ;
886 g_print (" /*%s*/\n", str) ;
890 str = cr_parsing_location_to_string
891 (&cur_term->location, 0) ;
893 g_print (" /*%s*/\n", str) ;
902 sac_parse_and_display_locations (guchar * a_file_uri)
904 enum CRStatus status = CR_OK;
905 CRDocHandler *sac_handler = NULL;
906 CRParser *parser = NULL;
908 g_return_val_if_fail (a_file_uri, CR_BAD_PARAM_ERROR);
910 parser = cr_parser_new_from_file (a_file_uri, CR_UTF_8);
912 cr_utils_trace_info ("parser instanciation failed");
915 sac_handler = cr_doc_handler_new ();
917 cr_utils_trace_info ("sac handler instanciation failed");
918 status = CR_OUT_OF_MEMORY_ERROR;
921 sac_handler->start_document = start_document ;
922 sac_handler->end_document = end_document ;
923 sac_handler->charset = charset ;
924 sac_handler->import_style = import_style ;
925 sac_handler->start_font_face = start_font_face ;
926 sac_handler->end_font_face = end_font_face ;
927 sac_handler->start_media = start_media ;
928 sac_handler->end_media = end_media ;
929 sac_handler->start_page = start_page ;
930 sac_handler->end_page = end_page ;
931 sac_handler->start_selector = start_selector;
932 sac_handler->end_selector = end_selector;
933 sac_handler->property = property ;
935 cr_parser_set_sac_handler (parser, sac_handler) ;
936 status = cr_parser_parse (parser) ;
940 cr_parser_destroy (parser);
947 main (int argc, char **argv)
949 struct Options options;
950 enum CRStatus status = CR_OK;
952 memset (&options, 0, sizeof (struct Options));
953 options.use_cssom = TRUE;
954 parse_cmd_line (argc, argv, &options);
956 if (options.show_version == TRUE) {
961 if (options.display_help == TRUE) {
965 if (options.use_cssom == TRUE) {
966 if (options.evaluate == TRUE) {
967 status = evaluate_selectors
969 options.author_sheet_path,
970 options.user_sheet_path,
971 options.ua_sheet_path, options.xpath);
972 } else if (options.css_files_list != NULL) {
973 status = cssom_parse (options.css_files_list[0]);
975 } else if (options.dump_location == TRUE) {
976 if (options.css_files_list) {
977 status = sac_parse_and_display_locations
978 (options.css_files_list[0]) ;