#include "TclQuote.h" ///////////////////////////////////////////////////////////////////// // Misc Support ///////////////////////////////////////////////////////////////////// std::string itoa(int i) { char buffer[30]; sprintf(buffer, "%d", i); return buffer; } std::string ltoa(long i) { char buffer[30]; sprintf(buffer, "%ld", i); return buffer; } std::string ultoa(unsigned long i) { char buffer[30]; sprintf(buffer, "%lu", i); return buffer; } std::string lltoa(long long i) { char buffer[45]; sprintf(buffer, "%lld", i); return buffer; } std::string ulltoa(unsigned long long i) { char buffer[45]; sprintf(buffer, "%llu", i); return buffer; } ///////////////////////////////////////////////////////////////////// // TCL quoting ///////////////////////////////////////////////////////////////////// // For the unquoting rules, see http://tmml.sourceforge.net/doc/tcl/Tcl.html std::string tclQuote(const std::string s) { const int inputLength = s.length(); if (inputLength == 0) { return "{}"; } bool requireSlashQuote = false; bool requireBraceQuote = false; unsigned int braceCount = 0; for (int i = 0; (i < inputLength) && !requireSlashQuote; i++) { unsigned char ch = s[i]; if ((s[i] < 32) || (s[i] >= 127)) { // Quote all unprintable characters. requireSlashQuote = true; } else { switch (s[i]) { case ' ': { requireBraceQuote = true; break; } case '\\': { if (i + 1 == inputLength) { requireSlashQuote = true; } else { i++; } break; } case '{': { braceCount++; break; } case '}': { if (braceCount) { braceCount--; } else { requireSlashQuote = true; } break; } } } } if (braceCount) { // The braces did not match. requireSlashQuote = true; } if (requireSlashQuote) { // This is the most comprehensive quoting solution. It can quote // anything. But the input can grow to 4 times the original size. // If applied recursively, the size could double each time. std::string result; result.reserve(s.length() * 4); for (int i = 0; i < inputLength; i++) { unsigned char ch = s[i]; switch (ch) { // Some of this effort is not required. \a does not need // to be quoted at all. \t could be quoted just like a brace // or an unprintable character. We go out of our way here to // make things more readable to a human. Also, sometimes it is // convenient to the programmer to get rid or \n and \r, // so the main input loop can use something simple like a gets(). case '\a': result += "\\a"; break; case '\b': result += "\\b"; break; case '\f': result += "\\f"; break; case '\n': result += "\\n"; break; case '\r': result += "\\r"; break; case '\t': result += "\\t"; break; case '\v': result += "\\v"; break; case ' ': case '\\': case '{': case '}': result +='\\'; result += ch; break; default: if ((ch < 32) || (ch >= 127)) { // By the time we get here we know that we could include the // character as is and TCL would not complain. But this // is convenient to the human reader. char buffer[] = "\\000"; // We always print these in octal and we always use 3 digits. // This is the most precise format used by TCL. 12\0034 will // be four characters long. '1', '2', (char)3, '4'. // 12x034 on the other hand is interpreted as '1', '2', // (int)34. The 0 is ignored. Any number of characters // would be ignored in this position. sprintf(buffer+1, "%03hho", ch); result += buffer; } else { result += ch; } break; } } result.reserve(result.length()); return result; } else if (requireBraceQuote) { return '{' + s + '}'; } else { return s; } } // Used for unit testing. static std::string testTcl() { std::string inputs[20]; inputs[0] = "All chars"; for (int j = 0; j < 256; j++) { inputs[1] += (char)j; } // should use slash quoting. See the code for each special case. inputs[2] = "Empty string"; // {} inputs[4] = "Simple value"; inputs[5] = "simple_value"; // simple_value inputs[6] = "Simple list"; inputs[7] = "a b c"; // {a b c} // just add { and } inputs[8] = "Recursive list"; inputs[9] = "{a b} {{c d} {e f}} {} {g h}"; // {{a b} {{c d} {e f}} {} {g h}} // just add { and } inputs[10] = "Fancy recursive list"; inputs[11] = "{a b} \\\\ \\} \\{ {c d}"; // {{a b} \\ \} \{ {c d}} // just add { and } inputs[12] = "Ends with slash"; inputs[13] = "a bc\\"; // a\ bc\\ // slash quoting of final slash and space inputs[14] = "Too many opens"; inputs[15] = "{{{}}"; // \{\{\{\}\} inputs[16] = "Too many closes"; inputs[17] = "{{}}}"; // \{\{\}\}\} inputs[18] = "Wrong order"; inputs[19] = "{}}{{}"; // \{\}\}\{\{\} return tclList(inputs, &inputs[20]); } void TclList::addSeperator() { if (!_s.empty()) { _s += ' '; } } TclList::operator std::string() const { return _s; } TclList &TclList::operator <<(std::string s) { addSeperator(); _s += tclQuote(s); return *this; } TclList &TclList::operator <<(char ch) { addSeperator(); _s += tclQuote(std::string(1, ch)); return *this; } TclList &TclList::operator <<(unsigned char ch) { addSeperator(); _s += tclQuote(std::string(1, ch)); return *this; } TclList &TclList::operator <<(int i) { addSeperator(); _s += itoa(i); return *this; } TclList &TclList::operator <<(unsigned int i) { addSeperator(); _s += ultoa(i); return *this; } TclList &TclList::operator <<(long int i) { addSeperator(); _s += ltoa(i); return *this; } TclList &TclList::operator <<(unsigned long int i) { addSeperator(); _s += ultoa(i); return *this; } TclList &TclList::operator <<(long long int i) { addSeperator(); _s += lltoa(i); return *this; } TclList &TclList::operator <<(unsigned long long int i) { addSeperator(); _s += ulltoa(i); return *this; } void TclList::clear() { _s.clear(); }