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