master xplshn/aruu / cmd / posix / fold.c
  1/* See LICENSE file for copyright and license details. */
  2
  3
  4#include <ctype.h>
  5#include <stdint.h>
  6#include <stdio.h>
  7#include <stdlib.h>
  8#include <string.h>
  9
 10#include "text.h"
 11#include "util.h"
 12#include "utf.h"
 13
 14static int    bflag = 0;
 15static int    sflag = 0;
 16static size_t width = 80;
 17
 18static void
 19foldline(struct line *l, const char *fname) {
 20	size_t i, col, last, spacesect, len;
 21	Rune r;
 22	int runelen;
 23
 24	for (i = 0, last = 0, col = 0, spacesect = 0; i < l->len; i += runelen) {
 25		if (col >= width && ((l->data[i] != '\r' && l->data[i] != '\b') || bflag)) {
 26			if (bflag && col > width)
 27				i -= runelen;	/* never split a character */
 28			len = ((sflag && spacesect) ? spacesect : i) - last;
 29			if (fwrite(l->data + last, 1, len, stdout) != len)
 30				eprintf("fwrite <stdout>:");
 31			if (l->data[i] != '\n')
 32				putchar('\n');
 33			if (sflag && spacesect)
 34				i = spacesect;
 35			last = i;
 36			col = 0;
 37			spacesect = 0;
 38		}
 39		runelen = charntorune(&r, l->data + i, l->len - i);
 40		if (!runelen || r == Runeerror)
 41			eprintf("charntorune: %s: invalid utf\n", fname);
 42		if (sflag && isblankrune(r))
 43			spacesect = i + runelen;
 44		if (!bflag && iscntrl(l->data[i])) {
 45			switch(l->data[i]) {
 46			case '\b':
 47				col -= (col > 0);
 48				break;
 49			case '\r':
 50				col = 0;
 51				break;
 52			case '\t':
 53				col += (8 - (col % 8));
 54				if (col >= width)
 55					i--;
 56				break;
 57			}
 58		} else {
 59			col += bflag ? runelen : 1;
 60		}
 61	}
 62	if (l->len - last)
 63		fwrite(l->data + last, 1, l->len - last, stdout);
 64}
 65
 66static void
 67fold(FILE *fp, const char *fname)
 68{
 69	static struct line line;
 70	static size_t size = 0;
 71	ssize_t len;
 72
 73	while ((len = getline(&line.data, &size, fp)) > 0) {
 74		line.len = len;
 75		foldline(&line, fname);
 76	}
 77	if (ferror(fp))
 78		eprintf("getline %s:", fname);
 79}
 80
 81static void
 82usage(void)
 83{
 84	eprintf("usage: %s [-bs] [-w num | -num] [FILE ...]\n", argv0);
 85}
 86
 87// ?man fold: wrap lines to fit width
 88// ?man arguments: FILE ...
 89// ?man wrap input lines to fit a specified width
 90int
 91main(int argc, char *argv[])
 92{
 93	FILE *fp;
 94	int ret = 0;
 95
 96	ARGBEGIN {
 97	// ?man -b: specify block size or base directory
 98	case 'b':
 99		bflag = 1;
100		break;
101	// ?man -s: silent mode or print summary
102	case 's':
103		sflag = 1;
104		break;
105	// ?man -w:num: wait for completion
106	case 'w':
107		width = estrtonum(EARGF(usage()), 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
108		break;
109	// ?man ARGNUM: specify RGNUM option
110	ARGNUM:
111		if (!(width = ARGNUMF()))
112			eprintf("illegal width value, too small\n");
113		break;
114	default:
115		usage();
116	} ARGEND
117
118	if (!argc) {
119		fold(stdin, "<stdin>");
120	} else {
121		for (; *argv; argc--, argv++) {
122			if (!strcmp(*argv, "-")) {
123				*argv = "<stdin>";
124				fp = stdin;
125			} else if (!(fp = fopen(*argv, "r"))) {
126				weprintf("fopen %s:", *argv);
127				ret = 1;
128				continue;
129			}
130			fold(fp, *argv);
131			if (fp != stdin && fshut(fp, *argv))
132				ret = 1;
133		}
134	}
135
136	ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
137
138	return ret;
139}