1 /* $NetBSD: ul.c,v 1.20 2019/02/03 03:19:30 mrg Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
35 The Regents of the University of California. All rights reserved.");
36 #endif /* not lint */
37
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)ul.c 8.1 (Berkeley) 6/6/93";
41 #endif
42 __RCSID("$NetBSD: ul.c,v 1.20 2019/02/03 03:19:30 mrg Exp $");
43 #endif /* not lint */
44
45 #include <err.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <term.h>
50 #include <unistd.h>
51 #include <util.h>
52
53 #define IESC '\033'
54 #define SO '\016'
55 #define SI '\017'
56 #define HFWD '9'
57 #define HREV '8'
58 #define FREV '7'
59 #define MAXBUF 512
60
61 #define NORMAL 000
62 #define ALTSET 001 /* Reverse */
63 #define SUPERSC 002 /* Dim */
64 #define SUBSC 004 /* Dim | Ul */
65 #define UNDERL 010 /* Ul */
66 #define BOLD 020 /* Bold */
67
68 static int must_overstrike;
69
70 struct CHAR {
71 char c_mode;
72 char c_char;
73 } ;
74
75 static size_t col, maxcol;
76 static int mode;
77 static int halfpos;
78 static int upln;
79 static int iflag;
80
81 static void filter(FILE *);
82 static void flushln(struct CHAR *, size_t);
83 static void fwd(struct CHAR *, size_t);
84 static void iattr(struct CHAR *);
85 static void initbuf(struct CHAR *, size_t);
86 static void outc(int);
87 static int outchar(int);
88 static void overstrike(struct CHAR *);
89 static void reverse(struct CHAR *, size_t);
90 static void setulmode(int);
91 static void alloc_buf(struct CHAR **, size_t *);
92 static void set_mode(void);
93
94
95 #define PRINT(s) if (s == NULL) /* void */; else tputs(s, 1, outchar)
96
97 int
main(int argc,char ** argv)98 main(int argc, char **argv)
99 {
100 int c;
101 const char *termtype;
102 FILE *f;
103
104 termtype = getenv("TERM");
105 if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
106 termtype = "lpr";
107 while ((c=getopt(argc, argv, "it:T:")) != -1)
108 switch(c) {
109
110 case 't':
111 case 'T': /* for nroff compatibility */
112 termtype = optarg;
113 break;
114 case 'i':
115 iflag = 1;
116 break;
117
118 default:
119 fprintf(stderr,
120 "usage: %s [ -i ] [ -tTerm ] file...\n",
121 argv[0]);
122 exit(1);
123 }
124
125 setupterm(termtype, 0, NULL);
126 if ((over_strike && enter_bold_mode == NULL) ||
127 (transparent_underline && enter_underline_mode == NULL &&
128 underline_char == NULL)) {
129 set_mode();
130 }
131 if (optind == argc)
132 filter(stdin);
133 else {
134 for (; optind < argc; optind++) {
135 f = fopen(argv[optind], "r");
136 if (f == NULL)
137 err(EXIT_FAILURE, "Failed to open `%s'", argv[optind]);
138 filter(f);
139 fclose(f);
140 }
141 }
142 exit(0);
143 }
144
145 static void
filter(FILE * f)146 filter(FILE *f)
147 {
148 int c;
149 struct CHAR *obuf = NULL;
150 size_t obuf_size = 0;
151 alloc_buf(&obuf, &obuf_size);
152
153 while ((c = getc(f)) != EOF) switch(c) {
154
155 case '\b':
156 if (col > 0)
157 col--;
158 continue;
159
160 case '\t':
161 col = (col+8) & ~07;
162 if (col > maxcol)
163 maxcol = col;
164 if (col >= obuf_size)
165 alloc_buf(&obuf, &obuf_size);
166 continue;
167
168 case '\r':
169 col = 0;
170 continue;
171
172 case SO:
173 mode |= ALTSET;
174 continue;
175
176 case SI:
177 mode &= ~ALTSET;
178 continue;
179
180 case IESC:
181 switch (c = getc(f)) {
182
183 case HREV:
184 if (halfpos == 0) {
185 mode |= SUPERSC;
186 halfpos--;
187 } else if (halfpos > 0) {
188 mode &= ~SUBSC;
189 halfpos--;
190 } else {
191 halfpos = 0;
192 reverse(obuf, obuf_size);
193 }
194 continue;
195
196 case HFWD:
197 if (halfpos == 0) {
198 mode |= SUBSC;
199 halfpos++;
200 } else if (halfpos < 0) {
201 mode &= ~SUPERSC;
202 halfpos++;
203 } else {
204 halfpos = 0;
205 fwd(obuf, obuf_size);
206 }
207 continue;
208
209 case FREV:
210 reverse(obuf, obuf_size);
211 continue;
212
213 default:
214 fprintf(stderr,
215 "Unknown escape sequence in input: %o, %o\n",
216 IESC, c);
217 exit(1);
218 }
219
220 case '_':
221 if (obuf[col].c_char)
222 obuf[col].c_mode |= UNDERL | mode;
223 else
224 obuf[col].c_char = '_';
225 /* FALLTHROUGH */
226 case ' ':
227 col++;
228 if (col > maxcol)
229 maxcol = col;
230 if (col >= obuf_size)
231 alloc_buf(&obuf, &obuf_size);
232 continue;
233
234 case '\n':
235 flushln(obuf, obuf_size);
236 continue;
237
238 case '\f':
239 flushln(obuf, obuf_size);
240 putchar('\f');
241 continue;
242
243 default:
244 if (c < ' ') /* non printing */
245 continue;
246 if (obuf[col].c_char == '\0') {
247 obuf[col].c_char = c;
248 obuf[col].c_mode = mode;
249 } else if (obuf[col].c_char == '_') {
250 obuf[col].c_char = c;
251 obuf[col].c_mode |= UNDERL|mode;
252 } else if (obuf[col].c_char == c)
253 obuf[col].c_mode |= BOLD|mode;
254 else
255 obuf[col].c_mode = mode;
256 col++;
257 if (col > maxcol)
258 maxcol = col;
259 if (col >= obuf_size)
260 alloc_buf(&obuf, &obuf_size);
261 continue;
262 }
263 if (maxcol)
264 flushln(obuf, obuf_size);
265
266 free(obuf);
267 }
268
269 static void
flushln(struct CHAR * obuf,size_t obuf_size)270 flushln(struct CHAR *obuf, size_t obuf_size)
271 {
272 int lastmode;
273 size_t i;
274 int hadmodes = 0;
275
276 lastmode = NORMAL;
277 for (i=0; i<maxcol; i++) {
278 if (obuf[i].c_mode != lastmode) {
279 hadmodes++;
280 setulmode(obuf[i].c_mode);
281 lastmode = obuf[i].c_mode;
282 }
283 if (obuf[i].c_char == '\0') {
284 if (upln) {
285 PRINT(cursor_right);
286 }
287 else {
288 outc(' ');
289 }
290 } else
291 outc(obuf[i].c_char);
292 }
293 if (lastmode != NORMAL) {
294 setulmode(0);
295 }
296 if (must_overstrike && hadmodes)
297 overstrike(obuf);
298 putchar('\n');
299 if (iflag && hadmodes)
300 iattr(obuf);
301 (void)fflush(stdout);
302 if (upln)
303 upln--;
304 initbuf(obuf, obuf_size);
305 }
306
307 /*
308 * For terminals that can overstrike, overstrike underlines and bolds.
309 * We don't do anything with halfline ups and downs, or Greek.
310 */
311 static void
overstrike(struct CHAR * obuf)312 overstrike(struct CHAR *obuf)
313 {
314 size_t i;
315 char lbuf[256];
316 char *cp = lbuf;
317 int hadbold=0;
318
319 /* Set up overstrike buffer */
320 for (i=0; i<maxcol; i++)
321 switch (obuf[i].c_mode) {
322 case NORMAL:
323 default:
324 *cp++ = ' ';
325 break;
326 case UNDERL:
327 *cp++ = '_';
328 break;
329 case BOLD:
330 *cp++ = obuf[i].c_char;
331 hadbold=1;
332 break;
333 }
334 putchar('\r');
335 for (*cp=' '; *cp==' '; cp--)
336 *cp = 0;
337 for (cp=lbuf; *cp; cp++)
338 putchar(*cp);
339 if (hadbold) {
340 putchar('\r');
341 for (cp=lbuf; *cp; cp++)
342 putchar(*cp=='_' ? ' ' : *cp);
343 putchar('\r');
344 for (cp=lbuf; *cp; cp++)
345 putchar(*cp=='_' ? ' ' : *cp);
346 }
347 }
348
349 static void
iattr(struct CHAR * obuf)350 iattr(struct CHAR *obuf)
351 {
352 size_t i;
353 char lbuf[256];
354 char *cp = lbuf;
355
356 for (i=0; i<maxcol; i++)
357 switch (obuf[i].c_mode) {
358 case NORMAL: *cp++ = ' '; break;
359 case ALTSET: *cp++ = 'g'; break;
360 case SUPERSC: *cp++ = '^'; break;
361 case SUBSC: *cp++ = 'v'; break;
362 case UNDERL: *cp++ = '_'; break;
363 case BOLD: *cp++ = '!'; break;
364 default: *cp++ = 'X'; break;
365 }
366 for (*cp=' '; *cp==' '; cp--)
367 *cp = 0;
368 for (cp=lbuf; *cp; cp++)
369 putchar(*cp);
370 putchar('\n');
371 }
372
373 static void
initbuf(struct CHAR * obuf,size_t obuf_size)374 initbuf(struct CHAR *obuf, size_t obuf_size)
375 {
376
377 memset(obuf, 0, obuf_size * sizeof(*obuf)); /* depends on NORMAL == 0 */
378 col = 0;
379 maxcol = 0;
380 set_mode();
381 }
382
383 static void
set_mode(void)384 set_mode(void)
385 {
386 mode &= ALTSET;
387 }
388
389 static void
fwd(struct CHAR * obuf,size_t obuf_size)390 fwd(struct CHAR *obuf, size_t obuf_size)
391 {
392 int oldcol, oldmax;
393
394 oldcol = col;
395 oldmax = maxcol;
396 flushln(obuf, obuf_size);
397 col = oldcol;
398 maxcol = oldmax;
399 }
400
401 static void
reverse(struct CHAR * obuf,size_t obuf_size)402 reverse(struct CHAR *obuf, size_t obuf_size)
403 {
404 upln++;
405 fwd(obuf, obuf_size);
406 PRINT(cursor_up);
407 PRINT(cursor_up);
408 upln++;
409 }
410
411 static int
outchar(int c)412 outchar(int c)
413 {
414 return (putchar(c & 0177));
415 }
416
417 static int curmode = 0;
418
419 static void
outc(int c)420 outc(int c)
421 {
422 putchar(c);
423 if (underline_char && !enter_underline_mode && (curmode & UNDERL)) {
424 if (cursor_left)
425 PRINT(cursor_left);
426 else
427 putchar('\b');
428 PRINT(underline_char);
429 }
430 }
431
432 static void
setulmode(int newmode)433 setulmode(int newmode)
434 {
435 if (!iflag) {
436 if (curmode != NORMAL && newmode != NORMAL)
437 setulmode(NORMAL);
438 switch (newmode) {
439 case NORMAL:
440 switch(curmode) {
441 case NORMAL:
442 break;
443 case UNDERL:
444 if (enter_underline_mode)
445 PRINT(exit_underline_mode);
446 else
447 PRINT(exit_standout_mode);
448 break;
449 default:
450 /* This includes standout */
451 if (exit_attribute_mode)
452 PRINT(exit_attribute_mode);
453 else
454 PRINT(exit_standout_mode);
455 break;
456 }
457 break;
458 case ALTSET:
459 if (enter_reverse_mode)
460 PRINT(enter_reverse_mode);
461 else
462 PRINT(enter_standout_mode);
463 break;
464 case SUPERSC:
465 /*
466 * This only works on a few terminals.
467 * It should be fixed.
468 */
469 PRINT(enter_underline_mode);
470 PRINT(enter_dim_mode);
471 break;
472 case SUBSC:
473 if (enter_dim_mode)
474 PRINT(enter_dim_mode);
475 else
476 PRINT(enter_standout_mode);
477 break;
478 case UNDERL:
479 if (enter_underline_mode)
480 PRINT(enter_underline_mode);
481 else
482 PRINT(enter_standout_mode);
483 break;
484 case BOLD:
485 if (enter_bold_mode)
486 PRINT(enter_bold_mode);
487 else
488 PRINT(enter_reverse_mode);
489 break;
490 default:
491 /*
492 * We should have some provision here for multiple modes
493 * on at once. This will have to come later.
494 */
495 PRINT(enter_standout_mode);
496 break;
497 }
498 }
499 curmode = newmode;
500 }
501
502 /*
503 * Reallocates the buffer pointed to by *buf and sets
504 * the newly allocated set of bytes to 0.
505 */
506 static void
alloc_buf(struct CHAR ** buf,size_t * size)507 alloc_buf(struct CHAR **buf, size_t *size)
508 {
509 size_t osize = *size;
510 *size += MAXBUF;
511 ereallocarr(buf, *size, sizeof(**buf));
512 memset(*buf + osize, 0, (*size - osize) * sizeof(**buf));
513 }
514