GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ul/ul.c Lines: 185 260 71.2 %
Date: 2017-11-13 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
1024
	char termcap[1024];
106
107
512
	setlocale(LC_CTYPE, "");
108
109
512
	if (pledge("stdio rpath tty flock cpath wpath", NULL) == -1)
110
		err(1, "pledge");
111
112
512
	termtype = getenv("TERM");
113

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

512
		switch (c) {
117
		case 't':
118
		case 'T': /* for nroff compatibility */
119
384
			termtype = optarg;
120
384
			break;
121
		case 'i':
122
128
			iflag = 1;
123
128
			break;
124
125
		default:
126
			fprintf(stderr,
127
			    "usage: %s [-i] [-t terminal] [file ...]\n",
128
			    argv[0]);
129
			exit(1);
130
		}
131
132
512
	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
512
	initcap();
145
512
	if ((tgetflag("os") && ENTER_BOLD == NULL ) ||
146
384
	    (tgetflag("ul") && ENTER_UNDERLINE == NULL && UNDER_CHAR == NULL))
147
128
		must_overstrike = 1;
148
512
	initbuf();
149
512
	if (optind == argc)
150
512
		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
1024
	col = 1;
170
	skip_bs = 0;
171
15120
	while (col < MAXBUF) {
172



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

1088
			if (ferror(f)) {
176
32
				if (errno != EILSEQ)
177
					err(1, NULL);
178
64
				clearerr(f);
179
				break;
180
			}
181
182
			/* End of file. */
183
512
			if (maxcol)
184
480
				flushln();
185
512
			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
496
			if (skip_bs > 0)
194
32
				skip_bs--;
195
464
			else if (col > 1)
196
464
				if (obuf[--col].c_width > 1)
197
160
					skip_bs = obuf[col].c_width - 1;
198
496
			continue;
199
200
		case L'\t':
201
			/* Calculate the target position. */
202
480
			wt = (obuf[col - 1].c_pos + 8) & ~7;
203
204
			/* Advance past known positions. */
205

1568
			while ((w = obuf[col].c_pos) > 0 && w <= wt)
206
192
				col++;
207
208
			/* Advance beyond the end. */
209
480
			if (w == 0) {
210
448
				w = obuf[col - 1].c_pos;
211
6016
				while (w < wt) {
212
2560
					obuf[col].c_width = 1;
213
2560
					obuf[col++].c_pos = ++w;
214
				}
215
			}
216
480
			if (col > maxcol)
217
448
				maxcol = col;
218
			break;
219
220
		case L'\r':
221
128
			col = 1;
222
128
			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
736
			if (obuf[col].c_char == L'\0') {
268
304
				obuf[col].c_char = L'_';
269
304
				obuf[col].c_width = 1;
270
736
			} else if (obuf[col].c_char == L'_') {
271
144
				if (obuf[col - 1].c_mode & UNDERL)
272
16
					obuf[col].c_mode |= UNDERL | mode;
273
128
				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
288
				obuf[col].c_mode |= UNDERL | mode;
279
			/* FALLTHROUGH */
280
281
		case L' ':
282
1344
			if (obuf[col].c_pos == 0) {
283
832
				obuf[col].c_width = 1;
284
832
				obuf[col].c_pos = obuf[col - 1].c_pos + 1;
285
832
			}
286
1344
			col++;
287
1344
			if (col > maxcol)
288
832
				maxcol = col;
289
			break;
290
291
		case L'\n':
292
32
			flushln();
293
32
			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
4784
			if ((w = wcwidth(c)) == -1)
303
				break;
304
305
4784
			if (obuf[col].c_char == L'\0') {
306
4288
				obuf[col].c_char = c;
307
4288
				obuf[col].c_mode = mode;
308
4288
				obuf[col].c_width = w;
309
4288
				obuf[col].c_pos = obuf[col - 1].c_pos + w;
310
4784
			} else if (obuf[col].c_char == L'_') {
311
128
				obuf[col].c_char = c;
312
128
				obuf[col].c_mode |= UNDERL|mode;
313
128
				obuf[col].c_width = w;
314
128
				obuf[col].c_pos = obuf[col - 1].c_pos + w;
315
544
				for (cp = obuf + col; cp[1].c_pos > 0; cp++)
316
288
					cp[1].c_pos = cp[0].c_pos +
317
144
					    cp[1].c_width;
318
368
			} else if (obuf[col].c_char == c)
319
288
				obuf[col].c_mode |= BOLD|mode;
320
			else
321
80
				obuf[col].c_mode = mode;
322
4784
			col++;
323
4784
			if (col > maxcol)
324
3952
				maxcol = col;
325
			break;
326
		}
327
		skip_bs = 0;
328
	}
329
512
}
330
331
void
332
flushln(void)
333
{
334
	int lastmode, i;
335
	int hadmodes = 0;
336
337
17248
	for (i = maxcol; i > 0; i--) {
338
7856
		if (obuf[i].c_mode & INDET) {
339
96
			obuf[i].c_mode &= ~INDET;
340

192
			if (i < maxcol && obuf[i + 1].c_mode & BOLD)
341
32
				obuf[i].c_mode |= BOLD;
342
			else
343
				obuf[i].c_mode |= UNDERL;
344
96
		}
345
	}
346
347
	lastmode = NORMAL;
348
15712
	for (i = 1; i < maxcol; i++) {
349
7344
		if (obuf[i].c_mode != lastmode) {
350
			hadmodes = 1;
351
1168
			msetmode(obuf[i].c_mode);
352
1168
			lastmode = obuf[i].c_mode;
353
1168
		}
354
7344
		if (obuf[i].c_char == L'\0') {
355
2752
			if (upln)
356
				PRINT(CURS_RIGHT);
357
			else
358
2752
				outc(L' ', 1);
359
		} else
360
4592
			outc(obuf[i].c_char, obuf[i].c_width);
361
	}
362
512
	if (lastmode != NORMAL)
363
32
		msetmode(0);
364
512
	if (must_overstrike && hadmodes && !iflag)
365
100
		overstrike();
366
512
	putwchar(L'\n');
367
512
	if (iflag && hadmodes)
368
100
		iattr();
369
512
	(void)fflush(stdout);
370
512
	if (upln)
371
		upln--;
372
512
	initbuf();
373
512
}
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
200
	putwchar(L'\r');
385
	needspace = 0;
386
3112
	for (i = 1; i < maxcol; i++) {
387

2796
		if (obuf[i].c_mode != UNDERL && obuf[i].c_mode != BOLD) {
388
1264
			needspace += obuf[i].c_width;
389
1264
			continue;
390
		}
391
632
		while (needspace > 0) {
392
124
			putwchar(L' ');
393
124
			needspace--;
394
		}
395
192
		if (obuf[i].c_mode == BOLD)
396
76
			putwchar(obuf[i].c_char);
397
		else
398
512
			for (j = 0; j < obuf[i].c_width; j++)
399
140
				putwchar(L'_');
400
	}
401
100
}
402
403
void
404
iattr(void)
405
{
406
	int i, j, needspace;
407
	char c;
408
409
	needspace = 0;
410
3212
	for (i = 1; i < maxcol; i++) {
411

1456
		switch (obuf[i].c_mode) {
412
		case NORMAL:
413
1264
			needspace += obuf[i].c_width;
414
1264
			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
116
			break;
427
		case BOLD:
428
			c = '!';
429
76
			break;
430
		default:
431
			c = 'X';
432
			break;
433
		}
434
632
		while (needspace > 0) {
435
124
			putwchar(L' ');
436
124
			needspace--;
437
		}
438
824
		for (j = 0; j < obuf[i].c_width; j++)
439
220
			putwchar(c);
440
	}
441
100
	putwchar(L'\n');
442
100
}
443
444
void
445
initbuf(void)
446
{
447
2048
	bzero(obuf, sizeof (obuf));	/* depends on NORMAL == 0 */
448
1024
	col = 1;
449
1024
	maxcol = 0;
450
1024
	mode &= ALTSET;
451
1024
}
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
1024
	char *bp = tcapbuf;
480
481
	/* This nonsense attempts to work with both old and new termcap */
482
512
	CURS_UP =		tgetstr("up", &bp);
483
512
	CURS_RIGHT =		tgetstr("ri", &bp);
484
512
	if (CURS_RIGHT == NULL)
485
512
		CURS_RIGHT =	tgetstr("nd", &bp);
486
512
	CURS_LEFT =		tgetstr("le", &bp);
487
512
	if (CURS_LEFT == NULL)
488
		CURS_LEFT =	tgetstr("bc", &bp);
489

512
	if (CURS_LEFT == NULL && tgetflag("bs"))
490
		CURS_LEFT =	"\b";
491
492
512
	ENTER_STANDOUT =	tgetstr("so", &bp);
493
512
	EXIT_STANDOUT =		tgetstr("se", &bp);
494
512
	ENTER_UNDERLINE =	tgetstr("us", &bp);
495
512
	EXIT_UNDERLINE =	tgetstr("ue", &bp);
496
512
	ENTER_DIM =		tgetstr("mh", &bp);
497
512
	ENTER_BOLD =		tgetstr("md", &bp);
498
512
	ENTER_REVERSE =		tgetstr("mr", &bp);
499
512
	EXIT_ATTRIBUTES =	tgetstr("me", &bp);
500
501
512
	if (!ENTER_BOLD && ENTER_REVERSE)
502
		ENTER_BOLD = ENTER_REVERSE;
503
512
	if (!ENTER_BOLD && ENTER_STANDOUT)
504
		ENTER_BOLD = ENTER_STANDOUT;
505
512
	if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
506
		ENTER_UNDERLINE = ENTER_STANDOUT;
507
		EXIT_UNDERLINE = EXIT_STANDOUT;
508
	}
509
512
	if (!ENTER_DIM && ENTER_STANDOUT)
510
		ENTER_DIM = ENTER_STANDOUT;
511
512
	if (!ENTER_REVERSE && ENTER_STANDOUT)
512
		ENTER_REVERSE = ENTER_STANDOUT;
513
512
	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
512
	UNDER_CHAR =		tgetstr("uc", &bp);
525
1152
	must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
526
512
}
527
528
int
529
outchar(int c)
530
{
531
3408
	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
14688
	putwchar(c);
542

9180
	if (must_use_uc && (curmode&UNDERL)) {
543
512
		for (i = 0; i < width; i++)
544
280
			PRINT(CURS_LEFT);
545
512
		for (i = 0; i < width; i++)
546
280
			PRINT(UNDER_CHAR);
547
	}
548
7344
}
549
550
void
551
msetmode(int newmode)
552
{
553
2424
	if (!iflag) {
554
912
		if (curmode != NORMAL && newmode != NORMAL)
555
12
			msetmode(NORMAL);
556

912
		switch (newmode) {
557
		case NORMAL:
558
912
			switch(curmode) {
559
			case NORMAL:
560
				break;
561
			case UNDERL:
562
384
				PRINT(EXIT_UNDERLINE);
563
				break;
564
			default:
565
				/* This includes standout */
566
224
				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
384
			PRINT(ENTER_UNDERLINE);
586
			break;
587
		case BOLD:
588
224
			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
1212
	curmode = newmode;
600
1212
}