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