316 lines
6.8 KiB
C++
316 lines
6.8 KiB
C++
/*
|
|
* Copyright 2003-2010, Haiku, Inc.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Oliver Tappe, zooey@hirschkaefer.de
|
|
* Adrien Destugues, pulkomandy@gmail.com
|
|
*/
|
|
|
|
|
|
#include <cctype>
|
|
#include <cerrno>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
|
|
#include <Entry.h>
|
|
#include <File.h>
|
|
#include "RegExp.h"
|
|
#include <StorageDefs.h>
|
|
#include <String.h>
|
|
|
|
#include <EditableCatalog.h>
|
|
|
|
using BPrivate::EditableCatalog;
|
|
|
|
|
|
bool showKeys = false;
|
|
bool showSummary = false;
|
|
bool showWarnings = false;
|
|
const char *inputFile = NULL;
|
|
BString outputFile;
|
|
const char *catalogSig = NULL;
|
|
const char *catalogLang = "English";
|
|
BString rxString("B_CATKEY\\s*");
|
|
|
|
BString str, ctx, cmt;
|
|
bool haveID;
|
|
int32 id;
|
|
|
|
|
|
EditableCatalog *catalog = NULL;
|
|
|
|
|
|
void
|
|
usage()
|
|
{
|
|
fputs("usage: collectcatkeys [-pvw] [-r <regex>] [-o <outfile>] "
|
|
"[-l <catalogLanguage>]\n"
|
|
" -s <catalogSig> <prepCppFile>\n"
|
|
"options:\n"
|
|
" -l <catalogLang>\tlanguage of the target-catalog (default is "
|
|
"English)\n"
|
|
" -o <outfile>\t\texplicitly specifies the name of the output-file\n"
|
|
" -p\t\t\tprint keys as they are found\n"
|
|
" -r <regex>\t\tchanges the regex used by the key-scanner to the one "
|
|
"given,\n"
|
|
" \t\t\tthe default is: ", stderr);
|
|
fputs(rxString.String(), stderr);
|
|
fputs("\n -s <catalogSig>\tsignature of the target-catalog\n"
|
|
" -v\t\t\tbe verbose, show summary\n"
|
|
" -w\t\t\tshow warnings about catalog-accesses that couldn't be "
|
|
" resolved completely\n", stderr);
|
|
exit(-1);
|
|
}
|
|
|
|
|
|
bool
|
|
fetchStr(const char *&in, BString &str, bool lookForID)
|
|
{
|
|
int parLevel = 0;
|
|
|
|
while (isspace(*in) || *in == '(') {
|
|
if (*in == '(')
|
|
parLevel++;
|
|
in++;
|
|
}
|
|
|
|
if (*in == '"') {
|
|
bool inString = true;
|
|
bool quoted = false;
|
|
in++;
|
|
while (parLevel >= 0 && inString)
|
|
{
|
|
// Collect string content until we find a quote marking end of
|
|
// string (skip escaped quotes)
|
|
while (*in != '"' || quoted)
|
|
{
|
|
str.Append(in,1);
|
|
if (*in == '\\' && !quoted)
|
|
quoted = true;
|
|
else
|
|
quoted = false;
|
|
in++;
|
|
}
|
|
in++;
|
|
|
|
inString = false;
|
|
|
|
// Strip all whitespace until we find a closing parenthesis, or the
|
|
// beginning of another string
|
|
while (isspace(*in) || *in == ')') {
|
|
if (*in == ')') {
|
|
if (parLevel == 0)
|
|
return true;
|
|
parLevel--;
|
|
}
|
|
|
|
in++;
|
|
}
|
|
|
|
if (*in == '"') {
|
|
inString = true;
|
|
in++;
|
|
}
|
|
}
|
|
} else {
|
|
if (!memcmp(in, "__null", 6)) {
|
|
// NULL is preprocessed into __null, which we parse as ""
|
|
in += 6;
|
|
} else if (lookForID && (isdigit(*in) || *in == '-' || *in == '+')) {
|
|
// try to parse an ID (a long):
|
|
errno = 0;
|
|
char *next;
|
|
id = strtol(in, &next, 10);
|
|
if (id != 0 || errno == 0) {
|
|
haveID = true;
|
|
in = next;
|
|
}
|
|
} else
|
|
return false;
|
|
|
|
while (isspace(*in) || *in == ')') {
|
|
if (*in == ')') {
|
|
if (!parLevel)
|
|
return true;
|
|
parLevel--;
|
|
}
|
|
in++;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool
|
|
fetchKey(const char *&in)
|
|
{
|
|
str = ctx = cmt = "";
|
|
haveID = false;
|
|
// fetch native string or id:
|
|
if (!fetchStr(in, str, true))
|
|
return false;
|
|
if (*in == ',') {
|
|
in++;
|
|
// fetch context:
|
|
if (!fetchStr(in, ctx, false))
|
|
return false;
|
|
if (*in == ',') {
|
|
in++;
|
|
// fetch comment:
|
|
if (!fetchStr(in, cmt, false))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
collectAllCatalogKeys(BString& inputStr)
|
|
{
|
|
RegExp rx;
|
|
struct regexp *rxprg = rx.Compile(rxString.String());
|
|
if (rx.InitCheck() != B_OK) {
|
|
fprintf(stderr, "regex-compilation error %s\n", rx.ErrorString());
|
|
return;
|
|
}
|
|
status_t res;
|
|
const char *in = inputStr.String();
|
|
while (rx.RunMatcher(rxprg, in)) {
|
|
const char *start = rxprg->startp[0];
|
|
in = rxprg->endp[0];
|
|
if (fetchKey(in)) {
|
|
if (haveID) {
|
|
if (showKeys)
|
|
printf("CatKey(%" B_PRId32 ")\n", id);
|
|
res = catalog->SetString(id, "");
|
|
if (res != B_OK) {
|
|
fprintf(stderr, "couldn't add key %" B_PRId32 " - error: "
|
|
"%s\n", id, strerror(res));
|
|
exit(-1);
|
|
}
|
|
} else {
|
|
if (showKeys) {
|
|
printf("CatKey(\"%s\", \"%s\", \"%s\")\n", str.String(),
|
|
ctx.String(), cmt.String());
|
|
}
|
|
res = catalog->SetString(str.String(), str.String(),
|
|
ctx.String(), cmt.String());
|
|
if (res != B_OK) {
|
|
fprintf(stderr, "couldn't add key %s,%s,%s - error: %s\n",
|
|
str.String(), ctx.String(), cmt.String(),
|
|
strerror(res));
|
|
exit(-1);
|
|
}
|
|
}
|
|
} else if (showWarnings) {
|
|
const char *end = strchr(in, ';');
|
|
BString match;
|
|
if (end)
|
|
match.SetTo(start, end-start+1);
|
|
else {
|
|
// can't determine end of statement, we output next 40 chars
|
|
match.SetTo(start, 40);
|
|
}
|
|
fprintf(stderr, "Warning: couldn't resolve catalog-access:\n\t%s\n",
|
|
match.String());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
while ((++argv)[0]) {
|
|
if (argv[0][0] == '-' && argv[0][1] != '-') {
|
|
char *arg = argv[0] + 1;
|
|
char c;
|
|
while ((c = *arg++) != '\0') {
|
|
if (c == 'p')
|
|
showKeys = true;
|
|
else if (c == 'l')
|
|
catalogLang = (++argv)[0];
|
|
else if (c == 's')
|
|
catalogSig = (++argv)[0];
|
|
else if (c == 'v')
|
|
showSummary = true;
|
|
else if (c == 'w')
|
|
showWarnings = true;
|
|
else if (c == 'o') {
|
|
outputFile = (++argv)[0];
|
|
break;
|
|
}
|
|
else if (c == 'r') {
|
|
rxString = (++argv)[0];
|
|
break;
|
|
}
|
|
}
|
|
} else if (!strcmp(argv[0], "--help")) {
|
|
usage();
|
|
} else {
|
|
if (!inputFile)
|
|
inputFile = argv[0];
|
|
else
|
|
usage();
|
|
}
|
|
}
|
|
|
|
if (!outputFile.Length() && inputFile) {
|
|
// generate default output-file from input-file by replacing
|
|
// the extension with '.catkeys':
|
|
outputFile = inputFile;
|
|
int32 dot = outputFile.FindLast('.');
|
|
if (dot >= B_OK)
|
|
outputFile.Truncate(dot);
|
|
outputFile << ".catkeys";
|
|
}
|
|
if (!inputFile || !catalogSig || !outputFile.Length() || !catalogLang)
|
|
usage();
|
|
|
|
BFile inFile;
|
|
status_t res = inFile.SetTo(inputFile, B_READ_ONLY);
|
|
if (res != B_OK) {
|
|
fprintf(stderr, "unable to open inputfile %s - error: %s\n", inputFile,
|
|
strerror(res));
|
|
exit(-1);
|
|
}
|
|
|
|
off_t sz;
|
|
inFile.GetSize(&sz);
|
|
if (sz > 0) {
|
|
BString inputStr;
|
|
char *buf = inputStr.LockBuffer(sz);
|
|
off_t rsz = inFile.Read(buf, sz);
|
|
if (rsz < sz) {
|
|
fprintf(stderr, "couldn't read %" B_PRId64 " bytes from %s (got "
|
|
"only %" B_PRId64 ")\n", sz, inputFile, rsz);
|
|
exit(-1);
|
|
}
|
|
inputStr.UnlockBuffer(rsz);
|
|
catalog = new EditableCatalog("plaintext", catalogSig, catalogLang);
|
|
collectAllCatalogKeys(inputStr);
|
|
res = catalog->WriteToFile(outputFile.String());
|
|
if (res != B_OK) {
|
|
fprintf(stderr, "couldn't write catalog to %s - error: %s\n",
|
|
outputFile.String(), strerror(res));
|
|
exit(-1);
|
|
}
|
|
if (showSummary) {
|
|
int32 count = catalog->CountItems();
|
|
if (count)
|
|
fprintf(stderr, "%" B_PRId32 " key%s found and written to %s\n",
|
|
count, (count==1 ? "": "s"), outputFile.String());
|
|
else
|
|
fprintf(stderr, "no keys found\n");
|
|
}
|
|
delete catalog;
|
|
}
|
|
|
|
// BEntry inEntry(inputFile);
|
|
// inEntry.Remove();
|
|
|
|
return res;
|
|
}
|