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