An audio player for macOS 10.8 and newer. https://kode54.net/cog
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

358 lines
9.7KB

  1. #include <string.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include "Bml_Parser.h"
  5. const char * strchr_limited( const char * in, const char * end, char c )
  6. {
  7. while ( in < end && *in != c ) ++in;
  8. if ( in < end ) return in;
  9. else return 0;
  10. }
  11. Bml_Node Bml_Node::emptyNode;
  12. Bml_Node::Bml_Node()
  13. {
  14. name = 0;
  15. value = 0;
  16. }
  17. Bml_Node::Bml_Node(char const* name, size_t max_length)
  18. {
  19. size_t length = 0;
  20. char const* ptr = name;
  21. while (*ptr && length < max_length) { ++ptr; ++length; }
  22. this->name = new char[ length + 1 ];
  23. memcpy( this->name, name, length );
  24. this->name[ length ] = '\0';
  25. value = 0;
  26. }
  27. Bml_Node::Bml_Node(const Bml_Node &in)
  28. {
  29. size_t length;
  30. name = 0;
  31. if (in.name)
  32. {
  33. length = strlen(in.name);
  34. name = new char[length + 1];
  35. memcpy(name, in.name, length + 1);
  36. }
  37. value = 0;
  38. if (in.value)
  39. {
  40. length = strlen(in.value);
  41. value = new char[length + 1];
  42. memcpy(value, in.value, length + 1);
  43. }
  44. children = in.children;
  45. }
  46. Bml_Node::~Bml_Node()
  47. {
  48. delete [] name;
  49. delete [] value;
  50. }
  51. void Bml_Node::clear()
  52. {
  53. delete [] name;
  54. delete [] value;
  55. name = 0;
  56. value = 0;
  57. children.resize( 0 );
  58. }
  59. void Bml_Node::setLine(const char *line, size_t max_length)
  60. {
  61. delete [] name;
  62. delete [] value;
  63. name = 0;
  64. value = 0;
  65. size_t length = 0;
  66. const char * end = line;
  67. while (*end && length < max_length) ++end;
  68. const char * line_end = strchr_limited(line, end, '\n');
  69. if ( !line_end ) line_end = end;
  70. const char * first_letter = line;
  71. while ( first_letter < line_end && *first_letter <= 0x20 ) first_letter++;
  72. const char * colon = strchr_limited(first_letter, line_end, ':');
  73. const char * last_letter = line_end - 1;
  74. if (colon)
  75. {
  76. const char * first_value_letter = colon + 1;
  77. while (first_value_letter < line_end && *first_value_letter <= 0x20) first_value_letter++;
  78. last_letter = line_end - 1;
  79. while (last_letter > first_value_letter && *last_letter <= 0x20) last_letter--;
  80. value = new char[last_letter - first_value_letter + 2];
  81. memcpy(value, first_value_letter, last_letter - first_value_letter + 1);
  82. value[last_letter - first_value_letter + 1] = '\0';
  83. last_letter = colon - 1;
  84. }
  85. while (last_letter > first_letter && *last_letter <= 0x20) last_letter--;
  86. name = new char[last_letter - first_letter + 2];
  87. memcpy(name, first_letter, last_letter - first_letter + 1);
  88. name[last_letter - first_letter + 1] = '\0';
  89. }
  90. Bml_Node& Bml_Node::addChild(const Bml_Node &child)
  91. {
  92. children.push_back(child);
  93. return *(children.end() - 1);
  94. }
  95. const char * Bml_Node::getName() const
  96. {
  97. return name;
  98. }
  99. const char * Bml_Node::getValue() const
  100. {
  101. return value;
  102. }
  103. void Bml_Node::setValue(char const* value)
  104. {
  105. delete [] this->value;
  106. size_t length = strlen( value ) + 1;
  107. this->value = new char[ length ];
  108. memcpy( this->value, value, length );
  109. }
  110. size_t Bml_Node::getChildCount() const
  111. {
  112. return children.size();
  113. }
  114. Bml_Node const& Bml_Node::getChild(size_t index) const
  115. {
  116. return children[index];
  117. }
  118. Bml_Node & Bml_Node::walkToNode(const char *path, bool use_indexes)
  119. {
  120. Bml_Node * next_node;
  121. Bml_Node * node = this;
  122. while ( *path )
  123. {
  124. bool item_found = false;
  125. size_t array_index = 0;
  126. const char * array_index_start = strchr( path, '[' );
  127. const char * next_separator = strchr( path, ':' );
  128. if ( !next_separator ) next_separator = path + strlen(path);
  129. if ( use_indexes && array_index_start && array_index_start < next_separator )
  130. {
  131. char * temp;
  132. array_index = strtoul( array_index_start + 1, &temp, 10 );
  133. }
  134. else
  135. {
  136. array_index_start = next_separator;
  137. }
  138. if ( use_indexes )
  139. {
  140. for ( std::vector<Bml_Node>::iterator it = node->children.begin(); it != node->children.end(); ++it )
  141. {
  142. if ( array_index_start - path == strlen(it->name) &&
  143. strncmp( it->name, path, array_index_start - path ) == 0 )
  144. {
  145. next_node = &(*it);
  146. item_found = true;
  147. if ( array_index == 0 ) break;
  148. --array_index;
  149. }
  150. if (array_index)
  151. item_found = false;
  152. }
  153. }
  154. else
  155. {
  156. for ( std::vector<Bml_Node>::iterator it = node->children.end(); it != node->children.begin(); )
  157. {
  158. --it;
  159. if ( next_separator - path == strlen(it->name) &&
  160. strncmp( it->name, path, next_separator - path ) == 0 )
  161. {
  162. next_node = &(*it);
  163. item_found = true;
  164. break;
  165. }
  166. }
  167. }
  168. if ( !item_found )
  169. {
  170. Bml_Node child( path, next_separator - path );
  171. node = &(node->addChild( child ));
  172. }
  173. else
  174. node = next_node;
  175. if ( *next_separator )
  176. {
  177. path = next_separator + 1;
  178. }
  179. else break;
  180. }
  181. return *node;
  182. }
  183. Bml_Node const& Bml_Node::walkToNode(const char *path) const
  184. {
  185. Bml_Node const* next_node;
  186. Bml_Node const* node = this;
  187. while ( *path )
  188. {
  189. bool item_found = false;
  190. size_t array_index = ~0;
  191. const char * array_index_start = strchr( path, '[' );
  192. const char * next_separator = strchr( path, ':' );
  193. if ( !next_separator ) next_separator = path + strlen(path);
  194. if ( array_index_start && array_index_start < next_separator )
  195. {
  196. char * temp;
  197. array_index = strtoul( array_index_start + 1, &temp, 10 );
  198. }
  199. else
  200. {
  201. array_index_start = next_separator;
  202. }
  203. for ( std::vector<Bml_Node>::const_iterator it = node->children.begin(), ite = node->children.end(); it != ite; ++it )
  204. {
  205. if ( array_index_start - path == strlen(it->name) &&
  206. strncmp( it->name, path, array_index_start - path ) == 0 )
  207. {
  208. next_node = &(*it);
  209. item_found = true;
  210. if ( array_index == 0 ) break;
  211. --array_index;
  212. }
  213. }
  214. if ( !item_found ) return emptyNode;
  215. node = next_node;
  216. if ( *next_separator )
  217. {
  218. path = next_separator + 1;
  219. }
  220. else break;
  221. }
  222. return *node;
  223. }
  224. void Bml_Parser::parseDocument( const char * source, size_t max_length )
  225. {
  226. std::vector<size_t> indents;
  227. std::string last_name;
  228. std::string current_path;
  229. document.clear();
  230. size_t last_indent = ~0;
  231. Bml_Node node;
  232. size_t length = 0;
  233. const char * end = source;
  234. while ( *end && length < max_length ) { ++end; ++length; }
  235. while ( source < end )
  236. {
  237. const char * line_end = strchr_limited( source, end, '\n' );
  238. if ( !line_end ) line_end = end;
  239. if ( node.getName() ) last_name = node.getName();
  240. node.setLine( source, line_end - source );
  241. size_t indent = 0;
  242. while ( source < line_end && *source <= 0x20 )
  243. {
  244. source++;
  245. indent++;
  246. }
  247. if ( last_indent == ~0 ) last_indent = indent;
  248. if ( indent > last_indent )
  249. {
  250. indents.push_back( last_indent );
  251. last_indent = indent;
  252. if ( current_path.length() ) current_path += ":";
  253. current_path += last_name;
  254. }
  255. else if ( indent < last_indent )
  256. {
  257. while ( last_indent > indent && indents.size() )
  258. {
  259. last_indent = *(indents.end() - 1);
  260. indents.pop_back();
  261. size_t colon = current_path.find_last_of( ':' );
  262. if ( colon != std::string::npos ) current_path.resize( colon );
  263. else current_path.resize( 0 );
  264. }
  265. last_indent = indent;
  266. }
  267. document.walkToNode( current_path.c_str() ).addChild( node );
  268. source = line_end;
  269. while ( *source && *source == '\n' ) source++;
  270. }
  271. }
  272. const char * Bml_Parser::enumValue(std::string const& path) const
  273. {
  274. return document.walkToNode(path.c_str()).getValue();
  275. }
  276. void Bml_Parser::setValue(std::string const& path, const char *value)
  277. {
  278. document.walkToNode(path.c_str(), true).setValue(value);
  279. }
  280. void Bml_Parser::setValue(std::string const& path, long value)
  281. {
  282. std::ostringstream str;
  283. str << value;
  284. setValue( path, str.str().c_str() );
  285. }
  286. void Bml_Parser::serialize(std::string & out) const
  287. {
  288. std::ostringstream strOut;
  289. serialize(strOut, &document, 0);
  290. out = strOut.str();
  291. }
  292. void Bml_Parser::serialize(std::ostringstream & out, Bml_Node const* node, unsigned int indent) const
  293. {
  294. for (unsigned i = 1; i < indent; ++i) out << " ";
  295. if ( indent )
  296. {
  297. out << node->getName();
  298. if (node->getValue() && strlen(node->getValue())) out << ":" << node->getValue();
  299. out << std::endl;
  300. }
  301. for (size_t i = 0, j = node->getChildCount(); i < j; ++i)
  302. {
  303. Bml_Node const& child = node->getChild(i);
  304. if ( (!child.getValue() || !strlen(child.getValue())) && !child.getChildCount() )
  305. continue;
  306. serialize( out, &child, indent + 1 );
  307. if ( indent == 0 ) out << std::endl;
  308. }
  309. }