column formatting pkg and cmd
diff --git a/colwriter/Readme b/colwriter/Readme
new file mode 100644
index 0000000..1c1f4e6
--- /dev/null
+++ b/colwriter/Readme
@@ -0,0 +1,5 @@
+Package colwriter provides a write filter that formats
+input lines in multiple columns.
+
+The package is a straightforward translation from
+/src/cmd/draw/mc.c in Plan 9 from User Space.
diff --git a/colwriter/column.go b/colwriter/column.go
new file mode 100644
index 0000000..c03f43a
--- /dev/null
+++ b/colwriter/column.go
@@ -0,0 +1,146 @@
+// Package colwriter provides a write filter that formats
+// input lines in multiple columns.
+//
+// The package is a straightforward translation from
+// /src/cmd/draw/mc.c in Plan 9 from User Space.
+package colwriter
+
+import (
+	"bytes"
+	"io"
+)
+
+const (
+	tab = 4
+)
+
+const (
+	// Print each input line ending in a colon ':' separately.
+	BreakOnColon uint = 1 << iota
+)
+
+// A Writer is a filter that arranges input lines in as many columns as will
+// fit in its width. Tab '\t' chars in the input are translated to sequences
+// of spaces ending at multiples of 4 positions.
+//
+// If BreakOnColon is set, each input line ending in a colon ':' is written
+// separately.
+//
+// The Writer assumes that all Unicode code points have the same width; this
+// may not be true in some fonts.
+type Writer struct {
+	w     io.Writer
+	buf   []byte
+	width int
+	flag  uint
+}
+
+// NewWriter allocates and initializes a new Writer writing to w.
+// Parameter width controls the total number of characters on each line
+// across all columns.
+func NewWriter(w io.Writer, width int, flag uint) *Writer {
+	return &Writer{
+		w:     w,
+		width: width,
+		flag:  flag,
+	}
+}
+
+// Write writes p to the writer w. The only errors returned are ones
+// encountered while writing to the underlying output stream.
+func (w *Writer) Write(p []byte) (n int, err error) {
+	var linelen int
+	var lastWasColon bool
+	for i, c := range p {
+		w.buf = append(w.buf, c)
+		linelen++
+		if c == '\t' {
+			w.buf[len(w.buf)-1] = ' '
+			for linelen%tab != 0 {
+				w.buf = append(w.buf, ' ')
+				linelen++
+			}
+		}
+		if w.flag&BreakOnColon != 0 && c == ':' {
+			lastWasColon = true
+		} else if lastWasColon {
+			if c == '\n' {
+				pos := bytes.LastIndex(w.buf[:len(w.buf)-1], []byte{'\n'})
+				if pos < 0 {
+					pos = 0
+				}
+				line := w.buf[pos:]
+				w.buf = w.buf[:pos]
+				if err = w.columnate(); err != nil {
+					if len(line) < i {
+						return i - len(line), err
+					}
+					return 0, err
+				}
+				if n, err := w.w.Write(line); err != nil {
+					if r := len(line) - n; r < i {
+						return i - r, err
+					}
+					return 0, err
+				}
+			}
+			lastWasColon = false
+		}
+		if c == '\n' {
+			linelen = 0
+		}
+	}
+	return len(p), nil
+}
+
+// Flush should be called after the last call to Write to ensure that any data
+// buffered in the Writer is written to output.
+func (w *Writer) Flush() error {
+	return w.columnate()
+}
+
+func (w *Writer) columnate() error {
+	words := bytes.Split(w.buf, []byte{'\n'})
+	w.buf = nil
+	if len(words[len(words)-1]) == 0 {
+		words = words[:len(words)-1]
+	}
+	maxwidth := 0
+	for _, wd := range words {
+		if len(wd) > maxwidth {
+			maxwidth = len(wd)
+		}
+	}
+	maxwidth++ // space char
+	wordsPerLine := w.width / maxwidth
+	if wordsPerLine <= 0 {
+		wordsPerLine = 1
+	}
+	nlines := (len(words) + wordsPerLine - 1) / wordsPerLine
+	for i := 0; i < nlines; i++ {
+		col := 0
+		endcol := 0
+		for j := i; j < len(words); j += nlines {
+			endcol += maxwidth
+			_, err := w.w.Write(words[j])
+			if err != nil {
+				return err
+			}
+			col += len(words[j])
+			if j+nlines < len(words) {
+				for col < endcol {
+					_, err := w.w.Write([]byte{' '})
+					if err != nil {
+						return err
+					}
+					col++
+				}
+			}
+		}
+		_, err := w.w.Write([]byte{'\n'})
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/colwriter/column_test.go b/colwriter/column_test.go
new file mode 100644
index 0000000..c60b691
--- /dev/null
+++ b/colwriter/column_test.go
@@ -0,0 +1,101 @@
+package colwriter
+
+import (
+	"bytes"
+	"testing"
+)
+
+var s = `
+.git
+.gitignore
+.godir
+Procfile:
+README.md
+api.go
+apps.go
+auth.go
+darwin.go
+data.go
+dyno.go:
+env.go
+git.go
+help.go
+hkdist
+linux.go
+ls.go
+main.go
+plugin.go
+run.go
+scale.go
+ssh.go
+tail.go
+term
+unix.go
+update.go
+version.go
+windows.go
+`[1:]
+
+var de = `
+.git       README.md  darwin.go  git.go     ls.go      scale.go   unix.go
+.gitignore api.go     data.go    help.go    main.go    ssh.go     update.go
+.godir     apps.go    dyno.go:   hkdist     plugin.go  tail.go    version.go
+Procfile:  auth.go    env.go     linux.go   run.go     term       windows.go
+`[1:]
+
+var ce = `
+.git       .gitignore .godir
+
+Procfile:
+README.md api.go    apps.go   auth.go   darwin.go data.go
+
+dyno.go:
+env.go     hkdist     main.go    scale.go   term       version.go
+git.go     linux.go   plugin.go  ssh.go     unix.go    windows.go
+help.go    ls.go      run.go     tail.go    update.go
+`[1:]
+
+func TestColumns(t *testing.T) {
+	b := new(bytes.Buffer)
+	w := NewWriter(b, 80, 0)
+	if _, err := w.Write([]byte(s)); err != nil {
+		t.Error(err)
+	}
+	if err := w.Flush(); err != nil {
+		t.Error(err)
+	}
+	g := string(b.Bytes())
+	if de != g {
+		t.Log("\n" + de)
+		t.Log("\n" + g)
+		t.Errorf("%q != %q", de, g)
+	}
+}
+
+func TestColumnsColon(t *testing.T) {
+	b := new(bytes.Buffer)
+	w := NewWriter(b, 80, BreakOnColon)
+	if _, err := w.Write([]byte(s)); err != nil {
+		t.Error(err)
+	}
+	if err := w.Flush(); err != nil {
+		t.Error(err)
+	}
+	g := string(b.Bytes())
+	if ce != g {
+		t.Log("\n" + ce)
+		t.Log("\n" + g)
+		t.Errorf("%q != %q", ce, g)
+	}
+}
+
+func TestColWriterFlushEmpty(t *testing.T) {
+	b := new(bytes.Buffer)
+	w := NewWriter(b, 80, 0)
+	if err := w.Flush(); err != nil {
+		t.Error(err)
+	}
+	if g := b.Bytes(); len(g) != 0 {
+		t.Errorf("expected empty output, got %#v", g)
+	}
+}
diff --git a/mc/Readme b/mc/Readme
new file mode 100644
index 0000000..519ddc0
--- /dev/null
+++ b/mc/Readme
@@ -0,0 +1,9 @@
+Command mc prints in multiple columns.
+
+  Usage: mc [-] [-N] [file...]
+
+Mc splits the input into as many columns as will fit in N
+print positions. If the output is a tty, the default N is
+the number of characters in a terminal line; otherwise the
+default N is 80. Under option - each input line ending in
+a colon ':' is printed separately.
diff --git a/mc/mc.go b/mc/mc.go
new file mode 100644
index 0000000..ab823d7
--- /dev/null
+++ b/mc/mc.go
@@ -0,0 +1,61 @@
+// Command mc prints in multiple columns.
+//
+//   Usage: mc [-] [-N] [file...]
+//
+// Mc splits the input into as many columns as will fit in N
+// print positions. If the output is a tty, the default N is
+// the number of characters in a terminal line; otherwise the
+// default N is 80. Under option - each input line ending in
+// a colon ':' is printed separately.
+package main
+
+import (
+	"github.com/kr/pty"
+	"github.com/kr/text/colwriter"
+	"io"
+	"log"
+	"os"
+	"strconv"
+)
+
+func main() {
+	var width int
+	var flag uint
+	args := os.Args[1:]
+	for len(args) > 0 && len(args[0]) > 0 && args[0][0] == '-' {
+		if len(args[0]) > 1 {
+			width, _ = strconv.Atoi(args[0][1:])
+		} else {
+			flag |= colwriter.BreakOnColon
+		}
+		args = args[1:]
+	}
+	if width < 1 {
+		_, width, _ = pty.Getsize(os.Stdout)
+	}
+	if width < 1 {
+		width = 80
+	}
+
+	w := colwriter.NewWriter(os.Stdout, width, flag)
+	if len(args) > 0 {
+		for _, s := range args {
+			if f, err := os.Open(s); err == nil {
+				copyin(w, f)
+			} else {
+				log.Println(err)
+			}
+		}
+	} else {
+		copyin(w, os.Stdin)
+	}
+}
+
+func copyin(w *colwriter.Writer, r io.Reader) {
+	if _, err := io.Copy(w, r); err != nil {
+		log.Println(err)
+	}
+	if err := w.Flush(); err != nil {
+		log.Println(err)
+	}
+}