third-party/leptonica/src/utils.c
/*====================================================================*
- Copyright (C) 2001 Leptonica. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials
- provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*====================================================================*/
/*
* utils.c
*
* Control of error, warning and info messages
* l_int32 setMsgSeverity()
*
* Error return functions, invoked by macros
* l_int32 returnErrorInt()
* l_float32 returnErrorFloat()
* void *returnErrorPtr()
*
* Safe string procs
* char *stringNew()
* l_int32 stringCopy()
* l_int32 stringReplace()
* l_int32 stringLength()
* l_int32 stringCat()
* char *stringConcatNew()
* char *stringJoin()
* l_int32 stringJoinIP()
* char *stringReverse()
* char *strtokSafe()
* l_int32 stringSplitOnToken()
*
* Find and replace string and array procs
* char *stringRemoveChars()
* l_int32 stringFindSubstr()
* char *stringReplaceSubstr()
* char *stringReplaceEachSubstr()
* L_DNA *arrayFindEachSequence()
* l_int32 arrayFindSequence()
*
* Safe realloc
* void *reallocNew()
*
* Read and write between file and memory
* l_uint8 *l_binaryRead()
* l_uint8 *l_binaryReadStream()
* l_uint8 *l_binaryReadSelect()
* l_uint8 *l_binaryReadSelectStream()
* l_int32 l_binaryWrite()
* l_int32 nbytesInFile()
* l_int32 fnbytesInFile()
*
* Copy in memory
* l_uint8 *l_binaryCopy()
*
* 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()
*
* Cross-platform functions for opening file streams
* FILE *fopenReadStream()
* FILE *fopenWriteStream()
*
* Cross-platform functions that avoid C-runtime boundary crossing
* with Windows DLLs
* FILE *lept_fopen()
* l_int32 lept_fclose()
* void lept_calloc()
* void lept_free()
*
* Cross-platform file system operations in temp directories
* l_int32 lept_mkdir()
* l_int32 lept_rmdir()
* l_int32 lept_direxists()
* l_int32 lept_mv()
* l_int32 lept_rm_match()
* l_int32 lept_rm()
* l_int32 lept_rmfile()
* l_int32 lept_cp()
*
* General file name operations
* l_int32 splitPathAtDirectory()
* l_int32 splitPathAtExtension()
* char *pathJoin()
* char *appendSubdirectory()
*
* Special file name operations
* l_int32 convertSepCharsInPath()
* char *genPathname()
* l_int32 makeTempDirname()
* l_int32 modifyTrailingSlash()
* char *genTempFilename()
* l_int32 extractNumberFromFilename()
*
* File corruption operation
* l_int32 fileCorruptByDeletion()
* l_int32 fileCorruptByMutation()
*
* Generate random integer in given range
* l_int32 genRandomIntegerInRange()
*
* Simple math function
* l_int32 lept_roundftoi()
*
* Gray code conversion
* l_uint32 convertBinaryToGrayCode()
* l_uint32 convertGrayToBinaryCode()
*
* Leptonica version number
* char *getLeptonicaVersion()
*
* Timing
* void startTimer()
* l_float32 stopTimer()
* L_TIMER startTimerNested()
* l_float32 stopTimerNested()
* void l_getCurrentTime()
* L_WALLTIMER *startWallTimer()
* l_float32 stopWallTimer()
* void l_getFormattedDate()
*
* Notes on cross-platform development
* -----------------------------------
* This is important:
* (1) With the exception of splitPathAtDirectory(), splitPathAtExtension()
* and genPathname(), all input pathnames must have unix separators.
* (2) On Windows, when you specify a read or write to "/tmp/...",
* the filename is rewritten to use the Windows temp directory:
* /tmp ==> <Temp>... (windows)
* (3) This filename rewrite, along with the conversion from unix
* to windows pathnames, happens in genPathname().
* (4) Use fopenReadStream() and fopenWriteStream() to open files,
* because these use genPathname() to find the platform-dependent
* filenames. Likewise for l_binaryRead() and l_binaryWrite().
* (5) For moving, copying and removing files and directories that are in
* subdirectories of /tmp, use the lept_*() file system shell wrappers:
* lept_mkdir(), lept_rmdir(), lept_mv(), lept_rm() and lept_cp().
* (6) Use the lept_*() C library wrappers. These work properly on
* Windows, where the same DLL must perform complementary operations
* on file streams (open/close) and heap memory (malloc/free):
* lept_fopen(), lept_fclose(), lept_calloc() and lept_free().
*/
#include <string.h>
#include <time.h>
#ifdef _MSC_VER
#include <process.h>
#include <direct.h>
#else
#include <unistd.h>
#endif /* _MSC_VER */
#include "allheaders.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/stat.h> /* for stat, mkdir(2) */
#include <sys/types.h>
#endif
#include <stddef.h>
/* Global for controlling message output at runtime */
LEPT_DLL l_int32 LeptMsgSeverity = DEFAULT_SEVERITY;
/*----------------------------------------------------------------------*
* Control of error, warning and info messages *
*----------------------------------------------------------------------*/
/*!
* setMsgSeverity()
*
* Input: newsev
* Return: oldsev
*
* Notes:
* (1) setMsgSeverity() allows the user to specify the desired
* message severity threshold. Messages of equal or greater
* severity will be output. The previous message severity is
* returned when the new severity is set.
* (2) If L_SEVERITY_EXTERNAL is passed, then the severity will be
* obtained from the LEPT_MSG_SEVERITY environment variable.
* If the environmental variable is not set, a warning is issued.
*/
l_int32
setMsgSeverity(l_int32 newsev)
{
l_int32 oldsev;
char *envsev;
PROCNAME("setMsgSeverity");
oldsev = LeptMsgSeverity;
if (newsev == L_SEVERITY_EXTERNAL) {
envsev = getenv("LEPT_MSG_SEVERITY");
if (envsev) {
LeptMsgSeverity = atoi(envsev);
L_INFO("message severity set to external\n", procName);
} else {
L_WARNING("environment var LEPT_MSG_SEVERITY not defined\n",
procName);
}
} else {
LeptMsgSeverity = newsev;
L_INFO("message severity set to %d\n", procName, newsev);
}
return oldsev;
}
/*----------------------------------------------------------------------*
* Error return functions, invoked by macros *
* *
* (1) These error functions print messages to stderr and allow *
* exit from the function that called them. *
* (2) They must be invoked only by the macros ERROR_INT, *
* ERROR_FLOAT and ERROR_PTR, which are in environ.h *
* (3) The print output can be disabled at compile time, either *
* by using -DNO_CONSOLE_IO or by setting LeptMsgSeverity. *
*----------------------------------------------------------------------*/
/*!
* returnErrorInt()
*
* Input: msg (error message)
* procname
* ival (return val)
* Return: ival (typically 1 for an error return)
*/
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;
}
/*--------------------------------------------------------------------*
* Safe string operations *
*--------------------------------------------------------------------*/
/*!
* stringNew()
*
* Input: src string
* Return: dest copy of src string, or null on error
*/
char *
stringNew(const char *src)
{
l_int32 len;
char *dest;
PROCNAME("stringNew");
if (!src) {
L_WARNING("src not defined\n", procName);
return NULL;
}
len = strlen(src);
if ((dest = (char *)CALLOC(len + 1, sizeof(char))) == NULL)
return (char *)ERROR_PTR("dest not made", procName, NULL);
stringCopy(dest, src, len);
return dest;
}
/*!
* stringCopy()
*
* Input: dest (existing byte buffer)
* src string (<optional> can be null)
* n (max number of characters to copy)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) Relatively safe wrapper for strncpy, that checks the input,
* and does not complain if @src is null or @n < 1.
* If @n < 1, this is a no-op.
* (2) @dest needs to be at least @n bytes in size.
* (3) We don't call strncpy() because valgrind complains about
* use of uninitialized values.
*/
l_int32
stringCopy(char *dest,
const char *src,
l_int32 n)
{
l_int32 i;
PROCNAME("stringCopy");
if (!dest)
return ERROR_INT("dest not defined", procName, 1);
if (!src || n < 1)
return 0;
/* Implementation of strncpy that valgrind doesn't complain about */
for (i = 0; i < n && src[i] != '\0'; i++)
dest[i] = src[i];
for (; i < n; i++)
dest[i] = '\0';
return 0;
}
/*!
* stringReplace()
*
* Input: &dest string (<return> copy)
* src string (<optional> can be null)
* 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 something reasonable.
*/
l_int32
stringReplace(char **pdest,
const char *src)
{
char *scopy;
l_int32 len;
PROCNAME("stringReplace");
if (!pdest)
return ERROR_INT("pdest not defined", procName, 1);
if (*pdest)
FREE(*pdest);
if (src) {
len = strlen(src);
if ((scopy = (char *)CALLOC(len + 1, sizeof(char))) == NULL)
return ERROR_INT("scopy not made", procName, 1);
stringCopy(scopy, src, len);
*pdest = scopy;
} else {
*pdest = NULL;
}
return 0;
}
/*!
* stringLength()
*
* Input: src string (can be null or null-terminated string)
* size (size of src buffer)
* Return: length of src in bytes.
*
* Notes:
* (1) Safe implementation of strlen that only checks size bytes
* for trailing NUL.
* (2) Valid returned string lengths are between 0 and size - 1.
* If size bytes are checked without finding a NUL byte, then
* an error is indicated by returning size.
*/
l_int32
stringLength(const char *src,
size_t size)
{
l_int32 i;
PROCNAME("stringLength");
if (!src)
return ERROR_INT("src not defined", procName, 0);
if (size < 1)
return 0;
for (i = 0; i < size; i++) {
if (src[i] == '\0')
return i;
}
return size; /* didn't find a NUL byte */
}
/*!
* stringCat()
*
* Input: dest (null-terminated byte buffer)
* size (size of dest)
* src string (can be null or null-terminated string)
* Return: number of bytes added to dest; -1 on error
*
* Notes:
* (1) Alternative implementation of strncat, that checks the input,
* is easier to use (since the size of the dest buffer is specified
* rather than the number of bytes to copy), and does not complain
* if @src is null.
* (2) Never writes past end of dest.
* (3) If it can't append src (an error), it does nothing.
* (4) N.B. The order of 2nd and 3rd args is reversed from that in
* strncat, as in the Windows function strcat_s().
*/
l_int32
stringCat(char *dest,
size_t size,
const char *src)
{
l_int32 i, n;
l_int32 lendest, lensrc;
PROCNAME("stringCat");
if (!dest)
return ERROR_INT("dest not defined", procName, -1);
if (size < 1)
return ERROR_INT("size < 1; too small", procName, -1);
if (!src)
return 0;
lendest = stringLength(dest, size);
if (lendest == size)
return ERROR_INT("no terminating nul byte", procName, -1);
lensrc = stringLength(src, size);
if (lensrc == 0)
return 0;
n = (lendest + lensrc > size - 1 ? size - lendest - 1 : lensrc);
if (n < 1)
return ERROR_INT("dest too small for append", procName, -1);
for (i = 0; i < n; i++)
dest[lendest + i] = src[i];
dest[lendest + n] = '\0';
return n;
}
/*!
* stringConcatNew()
*
* Input: first (first string in list)
* varargs (NULL-terminated list of strings)
* Return: result (new string concatenating the input strings), or
* NULL if first == NULL
*
* Notes:
* (1) The last arg in the list of strings must be NULL.
* (2) Caller must free the returned string.
*/
char *
stringConcatNew(const char *first, ...)
{
size_t len;
char *result, *ptr;
const char *arg;
va_list args;
if (!first) return NULL;
/* Find the length of the output string */
va_start(args, first);
len = strlen(first);
while ((arg = va_arg(args, const char *)) != NULL)
len += strlen(arg);
va_end(args);
result = (char *)CALLOC(len + 1, sizeof(char));
/* Concatenate the args */
va_start(args, first);
ptr = result;
arg = first;
while (*arg)
*ptr++ = *arg++;
while ((arg = va_arg(args, const char *)) != NULL) {
while (*arg)
*ptr++ = *arg++;
}
va_end(args);
return result;
}
/*!
* stringJoin()
*
* Input: src1 string (<optional> can be null)
* src2 string (<optional> can be null)
* Return: concatenated string, or null on error
*
* Notes:
* (1) This is a 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 of the pointers are null.
*/
char *
stringJoin(const char *src1,
const char *src2)
{
char *dest;
l_int32 srclen1, srclen2, destlen;
PROCNAME("stringJoin");
srclen1 = (src1) ? strlen(src1) : 0;
srclen2 = (src2) ? strlen(src2) : 0;
destlen = srclen1 + srclen2 + 3;
if ((dest = (char *)CALLOC(destlen, sizeof(char))) == NULL)
return (char *)ERROR_PTR("calloc fail for dest", procName, NULL);
if (src1)
stringCopy(dest, src1, srclen1);
if (src2)
strncat(dest, src2, srclen2);
return dest;
}
/*!
* stringJoinIP()
*
* Input: &src1 string (address of src1; cannot be on the stack)
* src2 string (<optional> can be null)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) This is a safe in-place version of strcat. The contents of
* src1 is replaced by the concatenation of src1 and src2.
* (2) It is not an error if either or both of the strings
* are empty (""), or if the pointers to the strings (*psrc1, src2)
* are null.
* (3) src1 should be initialized to null or an empty string
* before the first call. Use one of these:
* char *src1 = NULL;
* char *src1 = stringNew("");
* Then call with:
* stringJoinIP(&src1, src2);
* (4) This can also be implemented as a macro:
* #define stringJoinIP(src1, src2) \
* {tmpstr = stringJoin((src1),(src2)); \
* FREE(src1); \
* (src1) = tmpstr;}
* (5) Another function to consider for joining many strings is
* stringConcatNew().
*/
l_int32
stringJoinIP(char **psrc1,
const char *src2)
{
char *tmpstr;
PROCNAME("stringJoinIP");
if (!psrc1)
return ERROR_INT("&src1 not defined", procName, 1);
tmpstr = stringJoin(*psrc1, src2);
FREE(*psrc1);
*psrc1 = tmpstr;
return 0;
}
/*!
* 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));
stringCopy(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 (pcount) *pcount = 0;
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);
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)++;
}
}
/*!
* arrayFindEachSequence()
*
* Input: data (byte array)
* datalen (length of data, in bytes)
* sequence (subarray of bytes to find in data)
* seqlen (length of sequence, in bytes)
* Return: dna of offsets where the sequence is found, or null if
* none are found or 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 finds every occurrence in @data of @sequence.
*/
L_DNA *
arrayFindEachSequence(const l_uint8 *data,
size_t datalen,
const l_uint8 *sequence,
size_t seqlen)
{
l_int32 start, offset, realoffset, found;
L_DNA *da;
PROCNAME("arrayFindEachSequence");
if (!data || !sequence)
return (L_DNA *)ERROR_PTR("data & sequence not both defined",
procName, NULL);
da = l_dnaCreate(0);
start = 0;
while (1) {
arrayFindSequence(data + start, datalen - start, sequence, seqlen,
&offset, &found);
if (found == FALSE)
break;
realoffset = start + offset;
l_dnaAddNumber(da, realoffset);
start = realoffset + seqlen;
if (start >= datalen)
break;
}
if (l_dnaGetCount(da) == 0)
l_dnaDestroy(&da);
return da;
}
/*!
* 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 @sequence,
* which consists of @seqlen bytes. 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 0, so you
* must check @found.
*/
l_int32
arrayFindSequence(const l_uint8 *data,
size_t datalen,
const l_uint8 *sequence,
size_t seqlen,
l_int32 *poffset,
l_int32 *pfound)
{
l_int32 i, j, found, lastpos;
PROCNAME("arrayFindSequence");
if (poffset) *poffset = 0;
if (pfound) *pfound = FALSE;
if (!data || !sequence)
return ERROR_INT("data & sequence not both defined", procName, 1);
if (!poffset || !pfound)
return ERROR_INT("&offset and &found not defined", procName, 1);
lastpos = datalen - seqlen + 1;
found = FALSE;
for (i = 0; i < lastpos; i++) {
for (j = 0; j < seqlen; j++) {
if (data[i + j] != sequence[j])
break;
if (j == seqlen - 1)
found = TRUE;
}
if (found == TRUE)
break;
}
if (found == TRUE) {
*poffset = i;
*pfound = TRUE;
}
return 0;
}
/*--------------------------------------------------------------------*
* Safe realloc *
*--------------------------------------------------------------------*/
/*!
* reallocNew()
*
* Input: &indata (<optional>; nulls indata)
* oldsize (size of input data to be copied, in bytes)
* newsize (size of data to be reallocated in 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;
}
/*--------------------------------------------------------------------*
* Read and write between file and memory *
*--------------------------------------------------------------------*/
/*!
* l_binaryRead()
*
* Input: filename
* &nbytes (<return> number of bytes read)
* Return: data, or null on error
*/
l_uint8 *
l_binaryRead(const char *filename,
size_t *pnbytes)
{
l_uint8 *data;
FILE *fp;
PROCNAME("l_binaryRead");
if (!pnbytes)
return (l_uint8 *)ERROR_PTR("pnbytes not defined", procName, NULL);
*pnbytes = 0;
if (!filename)
return (l_uint8 *)ERROR_PTR("filename not defined", procName, NULL);
if ((fp = fopenReadStream(filename)) == NULL)
return (l_uint8 *)ERROR_PTR("file stream not opened", procName, NULL);
data = l_binaryReadStream(fp, pnbytes);
fclose(fp);
return data;
}
/*!
* l_binaryReadStream()
*
* Input: fp (stream opened to read; can be stdin)
* &nbytes (<return> number of bytes read)
* Return: null-terminated array, or null on error
* (reading 0 bytes is not an error)
*
* Notes:
* (1) The returned array is terminated with a null byte so that it can
* be used to read ascii data from a file into a proper C string.
* (2) This can be used to capture data that is piped in via stdin,
* because it does not require seeking within the file.
* (3) For example, you can read an image from stdin into memory
* using shell redirection, with one of these shell commands:
* cat <imagefile> | readprog
* readprog < <imagefile>
* where readprog is:
* l_uint8 *data = l_binaryReadStream(stdin, &nbytes);
* Pix *pix = pixReadMem(data, nbytes);
*/
l_uint8 *
l_binaryReadStream(FILE *fp,
size_t *pnbytes)
{
l_uint8 *data;
l_int32 seekable, navail, nadd, nread;
BBUFFER *bb;
PROCNAME("l_binaryReadStream");
if (!pnbytes)
return (l_uint8 *)ERROR_PTR("&nbytes not defined", procName, NULL);
*pnbytes = 0;
if (!fp)
return (l_uint8 *)ERROR_PTR("fp not defined", procName, NULL);
/* Test if the stream is seekable, by attempting to seek to
* the start of data. This is a no-op. If it is seekable, use
* l_binaryReadSelectStream() to determine the size of the
* data to be read in advance. */
seekable = (ftell(fp) == 0) ? 1 : 0;
if (seekable)
return l_binaryReadSelectStream(fp, 0, 0, pnbytes);
/* If it is not seekable, use the bbuffer to realloc memory
* as needed during reading. */
bb = bbufferCreate(NULL, 4096);
while (1) {
navail = bb->nalloc - bb->n;
if (navail < 4096) {
nadd = L_MAX(bb->nalloc, 4096);
bbufferExtendArray(bb, nadd);
}
nread = fread((void *)(bb->array + bb->n), 1, 4096, fp);
bb->n += nread;
if (nread != 4096) break;
}
/* Copy the data to a new array sized for the data, because
* the bbuffer array can be nearly twice the size we need. */
if ((data = (l_uint8 *)CALLOC(bb->n + 1, sizeof(l_uint8))) != NULL) {
memcpy(data, bb->array, bb->n);
*pnbytes = bb->n;
} else {
L_ERROR("calloc fail for data\n", procName);
}
bbufferDestroy(&bb);
return data;
}
/*!
* l_binaryReadSelect()
*
* Input: filename
* start (first byte to read)
* nbytes (number of bytes to read; use 0 to read to end of file)
* &nread (<return> number of bytes actually read)
* Return: data, or null on error
*
* Notes:
* (1) The returned array is terminated with a null byte so that it can
* be used to read ascii data from a file into a proper C string.
*/
l_uint8 *
l_binaryReadSelect(const char *filename,
size_t start,
size_t nbytes,
size_t *pnread)
{
l_uint8 *data;
FILE *fp;
PROCNAME("l_binaryReadSelect");
if (!pnread)
return (l_uint8 *)ERROR_PTR("pnread not defined", procName, NULL);
*pnread = 0;
if (!filename)
return (l_uint8 *)ERROR_PTR("filename not defined", procName, NULL);
if ((fp = fopenReadStream(filename)) == NULL)
return (l_uint8 *)ERROR_PTR("file stream not opened", procName, NULL);
data = l_binaryReadSelectStream(fp, start, nbytes, pnread);
fclose(fp);
return data;
}
/*!
* l_binaryReadSelectStream()
*
* Input: stream
* start (first byte to read)
* nbytes (number of bytes to read; use 0 to read to end of file)
* &nread (<return> number of bytes actually read)
* Return: null-terminated array, or null on error
* (reading 0 bytes is not an error)
*
* Notes:
* (1) The returned array is terminated with a null byte so that it can
* be used to read ascii data from a file into a proper C string.
* If the file to be read is empty and @start == 0, an array
* with a single null byte is returned.
* (2) Side effect: the stream pointer is re-positioned to the
* beginning of the file.
*/
l_uint8 *
l_binaryReadSelectStream(FILE *fp,
size_t start,
size_t nbytes,
size_t *pnread)
{
l_uint8 *data;
size_t bytesleft, bytestoread, nread, filebytes;
PROCNAME("l_binaryReadSelectStream");
if (!pnread)
return (l_uint8 *)ERROR_PTR("&nread not defined", procName, NULL);
*pnread = 0;
if (!fp)
return (l_uint8 *)ERROR_PTR("stream not defined", procName, NULL);
/* Verify and adjust the parameters if necessary */
fseek(fp, 0, SEEK_END); /* EOF */
filebytes = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (start > filebytes) {
L_ERROR("start = %lu but filebytes = %lu\n", procName,
(unsigned long)start, (unsigned long)filebytes);
return NULL;
}
if (filebytes == 0) /* start == 0; nothing to read; return null byte */
return (l_uint8 *)CALLOC(1, 1);
bytesleft = filebytes - start; /* greater than 0 */
if (nbytes == 0) nbytes = bytesleft;
bytestoread = (bytesleft >= nbytes) ? nbytes : bytesleft;
/* Read the data */
if ((data = (l_uint8 *)CALLOC(1, bytestoread + 1)) == NULL)
return (l_uint8 *)ERROR_PTR("calloc fail for data", procName, NULL);
fseek(fp, start, SEEK_SET);
nread = fread(data, 1, bytestoread, fp);
if (nbytes != nread)
L_INFO("%lu bytes requested; %lu bytes read\n", procName,
(unsigned long)nbytes, (unsigned long)nread);
*pnread = nread;
fseek(fp, 0, SEEK_SET);
return data;
}
/*!
* l_binaryWrite()
*
* 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
l_binaryWrite(const char *filename,
const char *operation,
void *data,
size_t nbytes)
{
char actualOperation[20];
FILE *fp;
PROCNAME("l_binaryWrite");
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);
/* The 'b' flag to fopen() is ignored for all POSIX
* conforming systems. However, Windows needs the 'b' flag. */
stringCopy(actualOperation, operation, 2);
strncat(actualOperation, "b", 2);
if ((fp = fopenWriteStream(filename, actualOperation)) == NULL)
return ERROR_INT("stream not opened", procName, 1);
fwrite(data, 1, nbytes, fp);
fclose(fp);
return 0;
}
/*!
* nbytesInFile()
*
* Input: filename
* Return: nbytes in file; 0 on error
*/
size_t
nbytesInFile(const char *filename)
{
size_t nbytes;
FILE *fp;
PROCNAME("nbytesInFile");
if (!filename)
return ERROR_INT("filename not defined", procName, 0);
if ((fp = fopenReadStream(filename)) == 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
*/
size_t
fnbytesInFile(FILE *fp)
{
size_t 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, pos, SEEK_SET); /* back to initial position */
return nbytes;
}
/*--------------------------------------------------------------------*
* Copy in memory *
*--------------------------------------------------------------------*/
/*!
* l_binaryCopy()
*
* Input: datas
* size (of data array)
* Return: datad (on heap), or null on error
*
* Notes:
* (1) We add 4 bytes to the zeroed output because in some cases
* (e.g., string handling) it is important to have the data
* be null terminated. This guarantees that after the memcpy,
* the result is automatically null terminated.
*/
l_uint8 *
l_binaryCopy(l_uint8 *datas,
size_t size)
{
l_uint8 *datad;
PROCNAME("l_binaryCopy");
if (!datas)
return (l_uint8 *)ERROR_PTR("datas not defined", procName, NULL);
if ((datad = (l_uint8 *)CALLOC(size + 4, 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: srcfile (copy this file)
* newfile (to this file)
* Return: 0 if OK, 1 on error
*/
l_int32
fileCopy(const char *srcfile,
const char *newfile)
{
l_int32 ret;
size_t nbytes;
l_uint8 *data;
PROCNAME("fileCopy");
if (!srcfile)
return ERROR_INT("srcfile not defined", procName, 1);
if (!newfile)
return ERROR_INT("newfile not defined", procName, 1);
if ((data = l_binaryRead(srcfile, &nbytes)) == NULL)
return ERROR_INT("data not returned", procName, 1);
ret = l_binaryWrite(newfile, "w", data, nbytes);
FREE(data);
return ret;
}
/*!
* fileConcatenate()
*
* Input: srcfile (file to append)
* destfile (file to add to)
* Return: 0 if OK, 1 on error
*/
l_int32
fileConcatenate(const char *srcfile,
const char *destfile)
{
size_t nbytes;
l_uint8 *data;
PROCNAME("fileConcatenate");
if (!srcfile)
return ERROR_INT("srcfile not defined", procName, 1);
if (!destfile)
return ERROR_INT("destfile not defined", procName, 1);
data = l_binaryRead(srcfile, &nbytes);
l_binaryWrite(destfile, "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 = fopenWriteStream(filename, "a")) == NULL)
return ERROR_INT("stream not opened", procName, 1);
fprintf(fp, "%s", 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;
size_t 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 = l_binaryRead(fname1, &nbytes1)) == NULL)
return ERROR_INT("array1 not read", procName, 1);
if ((array2 = l_binaryRead(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 file streams *
*--------------------------------------------------------------------*/
/*!
* fopenReadStream()
*
* Input: filename
* Return: stream, or null on error
*
* Notes:
* (1) This should be used whenever you want to run fopen() to
* read from a stream. Never call fopen() directory.
* (2) This also handles pathname conversions, if necessary:
* ==> /tmp (unix) [default]
* ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1]
* ==> <Temp>/leptonica (windows)
*/
FILE *
fopenReadStream(const char *filename)
{
char *fname, *tail;
FILE *fp;
PROCNAME("fopenReadStream");
if (!filename)
return (FILE *)ERROR_PTR("filename not defined", procName, NULL);
/* Try input filename */
fname = genPathname(filename, NULL);
fp = fopen(fname, "rb");
FREE(fname);
if (fp) return fp;
/* Else, strip directory and try locally */
splitPathAtDirectory(filename, NULL, &tail);
fp = fopen(tail, "rb");
FREE(tail);
if (!fp)
return (FILE *)ERROR_PTR("file not found", procName, NULL);
return fp;
}
/*!
* fopenWriteStream()
*
* Input: filename
* modestring
* Return: stream, or null on error
*
* Notes:
* (1) This should be used whenever you want to run fopen() to
* write or append to a stream. Never call fopen() directory.
* (2) This also handles pathname conversions, if necessary:
* ==> /tmp (unix) [default]
* ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1]
* ==> <Temp>/leptonica (windows)
*/
FILE *
fopenWriteStream(const char *filename,
const char *modestring)
{
char *fname;
FILE *fp;
PROCNAME("fopenWriteStream");
if (!filename)
return (FILE *)ERROR_PTR("filename not defined", procName, NULL);
fname = genPathname(filename, NULL);
fp = fopen(fname, modestring);
FREE(fname);
if (!fp)
return (FILE *)ERROR_PTR("stream not opened", procName, NULL);
return fp;
}
/*--------------------------------------------------------------------*
* Functions to avoid C-runtime boundary crossing with dlls *
*--------------------------------------------------------------------*/
/*
* Problems arise when pointers to streams and data are passed
* between two Windows DLLs that have been generated with different
* C runtimes. To avoid this, leptonica provides wrappers for
* several C library calls.
*/
/*!
* lept_fopen()
*
* Input: filename
* mode (same as for fopen(); e.g., "rb")
* Return: stream or null on error
*
* Notes:
* (1) This must be used by any application that passes
* a file handle to a leptonica Windows DLL.
*/
FILE *
lept_fopen(const char *filename,
const char *mode)
{
PROCNAME("lept_fopen");
if (!filename)
return (FILE *)ERROR_PTR("filename not defined", procName, NULL);
if (!mode)
return (FILE *)ERROR_PTR("mode not defined", procName, NULL);
if (stringFindSubstr(mode, "r", NULL))
return fopenReadStream(filename);
else
return fopenWriteStream(filename, mode);
}
/*!
* lept_fclose()
*
* Input: fp (stream handle)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) This should be used by any application that accepts
* a file handle generated by a leptonica Windows DLL.
*/
l_int32
lept_fclose(FILE *fp)
{
PROCNAME("lept_fclose");
if (!fp)
return ERROR_INT("stream not defined", procName, 1);
return fclose(fp);
}
/*!
* lept_calloc()
*
* Input: nmemb (number of members)
* size (of each member)
* Return: void ptr, or null on error
*
* Notes:
* (1) For safety with windows DLLs, this can be used in conjunction
* with lept_free() to avoid C-runtime boundary problems.
* Just use these two functions throughout your application.
*/
void *
lept_calloc(size_t nmemb,
size_t size)
{
if (nmemb <= 0 || size <= 0)
return NULL;
return CALLOC(nmemb, size);
}
/*!
* lept_free()
*
* Input: void ptr
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) This should be used by any application that accepts
* heap data allocated by a leptonica Windows DLL.
*/
void
lept_free(void *ptr)
{
if (!ptr) return;
FREE(ptr);
return;
}
/*--------------------------------------------------------------------*
* Cross-platform file system operations *
* [ These only write to /tmp or its subdirectories ] *
*--------------------------------------------------------------------*/
/*!
* lept_mkdir()
*
* Input: subdir (of /tmp or its equivalent on Windows)
* Return: 0 on success, non-zero on failure
*
* Notes:
* (1) This makes a subdirectory of the root temp directory.
* (2) The root temp directory is:
* /tmp (unix) [default]
* /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1]
* <Temp>/leptonica (windows)
*/
l_int32
lept_mkdir(const char *subdir)
{
char *dir;
l_int32 ret;
char *newpath;
#ifdef _WIN32
l_uint32 attributes;
#endif /* _WIN32 */
PROCNAME("lept_mkdir");
if (!subdir)
return ERROR_INT("subdir not defined", procName, 1);
if ((strlen(subdir) == 0) || (subdir[0] == '.') || (subdir[0] == '/'))
return ERROR_INT("subdir not an actual subdirectory", procName, 1);
dir = pathJoin("/tmp", subdir);
/* Make the subdirectory */
#ifndef _WIN32
if (ADD_LEPTONICA_SUBDIR == 1)
mkdir("/tmp/leptonica", 0777);
newpath = genPathname(dir, NULL);
ret = mkdir(newpath, 0777);
#else
/* Make sure the tmp directory exists */
newpath = genPathname("/tmp", NULL);
attributes = GetFileAttributes(newpath);
if (attributes == INVALID_FILE_ATTRIBUTES) {
ret = (CreateDirectory(newpath, NULL) ? 0 : 1);
}
FREE(newpath);
newpath = genPathname(dir, NULL);
ret = (CreateDirectory(newpath, NULL) ? 0 : 1);
#endif /* !_WIN32 */
FREE(newpath);
FREE(dir);
return ret;
}
/*!
* lept_rmdir()
*
* Input: subdir (of /tmp or its equivalent on Windows)
* Return: 0 on success, non-zero on failure
*
* Notes:
* (1) This removes all files from the specified subdirectory of
* the root temp directory:
* /tmp (unix) [default]
* /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1]
* <Temp>/leptonica (windows)
* and then removes the subdirectory.
* (2) The combination
* lept_rmdir(subdir);
* lept_mkdir(subdir);
* is guaranteed to give you an empty subdirectory.
*/
l_int32
lept_rmdir(const char *subdir)
{
char *rootdir, *dir, *fname, *fullname;
l_int32 exists, ret, i, nfiles;
SARRAY *sa;
#ifdef _WIN32
char *newpath;
#endif /* _WIN32 */
PROCNAME("lept_rmdir");
if (!subdir)
return ERROR_INT("subdir not defined", procName, 1);
if ((strlen(subdir) == 0) || (subdir[0] == '.') || (subdir[0] == '/'))
return ERROR_INT("subdir not an actual subdirectory", procName, 1);
/* Find the temp subdirectory */
rootdir = genPathname("/tmp", NULL);
dir = appendSubdirectory(rootdir, subdir);
FREE(rootdir);
if (!dir)
return ERROR_INT("directory name not made", procName, 1);
lept_direxists(dir, &exists);
if (!exists) { /* fail silently */
FREE(dir);
return 0;
}
/* List all the files */
if ((sa = getFilenamesInDirectory(dir)) == NULL) {
L_ERROR("directory %s does not exist!\n", procName, dir);
FREE(dir);
return 1;
}
nfiles = sarrayGetCount(sa);
#ifndef _WIN32
for (i = 0; i < nfiles; i++) {
fname = sarrayGetString(sa, i, L_NOCOPY);
fullname = genPathname(dir, fname);
remove(fullname);
FREE(fullname);
}
ret = remove(dir);
#else
for (i = 0; i < nfiles; i++) {
fname = sarrayGetString(sa, i, L_NOCOPY);
fullname = genPathname(dir, fname);
ret = lept_rmfile(fullname);
FREE(fullname);
}
newpath = genPathname(dir, NULL);
ret = RemoveDirectory(newpath) ? 0 : 1;
FREE(newpath);
#endif /* !_WIN32 */
sarrayDestroy(&sa);
FREE(dir);
return ret;
}
/*!
* lept_direxists()
*
* Input: dir
* &exists (<return> 1 if it exists; 0 otherwise)
* Return: void
*
* Notes:
* (1) Always use unix pathname separators.
* (2) By calling genPathname(), this does an automatic translation
* of "/tmp" if the pathname begins with "/tmp":
* ==> /tmp (unix) [default]
* ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1]
* ==> <Temp>/leptonica (windows)
*/
void
lept_direxists(const char *dir,
l_int32 *pexists)
{
char *realdir;
if (!pexists) return;
*pexists = 0;
if (!dir) return;
if ((realdir = genPathname(dir, NULL)) == NULL)
return;
#ifndef _WIN32
{
struct stat s;
l_int32 err = stat(realdir, &s);
if (err != -1 && S_ISDIR(s.st_mode))
*pexists = 1;
}
#else /* _WIN32 */
l_uint32 attributes;
attributes = GetFileAttributes(realdir);
if (attributes != INVALID_FILE_ATTRIBUTES &&
(attributes & FILE_ATTRIBUTE_DIRECTORY)) {
*pexists = 1;
}
#endif /* _WIN32 */
FREE(realdir);
return;
}
/*!
* lept_rm_match()
*
* Input: subdir (<optional> If NULL, the removed files are in /tmp)
* substr (<optional> pattern to match in filename)
* Return: 0 on success, non-zero on failure
*
* Notes:
* (1) This removes the matched files in /tmp or a subdirectory of /tmp.
* Use NULL for @subdir if the files are in /tmp.
* (2) If @substr == NULL, this removes all files in the directory.
* If @substr == "" (empty), this removes no files.
* If both @subdir == NULL and @substr == NULL, this removes
* all files in /tmp.
* (3) Use unix pathname separators.
* (4) By calling genPathname(), this does an automatic translation
* of "/tmp" if the pathname begins with "/tmp":
* ==> /tmp (unix) [default]
* ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1]
* ==> <Temp>/leptonica (windows)
* (5) Error conditions:
* * returns -1 if the directory is not found
* * returns the number of files (> 0) that it was unable to remove.
*/
l_int32
lept_rm_match(const char *subdir,
const char *substr)
{
char *path, *fname;
char tempdir[256];
l_int32 i, n, ret;
SARRAY *sa;
PROCNAME("lept_rm_match");
makeTempDirname(tempdir, 256, subdir);
if ((sa = getSortedPathnamesInDirectory(tempdir, substr, 0, 0)) == NULL)
return ERROR_INT("sa not made", procName, -1);
n = sarrayGetCount(sa);
if (n == 0) {
L_WARNING("no matching files found\n", procName);
return 0;
}
ret = 0;
for (i = 0; i < n; i++) {
fname = sarrayGetString(sa, i, L_NOCOPY);
path = genPathname(fname, NULL);
if (lept_rmfile(path) != 0) {
L_ERROR("failed to remove %s\n", procName, path);
ret++;
}
FREE(path);
}
sarrayDestroy(&sa);
return ret;
}
/*!
* lept_rm()
*
* Input: subdir (<optional> of '/tmp'; can be NULL)
* tail (filename without the directory)
* Return: 0 on success, non-zero on failure
*
* Notes:
* (1) This uses genPathname() to do a name translation from /tmp:
* ==> /tmp (unix) [default]
* ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1]
* ==> <Temp>/leptonica (windows)
*/
l_int32
lept_rm(const char *subdir,
const char *tail)
{
char *path;
char newtemp[256];
l_int32 ret;
PROCNAME("lept_rm");
if (!tail || strlen(tail) == 0)
return ERROR_INT("tail undefined or empty", procName, 1);
makeTempDirname(newtemp, 256, subdir);
path = genPathname(newtemp, tail);
ret = lept_rmfile(path);
FREE(path);
return ret;
}
/*!
* lept_rmfile()
*
* Input: filepath (full path to file including the directory)
* Return: 0 on success, non-zero on failure
*
* Notes:
* (1) This removes the named file.
* (2) Use unix pathname separators.
* (3) Unlike the other lept_* functions in this section, this can remove
* any file -- it is not restricted to files that are in /tmp or a
* subdirectory of it. No path translation is applied for files
* in /tmp or a subdirectory of /tmp.
*/
l_int32
lept_rmfile(const char *filepath)
{
l_int32 ret;
PROCNAME("lept_rmfile");
if (!filepath || strlen(filepath) == 0)
return ERROR_INT("filepath undefined or empty", procName, 1);
#ifndef _WIN32
ret = remove(filepath);
#else
/* Set attributes to allow deletion of read-only files */
SetFileAttributes(filepath, FILE_ATTRIBUTE_NORMAL);
ret = DeleteFile(filepath) ? 0 : 1;
#endif /* !_WIN32 */
return ret;
}
/*!
* lept_mv()
*
* Input: srcfile
* newdir (<optional>; can be NULL)
* newtail (<optional>; can be NULL)
* &newpath (<optional return> of actual path; can be NULL)
* Return: 0 on success, non-zero on failure
*
* Notes:
* (1) This moves @srcfile to /tmp or to a subdirectory of /tmp.
* (2) @srcfile can either be a full path or relative to the
* current directory.
* (3) @newdir can either specify an existing subdirectory of /tmp
* or can be NULL. In the latter case, the file will be written
* into /tmp.
* (4) @newtail can either specify a filename tail or, if NULL,
* the filename is taken from the tail of @srcfile.
* (5) For debugging, the computed newpath can be returned. It must
* be freed by the caller.
* (6) Reminders:
* (a) use unix pathname separators
* (b) Uses directory translation for files in /tmp:
* ==> /tmp (unix) [default]
* ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1]
* ==> <Temp>/leptonica (windows)
* (7) Examples before directory translation:
* * newdir = NULL, newtail = NULL ==> /tmp/src-tail
* * newdir = NULL, newtail = abc ==> /tmp/abc
* * newdir = def, newtail = NULL ==> /tmp/def/src-tail
* * newdir = def, newtail = abc ==> /tmp/def/abc
*/
l_int32
lept_mv(const char *srcfile,
const char *newdir,
const char *newtail,
char **pnewpath)
{
char *srcpath, *newpath, *dir, *srctail;
char newtemp[256];
l_int32 ret;
PROCNAME("lept_mv");
if (!srcfile)
return ERROR_INT("srcfile not defined", procName, 1);
/* Require output pathname to be in /tmp/ or a subdirectory */
if (makeTempDirname(newtemp, 256, newdir) == 1)
return ERROR_INT("newdir not NULL or a subdir of /tmp", procName, 1);
/* Get canonical src pathname */
splitPathAtDirectory(srcfile, &dir, &srctail);
srcpath = genPathname(dir, srctail);
FREE(dir);
/* Generate output pathname */
if (!newtail || newtail[0] == '\0')
newpath = genPathname(newtemp, srctail);
else
newpath = genPathname(newtemp, newtail);
FREE(srctail);
/* Overwrite any existing file at 'newpath' */
#ifndef _WIN32
ret = fileCopy(srcpath, newpath);
if (!ret)
remove(srcpath);
#else
ret = MoveFileEx(srcpath, newpath,
MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) ? 0 : 1;
#endif
FREE(srcpath);
if (pnewpath)
*pnewpath = newpath;
else
FREE(newpath);
return ret;
}
/*!
* lept_cp()
*
* Input: srcfile
* newdir (<optional>; can be NULL)
* newtail (<optional>; can be NULL)
* &newpath (<optional return> of actual path; can be NULL)
* Return: 0 on success, non-zero on failure
*
* Notes:
* (1) This copies @srcfile to /tmp or to a subdirectory of /tmp.
* (2) @srcfile can either be a full path or relative to the
* current directory.
* (3) @newdir can either specify an existing subdirectory of /tmp,
* or can be NULL. In the latter case, the file will be written
* into /tmp.
* (4) @newtail can either specify a filename tail or, if NULL,
* the filename is taken from the tail of @srcfile.
* (5) For debugging, the computed newpath can be returned. It must
* be freed by the caller.
* (6) Reminders:
* (a) use unix pathname separators
* (b) Uses directory translation for files in /tmp:
* ==> /tmp (unix) [default]
* ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1]
* ==> <Temp>/leptonica (windows)
* (7) Examples:
* * newdir = NULL, newtail = NULL ==> /tmp/src-tail
* * newdir = NULL, newtail = abc ==> /tmp/abc
* * newdir = def, newtail = NULL ==> /tmp/def/src-tail
* * newdir = def, newtail = abc ==> /tmp/def/abc
*
*/
l_int32
lept_cp(const char *srcfile,
const char *newdir,
const char *newtail,
char **pnewpath)
{
char *srcpath, *newpath, *dir, *srctail;
char newtemp[256];
l_int32 ret;
PROCNAME("lept_cp");
if (!srcfile)
return ERROR_INT("srcfile not defined", procName, 1);
/* Require output pathname to be in /tmp or a subdirectory */
if (makeTempDirname(newtemp, 256, newdir) == 1)
return ERROR_INT("newdir not NULL or a subdir of /tmp", procName, 1);
/* Get canonical src pathname */
splitPathAtDirectory(srcfile, &dir, &srctail);
srcpath = genPathname(dir, srctail);
FREE(dir);
/* Generate output pathname */
if (!newtail || newtail[0] == '\0')
newpath = genPathname(newtemp, srctail);
else
newpath = genPathname(newtemp, newtail);
FREE(srctail);
/* Overwrite any existing file at 'newpath' */
#ifndef _WIN32
ret = fileCopy(srcpath, newpath);
#else
ret = CopyFile(srcpath, newpath, FALSE) ? 0 : 1;
#endif
FREE(srcpath);
if (pnewpath)
*pnewpath = newpath;
else
FREE(newpath);
return ret;
}
/*--------------------------------------------------------------------*
* General 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
*
* Notes:
* (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
* abc --> dir: [empty string] tail: abc
* (4) The input can have either forward (unix) or backward (win)
* slash separators. The output has unix separators.
* Note that Win32 pathname functions generally accept both
* slash forms, but the windows command line interpreter
* only accepts backward slashes, because forward slashes are
* used to demarcate switches (vs. dashes in unix).
*/
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);
convertSepCharsInPath(cpathname, UNIX_PATH_SEPCHAR);
lastslash = strrchr(cpathname, '/');
if (lastslash) {
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/ ext: .jpg
* /usr/tmp.jpg/ --> basename: /usr/tmp.jpg/ ext: [empty str]
* ./.jpg --> basename: ./ ext: .jpg
* (4) The input can have either forward (unix) or backward (win)
* slash separators. The output has unix separators.
*/
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;
}
/*!
* pathJoin()
*
* Input: dir (<optional> can be null)
* fname (<optional> can be null)
* Return: specially concatenated path, or null on error
*
* Notes:
* (1) Use unix-style pathname separators ('/').
* (2) @fname can be the entire path, or part of the path containing
* at least one directory, or a tail without a directory, or null.
* (3) It produces a path that strips multiple slashes to a single
* slash, joins @dir and @fname by a slash, and has no trailing
* slashes (except in the cases where @dir == "/" and
* @fname == NULL, or v.v.).
* (4) If both @dir and @fname are null, produces an empty string.
* (5) Neither @dir nor @fname can begin with '.'.
* (6) The result is not canonicalized or tested for correctness:
* garbage in (e.g., /&%), garbage out.
* (7) Examples:
* //tmp// + //abc/ --> /tmp/abc
* tmp/ + /abc/ --> tmp/abc
* tmp/ + abc/ --> tmp/abc
* /tmp/ + /// --> /tmp
* /tmp/ + NULL --> /tmp
* // + /abc// --> /abc
* // + NULL --> /
* NULL + /abc/def/ --> /abc/def
* NULL + abc// --> abc
* NULL + // --> /
* NULL + NULL --> (empty string)
* "" + "" --> (empty string)
* "" + / --> /
* ".." + /etc/foo --> NULL
* /tmp + ".." --> NULL
*/
char *
pathJoin(const char *dir,
const char *fname)
{
char *slash = (char *)"/";
char *str, *dest;
l_int32 i, n1, n2, emptydir;
size_t size;
SARRAY *sa1, *sa2;
L_BYTEA *ba;
PROCNAME("pathJoin");
if (!dir && !fname)
return stringNew("");
if (dir && dir[0] == '.')
return (char *)ERROR_PTR("dir starts with '.'", procName, NULL);
if (fname && fname[0] == '.')
return (char *)ERROR_PTR("fname starts with '.'", procName, NULL);
sa1 = sarrayCreate(0);
sa2 = sarrayCreate(0);
ba = l_byteaCreate(4);
/* Process @dir */
if (dir && strlen(dir) > 0) {
if (dir[0] == '/')
l_byteaAppendString(ba, slash);
sarraySplitString(sa1, dir, "/"); /* removes all slashes */
n1 = sarrayGetCount(sa1);
for (i = 0; i < n1; i++) {
str = sarrayGetString(sa1, i, L_NOCOPY);
l_byteaAppendString(ba, str);
l_byteaAppendString(ba, slash);
}
}
/* Special case to add leading slash: dir NULL or empty string */
emptydir = dir && strlen(dir) == 0;
if ((!dir || emptydir) && fname && strlen(fname) > 0 && fname[0] == '/')
l_byteaAppendString(ba, slash);
/* Process @fname */
if (fname && strlen(fname) > 0) {
sarraySplitString(sa2, fname, "/");
n2 = sarrayGetCount(sa2);
for (i = 0; i < n2; i++) {
str = sarrayGetString(sa2, i, L_NOCOPY);
l_byteaAppendString(ba, str);
l_byteaAppendString(ba, slash);
}
}
/* Remove trailing slash */
dest = (char *)l_byteaCopyData(ba, &size);
if (size > 1 && dest[size - 1] == '/')
dest[size - 1] = '\0';
sarrayDestroy(&sa1);
sarrayDestroy(&sa2);
l_byteaDestroy(&ba);
return dest;
}
/*!
* appendSubdirectory()
*
* Input: dir
* subdir
* Return: concatenated directory path without trailing slash,
* or null on error
*
* Notes:
* (1) Use unix pathname separators
* (2) Allocates a new string: <dir>/<subdir>
*/
char *
appendSubdirectory(const char *dir,
const char *subdir)
{
char *newdir;
size_t len1, len2, len3, len4;
PROCNAME("appendSubdirectory");
if (!dir || !subdir)
return (char *)ERROR_PTR("dir and subdir not both defined",
procName, NULL);
len1 = strlen(dir);
len2 = strlen(subdir);
len3 = len1 + len2 + 6;
newdir = (char *)CALLOC(len3, 1);
strncat(newdir, dir, len3); /* add dir */
if (newdir[len1 - 1] != '/') /* add '/' if necessary */
newdir[len1] = '/';
if (subdir[0] == '/') /* add subdir, stripping leading '/' */
strncat(newdir, subdir + 1, len3);
else
strncat(newdir, subdir, len3);
len4 = strlen(newdir);
if (newdir[len4 - 1] == '/') /* strip trailing '/' */
newdir[len4 - 1] = '\0';
return newdir;
}
/*--------------------------------------------------------------------*
* Special file name operations *
*--------------------------------------------------------------------*/
/*!
* convertSepCharsInPath()
*
* Input: path
* type (UNIX_PATH_SEPCHAR, WIN_PATH_SEPCHAR)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) In-place conversion.
* (2) Type is the resulting type:
* * UNIX_PATH_SEPCHAR: '\\' ==> '/'
* * WIN_PATH_SEPCHAR: '/' ==> '\\'
* (3) Virtually all path operations in leptonica use unix separators.
*/
l_int32
convertSepCharsInPath(char *path,
l_int32 type)
{
l_int32 i;
size_t len;
PROCNAME("convertSepCharsInPath");
if (!path)
return ERROR_INT("path not defined", procName, 1);
if (type != UNIX_PATH_SEPCHAR && type != WIN_PATH_SEPCHAR)
return ERROR_INT("invalid type", procName, 1);
len = strlen(path);
if (type == UNIX_PATH_SEPCHAR) {
for (i = 0; i < len; i++) {
if (path[i] == '\\')
path[i] = '/';
}
} else { /* WIN_PATH_SEPCHAR */
for (i = 0; i < len; i++) {
if (path[i] == '/')
path[i] = '\\';
}
}
return 0;
}
/*!
* genPathname()
*
* Input: dir (<optional> directory or full path name, with or without
* trailing '/')
* fname (<optional> file name within a directory)
* Return: pathname (either a directory or full path), or null on error
*
* Notes:
* (1) This function generates actual paths in the following ways:
* * from two sub-parts (e.g., a directory and a file name).
* * from a single path full path, placed in @dir, with
* @fname == NULL.
* * from the name of a file in the local directory placed in
* @fname, with @dir == NULL.
* * if in a "/tmp" directory and on windows, the windows
* temp directory is used.
* (2) If the root of @dir is '/tmp', this does name translation:
* ==> /tmp (unix) [default]
* ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1]
* ==> <Temp>/leptonica (windows)
* where <Temp> is the windows temp directory.
* (3) There are four cases for the input:
* (a) @dir is a directory and @fname is defined: result is a full path
* (b) @dir is a directory and @fname is null: result is a directory
* (c) @dir is a full path and @fname is null: result is a full path
* (d) @dir is null or an empty string: start in the current dir;
* result is a full path
* (4) In all cases, the resulting pathname is not terminated with a slash
* (5) The caller is responsible for freeing the returned pathname.
*/
char *
genPathname(const char *dir,
const char *fname)
{
char *cdir, *pathout;
l_int32 dirlen, namelen, size;
PROCNAME("genPathname");
if (!dir && !fname)
return (char *)ERROR_PTR("no input", procName, NULL);
/* Handle the case where we start from the current directory */
if (!dir || dir[0] == '\0') {
if ((cdir = getcwd(NULL, 0)) == NULL)
return (char *)ERROR_PTR("no current dir found", procName, NULL);
} else {
cdir = stringNew(dir);
}
/* Convert to unix path separators, and remove the trailing
* slash in the directory, except when dir == "/" */
convertSepCharsInPath(cdir, UNIX_PATH_SEPCHAR);
dirlen = strlen(cdir);
if (cdir[dirlen - 1] == '/' && dirlen != 1) {
cdir[dirlen - 1] = '\0';
dirlen--;
}
namelen = (fname) ? strlen(fname) : 0;
size = dirlen + namelen + 256;
if ((pathout = (char *)CALLOC(size, sizeof(char))) == NULL)
return (char *)ERROR_PTR("pathout not made", procName, NULL);
/* First handle @dir (which may be a full pathname) */
if (strncmp(cdir, "/tmp", 4) != 0) { /* not in /tmp; OK as is */
stringCopy(pathout, cdir, dirlen);
} else { /* in /tmp */
#ifdef _WIN32
/* Start with the temp dir */
char dirt[MAX_PATH];
GetTempPath(sizeof(dirt), dirt); /* get the windows temp directory */
stringCopy(pathout, dirt, strlen(dirt) - 1);
/* Add a special subdirectory if it's not already there. This is
* advantageous for WIN32 because it puts all temporary files in
* a single subdirectory of <Temp>, facilitating cleanup. */
if (dirlen <= 5 ||
(dirlen > 5 && strncmp(cdir + 5, "leptonica", 9) != 0))
stringCat(pathout, size, "/leptonica");
/* Add the rest of cdir */
if (dirlen > 4)
stringCat(pathout, size, cdir + 4);
#else /* unix */
/* Add subdirectory if requested and it is not already in cdir.
* For the latter, add if either cdir is just /tmp or /tmp/, or
* if there is a subdirectory of /tmp but it's not "leptonica". */
if (ADD_LEPTONICA_SUBDIR &&
(dirlen <= 5 ||
(dirlen > 5 && strncmp(cdir + 5, "leptonica", 9) != 0))) {
stringCopy(pathout, "/tmp/leptonica", 14);
if (dirlen > 4)
stringCat(pathout, size, cdir + 4);
} else { /* OK as is */
stringCopy(pathout, cdir, dirlen);
}
#endif /* _WIN32 */
}
/* Now handle @fname */
if (fname && strlen(fname) > 0) {
dirlen = strlen(pathout);
pathout[dirlen] = '/';
strncat(pathout, fname, namelen);
}
FREE(cdir);
return pathout;
}
/*!
* makeTempDirname()
*
* Input: result (preallocated on stack or heap and passed in)
* nbytes (size of @result array, in bytes)
* subdir (<optional>; can be NULL or an empty string)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) This generates the directory path for output temp files,
* written into @result with unix separators.
* (2) Caller allocates @result, large enough to hold the path,
* which is:
* /tmp/@subdir (unix) [default]
* /tmp/leptonica/@subdir (unix) [if ADD_LEPTONICA_SUBDIR == 1]
* <Temp>/leptonica/@subdir (windows)
* where <Temp> is a path on windows determined by GenTempPath().
* (3) Usage example:
* char result[256];
* makeTempDirname(result, 256, "golden");
*/
l_int32
makeTempDirname(char *result,
size_t nbytes,
const char *subdir)
{
char *dir, *path;
l_int32 ret = 0;
size_t pathlen;
PROCNAME("makeTempDirname");
if (!result)
return ERROR_INT("result not defined", procName, 1);
if (subdir && ((subdir[0] == '.') || (subdir[0] == '/')))
return ERROR_INT("subdir not an actual subdirectory", procName, 1);
memset(result, 0, nbytes);
dir = pathJoin("/tmp", subdir);
path = genPathname(dir, NULL);
pathlen = strlen(path);
if (pathlen < nbytes - 1) {
strncpy(result, path, pathlen);
} else {
L_ERROR("result array too small for path\n", procName);
ret = 1;
}
FREE(dir);
FREE(path);
return ret;
}
/*!
* modifyTrailingSlash()
*
* Input: path (preallocated on stack or heap and passed in)
* nbytes (size of @path array, in bytes)
* flag (L_ADD_TRAIL_SLASH or L_REMOVE_TRAIL_SLASH)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) This carries out the requested action if necessary.
*/
l_int32
modifyTrailingSlash(char *path,
size_t nbytes,
l_int32 flag)
{
char lastchar;
size_t len;
PROCNAME("modifyTrailingSlash");
if (!path)
return ERROR_INT("path not defined", procName, 1);
if (flag != L_ADD_TRAIL_SLASH && flag != L_REMOVE_TRAIL_SLASH)
return ERROR_INT("invalid flag", procName, 1);
len = strlen(path);
lastchar = path[len - 1];
if (flag == L_ADD_TRAIL_SLASH && lastchar != '/' && len < nbytes - 2) {
path[len] = '/';
path[len + 1] = '\0';
} else if (flag == L_REMOVE_TRAIL_SLASH && lastchar == '/') {
path[len - 1] = '\0';
}
return 0;
}
/*!
* genTempFilename()
*
* Input: dir (directory name; use '.' for local dir;
* no trailing '/' and @dir == "/" is invalid)
* tail (<optional> tailname, including extension if any;
* can be null or empty but can't contain '/')
* usetime (1 to include current time in microseconds in
* the filename; 0 to omit.
* usepid (1 to include pid in filename; 0 to omit.
* Return: temp filename, or null on error
*
* Notes:
* (1) This makes a filename that is as unique as desired, and which
* can optionally include both the time and pid in the name.
* (2) Use unix-style pathname separators ('/').
* (3) Specifying the root directory (@dir == "/") is invalid.
* (4) Specifying a @tail containing '/' is invalid.
* (5) The most general form (@usetime = @usepid = 1) is:
* <dir>/<usec>_<pid>_<tail>
* When @usetime = 1, @usepid = 0, the output filename is:
* <dir>/<usec>_<tail>
* When @usepid = 0, @usepid = 1, the output filename is:
* <dir>/<pid>_<tail>
* When @usetime = @usepid = 0, the output filename is:
* <dir>/<tail>
* Note: It is not valid to have @tail = null or empty and have
* both @usetime = @usepid = 0. That is, there must be
* some non-empty tail name.
* (6) N.B. The caller is responsible for freeing the returned filename.
* For windows, to avoid C-runtime boundary crossing problems
* when using DLLs, you must use lept_free() to free the name.
* (7) When @dir is /tmp or a subdirectory of /tmp, genPathname()
* does a name translation for '/tmp':
* ==> /tmp (unix) [default]
* ==> /tmp/leptonica (unix) [if ADD_LEPTONICA_SUBDIR == 1]
* ==> <Temp>/leptonica (windows)
* where <Temp> is a path on windows determined by GenTempPath().
* (8) Set @usetime = @usepid = 1 when
* (a) more than one process is writing and reading temp files, or
* (b) multiple threads from a single process call this function, or
* (c) there is the possiblity of an attack where the intruder
* is logged onto the server and might try to guess filenames.
*/
char *
genTempFilename(const char *dir,
const char *tail,
l_int32 usetime,
l_int32 usepid)
{
char buf[256];
char *newpath;
l_int32 i, buflen, usec, pid, emptytail;
PROCNAME("genTempFilename");
if (!dir)
return (char *)ERROR_PTR("dir not defined", procName, NULL);
if (dir && strlen(dir) == 1 && dir[0] == '/')
return (char *)ERROR_PTR("dir == '/' not permitted", procName, NULL);
if (tail && strlen(tail) > 0 && stringFindSubstr(tail, "/", NULL))
return (char *)ERROR_PTR("tail can't contain '/'", procName, NULL);
emptytail = tail && (strlen(tail) == 0);
if (!usetime && !usepid && (!tail || emptytail))
return (char *)ERROR_PTR("name can't be a directory", procName, NULL);
if (usepid) pid = getpid();
buflen = sizeof(buf);
for (i = 0; i < buflen; i++)
buf[i] = 0;
l_getCurrentTime(NULL, &usec);
newpath = genPathname(dir, NULL);
if (usetime && usepid)
snprintf(buf, buflen, "%s/%d_%d_", newpath, usec, pid);
else if (usetime)
snprintf(buf, buflen, "%s/%d_", newpath, usec);
else if (usepid)
snprintf(buf, buflen, "%s/%d_", newpath, pid);
else
snprintf(buf, buflen, "%s/", newpath);
FREE(newpath);
return stringJoin(buf, tail);
}
/*!
* 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 */
}
/*---------------------------------------------------------------------*
* File corruption operations *
*---------------------------------------------------------------------*/
/*!
* fileCorruptByDeletion()
*
* Input: filein
* loc (fractional location of start of deletion)
* size (fractional size of deletion)
* fileout (corrupted file)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) @loc and @size are expressed as a fraction of the file size.
* (2) This makes a copy of the data in @filein, where bytes in the
* specified region have deleted.
* (3) If (@loc + @size) >= 1.0, this deletes from the position
* represented by @loc to the end of the file.
* (4) It is useful for testing robustness of I/O wrappers when the
* data is corrupted, by simulating data corruption by deletion.
*/
l_int32
fileCorruptByDeletion(const char *filein,
l_float32 loc,
l_float32 size,
const char *fileout)
{
l_int32 i, locb, sizeb, rembytes;
size_t inbytes, outbytes;
l_uint8 *datain, *dataout;
PROCNAME("fileCorruptByDeletion");
if (!filein || !fileout)
return ERROR_INT("filein and fileout not both specified", procName, 1);
if (loc < 0.0 || loc >= 1.0)
return ERROR_INT("loc must be in [0.0 ... 1.0)", procName, 1);
if (size <= 0.0)
return ERROR_INT("size must be > 0.0", procName, 1);
if (loc + size > 1.0)
size = 1.0 - loc;
datain = l_binaryRead(filein, &inbytes);
locb = (l_int32)(loc * inbytes + 0.5);
locb = L_MIN(locb, inbytes - 1);
sizeb = (l_int32)(size * inbytes + 0.5);
sizeb = L_MAX(1, sizeb);
sizeb = L_MIN(sizeb, inbytes - locb); /* >= 1 */
L_INFO("Removed %d bytes at location %d\n", procName, sizeb, locb);
rembytes = inbytes - locb - sizeb; /* >= 0; to be copied, after excision */
outbytes = inbytes - sizeb;
dataout = (l_uint8 *)CALLOC(outbytes, 1);
for (i = 0; i < locb; i++)
dataout[i] = datain[i];
for (i = 0; i < rembytes; i++)
dataout[locb + i] = datain[locb + sizeb + i];
l_binaryWrite(fileout, "w", dataout, outbytes);
FREE(datain);
FREE(dataout);
return 0;
}
/*!
* fileCorruptByMutation()
*
* Input: filein
* loc (fractional location of start of randomization)
* size (fractional size of randomization)
* fileout (corrupted file)
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) @loc and @size are expressed as a fraction of the file size.
* (2) This makes a copy of the data in @filein, where bytes in the
* specified region have been replaced by random data.
* (3) If (@loc + @size) >= 1.0, this modifies data from the position
* represented by @loc to the end of the file.
* (4) It is useful for testing robustness of I/O wrappers when the
* data is corrupted, by simulating data corruption.
*/
l_int32
fileCorruptByMutation(const char *filein,
l_float32 loc,
l_float32 size,
const char *fileout)
{
l_int32 i, locb, sizeb;
size_t bytes;
l_uint8 *data;
PROCNAME("fileCorruptByMutation");
if (!filein || !fileout)
return ERROR_INT("filein and fileout not both specified", procName, 1);
if (loc < 0.0 || loc >= 1.0)
return ERROR_INT("loc must be in [0.0 ... 1.0)", procName, 1);
if (size <= 0.0)
return ERROR_INT("size must be > 0.0", procName, 1);
if (loc + size > 1.0)
size = 1.0 - loc;
data = l_binaryRead(filein, &bytes);
locb = (l_int32)(loc * bytes + 0.5);
locb = L_MIN(locb, bytes - 1);
sizeb = (l_int32)(size * bytes + 0.5);
sizeb = L_MAX(1, sizeb);
sizeb = L_MIN(sizeb, bytes - locb); /* >= 1 */
L_INFO("Randomizing %d bytes at location %d\n", procName, sizeb, locb);
/* Make an array of random bytes and do the substitution */
for (i = 0; i < sizeb; i++) {
data[locb + i] =
(l_uint8)(255.9 * ((l_float64)rand() / (l_float64)RAND_MAX));
}
l_binaryWrite(fileout, "w", data, bytes);
FREE(data);
return 0;
}
/*---------------------------------------------------------------------*
* Generate random integer in given range *
*---------------------------------------------------------------------*/
/*!
* genRandomIntegerInRange()
*
* Input: range (size of range; must be >= 2)
* seed (use 0 to skip; otherwise call srand)
* val (<return> random integer in range {0 ... range-1}
* Return: 0 if OK, 1 on error
*
* Notes:
* (1) For example, to choose a rand integer between 0 and 99,
* use @range = 100.
*/
l_int32
genRandomIntegerInRange(l_int32 range,
l_int32 seed,
l_int32 *pval)
{
PROCNAME("genRandomIntegerInRange");
if (!pval)
return ERROR_INT("&val not defined", procName, 1);
*pval = 0;
if (range < 2)
return ERROR_INT("range must be >= 2", procName, 1);
if (seed > 0) srand(seed);
*pval = (l_int32)((l_float64)range *
((l_float64)rand() / (l_float64)RAND_MAX));
return 0;
}
/*---------------------------------------------------------------------*
* Simple math function *
*---------------------------------------------------------------------*/
/*!
* lept_roundftoi()
*
* Input: fval
* Return: value rounded to int
*
* Notes:
* (1) For fval >= 0, fval --> round(fval) == floor(fval + 0.5)
* For fval < 0, fval --> -round(-fval))
* This is symmetric around 0.
* e.g., for fval in (-0.5 ... 0.5), fval --> 0
*/
l_int32
lept_roundftoi(l_float32 fval)
{
return (fval >= 0.0) ? (l_int32)(fval + 0.5) : (l_int32)(fval - 0.5);
}
/*---------------------------------------------------------------------*
* Gray code conversion *
*---------------------------------------------------------------------*/
/*!
* convertBinaryToGrayCode()
*
* Input: val
* Return: gray code value
*
* Notes:
* (1) Gray code values corresponding to integers differ by
* only one bit transition between successive integers.
*/
l_uint32
convertBinaryToGrayCode(l_uint32 val)
{
return (val >> 1) ^ val;
}
/*!
* convertGrayCodeToBinary()
*
* Input: gray code value
* Return: binary value
*/
l_uint32
convertGrayCodeToBinary(l_uint32 val)
{
l_uint32 shift;
for (shift = 1; shift < 32; shift <<= 1)
val ^= val >> shift;
return val;
}
/*---------------------------------------------------------------------*
* Leptonica version number *
*---------------------------------------------------------------------*/
/*!
* getLeptonicaVersion()
*
* Return: string of version number (e.g., 'leptonica-1.68')
*
* Notes:
* (1) The caller has responsibility to free the memory.
*/
char *
getLeptonicaVersion()
{
char *version = (char *)CALLOC(100, sizeof(char));
#ifdef _MSC_VER
#ifdef _USRDLL
char dllStr[] = "DLL";
#else
char dllStr[] = "LIB";
#endif
#ifdef _DEBUG
char debugStr[] = "Debug";
#else
char debugStr[] = "Release";
#endif
#ifdef _M_IX86
char bitStr[] = " x86";
#elif _M_X64
char bitStr[] = " x64";
#else
char bitStr[] = "";
#endif
snprintf(version, 100, "leptonica-%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, "leptonica-%d.%d", LIBLEPT_MAJOR_VERSION,
LIBLEPT_MINOR_VERSION);
#endif /* _MSC_VER */
return version;
}
/*---------------------------------------------------------------------*
* Timing procs *
*---------------------------------------------------------------------*/
#ifndef _WIN32
#include <sys/time.h>
#include <sys/resource.h>
static struct rusage rusage_before;
static struct rusage rusage_after;
/*!
* startTimer(), stopTimer()
*
* Notes:
* (1) These measure the cpu time elapsed between the two calls:
* startTimer();
* ....
* fprintf(stderr, "Elapsed time = %7.3f sec\n", stopTimer());
*/
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);
}
/*!
* startTimerNested(), stopTimerNested()
*
* Example of usage:
*
* L_TIMER t1 = startTimerNested();
* ....
* L_TIMER t2 = startTimerNested();
* ....
* fprintf(stderr, "Elapsed time 2 = %7.3f sec\n", stopTimerNested(t2));
* ....
* fprintf(stderr, "Elapsed time 1 = %7.3f sec\n", stopTimerNested(t1));
*/
L_TIMER
startTimerNested(void)
{
struct rusage *rusage_start;
rusage_start = (struct rusage *)CALLOC(1, sizeof(struct rusage));
getrusage(RUSAGE_SELF, rusage_start);
return rusage_start;
}
l_float32
stopTimerNested(L_TIMER rusage_start)
{
l_int32 tsec, tusec;
struct rusage rusage_stop;
getrusage(RUSAGE_SELF, &rusage_stop);
tsec = rusage_stop.ru_utime.tv_sec -
((struct rusage *)rusage_start)->ru_utime.tv_sec;
tusec = rusage_stop.ru_utime.tv_usec -
((struct rusage *)rusage_start)->ru_utime.tv_usec;
FREE(rusage_start);
return (tsec + ((l_float32)tusec) / 1000000.0);
}
/*!
* l_getCurrentTime()
*
* Input: &sec (<optional return> in seconds since birth of Unix)
* &usec (<optional return> in microseconds since birth of Unix)
* Return: void
*/
void
l_getCurrentTime(l_int32 *sec,
l_int32 *usec)
{
struct timeval tv;
gettimeofday(&tv, NULL);
if (sec) *sec = (l_int32)tv.tv_sec;
if (usec) *usec = (l_int32)tv.tv_usec;
return;
}
#else /* _WIN32 : resource.h not implemented under Windows */
/* Note: if division by 10^7 seems strange, the time is expressed
* as the number of 100-nanosecond intervals that have elapsed
* since 12:00 A.M. January 1, 1601. */
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;
ULONGLONG hnsec; /* in units of hecto-nanosecond (100 ns) intervals */
this_process = GetCurrentProcess();
GetProcessTimes(this_process, &start, &stop, &kernel, &user);
utime_after.LowPart = user.dwLowDateTime;
utime_after.HighPart = user.dwHighDateTime;
hnsec = utime_after.QuadPart - utime_before.QuadPart;
return (l_float32)(signed)hnsec / 10000000.0;
}
L_TIMER
startTimerNested(void)
{
HANDLE this_process;
FILETIME start, stop, kernel, user;
ULARGE_INTEGER *utime_start;
this_process = GetCurrentProcess();
GetProcessTimes (this_process, &start, &stop, &kernel, &user);
utime_start = (ULARGE_INTEGER *)CALLOC(1, sizeof(ULARGE_INTEGER));
utime_start->LowPart = user.dwLowDateTime;
utime_start->HighPart = user.dwHighDateTime;
return utime_start;
}
l_float32
stopTimerNested(L_TIMER utime_start)
{
HANDLE this_process;
FILETIME start, stop, kernel, user;
ULARGE_INTEGER utime_stop;
ULONGLONG hnsec; /* in units of 100 ns intervals */
this_process = GetCurrentProcess ();
GetProcessTimes (this_process, &start, &stop, &kernel, &user);
utime_stop.LowPart = user.dwLowDateTime;
utime_stop.HighPart = user.dwHighDateTime;
hnsec = utime_stop.QuadPart - ((ULARGE_INTEGER *)utime_start)->QuadPart;
FREE(utime_start);
return (l_float32)(signed)hnsec / 10000000.0;
}
void
l_getCurrentTime(l_int32 *sec,
l_int32 *usec)
{
ULARGE_INTEGER utime, birthunix;
FILETIME systemtime;
LONGLONG birthunixhnsec = 116444736000000000; /*in units of 100 ns */
LONGLONG usecs;
GetSystemTimeAsFileTime(&systemtime);
utime.LowPart = systemtime.dwLowDateTime;
utime.HighPart = systemtime.dwHighDateTime;
birthunix.LowPart = (DWORD) birthunixhnsec;
birthunix.HighPart = birthunixhnsec >> 32;
usecs = (LONGLONG) ((utime.QuadPart - birthunix.QuadPart) / 10);
if (sec) *sec = (l_int32) (usecs / 1000000);
if (usec) *usec = (l_int32) (usecs % 1000000);
return;
}
#endif
/*!
* startWallTimer()
* Input: void
* Return: walltimer-ptr
*
* stopWallTimer()
* Input: &walltimer-ptr
* Return: time (wall time elapsed in seconds)
*
* Notes:
* (1) These measure the wall clock time elapsed between the two calls:
* L_WALLTIMER *timer = startWallTimer();
* ....
* fprintf(stderr, "Elapsed time = %f sec\n", stopWallTimer(&timer);
* (2) Note that the timer object is destroyed by stopWallTimer().
*/
L_WALLTIMER *
startWallTimer(void)
{
L_WALLTIMER *timer;
timer = (L_WALLTIMER *)CALLOC(1, sizeof(L_WALLTIMER));
l_getCurrentTime(&timer->start_sec, &timer->start_usec);
return timer;
}
l_float32
stopWallTimer(L_WALLTIMER **ptimer)
{
l_int32 tsec, tusec;
L_WALLTIMER *timer;
PROCNAME("stopWallTimer");
if (!ptimer)
return (l_float32)ERROR_FLOAT("&timer not defined", procName, 0.0);
timer = *ptimer;
if (!timer)
return (l_float32)ERROR_FLOAT("timer not defined", procName, 0.0);
l_getCurrentTime(&timer->stop_sec, &timer->stop_usec);
tsec = timer->stop_sec - timer->start_sec;
tusec = timer->stop_usec - timer->start_usec;
FREE(timer);
*ptimer = NULL;
return (tsec + ((l_float32)tusec) / 1000000.0);
}
/*!
* l_getFormattedDate()
*
* Input: (none)
* Return: formatted date string, or null on error
*/
char *
l_getFormattedDate()
{
char buf[64];
time_t tmp1;
struct tm *tmp2;
tmp1 = time(NULL);
tmp2 = localtime(&tmp1);
strftime(buf, sizeof(buf), "%y%m%d%H%M%S", tmp2);
return stringNew(buf);
}