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