From 64999c33fd2eb5bed83a9fddd9facf9700207c05 Mon Sep 17 00:00:00 2001
From: stateless <stateless@archlinux.us>
Date: Mon, 17 Jun 2013 12:00:36 +0100
Subject: [PATCH] Add expand(1)

---
 Makefile |    1 +
 expand.1 |   21 ++++++++++++
 expand.c |  108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 130 insertions(+)
 create mode 100644 expand.1
 create mode 100644 expand.c

diff --git a/Makefile b/Makefile
index d567b1f..4c45ff6 100644
--- a/Makefile
+++ b/Makefile
@@ -34,6 +34,7 @@ SRC = \
 	dirname.c  \
 	echo.c     \
 	env.c      \
+	expand.c   \
 	false.c    \
 	fold.c     \
 	grep.c     \
diff --git a/expand.1 b/expand.1
new file mode 100644
index 0000000..82be286
--- /dev/null
+++ b/expand.1
@@ -0,0 +1,21 @@
+.TH EXPAND 1 sbase\-VERSION
+.SH NAME
+expand \- expand tabs to spaces
+.SH SYNOPSIS
+.B expand
+.RB [ \-t
+.IR n ]
+.RI [ file ...]
+.SH DESCRIPTION
+expand processes the named files or the standard input, writing the
+standard output with tabs changed into spaces.  Backspace characters
+are preserved into the output and decrement the column count for tab
+calculations.
+.SH OPTIONS
+.TP
+.BI \-t " n"
+Expand tabs to
+.I n
+spaces.  We currently support only a single numerical argument.
+.SH SEE ALSO
+.IR fold (1)
diff --git a/expand.c b/expand.c
new file mode 100644
index 0000000..68336ec
--- /dev/null
+++ b/expand.c
@@ -0,0 +1,108 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include "util.h"
+
+typedef struct {
+	FILE *fp;
+	const char *name;
+} Fdescr;
+
+static int expand(Fdescr *f, int tabstop);
+
+static void
+usage(void)
+{
+	eprintf("usage: %s [-t n] [file...]\n", argv0);
+}
+
+int
+main(int argc, char *argv[])
+{
+	Fdescr dsc;
+	FILE *fp;
+	int tabstop = 8;
+
+	ARGBEGIN {
+	case 't':
+		tabstop = estrtol(EARGF(usage()), 0);
+		break;
+	default:
+		usage();
+	} ARGEND;
+
+	if (argc == 0) {
+		dsc.name = "<stdin>";
+		dsc.fp = stdin;
+		expand(&dsc, tabstop);
+	} else {
+		for (; argc > 0; argc--) {
+			if (!(fp = fopen(*argv, "r"))) {
+				eprintf("fopen %s:", *argv);
+			}
+			dsc.name = *argv;
+			dsc.fp = fp;
+			expand(&dsc, tabstop);
+			fclose(fp);
+			argv++;
+		}
+	}
+	return 0;
+}
+
+static wint_t
+in(Fdescr *f)
+{
+	wint_t c = fgetwc(f->fp);
+
+	if (c == WEOF && ferror(f->fp))
+		eprintf("'%s' read error:", f->name);
+
+	return c;
+}
+
+static void
+out(wint_t c)
+{
+	putwchar(c);
+	if (ferror(stdout))
+		eprintf("write error:");
+}
+
+static int
+expand(Fdescr *dsc, int tabstop)
+{
+	int col = 0;
+	wint_t c;
+
+	for (;;) {
+		c = in(dsc);
+		if (c == WEOF)
+			break;
+
+		switch (c) {
+		case '\t':
+			do {
+				col++;
+				out(' ');
+			} while (col & (tabstop - 1));
+			break;
+		case '\b':
+			if (col)
+				col--;
+			out(c);
+			break;
+		case '\n':
+			col = 0;
+			out(c);
+			break;
+		default:
+			col++;
+			out(c);
+			break;
+		}
+	}
+
+	return 0;
+}
-- 
1.7.10.4

