OSDN Git Service

Initial commit for ACC.
[zandronum/zandronum-acc.git] / error.c
1 \r
2 //**************************************************************************\r
3 //**\r
4 //** error.c\r
5 //**\r
6 //**************************************************************************\r
7 \r
8 // HEADER FILES ------------------------------------------------------------\r
9 \r
10 #include <stdio.h>\r
11 #include <stdlib.h>\r
12 #include <stdarg.h>\r
13 #include <string.h>\r
14 #include "common.h"\r
15 #include "error.h"\r
16 #include "token.h"\r
17 #include "misc.h"\r
18 \r
19 // MACROS ------------------------------------------------------------------\r
20 \r
21 #define ERROR_FILE_NAME "acs.err"\r
22 \r
23 // TYPES -------------------------------------------------------------------\r
24 \r
25 typedef enum\r
26 {\r
27         ERRINFO_GCC,\r
28         ERRINFO_VCC\r
29 } errorInfo_e;\r
30 \r
31 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------\r
32 \r
33 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------\r
34 \r
35 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------\r
36 \r
37 static char *ErrorText(error_t error);\r
38 static char *ErrorFileName(void);\r
39 static void eprintf(const char *fmt, ...);\r
40 static void veprintf(const char *fmt, va_list args);\r
41 \r
42 // EXTERNAL DATA DECLARATIONS ----------------------------------------------\r
43 \r
44 extern char acs_SourceFileName[MAX_FILE_NAME_LENGTH];\r
45 \r
46 // PUBLIC DATA DEFINITIONS -------------------------------------------------\r
47 \r
48 // PRIVATE DATA DEFINITIONS ------------------------------------------------\r
49 \r
50 static struct\r
51 {\r
52         error_t number;\r
53         char *name;\r
54 } ErrorNames[] =\r
55 {\r
56         { ERR_MISSING_SEMICOLON, "Missing semicolon." },\r
57         { ERR_MISSING_LPAREN, "Missing '('." },\r
58         { ERR_MISSING_RPAREN, "Missing ')'." },\r
59         { ERR_MISSING_LBRACE, "Missing '{'." },\r
60         { ERR_MISSING_SCRIPT_NUMBER, "Missing script number." },\r
61         { ERR_IDENTIFIER_TOO_LONG, "Identifier too long." },\r
62         { ERR_STRING_TOO_LONG, "String too long." },\r
63         { ERR_FILE_NAME_TOO_LONG, "File name too long.\nFile: \"%s\"" },\r
64         { ERR_BAD_CHARACTER, "Bad character in script text." },\r
65         { ERR_BAD_CHARACTER_CONSTANT, "Bad character constant in script text." },\r
66         { ERR_ALLOC_PCODE_BUFFER, "Failed to allocate PCODE buffer." },\r
67         { ERR_PCODE_BUFFER_OVERFLOW, "PCODE buffer overflow." },\r
68         { ERR_TOO_MANY_SCRIPTS, "Too many scripts." },\r
69         { ERR_TOO_MANY_FUNCTIONS, "Too many functions." },\r
70         { ERR_SAVE_OBJECT_FAILED, "Couldn't save object file." },\r
71         { ERR_MISSING_LPAREN_SCR, "Missing '(' in script definition." },\r
72         { ERR_INVALID_IDENTIFIER, "Invalid identifier." },\r
73         { ERR_REDEFINED_IDENTIFIER, "%s : Redefined identifier." },\r
74         { ERR_MISSING_COMMA, "Missing comma." },\r
75         { ERR_BAD_VAR_TYPE, "Invalid variable type." },\r
76         { ERR_BAD_RETURN_TYPE, "Invalid return type." },\r
77         { ERR_TOO_MANY_SCRIPT_ARGS, "Too many script arguments." },\r
78         { ERR_MISSING_LBRACE_SCR, "Missing opening '{' in script definition." },\r
79         { ERR_MISSING_RBRACE_SCR, "Missing closing '}' in script definition." },\r
80         { ERR_TOO_MANY_MAP_VARS, "Too many map variables." },\r
81         { ERR_TOO_MANY_SCRIPT_VARS, "Too many script variables." },\r
82         { ERR_TOO_MANY_FUNCTION_VARS, "Too many function variables." },\r
83         { ERR_MISSING_WVAR_INDEX, "Missing index in world variable declaration." },\r
84         { ERR_MISSING_GVAR_INDEX, "Missing index in global variable declaration." },\r
85         { ERR_BAD_WVAR_INDEX, "World variable index out of range." },\r
86         { ERR_MISSING_WVAR_COLON, "Missing colon in world variable declaration." },\r
87         { ERR_MISSING_GVAR_COLON, "Missing colon in global variable declaration." },\r
88         { ERR_MISSING_SPEC_VAL, "Missing value in special declaration." },\r
89         { ERR_MISSING_SPEC_COLON, "Missing colon in special declaration." },\r
90         { ERR_MISSING_SPEC_ARGC, "Missing argument count in special declaration." },\r
91         { ERR_CANT_READ_FILE, "Couldn't read file.\nFile: \"%s\"" },\r
92         { ERR_CANT_OPEN_FILE, "Couldn't open file.\nFile: \"%s\"" },\r
93         { ERR_CANT_OPEN_DBGFILE, "Couldn't open debug file." },\r
94         { ERR_INVALID_DIRECTIVE, "Invalid directive." },\r
95         { ERR_BAD_DEFINE, "Non-numeric constant found in #define." },\r
96         { ERR_INCL_NESTING_TOO_DEEP, "Include nesting too deep.\nUnable to include file \"%s\"." },\r
97         { ERR_STRING_LIT_NOT_FOUND, "String literal not found." },\r
98         { ERR_INVALID_DECLARATOR, "Invalid declarator." },\r
99         { ERR_BAD_LSPEC_ARG_COUNT, "Incorrect number of special arguments." },\r
100         { ERR_BAD_ARG_COUNT, "Incorrect number of arguments." },\r
101         { ERR_UNKNOWN_IDENTIFIER, "%s : Identifier has not been declared." },\r
102         { ERR_MISSING_COLON, "Missing colon." },\r
103         { ERR_BAD_EXPR, "Syntax error in expression." },\r
104         { ERR_BAD_CONST_EXPR, "Syntax error in constant expression." },\r
105         { ERR_NO_DIRECT_VER, "Internal function has no direct version." },\r
106         { ERR_ILLEGAL_EXPR_IDENT, "%s : Illegal identifier in expression." },\r
107         { ERR_EXPR_FUNC_NO_RET_VAL, "Function call in expression has no return value." },\r
108         { ERR_MISSING_ASSIGN_OP, "Missing assignment operator." },\r
109         { ERR_INCDEC_OP_ON_NON_VAR, "'++' or '--' used on a non-variable." },\r
110         { ERR_MISSING_RBRACE, "Missing '}' at end of compound statement." },\r
111         { ERR_INVALID_STATEMENT, "Invalid statement." },\r
112         { ERR_BAD_DO_STATEMENT, "Do statement not followed by 'while' or 'until'." },\r
113         { ERR_BAD_SCRIPT_DECL, "Bad script declaration." },\r
114         { ERR_CASE_OVERFLOW, "Internal Error: Case stack overflow." },\r
115         { ERR_BREAK_OVERFLOW, "Internal Error: Break stack overflow." },\r
116         { ERR_CONTINUE_OVERFLOW, "Internal Error: Continue stack overflow." },\r
117         { ERR_STATEMENT_OVERFLOW, "Internal Error: Statement overflow." },\r
118         { ERR_MISPLACED_BREAK, "Misplaced BREAK statement." },\r
119         { ERR_MISPLACED_CONTINUE, "Misplaced CONTINUE statement." },\r
120         { ERR_CASE_NOT_IN_SWITCH, "CASE must appear in switch statement." },\r
121         { ERR_DEFAULT_NOT_IN_SWITCH, "DEFAULT must appear in switch statement." },\r
122         { ERR_MULTIPLE_DEFAULT, "Only 1 DEFAULT per switch allowed." },\r
123         { ERR_EXPR_STACK_OVERFLOW, "Expression stack overflow." },\r
124         { ERR_EXPR_STACK_EMPTY, "Tried to POP empty expression stack." },\r
125         { ERR_UNKNOWN_CONST_EXPR_PCD, "Unknown PCD in constant expression." },\r
126         { ERR_BAD_RADIX_CONSTANT, "Radix out of range in integer constant." },\r
127         { ERR_BAD_ASSIGNMENT, "Syntax error in multiple assignment statement." },\r
128         { ERR_OUT_OF_MEMORY, "Out of memory." },\r
129         { ERR_TOO_MANY_STRINGS, "Too many strings. Current max is %d" },\r
130         { ERR_UNKNOWN_PRTYPE, "Unknown cast type in print statement." },\r
131         { ERR_SCRIPT_OUT_OF_RANGE, "Script number must be between 1 and 999." },\r
132         { ERR_MISSING_PARAM, "Missing required argument." },\r
133         { ERR_SCRIPT_ALREADY_DEFINED, "Script already has a body." },\r
134         { ERR_FUNCTION_ALREADY_DEFINED, "Function already has a body." },\r
135         { ERR_PARM_MUST_BE_VAR, "Parameter must be a variable." },\r
136         { ERR_LANGCODE_SIZE, "Language code must be 2 or 3 characters long." },\r
137         { ERR_MISSING_LBRACE_LOC, "Missing opening '{' in localization definition." },\r
138         { ERR_MISSING_RBRACE_LOC, "Missing closing '}' in localization definition." },\r
139         { ERR_MISSING_LOCALIZED, "Missing localized string." },\r
140         { ERR_BAD_LANGCODE, "Language code must be all letters." },\r
141         { ERR_MISSING_LANGCODE, "Missing language code in localization definiton." },\r
142         { ERR_MISSING_FONT_NAME, "Missing font name." },\r
143         { ERR_MISSING_LBRACE_FONTS, "Missing opening '{' in font list." },\r
144         { ERR_MISSING_RBRACE_FONTS, "Missing closing '}' in font list." },\r
145         { ERR_NOCOMPACT_NOT_HERE, "#nocompact must appear before any scripts." },\r
146         { ERR_MISSING_ASSIGN, "Missing '='." },\r
147         { ERR_PREVIOUS_NOT_VOID, "Previous use of function expected a return value." },\r
148         { ERR_MUST_RETURN_A_VALUE, "Function must return a value." },\r
149         { ERR_MUST_NOT_RETURN_A_VALUE, "Void functions cannot return a value." },\r
150         { ERR_SUSPEND_IN_FUNCTION, "Suspend cannot be used inside a function." },\r
151         { ERR_TERMINATE_IN_FUNCTION, "Terminate cannot be used inside a function." },\r
152         { ERR_RESTART_IN_FUNCTION, "Restart cannot be used inside a function." },\r
153         { ERR_RETURN_OUTSIDE_FUNCTION, "Return can only be used inside a function." },\r
154         { ERR_FUNC_ARGUMENT_COUNT, "Function %s should have %d argument%s." },\r
155         { ERR_EOF, "Unexpected end of file." },\r
156         { ERR_UNDEFINED_FUNC, "Function %s is used but not defined." },\r
157         { ERR_TOO_MANY_ARRAY_DIMS, "Too many array dimensions." },\r
158         { ERR_MISSING_LBRACKET, "Missing '['." },\r
159         { ERR_MISSING_RBRACKET, "Missing ']'." },\r
160         { ERR_ZERO_DIMENSION, "Arrays cannot have a dimension of zero." },\r
161         { ERR_TOO_MANY_DIM_USED, "%s only has %d dimensions." },\r
162         { ERR_ARRAY_MAPVAR_ONLY, "Only map variables can be arrays." },\r
163         { ERR_NOT_AN_ARRAY, "%s is not an array." },\r
164         { ERR_MISSING_LBRACE_ARR, "Missing opening '{' in array initializer." },\r
165         { ERR_MISSING_RBRACE_ARR, "Missing closing '}' in array initializer." },\r
166         { ERR_LATENT_IN_FUNC, "Latent functions cannot be used inside functions." },\r
167         { ERR_LOCAL_VAR_SHADOWED, "A global identifier already has this name." },\r
168         { ERR_MULTIPLE_IMPORTS, "You can only #import one file." },\r
169         { ERR_IMPORT_IN_EXPORT, "You cannot #import from inside an imported file." },\r
170         { ERR_EXPORTER_NOT_FLAGGED, "A file that you #import must have an #imported line." },\r
171         { ERR_TOO_MANY_IMPORTS, "Too many files imported." },\r
172         { ERR_NO_NEED_ARRAY_SIZE, "Only map arrays need a size." },\r
173         { ERR_NO_MULTIDIMENSIONS, "Only map arrays can have more than one dimension." },\r
174         { ERR_NEED_ARRAY_SIZE, "Missing array size." },\r
175         { ERR_DISCONNECT_NEEDS_1_ARG, "Disconnect scripts must have 1 argument." },\r
176         { ERR_UNCLOSED_WITH_ARGS, "Most special scripts must not have arguments." },\r
177         { ERR_NOT_A_CHAR_ARRAY, "%s has %d dimensions. Use %d subscripts to get a char array." },\r
178         { ERR_NONE, NULL }\r
179 };\r
180 \r
181 static FILE *ErrorFile;\r
182 static int ErrorCount;\r
183 static errorInfo_e ErrorFormat;\r
184 static char *ErrorSourceName;\r
185 static int ErrorSourceLine;\r
186 \r
187 // CODE --------------------------------------------------------------------\r
188 \r
189 //==========================================================================\r
190 //\r
191 // ERR_ErrorAt\r
192 //\r
193 //==========================================================================\r
194 \r
195 void ERR_ErrorAt(char *source, int line)\r
196 {\r
197         ErrorSourceName = source;\r
198         ErrorSourceLine = line;\r
199 }\r
200 \r
201 //==========================================================================\r
202 //\r
203 // ERR_Error\r
204 //\r
205 //==========================================================================\r
206 \r
207 void ERR_Error(error_t error, boolean info, ...)\r
208 {\r
209         va_list args;\r
210         va_start(args, info);\r
211         ERR_ErrorV(error, info, args);\r
212         va_end(args);\r
213 }\r
214 \r
215 //==========================================================================\r
216 //\r
217 // ERR_Exit\r
218 //\r
219 //==========================================================================\r
220 \r
221 void ERR_Exit(error_t error, boolean info, ...)\r
222 {\r
223         va_list args;\r
224         va_start(args, info);\r
225         ERR_ErrorV(error, info, args);\r
226         va_end(args);\r
227         ERR_Finish();\r
228 }\r
229 \r
230 //==========================================================================\r
231 //\r
232 // ERR_Finish\r
233 //\r
234 //==========================================================================\r
235 \r
236 void ERR_Finish(void)\r
237 {\r
238         if(ErrorFile)\r
239         {\r
240                 fclose(ErrorFile);\r
241                 ErrorFile = NULL;\r
242         }\r
243         if(ErrorCount)\r
244         {\r
245                 exit(1);\r
246         }\r
247 }\r
248 \r
249 //==========================================================================\r
250 //\r
251 // ShowError\r
252 //\r
253 //==========================================================================\r
254 \r
255 void ERR_ErrorV(error_t error, boolean info, va_list args)\r
256 {\r
257         char *text;\r
258         boolean showLine = NO;\r
259         static boolean showedInfo = NO;\r
260 \r
261         if(!ErrorFile)\r
262         {\r
263                 ErrorFile = fopen(ErrorFileName(), "w");\r
264         }\r
265         if(ErrorCount == 0)\r
266         {\r
267                 fprintf(stderr, "\n**** ERROR ****\n");\r
268         }\r
269         else if(ErrorCount == 100)\r
270         {\r
271                 eprintf("More than 100 errors. Can't continue.\n");\r
272                 ERR_Finish();\r
273         }\r
274         ErrorCount++;\r
275         if(info == YES)\r
276         {\r
277                 char *source;\r
278                 int line;\r
279 \r
280                 if(ErrorSourceName)\r
281                 {\r
282                         source = ErrorSourceName;\r
283                         line = ErrorSourceLine;\r
284                         ErrorSourceName = NULL;\r
285                 }\r
286                 else\r
287                 {\r
288                         source = tk_SourceName;\r
289                         line = tk_Line;\r
290                         showLine = YES;\r
291                 }\r
292                 if(showedInfo == NO)\r
293                 { // Output info compatible with older ACCs\r
294                   // for editors that expect it.\r
295                         showedInfo = YES;\r
296                         eprintf("Line %d in file \"%s\" ...\n", line, source);\r
297                 }\r
298                 if(ErrorFormat == ERRINFO_GCC)\r
299                 {\r
300                         eprintf("%s:%d: ", source, line);\r
301                 }\r
302                 else\r
303                 {\r
304                         eprintf("%s(%d) : ", source, line);\r
305                         if(error != ERR_NONE)\r
306                         {\r
307                                 eprintf("error %04d: ", error);\r
308                         }\r
309                 }\r
310         }\r
311         if(error != ERR_NONE)\r
312         {\r
313                 text = ErrorText(error);\r
314                 if(text != NULL)\r
315                 {\r
316                         veprintf(text, args);\r
317                 }\r
318                 eprintf("\n");\r
319                 if(showLine)\r
320                 {\r
321                         // deal with master source line and position indicator - Ty 07jan2000\r
322                         MasterSourceLine[MasterSourcePos] = '\0';  // pre-incremented already\r
323                         eprintf("> %s\n", MasterSourceLine);  // the string \r
324                         eprintf(">%*s\n", MasterSourcePos, "^");  // pointer to error\r
325                 }\r
326         }\r
327 #if 0\r
328         else\r
329         {\r
330                 va_list args2;\r
331                 va_start(va_arg(args,char*), args2);\r
332                 veprintf(va_arg(args,char*), args2);\r
333                 va_end(args2);\r
334         }\r
335 #endif\r
336 }\r
337 \r
338 //==========================================================================\r
339 //\r
340 // ERR_RemoveErrorFile\r
341 //\r
342 //==========================================================================\r
343 \r
344 void ERR_RemoveErrorFile(void)\r
345 {\r
346         remove(ErrorFileName());\r
347 }\r
348 \r
349 //==========================================================================\r
350 //\r
351 // ERR_ErrorFileName\r
352 //\r
353 //==========================================================================\r
354 \r
355 static char *ErrorFileName(void)\r
356 {\r
357         static char errFileName[MAX_FILE_NAME_LENGTH];\r
358 \r
359         strcpy(errFileName, acs_SourceFileName);\r
360         if(MS_StripFilename(errFileName) == NO)\r
361         {\r
362                 strcpy(errFileName, ERROR_FILE_NAME);\r
363         }\r
364         else\r
365         {\r
366                 strcat(errFileName, DIRECTORY_DELIMITER ERROR_FILE_NAME);\r
367         }\r
368         return errFileName;\r
369 }\r
370 \r
371 //==========================================================================\r
372 //\r
373 // ErrorText\r
374 //\r
375 //==========================================================================\r
376 \r
377 static char *ErrorText(error_t error)\r
378 {\r
379         int i;\r
380 \r
381         for(i = 0; ErrorNames[i].number != ERR_NONE; i++)\r
382         {\r
383                 if(error == ErrorNames[i].number)\r
384                 {\r
385                         return ErrorNames[i].name;\r
386                 }\r
387         }\r
388         return NULL;\r
389 }\r
390 \r
391 //==========================================================================\r
392 //\r
393 // eprintf\r
394 //\r
395 //==========================================================================\r
396 \r
397 static void eprintf(const char *fmt, ...)\r
398 {\r
399         va_list args;\r
400         va_start(args, fmt);\r
401         veprintf(fmt, args);\r
402         va_end(args);\r
403 }\r
404 \r
405 //==========================================================================\r
406 //\r
407 // veprintf\r
408 //\r
409 //==========================================================================\r
410 \r
411 static void veprintf(const char *fmt, va_list args)\r
412 {\r
413         vfprintf(stderr, fmt, args);\r
414         if(ErrorFile)\r
415         {\r
416                 vfprintf(ErrorFile, fmt, args);\r
417         }\r
418 }\r