r7360: added a few more heimdal configure tests
[jelmer/samba4-debian.git] / source / intl / lang_tdb.c
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 #include "lib/tdb/include/tdbutil.h"
23 #include "system/time.h"
24 #include "system/filesys.h"
25 #include "pstring.h"
26
27 static TDB_CONTEXT *tdb;
28
29 /* the currently selected language */
30 static char *current_lang;
31
32
33 /* load a msg file into the tdb */
34 static BOOL load_msg(const char *msg_file)
35 {
36         char **lines;
37         int num_lines, i;
38         char *msgid, *msgstr;
39         TDB_DATA key, data;
40
41         lines = file_lines_load(msg_file, &num_lines);
42
43         if (!lines) {
44                 return False;
45         }
46
47         if (tdb_lockall(tdb) != 0) return False;
48
49         /* wipe the db */
50         tdb_traverse(tdb, tdb_traverse_delete_fn, NULL);
51
52         msgid = NULL;
53         
54         for (i=0;i<num_lines;i++) {
55                 if (strncmp(lines[i], "msgid \"", 7) == 0) {
56                         msgid = lines[i] + 7;
57                 }
58                 if (msgid && strncmp(lines[i], "msgstr \"", 8) == 0) {
59                         msgstr = lines[i] + 8;
60                         trim_string(msgid, NULL, "\"");
61                         trim_string(msgstr, NULL, "\"");
62                         if (*msgstr == 0) {
63                                 msgstr = msgid;
64                         }
65                         key.dptr = msgid;
66                         key.dsize = strlen(msgid)+1;
67                         data.dptr = msgstr;
68                         data.dsize = strlen(msgstr)+1;
69                         tdb_store(tdb, key, data, 0);
70                         msgid = NULL;
71                 }
72         }
73
74         file_lines_free(lines);
75         tdb_unlockall(tdb);
76
77         return True;
78 }
79
80
81 /* work out what language to use from locale variables */
82 static const char *get_lang(void)
83 {
84         const char *vars[] = {"LANGUAGE", "LC_ALL", "LC_LANG", "LANG", NULL};
85         int i;
86         char *p;
87
88         for (i=0; vars[i]; i++) {
89                 if ((p = getenv(vars[i]))) {
90                         return p;
91                 }
92         }
93
94         return NULL;
95 }
96
97 /* initialise the message translation subsystem. If the "lang" argument
98    is NULL then get the language from the normal environment variables */
99 BOOL lang_tdb_init(const char *lang)
100 {
101         char *path = NULL;
102         char *msg_path = NULL;
103         struct stat st;
104         static int initialised;
105         time_t loadtime;
106         TALLOC_CTX *mem_ctx;
107
108         /* we only want to init once per process, unless given
109            an override */
110         if (initialised && !lang) return True;
111
112         if (initialised) {
113                 /* we are re-initialising, free up any old init */
114                 if (tdb) {
115                         tdb_close(tdb);
116                         tdb = NULL;
117                 }
118                 SAFE_FREE(current_lang);
119         }
120
121         initialised = 1;
122
123         if (!lang) {
124                 /* no lang given, use environment */
125                 lang = get_lang();
126         }
127
128         /* if no lang then we don't translate */
129         if (!lang) return True;
130
131         mem_ctx = talloc_init("lang_tdb_init");
132         if (!mem_ctx) {
133                 return False;
134         }
135         asprintf(&msg_path, "%s.msg", lib_path(mem_ctx, (const char *)lang));
136         if (stat(msg_path, &st) != 0) {
137                 /* the msg file isn't available */
138                 free(msg_path);
139                 talloc_free(mem_ctx);
140                 return False;
141         }
142         
143
144         asprintf(&path, "%s%s.tdb", lock_path(mem_ctx, "lang_"), lang);
145
146         tdb = tdb_open(path, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0644);
147         if (!tdb) {
148                 tdb = tdb_open(path, 0, TDB_DEFAULT, O_RDONLY, 0);
149                 free(path);
150                 free(msg_path);
151                 talloc_free(mem_ctx);
152                 if (!tdb) return False;
153                 current_lang = strdup(lang);
154                 return True;
155         }
156
157         free(path);
158         talloc_free(mem_ctx);
159
160         loadtime = tdb_fetch_int32(tdb, "/LOADTIME/");
161
162         if (loadtime == -1 || loadtime < st.st_mtime) {
163                 load_msg(msg_path);
164                 tdb_store_int32(tdb, "/LOADTIME/", (int)time(NULL));
165         }
166         free(msg_path);
167
168         current_lang = strdup(lang);
169
170         return True;
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 */
176 char *lang_msg(const char *msgid)
177 {
178         TDB_DATA key, data;
179
180         lang_tdb_init(NULL);
181
182         if (!tdb) return strdup(msgid);
183
184         key.dptr = strdup(msgid);
185         key.dsize = strlen(msgid)+1;
186         
187         data = tdb_fetch(tdb, key);
188
189         free(key.dptr);
190
191         /* if the message isn't found then we still need to return a pointer
192            that can be freed. Pity. */
193         if (!data.dptr)
194                 return strdup(msgid);
195
196         return data.dptr;
197 }
198
199
200 /* free up a string from lang_msg() */
201 void lang_msg_free(char *msgstr)
202 {
203         free(msgstr);
204 }
205
206
207 /*
208   when the _() translation macro is used there is no obvious place to free
209   the resulting string and there is no easy way to give a static pointer.
210   All we can do is rotate between some static buffers and hope a single d_printf() 
211   doesn't have more calls to _() than the number of buffers 
212 */
213 const char *lang_msg_rotate(const char *msgid)
214 {
215 #define NUM_LANG_BUFS 4
216         const char *msgstr;
217         static pstring bufs[NUM_LANG_BUFS];
218         static int next;
219
220         msgstr = lang_msg(msgid);
221         if (!msgstr) return msgid;
222
223         pstrcpy(bufs[next], msgstr);
224         msgstr = bufs[next];
225
226         next = (next+1) % NUM_LANG_BUFS;
227         
228         return msgstr;
229 }
230
231
232 /* 
233    return the current language - needed for language file mappings 
234 */
235 char *lang_tdb_current(void)
236 {
237         return current_lang;
238 }