diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | csv.c | 124 | ||||
| -rw-r--r-- | include/str.h | 31 |
3 files changed, 156 insertions, 1 deletions
@@ -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 @@ -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 */ |
