* What's new in version 1.8
+- Printf formats can now use "%#c" to escape non-printing characters.
+
- The systemtap compile-server and client now support IPv6 networks.
- IPv6 addresses may now be specified on the --use-server option and will
be displayed by --list-servers, if the avahi-daemon service is running and
}
+
+/*
+ * Output one character into the buffer. Usually this is just a
+ * straight copy, padded left or right up to 'width', but if the user
+ * gave the '#' flag then we need to escape special characters.
+ */
+static char *
+_stp_vsprint_char(char * str, char * end, char c,
+ int width, enum print_flag flags)
+{
+ int size = _stp_vsprint_char_size(c, 0, flags);
+
+ if (!(flags & STP_LEFT)) {
+ while (width-- > size) {
+ if (str <= end)
+ *str = ' ';
+ ++str;
+ }
+ }
+
+ if (size == 1) {
+ if (str <= end)
+ *str = c;
+ ++str;
+ }
+ else {
+ /* Other sizes mean this is not a printable character.
+ * First try to match up C escape characters: */
+ char escape = 0;
+ switch (c) {
+ case '\a':
+ escape = 'a';
+ break;
+ case '\b':
+ escape = 'b';
+ break;
+ case '\f':
+ escape = 'f';
+ break;
+ case '\n':
+ escape = 'n';
+ break;
+ case '\r':
+ escape = 'r';
+ break;
+ case '\t':
+ escape = 't';
+ break;
+ case '\v':
+ escape = 'v';
+ break;
+ case '\'':
+ escape = '\'';
+ break;
+ case '\\':
+ escape = '\\';
+ break;
+ }
+
+ if (str <= end)
+ *str = '\\';
+ ++str;
+ if (escape) {
+ if (str <= end)
+ *str = escape;
+ ++str;
+ }
+ else {
+ /* Fall back to octal for everything else */
+ if (str <= end)
+ *str = to_oct_digit((c >> 6) & 03);
+ ++str;
+ if (str <= end)
+ *str = to_oct_digit((c >> 3) & 07);
+ ++str;
+ if (str <= end)
+ *str = to_oct_digit(c & 07);
+ ++str;
+ }
+ }
+
+ while (width-- > size) {
+ if (str <= end)
+ *str = ' ';
+ ++str;
+ }
+
+ return str;
+}
+
+
+/*
+ * Compute the size of a given character in the buffer. Usually this is
+ * just 1 (padded up to 'width'), but if the user gave the '#' flag then
+ * we need to escape special characters.
+ */
+static int
+_stp_vsprint_char_size(char c, int width, enum print_flag flags)
+{
+ int size = 1;
+
+ /* look for quoteworthy characters */
+ if ((flags & STP_SPECIAL) &&
+ (!(isprint(c) && isascii(c)) || c == '\'' || c == '\\'))
+ switch (c) {
+ case '\a':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ case '\v':
+ case '\'':
+ case '\\':
+ /* backslash and one escape character */
+ size = 2;
+ break;
+ default:
+ /* backslash and three octal digits */
+ size = 4;
+ break;
+ }
+
+ return max(size, width);
+}
+
+
static char *
_stp_vsprint_memory(char * str, char * end, const char * ptr,
int width, int precision,
break;
case 'c':
- if (!(flags & STP_LEFT)) {
- while (--field_width > 0) {
- num_bytes++;
- }
- }
c = (unsigned char) va_arg(args_copy, int);
- num_bytes++;
- while (--field_width > 0) {
- num_bytes++;
- }
+ num_bytes += _stp_vsprint_char_size(c, field_width, flags);
continue;
default:
break;
case 'c':
- if (!(flags & STP_LEFT)) {
- while (--field_width > 0) {
- if (str <= end)
- *str = ' ';
- ++str;
- }
- }
c = (unsigned char) va_arg(args, int);
- if (str <= end)
- *str = c;
- ++str;
- while (--field_width > 0) {
- if (str <= end)
- *str = ' ';
- ++str;
- }
+ str = _stp_vsprint_char(str, end, c, field_width, flags);
continue;
default:
static int number_size(uint64_t num, int base, int size, int precision,
enum print_flag type);
+static char *_stp_vsprint_char(char * str, char * end, char c,
+ int width, enum print_flag flags);
+static int _stp_vsprint_char_size(char c, int width, enum print_flag flags);
+
static char *_stp_vsprint_memory(char * str, char * end, const char * ptr,
int width, int precision,
char format, enum print_flag flags);
Writes a %.
.RE
.PP
+The
+.IR #
+flag selects the alternate forms. For octal, this prefixes a 0. For hex, this
+prefixes 0x or 0X, depending on case. For characters, this escapes
+non-printing values with either C-like escapes or raw octal.
+.PP
Examples:
.SAMPLE
a = "alice", b = "bob", p = 0x1234abcd, i = 123, j = \-1, id[a] = 1234, id[b] = 4567
Prints: 2 byte of kernel buffer at address 0x1234abcd: <binary data>
printf("%4b", p)
Prints (these values as binary data): 0x1234abcd
+ printf("%#o %#x %#X\\n", 1, 2, 3)
+ Prints: 01 0x2 0X3
+ printf("%#c %#c %#c\\n", 0, 9, 42)
+ Prints: \\000 \\t *
.ESAMPLE
.SS STATISTICS
--- /dev/null
+set test "char2"
+set ::result_string {abc-ABC 123!
+\a\b\f\n\r\t\v\'\\
+\000\033\177\222\333\377}
+
+stap_run2 $srcdir/$subdir/$test.stp
+stap_run2 $srcdir/$subdir/$test.stp -DSTP_LEGACY_PRINT
--- /dev/null
+probe begin
+{
+ chars = "abc-ABC 123!"
+ for (i = 0; i < strlen(chars); ++i)
+ printf("%#c", stringat(chars, i))
+ print("\n")
+
+ chars = "\a\b\f\n\r\t\v\'\\"
+ for (i = 0; i < strlen(chars); ++i)
+ printf("%#c", stringat(chars, i))
+ print("\n")
+
+ printf("%#c%#c%#c%#c%#c%#c\n",
+ 0, 033, 0177, 0222, 0333, 0377)
+
+ exit()
+}
break;
case print_format::conv_char:
- o->newline() << "num_bytes += max(width, 1);";
+ o->newline() << "num_bytes += _stp_vsprint_char_size("
+ << value << ", width, " << c->flags << ");";
break;
case print_format::conv_string:
break;
case print_format::conv_char:
- if (c->test_flag(print_format::fmt_flag_left))
- o->newline() << "*str++ = " << value << ";";
- o->newline() << "while (width-- > 1 && str <= end)";
- o->newline(1) << "*str++ = ' ';";
- o->indent(-1);
- if (!c->test_flag(print_format::fmt_flag_left))
- o->newline() << "*str++ = " << value << ";";
+ o->newline() << "str = _stp_vsprint_char(str, end, "
+ << value << ", width, " << c->flags << ");";
break;
case print_format::conv_string: