For each imported dll, check if there is a static library with the
[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;
98         char *cptr;
99         const union cptable *cpdef = find_codepage(cp);
100         if(!cpdef)
101                 internal_error(__FILE__, __LINE__, "Codepage %d not found (vanished?)", cp);
102         len = wine_cp_wcstombs(cpdef, 0, uc, unistrlen(uc)+1, NULL, 0, NULL, NULL);
103         cptr = xmalloc(len);
104         if((len = wine_cp_wcstombs(cpdef, 0, uc, unistrlen(uc)+1, cptr, len, NULL, NULL)) < 0)
105                 internal_error(__FILE__, __LINE__, "Buffer overflow? code %d.", len);
106         return cptr;
107 }
108
109 static void killnl(char *s, int ddd)
110 {
111         char *tmp;
112         tmp = strstr(s, "\r\n");
113         if(tmp)
114         {
115                 if(ddd && tmp - s > 3)
116                 {
117                         tmp[0] = tmp[1] = tmp[2] = '.';
118                         tmp[3] = '\0';
119                 }
120                 else
121                         *tmp = '\0';
122         }
123         tmp = strchr(s, '\n');
124         if(tmp)
125         {
126                 if(ddd && tmp - s > 3)
127                 {
128                         tmp[0] = tmp[1] = tmp[2] = '.';
129                         tmp[3] = '\0';
130                 }
131                 else
132                         *tmp = '\0';
133         }
134 }
135
136 static int killcomment(char *s)
137 {
138         char *tmp = s;
139         int b = 0;
140         while((tmp = strstr(tmp, "/*")))
141         {
142                 tmp[1] = 'x';
143                 b++;
144         }
145         tmp = s;
146         while((tmp = strstr(tmp, "*/")))
147         {
148                 tmp[0] = 'x';
149                 b++;
150         }
151         return b;
152 }
153
154 void write_h_file(const char *fname)
155 {
156         node_t *ndp;
157         char *cptr;
158         char *cast;
159         FILE *fp;
160         token_t *ttab;
161         int ntab;
162         int i;
163         int once = 0;
164         int idx_en = 0;
165
166         fp = fopen(fname, "w");
167         if(!fp)
168         {
169                 perror(fname);
170                 exit(1);
171         }
172         cptr = ctime(&now);
173         killnl(cptr, 0);
174         fprintf(fp, str_header, input_name ? input_name : "<stdin>", cmdline, cptr);
175         fprintf(fp, "#ifndef __WMCGENERATED_%08lx_H\n", (long)now);
176         fprintf(fp, "#define __WMCGENERATED_%08lx_H\n", (long)now);
177         fprintf(fp, "\n");
178
179         /* Write severity and facility aliases */
180         get_tokentable(&ttab, &ntab);
181         fprintf(fp, "/* Severity codes */\n");
182         for(i = 0; i < ntab; i++)
183         {
184                 if(ttab[i].type == tok_severity && ttab[i].alias)
185                 {
186                         cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ttab[i].alias);
187                         fprintf(fp, "#define %s\t0x%x\n", cptr, ttab[i].token);
188                         free(cptr);
189                 }
190         }
191         fprintf(fp, "\n");
192
193         fprintf(fp, "/* Facility codes */\n");
194         for(i = 0; i < ntab; i++)
195         {
196                 if(ttab[i].type == tok_facility && ttab[i].alias)
197                 {
198                         cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ttab[i].alias);
199                         fprintf(fp, "#define %s\t0x%x\n", cptr, ttab[i].token);
200                         free(cptr);
201                 }
202         }
203         fprintf(fp, "\n");
204
205         /* Write the message codes */
206         fprintf(fp, "/* Message definitions */\n");
207         for(ndp = nodehead; ndp; ndp = ndp->next)
208         {
209                 switch(ndp->type)
210                 {
211                 case nd_comment:
212                         cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ndp->u.comment+1);
213                         killnl(cptr, 0);
214                         killcomment(cptr);
215                         if(*cptr)
216                                 fprintf(fp, "/* %s */\n", cptr);
217                         else
218                                 fprintf(fp, "\n");
219                         free(cptr);
220                         break;
221                 case nd_msg:
222                         if(!once)
223                         {
224                                 /*
225                                  * Search for an english text.
226                                  * If not found, then use the first in the list
227                                  */
228                                 once++;
229                                 for(i = 0; i < ndp->u.msg->nmsgs; i++)
230                                 {
231                                         if(ndp->u.msg->msgs[i]->lan == 0x409)
232                                         {
233                                                 idx_en = i;
234                                                 break;
235                                         }
236                                 }
237                                 fprintf(fp, "\n");
238                         }
239                         fprintf(fp, "/* MessageId  : 0x%08x */\n", ndp->u.msg->realid);
240                         cptr = dup_u2c(ndp->u.msg->msgs[idx_en]->cp, ndp->u.msg->msgs[idx_en]->msg);
241                         killnl(cptr, 0);
242                         killcomment(cptr);
243                         fprintf(fp, "/* Approx. msg: %s */\n", cptr);
244                         free(cptr);
245                         cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ndp->u.msg->sym);
246                         if(ndp->u.msg->cast)
247                                 cast = dup_u2c(WMC_DEFAULT_CODEPAGE, ndp->u.msg->cast);
248                         else
249                                 cast = NULL;
250                         switch(ndp->u.msg->base)
251                         {
252                         case 8:
253                                 if(cast)
254                                         fprintf(fp, "#define %s\t((%s)0%oL)\n\n", cptr, cast, ndp->u.msg->realid);
255                                 else
256                                         fprintf(fp, "#define %s\t0%oL\n\n", cptr, ndp->u.msg->realid);
257                                 break;
258                         case 10:
259                                 if(cast)
260                                         fprintf(fp, "#define %s\t((%s)%dL)\n\n", cptr, cast, ndp->u.msg->realid);
261                                 else
262                                         fprintf(fp, "#define %s\t%dL\n\n", cptr, ndp->u.msg->realid);
263                                 break;
264                         case 16:
265                                 if(cast)
266                                         fprintf(fp, "#define %s\t((%s)0x%08xL)\n\n", cptr, cast, ndp->u.msg->realid);
267                                 else
268                                         fprintf(fp, "#define %s\t0x%08xL\n\n", cptr, ndp->u.msg->realid);
269                                 break;
270                         default:
271                                 internal_error(__FILE__, __LINE__, "Invalid base for number print");
272                         }
273                         free(cptr);
274                         if(cast)
275                                 free(cast);
276                         break;
277                 default:
278                         internal_error(__FILE__, __LINE__, "Invalid node type %d", ndp->type);
279                 }
280         }
281         fprintf(fp, "\n#endif\n");
282         fclose(fp);
283 }
284
285 static void write_rcbin(FILE *fp)
286 {
287         lan_blk_t *lbp;
288         token_t *ttab;
289         int ntab;
290         int i;
291
292         get_tokentable(&ttab, &ntab);
293
294         for(lbp = lanblockhead; lbp; lbp = lbp->next)
295         {
296                 char *cptr = NULL;
297                 fprintf(fp, "LANGUAGE 0x%x, 0x%x\n", lbp->lan & 0x3ff, lbp->lan >> 10);
298                 for(i = 0; i < ntab; i++)
299                 {
300                         if(ttab[i].type == tok_language && ttab[i].token == lbp->lan)
301                         {
302                                 if(ttab[i].alias)
303                                         cptr = dup_u2c(WMC_DEFAULT_CODEPAGE, ttab[i].alias);
304                                 break;
305                         }
306                 }
307                 if(!cptr)
308                         internal_error(__FILE__, __LINE__, "Filename vanished for language 0x%0x", lbp->lan);
309                 fprintf(fp, "1 MESSAGETABLE \"%s.bin\"\n", cptr);
310                 free(cptr);
311         }
312 }
313
314 static char *make_string(WCHAR *uc, int len, int codepage)
315 {
316         char *str = xmalloc(7*len + 1);
317         char *cptr = str;
318         int i;
319         int b;
320
321         if(!codepage)
322         {
323                 *cptr++ = ' ';
324                 *cptr++ = 'L';
325                 *cptr++ = '"';
326                 for(i = b = 0; i < len; i++, uc++)
327                 {
328                         switch(*uc)
329                         {
330                         case '\a': *cptr++ = '\\'; *cptr++ = 'a'; b += 2; break;
331                         case '\b': *cptr++ = '\\'; *cptr++ = 'b'; b += 2; break;
332                         case '\f': *cptr++ = '\\'; *cptr++ = 'f'; b += 2; break;
333                         case '\n': *cptr++ = '\\'; *cptr++ = 'n'; b += 2; break;
334                         case '\r': *cptr++ = '\\'; *cptr++ = 'r'; b += 2; break;
335                         case '\t': *cptr++ = '\\'; *cptr++ = 't'; b += 2; break;
336                         case '\v': *cptr++ = '\\'; *cptr++ = 'v'; b += 2; break;
337                         case '\\': *cptr++ = '\\'; *cptr++ = '\\'; b += 2; break;
338                         case '"':  *cptr++ = '\\'; *cptr++ = '"'; b += 2; break;
339                         default:
340                             if (*uc < 0x100 && isprint(*uc))
341                             {
342                                 *cptr++ = *uc;
343                                 b++;
344                             }
345                             else
346                             {
347                                 int n = sprintf(cptr, "\\x%04x", *uc & 0xffff);
348                                 cptr += n;
349                                 b += n;
350                             }
351                             break;
352                         }
353                         if(i < len-1 && b >= 72)
354                         {
355                                 *cptr++ = '"';
356                                 *cptr++ = ',';
357                                 *cptr++ = '\n';
358                                 *cptr++ = ' ';
359                                 *cptr++ = 'L';
360                                 *cptr++ = '"';
361                                 b = 0;
362                         }
363                 }
364                 if (unicodeout)
365                     len = (len + 1) & ~1;
366                 else
367                     len = (len + 3) & ~3;
368                 for(; i < len; i++)
369                 {
370                         *cptr++ = '\\';
371                         *cptr++ = 'x';
372                         *cptr++ = '0';
373                         *cptr++ = '0';
374                         *cptr++ = '0';
375                         *cptr++ = '0';
376                 }
377                 *cptr++ = '"';
378                 *cptr = '\0';
379         }
380         else
381         {
382                 char *tmp, *cc;
383                 int mlen;
384                 const union cptable *cpdef = find_codepage(codepage);
385
386                 assert(cpdef != NULL);
387                 mlen = wine_cp_wcstombs(cpdef, 0, uc, unistrlen(uc)+1, NULL, 0, NULL, NULL);
388                 cc = tmp = xmalloc(mlen);
389                 if((i = wine_cp_wcstombs(cpdef, 0, uc, unistrlen(uc)+1, tmp, mlen, NULL, NULL)) < 0)
390                         internal_error(__FILE__, __LINE__, "Buffer overflow? code %d.", i);
391                 *cptr++ = ' ';
392                 *cptr++ = '"';
393                 for(i = b = 0; i < len; i++, cc++)
394                 {
395                         switch(*cc)
396                         {
397                         case '\a': *cptr++ = '\\'; *cptr++ = 'a'; b += 2; break;
398                         case '\b': *cptr++ = '\\'; *cptr++ = 'b'; b += 2; break;
399                         case '\f': *cptr++ = '\\'; *cptr++ = 'f'; b += 2; break;
400                         case '\n': *cptr++ = '\\'; *cptr++ = 'n'; b += 2; break;
401                         case '\r': *cptr++ = '\\'; *cptr++ = 'r'; b += 2; break;
402                         case '\t': *cptr++ = '\\'; *cptr++ = 't'; b += 2; break;
403                         case '\v': *cptr++ = '\\'; *cptr++ = 'v'; b += 2; break;
404                         case '\\': *cptr++ = '\\'; *cptr++ = '\\'; b += 2; break;
405                         case '"':  *cptr++ = '\\'; *cptr++ = '"'; b += 2; break;
406                         default:
407                             if(isprint(*cc))
408                             {
409                                 *cptr++ = *cc;
410                                 b++;
411                             }
412                             else
413                             {
414                                 int n = sprintf(cptr, "\\x%02x", *cc & 0xff);
415                                 cptr += n;
416                                 b += n;
417                             }
418                             break;
419                         }
420                         if(i < len-1 && b >= 72)
421                         {
422                                 *cptr++ = '"';
423                                 *cptr++ = ',';
424                                 *cptr++ = '\n';
425                                 *cptr++ = ' ';
426                                 *cptr++ = '"';
427                                 b = 0;
428                         }
429                 }
430                 len = (len + 3) & ~3;
431                 for(; i < len; i++)
432                 {
433                         *cptr++ = '\\';
434                         *cptr++ = 'x';
435                         *cptr++ = '0';
436                         *cptr++ = '0';
437                 }
438                 *cptr++ = '"';
439                 *cptr = '\0';
440                 free(tmp);
441         }
442         return str;
443 }
444
445 static void write_rcinline(FILE *fp)
446 {
447         lan_blk_t *lbp;
448         int i;
449         int j;
450
451         for(lbp = lanblockhead; lbp; lbp = lbp->next)
452         {
453                 unsigned offs = 4 * (lbp->nblk * 3 + 1);
454                 fprintf(fp, "\n1 MESSAGETABLE\n");
455                 fprintf(fp, "LANGUAGE 0x%x, 0x%x\n", lbp->lan & 0x3ff, lbp->lan >> 10);
456                 fprintf(fp, "{\n");
457                 fprintf(fp, " /* NBlocks    */ 0x%08xL,\n", lbp->nblk);
458                 for(i = 0; i < lbp->nblk; i++)
459                 {
460                         fprintf(fp, " /* Lo,Hi,Offs */ 0x%08xL, 0x%08xL, 0x%08xL,\n",
461                                         lbp->blks[i].idlo,
462                                         lbp->blks[i].idhi,
463                                         offs);
464                         offs += lbp->blks[i].size;
465                 }
466                 for(i = 0; i < lbp->nblk; i++)
467                 {
468                         block_t *blk = &lbp->blks[i];
469                         for(j = 0; j < blk->nmsg; j++)
470                         {
471                                 char *cptr;
472                                 int l = blk->msgs[j]->len;
473                                 char *comma = j == blk->nmsg-1  && i == lbp->nblk-1 ? "" : ",";
474                                 cptr = make_string(blk->msgs[j]->msg, l, unicodeout ? 0 : blk->msgs[j]->cp);
475                                 fprintf(fp, "\n /* Msg 0x%08x */ 0x%04x, 0x000%c,\n",
476                                         blk->idlo + j,
477                                         (unicodeout ? (l*2+3)&~3 : (l+3)&~3) + 4,
478                                         unicodeout ? '1' : '0');
479                                 fprintf(fp, "%s%s\n", cptr, comma);
480                                 free(cptr);
481                         }
482                 }
483                 fprintf(fp, "}\n");
484         }
485 }
486
487 void write_rc_file(const char *fname)
488 {
489         FILE *fp;
490         char *cptr;
491
492         fp = fopen(fname, "w");
493         if(!fp)
494         {
495                 perror(fname);
496                 exit(1);
497         }
498         cptr = ctime(&now);
499         killnl(cptr, 0);
500         fprintf(fp, str_header, input_name ? input_name : "<stdin>", cmdline, cptr);
501
502         if(rcinline)
503                 write_rcinline(fp);
504         else
505                 write_rcbin(fp);
506         fclose(fp);
507 }
508
509 void write_bin_files(void)
510 {
511         assert(rcinline == 0);
512 }