aboutsummaryrefslogtreecommitdiff
path: root/src/node.c
blob: 35e19d2c399d0535b091c0371564c53130191cd1 (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. switch (node->type) {
  12. case CMARK_NODE_ATX_HEADER:
  13. case CMARK_NODE_SETEXT_HEADER:
  14. node->as.header.level = 1;
  15. break;
  16. case CMARK_NODE_LIST: {
  17. cmark_list *list = &node->as.list;
  18. list->list_type = CMARK_BULLET_LIST;
  19. list->start = 1;
  20. list->tight = false;
  21. break;
  22. }
  23. default:
  24. break;
  25. }
  26. return node;
  27. }
  28. void
  29. cmark_node_destroy(cmark_node *node) {
  30. S_node_unlink(node);
  31. node->next = NULL;
  32. cmark_free_nodes(node);
  33. }
  34. cmark_node_type
  35. cmark_node_get_type(cmark_node *node)
  36. {
  37. return node->type;
  38. }
  39. static const char*
  40. S_type_string(cmark_node *node)
  41. {
  42. switch (node->type) {
  43. case CMARK_NODE_DOCUMENT: return "DOCUMENT";
  44. case CMARK_NODE_BLOCK_QUOTE: return "BLOCK_QUOTE";
  45. case CMARK_NODE_LIST: return "LIST";
  46. case CMARK_NODE_LIST_ITEM: return "LIST_ITEM";
  47. case CMARK_NODE_FENCED_CODE: return "FENCED_CODE";
  48. case CMARK_NODE_INDENTED_CODE: return "INDENTED_CODE";
  49. case CMARK_NODE_HTML: return "HTML";
  50. case CMARK_NODE_PARAGRAPH: return "PARAGRAPH";
  51. case CMARK_NODE_ATX_HEADER: return "ATX_HEADER";
  52. case CMARK_NODE_SETEXT_HEADER: return "SETEXT_HEADER";
  53. case CMARK_NODE_HRULE: return "HRULE";
  54. case CMARK_NODE_REFERENCE_DEF: return "REFERENCE_DEF";
  55. case CMARK_NODE_STRING: return "STRING";
  56. case CMARK_NODE_SOFTBREAK: return "SOFTBREAK";
  57. case CMARK_NODE_LINEBREAK: return "LINEBREAK";
  58. case CMARK_NODE_INLINE_CODE: return "INLINE_CODE";
  59. case CMARK_NODE_INLINE_HTML: return "INLINE_HTML";
  60. case CMARK_NODE_EMPH: return "EMPH";
  61. case CMARK_NODE_STRONG: return "STRONG";
  62. case CMARK_NODE_LINK: return "LINK";
  63. case CMARK_NODE_IMAGE: return "IMAGE";
  64. }
  65. return "<unknown>";
  66. }
  67. cmark_node*
  68. cmark_node_next(cmark_node *node)
  69. {
  70. return node->next;
  71. }
  72. cmark_node*
  73. cmark_node_previous(cmark_node *node)
  74. {
  75. return node->prev;
  76. }
  77. cmark_node*
  78. cmark_node_parent(cmark_node *node)
  79. {
  80. return node->parent;
  81. }
  82. cmark_node*
  83. cmark_node_first_child(cmark_node *node)
  84. {
  85. return node->first_child;
  86. }
  87. cmark_node*
  88. cmark_node_last_child(cmark_node *node)
  89. {
  90. return node->last_child;
  91. }
  92. static char*
  93. S_strdup(const char *str) {
  94. size_t size = strlen(str) + 1;
  95. char *dup = (char *)malloc(size);
  96. memcpy(dup, str, size);
  97. return dup;
  98. }
  99. const char*
  100. cmark_node_get_string_content(cmark_node *node) {
  101. switch (node->type) {
  102. case NODE_INDENTED_CODE:
  103. case NODE_FENCED_CODE:
  104. case NODE_HTML:
  105. return cmark_strbuf_cstr(&node->string_content);
  106. case NODE_STRING:
  107. case NODE_INLINE_HTML:
  108. case NODE_INLINE_CODE:
  109. return cmark_chunk_to_cstr(&node->as.literal);
  110. default:
  111. break;
  112. }
  113. return NULL;
  114. }
  115. int
  116. cmark_node_set_string_content(cmark_node *node, const char *content) {
  117. switch (node->type) {
  118. case NODE_INDENTED_CODE:
  119. case NODE_FENCED_CODE:
  120. case NODE_HTML:
  121. cmark_strbuf_sets(&node->string_content, content);
  122. return 1;
  123. case NODE_STRING:
  124. case NODE_INLINE_HTML:
  125. case NODE_INLINE_CODE:
  126. cmark_chunk_set_cstr(&node->as.literal, content);
  127. return 1;
  128. default:
  129. break;
  130. }
  131. return 0;
  132. }
  133. int
  134. cmark_node_get_header_level(cmark_node *node) {
  135. switch (node->type) {
  136. case CMARK_NODE_ATX_HEADER:
  137. case CMARK_NODE_SETEXT_HEADER:
  138. return node->as.header.level;
  139. default:
  140. break;
  141. }
  142. return 0;
  143. }
  144. int
  145. cmark_node_set_header_level(cmark_node *node, int level) {
  146. if (level < 1 || level > 6) {
  147. return 0;
  148. }
  149. switch (node->type) {
  150. case CMARK_NODE_ATX_HEADER:
  151. case CMARK_NODE_SETEXT_HEADER:
  152. node->as.header.level = level;
  153. return 1;
  154. default:
  155. break;
  156. }
  157. return 0;
  158. }
  159. cmark_list_type
  160. cmark_node_get_list_type(cmark_node *node) {
  161. if (node->type == CMARK_NODE_LIST) {
  162. return node->as.list.list_type;
  163. }
  164. else {
  165. return CMARK_NO_LIST;
  166. }
  167. }
  168. int
  169. cmark_node_set_list_type(cmark_node *node, cmark_list_type type) {
  170. if (!(type == CMARK_BULLET_LIST || type == CMARK_ORDERED_LIST)) {
  171. return 0;
  172. }
  173. if (node->type == CMARK_NODE_LIST) {
  174. node->as.list.list_type = type;
  175. return 1;
  176. }
  177. else {
  178. return 0;
  179. }
  180. }
  181. int
  182. cmark_node_get_list_start(cmark_node *node) {
  183. if (node->type == CMARK_NODE_LIST) {
  184. return node->as.list.start;
  185. }
  186. else {
  187. return 0;
  188. }
  189. }
  190. int
  191. cmark_node_set_list_start(cmark_node *node, int start) {
  192. if (start < 0) {
  193. return 0;
  194. }
  195. if (node->type == CMARK_NODE_LIST) {
  196. node->as.list.start = start;
  197. return 1;
  198. }
  199. else {
  200. return 0;
  201. }
  202. }
  203. int
  204. cmark_node_get_list_tight(cmark_node *node) {
  205. if (node->type == CMARK_NODE_LIST) {
  206. return node->as.list.tight;
  207. }
  208. else {
  209. return 0;
  210. }
  211. }
  212. int
  213. cmark_node_set_list_tight(cmark_node *node, int tight) {
  214. if (node->type == CMARK_NODE_LIST) {
  215. node->as.list.tight = tight;
  216. return 1;
  217. }
  218. else {
  219. return 0;
  220. }
  221. }
  222. const char*
  223. cmark_node_get_fence_info(cmark_node *node) {
  224. if (node->type == NODE_FENCED_CODE) {
  225. return cmark_strbuf_cstr(&node->as.code.info);
  226. }
  227. else {
  228. return NULL;
  229. }
  230. }
  231. int
  232. cmark_node_set_fence_info(cmark_node *node, const char *info) {
  233. if (node->type == NODE_FENCED_CODE) {
  234. cmark_strbuf_sets(&node->as.code.info, info);
  235. return 1;
  236. }
  237. else {
  238. return 0;
  239. }
  240. }
  241. const char*
  242. cmark_node_get_url(cmark_node *node) {
  243. switch (node->type) {
  244. case NODE_LINK:
  245. case NODE_IMAGE:
  246. return (char *)node->as.link.url;
  247. default:
  248. break;
  249. }
  250. return NULL;
  251. }
  252. int
  253. cmark_node_set_url(cmark_node *node, const char *url) {
  254. switch (node->type) {
  255. case NODE_LINK:
  256. case NODE_IMAGE:
  257. free(node->as.link.url);
  258. node->as.link.url = (unsigned char *)S_strdup(url);
  259. return 1;
  260. default:
  261. break;
  262. }
  263. return 0;
  264. }
  265. const char*
  266. cmark_node_get_title(cmark_node *node) {
  267. switch (node->type) {
  268. case NODE_LINK:
  269. case NODE_IMAGE:
  270. return (char *)node->as.link.title;
  271. default:
  272. break;
  273. }
  274. return NULL;
  275. }
  276. int
  277. cmark_node_set_title(cmark_node *node, const char *title) {
  278. switch (node->type) {
  279. case NODE_LINK:
  280. case NODE_IMAGE:
  281. free(node->as.link.title);
  282. node->as.link.title = (unsigned char *)S_strdup(title);
  283. return 1;
  284. default:
  285. break;
  286. }
  287. return 0;
  288. }
  289. int
  290. cmark_node_get_start_line(cmark_node *node) {
  291. return node->start_line;
  292. }
  293. int
  294. cmark_node_get_start_column(cmark_node *node) {
  295. return node->start_column;
  296. }
  297. int
  298. cmark_node_get_end_line(cmark_node *node) {
  299. return node->end_line;
  300. }
  301. static inline bool
  302. S_is_block(cmark_node *node) {
  303. return node->type >= CMARK_NODE_FIRST_BLOCK
  304. && node->type <= CMARK_NODE_LAST_BLOCK;
  305. }
  306. static inline bool
  307. S_is_inline(cmark_node *node) {
  308. return node->type >= CMARK_NODE_FIRST_INLINE
  309. && node->type <= CMARK_NODE_LAST_INLINE;
  310. }
  311. static bool
  312. S_can_contain(cmark_node *node, cmark_node *child)
  313. {
  314. cmark_node *cur;
  315. // Verify that child is not an ancestor of node or equal to node.
  316. cur = node;
  317. do {
  318. if (cur == child) {
  319. return false;
  320. }
  321. cur = cur->parent;
  322. } while (cur != NULL);
  323. if (child->type == CMARK_NODE_DOCUMENT) {
  324. return false;
  325. }
  326. switch (node->type) {
  327. case CMARK_NODE_DOCUMENT:
  328. case CMARK_NODE_BLOCK_QUOTE:
  329. case CMARK_NODE_LIST_ITEM:
  330. return S_is_block(child)
  331. && child->type != CMARK_NODE_LIST_ITEM;
  332. case CMARK_NODE_LIST:
  333. return child->type == CMARK_NODE_LIST_ITEM;
  334. case CMARK_NODE_PARAGRAPH:
  335. case CMARK_NODE_ATX_HEADER:
  336. case CMARK_NODE_SETEXT_HEADER:
  337. case CMARK_NODE_EMPH:
  338. case CMARK_NODE_STRONG:
  339. case CMARK_NODE_LINK:
  340. case CMARK_NODE_IMAGE:
  341. return S_is_inline(child);
  342. default:
  343. break;
  344. }
  345. return false;
  346. }
  347. // Unlink a node without adjusting its next, prev, and parent pointers.
  348. static void
  349. S_node_unlink(cmark_node *node)
  350. {
  351. if (node->prev) {
  352. node->prev->next = node->next;
  353. }
  354. if (node->next) {
  355. node->next->prev = node->prev;
  356. }
  357. // Adjust first_child and last_child of parent.
  358. cmark_node *parent = node->parent;
  359. if (parent) {
  360. if (parent->first_child == node) {
  361. parent->first_child = node->next;
  362. }
  363. if (parent->last_child == node) {
  364. parent->last_child = node->prev;
  365. }
  366. }
  367. }
  368. void
  369. cmark_node_unlink(cmark_node *node) {
  370. S_node_unlink(node);
  371. node->next = NULL;
  372. node->prev = NULL;
  373. node->parent = NULL;
  374. }
  375. int
  376. cmark_node_insert_before(cmark_node *node, cmark_node *sibling)
  377. {
  378. if (!S_can_contain(node->parent, sibling)) {
  379. return 0;
  380. }
  381. S_node_unlink(sibling);
  382. cmark_node *old_prev = node->prev;
  383. // Insert 'sibling' between 'old_prev' and 'node'.
  384. if (old_prev) {
  385. old_prev->next = sibling;
  386. }
  387. sibling->prev = old_prev;
  388. sibling->next = node;
  389. node->prev = sibling;
  390. // Set new parent.
  391. cmark_node *parent = node->parent;
  392. sibling->parent = parent;
  393. // Adjust first_child of parent if inserted as first child.
  394. if (parent && !old_prev) {
  395. parent->first_child = sibling;
  396. }
  397. return 1;
  398. }
  399. int
  400. cmark_node_insert_after(cmark_node *node, cmark_node *sibling)
  401. {
  402. if (!S_can_contain(node->parent, sibling)) {
  403. return 0;
  404. }
  405. S_node_unlink(sibling);
  406. cmark_node *old_next = node->next;
  407. // Insert 'sibling' between 'node' and 'old_next'.
  408. if (old_next) {
  409. old_next->prev = sibling;
  410. }
  411. sibling->next = old_next;
  412. sibling->prev = node;
  413. node->next = sibling;
  414. // Set new parent.
  415. cmark_node *parent = node->parent;
  416. sibling->parent = parent;
  417. // Adjust last_child of parent if inserted as last child.
  418. if (parent && !old_next) {
  419. parent->last_child = sibling;
  420. }
  421. return 1;
  422. }
  423. int
  424. cmark_node_prepend_child(cmark_node *node, cmark_node *child)
  425. {
  426. if (!S_can_contain(node, child)) {
  427. return 0;
  428. }
  429. S_node_unlink(child);
  430. cmark_node *old_first_child = node->first_child;
  431. child->next = old_first_child;
  432. child->prev = NULL;
  433. child->parent = node;
  434. node->first_child = child;
  435. if (old_first_child) {
  436. old_first_child->prev = child;
  437. }
  438. else {
  439. // Also set last_child if node previously had no children.
  440. node->last_child = child;
  441. }
  442. return 1;
  443. }
  444. int
  445. cmark_node_append_child(cmark_node *node, cmark_node *child)
  446. {
  447. if (!S_can_contain(node, child)) {
  448. return 0;
  449. }
  450. S_node_unlink(child);
  451. cmark_node *old_last_child = node->last_child;
  452. child->next = NULL;
  453. child->prev = old_last_child;
  454. child->parent = node;
  455. node->last_child = child;
  456. if (old_last_child) {
  457. old_last_child->next = child;
  458. }
  459. else {
  460. // Also set first_child if node previously had no children.
  461. node->first_child = child;
  462. }
  463. return 1;
  464. }
  465. static void
  466. S_print_error(FILE *out, cmark_node *node, const char *elem)
  467. {
  468. if (out == NULL) {
  469. return;
  470. }
  471. fprintf(out, "Invalid '%s' in node type %s at %d:%d\n", elem,
  472. S_type_string(node), node->start_line, node->start_column);
  473. }
  474. int
  475. cmark_node_check(cmark_node *node, FILE *out)
  476. {
  477. cmark_node *cur;
  478. int errors = 0;
  479. if (!node) {
  480. return 0;
  481. }
  482. cur = node;
  483. while (true) {
  484. if (cur->first_child) {
  485. if (cur->first_child->parent != cur) {
  486. S_print_error(out, cur->first_child, "parent");
  487. cur->first_child->parent = cur;
  488. ++errors;
  489. }
  490. cur = cur->first_child;
  491. continue;
  492. }
  493. next_sibling:
  494. if (cur == node) {
  495. break;
  496. }
  497. if (cur->next) {
  498. if (cur->next->prev != cur) {
  499. S_print_error(out, cur->next, "prev");
  500. cur->next->prev = cur;
  501. ++errors;
  502. }
  503. if (cur->next->parent != cur->parent) {
  504. S_print_error(out, cur->next, "parent");
  505. cur->next->parent = cur->parent;
  506. ++errors;
  507. }
  508. cur = cur->next;
  509. continue;
  510. }
  511. if (cur->parent->last_child != cur) {
  512. S_print_error(out, cur->parent, "last_child");
  513. cur->parent->last_child = cur;
  514. ++errors;
  515. }
  516. cur = cur->parent;
  517. goto next_sibling;
  518. }
  519. return errors;
  520. }
  521. // Free a cmark_node list and any children.
  522. void cmark_free_nodes(cmark_node *e)
  523. {
  524. cmark_node *next;
  525. while (e != NULL) {
  526. strbuf_free(&e->string_content);
  527. switch (e->type){
  528. case NODE_FENCED_CODE:
  529. strbuf_free(&e->as.code.info);
  530. break;
  531. case NODE_STRING:
  532. case NODE_INLINE_HTML:
  533. case NODE_INLINE_CODE:
  534. cmark_chunk_free(&e->as.literal);
  535. break;
  536. case NODE_LINK:
  537. case NODE_IMAGE:
  538. free(e->as.link.url);
  539. free(e->as.link.title);
  540. break;
  541. default:
  542. break;
  543. }
  544. if (e->last_child) {
  545. // Splice children into list
  546. e->last_child->next = e->next;
  547. e->next = e->first_child;
  548. }
  549. next = e->next;
  550. free(e);
  551. e = next;
  552. }
  553. }