/*************************************************************************\ * Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. * Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * SPDX-License-Identifier: EPICS * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* Authors: Jun-ichi Odagiri, Marty Kraimer, Eric Norum, * Mark Rivers, Andrew Johnson, Ralph Lange * * Routines in this file should have corresponding test code in * libCom/test/epicsStringTest.c */ #include #include #include #include #include #include #ifndef vxWorks #include #else /* VxWorks automaticaly includes stdint.h defining SIZE_MAX in 6.9 but not earlier */ #ifndef SIZE_MAX #define SIZE_MAX (size_t)-1 #endif #endif #include "epicsAssert.h" #include "epicsStdio.h" #include "cantProceed.h" #include "epicsString.h" #include "epicsMath.h" /* Deprecated, use epicsStrnRawFromEscaped() instead */ int dbTranslateEscape(char *dst, const char *src) { size_t big_enough = strlen(src) + 1; return epicsStrnRawFromEscaped(dst, big_enough, src, big_enough); } int epicsStrnRawFromEscaped(char *dst, size_t dstlen, const char *src, size_t srclen) { int rem = dstlen; int ndst = 0; while (srclen--) { int c = *src++; #define OUT(chr) if (--rem > 0) ndst++, *dst++ = chr if (!c) break; input: if (c != '\\') { OUT(c); continue; } if (!srclen-- || !(c = *src++)) break; switch (c) { case 'a': OUT('\a'); break; case 'b': OUT('\b'); break; case 'f': OUT('\f'); break; case 'n': OUT('\n'); break; case 'r': OUT('\r'); break; case 't': OUT('\t'); break; case 'v': OUT('\v'); break; case '\\': OUT('\\'); break; case '\'': OUT('\''); break; case '\"': OUT('\"'); break; case '0': OUT('\0'); break; case 'x' : { /* \xXX */ unsigned int u = 0; if (!srclen-- || !(c = *src++ & 0xff)) goto done; if (!isxdigit(c)) goto input; u = u << 4 | ((c > '9') ? toupper(c) - 'A' + 10 : c - '0'); if (!srclen-- || !(c = *src++ & 0xff)) { OUT(u); goto done; } if (!isxdigit(c)) { OUT(u); goto input; } u = u << 4 | ((c > '9') ? toupper(c) - 'A' + 10 : c - '0'); OUT(u); } break; default: OUT(c); } #undef OUT } done: if (dstlen) *dst = '\0'; return ndst; } int epicsStrnEscapedFromRaw(char *dst, size_t dstlen, const char *src, size_t srclen) { int rem = dstlen; int ndst = 0; if (dst == src) return -1; while (srclen--) { static const char hex[] = "0123456789abcdef"; int c = *src++; #define OUT(chr) ndst++; if (--rem > 0) *dst++ = chr switch (c) { case '\a': OUT('\\'); OUT('a'); break; case '\b': OUT('\\'); OUT('b'); break; case '\f': OUT('\\'); OUT('f'); break; case '\n': OUT('\\'); OUT('n'); break; case '\r': OUT('\\'); OUT('r'); break; case '\t': OUT('\\'); OUT('t'); break; case '\v': OUT('\\'); OUT('v'); break; case '\\': OUT('\\'); OUT('\\'); break; case '\'': OUT('\\'); OUT('\''); break; case '\"': OUT('\\'); OUT('\"'); break; case '\0': OUT('\\'); OUT('0'); break; default: if (isprint(c & 0xff)) { OUT(c); break; } OUT('\\'); OUT('x'); OUT(hex[(c >> 4) & 0x0f]); OUT(hex[ c & 0x0f]); } #undef OUT } if (dstlen) *dst = '\0'; return ndst; } size_t epicsStrnEscapedFromRawSize(const char *src, size_t srclen) { size_t ndst = srclen; while (srclen--) { int c = *src++; switch (c) { case '\a': case '\b': case '\f': case '\n': case '\r': case '\t': case '\v': case '\\': case '\'': case '\"': case '\0': ndst++; break; default: if (!isprint(c & 0xff)) ndst += 3; } } return ndst; } int epicsStrCaseCmp(const char *s1, const char *s2) { while (1) { int ch1 = toupper((int) *s1); int ch2 = toupper((int) *s2); if (ch2 == 0) return (ch1 != 0); if (ch1 == 0) return -1; if (ch1 < ch2) return -1; if (ch1 > ch2) return 1; s1++; s2++; } } int epicsStrnCaseCmp(const char *s1, const char *s2, size_t len) { size_t i = 0; while (i++ < len) { int ch1 = toupper((int) *s1); int ch2 = toupper((int) *s2); if (ch2 == 0) return (ch1 != 0); if (ch1 == 0) return -1; if (ch1 < ch2) return -1; if (ch1 > ch2) return 1; s1++; s2++; } return 0; } char * epicsStrnDup(const char *s, size_t len) { char *buf = mallocMustSucceed(len + 1, "epicsStrnDup"); strncpy(buf, s, len); buf[len] = '\0'; return buf; } char * epicsStrDup(const char *s) { return strcpy(mallocMustSucceed(strlen(s)+1, "epicsStrDup"), s); } int epicsStrPrintEscaped(FILE *fp, const char *s, size_t len) { int nout = 0; while (len--) { char c = *s++; switch (c) { case '\a': nout += fprintf(fp, "\\a"); break; case '\b': nout += fprintf(fp, "\\b"); break; case '\f': nout += fprintf(fp, "\\f"); break; case '\n': nout += fprintf(fp, "\\n"); break; case '\r': nout += fprintf(fp, "\\r"); break; case '\t': nout += fprintf(fp, "\\t"); break; case '\v': nout += fprintf(fp, "\\v"); break; case '\\': nout += fprintf(fp, "\\\\"); break; case '\'': nout += fprintf(fp, "\\'"); break; case '\"': nout += fprintf(fp, "\\\""); break; default: if (isprint(0xff & (int)c)) nout += fprintf(fp, "%c", c); else nout += fprintf(fp, "\\x%02x", (unsigned char)c); break; } } return nout; } /* Until Base requires POSIX 2008 we must provide our own implementation */ size_t epicsStrnLen(const char *s, size_t maxlen) { size_t i; for (i=0; i> 5)); if (!(c = *str++)) break; hash ^= (hash << 7) ^ c ^ (hash >> 3); } return hash; } unsigned int epicsMemHash(const char *str, size_t length, unsigned int seed) { unsigned int hash = seed; while (length--) { hash ^= ~((hash << 11) ^ *str++ ^ (hash >> 5)); if (!length--) break; hash ^= (hash << 7) ^ *str++ ^ (hash >> 3); } return hash; } /* Compute normalized Levenshtein distance * * https://en.wikipedia.org/wiki/Levenshtein_distance * * We modify this to give half weight to case insensitive substitution. * All normal integer weights are multiplied by two, with case * insensitive added in as one. */ double epicsStrSimilarity(const char *A, const char *B) { double ret = 0; size_t lA, lB, a, b; size_t norm; size_t *dist0, *dist1, *stemp; lA = strlen(A); lB = strlen(B); /* max number of edits to change A into B is max(lA, lB) */ norm = lA > lB ? lA : lB; /* take into account our weighting */ norm *= 2u; dist0 = calloc(1+lB, sizeof(*dist0)); dist1 = calloc(1+lB, sizeof(*dist1)); if(!dist0 || !dist1) { ret = -1.0; goto done; } for(b=0; b<1+lB; b++) dist0[b] = 2*b; for(a=0; a inscost) mincost = inscost; if(mincost > subcost) mincost = subcost; dist1[b+1] = mincost; } stemp = dist0; dist0 = dist1; dist1 = stemp; } ret = norm ? (norm - dist0[lB]) / (double)norm : 1.0; done: free(dist0); free(dist1); return ret; }