]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
(<inttypes.h>): Include if HAVE_INTTYPES_H.
authorJim Meyering <jim@meyering.net>
Sun, 30 Nov 1997 10:25:16 +0000 (10:25 +0000)
committerJim Meyering <jim@meyering.net>
Sun, 30 Nov 1997 10:25:16 +0000 (10:25 +0000)
("human.h"): Include.
(<limits.h>): Don't include; system.h does it now.
(INT_MAX): Remove.
(longdiff): Remove bogus definition that uses subtraction;
it gives the wrong answer when overflows occur.
(convert_blocks): Remove.
(output_units): New variable;
replaces booleans kilobyte_blocks and megabyte_blocks.
(human_readable_base): New variable.
(long_options, usage): Add -h or --human-readable and -H or --si.
(decode_switches): Adjust to renamed option variables.
Use -H if BLOCKSIZE is SI.  Add -h, -H.
(print_dir): Count blocks using uintmax_t, not int.
(gobble_file): Now returns uintmax_t, not int.
Don't convert blocks to 512 byte units, as this might overflow;
let caller handle the problem.  Deduce what caller will print
by invoking human_readable.
(print_long_format, print_file_name_and_frills): Don't assume
inode number, block count, file size fit in unsigned long.
Use human_readable to do block count conversion and to print
file sizes.
(prep_non_filename_text): Print decimal string for time if
localtime fails due to enormous time_t.

src/ls.c

index 12dab5e24775ecf2181f0c61d92dfc31c1d353f3..19f4405d103f0fa7f78df8d6ab34f8fd2a00bfe3 100644 (file)
--- a/src/ls.c
+++ b/src/ls.c
 #include <config.h>
 #include <sys/types.h>
 
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
 #ifdef HAVE_TERMIOS_H
 # include <termios.h>
 #endif
 #include <pwd.h>
 #include <getopt.h>
 
-#if HAVE_LIMITS_H
-/* limits.h must come before system.h because limits.h on some systems
-   undefs PATH_MAX, whereas system.h includes pathmax.h which sets
-   PATH_MAX.  */
-# include <limits.h>
-#endif
-
 #include "system.h"
 #include <fnmatch.h>
 
 #include "obstack.h"
 #include "ls.h"
 #include "error.h"
+#include "human.h"
 #include "argmatch.h"
 #include "xstrtol.h"
 #include "strverscmp.h"
 #define obstack_chunk_alloc malloc
 #define obstack_chunk_free free
 
-#ifndef INT_MAX
-# define INT_MAX 2147483647
-#endif
-
-/* Return an int indicating the result of comparing two longs. */
-#if (INT_MAX <= 65535)
-# define longdiff(a, b) ((a) < (b) ? -1 : (a) > (b) ? 1 : 0)
-#else
-# define longdiff(a, b) ((a) - (b))
-#endif
-
-/* Convert B 512-byte blocks to kilobytes if K is nonzero,
-   otherwise return it unchanged. */
-#define convert_blocks(b, k) ((k) ? ((b) + 1) / 2 : (b))
+/* Return an int indicating the result of comparing two integers.
+   Subtracting doesn't always work, due to overflow.  */
+#define longdiff(a, b) ((a) < (b) ? -1 : (a) > (b))
 
-/* The maximum number of digits required to print an inode number
-   in an unsigned format.  */
+/* The field width for inode numbers.  On some hosts inode numbers are
+   64 bits, so columns won't line up exactly when a huge inode number
+   is encountered, but in practice 7 digits is usually enough.  */
 #ifndef INODE_DIGITS
 # define INODE_DIGITS 7
 #endif
@@ -181,8 +169,8 @@ static int rev_cmp_version __P ((const struct fileinfo *file2,
                                   const struct fileinfo *file1));
 static int decode_switches __P ((int argc, char **argv));
 static int file_interesting __P ((const struct dirent *next));
-static int gobble_file __P ((const char *name, int explicit_arg,
-                            const char *dirname));
+static uintmax_t gobble_file __P ((const char *name, int explicit_arg,
+                                  const char *dirname));
 static int is_not_dot_or_dotdot __P ((const char *name));
 static void print_color_indicator __P ((const char *name, unsigned int mode,
                                        int linkok));
@@ -327,10 +315,9 @@ static int numeric_ids;
 
 static int print_block_size;
 
-/* Nonzero means show file sizes in kilobytes instead of blocks
-   (the size of which is system-dependent).  -k */
+/* The units to count blocks in. */
 
-static int kilobyte_blocks;
+static int output_units;
 
 /* Precede each line of long output (per file) with a string like `m,n:'
    where M is the number of characters after the `:' and before the
@@ -410,6 +397,10 @@ struct col_ext_type *col_ext_list = NULL;
 /* Buffer for color sequences */
 static char *color_buf;
 
+/* base used for human style output */
+
+static int human_readable_base;
+
 /* Nonzero means mention the inode number of each file.  -i  */
 
 static int print_inode;
@@ -508,6 +499,7 @@ static struct option const long_options[] =
   {"directory", no_argument, 0, 'd'},
   {"dired", no_argument, 0, 'D'},
   {"full-time", no_argument, &full_time, 1},
+  {"human-readable", no_argument, 0, 'h'},
   {"inode", no_argument, 0, 'i'},
   {"kilobytes", no_argument, 0, 'k'},
   {"numeric-uid-gid", no_argument, 0, 'n'},
@@ -520,6 +512,7 @@ static struct option const long_options[] =
   {"ignore-backups", no_argument, 0, 'B'},
   {"classify", no_argument, 0, 'F'},
   {"file-type", no_argument, 0, 'F'},
+  {"si", no_argument, 0, 'H'},
   {"ignore", required_argument, 0, 'I'},
   {"dereference", no_argument, 0, 'L'},
   {"literal", no_argument, 0, 'N'},
@@ -843,7 +836,7 @@ decode_switches (int argc, char **argv)
   sort_reverse = 0;
   numeric_ids = 0;
   print_block_size = 0;
-  kilobyte_blocks = getenv ("POSIXLY_CORRECT") == 0;
+  output_units = getenv ("POSIXLY_CORRECT") ? 512 : 1024;
   indicator_style = none;
   print_inode = 0;
   trace_links = 0;
@@ -854,6 +847,12 @@ decode_switches (int argc, char **argv)
   ignore_patterns = 0;
   quote_as_string = 0;
 
+  if ((p = getenv ("BLOCKSIZE"))
+      && strncmp (p, "HUMAN", sizeof ("HUMAN") - 1) == 0)
+    human_readable_base = 1024;
+  else if (p && strcmp (p, "SI") == 0)
+    human_readable_base = 1000;
+
   line_length = 80;
   if ((p = getenv ("COLUMNS")) && *p)
     {
@@ -898,7 +897,7 @@ decode_switches (int argc, char **argv)
     }
 
   while ((c = getopt_long (argc, argv,
-                          "abcdfgiklmnopqrstuvw:xABCDFGI:LNQRST:UX1",
+                          "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UX1",
                           long_options, NULL)) != -1)
     {
       switch (c)
@@ -941,12 +940,20 @@ decode_switches (int argc, char **argv)
          /* No effect.  For BSD compatibility. */
          break;
 
+       case 'h':
+         human_readable_base = 1024;
+         break;
+
+       case 'H':
+         human_readable_base = 1000;
+         break;
+
        case 'i':
          print_inode = 1;
          break;
 
        case 'k':
-         kilobyte_blocks = 1;
+         output_units = 1024;
          break;
 
        case 'l':
@@ -1505,7 +1512,7 @@ print_dir (const char *name, const char *realname)
 {
   register DIR *reading;
   register struct dirent *next;
-  register int total_blocks = 0;
+  register uintmax_t total_blocks = 0;
 
   errno = 0;
   reading = opendir (name);
@@ -1555,11 +1562,16 @@ print_dir (const char *name, const char *realname)
 
   if (format == long_format || print_block_size)
     {
-      char buf[6 + 20 + 1 + 1];
+      const char *p;
+      char buf[LONGEST_HUMAN_READABLE + 1];
 
       DIRED_INDENT ();
-      sprintf (buf, "total %u\n", total_blocks);
-      FPUTS (buf, stdout, strlen (buf));
+      p = _("total");
+      FPUTS (p, stdout, strlen (p));
+      PUTCHAR (' ');
+      p = human_readable (total_blocks, buf, ST_NBLOCKSIZE, output_units, 0);
+      FPUTS (p, stdout, strlen (p));
+      PUTCHAR ('\n');
     }
 
   if (files_index)
@@ -1629,10 +1641,10 @@ clear_files (void)
    Verify that the file exists, and print an error message if it does not.
    Return the number of blocks that the file occupies.  */
 
-static int
+static uintmax_t
 gobble_file (const char *name, int explicit_arg, const char *dirname)
 {
-  register int blocks;
+  register uintmax_t blocks;
   register int val;
   register char *path;
 
@@ -1741,14 +1753,14 @@ gobble_file (const char *name, int explicit_arg, const char *dirname)
       else
        files[files_index].filetype = normal;
 
-      blocks = convert_blocks (ST_NBLOCKS (files[files_index].stat),
-                              kilobyte_blocks);
-      if (blocks >= 10000 && block_size_size < 5)
-       block_size_size = 5;
-      if (blocks >= 100000 && block_size_size < 6)
-       block_size_size = 6;
-      if (blocks >= 1000000 && block_size_size < 7)
-       block_size_size = 7;
+      blocks = ST_NBLOCKS (files[files_index].stat);
+      {
+       char buf[LONGEST_HUMAN_READABLE + 1];
+       int len = strlen (human_readable (blocks, buf, ST_NBLOCKSIZE,
+                                         output_units, 0));
+       if (block_size_size < len)
+         block_size_size = len < 7 ? len : 7;
+      }
     }
   else
     blocks = 0;
@@ -2091,17 +2103,21 @@ print_long_format (const struct fileinfo *f)
 {
   char modebuf[11];
 
-  /* 7 fields that may (worst case: 64-bit integral values) require 20 bytes,
+  /* 7 fields that may require LONGEST_HUMAN_READABLE bytes,
      1 10-byte mode string,
-     1 24-byte time string (may be longer in some locales -- see below),
+     1 24-byte time string (may be longer in some locales -- see below)
+       or LONGEST_HUMAN_READABLE integer,
      9 spaces, one following each of these fields, and
      1 trailing NUL byte.  */
-  char init_bigbuf[7 * 20 + 10 + 24 + 9 + 1];
+  char init_bigbuf[7 * LONGEST_HUMAN_READABLE + 10
+                  + (LONGEST_HUMAN_READABLE < 24 ? 24 : LONGEST_HUMAN_READABLE)
+                  + 9 + 1];
   char *buf = init_bigbuf;
   size_t bufsize = sizeof (init_bigbuf);
   size_t s;
   char *p;
   time_t when;
+  struct tm *when_local;
   const char *fmt;
   char *user_name;
 
@@ -2153,15 +2169,18 @@ print_long_format (const struct fileinfo *f)
 
   if (print_inode)
     {
-      sprintf (p, "%*lu ", INODE_DIGITS, (unsigned long) f->stat.st_ino);
+      char buf[LONGEST_HUMAN_READABLE + 1];
+      sprintf (p, "%*s ", INODE_DIGITS,
+              human_readable ((uintmax_t) f->stat.st_ino, buf, 1, 1, 0));
       p += strlen (p);
     }
 
   if (print_block_size)
     {
-      sprintf (p, "%*u ", block_size_size,
-              (unsigned) convert_blocks (ST_NBLOCKS (f->stat),
-                                         kilobyte_blocks));
+      char buf[LONGEST_HUMAN_READABLE + 1];
+      sprintf (p, "%*s ", block_size_size,
+              human_readable ((uintmax_t) ST_NBLOCKS (f->stat), buf,
+                              ST_NBLOCKSIZE, output_units, 0));
       p += strlen (p);
     }
 
@@ -2191,25 +2210,53 @@ print_long_format (const struct fileinfo *f)
     sprintf (p, "%3u, %3u ", (unsigned) major (f->stat.st_rdev),
             (unsigned) minor (f->stat.st_rdev));
   else
-    sprintf (p, "%8lu ", (unsigned long) f->stat.st_size);
+    {
+      char buf[LONGEST_HUMAN_READABLE + 1];
+      sprintf (p, "%8s ",
+              human_readable ((uintmax_t) f->stat.st_size,
+                              buf, 1, 1, human_readable_base));
+    }
+
   p += strlen (p);
 
   /* Use strftime rather than ctime, because the former can produce
      locale-dependent names for the weekday (%a) and month (%b).  */
 
-  while (! (s = strftime (p, buf + bufsize - p - 1, fmt, localtime (&when))))
+  if ((when_local = localtime (&when)))
     {
-      char *newbuf = (char *) alloca (bufsize *= 2);
-      memcpy (newbuf, buf, p - buf);
-      p = newbuf + (p - buf);
-      buf = newbuf;
+      while (! (s = strftime (p, buf + bufsize - p - 1, fmt, when_local)))
+       {
+         char *newbuf = (char *) alloca (bufsize *= 2);
+         memcpy (newbuf, buf, p - buf);
+         p = newbuf + (p - buf);
+         buf = newbuf;
+       }
+
+      p += s;
+      *p++ = ' ';
+
+      /* NUL-terminate the string -- fputs (via FPUTS) requires it.  */
+      *p = '\0';
     }
+  else
+    {
+      /* The time cannot be represented as a local time;
+        print it as a huge integer number of seconds.  */
+      char buf[LONGEST_HUMAN_READABLE + 1];
+      int width = full_time ? 24 : 12;
 
-  p += s;
-  *p++ = ' ';
+      if (when < 0)
+       {
+         const char *num = human_readable (- (uintmax_t) when, buf, 1, 1, 0);
+         int sign_width = width - strlen (num);
+         sprintf (p, "%*s%s ", sign_width < 0 ? 0 : sign_width, "-", num);
+       }
+      else
+       sprintf (p, "%*s ", width,
+                human_readable ((uintmax_t) when, buf, 1, 1, 0));
 
-  /* NUL-terminate the string -- fputs (via FPUTS) requires it.  */
-  *p = '\0';
+      p += strlen (p);
+    }
 
   DIRED_INDENT ();
   FPUTS (buf, stdout, p - buf);
@@ -2414,13 +2461,16 @@ prep_non_filename_text (void)
 static void
 print_file_name_and_frills (const struct fileinfo *f)
 {
+  char buf[LONGEST_HUMAN_READABLE + 1];
+
   if (print_inode)
-    printf ("%*lu ", INODE_DIGITS, (unsigned long) f->stat.st_ino);
+    printf ("%*s ", INODE_DIGITS,
+           human_readable ((uintmax_t) f->stat.st_ino, buf, 1, 1, 0));
 
   if (print_block_size)
-    printf ("%*u ", block_size_size,
-           (unsigned) convert_blocks (ST_NBLOCKS (f->stat),
-                                      kilobyte_blocks));
+    printf ("%*s ", block_size_size,
+           human_readable ((uintmax_t) ST_NBLOCKS (f->stat), buf,
+                           ST_NBLOCKSIZE, output_units, 0));
 
   print_name_with_quoting (f->name, f->stat.st_mode, f->linkok);
 
@@ -2914,6 +2964,8 @@ Sort entries alphabetically if none of -cftuSUX nor --sort.\n\
       printf (_("\
   -g                         (ignored)\n\
   -G, --no-group             inhibit display of group information\n\
+  -h, --human-readable  print sizes in human readable format (e.g., 1K 234M 2G)\n\
+  -H, --si                   likewise, but use powers of 1000 not 1024\n\
   -i, --inode                print index number of each file\n\
   -I, --ignore=PATTERN       do not list implied entries matching shell PATTERN\n\
   -k, --kilobytes            use 1024 blocks, not 512 despite POSIXLY_CORRECT\n\