aboutsummaryrefslogtreecommitdiff
path: root/src/node.c
blob: 1b767ae718ec00324c2c6f01c6c34ad75f50ce75 (plain)
  1. #include <stdlib.h>
  2. #include <string.h>
  3. #include "config.h"
  4. #include "node.h"
  5. static void
  6. S_node_unlink(cmark_node *node);
  7. cmark_node*
  8. cmark_node_new(cmark_node_type type) {
  9. cmark_node *node = (cmark_node *)calloc(1, sizeof(*node));
  10. node->type = type;
  11. return node;
  12. }
  13. void
  14. cmark_node_destroy(cmark_node *node) {
  15. S_node_unlink(node);
  16. node->next = NULL;
  17. cmark_free_nodes(node);
  18. }
  19. cmark_node_type
  20. cmark_node_get_type(cmark_node *node)
  21. {
  22. return node->type;
  23. }
  24. static const char*
  25. S_type_string(cmark_node *node)
  26. {
  27. switch (node->type) {
  28. case CMARK_NODE_DOCUMENT: return "DOCUMENT";
  29. case CMARK_NODE_BQUOTE: return "BQUOTE";
  30. case CMARK_NODE_LIST: return "LIST";
  31. case CMARK_NODE_LIST_ITEM: return "LIST_ITEM";
  32. case CMARK_NODE_FENCED_CODE: return "FENCED_CODE";
  33. case CMARK_NODE_INDENTED_CODE: return "INDENTED_CODE";
  34. case CMARK_NODE_HTML: return "HTML";
  35. case CMARK_NODE_PARAGRAPH: return "PARAGRAPH";
  36. case CMARK_NODE_ATX_HEADER: return "ATX_HEADER";
  37. case CMARK_NODE_SETEXT_HEADER: return "SETEXT_HEADER";
  38. case CMARK_NODE_HRULE: return "HRULE";
  39. case CMARK_NODE_REFERENCE_DEF: return "REFERENCE_DEF";
  40. case CMARK_NODE_STRING: return "STRING";
  41. case CMARK_NODE_SOFTBREAK: return "SOFTBREAK";
  42. case CMARK_NODE_LINEBREAK: return "LINEBREAK";
  43. case CMARK_NODE_INLINE_CODE: return "INLINE_CODE";
  44. case CMARK_NODE_INLINE_HTML: return "INLINE_HTML";
  45. case CMARK_NODE_EMPH: return "EMPH";
  46. case CMARK_NODE_STRONG: return "STRONG";
  47. case CMARK_NODE_LINK: return "LINK";
  48. case CMARK_NODE_IMAGE: return "IMAGE";
  49. }
  50. return "<unknown>";
  51. }
  52. cmark_node*
  53. cmark_node_next(cmark_node *node)
  54. {
  55. return node->next;
  56. }
  57. cmark_node*
  58. cmark_node_previous(cmark_node *node)
  59. {
  60. return node->prev;
  61. }
  62. cmark_node*
  63. cmark_node_parent(cmark_node *node)
  64. {
  65. return node->parent;
  66. }
  67. cmark_node*
  68. cmark_node_first_child(cmark_node *node)
  69. {
  70. return node->first_child;
  71. }
  72. cmark_node*
  73. cmark_node_last_child(cmark_node *node)
  74. {
  75. return node->last_child;
  76. }
  77. static char*
  78. S_strdup(const char *str) {
  79. size_t size = strlen(str) + 1;
  80. char *dup = (char *)malloc(size);
  81. memcpy(dup, str, size);
  82. return dup;
  83. }
  84. const char*
  85. cmark_node_get_string_content(cmark_node *node) {
  86. switch (node->type) {
  87. case NODE_INDENTED_CODE:
  88. case NODE_FENCED_CODE:
  89. case NODE_HTML:
  90. return cmark_strbuf_cstr(&node->string_content);
  91. case NODE_STRING:
  92. case NODE_INLINE_HTML:
  93. case NODE_INLINE_CODE:
  94. return cmark_chunk_to_cstr(&node->as.literal);
  95. default:
  96. break;
  97. }
  98. return NULL;
  99. }
  100. int
  101. cmark_node_set_string_content(cmark_node *node, const char *content) {
  102. switch (node->type) {
  103. case NODE_INDENTED_CODE:
  104. case NODE_FENCED_CODE:
  105. case NODE_HTML:
  106. cmark_strbuf_sets(&node->string_content, content);
  107. case NODE_STRING:
  108. case NODE_INLINE_HTML:
  109. case NODE_INLINE_CODE:
  110. cmark_chunk_set_cstr(&node->as.literal, content);
  111. return 1;
  112. default:
  113. break;
  114. }
  115. return 0;
  116. }
  117. const char*
  118. cmark_node_get_url(cmark_node *node) {
  119. switch (node->type) {
  120. case NODE_LINK:
  121. case NODE_IMAGE:
  122. return (char *)node->as.link.url;
  123. default:
  124. break;
  125. }
  126. return NULL;
  127. }
  128. int
  129. cmark_node_set_url(cmark_node *node, const char *url) {
  130. switch (node->type) {
  131. case NODE_LINK:
  132. case NODE_IMAGE:
  133. free(node->as.link.url);
  134. node->as.link.url = (unsigned char *)S_strdup(url);
  135. return 1;
  136. default:
  137. break;
  138. }
  139. return 0;
  140. }
  141. static inline bool
  142. S_is_block(cmark_node *node) {
  143. return node->type >= CMARK_NODE_FIRST_BLOCK
  144. && node->type <= CMARK_NODE_LAST_BLOCK;
  145. }
  146. static inline bool
  147. S_is_inline(cmark_node *node) {
  148. return node->type >= CMARK_NODE_FIRST_INLINE
  149. && node->type <= CMARK_NODE_LAST_INLINE;
  150. }
  151. static bool
  152. S_can_contain(cmark_node *node, cmark_node *child)
  153. {
  154. if (child->type == CMARK_NODE_DOCUMENT) {
  155. return false;
  156. }
  157. switch (node->type) {
  158. case CMARK_NODE_DOCUMENT:
  159. case CMARK_NODE_BQUOTE:
  160. case CMARK_NODE_LIST_ITEM:
  161. return S_is_block(child)
  162. && child->type != CMARK_NODE_LIST_ITEM;
  163. case CMARK_NODE_LIST:
  164. return child->type == CMARK_NODE_LIST_ITEM;
  165. case CMARK_NODE_PARAGRAPH:
  166. case CMARK_NODE_ATX_HEADER:
  167. case CMARK_NODE_SETEXT_HEADER:
  168. case CMARK_NODE_EMPH:
  169. case CMARK_NODE_STRONG:
  170. case CMARK_NODE_LINK:
  171. case CMARK_NODE_IMAGE:
  172. return S_is_inline(child);
  173. default:
  174. break;
  175. }
  176. return false;
  177. }
  178. // Unlink a node without adjusting its next, prev, and parent pointers.
  179. static void
  180. S_node_unlink(cmark_node *node)
  181. {
  182. if (node->prev) {
  183. node->prev->next = node->next;
  184. }
  185. if (node->next) {
  186. node->next->prev = node->prev;
  187. }
  188. // Adjust first_child and last_child of parent.
  189. cmark_node *parent = node->parent;
  190. if (parent) {
  191. if (parent->first_child == node) {
  192. parent->first_child = node->next;
  193. }
  194. if (parent->last_child == node) {
  195. parent->last_child = node->prev;
  196. }
  197. }
  198. }
  199. void
  200. cmark_node_unlink(cmark_node *node) {
  201. S_node_unlink(node);
  202. node->next = NULL;
  203. node->prev = NULL;
  204. node->parent = NULL;
  205. }
  206. int
  207. cmark_node_insert_before(cmark_node *node, cmark_node *sibling)
  208. {
  209. if (!S_can_contain(node->parent, sibling)) {
  210. return 0;
  211. }
  212. S_node_unlink(sibling);
  213. cmark_node *old_prev = node->prev;
  214. // Insert 'sibling' between 'old_prev' and 'node'.
  215. if (old_prev) {
  216. old_prev->next = sibling;
  217. }
  218. sibling->prev = old_prev;
  219. sibling->next = node;
  220. node->prev = sibling;
  221. // Set new parent.
  222. cmark_node *parent = node->parent;
  223. sibling->parent = parent;
  224. // Adjust first_child of parent if inserted as first child.
  225. if (parent && !old_prev) {
  226. parent->first_child = sibling;
  227. }
  228. return 1;
  229. }
  230. int
  231. cmark_node_insert_after(cmark_node *node, cmark_node *sibling)
  232. {
  233. if (!S_can_contain(node->parent, sibling)) {
  234. return 0;
  235. }
  236. S_node_unlink(sibling);
  237. cmark_node *old_next = node->next;
  238. // Insert 'sibling' between 'node' and 'old_next'.
  239. if (old_next) {
  240. old_next->prev = sibling;
  241. }
  242. sibling->next = old_next;
  243. sibling->prev = node;
  244. node->next = sibling;
  245. // Set new parent.
  246. cmark_node *parent = node->parent;
  247. sibling->parent = parent;
  248. // Adjust last_child of parent if inserted as last child.
  249. if (parent && !old_next) {
  250. parent->last_child = sibling;
  251. }
  252. return 1;
  253. }
  254. int
  255. cmark_node_prepend_child(cmark_node *node, cmark_node *child)
  256. {
  257. if (!S_can_contain(node, child)) {
  258. return 0;
  259. }
  260. S_node_unlink(child);
  261. cmark_node *old_first_child = node->first_child;
  262. child->next = old_first_child;
  263. child->prev = NULL;
  264. child->parent = node;
  265. node->first_child = child;
  266. if (old_first_child) {
  267. old_first_child->prev = child;
  268. }
  269. else {
  270. // Also set last_child if node previously had no children.
  271. node->last_child = child;
  272. }
  273. return 1;
  274. }
  275. int
  276. cmark_node_append_child(cmark_node *node, cmark_node *child)
  277. {
  278. if (!S_can_contain(node, child)) {
  279. return 0;
  280. }
  281. S_node_unlink(child);
  282. cmark_node *old_last_child = node->last_child;
  283. child->next = NULL;
  284. child->prev = old_last_child;
  285. child->parent = node;
  286. node->last_child = child;
  287. if (old_last_child) {
  288. old_last_child->next = child;
  289. }
  290. else {
  291. // Also set first_child if node previously had no children.
  292. node->first_child = child;
  293. }
  294. return 1;
  295. }
  296. static void
  297. S_print_error(cmark_node *node, const char *elem)
  298. {
  299. fprintf(stderr, "Invalid '%s' in node type %s at %d:%d\n", elem,
  300. S_type_string(node), node->start_line, node->start_column);
  301. }
  302. int
  303. cmark_node_check(cmark_node *node)
  304. {
  305. cmark_node *cur = node;
  306. int errors = 0;
  307. while (cur) {
  308. if (cur->first_child) {
  309. if (cur->first_child->parent != cur) {
  310. S_print_error(cur->first_child, "parent");
  311. cur->first_child->parent = cur;
  312. ++errors;
  313. }
  314. cur = cur->first_child;
  315. }
  316. else if (cur->next) {
  317. if (cur->next->prev != cur) {
  318. S_print_error(cur->next, "prev");
  319. cur->next->prev = cur;
  320. ++errors;
  321. }
  322. if (cur->next->parent != cur->parent) {
  323. S_print_error(cur->next, "parent");
  324. cur->next->parent = cur->parent;
  325. ++errors;
  326. }
  327. cur = cur->next;
  328. }
  329. else {
  330. if (cur->parent->last_child != cur) {
  331. S_print_error(cur->parent, "last_child");
  332. cur->parent->last_child = cur;
  333. ++errors;
  334. }
  335. cmark_node *ancestor = cur->parent;
  336. cur = NULL;
  337. while (ancestor != node->parent) {
  338. if (ancestor->next) {
  339. cur = ancestor->next;
  340. break;
  341. }
  342. ancestor = ancestor->parent;
  343. }
  344. }
  345. }
  346. return errors;
  347. }
  348. // Free a cmark_node list and any children.
  349. void cmark_free_nodes(cmark_node *e)
  350. {
  351. cmark_node *next;
  352. while (e != NULL) {
  353. strbuf_free(&e->string_content);
  354. switch (e->type){
  355. case NODE_FENCED_CODE:
  356. strbuf_free(&e->as.code.info);
  357. break;
  358. case NODE_STRING:
  359. case NODE_INLINE_HTML:
  360. case NODE_INLINE_CODE:
  361. cmark_chunk_free(&e->as.literal);
  362. break;
  363. case NODE_LINK:
  364. case NODE_IMAGE:
  365. free(e->as.link.url);
  366. free(e->as.link.title);
  367. break;
  368. default:
  369. break;
  370. }
  371. if (e->last_child) {
  372. // Splice children into list
  373. e->last_child->next = e->next;
  374. e->next = e->first_child;
  375. }
  376. next = e->next;
  377. free(e);
  378. e = next;
  379. }
  380. }