libs-base/Tools/make_strings/make_strings.m

625 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 3 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 COPYINGv3.
If not, write to the Free Software Foundation,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#import "common.h"
#import "Foundation/NSString.h"
#import "Foundation/NSAutoreleasePool.h"
#import "Foundation/NSDictionary.h"
#import "Foundation/NSEnumerator.h"
#import "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 char *nilp = 0;
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).
*/
#define add_arg_ch(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;\
}
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;
int 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;
filenamestr = [NSString stringWithCString: filename
encoding: [NSString defaultCStringEncoding]];
if (verbose)
printf("Parsing '%s'.\n", filename);
f = fopen(filename, "rt");
if (!f)
{
NSLog(@"Unable to open '%@': %m\n", filenamestr);
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;
nilp = "nil";
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 == ')')
{
loc_func_t *lf = &loc_funcs[name];
/*{
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]);
}*/
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:
(char*)args[lf->key_index]];
if (lf->comment_index == -1 || !arg_len[lf->comment_index])
comment = nil;
else
comment = [NSString stringWithCString:
(char*)args[lf->comment_index]];
if (lf->table_index == -1
|| (arg_ok[lf->table_index]
&& (args[lf->table_index] == 0
|| strcmp("nil", (char*)args[lf->table_index]) == 0)))
{
table = @"Localizable"; /* TODO: customizable? */
}
else
{
table = [NSString stringWithCString:
(char*)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;
nilp = "nil";
continue;
}
if (nilp)
{
if (ch == *nilp)
{
nilp++;
if (*nilp == '\0')
{
args[num_args] = (unsigned char *)strdup("nil");
arg_len[num_args] = 3;
arg_size[num_args] = 4;
nilp = 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 arrayWithcCapacity: 10];
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;
}