]>
Commit | Line | Data |
---|---|---|
6dbe3af9 KZ |
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 | ||
fd6b7a7f | 34 | /* |
eb63b9b8 KZ |
35 | * modified by Kars de Jong <jongk@cs.utwente.nl> |
36 | * to use terminfo instead of termcap. | |
b50945d4 | 37 | * 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL> |
50646353 | 38 | * added Native Language Support |
eb63b9b8 | 39 | * 1999-09-19 Bruno Haible <haible@clisp.cons.org> |
50646353 | 40 | * modified to work correctly in multi-byte locales |
eb63b9b8 | 41 | */ |
fd6b7a7f | 42 | |
6dbe3af9 | 43 | #include <stdio.h> |
fd6b7a7f | 44 | #include <unistd.h> /* for getopt(), isatty() */ |
c0f19ccf | 45 | #include <string.h> /* for memset(), strcpy() */ |
2b6fc908 | 46 | #include <stdlib.h> /* for getenv() */ |
5c36a0eb | 47 | #include <limits.h> /* for INT_MAX */ |
4727b169 | 48 | #include <signal.h> /* for signal() */ |
59f8c787 | 49 | #include <errno.h> |
a3b76180 | 50 | #include <getopt.h> |
fd6b7a7f | 51 | |
3947ca4c KZ |
52 | #if defined(HAVE_NCURSESW_TERM_H) |
53 | # include <ncursesw/term.h> | |
2ac1bc84 KZ |
54 | #elif defined(HAVE_NCURSES_TERM_H) |
55 | # include <ncurses/term.h> | |
3947ca4c KZ |
56 | #elif defined(HAVE_TERM_H) |
57 | # include <term.h> | |
2ac1bc84 KZ |
58 | #endif |
59 | ||
f0961db2 DB |
60 | #include "nls.h" |
61 | #include "xalloc.h" | |
eb63b9b8 | 62 | #include "widechar.h" |
eb76ca98 | 63 | #include "c.h" |
b87cbe84 | 64 | #include "closestream.h" |
eb63b9b8 | 65 | |
9a59ee65 | 66 | #define ESC '\033' |
6dbe3af9 KZ |
67 | #define SO '\016' |
68 | #define SI '\017' | |
69 | #define HFWD '9' | |
70 | #define HREV '8' | |
71 | #define FREV '7' | |
6dbe3af9 | 72 | |
eaf68e30 | 73 | enum { |
9a59ee65 | 74 | NORMAL_CHARSET = 0, /* Must be zero, see initbuf() */ |
580c6325 | 75 | ALTERNATIVE_CHARSET = 1 << 0, /* Reverse */ |
9a59ee65 SK |
76 | SUPERSCRIPT = 1 << 1, /* Dim */ |
77 | SUBSCRIPT = 1 << 2, /* Dim | Ul */ | |
78 | UNDERLINE = 1 << 3, /* Ul */ | |
79 | BOLD = 1 << 4, /* Bold */ | |
eaf68e30 | 80 | }; |
6dbe3af9 | 81 | |
49421939 SK |
82 | struct term_caps { |
83 | char *curs_up; | |
84 | char *curs_right; | |
85 | char *curs_left; | |
86 | char *enter_standout; | |
87 | char *exit_standout; | |
88 | char *enter_underline; | |
89 | char *exit_underline; | |
90 | char *enter_dim; | |
91 | char *enter_bold; | |
92 | char *enter_reverse; | |
93 | char *under_char; | |
94 | char *exit_attributes; | |
95 | }; | |
6dbe3af9 | 96 | |
eaf68e30 | 97 | struct ul_char { |
eb63b9b8 | 98 | wchar_t c_char; |
66ee8158 | 99 | int c_width; |
eaf68e30 | 100 | char c_mode; |
565239b0 | 101 | }; |
6dbe3af9 | 102 | |
eaf68e30 | 103 | struct ul_ctl { |
0186ff36 SK |
104 | size_t column; |
105 | size_t max_column; | |
9a59ee65 SK |
106 | int half_position; |
107 | int up_line; | |
eaf68e30 | 108 | int mode; |
9a59ee65 | 109 | int current_mode; |
0186ff36 | 110 | size_t buflen; |
9a59ee65 | 111 | struct ul_char *buf; |
eaf68e30 | 112 | unsigned int |
9a59ee65 | 113 | indicated_opt:1, |
eaf68e30 SK |
114 | must_use_uc:1, |
115 | must_overstrike:1; | |
116 | }; | |
a2811907 | 117 | |
86be6a32 | 118 | static void __attribute__((__noreturn__)) usage(void) |
a3b76180 | 119 | { |
86be6a32 | 120 | FILE *out = stdout; |
5b627d84 | 121 | |
a2343e03 KZ |
122 | fputs(USAGE_HEADER, out); |
123 | fprintf(out, _(" %s [options] [<file> ...]\n"), program_invocation_short_name); | |
a3b76180 | 124 | |
451dbcfa BS |
125 | fputs(USAGE_SEPARATOR, out); |
126 | fputs(_("Do underlining.\n"), out); | |
127 | ||
128 | fputs(USAGE_OPTIONS, out); | |
a2343e03 KZ |
129 | fputs(_(" -t, -T, --terminal TERMINAL override the TERM environment variable\n"), out); |
130 | fputs(_(" -i, --indicated underlining is indicated via a separate line\n"), out); | |
f45f3ec3 | 131 | printf(USAGE_HELP_OPTIONS(30)); |
a2343e03 | 132 | |
f45f3ec3 | 133 | printf(USAGE_MAN_TAIL("ul(1)")); |
a3b76180 | 134 | |
86be6a32 | 135 | exit(EXIT_SUCCESS); |
a3b76180 SK |
136 | } |
137 | ||
0186ff36 | 138 | static void need_column(struct ul_ctl *ctl, size_t new_max) |
6dbe3af9 | 139 | { |
9a59ee65 | 140 | ctl->max_column = new_max; |
6dbe3af9 | 141 | |
9a59ee65 | 142 | while (new_max >= ctl->buflen) { |
0186ff36 | 143 | ctl->buflen *= 2; |
9a59ee65 | 144 | ctl->buf = xrealloc(ctl->buf, sizeof(struct ul_char) * ctl->buflen); |
a2811907 SK |
145 | } |
146 | } | |
4727b169 | 147 | |
0186ff36 | 148 | static void set_column(struct ul_ctl *ctl, size_t column) |
a2811907 | 149 | { |
9a59ee65 | 150 | ctl->column = column; |
565239b0 | 151 | |
0186ff36 | 152 | if (ctl->max_column < ctl->column) |
9a59ee65 | 153 | need_column(ctl, ctl->column); |
a2811907 | 154 | } |
6dbe3af9 | 155 | |
9a59ee65 | 156 | static void init_buffer(struct ul_ctl *ctl) |
a2811907 | 157 | { |
9a59ee65 | 158 | if (ctl->buf == NULL) { |
a2811907 | 159 | /* First time. */ |
9a59ee65 SK |
160 | ctl->buflen = BUFSIZ; |
161 | ctl->buf = xcalloc(ctl->buflen, sizeof(struct ul_char)); | |
a2811907 | 162 | } else |
580c6325 | 163 | /* assumes NORMAL_CHARSET == 0 */ |
9a59ee65 | 164 | memset(ctl->buf, 0, sizeof(struct ul_char) * ctl->max_column); |
2c308875 | 165 | |
9a59ee65 SK |
166 | set_column(ctl, 0); |
167 | ctl->max_column = 0; | |
580c6325 | 168 | ctl->mode &= ALTERNATIVE_CHARSET; |
a2811907 | 169 | } |
6dbe3af9 | 170 | |
9a59ee65 | 171 | static void init_term_caps(struct ul_ctl *ctl, struct term_caps *const tcs) |
a2811907 | 172 | { |
49421939 SK |
173 | tcs->curs_up = tigetstr("cuu1"); |
174 | tcs->curs_right = tigetstr("cuf1"); | |
175 | tcs->curs_left = tigetstr("cub1"); | |
176 | if (tcs->curs_left == NULL) | |
177 | tcs->curs_left = "\b"; | |
178 | ||
179 | tcs->enter_standout = tigetstr("smso"); | |
180 | tcs->exit_standout = tigetstr("rmso"); | |
181 | tcs->enter_underline = tigetstr("smul"); | |
182 | tcs->exit_underline = tigetstr("rmul"); | |
183 | tcs->enter_dim = tigetstr("dim"); | |
184 | tcs->enter_bold = tigetstr("bold"); | |
185 | tcs->enter_reverse = tigetstr("rev"); | |
186 | tcs->exit_attributes = tigetstr("sgr0"); | |
187 | ||
188 | if (!tcs->enter_bold && tcs->enter_reverse) | |
189 | tcs->enter_bold = tcs->enter_reverse; | |
190 | ||
191 | if (!tcs->enter_bold && tcs->enter_standout) | |
192 | tcs->enter_bold = tcs->enter_standout; | |
193 | ||
194 | if (!tcs->enter_underline && tcs->enter_standout) { | |
50646353 KZ |
195 | tcs->enter_underline = tcs->enter_standout; |
196 | tcs->exit_underline = tcs->exit_standout; | |
6dbe3af9 | 197 | } |
49421939 SK |
198 | |
199 | if (!tcs->enter_dim && tcs->enter_standout) | |
50646353 | 200 | tcs->enter_dim = tcs->enter_standout; |
49421939 SK |
201 | |
202 | if (!tcs->enter_reverse && tcs->enter_standout) | |
50646353 | 203 | tcs->enter_reverse = tcs->enter_standout; |
49421939 SK |
204 | |
205 | if (!tcs->exit_attributes && tcs->exit_standout) | |
50646353 | 206 | tcs->exit_attributes = tcs->exit_standout; |
5b627d84 | 207 | |
a2811907 SK |
208 | /* |
209 | * Note that we use REVERSE for the alternate character set, | |
210 | * not the as/ae capabilities. This is because we are modeling | |
211 | * the model 37 teletype (since that's what nroff outputs) and | |
212 | * the typical as/ae is more of a graphics set, not the greek | |
213 | * letters the 37 has. | |
214 | */ | |
49421939 | 215 | tcs->under_char = tigetstr("uc"); |
eaf68e30 | 216 | ctl->must_use_uc = (tcs->under_char && !tcs->enter_underline); |
49421939 SK |
217 | |
218 | if ((tigetflag("os") && tcs->enter_bold == NULL) || | |
50646353 KZ |
219 | (tigetflag("ul") && tcs->enter_underline == NULL |
220 | && tcs->under_char == NULL)) | |
eaf68e30 | 221 | ctl->must_overstrike = 1; |
6dbe3af9 KZ |
222 | } |
223 | ||
a2811907 | 224 | static void sig_handler(int signo __attribute__((__unused__))) |
a4949aaa | 225 | { |
a2811907 SK |
226 | _exit(EXIT_SUCCESS); |
227 | } | |
a4949aaa | 228 | |
52e3ce00 SK |
229 | static int ul_putwchar(int c) |
230 | { | |
231 | if (putwchar(c) == WEOF) | |
232 | return EOF; | |
233 | return c; | |
234 | } | |
235 | ||
9a59ee65 | 236 | static void print_line(char *line) |
a2811907 SK |
237 | { |
238 | if (line == NULL) | |
239 | return; | |
52e3ce00 | 240 | tputs(line, STDOUT_FILENO, ul_putwchar); |
a2811907 SK |
241 | } |
242 | ||
9a59ee65 SK |
243 | static void ul_setmode(struct ul_ctl *ctl, struct term_caps const *const tcs, |
244 | int new_mode) | |
a2811907 | 245 | { |
9a59ee65 SK |
246 | if (!ctl->indicated_opt) { |
247 | if (ctl->current_mode != NORMAL_CHARSET && new_mode != NORMAL_CHARSET) | |
248 | ul_setmode(ctl, tcs, NORMAL_CHARSET); | |
50646353 | 249 | |
9a59ee65 | 250 | switch (new_mode) { |
580c6325 | 251 | case NORMAL_CHARSET: |
9a59ee65 | 252 | switch (ctl->current_mode) { |
580c6325 | 253 | case NORMAL_CHARSET: |
a2811907 | 254 | break; |
580c6325 | 255 | case UNDERLINE: |
9a59ee65 | 256 | print_line(tcs->exit_underline); |
a2811907 SK |
257 | break; |
258 | default: | |
259 | /* This includes standout */ | |
9a59ee65 | 260 | print_line(tcs->exit_attributes); |
a2811907 SK |
261 | break; |
262 | } | |
263 | break; | |
580c6325 | 264 | case ALTERNATIVE_CHARSET: |
9a59ee65 | 265 | print_line(tcs->enter_reverse); |
a2811907 | 266 | break; |
580c6325 | 267 | case SUPERSCRIPT: |
a2811907 SK |
268 | /* |
269 | * This only works on a few terminals. | |
270 | * It should be fixed. | |
271 | */ | |
9a59ee65 SK |
272 | print_line(tcs->enter_underline); |
273 | print_line(tcs->enter_dim); | |
a2811907 | 274 | break; |
580c6325 | 275 | case SUBSCRIPT: |
9a59ee65 | 276 | print_line(tcs->enter_dim); |
a2811907 | 277 | break; |
580c6325 | 278 | case UNDERLINE: |
9a59ee65 | 279 | print_line(tcs->enter_underline); |
a2811907 SK |
280 | break; |
281 | case BOLD: | |
9a59ee65 | 282 | print_line(tcs->enter_bold); |
a2811907 SK |
283 | break; |
284 | default: | |
285 | /* | |
286 | * We should have some provision here for multiple modes | |
287 | * on at once. This will have to come later. | |
288 | */ | |
9a59ee65 | 289 | print_line(tcs->enter_standout); |
a2811907 | 290 | break; |
a4949aaa | 291 | } |
a4949aaa | 292 | } |
9a59ee65 | 293 | ctl->current_mode = new_mode; |
a4949aaa SK |
294 | } |
295 | ||
9a59ee65 | 296 | static void indicate_attribute(struct ul_ctl *ctl) |
6dbe3af9 | 297 | { |
0186ff36 | 298 | size_t i; |
9a59ee65 SK |
299 | wchar_t *buf = xcalloc(ctl->max_column + 1, sizeof(wchar_t)); |
300 | wchar_t *p = buf; | |
301 | ||
50646353 | 302 | for (i = 0; i < ctl->max_column; i++) { |
9a59ee65 SK |
303 | switch (ctl->buf[i].c_mode) { |
304 | case NORMAL_CHARSET: *p++ = ' '; break; | |
305 | case ALTERNATIVE_CHARSET: *p++ = 'g'; break; | |
306 | case SUPERSCRIPT: *p++ = '^'; break; | |
307 | case SUBSCRIPT: *p++ = 'v'; break; | |
308 | case UNDERLINE: *p++ = '_'; break; | |
309 | case BOLD: *p++ = '!'; break; | |
310 | default: *p++ = 'X'; break; | |
66ee8158 | 311 | } |
50646353 KZ |
312 | } |
313 | ||
9a59ee65 SK |
314 | for (*p = ' '; *p == ' '; p--) |
315 | *p = 0; | |
50646353 | 316 | |
9a59ee65 | 317 | fputws(buf, stdout); |
a2811907 | 318 | putwchar('\n'); |
9a59ee65 | 319 | free(buf); |
6dbe3af9 KZ |
320 | } |
321 | ||
9a59ee65 SK |
322 | static void output_char(struct ul_ctl *ctl, struct term_caps const *const tcs, |
323 | wint_t c, int width) | |
6dbe3af9 | 324 | { |
5c36a0eb | 325 | int i; |
6dbe3af9 | 326 | |
a2811907 | 327 | putwchar(c); |
9a59ee65 | 328 | if (ctl->must_use_uc && (ctl->current_mode & UNDERLINE)) { |
a2811907 | 329 | for (i = 0; i < width; i++) |
9a59ee65 | 330 | print_line(tcs->curs_left); |
a2811907 | 331 | for (i = 0; i < width; i++) |
9a59ee65 | 332 | print_line(tcs->under_char); |
6dbe3af9 | 333 | } |
6dbe3af9 KZ |
334 | } |
335 | ||
336 | /* | |
337 | * For terminals that can overstrike, overstrike underlines and bolds. | |
338 | * We don't do anything with halfline ups and downs, or Greek. | |
339 | */ | |
eaf68e30 | 340 | static void overstrike(struct ul_ctl *ctl) |
6dbe3af9 | 341 | { |
0186ff36 | 342 | size_t i; |
9a59ee65 SK |
343 | wchar_t *buf = xcalloc(ctl->max_column + 1, sizeof(wchar_t)); |
344 | wchar_t *p = buf; | |
345 | int had_bold = 0; | |
6dbe3af9 KZ |
346 | |
347 | /* Set up overstrike buffer */ | |
50646353 | 348 | for (i = 0; i < ctl->max_column; i++) { |
9a59ee65 | 349 | switch (ctl->buf[i].c_mode) { |
580c6325 | 350 | case NORMAL_CHARSET: |
6dbe3af9 | 351 | default: |
9a59ee65 | 352 | *p++ = ' '; |
6dbe3af9 | 353 | break; |
580c6325 | 354 | case UNDERLINE: |
9a59ee65 | 355 | *p++ = '_'; |
6dbe3af9 KZ |
356 | break; |
357 | case BOLD: | |
9a59ee65 | 358 | *p++ = ctl->buf[i].c_char; |
1a3f71b2 | 359 | if (1 < ctl->buf[i].c_width) |
9a59ee65 SK |
360 | i += ctl->buf[i].c_width - 1; |
361 | had_bold = 1; | |
6dbe3af9 KZ |
362 | break; |
363 | } | |
50646353 KZ |
364 | } |
365 | ||
eb63b9b8 | 366 | putwchar('\r'); |
9a59ee65 SK |
367 | for (*p = ' '; *p == ' '; p--) |
368 | *p = 0; | |
369 | fputws(buf, stdout); | |
50646353 | 370 | |
9a59ee65 | 371 | if (had_bold) { |
eb63b9b8 | 372 | putwchar('\r'); |
9a59ee65 SK |
373 | for (p = buf; *p; p++) |
374 | putwchar(*p == '_' ? ' ' : *p); | |
eb63b9b8 | 375 | putwchar('\r'); |
9a59ee65 SK |
376 | for (p = buf; *p; p++) |
377 | putwchar(*p == '_' ? ' ' : *p); | |
6dbe3af9 | 378 | } |
9a59ee65 | 379 | free(buf); |
6dbe3af9 KZ |
380 | } |
381 | ||
9a59ee65 | 382 | static void flush_line(struct ul_ctl *ctl, struct term_caps const *const tcs) |
6dbe3af9 | 383 | { |
9a59ee65 | 384 | int last_mode; |
0186ff36 | 385 | size_t i; |
9a59ee65 SK |
386 | int had_mode = 0; |
387 | ||
388 | last_mode = NORMAL_CHARSET; | |
389 | for (i = 0; i < ctl->max_column; i++) { | |
390 | if (ctl->buf[i].c_mode != last_mode) { | |
391 | had_mode = 1; | |
392 | ul_setmode(ctl, tcs, ctl->buf[i].c_mode); | |
393 | last_mode = ctl->buf[i].c_mode; | |
6dbe3af9 | 394 | } |
9a59ee65 SK |
395 | if (ctl->buf[i].c_char == '\0') { |
396 | if (ctl->up_line) | |
397 | print_line(tcs->curs_right); | |
a2811907 | 398 | else |
9a59ee65 | 399 | output_char(ctl, tcs, ' ', 1); |
a2811907 | 400 | } else |
9a59ee65 | 401 | output_char(ctl, tcs, ctl->buf[i].c_char, ctl->buf[i].c_width); |
1a3f71b2 | 402 | if (1 < ctl->buf[i].c_width) |
9a59ee65 | 403 | i += ctl->buf[i].c_width - 1; |
a2811907 | 404 | } |
9a59ee65 SK |
405 | if (last_mode != NORMAL_CHARSET) |
406 | ul_setmode(ctl, tcs, NORMAL_CHARSET); | |
407 | if (ctl->must_overstrike && had_mode) | |
eaf68e30 | 408 | overstrike(ctl); |
eb63b9b8 | 409 | putwchar('\n'); |
9a59ee65 SK |
410 | if (ctl->indicated_opt && had_mode) |
411 | indicate_attribute(ctl); | |
a2811907 | 412 | fflush(stdout); |
9a59ee65 SK |
413 | if (ctl->up_line) |
414 | ctl->up_line--; | |
415 | init_buffer(ctl); | |
6dbe3af9 KZ |
416 | } |
417 | ||
9a59ee65 | 418 | static void forward(struct ul_ctl *ctl, struct term_caps const *const tcs) |
6dbe3af9 | 419 | { |
9a59ee65 | 420 | int old_column, old_maximum; |
6dbe3af9 | 421 | |
9a59ee65 SK |
422 | old_column = ctl->column; |
423 | old_maximum = ctl->max_column; | |
424 | flush_line(ctl, tcs); | |
425 | set_column(ctl, old_column); | |
426 | ctl->max_column = old_maximum; | |
6dbe3af9 KZ |
427 | } |
428 | ||
eaf68e30 | 429 | static void reverse(struct ul_ctl *ctl, struct term_caps const *const tcs) |
6dbe3af9 | 430 | { |
9a59ee65 SK |
431 | ctl->up_line++; |
432 | forward(ctl, tcs); | |
433 | print_line(tcs->curs_up); | |
434 | print_line(tcs->curs_up); | |
435 | ctl->up_line++; | |
6dbe3af9 KZ |
436 | } |
437 | ||
eaf68e30 | 438 | static int handle_escape(struct ul_ctl *ctl, struct term_caps const *const tcs, FILE *f) |
6dbe3af9 | 439 | { |
a2811907 | 440 | wint_t c; |
6dbe3af9 | 441 | |
a2811907 SK |
442 | switch (c = getwc(f)) { |
443 | case HREV: | |
1a3f71b2 | 444 | if (0 < ctl->half_position) { |
580c6325 | 445 | ctl->mode &= ~SUBSCRIPT; |
9a59ee65 | 446 | ctl->half_position--; |
1a3f71b2 SK |
447 | } else if (ctl->half_position == 0) { |
448 | ctl->mode |= SUPERSCRIPT; | |
449 | ctl->half_position--; | |
a2811907 | 450 | } else { |
9a59ee65 | 451 | ctl->half_position = 0; |
eaf68e30 | 452 | reverse(ctl, tcs); |
a2811907 SK |
453 | } |
454 | return 0; | |
455 | case HFWD: | |
1a3f71b2 | 456 | if (ctl->half_position < 0) { |
580c6325 | 457 | ctl->mode &= ~SUPERSCRIPT; |
9a59ee65 | 458 | ctl->half_position++; |
1a3f71b2 SK |
459 | } else if (ctl->half_position == 0) { |
460 | ctl->mode |= SUBSCRIPT; | |
461 | ctl->half_position++; | |
a2811907 | 462 | } else { |
9a59ee65 SK |
463 | ctl->half_position = 0; |
464 | forward(ctl, tcs); | |
a2811907 SK |
465 | } |
466 | return 0; | |
467 | case FREV: | |
eaf68e30 | 468 | reverse(ctl, tcs); |
a2811907 SK |
469 | return 0; |
470 | default: | |
471 | /* unknown escape */ | |
472 | ungetwc(c, f); | |
473 | return 1; | |
6dbe3af9 | 474 | } |
6dbe3af9 KZ |
475 | } |
476 | ||
eaf68e30 | 477 | static void filter(struct ul_ctl *ctl, struct term_caps const *const tcs, FILE *f) |
5b627d84 | 478 | { |
a2811907 | 479 | wint_t c; |
9a59ee65 | 480 | int i, width; |
6dbe3af9 | 481 | |
a2811907 SK |
482 | while ((c = getwc(f)) != WEOF) { |
483 | switch (c) { | |
484 | case '\b': | |
792ff9fc | 485 | set_column(ctl, ctl->column && 0 < ctl->column ? ctl->column - 1 : 0); |
a2811907 SK |
486 | continue; |
487 | case '\t': | |
9a59ee65 | 488 | set_column(ctl, (ctl->column + 8) & ~07); |
a2811907 SK |
489 | continue; |
490 | case '\r': | |
9a59ee65 | 491 | set_column(ctl, 0); |
a2811907 SK |
492 | continue; |
493 | case SO: | |
580c6325 | 494 | ctl->mode |= ALTERNATIVE_CHARSET; |
a2811907 SK |
495 | continue; |
496 | case SI: | |
580c6325 | 497 | ctl->mode &= ~ALTERNATIVE_CHARSET; |
a2811907 | 498 | continue; |
9a59ee65 | 499 | case ESC: |
eaf68e30 | 500 | if (handle_escape(ctl, tcs, f)) { |
a2811907 SK |
501 | c = getwc(f); |
502 | errx(EXIT_FAILURE, | |
9a59ee65 | 503 | _("unknown escape sequence in input: %o, %o"), ESC, c); |
6dbe3af9 | 504 | } |
a2811907 SK |
505 | continue; |
506 | case '_': | |
9a59ee65 | 507 | if (ctl->buf[ctl->column].c_char || ctl->buf[ctl->column].c_width < 0) { |
1a3f71b2 | 508 | while (ctl->buf[ctl->column].c_width < 0 && 0 < ctl->column) |
9a59ee65 SK |
509 | ctl->column--; |
510 | width = ctl->buf[ctl->column].c_width; | |
511 | for (i = 0; i < width; i++) | |
512 | ctl->buf[ctl->column++].c_mode |= UNDERLINE | ctl->mode; | |
0186ff36 | 513 | set_column(ctl, 0 < ctl->column ? ctl->column : 0); |
a2811907 SK |
514 | continue; |
515 | } | |
9a59ee65 SK |
516 | ctl->buf[ctl->column].c_char = '_'; |
517 | ctl->buf[ctl->column].c_width = 1; | |
a2811907 SK |
518 | /* fallthrough */ |
519 | case ' ': | |
9a59ee65 | 520 | set_column(ctl, ctl->column + 1); |
a2811907 SK |
521 | continue; |
522 | case '\n': | |
9a59ee65 | 523 | flush_line(ctl, tcs); |
a2811907 SK |
524 | continue; |
525 | case '\f': | |
9a59ee65 | 526 | flush_line(ctl, tcs); |
a2811907 SK |
527 | putwchar('\f'); |
528 | continue; | |
6dbe3af9 | 529 | default: |
a2811907 SK |
530 | if (!iswprint(c)) |
531 | /* non printable */ | |
532 | continue; | |
9a59ee65 SK |
533 | width = wcwidth(c); |
534 | need_column(ctl, ctl->column + width); | |
535 | if (ctl->buf[ctl->column].c_char == '\0') { | |
536 | ctl->buf[ctl->column].c_char = c; | |
537 | for (i = 0; i < width; i++) | |
538 | ctl->buf[ctl->column + i].c_mode = ctl->mode; | |
539 | ctl->buf[ctl->column].c_width = width; | |
540 | for (i = 1; i < width; i++) | |
541 | ctl->buf[ctl->column + i].c_width = -1; | |
542 | } else if (ctl->buf[ctl->column].c_char == '_') { | |
543 | ctl->buf[ctl->column].c_char = c; | |
544 | for (i = 0; i < width; i++) | |
545 | ctl->buf[ctl->column + i].c_mode |= UNDERLINE | ctl->mode; | |
546 | ctl->buf[ctl->column].c_width = width; | |
547 | for (i = 1; i < width; i++) | |
548 | ctl->buf[ctl->column + i].c_width = -1; | |
549 | } else if ((wint_t) ctl->buf[ctl->column].c_char == c) { | |
550 | for (i = 0; i < width; i++) | |
551 | ctl->buf[ctl->column + i].c_mode |= BOLD | ctl->mode; | |
a2811907 | 552 | } else { |
9a59ee65 SK |
553 | width = ctl->buf[ctl->column].c_width; |
554 | for (i = 0; i < width; i++) | |
555 | ctl->buf[ctl->column + i].c_mode = ctl->mode; | |
a2811907 | 556 | } |
9a59ee65 | 557 | set_column(ctl, ctl->column + width); |
a2811907 | 558 | continue; |
6dbe3af9 KZ |
559 | } |
560 | } | |
9a59ee65 SK |
561 | if (ctl->max_column) |
562 | flush_line(ctl, tcs); | |
6dbe3af9 | 563 | } |
5c36a0eb | 564 | |
a2811907 | 565 | int main(int argc, char **argv) |
5b627d84 | 566 | { |
9a59ee65 | 567 | int c, ret, opt_terminal = 0; |
a2811907 | 568 | char *termtype; |
49421939 | 569 | struct term_caps tcs = { 0 }; |
9a59ee65 | 570 | struct ul_ctl ctl = { .current_mode = NORMAL_CHARSET }; |
a2811907 | 571 | FILE *f; |
5c36a0eb | 572 | |
a2811907 SK |
573 | static const struct option longopts[] = { |
574 | { "terminal", required_argument, NULL, 't' }, | |
575 | { "indicated", no_argument, NULL, 'i' }, | |
576 | { "version", no_argument, NULL, 'V' }, | |
577 | { "help", no_argument, NULL, 'h' }, | |
578 | { NULL, 0, NULL, 0 } | |
579 | }; | |
5c36a0eb | 580 | |
a2811907 SK |
581 | setlocale(LC_ALL, ""); |
582 | bindtextdomain(PACKAGE, LOCALEDIR); | |
583 | textdomain(PACKAGE); | |
584 | close_stdout_atexit(); | |
66ee8158 | 585 | |
a2811907 SK |
586 | signal(SIGINT, sig_handler); |
587 | signal(SIGTERM, sig_handler); | |
66ee8158 | 588 | |
a2811907 | 589 | termtype = getenv("TERM"); |
66ee8158 | 590 | |
50646353 | 591 | while ((c = getopt_long(argc, argv, "it:T:Vh", longopts, NULL)) != -1) { |
a2811907 | 592 | switch (c) { |
4727b169 | 593 | |
a2811907 SK |
594 | case 't': |
595 | case 'T': | |
596 | /* for nroff compatibility */ | |
597 | termtype = optarg; | |
9a59ee65 | 598 | opt_terminal = 1; |
a2811907 SK |
599 | break; |
600 | case 'i': | |
9a59ee65 | 601 | ctl.indicated_opt = 1; |
a2811907 | 602 | break; |
4727b169 | 603 | |
a2811907 SK |
604 | case 'V': |
605 | print_version(EXIT_SUCCESS); | |
606 | case 'h': | |
607 | usage(); | |
608 | default: | |
609 | errtryhelp(EXIT_FAILURE); | |
610 | } | |
50646353 KZ |
611 | } |
612 | ||
a2811907 SK |
613 | setupterm(termtype, STDOUT_FILENO, &ret); |
614 | switch (ret) { | |
a2811907 SK |
615 | case 1: |
616 | break; | |
a2811907 SK |
617 | default: |
618 | warnx(_("trouble reading terminfo")); | |
619 | /* fallthrough */ | |
a2811907 | 620 | case 0: |
9a59ee65 | 621 | if (opt_terminal) |
a2811907 SK |
622 | warnx(_("terminal `%s' is not known, defaulting to `dumb'"), |
623 | termtype); | |
624 | setupterm("dumb", STDOUT_FILENO, (int *)0); | |
625 | break; | |
626 | } | |
627 | ||
9a59ee65 SK |
628 | init_term_caps(&ctl, &tcs); |
629 | init_buffer(&ctl); | |
50646353 | 630 | |
a2811907 | 631 | if (optind == argc) |
eaf68e30 | 632 | filter(&ctl, &tcs, stdin); |
50646353 | 633 | else { |
a2811907 SK |
634 | for (; optind < argc; optind++) { |
635 | f = fopen(argv[optind], "r"); | |
636 | if (!f) | |
637 | err(EXIT_FAILURE, _("cannot open %s"), argv[optind]); | |
eaf68e30 | 638 | filter(&ctl, &tcs, f); |
a2811907 SK |
639 | fclose(f); |
640 | } | |
50646353 KZ |
641 | } |
642 | ||
9a59ee65 | 643 | free(ctl.buf); |
1962d5cf | 644 | del_curterm(cur_term); |
a2811907 | 645 | return EXIT_SUCCESS; |
cdbe31fc | 646 | } |