Fossil SCM

Add linenoise-win32.c so that linenoise support can be used on mingw32 builds. Update linenoise.c from upstream while we're at it.

stephan 2025-02-24 07:44 trunk
Commit 6782c579af31ad359a77e896cd1f10c09f9ae52646207fde7c8b92982dc72d9c
--- a/extsrc/linenoise-win32.c
+++ b/extsrc/linenoise-win32.c
@@ -0,0 +1,379 @@
1
+
2
+/* this code is not standalone
3
+ * it is included into linenoise.c
4
+ * for windows.
5
+ * It is deliberately kept separate so that
6
+ * applications that have no need for windows
7
+ * support can omit this
8
+ */
9
+static DWORD orig_consolemode = 0;
10
+
11
+static int flushOutput(struct current *current);
12
+static void outputNewline(struct current *current);
13
+
14
+static void refreshStart(struct current *current)
15
+{
16
+ (void)current;
17
+}
18
+
19
+static void refreshEnd(struct current *current)
20
+{
21
+ (void)current;
22
+}
23
+
24
+static void refreshStartChars(struct current *current)
25
+{
26
+ assert(current->output == NULL);
27
+ /* We accumulate all output here */
28
+ current->output = sb_alloc();
29
+#ifdef USE_UTF8
30
+ current->ubuflen = 0;
31
+#endif
32
+}
33
+
34
+static void refreshNewline(struct current *current)
35
+{
36
+ DRL("<nl>");
37
+ outputNewline(current);
38
+}
39
+
40
+static void refreshEndChars(struct current *current)
41
+{
42
+ assert(current->output);
43
+ flushOutput(current);
44
+ sb_free(current->output);
45
+ current->output = NULL;
46
+}
47
+
48
+static int enableRawMode(struct current *current) {
49
+ DWORD n;
50
+ INPUT_RECORD irec;
51
+
52
+ current->outh = GetStdHandle(STD_OUTPUT_HANDLE);
53
+ current->inh = GetStdHandle(STD_INPUT_HANDLE);
54
+
55
+ if (!PeekConsoleInput(current->inh, &irec, 1, &n)) {
56
+ return -1;
57
+ }
58
+ if (getWindowSize(current) != 0) {
59
+ return -1;
60
+ }
61
+ if (GetConsoleMode(current->inh, &orig_consolemode)) {
62
+ SetConsoleMode(current->inh, ENABLE_PROCESSED_INPUT);
63
+ }
64
+#ifdef USE_UTF8
65
+ /* XXX is this the right thing to do? */
66
+ SetConsoleCP(65001);
67
+#endif
68
+ return 0;
69
+}
70
+
71
+static void disableRawMode(struct current *current)
72
+{
73
+ SetConsoleMode(current->inh, orig_consolemode);
74
+}
75
+
76
+void linenoiseClearScreen(void)
77
+{
78
+ /* XXX: This is ugly. Should just have the caller pass a handle */
79
+ struct current current;
80
+
81
+ current.outh = GetStdHandle(STD_OUTPUT_HANDLE);
82
+
83
+ if (getWindowSize(&current) == 0) {
84
+ COORD topleft = { 0, 0 };
85
+ DWORD n;
86
+
87
+ FillConsoleOutputCharacter(current.outh, ' ',
88
+ current.cols * current.rows, topleft, &n);
89
+ FillConsoleOutputAttribute(current.outh,
90
+ FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN,
91
+ current.cols * current.rows, topleft, &n);
92
+ SetConsoleCursorPosition(current.outh, topleft);
93
+ }
94
+}
95
+
96
+static void cursorToLeft(struct current *current)
97
+{
98
+ COORD pos;
99
+ DWORD n;
100
+
101
+ pos.X = 0;
102
+ pos.Y = (SHORT)current->y;
103
+
104
+ FillConsoleOutputAttribute(current->outh,
105
+ FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, current->cols, pos, &n);
106
+ current->x = 0;
107
+}
108
+
109
+#ifdef USE_UTF8
110
+static void flush_ubuf(struct current *current)
111
+{
112
+ COORD pos;
113
+ DWORD nwritten;
114
+ pos.Y = (SHORT)current->y;
115
+ pos.X = (SHORT)current->x;
116
+ SetConsoleCursorPosition(current->outh, pos);
117
+ WriteConsoleW(current->outh, current->ubuf, current->ubuflen, &nwritten, 0);
118
+ current->x += current->ubufcols;
119
+ current->ubuflen = 0;
120
+ current->ubufcols = 0;
121
+}
122
+
123
+static void add_ubuf(struct current *current, int ch)
124
+{
125
+ /* This code originally by: Author: Mark E. Davis, 1994. */
126
+ static const int halfShift = 10; /* used for shifting by 10 bits */
127
+
128
+ static const DWORD halfBase = 0x0010000UL;
129
+ static const DWORD halfMask = 0x3FFUL;
130
+
131
+ #define UNI_SUR_HIGH_START 0xD800
132
+ #define UNI_SUR_HIGH_END 0xDBFF
133
+ #define UNI_SUR_LOW_START 0xDC00
134
+ #define UNI_SUR_LOW_END 0xDFFF
135
+
136
+ #define UNI_MAX_BMP 0x0000FFFF
137
+
138
+ if (ch > UNI_MAX_BMP) {
139
+ /* convert from unicode to utf16 surrogate pairs
140
+ * There is always space for one extra word in ubuf
141
+ */
142
+ ch -= halfBase;
143
+ current->ubuf[current->ubuflen++] = (WORD)((ch >> halfShift) + UNI_SUR_HIGH_START);
144
+ current->ubuf[current->ubuflen++] = (WORD)((ch & halfMask) + UNI_SUR_LOW_START);
145
+ }
146
+ else {
147
+ current->ubuf[current->ubuflen++] = ch;
148
+ }
149
+ current->ubufcols += utf8_width(ch);
150
+ if (current->ubuflen >= UBUF_MAX_CHARS) {
151
+ flush_ubuf(current);
152
+ }
153
+}
154
+#endif
155
+
156
+static int flushOutput(struct current *current)
157
+{
158
+ const char *pt = sb_str(current->output);
159
+ int len = sb_len(current->output);
160
+
161
+#ifdef USE_UTF8
162
+ /* convert utf8 in current->output into utf16 in current->ubuf
163
+ */
164
+ while (len) {
165
+ int ch;
166
+ int n = utf8_tounicode(pt, &ch);
167
+
168
+ pt += n;
169
+ len -= n;
170
+
171
+ add_ubuf(current, ch);
172
+ }
173
+ flush_ubuf(current);
174
+#else
175
+ DWORD nwritten;
176
+ COORD pos;
177
+
178
+ pos.Y = (SHORT)current->y;
179
+ pos.X = (SHORT)current->x;
180
+
181
+ SetConsoleCursorPosition(current->outh, pos);
182
+ WriteConsoleA(current->outh, pt, len, &nwritten, 0);
183
+
184
+ current->x += len;
185
+#endif
186
+
187
+ sb_clear(current->output);
188
+
189
+ return 0;
190
+}
191
+
192
+static int outputChars(struct current *current, const char *buf, int len)
193
+{
194
+ if (len < 0) {
195
+ len = strlen(buf);
196
+ }
197
+ assert(current->output);
198
+
199
+ sb_append_len(current->output, buf, len);
200
+
201
+ return 0;
202
+}
203
+
204
+static void outputNewline(struct current *current)
205
+{
206
+ /* On the last row output a newline to force a scroll */
207
+ if (current->y + 1 == current->rows) {
208
+ outputChars(current, "\n", 1);
209
+ }
210
+ flushOutput(current);
211
+ current->x = 0;
212
+ current->y++;
213
+}
214
+
215
+static void setOutputHighlight(struct current *current, const int *props, int nprops)
216
+{
217
+ int colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
218
+ int bold = 0;
219
+ int reverse = 0;
220
+ int i;
221
+
222
+ for (i = 0; i < nprops; i++) {
223
+ switch (props[i]) {
224
+ case 0:
225
+ colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
226
+ bold = 0;
227
+ reverse = 0;
228
+ break;
229
+ case 1:
230
+ bold = FOREGROUND_INTENSITY;
231
+ break;
232
+ case 7:
233
+ reverse = 1;
234
+ break;
235
+ case 30:
236
+ colour = 0;
237
+ break;
238
+ case 31:
239
+ colour = FOREGROUND_RED;
240
+ break;
241
+ case 32:
242
+ colour = FOREGROUND_GREEN;
243
+ break;
244
+ case 33:
245
+ colour = FOREGROUND_RED | FOREGROUND_GREEN;
246
+ break;
247
+ case 34:
248
+ colour = FOREGROUND_BLUE;
249
+ break;
250
+ case 35:
251
+ colour = FOREGROUND_RED | FOREGROUND_BLUE;
252
+ break;
253
+ case 36:
254
+ colour = FOREGROUND_BLUE | FOREGROUND_GREEN;
255
+ break;
256
+ case 37:
257
+ colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
258
+ break;
259
+ }
260
+ }
261
+
262
+ flushOutput(current);
263
+
264
+ if (reverse) {
265
+ SetConsoleTextAttribute(current->outh, BACKGROUND_INTENSITY);
266
+ }
267
+ else {
268
+ SetConsoleTextAttribute(current->outh, colour | bold);
269
+ }
270
+}
271
+
272
+static void eraseEol(struct current *current)
273
+{
274
+ COORD pos;
275
+ DWORD n;
276
+
277
+ pos.X = (SHORT) current->x;
278
+ pos.Y = (SHORT) current->y;
279
+
280
+ FillConsoleOutputCharacter(current->outh, ' ', current->cols - current->x, pos, &n);
281
+}
282
+
283
+static void setCursorXY(struct current *current)
284
+{
285
+ COORD pos;
286
+
287
+ pos.X = (SHORT) current->x;
288
+ pos.Y = (SHORT) current->y;
289
+
290
+ SetConsoleCursorPosition(current->outh, pos);
291
+}
292
+
293
+
294
+static void setCursorPos(struct current *current, int x)
295
+{
296
+ current->x = x;
297
+ setCursorXY(current);
298
+}
299
+
300
+static void cursorUp(struct current *current, int n)
301
+{
302
+ current->y -= n;
303
+ setCursorXY(current);
304
+}
305
+
306
+static void cursorDown(struct current *current, int n)
307
+{
308
+ current->y += n;
309
+ setCursorXY(current);
310
+}
311
+
312
+static int fd_read(struct current *current)
313
+{
314
+ while (1) {
315
+ INPUT_RECORD irec;
316
+ DWORD n;
317
+ if (WaitForSingleObject(current->inh, INFINITE) != WAIT_OBJECT_0) {
318
+ break;
319
+ }
320
+ if (!ReadConsoleInputW(current->inh, &irec, 1, &n)) {
321
+ break;
322
+ }
323
+ if (irec.EventType == KEY_EVENT) {
324
+ KEY_EVENT_RECORD *k = &irec.Event.KeyEvent;
325
+ if (k->bKeyDown || k->wVirtualKeyCode == VK_MENU) {
326
+ if (k->dwControlKeyState & ENHANCED_KEY) {
327
+ switch (k->wVirtualKeyCode) {
328
+ case VK_LEFT:
329
+ return SPECIAL_LEFT;
330
+ case VK_RIGHT:
331
+ return SPECIAL_RIGHT;
332
+ case VK_UP:
333
+ return SPECIAL_UP;
334
+ case VK_DOWN:
335
+ return SPECIAL_DOWN;
336
+ case VK_INSERT:
337
+ return SPECIAL_INSERT;
338
+ case VK_DELETE:
339
+ return SPECIAL_DELETE;
340
+ case VK_HOME:
341
+ return SPECIAL_HOME;
342
+ case VK_END:
343
+ return SPECIAL_END;
344
+ case VK_PRIOR:
345
+ return SPECIAL_PAGE_UP;
346
+ case VK_NEXT:
347
+ return SPECIAL_PAGE_DOWN;
348
+ case VK_RETURN:
349
+ return k->uChar.UnicodeChar;
350
+ }
351
+ }
352
+ /* Note that control characters are already translated in AsciiChar */
353
+ else if (k->wVirtualKeyCode == VK_CONTROL)
354
+ continue;
355
+ else {
356
+ return k->uChar.UnicodeChar;
357
+ }
358
+ }
359
+ }
360
+ }
361
+ return -1;
362
+}
363
+
364
+static int getWindowSize(struct current *current)
365
+{
366
+ CONSOLE_SCREEN_BUFFER_INFO info;
367
+ if (!GetConsoleScreenBufferInfo(current->outh, &info)) {
368
+ return -1;
369
+ }
370
+ current->cols = info.dwSize.X;
371
+ current->rows = info.dwSize.Y;
372
+ if (current->cols <= 0 || current->rows <= 0) {
373
+ current->cols = 80;
374
+ return -1;
375
+ }
376
+ current->y = info.dwCursorPosition.Y;
377
+ current->x = info.dwCursorPosition.X;
378
+ return 0;
379
+}
--- a/extsrc/linenoise-win32.c
+++ b/extsrc/linenoise-win32.c
@@ -0,0 +1,379 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/extsrc/linenoise-win32.c
+++ b/extsrc/linenoise-win32.c
@@ -0,0 +1,379 @@
1
2 /* this code is not standalone
3 * it is included into linenoise.c
4 * for windows.
5 * It is deliberately kept separate so that
6 * applications that have no need for windows
7 * support can omit this
8 */
9 static DWORD orig_consolemode = 0;
10
11 static int flushOutput(struct current *current);
12 static void outputNewline(struct current *current);
13
14 static void refreshStart(struct current *current)
15 {
16 (void)current;
17 }
18
19 static void refreshEnd(struct current *current)
20 {
21 (void)current;
22 }
23
24 static void refreshStartChars(struct current *current)
25 {
26 assert(current->output == NULL);
27 /* We accumulate all output here */
28 current->output = sb_alloc();
29 #ifdef USE_UTF8
30 current->ubuflen = 0;
31 #endif
32 }
33
34 static void refreshNewline(struct current *current)
35 {
36 DRL("<nl>");
37 outputNewline(current);
38 }
39
40 static void refreshEndChars(struct current *current)
41 {
42 assert(current->output);
43 flushOutput(current);
44 sb_free(current->output);
45 current->output = NULL;
46 }
47
48 static int enableRawMode(struct current *current) {
49 DWORD n;
50 INPUT_RECORD irec;
51
52 current->outh = GetStdHandle(STD_OUTPUT_HANDLE);
53 current->inh = GetStdHandle(STD_INPUT_HANDLE);
54
55 if (!PeekConsoleInput(current->inh, &irec, 1, &n)) {
56 return -1;
57 }
58 if (getWindowSize(current) != 0) {
59 return -1;
60 }
61 if (GetConsoleMode(current->inh, &orig_consolemode)) {
62 SetConsoleMode(current->inh, ENABLE_PROCESSED_INPUT);
63 }
64 #ifdef USE_UTF8
65 /* XXX is this the right thing to do? */
66 SetConsoleCP(65001);
67 #endif
68 return 0;
69 }
70
71 static void disableRawMode(struct current *current)
72 {
73 SetConsoleMode(current->inh, orig_consolemode);
74 }
75
76 void linenoiseClearScreen(void)
77 {
78 /* XXX: This is ugly. Should just have the caller pass a handle */
79 struct current current;
80
81 current.outh = GetStdHandle(STD_OUTPUT_HANDLE);
82
83 if (getWindowSize(&current) == 0) {
84 COORD topleft = { 0, 0 };
85 DWORD n;
86
87 FillConsoleOutputCharacter(current.outh, ' ',
88 current.cols * current.rows, topleft, &n);
89 FillConsoleOutputAttribute(current.outh,
90 FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN,
91 current.cols * current.rows, topleft, &n);
92 SetConsoleCursorPosition(current.outh, topleft);
93 }
94 }
95
96 static void cursorToLeft(struct current *current)
97 {
98 COORD pos;
99 DWORD n;
100
101 pos.X = 0;
102 pos.Y = (SHORT)current->y;
103
104 FillConsoleOutputAttribute(current->outh,
105 FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, current->cols, pos, &n);
106 current->x = 0;
107 }
108
109 #ifdef USE_UTF8
110 static void flush_ubuf(struct current *current)
111 {
112 COORD pos;
113 DWORD nwritten;
114 pos.Y = (SHORT)current->y;
115 pos.X = (SHORT)current->x;
116 SetConsoleCursorPosition(current->outh, pos);
117 WriteConsoleW(current->outh, current->ubuf, current->ubuflen, &nwritten, 0);
118 current->x += current->ubufcols;
119 current->ubuflen = 0;
120 current->ubufcols = 0;
121 }
122
123 static void add_ubuf(struct current *current, int ch)
124 {
125 /* This code originally by: Author: Mark E. Davis, 1994. */
126 static const int halfShift = 10; /* used for shifting by 10 bits */
127
128 static const DWORD halfBase = 0x0010000UL;
129 static const DWORD halfMask = 0x3FFUL;
130
131 #define UNI_SUR_HIGH_START 0xD800
132 #define UNI_SUR_HIGH_END 0xDBFF
133 #define UNI_SUR_LOW_START 0xDC00
134 #define UNI_SUR_LOW_END 0xDFFF
135
136 #define UNI_MAX_BMP 0x0000FFFF
137
138 if (ch > UNI_MAX_BMP) {
139 /* convert from unicode to utf16 surrogate pairs
140 * There is always space for one extra word in ubuf
141 */
142 ch -= halfBase;
143 current->ubuf[current->ubuflen++] = (WORD)((ch >> halfShift) + UNI_SUR_HIGH_START);
144 current->ubuf[current->ubuflen++] = (WORD)((ch & halfMask) + UNI_SUR_LOW_START);
145 }
146 else {
147 current->ubuf[current->ubuflen++] = ch;
148 }
149 current->ubufcols += utf8_width(ch);
150 if (current->ubuflen >= UBUF_MAX_CHARS) {
151 flush_ubuf(current);
152 }
153 }
154 #endif
155
156 static int flushOutput(struct current *current)
157 {
158 const char *pt = sb_str(current->output);
159 int len = sb_len(current->output);
160
161 #ifdef USE_UTF8
162 /* convert utf8 in current->output into utf16 in current->ubuf
163 */
164 while (len) {
165 int ch;
166 int n = utf8_tounicode(pt, &ch);
167
168 pt += n;
169 len -= n;
170
171 add_ubuf(current, ch);
172 }
173 flush_ubuf(current);
174 #else
175 DWORD nwritten;
176 COORD pos;
177
178 pos.Y = (SHORT)current->y;
179 pos.X = (SHORT)current->x;
180
181 SetConsoleCursorPosition(current->outh, pos);
182 WriteConsoleA(current->outh, pt, len, &nwritten, 0);
183
184 current->x += len;
185 #endif
186
187 sb_clear(current->output);
188
189 return 0;
190 }
191
192 static int outputChars(struct current *current, const char *buf, int len)
193 {
194 if (len < 0) {
195 len = strlen(buf);
196 }
197 assert(current->output);
198
199 sb_append_len(current->output, buf, len);
200
201 return 0;
202 }
203
204 static void outputNewline(struct current *current)
205 {
206 /* On the last row output a newline to force a scroll */
207 if (current->y + 1 == current->rows) {
208 outputChars(current, "\n", 1);
209 }
210 flushOutput(current);
211 current->x = 0;
212 current->y++;
213 }
214
215 static void setOutputHighlight(struct current *current, const int *props, int nprops)
216 {
217 int colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
218 int bold = 0;
219 int reverse = 0;
220 int i;
221
222 for (i = 0; i < nprops; i++) {
223 switch (props[i]) {
224 case 0:
225 colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
226 bold = 0;
227 reverse = 0;
228 break;
229 case 1:
230 bold = FOREGROUND_INTENSITY;
231 break;
232 case 7:
233 reverse = 1;
234 break;
235 case 30:
236 colour = 0;
237 break;
238 case 31:
239 colour = FOREGROUND_RED;
240 break;
241 case 32:
242 colour = FOREGROUND_GREEN;
243 break;
244 case 33:
245 colour = FOREGROUND_RED | FOREGROUND_GREEN;
246 break;
247 case 34:
248 colour = FOREGROUND_BLUE;
249 break;
250 case 35:
251 colour = FOREGROUND_RED | FOREGROUND_BLUE;
252 break;
253 case 36:
254 colour = FOREGROUND_BLUE | FOREGROUND_GREEN;
255 break;
256 case 37:
257 colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
258 break;
259 }
260 }
261
262 flushOutput(current);
263
264 if (reverse) {
265 SetConsoleTextAttribute(current->outh, BACKGROUND_INTENSITY);
266 }
267 else {
268 SetConsoleTextAttribute(current->outh, colour | bold);
269 }
270 }
271
272 static void eraseEol(struct current *current)
273 {
274 COORD pos;
275 DWORD n;
276
277 pos.X = (SHORT) current->x;
278 pos.Y = (SHORT) current->y;
279
280 FillConsoleOutputCharacter(current->outh, ' ', current->cols - current->x, pos, &n);
281 }
282
283 static void setCursorXY(struct current *current)
284 {
285 COORD pos;
286
287 pos.X = (SHORT) current->x;
288 pos.Y = (SHORT) current->y;
289
290 SetConsoleCursorPosition(current->outh, pos);
291 }
292
293
294 static void setCursorPos(struct current *current, int x)
295 {
296 current->x = x;
297 setCursorXY(current);
298 }
299
300 static void cursorUp(struct current *current, int n)
301 {
302 current->y -= n;
303 setCursorXY(current);
304 }
305
306 static void cursorDown(struct current *current, int n)
307 {
308 current->y += n;
309 setCursorXY(current);
310 }
311
312 static int fd_read(struct current *current)
313 {
314 while (1) {
315 INPUT_RECORD irec;
316 DWORD n;
317 if (WaitForSingleObject(current->inh, INFINITE) != WAIT_OBJECT_0) {
318 break;
319 }
320 if (!ReadConsoleInputW(current->inh, &irec, 1, &n)) {
321 break;
322 }
323 if (irec.EventType == KEY_EVENT) {
324 KEY_EVENT_RECORD *k = &irec.Event.KeyEvent;
325 if (k->bKeyDown || k->wVirtualKeyCode == VK_MENU) {
326 if (k->dwControlKeyState & ENHANCED_KEY) {
327 switch (k->wVirtualKeyCode) {
328 case VK_LEFT:
329 return SPECIAL_LEFT;
330 case VK_RIGHT:
331 return SPECIAL_RIGHT;
332 case VK_UP:
333 return SPECIAL_UP;
334 case VK_DOWN:
335 return SPECIAL_DOWN;
336 case VK_INSERT:
337 return SPECIAL_INSERT;
338 case VK_DELETE:
339 return SPECIAL_DELETE;
340 case VK_HOME:
341 return SPECIAL_HOME;
342 case VK_END:
343 return SPECIAL_END;
344 case VK_PRIOR:
345 return SPECIAL_PAGE_UP;
346 case VK_NEXT:
347 return SPECIAL_PAGE_DOWN;
348 case VK_RETURN:
349 return k->uChar.UnicodeChar;
350 }
351 }
352 /* Note that control characters are already translated in AsciiChar */
353 else if (k->wVirtualKeyCode == VK_CONTROL)
354 continue;
355 else {
356 return k->uChar.UnicodeChar;
357 }
358 }
359 }
360 }
361 return -1;
362 }
363
364 static int getWindowSize(struct current *current)
365 {
366 CONSOLE_SCREEN_BUFFER_INFO info;
367 if (!GetConsoleScreenBufferInfo(current->outh, &info)) {
368 return -1;
369 }
370 current->cols = info.dwSize.X;
371 current->rows = info.dwSize.Y;
372 if (current->cols <= 0 || current->rows <= 0) {
373 current->cols = 80;
374 return -1;
375 }
376 current->y = info.dwCursorPosition.Y;
377 current->x = info.dwCursorPosition.X;
378 return 0;
379 }
+44 -18
--- extsrc/linenoise.c
+++ extsrc/linenoise.c
@@ -892,10 +892,11 @@
892892
const char *prompt;
893893
stringbuf *capture; /* capture buffer, or NULL for none. Always null terminated */
894894
stringbuf *output; /* used only during refreshLine() - output accumulator */
895895
#if defined(USE_TERMIOS)
896896
int fd; /* Terminal fd */
897
+ int pending; /* pending char fd_read_char() */
897898
#elif defined(USE_WINCONSOLE)
898899
HANDLE outh; /* Console output handle */
899900
HANDLE inh; /* Console input handle */
900901
int rows; /* Screen rows */
901902
int x; /* Current column during output */
@@ -1121,28 +1122,31 @@
11211122
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
11221123
/* control chars - set return condition: min number of bytes and timer.
11231124
* We want read to return every single byte, without timeout. */
11241125
raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
11251126
1126
- /* put terminal in raw mode after flushing */
1127
- if (tcsetattr(current->fd,TCSADRAIN,&raw) < 0) {
1127
+ /* put terminal in raw mode. Because we aren't changing any output
1128
+ * settings we don't need to use TCSADRAIN and I have seen that hang on
1129
+ * OpenBSD when running under a pty
1130
+ */
1131
+ if (tcsetattr(current->fd,TCSANOW,&raw) < 0) {
11281132
goto fatal;
11291133
}
11301134
rawmode = 1;
11311135
return 0;
11321136
}
11331137
11341138
static void disableRawMode(struct current *current) {
11351139
/* Don't even check the return value as it's too late. */
1136
- if (rawmode && tcsetattr(current->fd,TCSADRAIN,&orig_termios) != -1)
1140
+ if (rawmode && tcsetattr(current->fd,TCSANOW,&orig_termios) != -1)
11371141
rawmode = 0;
11381142
}
11391143
11401144
/* At exit we'll try to fix the terminal to the initial conditions. */
11411145
static void linenoiseAtExit(void) {
11421146
if (rawmode) {
1143
- tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios);
1147
+ tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
11441148
}
11451149
linenoiseHistoryFree();
11461150
}
11471151
11481152
/* gcc/glibc insists that we care about the return code of write!
@@ -1230,29 +1234,35 @@
12301234
{
12311235
IGNORE_RC(write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7));
12321236
}
12331237
12341238
/**
1235
- * Reads a char from 'fd', waiting at most 'timeout' milliseconds.
1239
+ * Reads a char from 'current->fd', waiting at most 'timeout' milliseconds.
12361240
*
12371241
* A timeout of -1 means to wait forever.
12381242
*
12391243
* Returns -1 if no char is received within the time or an error occurs.
12401244
*/
1241
-static int fd_read_char(int fd, int timeout)
1245
+static int fd_read_char(struct current *current, int timeout)
12421246
{
12431247
struct pollfd p;
12441248
unsigned char c;
12451249
1246
- p.fd = fd;
1250
+ if (current->pending) {
1251
+ c = current->pending;
1252
+ current->pending = 0;
1253
+ return c;
1254
+ }
1255
+
1256
+ p.fd = current->fd;
12471257
p.events = POLLIN;
12481258
12491259
if (poll(&p, 1, timeout) == 0) {
12501260
/* timeout */
12511261
return -1;
12521262
}
1253
- if (read(fd, &c, 1) != 1) {
1263
+ if (read(current->fd, &c, 1) != 1) {
12541264
return -1;
12551265
}
12561266
return c;
12571267
}
12581268
@@ -1266,11 +1276,15 @@
12661276
char buf[MAX_UTF8_LEN];
12671277
int n;
12681278
int i;
12691279
int c;
12701280
1271
- if (read(current->fd, &buf[0], 1) != 1) {
1281
+ if (current->pending) {
1282
+ buf[0] = current->pending;
1283
+ current->pending = 0;
1284
+ }
1285
+ else if (read(current->fd, &buf[0], 1) != 1) {
12721286
return -1;
12731287
}
12741288
n = utf8_charlen(buf[0]);
12751289
if (n < 1) {
12761290
return -1;
@@ -1282,11 +1296,11 @@
12821296
}
12831297
/* decode and return the character */
12841298
utf8_tounicode(buf, &c);
12851299
return c;
12861300
#else
1287
- return fd_read_char(current->fd, -1);
1301
+ return fd_read_char(current, -1);
12881302
#endif
12891303
}
12901304
12911305
12921306
/**
@@ -1295,20 +1309,29 @@
12951309
*/
12961310
static int queryCursor(struct current *current, int* cols)
12971311
{
12981312
struct esc_parser parser;
12991313
int ch;
1314
+ /* Unfortunately we don't have any persistent state, so assume
1315
+ * a process will only ever interact with one terminal at a time.
1316
+ */
1317
+ static int query_cursor_failed;
1318
+
1319
+ if (query_cursor_failed) {
1320
+ /* If it ever fails, don't try again */
1321
+ return 0;
1322
+ }
13001323
13011324
/* Should not be buffering this output, it needs to go immediately */
13021325
assert(current->output == NULL);
13031326
13041327
/* control sequence - report cursor location */
13051328
outputChars(current, "\x1b[6n", -1);
13061329
13071330
/* Parse the response: ESC [ rows ; cols R */
13081331
initParseEscapeSeq(&parser, 'R');
1309
- while ((ch = fd_read_char(current->fd, 100)) > 0) {
1332
+ while ((ch = fd_read_char(current, 100)) > 0) {
13101333
switch (parseEscapeSequence(&parser, ch)) {
13111334
default:
13121335
continue;
13131336
case EP_END:
13141337
if (parser.numprops == 2 && parser.props[1] < 1000) {
@@ -1315,15 +1338,18 @@
13151338
*cols = parser.props[1];
13161339
return 1;
13171340
}
13181341
break;
13191342
case EP_ERROR:
1343
+ /* Push back the character that caused the error */
1344
+ current->pending = ch;
13201345
break;
13211346
}
13221347
/* failed */
13231348
break;
13241349
}
1350
+ query_cursor_failed = 1;
13251351
return 0;
13261352
}
13271353
13281354
/**
13291355
* Updates current->cols with the current window size (width)
@@ -1386,13 +1412,13 @@
13861412
* Returns SPECIAL_NONE if unrecognised, or -1 if EOF.
13871413
*
13881414
* If no additional char is received within a short time,
13891415
* CHAR_ESCAPE is returned.
13901416
*/
1391
-static int check_special(int fd)
1417
+static int check_special(struct current *current)
13921418
{
1393
- int c = fd_read_char(fd, 50);
1419
+ int c = fd_read_char(current, 50);
13941420
int c2;
13951421
13961422
if (c < 0) {
13971423
return CHAR_ESCAPE;
13981424
}
@@ -1399,11 +1425,11 @@
13991425
else if (c >= 'a' && c <= 'z') {
14001426
/* esc-a => meta-a */
14011427
return meta(c);
14021428
}
14031429
1404
- c2 = fd_read_char(fd, 50);
1430
+ c2 = fd_read_char(current, 50);
14051431
if (c2 < 0) {
14061432
return c2;
14071433
}
14081434
if (c == '[' || c == 'O') {
14091435
/* Potential arrow key */
@@ -1422,11 +1448,11 @@
14221448
return SPECIAL_HOME;
14231449
}
14241450
}
14251451
if (c == '[' && c2 >= '1' && c2 <= '8') {
14261452
/* extended escape */
1427
- c = fd_read_char(fd, 50);
1453
+ c = fd_read_char(current, 50);
14281454
if (c == '~') {
14291455
switch (c2) {
14301456
case '2':
14311457
return SPECIAL_INSERT;
14321458
case '3':
@@ -1441,11 +1467,11 @@
14411467
return SPECIAL_END;
14421468
}
14431469
}
14441470
while (c != -1 && c != '~') {
14451471
/* .e.g \e[12~ or '\e[11;2~ discard the complete sequence */
1446
- c = fd_read_char(fd, 50);
1472
+ c = fd_read_char(current, 50);
14471473
}
14481474
}
14491475
14501476
return SPECIAL_NONE;
14511477
}
@@ -2234,11 +2260,11 @@
22342260
}
22352261
continue;
22362262
}
22372263
#ifdef USE_TERMIOS
22382264
if (c == CHAR_ESCAPE) {
2239
- c = check_special(current->fd);
2265
+ c = check_special(current);
22402266
}
22412267
#endif
22422268
if (c == ctrl('R')) {
22432269
/* Search for the previous (earlier) match */
22442270
if (searchpos > 0) {
@@ -2346,11 +2372,11 @@
23462372
/* go on to process the returned char normally */
23472373
}
23482374
23492375
#ifdef USE_TERMIOS
23502376
if (c == CHAR_ESCAPE) { /* escape sequence */
2351
- c = check_special(current->fd);
2377
+ c = check_special(current);
23522378
}
23532379
#endif
23542380
if (c == -1) {
23552381
/* Return on errors */
23562382
return sb_len(current->buf);
23572383
--- extsrc/linenoise.c
+++ extsrc/linenoise.c
@@ -892,10 +892,11 @@
892 const char *prompt;
893 stringbuf *capture; /* capture buffer, or NULL for none. Always null terminated */
894 stringbuf *output; /* used only during refreshLine() - output accumulator */
895 #if defined(USE_TERMIOS)
896 int fd; /* Terminal fd */
 
897 #elif defined(USE_WINCONSOLE)
898 HANDLE outh; /* Console output handle */
899 HANDLE inh; /* Console input handle */
900 int rows; /* Screen rows */
901 int x; /* Current column during output */
@@ -1121,28 +1122,31 @@
1121 raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
1122 /* control chars - set return condition: min number of bytes and timer.
1123 * We want read to return every single byte, without timeout. */
1124 raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
1125
1126 /* put terminal in raw mode after flushing */
1127 if (tcsetattr(current->fd,TCSADRAIN,&raw) < 0) {
 
 
 
1128 goto fatal;
1129 }
1130 rawmode = 1;
1131 return 0;
1132 }
1133
1134 static void disableRawMode(struct current *current) {
1135 /* Don't even check the return value as it's too late. */
1136 if (rawmode && tcsetattr(current->fd,TCSADRAIN,&orig_termios) != -1)
1137 rawmode = 0;
1138 }
1139
1140 /* At exit we'll try to fix the terminal to the initial conditions. */
1141 static void linenoiseAtExit(void) {
1142 if (rawmode) {
1143 tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios);
1144 }
1145 linenoiseHistoryFree();
1146 }
1147
1148 /* gcc/glibc insists that we care about the return code of write!
@@ -1230,29 +1234,35 @@
1230 {
1231 IGNORE_RC(write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7));
1232 }
1233
1234 /**
1235 * Reads a char from 'fd', waiting at most 'timeout' milliseconds.
1236 *
1237 * A timeout of -1 means to wait forever.
1238 *
1239 * Returns -1 if no char is received within the time or an error occurs.
1240 */
1241 static int fd_read_char(int fd, int timeout)
1242 {
1243 struct pollfd p;
1244 unsigned char c;
1245
1246 p.fd = fd;
 
 
 
 
 
 
1247 p.events = POLLIN;
1248
1249 if (poll(&p, 1, timeout) == 0) {
1250 /* timeout */
1251 return -1;
1252 }
1253 if (read(fd, &c, 1) != 1) {
1254 return -1;
1255 }
1256 return c;
1257 }
1258
@@ -1266,11 +1276,15 @@
1266 char buf[MAX_UTF8_LEN];
1267 int n;
1268 int i;
1269 int c;
1270
1271 if (read(current->fd, &buf[0], 1) != 1) {
 
 
 
 
1272 return -1;
1273 }
1274 n = utf8_charlen(buf[0]);
1275 if (n < 1) {
1276 return -1;
@@ -1282,11 +1296,11 @@
1282 }
1283 /* decode and return the character */
1284 utf8_tounicode(buf, &c);
1285 return c;
1286 #else
1287 return fd_read_char(current->fd, -1);
1288 #endif
1289 }
1290
1291
1292 /**
@@ -1295,20 +1309,29 @@
1295 */
1296 static int queryCursor(struct current *current, int* cols)
1297 {
1298 struct esc_parser parser;
1299 int ch;
 
 
 
 
 
 
 
 
 
1300
1301 /* Should not be buffering this output, it needs to go immediately */
1302 assert(current->output == NULL);
1303
1304 /* control sequence - report cursor location */
1305 outputChars(current, "\x1b[6n", -1);
1306
1307 /* Parse the response: ESC [ rows ; cols R */
1308 initParseEscapeSeq(&parser, 'R');
1309 while ((ch = fd_read_char(current->fd, 100)) > 0) {
1310 switch (parseEscapeSequence(&parser, ch)) {
1311 default:
1312 continue;
1313 case EP_END:
1314 if (parser.numprops == 2 && parser.props[1] < 1000) {
@@ -1315,15 +1338,18 @@
1315 *cols = parser.props[1];
1316 return 1;
1317 }
1318 break;
1319 case EP_ERROR:
 
 
1320 break;
1321 }
1322 /* failed */
1323 break;
1324 }
 
1325 return 0;
1326 }
1327
1328 /**
1329 * Updates current->cols with the current window size (width)
@@ -1386,13 +1412,13 @@
1386 * Returns SPECIAL_NONE if unrecognised, or -1 if EOF.
1387 *
1388 * If no additional char is received within a short time,
1389 * CHAR_ESCAPE is returned.
1390 */
1391 static int check_special(int fd)
1392 {
1393 int c = fd_read_char(fd, 50);
1394 int c2;
1395
1396 if (c < 0) {
1397 return CHAR_ESCAPE;
1398 }
@@ -1399,11 +1425,11 @@
1399 else if (c >= 'a' && c <= 'z') {
1400 /* esc-a => meta-a */
1401 return meta(c);
1402 }
1403
1404 c2 = fd_read_char(fd, 50);
1405 if (c2 < 0) {
1406 return c2;
1407 }
1408 if (c == '[' || c == 'O') {
1409 /* Potential arrow key */
@@ -1422,11 +1448,11 @@
1422 return SPECIAL_HOME;
1423 }
1424 }
1425 if (c == '[' && c2 >= '1' && c2 <= '8') {
1426 /* extended escape */
1427 c = fd_read_char(fd, 50);
1428 if (c == '~') {
1429 switch (c2) {
1430 case '2':
1431 return SPECIAL_INSERT;
1432 case '3':
@@ -1441,11 +1467,11 @@
1441 return SPECIAL_END;
1442 }
1443 }
1444 while (c != -1 && c != '~') {
1445 /* .e.g \e[12~ or '\e[11;2~ discard the complete sequence */
1446 c = fd_read_char(fd, 50);
1447 }
1448 }
1449
1450 return SPECIAL_NONE;
1451 }
@@ -2234,11 +2260,11 @@
2234 }
2235 continue;
2236 }
2237 #ifdef USE_TERMIOS
2238 if (c == CHAR_ESCAPE) {
2239 c = check_special(current->fd);
2240 }
2241 #endif
2242 if (c == ctrl('R')) {
2243 /* Search for the previous (earlier) match */
2244 if (searchpos > 0) {
@@ -2346,11 +2372,11 @@
2346 /* go on to process the returned char normally */
2347 }
2348
2349 #ifdef USE_TERMIOS
2350 if (c == CHAR_ESCAPE) { /* escape sequence */
2351 c = check_special(current->fd);
2352 }
2353 #endif
2354 if (c == -1) {
2355 /* Return on errors */
2356 return sb_len(current->buf);
2357
--- extsrc/linenoise.c
+++ extsrc/linenoise.c
@@ -892,10 +892,11 @@
892 const char *prompt;
893 stringbuf *capture; /* capture buffer, or NULL for none. Always null terminated */
894 stringbuf *output; /* used only during refreshLine() - output accumulator */
895 #if defined(USE_TERMIOS)
896 int fd; /* Terminal fd */
897 int pending; /* pending char fd_read_char() */
898 #elif defined(USE_WINCONSOLE)
899 HANDLE outh; /* Console output handle */
900 HANDLE inh; /* Console input handle */
901 int rows; /* Screen rows */
902 int x; /* Current column during output */
@@ -1121,28 +1122,31 @@
1122 raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
1123 /* control chars - set return condition: min number of bytes and timer.
1124 * We want read to return every single byte, without timeout. */
1125 raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
1126
1127 /* put terminal in raw mode. Because we aren't changing any output
1128 * settings we don't need to use TCSADRAIN and I have seen that hang on
1129 * OpenBSD when running under a pty
1130 */
1131 if (tcsetattr(current->fd,TCSANOW,&raw) < 0) {
1132 goto fatal;
1133 }
1134 rawmode = 1;
1135 return 0;
1136 }
1137
1138 static void disableRawMode(struct current *current) {
1139 /* Don't even check the return value as it's too late. */
1140 if (rawmode && tcsetattr(current->fd,TCSANOW,&orig_termios) != -1)
1141 rawmode = 0;
1142 }
1143
1144 /* At exit we'll try to fix the terminal to the initial conditions. */
1145 static void linenoiseAtExit(void) {
1146 if (rawmode) {
1147 tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
1148 }
1149 linenoiseHistoryFree();
1150 }
1151
1152 /* gcc/glibc insists that we care about the return code of write!
@@ -1230,29 +1234,35 @@
1234 {
1235 IGNORE_RC(write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7));
1236 }
1237
1238 /**
1239 * Reads a char from 'current->fd', waiting at most 'timeout' milliseconds.
1240 *
1241 * A timeout of -1 means to wait forever.
1242 *
1243 * Returns -1 if no char is received within the time or an error occurs.
1244 */
1245 static int fd_read_char(struct current *current, int timeout)
1246 {
1247 struct pollfd p;
1248 unsigned char c;
1249
1250 if (current->pending) {
1251 c = current->pending;
1252 current->pending = 0;
1253 return c;
1254 }
1255
1256 p.fd = current->fd;
1257 p.events = POLLIN;
1258
1259 if (poll(&p, 1, timeout) == 0) {
1260 /* timeout */
1261 return -1;
1262 }
1263 if (read(current->fd, &c, 1) != 1) {
1264 return -1;
1265 }
1266 return c;
1267 }
1268
@@ -1266,11 +1276,15 @@
1276 char buf[MAX_UTF8_LEN];
1277 int n;
1278 int i;
1279 int c;
1280
1281 if (current->pending) {
1282 buf[0] = current->pending;
1283 current->pending = 0;
1284 }
1285 else if (read(current->fd, &buf[0], 1) != 1) {
1286 return -1;
1287 }
1288 n = utf8_charlen(buf[0]);
1289 if (n < 1) {
1290 return -1;
@@ -1282,11 +1296,11 @@
1296 }
1297 /* decode and return the character */
1298 utf8_tounicode(buf, &c);
1299 return c;
1300 #else
1301 return fd_read_char(current, -1);
1302 #endif
1303 }
1304
1305
1306 /**
@@ -1295,20 +1309,29 @@
1309 */
1310 static int queryCursor(struct current *current, int* cols)
1311 {
1312 struct esc_parser parser;
1313 int ch;
1314 /* Unfortunately we don't have any persistent state, so assume
1315 * a process will only ever interact with one terminal at a time.
1316 */
1317 static int query_cursor_failed;
1318
1319 if (query_cursor_failed) {
1320 /* If it ever fails, don't try again */
1321 return 0;
1322 }
1323
1324 /* Should not be buffering this output, it needs to go immediately */
1325 assert(current->output == NULL);
1326
1327 /* control sequence - report cursor location */
1328 outputChars(current, "\x1b[6n", -1);
1329
1330 /* Parse the response: ESC [ rows ; cols R */
1331 initParseEscapeSeq(&parser, 'R');
1332 while ((ch = fd_read_char(current, 100)) > 0) {
1333 switch (parseEscapeSequence(&parser, ch)) {
1334 default:
1335 continue;
1336 case EP_END:
1337 if (parser.numprops == 2 && parser.props[1] < 1000) {
@@ -1315,15 +1338,18 @@
1338 *cols = parser.props[1];
1339 return 1;
1340 }
1341 break;
1342 case EP_ERROR:
1343 /* Push back the character that caused the error */
1344 current->pending = ch;
1345 break;
1346 }
1347 /* failed */
1348 break;
1349 }
1350 query_cursor_failed = 1;
1351 return 0;
1352 }
1353
1354 /**
1355 * Updates current->cols with the current window size (width)
@@ -1386,13 +1412,13 @@
1412 * Returns SPECIAL_NONE if unrecognised, or -1 if EOF.
1413 *
1414 * If no additional char is received within a short time,
1415 * CHAR_ESCAPE is returned.
1416 */
1417 static int check_special(struct current *current)
1418 {
1419 int c = fd_read_char(current, 50);
1420 int c2;
1421
1422 if (c < 0) {
1423 return CHAR_ESCAPE;
1424 }
@@ -1399,11 +1425,11 @@
1425 else if (c >= 'a' && c <= 'z') {
1426 /* esc-a => meta-a */
1427 return meta(c);
1428 }
1429
1430 c2 = fd_read_char(current, 50);
1431 if (c2 < 0) {
1432 return c2;
1433 }
1434 if (c == '[' || c == 'O') {
1435 /* Potential arrow key */
@@ -1422,11 +1448,11 @@
1448 return SPECIAL_HOME;
1449 }
1450 }
1451 if (c == '[' && c2 >= '1' && c2 <= '8') {
1452 /* extended escape */
1453 c = fd_read_char(current, 50);
1454 if (c == '~') {
1455 switch (c2) {
1456 case '2':
1457 return SPECIAL_INSERT;
1458 case '3':
@@ -1441,11 +1467,11 @@
1467 return SPECIAL_END;
1468 }
1469 }
1470 while (c != -1 && c != '~') {
1471 /* .e.g \e[12~ or '\e[11;2~ discard the complete sequence */
1472 c = fd_read_char(current, 50);
1473 }
1474 }
1475
1476 return SPECIAL_NONE;
1477 }
@@ -2234,11 +2260,11 @@
2260 }
2261 continue;
2262 }
2263 #ifdef USE_TERMIOS
2264 if (c == CHAR_ESCAPE) {
2265 c = check_special(current);
2266 }
2267 #endif
2268 if (c == ctrl('R')) {
2269 /* Search for the previous (earlier) match */
2270 if (searchpos > 0) {
@@ -2346,11 +2372,11 @@
2372 /* go on to process the returned char normally */
2373 }
2374
2375 #ifdef USE_TERMIOS
2376 if (c == CHAR_ESCAPE) { /* escape sequence */
2377 c = check_special(current);
2378 }
2379 #endif
2380 if (c == -1) {
2381 /* Return on errors */
2382 return sb_len(current->buf);
2383

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button