a68g-bits.c
1 //! @file a68g-bits.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-2024 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 //! Miscellaneous routines.
25
26 #include "a68g.h"
27 #include "a68g-prelude.h"
28 #include "a68g-mp.h"
29 #include "a68g-numbers.h"
30 #include "a68g-genie.h"
31 #include "a68g-postulates.h"
32 #include "a68g-parser.h"
33 #include "a68g-options.h"
34 #include "a68g-optimiser.h"
35 #include "a68g-listing.h"
36
37 #if defined (HAVE_MATHLIB)
38 #include <Rmath.h>
39 #endif
40
41 #if defined (BUILD_WIN32)
42 #include <windows.h>
43 #endif
44
45 #define WRITE_TXT(fn, txt) ASSERT (write ((fn), (txt), 1 + strlen (txt)) != -1)
46
47 #if defined (BUILD_LINUX) && defined (HAVE_EXECINFO_H)
48
49 #include <execinfo.h>
50
51 void genie_sigsegv (NODE_T *p)
52 {
53 (void) p;
54 raise (SIGSEGV);
55 }
56
57 //! @brief Provide a rudimentary backtrace.
58
59 void stack_backtrace (void)
60 {
61 #define DEPTH 16
62 void *array[DEPTH];
63 WRITE_TXT (2, "\n++++ Top of call stack:");
64 int size = backtrace (array, DEPTH);
65 if (size > 0) {
66 WRITE_TXT (2, "\n");
67 backtrace_symbols_fd (array, size, 2);
68 }
69 #undef DEPTH
70 }
71
72 void genie_backtrace (NODE_T *p)
73 {
74 (void) p;
75 stack_backtrace ();
76 }
77
78 #else
79
80 void stack_backtrace (void)
81 {
82 WRITE_TXT (2, "\n++++ Stack backtrace is not available on this platform");
83 }
84
85 void genie_backtrace (NODE_T *p)
86 {
87 (void) p;
88 stack_backtrace ();
89 }
90 #endif
91
92 //! @brief Open a file in ~/.a68g, if possible.
93
94 FILE *a68_fopen (char *fn, char *mode, char *new_fn)
95 {
96 #if defined (BUILD_WIN32) || !defined (HAVE_DIRENT_H)
97 ASSERT (a68_bufprt (new_fn, SNPRINTF_SIZE, "%s", fn) >= 0);
98 return fopen (new_fn, mode);
99 #else
100 errno = 0;
101 BUFFER dn;
102 ASSERT (a68_bufprt (dn, SNPRINTF_SIZE, "%s/%s", getenv ("HOME"), A68_DIR) >= 0);
103 int ret = mkdir (dn, (mode_t) (S_IRUSR | S_IWUSR | S_IXUSR));
104 if (ret == 0 || (ret == -1 && errno == EEXIST)) {
105 struct stat status;
106 if (stat (dn, &status) == 0 && S_ISDIR (ST_MODE (&status)) != 0) {
107 FILE *f;
108 ASSERT (a68_bufprt (new_fn, SNPRINTF_SIZE, "%s/%s", dn, fn) >= 0);
109 f = fopen (new_fn, mode);
110 if (f != NO_FILE) {
111 return f;
112 }
113 }
114 }
115 ASSERT (a68_bufprt (new_fn, SNPRINTF_SIZE, "%s", fn) >= 0);
116 return fopen (new_fn, mode);
117 #endif
118 }
119
120 //! @brief Get terminal size.
121
122 void a68_getty (int *h, int *c)
123 {
124 // Default action first.
125 (*h) = MAX_TERM_HEIGTH;
126 (*c) = MAX_TERM_WIDTH;
127 #if (defined (HAVE_SYS_IOCTL_H) && defined (TIOCGWINSZ))
128 {
129 struct winsize w;
130 if (ioctl (0, TIOCGWINSZ, &w) == 0) {
131 (*h) = w.ws_row;
132 (*c) = w.ws_col;
133 }
134 }
135 #elif (defined (HAVE_SYS_IOCTL_H) && defined (TIOCGSIZE))
136 {
137 struct ttysize w;
138 (void) ioctl (0, TIOCGSIZE, &w);
139 if (w.ts_lines > 0) {
140 (*h) = w.ts_lines;
141 }
142 if (w.ts_cols > 0) {
143 (*c) = w.ts_cols;
144 }
145 }
146 #elif (defined (HAVE_SYS_IOCTL_H) && defined (WIOCGETD))
147 {
148 struct uwdata w;
149 (void) ioctl (0, WIOCGETD, &w);
150 if (w.uw_heigth > 0 && w.uw_vs != 0) {
151 (*h) = w.uw_heigth / w.uw_vs;
152 }
153 if (w.uw_width > 0 && w.uw_hs != 0) {
154 (*c) = w.uw_width / w.uw_hs;
155 }
156 }
157 #endif
158 }
159
160 // Signal handlers.
161
162 #if defined (SIGWINCH)
163
164 //! @brief Signal for window resize.
165
166 void sigwinch_handler (int i)
167 {
168 (void) i;
169 ABEND (signal (SIGWINCH, sigwinch_handler) == SIG_ERR, ERROR_ACTION, __func__);
170 a68_getty (&A68 (term_heigth), &A68 (term_width));
171 }
172 #endif
173
174 //! @brief Signal handler for segment violation.
175
176 void sigsegv_handler (int i)
177 {
178 (void) i;
179 // write () is asynchronous-safe and may be called here.
180 WRITE_TXT (2, "\nFatal");
181 if (FILE_INITIAL_NAME (&A68_JOB) != NO_TEXT) {
182 WRITE_TXT (2, ": ");
183 WRITE_TXT (2, FILE_INITIAL_NAME (&A68_JOB));
184 }
185 WRITE_TXT (2, ": memory access violation\n");
186 stack_backtrace ();
187 exit (EXIT_FAILURE);
188 }
189
190 //! @brief Raise SYSREQUEST so you get to a monitor.
191
192 void sigint_handler (int i)
193 {
194 (void) i;
195 ABEND (signal (SIGINT, sigint_handler) == SIG_ERR, ERROR_ACTION, __func__);
196 if (!(STATUS_TEST (TOP_NODE (&A68_JOB), BREAKPOINT_INTERRUPT_MASK) || A68 (in_monitor))) {
197 STATUS_SET (TOP_NODE (&A68_JOB), BREAKPOINT_INTERRUPT_MASK);
198 genie_break (TOP_NODE (&A68_JOB));
199 }
200 }
201
202 #if defined (BUILD_UNIX)
203
204 //! @brief Signal handler from disconnected terminal.
205
206 void sigttin_handler (int i)
207 {
208 (void) i;
209 ABEND (A68_TRUE, ERROR_ACTION, __func__);
210 }
211
212 //! @brief Signal broken pipe.
213
214 void sigpipe_handler (int i)
215 {
216 (void) i;
217 ABEND (A68_TRUE, ERROR_ACTION, __func__);
218 }
219
220 //! @brief Signal alarm - time limit check.
221
222 unsigned a68_alarm (unsigned seconds)
223 {
224 struct itimerval old, new;
225 new.it_interval.tv_usec = 0;
226 new.it_interval.tv_sec = 0;
227 new.it_value.tv_usec = 0;
228 new.it_value.tv_sec = (long int) seconds;
229 if (setitimer (ITIMER_REAL, &new, &old) < 0) {
230 return 0;
231 } else {
232 return old.it_value.tv_sec;
233 }
234 }
235
236 void sigalrm_handler (int i)
237 {
238 (void) i;
239 if (A68 (in_execution) && !A68 (in_monitor)) {
240 REAL_T _m_t = (REAL_T) OPTION_TIME_LIMIT (&A68_JOB);
241 if (_m_t > 0 && (seconds () - A68 (cputime_0)) > _m_t) {
242 diagnostic (A68_RUNTIME_ERROR, (NODE_T *) A68 (f_entry), ERROR_TIME_LIMIT_EXCEEDED);
243 exit_genie ((NODE_T *) A68 (f_entry), A68_RUNTIME_ERROR);
244 }
245 }
246 ABEND (signal (SIGALRM, sigalrm_handler) == SIG_ERR, ERROR_ACTION, __func__);
247 (void) a68_alarm (INTERRUPT_INTERVAL);
248 }
249
250 #endif
251
252 //! @brief Install_signal_handlers.
253
254 void install_signal_handlers (void)
255 {
256 ABEND (signal (SIGINT, sigint_handler) == SIG_ERR, ERROR_ACTION, __func__);
257 ABEND (signal (SIGSEGV, sigsegv_handler) == SIG_ERR, ERROR_ACTION, __func__);
258 #if defined (SIGWINCH)
259 ABEND (signal (SIGWINCH, sigwinch_handler) == SIG_ERR, ERROR_ACTION, __func__);
260 #endif
261 #if defined (BUILD_UNIX)
262 ABEND (signal (SIGALRM, sigalrm_handler) == SIG_ERR, ERROR_ACTION, __func__);
263 ABEND (signal (SIGPIPE, sigpipe_handler) == SIG_ERR, ERROR_ACTION, __func__);
264 ABEND (signal (SIGTTIN, sigttin_handler) == SIG_ERR, ERROR_ACTION, __func__);
265 #endif
266 }
267
268 //! @brief Time versus arbitrary origin.
269
270 REAL_T seconds (void)
271 {
272 return (REAL_T) clock () / (REAL_T) CLOCKS_PER_SEC;
273 }
274
275 //! @brief Delay for specified number of microseconds.
276
277 int a68_usleep (unsigned delay)
278 {
279 #if defined (BUILD_WIN32)
280 errno = ENOSYS; // Function not implemented.
281 return -1;
282 #else
283 // usleep is not compatible with _XOPEN_SOURCE 700.
284 struct timespec reqtime;
285 reqtime.tv_sec = delay / 1000000;
286 reqtime.tv_nsec = 1000 * (delay % 1000000);
287 return nanosleep (&reqtime, NULL);
288 #endif
289 }
290
291 //! @brief Safely set buffer.
292
293 void *a68_bufset (void *dst, int val, size_t len)
294 {
295 // Function a68_bufset can be optimized away by a compiler.
296 // Therefore we have this alternative.
297 ASSERT (dst != NO_TEXT);
298 char *p = (char *) dst;
299 while (len-- > 0) {
300 *(p++) = val;
301 }
302 return dst;
303 }
304
305 //! @brief Safely append to buffer.
306
307 void a68_bufcat (char *dst, char *src, size_t len)
308 {
309 ASSERT (dst != NO_TEXT);
310 ASSERT (src != NO_TEXT);
311 char *d = dst, *s = src;
312 int n = len;
313 // Find end of dst and left-adjust; do not go past end.
314 for (; n-- != 0 && d[0] != NULL_CHAR; d++) {
315 ;
316 }
317 int dlen = (int) (d - dst);
318 n = len - dlen;
319 if (n > 0) {
320 while (s[0] != NULL_CHAR) {
321 if (n != 1) {
322 (d++)[0] = s[0];
323 n--;
324 }
325 s++;
326 }
327 d[0] = NULL_CHAR;
328 }
329 // Better sure than sorry.
330 dst[len - 1] = NULL_CHAR;
331 }
332
333 //! @brief Safely copy to buffer.
334
335 void a68_bufcpy (char *dst, char *src, size_t len)
336 {
337 ASSERT (dst != NO_TEXT);
338 ASSERT (src != NO_TEXT);
339 char *d = dst, *s = src;
340 int n = len;
341 // Copy as many as fit.
342 if (n > 0 && --n > 0) {
343 do {
344 if (((d++)[0] = (s++)[0]) == NULL_CHAR) {
345 break;
346 }
347 } while (--n > 0);
348 }
349 if (n == 0 && len > 0) {
350 // Not enough room in dst, so terminate.
351 d[0] = NULL_CHAR;
352 }
353 // Better sure than sorry.
354 dst[len - 1] = NULL_CHAR;
355 }
356
357 //! @brief Safely print to buffer.
358
359 int a68_bufprt (char *dst, size_t len, const char *format, ...)
360 {
361 ASSERT (dst != NO_TEXT);
362 ASSERT (len > 1);
363 ASSERT (format != NO_TEXT);
364 va_list ap;
365 va_start (ap, format);
366 int rc = vsnprintf (dst, len, format, ap);
367 va_end (ap);
368 // Better sure than sorry.
369 dst[len - 1] = NULL_CHAR;
370 if (rc >= 0 && len <= (size_t) rc) {
371 return -1;
372 } else {
373 return rc;
374 }
375 }
© 2002-2024 J.M. van der Veer (jmvdveer@xs4all.nl)
|