#include #include #include #include #include "gettext.h" typedef struct _MSG { u32 id; char* msgstr; struct _MSG *next; } MSG; static MSG *baseMSG = 0; #define HASHWORDBITS 32 /* Defines the so called `hashpjw' function by P.J. Weinberger [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools, 1986, 1987 Bell Telephone Laboratories, Inc.] */ static inline u32 hash_string ( const char *str_param ) { u32 hval, g; const char *str = str_param; /* Compute the hash value for the given string. */ hval = 0; while ( *str != '\0' ) { hval <<= 4; hval += ( u8 ) * str++; g = hval & ( ( u32 ) 0xf << ( HASHWORDBITS - 4 ) ); if ( g != 0 ) { hval ^= g >> ( HASHWORDBITS - 8 ); hval ^= g; } } return hval; } /* Expand some escape sequences found in the argument string. */ static char * expand_escape ( const char *str ) { char *retval, *rp; const char *cp = str; retval = ( char * ) malloc ( strlen ( str ) + 1 ); if ( retval == NULL ) return NULL; rp = retval; while ( cp[0] != '\0' && cp[0] != '\\' ) *rp++ = *cp++; if ( cp[0] == '\0' ) goto terminate; do { /* Here cp[0] == '\\'. */ switch ( *++cp ) { case '\"': /* " */ *rp++ = '\"'; ++cp; break; case 'a': /* alert */ *rp++ = '\a'; ++cp; break; case 'b': /* backspace */ *rp++ = '\b'; ++cp; break; case 'f': /* form feed */ *rp++ = '\f'; ++cp; break; case 'n': /* new line */ *rp++ = '\n'; ++cp; break; case 'r': /* carriage return */ *rp++ = '\r'; ++cp; break; case 't': /* horizontal tab */ *rp++ = '\t'; ++cp; break; case 'v': /* vertical tab */ *rp++ = '\v'; ++cp; break; case '\\': *rp = '\\'; ++cp; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { int ch = *cp++ - '0'; if ( *cp >= '0' && *cp <= '7' ) { ch *= 8; ch += *cp++ - '0'; if ( *cp >= '0' && *cp <= '7' ) { ch *= 8; ch += *cp++ - '0'; } } *rp = ch; } break; default: *rp = '\\'; break; } while ( cp[0] != '\0' && cp[0] != '\\' ) *rp++ = *cp++; } while ( cp[0] != '\0' ); /* Terminate string. */ terminate: *rp = '\0'; return retval; } static MSG *findMSG( u32 id ) { MSG *msg; for ( msg = baseMSG; msg; msg = msg->next ) { if ( msg->id == id ) return msg; } return NULL; } static MSG *setMSG( const char *msgid, const char *msgstr ) { u32 id = hash_string( msgid ); MSG *msg = findMSG( id ); if ( !msg ) { msg = ( MSG * )malloc( sizeof( MSG ) ); msg->id = id; msg->msgstr = NULL; msg->next = baseMSG; baseMSG = msg; } if ( msg ) { if ( msgstr ) { if ( msg->msgstr ) free( msg->msgstr ); //msg->msgstr = strdup(msgstr); msg->msgstr = expand_escape( msgstr ); } return msg; } return NULL; } void gettextCleanUp( void ) { while ( baseMSG ) { MSG *nextMsg = baseMSG->next; free( baseMSG->msgstr ); free( baseMSG ); baseMSG = nextMsg; } } bool gettextLoadLanguage( const char* langFile ) { FILE *f; char line[200]; char *lastID = NULL; gettextCleanUp(); f = fopen( langFile, "r" ); if ( !f ) return false; while ( fgets( line, sizeof( line ), f ) ) { // lines starting with # are comments if ( line[0] == '#' ) continue; else if ( strncmp( line, "msgid \"", 7 ) == 0 ) { char *msgid, *end; if ( lastID ) { free( lastID ); lastID = NULL; } msgid = &line[7]; end = strrchr( msgid, '"' ); if ( end && end - msgid > 1 ) { *end = 0; lastID = strdup( msgid ); } } else if ( strncmp( line, "msgstr \"", 8 ) == 0 ) { char *msgstr, *end; if ( lastID == NULL ) continue; msgstr = &line[8]; end = strrchr( msgstr, '"' ); if ( end && end - msgstr > 1 ) { *end = 0; setMSG( lastID, msgstr ); } free( lastID ); lastID = NULL; } } fclose( f ); return true; } const char *gettext( const char *msgid ) { MSG *msg = findMSG( hash_string( msgid ) ); if ( msg && msg->msgstr ) return msg->msgstr; return msgid; }