1476d6b000799e85a04d3a2a4444c28c5af1e30b
[sdk] / ide / src / IDESettings.ec
1 #if defined(__WIN32__)
2 #define MessageBox _MessageBox
3 #define WIN32_LEAN_AND_MEAN
4 #include <windows.h>
5 #undef MessageBox
6 #endif
7
8 #ifdef ECERE_STATIC
9 public import static "ecere"
10 #else
11 public import "ecere"
12 #endif
13
14 define ecpDefaultCommand = "ecp";
15 define eccDefaultCommand = "ecc";
16 define ecsDefaultCommand = "ecs";
17 define earDefaultCommand = "ear";
18 define cppDefaultCommand = "gcc"; // As per #624 we decided to default to "gcc"...
19 define ccDefaultCommand = "gcc";
20 define cxxDefaultCommand = "g++";
21 //define ldDefaultCommand = "gcc";
22 define arDefaultCommand = "ar";
23 define objectDefaultFileExt = "o";
24 define outputDefaultFileExt = "";
25
26 import "StringsBox"
27
28 import "OldIDESettings"
29
30 define MaxRecent = 9;
31
32 enum DirTypes { includes, libraries, executables };
33
34 define defaultCompilerName = "Default";
35
36 define defaultObjDirExpression = "obj/$(CONFIG).$(PLATFORM)$(COMPILER_SUFFIX)$(DEBUG_SUFFIX)";
37
38 const char * settingsDirectoryNames[DirTypes] =
39 {
40    "Include Files",
41    "Library Files",
42    "Executable Files"
43 };
44
45 // This function cannot accept same pointer for source and output
46 // todo: rename ReplaceSpaces to EscapeSpaceAndSpecialChars or something
47 void ReplaceSpaces(char * output, const char * source)
48 {
49    int c, dc;
50    char ch, pch = 0;
51
52    for(c = 0, dc = 0; (ch = source[c]); c++, dc++)
53    {
54       if(ch == ' ') output[dc++] = '\\';
55       if(ch == '\"') output[dc++] = '\\';
56       if(ch == '&') output[dc++] = '\\';
57       if(pch != '$')
58       {
59          if(ch == '(' || ch == ')') output[dc++] = '\\';
60          pch = ch;
61       }
62       else if(ch == ')')
63          pch = 0;
64       output[dc] = ch;
65    }
66    output[dc] = '\0';
67 }
68
69
70 enum GlobalSettingsChange { none, editorSettings, projectOptions, compilerSettings };
71
72 enum PathRelationship { unrelated, identical, siblings, subPath, parentPath, insuficientInput, pathEmpty, toEmpty, pathNull, toNull, bothEmpty, bothNull };
73 PathRelationship eString_PathRelated(const char * path, const char * to, char * pathDiff)
74 {
75    PathRelationship result;
76    if(pathDiff) *pathDiff = '\0';
77    if(path && *path && to && *to)
78    {
79       char toPart[MAX_FILENAME], toRest[MAX_LOCATION];
80       char pathPart[MAX_FILENAME], pathRest[MAX_LOCATION];
81       strcpy(toRest, to);
82       strcpy(pathRest, path);
83       for(; toRest[0] && pathRest[0];)
84       {
85          SplitDirectory(toRest, toPart, toRest);
86          SplitDirectory(pathRest, pathPart, pathRest);
87          if(!fstrcmp(pathPart, toPart)) result = siblings;
88          else break;
89       }
90       if(result == siblings)
91       {
92          if(!*toRest && !*pathRest) result = identical;
93          else if(!*pathRest) result = parentPath;
94          else result = subPath;
95          if(pathDiff && result != identical) strcpy(pathDiff, *pathRest == '\0' ? toRest : pathRest);
96       }
97       else result = unrelated;
98    }
99    else
100    {
101       if(path && to)
102       {
103          if(!*path && !*to) result = bothEmpty;
104          else if(!*path) result = pathEmpty;
105          else result = toEmpty;
106       }
107       else if(!path && !to) result = bothNull;
108       else if(!path) result = pathNull;
109       else result = toNull;
110    }
111    return result;
112 }
113
114 char * CopyValidateMakefilePath(const char * path)
115 {
116    const int map[]  =    {           0,           1,             2,             3,           4,                    5,                 6,            0,                   1,                    2,        7 };
117    const char * vars[] = { "$(MODULE)", "$(CONFIG)", "$(PLATFORM)", "$(COMPILER)", "$(TARGET)", "$(COMPILER_SUFFIX)", "$(DEBUG_SUFFIX)", "$(PROJECT)",  "$(CONFIGURATION)", "$(TARGET_PLATFORM)",(char *)0 };
118
119    char * copy = null;
120    if(path)
121    {
122       int len;
123       len = (int)strlen(path);
124       copy = CopyString(path);
125       if(len)
126       {
127          int c;
128          char * tmp = copy;
129          char * start = tmp;
130          Array<const char *> parts { };
131
132          for(c=0; c<len; c++)
133          {
134             if(tmp[c] == '$')
135             {
136                int v;
137                for(v=0; vars[v]; v++)
138                {
139                   if(SearchString(&tmp[c], 0, vars[v], false, false) == &tmp[c])
140                   {
141                      tmp[c] = '\0';
142                      parts.Add(start);
143                      parts.Add(vars[map[v]]);
144                      c += strlen(vars[v]);
145                      start = &tmp[c];
146                      c--;
147                      break;
148                   }
149                }
150             }
151          }
152          if(start[0])
153             parts.Add(start);
154
155          if(parts.count)
156          {
157             /*int c, */len = 0;
158             for(c=0; c<parts.count; c++) len += strlen(parts[c]);
159             copy = new char[++len];
160             copy[0] = '\0';
161             for(c=0; c<parts.count; c++) strcat(copy, parts[c]);
162          }
163          else
164             copy = null;
165          delete parts;
166          delete tmp;
167       }
168    }
169    return copy;
170 }
171
172 void ValidPathBufCopy(char *output, const char *input)
173 {
174 #ifdef __WIN32__
175    bool volumePath = false;
176 #endif
177    strcpy(output, input);
178    TrimLSpaces(output, output);
179    TrimRSpaces(output, output);
180    MakeSystemPath(output);
181 #ifdef __WIN32__
182    if(output[0] && output[1] == ':')
183    {
184       output[1] = '_';
185       volumePath = true;
186    }
187 #endif
188    {
189       const char * chars = "*|:\",<>?";
190       char ch, * s = output, * o = output;
191       while((ch = *s++)) { if(!strchr(chars, ch)) *o++ = ch; }
192       *o = '\0';
193    }
194 #ifdef __WIN32__
195    if(volumePath && output[0])
196       output[1] = ':';
197 #endif
198 }
199
200 void RemoveTrailingPathSeparator(char *path)
201 {
202    int len;
203    len = (int)strlen(path);
204    if(len>1 && path[len-1] == DIR_SEP)
205       path[--len] = '\0';
206 }
207
208 void BasicValidatePathBoxPath(PathBox pathBox)
209 {
210    char path[MAX_LOCATION];
211    ValidPathBufCopy(path, pathBox.path);
212    RemoveTrailingPathSeparator(path);
213    pathBox.path = path;
214 }
215
216 CompilerConfig MakeDefaultCompiler(const char * name, bool readOnly)
217 {
218    CompilerConfig defaultCompiler
219    {
220       name,
221       readOnly,
222       gcc,
223       __runtimePlatform,
224       1,
225       makeDefaultCommand,
226       ecpDefaultCommand,
227       eccDefaultCommand,
228       ecsDefaultCommand,
229       earDefaultCommand,
230       cppDefaultCommand,
231       ccDefaultCommand,
232       cxxDefaultCommand,
233       arDefaultCommand
234       //ldDefaultCommand
235    };
236    incref defaultCompiler;
237    return defaultCompiler;
238 }
239
240 #ifdef SETTINGS_TEST
241 define settingsName = "ecereIDE-SettingsTest";
242 #else
243 define ideSettingsName = "ecereIDE";
244 #endif
245
246 class IDESettingsContainer : GlobalSettings
247 {
248    settingsName = ideSettingsName;
249
250    virtual void OnLoad(GlobalSettingsData data);
251
252    char moduleLocation[MAX_LOCATION];
253
254 private:
255    FileSize settingsFileSize;
256
257    IDESettingsContainer()
258    {
259       char path[MAX_LOCATION];
260       char * start;
261       LocateModule(null, moduleLocation);
262       strcpy(path, moduleLocation);
263       StripLastDirectory(moduleLocation, moduleLocation);
264       ChangeCh(moduleLocation, '\\', '/');
265       // PortableApps.com directory structure
266       if((start = strstr(path, "\\App\\EcereSDK\\bin\\ide.exe")))
267       {
268          char configFilePath[MAX_LOCATION];
269          char defaultConfigFilePath[MAX_LOCATION];
270
271          start[0] = '\0';
272
273          strcpy(configFilePath, path);
274          PathCat(configFilePath, "Data");
275          PathCat(configFilePath, ideSettingsName);
276          ChangeExtension(configFilePath, "ini", configFilePath);
277
278          strcpy(defaultConfigFilePath, path);
279          PathCat(defaultConfigFilePath, "App");
280          PathCat(defaultConfigFilePath, "DefaultData");
281          PathCat(defaultConfigFilePath, ideSettingsName);
282          ChangeExtension(defaultConfigFilePath, "ini", defaultConfigFilePath);
283
284          if(FileExists(defaultConfigFilePath))
285          {
286             if(!FileExists(configFilePath))
287             {
288                File f = FileOpen(defaultConfigFilePath, read);
289                f.CopyTo(configFilePath);
290                f.Flush();
291                delete f;
292             }
293             PathCat(path, "Data");
294             // the forced settings location will only be
295             // used if the running ide's path matches
296             // the PortableApps.com directory structure
297             // and the default ini file is found in
298             // the DefaultData directory
299             settingsLocation = path;
300             portable = true;
301          }
302       }
303    }
304
305    void OnAskReloadSettings()
306    {
307       FileSize newSettingsFileSize;
308
309       if(OpenAndLock(&newSettingsFileSize))
310       {
311          if((double)settingsFileSize - (double)newSettingsFileSize < 2048)
312             Load();
313          else
314          {
315             GuiApplication app = ((GuiApplication)__thisModule.application);
316             Window w;
317             for(w = app.desktop.firstChild; w && (!w.created || !w.visible); w = w.next);
318
319             CloseAndMonitor();
320
321             MessageBox { master = w, type = ok, isModal = true,
322                   text = "Global Settings Modified Externally",
323                   contents = "The global settings were modified by another process and a drastic shrinking of the settings file was detected.\n"
324                   "The new settings will not be loaded to prevent loss of your ide settings.\n"
325                   "Please check your settings file and make sure to save this IDE's global settings if your settings file has been compromised."
326                   }.Create();
327          }
328       }
329    }
330
331    SettingsIOResult Load()
332    {
333       SettingsIOResult result = GlobalSettings::Load();
334       IDESettings data = (IDESettings)this.data;
335       CompilerConfig defaultCompiler = null;
336       if(!data)
337       {
338          this.data = IDESettings { };
339          if(dataOwner)
340             *dataOwner = this.data;
341
342          if(result == fileNotCompatibleWithDriver)
343          {
344             bool loaded;
345             OldIDESettings oldSettings { };
346             Close();
347             loaded = oldSettings.Load() == success;
348             oldSettings.Close();
349             if(loaded)
350             {
351                data = (IDESettings)this.data;
352
353                for(c : oldSettings.compilerConfigs)
354                   data.compilerConfigs.Add(c.Copy());
355
356                for(s : oldSettings.recentFiles) data.recentFiles.Add(CopyString(s));
357                for(s : oldSettings.recentProjects) data.recentProjects.Add(CopyString(s));
358
359                data.docDir = oldSettings.docDir;
360                data.ideFileDialogLocation = oldSettings.ideFileDialogLocation;
361                data.ideProjectFileDialogLocation = oldSettings.ideProjectFileDialogLocation;
362                data.useFreeCaret = oldSettings.useFreeCaret;
363                data.showLineNumbers = oldSettings.showLineNumbers;
364                data.caretFollowsScrolling = oldSettings.caretFollowsScrolling;
365                delete data.displayDriver; data.displayDriver = CopyString(oldSettings.displayDriver);
366                data.projectDefaultTargetDir = oldSettings.projectDefaultTargetDir;
367                data.projectDefaultIntermediateObjDir = oldSettings.projectDefaultIntermediateObjDir;
368
369                Save();
370                result = success;
371             }
372             delete oldSettings;
373          }
374          if(result == fileNotFound || !data)
375          {
376             data = (IDESettings)this.data;
377             data.useFreeCaret = false; //true;
378             data.showLineNumbers = true;
379             data.caretFollowsScrolling = false; //true;
380          }
381       }
382       // Ensure we have a default compiler
383       defaultCompiler = data.GetCompilerConfig(defaultCompilerName);
384       if(!defaultCompiler)
385       {
386          defaultCompiler = MakeDefaultCompiler(defaultCompilerName, true);
387          data.compilerConfigs.Add(defaultCompiler);
388       }
389
390       // We incref the compilers below, so reset refCount to 0
391       defaultCompiler._refCount = 0;
392
393       CloseAndMonitor();
394       FileGetSize(settingsFilePath, &settingsFileSize);
395       if(data.compilerConfigs)
396       {
397          for(ccfg : data.compilerConfigs)
398          {
399             if(!ccfg.ecpCommand || !ccfg.ecpCommand[0])
400                ccfg.ecpCommand = ecpDefaultCommand;
401             if(!ccfg.eccCommand || !ccfg.eccCommand[0])
402                ccfg.eccCommand = eccDefaultCommand;
403             if(!ccfg.ecsCommand || !ccfg.ecsCommand[0])
404                ccfg.ecsCommand = ecsDefaultCommand;
405             if(!ccfg.earCommand || !ccfg.earCommand[0])
406                ccfg.earCommand = earDefaultCommand;
407             if(!ccfg.cppCommand || !ccfg.cppCommand[0])
408                ccfg.cppCommand = cppDefaultCommand;
409             if(!ccfg.ccCommand || !ccfg.ccCommand[0])
410                ccfg.ccCommand = ccDefaultCommand;
411             if(!ccfg.cxxCommand || !ccfg.cxxCommand[0])
412                ccfg.cxxCommand = cxxDefaultCommand;
413             /*if(!ccfg.ldCommand || !ccfg.ldCommand[0])
414                ccfg.ldCommand = ldDefaultCommand;*/
415             if(!ccfg.arCommand || !ccfg.arCommand[0])
416                ccfg.arCommand = arDefaultCommand;
417             if(!ccfg.objectFileExt || !ccfg.objectFileExt[0])
418                ccfg.objectFileExt = objectDefaultFileExt;
419             /*if(!ccfg.outputFileExt || !ccfg.outputFileExt[0])
420                ccfg.outputFileExt = outputDefaultFileExt;*/
421             incref ccfg;
422          }
423       }
424       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
425          data.ManagePortablePaths(moduleLocation, true);
426       data.ForcePathSeparatorStyle(true);
427       OnLoad(data);
428       return result;
429    }
430
431    SettingsIOResult Save()
432    {
433       SettingsIOResult result;
434
435       IDESettings data = (IDESettings)this.data;
436       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
437          data.ManagePortablePaths(moduleLocation, false);
438       data.ForcePathSeparatorStyle(true);
439       result = GlobalSettings::Save();
440       if(result != success)
441          PrintLn("Error saving IDE settings");
442       if(portable && moduleLocation[0] && FileExists(moduleLocation).isDirectory)
443          data.ManagePortablePaths(moduleLocation, true);
444       CloseAndMonitor();
445       FileGetSize(settingsFilePath, &settingsFileSize);
446       return result;
447    }
448 }
449
450 class IDESettings : GlobalSettingsData
451 {
452 public:
453    List<CompilerConfig> compilerConfigs { };
454    Array<String> recentFiles { };
455    Array<String> recentProjects { };
456    property const char * docDir
457    {
458       set { delete docDir; if(value && value[0]) docDir = CopyString(value); }
459       get { return docDir ? docDir : ""; }
460       isset { return docDir && docDir[0]; }
461    }
462    property const char * ideFileDialogLocation
463    {
464       set { delete ideFileDialogLocation; if(value && value[0]) ideFileDialogLocation = CopyString(value); }
465       get { return ideFileDialogLocation ? ideFileDialogLocation : ""; }
466       isset { return ideFileDialogLocation && ideFileDialogLocation[0]; }
467    }
468    property const char * ideProjectFileDialogLocation
469    {
470       set { delete ideProjectFileDialogLocation; if(value && value[0]) ideProjectFileDialogLocation = CopyString(value); }
471       get { return ideProjectFileDialogLocation ? ideProjectFileDialogLocation : ""; }
472       isset { return ideProjectFileDialogLocation && ideProjectFileDialogLocation[0]; }
473    }
474    bool useFreeCaret;
475    bool showLineNumbers;
476    bool caretFollowsScrolling;
477    char * displayDriver;
478
479    // TODO: Classify settings
480    //EditorSettings editor { };
481
482    property const char * projectDefaultTargetDir
483    {
484       set { delete projectDefaultTargetDir; if(value && value[0]) projectDefaultTargetDir = CopyValidateMakefilePath(value); }
485       get { return projectDefaultTargetDir ? projectDefaultTargetDir : ""; }
486       isset { return projectDefaultTargetDir && projectDefaultTargetDir[0]; }
487    }
488    property const char * projectDefaultIntermediateObjDir
489    {
490       set { delete projectDefaultIntermediateObjDir; if(value && value[0]) projectDefaultIntermediateObjDir = CopyValidateMakefilePath(value); }
491       get { return projectDefaultIntermediateObjDir ? projectDefaultIntermediateObjDir : ""; }
492       isset { return projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0]; }
493    }
494
495    property const char * compilerConfigsDir
496    {
497       set { delete compilerConfigsDir; if(value && value[0]) compilerConfigsDir = CopyString(value); }
498       get { return compilerConfigsDir ? compilerConfigsDir : ""; }
499       isset { return compilerConfigsDir && compilerConfigsDir[0]; }
500    }
501
502    property const char * defaultCompiler
503    {
504       set { delete defaultCompiler; if(value && value[0]) defaultCompiler = CopyString(value); }
505       get { return defaultCompiler && defaultCompiler[0] ? defaultCompiler : defaultCompilerName; }
506       isset { return defaultCompiler && defaultCompiler[0]; }
507    }
508
509    property const String language
510    {
511       set
512       {
513          delete language;
514          language = CopyString(value);
515       }
516       get { return language; }
517       isset { return language != null; }
518    }
519
520 private:
521    char * docDir;
522    char * ideFileDialogLocation;
523    char * ideProjectFileDialogLocation;
524    char * projectDefaultTargetDir;
525    char * projectDefaultIntermediateObjDir;
526    char * compilerConfigsDir;
527    char * defaultCompiler;
528    String language;
529
530    CompilerConfig GetCompilerConfig(const String compilerName)
531    {
532       const char * name = compilerName && compilerName[0] ? compilerName : defaultCompilerName;
533       CompilerConfig compilerConfig = null;
534       for(compiler : compilerConfigs)
535       {
536          if(!strcmp(compiler.name, name))
537          {
538             compilerConfig = compiler;
539             break;
540          }
541       }
542       if(!compilerConfig && compilerConfigs.count)
543          compilerConfig = compilerConfigs.firstIterator.data;
544       if(compilerConfig)
545          incref compilerConfig;
546       return compilerConfig;
547    }
548
549    ~IDESettings()
550    {
551       compilerConfigs.Free();
552       delete compilerConfigs;
553       recentFiles.Free();
554       delete recentFiles;
555       recentProjects.Free();
556       delete recentProjects;
557       delete docDir;
558
559       delete projectDefaultTargetDir;
560       delete projectDefaultIntermediateObjDir;
561       delete compilerConfigsDir;
562       delete defaultCompiler;
563       delete language;
564
565       delete ideFileDialogLocation;
566       delete ideProjectFileDialogLocation;
567       delete displayDriver;
568    }
569
570    void ForcePathSeparatorStyle(bool unixStyle)
571    {
572       char from, to;
573       if(unixStyle)
574          from = '\\', to = '/';
575       else
576          from = '/', to = '\\';
577       if(compilerConfigs && compilerConfigs.count)
578       {
579          int i;
580          for(config : compilerConfigs)
581          {
582             if(config.includeDirs && config.includeDirs.count)
583             {
584                for(i = 0; i < config.includeDirs.count; i++)
585                {
586                   if(config.includeDirs[i] && config.includeDirs[i][0])
587                      ChangeCh(config.includeDirs[i], from, to);
588                }
589             }
590             if(config.libraryDirs && config.libraryDirs.count)
591             {
592                for(i = 0; i < config.libraryDirs.count; i++)
593                {
594                   if(config.libraryDirs[i] && config.libraryDirs[i][0])
595                      ChangeCh(config.libraryDirs[i], from, to);
596                }
597             }
598             if(config.executableDirs && config.executableDirs.count)
599             {
600                for(i = 0; i < config.executableDirs.count; i++)
601                {
602                   if(config.executableDirs[i] && config.executableDirs[i][0])
603                      ChangeCh(config.executableDirs[i], from, to);
604                }
605             }
606          }
607       }
608       if(recentFiles && recentFiles.count)
609       {
610          int c;
611          for(c = 0; c < recentFiles.count; c++)
612          {
613             if(recentFiles[c] && recentFiles[c][0])
614                ChangeCh(recentFiles[c], from, to);
615          }
616       }
617       if(recentProjects && recentProjects.count)
618       {
619          int c;
620          for(c = 0; c < recentProjects.count; c++)
621          {
622             if(recentProjects[c] && recentProjects[c][0])
623                ChangeCh(recentProjects[c], from, to);
624          }
625       }
626       if(docDir && docDir[0])
627          ChangeCh(docDir, from, to);
628       if(ideFileDialogLocation && ideFileDialogLocation[0])
629          ChangeCh(ideFileDialogLocation, from, to);
630       if(ideProjectFileDialogLocation && ideProjectFileDialogLocation[0])
631          ChangeCh(ideProjectFileDialogLocation, from, to);
632
633       if(projectDefaultTargetDir && projectDefaultTargetDir[0])
634          ChangeCh(projectDefaultTargetDir, from, to);
635       if(projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0])
636          ChangeCh(projectDefaultIntermediateObjDir, from, to);
637
638       if(compilerConfigsDir && compilerConfigsDir[0])
639          ChangeCh(compilerConfigsDir, from, to);
640    }
641
642    void ManagePortablePaths(char * location, bool makeAbsolute)
643    {
644       int c;
645       if(compilerConfigs && compilerConfigs.count)
646       {
647          for(config : compilerConfigs)
648          {
649             DirTypes t;
650             for(t = 0; t < DirTypes::enumSize; t++)
651             {
652                Array<String> dirs = null;
653                if(t == executables) dirs = config.executableDirs;
654                else if(t == includes) dirs = config.includeDirs;
655                else if(t == libraries) dirs = config.libraryDirs;
656                if(dirs && dirs.count)
657                {
658                   for(c = 0; c < dirs.count; c++)
659                   {
660                      if(dirs[c] && dirs[c][0])
661                         dirs[c] = UpdatePortablePath(dirs[c], location, makeAbsolute);
662                   }
663                }
664             }
665          }
666       }
667       if(recentFiles && recentFiles.count)
668       {
669          for(c = 0; c < recentFiles.count; c++)
670          {
671             if(recentFiles[c] && recentFiles[c][0])
672                recentFiles[c] = UpdatePortablePath(recentFiles[c], location, makeAbsolute);
673          }
674       }
675       if(recentProjects && recentProjects.count)
676       {
677          for(c = 0; c < recentProjects.count; c++)
678          {
679             if(recentProjects[c] && recentProjects[c][0])
680                recentProjects[c] = UpdatePortablePath(recentProjects[c], location, makeAbsolute);
681          }
682       }
683       if(docDir && docDir[0])
684          docDir = UpdatePortablePath(docDir, location, makeAbsolute);
685       if(ideFileDialogLocation && ideFileDialogLocation[0])
686          ideFileDialogLocation = UpdatePortablePath(ideFileDialogLocation, location, makeAbsolute);
687       if(ideProjectFileDialogLocation && ideProjectFileDialogLocation[0])
688          ideProjectFileDialogLocation = UpdatePortablePath(ideProjectFileDialogLocation, location, makeAbsolute);
689
690       if(projectDefaultTargetDir && projectDefaultTargetDir[0])
691          projectDefaultTargetDir = UpdatePortablePath(projectDefaultTargetDir, location, makeAbsolute);
692       if(projectDefaultIntermediateObjDir && projectDefaultIntermediateObjDir[0])
693          projectDefaultIntermediateObjDir = UpdatePortablePath(projectDefaultIntermediateObjDir, location, makeAbsolute);
694
695       if(compilerConfigsDir && compilerConfigsDir[0])
696          compilerConfigsDir = UpdatePortablePath(compilerConfigsDir, location, makeAbsolute);
697    }
698
699    char * UpdatePortablePath(char * path, const char * location, bool makeAbsolute)
700    {
701       char * output;
702       if(makeAbsolute)
703       {
704          char p[MAX_LOCATION];
705          strcpy(p, location);
706          PathCatSlash(p, path);
707          delete path;
708          output = CopyString(p);
709       }
710       else
711       {
712          PathRelationship rel = eString_PathRelated(path, location, null);
713          if(rel == subPath || rel == identical)
714          {
715             char p[MAX_LOCATION];
716             MakePathRelative(path, location, p);
717             if(!*p) strcpy(p, "./");
718             else ChangeCh(p, '\\', '/');
719             delete path;
720             output = CopyString(p);
721          }
722          else
723             output = path;
724       }
725       return output;
726    }
727
728    void AddRecentFile(const char * fileName)
729    {
730       int c;
731       char * filePath = CopyString(fileName);
732       ChangeCh(filePath, '\\', '/');
733       for(c = 0; c<recentFiles.count; c++)
734       {
735          if(recentFiles[c] && !fstrcmp(recentFiles[c], filePath))
736          {
737             recentFiles.Delete((void *)&recentFiles[c]);
738             c--;
739          }
740       }
741       while(recentFiles.count >= MaxRecent)
742          recentFiles.Delete(recentFiles.GetLast());
743       recentFiles.Insert(null, filePath);
744       //UpdateRecentMenus(owner);
745    }
746
747    void AddRecentProject(const char * projectName)
748    {
749       int c;
750       char * filePath = CopyString(projectName);
751       ChangeCh(filePath, '\\', '/');
752       for(c = 0; c<recentProjects.count; c++)
753       {
754          if(recentProjects[c] && !fstrcmp(recentProjects[c], filePath))
755          {
756             recentProjects.Delete((void *)&recentProjects[c]);
757             c--;
758          }
759       }
760       while(recentProjects.count >= MaxRecent)
761          recentProjects.Delete(recentProjects.GetLast());
762       recentProjects.Insert(null, filePath);
763       //UpdateRecentMenus(owner);
764    }
765 }
766
767 static const char * compilerTypeNames[CompilerType] = { "GCC", "TCC", "PCC", "VS8", "VS9", "VS10" };
768 static const char * compilerTypeVersionString[CompilerType] = { "", "", "", "8.00", "9.00", "10.00" };
769 static const char * compilerTypeSolutionFileVersionString[CompilerType] = { "", "", "", "9.00", "10.00", "11.00" };
770 static const char * compilerTypeYearString[CompilerType] = { "", "", "", "2005", "2008", "2010" };
771 static const char * compilerTypeProjectFileExtension[CompilerType] = { "", "", "", "vcproj", "vcproj", "vcxproj" };
772 // TODO: i18n with Array
773 static Array<const String> compilerTypeLongNames
774 { [
775    $"GNU Compiler Collection (GCC) / GNU Make",
776    $"Tiny C Compiler / GNU Make",
777    $"Portable C Compiler / GNU Make",
778    $"Microsoft Visual Studio 2005 (8.0) Compiler",
779    $"Microsoft Visual Studio 2008 (9.0) Compiler",
780    $"Microsoft Visual Studio 2010 (10.0) Compiler"
781 ] };
782 const CompilerType firstCompilerType = gcc;
783 const CompilerType lastCompilerType = vs10;
784 public enum CompilerType
785 {
786    gcc, tcc, pcc, vs8, vs9, vs10;
787
788    property bool isVC
789    {
790       get { return this == vs8 || this == vs9 || this == vs10; }
791    }
792
793    property const char *
794    {
795       get { return OnGetString(null, null, null); }
796       set
797       {
798          if(value)
799          {
800             CompilerType c;
801             for(c = firstCompilerType; c <= lastCompilerType; c++)
802                if(!strcmpi(value, compilerTypeNames[c]))
803                   return c;
804          }
805          return gcc;
806       }
807    };
808
809    property const char * longName { get { return OnGetString(null, (void*)1, null); } };
810    property const char * versionString { get { return OnGetString(null, (void*)2, null); } };
811    property const char * yearString { get { return OnGetString(null, (void*)3, null); } };
812    property const char * projectFileExtension { get { return OnGetString(null, (void*)4, null); } };
813    property const char * solutionFileVersionString { get { return OnGetString(null, (void*)5, null); } };
814
815    const char * OnGetString(char * tempString, void * fieldData, bool * needClass)
816    {
817       if(this >= firstCompilerType && this <= lastCompilerType)
818       {
819          if(tempString)
820             strcpy(tempString, compilerTypeNames[this]);
821          if(fieldData == null)
822             return compilerTypeNames[this];
823          else if(fieldData == (void*)1)
824             return compilerTypeLongNames[this];
825          else if(fieldData == (void*)2)
826             return compilerTypeVersionString[this];
827          else if(fieldData == (void*)3)
828             return compilerTypeYearString[this];
829          else if(fieldData == (void*)4)
830             return compilerTypeProjectFileExtension[this];
831          else if(fieldData == (void*)5)
832             return compilerTypeSolutionFileVersionString[this];
833       }
834       return null;
835    }
836 };
837
838 class CompilerConfig
839 {
840    class_no_expansion;
841
842    numJobs = 1;
843 public:
844    property const char * name
845    {
846       set
847       {
848          delete name;
849          if(value)
850             name = CopyString(value);
851       }
852       get { return name; }
853    }
854    bool readOnly;
855    CompilerType type;
856    Platform targetPlatform;
857    int numJobs;
858    property const char * makeCommand
859    {
860       set { delete makeCommand; if(value && value[0]) makeCommand = CopyString(value); }
861       get { return makeCommand; }
862       isset { return makeCommand && makeCommand[0]; }
863    }
864    property const char * ecpCommand
865    {
866       set { delete ecpCommand; if(value && value[0]) ecpCommand = CopyString(value); }
867       get { return ecpCommand; }
868       isset { return ecpCommand && ecpCommand[0]; }
869    }
870    property const char * eccCommand
871    {
872       set { delete eccCommand; if(value && value[0]) eccCommand = CopyString(value); }
873       get { return eccCommand; }
874       isset { return eccCommand && eccCommand[0]; }
875    }
876    property const char * ecsCommand
877    {
878       set { delete ecsCommand; if(value && value[0]) ecsCommand = CopyString(value); }
879       get { return ecsCommand; }
880       isset { return ecsCommand && ecsCommand[0]; }
881    }
882    property const char * earCommand
883    {
884       set { delete earCommand; if(value && value[0]) earCommand = CopyString(value); }
885       get { return earCommand; }
886       isset { return earCommand && earCommand[0]; }
887    }
888    property const char * cppCommand
889    {
890       set { delete cppCommand; if(value && value[0]) cppCommand = CopyString(value); }
891       get { return cppCommand; }
892       isset { return cppCommand && cppCommand[0]; }
893    }
894    property const char * ccCommand
895    {
896       set { delete ccCommand; if(value && value[0]) ccCommand = CopyString(value); }
897       get { return ccCommand; }
898       isset { return ccCommand && ccCommand[0]; }
899    }
900    property const char * cxxCommand
901    {
902       set { delete cxxCommand; if(value && value[0]) cxxCommand = CopyString(value); }
903       get { return cxxCommand; }
904       isset { return cxxCommand && cxxCommand[0]; }
905    }
906    property const char * arCommand
907    {
908       set { delete arCommand; if(value && value[0]) arCommand = CopyString(value); }
909       get { return arCommand; }
910       isset { return arCommand && arCommand[0]; }
911    }
912    property const char * ldCommand
913    {
914       set { delete ldCommand; if(value && value[0]) ldCommand = CopyString(value); }
915       get { return ldCommand; }
916       isset { return ldCommand && ldCommand[0]; }
917    }
918    property const char * objectFileExt
919    {
920       set { delete objectFileExt; if(value && value[0]) objectFileExt = CopyString(value); }
921       get { return objectFileExt && objectFileExt[0] ? objectFileExt : objectDefaultFileExt ; }
922       isset { return objectFileExt && objectFileExt[0] && strcmp(objectFileExt, objectDefaultFileExt); }
923    }
924    property const char * outputFileExt
925    {
926       set { delete outputFileExt; if(value && value[0]) outputFileExt = CopyString(value); }
927       get { return outputFileExt; }
928       isset { return outputFileExt && outputFileExt[0]; }
929    }
930    property const char * executableLauncher
931    {
932       set { delete executableLauncher; if(value && value[0]) executableLauncher = CopyString(value); }
933       get { return executableLauncher; }
934       isset { return executableLauncher && executableLauncher[0]; }
935    }
936    // TODO: implement CompilerConfig::windresCommand
937    bool ccacheEnabled;
938    bool distccEnabled;
939    // deprecated
940    property bool supportsBitDepth { set { } get { return true; } isset { return false; } }
941
942    property const char * distccHosts
943    {
944       set { delete distccHosts; if(value && value[0]) distccHosts = CopyString(value); }
945       get { return distccHosts; }
946       isset { return distccHosts && distccHosts[0]; }
947    }
948    property const char * gnuToolchainPrefix
949    {
950       set { delete gnuToolchainPrefix; if(value && value[0]) gnuToolchainPrefix = CopyString(value); }
951       get { return gnuToolchainPrefix; }
952       isset { return gnuToolchainPrefix && gnuToolchainPrefix[0]; }
953    }
954    property const char * sysroot
955    {
956       set { delete sysroot; if(value && value[0]) sysroot = CopyString(value); }
957       get { return sysroot; }
958       isset { return sysroot && sysroot[0]; }
959    }
960    property Array<String> includeDirs
961    {
962       set
963       {
964          includeDirs.Free();
965          if(value)
966          {
967             delete includeDirs;
968             includeDirs = value;
969          }
970       }
971       get { return includeDirs; }
972       isset { return includeDirs.count != 0; }
973    }
974    property Array<String> libraryDirs
975    {
976       set
977       {
978          libraryDirs.Free();
979          if(value)
980          {
981             delete libraryDirs;
982             libraryDirs = value;
983          }
984       }
985       get { return libraryDirs; }
986       isset { return libraryDirs.count != 0; }
987    }
988    property Array<String> executableDirs
989    {
990       set
991       {
992          executableDirs.Free();
993          if(value)
994          {
995             delete executableDirs;
996             executableDirs = value;
997          }
998       }
999       get { return executableDirs; }
1000       isset { return executableDirs.count != 0; }
1001    }
1002    property Array<NamedString> environmentVars
1003    {
1004       set
1005       {
1006          environmentVars.Free();
1007          if(value)
1008          {
1009             delete environmentVars;
1010             environmentVars = value;
1011          }
1012       }
1013       get { return environmentVars; }
1014       isset { return environmentVars.count != 0; }
1015    }
1016    property Array<String> prepDirectives
1017    {
1018       set
1019       {
1020          prepDirectives.Free();
1021          if(value)
1022          {
1023             delete prepDirectives;
1024             prepDirectives = value;
1025          }
1026       }
1027       get { return prepDirectives; }
1028       isset { return prepDirectives.count != 0; }
1029    }
1030    property Array<String> excludeLibs
1031    {
1032       set
1033       {
1034          excludeLibs.Free();
1035          if(value)
1036          {
1037             delete excludeLibs;
1038             excludeLibs = value;
1039          }
1040       }
1041       get { return excludeLibs; }
1042       isset { return excludeLibs.count != 0; }
1043    }
1044    property Array<String> eCcompilerFlags
1045    {
1046       set
1047       {
1048          eCcompilerFlags.Free();
1049          if(value)
1050          {
1051             delete eCcompilerFlags;
1052             eCcompilerFlags = value;
1053          }
1054       }
1055       get { return eCcompilerFlags; }
1056       isset { return eCcompilerFlags.count != 0; }
1057    }
1058    property Array<String> compilerFlags
1059    {
1060       set
1061       {
1062          compilerFlags.Free();
1063          if(value)
1064          {
1065             delete compilerFlags;
1066             compilerFlags = value;
1067          }
1068       }
1069       get { return compilerFlags; }
1070       isset { return compilerFlags.count != 0; }
1071    }
1072    property Array<String> linkerFlags
1073    {
1074       set
1075       {
1076          linkerFlags.Free();
1077          if(value)
1078          {
1079             delete linkerFlags;
1080             linkerFlags = value;
1081          }
1082       }
1083       get { return linkerFlags; }
1084       isset { return linkerFlags.count != 0; }
1085    }
1086    // json backward compatibility
1087    property const char * gccPrefix
1088    {
1089       set { delete gnuToolchainPrefix; if(value && value[0]) gnuToolchainPrefix = CopyString(value); }
1090       get { return gnuToolchainPrefix; }
1091       isset { return false; }
1092    }
1093    property const char * execPrefixCommand
1094    {
1095       set { delete executableLauncher; if(value && value[0]) executableLauncher = CopyString(value); }
1096       get { return executableLauncher; }
1097       isset { return false; }
1098    }
1099    bool resourcesDotEar;
1100 private:
1101    Array<String> includeDirs { };
1102    Array<String> libraryDirs { };
1103    Array<String> executableDirs { };
1104    // TODO: Can JSON parse and serialize maps?
1105    //EnvironmentVariables { };
1106    Array<NamedString> environmentVars { };
1107    Array<String> prepDirectives { };
1108    Array<String> excludeLibs { };
1109    Array<String> eCcompilerFlags { };
1110    Array<String> compilerFlags { };
1111    Array<String> linkerFlags { };
1112    char * name;
1113    char * makeCommand;
1114    char * ecpCommand;
1115    char * eccCommand;
1116    char * ecsCommand;
1117    char * earCommand;
1118    char * cppCommand;
1119    char * ccCommand;
1120    char * cxxCommand;
1121    char * ldCommand;
1122    char * arCommand;
1123    char * objectFileExt;
1124    char * outputFileExt;
1125    char * executableLauncher;
1126    char * distccHosts;
1127    char * gnuToolchainPrefix;
1128    char * sysroot;
1129    /*union
1130    {
1131       struct { Array<String> includes, libraries, executables; };
1132       Array<String> dirs[DirTypes];
1133    }*/
1134
1135    ~CompilerConfig()
1136    {
1137       delete name;
1138       delete ecpCommand;
1139       delete eccCommand;
1140       delete ecsCommand;
1141       delete earCommand;
1142       delete cppCommand;
1143       delete ccCommand;
1144       delete cxxCommand;
1145       delete ldCommand;
1146       delete arCommand;
1147       delete objectFileExt;
1148       delete outputFileExt;
1149       delete makeCommand;
1150       delete executableLauncher;
1151       delete distccHosts;
1152       delete gnuToolchainPrefix;
1153       delete sysroot;
1154       if(environmentVars) environmentVars.Free();
1155       if(includeDirs) { includeDirs.Free(); }
1156       if(libraryDirs) { libraryDirs.Free(); }
1157       if(executableDirs) { executableDirs.Free(); }
1158       if(prepDirectives) { prepDirectives.Free(); }
1159       if(excludeLibs) { excludeLibs.Free(); }
1160       if(compilerFlags) { compilerFlags.Free(); }
1161       if(eCcompilerFlags) { eCcompilerFlags.Free(); }
1162       if(linkerFlags) { linkerFlags.Free(); }
1163    }
1164
1165 public:
1166    CompilerConfig Copy()
1167    {
1168       CompilerConfig copy
1169       {
1170          name,
1171          readOnly,
1172          type,
1173          targetPlatform,
1174          numJobs,
1175          makeCommand,
1176          ecpCommand,
1177          eccCommand,
1178          ecsCommand,
1179          earCommand,
1180          cppCommand,
1181          ccCommand,
1182          cxxCommand,
1183          arCommand,
1184          ldCommand,
1185          objectFileExt,
1186          outputFileExt,
1187          executableLauncher,
1188          ccacheEnabled,
1189          distccEnabled,
1190          false,
1191          distccHosts,
1192          gnuToolchainPrefix,
1193          sysroot
1194       };
1195       for(s : includeDirs) copy.includeDirs.Add(CopyString(s));
1196       for(s : libraryDirs) copy.libraryDirs.Add(CopyString(s));
1197       for(s : executableDirs) copy.executableDirs.Add(CopyString(s));
1198       for(ns : environmentVars) copy.environmentVars.Add(NamedString { name = ns.name, string = ns.string });
1199       for(s : prepDirectives) copy.prepDirectives.Add(CopyString(s));
1200       for(s : excludeLibs) copy.excludeLibs.Add(CopyString(s));
1201       for(s : compilerFlags) copy.compilerFlags.Add(CopyString(s));
1202       for(s : eCcompilerFlags) copy.eCcompilerFlags.Add(CopyString(s));
1203       for(s : linkerFlags) copy.linkerFlags.Add(CopyString(s));
1204
1205       incref copy;
1206       return copy;
1207    }
1208 }
1209
1210 struct LanguageOption
1211 {
1212    const String name;
1213    const String bitmap;
1214    const String code;
1215    BitmapResource res;
1216
1217    const char * OnGetString(char * tempString, void * fieldData, bool * needClass)
1218    {
1219       return name;
1220    }
1221
1222    void OnDisplay(Surface surface, int x, int y, int width, void * data, Alignment alignment, DataDisplayFlags flags)
1223    {
1224       Bitmap icon = res ? res.bitmap : null;
1225       int w = 8 + 16;
1226       if(icon)
1227          surface.Blit(icon, x + (16 - icon.width) / 2,y+2,0,0, icon.width, icon.height);
1228       class::OnDisplay(surface, x + w, y, width - w, null, alignment, flags);
1229    }
1230 };
1231
1232 Array<LanguageOption> languages
1233 { [
1234    { "English",            ":countryCode/gb.png", "" },
1235    { "汉语",                ":countryCode/cn.png", "zh_CN" },
1236    { "Español",            ":countryCode/es.png", "es" },
1237    { "Português (Brazil)", ":countryCode/br.png", "pt_BR" },
1238    { "Русский (43%)",      ":countryCode/ru.png", "ru" },
1239    { "Nederlandse (13%)",  ":countryCode/nl.png", "nl" },
1240    { "Tiếng Việt (12%)",   ":countryCode/vn.png", "vi" },
1241    { "मराठी (10%)",          ":countryCode/in.png", "mr" },
1242    { "Hebrew (8%)",        ":countryCode/il.png", "he" },
1243    { "Magyar (8%)",        ":countryCode/hu.png", "hu" }
1244 ] };
1245
1246 const String GetLanguageString()
1247 {
1248    char * dot, * colon;
1249    static char lang[256];
1250    const String language = getenv("ECERE_LANGUAGE");
1251    if(!language) language = getenv("LANGUAGE");
1252    if(!language) language = getenv("LC_ALL");
1253    if(!language) language = getenv("LC_MESSAGES");
1254    if(!language) language = getenv("LANG");
1255    if(!language) language = "";
1256    if(language && (colon = strchr(language, ':')))
1257    {
1258       if(lang != language)
1259          strncpy(lang, language, sizeof(lang));
1260       lang[sizeof(lang)-1] = 0;
1261       lang[colon - language] = 0;
1262       language = lang;
1263    }
1264    if(language && (dot = strchr(language, '.')))
1265    {
1266       if(lang != language)
1267          strncpy(lang, language, sizeof(lang));
1268       lang[sizeof(lang)-1] = 0;
1269       lang[dot - language] = 0;
1270       language = lang;
1271    }
1272    return language;
1273 }
1274
1275 bool LanguageRestart(const char * code, Application app, IDESettings settings, IDESettingsContainer settingsContainer, Window ide, Window projectView, bool wait)
1276 {
1277    bool restart = true;
1278    String command = null;
1279    int arg0Len = (int)strlen(app.argv[0]);
1280    int len = arg0Len;
1281    int j;
1282    char ch;
1283
1284    if(ide)
1285    {
1286       Window w;
1287
1288       if(projectView)
1289       {
1290          Window w;
1291          for(w = ide.firstChild; w; w = w.next)
1292          {
1293             if(w.isActiveClient && w.isDocument)
1294             {
1295                if(!w.CloseConfirmation(true))
1296                {
1297                   restart = false;
1298                   break;
1299                }
1300             }
1301          }
1302          if(restart)
1303          {
1304             if(!projectView.CloseConfirmation(true))
1305                restart = false;
1306             if(projectView.fileName)
1307             {
1308                const char * name = projectView.fileName;
1309                if(name)
1310                {
1311                   for(j = 0; (ch = name[j]); j++)
1312                      len += (ch == ' ' || ch == '\"' || ch == '&' || ch == '$' || ch == '(' || ch == ')') ? 2 : 1;
1313                }
1314             }
1315
1316             command = new char[len + 1];
1317
1318             strcpy(command, app.argv[0]);
1319             len = arg0Len;
1320             if(projectView.fileName)
1321             {
1322                strcat(command, " ");
1323                len++;
1324                ReplaceSpaces(command + len, projectView.fileName);
1325             }
1326          }
1327          if(restart)
1328          {
1329             for(w = ide.firstChild; w; w = w.next)
1330                if(w.isActiveClient && w.isDocument)
1331                   w.modifiedDocument = false;
1332             projectView.modifiedDocument = false;
1333          }
1334       }
1335       else
1336       {
1337          for(w = ide.firstChild; w; w = w.next)
1338          {
1339             if(w.isActiveClient && w.isDocument)
1340             {
1341                if(!w.CloseConfirmation(true))
1342                {
1343                   restart = false;
1344                   break;
1345                }
1346                if(w.fileName)
1347                {
1348                   const char * name = w.fileName;
1349                   len++;
1350                   for(j = 0; (ch = name[j]); j++)
1351                      len += (ch == ' ' || ch == '\"' || ch == '&' || ch == '$' || ch == '(' || ch == ')') ? 2 : 1;
1352                }
1353             }
1354          }
1355
1356          if(restart)
1357          {
1358             command = new char[len + 1];
1359             strcpy(command, app.argv[0]);
1360             len = arg0Len;
1361
1362             for(w = ide.firstChild; w; w = w.next)
1363             {
1364                if(w.isActiveClient && w.isDocument)
1365                {
1366                   const char * name = w.fileName;
1367                   if(name)
1368                   {
1369                      strcat(command, " ");
1370                      len++;
1371                      ReplaceSpaces(command + len, name);
1372                      len = (int)strlen(command);
1373                   }
1374                }
1375             }
1376          }
1377          if(restart)
1378          {
1379             for(w = ide.firstChild; w; w = w.next)
1380                if(w.isActiveClient && w.isDocument)
1381                   w.modifiedDocument = false;
1382          }
1383       }
1384       if(restart)
1385       {
1386          settings.language = code;
1387          settingsContainer.Save();
1388
1389 #if defined(__WIN32__)
1390          // Set LANGUAGE environment variable
1391          {
1392             HKEY key = null;
1393             uint16 wLanguage[256];
1394             DWORD status;
1395             wLanguage[0] = 0;
1396
1397             RegCreateKeyEx(HKEY_CURRENT_USER, "Environment", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1398             if(key)
1399             {
1400                UTF8toUTF16Buffer(code, wLanguage, sizeof(wLanguage) / sizeof(uint16));
1401                RegSetValueExW(key, L"ECERE_LANGUAGE", 0, REG_EXPAND_SZ, (byte *)wLanguage, (uint)(wcslen(wLanguage)+1) * 2);
1402                RegCloseKey(key);
1403             }
1404          }
1405 #endif
1406
1407          if(eClass_IsDerived(app._class, class(GuiApplication)))
1408          {
1409             GuiApplication guiApp = (GuiApplication)app;
1410             guiApp.desktop.Destroy(0);
1411          }
1412       }
1413    }
1414    else
1415    {
1416       int i;
1417       for(i = 1; i < app.argc; i++)
1418       {
1419          const char * arg = app.argv[i];
1420          len++;
1421          for(j = 0; (ch = arg[j]); j++)
1422             len += (ch == ' ' || ch == '\"' || ch == '&' || ch == '$' || ch == '(' || ch == ')') ? 2 : 1;
1423       }
1424
1425       command = new char[len + 1];
1426       strcpy(command, app.argv[0]);
1427       len = arg0Len;
1428       for(i = 1; i < app.argc; i++)
1429       {
1430          strcat(command, " ");
1431          len++;
1432          ReplaceSpaces(command + len, app.argv[i]);
1433          len = (int)strlen(command);
1434       }
1435    }
1436
1437    if(restart)
1438    {
1439       SetEnvironment("ECERE_LANGUAGE", code);
1440       if(wait)
1441          ExecuteWait(command);
1442       else
1443          Execute(command);
1444    }
1445    delete command;
1446    return restart;
1447 }