mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 22:02:58 -06:00
2140 lines
56 KiB
C
2140 lines
56 KiB
C
/*====================================================================*
|
|
- Copyright (C) 2001 Leptonica. All rights reserved.
|
|
- This software is distributed in the hope that it will be
|
|
- useful, but with NO WARRANTY OF ANY KIND.
|
|
- No author or distributor accepts responsibility to anyone for the
|
|
- consequences of using this software, or for whether it serves any
|
|
- particular purpose or works at all, unless he or she says so in
|
|
- writing. Everyone is granted permission to copy, modify and
|
|
- redistribute this source code, for commercial or non-commercial
|
|
- purposes, with the following restrictions: (1) the origin of this
|
|
- source code must not be misrepresented; (2) modified versions must
|
|
- be plainly marked as such; and (3) this notice may not be removed
|
|
- or altered from any source or modified source distribution.
|
|
*====================================================================*/
|
|
|
|
|
|
/*
|
|
* utils.c
|
|
*
|
|
* Error, warning and info procs; all invoked by macros
|
|
* l_int32 returnErrorInt()
|
|
* l_float32 returnErrorFloat()
|
|
* void *returnErrorPtr()
|
|
* void l_error()
|
|
* void l_errorString()
|
|
* void l_errorInt()
|
|
* void l_errorFloat()
|
|
* void l_warning()
|
|
* void l_warningString()
|
|
* void l_warningInt()
|
|
* void l_warningInt2()
|
|
* void l_warningFloat()
|
|
* void l_warningFloat2()
|
|
* void l_info()
|
|
* void l_infoString()
|
|
* void l_infoInt()
|
|
* void l_infoInt2()
|
|
* void l_infoFloat()
|
|
* void l_infoFloat2()
|
|
*
|
|
* Safe string procs
|
|
* char *stringNew()
|
|
* l_int32 stringReplace()
|
|
* char *stringJoin()
|
|
* char *stringReverse()
|
|
* char *strtokSafe()
|
|
* l_int32 stringSplitOnToken()
|
|
*
|
|
* Find and replace string procs
|
|
* char *stringRemoveChars()
|
|
* l_int32 stringFindSubstr()
|
|
* char *stringReplaceSubstr()
|
|
* char *stringReplaceEachSubstr()
|
|
* l_int32 arrayFindSequence()
|
|
*
|
|
* Safe realloc
|
|
* void *reallocNew()
|
|
*
|
|
* Read and write between file and memory
|
|
* l_uint8 *arrayRead()
|
|
* l_uint8 *arrayReadStream()
|
|
* l_int32 nbytesInFile()
|
|
* l_int32 fnbytesInFile()
|
|
* l_int32 arrayWrite()
|
|
*
|
|
* Copy in memory
|
|
* l_uint8 *arrayCopy()
|
|
*
|
|
* File copy operations
|
|
* l_int32 fileCopy()
|
|
* l_int32 fileConcatenate()
|
|
* l_int32 fileAppendString()
|
|
*
|
|
* Test files for equivalence
|
|
* l_int32 filesAreIdentical()
|
|
*
|
|
* Byte-swapping data conversion
|
|
* l_uint16 convertOnBigEnd16()
|
|
* l_uint32 convertOnBigEnd32()
|
|
* l_uint16 convertOnLittleEnd16()
|
|
* l_uint32 convertOnLittleEnd32()
|
|
*
|
|
* File opening
|
|
* FILE *fopenReadStream()
|
|
*
|
|
* File name operations
|
|
* l_int32 splitPathAtDirectory()
|
|
* l_int32 splitPathAtExtension()
|
|
* char *genPathname()
|
|
* char *genTempFilename()
|
|
* l_int32 extractNumberFromFilename()
|
|
*
|
|
* Version number
|
|
* char *getLeptonlibVersion()
|
|
*
|
|
* Timing
|
|
* void startTimer()
|
|
* l_float32 stopTimer()
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#if COMPILER_MSVC
|
|
#include <windows.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
#include "allheaders.h"
|
|
|
|
#if COMPILER_MSVC
|
|
static const char sepchar = '\\';
|
|
#else
|
|
static const char sepchar = '/';
|
|
#endif
|
|
|
|
|
|
/*----------------------------------------------------------------------*
|
|
* Error, warning and info message procs *
|
|
* *
|
|
* --------------------- N.B. --------------------- *
|
|
* *
|
|
* (1) These functions all print messages to stderr. *
|
|
* *
|
|
* (2) They must be invoked only by macros, which are in *
|
|
* environ.h, so that the print output can be disabled *
|
|
* at compile time, using -DNO_CONSOLE_IO. *
|
|
* *
|
|
*----------------------------------------------------------------------*/
|
|
/*!
|
|
* returnErrorInt()
|
|
*
|
|
* Input: msg (error message)
|
|
* procname
|
|
* ival (return val)
|
|
* Return: ival (typically 1)
|
|
*/
|
|
l_int32
|
|
returnErrorInt(const char *msg,
|
|
const char *procname,
|
|
l_int32 ival)
|
|
{
|
|
fprintf(stderr, "Error in %s: %s\n", procname, msg);
|
|
return ival;
|
|
}
|
|
|
|
|
|
/*!
|
|
* returnErrorFloat()
|
|
*
|
|
* Input: msg (error message)
|
|
* procname
|
|
* fval (return val)
|
|
* Return: fval
|
|
*/
|
|
l_float32
|
|
returnErrorFloat(const char *msg,
|
|
const char *procname,
|
|
l_float32 fval)
|
|
{
|
|
fprintf(stderr, "Error in %s: %s\n", procname, msg);
|
|
return fval;
|
|
}
|
|
|
|
|
|
/*!
|
|
* returnErrorPtr()
|
|
*
|
|
* Input: msg (error message)
|
|
* procname
|
|
* pval (return val)
|
|
* Return: pval (typically null)
|
|
*/
|
|
void *
|
|
returnErrorPtr(const char *msg,
|
|
const char *procname,
|
|
void *pval)
|
|
{
|
|
fprintf(stderr, "Error in %s: %s\n", procname, msg);
|
|
return pval;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_error()
|
|
*
|
|
* Input: msg (error message)
|
|
* procname
|
|
*/
|
|
void
|
|
l_error(const char *msg,
|
|
const char *procname)
|
|
{
|
|
fprintf(stderr, "Error in %s: %s\n", procname, msg);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_errorString()
|
|
*
|
|
* Input: msg (error message; must include '%s')
|
|
* procname
|
|
* str (embedded in error message via %s)
|
|
*/
|
|
void
|
|
l_errorString(const char *msg,
|
|
const char *procname,
|
|
const char *str)
|
|
{
|
|
l_int32 bufsize;
|
|
char *charbuf;
|
|
|
|
if (!msg || !procname || !str) {
|
|
L_ERROR("msg, procname or str not defined in l_errorString()",
|
|
procname);
|
|
return;
|
|
}
|
|
|
|
bufsize = strlen(msg) + strlen(procname) + 128;
|
|
if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) {
|
|
L_ERROR("charbuf not made in l_errorString()", procname);
|
|
return;
|
|
}
|
|
|
|
sprintf(charbuf, "Error in %s: %s\n", procname, msg);
|
|
fprintf(stderr, charbuf, str);
|
|
|
|
FREE(charbuf);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_errorInt()
|
|
*
|
|
* Input: msg (error message; must include '%d')
|
|
* procname
|
|
* ival (embedded in error message via %d)
|
|
*/
|
|
void
|
|
l_errorInt(const char *msg,
|
|
const char *procname,
|
|
l_int32 ival)
|
|
{
|
|
l_int32 bufsize;
|
|
char *charbuf;
|
|
|
|
if (!msg || !procname) {
|
|
L_ERROR("msg or procname not defined in l_errorInt()", procname);
|
|
return;
|
|
}
|
|
|
|
bufsize = strlen(msg) + strlen(procname) + 128;
|
|
if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) {
|
|
L_ERROR("charbuf not made in l_errorInt()", procname);
|
|
return;
|
|
}
|
|
|
|
sprintf(charbuf, "Error in %s: %s\n", procname, msg);
|
|
fprintf(stderr, charbuf, ival);
|
|
|
|
FREE(charbuf);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_errorFloat()
|
|
*
|
|
* Input: msg (error message; must include '%f')
|
|
* procname
|
|
* fval (embedded in error message via %f)
|
|
*/
|
|
void
|
|
l_errorFloat(const char *msg,
|
|
const char *procname,
|
|
l_float32 fval)
|
|
{
|
|
l_int32 bufsize;
|
|
char *charbuf;
|
|
|
|
if (!msg || !procname) {
|
|
L_ERROR("msg or procname not defined in l_errorFloat()", procname);
|
|
return;
|
|
}
|
|
|
|
bufsize = strlen(msg) + strlen(procname) + 128;
|
|
if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) {
|
|
L_ERROR("charbuf not made in l_errorFloat()", procname);
|
|
return;
|
|
}
|
|
|
|
sprintf(charbuf, "Error in %s: %s\n", procname, msg);
|
|
fprintf(stderr, charbuf, fval);
|
|
|
|
FREE(charbuf);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_warning()
|
|
*
|
|
* Input: msg (warning message)
|
|
* procname
|
|
*/
|
|
void
|
|
l_warning(const char *msg,
|
|
const char *procname)
|
|
{
|
|
fprintf(stderr, "Warning in %s: %s\n", procname, msg);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_warningString()
|
|
*
|
|
* Input: msg (warning message; must include '%s')
|
|
* procname
|
|
* str (embedded in warning message via %s)
|
|
*/
|
|
void
|
|
l_warningString(const char *msg,
|
|
const char *procname,
|
|
const char *str)
|
|
{
|
|
l_int32 bufsize;
|
|
char *charbuf;
|
|
|
|
if (!msg || !procname || !str) {
|
|
L_ERROR("msg, procname or str not defined in l_warningString()",
|
|
procname);
|
|
return;
|
|
}
|
|
|
|
bufsize = strlen(msg) + strlen(procname) + 128;
|
|
if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) {
|
|
L_ERROR("charbuf not made in l_warningString()", procname);
|
|
return;
|
|
}
|
|
|
|
sprintf(charbuf, "Warning in %s: %s\n", procname, msg);
|
|
fprintf(stderr, charbuf, str);
|
|
|
|
FREE(charbuf);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_warningInt()
|
|
*
|
|
* Input: msg (warning message; must include '%d')
|
|
* procname
|
|
* ival (embedded in warning message via %d)
|
|
*/
|
|
void
|
|
l_warningInt(const char *msg,
|
|
const char *procname,
|
|
l_int32 ival)
|
|
{
|
|
l_int32 bufsize;
|
|
char *charbuf;
|
|
|
|
if (!msg || !procname) {
|
|
L_ERROR("msg or procname not defined in l_warningInt()", procname);
|
|
return;
|
|
}
|
|
|
|
bufsize = strlen(msg) + strlen(procname) + 128;
|
|
if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) {
|
|
L_ERROR("charbuf not made in l_warningInt()", procname);
|
|
return;
|
|
}
|
|
|
|
sprintf(charbuf, "Warning in %s: %s\n", procname, msg);
|
|
fprintf(stderr, charbuf, ival);
|
|
|
|
FREE(charbuf);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_warningInt2()
|
|
*
|
|
* Input: msg (warning message; must include '%d')
|
|
* procname
|
|
* ival1, ival2 (two args, embedded in message via %d)
|
|
*/
|
|
void
|
|
l_warningInt2(const char *msg,
|
|
const char *procname,
|
|
l_int32 ival1,
|
|
l_int32 ival2)
|
|
{
|
|
l_int32 bufsize;
|
|
char *charbuf;
|
|
|
|
if (!msg || !procname) {
|
|
L_ERROR("msg or procname not defined in l_warningInt2()", procname);
|
|
return;
|
|
}
|
|
|
|
bufsize = strlen(msg) + strlen(procname) + 128;
|
|
if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) {
|
|
L_ERROR("charbuf not made in l_warningInt()", procname);
|
|
return;
|
|
}
|
|
|
|
sprintf(charbuf, "Warning in %s: %s\n", procname, msg);
|
|
fprintf(stderr, charbuf, ival1, ival2);
|
|
|
|
FREE(charbuf);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_warningFloat()
|
|
*
|
|
* Input: msg (warning message; must include '%f')
|
|
* procname
|
|
* fval (embedded in warning message via %f)
|
|
*/
|
|
void
|
|
l_warningFloat(const char *msg,
|
|
const char *procname,
|
|
l_float32 fval)
|
|
{
|
|
l_int32 bufsize;
|
|
char *charbuf;
|
|
|
|
if (!msg || !procname) {
|
|
L_ERROR("msg or procname not defined in l_warningFloat()", procname);
|
|
return;
|
|
}
|
|
|
|
bufsize = strlen(msg) + strlen(procname) + 128;
|
|
if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) {
|
|
L_ERROR("charbuf not made in l_warningFloat()", procname);
|
|
return;
|
|
}
|
|
|
|
sprintf(charbuf, "Warning in %s: %s\n", procname, msg);
|
|
fprintf(stderr, charbuf, fval);
|
|
|
|
FREE(charbuf);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_warningFloat2()
|
|
*
|
|
* Input: msg (warning message; must include '%f')
|
|
* procname
|
|
* fval1, fval2 (two args, embedded in message via %f)
|
|
*/
|
|
void
|
|
l_warningFloat2(const char *msg,
|
|
const char *procname,
|
|
l_float32 fval1,
|
|
l_float32 fval2)
|
|
{
|
|
l_int32 bufsize;
|
|
char *charbuf;
|
|
|
|
if (!msg || !procname) {
|
|
L_ERROR("msg or procname not defined in l_warningFloat2()", procname);
|
|
return;
|
|
}
|
|
|
|
bufsize = strlen(msg) + strlen(procname) + 128;
|
|
if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) {
|
|
L_ERROR("charbuf not made in l_warningFloat()", procname);
|
|
return;
|
|
}
|
|
|
|
sprintf(charbuf, "Warning in %s: %s\n", procname, msg);
|
|
fprintf(stderr, charbuf, fval1, fval2);
|
|
|
|
FREE(charbuf);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_info()
|
|
*
|
|
* Input: msg (info message)
|
|
* procname
|
|
*/
|
|
void
|
|
l_info(const char *msg,
|
|
const char *procname)
|
|
{
|
|
fprintf(stderr, "Info in %s: %s\n", procname, msg);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_infoString()
|
|
*
|
|
* Input: msg (info message; must include '%s')
|
|
* procname
|
|
* str (embedded in warning message via %s)
|
|
*/
|
|
void
|
|
l_infoString(const char *msg,
|
|
const char *procname,
|
|
const char *str)
|
|
{
|
|
l_int32 bufsize;
|
|
char *charbuf;
|
|
|
|
if (!msg || !procname || !str) {
|
|
L_ERROR("msg, procname or str not defined in l_infoString()", procname);
|
|
return;
|
|
}
|
|
|
|
bufsize = strlen(msg) + strlen(procname) + 128;
|
|
if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) {
|
|
L_ERROR("charbuf not made in l_infoString()", procname);
|
|
return;
|
|
}
|
|
|
|
sprintf(charbuf, "Info in %s: %s\n", procname, msg);
|
|
fprintf(stderr, charbuf, str);
|
|
|
|
FREE(charbuf);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_infoInt()
|
|
*
|
|
* Input: msg (info message; must include '%d')
|
|
* procname
|
|
* ival (embedded in info message via %d)
|
|
*/
|
|
void
|
|
l_infoInt(const char *msg,
|
|
const char *procname,
|
|
l_int32 ival)
|
|
{
|
|
l_int32 bufsize;
|
|
char *charbuf;
|
|
|
|
if (!msg || !procname) {
|
|
L_ERROR("msg or procname not defined in l_infoInt()", procname);
|
|
return;
|
|
}
|
|
|
|
bufsize = strlen(msg) + strlen(procname) + 128;
|
|
if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) {
|
|
L_ERROR("charbuf not made in l_infoInt()", procname);
|
|
return;
|
|
}
|
|
|
|
sprintf(charbuf, "Info in %s: %s\n", procname, msg);
|
|
fprintf(stderr, charbuf, ival);
|
|
|
|
FREE(charbuf);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_infoInt2()
|
|
*
|
|
* Input: msg (info message; must include two '%d')
|
|
* procname
|
|
* ival1, ival2 (two args, embedded in info message via %d)
|
|
*/
|
|
void
|
|
l_infoInt2(const char *msg,
|
|
const char *procname,
|
|
l_int32 ival1,
|
|
l_int32 ival2)
|
|
{
|
|
l_int32 bufsize;
|
|
char *charbuf;
|
|
|
|
if (!msg || !procname) {
|
|
L_ERROR("msg or procname not defined in l_infoInt2()", procname);
|
|
return;
|
|
}
|
|
|
|
bufsize = strlen(msg) + strlen(procname) + 128;
|
|
if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) {
|
|
L_ERROR("charbuf not made in l_infoInt2()", procname);
|
|
return;
|
|
}
|
|
|
|
sprintf(charbuf, "Info in %s: %s\n", procname, msg);
|
|
fprintf(stderr, charbuf, ival1, ival2);
|
|
|
|
FREE(charbuf);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_infoFloat()
|
|
*
|
|
* Input: msg (info message; must include '%f')
|
|
* procname
|
|
* fval (embedded in info message via %f)
|
|
*/
|
|
void
|
|
l_infoFloat(const char *msg,
|
|
const char *procname,
|
|
l_float32 fval)
|
|
{
|
|
l_int32 bufsize;
|
|
char *charbuf;
|
|
|
|
if (!msg || !procname) {
|
|
L_ERROR("msg or procname not defined in l_infoFloat()", procname);
|
|
return;
|
|
}
|
|
|
|
bufsize = strlen(msg) + strlen(procname) + 128;
|
|
if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) {
|
|
L_ERROR("charbuf not made in l_infoFloat()", procname);
|
|
return;
|
|
}
|
|
|
|
sprintf(charbuf, "Info in %s: %s\n", procname, msg);
|
|
fprintf(stderr, charbuf, fval);
|
|
|
|
FREE(charbuf);
|
|
return;
|
|
}
|
|
|
|
|
|
/*!
|
|
* l_infoFloat2()
|
|
*
|
|
* Input: msg (info message; must include two '%f')
|
|
* procname
|
|
* fval1, fval2 (two args, embedded in info message via %f)
|
|
*/
|
|
void
|
|
l_infoFloat2(const char *msg,
|
|
const char *procname,
|
|
l_float32 fval1,
|
|
l_float32 fval2)
|
|
{
|
|
l_int32 bufsize;
|
|
char *charbuf;
|
|
|
|
if (!msg || !procname) {
|
|
L_ERROR("msg or procname not defined in l_infoFloat2()", procname);
|
|
return;
|
|
}
|
|
|
|
bufsize = strlen(msg) + strlen(procname) + 128;
|
|
if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) {
|
|
L_ERROR("charbuf not made in l_infoFloat()", procname);
|
|
return;
|
|
}
|
|
|
|
sprintf(charbuf, "Info in %s: %s\n", procname, msg);
|
|
fprintf(stderr, charbuf, fval1, fval2);
|
|
|
|
FREE(charbuf);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------*
|
|
* Safe string operations *
|
|
*--------------------------------------------------------------------*/
|
|
/*!
|
|
* stringNew()
|
|
*
|
|
* Input: src string
|
|
* Return: dest copy of src string, or null on error
|
|
*/
|
|
char *
|
|
stringNew(const char *src)
|
|
{
|
|
char *dest;
|
|
|
|
PROCNAME("stringNew");
|
|
|
|
if (!src)
|
|
return (char *)ERROR_PTR("src not defined", procName, NULL);
|
|
|
|
if ((dest = (char *)CALLOC(strlen(src) + 2, sizeof(char))) == NULL)
|
|
return (char *)ERROR_PTR("dest not made", procName, NULL);
|
|
strcpy(dest, src);
|
|
|
|
return dest;
|
|
}
|
|
|
|
|
|
/*!
|
|
* stringReplace()
|
|
*
|
|
* Input: &dest string (<return> copy)
|
|
* src string
|
|
* Return: 0 if OK; 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) Frees any existing dest string
|
|
* (2) Puts a copy of src string in the dest
|
|
* (3) If either or both strings are null, does the reasonable thing.
|
|
*/
|
|
l_int32
|
|
stringReplace(char **pdest,
|
|
const char *src)
|
|
{
|
|
char *scopy;
|
|
|
|
PROCNAME("stringReplace");
|
|
|
|
if (!pdest)
|
|
return ERROR_INT("pdest not defined", procName, 1);
|
|
|
|
if (*pdest)
|
|
FREE(*pdest);
|
|
|
|
if (src) {
|
|
if ((scopy = (char *)CALLOC(strlen(src) + 2, sizeof(char))) == NULL)
|
|
return ERROR_INT("scopy not made", procName, 1);
|
|
strcpy(scopy, src);
|
|
*pdest = scopy;
|
|
}
|
|
else
|
|
*pdest = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* stringJoin()
|
|
*
|
|
* Input: src1 string (<optional>)
|
|
* src2 string (<optional>)
|
|
* Return: concatenated string, or null on error
|
|
*
|
|
* Notes:
|
|
* (1) This is the safe version of strcat; it makes a new string.
|
|
* (2) It is not an error if either or both of the strings
|
|
* are empty, or if either or both the pointers are null.
|
|
*/
|
|
char *
|
|
stringJoin(const char *src1,
|
|
const char *src2)
|
|
{
|
|
char *dest;
|
|
l_int32 srclen1, srclen2, destlen;
|
|
|
|
PROCNAME("stringJoin");
|
|
|
|
srclen1 = srclen2 = 0;
|
|
if (src1)
|
|
srclen1 = strlen(src1);
|
|
if (src2)
|
|
srclen2 = strlen(src2);
|
|
destlen = srclen1 + srclen2 + 3;
|
|
|
|
if ((dest = (char *)CALLOC(destlen, sizeof(char))) == NULL)
|
|
return (char *)ERROR_PTR("calloc fail for dest", procName, NULL);
|
|
|
|
if (src1)
|
|
strcpy(dest, src1);
|
|
if (src2)
|
|
strncat(dest, src2, srclen2);
|
|
return dest;
|
|
}
|
|
|
|
|
|
/*!
|
|
* stringReverse()
|
|
*
|
|
* Input: src (string)
|
|
* Return: dest (newly-allocated reversed string)
|
|
*/
|
|
char *
|
|
stringReverse(const char *src)
|
|
{
|
|
char *dest;
|
|
l_int32 i, len;
|
|
|
|
PROCNAME("stringReverse");
|
|
|
|
if (!src)
|
|
return (char *)ERROR_PTR("src not defined", procName, NULL);
|
|
len = strlen(src);
|
|
if ((dest = (char *)CALLOC(len + 1, sizeof(char))) == NULL)
|
|
return (char *)ERROR_PTR("calloc fail for dest", procName, NULL);
|
|
for (i = 0; i < len; i++)
|
|
dest[i] = src[len - 1 - i];
|
|
|
|
return dest;
|
|
}
|
|
|
|
|
|
/*!
|
|
* strtokSafe()
|
|
*
|
|
* Input: cstr (input string to be sequentially parsed;
|
|
* use NULL after the first call)
|
|
* seps (a string of character separators)
|
|
* &saveptr (<return> ptr to the next char after
|
|
* the last encountered separator)
|
|
* Return: substr (a new string that is copied from the previous
|
|
* saveptr up to but not including the next
|
|
* separator character), or NULL if end of cstr.
|
|
*
|
|
* Notes:
|
|
* (1) This is a thread-safe implementation of strtok.
|
|
* (2) It has the same interface as strtok_r.
|
|
* (3) It differs from strtok_r in usage in two respects:
|
|
* (a) the input string is not altered
|
|
* (b) each returned substring is newly allocated and must
|
|
* be freed after use.
|
|
* (4) Let me repeat that. This is "safe" because the input
|
|
* string is not altered and because each returned string
|
|
* is newly allocated on the heap.
|
|
* (5) It is here because, surprisingly, some C libraries don't
|
|
* include strtok_r.
|
|
* (6) Important usage points:
|
|
* - Input the string to be parsed on the first invocation.
|
|
* - Then input NULL after that; the value returned in saveptr
|
|
* is used in all subsequent calls.
|
|
* (7) This is only slightly slower than strtok_k.
|
|
*/
|
|
char *
|
|
strtokSafe(char *cstr,
|
|
const char *seps,
|
|
char **psaveptr)
|
|
{
|
|
char nextc;
|
|
char *start, *substr;
|
|
l_int32 istart, i, j, nchars;
|
|
|
|
PROCNAME("strtokSafe");
|
|
|
|
if (!seps)
|
|
return (char *)ERROR_PTR("seps not defined", procName, NULL);
|
|
if (!psaveptr)
|
|
return (char *)ERROR_PTR("&saveptr not defined", procName, NULL);
|
|
|
|
if (!cstr)
|
|
start = *psaveptr;
|
|
else
|
|
start = cstr;
|
|
if (!start) /* nothing to do */
|
|
return NULL;
|
|
|
|
/* First time, scan for the first non-sep character */
|
|
istart = 0;
|
|
if (cstr) {
|
|
for (istart = 0;; istart++) {
|
|
if ((nextc = start[istart]) == '\0') {
|
|
*psaveptr = NULL; /* in case caller doesn't check ret value */
|
|
return NULL;
|
|
}
|
|
if (!strchr(seps, nextc))
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Scan through, looking for a sep character; if none is
|
|
* found, 'i' will be at the end of the string. */
|
|
for (i = istart;; i++) {
|
|
if ((nextc = start[i]) == '\0')
|
|
break;
|
|
if (strchr(seps, nextc))
|
|
break;
|
|
}
|
|
|
|
/* Save the substring */
|
|
nchars = i - istart;
|
|
substr = (char *)CALLOC(nchars + 1, sizeof(char));
|
|
strncpy(substr, start + istart, nchars);
|
|
|
|
/* Look for the next non-sep character.
|
|
* If this is the last substring, return a null saveptr. */
|
|
for (j = i;; j++) {
|
|
if ((nextc = start[j]) == '\0') {
|
|
*psaveptr = NULL; /* no more non-sep characters */
|
|
break;
|
|
}
|
|
if (!strchr(seps, nextc)) {
|
|
*psaveptr = start + j; /* start here on next call */
|
|
break;
|
|
}
|
|
}
|
|
|
|
return substr;
|
|
}
|
|
|
|
|
|
/*!
|
|
* stringSplitOnToken()
|
|
*
|
|
* Input: cstr (input string to be split; not altered)
|
|
* seps (a string of character separators)
|
|
* &head (<return> ptr to copy of the input string, up to
|
|
* the first separator token encountered)
|
|
* &tail (<return> ptr to copy of the part of the input string
|
|
* starting with the first non-separator character
|
|
* that occurs after the first separator is found)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) The input string is not altered; all split parts are new strings.
|
|
* (2) The split occurs around the first consecutive sequence of
|
|
* tokens encountered.
|
|
* (3) The head goes from the beginning of the string up to
|
|
* but not including the first token found.
|
|
* (4) The tail contains the second part of the string, starting
|
|
* with the first char in that part that is NOT a token.
|
|
* (5) If no separator token is found, 'head' contains a copy
|
|
* of the input string and 'tail' is null.
|
|
*/
|
|
l_int32
|
|
stringSplitOnToken(char *cstr,
|
|
const char *seps,
|
|
char **phead,
|
|
char **ptail)
|
|
{
|
|
char *saveptr;
|
|
|
|
PROCNAME("stringSplitOnToken");
|
|
|
|
if (!phead)
|
|
return ERROR_INT("&head not defined", procName, 1);
|
|
if (!ptail)
|
|
return ERROR_INT("&tail not defined", procName, 1);
|
|
*phead = *ptail = NULL;
|
|
if (!cstr)
|
|
return ERROR_INT("cstr not defined", procName, 1);
|
|
if (!seps)
|
|
return ERROR_INT("seps not defined", procName, 1);
|
|
|
|
*phead = strtokSafe(cstr, seps, &saveptr);
|
|
if (saveptr)
|
|
*ptail = stringNew(saveptr);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*
|
|
* Find and replace procs *
|
|
*--------------------------------------------------------------------*/
|
|
/*!
|
|
* stringRemoveChars()
|
|
*
|
|
* Input: src (input string; can be of zero length)
|
|
* remchars (string of chars to be removed from src)
|
|
* Return: dest (string with specified chars removed), or null on error
|
|
*/
|
|
char *
|
|
stringRemoveChars(const char *src,
|
|
const char *remchars)
|
|
{
|
|
char ch;
|
|
char *dest;
|
|
l_int32 nsrc, i, k;
|
|
|
|
PROCNAME("stringRemoveChars");
|
|
|
|
if (!src)
|
|
return (char *)ERROR_PTR("src not defined", procName, NULL);
|
|
if (!remchars)
|
|
return stringNew(src);
|
|
|
|
if ((dest = (char *)CALLOC(strlen(src) + 1, sizeof(char))) == NULL)
|
|
return (char *)ERROR_PTR("dest not made", procName, NULL);
|
|
nsrc = strlen(src);
|
|
for (i = 0, k = 0; i < nsrc; i++) {
|
|
ch = src[i];
|
|
if (!strchr(remchars, ch))
|
|
dest[k++] = ch;
|
|
}
|
|
|
|
return dest;
|
|
}
|
|
|
|
|
|
/*!
|
|
* stringFindSubstr()
|
|
*
|
|
* Input: src (input string; can be of zero length)
|
|
* sub (substring to be searched for)
|
|
* &loc (<return optional> location of substring in src)
|
|
* Return: 1 if found; 0 if not found or on error
|
|
*
|
|
* Notes:
|
|
* (1) This is a wrapper around strstr().
|
|
* (2) Both @src and @sub must be defined, and @sub must have
|
|
* length of at least 1.
|
|
* (3) If the substring is not found and loc is returned, it has
|
|
* the value -1.
|
|
*/
|
|
l_int32
|
|
stringFindSubstr(const char *src,
|
|
const char *sub,
|
|
l_int32 *ploc)
|
|
{
|
|
char *ptr;
|
|
|
|
PROCNAME("stringFindSubstr");
|
|
|
|
if (!src)
|
|
return ERROR_INT("src not defined", procName, 0);
|
|
if (!sub)
|
|
return ERROR_INT("sub not defined", procName, 0);
|
|
if (ploc) *ploc = -1;
|
|
if (strlen(sub) == 0)
|
|
return ERROR_INT("substring length 0", procName, 0);
|
|
if (strlen(src) == 0)
|
|
return 0;
|
|
|
|
if ((ptr = (char *)strstr(src, sub)) == NULL) /* not found */
|
|
return 0;
|
|
|
|
if (ploc)
|
|
*ploc = ptr - src;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*!
|
|
* stringReplaceSubstr()
|
|
*
|
|
* Input: src (input string; can be of zero length)
|
|
* sub1 (substring to be replaced)
|
|
* sub2 (substring to put in; can be "")
|
|
* &found (<return optional> 1 if sub1 is found; 0 otherwise)
|
|
* &loc (<return optional> location of ptr after replacement)
|
|
* Return: dest (string with substring replaced), or null if the
|
|
* substring not found or on error.
|
|
*
|
|
* Notes:
|
|
* (1) Replaces the first instance.
|
|
* (2) To only remove sub1, use "" for sub2
|
|
* (3) Returns a new string if sub1 and sub2 are the same.
|
|
* (4) The optional loc is input as the byte offset within the src
|
|
* from which the search starts, and after the search it is the
|
|
* char position in the string of the next character after
|
|
* the substituted string.
|
|
* (5) N.B. If ploc is not null, loc must always be initialized.
|
|
* To search the string from the beginning, set loc = 0.
|
|
*/
|
|
char *
|
|
stringReplaceSubstr(const char *src,
|
|
const char *sub1,
|
|
const char *sub2,
|
|
l_int32 *pfound,
|
|
l_int32 *ploc)
|
|
{
|
|
char *ptr, *dest;
|
|
l_int32 nsrc, nsub1, nsub2, len, npre, loc;
|
|
|
|
PROCNAME("stringReplaceSubstr");
|
|
|
|
if (!src)
|
|
return (char *)ERROR_PTR("src not defined", procName, NULL);
|
|
if (!sub1)
|
|
return (char *)ERROR_PTR("sub1 not defined", procName, NULL);
|
|
if (!sub2)
|
|
return (char *)ERROR_PTR("sub2 not defined", procName, NULL);
|
|
|
|
if (pfound)
|
|
*pfound = 0;
|
|
if (ploc)
|
|
loc = *ploc;
|
|
else
|
|
loc = 0;
|
|
if ((ptr = (char *)strstr(src + loc, sub1)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (pfound)
|
|
*pfound = 1;
|
|
nsrc = strlen(src);
|
|
nsub1 = strlen(sub1);
|
|
nsub2 = strlen(sub2);
|
|
len = nsrc + nsub2 - nsub1;
|
|
if ((dest = (char *)CALLOC(len + 1, sizeof(char))) == NULL)
|
|
return (char *)ERROR_PTR("dest not made", procName, NULL);
|
|
npre = ptr - src;
|
|
memcpy(dest, src, npre);
|
|
strcpy(dest + npre, sub2);
|
|
strcpy(dest + npre + nsub2, ptr + nsub1);
|
|
if (ploc)
|
|
*ploc = npre + nsub2;
|
|
|
|
return dest;
|
|
}
|
|
|
|
|
|
/*!
|
|
* stringReplaceEachSubstr()
|
|
*
|
|
* Input: src (input string; can be of zero length)
|
|
* sub1 (substring to be replaced)
|
|
* sub2 (substring to put in; can be "")
|
|
* &count (<optional return > the number of times that sub1
|
|
* is found in src; 0 if not found)
|
|
* Return: dest (string with substring replaced), or null if the
|
|
* substring not found or on error.
|
|
*
|
|
* Notes:
|
|
* (1) Replaces every instance.
|
|
* (2) To only remove each instance of sub1, use "" for sub2
|
|
* (3) Returns NULL if sub1 and sub2 are the same.
|
|
*/
|
|
char *
|
|
stringReplaceEachSubstr(const char *src,
|
|
const char *sub1,
|
|
const char *sub2,
|
|
l_int32 *pcount)
|
|
{
|
|
char *currstr, *newstr;
|
|
l_int32 loc;
|
|
|
|
PROCNAME("stringReplaceEachSubstr");
|
|
|
|
if (!src)
|
|
return (char *)ERROR_PTR("src not defined", procName, NULL);
|
|
if (!sub1)
|
|
return (char *)ERROR_PTR("sub1 not defined", procName, NULL);
|
|
if (!sub2)
|
|
return (char *)ERROR_PTR("sub2 not defined", procName, NULL);
|
|
|
|
if (pcount)
|
|
*pcount = 0;
|
|
loc = 0;
|
|
if ((newstr = stringReplaceSubstr(src, sub1, sub2, NULL, &loc)) == NULL)
|
|
return NULL;
|
|
|
|
if (pcount)
|
|
(*pcount)++;
|
|
while (1) {
|
|
currstr = newstr;
|
|
newstr = stringReplaceSubstr(currstr, sub1, sub2, NULL, &loc);
|
|
if (!newstr)
|
|
return currstr;
|
|
FREE(currstr);
|
|
if (pcount)
|
|
(*pcount)++;
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
* arrayFindSequence()
|
|
*
|
|
* Input: data (byte array)
|
|
* datalen (length of data, in bytes)
|
|
* sequence (subarray of bytes to find in data)
|
|
* seqlen (length of sequence, in bytes)
|
|
* &offset (return> offset from beginning of
|
|
* data where the sequence begins)
|
|
* &found (<return> 1 if sequence is found; 0 otherwise)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) The byte arrays 'data' and 'sequence' are not C strings,
|
|
* as they can contain null bytes. Therefore, for each
|
|
* we must give the length of the array.
|
|
* (2) This searches for the first occurrence in 'data' of
|
|
* the first 'seqlen' bytes of 'sequence'. The parameter 'seqlen'
|
|
* must not exceed the actual length of the 'sequence' byte array.
|
|
* (3) If the sequence is not found, the offset will be set to -1.
|
|
*/
|
|
l_int32
|
|
arrayFindSequence(const l_uint8 *data,
|
|
l_int32 datalen,
|
|
const l_uint8 *sequence,
|
|
l_int32 seqlen,
|
|
l_int32 *poffset,
|
|
l_int32 *pfound)
|
|
{
|
|
l_int32 i, j, found, lastpos;
|
|
|
|
PROCNAME("arrayFindSequence");
|
|
|
|
if (!data || !sequence)
|
|
return ERROR_INT("data & sequence not both defined", procName, 1);
|
|
if (!poffset || !pfound)
|
|
return ERROR_INT("&offset and &found not both defined", procName, 1);
|
|
|
|
*pfound = 0;
|
|
*poffset = -1;
|
|
lastpos = datalen - seqlen + 1;
|
|
found = 0;
|
|
for (i = 0; i < lastpos; i++) {
|
|
for (j = 0; j < seqlen; j++) {
|
|
if (data[i + j] != sequence[j])
|
|
break;
|
|
if (j == seqlen - 1)
|
|
found = 1;
|
|
}
|
|
if (found)
|
|
break;
|
|
}
|
|
|
|
if (found) {
|
|
*pfound = 1;
|
|
*poffset = i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*
|
|
* Safe realloc *
|
|
*--------------------------------------------------------------------*/
|
|
/*!
|
|
* reallocNew()
|
|
*
|
|
* Input: &indata (<optional>; nulls indata)
|
|
* size of input data to be copied (bytes)
|
|
* size of data to be reallocated (bytes)
|
|
* Return: ptr to new data, or null on error
|
|
*
|
|
* Action: !N.B. (3) and (4)!
|
|
* (1) Allocates memory, initialized to 0
|
|
* (2) Copies as much of the input data as possible
|
|
* to the new block, truncating the copy if necessary
|
|
* (3) Frees the input data
|
|
* (4) Zeroes the input data ptr
|
|
*
|
|
* Notes:
|
|
* (1) If newsize <=0, just frees input data and nulls ptr
|
|
* (2) If input ptr is null, just callocs new memory
|
|
* (3) This differs from realloc in that it always allocates
|
|
* new memory (if newsize > 0) and initializes it to 0,
|
|
* it requires the amount of old data to be copied,
|
|
* and it takes the address of the input ptr and
|
|
* nulls the handle.
|
|
*/
|
|
void *
|
|
reallocNew(void **pindata,
|
|
l_int32 oldsize,
|
|
l_int32 newsize)
|
|
{
|
|
l_int32 minsize;
|
|
void *indata;
|
|
void *newdata;
|
|
|
|
PROCNAME("reallocNew");
|
|
|
|
if (!pindata)
|
|
return ERROR_PTR("input data not defined", procName, NULL);
|
|
indata = *pindata;
|
|
|
|
if (newsize <= 0) { /* nonstandard usage */
|
|
if (indata) {
|
|
FREE(indata);
|
|
*pindata = NULL;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
if (!indata) /* nonstandard usage */
|
|
{
|
|
if ((newdata = (void *)CALLOC(1, newsize)) == NULL)
|
|
return ERROR_PTR("newdata not made", procName, NULL);
|
|
return newdata;
|
|
}
|
|
|
|
/* Standard usage */
|
|
if ((newdata = (void *)CALLOC(1, newsize)) == NULL)
|
|
return ERROR_PTR("newdata not made", procName, NULL);
|
|
minsize = L_MIN(oldsize, newsize);
|
|
memcpy((char *)newdata, (char *)indata, minsize);
|
|
|
|
FREE(indata);
|
|
*pindata = NULL;
|
|
|
|
return newdata;
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------*
|
|
* Reading bytes from file *
|
|
*--------------------------------------------------------------------*/
|
|
/*!
|
|
* arrayRead()
|
|
*
|
|
* Input: filename
|
|
* &nbytes (<return> number of bytes read)
|
|
* Return: array, or null on error
|
|
*/
|
|
l_uint8 *
|
|
arrayRead(const char *fname,
|
|
l_int32 *pnbytes)
|
|
{
|
|
l_uint8 *data;
|
|
FILE *fp;
|
|
|
|
PROCNAME("arrayRead");
|
|
|
|
if (!fname)
|
|
return (l_uint8 *)ERROR_PTR("fname not defined", procName, NULL);
|
|
if (!pnbytes)
|
|
return (l_uint8 *)ERROR_PTR("pnbytes not defined", procName, NULL);
|
|
*pnbytes = 0;
|
|
|
|
if ((fp = fopen(fname, "rb")) == NULL)
|
|
return (l_uint8 *)ERROR_PTR("file stream not opened", procName, NULL);
|
|
|
|
data = arrayReadStream(fp, pnbytes);
|
|
fclose(fp);
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
/*!
|
|
* arrayReadStream()
|
|
*
|
|
* Input: stream
|
|
* &nbytes (<return> number of bytes read)
|
|
* Return: null-terminated array, or null on error
|
|
* (reading 0 bytes is not an error)
|
|
*
|
|
* Notes:
|
|
* (1) N.B.: as a side effect, this always re-positions the
|
|
* stream ptr to the beginning of the file.
|
|
*/
|
|
l_uint8 *
|
|
arrayReadStream(FILE *fp,
|
|
l_int32 *pnbytes)
|
|
{
|
|
l_uint8 *data;
|
|
|
|
PROCNAME("arrayReadStream");
|
|
|
|
if (!fp)
|
|
return (l_uint8 *)ERROR_PTR("stream not defined", procName, NULL);
|
|
if (!pnbytes)
|
|
return (l_uint8 *)ERROR_PTR("ptr to nbytes not defined", procName, NULL);
|
|
|
|
*pnbytes = fnbytesInFile(fp);
|
|
|
|
if ((data = (l_uint8 *)CALLOC(1, *pnbytes + 1)) == NULL)
|
|
return (l_uint8 *)ERROR_PTR("CALLOC fail for data", procName, NULL);
|
|
fread(data, *pnbytes, 1, fp);
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
/*!
|
|
* nbytesInFile()
|
|
*
|
|
* Input: filename
|
|
* Return: nbytes in file; 0 on error
|
|
*/
|
|
l_int32
|
|
nbytesInFile(const char *filename)
|
|
{
|
|
l_int32 nbytes;
|
|
FILE *fp;
|
|
|
|
PROCNAME("nbytesInFile");
|
|
|
|
if (!filename)
|
|
return ERROR_INT("filename not defined", procName, 0);
|
|
if ((fp = fopen(filename, "rb")) == NULL)
|
|
return ERROR_INT("stream not opened", procName, 0);
|
|
nbytes = fnbytesInFile(fp);
|
|
fclose(fp);
|
|
return nbytes;
|
|
}
|
|
|
|
|
|
/*!
|
|
* fnbytesInFile()
|
|
*
|
|
* Input: file stream
|
|
* Return: nbytes in file; 0 on error
|
|
*/
|
|
l_int32
|
|
fnbytesInFile(FILE *fp)
|
|
{
|
|
l_int32 nbytes, pos;
|
|
|
|
PROCNAME("fnbytesInFile");
|
|
|
|
if (!fp)
|
|
return ERROR_INT("stream not open", procName, 0);
|
|
|
|
pos = ftell(fp); /* initial position */
|
|
fseek(fp, 0, SEEK_END); /* EOF */
|
|
nbytes = ftell(fp);
|
|
fseek(fp, 0, pos); /* back to initial position */
|
|
return nbytes;
|
|
}
|
|
|
|
|
|
/*!
|
|
* arrayWrite()
|
|
*
|
|
* Input: filename (output)
|
|
* operation ("w" for write; "a" for append)
|
|
* data (binary data to be written)
|
|
* nbytes (size of data array)
|
|
* Return: 0 if OK; 1 on error
|
|
*/
|
|
l_int32
|
|
arrayWrite(const char *filename,
|
|
const char *operation,
|
|
void *data,
|
|
l_int32 nbytes)
|
|
{
|
|
FILE *fp;
|
|
char actualOperation[20];
|
|
|
|
PROCNAME("arrayWrite");
|
|
|
|
if (!filename)
|
|
return ERROR_INT("filename not defined", procName, 1);
|
|
if (!operation)
|
|
return ERROR_INT("operation not defined", procName, 1);
|
|
if (!data)
|
|
return ERROR_INT("data not defined", procName, 1);
|
|
if (nbytes <= 0)
|
|
return ERROR_INT("nbytes must be > 0", procName, 1);
|
|
|
|
if (!strcmp(operation, "w") && !strcmp(operation, "a"))
|
|
return ERROR_INT("operation not one of {'w','a'}", procName, 1);
|
|
|
|
strncpy(actualOperation, operation, 2);
|
|
strncat(actualOperation, "b", 2); /* for windows */
|
|
if ((fp = fopen(filename, actualOperation)) == NULL)
|
|
return ERROR_INT("stream not opened", procName, 1);
|
|
fwrite(data, 1, nbytes, fp);
|
|
fclose(fp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*
|
|
* Copy in memory *
|
|
*--------------------------------------------------------------------*/
|
|
/*!
|
|
* arrayCopy()
|
|
*
|
|
* Input: datas
|
|
* size (of data array)
|
|
* Return: datad (on heap), or null on error
|
|
*/
|
|
l_uint8 *
|
|
arrayCopy(l_uint8 *datas,
|
|
size_t size)
|
|
{
|
|
l_uint8 *datad;
|
|
|
|
PROCNAME("arrayCopy");
|
|
|
|
if (!datas)
|
|
return (l_uint8 *)ERROR_PTR("datas not defined", procName, NULL);
|
|
|
|
if ((datad = (l_uint8 *)CALLOC(size, sizeof(l_uint8))) == NULL)
|
|
return (l_uint8 *)ERROR_PTR("datad not made", procName, NULL);
|
|
memcpy(datad, datas, size);
|
|
return datad;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*
|
|
* File copy operations *
|
|
*--------------------------------------------------------------------*/
|
|
/*!
|
|
* fileCopy()
|
|
*
|
|
* Input: filename1 (copy this file)
|
|
* filename2 (to this file)
|
|
* Return: 0 if OK, 1 on error
|
|
*/
|
|
l_int32
|
|
fileCopy(const char *filename1,
|
|
const char *filename2)
|
|
{
|
|
l_uint8 *data;
|
|
l_int32 nbytes, ret;
|
|
|
|
PROCNAME("fileCopy");
|
|
|
|
if ((data = arrayRead(filename1, &nbytes)) == NULL)
|
|
return ERROR_INT("data not returned", procName, 1);
|
|
ret = arrayWrite(filename2, "w", data, nbytes);
|
|
FREE(data);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*!
|
|
* fileConcatenate()
|
|
*
|
|
* Input: filename1
|
|
* filename2 (file to add to filename1)
|
|
* Return: 0 if OK, 1 on error
|
|
*/
|
|
l_int32
|
|
fileConcatenate(const char *filename1,
|
|
const char *filename2)
|
|
{
|
|
l_uint8 *data;
|
|
l_int32 nbytes;
|
|
|
|
PROCNAME("fileConcatenate");
|
|
|
|
if (!filename1)
|
|
return ERROR_INT("filename1 not defined", procName, 1);
|
|
if (!filename2)
|
|
return ERROR_INT("filename2 not defined", procName, 1);
|
|
|
|
data = arrayRead(filename2, &nbytes);
|
|
arrayWrite(filename1, "a", data, nbytes);
|
|
FREE(data);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* fileAppendString()
|
|
*
|
|
* Input: filename
|
|
* str (string to append to file)
|
|
* Return: 0 if OK, 1 on error
|
|
*/
|
|
l_int32
|
|
fileAppendString(const char *filename,
|
|
const char *str)
|
|
{
|
|
FILE *fp;
|
|
|
|
PROCNAME("fileAppendString");
|
|
|
|
if (!filename)
|
|
return ERROR_INT("filename not defined", procName, 1);
|
|
if (!str)
|
|
return ERROR_INT("str not defined", procName, 1);
|
|
|
|
if ((fp = fopen(filename, "a")) == NULL)
|
|
return ERROR_INT("stream not opened", procName, 1);
|
|
fprintf(fp, str);
|
|
fclose(fp);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*
|
|
* Test files for equivalence *
|
|
*--------------------------------------------------------------------*/
|
|
/*!
|
|
* filesAreIdentical()
|
|
*
|
|
* Input: fname1
|
|
* fname2
|
|
* &same (<return> 1 if identical; 0 if different)
|
|
* Return: 0 if OK, 1 on error
|
|
*/
|
|
l_int32
|
|
filesAreIdentical(const char *fname1,
|
|
const char *fname2,
|
|
l_int32 *psame)
|
|
{
|
|
l_int32 i, same, nbytes1, nbytes2;
|
|
l_uint8 *array1, *array2;
|
|
|
|
PROCNAME("filesAreIdentical");
|
|
|
|
if (!psame)
|
|
return ERROR_INT("&same not defined", procName, 1);
|
|
*psame = 0;
|
|
if (!fname1 || !fname2)
|
|
return ERROR_INT("both names not defined", procName, 1);
|
|
|
|
nbytes1 = nbytesInFile(fname1);
|
|
nbytes2 = nbytesInFile(fname2);
|
|
if (nbytes1 != nbytes2)
|
|
return 0;
|
|
|
|
if ((array1 = arrayRead(fname1, &nbytes1)) == NULL)
|
|
return ERROR_INT("array1 not read", procName, 1);
|
|
if ((array2 = arrayRead(fname2, &nbytes2)) == NULL)
|
|
return ERROR_INT("array2 not read", procName, 1);
|
|
same = 1;
|
|
for (i = 0; i < nbytes1; i++) {
|
|
if (array1[i] != array2[i]) {
|
|
same = 0;
|
|
break;
|
|
}
|
|
}
|
|
FREE(array1);
|
|
FREE(array2);
|
|
*psame = same;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
* 16 and 32 bit byte-swapping on big endian and little endian machines *
|
|
* *
|
|
* These are typically used for I/O conversions: *
|
|
* (1) endian conversion for data that was read from a file *
|
|
* (2) endian conversion on data before it is written to a file *
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
/*--------------------------------------------------------------------*
|
|
* 16-bit byte swapping *
|
|
*--------------------------------------------------------------------*/
|
|
#ifdef L_BIG_ENDIAN
|
|
|
|
l_uint16
|
|
convertOnBigEnd16(l_uint16 shortin)
|
|
{
|
|
return ((shortin << 8) | (shortin >> 8));
|
|
}
|
|
|
|
l_uint16
|
|
convertOnLittleEnd16(l_uint16 shortin)
|
|
{
|
|
return shortin;
|
|
}
|
|
|
|
#else /* L_LITTLE_ENDIAN */
|
|
|
|
l_uint16
|
|
convertOnLittleEnd16(l_uint16 shortin)
|
|
{
|
|
return ((shortin << 8) | (shortin >> 8));
|
|
}
|
|
|
|
l_uint16
|
|
convertOnBigEnd16(l_uint16 shortin)
|
|
{
|
|
return shortin;
|
|
}
|
|
|
|
#endif /* L_BIG_ENDIAN */
|
|
|
|
|
|
/*--------------------------------------------------------------------*
|
|
* 32-bit byte swapping *
|
|
*--------------------------------------------------------------------*/
|
|
#ifdef L_BIG_ENDIAN
|
|
|
|
l_uint32
|
|
convertOnBigEnd32(l_uint32 wordin)
|
|
{
|
|
return ((wordin << 24) | ((wordin << 8) & 0x00ff0000) |
|
|
((wordin >> 8) & 0x0000ff00) | (wordin >> 24));
|
|
}
|
|
|
|
l_uint32
|
|
convertOnLittleEnd32(l_uint32 wordin)
|
|
{
|
|
return wordin;
|
|
}
|
|
|
|
#else /* L_LITTLE_ENDIAN */
|
|
|
|
l_uint32
|
|
convertOnLittleEnd32(l_uint32 wordin)
|
|
{
|
|
return ((wordin << 24) | ((wordin << 8) & 0x00ff0000) |
|
|
((wordin >> 8) & 0x0000ff00) | (wordin >> 24));
|
|
}
|
|
|
|
l_uint32
|
|
convertOnBigEnd32(l_uint32 wordin)
|
|
{
|
|
return wordin;
|
|
}
|
|
|
|
#endif /* L_BIG_ENDIAN */
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------*
|
|
* Opening read stream *
|
|
*--------------------------------------------------------------------*/
|
|
/*!
|
|
* fopenReadStream()
|
|
*
|
|
* Input: filename
|
|
* Return: stream or null on error
|
|
*/
|
|
FILE *
|
|
fopenReadStream(const char *filename)
|
|
{
|
|
char *tail;
|
|
FILE *fp;
|
|
|
|
PROCNAME("fopenReadStream");
|
|
|
|
if (!filename)
|
|
return (FILE *)ERROR_PTR("filename not defined", procName, NULL);
|
|
|
|
/* Try input filename */
|
|
if ((fp = fopen(filename, "rb")))
|
|
return fp;
|
|
|
|
/* Else, strip directory and try locally */
|
|
splitPathAtDirectory(filename, NULL, &tail);
|
|
if ((fp = fopen(tail, "rb"))) {
|
|
FREE(tail);
|
|
return fp;
|
|
}
|
|
FREE(tail);
|
|
|
|
return (FILE *)ERROR_PTR("file not found", procName, NULL);
|
|
}
|
|
|
|
|
|
/*--------------------------------------------------------------------*
|
|
* File name operations *
|
|
*--------------------------------------------------------------------*/
|
|
/*!
|
|
* splitPathAtDirectory()
|
|
*
|
|
* Input: pathname (full path; can be a directory)
|
|
* &dir (<optional return> root directory name of
|
|
* input path, including trailing '/')
|
|
* &tail (<optional return> path tail, which is either
|
|
* the file name within the root directory or
|
|
* the last sub-directory in the path)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Note: (1) if you only want the tail, input null for
|
|
* the root directory ptr.
|
|
* (2) if you only want the root directory name,
|
|
* input null for the tail ptr.
|
|
* (3) This function makes decisions based only on the lexical
|
|
* structure of the input. Examples:
|
|
* /usr/tmp/abc --> dir: /usr/tmp/ tail: abc
|
|
* /usr/tmp/ --> dir: /usr/tmp/ tail: [empty string]
|
|
* /usr/tmp --> dir: /usr/ tail: tmp
|
|
*/
|
|
l_int32
|
|
splitPathAtDirectory(const char *pathname,
|
|
char **pdir,
|
|
char **ptail)
|
|
{
|
|
char *cpathname, *lastslash;
|
|
|
|
PROCNAME("splitPathAtDirectory");
|
|
|
|
if (!pdir && !ptail)
|
|
return ERROR_INT("null input for both strings", procName, 1);
|
|
if (pdir) *pdir = NULL;
|
|
if (ptail) *ptail = NULL;
|
|
if (!pathname)
|
|
return ERROR_INT("pathname not defined", procName, 1);
|
|
|
|
cpathname = stringNew(pathname);
|
|
if ((lastslash = strrchr(cpathname, sepchar))) {
|
|
if (ptail)
|
|
*ptail = stringNew(lastslash + 1);
|
|
if (pdir) {
|
|
*(lastslash + 1) = '\0';
|
|
*pdir = cpathname;
|
|
}
|
|
else
|
|
FREE(cpathname);
|
|
}
|
|
else { /* no directory */
|
|
if (pdir)
|
|
*pdir = stringNew("");
|
|
if (ptail)
|
|
*ptail = cpathname;
|
|
else
|
|
FREE(cpathname);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* splitPathAtExtension()
|
|
*
|
|
* Input: pathname (full path; can be a directory)
|
|
* &basename (<optional return> pathname not including the
|
|
* last dot and characters after that)
|
|
* &extension (<optional return> path extension, which is
|
|
* the last dot and the characters after it. If
|
|
* there is no extension, it returns the empty string)
|
|
* Return: 0 if OK, 1 on error
|
|
*
|
|
* Notes:
|
|
* (1) If you only want the extension, input null for the basename ptr.
|
|
* (2) If you only want the basename without extension, input null
|
|
* for the extension ptr.
|
|
* (3) This function makes decisions based only on the lexical
|
|
* structure of the input. Examples:
|
|
* /usr/tmp/abc.jpg --> basename: /usr/tmp/abc ext: .jpg
|
|
* /usr/tmp/.jpg --> basename: /usr/tmp/ tail: .jpg
|
|
* /usr/tmp.jpg/ --> basename: /usr/tmp.jpg/ tail: [empty str]
|
|
* ./.jpg --> basename: ./ tail: .jpg
|
|
*/
|
|
l_int32
|
|
splitPathAtExtension(const char *pathname,
|
|
char **pbasename,
|
|
char **pextension)
|
|
{
|
|
char *tail, *dir, *lastdot;
|
|
char empty[4] = "";
|
|
|
|
PROCNAME("splitPathExtension");
|
|
|
|
if (!pbasename && !pextension)
|
|
return ERROR_INT("null input for both strings", procName, 1);
|
|
if (pbasename) *pbasename = NULL;
|
|
if (pextension) *pextension = NULL;
|
|
if (!pathname)
|
|
return ERROR_INT("pathname not defined", procName, 1);
|
|
|
|
/* Split out the directory first */
|
|
splitPathAtDirectory(pathname, &dir, &tail);
|
|
|
|
/* Then look for a "." in the tail part.
|
|
* This way we ignore all "." in the directory. */
|
|
if ((lastdot = strrchr(tail, '.'))) {
|
|
if (pextension)
|
|
*pextension = stringNew(lastdot);
|
|
if (pbasename) {
|
|
*lastdot = '\0';
|
|
*pbasename = stringJoin(dir, tail);
|
|
}
|
|
}
|
|
else {
|
|
if (pextension)
|
|
*pextension = stringNew(empty);
|
|
if (pbasename)
|
|
*pbasename = stringNew(pathname);
|
|
}
|
|
FREE(dir);
|
|
FREE(tail);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* genPathname()
|
|
*
|
|
* Input: dir (directory name, with or without trailing '/')
|
|
* fname (file name within the directory)
|
|
* Return: full pathname, or null on error
|
|
*/
|
|
char *
|
|
genPathname(const char *dir,
|
|
const char *fname)
|
|
{
|
|
char *charbuf;
|
|
l_int32 dirlen, namelen, totlen;
|
|
|
|
PROCNAME("genPathname");
|
|
|
|
if (!dir)
|
|
return (char *)ERROR_PTR("dir not defined", procName, NULL);
|
|
if (!fname)
|
|
return (char *)ERROR_PTR("fname not defined", procName, NULL);
|
|
|
|
dirlen = strlen(dir);
|
|
namelen = strlen(fname);
|
|
totlen = dirlen + namelen + 20;
|
|
if ((charbuf = (char *)CALLOC(totlen, sizeof(char))) == NULL)
|
|
return (char *)ERROR_PTR("charbuf not made", procName, NULL);
|
|
|
|
#if COMPILER_MSVC
|
|
if (stringFindSubstr(dir, "/", NULL) > 0) {
|
|
char *tempname;
|
|
tempname = stringReplaceEachSubstr(dir, "/", "\\", NULL);
|
|
strncpy(charbuf, tempname, strlen(tempname));
|
|
FREE(tempname);
|
|
}
|
|
else {
|
|
strncpy(charbuf, dir, dirlen);
|
|
}
|
|
#else
|
|
strncpy(charbuf, dir, dirlen);
|
|
#endif /* COMPILER_MSVC */
|
|
|
|
dirlen = strlen(charbuf);
|
|
if (charbuf[dirlen - 1] != sepchar) /* append sepchar */
|
|
charbuf[dirlen] = sepchar;
|
|
strncat(charbuf, fname, namelen);
|
|
return charbuf;
|
|
}
|
|
|
|
|
|
/*!
|
|
* genTempFilename()
|
|
*
|
|
* Input: dir (directory name; use '.' for local dir; no trailing '/')
|
|
* segment (an additional part of the name; can be null)
|
|
* extension (<optional> filename extension with '.'; can be null)
|
|
* Return: tempname (with pid embedded in file name), or null on error
|
|
*
|
|
* Notes:
|
|
* (1) This function is useful when there can be more than one
|
|
* process writing and reading temporary files. It will not
|
|
* work properly when multiple threads from a single process call
|
|
* this function. Furthermore, as with any function that
|
|
* provides easily guessed temporary filenames, it is not designed
|
|
* to be safe from an attack where the intruder is logged onto
|
|
* the server.
|
|
*/
|
|
char *
|
|
genTempFilename(const char *dir,
|
|
const char *segment,
|
|
const char *extension)
|
|
{
|
|
char buf[256];
|
|
char *name, *outname;
|
|
l_int32 pid, nseg, next;
|
|
|
|
PROCNAME("genTempFilename");
|
|
|
|
if (!dir)
|
|
return (char *)ERROR_PTR("dir not defined", procName, NULL);
|
|
#if COMPILER_MSVC
|
|
pid=GetCurrentProcessId();
|
|
#else
|
|
pid = getpid();
|
|
#endif
|
|
if (segment)
|
|
nseg = strlen(segment);
|
|
else
|
|
nseg = 0;
|
|
if (extension)
|
|
next = strlen(extension);
|
|
else
|
|
next = 0;
|
|
|
|
#if COMPILER_MSVC
|
|
snprintf(buf, 255 - nseg - next, "%s\\%d_", dir, pid);
|
|
#else
|
|
snprintf(buf, 255 - nseg - next, "%s/%d_", dir, pid);
|
|
#endif
|
|
|
|
name = stringJoin(buf, segment);
|
|
outname = stringJoin(name, extension);
|
|
FREE(name);
|
|
return outname;
|
|
}
|
|
|
|
|
|
/*!
|
|
* extractNumberFromFilename()
|
|
*
|
|
* Input: fname
|
|
* numpre (number of characters before the digits to be found)
|
|
* numpost (number of characters after the digits to be found)
|
|
* Return: num (number embedded in the filename); -1 on error or if
|
|
* not found
|
|
*
|
|
* Notes:
|
|
* (1) The number is to be found in the basename, which is the
|
|
* filename without either the directory or the last extension.
|
|
* (2) When a number is found, it is non-negative. If no number
|
|
* is found, this returns -1, without an error message. The
|
|
* caller needs to check.
|
|
*/
|
|
l_int32
|
|
extractNumberFromFilename(const char *fname,
|
|
l_int32 numpre,
|
|
l_int32 numpost)
|
|
{
|
|
char *tail, *basename;
|
|
l_int32 len, nret, num;
|
|
|
|
PROCNAME("extractNumberFromFilename");
|
|
|
|
if (!fname)
|
|
return ERROR_INT("fname not defined", procName, -1);
|
|
|
|
splitPathAtDirectory(fname, NULL, &tail);
|
|
splitPathAtExtension(tail, &basename, NULL);
|
|
FREE(tail);
|
|
|
|
len = strlen(basename);
|
|
if (numpre + numpost > len - 1) {
|
|
FREE(basename);
|
|
return ERROR_INT("numpre + numpost too big", procName, -1);
|
|
}
|
|
|
|
basename[len - numpost] = '\0';
|
|
nret = sscanf(basename + numpre, "%d", &num);
|
|
FREE(basename);
|
|
|
|
if (nret == 1)
|
|
return num;
|
|
else
|
|
return -1; /* not found */
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
* Version number *
|
|
*---------------------------------------------------------------------*/
|
|
/*!
|
|
* getLeptonlibVersion()
|
|
*
|
|
* Return: string of version number (e.g., 'leptonlib-1.65')
|
|
*
|
|
* Notes:
|
|
* (1) The caller has responsibility to free the memory.
|
|
*/
|
|
char *
|
|
getLeptonlibVersion()
|
|
{
|
|
char *version = (char *)CALLOC(100, sizeof(char));
|
|
|
|
#if COMPILER_MSVC
|
|
#ifdef _DLL
|
|
char dllStr[] = "DLL";
|
|
#else
|
|
char dllStr[] = "LIB";
|
|
#endif
|
|
#ifdef _DEBUG
|
|
char debugStr[] = "Debug";
|
|
#else
|
|
char debugStr[] = "Release";
|
|
#endif
|
|
#ifdef _M_IX86
|
|
char bitStr[] = " 32 bit";
|
|
#elif _M_X64
|
|
char bitStr[] = " 64 bit";
|
|
#else
|
|
char bitStr[] = ""
|
|
#endif
|
|
snprintf(version, 100, "leptonlib-%d.%d (%s, %s) [MSC v.%d %s %s%s]",
|
|
LIBLEPT_MAJOR_VERSION, LIBLEPT_MINOR_VERSION,
|
|
__DATE__, __TIME__, _MSC_VER, dllStr, debugStr, bitStr);
|
|
|
|
#else
|
|
|
|
snprintf(version, 100, "leptonlib-%d.%d", LIBLEPT_MAJOR_VERSION,
|
|
LIBLEPT_MINOR_VERSION);
|
|
|
|
#endif /* COMPILER_MSVC */
|
|
return version;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------*
|
|
* Timing procs *
|
|
*---------------------------------------------------------------------*/
|
|
/*
|
|
* Example of use:
|
|
*
|
|
* startTimer();
|
|
* ....
|
|
* fprintf(stderr, "Elapsed time = %7.3f sec\n", stopTimer());
|
|
*/
|
|
#if !defined(__MINGW32__) && !defined(COMPILER_MSVC)
|
|
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
|
|
static struct rusage rusage_before;
|
|
static struct rusage rusage_after;
|
|
|
|
void
|
|
startTimer(void)
|
|
{
|
|
getrusage(RUSAGE_SELF, &rusage_before);
|
|
}
|
|
|
|
|
|
l_float32
|
|
stopTimer(void)
|
|
{
|
|
l_int32 tsec, tusec;
|
|
|
|
getrusage(RUSAGE_SELF, &rusage_after);
|
|
|
|
tsec = rusage_after.ru_utime.tv_sec - rusage_before.ru_utime.tv_sec;
|
|
tusec = rusage_after.ru_utime.tv_usec - rusage_before.ru_utime.tv_usec;
|
|
|
|
return (tsec + ((l_float32)tusec) / 1000000.0);
|
|
}
|
|
|
|
#else /* __MINGW32__ : resource.h not implemented under MINGW */
|
|
|
|
#include <windows.h>
|
|
|
|
static ULARGE_INTEGER utime_before;
|
|
static ULARGE_INTEGER utime_after;
|
|
|
|
void
|
|
startTimer(void)
|
|
{
|
|
HANDLE this_process;
|
|
FILETIME start, stop, kernel, user;
|
|
|
|
this_process = GetCurrentProcess ();
|
|
|
|
GetProcessTimes (this_process, &start, &stop, &kernel, &user);
|
|
|
|
utime_before.LowPart = user.dwLowDateTime;
|
|
utime_before.HighPart = user.dwHighDateTime;
|
|
}
|
|
|
|
l_float32
|
|
stopTimer(void)
|
|
{
|
|
HANDLE this_process;
|
|
FILETIME start, stop, kernel, user;
|
|
|
|
this_process = GetCurrentProcess ();
|
|
|
|
GetProcessTimes (this_process, &start, &stop, &kernel, &user);
|
|
|
|
utime_after.LowPart = user.dwLowDateTime;
|
|
utime_after.HighPart = user.dwHighDateTime;
|
|
|
|
return ((l_float32)(signed)(utime_after.QuadPart - utime_before.QuadPart) /
|
|
10000000.0);
|
|
}
|
|
|
|
#endif
|