ecere; ide; compiler: Fixed setting safety null character after (v)snprintf
[sdk] / ecere / src / sys / System.ec
1 namespace sys;
2
3 #if defined(ECERE_BOOTSTRAP)
4 #undef __WIN32__
5 #undef __unix__
6 #undef __APPLE__
7 #endif
8
9 default:
10 #define uint _uint
11 #define set _set
12
13 #if defined(__WIN32__)
14
15 #define WIN32_LEAN_AND_MEAN
16 #define UNICODE
17 #define GetFreeSpace _GetFreeSpace
18 #include <windows.h>
19 #undef GetFreeSpace
20
21 #elif defined(__unix__) || defined(__APPLE__)
22
23 #include <sys/time.h>
24 #include <unistd.h>
25
26 #endif
27
28 #undef __BLOCKS__
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #undef uint
33 #undef set
34
35 default:
36 // IMPLEMENTED IN _System.c
37 bool System_MoveFile(char * source, char * dest);
38 bool System_RenameFile(char * oldName, char * newName);
39 bool System_DeleteFile(char * fileName);
40 bool System_MakeDir(char * path);
41 bool System_RemoveDir(char * path);
42 char * System_GetWorkingDir(char * buf, int size);
43 bool System_ChangeWorkingDir(char * buf);
44 char * System_GetEnvironment(char * envName, char * envValue, int max);
45 void System_SetEnvironment(char * envName, char * envValue);
46 void System_UnsetEnvironment(char * envName);
47 bool System_Execute(char * env, char * command, va_list args);
48 bool System_ShellOpen(char * fileName, va_list args);
49 void System_GetFreeSpace(char * path, FileSize64 * size);
50
51 private:
52
53 import "Array"
54 import "i18n"
55 import "File"
56 import "TempFile"
57 import "memory"
58
59 #if !defined(ECERE_BOOTSTRAP)
60 import "units"
61 import "Time"
62 import "Mutex"
63 import "Semaphore"
64 import "FileMonitor"
65 import "Thread"
66 import "Archive"
67 import "EARArchive"
68 import "Date"
69 import "unicode"
70 import "GuiApplication"
71 #endif
72
73 public enum LoggingMode { noLogging, stdOut, stdErr, debug, logFile, msgBox, buffer };
74
75 // GENERAL ERROR CODES
76 public enum ErrorLevel
77 {
78    veryFatal = 0,
79    fatal = 1,
80    major = 2,   
81    minor = 3
82 };
83
84 public define AllErrors = ErrorLevel::Minor;
85
86 public class ErrorCode
87 {
88    ErrorLevel level:2:12;
89    uint code:12:0;
90 };
91
92 public enum SysErrorCode : ErrorCode
93 {
94    allocationFailed = ErrorCode { Fatal, 1 },
95    nameInexistant   = ErrorCode { Fatal, 2 },
96    nameExists       = ErrorCode { Fatal, 3 },
97    missingLibrary   = ErrorCode { Fatal, 4 },
98    fileNotFound     = ErrorCode { Minor, 5 },
99    writeFailed      = ErrorCode { Major, 6 }
100 };
101
102 static define DEFAULT_BUFFER_SIZE = 100 * MAX_F_STRING;
103
104 static Array<String> errorMessages
105 { [
106    $"No error",
107    $"Memory allocation failed",
108    $"Inexistant string identifier specified",
109    $"Identic string identifier already exists",
110    $"Shared library loading failed",
111    $"File not found",
112    $"Couldn't write to file"
113 ] };
114
115 // --- File, directory & environment manipulation ---
116 #undef MoveFile
117
118 public bool MoveFile(char * source, char * dest)
119 {
120    return System_MoveFile(source, dest);
121 }
122
123 public bool RenameFile(char * oldName, char * newName)
124 {
125    return System_RenameFile(oldName, newName);
126 }
127
128 #undef DeleteFile
129
130 public bool DeleteFile(char * fileName)
131 {
132    return System_DeleteFile(fileName);
133 }
134
135 public bool MakeDir(char * path)
136 {
137    return System_MakeDir(path);
138 }
139
140 public bool RemoveDir(char * path)
141 {
142    return System_RemoveDir(path);
143 }
144
145 public char * GetWorkingDir(char * buf, int size)
146 {
147    return System_GetWorkingDir(buf, size);
148 }
149
150 public bool ChangeWorkingDir(char * buf)
151 {
152    return System_ChangeWorkingDir(buf);
153 }
154
155 public char * GetEnvironment(char * envName, char * envValue, int max)
156 {
157    return System_GetEnvironment(envName, envValue, max);
158 }
159
160 public void SetEnvironment(char * envName, char * envValue)
161 {
162    System_SetEnvironment(envName, envValue);
163 }
164
165 public void UnsetEnvironment(char * envName)
166 {
167    System_UnsetEnvironment(envName);
168 }
169
170 public bool Execute(char * command, ...)
171 {
172    bool result;
173    va_list args;
174    va_start(args, command);
175    result = System_Execute(null, command, args);
176    va_end(args);
177    return result;
178 }
179
180 public bool ExecuteEnv(char * env, char * command, ...)
181 {
182    bool result;
183    va_list args;
184    va_start(args, command);
185    result = System_Execute(env, command, args);
186    va_end(args);
187    return result;
188 }
189
190 public bool ShellOpen(char * fileName, ...)
191 {
192    bool result;
193    va_list args;
194    result = System_ShellOpen(fileName, args);
195    va_end(args);
196    return result;
197 }
198
199 public void GetFreeSpace(char * path, FileSize64 * size)
200 {
201    System_GetFreeSpace(path, size);
202 }
203
204 // --- Uncagotegorized Functions ---
205 public void Logf(char * format, ...)
206 {
207    va_list args;
208    char string[MAX_F_STRING];
209    va_start(args, format);
210    vsnprintf(string, sizeof(string), format, args);
211    string[sizeof(string)-1] = 0;
212    Log(string);
213    va_end(args);
214 }
215
216 public void Log(char * text)
217 {
218    switch(globalSystem.errorLoggingMode)
219    {
220       case debug:
221 #if defined(__WIN32__) && !defined(ECERE_BOOTSTRAP)
222       {
223          uint16 * _wtext = UTF8toUTF16(text, null);
224          OutputDebugString(_wtext);
225          delete _wtext;
226          break;
227       }
228 #endif
229       case stdOut: 
230          fputs(text, stdout);
231          fflush(stdout);
232          break;
233       case stdErr:
234          fputs(text, stderr);
235          fflush(stderr);
236          break;
237       case logFile:
238       {
239          File f;
240          if((f = FileOpen(globalSystem.logFile, append)))
241          {
242             f.Puts(text);
243             delete f;
244          }
245          break;
246       }
247       case buffer:
248       case msgBox:
249          strcat(globalSystem.errorBuffer, text); 
250          break;
251    }
252 }
253
254 public void DumpErrors(bool display)
255 {
256    if(globalSystem.errorBuffer && globalSystem.errorBuffer[0])
257    {
258       if(display)
259       {
260 #if defined(__WIN32__) && !defined(ECERE_BOOTSTRAP)
261          if(globalSystem.errorLoggingMode == buffer)
262             printf(globalSystem.errorBuffer);
263          else
264          {
265             char title[1024];
266             sprintf(title, "%s - Error Log", guiApp.appName);
267             MessageBox(HWND_DESKTOP, globalSystem.errorBuffer, title, MB_OK|MB_ICONWARNING);
268          }
269 #else
270          printf(globalSystem.errorBuffer);
271 #endif
272       }
273       globalSystem.errorBuffer[0] = '\0';
274    }
275 }
276
277 public void LogErrorCode(ErrorCode errorCode, char * details)
278 {
279    if(errorCode.level <= globalSystem.errorLevel)
280    {
281       if(details)
282          Logf("System Error [%d]: %s (%s).\n", 
283             errorCode.level,
284             errorMessages[errorCode.code], 
285             details);
286       else
287          Logf("System Error [%d]: %s.\n", 
288             errorCode.level,
289             errorMessages[errorCode.code]);
290    }
291    globalSystem.lastErrorCode = errorCode;
292 }
293
294 public uint GetLastErrorCode()
295 {
296    return globalSystem.lastErrorCode;
297 }
298
299 public void ResetError()
300 {
301    globalSystem.lastErrorCode = 0;
302 }
303
304 public void SetErrorLevel(ErrorLevel level)
305 {
306    globalSystem.errorLevel = level;
307 }
308
309 public void SetLoggingMode(LoggingMode mode, void * where)
310 {
311    globalSystem.errorLoggingMode = mode;
312
313    if(mode == logFile)
314    {
315       File file;
316       strcpy(globalSystem.logFile, where);
317       file = FileOpen(globalSystem.logFile, write);
318       delete file;
319    }
320    else if(mode == buffer || mode == msgBox)
321    {
322       if(!globalSystem.errorBuffer)
323       {
324          globalSystem.errorBufferSize = DEFAULT_BUFFER_SIZE;
325          globalSystem.errorBuffer = new char[DEFAULT_BUFFER_SIZE];
326       }
327       globalSystem.errorBuffer[0] = 0;
328    }
329    else if(mode == debug)
330    {
331 #if defined(__WIN32__) && !defined(ECERE_BOOTSTRAP)
332       uint16 * _wappName = UTF8toUTF16(guiApp.appName, null);
333       OutputDebugString(L"\n");
334       OutputDebugString(_wappName);
335       OutputDebugString(L" - Logging Errors...\n");
336       delete _wappName;
337 #endif
338    }
339    if(mode == 0)
340    {
341       DumpErrors(true);
342       if(globalSystem.errorBuffer)
343       {
344          delete globalSystem.errorBuffer;
345          globalSystem.errorBufferSize = 0;
346       }
347    }
348 }
349
350 static define errorLogMsg = $"\n\nWould you like to view the error log?";
351
352 #if defined(__WIN32__) && !defined(ECERE_BOOTSTRAP)
353 static DWORD REAL_ExceptionHandler(EXCEPTION_POINTERS *exception)
354 {
355    EXCEPTION_RECORD * record = exception->ExceptionRecord;
356    char exceptionString[1024] = "", title[1024];
357
358    switch(record->ExceptionCode)
359    {
360       case EXCEPTION_ACCESS_VIOLATION:
361          if(record->ExceptionInformation[0])
362             sprintf(exceptionString, "Access Violation Writing to 0x%08X", record->ExceptionInformation[1]);
363          else
364             sprintf(exceptionString, "Access Violation Reading from 0x%08X", record->ExceptionInformation[1]);
365          break;
366       case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
367          sprintf(exceptionString, "Array Bounds Exceeded");
368          break;
369       case EXCEPTION_BREAKPOINT:
370          sprintf(exceptionString, "Breakpoint Encountered");
371          break;
372       case EXCEPTION_DATATYPE_MISALIGNMENT:
373          sprintf(exceptionString, "Data Type Misalignment");
374          break;
375       case EXCEPTION_FLT_DENORMAL_OPERAND:
376          sprintf(exceptionString, "Floating-Point Denormal Operand");
377          break;
378       case EXCEPTION_FLT_DIVIDE_BY_ZERO:
379          sprintf(exceptionString, "Floating-Point Divide by Zero");
380          break;
381       case EXCEPTION_FLT_INEXACT_RESULT:
382          sprintf(exceptionString, "Floating-Point Inexact Result");
383          break;
384       case EXCEPTION_FLT_INVALID_OPERATION:
385          sprintf(exceptionString, "Floating-Point Invalid Operation");
386          break;
387       case EXCEPTION_FLT_OVERFLOW:
388          sprintf(exceptionString, "Floating-Point Overflow");
389          break;
390       case EXCEPTION_FLT_STACK_CHECK:
391          sprintf(exceptionString, "Floating-Point Stack Check");
392          break;
393       case EXCEPTION_FLT_UNDERFLOW:
394          sprintf(exceptionString, "Floating-Point Underflow");
395          break;
396       case EXCEPTION_ILLEGAL_INSTRUCTION:
397          sprintf(exceptionString, "Illegal Instruction");
398          break;
399       case EXCEPTION_IN_PAGE_ERROR:
400          sprintf(exceptionString, "In Page Error");
401          break;
402       case EXCEPTION_INT_DIVIDE_BY_ZERO:
403          sprintf(exceptionString, "Integer Divide by Zero");
404          break;
405       case EXCEPTION_INT_OVERFLOW:
406          sprintf(exceptionString, "Integer Overflow");
407          break;
408       case EXCEPTION_INVALID_DISPOSITION:
409          sprintf(exceptionString, "Invalid Disposition");
410          break;
411       case EXCEPTION_NONCONTINUABLE_EXCEPTION:
412          sprintf(exceptionString, "Non Continuable Exception");
413          break;
414       case EXCEPTION_PRIV_INSTRUCTION:
415          sprintf(exceptionString, "Unallowed Instruction");
416          break;
417       case EXCEPTION_SINGLE_STEP:
418          sprintf(exceptionString, "Single Step Exception");
419          break;
420       case EXCEPTION_STACK_OVERFLOW:
421          return EXCEPTION_CONTINUE_SEARCH;
422          /*
423          sprintf(exceptionString, "Stack Overflow");
424          break;
425          */
426    }
427    
428    sprintf(title, "%s - Fatal Error", guiApp.appName);
429
430    if(globalSystem.errorBuffer && globalSystem.errorBuffer[0])
431    {
432       strcat(exceptionString, errorLogMsg);
433       if(MessageBox(HWND_DESKTOP, exceptionString, title, MB_YESNO|MB_ICONERROR) == IDYES)
434          DumpErrors(true);
435    }
436    else
437       MessageBox(HWND_DESKTOP, exceptionString, title, MB_OK|MB_ICONERROR);
438    
439    return EXCEPTION_EXECUTE_HANDLER;
440 }
441 #endif
442
443 private struct System
444 {
445    LoggingMode errorLoggingMode;
446    char * errorBuffer;
447    int errorBufferSize;
448    char logFile[MAX_LOCATION];
449    ErrorCode lastErrorCode;
450    ErrorLevel errorLevel;
451
452    Semaphore eventSemaphore;
453
454    //FileSystem fileSystems;
455
456    // File Monitor
457    OldList fileMonitors;
458    Mutex fileMonitorMutex;
459    Thread fileMonitorThread;
460    bool systemTerminate;
461 };
462
463 System globalSystem;