ultimatepp/uppsrc/ide/Core/Package.cpp
2024-10-15 12:34:34 +02:00

532 lines
No EOL
13 KiB
C++

#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 String(v);
}
static bool sMatchOr(CParser& p, const Vector<String>& flag);
static bool sMatchFlag(CParser& p, const Vector<String>& 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<String>& 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<String>& 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<String>& 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<String>& 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<String> 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)
{
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<OptItem>& 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<OptItem>& 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;
noblitz = nowarnings = false;
bold = italic = false;
ink = Null;
spellcheck_comments = Null;
tabsize = Null;
#ifdef PLATFORM_WIN32
cr = true;
#else
cr = false;
#endif
}
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 decode(s, "UTF-8-BOM", CHARSET_UTF8_BOM,
"UTF-16-LE", CHARSET_UTF16_LE,
"UTF-16-BE", CHARSET_UTF16_BE,
"UTF-16-LE-BOM", CHARSET_UTF16_LE_BOM,
"UTF-16-BE-BOM", CHARSET_UTF16_BE_BOM,
CharsetByName(s));
}
void Package::Option(bool& option, const char *name)
{
File& f = file.Top();
for(int i = 0; i < f.option.GetCount(); i++) // Ugly BW compatibility hack
if(f.option[i].text == name) {
f.option.Remove(i);
option = true;
break;
}
}
bool Package::Load(const char *path)
{
for(;;) {
Reset();
library.Clear();
static_library.Clear();
target.Clear();
flag.Clear();
option.Clear();
link.Clear();
uses.Clear();
include.Clear();
pkg_config.Clear();
accepts.Clear();
file.Clear();
config.Clear();
custom.Clear();
description.Clear();
String f = LoadFile(path);
cr = f.Find('\r') >= 0;
time = FileGetTime(path);
if(IsNull(time))
return false;
CParser p(f);
p.NoSkipComments(); // allow file path like //home/user/project/something.cpp
try {
while(!p.IsEof()) {
if(!LoadOpt(p, "options", option) &&
!LoadOpt(p, "link", link) &&
!LoadOpt(p, "library", library) &&
!LoadOpt(p, "static_library", static_library) &&
!LoadOpt(p, "flags", flag) &&
!LoadOpt(p, "target", target) &&
!LoadOpt(p, "uses", uses) &&
!LoadOpt(p, "include", include) &&
!LoadOpt(p, "pkg_config", pkg_config)) {
if(p.Id("charset"))
charset = CharsetByNameX(p.ReadString());
else
if(p.Id("tabsize"))
tabsize = minmax(p.ReadInt(), 1, 20);
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"))
; // Legacy option ignored
else
if(p.Id("optimize_size"))
; // Legacy option ignored
else
if(p.Id("file")) {
do {
File fv(ReadValue(p));
if(fv.GetCount()) {
File& f = file.Add();
f = pick(fv);
while(!p.IsChar(',') && !p.IsChar(';') && !p.IsEof()) {
if(!LoadFOpt(p, "options", f.option) &&
!LoadFOpt(p, "depends", f.depends)) {
if(p.Id("optimize_speed"))
; // Legacy option ignored
else
if(p.Id("optimize_size"))
; // Legacy option ignored
else
if(p.Id("pch"))
f.pch = true;
else
if(p.Id("nopch"))
f.nopch = true;
else
if(p.Id("noblitz"))
f.noblitz = true;
else
if(p.Id("readonly"))
f.readonly = true;
else
if(p.Id("separator"))
f.separator = true;
else
if(p.Id("charset"))
f.charset = CharsetByNameX(p.ReadString());
else
if(p.Id("tabsize"))
f.tabsize = minmax(p.ReadInt(), 1, 20);
else
if(p.Id("font"))
f.font = minmax(p.ReadInt(), 0, 3);
else
if(p.Id("highlight"))
f.highlight = p.ReadId();
else
if(p.Id("spellcheck_comments"))
f.spellcheck_comments = LNGFromText(p.ReadString());
else
p.SkipTerm();
}
}
Option(f.pch, "PCH");
Option(f.nopch, "NOPCH");
Option(f.noblitz, "NOBLITZ");
}
}
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
if(p.Id("spellcheck_comments"))
spellcheck_comments = LNGFromText(p.ReadString());
else
p.SkipTerm();
}
p.Char(';');
}
for(int i = 0; i < option.GetCount(); i++)
if(option[i].when == "BUILDER_OPTION" && option[i].text == "NOWARNINGS") {
nowarnings = true;
option.Remove(i);
break;
}
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<OptItem>& 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<String>& 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<OptItem>& m)
{
for(int i = 0; i < m.GetCount(); i++)
out << "\n\t\t" << key << AsStringWhen(m[i].when) << ' ' << WriteValue(m[i].text);
}
String IdeCharsetName(byte charset) {
return decode(charset, CHARSET_UTF8_BOM, "UTF-8-BOM",
CHARSET_UTF16_LE, "UTF-16-LE",
CHARSET_UTF16_BE, "UTF-16-BE",
CHARSET_UTF16_LE_BOM, "UTF-16-LE-BOM",
CHARSET_UTF16_BE_BOM, "UTF-16-BE-BOM",
CharsetName(charset));
}
void PutSpellCheckComments(StringStream& out, int sc)
{
if(!IsNull(sc))
out << " spellcheck_comments " << AsCString(sc ? LNGAsText(sc) : String());
}
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)
out << "charset " << AsCString(IdeCharsetName(charset)) << ";\n\n";
if(!IsNull(tabsize))
out << "tabsize " << tabsize << ";\n\n";
if(noblitz)
out << "noblitz;\n\n";
if(nowarnings)
out << "options(BUILDER_OPTION) NOWARNINGS;\n\n";
putp(out, "acceptflags", accepts);
putopt(out, "flags", flag);
putopt(out, "uses", uses);
putopt(out, "target", target);
putopt(out, "library", library);
putopt(out, "static_library", static_library);
putopt(out, "options", option);
putopt(out, "link", link);
putopt(out, "include", include);
putopt(out, "pkg_config", pkg_config);
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.pch)
out << " options(BUILDER_OPTION) PCH";
if(f.nopch)
out << " options(BUILDER_OPTION) NOPCH";
if(f.noblitz)
out << " options(BUILDER_OPTION) NOBLITZ";
if(f.charset > 0)
out << " charset " << AsCString(IdeCharsetName(f.charset));
if(!IsNull(f.highlight))
out << " highlight " << f.highlight;
PutSpellCheckComments(out, f.spellcheck_comments);
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";
}
PutSpellCheckComments(out, spellcheck_comments);
for(int i = 0; i < custom.GetCount(); i++)
out << custom[i].AsString();
String content = out.GetResult();
if(cr)
content.Replace("\n", "\r\n");
return SaveChangedFile(path, content);
}