GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ul/ul.c Lines: 0 256 0.0 %
Date: 2016-12-06 Branches: 0 243 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: ul.c,v 1.21 2016/03/26 08:59:29 natano 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
	char termcap[1024];
106
107
	setlocale(LC_CTYPE, "");
108
109
	if (pledge("stdio rpath tty wpath cpath", NULL) == -1)
110
		err(1, "pledge");
111
112
	termtype = getenv("TERM");
113
	if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
114
		termtype = "lpr";
115
	while ((c = getopt(argc, argv, "it:T:")) != -1)
116
		switch (c) {
117
		case 't':
118
		case 'T': /* for nroff compatibility */
119
			termtype = optarg;
120
			break;
121
		case 'i':
122
			iflag = 1;
123
			break;
124
125
		default:
126
			fprintf(stderr,
127
			    "usage: %s [-i] [-t terminal] [file ...]\n",
128
			    argv[0]);
129
			exit(1);
130
		}
131
132
	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
	initcap();
145
	if ((tgetflag("os") && ENTER_BOLD == NULL ) ||
146
	    (tgetflag("ul") && ENTER_UNDERLINE == NULL && UNDER_CHAR == NULL))
147
		must_overstrike = 1;
148
	initbuf();
149
	if (optind == argc)
150
		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
	col = 1;
170
	skip_bs = 0;
171
	while (col < MAXBUF) {
172
		switch (c = fgetwc(f)) {
173
		case WEOF:
174
			/* Discard invalid bytes. */
175
			if (ferror(f)) {
176
				if (errno != EILSEQ)
177
					err(1, NULL);
178
				clearerr(f);
179
				break;
180
			}
181
182
			/* End of file. */
183
			if (maxcol)
184
				flushln();
185
			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
			if (skip_bs > 0)
194
				skip_bs--;
195
			else if (col > 1)
196
				if (obuf[--col].c_width > 1)
197
					skip_bs = obuf[col].c_width - 1;
198
			continue;
199
200
		case L'\t':
201
			/* Calculate the target position. */
202
			wt = (obuf[col - 1].c_pos + 8) & ~7;
203
204
			/* Advance past known positions. */
205
			while ((w = obuf[col].c_pos) > 0 && w <= wt)
206
				col++;
207
208
			/* Advance beyond the end. */
209
			if (w == 0) {
210
				w = obuf[col - 1].c_pos;
211
				while (w < wt) {
212
					obuf[col].c_width = 1;
213
					obuf[col++].c_pos = ++w;
214
				}
215
			}
216
			if (col > maxcol)
217
				maxcol = col;
218
			break;
219
220
		case L'\r':
221
			col = 1;
222
			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
			if (obuf[col].c_char == L'\0') {
268
				obuf[col].c_char = L'_';
269
				obuf[col].c_width = 1;
270
			} else if (obuf[col].c_char == L'_') {
271
				if (obuf[col - 1].c_mode & UNDERL)
272
					obuf[col].c_mode |= UNDERL | mode;
273
				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
				obuf[col].c_mode |= UNDERL | mode;
279
			/* FALLTHROUGH */
280
281
		case L' ':
282
			if (obuf[col].c_pos == 0) {
283
				obuf[col].c_width = 1;
284
				obuf[col].c_pos = obuf[col - 1].c_pos + 1;
285
			}
286
			col++;
287
			if (col > maxcol)
288
				maxcol = col;
289
			break;
290
291
		case L'\n':
292
			flushln();
293
			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
			if ((w = wcwidth(c)) == -1)
303
				break;
304
305
			if (obuf[col].c_char == L'\0') {
306
				obuf[col].c_char = c;
307
				obuf[col].c_mode = mode;
308
				obuf[col].c_width = w;
309
				obuf[col].c_pos = obuf[col - 1].c_pos + w;
310
			} else if (obuf[col].c_char == L'_') {
311
				obuf[col].c_char = c;
312
				obuf[col].c_mode |= UNDERL|mode;
313
				obuf[col].c_width = w;
314
				obuf[col].c_pos = obuf[col - 1].c_pos + w;
315
				for (cp = obuf + col; cp[1].c_pos > 0; cp++)
316
					cp[1].c_pos = cp[0].c_pos +
317
					    cp[1].c_width;
318
			} else if (obuf[col].c_char == c)
319
				obuf[col].c_mode |= BOLD|mode;
320
			else
321
				obuf[col].c_mode = mode;
322
			col++;
323
			if (col > maxcol)
324
				maxcol = col;
325
			break;
326
		}
327
		skip_bs = 0;
328
	}
329
}
330
331
void
332
flushln(void)
333
{
334
	int lastmode, i;
335
	int hadmodes = 0;
336
337
	for (i = maxcol; i > 0; i--) {
338
		if (obuf[i].c_mode & INDET) {
339
			obuf[i].c_mode &= ~INDET;
340
			if (i < maxcol && obuf[i + 1].c_mode & BOLD)
341
				obuf[i].c_mode |= BOLD;
342
			else
343
				obuf[i].c_mode |= UNDERL;
344
		}
345
	}
346
347
	lastmode = NORMAL;
348
	for (i = 1; i < maxcol; i++) {
349
		if (obuf[i].c_mode != lastmode) {
350
			hadmodes = 1;
351
			msetmode(obuf[i].c_mode);
352
			lastmode = obuf[i].c_mode;
353
		}
354
		if (obuf[i].c_char == L'\0') {
355
			if (upln)
356
				PRINT(CURS_RIGHT);
357
			else
358
				outc(L' ', 1);
359
		} else
360
			outc(obuf[i].c_char, obuf[i].c_width);
361
	}
362
	if (lastmode != NORMAL)
363
		msetmode(0);
364
	if (must_overstrike && hadmodes)
365
		overstrike();
366
	putwchar(L'\n');
367
	if (iflag && hadmodes)
368
		iattr();
369
	(void)fflush(stdout);
370
	if (upln)
371
		upln--;
372
	initbuf();
373
}
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
	wchar_t wc;
383
	int i, j, needspace;
384
385
	putwchar(L'\r');
386
	needspace = 0;
387
	for (i = 1; i < maxcol; i++) {
388
		if (obuf[i].c_mode != UNDERL && obuf[i].c_mode != BOLD) {
389
			needspace += obuf[i].c_width;
390
			continue;
391
		}
392
		while (needspace > 0) {
393
			putwchar(L' ');
394
			needspace--;
395
		}
396
		if (obuf[i].c_mode == BOLD)
397
			putwchar(obuf[i].c_char);
398
		else
399
			for (j = 0; j < obuf[i].c_width; j++)
400
				putwchar(L'_');
401
	}
402
}
403
404
void
405
iattr(void)
406
{
407
	int i, j, needspace;
408
	char c;
409
410
	needspace = 0;
411
	for (i = 1; i < maxcol; i++) {
412
		switch (obuf[i].c_mode) {
413
		case NORMAL:
414
			needspace += obuf[i].c_width;
415
			continue;
416
		case ALTSET:
417
			c = 'g';
418
			break;
419
		case SUPERSC:
420
			c = '^';
421
			break;
422
		case SUBSC:
423
			c = 'v';
424
			break;
425
		case UNDERL:
426
			c = '_';
427
			break;
428
		case BOLD:
429
			c = '!';
430
			break;
431
		default:
432
			c = 'X';
433
			break;
434
		}
435
		while (needspace > 0) {
436
			putwchar(L' ');
437
			needspace--;
438
		}
439
		for (j = 0; j < obuf[i].c_width; j++)
440
			putwchar(c);
441
	}
442
	putwchar(L'\n');
443
}
444
445
void
446
initbuf(void)
447
{
448
	bzero(obuf, sizeof (obuf));	/* depends on NORMAL == 0 */
449
	col = 1;
450
	maxcol = 0;
451
	mode &= ALTSET;
452
}
453
454
void
455
fwd(void)
456
{
457
	int oldcol, oldmax;
458
459
	oldcol = col;
460
	oldmax = maxcol;
461
	flushln();
462
	col = oldcol;
463
	maxcol = oldmax;
464
}
465
466
void
467
reverse(void)
468
{
469
	upln++;
470
	fwd();
471
	PRINT(CURS_UP);
472
	PRINT(CURS_UP);
473
	upln++;
474
}
475
476
void
477
initcap(void)
478
{
479
	static char tcapbuf[512];
480
	char *bp = tcapbuf;
481
482
	/* This nonsense attempts to work with both old and new termcap */
483
	CURS_UP =		tgetstr("up", &bp);
484
	CURS_RIGHT =		tgetstr("ri", &bp);
485
	if (CURS_RIGHT == NULL)
486
		CURS_RIGHT =	tgetstr("nd", &bp);
487
	CURS_LEFT =		tgetstr("le", &bp);
488
	if (CURS_LEFT == NULL)
489
		CURS_LEFT =	tgetstr("bc", &bp);
490
	if (CURS_LEFT == NULL && tgetflag("bs"))
491
		CURS_LEFT =	"\b";
492
493
	ENTER_STANDOUT =	tgetstr("so", &bp);
494
	EXIT_STANDOUT =		tgetstr("se", &bp);
495
	ENTER_UNDERLINE =	tgetstr("us", &bp);
496
	EXIT_UNDERLINE =	tgetstr("ue", &bp);
497
	ENTER_DIM =		tgetstr("mh", &bp);
498
	ENTER_BOLD =		tgetstr("md", &bp);
499
	ENTER_REVERSE =		tgetstr("mr", &bp);
500
	EXIT_ATTRIBUTES =	tgetstr("me", &bp);
501
502
	if (!ENTER_BOLD && ENTER_REVERSE)
503
		ENTER_BOLD = ENTER_REVERSE;
504
	if (!ENTER_BOLD && ENTER_STANDOUT)
505
		ENTER_BOLD = ENTER_STANDOUT;
506
	if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
507
		ENTER_UNDERLINE = ENTER_STANDOUT;
508
		EXIT_UNDERLINE = EXIT_STANDOUT;
509
	}
510
	if (!ENTER_DIM && ENTER_STANDOUT)
511
		ENTER_DIM = ENTER_STANDOUT;
512
	if (!ENTER_REVERSE && ENTER_STANDOUT)
513
		ENTER_REVERSE = ENTER_STANDOUT;
514
	if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
515
		EXIT_ATTRIBUTES = EXIT_STANDOUT;
516
517
	/*
518
	 * Note that we use REVERSE for the alternate character set,
519
	 * not the as/ae capabilities.  This is because we are modelling
520
	 * the model 37 teletype (since that's what nroff outputs) and
521
	 * the typical as/ae is more of a graphics set, not the greek
522
	 * letters the 37 has.
523
	 */
524
525
	UNDER_CHAR =		tgetstr("uc", &bp);
526
	must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
527
}
528
529
int
530
outchar(int c)
531
{
532
	return (putwchar(c) != WEOF ? c : EOF);
533
}
534
535
static int curmode = 0;
536
537
void
538
outc(wchar_t c, int width)
539
{
540
	int i;
541
542
	putwchar(c);
543
	if (must_use_uc && (curmode&UNDERL)) {
544
		for (i = 0; i < width; i++)
545
			PRINT(CURS_LEFT);
546
		for (i = 0; i < width; i++)
547
			PRINT(UNDER_CHAR);
548
	}
549
}
550
551
void
552
msetmode(int newmode)
553
{
554
	if (!iflag) {
555
		if (curmode != NORMAL && newmode != NORMAL)
556
			msetmode(NORMAL);
557
		switch (newmode) {
558
		case NORMAL:
559
			switch(curmode) {
560
			case NORMAL:
561
				break;
562
			case UNDERL:
563
				PRINT(EXIT_UNDERLINE);
564
				break;
565
			default:
566
				/* This includes standout */
567
				PRINT(EXIT_ATTRIBUTES);
568
				break;
569
			}
570
			break;
571
		case ALTSET:
572
			PRINT(ENTER_REVERSE);
573
			break;
574
		case SUPERSC:
575
			/*
576
			 * This only works on a few terminals.
577
			 * It should be fixed.
578
			 */
579
			PRINT(ENTER_UNDERLINE);
580
			PRINT(ENTER_DIM);
581
			break;
582
		case SUBSC:
583
			PRINT(ENTER_DIM);
584
			break;
585
		case UNDERL:
586
			PRINT(ENTER_UNDERLINE);
587
			break;
588
		case BOLD:
589
			PRINT(ENTER_BOLD);
590
			break;
591
		default:
592
			/*
593
			 * We should have some provision here for multiple modes
594
			 * on at once.  This will have to come later.
595
			 */
596
			PRINT(ENTER_STANDOUT);
597
			break;
598
		}
599
	}
600
	curmode = newmode;
601
}