summaryrefslogtreecommitdiff
path: root/src/ls.c
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>1997-11-30 10:25:16 +0000
committerJim Meyering <jim@meyering.net>1997-11-30 10:25:16 +0000
commit9c59f1dcc4da71a09a781d975918d17d1bd78d88 (patch)
tree7a283f871e1ecb7f209cc51696c02a5fa4ba94e3 /src/ls.c
parentd08a2b63dc00a5ee26171abe768956dcf4534228 (diff)
downloadcoreutils-9c59f1dcc4da71a09a781d975918d17d1bd78d88.tar.xz
(<inttypes.h>): Include if HAVE_INTTYPES_H.
("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.
Diffstat (limited to 'src/ls.c')
-rw-r--r--src/ls.c184
1 files changed, 118 insertions, 66 deletions
diff --git a/src/ls.c b/src/ls.c
index 12dab5e24..19f4405d1 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -43,6 +43,10 @@
#include <config.h>
#include <sys/types.h>
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
#ifdef HAVE_TERMIOS_H
# include <termios.h>
#endif
@@ -56,19 +60,13 @@
#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"
@@ -76,23 +74,13 @@
#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\