bug-coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

reverting `stat --format=FMT'


From: Jim Meyering
Subject: reverting `stat --format=FMT'
Date: Thu, 15 Dec 2005 13:40:45 +0100

As planned, here's the change to revert stat --format=FMT to
its previous behavior.  Note that with this change, backslash
escapes in a --format-specified format string are *not* interpreted.

I'm planning to put the exact same patch on the b5 branch
(i.e., for coreutils-5.94), so if you have any objections,
raise them now.

NEWS:

** Changes in behavior

  ...

  stat's --format=FMT option now works the way it did before 5.3.0:
  FMT is automatically newline terminated.  The first stable release
  containing this change was 5.92.

  stat accepts the new option --printf=FMT, where FMT is *not*
  automatically newline terminated.
  works, backslash escapes in FMT *are* interpreted.

  stat: backslash escapes are interpreted in a format string specified
  via --printf=FMT, but not one specified via --format=FMT.  That includes
  octal (\ooo, at most three octal digits), hexadecimal (\xhh, one or
  two hex digits), and the standard sequences (\a, \b, \f, \n, \r, \t,
  \v, \", \\).


2005-12-15  Jim Meyering  <address@hidden>

        stat: revert behavior of --format=FMT (-c)
        stat: add new option: --printf=FMT
        * NEWS: Mention this.
        * src/stat.c (isodigit, octtobin, hextobin): Define.
        (PRINTF_OPTION): Define.
        (usage): Document them.  Alphabetize on long option names.
        (interpret_backslash_escapes, trailing_delim): New globals.
        (print_esc_char): New function.
        (print_it): Rewrite, in order to handle backslash escapes.
        (main): Handle new option.  Set globals for --format, too.

        * tests/misc/stat-printf: Test --printf and --format.
        * tests/misc/Makefile.am (TESTS): Add stat-printf.


2005-12-15  Jim Meyering  <address@hidden>

        * coreutils.texi (stat invocation) [--printf]: Describe new option.
        [--format]: Add example.  Distinguish from --printf.
        Sort option descriptions.


Index: src/stat.c
===================================================================
RCS file: /fetish/cu/src/stat.c,v
retrieving revision 1.89
retrieving revision 1.90
diff -u -p -u -r1.89 -r1.90
--- src/stat.c  15 Oct 2005 10:15:48 -0000      1.89
+++ src/stat.c  15 Dec 2005 12:24:30 -0000      1.90
@@ -96,15 +96,27 @@
 # endif
 #endif
 
+/* FIXME: these are used by printf.c, too */
+#define isodigit(c) ('0' <= (c) && (c) <= '7')
+#define octtobin(c) ((c) - '0')
+#define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
+                    (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
+
 #define PROGRAM_NAME "stat"
 
 #define AUTHORS "Michael Meskes"
 
+enum
+{
+  PRINTF_OPTION = CHAR_MAX + 1,
+};
+
 static struct option const long_options[] = {
   {"dereference", no_argument, NULL, 'L'},
   {"file-system", no_argument, NULL, 'f'},
   {"filesystem", no_argument, NULL, 'f'}, /* obsolete and undocumented alias */
   {"format", required_argument, NULL, 'c'},
+  {"printf", required_argument, NULL, PRINTF_OPTION},
   {"terse", no_argument, NULL, 't'},
   {GETOPT_HELP_OPTION_DECL},
   {GETOPT_VERSION_OPTION_DECL},
@@ -113,6 +125,14 @@ static struct option const long_options[
 
 char *program_name;
 
+/* Whether to interpret backslash-escape sequences.
+   True for --printf=FMT, not for --format=FMT (-c).  */
+static bool interpret_backslash_escapes;
+
+/* The trailing delimiter string:
+   "" for --printf=FMT, "\n" for --format=FMT (-c).  */
+static char const *trailing_delim = "";
+
 /* Return the type of the specified file system.
    Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris)
    Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2)
@@ -535,59 +555,130 @@ print_stat (char *pformat, size_t buf_le
     }
 }
 
+/* Output a single-character \ escape.  */
+
+static void
+print_esc_char (char c)
+{
+  switch (c)
+    {
+    case 'a':                  /* Alert. */
+      c ='\a';
+      break;
+    case 'b':                  /* Backspace. */
+      c ='\b';
+      break;
+    case 'f':                  /* Form feed. */
+      c ='\f';
+      break;
+    case 'n':                  /* New line. */
+      c ='\n';
+      break;
+    case 'r':                  /* Carriage return. */
+      c ='\r';
+      break;
+    case 't':                  /* Horizontal tab. */
+      c ='\t';
+      break;
+    case 'v':                  /* Vertical tab. */
+      c ='\v';
+      break;
+    case '"':
+    case '\\':
+      break;
+    default:
+      error (0, 0, _("warning: unrecognized escape `\\%c'"), c);
+      break;
+    }
+  putchar (c);
+}
+
 static void
-print_it (char const *masterformat, char const *filename,
+print_it (char const *format, char const *filename,
          void (*print_func) (char *, size_t, char, char const *, void const *),
          void const *data)
 {
-  char *b;
-
-  /* create a working copy of the format string */
-  char *format = xstrdup (masterformat);
-
   /* Add 2 to accommodate our conversion of the stat `%s' format string
-     to the printf `%llu' one.  */
+     to the longer printf `%llu' one.  */
   size_t n_alloc = strlen (format) + 2 + 1;
   char *dest = xmalloc (n_alloc);
-
-  b = format;
-  while (b)
+  char const *b;
+  for (b = format; *b; b++)
     {
-      char *p = strchr (b, '%');
-      if (p != NULL)
+      switch (*b)
        {
-         size_t len;
-         *p++ = '\0';
-         fputs (b, stdout);
-
-         len = strspn (p, "#-+.I 0123456789");
-         dest[0] = '%';
-         memcpy (dest + 1, p, len);
-         dest[1 + len] = 0;
-         p += len;
+       case '%':
+         {
+           size_t len = strspn (b + 1, "#-+.I 0123456789");
+           char const *fmt_char = b + 1 + len;
+           memcpy (dest, b, 1 + len);
+           dest[1 + len] = 0;
+
+           b = fmt_char;
+           switch (*fmt_char)
+             {
+             case '\0':
+               --b;
+               /* fall through */
+             case '%':
+               if (0 < len)
+                 error (EXIT_FAILURE, 0, _("%s%s: invalid directive"),
+                        quotearg_colon (dest), *fmt_char ? "%" : "");
+               putchar ('%');
+               break;
+             default:
+               print_func (dest, n_alloc, *fmt_char, filename, data);
+               break;
+             }
+           break;
+         }
 
-         b = p + 1;
-         switch (*p)
+       case '\\':
+         if ( ! interpret_backslash_escapes)
            {
-           case '\0':
-             b = NULL;
-             /* fall through */
-           case '%':
-             putchar ('%');
-             break;
-           default:
-             print_func (dest, n_alloc, *p, filename, data);
+             putchar ('\\');
              break;
            }
-       }
-      else
-       {
-         fputs (b, stdout);
-         b = NULL;
+         ++b;
+         if (isodigit (*b))
+           {
+             int esc_value = octtobin (*b);
+             int esc_length = 1;       /* number of octal digits */
+             for (++b; esc_length < 3 && isodigit (*b);
+                  ++esc_length, ++b)
+               {
+                 esc_value = esc_value * 8 + octtobin (*b);
+               }
+             putchar (esc_value);
+             --b;
+           }
+         else if (*b == 'x' && ISXDIGIT (b[1]))
+           {
+             int esc_value = hextobin (b[1]);  /* Value of \xhh escape. */
+             /* A hexadecimal \xhh escape sequence must have
+                1 or 2 hex. digits.  */
+             ++b;
+             if (ISXDIGIT (b[1]))
+               {
+                 ++b;
+                 esc_value = esc_value * 16 + hextobin (*b);
+               }
+             putchar (esc_value);
+           }
+         else
+           {
+             print_esc_char (*b);
+           }
+         break;
+
+       default:
+         putchar (*b);
+         break;
        }
     }
-  free (format);
   free (dest);
+
+  fputs (trailing_delim, stdout);
 }
 
 /* Stat the file system and print what we find.  */
@@ -678,9 +769,15 @@ usage (int status)
       fputs (_("\
 Display file or file system status.\n\
 \n\
-  -f, --file-system     display file system status instead of file status\n\
-  -c  --format=FORMAT   use the specified FORMAT instead of the default\n\
   -L, --dereference     follow links\n\
+  -f, --file-system     display file system status instead of file status\n\
+"), stdout);
+      fputs (_("\
+  -c  --format=FORMAT   use the specified FORMAT instead of the default;\n\
+                          output a newline after each use of FORMAT\n\
+      --printf=FORMAT   like --format, but interpret backslash escapes,\n\
+                          and do not output a mandatory trailing newline.\n\
+                          If you want a newline, include \\n in FORMAT.\n\
   -t, --terse           print the information in terse form\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
@@ -771,8 +868,16 @@ main (int argc, char *argv[])
     {
       switch (c)
        {
+       case PRINTF_OPTION:
+         format = optarg;
+         interpret_backslash_escapes = true;
+         trailing_delim = "";
+         break;
+
        case 'c':
          format = optarg;
+         interpret_backslash_escapes = false;
+         trailing_delim = "\n";
          break;
 
        case 'L':
Index: doc/coreutils.texi
===================================================================
RCS file: /fetish/cu/doc/coreutils.texi,v
retrieving revision 1.300
diff -u -p -r1.300 coreutils.texi
--- doc/coreutils.texi  12 Dec 2005 22:42:16 -0000      1.300
+++ doc/coreutils.texi  15 Dec 2005 08:41:52 -0000
@@ -9277,14 +9277,6 @@ also give information about the files th
 
 @table @samp
 
address@hidden -f
address@hidden --file-system
address@hidden -f
address@hidden --file-system
address@hidden file systems
-Report information about the file systems where the given files are located
-instead of information about the files themselves.
-
 @item -L
 @itemx --dereference
 @opindex -L
@@ -9295,12 +9287,13 @@ With this option, @command{stat} acts on
 by each symbolic link argument.
 Without it, @command{stat} acts on any symbolic link argument directly.
 
address@hidden -t
address@hidden --terse
address@hidden -t
address@hidden --terse
address@hidden terse output
-Print the information in terse form, suitable for parsing by other programs.
address@hidden -f
address@hidden --file-system
address@hidden -f
address@hidden --file-system
address@hidden file systems
+Report information about the file systems where the given files are located
+instead of information about the files themselves.
 
 @item -c
 @itemx address@hidden
@@ -9308,6 +9301,36 @@ Print the information in terse form, sui
 @opindex address@hidden
 @cindex output format
 Use @var{format} rather than the default format.
address@hidden is automatically newline-terminated, so
+running a command like the following with two or more @var{file}
+operands produces a line of output for each operand:
address@hidden
+$ stat --format=%d:%i / /usr
+2050:2
+2057:2
address@hidden example
+
address@hidden address@hidden
address@hidden address@hidden
address@hidden output format
+Use @var{format} rather than the default format.
+Like like @option{--format}, but interpret backslash escapes,
+and do not output a mandatory trailing newline.
+If you want a newline, include @samp{\n} in the @var{format}.
+Here's how you would use @option{--printf} to print the device
+and inode numbers of @file{/} and @file{/usr}:
address@hidden
+$ stat --printf='%d:%i\n' / /usr
+2050:2
+2057:2
address@hidden example
+
address@hidden -t
address@hidden --terse
address@hidden -t
address@hidden --terse
address@hidden terse output
+Print the information in terse form, suitable for parsing by other programs.
 
 The valid format sequences for files are:
 
Index: tests/misc/stat-printf
===================================================================
RCS file: tests/misc/stat-printf
diff -N tests/misc/stat-printf
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/misc/stat-printf      15 Dec 2005 12:23:36 -0000      1.1
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+: ${PERL=perl}
+: ${srcdir=.}
+
+$PERL -e 1 > /dev/null 2>&1 || {
+  echo 1>&2 "$0: configure didn't find a usable version of Perl," \
+    "so can't run this test"
+  exit 77
+}
+
+exec $PERL -w -I$srcdir/.. -MCoreutils -- - <<\EOF
+require 5.003;
+use strict;
+
+(my $ME = $0) =~ s|.*/||;
+my $prog = 'stat';
+
+# Turn off localisation of executable's ouput.
address@hidden(LANGUAGE LANG LC_ALL)} = ('C') x 3;
+
+my @Tests =
+    (
+     # test-name, [option, option, ...] {OUT=>"expected-output"}
+     #
+     ['nl', q!--printf='\n' .!,          {OUT=>"\n"}],
+     ['no-nl', "--printf=%n .",          {OUT=>"."}],
+     ['pct-and-esc', q!--printf='\0%n\0' .!,    {OUT=>"\0.\0"}],
+     ['backslash', q!--printf='\\\\' .!, {OUT=>"\\"}],
+     ['nul', q!--printf='\0' .!,         {OUT=>"\0"}],
+     # Don't bother testing \v, since Perl doesn't handle it.
+     ['bel-etc', q!--printf='\a\b\f\n\r\t' .!, {OUT=>"\a\b\f\n\r\t"}],
+     ['octal-1', q!--printf='\012\377' .!,     {OUT=>"\012\377"}],
+     ['octal-2', q!--printf='.\012a\377b' .!,  {OUT=>".\012a\377b"}],
+     ['hex-1',   q!--printf='\x34\xf' .!,      {OUT=>"\x34\xf"}],
+     ['hex-2',   q!--printf='.\x18p\xfq' .!,   {OUT=>".\x18p\x0fq"}],
+     ['hex-3',   q!--printf='\x' .!,           {OUT=>'x'},
+        {ERR=>"$prog: warning: unrecognized escape `\\x'\n"}],
+
+     # With --format, there *is* a trailing newline.
+     ['f-nl', "--format=%n .",          {OUT=>".\n"}],
+     ['f-nl2', "--format=%n . .",       {OUT=>".\n.\n"}],
+
+     ['end-pct', "--printf=% .",       {OUT=>"%"}],
+     ['pct-pct', "--printf=%% .",      {OUT=>"%"}],
+
+     ['err-1', "--printf=%9% .",       {EXIT => 1},
+        {ERR=>"$prog: %9%: invalid directive\n"}],
+     ['err-2', "--printf=%9 .",        {EXIT => 1},
+        {ERR=>"$prog: %9: invalid directive\n"}],
+    );
+
+my $save_temps = $ENV{DEBUG};
+my $verbose = $ENV{VERBOSE};
+
+my $fail = run_tests ($ME, $prog, address@hidden, $save_temps, $verbose);
+exit $fail;
+EOF
Index: tests/misc/Makefile.am
===================================================================
RCS file: /fetish/cu/tests/misc/Makefile.am,v
retrieving revision 1.32
diff -u -p -r1.32 Makefile.am
--- tests/misc/Makefile.am      10 Dec 2005 09:38:07 -0000      1.32
+++ tests/misc/Makefile.am      15 Dec 2005 08:39:32 -0000
@@ -12,6 +12,7 @@ TESTS_ENVIRONMENT = \
   PROG=$$tst
 
 TESTS = \
+  stat-printf \
   sort-rand \
   sha224sum \
   sha256sum \




reply via email to

[Prev in Thread] Current Thread [Next in Thread]