/** * Copyright (c) 2025 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "language.h" #include "assert/assert.h" language_t LANGUAGE; void languageInit(void) { LANGUAGE.current = LANGUAGE_EN; } const char_t * languageGet(const char_t *key) { assertNotNull(key, "Key cannot be NULL"); assertTrue(LANGUAGE.current < LANGUAGE_COUNT, "Invalid language index"); int16_t keyIndex = -1; for(uint16_t i = 0; i < LANGUAGE_COUNTS[LANGUAGE.current]; i++) { if(strcmp(LANGUAGE_KEYS[LANGUAGE.current][i], key) != 0) continue; keyIndex = i; break; } assertTrue(keyIndex != -1, "Key not found in language"); return LANGUAGE_VALUES[LANGUAGE.current][keyIndex]; } uint16_t langaugeGetLength(const char_t *key) { assertNotNull(key, "Key cannot be NULL"); assertTrue(LANGUAGE.current < LANGUAGE_COUNT, "Invalid language index"); int16_t keyIndex = -1; for(uint16_t i = 0; i < LANGUAGE_COUNTS[LANGUAGE.current]; i++) { if(strcmp(LANGUAGE_KEYS[LANGUAGE.current][i], key) != 0) continue; keyIndex = i; break; } assertTrue(keyIndex != -1, "Key not found in language"); return strlen(LANGUAGE_VALUES[LANGUAGE.current][keyIndex]); } uint16_t languageFormat( const char_t *key, char_t *buffer, uint16_t buffSize, const char_t **keys, const char_t **values, const uint16_t valueCount ) { if(buffer != NULL) { assertTrue(buffSize > 0, "Buffer size must be greater than 0"); } else { assertTrue(buffSize == 0, "Buffer size must be 0 if buffer is NULL"); } assertNotNull(key, "Key cannot be NULL"); assertNotNull(keys, "Keys cannot be NULL"); assertNotNull(values, "Values cannot be NULL"); const char_t *val = languageGet(key); assertNotNull(val, "Value for key cannot be NULL"); char_t c; uint16_t i = 0; uint16_t j = 0; uint8_t k = 0; bool_t inBraces = false; char_t braceBuffer[64] = {0}; #define bufferChar(c) ( \ (buffer ? (buffer[j++] = c) : (j++)), \ assertTrue(buffer ? j < buffSize : true, "Buffer overflow") \ ) while((c = val[i++]) != '\0') { if(c == '{' && val[i] == '{') { goto startBraces; } else if(c == '}' && val[i] == '}') { goto endBraces; } else if(inBraces) { goto braceBuffering; } else { goto character; } character: { bufferChar(c); continue; } braceBuffering: { assertFalse(val[i] == '\0', "Unexpected end of string."); braceBuffer[k++] = c; assertTrue(k < sizeof(braceBuffer), "Brace buffer overflow"); if(val[i] == ' ') i++; continue; } startBraces: { assertFalse(inBraces, "Nested braces are not allowed"); inBraces = true; i++; k = 0; assertFalse(val[i] == '\0', "Unexpected end of string."); if(val[i] == ' ') i++; continue; } endBraces: { assertTrue(inBraces, "Unmatched closing brace found"); inBraces = false; i++; braceBuffer[k] = '\0'; uint16_t l; for(l = 0; l < valueCount; l++) { if(strcmp(braceBuffer, keys[l]) != 0) { continue; } const char_t *replacement = values[l]; uint16_t r = 0; while((c = replacement[r++]) != '\0') { bufferChar(c); } break; } assertTrue(l < valueCount, "No string replacement found!"); continue; } } if(buffer){ assertTrue(j < buffSize, "Buffer overflow"); buffer[j] = '\0'; } assertFalse(inBraces, "Unmatched opening brace found"); return j; }