aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Makefile3
-rw-r--r--src/mds-kbdc/make-tree.c164
-rw-r--r--src/mds-kbdc/make-tree.h39
-rw-r--r--src/mds-kbdc/mds-kbdc.c154
-rw-r--r--src/mds-kbdc/parse-error.c160
-rw-r--r--src/mds-kbdc/parse-error.h143
-rw-r--r--src/mds-kbdc/raw-data.c2
-rw-r--r--src/mds-kbdc/raw-data.h2
8 files changed, 523 insertions, 144 deletions
diff --git a/Makefile b/Makefile
index cbd0ede..51c2c61 100644
--- a/Makefile
+++ b/Makefile
@@ -30,7 +30,8 @@ OBJ_mds-server_ = mds-server interception-condition client multicast \
OBJ_mds-registry_ = mds-registry util globals reexec registry signals \
slave
-OBJ_mds-kbdc_ = mds-kbdc globals raw-data functions string tree
+OBJ_mds-kbdc_ = mds-kbdc globals raw-data functions string tree \
+ make-tree parse-error
OBJ_mds-server = $(foreach O,$(OBJ_mds-server_),obj/mds-server/$(O).o)
OBJ_mds-registry = $(foreach O,$(OBJ_mds-registry_),obj/mds-registry/$(O).o)
diff --git a/src/mds-kbdc/make-tree.c b/src/mds-kbdc/make-tree.c
new file mode 100644
index 0000000..80faf5f
--- /dev/null
+++ b/src/mds-kbdc/make-tree.c
@@ -0,0 +1,164 @@
+/**
+ * mds — A micro-display server
+ * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "make-tree.h"
+
+#include "raw-data.h"
+
+#include <libmdsserver/macros.h>
+
+#include <limits.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+
+
+/**
+ * Parse a file into a syntex tree
+ *
+ * @param filename The filename of the file to parse
+ * @param result Output parameter for the root of the tree, `NULL` if -1 is returned
+ * @param errors `NULL`-terminated list of found error, `NULL` if no errors were found or if -1 is returned
+ * @return -1 if an error occursed that cannot be stored in `*errors`, zero otherwise
+ */
+int parse_to_tree(const char* restrict filename, mds_kbdc_tree_t** restrict result,
+ mds_kbdc_parse_error_t*** restrict errors)
+{
+ mds_kbdc_parse_error_t** old_errors = NULL;
+ char* pathname;
+ source_code_t source_code;
+ size_t errors_size = 0;
+ size_t errors_ptr = 0;
+ size_t line_i, line_n;
+ int saved_errno;
+
+ *errors = NULL;
+ source_code_initialise(&source_code);
+
+ /* Get a non-relative pathname for the file, relative filenames
+ * can be misleading as the program can have changed working
+ * directroy to be able to resolve filenames. */
+ pathname = realpath(filename, NULL);
+ fail_if (pathname == NULL);
+
+ /* Check that the file exists and can be read. */
+ if (access(pathname, R_OK) < 0)
+ {
+ saved_errno = errno;
+ fail_if (xmalloc(*errors, 2, mds_kbdc_parse_error_t*));
+ fail_if (xmalloc(**errors, 1, mds_kbdc_parse_error_t));
+ (*errors)[1] = NULL;
+
+ (**errors)->severity = MDS_KBDC_PARSE_ERROR_ERROR;
+ (**errors)->error_is_in_file = 0;
+ (**errors)->pathname = pathname, pathname = NULL;
+ (**errors)->line = 0;
+ (**errors)->start = 0;
+ (**errors)->end = 0;
+ (**errors)->code = NULL;
+ (**errors)->description = strdup(strerror(saved_errno));
+ fail_if ((**errors)->description == NULL);
+ return 0;
+ }
+
+ /* Read the file and simplify it a bit. */
+ fail_if (read_source_lines(pathname, &source_code) < 0);
+ /* TODO '\t':s should be expanded into ' ':s. */
+
+ for (line_i = 0, line_n = source_code.line_count; line_i < line_n; line_i++)
+ {
+ char* line = source_code.lines[line_i];
+ char* end;
+ char prev_end_char;
+
+ while (*line && (*line == ' '))
+ line++;
+ end = strchrnul(line, ' ');
+ if (end == line)
+ continue;
+ prev_end_char = *end;
+ *end = '\0';
+
+ if (!strcmp(line, "information")) ;
+ else if (!strcmp(line, "language")) ;
+ else if (!strcmp(line, "country")) ;
+ else if (!strcmp(line, "variant")) ;
+ else if (!strcmp(line, "include")) ;
+ else if (!strcmp(line, "function")) ;
+ else if (!strcmp(line, "macro")) ;
+ else if (!strcmp(line, "assumption")) ;
+ else if (!strcmp(line, "if")) ;
+ else if (!strcmp(line, "else")) ;
+ else if (!strcmp(line, "for")) ;
+ else if (!strcmp(line, "end")) ;
+ else if (!strcmp(line, "return")) ;
+ else if (!strcmp(line, "continue")) ;
+ else if (!strcmp(line, "break")) ;
+ else if (!strcmp(line, "let")) ;
+ else if (!strcmp(line, "have")) ;
+ else if (!strcmp(line, "have_chars")) ;
+ else if (!strcmp(line, "have_range")) ;
+ else if (strchr("\"<([", *line) || strchr(line, '(')) ;
+ else
+ {
+ mds_kbdc_parse_error_t* error;
+
+ if (errors_ptr + 1 >= errors_size)
+ {
+ errors_size = errors_size ? (errors_size << 1) : 2;
+ fail_if (xxrealloc(old_errors, *errors, errors_size, mds_kbdc_parse_error_t*));
+ }
+
+ fail_if (xcalloc(error, 1, mds_kbdc_parse_error_t));
+ (*errors)[errors_ptr + 0] = error;
+ (*errors)[errors_ptr + 1] = NULL;
+ errors_ptr++;
+
+ error->severity = MDS_KBDC_PARSE_ERROR_ERROR;
+ error->error_is_in_file = 1;
+ fail_if ((error->pathname = strdup(pathname)) == NULL);
+ error->line = line_i;
+ error->start = (size_t)(line - source_code.lines[line_i]);
+ error->end = (size_t)(end - source_code.lines[line_i]);
+ fail_if ((error->code = strdup(source_code.real_lines[line_i])) == NULL);
+ if (asprintf(&(error->description), "invalid keyword ‘%s’", line) < 0)
+ {
+ error->description = NULL;
+ goto pfail;
+ }
+ }
+
+ *end = prev_end_char;
+ }
+
+ free(pathname);
+ source_code_destroy(&source_code);
+ return 0;
+
+ pfail:
+ saved_errno = errno;
+ free(pathname);
+ source_code_destroy(&source_code);
+ mds_kbdc_parse_error_free_all(old_errors);
+ mds_kbdc_parse_error_free_all(*errors), *errors = NULL;
+ return errno = saved_errno, -1;
+}
+
diff --git a/src/mds-kbdc/make-tree.h b/src/mds-kbdc/make-tree.h
new file mode 100644
index 0000000..bcd4773
--- /dev/null
+++ b/src/mds-kbdc/make-tree.h
@@ -0,0 +1,39 @@
+/**
+ * mds — A micro-display server
+ * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef MDS_MDS_KBDC_MAKE_TREE_H
+#define MDS_MDS_KBDC_MAKE_TREE_H
+
+
+#include "tree.h"
+#include "parse-error.h"
+
+
+/**
+ * Parse a file into a syntex tree
+ *
+ * @param filename The filename of the file to parse
+ * @param result Output parameter for the root of the tree, `NULL` if -1 is returned
+ * @param errors `NULL`-terminated list of found error, `NULL` if no errors were found or if -1 is returned
+ * @return -1 if an error occursed that cannot be stored in `*errors`, zero otherwise
+ */
+int parse_to_tree(const char* restrict filename, mds_kbdc_tree_t** restrict result,
+ mds_kbdc_parse_error_t*** restrict errors);
+
+
+#endif
+
diff --git a/src/mds-kbdc/mds-kbdc.c b/src/mds-kbdc/mds-kbdc.c
index d9484f6..d0d3aa7 100644
--- a/src/mds-kbdc/mds-kbdc.c
+++ b/src/mds-kbdc/mds-kbdc.c
@@ -18,7 +18,7 @@
#include "mds-kbdc.h"
#include "globals.h"
-#include "raw-data.h"
+#include "make-tree.h"
#include <libmdsserver/macros.h>
@@ -38,159 +38,31 @@
*/
int main(int argc_, char** argv_)
{
- const char* pathname = argv_[1];
- source_code_t source_code;
- size_t i, n;
+ mds_kbdc_parse_error_t** parse_errors;
argc = argc_;
argv = argv_;
- source_code_initialise(&source_code);
- fail_if (read_source_lines(pathname, &source_code) < 0);
- /* TODO '\t':s should be expanded into ' ':s. */
-
- for (i = 0, n = source_code.line_count; i < n; i++)
+ fail_if (parse_to_tree(argv[1], NULL, &parse_errors) < 0);
+ if (parse_errors != NULL)
{
- char* line = source_code.lines[i];
- char* end;
- char prev_end_char;
-
- while (*line && (*line == ' '))
- line++;
- end = strchrnul(line, ' ');
- if (end == line)
- continue;
- prev_end_char = *end;
- *end = '\0';
-
- if (!strcmp(line, "information"))
- ;
- else if (!strcmp(line, "language"))
- ;
- else if (!strcmp(line, "country"))
- ;
- else if (!strcmp(line, "variant"))
- ;
- else if (!strcmp(line, "include"))
- ;
- else if (!strcmp(line, "function"))
- ;
- else if (!strcmp(line, "macro"))
- ;
- else if (!strcmp(line, "assumption"))
- ;
- else if (!strcmp(line, "if"))
- ;
- else if (!strcmp(line, "else"))
- ;
- else if (!strcmp(line, "for"))
- ;
- else if (!strcmp(line, "end"))
- ;
- else if (!strcmp(line, "return"))
- ;
- else if (!strcmp(line, "continue"))
- ;
- else if (!strcmp(line, "break"))
- ;
- else if (!strcmp(line, "let"))
- ;
- else if (!strcmp(line, "have"))
- ;
- else if (!strcmp(line, "have_chars"))
- ;
- else if (!strcmp(line, "have_range"))
- ;
- else if ((*line != '"') && (*line != '<') && (strchr(line, '(') == NULL))
+ mds_kbdc_parse_error_t** errors = parse_errors;
+ int fatal = 0;
+ while (*errors)
{
- size_t j, m1, m2;
- m1 = (size_t)(line - source_code.lines[i]);
- m2 = (size_t)(end - source_code.lines[i]);
- fprintf(stderr, "\033[1m%s:%zu:%zu-%zu: \033[31merror:\033[0m invalid keyword \033[1m‘%s’\033[0m\n",
- pathname, i + 1, m1 + 1, m2 + 1 /* TODO measure chars, not bytes */, line);
- fprintf(stderr, " %s\n \033[1;32m", source_code.real_lines[i]);
- for (j = 0; j < m1; j++)
- fputc(' ', stderr);
- for (; j < m2; j++)
- if ((source_code.lines[i][j] & 0xC0) != 0x80)
- fputc('^', stderr);
- fprintf(stderr, "\033[0m\n");
+ if ((*errors)->severity >= MDS_KBDC_PARSE_ERROR_ERROR)
+ fatal = 1;
+ mds_kbdc_parse_error_print(*errors++, stderr);
}
-
- *end = prev_end_char;
+ mds_kbdc_parse_error_free_all(parse_errors);
+ if (fatal)
+ return 1;
}
- /*
-
- information
- language "LANGUAGE" # multiple is allowed
- country "COUNTRY" # multiple is allowed
- variant "VARIANT"
- end information
-
- include "some file"
-
- function add/3
- \add(\add(\1 \2) \3)
- end function
- function add/4
- \add(\add(\1 \2 \3) \4)
- end function
-
- macro caps_affected/2
- <letter \1> : "\1"
- <shift letter \1> : "\2"
- <caps letter \1> : "\2"
- <caps shift letter \1> : "\1"
- end macro
-
- <altgr letter \\> : <dead letter ´>
- <altgr shift letter \\> : "¬"
- <altgr shift letter j> : <dead letter \u031B> # horn
-
- assumption
- have_range "a" "z"
- have_range "A" "Z"
- have_range "0" "9"
- have_chars " !\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"
- have <dead compose>
- have <shift>
- have <altgr>
- have <alt>
- have <ctrl>
- end assumption
-
- for "A" to "Z" as \1
- <dead compose> "(" "\1" ")" : "\add(\u24B6 \sub(\1 "A"))"
- end for
-
- let \6 : { \and(\4 16) \and(\4 32) \and(\4 64) \and(\4 128) }
-
- for 0 to 3 as \8
- \set(\6 \8 \add(\rsh(\get(\6 \8) \8) \mul(2 \rsh(\get(\7 \8)))))
- end for
-
- if \or(\equals(\1 0) \equals(\1 "0"))
- "0"
- else if \or(\equals(\1 1) \equals(\1 "1"))
- "1"
- else
- "9"
- end if
-
- if \and(\1 128) ## a number is true iff it is not zero
- let \2 : \or(\2 64)
- end if
-
- <altgr menu> : <-altgr ultra>
-
- */
- source_code_destroy(&source_code);
return 0;
pfail:
xperror(*argv);
- source_code_destroy(&source_code);
return 1;
}
diff --git a/src/mds-kbdc/parse-error.c b/src/mds-kbdc/parse-error.c
new file mode 100644
index 0000000..c16b9f4
--- /dev/null
+++ b/src/mds-kbdc/parse-error.c
@@ -0,0 +1,160 @@
+/**
+ * mds — A micro-display server
+ * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "parse-error.h"
+
+#include <stdlib.h>
+#include <alloca.h>
+#include <string.h>
+
+
+
+/**
+ * Print information about a parsing error
+ *
+ * @param this The error structure
+ * @param output The output file
+ */
+void mds_kbdc_parse_error_print(const mds_kbdc_parse_error_t* restrict this, FILE* restrict output)
+{
+ size_t i, n, start = 0, end = 0;
+ ssize_t m;
+ const char* restrict code = this->code;
+ char* desc;
+ char* dend = this->description + strlen(this->description);
+ char* dstart;
+ char* dptr;
+ char* p;
+ char* q;
+
+ /* Count the number points in the description we should modify to format it. */
+ for (p = this->description, n = 0;;)
+ {
+ if (q = strstr(p, "‘"), q == NULL) q = dend;
+ if (p = strstr(p, "’"), p == NULL) p = dend;
+ if (q < p) p = q;
+ if (*p++) n++;
+ else break;
+ }
+
+ /* Allocate string for the formatted description. */
+ n = 1 + strlen(this->description) + strlen("\033[xxm’") * n;
+ dptr = desc = alloca(n * sizeof(char));
+
+ /* Format description. */
+ for (p = this->description;;)
+ {
+ dstart = p;
+ if (q = strstr(p, "‘"), q == NULL) q = dend;
+ if (p = strstr(p, "’"), p == NULL) p = dend;
+ if (q < p) p = q;
+ if ((n = (size_t)(p - dstart)))
+ memcpy(dptr, dstart, n), dptr += n;
+ if (p == dend)
+ break;
+ if (strstr(p, "‘") == p) sprintf(dptr, "\033[01m‘%zn", &m), dptr += (size_t)m, p += strlen("‘");
+ else sprintf(dptr, "’\033[21m%zn", &m), dptr += (size_t)m, p += strlen("’");
+ }
+ *dptr = '\0';
+
+ /* Convert bytes count to character count for the code position */
+ for (i = 0, n = this->start; i < n; i++)
+ start++;
+ for (n = this->end; i < n; i++)
+ if ((code[i] & 0xC0) != 0x80)
+ end++;
+ end += start;
+
+ /* Print error information. */
+ fprintf(output, "\033[01m%s\033[21m:", this->pathname); /* TODO should be relative to the current dir */
+ if (this->error_is_in_file)
+ fprintf(output, "%zu:%zu–%zu:", this->line + 1, start + 1, end + 1);
+ switch (this->severity)
+ {
+ case MDS_KBDC_PARSE_ERROR_NOTE: fprintf(output, " \033[01;36mnote:\033[00m "); break;
+ case MDS_KBDC_PARSE_ERROR_WARNING: fprintf(output, " \033[01;35mwarning:\033[00m "); break;
+ case MDS_KBDC_PARSE_ERROR_ERROR: fprintf(output, " \033[01;31merror:\033[00m "); break;
+ case MDS_KBDC_PARSE_ERROR_INTERNAL_ERROR: fprintf(output, " \033[01;31minternal error:\033[00m "); break;
+ default:
+ abort();
+ break;
+ }
+ fprintf(output, "%s\n %s\n \033[01;32m", desc, code);
+ i = 0;
+ for (n = start; i < n; i++) fputc(' ', output);
+ for (n = end; i < n; i++) fputc('^', output);
+ fprintf(output, "\033[00m\n");
+}
+
+
+
+/**
+ * Release all resources allocated in a `mds_kbdc_parse_error_t*`
+ *
+ * @param this The error structure
+ */
+void mds_kbdc_parse_error_destroy(mds_kbdc_parse_error_t* restrict this)
+{
+ if (this == NULL)
+ return;
+ free(this->pathname), this->pathname = NULL;
+ free(this->code), this->code = NULL;
+ free(this->description), this->description = NULL;
+}
+
+
+/**
+ * Release all resources allocated in a `mds_kbdc_parse_error_t*`
+ * and release the allocation of the structure itself
+ *
+ * @param this The error structure
+ */
+void mds_kbdc_parse_error_free(mds_kbdc_parse_error_t* restrict this)
+{
+ mds_kbdc_parse_error_destroy(this);
+ free(this);
+}
+
+
+/**
+ * Release all resources allocated in a `NULL`-terminated group
+ * of `mds_kbdc_parse_error_t*`:s
+ *
+ * @param this The group of error structures
+ */
+void mds_kbdc_parse_error_destroy_all(mds_kbdc_parse_error_t** restrict these)
+{
+ mds_kbdc_parse_error_t* restrict that;
+ if (these == NULL)
+ return;
+ while (that = *these, that != NULL)
+ mds_kbdc_parse_error_free(that), *these++ = NULL;
+}
+
+
+/**
+ * Release all resources allocated in a `NULL`-terminated group of
+ * `mds_kbdc_parse_error_t*`:s and release the allocation of the group itself
+ *
+ * @param this The group of error structures
+ */
+void mds_kbdc_parse_error_free_all(mds_kbdc_parse_error_t** restrict these)
+{
+ mds_kbdc_parse_error_destroy_all(these);
+ free(these);
+}
+
diff --git a/src/mds-kbdc/parse-error.h b/src/mds-kbdc/parse-error.h
new file mode 100644
index 0000000..2c4aca8
--- /dev/null
+++ b/src/mds-kbdc/parse-error.h
@@ -0,0 +1,143 @@
+/**
+ * mds — A micro-display server
+ * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef MDS_MDS_KBDC_PARSE_ERROR_H
+#define MDS_MDS_KBDC_PARSE_ERROR_H
+
+
+#include <stddef.h>
+#include <stdio.h>
+
+
+/**
+ * Not an error, simply a note about the previous error or warning
+ */
+#define MDS_KBDC_PARSE_ERROR_NOTE 1
+
+/**
+ * A warning, must likely an error that is not fatal to the compilation
+ */
+#define MDS_KBDC_PARSE_ERROR_WARNING 2
+
+/**
+ * An error, the compilation will halt
+ */
+#define MDS_KBDC_PARSE_ERROR_ERROR 3
+
+/**
+ * Internal compiler error or system error, compilation halts
+ */
+#define MDS_KBDC_PARSE_ERROR_INTERNAL_ERROR 4
+
+
+
+/**
+ * Description of an parsing error
+ */
+typedef struct mds_kbdc_parse_error
+{
+ /**
+ * Either of:
+ * - `MDS_KBDC_PARSE_ERROR_NOTE`
+ * - `MDS_KBDC_PARSE_ERROR_WARNING`
+ * - `MDS_KBDC_PARSE_ERROR_ERROR`
+ * - `MDS_KBDC_PARSE_ERROR_INTERNAL_ERROR`
+ */
+ int severity;
+
+ /**
+ * If zero, disregard `.line`, `.start`, `.end` and `.code`
+ */
+ int error_is_in_file;
+
+ /**
+ * The pathname of the file with the error
+ */
+ char* pathname;
+
+ /**
+ * The line where the error occurred, zero-based
+ */
+ size_t line;
+
+ /**
+ * The byte where the error started, inclusive, zero-based
+ */
+ size_t start;
+
+ /**
+ * The byte where the error ended, exclusive, zero-based
+ */
+ size_t end;
+
+ /**
+ * The code on the line where the error occurred
+ */
+ char* code;
+
+ /**
+ * Description of the error
+ */
+ char* description;
+
+} mds_kbdc_parse_error_t;
+
+
+
+/**
+ * Print information about a parsing error
+ *
+ * @param this The error structure
+ * @param output The output file
+ */
+void mds_kbdc_parse_error_print(const mds_kbdc_parse_error_t* restrict this, FILE* restrict output);
+
+
+/**
+ * Release all resources allocated in a `mds_kbdc_parse_error_t*`
+ *
+ * @param this The error structure
+ */
+void mds_kbdc_parse_error_destroy(mds_kbdc_parse_error_t* restrict this);
+
+/**
+ * Release all resources allocated in a `mds_kbdc_parse_error_t*`
+ * and release the allocation of the structure itself
+ *
+ * @param this The error structure
+ */
+void mds_kbdc_parse_error_free(mds_kbdc_parse_error_t* restrict this);
+
+/**
+ * Release all resources allocated in a `NULL`-terminated group
+ * of `mds_kbdc_parse_error_t*`:s
+ *
+ * @param this The group of error structures
+ */
+void mds_kbdc_parse_error_destroy_all(mds_kbdc_parse_error_t** restrict these);
+
+/**
+ * Release all resources allocated in a `NULL`-terminated group of
+ * `mds_kbdc_parse_error_t*`:s and release the allocation of the group itself
+ *
+ * @param this The group of error structures
+ */
+void mds_kbdc_parse_error_free_all(mds_kbdc_parse_error_t** restrict these);
+
+
+#endif
+
diff --git a/src/mds-kbdc/raw-data.c b/src/mds-kbdc/raw-data.c
index 1d81d5e..7c1f5fd 100644
--- a/src/mds-kbdc/raw-data.c
+++ b/src/mds-kbdc/raw-data.c
@@ -313,7 +313,7 @@ static char** line_split(char* content, size_t length)
* Read lines of a source file
*
* @param pathname The pathname of the source file
- * @parma source_code Output parameter for read data
+ * @param source_code Output parameter for read data
* @return Zero on success, -1 on error
*/
int read_source_lines(const char* restrict pathname, source_code_t* restrict source_code)
diff --git a/src/mds-kbdc/raw-data.h b/src/mds-kbdc/raw-data.h
index 54a9740..a0217d6 100644
--- a/src/mds-kbdc/raw-data.h
+++ b/src/mds-kbdc/raw-data.h
@@ -84,7 +84,7 @@ void source_code_free(source_code_t* restrict this);
* Read lines of a source file
*
* @param pathname The pathname of the source file
- * @parma source_code Output parameter for read data
+ * @param source_code Output parameter for read data
* @return Zero on success, -1 on error
*/
int read_source_lines(const char* restrict pathname, source_code_t* restrict source_code);