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)