Fortran · 2806 bytes Raw Blame History
1 module pty_mod
2 use, intrinsic :: iso_c_binding
3 use pty_bindings
4 implicit none
5 private
6
7 public :: pty_t
8 public :: pty_open, pty_close, pty_resize
9 public :: pty_read, pty_write, pty_is_alive
10
11 type :: pty_t
12 integer :: master_fd = -1
13 integer :: rows = 24
14 integer :: cols = 80
15 logical :: active = .false.
16 end type pty_t
17
18 contains
19
20 ! Open PTY and spawn shell
21 function pty_open(shell, rows, cols) result(p)
22 character(len=*), intent(in) :: shell
23 integer, intent(in) :: rows, cols
24 type(pty_t) :: p
25 integer(c_int) :: fd
26
27 p%rows = rows
28 p%cols = cols
29
30 ! Fork shell with PTY
31 fd = c_pty_fork(trim(shell) // c_null_char, int(rows, c_int), int(cols, c_int))
32
33 if (fd < 0) then
34 print *, "Error: Failed to create PTY"
35 p%active = .false.
36 return
37 end if
38
39 p%master_fd = fd
40 p%active = .true.
41
42 end function pty_open
43
44 ! Close PTY and cleanup
45 subroutine pty_close(p)
46 type(pty_t), intent(inout) :: p
47
48 if (p%master_fd >= 0) then
49 call c_pty_close(int(p%master_fd, c_int))
50 p%master_fd = -1
51 end if
52
53 p%active = .false.
54
55 end subroutine pty_close
56
57 ! Resize PTY
58 subroutine pty_resize(p, rows, cols)
59 type(pty_t), intent(inout) :: p
60 integer, intent(in) :: rows, cols
61 integer(c_int) :: result
62
63 if (.not. p%active) return
64
65 result = c_pty_set_size(int(p%master_fd, c_int), &
66 int(rows, c_int), int(cols, c_int))
67
68 if (result == 0) then
69 p%rows = rows
70 p%cols = cols
71 end if
72
73 end subroutine pty_resize
74
75 ! Read from PTY (non-blocking)
76 ! Returns: bytes read (>0), 0 if no data, -1 on error/EOF
77 function pty_read(p, buffer, maxlen) result(nbytes)
78 type(pty_t), intent(in) :: p
79 character(len=*), intent(out) :: buffer
80 integer, intent(in) :: maxlen
81 integer :: nbytes
82
83 if (.not. p%active) then
84 nbytes = -1
85 return
86 end if
87
88 nbytes = c_pty_read(int(p%master_fd, c_int), buffer, int(maxlen, c_int))
89
90 end function pty_read
91
92 ! Write to PTY
93 subroutine pty_write(p, data, length)
94 type(pty_t), intent(in) :: p
95 character(len=*), intent(in) :: data
96 integer, intent(in) :: length
97 integer(c_int) :: result
98
99 if (.not. p%active) return
100
101 result = c_pty_write(int(p%master_fd, c_int), data, int(length, c_int))
102
103 end subroutine pty_write
104
105 ! Check if child shell is alive
106 function pty_is_alive(p) result(alive)
107 type(pty_t), intent(inout) :: p
108 logical :: alive
109 integer(c_int) :: status
110
111 if (.not. p%active) then
112 alive = .false.
113 return
114 end if
115
116 status = c_pty_child_alive()
117 alive = (status /= 0)
118
119 ! Update active flag if child died
120 if (.not. alive) then
121 p%active = .false.
122 end if
123
124 end function pty_is_alive
125
126 end module pty_mod
127