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