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