1/* 2 Unix SMB/CIFS implementation. 3 tdb based replacement for gettext 4 Copyright (C) Andrew Tridgell 2001 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19*/ 20 21#include "includes.h" 22 23static TDB_CONTEXT *tdb; 24 25/* the currently selected language */ 26static char *current_lang; 27 28 29/* load a msg file into the tdb */ 30static BOOL load_msg(const char *msg_file) 31{ 32 char **lines; 33 int num_lines, i; 34 char *msgid, *msgstr; 35 TDB_DATA key, data; 36 37 lines = file_lines_load(msg_file, &num_lines); 38 39 if (!lines) { 40 return False; 41 } 42 43 if (tdb_lockall(tdb) != 0) return False; 44 45 /* wipe the db */ 46 tdb_traverse(tdb, tdb_traverse_delete_fn, NULL); 47 48 msgid = NULL; 49 50 for (i=0;i<num_lines;i++) { 51 if (strncmp(lines[i], "msgid \"", 7) == 0) { 52 msgid = lines[i] + 7; 53 } 54 if (msgid && strncmp(lines[i], "msgstr \"", 8) == 0) { 55 msgstr = lines[i] + 8; 56 trim_char(msgid, '\0', '\"'); 57 trim_char(msgstr, '\0', '\"'); 58 if (*msgstr == 0) { 59 msgstr = msgid; 60 } 61 all_string_sub(msgid, "\\n", "\n", 0); 62 all_string_sub(msgstr, "\\n", "\n", 0); 63 key.dptr = msgid; 64 key.dsize = strlen(msgid)+1; 65 data.dptr = msgstr; 66 data.dsize = strlen(msgstr)+1; 67 tdb_store(tdb, key, data, 0); 68 msgid = NULL; 69 } 70 } 71 72 file_lines_free(lines); 73 tdb_unlockall(tdb); 74 75 return True; 76} 77 78 79/* work out what language to use from locale variables */ 80static const char *get_lang(void) 81{ 82 const char *vars[] = {"LANGUAGE", "LC_ALL", "LC_LANG", "LANG", NULL}; 83 int i; 84 char *p; 85 86 for (i=0; vars[i]; i++) { 87 if ((p = getenv(vars[i]))) { 88 return p; 89 } 90 } 91 92 return NULL; 93} 94 95/* initialise the message translation subsystem. If the "lang" argument 96 is NULL then get the language from the normal environment variables */ 97BOOL lang_tdb_init(const char *lang) 98{ 99 char *path = NULL; 100 char *msg_path = NULL; 101 struct stat st; 102 static int initialised; 103 time_t loadtime; 104 BOOL result = False; 105 106 /* we only want to init once per process, unless given 107 an override */ 108 if (initialised && !lang) 109 return True; 110 111 if (initialised) { 112 /* we are re-initialising, free up any old init */ 113 if (tdb) { 114 tdb_close(tdb); 115 tdb = NULL; 116 } 117 SAFE_FREE(current_lang); 118 } 119 120 initialised = 1; 121 122 if (!lang) { 123 /* no lang given, use environment */ 124 lang = get_lang(); 125 } 126 127 /* if no lang then we don't translate */ 128 if (!lang) 129 return True; 130 131 asprintf(&msg_path, "%s.msg", lib_path((const char *)lang)); 132 if (stat(msg_path, &st) != 0) { 133 /* the msg file isn't available */ 134 DEBUG(10, ("lang_tdb_init: %s: %s\n", msg_path, 135 strerror(errno))); 136 goto done; 137 } 138 139 asprintf(&path, "%s%s.tdb", lock_path("lang_"), lang); 140 141 DEBUG(10, ("lang_tdb_init: loading %s\n", path)); 142 143 tdb = tdb_open_log(path, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0644); 144 if (!tdb) { 145 tdb = tdb_open_log(path, 0, TDB_DEFAULT, O_RDONLY, 0); 146 if (!tdb) { 147 DEBUG(10, ("lang_tdb_init: %s: %s\n", path, 148 strerror(errno))); 149 goto done; 150 } 151 current_lang = SMB_STRDUP(lang); 152 result = True; 153 goto done; 154 } 155 156 loadtime = tdb_fetch_int32(tdb, "/LOADTIME/"); 157 158 if (loadtime == -1 || loadtime < st.st_mtime) { 159 load_msg(msg_path); 160 tdb_store_int32(tdb, "/LOADTIME/", (int)time(NULL)); 161 } 162 163 current_lang = SMB_STRDUP(lang); 164 result = True; 165 166 done: 167 SAFE_FREE(msg_path); 168 SAFE_FREE(path); 169 170 return result; 171} 172 173/* translate a msgid to a message string in the current language 174 returns a string that must be freed by calling lang_msg_free() 175*/ 176const char *lang_msg(const char *msgid) 177{ 178 TDB_DATA key, data; 179 const char *p; 180 char *q, *msgid_quoted; 181 int count; 182 183 lang_tdb_init(NULL); 184 185 if (!tdb) return msgid; 186 187 /* Due to the way quotes in msgids are escaped in the msg file we 188 must replace " with \" before doing a lookup in the tdb. */ 189 190 count = 0; 191 192 for(p = msgid; *p; p++) { 193 if (*p == '\"') 194 count++; 195 } 196 197 if (!(msgid_quoted = SMB_MALLOC(strlen(msgid) + count + 1))) 198 return msgid; 199 200 /* string_sub() is unsuitable here as it replaces some punctuation 201 chars with underscores. */ 202 203 for(p = msgid, q = msgid_quoted; *p; p++) { 204 if (*p == '\"') { 205 *q = '\\'; 206 q++; 207 } 208 *q = *p; 209 q++; 210 } 211 212 *q = 0; 213 214 key.dptr = (char *)msgid_quoted; 215 key.dsize = strlen(msgid_quoted)+1; 216 217 data = tdb_fetch(tdb, key); 218 219 free(msgid_quoted); 220 221 /* if the message isn't found then we still need to return a pointer 222 that can be freed. Pity. */ 223 if (!data.dptr) 224 return SMB_STRDUP(msgid); 225 226 return (const char *)data.dptr; 227} 228 229 230/* free up a string from lang_msg() */ 231void lang_msg_free(const char *msgstr) 232{ 233 if (!tdb) return; 234 free((void *)msgstr); 235} 236 237 238/* 239 when the _() translation macro is used there is no obvious place to free 240 the resulting string and there is no easy way to give a static pointer. 241 All we can do is rotate between some static buffers and hope a single d_printf() 242 doesn't have more calls to _() than the number of buffers 243*/ 244const char *lang_msg_rotate(const char *msgid) 245{ 246#define NUM_LANG_BUFS 16 247 char *msgstr; 248 static pstring bufs[NUM_LANG_BUFS]; 249 static int next; 250 251 msgstr = (char *)lang_msg(msgid); 252 if (!msgstr) return msgid; 253 254 pstrcpy(bufs[next], msgstr); 255 msgstr = bufs[next]; 256 257 next = (next+1) % NUM_LANG_BUFS; 258 259 return msgstr; 260} 261 262 263/* 264 return the current language - needed for language file mappings 265*/ 266char *lang_tdb_current(void) 267{ 268 return current_lang; 269} 270