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)