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