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