Annotation of src/usr.bin/fold/fold.c, Revision 1.18
1.18 ! schwarze 1: /* $OpenBSD: fold.c,v 1.17 2015/10/09 01:37:07 deraadt Exp $ */
1.1 deraadt 2: /* $NetBSD: fold.c,v 1.6 1995/09/01 01:42:44 jtc Exp $ */
3:
4: /*-
5: * Copyright (c) 1990, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * This code is derived from software contributed to Berkeley by
9: * Kevin Ruddy.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
1.7 millert 19: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 20: * may be used to endorse or promote products derived from this software
21: * without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
1.18 ! schwarze 36: #include <ctype.h>
! 37: #include <err.h>
! 38: #include <limits.h>
! 39: #include <locale.h>
1.1 deraadt 40: #include <stdio.h>
41: #include <stdlib.h>
42: #include <string.h>
43: #include <unistd.h>
1.18 ! schwarze 44: #include <wchar.h>
1.1 deraadt 45:
46: #define DEFLINEWIDTH 80
47:
1.15 tedu 48: static void fold(unsigned int);
1.18 ! schwarze 49: static int isu8cont(unsigned char);
1.13 millert 50: static __dead void usage(void);
1.18 ! schwarze 51:
1.1 deraadt 52: int count_bytes = 0;
53: int split_words = 0;
54:
55: int
1.8 deraadt 56: main(int argc, char *argv[])
1.1 deraadt 57: {
1.15 tedu 58: int ch, lastch, newarg, prevoptind;
59: unsigned int width;
1.11 tedu 60: const char *errstr;
1.1 deraadt 61:
1.18 ! schwarze 62: setlocale(LC_CTYPE, "");
! 63:
1.17 deraadt 64: if (pledge("stdio rpath", NULL) == -1)
65: err(1, "pledge");
1.16 deraadt 66:
1.13 millert 67: width = 0;
68: lastch = '\0';
69: prevoptind = 1;
70: newarg = 1;
71: while ((ch = getopt(argc, argv, "0123456789bsw:")) != -1) {
1.1 deraadt 72: switch (ch) {
73: case 'b':
74: count_bytes = 1;
75: break;
76: case 's':
77: split_words = 1;
78: break;
79: case 'w':
1.15 tedu 80: width = strtonum(optarg, 1, UINT_MAX, &errstr);
1.11 tedu 81: if (errstr != NULL)
82: errx(1, "illegal width value, %s: %s", errstr,
83: optarg);
1.1 deraadt 84: break;
85: case '0': case '1': case '2': case '3': case '4':
86: case '5': case '6': case '7': case '8': case '9':
1.13 millert 87: if (newarg)
88: width = 0;
89: else if (!isdigit(lastch))
90: usage();
1.15 tedu 91: if (width > UINT_MAX / 10 - 1)
1.13 millert 92: errx(1, "illegal width value, too large");
93: width = (width * 10) + (ch - '0');
94: if (width < 1)
95: errx(1, "illegal width value, too small");
1.1 deraadt 96: break;
97: default:
1.13 millert 98: usage();
1.1 deraadt 99: }
1.13 millert 100: lastch = ch;
101: newarg = optind != prevoptind;
102: prevoptind = optind;
103: }
1.1 deraadt 104: argv += optind;
105: argc -= optind;
106:
1.13 millert 107: if (width == 0)
1.1 deraadt 108: width = DEFLINEWIDTH;
109:
1.16 deraadt 110: if (!*argv) {
1.17 deraadt 111: if (pledge("stdio", NULL) == -1)
112: err(1, "pledge");
1.1 deraadt 113: fold(width);
1.16 deraadt 114: } else {
115: for (; *argv; ++argv) {
116: if (!freopen(*argv, "r", stdin))
117: err(1, "%s", *argv);
118: else
119: fold(width);
120: }
121: }
1.18 ! schwarze 122: return 0;
1.1 deraadt 123: }
124:
125: /*
126: * Fold the contents of standard input to fit within WIDTH columns
127: * (or bytes) and write to standard output.
128: *
129: * If split_words is set, split the line at the last space character
130: * on the line. This flag necessitates storing the line in a buffer
131: * until the current column > width, or a newline or EOF is read.
132: *
133: * The buffer can grow larger than WIDTH due to backspaces and carriage
134: * returns embedded in the input stream.
135: */
136: static void
1.18 ! schwarze 137: fold(unsigned int max_width)
1.1 deraadt 138: {
1.18 ! schwarze 139: static char *buf = NULL;
! 140: static size_t bufsz = 2048;
! 141: char *cp; /* Current mb character. */
! 142: char *np; /* Next mb character. */
! 143: char *sp; /* To search for the last space. */
! 144: char *nbuf; /* For buffer reallocation. */
! 145: wchar_t wc; /* Current wide character. */
! 146: int ch; /* Last byte read. */
! 147: int len; /* Bytes in the current mb character. */
! 148: unsigned int col; /* Current display position. */
! 149: int width; /* Display width of wc. */
! 150:
! 151: if (buf == NULL && (buf = malloc(bufsz)) == NULL)
! 152: err(1, NULL);
! 153:
! 154: np = cp = buf;
! 155: ch = 0;
! 156: col = 0;
! 157:
! 158: while (ch != EOF) { /* Loop on input characters. */
! 159: while ((ch = getchar()) != EOF) { /* Loop on input bytes. */
! 160: if (np + 1 == buf + bufsz) {
! 161: nbuf = reallocarray(buf, 2, bufsz);
! 162: if (nbuf == NULL)
! 163: err(1, NULL);
! 164: bufsz *= 2;
! 165: cp = nbuf + (cp - buf);
! 166: np = nbuf + (np - buf);
! 167: buf = nbuf;
! 168: }
! 169: *np++ = ch;
! 170:
! 171: /*
! 172: * Read up to and including the first byte of
! 173: * the next character, such that we are sure
! 174: * to have a complete character in the buffer.
! 175: * There is no need to read more than five bytes
! 176: * ahead, since UTF-8 characters are four bytes
! 177: * long at most.
! 178: */
! 179:
! 180: if (np - cp > 4 || (np - cp > 1 && !isu8cont(ch)))
! 181: break;
1.1 deraadt 182: }
183:
1.18 ! schwarze 184: while (cp < np) { /* Loop on output characters. */
! 185:
! 186: /* Handle end of line and backspace. */
! 187:
! 188: if (*cp == '\n' || (*cp == '\r' && !count_bytes)) {
! 189: fwrite(buf, 1, ++cp - buf, stdout);
! 190: memmove(buf, cp, np - cp);
! 191: np = buf + (np - cp);
! 192: cp = buf;
! 193: col = 0;
! 194: continue;
! 195: }
! 196: if (*cp == '\b' && !count_bytes) {
! 197: if (col)
! 198: col--;
! 199: cp++;
! 200: continue;
1.1 deraadt 201: }
202:
1.18 ! schwarze 203: /*
! 204: * Measure display width.
! 205: * Process the last byte only if
! 206: * end of file was reached.
! 207: */
! 208:
! 209: if (np - cp > (ch != EOF)) {
! 210: len = 1;
! 211: width = 1;
! 212:
! 213: if (*cp == '\t') {
! 214: if (count_bytes == 0)
! 215: width = 8 - (col & 7);
! 216: } else if ((len = mbtowc(&wc, cp,
! 217: np - cp)) < 1)
! 218: len = 1;
! 219: else if (count_bytes)
! 220: width = len;
! 221: else if ((width = wcwidth(wc)) < 0)
! 222: width = 1;
! 223:
! 224: col += width;
! 225: if (col <= max_width || cp == buf) {
! 226: cp += len;
! 227: continue;
! 228: }
! 229: }
1.1 deraadt 230:
1.18 ! schwarze 231: /* Line break required. */
1.1 deraadt 232:
1.18 ! schwarze 233: if (col > max_width) {
! 234: if (split_words) {
! 235: for (sp = cp; sp > buf; sp--) {
! 236: if (sp[-1] == ' ') {
! 237: cp = sp;
! 238: break;
! 239: }
! 240: }
! 241: }
! 242: fwrite(buf, 1, cp - buf, stdout);
! 243: putchar('\n');
! 244: memmove(buf, cp, np - cp);
! 245: np = buf + (np - cp);
! 246: cp = buf;
1.1 deraadt 247: col = 0;
1.18 ! schwarze 248: continue;
1.1 deraadt 249: }
250:
1.18 ! schwarze 251: /* Need more input. */
1.1 deraadt 252:
1.18 ! schwarze 253: break;
1.1 deraadt 254: }
255: }
1.18 ! schwarze 256: fwrite(buf, 1, np - buf, stdout);
1.1 deraadt 257:
1.18 ! schwarze 258: if (ferror(stdin))
! 259: err(1, NULL);
1.1 deraadt 260: }
261:
1.18 ! schwarze 262: static int
! 263: isu8cont(unsigned char c)
1.1 deraadt 264: {
1.18 ! schwarze 265: return MB_CUR_MAX > 1 && (c & (0x80 | 0x40)) == 0x80;
1.13 millert 266: }
267:
268: static __dead void
269: usage(void)
270: {
271: (void)fprintf(stderr, "usage: fold [-bs] [-w width] [file ...]\n");
272: exit(1);
1.1 deraadt 273: }