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