11590Srgrimes/*-
21590Srgrimes * Copyright (c) 1990, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * This code is derived from software contributed to Berkeley by
61590Srgrimes * Kevin Ruddy.
71590Srgrimes *
81590Srgrimes * Redistribution and use in source and binary forms, with or without
91590Srgrimes * modification, are permitted provided that the following conditions
101590Srgrimes * are met:
111590Srgrimes * 1. Redistributions of source code must retain the above copyright
121590Srgrimes *    notice, this list of conditions and the following disclaimer.
131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer in the
151590Srgrimes *    documentation and/or other materials provided with the distribution.
161590Srgrimes * 4. Neither the name of the University nor the names of its contributors
171590Srgrimes *    may be used to endorse or promote products derived from this software
181590Srgrimes *    without specific prior written permission.
191590Srgrimes *
201590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301590Srgrimes * SUCH DAMAGE.
311590Srgrimes */
321590Srgrimes
331590Srgrimes#ifndef lint
3427270Scharnierstatic const char copyright[] =
351590Srgrimes"@(#) Copyright (c) 1990, 1993\n\
361590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
371590Srgrimes#endif /* not lint */
381590Srgrimes
391590Srgrimes#ifndef lint
4027270Scharnier#if 0
411590Srgrimesstatic char sccsid[] = "@(#)fold.c	8.1 (Berkeley) 6/6/93";
4227270Scharnier#endif
431590Srgrimes#endif /* not lint */
441590Srgrimes
4587751Scharnier#include <sys/cdefs.h>
4687751Scharnier__FBSDID("$FreeBSD$");
4787751Scharnier
4827270Scharnier#include <err.h>
4994978Stjr#include <limits.h>
5095033Sache#include <locale.h>
511590Srgrimes#include <stdio.h>
5227270Scharnier#include <stdlib.h>
53200462Sdelphij#include <string.h>
5427270Scharnier#include <unistd.h>
55131056Stjr#include <wchar.h>
56131056Stjr#include <wctype.h>
571590Srgrimes
581590Srgrimes#define	DEFLINEWIDTH	80
591590Srgrimes
6092920Simpvoid fold(int);
61131056Stjrstatic int newpos(int, wint_t);
6292920Simpstatic void usage(void);
6327270Scharnier
64227165Sedstatic int bflag;		/* Count bytes, not columns */
65227165Sedstatic int sflag;		/* Split on word boundaries */
6694978Stjr
6727270Scharnierint
68102944Sdwmalonemain(int argc, char **argv)
691590Srgrimes{
70214893Sdumbbell	int ch, previous_ch;
7197266Stjr	int rval, width;
721590Srgrimes
7395033Sache	(void) setlocale(LC_CTYPE, "");
7495033Sache
751590Srgrimes	width = -1;
76214893Sdumbbell	previous_ch = 0;
77214893Sdumbbell	while ((ch = getopt(argc, argv, "0123456789bsw:")) != -1) {
781590Srgrimes		switch (ch) {
7994978Stjr		case 'b':
8094978Stjr			bflag = 1;
8194978Stjr			break;
8294978Stjr		case 's':
8394978Stjr			sflag = 1;
8494978Stjr			break;
851590Srgrimes		case 'w':
861590Srgrimes			if ((width = atoi(optarg)) <= 0) {
8727270Scharnier				errx(1, "illegal width value");
881590Srgrimes			}
891590Srgrimes			break;
901590Srgrimes		case '0': case '1': case '2': case '3': case '4':
911590Srgrimes		case '5': case '6': case '7': case '8': case '9':
92214893Sdumbbell			/* Accept a width as eg. -30. Note that a width
93214893Sdumbbell			 * specified using the -w option is always used prior
94214893Sdumbbell			 * to this undocumented option. */
95214893Sdumbbell			switch (previous_ch) {
96214893Sdumbbell			case '0': case '1': case '2': case '3': case '4':
97214893Sdumbbell			case '5': case '6': case '7': case '8': case '9':
98214893Sdumbbell				/* The width is a number with multiple digits:
99214893Sdumbbell				 * add the last one. */
100214893Sdumbbell				width = width * 10 + (ch - '0');
101214893Sdumbbell				break;
102214893Sdumbbell			default:
103214893Sdumbbell				/* Set the width, unless it was previously
104214893Sdumbbell				 * set. For instance, the following options
105214893Sdumbbell				 * would all give a width of 5 and not 10:
106214893Sdumbbell				 *   -10 -w5
107214893Sdumbbell				 *   -5b10
108214893Sdumbbell				 *   -5 -10b */
109214893Sdumbbell				if (width == -1)
110214893Sdumbbell					width = ch - '0';
111214893Sdumbbell				break;
1121590Srgrimes			}
1131590Srgrimes			break;
1141590Srgrimes		default:
11527270Scharnier			usage();
1161590Srgrimes		}
117214893Sdumbbell		previous_ch = ch;
118214893Sdumbbell	}
1191590Srgrimes	argv += optind;
1201590Srgrimes	argc -= optind;
1211590Srgrimes
1221590Srgrimes	if (width == -1)
1231590Srgrimes		width = DEFLINEWIDTH;
12497266Stjr	rval = 0;
1251590Srgrimes	if (!*argv)
1261590Srgrimes		fold(width);
1271590Srgrimes	else for (; *argv; ++argv)
1281590Srgrimes		if (!freopen(*argv, "r", stdin)) {
12997266Stjr			warn("%s", *argv);
13097266Stjr			rval = 1;
1311590Srgrimes		} else
1321590Srgrimes			fold(width);
13397266Stjr	exit(rval);
1341590Srgrimes}
1351590Srgrimes
13627270Scharnierstatic void
137102944Sdwmaloneusage(void)
13827270Scharnier{
13994978Stjr	(void)fprintf(stderr, "usage: fold [-bs] [-w width] [file ...]\n");
14027270Scharnier	exit(1);
14127270Scharnier}
14227270Scharnier
14394978Stjr/*
14494978Stjr * Fold the contents of standard input to fit within WIDTH columns (or bytes)
14594978Stjr * and write to standard output.
14694978Stjr *
14794978Stjr * If sflag is set, split the line at the last space character on the line.
14894978Stjr * This flag necessitates storing the line in a buffer until the current
14994978Stjr * column > width, or a newline or EOF is read.
15094978Stjr *
15194978Stjr * The buffer can grow larger than WIDTH due to backspaces and carriage
15294978Stjr * returns embedded in the input stream.
15394978Stjr */
15427270Scharniervoid
155102944Sdwmalonefold(int width)
1561590Srgrimes{
157131056Stjr	static wchar_t *buf;
15894978Stjr	static int buf_max;
159131056Stjr	int col, i, indx, space;
160131056Stjr	wint_t ch;
1611590Srgrimes
16294978Stjr	col = indx = 0;
163131056Stjr	while ((ch = getwchar()) != WEOF) {
16494978Stjr		if (ch == '\n') {
165131056Stjr			wprintf(L"%.*ls\n", indx, buf);
16694978Stjr			col = indx = 0;
16794978Stjr			continue;
1681590Srgrimes		}
16994978Stjr		if ((col = newpos(col, ch)) > width) {
17094978Stjr			if (sflag) {
17194978Stjr				i = indx;
172131056Stjr				while (--i >= 0 && !iswblank(buf[i]))
17394978Stjr					;
17494978Stjr				space = i;
17594978Stjr			}
17694978Stjr			if (sflag && space != -1) {
17794978Stjr				space++;
178131056Stjr				wprintf(L"%.*ls\n", space, buf);
179131056Stjr				wmemmove(buf, buf + space, indx - space);
18094978Stjr				indx -= space;
18194978Stjr				col = 0;
18294978Stjr				for (i = 0; i < indx; i++)
183131056Stjr					col = newpos(col, buf[i]);
18494978Stjr			} else {
185131056Stjr				wprintf(L"%.*ls\n", indx, buf);
18694978Stjr				col = indx = 0;
18794978Stjr			}
18894978Stjr			col = newpos(col, ch);
1891590Srgrimes		}
19094978Stjr		if (indx + 1 > buf_max) {
19194978Stjr			buf_max += LINE_MAX;
192131056Stjr			buf = realloc(buf, sizeof(*buf) * buf_max);
193131056Stjr			if (buf == NULL)
19494978Stjr				err(1, "realloc()");
19594978Stjr		}
19694978Stjr		buf[indx++] = ch;
19794978Stjr	}
1981590Srgrimes
19994978Stjr	if (indx != 0)
200131056Stjr		wprintf(L"%.*ls", indx, buf);
20194978Stjr}
20294978Stjr
20394978Stjr/*
20494978Stjr * Update the current column position for a character.
20594978Stjr */
20694978Stjrstatic int
207131056Stjrnewpos(int col, wint_t ch)
20894978Stjr{
209131056Stjr	char buf[MB_LEN_MAX];
210131056Stjr	size_t len;
211131056Stjr	int w;
21294978Stjr
213131056Stjr	if (bflag) {
214131056Stjr		len = wcrtomb(buf, ch, NULL);
215131056Stjr		col += len;
216131056Stjr	} else
2171590Srgrimes		switch (ch) {
2181590Srgrimes		case '\b':
2191590Srgrimes			if (col > 0)
2201590Srgrimes				--col;
2211590Srgrimes			break;
2221590Srgrimes		case '\r':
2231590Srgrimes			col = 0;
2241590Srgrimes			break;
2251590Srgrimes		case '\t':
22694978Stjr			col = (col + 8) & ~7;
2271590Srgrimes			break;
2281590Srgrimes		default:
229131056Stjr			if ((w = wcwidth(ch)) > 0)
230131056Stjr				col += w;
2311590Srgrimes			break;
2321590Srgrimes		}
23394978Stjr
23494978Stjr	return (col);
2351590Srgrimes}
236