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-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 //! 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_R_MATHLIB)
38 #include <Rmath.h>
39 #endif
40
41 #if defined (BUILD_WINDOWS)
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 //! @brief Provide a rudimentary backtrace.
52
53 void a68g_stack_backtrace (void)
54 {
55 #define DEPTH 16
56 void *array[DEPTH];
57 WRITE_TXT (2, "\n++++ Top of call stack:");
58 size_t size = backtrace (array, DEPTH);
59 if (size > 0) {
60 WRITE_TXT (2, "\n");
61 backtrace_symbols_fd (array, size, 2);
62 }
63 #undef DEPTH
64 }
65
66 void genie_sigsegv (NODE_T *p)
67 {
68 (void) p;
69 raise (SIGSEGV);
70 }
71
72 void genie_backtrace (NODE_T *p)
73 {
74 (void) p;
75 a68g_stack_backtrace ();
76 }
77 #else
78 void a68g_stack_backtrace (void)
79 {
80 WRITE_TXT (2, "\n++++ Stack backtrace is not available on this platform");
81 }
82
83 void genie_backtrace (NODE_T *p)
84 {
85 (void) p;
86 a68g_stack_backtrace ();
87 }
88
89 #endif
90
91 //! @brief Whether x matches c; case insensitive.
92
93 BOOL_T match_string (char *x, char *c, char stop)
94 {
95 #define DONE(z) ((z) == NULL_CHAR || (z) == stop)
96 #define NOT_DONE(z) (! DONE (z))
97 BOOL_T match = A68G_TRUE;
98 // Uppercase characters in c are mandatory.
99 while ((IS_UPPER (c[0]) || IS_DIGIT (c[0]) || (NOT_DONE (x[0]) && c[0] == '-')) && match) {
100 match = (BOOL_T) (match & (TO_LOWER (x[0]) == TO_LOWER ((c++)[0])));
101 if (NOT_DONE (x[0])) {
102 x++;
103 }
104 }
105 // Lowercase characters in c are optional.
106 while (NOT_DONE (x[0]) && NOT_DONE (c[0]) && match) {
107 match = (BOOL_T) (match & (TO_LOWER ((x++)[0]) == TO_LOWER ((c++)[0])));
108 }
109 return (BOOL_T) (match ? DONE (x[0]) : A68G_FALSE);
110 #undef DONE
111 #undef NOT_DONE
112 }
113
114 //! @brief Open a file in ~/.a68g, if possible.
115
116 FILE *a68g_fopen (char *fn, char *mode, char *new_fn)
117 {
118 #if defined (BUILD_WINDOWS) || !defined (HAVE_DIRENT_H)
119 ASSERT (a68g_bufprt (new_fn, SNPRINTF_SIZE, "%s", fn) >= 0);
120 return fopen (new_fn, mode);
121 #else
122 errno = 0;
123 BUFFER dn;
124 ASSERT (a68g_bufprt (dn, SNPRINTF_SIZE, "%s/%s", getenv ("HOME"), A68G_DIR) >= 0);
125 int ret = mkdir (dn, (mode_t) (S_IRUSR | S_IWUSR | S_IXUSR));
126 if (ret == 0 || (ret == -1 && errno == EEXIST)) {
127 struct stat status;
128 if (stat (dn, &status) == 0 && S_ISDIR (ST_MODE (&status)) != 0) {
129 FILE *f;
130 ASSERT (a68g_bufprt (new_fn, SNPRINTF_SIZE, "%s/%s", dn, fn) >= 0);
131 f = fopen (new_fn, mode);
132 if (f != NO_FILE) {
133 return f;
134 }
135 }
136 }
137 ASSERT (a68g_bufprt (new_fn, SNPRINTF_SIZE, "%s", fn) >= 0);
138 return fopen (new_fn, mode);
139 #endif
140 }
141
142 //! @brief Get terminal size.
143
144 void a68g_getty (int *h, int *c)
145 {
146 // Default action first.
147 (*h) = MAX_TERM_HEIGTH;
148 (*c) = MAX_TERM_WIDTH;
149 #if (defined (HAVE_SYS_IOCTL_H) && defined (TIOCGWINSZ))
150 {
151 struct winsize w;
152 if (ioctl (0, TIOCGWINSZ, &w) == 0) {
153 (*h) = w.ws_row;
154 (*c) = w.ws_col;
155 }
156 }
157 #elif (defined (HAVE_SYS_IOCTL_H) && defined (TIOCGSIZE))
158 {
159 struct ttysize w;
160 (void) ioctl (0, TIOCGSIZE, &w);
161 if (w.ts_lines > 0) {
162 (*h) = w.ts_lines;
163 }
164 if (w.ts_cols > 0) {
165 (*c) = w.ts_cols;
166 }
167 }
168 #elif (defined (HAVE_SYS_IOCTL_H) && defined (WIOCGETD))
169 {
170 struct uwdata w;
171 (void) ioctl (0, WIOCGETD, &w);
172 if (w.uw_heigth > 0 && w.uw_vs != 0) {
173 (*h) = w.uw_heigth / w.uw_vs;
174 }
175 if (w.uw_width > 0 && w.uw_hs != 0) {
176 (*c) = w.uw_width / w.uw_hs;
177 }
178 }
179 #endif
180 }
181
182 // Signal handlers.
183
184 #if defined (SIGWINCH)
185
186 //! @brief Signal for window resize.
187
188 void sigwinch_handler (int i)
189 {
190 (void) i;
191 #if !defined (HAVE_SIGACTION)
192 ABEND (signal (SIGWINCH, sigwinch_handler) == SIG_ERR, ERROR_ACTION, NO_TEXT);
193 #endif
194 a68g_getty (&A68G (term_heigth), &A68G (term_width));
195 }
196
197 //! @brief Install_signal_handlers.
198
199 void install_sigwinch (void)
200 {
201 #if defined (HAVE_SIGACTION)
202 struct sigaction sa;
203 memset (&sa, 0, sizeof (sa));
204 sa.sa_handler = sigwinch_handler;
205 ABEND (sigaction (SIGWINCH, &sa, NULL) != 0, ERROR_ACTION, NO_TEXT);
206 #else
207 ABEND (signal (SIGWINCH, sigwinch_handler) == SIG_ERR, ERROR_ACTION, NO_TEXT);
208 #endif
209 }
210
211 #endif
212
213 //! @brief Signal handler for segment violation.
214
215 void sigsegv_handler (int i)
216 {
217 (void) i;
218 // write () is asynchronous-safe and may be called here.
219 WRITE_TXT (2, "\nFatal");
220 if (FILE_INITIAL_NAME (&A68G_JOB) != NO_TEXT) {
221 WRITE_TXT (2, ": ");
222 WRITE_TXT (2, FILE_INITIAL_NAME (&A68G_JOB));
223 }
224 WRITE_TXT (2, ": memory access violation\n");
225 #if defined (BUILD_LINUX) && defined (HAVE_EXECINFO_H)
226 a68g_stack_backtrace ();
227 #endif
228 exit (EXIT_FAILURE);
229 }
230
231 //! @brief Install_signal_handlers.
232
233 void install_sigsegv (void)
234 {
235 #if defined (HAVE_SIGACTION)
236 struct sigaction sa;
237 memset (&sa, 0, sizeof (sa));
238 sa.sa_handler = sigsegv_handler;
239 ABEND (sigaction (SIGSEGV, &sa, NULL) != 0, ERROR_ACTION, NO_TEXT);
240 #else
241 ABEND (signal (SIGSEGV, sigsegv_handler) == SIG_ERR, ERROR_ACTION, NO_TEXT);
242 #endif
243 }
244
245 //! @brief Raise SYSREQUEST so you get to a monitor.
246
247 void sigint_handler (int i)
248 {
249 (void) i;
250 #if !defined (HAVE_SIGACTION)
251 ABEND (signal (SIGINT, sigint_handler) == SIG_ERR, ERROR_ACTION, NO_TEXT);
252 #endif
253 if (!(STATUS_TEST (TOP_NODE (&A68G_JOB), BREAKPOINT_INTERRUPT_MASK) || A68G (in_monitor))) {
254 STATUS_SET (TOP_NODE (&A68G_JOB), BREAKPOINT_INTERRUPT_MASK);
255 genie_break (TOP_NODE (&A68G_JOB));
256 }
257 }
258
259 //! @brief Install_signal_handlers.
260
261 void install_sigint (void)
262 {
263 #if defined (HAVE_SIGACTION)
264 struct sigaction sa;
265 memset (&sa, 0, sizeof (sa));
266 sa.sa_handler = sigint_handler;
267 ABEND (sigaction (SIGINT, &sa, NULL) != 0, ERROR_ACTION, NO_TEXT);
268 #else
269 ABEND (signal (SIGINT, sigint_handler) == SIG_ERR, ERROR_ACTION, NO_TEXT);
270 #endif
271 }
272
273 #if defined (BUILD_UNIX)
274
275 //! @brief Signal handler from disconnected terminal.
276
277 void sigttin_handler (int i)
278 {
279 (void) i;
280 ABEND (A68G_TRUE, ERROR_ACTION, NO_TEXT);
281 }
282
283 //! @brief Install_signal_handlers.
284
285 void install_sigttin (void)
286 {
287 #if defined (HAVE_SIGACTION)
288 struct sigaction sa;
289 memset (&sa, 0, sizeof (sa));
290 sa.sa_handler = sigttin_handler;
291 ABEND (sigaction (SIGTTIN, &sa, NULL) != 0, ERROR_ACTION, NO_TEXT);
292 #endif
293 }
294
295 //! @brief Signal broken pipe.
296
297 void sigpipe_handler (int i)
298 {
299 (void) i;
300 ABEND (A68G_TRUE, ERROR_ACTION, NO_TEXT);
301 }
302
303 //! @brief Install_signal_handlers.
304
305 void install_sigpipe (void)
306 {
307 #if defined (HAVE_SIGACTION)
308 struct sigaction sa;
309 memset (&sa, 0, sizeof (sa));
310 sa.sa_handler = sigpipe_handler;
311 ABEND (sigaction (SIGPIPE, &sa, NULL) != 0, ERROR_ACTION, NO_TEXT);
312 #endif
313 }
314
315 //! @brief Signal alarm - time limit check.
316
317 unsigned a68g_alarm (unsigned seconds)
318 {
319 struct itimerval old, new;
320 new.it_interval.tv_usec = 0;
321 new.it_interval.tv_sec = 0;
322 new.it_value.tv_usec = 0;
323 new.it_value.tv_sec = (long int) seconds;
324 if (setitimer (ITIMER_REAL, &new, &old) < 0) {
325 return 0;
326 } else {
327 return old.it_value.tv_sec;
328 }
329 }
330
331 //! @brief SIGALRM handler.
332
333 void sigalrm_handler (int i)
334 {
335 (void) i;
336 if (A68G (in_execution) && !A68G (in_monitor)) {
337 REAL_T _m_t = (REAL_T) OPTION_TIME_LIMIT (&A68G_JOB);
338 if (_m_t > 0 && (seconds () - A68G (cputime_0)) > _m_t) {
339 diagnostic (A68G_RUNTIME_ERROR, (NODE_T *) A68G (f_entry), ERROR_TIME_LIMIT_EXCEEDED);
340 exit_genie ((NODE_T *) A68G (f_entry), A68G_RUNTIME_ERROR);
341 }
342 }
343 (void) a68g_alarm (A68G_SIGALRM_INTERVAL);
344 }
345
346 //! @brief Install_signal_handlers.
347
348 void install_sigalrm (void)
349 {
350 #if defined (HAVE_SIGACTION)
351 struct sigaction sa;
352 memset (&sa, 0, sizeof (sa));
353 sa.sa_handler = sigalrm_handler;
354 if (OPTION_RESTART (&A68G_JOB)) { // Default
355 sa.sa_flags = SA_RESTART;
356 }
357 ABEND (sigaction (SIGALRM, &sa, NULL) != 0, ERROR_ACTION, NO_TEXT);
358 #endif
359 }
360
361 #endif
362
363 //! @brief Install_signal_handlers.
364
365 void install_signal_handlers (void)
366 {
367 install_sigint ();
368 install_sigsegv ();
369 #if defined (SIGWINCH)
370 install_sigwinch ();
371 #endif
372 #if defined (BUILD_UNIX)
373 install_sigalrm ();
374 install_sigpipe ();
375 install_sigttin ();
376 #endif
377 }
378
379 //! @brief Wall clock versus arbitrary origin.
380
381 REAL_T wall_seconds (void)
382 {
383 #if defined (HAVE_RTLIB)
384 struct timespec ts;
385 clock_gettime (CLOCK_MONOTONIC, &ts);
386 return ts.tv_sec + ts.tv_nsec * 1e-9;
387 #else
388 return seconds();
389 #endif
390 }
391
392 //! @brief Process time versus arbitrary origin.
393
394 REAL_T seconds (void)
395 {
396 return (REAL_T) clock () / (REAL_T) CLOCKS_PER_SEC;
397 }
398
399 //! @brief Delay for specified number of microseconds.
400
401 int a68g_usleep (unsigned delay)
402 {
403 #if defined (BUILD_WINDOWS)
404 errno = ENOSYS; // Function not implemented.
405 return -1;
406 #else
407 // usleep is not compatible with _XOPEN_SOURCE 700.
408 struct timespec reqtime;
409 reqtime.tv_sec = delay / 1000000;
410 reqtime.tv_nsec = 1000 * (delay % 1000000);
411 return nanosleep (&reqtime, NULL);
412 #endif
413 }
414
415 //! @brief Safely set buffer.
416
417 void *a68g_bufset (void *dst, int val, size_t len)
418 {
419 // Function a68g_bufset can be optimized away by a compiler.
420 // Therefore we have this alternative.
421 ASSERT (dst != NO_TEXT);
422 char *p = (char *) dst;
423 while (len-- > 0) {
424 *(p++) = val;
425 }
426 return dst;
427 }
428
429 //! @brief Safely append to buffer.
430
431 void a68g_bufcat (char *dst, char *src, size_t len)
432 {
433 ASSERT (dst != NO_TEXT);
434 ASSERT (src != NO_TEXT);
435 char *d = dst, *s = src;
436 int n = len;
437 // Find end of dst and left-adjust; do not go past end.
438 for (; n-- != 0 && d[0] != NULL_CHAR; d++) {
439 ;
440 }
441 int dlen = (int) (d - dst);
442 n = len - dlen;
443 if (n > 0) {
444 while (s[0] != NULL_CHAR) {
445 if (n != 1) {
446 (d++)[0] = s[0];
447 n--;
448 }
449 s++;
450 }
451 d[0] = NULL_CHAR;
452 }
453 // Better sure than sorry.
454 dst[len - 1] = NULL_CHAR;
455 }
456
457 //! @brief Safely copy to buffer.
458
459 void a68g_bufcpy (char *dst, char *src, size_t len)
460 {
461 ASSERT (dst != NO_TEXT);
462 ASSERT (src != NO_TEXT);
463 char *d = dst, *s = src;
464 int n = len;
465 // Copy as many as fit.
466 if (n > 0 && --n > 0) {
467 do {
468 if (((d++)[0] = (s++)[0]) == NULL_CHAR) {
469 break;
470 }
471 } while (--n > 0);
472 }
473 if (n == 0 && len > 0) {
474 // Not enough room in dst, so terminate.
475 d[0] = NULL_CHAR;
476 }
477 // Better sure than sorry.
478 dst[len - 1] = NULL_CHAR;
479 }
480
481 //! @brief Safely print to buffer.
482
483 int a68g_bufprt (char *dst, size_t len, const char *format, ...)
484 {
485 ASSERT (dst != NO_TEXT);
486 ASSERT (len > 1);
487 ASSERT (format != NO_TEXT);
488 va_list ap;
489 va_start (ap, format);
490 int rc = vsnprintf (dst, len, format, ap);
491 va_end (ap);
492 // Better sure than sorry.
493 dst[len - 1] = NULL_CHAR;
494 if (rc >= 0 && len <= (size_t) rc) {
495 return -1;
496 } else {
497 return rc;
498 }
499 }
500
© 2002-2025 J.M. van der Veer (jmvdveer@xs4all.nl)
|