1 /* utility_functions.c - List manipulation functions, element
2 * constructors, and macro definitions for leg markdown parser. */
3
4 #include "utility_functions.h"
5 #include "markdown_peg.h"
6
7 #include <string.h>
8 #include <assert.h>
9
10
11 /**********************************************************************
12
13 List manipulation functions
14
15 ***********************************************************************/
16
17 /* cons - cons an element onto a list, returning pointer to new head */
cons(element * new,element * list)18 element * cons(element *new, element *list) {
19 assert(new != NULL);
20 new->next = list;
21 return new;
22 }
23
24 /* reverse - reverse a list, returning pointer to new list */
reverse(element * list)25 element *reverse(element *list) {
26 element *new = NULL;
27 element *next = NULL;
28 while (list != NULL) {
29 next = list->next;
30 new = cons(list, new);
31 list = next;
32 }
33 return new;
34 }
35
36 /* concat_string_list - concatenates string contents of list of STR elements.
37 * Frees STR elements as they are added to the concatenation. */
concat_string_list(element * list)38 GString *concat_string_list(element *list) {
39 GString *result;
40 element *next;
41 result = g_string_new("");
42 while (list != NULL) {
43 assert(list->key == STR);
44 assert(list->contents.str != NULL);
45 g_string_append(result, list->contents.str);
46 next = list->next;
47 free_element(list);
48 list = next;
49 }
50 return result;
51 }
52
53 /**********************************************************************
54
55 Global variables used in parsing
56
57 ***********************************************************************/
58
59 char *charbuf = ""; /* Buffer of characters to be parsed. */
60 element *references = NULL; /* List of link references found. */
61 element *notes = NULL; /* List of footnotes found. */
62 element *parse_result; /* Results of parse. */
63 int syntax_extensions; /* Syntax extensions selected. */
64
65 /**********************************************************************
66
67 Auxiliary functions for parsing actions.
68 These make it easier to build up data structures (including lists)
69 in the parsing actions.
70
71 ***********************************************************************/
72
73 /* mk_element - generic constructor for element */
mk_element(int key)74 element * mk_element(int key) {
75 element *result = malloc(sizeof(element));
76 result->key = key;
77 result->children = NULL;
78 result->next = NULL;
79 result->contents.str = NULL;
80 return result;
81 }
82
83 /* mk_str - constructor for STR element */
mk_str(char * string)84 element * mk_str(char *string) {
85 element *result;
86 assert(string != NULL);
87 result = mk_element(STR);
88 result->contents.str = strdup(string);
89 return result;
90 }
91
92 /* mk_str_from_list - makes STR element by concatenating a
93 * reversed list of strings, adding optional extra newline */
mk_str_from_list(element * list,bool extra_newline)94 element * mk_str_from_list(element *list, bool extra_newline) {
95 element *result;
96 GString *c = concat_string_list(reverse(list));
97 if (extra_newline)
98 g_string_append(c, "\n");
99 result = mk_element(STR);
100 result->contents.str = c->str;
101 g_string_free(c, false);
102 return result;
103 }
104
105 /* mk_list - makes new list with key 'key' and children the reverse of 'lst'.
106 * This is designed to be used with cons to build lists in a parser action.
107 * The reversing is necessary because cons adds to the head of a list. */
mk_list(int key,element * lst)108 element * mk_list(int key, element *lst) {
109 element *result;
110 result = mk_element(key);
111 result->children = reverse(lst);
112 return result;
113 }
114
115 /* mk_link - constructor for LINK element */
mk_link(element * label,char * url,char * title)116 element * mk_link(element *label, char *url, char *title) {
117 element *result;
118 result = mk_element(LINK);
119 result->contents.link = malloc(sizeof(link));
120 result->contents.link->label = label;
121 result->contents.link->url = strdup(url);
122 result->contents.link->title = strdup(title);
123 return result;
124 }
125
126 /* extension = returns true if extension is selected */
extension(int ext)127 bool extension(int ext) {
128 return (syntax_extensions & ext);
129 }
130
131 /* match_inlines - returns true if inline lists match (case-insensitive...) */
match_inlines(element * l1,element * l2)132 bool match_inlines(element *l1, element *l2) {
133 while (l1 != NULL && l2 != NULL) {
134 if (l1->key != l2->key)
135 return false;
136 switch (l1->key) {
137 case SPACE:
138 case LINEBREAK:
139 case ELLIPSIS:
140 case EMDASH:
141 case ENDASH:
142 case APOSTROPHE:
143 break;
144 case CODE:
145 case STR:
146 case HTML:
147 if (strcasecmp(l1->contents.str, l2->contents.str) == 0)
148 break;
149 else
150 return false;
151 case EMPH:
152 case STRONG:
153 case LIST:
154 case SINGLEQUOTED:
155 case DOUBLEQUOTED:
156 if (match_inlines(l1->children, l2->children))
157 break;
158 else
159 return false;
160 case LINK:
161 case IMAGE:
162 return false; /* No links or images within links */
163 default:
164 fprintf(stderr, "match_inlines encountered unknown key = %d\n", l1->key);
165 exit(EXIT_FAILURE);
166 break;
167 }
168 l1 = l1->next;
169 l2 = l2->next;
170 }
171 return (l1 == NULL && l2 == NULL); /* return true if both lists exhausted */
172 }
173
174 /* find_reference - return true if link found in references matching label.
175 * 'link' is modified with the matching url and title. */
find_reference(link * result,element * label)176 bool find_reference(link *result, element *label) {
177 element *cur = references; /* pointer to walk up list of references */
178 link *curitem;
179 while (cur != NULL) {
180 curitem = cur->contents.link;
181 if (match_inlines(label, curitem->label)) {
182 *result = *curitem;
183 return true;
184 }
185 else
186 cur = cur->next;
187 }
188 return false;
189 }
190
191 /* find_note - return true if note found in notes matching label.
192 if found, 'result' is set to point to matched note. */
193
find_note(element ** result,char * label)194 bool find_note(element **result, char *label) {
195 element *cur = notes; /* pointer to walk up list of notes */
196 while (cur != NULL) {
197 if (strcmp(label, cur->contents.str) == 0) {
198 *result = cur;
199 return true;
200 }
201 else
202 cur = cur->next;
203 }
204 return false;
205 }
206
207