diff options
Diffstat (limited to 'catapult/common/py_vulcanize/third_party/rcssmin/rcssmin.c')
-rw-r--r-- | catapult/common/py_vulcanize/third_party/rcssmin/rcssmin.c | 1163 |
1 files changed, 1163 insertions, 0 deletions
diff --git a/catapult/common/py_vulcanize/third_party/rcssmin/rcssmin.c b/catapult/common/py_vulcanize/third_party/rcssmin/rcssmin.c new file mode 100644 index 00000000..a722fc27 --- /dev/null +++ b/catapult/common/py_vulcanize/third_party/rcssmin/rcssmin.c @@ -0,0 +1,1163 @@ +/* + * Copyright 2011 - 2014 + * Andr\xe9 Malo or his licensors, as applicable + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cext.h" +EXT_INIT_FUNC; + +#ifdef EXT3 +typedef Py_UNICODE rchar; +#else +typedef unsigned char rchar; +#endif +#define U(c) ((rchar)(c)) + +typedef struct { + const rchar *start; + const rchar *sentinel; + const rchar *tsentinel; + Py_ssize_t at_group; + int in_macie5; + int in_rule; + int keep_bang_comments; +} rcssmin_ctx_t; + +typedef enum { + NEED_SPACE_MAYBE = 0, + NEED_SPACE_NEVER +} need_space_flag; + + +#define RCSSMIN_DULL_BIT (1 << 0) +#define RCSSMIN_HEX_BIT (1 << 1) +#define RCSSMIN_ESC_BIT (1 << 2) +#define RCSSMIN_SPACE_BIT (1 << 3) +#define RCSSMIN_STRING_DULL_BIT (1 << 4) +#define RCSSMIN_NMCHAR_BIT (1 << 5) +#define RCSSMIN_URI_DULL_BIT (1 << 6) +#define RCSSMIN_PRE_CHAR_BIT (1 << 7) +#define RCSSMIN_POST_CHAR_BIT (1 << 8) + +static const unsigned short rcssmin_charmask[128] = { + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 28, 8, 21, 8, 8, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 28, 469, 4, 85, 85, 85, 85, 4, + 149, 277, 85, 469, 469, 117, 85, 84, + 115, 115, 115, 115, 115, 115, 115, 115, + 115, 115, 468, 340, 85, 469, 468, 85, + 84, 115, 115, 115, 115, 115, 115, 117, + 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 213, 4, 341, 85, 117, + 85, 115, 115, 115, 115, 115, 115, 117, + 117, 117, 117, 117, 117, 117, 117, 117, + 117, 117, 117, 117, 117, 116, 117, 117, + 117, 117, 117, 468, 85, 468, 85, 21 +}; + +#define RCSSMIN_IS_DULL(c) ((U(c) > 127) || \ + (rcssmin_charmask[U(c) & 0x7F] & RCSSMIN_DULL_BIT)) + +#define RCSSMIN_IS_HEX(c) ((U(c) <= 127) && \ + (rcssmin_charmask[U(c) & 0x7F] & RCSSMIN_HEX_BIT)) + +#define RCSSMIN_IS_ESC(c) ((U(c) > 127) || \ + (rcssmin_charmask[U(c) & 0x7F] & RCSSMIN_ESC_BIT)) + +#define RCSSMIN_IS_SPACE(c) ((U(c) <= 127) && \ + (rcssmin_charmask[U(c) & 0x7F] & RCSSMIN_SPACE_BIT)) + +#define RCSSMIN_IS_STRING_DULL(c) ((U(c) > 127) || \ + (rcssmin_charmask[U(c) & 0x7F] & RCSSMIN_STRING_DULL_BIT)) + +#define RCSSMIN_IS_NMCHAR(c) ((U(c) > 127) || \ + (rcssmin_charmask[U(c) & 0x7F] & RCSSMIN_NMCHAR_BIT)) + +#define RCSSMIN_IS_URI_DULL(c) ((U(c) > 127) || \ + (rcssmin_charmask[U(c) & 0x7F] & RCSSMIN_URI_DULL_BIT)) + +#define RCSSMIN_IS_PRE_CHAR(c) ((U(c) <= 127) && \ + (rcssmin_charmask[U(c) & 0x7F] & RCSSMIN_PRE_CHAR_BIT)) + +#define RCSSMIN_IS_POST_CHAR(c) ((U(c) <= 127) && \ + (rcssmin_charmask[U(c) & 0x7F] & RCSSMIN_POST_CHAR_BIT)) + + +static const rchar pattern_url[] = { + /*U('u'),*/ U('r'), U('l'), U('(') +}; + +static const rchar pattern_ie7[] = { + /*U('>'),*/ U('/'), U('*'), U('*'), U('/') +}; + +static const rchar pattern_media[] = { + U('m'), U('e'), U('d'), U('i'), U('a'), + U('M'), U('E'), U('D'), U('I'), U('A') +}; + +static const rchar pattern_document[] = { + U('d'), U('o'), U('c'), U('u'), U('m'), U('e'), U('n'), U('t'), + U('D'), U('O'), U('C'), U('U'), U('M'), U('E'), U('N'), U('T') +}; + +static const rchar pattern_supports[] = { + U('s'), U('u'), U('p'), U('p'), U('o'), U('r'), U('t'), U('s'), + U('S'), U('U'), U('P'), U('P'), U('O'), U('R'), U('T'), U('S') +}; + +static const rchar pattern_keyframes[] = { + U('k'), U('e'), U('y'), U('f'), U('r'), U('a'), U('m'), U('e'), U('s'), + U('K'), U('E'), U('Y'), U('F'), U('R'), U('A'), U('M'), U('E'), U('S') +}; + +static const rchar pattern_vendor_o[] = { + U('-'), U('o'), U('-'), + U('-'), U('O'), U('-') +}; + +static const rchar pattern_vendor_moz[] = { + U('-'), U('m'), U('o'), U('z'), U('-'), + U('-'), U('M'), U('O'), U('Z'), U('-') +}; + +static const rchar pattern_vendor_webkit[] = { + U('-'), U('w'), U('e'), U('b'), U('k'), U('i'), U('t'), U('-'), + U('-'), U('W'), U('E'), U('B'), U('K'), U('I'), U('T'), U('-') +}; + +static const rchar pattern_vendor_ms[] = { + U('-'), U('m'), U('s'), U('-'), + U('-'), U('M'), U('S'), U('-') +}; + +static const rchar pattern_first[] = { + U('f'), U('i'), U('r'), U('s'), U('t'), U('-'), U('l'), + U('F'), U('I'), U('R'), U('S'), U('T'), U('-'), U('L') +}; + +static const rchar pattern_line[] = { + U('i'), U('n'), U('e'), + U('I'), U('N'), U('E'), +}; + +static const rchar pattern_letter[] = { + U('e'), U('t'), U('t'), U('e'), U('r'), + U('E'), U('T'), U('T'), U('E'), U('R') +}; + +static const rchar pattern_macie5_init[] = { + U('/'), U('*'), U('\\'), U('*'), U('/') +}; + +static const rchar pattern_macie5_exit[] = { + U('/'), U('*'), U('*'), U('/') +}; + +/* + * Match a pattern (and copy immediately to target) + */ +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-overflow" +#endif +static int +copy_match(const rchar *pattern, const rchar *psentinel, + const rchar **source_, rchar **target_, rcssmin_ctx_t *ctx) +{ + const rchar *source = *source_; + rchar *target = *target_; + rchar c; + + while (pattern < psentinel + && source < ctx->sentinel && target < ctx->tsentinel + && ((c = *source++) == *pattern++)) + *target++ = c; + + *source_ = source; + *target_ = target; + + return (pattern == psentinel); +} +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#pragma GCC diagnostic pop +#endif + +#define MATCH(PAT, source, target, ctx) ( \ + copy_match(pattern_##PAT, \ + pattern_##PAT + sizeof(pattern_##PAT) / sizeof(rchar), \ + source, target, ctx) \ +) + + +/* + * Match a pattern (and copy immediately to target) - CI version + */ +static int +copy_imatch(const rchar *pattern, const rchar *psentinel, + const rchar **source_, rchar **target_, rcssmin_ctx_t *ctx) +{ + const rchar *source = *source_, *pstart = pattern; + rchar *target = *target_; + rchar c; + + while (pattern < psentinel + && source < ctx->sentinel && target < ctx->tsentinel + && ((c = *source++) == *pattern + || c == pstart[(pattern - pstart) + (psentinel - pstart)])) { + ++pattern; + *target++ = c; + } + + *source_ = source; + *target_ = target; + + return (pattern == psentinel); +} + +#define IMATCH(PAT, source, target, ctx) ( \ + copy_imatch(pattern_##PAT, \ + pattern_##PAT + sizeof(pattern_##PAT) / sizeof(rchar) / 2, \ + source, target, ctx) \ +) + + +/* + * Copy characters + */ +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-overflow" +#endif +static int +copy(const rchar *source, const rchar *sentinel, rchar **target_, + rcssmin_ctx_t *ctx) +{ + rchar *target = *target_; + + while (source < sentinel && target < ctx->tsentinel) + *target++ = *source++; + + *target_ = target; + + return (source == sentinel); +} +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#pragma GCC diagnostic pop +#endif + +#define COPY_PAT(PAT, target, ctx) ( \ + copy(pattern_##PAT, \ + pattern_##PAT + sizeof(pattern_##PAT) / sizeof(rchar), \ + target, ctx) \ +) + + +/* + * The ABORT macros work with known local variables! + */ +#define ABORT_(RET) do { \ + if (source < ctx->sentinel && !(target < ctx->tsentinel)) { \ + *source_ = source; \ + *target_ = target; \ + } \ + return RET; \ +} while(0) + + +#define CRAPPY_C90_COMPATIBLE_EMPTY +#define ABORT ABORT_(CRAPPY_C90_COMPATIBLE_EMPTY) +#define RABORT(RET) ABORT_((RET)) + + +/* + * Copy escape + */ +static void +copy_escape(const rchar **source_, rchar **target_, rcssmin_ctx_t *ctx) +{ + const rchar *source = *source_, *hsentinel; + rchar *target = *target_; + rchar c; + + *target++ = U('\\'); + *target_ = target; + + if (source < ctx->sentinel && target < ctx->tsentinel) { + c = *source++; + if (RCSSMIN_IS_ESC(c)) { + *target++ = c; + } + else if (RCSSMIN_IS_HEX(c)) { + *target++ = c; + + /* 6 hex chars max, one we got already */ + if (ctx->sentinel - source > 5) + hsentinel = source + 5; + else + hsentinel = ctx->sentinel; + + while (source < hsentinel && target < ctx->tsentinel + && (c = *source, RCSSMIN_IS_HEX(c))) { + ++source; + *target++ = c; + } + + /* One optional space after */ + if (source < ctx->sentinel && target < ctx->tsentinel) { + if (source == hsentinel) + c = *source; + if (RCSSMIN_IS_SPACE(c)) { + ++source; + *target++ = U(' '); + if (c == U('\r') && source < ctx->sentinel + && *source == U('\n')) + ++source; + } + } + } + } + + *target_ = target; + *source_ = source; +} + + +/* + * Copy string + */ +static void +copy_string(const rchar **source_, rchar **target_, rcssmin_ctx_t *ctx) +{ + const rchar *source = *source_; + rchar *target = *target_; + rchar c, quote = source[-1]; + + *target++ = quote; + *target_ = target; + + while (source < ctx->sentinel && target < ctx->tsentinel) { + c = *target++ = *source++; + if (RCSSMIN_IS_STRING_DULL(c)) + continue; + + switch (c) { + case U('\''): case U('"'): + if (c == quote) { + *target_ = target; + *source_ = source; + return; + } + continue; + + case U('\\'): + if (source < ctx->sentinel && target < ctx->tsentinel) { + c = *source++; + switch (c) { + case U('\r'): + if (source < ctx->sentinel && *source == U('\n')) + ++source; + /* fall through */ + + case U('\n'): case U('\f'): + --target; + break; + + default: + *target++ = c; + } + } + continue; + } + break; /* forbidden characters */ + } + + ABORT; +} + + +/* + * Copy URI string + */ +static int +copy_uri_string(const rchar **source_, rchar **target_, rcssmin_ctx_t *ctx) +{ + const rchar *source = *source_; + rchar *target = *target_; + rchar c, quote = source[-1]; + + *target++ = quote; + *target_ = target; + + while (source < ctx->sentinel && target < ctx->tsentinel) { + c = *source++; + if (RCSSMIN_IS_SPACE(c)) + continue; + *target++ = c; + if (RCSSMIN_IS_STRING_DULL(c)) + continue; + + switch (c) { + case U('\''): case U('"'): + if (c == quote) { + *target_ = target; + *source_ = source; + return 0; + } + continue; + + case U('\\'): + if (source < ctx->sentinel && target < ctx->tsentinel) { + c = *source; + switch (c) { + case U('\r'): + if ((source + 1) < ctx->sentinel && source[1] == U('\n')) + ++source; + /* fall through */ + + case U('\n'): case U('\f'): + --target; + ++source; + break; + + default: + --target; + copy_escape(&source, &target, ctx); + } + } + continue; + } + + break; /* forbidden characters */ + } + + RABORT(-1); +} + + +/* + * Copy URI (unquoted) + */ +static int +copy_uri_unquoted(const rchar **source_, rchar **target_, rcssmin_ctx_t *ctx) +{ + const rchar *source = *source_; + rchar *target = *target_; + rchar c; + + *target++ = source[-1]; + *target_ = target; + + while (source < ctx->sentinel && target < ctx->tsentinel) { + c = *source++; + if (RCSSMIN_IS_SPACE(c)) + continue; + *target++ = c; + if (RCSSMIN_IS_URI_DULL(c)) + continue; + + switch (c) { + + case U(')'): + *target_ = target - 1; + *source_ = source - 1; + return 0; + + case U('\\'): + if (source < ctx->sentinel && target < ctx->tsentinel) { + c = *source; + switch (c) { + case U('\r'): + if ((source + 1) < ctx->sentinel && source[1] == U('\n')) + ++source; + /* fall through */ + + case U('\n'): case U('\f'): + --target; + ++source; + break; + + default: + --target; + copy_escape(&source, &target, ctx); + } + } + continue; + } + + break; /* forbidden characters */ + } + + RABORT(-1); +} + + +/* + * Copy url + */ +static void +copy_url(const rchar **source_, rchar **target_, rcssmin_ctx_t *ctx) +{ + const rchar *source = *source_; + rchar *target = *target_; + rchar c; + + *target++ = U('u'); + *target_ = target; + + /* Must not be inside an identifier */ + if ((source != ctx->start + 1) && RCSSMIN_IS_NMCHAR(source[-2])) + return; + + if (!MATCH(url, &source, &target, ctx) + || !(source < ctx->sentinel && target < ctx->tsentinel)) + ABORT; + + while (source < ctx->sentinel && RCSSMIN_IS_SPACE(*source)) + ++source; + + if (!(source < ctx->sentinel)) + ABORT; + + c = *source++; + switch (c) { + case U('"'): case U('\''): + if (copy_uri_string(&source, &target, ctx) == -1) + ABORT; + + while (source < ctx->sentinel && RCSSMIN_IS_SPACE(*source)) + ++source; + break; + + default: + if (copy_uri_unquoted(&source, &target, ctx) == -1) + ABORT; + } + + if (!(source < ctx->sentinel && target < ctx->tsentinel)) + ABORT; + + if ((*target++ = *source++) != U(')')) + ABORT; + + *target_ = target; + *source_ = source; +} + + +/* + * Copy @-group + */ +static void +copy_at_group(const rchar **source_, rchar **target_, rcssmin_ctx_t *ctx) +{ + const rchar *source = *source_; + rchar *target = *target_; + + *target++ = U('@'); + *target_ = target; + +#define REMATCH(what) ( \ + source = *source_, \ + target = *target_, \ + IMATCH(what, &source, &target, ctx) \ +) +#define CMATCH(what) IMATCH(what, &source, &target, ctx) + + if (( !CMATCH(media) + && !REMATCH(supports) + && !REMATCH(document) + && !REMATCH(keyframes) + && !(REMATCH(vendor_webkit) && CMATCH(keyframes)) + && !(REMATCH(vendor_moz) && CMATCH(keyframes)) + && !(REMATCH(vendor_o) && CMATCH(keyframes)) + && !(REMATCH(vendor_ms) && CMATCH(keyframes))) + || !(source < ctx->sentinel && target < ctx->tsentinel) + || RCSSMIN_IS_NMCHAR(*source)) + ABORT; + +#undef CMATCH +#undef REMATCH + + ++ctx->at_group; + + *target_ = target; + *source_ = source; +} + + +/* + * Skip space + */ +static const rchar * +skip_space(const rchar *source, rcssmin_ctx_t *ctx) +{ + const rchar *begin = source; + int res; + rchar c; + + while (source < ctx->sentinel) { + c = *source; + if (RCSSMIN_IS_SPACE(c)) { + ++source; + continue; + } + else if (c == U('/')) { + ++source; + if (!(source < ctx->sentinel && *source == U('*'))) { + --source; + break; + } + ++source; + res = 0; + while (source < ctx->sentinel) { + c = *source++; + if (c != U('*')) + continue; + if (!(source < ctx->sentinel)) + return begin; + if (*source != U('/')) + continue; + + /* Comment complete */ + ++source; + res = 1; + break; + } + if (!res) + return begin; + + continue; + } + + break; + } + + return source; +} + + +/* + * Copy space + */ +static void +copy_space(const rchar **source_, rchar **target_, rcssmin_ctx_t *ctx, + need_space_flag need_space) +{ + const rchar *source = *source_, *end, *comment; + rchar *target = *target_; + int res; + rchar c; + + --source; + if (need_space == NEED_SPACE_MAYBE + && source > ctx->start + && !RCSSMIN_IS_PRE_CHAR(source[-1]) + && (end = skip_space(source, ctx)) < ctx->sentinel + && (!RCSSMIN_IS_POST_CHAR(*end) + || (*end == U(':') && !ctx->in_rule && !ctx->at_group))) { + + if (!(target < ctx->tsentinel)) + ABORT; + *target++ = U(' '); + } + + while (source < ctx->sentinel) { + switch (c = *source) { + + /* comment */ + case U('/'): + comment = source++; + if (!((source < ctx->sentinel && *source == U('*')))) { + --source; + break; + } + ++source; + res = 0; + while (source < ctx->sentinel) { + c = *source++; + if (c != U('*')) + continue; + if (!(source < ctx->sentinel)) + ABORT; + if (*source != U('/')) + continue; + + /* Comment complete */ + ++source; + res = 1; + + if (ctx->keep_bang_comments && comment[2] == U('!')) { + ctx->in_macie5 = (source[-3] == U('\\')); + if (!copy(comment, source, &target, ctx)) + ABORT; + } + else if (source[-3] == U('\\')) { + if (!ctx->in_macie5) { + if (!COPY_PAT(macie5_init, &target, ctx)) + ABORT; + } + ctx->in_macie5 = 1; + } + else if (ctx->in_macie5) { + if (!COPY_PAT(macie5_exit, &target, ctx)) + ABORT; + ctx->in_macie5 = 0; + } + /* else don't copy anything */ + break; + } + if (!res) + ABORT; + continue; + + /* space */ + case U(' '): case U('\t'): case U('\r'): case U('\n'): case U('\f'): + ++source; + continue; + } + + break; + } + + *source_ = source; + *target_ = target; +} + + +/* + * Copy space if comment + */ +static int +copy_space_comment(const rchar **source_, rchar **target_, + rcssmin_ctx_t *ctx, need_space_flag need_space) +{ + const rchar *source = *source_; + rchar *target = *target_; + + if (source < ctx->sentinel && *source == U('*')) { + copy_space(source_, target_, ctx, need_space); + if (*source_ > source) + return 0; + } + if (!(target < ctx->tsentinel)) + RABORT(-1); + + *target++ = source[-1]; + + /* *source_ = source; <-- unchanged */ + *target_ = target; + + return -1; +} + + +/* + * Copy space if exists + */ +static int +copy_space_optional(const rchar **source_, rchar **target_, + rcssmin_ctx_t *ctx) +{ + const rchar *source = *source_; + + if (!(source < ctx->sentinel)) + return -1; + + if (*source == U('/')) { + *source_ = source + 1; + return copy_space_comment(source_, target_, ctx, NEED_SPACE_NEVER); + } + else if (RCSSMIN_IS_SPACE(*source)) { + *source_ = source + 1; + copy_space(source_, target_, ctx, NEED_SPACE_NEVER); + return 0; + } + + return -1; +} + + +/* + * Copy :first-line|letter + */ +static void +copy_first(const rchar **source_, rchar **target_, rcssmin_ctx_t *ctx) +{ + const rchar *source = *source_, *next, *source_fork; + rchar *target = *target_, *target_fork; + + *target++ = U(':'); + *target_ = target; + + if (!IMATCH(first, &source, &target, ctx) + || !(source < ctx->sentinel && target < ctx->tsentinel)) + ABORT; + + source_fork = source; + target_fork = target; + + if (!IMATCH(line, &source, &target, ctx)) { + source = source_fork; + target = target_fork; + + if (!IMATCH(letter, &source, &target, ctx) + || !(source < ctx->sentinel && target < ctx->tsentinel)) + ABORT; + } + + next = skip_space(source, ctx); + if (!(next < ctx->sentinel && target < ctx->tsentinel + && (*next == U('{') || *next == U(',')))) + ABORT; + + *target++ = U(' '); + *target_ = target; + *source_ = source; + (void)copy_space_optional(source_, target_, ctx); +} + + +/* + * Copy IE7 hack + */ +static void +copy_ie7hack(const rchar **source_, rchar **target_, rcssmin_ctx_t *ctx) +{ + const rchar *source = *source_; + rchar *target = *target_; + + *target++ = U('>'); + *target_ = target; + + if (ctx->in_rule || ctx->at_group) + return; /* abort */ + + if (!MATCH(ie7, &source, &target, ctx)) + ABORT; + + ctx->in_macie5 = 0; + + *target_ = target; + *source_ = source; + + (void)copy_space_optional(source_, target_, ctx); +} + + +/* + * Copy semicolon; miss out duplicates or even this one (before '}') + */ +static void +copy_semicolon(const rchar **source_, rchar **target_, rcssmin_ctx_t *ctx) +{ + const rchar *source = *source_, *begin, *end; + rchar *target = *target_; + + begin = source; + while (source < ctx->sentinel) { + end = skip_space(source, ctx); + if (!(end < ctx->sentinel)) { + if (!(target < ctx->tsentinel)) + ABORT; + *target++ = U(';'); + break; + } + switch (*end) { + case U(';'): + source = end + 1; + continue; + + case U('}'): + if (ctx->in_rule) + break; + + /* fall through */ + default: + if (!(target < ctx->tsentinel)) + ABORT; + *target++ = U(';'); + break; + } + + break; + } + + source = begin; + *target_ = target; + while (source < ctx->sentinel) { + if (*source == U(';')) { + ++source; + continue; + } + + if (copy_space_optional(&source, target_, ctx) == 0) + continue; + + break; + } + + *source_ = source; +} + + +/* + * Main function + * + * The return value determines the result length (kept in the target buffer). + * However, if the target buffer is too small, the return value is greater + * than tlength. The difference to tlength is the number of unconsumed source + * characters at the time the buffer was full. In this case you should resize + * the target buffer to the return value and call rcssmin again. Repeat as + * often as needed. + */ +static Py_ssize_t +rcssmin(const rchar *source, rchar *target, Py_ssize_t slength, + Py_ssize_t tlength, int keep_bang_comments) +{ + rcssmin_ctx_t ctx_, *ctx = &ctx_; + const rchar *tstart = target; + rchar c; + + ctx->start = source; + ctx->sentinel = source + slength; + ctx->tsentinel = target + tlength; + ctx->at_group = 0; + ctx->in_macie5 = 0; + ctx->in_rule = 0; + ctx->keep_bang_comments = keep_bang_comments; + + while (source < ctx->sentinel && target < ctx->tsentinel) { + c = *source++; + if (RCSSMIN_IS_DULL(c)) { + *target++ = c; + continue; + } + else if (RCSSMIN_IS_SPACE(c)) { + copy_space(&source, &target, ctx, NEED_SPACE_MAYBE); + continue; + } + + switch (c) { + + /* Escape */ + case U('\\'): + copy_escape(&source, &target, ctx); + continue; + + /* String */ + case U('"'): case U('\''): + copy_string(&source, &target, ctx); + continue; + + /* URL */ + case U('u'): + copy_url(&source, &target, ctx); + continue; + + /* IE7hack */ + case U('>'): + copy_ie7hack(&source, &target, ctx); + continue; + + /* @-group */ + case U('@'): + copy_at_group(&source, &target, ctx); + continue; + + /* ; */ + case U(';'): + copy_semicolon(&source, &target, ctx); + continue; + + /* :first-line|letter followed by [{,] */ + /* (apparently needed for IE6) */ + case U(':'): + copy_first(&source, &target, ctx); + continue; + + /* { */ + case U('{'): + if (ctx->at_group) + --ctx->at_group; + else + ++ctx->in_rule; + *target++ = c; + continue; + + /* } */ + case U('}'): + if (ctx->in_rule) + --ctx->in_rule; + *target++ = c; + continue; + + /* space starting with comment */ + case U('/'): + (void)copy_space_comment(&source, &target, ctx, NEED_SPACE_MAYBE); + continue; + + /* Fallback: copy character. Better safe than sorry. Should not be + * reached, though */ + default: + *target++ = c; + continue; + } + } + + return + (Py_ssize_t)(target - tstart) + (Py_ssize_t)(ctx->sentinel - source); +} + + +PyDoc_STRVAR(rcssmin_cssmin__doc__, +"cssmin(style, keep_bang_comments=False)\n\ +\n\ +Minify CSS.\n\ +\n\ +:Note: This is a hand crafted C implementation built on the regex\n\ + semantics.\n\ +\n\ +:Parameters:\n\ + `style` : ``str``\n\ + CSS to minify\n\ +\n\ +:Return: Minified style\n\ +:Rtype: ``str``"); + +static PyObject * +rcssmin_cssmin(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *style, *keep_bang_comments_ = NULL, *result; + static char *kwlist[] = {"style", "keep_bang_comments", NULL}; + Py_ssize_t rlength, slength, length; + int keep_bang_comments; +#ifdef EXT2 + int uni; +#define UOBJ "O" +#endif +#ifdef EXT3 +#define UOBJ "U" +#endif + + if (!PyArg_ParseTupleAndKeywords(args, kwds, UOBJ "|O", kwlist, + &style, &keep_bang_comments_)) + return NULL; + + if (!keep_bang_comments_) + keep_bang_comments = 0; + else { + keep_bang_comments = PyObject_IsTrue(keep_bang_comments_); + if (keep_bang_comments == -1) + return NULL; + } + +#ifdef EXT2 + if (PyUnicode_Check(style)) { + if (!(style = PyUnicode_AsUTF8String(style))) + return NULL; + uni = 1; + } + else { + if (!(style = PyObject_Str(style))) + return NULL; + uni = 0; + } +#endif + +#ifdef EXT3 + Py_INCREF(style); +#define PyString_GET_SIZE PyUnicode_GET_SIZE +#define PyString_AS_STRING PyUnicode_AS_UNICODE +#define _PyString_Resize PyUnicode_Resize +#define PyString_FromStringAndSize PyUnicode_FromUnicode +#endif + + rlength = slength = PyString_GET_SIZE(style); + +again: + if (!(result = PyString_FromStringAndSize(NULL, rlength))) { + Py_DECREF(style); + return NULL; + } + Py_BEGIN_ALLOW_THREADS + length = rcssmin((rchar *)PyString_AS_STRING(style), + (rchar *)PyString_AS_STRING(result), + slength, rlength, keep_bang_comments); + Py_END_ALLOW_THREADS + + if (length > rlength) { + Py_DECREF(result); + rlength = length; + goto again; + } + + Py_DECREF(style); + if (length < 0) { + Py_DECREF(result); + return NULL; + } + if (length != rlength && _PyString_Resize(&result, length) == -1) + return NULL; + +#ifdef EXT2 + if (uni) { + style = PyUnicode_DecodeUTF8(PyString_AS_STRING(result), + PyString_GET_SIZE(result), "strict"); + Py_DECREF(result); + if (!style) + return NULL; + result = style; + } +#endif + return result; +} + +/* ------------------------ BEGIN MODULE DEFINITION ------------------------ */ + +EXT_METHODS = { + {"cssmin", + (PyCFunction)rcssmin_cssmin, METH_VARARGS | METH_KEYWORDS, + rcssmin_cssmin__doc__}, + + {NULL} /* Sentinel */ +}; + +PyDoc_STRVAR(EXT_DOCS_VAR, +"C implementation of rcssmin\n\ +===========================\n\ +\n\ +C implementation of rcssmin."); + + +EXT_DEFINE(EXT_MODULE_NAME, EXT_METHODS_VAR, EXT_DOCS_VAR); + +EXT_INIT_FUNC { + PyObject *m; + + /* Create the module and populate stuff */ + if (!(m = EXT_CREATE(&EXT_DEFINE_VAR))) + EXT_INIT_ERROR(NULL); + + EXT_ADD_UNICODE(m, "__author__", "Andr\xe9 Malo", "latin-1"); + EXT_ADD_STRING(m, "__docformat__", "restructuredtext en"); + + EXT_INIT_RETURN(m); +} + +/* ------------------------- END MODULE DEFINITION ------------------------- */ |