summaryrefslogtreecommitdiff
path: root/sysutils/coreutils
diff options
context:
space:
mode:
authorrecht <recht@pkgsrc.org>2003-11-05 00:05:06 +0000
committerrecht <recht@pkgsrc.org>2003-11-05 00:05:06 +0000
commit0c58bd172625d0c32dc11625798f631b90bb5f91 (patch)
treeeaaa5f164a6a5266127c3c08f0c2f98c86e0c711 /sysutils/coreutils
parent330109670ce1214ffb976303a849b1903e987e32 (diff)
downloadpkgsrc-0c58bd172625d0c32dc11625798f631b90bb5f91.tar.gz
Fix two security issues:
1.) An integer overflow in ls in the fileutils or coreutils packages may allow local users to cause a denial of service or execute arbitrary code via a large -w value, which could be remotely exploited via applications that use ls, such as wu-ftpd. 2.) ls in the fileutils or coreutils packages allows local users to consume a large amount of memory via a large -w value, which can be remotely exploited via applications that use ls, such as wu-ftpd. See http://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2003-0853 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2003-0854 and the original report http://www.guninski.com/binls.html for details. Patches taken from Red Hat's Security Advisory RHSA-2003:309-01. reported by reed@ bump PKGREVISION
Diffstat (limited to 'sysutils/coreutils')
-rw-r--r--sysutils/coreutils/Makefile4
-rw-r--r--sysutils/coreutils/distinfo4
-rw-r--r--sysutils/coreutils/patches/patch-ab132
-rw-r--r--sysutils/coreutils/patches/patch-ac764
4 files changed, 901 insertions, 3 deletions
diff --git a/sysutils/coreutils/Makefile b/sysutils/coreutils/Makefile
index 83652246564..031704908c4 100644
--- a/sysutils/coreutils/Makefile
+++ b/sysutils/coreutils/Makefile
@@ -1,7 +1,7 @@
-# $NetBSD: Makefile,v 1.11 2003/09/14 18:13:51 recht Exp $
+# $NetBSD: Makefile,v 1.12 2003/11/05 00:05:06 recht Exp $
DISTNAME= coreutils-5.0
-PKGREVISION= 2
+PKGREVISION= 3
CATEGORIES= sysutils
MASTER_SITES= ${MASTER_SITE_GNU:=coreutils/}
EXTRACT_SUFX= .tar.bz2
diff --git a/sysutils/coreutils/distinfo b/sysutils/coreutils/distinfo
index 3a5bc68872a..c9840889287 100644
--- a/sysutils/coreutils/distinfo
+++ b/sysutils/coreutils/distinfo
@@ -1,5 +1,7 @@
-$NetBSD: distinfo,v 1.1.1.1 2003/04/10 13:18:36 wiz Exp $
+$NetBSD: distinfo,v 1.2 2003/11/05 00:05:06 recht Exp $
SHA1 (coreutils-5.0.tar.bz2) = ce67aacedfc917a92b5be62dd32095393c2f220c
Size (coreutils-5.0.tar.bz2) = 3952653 bytes
SHA1 (patch-aa) = 352b6b8eeff29159ebdbae4929db75d243a19354
+SHA1 (patch-ab) = 8cc6bbef46bdaf163129b06bf65ec2b775c57fe2
+SHA1 (patch-ac) = 761ba2182a191ca215f032228e678c8f0f5549be
diff --git a/sysutils/coreutils/patches/patch-ab b/sysutils/coreutils/patches/patch-ab
new file mode 100644
index 00000000000..53bb7a93b69
--- /dev/null
+++ b/sysutils/coreutils/patches/patch-ab
@@ -0,0 +1,132 @@
+$NetBSD: patch-ab,v 1.1 2003/11/05 00:05:06 recht Exp $
+
+--- lib/xmalloc.c~ 2002-11-21 21:39:59.000000000 +0100
++++ lib/xmalloc.c 2003-11-05 00:26:39.000000000 +0100
+@@ -22,7 +22,9 @@
+ #include <sys/types.h>
+
+ #if STDC_HEADERS
++# include <stdbool.h>
+ # include <stdlib.h>
++# include <string.h>
+ #else
+ void *calloc ();
+ void *malloc ();
+@@ -43,6 +45,10 @@
+
+ /* The following tests require AC_PREREQ(2.54). */
+
++#ifndef SIZE_MAX
++# define SIZE_MAX ((size_t) -1)
++#endif
++
+ #ifndef HAVE_MALLOC
+ "you must run the autoconf test for a GNU libc compatible malloc"
+ #endif
+@@ -58,6 +64,15 @@
+ /* If non NULL, call this function when memory is exhausted. */
+ void (*xalloc_fail_func) PARAMS ((void)) = 0;
+
++/* Return true if array of N objects, each of size S, cannot exist due
++ to arithmetic overflow. S must be nonzero. */
++
++static inline bool
++array_size_overflow (size_t n, size_t s)
++{
++ return SIZE_MAX / s < n;
++}
++
+ /* If XALLOC_FAIL_FUNC is NULL, or does return, display this message
+ before exiting when memory is exhausted. Goes through gettext. */
+ char const xalloc_msg_memory_exhausted[] = N_("memory exhausted");
+@@ -70,8 +85,20 @@
+ error (xalloc_exit_failure, 0, "%s", _(xalloc_msg_memory_exhausted));
+ /* The `noreturn' cannot be given to error, since it may return if
+ its first argument is 0. To help compilers understand the
+- xalloc_die does terminate, call exit. */
+- exit (EXIT_FAILURE);
++ xalloc_die does terminate, call abort. */
++ abort ();
++}
++
++/* Allocate an array of N objects, each with S bytes of memory,
++ dynamically, with error checking. S must be nonzero. */
++
++inline void *
++xnmalloc (size_t n, size_t s)
++{
++ void *p;
++ if (array_size_overflow (n, s) || ! (p = malloc (n * s)))
++ xalloc_die ();
++ return p;
+ }
+
+ /* Allocate N bytes of memory dynamically, with error checking. */
+@@ -79,10 +106,16 @@
+ void *
+ xmalloc (size_t n)
+ {
+- void *p;
++ return xnmalloc (n, 1);
++}
+
+- p = malloc (n);
+- if (p == 0)
++/* Change the size of an allocated block of memory P to an array of N
++ objects each of S bytes, with error checking. S must be nonzero. */
++
++inline void *
++xnrealloc (void *p, size_t n, size_t s)
++{
++ if (array_size_overflow (n, s) || ! (p = realloc (p, n * s)))
+ xalloc_die ();
+ return p;
+ }
+@@ -93,21 +126,39 @@
+ void *
+ xrealloc (void *p, size_t n)
+ {
+- p = realloc (p, n);
+- if (p == 0)
+- xalloc_die ();
+- return p;
++ return xnrealloc (p, n, 1);
+ }
+
+-/* Allocate memory for N elements of S bytes, with error checking. */
++/* Allocate S bytes of zeroed memory dynamically, with error checking.
++ There's no need for xnzalloc (N, S), since it would be equivalent
++ to xcalloc (N, S). */
++
++void *
++xzalloc (size_t s)
++{
++ return memset (xmalloc (s), 0, s);
++}
++
++/* Allocate zeroed memory for N elements of S bytes, with error
++ checking. S must be nonzero. */
+
+ void *
+ xcalloc (size_t n, size_t s)
+ {
+ void *p;
+-
+- p = calloc (n, s);
+- if (p == 0)
++ /* Test for overflow, since some calloc implementations don't have
++ proper overflow checks. */
++ if (array_size_overflow (n, s) || ! (p = calloc (n, s)))
+ xalloc_die ();
+ return p;
+ }
++
++/* Clone an object P of size S, with error checking. There's no need
++ for xnclone (P, N, S), since xclone (P, N * S) works without any
++ need for an arithmetic overflow check. */
++
++void *
++xclone (void const *p, size_t s)
++{
++ return memcpy (xmalloc (s), p, s);
++}
diff --git a/sysutils/coreutils/patches/patch-ac b/sysutils/coreutils/patches/patch-ac
new file mode 100644
index 00000000000..580cd0c6768
--- /dev/null
+++ b/sysutils/coreutils/patches/patch-ac
@@ -0,0 +1,764 @@
+$NetBSD: patch-ac,v 1.1 2003/11/05 00:05:06 recht Exp $
+
+--- src/ls.c~ 2003-03-20 00:01:51.000000000 +0100
++++ src/ls.c 2003-11-05 00:34:17.000000000 +0100
+@@ -243,7 +243,7 @@
+
+ struct bin_str
+ {
+- int len; /* Number of bytes */
++ size_t len; /* Number of bytes */
+ const char *string; /* Pointer to the same */
+ };
+
+@@ -265,15 +265,15 @@
+ static void print_color_indicator (const char *name, mode_t mode, int linkok);
+ static void put_indicator (const struct bin_str *ind);
+ static int put_indicator_direct (const struct bin_str *ind);
+-static int length_of_file_name_and_frills (const struct fileinfo *f);
++static size_t length_of_file_name_and_frills (const struct fileinfo *f);
+ static void add_ignore_pattern (const char *pattern);
+ static void attach (char *dest, const char *dirname, const char *name);
+ static void clear_files (void);
+ static void extract_dirs_from_files (const char *dirname,
+ int ignore_dot_and_dot_dot);
+ static void get_link_name (const char *filename, struct fileinfo *f);
+-static void indent (int from, int to);
+-static void init_column_info (void);
++static void indent (size_t from, size_t to);
++static size_t calculate_columns (bool by_columns);
+ static void print_current_files (void);
+ static void print_dir (const char *name, const char *realname);
+ static void print_file_name_and_frills (const struct fileinfo *f);
+@@ -319,10 +319,10 @@
+ static struct fileinfo *files; /* FIXME: rename this to e.g. cwd_file */
+
+ /* Length of block that `files' points to, measured in files. */
+-static int nfiles; /* FIXME: rename this to e.g. cwd_n_alloc */
++static size_t nfiles; /* FIXME: rename this to e.g. cwd_n_alloc */
+
+ /* Index of first unused in `files'. */
+-static int files_index; /* FIXME: rename this to e.g. cwd_n_used */
++static size_t files_index; /* FIXME: rename this to e.g. cwd_n_used */
+
+ /* When nonzero, in a color listing, color each symlink name according to the
+ type of file it points to. Otherwise, color them according to the `ln'
+@@ -632,7 +632,7 @@
+
+ /* The number of chars per hardware tab stop. Setting this to zero
+ inhibits the use of TAB characters for separating columns. -T */
+-static int tabsize;
++static size_t tabsize;
+
+ /* Nonzero means we are listing the working directory because no
+ non-option arguments were given. */
+@@ -646,7 +646,7 @@
+ /* The line length to use for breaking lines in many-per-line format.
+ Can be set with -w. */
+
+-static int line_length;
++static size_t line_length;
+
+ /* If nonzero, the file listing format requires that stat be called on
+ each file. */
+@@ -799,16 +799,16 @@
+ /* Information about filling a column. */
+ struct column_info
+ {
+- int valid_len;
+- int line_len;
+- int *col_arr;
++ bool valid_len;
++ size_t line_len;
++ size_t *col_arr;
+ };
+
+ /* Array with information about column filledness. */
+ static struct column_info *column_info;
+
+ /* Maximum number of columns ever possible for this display. */
+-static int max_idx;
++static size_t max_idx;
+
+ /* The minimum width of a colum is 3: 1 character for the name and 2
+ for the separating white space. */
+@@ -904,18 +904,18 @@
+ static void
+ dired_dump_obstack (const char *prefix, struct obstack *os)
+ {
+- int n_pos;
++ size_t n_pos;
+
+ n_pos = obstack_object_size (os) / sizeof (dired_pos);
+ if (n_pos > 0)
+ {
+- int i;
++ size_t i;
+ size_t *pos;
+
+ pos = (size_t *) obstack_finish (os);
+ fputs (prefix, stdout);
+ for (i = 0; i < n_pos; i++)
+- printf (" %lu", (unsigned long) pos[i]);
++ printf (" %lu", (unsigned long int) pos[i]);
+ putchar ('\n');
+ }
+ }
+@@ -952,7 +952,7 @@
+ struct dev_ino *ent_from_table;
+ int found_match;
+
+- ent = XMALLOC (struct dev_ino, 1);
++ ent = xmalloc (sizeof *ent);
+ ent->st_ino = ino;
+ ent->st_dev = dev;
+
+@@ -1134,7 +1134,7 @@
+ }
+
+ nfiles = 100;
+- files = XMALLOC (struct fileinfo, nfiles);
++ files = xnmalloc (nfiles, sizeof *files);
+ files_index = 0;
+
+ clear_files ();
+@@ -1322,11 +1322,11 @@
+ char const *p = getenv ("COLUMNS");
+ if (p && *p)
+ {
+- long int tmp_long;
+- if (xstrtol (p, NULL, 0, &tmp_long, NULL) == LONGINT_OK
+- && 0 < tmp_long && tmp_long <= INT_MAX)
++ unsigned long int tmp_ulong;
++ if (xstrtoul (p, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
++ && 0 < tmp_ulong && tmp_ulong <= SIZE_MAX)
+ {
+- line_length = (int) tmp_long;
++ line_length = tmp_ulong;
+ }
+ else
+ {
+@@ -1341,7 +1341,8 @@
+ {
+ struct winsize ws;
+
+- if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col != 0)
++ if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1
++ && 0 < ws.ws_col && ws.ws_col <= SIZE_MAX)
+ line_length = ws.ws_col;
+ }
+ #endif
+@@ -1353,11 +1354,11 @@
+ tabsize = 8;
+ if (!getenv ("POSIXLY_CORRECT") && (p = getenv ("TABSIZE")))
+ {
+- long int tmp_long;
+- if (xstrtol (p, NULL, 0, &tmp_long, NULL) == LONGINT_OK
+- && 0 <= tmp_long && tmp_long <= INT_MAX)
++ unsigned long int tmp_ulong;
++ if (xstrtoul (p, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
++ && tmp_ulong <= SIZE_MAX)
+ {
+- tabsize = (int) tmp_long;
++ tabsize = tmp_ulong;
+ }
+ else
+ {
+@@ -1476,12 +1477,12 @@
+
+ case 'w':
+ {
+- long int tmp_long;
+- if (xstrtol (optarg, NULL, 0, &tmp_long, NULL) != LONGINT_OK
+- || tmp_long <= 0 || tmp_long > INT_MAX)
++ unsigned long int tmp_ulong;
++ if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
++ || ! (0 < tmp_ulong && tmp_ulong <= SIZE_MAX))
+ error (EXIT_FAILURE, 0, _("invalid line width: %s"),
+ quotearg (optarg));
+- line_length = (int) tmp_long;
++ line_length = tmp_ulong;
+ break;
+ }
+
+@@ -1550,12 +1551,12 @@
+
+ case 'T':
+ {
+- long int tmp_long;
+- if (xstrtol (optarg, NULL, 0, &tmp_long, NULL) != LONGINT_OK
+- || tmp_long < 0 || tmp_long > INT_MAX)
++ unsigned long int tmp_ulong;
++ if (xstrtoul (optarg, NULL, 0, &tmp_ulong, NULL) != LONGINT_OK
++ || SIZE_MAX < tmp_ulong)
+ error (EXIT_FAILURE, 0, _("invalid tab size: %s"),
+ quotearg (optarg));
+- tabsize = (int) tmp_long;
++ tabsize = tmp_ulong;
+ break;
+ }
+
+@@ -1661,6 +1662,8 @@
+ }
+ }
+
++ max_idx = MAX (1, line_length / MIN_COLUMN_WIDTH);
++
+ filename_quoting_options = clone_quoting_options (NULL);
+ if (get_quoting_style (filename_quoting_options) == escape_quoting_style)
+ set_char_quoting (filename_quoting_options, ' ', 1);
+@@ -1762,7 +1765,8 @@
+ /* Parse a string as part of the LS_COLORS variable; this may involve
+ decoding all kinds of escape characters. If equals_end is set an
+ unescaped equal sign ends the string, otherwise only a : or \0
+- does. Returns the number of characters output, or -1 on failure.
++ does. Set *OUTPUT_COUNT to the number of bytes output. Return
++ true if successful.
+
+ The resulting string is *not* null-terminated, but may contain
+ embedded nulls.
+@@ -1771,11 +1775,12 @@
+ the first free byte after the array and the character that ended
+ the input string, respectively. */
+
+-static int
+-get_funky_string (char **dest, const char **src, int equals_end)
++static bool
++get_funky_string (char **dest, const char **src, bool equals_end,
++ size_t *output_count)
+ {
+ int num; /* For numerical codes */
+- int count; /* Something to count with */
++ size_t count; /* Something to count with */
+ enum {
+ ST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX, ST_CARET, ST_END, ST_ERROR
+ } state;
+@@ -1960,8 +1965,9 @@
+
+ *dest = q;
+ *src = p;
++ *output_count = count;
+
+- return state == ST_ERROR ? -1 : count;
++ return state != ST_ERROR;
+ }
+
+ static void
+@@ -2004,15 +2010,15 @@
+ override an earlier one, which can be useful for
+ having terminal-specific defs override global). */
+
+- ext = XMALLOC (struct color_ext_type, 1);
++ ext = xmalloc (sizeof *ext);
+ ext->next = color_ext_list;
+ color_ext_list = ext;
+
+ ++p;
+ ext->ext.string = buf;
+
+- state = (ext->ext.len =
+- get_funky_string (&buf, &p, 1)) < 0 ? -1 : 4;
++ state = (get_funky_string (&buf, &p, true, &ext->ext.len)
++ ? 4 : -1);
+ break;
+
+ case '\0':
+@@ -2045,8 +2051,9 @@
+ if (STREQ (label, indicator_name[ind_no]))
+ {
+ color_indicator[ind_no].string = buf;
+- state = ((color_indicator[ind_no].len =
+- get_funky_string (&buf, &p, 0)) < 0 ? -1 : 1);
++ state = (get_funky_string (&buf, &p, false,
++ &color_indicator[ind_no].len)
++ ? 1 : -1);
+ break;
+ }
+ }
+@@ -2059,8 +2066,8 @@
+ if (*(p++) == '=')
+ {
+ ext->seq.string = buf;
+- state = (ext->seq.len =
+- get_funky_string (&buf, &p, 0)) < 0 ? -1 : 1;
++ state = (get_funky_string (&buf, &p, false, &ext->seq.len)
++ ? 1 : -1);
+ }
+ else
+ state = -1;
+@@ -2104,7 +2111,7 @@
+ {
+ struct pending *new;
+
+- new = XMALLOC (struct pending, 1);
++ new = xmalloc (sizeof *new);
+ new->realname = realname ? xstrdup (realname) : NULL;
+ new->name = name ? xstrdup (name) : NULL;
+ new->next = pending_dirs;
+@@ -2259,7 +2266,7 @@
+ {
+ register struct ignore_pattern *ignore;
+
+- ignore = XMALLOC (struct ignore_pattern, 1);
++ ignore = xmalloc (sizeof *ignore);
+ ignore->pattern = pattern;
+ /* Add it to the head of the linked list. */
+ ignore->next = ignore_patterns;
+@@ -2294,7 +2301,7 @@
+ static void
+ clear_files (void)
+ {
+- register int i;
++ register size_t i;
+
+ for (i = 0; i < files_index; i++)
+ {
+@@ -2320,8 +2327,8 @@
+
+ if (files_index == nfiles)
+ {
++ files = xnrealloc (files, nfiles, 2 * sizeof *files);
+ nfiles *= 2;
+- files = XREALLOC (files, struct fileinfo, nfiles);
+ }
+
+ files[files_index].linkname = 0;
+@@ -2547,7 +2554,8 @@
+ static void
+ extract_dirs_from_files (const char *dirname, int ignore_dot_and_dot_dot)
+ {
+- register int i, j;
++ register size_t i;
++ register size_t j;
+
+ if (*dirname && LOOP_DETECT)
+ {
+@@ -2559,7 +2567,7 @@
+
+ /* Queue the directories last one first, because queueing reverses the
+ order. */
+- for (i = files_index - 1; i >= 0; i--)
++ for (i = files_index; i-- != 0; )
+ if ((files[i].filetype == directory || files[i].filetype == arg_directory)
+ && (!ignore_dot_and_dot_dot
+ || !basename_is_dot_or_dotdot (files[i].name)))
+@@ -2786,7 +2794,7 @@
+ static void
+ print_current_files (void)
+ {
+- register int i;
++ register size_t i;
+
+ switch (format)
+ {
+@@ -2799,12 +2807,10 @@
+ break;
+
+ case many_per_line:
+- init_column_info ();
+ print_many_per_line ();
+ break;
+
+ case horizontal:
+- init_column_info ();
+ print_horizontal ();
+ break;
+
+@@ -2907,7 +2913,7 @@
+ if (name)
+ sprintf (buffer, "%-8s ", name);
+ else
+- sprintf (buffer, "%-8lu ", (unsigned long) u);
++ sprintf (buffer, "%-8lu ", (unsigned long int) u);
+ return strlen (buffer);
+ }
+
+@@ -2981,7 +2987,7 @@
+
+ /* The last byte of the mode string is the POSIX
+ "optional alternate access method flag". */
+- sprintf (p, "%s %3lu ", modebuf, (unsigned long) f->stat.st_nlink);
++ sprintf (p, "%s %3lu ", modebuf, (unsigned long int) f->stat.st_nlink);
+ p += strlen (p);
+
+ if (print_owner)
+@@ -2993,7 +2999,7 @@
+ if (group_name)
+ sprintf (p, "%-8s ", group_name);
+ else
+- sprintf (p, "%-8lu ", (unsigned long) f->stat.st_gid);
++ sprintf (p, "%-8lu ", (unsigned long int) f->stat.st_gid);
+ p += strlen (p);
+ }
+
+@@ -3002,8 +3008,8 @@
+
+ if (S_ISCHR (f->stat.st_mode) || S_ISBLK (f->stat.st_mode))
+ sprintf (p, "%3lu, %3lu ",
+- (unsigned long) major (f->stat.st_rdev),
+- (unsigned long) minor (f->stat.st_rdev));
++ (unsigned long int) major (f->stat.st_rdev),
++ (unsigned long int) minor (f->stat.st_rdev));
+ else
+ {
+ char hbuf[LONGEST_HUMAN_READABLE + 1];
+@@ -3405,7 +3411,7 @@
+ name += len; /* Pointer to final \0. */
+ for (ext = color_ext_list; ext != NULL; ext = ext->next)
+ {
+- if ((size_t) ext->ext.len <= len
++ if (ext->ext.len <= len
+ && strncmp (name - ext->ext.len, ext->ext.string,
+ ext->ext.len) == 0)
+ break;
+@@ -3422,12 +3428,12 @@
+ static void
+ put_indicator (const struct bin_str *ind)
+ {
+- register int i;
++ register size_t i;
+ register const char *p;
+
+ p = ind->string;
+
+- for (i = ind->len; i > 0; --i)
++ for (i = ind->len; i != 0; --i)
+ putchar (*(p++));
+ }
+
+@@ -3445,10 +3451,10 @@
+ return (full_write (STDOUT_FILENO, ind->string, len) != len);
+ }
+
+-static int
++static size_t
+ length_of_file_name_and_frills (const struct fileinfo *f)
+ {
+- register int len = 0;
++ register size_t len = 0;
+ size_t name_width;
+
+ if (print_inode)
+@@ -3485,70 +3491,25 @@
+ static void
+ print_many_per_line (void)
+ {
+- struct column_info *line_fmt;
+- int filesno; /* Index into files. */
+- int row; /* Current row. */
+- int max_name_length; /* Length of longest file name + frills. */
+- int name_length; /* Length of each file name + frills. */
+- int pos; /* Current character column. */
+- int cols; /* Number of files across. */
+- int rows; /* Maximum number of files down. */
+- int max_cols;
+-
+- /* Normally the maximum number of columns is determined by the
+- screen width. But if few files are available this might limit it
+- as well. */
+- max_cols = max_idx > files_index ? files_index : max_idx;
+-
+- /* Compute the maximum number of possible columns. */
+- for (filesno = 0; filesno < files_index; ++filesno)
+- {
+- int i;
+-
+- name_length = length_of_file_name_and_frills (files + filesno);
+-
+- for (i = 0; i < max_cols; ++i)
+- {
+- if (column_info[i].valid_len)
+- {
+- int idx = filesno / ((files_index + i) / (i + 1));
+- int real_length = name_length + (idx == i ? 0 : 2);
+-
+- if (real_length > column_info[i].col_arr[idx])
+- {
+- column_info[i].line_len += (real_length
+- - column_info[i].col_arr[idx]);
+- column_info[i].col_arr[idx] = real_length;
+- column_info[i].valid_len = column_info[i].line_len < line_length;
+- }
+- }
+- }
+- }
+-
+- /* Find maximum allowed columns. */
+- for (cols = max_cols; cols > 1; --cols)
+- {
+- if (column_info[cols - 1].valid_len)
+- break;
+- }
+-
+- line_fmt = &column_info[cols - 1];
++ size_t row; /* Current row. */
++ size_t cols = calculate_columns (true);
++ struct column_info const *line_fmt = &column_info[cols - 1];
+
+ /* Calculate the number of rows that will be in each column except possibly
+ for a short column on the right. */
+- rows = files_index / cols + (files_index % cols != 0);
++ size_t rows = files_index / cols + (files_index % cols != 0);
+
+ for (row = 0; row < rows; row++)
+ {
+- int col = 0;
+- filesno = row;
+- pos = 0;
++ size_t col = 0;
++ size_t filesno = row;
++ size_t pos = 0;
+ /* Print the next row. */
+ while (1)
+ {
++ size_t name_length = length_of_file_name_and_frills (files + filesno);
++ size_t max_name_length = line_fmt->col_arr[col++];
+ print_file_name_and_frills (files + filesno);
+- name_length = length_of_file_name_and_frills (files + filesno);
+- max_name_length = line_fmt->col_arr[col++];
+
+ filesno += rows;
+ if (filesno >= files_index)
+@@ -3564,65 +3525,20 @@
+ static void
+ print_horizontal (void)
+ {
+- struct column_info *line_fmt;
+- int filesno;
+- int max_name_length;
+- int name_length;
+- int cols;
+- int pos;
+- int max_cols;
+-
+- /* Normally the maximum number of columns is determined by the
+- screen width. But if few files are available this might limit it
+- as well. */
+- max_cols = max_idx > files_index ? files_index : max_idx;
+-
+- /* Compute the maximum file name length. */
+- max_name_length = 0;
+- for (filesno = 0; filesno < files_index; ++filesno)
+- {
+- int i;
+-
+- name_length = length_of_file_name_and_frills (files + filesno);
+-
+- for (i = 0; i < max_cols; ++i)
+- {
+- if (column_info[i].valid_len)
+- {
+- int idx = filesno % (i + 1);
+- int real_length = name_length + (idx == i ? 0 : 2);
+-
+- if (real_length > column_info[i].col_arr[idx])
+- {
+- column_info[i].line_len += (real_length
+- - column_info[i].col_arr[idx]);
+- column_info[i].col_arr[idx] = real_length;
+- column_info[i].valid_len = column_info[i].line_len < line_length;
+- }
+- }
+- }
+- }
+-
+- /* Find maximum allowed columns. */
+- for (cols = max_cols; cols > 1; --cols)
+- {
+- if (column_info[cols - 1].valid_len)
+- break;
+- }
+-
+- line_fmt = &column_info[cols - 1];
+-
+- pos = 0;
++ size_t filesno;
++ size_t pos = 0;
++ size_t cols = calculate_columns (false);
++ struct column_info const *line_fmt = &column_info[cols - 1];
++ size_t name_length = length_of_file_name_and_frills (files);
++ size_t max_name_length = line_fmt->col_arr[0];
+
+ /* Print first entry. */
+ print_file_name_and_frills (files);
+- name_length = length_of_file_name_and_frills (files);
+- max_name_length = line_fmt->col_arr[0];
+
+ /* Now the rest. */
+ for (filesno = 1; filesno < files_index; ++filesno)
+ {
+- int col = filesno % cols;
++ size_t col = filesno % cols;
+
+ if (col == 0)
+ {
+@@ -3646,8 +3562,9 @@
+ static void
+ print_with_commas (void)
+ {
+- int filesno;
+- int pos, old_pos;
++ size_t filesno;
++ size_t pos;
++ size_t old_pos;
+
+ pos = 0;
+
+@@ -3679,11 +3596,11 @@
+ Use a TAB character instead of two or more spaces whenever possible. */
+
+ static void
+-indent (int from, int to)
++indent (size_t from, size_t to)
+ {
+ while (from < to)
+ {
+- if (tabsize > 0 && to / tabsize > (from + 1) / tabsize)
++ if (tabsize != 0 && to / tabsize > (from + 1) / tabsize)
+ {
+ putchar ('\t');
+ from += tabsize - from % tabsize;
+@@ -3719,37 +3636,129 @@
+ *dest = 0;
+ }
+
++/* Allocate enough column info suitable for the current number of
++ files and display columns, and initialize the info to represent the
++ narrowest possible columns. */
++
+ static void
+ init_column_info (void)
+ {
+- int i;
+- int allocate = 0;
++ size_t i;
++
++ size_t max_cols = MIN (max_idx, files_index);
+
+- max_idx = line_length / MIN_COLUMN_WIDTH;
+- if (max_idx == 0)
+- max_idx = 1;
++ /* Currently allocated columns in column_info. */
++ static size_t column_info_alloc;
++
++ if (column_info_alloc < max_cols)
++ {
++ size_t new_column_info_alloc;
++ size_t *p;
+
+- if (column_info == NULL)
++ if (max_cols < max_idx / 2)
+ {
+- column_info = XMALLOC (struct column_info, max_idx);
+- allocate = 1;
++ /* The number of columns is far less than the display width
++ allows. Grow the allocation, but only so that it's
++ double the current requirements. If the display is
++ extremely wide, this avoids allocating a lot of memory
++ that is never needed. */
++ column_info = xnrealloc (column_info, max_cols,
++ 2 * sizeof *column_info);
++ new_column_info_alloc = 2 * max_cols;
++ }
++ else
++ {
++ column_info = xnrealloc (column_info, max_idx, sizeof *column_info);
++ new_column_info_alloc = max_idx;
+ }
+
+- for (i = 0; i < max_idx; ++i)
++ /* Allocate the new size_t objects by computing the triangle
++ formula n * (n + 1) / 2, except that we don't need to
++ allocate the part of the triangle that we've already
++ allocated. Check for address arithmetic overflow. */
+ {
+- int j;
++ size_t column_info_growth = new_column_info_alloc - column_info_alloc;
++ size_t s = column_info_alloc + 1 + new_column_info_alloc;
++ size_t t = s * column_info_growth;
++ if (s < new_column_info_alloc || t / column_info_growth != s)
++ xalloc_die ();
++ p = xnmalloc (t / 2, sizeof *p);
++ }
+
+- column_info[i].valid_len = 1;
+- column_info[i].line_len = (i + 1) * MIN_COLUMN_WIDTH;
++ /* Grow the triangle by parceling out the cells just allocated. */
++ for (i = column_info_alloc; i < new_column_info_alloc; i++)
++ {
++ column_info[i].col_arr = p;
++ p += i + 1;
++ }
+
+- if (allocate)
+- column_info[i].col_arr = XMALLOC (int, i + 1);
++ column_info_alloc = new_column_info_alloc;
++ }
++
++ for (i = 0; i < max_cols; ++i)
++ {
++ size_t j;
+
++ column_info[i].valid_len = true;
++ column_info[i].line_len = (i + 1) * MIN_COLUMN_WIDTH;
+ for (j = 0; j <= i; ++j)
+ column_info[i].col_arr[j] = MIN_COLUMN_WIDTH;
+ }
+ }
+
++/* Calculate the number of columns needed to represent the current set
++ of files in the current display width. */
++
++static size_t
++calculate_columns (bool by_columns)
++{
++ size_t filesno; /* Index into files. */
++ size_t cols; /* Number of files across. */
++
++ /* Normally the maximum number of columns is determined by the
++ screen width. But if few files are available this might limit it
++ as well. */
++ size_t max_cols = MIN (max_idx, files_index);
++
++ init_column_info ();
++
++ /* Compute the maximum number of possible columns. */
++ for (filesno = 0; filesno < files_index; ++filesno)
++ {
++ size_t name_length = length_of_file_name_and_frills (files + filesno);
++ size_t i;
++
++ for (i = 0; i < max_cols; ++i)
++ {
++ if (column_info[i].valid_len)
++ {
++ size_t idx = (by_columns
++ ? filesno / ((files_index + i) / (i + 1))
++ : filesno % (i + 1));
++ size_t real_length = name_length + (idx == i ? 0 : 2);
++
++ if (column_info[i].col_arr[idx] < real_length)
++ {
++ column_info[i].line_len += (real_length
++ - column_info[i].col_arr[idx]);
++ column_info[i].col_arr[idx] = real_length;
++ column_info[i].valid_len = (column_info[i].line_len
++ < line_length);
++ }
++ }
++ }
++ }
++
++ /* Find maximum allowed columns. */
++ for (cols = max_cols; 1 < cols; --cols)
++ {
++ if (column_info[cols - 1].valid_len)
++ break;
++ }
++
++ return cols;
++}
++
+ void
+ usage (int status)
+ {