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