]> git.ipfire.org Git - thirdparty/util-linux.git/blob - text-utils/ul.c
Imported from util-linux-2.10s 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>
36 * to use terminfo instead of termcap.
37 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
38 * added Native Language Support
39 * 1999-09-19 Bruno Haible <haible@clisp.cons.org>
40 * modified to work correctly in multi-byte locales
41 */
42
43 #include <stdio.h>
44 #include <unistd.h> /* for getopt(), isatty() */
45 #include <string.h> /* for bzero(), strcpy() */
46 #include <term.h> /* for setupterm() */
47 #include <stdlib.h> /* for getenv() */
48 #include <limits.h> /* for INT_MAX */
49 #include "nls.h"
50
51 #include "widechar.h"
52
53 #ifdef ENABLE_WIDECHAR
54 static int put1wc(int c) /* Output an ASCII character as a wide character */
55 {
56 if (putwchar(c) == WEOF)
57 return EOF;
58 else
59 return c;
60 }
61 #define putwp(s) tputs(s,1,put1wc)
62 #else
63 #define putwp(s) putp(s)
64 #endif
65
66 void filter(FILE *f);
67 void flushln(void);
68 void overstrike(void);
69 void iattr(void);
70 void initbuf(void);
71 void fwd(void);
72 void reverse(void);
73 void initinfo(void);
74 void outc(wint_t c, int width);
75 void setmode(int newmode);
76 static void setcol(int newcol);
77 static void needcol(int col);
78
79 #define IESC '\033'
80 #define SO '\016'
81 #define SI '\017'
82 #define HFWD '9'
83 #define HREV '8'
84 #define FREV '7'
85
86 #define NORMAL 000
87 #define ALTSET 001 /* Reverse */
88 #define SUPERSC 002 /* Dim */
89 #define SUBSC 004 /* Dim | Ul */
90 #define UNDERL 010 /* Ul */
91 #define BOLD 020 /* Bold */
92 #define INITBUF 512
93
94 int must_use_uc, must_overstrike;
95 char *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
96 *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
97 *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
98
99 struct CHAR {
100 char c_mode;
101 wchar_t c_char;
102 int c_width;
103 } ;
104
105 struct CHAR *obuf;
106 int obuflen; /* Tracks number of elements in obuf. */
107 int col, maxcol;
108 int mode;
109 int halfpos;
110 int upln;
111 int iflag;
112
113 #define PRINT(s) if (s == NULL) /* void */; else putwp(s)
114
115 int main(int argc, char **argv)
116 {
117 extern int optind;
118 extern char *optarg;
119 int c, ret;
120 char *termtype;
121 FILE *f;
122
123 setlocale(LC_ALL, "");
124 bindtextdomain(PACKAGE, LOCALEDIR);
125 textdomain(PACKAGE);
126
127 termtype = getenv("TERM");
128 if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
129 termtype = "lpr";
130 while ((c=getopt(argc, argv, "it:T:")) != EOF)
131 switch(c) {
132
133 case 't':
134 case 'T': /* for nroff compatibility */
135 termtype = optarg;
136 break;
137 case 'i':
138 iflag = 1;
139 break;
140
141 default:
142 fprintf(stderr,
143 _("usage: %s [ -i ] [ -tTerm ] file...\n"),
144 argv[0]);
145 exit(1);
146 }
147 setupterm(termtype, 1, &ret);
148 switch(ret) {
149
150 case 1:
151 break;
152
153 default:
154 fprintf(stderr,_("trouble reading terminfo"));
155 /* fall through to ... */
156
157 case 0:
158 /* No such terminal type - assume dumb */
159 setupterm("dumb", 1, (int *)0);
160 break;
161 }
162 initinfo();
163 if ( (tigetflag("os") && ENTER_BOLD==NULL ) ||
164 (tigetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
165 must_overstrike = 1;
166 initbuf();
167 if (optind == argc)
168 filter(stdin);
169 else for (; optind<argc; optind++) {
170 f = fopen(argv[optind],"r");
171 if (f == NULL) {
172 perror(argv[optind]);
173 exit(1);
174 } else
175 filter(f);
176 }
177 if (ferror(stdout) || fclose(stdout))
178 return 1;
179 return 0;
180 }
181
182 void filter(FILE *f)
183 {
184 wint_t c;
185 int i, w;
186
187 while ((c = getwc(f)) != WEOF) switch(c) {
188
189 case '\b':
190 setcol(col - 1);
191 continue;
192
193 case '\t':
194 setcol((col+8) & ~07);
195 continue;
196
197 case '\r':
198 setcol(0);
199 continue;
200
201 case SO:
202 mode |= ALTSET;
203 continue;
204
205 case SI:
206 mode &= ~ALTSET;
207 continue;
208
209 case IESC:
210 switch (c = getwc(f)) {
211
212 case HREV:
213 if (halfpos == 0) {
214 mode |= SUPERSC;
215 halfpos--;
216 } else if (halfpos > 0) {
217 mode &= ~SUBSC;
218 halfpos--;
219 } else {
220 halfpos = 0;
221 reverse();
222 }
223 continue;
224
225 case HFWD:
226 if (halfpos == 0) {
227 mode |= SUBSC;
228 halfpos++;
229 } else if (halfpos < 0) {
230 mode &= ~SUPERSC;
231 halfpos++;
232 } else {
233 halfpos = 0;
234 fwd();
235 }
236 continue;
237
238 case FREV:
239 reverse();
240 continue;
241
242 default:
243 fprintf(stderr,
244 _("Unknown escape sequence in input: %o, %o\n"),
245 IESC, c);
246 exit(1);
247 }
248 continue;
249
250 case '_':
251 if (obuf[col].c_char || obuf[col].c_width < 0) {
252 while(col > 0 && obuf[col].c_width < 0)
253 col--;
254 w = obuf[col].c_width;
255 for (i = 0; i < w; i++)
256 obuf[col++].c_mode |= UNDERL | mode;
257 setcol(col);
258 continue;
259 }
260 obuf[col].c_char = '_';
261 obuf[col].c_width = 1;
262 /* fall through */
263 case ' ':
264 setcol(col + 1);
265 continue;
266
267 case '\n':
268 flushln();
269 continue;
270
271 case '\f':
272 flushln();
273 putwchar('\f');
274 continue;
275
276 default:
277 if (!iswprint(c)) /* non printing */
278 continue;
279 w = wcwidth(c);
280 needcol(col + w);
281 if (obuf[col].c_char == '\0') {
282 obuf[col].c_char = c;
283 for (i = 0; i < w; i++)
284 obuf[col+i].c_mode = mode;
285 obuf[col].c_width = w;
286 for (i = 1; i < w; i++)
287 obuf[col+i].c_width = -1;
288 } else if (obuf[col].c_char == '_') {
289 obuf[col].c_char = c;
290 for (i = 0; i < w; i++)
291 obuf[col+i].c_mode |= UNDERL|mode;
292 obuf[col].c_width = w;
293 for (i = 1; i < w; i++)
294 obuf[col+i].c_width = -1;
295 } else if (obuf[col].c_char == c) {
296 for (i = 0; i < w; i++)
297 obuf[col+i].c_mode |= BOLD|mode;
298 } else {
299 w = obuf[col].c_width;
300 for (i = 0; i < w; i++)
301 obuf[col+i].c_mode = mode;
302 }
303 setcol(col + w);
304 continue;
305 }
306 if (maxcol)
307 flushln();
308 }
309
310 void flushln(void)
311 {
312 int lastmode;
313 int i;
314 int hadmodes = 0;
315
316 lastmode = NORMAL;
317 for (i=0; i<maxcol; i++) {
318 if (obuf[i].c_mode != lastmode) {
319 hadmodes++;
320 setmode(obuf[i].c_mode);
321 lastmode = obuf[i].c_mode;
322 }
323 if (obuf[i].c_char == '\0') {
324 if (upln) {
325 PRINT(CURS_RIGHT);
326 } else
327 outc(' ', 1);
328 } else
329 outc(obuf[i].c_char, obuf[i].c_width);
330 if (obuf[i].c_width > 1)
331 i += obuf[i].c_width -1;
332 }
333 if (lastmode != NORMAL) {
334 setmode(0);
335 }
336 if (must_overstrike && hadmodes)
337 overstrike();
338 putwchar('\n');
339 if (iflag && hadmodes)
340 iattr();
341 (void)fflush(stdout);
342 if (upln)
343 upln--;
344 initbuf();
345 }
346
347 /*
348 * For terminals that can overstrike, overstrike underlines and bolds.
349 * We don't do anything with halfline ups and downs, or Greek.
350 */
351 void overstrike(void)
352 {
353 register int i;
354 #ifdef __GNUC__
355 register wchar_t *lbuf = __builtin_alloca((maxcol+1)*sizeof(wchar_t));
356 #else
357 wchar_t lbuf[256];
358 #endif
359 register wchar_t *cp = lbuf;
360 int hadbold=0;
361
362 /* Set up overstrike buffer */
363 for (i=0; i<maxcol; i++)
364 switch (obuf[i].c_mode) {
365 case NORMAL:
366 default:
367 *cp++ = ' ';
368 break;
369 case UNDERL:
370 *cp++ = '_';
371 break;
372 case BOLD:
373 *cp++ = obuf[i].c_char;
374 if (obuf[i].c_width > 1)
375 i += obuf[i].c_width - 1;
376 hadbold=1;
377 break;
378 }
379 putwchar('\r');
380 for (*cp=' '; *cp==' '; cp--)
381 *cp = 0;
382 for (cp=lbuf; *cp; cp++)
383 putwchar(*cp);
384 if (hadbold) {
385 putwchar('\r');
386 for (cp=lbuf; *cp; cp++)
387 putwchar(*cp=='_' ? ' ' : *cp);
388 putwchar('\r');
389 for (cp=lbuf; *cp; cp++)
390 putwchar(*cp=='_' ? ' ' : *cp);
391 }
392 }
393
394 void iattr(void)
395 {
396 register int i;
397 #ifdef __GNUC__
398 register char *lbuf = __builtin_alloca((maxcol+1)*sizeof(char));
399 #else
400 char lbuf[256];
401 #endif
402 register char *cp = lbuf;
403
404 for (i=0; i<maxcol; i++)
405 switch (obuf[i].c_mode) {
406 case NORMAL: *cp++ = ' '; break;
407 case ALTSET: *cp++ = 'g'; break;
408 case SUPERSC: *cp++ = '^'; break;
409 case SUBSC: *cp++ = 'v'; break;
410 case UNDERL: *cp++ = '_'; break;
411 case BOLD: *cp++ = '!'; break;
412 default: *cp++ = 'X'; break;
413 }
414 for (*cp=' '; *cp==' '; cp--)
415 *cp = 0;
416 for (cp=lbuf; *cp; cp++)
417 putwchar(*cp);
418 putwchar('\n');
419 }
420
421 void initbuf(void)
422 {
423 if (obuf == NULL) { /* First time. */
424 obuflen = INITBUF;
425 obuf = malloc(sizeof(struct CHAR) * obuflen);
426 if (obuf == NULL) {
427 fprintf(stderr, _("Unable to allocate buffer.\n"));
428 exit(1);
429 }
430 }
431
432 /* assumes NORMAL == 0 */
433 memset(obuf, 0, sizeof(struct CHAR) * obuflen);
434 setcol(0);
435 maxcol = 0;
436 mode &= ALTSET;
437 }
438
439 void fwd(void)
440 {
441 int oldcol, oldmax;
442
443 oldcol = col;
444 oldmax = maxcol;
445 flushln();
446 setcol(oldcol);
447 maxcol = oldmax;
448 }
449
450 void reverse(void)
451 {
452 upln++;
453 fwd();
454 PRINT(CURS_UP);
455 PRINT(CURS_UP);
456 upln++;
457 }
458
459 void initinfo(void)
460 {
461 CURS_UP = tigetstr("cuu1");
462 CURS_RIGHT = tigetstr("cuf1");
463 CURS_LEFT = tigetstr("cub1");
464 if (CURS_LEFT == NULL)
465 CURS_LEFT = "\b";
466
467 ENTER_STANDOUT = tigetstr("smso");
468 EXIT_STANDOUT = tigetstr("rmso");
469 ENTER_UNDERLINE = tigetstr("smul");
470 EXIT_UNDERLINE = tigetstr("rmul");
471 ENTER_DIM = tigetstr("dim");
472 ENTER_BOLD = tigetstr("bold");
473 ENTER_REVERSE = tigetstr("rev");
474 EXIT_ATTRIBUTES = tigetstr("sgr0");
475
476 if (!ENTER_BOLD && ENTER_REVERSE)
477 ENTER_BOLD = ENTER_REVERSE;
478 if (!ENTER_BOLD && ENTER_STANDOUT)
479 ENTER_BOLD = ENTER_STANDOUT;
480 if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
481 ENTER_UNDERLINE = ENTER_STANDOUT;
482 EXIT_UNDERLINE = EXIT_STANDOUT;
483 }
484 if (!ENTER_DIM && ENTER_STANDOUT)
485 ENTER_DIM = ENTER_STANDOUT;
486 if (!ENTER_REVERSE && ENTER_STANDOUT)
487 ENTER_REVERSE = ENTER_STANDOUT;
488 if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
489 EXIT_ATTRIBUTES = EXIT_STANDOUT;
490
491 /*
492 * Note that we use REVERSE for the alternate character set,
493 * not the as/ae capabilities. This is because we are modelling
494 * the model 37 teletype (since that's what nroff outputs) and
495 * the typical as/ae is more of a graphics set, not the greek
496 * letters the 37 has.
497 */
498
499 UNDER_CHAR = tigetstr("uc");
500 must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
501 }
502
503 static int curmode = 0;
504
505 void
506 outc(wint_t c, int width) {
507 int i;
508
509 putwchar(c);
510 if (must_use_uc && (curmode&UNDERL)) {
511 for (i=0; i<width; i++)
512 PRINT(CURS_LEFT);
513 for (i=0; i<width; i++)
514 PRINT(UNDER_CHAR);
515 }
516 }
517
518 void setmode(int newmode)
519 {
520 if (!iflag) {
521 if (curmode != NORMAL && newmode != NORMAL)
522 setmode(NORMAL);
523 switch (newmode) {
524 case NORMAL:
525 switch(curmode) {
526 case NORMAL:
527 break;
528 case UNDERL:
529 PRINT(EXIT_UNDERLINE);
530 break;
531 default:
532 /* This includes standout */
533 PRINT(EXIT_ATTRIBUTES);
534 break;
535 }
536 break;
537 case ALTSET:
538 PRINT(ENTER_REVERSE);
539 break;
540 case SUPERSC:
541 /*
542 * This only works on a few terminals.
543 * It should be fixed.
544 */
545 PRINT(ENTER_UNDERLINE);
546 PRINT(ENTER_DIM);
547 break;
548 case SUBSC:
549 PRINT(ENTER_DIM);
550 break;
551 case UNDERL:
552 PRINT(ENTER_UNDERLINE);
553 break;
554 case BOLD:
555 PRINT(ENTER_BOLD);
556 break;
557 default:
558 /*
559 * We should have some provision here for multiple modes
560 * on at once. This will have to come later.
561 */
562 PRINT(ENTER_STANDOUT);
563 break;
564 }
565 }
566 curmode = newmode;
567 }
568
569 static void
570 setcol(int newcol) {
571 col = newcol;
572
573 if (col < 0)
574 col = 0;
575 else if (col > maxcol)
576 needcol(col);
577 }
578
579 static void
580 needcol(int col) {
581 maxcol = col;
582
583 /* If col >= obuflen, expand obuf until obuflen > col. */
584 while (col >= obuflen) {
585 /* Paranoid check for obuflen == INT_MAX. */
586 if (obuflen == INT_MAX) {
587 fprintf(stderr,
588 _("Input line too long.\n"));
589 exit(1);
590 }
591
592 /* Similar paranoia: double only up to INT_MAX. */
593 obuflen = ((INT_MAX / 2) < obuflen)
594 ? INT_MAX
595 : obuflen * 2;
596
597 /* Now we can try to expand obuf. */
598 obuf = realloc(obuf, sizeof(struct CHAR) * obuflen);
599 if (obuf == NULL) {
600 fprintf(stderr,
601 _("Out of memory when growing buffer.\n"));
602 exit(1);
603 }
604 }
605 }