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