Blame |
Last modification |
View Log
| Download
| RSS feed
| ?url?
/* nlmessages.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* */
/* AS-Portierung */
/* */
/* Einlesen und Verwalten von Meldungs-Strings */
/* */
/* Historie: 13. 8.1997 Grundsteinlegung */
/* 17. 8.1997 Verallgemeinerung auf mehrere Kataloge */
/* */
/*****************************************************************************/
#include "stdinc.h"
#include <string.h>
#include "strutil.h"
#include "be_le.h"
#include "bpemu.h"
#include "nls.h"
#include "chardefs.h"
#include "nlmessages.h"
/*****************************************************************************/
static const char *IdentString
= "AS Message Catalog - not readable\n\032\004";
static const char *EOpenMsg
= "cannot open msg file %s";
static const char *ERdMsg
= "cannot read from msg file";
static const char *EIndMsg
= "string table index error";
static TMsgCat DefaultCatalog
=
{
NULL
, NULL
, 0
};
typedef struct
{
Byte
*p_data
;
size_t data_length
, rd_ptr
;
Boolean data_allocated
;
} as_raw_msg_struct_t
;
/*!------------------------------------------------------------------------
* \fn free_raw(as_raw_msg_struct_t *p_raw)
* \brief free raw message struct
* \param p_raw struct to free
* ------------------------------------------------------------------------ */
static void free_raw
(as_raw_msg_struct_t
*p_raw
)
{
if (p_raw
)
{
if (p_raw
->p_data
&& p_raw
->data_allocated
)
free(p_raw
->p_data
);
p_raw
->p_data
= NULL
;
p_raw
->data_allocated
= False
;
p_raw
->data_length
= p_raw
->rd_ptr
= 0;
free(p_raw
);
}
}
/*!------------------------------------------------------------------------
* \fn seek_raw(as_raw_msg_struct_t *p_raw, size_t offset, int whence)
* \brief seek in raw message struct
* \param p_raw struct to seek in
* \param offset offset from ref position
* \param whence ref position (like for fseek())
* \return 0 for OK, -1 for error
* ------------------------------------------------------------------------ */
static int seek_raw
(as_raw_msg_struct_t
*p_raw
, size_t offset
, int whence
)
{
size_t new_pos
;
switch (whence
)
{
case SEEK_SET
: new_pos
= offset
; break;
case SEEK_CUR
: new_pos
= p_raw
->rd_ptr
+ offset
; break;
case SEEK_END
: new_pos
= p_raw
->data_length
+ offset
; break;
default:
return -1;
}
if (new_pos
<= p_raw
->data_length
)
{
p_raw
->rd_ptr
= new_pos
;
return 0;
}
else
return -1;
}
/*!------------------------------------------------------------------------
* \fn read_raw(void *p_buf, size_t memb_size, size_t n_memb, as_raw_msg_struct_t *p_raw)
* \brief read data from raw message block
* \param p_buf where to read
* \param memb_size size of elements to read
* \param n_memb # of elements to read
* \param p_raw struct to read from
* \return actual # of elements read
* ------------------------------------------------------------------------ */
static size_t read_raw
(void *p_buf
, size_t memb_size
, size_t n_memb
, as_raw_msg_struct_t
*p_raw
)
{
size_t avl
= ((p_raw
->data_length
> p_raw
->rd_ptr
)
? (p_raw
->data_length
- p_raw
->rd_ptr
) : 0) / memb_size
,
to_copy
;
if (avl
< n_memb
)
n_memb
= avl
;
to_copy
= n_memb
* memb_size
;
memcpy(p_buf
, &p_raw
->p_data
[p_raw
->rd_ptr
], to_copy
);
p_raw
->rd_ptr
+= to_copy
;
return n_memb
;
}
/*!------------------------------------------------------------------------
* \fn read_raw_4(as_raw_msg_struct_t *p_raw, LongInt *p_dest)
* \brief read 32 bit integer from raw message block
* \param p_raw struct to read from
* \param p_dest where to read
* \return True if success
* ------------------------------------------------------------------------ */
static Boolean read_raw_4
(as_raw_msg_struct_t
*p_raw
, LongInt
*p_dest
)
{
if (read_raw
(p_dest
, 4, 1, p_raw
) != 1)
return False
;
if (HostBigEndian
)
DSwap
(p_dest
, 4);
return True
;
}
/*!------------------------------------------------------------------------
* \fn create_raw_file(const char *p_file_name)
* \brief create raw message struct from file
* \param p_file_name file path & name
* \return * to struct or NULL if failed
* ------------------------------------------------------------------------ */
static as_raw_msg_struct_t
*create_raw_file
(const char *p_file_name
)
{
FILE
*p_file
= NULL
;
as_raw_msg_struct_t
*p_result
= NULL
;
Boolean success
= False
;
p_file
= fopen(p_file_name
, OPENRDMODE
);
if (!p_file
)
goto func_exit
;
p_result
= (as_raw_msg_struct_t
*)calloc(1, sizeof(*p_result
));
if (!p_result
)
goto func_exit
;
fseek(p_file
, 0, SEEK_END
);
p_result
->data_length
= ftell(p_file
);
if (p_result
->data_length
>= 65535u
)
p_result
->data_length
= 65535u
;
fseek(p_file
, 0, SEEK_SET
);
p_result
->p_data
= (Byte
*)malloc(p_result
->data_length
);
if (p_result
->data_length
&& !p_result
->p_data
)
goto func_exit
;
p_result
->data_allocated
= True
;
if (fread(p_result
->p_data
, 1, p_result
->data_length
, p_file
) != p_result
->data_length
)
goto func_exit
;
success
= True
;
func_exit
:
if (p_file
)
fclose(p_file
);
if (p_result
&& !success
)
{
free_raw
(p_result
);
p_result
= NULL
;
}
return p_result
;
}
/*****************************************************************************/
static void error
(const char *Msg
)
{
fprintf(stderr
, "message catalog handling: %s - program terminated\n", Msg
);
exit(255);
}
char *catgetmessage
(PMsgCat Catalog
, int Num
)
{
if ((Num
>= 0) && (Num
< Catalog
->MsgCount
))
return Catalog
->MsgBlock
+ Catalog
->StrPosis
[Num
];
else
{
static char *umess
= NULL
;
if (!umess
)
umess
= (char*)malloc(sizeof(char) * STRINGSIZE
);
as_snprintf
(umess
, STRINGSIZE
, "catgetmessage: message number %d does not exist", Num
);
return umess
;
}
}
char *getmessage
(int Num
)
{
return catgetmessage
(&DefaultCatalog
, Num
);
}
/*!------------------------------------------------------------------------
* \fn check_header_raw(as_raw_msg_struct_t *p_raw, LongInt msg_id1, LongInt msg_id2)
* \brief check raw msg file for correct header
* \param p_raw file to check
* \param msg_id1, msg_id2 expected identifiers
* \return True if file OK
* ------------------------------------------------------------------------ */
static Boolean check_header_raw
(as_raw_msg_struct_t
*p_raw
, LongInt msg_id1
, LongInt msg_id2
)
{
String line
;
size_t ident_len
= strlen(IdentString
);
LongInt r_id1
, r_id2
;
return !seek_raw
(p_raw
, 0, SEEK_SET
)
&& (ident_len
== read_raw
(line
, 1, ident_len
, p_raw
))
&& !memcmp(line
, IdentString
, ident_len
)
&& read_raw_4
(p_raw
, &r_id1
)
&& (r_id1
== msg_id1
)
&& read_raw_4
(p_raw
, &r_id2
)
&& (r_id2
== msg_id2
);
}
/*!------------------------------------------------------------------------
* \fn open_raw_file_and_check(const char *name, LongInt MsgId1, LongInt MsgId2)
* \brief open raw msg file from file and check header
* \param name file's name & path
* \param MsgId1, MsgId2, expected identifiers
* \return * to raw file or NULL if failed
* ------------------------------------------------------------------------ */
as_raw_msg_struct_t
*open_raw_file_and_check
(const char *name
, LongInt MsgId1
, LongInt MsgId2
)
{
as_raw_msg_struct_t
*p_ret
= NULL
;
p_ret
= create_raw_file
(name
);
if (!p_ret
)
return NULL
;
if (!check_header_raw
(p_ret
, MsgId1
, MsgId2
))
{
fprintf(stderr
, "message catalog handling: warning: %s has invalid format or is out of date\n", name
);
free_raw
(p_ret
); p_ret
= NULL
;
}
return p_ret
;
}
/*!------------------------------------------------------------------------
* \fn parse_raw_msg_file(PMsgCat p_cat, as_raw_msg_struct_t *p_msg_raw)
* \brief parse raw message catalog into runtime structure
* \param p_cat where to store result
* \param p_raw raw catalog to parse
* ------------------------------------------------------------------------ */
static void parse_raw_file
(PMsgCat p_cat
, as_raw_msg_struct_t
*p_msg_raw
)
{
LongInt DefPos
= -1, MomPos
, DefLength
= 0, MomLength
, z
, StrStart
, CtryCnt
, Ctrys
[100];
unsigned StrCap
;
Boolean fi
, Gotcha
;
const tNLSCharacterTab
*CharacterTab
;
char *pStr
, str
[2048], *ptr
;
tNLSCharacter Ch
;
const char *lcstring
;
Word CountryCode
;
/* get reference for finding out which language set to use */
CountryCode
= NLS_GetCountryCode
();
lcstring
= getenv("LC_MESSAGES");
if (!lcstring
)
lcstring
= getenv("LC_ALL");
if (!lcstring
)
lcstring
= getenv("LANG");
if (!lcstring
)
lcstring
= "";
Gotcha
= False
;
do
{
ptr
= str
;
do
{
if (read_raw
(ptr
, 1, 1, p_msg_raw
) != 1)
error
(ERdMsg
);
fi
= (*ptr
== '\0');
if (!fi
) ptr
++;
}
while (!fi
);
if (*str
!= '\0')
{
if (!read_raw_4
(p_msg_raw
, &MomLength
))
error
(ERdMsg
);
if (!read_raw_4
(p_msg_raw
, &CtryCnt
))
error
(ERdMsg
);
for (z
= 0; z
< CtryCnt
; z
++)
if (!read_raw_4
(p_msg_raw
, Ctrys
+ z
))
error
(ERdMsg
);
if (!read_raw_4
(p_msg_raw
, &MomPos
))
error
(ERdMsg
);
if (DefPos
== -1)
{
DefPos
= MomPos
;
DefLength
= MomLength
;
}
for (z
= 0; z
< CtryCnt
; z
++)
if (Ctrys
[z
] == CountryCode
)
Gotcha
= True
;
if (!Gotcha
)
Gotcha
= !as_strncasecmp
(lcstring
, str
, strlen(str
));
}
}
while ((*str
!= '\0') && (!Gotcha
));
if (*str
== '\0')
{
MomPos
= DefPos
;
MomLength
= DefLength
;
}
/* read pointer table */
seek_raw
(p_msg_raw
, MomPos
, SEEK_SET
);
if (!read_raw_4
(p_msg_raw
, &StrStart
))
error
(ERdMsg
);
p_cat
->MsgCount
= (StrStart
- MomPos
) >> 2;
p_cat
->StrPosis
= (LongInt
*) malloc(sizeof(LongInt
)*p_cat
->MsgCount
);
p_cat
->StrPosis
[0] = 0;
if ((int)read_raw
(p_cat
->StrPosis
+ 1, 4, p_cat
->MsgCount
- 1, p_msg_raw
) + 1 != p_cat
->MsgCount
)
error
(ERdMsg
);
if (HostBigEndian
)
DSwap
(p_cat
->StrPosis
+ 1, (p_cat
->MsgCount
- 1) << 2);
for (z
= 1; z
< p_cat
->MsgCount
; z
++)
{
p_cat
->StrPosis
[z
] -= StrStart
;
if ((p_cat
->StrPosis
[z
] < 0) || (p_cat
->StrPosis
[z
] >= MomLength
))
error
(EIndMsg
);
}
/* read string table */
seek_raw
(p_msg_raw
, StrStart
, SEEK_SET
);
p_cat
->MsgBlock
= (char *) malloc(MomLength
);
if ((int)read_raw
(p_cat
->MsgBlock
, 1, MomLength
, p_msg_raw
) != MomLength
)
error
(ERdMsg
);
/* character replacement according to runtime codepage */
CharacterTab
= GetCharacterTab
(NLS_GetCodepage
());
for (z
= 1; z
< p_cat
->MsgCount
; z
++)
{
pStr
= p_cat
->MsgBlock
+ p_cat
->StrPosis
[z
];
StrCap
= strlen(pStr
);
for (Ch
= (tNLSCharacter
)0; Ch
< eCH_cnt
; Ch
++)
strreplace
(pStr
, NLS_HtmlCharacterTab
[Ch
], (*CharacterTab
)[Ch
], 2, StrCap
);
}
}
#define MSGPATHNAME "AS_MSGPATH"
/*!------------------------------------------------------------------------
* \fn msg_catalog_open_file(PMsgCat p_catalog, const char *p_file_name, const char *p_exe_path, LongInt msg_id1, LongInt msg_id2)
* \brief open message catalog from file
* \param p_catalog catalog to fill
* \param p_file_name catalog's file name
* \param p_exe_path executable's path
* \param file_msg_id1, file_msg_id1 file magic values to expect
* ------------------------------------------------------------------------ */
void msg_catalog_open_file
(PMsgCat p_catalog
, const char *p_file_name
, const char *p_exe_path
, LongInt msg_id1
, LongInt msg_id2
)
{
as_raw_msg_struct_t
*p_msg_raw
;
String str
;
char *ptr
;
const char *pSep
;
/* find first valid message file: current directory has prio 1: */
p_msg_raw
= open_raw_file_and_check
(p_file_name
, msg_id1
, msg_id2
);
/* if executable path (argv[0]) is given and contains more than just the
plain name, try its path with next-highest prio: */
if (!p_msg_raw
&& p_exe_path
&& *p_exe_path
)
{
#ifdef __CYGWIN32__
for (ptr
= (char*)p_exe_path
; *ptr
!= '\0'; ptr
++)
if (*ptr
== '/') *ptr
= '\\';
#endif
pSep
= strrchr(p_exe_path
, PATHSEP
);
if (pSep
)
{
int path_len
= pSep
- p_exe_path
;
as_snprintf
(str
, sizeof(str
), "%*.*s%s%s", path_len
, path_len
, p_exe_path
, SPATHSEP
, p_file_name
);
p_msg_raw
= open_raw_file_and_check
(str
, msg_id1
, msg_id2
);
}
}
if (!p_msg_raw
)
{
/* AS_MSGPATH has priority over.. */
ptr
= getenv(MSGPATHNAME
);
if (ptr
)
{
as_snprintf
(str
, sizeof(str
), "%s%c%s", ptr
, PATHSEP
, p_file_name
);
p_msg_raw
= open_raw_file_and_check
(str
, msg_id1
, msg_id2
);
}
/* ...PATH: */
else
{
ptr
= getenv("PATH");
if (ptr
)
{
String Dest
;
int Result
;
#ifdef __CYGWIN32__
DeCygWinDirList
(pCopy
);
#endif
Result
= FSearch
(Dest
, sizeof(Dest
), p_file_name
, NULL
, ptr
);
p_msg_raw
= Result
? NULL
: open_raw_file_and_check
(Dest
, msg_id1
, msg_id2
);
/* If we were found via PATH (no slashes in argv[0]/p_exe_path), look up this
path and replace bin/ with lib/ for 'companion path': */
if (!p_msg_raw
&& !strrchr(p_exe_path
, PATHSEP
) && !FSearch
(Dest
, sizeof(Dest
), p_exe_path
, NULL
, ptr
))
{
char *pSep2
;
strreplace
(Dest
, SPATHSEP
"bin", SPATHSEP
"lib", 0, sizeof(Dest
));
pSep2
= strrchr(Dest
, PATHSEP
);
if (pSep2
)
*pSep2
= '\0';
strmaxcat
(Dest
, SPATHSEP
, sizeof(Dest
));
strmaxcat
(Dest
, p_file_name
, sizeof(Dest
));
p_msg_raw
= open_raw_file_and_check
(Dest
, msg_id1
, msg_id2
);
}
}
}
}
#ifdef LIBDIR
if (!p_msg_raw
)
{
as_snprintf
(str
, sizeof(str
), "%s%c%s", LIBDIR
, PATHSEP
, p_file_name
);
p_msg_raw
= open_raw_file_and_check
(str
, msg_id1
, msg_id2
);
}
#endif
if (!p_msg_raw
)
{
as_snprintf
(str
, sizeof(str
), EOpenMsg
, p_file_name
);
error
(str
);
}
parse_raw_file
(p_catalog
, p_msg_raw
);
free_raw
(p_msg_raw
);
}
/*!------------------------------------------------------------------------
* \fn msg_catalog_open_buffer(PMsgCat p_catalog, const unsigned char *p_buffer, size_t buffer_size, LongInt msg_id1, LongInt msg_id2)
* \brief open message catalog from file
* \param p_catalog catalog to fill
* \param p_buffer buffer containing catalog
* \param buffer_size buffer's size
* \param msg_id1, msg_id1 file magic values to expect
* ------------------------------------------------------------------------ */
void msg_catalog_open_buffer
(PMsgCat p_catalog
, const unsigned char *p_buffer
, size_t buffer_size
, LongInt msg_id1
, LongInt msg_id2
)
{
as_raw_msg_struct_t
*p_raw
= (as_raw_msg_struct_t
*)calloc(1, sizeof(*p_raw
));
p_raw
->p_data
= (Byte
*)p_buffer
;
p_raw
->data_allocated
= False
;
p_raw
->data_length
= buffer_size
;
p_raw
->rd_ptr
= 0;
if (!check_header_raw
(p_raw
, msg_id1
, msg_id2
))
{
fprintf(stderr
, "message catalog handling: error: catalog'y buffer has invalid format or is out of date\n");
exit(255);
}
parse_raw_file
(p_catalog
, p_raw
);
free_raw
(p_raw
);
}
/*!------------------------------------------------------------------------
* \fn nlmessages_init_file(const char *p_file_name, char *p_exe_path, LongInt msg_id1, LongInt msg_id2)
* \brief module initialization, load default catalog from file
* \param p_file_name catalog's file name
* \param p_exe_path executable's path
* \param msg_id1, msg_id2 catalog file magic to expect
* ------------------------------------------------------------------------ */
void nlmessages_init_file
(const char *p_file_name
, char *p_exe_path
, LongInt msg_id1
, LongInt msg_id2
)
{
msg_catalog_open_file
(&DefaultCatalog
, p_file_name
, p_exe_path
, msg_id1
, msg_id2
);
}
/*!------------------------------------------------------------------------
* \fn nlmessages_init_buffer(const unsigned char *p_buffer, size_t buffer_size, LongInt msg_id1, LongInt msg_id2)
* \brief module initialization, load default catalog from buffer
* \param p_buffer buffer containing catalog
* \param buffer_size buffer's size
* \param msg_id1, msg_id2 catalog file magic to expect
* ------------------------------------------------------------------------ */
void nlmessages_init_buffer
(const unsigned char *p_buffer
, size_t buffer_size
, LongInt msg_id1
, LongInt msg_id2
)
{
msg_catalog_open_buffer
(&DefaultCatalog
, p_buffer
, buffer_size
, msg_id1
, msg_id2
);
}