Coverage Report

Created: 2025-07-12 06:33

/src/fwupd/libfwupdplugin/fu-csv-entry.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2023 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include "fu-common.h"
10
#include "fu-csv-entry.h"
11
#include "fu-csv-firmware-private.h"
12
#include "fu-string.h"
13
14
/**
15
 * FuCsvEntry:
16
 *
17
 * A comma seporated value entry.
18
 *
19
 * See also: [class@FuFirmware]
20
 */
21
22
typedef struct {
23
  GPtrArray *values; /* element-type utf-8 */
24
} FuCsvEntryPrivate;
25
26
G_DEFINE_TYPE_WITH_PRIVATE(FuCsvEntry, fu_csv_entry, FU_TYPE_FIRMWARE)
27
1.61M
#define GET_PRIVATE(o) (fu_csv_entry_get_instance_private(o))
28
29
231k
#define FU_CSV_ENTRY_COLUMNS_MAX 1000u
30
31
/**
32
 * fu_csv_entry_add_value:
33
 * @self: a #FuFirmware
34
 * @value: (not nullable): string
35
 *
36
 * Adds a string value to the entry.
37
 *
38
 * Since: 1.9.3
39
 **/
40
void
41
fu_csv_entry_add_value(FuCsvEntry *self, const gchar *value)
42
0
{
43
0
  FuCsvEntryPrivate *priv = GET_PRIVATE(self);
44
0
  g_return_if_fail(FU_IS_CSV_ENTRY(self));
45
0
  g_return_if_fail(value != NULL);
46
0
  g_ptr_array_add(priv->values, g_strdup(value));
47
0
}
48
49
/**
50
 * fu_csv_entry_get_value_by_idx:
51
 * @self: a #FuFirmware
52
 * @idx: column ID idx
53
 *
54
 * Gets the entry value for a specific index.
55
 *
56
 * Returns: a string, or %NULL if unset
57
 *
58
 * Since: 1.9.3
59
 **/
60
const gchar *
61
fu_csv_entry_get_value_by_idx(FuCsvEntry *self, guint idx)
62
0
{
63
0
  FuCsvEntryPrivate *priv = GET_PRIVATE(self);
64
0
  g_return_val_if_fail(FU_IS_CSV_ENTRY(self), NULL);
65
0
  if (idx >= priv->values->len)
66
0
    return NULL;
67
0
  return g_ptr_array_index(priv->values, idx);
68
0
}
69
70
/**
71
 * fu_csv_entry_get_value_by_column_id:
72
 * @self: a #FuFirmware
73
 * @column_id: (not nullable): string, e.g. `component_generation`
74
 *
75
 * Gets the entry value for a specific column ID.
76
 *
77
 * Returns: a string, or %NULL if unset or the column ID cannot be found
78
 *
79
 * Since: 1.9.3
80
 **/
81
const gchar *
82
fu_csv_entry_get_value_by_column_id(FuCsvEntry *self, const gchar *column_id)
83
0
{
84
0
  FuCsvEntryPrivate *priv = GET_PRIVATE(self);
85
0
  FuCsvFirmware *parent = FU_CSV_FIRMWARE(fu_firmware_get_parent(FU_FIRMWARE(self)));
86
0
  guint idx = fu_csv_firmware_get_idx_for_column_id(parent, column_id);
87
88
0
  g_return_val_if_fail(FU_IS_CSV_ENTRY(self), NULL);
89
0
  g_return_val_if_fail(FU_IS_CSV_FIRMWARE(parent), NULL);
90
0
  g_return_val_if_fail(idx != G_MAXUINT, NULL);
91
0
  g_return_val_if_fail(column_id != NULL, NULL);
92
93
0
  return g_ptr_array_index(priv->values, idx);
94
0
}
95
96
gboolean
97
fu_csv_entry_get_value_by_column_id_uint64(FuCsvEntry *self,
98
             const gchar *column_id,
99
             guint64 *value,
100
             GError **error)
101
0
{
102
0
  const gchar *str_value = fu_csv_entry_get_value_by_column_id(self, column_id);
103
104
0
  if (str_value == NULL) {
105
0
    g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "CSV value not found");
106
107
0
    return FALSE;
108
0
  }
109
110
0
  return fu_strtoull(str_value, value, 0, G_MAXUINT64, FU_INTEGER_BASE_AUTO, error);
111
0
}
112
113
static void
114
fu_csv_entry_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
115
0
{
116
0
  FuCsvEntry *self = FU_CSV_ENTRY(firmware);
117
0
  FuCsvEntryPrivate *priv = GET_PRIVATE(self);
118
0
  FuCsvFirmware *parent = FU_CSV_FIRMWARE(fu_firmware_get_parent(firmware));
119
0
  g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "values", NULL);
120
121
0
  for (guint i = 0; i < priv->values->len; i++) {
122
0
    const gchar *value = g_ptr_array_index(priv->values, i);
123
0
    const gchar *key = fu_csv_firmware_get_column_id(parent, i);
124
0
    if (key != NULL)
125
0
      fu_xmlb_builder_insert_kv(bc, key, value);
126
0
  }
127
0
}
128
129
static gboolean
130
fu_csv_entry_build(FuFirmware *firmware, XbNode *n, GError **error)
131
0
{
132
0
  FuCsvEntry *self = FU_CSV_ENTRY(firmware);
133
0
  FuCsvFirmware *parent = FU_CSV_FIRMWARE(fu_firmware_get_parent(firmware));
134
0
  gboolean add_columns = fu_csv_firmware_get_column_id(parent, 0) == NULL;
135
0
  g_autoptr(GPtrArray) values = NULL;
136
137
0
  values = xb_node_query(n, "values/*", 0, error);
138
0
  if (values == NULL) {
139
0
    fwupd_error_convert(error);
140
0
    return FALSE;
141
0
  }
142
0
  for (guint i = 0; i < values->len; i++) {
143
0
    XbNode *c = g_ptr_array_index(values, i);
144
0
    if (add_columns && xb_node_get_element(c) != NULL)
145
0
      fu_csv_firmware_add_column_id(parent, xb_node_get_element(c));
146
0
    fu_csv_entry_add_value(self, xb_node_get_text(c));
147
0
  }
148
0
  return TRUE;
149
0
}
150
151
static gboolean
152
fu_csv_entry_parse_token_cb(GString *token, guint token_idx, gpointer user_data, GError **error)
153
231k
{
154
231k
  FuCsvEntry *self = FU_CSV_ENTRY(user_data);
155
231k
  FuCsvEntryPrivate *priv = GET_PRIVATE(self);
156
231k
  FuCsvFirmware *parent = FU_CSV_FIRMWARE(fu_firmware_get_parent(FU_FIRMWARE(self)));
157
231k
  const gchar *column_id = fu_csv_firmware_get_column_id(parent, token_idx);
158
159
  /* sanity check */
160
231k
  if (token_idx > FU_CSV_ENTRY_COLUMNS_MAX) {
161
3
    g_set_error(error,
162
3
          FWUPD_ERROR,
163
3
          FWUPD_ERROR_INVALID_DATA,
164
3
          "too many columns, limit is %u",
165
3
          FU_CSV_ENTRY_COLUMNS_MAX);
166
3
    return FALSE;
167
3
  }
168
169
231k
  if (g_strcmp0(column_id, "$id") == 0) {
170
2.98k
    fu_firmware_set_id(FU_FIRMWARE(self), token->str);
171
228k
  } else if (g_strcmp0(column_id, "$idx") == 0) {
172
845
    guint64 value = 0;
173
845
    if (!fu_strtoull(token->str, &value, 0, G_MAXUINT64, FU_INTEGER_BASE_AUTO, error))
174
42
      return FALSE;
175
803
    fu_firmware_set_idx(FU_FIRMWARE(self), value);
176
228k
  } else if (g_strcmp0(column_id, "$version") == 0) {
177
3.33k
    fu_firmware_set_version(FU_FIRMWARE(self), token->str); /* nocheck:set-version */
178
224k
  } else if (g_strcmp0(column_id, "$version_raw") == 0) {
179
195
    guint64 value = 0;
180
195
    if (!fu_strtoull(token->str, &value, 0, G_MAXUINT64, FU_INTEGER_BASE_AUTO, error))
181
1
      return FALSE;
182
194
    fu_firmware_set_version_raw(FU_FIRMWARE(self), value);
183
194
  }
184
185
  /* always save to value so we can write it back out */
186
231k
  g_ptr_array_add(priv->values, g_strdup(token->str));
187
231k
  return TRUE;
188
231k
}
189
190
static gboolean
191
fu_csv_entry_parse(FuFirmware *firmware,
192
       GInputStream *stream,
193
       FuFirmwareParseFlags flags,
194
       GError **error)
195
188k
{
196
188k
  FuCsvEntry *self = FU_CSV_ENTRY(firmware);
197
188k
  return fu_strsplit_stream(stream, 0x0, ",", fu_csv_entry_parse_token_cb, self, error);
198
188k
}
199
200
static GByteArray *
201
fu_csv_entry_write(FuFirmware *firmware, GError **error)
202
155k
{
203
155k
  FuCsvEntry *self = FU_CSV_ENTRY(firmware);
204
155k
  FuCsvEntryPrivate *priv = GET_PRIVATE(self);
205
155k
  g_autoptr(GByteArray) buf = g_byte_array_new();
206
155k
  g_autoptr(GString) str = g_string_new(NULL);
207
208
  /* single line */
209
349k
  for (guint i = 0; i < priv->values->len; i++) {
210
193k
    const gchar *value = g_ptr_array_index(priv->values, i);
211
193k
    if (str->len > 0)
212
19.0k
      g_string_append(str, ",");
213
193k
    if (value != NULL)
214
193k
      g_string_append(str, value);
215
193k
  }
216
155k
  g_string_append(str, "\n");
217
155k
  g_byte_array_append(buf, (const guint8 *)str->str, str->len);
218
219
  /* success */
220
155k
  return g_steal_pointer(&buf);
221
155k
}
222
223
static void
224
fu_csv_entry_init(FuCsvEntry *self)
225
616k
{
226
616k
  FuCsvEntryPrivate *priv = GET_PRIVATE(self);
227
616k
  priv->values = g_ptr_array_new_with_free_func(g_free);
228
616k
}
229
230
static void
231
fu_csv_entry_finalize(GObject *object)
232
616k
{
233
616k
  FuCsvEntry *self = FU_CSV_ENTRY(object);
234
616k
  FuCsvEntryPrivate *priv = GET_PRIVATE(self);
235
616k
  g_ptr_array_unref(priv->values);
236
616k
  G_OBJECT_CLASS(fu_csv_entry_parent_class)->finalize(object);
237
616k
}
238
239
static void
240
fu_csv_entry_class_init(FuCsvEntryClass *klass)
241
1
{
242
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
243
1
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
244
1
  object_class->finalize = fu_csv_entry_finalize;
245
1
  firmware_class->parse = fu_csv_entry_parse;
246
1
  firmware_class->write = fu_csv_entry_write;
247
1
  firmware_class->build = fu_csv_entry_build;
248
1
  firmware_class->export = fu_csv_entry_export;
249
1
}
250
251
/**
252
 * fu_csv_entry_new:
253
 *
254
 * Creates a new #FuFirmware
255
 *
256
 * Since: 1.9.3
257
 **/
258
FuFirmware *
259
fu_csv_entry_new(void)
260
616k
{
261
616k
  return FU_FIRMWARE(g_object_new(FU_TYPE_CSV_ENTRY, NULL));
262
616k
}