1 module unde.command_line.run;
2 
3 import unde.global_state;
4 import unde.lsblk;
5 import unde.path_mnt;
6 import unde.slash;
7 import unde.command_line.db;
8 import unde.font;
9 import unde.lib;
10 import unde.marks;
11 
12 import std.stdio;
13 import std.conv;
14 import core.stdc.stdlib;
15 import std.math;
16 import berkeleydb.all;
17 import std.stdint;
18 import core.stdc.stdlib;
19 import std..string;
20 import std.path;
21 import std.algorithm.sorting;
22 import std.algorithm.searching;
23 import std.algorithm.comparison;
24 import std.range.primitives;
25 import std.utf;
26 import std.concurrency;
27 import core.time;
28 import core.exception;
29 import core.sys.posix.sys.select;
30 import core.sys.posix.fcntl;
31 import core.sys.posix.unistd;
32 import core.stdc..string;
33 import core.stdc.errno;
34 import core.thread;
35 import std.process;
36 import std.regex;
37 import std.datetime;
38 import std.algorithm.mutation;
39 import core.sys.posix.pty; //Hand-made module
40 import core.sys.posix.sys.ioctl;
41 import core.sys.posix.termios;
42 import core.sys.posix.sys.wait;
43 static import std.algorithm;
44 
45 import derelict.sdl2.sdl;
46 
47 import unde.command_line.delete_command;
48 import unde.command_line.lib;
49 import unde.marks;
50 import unde.lib;
51 
52 import std.file;
53 
54 version(Windows)
55 {
56 import core.sys.windows.windows;
57 import core.stdc.time;
58 alias ulong ulong_t;
59 }
60 
61 enum Mode
62 {
63     Working,
64     Alternate
65 }
66 
67 struct WorkMode
68 {
69     char[4096] buf;
70     ssize_t buf_r;
71     ssize_t max_r;
72     ushort cur_attr = Attr.Black<<4 | Attr.White;
73     ushort[] attrs;
74     ssize_t attrs_r;
75     ulong out_id1;
76     ssize_t saved_buf_r;
77     ushort saved_attr;
78     char[4096] prebuf;
79     ssize_t prebuf_r;
80     Tid tid;
81     bool select;
82 }
83 
84 struct AltMode
85 {
86     dchar[] buffer;
87     ushort[] alt_attrs;
88     int cols = 80;
89     int rows = 25;
90     ssize_t y;
91     ssize_t x;
92     ulong alt_out_id1;
93     ushort alt_cur_attr = Attr.Black<<4 | Attr.White;
94     string control_input;
95     ssize_t scroll_from;
96     ssize_t scroll_to;
97     bool insert_mode;
98     bool cursor_mode;
99 
100     void reset()
101     {
102         y = 0;
103         x = 0;
104         alt_out_id1 = 0;
105         alt_cur_attr = Attr.Black<<4 | Attr.White;
106         control_input = "";
107         scroll_from = 0;
108         scroll_to = rows;
109         insert_mode = false;
110     }
111 }
112 
113 private void
114 set_non_block_mode(int fd)
115 {
116 version(Posix)
117 {
118     int flags;
119     if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
120         flags = 0;
121     int res = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
122     if (res < 0)
123     {
124         throw new Exception("fcntl() error: " ~ fromStringz(strerror(errno)).idup());
125     }
126 }
127 }
128 
129 
130 private ulong
131 get_max_id_of_cwd(T)(T cgs, string cwd)
132 if (is (T == CMDGlobalState) || is (T == GlobalState))
133 {
134     Dbc cursor = cgs.db_commands.cursor(cgs.txn, 0);
135     scope(exit) cursor.close();
136 
137     Dbt key, data;
138     ulong id = find_prev_command(cursor, cwd, 0, key, data);
139 
140     if (id != 0)
141     {
142         string key_string = key.to!(string);
143         command_key cmd_key;
144         parse_key_for_command(key_string, cmd_key);
145         if (cwd != cmd_key.cwd)
146         {
147             id = 0;
148         }
149         else
150         {
151             id = cmd_key.id;
152         }
153     }
154 
155     return id;
156 }
157 
158 private ulong
159 find_command_in_cwd(T)(T cgs, string cwd, string command)
160 if (is (T == CMDGlobalState) || is (T == GlobalState))
161 {
162     Dbc cursor = cgs.db_commands.cursor(cgs.txn, 0);
163     scope(exit) cursor.close();
164 
165     Dbt key, data;
166     ulong id = 0;
167     string ks = get_key_for_command(command_key(cwd, 0));
168     key = ks;
169     auto res = cursor.get(&key, &data, DB_SET_RANGE);
170     if (res == DB_NOTFOUND)
171     {
172         return 0;
173     }
174 
175     do
176     {
177         string key_string = key.to!(string);
178         command_key cmd_key;
179         parse_key_for_command(key_string, cmd_key);
180 
181         string data_string = data.to!(string);
182         command_data cmd_data;
183         parse_data_for_command(data_string, cmd_data);
184 
185         if (cmd_key.cwd != cwd) break;
186         if (cmd_data.command == command)
187         {
188             id = cmd_key.id;
189             break;
190         }
191 
192     } while (cursor.get(&key, &data, DB_NEXT) == 0);
193 
194     return id;
195 }
196 
197 enum StateEscape
198 {
199     WaitEscape,
200     WaitBracket,
201     WaitNumberR,
202     WaitText,
203     WaitCSI,
204     WaitCharSet,
205     WaitEight,
206     WaitG0CharSet,
207     WaitG1CharSet
208 }
209 
210 /* Information about Escape sequence: man console_codes */
211 private size_t
212 process_escape_sequences(CMDGlobalState cgs, 
213         ref Mode mode, ref WorkMode workmode, ref AltMode altmode,
214         ref ssize_t r)
215 {
216     //process_escape_sequences: 1 sec, 403 ms of 3 s
217     with(workmode)
218     {
219         StateEscape state;
220         int substate;
221         string text;
222         int[] numbers;
223         ssize_t i;
224         ssize_t cur_chr_stride;
225         loop:
226         for (i = 0; i < r; i += cur_chr_stride)
227         {
228             //Stat processing: 570 ms, 203 μs of 3 s
229             //assert(buf[0..buf_r].walkLength == attrs_r, format("walkLength=%d, attrs_r=%d", buf[0..buf_r].walkLength, attrs_r));
230             cur_chr_stride = prebuf.mystride(i, r);
231 
232             char[] chr;
233             if (i+cur_chr_stride > r)
234             {
235                 chr = prebuf[i..i+1];
236             }
237             else
238                 chr = prebuf[i..i+cur_chr_stride];
239 
240             if (i+chr.length >= r && chr.length == 1 && chr[0] & 0b1000_0000)
241             {
242                 break;
243             }
244 
245             with(altmode)
246             {
247                 if (y < 0) y = 0;
248                 if (y >= rows) y = rows-1;
249                 if (x < 0) x = 0;
250                 if (x >= cols) y = cols-1;
251             }
252 
253             //writefln("Process '%c' - %X (%d)", prebuf[i], prebuf[i], prebuf[i]);
254             final switch (state)
255             {
256                 case StateEscape.WaitEscape:
257                     if (chr.length > 1)
258                     {
259                         //writef("+%s", chr);
260                         final switch(mode)
261                         {
262                             case Mode.Working:
263                             auto started2 = Clock.currTime();
264                                 //Long character rpocessing: 455 ms, 532 μs of 3 s
265                                 /*writefln("%s", chr);
266                                 if (buf_r > 4)
267                                     writefln("buf=%s", buf[buf_r-4..buf_r+4]);*/
268                                 if (max_r >= buf.length || buf_r >= buf.length) break loop;
269                                 auto chrlen = buf.mystride(buf_r);
270 
271                                 if (altmode.insert_mode)
272                                 {
273                                     for (ssize_t j = max_r-1; j >= cast(ssize_t)(buf_r+chr.length); j--)
274                                     {
275                                         buf[j] = buf[j-chr.length];
276                                     }
277                                     max_r += chr.length;
278                                 }
279                                 else if (chrlen != chr.length)
280                                 {
281                                     //if (max_r + chr.length-chrlen > buf.length) max_r = buf.length - (chr.length-chrlen);
282                                     if (max_r <= buf_r+chrlen) 
283                                     {
284                                         buf[max_r..buf_r+chrlen] = ' ';
285                                         max_r = buf_r+chrlen;
286                                     }
287                                     if (max_r+chr.length-chrlen >= buf.length) break loop;
288                                     for (ssize_t j = max_r-1; j >= buf_r+chrlen; j--)
289                                         buf[j + chr.length-chrlen] = buf[j];
290                                     //std.algorithm.mutation.copy(buf[buf_r+chrlen..max_r] , buf[buf_r+chr.length..max_r+chr.length-chrlen]);
291                                     max_r += chr.length-chrlen;
292                                 }
293 
294                                 buf[buf_r..buf_r+chr.length] = chr;
295                                 /*if (buf_r > 4)
296                                     writefln("After buf=%s", buf[buf_r-4..buf_r+4]);*/
297                                 buf_r+=chr.length;
298                                 //writefln("\nbuf_r=%d, buf=%s", buf_r, buf[0..buf_r]);
299                                 if (buf_r > max_r) max_r = buf_r;
300                                 if (attrs_r < 0) attrs_r = 0;
301                                 if (attrs_r >= attrs.length) attrs.length = attrs_r+1;
302                                 attrs[attrs_r] = cur_attr;
303                                 attrs_r++;
304                                 continue;
305 
306                             case Mode.Alternate:
307                                 with(altmode)
308                                 {
309                                     buffer[y*cols+x] = to!dchar(chr);
310                                     alt_attrs[y*cols+x] = alt_cur_attr;
311                                     x++;
312                                     if (x >= cols)
313                                     {
314                                         y++;
315                                         x = 0;
316                                     }
317                                     if (y >= scroll_to)
318                                     {
319                                         y--;
320                                         std.algorithm.mutation.copy(cast(ubyte[])(buffer[scroll_from*cols + cols..scroll_to*cols]), 
321                                                 cast(ubyte[])(buffer[scroll_from*cols..(scroll_to-1)*cols]));
322                                         std.algorithm.mutation.copy(alt_attrs[scroll_from*cols + cols..scroll_to*cols], 
323                                                 alt_attrs[scroll_from*cols..(scroll_to-1)*cols]);
324                                         buffer[(scroll_to-1)*cols..scroll_to*cols] = " "d[0];
325                                         alt_attrs[(scroll_to-1)*cols..scroll_to*cols] = 0;
326                                     }
327                                     continue;
328                                 }
329                         }
330                     }
331 
332                     if (prebuf[i] == '\x07') //Bell
333                     {
334                         continue;
335                     }
336                     else if (prebuf[i] == '\x08') //BackSpace
337                     {
338                         final switch(mode)
339                         {
340                             case Mode.Working:
341                                 if (buf_r > buf.length) buf_r = buf.length-1;
342                                 if (buf_r > 0 && buf_r >= buf.mystrideBack(buf_r))
343                                     buf_r -= buf.mystrideBack(buf_r);
344                                 attrs_r--;
345                                 break;
346                             case Mode.Alternate:
347                                 with(altmode)
348                                 {
349                                     x--;
350                                     if (x < 0) x = 0;
351                                 }
352                                 break;
353                         }
354                         //writefln("BackSpace");
355                         //writefln("buf_r2=%s", buf[buf_r..max_r]);
356                     }
357                     else if (prebuf[i] == '\x09') //Tab
358                     {
359                         final switch(mode)
360                         {
361                             case Mode.Working:
362                                 ssize_t n = buf[0..buf_r].lastIndexOf("\n");
363                                 if (n < 0) n = 0;
364                                 else n++;
365 
366                                 ssize_t spaces = 8-(buf_r - n)%8;
367 
368                                 for (ssize_t j=0; j < spaces; j++)
369                                 {
370                                     if (buf_r >= buf.length) break loop;
371                                     auto chrlen = buf.mystride(buf_r);
372                                     if (chrlen != 1)
373                                     {
374                                         if (max_r <= buf_r+chrlen) 
375                                         {
376                                             buf[max_r..buf_r+chrlen] = ' ';
377                                             max_r = buf_r+chrlen;
378                                         }
379                                         std.algorithm.mutation.copy(cast(ubyte[])(buf[buf_r+chrlen..max_r]), cast(ubyte[])(buf[buf_r+1..max_r+1-chrlen]));
380                                         max_r += 1-chrlen;
381                                     }
382                                     buf[buf_r] = ' ';
383                                     /*if (buf_r > 4)
384                                         writefln("After buf=%s", buf[buf_r-4..buf_r+4]);*/
385                                     buf_r++;
386                                     if (buf_r > max_r) max_r = buf_r;
387                                     if (attrs_r < 0) attrs_r = 0;
388                                     if (attrs_r >= attrs.length) attrs.length = attrs_r+1;
389                                     attrs[attrs_r] = cur_attr;
390                                     attrs_r++;
391                                 }
392                                 break;
393 
394                             case Mode.Alternate:
395                                 with(altmode)
396                                 {
397                                     ssize_t spaces = 8-x%8;
398                                     for (ssize_t j=0; j < spaces; j++)
399                                     {
400                                         buffer[y*cols+x] = " "d[0];
401                                         alt_attrs[y*cols+x] = alt_cur_attr;
402                                         x++;
403                                         if (x >= cols)
404                                         {
405                                             y++;
406                                             x = 0;
407                                         }
408                                         if (y >= scroll_to)
409                                         {
410                                             y--;
411                                             std.algorithm.mutation.copy(cast(ubyte[])(buffer[scroll_from*cols + cols..scroll_to*cols]), 
412                                                     cast(ubyte[])(buffer[scroll_from*cols..(scroll_to-1)*cols]));
413                                             std.algorithm.mutation.copy(alt_attrs[scroll_from*cols + cols..scroll_to*cols], 
414                                                     alt_attrs[scroll_from*cols..(scroll_to-1)*cols]);
415                                             buffer[(scroll_to-1)*cols..scroll_to*cols] = " "d[0];
416                                             alt_attrs[(scroll_to-1)*cols..scroll_to*cols] = 0;
417                                         }
418                                     }
419                                 }
420                                 break;
421                         }
422                     }
423                     else if (prebuf[i] == '\r') 
424                     {
425                         final switch(mode)
426                         {
427                             case Mode.Working:
428                                 if (prebuf[i+1] != '\n')
429                                 {
430                                     if (buf_r > buf.length) buf_r = buf.length;
431                                     ssize_t newline = buf[0..buf_r].lastIndexOf("\n");
432                                     if (newline < 0) newline = 0;
433                                     else newline++;
434                                     ssize_t wl = buf[newline..buf_r].myWalkLength();
435 
436                                     //writefln("\\r wl = %d", wl);
437                                     while ( wl%altmode.cols != 0 )
438                                     {
439                                         if (buf_r > 0)
440                                         {
441                                             buf_r -= buf.mystrideBack(buf_r);
442                                             attrs_r--;
443                                         }
444                                         wl--;
445                                     }
446                                 }
447                                 break;
448 
449                             case Mode.Alternate:
450                                 with(altmode)
451                                 {
452                                     x=0;
453                                 }
454                                 break;
455 
456                         }
457                     }
458                     else if (prebuf[i] == '\x1B')
459                     {
460                         state = StateEscape.WaitBracket;
461                         /*auto till = prebuf[i+1..r].indexOf("\x1B");
462                         if (till < 0) till = r;
463                         else till += i+1;
464                         writefln("\n!ESC %s", prebuf[i+1..till]);*/
465                     }
466                     else if (prebuf[i] == '\x9B')
467                     {
468                         state = StateEscape.WaitCSI;
469                         text = "";
470                         numbers = [];
471                     }
472                     else
473                     {
474                         /*if ( (prebuf[i] < 0x20 || prebuf[i] == 0x7F) && prebuf[i] != '\n')
475                         {
476                             writef("*%X(%d)", prebuf[i], prebuf[i]);
477                         }
478                         else
479                         {
480                             writef("+%c", prebuf[i]);
481                         }*/
482                         final switch(mode)
483                         {
484                             case Mode.Working:
485                                 /*writefln("%c", prebuf[i]);
486                                 if (buf_r > 4)
487                                     writefln("buf=%s", buf[buf_r-4..buf_r+4]);*/
488                                 if (prebuf[i] == '\n' && buf_r < max_r && i+1 >= r && r > 1)
489                                 {
490                                     break loop;
491                                 }
492                                 if (prebuf[i] == '\n' && buf_r < max_r && i+1 < r && prebuf[i+1] == '\r')
493                                 {
494                                     if (buf_r > buf.length) buf_r = buf.length;
495                                     ssize_t newline = buf[0..buf_r].lastIndexOf("\n");
496                                     if (newline < 0) newline = 0;
497                                     else newline++;
498                                     ssize_t wl = buf[newline..buf_r].myWalkLength();
499 
500                                     writefln("\\n wl = %d", wl);
501                                     while (wl%altmode.cols != 0)
502                                     {
503                                         if (buf_r < buf.length)
504                                         {
505                                             buf_r += buf.mystride(buf_r);
506                                             attrs_r++;
507                                         }
508                                         wl++;
509                                     }
510                                     if (buf_r > max_r) max_r = buf_r;
511                                 }
512                                 else
513                                 {
514                                     if (prebuf[i] == '\n')
515                                     {
516                                         if (altmode.scroll_to < altmode.rows)
517                                         {
518                                             if (buf_r > buf.length) buf_r = buf.length;
519                                             ssize_t newline = buf[0..buf_r].lastIndexOf("\n");
520                                             if (newline < 0) newline = 0;
521                                             else newline++;
522                                             ssize_t wl = buf[newline..buf_r].myWalkLength();
523 
524                                             writefln("\\n wl = %d", wl);
525                                             while ( wl%altmode.cols != altmode.cols-1 )
526                                             {
527                                                 if (buf_r < buf.length)
528                                                 {
529                                                     buf_r += buf.mystride(buf_r);
530                                                     if (buf[buf_r] == char.init) buf[buf_r] = ' ';
531                                                     attrs_r++;
532                                                 }
533                                                 wl++;
534                                             }
535                                             if (buf_r > max_r) max_r = buf_r;
536 
537                                             if (max_r > buf_r+1)
538                                             {
539                                                 std.algorithm.mutation.copy(cast(ubyte[])(buf[buf_r+1..max_r]), cast(ubyte[])(buf[buf_r+1+altmode.cols..max_r+altmode.cols]));
540                                                 attrs.length += altmode.cols;
541                                                 std.algorithm.mutation.copy(attrs[attrs_r+1..$-altmode.cols], attrs[attrs_r+1+altmode.cols..$]);
542                                                 buf[buf_r+1..max_r] = ' ';
543                                                 attrs[attrs_r+1..$-altmode.cols] = Attr.Black<<4 | Attr.White;
544                                                 max_r += altmode.cols;
545                                             }
546                                         }
547                                         else
548                                         {
549                                             buf_r = max_r;
550                                             attrs_r = attrs.length;
551                                         }
552                                     }
553                                     if (max_r >= buf.length || buf_r >= buf.length) break loop;
554                                     auto chrlen = buf.mystride(buf_r);
555                                     if (altmode.insert_mode)
556                                     {
557                                         for (ssize_t j = max_r-1; j >= buf_r+1; j--)
558                                             buf[j] = buf[j-1];
559                                         max_r++;
560                                     }
561                                     else if (chrlen != 1)
562                                     {
563                                         if (max_r <= buf_r+chrlen) 
564                                         {
565                                             buf[max_r..buf_r+chrlen] = ' ';
566                                             max_r = buf_r+chrlen;
567                                         }
568                                         std.algorithm.mutation.copy(cast(ubyte[])(buf[buf_r+chrlen..max_r]), cast(ubyte[])(buf[buf_r+1..max_r+1-chrlen]));
569                                         max_r += 1-chrlen;
570                                     }
571                                     buf[buf_r] = prebuf[i];
572                                     /*if (buf_r > 4)
573                                         writefln("After buf=%s", buf[buf_r-4..buf_r+4]);*/
574                                     buf_r++;
575                                     //writefln("\nbuf_r=%d, buf=%s", buf_r, buf[0..buf_r]);
576                                     if (buf_r > max_r) max_r = buf_r;
577                                     //writefln("ADD '%c': buf=%s", prebuf[i], buf[0..max_r]);
578                                     if (attrs_r < 0) attrs_r = 0;
579                                     if (attrs_r >= attrs.length) attrs.length = attrs_r+1;
580                                     attrs[attrs_r] = cur_attr;
581                                     //writefln("attrs[%d]=%X, buf_r=%d", attrs_r, cur_attr, buf_r);
582                                     attrs_r++;
583                                 }
584                                 break;
585 
586                                 case Mode.Alternate:
587                                     with(altmode)
588                                     {
589                                         if (prebuf[i] == '\n')
590                                         {
591                                             y++;
592                                             x = 0;
593                                         }
594                                         else
595                                         {
596                                             {
597                                                 if (y < 0) y = 0;
598                                                 if (y >= rows) y = rows-1;
599                                                 if (x < 0) x = 0;
600                                                 if (x >= cols) y = cols-1;
601                                             }
602                                             buffer[y*cols+x] = to!dchar(prebuf[i]);
603                                             alt_attrs[y*cols+x] = alt_cur_attr;
604                                             x++;
605                                             if (x >= cols)
606                                             {
607                                                 y++;
608                                                 x = 0;
609                                             }
610                                         }
611                                         if (y >= scroll_to)
612                                         {
613                                             y--;
614                                             std.algorithm.mutation.copy(cast(ubyte[])(buffer[scroll_from*cols + cols..scroll_to*cols]), 
615                                                     cast(ubyte[])(buffer[scroll_from*cols..(scroll_to-1)*cols]));
616                                             std.algorithm.mutation.copy(alt_attrs[scroll_from*cols + cols..scroll_to*cols], 
617                                                     alt_attrs[scroll_from*cols..(scroll_to-1)*cols]);
618                                             buffer[(scroll_to-1)*cols..scroll_to*cols] = " "d[0];
619                                             alt_attrs[(scroll_to-1)*cols..scroll_to*cols] = 0;
620                                         }
621                                     }
622                                     break;
623                         }
624                     }
625                     break;
626                 case StateEscape.WaitBracket:
627                     switch (prebuf[i])
628                     {
629                         case ']':
630                             state = StateEscape.WaitNumberR;
631                             break;
632                         case '[':
633                             state = StateEscape.WaitCSI;
634                             text = "";
635                             numbers = [];
636                             break;
637                         case 'c':
638                             /* Reset */
639                             state = StateEscape.WaitEscape;
640                             break;
641                         case 'D':
642                             /* Linefeed */
643                             state = StateEscape.WaitEscape;
644                             break;
645                         case 'E':
646                             /* Newline */
647                             state = StateEscape.WaitEscape;
648                             break;
649                         case 'H':
650                             /* Set tab stop at current column. */
651                             state = StateEscape.WaitEscape;
652                             break;
653                         case 'M':
654                             /* Reverse linefeed. */
655                             final switch(mode)
656                             {
657                                 case Mode.Working:
658                                     break;
659 
660                                 case Mode.Alternate:
661                                     with(altmode)
662                                     {
663                                         y--;
664                                         if (y < scroll_from)
665                                         {
666                                             y++;
667                                             //writefln("reverse linefeed");
668                                             for (ssize_t R = scroll_to-2; R >= scroll_from; R--)
669                                             {
670                                                 buffer[(R+1)*cols..(R+2)*cols] = buffer[R*cols..(R+1)*cols];
671                                                 alt_attrs[(R+1)*cols..(R+2)*cols] = alt_attrs[R*cols..(R+1)*cols];
672                                             }
673                                             buffer[scroll_from*cols..scroll_from*cols+cols] = " "d[0];
674                                             alt_attrs[scroll_from*cols..scroll_from*cols+cols] = 0;
675                                         }
676                                     }
677                                     break;
678                             }
679                             state = StateEscape.WaitEscape;
680                             break;
681                         case 'Z':
682                             /* DEC private identification. The kernel returns the
683                                string  ESC [ ? 6 c, claiming that it is a VT102.. */
684                             state = StateEscape.WaitEscape;
685                             break;
686                         case '7':
687                             /*Save current state (cursor coordinates,
688                               attributes, character sets pointed at by G0, G1).*/
689                             final switch(mode)
690                             {
691                                 case Mode.Working:
692                                     saved_buf_r = buf_r;
693                                     saved_attr = cur_attr;
694                                     break;
695 
696                                 case Mode.Alternate:
697                                     with(altmode)
698                                     {
699                                     }
700                             }
701                             state = StateEscape.WaitEscape;
702                             break;
703                         case '8':
704                             /*Restore state most recently saved by ESC 7.*/
705                             final switch(mode)
706                             {
707                                 case Mode.Working:
708                                     buf_r = saved_buf_r;
709                                     while ( (buf[buf_r] & 0b1100_0000) == 0b1000_0000 )
710                                         buf_r--;
711                                     attrs_r = buf[0..buf_r].myWalkLength();
712                                     cur_attr = saved_attr;
713                                     break;
714 
715                                 case Mode.Alternate:
716                                     with(altmode)
717                                     {
718                                     }
719                             }
720                             state = StateEscape.WaitEscape;
721                             break;
722                         case '%':
723                             state = StateEscape.WaitCharSet;
724                             break;
725                         case '#':
726                             state = StateEscape.WaitEight;
727                             break;
728                         case '(':
729                             state = StateEscape.WaitG0CharSet;
730                             break;
731                         case ')':
732                             state = StateEscape.WaitG1CharSet;
733                             break;
734                         case '>':
735                             /*Set numeric keypad mode*/
736                             state = StateEscape.WaitEscape;
737                             break;
738                         case '=':
739                             /*Set application keypad mode*/
740                             state = StateEscape.WaitEscape;
741                             break;
742                         default:
743                             writefln("UNKNOWN Escape Sequence \"ESC %c\"", prebuf[i]);
744                             state = StateEscape.WaitEscape;
745                             break;
746                     }
747                     break;
748                 case StateEscape.WaitNumberR:
749                     if (r - i < 3) break;
750                     if (prebuf[i..i+2] == "0;")
751                     {
752                         substate = 0;
753                         state = StateEscape.WaitText;
754                         text = "";
755                         i++;
756                     }
757                     else if (prebuf[i..i+2] == "1;")
758                     {
759                         substate = 1;
760                         state = StateEscape.WaitText;
761                         text = "";
762                         i++;
763                     }
764                     else if (prebuf[i..i+2] == "2;")
765                     {
766                         substate = 2;
767                         state = StateEscape.WaitText;
768                         text = "";
769                         i++;
770                     }
771                     else if (prebuf[i..i+2] == "4;")
772                     {
773                         substate = 4;
774                         state = StateEscape.WaitText;
775                         text = "";
776                         i++;
777                     }
778                     else if (prebuf[i..i+3] == "10;")
779                     {
780                         substate = 10;
781                         state = StateEscape.WaitText;
782                         text = "";
783                         i+=2;
784                     }
785                     else if (prebuf[i..i+3] == "11;")
786                     {
787                         substate = 11;
788                         state = StateEscape.WaitText;
789                         text = "";
790                         i+=2;
791                     }
792                     else if (prebuf[i..i+3] == "46;")
793                     {
794                         substate = 46;
795                         state = StateEscape.WaitText;
796                         text = "";
797                         i+=2;
798                     }
799                     else if (prebuf[i..i+3] == "50;")
800                     {
801                         substate = 50;
802                         state = StateEscape.WaitText;
803                         text = "";
804                         i+=2;
805                     }
806                     else
807                     {
808                         writefln("UNKNOWN Escape Sequence \"ESC ] %c%c\"", prebuf[i], prebuf[i+1]);
809                         state = StateEscape.WaitEscape;
810                     }
811                     break;
812                 case StateEscape.WaitText:
813                     if (prebuf[i] == '\x07')
814                     {
815                         switch(substate)
816                         {
817                             case 0:
818                                 /* Set icon name and window title to txt. */
819                                 break;
820                             case 1:
821                                 /* Set icon name to txt. */
822                                 break;
823                             case 2:
824                                 /* Set window title to txt. */
825                                 break;
826                             case 4:
827                                 /* Set ANSI color num to txt. */
828                                 break;
829                             case 10:
830                                 /* Set dynamic text color to txt.. */
831                                 break;
832                             case 11:
833                                 break;
834                             case 46:
835                                 /* Change log file to name */
836                                 break;
837                             case 50:
838                                 /* Set font to fn. */
839                                 break;
840                             default:
841                                 assert(0);
842                         }
843                         state = StateEscape.WaitEscape;
844                     }
845                     else
846                         text ~= prebuf[i];
847                     break;
848                 case StateEscape.WaitCSI:
849                     switch (prebuf[i])
850                     {
851                         case '@':
852                             /*Insert the indicated # of blank characters.*/
853                             ssize_t num = 1;
854                             if (text > "")
855                                 num = to!int(text);
856                             final switch(mode)
857                             {
858                                 case Mode.Working:
859                                     if (max_r+num > buf.length) num = buf.length-max_r;
860                                     for (ssize_t j = max_r-1; j >= buf_r; j--)
861                                         buf[j+num] = buf[j];
862                                     attrs.length += num;
863                                     for (ssize_t j = attrs.length-num-1; j >= attrs_r; j--)
864                                         attrs[j+num] = attrs[j];
865                                     buf[buf_r..buf_r+num] = ' ';
866                                     max_r+=num;
867                                     break;
868                                 case Mode.Alternate:
869                             }
870                             state = StateEscape.WaitEscape;
871                             break;
872                         case 'A':
873                             /*Move cursor up the indicated # of rows.*/
874                             final switch(mode)
875                             {
876                                 case Mode.Working:
877                                     
878                                     ssize_t nl = buf[0..buf_r].lastIndexOf("\n");
879                                     if (nl < 0) nl = 0;
880                                     else nl++;
881 
882                                     ssize_t wl = buf[nl..buf_r].myWalkLength();
883                                     writefln("UP: wl=%d", wl);
884                                     if (wl >= altmode.cols)
885                                     {
886                                         wl -= altmode.cols;
887                                         ssize_t pos = nl;
888                                         for (ssize_t j=0; j < wl; j++)
889                                         {
890                                             if (pos >= buf.length) break;
891                                             pos += buf.mystride(pos);
892                                         }
893 
894                                         writefln("UP: buf_r=%d, pos=%d", buf_r, pos);
895                                         buf_r = pos;
896                                         attrs_r = buf[0..buf_r].myWalkLength();
897                                     }
898                                     else if (nl > 2)
899                                     {
900                                         ssize_t nl2 = buf[0..nl-1].lastIndexOf("\n");
901                                         if (nl2 < 0) nl2 = 0;
902                                         else nl2++;
903 
904                                         ssize_t pos = nl2;
905                                         for (ssize_t j=0; j < wl; j++)
906                                         {
907                                             if (pos >= buf.length) break;
908                                             pos += buf.mystride(pos);
909                                         }
910 
911                                         writefln("UP2: buf_r=%d, pos=%d", buf_r, pos);
912                                         writefln("UP2: buf=%s|%s", buf[0..pos], buf[pos..buf_r]);
913                                         buf_r = pos;
914                                         attrs_r = buf[0..buf_r].myWalkLength();
915                                     }
916 
917                                     break;
918 
919                                 case Mode.Alternate:
920                                     with(altmode)
921                                     {
922                                     }
923                             }
924                             state = StateEscape.WaitEscape;
925                             break;
926                         case 'B':
927                             /*Move cursor down the indicated # of rows.*/
928                             state = StateEscape.WaitEscape;
929                             break;
930                         case 'C':
931                             /*Move cursor right the indicated # of columns.*/
932                             if (text > "")
933                                 numbers ~= to!int(text);
934 
935                             int num = 1;
936                             if (numbers.length > 0) num = numbers[0];
937 
938                             final switch(mode)
939                             {
940                                 case Mode.Working:
941                                     //writefln("buf before Right: %s", buf[buf_r..max_r]);
942                                     while (num > 0)
943                                     {
944                                         if (buf_r >= buf.length) break;
945                                         buf_r += buf.mystride(buf_r);
946                                         if (buf_r > max_r)
947                                         {
948                                             buf[max_r..buf_r] = ' ';
949                                             max_r = buf_r;
950                                         }
951                                         //attrs_r++;
952                                         num--;
953                                     }
954 
955                                     attrs_r = buf[0..buf_r].myWalkLength();
956                                     //writefln("buf after Right: %s", buf[buf_r..max_r]);
957                                     break;
958                                 case Mode.Alternate:
959                                     with(altmode)
960                                     {
961                                         x += num;
962                                         if (x >= cols)
963                                         {
964                                             y++;
965                                             x = 0;
966                                         }
967                                         if (y >= rows)
968                                         {
969                                             y--;
970                                             std.algorithm.mutation.copy(cast(ubyte[])(buffer[cols..rows*cols]), cast(ubyte[])(buffer[0..(rows-1)*cols]));
971                                             std.algorithm.mutation.copy(alt_attrs[cols..rows*cols], alt_attrs[0..(rows-1)*cols]);
972                                         }
973                                     }
974 
975                             }
976                             state = StateEscape.WaitEscape;
977                             break;
978                         case 'D':
979                             /*Move cursor left the indicated # of columns.*/
980                             final switch(mode)
981                             {
982                                 case Mode.Working:
983                                     if (buf_r > 0)
984                                         buf_r -= buf.mystrideBack(buf_r);
985                                     break;
986                                 case Mode.Alternate:
987                                     with(altmode)
988                                     {
989                                         x--;
990                                         if(x < 0) x = 0;
991                                     }
992                             }
993                             state = StateEscape.WaitEscape;
994                             break;
995                         case 'E':
996                             /*Move cursor down the indicated # of rows, to column 1.*/
997                             state = StateEscape.WaitEscape;
998                             break;
999                         case 'F':
1000                             /*Move cursor up the indicated # of rows, to column 1.*/
1001                             state = StateEscape.WaitEscape;
1002                             break;
1003                         case 'G':
1004                             /*Move cursor to indicated column in current row.*/
1005                             ssize_t num = 1;
1006                             if (text > "")
1007                                 num = to!int(text);
1008 
1009                             final switch(mode)
1010                             {
1011                                 case Mode.Working:
1012 
1013                                     if (buf_r > buf.length) buf_r = buf.length;
1014                                     ssize_t newline = buf[0..buf_r].lastIndexOf("\n");
1015                                     if (newline < 0) newline = 0;
1016                                     else newline++;
1017                                     ssize_t wl = buf[newline..buf_r].myWalkLength();
1018 
1019                                     if ( (num-1) < wl%altmode.cols )
1020                                     {
1021                                         while (wl%altmode.cols != (num-1))
1022                                         {
1023                                             if (buf_r > 0)
1024                                             {
1025                                                 buf_r -= buf.mystrideBack(buf_r);
1026                                                 attrs_r--;
1027                                             }
1028                                             wl--;
1029                                         }
1030                                     }
1031                                     else
1032                                     {
1033                                         while (wl%altmode.cols != num-1)
1034                                         {
1035                                             if (buf_r < buf.length)
1036                                             {
1037                                                 if (buf_r >= max_r)
1038                                                     buf[buf_r] = ' ';
1039                                                 buf_r += buf.mystride(buf_r);
1040                                                 attrs_r++;
1041                                             }
1042                                             wl++;
1043                                         }
1044                                     }
1045                                     break;
1046 
1047                                 case Mode.Alternate:
1048                                     with(altmode)
1049                                     {
1050                                         x = num-1;
1051                                     }
1052                                     break;
1053                             }
1054 
1055                             state = StateEscape.WaitEscape;
1056                             break;
1057                         case 'H':
1058                             /*Move cursor to the indicated row, column (origin at 1,1).*/
1059                             if (text > "")
1060                                 numbers ~= to!int(text);
1061                             while (numbers.length < 2) numbers ~= 1;
1062 
1063                             if (numbers[0] < 1) numbers[0]=1;
1064                             if (numbers[0] > altmode.rows) numbers[0]=altmode.rows;
1065                             if (numbers[1] < 1) numbers[1]=1;
1066                             if (numbers[1] > altmode.cols) numbers[1]=altmode.cols;
1067                             final switch(mode)
1068                             {
1069                                 case Mode.Working:
1070                                     if (buf_r > buf.length) buf_r = buf.length;
1071                                     ssize_t newline = buf[0..buf_r].lastIndexOf("\n");
1072                                     if (newline < 0) newline = 0;
1073                                     else newline++;
1074                                     ssize_t wl = buf[newline..buf_r].myWalkLength();
1075 
1076                                     writefln("[H wl = %d", wl);
1077                                     if (numbers[0] > altmode.scroll_to)
1078                                     {
1079                                         do
1080                                         {
1081                                             if (buf_r < buf.length)
1082                                             {
1083                                                 buf_r += buf.mystride(buf_r);
1084                                                 if (buf[buf_r] == char.init) buf[buf_r] = ' ';
1085                                                 attrs_r++;
1086                                             }
1087                                             wl++;
1088                                         }
1089                                         while ( wl%altmode.cols != numbers[1]-1 );
1090                                     }
1091                                     else
1092                                     {
1093                                         while ( wl%altmode.cols != numbers[1]-1 )
1094                                         {
1095                                             if (buf_r > 0)
1096                                             {
1097                                                 buf_r -= buf.mystrideBack(buf_r);
1098                                                 attrs_r--;
1099                                             }
1100                                             wl--;
1101                                         }
1102                                     }
1103                                     if (buf_r > max_r) max_r = buf_r;
1104                                     attrs_r = buf[0..buf_r].myWalkLength();
1105                                     break;
1106 
1107                                 case Mode.Alternate:
1108                                     with(altmode)
1109                                     {
1110                                         y = numbers[0]-1;
1111                                         x = numbers[1]-1;
1112                                         if (y < 0) y = 0;
1113                                         if (y >= rows) y = rows-1;
1114                                         if (x < 0) x = 0;
1115                                         if (x >= cols) y = cols-1;
1116                                     }
1117                                     break;
1118                             }
1119 
1120                             state = StateEscape.WaitEscape;
1121                             break;
1122                         case 'J':
1123                             /*Erase display (default: from cursor to end of display).*/
1124                             /*
1125                                ESC [ 1 J: erase from start to cursor.
1126                                ESC [ 2 J: erase whole display.
1127                                ESC [ 3 J: erase whole display including scroll-back
1128                                prebuffer (since Linux 3.0).
1129                              */
1130                             size_t num = 0;
1131                             if (text != "") num = to!int(text);
1132 
1133                             final switch(mode)
1134                             {
1135                                 case Mode.Working:
1136                                     if (max_r > buf.length) max_r = buf.length;
1137                                     buf[buf_r..max_r] = ' ';
1138                                     attrs[attrs_r..$] = Attr.Black<<4 | Attr.White;
1139                                     break;
1140                                 case Mode.Alternate:
1141                                     with(altmode)
1142                                     {
1143                                         switch (num)
1144                                         {
1145                                             case 0:
1146                                                 buffer[y*cols+x..$] = " "d[0];
1147                                                 alt_attrs[y*cols+x..$] = Attr.Black<<4 | Attr.White;
1148                                                 break;
1149                                             case 1:
1150                                                 buffer[0..y*cols+x] = " "d[0];
1151                                                 alt_attrs[0..y*cols+x] = Attr.Black<<4 | Attr.White;
1152                                                 break;
1153                                             case 2:
1154                                                 buffer[0..$] = " "d[0];
1155                                                 alt_attrs[0..$] = Attr.Black<<4 | Attr.White;
1156                                                 break;
1157                                             case 3:
1158                                                 buffer[0..$] = " "d[0];
1159                                                 alt_attrs[0..$] = Attr.Black<<4 | Attr.White;
1160                                                 break;
1161                                             default:
1162                                                 writefln("UNKNOWN sequence ESC [ %d J", num);
1163                                                 break;
1164                                         }
1165                                     }
1166                                     break;
1167                             }
1168                             state = StateEscape.WaitEscape;
1169                             break;
1170                         case 'K':
1171                             /*Erase line (default: from cursor to end of line).*/
1172                             /*
1173                                ESC [ 1 K: erase from start of line to cursor.
1174                                ESC [ 2 K: erase whole line.
1175                              */
1176                             final switch(mode)
1177                             {
1178                                 case Mode.Working:
1179                                     if (max_r > buf.length) max_r = buf.length;
1180                                     if (max_r <= buf_r) max_r = buf_r+1;
1181                                     buf[buf_r..max_r] = ' ';
1182                                     break;
1183                                 case Mode.Alternate:
1184                                     with(altmode)
1185                                     {
1186                                         buffer[y*cols+x..y*cols+cols] = " "d[0];
1187                                         alt_attrs[y*cols+x..y*cols+cols] = Attr.Black<<4 | Attr.White;
1188                                     }
1189                                     break;
1190                             }
1191                             state = StateEscape.WaitEscape;
1192                             break;
1193                         case 'L':
1194                             /*Insert the indicated # of blank lines.*/
1195                             size_t num = 1;
1196                             if (text != "") num = to!int(text);
1197 
1198                             final switch(mode)
1199                             {
1200                                 case Mode.Working:
1201                                     break;
1202                                 case Mode.Alternate:
1203                                     with(altmode)
1204                                     {
1205                                             for (ssize_t R = scroll_to-num-1; R >= y; R--)
1206                                             {
1207                                                 buffer[(R+num)*cols..(R+num+1)*cols] = buffer[R*cols..(R+1)*cols];
1208                                                 alt_attrs[(R+num)*cols..(R+num+1)*cols] = alt_attrs[R*cols..(R+1)*cols];
1209                                             }
1210                                             buffer[y*cols..(y+num)*cols] = " "d[0];
1211                                             alt_attrs[y*cols..(y+num)*cols] = 0;
1212                                     }
1213                             }
1214 
1215                             state = StateEscape.WaitEscape;
1216                             break;
1217                         case 'M':
1218                             /*Delete the indicated # of lines.*/
1219                             size_t num = 1;
1220                             if (text != "") num = to!int(text);
1221 
1222                             final switch(mode)
1223                             {
1224                                 case Mode.Working:
1225                                     break;
1226                                 case Mode.Alternate:
1227                                     with(altmode)
1228                                     {
1229                                             std.algorithm.mutation.copy(cast(ubyte[])(buffer[(y+num)*cols..scroll_to*cols]), 
1230                                                     cast(ubyte[])(buffer[y*cols..(scroll_to-num)*cols]));
1231                                             std.algorithm.mutation.copy(alt_attrs[(y+num)*cols..scroll_to*cols], 
1232                                                     alt_attrs[y*cols..(scroll_to-num)*cols]);
1233                                             buffer[(scroll_to-num)*cols..scroll_to*cols] = " "d[0];
1234                                             alt_attrs[(scroll_to-num)*cols..scroll_to*cols] = 0;
1235                                     }
1236                             }
1237 
1238                             state = StateEscape.WaitEscape;
1239                             break;
1240                         case 'P':
1241                             /*Delete the indicated # of characters on current line.*/
1242                             size_t num = 1;
1243                             if (text != "") num = to!int(text);
1244 
1245                             final switch(mode)
1246                             {
1247                                 case Mode.Working:
1248                                     //writefln("before buf='%s'", buf[buf_r..max_r]);
1249                                     size_t bytes = 0;
1250                                     for (auto j=0; j < num; j++)
1251                                     {
1252                                         bytes += buf.mystride(buf_r+bytes);
1253                                     }
1254 
1255                                     ssize_t newline = buf[0..buf_r].lastIndexOf("\n");
1256                                     if (newline < 0) newline = 0;
1257                                     else newline++;
1258                                     ssize_t wl = buf[newline..buf_r].myWalkLength();
1259 
1260                                     ssize_t eol = buf_r;
1261                                     ssize_t eol_attrs = attrs_r;
1262                                     do
1263                                     {
1264                                         if (eol >= max_r) break;
1265                                         eol += buf.mystride(eol);
1266                                         wl++;
1267                                         eol_attrs++;
1268                                     }
1269                                     while (wl%altmode.cols != 0);
1270 
1271                                     bool eob, eoa;
1272                                     if (eol > max_r) 
1273                                     {
1274                                         eob = true;
1275                                         eol = max_r;
1276                                     }
1277                                     if (eol_attrs > attrs.length)
1278                                     {
1279                                         eoa = true;
1280                                         eol_attrs = attrs.length;
1281                                     }
1282 
1283                                     if (buf_r+bytes > eol) bytes = eol - buf_r;
1284                                     if (attrs_r+num > eol_attrs) num = eol_attrs - attrs_r;
1285                                     if (buf_r+bytes != eol)
1286                                         std.algorithm.mutation.copy(cast(ubyte[])(buf[buf_r+bytes..eol]), cast(ubyte[])(buf[buf_r..eol-bytes]));
1287                                     if (attrs_r+num != eol_attrs)
1288                                         std.algorithm.mutation.copy(attrs[attrs_r+num..eol_attrs], attrs[attrs_r..eol_attrs-num]);
1289 
1290                                     buf[eol-bytes..eol] = ' ';
1291                                     attrs[eol_attrs-num..eol_attrs] = Attr.Black<<4 | Attr.White;
1292 
1293                                     if (eob)
1294                                         max_r -= bytes;
1295                                     if (eoa)
1296                                         attrs.length -= num;
1297                                     //writefln("buf='%s'", buf[buf_r..max_r]);
1298                                     break;
1299                                 case Mode.Alternate:
1300                                     with(altmode)
1301                                     {
1302                                         std.algorithm.mutation.copy(cast(ubyte[])(buffer[y*cols+x+num..y*cols+cols]), cast(ubyte[])(buffer[y*cols+x..y*cols+cols-num]));
1303                                         std.algorithm.mutation.copy(alt_attrs[y*cols+x+num..y*cols+cols], alt_attrs[y*cols+x..y*cols+cols-num]);
1304 
1305                                         buffer[y*cols+cols-num..y*cols+cols] = " "d[0];
1306                                         alt_attrs[y*cols+cols-num..y*cols+cols] = Attr.Black<<4 | Attr.White;
1307                                     }
1308                             }
1309 
1310                             state = StateEscape.WaitEscape;
1311                             break;
1312                         case 'X':
1313                             /*Erase the indicated # of characters on current line.*/
1314                             state = StateEscape.WaitEscape;
1315                             break;
1316                         case 'a':
1317                             /*Move cursor right the indicated # of columns.*/
1318                             state = StateEscape.WaitEscape;
1319                             break;
1320                         case 'c':
1321                             /*Answer ESC [ ? 6 c: "I am a VT102".*/
1322                             state = StateEscape.WaitEscape;
1323                             break;
1324                         case 'd':
1325                             /*Move cursor to the indicated row, current column.*/
1326                             ssize_t num = 1;
1327                             if (text > "")
1328                                 num = to!int(text);
1329 
1330                             final switch(mode)
1331                             {
1332                                 case Mode.Working:
1333                                     break;
1334                                 case Mode.Alternate:
1335                                     with(altmode)
1336                                     {
1337                                         y = num-1;
1338                                     }
1339                             }
1340                             state = StateEscape.WaitEscape;
1341                             break;
1342                         case 'e':
1343                             /*Move cursor down the indicated # of rows.*/
1344                             state = StateEscape.WaitEscape;
1345                             break;
1346                         case 'f':
1347                             /*Move cursor to the indicated row, column.*/
1348                             goto case 'H';
1349                         case 'g':
1350                             /*Without parameter: clear tab stop at current position.*/
1351                             /* ESC [ 3 g: delete all tab stops. */
1352                             state = StateEscape.WaitEscape;
1353                             break;
1354                         case 'h':
1355                             /*Set Mode.*/
1356                             /*More info on modes: https://conemu.github.io/blog/2015/11/09/Build-151109.html*/
1357                             /*http://www.vt100.net/docs/vt510-rm/chapter4.html*/
1358                             if (text > "")
1359                                 numbers ~= to!int(text);
1360 
1361                             if (numbers.length == 1)
1362                             {
1363                                 switch(numbers[0])
1364                                 {
1365                                     case 1:
1366                                         /*Turn on application cursor mode*/
1367                                         altmode.cursor_mode = true;
1368                                         break;
1369                                     case 4:
1370                                         /*Turn on insert mode*/
1371                                         altmode.insert_mode = true;
1372                                         break;
1373                                     case 25:
1374                                         /*Turn on cursor*/
1375                                         break;
1376                                     case 12:
1377                                         /*Turn off local echo*/
1378                                         break;
1379                                     case 1049:
1380                                         /* Save cursor position and activate xterm alternative buffer (no backscroll) */
1381                                         mode = Mode.Alternate;
1382                                         altmode.reset();
1383                                         altmode.buffer.length = altmode.cols*altmode.rows;
1384                                         altmode.alt_attrs.length = altmode.cols*altmode.rows;
1385                                         altmode.buffer[0..$] = " "d[0];
1386                                         altmode.scroll_from = 0;
1387                                         altmode.scroll_to = altmode.rows;
1388                                         break;
1389                                     default:
1390                                         writefln("Unknown mode %d", numbers[0]);
1391                                 }
1392                             }
1393 
1394                             state = StateEscape.WaitEscape;
1395                             break;
1396                         case 'l':
1397                             /*Reset Mode (see below).*/
1398                             if (text > "")
1399                                 numbers ~= to!int(text);
1400 
1401                             if (numbers.length == 1)
1402                             {
1403                                 switch(numbers[0])
1404                                 {
1405                                     case 1:
1406                                         /*Turn off application cursor mode*/
1407                                         altmode.cursor_mode = false;
1408                                         break;
1409                                     case 4:
1410                                         /*Turn off insert mode*/
1411                                         altmode.insert_mode = false;
1412                                         break;
1413                                     case 25:
1414                                         /*Turn off cursor*/
1415                                         break;
1416                                     case 12:
1417                                         /*Turn on local echo*/
1418                                         break;
1419                                     case 1049:
1420                                         mode = Mode.Working;
1421                                         break;
1422                                     default:
1423                                         writefln("Unknown mode %d", numbers[0]);
1424                                 }
1425                             }
1426 
1427                             state = StateEscape.WaitEscape;
1428                             break;
1429                         case 'm':
1430                             /*Set attributes (see below).*/
1431                             if (text > "")
1432                                 numbers ~= to!int(text);
1433 
1434                             if (numbers.length == 0) numbers ~= 0;
1435 
1436                             ushort *pcur_attr;
1437                             final switch(mode)
1438                             {
1439                                 case Mode.Working:
1440                                     pcur_attr = &cur_attr;
1441                                     break;
1442                                 case Mode.Alternate:
1443                                     with(altmode)
1444                                     {
1445                                         pcur_attr = &alt_cur_attr;
1446                                     }
1447                             }
1448 
1449                         numbers_loop:
1450                             foreach(ni, n; numbers)
1451                             {
1452                                 /* About mode: https://ru.wikipedia.org/wiki/%D0%A3%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D1%8F%D1%8E%D1%89%D0%B8%D0%B5_%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D0%B8_ANSI */
1453                                 switch (n)
1454                                 {
1455                                     case 0:
1456                                         *pcur_attr = Attr.Black<<4 | Attr.White;
1457                                         break;
1458                                     case 1:
1459                                         *pcur_attr |= Attr.Bold;
1460                                         break;
1461                                     case 2:
1462                                         *pcur_attr |= Attr.HalfBright;
1463                                         break;
1464                                     case 4:
1465                                         *pcur_attr |= Attr.Underscore;
1466                                         break;
1467                                     case 5:
1468                                         *pcur_attr |= Attr.Blink;
1469                                         break;
1470                                     case 7:
1471                                         *pcur_attr = *pcur_attr & 0xFF00 | ((*pcur_attr & 0xF0) >> 4) | ((*pcur_attr & 0x0F) << 4);
1472                                         break;
1473                                     case 10:
1474                                         break;
1475                                     case 11:
1476                                         break;
1477                                     case 12:
1478                                         break;
1479                                     case 21:
1480                                         *pcur_attr &= ~Attr.HalfBright;
1481                                         break;
1482                                     case 22:
1483                                         *pcur_attr &= ~Attr.HalfBright;
1484                                         break;
1485                                     case 23:
1486                                         /* Не курсивный, не фрактура */
1487                                         break;
1488                                     case 24:
1489                                         *pcur_attr &= ~Attr.Underscore;
1490                                         break;
1491                                     case 25:
1492                                         *pcur_attr &= ~Attr.Blink;
1493                                         break;
1494                                     case 27:
1495                                         *pcur_attr = *pcur_attr & 0xFF00 | ((*pcur_attr & 0xF0) >> 4) | ((*pcur_attr & 0x0F) << 4);
1496                                         break;
1497                                     case 30:..case 37:
1498                                         *pcur_attr = *pcur_attr & 0xFFF0 | ((n-30)&0xF);
1499                                         break;
1500                                     case 38:
1501                                         //*pcur_attr = *pcur_attr & 0xFFF0 | Attr.White | Attr.Underscore;
1502                                         if (numbers[ni+1] == 5)
1503                                         {
1504                                             if  (numbers[ni+2]==32) numbers[ni+2]=1;
1505                                             *pcur_attr = *pcur_attr & 0xFFF0 | (numbers[ni+2] & 0xF);
1506                                             break numbers_loop;
1507                                         }
1508                                         break;
1509                                     case 39:
1510                                         *pcur_attr = (*pcur_attr & 0xFFF0 | Attr.White) & ~Attr.Underscore;
1511                                         break;
1512                                     case 40:..case 47:
1513                                         *pcur_attr = *pcur_attr & 0xFF0F | (((n-40)&0xF) << 4);
1514                                         break;
1515                                     case 48:
1516                                         if (numbers[ni+1] == 5)
1517                                         {
1518                                             *pcur_attr = *pcur_attr & 0xFF0F | ((numbers[ni+2] & 0xF) << 4);
1519                                             break numbers_loop;
1520                                         }
1521                                         break;
1522                                     case 49:
1523                                         *pcur_attr = *pcur_attr & 0xFF0F | (Attr.Black << 4);
1524                                         break;
1525                                     default:
1526                                         writefln("Unknown ECMA-48 SGR sequence %d", n);
1527                                         break;
1528                                 }
1529                             }
1530 
1531                             state = StateEscape.WaitEscape;
1532                             break;
1533                         case 'n':
1534                             /*Status report (see below).*/
1535                             if (text > "") 
1536                                 numbers ~= to!int(text);
1537 
1538                             final switch(mode)
1539                             {
1540                                 case Mode.Working:
1541                                     break;
1542                                 case Mode.Alternate:
1543                                     with(altmode)
1544                                     {
1545                                         switch (numbers[0])
1546                                         {
1547                                             case 5:
1548                                                 altmode.control_input ~= "\x1B[0n";
1549                                                 break;
1550                                             case 6:
1551                                                 altmode.control_input ~= format("\x1B[%d;%dR", y+1, x+1);
1552                                                 break;
1553                                             default:
1554                                                 writefln("UNKNOWN sequence ESC [ %d n", numbers[0]);
1555                                         }
1556                                     }
1557                             }
1558 
1559                             state = StateEscape.WaitEscape;
1560                             break;
1561                         case 'q':
1562                             /*Set keyboard LEDs.*/
1563                             /*ESC [ 0 q: clear all LEDs
1564                               ESC [ 1 q: set Scroll Lock LED
1565                               ESC [ 2 q: set Num Lock LED
1566                               ESC [ 3 q: set Caps Lock LED*/
1567                             state = StateEscape.WaitEscape;
1568                             break;
1569                         case 'r':
1570                             /*Set scrolling region; parameters are top and bottom row.*/
1571                             if (text > "") 
1572                                 numbers ~= to!int(text);
1573 
1574                             if (numbers.length == 0) numbers ~= 1;
1575                             if (numbers.length == 1) numbers ~= altmode.rows;
1576 
1577                             final switch(mode)
1578                             {
1579                                 case Mode.Working:
1580                                     with(altmode)
1581                                     {
1582                                         scroll_from = numbers[0]-1;
1583                                         scroll_to = numbers[1];
1584                                     }
1585                                     break;
1586                                 case Mode.Alternate:
1587                                     with(altmode)
1588                                     {
1589                                         scroll_from = numbers[0]-1;
1590                                         scroll_to = numbers[1];
1591                                     }
1592                             }
1593 
1594                             state = StateEscape.WaitEscape;
1595                             break;
1596                         case 's':
1597                             /*Save cursor location.*/
1598                             state = StateEscape.WaitEscape;
1599                             break;
1600                         case 'u':
1601                             /*Restore cursor location.*/
1602                             state = StateEscape.WaitEscape;
1603                             break;
1604                         case '`':
1605                             /*Move cursor to indicated column in current row.*/
1606                             state = StateEscape.WaitEscape;
1607                             break;
1608                         case '>':
1609                             if (prebuf[i..i+1] == "c" || prebuf[i..i+2] == "0c")
1610                             {
1611                                 /*Secondary Device Attributes*/
1612                                 altmode.control_input ~= "\x1B[>85;95;0c";
1613                             }
1614                             break;
1615                         case '?':
1616                             break;
1617                         case '0':.. case '9':
1618                             text ~= prebuf[i];
1619                             break;
1620                         case ';':
1621                             if (text == "") numbers ~= 0;
1622                             else numbers ~= to!int(text);
1623                             text = "";
1624                             break;
1625                         default:
1626                             writefln("UNKNOWN Escape Sequence \"ESC [ %c\"", prebuf[i]);
1627                             state = StateEscape.WaitEscape;
1628                             break;
1629                     }
1630                     break;
1631                 case StateEscape.WaitCharSet:
1632                     switch (prebuf[i])
1633                     {
1634                         case '@':
1635                             /*Select default (ISO 646 / ISO 8859-1)*/
1636                             break;
1637                         case 'G':
1638                             /*Select UTF-8*/
1639                             break;
1640                         case '8':
1641                             /*Select UTF-8 (obsolete)*/
1642                             break;
1643                         default:
1644                             writefln("UNKNOWN Escape Sequence \"ESC %% %c\"", prebuf[i]);
1645                             break;
1646                     }
1647                     state = StateEscape.WaitEscape;
1648                     break;
1649                 case StateEscape.WaitEight:
1650                     switch (prebuf[i])
1651                     {
1652                         case '8':
1653                             /*DEC screen alignment test - fill screen with E's.*/
1654                             break;
1655                         default:
1656                             writefln("UNKNOWN Escape Sequence \"ESC # %c\"", prebuf[i]);
1657                             break;
1658                     }
1659                     state = StateEscape.WaitEscape;
1660                     break;
1661                 case StateEscape.WaitG0CharSet:
1662                     switch (prebuf[i])
1663                     {
1664                         case 'B':
1665                             /*Select default (ISO 8859-1 mapping)*/
1666                             break;
1667                         case '0':
1668                             /*Select VT100 graphics mapping*/
1669                             break;
1670                         case 'U':
1671                             /*Select null mapping - straight to character ROM*/
1672                             break;
1673                         case 'K':
1674                             /*Select user mapping - the map that is loaded by
1675                               the utility mapscrn(8).*/
1676                             break;
1677                         default:
1678                             writefln("UNKNOWN Escape Sequence \"ESC # %c\"", prebuf[i]);
1679                             break;
1680                     }
1681                     state = StateEscape.WaitEscape;
1682                     break;
1683                 case StateEscape.WaitG1CharSet:
1684                     switch (prebuf[i])
1685                     {
1686                         case 'B':
1687                             /*Select default (ISO 8859-1 mapping)*/
1688                             break;
1689                         case '0':
1690                             /*Select VT100 graphics mapping*/
1691                             break;
1692                         case 'U':
1693                             /*Select null mapping - straight to character ROM*/
1694                             break;
1695                         case 'K':
1696                             /*Select user mapping - the map that is loaded by
1697                               the utility mapscrn(8).*/
1698                                 break;
1699                         default:
1700                             writefln("UNKNOWN Escape Sequence \"ESC # %c\"", prebuf[i]);
1701                             break;
1702                     }
1703                     state = StateEscape.WaitEscape;
1704                     break;
1705             }
1706         }
1707 
1708         if (i < r)
1709         {
1710             std.algorithm.mutation.copy(cast(ubyte[])(prebuf[i..r]), cast(ubyte[])(prebuf[0..r-i]));
1711             prebuf_r = r-i;
1712         }
1713         else
1714             prebuf_r = 0;
1715 
1716         return max_r;
1717     }
1718 }
1719 
1720 private int
1721 process_input(CMDGlobalState cgs, string cwd, ulong new_id, 
1722         ref ulong out_id, ref Mode mode,
1723         ref WorkMode workmode, ref AltMode altmode, 
1724         int fd, OutPipe pipe)
1725 {
1726 version(Posix)
1727 {
1728     with(workmode)
1729     {
1730         //writefln("max_r=%d", max_r);
1731         ssize_t r;
1732         //writefln("buf.length-buf_r = %d", buf.length-buf_r);
1733         auto r1 = read(fd, prebuf[prebuf_r..$].ptr, buf.length-prebuf_r-buf_r-16);
1734         auto buf_length_was = buf.length-buf_r;
1735         bool buffer_full = (r1 >= buf.length-prebuf_r-buf_r-16);
1736 
1737         if(buf.length < buf_r) buf_r = buf.length-1;
1738 
1739         if (r1 > 0)
1740         {
1741             r1 += prebuf_r;
1742             //writefln("READED: %s", prebuf[0..r1]);
1743             //writefln("r1=%d, buf.length-buf_r=%d", r1, buf.length-buf_r);
1744 
1745             //writefln("read r=%d", r);
1746             buf[max_r..$] = ' ';
1747             r = process_escape_sequences(cgs, mode, workmode, altmode, r1);
1748 
1749             //writefln("PROCESSED: r=%d buf=%s", r, buf[0..r]);
1750 
1751             //writefln("r=%d buf=%s", r, buf[0..r]);
1752             //r = read(fd, buf[buf_r..$].ptr, buf[buf_r..$].length);
1753             Dbt key, data;
1754             string ks;
1755             final switch(mode)
1756             {
1757                 case Mode.Working:
1758                     if (out_id1 == 0)
1759                     {
1760                         out_id++;
1761                         out_id1 = out_id;
1762                     }
1763                     //writefln("WRITED: out_id1 = %s", out_id1);
1764                     //writefln("OUT: new_id=%s out_id=%s", new_id, out_id1);
1765                     ks = get_key_for_command_out(command_out_key(cwd, new_id, out_id1));
1766                     key = ks;
1767 
1768                     ssize_t split_r;
1769                     {
1770                         ssize_t sym = buf[0..r].lastIndexOf("\n");
1771                         if (sym >= 0) 
1772                         {
1773                             //writefln("Split_r by newline");
1774                             split_r = 0+sym+1;
1775                         }
1776                         else if (r1 < buf_length_was) split_r = r;
1777                         else
1778                         {
1779                             sym = buf[r/2..r].lastIndexOf(".");
1780                             if (sym >= 0) split_r = r/2+sym+1;
1781                             else
1782                             {
1783                                 sym = buf[r/2..r].lastIndexOf(" ");
1784                                 if (sym >= 0) split_r = r/2+sym+1;
1785                                 else
1786                                 {
1787                                     for (auto i = r-1; i >= 0; i--)
1788                                     {
1789                                         if ((buf[i] & 0b1000_0000) == 0 ||
1790                                                 (buf[i] & 0b1100_0000) == 0b1100_0000)
1791                                         {
1792                                             split_r = i;
1793                                             break;
1794                                         }
1795                                     }
1796                                     //writefln("Split_r by symbol");
1797                                 }
1798                             }
1799                         }
1800                     }
1801 
1802                     ssize_t count_n;
1803                     ssize_t i;
1804                     ssize_t a;
1805                     ssize_t from = 0;
1806                     ssize_t from_a = 0;
1807                     for (i=0; i < split_r; i+=buf.mystride(i))
1808                     {
1809                         char[] chr = buf[i..i + buf.mystride(i)];
1810                         if (chr == "\n")
1811                         {
1812                             count_n++;
1813                             if (select)
1814                             {
1815                                 string line = buf[from..i].idup();
1816 
1817                                 //writefln("line: %s", line);
1818                                 if ( exists(line) )
1819                                 {
1820                                     line = buildNormalizedPath(absolutePath(expandTilde(line)));
1821                                     tid.send("select", line);
1822                                 }
1823                                 else
1824                                 {
1825                                     writefln("doesn't exist");
1826                                 }
1827 
1828                                 from = i+1;
1829                                 from_a = a+1;
1830                                 count_n = 0;
1831                             }
1832                             else
1833                             {
1834                                 if (count_n >= 100)
1835                                 {
1836                                 //writefln("buf = %s", buf[from..i+1]);
1837                                     string ds = get_data_for_command_out(
1838                                             command_out_data(Clock.currTime().stdTime(), pipe, 
1839                                                 buf_r,
1840                                                 buf[from..i+1].idup(),
1841                                                 attrs[from_a..a+1]));
1842                                     data = ds;
1843                                     auto res = command_output_put(cgs, &key, &data);
1844                                     if (res != 0)
1845                                     {
1846                                         throw new Exception("DB command out not written");
1847                                     }
1848                                     cgs.OIT++;
1849                                     out_id1 = 0;
1850 
1851                                     if (out_id1 == 0)
1852                                     {
1853                                         out_id++;
1854                                         out_id1 = out_id;
1855                                     }
1856                                     ks = get_key_for_command_out(command_out_key(cwd, new_id, out_id1));
1857                                     key = ks;
1858                                     from = i+1;
1859                                     from_a = a+1;
1860                                     count_n = 0;
1861                                 }
1862                             }
1863                         }
1864                         a++;
1865                     }
1866 
1867                     ssize_t attrs_split_r;
1868                     if (select)
1869                     {
1870                         if (from < split_r) split_r = from-1;
1871                     }
1872                     else
1873                     {
1874                         if (i < split_r) split_r = i;
1875 
1876                         attrs_split_r = buf[0..split_r].myWalkLength();
1877                         if (attrs_split_r > attrs.length) attrs.length = attrs_split_r+1;
1878 
1879                         //writefln("Write to DB split_r=%d buf=%s, pos = %s, cmd_out=%s", split_r, buf[from..split_r], split_r > buf_r ? buf_r : split_r, out_id1);
1880                         //writefln("attrs=%s", attrs[0..attrs_split_r]);
1881                         string ds = get_data_for_command_out(
1882                                 command_out_data(Clock.currTime().stdTime(), pipe, 
1883                                     split_r > buf_r ? buf_r : split_r,
1884                                     buf[from..split_r].idup(),
1885                                     attrs[from_a..attrs_split_r]));
1886                         data = ds;
1887                         auto res = command_output_put(cgs, &key, &data);
1888                         if (res != 0)
1889                         {
1890                             throw new Exception("DB command out not written");
1891                         }
1892                         cgs.OIT++;
1893 
1894                         if (!buffer_full)
1895                         {
1896                             cgs.commit();
1897                             cgs.recommit();
1898                         }
1899                     }
1900 
1901                     //writefln("r=%d, split_r=%d, buf_r=%d", r, split_r, buf_r);
1902                     /*writefln("%s: buf=%s r=%s", pipe, buf[0..split_r], split_r);
1903                     if (pipe == OutPipe.STDERR)
1904                     {
1905                         for (auto i=0; i < split_r; i++)
1906                         {
1907                             writefln("char=%c code=%X (%d)", buf[i], buf[i], buf[i]);
1908                         }
1909                     }*/
1910                     /*if (split_r < r)
1911                     {
1912                         ssize_t a = ((split_r-50 >= 0) ? split_r-50 : 0);
1913                         ssize_t b = ((split_r+50 < r) ? split_r+50 : r);
1914                         writefln("OUT: split \"%s\"~\"%s\" r=%s", 
1915                                 buf[a..split_r], 
1916                                 buf[split_r..b], 
1917                                 split_r);
1918                     }*/
1919                     bool no_n = false;
1920                     if (split_r < r)
1921                     {
1922                         std.algorithm.mutation.copy(cast(ubyte[])(buf[split_r..r]), cast(ubyte[])(buf[0..r - split_r]));
1923                         max_r = r - split_r;
1924 
1925                         std.algorithm.mutation.copy(attrs[attrs_split_r..$], attrs[0..$ - attrs_split_r]);
1926                         attrs.length -= attrs_split_r;
1927 
1928                         if (split_r > buf_r)
1929                         {
1930                             buf_r = 0;
1931                             attrs_r = 0;
1932                         }
1933                         else
1934                         {
1935                             buf_r -= split_r;
1936                             attrs_r -= attrs_split_r;
1937                         }
1938                         /*ssize_t c = ((50 < r - split_r) ? 50 : r - split_r);
1939                         writefln("buf: \"%s\"", buf[0..c]);*/
1940                     }
1941                     else
1942                     {
1943                         if (r < buf.length/2)
1944                         {
1945                             if (r > 0 && buf[r-1] != '\n')
1946                             {
1947                                 no_n = true;
1948 
1949                                 //writefln("%s: Not ended with \\n line found: %s", pipe, buf[0..split_r]);
1950                             }
1951                         }
1952 
1953                         if (!no_n)
1954                         {
1955                             buf_r = 0;
1956                             max_r = 0;
1957                             attrs_r = 0;
1958                             attrs.length = 0;
1959                         }
1960                     }
1961                     if (!no_n)
1962                     {
1963                             //writefln("%s: Ended with \\n line found: %s", pipe, buf[0..split_r]);
1964                         out_id1 = 0;
1965                     }
1966                     //writefln("split_r = %d, r = %d, max_r = %d, buf_r=%d", split_r, r, max_r, buf_r);
1967                     if (split_r < r && max_r > 0 && !select)
1968                     {
1969                         if (out_id1 == 0)
1970                         {
1971                             out_id++;
1972                             out_id1 = out_id;
1973                         }
1974                         ks = get_key_for_command_out(command_out_key(cwd, new_id, out_id1));
1975                         key = ks;
1976                     //writefln("REST buf = %s", buf[0..max_r]);
1977                         string ds = get_data_for_command_out(
1978                                 command_out_data(Clock.currTime().stdTime(), pipe, 
1979                                     buf_r,
1980                                     buf[0..max_r].idup(),
1981                                     attrs[0..$]));
1982                         data = ds;
1983                         auto res = command_output_put(cgs, &key, &data);
1984                         if (res != 0)
1985                         {
1986                             throw new Exception("DB command out not written");
1987                         }
1988                         cgs.OIT++;
1989 
1990                         if (!buffer_full)
1991                         {
1992                             cgs.commit();
1993                             cgs.recommit();
1994                         }
1995                     }
1996                     break;
1997 
1998                 case Mode.Alternate:
1999                     with(altmode)
2000                     {
2001                         if (alt_out_id1 == 0)
2002                         {
2003                             alt_out_id1 = out_id+1;
2004                         }
2005                         ks = get_key_for_command_out(command_out_key(cwd, new_id, alt_out_id1));
2006                         key = ks;
2007 
2008                         string ds = get_data_for_command_out(
2009                                 command_out_data(Clock.currTime().stdTime(), pipe, 
2010                                     y*cols+x, cols, rows,
2011                                     buffer,
2012                                     alt_attrs));
2013                         data = ds;
2014                         auto res = command_output_put(cgs, &key, &data);
2015                         if (res != 0)
2016                         {
2017                             throw new Exception("DB command out not written");
2018                         }
2019                         cgs.OIT++;
2020 
2021                         if (!buffer_full)
2022                         {
2023                             cgs.commit();
2024                             cgs.recommit();
2025                         }
2026                     }
2027                     break;
2028             }
2029         }
2030         if (r1 < 0 && errno != EWOULDBLOCK)
2031         {
2032             //throw new Exception("read() error: " ~ fromStringz(strerror(errno)).idup());
2033             return 1;
2034         }
2035         if (r1 == 0) return 1;
2036         return 0;
2037     }
2038 }
2039 else
2040 version (Windows)
2041 {
2042 	return 0;
2043 }
2044 }
2045 
2046 private int
2047 fork_command(CMDGlobalState cgs, string cwd, string full_cwd, string command, 
2048         winsize ws, immutable string[] selection, Tid tid)
2049 {
2050 version (Posix)
2051 {
2052     cgs.recommit();
2053 
2054 /*db_commands
2055 cwd, id
2056     command, start, end, status*/
2057 
2058     ulong id = get_max_id_of_cwd(cgs, cwd);
2059     ulong new_id = id+1;
2060     if (id > 0)
2061     {
2062         new_id = id + 1000 - id%1000;
2063         //writefln("last_id=%s (%%1000=%s), new_id=%s", id, id%1000, new_id);
2064     }
2065 
2066     if (command[0] != '*')
2067     {
2068         ulong replace_id = find_command_in_cwd(cgs, cwd, command);
2069 
2070         Dbt key2, data2;
2071         string ks2 = get_key_for_command(command_key(cwd, replace_id));
2072         key2 = ks2;
2073 
2074         auto res = cgs.db_commands.get(cgs.txn, &key2, &data2);
2075         if (res == 0)
2076         {
2077             string data2_string = data2.to!(string);
2078             command_data cmd_data2;
2079             parse_data_for_command(data2_string, cmd_data2);
2080             if (cmd_data2.end > 0)
2081             {
2082                 delete_command_out(cgs, cwd, replace_id);
2083 
2084                 cgs.commit;
2085                 cgs.recommit;
2086 
2087                 string ks = get_key_for_command(command_key(cwd, replace_id));
2088                 Dbt key = ks;
2089                 res = cgs.db_commands.del(cgs.txn, &key);
2090 
2091                 cgs.commit;
2092                 cgs.recommit;
2093             }
2094         }
2095     }
2096 
2097     tid.send(thisTid, "command_id", new_id);
2098 
2099     cgs.commit();
2100     cgs.recommit();
2101 
2102     Dbt key, data;
2103     string ks = get_key_for_command(command_key(cwd, new_id));
2104     key = ks;
2105     command_data cmd_data = command_data(command, 0, 0, -1);
2106     string ds = get_data_for_command(cmd_data);
2107     data = ds;
2108 
2109     auto res = cgs.db_commands.put(cgs.txn, &key, &data);
2110     if (res != 0)
2111     {
2112         throw new Exception("DB command not written");
2113     }
2114 
2115     if (command[0] == '+' && id > 0)
2116     {
2117         Dbt key2, data2;
2118         string ks2 = get_key_for_command(command_key(cwd, id));
2119         key2 = ks2;
2120 
2121         do
2122         {
2123             res = cgs.db_commands.get(cgs.txn, &key2, &data2);
2124             if (res == 0)
2125             {
2126                 string data2_string = data2.to!(string);
2127                 command_data cmd_data2;
2128                 parse_data_for_command(data2_string, cmd_data2);
2129                 if (cmd_data2.end > 0)
2130                 {
2131                     if (cmd_data2.status != 0)
2132                     {
2133                         cmd_data.end = Clock.currTime().stdTime();
2134                         ds = get_data_for_command(cmd_data);
2135                         data = ds;
2136 
2137                         res = cgs.db_commands.put(cgs.txn, &key, &data);
2138                         if (res != 0)
2139                         {
2140                             throw new Exception("DB command not written");
2141                         }
2142                         cgs.OIT++;
2143 
2144                         return cmd_data2.status;
2145                     }
2146                     else
2147                     {
2148                         break;
2149                     }
2150                 }
2151             }
2152             else
2153             {
2154                 writefln("Command with id %d not found", id);
2155                 return -1;
2156             }
2157 
2158             cgs.commit;
2159             Thread.sleep( 200.msecs() );
2160             cgs.recommit;
2161         }
2162         while (true);
2163     }
2164 
2165     if (command[0] == '*' || command[0] == '+')
2166     {
2167         command = command[1..$];
2168     }
2169 
2170     if (!full_cwd.isDir()) full_cwd = full_cwd[0..cwd.lastIndexOf("/")];
2171     chdir(full_cwd);
2172 
2173     bool select_files;
2174     version(WithoutTTY)
2175     {
2176         auto cmd_pipes = pipeProcess(["bash", "-c", command], Redirect.stdout | Redirect.stderr | Redirect.stdin);
2177         auto pid = cmd_pipes.pid;
2178 
2179         auto fdstdout = cmd_pipes.stdout.fileno;
2180         auto fdstderr = cmd_pipes.stderr.fileno;
2181         auto fdstdin  = cmd_pipes.stdin.fileno;
2182         auto fdmax = max(fdstdout, fdstderr, fdstdin);
2183     }
2184     else
2185     {
2186         if (!access("/bin/bash".toStringz(), X_OK) == 0)
2187             throw new Exception(text("Not an executable file: ", "/bin/bash"));
2188 
2189         int master;
2190 
2191         auto stderrPipe = pipe();
2192 
2193         if (command.indexOf("${SELECTED[@]}") >= 0 && selection.length > 0)
2194         {
2195             environment["SELECTED_FILES"] = join(selection, "\n");
2196             command = q"{declare -a SELECTED
2197 OLD_IFS="$IFS"
2198 IFS=$'\n'
2199 i=0
2200 for s in $SELECTED_FILES; do
2201     SELECTED[$i]=$s
2202     i=$[$i+1]
2203 done
2204 IFS="$OLD_IFS"
2205 
2206 }" ~ command;
2207         }
2208 
2209         if ( command.endsWith("| select") )
2210         {
2211             command = command[0..$-8];
2212             select_files = true;
2213         }
2214 
2215         environment["TERM"] = "rxvt-unicode";
2216         environment["COLORTERM"] = "rxvt-xpm";
2217 
2218         immutable(char) *bash = "/bin/bash".toStringz();
2219         immutable(char) *c_opt = "-c".toStringz();
2220         immutable(char) *command_z = command.toStringz();
2221 
2222         auto pid = forkpty(&master, null, null, &ws);
2223         if (pid < 0)
2224         {
2225             throw new Exception("forkpty() error: " ~ fromStringz(strerror(errno)).idup());
2226         }
2227         else if ( pid == 0 ) // Child
2228         {
2229             stderrPipe.readEnd.close();
2230             dup2(stderrPipe.writeEnd.fileno, stderr.fileno);
2231             stderrPipe.writeEnd.close();
2232             execl(bash, bash, c_opt, command_z, null);
2233             writefln("execl() error: " ~ fromStringz(strerror(errno)).idup());
2234             assert(0);
2235         }
2236         //parent
2237         stderrPipe.writeEnd.close();
2238 
2239         if (command.indexOf("${SELECTED[@]}") >= 0 && selection.length > 0)
2240         {
2241             environment.remove("SELECTED_FILES");
2242         }
2243 
2244         File mfile;
2245         mfile.fdopen(master, "a+");
2246         scope(exit) {
2247             mfile.close();
2248             close(master);
2249         }
2250 
2251         //auto pid = spawnProcess(["bash", "-c", command], sfile, sfile, stderrPipe.writeEnd); 
2252 
2253         auto fdstdout = master;
2254         auto fdstderr = stderrPipe.readEnd.fileno;
2255         auto fdstdin  = master;
2256         auto fdmax = max(fdstdout, fdstderr, fdstdin);
2257     }
2258 
2259     scope(success) {
2260         waitpid(pid, null, 0);
2261     }
2262     scope(failure) {
2263         kill(pid, SIGKILL);
2264         waitpid(pid, null, 0);
2265     }
2266 
2267     cmd_data.start = Clock.currTime().stdTime();
2268     ds = get_data_for_command(cmd_data);
2269     data = ds;
2270 
2271     res = cgs.db_commands.put(cgs.txn, &key, &data);
2272     if (res != 0)
2273     {
2274         throw new Exception("DB command not written");
2275     }
2276 
2277     cgs.commit();
2278     cgs.recommit();
2279 
2280     /*Make file descriptions NON_BLOCKING */
2281     set_non_block_mode(fdstdout);
2282     set_non_block_mode(fdstderr);
2283     set_non_block_mode(fdstdin);
2284 
2285     fd_set rfds;
2286     fd_set wfds;
2287     timeval tv;
2288     int retval;
2289 
2290     /* Wait up to 100ms seconds. */
2291     tv.tv_sec = 0;
2292     tv.tv_usec = 100_000;
2293 
2294     bool select_zero;
2295     bool terminated;
2296     int result = -1;
2297     ulong out_id = 0;
2298     Mode mode;
2299     WorkMode workmode1;
2300     WorkMode workmode2;
2301     workmode1.tid = tid;
2302     workmode1.select = select_files;
2303     AltMode altmode;
2304     altmode.cols = ws.ws_col;
2305     altmode.rows = ws.ws_row;
2306     altmode.scroll_from = 0;
2307     altmode.scroll_to = altmode.rows;
2308     writefln("ALTMODE.WS: %dx%d", altmode.cols, altmode.rows);
2309     bool stdin_closed = false;
2310     while(!cgs.finish)
2311     {
2312         cgs.recommit();
2313         if (select_zero)
2314         {
2315             int status;
2316             if ( waitpid(pid, &status, WNOHANG) )
2317             {
2318                 cgs.commit();
2319                 cgs.recommit();
2320                 cmd_data.end = Clock.currTime().stdTime();
2321                 cmd_data.status = status;
2322                 result = status;
2323                 ds = get_data_for_command(cmd_data);
2324                 data = ds;
2325 
2326                 res = cgs.db_commands.put(cgs.txn, &key, &data);
2327                 if (res != 0)
2328                 {
2329                     throw new Exception("DB command not written");
2330                 }
2331                 cgs.OIT++;
2332                 break;
2333             }
2334         }
2335 
2336         select_zero = false;
2337 
2338         FD_ZERO(&rfds);
2339         FD_SET(fdstdout, &rfds);
2340         FD_SET(fdstderr, &rfds);
2341 
2342         if (!stdin_closed)
2343         {
2344             FD_ZERO(&wfds);
2345             FD_SET(fdstdin, &wfds);
2346         }
2347 
2348         int eof = 0;
2349         bool pause = true;
2350 
2351         retval = select(fdmax+1, &rfds, stdin_closed ? null : &wfds, null, &tv);
2352         if (retval < 0)
2353         {
2354             if (errno != EINTR)
2355                 throw new Exception("select() error: " ~ fromStringz(strerror(errno)).idup());
2356         }
2357         else if (retval > 0)
2358         {
2359             if (FD_ISSET(fdstdout, &rfds))
2360             {
2361                 pause = false;
2362                 eof += process_input(cgs, cwd, new_id, 
2363                         out_id, mode, workmode1, altmode,
2364                         fdstdout, OutPipe.STDOUT);
2365                 tid.send(thisTid, "update terminal");
2366             }
2367             if (FD_ISSET(fdstderr, &rfds))
2368             {
2369                 pause = false;
2370                 eof += process_input(cgs, cwd, new_id, 
2371                         out_id, mode, workmode2, altmode,
2372                         fdstderr, OutPipe.STDERR);
2373                 tid.send(thisTid, "update terminal");
2374             }
2375             if ( !stdin_closed && FD_ISSET(fdstdin, &wfds) )
2376             {
2377                 while( receiveTimeout( 0.seconds, 
2378                         (string input) {
2379                             /*foreach (i; input)
2380                             {
2381                                 writefln("INPUT: '%c' - %X (%d)", i, i, i);
2382                             }*/
2383                             if (input == "\x04") // Ctrl+D
2384                             {
2385                                 writefln("Ctrl+D");
2386                                 version(WithoutTTY)
2387                                 {
2388                                     cmd_pipes.stdin.close();
2389                                     stdin_closed = true;
2390                                 }
2391                                 else
2392                                 {
2393                                     core.sys.posix.unistd.write(fdstdin, input.ptr, input.length);
2394                                 }
2395                             }
2396                             else
2397                             {
2398                                 if (altmode.cursor_mode)
2399                                 {
2400                                     if (input.startsWith("\x1B[") &&
2401                                             input.length > 2 &&
2402                                             "ABCD".indexOf(input[2]) >= 0)
2403                                     {
2404                                         input = input[0..1] ~ "O" ~ input[2..3];
2405                                     }
2406                                 }
2407                                 core.sys.posix.unistd.write(fdstdin, input.ptr, input.length);
2408                             }
2409                         }
2410                 ) )
2411                 {
2412                 }
2413             }
2414             if (!stdin_closed && altmode.control_input.length > 0)
2415             {
2416                 string input = altmode.control_input;
2417                 core.sys.posix.unistd.write(fdstdin, input.ptr, input.length);
2418                 altmode.control_input = "";
2419             }
2420         }
2421         else
2422         {
2423             select_zero = true;
2424         }
2425 
2426         if (eof >= 2) select_zero = true;
2427 
2428         while ( receiveTimeout( 0.seconds, 
2429                 (OwnerTerminated ot) {
2430                     writefln("Abort command due stopping parent");
2431                     kill(pid, SIGKILL);
2432                     cgs.finish = true;
2433 
2434                     cmd_data.end = Clock.currTime().stdTime();
2435                     cmd_data.status = -1;
2436                     string ds = get_data_for_command(cmd_data);
2437                     data = ds;
2438 
2439                     auto res = cgs.db_commands.put(cgs.txn, &key, &data);
2440                     if (res != 0)
2441                     {
2442                         throw new Exception("DB command not written");
2443                     }
2444                 },
2445                 (string cmd, typeof(SIGTERM) signal)
2446                 {
2447                     if (cmd == "signal")
2448                     {
2449                         kill(pid, signal);
2450                     }
2451                 },
2452                 (winsize new_ws)
2453                 {
2454                     auto rc = ioctl(master, TIOCSWINSZ, &new_ws);
2455                     if (rc < 0)
2456                     {
2457                         throw new Exception("ioctl(TIOCSWINSZ) error: " ~ fromStringz(strerror(errno)).idup());
2458                     }
2459 
2460                     altmode.cols = new_ws.ws_col;
2461                     altmode.rows = new_ws.ws_row;
2462                     writefln("CHANGE WS: %d, %d", altmode.cols, altmode.rows);
2463 
2464                     altmode.buffer.length = altmode.cols*altmode.rows;
2465                     altmode.alt_attrs.length = altmode.cols*altmode.rows;
2466                     altmode.buffer[0..$] = " "d[0];
2467                     altmode.scroll_from = 0;
2468                     altmode.scroll_to = altmode.rows;
2469                 } ) )
2470         {
2471         }
2472 
2473         if (pause)
2474             Thread.sleep(100.msecs);
2475     }
2476     return result;
2477 }
2478 version (Windows)
2479 {
2480     return 0;
2481 }
2482 }
2483 
2484 private void
2485 command(string cwd, string full_cwd, string command, winsize ws,
2486         immutable string[] selection, Tid tid)
2487 {
2488     CMDGlobalState cgs = new CMDGlobalState();
2489     try {
2490         scope(exit)
2491         {
2492             destroy(cgs);
2493         }
2494         fork_command(cgs, cwd, full_cwd, command, ws, selection, tid);
2495         cgs.commit();
2496     } catch (shared(Throwable) exc) {
2497         send(tid, exc);
2498     }
2499 
2500     writefln("Finish command %s", command);
2501     send(tid, thisTid);
2502 }
2503 
2504 private auto
2505 get_arguments(bool Multiversion = true)(GlobalState gs, string command)
2506 {
2507     string full_current_path = gs.full_current_path;
2508     version (Windows)
2509     {
2510 	 if (full_current_path.length == 2 && full_current_path[1] == ':')
2511 		full_current_path ~= SL;
2512     }
2513 
2514     while (command > "" && command[0] == ' ' || command[0] == '\t')
2515         command = command[1..$];
2516 
2517     string argument = "";
2518     static if (Multiversion)
2519     {
2520         string[] arguments;
2521     }
2522 
2523     StringStatus status;
2524 
2525     for (ssize_t i=0; i < command.length; i+= command.stride(i))
2526     {
2527         string chr = command[i..i+command.stride(i)];
2528 
2529         if (chr == `\` && status != StringStatus.Quote)
2530         {
2531             i++;
2532 	    if (i >= command.length) continue;
2533             chr = command[i..i+command.stride(i)];
2534             argument ~= chr;
2535         }
2536         else if (chr == `"` && status == StringStatus.Normal)
2537         {
2538             status = StringStatus.DblQuote;
2539         }
2540         else if (chr == `"` && status == StringStatus.DblQuote)
2541         {
2542             status = StringStatus.Normal;
2543         }
2544         else if (chr == `'` && status == StringStatus.Normal)
2545         {
2546             status = StringStatus.Quote;
2547         }
2548         else if (chr == `'` && status == StringStatus.Quote)
2549         {
2550             status = StringStatus.Normal;
2551         }
2552         else if (chr == ` ` && status == StringStatus.Normal)
2553         {
2554             static if (Multiversion)
2555             {
2556                 arguments ~= buildNormalizedPath(absolutePath(expandTilde(argument), gs.full_current_path));
2557                 argument = "";
2558                 while (i+1 < command.length && command[i+1] == ' ')
2559                     i++;
2560             }
2561             else
2562             {
2563                 return [""];
2564             }
2565         }
2566         else if (chr == `&` && status == StringStatus.Normal)
2567         {
2568             return [""];
2569         }
2570         else if (chr == `|` && status == StringStatus.Normal)
2571         {
2572             return [""];
2573         }
2574         else
2575         {
2576             argument ~= chr;
2577         }
2578     }
2579 
2580     static if (Multiversion)
2581     {
2582         if (argument > "")
2583             arguments ~= buildNormalizedPath(absolutePath(expandTilde(argument), full_current_path));
2584         return arguments;
2585     }
2586     else
2587     {
2588         return [buildNormalizedPath(absolutePath(expandTilde(argument), full_current_path))];
2589     }
2590 }
2591 
2592 private string
2593 get_argument(GlobalState gs, string command)
2594 {
2595     string[] args = get_arguments!false(gs, command);
2596     return args[0];
2597 }
2598 
2599 private bool
2600 write_command_and_response(GlobalState gs, string command, string error)
2601 {
2602     gs.txn = null;
2603     string cwd = gs.current_path;
2604     ulong id = get_max_id_of_cwd(gs, cwd);
2605     ulong new_id = id+1;
2606     if (id > 0)
2607     {
2608         new_id = id + 1000 - id%1000;
2609         //writefln("last_id=%s (%%1000=%s), new_id=%s", id, id%1000, new_id);
2610     }
2611 
2612     {
2613         ulong replace_id = find_command_in_cwd(gs, cwd, command);
2614 
2615         delete_command_out(gs, cwd, replace_id);
2616 
2617         string ks = get_key_for_command(command_key(cwd, replace_id));
2618         Dbt key = ks;
2619         auto res = gs.db_commands.del(gs.txn, &key);
2620     }
2621 
2622     Dbt key, data;
2623     string ks = get_key_for_command(command_key(cwd, new_id));
2624     key = ks;
2625     command_data cmd_data = command_data(command, Clock.currTime().stdTime(), Clock.currTime().stdTime(), error > ""?-1:0);
2626     string ds = get_data_for_command(cmd_data);
2627     data = ds;
2628 
2629     auto res = gs.db_commands.put(null, &key, &data);
2630     if (res != 0)
2631     {
2632         throw new Exception("DB command not written");
2633     }
2634 
2635     if (error > "")
2636     {
2637         ulong out_id = 1;
2638         ks = get_key_for_command_out(command_out_key(cwd, new_id, out_id));
2639         key = ks;
2640 
2641         ds = get_data_for_command_out(
2642                 command_out_data(Clock.currTime().stdTime(), OutPipe.STDERR, 
2643                     error.length,
2644                     error));
2645         data = ds;
2646         res = gs.db_command_output.put(null, &key, &data);
2647         if (res != 0)
2648         {
2649             throw new Exception("DB command out not written");
2650         }
2651     }
2652     return true;
2653 }
2654 
2655 private bool
2656 exec_builtin_command(GlobalState gs, string command)
2657 {
2658     string orig_command = command;
2659     while (command > "" && command[0] == ' ' || command[0] == '\t')
2660         command = command[1..$];
2661 
2662     while (command > "" && command[$-1] == ' ' || command[$-1] == '\t')
2663         command = command[0..$-1];
2664 
2665     if (command.startsWith("cd "))
2666     {
2667         string argument = get_argument(gs, command[3..$]);
2668         if (argument == "") return write_command_and_response(
2669                 gs, orig_command, "Wrong using built-in command cd");
2670 
2671         auto apply_rect = DRect(0, 0, 1024*1024, 1024*1024);
2672         auto drect = get_rectsize_for_path(gs, PathMnt(gs.lsblk, SL), argument, apply_rect);
2673 
2674         if (!isNaN(drect.w))
2675         {
2676             drect.rescale_screen(gs.screen, SDL_Rect(0, 0, gs.screen.w, gs.screen.h));
2677         }
2678         else
2679         {
2680             writefln("Can't calculate DRect for %s", argument);
2681         }
2682 
2683         gs.state = State.FileManager;
2684         write_command_and_response(
2685                 gs, orig_command, "");
2686         return true;
2687     }
2688     else if (command.startsWith("go "))
2689     {
2690         string argument = get_argument(gs, command[3..$]);
2691         if (argument == "") return write_command_and_response(
2692                 gs, orig_command, "Wrong using built-in command go");
2693 
2694         auto apply_rect = DRect(0, 0, 1024*1024, 1024*1024);
2695         auto drect = get_rectsize_for_path(gs, PathMnt(gs.lsblk, SL), argument, apply_rect);
2696 
2697         if (!isNaN(drect.w))
2698         {
2699             drect.rescale_screen(gs.screen, 
2700                     SDL_Rect(cast(int)((gs.screen.w - gs.screen.h*0.75)/2), 
2701                         cast(int)((gs.screen.h - cast(double)gs.screen.h/(gs.screen.w/gs.screen.h)*0.75)/2), 
2702                         cast(int)(gs.screen.h*0.75), 
2703                         cast(int)(cast(double)gs.screen.h/(gs.screen.w/gs.screen.h)*0.75)));
2704         }
2705         else
2706         {
2707             writefln("Can't calculate DRect for %s", argument);
2708         }
2709 
2710         gs.state = State.FileManager;
2711         write_command_and_response(
2712                 gs, orig_command, "");
2713         return true;
2714     }
2715     else if (command.startsWith("open "))
2716     {
2717         string argument = get_argument(gs, command[5..$]);
2718         if (argument == "") return write_command_and_response(
2719                 gs, orig_command, "Wrong using built-in command open");
2720 
2721         openFile(gs, PathMnt(gs.lsblk, argument));
2722 
2723         write_command_and_response(
2724                 gs, orig_command, "");
2725         return true;
2726     }
2727     else if (command.startsWith("mopen "))
2728     {
2729         string argument = get_argument(gs, command[6..$]);
2730         if (argument == "") return write_command_and_response(
2731                 gs, orig_command, "Wrong using built-in command mopen");
2732 
2733         openFileByMime(gs, argument);
2734 
2735         write_command_and_response(
2736                 gs, orig_command, "");
2737         return true;
2738     }
2739     else if (command.startsWith("select "))
2740     {
2741         string[] arguments = get_arguments(gs, command[7..$]);
2742         if (arguments == []) return write_command_and_response(
2743                 gs, orig_command, "Wrong using built-in command select");
2744 
2745         foreach (arg; arguments)
2746         {
2747             auto pathmnt = PathMnt(gs.lsblk, arg);
2748             auto apply_rect = DRect(0, 0, 1024*1024, 1024*1024);
2749             auto drect = get_rectsize_for_path(gs, PathMnt(gs.lsblk, SL), arg, apply_rect);
2750             gs.selection_hash[pathmnt] = drect;
2751         }
2752 
2753         gs.dirty = true;
2754 
2755         write_command_and_response(
2756                 gs, orig_command, "");
2757         return true;
2758     }
2759 
2760     return false;
2761 }
2762 
2763 public int
2764 run_command(GlobalState gs, string command)
2765 {
2766     if (command == "") return -1;
2767 
2768     if (exec_builtin_command(gs, command)) return 0;
2769 
2770     string[] selection;
2771     if (command.indexOf("${SELECTED[@]}") >= 0)
2772         selection = gs.selection_hash.keys;
2773     writefln("Start command %s", command);
2774     auto tid = spawn(&.command, gs.current_path, gs.full_current_path, command,
2775            gs.command_line.ws, selection.idup(), thisTid);
2776     gs.commands[tid] = command;
2777     return 0;
2778 }
2779