/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2012-2024 Matthias Klumpp * * Licensed under the GNU Lesser General Public License Version 2.1 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the license, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "ascli-utils.h" #include #include #include #include #include "as-pool-private.h" /** * Set to true if we don't want colored output */ static gboolean _colored_output = FALSE; /** * ascli_format_long_output: */ gchar * ascli_format_long_output (const gchar *str, guint line_length, guint indent_level) { GString *res = NULL; g_auto(GStrv) spl = NULL; if (str == NULL) return NULL; if (indent_level >= line_length) indent_level = line_length - 4; res = g_string_sized_new (strlen (str)); spl = as_markup_strsplit_words (str, line_length - indent_level); for (guint i = 0; spl[i] != NULL; i++) g_string_append (res, spl[i]); /* drop trailing newline */ if (res->len > 0) g_string_truncate (res, res->len - 1); /* indent the block, if requested */ if (indent_level > 0) { g_autofree gchar *spacing = g_strnfill (indent_level, ' '); g_autofree gchar *spacing_nl = g_strconcat ("\n", spacing, NULL); as_gstring_replace (res, "\n", spacing_nl, 0); g_string_prepend (res, spacing); } return g_string_free (res, FALSE); } /** * ascli_print_key_value: */ void ascli_print_key_value (const gchar *key, const gchar *val, gboolean line_wrap) { gchar *str; gchar *fmtval; g_return_if_fail (key != NULL); if ((val == NULL) || (g_strcmp0 (val, "") == 0)) return; if (line_wrap && (strlen (val) > 100)) { g_autofree gchar *tmp = ascli_format_long_output (val, 100, 2); fmtval = g_strconcat ("\n", tmp, NULL); } else { fmtval = g_strdup (val); } str = g_strdup_printf ("%s: ", key); if (_colored_output) { g_print ("%c[%dm%s%c[%dm%s\n", 0x1B, 1, str, 0x1B, 0, fmtval); } else { g_print ("%s%s\n", str, fmtval); } g_free (str); g_free (fmtval); } /** * ascli_print_highlight: */ void ascli_print_highlight (const gchar *format, ...) { va_list args; g_autofree gchar *msg = NULL; va_start (args, format); msg = g_strdup_vprintf (format, args); va_end (args); if (_colored_output) { g_print ("%c[%dm%s%c[%dm\n", 0x1B, 1, msg, 0x1B, 0); } else { g_print ("%s\n", msg); } } /** * ascli_print_separator: */ void ascli_print_separator (void) { if (_colored_output) { g_print ("%c[%dm%s\n%c[%dm", 0x1B, 36, "---", 0x1B, 0); } else { g_print ("---\n"); } } /** * ascli_print_stderr: */ void ascli_print_stderr (const gchar *format, ...) { va_list args; g_autofree gchar *str = NULL; va_start (args, format); str = g_strdup_vprintf (format, args); va_end (args); g_printerr ("%s\n", str); } /** * ascli_print_stdout: */ void ascli_print_stdout (const gchar *format, ...) { va_list args; gchar *str; va_start (args, format); str = g_strdup_vprintf (format, args); va_end (args); g_print ("%s\n", str); g_free (str); } /** * ascli_get_char_success: */ const gchar * ascli_get_char_success (void) { if (_colored_output) return "\x1B[32m" "✔" "\033[0m"; else return "✔"; } /** * ascli_get_char_failure: */ const gchar * ascli_get_char_failure (void) { if (_colored_output) return "\x1B[31m" "✘" "\033[0m"; else return "✘"; } /** * ascli_set_output_colored: */ void ascli_set_output_colored (gboolean colored) { _colored_output = colored; } /** * ascli_get_output_colored: */ gboolean ascli_get_output_colored (void) { return _colored_output; } /** * as_get_bundle_str: */ static gchar * as_get_bundle_str (AsComponent *cpt) { GString *gstr; if (!as_component_has_bundle (cpt)) return NULL; gstr = g_string_new (""); for (guint i = 0; i < AS_BUNDLE_KIND_LAST; i++) { AsBundleKind kind = (AsBundleKind) i; AsBundle *bundle; bundle = as_component_get_bundle (cpt, kind); if (bundle == NULL) continue; g_string_append_printf (gstr, "%s:%s, ", as_bundle_kind_to_string (kind), as_bundle_get_id (bundle)); } if (gstr->len > 0) g_string_truncate (gstr, gstr->len - 2); return g_string_free (gstr, FALSE); } /** * ascli_ptrarray_to_pretty: * * Pretty-print a GPtrArray. */ static gchar * ascli_ptrarray_to_pretty (GPtrArray *array, guint indent) { GString *rstr = NULL; guint i; if (array->len == 1) return g_strdup (g_ptr_array_index (array, 0)); rstr = g_string_new ("\n"); for (i = 0; i < array->len; i++) { const gchar *astr = (const gchar *) g_ptr_array_index (array, i); g_string_append_printf (rstr, "%*s- %s\n", indent, "", astr); } if (rstr->len > 0) g_string_truncate (rstr, rstr->len - 1); return g_string_free (rstr, FALSE); } /** * ascli_print_component: * * Print well-formatted details about a component to stdout. */ void ascli_print_component (AsComponent *cpt, gboolean show_detailed) { gchar *short_idline; gchar *pkgs_str = NULL; gchar *bundles_str = NULL; AsIcon *icon = NULL; short_idline = g_strdup_printf ("%s [%s]", as_component_get_id (cpt), as_component_kind_to_string (as_component_get_kind (cpt))); if (as_component_get_pkgnames (cpt) != NULL) pkgs_str = g_strjoinv (", ", as_component_get_pkgnames (cpt)); bundles_str = as_get_bundle_str (cpt); icon = as_component_get_icon_by_size (cpt, 64, 64); if (icon == NULL) { GPtrArray *icons = as_component_get_icons (cpt); for (guint j = 0; j < icons->len; j++) { AsIcon *tmp_icon = AS_ICON (g_ptr_array_index (icons, j)); if (as_icon_get_kind (tmp_icon) == AS_ICON_KIND_STOCK) { icon = tmp_icon; break; } } } ascli_print_key_value (_("Identifier"), short_idline, FALSE); if (show_detailed && as_component_get_kind (cpt) != AS_COMPONENT_KIND_OPERATING_SYSTEM) ascli_print_key_value (_("Internal ID"), as_component_get_data_id (cpt), FALSE); ascli_print_key_value (_("Name"), as_component_get_name (cpt), FALSE); ascli_print_key_value (_("Summary"), as_component_get_summary (cpt), TRUE); ascli_print_key_value (_("Package"), pkgs_str, FALSE); ascli_print_key_value (_("Bundle"), bundles_str, FALSE); ascli_print_key_value ( _("Homepage"), as_component_get_url (cpt, AS_URL_KIND_HOMEPAGE), FALSE); ascli_print_key_value (_("Icon"), icon == NULL ? NULL : as_icon_get_name (icon), FALSE); g_free (short_idline); g_free (pkgs_str); g_free (bundles_str); short_idline = NULL; if (show_detailed) { GPtrArray *sshot_array; GPtrArray *imgs = NULL; GPtrArray *extends; GPtrArray *addons; GPtrArray *categories; GPtrArray *compulsory_desktops; GPtrArray *provided; AsScreenshot *sshot; AsImage *img; gchar *str; /* developer name */ ascli_print_key_value (_("Developer"), as_developer_get_name (as_component_get_developer (cpt)), TRUE); /* extends data (e.g. for addons) */ extends = as_component_get_extends (cpt); if (extends->len > 0) { str = ascli_ptrarray_to_pretty (extends, 2); ascli_print_key_value (_("Extends"), str, FALSE); g_free (str); } /* long description */ str = as_markup_convert (as_component_get_description (cpt), AS_MARKUP_KIND_TEXT, NULL); ascli_print_key_value (_("Description"), str, TRUE); g_free (str); /* some simple screenshot information */ sshot_array = as_component_get_screenshots_all (cpt); /* find default screenshot, if possible */ sshot = NULL; for (guint j = 0; j < sshot_array->len; j++) { sshot = AS_SCREENSHOT (g_ptr_array_index (sshot_array, j)); if (as_screenshot_get_kind (sshot) == AS_SCREENSHOT_KIND_DEFAULT) break; } if (sshot != NULL) { /* get the first source image and display it's url */ imgs = as_screenshot_get_images (sshot); for (guint j = 0; j < imgs->len; j++) { img = AS_IMAGE (g_ptr_array_index (imgs, j)); if (as_image_get_kind (img) == AS_IMAGE_KIND_SOURCE) { ascli_print_key_value (_("Default Screenshot URL"), as_image_get_url (img), TRUE); break; } } } /* project group */ ascli_print_key_value ( _("Project Group"), as_component_get_project_group (cpt), TRUE); /* license */ ascli_print_key_value (_("License"), as_component_get_project_license (cpt), TRUE); /* Categories */ categories = as_component_get_categories (cpt); if (categories->len > 0) { str = ascli_ptrarray_to_pretty (categories, 2); ascli_print_key_value (_("Categories"), str, FALSE); g_free (str); } /* Desktop-compulsority */ compulsory_desktops = as_component_get_compulsory_for_desktops (cpt); if (compulsory_desktops->len > 0) { str = ascli_ptrarray_to_pretty (compulsory_desktops, 2); ascli_print_key_value (_("Compulsory for"), str, FALSE); g_free (str); } /* list of addons extending this component */ addons = as_component_get_addons (cpt); if (addons->len > 0) { g_autoptr(GPtrArray) addons_str = g_ptr_array_new_with_free_func (g_free); for (guint i = 0; i < addons->len; i++) { AsComponent *addon = AS_COMPONENT (g_ptr_array_index (addons, i)); if (as_component_get_kind (addon) == AS_COMPONENT_KIND_LOCALIZATION) { g_ptr_array_add ( addons_str, g_strdup_printf ("l10n: %s", as_component_get_id (addon))); } else { g_ptr_array_add ( addons_str, g_strdup_printf ("%s (%s)", as_component_get_id (addon), as_component_get_name (cpt))); } } str = ascli_ptrarray_to_pretty (addons_str, 2); /* TRANSLATORS: Addons are extensions for existing software components, e.g. support for more visual effects for a video editor */ ascli_print_key_value (_("Add-ons"), str, FALSE); g_free (str); } /* Provided Items */ provided = as_component_get_provided (cpt); if (provided->len > 0) { /* TRANSLATORS: Other software or interfaces that this software component provides */ ascli_print_key_value (_("Provided Items"), "↓", FALSE); } for (guint i = 0; i < provided->len; i++) { GPtrArray *items = NULL; AsProvided *prov = AS_PROVIDED (g_ptr_array_index (provided, i)); items = as_provided_get_items (prov); if (items->len > 0) { g_autofree gchar *keyname = NULL; str = ascli_ptrarray_to_pretty (items, 4); keyname = g_strdup_printf ( " %s", as_provided_kind_to_l10n_string (as_provided_get_kind (prov))); ascli_print_key_value (keyname, str, FALSE); g_free (str); } } } } /** * ascli_print_components: * * Print well-formatted details about multiple components to stdout. */ void ascli_print_components (AsComponentBox *cbox, gboolean show_detailed) { for (guint i = 0; i < as_component_box_len (cbox); i++) { AsComponent *cpt = as_component_box_index (cbox, i); ascli_print_component (cpt, show_detailed); if (i < as_component_box_len (cbox) - 1) ascli_print_separator (); } } /** * ascli_data_pool_new_and_open: */ AsPool * ascli_data_pool_new_and_open (const gchar *cachepath, gboolean no_cache, GError **error) { AsPool *dpool; AsPoolFlags flags; dpool = as_pool_new (); flags = as_pool_get_flags (dpool); if (no_cache) as_flags_add (flags, AS_POOL_FLAG_IGNORE_CACHE_AGE); if (cachepath == NULL) { /* no cache object to load, we can use a normal pool */ as_pool_load (dpool, NULL, error); } else { /* use an exported cache object */ as_pool_override_cache_locations (dpool, cachepath, cachepath); as_pool_load (dpool, NULL, error); } as_pool_set_flags (dpool, flags); return dpool; } /** * ascli_query_numer: * @question: question to ask user * @maxnum: maximum number allowed * * Prompt the user to enter a number and confirm. * * Return value: a number entered by the user. **/ guint ascli_prompt_numer (const gchar *question, guint maxnum) { gint answer = 0; gint retval; /* pretty print */ g_print ("%s ", question); do { char buffer[64]; /* swallow the \n at end of line too */ if (!fgets (buffer, sizeof (buffer), stdin)) break; if (strlen (buffer) == sizeof (buffer) - 1) continue; /* get a number */ retval = sscanf (buffer, "%u", &answer); /* positive */ if (retval == 1 && answer > 0 && answer <= (gint) maxnum) break; g_print (_("Please enter a number from 1 to %i: "), maxnum); } while (TRUE); return answer; }