#include "tcli.h" #include "tconvert.h" #include using namespace std; using namespace TCli; //========================================================= // // Release // //--------------------------------------------------------- namespace { //--------------------------------------------------------- void printToonzRelease(ostream &out) { out << "Toonz 7.1" << endl; } //--------------------------------------------------------- void printLibRelease(ostream &out) { out << "Tnzcore 1.0 - " __DATE__ << endl; } //--------------------------------------------------------- } //namespace //========================================================= // // local UsageError // //--------------------------------------------------------- //========================================================= // // local UsageElements // //--------------------------------------------------------- class SpecialUsageElement : public UsageElement { public: SpecialUsageElement(string name) : UsageElement(name, " "){}; void dumpValue(ostream &) const {}; void resetValue(){}; }; //--------------------------------------------------------- static SpecialUsageElement bra("["); static SpecialUsageElement ket("]"); static Switcher help("-help", "Print this help page"); static Switcher release("-release", "Print the current Toonz version"); static Switcher libRelease("-librelease", ""); // hidden: print the lib version //========================================================= // // helper function // //--------------------------------------------------------- namespace { //--------------------------------------------------------- void fetchElement(int index, int &argc, char *argv[]) { if (index >= argc) throw UsageError("missing argument"); for (int i = index; i < argc - 1; i++) argv[i] = argv[i + 1]; argc--; } //--------------------------------------------------------- void fetchElement(int &dst, int index, int &argc, char *argv[]) { string s = argv[index]; if (isInt(s)) dst = toInt(s); else throw UsageError("expected int"); fetchElement(index, argc, argv); } //--------------------------------------------------------- void fetchElement(string &dst, int index, int &argc, char *argv[]) { char *s = argv[index]; fetchElement(index, argc, argv); if (*s == '-') throw UsageError("expected argument"); dst = string(s); } //--------------------------------------------------------- } //namespace //========================================================= // // UsageElement // //--------------------------------------------------------- UsageElement::UsageElement(string name, string help) : m_name(name), m_help(help), m_selected(false) { } //--------------------------------------------------------- void UsageElement::print(ostream &out) const { out << m_name; } //--------------------------------------------------------- void UsageElement::printHelpLine(ostream &out) const { out << " " << m_name << endl; out << " " << m_help << endl; } //========================================================= // // Qualifier // //--------------------------------------------------------- void Qualifier::print(ostream &out) const { if (isSwitcher()) out << m_name; else out << "[ " << m_name << " ]"; } //--------------------------------------------------------- void SimpleQualifier::fetch(int index, int &argc, char *argv[]) { fetchElement(index, argc, argv); } //--------------------------------------------------------- void SimpleQualifier::dumpValue(ostream &out) const { out << m_name << " = " << (isSelected() ? "on" : "off") << endl; } //--------------------------------------------------------- void SimpleQualifier::resetValue() { m_selected = false; } //========================================================= // // Argument & MultiArgument // //--------------------------------------------------------- void Argument::fetch(int index, int &argc, char *argv[]) { if (index >= argc) throw UsageError("missing argument"); if (!assign(argv[index])) throw UsageError(string("bad argument type :") + argv[index]); for (int i = index; i < argc - 1; i++) argv[i] = argv[i + 1]; argc--; } //--------------------------------------------------------- void MultiArgument::fetch(int index, int &argc, char *argv[]) { if (index >= argc) throw UsageError("missing argument(s)"); allocate(argc - index); for (m_index = 0; m_index < m_count; m_index++) if (!assign(argv[index + m_index])) throw UsageError(string("bad argument type :") + argv[index + m_index]); argc -= m_count; } //========================================================= // // UsageLine // //--------------------------------------------------------- UsageLine::UsageLine() : m_elements(0), m_count(0) { } //--------------------------------------------------------- UsageLine::~UsageLine() { if (m_elements) { delete[] m_elements; } } //--------------------------------------------------------- UsageLine::UsageLine(const UsageLine &src) { m_count = src.m_count; m_elements = new UsageElementPtr[m_count]; ::memcpy(m_elements, src.m_elements, m_count * sizeof(m_elements[0])); } //--------------------------------------------------------- UsageLine &UsageLine::operator=(const UsageLine &src) { if (src.m_elements != m_elements) { delete[] m_elements; m_elements = new UsageElementPtr[src.m_count]; ::memcpy(m_elements, src.m_elements, src.m_count * sizeof(m_elements[0])); } m_count = src.m_count; return *this; } //--------------------------------------------------------- UsageLine::UsageLine(const UsageLine &src, UsageElement &elem) { m_count = src.m_count; m_elements = new UsageElementPtr[m_count + 1]; ::memcpy(m_elements, src.m_elements, m_count * sizeof(m_elements[0])); m_elements[m_count++] = &elem; } //--------------------------------------------------------- UsageLine::UsageLine(int count) { m_count = count; m_elements = new UsageElementPtr[m_count]; ::memset(m_elements, 0, m_count * sizeof(m_elements[0])); } //--------------------------------------------------------- UsageLine::UsageLine(UsageElement &elem) { m_count = 1; m_elements = new UsageElementPtr[m_count]; m_elements[0] = &elem; } //--------------------------------------------------------- UsageLine::UsageLine(UsageElement &a, UsageElement &b) { m_count = 2; m_elements = new UsageElementPtr[m_count]; m_elements[0] = &a; m_elements[1] = &b; } //--------------------------------------------------------- UsageLine TCli::UsageLine::operator+(UsageElement &elem) { return UsageLine(*this, elem); } //--------------------------------------------------------- UsageLine TCli::operator+(UsageElement &a, UsageElement &b) { return UsageLine(a, b); } //--------------------------------------------------------- UsageLine TCli::operator+(const UsageLine &a, const Optional &b) { UsageLine ul(a.getCount() + b.getCount()); int i; for (i = 0; i < a.getCount(); i++) ul[i] = a[i]; for (int j = 0; j < b.getCount(); j++) ul[i++] = b[j]; return ul; } //========================================================= Optional::Optional(const UsageLine &ul) : UsageLine(ul.getCount() + 2) { m_elements[0] = &bra; m_elements[m_count - 1] = &ket; for (int i = 0; i < ul.getCount(); i++) m_elements[i + 1] = ul[i]; } //========================================================= // // UsageImp // //--------------------------------------------------------- class TCli::UsageImp { string m_progName; vector m_usageLines; std::map m_qtable; vector m_qlist; vector m_args; typedef std::map::iterator qiterator; typedef std::map::const_iterator const_qiterator; UsageLine *m_selectedUsageLine; public: UsageImp(string progName); ~UsageImp(){}; void add(const UsageLine &); void addStandardUsages(); void registerQualifier(Qualifier *q); void registerQualifier(string name, Qualifier *q); void registerArgument(Argument *arg); void printUsageLine(ostream &out, const UsageLine &ul) const; void printUsageLines(ostream &out) const; void print(ostream &out) const; void resetValues(); void clear(); void parse(int argc, char *argv[]); void fetchArguments( UsageLine &ul, int a, int b, int &argc, char *argv[]); bool matchSwitcher(const UsageLine &ul) const; bool hasSwitcher(const UsageLine &ul) const; bool matchArgCount(const UsageLine &ul, int a, int b, int argc) const; void dumpValues(ostream &out) const; void getArgCountRange(const UsageLine &ul, int a, int b, int &min, int &max) const; static const int InfiniteArgCount; }; //--------------------------------------------------------- const int UsageImp::InfiniteArgCount = 2048; //--------------------------------------------------------- UsageImp::UsageImp(string progName) : m_progName(progName), m_selectedUsageLine(0) { addStandardUsages(); } //--------------------------------------------------------- void UsageImp::addStandardUsages() { add(help); add(release); add(libRelease); } //--------------------------------------------------------- void UsageImp::clear() { m_usageLines.clear(); m_qtable.clear(); m_qlist.clear(); m_args.clear(); m_selectedUsageLine = 0; addStandardUsages(); } //--------------------------------------------------------- void UsageImp::registerArgument(Argument *arg) { unsigned int i; for (i = 0; i < m_args.size() && m_args[i] != arg; i++) { } if (i == m_args.size()) m_args.push_back(arg); } //--------------------------------------------------------- void UsageImp::registerQualifier(string name, Qualifier *q) { m_qtable[name] = q; unsigned int i; for (i = 0; i < m_qlist.size() && m_qlist[i] != q; i++) { } if (i == m_qlist.size()) m_qlist.push_back(q); } //--------------------------------------------------------- void UsageImp::registerQualifier(Qualifier *q) { string str = q->getName(); const char *s0 = str.c_str(); while (*s0 == ' ') s0++; const char *s = s0; for (;;) { if (*s != '-') { assert(!"Qualifier name must start with '-'"); } do s++; while (isalnum(*s)); if (s == s0 + 1) { assert(!"Empty qualifier name"); } string name(s0, s - s0); registerQualifier(name, q); while (*s == ' ') s++; // ignoro gli eventuali parametri while (isalnum(*s)) { while (isalnum(*s)) s++; while (*s == ' ') s++; } if (*s != '|') break; s++; while (*s == ' ') s++; s0 = s; } if (*s != '\0') { assert(!"Unexpected character"); } } //--------------------------------------------------------- void UsageImp::add(const UsageLine &ul) { m_usageLines.push_back(ul); for (int i = 0; i < ul.getCount(); i++) { Qualifier *q = dynamic_cast(ul[i]); if (q) registerQualifier(q); Argument *a = dynamic_cast(ul[i]); if (a) registerArgument(a); } } //--------------------------------------------------------- void UsageImp::printUsageLine(ostream &out, const UsageLine &ul) const { out << m_progName; for (int j = 0; j < ul.getCount(); j++) if (!ul[j]->isHidden()) { out << " "; ul[j]->print(out); } out << endl; } //--------------------------------------------------------- void UsageImp::printUsageLines(ostream &out) const { bool first = true; for (unsigned int i = 0; i < m_usageLines.size(); i++) { const UsageLine &ul = m_usageLines[i]; int j; for (j = 0; j < ul.getCount(); j++) if (!ul[j]->isHidden()) break; if (j == ul.getCount()) continue; out << (first ? "usage: " : " "); printUsageLine(out, ul); first = false; } out << endl; } //--------------------------------------------------------- void UsageImp::print(ostream &out) const { printUsageLines(out); out << endl; unsigned int i; for (i = 0; i < m_qlist.size(); i++) if (!m_qlist[i]->isHidden()) m_qlist[i]->printHelpLine(out); for (i = 0; i < m_args.size(); i++) m_args[i]->printHelpLine(out); out << endl; } //--------------------------------------------------------- void UsageImp::parse(int argc, char *argv[]) { resetValues(); if (argc == 0 || argv[0] == 0) throw UsageError("missing program name"); m_progName = string(argv[0]); unsigned int i; for (i = 1; i < (unsigned int)argc;) if (argv[i][0] == '-') { string qname(argv[i]); qiterator qit = m_qtable.find(qname); if (qit == m_qtable.end()) { string msg = "unknown qualifier '" + qname + "'"; throw UsageError(msg); } Qualifier *qualifier = qit->second; qualifier->fetch(i, argc, argv); qualifier->select(); } else i++; vector usages; for (i = 0; i < m_usageLines.size(); i++) if (hasSwitcher(m_usageLines[i]) && matchSwitcher(m_usageLines[i])) usages.push_back(&m_usageLines[i]); if (usages.empty()) for (i = 0; i < m_usageLines.size(); i++) if (!hasSwitcher(m_usageLines[i])) usages.push_back(&m_usageLines[i]); if (usages.size() == 0) throw UsageError("unrecognized syntax"); if (usages.size() > 1) { int min = InfiniteArgCount; int max = 0; std::vector::iterator it; for (it = usages.begin(); it != usages.end();) { UsageLine &ul = **it; int lmin, lmax; getArgCountRange(ul, 0, ul.getCount() - 1, lmin, lmax); min = tmin(min, lmin); max = tmax(max, lmax); if (matchArgCount(ul, 0, ul.getCount() - 1, argc - 1) == false) it = usages.erase(it); else it++; } if (usages.size() == 0) { if (argc - 1 < min) throw UsageError("missing argument(s)"); else if (argc - 1 > max) throw UsageError("too many arguments"); else throw UsageError("bad argument number"); } else if (usages.size() > 1) throw UsageError("ambiguous syntax"); } if (usages.size() == 1) { UsageLine *found = usages[0]; if (found->getCount() > 0) fetchArguments(*found, 0, found->getCount() - 1, argc, argv); if (argc > 1) throw UsageError("too many arguments"); m_selectedUsageLine = found; } } //--------------------------------------------------------- void UsageImp::fetchArguments( UsageLine &ul, int a, int b, int &argc, char *argv[]) { assert(0 <= a && a <= b && b < ul.getCount()); int i; for (i = a; i <= b; i++) { if (ul[i] == &bra || ul[i]->isMultiArgument()) break; if (ul[i]->isArgument()) { Argument *argument = dynamic_cast(ul[i]); assert(argument); argument->fetch(1, argc, argv); argument->select(); } } if (i > b) { } else if (ul[i] == &bra) { int n = 0; int j; for (j = b; j > i && ul[j] != &ket; j--) if (ul[j]->isArgument()) n++; assert(j > i + 1); if (argc - 1 > n) fetchArguments(ul, i + 1, j - 1, argc, argv); if (j + 1 <= b) fetchArguments(ul, j + 1, b, argc, argv); } else if (ul[i]->isMultiArgument()) { MultiArgument *argument = dynamic_cast(ul[i]); assert(argument); int n = 0; for (int j = i + 1; j <= b; j++) if (ul[j]->isArgument()) n++; int oldArgc = argc; argc -= n; argument->fetch(1, argc, argv); argument->select(); argc += n; if (n > 0) { if (argc < oldArgc) for (int j = n; j > 0; j--) argv[argc - j] = argv[oldArgc - j]; fetchArguments(ul, i + 1, b, argc, argv); } } } //--------------------------------------------------------- bool UsageImp::matchSwitcher(const UsageLine &ul) const { for (int i = 0; i < ul.getCount(); i++) if (ul[i]->isSwitcher() && !ul[i]->isSelected()) return false; return true; } //--------------------------------------------------------- bool UsageImp::hasSwitcher(const UsageLine &ul) const { for (int i = 0; i < ul.getCount(); i++) if (ul[i]->isSwitcher()) return true; return false; } //--------------------------------------------------------- bool UsageImp::matchArgCount(const UsageLine &ul, int a, int b, int argc) const { int n = 0; int i; for (i = a; i <= b; i++) { if (ul[i] == &bra || ul[i]->isMultiArgument()) break; if (ul[i]->isArgument()) n++; } if (i > b) return argc == n; else if (ul[i] == &bra) { int j; for (j = b; j > i && ul[j] != &ket; j--) if (ul[j]->isArgument()) n++; assert(j > i + 1); if (n == argc) return true; else if (n > argc) return false; else return matchArgCount(ul, i + 1, j - 1, argc - n); } else { assert(ul[i]->isMultiArgument()); n++; for (i++; i <= b; i++) if (ul[i]->isArgument()) n++; return argc >= n; } } //--------------------------------------------------------- void UsageImp::getArgCountRange(const UsageLine &ul, int a, int b, int &min, int &max) const { min = max = 0; int n = 0; int i; for (i = a; i <= b; i++) { if (ul[i] == &bra || ul[i]->isMultiArgument()) break; if (ul[i]->isArgument()) n++; } if (i > b) { min = max = n; return; } else if (ul[i] == &bra) { int j; for (j = b; j > i && ul[j] != &ket; j--) if (ul[j]->isArgument()) n++; assert(j > i + 1); min = max = n; int lmin, lmax; getArgCountRange(ul, i + 1, j - 1, lmin, lmax); if (lmax == InfiniteArgCount) max = InfiniteArgCount; else max += lmax; } else { assert(ul[i]->isMultiArgument()); n++; for (i++; i <= b; i++) if (ul[i]->isArgument()) n++; min = n; max = InfiniteArgCount; } } //--------------------------------------------------------- void UsageImp::dumpValues(ostream &out) const { if (m_selectedUsageLine == 0) return; cout << "selected usage: "; printUsageLine(out, *m_selectedUsageLine); unsigned int i; for (i = 0; i < m_qlist.size(); i++) m_qlist[i]->dumpValue(out); for (i = 0; i < m_args.size(); i++) m_args[i]->dumpValue(out); out << endl << endl; } //--------------------------------------------------------- void UsageImp::resetValues() { unsigned int i; for (i = 0; i < m_qlist.size(); i++) m_qlist[i]->resetValue(); for (i = 0; i < m_args.size(); i++) m_args[i]->resetValue(); } //========================================================= // // Usage // //--------------------------------------------------------- Usage::Usage(string progName) { m_imp = new UsageImp(progName); } Usage::~Usage() { delete m_imp; } void Usage::add(const UsageLine &ul) { m_imp->add(ul); } void Usage::print(ostream &out) const { m_imp->print(out); } void Usage::dumpValues(ostream &out) const { m_imp->dumpValues(out); } void Usage::clear() { m_imp->clear(); } bool Usage::parse(const char *argvString, ostream &err) { string s = string(argvString); std::vector argv; int len = s.size(); for (int i = 0; i < len;) { while (s[i] == ' ' || s[i] == '\t') i++; if (i < len) argv.push_back(&s[i]); while (i < len && !(s[i] == ' ' || s[i] == '\t')) i++; if (i < len) s[i++] = '\0'; } return parse(argv.size(), &argv[0], err); } bool Usage::parse(int argc, char *argv[], ostream &err) { try { m_imp->parse(argc, argv); if (help) { print(err); return false; } if (release) { printToonzRelease(err); return false; } if (libRelease) { printLibRelease(err); return false; } return true; } catch (UsageError e) { err << "Usage error: " << e.getError() << endl; m_imp->printUsageLines(err); return false; } } //========================================================= // // RangeQualifier // //--------------------------------------------------------- RangeQualifier::RangeQualifier() : Qualifier("-range from to | -frame fr", "frame range"), m_from(0), m_to(-1) { } //--------------------------------------------------------- void RangeQualifier::fetch(int index, int &argc, char *argv[]) { assert(index < argc); string qname(argv[index]); fetchElement(index, argc, argv); if (qname == "-range") { fetchElement(m_from, index, argc, argv); fetchElement(m_to, index, argc, argv); } else if (qname == "-frame") { fetchElement(m_from, index, argc, argv); m_to = m_from; } else { assert(0); } } ostream &operator<<(ostream &out, const RangeQualifier &range) { return out << "[" << range.getFrom() << ", " << range.getTo() << "]"; } //--------------------------------------------------------- void RangeQualifier::dumpValue(ostream &out) const { out << m_name << " = "; if (m_from <= m_to) out << m_from << ", " << m_to; else out << "undefined"; out << endl; } //--------------------------------------------------------- void RangeQualifier::resetValue() { m_from = 0; m_to = -1; m_selected = false; }