GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ul/ul.c Lines: 184 259 71.0 %
Date: 2017-11-07 Branches: 145 224 64.7 %

Line Branch Exec Source
1
/*	$OpenBSD: ul.c,v 1.23 2016/10/16 11:28:54 jca Exp $	*/
2
/*	$NetBSD: ul.c,v 1.3 1994/12/07 00:28:24 jtc Exp $	*/
3
4
/*
5
 * Copyright (c) 1980, 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 <curses.h>
34
#include <err.h>
35
#include <errno.h>
36
#include <locale.h>
37
#include <stdio.h>
38
#include <stdlib.h>
39
#include <string.h>
40
#include <term.h>
41
#include <unistd.h>
42
#include <wchar.h>
43
44
#define	IESC	L'\033'
45
#define	SO	L'\016'
46
#define	SI	L'\017'
47
#define	HFWD	'9'
48
#define	HREV	'8'
49
#define	FREV	'7'
50
#define	MAXBUF	512
51
52
#define	NORMAL	000
53
#define	ALTSET	001	/* Reverse */
54
#define	SUPERSC	002	/* Dim */
55
#define	SUBSC	004	/* Dim | Ul */
56
#define	UNDERL	010	/* Ul */
57
#define	BOLD	020	/* Bold */
58
#define	INDET	040	/* Indeterminate: either Bold or Ul */
59
60
int	must_use_uc, must_overstrike;
61
char	*CURS_UP, *CURS_RIGHT, *CURS_LEFT,
62
	*ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
63
	*ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
64
65
struct	CHAR	{
66
	char	c_mode;
67
	wchar_t	c_char;
68
	int	c_width;
69
	int	c_pos;
70
} ;
71
72
struct	CHAR	obuf[MAXBUF];
73
int	col, maxcol;
74
int	mode;
75
int	halfpos;
76
int	upln;
77
int	iflag;
78
79
int	outchar(int);
80
void	initcap(void);
81
void	initbuf(void);
82
void	mfilter(FILE *);
83
void	reverse(void);
84
void	fwd(void);
85
void	flushln(void);
86
void	msetmode(int);
87
void	outc(wchar_t, int);
88
void	overstrike(void);
89
void	iattr(void);
90
91
#define	PRINT(s) \
92
	do { \
93
		if (s) \
94
			tputs(s, 1, outchar); \
95
	} while (0)
96
97
int
98
main(int argc, char *argv[])
99
{
100
	extern int optind;
101
	extern char *optarg;
102
	int c;
103
	char *termtype;
104
	FILE *f;
105
1792
	char termcap[1024];
106
107
896
	setlocale(LC_CTYPE, "");
108
109
896
	if (pledge("stdio rpath tty flock cpath wpath", NULL) == -1)
110
		err(1, "pledge");
111
112
896
	termtype = getenv("TERM");
113

1792
	if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
114
		termtype = "lpr";
115
1792
	while ((c = getopt(argc, argv, "it:T:")) != -1)
116

896
		switch (c) {
117
		case 't':
118
		case 'T': /* for nroff compatibility */
119
672
			termtype = optarg;
120
672
			break;
121
		case 'i':
122
224
			iflag = 1;
123
224
			break;
124
125
		default:
126
			fprintf(stderr,
127
			    "usage: %s [-i] [-t terminal] [file ...]\n",
128
			    argv[0]);
129
			exit(1);
130
		}
131
132
896
	switch (tgetent(termcap, termtype)) {
133
	case 1:
134
		break;
135
	default:
136
		warnx("trouble reading termcap");
137
		/* FALLTHROUGH */
138
	case 0:
139
		/* No such terminal type - assume dumb */
140
		(void)strlcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:",
141
		    sizeof termcap);
142
		break;
143
	}
144
896
	initcap();
145
896
	if ((tgetflag("os") && ENTER_BOLD == NULL ) ||
146
672
	    (tgetflag("ul") && ENTER_UNDERLINE == NULL && UNDER_CHAR == NULL))
147
224
		must_overstrike = 1;
148
896
	initbuf();
149
896
	if (optind == argc)
150
896
		mfilter(stdin);
151
	else for (; optind<argc; optind++) {
152
		f = fopen(argv[optind],"r");
153
		if (f == NULL)
154
			err(1, "%s", argv[optind]);
155
156
		mfilter(f);
157
		fclose(f);
158
	}
159
	exit(0);
160
}
161
162
void
163
mfilter(FILE *f)
164
{
165
	struct CHAR	*cp;
166
	wint_t		 c;
167
	int		 skip_bs, w, wt;
168
169
1792
	col = 1;
170
	skip_bs = 0;
171
26460
	while (col < MAXBUF) {
172



14952
		switch (c = fgetwc(f)) {
173
		case WEOF:
174
			/* Discard invalid bytes. */
175

1904
			if (ferror(f)) {
176
56
				if (errno != EILSEQ)
177
					err(1, NULL);
178
112
				clearerr(f);
179
				break;
180
			}
181
182
			/* End of file. */
183
896
			if (maxcol)
184
840
				flushln();
185
896
			return;
186
187
		case L'\b':
188
			/*
189
			 * Back up one character position, not one
190
			 * display column, but ignore a second
191
			 * backspace after a double-width character.
192
			 */
193
868
			if (skip_bs > 0)
194
56
				skip_bs--;
195
812
			else if (col > 1)
196
812
				if (obuf[--col].c_width > 1)
197
280
					skip_bs = obuf[col].c_width - 1;
198
			continue;
199
200
		case L'\t':
201
			/* Calculate the target position. */
202
840
			wt = (obuf[col - 1].c_pos + 8) & ~7;
203
204
			/* Advance past known positions. */
205

2744
			while ((w = obuf[col].c_pos) > 0 && w <= wt)
206
336
				col++;
207
208
			/* Advance beyond the end. */
209
840
			if (w == 0) {
210
784
				w = obuf[col - 1].c_pos;
211
10528
				while (w < wt) {
212
4480
					obuf[col].c_width = 1;
213
4480
					obuf[col++].c_pos = ++w;
214
				}
215
			}
216
840
			if (col > maxcol)
217
784
				maxcol = col;
218
			break;
219
220
		case L'\r':
221
224
			col = 1;
222
224
			break;
223
224
		case SO:
225
			mode |= ALTSET;
226
			break;
227
228
		case SI:
229
			mode &= ~ALTSET;
230
			break;
231
232
		case IESC:
233
			switch (c = fgetwc(f)) {
234
			case HREV:
235
				if (halfpos == 0) {
236
					mode |= SUPERSC;
237
					halfpos--;
238
				} else if (halfpos > 0) {
239
					mode &= ~SUBSC;
240
					halfpos--;
241
				} else {
242
					halfpos = 0;
243
					reverse();
244
				}
245
				break;
246
			case HFWD:
247
				if (halfpos == 0) {
248
					mode |= SUBSC;
249
					halfpos++;
250
				} else if (halfpos < 0) {
251
					mode &= ~SUPERSC;
252
					halfpos++;
253
				} else {
254
					halfpos = 0;
255
					fwd();
256
				}
257
				break;
258
			case FREV:
259
				reverse();
260
				break;
261
			default:
262
				errx(1, "0%o: unknown escape sequence", c);
263
			}
264
			break;
265
266
		case L'_':
267
1288
			if (obuf[col].c_char == L'\0') {
268
532
				obuf[col].c_char = L'_';
269
532
				obuf[col].c_width = 1;
270
1288
			} else if (obuf[col].c_char == L'_') {
271
252
				if (obuf[col - 1].c_mode & UNDERL)
272
28
					obuf[col].c_mode |= UNDERL | mode;
273
224
				else if (obuf[col - 1].c_mode & BOLD)
274
					obuf[col].c_mode |= BOLD | mode;
275
				else
276
					obuf[col].c_mode |= INDET | mode;
277
			} else
278
504
				obuf[col].c_mode |= UNDERL | mode;
279
			/* FALLTHROUGH */
280
281
		case L' ':
282
2352
			if (obuf[col].c_pos == 0) {
283
1456
				obuf[col].c_width = 1;
284
1456
				obuf[col].c_pos = obuf[col - 1].c_pos + 1;
285
1456
			}
286
2352
			col++;
287
2352
			if (col > maxcol)
288
1456
				maxcol = col;
289
			break;
290
291
		case L'\n':
292
56
			flushln();
293
56
			break;
294
295
		case L'\f':
296
			flushln();
297
			putwchar(L'\f');
298
			break;
299
300
		default:
301
			/* Discard valid, but non-printable characters. */
302
8372
			if ((w = wcwidth(c)) == -1)
303
				break;
304
305
8372
			if (obuf[col].c_char == L'\0') {
306
7504
				obuf[col].c_char = c;
307
7504
				obuf[col].c_mode = mode;
308
7504
				obuf[col].c_width = w;
309
7504
				obuf[col].c_pos = obuf[col - 1].c_pos + w;
310
8372
			} else if (obuf[col].c_char == L'_') {
311
224
				obuf[col].c_char = c;
312
224
				obuf[col].c_mode |= UNDERL|mode;
313
224
				obuf[col].c_width = w;
314
224
				obuf[col].c_pos = obuf[col - 1].c_pos + w;
315
952
				for (cp = obuf + col; cp[1].c_pos > 0; cp++)
316
504
					cp[1].c_pos = cp[0].c_pos +
317
252
					    cp[1].c_width;
318
644
			} else if (obuf[col].c_char == c)
319
504
				obuf[col].c_mode |= BOLD|mode;
320
			else
321
140
				obuf[col].c_mode = mode;
322
8372
			col++;
323
8372
			if (col > maxcol)
324
6916
				maxcol = col;
325
			break;
326
		}
327
		skip_bs = 0;
328
	}
329
896
}
330
331
void
332
flushln(void)
333
{
334
	int lastmode, i;
335
	int hadmodes = 0;
336
337
30184
	for (i = maxcol; i > 0; i--) {
338
13748
		if (obuf[i].c_mode & INDET) {
339
168
			obuf[i].c_mode &= ~INDET;
340

336
			if (i < maxcol && obuf[i + 1].c_mode & BOLD)
341
56
				obuf[i].c_mode |= BOLD;
342
			else
343
				obuf[i].c_mode |= UNDERL;
344
168
		}
345
	}
346
347
	lastmode = NORMAL;
348
27496
	for (i = 1; i < maxcol; i++) {
349
12852
		if (obuf[i].c_mode != lastmode) {
350
			hadmodes = 1;
351
2044
			msetmode(obuf[i].c_mode);
352
2044
			lastmode = obuf[i].c_mode;
353
2044
		}
354
12852
		if (obuf[i].c_char == L'\0') {
355
4816
			if (upln)
356
				PRINT(CURS_RIGHT);
357
			else
358
4816
				outc(L' ', 1);
359
		} else
360
8036
			outc(obuf[i].c_char, obuf[i].c_width);
361
	}
362
896
	if (lastmode != NORMAL)
363
56
		msetmode(0);
364
896
	if (must_overstrike && hadmodes && !iflag)
365
175
		overstrike();
366
896
	putwchar(L'\n');
367
896
	if (iflag && hadmodes)
368
175
		iattr();
369
896
	(void)fflush(stdout);
370
896
	if (upln)
371
		upln--;
372
896
	initbuf();
373
896
}
374
375
/*
376
 * For terminals that can overstrike, overstrike underlines and bolds.
377
 * We don't do anything with halfline ups and downs, or Greek.
378
 */
379
void
380
overstrike(void)
381
{
382
	int i, j, needspace;
383
384
350
	putwchar(L'\r');
385
	needspace = 0;
386
5446
	for (i = 1; i < maxcol; i++) {
387

4893
		if (obuf[i].c_mode != UNDERL && obuf[i].c_mode != BOLD) {
388
2212
			needspace += obuf[i].c_width;
389
2212
			continue;
390
		}
391
770
		while (needspace > 0) {
392
217
			putwchar(L' ');
393
217
			needspace--;
394
		}
395
336
		if (obuf[i].c_mode == BOLD)
396
133
			putwchar(obuf[i].c_char);
397
		else
398
896
			for (j = 0; j < obuf[i].c_width; j++)
399
245
				putwchar(L'_');
400
	}
401
175
}
402
403
void
404
iattr(void)
405
{
406
	int i, j, needspace;
407
	char c;
408
409
	needspace = 0;
410
5621
	for (i = 1; i < maxcol; i++) {
411

2548
		switch (obuf[i].c_mode) {
412
		case NORMAL:
413
2212
			needspace += obuf[i].c_width;
414
2212
			continue;
415
		case ALTSET:
416
			c = 'g';
417
			break;
418
		case SUPERSC:
419
			c = '^';
420
			break;
421
		case SUBSC:
422
			c = 'v';
423
			break;
424
		case UNDERL:
425
			c = '_';
426
203
			break;
427
		case BOLD:
428
			c = '!';
429
133
			break;
430
		default:
431
			c = 'X';
432
			break;
433
		}
434
770
		while (needspace > 0) {
435
217
			putwchar(L' ');
436
217
			needspace--;
437
		}
438
1442
		for (j = 0; j < obuf[i].c_width; j++)
439
385
			putwchar(c);
440
	}
441
175
	putwchar(L'\n');
442
175
}
443
444
void
445
initbuf(void)
446
{
447
3584
	bzero(obuf, sizeof (obuf));	/* depends on NORMAL == 0 */
448
1792
	col = 1;
449
1792
	maxcol = 0;
450
1792
	mode &= ALTSET;
451
1792
}
452
453
void
454
fwd(void)
455
{
456
	int oldcol, oldmax;
457
458
	oldcol = col;
459
	oldmax = maxcol;
460
	flushln();
461
	col = oldcol;
462
	maxcol = oldmax;
463
}
464
465
void
466
reverse(void)
467
{
468
	upln++;
469
	fwd();
470
	PRINT(CURS_UP);
471
	PRINT(CURS_UP);
472
	upln++;
473
}
474
475
void
476
initcap(void)
477
{
478
	static char tcapbuf[512];
479
1792
	char *bp = tcapbuf;
480
481
	/* This nonsense attempts to work with both old and new termcap */
482
896
	CURS_UP =		tgetstr("up", &bp);
483
896
	CURS_RIGHT =		tgetstr("ri", &bp);
484
896
	if (CURS_RIGHT == NULL)
485
896
		CURS_RIGHT =	tgetstr("nd", &bp);
486
896
	CURS_LEFT =		tgetstr("le", &bp);
487
896
	if (CURS_LEFT == NULL)
488
		CURS_LEFT =	tgetstr("bc", &bp);
489

896
	if (CURS_LEFT == NULL && tgetflag("bs"))
490
		CURS_LEFT =	"\b";
491
492
896
	ENTER_STANDOUT =	tgetstr("so", &bp);
493
896
	EXIT_STANDOUT =		tgetstr("se", &bp);
494
896
	ENTER_UNDERLINE =	tgetstr("us", &bp);
495
896
	EXIT_UNDERLINE =	tgetstr("ue", &bp);
496
896
	ENTER_DIM =		tgetstr("mh", &bp);
497
896
	ENTER_BOLD =		tgetstr("md", &bp);
498
896
	ENTER_REVERSE =		tgetstr("mr", &bp);
499
896
	EXIT_ATTRIBUTES =	tgetstr("me", &bp);
500
501
896
	if (!ENTER_BOLD && ENTER_REVERSE)
502
		ENTER_BOLD = ENTER_REVERSE;
503
896
	if (!ENTER_BOLD && ENTER_STANDOUT)
504
		ENTER_BOLD = ENTER_STANDOUT;
505
896
	if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
506
		ENTER_UNDERLINE = ENTER_STANDOUT;
507
		EXIT_UNDERLINE = EXIT_STANDOUT;
508
	}
509
896
	if (!ENTER_DIM && ENTER_STANDOUT)
510
		ENTER_DIM = ENTER_STANDOUT;
511
896
	if (!ENTER_REVERSE && ENTER_STANDOUT)
512
		ENTER_REVERSE = ENTER_STANDOUT;
513
896
	if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
514
		EXIT_ATTRIBUTES = EXIT_STANDOUT;
515
516
	/*
517
	 * Note that we use REVERSE for the alternate character set,
518
	 * not the as/ae capabilities.  This is because we are modelling
519
	 * the model 37 teletype (since that's what nroff outputs) and
520
	 * the typical as/ae is more of a graphics set, not the greek
521
	 * letters the 37 has.
522
	 */
523
524
896
	UNDER_CHAR =		tgetstr("uc", &bp);
525
2016
	must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
526
896
}
527
528
int
529
outchar(int c)
530
{
531
5964
	return (putwchar(c) != WEOF ? c : EOF);
532
}
533
534
static int curmode = 0;
535
536
void
537
outc(wchar_t c, int width)
538
{
539
	int i;
540
541
25704
	putwchar(c);
542

16065
	if (must_use_uc && (curmode&UNDERL)) {
543
896
		for (i = 0; i < width; i++)
544
490
			PRINT(CURS_LEFT);
545
896
		for (i = 0; i < width; i++)
546
490
			PRINT(UNDER_CHAR);
547
	}
548
12852
}
549
550
void
551
msetmode(int newmode)
552
{
553
4242
	if (!iflag) {
554
1596
		if (curmode != NORMAL && newmode != NORMAL)
555
21
			msetmode(NORMAL);
556

1596
		switch (newmode) {
557
		case NORMAL:
558
1596
			switch(curmode) {
559
			case NORMAL:
560
				break;
561
			case UNDERL:
562
672
				PRINT(EXIT_UNDERLINE);
563
				break;
564
			default:
565
				/* This includes standout */
566
392
				PRINT(EXIT_ATTRIBUTES);
567
				break;
568
			}
569
			break;
570
		case ALTSET:
571
			PRINT(ENTER_REVERSE);
572
			break;
573
		case SUPERSC:
574
			/*
575
			 * This only works on a few terminals.
576
			 * It should be fixed.
577
			 */
578
			PRINT(ENTER_UNDERLINE);
579
			PRINT(ENTER_DIM);
580
			break;
581
		case SUBSC:
582
			PRINT(ENTER_DIM);
583
			break;
584
		case UNDERL:
585
672
			PRINT(ENTER_UNDERLINE);
586
			break;
587
		case BOLD:
588
392
			PRINT(ENTER_BOLD);
589
			break;
590
		default:
591
			/*
592
			 * We should have some provision here for multiple modes
593
			 * on at once.  This will have to come later.
594
			 */
595
			PRINT(ENTER_STANDOUT);
596
			break;
597
		}
598
	}
599
2121
	curmode = newmode;
600
2121
}