mirror of https://gitlab.com/rnger/amath
876 lines
25 KiB
C
876 lines
25 KiB
C
|
/*
|
|||
|
* $Id$
|
|||
|
*
|
|||
|
* Copyright (C) 1993-1999 Jochen Wiedmann and Marcin Orlowski
|
|||
|
* Copyright (C) 2002-2015 FlexCat Open Source Team
|
|||
|
*
|
|||
|
* 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.
|
|||
|
*
|
|||
|
* This program is distributed in the hope that it will be useful, but
|
|||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|||
|
* General Public License for more details.
|
|||
|
*
|
|||
|
* You should have received a copy of the GNU General Public License
|
|||
|
* along with this program; if not, write to the Free Software
|
|||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|||
|
*
|
|||
|
*/
|
|||
|
|
|||
|
#ifdef AMIGA
|
|||
|
#include <proto/locale.h> /* This is to get locale.library/IsAlpha() */
|
|||
|
#endif
|
|||
|
|
|||
|
#include <errno.h>
|
|||
|
#include <limits.h>
|
|||
|
#include <time.h>
|
|||
|
|
|||
|
#include "flexcat.h"
|
|||
|
#include "showfuncs.h"
|
|||
|
#include "readprefs.h"
|
|||
|
#include "globals.h"
|
|||
|
#include "utils.h"
|
|||
|
#include "createcat.h"
|
|||
|
|
|||
|
extern char *CatVersionString;
|
|||
|
extern char *CatLanguage;
|
|||
|
extern char *CatRcsId;
|
|||
|
extern char *CatName;
|
|||
|
extern int CodeSet;
|
|||
|
extern int CT_Scanned;
|
|||
|
|
|||
|
#define IS_NUMBER_OR_LETTER(c) (((c) >= '0' && (c) <= '9') || \
|
|||
|
((c) >= 'a' && (c) <= 'z') || \
|
|||
|
((c) >= 'A' && (c) <= 'Z'))
|
|||
|
|
|||
|
#if defined(__amigaos3__) || defined(__MORPHOS__) || defined(WIN32)
|
|||
|
char *strptime(const char *string, const char *fmt, struct tm *res);
|
|||
|
#endif
|
|||
|
|
|||
|
/// ScanCTFile
|
|||
|
|
|||
|
/* This function scans a PO-style format catalog description/translation file.
|
|||
|
|
|||
|
Inputs: pofile - name of the description/translation file to scan.
|
|||
|
Result: TRUE if successful, FALSE otherwise.
|
|||
|
*/
|
|||
|
int ScanPOFile(char *pofile)
|
|||
|
{
|
|||
|
FILE *fp;
|
|||
|
char *newline, *line;
|
|||
|
int Result = TRUE;
|
|||
|
int CodeSet_checked = FALSE;
|
|||
|
int revision_found = FALSE;
|
|||
|
int inHeader = TRUE;
|
|||
|
int NextID = 0;
|
|||
|
const char *PoSrcCharset = "utf-8";
|
|||
|
const char *CatDstCharset = "iso-8859-1";
|
|||
|
char CatVersionDate[255] = "";
|
|||
|
char CatProjectName[255] = "";
|
|||
|
struct CatString *cs = NULL;
|
|||
|
struct CatString **csptr = &FirstCatString;
|
|||
|
int inMsgID = FALSE;
|
|||
|
int inMsgSTR = FALSE;
|
|||
|
|
|||
|
ScanFile = pofile;
|
|||
|
ScanLine = 0;
|
|||
|
if((fp = fopen(pofile, "r")) == NULL)
|
|||
|
ShowErrorQuick(MSG_ERR_NOCATALOGTRANSLATION, pofile);
|
|||
|
|
|||
|
if(!NoBufferedIO)
|
|||
|
setvbuf(fp, NULL, _IOFBF, buffer_size);
|
|||
|
|
|||
|
while(!feof(fp) && (line = newline = ReadLine(fp, TRUE)) != NULL)
|
|||
|
{
|
|||
|
if(inHeader == TRUE)
|
|||
|
{
|
|||
|
if(*line == '\0')
|
|||
|
{
|
|||
|
inHeader = FALSE;
|
|||
|
|
|||
|
// we found the end of the header so lets check if we have all
|
|||
|
// we require to continue
|
|||
|
if(CatVersion > 0 && CatVersionDate[0] != '\0' && CatProjectName[0] != '\0' &&
|
|||
|
CatVersionString == NULL)
|
|||
|
{
|
|||
|
char buf[255];
|
|||
|
|
|||
|
// warn about missing revision information
|
|||
|
if(CatRevision == 0)
|
|||
|
ShowWarn(MSG_ERR_NO_CAT_REVISION);
|
|||
|
|
|||
|
if(strstr(CatProjectName, ".catalog") != NULL)
|
|||
|
snprintf(buf, sizeof(buf), "$VER: %s %d.%d (%s)", CatProjectName, CatVersion, CatRevision, CatVersionDate);
|
|||
|
else
|
|||
|
snprintf(buf, sizeof(buf), "$VER: %s.catalog %d.%d (%s)", CatProjectName, CatVersion, CatRevision, CatVersionDate);
|
|||
|
CatVersionString = AllocString(buf);
|
|||
|
}
|
|||
|
}
|
|||
|
else switch(*line)
|
|||
|
{
|
|||
|
case '#':
|
|||
|
{
|
|||
|
// comment lines start with #
|
|||
|
// but they may contain some valueable information for catalog
|
|||
|
// file creation. So lets parse these lines as well
|
|||
|
while(*line == '#' || *line == ' ' || *line == '\t')
|
|||
|
++line;
|
|||
|
|
|||
|
if(Strnicmp(line, "version", 7) == 0)
|
|||
|
{
|
|||
|
line += 8;
|
|||
|
OverSpace(&line);
|
|||
|
CatVersion = strtol(line, &line, 0);
|
|||
|
}
|
|||
|
else if(Strnicmp(line, "revision", 8) == 0)
|
|||
|
{
|
|||
|
line += 9;
|
|||
|
OverSpace(&line);
|
|||
|
CatRevision = strtol(line, &line, 0);
|
|||
|
revision_found = TRUE;
|
|||
|
}
|
|||
|
else if(revision_found == FALSE &&
|
|||
|
Strnicmp(line, "$Id: ", 5) == 0)
|
|||
|
{
|
|||
|
char *p;
|
|||
|
|
|||
|
line += 6;
|
|||
|
p = line;
|
|||
|
|
|||
|
// search second space
|
|||
|
p = strchr(p, ' ');
|
|||
|
if(p != NULL)
|
|||
|
{
|
|||
|
p++;
|
|||
|
CatRevision = strtol(p, &p, 0);
|
|||
|
}
|
|||
|
}
|
|||
|
else if(revision_found == FALSE &&
|
|||
|
Strnicmp(line, "$Revision: ", 11) == 0)
|
|||
|
{
|
|||
|
line += 12;
|
|||
|
CatRevision = strtol(line, &line, 0);
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case '"':
|
|||
|
{
|
|||
|
if(Strnicmp(line, "\"Language: ", 11) == 0)
|
|||
|
{
|
|||
|
char *p;
|
|||
|
const char *language = NULL;
|
|||
|
|
|||
|
if(CatLanguage)
|
|||
|
ShowError(MSG_ERR_DOUBLECTLANGUAGE);
|
|||
|
|
|||
|
line += 11;
|
|||
|
p = strchr(line, '\\');
|
|||
|
if(p != NULL)
|
|||
|
*p = '\0';
|
|||
|
|
|||
|
if(Stricmp(line, "bs") == 0) // bosnian
|
|||
|
{
|
|||
|
language = "bosanski";
|
|||
|
CatDstCharset = "iso-8859-2";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "ca") == 0) // catalan
|
|||
|
{
|
|||
|
language = "catal<EFBFBD>";
|
|||
|
CatDstCharset = "iso-8859-15";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "hr") == 0) // croatian
|
|||
|
{
|
|||
|
language = "hrvatski";
|
|||
|
CatDstCharset = "iso-8859-16";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "cs") == 0) // czech
|
|||
|
{
|
|||
|
language = "czech";
|
|||
|
CatDstCharset = "iso-8859-2";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "da") == 0) // danish
|
|||
|
{
|
|||
|
language = "dansk";
|
|||
|
CatDstCharset = "iso-8859-15";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "nl") == 0) // dutch
|
|||
|
{
|
|||
|
language = "nederlands";
|
|||
|
CatDstCharset = "iso-8859-15";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "en_GB") == 0) // english-british
|
|||
|
language = "english-british";
|
|||
|
else if(Stricmp(line, "fi") == 0) // finnish
|
|||
|
{
|
|||
|
language = "suomi";
|
|||
|
CatDstCharset = "iso-8859-15";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "fr") == 0) // french
|
|||
|
{
|
|||
|
language = "fran<EFBFBD>ais";
|
|||
|
CatDstCharset = "iso-8859-15";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "de") == 0) // german
|
|||
|
{
|
|||
|
language = "deutsch";
|
|||
|
CatDstCharset = "iso-8859-15";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "el") == 0) // greek
|
|||
|
{
|
|||
|
language = "greek";
|
|||
|
CatDstCharset = "iso-8859-7";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "hu") == 0) // hungarian
|
|||
|
{
|
|||
|
language = "magyar";
|
|||
|
CatDstCharset = "iso-8859-16";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "it") == 0) // italian
|
|||
|
{
|
|||
|
language = "italiano";
|
|||
|
CatDstCharset = "iso-8859-15";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "ja") == 0) // japanese
|
|||
|
{
|
|||
|
language = "nihongo";
|
|||
|
CatDstCharset = "euc-jp";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "ko") == 0) // korean
|
|||
|
{
|
|||
|
language = "hangul";
|
|||
|
CatDstCharset = "euc-kr";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "no") == 0) // norwegian
|
|||
|
{
|
|||
|
language = "norsk";
|
|||
|
CatDstCharset = "iso-8859-15";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "fa") == 0) // persian
|
|||
|
{
|
|||
|
language = "farsi";
|
|||
|
CatDstCharset = "utf-8";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "pl") == 0) // polish
|
|||
|
{
|
|||
|
language = "polski";
|
|||
|
CatDstCharset = "iso-8859-2";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "pt") == 0) // portuguese
|
|||
|
language = "portugu<EFBFBD>s";
|
|||
|
else if(Stricmp(line, "pt_BR") == 0) // portuguese-brazil
|
|||
|
language = "portugu<EFBFBD>s-brasil";
|
|||
|
else if(Stricmp(line, "ru") == 0) // russian
|
|||
|
{
|
|||
|
language = "russian";
|
|||
|
#if defined(AMIGA)
|
|||
|
CatDstCharset = "Amiga-1251";
|
|||
|
#else
|
|||
|
CatDstCharset = "windows-1251"; // iconv doesn't know anything about Amiga-1251 :(
|
|||
|
#endif
|
|||
|
}
|
|||
|
else if(Stricmp(line, "sr") == 0) // serbian
|
|||
|
{
|
|||
|
language = "srpski";
|
|||
|
CatDstCharset = "iso-8859-16";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "sk") == 0) // slovakian
|
|||
|
{
|
|||
|
language = "slovak";
|
|||
|
CatDstCharset = "iso-8859-2";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "sl") == 0) // slovenian
|
|||
|
{
|
|||
|
language = "slovensko";
|
|||
|
CatDstCharset = "iso-8859-2";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "es") == 0) // spanish
|
|||
|
{
|
|||
|
language = "espa<EFBFBD>ol";
|
|||
|
CatDstCharset = "iso-8859-15";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "sv") == 0) // swedish
|
|||
|
{
|
|||
|
language = "svenska";
|
|||
|
CatDstCharset = "iso-8859-15";
|
|||
|
}
|
|||
|
else if(Stricmp(line, "tr") == 0) // turkish
|
|||
|
{
|
|||
|
language = "t<EFBFBD>rk<EFBFBD>e";
|
|||
|
CatDstCharset = "iso-8859-9";
|
|||
|
}
|
|||
|
|
|||
|
if(language != NULL)
|
|||
|
CatLanguage = AddCatalogChunk(strdup("LANG"), language);
|
|||
|
}
|
|||
|
else if(Strnicmp(line, "\"Language-Team: ", 16) == 0)
|
|||
|
{
|
|||
|
char *p;
|
|||
|
|
|||
|
line += 16;
|
|||
|
p = strchr(line, '\\');
|
|||
|
if(p != NULL)
|
|||
|
*p = '\0';
|
|||
|
|
|||
|
AddCatalogChunk(strdup("AUTH"), line);
|
|||
|
}
|
|||
|
else if(CodeSet_checked == FALSE &&
|
|||
|
Strnicmp(line, "\"Content-Type: ", 15) == 0)
|
|||
|
{
|
|||
|
char *p;
|
|||
|
|
|||
|
line += 16;
|
|||
|
p = strstr(line, "charset=");
|
|||
|
if(p != NULL)
|
|||
|
{
|
|||
|
char *q;
|
|||
|
|
|||
|
p += 8;
|
|||
|
|
|||
|
q = strchr(p, '\\');
|
|||
|
if(q != NULL)
|
|||
|
*q = '\0';
|
|||
|
|
|||
|
PoSrcCharset = strdup(p);
|
|||
|
}
|
|||
|
|
|||
|
CodeSet_checked = TRUE;
|
|||
|
}
|
|||
|
else if(Strnicmp(line, "\"PO-Revision-Date: ", 19) == 0)
|
|||
|
{
|
|||
|
struct tm tm;
|
|||
|
|
|||
|
line += 19;
|
|||
|
memset(&tm, 0, sizeof(tm));
|
|||
|
strptime(line, "%Y-%m-%d", &tm);
|
|||
|
strftime(CatVersionDate, sizeof(CatVersionDate), "%d.%m.%Y", &tm);
|
|||
|
}
|
|||
|
else if(Strnicmp(line, "\"Catalog-Name: ", 15) == 0)
|
|||
|
{
|
|||
|
char *p;
|
|||
|
|
|||
|
line += 15;
|
|||
|
p = strchr(line, '\\');
|
|||
|
if(p != NULL)
|
|||
|
*p = '\0';
|
|||
|
|
|||
|
strcpy(CatProjectName, line);
|
|||
|
}
|
|||
|
else if(Strnicmp(line, "\"Project-Id-Version: ", 21) == 0 && CatProjectName[0] == '\0')
|
|||
|
{
|
|||
|
// fall back to the project ID as catalog name if it is not yet defined
|
|||
|
char *p;
|
|||
|
|
|||
|
line += 21;
|
|||
|
p = strchr(line, '\\');
|
|||
|
if(p != NULL)
|
|||
|
*p = '\0';
|
|||
|
|
|||
|
strcpy(CatProjectName, line);
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// check if we found a line starting with "msgctxt" as that signals us
|
|||
|
// a new catalog string should be added
|
|||
|
if(Strnicmp(line, "msgctxt \"", 9) == 0)
|
|||
|
{
|
|||
|
char *idstr;
|
|||
|
|
|||
|
// we found a new 'msgctxt' lets clear cs
|
|||
|
cs = NULL;
|
|||
|
inMsgID = FALSE;
|
|||
|
inMsgSTR = FALSE;
|
|||
|
|
|||
|
line += 9;
|
|||
|
|
|||
|
/* Check for blanks at the start of line. */
|
|||
|
if(*line == ' ' || *line == '\t')
|
|||
|
{
|
|||
|
ShowError(MSG_ERR_UNEXPECTEDBLANKS);
|
|||
|
OverSpace(&line);
|
|||
|
}
|
|||
|
|
|||
|
idstr = line;
|
|||
|
while(IS_NUMBER_OR_LETTER(*line) || *line == '_')
|
|||
|
++line;
|
|||
|
|
|||
|
if(idstr == line)
|
|||
|
{
|
|||
|
ShowError(MSG_ERR_NOIDENTIFIER);
|
|||
|
Result = FALSE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
int found;
|
|||
|
|
|||
|
if((cs = malloc(sizeof(*cs))) == NULL)
|
|||
|
MemError();
|
|||
|
|
|||
|
// search for the next catstring ID in case the ID
|
|||
|
// specifier is missing "(//)" in the msgctxt
|
|||
|
do
|
|||
|
{
|
|||
|
struct CatString *scs;
|
|||
|
|
|||
|
found = TRUE;
|
|||
|
for(scs = FirstCatString; scs != NULL; scs = scs->Next)
|
|||
|
{
|
|||
|
if(scs->ID == NextID)
|
|||
|
{
|
|||
|
found = FALSE;
|
|||
|
++NextID;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
while(found == FALSE);
|
|||
|
|
|||
|
cs->Next = NULL;
|
|||
|
cs->ID = NextID;
|
|||
|
cs->MinLen = 0;
|
|||
|
cs->MaxLen = -1;
|
|||
|
cs->CD_Str = (char *)"";
|
|||
|
cs->CT_Str = NULL;
|
|||
|
cs->NotInCT = TRUE;
|
|||
|
cs->POformat = TRUE;
|
|||
|
|
|||
|
if((cs->ID_Str = malloc((line - idstr) + 1)) == NULL)
|
|||
|
MemError();
|
|||
|
|
|||
|
strncpy(cs->ID_Str, idstr, line - idstr);
|
|||
|
cs->ID_Str[line - idstr] = '\0';
|
|||
|
OverSpace(&line);
|
|||
|
|
|||
|
/* Check if next char in line is '('? (//) */
|
|||
|
if(*line != '(')
|
|||
|
{
|
|||
|
ShowError(MSG_ERR_NO_LEADING_BRACKET, cs->ID_Str);
|
|||
|
Result = FALSE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
struct CatString *scs;
|
|||
|
|
|||
|
++line;
|
|||
|
OverSpace(&line);
|
|||
|
|
|||
|
/* Check for default config of line (//) */
|
|||
|
if(*line != '/')
|
|||
|
{
|
|||
|
if(*line == '+')
|
|||
|
NextID = cs->ID = NextID + strtol(line, &line, 0);
|
|||
|
else if(*line == '$')
|
|||
|
{
|
|||
|
line++;
|
|||
|
cs->ID = NextID = strtol(line, &line, 16);
|
|||
|
}
|
|||
|
else
|
|||
|
cs->ID = NextID = strtol(line, &line, 0);
|
|||
|
|
|||
|
OverSpace(&line);
|
|||
|
}
|
|||
|
|
|||
|
/* Check for already used identifier. */
|
|||
|
for(scs = FirstCatString; scs != NULL; scs = scs->Next)
|
|||
|
{
|
|||
|
if(scs->ID == cs->ID)
|
|||
|
{
|
|||
|
ShowError(MSG_ERR_DOUBLE_ID, cs->ID_Str);
|
|||
|
Result = FALSE;
|
|||
|
}
|
|||
|
if(strcmp(cs->ID_Str, scs->ID_Str) == 0)
|
|||
|
{
|
|||
|
ShowError(MSG_ERR_DOUBLE_IDENTIFIER, cs->ID_Str);
|
|||
|
Result = FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Check for min/len values (//) */
|
|||
|
if(*line != '/')
|
|||
|
{
|
|||
|
ShowWarn(MSG_ERR_NO_MIN_LEN, cs->ID_Str);
|
|||
|
Result = FALSE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
++line;
|
|||
|
OverSpace(&line);
|
|||
|
if(*line != '/')
|
|||
|
{
|
|||
|
cs->MinLen = strtol(line, &line, 0);
|
|||
|
OverSpace(&line);
|
|||
|
}
|
|||
|
if(*line != '/')
|
|||
|
{
|
|||
|
ShowWarn(MSG_ERR_NO_MAX_LEN, cs->ID_Str);
|
|||
|
Result = FALSE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
++line;
|
|||
|
OverSpace(&line);
|
|||
|
if(*line != ')')
|
|||
|
{
|
|||
|
cs->MaxLen = strtol(line, &line, 0);
|
|||
|
OverSpace(&line);
|
|||
|
}
|
|||
|
if(*line != ')')
|
|||
|
{
|
|||
|
ShowError(MSG_ERR_NO_TRAILING_BRACKET, cs->ID_Str);
|
|||
|
Result = FALSE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
++line;
|
|||
|
OverSpace(&line);
|
|||
|
if(*line && *line != '\"')
|
|||
|
ShowError(MSG_ERR_EXTRA_CHARACTERS_ID, cs->ID_Str);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//printf("ID_Str: '%s' (%d)\n", cs->ID_Str, cs->ID);
|
|||
|
|
|||
|
cs->Nr = NumStrings;
|
|||
|
cs->LenBytes = 0;
|
|||
|
*csptr = cs;
|
|||
|
csptr = &cs->Next;
|
|||
|
++NumStrings;
|
|||
|
}
|
|||
|
}
|
|||
|
else if(cs != NULL)
|
|||
|
{
|
|||
|
char *p;
|
|||
|
|
|||
|
// if the user want to force a certain output (destination)
|
|||
|
// codeset we set it here.
|
|||
|
if(DestCodeset[0] != '\0')
|
|||
|
CatDstCharset = DestCodeset;
|
|||
|
|
|||
|
// Make sure double backslashes end up in a single backslash.
|
|||
|
// We catch any double backslash followed by a zero character,
|
|||
|
// which covers strings like "\\0" and "\\033" or "\\33" as these are
|
|||
|
// common strings in MUI applications.
|
|||
|
while((p = strstr(line, "\\\\0")) != NULL || (p = strstr(line, "\\\\33")) != NULL)
|
|||
|
memmove(p, p+1, strlen(p));
|
|||
|
|
|||
|
// unquote the string
|
|||
|
if(line[strlen(line)-1] == '"')
|
|||
|
line[strlen(line)-1] = '\0';
|
|||
|
|
|||
|
if(Strnicmp(line, "msgid \"", 7) == 0)
|
|||
|
{
|
|||
|
line += 7;
|
|||
|
|
|||
|
// if the string starts with <EMPTY> we out to remove
|
|||
|
// the rest of the string!
|
|||
|
if(strncmp(line, "<EMPTY>", 7) == 0)
|
|||
|
*line = '\0';
|
|||
|
|
|||
|
if(strlen(line) > 0)
|
|||
|
{
|
|||
|
if((cs->CD_Str = ConvertString(line, PoSrcCharset, CatDstCharset)) == NULL)
|
|||
|
ShowWarn(MSG_ERR_CONVERSION_FAILED, cs->ID_Str);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
cs->CD_Str = malloc(1);
|
|||
|
cs->CD_Str[0] = '\0';
|
|||
|
}
|
|||
|
|
|||
|
//printf("CD_Str: '%s' '%s'\n", cs->CD_Str, line);
|
|||
|
|
|||
|
inMsgID = TRUE;
|
|||
|
inMsgSTR = FALSE;
|
|||
|
}
|
|||
|
else if(Strnicmp(line, "msgstr \"", 8) == 0)
|
|||
|
{
|
|||
|
line += 8;
|
|||
|
|
|||
|
// don't search for "<EMPTY>" here, this will be done later
|
|||
|
// during all other checks. If the string would be erased here
|
|||
|
// we would be no longer able to tell it apart from really
|
|||
|
// missing translations.
|
|||
|
if(strlen(line) > 0)
|
|||
|
{
|
|||
|
if((cs->CT_Str = ConvertString(line, PoSrcCharset, CatDstCharset)) == NULL)
|
|||
|
ShowWarn(MSG_ERR_CONVERSION_FAILED, cs->ID_Str);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
cs->CT_Str = malloc(1);
|
|||
|
cs->CT_Str[0] = '\0';
|
|||
|
}
|
|||
|
|
|||
|
cs->NotInCT = FALSE;
|
|||
|
|
|||
|
//printf("CT_Str: '%s'\n", cs->CT_Str);
|
|||
|
|
|||
|
inMsgSTR = TRUE;
|
|||
|
inMsgID = FALSE;
|
|||
|
}
|
|||
|
else if(*line == '"') // line starts with "
|
|||
|
{
|
|||
|
line++;
|
|||
|
|
|||
|
if(inMsgID == TRUE)
|
|||
|
{
|
|||
|
char *t;
|
|||
|
|
|||
|
if((t = ConvertString(line, PoSrcCharset, CatDstCharset)) == NULL)
|
|||
|
ShowWarn(MSG_ERR_CONVERSION_FAILED, cs->ID_Str);
|
|||
|
else
|
|||
|
cs->CD_Str = AddString(cs->CD_Str, t);
|
|||
|
|
|||
|
//printf("CD_Str2: '%s' '%s'\n", cs->CD_Str, line);
|
|||
|
|
|||
|
free(t);
|
|||
|
}
|
|||
|
else if(inMsgSTR == TRUE)
|
|||
|
{
|
|||
|
char *t;
|
|||
|
|
|||
|
if((t = ConvertString(line, PoSrcCharset, CatDstCharset)) == NULL)
|
|||
|
ShowWarn(MSG_ERR_CONVERSION_FAILED, cs->ID_Str);
|
|||
|
else
|
|||
|
cs->CT_Str = AddString(cs->CT_Str, t);
|
|||
|
|
|||
|
//printf("CT_Str2: '%s' '%s'\n", cs->CT_Str, line);
|
|||
|
|
|||
|
free(t);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
printf("CatVersion: %d.%d\n", CatVersion, CatRevision);
|
|||
|
printf("CatVersionDate: '%s'\n", CatVersionDate);
|
|||
|
printf("CatVersionString: '%s'\n", CatVersionString);
|
|||
|
printf("CatLanguage: '%s'\n", CatLanguage);
|
|||
|
printf("PoSrcCharset: '%s'\n", PoSrcCharset);
|
|||
|
printf("CatDstCharset: '%s'\n", CatDstCharset);
|
|||
|
*/
|
|||
|
|
|||
|
if(!CodeSet_checked)
|
|||
|
ShowErrorQuick(MSG_ERR_NOCTCODESET);
|
|||
|
|
|||
|
if(!(CatVersionString || (CatRcsId && CatName)))
|
|||
|
ShowErrorQuick(MSG_ERR_NOCTVERSION);
|
|||
|
|
|||
|
// lets translate CatDstCharset to CodeSet number
|
|||
|
if(Stricmp(CatDstCharset, "iso-8859-1") == 0)
|
|||
|
CodeSet = 4;
|
|||
|
else if(Stricmp(CatDstCharset, "iso-8859-2") == 0)
|
|||
|
CodeSet = 5;
|
|||
|
else if(Stricmp(CatDstCharset, "iso-8859-7") == 0)
|
|||
|
CodeSet = 10;
|
|||
|
else if(Stricmp(CatDstCharset, "iso-8859-9") == 0)
|
|||
|
CodeSet = 12;
|
|||
|
else if(Stricmp(CatDstCharset, "utf-8") == 0 || Stricmp(CatDstCharset, "utf8") == 0)
|
|||
|
CodeSet = 106;
|
|||
|
else if(Stricmp(CatDstCharset, "iso-8859-15") == 0)
|
|||
|
CodeSet = 111;
|
|||
|
else if(Stricmp(CatDstCharset, "iso-8859-16") == 0)
|
|||
|
CodeSet = 112;
|
|||
|
else if(Stricmp(CatDstCharset, "amiga-1251") == 0 || Stricmp(CatDstCharset, "windows-1251") == 0)
|
|||
|
CodeSet = 2104;
|
|||
|
else
|
|||
|
CodeSet = 0;
|
|||
|
|
|||
|
// check consistenty of translations found
|
|||
|
for(cs = FirstCatString; cs != NULL; cs = cs->Next)
|
|||
|
{
|
|||
|
if(cs->CT_Str == NULL)
|
|||
|
ShowWarnQuick(MSG_ERR_MISSINGTRANSLATION, cs->ID_Str);
|
|||
|
else
|
|||
|
{
|
|||
|
size_t reallen;
|
|||
|
size_t reallen_utf8;
|
|||
|
size_t cd_len;
|
|||
|
|
|||
|
// get string length for both ASCII and UTF8 encoding
|
|||
|
// the length check must be done against the UTF8 length,
|
|||
|
// which might be less than the ASCII length due to certain
|
|||
|
// UTF8 characters which are encoded with up to 3 ASCII
|
|||
|
// characters
|
|||
|
reallen = strlen(cs->CT_Str);
|
|||
|
reallen_utf8 = utf8_strlen(cs->CT_Str);
|
|||
|
cd_len = strlen(cs->CD_Str);
|
|||
|
|
|||
|
// check for empty translations
|
|||
|
if(cd_len > 0)
|
|||
|
{
|
|||
|
if(reallen == 0)
|
|||
|
{
|
|||
|
// for .po files empty strings are really missing translations
|
|||
|
ShowWarnQuick(MSG_ERR_MISSINGTRANSLATION, cs->ID_Str);
|
|||
|
|
|||
|
// now remove the cs from the list
|
|||
|
cs->NotInCT = TRUE;
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// check for intentionally empty translations
|
|||
|
if(strncmp(cs->CT_Str, "<EMPTY>", 7) == 0)
|
|||
|
cs->CT_Str[0] = '\0';
|
|||
|
|
|||
|
if(cs->MinLen > 0 && reallen_utf8 < (size_t)cs->MinLen)
|
|||
|
ShowWarnQuick(MSG_ERR_STRING_TOO_SHORT, cs->ID_Str);
|
|||
|
|
|||
|
if(cs->MaxLen > 0 && reallen_utf8 > (size_t)cs->MaxLen)
|
|||
|
ShowWarnQuick(MSG_ERR_STRING_TOO_LONG, cs->ID_Str);
|
|||
|
|
|||
|
// check for trailing ellipsis
|
|||
|
if(reallen >= 3 && cd_len >= 3)
|
|||
|
{
|
|||
|
if(strcmp(&cs->CD_Str[cd_len - 3], "...") == 0 &&
|
|||
|
strcmp(&cs->CT_Str[reallen - 3], "...") != 0)
|
|||
|
{
|
|||
|
ShowWarnQuick(MSG_ERR_TRAILING_ELLIPSIS, cs->ID_Str);
|
|||
|
}
|
|||
|
|
|||
|
if(strcmp(&cs->CD_Str[cd_len - 3], "...") != 0 &&
|
|||
|
strcmp(&cs->CT_Str[reallen - 3], "...") == 0)
|
|||
|
{
|
|||
|
ShowWarnQuick(MSG_ERR_NO_TRAILING_ELLIPSIS, cs->ID_Str);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// check for trailing spaces
|
|||
|
if(reallen >= 1 && cd_len >= 1)
|
|||
|
{
|
|||
|
if(strcmp(&cs->CD_Str[cd_len - 1], " ") == 0 &&
|
|||
|
strcmp(&cs->CT_Str[reallen - 1], " ") != 0)
|
|||
|
|
|||
|
{
|
|||
|
ShowWarnQuick(MSG_ERR_TRAILING_BLANKS, cs->ID_Str);
|
|||
|
}
|
|||
|
|
|||
|
if(strcmp(&cs->CD_Str[cd_len - 1], " ") != 0 &&
|
|||
|
strcmp(&cs->CT_Str[reallen - 1], " ") == 0)
|
|||
|
|
|||
|
{
|
|||
|
ShowWarnQuick(MSG_ERR_NO_TRAILING_BLANKS, cs->ID_Str);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// check for matching placeholders
|
|||
|
if(reallen >= 1 && cd_len >= 1)
|
|||
|
{
|
|||
|
char *cdP = cs->CD_Str;
|
|||
|
char *ctP = cs->CT_Str;
|
|||
|
|
|||
|
do
|
|||
|
{
|
|||
|
cdP = strchr(cdP, '%');
|
|||
|
ctP = strchr(ctP, '%');
|
|||
|
|
|||
|
if(cdP == NULL && ctP == NULL)
|
|||
|
{
|
|||
|
// no more placeholders, bail out
|
|||
|
break;
|
|||
|
}
|
|||
|
else if(cdP != NULL && ctP != NULL)
|
|||
|
{
|
|||
|
// skip the '%' sign
|
|||
|
cdP++;
|
|||
|
ctP++;
|
|||
|
|
|||
|
// check the placeholder only if the '%' is followed by an
|
|||
|
// alpha-numerical character or another percent sign
|
|||
|
if(IS_NUMBER_OR_LETTER(*cdP) || *cdP == '%')
|
|||
|
{
|
|||
|
if(*cdP != *ctP)
|
|||
|
{
|
|||
|
ShowWarnQuick(MSG_ERR_MISMATCHING_PLACEHOLDERS, cs->ID_Str);
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
// skip the second '%' sign
|
|||
|
if(*cdP == '%')
|
|||
|
cdP++;
|
|||
|
if(*ctP == '%')
|
|||
|
ctP++;
|
|||
|
}
|
|||
|
else if(IS_NUMBER_OR_LETTER(*ctP) || *ctP == '%')
|
|||
|
{
|
|||
|
// the translation uses a placeholder while the description
|
|||
|
// uses none.
|
|||
|
ShowWarnQuick(MSG_ERR_EXCESSIVE_PLACEHOLDERS, cs->ID_Str);
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
else if(cdP != NULL && ctP == NULL)
|
|||
|
{
|
|||
|
// skip the '%' sign
|
|||
|
cdP++;
|
|||
|
|
|||
|
// check if really a placeholder follows or just another percent sign
|
|||
|
// the original string is allowed to contain more single percent signs than the translated string
|
|||
|
if(IS_NUMBER_OR_LETTER(*cdP) || *cdP == '%')
|
|||
|
{
|
|||
|
// the description uses at least one more placeholder than the translation
|
|||
|
ShowWarnQuick(MSG_ERR_MISSING_PLACEHOLDERS, cs->ID_Str);
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
else if(cdP == NULL && ctP != NULL)
|
|||
|
{
|
|||
|
// skip the '%' sign
|
|||
|
ctP++;
|
|||
|
|
|||
|
// check if really a placeholder follows or just another percent sign
|
|||
|
// the translated string is allowed to contain more single percent signs than the original string
|
|||
|
if(IS_NUMBER_OR_LETTER(*ctP) || *ctP == '%')
|
|||
|
{
|
|||
|
// the translation uses at least one more placeholder than the description
|
|||
|
ShowWarnQuick(MSG_ERR_EXCESSIVE_PLACEHOLDERS, cs->ID_Str);
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
while(TRUE);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if(line != NULL)
|
|||
|
free(line);
|
|||
|
|
|||
|
fclose(fp);
|
|||
|
|
|||
|
if(WarnCTGaps)
|
|||
|
{
|
|||
|
for(cs = FirstCatString; cs != NULL; cs = cs->Next)
|
|||
|
{
|
|||
|
if(cs->CT_Str == NULL)
|
|||
|
{
|
|||
|
ShowWarn(MSG_ERR_CTGAP, cs->ID_Str);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if(Result)
|
|||
|
CT_Scanned = TRUE;
|
|||
|
|
|||
|
return(Result);
|
|||
|
}
|
|||
|
|
|||
|
///
|
|||
|
|