1 module unde.command_line.lib;
2 
3 import unde.global_state;
4 import unde.path_mnt;
5 import unde.lib;
6 import unde.slash;
7 import unde.font;
8 import unde.keybar.lib;
9 import unde.command_line.db;
10 import unde.command_line.run;
11 import unde.command_line.delete_command;
12 
13 import berkeleydb.all;
14 
15 import derelict.sdl2.sdl;
16 import derelict.sdl2.ttf;
17 
18 import core.exception;
19 
20 import std..string;
21 import std.stdio;
22 import std.math;
23 import std.conv;
24 import std.utf;
25 import std.file;
26 import std.path;
27 import std.process;
28 import std.concurrency;
29 import std.algorithm.iteration;
30 import std.algorithm.sorting;
31 import core.sys.posix.signal;
32 import core.sys.posix.stdlib;
33 
34 public ssize_t
35 get_position_by_chars(
36         int x, int y, SDL_Rect[] chars, ssize_t p=0)
37 {
38     if (chars.length == 0) return -1;
39     ssize_t pos = chars.length/2;
40     while (pos >= 0 && chars[pos].w == 0 && chars[pos].h == 0)
41         pos--;
42     if (pos < 0) return -1;
43 
44     if (y < chars[pos].y)
45     {
46         return get_position_by_chars(x, y, chars[0..pos], p);
47     }
48     else if (y > chars[pos].y + chars[pos].h)
49     {
50         return get_position_by_chars(x, y, chars[pos+1..$], p+pos+1);
51     }
52     else if (x < chars[pos].x)
53     {
54         return get_position_by_chars(x, y, chars[0..pos], p);
55     }
56     else if (x > chars[pos].x + chars[pos].w)
57     {
58         return get_position_by_chars(x, y, chars[pos+1..$], p+pos+1);
59     }
60     else
61     {
62         return p+pos;
63     }
64 }
65 
66 private void
67 fix_bottom_line(GlobalState gs)
68 {
69     //writefln("Fix Bottom Line");
70     with (gs.command_line)
71     {
72         nav_cmd_id = 0;
73         nav_out_id = 0;
74         nav_skip_cmd_id = 0;
75 
76         cwd = gs.current_path;
77 
78         Dbc cursor = gs.db_commands.cursor(null, 0);
79         scope(exit) cursor.close();
80 
81         Dbt key, data;
82 
83         ulong id = mouse.cmd_id > 0 ? mouse.cmd_id : 1;
84         string ks = get_key_for_command(command_key(cwd, id));
85         //writefln("SET RANGE: %s (cwd=%s, id=%X)", ks, cwd, id);
86         key = ks;
87         id = find_next_by_key(cursor, 0, id, key, data);
88 
89         //writefln("mouse.cmd_id=%s", mouse.cmd_id);
90         if (id != 0)
91         {
92             bool cmd_next_stop;
93             /* The bottom command found */
94             long y_off = neg_y;
95             //writefln("y=%s, y_off=%s", y, y_off);
96             do
97             {
98                 string key_string = key.to!(string);
99                 command_key cmd_key;
100                 parse_key_for_command(key_string, cmd_key);
101                 if (cwd != cmd_key.cwd)
102                 {
103                     id = 0;
104                     break;
105                 }
106                 else
107                 {
108                     //writefln("cmd_id=%s", id);
109                     id = cmd_key.id;
110                     string data_string = data.to!(string);
111                     command_data cmd_data;
112                     parse_data_for_command(data_string, cmd_data);
113 
114                     if (search_mode && cmd_data.command.indexOf(search) < 0)
115                         continue;
116 
117                     if (cmd_next_stop)
118                     {
119                         cmd_next_stop = false;
120                         nav_skip_cmd_id = cmd_key.id;
121                         //writefln("nav_skip_cmd_id=%d", nav_skip_cmd_id);
122                         if (fontsize < 5)
123                         {
124                             int line_height9 = cast(int)(round(SQRT2^^fontsize)*1.2);
125                             y = y_off - gs.screen.h + line_height9*3 + 8;
126                             //writefln("CMD y = %d", y);
127                         }
128                         break;
129                     }
130 
131                     int line_height = cast(int)(round(SQRT2^^9)*1.2);
132                     auto rt = gs.text_viewer.font.get_size_of_line(cmd_data.command, 
133                             9, gs.screen.w-80, line_height, SDL_Color(0xFF,0x00,0xFF,0xFF));
134 
135                     int lines = rt.h / line_height;
136 
137                     auto rect = SDL_Rect();
138                     rect.x = 40;
139                     rect.y = cast(int)(y_off + 4);
140                     rect.w = rt.w;
141                     rect.h = rt.h;
142 
143                     if (cmd_key.id == mouse.cmd_id &&
144                             (0 == mouse.out_id || fontsize < 5))
145                     {
146                         int ry = cast(int)(gs.mouse_screen_y - mouse_rel_y*rect.h);
147                         y_off = ry - 4 + neg_y;
148                         //writefln("on mouse cmd_id=%d ry-4=%d y_off=%s", mouse.cmd_id, ry-4, y_off);
149                     }
150 
151                     //writefln("CMD id=%s %s, rect.y = %s > %s", cmd_key.id, cmd_data.command, rect.y, gs.screen.h + line_height);
152                     if (rect.y >= gs.screen.h + line_height && nav_skip_cmd_id == 0)
153                     {
154                         cmd_next_stop = true;
155                     }
156                     if (nav_skip_cmd_id > 0 && (nav_cmd_id > 0 || fontsize < 5) )
157                         break;
158 
159                     y_off += line_height*lines;
160 
161                     /* Loop through all commands */
162                     if (fontsize >= 5 && !search_mode)
163                     {
164                         /* Try to find last output for command */
165                         Dbc cursor2 = gs.db_command_output.cursor(null, 0);
166                         scope(exit) cursor2.close();
167 
168                         Dbt key2, data2;
169                         ulong out_id = cmd_key.id == mouse.cmd_id && mouse.out_id > 0 ? mouse.out_id : 1;
170                         ks = get_key_for_command_out(command_out_key(cwd, cmd_key.id, out_id));
171                         //writefln("SET RANGE: %s (cwd=%s, id=%X)", ks, cwd, id);
172                         key2 = ks;
173                         out_id = find_next_by_key(cursor2, 0, out_id, key2, data2);
174 
175                         //writefln("mouse.out_id=%s", mouse.out_id);
176                         if (out_id > 0)
177                         {
178                             bool next_stop = false;
179                             /* The last output found */
180                             do
181                             {
182                                 /* Loop through all outputs */
183                                 string key_string2 = key2.to!(string);
184                                 command_out_key cmd_key2;
185                                 parse_key_for_command_out(key_string2, cmd_key2);
186                                 //writefln("cmd_id=%s, out_id=%s", cmd_key2.cmd_id, cmd_key2.out_id);
187                                 if (cmd_key.id != cmd_key2.cmd_id || cwd != cmd_key2.cwd)
188                                 {
189                                     out_id = 0;
190                                     break;
191                                 }
192                                 else
193                                 {
194                                     if (next_stop)
195                                     {
196                                         nav_cmd_id = cmd_key2.cmd_id;
197                                         nav_out_id = cmd_key2.out_id;
198                                         //writefln("nav_cmd_id=%s, nav_out_id=%s", nav_cmd_id, nav_out_id);
199                                         next_stop=false;
200 
201                                         int line_height9 = cast(int)(round(SQRT2^^fontsize)*1.2);
202                                         y = y_off - gs.screen.h + line_height9*3 + 8;
203                                         //writefln("OUT y = %d", y);
204                                         break;
205                                     }
206 
207                                     //writefln("out_id=%s", cmd_key2.out_id);
208                                     string data_string2 = data2.to!(string);
209                                     command_out_data cmd_data2;
210                                     parse_data_for_command_out(data_string2, cmd_data2);
211 
212                                     auto color = SDL_Color(0xFF,0xFF,0xFF,0xFF);
213                                     if (cmd_data2.pipe == OutPipe.STDERR)
214                                     {
215                                         color = SDL_Color(0xFF,0x80,0x80,0xFF);
216                                     }
217 
218                                     line_height = cast(int)(round(SQRT2^^fontsize)*1.2);
219 
220                                     final switch(cmd_data2.vers)
221                                     {
222                                         case CommandsOutVersion.Simple:
223                                             rt = gs.text_viewer.font.get_size_of_line(cmd_data2.output, 
224                                                     fontsize, gs.screen.w-80, line_height, color);
225 
226                                             lines = rt.h / line_height - 1;
227                                             if (cmd_data2.output.length > 0 && cmd_data2.output[$-1] != '\n') lines++;
228                                             break;
229 
230                                         case CommandsOutVersion.Screen:
231                                             rt = gs.text_viewer.font.get_size_of_line(cmd_data2.cols,
232                                                     cmd_data2.rows, fontsize, color);
233                                             lines = cmd_data2.rows;
234                                             break;
235                                     }
236 
237 
238                                     rect = SDL_Rect();
239                                     rect.x = 40;
240                                     rect.y = cast(int)(y_off + 4);
241                                     rect.w = rt.w;
242                                     rect.h = rt.h;
243 
244                                     if (cmd_key2.cmd_id == mouse.cmd_id &&
245                                             cmd_key2.out_id == mouse.out_id)
246                                     {
247                                         int ry = cast(int)(gs.mouse_screen_y - mouse_rel_y*rect.h);
248                                         y_off = ry - 4 + neg_y;
249                                         //writefln("on mouse out mouse.cmd_id=%s, mouse.out_id=%d", mouse.cmd_id, mouse.out_id);
250                                         //writefln("on mouse out ry-4=%d y_off=%s", ry-4, y_off);
251                                     }
252 
253                                     //writefln("OUT cmd_key2.cmd_id=%s, out_id=%s %s, rect.y = %s > %s", cmd_key2.cmd_id, cmd_key2.out_id, cmd_data2.output, rect.y, gs.screen.h + line_height);
254                                     if (rect.y > gs.screen.h + line_height)
255                                     {
256                                         next_stop = true;
257                                         cmd_next_stop = true;
258                                     }
259 
260                                     y_off += line_height*lines;
261                                 }
262                             }
263                             while (cursor2.get(&key2, &data2, DB_NEXT) == 0);
264 
265                             if (next_stop)
266                             {
267                                 nav_cmd_id = cmd_key.id;
268                                 nav_out_id = 0;
269 
270                                 int line_height9 = cast(int)(round(SQRT2^^fontsize)*1.2);
271                                 y = y_off - gs.screen.h + line_height9*3 + 8;
272                                 //writefln("OUT y = %d", y);
273                             }
274                         }
275                     }
276                 }
277             }
278             while (cursor.get(&key, &data, DB_NEXT) == 0);
279 
280             if (cmd_next_stop)
281             {
282                 nav_skip_cmd_id = 0;
283                 if (fontsize < 5)
284                 {
285                     int line_height9 = cast(int)(round(SQRT2^^fontsize)*1.2);
286                     y = y_off - gs.screen.h + line_height9*3 + 8;
287                     //writefln("CMD y = %d", y);
288                 }
289             }
290         }
291         neg_y = 0;
292     }
293 }
294 
295 package void
296 selection_to_buffer(GlobalState gs)
297 {
298     with (gs.command_line)
299     {
300         if (start_selection.cmd_id == 0 || end_selection.cmd_id == 0)
301             return;
302 
303         string selection = "";
304 
305         cwd = gs.current_path;
306 
307         Dbc cursor = gs.db_commands.cursor(null, 0);
308         scope(exit) cursor.close();
309 
310         Dbt key, data;
311 
312         ulong id = start_selection.cmd_id;
313         string ks = get_key_for_command(command_key(cwd, id));
314         //writefln("SET RANGE: %s (cwd=%s, id=%d)", ks, cwd, id);
315         key = ks;
316         id = find_next_by_key(cursor, 0, id, key, data);
317 
318         //writefln("mouse.cmd_id=%s", mouse.cmd_id);
319         if (id != 0)
320         {
321             bool cmd_next_stop;
322             /* The bottom command found */
323             long y_off = neg_y;
324             //writefln("y=%s, y_off=%s", y, y_off);
325             do
326             {
327                 string key_string = key.to!(string);
328                 command_key cmd_key;
329                 parse_key_for_command(key_string, cmd_key);
330                 if (cwd != cmd_key.cwd)
331                 {
332                     id = 0;
333                     break;
334                 }
335                 else
336                 {
337                     //writefln("cmd_id=%s", id);
338                     id = cmd_key.id;
339                     string data_string = data.to!(string);
340                     command_data cmd_data;
341                     parse_data_for_command(data_string, cmd_data);
342 
343                     ssize_t start_pos, end_pos;
344                     get_start_end_pos(cmd_key.id,
345                             0,
346                             start_selection, 
347                             end_selection,
348                             start_pos, end_pos);
349                     if (start_pos >= 0)
350                     {
351                         //writefln("start_pos=%s end_pos=%s", start_pos, end_pos);
352                         if (start_pos == 0)
353                             selection ~= "$ ";
354                         if (end_pos+1 >= cmd_data.command.length) end_pos = cmd_data.command.length-1;
355                         selection ~= cmd_data.command[start_pos..end_pos+1];
356                         if (end_pos+1 == cmd_data.command.length)
357                             selection ~= "\n";
358                     }
359                     else if ( !(start_selection.cmd_id == cmd_key.id && start_selection.out_id > 0) )
360                     {
361                         break;
362                     }
363                 
364                     /* Try to find last output for command */
365                     Dbc cursor2 = gs.db_command_output.cursor(null, 0);
366                     scope(exit) cursor2.close();
367 
368                     Dbt key2, data2;
369                     ulong out_id = cmd_key.id == start_selection.cmd_id && 
370                         start_selection.out_id != 0 ? start_selection.out_id : 1;
371                     ks = get_key_for_command_out(command_out_key(cwd, cmd_key.id, out_id));
372                     //writefln("SET RANGE: %s (cwd=%s, key_id=%d, out_id=%s)", ks, cwd, cmd_key.id, out_id);
373                     key2 = ks;
374                     out_id = find_next_by_key(cursor2, 0, out_id, key2, data2);
375 
376                     //writefln("mouse.out_id=%s", mouse.out_id);
377                     if (out_id > 0)
378                     {
379                         do
380                         {
381                             /* Loop through all outputs */
382                             string key_string2 = key2.to!(string);
383                             command_out_key cmd_key2;
384                             parse_key_for_command_out(key_string2, cmd_key2);
385                             //writefln("cmd_id=%s, out_id=%s", cmd_key2.cmd_id, cmd_key2.out_id);
386                             if (cmd_key.id != cmd_key2.cmd_id || cwd != cmd_key2.cwd)
387                             {
388                                 out_id = 0;
389                                 break;
390                             }
391                             else
392                             {
393                                 //writefln("out_id=%s", cmd_key2.out_id);
394                                 string data_string2 = data2.to!(string);
395                                 command_out_data cmd_data2;
396                                 parse_data_for_command_out(data_string2, cmd_data2);
397 
398                                 get_start_end_pos(cmd_key2.cmd_id,
399                                         cmd_key2.out_id,
400                                         start_selection, 
401                                         end_selection,
402                                         start_pos, end_pos);
403 
404                                 if (start_pos < 0) break;
405                                 if (end_pos+1 >= cmd_data2.output.length) end_pos = cmd_data2.output.length-1;
406 
407                                 final switch(cmd_data2.vers)
408                                 {
409                                     case CommandsOutVersion.Simple:
410                                         selection ~= cmd_data2.output[start_pos..end_pos+1];
411                                         break;
412                                     case CommandsOutVersion.Screen:
413                                         selection ~= to!string(cmd_data2.screen[start_pos..end_pos+1]);
414                                         break;
415                                 }
416                             }
417                         }
418                         while (cursor2.get(&key2, &data2, DB_NEXT) == 0);
419                     }
420                 }
421             }
422             while (cursor.get(&key, &data, DB_NEXT) == 0);
423         }
424         //writefln("SELECTION:\n%s", selection);
425         SDL_SetClipboardText(selection.toStringz());
426     }
427 }
428 
429 void cmd_selection_to_buffer(GlobalState gs)
430 {
431     with (gs.command_line)
432     {
433         string selection = command[cmd_start_selection..cmd_end_selection + command.mystride(cmd_end_selection)];
434         SDL_SetClipboardText(selection.toStringz());
435     }
436 }
437 
438 void shift_selected(GlobalState gs)
439 {
440     with (gs.command_line)
441     {
442         if (cmd_start_selection < 0 || cmd_end_selection < 0) return;
443         if (cmd_end_selection > command.length)
444             cmd_end_selection = command.length - 1 - command.strideBack(command.length - 1);
445         string converted;
446         for (ssize_t i=cmd_start_selection; i < cmd_end_selection + command.mystride(cmd_end_selection); i+=command.stride(i))
447         {
448             string chr = command[i..i+command.stride(i)];
449             if (chr.toLower() == chr)
450             {
451                 chr = chr.toUpper();
452             }
453             else
454                 chr = chr.toLower();
455 
456             converted ~= chr;
457         }
458 
459         command = command[0..cmd_start_selection] ~ converted ~ command[cmd_end_selection + command.mystride(cmd_end_selection)..$];
460     }
461 }
462 
463 bool find_chr(string chr, string[][3] *letters, ref ButtonPos buttonpos)
464 {
465     for (buttonpos.i = 0; buttonpos.i < 3; buttonpos.i++)
466     {
467         for (buttonpos.pos = 0; buttonpos.pos <
468                 (*letters)[buttonpos.i].length; buttonpos.pos++)
469         {
470             if ((*letters)[buttonpos.i][buttonpos.pos] == chr)
471                 return true;
472         }
473     }
474     return false;
475 }
476 
477 void change_layout_selected(GlobalState gs)
478 {
479     with (gs.command_line)
480     {
481         if (cmd_start_selection < 0 || cmd_end_selection < 0) return;
482         if (cmd_end_selection > command.length)
483             cmd_end_selection = command.length - 1 - command.strideBack(command.length - 1);
484         ssize_t end_selection = cmd_end_selection + command.mystride(cmd_end_selection);
485         string converted;
486         for (ssize_t i=cmd_start_selection; i < end_selection; i+=command.stride(i))
487         {
488             string chr = command[i..i+command.stride(i)];
489 
490             ssize_t prev_mode = gs.keybar.mode-1;
491             if (prev_mode < 0)
492                 prev_mode = gs.keybar.layout_modes.length - 1;
493 
494             ButtonPos buttonpos;
495             auto letters = &gs.keybar.layout_modes[prev_mode].letters;
496             if (find_chr(chr, letters, buttonpos))
497             {
498                 letters = &gs.keybar.layout_modes[gs.keybar.mode].letters;
499                 chr = (*letters)[buttonpos.i][buttonpos.pos];
500             }
501             letters = &gs.keybar.layout_modes[prev_mode].letters_shift;
502             if (find_chr(chr, letters, buttonpos))
503             {
504                 letters = &gs.keybar.layout_modes[gs.keybar.mode].letters_shift;
505                 chr = (*letters)[buttonpos.i][buttonpos.pos];
506             }
507             letters = &gs.keybar.layout_modes[prev_mode].letters_altgr;
508             if (find_chr(chr, letters, buttonpos))
509             {
510                 letters = &gs.keybar.layout_modes[gs.keybar.mode].letters_altgr;
511                 chr = (*letters)[buttonpos.i][buttonpos.pos];
512             }
513             letters = &gs.keybar.layout_modes[prev_mode].letters_shift_altgr;
514             if (find_chr(chr, letters, buttonpos))
515             {
516                 letters = &gs.keybar.layout_modes[gs.keybar.mode].letters_shift_altgr;
517                 chr = (*letters)[buttonpos.i][buttonpos.pos];
518             }
519 
520             converted ~= chr;
521         }
522 
523         command = command[0..cmd_start_selection] ~ converted ~ command[end_selection..$];
524         cmd_end_selection = cmd_start_selection + converted.length;
525         cmd_end_selection -= command.strideBack(cmd_end_selection);
526         if (pos > cmd_start_selection) pos = command.length;
527     }
528 }
529 
530 
531 bool is_command_position(GlobalState gs, string command, ssize_t pos)
532 {
533     for (ssize_t i = pos-1; i >= -1; i--)
534     {
535         if (i == -1 || command[i] == '&' || command[i] == '|')
536         {
537             return true;
538         }
539         else if (command[i] != ' ' && command[i] != '\t')
540         {
541             return false;
542         }
543     }
544     return true;
545 }
546 
547 enum StringStatus{
548     Normal,
549     Quote,
550     DblQuote,
551     BackApostrophe
552 }
553 
554 void uniq(ref string[] strings)
555 {
556     for (ssize_t i = 0; i < strings.length-1; i++)
557     {
558         if (strings[i] == strings[i+1])
559         {
560             strings = strings[0..i] ~ strings[i+1..$];
561             i--;
562         }
563     }
564 }
565 
566 ssize_t find_in_sorted(string[] hashstock, string needle, ssize_t pos = 0)
567 {
568     if (hashstock.length == 0 )
569         return pos;
570 
571     if ( needle == hashstock[$/2] )
572     {
573         return pos+hashstock.length/2;
574     }
575     else if ( needle < hashstock[$/2] )
576     {
577         return find_in_sorted(hashstock[0..$/2], needle, pos);
578     }
579     else
580     {
581         return find_in_sorted(hashstock[$/2+1..$], needle, pos+hashstock.length/2+1);
582     }
583 }
584 
585 string remove_backslashes(string str)
586 {
587     bool prevslash;
588     for (ssize_t i = 0; i < str.length; i += str.stride(i))
589     {
590         string chr = str[i..i+str.stride(i)];
591         if (chr == "\\" && !prevslash)
592         {
593             str = str[0..i] ~ str[i+chr.length..$];
594             if (i >= str.length) break;
595             prevslash = true;
596         }
597         if (chr != "\\")
598             prevslash = false;
599     }
600     return str;
601 }
602 
603 string backslashes(string str, StringStatus status)
604 {
605     string[] chars_needed_backslashes = [];
606     final switch (status)
607     {
608         case StringStatus.Quote:
609             break;
610         case StringStatus.DblQuote:
611             chars_needed_backslashes = [`"`, "`"];
612             break;
613         case StringStatus.BackApostrophe:
614             chars_needed_backslashes = [`"`, "`", "`", "`"];
615             break;
616         case StringStatus.Normal:
617             chars_needed_backslashes = [`"`, "`", "'", " "];
618             break;
619     }
620 
621     for (ssize_t i = 0; i < str.length; i += str.stride(i))
622     {
623         string chr = str[i..i+str.stride(i)];
624         foreach (cnb; chars_needed_backslashes)
625         {
626             if (chr == cnb)
627             {
628                 str = str[0..i] ~ `\` ~ str[i..$];
629                 i++;
630             }
631         }
632     }
633     return str;
634 }
635 
636 string common_part(string a, string b)
637 {
638     string res;
639     for (ssize_t i = 0; i < a.length && i < b.length; i+=a.stride(i))
640     {
641         string chr1 = a[i..i+a.stride(i)];
642         string chr2 = b[i..i+b.stride(i)];
643         if (chr1 == chr2)
644             res ~= chr1;
645         else break;
646     }
647     return res;
648 }
649 
650 string autocomplete(GlobalState gs, string command, bool is_command, StringStatus status)
651 {
652     string[] completions;
653     if (is_command && command.indexOf(SL) < 0)
654     {
655         string path = environment["PATH"];
656         auto paths = splitter(path, pathSeparator);
657 
658         string[] binaries = [];
659         foreach(p; paths)
660         {
661             p = p.expandTilde();
662             try
663             {
664                 foreach (string name; dirEntries(p, SpanMode.shallow))
665                 {
666                     binaries ~= name[name.lastIndexOf(SL)+1..$];
667                 }
668             }
669             catch (FileException exp)
670             {
671             }
672         }
673 
674         binaries ~= ["cd", "select", "go", "open", "mopen"];
675         sort!("a < b")(binaries);
676         uniq(binaries);
677         completions = binaries;
678     }
679     else
680     {
681 	version (Windows)
682 	{
683 		if (gs.full_current_path == "")
684 		{
685 		    string[] files = [];
686 		    files ~= "C:\\";
687 		    files ~= "D:\\";
688 		    files ~= "E:\\";
689 		    completions = files;
690 		}
691 
692 	}
693 	if (gs.full_current_path > "")
694 	{
695 		string full_current_path = gs.full_current_path;
696 		version (Windows)
697 		{
698 		    if (full_current_path[1] == ':' && full_current_path.length == 2)
699 		  	    full_current_path ~= SL;
700 		}
701 
702 		string dir;
703 		dir = buildNormalizedPath(absolutePath(expandTilde(command), full_current_path));
704 		if (command.length > 0 && command[$-1] == SL[0]) dir ~= SL;
705 		dir = dir[0..dir.lastIndexOf(SL)+1];
706 
707 		string[] files = [];
708 		if (gs.selection_hash.length > 0)
709 		    files ~= "${SELECTED[@]}";
710 		//writefln("dir = %s, is null = %s, %s", dir, dir is null, dir.length);
711 		if (dir !is null)
712 		{
713 		    try{
714 			if (!dir.isDir()) dir = dir[0..dir.lastIndexOf(SL)];
715 			foreach (string name; dirEntries(dir, SpanMode.shallow))
716 			{
717 			    files ~= name[name.lastIndexOf(SL)+1..$];
718 			}
719 
720 			command = command[command.lastIndexOf(SL)+1..$];
721 
722 			sort!("a < b")(files);
723 			completions = files;
724 		    } catch (FileException exp)
725 		    {
726 		    }
727 		}
728 	}
729     }
730 
731     final switch (status)
732     {
733         case StringStatus.Quote:
734             break;
735         case StringStatus.DblQuote:
736             command = remove_backslashes(command);
737             break;
738         case StringStatus.BackApostrophe:
739             command = remove_backslashes(command);
740             break;
741         case StringStatus.Normal:
742             command = remove_backslashes(command);
743             break;
744     }
745 
746     string[] next_chars = [];
747     ssize_t pos = find_in_sorted(completions, command);
748     ssize_t i;
749     for (i=pos; i < completions.length; i++)
750     {
751         if ( completions[i].startsWith(command) )
752         {
753             if ( completions[i].length <= command.length )
754             {
755                 next_chars ~= "✔";
756                 continue;
757             }
758             if (next_chars.length == 0 || next_chars[$-1] != completions[i][command.length..command.length+completions[i].stride(command.length)])
759                 next_chars ~= completions[i][command.length..command.length+completions[i].stride(command.length)];
760         }
761         else
762             break;
763     }
764 
765     if (i == pos)
766         return "";
767     else if (i == pos+1)
768         return "1"~completions[pos][command.length..$].backslashes(status);
769     else if (next_chars.length == 1)
770     {
771         string common = completions[pos];
772         for (ssize_t j = pos+1; j < i; j++)
773             common = common_part(common, completions[j]);
774         return "1"~common[command.length..$].backslashes(status);
775     }
776     else if (i - pos <= 5)
777     {
778         string result = "";
779         for (ssize_t j = pos; j < i; j++)
780         {
781             result ~= completions[j] ~ " ";
782         }
783         return "2"~result;
784     }
785     else
786     {
787         string result = "";
788         foreach (chr; next_chars)
789         {
790             result ~= chr ~ " ";
791         }
792         return "2"~result;
793     }
794 }
795 
796 string autocomplete(GlobalState gs, string command)
797 {
798     static string prev_command;
799     static string prev_result;
800     if (prev_command == command)
801         return prev_result;
802 
803     prev_command = command;
804 
805     if (command.length > 0 && (command[0] == '+' || command[0] == '*'))
806         command = command[1..$];
807 
808     ssize_t dbl_quotes = command.count("\"");
809     ssize_t esc_dbl_quotes = command.count("\\\"");
810     dbl_quotes -= esc_dbl_quotes;
811 
812     ssize_t back_apostrophes = command.count("`");
813     ssize_t esc_back_apostrophes = command.count("\\`");
814     back_apostrophes -= esc_back_apostrophes;
815 
816     ssize_t quotes = command.count("'");
817 
818     ssize_t quote;
819     if (quotes%2 == 1)
820     {
821         quote = command.lastIndexOf("'");
822     }
823 
824     ssize_t dbl_quote;
825     if (dbl_quotes%2 == 1)
826     {
827         dbl_quote = command.lastIndexOf("\"");
828         while (dbl_quote > 0 && command[dbl_quote-1] == '\\')
829         {
830             dbl_quote = command[0..dbl_quote].lastIndexOf("`");
831         }
832     }
833 
834     ssize_t back_apostrophe;
835     if (back_apostrophes%2 == 1)
836     {
837         back_apostrophe = command.lastIndexOf("`");
838         while (back_apostrophe > 0 && command[back_apostrophe-1] == '\\')
839         {
840             back_apostrophe = command[0..back_apostrophe].lastIndexOf("`");
841         }
842     }
843 
844     bool is_command;
845     StringStatus string_status;
846     ssize_t pos;
847     if (quote > dbl_quote && quote > back_apostrophe)
848     {
849         is_command = is_command_position(gs, command, quote);
850         pos = quote+1;
851         string_status = StringStatus.Quote;
852     }
853     else if (dbl_quote > quote && dbl_quote > back_apostrophe)
854     {
855         is_command = is_command_position(gs, command, dbl_quote);
856         pos = dbl_quote+1;
857         string_status = StringStatus.DblQuote;
858     }
859     else 
860     {
861         if (back_apostrophe > quote && back_apostrophe > dbl_quote)
862         {
863             command = command[dbl_quote+1..$];
864         }
865 
866         ssize_t space = command.lastIndexOf(" ");
867         while ( space > 0 && command[space-1] == '\\')
868         {
869             space = command[0..space].lastIndexOf(" ");
870         }
871         is_command = is_command_position(gs, command, space);
872 
873         pos = space+1;
874         if (back_apostrophe > quote && back_apostrophe > dbl_quote)
875         {
876             string_status = StringStatus.BackApostrophe;
877         }
878         else
879         {
880             string_status = StringStatus.Normal;
881         }
882     }
883 
884     prev_result = autocomplete(gs, command[pos..$], is_command, string_status);
885     return prev_result;
886 }
887 
888 private void
889 get_start_end_pos(in ulong cmd_id,
890         in ulong out_id,
891         in CmdOutPos start_selection, 
892         in CmdOutPos end_selection,
893         out ssize_t start_pos, out ssize_t end_pos)
894 {
895     if (cmd_id < start_selection.cmd_id ||
896             cmd_id == start_selection.cmd_id &&
897             out_id < start_selection.out_id ||
898             cmd_id > end_selection.cmd_id ||
899             cmd_id == end_selection.cmd_id &&
900             out_id > end_selection.out_id)
901     {
902         start_pos = -1;
903         end_pos = -1;
904         return;
905     }
906 
907     if (cmd_id > start_selection.cmd_id ||
908             cmd_id == start_selection.cmd_id &&
909             out_id > start_selection.out_id)
910     {
911         start_pos = 0;
912     }
913 
914     if ( cmd_id == start_selection.cmd_id &&
915             out_id == start_selection.out_id)
916     {
917         start_pos = start_selection.pos;
918     }
919 
920     if (cmd_id < end_selection.cmd_id ||
921             cmd_id == end_selection.cmd_id &&
922             out_id< end_selection.cmd_id)
923     {
924         end_pos = ssize_t.max;
925     }
926 
927     if ( cmd_id == end_selection.cmd_id &&
928             out_id == end_selection.out_id)
929     {
930         end_pos = end_selection.pos;
931         if (end_pos == -1)
932             end_pos = ssize_t.max;
933     }
934 }
935 
936 void
937 draw_command_line(GlobalState gs)
938 {
939     with (gs.command_line)
940     {
941         if (SDL_GetTicks() - last_redraw > 200)
942         {
943             on_click = null;
944 
945             /* Setup texture render target */
946             auto old_texture = SDL_GetRenderTarget(gs.renderer);
947             int r = SDL_SetRenderTarget(gs.renderer, texture);
948             if (r < 0)
949             {
950                 throw new Exception(format("Error while set render target text_viewer.texture: %s",
951                             fromStringz(SDL_GetError()) ));
952             }
953 
954             SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
955             r = SDL_SetRenderDrawColor(gs.renderer, 0, 0, 0, 0);
956             if (r < 0)
957             {
958                 writefln("Can't SDL_SetRenderDrawColor: %s",
959                         to!string(SDL_GetError()));
960             }
961 redraw:
962             r = SDL_RenderClear(gs.renderer);
963             if (r < 0)
964             {
965                 throw new Exception(format("Error while clear renderer: %s",
966                             fromStringz(SDL_GetError()) ));
967             }
968 
969             /* If terminal */
970             if (gs.command_line.terminal)
971             {
972                 if (font_changed || neg_y < 0)
973                 {
974                     fix_bottom_line(gs);
975                     font_changed = false;
976                 }
977 
978                 /* Background */
979                 r = SDL_RenderCopy(gs.renderer, gs.texture_black, null, null);
980                 if (r < 0)
981                 {
982                     writefln( "draw_command_line(), 1: Error while render copy: %s",
983                             SDL_GetError().to!string() );
984                 }
985 
986                 with (gs.command_line)
987                 {
988                     cwd = gs.current_path;
989                     Dbc cursor = gs.db_commands.cursor(null, 0);
990                     scope(exit) cursor.close();
991 
992                     /* Try to find bottom command in commands history */
993                     bool first_cmd_or_out = true;
994 
995                     //writefln("nav_skip_cmd_id=%s", nav_skip_cmd_id);
996                     Dbt key, data;
997                     //writefln("Find prev nav_skip_cmd_id=%d", nav_skip_cmd_id);
998                     ulong id = find_prev_command(cursor, cwd, nav_skip_cmd_id,
999                             key, data);
1000 
1001                     if (id != 0)
1002                     {
1003                         /* The bottom command found */
1004                         int line_height9 = cast(int)(round(SQRT2^^fontsize)*1.2);
1005                         long y_off = y + gs.screen.h - line_height9*3 - 8;
1006                         //writefln("y=%s, y_off=%s", y, y_off);
1007                         do
1008                         {
1009                             /* Loop through all commands */
1010                             string key_string = key.to!(string);
1011                             command_key cmd_key;
1012                             parse_key_for_command(key_string, cmd_key);
1013                             if (cwd != cmd_key.cwd)
1014                             {
1015                                 id = 0;
1016                                 break;
1017                             }
1018                             else
1019                             {
1020                     //writefln("cmd_id=%s found", cmd_key.id);
1021                                 if (fontsize >= 5 && !search_mode)
1022                                 {
1023                                     /* Try to find last output for command */
1024                                     Dbc cursor2 = gs.db_command_output.cursor(null, 0);
1025                                     scope(exit) cursor2.close();
1026 
1027                                     Dbt key2, data2;
1028                                     ulong out_id = cmd_key.id == nav_cmd_id ? nav_out_id : 0;
1029                                     out_id = find_prev_command_out(cursor2, cwd, cmd_key.id, out_id, key2, data2);
1030                                     bool first_out = true;
1031 
1032                                     if (out_id > 0)
1033                                     {
1034                                         /* The last output found */
1035                                         do
1036                                         {
1037                                             /* Loop through all outputs */
1038                                             string key_string2 = key2.to!(string);
1039                                             command_out_key cmd_key2;
1040                                             parse_key_for_command_out(key_string2, cmd_key2);
1041                                             //writefln("cmd_id=%s, out_id=%s", cmd_key2.cmd_id, cmd_key2.out_id);
1042                                             if (cmd_key.id != cmd_key2.cmd_id)
1043                                             {
1044                                                 out_id = 0;
1045                                                 break;
1046                                             }
1047                                             else
1048                                             {
1049                                                 string data_string2 = data2.to!(string);
1050                                                 command_out_data cmd_data2;
1051                                                 parse_data_for_command_out(data_string2, cmd_data2);
1052 
1053                                                 auto color = SDL_Color(0xFF,0xFF,0xFF,0xFF);
1054                                                 if (cmd_data2.pipe == OutPipe.STDERR)
1055                                                 {
1056                                                     color = SDL_Color(0xFF,0x80,0x80,0xFF);
1057                                                 }
1058                                                 int line_height = cast(int)(round(SQRT2^^fontsize)*1.2);
1059 
1060                                                 auto rect = SDL_Rect();
1061                                                 SDL_Rect rt;
1062                                                 Texture_Tick *tt;
1063                                                 final switch(cmd_data2.vers)
1064                                                 {
1065                                                     case CommandsOutVersion.Simple:
1066                                                         //writefln("out_id=%s  cmd_data2.output=%s", cmd_key2.out_id, cmd_data2.output);
1067                                                         rt = gs.text_viewer.font.get_size_of_line(cmd_data2.output, 
1068                                                                 fontsize, gs.screen.w-80, line_height, color);
1069 
1070                                                         int lines = rt.h / line_height - 1;
1071                                                         if (cmd_data2.output.length > 0 && cmd_data2.output[$-1] != '\n') lines++;
1072                                                         y_off -= line_height*lines;
1073 
1074                                                         auto i = 0;
1075                                                         rect.x = 40;
1076                                                         rect.y = cast(int)(y_off + 4 + line_height*i);
1077                                                         rect.w = rt.w;
1078                                                         rect.h = rt.h;
1079 
1080                                                         if (rect.y < gs.screen.h && rect.y+rect.h > 0)
1081                                                         {
1082                                                             /* Selection */
1083                                                             ssize_t start_pos, end_pos;
1084                                                             get_start_end_pos(cmd_key2.cmd_id,
1085                                                                     cmd_key2.out_id,
1086                                                                     start_selection, 
1087                                                                     end_selection,
1088                                                                     start_pos, end_pos);
1089 
1090                                                             tt = gs.text_viewer.font.get_line_from_cache(cmd_data2.output, 
1091                                                                     fontsize, gs.screen.w-80, line_height, color,
1092                                                                     cmd_data2.attrs, start_pos, end_pos);
1093                                                             if (!tt && !tt.texture)
1094                                                             {
1095                                                                 throw new Exception("Can't create text_surface: "~
1096                                                                         to!string(TTF_GetError()));
1097                                                             }
1098                                                             //assert(rt.w == tt.w && rt.h == tt.h, format("rt.w=%d, tt.w=%d, rt.h=%d, tt.h=%d",
1099                                                             //            rt.w, tt.w, rt.h, tt.h));
1100 
1101                                                             r = SDL_RenderCopy(gs.renderer, tt.texture, null, &rect);
1102                                                             if (r < 0)
1103                                                             {
1104                                                                 writefln(
1105                                                                         "draw_command_line(), 2: Error while render copy: %s", 
1106                                                                         SDL_GetError().to!string() );
1107                                                                 writefln("text: %s", cmd_data2.output);
1108                                                             }
1109 
1110                                                             /* Draw cursor */
1111                                                             if (first_out && cmd_data2.pos < tt.chars.length &&
1112                                                                     command_in_focus_id == cmd_key2.cmd_id)
1113                                                             {
1114                                                                 auto rect2= tt.chars[cmd_data2.pos];
1115                                                                 rect2.x += 40;
1116                                                                 rect2.y += cast(int)(y_off + 4 + line_height*i);
1117                                                                 string chr = " ";
1118                                                                 try
1119                                                                 {
1120                                                                 if (cmd_data2.pos >= 0 && cmd_data2.pos < cmd_data2.output.length && cmd_data2.pos + cmd_data2.output.mystride(cmd_data2.pos) < cmd_data2.output.length)
1121                                                                     chr = cmd_data2.output[cmd_data2.pos..cmd_data2.pos+cmd_data2.output.mystride(cmd_data2.pos)];
1122                                                                 }
1123                                                                 catch (UTFException exp)
1124                                                                 {
1125                                                                     chr = " ";
1126                                                                 }
1127                                                                 if (chr == "\n") chr = " ";
1128 
1129                                                                 r = SDL_RenderCopy(gs.renderer, gs.texture_cursor, null, &rect2);
1130                                                                 if (r < 0)
1131                                                                 {
1132                                                                     writefln( "draw_command_line(), 3: Error while render copy: %s",
1133                                                                             SDL_GetError().to!string() );
1134                                                                 }
1135 
1136                                                                 auto st = gs.text_viewer.font.get_char_from_cache(chr, fontsize, SDL_Color(0x00, 0x00, 0x20, 0xFF));
1137                                                                 if (!st) return;
1138 
1139                                                                 r = SDL_RenderCopy(gs.renderer, st.texture, null, &rect2);
1140                                                                 if (r < 0)
1141                                                                 {
1142                                                                     writefln(
1143                                                                             "draw_command_line(), 4: Error while render copy: %s", 
1144                                                                             SDL_GetError().to!string() );
1145                                                                     writefln("chr: %s", chr);
1146                                                                 }
1147                                                             }
1148                                                         }
1149                                                         break;
1150                                                     case CommandsOutVersion.Screen:
1151                                                         rt = gs.text_viewer.font.get_size_of_line(cmd_data2.cols,
1152                                                                cmd_data2.rows, fontsize, color);
1153 
1154                                                         int lines = rt.h / line_height - 1;
1155                                                         if (cmd_data2.output.length > 0 && cmd_data2.output[$-1] != '\n') lines++;
1156                                                         y_off -= line_height*lines;
1157 
1158                                                         auto i = 0;
1159                                                         rect.x = 40;
1160                                                         rect.y = cast(int)(y_off + 4 + line_height*i);
1161                                                         rect.w = rt.w;
1162                                                         rect.h = rt.h;
1163 
1164                                                         if (rect.y < gs.screen.h && rect.y+rect.h > 0)
1165                                                         {
1166                                                             /* Selection */
1167                                                             ssize_t start_pos, end_pos;
1168                                                             get_start_end_pos(cmd_key2.cmd_id,
1169                                                                     cmd_key2.out_id,
1170                                                                     start_selection, 
1171                                                                     end_selection,
1172                                                                     start_pos, end_pos);
1173 
1174                                                             tt = gs.text_viewer.font.get_line_from_cache(cmd_data2.screen, 
1175                                                                     cmd_data2.cols, cmd_data2.rows,
1176                                                                     fontsize, line_height, color,
1177                                                                     cmd_data2.scr_attrs, start_pos, end_pos);
1178                                                             if (!tt && !tt.texture)
1179                                                             {
1180                                                                 throw new Exception("Can't create text_surface: "~
1181                                                                         to!string(TTF_GetError()));
1182                                                             }
1183                                                             //assert(rt.w == tt.w && rt.h == tt.h, format("rt.w=%d, tt.w=%d, rt.h=%d, tt.h=%d",
1184                                                             //            rt.w, tt.w, rt.h, tt.h));
1185 
1186                                                             r = SDL_RenderCopy(gs.renderer, tt.texture, null, &rect);
1187                                                             if (r < 0)
1188                                                             {
1189                                                                 writefln(
1190                                                                         "draw_command_line(), 2: Error while render copy: %s", 
1191                                                                         SDL_GetError().to!string() );
1192                                                                 writefln("text: %s", cmd_data2.output);
1193                                                             }
1194 
1195                                                             /* Draw cursor */
1196                                                             if (first_out && cmd_data2.pos < tt.chars.length &&
1197                                                                     command_in_focus_id == cmd_key2.cmd_id)
1198                                                             {
1199                                                                 auto rect2= tt.chars[cmd_data2.pos];
1200                                                                 rect2.x += 40;
1201                                                                 rect2.y += cast(int)(y_off + 4 + line_height*i);
1202                                                                 string chr = " ";
1203                                                                 try
1204                                                                 {
1205                                                                     chr = to!string(cmd_data2.screen[cmd_data2.pos]);
1206                                                                 }
1207                                                                 catch (UTFException exp)
1208                                                                 {
1209                                                                     chr = " ";
1210                                                                 }
1211                                                                 if (chr == "\n") chr = " ";
1212 
1213                                                                 r = SDL_RenderCopy(gs.renderer, gs.texture_cursor, null, &rect2);
1214                                                                 if (r < 0)
1215                                                                 {
1216                                                                     writefln( "draw_command_line(), 3: Error while render copy: %s",
1217                                                                             SDL_GetError().to!string() );
1218                                                                 }
1219 
1220                                                                 auto st = gs.text_viewer.font.get_char_from_cache(chr, fontsize, SDL_Color(0x00, 0x00, 0x20, 0xFF));
1221                                                                 if (!st) return;
1222 
1223                                                                 r = SDL_RenderCopy(gs.renderer, st.texture, null, &rect2);
1224                                                                 if (r < 0)
1225                                                                 {
1226                                                                     writefln(
1227                                                                             "draw_command_line(), 4: Error while render copy: %s", 
1228                                                                             SDL_GetError().to!string() );
1229                                                                     writefln("chr: %s", chr);
1230                                                                 }
1231                                                             }
1232                                                         }
1233 
1234                                                         break;
1235                                                 }
1236 
1237                                                 first_out = false;
1238 
1239                                                 if (rect.y <= gs.mouse_screen_y && 
1240                                                         rect.y+rect.h-line_height >= gs.mouse_screen_y && neg_y >= 0)
1241                                                 {
1242                                                     mouse.cmd_id = cmd_key2.cmd_id;
1243                                                     mouse.out_id = cmd_key2.out_id;
1244                                                     mouse.pos = get_position_by_chars(
1245                                                             gs.mouse_screen_x - rect.x,
1246                                                             gs.mouse_screen_y - rect.y, tt.chars);
1247                                                     mouse_rel_y = (cast(double)gs.mouse_screen_y-rect.y)/rect.h;
1248                                                     //writefln("OUT: mouse.cmd_id=%s, mouse.out_id=%s, gs.mouse_screen_y=%s rect.y=%s rect.h=%s, mouse_rel_y=%s",
1249                                                     //        mouse.cmd_id, mouse.out_id, gs.mouse_screen_y, rect.y, rect.h, mouse_rel_y);
1250                                                 }
1251 
1252                                                 if (rect.y > gs.screen.h)
1253                                                 {
1254                                                     //writefln("tt.h=%s", tt.h);
1255                                                     //writefln("rect.y-gs.screen.h=%s", rect.y-gs.screen.h);
1256                                                     y -= rt.h - line_height;
1257                                                     nav_out_id = cmd_key2.out_id;
1258                                                     nav_cmd_id = cmd_key.id;
1259                                                     //writefln("OUT UP");
1260                                                 }
1261 
1262                                                 //writefln("OUT: y=%s, first_cmd_or_out=%s, nav_out_id=%s, rect.y + rect.h=%s, gs.screen.h=%s",
1263                                                 //        y, first_cmd_or_out, nav_out_id, rect.y + rect.h, gs.screen.h);
1264                                                 if (y != 0 && first_cmd_or_out && nav_out_id > 0 && (rect.y + rect.h) < gs.screen.h)
1265                                                 {
1266                                                     fix_bottom_line(gs);
1267                                                     goto redraw;
1268                                                 }
1269                                                 first_cmd_or_out = false;
1270 
1271                                                 if (rect.y + rect.h < 0)
1272                                                     break;
1273                                             }
1274                                         }
1275                                         while (cursor2.get(&key2, &data2, DB_PREV) == 0);
1276 
1277                                     }
1278                                 }
1279 
1280                                 //writefln("Excellent");
1281                                 id = cmd_key.id;
1282                                 string data_string = data.to!(string);
1283                                 command_data cmd_data;
1284                                 parse_data_for_command(data_string, cmd_data);
1285 
1286                                 if (search_mode && cmd_data.command.indexOf(search) < 0)
1287                                     continue;
1288 
1289                                 int line_height = cast(int)(round(SQRT2^^9)*1.2);
1290                                 auto rt = gs.text_viewer.font.get_size_of_line(cmd_data.command, 
1291                                         9, gs.screen.w-120, line_height, SDL_Color(0xFF,0x00,0xFF,0xFF));
1292 
1293                                 int lines = rt.h / line_height;
1294                                 y_off -= line_height*lines;
1295 
1296                                 /* Draw Status of cmd*/
1297                                 if (cmd_data.end == 0)
1298                                 {
1299                                     string symbol;
1300                                     SDL_Color color;
1301                                     if (cmd_key.id in gs.tid_by_command_id)
1302                                     {
1303                                         symbol = "⬤";
1304                                         color = SDL_Color(0x00, 0xFF, 0x00, 0xFF);
1305                                     }
1306                                     else
1307                                     {
1308                                         symbol = "◯";
1309                                         color = SDL_Color(0xFF, 0x00, 0x00, 0xFF);
1310                                     }
1311                                     auto tt = gs.text_viewer.font.get_char_from_cache(
1312                                             symbol, 7, color);
1313 
1314                                     auto rect = SDL_Rect();
1315                                     rect.x = 30;
1316                                     rect.y = cast(int)(y_off + 4);
1317                                     rect.w = tt.w;
1318                                     rect.h = tt.h;
1319 
1320                                     r = SDL_RenderCopy(gs.renderer, tt.texture, null, &rect);
1321                                     if (r < 0)
1322                                     {
1323                                         writefln(
1324                                                 "draw_command_line(), 5: Error while render copy: %s", 
1325                                                 SDL_GetError().to!string() );
1326                                     }
1327                                 }
1328                                 else
1329                                 {
1330                                     auto color = SDL_Color(0x00,0xFF,0x00,0xFF);
1331                                     if (cmd_data.status != 0) color = SDL_Color(0xFF,0x00,0x00,0xFF);
1332 
1333                                     auto tt = gs.text_viewer.font.get_line_from_cache(format("%d", cmd_data.status), 
1334                                             8, gs.screen.w-120, line_height, color);
1335                                     if (!tt && !tt.texture)
1336                                     {
1337                                         throw new Exception("Can't create text_surface: "~
1338                                                 to!string(TTF_GetError()));
1339                                     }
1340 
1341                                     auto rect = SDL_Rect();
1342                                     rect.x = 30;
1343                                     rect.y = cast(int)(y_off + 4);
1344                                     rect.w = tt.w;
1345                                     rect.h = tt.h;
1346 
1347                                     r = SDL_RenderCopy(gs.renderer, tt.texture, null, &rect);
1348                                     if (r < 0)
1349                                     {
1350                                         writefln(
1351                                                 "draw_command_line(), 6: Error while render copy: %s", 
1352                                                 SDL_GetError().to!string() );
1353                                     }
1354                                 }
1355 
1356                                 auto i = 0;
1357                                 auto rect = SDL_Rect();
1358                                 rect.x = 60;
1359                                 rect.y = cast(int)(y_off + 4 + line_height*i);
1360                                 rect.w = rt.w;
1361                                 rect.h = rt.h;
1362 
1363                                 Texture_Tick *tt;
1364                                 if (rect.y < gs.screen.h && rect.y+rect.h > 0)
1365                                 {
1366                                     /* Draw command */
1367                                     /* Selection */
1368                                     ssize_t start_pos, end_pos;
1369                                     get_start_end_pos(cmd_key.id,
1370                                             0,
1371                                             start_selection, 
1372                                             end_selection,
1373                                             start_pos, end_pos);
1374                                     tt = gs.text_viewer.font.get_line_from_cache(cmd_data.command, 
1375                                             9, gs.screen.w-80, line_height, SDL_Color(0xFF,0x00,0xFF,0xFF),
1376                                             null, start_pos, end_pos);
1377                                     if (!tt && !tt.texture)
1378                                     {
1379                                         throw new Exception("Can't create text_surface: "~
1380                                                 to!string(TTF_GetError()));
1381                                     }
1382 
1383                                     r = SDL_RenderCopy(gs.renderer, tt.texture, null, &rect);
1384                                     if (r < 0)
1385                                     {
1386                                         writefln(
1387                                                 "draw_command_line(), 7: Error while render copy: %s", 
1388                                                 SDL_GetError().to!string() );
1389                                     }
1390 
1391                                     if (cmd_data.end == 0 && cmd_key.id in gs.tid_by_command_id)
1392                                     {
1393                                         /* Control elements for running processes */
1394                                         auto color = SDL_Color(0x80,0x80,0x00,0xFF);
1395 
1396                                         /*SIGTERM*/
1397                                         auto tt2 = gs.text_viewer.font.get_line_from_cache("TERM", 
1398                                                 8, gs.screen.w-80, line_height, color);
1399                                         if (!tt2 && !tt2.texture)
1400                                         {
1401                                             throw new Exception("Can't create text_surface: "~
1402                                                     to!string(TTF_GetError()));
1403                                         }
1404 
1405                                         auto rect2 = SDL_Rect();
1406                                         rect2.x = rect.x+rect.w + 5;
1407                                         rect2.y = rect.y+3;
1408                                         rect2.w = tt2.w;
1409                                         rect2.h = tt2.h;
1410 
1411                                         if (gs.mouse_screen_x >= rect2.x && gs.mouse_screen_x <= rect2.x+rect2.w &&
1412                                                 gs.mouse_screen_y >= rect2.y && gs.mouse_screen_y <= rect2.y+rect2.h)
1413                                         {
1414                                             color = SDL_Color(0xFF,0xFF,0x00,0xFF);
1415                                             tt2 = gs.text_viewer.font.get_line_from_cache("TERM", 
1416                                                     8, gs.screen.w-80, line_height, color);
1417                                             if (!tt2 && !tt2.texture)
1418                                             {
1419                                                 throw new Exception("Can't create text_surface: "~
1420                                                         to!string(TTF_GetError()));
1421                                             }
1422 
1423                                             auto get_handler1(Tid tid)
1424                                             {
1425                                                 return ()
1426                                                 {
1427                                                     send(tid, "signal", SIGTERM);
1428                                                 };
1429                                             }
1430 
1431                                             on_click = get_handler1(gs.tid_by_command_id[cmd_key.id]);
1432                                         }
1433 
1434                                         r = SDL_RenderCopy(gs.renderer, tt2.texture, null, &rect2);
1435                                         if (r < 0)
1436                                         {
1437                                             writefln(
1438                                                     "draw_command_line(), 8: Error while render copy: %s", 
1439                                                     SDL_GetError().to!string() );
1440                                         }
1441 
1442                                         /*SIGKILL*/
1443                                         color = SDL_Color(0x80,0x00,0x00,0x80);
1444                                         auto tt3 = gs.text_viewer.font.get_line_from_cache("KILL", 
1445                                                 8, gs.screen.w-80, line_height, color);
1446                                         if (!tt3 && !tt3.texture)
1447                                         {
1448                                             throw new Exception("Can't create text_surface: "~
1449                                                     to!string(TTF_GetError()));
1450                                         }
1451 
1452                                         auto rect3 = SDL_Rect();
1453                                         rect3.x = rect2.x+rect2.w + 5;
1454                                         rect3.y = rect2.y;
1455                                         rect3.w = tt3.w;
1456                                         rect3.h = tt3.h;
1457 
1458                                         if (gs.mouse_screen_x >= rect3.x && gs.mouse_screen_x <= rect3.x+rect3.w &&
1459                                                 gs.mouse_screen_y >= rect3.y && gs.mouse_screen_y <= rect3.y+rect3.h)
1460                                         {
1461                                             color = SDL_Color(0xFF,0x00,0x00,0x80);
1462                                             tt3 = gs.text_viewer.font.get_line_from_cache("KILL", 
1463                                                     8, gs.screen.w-80, line_height, color);
1464                                             if (!tt2 && !tt2.texture)
1465                                             {
1466                                                 throw new Exception("Can't create text_surface: "~
1467                                                         to!string(TTF_GetError()));
1468                                             }
1469 
1470                                             auto get_handler2(Tid tid)
1471                                             {
1472                                                 return ()
1473                                                 {
1474 						   version(Posix)
1475 						   { 
1476                                                        send(tid, "signal", SIGKILL);
1477 						   }
1478 						   else version (Windows)
1479 						   {
1480 						       writefln("send SIGKILL Signal in windows not supported (Tid=%s)", tid);
1481 						   }
1482                                                 };
1483                                             }
1484 
1485                                             on_click = get_handler2(gs.tid_by_command_id[cmd_key.id]);
1486                                         }
1487 
1488                                         r = SDL_RenderCopy(gs.renderer, tt3.texture, null, &rect3);
1489                                         if (r < 0)
1490                                         {
1491                                             writefln(
1492                                                     "draw_command_line(), 9: Error while render copy: %s", 
1493                                                     SDL_GetError().to!string() );
1494                                         }
1495                                     }
1496                                     else
1497                                     {
1498                                         /* Control elements for not running processes */
1499 
1500                                         /*PLAY*/
1501                                         auto color = SDL_Color(0x00,0x80,0x00,0xFF);
1502                                         auto tt2 = gs.text_viewer.font.get_char_from_cache(
1503                                                 "⏵", 9, color);
1504 
1505                                         auto rect2 = SDL_Rect();
1506                                         rect2.x = rect.x+rect.w;
1507                                         rect2.y = rect.y;
1508                                         rect2.w = tt2.w;
1509                                         rect2.h = tt2.h;
1510 
1511                                         if (gs.mouse_screen_x >= rect2.x && gs.mouse_screen_x <= rect2.x+rect2.w &&
1512                                                 gs.mouse_screen_y >= rect2.y && gs.mouse_screen_y <= rect2.y+rect2.h)
1513                                         {
1514                                             color = SDL_Color(0x00,0xFF,0x00,0xFF);
1515                                             tt2 = gs.text_viewer.font.get_char_from_cache(
1516                                                     "⏵", 9, color);
1517                                             auto get_handler3(string command)
1518                                             {
1519                                                 return ()
1520                                                 {
1521                                                     run_command(gs, command);
1522                                                 };
1523                                             }
1524                                             on_click = get_handler3(cmd_data.command.idup());
1525                                         }
1526 
1527                                         r = SDL_RenderCopy(gs.renderer, tt2.texture, null, &rect2);
1528                                         if (r < 0)
1529                                         {
1530                                             writefln(
1531                                                     "draw_command_line(), 10: Error while render copy: %s", 
1532                                                     SDL_GetError().to!string() );
1533                                         }
1534 
1535                                         /*REMOVE*/
1536                                         color = SDL_Color(0x80,0x00,0x00,0xFF);
1537                                         auto tt3 = gs.text_viewer.font.get_char_from_cache(
1538                                                 "❌", 9, color);
1539 
1540                                         auto rect3 = SDL_Rect();
1541                                         rect3.x = rect2.x+rect2.w + 5;
1542                                         rect3.y = rect2.y;
1543                                         rect3.w = tt3.w;
1544                                         rect3.h = tt3.h;
1545 
1546                                         if (gs.mouse_screen_x >= rect3.x && gs.mouse_screen_x <= rect3.x+rect3.w &&
1547                                                 gs.mouse_screen_y >= rect3.y && gs.mouse_screen_y <= rect3.y+rect3.h)
1548                                         {
1549                                             color = SDL_Color(0xFF,0x00,0x00,0x80);
1550                                             tt3 = gs.text_viewer.font.get_char_from_cache(
1551                                                     "❌", 9, color);
1552 
1553                                             auto get_handler4(string cwd, ulong cmd_id)
1554                                             {
1555                                                 return ()
1556                                                 {
1557                                                     delete_command(gs, cwd, cmd_id);
1558                                                 };
1559                                             }
1560 
1561                                             on_click = get_handler4(cmd_key.cwd.idup(), cmd_key.id);
1562                                         }
1563 
1564                                         r = SDL_RenderCopy(gs.renderer, tt3.texture, null, &rect3);
1565                                         if (r < 0)
1566                                         {
1567                                             writefln(
1568                                                     "draw_command_line(), 11: Error while render copy: %s", 
1569                                                     SDL_GetError().to!string() );
1570                                         }
1571                                     }
1572 
1573                                 }
1574 
1575                                 if (rect.y <= gs.mouse_screen_y && 
1576                                         rect.y+rect.h >= gs.mouse_screen_y && neg_y >= 0)
1577                                 {
1578                                     mouse.cmd_id = cmd_key.id;
1579                                     mouse.out_id = 0;
1580                                     mouse.pos = get_position_by_chars(
1581                                             gs.mouse_screen_x - rect.x,
1582                                             gs.mouse_screen_y - rect.y, tt.chars);
1583                                     mouse_rel_y = (cast(double)gs.mouse_screen_y-rect.y)/rect.h;
1584                                     //writefln("CMD: mouse.cmd_id=%s, mouse.out_id=%s, command=%s",
1585                                     //        mouse.cmd_id, mouse.out_id, cmd_data.command);
1586                                 }
1587 
1588                                 if (rect.y > gs.screen.h)
1589                                 {
1590                                     y -= rt.h;
1591                                     nav_skip_cmd_id = cmd_key.id;
1592                                     //writefln("CMD UP");
1593                                 }
1594 
1595                                 //writefln("CMD: y=%s, first_cmd_or_out=%s, nav_skip_cmd_id=%s, rect.y + rect.h=%s, gs.screen.h=%s",
1596                                 //        y, first_cmd_or_out, nav_skip_cmd_id, rect.y + rect.h, gs.screen.h);
1597                                 /*if (y != 0 && first_cmd_or_out && nav_skip_cmd_id > 0 && (rect.y + rect.h) < gs.screen.h)
1598                                 {
1599                                     //FYI: This loop maybe be infinite
1600                                     fix_bottom_line(gs);
1601                                     goto redraw;
1602                                 }*/
1603 
1604                                 first_cmd_or_out = false;
1605 
1606                                 if (rect.y + rect.h < 0)
1607                                     break;
1608                             }
1609                         }
1610                         while (cursor.get(&key, &data, DB_PREV) == 0);
1611                     }
1612 
1613                     if (first_cmd_or_out)
1614                     {
1615                         fix_bottom_line(gs);
1616                         //goto redraw;
1617                     }
1618                 }
1619             }
1620 
1621             if (gs.command_line.enter)
1622             {
1623                 long y_off;
1624 
1625                 string prompt = "$ ";
1626                 if (search_mode && hist_pos == 0)
1627                     prompt ="<search> ";
1628                 int line_height = cast(int)(round(SQRT2^^9)*1.2);
1629                 auto ptt = gs.text_viewer.font.get_line_from_cache(prompt, 
1630                         9, gs.screen.w-80, line_height, SDL_Color(0x00,0xFF,0x00,0xFF));
1631                 if (!ptt && !ptt.texture)
1632                 {
1633                     throw new Exception("Can't create text_surface: "~
1634                             to!string(TTF_GetError()));
1635                 }
1636 
1637                 string str = command;
1638                 if (search_mode && hist_pos == 0)
1639                     str = search;
1640 
1641                 auto tt = gs.text_viewer.font.get_line_from_cache(str, 
1642                         9, gs.screen.w-80-ptt.w, line_height, SDL_Color(0xFF, 0xFF, 0xFF, 0xFF),
1643                         null, cmd_start_selection, cmd_end_selection);
1644                 if (!tt && !tt.texture)
1645                 {
1646                     throw new Exception("Can't create text_surface: "~
1647                             to!string(TTF_GetError()));
1648                 }
1649 
1650                 Texture_Tick *ctt;
1651                 if (!search_mode || hist_pos != 0)
1652                 {
1653                     ssize_t ipos = pos;
1654                     if (ipos > command.length) ipos = command.length-1;
1655                     complete = autocomplete(gs, command[0..ipos]);
1656 
1657                     if (complete.length > 1)
1658                     {
1659                         int linewidth = gs.screen.w-80-ptt.w-tt.w;
1660                         if (complete[0] == '2')
1661                         {
1662                             linewidth = gs.screen.w-80;
1663                         }
1664 
1665                         ctt = gs.text_viewer.font.get_line_from_cache(complete[1..$], 
1666                                 9, linewidth, line_height, SDL_Color(0xFF, 0x96, 0x00, 0xFF));
1667                         if (!ctt && !ctt.texture)
1668                         {
1669                             throw new Exception("Can't create text_surface: "~
1670                                     to!string(TTF_GetError()));
1671                         }
1672                     }
1673                 }
1674 
1675                 auto lines = tt.h / line_height;
1676                 y_off = gs.screen.h - line_height*3 - line_height*lines - 8;
1677 
1678                 /* EN: render background of console messages
1679                    RU: рендерим фон консоли сообщений */
1680                 SDL_Rect rect;
1681                 rect.x = 32;
1682                 rect.y = cast(int)y_off;
1683                 rect.w = gs.screen.w - 32*2;
1684                 rect.h = cast(int)(line_height*lines + 8);
1685 
1686                 cmd_rect = rect;
1687 
1688                 if (complete.length > 1 && complete[0] == '2' && ctt !is null)
1689                 {
1690                     rect.y -= ctt.h;
1691                     rect.h += ctt.h;
1692                 }
1693 
1694                 r = SDL_RenderCopy(gs.renderer, gs.texture_black, null, &rect);
1695                 if (r < 0)
1696                 {
1697                     writefln( "draw_command_line(), 8: Error while render copy: %s",
1698                             SDL_GetError().to!string() );
1699                 }
1700                 
1701                 /* EN: Render prompt to screen
1702                    RU: Рендерим приглашение на экран */
1703                 auto i = 0;
1704                 rect = SDL_Rect();
1705                 rect.x = 40;
1706                 rect.y = cast(int)(y_off + 4 + line_height*i);
1707                 rect.w = ptt.w;
1708                 rect.h = ptt.h;
1709 
1710                 r = SDL_RenderCopy(gs.renderer, ptt.texture, null, &rect);
1711                 if (r < 0)
1712                 {
1713                     writefln(
1714                         "draw_command_line(), 9: Error while render copy: %s", 
1715                         SDL_GetError().to!string() );
1716                 }
1717 
1718                 /* EN: Render text to screen
1719                    RU: Рендерим текст на экран */
1720                 rect = SDL_Rect();
1721                 rect.x = 40+ptt.w;
1722                 rect.y = cast(int)(y_off + 4 + line_height*i);
1723                 rect.w = tt.w;
1724                 rect.h = tt.h;
1725 
1726                 cmd_mouse_pos = get_position_by_chars(
1727                         gs.mouse_screen_x - rect.x,
1728                         gs.mouse_screen_y - rect.y, tt.chars);
1729 
1730                 r = SDL_RenderCopy(gs.renderer, tt.texture, null, &rect);
1731                 if (r < 0)
1732                 {
1733                     writefln(
1734                         "draw_command_line(), 10: Error while render copy: %s", 
1735                         SDL_GetError().to!string() );
1736                 }
1737 
1738                 /* EN: Render autocomplete to screen
1739                    RU: Рендерим автодополнение на экран */
1740                 if ((!search_mode || hist_pos != 0) && complete.length > 1)
1741                 {
1742                     rect = SDL_Rect();
1743                     if (complete[0] == '1' && pos == command.length)
1744                     {
1745                         rect.x = 40+ptt.w+tt.w-tt.chars[$-1].w;
1746                         rect.y = cast(int)(y_off + 4 + line_height*i);
1747                         rect.w = ctt.w;
1748                         rect.h = ctt.h;
1749                     }
1750                     else if (complete[0] == '2' || pos < command.length)
1751                     {
1752                         rect.x = 40;
1753                         rect.y = cast(int)(y_off + 4 + line_height*i - ctt.h);
1754                         rect.w = ctt.w;
1755                         rect.h = ctt.h;
1756                     }
1757 
1758                     r = SDL_RenderCopy(gs.renderer, ctt.texture, null, &rect);
1759                     if (r < 0)
1760                     {
1761                         writefln(
1762                             "draw_command_line(), 10: Error while render copy: %s", 
1763                             SDL_GetError().to!string() );
1764                     }
1765                 }
1766 
1767                 /* Render cursor */
1768                 if (pos < tt.chars.length)
1769                 {
1770                     rect = tt.chars[pos];
1771                     rect.x += 40+ptt.w;
1772                     rect.y += cast(int)(y_off + 4 + line_height*i);
1773                     string chr = " ";
1774                     if (pos < str.length)
1775                         chr = str[pos..pos+str.stride(pos)];
1776                     else if (complete.length > 1 && complete[0] == '1')
1777                         chr = complete[1..1+complete.stride(1)];
1778                     if (chr == "\n") chr = " ";
1779 
1780                     r = SDL_RenderCopy(gs.renderer, gs.texture_cursor, null, &rect);
1781                     if (r < 0)
1782                     {
1783                         writefln( "draw_command_line(), 11: Error while render copy: %s",
1784                                 SDL_GetError().to!string() );
1785                     }
1786 
1787                     auto st = gs.text_viewer.font.get_char_from_cache(chr, 9, SDL_Color(0x00, 0x00, 0x20, 0xFF));
1788                     if (!st) return;
1789 
1790                     r = SDL_RenderCopy(gs.renderer, st.texture, null, &rect);
1791                     if (r < 0)
1792                     {
1793                         writefln(
1794                             "draw_command_line(), 12: Error while render copy: %s", 
1795                             SDL_GetError().to!string() );
1796                     }
1797                 }
1798             }
1799 
1800             last_redraw = SDL_GetTicks();
1801 
1802             r = SDL_SetRenderTarget(gs.renderer, old_texture);
1803             if (r < 0)
1804             {
1805                 throw new Exception(format("Error while restore render target: %s",
1806                         fromStringz(SDL_GetError()) ));
1807             }
1808 
1809             gs.text_viewer.font.clear_chars_cache();
1810             gs.text_viewer.font.clear_lines_cache();
1811         }
1812 
1813         SDL_Rect dst = SDL_Rect(0, 0, gs.screen.w, gs.screen.h);
1814         int r = SDL_RenderCopy(gs.renderer, texture, null, &dst);
1815         if (r < 0)
1816         {
1817             writefln( "draw_text(): Error while render copy texture: %s", fromStringz(SDL_GetError()) );
1818         }
1819     }
1820 
1821 }
1822 
1823 void update_winsize(GlobalState gs)
1824 {
1825     if (gs.command_line.fontsize < 5) return;
1826 
1827     int line_height = cast(int)(round(SQRT2^^9)*1.2);
1828     auto h = gs.screen.h - line_height*2;
1829     auto w = gs.screen.w - 80;
1830 
1831     auto st = gs.text_viewer.font.get_char_from_cache(" ", 
1832             gs.command_line.fontsize, SDL_Color(0xFF, 0xFF, 0xFF, 0xFF));
1833     if (!st) return;
1834 
1835    with ( gs.command_line.ws)
1836    {
1837         ws_xpixel = cast(ushort)w;
1838         ws_ypixel = cast(ushort)h;
1839         ws_row = cast(ushort)(h/st.h);
1840         ws_col = cast(ushort)(w/st.w);
1841    }
1842 
1843    foreach (tid; gs.tid_by_command_id)
1844    {
1845        tid.send(gs.command_line.ws);
1846    }
1847 }
1848 
1849 
1850 void
1851 hist_up(GlobalState gs)
1852 {
1853     with (gs.command_line)
1854     {
1855         cmd_start_selection = -1;
1856         cmd_end_selection = -1;
1857 
1858         cwd = gs.current_path;
1859         Dbc cursor = gs.db_commands.cursor(null, 0);
1860         scope(exit) cursor.close();
1861 
1862         Dbt key, data;
1863         ulong id = find_prev_command(cursor, cwd, hist_cmd_id,
1864                 key, data);
1865 
1866         if (id != 0)
1867         {
1868             do
1869             {
1870                 string key_string = key.to!(string);
1871                 command_key cmd_key;
1872                 parse_key_for_command(key_string, cmd_key);
1873                 if (cwd != cmd_key.cwd)
1874                 {
1875                     id = 0;
1876                 }
1877                 else
1878                 {
1879                     string data_string = data.to!(string);
1880                     command_data cmd_data;
1881                     parse_data_for_command(data_string, cmd_data);
1882 
1883                     if (hist_pos == 0)
1884                     {
1885                         edited_command = command;
1886                     }
1887                     hist_pos++;
1888 
1889                     if (search_mode && cmd_data.command.indexOf(search) < 0)
1890                         continue;
1891 
1892                     id = cmd_key.id;
1893                     command = cmd_data.command.idup();
1894                     pos = command.length;
1895                     //writefln("Excellent");
1896                     //writefln("cmd_data.command=%s", cmd_data.command);
1897                     //writefln("pos=%s", pos);
1898                 }
1899                 break;
1900             }
1901             while (cursor.get(&key, &data, DB_PREV) == 0);
1902         }
1903 
1904         if (id == 0)
1905         {
1906             hist_pos = 0;
1907             command = edited_command;
1908             pos = command.length;
1909         }
1910 
1911         hist_cmd_id = id;
1912     }
1913 }
1914 
1915 void
1916 hist_down(GlobalState gs)
1917 {
1918     with (gs.command_line)
1919     {
1920         cmd_start_selection = -1;
1921         cmd_end_selection = -1;
1922 
1923         cwd = gs.current_path;
1924         Dbc cursor = gs.db_commands.cursor(null, 0);
1925         scope(exit) cursor.close();
1926 
1927         Dbt key, data;
1928         ulong id = find_next_command(cursor, cwd, hist_cmd_id,
1929                 key, data);
1930 
1931         if (id != 0)
1932         {
1933             do
1934             {
1935                 string key_string = key.to!(string);
1936                 command_key cmd_key;
1937                 parse_key_for_command(key_string, cmd_key);
1938                 if (cwd != cmd_key.cwd)
1939                 {
1940                     id = 0;
1941                 }
1942                 else
1943                 {
1944                     //writefln("Excellent");
1945                     string data_string = data.to!(string);
1946                     command_data cmd_data;
1947                     parse_data_for_command(data_string, cmd_data);
1948 
1949                     if (hist_pos == 0)
1950                     {
1951                         edited_command = command;
1952                     }
1953                     hist_pos++;
1954 
1955                     if (search_mode && cmd_data.command.indexOf(search) < 0)
1956                         continue;
1957 
1958                     id = cmd_key.id;
1959                     command = cmd_data.command.idup();
1960                     pos = command.length;
1961                 }
1962                 break;
1963             }
1964             while (cursor.get(&key, &data, DB_NEXT) == 0);
1965         }
1966 
1967         if (id == 0)
1968         {
1969             hist_pos = 0;
1970             command = edited_command;
1971             pos = command.length;
1972         }
1973 
1974         gs.command_line.hist_cmd_id = id;
1975     }
1976 }