/*====================================================================* - 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 returnErrorVoid() * void l_error() * void l_errorString() * void l_errorInt() * void l_errorFloat() * void l_warning() * void l_warningString() * void l_warningInt() * void l_warningFloat() * 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 procs * char *stringRemoveChars() * l_int32 stringFindSubstr() * char *stringReplaceSubstr() * char *stringReplaceEachSubstr() * l_int32 arrayFindSequence() * * safe realloc * void *reallocNew() * * read file to memory * l_uint8 *arrayRead() * l_uint8 *arrayReadStream() * l_int32 nbytesInFile() * l_int32 fnbytesInFile() * * write memory to file * l_int32 arrayWrite() * * 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() * * timing * void startTimer() * l_float32 stopTimer() */ #include #include #include #include #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; } /*! * returnErrorVoid() * * Input: msg (error message) * procname */ void returnErrorVoid(const char *msg, const char *procname) { fprintf(stderr, "Error in %s: %s\n", procname, msg); return; } /*! * 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) { ERROR_VOID("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) { ERROR_VOID("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) { ERROR_VOID("msg or procname not defined in l_errorInt()", procname); return; } bufsize = strlen(msg) + strlen(procname) + 128; if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) { ERROR_VOID("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) { ERROR_VOID("msg or procname not defined in l_errorFloat()", procname); return; } bufsize = strlen(msg) + strlen(procname) + 128; if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) { ERROR_VOID("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) { ERROR_VOID("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) { ERROR_VOID("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) { ERROR_VOID("msg or procname not defined in l_warningInt()", procname); return; } bufsize = strlen(msg) + strlen(procname) + 128; if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) { ERROR_VOID("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_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) { ERROR_VOID("msg or procname not defined in l_warningFloat()", procname); return; } bufsize = strlen(msg) + strlen(procname) + 128; if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) { ERROR_VOID("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_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) { ERROR_VOID("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) { ERROR_VOID("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) { ERROR_VOID("msg or procname not defined in l_infoInt()", procname); return; } bufsize = strlen(msg) + strlen(procname) + 128; if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) { ERROR_VOID("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) { ERROR_VOID("msg or procname not defined in l_infoInt2()", procname); return; } bufsize = strlen(msg) + strlen(procname) + 128; if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) { ERROR_VOID("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) { ERROR_VOID("msg or procname not defined in l_infoFloat()", procname); return; } bufsize = strlen(msg) + strlen(procname) + 128; if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) { ERROR_VOID("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) { ERROR_VOID("msg or procname not defined in l_infoFloat2()", procname); return; } bufsize = strlen(msg) + strlen(procname) + 128; if ((charbuf = (char *)CALLOC(bufsize, sizeof(char))) == NULL) { ERROR_VOID("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 ( 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 () * src2 string () * 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) strcat(dest, src2); 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 ( 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 ( ptr to copy of the input string, up to * the first separator token encountered) * &tail ( 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 ( 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 ( 1 if sub1 is found; 0 otherwise) * &loc ( 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 ( 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 ( 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 (; 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 ( 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, "r")) == NULL) return (l_uint8 *)ERROR_PTR("file stream not opened", procName, NULL); data = arrayReadStream(fp, pnbytes); fclose(fp); return data; } /*! * arrayReadStream() * * Input: stream * &nbytes ( 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); fp = fopen(filename, "r"); 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; } /*--------------------------------------------------------------------* * Writing bytes to file * *--------------------------------------------------------------------*/ /*! * 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; 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); if ((fp = fopen(filename, operation)) == NULL) return ERROR_INT("stream not opened", procName, 1); fwrite(data, 1, nbytes, fp); fclose(fp); 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 ( root directory name of * input path, including trailing '/') * &tail ( 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, '/'))) { 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 ( pathname not including the * last dot and characters after that) * &extension ( 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; 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); if ((charbuf = (char *)CALLOC(dirlen + namelen + 10, sizeof(char))) == NULL) return (char *)ERROR_PTR("charbuf not made", procName, NULL); if (dir[dirlen - 1] != sepchar) #if COMPILER_MSVC sprintf(charbuf, "%s\\", dir); #else sprintf(charbuf, "%s/", dir); #endif else strcpy(charbuf, dir); strcat(charbuf, fname); return charbuf; } /*! * genTempFilename() * * Input: dir (directory name; use '.' for local dir; no trailing '/') * extension ( filename extention 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 *extension) { char buf[256]; char *tempname; l_int32 pid, nchars; PROCNAME("genTempFilename"); if (!dir) return (char *)ERROR_PTR("dir not defined", procName, NULL); pid = getpid(); if (extension) nchars = strlen(extension); else nchars = 0; #if COMPILER_MSVC snprintf(buf, 255 - nchars, "%s\\%d", dir, pid); #else snprintf(buf, 25 - nchars, "%s/%d", dir, pid); #endif tempname = stringJoin(buf, extension); return tempname; } /*! * 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 */ 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] = '\n'; nret = sscanf(basename + numpre, "%d", &num); FREE(basename); if (nret == 1) return num; else return ERROR_INT("no number found", procName, -1); } /*---------------------------------------------------------------------* * Timing procs * *---------------------------------------------------------------------*/ /* * Example of use: * * startTimer(); * .... * fprintf(stderr, "Elapsed time = %7.3f sec\n", stopTimer()); */ #if !defined(__MINGW32__) && !defined(_WIN32) #include #include 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 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