a68g-io.c
1 //! @file a68g-io.c
2 //! @author J. Marcel van der Veer
3
4 //! @section Copyright
5 //!
6 //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
7 //! Copyright 2001-2025 J. Marcel van der Veer [algol68g@xs4all.nl].
8
9 //! @section License
10 //!
11 //! This program is free software; you can redistribute it and/or modify it
12 //! under the terms of the GNU General Public License as published by the
13 //! Free Software Foundation; either version 3 of the License, or
14 //! (at your option) any later version.
15 //!
16 //! This program is distributed in the hope that it will be useful, but
17 //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 //! more details. You should have received a copy of the GNU General Public
20 //! License along with this program. If not, see [http://www.gnu.org/licenses/].
21
22 //! @section Synopsis
23 //!
24 //! Low-level input-output routines.
25
26 // These routines fail in principle when 'read' or 'write' are interrupted
27 // MAX_RESTART_AT_INTERRUPT times, for instance by SIGALRM.
28 //
29 // On platforms that support it, SIGALRM is used to implement a CPU time out
30 // on a running Algol 68 program.
31 //
32 // The SIGALRM handler however implements BSD type restarting of primitive
33 // system call interupted by it, which includes 'read' and 'write'.
34 // Consider however that non-responsive devices could make a68g 'hang'.
35 // In that case, specify option NORESTART, and SIGALRM will interrupt IO.
36 // Current settings make these routines fail after 10 minutes of trying.
37
38 #include "a68g.h"
39 #include "a68g-prelude.h"
40
41 //! @brief Initialise output to STDOUT.
42
43 void init_tty (void)
44 {
45 A68G (chars_in_stderr) = 0;
46 A68G (chars_in_stdout) = 0;
47 A68G (halt_typing) = A68G_FALSE;
48 change_masks (TOP_NODE (&A68G_JOB), BREAKPOINT_INTERRUPT_MASK, A68G_FALSE);
49 }
50
51 //! @brief Terminate current line on STDOUT.
52
53 void io_close_tty_line (void)
54 {
55 if (A68G (chars_in_stderr) > 0) {
56 A68G (chars_in_stderr) = 0;
57 io_write_string (A68G_STDERR, NEWLINE_STRING);
58 }
59 if (A68G (chars_in_stdout) > 0) {
60 A68G (chars_in_stdout) = 0;
61 io_write_string (A68G_STDOUT, NEWLINE_STRING);
62 }
63 }
64
65 //! @brief Get a char from STDIN.
66
67 char get_stdin_char (void)
68 {
69 char ch[4];
70 errno = 0;
71 ssize_t j = io_read_conv (A68G_STDIN, &(ch[0]), 1);
72 ABEND (j < 0, ERROR_ACTION, NO_TEXT);
73 return (char) (j == 1 ? ch[0] : EOF_CHAR);
74 }
75
76 //! @brief Read string from STDIN, until NEWLINE_STRING.
77
78 char *read_string_from_tty (char *prompt)
79 {
80 #if defined (HAVE_READLINE)
81 char *line = readline (prompt);
82 if (line != NO_TEXT && strlen (line) > 0) {
83 add_history (line);
84 }
85 a68g_bufcpy (A68G (input_line), line, BUFFER_SIZE);
86 A68G (chars_in_stdout) = strlen (A68G (input_line));
87 a68g_free (line);
88 return A68G (input_line);
89 #else
90 if (prompt != NO_TEXT) {
91 io_close_tty_line ();
92 io_write_string (A68G_STDOUT, prompt);
93 }
94 int ch = get_stdin_char (), k= 0;
95 while (ch != NEWLINE_CHAR && k < BUFFER_SIZE - 1) {
96 if (ch == EOF_CHAR) {
97 A68G (input_line)[0] = EOF_CHAR;
98 A68G (input_line)[1] = NULL_CHAR;
99 A68G (chars_in_stdout) = 1;
100 return A68G (input_line);
101 } else {
102 A68G (input_line)[k++] = (char) ch;
103 ch = get_stdin_char ();
104 }
105 }
106 A68G (input_line)[k] = NULL_CHAR;
107 size_t n = strlen (A68G (input_line));
108 A68G (chars_in_stdout) = (ch == NEWLINE_CHAR ? 0 : (n > 0 ? n : 1));
109 return A68G (input_line);
110 #endif
111 }
112
113 //! @brief Write string to file.
114
115 void io_write_string (FILE_T f, const char *z)
116 {
117 errno = 0;
118 if (f != A68G_STDOUT && f != A68G_STDERR) {
119 // Writing to file.
120 ssize_t j = io_write_conv (f, z, strlen (z));
121 ABEND (j < 0, ERROR_ACTION, NO_TEXT);
122 } else {
123 // Writing to TTY parts until end-of-string.
124 int first = 0, k;
125 do {
126 k = first;
127 // How far can we get?.
128 while (z[k] != NULL_CHAR && z[k] != NEWLINE_CHAR) {
129 k++;
130 }
131 if (k > first) {
132 // Write these characters.
133 int n = k - first;
134 ssize_t j = io_write_conv (f, &(z[first]), (size_t) n);
135 ABEND (j < 0, ERROR_ACTION, NO_TEXT);
136 if (f == A68G_STDERR) {
137 A68G (chars_in_stderr) += n;
138 } else {
139 A68G (chars_in_stdout) += n;
140 }
141 }
142 if (z[k] == NEWLINE_CHAR) {
143 // Pretty-print newline.
144 k++;
145 first = k;
146 ssize_t j = io_write_conv (f, NEWLINE_STRING, 1);
147 ABEND (j < 0, ERROR_ACTION, NO_TEXT);
148 if (f == A68G_STDERR) {
149 A68G (chars_in_stderr) = 0;
150 } else {
151 A68G (chars_in_stdout) = 0;
152 }
153 }
154 } while (z[k] != NULL_CHAR);
155 }
156 }
157
158 //! @brief Read bytes from file into buffer.
159
160 ssize_t io_read (FILE_T fd, void *buf, size_t n)
161 {
162 size_t to_do = n;
163 int retry = 0;
164 char *z = (char *) buf;
165 while (to_do > 0) {
166 #if defined (BUILD_WIN32)
167 int bytes_read;
168 #elif defined (BUILD_WIN64)
169 ssize_t bytes_read;
170 #else
171 ssize_t bytes_read;
172 #endif
173 errno = 0;
174 bytes_read = read (fd, z, to_do);
175 if (bytes_read < 0) {
176 if (errno == EINTR) {
177 // interrupt, retry.
178 bytes_read = 0;
179 if (++retry > MAX_INTERRUPT_RESTART) {
180 return -1;
181 }
182 } else {
183 // read error.
184 return -1;
185 }
186 } else if (bytes_read == 0) {
187 break; // EOF_CHAR
188 }
189 to_do -= (size_t) bytes_read;
190 z += bytes_read;
191 }
192 return (ssize_t) n - (ssize_t) to_do; // return >= 0
193 }
194
195 //! @brief Writes n bytes from buffer to file.
196
197 ssize_t io_write (FILE_T fd, const void *buf, size_t n)
198 {
199 size_t to_do = n;
200 int retry = 0;
201 char *z = (char *) buf;
202 while (to_do > 0) {
203 ssize_t bytes_written;
204 errno = 0;
205 bytes_written = write (fd, z, to_do);
206 if (bytes_written <= 0) {
207 if (errno == EINTR) {
208 // interrupt, retry.
209 bytes_written = 0;
210 if (++retry > MAX_INTERRUPT_RESTART) {
211 return -1;
212 }
213 } else {
214 // write error.
215 return -1;
216 }
217 }
218 to_do -= (size_t) bytes_written;
219 z += bytes_written;
220 }
221 return (ssize_t) n;
222 }
223
224 //! @brief Read bytes from file into buffer.
225
226 ssize_t io_read_conv (FILE_T fd, void *buf, size_t n)
227 {
228 size_t to_do = n;
229 int retry = 0;
230 char *z = (char *) buf;
231 while (to_do > 0) {
232 #if defined (BUILD_WIN32)
233 int bytes_read;
234 #elif defined (BUILD_WIN64)
235 ssize_t bytes_read;
236 #else
237 ssize_t bytes_read;
238 #endif
239 errno = 0;
240 bytes_read = read (fd, z, to_do);
241 if (bytes_read < 0) {
242 if (errno == EINTR) {
243 // interrupt, retry.
244 bytes_read = 0;
245 if (++retry > MAX_INTERRUPT_RESTART) {
246 return -1;
247 }
248 } else {
249 // read error.
250 return -1;
251 }
252 } else if (bytes_read == 0) {
253 break; // EOF_CHAR
254 }
255 to_do -= (size_t) bytes_read;
256 z += bytes_read;
257 }
258 return (ssize_t) n - (ssize_t) to_do;
259 }
260
261 //! @brief Writes n bytes from buffer to file.
262
263 ssize_t io_write_conv (FILE_T fd, const void *buf, size_t n)
264 {
265 size_t to_do = n;
266 int retry = 0;
267 char *z = (char *) buf;
268 while (to_do > 0) {
269 ssize_t bytes_written;
270 errno = 0;
271 bytes_written = write (fd, z, to_do);
272 if (bytes_written <= 0) {
273 if (errno == EINTR) {
274 // interrupt, retry.
275 bytes_written = 0;
276 if (++retry > MAX_INTERRUPT_RESTART) {
277 return -1;
278 }
279 } else {
280 // write error.
281 return -1;
282 }
283 }
284 to_do -= (size_t) bytes_written;
285 z += bytes_written;
286 }
287 return (ssize_t) n;
288 }
© 2002-2025 J.M. van der Veer (jmvdveer@xs4all.nl)
|