summaryrefslogtreecommitdiff
path: root/src/pdflib/pdcore/pc_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pdflib/pdcore/pc_util.c')
-rw-r--r--src/pdflib/pdcore/pc_util.c2726
1 files changed, 2726 insertions, 0 deletions
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 <errno.h>
+
+#include "pc_util.h"
+#include "pc_file.h"
+#include "pc_ctype.h"
+
+#ifdef AS400
+#include <qp0z1170.h> /* for getenv() emulation */
+#endif
+
+#ifdef __sun
+#include <ieeefp.h> /* for finite */
+#endif
+
+#if defined (isfinite)
+#define PDC_ISFINITE isfinite
+#else /* isfinite */
+
+#ifdef _WIN32
+#include <windows.h>
+#include <float.h>
+#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 <time.h>
+#else
+#include <winbase.h>
+#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, &ltime);
+
+#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, &ltime);
+ gtimer = mktime(&ltime);
+ pdc_localtime_r(&timer, &ltime);
+ ltime.tm_isdst = 0;
+ diffminutes = difftime(mktime(&ltime), 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, &ltime);
+
+ 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 */