summaryrefslogtreecommitdiff
path: root/coreutils/fold.c
blob: 01849615e9764818a19c821cad1790fbc40fabd5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* vi: set sw=4 ts=4: */
/* fold -- wrap each input line to fit in specified width.

   Written by David MacKenzie, djm@gnu.ai.mit.edu.
   Copyright (C) 91, 1995-2002 Free Software Foundation, Inc.

   Modified for busybox based on coreutils v 5.0
   Copyright (C) 2003 Glenn McGrath <bug1@iinet.net.au>

   Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*/

#include "libbb.h"

static unsigned long flags;
#define FLAG_COUNT_BYTES	1
#define FLAG_BREAK_SPACES	2
#define FLAG_WIDTH			4

/* Assuming the current column is COLUMN, return the column that
   printing C will move the cursor to.
   The first column is 0. */

static int adjust_column(int column, char c)
{
	if (!(flags & FLAG_COUNT_BYTES)) {
		if (c == '\b') {
			if (column > 0)
				column--;
		} else if (c == '\r')
			column = 0;
		else if (c == '\t')
			column = column + 8 - column % 8;
		else			/* if (isprint (c)) */
			column++;
	} else
		column++;
	return column;
}

int fold_main(int argc, char **argv);
int fold_main(int argc, char **argv)
{
	char *line_out = NULL;
	int allocated_out = 0;
	char *w_opt;
	int width = 80;
	int i;
	int errs = 0;

	if (ENABLE_INCLUDE_SUSv2) {
		/* Turn any numeric options into -w options.  */
		for (i = 1; i < argc; i++) {
			char const *a = argv[i];

			if (*a++ == '-') {
				if (*a == '-' && !a[1])
					break;
				if (isdigit(*a)) {
					argv[i] = xasprintf("-w%s", a);
				}
			}
		}
	}

	flags = getopt32(argv, "bsw:", &w_opt);
	if (flags & FLAG_WIDTH)
		width = xatoul_range(w_opt, 1, 10000);

	argv += optind;
	if (!*argv) {
		*--argv = (char*)"-";
	}

	do {
		FILE *istream = fopen_or_warn_stdin(*argv);
		int c;
		int column = 0;		/* Screen column where next char will go. */
		int offset_out = 0;	/* Index in `line_out' for next char. */

		if (istream == NULL) {
			errs |= EXIT_FAILURE;
			continue;
		}

		while ((c = getc(istream)) != EOF) {
			if (offset_out + 1 >= allocated_out) {
				allocated_out += 1024;
				line_out = xrealloc(line_out, allocated_out);
			}

			if (c == '\n') {
				line_out[offset_out++] = c;
				fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
				column = offset_out = 0;
				continue;
			}
 rescan:
			column = adjust_column(column, c);

			if (column > width) {
				/* This character would make the line too long.
				   Print the line plus a newline, and make this character
				   start the next line. */
				if (flags & FLAG_BREAK_SPACES) {
					/* Look for the last blank. */
					int logical_end;

					for (logical_end = offset_out - 1; logical_end >= 0; logical_end--) {
						if (isblank(line_out[logical_end])) {
							break;
						}
					}
					if (logical_end >= 0) {
						/* Found a blank.  Don't output the part after it. */
						logical_end++;
						fwrite(line_out, sizeof(char), (size_t) logical_end, stdout);
						putchar('\n');
						/* Move the remainder to the beginning of the next line.
						   The areas being copied here might overlap. */
						memmove(line_out, line_out + logical_end, offset_out - logical_end);
						offset_out -= logical_end;
						for (column = i = 0; i < offset_out; i++) {
							column = adjust_column(column, line_out[i]);
						}
						goto rescan;
					}
				} else {
					if (offset_out == 0) {
						line_out[offset_out++] = c;
						continue;
					}
				}
				line_out[offset_out++] = '\n';
				fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
				column = offset_out = 0;
				goto rescan;
			}

			line_out[offset_out++] = c;
		}

		if (offset_out) {
			fwrite(line_out, sizeof(char), (size_t) offset_out, stdout);
		}

		if (ferror(istream) || fclose_if_not_stdin(istream)) {
			bb_perror_msg("%s", *argv);	/* Avoid multibyte problems. */
			errs |= EXIT_FAILURE;
		}
	} while (*++argv);

	fflush_stdout_and_exit(errs);
}