1 /*
2  *  The Regina Rexx Interpreter
3  *  Copyright (C) 1992-1994  Anders Christensen <anders@pvv.unit.no>
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Library General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Library General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Library General Public
16  *  License along with this library; if not, write to the Free
17  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #include "rexx.h"
21 #include <errno.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <assert.h>
25 #include <stdarg.h>
26 #ifdef HAVE_UNISTD_H
27 # include <unistd.h>
28 #endif
29 #include <sys/stat.h>
30 #include "rexxmsg.h"
31 
32 /* MAX_ET_BUFFERS is the maximum number of strings required to be allocated when an error occurs */
33 #define MAX_ET_BUFFERS 10
34 typedef struct /* err_tsd: static variables of this module (thread-safe) */
35 {
36    int number_messages;
37    int native_language;
38    FILE *nls_fp;
39    streng *buffer[MAX_ET_BUFFERS];
40    struct textindex nls_tmi[NUMBER_ERROR_MESSAGES]; /* indexes for native language messages */
41    int number_prefix_messages; /* new */
42    struct textindex nls_tpi[NUMBER_PREFIX_MESSAGES]; /* indexes for prefix messages */
43    int conditions;
44    streng *errornum;
45 } err_tsd_t;   /* thread-specific but only needed by this module. see init_error */
46 
47 typedef struct
48 {
49    int errnum;
50    int suberrnum;
51    char *text;
52 } errtext_t;
53 
54 /*
55  * English message text - generated by makeerror.rexx
56  */
57 static const errtext_t errtext[NUMBER_ERROR_MESSAGES] =
58 {
59    {   0,  1,"Error %s running %s, line %d:|<value>,<source>,<linenumber>" },
60    {   0,  2,"Error %s in interactive trace:|<value>" },
61    {   0,  3,"Interactive trace. \"Trace Off\" to end debug. ENTER to continue." },
62    {   2,  0,"Failure during finalization" },
63    {   2,  1,"Failure during finalization: %s|<description>" },
64    {   3,  0,"Failure during initialization" },
65    {   3,  1,"Failure during initialization: %s|<description>" },
66    {   4,  0,"Program interrupted" },
67    {   4,  1,"Program interrupted with HALT condition: %s|<description>" },
68    {   5,  0,"System resources exhausted" },
69    {   5,  1,"System resources exhausted: %s|<description>" },
70    {   6,  0,"Unmatched \"/*\" or quote" },
71    {   6,  1,"Unmatched comment delimiter (\"/*\")" },
72    {   6,  2,"Unmatched single quote (')" },
73    {   6,  3,"Unmatched double quote (\")" },
74    {   7,  0,"WHEN or OTHERWISE expected" },
75    {   7,  1,"SELECT on line %d requires WHEN; found \"%s\"|<linenumber>,<token>" },
76    {   7,  2,"SELECT on line %d requires WHEN, OTHERWISE, or END; found \"%s\"|<linenumber>,<token>" },
77    {   7,  3,"All WHEN expressions of SELECT on line %d are false; OTHERWISE expected|<linenumber>" },
78    {   8,  0,"Unexpected THEN or ELSE" },
79    {   8,  1,"THEN has no corresponding IF or WHEN clause" },
80    {   8,  2,"ELSE has no corresponding THEN clause" },
81    {   9,  0,"Unexpected WHEN or OTHERWISE" },
82    {   9,  1,"WHEN has no corresponding SELECT" },
83    {   9,  2,"OTHERWISE has no corresponding SELECT" },
84    {  10,  0,"Unexpected or unmatched END" },
85    {  10,  1,"END has no corresponding DO or SELECT" },
86    {  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>" },
87    {  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>" },
88    {  10,  4,"END corresponding to SELECT on line %d must not have a symbol following; found \"%s\"|<linenumber>,<token>" },
89    {  10,  5,"END must not immediately follow THEN" },
90    {  10,  6,"END must not immediately follow ELSE" },
91    {  11,  0,"[Control stack full]" },
92    {  12,  0,"[Clause > 1024 characters]" },
93    {  13,  0,"Invalid character in program" },
94    {  13,  1,"Invalid character in program \"('%x'X)\"|<hex-encoding>" },
95    {  14,  0,"Incomplete DO/SELECT/IF" },
96    {  14,  1,"DO instruction requires a matching END" },
97    {  14,  2,"SELECT instruction requires a matching END" },
98    {  14,  3,"THEN requires a following instruction" },
99    {  14,  4,"ELSE requires a following instruction" },
100    {  15,  0,"Invalid hexadecimal or binary string" },
101    {  15,  1,"Invalid location of blank in position %d in hexadecimal string|<position>" },
102    {  15,  2,"Invalid location of blank in position %d in binary string|<position>" },
103    {  15,  3,"Only 0-9, a-f, A-F, and blank are valid in a hexadecimal string; found \"%c\"|<char>" },
104    {  15,  4,"Only 0, 1, and blank are valid in a binary string; found \"%c\"|<char>" },
105    {  16,  0,"Label not found" },
106    {  16,  1,"Label \"%s\" not found|<name>" },
107    {  16,  2,"Cannot SIGNAL to label \"%s\" because it is inside an IF, SELECT or DO group|<name>" },
108    {  16,  3,"Cannot invoke label \"%s\" because it is inside an IF, SELECT or DO group|<name>" },
109    {  17,  0,"Unexpected PROCEDURE" },
110    {  17,  1,"PROCEDURE is valid only when it is the first instruction executed after an internal CALL or function invocation" },
111    {  18,  0,"THEN expected" },
112    {  18,  1,"IF keyword on line %d requires matching THEN clause; found \"%s\"|<linenumber>,<token>" },
113    {  18,  2,"WHEN keyword on line %d requires matching THEN clause; found \"%s\"|<linenumber>,<token>" },
114    {  19,  0,"String or symbol expected" },
115    {  19,  1,"String or symbol expected after ADDRESS keyword; found \"%s\"|<token>" },
116    {  19,  2,"String or symbol expected after CALL keyword; found \"%s\"|<token>" },
117    {  19,  3,"String or symbol expected after NAME keyword; found \"%s\"|<token>" },
118    {  19,  4,"String or symbol expected after SIGNAL keyword; found \"%s\"|<token>" },
119    {  19,  6,"String or symbol expected after TRACE keyword; found \"%s\"|<token>" },
120    {  19,  7,"Symbol expected in parsing pattern; found \"%s\"|<token>" },
121    {  20,  0,"Name expected" },
122    {  20,  1,"Name required; found \"%s\"|<token>" },
123    {  20,  2,"Found \"%s\" where only a name is valid|<token>" },
124    {  21,  0,"Invalid data on end of clause" },
125    {  21,  1,"The clause ended at an unexpected token; found \"%s\"|<token>" },
126    {  22,  0,"Invalid character string" },
127    {  22,  1,"Invalid character string '%s'X|<hex-encoding>" },
128    {  23,  0,"Invalid data string" },
129    {  23,  1,"Invalid data string '%s'X|<hex-encoding>" },
130    {  24,  0,"Invalid TRACE request" },
131    {  24,  1,"TRACE request letter must be one of \"%s\"; found \"%c\"|ACEFILNOR,<value>" },
132    {  25,  0,"Invalid sub-keyword found" },
133    {  25,  1,"CALL ON must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
134    {  25,  2,"CALL OFF must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
135    {  25,  3,"SIGNAL ON must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
136    {  25,  4,"SIGNAL OFF must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
137    {  25,  5,"ADDRESS WITH must be followed by one of the keywords INPUT, OUTPUT or ERROR; found \"%s\"|<token>" },
138    {  25,  6,"INPUT must be followed by one of the keywords STREAM, STEM, LIFO, FIFO, NOEOL or NORMAL; found \"%s\"|<token>" },
139    {  25,  7,"OUTPUT must be followed by one of the keywords STREAM, STEM, LIFO, FIFO, APPEND, REPLACE or NORMAL; found \"%s\"|<token>" },
140    {  25,  8,"APPEND must be followed by one of the keywords STREAM, STEM, LIFO or FIFO; found \"%s\"|<token>" },
141    {  25,  9,"REPLACE must be followed by one of the keywords STREAM, STEM, LIFO or FIFO; found \"%s\"|<token>" },
142    {  25, 11,"NUMERIC FORM must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
143    {  25, 12,"PARSE must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
144    {  25, 13,"UPPER must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
145    {  25, 14,"ERROR must be followed by one of the keywords STREAM, STEM, LIFO, FIFO, APPEND, REPLACE or NORMAL; found \"%s\"|<token>" },
146    {  25, 15,"NUMERIC must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
147    {  25, 16,"FOREVER must be followed by one of the keywords %s; found \"%s\"|<keywords>,<token>" },
148    {  25, 17,"PROCEDURE must be followed by the keyword EXPOSE or nothing; found \"%s\"|<token>" },
149    {  26,  0,"Invalid whole number" },
150    {  26,  1,"Whole numbers must fit within current DIGITS setting(%d); found \"%s\"|<value>,<value>" },
151    {  26,  2,"Value of repetition count expression in DO instruction must be zero or a positive whole number; found \"%s\"|<value>" },
152    {  26,  3,"Value of FOR expression in DO instruction must be zero or a positive whole number; found \"%s\"|<value>" },
153    {  26,  4,"Positional parameter of parsing template must be a whole number; found \"%s\"|<value>" },
154    {  26,  5,"NUMERIC DIGITS value must be a positive whole number; found \"%s\"|<value>" },
155    {  26,  6,"NUMERIC FUZZ value must be zero or a positive whole number; found \"%s\"|<value>" },
156    {  26,  7,"Number used in TRACE setting must be a whole number; found \"%s\"|<value>" },
157    {  26,  8,"Operand to right of power operator (\"**\") must be a whole number; found \"%s\"|<value>" },
158    {  26, 11,"Result of %s %% %s operation would need exponential notation at current NUMERIC DIGITS %d|<value>,<value>,<value>" },
159    {  26, 12,"Result of %% operation used for %s // %s operation would need exponential notation at current NUMERIC DIGITS %d|<value>,<value>,<value>" },
160    {  27,  0,"Invalid DO syntax" },
161    {  27,  1,"Invalid use of keyword \"%s\" in DO clause|<token>" },
162    {  28,  0,"Invalid LEAVE or ITERATE" },
163    {  28,  1,"LEAVE is valid only within a repetitive DO loop" },
164    {  28,  2,"ITERATE is valid only within a repetitive DO loop" },
165    {  28,  3,"Symbol following LEAVE (\"%s\") must either match control variable of a current DO loop or be omitted|<token>" },
166    {  28,  4,"Symbol following ITERATE (\"%s\") must either match control variable of a current DO loop or be omitted|<token>" },
167    {  29,  0,"Environment name too long" },
168    {  29,  1,"Environment name exceeds %d characters; found \"%s\"|#Limit_EnvironmentName,<name>" },
169    {  30,  0,"Name or string too long" },
170    {  30,  1,"Name exceeds %d characters|#Limit_Name" },
171    {  30,  2,"Literal string exceeds %d characters|#Limit_Literal" },
172    {  31,  0,"Name starts with number or \".\"" },
173    {  31,  1,"A value cannot be assigned to a number; found \"%s\"|<token>" },
174    {  31,  2,"Variable symbol must not start with a number; found \"%s\"|<token>" },
175    {  31,  3,"Variable symbol must not start with a \".\"; found \"%s\"|<token>" },
176    {  32,  0,"[Invalid use of stem]" },
177    {  33,  0,"Invalid expression result" },
178    {  33,  1,"Value of NUMERIC DIGITS (\"%d\") must exceed value of NUMERIC FUZZ (\"%d\")|<value>,<value>" },
179    {  33,  2,"Value of NUMERIC DIGITS (\"%d\") must not exceed %d|<value>,#Limit_Digits" },
180    {  33,  3,"Result of expression following NUMERIC FORM must start with \"E\" or \"S\"; found \"%s\"|<value>" },
181    {  34,  0,"Logical value not \"0\" or \"1\"" },
182    {  34,  1,"Value of expression following IF keyword must be exactly \"0\" or \"1\"; found \"%s\"|<value>" },
183    {  34,  2,"Value of expression following WHEN keyword must be exactly \"0\" or \"1\"; found \"%s\"|<value>" },
184    {  34,  3,"Value of expression following WHILE keyword must be exactly \"0\" or \"1\"; found \"%s\"|<value>" },
185    {  34,  4,"Value of expression following UNTIL keyword must be exactly \"0\" or \"1\"; found \"%s\"|<value>" },
186    {  34,  5,"Value of expression to left of logical operator \"%s\" must be exactly \"0\" or \"1\"; found \"%s\"|<operator>,<value>" },
187    {  34,  6,"Value of expression to right of logical operator \"%s\" must be exactly \"0\" or \"1\"; found \"%s\"|<operator>,<value>" },
188    {  35,  0,"Invalid expression" },
189    {  35,  1,"Invalid expression detected at \"%s\"|<token>" },
190    {  36,  0,"Unmatched \"(\" in expression" },
191    {  37,  0,"Unexpected \",\" or \")\"" },
192    {  37,  1,"Unexpected \",\"" },
193    {  37,  2,"Unmatched \")\" in expression" },
194    {  38,  0,"Invalid template or pattern" },
195    {  38,  1,"Invalid parsing template detected at \"%s\"|<token>" },
196    {  38,  2,"Invalid parsing position detected at \"%s\"|<token>" },
197    {  38,  3,"PARSE VALUE instruction requires WITH keyword" },
198    {  39,  0,"[Evaluation stack overflow]" },
199    {  40,  0,"Incorrect call to routine" },
200    {  40,  1,"External routine \"%s\" failed|<name>" },
201    {  40,  3,"Not enough arguments in invocation of \"%s\"; minimum expected is %d|<bif>,<argnumber>" },
202    {  40,  4,"Too many arguments in invocation of \"%s\"; maximum expected is %d|<bif>,<argnumber>" },
203    {  40,  5,"Missing argument in invocation of \"%s\"; argument %d is required|<bif>,<argnumber>" },
204    {  40,  9,"%s argument %d exponent exceeds %d digits; found \"%s\"|<bif>,<argnumber>,#Limit_ExponentDigits,<value>" },
205    {  40, 11,"%s argument %d must be a number; found \"%s\"|<bif>,<argnumber>,<value>" },
206    {  40, 12,"%s argument %d must be a whole number; found \"%s\"|<bif>,<argnumber>,<value>" },
207    {  40, 13,"%s argument %d must be zero or positive; found \"%s\"|<bif>,<argnumber>,<value>" },
208    {  40, 14,"%s argument %d must be positive; found \"%s\"|<bif>,<argnumber>,<value>" },
209    {  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>" },
210    {  40, 18,"%s conversion must have a year in the range 0001 to 9999|<bif>" },
211    {  40, 19,"%s argument 2, \"%s\", is not in the format described by argument 3, \"%s\"|<bif>,<value>,<value>" },
212    {  40, 21,"%s argument %d must not be null|<bif>,<argnumber>" },
213    {  40, 23,"%s argument %d must be a single character; found \"%s\"|<bif>,<argnumber>,<value>" },
214    {  40, 24,"%s argument 1 must be a binary string; found \"%s\"|<bif>,<value>" },
215    {  40, 25,"%s argument 1 must be a hexadecimal string; found \"%s\"|<bif>,<value>" },
216    {  40, 26,"%s argument 1 must be a valid symbol; found \"%s\"|<bif>,<value>" },
217    {  40, 27,"%s argument 1, must be a valid stream name; found \"%s\"|<bif>,<value>" },
218    {  40, 28,"%s argument %d, option must start with one of \"%s\"; found \"%s\"|<bif>,<argnumber>,<optionslist>,<value>" },
219    {  40, 29,"%s conversion to format \"%s\" is not allowed|<bif>,<value>" },
220    {  40, 31,"%s argument 1 (\"%d\") must not exceed 100000|<bif>,<value>" },
221    {  40, 32,"%s the difference between argument 1 (\"%d\") and argument 2 (\"%d\") must not exceed 100000|<bif>,<value>,<value>" },
222    {  40, 33,"%s argument 1 (\"%d\") must be less than or equal to argument 2 (\"%d\")|<bif>,<value>,<value>" },
223    {  40, 34,"%s argument 1 (\"%d\") must be less than or equal to the number of lines in the program (%d)|<bif>,<value>,<sourceline()>" },
224    {  40, 35,"%s argument 1 cannot be expressed as a whole number; found \"%s\"|<bif>,<value>" },
225    {  40, 36,"%s argument 1 must be a name of a variable in the pool; found \"%s\"|<bif>,<value>" },
226    {  40, 37,"%s argument 3 must be the name of a pool; found \"%s\"|<bif>,<value>" },
227    {  40, 38,"%s argument %d is not large enough to format \"%s\"|<bif>,<argnumber>,<value>" },
228    {  40, 39,"%s argument 3 is not zero or one; found \"%s\"|<bif>,<value>" },
229    {  40, 41,"%s argument %d must be within the bounds of the stream; found \"%s\"|<bif>,<argnumber>,<value>" },
230    {  40, 42,"%s argument 1; cannot position on this stream; found \"%s\"|<bif>,<value>" },
231    {  40, 43,"%s argument %d must be a single non-alphanumeric character or the null string; found \"%s\"|<bif>,<argnumber>,<value>" },
232    {  40, 44,"%s argument %d, \"%s\", is a format incompatible with the separator specified in argument %d|<bif>,<argnumber>,<value>,<argnumber>" },
233    {  40,914,"[%s argument %d, must be one of \"%s\"; found \"%s\"]|<bif>,<argnumber>,<optionslist>,<value>" },
234    {  40,920,"[%s: low-level stream I/O error; %s]|<bif>,<description>" },
235    {  40,921,"[%s argument %d, stream positioning mode \"%s\"; incompatible with stream open mode]|<bif>,<argnumber>,<value>" },
236    {  40,922,"[%s argument %d, too few sub-commands; minimum expected is %d; found %d]|<bif>,<argnumber>,<value>,<value>" },
237    {  40,923,"[%s argument %d, too many sub-commands; maximum expected is %d; found %d]|<bif>,<argnumber>,<value>,<value>" },
238    {  40,924,"[%s argument %d, invalid positional specification; expecting one of \"%s\"; found \"%s\"]|<bif>,<argnumber>,<value>,<value>" },
239    {  40,930,"[RXQUEUE, function TIMEOUT, expecting a whole number between 0 and %d; found \"%s\"]|<value>,<value>" },
240    {  40,980,"Unexpected input, either unknown type or illegal data%s%s|: ,<location>" },
241    {  40,981,"Number out of the allowed range%s%s|: ,<location>" },
242    {  40,982,"String too big for the defined buffer%s%s|: ,<location>" },
243    {  40,983,"Illegal combination of type/size%s%s|: ,<location>" },
244    {  40,984,"Unsupported number like NAN, +INF, -INF%s%s|: ,<location>" },
245    {  40,985,"Structure too complex for static internal buffer%s%s|: ,<location>" },
246    {  40,986,"An element of the structure is missing%s%s|: ,<location>" },
247    {  40,987,"A value of the structure is missing%s%s|: ,<location>" },
248    {  40,988,"The name or part of the name is illegal for the interpreter%s%s|: ,<location>" },
249    {  40,989,"A problem raises at the interface between Regina and GCI%s%s|: ,<location>" },
250    {  40,990,"The type won't fit the requirements for basic types (arguments/return value)%s%s|: ,<location>" },
251    {  40,991,"The number of arguments is wrong or an argument is missing%s%s|: ,<location>" },
252    {  40,992,"GCI's internal stack for arguments got an overflow%s%s|: ,<location>" },
253    {  40,993,"GCI counted too many nested LIKE containers%s%s|: ,<location>" },
254    {  41,  0,"Bad arithmetic conversion" },
255    {  41,  1,"Non-numeric value (\"%s\") to left of arithmetic operation \"%s\"|<value>,<operator>" },
256    {  41,  2,"Non-numeric value (\"%s\") to right of arithmetic operation \"%s\"|<value>,<operator>" },
257    {  41,  3,"Non-numeric value (\"%s\") used with prefix operator \"%s\"|<value>,<operator>" },
258    {  41,  4,"Value of TO expression in DO instruction must be numeric; found \"%s\"|<value>" },
259    {  41,  5,"Value of BY expression in DO instruction must be numeric; found \"%s\"|<value>" },
260    {  41,  6,"Value of control variable expression of DO instruction must be numeric; found \"%s\"|<value>" },
261    {  41,  7,"Exponent exceeds %d digits; found \"%s\"|#Limit_ExponentDigits,<value>" },
262    {  42,  0,"Arithmetic overflow/underflow" },
263    {  42,  1,"Arithmetic overflow detected at \"%s %s %s\"; exponent of result requires more than %d digits|<value>,<operator>,<value>,#Limit_ExponentDigits" },
264    {  42,  2,"Arithmetic underflow detected at \"%s %s %s\"; exponent of result requires more than %d digits|<value>,<operator>,<value>,#Limit_ExponentDigits" },
265    {  42,  3,"Arithmetic overflow; divisor must not be zero" },
266    {  43,  0,"Routine not found" },
267    {  43,  1,"Could not find routine \"%s\"|<name>" },
268    {  44,  0,"Function did not return data" },
269    {  44,  1,"No data returned from function \"%s\"|<name>" },
270    {  45,  0,"No data specified on function RETURN" },
271    {  45,  1,"Data expected on RETURN instruction because routine \"%s\" was called as a function|<name>" },
272    {  46,  0,"Invalid variable reference" },
273    {  46,  1,"Extra token (\"%s\") found in variable reference; \")\" expected|<token>" },
274    {  47,  0,"Unexpected label" },
275    {  47,  1,"INTERPRET data must not contain labels; found \"%s\"|<name>" },
276    {  48,  0,"Failure in system service" },
277    {  48,  1,"Failure in system service: %s|<description>" },
278    {  48,920,"Low-level stream I/O error: %s %s: %s|<description>,<stream>,<description>" },
279    {  49,  0,"Interpretation Error" },
280    {  49,  1,"Interpretation Error: Failed in %s, line %d: \"%s\". Please report error!|<module>,<linenumber>,<description>" },
281    {  50,  0,"Unrecognized reserved symbol" },
282    {  50,  1,"Unrecognized reserved symbol \"%s\"|<token>" },
283    {  51,  0,"Invalid function name" },
284    {  51,  1,"Unquoted function names must not end in a period; found \"%s\"|<token>" },
285    {  52,  0,"Result returned by \"%s\" is longer than %d characters|<name>,#Limit_String" },
286    {  53,  0,"Invalid option" },
287    {  53,  1,"String or symbol expected after STREAM keyword; found \"%s\"|<token>" },
288    {  53,  2,"Variable reference expected after STEM keyword; found \"%s\"|<token>" },
289    {  53,  3,"Argument to STEM must have one period, as its last character; found \"%s\"|<name>" },
290    {  53,100,"String or symbol expected after LIFO keyword; found \"%s\"|<token>" },
291    {  53,101,"String or symbol expected after FIFO keyword; found \"%s\"|<token>" },
292    {  54,  0,"Invalid STEM value" },
293    {  54,  1,"For this STEM APPEND, the value of \"%s\" must be a count of lines; found \"%s\"|<name>,<value>" },
294    {  60,  0,"[Can't rewind transient file]" },
295    {  61,  0,"[Improper seek operation on file]" },
296    {  64,  0,"[Syntax error while parsing]" },
297    {  64,  1,"[Syntax error at line %d]" },
298    {  64,  2,"[General syntax error at line %d, column %d]|<linenumber>,<columnnumber>" },
299    {  90,  0,"[Non-ANSI feature used with \"OPTIONS STRICT_ANSI\"]" },
300    {  90,  1,"[%s is a Regina extension BIF]|<bif>" },
301    {  90,  2,"[%s is a Regina extension instruction]|<token>" },
302    {  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>" },
303    {  90,  4,"[%s is a Regina extension operator]|<token>" },
304    {  93,  0,"[Incorrect call to routine]" },
305    {  93,  1,"[STREAM command %s must be followed by one of \"%s\"; found \"%s\"]|<token>,<value>,<value>" },
306    {  93,  3,"[STREAM command must be one of \"%s\"; found \"%s\"]|<value>,<value>" },
307    {  94,  0,"[External queue interface error]" },
308    {  94,  1,"[External queue timed out]" },
309    {  94, 99,"[Internal error with external queue interface: %d \"%s\"]|<errcode>,<systemerror>" },
310    {  94,100,"[General system error with external queue interface. %s. %s]|<description>,<systemerror>" },
311    {  94,101,"[Error connecting to %s on port %d: \"%s\"]|<machine>,<portnumber>,<systemerror>" },
312    {  94,102,"[Unable to obtain IP address for %s]|<machine>" },
313    {  94,103,"[Invalid format for server in specified queue name: \"%s\"]|<queuename>" },
314    {  94,104,"[Invalid format for queue name: \"%s\"]|<queuename>" },
315    {  94,105,"[Unable to start Windows Socket interface: %s]|<systemerror>" },
316    {  94,106,"[Maximum number of external queues exceeded: %d]|<maxqueues>" },
317    {  94,107,"[Error occured reading socket: %s]|<systemerror>" },
318    {  94,108,"[Invalid switch passed. Must be one of \"%s\"]|<switch>" },
319    {  94,109,"[Queue \"%s\" not found]|<queuename>" },
320    {  94,110,"[%s invalid for external queues]|<bif>" },
321    {  94,111,"[RXQUEUE function %s invalid for internal queues]|<functionname>" },
322    {  94,112,"[Unable to %s SESSION queue]|<action>" },
323    {  95,  0,"[Restricted feature used in \"safe\" mode]" },
324    {  95,  1,"[%s invalid in \"safe\" mode]|<token>" },
325    {  95,  2,"[%s argument %d invalid in \"safe\" mode]|<bif>,<argnumber>" },
326    {  95,  3,"[%s argument %d: \"%s\", invalid in \"safe\" mode]|<bif>,<argnumber>,<token>" },
327    {  95,  4,"[STREAM argument 3: Opening files for WRITE access invalid in \"safe\" mode]" },
328    {  95,  5,"[Running external commands invalid in \"safe\" mode]" },
329    { 100,  0,"[Unknown filesystem error]" },
330    /*
331     * Do not use an error number > 100, these are treated as system errors (errno-100) and the
332     * error string is obtained from strerror().
333     */
334 } ;
335 
336 /*
337  * Static pointers to language-specific error messages
338  * IF THIS EVER CHANGES, ALSO CHANGE THE SAME TABLE IN msgcmp.c
339  */
340 static const char *errlang[] =
341 {
342    "en", /* english */
343    "de", /* german */
344    "es", /* spanish */
345    "no", /* norwegian */
346    "pt", /* portuguese */
347    "pl", /* polish */
348    "sv", /* swedish */
349    "tr", /* turkish */
350    NULL
351 } ;
352 
353 #define ERR1PREFIX_IDX   0
354 #define SUBERRPREFIX_IDX 1
355 #define ERR2PREFIX_IDX   2
356 static const char *err1prefix = "Error %d running \"%.*s\", line %d: %.*s";
357 static const char *suberrprefix = "Error %d.%d: %.*s";
358 static const char *err2prefix = "Error %d running \"%.*s\": %.*s";
359 
360 static const char *erropen = "Unable to open language file: %s";
361 static const char *errcount = "Incorrect number of messages in language file: %s";
362 static const char *errread = "Unable to read from language file: %s";
363 static const char *errmissing = "Text missing from language file: %s.mtb";
364 static const char *errcorrupt = "Language file: %s.mtb is corrupt";
365 
366 static const char *get_embedded_text_message( int errorno, int suberrorno );
367 
368 /* init_error initializes the module.
369  * Currently, we set up the thread specific data.
370  * The function returns 1 on success, 0 if memory is short.
371  */
init_error(tsd_t * TSD)372 int init_error( tsd_t *TSD )
373 {
374    err_tsd_t *et;
375 
376    if (TSD->err_tsd != NULL)
377       return(1);
378 
379    if ( ( TSD->err_tsd = MallocTSD( sizeof(err_tsd_t) ) ) == NULL )
380       return(0);
381    et = (err_tsd_t *)TSD->err_tsd;
382    memset( et, 0, sizeof(err_tsd_t) );
383    et->errornum = Str_makeTSD( 3 * sizeof( int ) );
384    return(1);
385 }
386 
387 /*
388  * get_buffer allocates a buffer from one of the internal buffers.
389  * No optimusation is done. Memory is cleard by clear-buffers()
390  * A returned buffer will always have a size of 0.
391  * This should never exhaust all memory!
392  */
get_buffer(const tsd_t * TSD,unsigned minsize)393 static streng *get_buffer( const tsd_t *TSD, unsigned minsize )
394 {
395    err_tsd_t *et = (err_tsd_t *)TSD->err_tsd;
396    int idx=-1;
397 
398    minsize++;
399    for ( idx = 0; idx < MAX_ET_BUFFERS; idx++ )
400    {
401       if ( et->buffer[idx] == NULL )
402       {
403          et->buffer[idx] = Str_makeTSD( minsize );
404          break;
405       }
406    }
407    Str_len(et->buffer[idx]) = 0;
408 
409    return et->buffer[idx];
410 }
411 
412 /*
413  * clear_errortext_buffers frees up memory used for internal buffers
414  */
clear_errortext_buffers(const tsd_t * TSD)415 void clear_errortext_buffers( const tsd_t *TSD )
416 {
417    err_tsd_t *et = (err_tsd_t *)TSD->err_tsd;
418    int idx=-1;
419 
420    for ( idx = 0; idx < MAX_ET_BUFFERS; idx++ )
421    {
422       if ( et->buffer[idx] != NULL )
423       {
424          Free_stringTSD( et->buffer[idx] );
425          /*
426           * Fix for Bug #436. Enable system exit to trap parsing errors
427           */
428          et->buffer[idx] = NULL;
429       }
430    }
431 }
432 
lineno_of(cnodeptr node)433 int lineno_of( cnodeptr node )
434 {
435    if (node)
436       return (node->lineno>=0) ? node->lineno : 0 ;
437    else
438       return 0 ;
439 }
440 
charno_of(cnodeptr node)441 static int charno_of( cnodeptr node )
442 {
443    if (node)
444       return (node->charnr>=0) ? node->charnr : 0 ;
445    else
446       return 0 ;
447 }
448 
449 /*
450  * Returns a 0-terminated string in a streng which will be formatted by a
451  * "string formatter" which argument is arg.
452  */
simple_msg(const tsd_t * TSD,const char * fmt,const char * arg)453 static streng *simple_msg( const tsd_t *TSD, const char *fmt,
454                            const char *arg )
455 {
456    int lf = strlen( fmt );
457    int la = strlen( arg );
458    streng *retval;
459 
460    retval = get_buffer( TSD, lf + la );
461    Str_len( retval ) = sprintf( retval->value, fmt, arg );
462 
463    return retval;
464 }
465 
get_text_message(const tsd_t * TSD,FILE * fp,unsigned fileoffset,unsigned textlength,int errorno,int suberrorno,int * is_fmt)466 static streng *get_text_message( const tsd_t *TSD, FILE *fp,
467                                  unsigned fileoffset, unsigned textlength,
468                                  int errorno, int suberrorno, int *is_fmt )
469 {
470    err_tsd_t *et;
471    streng *retval;
472    const char *errfn;
473 
474    et = (err_tsd_t *)TSD->err_tsd;
475 
476 #if defined(__EPOC32__) || defined(__WINS__)
477    errfn="default";
478 #else
479    errfn=errlang[et->native_language];
480 #endif
481    if ( fseek( fp, fileoffset, SEEK_SET ) == -1 )
482    {
483       *is_fmt = 0;
484       return simple_msg( TSD, errcorrupt, errfn );
485    }
486 
487    retval = get_buffer( TSD, textlength + 1 );
488    if ( fread( retval->value, 1, textlength, fp ) != textlength )
489    {
490       *is_fmt = 0;
491       return simple_msg( TSD, errcorrupt, errfn );
492    }
493    retval->value[textlength] = '\0';
494    Str_len(retval) = textlength;
495 
496    return retval;
497 }
498 
get_embedded_text_message(int errorno,int suberrorno)499 static const char *get_embedded_text_message( int errorno, int suberrorno )
500 {
501    int i;
502 
503    for ( i = 0; i < NUMBER_ERROR_MESSAGES; i++ )
504    {
505       if ( errtext[i].errnum == errorno
506       &&   errtext[i].suberrnum == suberrorno )
507       {
508          return errtext[i].text;
509       }
510    }
511    return "";
512 }
513 
514 
515 /* only to be used by syntax and runtime errors, and the halt condition
516  * FIXME: FGC: This function is used while initializing the runtime system.
517  *             Maybe, we don't have a functional tsd_t!
518  *             What shall we do?
519  */
exiterror(int errorno,int suberrorno,...)520 void exiterror( int errorno, int suberrorno, ... )
521 {
522    staticstreng( nofile, "<name>" );
523    va_list argptr;
524    int lineno,signtype;
525 /*   int charno; could be useful for pinpointing actual error */
526    streng *inputfile;
527    streng *suberror_streng=NULL;
528    streng *errmsg, *ptr;
529    int i,ok,len;
530    const streng *fmt, *etext ;
531    FILE *fp = stderr ;
532    err_tsd_t *et;
533    int is_fmt=1;
534    tsd_t *TSD = __regina_get_tsd(); /* The TSD should be fetched directly. This
535                                      * will help if someone corrupted a TSD as
536                                      * an argument.
537                                      * A "fresh" value is always better for
538                                      * tracking down ugly errors.
539                                      * Speed advantage is no reason here! */
540    et = (err_tsd_t *)TSD->err_tsd;
541 
542    if ( ( et == NULL )
543      || ( ( errorno == ERR_STORAGE_EXHAUSTED ) && ( et->conditions > 10 ) ) )
544    {
545       const char *out = get_embedded_text_message( errorno, 0 );
546 
547       len = strlen( out );
548       /*
549        * We allow 10 pending errors only before doing a hard cleanup.
550        * You can use any fixed limit as far as we stop at some time when
551        * having a permanent memory allocation error.
552        * Just write a description end exit. DON'T DO USE A ROUTINE CALLING A
553        * MEMORY ALLOCATION ROUTINE!
554        */
555       if ( ( TSD->currlevel != NULL )
556       && get_options_flag( TSD->currlevel, EXT_STDOUT_FOR_STDERR ) )
557          fp = stdout;
558 
559       fwrite( out, len, 1, fp );
560 #if defined(DOS) || defined(OS2) || defined(WIN32)
561       /*
562        * stdout is open in binary mode, so we need to add the
563        * extra CR to the end of the line.
564        */
565       fputc( REGINA_CR, fp );
566 #endif
567       fputc( REGINA_EOL, fp );
568       goto not_hookable;
569    }
570    et->conditions++;
571 
572    if ( TSD->currentnode )
573    {
574       lineno = lineno_of( TSD->currentnode );
575 /*      charno = charno_of( TSD->currentnode ); */
576    }
577    else
578    {
579 /*      charno = 0; */
580       lineno = parser_data.tline;
581    }
582 
583    signtype = SIGNAL_SYNTAX;
584    if ( errorno == ERR_PROG_INTERRUPT )
585       signtype = SIGNAL_HALT;
586 #ifdef HAVE_VSPRINTF
587    /*
588     * Expanded the sub-error text and pass this to condition_hook for
589     * condition('D') to return the expanded string.
590     */
591    if ( ( errorno <= ERR_MAX_NUMBER ) && ( suberrorno != 0 ) )
592    {
593       fmt = errortext( TSD, errorno, suberrorno, 0, 0 );
594       len = Str_len( fmt );
595       is_fmt = 1;
596       if ( et->native_language == LANGUAGE_ENGLISH
597       ||   et->nls_fp == NULL )
598       {
599          ptr = simple_msg( TSD, "%s", suberrprefix );
600       }
601       else
602       {
603          ptr = get_text_message( TSD, et->nls_fp, et->nls_tpi[SUBERRPREFIX_IDX].fileoffset, et->nls_tpi[SUBERRPREFIX_IDX].textlength, errorno, suberrorno, &is_fmt );
604          if ( !is_fmt )
605          {
606             ptr = simple_msg( TSD, "%s", suberrprefix );
607          }
608       }
609       len += Str_len( ptr );
610       len += 2 * ( ( sizeof(unsigned) * 8 ) / 3 + 2 );
611       errmsg = get_buffer( TSD, len + 3 );
612       len = sprintf( errmsg->value, ptr->value,
613                      errorno, suberrorno, Str_len( fmt ), fmt->value );
614 
615       va_start( argptr, suberrorno );
616       for ( i = 0; i < Str_len( fmt ); i++ )
617       {
618          if ( fmt->value[i] == '%' )
619          {
620             switch ( fmt->value[i+1] )
621             {
622                case 's':
623                   len += strlen( va_arg( argptr, char * ) );
624                   break;
625 
626                case 'c':
627                   /* assignment to anything inhibits compiler warnings */
628                   ok = (int) va_arg( argptr, int );
629                   break;
630 
631                case '%': /* Fixes 1107759 */
632                   i++;
633                   break;
634 
635                default:
636                   len += ( sizeof( unsigned ) * 8 ) / 3 + 2;
637                   /* assignment to anything inhibits compiler warnings */
638                   ok = (int) va_arg( argptr, unsigned );
639                   break;
640             }
641          }
642       }
643       va_end( argptr );
644 
645       suberror_streng = Str_makeTSD( len + 1 );
646       if ( suberror_streng )
647       {
648          va_start( argptr, suberrorno );
649          Str_len( suberror_streng ) = vsprintf( suberror_streng->value,
650                                                 errmsg->value, argptr );
651          va_end( argptr );
652       }
653    }
654 #endif
655 
656    /* Here we should set sigtype to SIGNAL_FATAL for some 'errno's */
657 
658    /* clean up internal buffers before calling condition_hook() as this may not return */
659    /* Fixes bug #463 */
660    clear_errortext_buffers( TSD );
661    /* Get the text for the base errorno for condition_hook() */
662    etext = errortext( TSD, errorno, 0, 0, 0 );
663 
664    /*
665     * Only in case of a SYNTAX error set .MN, ANSI 8.4.1
666     * Keep care of 64 bit machines. A huge number may contain 80 characters
667     * and we are not allowed to use ints, we have to use unsigneds.
668     */
669    if ( signtype == SIGNAL_SYNTAX )
670    {
671       char num[2 * ( ( sizeof(unsigned) * 8 ) / 3 + 2 )];
672 
673       if ( suberrorno )
674       {
675          sprintf( num, "%u.%u", (unsigned) errorno, (unsigned) suberrorno );
676          set_reserved_value( TSD, POOL0_MN, Str_creTSD( num ), 0, VFLAG_STR );
677       }
678       else
679          set_reserved_value( TSD, POOL0_MN, NULL, errorno, VFLAG_NUM );
680    }
681 
682    /* enable a hook into the condition system */
683    et->conditions--;
684 
685    if ( condition_hook( TSD, signtype, errorno, suberrorno, lineno,
686                         Str_dupTSD( etext ), suberror_streng ) )
687    {
688       if ( suberror_streng )
689          Free_stringTSD( suberror_streng );
690       suberror_streng = NULL;
691       return ; /* if CALL ON */
692    }
693 
694    et->conditions++;
695    if ( ( inputfile = TSD->systeminfo->input_file ) == NULL )
696       inputfile = (streng *) nofile;
697    ok = HOOK_GO_ON;
698    if ( lineno > 0 )
699    {
700       traceback( TSD );
701       if ( et->native_language == LANGUAGE_ENGLISH
702       ||   et->nls_fp == NULL )
703       {
704          ptr = simple_msg( TSD, "%s", err1prefix );
705       }
706       else
707       {
708          is_fmt = 1;
709          ptr = get_text_message( TSD, et->nls_fp, et->nls_tpi[ERR1PREFIX_IDX].fileoffset, et->nls_tpi[ERR1PREFIX_IDX].textlength, errorno, suberrorno, &is_fmt );
710          if ( !is_fmt )
711          {
712             ptr = simple_msg( TSD, "%s", err1prefix );
713          }
714       }
715       errmsg = Str_makeTSD( 80 + Str_len( etext ) + Str_len( inputfile ) +
716                             Str_len( ptr ) );
717       sprintf( errmsg->value, ptr->value,
718                errorno, Str_len( inputfile ), inputfile->value, lineno,
719                Str_len( etext ), etext->value );
720    }
721    else
722    {
723       if ( et->native_language == LANGUAGE_ENGLISH
724       ||   et->nls_fp == NULL )
725       {
726          ptr = simple_msg( TSD, "%s", err2prefix );
727       }
728       else
729       {
730          is_fmt = 1;
731          ptr = get_text_message( TSD, et->nls_fp, et->nls_tpi[ERR2PREFIX_IDX].fileoffset, et->nls_tpi[ERR2PREFIX_IDX].textlength, errorno, suberrorno, &is_fmt );
732          if ( !is_fmt )
733          {
734             ptr = simple_msg( TSD, "%s", err2prefix );
735          }
736       }
737       errmsg = Str_makeTSD( 80 + Str_len( etext ) + Str_len( inputfile ) +
738                             Str_len( ptr ) );
739       sprintf( errmsg->value, ptr->value,
740                errorno, Str_len( inputfile ), inputfile->value,
741                Str_len( etext ), etext->value );
742    }
743 
744    errmsg->len = strlen( errmsg->value );
745    assert( errmsg->len < errmsg->max );
746    /*
747     * If we have a system exit installed to handle errors, call it here...
748     */
749    et->conditions--;
750    if ( TSD->systeminfo->hooks & HOOK_MASK( HOOK_STDERR ) )
751       ok = hookup_output( TSD, HOOK_STDERR, errmsg ) == HOOK_GO_ON;
752 
753    if ( ok == HOOK_GO_ON )
754    {
755       /*
756        * To get here we either don't have an exit handler or the exit
757        * handler refused to handle the message, so write it to the
758        * error (or output) stream.
759        */
760       if ( get_options_flag( TSD->currlevel, EXT_STDOUT_FOR_STDERR ) )
761          fp = stdout;
762       fwrite( errmsg->value, Str_len(errmsg), 1, fp );
763 #if defined(DOS) || defined(OS2) || defined(WIN32)
764       /*
765        * stdout is open in binary mode, so we need to add the
766        * extra CR to the end of the line.
767        */
768       fputc( REGINA_CR, fp );
769 #endif
770       fputc( REGINA_EOL, fp );
771    }
772    /*
773     * Display the sub-error text if there is one.
774     */
775    if ( ( errorno <= ERR_MAX_NUMBER ) && suberrorno && suberror_streng )
776    {
777       if ( TSD->systeminfo->hooks & HOOK_MASK( HOOK_STDERR ) )
778          ok = hookup_output(TSD, HOOK_STDERR, suberror_streng ) == HOOK_GO_ON;
779       if ( ok == HOOK_GO_ON )
780       {
781          fwrite( suberror_streng->value, Str_len(suberror_streng), 1, fp );
782 #if defined(DOS) || defined(OS2) || defined(WIN32)
783          /*
784           * stdout is open in binary mode, so we need to add the
785           * extra CR to the end of the line.
786           */
787          fputc( REGINA_CR, fp );
788 #endif
789          fputc( REGINA_EOL, fp );
790       }
791    }
792    if ( ok == HOOK_GO_ON )
793       fflush( fp );
794    if ( suberror_streng )
795       Free_stringTSD( suberror_streng );
796 
797    Free_stringTSD( errmsg );
798    /* clean up internal buffers */
799    clear_errortext_buffers( TSD );
800 
801 not_hookable:
802 
803    if ( TSD->systeminfo->script_exit )
804    {
805       TSD->instore_is_errorfree = 0;
806       if ( et != NULL )
807       {
808          /*
809           * The error handler must inhibit the cleanup of errornum.
810           */
811          et->errornum->len = sprintf( et->errornum->value, "%d", -errorno );
812          jump_script_exit( TSD, et->errornum );
813       }
814    }
815    CloseOpenFiles( TSD, fpdRETAIN );
816    free_orphaned_libs( TSD );
817 
818 #ifdef VMS
819    jump_interpreter_exit( TSD, EXIT_SUCCESS );
820 #else
821    jump_interpreter_exit( TSD, errorno );
822 #endif
823 }
824 
825 /* This function is called by the parser (syntactical interpreter) if an error
826  * occurs.
827  */
__reginaerror(char * dummy)828 void __reginaerror(char *dummy)
829 {
830    /* We ignore the message although it may contain useful informations. */
831    return ;
832 }
833 
read_index_header(const tsd_t * TSD,char * errfn,int native_language,FILE ** fp,int * number_messages,int * file_lang,int * number_prefix_messages)834 static streng *read_index_header( const tsd_t *TSD, char *errfn,
835                                   int native_language, FILE **fp,
836                                   int *number_messages, int *file_lang,
837                                   int *number_prefix_messages )
838 {
839    err_tsd_t *et;
840 
841    et = (err_tsd_t *)TSD->err_tsd;
842    /*
843     * Read the language file header...
844     */
845    *fp = fopen( errfn, "rb" );
846    if ( *fp == NULL )
847    {
848       return simple_msg( TSD, erropen, errfn );
849    }
850    if ( fread( &et->number_messages, sizeof(unsigned int), 1, *fp ) != 1 )
851    {
852       fclose( *fp );
853       return simple_msg( TSD, errread, errfn );
854    }
855    if ( fread( file_lang, sizeof(unsigned int), 1, *fp ) != 1 )
856    {
857       fclose( *fp );
858       return simple_msg( TSD, errread, errfn );
859    }
860    if ( fread( &et->number_prefix_messages, sizeof(unsigned int), 1, *fp ) != 1 )
861    {
862       fclose( *fp );
863       return simple_msg( TSD, errread, errfn );
864    }
865    return NULL;
866 }
867 
read_index_file(const tsd_t * TSD,char * errfn,int native_language,int language_file,FILE ** fp,struct textindex * tmi,struct textindex * tpi)868 static streng *read_index_file( const tsd_t *TSD, char *errfn,
869                                 int native_language, int language_file,
870                                 FILE **fp,
871                                 struct textindex *tmi,
872                                 struct textindex *tpi )
873 {
874    err_tsd_t *et;
875    streng *ptr;
876    int file_lang;
877 
878    et = (err_tsd_t *)TSD->err_tsd;
879    /*
880     * Read the language file header...
881     */
882    if ( ( ptr = read_index_header( TSD, errfn, native_language, fp, &et->number_messages, &file_lang, &et->number_prefix_messages ) ) != NULL )
883    {
884       et->number_messages = 0;
885       return ptr;
886    }
887    /*
888     * Eunsure that the number of messages in the file matches the number defined as
889     * NUMBER_ERROR_MESSAGES in rexxmsg.h
890     */
891    if ( et->number_messages != NUMBER_ERROR_MESSAGES )
892    {
893       fclose( *fp );
894       et->number_messages = 0;
895       return simple_msg( TSD, errcount, errfn );
896    }
897    if ( fread( tmi, sizeof(struct textindex), NUMBER_ERROR_MESSAGES, *fp ) != NUMBER_ERROR_MESSAGES )
898    {
899       fclose( *fp );
900       et->number_messages = 0;
901       return simple_msg( TSD, errread, errfn );
902    }
903    /*
904     * Eunsure that the number of prefix messages in the file matches the number defined as
905     * NUMBER_PREFIX_MESSAGES in rexxmsg.h
906     */
907    if ( et->number_prefix_messages != NUMBER_PREFIX_MESSAGES )
908    {
909       fclose( *fp );
910       et->number_prefix_messages = 0;
911       return simple_msg( TSD, errcount, errfn );
912    }
913    if ( fread( tpi, sizeof(struct textindex), NUMBER_PREFIX_MESSAGES, *fp ) != NUMBER_PREFIX_MESSAGES )
914    {
915       fclose( *fp );
916       et->number_prefix_messages = 0;
917       return simple_msg( TSD, errread, errfn );
918    }
919    return NULL;
920 }
921 
922 /*
923  * Called the first time we need to access an error message
924  * Determines which language file to open and read (always does English as well)
925  * Returns NULL on success, otherwise a pointer to an error message
926  */
get_message_indexes(const tsd_t * TSD)927 static streng *get_message_indexes( const tsd_t *TSD )
928 {
929    streng *err;
930    char *ptr;
931    char fn[REXX_PATH_MAX+20];
932    err_tsd_t *et;
933    char *errpath=NULL;
934 #if defined(__EPOC32__) || defined(__WINS__)
935    FILE *fp;
936    int number_messages, file_lang. number_prefix_messages;
937    struct stat buffer ;
938 #else
939    int i, found=0;
940 #endif
941 
942    et = (err_tsd_t *)TSD->err_tsd;
943 
944 #if defined(__EPOC32__) || defined(__WINS__)
945    /*
946     * Open the default.mtb and read the language type from it.
947     */
948    errpath = "c:\\system\\apps\\reginarexx";
949    sprintf( fn, "%s\\default.mtb", errpath );
950    /*
951     * If there is no default.mtb file, then default to English
952     */
953    if ( stat( fn, &buffer ) != 0 )
954    {
955       et->native_language = LANGUAGE_ENGLISH;
956       return NULL;
957    }
958    /*
959     * We do have a default.mtb file, so read it to determine the language
960     */
961    if ( ( err = read_index_header( TSD, fn, LANGUAGE_ENGLISH, &fp, &number_messages, &file_lang, &number_prefix_messgaes ) ) != NULL )
962    {
963       et->number_messages = et->number_prefix_messages = 0;
964       return err;
965    }
966    if ( fp )
967       fclose( fp );
968    et->native_language = file_lang;
969 #else
970    ptr = getenv( "REGINA_LANG" );
971    if ( ptr == NULL || strlen( ptr) == 0 )
972    {
973       et->native_language = LANGUAGE_ENGLISH;
974    }
975    else
976    {
977       /*
978        * REGINA_LANG may have a comma separated default locale appended.
979        */
980       int len = strcspn( ptr, "," );
981       for ( i = 0; errlang[i] != NULL; i++ )
982       {
983          if ( ( (int) strlen( errlang[i] ) == len )
984            && ( memcmp( ptr, errlang[i], len ) == 0 ) )
985          {
986             et->native_language = i;
987             found = 1;
988             break;
989          }
990       }
991       if ( !found )
992       {
993          err = get_buffer( TSD, 40 + len );
994          Str_len( err ) = sprintf( err->value, "Unsupported native language \"%.*s\"",
995                                                len, ptr );
996          return err;
997       }
998    }
999    if ( et->native_language != LANGUAGE_ENGLISH )
1000    {
1001       errpath = getenv( "REGINA_LANG_DIR" );
1002       if ( errpath == NULL )
1003       {
1004 # if defined(REGINA_SHARE_DIRECTORY)
1005          errpath = REGINA_SHARE_DIRECTORY;
1006 # else
1007          return simple_msg( TSD, "%s", "Unable to determine where Regina language files (*.mtb) are located. Set REGINA_LANG_DIR." );
1008 # endif
1009       }
1010       else if ( strlen( errpath ) > REXX_PATH_MAX )
1011          return simple_msg( TSD, "Length of \"%s\" exceeds the path's maximum", errpath );
1012    }
1013 #endif
1014    /*
1015     * Now read the native language file. If the native language is
1016     * English, don't do anything.
1017     */
1018    if ( et->native_language != LANGUAGE_ENGLISH)
1019    {
1020 #if defined(__EPOC32__) || defined(__WINS__)
1021       sprintf( fn, "%s\\default.mtb", errpath );
1022 #else
1023       sprintf( fn, "%s%c%s.mtb", errpath, FILE_SEPARATOR, errlang[et->native_language] );
1024 #endif
1025       if ( ( err = read_index_file( TSD, fn, et->native_language, et->native_language, &et->nls_fp, (struct textindex *)&et->nls_tmi,(struct textindex *)&et->nls_tpi ) ) != NULL )
1026          return err;
1027    }
1028    return NULL;
1029 }
1030 
1031 
errortext(const tsd_t * TSD,int errorno,int suberrorno,int request_english,int apply_inserts)1032 const streng *errortext( const tsd_t *TSD, int errorno, int suberrorno, int request_english, int apply_inserts )
1033 {
1034    int low=0, mid=0, end=1, up, have_inserts=0, num_inserts=0;
1035    int this_errorno, this_suberrorno;
1036    err_tsd_t *et;
1037    streng *ptr=NULL,*h;
1038    const char *errfn,*embedded;
1039    char *ins;
1040    char *insert[5]; /* maximum of 5 inserts allowed for any one message */
1041    int is_fmt=1;
1042 
1043    /*
1044     * If the supplied errorno is > 100 (the internal limit for interpreter
1045     * errors), assume that a system error message is required.
1046     */
1047    if (errorno>100)
1048    {
1049       return simple_msg( TSD, "%s", strerror(errorno-100) );
1050    }
1051 
1052    et = (err_tsd_t *)TSD->err_tsd;
1053 
1054 #if defined(__EPOC32__) || defined(__WINS__)
1055    errfn="default";
1056 #else
1057    errfn=errlang[et->native_language];
1058 #endif
1059    /*
1060     * The first time this is called, determine the language and read the message file
1061     * indexes into memory from the message file.
1062     */
1063    if ( et->number_messages == 0 )
1064    {
1065       if ( ( ptr = get_message_indexes( TSD ) ) != NULL )
1066       {
1067          /*
1068           * Corrupt or missing language file. Prepend the returned message to the
1069           * embedded error message format.
1070           */
1071 
1072          embedded = get_embedded_text_message( errorno, suberrorno );
1073          h = get_buffer( TSD, Str_len( ptr ) + strlen( embedded ) + 6 );
1074          Str_catstrTSD( h, "(" );
1075          Str_catTSD( h, ptr );
1076          Str_catstrTSD( h, ") " );
1077          Str_catstrTSD( h, embedded );
1078          h->value[Str_len( h )] = '\0';
1079          ptr = h;
1080       }
1081    }
1082    /*
1083     * If we don't already have an error message,
1084     * and we are explicitly requesting an english message, or the native
1085     * language is English, then simply get the message from the internal
1086     * array.
1087     */
1088    if ( !ptr )
1089    {
1090       if ( request_english
1091       ||   et->native_language == LANGUAGE_ENGLISH )
1092       {
1093          ptr = simple_msg( TSD, "%s", get_embedded_text_message( errorno, suberrorno ) );
1094       }
1095       else
1096       {
1097          up = et->number_messages-1;
1098 
1099          while ((end)&&(up>=low))
1100          {
1101             mid = (up+low)/2 ;
1102             this_errorno = et->nls_tmi[mid].errorno;
1103             this_suberrorno = et->nls_tmi[mid].suberrorno;
1104             if ( errorno == this_errorno
1105             &&   suberrorno == this_suberrorno )
1106             {
1107                end = 0;
1108                break;
1109             }
1110             if ( ( errorno > this_errorno )
1111             ||   ( errorno == this_errorno
1112             &&   suberrorno > this_suberrorno ) )
1113                low = mid + 1;
1114             else
1115                up = mid - 1;
1116          }
1117          if (end)
1118          {
1119             /*
1120              * We couldn't find our message...
1121              */
1122             embedded = get_embedded_text_message( errorno, suberrorno );
1123             ptr = simple_msg( TSD, errmissing, errfn );
1124             h = get_buffer( TSD, Str_len( ptr ) + strlen( embedded ) + 6 );
1125             Str_catstrTSD( h, "(" );
1126             Str_catTSD( h, ptr );
1127             Str_catstrTSD( h, ") " );
1128             Str_catstrTSD( h, embedded );
1129             h->value[Str_len( h )] = '\0';
1130             ptr = h;
1131          }
1132          else
1133          {
1134             ptr = get_text_message( TSD, et->nls_fp, et->nls_tmi[mid].fileoffset, et->nls_tmi[mid].textlength, errorno, suberrorno, &is_fmt );
1135             if ( !is_fmt )
1136             {
1137                embedded = get_embedded_text_message( errorno, suberrorno );
1138                h = get_buffer( TSD, Str_len( ptr ) + strlen( embedded ) + 6 );
1139                Str_catstrTSD( h, "(" );
1140                Str_catTSD( h, ptr );
1141                Str_catstrTSD( h, ") " );
1142                Str_catstrTSD( h, embedded );
1143                h->value[Str_len( h )] = '\0';
1144                ptr = h;
1145             }
1146          }
1147       }
1148    }
1149    for ( low = 0; low < (int) Str_len( ptr ); low++ )
1150    {
1151       if ( ptr->value[low] == '|' )
1152       {
1153          ptr->value[low] = '\0';
1154          Str_len( ptr ) = low;
1155          have_inserts = 1;
1156          break;
1157       }
1158    }
1159    /*
1160     * If we need to apply insert, then:
1161     * - adjust the returned fmt string replacing %c, %d, %x with %s
1162     * - iterate through the inserts ( ptr+low+1 )
1163     */
1164    if ( apply_inserts
1165    &&   have_inserts )
1166    {
1167       /*
1168        * The code below makes several assumptions about the format
1169        * of each message. All assumptions are based on having checked
1170        * the format of the messages using the checkmts.rexx script.
1171        */
1172       for ( end = 0; end < Str_len( ptr ); end++ )
1173       {
1174          if ( ptr->value[end] == '%' )
1175          {
1176             switch( ptr->value[end+1] ) /* assumes no message ends in % */
1177             {
1178                case 's':
1179                   num_inserts++;
1180                   break;
1181                case 'c':
1182                case 'x':
1183                case 'd':
1184                   ptr->value[end+1] = 's';
1185                   num_inserts++;
1186                   break;
1187                default:
1188                   break;
1189             }
1190          }
1191       }
1192       ins = ptr->value+low+1;
1193       insert[0] = ins;
1194       low = strlen( ins );
1195       for ( mid = 0,end = 0; end < low; end++ )
1196       {
1197          if ( ins[end] == ',' )
1198          {
1199             ins[end] = '\0';
1200             insert[++mid] = ins+end+1;
1201          }
1202       }
1203       h = get_buffer( TSD, Str_len( ptr ) + low + 1 );
1204       switch( num_inserts )
1205       {
1206          case 1:
1207             Str_len( h ) = sprintf( h->value, ptr->value, insert[0] );
1208             break;
1209          case 2:
1210             Str_len( h ) = sprintf( h->value, ptr->value, insert[0], insert[1] );
1211             break;
1212          case 3:
1213             Str_len( h ) = sprintf( h->value, ptr->value, insert[0], insert[1], insert[2] );
1214             break;
1215          case 4:
1216             Str_len( h ) = sprintf( h->value, ptr->value, insert[0], insert[1], insert[2], insert[3] );
1217             break;
1218          case 5:
1219             Str_len( h ) = sprintf( h->value, ptr->value, insert[0], insert[1], insert[2], insert[3], insert[4] );
1220             break;
1221       }
1222       ptr = h;
1223    }
1224 
1225    return ptr;
1226 }
1227 
1228 #ifndef NDEBUG
1229 
getsym(int numb)1230 const char *getsym( int numb )
1231 {
1232    char *symb=NULL ;
1233 
1234    switch (numb)
1235    {
1236       case X_NULL: symb="Null statement" ; break ;
1237       case X_PROGRAM: symb="Program" ; break ;
1238       case X_STATS: symb="Statements" ; break ;
1239       case X_COMMAND: symb="External command" ; break ;
1240       case X_ADDR_V: symb="ADDRESS (value) statement" ; break ;
1241       case X_ADDR_S: symb="ADDRESS" ; break ;
1242       case X_ADDR_N: symb="ADDRESS (normal) statement" ; break ;
1243       case X_CALL: symb="CALL statement" ; break ;
1244       case X_DO: symb="DO statement" ; break ;
1245       case X_REP: symb="Repetitor in DO" ; break ;
1246       case X_REP_FOREVER: symb="Forever in DO" ; break ;
1247       case X_DO_TO: symb="Upper limit in DO" ; break ;
1248       case X_DO_BY: symb="Step-size in DO" ; break ;
1249       case X_DO_FOR: symb="Max number in DO" ; break ;
1250       case X_WHILE: symb="WHILE expr in DO" ; break ;
1251       case X_UNTIL: symb="UNTIL expr in DO" ; break ;
1252       case X_DROP: symb="DROP statement" ; break ;
1253       case X_EXIT: symb="EXIT statement" ; break ;
1254       case X_IF: symb="IF statement" ; break ;
1255       case X_IPRET: symb="INTERPRET statement" ; break ;
1256       case X_ITERATE: symb="ITERATE statement" ; break ;
1257       case X_LABEL: symb="Label specification" ; break ;
1258       case X_LEAVE: symb="LEAVE statement" ; break ;
1259       case X_NUM_D: symb="NUMERIC DIGIT statement" ; break ;
1260       case X_NUM_F: symb="NUMERIC FORM statement" ; break ;
1261       case X_NUM_FUZZ: symb="NUMERIC FUZZ statement" ; break ;
1262       case X_NUM_SCI: symb="Scientific numeric form" ; break ;
1263       case X_NUM_ENG: symb="Engeenering scientific form" ; break ;
1264       case X_PARSE: symb="PARSE statement" ; break ;
1265       case X_PARSE_ARG: symb="PARSE ARG atatement" ; break ;
1266       case X_PARSE_EXT: symb="External parsing" ; break ;
1267       case X_PARSE_PULL: symb="Parse pull" ; break ;
1268       case X_PARSE_SRC: symb="Parse source" ; break ;
1269       case X_PARSE_VAR: symb="Parse variable" ; break ;
1270       case X_PARSE_VAL: symb="Parse value" ; break ;
1271       case X_PARSE_VER: symb="Parse version" ; break ;
1272       case X_PROC: symb="PROCEDURE statement" ; break ;
1273       case X_PULL: symb="PULL statement" ; break ;
1274       case X_PUSH: symb="PUSH statement" ; break ;
1275       case X_QUEUE: symb="QUEUE statement" ; break ;
1276       case X_RETURN: symb="RETURN statement" ; break ;
1277       case X_SAY: symb="SAY statement" ; break ;
1278       case X_SELECT: symb="SELECT statement" ; break ;
1279       case X_WHENS: symb="WHEN connector" ; break ;
1280       case X_WHEN: symb="WHEN clause" ; break ;
1281       case X_OTHERWISE: symb="OTHERWISE clause" ; break ;
1282       case X_SIG_VAL: symb="SIGNAL VALUE statement" ; break ;
1283       case X_SIG_LAB: symb="SIGNAL (label) statement" ; break ;
1284       case X_SIG_SET: symb="SIGNAL (setting) statement" ; break ;
1285       case X_ON: symb="Setting is ON" ; break ;
1286       case X_OFF: symb="Setting is OFF" ; break ;
1287       case X_S_ERROR: symb="ERROR option" ; break ;
1288       case X_S_HALT: symb="HALT option" ; break ;
1289       case X_S_NOVALUE: symb="NOVALUE option" ; break ;
1290       case X_S_SYNTAX: symb="SYNTAX option" ; break ;
1291       case X_TRACE: symb="TRACE statement" ; break ;
1292       case X_UPPER_VAR: symb="UPPER statement" ; break ;
1293       case X_ASSIGN: symb="Assignment" ; break ;
1294       case X_LOG_NOT: symb="Logical NOT" ; break ;
1295       case X_PLUSS: symb="Plus operator" ; break ;
1296       case X_EQUAL: symb="Equal operator" ; break ;
1297       case X_MINUS: symb="Minus operator" ; break ;
1298       case X_MULT: symb="Multiplication operator" ; break ;
1299       case X_DEVIDE: symb="Division operator" ; break ;
1300       case X_MODULUS: symb="Modulus operator" ; break ;
1301       case X_LOG_OR: symb="Logical or" ; break ;
1302       case X_LOG_AND: symb="Logical and" ; break ;
1303       case X_LOG_XOR: symb="Logical xor" ; break ;
1304       case X_EXP: symb="Exponent operator" ; break ;
1305       case X_CONCAT: symb="String concatenation" ; break ;
1306       case X_SPACE: symb="Space separator" ; break ;
1307       case X_GTE: symb="Greater than or equal operator" ; break ;
1308       case X_LTE: symb="Less than or equal operator" ; break ;
1309       case X_GT: symb="Greater than operator" ; break ;
1310       case X_LT: symb="Less than operator" ; break ;
1311       case X_DIFF: symb="Different operator" ; break ;
1312       case X_SIM_SYMBOL: symb="Simple symbol" ; break ;
1313       case X_CON_SYMBOL: symb="Constant symbol" ; break ;
1314       case X_STRING: symb="Constant string" ; break ;
1315       case X_U_MINUS: symb="Unary minus" ; break ;
1316       case X_S_EQUAL: symb="String equal operator" ; break ;
1317       case X_S_DIFF: symb="String different operator" ; break ;
1318       case X_INTDIV: symb="Integer division" ; break ;
1319       case X_EX_FUNC: symb="External function call" ; break ;
1320       case X_IN_FUNC: symb="Internal function call" ; break ;
1321       case X_TPL_SOLID: symb="Solid point in template" ; break ;
1322       case X_TPL_MVE: symb="Constant pattern" ; break ;
1323       case X_TPL_VAR: symb="Variable pattern" ; break ;
1324       case X_TPL_SYMBOL: symb="Variable in template" ; break ;
1325       case X_TPL_POINT: symb="Placeholder in template" ; break ;
1326       case X_NEG_OFFS: symb="Negative offset" ; break ;
1327       case X_POS_OFFS: symb="Positive offset" ; break ;
1328       case X_ABS_OFFS: symb="Absolute offset" ; break ;
1329       case X_EXPRLIST: symb="Expression connector" ; break ;
1330       case X_S_NOTREADY: symb="NOTREADY option" ; break ;
1331       case X_S_FAILURE: symb="FAILURE option" ; break ;
1332       case X_END: symb="End statement" ; break ;
1333       case X_CALL_SET: symb="CALL specification" ; break ;
1334       case X_NO_OTHERWISE: symb="No otherwise statement" ; break ;
1335       case X_IND_SYMBOL: symb="Indirect symbol" ; break ;
1336       case X_IS_INTERNAL: symb="Internal function" ; break ;
1337       case X_IS_BUILTIN: symb="Builtin function" ; break ;
1338       case X_IS_EXTERNAL: symb="External function" ; break ;
1339       case X_CTAIL_SYMBOL: symb="Constant tail symbol" ; break ;
1340       case X_VTAIL_SYMBOL: symb="Variable tail symbol" ; break ;
1341       case X_HEAD_SYMBOL: symb="Compound variable symbol" ; break ;
1342       case X_STEM_SYMBOL: symb="Stem variable symbol" ; break ;
1343       case X_SEQUAL: symb="Strictly equal operator" ; break ;
1344       case X_SDIFF: symb="Strictly different operator" ; break ;
1345       case X_SGT: symb="Strictly greater than operator" ; break ;
1346       case X_SGTE: symb="Strictly greater than or equal operator" ; break ;
1347       case X_SLT: symb="Strictly less than operator" ; break ;
1348       case X_SLTE: symb="Strictly less than or equal operator" ; break ;
1349       case X_NEQUAL: symb="Not equal operator" ; break ;
1350       case X_NDIFF: symb="Not different operator" ; break ;
1351       case X_NGT: symb="Not greater than operator" ; break ;
1352       case X_NGTE: symb="Not greater than or equal operator" ; break ;
1353       case X_NLT: symb="Not less than operator" ; break ;
1354       case X_NLTE: symb="Not less than or equal operator" ; break ;
1355       case X_NASSIGN: symb="Numeric Assignment" ; break ;
1356       case X_CEXPRLIST: symb="Expression list" ; break ;
1357       case X_U_PLUSS: symb="Unary Plus" ; break ;
1358       case X_OPTIONS: symb="OPTIONS statement" ; break ;
1359       case X_NUM_V: symb="NUMERIC FORM VALUE statement" ; break ;
1360       case X_NUM_DDEF: symb="NUMERIC DIGITS statement" ; break ;
1361       case X_NUM_FDEF: symb="NUMERIC FUZZ statement" ; break ;
1362       case X_NUM_FRMDEF: symb="NUMERIC FORM statement" ; break ;
1363       case X_S_NGT: symb="Strictly not greater than operator" ; break ;
1364       case X_S_NLT: symb="Strictly not less than operator" ; break ;
1365       case X_S_GT: symb="Strictly greater than operator" ; break ;
1366       case X_S_GTE: symb="Strictly greater than or equal operator" ; break ;
1367       case X_S_LT: symb="Strictly less than operator" ; break ;
1368       case X_S_LTE: symb="Strictly less than or equal operator" ; break ;
1369       case X_ADDR_WITH: symb="ADDRESS WITH option list" ; break ;
1370       case X_S_LOSTDIGITS: symb="LOSTDIGITS option" ; break ;
1371       case X_DO_EXPR: symb="Upper limit in DO" ; break ;
1372       case X_PLUSASSIGN: symb="Plus assignemnt operator" ; break ;
1373       case X_MINUSASSIGN: symb="Minus assignemnt operator" ; break ;
1374       case X_MULTASSIGN: symb="Multiplication assignemnt operator" ; break ;
1375       case X_DIVASSIGN: symb="Division assignemnt operator" ; break ;
1376       case X_INTDIVASSIGN: symb="Integer division assignemnt operator" ; break ;
1377       case X_MODULUSASSIGN: symb="Modulus assignemnt operator" ; break ;
1378       case X_ORASSIGN: symb="Logical OR assignemnt operator" ; break ;
1379       case X_XORASSIGN: symb="Logical XOR assignemnt operator" ; break ;
1380       case X_ANDASSIGN: symb="Logical AND assignemnt operator" ; break ;
1381       case X_CONCATASSIGN: symb="Concatenation assignemnt operator" ; break ;
1382       default: symb="Unrecognized value" ;
1383    }
1384 
1385    return symb ;
1386 }
1387 
1388 #endif /* !NDEBUG */
1389