home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Fish 3
/
goldfish_volume_3.bin
/
files
/
dev
/
misc
/
flexcat
/
src
/
flexcat.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-12-12
|
40KB
|
1,934 lines
/*
FlexCat.c: The flexible catalog creator V 1.5
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.
Ok, this is nothing special. It grabs a catalog translation and a
catalog description file and produces catalogs and the source to
handle 'em. What is it else than lots of other programs?
The difference is, that YOU determine what source FlexCat produces.
Another file is scanned by FlexCat to produce code. This file contains
some c-string like special characters (%v for version for example)
You can edit this file and modify it as you want. So FlexCat can produce
C source as well as Assembler, Oberon, Modula 2, E, ...
Command line syntax:
FlexCat CDFILE/A,CTFILE,NEWCTFILE/K,CATALOG/K,SOURCES/M
CDFILE - the name of the catalog description file; see CatComp for
syntax
CTFILE - the name of a catalog translation file; see CatComp for
syntax
NEWCTFILE - the name of a catalog transcription file to be created
(will be a copy of CDFILE except that the strings remain
empty and will be put as comments behind the string
definition)
CATALOG - the name of the catalog to be created; default is the
basename of the CDFILE and .catalog appended
SOURCES - the sourcefiles to be created. These arguments take a special
form: sourcefile=templatefile where sourcefile is the
name of the file to create and templatefile is the name of
a template file containing some special patterns. The
template file will be scanned and written to the source
file with these special patterns replaced by certain
strings. Possible patterns are:
%b basename of CDFILE (for example "file", if
CDFILE is "file.cd")
%f0 the filename of CDFILE
%f1 same as %f0, except that a trailing
ending will be removed
%f2 same as %f0, except that leading pathname
components will be removed
%f3 same as %f0, except that both ending and
pathname components will be removed
%fv flexcat version
%l language (of CDFILE)
%n number of catalog strings
%o0 the name of the sourcefile, that is currently
created; (What the heck do the guys need this
for? :-)
%o1 same as %00, except that a trailing
ending will be removed
%o2 same as %o0, except that leading pathname
components will be removed
%o3 same as %o0, except that both ending and
pathname components will be removed
%v version
%% the percent sign itself
The following patterns indicate, that the line should be
repeated for each catalog string.
%i the string's ID descriptor
%nd the string's ID (decimal), n is the number of
bytes to write (will be filled up with zeros);
default is to suppress leading zeros, if n
is omitted
%nx like %hd, except that output is in hex
%nc like %hd, except that output is in octal
%e the string's number (counting from 0)
%s the string itself
%(...) indicates that anything between the brackets
should appear for any line except the last
(Oberon and Modula need a "," to separate array
entries, but the last entry must not be
terminated by a ",". So you would terminate an
array entry by a %(,).)
Example: Let the string description be
MSG_HELLO (5//)
Hello!
Then the special characters produce
%i = MSG_HELLO
%d = 5
%s = "Hello!"
Additionally the following characters may appear (as well as in the
catalog description and catalog translation file)
\b = Backspace (Ascii 8)
\c = Control sequence introducer (Ascii 155)
\e = Escape (Ascii 27)
\f = Form feed (Ascii 12)
\g = Display beep (Ascii 7)
\n = line feed, newline (Ascii 10)
\r = carriage return (Ascii 13)
\t = Tab (Ascii 9)
\v = Vertical tab (Ascii 11)
\) = the character ')' (Needed inside %(..))
\\ = the backslash itself
\xHH = Ascii code HH (where HH are hex digits)
\OOO = Ascii code OOO (where OOO are octal digits)
A backslash at the end of a line indicates that the line should be
concatenated with the next one.
Computer: Amiga 1200 Compiler: Dice, 3.01
Author: V 1.0 31.06.1993 Jochen Wiedmann
Am Eisteich 9
72555 Metzingen
Tel. 07123 / 14881
Internet: wiedmann@uni-tuebingen.de
V1.01 Fixed a bug: The length of the source string
was used to check for the stringlen instead of
the real stringlen.
V1.1 Fixed two bugs: FlexCat didn't notice, if an
ID was defined twice (C-Compiler did later.) and
using language strings like français did not work
because of the the accented char. Introduced E
support. (Thanks Lionel Vintenat)
V1.2 Fixed a bug in the E source generator: " was
converted into \" and ' was not converted.
V1.3 Fixed a bug that caused FlexCat to hang if a
catalog translation was updated and a string
was missing in the catalog description.
Fixed a bug that caused FlexCat to create
invalid catalogs probably. (Seems like the
locale.library expects a completely different
behaviour in padding version and language
strings and the real catalog strings.)
Added %e to source descriptions.
V1.4 Added the Environment variable FLEXCAT_SDDIR.
Uses AutoC_c.sd and AutoC_h.sd now and is thus
compilable by Dice and SAS/C only.
Fixed a problem in stringtype Oberon:
Binary characters should be like \000 and
not \0.
Fixed a problem in stringtype E:
\e was written as \033.
Added %f* (source description filename) and %o*
(source filename) to source descriptions.
V1.5 Added WARNCTGAPS option.
Added #chunk to catalog translations.
*/
#define VERSION 1
#define REVISION 5
#define VERS "FlexCat 1.5"
#define VSTRING "FlexCat 1.5 (07.11.94)"
#define VERSTAG "\0$VER: FlexCat 1.5 (07.11.94)"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "FlexCat_cat.h"
#if ((defined(_DCC) && defined(AMIGA)) || \
(defined(__SASC) && defined(_AMIGA))) && \
!defined(__amigados)
#define __amigados
#endif
#if defined(__amigados)
#include <exec/types.h>
#if defined(_DCC) || defined(__SASC) || defined(__GNUC__)
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>
#else
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/utility_protos.h>
#endif
#ifdef tolower
#undef tolower
#endif
#define tolower ToLower
#define stricmp(s,t) Stricmp((STRPTR) (s), (STRPTR) (t))
#define strnicmp(s,t,l) Strnicmp((STRPTR) (s), (STRPTR) (t), l)
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
#define MAXPATHLEN 512
#define FLEXCAT_SDDIR "FLEXCAT_SDDIR"
#if defined(__amigados)
#define DEFAULT_FLEXCAT_SDDIR "PROGDIR:lib"
#else
#define DEFAULT_FLEXCAT_SDDIR "lib"
#endif
enum StringTypes {
TYPE_C, /* Produce C strings */
TYPE_ASSEMBLER, /* Produce Assembler strings */
TYPE_OBERON, /* Produce Oberon strings */
TYPE_E, /* Produce E strings. (Oops, thought */
/* it allows only 32 bit integers? ;-) */
TYPE_NONE /* Simple strings */
};
enum OutputModes {
OutputMode_None, /* Nothing written yet */
OutputMode_Bin, /* Last character written was binary */
OutputMode_Ascii /* Last character written was Ascii */
};
struct CatString
{ struct CatString *Next;
char *CD_Str;
char *CT_Str;
char *ID_Str;
int MinLen, MaxLen, ID, Nr;
};
struct CDLine
{ struct CDLine *Next;
char *Line;
};
struct CatalogChunk
{ struct CatalogChunk *Next;
ULONG ID;
char *ChunkStr;
};
struct CatString *FirstCatString = NULL; /* First catalog string */
struct CDLine *FirstCDLine = NULL; /* First catalog description line */
struct CatalogChunk *FirstChunk = NULL; /* List of catalog chunks */
char *BaseName = ""; /* Basename of catalog description */
char *Language = "english"; /* Language of catalog description */
int CatVersion = 0; /* Version of catalog to be opened */
int LengthBytes = 0; /* Number of bytes to preceed a */
/* created string and containing */
/* its length. */
char *CatLanguage = NULL; /* Language of catalog translation */
char *CatVersionString = NULL; /* version string of catalog */
/* translation */
int CodeSet = 0; /* Codeset of catalog translation */
int NumStrings = 0; /* Number of catalog strings */
int LongStrings = TRUE; /* Generate long or short strings */
char *ScanFile; /* File currently scanned */
int ScanLine; /* Line currently scanned */
int GlobalReturnCode = 0; /* Will be 5, if warnings appear */
int WarnCTGaps = FALSE; /* Warn missing symbols in CT */
/* file. */
const UBYTE VersTag[] = VERSTAG;
const UBYTE VString[] = VSTRING " by Jochen Wiedmann";
/*
This shows an error message and terminates
*/
struct RDArgs *Args;
void ShowError(const UBYTE (*msg), ...)
{ char **ptr = (char **) &msg;
fprintf(stderr, ptr[0], ptr[1], ptr[2], ptr[3], ptr[4]);
putc('\n', stderr);
exit(10);
}
/*
This shows the message: Memory error.
*/
void MemError(void)
{ ShowError(GetString(msgMemoryError), NULL);
}
/*
This shows a warning
*/
void ShowWarn(const UBYTE (*msg), ...)
{ char **ptr = (char **) &msg;
fprintf(stderr, (char *) GetString(msgWarning), ScanFile, ScanLine);
fprintf(stderr, ptr[0], ptr[1], ptr[2], ptr[3], ptr[4]);
putc('\n', stderr);
GlobalReturnCode = 5;
}
/*
This allocates a string
*/
char *AllocString(const char *str)
{ char *ptr;
if (!(ptr = malloc(strlen(str)+1)))
{ MemError();
}
strcpy(ptr, str);
return(ptr);
}
/*
This adds a new catalog chunk to the list of catalog
chunks.
*/
char *AddCatalogChunk(char *ID, const char *string)
{ struct CatalogChunk *cc, **ccptr;
if (!(cc = malloc(sizeof(*cc))))
{ MemError();
}
cc->Next = NULL;
cc->ID = *((ULONG *) ID);
cc->ChunkStr = AllocString(string);
/*
Put the new chunk to the end of the chunk list.
*/
for (ccptr = &FirstChunk; *ccptr != NULL; ccptr = &(*ccptr)->Next)
{
}
*ccptr = cc;
return(cc->ChunkStr);
}
/*
This translates a hex character.
*/
int gethex(int c)
{
if (c >= '0' && c <= '9')
{ return(c - '0');
}
else if (c >= 'a' && c <= 'f')
{ return(c - 'a' + 10);
}
else if (c >= 'A' && c <= 'F')
{ return(c - 'A' + 10);
}
ShowWarn(GetString(msgExpectedHex));
return(0);
}
/*
This translates an octal digit.
*/
int getoctal(int c)
{
if (c >= '0' && c <= '7')
{ return(c - '0');
}
ShowWarn(GetString(msgExpectedOctal));
return(0);
}
/*
Reading a line is somewhat complicated in order to allow lines of any
length.
Inputs: fp - the file, where the input comes from
AllowComment - TRUE, if a leading semicolon should force to
interpret the line as a comment line
*/
#define BUFSIZE 4096
char *ReadLine(FILE *fp, int AllowComment)
{ char *OldLine, *NewLine = NULL;
int c = '\0';
int Len = 0, LineLen = 0;
int FirstChar = TRUE;
int BackslashSeen = FALSE;
while (c != EOF)
{ if (Len+10 > LineLen)
{ OldLine = NewLine;
if (!(NewLine = malloc(LineLen+BUFSIZE)))
{ MemError();
}
strncpy(NewLine, OldLine, LineLen);
if (OldLine)
{ free(OldLine);
}
LineLen += BUFSIZE;
}
c = getc(fp);
if (FirstChar)
{ if (c == EOF)
{ free(NewLine);
return(NULL);
}
FirstChar = FALSE;
}
switch(c)
{ case '\r':
break;
case '\n':
++ScanLine;
if (BackslashSeen)
{ NewLine[Len++] = c;
BackslashSeen = FALSE;
break;
}
c = EOF;
case EOF:
break;
case '\\':
BackslashSeen = TRUE;
NewLine[Len++] = c;
break;
default:
BackslashSeen = FALSE;
NewLine[Len++] = c;
}
}
NewLine[Len] = '\0';
return(NewLine);
}
/*
This removes trailing blanks.
*/
void OverSpace(char **strptr)
{ int c;
while ((c = **strptr) == ' ' || c == '\t')
{ (*strptr)++;
}
}
/*
ReadChar scans an input line translating the backslash characters.
Inputs: strptr - a pointer to a stringpointer; the latter points to the
next character to be read and points behind the read
bytes after executing ReadChar
dest - a pointer to a buffer, where the read bytes should be
stored
Result: number of bytes that are written to dest (between 0 and 2)
*/
int ReadChar(char **strptr, char *dest)
{ char c;
int i;
switch(c = *((*strptr)++))
{ case '\\':
switch(c = tolower((int) *((*strptr)++)))
{ case '\n':
return(0);
case 'b':
*dest = '\b';
break;
case 'c':
*dest = '\233';
break;
case 'e':
*dest = '\033';
break;
case 'f':
*dest = '\f';
break;
case 'g':
*dest = '\007';
break;
case 'n':
*dest = '\n';
break;
case 'r':
*dest = '\r';
break;
case 't':
*dest = '\t';
break;
case 'v':
*dest = '\013';
break;
case 'x':
*dest = gethex((int) **strptr);
(*strptr)++;
if (((c = **strptr) >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
{ *dest = (*dest << 4) + gethex((int) c);
(*strptr)++;
}
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
*dest = getoctal((int) **strptr);
(*strptr)++;
for (i = 0; i < 2; i++)
{ if ((c = **strptr) >= '0' && c <= '7')
{ *dest = (*dest << 3) + getoctal((int) c);
(*strptr)++;
}
}
break;
case ')':
case '\\':
*(dest++) = '\\';
*dest = c;
return(2);
default:
*dest = c;
}
break;
default:
*dest = c;
}
return(1);
}
/*
This scans the catalog description file.
Inputs: cdfile - name of the catalog description file
Result: TRUE, if successfull, FALSE otherwise
*/
int ScanCDFile(char *cdfile)
{ FILE *fp;
struct CDLine *cdline, **cdptr = &FirstCDLine;
struct CatString *cs, **csptr = &FirstCatString;
char *line, *newline;
char *ptr;
int NextID = 0, len;
int Result = TRUE;
ScanFile = cdfile;
ScanLine = 0;
if (!(fp = fopen(cdfile, "r")))
{ ShowError(GetString(msgNoCatalogDescription), cdfile);
}
/*
Get the basename
*/
if ((ptr = strchr(cdfile, ':')))
{ cdfile = ptr+1;
}
if ((ptr = strrchr(cdfile, '/')))
{ cdfile = ptr+1;
}
if ((ptr = strrchr(cdfile, '.')))
{ len = ptr-cdfile;
}
else
{ len = strlen(cdfile);
}
if (!(BaseName = malloc(len+1)))
{ MemError();
}
strncpy(BaseName, cdfile, len);
BaseName[len] = '\0';
while(!feof(fp) && (line = newline = ReadLine(fp, TRUE)))
{ if (!(cdline = malloc(sizeof(*cdline))))
{ MemError();
}
*cdptr = cdline;
cdptr = &cdline->Next;
cdline->Next = NULL;
cdline->Line = line = AllocString(newline);
free(newline);
if (*line == ';')
{ continue;
}
if (*line == '#')
{ int CheckExtra = TRUE;
if (strnicmp(line+1, "language", 8) == 0)
{ char *ptr;
line += 9;
OverSpace(&line);
Language = AllocString(line);
for (ptr = Language; *ptr; ptr++)
{ *ptr = tolower((int) *ptr);
}
CheckExtra = FALSE;
}
else if (strnicmp(line+1, "version", 7) == 0)
{ CatVersion = strtol(line+8, &line, 10);
}
else if (strnicmp(line+1, "lengthbytes", 11) == 0)
{ line += 12;
if ((LengthBytes = strtol(line, &line, 10))
> sizeof(long))
{ ShowWarn(GetString(msgNoLengthBytes), sizeof(long));
LengthBytes = sizeof(long);
}
}
else if (strnicmp(line+1, "basename", 8) == 0)
{ line += 9;
OverSpace(&line);
free(BaseName);
BaseName = AllocString(line);
CheckExtra = FALSE;
}
else
{ ShowWarn(GetString(msgUnknownCDCommand));
Result = FALSE;
CheckExtra = FALSE;
}
if (CheckExtra)
{ OverSpace(&line);
if (*line)
{ ShowWarn(GetString(msgExtraCharacters));
Result = FALSE;
}
}
}
else
{ char *idstr;
if (*line == ' ' || *line == '\t')
{ ShowWarn(GetString(msgUnexpectedBlanks));
Result = FALSE;
OverSpace(&line);
}
idstr = line;
while ((*line >= 'a' && *line <= 'z') ||
(*line >= 'A' && *line <= 'Z') ||
(*line >= '0' && *line <= '9') ||
*line == '_')
{ ++line;
}
if (idstr == line)
{ ShowWarn(GetString(msgNoIdentifier));
Result = FALSE;
}
else
{ int found;
if (!(cs = malloc(sizeof(*cs))))
{ MemError();
}
do
{ struct CatString *scs;
found = TRUE;
for (scs = FirstCatString; scs != NULL; scs = scs->Next)
{ if (scs->ID == NextID)
{ found = FALSE;
++NextID;
break;
}
}
}
while (!found);
cs->Next = NULL;
cs->ID = NextID;
cs->MinLen = 0;
cs->MaxLen = -1;
cs->CD_Str = "";
cs->CT_Str = NULL;
if (!(cs->ID_Str = malloc((line-idstr)+1)))
{ MemError();
}
strncpy(cs->ID_Str, idstr, line-idstr);
cs->ID_Str[line-idstr] = '\0';
OverSpace(&line);
if (*line != '(')
{ ShowWarn(GetString(msgNoLeadingBracket));
Result = FALSE;
}
else
{ char *oldstr;
struct CatString *scs;
char bytes[10];
int bytesread, reallen;
++line;
OverSpace(&line);
if (*line != '/')
{
cs->ID = NextID = strtol(line, &line, 10);
OverSpace(&line);
}
for(scs = FirstCatString; scs != NULL; scs = scs->Next)
{ if (scs->ID == cs->ID)
{ ShowWarn(GetString(msgDoubleID));
Result = FALSE;
}
if (strcmp(cs->ID_Str, scs->ID_Str) == 0)
{ ShowWarn(GetString(msgDoubleIdentifier));
Result = FALSE;
}
}
if (*line != '/')
{ ShowWarn(GetString(msgNoMinLen));
Result = FALSE;
}
else
{ ++line;
OverSpace(&line);
if (*line != '/')
{ cs->MinLen = strtol(line, &line, 10);
OverSpace(&line);
}
if (*line != '/')
{ ShowWarn(GetString(msgNoMaxLen));
Result = FALSE;
}
else
{ ++line;
OverSpace(&line);
if (*line != ')')
{ cs->MaxLen = strtol(line, &line, 10);
OverSpace(&line);
}
if (*line != ')')
{ ShowWarn(GetString(msgNoTrailingBracket));
Result = FALSE;
}
else
{ ++line;
OverSpace(&line);
if (*line)
{ ShowWarn(GetString(msgExtraCharacters));
}
}
}
}
if (!(newline = ReadLine(fp, FALSE)))
{ ShowWarn(GetString(msgNoString));
Result = FALSE;
cs->CD_Str = "";
}
else
{ cs->CD_Str = AllocString(newline);
free(newline);
}
/*
Get stringlen
*/
oldstr = cs->CD_Str;
reallen = 0;
while (*oldstr)
{ bytesread = ReadChar(&oldstr, bytes);
if (bytesread == 2)
{ bytesread--;
}
reallen += bytesread;
}
if (cs->MinLen > 0 && reallen < cs->MinLen)
{ ShowWarn(GetString(msgShortString));
}
if (cs->MaxLen > 0 && reallen > cs->MaxLen)
{ ShowWarn(GetString(msgLongString));
}
cs->Nr = NumStrings;
*csptr = cs;
csptr = &cs->Next;
++NumStrings;
}
}
}
}
fclose(fp);
return(Result);
}
/*
This scans a catalog translation file.
Inputs: ctfile - name of the translation file to scan.
Result: TRUE, if successfull, FALSE otherwise.
*/
int ScanCTFile(char *ctfile)
{ FILE *fp;
char *newline, *line, *idstr, *newidstr, *newstr;
struct CatString *cs;
int Result = TRUE;
ScanFile = ctfile;
ScanLine = 0;
if (!(fp = fopen(ctfile, "r")))
{ ShowError(GetString(msgNoCatalogTranslation), ctfile);
}
while (!feof(fp) && (line = newline = ReadLine(fp, TRUE)))
{ switch(*line)
{ case ';':
break;
case '#':
if (*(++line) != '#')
{ ShowWarn(GetString(msgNoCTCommand));
}
++line;
OverSpace(&line);
if (strnicmp(line, "version", 7) == 0)
{ if (CatVersionString)
{ ShowWarn(GetString(msgDoubleCTVersion));
}
line += 7;
OverSpace(&line);
CatVersionString = AddCatalogChunk("FVER", line);
}
else if (strnicmp(line, "codeset", 7) == 0)
{ line += 7;
CodeSet = strtol(line, &line, 10);
OverSpace(&line);
if (*line)
{ ShowWarn(GetString(msgExtraCharacters));
}
}
else if (strnicmp(line, "language", 8) == 0)
{ char *ptr;
if (CatLanguage)
{ ShowWarn(GetString(msgDoubleCTLanguage));
}
line += 8;
OverSpace(&line);
CatLanguage = AddCatalogChunk("LANG", line);
for (ptr = CatLanguage; *ptr; ptr++)
{ *ptr = tolower((int) *ptr);
}
}
else if (strnicmp(line, "chunk", 5) == 0)
{ char *ID;
line += 5;
OverSpace(&line);
ID = line;
line += sizeof(ULONG);
OverSpace(&line);
AddCatalogChunk(ID, AllocString(line));
}
else
{ ShowWarn(GetString(msgUnknownCTCommand));
}
break;
default:
if (*line == ' ' || *line == '\t')
{ ShowWarn(GetString(msgUnexpectedBlanks));
OverSpace(&line);
}
idstr = line;
while ((*line >= 'a' && *line <= 'z') ||
(*line >= 'A' && *line <= 'Z') ||
(*line >= '0' && *line <= '9') ||
*line == '_')
{ ++line;
}
if (idstr == line)
{ ShowWarn(GetString(msgNoIdentifier));
break;
}
if (!(newidstr = malloc(line-idstr+1)))
{ MemError();
}
strncpy(newidstr, idstr, line-idstr);
newidstr[line-idstr] = '\0';
OverSpace(&line);
if (*line)
{ ShowWarn(GetString(msgExtraCharacters));
}
if ((newstr = ReadLine(fp, FALSE)))
{ for(cs = FirstCatString; cs != NULL; cs = cs->Next)
{ if (strcmp(cs->ID_Str, newidstr) == 0)
{ break;
}
}
if (cs == NULL)
{ ShowWarn(GetString(msgUnknownIdentifier), newidstr);
}
else
{ char *oldstr;
char bytes[10];
int bytesread, reallen;
if (cs->CT_Str)
{ ShowWarn(GetString(msgDoubleIdentifier));
Result = FALSE;
free (cs->CT_Str);
}
cs->CT_Str = AllocString(newstr);
/*
Get stringlen
*/
oldstr = cs->CT_Str;
reallen = 0;
while (*oldstr)
{ bytesread = ReadChar(&oldstr, bytes);
if (bytesread == 2)
{ bytesread--;
}
reallen += bytesread;
}
if (cs->MinLen > 0 && reallen < cs->MinLen)
{ ShowWarn(GetString(msgShortString));
}
if (cs->MaxLen > 0 && reallen > cs->MaxLen)
{ ShowWarn(GetString(msgLongString));
}
}
free(newstr);
}
else
{ ShowWarn(GetString(msgNoString));
cs->CT_Str = "";
}
free(newidstr);
}
free(newline);
}
fclose(fp);
if (WarnCTGaps)
{ for (cs = FirstCatString; cs != NULL; cs = cs->Next)
{ if (cs->CT_Str == NULL)
{ ShowWarn(GetString(msgCTGap), cs->ID_Str);
}
}
}
return(Result);
}
/*
CatPuts prints a string to a catalog. (The string is preceded by a
long integer containing its length and probably padded up to word
boundary or longword boundary, depending on the argument padbytes.)
The arguments countnul should be TRUE if the NUL byte at the end of
the string should be counted.
*/
int CatPuts(FILE *fp, char *str, int padbytes, int countnul)
{ unsigned long reallen, virtuallen, chunklen;
int bytesread;
char *oldstr;
char bytes[10];
/*
Get Length of string.
*/
oldstr = str;
reallen = 0;
while (*oldstr)
{ bytesread = ReadChar(&oldstr, bytes);
if (bytesread == 2)
{ bytesread--;
}
reallen += bytesread;
}
virtuallen = chunklen = reallen + LengthBytes;
if (countnul || chunklen % padbytes == 0)
{ virtuallen++;
}
fwrite(&virtuallen, sizeof(virtuallen), 1, fp);
if (LengthBytes)
{ fwrite(((char *) &reallen)+sizeof(reallen)-LengthBytes,
LengthBytes, 1, fp);
}
while(*str)
{ bytesread = ReadChar(&str, bytes);
if (bytesread)
{ fwrite(bytes+bytesread-1, 1, 1, fp);
}
}
do
{ putc('\0', fp);
}
while(++chunklen % padbytes);
return((int) chunklen+4);
}
/*
This puts a string chunk into the catalog
*/
int PutCatalogChunk(FILE *fp, struct CatalogChunk *cc)
{ fwrite(&cc->ID, sizeof(cc->ID), 1, fp);
return(4 + CatPuts(fp, cc->ChunkStr, 2, TRUE));
}
/*
This creates a catalog.
*/
void CreateCat(char *CatFile)
{ FILE *fp;
int CatLen, HeadLen;
struct CatString *cs;
struct CatalogChunk *cc;
int i;
if (!CatVersionString)
{ ShowError(GetString(msgNoCTVersion));
}
if (!CatLanguage)
{ ShowError(GetString(msgNoCTLanguage));
}
if (!(fp = fopen(CatFile, "w")))
{ ShowError(GetString(msgNoCatalog), CatFile);
}
fputs("FORM0000CTLG", fp);
CatLen = 4;
for (cc = FirstChunk; cc != NULL; cc = cc->Next)
{ CatLen += PutCatalogChunk(fp, cc);
}
i = 32;
fputs("CSET", fp);
fwrite(&i, sizeof(i), 1, fp);
while(i-- > 0)
{ putc('\0', fp);
}
CatLen += 48;
fprintf(fp, "STRS0000");
HeadLen = CatLen;
for (cs = FirstCatString; cs != NULL; cs = cs->Next)
{ if (strcmp(cs->CT_Str, cs->CD_Str))
{ fwrite(&cs->ID, sizeof(cs->ID), 1, fp);
CatLen += 4 + CatPuts(fp, cs->CT_Str, 4, FALSE);
}
}
fseek(fp, 4, SEEK_SET);
fwrite(&CatLen, sizeof(CatLen), 1, fp);
fseek(fp, HeadLen-4, SEEK_CUR);
CatLen -= HeadLen;
fwrite(&CatLen, sizeof(CatLen), 1, fp);
fclose(fp);
}
/*
This creates a new catalog translation file.
*/
void CreateCTFile(char *NewCTFile)
{ FILE *fp;
struct CDLine *cd;
struct CatString *cs;
struct CatalogChunk *cc;
char *line;
if (!(fp = fopen(NewCTFile, "w")))
{ ShowError(GetString(msgNoNewCTFile));
}
fprintf(fp, "## version %s\n## language %s\n## codeset %d\n",
CatVersionString, CatLanguage, CodeSet);
for (cc = FirstChunk; cc != NULL; cc = cc->Next)
{ if (cc->ChunkStr != CatVersionString &&
cc->ChunkStr != CatLanguage)
{ fprintf(fp, "## chunk ");
fwrite((char *) &cc->ID, sizeof(cc->ID), 1, fp);
fprintf(fp, " %s\n", cc->ChunkStr);
}
}
for(cd = FirstCDLine, cs = FirstCatString;
cd != NULL;
cd = cd->Next)
{ switch(*cd->Line)
{ case '#':
break;
case ';':
fprintf(fp, "%s\n", cd->Line);
break;
default:
if(cs)
{ fprintf(fp, "%s\n", cs->ID_Str);
fprintf(fp, "%s\n", cs->CT_Str ? cs->CT_Str : "");
putc(';', fp);
for (line = cs->CD_Str; *line; ++line)
{ putc((int) *line, fp);
if(*line == '\n')
{ putc(';', fp);
}
}
putc('\n', fp);
cs = cs->Next;
}
}
}
fclose(fp);
}
/*
InitCatStringOutput gets called before writing a catalog string as
source.
Inputs: fp = file pointer to the output file
type = one of TYPE_C create C strings
TYPE_ASSEMBLER create Assembler strings
TYPE_OBERON create Oberon strings
TYPE_E create E strings
TYPE_NONE create simple strings
*/
int OutputMode = OutputMode_None;
int OutputType = TYPE_C;
FILE *OutputFile;
int OutputLen;
void InitCatStringOutput(FILE *fp)
{
OutputLen = 0;
OutputFile = fp;
OutputMode = OutputMode_None;
switch(OutputType)
{ case TYPE_C:
case TYPE_OBERON:
putc('\"', fp);
OutputMode = OutputMode_Ascii;
break;
case TYPE_E:
putc('\'', fp);
case TYPE_ASSEMBLER:
case TYPE_NONE:
break;
}
}
/*
SeparateCatStringOutput gets called to split a catalog into separate
lines.
*/
void SeparateCatStringOutput(void)
{
switch(OutputType)
{ case TYPE_C:
if (!LongStrings)
{ fputs("\"\\\n\t\"", OutputFile);
}
break;
case TYPE_E:
if (!LongStrings)
{ fputs("\' +\n\t\'", OutputFile);
}
break;
case TYPE_OBERON:
if (!LongStrings)
{ fputs("\"\n\t\"", OutputFile);
}
break;
case TYPE_ASSEMBLER:
if (!LongStrings)
{ if (OutputMode == OutputMode_Ascii)
{ putc('\'', OutputFile);
}
putc('\n', OutputFile);
OutputMode = OutputMode_None;
}
break;
case TYPE_NONE:
break;
}
}
/*
WriteBinChar writes one binary character into the source file
*/
void WriteBinChar(int c)
{
switch(OutputType)
{ case TYPE_C:
case TYPE_E:
case TYPE_OBERON:
switch(c)
{ case '\b':
fputs("\\b", OutputFile);
break;
case '\n':
fputs("\\n", OutputFile);
break;
case '\r':
fputs("\\r", OutputFile);
break;
case '\t':
fputs("\\t", OutputFile);
break;
case '\f':
fputs("\\f", OutputFile);
break;
case '\0':
fputs("\\000", OutputFile);
break;
default:
if(OutputType == TYPE_E && c == '\033')
{ fputs("\\e", OutputFile);
}
else
{ fprintf(OutputFile, "\\%c%c%c", ((c >> 6) & 3) + '0',
((c >> 3) & 7) + '0', (c & 7) + '0');
}
break;
}
++OutputLen;
OutputMode = OutputMode_Bin;
break;
case TYPE_ASSEMBLER:
switch(OutputMode)
{ case OutputMode_None:
fprintf(OutputFile, "\tdc.b\t$%02x", c & 0xff);
break;
case OutputMode_Ascii:
putc('\'', OutputFile);
case OutputMode_Bin:
fprintf(OutputFile, ",$%02x", c & 0xff);
break;
}
++OutputLen;
OutputMode = OutputMode_Bin;
break;
case TYPE_NONE:
ShowWarn(GetString(msgNoBinChars));
break;
}
}
/*
WriteAsciiChar writes one ascii character into the source file.
*/
void WriteAsciiChar(int c)
{
switch(OutputType)
{ case TYPE_C:
case TYPE_OBERON:
switch(c)
{ case '\"':
fputs("\\\"", OutputFile);
break;
default:
putc(c, OutputFile);
break;
}
++OutputLen;
OutputMode = OutputMode_Ascii;
break;
case TYPE_E:
switch(c)
{ case '\'':
fputs("''", OutputFile);
break;
default:
putc(c, OutputFile);
break;
}
++OutputLen;
OutputMode = OutputMode_Ascii;
break;
case TYPE_ASSEMBLER:
if (c == '\'')
{ WriteBinChar(c);
}
else
{ switch (OutputMode)
{ case OutputMode_None:
fprintf(OutputFile, "\tdc.b\t\'%c", c);
break;
case OutputMode_Ascii:
putc(c, OutputFile);
break;
case OutputMode_Bin:
fprintf(OutputFile, ",\'%c", c);
break;
}
++OutputLen;
OutputMode = OutputMode_Ascii;
}
break;
case TYPE_NONE:
putc(c, OutputFile);
break;
}
}
/*
TerminateCatStringOutput finishs the output of a catalog string.
*/
void TerminateCatStringOutput(void)
{
switch(OutputType)
{ case TYPE_C:
case TYPE_OBERON:
putc('\"', OutputFile);
break;
case TYPE_E:
putc('\'', OutputFile);
break;
case TYPE_ASSEMBLER:
switch(OutputMode)
{ case OutputMode_Ascii:
putc('\'', OutputFile);
case OutputMode_Bin:
break;
case OutputMode_None:
break;
}
case TYPE_NONE:
break;
}
}
/*
This writes a sourcestring.
*/
void WriteString(FILE *fpout, char *str, long Len)
{ char bytes[10];
int bytesread;
int needseparate = FALSE;
InitCatStringOutput(fpout);
if (Len >= 0)
{ int i;
for(i = LengthBytes; i >= 1; i--)
{ WriteBinChar((int) ((char *) &Len)[sizeof(Len)-i]);
}
}
while (*str)
{ bytesread = ReadChar(&str, bytes);
if (bytesread)
{ unsigned char c;
if (needseparate)
{ SeparateCatStringOutput();
needseparate = FALSE;
}
c = bytes[bytesread-1];
if ((c >= 0x20 && c < 0x7f) || c >= 0xa0)
{ WriteAsciiChar((int) c);
}
else
{ WriteBinChar((int) c);
}
}
else
{ needseparate = TRUE;
}
}
TerminateCatStringOutput();
}
/*
This function creates a copy of a filename, removes an
optional ending and pathname components, if desired.
Inputs: filename - the filename to copy
howto - a set of bits
bit 0: 1 = remove ending, 0 = leave it
bit 1: 1 = remove pathname, 0 = leave it
Result: The copy of the filename
*/
char *AllocFileName(char *filename, int howto)
{ char *tempstr, *ptr;
if (!(tempstr = strdup(filename)))
{ MemError();
exit(10);
}
/* Remove pathname components, if desired */
if (howto & 2)
{ if ((ptr = strchr(tempstr, ':')))
{ tempstr = ptr+1;
}
if ((ptr = strrchr(tempstr, '/')))
{ tempstr = ptr+1;
}
}
/* Remove ending, if desired. */
if (howto & 1)
{ if ((ptr = strrchr(tempstr, '.')))
{ *ptr = '\0';
}
}
return(tempstr);
}
/*
This function adds a pathname and a filename to a full
filename.
Inputs: pathname - the leading pathname
filename - the filename
Result: The new filename
*/
char *AddFileName(char *pathname, char *filename)
{ char *buffer;
int size = strlen(pathname) + strlen(filename) + 2;
if (!(buffer = malloc(size)))
{ MemError();
exit(10);
}
#if defined(__amigados)
strcpy(buffer, pathname);
AddPart((STRPTR) buffer, (STRPTR) filename, size);
#else
sprintf(buffer, "%s/%s", pathname, filename);
#endif
return(buffer);
}
/*
Finally the source creation.
*/
void CreateSourceFile(char *SourceFile, char *TemplateFile,
char *CDFile)
{ FILE *fpin, *fpout;
char *line;
char *OrigTemplateFile = TemplateFile;
/*
Open the source file. This may be found in various places
*/
if (!(fpin = fopen(TemplateFile, "r")))
{ char *sddir;
if ((sddir = getenv(FLEXCAT_SDDIR)))
{ TemplateFile = AddFileName(sddir, OrigTemplateFile);
fpin = fopen(TemplateFile, "r");
}
}
if (!fpin)
{ TemplateFile = AddFileName(DEFAULT_FLEXCAT_SDDIR, OrigTemplateFile);
fpin = fopen(TemplateFile, "r");
}
if (!fpin)
{
ShowError(GetString(msgNoSourceDescription), OrigTemplateFile);
return;
}
if (!(fpout = fopen(SourceFile, "w")))
{
ShowError(GetString(msgNoSource), SourceFile);
return;
}
while(!feof(fpin) && (line = ReadLine(fpin, FALSE)))
{ struct CatString *cs;
int NeedRepeat;
char bytes[10];
int bytesread;
cs = FirstCatString;
do
{ char *currentline = line;
NeedRepeat = FALSE;
if (*currentline == '#' && *(++currentline) == '#')
{ ++currentline;
OverSpace(¤tline);
if (strnicmp(currentline, "stringtype", 10) == 0)
{ currentline += 10;
OverSpace(¤tline);
if (strnicmp(currentline, "c", 1) == 0)
{ OutputType = TYPE_C;
++currentline;
}
else if (strnicmp(currentline, "assembler", 9) == 0)
{ OutputType = TYPE_ASSEMBLER;
currentline += 9;
}
else if (strnicmp(currentline, "oberon", 6) == 0)
{ OutputType = TYPE_OBERON;
currentline += 6;
}
else if (strnicmp(currentline, "e", 1) == 0)
{ OutputType = TYPE_E;
++currentline;
}
else if (strnicmp(currentline, "none", 4) == 0)
{ OutputType = TYPE_NONE;
currentline += 4;
}
else
{ ShowWarn(GetString(msgUnknownStringType));
currentline += strlen(currentline);
}
OverSpace(¤tline);
if (*currentline)
{ ShowWarn(GetString(msgExtraCharacters));
}
continue;
}
else if (strnicmp(currentline, "shortstrings", 12) == 0)
{ currentline += 12;
LongStrings = FALSE;
OverSpace(¤tline);
if (*currentline)
{ ShowWarn(GetString(msgExtraCharacters));
}
continue;
}
}
currentline = line;
while(*currentline)
{ bytesread = ReadChar(¤tline, bytes);
if (bytesread)
{ if (*bytes == '%')
{ char c;
switch(c = *(currentline++))
{ case 'b':
fputs(BaseName, fpout);
break;
case 'n':
fprintf(fpout, "%d", NumStrings);
break;
case 'v':
fprintf(fpout, "%d", CatVersion);
break;
case 'l':
WriteString(fpout, Language, -1);
break;
case 'f':
{ char *tempstr;
if ((c = *currentline++) == 'v')
{ fputs(VERS, fpout);
}
else
{ tempstr = AllocFileName(CDFile, c - '0');
fputs(tempstr, fpout);
}
}
break;
case 'o':
{ char *tempstr;
tempstr = AllocFileName(SourceFile, *currentline++ - '0');
fputs(tempstr, fpout);
}
break;
case 'i':
NeedRepeat = TRUE;
if (cs) fputs(cs->ID_Str, fpout);
break;
case 'd':
case 'x':
case 'c':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{ int len = 0;
char buffer[20];
while(c >= '0' && c <= '9')
{ len = (c - '0') + len * 10;
c = *currentline++;
}
if (c == 'c')
{ c = 'o';
}
if (len)
{ sprintf(buffer, "%%0%d%c", len, c);
}
else
{ sprintf(buffer, "%%%c", c);
}
if (cs) fprintf(fpout, buffer, cs->ID);
NeedRepeat = TRUE;
}
break;
case 'e':
NeedRepeat = TRUE;
if (cs) fprintf(fpout, "%d", cs->Nr);
break;
case 's':
NeedRepeat = TRUE;
if (cs)
{ char *idstr;
unsigned long len = 0;
if (LengthBytes)
{ idstr = cs->CD_Str;
while(*idstr)
{ bytesread = ReadChar(&idstr, bytes);
if (bytesread)
{ ++len;
}
}
}
WriteString(fpout, cs->CD_Str, LengthBytes ? len : -1);
}
break;
case '(':
NeedRepeat = TRUE;
while(*currentline && *currentline != ')')
{ bytesread = ReadChar(¤tline, bytes);
if (bytesread && cs && cs->Next)
{ putc((int) bytes[bytesread-1], fpout);
}
}
if (!*currentline)
{ ShowWarn(GetString(msgNoTerminateBracket));
}
else
{ ++currentline;
}
break;
default:
{ int c = *currentline++;
putc(c, fpout);
}
}
}
else
{ putc((int) bytes[bytesread-1], fpout);
}
}
}
putc('\n', fpout);
}
while(NeedRepeat && cs && (cs = cs->Next));
free(line);
}
fclose(fpin);
fclose(fpout);
}
/*
The Usage function describes the programs calling syntax.
*/
void Usage(void)
{
fputs((char *) GetString(msgUsage), stderr);
fprintf(stderr, "\n\n%s\n", VString);
exit(5);
}
/*
Finally the main function. Does nothing special except for scanning
the arguments.
*/
int main (int argc, char *argv [])
{ char *cdfile, *ctfile, *newctfile, *catalog;
char *source, *template;
int i;
if (argc == 0) /* Aztec's entry point for workbench programs */
{ exit(5);
}
cdfile = ctfile = newctfile = catalog = NULL;
if (argc == 1)
{ Usage();
}
for (i = 1; i < argc; i++)
{ if (strnicmp (argv[i], "catalog=", 8) == 0)
{ catalog = argv[i] + 8;
}
else if (stricmp (argv[i], "catalog") == 0)
{ if (i+1 == argc)
{ Usage();
}
catalog = argv[++i];
}
else if (strnicmp (argv[i], "newctfile=", 10) == 0)
{ newctfile = argv[i] + 10;
}
else if (strnicmp (argv[i], "newctfile", 10) == 0)
{ if (i+1 == argc)
{ Usage();
}
newctfile = argv[++i];
}
else if (stricmp(argv[i], "warnctgaps") == 0)
{ WarnCTGaps = TRUE;
}
else if (cdfile == NULL)
{ if (stricmp(argv[i], "?") == 0 || stricmp(argv[i], "-h") == 0 ||
stricmp(argv[i], "help") == 0)
{ Usage();
}
if (!ScanCDFile(cdfile = argv[i]))
{ exit(10);
}
}
else if (strchr(argv[i], '='))
{ source = AllocString(argv[i]);
*(template = strchr(source, '=')) = '\0';
++template;
CreateSourceFile(source, template, cdfile);
}
else
{ if (ctfile)
{ Usage();
}
ctfile = argv[i];
}
}
if (ctfile)
{ if(!ScanCTFile(ctfile))
{ exit(10);
}
}
if (catalog)
{ if (!ctfile)
{ fprintf(stderr, (char *) GetString(msgNoCTArgument));
Usage();
}
CreateCat(catalog);
}
if (newctfile)
{ CreateCTFile(newctfile);
}
exit(GlobalReturnCode);
}
/*
Dice's entry point for workbench programs
*/
#if defined(__amigados) && defined(_DCC)
void wbmain(struct WBStartup *wbmsg)
{ exit(5);
}
#endif