amath/build/flexcat/utils.c

781 lines
18 KiB
C

/*
* $Id$
*
* Copyright (C) 1993-1999 by 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.
*
*/
#if defined(AMIGA)
#include <proto/codesets.h>
#else
#include <iconv.h>
#include <errno.h>
#endif
#include "flexcat.h"
#include "readprefs.h"
#include "swapfuncs.h"
#include "showfuncs.h"
#include "scancd.h"
#include "scanct.h"
#include "createcat.h"
#include "globals.h"
#include "utils.h"
#include "openlibs.h"
#include "SDI_compiler.h"
const char VString[] = VERS " [" SYSTEMSHORT "/" CPU "] (" EXE_DATE ")\n" EXE_COPYRIGHT;
const char EString[] = "Contact: http://sf.net/p/flexcat/";
/// MyExit
void MyExit(int Code)
{
#ifdef AMIGA
if((NumberOfWarnings > 0 || Code != 0) && !NoBeep)
DisplayBeep(NULL);
#endif
CloseFlexCatCatalog();
CloseLibs();
exit(Code);
}
///
#ifndef AMIGA
/*
* This array is designed for mapping upper and lower case letters
* together for a case independent comparison. The mappings are
* based upon ascii character sequences.
*/
typedef unsigned char uc;
static const unsigned char charmap[] =
{
(uc)'\000', (uc)'\001', (uc)'\002', (uc)'\003', (uc)'\004', (uc)'\005', (uc)'\006', (uc)'\007',
(uc)'\010', (uc)'\011', (uc)'\012', (uc)'\013', (uc)'\014', (uc)'\015', (uc)'\016', (uc)'\017',
(uc)'\020', (uc)'\021', (uc)'\022', (uc)'\023', (uc)'\024', (uc)'\025', (uc)'\026', (uc)'\027',
(uc)'\030', (uc)'\031', (uc)'\032', (uc)'\033', (uc)'\034', (uc)'\035', (uc)'\036', (uc)'\037',
(uc)'\040', (uc)'\041', (uc)'\042', (uc)'\043', (uc)'\044', (uc)'\045', (uc)'\046', (uc)'\047',
(uc)'\050', (uc)'\051', (uc)'\052', (uc)'\053', (uc)'\054', (uc)'\055', (uc)'\056', (uc)'\057',
(uc)'\060', (uc)'\061', (uc)'\062', (uc)'\063', (uc)'\064', (uc)'\065', (uc)'\066', (uc)'\067',
(uc)'\070', (uc)'\071', (uc)'\072', (uc)'\073', (uc)'\074', (uc)'\075', (uc)'\076', (uc)'\077',
(uc)'\100', (uc)'\141', (uc)'\142', (uc)'\143', (uc)'\144', (uc)'\145', (uc)'\146', (uc)'\147',
(uc)'\150', (uc)'\151', (uc)'\152', (uc)'\153', (uc)'\154', (uc)'\155', (uc)'\156', (uc)'\157',
(uc)'\160', (uc)'\161', (uc)'\162', (uc)'\163', (uc)'\164', (uc)'\165', (uc)'\166', (uc)'\167',
(uc)'\170', (uc)'\171', (uc)'\172', (uc)'\133', (uc)'\134', (uc)'\135', (uc)'\136', (uc)'\137',
(uc)'\140', (uc)'\141', (uc)'\142', (uc)'\143', (uc)'\144', (uc)'\145', (uc)'\146', (uc)'\147',
(uc)'\150', (uc)'\151', (uc)'\152', (uc)'\153', (uc)'\154', (uc)'\155', (uc)'\156', (uc)'\157',
(uc)'\160', (uc)'\161', (uc)'\162', (uc)'\163', (uc)'\164', (uc)'\165', (uc)'\166', (uc)'\167',
(uc)'\170', (uc)'\171', (uc)'\172', (uc)'\173', (uc)'\174', (uc)'\175', (uc)'\176', (uc)'\177',
(uc)'\200', (uc)'\201', (uc)'\202', (uc)'\203', (uc)'\204', (uc)'\205', (uc)'\206', (uc)'\207',
(uc)'\210', (uc)'\211', (uc)'\212', (uc)'\213', (uc)'\214', (uc)'\215', (uc)'\216', (uc)'\217',
(uc)'\220', (uc)'\221', (uc)'\222', (uc)'\223', (uc)'\224', (uc)'\225', (uc)'\226', (uc)'\227',
(uc)'\230', (uc)'\231', (uc)'\232', (uc)'\233', (uc)'\234', (uc)'\235', (uc)'\236', (uc)'\237',
(uc)'\240', (uc)'\241', (uc)'\242', (uc)'\243', (uc)'\244', (uc)'\245', (uc)'\246', (uc)'\247',
(uc)'\250', (uc)'\251', (uc)'\252', (uc)'\253', (uc)'\254', (uc)'\255', (uc)'\256', (uc)'\257',
(uc)'\260', (uc)'\261', (uc)'\262', (uc)'\263', (uc)'\264', (uc)'\265', (uc)'\266', (uc)'\267',
(uc)'\270', (uc)'\271', (uc)'\272', (uc)'\273', (uc)'\274', (uc)'\275', (uc)'\276', (uc)'\277',
(uc)'\300', (uc)'\341', (uc)'\342', (uc)'\343', (uc)'\344', (uc)'\345', (uc)'\346', (uc)'\347',
(uc)'\350', (uc)'\351', (uc)'\352', (uc)'\353', (uc)'\354', (uc)'\355', (uc)'\356', (uc)'\357',
(uc)'\360', (uc)'\361', (uc)'\362', (uc)'\363', (uc)'\364', (uc)'\365', (uc)'\366', (uc)'\367',
(uc)'\370', (uc)'\371', (uc)'\372', (uc)'\333', (uc)'\334', (uc)'\335', (uc)'\336', (uc)'\337',
(uc)'\340', (uc)'\341', (uc)'\342', (uc)'\343', (uc)'\344', (uc)'\345', (uc)'\346', (uc)'\347',
(uc)'\350', (uc)'\351', (uc)'\352', (uc)'\353', (uc)'\354', (uc)'\355', (uc)'\356', (uc)'\357',
(uc)'\360', (uc)'\361', (uc)'\362', (uc)'\363', (uc)'\364', (uc)'\365', (uc)'\366', (uc)'\367',
(uc)'\370', (uc)'\371', (uc)'\372', (uc)'\373', (uc)'\374', (uc)'\375', (uc)'\376', (uc)'\377',
};
/// Stricmp
int Stricmp(const char *str1, const char *str2)
{
unsigned char u1, u2;
for(;;)
{
u1 = (unsigned char)*str1++;
u2 = (unsigned char)*str2++;
if(charmap[u1] != charmap[u2])
return charmap[u1] - charmap[u2];
if(u1 == '\0')
return 0;
}
}
///
/// Strnicmp
int Strnicmp(const char *str1, const char *str2, int len)
{
unsigned char u1, u2;
for(; len != 0; --len)
{
u1 = (unsigned char)*str1++;
u2 = (unsigned char)*str2++;
if(charmap[u1] != charmap[u2])
return charmap[u1] - charmap[u2];
if(u1 == '\0')
return 0;
}
return 0;
}
#endif
///
/// utf8_strlen
size_t utf8_strlen(const char *str)
{
size_t ix = strlen(str);
size_t i, q;
for(q=0, i=0; i < ix; i++, q++)
{
int c = (unsigned char)str[i];
if(c >= 0 && c <= 127)
i += 0;
else if((c & 0xe0) == 0xc0)
i += 1;
else if((c & 0xf0) == 0xe0)
i += 2;
else if((c & 0xf8) == 0xf0)
i += 3;
else
return 0; // invalid utf8
}
return q;
}
///
/// AllocString
/* This allocates a string */
char *AllocString(const char *str)
{
char *ptr;
if((ptr = malloc(strlen(str) + 1)) == NULL)
MemError();
strcpy(ptr, str);
return ptr;
}
///
/// Add a string to an already allocated one
char *AddString(char *str, const char *astr)
{
char *ptr;
if((ptr = malloc(strlen(str) + strlen(astr) + 1)) == NULL)
MemError();
strcpy(ptr, str);
strcat(ptr, astr);
free(str);
return ptr;
}
///
/// Convert a string from one charset to another
#ifdef AMIGA
char *ConvertString(char *str, const char *from_charset, const char *to_charset)
{
char *result = NULL;
BOOL fromIsUTF8 = (Stricmp(from_charset, "UTF-8") == 0 || Stricmp(from_charset, "UTF8") == 0);
BOOL toIsUTF8 = (Stricmp(to_charset, "UTF-8") == 0 || Stricmp(to_charset, "UTF8") == 0);
if(fromIsUTF8 == TRUE && toIsUTF8 == TRUE)
{
// no need to convert from UTF8 to UTF8
// just return a plain copy of the string
result = strdup(str);
}
else
{
struct codeset *dstCodeset;
dstCodeset = CodesetsFind((STRPTR)to_charset,
CSA_FallbackToDefault, FALSE,
TAG_DONE);
if(dstCodeset != NULL)
{
ULONG dstLen = 0;
char *dstText = NULL;
int errPtr = 0;
if(fromIsUTF8 == TRUE)
{
dstText = CodesetsUTF8ToStr(CSA_Source, str,
CSA_DestCodeset, dstCodeset,
CSA_DestLenPtr, &dstLen,
CSA_ErrPtr, &errPtr,
TAG_DONE);
}
else
{
struct codeset *srcCodeset;
srcCodeset = CodesetsFind((STRPTR)from_charset,
CSA_FallbackToDefault, FALSE,
TAG_DONE);
if(srcCodeset != NULL)
{
dstText = CodesetsConvertStr(CSA_Source, str,
CSA_SourceCodeset, srcCodeset,
CSA_DestCodeset, dstCodeset,
CSA_DestLenPtr, &dstLen,
CSA_ErrPtr, &errPtr,
TAG_DONE);
}
else
ShowWarn(MSG_ERR_UNKNOWN_SOURCE_CHARSET, from_charset);
}
if(dstText != NULL && dstLen != 0 && errPtr == 0)
{
char *buf;
// copy the converted string into a separately allocated string
if((buf = malloc(dstLen+1)) != NULL)
{
memcpy(buf, dstText, dstLen);
buf[dstLen] = '\0';
result = buf;
}
CodesetsFreeA(dstText, NULL);
if(buf == NULL)
MemError();
}
else
{
if(errPtr != 0)
ShowWarn(MSG_ERR_INVALID_CHARS_FOUND, errPtr);
if(dstText == NULL)
MemError();
}
}
else
ShowWarn(MSG_ERR_UNKNOWN_DESTINATION_CHARSET, to_charset);
}
return result;
}
#else
char *ConvertString(char *str, const char *from_charset, const char *to_charset)
{
char *result = NULL;
iconv_t ict;
if((ict = iconv_open(to_charset, from_charset)) != (iconv_t)-1)
{
size_t inleft = strlen(str);
char *buf;
if((buf = malloc(inleft+1)) != NULL)
{
size_t outleft = inleft;
char *outbuf = buf;
if(iconv(ict, &str, &inleft, &outbuf, &outleft) != (size_t)-1)
{
*outbuf = '\0';
result = buf;
}
else
{
ShowWarn(MSG_ERR_ICONV_FAILED, strerror(errno));
free(buf);
}
}
else
MemError();
iconv_close(ict);
}
else
ShowWarn(MSG_ERR_ICONV_OPEN_FAILED, strerror(errno));
return result;
}
#endif
///
/// Add catalog chunk
/* 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))) == NULL)
MemError();
cc->Next = NULL;
cc->ID = *((ULONG *)ID);
cc->ChunkStr = AllocString(string);
/* Put the new chunk at the end of the chunk list. */
for(ccptr = &FirstChunk; *ccptr != NULL; ccptr = &(*ccptr)->Next)
{
}
*ccptr = cc;
return cc->ChunkStr;
}
///
/// gethex
/* This translates an 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;
ShowError(MSG_ERR_EXPECTEDHEX);
return 0;
}
///
/// getoctal
/* This translates an octal digit. */
int getoctal(int c)
{
if(c >= '0' && c <= '7')
return c - '0';
ShowError(MSG_ERR_EXPECTEDOCTAL);
return 0;
}
///
/// ReadLine
/* 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 the
line to be interpreted as a comment */
char *ReadLine(FILE *fp, UNUSED int AllowComment)
{
char *NewLine = NULL;
int c = '\0';
int Len = 0, LineLen = 0;
int FirstChar = TRUE;
int BackslashSeen = FALSE;
int BackslashSeenOn = 0; /* Position where the last backslash was seen. */
int CommentLine = FALSE; /* If TRUE, we should ignore any trailing \'s */
while(c != EOF)
{
if(Len + 10 > LineLen)
{
NewLine = realloc(NewLine, LineLen + BUFSIZE);
LineLen += BUFSIZE;
}
c = getc(fp);
if(FirstChar)
{
if(c == EOF)
{
if(NewLine != NULL)
free(NewLine);
return NULL;
}
if(c == ';')
{
CommentLine = TRUE;
}
FirstChar = FALSE;
}
switch(c)
{
case '\r':
break;
case '\n':
++ScanLine;
if(BackslashSeen)
{
NewLine[Len++] = c;
BackslashSeen = FALSE;
break;
}
c = EOF;
case EOF:
break;
/* Check for trailing \\ */
case '\\':
{
if(!CommentLine)
{
if(BackslashSeen)
{
if(BackslashSeenOn ==(Len - 1))
{
BackslashSeen = FALSE;
NewLine[Len++] = c;
break;
}
}
BackslashSeen = TRUE;
BackslashSeenOn = Len;
}
NewLine[Len++] = c;
break;
}
default:
BackslashSeen = FALSE;
NewLine[Len++] = c;
}
}
NewLine[Len] = '\0';
return NewLine;
}
///
/// OverSpace
/* This removes trailing blanks. */
void OverSpace(char **strptr)
{
int c;
while((c = **strptr) == ' ' || c == '\t')
{
(*strptr)++;
}
}
///
/// Expunge
void Expunge(void)
{
#ifdef AMIGA
if(DoExpunge)
{
struct Library *localeBase;
// this may look utterly wrong since we are trying to RemLibrary() a library
// which we just opened. But this is the most convenient way to invoke the
// Expunge() function of locale.library, which will just remove any still
// opened but unused catalog file from memory without removing locale.library
// itself.
if((localeBase = OpenLibrary( "locale.library", 0)) != NULL)
{
RemLibrary(localeBase);
CloseLibrary(localeBase);
}
}
#endif // AMIGA
}
///
/// ReadChar
/* ReadChar scans an input line and translates the backslash characters.
Inputs: char * - a pointer to a string pointer; 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);
break;
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)++;
c = **strptr;
if((c >= '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)c);
for(i = 0; i < 2; i++)
{
c = **strptr;
if(c >= '0' && c <= '7')
{
*dest =(*dest << 3) + getoctal((int)c);
(*strptr)++;
}
}
}
break;
case ')':
case '\\':
*(dest++) = '\\';
*dest = c;
return(2);
break;
default:
*dest = c;
break;
}
}
break;
default:
{
*dest = c;
}
break;
}
return(1);
}
///
/// AllocFileName
/* This function creates a copy of a filename, and optionally
removes an 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)) == NULL)
{
MemError();
MyExit(10);
}
/* Remove pathname components, if desired. */
if(howto & 2)
{
if((ptr = strchr(tempstr, ':')) != NULL)
{
tempstr = ptr + 1;
}
if((ptr = strrchr(tempstr, '/')) != NULL)
{
tempstr = ptr + 1;
}
}
/* Remove ending, if desired. */
if(howto & 1)
{
if((ptr = strrchr(tempstr, '.')) != NULL)
{
*ptr = '\0';
}
}
return(tempstr);
}
///
/// AddFileName
/* 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;
#ifdef AMIGA
int size = strlen(pathname) + strlen(filename) + 2;
if((buffer = malloc(size)) == NULL)
{
MemError();
MyExit(10);
}
strcpy(buffer, pathname);
AddPart((char *)buffer, (char *)filename, size);
#else
if(asprintf(&buffer, "%s/%s", pathname, filename) < 0)
{
MemError();
MyExit(10);
}
#endif
return buffer;
}
///
/// Usage
/* The Usage function describes the program's calling syntax. */
void Usage(void)
{
fprintf(stderr, "%s\n", VString);
fprintf(stderr, "%s\n", EString);
fprintf(stderr,
"\n" \
"%s\n" \
" FlexCat CDFILE/A,CTFILE,POFILE,CATALOG/K,NEWCTFILE/K,SOURCES/M,\n" \
" WARNCTGAPS/S,NOOPTIM/S,FILL/S,FLUSH/S,NOBEEP/S,\n" \
" QUIET/S,NOLANGTOLOWER/S,NOBUFFEREDIO/S,MODIFIED/S,\n" \
" CODESET/K,COPYMSGNEW/S,OLDMSGNEW/K\n" \
"\n", MSG_USAGE_HEAD);
fprintf(stderr, "%s\n", MSG_USAGE);
MyExit(5);
}
///
/// wbmain
/* Dice's entry point for workbench programs */
#if defined(AMIGA) && defined(_DCC)
void wbmain(struct WBStartup *wbmsg)
{
fprintf(stderr, "FlexCat can't be run from Workbench!\n\n");
fprintf(stderr, "Open a shell session and type FlexCat\n");
fprintf(stderr, "for syntax and more information.\n");
exit(5);
}
#endif
///