/*---------------------------------------------------------------------------* | PDFlib - A library for generating PDF on the fly | +---------------------------------------------------------------------------+ | Copyright (c) 1997-2005 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.2 2006-07-11 13:10:33 alex Exp $ * * PDFlib various utility routines * */ #include #include "pc_util.h" #ifdef AS400 #include /* for getenv() emulation */ #endif #if defined (isfinite) #define PDC_ISFINITE isfinite #else /* isfinite */ #ifdef _WIN32 #include #include #define PDC_ISFINITE _finite #else /* _WIN32 */ #define PDC_ISFINITE finite #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); } } /* -------------------------- 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) { #ifndef WINCE time_t timer, gtimer; struct tm ltime; double diffminutes; int utcoffset; #else SYSTEMTIME st; #endif #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 (isupper((int)language[i])) { language[i] = (char)tolower((int)language[i]); } } country_code = 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 = 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(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(pdc_byte *data) { return (pdc_short) ((pdc_short) (data[1] << 8) | data[0]); } pdc_ushort pdc_get_le_ushort(pdc_byte *data) { return (pdc_ushort) ((data[1] << 8) | data[0]); } pdc_uint32 pdc_get_le_ulong3(pdc_byte *data) { return (pdc_uint32) (((((data[2]) << 8) | data[1]) << 8) | data[0]); } pdc_sint32 pdc_get_le_long(pdc_byte *data) { return ((pdc_sint32) (((((data[3] << 8) | data[2]) << 8) | data[1]) << 8) | data[0]); } pdc_uint32 pdc_get_le_ulong(pdc_byte *data) { return (pdc_uint32) ((((((data[3] << 8) | data[2]) << 8) | data[1]) << 8) | data[0]); } pdc_short pdc_get_be_short(pdc_byte *data) { return (pdc_short) ((pdc_short) (data[0] << 8) | data[1]); } pdc_ushort pdc_get_be_ushort(pdc_byte *data) { return (pdc_ushort) ((data[0] << 8) | data[1]); } pdc_uint32 pdc_get_be_ulong3(pdc_byte *data) { return (pdc_uint32) (((((data[0]) << 8) | data[1]) << 8) | data[2]); } pdc_sint32 pdc_get_be_long(pdc_byte *data) { return ((pdc_sint32) (((((data[0] << 8) | data[1]) << 8) | data[2]) << 8) | data[3]); } pdc_uint32 pdc_get_be_ulong(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. */ char * pdc_strdup(pdc_core *pdc, const char *text) { char *buf; size_t len; static const char fn[] = "pdc_strdup"; if (text == NULL) pdc_error(pdc, PDC_E_INT_NULLARG, fn, 0, 0, 0); len = pdc_strlen(text) + 1; 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; size_t len; static const char fn[] = "pdc_strdup_tmp"; if (text == NULL) pdc_error(pdc, PDC_E_INT_NULLARG, fn, 0, 0, 0); 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; size_t len; static const char fn[] = "pdc_strdup_withbom"; if (text == NULL) pdc_error(pdc, PDC_E_INT_NULLARG, fn, 0, 0, 0); len = strlen(text); buf = (char *) pdc_malloc(pdc, len + 4, fn); pdc_copy_utf8_bom(buf); strcpy(&buf[3], text); return buf; } /* * 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] > 0x00FF) { 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 (c < 0x20 || (c >= 0x7F && c <= 0xA0)) { 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_ET_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, char ***stringlist) { static const char fn[] = "pdc_split_stringlist"; const char *separstr = " \f\n\r\t\v"; const char *oldtext; char **strlist = NULL, *newtext; int i, it, len, jt = 0, jtb = 0, maxk = 0, count = 0, inside = 0; if (text == NULL) pdc_error(pdc, PDC_E_INT_NULLARG, fn, 0, 0, 0); if (stringlist) *stringlist = NULL; if (i_separstr) separstr = i_separstr; /* check for empty string */ i = (int) strspn(text, separstr); oldtext = &text[i]; len = (int) strlen(oldtext); if (!len) return 0; /* check for UTF-8-BOM */ if (pdc_is_utf8_bytecode(oldtext)) { oldtext = &text[i + 3]; len -= 3; i = (int) strspn(oldtext, separstr); oldtext = &oldtext[i]; len -= i; 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) i = 1; else if (inside <= 0) i = (int) strspn(&oldtext[it], separstr); else i = 0; /* close text part */ if (i) { 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 += i; if (it >= len ) break; /* new text part */ jt++; jtb = jt; } /* open and close brace */ if (oldtext[it] == '{') { inside++; if (inside == 1) continue; } else if (oldtext[it] == '}') { inside--; if (inside == 0) continue; } /* masked braces */ if (oldtext[it] == '\\' && (oldtext[it+1] == '{' || oldtext[it+1] == '}')) { if (it > 0 && oldtext[it-1] == '\\') continue; it++; } /* 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); } } /* * 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_stricmp(const char *s1, const char *s2) { char c1, c2; if (s1 == s2) return (0); if (s1 == NULL) return (-1); if (s2 == NULL) return (1); for (; *s1 != '\0' && *s2 != '\0'; s1++, s2++) { if ((c1 = *s1) == (c2 = *s2)) continue; if (isupper((int)c1)) c1 = (char) tolower((int)c1); if (isupper((int)c2)) c2 = (char) tolower((int)c2); if (c1 != c2) break; } /* TODO: this function is useful for checks on (in)equality only; ** it does NOT compare correctly! */ return (*s1 - *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) { char c1, c2; int i; if (s1 == s2) return (0); if (s1 == NULL) return (-1); if (s2 == NULL) return (1); for (i=0; i < n && *s1 != '\0' && *s2 != '\0'; i++, s1++, s2++) { if ((c1 = *s1) == (c2 = *s2)) continue; if (isupper((int)c1)) c1 = (char) tolower((int)c1); if (isupper((int)c2)) c2 = (char) tolower((int)c2); if (c1 != c2) break; } return ((i < n) ? (int)(*s1 - *s2) : 0); } /* * 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; for (i = (int) strlen(str) - 1; i >= 0; i--) if (!isspace((unsigned char) str[i])) break; str[i + 1] = '\0'; return str; } char * pdc_str2trim(char *str) { int i; for (i = (int) strlen(str) - 1; i >= 0; i--) if (!isspace((unsigned char) str[i])) break; str[i + 1] = '\0'; for (i = 0; ; i++) if (!isspace((unsigned char) str[i])) break; if (i > 0) memmove(str, &str[i], strlen(&str[i]) + 1); return str; } 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++; } } /* ----------------------- number converting ----------------------- */ /* * pdc_str2double converts a null terminated and trimed 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 = isdigit((int) *s); if (isd) { do { dz = 10 * dz + *s - '0'; s++; } while (isdigit((int) *s)); } /* decimal point */ if (*s == '.' || *s == ',') { const char *sa; double adz = 0; s++; isd = isdigit((int) *s); if (!isd) return pdc_false; /* places after decimal point */ sa = s; do { adz = 10 * adz + *s - '0'; s++; } while (isdigit((int) *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 (!isdigit((int) *s)) return pdc_false; do { pdz = 10 * pdz + *s - '0'; s++; } while (isdigit((int) *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 trimed 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)) { 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 > string) { if (!*s) return pdc_false; flags |= PDC_INT_HEXADEC; } } /* hexadecimal */ if (flags & PDC_INT_HEXADEC) { while (isxdigit((int) *s)) { if (isalpha(*s)) lzd = (isupper((int) *s) ? 'A' : 'a') - 10; else lzd = '0'; dz = 16 * dz + *s - lzd; s++; } if (*string == '<') { if (*s == '>') s += 1; else return pdc_false; } } /* decimal */ else { while (isdigit((int) *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) { 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_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_get_floatdigits(pdc); 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) { 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) fwrite(buf, 1, (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 (isdigit((int) *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 (isdigit((int) *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", 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 's': case 'T': { char * str = va_arg(args, char *); const char *cstr = str; 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_tracestring(pdc, str, l); len = strlen(cstr); } } 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) { fwrite(buf, 1, (size_t) (dest - buf), fp); dest = buf; } fwrite(cstr, 1, len, fp); } else { memcpy(dest, cstr, len); dest += len; } } 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", 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, 0, 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, 0, 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, 0, 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, 0, format, ap); va_end(ap); return result; } /* pdc_sprintf */ /* --------------------- 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, &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_ET_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_ET_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); }