1 /*
2 * $Id: msgbind.c,v 1.6 2004/04/17 11:39:43 andrew_belov Exp $
3 * ---------------------------------------------------------------------------
4 * This file creates the message files for all ARJ modules. It is essential
5 * that it does not make use of any ARJ modules (MISC.C and so on) because it
6 * is the first file of the project.
7 *
8 */
9
10 /* We need to manually include the needed files because ARJ.H may contain
11 references to missing message header files */
12
13 #include "environ.h"
14 #include "defines.h"
15 #include "filemode.h"
16 #include "misc.h"
17 #include "crc32.h"
18 #include "arjdata.h"
19
20 #include <ctype.h>
21 #include <signal.h>
22 #include <time.h>
23
24 #define MSG_SIZE 32752 /* Constant msg buffer size */
25 #define POOL_SIZE 51200 /* Maximum size of variable-len buf */
26 #define POOL_R_INC 1024 /* Realloc incrementation */
27 #define COLUMNS 79 /* Columns per line in I*.* */
28
29 static char msgpool[32]; /* {MSGPOOL} or {HARDERR} */
30 static char msgname[40]; /* {MSG_OUT_OF_MEM} */
31 static char targets[128]; /* {ARJ, ARJSFXV, ARJSFXJR} */
32 static char systems[128]; /* {DOS, OS2, WIN32, UNIX} */
33 static char bindings[16]; /* {S, C, or none (means ANY} */
34 static char locales[32]; /* {en, ru} */
35 static char msgtype[8]; /* {N} or {F}, for NMSG/FMSG, respectively */
36 static char rdir[FILENAME_MAX];/* Resource directory */
37
38 static char M_FMSG[]="FMSG";
39 static char M_NMSG[]="char";
40
41 static char INCL[]="#include \"bindings.h\"\n#include \"environ.h\"\n#include \"arjtypes.h\"\n\n";
42
43 static char SKIP[]="NULL";
44
45 struct pool
46 {
47 char name[32];
48 unsigned int msgs;
49 char *data;
50 char st_class;
51 unsigned int safesize;
52 unsigned int columns;
53 unsigned int indent;
54 unsigned long crc32;
55 };
56
57 /* A Q&D emulation of the strupr() and strlwr() for those who don't have it */
58
59 #ifndef HAVE_STRLWR
strlwr(char * str)60 static char *strlwr(char *str)
61 {
62 char *p;
63
64 for(p=str; *p!='\0'; p++)
65 *p=tolower(*p);
66 return(str);
67 }
68 #endif
69
70 #ifndef HAVE_STRUPR
strupr(char * str)71 static char *strupr(char *str)
72 {
73 char *p;
74
75 for(p=str; *p!='\0'; p++)
76 *p=toupper(*p);
77 return(str);
78 }
79 #endif
80
81 /* Remove given characters */
82
compress(char * i,char f)83 char *compress(char *i, char f)
84 {
85 int c, d;
86 for(c=d=0; i[c]!='\0'; c++)
87 {
88 if(i[c]!=f)
89 i[d++]=i[c];
90 }
91 i[d]='\0';
92 return(i);
93 }
94
95 /* Strip all leading spaces */
96
ltrim(char * i)97 char *ltrim(char *i)
98 {
99 if (i!=NULL)
100 {
101 unsigned int c, j;
102 for (c=0; i[c]==' '||i[c]=='\x9'; c++);
103 if (i[c]!=(char)0)
104 {
105 for (j=0; i[c]!='\0'; j++)
106 {
107 i[j]=i[c]; c++;
108 }
109 i[j]=(char)0;
110 }
111 }
112 return i;
113 }
114
115 /* Strip all trailing spaces */
116
rtrim(char * i)117 char *rtrim(char *i)
118 {
119 if (i!=NULL)
120 {
121 unsigned int c, j;
122 j=0;
123 for (c=0; i[c]!='\0'; c++) if (i[c]!=' '&&i[c]!='\x9') j=c+1;
124 i[j]='\0';
125 }
126 return i;
127 }
128
129 /* Transform "\n", "\r", "\a" and "\b" characters to the corresponding ASCII
130 equivalents */
131
patch_string(char * i)132 void patch_string(char *i)
133 {
134 int c, j;
135
136 if (i!=NULL)
137 {
138 for(c=0; i[c]!='\0'; c++)
139 {
140 if(i[c]=='\\')
141 {
142 switch(i[c+1])
143 {
144 case 'a':
145 i[c]='\a';
146 break;
147 case 'b':
148 i[c]='\b';
149 break;
150 case 'f':
151 i[c]='\f';
152 break;
153 case 'n':
154 i[c]='\n';
155 break;
156 case 'r':
157 i[c]='\r';
158 break;
159 case 't':
160 i[c]='\t';
161 break;
162 case 'v':
163 i[c]='\v';
164 break;
165 case '\"':
166 i[c]='\"';
167 break;
168 case '\\':
169 i[c]='\\';
170 break;
171 }
172 for(j=c+1; i[j]!='\0'; j++)
173 i[j]=i[j+1];
174 }
175 }
176 }
177 }
178
179 /* Get one phrase in brackets */
180
read_brackets(FILE * file,char * buf,int size,int keep)181 char *read_brackets(FILE *file, char *buf, int size, int keep)
182 {
183 int c, offset;
184
185 /* Until the left one has been met... */
186 while((c=fgetc(file))!=(int)'{')
187 if(c==-1) return(NULL);
188 /* Now fill the buffer */
189 if(keep)
190 {
191 offset=1;
192 buf[0]='{';
193 }
194 else
195 offset=0;
196 while((c=fgetc(file))!=(int)'}')
197 {
198 if(c==-1) return(NULL);
199 if(offset<size-1-keep) buf[offset++]=(char)c;
200 }
201 if(keep)
202 buf[offset++]='}';
203 buf[offset]='\0';
204 return(ltrim(rtrim(buf)));
205 }
206
207 /* Checks if the given parameter is present in the bracketed list. Returns
208 parameter number (1...32767) or 0 => the parameter was not found. */
209
is_in_brackets(char * brackets,char * param)210 int is_in_brackets(char *brackets, char *param)
211 {
212 const char *delimiters="{, ;}";
213 int j, firstpos=1, inspace=0, count=0;
214 int invert=0;
215
216 for(j=1; brackets[j]!='\0'; j++)
217 {
218 while(brackets[firstpos]=='!')
219 {
220 invert=!invert;
221 firstpos++;
222 }
223 if(strchr(delimiters, brackets[j])!=NULL&&!inspace)
224 {
225 count++;
226 inspace=1;
227 if(memcmp(brackets+firstpos, param, j-firstpos)==0||brackets[firstpos]=='*')
228 return(invert?0:count);
229 }
230 else if(strchr(delimiters, brackets[j])==NULL&&inspace)
231 {
232 inspace=0;
233 firstpos=j;
234 }
235 }
236 return(invert);
237 }
238
239 /* Fetch a quoted message from the brackets. The index given varies from 1
240 to 32767. */
241
fetch_quotes(FILE * resfile,int index,char * buf,int size)242 char *fetch_quotes(FILE *resfile, int index, char *buf, int size)
243 {
244 int c, tc, offset, qcount=0, quoted=0;
245 FILE *tstream;
246 char t_name[FILENAME_MAX];
247 int t_offset;
248
249 /* Until the left one has been met... */
250 while((c=fgetc(resfile))!=(int)'{')
251 if(c==EOF)
252 return(NULL);
253 /* Now wait until we come to the closing bracket, or... */
254 offset=0;
255 while((c=fgetc(resfile))!=(int)'}')
256 {
257 if(c==EOF)
258 return(NULL);
259 /* Reference to external file (v 1.30+) */
260 else if(c=='@'&&!quoted&&qcount%2==0)
261 {
262 qcount+=2;
263 if(qcount==index*2)
264 {
265 strcpy(t_name, rdir);
266 t_offset=strlen(t_name);
267 while((tc=fgetc(resfile))!=EOF&&(isalnum(tc)||tc=='.'||tc=='_'||tc=='\\'||tc=='/'))
268 t_name[t_offset++]=tc;
269 t_name[t_offset]='\0';
270 if((tstream=fopen(t_name, m_r))==NULL)
271 printf("Malformed declaration: <%s>\n", t_name);
272 else
273 {
274 buf[offset++]='\"';
275 while((tc=fgetc(tstream))!=EOF)
276 {
277 switch(tc)
278 {
279 case '\n':
280 buf[offset++]='\\';
281 buf[offset++]='n';
282 break;
283 case '\a':
284 buf[offset++]='\\';
285 buf[offset++]='a';
286 break;
287 case '\b':
288 buf[offset++]='\\';
289 buf[offset++]='b';
290 break;
291 case '\"':
292 buf[offset++]='\\';
293 buf[offset++]='\"';
294 break;
295 case '\\':
296 buf[offset++]='\\';
297 buf[offset++]='\\';
298 break;
299 default:
300 buf[offset++]=tc;
301 }
302 }
303 buf[offset++]='\"';
304 buf[offset]='\0';
305 fclose(tstream);
306 return(buf);
307 }
308 }
309 }
310 else if(c=='\"'&&!quoted)
311 {
312 if(++qcount==index*2)
313 {
314 buf[offset++]='\"';
315 buf[offset]='\0';
316 return(buf);
317 }
318 }
319 quoted=c=='\\';
320 if(offset<size-1&&qcount==index*2-1)
321 buf[offset++]=(char)c;
322 }
323 return(NULL);
324 }
325
326 /* Fetch messages from the resource file, return NULL if EOF. */
327
get_msg(FILE * resfile,char * target,char * c_system,char binding,char * locale,char * buf,int size)328 char *get_msg(FILE *resfile, char *target, char *c_system, char binding, char *locale, char *buf, int size)
329 {
330 int locale_offset;
331
332 while(!feof(resfile))
333 {
334 if(read_brackets(resfile, msgpool, sizeof(msgpool), 0)==NULL||
335 read_brackets(resfile, msgname, sizeof(msgname), 0)==NULL||
336 read_brackets(resfile, targets, sizeof(targets), 1)==NULL||
337 read_brackets(resfile, systems, sizeof(systems), 1)==NULL||
338 read_brackets(resfile, bindings, sizeof(bindings), 0)==NULL||
339 read_brackets(resfile, locales, sizeof(locales), 1)==NULL||
340 read_brackets(resfile, msgtype, sizeof(msgtype), 0)==NULL)
341 return(NULL);
342 strlwr(targets);
343 strlwr(systems);
344 strlwr(bindings);
345 strlwr(locales);
346 strupr(msgtype);
347
348 locale_offset=is_in_brackets(locales, locale);
349 if(is_in_brackets(targets, target)&&is_in_brackets(systems, c_system)&&(bindings[0]=='\0'||strchr(bindings, binding)!=NULL)&&locale_offset)
350 {
351 if(fetch_quotes(resfile, locale_offset, buf, size)!=NULL)
352 return(buf);
353 }
354 else
355 read_brackets(resfile, buf, 0, 0);
356 }
357 return(NULL);
358 }
359
360 /* malloc */
361
malloc_msg(unsigned int size)362 void *malloc_msg(unsigned int size)
363 {
364 void *p;
365 if((p=malloc(size))==NULL)
366 {
367 printf("Out of memory!\r\n");
368 exit(4);
369 }
370 return(p);
371 }
372
373 /* Add header to file */
374
put_hdr(FILE * file,char * name,char * src)375 void put_hdr(FILE *file, char *name, char *src)
376 {
377 char buf[FILENAME_MAX];
378 time_t cur_unixtime;
379 struct tm *stm;
380
381 cur_unixtime=time(NULL);
382 stm=localtime(&cur_unixtime);
383 strcpyn(buf, name, sizeof(buf));
384 strupr(buf);
385 fprintf(file, "/*\n"
386 " * %-29s, %04u/%02u/%02u\n"
387 " * ---------------------------------------------------------------------------\n"
388 " * Do not modify this file. It is automatically generated by MSGBIND from\n"
389 " * %s.\n"
390 " * To rebuild the language resources, run MSGBIND.\n"
391 " *\n"
392 " */\n\n", buf, (unsigned int)stm->tm_year+1900, stm->tm_mon+1, stm->tm_mday, strupr(src));
393 }
394
395 /* And so we begin... */
396
main(int argc,char ** argv)397 int main(int argc, char **argv)
398 {
399 char source[FILENAME_MAX]; /* ReSource filename */
400 char target_i[FILENAME_MAX]; /* .C-file containing pointers */
401 char target_n[FILENAME_MAX]; /* All NMSGs */
402 char target_f[FILENAME_MAX]; /* All FMSGs */
403 char target_h[FILENAME_MAX]; /* Include file - all NMSGs/FMSGs */
404 char target[FILENAME_MAX], locale[15];
405 char c_system[32];
406 char binding;
407 char *msg_buffer; /* Messages may be large enough... */
408 struct pool pool[32]; /* Up to 32 separate msg arrays */
409 int tpool, cur_pool=0, i;
410 int buf_len;
411 FILE *resfile, *ifile, *nfile, *ffile, *hfile;
412 char pathsep[2];
413
414 printf("MSGBIND v 1.65 [14/12/2002] Not a part of any binary package!\n\n");
415 if(argc<6)
416 {
417 printf("Usage: MSGBIND <resource> <target> <OS> <binding> <locale> [target directory],\n"
418 " e.g, to build MSG_SFXV.*, type MSGBIND MSG.RES MSG_SFXV DOS en\n"
419 "\n"
420 "The target directory is optional. If specified (e.g., BINARIES\\ENGLISH), all\n"
421 "compiled .C files will be placed there.\n");
422 exit(1);
423 }
424 msg_buffer=(char *)malloc_msg(MSG_SIZE);
425 build_crc32_table();
426 pathsep[0]=PATHSEP_DEFAULT;
427 pathsep[1]='\0';
428 strcpyn(source, argv[1], sizeof(source)-8);
429 /* Fix for GCC/EMX: convert UNIX-like representations to DOS */
430 #if PATHSEP_UNIX!=PATHSEP_DEFAULT
431 for(i=0; source[i]!='\0'; i++)
432 if(source[i]==PATHSEP_UNIX)
433 source[i]=PATHSEP_DEFAULT;
434 #endif
435 if(strrchr(source, PATHSEP_DEFAULT)==NULL)
436 rdir[0]='\0';
437 else
438 {
439 strcpy(rdir, source);
440 strrchr(rdir, PATHSEP_DEFAULT)[1]='\0';
441 }
442 strcpyn(target, argv[2], sizeof(target)-8);
443 strcpyn(c_system, argv[3], sizeof(c_system));
444 binding=tolower(argv[4][0]);
445 /* Beginning with v 1.21, target directory may be also specified */
446 if(argc==7)
447 {
448 strcpyn(target_i, argv[6], sizeof(target_i)-8);
449 strcpyn(target_n, argv[6], sizeof(target_n)-8);
450 strcpyn(target_f, argv[6], sizeof(target_f)-8);
451 strcpyn(target_h, argv[6], sizeof(target_f)-8); /* v 1.41+ */
452 if(argv[6][strlen(argv[6])-1]!=PATHSEP_DEFAULT);
453 {
454 strcat(target_i, pathsep);
455 strcat(target_n, pathsep);
456 strcat(target_f, pathsep);
457 strcat(target_h, pathsep);
458 }
459 }
460 else
461 {
462 target_i[0]='\0';
463 target_n[0]='\0';
464 target_f[0]='\0';
465 target_h[0]='\0';
466 }
467 strcat(target_i, "i");
468 strcat(target_n, "n");
469 strcat(target_f, "f");
470 strcat(target_i, argv[2]);
471 strcat(target_n, argv[2]);
472 strcat(target_f, argv[2]);
473 strcat(target_h, argv[2]);
474 /* The source has the extension .MSG, the targets are .H and .C */
475 if(strchr(source, '.')==NULL)
476 strcat(source, ".msg");
477 strcat(target_i, ".c");
478 strcat(target_n, ".c");
479 strcat(target_f, ".c");
480 strcat(target_h, ".h");
481 strcpyn(locale, argv[5], sizeof(locale));
482 strlwr(target);
483 strlwr(c_system);
484 strlwr(locale);
485 /* Block out all signals, since this transaction is mission-critical */
486 signal(SIGINT, SIG_IGN);
487 #ifndef NO_TERM_HDL
488 signal(SIGTERM, SIG_IGN);
489 #endif
490 if((resfile=fopen(source, m_r))==NULL)
491 {
492 printf("Can't open source file!\n");
493 exit(2);
494 }
495 if((ifile=fopen(target_i, m_w))==NULL)
496 {
497 printf("Can't open index file!\n");
498 exit(3);
499 }
500 if((nfile=fopen(target_n, m_w))==NULL)
501 {
502 printf("Can't open NMSG output file!\n");
503 exit(3);
504 }
505 if((ffile=fopen(target_f, m_w))==NULL)
506 {
507 printf("Can't open FMSG output file!\n");
508 exit(3);
509 }
510 if((hfile=fopen(target_h, m_w))==NULL)
511 {
512 printf("Can't open .h output file!\n");
513 exit(3);
514 }
515 put_hdr(ifile, target_i, source);
516 put_hdr(nfile, target_n, source);
517 put_hdr(ffile, target_f, source);
518 put_hdr(hfile, target_h, source);
519 fputs(INCL, ifile);
520 fputs(INCL, nfile);
521 fputs(INCL, ffile);
522 fprintf(ifile, "#include \"");
523 for(i=0; target_h[i]!='\0'; i++)
524 fputc(target_h[i]=='\\'?'/':target_h[i], ifile);
525 fprintf(ifile, "\"\n\n");
526 /* Ack. Now process the source file line by line... */
527 while(get_msg(resfile, target, c_system, binding, locale, msg_buffer, MSG_SIZE)!=NULL)
528 {
529 expand_tags(msg_buffer, MSG_SIZE);
530 fprintf(toupper(msgtype[0])=='N'?nfile:ffile, "char %s[]=%s;\n", msgname, msg_buffer);
531 fprintf(hfile, "extern %s %s[];\n", toupper(msgtype[0])=='N'?M_NMSG:M_FMSG, msgname);
532 /* Check if the message belongs to a pre-defined message pool */
533 if(strcmp(msgpool, SKIP))
534 {
535 /* Pick a message heap */
536 for(tpool=0; tpool<cur_pool; tpool++)
537 {
538 if(!strcmp(pool[tpool].name, msgpool))
539 break;
540 }
541 /* Allocate new heap if needed */
542 if(tpool==cur_pool)
543 {
544 if(cur_pool>=sizeof(pool))
545 {
546 printf("Too many message groups!\n");
547 exit(4);
548 }
549 strcpy(pool[tpool].name, msgpool);
550 pool[tpool].msgs=0;
551 pool[tpool].crc32=CRC_MASK;
552 pool[tpool].safesize=POOL_R_INC;
553 pool[tpool].data=(char *)malloc_msg(POOL_R_INC);
554 pool[tpool].st_class=toupper(msgtype[0])=='N'?'N':'F';
555 sprintf(pool[tpool].data, "%cMSGP %s []={", pool[tpool].st_class, msgpool);
556 pool[tpool].columns=pool[tpool].indent=strlen(pool[tpool].data);
557 cur_pool++;
558 }
559 pool[tpool].msgs++;
560 if(strlen(pool[tpool].data)+strlen(msgname)+pool[tpool].indent+16>pool[tpool].safesize)
561 {
562 if((pool[tpool].safesize+=POOL_R_INC)>POOL_SIZE)
563 {
564 printf("Message pool for %s exceeded %u bytes, exiting\n", msgpool, POOL_SIZE);
565 }
566 if((pool[tpool].data=realloc(pool[tpool].data, pool[tpool].safesize))==NULL)
567 {
568 printf("Unexpected lack of memory!r\n");
569 exit(5);
570 }
571 }
572 if((pool[tpool].columns+=strlen(msgname))>COLUMNS)
573 {
574 strcat(pool[tpool].data, "\n");
575 pool[tpool].columns=pool[tpool].indent;
576 pool[tpool].data[strlen(pool[tpool].data)+pool[tpool].indent]='\0';
577 memset(pool[tpool].data+strlen(pool[tpool].data), 32, pool[tpool].indent);
578 }
579 strcat(pool[tpool].data, msgname);
580 strcat(pool[tpool].data, ", ");
581 safe_strcpy(msg_buffer, msg_buffer+1);
582 buf_len=strlen(msg_buffer);
583 msg_buffer[--buf_len]='\0';
584 patch_string(msg_buffer);
585 crc32term=pool[tpool].crc32;
586 crc32_for_string(msg_buffer);
587 pool[tpool].crc32=crc32term;
588 }
589 }
590 fputs("\n", hfile);
591 /* First, flush the message pools... */
592 for(tpool=0; tpool<cur_pool; tpool++)
593 {
594 strcat(pool[tpool].data, "NULL};\n\n");
595 fputs(pool[tpool].data, ifile);
596 free(pool[tpool].data);
597 /* ...by the way, flushing the CRC-32 values */
598 fprintf(hfile, "#define %s_CRC32 0x%08lx\n", pool[tpool].name, pool[tpool].crc32);
599 fprintf(hfile, "extern %cMSGP %s[];\n", pool[tpool].st_class, pool[tpool].name);
600 }
601 /* Now, put an ending LF to all files */
602 fputs("\n", ifile); fputs("\n", nfile); fputs("\n", ffile); fputs("\n", hfile);
603 fclose(ifile); fclose(nfile); fclose(ffile); fclose(hfile);
604 free(msg_buffer);
605 return(0); /* Report no error */
606 }
607