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