1 |
/* $MidnightBSD$ */ |
2 |
/* |
3 |
* CDDL HEADER START |
4 |
* |
5 |
* The contents of this file are subject to the terms of the |
6 |
* Common Development and Distribution License (the "License"). |
7 |
* You may not use this file except in compliance with the License. |
8 |
* |
9 |
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
10 |
* or http://www.opensolaris.org/os/licensing. |
11 |
* See the License for the specific language governing permissions |
12 |
* and limitations under the License. |
13 |
* |
14 |
* When distributing Covered Code, include this CDDL HEADER in each |
15 |
* file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
16 |
* If applicable, add the following below this CDDL HEADER, with the |
17 |
* fields enclosed by brackets "[]" replaced with your own identifying |
18 |
* information: Portions Copyright [yyyy] [name of copyright owner] |
19 |
* |
20 |
* CDDL HEADER END |
21 |
*/ |
22 |
/* |
23 |
* Copyright 2008 Sun Microsystems, Inc. All rights reserved. |
24 |
* Use is subject to license terms. |
25 |
* |
26 |
* sgsmsg generates several message files from an input template file. Messages |
27 |
* are constructed for use with gettext(3i) - the default - or catgets(3c). The |
28 |
* files generate are: |
29 |
* |
30 |
* msg.h a header file containing definitions for each message. The -h |
31 |
* option triggers the creation of these definitions and specifies |
32 |
* the name to use. |
33 |
* |
34 |
* msg.c a data array of message strings. The msg.h definitions are |
35 |
* offsets into this array. The -d option triggers the creation of |
36 |
* these definitions and specifies the name to use. |
37 |
* |
38 |
* messages a message file suitable for catgets(3c) or gettext(3i) use. The |
39 |
* -m option triggers this output and specifies the filename to be |
40 |
* used. |
41 |
* |
42 |
* The template file is processed based on the first character of each line: |
43 |
* |
44 |
* # or $ entries are copied (as is) to the message file (messages). |
45 |
* |
46 |
* @ token(s) entries are translated. Two translations are possible dependent |
47 |
* on whether one or more tokens are supplied: |
48 |
* |
49 |
* A single token is interpreted as one of two reserved message |
50 |
* output indicators, or a message identifier. The reserved output |
51 |
* indicator _START_ enables output to the message file - Note that |
52 |
* the occurance of any other @ token will also enable message |
53 |
* output. The reserved output indicator _END_ disables output to |
54 |
* the message file. The use of these two indicators provides for |
55 |
* only those message strings that require translation to be output |
56 |
* to the message file. |
57 |
* |
58 |
* Besides the reserved output indicators, a single token is taken |
59 |
* to be a message identifier which will be subsituted for a |
60 |
* `setid' for catgets(3c) output, or a `domain' name for |
61 |
* gettext(3i) output. This value is determine by substituting the |
62 |
* token for the associated definition found in the message |
63 |
* identifier file (specified with the -i option). |
64 |
* |
65 |
* Multiple tokens are taken to be a message definition followed by |
66 |
* the associated message string. The message string is copied to |
67 |
* the data array being built in msg.c. The index into this array |
68 |
* becomes the `message' identifier created in the msg.h file. |
69 |
*/ |
70 |
#pragma ident "%Z%%M% %I% %E% SMI" |
71 |
|
72 |
#include <fcntl.h> |
73 |
#include <stdlib.h> |
74 |
#include <stdio.h> |
75 |
#include <unistd.h> |
76 |
#include <limits.h> |
77 |
#include <string.h> |
78 |
#include <ctype.h> |
79 |
#include <errno.h> |
80 |
#include <sys/param.h> |
81 |
|
82 |
#include <sgs.h> |
83 |
#include <_string_table.h> |
84 |
|
85 |
/* |
86 |
* Define any error message strings. |
87 |
*/ |
88 |
static const char |
89 |
* Errmsg_malt = "sgsmsg: file %s: line %d: malformed input " |
90 |
"at line\n", |
91 |
* Errmsg_nmem = "sgsmsg: memory allocation failed: %s\n", |
92 |
* Errmsg_opne = "sgsmsg: file %s: open failed: %s\n", |
93 |
* Errmsg_wrte = "sgsmsg: file %s: write failed: %s\n", |
94 |
* Errmsg_read = "sgsmsg: file %s: read failed %s\n", |
95 |
* Errmsg_stnw = "sgsmsg: st_new(): failed: %s\n", |
96 |
* Errmsg_stin = "sgsmsg: Str_tbl insert failed: %s\n", |
97 |
* Errmsg_mnfn = "sgsmsg: message not found in Str_tbl: %s\n", |
98 |
* Errmsg_use = "usage: sgsmsg [-clv] [-d mesgdata] [-h mesgdefs] " |
99 |
"[-m messages] [-n name] [-i mesgident] file ...\n"; |
100 |
|
101 |
/* |
102 |
* Define all output filenames and associated descriptors. |
103 |
*/ |
104 |
static FILE *fddefs, *fddata, *fdmsgs, *fdmids, *fddesc; |
105 |
static char *fldefs, *fldata, *flmsgs, *flmids, *fldesc; |
106 |
static FILE *fdlint; |
107 |
static char fllint[MAXPATHLEN]; |
108 |
|
109 |
static uint_t vflag; /* verbose flag */ |
110 |
static Str_tbl *stp; /* string table */ |
111 |
|
112 |
/* |
113 |
* Define any default strings. |
114 |
*/ |
115 |
static const char |
116 |
*nmlint = "/tmp/sgsmsg.lint", |
117 |
*interface = "sgs_msg", |
118 |
*start = "_START_", |
119 |
*end = "_END_"; |
120 |
|
121 |
/* |
122 |
* Define any default flags and data items. |
123 |
*/ |
124 |
static int cflag = 0, lflag = 0, prtmsgs = 0, line, ptr = 1, msgid = 0; |
125 |
static char *mesgid = 0, *setid = 0, *domain = 0; |
126 |
|
127 |
typedef struct msg_string { |
128 |
char *ms_defn; |
129 |
char *ms_message; |
130 |
struct msg_string *ms_next; |
131 |
} msg_string; |
132 |
|
133 |
static msg_string *msg_head; |
134 |
static msg_string *msg_tail; |
135 |
|
136 |
/* |
137 |
* message_append() is responsible for both inserting strings into |
138 |
* the master Str_tbl as well as maintaining a list of the |
139 |
* DEFINITIONS associated with each string. |
140 |
* |
141 |
* The list of strings is traversed at the end once the full |
142 |
* Str_tbl has been constructed - and string offsets can be |
143 |
* assigned. |
144 |
*/ |
145 |
static void |
146 |
message_append(const char *defn, const char *message) |
147 |
{ |
148 |
msg_string *msg; |
149 |
if ((msg = calloc(sizeof (msg_string), 1)) == 0) { |
150 |
(void) fprintf(stderr, Errmsg_nmem, strerror(errno)); |
151 |
exit(1); |
152 |
} |
153 |
|
154 |
/* |
155 |
* Initialize the string table. |
156 |
*/ |
157 |
if ((stp == 0) && ((stp = st_new(FLG_STNEW_COMPRESS)) == NULL)) { |
158 |
(void) fprintf(stderr, Errmsg_stnw, strerror(errno)); |
159 |
exit(1); |
160 |
} |
161 |
|
162 |
|
163 |
if ((msg->ms_defn = strdup(defn)) == 0) { |
164 |
(void) fprintf(stderr, Errmsg_nmem, strerror(errno)); |
165 |
exit(1); |
166 |
} |
167 |
if ((msg->ms_message = strdup(message)) == 0) { |
168 |
(void) fprintf(stderr, Errmsg_nmem, strerror(errno)); |
169 |
exit(1); |
170 |
} |
171 |
|
172 |
if (st_insert(stp, msg->ms_message) == -1) { |
173 |
(void) fprintf(stderr, Errmsg_stin, |
174 |
message); |
175 |
exit(1); |
176 |
} |
177 |
|
178 |
if (msg_head == 0) { |
179 |
msg_head = msg_tail = msg; |
180 |
return; |
181 |
} |
182 |
msg_tail->ms_next = msg; |
183 |
msg_tail = msg; |
184 |
} |
185 |
|
186 |
/* |
187 |
* Initialize a setid value. Given a setid definition determine its numeric |
188 |
* value from the specified message identifier file (specified with the -i |
189 |
* option). Return a pointer to the numeric string. |
190 |
*/ |
191 |
static int |
192 |
getmesgid(char *id) |
193 |
{ |
194 |
char *buffer, *token, *_mesgid = 0, *_setid = 0, *_domain = 0; |
195 |
|
196 |
/* |
197 |
* If we're being asked to interpret a message id but the user didn't |
198 |
* provide the required message identifier file (-i option) we're in |
199 |
* trouble. |
200 |
*/ |
201 |
if (flmids == 0) { |
202 |
(void) fprintf(stderr, "sgsmsg: file %s: line %d: mesgid %s: " |
203 |
"unable to process mesgid\n\t" |
204 |
"no message identifier file specified " |
205 |
"(see -i option)\n", fldesc, line, id); |
206 |
return (1); |
207 |
} |
208 |
|
209 |
if ((buffer = malloc(LINE_MAX)) == 0) { |
210 |
(void) fprintf(stderr, Errmsg_nmem, strerror(errno)); |
211 |
return (1); |
212 |
} |
213 |
|
214 |
/* |
215 |
* Read the message identifier file and locate the required mesgid. |
216 |
*/ |
217 |
rewind(fdmids); |
218 |
while (fgets(buffer, LINE_MAX, fdmids) != NULL) { |
219 |
if ((token = strstr(buffer, id)) == NULL) |
220 |
continue; |
221 |
|
222 |
/* |
223 |
* Establish individual strings for the mesgid, setid and domain |
224 |
* values. |
225 |
*/ |
226 |
_mesgid = token; |
227 |
while (!(isspace(*token))) |
228 |
token++; |
229 |
*token++ = 0; |
230 |
|
231 |
while (isspace(*token)) |
232 |
token++; |
233 |
_setid = token; |
234 |
while (!(isspace(*token))) |
235 |
token++; |
236 |
*token++ = 0; |
237 |
|
238 |
while (isspace(*token)) |
239 |
token++; |
240 |
_domain = token; |
241 |
while (!(isspace(*token))) |
242 |
token++; |
243 |
*token = 0; |
244 |
break; |
245 |
} |
246 |
|
247 |
/* |
248 |
* Did we find a match? |
249 |
*/ |
250 |
if ((_mesgid == 0) || (_setid == 0) || (_domain == 0)) { |
251 |
(void) fprintf(stderr, "sgsmsg: file %s: line %d: mesgid %s: " |
252 |
"unable to process mesgid\n\t" |
253 |
"identifier does not exist in file %s\n", |
254 |
fldesc, line, id, flmids); |
255 |
return (1); |
256 |
} |
257 |
|
258 |
/* |
259 |
* Have we been here before? |
260 |
*/ |
261 |
if (mesgid) { |
262 |
if (cflag == 1) { |
263 |
/* |
264 |
* If we're being asked to process more than one mesgid |
265 |
* warn the user that only one mesgid can be used for |
266 |
* the catgets(3c) call. |
267 |
*/ |
268 |
(void) fprintf(stderr, "sgsmsg: file %s: line %d: " |
269 |
"setid %s: warning: multiple mesgids " |
270 |
"encountered\n\t" |
271 |
"last setting used in messaging code\n", |
272 |
fldesc, line, id); |
273 |
} |
274 |
} |
275 |
|
276 |
mesgid = _mesgid; |
277 |
setid = _setid; |
278 |
domain = _domain; |
279 |
|
280 |
/* |
281 |
* Generate the message file output (insure output flag is enabled). |
282 |
*/ |
283 |
if (prtmsgs != -1) |
284 |
prtmsgs = 1; |
285 |
if (fdmsgs && (prtmsgs == 1)) { |
286 |
if (cflag == 1) { |
287 |
if (fprintf(fdmsgs, "$quote \"\n$set %s\n", |
288 |
setid) < 0) { |
289 |
(void) fprintf(stderr, Errmsg_wrte, flmsgs, |
290 |
strerror(errno)); |
291 |
return (1); |
292 |
} |
293 |
} else { |
294 |
if (fprintf(fdmsgs, "domain\t\"%s\"\n", domain) < 0) { |
295 |
(void) fprintf(stderr, Errmsg_wrte, flmsgs, |
296 |
strerror(errno)); |
297 |
return (1); |
298 |
} |
299 |
} |
300 |
} |
301 |
|
302 |
/* |
303 |
* For catgets(3c) output generate a setid definition in the message |
304 |
* definition file. |
305 |
*/ |
306 |
if (fddefs && (cflag == 1) && |
307 |
(fprintf(fddefs, "#define\t%s\t%s\n\n", mesgid, setid) < 0)) { |
308 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); |
309 |
return (1); |
310 |
} |
311 |
|
312 |
return (0); |
313 |
} |
314 |
|
315 |
/* |
316 |
* Dump contents of String Table to standard out |
317 |
*/ |
318 |
static void |
319 |
dump_stringtab(Str_tbl *stp) |
320 |
{ |
321 |
uint_t cnt; |
322 |
|
323 |
if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) { |
324 |
(void) printf("string table full size: %ld: uncompressed\n", |
325 |
stp->st_fullstrsize); |
326 |
return; |
327 |
} |
328 |
|
329 |
(void) printf("string table full size: %ld compressed down to: %ld\n\n", |
330 |
stp->st_fullstrsize, stp->st_strsize); |
331 |
(void) printf("string table compression information [%d buckets]:\n", |
332 |
stp->st_hbckcnt); |
333 |
|
334 |
for (cnt = 0; cnt < stp->st_hbckcnt; cnt++) { |
335 |
Str_hash *sthash = stp->st_hashbcks[cnt]; |
336 |
|
337 |
if (sthash == 0) |
338 |
continue; |
339 |
|
340 |
(void) printf(" bucket: [%d]\n", cnt); |
341 |
|
342 |
while (sthash) { |
343 |
size_t stroff = sthash->hi_mstr->sm_strlen - |
344 |
sthash->hi_strlen; |
345 |
|
346 |
if (stroff == 0) { |
347 |
(void) printf(" [%ld]: '%s' <master>\n", |
348 |
sthash->hi_refcnt, sthash->hi_mstr->sm_str); |
349 |
} else { |
350 |
(void) printf(" [%ld]: '%s' <suffix of: " |
351 |
"'%s'>\n", sthash->hi_refcnt, |
352 |
&sthash->hi_mstr->sm_str[stroff], |
353 |
sthash->hi_mstr->sm_str); |
354 |
} |
355 |
sthash = sthash->hi_next; |
356 |
} |
357 |
} |
358 |
} |
359 |
|
360 |
/* |
361 |
* Initialize the message definition header file stream. |
362 |
*/ |
363 |
static int |
364 |
init_defs(void) |
365 |
{ |
366 |
static char guard[FILENAME_MAX + 6]; |
367 |
char *optr; |
368 |
const char *iptr, *_ptr; |
369 |
|
370 |
/* |
371 |
* Establish a header guard name using the files basename. |
372 |
*/ |
373 |
for (iptr = 0, _ptr = fldefs; _ptr && (*_ptr != '\0'); _ptr++) { |
374 |
if (*_ptr == '/') |
375 |
iptr = _ptr + 1; |
376 |
} |
377 |
if (iptr == 0) |
378 |
iptr = fldefs; |
379 |
|
380 |
optr = guard; |
381 |
for (*optr++ = '_'; iptr && (*iptr != '\0'); iptr++, optr++) { |
382 |
if (*iptr == '.') { |
383 |
*optr++ = '_'; |
384 |
*optr++ = 'D'; |
385 |
*optr++ = 'O'; |
386 |
*optr++ = 'T'; |
387 |
*optr = '_'; |
388 |
} else |
389 |
*optr = toupper(*iptr); |
390 |
} |
391 |
|
392 |
if (fprintf(fddefs, "#ifndef\t%s\n#define\t%s\n\n", guard, guard) < 0) { |
393 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); |
394 |
return (1); |
395 |
} |
396 |
|
397 |
if (fprintf(fddefs, "#ifndef\t__lint\n\n") < 0) { |
398 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); |
399 |
return (1); |
400 |
} |
401 |
|
402 |
/* |
403 |
* add "typedef int Msg;" |
404 |
*/ |
405 |
if (fprintf(fddefs, "typedef int\tMsg;\n\n") < 0) { |
406 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); |
407 |
return (1); |
408 |
} |
409 |
|
410 |
/* |
411 |
* If the associated data array is global define a prototype. |
412 |
* Define a macro to access the array elements. |
413 |
*/ |
414 |
if (lflag == 0) { |
415 |
if (fprintf(fddefs, "extern\tconst char\t__%s[];\n\n", |
416 |
interface) < 0) { |
417 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, |
418 |
strerror(errno)); |
419 |
return (1); |
420 |
} |
421 |
} |
422 |
if (fprintf(fddefs, "#define\tMSG_ORIG(x)\t&__%s[x]\n\n", |
423 |
interface) < 0) { |
424 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); |
425 |
return (1); |
426 |
} |
427 |
|
428 |
/* |
429 |
* Generate a prototype to access the associated data array. |
430 |
*/ |
431 |
if (fprintf(fddefs, "extern\tconst char *\t_%s(Msg);\n\n", |
432 |
interface) < 0) { |
433 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); |
434 |
return (1); |
435 |
} |
436 |
if (fprintf(fddefs, "#define\tMSG_INTL(x)\t_%s(x)\n\n", |
437 |
interface) < 0) { |
438 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); |
439 |
return (1); |
440 |
} |
441 |
|
442 |
return (0); |
443 |
} |
444 |
|
445 |
|
446 |
/* |
447 |
* Finish the message definition header file. |
448 |
*/ |
449 |
static int |
450 |
fini_defs(void) |
451 |
{ |
452 |
if (fprintf(fddefs, "\n#else\t/* __lint */\n\n") < 0) { |
453 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); |
454 |
return (1); |
455 |
} |
456 |
|
457 |
/* |
458 |
* When __lint is defined, Msg is a char *. This allows lint to |
459 |
* check our format strings against it's arguments. |
460 |
*/ |
461 |
if (fprintf(fddefs, "\ntypedef char *\tMsg;\n\n") < 0) { |
462 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); |
463 |
return (1); |
464 |
} |
465 |
|
466 |
if (fprintf(fddefs, "extern\tconst char *\t_%s(Msg);\n\n", |
467 |
interface) < 0) { |
468 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); |
469 |
return (1); |
470 |
} |
471 |
|
472 |
if (lflag == 0) { |
473 |
if (fprintf(fddefs, "extern\tconst char\t__%s[];\n\n", |
474 |
interface) < 0) { |
475 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, |
476 |
strerror(errno)); |
477 |
return (1); |
478 |
} |
479 |
} |
480 |
|
481 |
if (fprintf(fddefs, |
482 |
"#define MSG_ORIG(x)\tx\n#define MSG_INTL(x)\tx\n") < 0) { |
483 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); |
484 |
return (1); |
485 |
} |
486 |
|
487 |
/* |
488 |
* Copy the temporary lint defs file into the new header. |
489 |
*/ |
490 |
if (fdlint) { |
491 |
long size; |
492 |
char *buf; |
493 |
|
494 |
size = ftell(fdlint); |
495 |
(void) rewind(fdlint); |
496 |
|
497 |
if ((buf = malloc(size)) == 0) { |
498 |
(void) fprintf(stderr, Errmsg_nmem, strerror(errno)); |
499 |
return (1); |
500 |
} |
501 |
if (fread(buf, size, 1, fdlint) == 0) { |
502 |
(void) fprintf(stderr, Errmsg_read, fllint, |
503 |
strerror(errno)); |
504 |
return (1); |
505 |
} |
506 |
if (fwrite(buf, size, 1, fddefs) == 0) { |
507 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, |
508 |
strerror(errno)); |
509 |
return (1); |
510 |
} |
511 |
(void) free(buf); |
512 |
} |
513 |
|
514 |
if (fprintf(fddefs, "\n#endif\t/* __lint */\n") < 0) { |
515 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); |
516 |
return (1); |
517 |
} |
518 |
|
519 |
if (fprintf(fddefs, "\n#endif\n") < 0) { |
520 |
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno)); |
521 |
return (1); |
522 |
} |
523 |
|
524 |
return (0); |
525 |
} |
526 |
|
527 |
/* |
528 |
* The entire messaging file has been scanned - and all strings have been |
529 |
* inserted into the string_table. We can now walk the message queue |
530 |
* and create the '#define <DEFN>' for each string - with the strings |
531 |
* assigned offset into the string_table. |
532 |
*/ |
533 |
static int |
534 |
output_defs(void) |
535 |
{ |
536 |
msg_string *msg; |
537 |
size_t stbufsize; |
538 |
char *stbuf; |
539 |
|
540 |
stbufsize = st_getstrtab_sz(stp); |
541 |
if ((stbuf = malloc(stbufsize)) == 0) { |
542 |
(void) fprintf(stderr, Errmsg_nmem, strerror(errno)); |
543 |
exit(1); |
544 |
} |
545 |
(void) st_setstrbuf(stp, stbuf, stbufsize); |
546 |
for (msg = msg_head; msg; msg = msg->ms_next) { |
547 |
size_t stoff; |
548 |
if ((st_setstring(stp, msg->ms_message, &stoff)) == -1) { |
549 |
(void) fprintf(stderr, Errmsg_mnfn, msg->ms_message); |
550 |
return (1); |
551 |
} |
552 |
if (fprintf(fddefs, "\n#define\t%s\t%ld\n", |
553 |
msg->ms_defn, stoff) < 0) { |
554 |
(void) fprintf(stderr, Errmsg_wrte, |
555 |
fldefs, strerror(errno)); |
556 |
return (1); |
557 |
} |
558 |
if (fddefs && fprintf(fddefs, "#define\t%s_SIZE\t%d\n", |
559 |
msg->ms_defn, strlen(msg->ms_message)) < 0) { |
560 |
(void) fprintf(stderr, Errmsg_wrte, |
561 |
fldefs, strerror(errno)); |
562 |
return (1); |
563 |
} |
564 |
} |
565 |
return (0); |
566 |
} |
567 |
|
568 |
|
569 |
/* |
570 |
* Finish off the data structure definition. |
571 |
*/ |
572 |
static int |
573 |
output_data(void) |
574 |
{ |
575 |
size_t stbufsize; |
576 |
size_t ndx; |
577 |
size_t column = 1; |
578 |
const char *stbuf; |
579 |
const char *fmtstr; |
580 |
|
581 |
stbufsize = st_getstrtab_sz(stp); |
582 |
stbuf = st_getstrbuf(stp); |
583 |
|
584 |
assert(stbuf); |
585 |
|
586 |
/* |
587 |
* Determine from the local flag whether the data declaration should |
588 |
* be static. |
589 |
*/ |
590 |
if (lflag) |
591 |
fmtstr = (const char *)"static const"; |
592 |
else |
593 |
fmtstr = (const char *)"const"; |
594 |
|
595 |
if (fprintf(fddata, "\n%s char __%s[%ld] = { ", |
596 |
fmtstr, interface, stbufsize) < 0) { |
597 |
(void) fprintf(stderr, Errmsg_wrte, fldata, strerror(errno)); |
598 |
return (1); |
599 |
} |
600 |
|
601 |
for (ndx = 0; ndx < (stbufsize - 1); ndx++) { |
602 |
if (column == 1) { |
603 |
if (fddata && fprintf(fddata, |
604 |
"\n/* %4ld */ 0x%.2x,", ndx, |
605 |
(unsigned char)stbuf[ndx]) < 0) { |
606 |
(void) fprintf(stderr, Errmsg_wrte, |
607 |
fldata, strerror(errno)); |
608 |
return (1); |
609 |
} |
610 |
} else { |
611 |
if (fddata && fprintf(fddata, " 0x%.2x,", |
612 |
(unsigned char)stbuf[ndx]) < 0) { |
613 |
(void) fprintf(stderr, Errmsg_wrte, |
614 |
fldata, strerror(errno)); |
615 |
return (1); |
616 |
} |
617 |
} |
618 |
|
619 |
if (column++ == 10) |
620 |
column = 1; |
621 |
} |
622 |
|
623 |
if (column == 1) |
624 |
fmtstr = "\n\t0x%.2x };\n"; |
625 |
else |
626 |
fmtstr = " 0x%.2x };\n"; |
627 |
|
628 |
if (fprintf(fddata, fmtstr, (unsigned char)stbuf[stbufsize - 1]) < 0) { |
629 |
(void) fprintf(stderr, Errmsg_wrte, fldata, strerror(errno)); |
630 |
return (1); |
631 |
} |
632 |
|
633 |
return (0); |
634 |
} |
635 |
|
636 |
static int |
637 |
file() |
638 |
{ |
639 |
char buffer[LINE_MAX], * token; |
640 |
uint_t bufsize; |
641 |
char *token_buffer; |
642 |
int escape = 0; |
643 |
|
644 |
if ((token_buffer = malloc(LINE_MAX)) == 0) { |
645 |
(void) fprintf(stderr, Errmsg_nmem, strerror(errno)); |
646 |
return (1); |
647 |
} |
648 |
bufsize = LINE_MAX; |
649 |
|
650 |
line = 1; |
651 |
|
652 |
while ((token = fgets(buffer, LINE_MAX, fddesc)) != NULL) { |
653 |
char defn[PATH_MAX], * _defn, * str; |
654 |
int len; |
655 |
|
656 |
switch (*token) { |
657 |
case '#': |
658 |
case '$': |
659 |
if (escape) { |
660 |
(void) fprintf(stderr, Errmsg_malt, fldesc, |
661 |
line); |
662 |
return (1); |
663 |
} |
664 |
|
665 |
/* |
666 |
* If a msgid has been output a msgstr must follow |
667 |
* before we digest the new token. A msgid is only set |
668 |
* if fdmsgs is in use. |
669 |
*/ |
670 |
if (msgid) { |
671 |
msgid = 0; |
672 |
if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) { |
673 |
(void) fprintf(stderr, Errmsg_wrte, |
674 |
flmsgs, strerror(errno)); |
675 |
return (1); |
676 |
} |
677 |
} |
678 |
|
679 |
/* |
680 |
* Pass lines directly through to the output message |
681 |
* file. |
682 |
*/ |
683 |
if (fdmsgs && (prtmsgs == 1)) { |
684 |
char comment; |
685 |
|
686 |
if (cflag == 0) |
687 |
comment = '#'; |
688 |
else |
689 |
comment = '$'; |
690 |
|
691 |
if (fprintf(fdmsgs, "%c%s", comment, |
692 |
++token) < 0) { |
693 |
(void) fprintf(stderr, Errmsg_wrte, |
694 |
flmsgs, strerror(errno)); |
695 |
return (1); |
696 |
} |
697 |
} |
698 |
break; |
699 |
|
700 |
case '@': |
701 |
if (escape) { |
702 |
(void) fprintf(stderr, Errmsg_malt, fldesc, |
703 |
line); |
704 |
return (1); |
705 |
} |
706 |
|
707 |
/* |
708 |
* If a msgid has been output a msgstr must follow |
709 |
* before we digest the new token. |
710 |
*/ |
711 |
if (msgid) { |
712 |
msgid = 0; |
713 |
if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) { |
714 |
(void) fprintf(stderr, Errmsg_wrte, |
715 |
flmsgs, strerror(errno)); |
716 |
return (1); |
717 |
} |
718 |
} |
719 |
|
720 |
/* |
721 |
* Determine whether we have one or more tokens. |
722 |
*/ |
723 |
token++; |
724 |
while (isspace(*token)) /* rid any whitespace */ |
725 |
token++; |
726 |
_defn = token; /* definition start */ |
727 |
while (!(isspace(*token))) |
728 |
token++; |
729 |
*token++ = 0; |
730 |
|
731 |
while (isspace(*token)) /* rid any whitespace */ |
732 |
token++; |
733 |
|
734 |
/* |
735 |
* Determine whether the single token is one of the |
736 |
* reserved message output delimiters otherwise |
737 |
* translate it as a message identifier. |
738 |
*/ |
739 |
if (*token == 0) { |
740 |
if (strcmp(_defn, start) == 0) |
741 |
prtmsgs = 1; |
742 |
else if (strcmp(_defn, end) == 0) |
743 |
prtmsgs = -1; |
744 |
else if (getmesgid(_defn) == 1) |
745 |
return (1); |
746 |
break; |
747 |
} |
748 |
|
749 |
/* |
750 |
* Multiple tokens are translated by taking the first |
751 |
* token as the message definition, and the rest of the |
752 |
* line as the message itself. A message line ending |
753 |
* with an escape ('\') is expected to be continued on |
754 |
* the next line. |
755 |
*/ |
756 |
if (prtmsgs != -1) |
757 |
prtmsgs = 1; |
758 |
if (fdmsgs && (prtmsgs == 1)) { |
759 |
/* |
760 |
* For catgets(3c) make sure a message |
761 |
* identifier has been established (this is |
762 |
* normally a domain for gettext(3i), but for |
763 |
* sgsmsg use this could be argued as being |
764 |
* redundent). Also make sure that the message |
765 |
* definitions haven't exceeeded the maximum |
766 |
* value allowed by gencat(1) before generating |
767 |
* any message file entries. |
768 |
*/ |
769 |
if (cflag == 1) { |
770 |
if (setid == 0) { |
771 |
(void) fprintf(stderr, "file " |
772 |
"%s: no message identifier " |
773 |
"has been established\n", |
774 |
fldesc); |
775 |
return (1); |
776 |
} |
777 |
if (ptr > NL_MSGMAX) { |
778 |
(void) fprintf(stderr, "file " |
779 |
"%s: message definition " |
780 |
"(%d) exceeds allowable " |
781 |
"limit (NL_MSGMAX)\n", |
782 |
fldesc, ptr); |
783 |
return (1); |
784 |
} |
785 |
} |
786 |
|
787 |
/* |
788 |
* For catgets(3c) write the definition and the |
789 |
* message string to the message file. For |
790 |
* gettext(3i) write the message string as a |
791 |
* mesgid - indicate a mesgid has been output |
792 |
* so that a msgstr can follow. |
793 |
*/ |
794 |
if (cflag == 1) { |
795 |
if (fprintf(fdmsgs, "%d\t%s", ptr, |
796 |
token) < 0) { |
797 |
(void) fprintf(stderr, |
798 |
Errmsg_wrte, flmsgs, |
799 |
strerror(errno)); |
800 |
return (1); |
801 |
} |
802 |
} else { |
803 |
if (fprintf(fdmsgs, "msgid\t\"") < 0) { |
804 |
(void) fprintf(stderr, |
805 |
Errmsg_wrte, flmsgs, |
806 |
strerror(errno)); |
807 |
return (1); |
808 |
} |
809 |
msgid = 1; |
810 |
} |
811 |
} |
812 |
|
813 |
/* |
814 |
* The message itself is a quoted string as this makes |
815 |
* embedding spaces at the start (or the end) of the |
816 |
* string very easy. |
817 |
*/ |
818 |
if (*token != '"') { |
819 |
(void) fprintf(stderr, Errmsg_malt, fldesc, |
820 |
line); |
821 |
return (1); |
822 |
} |
823 |
|
824 |
(void) strcpy(defn, _defn); |
825 |
|
826 |
/* |
827 |
* Write the tag to the lint definitions. |
828 |
*/ |
829 |
if (fdlint) { |
830 |
if (fprintf(fdlint, "\n#define\t%s\t", |
831 |
_defn) < 0) { |
832 |
(void) fprintf(stderr, Errmsg_wrte, |
833 |
fllint, strerror(errno)); |
834 |
return (1); |
835 |
} |
836 |
} |
837 |
|
838 |
len = 0; |
839 |
|
840 |
/* |
841 |
* Write each character of the message string to the |
842 |
* data array. Translate any escaped characters - use |
843 |
* the same specially recognized characters as defined |
844 |
* by gencat(1). |
845 |
*/ |
846 |
message: |
847 |
if (*token == '"') { |
848 |
if (fdlint && |
849 |
(fprintf(fdlint, "%c", *token) < 0)) { |
850 |
(void) fprintf(stderr, Errmsg_wrte, |
851 |
fllint, strerror(errno)); |
852 |
return (1); |
853 |
} |
854 |
token++; |
855 |
} |
856 |
while (*token) { |
857 |
char _token; |
858 |
|
859 |
if ((*token == '\\') && (escape == 0)) { |
860 |
escape = 1; |
861 |
if (fdlint && (*(token + 1) != '\n') && |
862 |
fprintf(fdlint, "%c", *token) < 0) { |
863 |
(void) fprintf(stderr, |
864 |
Errmsg_wrte, fllint, |
865 |
strerror(errno)); |
866 |
return (1); |
867 |
} |
868 |
token++; |
869 |
continue; |
870 |
} |
871 |
if (escape) { |
872 |
if (*token == 'n') |
873 |
_token = '\n'; |
874 |
else if (*token == 't') |
875 |
_token = '\t'; |
876 |
else if (*token == 'v') |
877 |
_token = '\v'; |
878 |
else if (*token == 'b') |
879 |
_token = '\b'; |
880 |
else if (*token == 'f') |
881 |
_token = '\f'; |
882 |
else if (*token == '\\') |
883 |
_token = '\\'; |
884 |
else if (*token == '"') |
885 |
_token = '"'; |
886 |
else if (*token == '\n') |
887 |
break; |
888 |
else |
889 |
_token = *token; |
890 |
|
891 |
if (fdmsgs && (prtmsgs == 1) && |
892 |
(fprintf(fdmsgs, "\\") < 0)) { |
893 |
(void) fprintf(stderr, |
894 |
Errmsg_wrte, flmsgs, |
895 |
strerror(errno)); |
896 |
return (1); |
897 |
} |
898 |
} else { |
899 |
/* |
900 |
* If this is the trailing quote then |
901 |
* thats the last of the message string. |
902 |
* Eat up any remaining white space and |
903 |
* unless an escape character is found |
904 |
* terminate the data string with a 0. |
905 |
*/ |
906 |
/* BEGIN CSTYLED */ |
907 |
if (*token == '"') { |
908 |
if (fdlint && (fprintf(fdlint, |
909 |
"%c", *token) < 0)) { |
910 |
(void) fprintf(stderr, |
911 |
Errmsg_wrte, fllint, |
912 |
strerror(errno)); |
913 |
return (1); |
914 |
} |
915 |
|
916 |
if (fdmsgs && (prtmsgs == 1) && |
917 |
(fprintf(fdmsgs, "%c", |
918 |
*token) < 0)) { |
919 |
(void) fprintf(stderr, |
920 |
Errmsg_wrte, flmsgs, |
921 |
strerror(errno)); |
922 |
return (1); |
923 |
} |
924 |
|
925 |
while (*++token) { |
926 |
if (*token == '\n') |
927 |
break; |
928 |
} |
929 |
_token = '\0'; |
930 |
} else |
931 |
_token = *token; |
932 |
/* END CSTYLED */ |
933 |
} |
934 |
|
935 |
if (fdmsgs && (prtmsgs == 1) && |
936 |
(fprintf(fdmsgs, "%c", *token) < 0)) { |
937 |
(void) fprintf(stderr, Errmsg_wrte, |
938 |
flmsgs, strerror(errno)); |
939 |
return (1); |
940 |
} |
941 |
|
942 |
if (fdlint && fprintf(fdlint, |
943 |
"%c", *token) < 0) { |
944 |
(void) fprintf(stderr, Errmsg_wrte, |
945 |
fllint, strerror(errno)); |
946 |
return (1); |
947 |
} |
948 |
|
949 |
if (len >= bufsize) { |
950 |
bufsize += LINE_MAX; |
951 |
if ((token_buffer = realloc( |
952 |
token_buffer, bufsize)) == 0) { |
953 |
(void) fprintf(stderr, |
954 |
Errmsg_nmem, |
955 |
strerror(errno)); |
956 |
return (1); |
957 |
} |
958 |
} |
959 |
token_buffer[len] = _token; |
960 |
ptr++, token++, len++; |
961 |
escape = 0; |
962 |
|
963 |
if (_token == '\0') |
964 |
break; |
965 |
} |
966 |
|
967 |
/* |
968 |
* After the complete message string has been processed |
969 |
* (including its continuation beyond one line), create |
970 |
* a string size definition. |
971 |
*/ |
972 |
if (escape == 0) { |
973 |
const char *form = "#define\t%s_SIZE\t%d\n"; |
974 |
|
975 |
token_buffer[len] = '\0'; |
976 |
|
977 |
message_append(defn, token_buffer); |
978 |
|
979 |
if (fdlint && fprintf(fdlint, form, defn, |
980 |
(len - 1)) < 0) { |
981 |
(void) fprintf(stderr, Errmsg_wrte, |
982 |
fllint, strerror(errno)); |
983 |
return (1); |
984 |
} |
985 |
} |
986 |
break; |
987 |
|
988 |
default: |
989 |
/* |
990 |
* Empty lines are passed through to the message file. |
991 |
*/ |
992 |
while (isspace(*token)) |
993 |
token++; |
994 |
|
995 |
if (*token == 0) { |
996 |
if (msgid || (fdmsgs && (prtmsgs == 1))) { |
997 |
/* |
998 |
* If a msgid has been output a msgstr |
999 |
* must follow before we digest the new |
1000 |
* token. |
1001 |
*/ |
1002 |
if (msgid) { |
1003 |
msgid = 0; |
1004 |
str = "msgstr\t\"\"\n\n"; |
1005 |
} else |
1006 |
str = "\n"; |
1007 |
|
1008 |
if (fprintf(fdmsgs, str) < 0) { |
1009 |
(void) fprintf(stderr, |
1010 |
Errmsg_wrte, flmsgs, |
1011 |
strerror(errno)); |
1012 |
return (1); |
1013 |
} |
1014 |
} |
1015 |
break; |
1016 |
} |
1017 |
|
1018 |
/* |
1019 |
* If an escape is in effect then any tokens are taken |
1020 |
* to be message continuations. |
1021 |
*/ |
1022 |
if (escape) { |
1023 |
escape = 0; |
1024 |
goto message; |
1025 |
} |
1026 |
|
1027 |
(void) fprintf(stderr, "file %s: line %d: invalid " |
1028 |
"input does not start with #, $ or @\n", fldesc, |
1029 |
line); |
1030 |
return (1); |
1031 |
} |
1032 |
line++; |
1033 |
} |
1034 |
|
1035 |
free(token_buffer); |
1036 |
|
1037 |
return (0); |
1038 |
} |
1039 |
|
1040 |
int |
1041 |
main(int argc, char ** argv) |
1042 |
{ |
1043 |
opterr = 0; |
1044 |
while ((line = getopt(argc, argv, "cd:h:lm:n:i:v")) != EOF) { |
1045 |
switch (line) { |
1046 |
case 'c': /* catgets instead of gettext */ |
1047 |
cflag = 1; |
1048 |
break; |
1049 |
case 'd': /* new message data filename */ |
1050 |
fldata = optarg; /* (msg.c is default) */ |
1051 |
break; |
1052 |
case 'h': /* new message defs filename */ |
1053 |
fldefs = optarg; /* (msg.h is default) */ |
1054 |
break; |
1055 |
case 'i': /* input message ids from */ |
1056 |
flmids = optarg; /* from this file */ |
1057 |
break; |
1058 |
case 'l': /* define message data arrays */ |
1059 |
lflag = 1; /* to be local (static) */ |
1060 |
break; |
1061 |
case 'm': /* generate message database */ |
1062 |
flmsgs = optarg; /* to this file */ |
1063 |
break; |
1064 |
case 'n': /* new data array and func */ |
1065 |
interface = optarg; /* name (msg is default) */ |
1066 |
break; |
1067 |
case 'v': |
1068 |
vflag = 1; /* set verbose flag */ |
1069 |
break; |
1070 |
case '?': |
1071 |
(void) fprintf(stderr, Errmsg_use, argv[0]); |
1072 |
exit(1); |
1073 |
default: |
1074 |
break; |
1075 |
} |
1076 |
} |
1077 |
|
1078 |
/* |
1079 |
* Validate the we have been given at least one input file. |
1080 |
*/ |
1081 |
if ((argc - optind) < 1) { |
1082 |
(void) fprintf(stderr, Errmsg_use); |
1083 |
exit(1); |
1084 |
} |
1085 |
|
1086 |
/* |
1087 |
* Open all the required output files. |
1088 |
*/ |
1089 |
if (fldefs) { |
1090 |
if ((fddefs = fopen(fldefs, "w+")) == NULL) { |
1091 |
(void) fprintf(stderr, Errmsg_opne, fldefs, |
1092 |
strerror(errno)); |
1093 |
return (1); |
1094 |
} |
1095 |
} |
1096 |
if (fldata) { |
1097 |
if (fldefs && (strcmp(fldefs, fldata) == 0)) |
1098 |
fddata = fddefs; |
1099 |
else if ((fddata = fopen(fldata, "w+")) == NULL) { |
1100 |
(void) fprintf(stderr, Errmsg_opne, fldata, |
1101 |
strerror(errno)); |
1102 |
return (1); |
1103 |
} |
1104 |
} |
1105 |
if (fddefs && fddata) { |
1106 |
(void) sprintf(fllint, "%s.%d", nmlint, (int)getpid()); |
1107 |
if ((fdlint = fopen(fllint, "w+")) == NULL) { |
1108 |
(void) fprintf(stderr, Errmsg_opne, fllint, |
1109 |
strerror(errno)); |
1110 |
return (1); |
1111 |
} |
1112 |
} |
1113 |
if (flmsgs) { |
1114 |
if ((fdmsgs = fopen(flmsgs, "w+")) == NULL) { |
1115 |
(void) fprintf(stderr, Errmsg_opne, flmsgs, |
1116 |
strerror(errno)); |
1117 |
return (1); |
1118 |
} |
1119 |
} |
1120 |
if (flmids) { |
1121 |
if ((fdmids = fopen(flmids, "r")) == NULL) { |
1122 |
(void) fprintf(stderr, Errmsg_opne, flmids, |
1123 |
strerror(errno)); |
1124 |
return (1); |
1125 |
} |
1126 |
} |
1127 |
|
1128 |
|
1129 |
/* |
1130 |
* Initialize the message definition and message data streams. |
1131 |
*/ |
1132 |
if (fddefs) { |
1133 |
if (init_defs()) |
1134 |
return (1); |
1135 |
} |
1136 |
|
1137 |
/* |
1138 |
* Read the input message file, and for each line process accordingly. |
1139 |
*/ |
1140 |
for (; optind < argc; optind++) { |
1141 |
int err; |
1142 |
|
1143 |
fldesc = argv[optind]; |
1144 |
|
1145 |
if ((fddesc = fopen(fldesc, "r")) == NULL) { |
1146 |
(void) fprintf(stderr, Errmsg_opne, fldesc, |
1147 |
strerror(errno)); |
1148 |
return (1); |
1149 |
} |
1150 |
err = file(); |
1151 |
(void) fclose(fddesc); |
1152 |
|
1153 |
if (err != 0) |
1154 |
return (1); |
1155 |
} |
1156 |
|
1157 |
/* |
1158 |
* If a msgid has been output a msgstr must follow before we end the |
1159 |
* file. |
1160 |
*/ |
1161 |
if (msgid) { |
1162 |
msgid = 0; |
1163 |
if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) { |
1164 |
(void) fprintf(stderr, Errmsg_wrte, flmsgs, |
1165 |
strerror(errno)); |
1166 |
return (1); |
1167 |
} |
1168 |
} |
1169 |
|
1170 |
if (fdmids) |
1171 |
(void) fclose(fdmids); |
1172 |
if (fdmsgs) |
1173 |
(void) fclose(fdmsgs); |
1174 |
|
1175 |
if (fddefs) { |
1176 |
if (output_defs()) |
1177 |
return (1); |
1178 |
} |
1179 |
|
1180 |
/* |
1181 |
* Finish off any generated data and header file. |
1182 |
*/ |
1183 |
if (fldata) { |
1184 |
if (output_data()) |
1185 |
return (1); |
1186 |
} |
1187 |
if (fddefs) { |
1188 |
if (fini_defs()) |
1189 |
return (1); |
1190 |
} |
1191 |
|
1192 |
if (vflag) |
1193 |
dump_stringtab(stp); |
1194 |
|
1195 |
/* |
1196 |
* Close up everything and go home. |
1197 |
*/ |
1198 |
if (fddata) |
1199 |
(void) fclose(fddata); |
1200 |
if (fddefs && (fddefs != fddata)) |
1201 |
(void) fclose(fddefs); |
1202 |
if (fddefs && fddata) { |
1203 |
(void) fclose(fdlint); |
1204 |
(void) unlink(fllint); |
1205 |
} |
1206 |
|
1207 |
if (stp) |
1208 |
st_destroy(stp); |
1209 |
|
1210 |
return (0); |
1211 |
} |