Import rs_20101029.orig.tar.gz rs-20101029
authortg <tg@mirbsd.org>
Thu, 28 Jul 2011 16:50:59 +0000 (16:50 +0000)
committertg <tg@mirbsd.org>
Thu, 28 Jul 2011 16:50:59 +0000 (16:50 +0000)
Makefile [new file with mode: 0644]
rs.1 [new file with mode: 0644]
rs.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..6c658e8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,6 @@
+#      $OpenBSD: Makefile,v 1.2 1996/06/26 05:38:46 deraadt Exp $
+
+
+PROG=  rs
+
+.include <bsd.prog.mk>
diff --git a/rs.1 b/rs.1
new file mode 100644 (file)
index 0000000..4929b9b
--- /dev/null
+++ b/rs.1
@@ -0,0 +1,234 @@
+.\"    $OpenBSD: rs.1,v 1.12 2005/05/15 13:21:11 jmc Exp $
+.\"    $FreeBSD: src/usr.bin/rs/rs.1,v 1.4 1999/08/28 01:05:21 peter Exp $
+.\"
+.\" Copyright (c) 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    @(#)rs.1        8.2 (Berkeley) 12/30/93
+.\"
+.Dd December 30, 1993
+.Dt RS 1
+.Os
+.Sh NAME
+.Nm rs
+.Nd reshape a data array
+.Sh SYNOPSIS
+.Nm rs
+.Op Fl CcSs Ns Op Ar x
+.Op Fl KkGgw Ar N
+.Op Fl EeHhjmnTtyz
+.Op Ar rows Op Ar cols
+.Sh DESCRIPTION
+.Nm
+reads the standard input, interpreting each line as a row
+of blank-separated entries in an array,
+transforms the array according to the options,
+and writes it on the standard output.
+With no arguments it transforms stream input into a columnar
+format convenient for terminal viewing.
+.Pp
+The shape of the input array is deduced from the number of lines
+and the number of columns on the first line.
+If that shape is inconvenient, a more useful one might be
+obtained by skipping some of the input with the
+.Fl k
+option.
+Other options control interpretation of the input columns.
+.Pp
+The shape of the output array is influenced by the
+.Ar rows
+and
+.Ar cols
+specifications, which should be positive integers.
+If only one of them is a positive integer,
+.Nm
+computes a value for the other which will accommodate
+all of the data.
+When necessary, missing data are supplied in a manner
+specified by the options and surplus data are deleted.
+There are options to control presentation of the output columns,
+including transposition of the rows and columns.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl C Ns Ar x
+Output columns are delimited by the single character
+.Ar x .
+A missing
+.Ar x
+is taken to be
+.Ql ^I .
+.It Fl c Ns Ar x
+Input columns are delimited by the single character
+.Ar x .
+A missing
+.Ar x
+is taken to be
+.Ql ^I .
+.It Fl E
+Consider each character of input as an array entry.
+.It Fl e
+Consider each line of input as an array entry.
+.It Fl G Ns Ar N
+The gutter width has
+.Ar N
+percent of the maximum column width added to it.
+.It Fl g Ns Ar N
+The gutter width (inter-column space), normally 2, is taken to be
+.Ar N .
+.It Fl H
+Like
+.Fl h ,
+but also print the length of each line.
+.It Fl h
+Print the shape of the input array and do nothing else.
+The shape is just the number of lines and the number of
+entries on the first line.
+.It Fl j
+Right adjust entries within columns.
+.It Fl K Ns Ar N
+Like
+.Fl k ,
+but print the ignored lines.
+.It Fl k Ns Ar N
+Ignore the first
+.Ar N
+lines of input.
+.It Fl m
+Do not trim excess delimiters from the ends of the output array.
+.It Fl n
+On lines having fewer entries than the first line,
+use null entries to pad out the line.
+Normally, missing entries are taken from the next line of input.
+.It Fl S Ns Ar x
+Like
+.Fl C ,
+but padded strings of
+.Ar x
+are delimiters.
+.It Fl s Ns Ar x
+Like
+.Fl c ,
+but maximal strings of
+.Ar x
+are delimiters.
+.It Fl T
+Print the pure transpose of the input, ignoring any
+.Ar rows
+or
+.Ar cols
+specification.
+.It Fl t
+Fill in the rows of the output array using the columns of the
+input array, that is, transpose the input while honoring any
+.Ar rows
+and
+.Ar cols
+specifications.
+.It Fl w Ns Ar N
+The width of the display, normally 80, is taken to be the positive
+integer
+.Ar N .
+.It Fl y
+If there are too few entries to make up the output dimensions,
+pad the output by recycling the input from the beginning.
+Normally, the output is padded with blanks.
+.It Fl z
+Adapt column widths to fit the largest entries appearing in them.
+.El
+.Pp
+With no arguments,
+.Nm
+transposes its input, and assumes one array entry per input line
+unless the first non-ignored line is longer than the display width.
+Option letters which take numerical arguments interpret a missing
+number as zero unless otherwise indicated.
+.Sh EXAMPLES
+.Nm
+can be used as a filter to convert the stream output
+of certain programs (e.g.,
+.Xr spell ,
+.Xr du ,
+.Xr file ,
+.Xr look ,
+.Xr nm ,
+.Xr who ,
+and
+.Xr wc 1 )
+into a convenient
+.Dq window
+format, as in
+.Bd -literal -offset indent
+$ who | rs
+.Ed
+.Pp
+This function has been incorporated into the
+.Xr ls 1
+program, though for most programs with similar output
+.Nm
+suffices.
+.Pp
+To convert stream input into vector output and back again, use
+.Bd -literal -offset indent
+$ rs 1 0 | rs 0 1
+.Ed
+.Pp
+A 10 by 10 array of random numbers from 1 to 100 and
+its transpose can be generated with
+.Bd -literal -offset indent
+$ jot \-r 100 | rs 10 10 | tee array | rs \-T > tarray
+.Ed
+.Pp
+In the editor
+.Xr vi 1 ,
+a file consisting of a multi-line vector with 9 elements per line
+can undergo insertions and deletions,
+and then be neatly reshaped into 9 columns with
+.Bd -literal -offset indent
+:1,$!rs 0 9
+.Ed
+.Pp
+Finally, to sort a database by the first line of each 4-line field, try
+.Bd -literal -offset indent
+$ rs \-eC 0 4 | sort | rs \-c 0 1
+.Ed
+.Sh SEE ALSO
+.Xr jot 1 ,
+.Xr pr 1 ,
+.Xr sort 1 ,
+.Xr vi 1
+.Sh BUGS
+Handles only two dimensional arrays.
+.Pp
+The algorithm currently reads the whole file into memory,
+so files that do not fit in memory will not be reshaped.
+.Pp
+Fields cannot be defined yet on character positions.
+.Pp
+Re-ordering of columns is not yet possible.
+.Pp
+There are too many options.
diff --git a/rs.c b/rs.c
new file mode 100644 (file)
index 0000000..dd1a493
--- /dev/null
+++ b/rs.c
@@ -0,0 +1,547 @@
+/*     $OpenBSD: rs.c,v 1.16 2005/05/15 13:19:14 jmc Exp $     */
+
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ *     rs - reshape a data array
+ *     Author:  John Kunze, Office of Comp. Affairs, UCB
+ *             BEWARE: lots of unfinished edges
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef USE_LIBBSD
+#include <bsd/bsd.h>
+#ifndef __dead
+#define __dead         __attribute__((__noreturn__))
+#endif
+#endif
+
+__COPYRIGHT("@(#) Copyright (c) 1993\n\
+       The Regents of the University of California.  All rights reserved.\n");
+__SCCSID("@(#)rs.c     8.1 (Berkeley) 6/6/93");
+__RCSID("$MirOS: src/usr.bin/rs/rs.c,v 1.3 2010/10/28 18:13:48 tg Exp $");
+
+long   flags;
+#define        TRANSPOSE       000001
+#define        MTRANSPOSE      000002
+#define        ONEPERLINE      000004
+#define        ONEISEPONLY     000010
+#define        ONEOSEPONLY     000020
+#define        NOTRIMENDCOL    000040
+#define        SQUEEZE         000100
+#define        SHAPEONLY       000200
+#define        DETAILSHAPE     000400
+#define        RIGHTADJUST     001000
+#define        NULLPAD         002000
+#define        RECYCLE         004000
+#define        SKIPPRINT       010000
+#define        ICOLBOUNDS      020000
+#define        OCOLBOUNDS      040000
+#define ONEPERCHAR     0100000
+#define NOARGS         0200000
+
+short  *colwidths;
+short  *cord;
+short  *icbd;
+short  *ocbd;
+int    nelem;
+const char **elem;
+const char **endelem;
+char   *curline;
+int    allocsize = BUFSIZ;
+int    curlen;
+int    irows, icols;
+int    orows, ocols;
+int    maxlen;
+int    skip;
+int    propgutter;
+char   isep = ' ', osep = ' ';
+int    owidth = 80, gutter = 2;
+
+void     usage(void) __dead;
+void     getargs(int, char *[]);
+void     getfile(void);
+int      get_line(void);
+char    *getlist(short **, char *);
+const char **getptrs(const char **);
+void     prepfile(void);
+void     prints(const char *, int);
+void     putfile(void);
+
+#define INCR(ep) do {                  \
+       if (++ep >= endelem)            \
+               ep = getptrs(ep);       \
+} while(0)
+
+int
+main(int argc, char *argv[])
+{
+       getargs(argc, argv);
+       getfile();
+       if (flags & SHAPEONLY) {
+               printf("%d %d\n", irows, icols);
+               exit(0);
+       }
+       prepfile();
+       putfile();
+       exit(0);
+}
+
+void
+getfile(void)
+{
+       char *p;
+       char *endp;
+       const char **ep = NULL;
+       int multisep = (flags & ONEISEPONLY ? 0 : 1);
+       int nullpad = flags & NULLPAD;
+       const char **padto;
+
+       while (skip--) {
+               get_line();
+               if (flags & SKIPPRINT)
+                       puts(curline);
+       }
+       get_line();
+       if (flags & NOARGS && curlen < owidth)
+               flags |= ONEPERLINE;
+       if (flags & ONEPERLINE)
+               icols = 1;
+       else                            /* count cols on first line */
+               for (p = curline, endp = curline + curlen; p < endp; p++) {
+                       if (*p == isep && multisep)
+                               continue;
+                       icols++;
+                       while (*p && *p != isep)
+                               p++;
+               }
+       ep = getptrs(elem);
+       p = curline;
+       do {
+               if (flags & ONEPERLINE) {
+                       *ep = curline;
+                       INCR(ep);               /* prepare for next entry */
+                       if (maxlen < curlen)
+                               maxlen = curlen;
+                       irows++;
+                       continue;
+               }
+               for (p = curline, endp = curline + curlen; p < endp; p++) {
+                       if (*p == isep && multisep)
+                               continue;       /* eat up column separators */
+                       if (*p == isep)         /* must be an empty column */
+                               *ep = "";
+                       else                    /* store column entry */
+                               *ep = p;
+                       while (p < endp && *p != isep)
+                               p++;            /* find end of entry */
+                       *p = '\0';              /* mark end of entry */
+                       if (maxlen < p - *ep)   /* update maxlen */
+                               maxlen = p - *ep;
+                       INCR(ep);               /* prepare for next entry */
+               }
+               irows++;                        /* update row count */
+               if (nullpad) {                  /* pad missing entries */
+                       padto = elem + irows * icols;
+                       while (ep < padto) {
+                               *ep = "";
+                               INCR(ep);
+                       }
+               }
+       } while (get_line() != EOF);
+       *ep = NULL;                             /* mark end of pointers */
+       nelem = ep - elem;
+}
+
+void
+putfile(void)
+{
+       const char **ep;
+       int i, j, n;
+
+       ep = elem;
+       if (flags & TRANSPOSE) {
+               for (i = 0; i < orows; i++) {
+                       for (j = i; j < nelem; j += orows)
+                               prints(ep[j], (j - i) / orows);
+                       putchar('\n');
+               }
+       } else {
+               for (n = 0, i = 0; i < orows && n < nelem; i++) {
+                       for (j = 0; j < ocols; j++) {
+                               if (n++ >= nelem)
+                                       break;
+                               prints(*ep++, j);
+                       }
+                       putchar('\n');
+               }
+       }
+}
+
+void
+prints(const char *s, int col)
+{
+       int n;
+       const char *p = s;
+
+       while (*p)
+               p++;
+       n = (flags & ONEOSEPONLY ? 1 : colwidths[col] - (p - s));
+       if (flags & RIGHTADJUST)
+               while (n-- > 0)
+                       putchar(osep);
+       for (p = s; *p; p++)
+               putchar(*p);
+       while (n-- > 0)
+               putchar(osep);
+}
+
+void
+usage(void)
+{
+       extern char *__progname;
+
+       fprintf(stderr,
+           "usage: %s [-CcSs[x]] [-KkGgw N] [-EeHhjmnTtyz] [rows [cols]]\n",
+           __progname);
+       exit(1);
+}
+
+void
+prepfile(void)
+{
+       const char **ep;
+       int  i;
+       int  j;
+       const char **lp;
+       int colw;
+       int max = 0;
+       int n;
+
+       if (!nelem)
+               exit(0);
+       gutter += maxlen * propgutter / 100.0;
+       colw = maxlen + gutter;
+       if (flags & MTRANSPOSE) {
+               orows = icols;
+               ocols = irows;
+       }
+       else if (orows == 0 && ocols == 0) {    /* decide rows and cols */
+               ocols = owidth / colw;
+               if (ocols == 0) {
+                       warnx("Display width %d is less than column width %d",
+                           owidth, colw);
+                       ocols = 1;
+               }
+               if (ocols > nelem)
+                       ocols = nelem;
+               orows = nelem / ocols + (nelem % ocols ? 1 : 0);
+       }
+       else if (orows == 0)                    /* decide on rows */
+               orows = nelem / ocols + (nelem % ocols ? 1 : 0);
+       else if (ocols == 0)                    /* decide on cols */
+               ocols = nelem / orows + (nelem % orows ? 1 : 0);
+       lp = elem + orows * ocols;
+       while (lp > endelem) {
+               getptrs(elem + nelem);
+               lp = elem + orows * ocols;
+       }
+       if (flags & RECYCLE) {
+               for (ep = elem + nelem; ep < lp; ep++)
+                       *ep = *(ep - nelem);
+               nelem = lp - elem;
+       }
+       if (!(colwidths = (short *) malloc(ocols * sizeof(short))))
+               errx(1, "malloc:  No gutter space");
+       if (flags & SQUEEZE) {
+               if (flags & TRANSPOSE)
+                       for (ep = elem, i = 0; i < ocols; i++) {
+                               for (j = 0; j < orows; j++)
+                                       if ((n = strlen(*ep++)) > max)
+                                               max = n;
+                               colwidths[i] = max + gutter;
+                       }
+               else
+                       for (ep = elem, i = 0; i < ocols; i++) {
+                               for (j = i; j < nelem; j += ocols)
+                                       if ((n = strlen(ep[j])) > max)
+                                               max = n;
+                               colwidths[i] = max + gutter;
+                       }
+       } else {
+               for (i = 0; i < ocols; i++)
+                       colwidths[i] = colw;
+       }
+       if (!(flags & NOTRIMENDCOL)) {
+               if (flags & RIGHTADJUST)
+                       colwidths[0] -= gutter;
+               else
+                       colwidths[ocols - 1] = 0;
+       }
+       n = orows * ocols;
+       if (n > nelem && (flags & RECYCLE))
+               nelem = n;
+}
+
+#define        BSIZE   2048
+char   ibuf[BSIZE];            /* two screenfuls should do */
+
+int
+get_line(void) /* get line; maintain curline, curlen; manage storage */
+{
+       static  int putlength;
+       static  char *endblock = ibuf + BSIZE;
+       char *p;
+       int c, i;
+
+       if (!irows) {
+               curline = ibuf;
+               putlength = flags & DETAILSHAPE;
+       }
+       else if (skip <= 0) {                   /* don't waste storage */
+               curline += curlen + 1;
+               if (putlength)          /* print length, recycle storage */
+                       printf(" %d line %d\n", curlen, irows);
+       }
+       if (!putlength && endblock - curline < BUFSIZ) {   /* need storage */
+               if (!(curline = (char *) malloc(BSIZE)))
+                       errx(1, "File too large");
+               endblock = curline + BSIZE;
+       }
+       for (p = curline, i = 1; i < BUFSIZ; *p++ = c, i++)
+               if ((c = getchar()) == EOF || c == '\n')
+                       break;
+       *p = '\0';
+       curlen = i - 1;
+       return(c);
+}
+
+const char **
+getptrs(const char **sp)
+{
+       const char **p;
+       int newsize, gap;
+
+       newsize = allocsize * 2;
+       p = realloc(elem, newsize * sizeof(char *));
+       if (p == NULL)
+               err(1, "no memory");
+
+       gap = p - elem;
+       elem = p;
+       allocsize = newsize;
+       sp += gap;
+       endelem = elem + allocsize;
+       return(sp);
+}
+
+void
+getargs(int ac, char *av[])
+{
+       int ch;
+       const char *errstr;
+
+       if (ac == 1)
+               flags |= NOARGS | TRANSPOSE;
+       while ((ch = getopt(ac, av, "c::C::s::S::k:K:g:G:w:tTeEnyjhHmz")) != -1) {
+               switch (ch) {
+               case 'T':
+                       flags |= MTRANSPOSE;
+                       /* FALLTHROUGH */
+               case 't':
+                       flags |= TRANSPOSE;
+                       break;
+               case 'c':               /* input col. separator */
+                       flags |= ONEISEPONLY;
+                       /* FALLTHROUGH */
+               case 's':               /* one or more allowed */
+                       if (optarg == NULL)
+                               isep = '\t';    /* default is ^I */
+                       else if (optarg[1] != '\0')
+                               usage();        /* single char only */
+                       else
+                               isep = *optarg;
+                       break;
+               case 'C':
+                       flags |= ONEOSEPONLY;
+                       /* FALLTHROUGH */
+               case 'S':
+                       if (optarg == NULL)
+                               osep = '\t';    /* default is ^I */
+                       else if (optarg[1] != '\0')
+                               usage();        /* single char only */
+                       else
+                               osep = *optarg;
+                       break;
+               case 'w':               /* window width, default 80 */
+                       owidth = strtonum(optarg, 1, INT_MAX, &errstr);
+                       if (errstr) {
+                               warnx("width %s", errstr);
+                               usage();
+                       }
+                       break;
+               case 'K':                       /* skip N lines */
+                       flags |= SKIPPRINT;
+                       /* FALLTHROUGH */
+               case 'k':                       /* skip, do not print */
+                       skip = strtonum(optarg, 0, INT_MAX, &errstr);
+                       if (errstr) {
+                               warnx("skip value %s", errstr);
+                               usage();
+                       }
+                       if (skip == 0)
+                               skip = 1;
+                       break;
+               case 'm':
+                       flags |= NOTRIMENDCOL;
+                       break;
+               case 'g':               /* gutter width */
+                       gutter = strtonum(optarg, 0, INT_MAX, &errstr);
+                       if (errstr) {
+                               warnx("gutter width %s", errstr);
+                               usage();
+                       }
+                       break;
+               case 'G':
+                       propgutter = strtonum(optarg, 0, INT_MAX, &errstr);
+                       if (errstr) {
+                               warnx("gutter proportion %s", errstr);
+                               usage();
+                       }
+                       break;
+               case 'e':               /* each line is an entry */
+                       flags |= ONEPERLINE;
+                       break;
+               case 'E':
+                       flags |= ONEPERCHAR;
+                       break;
+               case 'j':                       /* right adjust */
+                       flags |= RIGHTADJUST;
+                       break;
+               case 'n':       /* null padding for missing values */
+                       flags |= NULLPAD;
+                       break;
+               case 'y':
+                       flags |= RECYCLE;
+                       break;
+               case 'H':                       /* print shape only */
+                       flags |= DETAILSHAPE;
+                       /* FALLTHROUGH */
+               case 'h':
+                       flags |= SHAPEONLY;
+                       break;
+               case 'z':                       /* squeeze col width */
+                       flags |= SQUEEZE;
+                       break;
+               case 'o':                       /* col order */
+                       getlist(&cord, optarg);
+                       break;
+               case 'b':
+                       flags |= ICOLBOUNDS;
+                       getlist(&icbd, optarg);
+                       break;
+               case 'B':
+                       flags |= OCOLBOUNDS;
+                       getlist(&ocbd, optarg);
+                       break;
+               default:
+                       usage();
+               }
+       }
+       ac -= optind;
+       av += optind;
+
+       switch (ac) {
+       case 2:
+               ocols = strtonum(av[1], 0, INT_MAX, &errstr);
+               if (errstr) {
+                       warnx("columns value %s", errstr);
+                       usage();
+               }
+       case 1:
+               orows = strtonum(av[0], 0, INT_MAX, &errstr);
+               if (errstr) {
+                       warnx("columns value %s", errstr);
+                       usage();
+               }
+       case 0:
+               break;
+       default:
+               usage();
+       }
+}
+
+char *
+getlist(short **list, char *p)
+{
+       int count = 1;
+       char *t, *ep;
+       long l;
+
+       for (t = p + 1; *t; t++) {
+               if (!isdigit(*t)) {
+                       warnx("option -%c requires a list of unsigned numbers separated by commas", *t);
+                       usage();
+               }
+               count++;
+               while (*t && isdigit(*t))
+                       t++;
+               if (*t != ',')
+                       break;
+       }
+       if (!(*list = (short *) malloc(count * sizeof(short))))
+               errx(1, "No list space");
+       count = 0;
+       for (t = p + 1; *t; t++) {
+               errno = 0;
+               l = strtol(t, &ep, 10);
+               if (t == ep)
+                       break;          /* can't happen */
+               if ((errno == ERANGE && (l == LONG_MAX || l == LONG_MIN)) ||
+                   (l > SHRT_MAX || l < SHRT_MIN)) {
+                       warnx("list value out of range");
+                       usage();
+               }
+               (*list)[count++] = (short)l;
+               printf("++ %d ", (*list)[count-1]);
+               fflush(stdout);
+               if (*(t = ep) != ',')
+                       break;
+       }
+       (*list)[count] = 0;
+       return(t - 1);
+}