Implemented /etc/printcap based printing support.
[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                         int n;
314                         if(*uc < 0x100)
315                         {
316                                 if(isprint(*uc))
317                                 {
318                                         *cptr++ = *uc;
319                                         b++;
320                                 }
321                                 else
322                                 {
323                                         switch(*uc)
324                                         {
325                                         case '\a': *cptr++ = '\\'; *cptr++ = 'a'; b += 2; break;
326                                         case '\b': *cptr++ = '\\'; *cptr++ = 'b'; b += 2; break;
327                                         case '\f': *cptr++ = '\\'; *cptr++ = 'f'; b += 2; break;
328                                         case '\n': *cptr++ = '\\'; *cptr++ = 'n'; b += 2; break;
329                                         case '\r': *cptr++ = '\\'; *cptr++ = 'r'; b += 2; break;
330                                         case '\t': *cptr++ = '\\'; *cptr++ = 't'; b += 2; break;
331                                         case '\v': *cptr++ = '\\'; *cptr++ = 'v'; b += 2; break;
332                                         case '\\': *cptr++ = '\\'; *cptr++ = '\\'; b += 2; break;
333                                         case '"':  *cptr++ = '\\'; *cptr++ = '"'; b += 2; break;
334                                         default:
335                                                 n = sprintf(cptr, "\\x%04x", *uc & 0xffff);
336                                                 cptr += n;
337                                                 b += n;
338                                         }
339                                 }
340                         }
341                         else
342                         {
343                                 n = sprintf(cptr, "\\x%04x", *uc & 0xffff);
344                                 cptr += n;
345                                 b += n;
346                         }
347                         if(i < len-1 && b >= 72)
348                         {
349                                 *cptr++ = '"';
350                                 *cptr++ = ',';
351                                 *cptr++ = '\n';
352                                 *cptr++ = ' ';
353                                 *cptr++ = 'L';
354                                 *cptr++ = '"';
355                                 b = 0;
356                         }
357                 }
358                 len = (len + 3) & ~3;
359                 for(; i < len; i++)
360                 {
361                         *cptr++ = '\\';
362                         *cptr++ = 'x';
363                         *cptr++ = '0';
364                         *cptr++ = '0';
365                         *cptr++ = '0';
366                         *cptr++ = '0';
367                 }
368                 *cptr++ = '"';
369                 *cptr = '\0';
370         }
371         else
372         {
373                 char *tmp = xmalloc(2*len+1);
374                 char *cc = tmp;
375                 const union cptable *cpdef = find_codepage(codepage);
376
377                 assert(cpdef != NULL);
378                 if((i = cp_wcstombs(cpdef, 0, uc, unistrlen(uc)+1, tmp, 2*len+1, NULL, NULL)) < 0)
379                         internal_error(__FILE__, __LINE__, "Buffer overflow? code %d.", i);
380                 *cptr++ = ' ';
381                 *cptr++ = '"';
382                 for(i = b = 0; i < len; i++, cc++)
383                 {
384                         int n;
385                         if(isprint(*cc))
386                         {
387                                 *cptr++ = *cc;
388                                 b++;
389                         }
390                         else
391                         {
392                                 switch(*cc)
393                                 {
394                                 case '\a': *cptr++ = '\\'; *cptr++ = 'a'; b += 2; break;
395                                 case '\b': *cptr++ = '\\'; *cptr++ = 'b'; b += 2; break;
396                                 case '\f': *cptr++ = '\\'; *cptr++ = 'f'; b += 2; break;
397                                 case '\n': *cptr++ = '\\'; *cptr++ = 'n'; b += 2; break;
398                                 case '\r': *cptr++ = '\\'; *cptr++ = 'r'; b += 2; break;
399                                 case '\t': *cptr++ = '\\'; *cptr++ = 't'; b += 2; break;
400                                 case '\v': *cptr++ = '\\'; *cptr++ = 'v'; b += 2; break;
401                                 case '\\': *cptr++ = '\\'; *cptr++ = '\\'; b += 2; break;
402                                 case '"':  *cptr++ = '\\'; *cptr++ = '"'; b += 2; break;
403                                 default:
404                                         n = sprintf(cptr, "\\x%02x", *cc & 0xff);
405                                         cptr += n;
406                                         b += n;
407                                 }
408                         }
409                         if(i < len-1 && b >= 72)
410                         {
411                                 *cptr++ = '"';
412                                 *cptr++ = ',';
413                                 *cptr++ = '\n';
414                                 *cptr++ = ' ';
415                                 *cptr++ = '"';
416                                 b = 0;
417                         }
418                 }
419                 len = (len + 3) & ~3;
420                 for(; i < len; i++)
421                 {
422                         *cptr++ = '\\';
423                         *cptr++ = 'x';
424                         *cptr++ = '0';
425                         *cptr++ = '0';
426                 }
427                 *cptr++ = '"';
428                 *cptr = '\0';
429                 free(tmp);
430         }
431         return str;
432 }
433
434 static void write_rcinline(FILE *fp)
435 {
436         lan_blk_t *lbp;
437         int i;
438         int j;
439
440         for(lbp = lanblockhead; lbp; lbp = lbp->next)
441         {
442                 unsigned offs = 4 * (lbp->nblk * 3 + 1); 
443                 fprintf(fp, "\n1 MESSAGETABLE\n");
444                 fprintf(fp, "LANGUAGE 0x%x, 0x%x\n", lbp->lan & 0x3ff, lbp->lan >> 10);
445                 fprintf(fp, "{\n");
446                 fprintf(fp, " /* NBlocks    */ 0x%08xL,\n", lbp->nblk);
447                 for(i = 0; i < lbp->nblk; i++)
448                 {
449                         fprintf(fp, " /* Lo,Hi,Offs */ 0x%08xL, 0x%08xL, 0x%08xL,\n",
450                                         lbp->blks[i].idlo,
451                                         lbp->blks[i].idhi,
452                                         offs);
453                         offs += lbp->blks[i].size;
454                 }
455                 for(i = 0; i < lbp->nblk; i++)
456                 {
457                         block_t *blk = &lbp->blks[i];
458                         for(j = 0; j < blk->nmsg; j++)
459                         {
460                                 char *cptr;
461                                 int l = blk->msgs[j]->len;
462                                 char *comma = j == blk->nmsg-1  && i == lbp->nblk-1 ? "" : ",";
463                                 cptr = make_string(blk->msgs[j]->msg, l, unicodeout ? 0 : blk->msgs[j]->cp);
464                                 fprintf(fp, "\n /* Msg 0x%08x */ 0x%04x, 0x000%c,\n",
465                                         blk->idlo + j,
466                                         (unicodeout ? (l*2+3)&~3 : (l+3)&~3)+4,
467                                         unicodeout ? '1' : '0');
468                                 fprintf(fp, "%s%s\n", cptr, comma);
469                                 free(cptr);
470                         }
471                 }
472                 fprintf(fp, "}\n");
473         }
474 }
475
476 void write_rc_file(const char *fname)
477 {
478         FILE *fp;
479         char *cptr;
480
481         fp = fopen(fname, "w");
482         if(!fp)
483         {
484                 perror(fname);
485                 exit(1);
486         }
487         cptr = ctime(&now);
488         killnl(cptr, 0);
489         fprintf(fp, str_header, input_name ? input_name : "<stdin>", cmdline, cptr);
490
491         if(rcinline)
492                 write_rcinline(fp);
493         else
494                 write_rcbin(fp);
495         fclose(fp);
496 }
497
498 void write_bin_files(void)
499 {
500         assert(rcinline == 0);
501 }
502