70 |
70 |
71 void writeSeparator() { |
71 void writeSeparator() { |
72 output << std::endl; |
72 output << std::endl; |
73 } |
73 } |
74 |
74 |
|
75 bool between(wchar_t ch, wchar_t start, wchar_t end) { |
|
76 return ch >= start && ch <= end; |
|
77 } |
|
78 |
|
79 bool isValidNameCharacter(wchar_t ch, bool first) { |
|
80 if (first) { |
|
81 // also '%' is technically valid here, but it is used for special |
|
82 // purposes like the relation name or attribute types |
|
83 return /**/between(ch, L'a', L'z') |
|
84 || between(ch, L'A', L'Z'); |
|
85 } else { |
|
86 return ch == L'_' |
|
87 || between(ch, L'a', L'z') |
|
88 || between(ch, L'A', L'Z') |
|
89 || between(ch, L'0', L'9'); |
|
90 } |
|
91 } |
|
92 |
75 void writeAttribute(const string_t& name, const TypeId& type, const string_t& value) { |
93 void writeAttribute(const string_t& name, const TypeId& type, const string_t& value) { |
76 // FIXME: escaping/filtering |
94 // TODO: multiple escapting mode - including one that is not lossless |
77 output << convertor.to_bytes(name) << ": "; |
95 // but allows writing a single '_' inside the name |
|
96 for (size_t i = 0, limit = name.size(); i < limit; i++) { |
|
97 wchar_t ch = name[i]; |
|
98 bool valid = isValidNameCharacter(ch, i == 0); |
|
99 |
|
100 // Not a lossless round-trip |
|
101 // (maybe we could sacrifice some reserved prefix): |
|
102 if (i == 0 && !valid) output << 'x'; |
|
103 |
|
104 if (ch == '_') output << "__"; |
|
105 else if (valid) output << convertor.to_bytes(ch); |
|
106 else output << '_' << ((uint32_t) ch) << '_'; |
|
107 } |
|
108 output << ": "; |
78 |
109 |
79 for (char ch : convertor.to_bytes(value)) { |
110 for (char ch : convertor.to_bytes(value)) { |
80 output << ch; |
111 output << ch; |
81 if (ch == '\n') output << "+ "; |
112 if (ch == '\n') output << "+ "; |
82 } |
113 } |