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