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