From: meissner@osf.org (Michael Meissner)
Newsgroups: comp.lang.perl
Subject: Re: Columnization
Date: 13 Sep 92 11:59:58 GMT
	<1360@minya.UUCP>
Organization: Open Software Foundation

In article <1360@minya.UUCP> jc@minya.UUCP (John Chambers) writes:

| > | Consider the example from the manual page:
| > |    ls | paste - - - -
| > | This is supposed to produce four-column output.  It does put the names
| > | four  to a line, but in most directories, they will not be in columns.
| > | There seems to be no options that will produce columnar output.
| > 
| > pr -m -t -w# file1 file2 ...
| > 
| > where # is the width (default 72).  May be System V only.
| 
| Several people have suggested something like this, but I don't see any
| way  that  you can use pr to columnize the output of ls like the above
| "ls | paste" command does (but correctly ;-). To do so, you would need
| a  filter  that reads ls's output and writes it into N files, and then
| feed these files to pr, and then remember to delete  the  intermediate
| files.   If you're going to write this filter, well, you might just as
| well have it write a single file (stdout) instead and align the fields
| itself.  So pr doesn't help you at all. If you have to write a program
| to chop up the data into multiple files, it's just as  easy  to  write
| your own program in C or perl to do the entire job.

(I couldn't find the article the above article quotes, so hopefully it
is solving the problem desired).

While it doesn't allow you to specify exactly 4 column output, my
columnization library will automatically pick as many columns as can
fit on the line size.  It would be used like:

	require 'columnize.pl'
	chop (@lines = <STDIN>);
	$, = '';
	$\ = '';
	print &columnize (4, 4, 72, 0, @lines);

The initial arguments to columnize are:

	Arg 1:	The number of leading spaces to print on each line.
	Arg 2:	The number of spaces to leave between each column.
	Arg 3:	The line width to use or 0, to use the current terminal's line size.
	Arg 4:	The minimun column width to use
	Arg 5-	Items to columnize.

Note the line_size has the system call number for TIOCGWINSZ hard
coded.

#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 09/13/1992 11:47 UTC by meissner@curley.osf.org
# Source directory /usr/users/meissner/bin.perl
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   2234 -rw-rw-r-- columnize.pl
#
# ============= columnize.pl ==============
if test -f 'columnize.pl' -a X"$1" != X"-c"; then
	echo 'x - skipping columnize.pl (File already exists)'
else
echo 'x - extracting columnize.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'columnize.pl' &&
X#! /usr/local/bin/perl
X
X# External function to return a string array, such that when each
X# element is printed, it is printed as a series of columns, each
X# equally spaced.
X#
X# Arg 1:	The number of leading spaces to print on each line.
X# Arg 2:	The number of spaces to leave between each column.
X# Arg 3:	The line width to use or 0, to use the current terminal's line size.
X# Arg 4:	The minimun column width to use
X# Arg 5-	Items to columnize.
X
Xsub columnize {
X	package columnize;
X	local ($leading_spaces)	= shift (@_);
X	local ($num_spaces)	= shift (@_);
X	local ($user_line_size)	= shift (@_);
X	local ($max_length)	= shift (@_);
X	local (@ret)		= ();
X	local ($cur_col)	= 1;
X	local ($initial)	= ("\t" x ($leading_spaces / 8)) . (' ' x ($leading_spaces % 8));
X	local ($element);
X	local ($ncols);
X
X	if ($#_ >= $[) {
X		foreach $element (@_) {
X			$max_length = length ($element)		if (length ($element) > $max_length);
X		}
X
X		$ncols = &main'line_length ($user_line_size) - 1 - $leading_spaces;
X		$ncols += ($num_spaces - 1)			if ($num_spaces > 0);
X		$ncols /= ($max_length + $num_spaces);
X		$ncols = 1					if ($ncols <= 0);
X
X		foreach $element (@_[ $[ .. $#_ - 1 ]) {
X			if (++$cur_col >= $ncols) {
X				$cur_col = 1;
X				push (@ret, "$initial$element\n");
X				$initial = ' ' x $leading_spaces;
X
X			} else {
X				push (@ret, ($initial . $element . (' ' x ($max_length - length ($element)))));
X				$initial = ' ' x $num_spaces;
X			}
X		}
X
X		push (@ret, "$initial$_[$#_]\n");
X	}
X
X	return @ret;
X}
X
X
X# Internal function to get the line length.
X
Xsub line_length {
X	package columnize;
X	local ($user_line_size)	= shift (@_);
X
X	return $user_line_size				if (defined ($user_line_size) && $user_line_size > 0);
X
X	if (! defined ($line_length) || ! $line_length) {
X
X		if (defined ($ENV{'COLUMNS'}) && $ENV{'COLUMNS'} > 0) {
X			$line_length = $ENV{'COLUMNS'} + 0;
X
X		} else {
X			local ($winsize)	= "\0\0\0\0\0\0\0\0";
X			local ($TIOCGWINSZ)	= 0x40087468;		# on OSF/1 at least
X			local (@winsize2);
X
X			$line_length = 79;
X			if (-t STDIN && ioctl (STDIN, $TIOCGWINSZ, $winsize)) {
X				@winsize2 = unpack ('SSSS', $winsize);
X
X				$line_length = $winsize2[1]	if ($winsize2[1] > 0);
X			}
X		}
X	}
X
X	return $line_length;
X}
X
X
X# Return 1, so require succeeds.
X
X1;
SHAR_EOF
chmod 0664 columnize.pl ||
echo 'restore of columnize.pl failed'
Wc_c="`wc -c < 'columnize.pl'`"
test 2234 -eq "$Wc_c" ||
	echo 'columnize.pl: original size 2234, current size' "$Wc_c"
fi
exit 0
--
Michael Meissner	email: meissner@osf.org		phone: 617-621-8861
Open Software Foundation, 11 Cambridge Center, Cambridge, MA, 02142

You are in a twisty little passage of standards, all conflicting.
Newsgroups: comp.lang.perl