]>
Commit | Line | Data |
---|---|---|
031e5cce SM |
1 | // SPDX-License-Identifier: BSD-2-Clause-Patent |
2 | /* | |
3 | * csv.c - CSV parser | |
4 | */ | |
5 | ||
6 | #include "shim.h" | |
7 | ||
8 | void NONNULL(1, 3, 4) | |
9 | parse_csv_line(char * line, size_t max, size_t *n_columns, const char *columns[]) | |
10 | { | |
11 | char *next = line; | |
12 | size_t n = 0, new_n = n; | |
13 | const char * const delims = ","; | |
14 | char state = 0; | |
15 | char *token = NULL; | |
16 | ||
17 | bool valid = true; | |
18 | for (n = 0; n < *n_columns; n++) { | |
19 | ||
20 | if (valid) { | |
21 | valid = strntoken(next, max, delims, &token, &state); | |
22 | } | |
23 | if (valid) { | |
24 | next += strlen(token) + 1; | |
25 | max -= strlen(token) + 1; | |
26 | columns[n] = token; | |
27 | new_n = n + 1; | |
28 | } else { | |
29 | columns[n] = NULL; | |
30 | continue; | |
31 | } | |
32 | } | |
33 | *n_columns = new_n; | |
34 | } | |
35 | ||
36 | void | |
37 | free_csv_list(list_t *list) | |
38 | { | |
39 | list_t *pos = NULL, *tmp = NULL; | |
40 | list_for_each_safe(pos, tmp, list) { | |
41 | struct csv_row *row; | |
42 | ||
43 | row = list_entry(pos, struct csv_row, list); | |
44 | list_del(&row->list); | |
45 | FreePool(row); | |
46 | } | |
47 | } | |
48 | ||
49 | EFI_STATUS | |
50 | parse_csv_data(char *data, char *data_end, size_t n_columns, list_t *list) | |
51 | { | |
52 | EFI_STATUS efi_status = EFI_OUT_OF_RESOURCES; | |
53 | char delims[] = "\r\n"; | |
54 | char *line = data; | |
55 | size_t max = 0; | |
56 | char *end = data_end; | |
57 | ||
8119f718 SM |
58 | if (!data || !end || end <= data || !n_columns || !list) { |
59 | dprint(L"data:0x%lx end:0x%lx n_columns:%lu list:0x%lx\n", | |
60 | data, end, n_columns, list); | |
031e5cce | 61 | return EFI_INVALID_PARAMETER; |
8119f718 | 62 | } |
031e5cce SM |
63 | |
64 | max = (uintptr_t)end - (uintptr_t)line + (end > line ? 1 : 0); | |
65 | ||
66 | if (line && is_utf8_bom(line, max)) | |
67 | line += UTF8_BOM_SIZE; | |
68 | ||
69 | while (line && line <= data_end) { | |
70 | size_t entrysz = sizeof(char *) * n_columns + sizeof(struct csv_row); | |
71 | struct csv_row *entry; | |
72 | size_t m_columns = n_columns; | |
73 | char *delim; | |
74 | bool found = true; | |
75 | ||
76 | end = data_end; | |
77 | max = (uintptr_t)end - (uintptr_t)line + (end > line ? 1 : 0); | |
78 | while (max && found) { | |
79 | found = false; | |
80 | for (delim = &delims[0]; max && *delim; delim++) { | |
81 | if (line[0] == *delim) { | |
82 | line++; | |
83 | max--; | |
84 | found = true; | |
85 | } | |
86 | } | |
87 | } | |
88 | for (delim = &delims[0]; *delim; delim++) { | |
89 | char *tmp = strnchrnul(line, max, *delim); | |
90 | if (tmp < end) | |
91 | end = tmp; | |
92 | } | |
93 | max = (uintptr_t)end - (uintptr_t)line + (end > line ? 1 : 0); | |
94 | *end = '\0'; | |
95 | ||
96 | if (line == data_end || max == 0) { | |
97 | line = end + 1; | |
98 | continue; | |
99 | } | |
100 | ||
101 | entry = AllocateZeroPool(entrysz); | |
102 | if (!entry) { | |
103 | efi_status = EFI_OUT_OF_RESOURCES; | |
104 | goto err_oom; | |
105 | } | |
106 | ||
107 | INIT_LIST_HEAD(&entry->list); | |
108 | list_add_tail(&entry->list, list); | |
109 | ||
110 | for (delim = &delims[0]; *delim; delim++) { | |
111 | char *tmp = strnchrnul((const char *)line, max, *delim); | |
112 | if (tmp < end) | |
113 | end = tmp; | |
114 | } | |
115 | ||
116 | parse_csv_line(line, max, &m_columns, (const char **)entry->columns); | |
117 | entry->n_columns = m_columns; | |
118 | line = end + 1; | |
119 | } | |
120 | ||
121 | return EFI_SUCCESS; | |
122 | err_oom: | |
123 | free_csv_list(list); | |
124 | return efi_status; | |
125 | } | |
126 | ||
127 | // vim:fenc=utf-8:tw=75:noet |