buildsystem,makefiles,tarball: improve distclean targets to remove .configs/, *.ews...
[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 private:
1100    Array<String> includeDirs { };
1101    Array<String> libraryDirs { };
1102    Array<String> executableDirs { };
1103    // TODO: Can JSON parse and serialize maps?
1104    //EnvironmentVariables { };
1105    Array<NamedString> environmentVars { };
1106    Array<String> prepDirectives { };
1107    Array<String> excludeLibs { };
1108    Array<String> eCcompilerFlags { };
1109    Array<String> compilerFlags { };
1110    Array<String> linkerFlags { };
1111    char * name;
1112    char * makeCommand;
1113    char * ecpCommand;
1114    char * eccCommand;
1115    char * ecsCommand;
1116    char * earCommand;
1117    char * cppCommand;
1118    char * ccCommand;
1119    char * cxxCommand;
1120    char * ldCommand;
1121    char * arCommand;
1122    char * objectFileExt;
1123    char * outputFileExt;
1124    char * executableLauncher;
1125    char * distccHosts;
1126    char * gnuToolchainPrefix;
1127    char * sysroot;
1128    /*union
1129    {
1130       struct { Array<String> includes, libraries, executables; };
1131       Array<String> dirs[DirTypes];
1132    }*/
1133
1134    ~CompilerConfig()
1135    {
1136       delete name;
1137       delete ecpCommand;
1138       delete eccCommand;
1139       delete ecsCommand;
1140       delete earCommand;
1141       delete cppCommand;
1142       delete ccCommand;
1143       delete cxxCommand;
1144       delete ldCommand;
1145       delete arCommand;
1146       delete objectFileExt;
1147       delete outputFileExt;
1148       delete makeCommand;
1149       delete executableLauncher;
1150       delete distccHosts;
1151       delete gnuToolchainPrefix;
1152       delete sysroot;
1153       if(environmentVars) environmentVars.Free();
1154       if(includeDirs) { includeDirs.Free(); }
1155       if(libraryDirs) { libraryDirs.Free(); }
1156       if(executableDirs) { executableDirs.Free(); }
1157       if(prepDirectives) { prepDirectives.Free(); }
1158       if(excludeLibs) { excludeLibs.Free(); }
1159       if(compilerFlags) { compilerFlags.Free(); }
1160       if(eCcompilerFlags) { eCcompilerFlags.Free(); }
1161       if(linkerFlags) { linkerFlags.Free(); }
1162    }
1163
1164 public:
1165    CompilerConfig Copy()
1166    {
1167       CompilerConfig copy
1168       {
1169          name,
1170          readOnly,
1171          type,
1172          targetPlatform,
1173          numJobs,
1174          makeCommand,
1175          ecpCommand,
1176          eccCommand,
1177          ecsCommand,
1178          earCommand,
1179          cppCommand,
1180          ccCommand,
1181          cxxCommand,
1182          arCommand,
1183          ldCommand,
1184          objectFileExt,
1185          outputFileExt,
1186          executableLauncher,
1187          ccacheEnabled,
1188          distccEnabled,
1189          false,
1190          distccHosts,
1191          gnuToolchainPrefix,
1192          sysroot
1193       };
1194       for(s : includeDirs) copy.includeDirs.Add(CopyString(s));
1195       for(s : libraryDirs) copy.libraryDirs.Add(CopyString(s));
1196       for(s : executableDirs) copy.executableDirs.Add(CopyString(s));
1197       for(ns : environmentVars) copy.environmentVars.Add(NamedString { name = ns.name, string = ns.string });
1198       for(s : prepDirectives) copy.prepDirectives.Add(CopyString(s));
1199       for(s : excludeLibs) copy.excludeLibs.Add(CopyString(s));
1200       for(s : compilerFlags) copy.compilerFlags.Add(CopyString(s));
1201       for(s : eCcompilerFlags) copy.eCcompilerFlags.Add(CopyString(s));
1202       for(s : linkerFlags) copy.linkerFlags.Add(CopyString(s));
1203
1204       incref copy;
1205       return copy;
1206    }
1207 }
1208
1209 struct LanguageOption
1210 {
1211    const String name;
1212    const String bitmap;
1213    const String code;
1214    BitmapResource res;
1215
1216    const char * OnGetString(char * tempString, void * fieldData, bool * needClass)
1217    {
1218       return name;
1219    }
1220
1221    void OnDisplay(Surface surface, int x, int y, int width, void * data, Alignment alignment, DataDisplayFlags flags)
1222    {
1223       Bitmap icon = res ? res.bitmap : null;
1224       int w = 8 + 16;
1225       if(icon)
1226          surface.Blit(icon, x + (16 - icon.width) / 2,y+2,0,0, icon.width, icon.height);
1227       class::OnDisplay(surface, x + w, y, width - w, null, alignment, flags);
1228    }
1229 };
1230
1231 Array<LanguageOption> languages
1232 { [
1233    { "English",            ":countryCode/gb.png", "" },
1234    { "汉语",                ":countryCode/cn.png", "zh_CN" },
1235    { "Español",            ":countryCode/es.png", "es" },
1236    { "Português (Brazil)", ":countryCode/br.png", "pt_BR" },
1237    { "Русский (43%)",      ":countryCode/ru.png", "ru" },
1238    { "Nederlandse (13%)",  ":countryCode/nl.png", "nl" },
1239    { "Tiếng Việt (12%)",   ":countryCode/vn.png", "vi" },
1240    { "मराठी (10%)",          ":countryCode/in.png", "mr" },
1241    { "Hebrew (8%)",        ":countryCode/il.png", "he" },
1242    { "Magyar (8%)",        ":countryCode/hu.png", "hu" }
1243 ] };
1244
1245 const String GetLanguageString()
1246 {
1247    char * dot, * colon;
1248    static char lang[256];
1249    const String language = getenv("ECERE_LANGUAGE");
1250    if(!language) language = getenv("LANGUAGE");
1251    if(!language) language = getenv("LC_ALL");
1252    if(!language) language = getenv("LC_MESSAGES");
1253    if(!language) language = getenv("LANG");
1254    if(!language) language = "";
1255    if(language && (colon = strchr(language, ':')))
1256    {
1257       if(lang != language)
1258          strncpy(lang, language, sizeof(lang));
1259       lang[sizeof(lang)-1] = 0;
1260       lang[colon - language] = 0;
1261       language = lang;
1262    }
1263    if(language && (dot = strchr(language, '.')))
1264    {
1265       if(lang != language)
1266          strncpy(lang, language, sizeof(lang));
1267       lang[sizeof(lang)-1] = 0;
1268       lang[dot - language] = 0;
1269       language = lang;
1270    }
1271    return language;
1272 }
1273
1274 bool LanguageRestart(const char * code, Application app, IDESettings settings, IDESettingsContainer settingsContainer, Window ide, Window projectView, bool wait)
1275 {
1276    bool restart = true;
1277    String command = null;
1278    int arg0Len = (int)strlen(app.argv[0]);
1279    int len = arg0Len;
1280    int j;
1281    char ch;
1282
1283    if(ide)
1284    {
1285       Window w;
1286
1287       if(projectView)
1288       {
1289          Window w;
1290          for(w = ide.firstChild; w; w = w.next)
1291          {
1292             if(w.isActiveClient && w.isDocument)
1293             {
1294                if(!w.CloseConfirmation(true))
1295                {
1296                   restart = false;
1297                   break;
1298                }
1299             }
1300          }
1301          if(restart)
1302          {
1303             if(!projectView.CloseConfirmation(true))
1304                restart = false;
1305             if(projectView.fileName)
1306             {
1307                const char * name = projectView.fileName;
1308                if(name)
1309                {
1310                   for(j = 0; (ch = name[j]); j++)
1311                      len += (ch == ' ' || ch == '\"' || ch == '&' || ch == '$' || ch == '(' || ch == ')') ? 2 : 1;
1312                }
1313             }
1314
1315             command = new char[len + 1];
1316
1317             strcpy(command, app.argv[0]);
1318             len = arg0Len;
1319             if(projectView.fileName)
1320             {
1321                strcat(command, " ");
1322                len++;
1323                ReplaceSpaces(command + len, projectView.fileName);
1324             }
1325          }
1326          if(restart)
1327          {
1328             for(w = ide.firstChild; w; w = w.next)
1329                if(w.isActiveClient && w.isDocument)
1330                   w.modifiedDocument = false;
1331             projectView.modifiedDocument = false;
1332          }
1333       }
1334       else
1335       {
1336          for(w = ide.firstChild; w; w = w.next)
1337          {
1338             if(w.isActiveClient && w.isDocument)
1339             {
1340                if(!w.CloseConfirmation(true))
1341                {
1342                   restart = false;
1343                   break;
1344                }
1345                if(w.fileName)
1346                {
1347                   const char * name = w.fileName;
1348                   len++;
1349                   for(j = 0; (ch = name[j]); j++)
1350                      len += (ch == ' ' || ch == '\"' || ch == '&' || ch == '$' || ch == '(' || ch == ')') ? 2 : 1;
1351                }
1352             }
1353          }
1354
1355          if(restart)
1356          {
1357             command = new char[len + 1];
1358             strcpy(command, app.argv[0]);
1359             len = arg0Len;
1360
1361             for(w = ide.firstChild; w; w = w.next)
1362             {
1363                if(w.isActiveClient && w.isDocument)
1364                {
1365                   const char * name = w.fileName;
1366                   if(name)
1367                   {
1368                      strcat(command, " ");
1369                      len++;
1370                      ReplaceSpaces(command + len, name);
1371                      len = (int)strlen(command);
1372                   }
1373                }
1374             }
1375          }
1376          if(restart)
1377          {
1378             for(w = ide.firstChild; w; w = w.next)
1379                if(w.isActiveClient && w.isDocument)
1380                   w.modifiedDocument = false;
1381          }
1382       }
1383       if(restart)
1384       {
1385          settings.language = code;
1386          settingsContainer.Save();
1387
1388 #if defined(__WIN32__)
1389          // Set LANGUAGE environment variable
1390          {
1391             HKEY key = null;
1392             uint16 wLanguage[256];
1393             DWORD status;
1394             wLanguage[0] = 0;
1395
1396             RegCreateKeyEx(HKEY_CURRENT_USER, "Environment", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, null, &key, &status);
1397             if(key)
1398             {
1399                UTF8toUTF16Buffer(code, wLanguage, sizeof(wLanguage) / sizeof(uint16));
1400                RegSetValueExW(key, L"ECERE_LANGUAGE", 0, REG_EXPAND_SZ, (byte *)wLanguage, (uint)(wcslen(wLanguage)+1) * 2);
1401                RegCloseKey(key);
1402             }
1403          }
1404 #endif
1405
1406          if(eClass_IsDerived(app._class, class(GuiApplication)))
1407          {
1408             GuiApplication guiApp = (GuiApplication)app;
1409             guiApp.desktop.Destroy(0);
1410          }
1411       }
1412    }
1413    else
1414    {
1415       int i;
1416       for(i = 1; i < app.argc; i++)
1417       {
1418          const char * arg = app.argv[i];
1419          len++;
1420          for(j = 0; (ch = arg[j]); j++)
1421             len += (ch == ' ' || ch == '\"' || ch == '&' || ch == '$' || ch == '(' || ch == ')') ? 2 : 1;
1422       }
1423
1424       command = new char[len + 1];
1425       strcpy(command, app.argv[0]);
1426       len = arg0Len;
1427       for(i = 1; i < app.argc; i++)
1428       {
1429          strcat(command, " ");
1430          len++;
1431          ReplaceSpaces(command + len, app.argv[i]);
1432          len = (int)strlen(command);
1433       }
1434    }
1435
1436    if(restart)
1437    {
1438       SetEnvironment("ECERE_LANGUAGE", code);
1439       if(wait)
1440          ExecuteWait(command);
1441       else
1442          Execute(command);
1443    }
1444    delete command;
1445    return restart;
1446 }