/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2021 Oxide Computer Company */ #include #include #include #include #include #include #include #include #include /* * Convert the specified integer value to a string represented in the given * base. The flags parameter is a bitfield of the formatting flags defined in * mdb_string.h. A pointer to a static conversion buffer is returned. */ const char * numtostr(uintmax_t uvalue, int base, uint_t flags) { static const char ldigits[] = "0123456789abcdef"; static const char udigits[] = "0123456789ABCDEF"; static char buf[68]; /* Enough for ULLONG_MAX in binary plus prefixes */ const char *digits = (flags & NTOS_UPCASE) ? udigits : ldigits; int i = sizeof (buf); intmax_t value = (intmax_t)uvalue; int neg = (flags & NTOS_UNSIGNED) == 0 && value < 0; uintmax_t rem = neg ? -value : value; buf[--i] = 0; do { buf[--i] = digits[rem % base]; rem /= base; } while (rem != 0); if (flags & NTOS_SHOWBASE) { uintmax_t lim; char c = 0; switch (base) { case 2: lim = 1; c = 'i'; break; case 8: lim = 7; c = 'o'; break; case 10: lim = 9; c = 't'; break; case 16: lim = 9; c = 'x'; break; } if (c != 0 && uvalue > lim) { buf[--i] = c; buf[--i] = '0'; } } if (neg) buf[--i] = '-'; else if (flags & NTOS_SIGNPOS) buf[--i] = '+'; return ((const char *)(&buf[i])); } #define CTOI(x) (((x) >= '0' && (x) <= '9') ? (x) - '0' : \ ((x) >= 'a' && (x) <= 'z') ? (x) + 10 - 'a' : (x) + 10 - 'A') /* * Convert a string to an unsigned integer value using the specified base. * In the event of overflow or an invalid character, we generate an * error message and longjmp back to the main loop using yyerror(). */ uintmax_t mdb_strtonum(const char *s, int base) { uintmax_t multmax = (uintmax_t)ULLONG_MAX / (uintmax_t)(uint_t)base; uintmax_t val = 0; int c, i, neg = 0; switch (c = *s) { case '-': neg++; /*FALLTHRU*/ case '+': c = *++s; } if (c == '\0') goto done; if ((val = CTOI(c)) >= base) yyerror("digit '%c' is invalid in current base\n", c); for (c = *++s; c != '\0'; c = *++s) { if (val > multmax) goto oflow; if (c == '_') continue; if ((i = CTOI(c)) >= base) yyerror("digit '%c' is invalid in current base\n", c); val *= base; if ((uintmax_t)ULLONG_MAX - val < (uintmax_t)i) goto oflow; val += i; } done: return (neg ? -val : val); oflow: yyerror("specified value exceeds maximum immediate value\n"); return ((uintmax_t)ULLONG_MAX); } /* * Quick string to unsigned long conversion function. This function performs * no overflow checking and is only meant for internal mdb use. It allows * the caller to specify the length of the string in bytes and a base. */ ulong_t strntoul(const char *s, size_t nbytes, int base) { ulong_t n; int c; for (n = 0; nbytes != 0 && (c = *s) != '\0'; s++, nbytes--) n = n * base + CTOI(c); return (n); } /* * Return a boolean value indicating whether or not a string consists * solely of characters which are digits 0..9. */ int strisnum(const char *s) { for (; *s != '\0'; s++) { if (*s < '0' || *s > '9') return (0); } return (1); } /* * Return a boolean value indicating whether or not a string contains a * number. The number may be in the current radix, or it may have an * explicit radix qualifier. The number will be validated against the * legal characters for the given radix. */ int strisbasenum(const char *s) { char valid[] = "0123456789aAbBcCdDeEfF"; int radix = mdb.m_radix; if (s[0] == '0') { switch (s[1]) { case 'I': case 'i': radix = 2; s += 2; break; case 'O': case 'o': radix = 8; s += 2; break; case 'T': case 't': radix = 10; s += 2; break; case 'x': case 'X': radix = 16; s += 2; break; } } /* limit `valid' to the digits valid for this base */ valid[radix > 10 ? 10 + (radix - 10) * 2 : radix] = '\0'; do { if (!strchr(valid, *s)) return (0); } while (*++s != '\0'); return (1); } /* * Quick string to integer (base 10) conversion function. This performs * no overflow checking and is only meant for internal mdb use. */ int strtoi(const char *s) { int c, n; for (n = 0; (c = *s) >= '0' && c <= '9'; s++) n = n * 10 + c - '0'; return (n); } /* * Create a copy of string s using the mdb allocator interface. */ char * strdup(const char *s) { char *s1 = mdb_alloc(strlen(s) + 1, UM_SLEEP); (void) strcpy(s1, s); return (s1); } /* * Create a copy of string s, but only duplicate the first n bytes. */ char * strndup(const char *s, size_t n) { char *s2 = mdb_alloc(n + 1, UM_SLEEP); (void) strncpy(s2, s, n); s2[n] = '\0'; return (s2); } /* * Convenience routine for freeing strings. */ void strfree(char *s) { mdb_free(s, strlen(s) + 1); } /* * Transform string s inline, converting each embedded C escape sequence string * to the corresponding character. For example, the substring "\n" is replaced * by an inline '\n' character. The length of the resulting string is returned. */ size_t stresc2chr(char *s) { char *p, *q, c; int esc = 0; for (p = q = s; (c = *p) != '\0'; p++) { if (esc) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c -= '0'; p++; if (*p >= '0' && *p <= '7') { c = c * 8 + *p++ - '0'; if (*p >= '0' && *p <= '7') c = c * 8 + *p - '0'; else p--; } else p--; *q++ = c; break; case 'a': *q++ = '\a'; break; case 'b': *q++ = '\b'; break; case 'f': *q++ = '\f'; break; case 'n': *q++ = '\n'; break; case 'r': *q++ = '\r'; break; case 't': *q++ = '\t'; break; case 'v': *q++ = '\v'; break; case '"': case '\\': *q++ = c; break; default: *q++ = '\\'; *q++ = c; } esc = 0; } else { if ((esc = c == '\\') == 0) *q++ = c; } } *q = '\0'; return ((size_t)(q - s)); } /* * Create a copy of string s in which certain unprintable or special characters * have been converted to the string representation of their C escape sequence. * For example, the newline character is expanded to the string "\n". */ char * strchr2esc(const char *s, size_t n) { const char *p; char *q, *s2, c; size_t addl = 0; for (p = s; p < s + n; p++) { switch (c = *p) { case '\0': case '\a': case '\b': case '\f': case '\n': case '\r': case '\t': case '\v': case '"': case '\\': addl++; /* 1 add'l char needed to follow \ */ break; case ' ': break; default: if (c < '!' || c > '~') addl += 3; /* 3 add'l chars following \ */ } } s2 = mdb_alloc(n + addl + 1, UM_SLEEP); for (p = s, q = s2; p < s + n; p++) { switch (c = *p) { case '\0': *q++ = '\\'; *q++ = '0'; break; case '\a': *q++ = '\\'; *q++ = 'a'; break; case '\b': *q++ = '\\'; *q++ = 'b'; break; case '\f': *q++ = '\\'; *q++ = 'f'; break; case '\n': *q++ = '\\'; *q++ = 'n'; break; case '\r': *q++ = '\\'; *q++ = 'r'; break; case '\t': *q++ = '\\'; *q++ = 't'; break; case '\v': *q++ = '\\'; *q++ = 'v'; break; case '"': *q++ = '\\'; *q++ = '"'; break; case '\\': *q++ = '\\'; *q++ = '\\'; break; case ' ': *q++ = c; break; default: if (c < '!' || c > '~') { *q++ = '\\'; *q++ = ((c >> 6) & 3) + '0'; *q++ = ((c >> 3) & 7) + '0'; *q++ = (c & 7) + '0'; } else *q++ = c; } } *q = '\0'; return (s2); } /* * Create a copy of string s in which certain unprintable or special characters * have been converted to an odd representation of their escape sequence. * This algorithm is the old adb convention for representing such sequences. */ char * strchr2adb(const char *s, size_t n) { size_t addl = 0; const char *p; char *q, *s2; for (p = s; p < s + n; p++) { char c = *p & CHAR_MAX; if (c < ' ' || c == CHAR_MAX) addl++; /* 1 add'l char needed for "^" */ } s2 = mdb_alloc(n + addl + 1, UM_SLEEP); for (p = s, q = s2; p < s + n; p++) { char c = *p & CHAR_MAX; if (c == CHAR_MAX) { *q++ = '^'; *q++ = '?'; } else if (c < ' ') { *q++ = '^'; *q++ = c + '@'; } else *q++ = c; } *q = '\0'; return (s2); } /* * Same as strchr, but we only search the first n characters */ char * strnchr(const char *s, int c, size_t n) { int i = 0; for (i = 0; i < n; i++) { if (*(s + i) == (char)c) return ((char *)(s + i)); } return (NULL); } /* * Split the string s at the first occurrence of character c. This character * is replaced by \0, and a pointer to the remainder of the string is returned. */ char * strsplit(char *s, char c) { char *p; if ((p = strchr(s, c)) == NULL) return (NULL); *p++ = '\0'; return (p); } /* * Same as strsplit, but split from the last occurrence of character c. */ char * strrsplit(char *s, char c) { char *p; if ((p = strrchr(s, c)) == NULL) return (NULL); *p++ = '\0'; return (p); } /* * Return the address of the first occurrence of any character from s2 * in the string s1, or NULL if none exists. This is similar to libc's * strpbrk, but we add a third parameter to limit the search to the * specified number of bytes in s1, or a \0 character, whichever is * encountered first. */ const char * strnpbrk(const char *s1, const char *s2, size_t nbytes) { const char *p; if (nbytes == 0) return (NULL); do { for (p = s2; *p != '\0' && *p != *s1; p++) continue; if (*p != '\0') return (s1); } while (--nbytes != 0 && *s1++ != '\0'); return (NULL); } /* * Abbreviate a string if it meets or exceeds the specified length, including * the terminating null character. The string is abbreviated by replacing the * last four characters with " ...". strabbr is useful in constructs such as * this one, where nbytes = sizeof (buf): * * if (mdb_snprintf(buf, nbytes, "%s %d %c", ...) >= nbytes) * (void) strabbr(buf, nbytes); * * No modifications are made if nbytes is too small to hold the suffix itself. */ char * strabbr(char *s, size_t nbytes) { static const char suffix[] = " ..."; if (nbytes > sizeof (suffix) && strlen(s) >= nbytes - 1) (void) strcpy(&s[nbytes - sizeof (suffix)], suffix); return (s); } /* * Return the basename (name after final /) of the given string. We use * strbasename rather than basename to avoid conflicting with libgen.h's * non-const function prototype. */ const char * strbasename(const char *s) { const char *p = strrchr(s, '/'); if (p == NULL) return (s); return (++p); } /* * Return the directory name (name prior to the final /) of the given string. * The string itself is modified. */ char * strdirname(char *s) { static char slash[] = "/"; static char dot[] = "."; char *p; if (s == NULL || *s == '\0') return (dot); for (p = s + strlen(s); p != s && *--p == '/'; ) continue; if (p == s && *p == '/') return (slash); while (p != s) { if (*--p == '/') { while (*p == '/' && p != s) p--; *++p = '\0'; return (s); } } return (dot); } /* * Return a pointer to the first character in the string that makes it an * invalid identifer (i.e. incompatible with the mdb syntax), or NULL if * the string is a valid identifier. */ const char * strbadid(const char *s) { return (strpbrk(s, "#%^&*-+=,:$/\\?<>;|!`'\"[]\n\t() {}")); } /* * Return a boolean value indicating if the given string consists solely * of printable ASCII characters terminated by \0. */ int strisprint(const char *s) { for (; *s != '\0'; s++) { if (*s < ' ' || *s > '~') return (0); } return (1); } /* * This is a near direct copy of the inet_ntop() code in * uts/common/inet/ip/ipv6.c, duplicated here for kmdb's sake. */ static void convert2ascii(char *buf, const in6_addr_t *addr) { int hexdigits; int head_zero = 0; int tail_zero = 0; /* tempbuf must be big enough to hold ffff:\0 */ char tempbuf[6]; char *ptr; uint16_t *addr_component, host_component; size_t len; int first = FALSE; int med_zero = FALSE; int end_zero = FALSE; addr_component = (uint16_t *)addr; ptr = buf; /* First count if trailing zeroes higher in number */ for (hexdigits = 0; hexdigits < 8; hexdigits++) { if (*addr_component == 0) { if (hexdigits < 4) head_zero++; else tail_zero++; } addr_component++; } addr_component = (uint16_t *)addr; if (tail_zero > head_zero && (head_zero + tail_zero) != 7) end_zero = TRUE; for (hexdigits = 0; hexdigits < 8; hexdigits++) { /* if entry is a 0 */ if (*addr_component == 0) { if (!first && *(addr_component + 1) == 0) { if (end_zero && (hexdigits < 4)) { *ptr++ = '0'; *ptr++ = ':'; } else { if (hexdigits == 0) *ptr++ = ':'; /* add another */ *ptr++ = ':'; first = TRUE; med_zero = TRUE; } } else if (first && med_zero) { if (hexdigits == 7) *ptr++ = ':'; addr_component++; continue; } else { *ptr++ = '0'; *ptr++ = ':'; } addr_component++; continue; } if (med_zero) med_zero = FALSE; tempbuf[0] = '\0'; mdb_nhconvert(&host_component, addr_component, sizeof (uint16_t)); (void) mdb_snprintf(tempbuf, sizeof (tempbuf), "%x:", host_component & 0xffff); len = strlen(tempbuf); bcopy(tempbuf, ptr, len); ptr = ptr + len; addr_component++; } *--ptr = '\0'; } char * mdb_inet_ntop(int af, const void *addr, char *buf, size_t buflen) { in6_addr_t *v6addr; uchar_t *v4addr; char *caddr; #define UC(b) (((int)b) & 0xff) switch (af) { case AF_INET: ASSERT(buflen >= INET_ADDRSTRLEN); v4addr = (uchar_t *)addr; (void) mdb_snprintf(buf, buflen, "%d.%d.%d.%d", UC(v4addr[0]), UC(v4addr[1]), UC(v4addr[2]), UC(v4addr[3])); return (buf); case AF_INET6: ASSERT(buflen >= INET6_ADDRSTRLEN); v6addr = (in6_addr_t *)addr; if (IN6_IS_ADDR_V4MAPPED(v6addr)) { caddr = (char *)addr; (void) mdb_snprintf(buf, buflen, "::ffff:%d.%d.%d.%d", UC(caddr[12]), UC(caddr[13]), UC(caddr[14]), UC(caddr[15])); } else if (IN6_IS_ADDR_V4COMPAT(v6addr)) { caddr = (char *)addr; (void) mdb_snprintf(buf, buflen, "::%d.%d.%d.%d", UC(caddr[12]), UC(caddr[13]), UC(caddr[14]), UC(caddr[15])); } else if (IN6_IS_ADDR_UNSPECIFIED(v6addr)) { (void) mdb_snprintf(buf, buflen, "::"); } else { convert2ascii(buf, v6addr); } return (buf); } #undef UC return (NULL); }