version 1.17, 2015/10/09 01:37:07 |
version 1.18, 2016/05/23 10:31:42 |
|
|
* SUCH DAMAGE. |
* SUCH DAMAGE. |
*/ |
*/ |
|
|
|
#include <ctype.h> |
|
#include <err.h> |
|
#include <limits.h> |
|
#include <locale.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
#include <unistd.h> |
#include <unistd.h> |
#include <ctype.h> |
#include <wchar.h> |
#include <err.h> |
|
#include <limits.h> |
|
|
|
#define DEFLINEWIDTH 80 |
#define DEFLINEWIDTH 80 |
|
|
static void fold(unsigned int); |
static void fold(unsigned int); |
static unsigned int new_column_position(unsigned int, int); |
static int isu8cont(unsigned char); |
static __dead void usage(void); |
static __dead void usage(void); |
|
|
int count_bytes = 0; |
int count_bytes = 0; |
int split_words = 0; |
int split_words = 0; |
|
|
|
|
unsigned int width; |
unsigned int width; |
const char *errstr; |
const char *errstr; |
|
|
|
setlocale(LC_CTYPE, ""); |
|
|
if (pledge("stdio rpath", NULL) == -1) |
if (pledge("stdio rpath", NULL) == -1) |
err(1, "pledge"); |
err(1, "pledge"); |
|
|
|
|
for (; *argv; ++argv) { |
for (; *argv; ++argv) { |
if (!freopen(*argv, "r", stdin)) |
if (!freopen(*argv, "r", stdin)) |
err(1, "%s", *argv); |
err(1, "%s", *argv); |
/* NOTREACHED */ |
|
else |
else |
fold(width); |
fold(width); |
} |
} |
} |
} |
exit(0); |
return 0; |
} |
} |
|
|
/* |
/* |
|
|
* returns embedded in the input stream. |
* returns embedded in the input stream. |
*/ |
*/ |
static void |
static void |
fold(unsigned int width) |
fold(unsigned int max_width) |
{ |
{ |
static char *buf = NULL; |
static char *buf = NULL; |
static int buf_max = 0; |
static size_t bufsz = 2048; |
int ch; |
char *cp; /* Current mb character. */ |
unsigned int col, indx; |
char *np; /* Next mb character. */ |
|
char *sp; /* To search for the last space. */ |
|
char *nbuf; /* For buffer reallocation. */ |
|
wchar_t wc; /* Current wide character. */ |
|
int ch; /* Last byte read. */ |
|
int len; /* Bytes in the current mb character. */ |
|
unsigned int col; /* Current display position. */ |
|
int width; /* Display width of wc. */ |
|
|
col = indx = 0; |
if (buf == NULL && (buf = malloc(bufsz)) == NULL) |
while ((ch = getchar()) != EOF) { |
err(1, NULL); |
if (ch == '\n') { |
|
if (indx != 0) |
|
fwrite(buf, 1, indx, stdout); |
|
putchar('\n'); |
|
col = indx = 0; |
|
continue; |
|
} |
|
|
|
col = new_column_position(col, ch); |
np = cp = buf; |
if (col > width) { |
ch = 0; |
unsigned int i, last_space; |
col = 0; |
|
|
if (split_words) { |
while (ch != EOF) { /* Loop on input characters. */ |
for (i = 0, last_space = -1; i < indx; i++) |
while ((ch = getchar()) != EOF) { /* Loop on input bytes. */ |
if(buf[i] == ' ') |
if (np + 1 == buf + bufsz) { |
last_space = i; |
nbuf = reallocarray(buf, 2, bufsz); |
|
if (nbuf == NULL) |
|
err(1, NULL); |
|
bufsz *= 2; |
|
cp = nbuf + (cp - buf); |
|
np = nbuf + (np - buf); |
|
buf = nbuf; |
} |
} |
|
*np++ = ch; |
|
|
if (split_words && last_space != -1) { |
/* |
last_space++; |
* Read up to and including the first byte of |
|
* the next character, such that we are sure |
|
* to have a complete character in the buffer. |
|
* There is no need to read more than five bytes |
|
* ahead, since UTF-8 characters are four bytes |
|
* long at most. |
|
*/ |
|
|
fwrite(buf, 1, last_space, stdout); |
if (np - cp > 4 || (np - cp > 1 && !isu8cont(ch))) |
memmove(buf, buf+last_space, indx-last_space); |
break; |
|
} |
|
|
indx -= last_space; |
while (cp < np) { /* Loop on output characters. */ |
|
|
|
/* Handle end of line and backspace. */ |
|
|
|
if (*cp == '\n' || (*cp == '\r' && !count_bytes)) { |
|
fwrite(buf, 1, ++cp - buf, stdout); |
|
memmove(buf, cp, np - cp); |
|
np = buf + (np - cp); |
|
cp = buf; |
col = 0; |
col = 0; |
for (i = 0; i < indx; i++) { |
continue; |
col = new_column_position(col, buf[i]); |
|
} |
|
} else { |
|
fwrite(buf, 1, indx, stdout); |
|
col = indx = 0; |
|
} |
} |
putchar('\n'); |
if (*cp == '\b' && !count_bytes) { |
|
if (col) |
|
col--; |
|
cp++; |
|
continue; |
|
} |
|
|
/* calculate the column position for the next line. */ |
/* |
col = new_column_position(col, ch); |
* Measure display width. |
} |
* Process the last byte only if |
|
* end of file was reached. |
|
*/ |
|
|
if (indx + 1 > buf_max) { |
if (np - cp > (ch != EOF)) { |
int newmax = buf_max + 2048; |
len = 1; |
char *newbuf; |
width = 1; |
|
|
/* Allocate buffer in LINE_MAX increments */ |
if (*cp == '\t') { |
if ((newbuf = realloc(buf, newmax)) == NULL) { |
if (count_bytes == 0) |
err(1, NULL); |
width = 8 - (col & 7); |
/* NOTREACHED */ |
} else if ((len = mbtowc(&wc, cp, |
|
np - cp)) < 1) |
|
len = 1; |
|
else if (count_bytes) |
|
width = len; |
|
else if ((width = wcwidth(wc)) < 0) |
|
width = 1; |
|
|
|
col += width; |
|
if (col <= max_width || cp == buf) { |
|
cp += len; |
|
continue; |
|
} |
} |
} |
buf = newbuf; |
|
buf_max = newmax; |
|
} |
|
buf[indx++] = ch; |
|
} |
|
|
|
if (indx != 0) |
/* Line break required. */ |
fwrite(buf, 1, indx, stdout); |
|
} |
|
|
|
/* |
if (col > max_width) { |
* calculate the column position |
if (split_words) { |
*/ |
for (sp = cp; sp > buf; sp--) { |
static unsigned int |
if (sp[-1] == ' ') { |
new_column_position(unsigned int col, int ch) |
cp = sp; |
{ |
break; |
if (!count_bytes) { |
} |
switch (ch) { |
} |
case '\b': |
} |
if (col > 0) |
fwrite(buf, 1, cp - buf, stdout); |
--col; |
putchar('\n'); |
|
memmove(buf, cp, np - cp); |
|
np = buf + (np - cp); |
|
cp = buf; |
|
col = 0; |
|
continue; |
|
} |
|
|
|
/* Need more input. */ |
|
|
break; |
break; |
case '\r': |
|
col = 0; |
|
break; |
|
case '\t': |
|
col = (col + 8) & ~7; |
|
break; |
|
default: |
|
++col; |
|
break; |
|
} |
} |
} else { |
|
++col; |
|
} |
} |
|
fwrite(buf, 1, np - buf, stdout); |
|
|
return col; |
if (ferror(stdin)) |
|
err(1, NULL); |
|
} |
|
|
|
static int |
|
isu8cont(unsigned char c) |
|
{ |
|
return MB_CUR_MAX > 1 && (c & (0x80 | 0x40)) == 0x80; |
} |
} |
|
|
static __dead void |
static __dead void |