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