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  }