Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/lib/libast/misc/translate.c
1810 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1985-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* David Korn <[email protected]> *
19
* Phong Vo <[email protected]> *
20
* *
21
***********************************************************************/
22
#pragma prototyped
23
24
/*
25
* AT&T Research and SCO
26
* ast l10n message translation
27
*/
28
29
#include "lclib.h"
30
31
#include <cdt.h>
32
#include <error.h>
33
#include <mc.h>
34
#include <nl_types.h>
35
36
#ifndef DEBUG_trace
37
#define DEBUG_trace 0
38
#endif
39
40
#define NOCAT ((nl_catd)-1)
41
#define GAP 100
42
43
typedef struct
44
{
45
Dtlink_t link; /* dictionary link */
46
Dt_t* messages; /* message dictionary handle */
47
nl_catd cat; /* message catalog handle */
48
int debug; /* special debug locale */
49
const char* locale; /* message catalog locale */
50
const char* nlspath; /* message catalog NLSPATH */
51
char name[1]; /* catalog name */
52
} Catalog_t;
53
54
typedef struct
55
{
56
Dtlink_t link; /* dictionary link */
57
Catalog_t* cat; /* current catalog pointer */
58
int set; /* set number */
59
int seq; /* sequence number */
60
char text[1]; /* message text */
61
} Message_t;
62
63
typedef struct
64
{
65
Sfio_t* sp; /* temp string stream */
66
int off; /* string base offset */
67
} Temp_t;
68
69
typedef struct
70
{
71
Dtdisc_t message_disc; /* message dict discipline */
72
Dtdisc_t catalog_disc; /* catalog dict discipline */
73
Dt_t* catalogs; /* catalog dictionary handle */
74
Sfio_t* tmp; /* temporary string stream */
75
int error; /* no dictionaries! */
76
char null[1]; /* null string */
77
} State_t;
78
79
static State_t state =
80
{
81
{ offsetof(Message_t, text), 0, 0 },
82
{ offsetof(Catalog_t, name), 0, 0 },
83
};
84
85
static int
86
tempget(Sfio_t* sp)
87
{
88
if (sfstrtell(sp) > sfstrsize(sp) / 2)
89
sfstrseek(sp, 0, SEEK_SET);
90
return sfstrtell(sp);
91
}
92
93
static char*
94
tempuse(Sfio_t* sp, int off)
95
{
96
sfputc(sp, 0);
97
return sfstrbase(sp) + off;
98
}
99
100
/*
101
* add msg to dict
102
*/
103
104
static int
105
entry(Dt_t* dict, int set, int seq, const char* msg)
106
{
107
Message_t* mp;
108
109
if (!(mp = newof(0, Message_t, 1, strlen(msg))))
110
return 0;
111
strcpy(mp->text, msg);
112
mp->set = set;
113
mp->seq = seq;
114
if (!dtinsert(dict, mp))
115
{
116
free(mp);
117
return 0;
118
}
119
#if DEBUG_trace > 1
120
sfprintf(sfstderr, "AHA#%d:%s set %d seq %d msg `%s'\n", __LINE__, __FILE__, set, seq, msg);
121
#endif
122
return 1;
123
}
124
125
/*
126
* find catalog in locale and return catopen() descriptor
127
*/
128
129
static nl_catd
130
find(const char* locale, const char* catalog)
131
{
132
char* o;
133
nl_catd d;
134
char path[PATH_MAX];
135
136
if (!mcfind(locale, catalog, LC_MESSAGES, 0, path, sizeof(path)) || (d = catopen(path, NL_CAT_LOCALE)) == NOCAT)
137
{
138
if (locale == (const char*)lc_categories[AST_LC_MESSAGES].prev)
139
o = 0;
140
else if (o = setlocale(LC_MESSAGES, NiL))
141
{
142
ast.locale.set |= AST_LC_internal;
143
setlocale(LC_MESSAGES, locale);
144
}
145
d = catopen(catalog, NL_CAT_LOCALE);
146
if (o)
147
{
148
setlocale(LC_MESSAGES, o);
149
ast.locale.set &= ~AST_LC_internal;
150
}
151
}
152
return d;
153
}
154
155
/*
156
* initialize the catalog s by loading in the default locale messages
157
*/
158
159
static Catalog_t*
160
init(register char* s)
161
{
162
register Catalog_t* cp;
163
register int n;
164
register int m;
165
register int set;
166
nl_catd d;
167
168
/*
169
* insert into the catalog dictionary
170
*/
171
172
if (!(cp = newof(0, Catalog_t, 1, strlen(s))))
173
return 0;
174
strcpy(cp->name, s);
175
if (!dtinsert(state.catalogs, cp))
176
{
177
free(cp);
178
return 0;
179
}
180
cp->cat = NOCAT;
181
182
/*
183
* locate the default locale catalog
184
*/
185
186
if ((d = find("C", s)) != NOCAT)
187
{
188
/*
189
* load the default locale messages
190
* this assumes one mesage set for ast (AST_MESSAGE_SET or fallback to 1)
191
* different packages can share the same message catalog
192
* name by using different message set numbers
193
* see <mc.h> mcindex()
194
*
195
* this method requires a scan of each catalog, and the
196
* catalogs do not advertise the max message number, so
197
* we assume there are no messages after a gap of GAP
198
* missing messages
199
*/
200
201
if (cp->messages = dtopen(&state.message_disc, Dtset))
202
{
203
n = m = 0;
204
for (;;)
205
{
206
n++;
207
if (((s = catgets(d, set = AST_MESSAGE_SET, n, state.null)) && *s || (s = catgets(d, set = 1, n, state.null)) && *s) && entry(cp->messages, set, n, s))
208
m = n;
209
else if ((n - m) > GAP)
210
break;
211
}
212
if (!m)
213
{
214
dtclose(cp->messages);
215
cp->messages = 0;
216
}
217
}
218
catclose(d);
219
}
220
return cp;
221
}
222
223
/*
224
* return the C locale message pointer for msg in cat
225
* cat may be a : separated list of candidate names
226
*/
227
228
static Message_t*
229
match(const char* cat, const char* msg)
230
{
231
register char* s;
232
register char* t;
233
Catalog_t* cp;
234
Message_t* mp;
235
size_t n;
236
237
char buf[1024];
238
239
s = (char*)cat;
240
for (;;)
241
{
242
if (t = strchr(s, ':'))
243
{
244
if (s == (char*)cat)
245
{
246
if ((n = strlen(s)) >= sizeof(buf))
247
n = sizeof(buf) - 1;
248
s = (char*)memcpy(buf, s, n);
249
s[n] = 0;
250
t = strchr(s, ':');
251
}
252
*t = 0;
253
}
254
if (*s && ((cp = (Catalog_t*)dtmatch(state.catalogs, s)) || (cp = init(s))) && cp->messages && (mp = (Message_t*)dtmatch(cp->messages, msg)))
255
{
256
mp->cat = cp;
257
return mp;
258
}
259
if (!t)
260
break;
261
s = t + 1;
262
}
263
return 0;
264
}
265
266
/*
267
* translate() is called with four arguments:
268
*
269
* loc the LC_MESSAGES locale name
270
* cmd the calling command name
271
* cat the catalog name, possibly a : separated list
272
* "libFOO" FOO library messages
273
* "libshell" ksh command messages
274
* "SCRIPT" script SCRIPT application messages
275
* msg message text to be translated
276
*
277
* the translated message text is returned on success
278
* otherwise the original msg is returned
279
*
280
* The first time translate() is called (for a non-C locale)
281
* it creates the state.catalogs dictionary. A dictionary entry
282
* (Catalog_t) is made each time translate() is called with a new
283
* cmd:cat argument.
284
*
285
* The X/Open interface catgets() is used to obtain a translated
286
* message. Its arguments include the message catalog name
287
* and the set/sequence numbers within the catalog. An additional
288
* dictionary, with entries of type Message_t, is needed for
289
* mapping untranslated message strings to the set/sequence numbers
290
* needed by catgets(). A separate Message_t dictionary is maintained
291
* for each Catalog_t.
292
*/
293
294
char*
295
translate(const char* loc, const char* cmd, const char* cat, const char* msg)
296
{
297
register char* r;
298
char* t;
299
int p;
300
int oerrno;
301
Catalog_t* cp;
302
Message_t* mp;
303
304
static uint32_t serial;
305
static char* nlspath;
306
307
oerrno = errno;
308
r = (char*)msg;
309
310
/*
311
* quick out
312
*/
313
314
if (!cmd && !cat)
315
goto done;
316
if (cmd && (t = strrchr(cmd, '/')))
317
cmd = (const char*)(t + 1);
318
319
/*
320
* initialize the catalogs dictionary
321
*/
322
323
if (!state.catalogs)
324
{
325
if (state.error)
326
goto done;
327
if (!(state.tmp = sfstropen()))
328
{
329
state.error = 1;
330
goto done;
331
}
332
if (!(state.catalogs = dtopen(&state.catalog_disc, Dtset)))
333
{
334
sfclose(state.tmp);
335
state.error = 1;
336
goto done;
337
}
338
}
339
340
/*
341
* get the message
342
* or do we have to spell it out for you
343
*/
344
345
if ((!cmd || !(mp = match(cmd, msg))) &&
346
(!cat || !(mp = match(cat, msg))) &&
347
(!error_info.catalog || !(mp = match(error_info.catalog, msg))) &&
348
(!ast.id || !(mp = match(ast.id, msg))) ||
349
!(cp = mp->cat))
350
{
351
#if DEBUG_trace > 1
352
sfprintf(sfstderr, "AHA#%d:%s cmd %s cat %s:%s id %s msg `%s'\n", __LINE__, __FILE__, cmd, cat, error_info.catalog, ast.id, msg);
353
#endif
354
cp = 0;
355
goto done;
356
}
357
358
/*
359
* adjust for the current locale
360
*/
361
362
#if DEBUG_trace
363
sfprintf(sfstderr, "AHA#%d:%s cp->locale `%s' %p loc `%s' %p\n", __LINE__, __FILE__, cp->locale, cp->locale, loc, loc);
364
#endif
365
if (serial != ast.env_serial)
366
{
367
serial = ast.env_serial;
368
nlspath = getenv("NLSPATH");
369
}
370
if (cp->locale != loc || cp->nlspath != nlspath)
371
{
372
cp->locale = loc;
373
cp->nlspath = nlspath;
374
if (cp->cat != NOCAT)
375
catclose(cp->cat);
376
if ((cp->cat = find(cp->locale, cp->name)) == NOCAT)
377
cp->debug = streq(cp->locale, "debug");
378
else
379
cp->debug = 0;
380
#if DEBUG_trace
381
sfprintf(sfstderr, "AHA#%d:%s cp->cat %p cp->debug %d NOCAT %p\n", __LINE__, __FILE__, cp->cat, cp->debug, NOCAT);
382
#endif
383
}
384
if (cp->cat == NOCAT)
385
{
386
if (cp->debug)
387
{
388
p = tempget(state.tmp);
389
sfprintf(state.tmp, "(%s,%d,%d)", cp->name, mp->set, mp->seq);
390
r = tempuse(state.tmp, p);
391
}
392
else if (ast.locale.set & AST_LC_debug)
393
{
394
p = tempget(state.tmp);
395
sfprintf(state.tmp, "(%s,%d,%d)%s", cp->name, mp->set, mp->seq, r);
396
r = tempuse(state.tmp, p);
397
}
398
}
399
else
400
{
401
/*
402
* get the translated message
403
*/
404
405
r = catgets(cp->cat, mp->set, mp->seq, msg);
406
if (r != (char*)msg)
407
{
408
if (streq(r, (char*)msg))
409
r = (char*)msg;
410
else if (strcmp(fmtfmt(r), fmtfmt(msg)))
411
{
412
sfprintf(sfstderr, "locale %s catalog %s message %d.%d \"%s\" does not match \"%s\"\n", cp->locale, cp->name, mp->set, mp->seq, r, msg);
413
r = (char*)msg;
414
}
415
}
416
if (ast.locale.set & AST_LC_debug)
417
{
418
p = tempget(state.tmp);
419
sfprintf(state.tmp, "(%s,%d,%d)%s", cp->name, mp->set, mp->seq, r);
420
r = tempuse(state.tmp, p);
421
}
422
}
423
if (ast.locale.set & AST_LC_translate)
424
sfprintf(sfstderr, "translate locale=%s catalog=%s set=%d seq=%d \"%s\" => \"%s\"\n", cp->locale, cp->name, mp->set, mp->seq, msg, r == (char*)msg ? "NOPE" : r);
425
done:
426
if (r == (char*)msg && (!cp && streq(loc, "debug") || cp && cp->debug))
427
{
428
p = tempget(state.tmp);
429
sfprintf(state.tmp, "(%s,%s,%s,%s)", loc, cmd, cat, r);
430
r = tempuse(state.tmp, p);
431
}
432
errno = oerrno;
433
return r;
434
}
435
436