#ifndef lint
static char *RCSid = "$Id: error.c,v 1.1.1.1 2003/10/04 08:29:26 prokushev Exp $";
#endif

/*
 *  The Regina Rexx Interpreter
 *  Copyright (C) 1992-1994  Anders Christensen <anders@pvv.unit.no>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "rexx.h"
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <stdarg.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <sys/stat.h>
#include "rexxmsg.h"

typedef struct /* err_tsd: static variables of this module (thread-safe) */
{
   int number_messages;
   int native_language;
   FILE *nls_fp;
   streng *buffer[2];
   struct textindex nls_tmi[NUMBER_ERROR_MESSAGES]; /* indexes for native language messages */
} err_tsd_t;   /* thread-specific but only needed by this module. see init_error */

typedef struct
{
   int errnum;
   int suberrnum;
   char *text;
} errtext_t;

/*
 * English message text - generated by makeerror.rexx
 */
static const errtext_t errtext[NUMBER_ERROR_MESSAGES] =
{
   {   0,  1,"Error %s running %s, line %d:|<value>,<source>,<linenumber>" },
   {   0,  2,"Error %s in interactive trace:|<value>" },
   {   0,  3,"Interactive trace.  \"Trace Off\" to end debug. ENTER to continue." },
   {   2,  0,"Failure during finalization" },
   {   2,  1,"Failure during finalization: %s|<description>" },
   {   3,  0,"Failure during initialization" },
   {   3,  1,"Failure during initialization: %s|<description>" },
   {   4,  0,"Program interrupted" },
   {   4,  1,"Program interrupted with HALT condition: %s|<description>" },
   {   5,  0,"System resources exhausted" },
   {   5,  1,"System resources exhausted: %s|<description>" },
   {   6,  0,"Unmatched \"/*\" or quote" },
   {   6,  1,"Unmatched comment delimiter (\"/*\")" },
   {   6,  2,"Unmatched single quote (')" },
   {   6,  3,"Unmatched double quote (\")" },
   {   7,  0,"WHEN or OTHERWISE expected" },
   {   7,  1,"SELECT on line %d requires WHEN; found \"%s\"|<linenumber>,<token>" },
   {   7,  2,"SELECT on line %d requires WHEN, OTHERWISE, or END; found \"%s\"|<linenumber>,<token>" },
   {   7,  3,"All WHEN expressions of SELECT on line %d are false; OTHERWISE expected|<linenumber>" },
   {   8,  0,"Unexpected THEN or ELSE" },
   {   8,  1,"THEN has no corresponding IF or WHEN clause" },
   {   8,  2,"ELSE has no corresponding THEN clause" },
   {   9,  0,"Unexpected WHEN or OTHERWISE" },
   {   9,  1,"WHEN has no corresponding SELECT" },
   {   9,  2,"OTHERWISE has no corresponding SELECT" },
   {  10,  0,"Unexpected or unmatched END" },
   {  10,  1,"END has no corresponding DO or SELECT" },
   {  10,  2,"END corresponding to DO on line %d must have a symbol following that matches the control variable (or no symbol); found \"%s\"|<linenumber>,<token>" },
   {  10,  3,"END corresponding to DO on line %d must not have a symbol following it because there is no control variable; found \"%s\"|<linenumber>,<token>" },
   {  10,  4,"END corresponding to SELECT on line %d must not have a symbol following; found \"%s\"|<linenumber>,<token>" },
   {  10,  5,"END must not immediately follow THEN" },
   {  10,  6,"END must not immediately follow ELSE" },
   {  11,  0,"[Control stack full]" },
   {  12,  0,"[Clause > 1024 characters]" },
   {  13,  0,"Invalid character in program" },
   {  13,  1,"Invalid character in program \"('%x'X)\"|<hex-encoding>" },
   {  14,  0,"Incomplete DO/SELECT/IF" },
   {  14,  1,"DO instruction requires a matching END" },
   {  14,  2,"SELECT instruction requires a matching END" },
   {  14,  3,"THEN requires a following instruction" },
   {  14,  4,"ELSE requires a following instruction" },
   {  15,  0,"Invalid hexadecimal or binary string" },
   {  15,  1,"Invalid location of blank in position %d in hexadecimal string|<position>" },
   {  15,  2,"Invalid location of blank in position %d in binary string|<position>" },
   {  15,  3,"Only 0-9, a-f, A-F, and blank are valid in a hexadecimal string; found \"%c\"|<char>" },
   {  15,  4,"Only 0, 1, and blank are valid in a binary string; found \"%c\"|<char>" },
   {  16,  0,"Label not found" },
   {  16,  1,"Label \"%s\" not found|<name>" },
   {  16,  2,"Cannot SIGNAL to label \"%s\" because it is inside an IF, SELECT or DO group|<name>" },
   {  16,  3,"Cannot invoke label \"%s\" because it is inside an IF, SELECT or DO group|<name>" },
   {  17,  0,"Unexpected PROCEDURE" },
   {  17,  1,"PROCEDURE is valid only when it is the first instruction executed after an internal CALL or function invocation" },
   {  18,  0,"THEN expected" },
   {  18,  1,"IF keyword on line %d requires matching THEN clause; found \"%s\"|<linenumber>,<token>" },
   {  18,  2,"WHEN keyword on line %d requires matching THEN clause; found \"%s\"|<linenumber>,<token>" },
   {  19,  0,"String or symbol expected" },
   {  19,  1,"String or symbol expected after ADDRESS keyword; found \"%s\"|<token>" },
   {  19,  2,"String or symbol expected after CALL keyword; found \"%s\"|<token>" },
   {  19,  3,"String or symbol expected after NAME keyword; found \"%s\"|<token>" },
   {  19,  4,"String or symbol expected after SIGNAL keyword; found \"%s\"|<token>" },
   {  19,  6,"String or symbol expected after TRACE keyword; found \"%s\"|<token>" },
   {  19,  7,"Symbol expected in parsing pattern; found \"%s\"|<token>" },
   {  20,  0,"Name expected" },
   {  20,  1,"Name required; found \"%s\"|<token>" },
   {  20,  2,"Found \"%s\" where only a name is valid|<token>" },
   {  21,  0,"Invalid data on end of clause" },
   {  21,  1,"The clause ended at an unexpected token; found \"%s\"|<token>" },
   {  22,  0,"Invalid character string" },
   {  22,  1,"Invalid character string '%s'X|<hex-encoding>" },
   {  23,  0,"Invalid data string" },
   {  23,  1,"Invalid data string '%s'X|<hex-encoding>" },
   {  24,  0,"Invalid TRACE request" },
   {  24,  1,"TRACE request letter must be one of \"%s\"; found \"%c\"|ACEFILNOR,<value>" },
   {  25,  0,"Invalid sub-keyword found" },
   {  25,  1,"CALL ON must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
   {  25,  2,"CALL OFF must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
   {  25,  3,"SIGNAL ON must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
   {  25,  4,"SIGNAL OFF must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
   {  25,  5,"ADDRESS WITH must be followed by one of the keywords INPUT, OUTPUT or ERROR; found \"%s\"|<token>" },
   {  25,  6,"INPUT must be followed by one of the keywords STREAM, STEM, LIFO, FIFO or NORMAL; found \"%s\"|<token>" },
   {  25,  7,"OUTPUT must be followed by one of the keywords STREAM, STEM, LIFO, FIFO, APPEND, REPLACE or NORMAL; found \"%s\"|<token>" },
   {  25,  8,"APPEND must be followed by one of the keywords STREAM, STEM, LIFO or FIFO; found \"%s\"|<token>" },
   {  25,  9,"REPLACE must be followed by one of the keywords STREAM, STEM, LIFO or FIFO; found \"%s\"|<token>" },
   {  25, 11,"NUMERIC FORM must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
   {  25, 12,"PARSE must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
   {  25, 13,"UPPER must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
   {  25, 14,"ERROR must be followed by one of the keywords STREAM, STEM, LIFO, FIFO, APPEND, REPLACE or NORMAL; found \"%s\"|<token>" },
   {  25, 15,"NUMERIC must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
   {  25, 16,"FOREVER must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
   {  25, 17,"PROCEDURE must be followed by the keyword EXPOSE or nothing; found \"%s\"|<token>" },
   {  26,  0,"Invalid whole number" },
   {  26,  1,"Whole numbers must fit within current DIGITS setting(%d); found \"%s\"|<value>,<value>" },
   {  26,  2,"Value of repetition count expression in DO instruction must be zero or a positive whole number; found \"%s\"|<value>" },
   {  26,  3,"Value of FOR expression in DO instruction must be zero or a positive whole number; found \"%s\"|<value>" },
   {  26,  4,"Positional parameter of parsing template must be a whole number; found \"%s\"|<value>" },
   {  26,  5,"NUMERIC DIGITS value must be zero or a positive whole number; found \"%s\"|<value>" },
   {  26,  6,"NUMERIC FUZZ value must be zero or a positive whole number; found \"%s\"|<value>" },
   {  26,  7,"Number used in TRACE setting must be a whole number; found \"%s\"|<value>" },
   {  26,  8,"Operand to right of power operator (\"**\") must be a whole number; found \"%s\"|<value>" },
   {  26, 11,"Result of %s %% %s operation would need exponential notation at current NUMERIC DIGITS %d|<value>,<value>,<value>" },
   {  26, 12,"Result of %% operation used for %s // %s operation would need exponential notation at current NUMERIC DIGITS %d|<value>,<value>,<value>" },
   {  27,  0,"Invalid DO syntax" },
   {  27,  1,"Invalid use of keyword \"%s\" in DO clause|<token>" },
   {  28,  0,"Invalid LEAVE or ITERATE" },
   {  28,  1,"LEAVE is valid only within a repetitive DO loop" },
   {  28,  2,"ITERATE is valid only within a repetitive DO loop" },
   {  28,  3,"Symbol following LEAVE (\"%s\") must either match control variable of a current DO loop or be omitted|<token>" },
   {  28,  4,"Symbol following ITERATE (\"%s\") must either match control variable of a current DO loop or be omitted|<token>" },
   {  29,  0,"Environment name too long" },
   {  29,  1,"Environment name exceeds %d characters; found \"%s\"|#Limit_EnvironmentName,<name>" },
   {  30,  0,"Name or string too long" },
   {  30,  1,"Name exceeds %d characters|#Limit_Name" },
   {  30,  2,"Literal string exceeds %d characters|#Limit_Literal" },
   {  31,  0,"Name starts with number or \".\"" },
   {  31,  1,"A value cannot be assigned to a number; found \"%s\"|<token>" },
   {  31,  2,"Variable symbol must not start with a number; found \"%s\"|<token>" },
   {  31,  3,"Variable symbol must not start with a \".\"; found \"%s\"|<token>" },
   {  32,  0,"[Invalid use of stem]" },
   {  33,  0,"Invalid expression result" },
   {  33,  1,"Value of NUMERIC DIGITS \"%d\" must exceed value of NUMERIC FUZZ \"(%d)\"|<value>,<value>" },
   {  33,  2,"Value of NUMERIC DIGITS \"%d\" must not exceed %d|<value>,#Limit_Digits" },
   {  33,  3,"Result of expression following NUMERIC FORM must start with \"E\" or \"S\"; found \"%s\"|<value>" },
   {  34,  0,"Logical value not \"0\" or \"1\"" },
   {  34,  1,"Value of expression following IF keyword must be exactly \"0\" or \"1\"; found \"%s\"|<value>" },
   {  34,  2,"Value of expression following WHEN keyword must be exactly \"0\" or \"1\"; found \"%s\"|<value>" },
   {  34,  3,"Value of expression following WHILE keyword must be exactly \"0\" or \"1\"; found \"%s\"|<value>" },
   {  34,  4,"Value of expression following UNTIL keyword must be exactly \"0\" or \"1\"; found \"%s\"|<value>" },
   {  34,  5,"Value of expression to left of logical operator \"%s\" must be exactly \"0\" or \"1\"; found \"%s\"|<operator>,<value>" },
   {  34,  6,"Value of expression to right of logical operator \"%s\" must be exactly \"0\" or \"1\"; found \"%s\"|<operator>,<value>" },
   {  35,  0,"Invalid expression" },
   {  35,  1,"Invalid expression detected at \"%s\"|<token>" },
   {  36,  0,"Unmatched \"(\" in expression" },
   {  37,  0,"Unexpected \",\" or \")\"" },
   {  37,  1,"Unexpected \",\"" },
   {  37,  2,"Unmatched \")\" in expression" },
   {  38,  0,"Invalid template or pattern" },
   {  38,  1,"Invalid parsing template detected at \"%s\"|<token>" },
   {  38,  2,"Invalid parsing position detected at \"%s\"|<token>" },
   {  38,  3,"PARSE VALUE instruction requires WITH keyword" },
   {  39,  0,"[Evaluation stack overflow]" },
   {  40,  0,"Incorrect call to routine" },
   {  40,  1,"External routine \"%s\" failed|<name>" },
   {  40,  3,"Not enough arguments in invocation of \"%s\"; minimum expected is %d|<bif>,<argnumber>" },
   {  40,  4,"Too many arguments in invocation of \"%s\"; maximum expected is %d|<bif>,<argnumber>" },
   {  40,  5,"Missing argument in invocation of \"%s\"; argument %d is required|<bif>,<argnumber>" },
   {  40,  9,"%s argument %d exponent exceeds %d digits; found \"%s\"|<bif>,<argnumber>,#Limit_ExponentDigits,<value>" },
   {  40, 11,"%s argument %d must be a number; found \"%s\"|<bif>,<argnumber>,<value>" },
   {  40, 12,"%s argument %d must be a whole number; found \"%s\"|<bif>,<argnumber>,<value>" },
   {  40, 13,"%s argument %d must be zero or positive; found \"%s\"|<bif>,<argnumber>,<value>" },
   {  40, 14,"%s argument %d must be positive; found \"%s\"|<bif>,<argnumber>,<value>" },
   {  40, 17,"%s argument 1, must have an integer part in the range 0:90 and a decimal part no larger than .9; found \"%s\"|<bif>,<value>" },
   {  40, 18,"%s conversion must have a year in the range 0001 to 9999|<bif>" },
   {  40, 19,"%s argument 2, \"%s\", is not in the format described by argument 3, \"%s\"|<bif>,<value>,<value>" },
   {  40, 21,"%s argument %d must not be null|<bif>,<argnumber>" },
   {  40, 23,"%s argument %d must be a single character; found \"%s\"|<bif>,<argnumber>,<value>" },
   {  40, 24,"%s argument 1 must be a binary string; found \"%s\"|<bif>,<value>" },
   {  40, 25,"%s argument 1 must be a hexadecimal string; found \"%s\"|<bif>,<value>" },
   {  40, 26,"%s argument 1 must be a valid symbol; found \"%s\"|<bif>,<value>" },
   {  40, 27,"%s argument 1, must be a valid stream name; found \"%s\"|<bif>,<value>" },
   {  40, 28,"%s argument %d, option must start with one of \"%s\"; found \"%s\"|<bif>,<argnumber>,<optionslist>,<value>" },
   {  40, 29,"%s conversion to format \"%s\" is not allowed|<bif>,<value>" },
   {  40, 31,"%s argument 1 (\"%d\") must not exceed 100000|<bif>,<value>" },
   {  40, 32,"%s the difference between argument 1 (\"%d\") and argument 2 (\"%d\") must not exceed 100000|<bif>,<value>,<value>" },
   {  40, 33,"%s argument 1 (\"%d\") must be less than or equal to argument 2 (\"%d\")|<bif>,<value>,<value>" },
   {  40, 34,"%s argument 1 (\"%d\") must be less than or equal to the number of lines in the program (%d)|<bif>,<value>,<sourceline()>" },
   {  40, 35,"%s argument 1 cannot be expressed as a whole number; found \"%s\"|<bif>,<value>" },
   {  40, 36,"%s argument 1 must be a name of a variable in the pool; found \"%s\"|<bif>,<value>" },
   {  40, 37,"%s argument 3 must be the name of a pool; found \"%s\"|<bif>,<value>" },
   {  40, 38,"%s argument %d is not large enough to format \"%s\"|<bif>,<argnumber>,<value>" },
   {  40, 39,"%s argument 3 is not zero or one; found \"%s\"|<bif>,<value>" },
   {  40, 41,"%s argument %d must be within the bounds of the stream; found \"%s\"|<bif>,<argnumber>,<value>" },
   {  40, 42,"%s argument 1; cannot position on this stream; found \"%s\"|<bif>,<value>" },
   {  40,914,"[%s argument %d, must be one of \"%s\"; found \"%s\"]|<bif>,<argnumber>,<optionslist>,<value>" },
   {  40,920,"[%s: low-level stream I/O error; %s]|<bif>,<description>" },
   {  40,921,"[%s argument %d, stream positioning mode \"%s\"; incompatible with stream open mode]|<bif>,<argnumber>,<value>" },
   {  40,922,"[%s argument %d, too few sub-commands; minimum expected is %d; found %d]|<bif>,<argnumber>,<value>,<value>" },
   {  40,923,"[%s argument %d, too many sub-commands; maximum expected is %d; found %d]|<bif>,<argnumber>,<value>,<value>" },
   {  40,924,"[%s argument %d, invalid positional specification; expecting one of \"%s\"; found \"%s\"]|<bif>,<argnumber>,<value>,<value>" },
   {  40,930,"[RXQUEUE, function TIMEOUT, expecting a whole number between -1 and %d; found \"%s\"]|<value>,<value>" },
   {  41,  0,"Bad arithmetic conversion" },
   {  41,  1,"Non-numeric value (\"%s\") to left of arithmetic operation \"%s\"|<value>,<operator>" },
   {  41,  2,"Non-numeric value (\"%s\") to right of arithmetic operation \"%s\"|<value>,<operator>" },
   {  41,  3,"Non-numeric value (\"%s\") used with prefix operator \"%s\"|<value>,<operator>" },
   {  41,  4,"Value of TO expression in DO instruction must be numeric; found \"%s\"|<value>" },
   {  41,  5,"Value of BY expression in DO instruction must be numeric; found \"%s\"|<value>" },
   {  41,  6,"Value of control variable expression of DO instruction must be numeric; found \"%s\"|<value>" },
   {  41,  7,"Exponent exceeds %d digits; found \"%s\"|#Limit_ExponentDigits,<value>" },
   {  42,  0,"Arithmetic overflow/underflow" },
   {  42,  1,"Arithmetic overflow detected at \"%s %s %s\"; exponent of result requires more than %d digits|<value>,<operator>,<value>,#Limit_ExponentDigits" },
   {  42,  2,"Arithmetic underflow detected at \"%s %s %s\"; exponent of result requires more than %d digits|<value>,<operator>,<value>,#Limit_ExponentDigits" },
   {  42,  3,"Arithmetic overflow; divisor must not be zero" },
   {  43,  0,"Routine not found" },
   {  43,  1,"Could not find routine \"%s\"|<name>" },
   {  44,  0,"Function did not return data" },
   {  44,  1,"No data returned from function \"%s\"|<name>" },
   {  45,  0,"No data specified on function RETURN" },
   {  45,  1,"Data expected on RETURN instruction because routine \"%s\" was called as a function|<name>" },
   {  46,  0,"Invalid variable reference" },
   {  46,  1,"Extra token (\"%s\") found in variable reference; \")\" expected|<token>" },
   {  47,  0,"Unexpected label" },
   {  47,  1,"INTERPRET data must not contain labels; found \"%s\"|<name>" },
   {  48,  0,"Failure in system service" },
   {  48,  1,"Failure in system service: %s|<description>" },
   {  48,920,"Low-level stream I/O error: %s %s: %s|<description>,<stream>,<description>" },
   {  49,  0,"Interpretation Error" },
   {  49,  1,"Interpretation Error: Failed in %s, line %d: \"%s\". Please report error!|<module>,<linenumber>,<description>" },
   {  50,  0,"Unrecognized reserved symbol" },
   {  50,  1,"Unrecognized reserved symbol \"%s\"|<token>" },
   {  51,  0,"Invalid function name" },
   {  51,  1,"Unquoted function names must not end in a period; found \"%s\"|<token>" },
   {  52,  0,"Result returned by \"%s\" is longer than %d characters|<name>,#Limit_String" },
   {  53,  0,"Invalid option" },
   {  53,  1,"String or symbol expected after STREAM keyword; found \"%s\"|<token>" },
   {  53,  2,"Variable reference expected after STEM keyword; found \"%s\"|<token>" },
   {  53,  3,"Argument to STEM must have one period, as its last character; found \"%s\"|<name>" },
   {  53,100,"String or symbol expected after LIFO keyword; found \"%s\"|<token>" },
   {  53,101,"String or symbol expected after FIFO keyword; found \"%s\"|<token>" },
   {  54,  0,"Invalid STEM value" },
   {  54,  1,"For this STEM APPEND, the value of \"%s\" must be a count of lines; found \"%s\"|<name>,<value>" },
   {  60,  0,"[Can't rewind transient file]" },
   {  61,  0,"[Improper seek operation on file]" },
   {  64,  0,"[Syntax error while parsing]" },
   {  64,  1,"[Syntax error at line %d]" },
   {  64,  2,"[General syntax error at line %d, column %d]|<linenumber>,<columnnumber>" },
   {  90,  0,"[Non-ANSI feature used with \"OPTIONS STRICT_ANSI\"]" },
   {  90,  1,"[%s is a Regina extension BIF]|<bif>" },
   {  90,  2,"[%s is a Regina extension instruction]|<token>" },
   {  90,  3,"[%s argument %d, option must start with one of \"%s\" with \"OPTIONS STRICT_ANSI\"; found \"%s\"; a Regina extension]|<bif>,<argnumber>,<optionslist>,<value>" },
   {  93,  0,"[Incorrect call to routine]" },
   {  93,  1,"[STREAM command %s must be followed by one of \"%s\"; found \"%s\"]|<token>,<value>,<value>" },
   {  93,  3,"[STREAM command must be one of \"%s\"; found \"%s\"]|<value>,<value>" },
   {  94,  0,"[External queue interface error]" },
   {  94, 99,"[Internal error with external queue interface: %d \"%s\"]|<description>,<systemerror>" },
   {  94,100,"[General system error with external queue interface. %s. %s]|<description>,<systemerror>" },
   {  94,101,"[Error connecting to %s on port %d: \"%s\"]|<machine>,<portnumber>,<systemerror>" },
   {  94,102,"[Unable to obtain IP address for %s]|<machine>" },
   {  94,103,"[Invalid format for server in specified queue name: \"%s\"]|<queuename>" },
   {  94,104,"[Invalid format for queue name: \"%s\"]|<queuename>" },
   {  94,105,"[Unable to start Windows Socket interface: %s]|<systemerror>" },
   {  94,106,"[Maximum number of external queues exceeded: %d]|<maxqueues>" },
   {  94,107,"[Error occured reading socket: %s]|<systemerror>" },
   {  94,108,"[Invalid switch passed. Must be one of \"%s\"]|<switch>" },
   {  94,109,"[Queue \"%s\" not found]|<queuename>" },
   {  94,110,"[%s invalid for external queues]|<bif>" },
   {  94,111,"[RXQUEUE function %s invalid for internal queues]|<functionname>" },
   {  95,  0,"[Restricted feature used in \"safe\" mode]" },
   {  95,  1,"[%s invalid in \"safe\" mode]|<token>" },
   {  95,  2,"[%s argument %d invalid in \"safe\" mode]|<bif>,<argnumber>" },
   {  95,  3,"[%s argument %d: \"%s\", invalid in \"safe\" mode]|<bif>,<argnumber>,<token>" },
   {  95,  4,"[STREAM argument 3: Opening files for WRITE access invalid in \"safe\" mode]" },
   {  95,  5,"[Running external commands invalid in \"safe\" mode]" },
   { 100,  0,"[Unknown filesystem error]" },
} ;

/*
 * Static pointers to language-specific error messages
 * IF THIS EVER CHANGES, ALSO CHANGE THE SAME TABLE IN msgcmp.c
 */
static const char *errlang[] =
{
   "en", /* english */
   "de", /* german */
   "es", /* spanish */
   "no", /* norwegian */
   "pt", /* portuguese */
} ;

static const char *err1prefix[] =
{
/*en*/   "Error %d running \"%.*s\", line %d: %.*s",
/*de*/   "Fehler %d während des Ausführens von \"%.*s\", Zeile %d: %.*s",
/*es*/   "Error %d ejecutando \"%.*s\" línea %d: %.*s",
/*no*/   "Feil %d under kjøring av \"%.*s\" linje %d: %.*s",
/*pt*/   "Erro %d ao executar \"%.*s\", linha %d: %.*s",
} ;

static const char *suberrprefix[] =
{
/*en*/   "Error %d.%d: %.*s",
/*de*/   "Fehler %d.%d: %.*s",
/*es*/   "Error %d.%d: %.*s",
/*no*/   "Feil %d.%d: %.*s",
/*pt*/   "Erro %d.%d: %.*s",
} ;

static const char *err2prefix[] =
{
/*en*/   "Error %d running \"%.*s\": %.*s",
/*de*/   "Fehler %d während des Ausführens von \"%.*s\": %.*s",
/*es*/   "Error %d ejecutando \"%.*s\": %.*s",
/*no*/   "Feil %d under kjøring av \"%.*s\": %.*s",
/*pt*/   "Erro %d ao executar \"%.*s\": %.*s",
} ;

#if 0
static const char *errunavial[] =
{
   "language file: %s not available",
/*pt*/ "arquivo de linguagem: %s nao estah disponivel",
/*no*/ "språkfil: %s ikke tilgjengelig",
/*de*/ "Sprachspezifische Datei %s nicht verfügbar"
/*es*/   "Fichero de lenguaje: %s no disponible"
} ;
#endif

static const char *erropen[] =
{
/*en*/   "Unable to open language file: %s",
/*de*/   "Kann die sprachspezifische Datei nicht öffnen: %s",
/*es*/   "Incapaz de abrir el fichero de lenguaje: %s",
/*no*/   "Ikke i stand til å åpne språkfil: %s",
/*pt*/   "nao eh possivel abrir arquivo de linguagem: %s",
} ;

static const char *errcount[] =
{
/*en*/   "Incorrect number of messages in language file: %s",
/*de*/   "Ungültige Anzahl von Meldungen in der sprachspezifischen Datei: %s",
/*es*/   "Número incorrecto de mensajes en el fichero de lenguaje: %s",
/*no*/   "Uriktig antall meldinger i språkfil: %s",
/*pt*/   "numero incorreto de mensagens no arquivo de linguagem: %s",
} ;

static const char *errread[] =
{
/*en*/   "Unable to read from language file: %s",
/*de*/   "Kann von der sprachspezifischen Datei nicht lesen: %s",
/*es*/   "Incapaz de leer el fichero de lenguaje: %s",
/*no*/   "Ikke i stand til å lese fra språkfil: %s",
/*pt*/   "nao eh possivel ler o arquivo de linguagem: %s",
} ;

static const char *errmissing[] =
{
/*en*/   "Text missing from language file: %s.mtb",
/*de*/   "Text fehlt in der sprachspezifischen Datei: %s.mtb",
/*es*/   "Falta el texto del fichero de lenguaje: %s.mtb",
/*no*/   "tekst mangler i språkfil: %s.mtb",
/*pt*/   "falta texto no arquivo de linguagem: %s.mtb",
} ;

static const char *errcorrupt[] =
{
/*en*/   "Language file: %s.mtb is corrupt",
/*de*/   "Sprachspezifische Datei %s.mtb ist beschädigt",
/*es*/   "Fichero de lenguaje: %s.mtb está corrupto",
/*no*/   "språkfil: %s.mtb er ødelagt",
/*pt*/   "arquivo de linguagem: %s.mtb estah corrompido",
} ;

/* init_error initializes the module.
 * Currently, we set up the thread specific data.
 * The function returns 1 on success, 0 if memory is short.
 */
int init_error( tsd_t *TSD )
{
   err_tsd_t *et;

   if (TSD->err_tsd != NULL)
      return(1);

   if ((et = TSD->err_tsd = MallocTSD(sizeof(err_tsd_t))) == NULL)
      return(0);
   memset(et,0,sizeof(err_tsd_t));
   return(1);
}

/*
 * get_buffer returns one buffer of those from the err_tsd_t. If not_this
 * is set, that buffer will never be used. The buffer will have a size of
 * at least minsize byte.
 * A buffer is reallocated if there is no sufficient space in the best
 * fitting buffer.
 * A returned buffer will always have a size of 0.
 */
static streng *get_buffer( const tsd_t *TSD, const streng *not_this,
                           unsigned minsize )
{
   err_tsd_t *et = TSD->err_tsd;
   int l[2];
   streng *p;
   int idx=-1;

   minsize++;

   if ( et->buffer[0] != NULL )
      l[0] = Str_max( et->buffer[0] );
   else
      l[0] = 0;
   if ( et->buffer[1] != NULL )
      l[1] = Str_max( et->buffer[1] );
   else
      l[1] = 0;

   if ( ( unsigned ) l[1] >= minsize )
   {
      idx = 1;
      if ( ( ( unsigned ) l[0] >= minsize ) && ( l[0] < l[1] ) )
         idx = 0;
   }
   else
      idx = 0;

   if ( ( not_this != NULL ) && ( et->buffer[idx] == not_this ) )
      idx = ( idx == 0 ) ? 1 : 0;

   if ( ( unsigned ) l[idx] < minsize )
   {
      /*
       * assigning very late prevents race conditions in case of errors.
       */
      p = Str_makeTSD( minsize );
      if ( et->buffer[idx] != NULL )
         Free_stringTSD( et->buffer[idx] );
      et->buffer[idx] = p;
   }
   Str_len(et->buffer[idx]) = 0;

   return et->buffer[idx];
}

int lineno_of( cnodeptr node )
{
   if (node)
      return (node->lineno>=0) ? node->lineno : 0 ;
   else
      return 0 ;
}

static int charno_of( cnodeptr node )
{
   if (node)
      return (node->charnr>=0) ? node->charnr : 0 ;
   else
      return 0 ;
}


/* only to be used by syntax and runtime errors, and the halt condition
 * FIXME: FGC: This function is used while initializing the runtime system.
 *             Maybe, we don't have a functional tsd_t!
 *             What shall we do?
 */
void exiterror( int errorno, int suberrorno, ... )
{
   va_list argptr;
   int lineno=0, charno=0, signtype=0 ;
   streng *inputfile=NULL ;
   streng *suberror_streng=NULL;
   streng *errmsg=NULL ;
   int i,ok=0,len;
   const streng *fmt, *etext ;
   FILE *fp = stderr ;
   err_tsd_t *et;
   tsd_t *TSD = __regina_get_tsd(); /* The TSD should be fetched directly. This
                                     * will help if someone corrupted a TSD as
                                     * an argument.
                                     * A "fresh" value is always better for
                                     * tracking down ugly errors.
                                     * Speed advantage is no reason here! */
   et = TSD->err_tsd;

   if (TSD->currentnode)
   {
      lineno = lineno_of( TSD->currentnode ) ;
      charno = charno_of( TSD->currentnode ) ;
   }
   else
   {
      charno =  0 ;
      lineno = parser_data.tline ;
   }

   signtype = SIGNAL_SYNTAX ;
   if ( errorno==ERR_PROG_INTERRUPT )
      signtype = SIGNAL_HALT ;
#ifdef HAVE_VSPRINTF
   /*
    * Expanded the sub-error text and pass this to condition_hook for
    * condition('D') to return the expanded string.
    */
   if (errorno <= ERR_MAX_NUMBER
   &&  suberrorno != 0)
   {
      fmt = errortext( TSD, errorno, suberrorno, 0, 0 );
      len = Str_len( fmt );
      len += strlen( suberrprefix[et->native_language] );
      len += 2 * ( ( sizeof(unsigned) * 8 ) / 3 + 2 );
      errmsg = get_buffer( TSD, fmt, len + 3 );
      len = sprintf( errmsg->value, suberrprefix[et->native_language], errorno, suberrorno, Str_len( fmt ), fmt->value );

      va_start( argptr, suberrorno );
      for ( i = 0; i < Str_len( fmt ); i++ )
      {
         if ( fmt->value[i] == '%' )
         {
            switch ( fmt->value[i+1] )
            {
               case 's':
                  len += strlen( va_arg( argptr, char * ) );
                  break;

               case 'c':
                  va_arg( argptr, int );
                  break;

               default:
                  len += ( sizeof( unsigned ) * 8 ) / 3 + 2;
                  va_arg( argptr, unsigned );
                  break;
            }
         }
      }
      va_end( argptr );

      suberror_streng = Str_makeTSD( len + 1 );
      if (suberror_streng)
      {
         va_start( argptr, suberrorno );
         Str_len( suberror_streng ) = vsprintf( suberror_streng->value, errmsg->value, argptr );
         va_end( argptr );
      }
   }
#endif

   /* Here we should set sigtype to SIGNAL_FATAL for some 'errno's */

   /* Get the text for the base errorno */
   etext = errortext( TSD, errorno, 0, 0, 0 ) ;

   /*
    * Only in case of a SYNTAX error set .MN, ANSI 8.4.1
    * Keep care of 64 bit machines. A huge number may contain 80 characters
    * and we are not allowed to use ints, we have to use unsigneds.
    */
   if ( signtype == SIGNAL_SYNTAX )
   {
      char num[2 * ( ( sizeof(unsigned) * 8 ) / 3 + 2 )];

      if ( suberrorno )
         sprintf( num, "%u.%u", (unsigned) errorno, (unsigned) suberrorno );
      else
         sprintf( num, "%u", (unsigned) errorno );
      setvalue( TSD, dotMN_name, Str_creTSD( num ) ) ;
   }

   /* enable a hook into the condition system */
   if (condition_hook( TSD, signtype, errorno, suberrorno, lineno, Str_dupTSD(etext), suberror_streng))
   {
      if (suberror_streng)
         Free_stringTSD(suberror_streng);
      suberror_streng = NULL;
      return ; /* if CALL ON */
   }

   inputfile = TSD->systeminfo->input_file ;
   ok = HOOK_GO_ON ;
   if (lineno>0)
   {
      traceback(TSD);
      errmsg = Str_makeTSD( 80 + Str_len( etext ) + Str_len( inputfile ) + strlen( err1prefix[et->native_language] ) );
      sprintf( errmsg->value, err1prefix[et->native_language], errorno, Str_len( inputfile ), inputfile->value, lineno, Str_len( etext ), etext->value ) ;
   }
   else
   {
      errmsg = Str_makeTSD( 80 + Str_len( etext ) + Str_len( inputfile ) + strlen( err2prefix[et->native_language] ) );
      sprintf(errmsg->value, err2prefix[et->native_language], errorno, Str_len( inputfile ), inputfile->value, Str_len( etext ), etext->value );
   }

   errmsg->len = strlen( errmsg->value ) ;
   assert( errmsg->len < errmsg->max ) ;
   /*
    * If we have a system exit installed to handle errors, call it here...
    */
   if (TSD->systeminfo->hooks & HOOK_MASK(HOOK_STDERR))
      ok = (hookup_output(TSD, HOOK_STDERR, errmsg) == HOOK_GO_ON) ;

   if (ok==HOOK_GO_ON)
   {
      /*
       * To get here we either don't have an exit handler or the exit
       * handler refused to handle the message, so write it to the
       * error (or output) stream.
       */
      if ( get_options_flag( TSD->currlevel, EXT_STDOUT_FOR_STDERR ) )
         fp = stdout ;
      fwrite( errmsg->value, Str_len(errmsg), 1, fp ) ;
#if defined(DOS) || defined(OS2) || defined(WIN32)
      /*
       * stdout is open in binary mode, so we need to add the
       * extra CR to the end of the line.
       */
      fputc( REGINA_CR, fp ) ;
#endif
      fputc( REGINA_EOL, fp ) ;
   }
   /*
    * Display the sub-error text if there is one.
    */
   if (errorno <= ERR_MAX_NUMBER
   &&  suberrorno != 0
   &&  suberror_streng)
   {
      if (TSD->systeminfo->hooks & HOOK_MASK(HOOK_STDERR))
         ok = (hookup_output(TSD, HOOK_STDERR, suberror_streng) == HOOK_GO_ON) ;
      if (ok==HOOK_GO_ON)
      {
         fwrite( suberror_streng->value, Str_len(suberror_streng), 1, fp ) ;
#if defined(DOS) || defined(OS2) || defined(WIN32)
         /*
          * stdout is open in binary mode, so we need to add the
          * extra CR to the end of the line.
          */
         fputc( REGINA_CR, fp ) ;
#endif
         fputc( REGINA_EOL, fp ) ;
      }
   }
   if (ok==HOOK_GO_ON)
      fflush( fp );
   if (suberror_streng)
      Free_stringTSD(suberror_streng);

#ifndef NDEBUG
   if (errorno == ERR_INTERPRETER_FAILURE)
      abort() ;
#endif

   Free_stringTSD( errmsg ) ;
   if (TSD->systeminfo->panic)
   {
      TSD->systeminfo->result = NULL ;
      if (TSD->in_protected)
      {
         TSD->delayed_error_type = PROTECTED_DelayedSetjmpPanic;
         longjmp( TSD->protect_return, 1 ) ;
      }
      longjmp( *(TSD->systeminfo->panic), 1 ) ;
   }
   CloseOpenFiles( TSD );

   if (TSD->in_protected)
   {
#ifdef VMS
      TSD->expected_exit_error = EXIT_SUCCESS;
#else
      TSD->expected_exit_error = errorno;
#endif
      TSD->delayed_error_type = PROTECTED_DelayedExit;
      longjmp( TSD->protect_return, 1 ) ;
   }
#ifdef VMS
   TSD->MTExit( EXIT_SUCCESS ) ;
#else
   TSD->MTExit( errorno ) ;
#endif
}

/* This function is called by the parser (syntactical interpreter) if an error
 * occurs.
 */
void __reginaerror(char *dummy)
{
   /* We ignore the message although it may contain useful informations. */
   dummy = dummy; /* keep compiler happy */
   return ;
}

/*
 * Returns a 0-terminated string in a streng which will be formatted by a
 * "string formatter" which argument is arg. not_this will not be returned.
 */
static streng *simple_msg( const tsd_t *TSD, const char *fmt,
                           const char *arg, const streng *not_this )
{
   int lf = strlen( fmt );
   int la = strlen( arg );
   streng *retval;

   retval = get_buffer( TSD, not_this, lf + la );
   Str_len( retval ) = sprintf( retval->value, fmt, arg );

   return retval;
}

static streng *read_index_header( const tsd_t *TSD, char *errfn,
                                  int native_language, FILE **fp,
                                  int *number_messages, int *file_lang,
                                  const streng *not_this )
{
   err_tsd_t *et;

   et = TSD->err_tsd;
   /*
    * Read the language file header...
    */
   *fp = fopen( errfn, "rb" );
   if ( *fp == NULL )
   {
      return simple_msg( TSD, erropen[native_language], errfn, not_this );
   }
   if ( fread( &et->number_messages, sizeof(unsigned int), 1, *fp ) != 1 )
   {
      fclose( *fp );
      return simple_msg( TSD, errread[native_language], errfn, not_this );
   }
   if ( fread( file_lang, sizeof(unsigned int), 1, *fp ) != 1 )
   {
      fclose( *fp );
      return simple_msg( TSD, errread[native_language], errfn, not_this );
   }
   return NULL;
}

static streng *read_index_file( const tsd_t *TSD, char *errfn,
                                int native_language, int language_file,
                                FILE **fp, struct textindex *tmi,
                                const streng *not_this )
{
   err_tsd_t *et;
   streng *ptr;
   int file_lang;

   et = TSD->err_tsd;
   /*
    * Read the language file header...
    */
   if ( ( ptr = read_index_header( TSD, errfn, native_language, fp, &et->number_messages, &file_lang, not_this ) ) != NULL )
   {
      et->number_messages = 0;
      return ptr;
   }
   /*
    * Eunsure that the number of messages in the file matches the number defined as
    * NUMBER_ERROR_MESSAGE in rexxmsg.h
    */
   if ( et->number_messages != NUMBER_ERROR_MESSAGES )
   {
      fclose( *fp );
      et->number_messages = 0;
      return simple_msg( TSD, errcount[native_language], errfn, not_this );
   }
   if ( fread( tmi, sizeof(struct textindex), NUMBER_ERROR_MESSAGES, *fp ) != NUMBER_ERROR_MESSAGES )
   {
      fclose( *fp );
      et->number_messages = 0;
      return simple_msg( TSD, errread[native_language], errfn, not_this );
   }
   return NULL;
}

/*
 * Called the first time we need to access an error message
 * Determines which language file to open and read (always does English as well)
 * Returns NULL on success, otherwise a pointer to an error message
 */
static streng *get_message_indexes( const tsd_t *TSD, const streng *not_this )
{
   streng *err;
   char *ptr;
   char fn[REXX_PATH_MAX+20];
   err_tsd_t *et;
   char *errpath=NULL;
#if defined(__EPOC32__) || defined(__WINS__)
   FILE *fp;
   int number_messages, file_lang;
   struct stat buffer ;
#else
   int i, found=0;
#endif

   et = TSD->err_tsd;

#if defined(__EPOC32__) || defined(__WINS__)
   /*
    * Open the default.mtb and read the language type from it.
    */
   errpath = "c:\\system\\apps\\reginarexx";
   sprintf( fn, "%s\\default.mtb", errpath );
   /*
    * If there is no default.mtb file, then default to English
    */
   if ( stat( fn, &buffer ) != 0 )
   {
      et->native_language = LANGUAGE_ENGLISH;
      return NULL;
   }
   /*
    * We do have a default.mtb file, so read it to determine the language
    */
   if ( ( err = read_index_header( TSD, fn, LANGUAGE_ENGLISH, &fp, &number_messages, &file_lang, not_this ) ) != NULL )
   {
      et->number_messages = 0;
      return err;
   }
   if ( fp )
      fclose( fp );
   et->native_language = file_lang;
#else
   ptr = getenv( "REGINA_LANG" );
   if ( ptr == NULL )
   {
      et->native_language = LANGUAGE_ENGLISH;
   }
   else
   {
      for ( i = 0; i < LANGUAGE_MAXIMUM; i++ )
      {
         if ( strcmp( ptr, errlang[i] ) == 0 )
         {
            et->native_language = i;
            found = 1;
            break;
         }
      }
      if ( found == 0 )
      {
         return simple_msg( TSD, "Unsupported native language \"%s\"", ptr, not_this );
      }
   }
   if ( et->native_language != LANGUAGE_ENGLISH )
   {
      errpath = getenv( "REGINA_LANG_DIR" );
      if ( errpath == NULL )
      {
# if defined(REGINA_SHARE_DIRECTORY)
         errpath = REGINA_SHARE_DIRECTORY;
# else
         return simple_msg( TSD, "%s", "Unable to determine where Regina language files (*.mtb) are located. Set REGINA_LANG_DIR.", not_this );
# endif
      }
      else if ( strlen( errpath ) > REXX_PATH_MAX )
         return simple_msg( TSD, "Length of \"%s\" exceeds the path's maximum", errpath, not_this );
   }
#endif
   /*
    * Now read the native language file. If the native language is
    * English, don't do anything.
    */
   if ( et->native_language != LANGUAGE_ENGLISH)
   {
#if defined(__EPOC32__) || defined(__WINS__)
      sprintf( fn, "%s\\default.mtb", errpath );
#else
      sprintf( fn, "%s%c%s.mtb", errpath, FILE_SEPARATOR, errlang[et->native_language] );
#endif
      if ( ( err = read_index_file( TSD, fn, et->native_language, et->native_language, &et->nls_fp, (struct textindex *)&et->nls_tmi, not_this ) ) != NULL )
         return err;
   }
   return NULL;
}

static streng *get_text_message( const tsd_t *TSD, FILE *fp,
                                 unsigned fileoffset, unsigned textlength,
                                 int errorno, int suberrorno, int *is_fmt,
                                 const streng *not_this )
{
   err_tsd_t *et;
   streng *retval;
   const char *errfn;

   et = TSD->err_tsd;

#if defined(__EPOC32__) || defined(__WINS__)
   errfn="default";
#else
   errfn=errlang[et->native_language];
#endif
   if ( fseek( fp, fileoffset, SEEK_SET ) == -1 )
   {
      *is_fmt = 0;
      return simple_msg( TSD, errcorrupt[et->native_language], errfn, not_this );
   }

   retval = get_buffer( TSD, not_this, textlength + 1 );
   if ( fread( retval->value, 1, textlength, fp ) != textlength )
   {
      *is_fmt = 0;
      return simple_msg( TSD, errcorrupt[et->native_language], errfn, not_this );
   }
   retval->value[textlength] = '\0';
   Str_len(retval) = textlength;

   return retval;
}

static const char *get_embedded_text_message( const tsd_t *TSD, int errorno, int suberrorno )
{
   int i;

   for ( i = 0; i < NUMBER_ERROR_MESSAGES; i++ )
   {
      if ( errtext[i].errnum == errorno
      &&   errtext[i].suberrnum == suberrorno )
      {
         return errtext[i].text;
      }
   }
   return "";
}


const streng *errortext( const tsd_t *TSD, int errorno, int suberrorno, int request_english, int apply_inserts )
{
   int low=0, mid=0, end=1, up, have_inserts=0, num_inserts=0;
   int this_errorno, this_suberrorno;
   err_tsd_t *et;
   streng *ptr=NULL,*h;
   const char *errfn,*embedded;
   char *ins;
   char *insert[5]; /* maximum of 5 inserts allowed for any one message */
   int is_fmt=1;

   /*
    * If the supplied errorno is > 100 (the internal limit for interpreter
    * errors), assume that a system error message is required.
    */
   if (errorno>100)
   {
      return simple_msg( TSD, "%s", strerror(errorno-100), NULL );
   }

   et = TSD->err_tsd;

#if defined(__EPOC32__) || defined(__WINS__)
   errfn="default";
#else
   errfn=errlang[et->native_language];
#endif
   /*
    * The first time this is called, determine the language and read the message file
    * indexes into memory from the message file.
    */
   if ( et->number_messages == 0 )
   {
      if ( ( ptr = get_message_indexes( TSD, NULL ) ) != NULL )
      {
         /*
          * Corrupt or missing language file. Prepend the returned message to the
          * embedded error message format.
          */

         embedded = get_embedded_text_message( TSD, errorno, suberrorno );
         h = get_buffer( TSD, ptr, Str_len( ptr ) + strlen( embedded ) + 6 );
         Str_catstrTSD( h, "(" );
         Str_catTSD( h, ptr );
         Str_catstrTSD( h, ") " );
         Str_catstrTSD( h, embedded );
         h->value[Str_len( h )] = '\0';
         ptr = h;
      }
   }
   /*
    * If we don't already have an error message,
    * and we are explicitly requesting an english message, or the native
    * language is English, then simply get the message from the internal
    * array.
    */
   if ( !ptr )
   {
      if ( request_english
      ||   et->native_language == LANGUAGE_ENGLISH )
      {
         ptr = simple_msg( TSD, "%s", get_embedded_text_message( TSD, errorno, suberrorno ), NULL );
      }
      else
      {
         up = et->number_messages-1;

         while ((end)&&(up>=low))
         {
            mid = (up+low)/2 ;
            this_errorno = et->nls_tmi[mid].errorno;
            this_suberrorno = et->nls_tmi[mid].suberrorno;
            if ( errorno == this_errorno
            &&   suberrorno == this_suberrorno )
            {
               end = 0;
               break;
            }
            if ( ( errorno > this_errorno )
            ||   ( errorno == this_errorno
            &&   suberrorno > this_suberrorno ) )
               low = mid + 1;
            else
               up = mid - 1;
         }
         if (end)
         {
            /*
             * We couldn't find our message...
             */
            embedded = get_embedded_text_message( TSD, errorno, suberrorno );
            ptr = simple_msg( TSD, errmissing[et->native_language], errfn, NULL );
            h = get_buffer( TSD, ptr, Str_len( ptr ) + strlen( embedded ) + 6 );
            Str_catstrTSD( h, "(" );
            Str_catTSD( h, ptr );
            Str_catstrTSD( h, ") " );
            Str_catstrTSD( h, embedded );
            h->value[Str_len( h )] = '\0';
            ptr = h;
         }
         else
         {
            ptr = get_text_message( TSD, et->nls_fp, et->nls_tmi[mid].fileoffset, et->nls_tmi[mid].textlength, errorno, suberrorno, &is_fmt, NULL );
            if ( !is_fmt )
            {
               embedded = get_embedded_text_message( TSD, errorno, suberrorno );
               h = get_buffer( TSD, ptr, Str_len( ptr ) + strlen( embedded ) + 6 );
               Str_catstrTSD( h, "(" );
               Str_catTSD( h, ptr );
               Str_catstrTSD( h, ") " );
               Str_catstrTSD( h, embedded );
               h->value[Str_len( h )] = '\0';
               ptr = h;
            }
         }
      }
   }
   for ( low = 0; low < (int) Str_len( ptr ); low++ )
   {
      if ( ptr->value[low] == '|' )
      {
         ptr->value[low] = '\0';
         Str_len( ptr ) = low;
         have_inserts = 1;
         break;
      }
   }
   /*
    * If we need to apply insert, then:
    * - adjust the returned fmt string replacing %c, %d, %x with %s
    * - iterate through the inserts ( ptr+low+1 )
    */
   if ( apply_inserts
   &&   have_inserts )
   {
      /*
       * The code below makes several assumptions about the format
       * of each message. All assumptions are based on having checked
       * the format of the messages using the checkmts.rexx script.
       */
      for ( end = 0; end < Str_len( ptr ); end++ )
      {
         if ( ptr->value[end] == '%' )
         {
            switch( ptr->value[end+1] ) /* assumes no message ends in % */
            {
               case 's':
                  num_inserts++;
                  break;
               case 'c':
               case 'x':
               case 'd':
                  ptr->value[end+1] = 's';
                  num_inserts++;
                  break;
               default:
                  break;
            }
         }
      }
      ins = ptr->value+low+1;
      insert[0] = ins;
      low = strlen( ins );
      for ( mid = 0,end = 0; end < low; end++ )
      {
         if ( ins[end] == ',' )
         {
            ins[end] = '\0';
            insert[++mid] = ins+end+1;
         }
      }
      h = get_buffer( TSD, ptr, Str_len( ptr ) + low + 1 );
      switch( num_inserts )
      {
         case 1:
            Str_len( h ) = sprintf( h->value, ptr->value, insert[0] );
            break;
         case 2:
            Str_len( h ) = sprintf( h->value, ptr->value, insert[0], insert[1] );
            break;
         case 3:
            Str_len( h ) = sprintf( h->value, ptr->value, insert[0], insert[1], insert[2] );
            break;
         case 4:
            Str_len( h ) = sprintf( h->value, ptr->value, insert[0], insert[1], insert[2], insert[3] );
            break;
         case 5:
            Str_len( h ) = sprintf( h->value, ptr->value, insert[0], insert[1], insert[2], insert[3], insert[4] );
            break;
      }
      ptr = h;
   }

   return ptr;
}

#ifndef NDEBUG

const char *getsym( int numb )
{
   char *symb=NULL ;

   switch (numb)
   {

      case X_NULL: symb="Null statement" ; break ;
      case X_PROGRAM: symb="Program" ; break ;
      case X_STATS: symb="Statements" ; break ;
      case X_COMMAND: symb="External command" ; break ;
      case X_ADDR_V: symb="ADDRESS (value) statement" ; break ;
      case X_ADDR_S: symb="ADDRESS" ; break ;
      case X_ADDR_N: symb="ADDRESS (normal) statement" ; break ;
      case X_ARG: symb="ARG statement" ; break ;
      case X_CALL: symb="CALL statement" ; break ;
      case X_DO: symb="DO statement" ; break ;
      case X_REP: symb="Repetitor in DO" ; break ;
      case X_REP_FOREVER: symb="Forever in DO" ; break ;
      case X_REP_COUNT: symb="Counter in DO" ; break ;
      case X_DO_TO: symb="Upper limit in DO" ; break ;
      case X_DO_BY: symb="Step-size in DO" ; break ;
      case X_DO_FOR: symb="Max number in DO" ; break ;
      case X_DO_EXPR: symb="Upper limit in DO" ; break ;
      case X_WHILE: symb="WHILE expr in DO" ; break ;
      case X_UNTIL: symb="UNTIL expr in DO" ; break ;
      case X_DROP: symb="DROP statement" ; break ;
      case X_EXIT: symb="EXIT statement" ; break ;
      case X_IF: symb="IF statement" ; break ;
      case X_IPRET: symb="INTERPRET statement" ; break ;
      case X_ITERATE: symb="ITERATE statement" ; break ;
      case X_LABEL: symb="Label specification" ; break ;
      case X_LEAVE: symb="LEAVE statement" ; break ;
      case X_NUM_D: symb="NUMERIC DIGIT statement" ; break ;
      case X_NUM_F: symb="NUMERIC FORM statement" ; break ;
      case X_NUM_FUZZ: symb="NUMERIC FUZZ statement" ; break ;
      case X_NUM_SCI: symb="Scientific numeric form" ; break ;
      case X_NUM_ENG: symb="Engeenering scientific form" ; break ;
      case X_PARSE: symb="PARSE statement" ; break ;
      case X_PARSE_U: symb="UPPER PARSE statement" ; break ;
      case X_PARSE_ARG: symb="PARSE ARG atatement" ; break ;
      case X_PARSE_EXT: symb="External parsing" ; break ;
      case X_PARSE_NUM: symb="Numeric parsing" ; break ;
      case X_PARSE_PULL: symb="Parse pull" ; break ;
      case X_PARSE_SRC: symb="Parse source" ; break ;
      case X_PARSE_VAR: symb="Parse variable" ; break ;
      case X_PARSE_VAL: symb="Parse value" ; break ;
      case X_PARSE_VER: symb="Parse version" ; break ;
      case X_PARSE_ARG_U: symb="PARSE UPPER ARG statement" ; break ;
      case X_PROC: symb="PROCEDURE statement" ; break ;
      case X_PULL: symb="PULL statement" ; break ;
      case X_PUSH: symb="PUSH statement" ; break ;
      case X_QUEUE: symb="QUEUE statement" ; break ;
      case X_RETURN: symb="RETURN statement" ; break ;
      case X_SAY: symb="SAY statement" ; break ;
      case X_SELECT: symb="SELECT statement" ; break ;
      case X_WHENS: symb="WHEN connector" ; break ;
      case X_WHEN: symb="WHEN clause" ; break ;
      case X_OTHERWISE: symb="OTHERWISE clause" ; break ;
      case X_SIG_VAL: symb="SIGNAL VALUE statement" ; break ;
      case X_SIG_LAB: symb="SIGNAL (label) statement" ; break ;
      case X_SIG_SET: symb="SIGNAL (setting) statement" ; break ;
      case X_ON: symb="Setting is ON" ; break ;
      case X_OFF: symb="Setting is OFF" ; break ;
      case X_S_ERROR: symb="ERROR option" ; break ;
      case X_S_HALT: symb="HALT option" ; break ;
      case X_S_LOSTDIGITS: symb="LOSTDIGITS option" ; break ;
      case X_S_NOVALUE: symb="NOVALUE option" ; break ;
      case X_S_SYNTAX: symb="SYNTAX option" ; break ;
      case X_TRACE: symb="TRACE statement" ; break ;
      case X_T_ALL: symb="ALL option" ; break ;
      case X_T_COMM: symb="COMMAND option" ; break ;
      case X_T_ERR: symb="ERROR option" ; break ;
      case X_T_INTER: symb="INTERMEDIATE option" ; break ;
      case X_T_LABEL: symb="LABEL option" ; break ;
      case X_T_NORMAL: symb="NORMAL option" ; break ;
      case X_T_OFF: symb="OFF option" ; break ;
      case X_T_SCAN: symb="SCAN option" ; break ;
      case X_UPPER_VAR: symb="UPPER statement" ; break ;
      case X_ASSIGN: symb="Assignment" ; break ;
      case X_LOG_NOT: symb="Logical NOT" ; break ;
      case X_PLUSS: symb="Plus operator" ; break ;
      case X_EQUAL: symb="Equal operator" ; break ;
      case X_MINUS: symb="Minus operator" ; break ;
      case X_MULT: symb="Multiplication operator" ; break ;
      case X_DEVIDE: symb="Division operator" ; break ;
      case X_MODULUS: symb="Modulus operator" ; break ;
      case X_LOG_OR: symb="Logical or" ; break ;
      case X_LOG_AND: symb="Logical and" ; break ;
      case X_LOG_XOR: symb="Logical xor" ; break ;
      case X_EXP: symb="Exponent operator" ; break ;
      case X_CONCAT: symb="String concatenation" ; break ;
      case X_SPACE: symb="Space separator" ; break ;
      case X_GTE: symb="Greater than or equal operator" ; break ;
      case X_LTE: symb="Less than or equal operator" ; break ;
      case X_GT: symb="Greater than operator" ; break ;
      case X_LT: symb="Less than operator" ; break ;
      case X_NEQUAL: symb="Not equal operator" ; break ;
      case X_NDIFF: symb="Not different operator" ; break ;
      case X_NGT: symb="Not greater than operator" ; break ;
      case X_NGTE: symb="Not greater than or equal operator" ; break ;
      case X_NLT: symb="Not less than operator" ; break ;
      case X_NLTE: symb="Not less than or equal operator" ; break ;
      case X_DIFF: symb="Different operator" ; break ;
      case X_SEQUAL: symb="Strictly equal operator" ; break ;
      case X_SDIFF: symb="Strictly different operator" ; break ;
      case X_SGT: symb="Strictly greater than operator" ; break ;
      case X_SGTE: symb="Strictly greater than or equal operator" ; break ;
      case X_SLT: symb="Strictly less than operator" ; break ;
      case X_SLTE: symb="Strictly less than or equal operator" ; break ;
      case X_SIM_SYMBOL: symb="Simple symbol" ; break ;
      case X_CON_SYMBOL: symb="Constant symbol" ; break ;
      case X_HEX_STR: symb="Hexadecimal string" ; break ;
      case X_STRING: symb="Constant string" ; break ;
      case X_FUNC: symb="Function call" ; break ;
      case X_U_MINUS: symb="Unary minus" ; break ;
      case X_S_EQUAL: symb="String equal operator" ; break ;
      case X_S_DIFF: symb="String different operator" ; break ;
      case X_SIMSYMB: symb="Simple symbol (2)" ; break ;
      case X_INTDIV: symb="Integer division" ; break ;
      case X_EX_FUNC: symb="External function call" ; break ;
      case X_IN_FUNC: symb="Internal function call" ; break ;
      case X_TPL_SOLID: symb="Solid point in template" ; break ;
      case X_TPL_MVE: symb="Constant pattern" ; break ;
      case X_TPL_VAR: symb="Variable pattern" ; break ;
      case X_TPL_TO: symb="Ehh, what does \"TO\" mean???" ; break ;
      case X_TPL_SYMBOL: symb="Variable in template" ; break ;
      case X_TPL_SPACE: symb="Space in template" ; break ;
      case X_TPL_POINT: symb="Placeholder in template" ; break ;
      case X_TMPLS: symb="Template connector" ; break ;
      case X_TPL_OFF: symb="Offset in template" ; break ;
      case X_TPL_PATT: symb="Pattern in template" ; break ;
      case X_NEG_OFFS: symb="Negative offset" ; break ;
      case X_POS_OFFS: symb="Positive offset" ; break ;
      case X_ABS_OFFS: symb="Absolute offset" ; break ;
      case X_EXPRLIST: symb="Expression connector" ; break ;
      case X_SYMBOLS: symb="Symbol connector" ; break ;
      case X_SYMBOL: symb="Symbol?" ; break ;
      case X_END: symb="End statement" ; break ;
      case X_IS_INTERNAL: symb="Internal function" ; break ;
      case X_IS_BUILTIN: symb="Builtin function" ; break ;
      case X_IS_EXTERNAL: symb="External function" ; break ;
      case X_CEXPRLIST: symb="Expression list" ; break ;
      case X_NASSIGN: symb="Numeric Assignment" ; break ;
      case X_VTAIL_SYMBOL: symb="Variable tail symbol" ; break ;
      case X_CTAIL_SYMBOL: symb="Constant tail symbol" ; break ;
      case X_HEAD_SYMBOL: symb="Compound variable symbol" ; break ;
      case X_STEM_SYMBOL: symb="Stem variable symbol" ; break ;
      case X_NO_OTHERWISE: symb="No otherwise statement" ; break ;
      default: symb="Unrecognized value" ;
   }

   return symb ;
}

#endif /* !NDEBUG */
