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-2023 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-genie.h"
30 #include "a68g-postulates.h"
31 #include "a68g-parser.h"
32 #include "a68g-options.h"
33 #include "a68g-optimiser.h"
34 #include "a68g-listing.h"
35
36 #if defined (HAVE_MATHLIB)
37 #include <Rmath.h>
38 #endif
39
40 #if defined (BUILD_WIN32)
41 #include <windows.h>
42 #endif
43
44 #define WRITE_TXT(fn, txt) ASSERT (write ((fn), (txt), 1 + strlen (txt)) != -1)
45
46 #if defined (BUILD_LINUX)
47
48 #include <execinfo.h>
49
50 void genie_sigsegv (NODE_T *p)
51 {
52 (void) p;
53 raise (SIGSEGV);
54 }
55
56 //! @brief Provide a rudimentary backtrace.
57
58 void stack_backtrace (void)
59 {
60 #define DEPTH 16
61 void *array[DEPTH];
62 WRITE_TXT (2, "\n++++ Top of call stack:");
63 int size = backtrace (array, DEPTH);
64 if (size > 0) {
65 WRITE_TXT (2, "\n");
66 backtrace_symbols_fd (array, size, 2);
67 }
68 #undef DEPTH
69 }
70
71 void genie_backtrace (NODE_T *p)
72 {
73 (void) p;
74 stack_backtrace ();
75 }
76
77 #else
78
79 void stack_backtrace (void)
80 {
81 WRITE_TXT (2, "\n++++ Stack backtrace is linux-only");
82 }
83
84 void genie_backtrace (NODE_T *p)
85 {
86 (void) p;
87 stack_backtrace ();
88 }
89 #endif
90
91 //! @brief Open a file in ~/.a68g, if possible.
92
93 FILE *a68_fopen (char *fn, char *mode, char *new_fn)
94 {
95 #if defined (BUILD_WIN32) || !defined (HAVE_DIRENT_H)
96 ASSERT (snprintf (new_fn, SNPRINTF_SIZE, "%s", fn) >= 0);
97 return fopen (new_fn, mode);
98 #else
99 BUFFER dn;
100 int rc;
101 errno = 0;
102 ASSERT (snprintf (dn, SNPRINTF_SIZE, "%s/%s", getenv ("HOME"), A68_DIR) >= 0);
103 rc = mkdir (dn, (mode_t) (S_IRUSR | S_IWUSR | S_IXUSR));
104 if (rc == 0 || (rc == -1 && errno == EEXIST)) {
105 struct stat status;
106 if (stat (dn, &status) == 0 && S_ISDIR (ST_MODE (&status)) != 0) {
107 FILE *f;
108 ASSERT (snprintf (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 (snprintf (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 void sigalrm_handler (int i)
223 {
224 (void) i;
225 if (A68 (in_execution) && !A68 (in_monitor)) {
226 REAL_T _m_t = (REAL_T) OPTION_TIME_LIMIT (&A68_JOB);
227 if (_m_t > 0 && (seconds () - A68 (cputime_0)) > _m_t) {
228 diagnostic (A68_RUNTIME_ERROR, (NODE_T *) A68 (f_entry), ERROR_TIME_LIMIT_EXCEEDED);
229 exit_genie ((NODE_T *) A68 (f_entry), A68_RUNTIME_ERROR);
230 }
231 }
232 (void) alarm (1);
233 }
234
235 #endif
236
237 //! @brief Install_signal_handlers.
238
239 void install_signal_handlers (void)
240 {
241 ABEND (signal (SIGINT, sigint_handler) == SIG_ERR, ERROR_ACTION, __func__);
242 ABEND (signal (SIGSEGV, sigsegv_handler) == SIG_ERR, ERROR_ACTION, __func__);
243 #if defined (SIGWINCH)
244 ABEND (signal (SIGWINCH, sigwinch_handler) == SIG_ERR, ERROR_ACTION, __func__);
245 #endif
246 #if defined (BUILD_UNIX)
247 ABEND (signal (SIGALRM, sigalrm_handler) == SIG_ERR, ERROR_ACTION, __func__);
248 ABEND (signal (SIGPIPE, sigpipe_handler) == SIG_ERR, ERROR_ACTION, __func__);
249 ABEND (signal (SIGTTIN, sigttin_handler) == SIG_ERR, ERROR_ACTION, __func__);
250 #endif
251 }
252
253 //! @brief Time versus arbitrary origin.
254
255 REAL_T seconds (void)
256 {
257 return (REAL_T) clock () / (REAL_T) CLOCKS_PER_SEC;
258 }
259
260 //! @brief Safely append to buffer.
261
262 void bufcat (char *dst, char *src, int len)
263 {
264 if (src != NO_TEXT) {
265 char *d = dst, *s = src;
266 int n = len, dlen;
267 // Find end of dst and left-adjust; do not go past end
268 for (; n-- != 0 && d[0] != NULL_CHAR; d++) {
269 ;
270 }
271 dlen = (int) (d - dst);
272 n = len - dlen;
273 if (n > 0) {
274 while (s[0] != NULL_CHAR) {
275 if (n != 1) {
276 (d++)[0] = s[0];
277 n--;
278 }
279 s++;
280 }
281 d[0] = NULL_CHAR;
282 }
283 // Better sure than sorry
284 dst[len - 1] = NULL_CHAR;
285 }
286 }
287
288 //! @brief Safely copy to buffer.
289
290 void bufcpy (char *dst, char *src, int len)
291 {
292 if (src != NO_TEXT) {
293 char *d = dst, *s = src;
294 int n = len;
295 // Copy as many as fit
296 if (n > 0 && --n > 0) {
297 do {
298 if (((d++)[0] = (s++)[0]) == NULL_CHAR) {
299 break;
300 }
301 } while (--n > 0);
302 }
303 if (n == 0 && len > 0) {
304 // Not enough room in dst, so terminate
305 d[0] = NULL_CHAR;
306 }
307 // Better sure than sorry
308 dst[len - 1] = NULL_CHAR;
309 }
310 }
311
312 // @brief own memmove
313
314 void *a68_memmove (void *dest, void *src, size_t len)
315 {
316 char *d = dest, *s = src;
317 if (d < s) {
318 while (len--) {
319 *d++ = *s++;
320 }
321 } else {
322 char *lasts = s + (len - 1), *lastd = d + (len - 1);
323 while (len--) {
324 *lastd-- = *lasts--;
325 }
326 }
327 return dest;
328 }