Fix bug where, on an unformatted screen, we were changing the
[unix-history] / usr / src / usr.bin / ul / ul.c
CommitLineData
22e155fc
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
8char copyright[] =
9"@(#) Copyright (c) 1980 Regents of the University of California.\n\
10 All rights reserved.\n";
ac0d117c 11#endif /* not lint */
22e155fc 12
476fcd16 13#ifndef lint
ac0d117c
KB
14static char sccsid[] = "@(#)ul.c 5.4 (Berkeley) %G%";
15#endif /* not lint */
a7878ce2 16
2770968b
BJ
17#include <stdio.h>
18
94c88dd0
BJ
19#define IESC '\033'
20#define SO '\016'
21#define SI '\017'
22#define HFWD '9'
23#define HREV '8'
24#define FREV '7'
25#define MAXBUF 512
26
27#define NORMAL 000
28#define ALTSET 001 /* Reverse */
29#define SUPERSC 002 /* Dim */
30#define SUBSC 004 /* Dim | Ul */
31#define UNDERL 010 /* Ul */
32#define BOLD 020 /* Bold */
33
34int must_use_uc, must_overstrike;
35char *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
36 *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
37 *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
38
39struct CHAR {
40 char c_mode;
41 char c_char;
42} ;
43
94c88dd0
BJ
44struct CHAR obuf[MAXBUF];
45int col, maxcol;
46int mode;
47int halfpos;
48int upln;
49int iflag;
2770968b
BJ
50
51main(argc, argv)
52 int argc;
53 char **argv;
54{
ac0d117c
KB
55 extern int optind;
56 extern char *optarg;
94c88dd0 57 int c;
ac0d117c 58 char *termtype;
2770968b 59 FILE *f;
94c88dd0 60 char termcap[1024];
ac0d117c 61 char *getenv(), *strcpy();
2770968b 62
2770968b 63 termtype = getenv("TERM");
94c88dd0
BJ
64 if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
65 termtype = "lpr";
66 while ((c=getopt(argc, argv, "it:T:")) != EOF)
67 switch(c) {
2770968b
BJ
68
69 case 't':
70 case 'T': /* for nroff compatibility */
94c88dd0 71 termtype = optarg;
2770968b
BJ
72 break;
73 case 'i':
94c88dd0
BJ
74 iflag = 1;
75 break;
2770968b
BJ
76
77 default:
94c88dd0 78 fprintf(stderr,
ac0d117c 79 "usage: %s [ -i ] [ -tTerm ] file...\n",
94c88dd0 80 argv[0]);
2770968b
BJ
81 exit(1);
82 }
94c88dd0 83
2770968b
BJ
84 switch(tgetent(termcap, termtype)) {
85
86 case 1:
2770968b
BJ
87 break;
88
89 default:
90 fprintf(stderr,"trouble reading termcap");
91 /* fall through to ... */
92
93 case 0:
94 /* No such terminal type - assume dumb */
ac0d117c 95 (void)strcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:");
2770968b
BJ
96 break;
97 }
94c88dd0
BJ
98 initcap();
99 if ( (tgetflag("os") && ENTER_BOLD==NULL ) ||
100 (tgetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
101 must_overstrike = 1;
94c88dd0
BJ
102 initbuf();
103 if (optind == argc)
2770968b 104 filter(stdin);
94c88dd0
BJ
105 else for (; optind<argc; optind++) {
106 f = fopen(argv[optind],"r");
2770968b 107 if (f == NULL) {
94c88dd0 108 perror(argv[optind]);
2770968b
BJ
109 exit(1);
110 } else
111 filter(f);
112 }
113 exit(0);
114}
115
116filter(f)
117FILE *f;
118{
94c88dd0 119 register c;
2770968b 120
94c88dd0
BJ
121 while((c = getc(f)) != EOF) switch(c) {
122
123 case '\b':
124 if (col > 0)
125 col--;
126 continue;
127
128 case '\t':
129 col = (col+8) & ~07;
130 if (col > maxcol)
131 maxcol = col;
132 continue;
133
134 case '\r':
135 col = 0;
136 continue;
137
138 case SO:
139 mode |= ALTSET;
140 continue;
141
142 case SI:
143 mode &= ~ALTSET;
144 continue;
145
146 case IESC:
147 switch (c = getc(f)) {
148
149 case HREV:
150 if (halfpos == 0) {
151 mode |= SUPERSC;
152 halfpos--;
153 } else if (halfpos > 0) {
154 mode &= ~SUBSC;
155 halfpos--;
2770968b 156 } else {
94c88dd0
BJ
157 halfpos = 0;
158 reverse();
2770968b 159 }
94c88dd0 160 continue;
2770968b 161
94c88dd0
BJ
162 case HFWD:
163 if (halfpos == 0) {
164 mode |= SUBSC;
165 halfpos++;
166 } else if (halfpos < 0) {
167 mode &= ~SUPERSC;
168 halfpos++;
169 } else {
170 halfpos = 0;
171 fwd();
2770968b 172 }
94c88dd0
BJ
173 continue;
174
175 case FREV:
176 reverse();
177 continue;
178
179 default:
180 fprintf(stderr,
181 "Unknown escape sequence in input: %o, %o\n",
182 IESC, c);
183 exit(1);
2770968b 184 }
94c88dd0
BJ
185 continue;
186
187 case '_':
188 if (obuf[col].c_char)
189 obuf[col].c_mode |= UNDERL | mode;
190 else
191 obuf[col].c_char = '_';
192 case ' ':
193 col++;
194 if (col > maxcol)
195 maxcol = col;
196 continue;
197
198 case '\n':
199 flushln();
5041df75 200 continue;
94c88dd0 201
ac0d117c
KB
202 case '\f':
203 flushln();
204 putchar('\f');
205 continue;
206
94c88dd0
BJ
207 default:
208 if (c < ' ') /* non printing */
209 continue;
210 if (obuf[col].c_char == '\0') {
211 obuf[col].c_char = c;
212 obuf[col].c_mode = mode;
213 } else if (obuf[col].c_char == '_') {
214 obuf[col].c_char = c;
215 obuf[col].c_mode |= UNDERL|mode;
5041df75 216 } else if (obuf[col].c_char == c)
94c88dd0 217 obuf[col].c_mode |= BOLD|mode;
eda93319 218 else
5041df75 219 obuf[col].c_mode = mode;
94c88dd0
BJ
220 col++;
221 if (col > maxcol)
222 maxcol = col;
223 continue;
2770968b 224 }
5041df75
RC
225 if (maxcol)
226 flushln();
2770968b
BJ
227}
228
94c88dd0 229flushln()
2770968b 230{
94c88dd0
BJ
231 register lastmode;
232 register i;
233 int hadmodes = 0;
234
235 lastmode = NORMAL;
236 for (i=0; i<maxcol; i++) {
237 if (obuf[i].c_mode != lastmode) {
238 hadmodes++;
239 setmode(obuf[i].c_mode);
240 lastmode = obuf[i].c_mode;
241 }
242 if (obuf[i].c_char == '\0') {
243 if (upln) {
244 puts(CURS_RIGHT);
245 } else
246 outc(' ');
247 } else
248 outc(obuf[i].c_char);
249 }
250 if (lastmode != NORMAL) {
251 setmode(0);
252 }
253 if (must_overstrike && hadmodes)
254 overstrike();
255 putchar('\n');
256 if (iflag && hadmodes)
257 iattr();
ac0d117c 258 (void)fflush(stdout);
94c88dd0
BJ
259 if (upln)
260 upln--;
261 initbuf();
2770968b
BJ
262}
263
94c88dd0
BJ
264/*
265 * For terminals that can overstrike, overstrike underlines and bolds.
266 * We don't do anything with halfline ups and downs, or Greek.
267 */
268overstrike()
269{
270 register int i;
271 char lbuf[256];
272 register char *cp = lbuf;
273 int hadbold=0;
2770968b 274
94c88dd0
BJ
275 /* Set up overstrike buffer */
276 for (i=0; i<maxcol; i++)
277 switch (obuf[i].c_mode) {
278 case NORMAL:
279 default:
280 *cp++ = ' ';
281 break;
282 case UNDERL:
283 *cp++ = '_';
284 break;
285 case BOLD:
286 *cp++ = obuf[i].c_char;
287 hadbold=1;
288 break;
289 }
290 putchar('\r');
291 for (*cp=' '; *cp==' '; cp--)
292 *cp = 0;
293 for (cp=lbuf; *cp; cp++)
294 putchar(*cp);
295 if (hadbold) {
296 putchar('\r');
297 for (cp=lbuf; *cp; cp++)
298 putchar(*cp=='_' ? ' ' : *cp);
299 putchar('\r');
300 for (cp=lbuf; *cp; cp++)
301 putchar(*cp=='_' ? ' ' : *cp);
302 }
303}
2770968b 304
94c88dd0 305iattr()
2770968b 306{
94c88dd0
BJ
307 register int i;
308 char lbuf[256];
309 register char *cp = lbuf;
2770968b 310
94c88dd0
BJ
311 for (i=0; i<maxcol; i++)
312 switch (obuf[i].c_mode) {
313 case NORMAL: *cp++ = ' '; break;
314 case ALTSET: *cp++ = 'g'; break;
315 case SUPERSC: *cp++ = '^'; break;
316 case SUBSC: *cp++ = 'v'; break;
317 case UNDERL: *cp++ = '_'; break;
318 case BOLD: *cp++ = '!'; break;
319 default: *cp++ = 'X'; break;
2770968b 320 }
94c88dd0
BJ
321 for (*cp=' '; *cp==' '; cp--)
322 *cp = 0;
323 for (cp=lbuf; *cp; cp++)
324 putchar(*cp);
325 putchar('\n');
2770968b
BJ
326}
327
94c88dd0 328initbuf()
2770968b 329{
2770968b 330
ac0d117c 331 bzero((char *)obuf, sizeof (obuf)); /* depends on NORMAL == 0 */
94c88dd0
BJ
332 col = 0;
333 maxcol = 0;
334 mode &= ALTSET;
2770968b
BJ
335}
336
94c88dd0 337fwd()
2770968b 338{
94c88dd0 339 register oldcol, oldmax;
2770968b 340
94c88dd0
BJ
341 oldcol = col;
342 oldmax = maxcol;
343 flushln();
344 col = oldcol;
345 maxcol = oldmax;
346}
347
348reverse()
349{
350 upln++;
351 fwd();
352 puts(CURS_UP);
353 puts(CURS_UP);
354 upln++;
355}
356
357initcap()
358{
359 static char tcapbuf[512];
94c88dd0
BJ
360 char *bp = tcapbuf;
361 char *getenv(), *tgetstr();
362
363 /* This nonsense attempts to work with both old and new termcap */
364 CURS_UP = tgetstr("up", &bp);
365 CURS_RIGHT = tgetstr("ri", &bp);
366 if (CURS_RIGHT == NULL)
367 CURS_RIGHT = tgetstr("nd", &bp);
368 CURS_LEFT = tgetstr("le", &bp);
369 if (CURS_LEFT == NULL)
370 CURS_LEFT = tgetstr("bc", &bp);
371 if (CURS_LEFT == NULL && tgetflag("bs"))
372 CURS_LEFT = "\b";
373
374 ENTER_STANDOUT = tgetstr("so", &bp);
375 EXIT_STANDOUT = tgetstr("se", &bp);
376 ENTER_UNDERLINE = tgetstr("us", &bp);
377 EXIT_UNDERLINE = tgetstr("ue", &bp);
378 ENTER_DIM = tgetstr("mh", &bp);
379 ENTER_BOLD = tgetstr("md", &bp);
380 ENTER_REVERSE = tgetstr("mr", &bp);
381 EXIT_ATTRIBUTES = tgetstr("me", &bp);
382
383 if (!ENTER_BOLD && ENTER_REVERSE)
384 ENTER_BOLD = ENTER_REVERSE;
385 if (!ENTER_BOLD && ENTER_STANDOUT)
386 ENTER_BOLD = ENTER_STANDOUT;
387 if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
388 ENTER_UNDERLINE = ENTER_STANDOUT;
389 EXIT_UNDERLINE = EXIT_STANDOUT;
390 }
391 if (!ENTER_DIM && ENTER_STANDOUT)
392 ENTER_DIM = ENTER_STANDOUT;
393 if (!ENTER_REVERSE && ENTER_STANDOUT)
394 ENTER_REVERSE = ENTER_STANDOUT;
395 if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
396 EXIT_ATTRIBUTES = EXIT_STANDOUT;
397
398 /*
399 * Note that we use REVERSE for the alternate character set,
400 * not the as/ae capabilities. This is because we are modelling
401 * the model 37 teletype (since that's what nroff outputs) and
402 * the typical as/ae is more of a graphics set, not the greek
403 * letters the 37 has.
404 */
405
94c88dd0
BJ
406 UNDER_CHAR = tgetstr("uc", &bp);
407 must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
408}
409
410outchar(c)
411char c;
412{
413 putchar(c&0177);
414}
415
416puts(str)
417char *str;
418{
419 if (str)
420 tputs(str, 1, outchar);
421}
422
423static curmode = 0;
424outc(c)
425char c;
426{
427 putchar(c);
428 if (must_use_uc && (curmode&UNDERL)) {
429 puts(CURS_LEFT);
430 puts(UNDER_CHAR);
431 }
432}
433
434setmode(newmode)
435int newmode;
436{
437 if (!iflag)
a7878ce2
BJ
438 {
439 if (curmode != NORMAL && newmode != NORMAL)
440 setmode(NORMAL);
94c88dd0
BJ
441 switch (newmode) {
442 case NORMAL:
443 switch(curmode) {
444 case NORMAL:
2770968b 445 break;
94c88dd0
BJ
446 case UNDERL:
447 puts(EXIT_UNDERLINE);
2770968b 448 break;
94c88dd0
BJ
449 default:
450 /* This includes standout */
451 puts(EXIT_ATTRIBUTES);
2770968b 452 break;
94c88dd0
BJ
453 }
454 break;
455 case ALTSET:
456 puts(ENTER_REVERSE);
457 break;
458 case SUPERSC:
459 /*
460 * This only works on a few terminals.
461 * It should be fixed.
462 */
463 puts(ENTER_UNDERLINE);
464 puts(ENTER_DIM);
465 break;
466 case SUBSC:
467 puts(ENTER_DIM);
468 break;
469 case UNDERL:
470 puts(ENTER_UNDERLINE);
471 break;
472 case BOLD:
473 puts(ENTER_BOLD);
474 break;
475 default:
476 /*
477 * We should have some provision here for multiple modes
478 * on at once. This will have to come later.
479 */
480 puts(ENTER_STANDOUT);
481 break;
2770968b 482 }
a7878ce2 483 }
94c88dd0
BJ
484 curmode = newmode;
485}