Merge-in fix for segfault from current github repository.
[AROS.git] / tools / flexcat / src / scanct.c
blob335f3e806fc6179488c6a52b00fb1e38e3ed7bb0
1 /*
2 * $Id$
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.
23 #ifdef AMIGA
24 #include <proto/locale.h> /* This is to get locale.library/IsAlpha() */
25 #endif
27 // #include <stdio.h>
28 // #include <stdlib.h>
29 #include <errno.h>
30 #include <limits.h>
33 #include "flexcat.h"
34 #include "showfuncs.h"
35 #include "readprefs.h"
36 #include "globals.h"
37 #include "utils.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'))
53 /// ScanCTFile
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)
61 FILE *fp;
62 char *newline, *line, *idstr, *newidstr, *newstr;
63 struct CatString *cs = NULL;
64 int Result = TRUE;
65 int CodeSet_checked = 0;
67 ScanFile = ctfile;
68 ScanLine = 0;
70 if((fp = fopen(ctfile, "r")) == NULL)
72 ShowErrorQuick(MSG_ERR_NOCATALOGTRANSLATION, ctfile);
75 if(!NoBufferedIO)
76 setvbuf(fp, NULL, _IOFBF, buffer_size);
78 // initialize "line" ahead of the loop
79 // the loop will bail out early for empty files
80 line = NULL;
81 newline = NULL;
82 while(!feof(fp) && (line = newline = ReadLine(fp, TRUE)) != NULL)
84 switch(*line)
86 case ';':
87 if(CopyNEWs == TRUE)
89 if(cs && Strnicmp(line, Old_Msg_New, (int)strlen(Old_Msg_New)) == 0)
91 cs->NotInCT = TRUE;
94 break;
96 case '#':
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')
104 ++line;
107 if(Strnicmp(line, "version", 7) == 0)
109 if(CatVersionString || CatRcsId || CatName)
111 ShowError(MSG_ERR_DOUBLECTVERSION);
113 line += 7;
114 OverSpace(&line);
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);
121 else
123 ShowError(MSG_ERR_BADCTVERSION);
126 else if(Strnicmp(line, "codeset", 7) == 0)
128 char *ptr;
130 if(CodeSet_checked)
132 ShowError(MSG_ERR_DOUBLECTCODESET);
134 line += 7;
135 OverSpace(&line);
137 if(!*line)
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);
150 errno = 0;
152 CodeSet = strtoul(line, &line, 0);
154 if(errno == ERANGE && CodeSet == ULONG_MAX)
156 ShowError(MSG_ERR_BADCTCODESET);
159 CodeSet_checked = 1;
160 // errno = 0;
162 else if(Strnicmp(line, "language", 8) == 0)
164 char *ptr;
166 if(CatLanguage)
168 ShowError(MSG_ERR_DOUBLECTLANGUAGE);
170 line += 8;
171 OverSpace(&line);
172 CatLanguage = AddCatalogChunk(strdup("LANG"), line);
174 if(LANGToLower)
175 for(ptr = CatLanguage; *ptr; ptr++)
176 *ptr = tolower((int)*ptr);
178 else if(Strnicmp(line, "chunk", 5) == 0)
180 char *ID;
182 line += 5;
183 OverSpace(&line);
184 ID = line;
185 line += sizeof(ULONG);
186 OverSpace(&line);
188 AddCatalogChunk(ID, AllocString(line));
190 else if(Strnicmp(line, "rcsid", 5) == 0)
192 if(CatVersionString || CatRcsId)
194 ShowError(MSG_ERR_DOUBLECTVERSION);
196 line += 5;
197 OverSpace(&line);
198 CatRcsId = AllocString(line);
200 else if(Strnicmp(line, "name", 5) == 0)
202 if(CatVersionString || CatName)
204 ShowError(MSG_ERR_DOUBLECTVERSION);
206 line += 4;
207 OverSpace(&line);
208 CatName = AllocString(line);
210 else
212 ShowWarn(MSG_ERR_UNKNOWNCTCOMMAND);
215 /* Stop looking for commands */
217 break;
219 default:
220 if(*line == ' ' || *line == '\t')
222 ShowError(MSG_ERR_UNEXPECTEDBLANKS);
223 OverSpace(&line);
225 idstr = line;
227 while(IS_NUMBER_OR_LETTER(*line) || *line == '_')
229 ++line;
231 if(idstr == line)
233 ShowError(MSG_ERR_NOIDENTIFIER);
234 break;
237 if((newidstr = malloc(line - idstr + 1)) == NULL)
239 MemError();
242 strncpy(newidstr, idstr, line - idstr);
243 newidstr[line - idstr] = '\0';
244 OverSpace(&line);
246 if(*line)
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)
257 break;
260 if(cs == NULL)
262 ShowWarn(MSG_ERR_UNKNOWNIDENTIFIER, newidstr);
264 else
266 size_t reallen;
267 size_t cd_len;
269 if(cs->CT_Str)
271 ShowError(MSG_ERR_DOUBLE_IDENTIFIER, cs->ID_Str);
272 Result = FALSE;
273 free(cs->CT_Str);
275 cs->CT_Str = AllocString(newstr);
276 cs->NotInCT = FALSE;
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
345 break;
347 else if(cdP != NULL && ctP != NULL)
349 // skip the '%' sign
350 cdP++;
351 ctP++;
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 == '%')
357 if(*cdP != *ctP)
359 ShowWarn(MSG_ERR_MISMATCHING_PLACEHOLDERS, cs->ID_Str);
360 break;
362 // skip the second '%' sign
363 if(*cdP == '%')
364 cdP++;
365 if(*ctP == '%')
366 ctP++;
368 else if(IS_NUMBER_OR_LETTER(*ctP) || *ctP == '%')
370 // the translation uses a placeholder while the description
371 // uses none.
372 ShowWarn(MSG_ERR_EXCESSIVE_PLACEHOLDERS, cs->ID_Str);
373 break;
376 else if(cdP != NULL && ctP == NULL)
378 // skip the '%' sign
379 cdP++;
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);
388 break;
390 else if(cdP == NULL && ctP != NULL)
392 // skip the '%' sign
393 ctP++;
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);
402 break;
405 while(TRUE);
409 free(newstr);
411 else
413 ShowWarn(MSG_ERR_MISSINGSTRING);
414 if(cs)
415 cs->CT_Str = (char *)"";
417 free(newidstr);
419 free(newline);
420 // forget the pointers as we just freed them and 'line' must not be freed again after the loop
421 newline = NULL;
422 line = NULL;
425 if(!CodeSet_checked)
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);
444 if(line != NULL)
445 free(line);
447 fclose(fp);
449 if(WarnCTGaps)
451 for(cs = FirstCatString; cs != NULL; cs = cs->Next)
453 if(cs->CT_Str == NULL)
455 ShowWarn(MSG_ERR_CTGAP, cs->ID_Str);
460 if(Result)
461 CT_Scanned = TRUE;
463 return(Result);