]>
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; | |
8529e0f7 | 18 | |
031e5cce SM |
19 | for (n = 0; n < *n_columns; n++) { |
20 | ||
21 | if (valid) { | |
22 | valid = strntoken(next, max, delims, &token, &state); | |
23 | } | |
24 | if (valid) { | |
25 | next += strlen(token) + 1; | |
26 | max -= strlen(token) + 1; | |
27 | columns[n] = token; | |
28 | new_n = n + 1; | |
29 | } else { | |
30 | columns[n] = NULL; | |
31 | continue; | |
32 | } | |
33 | } | |
34 | *n_columns = new_n; | |
35 | } | |
36 | ||
37 | void | |
38 | free_csv_list(list_t *list) | |
39 | { | |
40 | list_t *pos = NULL, *tmp = NULL; | |
41 | list_for_each_safe(pos, tmp, list) { | |
42 | struct csv_row *row; | |
43 | ||
44 | row = list_entry(pos, struct csv_row, list); | |
45 | list_del(&row->list); | |
46 | FreePool(row); | |
47 | } | |
48 | } | |
49 | ||
50 | EFI_STATUS | |
51 | parse_csv_data(char *data, char *data_end, size_t n_columns, list_t *list) | |
52 | { | |
53 | EFI_STATUS efi_status = EFI_OUT_OF_RESOURCES; | |
54 | char delims[] = "\r\n"; | |
55 | char *line = data; | |
56 | size_t max = 0; | |
57 | char *end = data_end; | |
58 | ||
8119f718 SM |
59 | if (!data || !end || end <= data || !n_columns || !list) { |
60 | dprint(L"data:0x%lx end:0x%lx n_columns:%lu list:0x%lx\n", | |
61 | data, end, n_columns, list); | |
031e5cce | 62 | return EFI_INVALID_PARAMETER; |
8119f718 | 63 | } |
031e5cce SM |
64 | |
65 | max = (uintptr_t)end - (uintptr_t)line + (end > line ? 1 : 0); | |
8529e0f7 | 66 | if (is_utf8_bom(line, max)) |
031e5cce | 67 | |
031e5cce SM |
68 | line += UTF8_BOM_SIZE; |
69 | ||
8529e0f7 | 70 | while (line <= data_end && *line) { |
031e5cce SM |
71 | size_t entrysz = sizeof(char *) * n_columns + sizeof(struct csv_row); |
72 | struct csv_row *entry; | |
73 | size_t m_columns = n_columns; | |
74 | char *delim; | |
75 | bool found = true; | |
8529e0f7 | 76 | bool eof = false; |
031e5cce SM |
77 | |
78 | end = data_end; | |
79 | max = (uintptr_t)end - (uintptr_t)line + (end > line ? 1 : 0); | |
8529e0f7 | 80 | /* Skip the delimiter(s) of the previous line */ |
031e5cce SM |
81 | while (max && found) { |
82 | found = false; | |
83 | for (delim = &delims[0]; max && *delim; delim++) { | |
84 | if (line[0] == *delim) { | |
85 | line++; | |
86 | max--; | |
87 | found = true; | |
88 | } | |
89 | } | |
90 | } | |
8529e0f7 | 91 | /* Find the first delimiter of the current line */ |
031e5cce SM |
92 | for (delim = &delims[0]; *delim; delim++) { |
93 | char *tmp = strnchrnul(line, max, *delim); | |
94 | if (tmp < end) | |
95 | end = tmp; | |
96 | } | |
97 | max = (uintptr_t)end - (uintptr_t)line + (end > line ? 1 : 0); | |
8529e0f7 SM |
98 | |
99 | if (!*end) | |
100 | eof = true; | |
031e5cce SM |
101 | *end = '\0'; |
102 | ||
103 | if (line == data_end || max == 0) { | |
104 | line = end + 1; | |
105 | continue; | |
106 | } | |
107 | ||
108 | entry = AllocateZeroPool(entrysz); | |
109 | if (!entry) { | |
110 | efi_status = EFI_OUT_OF_RESOURCES; | |
111 | goto err_oom; | |
112 | } | |
113 | ||
114 | INIT_LIST_HEAD(&entry->list); | |
115 | list_add_tail(&entry->list, list); | |
116 | ||
117 | for (delim = &delims[0]; *delim; delim++) { | |
118 | char *tmp = strnchrnul((const char *)line, max, *delim); | |
119 | if (tmp < end) | |
120 | end = tmp; | |
121 | } | |
122 | ||
123 | parse_csv_line(line, max, &m_columns, (const char **)entry->columns); | |
124 | entry->n_columns = m_columns; | |
8529e0f7 SM |
125 | if (eof) |
126 | break; | |
127 | ||
031e5cce SM |
128 | line = end + 1; |
129 | } | |
130 | ||
131 | return EFI_SUCCESS; | |
132 | err_oom: | |
133 | free_csv_list(list); | |
134 | return efi_status; | |
135 | } | |
136 | ||
137 | // vim:fenc=utf-8:tw=75:noet |