version 1.11, 2015/10/09 01:37:06 |
version 1.12, 2016/01/18 20:31:36 |
|
|
#include <err.h> |
#include <err.h> |
#include <errno.h> |
#include <errno.h> |
#include <limits.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 <wchar.h> |
|
|
#define TAB 8 |
#define TAB 8 |
|
|
void check(FILE *); |
|
void usage(void); |
void usage(void); |
|
|
int |
int |
main(int argc, char *argv[]) |
main(int argc, char *argv[]) |
{ |
{ |
u_long column, start, stop; |
char *line, *p; |
int ch; |
ssize_t linesz; |
char *p; |
wchar_t wc; |
|
u_long column, newcol, start, stop; |
|
int ch, len, width; |
|
|
|
setlocale(LC_ALL, ""); |
|
|
if (pledge("stdio", NULL) == -1) |
if (pledge("stdio", NULL) == -1) |
err(1, "pledge"); |
err(1, "pledge"); |
|
|
|
|
if (stop && start > stop) |
if (stop && start > stop) |
err(1, "illegal start and stop columns"); |
err(1, "illegal start and stop columns"); |
|
|
for (column = 0;;) { |
line = NULL; |
switch (ch = getchar()) { |
while (getline(&line, &linesz, stdin) != -1) { |
case EOF: |
column = 0; |
check(stdin); |
width = 0; |
break; |
for (p = line; *p != '\0'; p += len) { |
case '\b': |
len = 1; |
if (column) |
switch (*p) { |
--column; |
case '\n': |
break; |
putchar('\n'); |
case '\n': |
continue; |
column = 0; |
case '\b': |
break; |
/* |
case '\t': |
* Pass it through if the previous character |
column = (column + TAB) & ~(TAB - 1); |
* was in scope, still represented by the |
break; |
* current value of "column". |
default: |
* Allow an optional second backspace |
++column; |
* after a double-width character. |
break; |
*/ |
} |
if (start == 0 || column < start || |
|
(stop > 0 && |
|
column > stop + (width > 1))) { |
|
putchar('\b'); |
|
if (width > 1 && p[1] == '\b') |
|
putchar('\b'); |
|
} |
|
if (width > 1 && p[1] == '\b') |
|
p++; |
|
column -= width; |
|
continue; |
|
case '\t': |
|
newcol = (column + TAB) & ~(TAB - 1); |
|
if (start == 0 || newcol < start) { |
|
putchar('\t'); |
|
column = newcol; |
|
} else |
|
/* |
|
* Expand tabs that intersect or |
|
* follow deleted columns. |
|
*/ |
|
while (column < newcol) |
|
if (++column < start || |
|
(stop > 0 && |
|
column > stop)) |
|
putchar(' '); |
|
continue; |
|
default: |
|
break; |
|
} |
|
|
if ((!start || column < start || (stop && column > stop)) && |
/* |
putchar(ch) == EOF) |
* Handle the three cases of invalid bytes, |
check(stdout); |
* non-printable, and printable characters. |
} |
*/ |
} |
|
|
|
void |
if ((len = mbtowc(&wc, p, MB_CUR_MAX)) == -1) { |
check(FILE *stream) |
(void)mbtowc(NULL, NULL, MB_CUR_MAX); |
{ |
len = 1; |
if (feof(stream)) |
width = 1; |
exit(0); |
} else if ((width = wcwidth(wc)) == -1) |
if (ferror(stream)) |
width = 1; |
err(1, "%s", stream == stdin ? "stdin" : "stdout"); |
|
|
/* |
|
* If the character completely fits before or |
|
* after the cut, keep it; otherwise, skip it. |
|
*/ |
|
|
|
if ((start == 0 || column + width < start || |
|
(stop > 0 && column + (width > 0) > stop))) |
|
fwrite(p, 1, len, stdout); |
|
|
|
/* |
|
* If the cut cuts the character in half |
|
* and no backspace follows, |
|
* print a blank for correct columnation. |
|
*/ |
|
|
|
else if (width > 1 && p[len] != '\b' && |
|
(start == 0 || column + 1 < start || |
|
(stop > 0 && column + width > stop))) |
|
putchar(' '); |
|
|
|
column += width; |
|
} |
|
} |
|
if (ferror(stdin)) |
|
err(1, "stdin"); |
|
if (ferror(stdout)) |
|
err(1, "stdout"); |
|
return 0; |
} |
} |
|
|
void |
void |