1 #include "Core.h"
2
IsUppValueChar(int c)3 bool IsUppValueChar(int c)
4 {
5 return c > ' ' && c != ',' && c != ';';
6 }
7
ReadValue(CParser & p)8 String ReadValue(CParser& p)
9 {
10 p.Spaces();
11 if(p.IsString())
12 return p.ReadOneString();
13 StringBuffer v;
14 while(IsUppValueChar(p.PeekChar()))
15 v.Cat(p.GetChar());
16 p.Spaces();
17 return String(v);
18 }
19
20 static bool sMatchOr(CParser& p, const Vector<String>& flag);
21
sMatchFlag(CParser & p,const Vector<String> & flag)22 static bool sMatchFlag(CParser& p, const Vector<String>& flag)
23 {
24 if(p.Char('!'))
25 return !sMatchFlag(p, flag);
26 if(p.Char('(')) {
27 bool b = sMatchOr(p, flag);
28 p.PassChar(')');
29 return b;
30 }
31 if(p.IsEof())
32 return true;
33 return FindIndex(flag, p.ReadId()) >= 0;
34 }
35
sMatchAnd(CParser & p,const Vector<String> & flag)36 static bool sMatchAnd(CParser& p, const Vector<String>& flag)
37 {
38 bool b = sMatchFlag(p, flag);
39 while(p.IsId() || p.IsChar('!') || p.IsChar('(') || p.Char2('&', '&') || p.Char('&'))
40 b = sMatchFlag(p, flag) && b;
41 return b;
42 }
43
sMatchOr(CParser & p,const Vector<String> & flag)44 static bool sMatchOr(CParser& p, const Vector<String>& flag)
45 {
46 bool b = sMatchAnd(p, flag);
47 while(p.Char2('|', '|') || p.Char('|'))
48 b = sMatchFlag(p, flag) || b;
49 return b;
50 }
51
MatchWhen_X(const String & when,const Vector<String> & flag)52 bool MatchWhen_X(const String& when, const Vector<String>& flag)
53 {
54 CParser p(when);
55 bool b = sMatchOr(p, flag);
56 if(!p.IsEof())
57 p.ThrowError("expected end of expression");
58 return b;
59 }
60
MatchWhen(const String & when,const Vector<String> & flag)61 bool MatchWhen(const String& when, const Vector<String>& flag)
62 {
63 try {
64 return MatchWhen_X(when, flag);
65 }
66 catch(CParser::Error e) {
67 PutConsole(String().Cat()
68 << "Invalid When expression: " << AsCString(when) << ": " << e);
69 return false;
70 }
71 }
72
ReadWhen(CParser & p)73 String ReadWhen(CParser& p) {
74 String when;
75 if(p.Char('(')) {
76 if(p.IsString())
77 when = p.ReadString();
78 else {
79 const char *b = p.GetPtr();
80 int lvl = 0;
81 for(;;) {
82 if(p.IsEof() || lvl == 0 && p.IsChar(')'))
83 break;
84 if(p.Char('('))
85 lvl++;
86 else
87 if(p.Char(')'))
88 lvl--;
89 else
90 p.SkipTerm();
91 }
92 when = String(b, p.GetPtr());
93 p.Char(')');
94 }
95 }
96 return when;
97 }
98
AsStringWhen(const String & when)99 String AsStringWhen(const String& when) {
100 String out;
101 try {
102 Vector<String> x;
103 MatchWhen_X(when, x);
104 out << '(' << when << ')';
105 }
106 catch(CParser::Error) {
107 out << '(' << AsCString(when) << ')';
108 }
109 return out;
110 }
111
AsString() const112 String CustomStep::AsString() const {
113 return "custom" + AsStringWhen(when) + ' ' + AsCString(ext) + ",\n\t" +
114 AsCString(command, 70, "\t") + ",\n\t" +
115 AsCString(output, 70, "\t") + ";\n\n";
116 }
117
Load(CParser & p)118 void CustomStep::Load(CParser& p)
119 {
120 when = ReadWhen(p);
121 ext = p.ReadString();
122 p.PassChar(',');
123 command = p.ReadString();
124 p.PassChar(',');
125 output = p.ReadString();
126 p.PassChar(';');
127 }
128
GetExt() const129 String CustomStep::GetExt() const {
130 return ext[0] != '.' ? "." + ToLower(ext) : ToLower(ext);
131 }
132
MatchExt(const char * fn) const133 bool CustomStep::MatchExt(const char *fn) const {
134 return ToLower(GetFileExt(fn)) == GetExt();
135 }
136
LoadOpt(CParser & p,const char * key,Array<OptItem> & v)137 bool LoadOpt(CParser& p, const char *key, Array<OptItem>& v) {
138 if(p.Id(key)) {
139 String when = ReadWhen(p);
140 do {
141 OptItem& m = v.Add();
142 m.when = when;
143 m.text = ReadValue(p);
144 }
145 while(p.Char(','));
146 return true;
147 }
148 return false;
149 }
150
LoadFOpt(CParser & p,const char * key,Array<OptItem> & v)151 bool LoadFOpt(CParser& p, const char *key, Array<OptItem>& v) {
152 if(p.Id(key)) {
153 OptItem& m = v.Add();
154 m.when = ReadWhen(p);
155 m.text = ReadValue(p);
156 return true;
157 }
158 return false;
159 }
160
Reset()161 void Package::Reset()
162 {
163 charset = 0;
164 noblitz = nowarnings = false;
165 bold = italic = false;
166 ink = Null;
167 spellcheck_comments = Null;
168 tabsize = Null;
169 }
170
Package()171 Package::Package()
172 {
173 Reset();
174 }
175
StdResolver(const String & error,const String & path,int line)176 bool StdResolver(const String& error, const String& path, int line)
177 {
178 PutConsole("Invalid package: " + path);
179 exit(1);
180 return false;
181 }
182
183 static bool (*sResolve)(const String& error, const String& path, int line) = StdResolver;
184
SetPackageResolver(bool (* Resolve)(const String & error,const String & path,int line))185 void Package::SetPackageResolver(bool (*Resolve)(const String& error, const String& path, int line))
186 {
187 sResolve = Resolve;
188 }
189
CharsetByNameX(const String & s)190 byte CharsetByNameX(const String& s)
191 {
192 return decode(s, "UTF-8-BOM", CHARSET_UTF8_BOM,
193 "UTF-16-LE", CHARSET_UTF16_LE,
194 "UTF-16-BE", CHARSET_UTF16_BE,
195 "UTF-16-LE-BOM", CHARSET_UTF16_LE_BOM,
196 "UTF-16-BE-BOM", CHARSET_UTF16_BE_BOM,
197 CharsetByName(s));
198 }
199
Option(bool & option,const char * name)200 void Package::Option(bool& option, const char *name)
201 {
202 File& f = file.Top();
203 for(int i = 0; i < f.option.GetCount(); i++) // Ugly BW compatibility hack
204 if(f.option[i].text == name) {
205 f.option.Remove(i);
206 option = true;
207 break;
208 }
209 }
210
Load(const char * path)211 bool Package::Load(const char *path)
212 {
213 for(;;) {
214 Reset();
215 library.Clear();
216 static_library.Clear();
217 target.Clear();
218 flag.Clear();
219 option.Clear();
220 link.Clear();
221 uses.Clear();
222 include.Clear();
223 pkg_config.Clear();
224 accepts.Clear();
225 file.Clear();
226 config.Clear();
227 custom.Clear();
228 description.Clear();
229 String f = LoadFile(path);
230 time = FileGetTime(path);
231 if(IsNull(time))
232 return false;
233 CParser p(f);
234 p.NoSkipComments(); // allow file path like //home/user/project/something.cpp
235 try {
236 while(!p.IsEof()) {
237 if(!LoadOpt(p, "options", option) &&
238 !LoadOpt(p, "link", link) &&
239 !LoadOpt(p, "library", library) &&
240 !LoadOpt(p, "static_library", static_library) &&
241 !LoadOpt(p, "flags", flag) &&
242 !LoadOpt(p, "target", target) &&
243 !LoadOpt(p, "uses", uses) &&
244 !LoadOpt(p, "include", include) &&
245 !LoadOpt(p, "pkg_config", pkg_config)) {
246 if(p.Id("charset"))
247 charset = CharsetByNameX(p.ReadString());
248 else
249 if(p.Id("tabsize"))
250 tabsize = minmax(p.ReadInt(), 1, 20);
251 else
252 if(p.Id("description")) {
253 description = p.ReadString();
254 const char *q = strchr(description, 255);
255 ink = Null;
256 bold = italic = false;
257 if(q) {
258 CParser p(q + 1);
259 bold = p.Char('B');
260 italic = p.Char('I');
261 if(p.IsNumber()) {
262 RGBA c = Black();
263 c.r = p.ReadInt();
264 p.Char(',');
265 if(p.IsNumber())
266 c.g = p.ReadInt();
267 p.Char(',');
268 if(p.IsNumber())
269 c.b = p.ReadInt();
270 ink = c;
271 }
272 description = String(~description, q);
273 }
274 }
275 else
276 if(p.Id("acceptflags")) {
277 do
278 accepts.Add(ReadValue(p));
279 while(p.Char(','));
280 }
281 else
282 if(p.Id("noblitz"))
283 noblitz = true;
284 else
285 if(p.Id("optimize_speed"))
286 ; // Legacy option ignored
287 else
288 if(p.Id("optimize_size"))
289 ; // Legacy option ignored
290 else
291 if(p.Id("file")) {
292 do {
293 File fv(ReadValue(p));
294 if(fv.GetCount()) {
295 File& f = file.Add();
296 f = pick(fv);
297 while(!p.IsChar(',') && !p.IsChar(';') && !p.IsEof()) {
298 if(!LoadFOpt(p, "options", f.option) &&
299 !LoadFOpt(p, "depends", f.depends)) {
300 if(p.Id("optimize_speed"))
301 ; // Legacy option ignored
302 else
303 if(p.Id("optimize_size"))
304 ; // Legacy option ignored
305 else
306 if(p.Id("pch"))
307 f.pch = true;
308 else
309 if(p.Id("nopch"))
310 f.nopch = true;
311 else
312 if(p.Id("noblitz"))
313 f.noblitz = true;
314 else
315 if(p.Id("readonly"))
316 f.readonly = true;
317 else
318 if(p.Id("separator"))
319 f.separator = true;
320 else
321 if(p.Id("charset"))
322 f.charset = CharsetByNameX(p.ReadString());
323 else
324 if(p.Id("tabsize"))
325 f.tabsize = minmax(p.ReadInt(), 1, 20);
326 else
327 if(p.Id("font"))
328 f.font = minmax(p.ReadInt(), 0, 3);
329 else
330 if(p.Id("highlight"))
331 f.highlight = p.ReadId();
332 else
333 if(p.Id("spellcheck_comments"))
334 f.spellcheck_comments = LNGFromText(p.ReadString());
335 else
336 p.SkipTerm();
337 }
338 }
339 Option(f.pch, "PCH");
340 Option(f.nopch, "NOPCH");
341 Option(f.noblitz, "NOBLITZ");
342 }
343 }
344 while(p.Char(','));
345 }
346 else
347 if(p.Id("mainconfig")) {
348 do {
349 String c = p.ReadString();
350 p.Char('=');
351 p.Id("external"); // Backward compatibility...
352 p.Id("console");
353 p.Id("remotelinux");
354 p.Id("normal");
355 String f = p.ReadString();
356 Config& cf = config.Add();
357 cf.name = c;
358 cf.param = f;
359 }
360 while(p.Char(','));
361 }
362 else
363 if(p.Id("custom"))
364 custom.Add().Load(p);
365 else
366 if(p.Id("spellcheck_comments"))
367 spellcheck_comments = LNGFromText(p.ReadString());
368 else
369 p.SkipTerm();
370 }
371 p.Char(';');
372 }
373 for(int i = 0; i < option.GetCount(); i++)
374 if(option[i].when == "BUILDER_OPTION" && option[i].text == "NOWARNINGS") {
375 nowarnings = true;
376 option.Remove(i);
377 break;
378 }
379 return true;
380 }
381 catch(CParser::Error error) {
382 if(sResolve(error, path, p.GetLine() - 1))
383 return false;
384 Save(path);
385 return true;
386 }
387 }
388 }
389
WriteValue(const String & x)390 String WriteValue(const String& x) {
391 for(const char *s = x; s < x.End(); s++)
392 if(!IsUppValueChar(*s))
393 return AsCString(x);
394 return x;
395 }
396
putopt(Stream & out,const char * key,const Array<OptItem> & m)397 void putopt(Stream& out, const char *key, const Array<OptItem>& m) {
398 bool was = false;
399 for(int i = 0; i < m.GetCount(); i++)
400 if(IsNull(m[i].when)) {
401 if(was)
402 out << ",\n\t";
403 else
404 out << key << "\n\t";
405 out << WriteValue(m[i].text);
406 was = true;
407 }
408 if(was)
409 out << ";\n\n";
410 for(int i = 0; i < m.GetCount(); i++)
411 if(!IsNull(m[i].when))
412 out << key << AsStringWhen(m[i].when) << ' ' << WriteValue(m[i].text) << ";\n\n";
413 }
414
putp(Stream & out,const char * key,const Vector<String> & v)415 void putp(Stream& out, const char *key, const Vector<String>& v)
416 {
417 if(v.GetCount()) {
418 out << key << "\n";
419 for(int i = 0; i < v.GetCount(); i++) {
420 if(i) out << ",\n";
421 out << '\t' << WriteValue(v[i]);
422 }
423 out << ";\n\n";
424 }
425 }
426
putfopt(Stream & out,const char * key,const Array<OptItem> & m)427 void putfopt(Stream& out, const char *key, const Array<OptItem>& m)
428 {
429 for(int i = 0; i < m.GetCount(); i++)
430 out << "\n\t\t" << key << AsStringWhen(m[i].when) << ' ' << WriteValue(m[i].text);
431 }
432
IdeCharsetName(byte charset)433 String IdeCharsetName(byte charset) {
434 return decode(charset, CHARSET_UTF8_BOM, "UTF-8-BOM",
435 CHARSET_UTF16_LE, "UTF-16-LE",
436 CHARSET_UTF16_BE, "UTF-16-BE",
437 CHARSET_UTF16_LE_BOM, "UTF-16-LE-BOM",
438 CHARSET_UTF16_BE_BOM, "UTF-16-BE-BOM",
439 CharsetName(charset));
440 }
441
PutSpellCheckComments(StringStream & out,int sc)442 void PutSpellCheckComments(StringStream& out, int sc)
443 {
444 if(!IsNull(sc))
445 out << " spellcheck_comments " << AsCString(sc ? LNGAsText(sc) : "");
446 }
447
Save(const char * path) const448 bool Package::Save(const char *path) const {
449 StringStream out;
450 if(description.GetCount() || italic || bold || !IsNull(ink)) {
451 String d = description;
452 d.Cat(255);
453 if(bold)
454 d << 'B';
455 if(italic)
456 d << 'I';
457 if(!IsNull(ink))
458 d << (int)ink.GetR() << ',' << (int)ink.GetG() << ',' << (int)ink.GetB();
459 out << "description " << AsCString(d) << ";\n\n";
460 }
461 if(charset > 0)
462 out << "charset " << AsCString(IdeCharsetName(charset)) << ";\n\n";
463 if(!IsNull(tabsize))
464 out << "tabsize " << tabsize << ";\n\n";
465 if(noblitz)
466 out << "noblitz;\n\n";
467 if(nowarnings)
468 out << "options(BUILDER_OPTION) NOWARNINGS;\n\n";
469 putp(out, "acceptflags", accepts);
470 putopt(out, "flags", flag);
471 putopt(out, "uses", uses);
472 putopt(out, "target", target);
473 putopt(out, "library", library);
474 putopt(out, "static_library", static_library);
475 putopt(out, "options", option);
476 putopt(out, "link", link);
477 putopt(out, "include", include);
478 putopt(out, "pkg_config", pkg_config);
479 if(file.GetCount()) {
480 out << "file\n";
481 int i;
482 for(i = 0; i < file.GetCount(); i++) {
483 if(i) out << ",\n";
484 const File& f = file[i];
485 out << '\t' << WriteValue(f);
486 if(f.readonly)
487 out << " readonly";
488 if(f.separator)
489 out << " separator";
490 if(f.tabsize > 0)
491 out << " tabsize " << f.tabsize;
492 if(f.font > 0)
493 out << " font " << f.font;
494 if(f.pch)
495 out << " options(BUILDER_OPTION) PCH";
496 if(f.nopch)
497 out << " options(BUILDER_OPTION) NOPCH";
498 if(f.noblitz)
499 out << " options(BUILDER_OPTION) NOBLITZ";
500 if(f.charset > 0)
501 out << " charset " << AsCString(IdeCharsetName(f.charset));
502 if(!IsNull(f.highlight))
503 out << " highlight " << f.highlight;
504 PutSpellCheckComments(out, f.spellcheck_comments);
505 putfopt(out, "options", f.option);
506 putfopt(out, "depends", f.depends);
507 }
508 out << ";\n\n";
509 }
510 if(config.GetCount()) {
511 out << "mainconfig\n";
512 for(int i = 0; i < config.GetCount(); i++) {
513 const Config& f = config[i];
514 if(i) out << ",\n";
515 out << '\t' << AsCString(f.name) << " = " << AsCString(f.param);
516 }
517 out << ";\n\n";
518 }
519 PutSpellCheckComments(out, spellcheck_comments);
520 for(int i = 0; i < custom.GetCount(); i++)
521 out << custom[i].AsString();
522 return SaveChangedFile(path, out.GetResult());
523 }
524