Fixed some deadlocks.
[wine] / tools / wmc / write.c
1 /*
2  * Wine Message Compiler output generation
3  *
4  * Copyright 2000 Bertho A. Stultiens (BS)
5  *
6  */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <assert.h>
12 #include <ctype.h>
13
14 #include "wmc.h"
15 #include "utils.h"
16 #include "lang.h"
17 #include "write.h"
18
19 /*
20  * The binary resource layout is as follows:
21  *
22  *         +===============+
23  * Header  |    NBlocks    |
24  *         +===============+
25  * Block 0 |    Low ID     |
26  *         +---------------+
27  *         |    High ID    |
28  *         +---------------+
29  *         |    Offset     |---+
30  *         +===============+   |
31  * Block 1 |    Low ID     |   |
32  *         +---------------+   |
33  *         |    High ID    |   |
34  *         +---------------+   |
35  *         |    Offset     |------+
36  *         +===============+   |  |
37  *         |               |   |  |
38  *        ...             ...  |  |
39  *         |               |   |  |
40  *         +===============+ <-+  |
41  * B0 LoID |  Len  | Flags |      |
42  *         +---+---+---+---+      |
43  *         | b | l | a | b |      |
44  *         +---+---+---+---+      |
45  *         | l | a | \0| \0|      |
46  *         +===============+      |
47  *         |               |      |
48  *        ...             ...     |
49  *         |               |      |
50  *         +===============+      |
51  * B0 HiID |  Len  | Flags |      |
52  *         +---+---+---+---+      |
53  *         | M | o | r | e |      |
54  *         +---+---+---+---+      |
55  *         | b | l | a | \0|      |
56  *         +===============+ <----+
57  * B1 LoID |  Len  | Flags |
58  *         +---+---+---+---+
59  *         | J | u | n | k |
60  *         +---+---+---+---+
61  *         | \0| \0| \0| \0|
62  *         +===============+
63  *         |               |
64  *        ...             ...
65  *         |               |
66  *         +===============+
67  *
68  * All Fields are aligned on their natural boundaries. The length
69  * field (Len) covers both the length of the string and the header
70  * fields (Len and Flags). Strings are '\0' terminated. Flags is 0
71  * for normal character strings and 1 for unicode strings.
72  */
73
74 static char str_header[] =
75         "/* This file is generated with wmc version " WMC_FULLVERSION ". Do not edit! */\n"
76         "/* Source : %s */\n"
77         "/* Cmdline: %s */\n"
78         "/* Date   : %s */\n"
79         "\n"
80         ;
81
82 static char *dup_u2c(int cp, const WCHAR *uc)
83 {
84         int len = unistrlen(uc);
85         char *cptr = xmalloc(len+1);
86         const union cptable *cpdef = find_codepage(cp);
87         if(!cpdef)
88                 internal_error(__FILE__, __LINE__, "Codepage %d not found (vanished?)", cp);
89         if((len = cp_wcstombs(cpdef, 0, uc, unistrlen(uc)+1, cptr, len+1, NULL, NULL)) < 0)
90                 internal_error(__FILE__, __LINE__, "Buffer overflow? code %d.", len);
91         return cptr;
92 }
93
94 static void killnl(char *s, int ddd)
95 {
96         char *tmp;
97         tmp = strstr(s, "\r\n");
98         if(tmp)
99         {
100                 if(ddd && tmp - s > 3)
101                 {
102                         tmp[0] = tmp[1] = tmp[2] = '.';
103                         tmp[3] = '\0';
104                 }
105                 else
106                         *tmp = '\0';
107         }
108         tmp = strchr(s, '\n');
109         if(tmp)
110         {
111                 if(ddd && tmp - s > 3)
112                 {
113                         tmp[0] = tmp[1] = tmp[2] = '.';
114                         tmp[3] = '\0';
115                 }
116                 else
117                         *tmp = '\0';
118         }
119 }
120
121 static int killcomment(char *s)
122 {
123         char *tmp = s;
124         int b = 0;
125         while((tmp = strstr(tmp, "/*")))
126         {
127                 tmp[1] = 'x';
128                 b++;
129         }
130         tmp = s;
131         while((tmp = strstr(tmp, "*/")))
132         {
133                 tmp[0] = 'x';
134                 b++;
135         }
136         return b;
137 }
138
139 void write_h_file(const char *fname)
140 {
141         node_t *ndp;
142         char *cptr;
143         char *cast;
144         FILE *fp;
145         token_t *ttab;
146         int ntab;
147         int i;
148         int once = 0;
149         int idx_en = 0;
150
151         fp = fopen(fname, "w");
152         if(!fp)
153         {
154                 perror(fname);
155                 exit(1);
156         }
157         cptr = ctime(&now);
158         killnl(cptr, 0);
159         fprintf(fp, str_header, input_name ? input_name : "<stdin>", cmdline, cptr);
160         fprintf(fp, "#ifndef __WMCGENERATED_%08lx_H\n", now);
161         fprintf(fp, "#define __WMCGENERATED_%08lx_H\n", now);
162         fprintf(fp, "\n");
163
164         /* Write severity and facility aliases */
165         get_tokentable(&ttab, &ntab);
166         fprintf(fp, "/* Severity codes */\n");
167         for(i = 0; i < ntab; i++)
168         {
169                 if(ttab[i].type == tok_severity && ttab[i].alias)
170                 {
171                         cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ttab[i].alias);
172                         fprintf(fp, "#define %s\t0x%x\n", cptr, ttab[i].token);
173                         free(cptr);
174                 }
175         }
176         fprintf(fp, "\n");
177
178         fprintf(fp, "/* Facility codes */\n");
179         for(i = 0; i < ntab; i++)
180         {
181                 if(ttab[i].type == tok_facility && ttab[i].alias)
182                 {
183                         cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ttab[i].alias);
184                         fprintf(fp, "#define %s\t0x%x\n", cptr, ttab[i].token);
185                         free(cptr);
186                 }
187         }
188         fprintf(fp, "\n");
189
190         /* Write the message codes */
191         fprintf(fp, "/* Message definitions */\n");
192         for(ndp = nodehead; ndp; ndp = ndp->next)
193         {
194                 switch(ndp->type)
195                 {
196                 case nd_comment:
197                         cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ndp->u.comment+1);
198                         killnl(cptr, 0);
199                         killcomment(cptr);
200                         if(*cptr)
201                                 fprintf(fp, "/* %s */\n", cptr);
202                         else
203                                 fprintf(fp, "\n");
204                         free(cptr);
205                         break;
206                 case nd_msg:
207                         if(!once)
208                         {
209                                 /*
210                                  * Search for an english text.
211                                  * If not found, then use the first in the list
212                                  */
213                                 once++;
214                                 for(i = 0; i < ndp->u.msg->nmsgs; i++)
215                                 {
216                                         if(ndp->u.msg->msgs[i]->lan == 0x409)
217                                         {
218                                                 idx_en = i;
219                                                 break;
220                                         }
221                                 }
222                                 fprintf(fp, "\n");
223                         }
224                         fprintf(fp, "/* MessageId  : 0x%08x */\n", ndp->u.msg->realid);
225                         cptr = dup_u2c(ndp->u.msg->msgs[idx_en]->cp, ndp->u.msg->msgs[idx_en]->msg);
226                         killnl(cptr, 0);
227                         killcomment(cptr);
228                         fprintf(fp, "/* Approx. msg: %s */\n", cptr);
229                         free(cptr);
230                         cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ndp->u.msg->sym);
231                         if(ndp->u.msg->cast)
232                                 cast = dup_u2c(WMC_DEFAULT_CODEPAGE, ndp->u.msg->cast);
233                         else
234                                 cast = NULL;
235                         switch(ndp->u.msg->base)
236                         {
237                         case 8:
238                                 if(cast)
239                                         fprintf(fp, "#define %s\t((%s)0%oL)\n\n", cptr, cast, ndp->u.msg->realid);
240                                 else
241                                         fprintf(fp, "#define %s\t0%oL\n\n", cptr, ndp->u.msg->realid);
242                                 break;
243                         case 10:
244                                 if(cast)
245                                         fprintf(fp, "#define %s\t((%s)%dL)\n\n", cptr, cast, ndp->u.msg->realid);
246                                 else
247                                         fprintf(fp, "#define %s\t%dL\n\n", cptr, ndp->u.msg->realid);
248                                 break;
249                         case 16:
250                                 if(cast)
251                                         fprintf(fp, "#define %s\t((%s)0x%08xL)\n\n", cptr, cast, ndp->u.msg->realid);
252                                 else
253                                         fprintf(fp, "#define %s\t0x%08xL\n\n", cptr, ndp->u.msg->realid);
254                                 break;
255                         default:
256                                 internal_error(__FILE__, __LINE__, "Invalid base for number print");
257                         }
258                         free(cptr);
259                         if(cast)
260                                 free(cast);
261                         break;
262                 default:
263                         internal_error(__FILE__, __LINE__, "Invalid node type %d", ndp->type);
264                 }
265         }
266         fprintf(fp, "\n#endif\n");
267         fclose(fp);
268 }
269
270 static void write_rcbin(FILE *fp)
271 {
272         lan_blk_t *lbp;
273         token_t *ttab;
274         int ntab;
275         int i;
276
277         get_tokentable(&ttab, &ntab);
278
279         for(lbp = lanblockhead; lbp; lbp = lbp->next)
280         {
281                 char *cptr = NULL;
282                 fprintf(fp, "LANGUAGE 0x%x, 0x%x\n", lbp->lan & 0x3ff, lbp->lan >> 10);
283                 for(i = 0; i < ntab; i++)
284                 {
285                         if(ttab[i].type == tok_language && ttab[i].token == lbp->lan)
286                         {
287                                 if(ttab[i].alias)
288                                         cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ttab[i].alias);
289                                 break;
290                         }
291                 }
292                 if(!cptr)
293                         internal_error(__FILE__, __LINE__, "Filename vanished for language 0x%0x", lbp->lan);
294                 fprintf(fp, "1 MESSAGETABLE \"%s.bin\"\n", cptr);
295                 free(cptr);
296         }
297 }
298
299 static char *make_string(WCHAR *uc, int len, int codepage)
300 {
301         char *str = xmalloc(7*len + 1);
302         char *cptr = str;
303         int i;
304         int b;
305
306         if(!codepage)
307         {
308                 *cptr++ = ' ';
309                 *cptr++ = 'L';
310                 *cptr++ = '"';
311                 for(i = b = 0; i < len; i++, uc++)
312                 {
313                         switch(*uc)
314                         {
315                         case '\a': *cptr++ = '\\'; *cptr++ = 'a'; b += 2; break;
316                         case '\b': *cptr++ = '\\'; *cptr++ = 'b'; b += 2; break;
317                         case '\f': *cptr++ = '\\'; *cptr++ = 'f'; b += 2; break;
318                         case '\n': *cptr++ = '\\'; *cptr++ = 'n'; b += 2; break;
319                         case '\r': *cptr++ = '\\'; *cptr++ = 'r'; b += 2; break;
320                         case '\t': *cptr++ = '\\'; *cptr++ = 't'; b += 2; break;
321                         case '\v': *cptr++ = '\\'; *cptr++ = 'v'; b += 2; break;
322                         case '\\': *cptr++ = '\\'; *cptr++ = '\\'; b += 2; break;
323                         case '"':  *cptr++ = '\\'; *cptr++ = '"'; b += 2; break;
324                         default:
325                             if (*uc < 0x100 && isprint(*uc))
326                             {
327                                 *cptr++ = *uc;
328                                 b++;
329                             }
330                             else
331                             {
332                                 int n = sprintf(cptr, "\\x%04x", *uc & 0xffff);
333                                 cptr += n;
334                                 b += n;
335                             }
336                             break;
337                         }
338                         if(i < len-1 && b >= 72)
339                         {
340                                 *cptr++ = '"';
341                                 *cptr++ = ',';
342                                 *cptr++ = '\n';
343                                 *cptr++ = ' ';
344                                 *cptr++ = 'L';
345                                 *cptr++ = '"';
346                                 b = 0;
347                         }
348                 }
349                 len = (len + 3) & ~3;
350                 for(; i < len; i++)
351                 {
352                         *cptr++ = '\\';
353                         *cptr++ = 'x';
354                         *cptr++ = '0';
355                         *cptr++ = '0';
356                         *cptr++ = '0';
357                         *cptr++ = '0';
358                 }
359                 *cptr++ = '"';
360                 *cptr = '\0';
361         }
362         else
363         {
364                 char *tmp = xmalloc(2*len+1);
365                 char *cc = tmp;
366                 const union cptable *cpdef = find_codepage(codepage);
367
368                 assert(cpdef != NULL);
369                 if((i = cp_wcstombs(cpdef, 0, uc, unistrlen(uc)+1, tmp, 2*len+1, NULL, NULL)) < 0)
370                         internal_error(__FILE__, __LINE__, "Buffer overflow? code %d.", i);
371                 *cptr++ = ' ';
372                 *cptr++ = '"';
373                 for(i = b = 0; i < len; i++, cc++)
374                 {
375                         switch(*cc)
376                         {
377                         case '\a': *cptr++ = '\\'; *cptr++ = 'a'; b += 2; break;
378                         case '\b': *cptr++ = '\\'; *cptr++ = 'b'; b += 2; break;
379                         case '\f': *cptr++ = '\\'; *cptr++ = 'f'; b += 2; break;
380                         case '\n': *cptr++ = '\\'; *cptr++ = 'n'; b += 2; break;
381                         case '\r': *cptr++ = '\\'; *cptr++ = 'r'; b += 2; break;
382                         case '\t': *cptr++ = '\\'; *cptr++ = 't'; b += 2; break;
383                         case '\v': *cptr++ = '\\'; *cptr++ = 'v'; b += 2; break;
384                         case '\\': *cptr++ = '\\'; *cptr++ = '\\'; b += 2; break;
385                         case '"':  *cptr++ = '\\'; *cptr++ = '"'; b += 2; break;
386                         default:
387                             if(isprint(*cc))
388                             {
389                                 *cptr++ = *cc;
390                                 b++;
391                             }
392                             else
393                             {
394                                 int n = sprintf(cptr, "\\x%02x", *cc & 0xff);
395                                 cptr += n;
396                                 b += n;
397                             }
398                             break;
399                         }
400                         if(i < len-1 && b >= 72)
401                         {
402                                 *cptr++ = '"';
403                                 *cptr++ = ',';
404                                 *cptr++ = '\n';
405                                 *cptr++ = ' ';
406                                 *cptr++ = '"';
407                                 b = 0;
408                         }
409                 }
410                 len = (len + 3) & ~3;
411                 for(; i < len; i++)
412                 {
413                         *cptr++ = '\\';
414                         *cptr++ = 'x';
415                         *cptr++ = '0';
416                         *cptr++ = '0';
417                 }
418                 *cptr++ = '"';
419                 *cptr = '\0';
420                 free(tmp);
421         }
422         return str;
423 }
424
425 static void write_rcinline(FILE *fp)
426 {
427         lan_blk_t *lbp;
428         int i;
429         int j;
430
431         for(lbp = lanblockhead; lbp; lbp = lbp->next)
432         {
433                 unsigned offs = 4 * (lbp->nblk * 3 + 1); 
434                 fprintf(fp, "\n1 MESSAGETABLE\n");
435                 fprintf(fp, "LANGUAGE 0x%x, 0x%x\n", lbp->lan & 0x3ff, lbp->lan >> 10);
436                 fprintf(fp, "{\n");
437                 fprintf(fp, " /* NBlocks    */ 0x%08xL,\n", lbp->nblk);
438                 for(i = 0; i < lbp->nblk; i++)
439                 {
440                         fprintf(fp, " /* Lo,Hi,Offs */ 0x%08xL, 0x%08xL, 0x%08xL,\n",
441                                         lbp->blks[i].idlo,
442                                         lbp->blks[i].idhi,
443                                         offs);
444                         offs += lbp->blks[i].size;
445                 }
446                 for(i = 0; i < lbp->nblk; i++)
447                 {
448                         block_t *blk = &lbp->blks[i];
449                         for(j = 0; j < blk->nmsg; j++)
450                         {
451                                 char *cptr;
452                                 int l = blk->msgs[j]->len;
453                                 char *comma = j == blk->nmsg-1  && i == lbp->nblk-1 ? "" : ",";
454                                 cptr = make_string(blk->msgs[j]->msg, l, unicodeout ? 0 : blk->msgs[j]->cp);
455                                 fprintf(fp, "\n /* Msg 0x%08x */ 0x%04x, 0x000%c,\n",
456                                         blk->idlo + j,
457                                         (unicodeout ? (l*2+3)&~3 : (l+3)&~3)+4,
458                                         unicodeout ? '1' : '0');
459                                 fprintf(fp, "%s%s\n", cptr, comma);
460                                 free(cptr);
461                         }
462                 }
463                 fprintf(fp, "}\n");
464         }
465 }
466
467 void write_rc_file(const char *fname)
468 {
469         FILE *fp;
470         char *cptr;
471
472         fp = fopen(fname, "w");
473         if(!fp)
474         {
475                 perror(fname);
476                 exit(1);
477         }
478         cptr = ctime(&now);
479         killnl(cptr, 0);
480         fprintf(fp, str_header, input_name ? input_name : "<stdin>", cmdline, cptr);
481
482         if(rcinline)
483                 write_rcinline(fp);
484         else
485                 write_rcbin(fp);
486         fclose(fp);
487 }
488
489 void write_bin_files(void)
490 {
491         assert(rcinline == 0);
492 }
493