4 * Copyright (C) 1993-1999 by Jochen Wiedmann and Marcin Orlowski
5 * Copyright (C) 2002-2017 FlexCat Open Source Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or (at
10 * your option) any later version.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include <proto/locale.h> /* This is to get locale.library/IsAlpha() */
28 // #include <stdlib.h>
34 #include "showfuncs.h"
35 #include "readprefs.h"
38 #include "createcat.h"
40 char *CatVersionString
= NULL
; /* Version string of catalog translation (## version) */
41 char *CatLanguage
= NULL
; /* Language of catalog translation */
42 char *CatRcsId
= NULL
; /* RCS ID of catalog translation (## rcsid) */
43 char *CatName
= NULL
; /* Name of catalog translation */
44 unsigned long int CodeSet
= 0; /* Codeset of catalog translation */
45 int CT_Scanned
= FALSE
; /* If TRUE and we are going to write a new #?.ct file, then the
46 user is surely updating his own #?.ct file, so we should write
47 ***NEW*** wherever necessary. */
49 #define IS_NUMBER_OR_LETTER(c) (((c) >= '0' && (c) <= '9') || \
50 ((c) >= 'a' && (c) <= 'z') || \
51 ((c) >= 'A' && (c) <= 'Z'))
55 /* This scans a catalog translation file.
56 Inputs: ctfile - name of the translation file to scan.
57 Result: TRUE if successful, FALSE otherwise. */
59 int ScanCTFile(char *ctfile
)
62 char *newline
, *line
, *idstr
, *newidstr
, *newstr
;
63 struct CatString
*cs
= NULL
;
65 int CodeSet_checked
= 0;
70 if((fp
= fopen(ctfile
, "r")) == NULL
)
72 ShowErrorQuick(MSG_ERR_NOCATALOGTRANSLATION
, ctfile
);
76 setvbuf(fp
, NULL
, _IOFBF
, buffer_size
);
78 // initialize "line" ahead of the loop
79 // the loop will bail out early for empty files
82 while(!feof(fp
) && (line
= newline
= ReadLine(fp
, TRUE
)) != NULL
)
89 if(cs
&& Strnicmp(line
, Old_Msg_New
, (int)strlen(Old_Msg_New
)) == 0)
98 /* '#' in the first column of a line is the command introducer --
99 any number of # symbols, blank spaces and tabs afterwards are
100 skipped for compatibility with CatComp */
102 while(*line
== '#' || *line
== ' ' || *line
== '\t')
107 if(Strnicmp(line
, "version", 7) == 0)
109 if(CatVersionString
|| CatRcsId
|| CatName
)
111 ShowError(MSG_ERR_DOUBLECTVERSION
);
115 // perform a slightly obfuscated check for the version cookie to
116 // avoid having multiple cookies in the final binary
117 if(line
[0] == '$' && Strnicmp(&line
[1], "VER:", 4) == 0)
119 CatVersionString
= AllocString(line
);
123 ShowError(MSG_ERR_BADCTVERSION
);
126 else if(Strnicmp(line
, "codeset", 7) == 0)
132 ShowError(MSG_ERR_DOUBLECTCODESET
);
138 /* Missing argument for "## codeset" */
140 ShowError(MSG_ERR_BADCTCODESET
);
143 for(ptr
= line
; *ptr
; ptr
++)
144 if(!isdigit((int)*ptr
))
145 /* Non-digit char detected */
147 ShowError(MSG_ERR_BADCTCODESET
);
152 CodeSet
= strtoul(line
, &line
, 0);
154 if(errno
== ERANGE
&& CodeSet
== ULONG_MAX
)
156 ShowError(MSG_ERR_BADCTCODESET
);
162 else if(Strnicmp(line
, "language", 8) == 0)
168 ShowError(MSG_ERR_DOUBLECTLANGUAGE
);
172 CatLanguage
= AddCatalogChunk(strdup("LANG"), line
);
175 for(ptr
= CatLanguage
; *ptr
; ptr
++)
176 *ptr
= tolower((int)*ptr
);
178 else if(Strnicmp(line
, "chunk", 5) == 0)
185 line
+= sizeof(ULONG
);
188 AddCatalogChunk(ID
, AllocString(line
));
190 else if(Strnicmp(line
, "rcsid", 5) == 0)
192 if(CatVersionString
|| CatRcsId
)
194 ShowError(MSG_ERR_DOUBLECTVERSION
);
198 CatRcsId
= AllocString(line
);
200 else if(Strnicmp(line
, "name", 5) == 0)
202 if(CatVersionString
|| CatName
)
204 ShowError(MSG_ERR_DOUBLECTVERSION
);
208 CatName
= AllocString(line
);
212 ShowWarn(MSG_ERR_UNKNOWNCTCOMMAND
);
215 /* Stop looking for commands */
220 if(*line
== ' ' || *line
== '\t')
222 ShowError(MSG_ERR_UNEXPECTEDBLANKS
);
227 while(IS_NUMBER_OR_LETTER(*line
) || *line
== '_')
233 ShowError(MSG_ERR_NOIDENTIFIER
);
237 if((newidstr
= malloc(line
- idstr
+ 1)) == NULL
)
242 strncpy(newidstr
, idstr
, line
- idstr
);
243 newidstr
[line
- idstr
] = '\0';
248 ShowError(MSG_ERR_EXTRA_CHARACTERS_ID
, newidstr
);
251 if((newstr
= ReadLine(fp
, FALSE
)) != NULL
)
253 for(cs
= FirstCatString
; cs
!= NULL
; cs
= cs
->Next
)
255 if(strcmp(cs
->ID_Str
, newidstr
) == 0)
262 ShowWarn(MSG_ERR_UNKNOWNIDENTIFIER
, newidstr
);
271 ShowError(MSG_ERR_DOUBLE_IDENTIFIER
, cs
->ID_Str
);
275 cs
->CT_Str
= AllocString(newstr
);
278 /* Get string length */
280 reallen
= strlen(cs
->CT_Str
);
281 cd_len
= strlen(cs
->CD_Str
);
283 if(cs
->MinLen
> 0 && reallen
< (size_t)cs
->MinLen
)
285 ShowWarn(MSG_ERR_STRING_TOO_SHORT
, cs
->ID_Str
);
287 if(cs
->MaxLen
> 0 && reallen
> (size_t)cs
->MaxLen
)
289 ShowWarn(MSG_ERR_STRING_TOO_LONG
, cs
->ID_Str
);
292 // check for empty translations
293 if(cd_len
> 0 && reallen
== 0)
295 ShowWarn(MSG_ERR_EMPTYTRANSLATION
, cs
->ID_Str
);
298 /* Check for trailing ellipsis. */
299 if(reallen
>= 3 && cd_len
>= 3)
301 if(strcmp(&cs
->CD_Str
[cd_len
- 3], "...") == 0 &&
302 strcmp(&cs
->CT_Str
[reallen
- 3], "...") != 0)
304 ShowWarn(MSG_ERR_TRAILING_ELLIPSIS
, cs
->ID_Str
);
306 if(strcmp(&cs
->CD_Str
[cd_len
- 3], "...") != 0 &&
307 strcmp(&cs
->CT_Str
[reallen
- 3], "...") == 0)
309 ShowWarn(MSG_ERR_NO_TRAILING_ELLIPSIS
, cs
->ID_Str
);
314 /* Check for trailing spaces. */
315 if(reallen
>= 1 && cd_len
>= 1)
317 if(strcmp(&cs
->CD_Str
[cd_len
- 1], " ") == 0 &&
318 strcmp(&cs
->CT_Str
[reallen
- 1], " ") != 0)
321 ShowWarn(MSG_ERR_TRAILING_BLANKS
, cs
->ID_Str
);
323 if(strcmp(&cs
->CD_Str
[cd_len
- 1], " ") != 0 &&
324 strcmp(&cs
->CT_Str
[reallen
- 1], " ") == 0)
327 ShowWarn(MSG_ERR_NO_TRAILING_BLANKS
, cs
->ID_Str
);
331 /* Check for matching placeholders */
332 if(reallen
>= 1 && cd_len
>= 1)
334 char *cdP
= cs
->CD_Str
;
335 char *ctP
= cs
->CT_Str
;
339 cdP
= strchr(cdP
, '%');
340 ctP
= strchr(ctP
, '%');
342 if(cdP
== NULL
&& ctP
== NULL
)
344 // no more placeholders, bail out
347 else if(cdP
!= NULL
&& ctP
!= NULL
)
353 // check the placeholder only if the '%' is followed by an
354 // alpha-numerical character or another percent sign
355 if(IS_NUMBER_OR_LETTER(*cdP
) || *cdP
== '%')
359 ShowWarn(MSG_ERR_MISMATCHING_PLACEHOLDERS
, cs
->ID_Str
);
362 // skip the second '%' sign
368 else if(IS_NUMBER_OR_LETTER(*ctP
) || *ctP
== '%')
370 // the translation uses a placeholder while the description
372 ShowWarn(MSG_ERR_EXCESSIVE_PLACEHOLDERS
, cs
->ID_Str
);
376 else if(cdP
!= NULL
&& ctP
== NULL
)
381 // check if really a placeholder follows or just another percent sign
382 // the original string is allowed to contain more single percent signs than the translated string
383 if(IS_NUMBER_OR_LETTER(*cdP
) || *cdP
== '%')
385 // the description uses at least one more placeholder than the translation
386 ShowWarn(MSG_ERR_MISSING_PLACEHOLDERS
, cs
->ID_Str
);
390 else if(cdP
== NULL
&& ctP
!= NULL
)
395 // check if really a placeholder follows or just another percent sign
396 // the translated string is allowed to contain more single percent signs than the original string
397 if(IS_NUMBER_OR_LETTER(*ctP
) || *ctP
== '%')
399 // the translation uses at least one more placeholder than the description
400 ShowWarn(MSG_ERR_EXCESSIVE_PLACEHOLDERS
, cs
->ID_Str
);
413 ShowWarn(MSG_ERR_MISSINGSTRING
);
415 cs
->CT_Str
= (char *)"";
420 // forget the pointers as we just freed them and 'line' must not be freed again after the loop
427 ShowErrorQuick(MSG_ERR_NOCTCODESET
);
430 if(!(CatVersionString
|| (CatRcsId
&& CatName
)))
432 ShowErrorQuick(MSG_ERR_NOCTVERSION
);
435 // check if a translation exists for all identifiers
436 for(cs
= FirstCatString
; cs
!= NULL
; cs
= cs
->Next
)
438 if(cs
->CT_Str
== NULL
)
440 ShowWarnQuick(MSG_ERR_MISSINGTRANSLATION
, cs
->ID_Str
);
451 for(cs
= FirstCatString
; cs
!= NULL
; cs
= cs
->Next
)
453 if(cs
->CT_Str
== NULL
)
455 ShowWarn(MSG_ERR_CTGAP
, cs
->ID_Str
);