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