#include "Core.h" bool IsUppValueChar(int c) { return c > ' ' && c != ',' && c != ';'; } String ReadValue(CParser& p) { p.Spaces(); if(p.IsString()) return p.ReadOneString(); StringBuffer v; while(IsUppValueChar(p.PeekChar())) v.Cat(p.GetChar()); p.Spaces(); return v; } static bool sMatchOr(CParser& p, const Vector& flag); static bool sMatchFlag(CParser& p, const Vector& flag) { if(p.Char('!')) return !sMatchFlag(p, flag); if(p.Char('(')) { bool b = sMatchOr(p, flag); p.PassChar(')'); return b; } if(p.IsEof()) return true; return FindIndex(flag, p.ReadId()) >= 0; } static bool sMatchAnd(CParser& p, const Vector& flag) { bool b = sMatchFlag(p, flag); while(p.IsId() || p.IsChar('!') || p.IsChar('(') || p.Char2('&', '&') || p.Char('&')) b = sMatchFlag(p, flag) && b; return b; } static bool sMatchOr(CParser& p, const Vector& flag) { bool b = sMatchAnd(p, flag); while(p.Char2('|', '|') || p.Char('|')) b = sMatchFlag(p, flag) || b; return b; } bool MatchWhen_X(const String& when, const Vector& flag) { CParser p(when); bool b = sMatchOr(p, flag); if(!p.IsEof()) p.ThrowError("expected end of expression"); return b; } bool MatchWhen(const String& when, const Vector& flag) { try { return MatchWhen_X(when, flag); } catch(CParser::Error e) { PutConsole(String().Cat() << "Invalid When expression: " << AsCString(when) << ": " << e); return false; } } String ReadWhen(CParser& p) { String when; if(p.Char('(')) { if(p.IsString()) when = p.ReadString(); else { const char *b = p.GetPtr(); int lvl = 0; for(;;) { if(p.IsEof() || lvl == 0 && p.IsChar(')')) break; if(p.Char('(')) lvl++; else if(p.Char(')')) lvl--; else p.SkipTerm(); } when = String(b, p.GetPtr()); p.Char(')'); } } return when; } String AsStringWhen(const String& when) { String out; try { Vector x; MatchWhen_X(when, x); out << '(' << when << ')'; } catch(CParser::Error) { out << '(' << AsCString(when) << ')'; } return out; } String CustomStep::AsString() const { return "custom" + AsStringWhen(when) + ' ' + AsCString(ext) + ",\n\t" + AsCString(command, 70, "\t") + ",\n\t" + AsCString(output, 70, "\t") + ";\n\n"; } void CustomStep::Load(CParser& p) throw(CParser::Error) { when = ReadWhen(p); ext = p.ReadString(); p.PassChar(','); command = p.ReadString(); p.PassChar(','); output = p.ReadString(); p.PassChar(';'); } String CustomStep::GetExt() const { return ext[0] != '.' ? "." + ToLower(ext) : ToLower(ext); } bool CustomStep::MatchExt(const char *fn) const { return ToLower(GetFileExt(fn)) == GetExt(); } bool LoadOpt(CParser& p, const char *key, Array& v) { if(p.Id(key)) { String when = ReadWhen(p); do { OptItem& m = v.Add(); m.when = when; m.text = ReadValue(p); } while(p.Char(',')); return true; } return false; } bool LoadFOpt(CParser& p, const char *key, Array& v) { if(p.Id(key)) { OptItem& m = v.Add(); m.when = ReadWhen(p); m.text = ReadValue(p); return true; } return false; } void Package::Reset() { charset = 0; optimize_speed = false; noblitz = false; bold = italic = false; ink = Null; } Package::Package() { Reset(); } bool StdResolver(const String& error, const String& path, int line) { PutConsole("Invalid package: " + path); exit(1); return false; } static bool (*sResolve)(const String& error, const String& path, int line) = StdResolver; void Package::SetPackageResolver(bool (*Resolve)(const String& error, const String& path, int line)) { sResolve = Resolve; } byte CharsetByNameX(const String& s) { return s == "UTF-8-BOM" ? CHARSET_UTF8_BOM : CharsetByName(s); } bool Package::Load(const char *path) { for(;;) { Reset(); library.Clear(); target.Clear(); flag.Clear(); option.Clear(); link.Clear(); uses.Clear(); include.Clear(); accepts.Clear(); file.Clear(); config.Clear(); custom.Clear(); description.Clear(); String f = LoadFile(path); time = FileGetTime(path); if(IsNull(time)) return false; CParser p(f); try { while(!p.IsEof()) { if(!LoadOpt(p, "options", option) && !LoadOpt(p, "link", link) && !LoadOpt(p, "library", library) && !LoadOpt(p, "flags", flag) && !LoadOpt(p, "target", target) && !LoadOpt(p, "uses", uses) && !LoadOpt(p, "include", include)) { if(p.Id("charset")) charset = CharsetByNameX(p.ReadString()); else if(p.Id("description")) { description = p.ReadString(); const char *q = strchr(description, 255); ink = Null; bold = italic = false; if(q) { CParser p(q + 1); bold = p.Char('B'); italic = p.Char('I'); if(p.IsNumber()) { RGBA c = Black(); c.r = p.ReadInt(); p.Char(','); if(p.IsNumber()) c.g = p.ReadInt(); p.Char(','); if(p.IsNumber()) c.b = p.ReadInt(); ink = c; } description = String(~description, q); } } else if(p.Id("acceptflags")) { do accepts.Add(ReadValue(p)); while(p.Char(',')); } else if(p.Id("noblitz")) noblitz = true; else if(p.Id("optimize_speed")) optimize_speed = true; else if(p.Id("optimize_size")) optimize_speed = false; else if(p.Id("file")) { do { File f(ReadValue(p)); file.Add() = pick(f); while(!p.IsChar(',') && !p.IsChar(';')) { if(!LoadFOpt(p, "options", file.Top().option) && !LoadFOpt(p, "depends", file.Top().depends)) { if(p.Id("optimize_speed")) file.Top().optimize_speed = true; else if(p.Id("optimize_size")) file.Top().optimize_speed = false; else if(p.Id("readonly")) file.Top().readonly = true; else if(p.Id("separator")) file.Top().separator = true; else if(p.Id("charset")) file.Top().charset = CharsetByNameX(p.ReadString()); else if(p.Id("tabsize")) file.Top().tabsize = minmax(p.ReadInt(), 1, 20); else if(p.Id("font")) file.Top().font = minmax(p.ReadInt(), 0, 3); else if(p.Id("highlight")) file.Top().highlight = p.ReadId(); else p.ThrowError("invalid keyword"); } } } while(p.Char(',')); } else if(p.Id("mainconfig")) { do { String c = p.ReadString(); p.Char('='); p.Id("external"); // Backward compatibility... p.Id("console"); p.Id("remotelinux"); p.Id("normal"); String f = p.ReadString(); Config& cf = config.Add(); cf.name = c; cf.param = f; } while(p.Char(',')); } else if(p.Id("custom")) custom.Add().Load(p); else p.ThrowError("invalid keyword"); } p.Char(';'); } return true; } catch(CParser::Error error) { if(sResolve(error, path, p.GetLine() - 1)) return false; Save(path); return true; } } } String WriteValue(const String& x) { for(const char *s = x; s < x.End(); s++) if(!IsUppValueChar(*s)) return AsCString(x); return x; } void putopt(Stream& out, const char *key, const Array& m) { bool was = false; for(int i = 0; i < m.GetCount(); i++) if(IsNull(m[i].when)) { if(was) out << ",\n\t"; else out << key << "\n\t"; out << WriteValue(m[i].text); was = true; } if(was) out << ";\n\n"; for(int i = 0; i < m.GetCount(); i++) if(!IsNull(m[i].when)) out << key << AsStringWhen(m[i].when) << ' ' << WriteValue(m[i].text) << ";\n\n"; } void putp(Stream& out, const char *key, const Vector& v) { if(v.GetCount()) { out << key << "\n"; for(int i = 0; i < v.GetCount(); i++) { if(i) out << ",\n"; out << '\t' << WriteValue(v[i]); } out << ";\n\n"; } } void putfopt(Stream& out, const char *key, const Array& m) { for(int i = 0; i < m.GetCount(); i++) out << "\n\t\t" << key << AsStringWhen(m[i].when) << ' ' << WriteValue(m[i].text); } bool Package::Save(const char *path) const { StringStream out; if(description.GetCount() || italic || bold || !IsNull(ink)) { String d = description; d.Cat(255); if(bold) d << 'B'; if(italic) d << 'I'; if(!IsNull(ink)) d << (int)ink.GetR() << ',' << (int)ink.GetG() << ',' << (int)ink.GetB(); out << "description " << AsCString(d) << ";\n\n"; } if(charset > 0 && charset < CharsetCount() || charset == CHARSET_UTF8) out << "charset " << AsCString(CharsetName(charset)) << ";\n\n"; else if(charset == CHARSET_UTF8_BOM) out << "charset \"UTF-8-BOM\";\n\n"; if(optimize_speed) out << "optimize_speed;\n\n"; if(noblitz) out << "noblitz;\n\n"; putp(out, "acceptflags", accepts); putopt(out, "flags", flag); putopt(out, "uses", uses); putopt(out, "target", target); putopt(out, "library", library); putopt(out, "options", option); putopt(out, "link", link); putopt(out, "include", include); if(file.GetCount()) { out << "file\n"; int i; for(i = 0; i < file.GetCount(); i++) { if(i) out << ",\n"; const File& f = file[i]; out << '\t' << WriteValue(f); if(f.readonly) out << " readonly"; if(f.separator) out << " separator"; if(f.tabsize > 0) out << " tabsize " << f.tabsize; if(f.font > 0) out << " font " << f.font; if(f.optimize_speed) out << " optimize_speed"; if(f.charset > 0 && f.charset < CharsetCount() || f.charset == CHARSET_UTF8) out << " charset " << AsCString(CharsetName(f.charset)); else if(f.charset == CHARSET_UTF8_BOM) out << " charset \"UTF-8-BOM\""; if(!IsNull(f.highlight)) out << " highlight " << f.highlight; putfopt(out, "options", f.option); putfopt(out, "depends", f.depends); } out << ";\n\n"; } if(config.GetCount()) { out << "mainconfig\n"; for(int i = 0; i < config.GetCount(); i++) { const Config& f = config[i]; if(i) out << ",\n"; out << '\t' << AsCString(f.name) << " = " << AsCString(f.param); } out << ";\n\n"; } for(int i = 0; i < custom.GetCount(); i++) out << custom[i].AsString(); return SaveChangedFile(path, out.GetResult()); }