1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* error.c */ |
4 | /* */ |
5 | /* Error handling for the ca65 macroassembler */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1998-2012, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
15 | /* This software is provided 'as-is', without any expressed or implied */ |
16 | /* warranty. In no event will the authors be held liable for any damages */ |
17 | /* arising from the use of this software. */ |
18 | /* */ |
19 | /* Permission is granted to anyone to use this software for any purpose, */ |
20 | /* including commercial applications, and to alter it and redistribute it */ |
21 | /* freely, subject to the following restrictions: */ |
22 | /* */ |
23 | /* 1. The origin of this software must not be misrepresented; you must not */ |
24 | /* claim that you wrote the original software. If you use this software */ |
25 | /* in a product, an acknowledgment in the product documentation would be */ |
26 | /* appreciated but is not required. */ |
27 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
28 | /* be misrepresented as being the original software. */ |
29 | /* 3. This notice may not be removed or altered from any source */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | #include <stdio.h> |
37 | #include <stdlib.h> |
38 | #include <stdarg.h> |
39 | |
40 | /* common */ |
41 | #include "strbuf.h" |
42 | |
43 | /* ca65 */ |
44 | #include "error.h" |
45 | #include "filetab.h" |
46 | #include "lineinfo.h" |
47 | #include "nexttok.h" |
48 | |
49 | |
50 | |
51 | /*****************************************************************************/ |
52 | /* Data */ |
53 | /*****************************************************************************/ |
54 | |
55 | |
56 | |
57 | /* Warning level */ |
58 | unsigned WarnLevel = 1; |
59 | |
60 | /* Statistics */ |
61 | unsigned ErrorCount = 0; |
62 | unsigned WarningCount = 0; |
63 | |
64 | /* Maximum number of additional notifications */ |
65 | #define MAX_NOTES 8 |
66 | |
67 | |
68 | |
69 | /*****************************************************************************/ |
70 | /* Helper functions */ |
71 | /*****************************************************************************/ |
72 | |
73 | |
74 | |
75 | static void VPrintMsg (const FilePos* Pos, const char* Desc, |
76 | const char* Format, va_list ap) |
77 | /* Format and output an error/warning message. */ |
78 | { |
79 | StrBuf S = STATIC_STRBUF_INITIALIZER; |
80 | |
81 | /* Format the actual message */ |
82 | StrBuf Msg = STATIC_STRBUF_INITIALIZER; |
83 | SB_VPrintf (&Msg, Format, ap); |
84 | SB_Terminate (&Msg); |
85 | |
86 | /* Format the message header */ |
87 | SB_Printf (&S, "%s(%u): %s: " , |
88 | SB_GetConstBuf (GetFileName (Pos->Name)), |
89 | Pos->Line, |
90 | Desc); |
91 | |
92 | /* Append the message to the message header */ |
93 | SB_Append (&S, &Msg); |
94 | |
95 | /* Delete the formatted message */ |
96 | SB_Done (&Msg); |
97 | |
98 | /* Add a new line and terminate the generated full message */ |
99 | SB_AppendChar (&S, '\n'); |
100 | SB_Terminate (&S); |
101 | |
102 | /* Output the full message */ |
103 | fputs (SB_GetConstBuf (&S), stderr); |
104 | |
105 | /* Delete the buffer for the full message */ |
106 | SB_Done (&S); |
107 | } |
108 | |
109 | |
110 | |
111 | static void PrintMsg (const FilePos* Pos, const char* Desc, |
112 | const char* Format, ...) |
113 | /* Format and output an error/warning message. */ |
114 | { |
115 | va_list ap; |
116 | va_start (ap, Format); |
117 | VPrintMsg (Pos, Desc, Format, ap); |
118 | va_end (ap); |
119 | } |
120 | |
121 | |
122 | |
123 | static void AddNotifications (const Collection* LineInfos) |
124 | /* Output additional notifications for an error or warning */ |
125 | { |
126 | unsigned I; |
127 | unsigned Output; |
128 | unsigned Skipped; |
129 | |
130 | /* The basic line info is always in slot zero. It has been used to |
131 | ** output the actual error or warning. The following slots may contain |
132 | ** more information. Check them and print additional notifications if |
133 | ** they're present, but limit the number to a reasonable value. |
134 | */ |
135 | for (I = 1, Output = 0, Skipped = 0; I < CollCount (LineInfos); ++I) { |
136 | /* Get next line info */ |
137 | const LineInfo* LI = CollConstAt (LineInfos, I); |
138 | /* Check the type and output an appropriate note */ |
139 | const char* Msg; |
140 | switch (GetLineInfoType (LI)) { |
141 | |
142 | case LI_TYPE_ASM: |
143 | Msg = "Expanded from here" ; |
144 | break; |
145 | |
146 | case LI_TYPE_EXT: |
147 | Msg = "Assembly code generated from this line" ; |
148 | break; |
149 | |
150 | case LI_TYPE_MACRO: |
151 | Msg = "Macro was defined here" ; |
152 | break; |
153 | |
154 | case LI_TYPE_MACPARAM: |
155 | Msg = "Macro parameter came from here" ; |
156 | break; |
157 | |
158 | default: |
159 | /* No output */ |
160 | Msg = 0; |
161 | break; |
162 | |
163 | } |
164 | |
165 | /* Output until an upper limit of messages is reached */ |
166 | if (Msg) { |
167 | if (Output < MAX_NOTES) { |
168 | PrintMsg (GetSourcePos (LI), "Note" , "%s" , Msg); |
169 | ++Output; |
170 | } else { |
171 | ++Skipped; |
172 | } |
173 | } |
174 | } |
175 | |
176 | /* Add a note if we have more stuff that we won't output */ |
177 | if (Skipped > 0) { |
178 | const LineInfo* LI = CollConstAt (LineInfos, 0); |
179 | PrintMsg (GetSourcePos (LI), "Note" , |
180 | "Dropping %u additional line infos" , Skipped); |
181 | } |
182 | } |
183 | |
184 | |
185 | |
186 | /*****************************************************************************/ |
187 | /* Warnings */ |
188 | /*****************************************************************************/ |
189 | |
190 | |
191 | |
192 | static void WarningMsg (const Collection* LineInfos, const char* Format, va_list ap) |
193 | /* Print warning message. */ |
194 | { |
195 | /* The first entry in the collection is that of the actual source pos */ |
196 | const LineInfo* LI = CollConstAt (LineInfos, 0); |
197 | |
198 | /* Output a warning for this position */ |
199 | VPrintMsg (GetSourcePos (LI), "Warning" , Format, ap); |
200 | |
201 | /* Add additional notifications if necessary */ |
202 | AddNotifications (LineInfos); |
203 | |
204 | /* Count warnings */ |
205 | ++WarningCount; |
206 | } |
207 | |
208 | |
209 | |
210 | void Warning (unsigned Level, const char* Format, ...) |
211 | /* Print warning message. */ |
212 | { |
213 | if (Level <= WarnLevel) { |
214 | |
215 | va_list ap; |
216 | Collection LineInfos = STATIC_COLLECTION_INITIALIZER; |
217 | |
218 | /* Get line infos for the current position */ |
219 | GetFullLineInfo (&LineInfos); |
220 | |
221 | /* Output the message */ |
222 | va_start (ap, Format); |
223 | WarningMsg (&LineInfos, Format, ap); |
224 | va_end (ap); |
225 | |
226 | /* Free the line info list */ |
227 | ReleaseFullLineInfo (&LineInfos); |
228 | DoneCollection (&LineInfos); |
229 | } |
230 | } |
231 | |
232 | |
233 | |
234 | void PWarning (const FilePos* Pos, unsigned Level, const char* Format, ...) |
235 | /* Print warning message giving an explicit file and position. */ |
236 | { |
237 | if (Level <= WarnLevel) { |
238 | va_list ap; |
239 | va_start (ap, Format); |
240 | VPrintMsg (Pos, "Warning" , Format, ap); |
241 | va_end (ap); |
242 | |
243 | /* Count warnings */ |
244 | ++WarningCount; |
245 | } |
246 | } |
247 | |
248 | |
249 | |
250 | void LIWarning (const Collection* LineInfos, unsigned Level, const char* Format, ...) |
251 | /* Print warning message using the given line infos */ |
252 | { |
253 | if (Level <= WarnLevel) { |
254 | /* Output the message */ |
255 | va_list ap; |
256 | va_start (ap, Format); |
257 | WarningMsg (LineInfos, Format, ap); |
258 | va_end (ap); |
259 | } |
260 | } |
261 | |
262 | |
263 | |
264 | /*****************************************************************************/ |
265 | /* Errors */ |
266 | /*****************************************************************************/ |
267 | |
268 | |
269 | |
270 | void ErrorMsg (const Collection* LineInfos, const char* Format, va_list ap) |
271 | /* Print an error message */ |
272 | { |
273 | /* The first entry in the collection is that of the actual source pos */ |
274 | const LineInfo* LI = CollConstAt (LineInfos, 0); |
275 | |
276 | /* Output an error for this position */ |
277 | VPrintMsg (GetSourcePos (LI), "Error" , Format, ap); |
278 | |
279 | /* Add additional notifications if necessary */ |
280 | AddNotifications (LineInfos); |
281 | |
282 | /* Count errors */ |
283 | ++ErrorCount; |
284 | } |
285 | |
286 | |
287 | |
288 | void Error (const char* Format, ...) |
289 | /* Print an error message */ |
290 | { |
291 | va_list ap; |
292 | Collection LineInfos = STATIC_COLLECTION_INITIALIZER; |
293 | |
294 | /* Get line infos for the current position */ |
295 | GetFullLineInfo (&LineInfos); |
296 | |
297 | /* Output the message */ |
298 | va_start (ap, Format); |
299 | ErrorMsg (&LineInfos, Format, ap); |
300 | va_end (ap); |
301 | |
302 | /* Free the line info list */ |
303 | ReleaseFullLineInfo (&LineInfos); |
304 | DoneCollection (&LineInfos); |
305 | } |
306 | |
307 | |
308 | |
309 | void PError (const FilePos* Pos, const char* Format, ...) |
310 | /* Print an error message giving an explicit file and position. */ |
311 | { |
312 | va_list ap; |
313 | va_start (ap, Format); |
314 | VPrintMsg (Pos, "Error" , Format, ap); |
315 | va_end (ap); |
316 | |
317 | /* Count errors */ |
318 | ++ErrorCount; |
319 | } |
320 | |
321 | |
322 | |
323 | void LIError (const Collection* LineInfos, const char* Format, ...) |
324 | /* Print an error message using the given line infos. */ |
325 | { |
326 | /* Output an error for this position */ |
327 | va_list ap; |
328 | va_start (ap, Format); |
329 | ErrorMsg (LineInfos, Format, ap); |
330 | va_end (ap); |
331 | } |
332 | |
333 | |
334 | |
335 | void ErrorSkip (const char* Format, ...) |
336 | /* Print an error message and skip the rest of the line */ |
337 | { |
338 | va_list ap; |
339 | Collection LineInfos = STATIC_COLLECTION_INITIALIZER; |
340 | |
341 | /* Get line infos for the current position */ |
342 | GetFullLineInfo (&LineInfos); |
343 | |
344 | /* Output the message */ |
345 | va_start (ap, Format); |
346 | ErrorMsg (&LineInfos, Format, ap); |
347 | va_end (ap); |
348 | |
349 | /* Free the line info list */ |
350 | ReleaseFullLineInfo (&LineInfos); |
351 | DoneCollection (&LineInfos); |
352 | |
353 | /* Skip tokens until we reach the end of the line */ |
354 | SkipUntilSep (); |
355 | } |
356 | |
357 | |
358 | |
359 | /*****************************************************************************/ |
360 | /* Code */ |
361 | /*****************************************************************************/ |
362 | |
363 | |
364 | |
365 | void Fatal (const char* Format, ...) |
366 | /* Print a message about a fatal error and die */ |
367 | { |
368 | va_list ap; |
369 | StrBuf S = STATIC_STRBUF_INITIALIZER; |
370 | |
371 | va_start (ap, Format); |
372 | SB_VPrintf (&S, Format, ap); |
373 | SB_Terminate (&S); |
374 | va_end (ap); |
375 | |
376 | fprintf (stderr, "Fatal error: %s\n" , SB_GetConstBuf (&S)); |
377 | |
378 | SB_Done (&S); |
379 | |
380 | /* And die... */ |
381 | exit (EXIT_FAILURE); |
382 | } |
383 | |
384 | |
385 | |
386 | void Internal (const char* Format, ...) |
387 | /* Print a message about an internal assembler error and die. */ |
388 | { |
389 | va_list ap; |
390 | StrBuf S = STATIC_STRBUF_INITIALIZER; |
391 | |
392 | va_start (ap, Format); |
393 | SB_VPrintf (&S, Format, ap); |
394 | SB_Terminate (&S); |
395 | va_end (ap); |
396 | |
397 | fprintf (stderr, "Internal assembler error: %s\n" , SB_GetConstBuf (&S)); |
398 | |
399 | SB_Done (&S); |
400 | |
401 | exit (EXIT_FAILURE); |
402 | } |
403 | |