/* * This file is part of Beastie * SPDX-FileCopyrightText: 2024 Norman Gray * SPDX-License-Identifier: BSD-2-Clause */ #ifndef UNICODE_H #define UNICODE_H 1 #include #include #include #include "config.h" #if HAVE_ICU #include #include #include #else // unicode codepoints typedef uint16_t UChar; // imitating ICU #endif #include "unidefs.h" // codepoint_t codepoint_t decode_utf8(const unsigned char* bc, size_t len, unsigned char* nused, const char** errmsg); // Returned by decode_utf8 when it can't decode a UTF-8 sequence // (this bit-pattern is not a valid codepoint). extern const codepoint_t UNICODE_BAD_DECODE; // used to signal EOF extern const codepoint_t UNICODE_EOF; // The unicode 'replacement character' extern const codepoint_t UNICODE_REPLACEMENT_CHARACTER; const unsigned char* encode_utf8(const codepoint_t codepoint, int* len); // The ustring_s type represents strings as an array of codepoints. // The encoding is equivalent to UTF-16. // The `len` field is the number of codepoints in the string, // while `idx` is the first unused slot in `s[]`, // ie, taking account of the fact that a codepoint outside the BMP // will occupy two code-units (uint16_t); // these will be the same if there are no surrogates in the string. // // The cache_store_ pointer must in fact be an s7_pointer, cast to // void. This supports a simple cache mechanism, managed exclusively // by unicode-scm.c:ustring_cache_object_{set,get}_proc (qv). struct ustring_s { UChar* s_; size_t alloc_; // number of uint16_t allocated to s size_t len_; // number of characters/codepoints in the string size_t idx_; // the number of code-units allocated const void* cache_store_; // cache information for higher layers }; typedef struct ustring_s* ustring_t; typedef struct unicode_reader_s { #if HAVE_ICU UFILE* in; // input file, if we're reading from a file #else FILE* in; // input file; NULL if we're reading from a string, // and set to NULL at EOF unsigned char* buf; // bytes; non-NULL when reading from file size_t buflen; // size of buf[] size_t len; // amount of valid data in buf #endif // This could be implemented using a UText, but in fact that provides // very little real benefit, and when I did implement it this way, // in most of the cases the semantics of UText meant that I had to // fall back to looking at the chunkContents within it. ustring_t us; // non-NULL when we are reading from a ustring char our_ustring_p; // true if this ustring belongs to this reader size_t idx; // next point to be read size_t count; // total number of codepoints returned so far char* filename; // NULL if we are reading from a string size_t line_count; // current line number (first line is line 1) char ascii_p; // if true, return codepoints below 0x80 as characters codepoint_t pushed; // one character of pushback char at_eof_p; // true at EOF/EOS } unicode_reader; unicode_reader* make_unicode_reader_file(const char* filename, size_t buflen, const char** errmsg); unicode_reader* make_unicode_reader_string(const unsigned char* bstr, const char** errmsg); unicode_reader* make_unicode_reader_ustring(ustring_t us, const char** errmsg); void unicode_reader_free(unicode_reader* p); const size_t unicode_reader_get_source(unicode_reader* ur, char* buf, size_t buflen, char* is_file_p); const codepoint_t unicode_reader_next_cp(unicode_reader* p, const char** errmsg); void push_codepoint(unicode_reader* p, codepoint_t cp); codepoint_t from_surrogate(const uint16_t* const p, const char** errmsg); int is_surrogate(const codepoint_t cp); ustring_t make_ustring(const char** errmsg); void ustring_free(ustring_t); void ustring_reset(ustring_t); int ustring_length(ustring_t); int ustring_lt(ustring_t us1, ustring_t us2); const char* unicode_get_locale(void); int unicode_set_locale(const char* locale, char** errmsg); ustring_t ustring_map_func(ustring_t us, codepoint_t (*f)(codepoint_t)); #define USTRING_EQUAL_COLLAPSE_REPLACEMENTS 1 int ustring_equal(ustring_t s1, ustring_t s2, byte_t flags); ustring_t ustring_append_cp(ustring_t, codepoint_t, const char**); ustring_t ustring_append_utf8(ustring_t us, const byte_t* s, const char** errmsg); ustring_t ustring_append_utf8_with_length(ustring_t us, const byte_t* s, const size_t len, const char** errmsg); ustring_t ustring_append_ustring(ustring_t us1, ustring_t us2, const char** errmsg); ustring_t ustring_append_uchars(ustring_t us1, UChar* uchars, size_t n_cp, size_t n_uchars, const char** errmsg); codepoint_t ustring_ref(ustring_t, size_t); ustring_t ustring_substring(ustring_t us, int start, int end, const char** errmsg); const byte_t* ustring_to_utf8(ustring_t, const char** errmsg); const void* ustring_cache_store_get(ustring_t); void ustring_cache_store_set(ustring_t, const void*); int unicode_verbosity(int); const char* unicode_version(); #if !HAVE_ICU // These are copied from unicode/utf.h, which says: // // Code points that are not characters include: // * single surrogate code points (U+d800..U+dfff, 2048 code points) // * the last two code points on each plane // (U+__fffe and U+__ffff, 34 code points) // * U+fdd0..U+fdef (new with Unicode 3.1, 32 code points) // * the highest Unicode code point value is U+10ffff // // This means that all code points below U+d800 are character code points, // and that boundary is tested first for performance. #define U_IS_UNICODE_CHAR(c) \ ((uint32_t)(c)<0xd800 || \ (0xdfff<(c) && (c)<=0x10ffff && !U_IS_UNICODE_NONCHAR(c))) // Note: the 'noncharacters' are as discussed in the Standard, // Sect.3.4, D14. This set is _not_ the complement of U_IS_UNICODE_CHAR. #define U_IS_UNICODE_NONCHAR(c) \ ((c)>=0xfdd0 && \ ((c)<=0xfdef || ((c)&0xfffe)==0xfffe) && (c)<=0x10ffff) #endif int initialise_unicode_module(char** errmsg); #endif /* UNICODE_H */