1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2016 Richard Hughes <richard@hughsie.com>
4 *
5 * SPDX-License-Identifier: LGPL-2.1+
6 */
7
8 /**
9 * SECTION:as-require
10 * @short_description: Object representing a single requirement.
11 * @include: appstream-glib.h
12 * @stability: Unstable
13 *
14 * Requirements are things the component needs to be valid.
15 *
16 * See also: #AsApp
17 */
18
19 #include "config.h"
20
21 #include "as-require-private.h"
22 #include "as-node-private.h"
23 #include "as-ref-string.h"
24 #include "as-utils-private.h"
25
26 #ifndef _WIN32
27 #include <fnmatch.h>
28 #endif
29
30 typedef struct
31 {
32 AsRequireKind kind;
33 AsRequireCompare compare;
34 AsRefString *version; /* utf8 */
35 AsRefString *value; /* utf8 */
36 } AsRequirePrivate;
37
G_DEFINE_TYPE_WITH_PRIVATE(AsRequire,as_require,G_TYPE_OBJECT)38 G_DEFINE_TYPE_WITH_PRIVATE (AsRequire, as_require, G_TYPE_OBJECT)
39
40 #define GET_PRIVATE(o) (as_require_get_instance_private (o))
41
42 static void
43 as_require_finalize (GObject *object)
44 {
45 AsRequire *require = AS_REQUIRE (object);
46 AsRequirePrivate *priv = GET_PRIVATE (require);
47
48 if (priv->version != NULL)
49 as_ref_string_unref (priv->version);
50 if (priv->value != NULL)
51 as_ref_string_unref (priv->value);
52
53 G_OBJECT_CLASS (as_require_parent_class)->finalize (object);
54 }
55
56 static void
as_require_init(AsRequire * require)57 as_require_init (AsRequire *require)
58 {
59 // AsRequirePrivate *priv = GET_PRIVATE (require);
60 }
61
62 static void
as_require_class_init(AsRequireClass * klass)63 as_require_class_init (AsRequireClass *klass)
64 {
65 GObjectClass *object_class = G_OBJECT_CLASS (klass);
66 object_class->finalize = as_require_finalize;
67 }
68
69 /**
70 * as_require_kind_from_string:
71 * @kind: the string.
72 *
73 * Converts the text representation to an enumerated value.
74 *
75 * Returns: (transfer full): a #AsRequireKind, or %AS_REQUIRE_KIND_UNKNOWN for unknown.
76 *
77 * Since: 0.6.7
78 **/
79 AsRequireKind
as_require_kind_from_string(const gchar * kind)80 as_require_kind_from_string (const gchar *kind)
81 {
82 if (g_strcmp0 (kind, "id") == 0)
83 return AS_REQUIRE_KIND_ID;
84 if (g_strcmp0 (kind, "firmware") == 0)
85 return AS_REQUIRE_KIND_FIRMWARE;
86 if (g_strcmp0 (kind, "hardware") == 0)
87 return AS_REQUIRE_KIND_HARDWARE;
88 if (g_strcmp0 (kind, "modalias") == 0)
89 return AS_REQUIRE_KIND_MODALIAS;
90 if (g_strcmp0 (kind, "kernel") == 0)
91 return AS_REQUIRE_KIND_KERNEL;
92 if (g_strcmp0 (kind, "memory") == 0)
93 return AS_REQUIRE_KIND_MEMORY;
94 return AS_REQUIRE_KIND_UNKNOWN;
95 }
96
97 /**
98 * as_require_kind_to_string:
99 * @kind: the #AsRequireKind.
100 *
101 * Converts the enumerated value to an text representation.
102 *
103 * Returns: string version of @kind
104 *
105 * Since: 0.6.7
106 **/
107 const gchar *
as_require_kind_to_string(AsRequireKind kind)108 as_require_kind_to_string (AsRequireKind kind)
109 {
110 if (kind == AS_REQUIRE_KIND_ID)
111 return "id";
112 if (kind == AS_REQUIRE_KIND_FIRMWARE)
113 return "firmware";
114 if (kind == AS_REQUIRE_KIND_HARDWARE)
115 return "hardware";
116 if (kind == AS_REQUIRE_KIND_MODALIAS)
117 return "modalias";
118 if (kind == AS_REQUIRE_KIND_KERNEL)
119 return "kernel";
120 if (kind == AS_REQUIRE_KIND_MEMORY)
121 return "memory";
122 return NULL;
123 }
124
125 /**
126 * as_require_compare_from_string:
127 * @compare: the string.
128 *
129 * Converts the text representation to an enumerated value.
130 *
131 * Returns: (transfer full): a #AsRequireCompare, or
132 * %AS_REQUIRE_COMPARE_UNKNOWN for unknown.
133 *
134 * Since: 0.6.7
135 **/
136 AsRequireCompare
as_require_compare_from_string(const gchar * compare)137 as_require_compare_from_string (const gchar *compare)
138 {
139 if (g_strcmp0 (compare, "eq") == 0)
140 return AS_REQUIRE_COMPARE_EQ;
141 if (g_strcmp0 (compare, "ne") == 0)
142 return AS_REQUIRE_COMPARE_NE;
143 if (g_strcmp0 (compare, "gt") == 0)
144 return AS_REQUIRE_COMPARE_GT;
145 if (g_strcmp0 (compare, "lt") == 0)
146 return AS_REQUIRE_COMPARE_LT;
147 if (g_strcmp0 (compare, "ge") == 0)
148 return AS_REQUIRE_COMPARE_GE;
149 if (g_strcmp0 (compare, "le") == 0)
150 return AS_REQUIRE_COMPARE_LE;
151 if (g_strcmp0 (compare, "glob") == 0)
152 return AS_REQUIRE_COMPARE_GLOB;
153 if (g_strcmp0 (compare, "regex") == 0)
154 return AS_REQUIRE_COMPARE_REGEX;
155 return AS_REQUIRE_COMPARE_UNKNOWN;
156 }
157
158 /**
159 * as_require_compare_to_string:
160 * @compare: the #AsRequireCompare.
161 *
162 * Converts the enumerated value to an text representation.
163 *
164 * Returns: string version of @compare
165 *
166 * Since: 0.6.7
167 **/
168 const gchar *
as_require_compare_to_string(AsRequireCompare compare)169 as_require_compare_to_string (AsRequireCompare compare)
170 {
171 if (compare == AS_REQUIRE_COMPARE_EQ)
172 return "eq";
173 if (compare == AS_REQUIRE_COMPARE_NE)
174 return "ne";
175 if (compare == AS_REQUIRE_COMPARE_GT)
176 return "gt";
177 if (compare == AS_REQUIRE_COMPARE_LT)
178 return "lt";
179 if (compare == AS_REQUIRE_COMPARE_GE)
180 return "ge";
181 if (compare == AS_REQUIRE_COMPARE_LE)
182 return "le";
183 if (compare == AS_REQUIRE_COMPARE_GLOB)
184 return "glob";
185 if (compare == AS_REQUIRE_COMPARE_REGEX)
186 return "regex";
187 return NULL;
188 }
189
190 /**
191 * as_require_get_version:
192 * @require: a #AsRequire instance.
193 *
194 * Gets the require version if set.
195 *
196 * Returns: the version, e.g. "0.1.2"
197 *
198 * Since: 0.6.7
199 **/
200 const gchar *
as_require_get_version(AsRequire * require)201 as_require_get_version (AsRequire *require)
202 {
203 AsRequirePrivate *priv = GET_PRIVATE (require);
204 g_return_val_if_fail (AS_IS_REQUIRE (require), NULL);
205 return priv->version;
206 }
207
208 /**
209 * as_require_get_value:
210 * @require: a #AsRequire instance.
211 *
212 * Gets the require value if set.
213 *
214 * Returns: the value, e.g. "bootloader"
215 *
216 * Since: 0.6.7
217 **/
218 const gchar *
as_require_get_value(AsRequire * require)219 as_require_get_value (AsRequire *require)
220 {
221 AsRequirePrivate *priv = GET_PRIVATE (require);
222 g_return_val_if_fail (AS_IS_REQUIRE (require), NULL);
223 return priv->value;
224 }
225
226 /**
227 * as_require_get_kind:
228 * @require: a #AsRequire instance.
229 *
230 * Gets the require kind.
231 *
232 * Returns: the #AsRequireKind
233 *
234 * Since: 0.6.7
235 **/
236 AsRequireKind
as_require_get_kind(AsRequire * require)237 as_require_get_kind (AsRequire *require)
238 {
239 AsRequirePrivate *priv = GET_PRIVATE (require);
240 g_return_val_if_fail (AS_IS_REQUIRE (require), AS_REQUIRE_KIND_UNKNOWN);
241 return priv->kind;
242 }
243
244 /**
245 * as_require_set_kind:
246 * @require: a #AsRequire instance.
247 * @kind: the #AsRequireKind, e.g. %AS_REQUIRE_KIND_ID.
248 *
249 * Sets the require kind.
250 *
251 * Since: 0.6.7
252 **/
253 void
as_require_set_kind(AsRequire * require,AsRequireKind kind)254 as_require_set_kind (AsRequire *require, AsRequireKind kind)
255 {
256 AsRequirePrivate *priv = GET_PRIVATE (require);
257 g_return_if_fail (AS_IS_REQUIRE (require));
258 priv->kind = kind;
259 }
260
261 /**
262 * as_require_get_compare:
263 * @require: a #AsRequire instance.
264 *
265 * Gets the require version comparison type.
266 *
267 * Returns: the #AsRequireKind
268 *
269 * Since: 0.6.7
270 **/
271 AsRequireCompare
as_require_get_compare(AsRequire * require)272 as_require_get_compare (AsRequire *require)
273 {
274 AsRequirePrivate *priv = GET_PRIVATE (require);
275 g_return_val_if_fail (AS_IS_REQUIRE (require), AS_REQUIRE_COMPARE_UNKNOWN);
276 return priv->compare;
277 }
278
279 /**
280 * as_require_set_compare:
281 * @require: a #AsRequire instance.
282 * @compare: the #AsRequireKind, e.g. %AS_REQUIRE_KIND_ID.
283 *
284 * Sets the require version comparison type.
285 *
286 * Since: 0.6.7
287 **/
288 void
as_require_set_compare(AsRequire * require,AsRequireCompare compare)289 as_require_set_compare (AsRequire *require, AsRequireCompare compare)
290 {
291 AsRequirePrivate *priv = GET_PRIVATE (require);
292 g_return_if_fail (AS_IS_REQUIRE (require));
293 priv->compare = compare;
294 }
295
296 /**
297 * as_require_set_version:
298 * @require: a #AsRequire instance.
299 * @version: an version number, e.g. `0.1.2`
300 *
301 * Sets the require version.
302 *
303 * Since: 0.6.7
304 **/
305 void
as_require_set_version(AsRequire * require,const gchar * version)306 as_require_set_version (AsRequire *require, const gchar *version)
307 {
308 AsRequirePrivate *priv = GET_PRIVATE (require);
309 g_return_if_fail (AS_IS_REQUIRE (require));
310 if (priv->version != NULL)
311 as_ref_string_unref (priv->version);
312 priv->version = as_ref_string_new (version);
313 }
314
315 /**
316 * as_require_set_value:
317 * @require: a #AsRequire instance.
318 * @value: an require version, e.g. `firmware`
319 *
320 * Sets the require value.
321 *
322 * Since: 0.6.7
323 **/
324 void
as_require_set_value(AsRequire * require,const gchar * value)325 as_require_set_value (AsRequire *require, const gchar *value)
326 {
327 AsRequirePrivate *priv = GET_PRIVATE (require);
328 g_return_if_fail (AS_IS_REQUIRE (require));
329 if (priv->value != NULL)
330 as_ref_string_unref (priv->value);
331 priv->value = as_ref_string_new (value);
332 }
333
334
335 /**
336 * as_require_version_compare:
337 * @require: a #AsRequire instance.
338 * @version: a version number, e.g. `0.1.3`
339 * @error: A #GError or %NULL
340 *
341 * Compares the version number of the requirement with a predicate.
342 *
343 * Returns: %TRUE if the predicate was true
344 *
345 * Since: 0.6.7
346 **/
347 gboolean
as_require_version_compare(AsRequire * require,const gchar * version,GError ** error)348 as_require_version_compare (AsRequire *require,
349 const gchar *version,
350 GError **error)
351 {
352 AsRequirePrivate *priv = GET_PRIVATE (require);
353 gboolean ret = FALSE;
354 gint rc = 0;
355
356 g_return_val_if_fail (AS_IS_REQUIRE (require), FALSE);
357
358 switch (priv->compare) {
359 case AS_REQUIRE_COMPARE_EQ:
360 rc = as_utils_vercmp_full (version, priv->version, AS_VERSION_COMPARE_FLAG_NONE);
361 ret = rc == 0;
362 break;
363 case AS_REQUIRE_COMPARE_NE:
364 rc = as_utils_vercmp_full (version, priv->version, AS_VERSION_COMPARE_FLAG_NONE);
365 ret = rc != 0;
366 break;
367 case AS_REQUIRE_COMPARE_LT:
368 rc = as_utils_vercmp_full (version, priv->version, AS_VERSION_COMPARE_FLAG_NONE);
369 ret = rc < 0;
370 break;
371 case AS_REQUIRE_COMPARE_GT:
372 rc = as_utils_vercmp_full (version, priv->version, AS_VERSION_COMPARE_FLAG_NONE);
373 ret = rc > 0;
374 break;
375 case AS_REQUIRE_COMPARE_LE:
376 rc = as_utils_vercmp_full (version, priv->version, AS_VERSION_COMPARE_FLAG_NONE);
377 ret = rc <= 0;
378 break;
379 case AS_REQUIRE_COMPARE_GE:
380 rc = as_utils_vercmp_full (version, priv->version, AS_VERSION_COMPARE_FLAG_NONE);
381 ret = rc >= 0;
382 break;
383 case AS_REQUIRE_COMPARE_GLOB:
384 #ifdef _WIN32
385 ret = g_strcmp0 (priv->version, version) == 0;
386 #else
387 ret = fnmatch (priv->version, version, 0) == 0;
388 #endif
389 break;
390 case AS_REQUIRE_COMPARE_REGEX:
391 ret = g_regex_match_simple (priv->version, version, 0, 0);
392 break;
393 default:
394 break;
395 }
396
397 /* could not compare */
398 if (rc == G_MAXINT) {
399 g_set_error (error,
400 AS_UTILS_ERROR,
401 AS_UTILS_ERROR_FAILED,
402 "failed to compare [%s] and [%s]",
403 priv->version,
404 version);
405 return FALSE;
406 }
407
408 /* set error */
409 if (!ret && error != NULL) {
410 g_set_error (error,
411 AS_UTILS_ERROR,
412 AS_UTILS_ERROR_FAILED,
413 "failed predicate [%s %s %s]",
414 priv->version,
415 as_require_compare_to_string (priv->compare),
416 version);
417 }
418 return ret;
419 }
420
421 /**
422 * as_require_equal:
423 * @require1: a #AsRequire instance.
424 * @require2: a #AsRequire instance.
425 *
426 * Checks if two requires are the same.
427 *
428 * Returns: %TRUE for success
429 *
430 * Since: 0.7.7
431 **/
432 gboolean
as_require_equal(AsRequire * require1,AsRequire * require2)433 as_require_equal (AsRequire *require1, AsRequire *require2)
434 {
435 AsRequirePrivate *priv1 = GET_PRIVATE (require1);
436 AsRequirePrivate *priv2 = GET_PRIVATE (require2);
437
438 g_return_val_if_fail (AS_IS_REQUIRE (require1), FALSE);
439 g_return_val_if_fail (AS_IS_REQUIRE (require2), FALSE);
440
441 /* trivial */
442 if (require1 == require2)
443 return TRUE;
444
445 /* check for equality */
446 if (priv1->kind != priv2->kind)
447 return FALSE;
448 if (priv1->compare != priv2->compare)
449 return FALSE;
450 if (g_strcmp0 (priv1->version, priv2->version) != 0)
451 return FALSE;
452 if (g_strcmp0 (priv1->value, priv2->value) != 0)
453 return FALSE;
454
455 /* success */
456 return TRUE;
457 }
458
459 /**
460 * as_require_node_insert: (skip)
461 * @require: a #AsRequire instance.
462 * @parent: the parent #GNode to use..
463 * @ctx: the #AsNodeContext
464 *
465 * Inserts the require into the DOM tree.
466 *
467 * Returns: (transfer none): A populated #GNode
468 *
469 * Since: 0.6.7
470 **/
471 GNode *
as_require_node_insert(AsRequire * require,GNode * parent,AsNodeContext * ctx)472 as_require_node_insert (AsRequire *require, GNode *parent, AsNodeContext *ctx)
473 {
474 AsRequirePrivate *priv = GET_PRIVATE (require);
475 GNode *n;
476
477 g_return_val_if_fail (AS_IS_REQUIRE (require), NULL);
478
479 /* don't know what to do here */
480 if (priv->kind == AS_REQUIRE_KIND_UNKNOWN)
481 return NULL;
482
483 n = as_node_insert (parent, as_require_kind_to_string (priv->kind), NULL,
484 AS_NODE_INSERT_FLAG_NONE,
485 NULL);
486 if (priv->compare != AS_REQUIRE_COMPARE_UNKNOWN) {
487 as_node_add_attribute (n, "compare",
488 as_require_compare_to_string (priv->compare));
489 }
490 if (priv->version != NULL)
491 as_node_add_attribute (n, "version", priv->version);
492 if (priv->value != NULL)
493 as_node_set_data (n, priv->value, AS_NODE_INSERT_FLAG_NONE);
494 return n;
495 }
496
497 /**
498 * as_require_node_parse:
499 * @require: a #AsRequire instance.
500 * @node: a #GNode.
501 * @ctx: a #AsNodeContext.
502 * @error: A #GError or %NULL.
503 *
504 * Populates the object from a DOM node.
505 *
506 * Returns: %TRUE for success
507 *
508 * Since: 0.6.7
509 **/
510 gboolean
as_require_node_parse(AsRequire * require,GNode * node,AsNodeContext * ctx,GError ** error)511 as_require_node_parse (AsRequire *require, GNode *node,
512 AsNodeContext *ctx, GError **error)
513 {
514 AsRequirePrivate *priv = GET_PRIVATE (require);
515 const gchar *tmp;
516 g_return_val_if_fail (AS_IS_REQUIRE (require), FALSE);
517 tmp = as_node_get_name (node);
518 if (tmp != NULL)
519 as_require_set_kind (require, as_require_kind_from_string (tmp));
520 tmp = as_node_get_attribute (node, "compare");
521 if (tmp != NULL)
522 as_require_set_compare (require, as_require_compare_from_string (tmp));
523 as_ref_string_assign (&priv->version, as_node_get_attribute_as_refstr (node, "version"));
524 as_ref_string_assign (&priv->value, as_node_get_data_as_refstr (node));
525 return TRUE;
526 }
527
528 /**
529 * as_require_node_parse_dep11:
530 * @require: a #AsRequire instance.
531 * @node: a #GNode.
532 * @ctx: a #AsNodeContext.
533 * @error: A #GError or %NULL.
534 *
535 * Populates the object from a DEP-11 node.
536 *
537 * Returns: %TRUE for success
538 *
539 * Since: 0.6.7
540 **/
541 gboolean
as_require_node_parse_dep11(AsRequire * im,GNode * node,AsNodeContext * ctx,GError ** error)542 as_require_node_parse_dep11 (AsRequire *im, GNode *node,
543 AsNodeContext *ctx, GError **error)
544 {
545 return TRUE;
546 }
547
548 /**
549 * as_require_new:
550 *
551 * Creates a new #AsRequire.
552 *
553 * Returns: (transfer full): a #AsRequire
554 *
555 * Since: 0.6.7
556 **/
557 AsRequire *
as_require_new(void)558 as_require_new (void)
559 {
560 AsRequire *require;
561 require = g_object_new (AS_TYPE_REQUIRE, NULL);
562 return AS_REQUIRE (require);
563 }
564