a68g-diagnostics.c

     
   1  //! @file a68g-diagnostics.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  //! Error and warning routines.
  25  
  26  #include "a68g.h"
  27  #include "a68g-prelude.h"
  28  #include "a68g-transput.h"
  29  #include "a68g-parser.h"
  30  
  31  // Error handling routines.
  32  
  33  #define TABULATE(n) (8 * (n / 8 + 1) - n)
  34  
  35  //! @brief Return error test from errno.
  36  
  37  char *error_specification (void)
  38  {
  39    static BUFFER txt;
  40    if (errno == 0) {
  41      ASSERT (a68g_bufprt (txt, SNPRINTF_SIZE, "no information") >= 0);
  42    } else {
  43      ASSERT (a68g_bufprt (txt, SNPRINTF_SIZE, "%s", strerror (errno)) >= 0);
  44    }
  45    if (strlen (txt) > 0) {
  46      txt[0] = TO_LOWER (txt[0]);
  47    }
  48    return txt;
  49  }
  50  
  51  //! @brief Whether unprintable control character.
  52  
  53  BOOL_T unprintable (char ch)
  54  {
  55    return (BOOL_T) (!IS_PRINT (ch) && ch != TAB_CHAR);
  56  }
  57  
  58  //! @brief Format for printing control character.
  59  
  60  char *ctrl_char (int ch)
  61  {
  62    static char txt[SMALL_BUFFER_SIZE];
  63    ch = TO_UCHAR (ch);
  64    if (IS_CNTRL (ch) && IS_LOWER (ch + 96)) {
  65      ASSERT (a68g_bufprt (txt, (size_t) SMALL_BUFFER_SIZE, "\\^%c", ch + 96) >= 0);
  66    } else {
  67      ASSERT (a68g_bufprt (txt, (size_t) SMALL_BUFFER_SIZE, "\\%02x", (unt) ch) >= 0);
  68    }
  69    return txt;
  70  }
  71  
  72  //! @brief Widen single char to string.
  73  
  74  char *char_to_str (char ch)
  75  {
  76    static char txt[2];
  77    txt[0] = ch;
  78    txt[1] = NULL_CHAR;
  79    return txt;
  80  }
  81  
  82  //! @brief Pretty-print diagnostic.
  83  
  84  void pretty_diag (FILE_T f, char *p)
  85  {
  86    int line_width = (f == A68G_STDERR || f == A68G_STDOUT ? A68G (term_width) : MAX_TERM_WIDTH);
  87    int pos = 1;
  88    while (p[0] != NULL_CHAR) {
  89      char *q;
  90      int k;
  91  // Count the number of characters in token to print.
  92      if (IS_GRAPH (p[0])) {
  93        for (k = 0, q = p; q[0] != BLANK_CHAR && q[0] != NULL_CHAR && k <= line_width; q++, k++) {
  94          ;
  95        }
  96      } else {
  97        k = 1;
  98      }
  99  // Now see if there is space for the token.
 100      if (k > line_width) {
 101        k = 1;
 102      }
 103      if ((pos + k) >= line_width) {
 104        WRITE (f, NEWLINE_STRING);
 105        pos = 1;
 106      }
 107      for (; k > 0; k--, p++, pos++) {
 108        WRITE (f, char_to_str (p[0]));
 109      }
 110    }
 111    for (; p[0] == BLANK_CHAR; p++, pos++) {
 112      WRITE (f, char_to_str (p[0]));
 113    }
 114  }
 115  
 116  //! @brief Abnormal end.
 117  
 118  void abend (char *reason, char *info, char *file, int line)
 119  {
 120    ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%s: exiting: %s: %d: %s", A68G (a68g_cmd_name), file, line, reason) >= 0);
 121    if (info != NO_TEXT) {
 122      a68g_bufcat (A68G (output_line), ", ", BUFFER_SIZE);
 123      a68g_bufcat (A68G (output_line), info, BUFFER_SIZE);
 124    }
 125    if (errno != 0) {
 126      a68g_bufcat (A68G (output_line), " (", BUFFER_SIZE);
 127      a68g_bufcat (A68G (output_line), error_specification (), BUFFER_SIZE);
 128      a68g_bufcat (A68G (output_line), ")", BUFFER_SIZE);
 129    }
 130    a68g_bufcat (A68G (output_line), "\n", BUFFER_SIZE);
 131    io_close_tty_line ();
 132    pretty_diag (A68G_STDERR, A68G (output_line));
 133    a68g_exit (EXIT_FAILURE);
 134  }
 135  
 136  //! @brief Position in line .
 137  
 138  char *where_pos (LINE_T * p, NODE_T * q)
 139  {
 140    char *pos;
 141    if (q != NO_NODE && p == LINE (INFO (q))) {
 142      pos = CHAR_IN_LINE (INFO (q));
 143    } else {
 144      pos = STRING (p);
 145    }
 146    if (pos == NO_TEXT) {
 147      pos = STRING (p);
 148    }
 149    for (; IS_SPACE (pos[0]) && pos[0] != NULL_CHAR; pos++) {
 150      ;
 151    }
 152    if (pos[0] == NULL_CHAR) {
 153      pos = STRING (p);
 154    }
 155    return pos;
 156  }
 157  
 158  //! @brief Position in line where diagnostic points at.
 159  
 160  char *diag_pos (LINE_T * p, DIAGNOSTIC_T * d)
 161  {
 162    char *pos;
 163    if (WHERE (d) != NO_NODE && p == LINE (INFO (WHERE (d)))) {
 164      pos = CHAR_IN_LINE (INFO (WHERE (d)));
 165    } else {
 166      pos = STRING (p);
 167    }
 168    if (pos == NO_TEXT) {
 169      pos = STRING (p);
 170    }
 171    for (; IS_SPACE (pos[0]) && pos[0] != NULL_CHAR; pos++) {
 172      ;
 173    }
 174    if (pos[0] == NULL_CHAR) {
 175      pos = STRING (p);
 176    }
 177    return pos;
 178  }
 179  
 180  //! @brief Write source line to file with diagnostics.
 181  
 182  void write_source_line (FILE_T f, LINE_T * p, NODE_T * nwhere, int mask)
 183  {
 184    int line_width = (f == A68G_STDERR || f == A68G_STDOUT ? A68G (term_width) : MAX_TERM_WIDTH);
 185  // Terminate properly.
 186    if ((STRING (p))[strlen (STRING (p)) - 1] == NEWLINE_CHAR) {
 187      (STRING (p))[strlen (STRING (p)) - 1] = NULL_CHAR;
 188      if ((STRING (p))[strlen (STRING (p)) - 1] == CR_CHAR) {
 189        (STRING (p))[strlen (STRING (p)) - 1] = NULL_CHAR;
 190      }
 191    }
 192  // Print line number.
 193    if (f == A68G_STDERR || f == A68G_STDOUT) {
 194      io_close_tty_line ();
 195    } else {
 196      WRITE (f, NEWLINE_STRING);
 197    }
 198    if (NUMBER (p) == 0) {
 199      ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "      ") >= 0);
 200    } else {
 201      ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%-5d ", NUMBER (p) % 100000) >= 0);
 202    }
 203    WRITE (f, A68G (output_line));
 204  // Pretty print line.
 205    char *c, *c0;
 206    c = c0 = STRING (p);
 207    int pos = 5, col = 1;
 208    int continuations = 0;
 209    BOOL_T line_ended = A68G_FALSE;
 210    while (!line_ended) {
 211      int len = 0;
 212      char *new_pos = NO_TEXT;
 213      if (c[0] == NULL_CHAR) {
 214        a68g_bufcpy (A68G (output_line), "", BUFFER_SIZE);
 215        line_ended = A68G_TRUE;
 216      } else {
 217        if (IS_GRAPH (c[0])) {
 218          char *c1;
 219          a68g_bufcpy (A68G (output_line), "", BUFFER_SIZE);
 220          for (c1 = c; IS_GRAPH (c1[0]) && len <= line_width - 5; c1++, len++) {
 221            a68g_bufcat (A68G (output_line), char_to_str (c1[0]), BUFFER_SIZE);
 222          }
 223          if (len > line_width - 5) {
 224            a68g_bufcpy (A68G (output_line), char_to_str (c[0]), BUFFER_SIZE);
 225            len = 1;
 226          }
 227          new_pos = &c[len];
 228          col += len;
 229        } else if (c[0] == TAB_CHAR) {
 230          int n = TABULATE (col);
 231          len = n;
 232          col += n;
 233          a68g_bufcpy (A68G (output_line), "", BUFFER_SIZE);
 234          while (n--) {
 235            a68g_bufcat (A68G (output_line), " ", BUFFER_SIZE);
 236          }
 237          new_pos = &c[1];
 238        } else if (unprintable (c[0])) {
 239          a68g_bufcpy (A68G (output_line), ctrl_char ((int) c[0]), BUFFER_SIZE);
 240          len = (int) strlen (A68G (output_line));
 241          new_pos = &c[1];
 242          col++;
 243        } else {
 244          a68g_bufcpy (A68G (output_line), char_to_str (c[0]), BUFFER_SIZE);
 245          len = 1;
 246          new_pos = &c[1];
 247          col++;
 248        }
 249      }
 250      if (!line_ended && (pos + len) <= line_width) {
 251  // Still room - print a character.
 252        WRITE (f, A68G (output_line));
 253        pos += len;
 254        c = new_pos;
 255      } else {
 256  // First see if there are diagnostics to be printed.
 257        BOOL_T y = A68G_FALSE, z = A68G_FALSE;
 258        DIAGNOSTIC_T *d = DIAGNOSTICS (p);
 259        if (d != NO_DIAGNOSTIC || nwhere != NO_NODE) {
 260          char *c1;
 261          for (c1 = c0; c1 != c; c1++) {
 262            y |= (BOOL_T) (nwhere != NO_NODE && p == LINE (INFO (nwhere)) ? c1 == where_pos (p, nwhere) : A68G_FALSE);
 263            if (mask != A68G_NO_DIAGNOSTICS) {
 264              for (d = DIAGNOSTICS (p); d != NO_DIAGNOSTIC; FORWARD (d)) {
 265                z = (BOOL_T) (z | (c1 == diag_pos (p, d)));
 266              }
 267            }
 268          }
 269        }
 270  // If diagnostics are to be printed then print marks.
 271        if (y || z) {
 272          DIAGNOSTIC_T *d2;
 273          char *c1;
 274          int col_2 = 1;
 275          WRITE (f, "\n      ");
 276          for (c1 = c0; c1 != c; c1++) {
 277            int k = 0, diags_at_this_pos = 0;
 278            for (d2 = DIAGNOSTICS (p); d2 != NO_DIAGNOSTIC; FORWARD (d2)) {
 279              if (c1 == diag_pos (p, d2)) {
 280                diags_at_this_pos++;
 281                k = NUMBER (d2);
 282              }
 283            }
 284            if (y == A68G_TRUE && c1 == where_pos (p, nwhere)) {
 285              a68g_bufcpy (A68G (output_line), "-", BUFFER_SIZE);
 286            } else if (diags_at_this_pos != 0) {
 287              if (mask == A68G_NO_DIAGNOSTICS) {
 288                a68g_bufcpy (A68G (output_line), " ", BUFFER_SIZE);
 289              } else if (diags_at_this_pos == 1) {
 290                ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%c", digchar (k)) >= 0);
 291              } else {
 292                a68g_bufcpy (A68G (output_line), "*", BUFFER_SIZE);
 293              }
 294            } else {
 295              if (unprintable (c1[0])) {
 296                int n = (int) strlen (ctrl_char (c1[0]));
 297                col_2 += 1;
 298                a68g_bufcpy (A68G (output_line), "", BUFFER_SIZE);
 299                while (n--) {
 300                  a68g_bufcat (A68G (output_line), " ", BUFFER_SIZE);
 301                }
 302              } else if (c1[0] == TAB_CHAR) {
 303                int n = TABULATE (col_2);
 304                col_2 += n;
 305                a68g_bufcpy (A68G (output_line), "", BUFFER_SIZE);
 306                while (n--) {
 307                  a68g_bufcat (A68G (output_line), " ", BUFFER_SIZE);
 308                }
 309              } else {
 310                a68g_bufcpy (A68G (output_line), " ", BUFFER_SIZE);
 311                col_2++;
 312              }
 313            }
 314            WRITE (f, A68G (output_line));
 315          }
 316        }
 317  // Resume pretty printing of line.
 318        if (!line_ended) {
 319          continuations++;
 320          ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "\n.%1d   ", continuations) >= 0);
 321          WRITE (f, A68G (output_line));
 322          if (continuations >= 9) {
 323            WRITE (f, "...");
 324            line_ended = A68G_TRUE;
 325          } else {
 326            c0 = c;
 327            pos = 5;
 328            col = 1;
 329          }
 330        }
 331      }
 332    }
 333  // Print the diagnostics.
 334    if (mask) {
 335      if (DIAGNOSTICS (p) != NO_DIAGNOSTIC) {
 336        DIAGNOSTIC_T *d;
 337        for (d = DIAGNOSTICS (p); d != NO_DIAGNOSTIC; FORWARD (d)) {
 338          if (mask == A68G_RUNTIME_ERROR) {
 339            if (IS (d, A68G_RUNTIME_ERROR) || IS (d, A68G_MATH_ERROR) || (IS (d, A68G_MATH_WARNING))) {
 340              WRITE (f, NEWLINE_STRING);
 341              pretty_diag (f, TEXT (d));
 342            }
 343          } else {
 344            WRITE (f, NEWLINE_STRING);
 345            pretty_diag (f, TEXT (d));
 346          }
 347        }
 348      }
 349    }
 350  }
 351  
 352  //! @brief Write diagnostics to STDERR.
 353  
 354  void diagnostics_to_terminal (LINE_T * p, int sev)
 355  {
 356    for (; p != NO_LINE; FORWARD (p)) {
 357      if (DIAGNOSTICS (p) != NO_DIAGNOSTIC) {
 358        int N = 0;
 359        for (DIAGNOSTIC_T *d = DIAGNOSTICS (p); d != NO_DIAGNOSTIC; FORWARD (d)) {
 360          if (sev == A68G_ALL_DIAGNOSTICS) {
 361            if (
 362              IS (d, A68G_ERROR)         || 
 363              IS (d, A68G_NOTICE)        || 
 364              IS (d, A68G_SCANNER_ERROR) ||
 365              IS (d, A68G_SYNTAX_ERROR)  || 
 366              IS (d, A68G_WARNING)
 367            ) {
 368              N++;
 369            }
 370          } 
 371          if (sev == A68G_ALL_DIAGNOSTICS || sev == A68G_RUNTIME_ERROR) {
 372            if (
 373              IS (d, A68G_MATH_ERROR)   ||
 374              IS (d, A68G_MATH_WARNING) ||
 375              IS (d, A68G_RUNTIME_ERROR)
 376            ) {
 377              N++;
 378            }
 379          }
 380        }
 381        if (N > 0) {
 382          write_source_line (A68G_STDERR, p, NO_NODE, sev);
 383        }
 384      }
 385    }
 386  }
 387  
 388  //! @brief Give an intelligible error and exit.
 389  
 390  void scan_error (LINE_T * u, char *v, char *txt)
 391  {
 392    if (errno != 0) {
 393      diagnostic (A68G_SCANNER_ERROR, NO_NODE, txt, u, v, error_specification ());
 394    } else {
 395      diagnostic (A68G_SCANNER_ERROR, NO_NODE, txt, u, v, ERROR_UNSPECIFIED);
 396    }
 397    longjmp (RENDEZ_VOUS (&A68G_JOB), 1);
 398  }
 399  
 400  void scan_error_info (LINE_T * u, char *v, char *txt, char *info)
 401  {
 402    if (info != NO_TEXT) {
 403      size_t len = strlen (txt) + 3;
 404      char *txti = a68g_alloc(len, __FILE__, __LINE__);
 405      ABEND (txti == NO_TEXT, ERROR_OUT_OF_CORE, __func__);
 406      a68g_bufcpy (txti, txt, len);
 407      a68g_bufcat (txti, " Z", len);
 408      if (errno != 0) {
 409        diagnostic (A68G_SCANNER_ERROR, NO_NODE, txti, u, v, info, error_specification ());
 410      } else {
 411        diagnostic (A68G_SCANNER_ERROR, NO_NODE, txti, u, v, info, ERROR_UNSPECIFIED);
 412      }
 413      longjmp (RENDEZ_VOUS (&A68G_JOB), 1);
 414    } else {
 415      scan_error(u, v, txt);
 416    }
 417  }
 418  
 419  //! @brief Give an intelligible warning.
 420  
 421  void scan_warning (LINE_T * u, char *v, char *txt)
 422  {
 423    if (errno != 0) {
 424      diagnostic (A68G_SCANNER_WARNING, NO_NODE, txt, u, v, error_specification ());
 425    } else {
 426      diagnostic (A68G_SCANNER_WARNING, NO_NODE, txt, u, v, ERROR_UNSPECIFIED);
 427    }
 428  }
 429  
 430  //! @brief Get severity text.
 431  
 432  char *get_severity (int sev)
 433  {
 434    switch (sev) {
 435    case A68G_ERROR: {
 436        return "error";
 437      }
 438    case A68G_SYNTAX_ERROR: {
 439        return "syntax error";
 440      }
 441    case A68G_RUNTIME_ERROR: {
 442        return "runtime error";
 443      }
 444    case A68G_MATH_ERROR: {
 445        return "math error";
 446      }
 447    case A68G_MATH_WARNING: {
 448        return "math warning";
 449      }
 450    case A68G_NOTICE: {
 451        return "notice";
 452      }
 453    case A68G_WARNING: {
 454        return "warning";
 455      }
 456    case A68G_SCANNER_ERROR: {
 457        return "scanner error";
 458      }
 459    case A68G_SCANNER_WARNING: {
 460        return "scanner warning";
 461      }
 462    default: {
 463        return NO_TEXT;
 464      }
 465    }
 466  }
 467  
 468  //! @brief Print diagnostic.
 469  
 470  void write_diagnostic (int sev, char *b)
 471  {
 472    char txt[SMALL_BUFFER_SIZE];
 473    char *severity = get_severity (sev);
 474    if (severity == NO_TEXT) {
 475      ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%s: %s.", A68G (a68g_cmd_name), b) >= 0);
 476    } else {
 477      a68g_bufcpy (txt, get_severity (sev), SMALL_BUFFER_SIZE);
 478      ASSERT (a68g_bufprt (A68G (output_line), SNPRINTF_SIZE, "%s: %s: %s.", A68G (a68g_cmd_name), txt, b) >= 0);
 479    }
 480    io_close_tty_line ();
 481    pretty_diag (A68G_STDERR, A68G (output_line));
 482  }
 483  
 484  //! @brief Add diagnostic to source line.
 485  
 486  void add_diagnostic (LINE_T * line, char *pos, NODE_T * p, int sev, char *b)
 487  {
 488  // Add diagnostic and choose GNU style or non-GNU style.
 489    DIAGNOSTIC_T *msg = (DIAGNOSTIC_T *) get_heap_space ((size_t) SIZE_ALIGNED (DIAGNOSTIC_T));
 490    DIAGNOSTIC_T **ref_msg;
 491    BUFFER a, nst;
 492    char st[SMALL_BUFFER_SIZE];
 493    char *severity = get_severity (sev);
 494    int k = 1;
 495    if (line == NO_LINE && p == NO_NODE) {
 496      return;
 497    }
 498    if (A68G (in_monitor)) {
 499      monitor_error (b, NO_TEXT);
 500      return;
 501    }
 502    nst[0] = NULL_CHAR;
 503    if (line == NO_LINE && p != NO_NODE) {
 504      line = LINE (INFO (p));
 505    }
 506    while (line != NO_LINE && NUMBER (line) == 0) {
 507      FORWARD (line);
 508    }
 509    if (line == NO_LINE) {
 510      return;
 511    }
 512    ref_msg = &(DIAGNOSTICS (line));
 513    while (*ref_msg != NO_DIAGNOSTIC) {
 514      ref_msg = &(NEXT (*ref_msg));
 515      k++;
 516    }
 517    if (p != NO_NODE) {
 518      NODE_T *n = NEST (p);
 519      if (n != NO_NODE && NSYMBOL (n) != NO_TEXT) {
 520        char *nt = non_terminal_string (A68G (edit_line), ATTRIBUTE (n));
 521        if (nt != NO_TEXT) {
 522          if (LINE_NUMBER (n) == 0) {
 523            ASSERT (a68g_bufprt (nst, SNPRINTF_SIZE, ", in %s", nt) >= 0);
 524          } else {
 525            if (MOID (n) != NO_MOID) {
 526              if (LINE_NUMBER (n) == NUMBER (line)) {
 527                ASSERT (a68g_bufprt (nst, SNPRINTF_SIZE, ", in %s %s starting at \"%.64s\" in this line", moid_to_string (MOID (n), MOID_ERROR_WIDTH, p), nt, NSYMBOL (n)) >= 0);
 528              } else {
 529                ASSERT (a68g_bufprt (nst, SNPRINTF_SIZE, ", in %s %s starting at \"%.64s\" in line %d", moid_to_string (MOID (n), MOID_ERROR_WIDTH, p), nt, NSYMBOL (n), LINE_NUMBER (n)) >= 0);
 530              }
 531            } else {
 532              if (LINE_NUMBER (n) == NUMBER (line)) {
 533                ASSERT (a68g_bufprt (nst, SNPRINTF_SIZE, ", in %s starting at \"%.64s\" in this line", nt, NSYMBOL (n)) >= 0);
 534              } else {
 535                ASSERT (a68g_bufprt (nst, SNPRINTF_SIZE, ", in %s starting at \"%.64s\" in line %d", nt, NSYMBOL (n), LINE_NUMBER (n)) >= 0);
 536              }
 537            }
 538          }
 539        }
 540      }
 541    }
 542    if (severity == NO_TEXT) {
 543      if (FILENAME (line) != NO_TEXT && strcmp (FILE_SOURCE_NAME (&A68G_JOB), FILENAME (line)) == 0) {
 544        ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %x: %s", A68G (a68g_cmd_name), (unt) k, b) >= 0);
 545      } else if (FILENAME (line) != NO_TEXT) {
 546        ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %s: %x: %s", A68G (a68g_cmd_name), FILENAME (line), (unt) k, b) >= 0);
 547      } else {
 548        ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %x: %s", A68G (a68g_cmd_name), (unt) k, b) >= 0);
 549      }
 550    } else {
 551      a68g_bufcpy (st, get_severity (sev), SMALL_BUFFER_SIZE);
 552      if (FILENAME (line) != NO_TEXT && strcmp (FILE_SOURCE_NAME (&A68G_JOB), FILENAME (line)) == 0) {
 553        ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %s: %x: %s", A68G (a68g_cmd_name), st, (unt) k, b) >= 0);
 554      } else if (FILENAME (line) != NO_TEXT) {
 555        ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %s: %s: %x: %s", A68G (a68g_cmd_name), FILENAME (line), st, (unt) k, b) >= 0);
 556      } else {
 557        ASSERT (a68g_bufprt (a, SNPRINTF_SIZE, "%s: %s: %x: %s", A68G (a68g_cmd_name), st, (unt) k, b) >= 0);
 558      }
 559    }
 560  // cppcheck might complain here but this memory is not returned, for obvious reasons.
 561    *ref_msg = msg;
 562    ATTRIBUTE (msg) = sev;
 563    if (nst[0] != NULL_CHAR) {
 564      a68g_bufcat (a, nst, BUFFER_SIZE);
 565    }
 566    a68g_bufcat (a, ".", BUFFER_SIZE);
 567    TEXT (msg) = new_string (a, NO_TEXT);
 568    WHERE (msg) = p;
 569    LINE (msg) = line;
 570    SYMBOL (msg) = pos;
 571    NUMBER (msg) = k;
 572    NEXT (msg) = NO_DIAGNOSTIC;
 573  }
 574  
 575  //! @brief Give a diagnostic message.
 576  
 577  void diagnostic (STATUS_MASK_T sev, NODE_T * p, char *loc_str, ...)
 578  {
 579    va_list args;
 580    MOID_T *moid = NO_MOID;
 581    char *t = loc_str, b[BUFFER_SIZE];
 582    BOOL_T compose = A68G_TRUE, issue = A68G_TRUE;
 583    va_start (args, loc_str);
 584    b[0] = NULL_CHAR;
 585  // Node or line?
 586    LINE_T *line = NO_LINE;
 587    char *pos = NO_TEXT;
 588    if (p == NO_NODE) {
 589      line = va_arg (args, LINE_T *);
 590      pos = va_arg (args, char *);
 591    }
 592  // No warnings or notices?
 593    if (sev == A68G_NOTICE && OPTION_NO_NOTICES (&A68G_JOB)) {
 594      va_end (args);
 595      return;
 596    }
 597    if (sev == A68G_WARNING && OPTION_NO_WARNINGS (&A68G_JOB)) {
 598      va_end (args);
 599      return;
 600    }
 601    if (sev == A68G_MATH_WARNING && OPTION_NO_WARNINGS (&A68G_JOB)) {
 602      va_end (args);
 603      return;
 604    }
 605    if (sev == A68G_NOTICE && OPTION_QUIET (&A68G_JOB)) {
 606      va_end (args);
 607      return;
 608    }
 609    if (sev == A68G_WARNING && OPTION_QUIET (&A68G_JOB)) {
 610      va_end (args);
 611      return;
 612    }
 613    if (sev == A68G_MATH_WARNING && OPTION_QUIET (&A68G_JOB)) {
 614      va_end (args);
 615      return;
 616    }
 617  // Suppressed?.
 618    if (sev == A68G_ERROR || sev == A68G_SYNTAX_ERROR) {
 619      if (ERROR_COUNT (&A68G_JOB) == MAX_ERRORS) {
 620        a68g_bufcpy (b, "further diagnostics suppressed", BUFFER_SIZE);
 621        compose = A68G_FALSE;
 622        sev = A68G_ERROR;
 623      } else if (ERROR_COUNT (&A68G_JOB) > MAX_ERRORS) {
 624        ERROR_COUNT (&A68G_JOB)++;
 625        compose = issue = A68G_FALSE;
 626      }
 627    } else if (sev == A68G_NOTICE || sev == A68G_WARNING || sev == A68G_MATH_WARNING) {
 628      if (WARNING_COUNT (&A68G_JOB) == MAX_ERRORS) {
 629        a68g_bufcpy (b, "further diagnostics suppressed", BUFFER_SIZE);
 630        compose = A68G_FALSE;
 631      } else if (WARNING_COUNT (&A68G_JOB) > MAX_ERRORS) {
 632        WARNING_COUNT (&A68G_JOB)++;
 633        compose = issue = A68G_FALSE;
 634      }
 635    }
 636    if (compose) {
 637  // Synthesize diagnostic message.
 638      if ((sev & A68G_NO_SYNTHESIS) != NULL_MASK) {
 639        sev &= ~A68G_NO_SYNTHESIS;
 640        a68g_bufcat (b, t, BUFFER_SIZE);
 641      } else {
 642  // Legend for special symbols:
 643  // * as first character, copy rest of string literally
 644  // # skip extra syntactical information
 645  // @ non terminal
 646  // A non terminal
 647  // B keyword
 648  // C context
 649  // D argument in decimal
 650  // H char argument
 651  // K 'LONG'
 652  // L line number
 653  // M moid - if error mode return without giving a message
 654  // N mode - M_NIL
 655  // O moid - operand
 656  // S quoted symbol, when possible with typographical display features
 657  // X expected attribute
 658  // Y string literal. 
 659  // Z quoted string literal. 
 660        if (t[0] == '*') {
 661          a68g_bufcat (b, &t[1], BUFFER_SIZE);
 662        } else
 663          while (t[0] != NULL_CHAR) {
 664            if (t[0] == '#') {
 665              ;
 666            } else if (t[0] == '@') {
 667              if (p == NO_NODE) {
 668                a68g_bufcat (b, "construct", BUFFER_SIZE);
 669              } else {
 670                char *nt = non_terminal_string (A68G (edit_line), ATTRIBUTE (p));
 671                if (t != NO_TEXT) {
 672                  a68g_bufcat (b, nt, BUFFER_SIZE);
 673                } else {
 674                  a68g_bufcat (b, "construct", BUFFER_SIZE);
 675                }
 676              }
 677            } else if (t[0] == 'A') {
 678              int att = va_arg (args, int);
 679              char *nt = non_terminal_string (A68G (edit_line), att);
 680              if (nt != NO_TEXT) {
 681                a68g_bufcat (b, nt, BUFFER_SIZE);
 682              } else {
 683                a68g_bufcat (b, "construct", BUFFER_SIZE);
 684              }
 685            } else if (t[0] == 'B') {
 686              int att = va_arg (args, int);
 687              KEYWORD_T *nt = find_keyword_from_attribute (A68G (top_keyword), att);
 688              if (nt != NO_KEYWORD) {
 689                a68g_bufcat (b, "\"", BUFFER_SIZE);
 690                a68g_bufcat (b, TEXT (nt), BUFFER_SIZE);
 691                a68g_bufcat (b, "\"", BUFFER_SIZE);
 692              } else {
 693                a68g_bufcat (b, "keyword", BUFFER_SIZE);
 694              }
 695            } else if (t[0] == 'C') {
 696              int att = va_arg (args, int);
 697              if (att == NO_SORT) {
 698                a68g_bufcat (b, "this", BUFFER_SIZE);
 699              }
 700              if (att == SOFT) {
 701                a68g_bufcat (b, "a soft", BUFFER_SIZE);
 702              } else if (att == WEAK) {
 703                a68g_bufcat (b, "a weak", BUFFER_SIZE);
 704              } else if (att == MEEK) {
 705                a68g_bufcat (b, "a meek", BUFFER_SIZE);
 706              } else if (att == FIRM) {
 707                a68g_bufcat (b, "a firm", BUFFER_SIZE);
 708              } else if (att == STRONG) {
 709                a68g_bufcat (b, "a strong", BUFFER_SIZE);
 710              }
 711            } else if (t[0] == 'D') {
 712              int a = va_arg (args, int);
 713              BUFFER d;
 714              BUFCLR (d);
 715              ASSERT (a68g_bufprt (d, SNPRINTF_SIZE, "%d", a) >= 0);
 716              a68g_bufcat (b, d, BUFFER_SIZE);
 717            } else if (t[0] == 'H') {
 718              char *a = va_arg (args, char *);
 719              char d[SMALL_BUFFER_SIZE];
 720              ASSERT (a68g_bufprt (d, (size_t) SMALL_BUFFER_SIZE, "\"%c\"", a[0]) >= 0);
 721              a68g_bufcat (b, d, BUFFER_SIZE);
 722            } else if (t[0] == 'K') {
 723              a68g_bufcat (b, "LONG", BUFFER_SIZE);
 724            } else if (t[0] == 'L') {
 725              LINE_T *a = va_arg (args, LINE_T *);
 726              char d[SMALL_BUFFER_SIZE];
 727              ABEND (a == NO_LINE, ERROR_INTERNAL_CONSISTENCY, __func__);
 728              if (NUMBER (a) == 0) {
 729                a68g_bufcat (b, "in standard environment", BUFFER_SIZE);
 730              } else {
 731                if (p != NO_NODE && NUMBER (a) == LINE_NUMBER (p)) {
 732                  ASSERT (a68g_bufprt (d, (size_t) SMALL_BUFFER_SIZE, "in this line") >= 0);
 733                } else {
 734                  ASSERT (a68g_bufprt (d, (size_t) SMALL_BUFFER_SIZE, "in line %d", NUMBER (a)) >= 0);
 735                }
 736                a68g_bufcat (b, d, BUFFER_SIZE);
 737              }
 738            } else if (t[0] == 'M') {
 739              moid = va_arg (args, MOID_T *);
 740              if (moid == NO_MOID || moid == M_ERROR) {
 741                moid = M_UNDEFINED;
 742              }
 743              if (IS (moid, SERIES_MODE)) {
 744                if (PACK (moid) != NO_PACK && NEXT (PACK (moid)) == NO_PACK) {
 745                  a68g_bufcat (b, moid_to_string (MOID (PACK (moid)), MOID_ERROR_WIDTH, p), BUFFER_SIZE);
 746                } else {
 747                  a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
 748                }
 749              } else {
 750                a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
 751              }
 752            } else if (t[0] == 'N') {
 753              a68g_bufcat (b, "NIL name of mode ", BUFFER_SIZE);
 754              moid = va_arg (args, MOID_T *);
 755              if (moid != NO_MOID) {
 756                a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
 757              }
 758            } else if (t[0] == 'O') {
 759              moid = va_arg (args, MOID_T *);
 760              if (moid == NO_MOID || moid == M_ERROR) {
 761                moid = M_UNDEFINED;
 762              }
 763              if (moid == M_VOID) {
 764                a68g_bufcat (b, "UNION (VOID, ..)", BUFFER_SIZE);
 765              } else if (IS (moid, SERIES_MODE)) {
 766                if (PACK (moid) != NO_PACK && NEXT (PACK (moid)) == NO_PACK) {
 767                  a68g_bufcat (b, moid_to_string (MOID (PACK (moid)), MOID_ERROR_WIDTH, p), BUFFER_SIZE);
 768                } else {
 769                  a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
 770                }
 771              } else {
 772                a68g_bufcat (b, moid_to_string (moid, MOID_ERROR_WIDTH, p), BUFFER_SIZE);
 773              }
 774            } else if (t[0] == 'S') {
 775              if (p != NO_NODE && NSYMBOL (p) != NO_TEXT) {
 776                char *txt = NSYMBOL (p);
 777                char *sym = NCHAR_IN_LINE (p);
 778                int n = 0, size = (int) strlen (txt);
 779                a68g_bufcat (b, "\"", BUFFER_SIZE);
 780                if (txt[0] != sym[0] || (int) strlen (sym) < size) {
 781                  a68g_bufcat (b, txt, BUFFER_SIZE);
 782                } else {
 783                  while (n < size) {
 784                    if (IS_PRINT (sym[0])) {
 785                      char str[2];
 786                      str[0] = sym[0];
 787                      str[1] = NULL_CHAR;
 788                      a68g_bufcat (b, str, BUFFER_SIZE);
 789                    }
 790                    if (TO_LOWER (txt[0]) == TO_LOWER (sym[0])) {
 791                      txt++;
 792                      n++;
 793                    }
 794                    sym++;
 795                  }
 796                }
 797                a68g_bufcat (b, "\"", BUFFER_SIZE);
 798              } else {
 799                a68g_bufcat (b, "symbol", BUFFER_SIZE);
 800              }
 801            } else if (t[0] == 'V') {
 802              a68g_bufcat (b, PACKAGE_STRING, BUFFER_SIZE);
 803            } else if (t[0] == 'X') {
 804              int att = va_arg (args, int);
 805              BUFFER z;
 806              (void) non_terminal_string (z, att);
 807              a68g_bufcat (b, new_string (z, NO_TEXT), BUFFER_SIZE);
 808            } else if (t[0] == 'Y') {
 809              char *loc_string = va_arg (args, char *);
 810              if (loc_string != NO_TEXT) {
 811                a68g_bufcat (b, loc_string, BUFFER_SIZE);
 812              }
 813            } else if (t[0] == 'Z') {
 814              char *loc_string = va_arg (args, char *);
 815              a68g_bufcat (b, "\"", BUFFER_SIZE);
 816              if (loc_string != NO_TEXT) {
 817                a68g_bufcat (b, loc_string, BUFFER_SIZE);
 818              }
 819              a68g_bufcat (b, "\"", BUFFER_SIZE);
 820            } else {
 821              char q[2];
 822              q[0] = t[0];
 823              q[1] = NULL_CHAR;
 824              a68g_bufcat (b, q, BUFFER_SIZE);
 825            }
 826            t++;
 827          }
 828  // Add information from errno, if any.
 829        if (errno != 0) {
 830          char *loc_str2 = new_string (error_specification (), NO_TEXT);
 831          if (loc_str2 != NO_TEXT) {
 832            char *stu;
 833            a68g_bufcat (b, ", ", BUFFER_SIZE);
 834            for (stu = loc_str2; stu[0] != NULL_CHAR; stu++) {
 835              stu[0] = (char) TO_LOWER (stu[0]);
 836            }
 837            a68g_bufcat (b, loc_str2, BUFFER_SIZE);
 838          }
 839        }
 840      }
 841    }
 842  // Construct a diagnostic message.
 843    if (issue) {
 844      if (sev == A68G_NOTICE || sev == A68G_WARNING) {
 845        WARNING_COUNT (&A68G_JOB)++;
 846      } else {
 847        ERROR_COUNT (&A68G_JOB)++;
 848      }
 849      if (p == NO_NODE) {
 850        if (line == NO_LINE) {
 851          write_diagnostic (sev, b);
 852        } else {
 853          add_diagnostic (line, pos, NO_NODE, sev, b);
 854        }
 855      } else {
 856        add_diagnostic (NO_LINE, NO_TEXT, p, sev, b);
 857        if (sev == A68G_MATH_WARNING && p != NO_NODE && LINE (INFO (p)) != NO_LINE) {
 858          write_source_line (A68G_STDERR, LINE (INFO (p)), p, A68G_TRUE);
 859          WRITE (A68G_STDERR, NEWLINE_STRING);
 860        }
 861      }
 862    }
 863    va_end (args);
 864  }
     


© 2002-2025 J.M. van der Veer (jmvdveer@xs4all.nl)