1 /* teseq.c: Analysis of terminal controls and escape sequences. */
2
3 /*
4 Copyright (C) 2008,2010,2013 Micah Cowan
5
6 This file is part of GNU teseq.
7
8 GNU teseq is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 GNU teseq is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "teseq.h"
23
24 #include <assert.h>
25 #include <errno.h>
26 #ifdef HAVE_GETOPT_H
27 # include <getopt.h>
28 #endif
29 #include <limits.h>
30 #include <signal.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #ifdef HAVE_STRINGS_H
35 # include <strings.h>
36 #endif
37 #include <sys/stat.h>
38 #include <termios.h>
39 #include <unistd.h>
40
41 #include "inputbuf.h"
42 #include "putter.h"
43
44 /* label/description maps. */
45 #include "csi.h"
46 #include "c1.h"
47
48
49 #define CONTROL(c) ((unsigned char)((c) - 0x40) & 0x7f)
50 #define UNCONTROL(c) ((unsigned char)((c) + 0x40) & 0x7f)
51 #define C_ESC (CONTROL ('['))
52 #define C_DEL (CONTROL ('?'))
53
54 #define GET_COLUMN(c) (((c) & 0xf0) >> 4)
55 #define IS_CSI_FINAL_COLUMN(col) ((col) >= 4 && (col) <= 7)
56 #define IS_CSI_INTERMEDIATE_COLUMN(col) ((col) == 2)
57 #define IS_CSI_INTERMEDIATE_CHAR(c) \
58 IS_CSI_INTERMEDIATE_COLUMN (GET_COLUMN (c))
59 #define IS_CSI_FINAL_CHAR(c) (((c) != C_DEL) && \
60 IS_CSI_FINAL_COLUMN (GET_COLUMN (c)))
61
62 #define IS_nF_INTERMEDIATE_CHAR(c) (GET_COLUMN (c) == 2)
63 #define IS_nF_FINAL_CHAR(c) ((c) >= 0x30 && (c) < 0x7f)
64 #define IS_CONTROL(c) (GET_COLUMN (c) <= 1)
65
66 /* 0x3a (:) is not actually a private parameter, but since it's not
67 * used by any standard we're aware of, except ones that aren't used in
68 * practice, we'll consider it private for our purposes. */
69 #define IS_PRIVATE_PARAM_CHAR(c) (((c) >= 0x3c && (c) <= 0x3f) \
70 || (c) == 0x3a)
71
72 enum processor_state
73 {
74 ST_INIT,
75 ST_TEXT,
76 ST_CTRL
77 };
78
79 struct processor
80 {
81 struct inputbuf *ibuf;
82 struct putter *putr;
83 enum processor_state st;
84 int print_dot;
85 size_t mark;
86 size_t next_mark;
87
88 };
89
90 struct delay
91 {
92 double time;
93 size_t chars;
94 };
95
96 const char *control_names[] = {
97 "NUL", "SOH", "STX", "ETX",
98 "EOT", "ENQ", "ACK", "BEL",
99 "BS", "HT", "LF", "VT",
100 "FF", "CR", "SO", "SI",
101 "DLE", "DC1", "DC2", "DC3",
102 "DC4", "NAK", "SYN", "ETB",
103 "CAN", "EM", "SUB", "ESC",
104 "IS4", "IS3", "IS2", "IS1"
105 };
106
107 static const char default_color_string[] = "|>=36;7,.=31,:=33,&=35,\"=32,@=34";
108
109 struct sgr_def sgr_text, sgr_text_decor, sgr_ctrl, sgr_esc,
110 sgr_label, sgr_desc, sgr_delay;
111
112 struct config configuration = { 0 };
113 const char *program_name;
114
115 static struct termios saved_stty;
116 static struct termios working_stty;
117 static int input_term_fd = -1;
118 static int output_tty_p = 0;
119 static volatile sig_atomic_t signal_pending_p;
120 static int pending_signal;
121
122
123 #define is_normal_text(x) ((x) >= 0x20 && (x) < 0x7f)
124 #define is_ascii_digit(x) ((x) >= 0x30 && (x) <= 0x39)
125
126 /* Handle write error in putter. */
127 void
handle_write_error(int e,void * arg)128 handle_write_error (int e, void *arg)
129 {
130 const char *argv0 = arg;
131
132 fprintf (stderr, "%s: %s: %s\n", argv0, "write error", strerror (e));
133 exit (e);
134 }
135
136 /* Read a line from a typescript timings file. */
137 void
delay_read(FILE * f,struct delay * d)138 delay_read (FILE *f, struct delay *d)
139 {
140 double time;
141 unsigned int chars;
142 int converted;
143
144 converted = fscanf (f, "%lf %u", &time, &chars);
145 if (converted != 2)
146 {
147 d->time = 0.0;
148 d->chars = 0;
149 configuration.timings = NULL;
150 }
151 else
152 {
153 d->time = time;
154 d->chars = chars;
155 }
156 }
157
158 void
print_esc_char(struct processor * p,unsigned char c)159 print_esc_char (struct processor *p, unsigned char c)
160 {
161 if (c == C_ESC)
162 putter_puts (p->putr, " Esc");
163 else if (c == ' ')
164 putter_puts (p->putr, " Spc");
165 else
166 {
167 assert(c > 0x20 && c < 0x7f);
168 putter_printf (p->putr, " %c", c);
169 }
170 }
171
172 void
maybe_print_label(struct processor * p,const char * acro,const char * name)173 maybe_print_label (struct processor *p, const char *acro, const char *name)
174 {
175 if (configuration.labels)
176 putter_single_label (p->putr, "%s: %s", acro, name);
177 }
178
179 void
print_csi_label(struct processor * p,const struct csi_handler * handler,int private)180 print_csi_label (struct processor *p, const struct csi_handler *handler,
181 int private)
182 {
183 if (handler->acro)
184 {
185 const char *privmsg = "";
186 if (private)
187 privmsg = " (private params)";
188 putter_single_label (p->putr, "%s: %s%s", handler->acro, handler->label,
189 privmsg);
190 }
191 }
192
193 void
print_c1_label(struct processor * p,unsigned char c)194 print_c1_label (struct processor *p, unsigned char c)
195 {
196 unsigned char i = c - 0x40;
197 const char **label = c1_labels[i];
198 if (label[0])
199 putter_single_label (p->putr, "%s: %s", label[0], label[1]);
200 }
201
202 void
init_csi_params(const struct csi_handler * handler,size_t * n_params,unsigned int params[])203 init_csi_params (const struct csi_handler *handler, size_t *n_params,
204 unsigned int params[])
205 {
206 if (handler->fn)
207 {
208 if (*n_params == 0 && handler->default0 != CSI_DEFAULT_NONE)
209 params[(*n_params)++] = handler->default0;
210 if (*n_params == 1 && CSI_USE_DEFAULT1 (handler->type))
211 params[(*n_params)++] = handler->default1;
212 }
213 }
214
215 /* Called after read_csi_sequence has determined that we found a valid
216 control sequence. Prints the escape sequence line (if configured),
217 collects parameters, and invokes a hook to describe the control
218 function (if configured). */
219 void
process_csi_sequence(struct processor * p,const struct csi_handler * handler)220 process_csi_sequence (struct processor *p, const struct csi_handler *handler)
221 {
222 int c;
223 int e = configuration.escapes;
224 int private_params = 0;
225 int last = 0;
226 size_t n_params = 0;
227
228 size_t cur_param = 0;
229 unsigned int params[255];
230
231 if (e)
232 putter_start (p->putr, &sgr_esc, NULL, ":", "", ": ");
233
234 if (e)
235 putter_puts (p->putr, " Esc");
236 c = inputbuf_get (p->ibuf);
237 assert (c == '[');
238 if (e)
239 putter_printf (p->putr, " [", c);
240 c = inputbuf_get (p->ibuf);
241 if (!IS_CSI_FINAL_CHAR (c))
242 {
243 if (IS_PRIVATE_PARAM_CHAR (c))
244 {
245 private_params = c;
246 }
247 }
248 for (;;)
249 {
250 if (is_ascii_digit (c))
251 {
252 if (is_ascii_digit (last))
253 {
254 /* XXX: range check here. */
255 cur_param *= 10;
256 cur_param += c - '0';
257 }
258 else
259 {
260 cur_param = c - '0';
261 }
262 }
263 else
264 {
265 if (is_ascii_digit (last))
266 {
267 if (n_params < N_ARY_ELEMS (params))
268 params[n_params++] = cur_param;
269 if (e)
270 putter_printf (p->putr, " %d", cur_param);
271 }
272 else if ((last != 0 || private_params == 0)
273 && ! IS_CSI_INTERMEDIATE_CHAR (last)
274 && n_params < N_ARY_ELEMS (params))
275 {
276 int param = CSI_GET_DEFAULT (handler, n_params);
277 if (param >= 0)
278 {
279 params[n_params] = param;
280 ++n_params;
281 }
282 }
283
284 if (e)
285 print_esc_char (p, c);
286 }
287 last = c;
288 if (IS_CSI_FINAL_CHAR (c)) break;
289 c = inputbuf_get (p->ibuf);
290 }
291 if (e)
292 putter_finish (p->putr, "");
293 if (configuration.labels)
294 print_csi_label (p, handler, private_params);
295
296 if (configuration.descriptions && handler->fn)
297 {
298 int wrong_num_params = 0;
299 init_csi_params (handler, &n_params, params);
300 wrong_num_params = ((handler->type == CSI_FUNC_PN
301 || handler->type == CSI_FUNC_PS)
302 && n_params != 1);
303 wrong_num_params |= ((handler->type == CSI_FUNC_PN_PN
304 || handler->type == CSI_FUNC_PS_PS)
305 && n_params != 2);
306 if (! wrong_num_params)
307 {
308 handler->fn (c, private_params, p->putr, n_params, params);
309 }
310 }
311 }
312
313 /* Determine whether the remaining characters after an initial CSI
314 make a valid control sequence; and if so, return information about
315 the control function from the final byte. */
316 const struct csi_handler *
read_csi_sequence(struct processor * p)317 read_csi_sequence (struct processor *p)
318 {
319 enum
320 {
321 SEQ_CSI_PARAM_FIRST_CHAR,
322 SEQ_CSI_PARAMETER,
323 SEQ_CSI_INTERMEDIATE
324 } state = SEQ_CSI_PARAM_FIRST_CHAR;
325 int c, col;
326 int private_params = 0;
327 unsigned char interm = 0;
328 size_t intermsz = 0;
329
330 while (1)
331 {
332 c = inputbuf_get (p->ibuf);
333 if (c == EOF)
334 return NULL;
335 col = GET_COLUMN (c);
336 switch (state)
337 {
338 case SEQ_CSI_PARAM_FIRST_CHAR:
339 state = SEQ_CSI_PARAMETER;
340 if (IS_PRIVATE_PARAM_CHAR (c))
341 private_params = c;
342 case SEQ_CSI_PARAMETER:
343 if (IS_CSI_INTERMEDIATE_COLUMN (col))
344 {
345 state = SEQ_CSI_INTERMEDIATE;
346 }
347 else if (col == 3)
348 {
349 if (private_params == 0 && IS_PRIVATE_PARAM_CHAR (c))
350 return NULL;
351 break;
352 }
353 /* Fall through */
354 case SEQ_CSI_INTERMEDIATE:
355 if (c == C_DEL)
356 {
357 return NULL;
358 }
359 else if (IS_CSI_FINAL_COLUMN (col))
360 {
361 inputbuf_rewind (p->ibuf);
362 return get_csi_handler (private_params, intermsz, interm, c);
363 }
364 else if (! IS_CSI_INTERMEDIATE_COLUMN (col))
365 {
366 return NULL;
367 }
368 else
369 {
370 interm = c;
371 ++intermsz;
372 }
373 }
374 }
375
376 abort ();
377
378 }
379
380 /* Table of names of ISO-IR character sets.
381 See http://www.itscj.ipsj.or.jp/ISO-IR/overview.htm */
382 static const char * const iso_ir_names[] =
383 {
384 /* 000 */ NULL,
385 /* 001 */ NULL,
386 /* 002 */ "ISO_646.irv:1973",
387 /* 003 */ NULL,
388 /* 004 */ "ISO646-GB", /* = "BS_4730" */
389 /* 005 */ NULL,
390 /* 006 */ "US-ASCII", /* = "ISO646-US", "ISO_646.irv:1991",
391 "ANSI_X3.4-1968" */
392 /* 007 */ NULL,
393 /* 008 */ NULL, /* 008-1 and 008-2 handled below: "NATS-SEFI" and "NATS-SEFI-ADD" */
394 /* 009 */ NULL, /* 009-1 and 009-2 handled below: "NATS-DANO" and "NATS-DANO-ADD" */
395 /* 010 */ "ISO646-SE", /* = "ISO646-FI" = "SEN_850200_B" */
396 /* 011 */ "ISO646-SE2", /* = "SEN_850200_C" */
397 /* 012 */ NULL,
398 /* 013 */ "JIS_C6220-1969-JP",
399 /* 014 */ "ISO646-JP",/* = "JIS_C6220-1969" */
400 /* 015 */ "ISO646-IT",
401 /* 016 */ "ISO646-PT",
402 /* 017 */ "ISO646-ES",
403 /* 018 */ "GREEK7-OLD",
404 /* 019 */ "LATIN-GREEK",
405 /* 020 */ NULL,
406 /* 021 */ "ISO646-DE", /* = "DIN_66003" */
407 /* 022 */ NULL,
408 /* 023 */ NULL,
409 /* 024 */ NULL,
410 /* 025 */ "ISO646-FR1", /* = "NF_Z_62-010_1973" */
411 /* 026 */ NULL,
412 /* 027 */ "LATIN-GREEK-1",
413 /* 028 */ NULL,
414 /* 029 */ NULL,
415 /* 030 */ NULL,
416 /* 031 */ "ISO_5428:1976",
417 /* 032 */ NULL,
418 /* 033 */ NULL,
419 /* 034 */ NULL,
420 /* 035 */ NULL,
421 /* 036 */ NULL,
422 /* 037 */ "ISO_5427",
423 /* 038 */ "DIN_31624",
424 /* 039 */ "ISO_6438", /* = "DIN_31625" */
425 /* 040 */ NULL,
426 /* 041 */ NULL,
427 /* 042 */ "JIS_C6226-1978",
428 /* 043 */ NULL,
429 /* 044 */ NULL,
430 /* 045 */ NULL,
431 /* 046 */ NULL,
432 /* 047 */ "ISO-IR-47", /* an ISO646 variant */
433 /* 048 */ NULL,
434 /* 049 */ "INIS",
435 /* 050 */ "INIS-8",
436 /* 051 */ "INIS-CYRILLIC",
437 /* 052 */ NULL,
438 /* 053 */ "ISO_5426", /* = "ISO_5426:1980" */
439 /* 054 */ "ISO_5427:1981",
440 /* 055 */ "ISO_5428", /* = "ISO_5428:1980" */
441 /* 056 */ NULL,
442 /* 057 */ "ISO646-CN", /* = "GB_1988-80" */
443 /* 058 */ "GB_2312-80",
444 /* 059 */ "CODAR-U",
445 /* 060 */ "ISO646-NO", /* = "NS_4551-1" */
446 /* 061 */ "ISO646-NO2", /* = "NS_4551-2" */
447 /* 062 */ NULL,
448 /* 063 */ NULL,
449 /* 064 */ NULL,
450 /* 065 */ NULL,
451 /* 066 */ NULL,
452 /* 067 */ NULL,
453 /* 068 */ "APL",
454 /* 069 */ "ISO646-FR", /* = "NF_Z_62-010" */
455 /* 070 */ "CCITT-VIDEOTEX", /* not an official name */
456 /* 071 */ "CCITT-MOSAIC-2", /* not an official name */
457 /* 072 */ NULL,
458 /* 073 */ NULL,
459 /* 074 */ NULL,
460 /* 075 */ NULL,
461 /* 076 */ NULL,
462 /* 077 */ NULL,
463 /* 078 */ NULL,
464 /* 079 */ NULL,
465 /* 080 */ NULL,
466 /* 081 */ NULL,
467 /* 082 */ NULL,
468 /* 083 */ NULL,
469 /* 084 */ "ISO646-PT2",
470 /* 085 */ "ISO646-ES2",
471 /* 086 */ "ISO646-HU", /* = "MSZ_7795-3" */
472 /* 087 */ NULL, /* usused: "JIS_C6226-1983" = "JIS_X0208-1983" */
473 /* 088 */ "GREEK7",
474 /* 089 */ "ARABIC7", /* = "ASMO_449" */
475 /* 090 */ "ISO_6937-2", /* = "ISO_6937-2:1983" */
476 /* 091 */ "ISO646-JP-OCR-A",
477 /* 092 */ "ISO646-JP-OCR-B",
478 /* 093 */ "ISO646-JP-OCR-B-EXT", /* not an official name */
479 /* 094 */ "ISO646-JP-OCR-HAND", /* not an official name */
480 /* 095 */ "ISO646-JP-OCR-HAND-EXT", /* not an official name */
481 /* 096 */ "JIS_C6229-1984-OCR-HAND", /* not an official name */
482 /* 097 */ NULL,
483 /* 098 */ "ISO_2033",
484 /* 099 */ "ANSI_X3.110",
485 /* 100 */ "ISO-8859-1",
486 /* 101 */ "ISO-8859-2",
487 /* 102 */ "ISO646-T.61", /* not an official name */
488 /* 103 */ "T.61",
489 /* 104 */ NULL,
490 /* 105 */ NULL,
491 /* 106 */ NULL,
492 /* 107 */ NULL,
493 /* 108 */ NULL,
494 /* 109 */ "ISO-8859-3",
495 /* 110 */ "ISO-8859-4",
496 /* 111 */ "ECMA-CYRILLIC",
497 /* 112 */ NULL,
498 /* 113 */ NULL,
499 /* 114 */ NULL,
500 /* 115 */ NULL,
501 /* 116 */ NULL,
502 /* 117 */ NULL,
503 /* 118 */ NULL,
504 /* 119 */ NULL,
505 /* 120 */ NULL,
506 /* 121 */ "ISO646-CA", /* = "CSA_Z243.4-1985-1" */
507 /* 122 */ "ISO646-CA2", /* = "CSA_Z243.4-1985-2" */
508 /* 123 */ "CSA_Z243.4-1985-EXT", /* not an official name */
509 /* 124 */ NULL,
510 /* 125 */ NULL,
511 /* 126 */ "ISO-8859-7:1987", /* = "ELOT_128" = "ECMA-118" */
512 /* 127 */ "ISO-8859-6", /* = "ECMA-114" = "ASMO-708" */
513 /* 128 */ "T.101-2", /* not an official name, same as ISO-IR-99 */
514 /* 129 */ "T.101-3", /* not an official name */
515 /* 130 */ NULL,
516 /* 131 */ NULL,
517 /* 132 */ NULL,
518 /* 133 */ NULL,
519 /* 134 */ NULL,
520 /* 135 */ NULL,
521 /* 136 */ NULL,
522 /* 137 */ "CCITT-MOSAIC-1", /* not an official name */
523 /* 138 */ "ISO-8859-8:1988", /* = "ECMA-121" */
524 /* 139 */ "CSN_369103",
525 /* 140 */ NULL,
526 /* 141 */ "ISO646-YU",
527 /* 142 */ "BSI_IST-2", /* not an official name */
528 /* 143 */ "IEC_P27-1",
529 /* 144 */ "ISO-8859-5", /* = "ECMA-113:1988" */
530 /* 145 */ NULL,
531 /* 146 */ "JUS_003", /* not an official name */
532 /* 147 */ "JUS_004", /* not an official name */
533 /* 148 */ "ISO-8859-9", /* = "ECMA-128" */
534 /* 149 */ "KSC_5601", /* = "KS_C_5601-1987" */
535 /* 150 */ "GREEK-CCITT",
536 /* 151 */ "ISO646-CU", /* = "NC_99-10:81" */
537 /* 152 */ "ISO_6937-2-RESIDUAL", /* not an official name */
538 /* 153 */ "GOST_19768-74", /* = "ST_SEV_358-88" */
539 /* 154 */ "ISO-IR-154",
540 /* 155 */ "ISO_10367-BOX",
541 /* 156 */ "ISO_6937:1992",
542 /* 157 */ "ISO-8859-10",
543 /* 158 */ "ISO-IR-158",
544 /* 159 */ "JIS_X0212-1990",
545 /* 160 */ NULL,
546 /* 161 */ NULL,
547 /* 162 */ NULL,
548 /* 163 */ NULL,
549 /* 164 */ "HEBREW-CCITT", /* not an official name */
550 /* 165 */ "CHINESE-CCITT", /* not an official name */
551 /* 166 */ "TIS-620", /* = "TIS620-2533:1990" */
552 /* 167 */ "ARABIC-BULL", /* not an official name */
553 /* 168 */ "JIS_X0208-1990",
554 /* 169 */ "BLISSYMBOL", /* not an official name */
555 /* 170 */ "ISO646-INV",
556 /* 171 */ "CNS11643-1:1986",
557 /* 172 */ "CNS11643-2:1986",
558 /* 173 */ "CCITT-MOSAIC-3", /* not an official name */
559 /* 174 */ NULL,
560 /* 175 */ NULL,
561 /* 176 */ NULL,
562 /* 177 */ NULL,
563 /* 178 */ NULL,
564 /* 179 */ "ISO-8859-13",
565 /* 180 */ "TCVN5712:1993", /* = "VSCII-2" */
566 /* 181 */ "ISO-IR-181",
567 /* 182 */ "LATIN-WELSH", /* not an official name */
568 /* 183 */ "CNS11643-3:1992",
569 /* 184 */ "CNS11643-4:1992",
570 /* 185 */ "CNS11643-5:1992",
571 /* 186 */ "CNS11643-6:1992",
572 /* 187 */ "CNS11643-7:1992",
573 /* 188 */ NULL,
574 /* 189 */ NULL,
575 /* 190 */ NULL,
576 /* 191 */ NULL,
577 /* 192 */ NULL,
578 /* 193 */ NULL,
579 /* 194 */ NULL,
580 /* 195 */ NULL,
581 /* 196 */ NULL,
582 /* 197 */ "ISO-IR-197",
583 /* 198 */ "ISO-8859-8",
584 /* 199 */ "ISO-8859-14",
585 /* 200 */ "CYRILLIC-URALIC", /* not an official name */
586 /* 201 */ "CYRILLIC-VOLGAIC", /* not an official name */
587 /* 202 */ "KPS_9566-97",
588 /* 203 */ "ISO-8859-15",
589 /* 204 */ "ISO-8859-1-EURO", /* not an official name */
590 /* 205 */ "ISO-8859-4-EURO", /* not an official name */
591 /* 206 */ "ISO-8859-13-EURO", /* not an official name */
592 /* 207 */ "ISO646-IE", /* = "IS_433:1996" */
593 /* 208 */ "IS_434:1997",
594 /* 209 */ "ISO-IR-209",
595 /* 210 */ NULL,
596 /* 211 */ NULL,
597 /* 212 */ NULL,
598 /* 213 */ NULL,
599 /* 214 */ NULL,
600 /* 215 */ NULL,
601 /* 216 */ NULL,
602 /* 217 */ NULL,
603 /* 218 */ NULL,
604 /* 219 */ NULL,
605 /* 220 */ NULL,
606 /* 221 */ NULL,
607 /* 222 */ NULL,
608 /* 223 */ NULL,
609 /* 224 */ NULL,
610 /* 225 */ NULL,
611 /* 226 */ "ISO-8859-16", /* = "SR_14111:1998" */
612 /* 227 */ "ISO-8859-7", /* = "ISO-8859-7:2003" */
613 /* 228 */ "JIS_X0213-1:2000",
614 /* 229 */ "JIS_X0213-2:2000",
615 /* 230 */ "TDS-565",
616 /* 231 */ "ANSI_Z39.47",
617 /* 232 */ "TDS-616", /* = "TDS-616:2003" */
618 /* 233 */ "JIS_X0213-1:2004",
619 /* 234 */ "SI1311:2002"
620 };
621
622 /* Return the name of a character set, given its ISO-IR registry number. */
623 static const char *
iso_ir_name(float id)624 iso_ir_name (float id)
625 {
626 if (id == 8.1)
627 return "NATS-SEFI";
628 else if (id == 8.2)
629 return "NATS-SEFI-ADD";
630 else if (id == 9.1)
631 return "NATS-DANO";
632 else if (id == 9.2)
633 return "NATS-DANO-ADD";
634 else
635 {
636 int i = (int) id;
637 const char *name = NULL;
638 if (i >= 0 && i < sizeof (iso_ir_names) / sizeof (iso_ir_names[0]))
639 name = iso_ir_names[i];
640 if (name == NULL)
641 {
642 static char buf[20];
643 sprintf (buf, "ISO-IR-%d", i);
644 name = buf;
645 }
646 return name;
647 }
648 }
649
650 /* Table of code sets with 94 characters, assigned 1988-10 or before,
651 for 3-byte escape sequences. ESC 0x28..0x2B 0x40+XX.
652 See http://www.itscj.ipsj.or.jp/ISO-IR/table01.htm */
653 static float const iso_ir_table1[] =
654 {
655 2, 4, 6, 8.1, 8.2, 9.1, 9.2, 10, 11, 13, 14, 21, 16, 39, 37, 38,
656 53, 54, 25, 55, 57, 27, 47, 49, 31, 15, 17, 18, 19, 50, 51, 59,
657 60, 61, 70, 71, 173, 68, 69, 84, 85, 86, 88, 89, 90, 91, 92, 93,
658 94, 95, 96, 98, 99, 102, 103, 121, 122, 137, 141, 146, 128, 147
659 };
660
661 /* Return the name of a character set, given the final byte
662 of the 3-byte escape sequence ESC 0x28..0x2B 0x40+XX.
663 Return NULL if unknown. */
664 static const char *
iso_ir_table1_name(int f)665 iso_ir_table1_name (int f)
666 {
667 if (f >= 0x40 && f < 0x40 + sizeof (iso_ir_table1) / sizeof (iso_ir_table1[0]))
668 return iso_ir_name (iso_ir_table1[f - 0x40]);
669 else
670 return NULL;
671 }
672
673 /* Table of code sets with 94 characters, assigned 1988-11 or later,
674 for 4-byte escape sequences ESC 0x28..0x2B 0x21 0x40+XX.
675 See http://www.itscj.ipsj.or.jp/ISO-IR/table02.htm */
676 static int const iso_ir_table2[] =
677 {
678 150, 151, 170, 207, 230, 231, 232
679 };
680
681 /* Return the name of a character set, given the final byte
682 of the 4-byte escape sequence ESC 0x28..0x2B 0x21 0x40+XX.
683 Return NULL if unknown. */
684 static const char *
iso_ir_table2_name(int f)685 iso_ir_table2_name (int f)
686 {
687 if (f >= 0x40 && f < 0x40 + sizeof (iso_ir_table2) / sizeof (iso_ir_table2[0]))
688 return iso_ir_name (iso_ir_table2[f - 0x40]);
689 else
690 return NULL;
691 }
692
693 /* Table of code sets with 96 characters,
694 for 3-byte escape sequences ESC 0x2D..0x2F 0x40+XX.
695 See http://www.itscj.ipsj.or.jp/ISO-IR/table03.htm */
696 static int const iso_ir_table3[] =
697 {
698 111, 100, 101, 109, 110, 123, 126, 127, 138, 139, 142, 143, 144, 148, 152, 153,
699 154, 155, 156, 164, 166, 167, 157, -1, 158, 179, 180, 181, 182, 197, 198, 199,
700 200, 201, 203, 204, 205, 206, 226, 208, 209, 227, 234, -1, -1, -1, -1, -1,
701 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 129
702 };
703
704 /* Return the name of a character set, given the final byte
705 of the 3-byte escape sequence ESC 0x2D..0x2F 0x40+XX.
706 Return NULL if unknown. */
707 static const char *
iso_ir_table3_name(int f)708 iso_ir_table3_name (int f)
709 {
710 if (f >= 0x40 && f < 0x40 + sizeof (iso_ir_table3) / sizeof (iso_ir_table3[0]))
711 {
712 int id = iso_ir_table3[f - 0x40];
713 if (id >= 0)
714 return iso_ir_name (id);
715 }
716 return NULL;
717 }
718
719 /* Table of code sets with multi-byte characters, for escape sequences
720 ESC 0x24 [0x28] 0x40+XX (the 0x28 can be omitted only for the first three)
721 and ESC 0x24 0x29..0x2B 0x40+XX.
722 See http://www.itscj.ipsj.or.jp/ISO-IR/table04.htm */
723 static int const iso_ir_table4[] =
724 {
725 42, 58, 168, 149, 159, 165, 169, 171, 172, 183, 184, 185, 186, 187, 202, 228,
726 229, 233
727 };
728
729 /* Return the name of a character set, given the final byte
730 of the escape sequence ESC 0x24 [0x28] 0x40+XX (the 0x28 can be omitted only
731 for the first three) or ESC 0x24 0x29..0x2B 0x40+XX.
732 Return NULL if unknown. */
733 static const char *
iso_ir_table4_name(int f)734 iso_ir_table4_name (int f)
735 {
736 if (f >= 0x40 && f < 0x40 + sizeof (iso_ir_table3) / sizeof (iso_ir_table3[0]))
737 return iso_ir_name (iso_ir_table4[f - 0x40]);
738 else
739 return NULL;
740 }
741
742 /* Table of names of ISO-IR control character sets.
743 See http://www.itscj.ipsj.or.jp/ISO-IR/overview.htm */
744 static const char * const iso_ir_control_names[] =
745 {
746 /* 000 */ NULL,
747 /* 001 */ "ISO 646",
748 /* 002 */ NULL,
749 /* 003 */ NULL,
750 /* 004 */ NULL,
751 /* 005 */ NULL,
752 /* 006 */ NULL,
753 /* 007 */ "NATS",
754 /* 008 */ NULL,
755 /* 009 */ NULL,
756 /* 010 */ NULL,
757 /* 011 */ NULL,
758 /* 012 */ NULL,
759 /* 013 */ NULL,
760 /* 014 */ NULL,
761 /* 015 */ NULL,
762 /* 016 */ NULL,
763 /* 017 */ NULL,
764 /* 018 */ NULL,
765 /* 019 */ NULL,
766 /* 020 */ NULL,
767 /* 021 */ NULL,
768 /* 022 */ NULL,
769 /* 023 */ NULL,
770 /* 024 */ NULL,
771 /* 025 */ NULL,
772 /* 026 */ "ISO-IR-26",
773 /* 027 */ NULL,
774 /* 028 */ NULL,
775 /* 029 */ NULL,
776 /* 030 */ NULL,
777 /* 031 */ NULL,
778 /* 032 */ NULL,
779 /* 033 */ NULL,
780 /* 034 */ NULL,
781 /* 035 */ NULL,
782 /* 036 */ "ISO-IR-36",
783 /* 037 */ NULL,
784 /* 038 */ NULL,
785 /* 039 */ NULL,
786 /* 040 */ "DIN_31626",
787 /* 041 */ NULL,
788 /* 042 */ NULL,
789 /* 043 */ NULL,
790 /* 044 */ NULL,
791 /* 045 */ NULL,
792 /* 046 */ NULL,
793 /* 047 */ NULL,
794 /* 048 */ "INIS",
795 /* 049 */ NULL,
796 /* 050 */ NULL,
797 /* 051 */ NULL,
798 /* 052 */ NULL,
799 /* 053 */ NULL,
800 /* 054 */ NULL,
801 /* 055 */ NULL,
802 /* 056 */ "VIDEOTEX-GB", /* not an official name */
803 /* 057 */ NULL,
804 /* 058 */ NULL,
805 /* 059 */ NULL,
806 /* 060 */ NULL,
807 /* 061 */ NULL,
808 /* 062 */ NULL,
809 /* 063 */ NULL,
810 /* 064 */ NULL,
811 /* 065 */ NULL,
812 /* 066 */ NULL,
813 /* 067 */ NULL,
814 /* 068 */ NULL,
815 /* 069 */ NULL,
816 /* 070 */ NULL,
817 /* 071 */ NULL,
818 /* 072 */ NULL,
819 /* 073 */ "VIDEOTEX-CCITT", /* not an official name */
820 /* 074 */ "JIS_C6225-1979",
821 /* 075 */ NULL,
822 /* 076 */ NULL,
823 /* 077 */ "ISO_6429-1983",
824 /* 078 */ NULL,
825 /* 079 */ NULL,
826 /* 080 */ NULL,
827 /* 081 */ NULL,
828 /* 082 */ NULL,
829 /* 083 */ NULL,
830 /* 084 */ NULL,
831 /* 085 */ NULL,
832 /* 086 */ NULL,
833 /* 087 */ NULL,
834 /* 088 */ NULL,
835 /* 089 */ NULL,
836 /* 090 */ NULL,
837 /* 091 */ NULL,
838 /* 092 */ NULL,
839 /* 093 */ NULL,
840 /* 094 */ NULL,
841 /* 095 */ NULL,
842 /* 096 */ NULL,
843 /* 097 */ NULL,
844 /* 098 */ NULL,
845 /* 099 */ NULL,
846 /* 100 */ NULL,
847 /* 101 */ NULL,
848 /* 102 */ NULL,
849 /* 103 */ NULL,
850 /* 104 */ "ISO_4873",
851 /* 105 */ "ISO_4873",
852 /* 106 */ "T.61",
853 /* 107 */ "T.61",
854 /* 108 */ NULL,
855 /* 109 */ NULL,
856 /* 110 */ NULL,
857 /* 111 */ NULL,
858 /* 112 */ NULL,
859 /* 113 */ NULL,
860 /* 114 */ NULL,
861 /* 115 */ NULL,
862 /* 116 */ NULL,
863 /* 117 */ NULL,
864 /* 118 */ NULL,
865 /* 119 */ NULL,
866 /* 120 */ NULL,
867 /* 121 */ NULL,
868 /* 122 */ NULL,
869 /* 123 */ NULL,
870 /* 124 */ "ISO_6630-1985",
871 /* 125 */ NULL,
872 /* 126 */ NULL,
873 /* 127 */ NULL,
874 /* 128 */ NULL,
875 /* 129 */ NULL,
876 /* 130 */ "ASMO_662-1985", /* = "ST_SEV_358" */
877 /* 131 */ NULL,
878 /* 132 */ "T.101-1", /* not an official name */
879 /* 133 */ "T.101-1", /* not an official name */
880 /* 134 */ "T.101-2", /* not an official name */
881 /* 135 */ "T.101-3", /* not an official name */
882 /* 136 */ "T.101-3", /* not an official name */
883 /* 137 */ NULL,
884 /* 138 */ NULL,
885 /* 139 */ NULL,
886 /* 140 */ "CSN_369102"
887 };
888
889 /* Table of C0 control character sets,
890 for 3-byte escape sequences ESC 0x21 0x40+XX.
891 See http://www.itscj.ipsj.or.jp/ISO-IR/table05.htm */
892 static int const iso_ir_table5[] =
893 {
894 1, 7, 48, 26, 36, 106, 74, 104, 130, 132, 134, 135, 140
895 };
896
897 /* Return the name of a C0 control character set, given the final byte of
898 the 3-byte escape sequence ESC 0x21 0x40+XX.
899 Return NULL if unknown. */
900 static const char *
iso_ir_c0_name(int f)901 iso_ir_c0_name (int f)
902 {
903 if (f >= 0x40 && f < 0x40 + sizeof (iso_ir_table5) / sizeof (iso_ir_table5[0]))
904 {
905 int id = iso_ir_table5[f - 0x40];
906 if (id >= 0)
907 return iso_ir_control_names[id];
908 }
909 return NULL;
910 }
911
912 /* Table of C1 control character sets,
913 for 3-byte escape sequences ESC 0x22 0x40+XX.
914 See http://www.itscj.ipsj.or.jp/ISO-IR/table06.htm */
915 static int const iso_ir_table6[] =
916 {
917 56, 73, 124, 77, 133, 40, 136, 105, 107
918 };
919
920 /* Return the name of a C1 control character set, given the final byte of
921 the 3-byte escape sequence ESC 0x22 0x40+XX.
922 Return NULL if unknown. */
923 static const char *
iso_ir_c1_name(int f)924 iso_ir_c1_name (int f)
925 {
926 if (f >= 0x40 && f < 0x40 + sizeof (iso_ir_table6) / sizeof (iso_ir_table6[0]))
927 {
928 int id = iso_ir_table6[f - 0x40];
929 if (id >= 0)
930 return iso_ir_control_names[id];
931 }
932 return NULL;
933 }
934
935 /* Identify control character set invocation.
936 Escape sequence: ESC 0x21..0x22 FINAL */
937 void
print_cxd_info(struct processor * p,int intermediate,int final)938 print_cxd_info (struct processor *p, int intermediate, int final)
939 {
940 if (intermediate == 0x21)
941 {
942 maybe_print_label (p, "CZD", "C0-DESIGNATE");
943 if (configuration.descriptions)
944 {
945 const char *name = iso_ir_c0_name (final);
946 if (name != NULL)
947 putter_single_desc (p->putr, "Designate C0 Control Set of %s.",
948 name);
949 }
950 }
951 else
952 {
953 maybe_print_label (p, "C1D", "C1-DESIGNATE");
954 if (configuration.descriptions)
955 {
956 const char *name = iso_ir_c1_name (final);
957 if (name != NULL)
958 putter_single_desc (p->putr, "Designate C1 Control Set of %s.",
959 name);
960 }
961 }
962 }
963
964 /* Identify graphical character set invocation.
965 Escape sequence: ESC 0x28..2F [I1] FINAL */
966 void
print_gxd_info(struct processor * p,int intermediate,int i1,int final)967 print_gxd_info (struct processor *p, int intermediate, int i1, int final)
968 {
969 /* See ISO 2022 = ECMA 035, section 14.3.2. */
970 int designate;
971 const char *desig_strs = "Z123";
972 int set;
973
974 if (intermediate >= 0x28 && intermediate <= 0x2b)
975 {
976 set = 4;
977 designate = intermediate - 0x28;
978 }
979 else if (intermediate >= 0x2d && intermediate <= 0x2f)
980 {
981 set = 6;
982 designate = intermediate - 0x2c;
983 }
984 else
985 return;
986
987 if (configuration.labels)
988 {
989 putter_single_label (p->putr, "G%cD%d: G%d-DESIGNATE 9%d-SET",
990 desig_strs[designate], set, designate, set);
991 }
992 if (configuration.descriptions)
993 {
994 const char *designator;
995 const char *explanation;
996
997 {
998 static char buf[10];
999 char *p = buf;
1000
1001 if (i1 != 0)
1002 *p++ = i1;
1003 *p++ = final;
1004 *p = '\0';
1005 designator = buf;
1006 }
1007
1008 if (GET_COLUMN (final) == 3)
1009 explanation = " (private)";
1010 else
1011 {
1012 static char buf[100];
1013 const char *name;
1014
1015 if (set == 4)
1016 {
1017 if (i1 == 0)
1018 /* ESC 0x28..0x2B FINAL */
1019 name = iso_ir_table1_name (final);
1020 else if (i1 == 0x21)
1021 /* ESC 0x28..0x2B 0x21 FINAL */
1022 name = iso_ir_table2_name (final);
1023 else
1024 name = NULL;
1025 }
1026 else
1027 {
1028 if (i1 == 0)
1029 /* ESC 0x2D..0x2F FINAL */
1030 name = iso_ir_table3_name (final);
1031 else
1032 name = NULL;
1033 }
1034 if (name != NULL)
1035 {
1036 sprintf (buf, " (%s)", name);
1037 explanation = buf;
1038 }
1039 else
1040 explanation = "";
1041 }
1042
1043 putter_single_desc (p->putr, "Designate 9%d-character set "
1044 "%s%s to G%d.",
1045 set, designator, explanation, designate);
1046 }
1047 }
1048
1049 /* Identify multibyte graphical character set invocation.
1050 Escape sequence: ESC 0x24 [I1] FINAL */
1051 void
print_gxdm_info(struct processor * p,int i1,int final)1052 print_gxdm_info (struct processor *p, int i1, int final)
1053 {
1054 /* See ISO 2022 = ECMA 035, section 14.3.2. */
1055 int designate;
1056 const char *desig_strs = "Z123";
1057 int set;
1058
1059 if (i1 == (final == 0x40 || final == 0x41 || final == 0x42 ? 0 : 0x28))
1060 {
1061 set = 4;
1062 designate = 0;
1063 }
1064 else if (i1 >= 0x29 && i1 <= 0x2b)
1065 {
1066 set = 4;
1067 designate = i1 - 0x28;
1068 }
1069 else if (i1 >= 0x2d && i1 <= 0x2f)
1070 {
1071 set = 6;
1072 designate = i1 - 0x2c;
1073 }
1074 else
1075 return;
1076
1077 assert (designate >= 0);
1078 assert (designate < 4);
1079 if (configuration.labels)
1080 {
1081 putter_single_label (p->putr, "G%cDM%d: G%d-DESIGNATE MULTIBYTE 9%d-SET",
1082 desig_strs[designate], set, designate, set);
1083 }
1084 if (configuration.descriptions)
1085 {
1086 const char *explanation;
1087
1088 if (GET_COLUMN (final) == 3)
1089 explanation = " (private)";
1090 else
1091 {
1092 static char buf[100];
1093 const char *name;
1094
1095 name = (set == 4 ? iso_ir_table4_name (final) : NULL);
1096 if (name != NULL)
1097 {
1098 sprintf (buf, " (%s)", name);
1099 explanation = buf;
1100 }
1101 else
1102 explanation = "";
1103 }
1104
1105 putter_single_desc (p->putr, "Designate multibyte 9%d-character set "
1106 "%c%s to G%d.",
1107 set, final, explanation, designate);
1108 }
1109 }
1110
1111 /*
1112 handle_nF: Handles Ecma-35 (nF)-type escape sequences. These
1113 generally control switching of character-encoding elements, and
1114 follow the format "Esc I... F", where "I..." is one or more intermediate
1115 characters in the range 0x20-0x2f, and the final byte "F" is a
1116 character in the range 0x30-0x7e. A final byte in the range 0x30-0x3f
1117 indicates a private function (but the type of function is always indicated
1118 by the first byte to follow the Esc).
1119 */
1120 int
handle_nF(struct processor * p,unsigned char i)1121 handle_nF (struct processor *p, unsigned char i)
1122 {
1123 int i1 = 0;
1124 int f;
1125 int c;
1126
1127 /* Esc already given. */
1128 f = inputbuf_get (p->ibuf);
1129 if (IS_nF_INTERMEDIATE_CHAR (f))
1130 {
1131 i1 = f;
1132 f = inputbuf_get (p->ibuf);
1133 c = f;
1134 while (IS_nF_INTERMEDIATE_CHAR (c))
1135 c = inputbuf_get (p->ibuf);
1136 if (! IS_nF_FINAL_CHAR (c))
1137 return 0;
1138 }
1139 else if (! IS_nF_FINAL_CHAR (f))
1140 return 0;
1141
1142 if (configuration.escapes)
1143 {
1144 inputbuf_rewind (p->ibuf);
1145
1146 putter_start (p->putr, &sgr_esc, NULL, ":", "", ": ");
1147 print_esc_char (p, C_ESC);
1148 do
1149 {
1150 c = inputbuf_get (p->ibuf);
1151 print_esc_char (p, c);
1152 }
1153 while (! IS_nF_FINAL_CHAR (c));
1154
1155 putter_finish (p->putr, "");
1156 }
1157
1158 if (! IS_nF_FINAL_CHAR (f))
1159 return 1;
1160
1161 if (i == 0x20)
1162 maybe_print_label (p, "ACS", "ANNOUNCE CODE STRUCTURE");
1163 else if (i == 0x21 || i == 0x22)
1164 print_cxd_info (p, i, f);
1165 else if (i == 0x24 && (i1 == 0 || i1 >= 0x27))
1166 print_gxdm_info (p, i1, f);
1167 else if (i >= 0x28)
1168 print_gxd_info (p, i, i1, f);
1169 return 1;
1170 }
1171
1172 /*
1173 handle_c1: format is Esc Fe, where Fe is a single byte in the range
1174 0x40-0x5f. It indicates a control from the C1 set of Ecma-48 controls.
1175 In 8-bit Ecma-35-based encodings, these controls may also be specified
1176 as a single byte in the range 0x80-0x9f; but this representation is not
1177 currently supported by Teseq.
1178
1179 If CSI (Esc [) is invoked, further processing is done to determine if
1180 there is a valid control sequence.
1181 */
1182 int
handle_c1(struct processor * p,unsigned char c)1183 handle_c1 (struct processor *p, unsigned char c)
1184 {
1185 if (c == '[')
1186 {
1187 const struct csi_handler *h;
1188 if ((h = read_csi_sequence (p)))
1189 {
1190 process_csi_sequence (p, h);
1191 return 1;
1192 }
1193 else
1194 {
1195 inputbuf_rewind (p->ibuf);
1196 inputbuf_get (p->ibuf); /* Throw away '[' */
1197 }
1198 }
1199
1200 if (configuration.escapes)
1201 putter_single_esc (p->putr, "Esc %c", c);
1202 if (configuration.labels)
1203 print_c1_label (p, c);
1204 return 1;
1205 }
1206
1207 /*
1208 handle_Fp: private function escape sequence, in the format "Esc Fp",
1209 where Fp is a byte in the 0x30-0x3f range.
1210
1211 Among common uses for this sequence is the "keypad application mode",
1212 which VT100-style terminals use to change the key sequences generated by
1213 keys from the keypad; and "save/restore cursor".
1214 */
1215 int
handle_Fp(struct processor * p,unsigned char c)1216 handle_Fp (struct processor *p, unsigned char c)
1217 {
1218 if (configuration.escapes)
1219 putter_single_esc (p->putr, "Esc %c", c);
1220 switch (c)
1221 {
1222 case '7':
1223 maybe_print_label (p, "DECSC", "SAVE CURSOR");
1224 break;
1225 case '8':
1226 maybe_print_label (p, "DECRC", "RESTORE CURSOR");
1227 break;
1228 case '=':
1229 maybe_print_label (p, "DECKPAM", "KEYPAD APPLICATION MODE");
1230 break;
1231 case '>':
1232 maybe_print_label (p, "DECKPNM", "KEYPAD NORMAL MODE");
1233 break;
1234 }
1235 return 1;
1236 }
1237
1238 /*
1239 handle_Fs: Standardized single function control, in the format "Esc Fs",
1240 where Fs is a byte in the 0x60-0x7e range, and designates a control
1241 function registered with ISO.
1242 */
1243 int
handle_Fs(struct processor * p,unsigned char c)1244 handle_Fs (struct processor *p, unsigned char c)
1245 {
1246 if (configuration.escapes)
1247 putter_single_esc (p->putr, "Esc %c", c);
1248 switch (c)
1249 {
1250 case 0x60:
1251 maybe_print_label (p, "DMI", "DISABLE MANUAL INPUT");
1252 break;
1253 case 0x61:
1254 maybe_print_label (p, "INT", "INTERRUPT");
1255 break;
1256 case 0x62:
1257 maybe_print_label (p, "EMI", "END OF MEDIUM");
1258 break;
1259 case 0x63:
1260 maybe_print_label (p, "RIS", "RESET TO INITIAL STATE");
1261 break;
1262 case 0x64:
1263 maybe_print_label (p, "CMD", "CODING METHOD DELIMITER");
1264 break;
1265 case 0x6e:
1266 maybe_print_label (p, "LS2", "LOCKING-SHIFT TWO");
1267 break;
1268 case 0x6f:
1269 maybe_print_label (p, "LS3", "LOCKING-SHIFT THREE");
1270 break;
1271 case 0x7c:
1272 maybe_print_label (p, "LS3R", "LOCKING-SHIFT THREE RIGHT ");
1273 break;
1274 case 0x7d:
1275 maybe_print_label (p, "LS2R", "LOCKING-SHIFT TWO RIGHT ");
1276 break;
1277 case 0x7e:
1278 maybe_print_label (p, "LS1R", "LOCKING-SHIFT ONE RIGHT ");
1279 break;
1280 }
1281 return 1;
1282 }
1283
1284 int
handle_escape_sequence(struct processor * p)1285 handle_escape_sequence (struct processor *p)
1286 {
1287 int c;
1288 int handled = 0;
1289
1290 inputbuf_saving (p->ibuf);
1291
1292 c = inputbuf_get (p->ibuf);
1293
1294 if (c != EOF)
1295 switch (GET_COLUMN (c))
1296 {
1297 case 2:
1298 handled = handle_nF (p, c);
1299 break;
1300 case 3:
1301 handled = handle_Fp (p, c);
1302 break;
1303 case 4:
1304 case 5:
1305 handled = handle_c1 (p, c);
1306 break;
1307 case 6:
1308 case 7:
1309 if (c != C_DEL)
1310 handled = handle_Fs (p, c);
1311 break;
1312 }
1313
1314 if (handled)
1315 {
1316 inputbuf_forget (p->ibuf);
1317 p->print_dot = 1;
1318 }
1319 else
1320 inputbuf_rewind (p->ibuf);
1321
1322 return handled;
1323 }
1324
1325 int
print_control(struct processor * p,unsigned char c)1326 print_control (struct processor *p, unsigned char c)
1327 {
1328 if (p->print_dot)
1329 {
1330 p->print_dot = 0;
1331 putter_start (p->putr, &sgr_ctrl, NULL, ".", "", ".");
1332 }
1333 if (IS_CONTROL (c) || c == C_DEL)
1334 {
1335 const char *name = "DEL";
1336 if (c < 0x20)
1337 name = control_names[c];
1338 if (configuration.control_hats)
1339 putter_printf (p->putr, " %s/^%c", name, UNCONTROL (c));
1340 else
1341 putter_printf (p->putr, " %s", name);
1342 }
1343 else
1344 putter_printf (p->putr, " x%02X", (unsigned int) c);
1345 p->st = ST_CTRL;
1346 return 0;
1347 }
1348
1349 void
init_state(struct processor * p,unsigned char c)1350 init_state (struct processor *p, unsigned char c)
1351 {
1352 p->print_dot = 1;
1353 if (c != '\n' && !is_normal_text (c))
1354 {
1355 p->st = ST_CTRL;
1356 }
1357 else
1358 {
1359 putter_start (p->putr, &sgr_text, &sgr_text_decor, "|", "|-", "-|");
1360 p->st = ST_TEXT;
1361 }
1362 }
1363
1364 /* Finish the current state and return to ST_INIT. */
1365 void
finish_state(struct processor * p)1366 finish_state (struct processor *p)
1367 {
1368 switch (p->st)
1369 {
1370 case ST_TEXT:
1371 putter_finish (p->putr, "|");
1372 break;
1373 case ST_CTRL:
1374 putter_finish (p->putr, "");
1375 break;
1376 case ST_INIT:
1377 break;
1378 default:
1379 assert (!"Can't get here!");
1380 }
1381
1382 p->st = ST_INIT;
1383 }
1384
1385 void
catchsig(int s)1386 catchsig (int s)
1387 {
1388 if (!signal_pending_p)
1389 {
1390 pending_signal = s;
1391 signal_pending_p = 1;
1392 }
1393 }
1394
1395 void
handle_pending_signal(struct processor * p)1396 handle_pending_signal (struct processor *p)
1397 {
1398 struct sigaction sa;
1399 if (!signal_pending_p || inputbuf_avail (p->ibuf))
1400 return;
1401
1402 if (output_tty_p)
1403 finish_state (p);
1404
1405 if (input_term_fd != -1)
1406 tcsetattr (input_term_fd, TCSANOW, &saved_stty);
1407
1408 sigaction (pending_signal, NULL, &sa);
1409 sa.sa_handler = SIG_DFL;
1410 sigaction (pending_signal, &sa, NULL);
1411 raise (pending_signal);
1412 sa.sa_handler = catchsig;
1413 sigaction (pending_signal, &sa, NULL);
1414
1415 if (input_term_fd != -1)
1416 tcsetattr (input_term_fd, TCSANOW, &working_stty);
1417
1418 signal_pending_p = 0;
1419 }
1420
1421 void
process(struct processor * p,unsigned char c)1422 process (struct processor *p, unsigned char c)
1423 {
1424 int handled = 0;
1425 while (!handled)
1426 {
1427 switch (p->st)
1428 {
1429 case ST_INIT:
1430 /* We're not in the middle of processing
1431 any particular sort of characters. */
1432 init_state (p, c);
1433 continue;
1434 case ST_TEXT:
1435 if (c == '\n')
1436 {
1437 putter_finish (p->putr, "|.");
1438 p->st = ST_INIT;
1439 /* Handled, don't continue. */
1440 }
1441 else if (!is_normal_text (c))
1442 {
1443 finish_state (p);
1444 continue;
1445 }
1446 else
1447 {
1448 putter_putc (p->putr, c);
1449 }
1450 break;
1451 case ST_CTRL:
1452 if (is_normal_text (c))
1453 {
1454 finish_state (p);
1455 continue;
1456 }
1457 else if (c != C_ESC || !handle_escape_sequence (p))
1458 print_control (p, c);
1459 break;
1460 }
1461 handled = 1;
1462 }
1463 }
1464
1465 void
usage(int status)1466 usage (int status)
1467 {
1468 FILE *f = status == EXIT_SUCCESS ? stdout : stderr;
1469 fputs ("\
1470 Usage: teseq [-CLDEx] [in [out]]\n\
1471 or: teseq -h | --help\n\
1472 or: teseq -V | --version\n\
1473 Format text with terminal escapes and control sequences for human\n\
1474 consumption.\n", f);
1475 putc ('\n', f);
1476 fputs ("\
1477 -h, --help Display usage information (this message).\n\
1478 -V, --version Display version and warrantee.\n", f);
1479 fputs ("\
1480 -C Don't print ^X for C0 controls.\n\
1481 -D Don't print descriptions.\n\
1482 -E Don't print escape sequences.\n\
1483 -L Don't print labels.\n", f);
1484 fputs ("\
1485 --color=[WHEN], --colour=[WHEN]\n\
1486 Colorize the output. WHEN defaults to 'always'\n\
1487 or can be 'never' or 'auto'. See the full documentation.\n\
1488 -I, --no-interactive\n\
1489 Don't put the terminal into non-canonical or no-echo\n\
1490 mode, and don't try to ensure output lines are finished\n\
1491 when a signal is received.\n\
1492 -b, --buffered Force teseq to buffer I/O.\n\
1493 -t, --timings=TIMINGS\n\
1494 Read timing info from TIMINGS and emit delay lines.\n\
1495 -x (No effect; accepted for backwards compatibility.)\n", f);
1496 putc ('\n', f);
1497 fputs ("\
1498 The GNU Teseq home page is at http://www.gnu.org/software/teseq/.\n\
1499 Report all bugs to " PACKAGE_BUGREPORT "\n\
1500 ", f);
1501 exit (status);
1502 }
1503
1504 void
version(void)1505 version (void)
1506 {
1507 puts (PACKAGE_STRING);
1508 puts ("\
1509 Copyright (C) 2008,2013 Micah Cowan <micah@addictivecode.org>.\n\
1510 License GPLv3+: GNU GPL version 3 or later \
1511 <http://gnu.org/licenses/gpl.html>\n\
1512 This is free software: you are free to change and redistribute it.\n\
1513 There is NO WARRANTY, to the extent permitted by law.\
1514 ");
1515 exit (EXIT_SUCCESS);
1516 }
1517
1518 FILE *
must_fopen(const char * fname,const char * mode,int dash)1519 must_fopen (const char *fname, const char *mode, int dash)
1520 {
1521 FILE *f;
1522 if (dash && fname[0] == '-' && fname[1] == '\0')
1523 {
1524 if (strchr (mode, 'w'))
1525 return stdout;
1526 else
1527 return stdin;
1528 }
1529 f = fopen (fname, mode);
1530 if (f)
1531 return f;
1532 fprintf (stderr, "%s: couldn't open file %s: %s\n", program_name,
1533 fname, strerror (errno));
1534 exit (EXIT_FAILURE);
1535 }
1536
1537 void
tty_setup(int fd)1538 tty_setup (int fd)
1539 {
1540 struct termios ti;
1541 int intr;
1542 char *ctrl_name = NULL;
1543
1544 if (tcgetattr (fd, &ti) != 0)
1545 return;
1546 saved_stty = ti;
1547 intr = ti.c_cc[VINTR];
1548 ti.c_lflag &= ~ICANON;
1549 if (output_tty_p)
1550 ti.c_lflag &= ~ECHO;
1551 working_stty = ti;
1552 input_term_fd = fd;
1553 tcsetattr (fd, TCSANOW, &ti);
1554
1555 /* Notify the user that they're in non-canonical mode. */
1556
1557 fprintf (stderr,
1558 " Terminal detected. Interactive mode (-I option to disable).\n"
1559 " Send the interrupt character to exit.");
1560
1561 if (IS_CONTROL(intr))
1562 {
1563 fprintf (stderr, " (Control-%c)",
1564 intr + '@'); /* <--- Example: '\003' -> (Control-C) */
1565 }
1566 else if (intr == C_DEL)
1567 {
1568 fprintf (stderr, " (DEL, or Control-?)");
1569 }
1570
1571 fputs ("\n\n", stderr);
1572 }
1573
1574 void
signal_setup(void)1575 signal_setup (void)
1576 {
1577 static const int sigs[] =
1578 {
1579 SIGINT,
1580 SIGTERM,
1581 SIGTSTP,
1582 SIGTTIN,
1583 SIGTTOU
1584 };
1585 const int *sig, *sige = sigs + N_ARY_ELEMS (sigs);
1586 struct sigaction sa;
1587 sigset_t mask;
1588
1589 sigemptyset (&mask);
1590 for (sig = sigs; sig != sige; ++sig)
1591 sigaddset (&mask, *sig);
1592
1593 sa.sa_handler = catchsig;
1594 sa.sa_mask = mask;
1595 sa.sa_flags = 0;
1596
1597 for (sig = sigs; sig != sige; ++sig)
1598 sigaction (*sig, &sa, NULL);
1599 }
1600
1601 void
parse_colors(const char * color_string)1602 parse_colors (const char *color_string)
1603 {
1604 const char *p, *s, *e;
1605 struct sgr_def *set_me;
1606
1607 for (p = color_string; p[0] != '\0'; )
1608 {
1609 set_me = NULL;
1610 switch (p[0])
1611 {
1612 case '|':
1613 if (p[1] == '>')
1614 {
1615 set_me = &sgr_text;
1616 ++p;
1617 }
1618 else
1619 set_me = &sgr_text_decor;
1620 break;
1621 case '.': set_me = &sgr_ctrl; break;
1622 case ':': set_me = &sgr_esc; break;
1623 case '&': set_me = &sgr_label; break;
1624 case '"': set_me = &sgr_desc; break;
1625 case '@': set_me = &sgr_delay; break;
1626 default:
1627 ; /* Won't set anything, just skip to next one. */
1628 }
1629 if (p[1] != '=')
1630 {
1631 /* Invalid definition, skip to next one. */
1632 set_me = NULL;
1633 }
1634 if (p[1] == '\0')
1635 break;
1636 for (s = e = &p[2]; e[0] != '\0' && e[0] != ','; ++e)
1637 {
1638 if (! (e[0] >= 0x30 && e[0] < 0x40))
1639 {
1640 /* Parameters can't fall outside this range. Skip
1641 * assignment. */
1642 set_me = NULL;
1643 }
1644 }
1645 if ((set_me != NULL) && ((e - s) <= UINT_MAX))
1646 {
1647 set_me->sgr = s;
1648 set_me->len = e - s;
1649 }
1650 if (e[0] == '\0')
1651 p = e;
1652 else
1653 p = e + 1; /* Skip comma. */
1654 }
1655 }
1656
1657 void
color_setup(void)1658 color_setup (void)
1659 {
1660 const char *envstr = getenv("TESEQ_COLORS");
1661
1662 if (configuration.color != CFG_COLOR_ALWAYS)
1663 return;
1664
1665 parse_colors (default_color_string);
1666 if (envstr)
1667 parse_colors (envstr);
1668 }
1669
1670 #ifdef HAVE_GETOPT_H
1671 struct option teseq_opts[] = {
1672 { "help", 0, NULL, 'h' },
1673 { "version", 0, NULL, 'V' },
1674 { "timings", 1, NULL, 't' },
1675 { "buffered", 0, NULL, 'b' },
1676 { "no-interactive", 0, NULL, 'I' },
1677 { "color", 2, &configuration.color, CFG_COLOR_SET },
1678 { "colour", 2, &configuration.color, CFG_COLOR_SET },
1679 { 0 }
1680 };
1681 #endif
1682
1683 void
configure(struct processor * p,int argc,char ** argv)1684 configure (struct processor *p, int argc, char **argv)
1685 {
1686 int opt, which;
1687 const char *timings_fname = NULL;
1688 FILE *inf = stdin;
1689 FILE *outf = stdout;
1690 int infd;
1691
1692 configuration.control_hats = 1;
1693 configuration.descriptions = 1;
1694 configuration.labels = 1;
1695 configuration.escapes = 1;
1696 configuration.buffered = 0;
1697 configuration.handle_signals = 1;
1698 configuration.timings = NULL;
1699 configuration.color = CFG_COLOR_NONE;
1700
1701 program_name = argv[0];
1702
1703 while ((opt = (
1704 #define ACCEPTOPTS ":hVo:C^&D\"LEt:xbI"
1705 #ifdef HAVE_GETOPT_H
1706 getopt_long (argc, argv, ACCEPTOPTS,
1707 teseq_opts, &which)
1708 #else
1709 getopt (argc, argv, ACCEPTOPTS)
1710 #endif
1711 ))
1712 != -1)
1713 {
1714 switch (opt)
1715 {
1716 case 'h':
1717 usage (EXIT_SUCCESS);
1718 break;
1719 case 'V':
1720 version ();
1721 break;
1722 case '^':
1723 case 'C':
1724 configuration.control_hats = 0;
1725 break;
1726 case '"':
1727 case 'D':
1728 configuration.descriptions = 0;
1729 break;
1730 case '&':
1731 case 'L':
1732 configuration.labels = 0;
1733 break;
1734 case 'E':
1735 configuration.escapes = 0;
1736 break;
1737 case 'I':
1738 configuration.handle_signals = 0;
1739 break;
1740 case 'b':
1741 configuration.buffered = 1;
1742 break;
1743 case 't':
1744 timings_fname = optarg;
1745 break;
1746 case 'x':
1747 /* Used to control whether we print descriptions of
1748 * non-ANSI-defined sequences. This option is always on now. */
1749 break;
1750 case ':':
1751 fprintf (stderr, "Option -%c requires an argument.\n\n", optopt);
1752 usage (EXIT_FAILURE);
1753 break;
1754 case 0:
1755 /* Long-only option. */
1756 if (configuration.color == CFG_COLOR_SET)
1757 {
1758 if (!optarg || !strcasecmp(optarg, "always"))
1759 configuration.color = CFG_COLOR_ALWAYS;
1760 else if (!strcasecmp(optarg, "none"))
1761 configuration.color = CFG_COLOR_NONE;
1762 else if (!strcasecmp(optarg, "auto"))
1763 configuration.color = CFG_COLOR_AUTO;
1764 else
1765 {
1766 fprintf (stderr,
1767 "Option --color: Unknown argument ``%s''.\n\n",
1768 optarg);
1769 usage (EXIT_FAILURE);
1770 }
1771 }
1772 break;
1773 default:
1774 if (optopt == ':')
1775 {
1776 configuration.escapes = 0;
1777 break;
1778 }
1779 fprintf (stderr, "Unrecognized option -%c.\n\n", optopt);
1780 usage (EXIT_FAILURE);
1781 break;
1782 }
1783 }
1784 if (argv[optind] != NULL)
1785 {
1786 inf = must_fopen (argv[optind++], "r", 1);
1787 }
1788 if (argv[optind] != NULL)
1789 {
1790 outf = must_fopen (argv[optind++], "w", 1);
1791 }
1792 if (timings_fname != NULL)
1793 {
1794 configuration.timings = must_fopen (timings_fname, "r", 0);
1795 }
1796
1797 /* Set input/output to unbuffered. */
1798 infd = fileno (inf);
1799 if (!configuration.buffered)
1800 {
1801 /* Don't unbuffer if input's a plain file. */
1802 struct stat s;
1803 int r = fstat (infd, &s);
1804
1805 if (r == -1 || !S_ISREG (s.st_mode))
1806 {
1807 setvbuf (inf, NULL, _IONBF, 0);
1808 setvbuf (outf, NULL, _IONBF, 0);
1809 }
1810 }
1811
1812 output_tty_p = isatty (fileno (outf));
1813
1814 if (configuration.color != CFG_COLOR_AUTO)
1815 ; /* Nothing to do. */
1816 else if (output_tty_p)
1817 configuration.color = CFG_COLOR_ALWAYS;
1818 else
1819 configuration.color = CFG_COLOR_NONE;
1820
1821 color_setup ();
1822
1823 if (configuration.handle_signals)
1824 {
1825 if (isatty (infd))
1826 tty_setup (infd);
1827 signal_setup ();
1828 }
1829
1830 p->ibuf = inputbuf_new (inf, 1024);
1831 p->putr = putter_new (outf);
1832 if (!p->ibuf || !p->putr)
1833 {
1834 fprintf (stderr, "%s: Out of memory.\n", program_name);
1835 exit (EXIT_FAILURE);
1836 }
1837 putter_set_handler (p->putr, handle_write_error, (void *)program_name);
1838 }
1839
1840 void
emit_delay(struct processor * p)1841 emit_delay (struct processor *p)
1842 {
1843 static int first = 1;
1844 size_t count = inputbuf_get_count (p->ibuf);
1845 finish_state (p);
1846 do
1847 {
1848 /* Why the "next mark"? ...script issues the amount of delay
1849 that has occurred *before* a read has been attempted, thus
1850 scriptreplay.pl Actually reads and executes two delays before
1851 printing the first byte, to keep the remaining delays
1852 properly synched. So, we throw out the first delay (but
1853 remember the number-of-bytes field), and execute the second,
1854 before _we_ process the byte. */
1855 struct delay d;
1856 delay_read (configuration.timings, &d);
1857 p->mark += p->next_mark;
1858 p->next_mark = d.chars;
1859 if (first)
1860 first = 0;
1861 else
1862 putter_single_delay (p->putr, "%f", d.time);
1863 }
1864 while (configuration.timings && p->mark <= count);
1865
1866 /* Following couple lines aren't strictly necessary,
1867 but keep the count/mark from getting huge, and avoid the
1868 unlikely potential for overflow. */
1869 p->mark -= count;
1870 inputbuf_reset_count (p->ibuf);
1871 }
1872
1873 #define SHOULD_EMIT_DELAY(p) (configuration.timings && \
1874 (p)->mark <= inputbuf_get_count ((p)->ibuf))
1875
1876 int
main(int argc,char ** argv)1877 main (int argc, char **argv)
1878 {
1879 int c;
1880 int err;
1881 struct processor p = { 0, 0, ST_INIT };
1882
1883 configure (&p, argc, argv);
1884 /* If we're in timings mode, we need to handle up to the first
1885 newline without checking the delay, because that's the timestamp
1886 line from script, and the delays don't start until after that. */
1887 if (configuration.timings)
1888 {
1889 while ((c = inputbuf_get (p.ibuf)) != EOF)
1890 {
1891 process (&p, c);
1892 if (c == '\n') break;
1893 }
1894 inputbuf_reset_count (p.ibuf);
1895 }
1896 for (;;)
1897 {
1898 if (SHOULD_EMIT_DELAY (&p))
1899 emit_delay (&p);
1900 handle_pending_signal (&p);
1901 c = inputbuf_get (p.ibuf);
1902 if (c == EOF)
1903 {
1904 if (signal_pending_p)
1905 continue;
1906 else
1907 break;
1908 }
1909 else
1910 {
1911 process (&p, c);
1912 }
1913 }
1914 finish_state (&p);
1915 if ((err = inputbuf_io_error (p.ibuf)) != 0)
1916 fprintf (stderr, "%s: %s: %s\n", program_name, "read error", strerror (err));
1917 return EXIT_SUCCESS;
1918 }
1919