Repository navigation
Expand file tree
/
Copy pathatom.c
More file actions
379 lines (335 loc) · 8.82 KB
/
Copy pathatom.c
File metadata and controls
379 lines (335 loc) · 8.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
/**
* atom.c
* BBCode atom object. The output of the parser.
*
* @author Nathan Campos <nathan@innoveworkshop.com>
*/
#include "atom.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "utils.h"
/**
* Maximum length of an atom type string buffer length.
*/
#define TYPE_STR_MAX 15
/* Private functions. */
int has_prefmt_suffix(const char *str);
/**
* Allocates a brand new atom object.
*
* @param type Type of the new atom.
*
* @return Brand new, dynamically allocated, atom object.
*/
atom_t* atom_new(atom_type_t type) {
atom_t *atom;
/* Allocate a new atom object. */
atom = malloc(sizeof(atom_t));
atom->type = type;
atom->value = NULL;
return atom;
}
/**
* Frees up any resources taken by an atom object.
*
* @param atom Atom object to be completely freed.
*/
void atom_free(atom_t *atom) {
/* Do we even need to do anything? */
if (atom == NULL)
return;
/* Invalidate type and free value string. */
atom->type = -1;
if (atom->value) {
free(atom->value);
atom->value = NULL;
}
/* Free the atom object itself. */
free(atom);
}
/**
* Parses an atom from a line of compiled BBCode intermediate language.
*
* @param atom Atom object to be populated.
* @param line Line of compiled BBCode.
*
* @return 0 if the parsing was successful. 1 if we need to keep parsing on the
* next line. -1 if an error occurred.
*/
int atom_parse_line(atom_t *atom, const char *line) {
char type[TYPE_STR_MAX];
const char *cursor;
/* Extract the atom type. */
cursor = strcpyu(type, line, '\t');
if (*cursor == '\0') {
fprintf(stderr, "error: failed to extract atom type.\n");
return -1;
}
atom->type = atom_type_fromstr(type);
cursor++; /* Move past the delimiter. */
/* Skip double quote in case of a preformatted section. */
if (atom->type == ATOM_PREFMT)
cursor++;
/* Allocate enough space for our atom value. */
if (atom->value == NULL) {
atom->value = malloc((strlen(cursor) + 1) * sizeof(char));
} else {
atom->value = realloc(atom->value, (strlen(cursor) + 1) * sizeof(char));
}
/* Copy our atom's value. */
strcpy(atom->value, cursor);
/* Ensure we don't have a single line preformatted section. */
if (atom->type == ATOM_PREFMT) {
size_t lenv;
int lensfx;
lenv = strlen(atom->value);
lensfx = has_prefmt_suffix(atom->value);
if (lensfx) {
/* Single line preformatted section. */
atom->value[lenv - lensfx] = '\0';
} else {
/* Preformatted section continues, thus add a NL to the value. */
atom->value = realloc(atom->value, (lenv + 2) * sizeof(char));
atom->value[lenv] = '\n';
atom->value[lenv + 1] = '\0';
return 1;
}
}
return 0;
}
/**
* Continue parsing an atom from a line of compiled BBCode intermediate
* language.
*
* @param atom Atom object to be populated.
* @param line Line of compiled BBCode.
*
* @return 0 if the parsing was successful and no longer need to continue. 1 if
* we need to keep parsing on the next line. -1 if an error occurred.
*/
int atom_parse_continue(atom_t *atom, const char *line) {
if (atom->type == ATOM_PREFMT) {
size_t lenv;
size_t lenl;
int lens;
/* Allocate space for our string to grow. */
lenv = strlen(atom->value);
lenl = strlen(line);
atom->value = realloc(atom->value, (lenv + lenl + 1) * sizeof(char));
/* Append our line to the value. */
strcat(atom->value, line);
lens = has_prefmt_suffix(line);
lenv += lenl;
if (lens) {
/* Preformatted section has ended here. */
atom->value[lenv - lens] = '\0';
return 0;
} else {
/* Preformatted section continues on the next line. */
atom->value = realloc(atom->value, (lenv + 2) * sizeof(char));
atom->value[lenv] = '\n';
atom->value[lenv + 1] = '\0';
}
return 1;
}
/* I don't know what to do with this... */
fprintf(stderr, "error: unknown atom type to keep parsing on next line.\n");
return -1;
}
/**
* Determines an atom type from its compiled string representation.
*
* @param str Atom type as a compiled BBCode string.
*
* @return Atom type.
*/
atom_type_t atom_type_fromstr(const char *str) {
if (!strcmp(str, "TEXT")) {
return ATOM_RAW;
} else if (!strcmp(str, "PREFMT")) {
return ATOM_PREFMT;
} else if (!strcmp(str, "NEWLINE")) {
return ATOM_LINEBREAK;
} else if (!strcmp(str, "TAG_OPEN")) {
return ATOM_TAG_OPEN;
} else if (!strcmp(str, "TAG_ARG")) {
return ATOM_TAG_ARG;
} else if (!strcmp(str, "TAG_CLOSE")) {
return ATOM_TAG_CLOSE;
}
return -1;
}
/**
* Checks if an atom is of a specific type.
*
* @param atom Atom object to be tested.
* @param type Type of atom we are expecting.
*
* @return TRUE if the atom is of the specified type, FALSE otherwise.
*/
int atom_is_type(const atom_t *atom, atom_type_t type) {
return atom->type == type;
}
/**
* Checks if an atom is of a specific value.
*
* @param atom Atom object to be tested.
* @param value Specific value we are expecting.
*
* @return TRUE if the atom is of the specified value, FALSE otherwise.
*/
int atom_is_value(const atom_t *atom, const char *value) {
return !strcmp(atom->value, value);
}
/**
* Checks if an atom is of a specific type and of an specific value.
*
* @param atom Atom object to be tested.
* @param type Type of atom we are expecting.
* @param value Expected value of our atom.
*
* @return TRUE if both type and value are equal, FALSE otherwise.
*/
int atom_eq(const atom_t *atom, atom_type_t type, const char *value) {
return (atom->type == type) && !strcmp(atom->value, value);
}
/**
* Checks if a TAG_OPEN atom is of a type which may contain a subsequent
* argument atom.
*
* @param atom Atom object of type TAG_OPEN to be checked.
*
* @return TRUE if the tag is one that may contain arguments. FALSE if it is a
* tag that never has arguments.
*/
int atom_args_maybe(const atom_t *atom) {
const char *v;
if ((atom->type != ATOM_TAG_OPEN) && (atom->type != ATOM_TAG_CLOSE))
return 0;
v = atom->value;
return !strcmp(v, "URL") || !strcmp(v, "IMG") || !strcmp(v, "CODE") ||
!strcmp(v, "QUOTE") || !strcmp(v, "COLOR");
}
/**
* Prints out the contents of an atom in the format of an intermediate output
* language for formatters to use.
*
* @param atom Atom to be printed out.
*/
void atom_print(const atom_t *atom) {
/* Type */
switch (atom->type) {
case ATOM_RAW:
case ATOM_ESCAPED:
printf("TEXT");
break;
case ATOM_PREFMT:
printf("PREFMT");
break;
case ATOM_LINEBREAK:
printf("NEWLINE");
break;
case ATOM_TAG_OPEN:
printf("TAG_OPEN");
break;
case ATOM_TAG_ARG:
printf("TAG_ARG");
break;
case ATOM_TAG_CLOSE:
printf("TAG_CLOSE");
break;
default:
printf("UNKNOWN");
}
putchar('\t');
/* Value */
if (atom->type == ATOM_PREFMT)
putchar('"');
printf("%s", (atom->value == NULL) ? "" : atom->value);
if (atom->type == ATOM_PREFMT)
printf("\"\tPREFMT");
putchar('\n');
}
/**
* Checks if a string ends with the compiled BBCode preformatted section ending.
*
* @param String to be tested for the preformatted section ending.
*
* @return The number of bytes to trim from the string if the preformatted
* section ending. 0 if no valid suffix was found.
*/
int has_prefmt_suffix(const char *str) {
if (!strcmpe(str, "\"\tPREFMT"))
return 8;
return 0;
}
/**
* Allocates a brand new atom group object.
*
* @return Brand new atom group object that must be freed using atom_group_free.
*/
atom_group_t* atom_group_new(void) {
atom_group_t *atoms = malloc(sizeof(atom_group_t));
atoms->group = NULL;
atoms->len = 0;
return atoms;
}
/**
* Frees up any resources created by an atom group object, including itself and
* all atom objects inside it.
*
* @param atoms Atom group object to be freed.
*/
void atom_group_free(atom_group_t *atoms) {
/* Do we even need to do anything? */
if (atoms == NULL)
return;
/* Go through group freeing all atoms. */
if (atoms->group) {
int i;
for (i = 0; i < atoms->len; i++) {
atom_free(atoms->group[i]);
atoms->group[i] = NULL;
}
}
/* Free ourselves. */
atom_group_free_partial(atoms);
}
/**
* Frees up an atom group without touching any of the atoms it contains.
*
* @param atoms Atom group object to be freed.
*/
void atom_group_free_partial(atom_group_t *atoms) {
/* Do we even need to do anything? */
if (atoms == NULL)
return;
/* Free the atom group array. */
if (atoms->group) {
free(atoms->group);
atoms->group = NULL;
}
/* Free ourselves. */
atoms->len = 0;
free(atoms);
}
/**
* Appends a new atom to a group.
*
* @param atoms Atom group object to receive the new atom.
* @param atom Atom object to be added to the end of the group.
*
* @return Total number of atoms in the group.
*/
int atom_group_append(atom_group_t *atoms, atom_t *atom) {
if (atoms->len == 0) {
atoms->group = malloc(sizeof(atom_t*));
} else {
atoms->group = realloc(atoms->group, (atoms->len + 1) *
sizeof(atom_t*));
}
atoms->group[atoms->len++] = atom;
return atoms->len;
}