ul.c revision 1.15 1 /* $NetBSD: ul.c,v 1.15 2010/02/03 15:34:46 roy 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.15 2010/02/03 15:34:46 roy Exp $");
43 #endif /* not lint */
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <term.h>
49 #include <unistd.h>
50
51 #define IESC '\033'
52 #define SO '\016'
53 #define SI '\017'
54 #define HFWD '9'
55 #define HREV '8'
56 #define FREV '7'
57 #define MAXBUF 512
58
59 #define NORMAL 000
60 #define ALTSET 001 /* Reverse */
61 #define SUPERSC 002 /* Dim */
62 #define SUBSC 004 /* Dim | Ul */
63 #define UNDERL 010 /* Ul */
64 #define BOLD 020 /* Bold */
65
66 struct tinfo *info;
67 int must_overstrike;
68 const char *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
69 *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
70 *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
71
72 struct CHAR {
73 char c_mode;
74 char c_char;
75 } ;
76
77 struct CHAR obuf[MAXBUF];
78 int col, maxcol;
79 int mode;
80 int halfpos;
81 int upln;
82 int iflag;
83
84 int main __P((int, char **));
85 void filter __P((FILE *));
86 void flushln __P((void));
87 void fwd __P((void));
88 void iattr __P((void));
89 void initbuf __P((void));
90 void initcap __P((void));
91 void outc __P((int));
92 int outchar __P((int));
93 void overstrike __P((void));
94 void reverse __P((void));
95 void setulmode __P((int));
96
97
98 #define PRINT(s) if (s == NULL) /* void */; else tputs(s, 1, outchar)
99
100 int
101 main(argc, argv)
102 int argc;
103 char **argv;
104 {
105 int c;
106 const char *termtype;
107 FILE *f;
108
109 termtype = getenv("TERM");
110 if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
111 termtype = "lpr";
112 while ((c=getopt(argc, argv, "it:T:")) != -1)
113 switch(c) {
114
115 case 't':
116 case 'T': /* for nroff compatibility */
117 termtype = optarg;
118 break;
119 case 'i':
120 iflag = 1;
121 break;
122
123 default:
124 fprintf(stderr,
125 "usage: %s [ -i ] [ -tTerm ] file...\n",
126 argv[0]);
127 exit(1);
128 }
129
130 setupterm(termtype, 0, NULL);
131 if ((over_strike && enter_bold_mode == NULL) ||
132 (transparent_underline && enter_underline_mode == NULL &&
133 underline_char == NULL))
134 initbuf();
135 if (optind == argc)
136 filter(stdin);
137 else for (; optind<argc; optind++) {
138 f = fopen(argv[optind],"r");
139 if (f == NULL) {
140 perror(argv[optind]);
141 exit(1);
142 }
143
144 filter(f);
145 fclose(f);
146 }
147 exit(0);
148 }
149
150 void
151 filter(f)
152 FILE *f;
153 {
154 int c;
155
156 while ((c = getc(f)) != EOF) switch(c) {
157
158 case '\b':
159 if (col > 0)
160 col--;
161 continue;
162
163 case '\t':
164 col = (col+8) & ~07;
165 if (col > maxcol)
166 maxcol = col;
167 continue;
168
169 case '\r':
170 col = 0;
171 continue;
172
173 case SO:
174 mode |= ALTSET;
175 continue;
176
177 case SI:
178 mode &= ~ALTSET;
179 continue;
180
181 case IESC:
182 switch (c = getc(f)) {
183
184 case HREV:
185 if (halfpos == 0) {
186 mode |= SUPERSC;
187 halfpos--;
188 } else if (halfpos > 0) {
189 mode &= ~SUBSC;
190 halfpos--;
191 } else {
192 halfpos = 0;
193 reverse();
194 }
195 continue;
196
197 case HFWD:
198 if (halfpos == 0) {
199 mode |= SUBSC;
200 halfpos++;
201 } else if (halfpos < 0) {
202 mode &= ~SUPERSC;
203 halfpos++;
204 } else {
205 halfpos = 0;
206 fwd();
207 }
208 continue;
209
210 case FREV:
211 reverse();
212 continue;
213
214 default:
215 fprintf(stderr,
216 "Unknown escape sequence in input: %o, %o\n",
217 IESC, c);
218 exit(1);
219 }
220 continue;
221
222 case '_':
223 if (obuf[col].c_char)
224 obuf[col].c_mode |= UNDERL | mode;
225 else
226 obuf[col].c_char = '_';
227 case ' ':
228 col++;
229 if (col > maxcol)
230 maxcol = col;
231 continue;
232
233 case '\n':
234 flushln();
235 continue;
236
237 case '\f':
238 flushln();
239 putchar('\f');
240 continue;
241
242 default:
243 if (c < ' ') /* non printing */
244 continue;
245 if (obuf[col].c_char == '\0') {
246 obuf[col].c_char = c;
247 obuf[col].c_mode = mode;
248 } else if (obuf[col].c_char == '_') {
249 obuf[col].c_char = c;
250 obuf[col].c_mode |= UNDERL|mode;
251 } else if (obuf[col].c_char == c)
252 obuf[col].c_mode |= BOLD|mode;
253 else
254 obuf[col].c_mode = mode;
255 col++;
256 if (col > maxcol)
257 maxcol = col;
258 continue;
259 }
260 if (maxcol)
261 flushln();
262 }
263
264 void
265 flushln()
266 {
267 int lastmode;
268 int i;
269 int hadmodes = 0;
270
271 lastmode = NORMAL;
272 for (i=0; i<maxcol; i++) {
273 if (obuf[i].c_mode != lastmode) {
274 hadmodes++;
275 setulmode(obuf[i].c_mode);
276 lastmode = obuf[i].c_mode;
277 }
278 if (obuf[i].c_char == '\0') {
279 if (upln) {
280 PRINT(cursor_right);
281 }
282 else {
283 outc(' ');
284 }
285 } else
286 outc(obuf[i].c_char);
287 }
288 if (lastmode != NORMAL) {
289 setulmode(0);
290 }
291 if (must_overstrike && hadmodes)
292 overstrike();
293 putchar('\n');
294 if (iflag && hadmodes)
295 iattr();
296 (void)fflush(stdout);
297 if (upln)
298 upln--;
299 initbuf();
300 }
301
302 /*
303 * For terminals that can overstrike, overstrike underlines and bolds.
304 * We don't do anything with halfline ups and downs, or Greek.
305 */
306 void
307 overstrike()
308 {
309 int i;
310 char lbuf[256];
311 char *cp = lbuf;
312 int hadbold=0;
313
314 /* Set up overstrike buffer */
315 for (i=0; i<maxcol; i++)
316 switch (obuf[i].c_mode) {
317 case NORMAL:
318 default:
319 *cp++ = ' ';
320 break;
321 case UNDERL:
322 *cp++ = '_';
323 break;
324 case BOLD:
325 *cp++ = obuf[i].c_char;
326 hadbold=1;
327 break;
328 }
329 putchar('\r');
330 for (*cp=' '; *cp==' '; cp--)
331 *cp = 0;
332 for (cp=lbuf; *cp; cp++)
333 putchar(*cp);
334 if (hadbold) {
335 putchar('\r');
336 for (cp=lbuf; *cp; cp++)
337 putchar(*cp=='_' ? ' ' : *cp);
338 putchar('\r');
339 for (cp=lbuf; *cp; cp++)
340 putchar(*cp=='_' ? ' ' : *cp);
341 }
342 }
343
344 void
345 iattr()
346 {
347 int i;
348 char lbuf[256];
349 char *cp = lbuf;
350
351 for (i=0; i<maxcol; i++)
352 switch (obuf[i].c_mode) {
353 case NORMAL: *cp++ = ' '; break;
354 case ALTSET: *cp++ = 'g'; break;
355 case SUPERSC: *cp++ = '^'; break;
356 case SUBSC: *cp++ = 'v'; break;
357 case UNDERL: *cp++ = '_'; break;
358 case BOLD: *cp++ = '!'; break;
359 default: *cp++ = 'X'; break;
360 }
361 for (*cp=' '; *cp==' '; cp--)
362 *cp = 0;
363 for (cp=lbuf; *cp; cp++)
364 putchar(*cp);
365 putchar('\n');
366 }
367
368 void
369 initbuf()
370 {
371
372 memset((char *)obuf, 0, sizeof (obuf)); /* depends on NORMAL == 0 */
373 col = 0;
374 maxcol = 0;
375 mode &= ALTSET;
376 }
377
378 void
379 fwd()
380 {
381 int oldcol, oldmax;
382
383 oldcol = col;
384 oldmax = maxcol;
385 flushln();
386 col = oldcol;
387 maxcol = oldmax;
388 }
389
390 void
391 reverse()
392 {
393 upln++;
394 fwd();
395 PRINT(cursor_up);
396 PRINT(cursor_up);
397 upln++;
398 }
399
400 int
401 outchar(c)
402 int c;
403 {
404 return (putchar(c & 0177));
405 }
406
407 static int curmode = 0;
408
409 void
410 outc(c)
411 int c;
412 {
413 putchar(c);
414 if (underline_char && !enter_underline_mode && (curmode & UNDERL)) {
415 if (cursor_left)
416 PRINT(cursor_left);
417 else
418 putchar('\b');
419 PRINT(underline_char);
420 }
421 }
422
423 void
424 setulmode(newmode)
425 int newmode;
426 {
427 if (!iflag) {
428 if (curmode != NORMAL && newmode != NORMAL)
429 setulmode(NORMAL);
430 switch (newmode) {
431 case NORMAL:
432 switch(curmode) {
433 case NORMAL:
434 break;
435 case UNDERL:
436 if (enter_underline_mode)
437 PRINT(exit_underline_mode);
438 else
439 PRINT(exit_standout_mode);
440 break;
441 default:
442 /* This includes standout */
443 if (exit_attribute_mode)
444 PRINT(exit_attribute_mode);
445 else
446 PRINT(exit_standout_mode);
447 break;
448 }
449 break;
450 case ALTSET:
451 if (enter_reverse_mode)
452 PRINT(enter_reverse_mode);
453 else
454 PRINT(enter_standout_mode);
455 break;
456 case SUPERSC:
457 /*
458 * This only works on a few terminals.
459 * It should be fixed.
460 */
461 PRINT(enter_underline_mode);
462 PRINT(enter_dim_mode);
463 break;
464 case SUBSC:
465 if (enter_dim_mode)
466 PRINT(enter_dim_mode);
467 else
468 PRINT(enter_standout_mode);
469 break;
470 case UNDERL:
471 if (enter_underline_mode)
472 PRINT(enter_underline_mode);
473 else
474 PRINT(enter_standout_mode);
475 break;
476 case BOLD:
477 if (enter_bold_mode)
478 PRINT(enter_bold_mode);
479 else
480 PRINT(enter_reverse_mode);
481 break;
482 default:
483 /*
484 * We should have some provision here for multiple modes
485 * on at once. This will have to come later.
486 */
487 PRINT(enter_standout_mode);
488 break;
489 }
490 }
491 curmode = newmode;
492 }
493