/* PIP - Platform Independent Primitives Code model generator Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "picli.h" #include "picodeparser.h" #include "piiostream.h" #include using namespace PICoutManipulators; PICodeParser parser; const char help_string[] = "-M (Metainfo)\n" "Generate classes and structs info. It contains\n" "names, subclass info, all methods and variables.\n" "Place information:\n" " * classes and structs - PICODEINFO::classes()\n" " * methods - ClassInfo::functions\n" " * variables - ClassInfo::variables\n" "\n" "-E (Enums)\n" "Generate enumeration descriptions.\n" "Useful for GUI integrations, because\n" "you can obtain enumerators value and name.\n" " * enums - PICODEINFO::enums()\n" "\n" "-S (Stream operators)\n" "Generate store/restore operators with format\n" "BINARY_STREAM_WRITE();\n" "BINARY_STREAM_READ ();\n" "Only public variables used. All variables stored/restored\n" "using PIChunkStream. IDs are variable number, starting from 1.\n" "You can override ID with PIMETA(id=). If in class or struct\n" "PIMETA(simple-stream) presence, then variables stored/restored\n" "with simple << and >> operators.\n" "If PIMETA(no-stream) presence, then class or struct ignored.\n" "\n" "-G (Getter functions)\n" "Generate anonymous access methods for member typenames and values.\n" "Every class or struct member typename can be obtained with:\n" "const char * getMemberType(const char * class_name, const char * member_name)\n" "Member value can be obtained with:\n" "PIByteArray getMemberValue(const void * p, const char * class_name, const char * member_name)\n" "where \"p\" - class or struct pointer, and returns serialized value.\n" "PIVariant getMemberAsVariant(const void * p, const char * class_name, const char * member_name)\n" "where \"p\" - class or struct pointer, and returns value as registered PIVariant.\n" ""; void header() { piCout << Bold << "PIP Code model generator"; piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine; piCout << Green << Bold << "Usage:" << Default << "\"pip_cmg [-hHqPpsAMESTG] -o [-I] [-I] [...] [-D] [-D] [...] " " [] [] [...]\"" << NewLine; } void usage() { header(); piCout << Green << Bold << "Details:"; piCout << Bold << "Debug control"; piCout << "-h " << Green << "- display this message and exit"; piCout << "-H " << Green << "- display details help"; piCout << "-q " << Green << "- quiet, no debug output to console"; piCout << "-P " << Green << "- print list of all parsed files to console before exit"; piCout << "-p " << Green << "- print list of all parsed files without file with \"main\" function to console before exit"; piCout << ""; piCout << Bold << "Parsing control"; piCout << "-s " << Green << "- single file (don`t follow includes)"; piCout << "-I " << Green << "- add include dir (e.g. -I.. -I../some_dir -I/usr/include)"; piCout << "-D " << Green << "- add define to preprocessor, macro PICODE is always defined (e.g. -DMY_DEFINE will add MY_DEFINE define)"; piCout << ""; piCout << Bold << "Output control"; piCout << "-A " << Green << "- write all"; piCout << "-M " << Green << "- write metainfo"; piCout << "-E " << Green << "- write enums"; piCout << "-S " << Green << "- write stream operators"; piCout << "-G " << Green << "- write getter functions"; // piCout << "-T " << Green << "- write text serialize functions"; piCout << "-o " << Green << "- output file for code model without extension (e.g. \"ccm\" - files \"ccm.h\" and \"ccm.cpp\" will be created)"; piCout << ""; piCout << Bold << "Input control"; piCout << " " << Green << "- add file to code model, all includes of this file will be proceed (e.g. \"main.cpp\")"; } void help() { header(); piCout << help_string; } void printError(const PIString & msg) { std::cerr << msg.data() << std::endl; } PIString toCName(const PIString & s) { PIString ret(s.trimmed()); if (ret.isEmpty()) return ret; ret.replaceAll('<', '_').replaceAll('>', '_'); for (int i = 0; i < ret.size_s(); ++i) { if (i == 0) { if (!(ret[i].isAlpha() || ret[i].toAscii() == '_')) { ret.pop_front(); --i; } } else { if (!(ret[i].isAlpha() || ret[i].isDigit() || ret[i].toAscii() == '_')) { ret.remove(i); --i; } } } ret.replaceAll("__", "_"); return ret; } void makeClassInfo(PIIOTextStream & ts, const PICodeParser::Entity * e) { ts << "\n\t{\n\tClassInfo * ci = new ClassInfo();\n"; ts << "\tci->type = \"" << e->type << "\";\n"; ts << "\tci->name = \"" << e->name << "\";\n"; ts << "\tci->has_name = " << (e->has_name ? "true" : "false") << ";\n"; if (!e->meta.isEmpty()) { auto i = e->meta.makeIterator(); while (i.next()) ts << "\tci->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n"; } ts << "\t(*PICodeInfo::__Storage__::instance()->classesInfo)[ci->name] = ci;\n"; if (e->parent_scope) { ts << "\tpci = " << "PICodeInfo::__Storage__::instance()->classesInfo->value(\"" << e->parent_scope->name << "\", 0);\n"; ts << "\tif (pci) pci->children_info << ci;\n"; } for (const PICodeParser::Entity * p: e->parents) ts << "\tci->parents << \"" << p->name << "\";\n"; if (!e->members.isEmpty()) ts << "\n\tTypeInfo ti;\n"; for (const PICodeParser::Member & m: e->members) { ts << "\tti = TypeInfo(\"" << m.name << "\", \"" << m.type << "\""; if (m.attributes != 0) { bool fir = true; ts << ", "; if (m.attributes[PICodeParser::Const]) { if (fir) fir = false; else ts << " | "; ts << "Const"; } if (m.attributes[PICodeParser::Static]) { if (fir) fir = false; else ts << " | "; ts << "Static"; } if (m.attributes[PICodeParser::Mutable]) { if (fir) fir = false; else ts << " | "; ts << "Mutable"; } if (m.attributes[PICodeParser::Volatile]) { if (fir) fir = false; else ts << " | "; ts << "Volatile"; } if (m.attributes[PICodeParser::Inline]) { if (fir) fir = false; else ts << " | "; ts << "Inline"; } if (m.attributes[PICodeParser::Virtual]) { if (fir) fir = false; else ts << " | "; ts << "Virtual"; } } else { if (m.isBitfield()) ts << ", 0"; } if (m.isBitfield()) ts << ", " << m.bits; ts << ");\n"; if (!m.meta.isEmpty()) { for (PICodeParser::MetaMap::const_iterator i = m.meta.begin(); i != m.meta.end(); ++i) ts << "\tti.meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n"; } ts << "\tci->variables << ti;\n"; } PIString arg; bool has_fi = false; for (const PICodeParser::Member & m: e->functions) { if (e->name.findCWord(m.name) >= 0) continue; if (!has_fi) ts << "\n\tFunctionInfo * fi;\n"; has_fi = true; ts << "\tci->functions.push_back(FunctionInfo()); fi = &(ci->functions.back());\n"; ts << "\tfi->name = \"" << m.name << "\";"; ts << " fi->return_type = TypeInfo(\"\", \"" << m.type << "\""; if (m.attributes[PICodeParser::Const] || m.attributes[PICodeParser::Static]) { bool fir = true; ts << ", "; if (m.attributes[PICodeParser::Const]) { if (fir) fir = false; else ts << " | "; ts << "Const"; } if (m.attributes[PICodeParser::Static]) { if (fir) fir = false; else ts << " | "; ts << "Static"; } } ts << ");\n"; // piCout << "write func" << m.name; for (const PIString & a: m.arguments_full) { // piCout << "write arg" << a; ts << "\tfi->arguments << TypeInfo("; arg = a; bool con = false; arg.prepend(" "); if (arg.find(" const ") >= 0) { arg.replaceAll(" const ", " "); con = true; } arg.trim(); int i = 0; for (i = arg.size_s() - 1; i > 0; --i) if (!_isCChar(arg[i]) && !(arg[i].isDigit())) break; ts << "\"" << arg.takeRight(arg.size_s() - i - 1).trim() << "\", "; ts << "\"" << arg.trim() << "\""; if (con) ts << ", Const"; ts << ");\n"; } if (!m.meta.isEmpty()) { for (PICodeParser::MetaMap::const_iterator i = m.meta.begin(); i != m.meta.end(); ++i) ts << "\tfi->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n"; } } ts << "\n\t}"; } void makeEnumInfo(PIIOTextStream & ts, const PICodeParser::Enum * e) { if (e->name.isEmpty()) { ts << "\n\tei = (*PICodeInfo::__Storage__::instance()->enumsInfo)[\"\"];\n"; } else { ts << "\n\tei = new EnumInfo();\n"; ts << "\t(*PICodeInfo::__Storage__::instance()->enumsInfo)[\"" << e->name << "\"] = ei;\n"; ts << "\tei->name = \"" << e->name << "\";\n"; if (!e->meta.isEmpty()) { auto i = e->meta.makeIterator(); while (i.next()) ts << "\tei->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n"; } } for (const PICodeParser::EnumeratorInfo & m: e->members) { ts << "\tei->members << PICodeInfo::EnumeratorInfo(\"" << m.name << "\", " << m.value << ");\n"; if (!m.meta.isEmpty()) { auto i = m.meta.makeIterator(); while (i.next()) ts << "\tei->members.back().meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n"; } } } bool writeClassStreamMembersOut(PIIOTextStream & ts, const PICodeParser::Entity * e, int & cnt, bool simple) { PIVector ml; for (const PICodeParser::Member & m: e->members) { if (m.is_type_ptr || (m.visibility != PICodeParser::Public)) continue; ml << m; } bool is_union = e->type == "union"; PISet used_id; for (const PICodeParser::Member & m: ml) { if (is_union && m.isBitfield()) continue; if (m.attributes[PICodeParser::Static]) continue; if (m.meta.value("id") == "-") continue; ++cnt; if (m.meta.contains("id")) cnt = m.meta.value("id").toInt(); if (used_id[cnt]) { printError("Error with \"" + e->name + "\" stream operator: ID " + PIString::fromNumber(cnt) + " already used"); return false; } if (m.dims.isEmpty()) { if (simple) { ts << "\ts << "; if (parser.isEnum(m.type)) ts << "(int)"; ts << "v." << m.name << ";\n"; } else { ts << "\tcs.add(" << cnt << ", "; if (parser.isEnum(m.type)) ts << "(int)"; ts << "v." << m.name << ");\n"; } } else { PIString ptype = m.type.left(m.type.find('[')).trim(); PIString size = m.dims[0]; for (int i = 1; i < m.dims.size_s(); ++i) { size += " * "; size += m.dims[i]; } if (simple) { ts << "\tfor (int i = 0; i < " << size << "; ++i)\n"; ts << "\t\ts << ((const " << ptype << " *)(v." << m.name << "))[i];\n"; } else { ts << "\tcs << cs.chunk(" << cnt << ", PIVector<" << ptype << " >((const " << ptype << " *)(v." << m.name << "), "; ts << size << "));\n"; } } if (is_union) break; } if (is_union) return true; for (const PICodeParser::Entity * ce: e->children) { if (ce->has_name) continue; if (!writeClassStreamMembersOut(ts, ce, cnt, simple)) return false; } return true; } bool writeClassStreamMembersIn(PIIOTextStream & ts, const PICodeParser::Entity * e, int & cnt, bool simple) { PIVector ml; for (const PICodeParser::Member & m: e->members) { if (m.is_type_ptr || (m.visibility != PICodeParser::Public)) continue; ml << m; } bool is_union = e->type == "union"; PISet used_id; for (const PICodeParser::Member & m: ml) { if (is_union && m.isBitfield()) continue; if (m.attributes[PICodeParser::Static]) continue; if (m.meta.value("id") == "-") continue; ++cnt; if (m.meta.contains("id")) cnt = m.meta.value("id").toInt(); if (used_id[cnt]) { printError("Error with \"" + e->name + "\" stream operator: ID " + PIString::fromNumber(cnt) + " already used"); return false; } used_id << cnt; if (m.dims.isEmpty()) { bool is_enum = parser.isEnum(m.type); if (simple) { ts << "\t"; if (is_enum) ts << "{int i; "; ts << "s >> "; if (is_enum) ts << "i;"; else ts << "v." << m.name << ";"; if (is_enum) ts << " v." << m.name << " = (" << m.type << ")i;}"; ts << "\n"; } else { ts << "\t\tcase " << cnt << ":"; if (is_enum) ts << " {int i;"; ts << " cs.get("; if (is_enum) ts << "i"; else ts << "v." << m.name; ts << ");"; if (is_enum) ts << " v." << m.name << " = (" << m.type << ")i;}"; ts << " break;\n"; } } else { PIString ptype = m.type.left(m.type.find('[')).trim(); PIString size = m.dims[0]; for (int i = 1; i < m.dims.size_s(); ++i) { size += " * "; size += m.dims[i]; } if (simple) { ts << "\tfor (int i = 0; i < " << size << "; ++i)\n"; ts << "\t\ts >> ((" << ptype << " *)(v." << m.name << "))[i];\n"; } else { ts << "\t\tcase " << cnt << ": {\n\t\t\tPIVector<" << ptype << " > d; cs.get(d);\n"; ts << "\t\t\tint cnt = piMini(d.size_s(), " << size << ");\n"; ts << "\t\t\tfor (int i = 0; i < cnt; ++i)\n"; ts << "\t\t\t\t((" << ptype << " *)(v." << m.name << "))[i] = d[i];\n"; ts << "\t\t\t}\n"; ts << "\t\t\tbreak;\n"; } } if (is_union) break; } if (is_union) return true; for (const PICodeParser::Entity * ce: e->children) { if (ce->has_name) continue; if (!writeClassStreamMembersIn(ts, ce, cnt, simple)) return false; } return true; } bool needClassStream(const PICodeParser::Entity * e) { if (e->meta.contains("no-stream")) return false; for (const PICodeParser::Member & m: e->members) { if (m.is_type_ptr || m.isBitfield() || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public)) continue; if (m.attributes[PICodeParser::Static]) continue; if (m.meta.value("id") == "-") continue; return true; } return false; } bool makeClassStream(PIIOTextStream & ts, const PICodeParser::Entity * e) { if (!needClassStream(e)) return true; bool simple = e->meta.contains("simple-stream"); ts << "\nBINARY_STREAM_WRITE(" << e->name << ") {\n"; if (!simple) ts << "\tPIChunkStream cs;\n"; int cnt = 0; if (!writeClassStreamMembersOut(ts, e, cnt, simple)) return false; if (!simple) ts << "\ts << cs.data();\n"; ts << "\treturn s;\n}\n"; ts << "BINARY_STREAM_READ (" << e->name << ") {\n"; if (!simple) { // ts << "\tif (s.size_s() < 4) return s;\n"; ts << "\tPIChunkStream cs;\n"; ts << "\tcs.extract(s);\n"; ts << "\twhile (!cs.atEnd()) {\n"; ts << "\t\tswitch (cs.read()) {\n"; } cnt = 0; if (!writeClassStreamMembersIn(ts, e, cnt, simple)) return false; if (!simple) ts << "\t\t}\n\t}\n"; ts << "\treturn s;\n}\n"; return true; } void makeGetterType(PIIOTextStream & ts, const PICodeParser::Entity * e) { if (!needClassStream(e)) return; ts << "\nconst char * getterType" << toCName(e->name) << "(const char * name) {\n"; ts << "\tif (!name) return \"\";\n"; for (const PICodeParser::Member & m: e->members) { if (m.is_type_ptr || m.isBitfield() || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public)) continue; ts << "\tif (strcmp(name, \"" << m.name << "\") == 0) return \"" << m.type << "\";\n"; } ts << "\treturn \"\";\n}\n"; } void makeGetterValue(PIIOTextStream & ts, const PICodeParser::Entity * e) { if (!needClassStream(e)) return; ts << "\nPIByteArray getterValue" << toCName(e->name) << "(const void * p, const char * name) {\n"; ts << "\tPIByteArray ret;\n"; ts << "\tif (!p || !name) return ret;\n"; ts << "\t" << e->name << " * o = (" << e->name << "*)p;\n"; for (const PICodeParser::Member & m: e->members) { if (m.is_type_ptr || m.isBitfield() || !m.dims.isEmpty() || (m.visibility != PICodeParser::Public)) continue; ts << "\tif (strcmp(name, \"" << m.name << "\") == 0) {serialize(ret, o->" << m.name << "); return ret;}\n"; } ts << "\treturn ret;\n}\n"; } bool writeModel(PICodeParser & parser, PICLI & cli, const PIString out, bool meta, bool enums, bool streams, bool texts, bool getters) { PIString defname = "CCM_" + PIString::fromNumber(out.hash()) + "_H"; PISet inc_files; for (const PICodeParser::Entity * e: parser.entities) if (!e->name.startsWith("_PI")) inc_files << e->file; PIString inc_string; PIVector incf = inc_files.toVector(); for (const PIString & i: incf) { if ((i != parser.mainFile()) && (streams || texts || getters)) inc_string += "\n#include \"" + i + "\""; } PIFile f(out + ".cpp"); f.clear(); if (!f.open(PIIODevice::WriteOnly)) { piCout << "Error: can`t open out file" << f.path(); return false; } PIIOTextStream ts(&f); ts << "// Generated by \"PIP Code model generator\" " << PIDateTime::current().toString("dd.MM.yyyy hh:mm:ss\n\n"); ts << "#include \n"; ts << "#include \"" << out << ".h\"\n"; ts << "\nusing namespace PICodeInfo;\n"; if (meta || enums || getters) { if (getters) { ts << "\n\n// Getter funtions\n"; for (const PICodeParser::Entity * e: parser.entities) { if (!e->has_name || e->name.startsWith("_PI")) continue; makeGetterType(ts, e); makeGetterValue(ts, e); } } ts << "\n\n// Metainformation\n\n__ClassInfo_" << defname << "_Initializer__::__ClassInfo_" << defname << "_Initializer__() {\n"; ts << "\tstatic __ClassInfo_" << defname << "_Initializer__::Content content;\n"; ts << "}\n\n"; ts << "__ClassInfo_" << defname << "_Initializer__::Content::Content() {\n"; if (meta) { ts << "\tClassInfo * pci = new ClassInfo();\n"; ts << "\t(*PICodeInfo::__Storage__::instance()->classesInfo)[\"\"] = pci;\n"; } if (enums && !parser.enums.isEmpty()) { ts << "\tEnumInfo * ei;\n"; } if (meta) { ts << "\n\n// Classes\n"; for (const PICodeParser::Entity * e: parser.entities) { if (e->name.startsWith("_PI")) continue; makeClassInfo(ts, e); } } if (enums) { ts << "\n// Enums\n"; for (const PICodeParser::Enum & e: parser.enums) makeEnumInfo(ts, &e); } if (getters) { ts << "\n// Getters\n"; for (const PICodeParser::Entity * e: parser.entities) { if (!needClassStream(e)) continue; if (!e->has_name || e->name.startsWith("_PI")) continue; ts << "\t(*PICodeInfo::__Storage__::instance()->accessValueFunctions)[\"" << e->name << "\"] = getterValue" << toCName(e->name) << ";\n"; ts << "\t(*PICodeInfo::__Storage__::instance()->accessTypeFunctions)[\"" << e->name << "\"] = getterType" << toCName(e->name) << ";\n"; } } ts << "}\n\n"; ts << "__ClassInfo_" << defname << "_Initializer__::Content::~Content() {\n"; if (meta) { ts << "\n// Classes clean\n"; for (const PICodeParser::Entity * e: parser.entities) { if (e->name.startsWith("_PI")) continue; ts << "\tpiDeleteSafety((*PICodeInfo::__Storage__::instance()->classesInfo)[\"" << e->name << "\"]);\n"; ts << "\tPICodeInfo::__Storage__::instance()->classesInfo->remove(\"" << e->name << "\");\n"; } } if (enums) { ts << "\n// Enums clean\n"; for (const PICodeParser::Enum & e: parser.enums) { if (e.name.isNotEmpty()) { ts << "\tpiDeleteSafety((*PICodeInfo::__Storage__::instance()->enumsInfo)[\"" << e.name << "\"]);\n"; ts << "\tPICodeInfo::__Storage__::instance()->enumsInfo->remove(\"" << e.name << "\");\n"; } } } if (getters) { ts << "\n// Getters clean\n"; for (const PICodeParser::Entity * e: parser.entities) { if (!needClassStream(e)) continue; if (!e->has_name || e->name.startsWith("_PI")) continue; ts << "\tPICodeInfo::__Storage__::instance()->accessValueFunctions->remove(\"" << e->name << "\");\n"; ts << "\tPICodeInfo::__Storage__::instance()->accessTypeFunctions->remove(\"" << e->name << "\");\n"; } } ts << "}\n"; } f.close(); f.setPath(out + ".h"); f.clear(); if (!f.open(PIIODevice::WriteOnly)) { piCout << "Error: can`t open out file" << f.path(); return false; } ts << "// Generated by \"PIP Code model generator\" " << PIDateTime::current().toString("dd.MM.yyyy hh:mm:ss\n"); ts << "// Execute command:\n"; for (const PIString & _a: cli.rawArguments()) ts << "// \"" << _a << "\"\n"; ts << "\n"; ts << "#ifndef " << defname << "\n#define " << defname << "\n\n"; ts << "#include \n#include "; if (streams || texts) ts << "\n#include "; ts << inc_string << "\n"; if (streams) { ts << "\n\n// Stream operators\n"; for (const PICodeParser::Entity * e: parser.entities) { if (!e->has_name || e->name.startsWith("_PI") || !(e->visibility == PICodeParser::Global || e->visibility == PICodeParser::Public)) continue; if (!makeClassStream(ts, e)) return false; } } if (meta || enums || getters) { ts << "\n\n// Metainformation\n\nclass __ClassInfo_" << defname << "_Initializer__ {\n"; ts << "public:\n"; ts << "\t__ClassInfo_" << defname << "_Initializer__();\n"; ts << "private:\n"; ts << "\tclass Content {\n"; ts << "\tpublic:\n"; ts << "\t\tContent();\n"; ts << "\t\t~Content();\n"; ts << "\t};\n"; ts << "};\n"; ts << "\nstatic __ClassInfo_" << defname << "_Initializer__ __classinfo_" << defname.toLowerCase() << "_initializer__;\n"; } ts << "\n\n#endif // " << defname << "\n"; f.close(); return true; } int main(int argc, char * argv[]) { PICLI cli(argc, argv); // piCout << cli.rawArguments(); cli.setOptionalArgumentsCount(-1); cli.addArgument("output", true); cli.addArgument("help"); cli.addArgument("Help"); cli.addArgument("quiet"); cli.addArgument("All"); cli.addArgument("Metainfo"); cli.addArgument("Enum"); cli.addArgument("Stream"); cli.addArgument("Getter"); cli.addArgument("Text"); cli.addArgument("print"); cli.addArgument("Print"); cli.addArgument("single"); if (cli.hasArgument("Help")) { help(); return 0; } if (cli.hasArgument("help") || cli.argumentValue("output").isEmpty() || cli.optionalArguments().isEmpty()) { usage(); return 0; } piDebug = !cli.hasArgument("quiet"); for (const PIString & a: cli.rawArguments()) { if (a.startsWith("-I")) parser.includeDirectory(a.mid(2)); if (a.startsWith("-D")) parser.addDefine(a.mid(2), PIString()); } PIStringList files; for (const PIString & a: cli.optionalArguments()) if (!a.startsWith("-")) files << a; piCout << Cyan << Bold << "Parse files" << files << "..."; parser.parseFiles(files, !cli.hasArgument("single")); piCout << Cyan << Bold << "Parsing done"; piCout << Cyan << Bold << "Writing code model ..."; bool all = cli.hasArgument("All"); if (!writeModel(parser, cli, cli.argumentValue("output"), cli.hasArgument("Metainfo") || all, cli.hasArgument("Enum") || all, cli.hasArgument("Stream") || all, cli.hasArgument("Text") || all, cli.hasArgument("Getter") || all)) return 1; piCout << Cyan << Bold << "Writing done"; if (cli.hasArgument("print") || cli.hasArgument("Print")) { bool womain = cli.hasArgument("print"); piDebug = true; PIStringList pf(parser.parsedFiles()); for (const PIString & f: pf) { if (!womain || (f != parser.mainFile())) piCout << f; } } return 0; }