]> git.ozlabs.org Git - petitboot/blob - ui/ncurses/console-codes.c
lib/log: Switch to pb_log_fn
[petitboot] / ui / ncurses / console-codes.c
1 /*
2  *  Copyright (C) 2017 IBM Corporation
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; version 2 of the License.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  */
14
15 #if defined(HAVE_CONFIG_H)
16 #include "config.h"
17 #endif
18
19 #include <ctype.h>
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdbool.h>
24
25 #include "talloc/talloc.h"
26
27 #ifndef PETITBOOT_TEST
28 #include "log/log.h"
29 #include "nc-scr.h"
30 #endif
31
32 #include "console-codes.h"
33
34 #define ESC_CHAR                        033
35 #define CSI_CHAR                        '['
36 #define INTER_CHAR_START                040
37 #define INTER_CHAR_END                  057
38 #define ESC_SEQUENCE_FINAL_START        060
39 #define ESC_SEQUENCE_FINAL_END          0176
40 #define CTRL_SEQUENCE_FINAL_START       0100
41 #define CTRL_SEQUENCE_FINAL_END         0176
42 #define DEC_PARAMETER                   077
43
44 enum console_sequence_state {
45         CONSOLE_STATE_START,
46         CONSOLE_STATE_ESC_SEQ,
47         CONSOLE_STATE_CTRL_SEQ_START,
48         CONSOLE_STATE_CTRL_SEQ,
49         CONSOLE_STATE_DONE,
50         CONSOLE_STATE_CONFUSED,
51 };
52
53 static inline bool is_intermediate(signed char c)
54 {
55         return c > INTER_CHAR_START && c < INTER_CHAR_END;
56 }
57
58 static inline bool is_parameter(signed char c)
59 {
60         return (c >= 060 && c <= 071) || c == 073;
61 }
62
63 static inline bool is_escape_final(signed char c)
64 {
65         return c >= ESC_SEQUENCE_FINAL_START && c < ESC_SEQUENCE_FINAL_END;
66 }
67
68 static inline bool is_control_final(signed char c)
69 {
70         return c >= CTRL_SEQUENCE_FINAL_START && c <= CTRL_SEQUENCE_FINAL_END;
71 }
72
73 static char console_sequence_getch(char **sequence)
74 {
75         signed char c = getch();
76
77         if (c != ERR)
78                 *sequence = talloc_asprintf_append(*sequence, "%c", c);
79         return c;
80 }
81
82 /*
83  * Catch terminal control sequences that have accidentally been sent to
84  * Petitboot. These are of the form
85  *      ESC I .. I F
86  * where I is an Intermediate Character and F is a Final Character, eg:
87  *      ESC ^ [ ? 1 ; 0 c
88  * or   ESC # 6
89  *
90  * This is based off the definitions provided by
91  * https://vt100.net/docs/vt100-ug/contents.html
92  */
93 char *handle_control_sequence(void *ctx, signed char start)
94 {
95         enum console_sequence_state state = CONSOLE_STATE_START;
96         bool in_sequence = true;
97         signed char c;
98         char *seq;
99
100         if (start != ESC_CHAR) {
101                 pb_log("%s: Called with non-escape character: 0%o\n",
102                                 __func__, start);
103                 return NULL;
104         }
105
106         seq = talloc_asprintf(ctx, "%c", start);
107
108         while (in_sequence) {
109                 switch (state) {
110                 case CONSOLE_STATE_START:
111                         c = console_sequence_getch(&seq);
112                         if (c == CSI_CHAR)
113                                 state = CONSOLE_STATE_CTRL_SEQ_START;
114                         else if (is_intermediate(c))
115                                 state = CONSOLE_STATE_ESC_SEQ;
116                         else if (is_escape_final(c))
117                                 state = CONSOLE_STATE_DONE;
118                         else if (c != ERR) {
119                                 /* wait on c == ERR */
120                                 pb_debug("Unexpected start: \\x%x\n", c);
121                                 state = CONSOLE_STATE_CONFUSED;
122                         }
123                         break;
124                 case CONSOLE_STATE_ESC_SEQ:
125                         c = console_sequence_getch(&seq);
126                         if (is_intermediate(c))
127                                 state = CONSOLE_STATE_ESC_SEQ;
128                         else if (is_escape_final(c))
129                                 state = CONSOLE_STATE_DONE;
130                         else if (c != ERR) {
131                                 /* wait on c == ERR */
132                                 pb_debug("Unexpected character after intermediate: \\x%x\n",
133                                                 c);
134                                 state = CONSOLE_STATE_CONFUSED;
135                         }
136                         break;
137                 case CONSOLE_STATE_CTRL_SEQ_START:
138                         c = console_sequence_getch(&seq);
139                         if (is_intermediate(c) || is_parameter(c) ||
140                                         c == DEC_PARAMETER)
141                                 state = CONSOLE_STATE_CTRL_SEQ;
142                         else if (is_control_final(c))
143                                 state = CONSOLE_STATE_DONE;
144                         else if (c != ERR) {
145                                 /* wait on c == ERR */
146                                 pb_debug("Unexpected character in param string:  \\x%x\n",
147                                                 c);
148                                 state = CONSOLE_STATE_CONFUSED;
149                         }
150                         break;
151                 case CONSOLE_STATE_CTRL_SEQ:
152                         c = console_sequence_getch(&seq);
153                         if (is_intermediate(c) || is_parameter(c))
154                                 state = CONSOLE_STATE_CTRL_SEQ;
155                         else if (is_control_final(c))
156                                 state = CONSOLE_STATE_DONE;
157                         else if (c != ERR) {
158                                 /* wait on c == ERR */
159                                 pb_debug("Unexpected character in param string:  \\x%x\n",
160                                                 c);
161                                 state = CONSOLE_STATE_CONFUSED;
162                         }
163                         break;
164                 case CONSOLE_STATE_DONE:
165                         in_sequence = false;
166                         break;
167                 case CONSOLE_STATE_CONFUSED:
168                         /* fall-through */
169                 default:
170                         pb_debug("We got lost interpreting a control sequence!\n");
171                         seq = talloc_asprintf_append(seq, "...");
172                         in_sequence = false;
173                         break;
174                 };
175         }
176
177         return seq;
178 }