xref: /plugin/pagecss/vendor/csstidy-2.2.1/bin/pcsstidy (revision 7d6669007238fef7e8728f167d637ed824899eb0)
1#!/usr/bin/env php
2<?php
3/**
4 * CSSTidy - Command Line Interface (CLI)
5 *
6 * This file is part of CSSTidy.
7 *
8 *   CSSTidy is free software; you can redistribute it and/or modify
9 *   it under the terms of the GNU Lesser General Public License as published by
10 *   the Free Software Foundation; either version 2.1 of the License, or
11 *   (at your option) any later version.
12 *
13 *   CSSTidy is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU Lesser General Public License for more details.
17 *
18 *   You should have received a copy of the GNU Lesser General Public License
19 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 *
21 * @license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
22 * @package csstidy
23 * @author Florian Schmitz (floele at gmail dot com) 2005-2007
24 * @author Brett Zamir (brettz9 at yahoo dot com) 2007
25 * @author Nikolay Matsievsky (speed at webo dot name) 2009-2010
26 * @author Christopher Finke (cfinke at gmail.com) 2012
27 * @author Etienne Desautels (etienne dot desautels at gmail dot com) 2012
28 * @author Cedric Morin (cedric at yterium dot com) 2010-2019
29 */
30
31
32error_reporting(E_ALL);
33
34if (version_compare(PHP_VERSION, '5.4')<0){
35	die('Requires PHP 5.4 or above');
36}
37
38/**
39 * Contains the Parser class
40 *
41 * @version 1.6.5
42 */
43require dirname(__DIR__) . DIRECTORY_SEPARATOR . 'class.csstidy.php';
44$csstidy = new csstidy();
45$dumpTree = false;
46$default_media = '';
47
48/**
49 * Return the usage message to sdterr and exit with return code 1
50 * @access private
51 * @version 1.0
52 */
53function _show_usage(){
54	$exe = $GLOBALS['argv'][0];
55	$version = "Version " . $GLOBALS['csstidy']->version;
56
57	$HELP = <<<EOT
58Usage: $exe [options] [input-file]
59$version
60
61Options include:
62
63-h, --help                   Show this message
64-t=template                  Set the output template [default|filename|low|high|highest]
65--case_properties            [1|2|0]
66--compress_colors            [true|false]
67--compress_font-weight       [true|false]
68--default_media              Set the default media rule to apply to the CSS
69--css_level                  [CSS3.0|CSS2.1|CSS2.0|CSS1.0]
70--discard_invalid_properties [false|true]
71--discard_invalid_selectors  [false|true]
72--lowercase_s                [false|true]
73--merge_selectors            [0|1|2]
74--optimise_shorthands        [1|2|0]
75--preserve_css               [false|true]
76--remove_bslash              [true|false]
77--remove_last_semicolon      [false|true]
78--reverse_left_and_right     [false|true]
79--sort_properties            [false|true]
80--sort_selectors             [false|true]
81--timestamp                  [false|true]
82
83-T                  Dump formatted parse tree
84-v, --version       Print the version
85
86EOT;
87	exit($HELP);
88}
89
90
91/**
92 * Parse argument
93 *
94 * @param integer $i
95 * @param array $options
96 *
97 * @return string|null
98 */
99function parseArgument(&$i, $options){
100	global $argc;
101	global $argv;
102
103	if (!preg_match('/^(?:' . implode('|', (array)$options) . ')=?(.*)/', $argv[$i], $matches)){
104		return;
105	}
106
107	if (strlen($matches[1])){
108		return $matches[1];
109	}
110
111	if ($i+1<$argc){
112		$i++;
113
114		return $argv[$i];
115	}
116}
117
118/**
119 * Cast Arguments relative to expected format
120 * @param mixed $value
121 * @param string $format
122 * @param array $expected
123 * @return mixed
124 */
125function castArgument($value, $format, $expected = null) {
126	if ($expected and !in_array($value, $expected)) {
127		if (in_array('filename', $expected)) {
128			$fileName = castArgument($value, $format);
129			if (!file_exists($fileName)) {
130				_show_usage();
131			}
132		}
133		else {
134			_show_usage();
135		}
136	}
137	switch (strtolower($format)) {
138		case "int":
139			$value = intval($value);
140			break;
141		case "str":
142			$value = trim(strval($value));
143			foreach (["'", '"'] as $q) {
144				if (substr($value, 0, 1) == $q && substr($value, -1, 1) == $q) {
145					$value = substr($value, 1, -1);
146					$value = str_replace("\\" . $q, $q, $value);
147					break;
148				}
149			}
150			break;
151		case "bool":
152			$value = strtolower(trim(strval($value)));
153			if (!in_array($value, [ '0', '1', 'on', 'off', 'true', 'false'])) {
154				_show_usage();
155			}
156			$value = (in_array(strtolower($value), ['1', 'on', 'true']) ? true : false);
157			break;
158	}
159	return $value;
160}
161
162// Check if there's at least one argument
163if ($argc<1){
164	_show_usage();
165}
166
167// Set the settings defined on the CLI
168$settings = [
169	'case_properties' => ['format' => 'int', 'expected' => [0, 1, 2]],
170	'compress_colors' => ['format' => 'bool'],
171	'compress_font-weight' => ['format' => 'bool'],
172	'css_level' => ['format' => 'str', 'expected' => ['CSS3.0', 'CSS2.1', 'CSS2.0', 'CSS1.0']],
173	'discard_invalid_properties' => ['format' => 'bool'],
174	'discard_invalid_selectors' => ['format' => 'bool'],
175	'merge_selectors' => ['format' => 'int', 'expected' => [0, 1, 2]],
176	'lowercase_s' => ['format' => 'bool'],
177	'optimise_shorthands' => ['format' => 'int', 'expected' => [0, 1, 2]],
178	'preserve_css' => ['format' => 'bool'],
179	'remove_bslash' => ['format' => 'bool'],
180	'remove_last_semicolon' => ['format' => 'bool', 'setting' => 'remove_last_;'],
181	'reverse_left_and_right' => ['format' => 'bool'],
182	'sort_properties' => ['format' => 'bool'],
183	'sort_selectors' => ['format' => 'bool'],
184	'template' => ['format' => 'str', 'short' => 't', 'expected' => ['default', 'filename', 'low', 'high', 'highest']],
185	'timestamp' => ['format' => 'bool'],
186];
187
188foreach ($settings as $option_name => $desc) {
189	$options_list = [ "--$option_name" ];
190	if (isset($desc['short'])) {
191		$options_list[] = "-" . $desc['short'];
192	}
193	$settings[$option_name]['options_list'] = $options_list;
194}
195
196
197for ($i = 1; $i<$argc; $i++){
198	if ($argv[$i]==='-h' || $argv[$i]==='--help'){
199		_show_usage();
200	}
201
202	if ($argv[$i]==='-v' || $argv[$i]==='--version'){
203		exit($csstidy->version . "\n");
204	}
205	$value = parseArgument($i, ['--default_media']);
206	if (isset($value)) {
207		$value = castArgument($value, 'str');
208		$default_media = trim($value);
209		if (strpos($default_media, '@') === false) {
210			$default_media = '@media ' . $default_media;
211		}
212	}
213
214	foreach ($settings as $option_name => $desc) {
215		$value = parseArgument($i, $desc['options_list']);
216		if (isset($value)){
217			if (isset($desc['format'])) {
218				$value = castArgument($value, $desc['format'], isset($desc['expected']) ? $desc['expected'] : null);
219			}
220			$setting_name = $option_name;
221			if (isset($desc['setting'])) {
222				$setting_name = $desc['setting'];
223			}
224			$csstidy->set_cfg($setting_name, $value);
225			continue 2;
226		}
227	}
228
229	if ($argv[$i]==='-T'){
230		$dumpTree = true;
231		continue;
232	}
233
234	if (file_exists($argv[$i])){
235		$inputFile = $argv[$i];
236		continue;
237	}
238}
239
240if (!$inputFile) {
241	$inputFile = 'php://stdin';
242}
243
244// Get the data
245$css_code = file_get_contents($inputFile);
246
247// Exit on error when reading the data
248if ($css_code===false){
249	file_put_contents('php://stderr', "The file \"" . $inputFile . "\" does not exist.\n");
250	exit(1);
251}
252
253// Parse the CSS
254$csstidy->parse($css_code);
255if ($dumpTree){
256	var_dump($csstidy->css);
257} else {
258	// Optimize and output the CSS file
259	echo $csstidy->print->plain($default_media);
260	echo "\n";
261}