/*------------------------------------------------------------------ * wcsnatcmp.c -- Natural order comparison of two strings * * Changed February 2018, Reini Urban * * Copyright (C) 2000, 2004 by Martin Pool * Copyright (C) 2018 by Reini Urban * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. *------------------------------------------------------------------ */ #ifdef FOR_DOXYGEN #include "safe_str_lib.h" #else #include "safeclib_private.h" #include #endif #ifndef HAVE_ISWDIGIT #define iswdigit(ch) isdigit((int)(ch)) #endif #ifndef HAVE_ISWSPACE #define iswspace(ch) isspace((int)(ch)) #endif /** * @def wcsnatcmp_s(dest,dmax,src,smax,resultp) * @def wcsnaticmp_s(dest,dmax,src,smax,resultp) * @brief * Natural order comparison of strings. The longest run of numbers wins * over alpha chars. * * @remark EXTENSION TO * ISO/IEC TR 24731, Programming languages, environments * and system software interfaces, Extensions to the C Library, * Part I: Bounds-checking interfaces * * @param[in] dest pointer to wide string to compare against * @param[in] dmax restricted maximum length of wide string dest * @param[in] src pointer to the wide string to be compared to dest * @param[in] smax restricted maximum length of wide string src * @param[out] resultp pointer to int result, greater than 0, * equal to 0 or less than 0, if the string pointed * to by dest is greater than, equal to or less * than the string pointed to by src respectively. * * @pre Neither dest nor src shall be a null pointer. * @pre resultp shall not be a null pointer. * @pre dmax/smax shall not be 0 * @pre dmax/smax shall not be greater than RSIZE_MAX_WSTR and size of * dest/src * * @return resultp (when the return code is OK) * >0 when dest greater than src * 0 when strings are the same * <0 when dest less than src * * @retval EOK when comparison is complete * @retval ESUNTERM when dest or src is unterminated, or dmax is too small. * @retval ESNULLP when dest/src/resultp is NULL pointer * @retval ESZEROL when dmax/smax = 0 * @retval ESLEMAX when dmax/smax > RSIZE_MAX_WSTR * @retval EOVERFLOW when dmax/smax > size of dest/src (optionally, when the * compiler knows the object_size statically) * @retval ESLEWRNG when dmax != size of dest and --enable-error-dmax * * @see * strnatcmp_s() wcscmp_s() */ /* TODO: bounds check */ static int compare_right(wchar_t const *a, wchar_t const *b) { int bias = 0; /* The longest run of digits wins. That aside, the greatest value wins, but we can't know that it will until we've scanned both numbers to know that they have the same magnitude, so we remember it in BIAS. */ for (;; a++, b++) { if (!iswdigit((wint_t)*a) && !iswdigit((wint_t)*b)) return bias; if (!iswdigit((wint_t)*a)) return -1; if (!iswdigit((wint_t)*b)) return +1; if (*a < *b) { if (!bias) bias = -1; } else if (*a > *b) { if (!bias) bias = +1; } else if (!*a && !*b) return bias; } return 0; } /* TODO: bounds check */ static int compare_left(wchar_t const *a, wchar_t const *b) { /* Compare two left-aligned numbers: the first to have a different value wins. */ for (;; a++, b++) { if (!iswdigit((wint_t)*a) && !iswdigit((wint_t)*b)) return 0; if (!iswdigit((wint_t)*a)) return -1; if (!iswdigit((wint_t)*b)) return +1; if (*a < *b) return -1; if (*a > *b) return +1; } return 0; } #ifdef FOR_DOXYGEN errno_t wcsnatcmp_s(const wchar_t *dest, rsize_t dmax, const wchar_t *src, rsize_t smax, const int fold_case, int *resultp) #else EXPORT errno_t _wcsnatcmp_s_chk(const wchar_t *dest, rsize_t dmax, const wchar_t *src, rsize_t smax, const int fold_case, int *resultp, const size_t destbos, const size_t srcbos) #endif { wchar_t *d1 = NULL, *d2 = NULL; size_t ai, bi; const size_t destsz = dmax * sizeof(wchar_t); const size_t srcsz = smax * sizeof(wchar_t); CHK_SRC_NULL("wcsnatcmp_s", resultp) *resultp = 0; CHK_DEST_NULL("wcsnatcmp_s") CHK_SRC_NULL("wcsnatcmp_s", src) CHK_DMAX_ZERO("wcsnatcmp_s") if (unlikely(dmax == 0 || smax == 0)) { invoke_safe_str_constraint_handler("wcsnatcmp_s: dmax/smax is 0", (void *)dest, ESZEROL); return RCNEGATE(ESZEROL); } if (destbos == BOS_UNKNOWN) { CHK_DMAX_MAX("wcsnatcmp_s", RSIZE_MAX_STR) BND_CHK_PTR_BOUNDS(dest, destsz); } else { CHK_DESTW_OVR("wcsnatcmp_s", destsz, destbos) } if (unlikely(smax > RSIZE_MAX_WSTR)) { invoke_safe_str_constraint_handler("wcsnatcmp_s" ": smax exceeds max", (void *)src, ESLEMAX); return RCNEGATE(ESLEMAX); } if (srcbos == BOS_UNKNOWN) { BND_CHK_PTR_BOUNDS(src, srcsz); } else { if (unlikely(srcsz > srcbos)) { invoke_safe_str_constraint_handler("wcsnatcmp_s" ": smax exceeds src", (void *)src, EOVERFLOW); return RCNEGATE(EOVERFLOW); } } if (fold_case) { rsize_t l1, l2; errno_t rc; d1 = (wchar_t *)malloc(2 * destsz); rc = wcsfc_s(d1, dmax * 2, (wchar_t * restrict) dest, &l1); if (rc != EOK) { free(d1); return rc; } d2 = (wchar_t *)malloc(2 * srcsz); rc = wcsfc_s(d2, smax * 2, (wchar_t * restrict) src, &l2); if (rc != EOK) { free(d1); free(d2); return rc; } dest = d1; src = d2; dmax = l1; smax = l2; } ai = bi = 0; while (ai < dmax) { int fractional; wchar_t ca = dest[ai]; wchar_t cb = src[bi]; /* skip over leading spaces or zeros */ while (iswspace((wint_t)ca)) ca = dest[++ai]; while (iswspace((wint_t)cb)) cb = src[++bi]; /* process run of digits */ if (iswdigit((wint_t)ca) && iswdigit((wint_t)cb)) { fractional = (ca == L'0' || cb == L'0'); if (fractional) { if ((*resultp = compare_left(dest + ai, src + bi)) != 0) { goto eok; } } else if ((*resultp = compare_right(dest + ai, src + bi)) != 0) { goto eok; } } if (!ca && !cb) { /* The strings compare the same. Perhaps the caller will want to call wcscmp_s to break the tie. */ *resultp = 0; goto eok; } if (ca < cb) { *resultp = -1; goto eok; } if (ca > cb) { *resultp = 1; goto eok; } ++ai; ++bi; if (unlikely(bi >= smax)) { invoke_safe_str_constraint_handler("wcsnatcmp_s" ": src unterminated", (void *)src, ESUNTERM); if (fold_case) { free(d1); free(d2); } return RCNEGATE(ESUNTERM); } } eok: if (fold_case) { free(d1); free(d2); } return RCNEGATE(EOK); } #ifdef __KERNEL__ EXPORT_SYMBOL(_wcsnatcmp_s_chk); #endif /* __KERNEL__ */