xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_string.c (revision f32691c936d40863e9f2c96ce452c6208bd056f2)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright 2021 Oxide Computer Company
26  */
27 
28 #include <netinet/in.h>
29 #include <limits.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 
33 #include <mdb/mdb_string.h>
34 #include <mdb/mdb_modapi.h>
35 #include <mdb/mdb_lex.h>
36 #include <mdb/mdb_debug.h>
37 #include <mdb/mdb.h>
38 
39 /*
40  * Convert the specified integer value to a string represented in the given
41  * base.  The flags parameter is a bitfield of the formatting flags defined in
42  * mdb_string.h.  A pointer to a static conversion buffer is returned.
43  */
44 const char *
numtostr(uintmax_t uvalue,int base,uint_t flags)45 numtostr(uintmax_t uvalue, int base, uint_t flags)
46 {
47 	static const char ldigits[] = "0123456789abcdef";
48 	static const char udigits[] = "0123456789ABCDEF";
49 
50 	static char buf[68]; /* Enough for ULLONG_MAX in binary plus prefixes */
51 
52 	const char *digits = (flags & NTOS_UPCASE) ? udigits : ldigits;
53 	int i = sizeof (buf);
54 
55 	intmax_t value = (intmax_t)uvalue;
56 	int neg = (flags & NTOS_UNSIGNED) == 0 && value < 0;
57 	uintmax_t rem = neg ? -value : value;
58 
59 	buf[--i] = 0;
60 
61 	do {
62 		buf[--i] = digits[rem % base];
63 		rem /= base;
64 	} while (rem != 0);
65 
66 	if (flags & NTOS_SHOWBASE) {
67 		uintmax_t lim;
68 		char c = 0;
69 
70 		switch (base) {
71 		case 2:
72 			lim = 1;
73 			c = 'i';
74 			break;
75 		case 8:
76 			lim = 7;
77 			c = 'o';
78 			break;
79 		case 10:
80 			lim = 9;
81 			c = 't';
82 			break;
83 		case 16:
84 			lim = 9;
85 			c = 'x';
86 			break;
87 		}
88 
89 		if (c != 0 && uvalue > lim) {
90 			buf[--i] = c;
91 			buf[--i] = '0';
92 		}
93 	}
94 
95 	if (neg)
96 		buf[--i] = '-';
97 	else if (flags & NTOS_SIGNPOS)
98 		buf[--i] = '+';
99 
100 	return ((const char *)(&buf[i]));
101 }
102 
103 #define	CTOI(x)	(((x) >= '0' && (x) <= '9') ? (x) - '0' : \
104 	((x) >= 'a' && (x) <= 'z') ? (x) + 10 - 'a' : (x) + 10 - 'A')
105 
106 /*
107  * Convert a string to an unsigned integer value using the specified base.
108  * In the event of overflow or an invalid character, we generate an
109  * error message and longjmp back to the main loop using yyerror().
110  */
111 uintmax_t
mdb_strtonum(const char * s,int base)112 mdb_strtonum(const char *s, int base)
113 {
114 	uintmax_t multmax = (uintmax_t)ULLONG_MAX / (uintmax_t)(uint_t)base;
115 	uintmax_t val = 0;
116 	int c, i, neg = 0;
117 
118 	switch (c = *s) {
119 	case '-':
120 		neg++;
121 		/*FALLTHRU*/
122 	case '+':
123 		c = *++s;
124 	}
125 
126 	if (c == '\0')
127 		goto done;
128 
129 	if ((val = CTOI(c)) >= base)
130 		yyerror("digit '%c' is invalid in current base\n", c);
131 
132 	for (c = *++s; c != '\0'; c = *++s) {
133 		if (val > multmax)
134 			goto oflow;
135 
136 		if (c == '_')
137 			continue;
138 
139 		if ((i = CTOI(c)) >= base)
140 			yyerror("digit '%c' is invalid in current base\n", c);
141 
142 		val *= base;
143 
144 		if ((uintmax_t)ULLONG_MAX - val < (uintmax_t)i)
145 			goto oflow;
146 
147 		val += i;
148 	}
149 done:
150 	return (neg ? -val : val);
151 oflow:
152 	yyerror("specified value exceeds maximum immediate value\n");
153 	return ((uintmax_t)ULLONG_MAX);
154 }
155 
156 /*
157  * Quick string to unsigned long conversion function.  This function performs
158  * no overflow checking and is only meant for internal mdb use.  It allows
159  * the caller to specify the length of the string in bytes and a base.
160  */
161 ulong_t
strntoul(const char * s,size_t nbytes,int base)162 strntoul(const char *s, size_t nbytes, int base)
163 {
164 	ulong_t n;
165 	int c;
166 
167 	for (n = 0; nbytes != 0 && (c = *s) != '\0'; s++, nbytes--)
168 		n = n * base + CTOI(c);
169 
170 	return (n);
171 }
172 
173 /*
174  * Return a boolean value indicating whether or not a string consists
175  * solely of characters which are digits 0..9.
176  */
177 int
strisnum(const char * s)178 strisnum(const char *s)
179 {
180 	for (; *s != '\0'; s++) {
181 		if (*s < '0' || *s > '9')
182 			return (0);
183 	}
184 
185 	return (1);
186 }
187 
188 /*
189  * Return a boolean value indicating whether or not a string contains a
190  * number.  The number may be in the current radix, or it may have an
191  * explicit radix qualifier.  The number will be validated against the
192  * legal characters for the given radix.
193  */
194 int
strisbasenum(const char * s)195 strisbasenum(const char *s)
196 {
197 	char valid[] = "0123456789aAbBcCdDeEfF";
198 	int radix = mdb.m_radix;
199 
200 	if (s[0] == '0') {
201 		switch (s[1]) {
202 		case 'I':
203 		case 'i':
204 			radix = 2;
205 			s += 2;
206 			break;
207 		case 'O':
208 		case 'o':
209 			radix = 8;
210 			s += 2;
211 			break;
212 		case 'T':
213 		case 't':
214 			radix = 10;
215 			s += 2;
216 			break;
217 		case 'x':
218 		case 'X':
219 			radix = 16;
220 			s += 2;
221 			break;
222 		}
223 	}
224 
225 	/* limit `valid' to the digits valid for this base */
226 	valid[radix > 10 ? 10 + (radix - 10) * 2 : radix] = '\0';
227 
228 	do {
229 		if (!strchr(valid, *s))
230 			return (0);
231 	} while (*++s != '\0');
232 
233 	return (1);
234 }
235 
236 /*
237  * Quick string to integer (base 10) conversion function.  This performs
238  * no overflow checking and is only meant for internal mdb use.
239  */
240 int
strtoi(const char * s)241 strtoi(const char *s)
242 {
243 	int c, n;
244 
245 	for (n = 0; (c = *s) >= '0' && c <= '9'; s++)
246 		n = n * 10 + c - '0';
247 
248 	return (n);
249 }
250 
251 /*
252  * Create a copy of string s using the mdb allocator interface.
253  */
254 char *
strdup(const char * s)255 strdup(const char *s)
256 {
257 	char *s1 = mdb_alloc(strlen(s) + 1, UM_SLEEP);
258 
259 	(void) strcpy(s1, s);
260 	return (s1);
261 }
262 
263 /*
264  * Create a copy of string s, but only duplicate the first n bytes.
265  */
266 char *
strndup(const char * s,size_t n)267 strndup(const char *s, size_t n)
268 {
269 	char *s2 = mdb_alloc(n + 1, UM_SLEEP);
270 
271 	(void) strncpy(s2, s, n);
272 	s2[n] = '\0';
273 	return (s2);
274 }
275 
276 /*
277  * Convenience routine for freeing strings.
278  */
279 void
strfree(char * s)280 strfree(char *s)
281 {
282 	mdb_free(s, strlen(s) + 1);
283 }
284 
285 /*
286  * Transform string s inline, converting each embedded C escape sequence string
287  * to the corresponding character.  For example, the substring "\n" is replaced
288  * by an inline '\n' character.  The length of the resulting string is returned.
289  */
290 size_t
stresc2chr(char * s)291 stresc2chr(char *s)
292 {
293 	char *p, *q, c;
294 	int esc = 0;
295 
296 	for (p = q = s; (c = *p) != '\0'; p++) {
297 		if (esc) {
298 			switch (c) {
299 				case '0':
300 				case '1':
301 				case '2':
302 				case '3':
303 				case '4':
304 				case '5':
305 				case '6':
306 				case '7':
307 					c -= '0';
308 					p++;
309 
310 					if (*p >= '0' && *p <= '7') {
311 						c = c * 8 + *p++ - '0';
312 
313 						if (*p >= '0' && *p <= '7')
314 							c = c * 8 + *p - '0';
315 						else
316 							p--;
317 					} else
318 						p--;
319 
320 					*q++ = c;
321 					break;
322 
323 				case 'a':
324 					*q++ = '\a';
325 					break;
326 				case 'b':
327 					*q++ = '\b';
328 					break;
329 				case 'f':
330 					*q++ = '\f';
331 					break;
332 				case 'n':
333 					*q++ = '\n';
334 					break;
335 				case 'r':
336 					*q++ = '\r';
337 					break;
338 				case 't':
339 					*q++ = '\t';
340 					break;
341 				case 'v':
342 					*q++ = '\v';
343 					break;
344 				case '"':
345 				case '\\':
346 					*q++ = c;
347 					break;
348 				default:
349 					*q++ = '\\';
350 					*q++ = c;
351 			}
352 
353 			esc = 0;
354 
355 		} else {
356 			if ((esc = c == '\\') == 0)
357 				*q++ = c;
358 		}
359 	}
360 
361 	*q = '\0';
362 	return ((size_t)(q - s));
363 }
364 
365 /*
366  * Create a copy of string s in which certain unprintable or special characters
367  * have been converted to the string representation of their C escape sequence.
368  * For example, the newline character is expanded to the string "\n".
369  */
370 char *
strchr2esc(const char * s,size_t n)371 strchr2esc(const char *s, size_t n)
372 {
373 	const char *p;
374 	char *q, *s2, c;
375 	size_t addl = 0;
376 
377 	for (p = s; p < s + n; p++) {
378 		switch (c = *p) {
379 		case '\0':
380 		case '\a':
381 		case '\b':
382 		case '\f':
383 		case '\n':
384 		case '\r':
385 		case '\t':
386 		case '\v':
387 		case '"':
388 		case '\\':
389 			addl++;		/* 1 add'l char needed to follow \ */
390 			break;
391 		case ' ':
392 			break;
393 		default:
394 			if (c < '!' || c > '~')
395 				addl += 3; /* 3 add'l chars following \ */
396 		}
397 	}
398 
399 	s2 = mdb_alloc(n + addl + 1, UM_SLEEP);
400 
401 	for (p = s, q = s2; p < s + n; p++) {
402 		switch (c = *p) {
403 		case '\0':
404 			*q++ = '\\';
405 			*q++ = '0';
406 			break;
407 		case '\a':
408 			*q++ = '\\';
409 			*q++ = 'a';
410 			break;
411 		case '\b':
412 			*q++ = '\\';
413 			*q++ = 'b';
414 			break;
415 		case '\f':
416 			*q++ = '\\';
417 			*q++ = 'f';
418 			break;
419 		case '\n':
420 			*q++ = '\\';
421 			*q++ = 'n';
422 			break;
423 		case '\r':
424 			*q++ = '\\';
425 			*q++ = 'r';
426 			break;
427 		case '\t':
428 			*q++ = '\\';
429 			*q++ = 't';
430 			break;
431 		case '\v':
432 			*q++ = '\\';
433 			*q++ = 'v';
434 			break;
435 		case '"':
436 			*q++ = '\\';
437 			*q++ = '"';
438 			break;
439 		case '\\':
440 			*q++ = '\\';
441 			*q++ = '\\';
442 			break;
443 		case ' ':
444 			*q++ = c;
445 			break;
446 		default:
447 			if (c < '!' || c > '~') {
448 				*q++ = '\\';
449 				*q++ = ((c >> 6) & 3) + '0';
450 				*q++ = ((c >> 3) & 7) + '0';
451 				*q++ = (c & 7) + '0';
452 			} else
453 				*q++ = c;
454 		}
455 	}
456 
457 	*q = '\0';
458 	return (s2);
459 }
460 
461 /*
462  * Create a copy of string s in which certain unprintable or special characters
463  * have been converted to an odd representation of their escape sequence.
464  * This algorithm is the old adb convention for representing such sequences.
465  */
466 char *
strchr2adb(const char * s,size_t n)467 strchr2adb(const char *s, size_t n)
468 {
469 	size_t addl = 0;
470 	const char *p;
471 	char *q, *s2;
472 
473 	for (p = s; p < s + n; p++) {
474 		char c = *p & CHAR_MAX;
475 
476 		if (c < ' ' || c == CHAR_MAX)
477 			addl++; /* 1 add'l char needed for "^" */
478 	}
479 
480 	s2 = mdb_alloc(n + addl + 1, UM_SLEEP);
481 
482 	for (p = s, q = s2; p < s + n; p++) {
483 		char c = *p & CHAR_MAX;
484 
485 		if (c == CHAR_MAX) {
486 			*q++ = '^';
487 			*q++ = '?';
488 		} else if (c < ' ') {
489 			*q++ = '^';
490 			*q++ = c + '@';
491 		} else
492 			*q++ = c;
493 	}
494 
495 	*q = '\0';
496 	return (s2);
497 }
498 
499 /*
500  * Same as strchr, but we only search the first n characters
501  */
502 char *
strnchr(const char * s,int c,size_t n)503 strnchr(const char *s, int c, size_t n)
504 {
505 	int i = 0;
506 
507 	for (i = 0; i < n; i++) {
508 		if (*(s + i) == (char)c)
509 			return ((char *)(s + i));
510 	}
511 
512 	return (NULL);
513 }
514 
515 /*
516  * Split the string s at the first occurrence of character c.  This character
517  * is replaced by \0, and a pointer to the remainder of the string is returned.
518  */
519 char *
strsplit(char * s,char c)520 strsplit(char *s, char c)
521 {
522 	char *p;
523 
524 	if ((p = strchr(s, c)) == NULL)
525 		return (NULL);
526 
527 	*p++ = '\0';
528 	return (p);
529 }
530 
531 /*
532  * Same as strsplit, but split from the last occurrence of character c.
533  */
534 char *
strrsplit(char * s,char c)535 strrsplit(char *s, char c)
536 {
537 	char *p;
538 
539 	if ((p = strrchr(s, c)) == NULL)
540 		return (NULL);
541 
542 	*p++ = '\0';
543 	return (p);
544 }
545 
546 /*
547  * Return the address of the first occurrence of any character from s2
548  * in the string s1, or NULL if none exists.  This is similar to libc's
549  * strpbrk, but we add a third parameter to limit the search to the
550  * specified number of bytes in s1, or a \0 character, whichever is
551  * encountered first.
552  */
553 const char *
strnpbrk(const char * s1,const char * s2,size_t nbytes)554 strnpbrk(const char *s1, const char *s2, size_t nbytes)
555 {
556 	const char *p;
557 
558 	if (nbytes == 0)
559 		return (NULL);
560 
561 	do {
562 		for (p = s2; *p != '\0' && *p != *s1; p++)
563 			continue;
564 
565 		if (*p != '\0')
566 			return (s1);
567 
568 	} while (--nbytes != 0 && *s1++ != '\0');
569 
570 	return (NULL);
571 }
572 
573 /*
574  * Abbreviate a string if it meets or exceeds the specified length, including
575  * the terminating null character.  The string is abbreviated by replacing the
576  * last four characters with " ...".  strabbr is useful in constructs such as
577  * this one, where nbytes = sizeof (buf):
578  *
579  * if (mdb_snprintf(buf, nbytes, "%s %d %c", ...) >= nbytes)
580  *         (void) strabbr(buf, nbytes);
581  *
582  * No modifications are made if nbytes is too small to hold the suffix itself.
583  */
584 char *
strabbr(char * s,size_t nbytes)585 strabbr(char *s, size_t nbytes)
586 {
587 	static const char suffix[] = " ...";
588 
589 	if (nbytes > sizeof (suffix) && strlen(s) >= nbytes - 1)
590 		(void) strcpy(&s[nbytes - sizeof (suffix)], suffix);
591 
592 	return (s);
593 }
594 
595 /*
596  * Return the basename (name after final /) of the given string.  We use
597  * strbasename rather than basename to avoid conflicting with libgen.h's
598  * non-const function prototype.
599  */
600 const char *
strbasename(const char * s)601 strbasename(const char *s)
602 {
603 	const char *p = strrchr(s, '/');
604 
605 	if (p == NULL)
606 		return (s);
607 
608 	return (++p);
609 }
610 
611 /*
612  * Return the directory name (name prior to the final /) of the given string.
613  * The string itself is modified.
614  */
615 char *
strdirname(char * s)616 strdirname(char *s)
617 {
618 	static char slash[] = "/";
619 	static char dot[] = ".";
620 	char *p;
621 
622 	if (s == NULL || *s == '\0')
623 		return (dot);
624 
625 	for (p = s + strlen(s); p != s && *--p == '/'; )
626 		continue;
627 
628 	if (p == s && *p == '/')
629 		return (slash);
630 
631 	while (p != s) {
632 		if (*--p == '/') {
633 			while (*p == '/' && p != s)
634 				p--;
635 			*++p = '\0';
636 			return (s);
637 		}
638 	}
639 
640 	return (dot);
641 }
642 
643 /*
644  * Return a pointer to the first character in the string that makes it an
645  * invalid identifer (i.e. incompatible with the mdb syntax), or NULL if
646  * the string is a valid identifier.
647  */
648 const char *
strbadid(const char * s)649 strbadid(const char *s)
650 {
651 	return (strpbrk(s, "#%^&*-+=,:$/\\?<>;|!`'\"[]\n\t() {}"));
652 }
653 
654 /*
655  * Return a boolean value indicating if the given string consists solely
656  * of printable ASCII characters terminated by \0.
657  */
658 int
strisprint(const char * s)659 strisprint(const char *s)
660 {
661 	for (; *s != '\0'; s++) {
662 		if (*s < ' ' || *s > '~')
663 			return (0);
664 	}
665 
666 	return (1);
667 }
668 
669 /*
670  * This is a near direct copy of the inet_ntop() code in
671  * uts/common/inet/ip/ipv6.c, duplicated here for kmdb's sake.
672  */
673 static void
convert2ascii(char * buf,const in6_addr_t * addr)674 convert2ascii(char *buf, const in6_addr_t *addr)
675 {
676 	int		hexdigits;
677 	int		head_zero = 0;
678 	int		tail_zero = 0;
679 	/* tempbuf must be big enough to hold ffff:\0 */
680 	char		tempbuf[6];
681 	char		*ptr;
682 	uint16_t	*addr_component, host_component;
683 	size_t		len;
684 	int		first = FALSE;
685 	int		med_zero = FALSE;
686 	int		end_zero = FALSE;
687 
688 	addr_component = (uint16_t *)addr;
689 	ptr = buf;
690 
691 	/* First count if trailing zeroes higher in number */
692 	for (hexdigits = 0; hexdigits < 8; hexdigits++) {
693 		if (*addr_component == 0) {
694 			if (hexdigits < 4)
695 				head_zero++;
696 			else
697 				tail_zero++;
698 		}
699 		addr_component++;
700 	}
701 	addr_component = (uint16_t *)addr;
702 	if (tail_zero > head_zero && (head_zero + tail_zero) != 7)
703 		end_zero = TRUE;
704 
705 	for (hexdigits = 0; hexdigits < 8; hexdigits++) {
706 		/* if entry is a 0 */
707 		if (*addr_component == 0) {
708 			if (!first && *(addr_component + 1) == 0) {
709 				if (end_zero && (hexdigits < 4)) {
710 					*ptr++ = '0';
711 					*ptr++ = ':';
712 				} else {
713 					if (hexdigits == 0)
714 						*ptr++ = ':';
715 					/* add another */
716 					*ptr++ = ':';
717 					first = TRUE;
718 					med_zero = TRUE;
719 				}
720 			} else if (first && med_zero) {
721 				if (hexdigits == 7)
722 					*ptr++ = ':';
723 				addr_component++;
724 				continue;
725 			} else {
726 				*ptr++ = '0';
727 				*ptr++ = ':';
728 			}
729 			addr_component++;
730 			continue;
731 		}
732 		if (med_zero)
733 			med_zero = FALSE;
734 
735 		tempbuf[0] = '\0';
736 		mdb_nhconvert(&host_component, addr_component,
737 		    sizeof (uint16_t));
738 		(void) mdb_snprintf(tempbuf, sizeof (tempbuf), "%x:",
739 		    host_component & 0xffff);
740 		len = strlen(tempbuf);
741 		bcopy(tempbuf, ptr, len);
742 		ptr = ptr + len;
743 		addr_component++;
744 	}
745 	*--ptr = '\0';
746 }
747 
748 char *
mdb_inet_ntop(int af,const void * addr,char * buf,size_t buflen)749 mdb_inet_ntop(int af, const void *addr, char *buf, size_t buflen)
750 {
751 	in6_addr_t	*v6addr;
752 	uchar_t		*v4addr;
753 	char		*caddr;
754 
755 #define	UC(b)	(((int)b) & 0xff)
756 	switch (af) {
757 	case AF_INET:
758 		ASSERT(buflen >= INET_ADDRSTRLEN);
759 		v4addr = (uchar_t *)addr;
760 		(void) mdb_snprintf(buf, buflen, "%d.%d.%d.%d",
761 		    UC(v4addr[0]), UC(v4addr[1]), UC(v4addr[2]), UC(v4addr[3]));
762 		return (buf);
763 
764 	case AF_INET6:
765 		ASSERT(buflen >= INET6_ADDRSTRLEN);
766 		v6addr = (in6_addr_t *)addr;
767 		if (IN6_IS_ADDR_V4MAPPED(v6addr)) {
768 			caddr = (char *)addr;
769 			(void) mdb_snprintf(buf, buflen, "::ffff:%d.%d.%d.%d",
770 			    UC(caddr[12]), UC(caddr[13]),
771 			    UC(caddr[14]), UC(caddr[15]));
772 		} else if (IN6_IS_ADDR_V4COMPAT(v6addr)) {
773 			caddr = (char *)addr;
774 			(void) mdb_snprintf(buf, buflen, "::%d.%d.%d.%d",
775 			    UC(caddr[12]), UC(caddr[13]), UC(caddr[14]),
776 			    UC(caddr[15]));
777 		} else if (IN6_IS_ADDR_UNSPECIFIED(v6addr)) {
778 			(void) mdb_snprintf(buf, buflen, "::");
779 		} else {
780 			convert2ascii(buf, v6addr);
781 		}
782 		return (buf);
783 	}
784 #undef UC
785 
786 	return (NULL);
787 }
788