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