From 7b52cc13af4e85f1ca2deb6b6c77de9c95ea0dcf Mon Sep 17 00:00:00 2001 From: scuri Date: Fri, 17 Oct 2008 06:10:33 +0000 Subject: First commit - moving from LuaForge to SourceForge --- src/pdflib/pdcore/pc_util.c | 2726 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2726 insertions(+) create mode 100644 src/pdflib/pdcore/pc_util.c (limited to 'src/pdflib/pdcore/pc_util.c') diff --git a/src/pdflib/pdcore/pc_util.c b/src/pdflib/pdcore/pc_util.c new file mode 100644 index 0000000..320ee52 --- /dev/null +++ b/src/pdflib/pdcore/pc_util.c @@ -0,0 +1,2726 @@ +/*---------------------------------------------------------------------------* + | PDFlib - A library for generating PDF on the fly | + +---------------------------------------------------------------------------+ + | Copyright (c) 1997-2006 Thomas Merz and PDFlib GmbH. All rights reserved. | + +---------------------------------------------------------------------------+ + | | + | This software is subject to the PDFlib license. It is NOT in the | + | public domain. Extended versions and commercial licenses are | + | available, please check http://www.pdflib.com. | + | | + *---------------------------------------------------------------------------*/ + +/* $Id: pc_util.c,v 1.1 2008/10/17 06:10:43 scuri Exp $ + * + * PDFlib various utility routines + * + */ + +#include + +#include "pc_util.h" +#include "pc_file.h" +#include "pc_ctype.h" + +#ifdef AS400 +#include /* for getenv() emulation */ +#endif + +#ifdef __sun +#include /* for finite */ +#endif + +#if defined (isfinite) +#define PDC_ISFINITE isfinite +#else /* isfinite */ + +#ifdef _WIN32 +#include +#include +#define PDC_ISFINITE _finite +#else /* _WIN32 */ + +#ifdef OS_ZOS_SASC +#define PDC_ISFINITE isfinite +#else /* OS_ZOS_SASC */ + +#define PDC_ISFINITE finite +#endif +#endif +#endif + + +/* ------------------- Floating-point number check ----------------------- */ + + + +/* + * pdc_check_number checks whether a floating-point number + * is valid and within the specified range. If not, an exception + * will be thrown. + */ +void +pdc_check_number_limits(pdc_core *pdc, const char *paramname, double dz, + double dmin, double dmax) +{ + if (!PDC_ISFINITE(dz)) + { + pdc_error(pdc, PDC_E_ILLARG_FLOAT_NAN, paramname, 0, 0, 0); + } + else if (dz < dmin) + { + pdc_error(pdc, PDC_E_ILLARG_FLOAT_TOOSMALL, paramname, + pdc_errprintf(pdc, "%f", dz), + pdc_errprintf(pdc, "%f", dmin), 0); + } + else if (dz > dmax) + { + pdc_error(pdc, PDC_E_ILLARG_FLOAT_TOOLARGE, paramname, + pdc_errprintf(pdc, "%f", dz), + pdc_errprintf(pdc, "%f", dmax), 0); + } +} + +void +pdc_check_number(pdc_core *pdc, const char *paramname, double dz) +{ + pdc_check_number_limits(pdc, paramname, dz, PDC_FLOAT_MIN, PDC_FLOAT_MAX); +} + +void +pdc_check_number_zero(pdc_core *pdc, const char *paramname, double dz) +{ + pdc_check_number_limits(pdc, paramname, dz, PDC_FLOAT_MIN, PDC_FLOAT_MAX); + + if (PDC_FLOAT_ISNULL(dz)) + { + pdc_error(pdc, PDC_E_ILLARG_FLOAT_ZERO, paramname, + pdc_errprintf(pdc, "%f", dz), 0, 0); + } +} + + +/* ---------------- "unsupported feature" error message ------------------ */ + +void +pdc_set_unsupp_error(pdc_core *pdc, int err_config, int err_lite, + pdc_bool warning) +{ + (void) err_config; + (void) err_lite; + +/* this feature is sufficient for non public version */ + if (warning) + pdc_warning(pdc, err_lite, 0, 0, 0, 0); + else + pdc_error(pdc, err_lite, 0, 0, 0, 0); +} + + +/* -------------------------- Time functions ------------------------------ */ + +#ifndef WINCE +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#include +#else +#include +#endif + +/* our private localtime() function. this one circumvents platform +** quirks we found on WINCE and Solaris, and perhaps some more in +** the future. +*/ +void +pdc_localtime(pdc_time *t) +{ +#ifdef WINCE + + SYSTEMTIME st; + + GetLocalTime (&st); + + t->second = st.wSecond; + t->minute = st.wMinute; + t->hour = st.wHour; + t->mday = st.wDay; + t->wday = st.wDayOfWeek; + t->month = st.wMonth; + t->year = st.wYear; + +#else + + time_t timer; + struct tm ltime; + + time(&timer); + +#if defined(PDC_NEEDS_R_FUNCTIONS) + + /* the localtime() function isn't thread safe on this platform. + ** a thread safe variant must be used instead. + */ + (void) localtime_r(&timer, <ime); + +#else + + ltime = *localtime(&timer); + +#endif /* !PDC_NEEDS_R_FUNCTIONS */ + + t->second = ltime.tm_sec; + t->minute = ltime.tm_min; + t->hour = ltime.tm_hour; + t->mday = ltime.tm_mday; + t->wday = ltime.tm_wday; + t->month = ltime.tm_mon; + t->year = ltime.tm_year; + +#endif /* !WINCE */ +} + +static void +pdc_localtime_r(const time_t *timer, struct tm *res) +{ +#if defined(PDC_NEEDS_R_FUNCTIONS) + (void) localtime_r(timer, res); +#else + *res = *localtime(timer); +#endif +} + +static void +pdc_gmtime_r(const time_t *timer, struct tm *res) +{ +#if defined(PDC_NEEDS_R_FUNCTIONS) + (void) gmtime_r(timer, res); +#else + *res = *gmtime(timer); +#endif +} + +void +pdc_get_timestr(char *str, pdc_bool ktoascii) +{ +#ifndef WINCE + time_t timer, gtimer; + struct tm ltime; + double diffminutes; + int utcoffset; +#else + SYSTEMTIME st; +#endif + + (void) ktoascii; + +#ifndef WINCE + time(&timer); + +#if !defined(I370) + pdc_gmtime_r(&timer, <ime); + gtimer = mktime(<ime); + pdc_localtime_r(&timer, <ime); + ltime.tm_isdst = 0; + diffminutes = difftime(mktime(<ime), gtimer) / 60; + if (diffminutes >= 0) + utcoffset = (int)(diffminutes + 0.5); + else + utcoffset = (int)(diffminutes - 0.5); +#else + utcoffset = 0; +#endif + + /* Get local time again, previous data is damaged by mktime(). */ + pdc_localtime_r(&timer, <ime); + + if (utcoffset > 0) + sprintf(str, "D:%04d%02d%02d%02d%02d%02d+%02d'%02d'", + ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, + ltime.tm_hour, ltime.tm_min, ltime.tm_sec, + utcoffset / 60, utcoffset % 60); + else if (utcoffset < 0) + sprintf(str, "D:%04d%02d%02d%02d%02d%02d-%02d'%02d'", + ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, + ltime.tm_hour, ltime.tm_min, ltime.tm_sec, + abs(utcoffset) / 60, abs(utcoffset) % 60); + else + sprintf(str, "D:%04d%02d%02d%02d%02d%02dZ", + ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday, + ltime.tm_hour, ltime.tm_min, ltime.tm_sec); + +#else + GetLocalTime (&st); + sprintf(str, "D:%04d%02d%02d%02d%02d%02d", + st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); +#endif /* !WINCE */ + + +} + + +/* -------------------------- Environment ------------------------------ */ + +char * +pdc_getenv(const char *name) +{ +#ifdef HAVE_ENVVARS + return getenv(name); +#else + (void) name; + + return (char *) 0; +#endif +} + + +/* ------------------------ Language Code ------------------------------ */ + +/* ISO 639 Windows and Mac Language codes */ +static const char lang_codes_ISO639[] = + "ab aa af sq am ar hy as ay az ba eu bn dz bh bi br bg my be km ca zh co" + "hr cs da nl en eo et fo fa fj fi fr fy gl gd gv ka de el kl gn gu ha he" + "hi hu is id ia ie iu ik ga it ja jv kn ks kk rw ky rn ko ku lo la lv li" + "ln lt mk mg ms ml mt mi mr mo mn na ne no oc or om ps pl pt pa qu rm ro" + "ru sm sg sa sr sh st tn sn sd si ss sk sl so es su sw sv tl tg ta tt te" + "th bo ti to ts tr tk tw ug uk ur uz vi vo cy wo xh yi yo zu" + "pt-br en-gb en-us de-de de-ch"; + +pdc_bool +pdc_check_lang_code(pdc_core *pdc, const char* lang_code) +{ + pdc_bool valid = pdc_false; + int i; + char* country_code; + char* language; + + if ((lang_code != NULL) && *lang_code) + { + /* do not check for IANA or private languages */ + if (!(valid = ((lang_code[0] == 'i') || (lang_code[0] == 'x')))) + { + language = pdc_strdup(pdc, lang_code); + for (i = 0; i < (int)strlen(language); i++) + { + if (pdc_isupper(language[i])) + { + language[i] = (char) pdc_tolower((int)language[i]); + } + } + + + country_code = (char *)strstr(lang_codes_ISO639, language); + valid = (country_code != NULL); + + if (!valid && (strlen(language) > 2)) + { + country_code = strchr(language, '-'); + if (country_code != NULL) + { + country_code[0] = '\0'; + + country_code = (char *)strstr(lang_codes_ISO639, language); + valid = (country_code != NULL); + + if (!valid) + { + pdc_warning(pdc, PDC_E_ILLARG_LANG_CODE, + lang_code, 0, 0, 0); + } + } + } + + pdc_free(pdc, language); + } + } + + return valid; +} + + +/* -------------------------- Bit arryas ------------------------------ */ + +void +pdc_setbit(char *bitarr, int bit) +{ + bitarr[bit/8] |= (char) (1<<(bit%8)); +} + +pdc_bool +pdc_getbit(const char *bitarr, int bit) +{ + return (pdc_bool) (bitarr[bit/8] & (1<<(bit%8))); +} + +void +pdc_setbit_text(char *bitarr, const pdc_byte *text, int len, + int nbits, int size) +{ + int i, bit; + pdc_ushort *ustext = (pdc_ushort *) text; + + for (i = 0; i < len; i += size) + { + if (size == sizeof(pdc_byte)) + bit = (int) text[i]; + else + bit = ustext[i/size]; + if (bit < nbits) pdc_setbit(bitarr, bit); + } +} + + +/* ---------- Get functions of integer binary data types --------------- */ + +pdc_short +pdc_get_le_short(const pdc_byte *data) +{ + return (pdc_short) ((pdc_short) (data[1] << 8) | data[0]); +} + +pdc_ushort +pdc_get_le_ushort(const pdc_byte *data) +{ + return (pdc_ushort) ((data[1] << 8) | data[0]); +} + +pdc_uint32 +pdc_get_le_ulong3(const pdc_byte *data) +{ + return (pdc_uint32) (((((data[2]) << 8) | data[1]) << 8) | data[0]); +} + +pdc_sint32 +pdc_get_le_long(const pdc_byte *data) +{ + return ((pdc_sint32) + (((((data[3] << 8) | data[2]) << 8) | data[1]) << 8) | data[0]); +} + +pdc_uint32 +pdc_get_le_ulong(const pdc_byte *data) +{ + return (pdc_uint32) + ((((((data[3] << 8) | data[2]) << 8) | data[1]) << 8) | data[0]); +} + +pdc_short +pdc_get_be_short(const pdc_byte *data) +{ + return (pdc_short) ((pdc_short) (data[0] << 8) | data[1]); +} + +pdc_ushort +pdc_get_be_ushort(const pdc_byte *data) +{ + return (pdc_ushort) ((data[0] << 8) | data[1]); +} + +pdc_uint32 +pdc_get_be_ulong3(const pdc_byte *data) +{ + return (pdc_uint32) (((((data[0]) << 8) | data[1]) << 8) | data[2]); +} + +pdc_sint32 +pdc_get_be_long(const pdc_byte *data) +{ + return ((pdc_sint32) + (((((data[0] << 8) | data[1]) << 8) | data[2]) << 8) | data[3]); +} + +pdc_uint32 +pdc_get_be_ulong(const pdc_byte *data) +{ + return (pdc_uint32) + ((((((data[0] << 8) | data[1]) << 8) | data[2]) << 8) | data[3]); +} + + +/* ----------------- String handling for Unicode too ------------------- */ + +/* strlen() for unicode strings, which are terminated by two zero bytes. + * wstrlen() returns the number of bytes in the Unicode string, + * not including the two terminating null bytes. + */ +static size_t +wstrlen(const char *s) +{ + size_t len = 0; + + while(s[len] != 0 || s[len+1] != 0) + { + len += 2; + } + + return len; +} + +/* + * This function returns the length in bytes for C and Unicode strings. + * Note that unlike strlen() it returns the length _including_ the + * terminator, which may be one or two null bytes. + */ +size_t +pdc_strlen(const char *text) +{ + if (pdc_is_utf16be_unicode(text) || pdc_is_utf16le_unicode(text)) + return wstrlen(text); + else + return strlen(text); +} + + +/* Allocate a local buffer and copy the string including + * the terminating sentinel. If the string starts with the Unicode BOM + * it is considered a Unicode string, and must be terminated by + * two null bytes. Otherwise it is considered a plain C string and + * must be terminated by a single null byte. + * The caller is responsible for freeing the buffer. + * + * The special functions pdc_strdup and pdc_strdup_tmp + * should be replaced by the more sophisticated function pdc_strdup_ext. + * There: flags (see pc_unicode.h): + * + * PDC_CONV_TMPALLOC, PDC_CONV_EBCDIC + * + */ +char * +pdc_strdup_ext(pdc_core *pdc, const char *text, int flags, const char *fn) +{ + char *buf = NULL; + + if (text != NULL) + { + size_t len = pdc_strlen(text) + 1; + + if (flags & PDC_CONV_TMPALLOC) + buf = (char *) pdc_malloc_tmp(pdc, len + 1, fn, NULL, NULL); + else + buf = (char *) pdc_malloc(pdc, len + 1, fn); + memcpy(buf, text, len); + buf[len] = 0; + + } + + return buf; +} + +char * +pdc_strdup(pdc_core *pdc, const char *text) +{ + char *buf = NULL; + static const char fn[] = "pdc_strdup"; + + if (text != NULL) + { + size_t len = pdc_strlen(text) + 1; + + buf = (char *) pdc_malloc(pdc, len + 1, fn); + memcpy(buf, text, len); + buf[len] = 0; + } + + return buf; +} + +char * +pdc_strdup2(pdc_core *pdc, const char *text, size_t len) +{ + char *buf = NULL; + static const char fn[] = "pdc_strdup2"; + + if (text != NULL) + { + buf = (char *) pdc_malloc(pdc, len + 1, fn); + memcpy(buf, text, len); + buf[len] = 0; + } + + return buf; +} + +char * +pdc_strdup_tmp(pdc_core *pdc, const char *text) +{ + char *buf = NULL; + static const char fn[] = "pdc_strdup_tmp"; + + if (text != NULL) + { + size_t len = pdc_strlen(text) + 1; + + buf = (char *) pdc_malloc_tmp(pdc, len + 1, fn, NULL, NULL); + memcpy(buf, text, len); + buf[len] = 0; + } + + return buf; +} + +/* Allocate a local buffer and copy a locale UTF-8 string + * provided with an UTF-8 BOM. + * The caller is responsible for freeing the buffer. + */ +char * +pdc_strdup_withbom(pdc_core *pdc, const char *text) +{ + char *buf = NULL; + static const char fn[] = "pdc_strdup_withbom"; + + if (text != NULL) + { + size_t len; + + if (pdc_is_utf8_bytecode(text)) + { + buf = pdc_strdup(pdc, text); + } + else + { + len = strlen(text); + buf = (char *) pdc_malloc(pdc, len + 4, fn); + + pdc_copy_utf8_bom(buf); + strcpy(&buf[3], text); + } + } + + return buf; +} + +char * +pdc_strdup_convert(pdc_core *pdc, pdc_encoding encto, pdc_encoding encfrom, + const char *text, int flags, const char *fn) +{ + pdc_encodingvector *evfrom, *evto; + char *buf; + size_t len; + int i; + + evto = pdc_get_encoding_vector(pdc, encto); + evfrom = pdc_get_encoding_vector(pdc, encfrom); + buf = pdc_strdup_ext(pdc, text, flags, fn); + len = strlen(buf); + + for (i = 0; i < (int) len; i++) + buf[i] = (char) pdc_transform_bytecode(pdc, evto, evfrom, + (pdc_byte) text[i]); + + return buf; +} + +pdc_bool +pdc_logg_isprint(int c) +{ + if (c < 0x20 || (c >= 0x7F && c < 0xA0)) + return pdc_false; + return pdc_true; +} + + +/* + * Put out an arbitrary string. + * + * strform = readable: Direct byte output with replacing not + * printable bytes by their octal codes. + * = readable0: Like readable, but byte 0 will be displayed as space. + * = octal: All bytes will be put out as octal. + * = hexa: All bytes will be put out as hexadecimal value. + * = java: Like readable, but Unicode strings and not printable + * bytes will be put out in Java notation \uxxxx, + * + * Output string is temporarily allocated. + * + */ +char * +pdc_strprint(pdc_core *pdc, const char *str, int leni, int maxchar, + pdc_strform_kind strform) +{ + static const char fn[] = "pdc_strprint"; + + if (str != NULL) + { + pdc_bool isunicode = pdc_false; + int len = leni; + + if (!leni) + len = (int) strlen(str); + + if (len) + { + pdc_strform_kind sf; + char *ts, *tmpstr; + pdc_byte c = ' ', cp = '.'; + pdc_ushort *ush = (pdc_ushort *) str; + int i, im; + + tmpstr = (char *) pdc_calloc_tmp(pdc, (size_t) (4 * (len + 4)), fn, + NULL, NULL); + ts = tmpstr; + + if (strform == strform_java) + { + if (leni && !(leni % 2)) + isunicode = pdc_true; + else + strform = strform_readable; + } + + if (maxchar <= 0) + maxchar = len; + im = (maxchar < len) ? maxchar : len; + if (isunicode) + im = im/2; + for (i = 0; i < im; i++) + { + if (isunicode) + { + if (ush[i] > PDC_UNICODE_MAXLATIN1) + { + sf = strform_java; + } + else + { + c = (pdc_byte) ush[i]; + sf = strform_readable; + } + } + else + { + c = (pdc_byte) str[i]; + sf = strform; + } + + switch (sf) + { + case strform_hexa: + ts += sprintf(ts, "\\x%02X", c); + break; + + case strform_octal: + ts += sprintf(ts, "\\%03o", c); + break; + + case strform_java: + ts += sprintf(ts, "\\u%04X", ush[i]); + break; + + default: + if (c == 0x0 && sf == strform_readable0) + c = 0x20; + + if (!pdc_logg_isprint((int) c)) + { + if (isunicode) + ts += sprintf(ts, "\\u%04X", c); + else + ts += sprintf(ts, "\\%03o", c); + } + else + { + if (c == '"') + { + *ts = '\\'; + ts++; + } + *ts = (char) c; + ts++; + } + } + } + + if (maxchar < len) + { + switch (strform) + { + case strform_hexa: + ts += sprintf(ts, "\\x%02X\\x%02X\\x%02X", cp, cp, cp); + break; + + case strform_octal: + ts += sprintf(ts, "\\%03o\\%03o\\%03o", cp, cp, cp); + break; + + case strform_java: + ts += sprintf(ts, "\\u%04X\\u%04X\\u%04X", cp, cp, cp); + break; + + default: + ts += sprintf(ts, "%c%c%c", cp, cp, cp); + break; + } + } + + return tmpstr; + } + } + + return (char *) pdc_calloc_tmp(pdc, 1, fn, NULL, NULL); +} + +const char * +pdc_utf8strprint(pdc_core *pdc, const char *str) +{ + int i = pdc_is_utf8_bytecode(str) ? 3 : 0; + return pdc_errprintf(pdc, "%.*s", PDC_ERR_MAXSTRLEN, &str[i]); +} + +/* + * Split a given text string into single strings which are separated by + * arbitrary characters. This characters must be specified in a string. + * If this string is NULL, " \f\n\r\t\v" (standard white spaces) is assumed. + * + * There is the convention that text inside braces {} will be taken verbatim. + * Inside brace expressions braces must exist only in pairs. Braces are + * masked by backslash. + * + * The caller is responsible for freeing the resultated string list + * by calling the function pdc_cleanup_stringlist. + * + * Not for unicode strings. + * + * Return value: Number of strings. + * If braces aren't balanced the number is negative. + * + */ +int +pdc_split_stringlist(pdc_core *pdc, const char *text, const char *i_separstr, + int flags, char ***stringlist) +{ + static const char fn[] = "pdc_split_stringlist"; + const char *separstr = " \f\n\r\t\v"; + const char *oldtext; + char **strlist = NULL, *newtext; + pdc_bool isoptlist = (flags & PDC_SPLIT_ISOPTLIST); + int it, len, jt = 0, jtb = 0, maxk = 0, count = 0, inside = 0; + int ns, nbs = 0, nbss; + + if (stringlist) + *stringlist = NULL; + if (i_separstr) + separstr = i_separstr; + + if (text == NULL) + return 0; + + /* check for empty string */ + ns = (int) strspn(text, separstr); + oldtext = &text[ns]; + len = (int) strlen(oldtext); + if (!len) return 0; + + /* check for UTF-8-BOM */ + if (pdc_is_utf8_bytecode(oldtext)) + { + oldtext = &text[ns + 3]; + len -= 3; + ns = (int) strspn(oldtext, separstr); + oldtext = &oldtext[ns]; + len -= ns; + if (!len) return 0; + } + + /* new string */ + newtext = (char *) pdc_malloc(pdc, (size_t) (len + 1), fn); + for (it = 0; it <= len; it++) + { + /* check for separators */ + if (it == len) + ns = 1; + else if (inside <= 0) + ns = (int) strspn(&oldtext[it], separstr); + else + ns = 0; + + /* close text part */ + if (ns) + { + newtext[jt] = 0; + if (count == maxk) + { + maxk += 16; + strlist = (strlist == NULL) ? + (char **) pdc_malloc(pdc, maxk * sizeof(char *), fn): + (char **) pdc_realloc(pdc, strlist, maxk * + sizeof(char *), fn); + } + strlist[count] = &newtext[jtb]; + count++; + + /* Exit */ + it += ns; + if (it >= len ) break; + + /* new text part */ + jt++; + jtb = jt; + } + + /* option list */ + if (isoptlist) + { + /* save backslash counter */ + nbss = nbs; + + /* backslash */ + if (oldtext[it] == '\\') + { + nbs++; + if (!(nbs % 2) && inside <= 1) + continue; + } + else + { + nbs = 0; + } + + /* open and close brace */ + if (oldtext[it] == '{') + { + if (!(nbss % 2)) + { + inside++; + if (inside == 1) + continue; + } + else if (inside <= 1) + { + jt--; + } + } + else if (oldtext[it] == '}') + { + if (!(nbss % 2)) + { + inside--; + if (inside == 0) + continue; + } + else if (inside <= 1) + { + jt--; + } + } + } + + /* save character */ + newtext[jt] = oldtext[it]; + jt++; + } + + if (stringlist) + *stringlist = strlist; + + return inside ? -count : count; +} + +void +pdc_cleanup_stringlist(pdc_core *pdc, char **stringlist) +{ + if(stringlist != NULL) + { + if(stringlist[0] != NULL) + pdc_free(pdc, stringlist[0]); + + pdc_free(pdc, stringlist); + } +} + + +/* + * Substitute a list of variables in a string by its values recursively. + * A variable begins with the character 'vchar' and ends at a character + * in 'delimiters' or at the end of string resp.. + * + * The character 'vchar' must be masked by 'vchar'. + * + * If at least one of a variable was substituted, a new allocated null + * terminated string is returned. Otherwise the original pointer. + * + * The caller is responsible for freeing the new string. + * + * string null terminated string with variables + * vchar begin character for a variable + * delimiters string with characters delimiting a variable name + * varslist list of variable names + * valslist list of variable values + * nvars number of variables + * errind[2] contains index and length of an unkown variable in string + * + */ + +static char * +substitute_variables(pdc_core *pdc, char *string, int ibeg, int *level, + const char **varslist, const char **valslist, int nvars, char vchar, + const char *separstr, int *errind) +{ + static const char fn[] = "substitue_variables"; + int i, j, l; + + j = ibeg; + for (i = ibeg; string[i] != 0; i++) + { + if (string[i] == vchar) + { + if (string[i + 1] == vchar) + i++; + else + break; + } + + string[j] = string[i]; + j++; + } + + if (string[i] != 0) + { + char *s = &string[i + 1]; + size_t n = strcspn(s, separstr); + + for (l = 0; l < nvars; l++) + { + if (n == strlen(varslist[l]) && !strncmp(s, varslist[l], n)) + { + char *newstring; + int k = (int) (i + n + 1); + size_t nv = strlen(valslist[l]); + size_t nr = strlen(&string[k]); + size_t nb = (size_t) j + nv + nr + 1; + + newstring = (char *) pdc_malloc(pdc, nb, fn); + strncpy(newstring, string, (size_t) j); + strncpy(&newstring[j], valslist[l], nv); + strcpy(&newstring[j + nv], &string[k]); + + pdc_free(pdc, string); + (*level)++; + + string = substitute_variables(pdc, newstring, j, level, + varslist, valslist, nvars, vchar, separstr, + errind); + break; + } + } + if (l == nvars) + { + errind[0] = i; + errind[1] = (int) (n + 1); + } + } + else + { + string[j] = 0; + } + return string; +} + +char * +pdc_substitute_variables(pdc_core *pdc, const char *string, char vchar, + const char *delimiters, const char **varslist, + const char **valslist, int nvars, int *errind) +{ + static const char fn[] = "pdc_substitue_variables"; + char *subststr, *newstring, separstr[64]; + int level = 0; + + newstring = pdc_strdup_ext(pdc, string, 0, fn); + + separstr[0] = vchar; + separstr[1] = 0; + strcat(separstr, delimiters); + + errind[0] = -1; + errind[1] = 0; + subststr = substitute_variables(pdc, newstring, 0, &level, + varslist, valslist, nvars, vchar, separstr, errind); + + return subststr; +} + +/* + * Compares its arguments and returns an integer less than, + * equal to, or greater than zero, depending on whether s1 + * is lexicographically less than, equal to, or greater than s2. + * Null pointer values for s1 and s2 are treated the same as pointers + * to empty strings. + * + * Presupposition: basic character set + * + * Return value: < 0 s1 < s2; + * = 0 s1 == s2; + * > 0 s1 > s2; + * + */ +int +pdc_strcmp(const char *s1, const char *s2) +{ + if (s1 == s2) return (0); + if (s1 == NULL) return (-1); + if (s2 == NULL) return (1); + + return strcmp(s1, s2); +} + +int +pdc_stricmp(const char *s1, const char *s2) +{ + if (s1 == s2) return (0); + if (s1 == NULL) return (-1); + if (s2 == NULL) return (1); + + for (; *s1; ++s1, ++s2) + { + if (pdc_tolower(*s1) != pdc_tolower(*s2)) + break; + } + + return (pdc_tolower(*s1) - pdc_tolower(*s2)); +} + + +/* + * Compares its arguments and returns an integer less than, + * equal to, or greater than zero, depending on whether s1 + * is lexicographically less than, equal to, or greater than s2. + * But only up to n characters compared (n less than or equal + * to zero yields equality).Null pointer values for s1 and s2 + * are treated the same as pointers to empty strings. + * + * Presupposition: basic character set + * + * Return value: < 0 s1 < s2; + * = 0 s1 == s2; + * > 0 s1 > s2; + * + */ +int +pdc_strincmp(const char *s1, const char *s2, int n) +{ + int i; + + if (s1 == s2) return (0); + if (s1 == NULL) return (-1); + if (s2 == NULL) return (1); + + for (i = 0; i < n && *s1 && *s2; ++i, ++s1, ++s2) + { + if (pdc_tolower(*s1) != pdc_tolower(*s2)) + break; + } + + return (i == n) ? 0 : (pdc_tolower(*s1) - pdc_tolower(*s2)); +} + +/* + * pdc_strtrim removes trailing white space characters from an input string. + * pdc_str2trim removes leading and trailing white space characters from an + * input string.. + */ +char * +pdc_strtrim(char *str) +{ + int i, n; + + n = (int) strlen(str); + for (i = n - 1; i >= 0; i--) + if (!pdc_isspace(str[i])) break; + str[i + 1] = '\0'; + + return str; +} + +char * +pdc_str2trim(char *str) +{ + int i, n; + + n = (int) strlen(str); + for (i = n - 1; i >= 0; i--) + if (!pdc_isspace(str[i])) break; + str[i + 1] = '\0'; + + for (i = 0; ; i++) + if (!pdc_isspace(str[i])) break; + if (i > 0) + memmove(str, &str[i], strlen(&str[i]) + 1); + + return str; +} + +char * +pdc_strtoupper(char *str) +{ + int i, n; + + n = (int) strlen(str); + for (i = 0; i < n; i++) + str[i] = (char) pdc_toupper(str[i]); + + return str; +} + +char * +pdc_strtolower(char *str) +{ + int i, n; + + n = (int) strlen(str); + for (i = 0; i < n; i++) + str[i] = (char) pdc_tolower(str[i]); + + return str; +} + +int +pdc_tolower_ascii(int c) +{ + c = (int) pdc_tolower(c); + + return c; +} + +int +pdc_toupper_ascii(int c) +{ + c = (int) pdc_toupper((int) c); + + return c; +} + +void +pdc_swap_bytes(char *instring, int inlen, char *outstring) +{ + char c; + int i,j; + + if (instring == NULL) + return; + + if (outstring == NULL) + outstring = instring; + + inlen = 2 * inlen / 2; + for (i = 0; i < inlen; i++) + { + j = i; + i++; + c = instring[j]; + outstring[j] = instring[i]; + outstring[i] = c; + } +} + +void +pdc_swap_unicodes(char *instring) +{ + if (instring && + ((pdc_is_utf16be_unicode(instring) && !PDC_ISBIGENDIAN) || + (pdc_is_utf16le_unicode(instring) && PDC_ISBIGENDIAN))) + pdc_swap_bytes(&instring[2], (int) (wstrlen(instring) - 2), NULL); +} + +void +pdc_inflate_ascii(const char *instring, int inlen, char *outstring, + pdc_text_format textformat) +{ + int i, j; + pdc_bool is_bigendian = (textformat == pdc_utf16be) || + (textformat == pdc_utf16 && PDC_ISBIGENDIAN); + + j = 0; + for (i = 0; i < inlen; i++) + { + if (is_bigendian) + { + outstring[j] = 0; + j++; + outstring[j] = instring[i]; + } + else + { + outstring[j] = instring[i]; + j++; + outstring[j] = 0; + } + j++; + } +} + +/* + * pdc_stresc -- + * Remove from a string containing escaped non-printable cha- + * racters. The string must follows the C-standard escape + * mechanism: an escaped character is preceeded by an escape + * character which is a backslash '\' character and followed + * by one or more characters to define the non-printable + * character to be inserted here. The supported escapes are + * + * \a bell (ASCII/EBCDIC-BEL) + * \b backspace (ASCII/EBCDIC-BS) + * \e escape charater (ASCII/EBCDIC-ESC) + * \f formfeed (ASCII/EBCDIC-FF) + * \n linefeed (ASCII/EBCDIC-LF) + * \r return (ASCII/EBCDIC-CR) + * \t tab character (ASCII/EBCDIC-TAB) + * \v vertical tab (ASCII/EBCDIC-VT) + * \\ the slash itself + * \xnn two hex digits n to define a + * character numerically as ASCII/EBCDIC value. + * \nnn three octal digits n to define a + * character numerically as ASCII/EBCDIC value. + * + * For example: \x0A, \x0a or \012 has the same effect in ASCII + * as \n (i.e linefeed). + * Note, if the last character in a string is the backslash + * then the backslash is illegal. + * The special characters a,b,e,f, and so on are recognized in + * lower case only. + * + * textformat: + * pdc_bytes: Latin1 or EBCDIC bytes on EBCDIC platforms + * pdc_utf8: Latin1 + * pdc_ebcdicutf8: EBCDIC - only on EBCDIC platforms + * pdc_utf16: 2 bytes Latin1 + * + * If a illegal escaped sequence was detected an exception will + * be thrown (verbose == pdc_true) or the sequence will be taken + * as it (verbose == pdc_false). + * +*/ + +static const pdc_keyconn pdc_ascii_escape_keylist[] = +{ + {"\\", 0x5C}, + {"a", 0x07}, + {"b", 0x08}, + {"e", 0x1B}, + {"f", 0x0C}, + {"n", 0x0A}, + {"r", 0x0D}, + {"t", 0x09}, + {"v", 0x0B}, + {"x", 0x78}, + {NULL, 0} +}; + +pdc_ushort +pdc_get_string_value(pdc_byte *str, int i, int charlen) +{ + pdc_ushort retval = 0; + + if (charlen == 1) + { + retval = (pdc_ushort) str[i]; + } + else + { + pdc_ushort *ustr = (pdc_ushort *) str; + + retval = ustr[i]; + } + + return retval; +} + +int +pdc_subst_backslash(pdc_core *pdc, pdc_byte *str, int len, + pdc_encodingvector *ev, pdc_text_format textformat, + pdc_bool verbose) +{ + pdc_ushort *ustr = (pdc_ushort *) str; + int charlen = (textformat == pdc_utf16) ? 2 : 1; + pdc_byte bschar = '\\'; + pdc_ushort uv; + int i, j, k, code; + + if (ev != NULL) + { + code = pdc_get_encoding_bytecode(pdc, ev, PDC_UNICODE_BACKSLASH); + if (code != -1) + bschar = (pdc_byte) code; + } + + + j = 0; + len /= charlen; + for (i = 0; i < len; i++) + { + uv = pdc_get_string_value(str, i, charlen); + if (uv > PDC_UNICODE_MAXLATIN1) + { + ustr[j] = uv; + j++; + continue; + } + + /* backslash found */ + if (uv == bschar) + { + pdc_byte escseq[4], stemp[6]; + pdc_bool kerror = pdc_false; + + i++; + if (i < len) + { + uv = pdc_get_string_value(str, i, charlen); + if (uv > PDC_UNICODE_MAXLATIN1) + goto PDC_OVERFLOW_EXIT; + + escseq[0] = (pdc_byte) uv; + escseq[1] = 0; + + code = pdc_get_keycode((char *) escseq, + pdc_ascii_escape_keylist); + if (code != PDC_KEY_NOTFOUND) + { + /* hex number */ + if (code == 0x78) + { + for (k = 0; k < 2; k++) + { + i++; + if (i < len) + { + uv = pdc_get_string_value(str, i, charlen); + if (uv > PDC_UNICODE_MAXLATIN1) + goto PDC_OVERFLOW_EXIT; + } + else + { + uv = 0; + } + escseq[k] = (pdc_byte) uv; + } + escseq[k] = 0; + if (i >= len || + !pdc_str2integer((char *) escseq, PDC_INT_UNICODE, + &uv)) + { + strcpy((char *) stemp, "\\x"); + strcat((char *) stemp, (char *) escseq); + kerror = pdc_true; + } + } + else + { + pdc_char c = (pdc_char) code; + uv = (pdc_ushort) (pdc_byte) c; + } + } + else + { + /* octal number */ + for (k = 0; k < 3; k++) + { + if (k) i++; + if (i < len) + { + uv = pdc_get_string_value(str, i, charlen); + if (uv > PDC_UNICODE_MAXLATIN1) + goto PDC_OVERFLOW_EXIT; + } + else + { + uv = 0; + } + escseq[k] = (pdc_byte) uv; + } + escseq[k] = 0; + if (i >= len || + !pdc_str2integer((char *) escseq, + PDC_INT_SHORT | + PDC_INT_UNSIGNED | + PDC_INT_OCTAL, + &uv) || + (charlen == 1 && uv > 0xFF)) + { + strcpy((char *) stemp, "\\"); + strcat((char *) stemp, (char *) escseq); + kerror = pdc_true; + } + } + } + else + { + strcpy((char *) stemp, "\\"); + kerror = pdc_true; + } + + /* error message */ + if (kerror) + { + pdc_set_errmsg(pdc, PDC_E_STR_ILL_ESCSEQ, (char *) stemp, + 0, 0, 0); + + if (verbose) + pdc_error(pdc, -1, 0, 0, 0, 0); + + return 0; + } + } + + if (charlen == 1) + str[j] = (pdc_byte) uv; + else + ustr[j] = uv; + + j++; + } + + if (charlen == 1) + str[j] = 0; + else + ustr[j] = 0; + + return charlen * j; + + PDC_OVERFLOW_EXIT: + + pdc_set_errmsg(pdc, PDC_E_STR_ILL_UNIESCSEQ, + pdc_errprintf(pdc, "%04X", uv), 0, 0, 0); + + if (verbose) + pdc_error(pdc, -1, 0, 0, 0, 0); + + return 0; +} + + +/* ----------------------- number converting ----------------------- */ + +/* + * pdc_str2double converts a null terminated and trimmed string + * to a double precision number + */ +pdc_bool +pdc_str2double(const char *string, double *o_dz) +{ + const char *s = string; + double dz = 0; + int is = 1, isd = 0; + + *o_dz = 0; + + /* sign */ + if (*s == '-') + { + is = -1; + s++; + } + else if (*s == '+') + s++; + + if (!*s) + return pdc_false; + + /* places before decimal point */ + isd = pdc_isdigit(*s); + if (isd) + { + do + { + dz = 10 * dz + *s - '0'; + s++; + } + while (pdc_isdigit(*s)); + } + + /* decimal point */ + if (*s == '.' || *s == ',') + { + const char *sa; + double adz = 0; + + s++; + isd = pdc_isdigit(*s); + if (!isd) + return pdc_false; + + /* places after decimal point */ + sa = s; + do + { + adz = 10 * adz + *s - '0'; + s++; + } + while (pdc_isdigit(*s)); + dz += adz / pow(10.0, (double)(s - sa)); + } + + /* power sign */ + if (*s == 'e' || *s == 'E') + { + s++; + if (!isd) + return pdc_false; + + /* sign */ + if (!*s) + { + dz *= 10; + } + else + { + int isp = 1; + double pdz = 0, pdl = log10(dz); + + if (*s == '-') + { + isp = -1; + s++; + } + else if (*s == '+') + s++; + + if (!pdc_isdigit(*s)) + return pdc_false; + do + { + pdz = 10 * pdz + *s - '0'; + s++; + } + while (pdc_isdigit(*s)); + + + if (*s || fabs(pdl + pdz) > 300.0) + return pdc_false; + + dz *= pow(10.0, isp * pdz); + } + } + else if(*s) + { + return pdc_false; + } + + *o_dz = is * dz; + return pdc_true; +} + +/* + * pdc_str2integer converts a null terminated and trimmed string + * to an hexadecimal or decimal integer number of arbitrary size + */ +pdc_bool +pdc_str2integer(const char *string, int flags, void *o_iz) +{ + const char *s = string; + double dz = 0; + pdc_char cz = 0; + pdc_short sz = 0; + pdc_sint32 lz = 0; + pdc_byte ucz = 0; + pdc_ushort usz = 0; + pdc_uint32 ulz = 0; + int is = 1, lzd; + + if (flags & PDC_INT_CHAR) + memcpy(o_iz, &cz, sizeof(pdc_char)); + else if (flags & PDC_INT_SHORT) + memcpy(o_iz, &sz, sizeof(pdc_short)); + else + memcpy(o_iz, &lz, sizeof(pdc_sint32)); + + /* sign */ + if (*s == '-') + { + if (flags & PDC_INT_UNSIGNED) + return pdc_false; + is = -1; + s++; + } + else if (*s == '+') + s++; + + if (!*s) + return pdc_false; + + /* hexadecimal test */ + if (!(flags & PDC_INT_DEC)) + { + const char *ss = s; + + if (*s == '<') + s += 1; + else if (*s == 'x' || *s == 'X') + s += 1; + else if (!strncmp(s, "0x", 2) || !strncmp(s, "0X", 2)) + s += 2; + if (s > ss) + { + if (!*s) + return pdc_false; + flags |= PDC_INT_HEXADEC; + } + } + + /* hexadecimal */ + if (flags & PDC_INT_HEXADEC) + { + while (pdc_isxdigit(*s)) + { + if (pdc_isalpha(*s)) + lzd = (pdc_isupper(*s) ? 'A' : 'a') - 10; + else + lzd = '0'; + dz = 16 * dz + *s - lzd; + s++; + } + if (*string == '<') + { + if (*s == '>') + s += 1; + else + return pdc_false; + } + } + + /* octal */ + if (flags & PDC_INT_OCTAL) + { + while (pdc_isdigit(*s) && *s < '8') + { + dz = 8 * dz + *s - '0'; + s++; + } + } + + /* decimal */ + else + { + while (pdc_isdigit(*s)) + { + dz = 10 * dz + *s - '0'; + s++; + } + } + if (*s) + return pdc_false; + + dz *= is; + if (flags & PDC_INT_CHAR) + { + if (flags & PDC_INT_UNSIGNED) + { + if (dz > PDC_UCHAR_MAX) + return pdc_false; + ucz = (pdc_byte) dz; + memcpy(o_iz, &ucz, sizeof(pdc_byte)); + } + else + { + if (dz < PDC_SCHAR_MIN || dz > PDC_SCHAR_MAX) + return pdc_false; + cz = (pdc_char) dz; + memcpy(o_iz, &cz, sizeof(pdc_char)); + } + } + else if (flags & PDC_INT_SHORT) + { + if (flags & PDC_INT_UNSIGNED) + { + if (dz > PDC_USHRT_MAX) + return pdc_false; + usz = (pdc_ushort) dz; + memcpy(o_iz, &usz, sizeof(pdc_ushort)); + } + else + { + if (dz < PDC_SHRT_MIN || dz > PDC_SHRT_MAX) + return pdc_false; + sz = (pdc_short) dz; + memcpy(o_iz, &sz, sizeof(pdc_short)); + } + } + else + { + if (flags & PDC_INT_UNSIGNED) + { + if (dz > PDC_UINT_MAX) + return pdc_false; + ulz = (pdc_uint32) dz; + memcpy(o_iz, &ulz, sizeof(pdc_uint32)); + } + else + { + if (dz < PDC_INT_MIN || dz > PDC_INT_MAX) + return pdc_false; + lz = (pdc_sint32) dz; + memcpy(o_iz, &lz, sizeof(pdc_sint32)); + } + } + + return pdc_true; +} + +static const char digits[] = "0123456789ABCDEF"; + +static char * +pdc_ltoa(char *buf, long n, int width, char pad, int base) +{ + char aux[100]; + int k, i = sizeof aux; + char * dest = buf; + pdc_bool sign; + + if (n == 0) + { + if (width == 0) + width = 1; + + for (k = 0; k < width; ++k) + *(dest++) = '0'; + + return dest; + } + + if (n < 0 && base == 10) + { + --width; + sign = pdc_true; + aux[--i] = digits[- (n % base)]; + n = n / -base; + } + else + { + sign = pdc_false; + aux[--i] = digits[n % base]; + n = n / base; + } + + while (0 < n) + { + aux[--i] = digits[n % base]; + n = n / base; + } + + width -= (int) (sizeof aux) - i; + for (k = 0; k < width; ++k) + *(dest++) = pad; + + if (sign) + *(dest++) = '-'; + + memcpy(dest, &aux[i], sizeof aux - i); + return dest + sizeof aux - i; +} /* pdc_ltoa */ + + +static char * +pdc_off_t2a(char *buf, pdc_off_t n, int width, char pad, int base) +{ + char aux[100]; + int k, i = sizeof aux; + char * dest = buf; + pdc_bool sign; + + if (n < 0 && base == 10) + { + --width; + sign = pdc_true; + aux[--i] = digits[- (n % base)]; + n = n / -base; + } + else + { + sign = pdc_false; + aux[--i] = digits[n % base]; + n = n / base; + } + + while (0 < n) + { + aux[--i] = digits[n % base]; + n = n / base; + } + + width -= (int) (sizeof aux) - i; + for (k = 0; k < width; ++k) + *(dest++) = pad; + + if (sign) + *(dest++) = '-'; + + memcpy(dest, &aux[i], sizeof aux - i); + return dest + sizeof aux - i; +} /* pdc_off_t2a */ + + +/* + * pdc_ftoa converts a floating point number to string + * + * Because of historical reason "%f" = "%.12g". + * + * The function calls sprintf() and replaces + * decimal comma by decimal point. + * + * If the number is infinite or not a number + * "nan" will be set. + * + */ + +static char * +pdc_ftoa(pdc_core *pdc, const char *format, char *buf, double x) +{ + char *dest = buf; + char *cd; + int n; + + (void) pdc; + + /* check whether the number is valid */ + if (!PDC_ISFINITE(x)) + { + strcpy(dest, "nan"); + return dest + 3; + } + + /* standard C convert */ + if (!strcmp(format, "%f")) + n = sprintf(dest, "%.12g", x); + else + n = sprintf(dest, format, x); + + /* normalized to decimal point */ + cd = strchr(dest, ','); + if (cd != NULL) + *cd = '.'; + + return dest + n; +} /* pdc_ftoa */ + +/* + * pdc_ftoa_pdfconf converts a floating point number to string + * PDF conforming + * + */ + +static char * +pdc_ftoa_pdfconf(pdc_core *pdc, char *buf, double x) +{ + static const long pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000 }; + char * dest = buf; + double integ, fract, powd; + int ifd; + long f; + + /* check whether the number is valid */ + if (!PDC_ISFINITE(x)) + pdc_error(pdc, PDC_E_INT_ILLFLOAT, 0, 0, 0, 0); + + /* small number will be mapped to 0 */ + if (x < PDF_SMALLREAL && x > -PDF_SMALLREAL) + { + *dest = '0'; + return dest + 1; + } + + /* negative number */ + if (x < 0) + { + x = -x; + *(dest++) = '-'; + } + + /* large number is invalid or will be mapped to integer */ + if (x >= PDF_BIGREAL) + { + if (x > PDF_BIGINT) + pdc_error(pdc, PDC_E_INT_FLOATTOOLARGE, + pdc_errprintf(pdc, "%f", x), 0, 0, 0); + + return pdc_ltoa(dest, (long) (x + 0.5), 0, ' ', 10); + } + + ifd = pdc->floatdigits; + powd = pow10[ifd]; + + fract = modf(x, &integ); + f = (long) (fract * powd + 0.5); + + if (f == powd) + { + integ += 1.0; + f = 0; + } + + if (integ == 0 && f == 0) /* avoid "-0" */ + dest = buf; + + dest = pdc_ltoa(dest, (long) integ, 0, ' ', 10); + + if (f != 0) + { + char * aux; + long rem; + + *(dest++) = '.'; + + do /* avoid trailing zeros */ + { + rem = f % 10; + f = f / 10; + --ifd; + } while (rem == 0); + + aux = dest + ifd + 1; + dest[ifd--] = digits[rem]; + + for (; 0 <= ifd; --ifd) + { + dest[ifd] = digits[f % 10]; + f = f / 10; + } + + return aux; + } + + return dest; +} /* pdc_ftoa_pdfconf */ + +static int +pdc_vxprintf( + pdc_core *pdc, + pdc_bool pdfconf, + char *cp, + FILE *fp, + const char *format, + va_list args) +{ + static const char fn[] = "pdc_vxprintf"; + const char *format_p; + char aux[1024]; + char *buf = cp ? cp : aux; + char *dest = buf; + + for (/* */ ; /* */ ; /* */) + { + int width = 0; + int prec = 0; + char pad = ' '; + pdc_bool left_justify = pdc_false; + + /* as long as there is no '%', just print. + */ + while (*format != 0 && *format != '%') + *(dest++) = *(format++); + + if (*format == 0) + { + if (fp != (FILE *) 0) + { + if (dest > buf) + pdc_fwrite_ascii(pdc, buf, (size_t) (dest - buf), fp); + } + else + *dest = 0; + + return (int) (dest - buf); + } + format_p = format; + + /* get the "flags", if any. + */ + if (*(++format) == '-') + { + left_justify = pdc_true; + ++format; + } + + if (*format == '0') + { + if (!left_justify) + pad = '0'; + + ++format; + } + + /* get the "width", if present. + */ + if (*format == '*') + { + width = va_arg(args, int); /* TODO: sign? */ + ++format; + } + else + { + while (pdc_isdigit(*format)) + width = 10 * width + *(format++) - '0'; + } + + /* get the "precision", if present. + */ + if (*format == '.') + { + ++format; + + if (*format == '*') + { + prec = va_arg(args, int); /* TODO: sign? */ + ++format; + } + else + { + while (pdc_isdigit(*format)) + prec = 10 * prec + *(format++) - '0'; + } + } + + switch (*format) + { + case 'x': + case 'X': + dest = pdc_off_t2a( + dest, (pdc_off_t) va_arg(args, unsigned int), + width, pad, 16); + break; + + case 'c': + *(dest++) = (char) va_arg(args, int); + break; + + case 'd': + dest = pdc_off_t2a(dest, (pdc_off_t) va_arg(args, int), + width, pad, 10); + break; + + case 'g': + case 'f': + if (pdfconf) + { + dest = pdc_ftoa_pdfconf(pdc, dest, va_arg(args, double)); + } + else + { + char ff[32]; + size_t n = (size_t) (format - format_p + 1); + + strncpy(ff, format_p, n); + ff[n] = 0; + dest = pdc_ftoa(pdc, ff, dest, va_arg(args, double)); + } + break; + + case 'l': + { + pdc_off_t n; + + if (format[1] == 'l') + { + n = va_arg(args, pdc_off_t); + ++format; + } + else + { + n = va_arg(args, long); + } + + switch (*(++format)) + { + case 'x': + case 'X': + dest = pdc_off_t2a(dest, n, width, pad, 16); + break; + + case 'd': + dest = pdc_off_t2a(dest, n, width, pad, 10); + break; + + default: + pdc_error(pdc, PDC_E_INT_BADFORMAT, + pdc_errprintf(pdc, "l%c", + pdc_isprint((int) *format) ? *format : '?'), + pdc_errprintf(pdc, "0x%02X", *format), + 0, 0); + } + + break; + } + + case 'p': + { + void *ptr = va_arg(args, void *); + dest += sprintf(dest, "%p", ptr); + break; + } + + case 'a': + case 's': + case 'T': + { + char *str = va_arg(args, char *); + const char *cstr = str; + pdc_bool tobefree = pdc_false; + size_t len; + + if (str == 0) + cstr = "(NULL)"; + len = strlen(cstr); + + if (*format == 'T') + { + int l = va_arg(args, int); + + if (str != 0) + { + cstr = pdc_print_loggstring(pdc, str, l); + len = strlen(cstr); + } + } + + if (*format == 'a' && str != 0) + { + cstr = pdc_strdup_ext(pdc, str, PDC_CONV_EBCDIC, fn); + tobefree = pdc_true; + } + + if (!left_justify && len < (size_t) width) + { + memset(dest, pad, width - len); + dest += width - len; + } + + if (len != 0) + { + if (fp != (FILE *) 0) + { + if (dest > buf) + { + pdc_fwrite_ascii(pdc, buf, + (size_t) (dest - buf), fp); + dest = buf; + } + + pdc_fwrite_ascii(pdc, cstr, len, fp); + } + else + { + memcpy(dest, cstr, len); + dest += len; + } + + if (tobefree) + pdc_free(pdc, (char *) cstr); + } + + if (left_justify && len < (size_t) width) + { + memset(dest, pad, width - len); + dest += width - len; + } + + break; + } + + case '%': + *(dest++) = '%'; + break; + + default: + pdc_error(pdc, PDC_E_INT_BADFORMAT, + pdc_errprintf(pdc, "%c", pdc_isprint((int) *format) ? + *format : '?'), + pdc_errprintf(pdc, "0x%02X", *format), + 0, 0); + } /* switch */ + + ++format; + } /* loop */ +} /* pdc_vxprintf */ + + +/* ----------------------- formatted output ----------------------- */ + +/* + * formatted output to file + */ +int +pdc_vfprintf(pdc_core *pdc, pdc_bool pdfconf, FILE *fp, + const char *format, va_list args) +{ + return pdc_vxprintf(pdc, pdfconf, NULL, fp, format, args); +} /* pdc_vfprintf */ + +int +pdc_fprintf(pdc_core *pdc, pdc_bool pdfconf, FILE *fp, + const char *format, ...) +{ + int result; + va_list ap; + + va_start(ap, format); + result = pdc_vxprintf(pdc, pdfconf, NULL, fp, format, ap); + va_end(ap); + + return result; +} /* pdc_fprintf */ + + +/* + * formatted output to character string + */ +int +pdc_vsprintf(pdc_core *pdc, pdc_bool pdfconf, char *buf, + const char *format, va_list args) +{ + return pdc_vxprintf(pdc, pdfconf, buf, NULL, format, args); +} /* pdc_vsprintf */ + +int +pdc_sprintf(pdc_core *pdc, pdc_bool pdfconf, char *buf, + const char *format, ...) +{ + int result; + va_list ap; + + va_start(ap, format); + result = pdc_vxprintf(pdc, pdfconf, buf, NULL, format, ap); + va_end(ap); + + return result; +} /* pdc_sprintf */ + +/* + * we cannot use own converter because of missing format + * specifications like %lu + */ +int +pdc_vsnprintf(char *buf, size_t size, const char *format, va_list args) +{ + int result; + +#if defined (PDC_NO_VSNPRINTF) + (void) size; + result = vsprintf(buf, format, args); +#else +#if defined(WIN32) + result = _vsnprintf(buf, size, format, args); +#else + result = vsnprintf(buf, size, format, args); +#endif +#endif + + return result; +} /* pdc_vsnprintf */ + + +/* --------------------- name tree handling ----------------------- */ + +struct pdc_branch_s +{ + char *name; /* name - must be allocated pointer */ + void *data; /* private data - must be allocated pointer */ + int nalloc; /* number of allocated kid structs */ + int nkids; /* number of kids */ + pdc_branch **kids; /* kids */ + pdc_branch *parent; /* parent branch */ +}; + +pdc_branch * +pdc_init_tree(pdc_core *pdc) +{ + return pdc_create_treebranch(pdc, NULL, "__tree__root__", + NULL, 0, 0, NULL, NULL); +} + +pdc_branch * +pdc_create_treebranch(pdc_core *pdc, pdc_branch *root, const char *pathname, + void *data, int flags, int size, + pdc_branch_error *errcode, const char **name_p) +{ + static const char fn[] = "pdc_create_branch"; + char *name = NULL; + pdc_branch *branch = NULL; + pdc_branch *kid = NULL; + pdc_branch *parent = NULL; + char **namelist; + int i, j, k, nnames, nkids; + + if (errcode) *errcode = tree_ok; + if (name_p) *name_p = ""; + + if (root) + { + /* search for parent branch */ + parent = root; + nnames = pdc_split_stringlist(pdc, pathname, PDC_NAME_SEPARSTRG, 0, + &namelist); + for (i = 0; i < nnames; i++) + { + /* parent branch must not be a leaf branch */ + if (!parent->nalloc) + { + if (errcode) *errcode = tree_isleaf; + pdc_cleanup_stringlist(pdc, namelist); + return NULL; + } + if (i == nnames - 1) + break; + + name = namelist[i]; + if (name_p) + *name_p = pdc_errprintf(pdc, "%.*s", PDC_ERR_MAXSTRLEN, name); + + nkids = parent->nkids; + for (j = 0; j < nkids; j++) + { + kid = parent->kids[j]; + k = pdc_is_utf8_bytecode(kid->name) ? 3 : 0; + if (!strcmp(&kid->name[k], name)) + { + parent = kid; + break; + } + } + if (j == nkids) + { + if (errcode) *errcode = tree_notfound; + pdc_cleanup_stringlist(pdc, namelist); + return NULL; + } + } + + if (pdc_is_utf8_bytecode(pathname)) + name = pdc_strdup_withbom(pdc, namelist[nnames - 1]); + else + name = pdc_strdup(pdc, namelist[nnames - 1]); + pdc_cleanup_stringlist(pdc, namelist); + + /* kids must have different names */ + for (j = 0; j < parent->nkids; j++) + { + kid = parent->kids[j]; + if (!strcmp(kid->name, name)) + { + if (errcode) *errcode = tree_nameexists; + if (name_p) *name_p = + pdc_errprintf(pdc, "%.*s", PDC_ERR_MAXSTRLEN, name); + pdc_free(pdc, name); + return NULL; + } + } + } + else + { + parent = NULL; + name = pdc_strdup(pdc, pathname); + } + + branch = (pdc_branch *) pdc_malloc(pdc, sizeof(pdc_branch), fn); + branch->name = name; + branch->data = data; + if (flags & PDC_TREE_ISLEAF) + { + branch->nalloc = 0; + branch->nkids = 0; + branch->kids = NULL; + } + else + { + branch->nalloc = PDC_KIDS_CHUNKSIZE; + branch->nkids = 0; + branch->kids = (pdc_branch **) pdc_malloc(pdc, + branch->nalloc * sizeof(pdc_branch *), fn); + } + branch->parent = parent; + + /* insert kid */ + if (parent) + { + if (parent->nkids == parent->nalloc) + { + parent->nalloc *= 2; + parent->kids = (pdc_branch **) pdc_realloc(pdc, parent->kids, + parent->nalloc * sizeof(pdc_branch *), fn); + } + parent->kids[parent->nkids] = branch; + (parent->nkids)++; + + if ((flags & PDC_TREE_INHERIT) && parent->data) + memcpy(branch->data, parent->data, (size_t) size); + } + + return branch; +} + +void +pdc_deactivate_name_treebranch(pdc_core *pdc, pdc_branch *branch) +{ + static const char fn[] = "pdc_deactivate_name_treebranch"; + size_t len = strlen(branch->name); + + branch->name = (char *) pdc_realloc(pdc, branch->name, len + 2, fn); + branch->name[len] = PDC_NAME_SEPARSIGN; + branch->name[len+1] = 0; +} + +char * +pdc_get_name_treebranch(pdc_branch *branch) +{ + return branch->name; +} + +pdc_branch * +pdc_get_parent_treebranch(pdc_branch *branch) +{ + return branch->parent; +} + +void * +pdc_get_data_treebranch(pdc_branch *branch) +{ + return branch->data; +} + +pdc_branch ** +pdc_get_kids_treebranch(pdc_branch *branch, int *nkids) +{ + *nkids = branch->nkids; + return branch->kids; +} + +void +pdc_cleanup_treebranch(pdc_core *pdc, pdc_branch *branch) +{ + int i; + + if (branch->name) + pdc_free(pdc, branch->name); + + if (branch->data) + pdc_free(pdc, branch->data); + + if (branch->kids) + { + for(i = 0; i < branch->nkids; i++) + pdc_cleanup_treebranch(pdc, branch->kids[i]); + pdc_free(pdc, branch->kids); + } + + pdc_free(pdc, branch); +} + +/***************************** memory pools *****************************/ + +/* the data structures and functions in this section are more than +** confusing. the funny "mp_item" structure below makes them more +** readable, believe it or not. +*/ +typedef struct mp_item_s mp_item; + +struct mp_item_s +{ + mp_item * next; +}; + +struct pdc_mempool_s +{ + pdc_core * pdc; + + char ** pool_tab; + mp_item * free_list; + + size_t pool_incr; /* pool growth chunk size (items) */ + + size_t ptab_cap; /* total # of slots in pool_tab */ + size_t ptab_size; /* used # of slots in pool_tab */ + size_t ptab_incr; /* pool_tab growth chunk size (slots) */ + + size_t item_size; /* size of a single item (bytes) */ +}; + +#undef COMMENT +#ifdef COMMENT + + pool_incr = 5 + ptab_incr = 4 + ptab_cap = 4 (1 * ptab_incr) + + + +------+ + | free | + +------+ +----------------------------------+ + | free | +--> | | | | free | free | + +------+ | +----------------------------------+ + | | ---+ + +------+ +----------------------------------+ + | | ------> | | | | | | + +------+ +----------------------------------+ + + pool_tab + +#endif /* COMMENT */ + + +pdc_mempool * +pdc_mp_new(pdc_core *pdc, size_t item_size) +{ + static const char fn[] = "pdc_mp_new"; + + int m; + pdc_mempool *mp = (pdc_mempool *) + pdc_malloc(pdc, sizeof (pdc_mempool), fn); + + /* round up 'item_size' to a multiple of 'sizeof (mp_item)' + ** to ensure proper alignment. + */ + if ((m = (int) (item_size % sizeof (mp_item))) != 0) + item_size += sizeof (mp_item) - m; + + mp->pdc = pdc; + + mp->pool_tab = (char **) 0; + mp->free_list = (mp_item *) 0; + mp->pool_incr = 1000; + + mp->ptab_cap = 0; + mp->ptab_size = 0; + mp->ptab_incr = 100; + + mp->item_size = item_size; + + return mp; +} /* pdc_mp_new */ + + +void +pdc_mp_delete(pdc_mempool *mp) +{ + /* TODO: exception if there are still alloc'd items in the pool? */ + /* or, the other way round, call destructors? */ + + pdc_core * pdc = mp->pdc; + int i; + + for (i = 0; i < (int) mp->ptab_size; ++i) + pdc_free(pdc, mp->pool_tab[i]); + + if (mp->pool_tab) + pdc_free(pdc, mp->pool_tab); + + pdc_free(pdc, mp); +} /* pdc_mp_delete */ + + +void * +pdc_mp_alloc(pdc_mempool *mp) +{ + static const char fn[] = "pdc_mp_alloc"; + + pdc_core * pdc = mp->pdc; + mp_item * result; + + if (!mp->free_list) + { + char * new_chunk; + int i; + + if (mp->ptab_size == mp->ptab_cap) + { + mp->ptab_cap += mp->ptab_incr; + + mp->pool_tab = (char **) pdc_realloc(pdc, + mp->pool_tab, mp->ptab_cap * sizeof (char **), fn); + } + + new_chunk = mp->pool_tab[mp->ptab_size] = (char *) + pdc_malloc(pdc, mp->pool_incr * mp->item_size, fn); + + ++mp->ptab_size; + mp->free_list = (mp_item *) new_chunk; + mp->free_list->next = (mp_item *) 0; + + for (i = 1; i < (int) mp->pool_incr; ++i) + { + mp_item *scan = (mp_item *) (new_chunk + i * mp->item_size); + + scan->next = mp->free_list; + mp->free_list = scan; + } + } + + result = mp->free_list; + mp->free_list = result->next; + + return (void *) result; +} /* pdc_mp_alloc */ + + +void +pdc_mp_free(pdc_mempool *mp, void *item) +{ + mp_item *mpi = (mp_item *) item; + + mpi->next = mp->free_list; + mp->free_list = mpi; +} /* pdc_mp_free */ + + +/***************************** miscellaneous ****************************/ + +/* search a sorted (strcmp order) array "names" of size "size" +** for string "name". return the index if found, otherwise -1. +*/ +int +pdc_name2idx(const char **names, int size, const char *name) +{ + int lo = 0, hi = size; + + while (lo != hi) + { + int idx = (lo + hi) / 2; + int cmp = strcmp(name, names[idx]); + + if (cmp == 0) + return idx; + + if (cmp < 0) + hi = idx; + else + lo = idx + 1; + } + + return -1; +} /* pdc_name2idx */ + + +/* linear search; see man page LSEARCH(3). +*/ +void * +pdc_lfind( + const void *key, + const void *base, + size_t * nmemb, + size_t size, + int (*compar)(const void *, const void *)) +{ + size_t i; + + for (i = 0; i < *nmemb; ++i) + { + const char *cp = (const char *) base + i * size; + + if (compar(key, (void *) cp) == 0) + return (void *) cp; + } + + return (void *) 0; +} /* pdc_lfind */ + + +/********************* pseudo random numbers *********************/ + +int +pdc_rand(pdc_core *pdc) +{ + pdc->last_rand = pdc->last_rand * 1103515245 + 12345; + + return (pdc_uint)(pdc->last_rand / 65536) % 32768; +} /* pdc_rand */ + +void +pdc_srand(pdc_core *pdc, pdc_uint seed) +{ + pdc->last_rand = seed; +} /* pdc_srand */ -- cgit v1.2.3