aboutsummaryrefslogtreecommitdiff
path: root/src/node.c
blob: 190deb990aa7b6319eb8048f4460b0238a82b923 (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. return 1;
  108. case NODE_STRING:
  109. case NODE_INLINE_HTML:
  110. case NODE_INLINE_CODE:
  111. cmark_chunk_set_cstr(&node->as.literal, content);
  112. return 1;
  113. default:
  114. break;
  115. }
  116. return 0;
  117. }
  118. int
  119. cmark_node_get_header_level(cmark_node *node) {
  120. switch (node->type) {
  121. case CMARK_NODE_ATX_HEADER:
  122. case CMARK_NODE_SETEXT_HEADER:
  123. return node->as.header.level;
  124. default:
  125. break;
  126. }
  127. return 0;
  128. }
  129. int
  130. cmark_node_set_header_level(cmark_node *node, int level) {
  131. switch (node->type) {
  132. case CMARK_NODE_ATX_HEADER:
  133. case CMARK_NODE_SETEXT_HEADER:
  134. node->as.header.level = level;
  135. return 1;
  136. default:
  137. break;
  138. }
  139. return 0;
  140. }
  141. cmark_list_type
  142. cmark_node_get_list_type(cmark_node *node) {
  143. if (node->type == CMARK_NODE_LIST) {
  144. return node->as.list.list_type;
  145. }
  146. else {
  147. return CMARK_NO_LIST;
  148. }
  149. }
  150. int
  151. cmark_node_set_list_type(cmark_node *node, cmark_list_type type) {
  152. if (node->type == CMARK_NODE_LIST) {
  153. node->as.list.list_type = type;
  154. return 1;
  155. }
  156. else {
  157. return 0;
  158. }
  159. }
  160. int
  161. cmark_node_get_list_start(cmark_node *node) {
  162. if (node->type == CMARK_NODE_LIST) {
  163. return node->as.list.start;
  164. }
  165. else {
  166. return 0;
  167. }
  168. }
  169. int
  170. cmark_node_set_list_start(cmark_node *node, int start) {
  171. if (node->type == CMARK_NODE_LIST) {
  172. node->as.list.start = start;
  173. return 1;
  174. }
  175. else {
  176. return 0;
  177. }
  178. }
  179. int
  180. cmark_node_get_list_tight(cmark_node *node) {
  181. if (node->type == CMARK_NODE_LIST) {
  182. return node->as.list.tight;
  183. }
  184. else {
  185. return 0;
  186. }
  187. }
  188. int
  189. cmark_node_set_list_tight(cmark_node *node, int tight) {
  190. if (node->type == CMARK_NODE_LIST) {
  191. node->as.list.tight = tight;
  192. return 1;
  193. }
  194. else {
  195. return 0;
  196. }
  197. }
  198. const char*
  199. cmark_node_get_fence_info(cmark_node *node) {
  200. if (node->type == NODE_FENCED_CODE) {
  201. return cmark_strbuf_cstr(&node->as.code.info);
  202. }
  203. else {
  204. return NULL;
  205. }
  206. }
  207. int
  208. cmark_node_set_fence_info(cmark_node *node, const char *info) {
  209. if (node->type == NODE_FENCED_CODE) {
  210. cmark_strbuf_sets(&node->as.code.info, info);
  211. return 1;
  212. }
  213. else {
  214. return 0;
  215. }
  216. }
  217. const char*
  218. cmark_node_get_url(cmark_node *node) {
  219. switch (node->type) {
  220. case NODE_LINK:
  221. case NODE_IMAGE:
  222. return (char *)node->as.link.url;
  223. default:
  224. break;
  225. }
  226. return NULL;
  227. }
  228. int
  229. cmark_node_set_url(cmark_node *node, const char *url) {
  230. switch (node->type) {
  231. case NODE_LINK:
  232. case NODE_IMAGE:
  233. free(node->as.link.url);
  234. node->as.link.url = (unsigned char *)S_strdup(url);
  235. return 1;
  236. default:
  237. break;
  238. }
  239. return 0;
  240. }
  241. const char*
  242. cmark_node_get_title(cmark_node *node) {
  243. switch (node->type) {
  244. case NODE_LINK:
  245. case NODE_IMAGE:
  246. return (char *)node->as.link.title;
  247. default:
  248. break;
  249. }
  250. return NULL;
  251. }
  252. int
  253. cmark_node_set_title(cmark_node *node, const char *title) {
  254. switch (node->type) {
  255. case NODE_LINK:
  256. case NODE_IMAGE:
  257. free(node->as.link.title);
  258. node->as.link.title = (unsigned char *)S_strdup(title);
  259. return 1;
  260. default:
  261. break;
  262. }
  263. return 0;
  264. }
  265. int
  266. cmark_node_get_start_line(cmark_node *node) {
  267. return node->start_line;
  268. }
  269. int
  270. cmark_node_get_start_column(cmark_node *node) {
  271. return node->start_column;
  272. }
  273. int
  274. cmark_node_get_end_line(cmark_node *node) {
  275. return node->end_line;
  276. }
  277. static inline bool
  278. S_is_block(cmark_node *node) {
  279. return node->type >= CMARK_NODE_FIRST_BLOCK
  280. && node->type <= CMARK_NODE_LAST_BLOCK;
  281. }
  282. static inline bool
  283. S_is_inline(cmark_node *node) {
  284. return node->type >= CMARK_NODE_FIRST_INLINE
  285. && node->type <= CMARK_NODE_LAST_INLINE;
  286. }
  287. static bool
  288. S_can_contain(cmark_node *node, cmark_node *child)
  289. {
  290. if (child->type == CMARK_NODE_DOCUMENT) {
  291. return false;
  292. }
  293. switch (node->type) {
  294. case CMARK_NODE_DOCUMENT:
  295. case CMARK_NODE_BQUOTE:
  296. case CMARK_NODE_LIST_ITEM:
  297. return S_is_block(child)
  298. && child->type != CMARK_NODE_LIST_ITEM;
  299. case CMARK_NODE_LIST:
  300. return child->type == CMARK_NODE_LIST_ITEM;
  301. case CMARK_NODE_PARAGRAPH:
  302. case CMARK_NODE_ATX_HEADER:
  303. case CMARK_NODE_SETEXT_HEADER:
  304. case CMARK_NODE_EMPH:
  305. case CMARK_NODE_STRONG:
  306. case CMARK_NODE_LINK:
  307. case CMARK_NODE_IMAGE:
  308. return S_is_inline(child);
  309. default:
  310. break;
  311. }
  312. return false;
  313. }
  314. // Unlink a node without adjusting its next, prev, and parent pointers.
  315. static void
  316. S_node_unlink(cmark_node *node)
  317. {
  318. if (node->prev) {
  319. node->prev->next = node->next;
  320. }
  321. if (node->next) {
  322. node->next->prev = node->prev;
  323. }
  324. // Adjust first_child and last_child of parent.
  325. cmark_node *parent = node->parent;
  326. if (parent) {
  327. if (parent->first_child == node) {
  328. parent->first_child = node->next;
  329. }
  330. if (parent->last_child == node) {
  331. parent->last_child = node->prev;
  332. }
  333. }
  334. }
  335. void
  336. cmark_node_unlink(cmark_node *node) {
  337. S_node_unlink(node);
  338. node->next = NULL;
  339. node->prev = NULL;
  340. node->parent = NULL;
  341. }
  342. int
  343. cmark_node_insert_before(cmark_node *node, cmark_node *sibling)
  344. {
  345. if (!S_can_contain(node->parent, sibling)) {
  346. return 0;
  347. }
  348. S_node_unlink(sibling);
  349. cmark_node *old_prev = node->prev;
  350. // Insert 'sibling' between 'old_prev' and 'node'.
  351. if (old_prev) {
  352. old_prev->next = sibling;
  353. }
  354. sibling->prev = old_prev;
  355. sibling->next = node;
  356. node->prev = sibling;
  357. // Set new parent.
  358. cmark_node *parent = node->parent;
  359. sibling->parent = parent;
  360. // Adjust first_child of parent if inserted as first child.
  361. if (parent && !old_prev) {
  362. parent->first_child = sibling;
  363. }
  364. return 1;
  365. }
  366. int
  367. cmark_node_insert_after(cmark_node *node, cmark_node *sibling)
  368. {
  369. if (!S_can_contain(node->parent, sibling)) {
  370. return 0;
  371. }
  372. S_node_unlink(sibling);
  373. cmark_node *old_next = node->next;
  374. // Insert 'sibling' between 'node' and 'old_next'.
  375. if (old_next) {
  376. old_next->prev = sibling;
  377. }
  378. sibling->next = old_next;
  379. sibling->prev = node;
  380. node->next = sibling;
  381. // Set new parent.
  382. cmark_node *parent = node->parent;
  383. sibling->parent = parent;
  384. // Adjust last_child of parent if inserted as last child.
  385. if (parent && !old_next) {
  386. parent->last_child = sibling;
  387. }
  388. return 1;
  389. }
  390. int
  391. cmark_node_prepend_child(cmark_node *node, cmark_node *child)
  392. {
  393. if (!S_can_contain(node, child)) {
  394. return 0;
  395. }
  396. S_node_unlink(child);
  397. cmark_node *old_first_child = node->first_child;
  398. child->next = old_first_child;
  399. child->prev = NULL;
  400. child->parent = node;
  401. node->first_child = child;
  402. if (old_first_child) {
  403. old_first_child->prev = child;
  404. }
  405. else {
  406. // Also set last_child if node previously had no children.
  407. node->last_child = child;
  408. }
  409. return 1;
  410. }
  411. int
  412. cmark_node_append_child(cmark_node *node, cmark_node *child)
  413. {
  414. if (!S_can_contain(node, child)) {
  415. return 0;
  416. }
  417. S_node_unlink(child);
  418. cmark_node *old_last_child = node->last_child;
  419. child->next = NULL;
  420. child->prev = old_last_child;
  421. child->parent = node;
  422. node->last_child = child;
  423. if (old_last_child) {
  424. old_last_child->next = child;
  425. }
  426. else {
  427. // Also set first_child if node previously had no children.
  428. node->first_child = child;
  429. }
  430. return 1;
  431. }
  432. static void
  433. S_print_error(cmark_node *node, const char *elem)
  434. {
  435. fprintf(stderr, "Invalid '%s' in node type %s at %d:%d\n", elem,
  436. S_type_string(node), node->start_line, node->start_column);
  437. }
  438. int
  439. cmark_node_check(cmark_node *node)
  440. {
  441. cmark_node *cur = node;
  442. int errors = 0;
  443. while (cur) {
  444. if (cur->first_child) {
  445. if (cur->first_child->parent != cur) {
  446. S_print_error(cur->first_child, "parent");
  447. cur->first_child->parent = cur;
  448. ++errors;
  449. }
  450. cur = cur->first_child;
  451. }
  452. else if (cur->next) {
  453. if (cur->next->prev != cur) {
  454. S_print_error(cur->next, "prev");
  455. cur->next->prev = cur;
  456. ++errors;
  457. }
  458. if (cur->next->parent != cur->parent) {
  459. S_print_error(cur->next, "parent");
  460. cur->next->parent = cur->parent;
  461. ++errors;
  462. }
  463. cur = cur->next;
  464. }
  465. else {
  466. if (cur->parent->last_child != cur) {
  467. S_print_error(cur->parent, "last_child");
  468. cur->parent->last_child = cur;
  469. ++errors;
  470. }
  471. cmark_node *ancestor = cur->parent;
  472. cur = NULL;
  473. while (ancestor != node->parent) {
  474. if (ancestor->next) {
  475. cur = ancestor->next;
  476. break;
  477. }
  478. ancestor = ancestor->parent;
  479. }
  480. }
  481. }
  482. return errors;
  483. }
  484. // Free a cmark_node list and any children.
  485. void cmark_free_nodes(cmark_node *e)
  486. {
  487. cmark_node *next;
  488. while (e != NULL) {
  489. strbuf_free(&e->string_content);
  490. switch (e->type){
  491. case NODE_FENCED_CODE:
  492. strbuf_free(&e->as.code.info);
  493. break;
  494. case NODE_STRING:
  495. case NODE_INLINE_HTML:
  496. case NODE_INLINE_CODE:
  497. cmark_chunk_free(&e->as.literal);
  498. break;
  499. case NODE_LINK:
  500. case NODE_IMAGE:
  501. free(e->as.link.url);
  502. free(e->as.link.title);
  503. break;
  504. default:
  505. break;
  506. }
  507. if (e->last_child) {
  508. // Splice children into list
  509. e->last_child->next = e->next;
  510. e->next = e->first_child;
  511. }
  512. next = e->next;
  513. free(e);
  514. e = next;
  515. }
  516. }