Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson 2024-12-12 16:43:03 -08:00 committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

9
third_party/citrus/LICENSE vendored Normal file
View file

@ -0,0 +1,9 @@
Copyright (c) 2000, 2001 Citrus Project,
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

595
third_party/citrus/i18n.c vendored Normal file
View file

@ -0,0 +1,595 @@
/*-
* Copyright (c) 2000, 2001 Citrus Project,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "i18n.h"
#include "mo.h"
#include "kernel/event_loop.h"
#include "kernel/pbl_malloc.h"
#include "resource/resource.h"
#include "services/normal/filesystem/pfs.h"
#include "shell/normal/language_ui.h"
#include "shell/prefs.h"
#include "system/logging.h"
#include "system/passert.h"
#include "util/list.h"
//////////////////////////////////////////////////////
// See mo.h for a description of the MO file format //
//////////////////////////////////////////////////////
typedef struct {
uint32_t hash;
const char *string;
const void *owner;
} StringLookupInfo;
static struct DomainBinding {
uint32_t resource_id;
ResourceCallbackHandle watch_handle;
bool need_reload;
ResourceVersion version;
MoHandle mohandle;
I18nString *strings_list;
char iso_locale[ISO_LOCALE_LENGTH];
char lang_name[LOCALE_NAME_LENGTH];
uint16_t lang_version;
} s_system_domain;
static void prv_list_flush(void);
///////////////////////////////////////////////////
// MO File Hash Table
uint32_t prv_gettext_hash(const char *str) {
const uint8_t *p;
uint32_t hash = 0, tmp;
for (p = (const uint8_t *)str; *p; p++) {
hash <<= 4;
hash += *p;
tmp = hash & 0xF0000000;
if (tmp != 0) {
hash ^= tmp;
hash ^= tmp >> 24;
}
}
return hash;
}
static uint32_t prv_collision_step(uint32_t hashval, uint32_t hashsize) {
return (hashval % (hashsize - 2)) + 1;
}
static uint32_t prv_next_index(uint32_t curidx, uint32_t hashsize, uint32_t step) {
return curidx + step - (curidx >= hashsize - step ? hashsize : 0);
}
//! Lookup a translated string.
//! @param rlen[out] Can be NULL. If non-null will be populated with the length of the translated
//! string.
//! @param rstring[out] Can be NULL. If non-null this buffer will be populated with the translated
//! string. This buffer will be null-terminated.
//! @param rstring_len The length of the rstring buffer.
static void prv_lookup(const char *msgid, struct DomainBinding *db,
size_t *rlen, char *rstring, size_t rstring_len) {
MoHandle *mohandle = &db->mohandle;
*rlen = 0;
if (mohandle->mo.hdr.mo_hsize <= 2 || mohandle->mo.mo_htable == NULL) {
return;
}
uint32_t hashval = prv_gettext_hash(msgid);
uint32_t step = prv_collision_step(hashval, mohandle->mo.hdr.mo_hsize);
uint32_t idx = hashval % mohandle->mo.hdr.mo_hsize;
size_t len = strlen(msgid);
while (1) {
uint32_t strno = mohandle->mo.mo_htable[idx];
if (strno-- == 0) {
/* unexpected miss */
return;
}
MoEntry oentry;
if (resource_load_byte_range_system(0, db->resource_id, mohandle->mo.hdr.mo_otable
+ sizeof(MoEntry) * strno, (uint8_t *)&oentry, sizeof(MoEntry)) != sizeof(MoEntry)) {
return;
}
if (len == oentry.len) {
// Length of original matches, compare the contents
char key[oentry.len + 1];
if (resource_load_byte_range_system(0, db->resource_id, oentry.off, (uint8_t *)key,
oentry.len) != oentry.len) {
return;
}
key[oentry.len] = '\0';
if (!strcmp(msgid, key)) {
// Contents of original string matches, get the translated string
MoEntry tentry;
if (resource_load_byte_range_system(0, db->resource_id, mohandle->mo.hdr.mo_ttable
+ sizeof(MoEntry) * strno, (uint8_t *)&tentry, sizeof(MoEntry)) != sizeof(MoEntry)) {
return;
}
if (rstring) { // If we want the translated string, copy it out.
// Make sure we don't read out more than the length of the buffer we're reading into.
// Leave space for the null-terminator as well.
const size_t read_length = MIN(tentry.len, rstring_len - 1);
if (resource_load_byte_range_system(0, db->resource_id, tentry.off,
(uint8_t *)rstring, read_length) != read_length) {
return;
}
rstring[read_length] = '\0';
}
if (rlen) { // If we want the translated string length, copy it out.
*rlen = tentry.len;
}
return;
}
}
idx = prv_next_index(idx, mohandle->mo.hdr.mo_hsize, step);
}
}
///////////////////////////////////////////////////
// MO File Mapping & Lookup
static bool prv_get_property(const char *header, const char *name, char *buffer, size_t size) {
// Isolate the language name
char *str = strstr(header, name);
if (str == NULL) { // strstr failed
return false;
}
str += strlen(name);
char *end = strchr(str, '\n');
unsigned int length = end - str;
if (end == NULL || length > size) { // strchr failed
return false;
}
memcpy(buffer, str, length);
buffer[length] = '\0';
return true;
}
static bool prv_get_metadata(struct DomainBinding *db) {
const size_t HEADER_BUFFER_SIZE = 400;
// malloc a comfortable amount of RAM to save the header in
char *header = kernel_malloc_check(HEADER_BUFFER_SIZE);
size_t header_len = 0;
bool success = false;
// all metadata is in the "" header entry
prv_lookup("", db, &header_len, header, HEADER_BUFFER_SIZE);
if (!header_len) {
PBL_LOG(LOG_LEVEL_WARNING, "Could not find header in language pack");
goto cleanup;
}
// Isolate the language substring
if (!prv_get_property(header, "Language: ", db->iso_locale, ISO_LOCALE_LENGTH)) {
PBL_LOG(LOG_LEVEL_WARNING, "Could not parse a language from language pack");
goto cleanup;
}
// Isolate the language name
if (!prv_get_property(header, "Name: ", db->lang_name, LOCALE_NAME_LENGTH)) {
strcpy(db->lang_name, "Unknown");
}
// Isolate the version value
char version_str[10] = {0};
if (!prv_get_property(header, "Project-Id-Version: ", version_str, 10)) {
PBL_LOG(LOG_LEVEL_WARNING, "Could not parse a version from language pack");
goto cleanup;
}
char *version_end;
db->lang_version = strtol(version_str, &version_end, 0);
if (version_end == version_str) {
PBL_LOG(LOG_LEVEL_WARNING, "Could not parse a version from language pack");
goto cleanup;
}
success = true;
PBL_LOG(LOG_LEVEL_INFO, "language: %s, version %d", db->iso_locale, db->lang_version);
cleanup:
kernel_free(header);
return (success);
}
static int prv_unmapit(struct DomainBinding *db) {
MoHandle *mohandle = &db->mohandle;
kernel_free(mohandle->mo.mo_htable);
mohandle->mo.mo_htable = NULL;
mohandle->mo = (Mo){};
strcpy(db->iso_locale, "en_US");
strcpy(db->lang_name, "English");
db->lang_version = 1;
return 0;
}
static bool prv_mapit(const uint32_t resource_id, struct DomainBinding *db) {
// If the resource is changed on disk, our resource_watch callback will set need_reload
if (!db->need_reload) {
return (db->version.crc != 0);
}
PBL_LOG(LOG_LEVEL_DEBUG, "New language detected!");
db->need_reload = false;
/* save version */
db->version = resource_get_version(SYSTEM_APP, resource_id);
prv_list_flush();
prv_unmapit(db);
unsigned int size;
if ((size = resource_size(SYSTEM_APP, resource_id)) < sizeof(MoHeader)) {
goto fail;
}
if (!resource_is_valid(SYSTEM_APP, resource_id)) {
goto fail;
}
MoHandle *mohandle = &db->mohandle;
if (resource_load_byte_range_system(SYSTEM_APP, resource_id, 0,
(uint8_t *)&mohandle->mo.hdr, sizeof(MoHeader)) == 0) {
goto fail;
}
if (mohandle->mo.hdr.mo_magic != MO_MAGIC) {
goto fail;
}
mohandle->len = size;
/* validate htable */
if (mohandle->mo.hdr.mo_hsize < 2) {
goto fail;
}
size_t htable_size = sizeof(uint32_t) * mohandle->mo.hdr.mo_hsize;
uint32_t *htable = kernel_malloc_check(htable_size);
mohandle->mo.mo_htable = htable;
if (resource_load_byte_range_system(SYSTEM_APP, resource_id, mohandle->mo.hdr.mo_hoffset,
(uint8_t *)htable, htable_size) == 0) {
prv_unmapit(db);
goto fail;
}
for (unsigned int i = 0; i < mohandle->mo.hdr.mo_hsize; ++i) {
if (htable[i] > mohandle->mo.hdr.mo_nstring) {
/* illegal string number */
prv_unmapit(db);
goto fail;
}
}
if (!prv_get_metadata(db)) {
prv_unmapit(db);
goto fail;
}
return true;
fail:
return false;
}
///////////////////////////////////////////////////
// Strings List Manipulation
void prv_list_flush(void) {
ListNode *cur = (ListNode *)s_system_domain.strings_list;
while (cur) {
ListNode *next = list_get_next(cur);
kernel_free(cur);
cur = next;
}
s_system_domain.strings_list = NULL;
}
static bool prv_list_string_filter_callback(ListNode *found_node, void *data) {
I18nString *i18n_string = (I18nString *)found_node;
StringLookupInfo *lookup_info = data;
if (i18n_string->original_hash == lookup_info->hash &&
lookup_info->owner == i18n_string->owner &&
strcmp(i18n_string->original_string, lookup_info->string) == 0) {
return true;
} else {
return false;
}
}
static bool prv_list_owner_filter_callback(ListNode *found_node, void *owner) {
I18nString *i18n_string = (I18nString *)found_node;
if (i18n_string->owner == owner) {
return true;
} else {
return false;
}
}
// Not static because we call this from unit test code
I18nString *prv_list_find_string(const char *string, const void *owner) {
StringLookupInfo lookup_info = {
.string = string,
.hash = prv_gettext_hash(string),
.owner = owner
};
return (I18nString *)list_find((ListNode *)s_system_domain.strings_list,
prv_list_string_filter_callback, (void *)&lookup_info);
}
static const char *prv_list_add_string(const char *original_string, const char *translated_string,
const void *owner) {
uint32_t translated_len = strlen(translated_string);
// Allocate enough space to hold the original and translated strings. The translated string
// is stored at i18n_string->translated and the original string immediately after that.
I18nString *i18n_string = kernel_malloc_check(sizeof(I18nString) + translated_len + 1
+ strlen(original_string) + 1);
list_init(&i18n_string->node);
i18n_string->owner = owner;
strcpy(i18n_string->translated_string, translated_string);
i18n_string->original_hash = prv_gettext_hash(original_string);
// Store the original string immediately after the translated one in memory.
i18n_string->original_string = &i18n_string->translated_string[translated_len + 1];
strcpy(i18n_string->original_string, original_string);
I18nString **strings_list = &s_system_domain.strings_list;
*strings_list = (I18nString *)list_prepend((ListNode *)*strings_list, &i18n_string->node);
if (translated_len > 0) {
return (i18n_string->translated_string);
} else {
return original_string;
}
}
static void prv_list_remove_string(I18nString *i18n_string) {
list_remove(&i18n_string->node, (ListNode **)&s_system_domain.strings_list, NULL);
kernel_free(i18n_string);
}
static bool prv_check_domain(struct DomainBinding *db) {
return (prv_mapit(s_system_domain.resource_id, db));
}
static const char *prv_message_from_msgid(const char *msgid) {
// If a string wasn't found, we want to return the original string.
// However, if we have a context, this string needs to not show the context.
// So we just find EOT and if it's present return the next character.
const char *message = strchr(msgid, '\4');
if (message == NULL) {
// No context, the whole string is the message.
return msgid;
}
// strchr gets the address of that character. We want to skip the EOT, so +1.
return message + 1;
}
///////////////////////////////////////////////////
// i18n API
// NOTE: Currently, we don't do reference counting, so bad things will happen if the caller
// calls i18n_get() on the same string more than once and assumes that any of those return
// pointers will still be valid after i18n_free() is called on one of them.
const char *i18n_get(const char *msgid, const void *owner) {
PBL_ASSERTN(owner);
if (msgid == NULL || msgid[0] == 0) {
goto fail;
}
struct DomainBinding *db = &s_system_domain;
if (!prv_check_domain(db)) {
goto fail;
}
// See if this original has been cached.
I18nString *i18n_string = prv_list_find_string(msgid, owner);
if (i18n_string) {
if (i18n_string->translated_string[0]) {
return i18n_string->translated_string;
} else {
// No translation exists for this string, return original
goto fail;
}
}
// Lookup the translation from the language pack and add it to our cache
char translated[200];
size_t len = 0;
prv_lookup(msgid, db, &len, translated, sizeof(translated));
if (len >= sizeof(translated)) {
PBL_LOG(LOG_LEVEL_WARNING, "Truncated string: <%s>", msgid);
}
if (len) {
return prv_list_add_string(msgid, translated, owner);
} else {
// Add to cache as an untranslatable string so we don't waste time looking for it again.
prv_list_add_string(msgid, (const char *)"", owner);
}
fail:
// String not found or an error occurred.
return prv_message_from_msgid(msgid);
}
void i18n_get_with_buffer(const char *msgid, char *buffer, size_t length) {
if (msgid == NULL || msgid[0] == 0) {
goto fail;
}
struct DomainBinding *db = &s_system_domain;
if (!prv_check_domain(db)) {
goto fail;
}
size_t len = 0;
prv_lookup(msgid, db, &len, buffer, length);
if (len >= length) {
PBL_LOG(LOG_LEVEL_WARNING, "Truncated string: <%s>", msgid);
}
if (len) {
// buffer has been written, return
return;
}
fail:
msgid = prv_message_from_msgid(msgid);
strncpy(buffer, msgid, length);
buffer[length - 1] = '\0';
}
size_t i18n_get_length(const char *msgid) {
if (msgid == NULL || msgid[0] == 0) {
return 0;
}
struct DomainBinding *db = &s_system_domain;
if (!prv_check_domain(db)) {
goto fail;
}
size_t len = 0;
prv_lookup(msgid, db, &len, NULL, 0);
if (len) { // String was found
return len;
}
fail:
// String not found, or error occurred
msgid = prv_message_from_msgid(msgid);
return strlen(msgid);
}
void i18n_free(const char *original, const void *owner) {
PBL_ASSERTN(owner);
I18nString *i18n_string = prv_list_find_string(original, owner);
if (i18n_string) {
prv_list_remove_string(i18n_string);
}
}
void i18n_free_all(const void *owner) {
I18nString *cur_string = (I18nString *)list_find((ListNode *)s_system_domain.strings_list,
prv_list_owner_filter_callback, (void*)owner);
while (cur_string) {
I18nString *next_string = (I18nString *)list_find_next(&cur_string->node,
prv_list_owner_filter_callback, false, (void*)owner);
prv_list_remove_string(cur_string);
cur_string = next_string;
}
}
static void prv_resource_changed_handler(void *data) {
struct DomainBinding *db = (struct DomainBinding *)data;
// Mark as invalid
PBL_LOG(LOG_LEVEL_DEBUG, "lang resource file reloading");
shell_prefs_set_language_english(false);
db->need_reload = true;
if (resource_is_valid(SYSTEM_APP, db->resource_id)) {
language_ui_display_changed(db->lang_name);
}
}
static void prv_resource_changed_callback(void *data) {
// We want to not actually handle the reload here, because the PFS lock is still held here.
// So instead we throw in the reload as an event callback.
PBL_LOG(LOG_LEVEL_DEBUG, "lang resource file was modified");
launcher_task_add_callback(prv_resource_changed_handler, data);
}
static void prv_unset(void) {
s_system_domain.need_reload = false;
prv_list_flush();
prv_unmapit(&s_system_domain);
}
void i18n_set_resource(uint32_t resource_id) {
// Remove prior watch, if any
// Warning: you better be sure we're not calling from the resource changed callback.
if (s_system_domain.watch_handle) {
resource_unwatch(s_system_domain.watch_handle);
}
s_system_domain.resource_id = resource_id;
s_system_domain.watch_handle = resource_watch(SYSTEM_APP, resource_id,
prv_resource_changed_callback, &s_system_domain);
if (shell_prefs_get_language_english()) {
prv_unset();
return;
}
s_system_domain.need_reload = true;
// try mapping it right away
prv_mapit(resource_id, &s_system_domain);
}
char *i18n_get_locale(void) {
return (s_system_domain.iso_locale);
}
uint16_t i18n_get_version(void) {
return (s_system_domain.lang_version);
}
char *i18n_get_lang_name(void) {
return (s_system_domain.lang_name);
}
void i18n_enable(bool enable) {
if (enable) {
s_system_domain.need_reload = true;
prv_mapit(s_system_domain.resource_id, &s_system_domain);
} else {
prv_unset();
}
}
void command_i18n_resource(const char *arg) {
uint32_t resource_id = atoi(arg);
i18n_set_resource(resource_id);
}

90
third_party/citrus/i18n.h vendored Normal file
View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2000 Citrus Project,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#pragma once
#include <inttypes.h>
#include <stddef.h>
#include "util/list.h"
#define ISO_LOCALE_LENGTH 6
#define LOCALE_NAME_LENGTH 30
typedef struct {
ListNode node; //!< Linked list node
const void *owner; //!< pointer to owner object
uint32_t original_hash; //!< hashed original string
char *original_string; //!< original string. Stored following translated_string below
char translated_string[]; //!< i18n'ed string. Storage for original string comes after this
} I18nString;
//! macro used to tag strings for extractions. Needed when we
//! can't call i18n_get directly (i.e constant initializers)
#define i18n_noop(string) (string)
//! macro used to tag strings for extractions. Needed when we
//! can't call i18n_ctx_get directly (i.e constant initializers)
//! The resulting string should be used with i18n_get instead of i18n_ctx_get.
#define i18n_ctx_noop(ctx, string) (ctx "\4" string)
//! Look up and return i18n'ed string (or original string if not found)
//! Tags it as owned by owner
//! NOTE: Currently, we don't do reference counting, so bad things will happen if the caller
//! calls i18n_get() on the same string more than once and assumes that any of those return
//! pointers will still be valid after i18n_free() is called on one of them.
const char *i18n_get(const char *string, const void *owner);
#define i18n_ctx_get(ctx, string, owner) i18n_get(i18n_ctx_noop(ctx, string), owner)
//! Look up an i18n'ed string and copy it into a provided buffer.
void i18n_get_with_buffer(const char *string, char *buffer, size_t length);
#define i18n_ctx_get_with_buffer(ctx, string, buffer, length) \
i18n_get_with_buffer(i18n_ctx_noop(ctx, string), buffer, length)
//! Look up an i18n'ed string and return the length of it.
size_t i18n_get_length(const char *string);
#define i18n_ctx_get_length(ctx, string) i18n_get_length(i18n_ctx_noop(ctx, string))
//! Free an i18n'ed string and it's associated metadata
void i18n_free(const char *string, const void *owner);
#define i18n_ctx_free(ctx, string, owner) i18n_free(i18n_ctx_noop(ctx, string), owner)
//! Free all i18n'ed strings associated with owner
void i18n_free_all(const void *owner);
void i18n_set_resource(uint32_t resource_id);
//! return the ISO language string for the currently installed language
char *i18n_get_locale(void);
//! return the version number for the currently installed language
uint16_t i18n_get_version(void);
char *i18n_get_lang_name(void);
void i18n_enable(bool enable);

122
third_party/citrus/mo.h vendored Normal file
View file

@ -0,0 +1,122 @@
/*-
* Copyright (c) 2000, 2001 Citrus Project,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#pragma once
#include <inttypes.h>
#include <unistd.h>
#include "util/attributes.h"
/* ==============
* MO File Format *
* ==============
*
byte
+------------------------------------------+
0 | magic number = 0x950412de |
| |
4 | file format revision = 0 |
| |
8 | number of strings | == N
| |
12 | offset of table with original strings | == O
| |
16 | offset of table with translation strings | == T
| |
20 | size of hashing table | == S
| |
24 | offset of hashing table | == H
| |
. .
. (possibly more entries later) .
. .
| |
O | length & offset 0th string ----------------.
O + 8 | length & offset 1st string ------------------.
... ... | |
O + ((N-1)*8)| length & offset (N-1)th string | | |
| | | |
T | length & offset 0th translation ---------------.
T + 8 | length & offset 1st translation -----------------.
... ... | | | |
T + ((N-1)*8)| length & offset (N-1)th translation | | | | |
| | | | | |
H | start hash table | | | | |
... ... | | | |
H + S * 4 | end hash table | | | | |
| | | | | |
| NUL terminated 0th string <----------------' | | |
| | | | |
| NUL terminated 1st string <------------------' | |
| | | |
... ... | |
| | | |
| NUL terminated 0th translation <---------------' |
| | |
| NUL terminated 1st translation <-----------------'
| |
... ...
| |
+------------------------------------------+
*
*/
#define MO_MAGIC 0x950412de
#define MO_GET_REV_MAJOR(r) (((r) >> 16) & 0xFFFF)
#define MO_GET_REV_MINOR(r) ((r) & 0xFFFF)
#define MO_MAKE_REV(maj, min) (((maj) << 16) | (min))
#define LANG_PROP_NAME "Language: "
/* *.mo file format */
typedef struct PACKED {
uint32_t mo_magic; /* determines endian */
uint32_t mo_revision; /* file format revision: 0 */
uint32_t mo_nstring; /* N: number of strings */
uint32_t mo_otable; /* O: original text table offset */
uint32_t mo_ttable; /* T: translated text table offset */
uint32_t mo_hsize; /* S: size of hashing table */
uint32_t mo_hoffset; /* H: offset of hashing table */
} MoHeader;
typedef struct PACKED {
uint32_t len; /* strlen(str), so region will be len + 1 */
uint32_t off; /* offset of \0-terminated string */
} MoEntry;
typedef struct {
MoHeader hdr;
char *mo_lang;
uint32_t *mo_htable; /* H: hash table */
} Mo;
typedef struct {
size_t len;
Mo mo; /* endian-flipped mo file header */
} MoHandle;