This source file includes following definitions.
- write_all
- read_nonblock
- init_subshell_child
- init_raw_mode
- synchronize
- read_command_line_buffer
- clear_subshell_prompt_string
- parse_subshell_prompt_string
- set_prompt_string
- peek_subshell_switch_key
- feed_subshell
- pty_open_master
- pty_open_slave
- pty_open_master
- pty_open_slave
- init_subshell_precmd
- create_cd_command
- clear_cwd_pipe
- do_subshell_chdir
- init_subshell
- invoke_subshell
- flush_subshell
- read_subshell_prompt
- do_update_prompt
- exit_subshell
- subshell_chdir
- subshell_get_console_attributes
- sigchld_handler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 #include <config.h>
56
57 #ifndef _GNU_SOURCE
58 #define _GNU_SOURCE 1
59 #endif
60
61 #include <ctype.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <errno.h>
65 #include <string.h>
66 #include <signal.h>
67 #ifdef HAVE_SYS_SELECT_H
68 #include <sys/select.h>
69 #else
70 #include <sys/time.h>
71 #include <unistd.h>
72 #endif
73 #include <sys/types.h>
74 #include <sys/wait.h>
75 #ifdef HAVE_SYS_IOCTL_H
76 #include <sys/ioctl.h>
77 #endif
78 #include <termios.h>
79
80 #ifdef HAVE_STROPTS_H
81 #include <stropts.h>
82 #endif
83
84 #ifdef HAVE_OPENPTY
85
86 #ifdef HAVE_PTY_H
87 #include <pty.h>
88 #endif
89 #ifdef HAVE_UTIL_H
90 #include <util.h>
91 #endif
92
93 #ifdef HAVE_LIBUTIL_H
94 #include <libutil.h>
95 #endif
96 #endif
97
98 #include "lib/global.h"
99
100 #include "lib/fileloc.h"
101 #include "lib/terminal.h"
102 #include "lib/unixcompat.h"
103 #include "lib/tty/tty.h"
104 #include "lib/tty/key.h"
105 #include "lib/vfs/vfs.h"
106 #include "lib/strutil.h"
107 #include "lib/mcconfig.h"
108 #include "lib/util.h"
109 #include "lib/widget.h"
110
111 #include "src/filemanager/layout.h"
112 #include "src/filemanager/command.h"
113
114 #include "subshell.h"
115 #include "internal.h"
116
117
118
119
120
121
122
123 enum subshell_state_enum subshell_state;
124
125
126 GString *subshell_prompt = NULL;
127
128
129
130 gboolean update_subshell_prompt = FALSE;
131
132
133
134 gboolean should_read_new_subshell_prompt;
135
136
137
138 #ifndef WEXITSTATUS
139 #define WEXITSTATUS(stat_val) ((unsigned) (stat_val) >> 8)
140 #endif
141
142 #ifndef WIFEXITED
143 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
144 #endif
145
146
147 #define INITIAL_PROMPT_SIZE 10
148
149
150 #define FORK_FAILURE 69
151
152
153 #define PTY_BUFFER_SIZE BUF_MEDIUM
154
155
156
157 #define COOKED_MODE_BUFFER_SIZE 250
158
159
160
161
162 enum
163 {
164 READ = 0,
165 WRITE = 1
166 };
167
168 typedef enum
169 {
170 MODIFIER_SHIFT = 0x01,
171 MODIFIER_CTRL = 0x04,
172 MODIFIER_CAPS_LOCK = 0x40,
173 MODIFIER_NUM_LOCK = 0x80,
174
175 EVENT_TYPE_PRESS = 1,
176 EVENT_TYPE_REPEAT = 2,
177 } kitty_keyboard_protocol_t;
178
179
180
181 #define SHELL_BUFFER_KEYBINDING "_"
182
183
184
185
186
187
188 static char tcsh_fifo[BUF_SMALL];
189
190 static int subshell_pty_slave = -1;
191
192
193 static char pty_buffer[PTY_BUFFER_SIZE] = "\0";
194
195
196 static int subshell_pipe[2];
197
198
199 static int command_buffer_pipe[2];
200
201
202 static pid_t subshell_pid = 1;
203
204
205 static char subshell_cwd[MC_MAXPATHLEN + 1];
206
207
208 static int subshell_ready;
209
210
211 static gboolean use_persistent_buffer = FALSE;
212
213
214 static GString *subshell_prompt_temp_buffer = NULL;
215
216
217
218 static volatile int subshell_alive, subshell_stopped;
219
220
221
222
223 static struct termios shell_mode;
224
225
226
227
228 static struct termios raw_mode;
229
230
231
232 static gboolean subshell_initialized = FALSE;
233
234
235
236
237
238
239
240
241 static ssize_t
242 write_all (int fd, const void *buf, size_t count)
243 {
244 ssize_t written = 0;
245
246 while (count > 0)
247 {
248 const ssize_t ret = write (fd, (const unsigned char *) buf + written, count);
249
250 if (ret < 0)
251 {
252 if (errno == EINTR)
253 {
254 if (tty_got_winch ())
255 tty_change_screen_size ();
256
257 continue;
258 }
259
260 return written > 0 ? written : ret;
261 }
262 count -= ret;
263 written += ret;
264 }
265 return written;
266 }
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281 static ssize_t
282 read_nonblock (int fd, void *buf, size_t count)
283 {
284 const int old_flags = fcntl (fd, F_GETFL);
285
286 fcntl (fd, F_SETFL, old_flags | O_NONBLOCK);
287
288 const ssize_t ret = read (fd, buf, count);
289
290 fcntl (fd, F_SETFL, old_flags);
291
292 return ret;
293 }
294
295
296
297
298
299
300
301
302
303
304
305 static void
306 init_subshell_child (const char *pty_name)
307 {
308 char *init_file = NULL;
309 pid_t mc_sid;
310
311 (void) pty_name;
312
313 setsid ();
314
315
316
317
318
319 #ifdef TIOCSCTTY
320 ioctl (subshell_pty_slave, TIOCSCTTY, 0);
321 #endif
322
323
324
325
326 if (tcsetattr (subshell_pty_slave, TCSANOW, &shell_mode))
327 {
328 fprintf (stderr, "Cannot set pty terminal modes: %s\r\n", unix_error_string (errno));
329 my_exit (FORK_FAILURE);
330 }
331
332
333
334 tty_resize (subshell_pty_slave);
335
336
337
338
339
340
341 MC_UNUSED const int ret_chdir = chdir (mc_config_get_home_dir ());
342
343
344 mc_sid = getsid (0);
345 if (mc_sid != -1)
346 {
347 char sid_str[BUF_SMALL];
348
349 g_snprintf (sid_str, sizeof (sid_str), "MC_SID=%ld", (long) mc_sid);
350 putenv (g_strdup (sid_str));
351 }
352
353 switch (mc_global.shell->type)
354 {
355 case SHELL_BASH:
356
357 init_file = mc_config_get_full_path (MC_BASHRC_CUSTOM_PROFILE_FILE);
358
359
360 if (!exist_file (init_file))
361 {
362 g_free (init_file);
363 init_file = g_strdup (MC_BASHRC_DEFAULT_PROFILE_FILE);
364 }
365
366
367
368 putenv ((char *) "HISTCONTROL=ignoreboth");
369
370
371 {
372 char *input_file;
373
374 input_file = mc_config_get_full_path (MC_INPUTRC_FILE);
375 if (exist_file (input_file))
376 g_setenv ("INPUTRC", input_file, TRUE);
377 g_free (input_file);
378 }
379
380 break;
381
382 case SHELL_ASH_BUSYBOX:
383 case SHELL_DASH:
384
385 init_file = mc_config_get_full_path (MC_ASHRC_CUSTOM_PROFILE_FILE);
386
387
388 if (!exist_file (init_file))
389 {
390 g_free (init_file);
391 init_file = g_strdup (MC_GENERIC_DEFAULT_PROFILE_FILE);
392 }
393
394
395
396 g_setenv ("ENV", init_file, FALSE);
397
398 break;
399
400 case SHELL_KSH:
401
402 init_file = mc_config_get_full_path (MC_KSHRC_CUSTOM_PROFILE_FILE);
403
404
405 if (!exist_file (init_file))
406 {
407 g_free (init_file);
408 init_file = g_strdup (MC_GENERIC_DEFAULT_PROFILE_FILE);
409 }
410
411
412
413 g_setenv ("ENV", init_file, FALSE);
414
415
416 putenv ((char *) "HISTCONTROL=ignorespace");
417
418 break;
419
420 case SHELL_MKSH:
421
422 init_file = mc_config_get_full_path (MC_MKSHRC_CUSTOM_PROFILE_FILE);
423
424
425 if (!exist_file (init_file))
426 {
427 g_free (init_file);
428 init_file = g_strdup (MC_MKSHRC_DEFAULT_PROFILE_FILE);
429 }
430
431
432
433 g_setenv ("ENV", init_file, FALSE);
434
435
436
437 break;
438
439 case SHELL_ZSH:
440
441
442
443
444 if (g_getenv ("ZDOTDIR") != NULL)
445 {
446
447
448 init_file = mc_config_get_full_path (MC_ZSHRC_CUSTOM_PROFILE_FILE);
449 if (exist_file (init_file))
450 {
451
452 g_setenv ("ZDOTDIR", mc_config_get_data_path (), TRUE);
453 }
454 }
455 break;
456
457
458 case SHELL_TCSH:
459 case SHELL_FISH:
460 break;
461
462 default:
463 fprintf (stderr, __FILE__ ": unimplemented subshell type %u\r\n", mc_global.shell->type);
464 my_exit (FORK_FAILURE);
465 }
466
467
468
469
470
471
472
473 dup2 (subshell_pty_slave, STDIN_FILENO);
474 dup2 (subshell_pty_slave, STDOUT_FILENO);
475 dup2 (subshell_pty_slave, STDERR_FILENO);
476
477 close (subshell_pipe[READ]);
478
479 if (use_persistent_buffer)
480 close (command_buffer_pipe[READ]);
481
482 close (subshell_pty_slave);
483
484
485
486
487
488 close (mc_global.tty.subshell_pty);
489
490
491
492 switch (mc_global.shell->type)
493 {
494 case SHELL_BASH:
495 execl (mc_global.shell->path, mc_global.shell->path, "--rcfile", init_file, (char *) NULL);
496 break;
497
498 case SHELL_ZSH:
499
500
501 execl (mc_global.shell->path, mc_global.shell->path, "-Z", "-g", (char *) NULL);
502 break;
503
504 case SHELL_FISH:
505 execl (mc_global.shell->path, mc_global.shell->path, "--init-command",
506 "set --global __mc_kitty_keyboard 1", (char *) NULL);
507 break;
508
509 case SHELL_ASH_BUSYBOX:
510 case SHELL_DASH:
511 case SHELL_TCSH:
512 case SHELL_KSH:
513 case SHELL_MKSH:
514 execl (mc_global.shell->path, mc_global.shell->path, (char *) NULL);
515 break;
516
517 default:
518 break;
519 }
520
521
522 g_free (init_file);
523 my_exit (FORK_FAILURE);
524 }
525
526
527
528 static void
529 init_raw_mode (void)
530 {
531 static gboolean initialized = FALSE;
532
533
534
535
536
537
538
539 if (!initialized)
540 {
541 tcgetattr (STDOUT_FILENO, &raw_mode);
542 raw_mode.c_lflag &= ~ICANON;
543 raw_mode.c_lflag &= ~ISIG;
544 raw_mode.c_lflag &= ~ECHO;
545 raw_mode.c_iflag &= ~IXON;
546 raw_mode.c_iflag &= ~ICRNL;
547 raw_mode.c_oflag &= ~OPOST;
548 raw_mode.c_cc[VTIME] = 0;
549 raw_mode.c_cc[VMIN] = 1;
550 initialized = TRUE;
551 }
552 }
553
554
555
556
557
558
559
560 static void
561 synchronize (void)
562 {
563 sigset_t sigchld_mask, old_mask;
564
565 sigemptyset (&sigchld_mask);
566 sigaddset (&sigchld_mask, SIGCHLD);
567 sigprocmask (SIG_BLOCK, &sigchld_mask, &old_mask);
568
569
570
571
572
573 sigdelset (&old_mask, SIGCHLD);
574
575
576 while (subshell_alive && !subshell_stopped)
577 {
578
579
580 pselect (0, NULL, NULL, NULL, NULL, &old_mask);
581 }
582
583 if (subshell_state != ACTIVE && subshell_initialized)
584 {
585
586 tcflush (subshell_pty_slave, TCIFLUSH);
587 }
588
589 subshell_stopped = FALSE;
590 kill (subshell_pid, SIGCONT);
591
592 sigprocmask (SIG_SETMASK, &old_mask, NULL);
593
594 }
595
596
597
598
599
600
601 static gboolean
602 read_command_line_buffer (gboolean test_mode)
603 {
604 char subshell_response_buffer[BUF_LARGE];
605 size_t response_char_length = 0;
606
607 char *subshell_command;
608 int command_length;
609
610 fd_set read_set;
611 ssize_t bytes;
612 struct timeval subshell_prompt_timer = {
613 .tv_sec = 0,
614 .tv_usec = 0,
615 };
616 int bash_version;
617 int cursor_position;
618 int rc;
619
620 if (!use_persistent_buffer)
621 return TRUE;
622
623 FD_ZERO (&read_set);
624 FD_SET (command_buffer_pipe[READ], &read_set);
625
626 const int maxfdp = MAX (command_buffer_pipe[READ], mc_global.tty.subshell_pty);
627
628
629
630
631
632 while ((rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer)) != 0)
633 {
634 if (rc == -1)
635 {
636 if (errno == EINTR)
637 continue;
638
639 return FALSE;
640 }
641
642 if (rc == 1)
643 {
644 bytes = read (command_buffer_pipe[READ], subshell_response_buffer,
645 sizeof (subshell_response_buffer));
646 (void) bytes;
647 }
648 }
649
650
651 write_all (mc_global.tty.subshell_pty, ESC_STR SHELL_BUFFER_KEYBINDING,
652 sizeof (ESC_STR SHELL_BUFFER_KEYBINDING) - 1);
653
654
655 subshell_prompt_timer.tv_sec = 1;
656
657 while (TRUE)
658 {
659 FD_ZERO (&read_set);
660 FD_SET (command_buffer_pipe[READ], &read_set);
661 FD_SET (mc_global.tty.subshell_pty, &read_set);
662
663 rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer);
664
665 if (rc == -1)
666 {
667 if (errno == EINTR)
668 continue;
669
670 return FALSE;
671 }
672
673 if (rc == 0)
674 return FALSE;
675
676
677
678
679
680
681
682
683
684
685
686
687 if (FD_ISSET (mc_global.tty.subshell_pty, &read_set))
688 flush_subshell (0, test_mode ? QUIETLY : VISIBLY);
689
690 if (FD_ISSET (command_buffer_pipe[READ], &read_set))
691 {
692 bytes =
693 read (command_buffer_pipe[READ], subshell_response_buffer + response_char_length,
694 sizeof (subshell_response_buffer) - response_char_length);
695 if (bytes <= 0
696 || (size_t) bytes == sizeof (subshell_response_buffer) - response_char_length)
697 return FALSE;
698
699
700
701 const size_t latest_chunk_data_length =
702 strnlen (subshell_response_buffer + response_char_length, bytes);
703
704 if (latest_chunk_data_length < (size_t) bytes)
705 {
706
707 response_char_length += latest_chunk_data_length;
708 break;
709 }
710
711 response_char_length += bytes;
712 }
713 }
714
715
716 if (mc_global.shell->type == SHELL_FISH)
717 {
718 if (response_char_length == 0)
719 return FALSE;
720 if (subshell_response_buffer[response_char_length - 1] != '\n')
721 return FALSE;
722 subshell_response_buffer[--response_char_length] = '\0';
723 }
724
725
726 if (mc_global.shell->type == SHELL_BASH)
727 {
728 if (sscanf (subshell_response_buffer, "%d:%d", &bash_version, &cursor_position) != 2)
729 return FALSE;
730 }
731 else
732 {
733 if (sscanf (subshell_response_buffer, "%d", &cursor_position) != 1)
734 return FALSE;
735 bash_version = 1000;
736 }
737
738
739 subshell_command = strchr (subshell_response_buffer, '\n');
740 if (subshell_command == NULL)
741 return FALSE;
742 subshell_command++;
743 command_length = str_length (subshell_command);
744
745
746 if (test_mode)
747 return TRUE;
748
749
750
751 for (unsigned char *p = (unsigned char *) subshell_command; *p != '\0'; p++)
752 if (*p < 32 || *p == 127)
753 *p = ' ';
754
755 input_assign_text (cmdline, "");
756 input_insert (cmdline, subshell_command, FALSE);
757
758 if (bash_version < 5)
759 {
760
761
762 char *curr, *stop;
763
764 curr = subshell_command;
765 stop = curr + cursor_position;
766
767 for (cursor_position = 0; curr < stop; cursor_position++)
768 str_next_char_safe (&curr);
769 }
770 if (cursor_position > command_length)
771 cursor_position = command_length;
772 cmdline->point = cursor_position;
773
774
775 flush_subshell (0, VISIBLY);
776
777
778 if (mc_global.shell->type != SHELL_ZSH)
779 {
780
781
782
783
784 if (cursor_position != command_length)
785 {
786 write_all (mc_global.tty.subshell_pty, "\005", 1);
787 if (flush_subshell (1, VISIBLY) != 1)
788 return FALSE;
789 }
790 }
791
792 if (command_length > 0)
793 {
794
795 write_all (mc_global.tty.subshell_pty, "\025", 1);
796 if (flush_subshell (1, VISIBLY) != 1)
797 return FALSE;
798 }
799
800 return TRUE;
801 }
802
803
804
805 static void
806 clear_subshell_prompt_string (void)
807 {
808 if (subshell_prompt_temp_buffer != NULL)
809 g_string_set_size (subshell_prompt_temp_buffer, 0);
810 }
811
812
813
814 static void
815 parse_subshell_prompt_string (const char *buffer, size_t bytes)
816 {
817 if (mc_global.mc_run_mode != MC_RUN_FULL)
818 return;
819
820
821 if (subshell_prompt == NULL)
822 subshell_prompt = g_string_sized_new (INITIAL_PROMPT_SIZE);
823 if (subshell_prompt_temp_buffer == NULL)
824 subshell_prompt_temp_buffer = g_string_sized_new (INITIAL_PROMPT_SIZE);
825
826
827 for (size_t i = 0; i < bytes; i++)
828 if (buffer[i] == '\n' || buffer[i] == '\r')
829 g_string_set_size (subshell_prompt_temp_buffer, 0);
830 else if (buffer[i] != '\0')
831 g_string_append_c (subshell_prompt_temp_buffer, buffer[i]);
832 }
833
834
835
836 static void
837 set_prompt_string (void)
838 {
839 if (mc_global.mc_run_mode != MC_RUN_FULL)
840 return;
841
842 if (subshell_prompt_temp_buffer->len != 0)
843 mc_g_string_copy (subshell_prompt, subshell_prompt_temp_buffer);
844
845 setup_cmdline ();
846 }
847
848
849
850 static gboolean
851 peek_subshell_switch_key (const char *buffer, size_t len)
852 {
853 csi_command_t csi;
854
855 if (len == 0)
856 return FALSE;
857 if (buffer[0] == (XCTRL ('o') & 255))
858 return TRUE;
859
860
861 if (len == 1)
862 return FALSE;
863 if (buffer[0] != ESC_CHAR || buffer[1] != '[')
864 return FALSE;
865
866 buffer += 2;
867 len -= 2;
868
869 if (!parse_csi (&csi, &buffer, buffer + len))
870 return FALSE;
871 if (csi.private_mode != '\0' || buffer[-1] != 'u')
872 return FALSE;
873 if (csi.param_count != 2)
874 return FALSE;
875 if (csi.params[1][0] == 0)
876 return FALSE;
877
878 const uint32_t codepoint = csi.params[0][0];
879 const uint32_t modifiers = csi.params[1][0] - 1;
880 const uint32_t event = csi.params[1][1];
881
882 if (event != 0 && event != EVENT_TYPE_PRESS && event != EVENT_TYPE_REPEAT)
883 return FALSE;
884
885 return codepoint == 'o'
886 && (modifiers & ~(MODIFIER_CAPS_LOCK | MODIFIER_NUM_LOCK)) == MODIFIER_CTRL;
887 }
888
889
890
891
892 static gboolean
893 feed_subshell (int how, gboolean fail_on_error)
894 {
895 fd_set read_set;
896
897 struct timeval wtime;
898 struct timeval *wptr;
899
900 should_read_new_subshell_prompt = FALSE;
901
902
903
904 wtime.tv_sec = 10;
905 wtime.tv_usec = 0;
906 wptr = fail_on_error ? &wtime : NULL;
907
908 while (TRUE)
909 {
910 int maxfdp;
911
912 if (!subshell_alive)
913 return FALSE;
914
915
916
917 FD_ZERO (&read_set);
918 FD_SET (mc_global.tty.subshell_pty, &read_set);
919 FD_SET (subshell_pipe[READ], &read_set);
920 maxfdp = MAX (mc_global.tty.subshell_pty, subshell_pipe[READ]);
921 if (how == VISIBLY)
922 {
923 FD_SET (STDIN_FILENO, &read_set);
924 maxfdp = MAX (maxfdp, STDIN_FILENO);
925 }
926
927 if (select (maxfdp + 1, &read_set, NULL, NULL, wptr) == -1)
928 {
929
930 if (errno == EINTR)
931 {
932 if (tty_got_winch ())
933 tty_change_screen_size ();
934
935 continue;
936 }
937 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
938 fprintf (stderr, "select (FD_SETSIZE, &read_set...): %s\r\n",
939 unix_error_string (errno));
940 exit (EXIT_FAILURE);
941 }
942
943 if (FD_ISSET (mc_global.tty.subshell_pty, &read_set))
944
945
946
947
948
949
950 {
951 const ssize_t bytes =
952 read_nonblock (mc_global.tty.subshell_pty, pty_buffer, sizeof (pty_buffer));
953
954 if (bytes == -1)
955 {
956 if (errno == EAGAIN || errno == EWOULDBLOCK)
957 continue;
958
959 if (errno == EIO && !subshell_alive)
960
961 return FALSE;
962 }
963
964 if (bytes <= 0)
965 {
966 #ifdef PTY_ZEROREAD
967
968 continue;
969 #else
970 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
971 fprintf (stderr, "read (subshell_pty...): %s\r\n", unix_error_string (errno));
972 exit (EXIT_FAILURE);
973 #endif
974 }
975
976 if (how == VISIBLY)
977 write_all (STDOUT_FILENO, pty_buffer, (size_t) bytes);
978
979 if (should_read_new_subshell_prompt)
980 parse_subshell_prompt_string (pty_buffer, (size_t) bytes);
981 }
982
983 else if (FD_ISSET (subshell_pipe[READ], &read_set))
984
985
986
987
988 {
989
990
991
992
993 synchronize ();
994
995 const ssize_t bytes = read (subshell_pipe[READ], subshell_cwd, sizeof (subshell_cwd));
996
997
998
999
1000
1001 if (bytes <= 0)
1002 {
1003 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
1004 fprintf (stderr, "read (subshell_pipe[READ]...): %s\r\n",
1005 unix_error_string (errno));
1006 exit (EXIT_FAILURE);
1007 }
1008
1009 subshell_cwd[(size_t) bytes - 1] = '\0';
1010
1011 clear_subshell_prompt_string ();
1012 should_read_new_subshell_prompt = TRUE;
1013 subshell_ready = TRUE;
1014 if (subshell_state == RUNNING_COMMAND)
1015 {
1016 subshell_state = INACTIVE;
1017 return TRUE;
1018 }
1019 }
1020
1021 else if (FD_ISSET (STDIN_FILENO, &read_set))
1022
1023 {
1024 should_read_new_subshell_prompt = FALSE;
1025
1026 const ssize_t bytes = read (STDIN_FILENO, pty_buffer, sizeof (pty_buffer));
1027
1028 if (bytes <= 0)
1029 {
1030 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
1031 fprintf (stderr, "read (STDIN_FILENO, pty_buffer...): %s\r\n",
1032 unix_error_string (errno));
1033 exit (EXIT_FAILURE);
1034 }
1035
1036
1037 const size_t ubytes = (size_t) bytes;
1038
1039 for (size_t i = 0; i < ubytes; i++)
1040 if (peek_subshell_switch_key (pty_buffer + i, ubytes - i))
1041 {
1042 write_all (mc_global.tty.subshell_pty, pty_buffer, i);
1043
1044 if (subshell_ready)
1045 {
1046 subshell_state = INACTIVE;
1047 set_prompt_string ();
1048 if (subshell_ready && !read_command_line_buffer (FALSE))
1049 {
1050
1051 if (subshell_initialized && mc_global.shell->type != SHELL_FISH)
1052 {
1053 write_all (mc_global.tty.subshell_pty, "\003", 1);
1054 subshell_state = RUNNING_COMMAND;
1055 if (feed_subshell (QUIETLY, TRUE)
1056 && read_command_line_buffer (FALSE))
1057 return TRUE;
1058 }
1059
1060 subshell_state = ACTIVE;
1061 flush_subshell (0, VISIBLY);
1062 input_assign_text (cmdline, "");
1063 }
1064 }
1065
1066 return TRUE;
1067 }
1068
1069 write_all (mc_global.tty.subshell_pty, pty_buffer, ubytes);
1070
1071 if (pty_buffer[ubytes - 1] == '\n' || pty_buffer[ubytes - 1] == '\r')
1072 {
1073
1074
1075 if (use_persistent_buffer)
1076 input_assign_text (cmdline, "");
1077 subshell_ready = FALSE;
1078 }
1079 }
1080 else
1081 return FALSE;
1082 }
1083 }
1084
1085
1086
1087
1088 #ifndef HAVE_OPENPTY
1089
1090 #ifdef HAVE_GRANTPT
1091
1092
1093
1094 static int
1095 pty_open_master (char *pty_name)
1096 {
1097 char *slave_name;
1098
1099 #ifdef HAVE_POSIX_OPENPT
1100 const int pty_master = posix_openpt (O_RDWR);
1101 #elif defined HAVE_GETPT
1102
1103 const int pty_master = getpt ();
1104 #elif defined IS_AIX
1105 strcpy (pty_name, "/dev/ptc");
1106 const int pty_master = open (pty_name, O_RDWR);
1107 #else
1108 strcpy (pty_name, "/dev/ptmx");
1109 const int pty_master = open (pty_name, O_RDWR);
1110 #endif
1111
1112 if (pty_master == -1)
1113 return -1;
1114
1115 if (grantpt (pty_master) == -1
1116 || unlockpt (pty_master) == -1
1117 || (slave_name = ptsname (pty_master)) == NULL)
1118 {
1119 close (pty_master);
1120 return -1;
1121 }
1122 strcpy (pty_name, slave_name);
1123 return pty_master;
1124 }
1125
1126
1127
1128
1129 static int
1130 pty_open_slave (const char *pty_name)
1131 {
1132 const int pty_slave = open (pty_name, O_RDWR);
1133
1134 if (pty_slave == -1)
1135 {
1136 fprintf (stderr, "open (%s, O_RDWR): %s\r\n", pty_name, unix_error_string (errno));
1137 return -1;
1138 }
1139 #if !defined(__osf__) && !defined(__linux__)
1140 #if defined(I_FIND) && defined(I_PUSH)
1141 if (ioctl (pty_slave, I_FIND, "ptem") == 0 && ioctl (pty_slave, I_PUSH, "ptem") == -1)
1142 {
1143 fprintf (stderr, "ioctl (%d, I_PUSH, \"ptem\") failed: %s\r\n", pty_slave,
1144 unix_error_string (errno));
1145 close (pty_slave);
1146 return -1;
1147 }
1148
1149 if (ioctl (pty_slave, I_FIND, "ldterm") == 0 && ioctl (pty_slave, I_PUSH, "ldterm") == -1)
1150 {
1151 fprintf (stderr, "ioctl (%d, I_PUSH, \"ldterm\") failed: %s\r\n", pty_slave,
1152 unix_error_string (errno));
1153 close (pty_slave);
1154 return -1;
1155 }
1156 #if !defined(sgi) && !defined(__sgi)
1157 if (ioctl (pty_slave, I_FIND, "ttcompat") == 0 && ioctl (pty_slave, I_PUSH, "ttcompat") == -1)
1158 {
1159 fprintf (stderr, "ioctl (%d, I_PUSH, \"ttcompat\") failed: %s\r\n", pty_slave,
1160 unix_error_string (errno));
1161 close (pty_slave);
1162 return -1;
1163 }
1164 #endif
1165 #endif
1166 #endif
1167
1168 fcntl (pty_slave, F_SETFD, FD_CLOEXEC);
1169 return pty_slave;
1170 }
1171
1172 #else
1173
1174
1175
1176
1177 static int
1178 pty_open_master (char *pty_name)
1179 {
1180 strcpy (pty_name, "/dev/ptyXX");
1181
1182 for (const char *ptr1 = "pqrstuvwxyzPQRST"; *ptr1 != '\0'; ++ptr1)
1183 {
1184 pty_name[8] = *ptr1;
1185
1186 for (const char *ptr2 = "0123456789abcdef"; *ptr2 != '\0'; ++ptr2)
1187 {
1188 pty_name[9] = *ptr2;
1189
1190
1191 const int pty_master = open (pty_name, O_RDWR);
1192
1193 if (pty_master == -1)
1194 {
1195 if (errno == ENOENT)
1196 return -1;
1197 continue;
1198 }
1199 pty_name[5] = 't';
1200 if (access (pty_name, 6) != 0)
1201 {
1202 close (pty_master);
1203 pty_name[5] = 'p';
1204 continue;
1205 }
1206 return pty_master;
1207 }
1208 }
1209 return -1;
1210 }
1211
1212
1213
1214
1215 static int
1216 pty_open_slave (const char *pty_name)
1217 {
1218 struct group *group_info = getgrnam ("tty");
1219
1220 if (group_info != NULL)
1221 {
1222
1223
1224
1225
1226 }
1227
1228 const int pty_slave = open (pty_name, O_RDWR);
1229
1230 if (pty_slave == -1)
1231 fprintf (stderr, "open (pty_name, O_RDWR): %s\r\n", pty_name);
1232 fcntl (pty_slave, F_SETFD, FD_CLOEXEC);
1233 return pty_slave;
1234 }
1235 #endif
1236
1237 #endif
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261 static gchar *
1262 init_subshell_precmd (void)
1263 {
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281 static const char *precmd_fallback = " mc_precmd() { \\\n"
1282 " pwd >&%d; \\\n"
1283 " kill -STOP $$; \\\n"
1284 " }; \\\n"
1285 " MC_PRECMD=mc_precmd; \\\n"
1286 " PS1='$($MC_PRECMD)'\"$PS1\"\n";
1287
1288 switch (mc_global.shell->type)
1289 {
1290 case SHELL_BASH:
1291 return g_strdup_printf (
1292 " mc_print_command_buffer () { printf '%%s:%%s\\n%%s\\000' \"$BASH_VERSINFO\" "
1293 "\"$READLINE_POINT\" \"$READLINE_LINE\" >&%d; }\n"
1294 " bind -x '\"\\e" SHELL_BUFFER_KEYBINDING "\":\"mc_print_command_buffer\"'\n"
1295 " if test $BASH_VERSINFO -ge 5 && [[ ${PROMPT_COMMAND@a} == *a* ]] 2> "
1296 "/dev/null; then \\\n"
1297 " PROMPT_COMMAND+=( 'pwd >&%d; kill -STOP $$' ); \\\n"
1298 " else \\\n"
1299 " PROMPT_COMMAND=${PROMPT_COMMAND:+$PROMPT_COMMAND\n}'pwd >&%d; kill -STOP $$'; \\\n"
1300 " fi\n",
1301 command_buffer_pipe[WRITE], subshell_pipe[WRITE], subshell_pipe[WRITE]);
1302
1303 case SHELL_ASH_BUSYBOX:
1304
1305 return g_strdup_printf (precmd_fallback, subshell_pipe[WRITE]);
1306
1307 case SHELL_DASH:
1308 return g_strdup_printf (precmd_fallback, subshell_pipe[WRITE]);
1309
1310 case SHELL_MKSH:
1311 return g_strdup_printf (precmd_fallback, subshell_pipe[WRITE]);
1312
1313 case SHELL_KSH:
1314 return g_strdup_printf (" PS1='$(pwd >&%d; kill -STOP $$)'\"$PS1\"\n",
1315 subshell_pipe[WRITE]);
1316
1317 case SHELL_ZSH:
1318 return g_strdup_printf (
1319 " mc_print_command_buffer () { printf '%%s\\n%%s\\000' \"$CURSOR\" \"$BUFFER\" "
1320 ">&%d; }; \\\n"
1321 " zle -N mc_print_command_buffer; \\\n"
1322 " bindkey '^[" SHELL_BUFFER_KEYBINDING "' mc_print_command_buffer; \\\n"
1323 " _mc_precmd() { pwd >&%d; kill -STOP $$; }; \\\n"
1324 " precmd_functions+=(_mc_precmd)\n",
1325 command_buffer_pipe[WRITE], subshell_pipe[WRITE]);
1326
1327 case SHELL_TCSH:
1328
1329 return g_strdup_printf (" set echo_style=both;"
1330 " alias precmd 'echo -n; echo $cwd:q >%s; kill -STOP $$'\n",
1331 tcsh_fifo);
1332
1333 case SHELL_FISH:
1334 return g_strdup_printf (
1335 " bind \\e" SHELL_BUFFER_KEYBINDING
1336 " \"begin; commandline -C; commandline; printf '\\000'; end >&%d\"; \\\n"
1337 " if not functions -q fish_prompt_mc; \\\n"
1338 " functions -e fish_right_prompt; \\\n"
1339 " functions -c fish_prompt fish_prompt_mc; \\\n"
1340 " end; \\\n"
1341 " function fish_prompt; \\\n"
1342 " echo \"$PWD\" >&%d; kill -STOP $fish_pid; fish_prompt_mc; \\\n"
1343 " end\n",
1344 command_buffer_pipe[WRITE], subshell_pipe[WRITE]);
1345 default:
1346 fprintf (stderr, "subshell: unknown shell type (%u), aborting!\r\n", mc_global.shell->type);
1347 exit (EXIT_FAILURE);
1348 }
1349 }
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366 static GString *
1367 create_cd_command (const char *s)
1368 {
1369 GString *ret;
1370 const char *n;
1371 const char *quote_cmd_start, *before_wrap, *after_wrap, *quote_cmd_end;
1372 const char *escape_fmt;
1373 int line_length;
1374 char buf[BUF_TINY];
1375
1376 if (mc_global.shell->type == SHELL_BASH || mc_global.shell->type == SHELL_ZSH)
1377 {
1378
1379
1380
1381
1382
1383
1384
1385 quote_cmd_start = " cd $'";
1386 before_wrap = "'\\";
1387 after_wrap = "$'";
1388 quote_cmd_end = "'";
1389 escape_fmt = "\\%03o";
1390 }
1391 else if (mc_global.shell->type == SHELL_FISH)
1392 {
1393
1394
1395
1396
1397
1398
1399
1400
1401 quote_cmd_start = " cd ";
1402 before_wrap = "\\";
1403 after_wrap = "";
1404 quote_cmd_end = "";
1405 escape_fmt = "\\x%02X";
1406 }
1407 else if (mc_global.shell->type == SHELL_TCSH)
1408 {
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421 quote_cmd_start = " set _mc_newdir=$'";
1422 before_wrap = "'; \\";
1423 after_wrap = " set _mc_newdir=${_mc_newdir:q}$'";
1424 quote_cmd_end = "'; cd ${_mc_newdir:q}";
1425 escape_fmt = "\\%03o";
1426 }
1427 else
1428 {
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439 quote_cmd_start = " _mc_newdir_=`printf '%b_' '";
1440 before_wrap = "'\\";
1441 after_wrap = "'";
1442 quote_cmd_end = "'`; cd \"${_mc_newdir_%_}\"";
1443 escape_fmt = "\\0%03o";
1444 }
1445
1446 const int quote_cmd_start_len = (int) strlen (quote_cmd_start);
1447 const int before_wrap_len = (int) strlen (before_wrap);
1448 const int after_wrap_len = (int) strlen (after_wrap);
1449 const int quote_cmd_end_len = (int) strlen (quote_cmd_end);
1450
1451
1452
1453 const int escaped_char_len = sprintf (buf, escape_fmt, 0xFF);
1454
1455 ret = g_string_sized_new (64);
1456
1457
1458 g_string_append_len (ret, quote_cmd_start, quote_cmd_start_len);
1459
1460
1461 if (s[0] == '-')
1462 g_string_append (ret, "./");
1463
1464
1465
1466
1467 const int max_length = COOKED_MODE_BUFFER_SIZE - MAX (before_wrap_len, quote_cmd_end_len);
1468
1469 g_assert (max_length >= 64);
1470
1471 line_length = (int) ret->len;
1472
1473
1474 for (const char *su = s; su[0] != '\0'; su = n)
1475 {
1476 n = str_cget_next_char_safe (su);
1477
1478
1479
1480
1481
1482
1483 if ((unsigned char) su[0] >= 0x80 || g_ascii_isalnum (su[0])
1484 || strchr ("/.-_", su[0]) != NULL)
1485 {
1486
1487 if (line_length + (n - su) > max_length)
1488 {
1489
1490 g_string_append_len (ret, before_wrap, before_wrap_len);
1491 g_string_append_c (ret, '\n');
1492 g_string_append_len (ret, after_wrap, after_wrap_len);
1493 line_length = after_wrap_len;
1494 }
1495
1496 g_string_append_len (ret, su, (size_t) (n - su));
1497 line_length += (n - su);
1498 }
1499 else
1500
1501
1502 for (size_t c = 0; c < (size_t) (n - su); c++)
1503 {
1504 if (line_length + escaped_char_len > max_length)
1505 {
1506
1507 g_string_append_len (ret, before_wrap, before_wrap_len);
1508 g_string_append_c (ret, '\n');
1509 g_string_append_len (ret, after_wrap, after_wrap_len);
1510 line_length = after_wrap_len;
1511 }
1512
1513 g_string_append_printf (ret, escape_fmt, (unsigned char) su[c]);
1514 line_length += escaped_char_len;
1515 }
1516 }
1517
1518 g_string_append_len (ret, quote_cmd_end, quote_cmd_end_len);
1519
1520 return ret;
1521 }
1522
1523
1524
1525
1526
1527
1528
1529 static void
1530 clear_cwd_pipe (void)
1531 {
1532 fd_set read_set;
1533 struct timeval wtime = { 0, 0 };
1534
1535 FD_ZERO (&read_set);
1536 FD_SET (subshell_pipe[READ], &read_set);
1537
1538 const int maxfdp = subshell_pipe[READ];
1539
1540 if (select (maxfdp + 1, &read_set, NULL, NULL, &wtime) > 0
1541 && FD_ISSET (subshell_pipe[READ], &read_set))
1542 {
1543 if (read (subshell_pipe[READ], subshell_cwd, sizeof (subshell_cwd)) <= 0)
1544 {
1545 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
1546 fprintf (stderr, "read (subshell_pipe[READ]...): %s\r\n", unix_error_string (errno));
1547 exit (EXIT_FAILURE);
1548 }
1549
1550 synchronize ();
1551 }
1552 }
1553
1554
1555
1556 static void
1557 do_subshell_chdir (const vfs_path_t *vpath, gboolean force, gboolean update_prompt)
1558 {
1559 char *pcwd;
1560
1561 pcwd = vfs_path_to_str_flags (subshell_get_cwd (), 0, VPF_RECODE);
1562
1563 if (!force && !(subshell_state == INACTIVE && strcmp (subshell_cwd, pcwd) != 0))
1564 {
1565
1566
1567
1568
1569 if (update_prompt)
1570 do_update_prompt ();
1571 g_free (pcwd);
1572 return;
1573 }
1574
1575
1576
1577 if (!use_persistent_buffer && subshell_initialized)
1578 {
1579 write_all (mc_global.tty.subshell_pty, "\003", 1);
1580 subshell_state = RUNNING_COMMAND;
1581 if (mc_global.shell->type != SHELL_FISH && !feed_subshell (QUIETLY, TRUE))
1582 {
1583 subshell_state = ACTIVE;
1584 return;
1585 }
1586 }
1587
1588
1589
1590
1591
1592 if (mc_global.shell->type == SHELL_FISH)
1593 {
1594 write_all (mc_global.tty.subshell_pty, "\n", 1);
1595 subshell_state = RUNNING_COMMAND;
1596 feed_subshell (QUIETLY, TRUE);
1597 }
1598
1599 if (vpath == NULL)
1600 {
1601
1602
1603 const char *cmd = " cd /";
1604
1605 write_all (mc_global.tty.subshell_pty, cmd, sizeof (cmd) - 1);
1606 }
1607 else
1608 {
1609 const char *translate = vfs_translate_path (vfs_path_as_str (vpath));
1610
1611 if (translate == NULL)
1612 {
1613 const char *cmd = " cd .";
1614
1615 write_all (mc_global.tty.subshell_pty, cmd, sizeof (cmd) - 1);
1616 }
1617 else
1618 {
1619 GString *temp;
1620
1621 temp = create_cd_command (translate);
1622 write_all (mc_global.tty.subshell_pty, temp->str, temp->len);
1623 g_string_free (temp, TRUE);
1624 }
1625 }
1626
1627 write_all (mc_global.tty.subshell_pty, "\n", 1);
1628
1629 subshell_state = RUNNING_COMMAND;
1630 if (!feed_subshell (QUIETLY, TRUE))
1631 {
1632 subshell_state = ACTIVE;
1633 return;
1634 }
1635
1636 if (subshell_alive)
1637 {
1638 gboolean bPathNotEq;
1639
1640 bPathNotEq = strcmp (subshell_cwd, pcwd) != 0;
1641
1642 if (bPathNotEq && mc_global.shell->type == SHELL_TCSH)
1643 {
1644 char rp_subshell_cwd[PATH_MAX];
1645 char rp_current_panel_cwd[PATH_MAX];
1646 char *p_subshell_cwd, *p_current_panel_cwd;
1647
1648 p_subshell_cwd = mc_realpath (subshell_cwd, rp_subshell_cwd);
1649 p_current_panel_cwd = mc_realpath (pcwd, rp_current_panel_cwd);
1650
1651 if (p_subshell_cwd == NULL)
1652 p_subshell_cwd = subshell_cwd;
1653 if (p_current_panel_cwd == NULL)
1654 p_current_panel_cwd = pcwd;
1655 bPathNotEq = strcmp (p_subshell_cwd, p_current_panel_cwd) != 0;
1656 }
1657
1658 if (bPathNotEq && !DIR_IS_DOT (pcwd))
1659 {
1660 char *cwd;
1661
1662 cwd = vfs_path_to_str_flags (subshell_get_cwd (), 0, VPF_STRIP_PASSWORD);
1663 vfs_print_message (_ ("Warning: Cannot change to %s.\n"), cwd);
1664 g_free (cwd);
1665 }
1666 }
1667
1668
1669 if (mc_global.shell->type == SHELL_ZSH || mc_global.shell->type == SHELL_FISH)
1670 {
1671
1672
1673
1674
1675
1676
1677
1678 write_all (mc_global.tty.subshell_pty, " \n", 2);
1679 subshell_state = RUNNING_COMMAND;
1680 feed_subshell (QUIETLY, TRUE);
1681 }
1682
1683 update_subshell_prompt = FALSE;
1684
1685 g_free (pcwd);
1686
1687
1688 }
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702 void
1703 init_subshell (void)
1704 {
1705 vfs_path_t *vfs_subshell_cwd;
1706
1707
1708 static char pty_name[BUF_SMALL];
1709
1710
1711
1712 init_raw_mode ();
1713
1714 if (mc_global.tty.subshell_pty == 0)
1715 {
1716 if (mc_global.shell->type == SHELL_NONE)
1717 return;
1718
1719
1720
1721 if (mc_global.shell->type == SHELL_TCSH)
1722 {
1723 g_snprintf (tcsh_fifo, sizeof (tcsh_fifo), "%s/mc.pipe.%d", mc_tmpdir (),
1724 (int) getpid ());
1725 if (mkfifo (tcsh_fifo, 0600) == -1)
1726 {
1727 fprintf (stderr, "mkfifo(%s) failed: %s\r\n", tcsh_fifo, unix_error_string (errno));
1728 mc_global.tty.use_subshell = FALSE;
1729 return;
1730 }
1731
1732
1733
1734 if ((subshell_pipe[READ] = open (tcsh_fifo, O_RDWR)) == -1
1735 || (subshell_pipe[WRITE] = open (tcsh_fifo, O_RDWR)) == -1)
1736 {
1737 fprintf (stderr, _ ("Cannot open named pipe %s\n"), tcsh_fifo);
1738 perror (__FILE__ ": open");
1739 mc_global.tty.use_subshell = FALSE;
1740 return;
1741 }
1742 }
1743 else if (pipe (subshell_pipe) != 0)
1744 {
1745 perror (__FILE__ ": couldn't create pipe");
1746 mc_global.tty.use_subshell = FALSE;
1747 return;
1748 }
1749
1750 if (mc_global.mc_run_mode == MC_RUN_FULL
1751 && (mc_global.shell->type == SHELL_BASH || mc_global.shell->type == SHELL_ZSH
1752 || mc_global.shell->type == SHELL_FISH))
1753 use_persistent_buffer = TRUE;
1754
1755 if (use_persistent_buffer && pipe (command_buffer_pipe) != 0)
1756 {
1757 perror (__FILE__ ": couldn't create pipe");
1758 mc_global.tty.use_subshell = FALSE;
1759 return;
1760 }
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770 #ifdef HAVE_OPENPTY
1771 if (openpty (&mc_global.tty.subshell_pty, &subshell_pty_slave, NULL, NULL, NULL))
1772 {
1773 fprintf (stderr, "Cannot open master and slave sides of pty: %s\n",
1774 unix_error_string (errno));
1775 mc_global.tty.use_subshell = FALSE;
1776 return;
1777 }
1778 #else
1779 mc_global.tty.subshell_pty = pty_open_master (pty_name);
1780 if (mc_global.tty.subshell_pty == -1)
1781 {
1782 fprintf (stderr, "Cannot open master side of pty: %s\r\n", unix_error_string (errno));
1783 mc_global.tty.use_subshell = FALSE;
1784 return;
1785 }
1786
1787 subshell_pty_slave = pty_open_slave (pty_name);
1788 if (subshell_pty_slave == -1)
1789 {
1790 fprintf (stderr, "Cannot open slave side of pty %s: %s\r\n", pty_name,
1791 unix_error_string (errno));
1792 mc_global.tty.use_subshell = FALSE;
1793 return;
1794 }
1795 #endif
1796 }
1797
1798
1799
1800 subshell_alive = TRUE;
1801 subshell_stopped = FALSE;
1802 subshell_pid = my_fork ();
1803
1804 if (subshell_pid == -1)
1805 {
1806 fprintf (stderr, "Cannot spawn the subshell process: %s\r\n", unix_error_string (errno));
1807
1808
1809 exit (EXIT_FAILURE);
1810 }
1811
1812 if (subshell_pid == 0)
1813 {
1814
1815 init_subshell_child (pty_name);
1816 }
1817
1818 {
1819 gchar *precmd;
1820
1821 precmd = init_subshell_precmd ();
1822 write_all (mc_global.tty.subshell_pty, precmd, strlen (precmd));
1823 g_free (precmd);
1824 }
1825
1826
1827 subshell_state = RUNNING_COMMAND;
1828 tty_enable_interrupt_key ();
1829 if (!feed_subshell (QUIETLY, TRUE))
1830 mc_global.tty.use_subshell = FALSE;
1831 tty_disable_interrupt_key ();
1832 if (!subshell_alive)
1833 mc_global.tty.use_subshell = FALSE;
1834
1835
1836
1837
1838
1839 if (use_persistent_buffer && !read_command_line_buffer (TRUE))
1840 use_persistent_buffer = FALSE;
1841
1842
1843
1844
1845 vfs_subshell_cwd = vfs_path_from_str (subshell_cwd);
1846 do_subshell_chdir (vfs_subshell_cwd, TRUE, FALSE);
1847 vfs_path_free (vfs_subshell_cwd, TRUE);
1848
1849 subshell_initialized = TRUE;
1850 }
1851
1852
1853
1854 int
1855 invoke_subshell (const char *command, int how, vfs_path_t **new_dir_vpath)
1856 {
1857
1858 tcsetattr (STDOUT_FILENO, TCSANOW, &raw_mode);
1859
1860
1861 if (new_dir_vpath != NULL)
1862 do_subshell_chdir (subshell_get_cwd (), FALSE, TRUE);
1863
1864 if (command == NULL)
1865 {
1866 if (subshell_state == INACTIVE)
1867 {
1868 subshell_state = ACTIVE;
1869
1870
1871
1872 if (subshell_ready && mc_global.mc_run_mode == MC_RUN_FULL)
1873 write_all (mc_global.tty.subshell_pty, " \b", 2);
1874
1875 if (use_persistent_buffer)
1876 {
1877 const char *s = input_get_ctext (cmdline);
1878
1879
1880
1881 for (size_t i = 0; i < cmdline->buffer->len; i++)
1882 if ((unsigned char) s[i] < 32 || (unsigned char) s[i] == 127)
1883 g_string_overwrite_len (cmdline->buffer, i, " ", 1);
1884
1885
1886 write_all (mc_global.tty.subshell_pty, s, cmdline->buffer->len);
1887
1888
1889 const int pos = (int) (str_length (s) - cmdline->point);
1890
1891 for (int i = 0; i < pos; i++)
1892 write_all (mc_global.tty.subshell_pty, ESC_STR "[D", 3);
1893 }
1894 }
1895 }
1896 else
1897 {
1898
1899
1900
1901
1902 if (use_persistent_buffer)
1903 clear_cwd_pipe ();
1904 else
1905 {
1906
1907
1908 if (subshell_initialized && mc_global.shell->type != SHELL_FISH)
1909 {
1910 write_all (mc_global.tty.subshell_pty, "\003", 1);
1911 subshell_state = RUNNING_COMMAND;
1912 feed_subshell (QUIETLY, FALSE);
1913 }
1914 }
1915
1916 if (how == QUIETLY)
1917 write_all (mc_global.tty.subshell_pty, " ", 1);
1918
1919 write_all (mc_global.tty.subshell_pty, command, strlen (command));
1920 write_all (mc_global.tty.subshell_pty, "\n", 1);
1921 subshell_state = RUNNING_COMMAND;
1922 subshell_ready = FALSE;
1923 }
1924
1925 feed_subshell (how, FALSE);
1926
1927 if (new_dir_vpath != NULL && subshell_alive)
1928 {
1929 const char *pcwd = vfs_translate_path (vfs_path_as_str (subshell_get_cwd ()));
1930
1931 if (strcmp (subshell_cwd, pcwd) != 0)
1932 *new_dir_vpath =
1933 vfs_path_from_str (subshell_cwd);
1934 }
1935
1936
1937 while (!subshell_alive && subshell_get_mainloop_quit () == 0 && mc_global.tty.use_subshell)
1938 init_subshell ();
1939
1940 return subshell_get_mainloop_quit ();
1941 }
1942
1943
1944
1945 gboolean
1946 flush_subshell (int max_wait_length, int how)
1947 {
1948 int rc = 0;
1949 struct timeval timeleft = {
1950 .tv_sec = max_wait_length,
1951 .tv_usec = 0,
1952 };
1953 gboolean return_value = FALSE;
1954 fd_set tmp;
1955
1956 FD_ZERO (&tmp);
1957 FD_SET (mc_global.tty.subshell_pty, &tmp);
1958
1959 while (subshell_alive
1960 && (rc = select (mc_global.tty.subshell_pty + 1, &tmp, NULL, NULL, &timeleft)) != 0)
1961 {
1962
1963 if (rc == -1)
1964 {
1965 if (errno == EINTR)
1966 {
1967 if (tty_got_winch ())
1968 tty_change_screen_size ();
1969
1970 continue;
1971 }
1972
1973 fprintf (stderr, "select (FD_SETSIZE, &tmp...): %s\r\n", unix_error_string (errno));
1974 exit (EXIT_FAILURE);
1975 }
1976
1977 return_value = TRUE;
1978 timeleft.tv_sec = 0;
1979 timeleft.tv_usec = 0;
1980
1981 const ssize_t bytes =
1982 read_nonblock (mc_global.tty.subshell_pty, pty_buffer, sizeof (pty_buffer));
1983
1984
1985 if (bytes > 0 && how == VISIBLY)
1986 write_all (STDOUT_FILENO, pty_buffer, (size_t) bytes);
1987 }
1988
1989 return return_value;
1990 }
1991
1992
1993
1994 gboolean
1995 read_subshell_prompt (void)
1996 {
1997 int rc = 0;
1998 ssize_t bytes = 0;
1999 struct timeval timeleft = {
2000 .tv_sec = 0,
2001 .tv_usec = 0,
2002 };
2003 gboolean got_new_prompt = FALSE;
2004 fd_set tmp;
2005
2006 FD_ZERO (&tmp);
2007 FD_SET (mc_global.tty.subshell_pty, &tmp);
2008
2009 while (subshell_alive
2010 && (rc = select (mc_global.tty.subshell_pty + 1, &tmp, NULL, NULL, &timeleft)) != 0)
2011 {
2012
2013 if (rc == -1)
2014 {
2015 if (errno == EINTR)
2016 {
2017 if (tty_got_winch ())
2018 tty_change_screen_size ();
2019
2020 continue;
2021 }
2022
2023 fprintf (stderr, "select (FD_SETSIZE, &tmp...): %s\r\n", unix_error_string (errno));
2024 exit (EXIT_FAILURE);
2025 }
2026
2027 bytes = read_nonblock (mc_global.tty.subshell_pty, pty_buffer, sizeof (pty_buffer));
2028
2029 if (bytes > 0)
2030 {
2031 parse_subshell_prompt_string (pty_buffer, bytes);
2032 got_new_prompt = TRUE;
2033 }
2034 }
2035
2036 if (got_new_prompt)
2037 set_prompt_string ();
2038
2039 return (rc != 0 || bytes != 0);
2040 }
2041
2042
2043
2044 void
2045 do_update_prompt (void)
2046 {
2047 if (update_subshell_prompt)
2048 {
2049 if (subshell_prompt != NULL)
2050 {
2051 printf ("\r\n%s", subshell_prompt->str);
2052 fflush (stdout);
2053 }
2054 update_subshell_prompt = FALSE;
2055 }
2056 }
2057
2058
2059
2060 gboolean
2061 exit_subshell (void)
2062 {
2063 gboolean subshell_quit = TRUE;
2064
2065 if (subshell_state != INACTIVE && subshell_alive)
2066 subshell_quit = query_dialog (_ ("Warning"), _ ("The shell is still active. Quit anyway?"),
2067 D_NORMAL, 2, _ ("&Yes"), _ ("&No"))
2068 == 0;
2069
2070 if (subshell_quit)
2071 {
2072 if (mc_global.shell->type == SHELL_TCSH)
2073 {
2074 if (unlink (tcsh_fifo) == -1)
2075 fprintf (stderr, "Cannot remove named pipe %s: %s\r\n", tcsh_fifo,
2076 unix_error_string (errno));
2077 }
2078
2079 if (subshell_prompt != NULL)
2080 {
2081 g_string_free (subshell_prompt, TRUE);
2082 subshell_prompt = NULL;
2083 }
2084
2085 if (subshell_prompt_temp_buffer != NULL)
2086 {
2087 g_string_free (subshell_prompt_temp_buffer, TRUE);
2088 subshell_prompt_temp_buffer = NULL;
2089 }
2090
2091 pty_buffer[0] = '\0';
2092 }
2093
2094 return subshell_quit;
2095 }
2096
2097
2098
2099 void
2100 subshell_chdir (const vfs_path_t *vpath)
2101 {
2102 if (mc_global.tty.use_subshell && vfs_current_is_local ())
2103 do_subshell_chdir (vpath, FALSE, FALSE);
2104 }
2105
2106
2107
2108 void
2109 subshell_get_console_attributes (void)
2110 {
2111
2112
2113 if (tcgetattr (STDOUT_FILENO, &shell_mode))
2114 {
2115 fprintf (stderr, "Cannot get terminal settings: %s\r\n", unix_error_string (errno));
2116 mc_global.tty.use_subshell = FALSE;
2117 }
2118 }
2119
2120
2121
2122
2123
2124
2125 void
2126 sigchld_handler (MC_UNUSED int sig)
2127 {
2128 int status;
2129 const pid_t pid = waitpid (subshell_pid, &status, WUNTRACED | WNOHANG);
2130
2131 if (pid == subshell_pid)
2132 {
2133
2134
2135 if (WIFSTOPPED (status))
2136 {
2137 if (WSTOPSIG (status) == SIGSTOP)
2138 {
2139
2140 subshell_stopped = TRUE;
2141 }
2142 else
2143 {
2144
2145 kill (subshell_pid, SIGCONT);
2146 }
2147 }
2148 else
2149 {
2150
2151 subshell_alive = FALSE;
2152 delete_select_channel (mc_global.tty.subshell_pty);
2153 if (WIFEXITED (status) && WEXITSTATUS (status) != FORK_FAILURE)
2154 {
2155 const int subshell_quit =
2156 subshell_get_mainloop_quit () | SUBSHELL_EXIT;
2157
2158 subshell_set_mainloop_quit (subshell_quit);
2159 }
2160 }
2161 }
2162
2163 subshell_handle_cons_saver ();
2164
2165
2166 }
2167
2168