1 /* $OpenBSD: colrm.c,v 1.14 2022/12/04 23:50:47 cheloha Exp $ */
2 /* $NetBSD: colrm.c,v 1.4 1995/09/02 05:51:37 jtc Exp $ */
3
4 /*-
5 * Copyright (c) 1991, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include <sys/types.h>
34
35 #include <err.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <locale.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <wchar.h>
44
45 #define TAB 8
46
47 void usage(void);
48
49 int
main(int argc,char * argv[])50 main(int argc, char *argv[])
51 {
52 char *line, *p;
53 ssize_t linesz;
54 wchar_t wc;
55 u_long column, newcol, start, stop;
56 int ch, len, width;
57
58 setlocale(LC_CTYPE, "");
59
60 if (pledge("stdio", NULL) == -1)
61 err(1, "pledge");
62
63 while ((ch = getopt(argc, argv, "")) != -1)
64 switch(ch) {
65 default:
66 usage();
67 }
68 argc -= optind;
69 argv += optind;
70
71 start = stop = 0;
72 switch(argc) {
73 case 2:
74 stop = strtol(argv[1], &p, 10);
75 if (stop <= 0 || *p)
76 errx(1, "illegal column -- %s", argv[1]);
77 /* FALLTHROUGH */
78 case 1:
79 start = strtol(argv[0], &p, 10);
80 if (start <= 0 || *p)
81 errx(1, "illegal column -- %s", argv[0]);
82 break;
83 case 0:
84 break;
85 default:
86 usage();
87 }
88
89 if (stop && start > stop)
90 err(1, "illegal start and stop columns");
91
92 line = NULL;
93 while (getline(&line, &linesz, stdin) != -1) {
94 column = 0;
95 width = 0;
96 for (p = line; *p != '\0'; p += len) {
97 len = 1;
98 switch (*p) {
99 case '\n':
100 putchar('\n');
101 continue;
102 case '\b':
103 /*
104 * Pass it through if the previous character
105 * was in scope, still represented by the
106 * current value of "column".
107 * Allow an optional second backspace
108 * after a double-width character.
109 */
110 if (start == 0 || column < start ||
111 (stop > 0 &&
112 column > stop + (width > 1))) {
113 putchar('\b');
114 if (width > 1 && p[1] == '\b')
115 putchar('\b');
116 }
117 if (width > 1 && p[1] == '\b')
118 p++;
119 column -= width;
120 continue;
121 case '\t':
122 newcol = (column + TAB) & ~(TAB - 1);
123 if (start == 0 || newcol < start) {
124 putchar('\t');
125 column = newcol;
126 } else
127 /*
128 * Expand tabs that intersect or
129 * follow deleted columns.
130 */
131 while (column < newcol)
132 if (++column < start ||
133 (stop > 0 &&
134 column > stop))
135 putchar(' ');
136 continue;
137 default:
138 break;
139 }
140
141 /*
142 * Handle the three cases of invalid bytes,
143 * non-printable, and printable characters.
144 */
145
146 if ((len = mbtowc(&wc, p, MB_CUR_MAX)) == -1) {
147 (void)mbtowc(NULL, NULL, MB_CUR_MAX);
148 len = 1;
149 width = 1;
150 } else if ((width = wcwidth(wc)) == -1)
151 width = 1;
152
153 /*
154 * If the character completely fits before or
155 * after the cut, keep it; otherwise, skip it.
156 */
157
158 if ((start == 0 || column + width < start ||
159 (stop > 0 && column + (width > 0) > stop)))
160 fwrite(p, 1, len, stdout);
161
162 /*
163 * If the cut cuts the character in half
164 * and no backspace follows,
165 * print a blank for correct columnation.
166 */
167
168 else if (width > 1 && p[len] != '\b' &&
169 (start == 0 || column + 1 < start ||
170 (stop > 0 && column + width > stop)))
171 putchar(' ');
172
173 column += width;
174 }
175 }
176 if (ferror(stdin))
177 err(1, "stdin");
178 if (ferror(stdout))
179 err(1, "stdout");
180 return 0;
181 }
182
183 void
usage(void)184 usage(void)
185 {
186 (void)fprintf(stderr, "usage: colrm [start [stop]]\n");
187 exit(1);
188 }
189