1/* vi: set sw=4 ts=4: */
2/* fold -- wrap each input line to fit in specified width.
3
4   Written by David MacKenzie, djm@gnu.ai.mit.edu.
5   Copyright (C) 91, 1995-2002 Free Software Foundation, Inc.
6
7   Modified for busybox based on coreutils v 5.0
8   Copyright (C) 2003 Glenn McGrath <bug1@iinet.net.au>
9
10   Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
11*/
12
13#include "libbb.h"
14
15static unsigned long flags;
16#define FLAG_COUNT_BYTES	1
17#define FLAG_BREAK_SPACES	2
18#define FLAG_WIDTH			4
19
20/* Assuming the current column is COLUMN, return the column that
21   printing C will move the cursor to.
22   The first column is 0. */
23
24static int adjust_column(int column, char c)
25{
26	if (!(flags & FLAG_COUNT_BYTES)) {
27		if (c == '\b') {
28			if (column > 0)
29				column--;
30		} else if (c == '\r')
31			column = 0;
32		else if (c == '\t')
33			column = column + 8 - column % 8;
34		else			/* if (isprint (c)) */
35			column++;
36	} else
37		column++;
38	return column;
39}
40
41int fold_main(int argc, char **argv);
42int fold_main(int argc, char **argv)
43{
44	char *line_out = NULL;
45	int allocated_out = 0;
46	char *w_opt;
47	int width = 80;
48	int i;
49	int errs = 0;
50
51	if (ENABLE_INCLUDE_SUSv2) {
52		/* Turn any numeric options into -w options.  */
53		for (i = 1; i < argc; i++) {
54			char const *a = argv[i];
55
56			if (*a++ == '-') {
57				if (*a == '-' && !a[1])
58					break;
59				if (isdigit(*a)) {
60					argv[i] = xasprintf("-w%s", a);
61				}
62			}
63		}
64	}
65
66	flags = getopt32(argv, "bsw:", &w_opt);
67	if (flags & FLAG_WIDTH)
68		width = xatoul_range(w_opt, 1, 10000);
69
70	argv += optind;
71	if (!*argv) {
72		*--argv = (char*)"-";
73	}
74
75	do {
76		FILE *istream = fopen_or_warn_stdin(*argv);
77		int c;
78		int column = 0;		/* Screen column where next char will go. */
79		int offset_out = 0;	/* Index in `line_out' for next char. */
80
81		if (istream == NULL) {
82			errs |= EXIT_FAILURE;
83			continue;
84		}
85
86		while ((c = getc(istream)) != EOF) {
87			if (offset_out + 1 >= allocated_out) {
88				allocated_out += 1024;
89				line_out = xrealloc(line_out, allocated_out);
90			}
91
92			if (c == '\n') {
93				line_out[offset_out++] = c;
94				fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
95				column = offset_out = 0;
96				continue;
97			}
98 rescan:
99			column = adjust_column(column, c);
100
101			if (column > width) {
102				/* This character would make the line too long.
103				   Print the line plus a newline, and make this character
104				   start the next line. */
105				if (flags & FLAG_BREAK_SPACES) {
106					/* Look for the last blank. */
107					int logical_end;
108
109					for (logical_end = offset_out - 1; logical_end >= 0; logical_end--) {
110						if (isblank(line_out[logical_end])) {
111							break;
112						}
113					}
114					if (logical_end >= 0) {
115						/* Found a blank.  Don't output the part after it. */
116						logical_end++;
117						fwrite(line_out, sizeof(char), (size_t) logical_end, stdout);
118						putchar('\n');
119						/* Move the remainder to the beginning of the next line.
120						   The areas being copied here might overlap. */
121						memmove(line_out, line_out + logical_end, offset_out - logical_end);
122						offset_out -= logical_end;
123						for (column = i = 0; i < offset_out; i++) {
124							column = adjust_column(column, line_out[i]);
125						}
126						goto rescan;
127					}
128				} else {
129					if (offset_out == 0) {
130						line_out[offset_out++] = c;
131						continue;
132					}
133				}
134				line_out[offset_out++] = '\n';
135				fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
136				column = offset_out = 0;
137				goto rescan;
138			}
139
140			line_out[offset_out++] = c;
141		}
142
143		if (offset_out) {
144			fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
145		}
146
147		if (ferror(istream) || fclose_if_not_stdin(istream)) {
148			bb_perror_msg("%s", *argv);	/* Avoid multibyte problems. */
149			errs |= EXIT_FAILURE;
150		}
151	} while (*++argv);
152
153	fflush_stdout_and_exit(errs);
154}
155