From 47c79b79d57d017d590e9423cf1a93ed6bdfad38 Mon Sep 17 00:00:00 2001 From: Grigori Goronzy Date: Mon, 7 Sep 2015 23:44:41 +0200 Subject: NIH: add locale-independent string functions OS or platform-specific locale independent functions are painful to use and/or not available, so roll our own. Not great but the least painful and least intrusive. v2: fix indexing, use static inline --- libass/Makefile.am | 3 ++- libass/ass.c | 45 +++++++++++++++---------------- libass/ass_font.c | 1 - libass/ass_fontselect.c | 8 +++--- libass/ass_library.c | 1 + libass/ass_string.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ libass/ass_string.h | 40 +++++++++++++++++++++++++++ libass/ass_strtod.c | 8 +++--- libass/ass_utils.c | 35 ++++++++++++------------ 9 files changed, 162 insertions(+), 51 deletions(-) create mode 100644 libass/ass_string.c create mode 100644 libass/ass_string.h diff --git a/libass/Makefile.am b/libass/Makefile.am index 2cf6354..cef1291 100644 --- a/libass/Makefile.am +++ b/libass/Makefile.am @@ -29,7 +29,8 @@ libass_la_SOURCES = ass.c ass_cache.c ass_font.c ass_fontselect.c ass_render.c \ ass_library.h ass_types.h ass_utils.h ass_drawing.c \ ass_drawing.h ass_cache_template.h ass_render.h \ ass_parse.c ass_parse.h ass_render_api.c ass_shaper.c \ - ass_shaper.h ass_strtod.c ass_fontconfig.c ass_fontconfig.h + ass_shaper.h ass_strtod.c ass_fontconfig.c ass_fontconfig.h \ + ass_string.h ass_string.c libass_la_LDFLAGS = -no-undefined -version-info $(LIBASS_LT_CURRENT):$(LIBASS_LT_REVISION):$(LIBASS_LT_AGE) libass_la_LDFLAGS += -export-symbols $(srcdir)/libass.sym diff --git a/libass/ass.c b/libass/ass.c index b51cf2e..a0a6d47 100644 --- a/libass/ass.c +++ b/libass/ass.c @@ -21,14 +21,12 @@ #include #include #include -#include #include #include #include #include #include #include -#include #ifdef CONFIG_ICONV #include @@ -37,6 +35,7 @@ #include "ass.h" #include "ass_utils.h" #include "ass_library.h" +#include "ass_string.h" #define ass_atof(STR) (ass_strtod((STR),NULL)) @@ -212,7 +211,7 @@ static int numpad2align(int val) #define ALIAS(alias,name) \ - if (strcasecmp(tname, #alias) == 0) {tname = #name;} + if (ass_strcasecmp(tname, #alias) == 0) {tname = #name;} /* One section started with PARSE_START and PARSE_END parses a single token * (contained in the variable named token) for the header indicated by the @@ -228,16 +227,16 @@ static int numpad2align(int val) #define PARSE_END } #define ANYVAL(name,func) \ - } else if (strcasecmp(tname, #name) == 0) { \ + } else if (ass_strcasecmp(tname, #name) == 0) { \ target->name = func(token); #define STRVAL(name) \ - } else if (strcasecmp(tname, #name) == 0) { \ + } else if (ass_strcasecmp(tname, #name) == 0) { \ if (target->name != NULL) free(target->name); \ target->name = strdup(token); #define STARREDSTRVAL(name) \ - } else if (strcasecmp(tname, #name) == 0) { \ + } else if (ass_strcasecmp(tname, #name) == 0) { \ if (target->name != NULL) free(target->name); \ while (*token == '*') ++token; \ target->name = strdup(token); @@ -246,11 +245,11 @@ static int numpad2align(int val) #define INTVAL(name) ANYVAL(name,atoi) #define FPVAL(name) ANYVAL(name,ass_atof) #define TIMEVAL(name) \ - } else if (strcasecmp(tname, #name) == 0) { \ + } else if (ass_strcasecmp(tname, #name) == 0) { \ target->name = string2timecode(track->library, token); #define STYLEVAL(name) \ - } else if (strcasecmp(tname, #name) == 0) { \ + } else if (ass_strcasecmp(tname, #name) == 0) { \ target->name = lookup_style(track, token); static char *next_token(char **str) @@ -309,7 +308,7 @@ static int process_event_tail(ASS_Track *track, ASS_Event *event, while (1) { NEXT(q, tname); - if (strcasecmp(tname, "Text") == 0) { + if (ass_strcasecmp(tname, "Text") == 0) { char *last; event->Text = strdup(p); if (*event->Text != 0) { @@ -362,19 +361,19 @@ void ass_process_force_style(ASS_Track *track) *eq = '\0'; token = eq + 1; - if (!strcasecmp(*fs, "PlayResX")) + if (!ass_strcasecmp(*fs, "PlayResX")) track->PlayResX = atoi(token); - else if (!strcasecmp(*fs, "PlayResY")) + else if (!ass_strcasecmp(*fs, "PlayResY")) track->PlayResY = atoi(token); - else if (!strcasecmp(*fs, "Timer")) + else if (!ass_strcasecmp(*fs, "Timer")) track->Timer = ass_atof(token); - else if (!strcasecmp(*fs, "WrapStyle")) + else if (!ass_strcasecmp(*fs, "WrapStyle")) track->WrapStyle = atoi(token); - else if (!strcasecmp(*fs, "ScaledBorderAndShadow")) + else if (!ass_strcasecmp(*fs, "ScaledBorderAndShadow")) track->ScaledBorderAndShadow = parse_bool(token); - else if (!strcasecmp(*fs, "Kerning")) + else if (!ass_strcasecmp(*fs, "Kerning")) track->Kerning = parse_bool(token); - else if (!strcasecmp(*fs, "YCbCr Matrix")) + else if (!ass_strcasecmp(*fs, "YCbCr Matrix")) track->YCbCrMatrix = parse_ycbcr_matrix(token); dt = strrchr(*fs, '.'); @@ -388,7 +387,7 @@ void ass_process_force_style(ASS_Track *track) } for (sid = 0; sid < track->n_styles; ++sid) { if (style == NULL - || strcasecmp(track->styles[sid].Name, style) == 0) { + || ass_strcasecmp(track->styles[sid].Name, style) == 0) { target = track->styles + sid; PARSE_START STRVAL(FontName) @@ -575,7 +574,7 @@ static int process_info_line(ASS_Track *track, char *str) track->YCbCrMatrix = parse_ycbcr_matrix(str + 13); } else if (!strncmp(str, "Language:", 9)) { char *p = str + 9; - while (*p && isspace(*p)) p++; + while (*p && ass_isspace(*p)) p++; track->Language = strndup(p, 2); } return 0; @@ -743,17 +742,17 @@ static int process_fonts_line(ASS_Track *track, char *str) */ static int process_line(ASS_Track *track, char *str) { - if (!strncasecmp(str, "[Script Info]", 13)) { + if (!ass_strncasecmp(str, "[Script Info]", 13)) { track->parser_priv->state = PST_INFO; - } else if (!strncasecmp(str, "[V4 Styles]", 11)) { + } else if (!ass_strncasecmp(str, "[V4 Styles]", 11)) { track->parser_priv->state = PST_STYLES; track->track_type = TRACK_TYPE_SSA; - } else if (!strncasecmp(str, "[V4+ Styles]", 12)) { + } else if (!ass_strncasecmp(str, "[V4+ Styles]", 12)) { track->parser_priv->state = PST_STYLES; track->track_type = TRACK_TYPE_ASS; - } else if (!strncasecmp(str, "[Events]", 8)) { + } else if (!ass_strncasecmp(str, "[Events]", 8)) { track->parser_priv->state = PST_EVENTS; - } else if (!strncasecmp(str, "[Fonts]", 7)) { + } else if (!ass_strncasecmp(str, "[Fonts]", 7)) { track->parser_priv->state = PST_FONTS; } else { switch (track->parser_priv->state) { diff --git a/libass/ass_font.c b/libass/ass_font.c index 5b0131f..67f8eec 100644 --- a/libass/ass_font.c +++ b/libass/ass_font.c @@ -25,7 +25,6 @@ #include FT_GLYPH_H #include FT_TRUETYPE_TABLES_H #include FT_OUTLINE_H -#include #include #include "ass.h" diff --git a/libass/ass_fontselect.c b/libass/ass_fontselect.c index 19ae6d4..2a2ec51 100644 --- a/libass/ass_fontselect.c +++ b/libass/ass_fontselect.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -45,6 +44,7 @@ #include "ass_coretext.h" #include "ass_directwrite.h" #include "ass_font.h" +#include "ass_string.h" #define ABS(x) ((x) < 0 ? -(x) : (x)) #define MAX_FULLNAME 100 @@ -408,7 +408,7 @@ void ass_font_provider_free(ASS_FontProvider *provider) static bool matches_family_name(ASS_FontInfo *f, const char *family) { for (int i = 0; i < f->n_family; i++) { - if (strcasecmp(f->families[i], family) == 0) + if (ass_strcasecmp(f->families[i], family) == 0) return true; } return false; @@ -420,7 +420,7 @@ static bool matches_family_name(ASS_FontInfo *f, const char *family) static bool matches_fullname(ASS_FontInfo *f, const char *fullname) { for (int i = 0; i < f->n_fullname; i++) { - if (strcasecmp(f->fullnames[i], fullname) == 0) + if (ass_strcasecmp(f->fullnames[i], fullname) == 0) return true; } return false; @@ -1054,7 +1054,7 @@ void ass_map_font(const ASS_FontMapping *map, int len, const char *name, ASS_FontProviderMetaData *meta) { for (int i = 0; i < len; i++) { - if (strcasecmp(map[i].from, name) == 0) { + if (ass_strcasecmp(map[i].from, name) == 0) { meta->fullnames = calloc(1, sizeof(char *)); if (meta->fullnames) { meta->fullnames[0] = strdup(map[i].to); diff --git a/libass/ass_library.c b/libass/ass_library.c index 5b7a5c9..a2945d8 100644 --- a/libass/ass_library.c +++ b/libass/ass_library.c @@ -27,6 +27,7 @@ #include "ass.h" #include "ass_library.h" #include "ass_utils.h" +#include "ass_string.h" static void ass_msg_handler(int level, const char *fmt, va_list va, void *data) { diff --git a/libass/ass_string.c b/libass/ass_string.c new file mode 100644 index 0000000..bb755a6 --- /dev/null +++ b/libass/ass_string.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2015 Grigori Goronzy + * + * This file is part of libass. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ass_string.h" + +static const char lowertab[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x61, + 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, + 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, + 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, + 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, + 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, + 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, + 0xfd, 0xfe, 0xff +}; + +int ass_strcasecmp(const char *s1, const char *s2) +{ + unsigned char a, b; + + do { + a = lowertab[(unsigned char)*s1++]; + b = lowertab[(unsigned char)*s2++]; + } while (a && a == b); + + return a - b; +} + +int ass_strncasecmp(const char *s1, const char *s2, size_t n) +{ + unsigned char a, b; + const char *last = s1 + n; + + do { + a = lowertab[(unsigned char)*s1++]; + b = lowertab[(unsigned char)*s2++]; + } while (s1 < last && a && a == b); + + return a - b; +} + diff --git a/libass/ass_string.h b/libass/ass_string.h new file mode 100644 index 0000000..8944804 --- /dev/null +++ b/libass/ass_string.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 Grigori Goronzy + * + * This file is part of libass. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +#include + +#ifndef ASS_STRING_H +#define ASS_STRING_H + +int ass_strcasecmp(const char *s1, const char *s2); +int ass_strncasecmp(const char *s1, const char *s2, size_t n); + +static inline int ass_isspace(int c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || + c == '\f' || c == '\r'; +} + +static inline int ass_isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +#endif diff --git a/libass/ass_strtod.c b/libass/ass_strtod.c index 4e96404..566f6e4 100644 --- a/libass/ass_strtod.c +++ b/libass/ass_strtod.c @@ -13,8 +13,8 @@ */ #include -#include #include +#include "ass_string.h" static const int maxExponent = 511; /* Largest possible base 10 exponent. Any @@ -100,7 +100,7 @@ ass_strtod( */ p = string; - while (isspace(*p)) { + while (ass_isspace(*p)) { p += 1; } if (*p == '-') { @@ -122,7 +122,7 @@ ass_strtod( for (mantSize = 0; ; mantSize += 1) { c = *p; - if (!isdigit(c)) { + if (!ass_isdigit(c)) { if ((c != '.') || (decPt >= 0)) { break; } @@ -198,7 +198,7 @@ ass_strtod( } expSign = 0; } - while (isdigit(*p)) { + while (ass_isdigit(*p)) { exp = exp * 10 + (*p - '0'); p += 1; } diff --git a/libass/ass_utils.c b/libass/ass_utils.c index 1c0ebdd..9c9155b 100644 --- a/libass/ass_utils.c +++ b/libass/ass_utils.c @@ -23,12 +23,11 @@ #include #include #include -#include -#include #include "ass_library.h" #include "ass.h" #include "ass_utils.h" +#include "ass_string.h" #if (defined(__i386__) || defined(__x86_64__)) && CONFIG_ASM @@ -232,7 +231,7 @@ static int mystrtou32_modulo(char **p, int base, uint32_t *res) else if (**p == '-') sign = -1, ++*p; - if (base == 16 && !strncasecmp(*p, "0x", 2)) + if (base == 16 && !ass_strncasecmp(*p, "0x", 2)) *p += 2; if (read_digits(p, base, res)) { @@ -271,7 +270,7 @@ uint32_t parse_color_header(char *str) uint32_t color = 0; int base; - if (!strncasecmp(str, "&h", 2) || !strncasecmp(str, "0x", 2)) { + if (!ass_strncasecmp(str, "&h", 2) || !ass_strncasecmp(str, "0x", 2)) { str += 2; base = 16; } else @@ -285,7 +284,7 @@ uint32_t parse_color_header(char *str) char parse_bool(char *str) { skip_spaces(&str); - return !strncasecmp(str, "yes", 3) || strtol(str, NULL, 10) > 0; + return !ass_strncasecmp(str, "yes", 3) || strtol(str, NULL, 10) > 0; } int parse_ycbcr_matrix(char *str) @@ -305,23 +304,23 @@ int parse_ycbcr_matrix(char *str) memcpy(buffer, str, n); buffer[n] = '\0'; - if (!strcasecmp(buffer, "none")) + if (!ass_strcasecmp(buffer, "none")) return YCBCR_NONE; - if (!strcasecmp(buffer, "tv.601")) + if (!ass_strcasecmp(buffer, "tv.601")) return YCBCR_BT601_TV; - if (!strcasecmp(buffer, "pc.601")) + if (!ass_strcasecmp(buffer, "pc.601")) return YCBCR_BT601_PC; - if (!strcasecmp(buffer, "tv.709")) + if (!ass_strcasecmp(buffer, "tv.709")) return YCBCR_BT709_TV; - if (!strcasecmp(buffer, "pc.709")) + if (!ass_strcasecmp(buffer, "pc.709")) return YCBCR_BT709_PC; - if (!strcasecmp(buffer, "tv.240m")) + if (!ass_strcasecmp(buffer, "tv.240m")) return YCBCR_SMPTE240M_TV; - if (!strcasecmp(buffer, "pc.240m")) + if (!ass_strcasecmp(buffer, "pc.240m")) return YCBCR_SMPTE240M_PC; - if (!strcasecmp(buffer, "tv.fcc")) + if (!ass_strcasecmp(buffer, "tv.fcc")) return YCBCR_FCC_TV; - if (!strcasecmp(buffer, "pc.fcc")) + if (!ass_strcasecmp(buffer, "pc.fcc")) return YCBCR_FCC_PC; return YCBCR_UNKNOWN; } @@ -345,8 +344,8 @@ char *strdup_trimmed(const char *str) int right = strlen(str) - 1; char *out = NULL; - while (isspace(str[left])) left++; - while (right > left && isspace(str[right])) right--; + while (ass_isspace(str[left])) left++; + while (right > left && ass_isspace(str[right])) right--; out = calloc(1, right-left+2); @@ -435,7 +434,7 @@ int lookup_style(ASS_Track *track, char *name) ++name; // VSFilter then normalizes the case of "Default" // (only in contexts where this function is called) - if (strcasecmp(name, "Default") == 0) + if (ass_strcasecmp(name, "Default") == 0) name = "Default"; for (i = track->n_styles - 1; i >= 0; --i) { if (strcmp(track->styles[i].Name, name) == 0) @@ -491,7 +490,7 @@ void *ass_guess_buffer_cp(ASS_Library *library, unsigned char *buffer, for (i = 0; i < langcnt; i++) { const char *tmp; - if (strcasecmp(languages[i], preferred_language) != 0) + if (ass_strcasecmp(languages[i], preferred_language) != 0) continue; analyser = enca_analyser_alloc(languages[i]); encoding = enca_analyse_const(analyser, buffer, buflen); -- cgit v1.2.3