libs-base/Tools/make_strings/make_strings.m

583 lines
12 KiB
Mathematica
Raw Normal View History

/* make_strings
Copyright (C) 2002 Free Software Foundation, Inc.
Written by: Alexander Malmberg <alexander@malmberg.org>
Created: 2002
This file is part of the GNUstep Project
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
You should have received a copy of the GNU General Public
License along with this program; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <Foundation/NSObject.h>
#include <Foundation/NSString.h>
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/NSArray.h>
#include "make_strings.h"
#include "StringsFile.h"
#include "SourceEntry.h"
#include "StringsEntry.h"
int verbose,aggressive_import,aggressive_match,aggressive_remove;
typedef struct
{
const char *func_name;
int num_args;
int key_index,comment_index,table_index;
} loc_func_t;
/*
List of functions we should look for (easy to extend).
*/
static loc_func_t loc_funcs[]=
{
{"_" , 1, 0,-1,-1},
{"NSLocalizedString" , 2, 0, 1,-1},
{"NSLocalizedStringFromTable" , 3, 0, 2, 1},
{"NSLocalizedStringFromTableInBundle" , 4, 0, 3, 1},
{"__" , 1, 0,-1,-1},
{"NSLocalizedStaticString" , 2, 0, 1,-1},
{},
};
#define MAX_ARGS 4
static int isname1(unsigned char ch)
{
if (ch=='_' || (ch>='a' && ch<='z') || (ch>='A' && ch<='Z')) return 1;
return 0;
}
static int isname(unsigned char ch)
{
if (isname1(ch)) return 1;
if (ch>='0' && ch<='9') return 1;
return 0;
}
/*
This function attempts to parse the specified file and adds any calls to
tables. To avoid excessive complexity and to easily handle comments and
strings everywhere, it's written as a state machine.
Things that can fool it:
- Stupid use of pre-processor stuff, like '#define foo bar('.
Solution: Don't do that.
- Nested localization calls, like:
'NSLocalizedString(@"foo",NSLocalizedString(@"bar",@"zot"))'.
Solution: don't do that (with the current functions, there should never
be any reason to).
*/
static int ParseFile(const char *filename,NSMutableDictionary *tables)
{
FILE *f;
NSString *filenamestr;
/*
0: normal parsing
1: in '//' comment
2: in '/ *' comment
3: in string
5: parsing potentially interesting name
6: parsing uninteresting name
7: got name, wait for '('
8: parsing arguments
*/
int state,old_state,skip;
int ch,nch;
int name=-1,nindex=0;
int cur_line;
unsigned char *args[MAX_ARGS];
int arg_size[MAX_ARGS],arg_len[MAX_ARGS];
int arg_ok[MAX_ARGS];
int num_args;
int i;
int depth=0;
void add_arg_ch(int ch)
{
if (arg_len[num_args]+1>=arg_size[num_args])
{
arg_size[num_args]+=512;
args[num_args]=realloc(args[num_args],arg_size[num_args]);
if (!args[num_args])
{
NSLog(@"out of memory!\n");
exit(1);
}
}
args[num_args][arg_len[num_args]++]=ch;
args[num_args][arg_len[num_args]]=0;
}
filenamestr=[NSString stringWithCString: filename];
if (verbose)
printf("Parsing '%s'.\n", [filenamestr cString]);
f=fopen(filename,"rt");
if (!f)
{
NSLog(@"Unable to open '%s': %m\n",filename);
return 1;
}
old_state=state=0;
skip=0;
cur_line=1;
num_args=-1;
for (i=0;i<MAX_ARGS;i++)
{
args[i]=NULL;
arg_size[i]=0;
}
nch=fgetc(f);
while (!feof(f))
{
ch=nch;
if (ch==EOF) break;
nch=fgetc(f);
// printf("ch=%02x '%c' state=%i skip=%i\n",ch,ch,state,skip);
if (skip)
{
skip--;
continue;
}
if (ch=='\n') cur_line++;
if (state==3)
{
if (ch!='"' && num_args!=-1 && arg_ok[num_args])
add_arg_ch(ch);
if (ch=='\\')
{
if (num_args!=-1 && arg_ok[num_args])
add_arg_ch(nch);
skip=1;
}
else if (ch=='"')
state=old_state;
continue;
}
if (state==1)
{
if (ch=='\n' || ch=='\r')
state=old_state;
continue;
}
if (state==2)
{
if (ch=='*' && nch=='/')
{
state=old_state;
skip=1;
}
continue;
}
if (ch=='"')
{
old_state=state;
state=3;
continue;
}
if (ch=='/' && nch=='/')
{
old_state=state;
state=1;
continue;
}
if (ch=='/' && nch=='*')
{
old_state=state;
state=2;
/* skip a character so we'll parse '/ * /' correctly */
skip=1;
continue;
}
if (state==0 && isname1(ch))
{
for (name=0;loc_funcs[name].func_name;name++)
if (ch==loc_funcs[name].func_name[0])
break;
if (loc_funcs[name].func_name)
{
state=5;
nindex=1;
}
else
state=6;
continue;
}
if (state==6)
{
if (!isname(ch))
state=0;
continue;
}
if (state==5)
{
if (isname(ch))
{
int old_name;
if (loc_funcs[name].func_name[nindex]==ch)
{
nindex++;
continue;
}
old_name=name;
for (name++;loc_funcs[name].func_name;name++)
{
if (!strncmp(loc_funcs[old_name].func_name,loc_funcs[name].func_name,nindex) &&
loc_funcs[name].func_name[nindex]==ch)
break;
}
if (loc_funcs[name].func_name)
nindex++;
else
state=6;
continue;
}
if (loc_funcs[name].func_name[nindex]!=0)
{
int old_name=name;
for (name++;loc_funcs[name].func_name;name++)
{
if (!strncmp(loc_funcs[old_name].func_name,loc_funcs[name].func_name,nindex) &&
loc_funcs[name].func_name[nindex]==0)
break;
}
}
if (!loc_funcs[name].func_name)
{
state=0;
continue;
}
else
{
// printf("found call to '%s' at line %i\n",loc_funcs[name].func_name,cur_line);
state=7;
}
}
if (state==7)
{
if (ch=='(')
{
num_args=0;
depth=0;
state=8;
for (i=0;i<MAX_ARGS;i++)
{
arg_len[i]=0;
arg_ok[i]=1;
}
// printf(" start arg list, want %i args\n",loc_funcs[name].num_args);
}
else if (ch>32)
state=0;
continue;
}
if (state==8)
{
if (depth)
{
if (ch==')' || ch==']' || ch=='}')
depth--;
continue;
}
if (ch=='(' || ch=='[' || ch=='{')
{
arg_ok[num_args]=0;
depth++;
continue;
}
if (ch==')')
{
num_args++;
/* {
printf("got call to '%s', %i args\n",loc_funcs[name].func_name,num_args);
for (i=0;i<num_args;i++)
printf(" %3i : %i '%s'\n",i,arg_ok[i],args[i]);
}*/
{
loc_func_t *lf=&loc_funcs[name];
if (arg_ok[lf->key_index] &&
(lf->comment_index==-1 || arg_ok[lf->comment_index]) &&
(lf->table_index==-1 || arg_ok[lf->table_index]))
{
SourceEntry *e;
NSString *key,*comment,*table;
/* TODO: let user specify source file encoding */
key=[NSString stringWithCString: args[lf->key_index]];
if (lf->comment_index==-1 || !arg_len[lf->comment_index])
comment=nil;
else
comment=[NSString stringWithCString: args[lf->comment_index]];
if (lf->table_index==-1)
table=@"Localizable"; /* TODO: customizable? */
else
table=[NSString stringWithCString: args[lf->table_index]];
e=[[SourceEntry alloc] initWithKey: key comment: comment file: filenamestr line: cur_line];
[tables addEntry: e toTable: table];
[e release];
}
else
{
NSLog(@"unable to parse call to '%s' at %s:%i\n",lf->func_name,filename,cur_line);
}
}
num_args=-1;
state=0;
continue;
}
if (ch==',')
{
num_args++;
if (num_args==MAX_ARGS)
state=0;
continue;
}
if (ch>32 && ch!='@')
arg_ok[num_args]=0;
continue;
}
}
for (i=0;i<MAX_ARGS;i++)
if (args[i])
free(args[i]);
fclose(f);
return 0;
}
@implementation NSMutableDictionary (make_strings)
-(void) addEntry: (SourceEntry *)e toTable: (NSString *)table
{
NSMutableArray *a;
a=[self objectForKey: table];
if (!a)
{
a=[[NSMutableArray alloc] init];
[self setObject: a forKey: table];
[a release];
}
[a addObject: e];
}
@end
static void UpdateTable(NSArray *source_table,NSString *filename)
{
int i,c;
StringsFile *sf;
if (verbose)
printf("Updating '%s'.\n", [filename cString]);
sf=[[StringsFile alloc] initWithFile: filename];
c=[source_table count];
for (i=0;i<c;i++)
{
[sf addSourceEntry: [source_table objectAtIndex: i]];
}
[sf writeToFile: filename];
DESTROY(sf);
}
static void HandleLanguage(NSString *language_name,NSMutableDictionary *source_entries)
{
NSEnumerator *e;
NSString *table_name;
NSString *filename;
CREATE_AUTORELEASE_POOL(arp);
if (verbose)
printf("Updating language '%s'.\n", [language_name cString]);
for (e=[source_entries keyEnumerator];(table_name=[e nextObject]);)
{
filename=[[[NSString stringWithFormat: @"%@.lproj",language_name]
stringByAppendingPathComponent:
[NSString stringWithFormat: @"%@.strings",table_name]]
stringByStandardizingPath];
UpdateTable([source_entries objectForKey: table_name],filename);
}
#if GS_WITH_GC == 0
DESTROY(arp);
#endif
}
int main(int argc, char **argv)
{
#if GS_WITH_GC == 0
CREATE_AUTORELEASE_POOL(arp);
#endif
NSMutableDictionary *source_entries;
NSMutableArray *languages=[[NSMutableArray alloc] init];
int error;
{
int i, j;
char *c;
for (j = i = 1; i < argc; i++)
{
c=argv[i];
if (!strcmp (c,"--help"))
{
printf ("Syntax: %s [--help] [--verbose] [--aggressive-import] [--aggressive-match] [--aggressive-remove] [-L languages] files.[hmc...]\n",
argv[0]);
printf("\n");
printf("Example: %s -L \"English Swedish German\" *.[hm]\n",
argv[0]);
return 0;
}
else if (!strcmp (c,"--verbose"))
{
verbose = 1;
}
else if (!strcmp (c,"--aggressive-import"))
{
aggressive_import = 1;
aggressive_match = 1;
}
else if (!strcmp (c,"--aggressive-match"))
{
aggressive_match = 1;
}
else if (!strcmp(c,"--aggressive-remove"))
{
aggressive_remove=1;
aggressive_match=1;
}
else if (!strcmp (c,"-L"))
{
char *d,*d2;
if (++i == argc)
{
NSLog (@"syntax error\n");
return 1;
}
d = argv[i];
while (1)
{
d2 = strchr (d,' ');
if (d2)
*d2=0;
[languages addObject: [NSString stringWithCString: d]];
d = d2 + 1;
if (!d2)
break;
}
}
else
{
argv[j++] = c;
}
}
argc = j;
}
/* Remove any empty language from the list. */
{
int k;
for (k = [languages count] - 1; k >= 0; k--)
{
NSString *language = [languages objectAtIndex: k];
if ([language isEqualToString: @""])
{
[languages removeObjectAtIndex: k];
}
}
}
if (![languages count])
{
NSLog (@"No languages specified!\n");
return 1;
}
if (argc == 1)
{
NSLog (@"No files specified!\n");
return 1;
}
source_entries = [[NSMutableDictionary alloc] init];
error = 0;
{
int i;
for (i = 1; i < argc; i++)
error += ParseFile (argv[i], source_entries);
}
if (!error)
{
int i, c = [languages count];
for (i = 0; i < c; i++)
{
HandleLanguage ([languages objectAtIndex: i], source_entries);
}
}
#if GS_WITH_GC == 0
DESTROY(arp);
#endif
if (error)
return 1;
else
return 0;
}