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  }