mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-26 10:11:03 +00:00
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@39608 72102866-910b-0410-8b05-ffd578937521
625 lines
12 KiB
Objective-C
625 lines
12 KiB
Objective-C
/* 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);
|
|
}
|
|
|
|
DESTROY(arp);
|
|
}
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
CREATE_AUTORELEASE_POOL(arp);
|
|
|
|
NSMutableDictionary *source_entries;
|
|
NSMutableArray *languages = [NSMutableArray arrayWithCapacity: 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);
|
|
}
|
|
}
|
|
|
|
DESTROY(arp);
|
|
|
|
if (error)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|