summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--csv.c124
-rw-r--r--include/str.h31
3 files changed, 156 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index a6807768..5dc745b3 100644
--- a/Makefile
+++ b/Makefile
@@ -34,7 +34,7 @@ CFLAGS += -DENABLE_SHIM_CERT
else
TARGETS += $(MMNAME) $(FBNAME)
endif
-OBJS = shim.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o pe.o httpboot.o
+OBJS = shim.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o sbat_data.o pe.o httpboot.o csv.o
KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer
ORIG_SOURCES = shim.c mok.c netboot.c replacements.c tpm.c errlog.c sbat.c pe.c httpboot.c shim.h version.h $(wildcard include/*.h)
MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat_data.o
diff --git a/csv.c b/csv.c
new file mode 100644
index 00000000..3c821414
--- /dev/null
+++ b/csv.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+/*
+ * csv.c - CSV parser
+ */
+
+#include "shim.h"
+
+void NONNULL(1, 3, 4)
+parse_csv_line(char * line, size_t max, size_t *n_columns, const char *columns[])
+{
+ char *next = line;
+ size_t n = 0, new_n = n;
+ const char * const delims = ",";
+ char state = 0;
+ char *token = NULL;
+
+ bool valid = true;
+ for (n = 0; n < *n_columns; n++) {
+
+ if (valid) {
+ valid = strntoken(next, max, delims, &token, &state);
+ }
+ if (valid) {
+ next += strlena(token) + 1;
+ max -= strlena(token) + 1;
+ columns[n] = token;
+ new_n = n + 1;
+ } else {
+ columns[n] = NULL;
+ continue;
+ }
+ }
+ *n_columns = new_n;
+}
+
+void
+free_csv_list(list_t *list)
+{
+ list_t *pos = NULL, *tmp = NULL;
+ list_for_each_safe(pos, tmp, list) {
+ struct csv_row *row;
+
+ row = list_entry(pos, struct csv_row, list);
+ list_del(&row->list);
+ FreePool(row);
+ }
+}
+
+EFI_STATUS
+parse_csv_data(char *data, char *data_end, size_t n_columns, list_t *list)
+{
+ EFI_STATUS efi_status = EFI_OUT_OF_RESOURCES;
+ char delims[] = "\r\n";
+ char *line = data;
+ size_t max = 0;
+ char *end = data_end;
+
+ if (!data || !end || end <= data || !n_columns || !list)
+ return EFI_INVALID_PARAMETER;
+
+ max = (uintptr_t)end - (uintptr_t)line + (end > line ? 1 : 0);
+
+ if (line && is_utf8_bom(line, max))
+ line += UTF8_BOM_SIZE;
+
+ while (line && line <= data_end) {
+ size_t entrysz = sizeof(char *) * n_columns + sizeof(struct csv_row);
+ struct csv_row *entry;
+ size_t m_columns = n_columns;
+ char *delim;
+ bool found = true;
+
+ end = data_end;
+ max = (uintptr_t)end - (uintptr_t)line + (end > line ? 1 : 0);
+ while (max && found) {
+ found = false;
+ for (delim = &delims[0]; max && *delim; delim++) {
+ if (line[0] == *delim) {
+ line++;
+ max--;
+ found = true;
+ }
+ }
+ }
+ for (delim = &delims[0]; *delim; delim++) {
+ char *tmp = strnchrnul(line, max, *delim);
+ if (tmp < end)
+ end = tmp;
+ }
+ max = (uintptr_t)end - (uintptr_t)line + (end > line ? 1 : 0);
+ *end = '\0';
+
+ if (line == data_end || max == 0) {
+ line = end + 1;
+ continue;
+ }
+
+ entry = AllocateZeroPool(entrysz);
+ if (!entry) {
+ efi_status = EFI_OUT_OF_RESOURCES;
+ goto err_oom;
+ }
+
+ INIT_LIST_HEAD(&entry->list);
+ list_add_tail(&entry->list, list);
+
+ for (delim = &delims[0]; *delim; delim++) {
+ char *tmp = strnchrnul((const char *)line, max, *delim);
+ if (tmp < end)
+ end = tmp;
+ }
+
+ parse_csv_line(line, max, &m_columns, (const char **)entry->columns);
+ entry->n_columns = m_columns;
+ line = end + 1;
+ }
+
+ return EFI_SUCCESS;
+err_oom:
+ free_csv_list(list);
+ return efi_status;
+}
+
+// vim:fenc=utf-8:tw=75:noet
diff --git a/include/str.h b/include/str.h
index 72f87b75..91f05dc6 100644
--- a/include/str.h
+++ b/include/str.h
@@ -225,4 +225,35 @@ is_utf8_bom(CHAR8 *buf, size_t bufsize)
return CompareMem(buf, bom, MIN(UTF8_BOM_SIZE, bufsize)) == 0;
}
+/**
+ * parse CSV data from data to end.
+ * *data points to the first byte of the data
+ * end points to a NUL byte at the end of the data
+ * n_columns number of columns per entry
+ * list the list head we're adding to
+ *
+ * On success, list will be populated with individually allocate a list of
+ * struct csv_list objects, with one column per entry of the "columns" array,
+ * filled left to right with up to n_columns elements, or NULL when a csv line
+ * does not have enough elements.
+ *
+ * Note that the data will be modified; all comma, linefeed, and newline
+ * characters will be set to '\000'. Additionally, consecutive linefeed and
+ * newline characters will not result in rows in the results.
+ *
+ * On failure, list will be empty and all entries on it will have been freed,
+ * using free_csv_list(), whether they were there before calling
+ * parse_csv_data or not.
+ */
+
+struct csv_row {
+ list_t list; /* this is a linked list */
+ size_t n_columns; /* this is how many columns are actually populated */
+ char *columns[0]; /* these are pointers to columns */
+};
+
+EFI_STATUS parse_csv_data(char *data, char *end, size_t n_columns,
+ list_t *list);
+void free_csv_list(list_t *list);
+
#endif /* SHIM_STR_H */