]> sourceware.org Git - systemtap.git/commitdiff
PR13999: Let "%#c" add escapes for nonprintables
authorJosh Stone <jistone@redhat.com>
Fri, 20 Apr 2012 00:11:14 +0000 (17:11 -0700)
committerJosh Stone <jistone@redhat.com>
Fri, 20 Apr 2012 00:11:14 +0000 (17:11 -0700)
The special '#' flag previously didn't do anything for characters.  Now
it signals that non-printable characters should be escaped in the output
string, either using C shortcuts or octal values.

* runtime/vsprintf.c (_stp_vsprint_char_size, _stp_vsprint_char): New
  functions to size and fill the buffer, accounting for escape chars.
  (_stp_vsnprintf): Use the new char functions.
* translate.cxx (c_unparser::emit_compiled_printfs): Ditto.
* testsuite/systemtap.printf/char2.*: Test %#c
* NEWS, stap.1: Document it.

NEWS
runtime/vsprintf.c
runtime/vsprintf.h
stap.1
testsuite/systemtap.printf/char2.exp [new file with mode: 0644]
testsuite/systemtap.printf/char2.stp [new file with mode: 0644]
translate.cxx

diff --git a/NEWS b/NEWS
index d04ddeb2d23a3393e1591de17adf874ac22f4918..da1f101d1786d99bce39cb2ae468fd7b1e49f7c7 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,7 @@
 * 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
index 18fe176cebedb57c8592516c0b1e8f7b8924bf18..d3ed0574b12fdd0def13d3b7590d341f8487b3da 100644 (file)
@@ -198,6 +198,133 @@ static int number_size(uint64_t num, int base, int size, int precision, enum pri
 
 }
 
+
+/*
+ * 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,
@@ -540,16 +667,8 @@ static int _stp_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
                             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:
@@ -739,22 +858,8 @@ static int _stp_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
                        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:
index 57b5e484b545e7599ec0540f9b615ad2e5724d6c..2f948912f772a35d52ea8122b36980ba93e34e15 100644 (file)
@@ -18,6 +18,10 @@ static char *number(char * buf, char * end, uint64_t num, int base,
 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);
diff --git a/stap.1 b/stap.1
index 8f51e333f9acb964ebabf58db268e6c88c161def..aea093641b11af3e4917028df36f04ed7b86c4da 100644 (file)
--- a/stap.1
+++ b/stap.1
@@ -1112,6 +1112,12 @@ Unsigned hex value, in all upper-case.
 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
@@ -1129,6 +1135,10 @@ Examples:
                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
diff --git a/testsuite/systemtap.printf/char2.exp b/testsuite/systemtap.printf/char2.exp
new file mode 100644 (file)
index 0000000..c91367e
--- /dev/null
@@ -0,0 +1,7 @@
+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
diff --git a/testsuite/systemtap.printf/char2.stp b/testsuite/systemtap.printf/char2.stp
new file mode 100644 (file)
index 0000000..9ccd421
--- /dev/null
@@ -0,0 +1,17 @@
+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()
+}
index 68b8105ac45b5f61834866619f5b1a98ecb4209d..c1e58fa6c7ab4ae25ec6da9985dcbe3422bd41f2 100644 (file)
@@ -1259,7 +1259,8 @@ c_unparser::emit_compiled_printfs ()
                  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:
@@ -1363,13 +1364,8 @@ c_unparser::emit_compiled_printfs ()
              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:
This page took 0.047718 seconds and 5 git commands to generate.