aboutsummaryrefslogtreecommitdiff
path: root/hardinfo2
diff options
context:
space:
mode:
authorLucas de Castro Borges <lucas@gnuabordo.com.br>2024-04-22 00:35:56 -0300
committerLucas de Castro Borges <lucas@gnuabordo.com.br>2024-04-22 00:35:56 -0300
commit754b5d1114f096778e483f8a6f3a5dc333225e26 (patch)
tree30911ec9da4cfd2f5572c27f7288fcbfa4cd212d /hardinfo2
parent35c2857da302ab8b3c308052f2cd1674fb4141a6 (diff)
parent5f01c706267c595de92406a32e7f31ef5056c2d0 (diff)
Update upstream source from tag 'upstream/2.0.3pre'
Update to upstream version '2.0.3pre' with Debian dir 6683980bf6b5c02f6847fd56765833301f75f4f3
Diffstat (limited to 'hardinfo2')
-rw-r--r--hardinfo2/binreloc.c668
-rw-r--r--hardinfo2/cpu_util.c252
-rw-r--r--hardinfo2/dmi_util.c450
-rw-r--r--hardinfo2/dt_util.c1187
-rw-r--r--hardinfo2/expr.c250
-rw-r--r--hardinfo2/gg_key_file_parse_string_as_value.c112
-rw-r--r--hardinfo2/gg_strescape.c139
-rw-r--r--hardinfo2/gpu_util.c477
-rw-r--r--hardinfo2/hardinfo.c175
-rw-r--r--hardinfo2/hinote_util.c27
-rw-r--r--hardinfo2/info.c584
-rw-r--r--hardinfo2/pci_util.c463
-rw-r--r--hardinfo2/problem_marker.c13
-rw-r--r--hardinfo2/socket.c127
-rw-r--r--hardinfo2/storage_util.c250
-rw-r--r--hardinfo2/udisks2_util.c641
-rw-r--r--hardinfo2/usb_util.c557
-rw-r--r--hardinfo2/util.c1452
-rw-r--r--hardinfo2/vendor.c655
-rw-r--r--hardinfo2/x_util.c342
20 files changed, 8821 insertions, 0 deletions
diff --git a/hardinfo2/binreloc.c b/hardinfo2/binreloc.c
new file mode 100644
index 00000000..f47a3fae
--- /dev/null
+++ b/hardinfo2/binreloc.c
@@ -0,0 +1,668 @@
+/*
+ * BinReloc - a library for creating relocatable executables
+ * Written by: Hongli Lai <h.lai@chello.nl>
+ * http://autopackage.org/
+ *
+ * This source code is public domain. You can relicense this code
+ * under whatever license you want.
+ *
+ * See http://autopackage.org/docs/binreloc/ for
+ * more information and how to use this.
+ */
+
+#ifndef __BINRELOC_C__
+#define __BINRELOC_C__
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include "binreloc.h"
+#include "config.h"
+
+G_BEGIN_DECLS
+/** @internal
+ * Find the canonical filename of the executable. Returns the filename
+ * (which must be freed) or NULL on error. If the parameter 'error' is
+ * not NULL, the error code will be stored there, if an error occured.
+ */
+static char *_br_find_exe(GbrInitError * error)
+{
+ char *path, *path2, *line, *result;
+ size_t buf_size;
+ ssize_t size;
+ struct stat stat_buf;
+ FILE *f;
+
+ /* Read from /proc/self/exe (symlink) */
+ if (sizeof(path) > SSIZE_MAX)
+ buf_size = SSIZE_MAX - 1;
+ else
+ buf_size = PATH_MAX - 1;
+ path = (char *) g_try_malloc(buf_size);
+ if (path == NULL) {
+ /* Cannot allocate memory. */
+ if (error)
+ *error = GBR_INIT_ERROR_NOMEM;
+ return NULL;
+ }
+ path2 = (char *) g_try_malloc(buf_size);
+ if (path2 == NULL) {
+ /* Cannot allocate memory. */
+ if (error)
+ *error = GBR_INIT_ERROR_NOMEM;
+ g_free(path);
+ return NULL;
+ }
+
+ strncpy(path2, "/proc/self/exe", buf_size - 1);
+
+ while (1) {
+ int i;
+
+ size = readlink(path2, path, buf_size - 1);
+ if (size == -1) {
+ /* Error. */
+ g_free(path2);
+ break;
+ }
+
+ /* readlink() success. */
+ path[size] = '\0';
+
+ /* Check whether the symlink's target is also a symlink.
+ * We want to get the final target. */
+ i = stat(path, &stat_buf);
+ if (i == -1) {
+ /* Error. */
+ g_free(path2);
+ break;
+ }
+
+ /* stat() success. */
+ if (!S_ISLNK(stat_buf.st_mode)) {
+ /* path is not a symlink. Done. */
+ g_free(path2);
+ return path;
+ }
+
+ /* path is a symlink. Continue loop and resolve this. */
+ strncpy(path, path2, buf_size - 1);
+ }
+
+
+ /* readlink() or stat() failed; this can happen when the program is
+ * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
+
+ buf_size = PATH_MAX + 128;
+ line = (char *) g_try_realloc(path, buf_size);
+ if (line == NULL) {
+ /* Cannot allocate memory. */
+ g_free(path);
+ if (error)
+ *error = GBR_INIT_ERROR_NOMEM;
+ return NULL;
+ }
+
+ f = fopen("/proc/self/maps", "r");
+ if (f == NULL) {
+ g_free(line);
+ if (error)
+ *error = GBR_INIT_ERROR_OPEN_MAPS;
+ return NULL;
+ }
+
+ /* The first entry should be the executable name. */
+ result = fgets(line, (int) buf_size, f);
+ if (result == NULL) {
+ fclose(f);
+ g_free(line);
+ if (error)
+ *error = GBR_INIT_ERROR_READ_MAPS;
+ return NULL;
+ }
+
+ /* Get rid of newline character. */
+ buf_size = strlen(line);
+ if (buf_size <= 0) {
+ /* Huh? An empty string? */
+ fclose(f);
+ g_free(line);
+ if (error)
+ *error = GBR_INIT_ERROR_INVALID_MAPS;
+ return NULL;
+ }
+ if (line[buf_size - 1] == 10)
+ line[buf_size - 1] = 0;
+
+ /* Extract the filename; it is always an absolute path. */
+ path = strchr(line, '/');
+
+ /* Sanity check. */
+ if (strstr(line, " r-xp ") == NULL || path == NULL) {
+ fclose(f);
+ g_free(line);
+ if (error)
+ *error = GBR_INIT_ERROR_INVALID_MAPS;
+ return NULL;
+ }
+
+ path = g_strdup(path);
+ g_free(line);
+ fclose(f);
+ return path;
+}
+
+
+/** @internal
+ * Find the canonical filename of the executable which owns symbol.
+ * Returns a filename which must be freed, or NULL on error.
+ */
+static char *_br_find_exe_for_symbol(const void *symbol,
+ GbrInitError * error)
+{
+#define SIZE PATH_MAX + 100
+ FILE *f;
+ size_t address_string_len;
+ char *address_string, line[SIZE], *found;
+
+ if (symbol == NULL)
+ return (char *) NULL;
+
+ f = fopen("/proc/self/maps", "r");
+ if (f == NULL)
+ return (char *) NULL;
+
+ address_string_len = 4;
+ address_string = (char *) g_try_malloc(address_string_len);
+ found = (char *) NULL;
+
+ while (!feof(f)) {
+ char *start_addr, *end_addr, *end_addr_end, *file;
+ void *start_addr_p, *end_addr_p;
+ size_t len;
+
+ if (fgets(line, SIZE, f) == NULL)
+ break;
+
+ /* Sanity check. */
+ if (strstr(line, " r-xp ") == NULL || strchr(line, '/') == NULL)
+ continue;
+
+ /* Parse line. */
+ start_addr = line;
+ end_addr = strchr(line, '-');
+ file = strchr(line, '/');
+
+ /* More sanity check. */
+ if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
+ continue;
+
+ end_addr[0] = '\0';
+ end_addr++;
+ end_addr_end = strchr(end_addr, ' ');
+ if (end_addr_end == NULL)
+ continue;
+
+ end_addr_end[0] = '\0';
+ len = strlen(file);
+ if (len == 0)
+ continue;
+ if (file[len - 1] == '\n')
+ file[len - 1] = '\0';
+
+ /* Get rid of "(deleted)" from the filename. */
+ len = strlen(file);
+ if (len > 10 && strcmp(file + len - 10, " (deleted)") == 0)
+ file[len - 10] = '\0';
+
+ /* I don't know whether this can happen but better safe than sorry. */
+ len = strlen(start_addr);
+ if (len != strlen(end_addr))
+ continue;
+
+
+ /* Transform the addresses into a string in the form of 0xdeadbeef,
+ * then transform that into a pointer. */
+ if (address_string_len < len + 3) {
+ address_string_len = len + 3;
+ address_string =
+ (char *) g_try_realloc(address_string, address_string_len);
+ }
+
+ memcpy(address_string, "0x", 2);
+ memcpy(address_string + 2, start_addr, len);
+ address_string[2 + len] = '\0';
+ sscanf(address_string, "%p", &start_addr_p);
+
+ memcpy(address_string, "0x", 2);
+ memcpy(address_string + 2, end_addr, len);
+ address_string[2 + len] = '\0';
+ sscanf(address_string, "%p", &end_addr_p);
+
+
+ if (symbol >= start_addr_p && symbol < end_addr_p) {
+ found = file;
+ break;
+ }
+ }
+
+ g_free(address_string);
+ fclose(f);
+
+ if (found == NULL)
+ return (char *) NULL;
+ else
+ return g_strdup(found);
+}
+
+
+static gchar *exe = NULL;
+
+static void set_gerror(GError ** error, GbrInitError errcode);
+
+
+/** Initialize the BinReloc library (for applications).
+ *
+ * This function must be called before using any other BinReloc functions.
+ * It attempts to locate the application's canonical filename.
+ *
+ * @note If you want to use BinReloc for a library, then you should call
+ * gbr_init_lib() instead.
+ *
+ * @param error If BinReloc failed to initialize, then the error report will
+ * be stored in this variable. Set to NULL if you don't want an
+ * error report. See the #GbrInitError for a list of error
+ * codes.
+ *
+ * @returns TRUE on success, FALSE if BinReloc failed to initialize.
+ */
+gboolean gbr_init(GError ** error)
+{
+ GbrInitError errcode = 0;
+
+ /* Locate the application's filename. */
+ exe = _br_find_exe(&errcode);
+ if (exe != NULL)
+ /* Success! */
+ return TRUE;
+ else {
+ /* Failed :-( */
+ set_gerror(error, errcode);
+ return FALSE;
+ }
+}
+
+
+/** Initialize the BinReloc library (for libraries).
+ *
+ * This function must be called before using any other BinReloc functions.
+ * It attempts to locate the calling library's canonical filename.
+ *
+ * @note The BinReloc source code MUST be included in your library, or this
+ * function won't work correctly.
+ *
+ * @returns TRUE on success, FALSE if a filename cannot be found.
+ */
+gboolean gbr_init_lib(GError ** error)
+{
+ GbrInitError errcode = 0;
+
+ exe = _br_find_exe_for_symbol((const void *) "", &errcode);
+ if (exe != NULL)
+ /* Success! */
+ return TRUE;
+ else {
+ /* Failed :-( */
+ set_gerror(error, errcode);
+ return exe != NULL;
+ }
+}
+
+
+static void set_gerror(GError ** error, GbrInitError errcode)
+{
+ gchar *error_message;
+
+ if (error == NULL)
+ return;
+
+ switch (errcode) {
+ case GBR_INIT_ERROR_NOMEM:
+ error_message = "Cannot allocate memory.";
+ break;
+ case GBR_INIT_ERROR_OPEN_MAPS:
+ error_message = "Unable to open /proc/self/maps for reading.";
+ break;
+ case GBR_INIT_ERROR_READ_MAPS:
+ error_message = "Unable to read from /proc/self/maps.";
+ break;
+ case GBR_INIT_ERROR_INVALID_MAPS:
+ error_message = "The file format of /proc/self/maps is invalid.";
+ break;
+ case GBR_INIT_ERROR_DISABLED:
+ error_message = "Binary relocation support is disabled.";
+ break;
+ default:
+ error_message = "Unknown error.";
+ break;
+ };
+ g_set_error(error, g_quark_from_static_string("GBinReloc"),
+ errcode, "%s", error_message);
+}
+
+
+/** Find the canonical filename of the current application.
+ *
+ * @param default_exe A default filename which will be used as fallback.
+ * @returns A string containing the application's canonical filename,
+ * which must be freed when no longer necessary. If BinReloc is
+ * not initialized, or if the initialization function failed,
+ * then a copy of default_exe will be returned. If default_exe
+ * is NULL, then NULL will be returned.
+ */
+gchar *gbr_find_exe(const gchar * default_exe)
+{
+ if (exe == NULL) {
+ /* BinReloc is not initialized. */
+ if (default_exe != NULL)
+ return g_strdup(default_exe);
+ else
+ return NULL;
+ }
+ return g_strdup(exe);
+}
+
+
+/** Locate the directory in which the current application is installed.
+ *
+ * The prefix is generated by the following pseudo-code evaluation:
+ * \code
+ * dirname(exename)
+ * \endcode
+ *
+ * @param default_dir A default directory which will used as fallback.
+ * @return A string containing the directory, which must be freed when no
+ * longer necessary. If BinReloc is not initialized, or if the
+ * initialization function failed, then a copy of default_dir
+ * will be returned. If default_dir is NULL, then NULL will be
+ * returned.
+ */
+gchar *gbr_find_exe_dir(const gchar * default_dir)
+{
+ if (exe == NULL) {
+ /* BinReloc not initialized. */
+ if (default_dir != NULL)
+ return g_strdup(default_dir);
+ else
+ return NULL;
+ }
+
+ return g_path_get_dirname(exe);
+}
+
+
+/** Locate the prefix in which the current application is installed.
+ *
+ * The prefix is generated by the following pseudo-code evaluation:
+ * \code
+ * dirname(dirname(exename))
+ * \endcode
+ *
+ * @param default_prefix A default prefix which will used as fallback.
+ * @return A string containing the prefix, which must be freed when no
+ * longer necessary. If BinReloc is not initialized, or if the
+ * initialization function failed, then a copy of default_prefix
+ * will be returned. If default_prefix is NULL, then NULL will be
+ * returned.
+ */
+gchar *gbr_find_prefix(const gchar * default_prefix)
+{
+ gchar *dir1, *dir2;
+
+ if (exe == NULL) {
+ /* BinReloc not initialized. */
+ if (default_prefix != NULL)
+ return g_strdup(default_prefix);
+ else
+ return NULL;
+ }
+
+ dir1 = g_path_get_dirname(exe);
+ dir2 = g_path_get_dirname(dir1);
+ g_free(dir1);
+ return dir2;
+}
+
+
+/** Locate the application's binary folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/bin"
+ * \endcode
+ *
+ * @param default_bin_dir A default path which will used as fallback.
+ * @return A string containing the bin folder's path, which must be freed when
+ * no longer necessary. If BinReloc is not initialized, or if the
+ * initialization function failed, then a copy of default_bin_dir will
+ * be returned. If default_bin_dir is NULL, then NULL will be returned.
+ */
+gchar *gbr_find_bin_dir(const gchar * default_bin_dir)
+{
+ gchar *prefix, *dir;
+
+ prefix = gbr_find_prefix(NULL);
+ if (prefix == NULL) {
+ /* BinReloc not initialized. */
+ if (default_bin_dir != NULL)
+ return g_strdup(default_bin_dir);
+ else
+ return NULL;
+ }
+
+ dir = g_build_filename(prefix, "bin", NULL);
+ g_free(prefix);
+ return dir;
+}
+
+
+/** Locate the application's superuser binary folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/sbin"
+ * \endcode
+ *
+ * @param default_sbin_dir A default path which will used as fallback.
+ * @return A string containing the sbin folder's path, which must be freed when
+ * no longer necessary. If BinReloc is not initialized, or if the
+ * initialization function failed, then a copy of default_sbin_dir will
+ * be returned. If default_bin_dir is NULL, then NULL will be returned.
+ */
+gchar *gbr_find_sbin_dir(const gchar * default_sbin_dir)
+{
+ gchar *prefix, *dir;
+
+ prefix = gbr_find_prefix(NULL);
+ if (prefix == NULL) {
+ /* BinReloc not initialized. */
+ if (default_sbin_dir != NULL)
+ return g_strdup(default_sbin_dir);
+ else
+ return NULL;
+ }
+
+ dir = g_build_filename(prefix, "sbin", NULL);
+ g_free(prefix);
+ return dir;
+}
+
+
+/** Locate the application's data folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/share"
+ * \endcode
+ *
+ * @param default_data_dir A default path which will used as fallback.
+ * @return A string containing the data folder's path, which must be freed when
+ * no longer necessary. If BinReloc is not initialized, or if the
+ * initialization function failed, then a copy of default_data_dir
+ * will be returned. If default_data_dir is NULL, then NULL will be
+ * returned.
+ */
+gchar *gbr_find_data_dir(const gchar * default_data_dir)
+{
+ gchar *prefix, *dir;
+
+ prefix = gbr_find_prefix(NULL);
+ if (prefix == NULL) {
+ /* BinReloc not initialized. */
+ if (default_data_dir != NULL)
+ return g_strdup(default_data_dir);
+ else
+ return NULL;
+ }
+
+ dir = g_build_filename(prefix, "share", NULL);
+ g_free(prefix);
+ return dir;
+}
+
+
+/** Locate the application's localization folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/share/locale"
+ * \endcode
+ *
+ * @param default_locale_dir A default path which will used as fallback.
+ * @return A string containing the localization folder's path, which must be freed when
+ * no longer necessary. If BinReloc is not initialized, or if the
+ * initialization function failed, then a copy of default_locale_dir will be returned.
+ * If default_locale_dir is NULL, then NULL will be returned.
+ */
+gchar *gbr_find_locale_dir(const gchar * default_locale_dir)
+{
+ gchar *data_dir, *dir;
+
+ data_dir = gbr_find_data_dir(NULL);
+ if (data_dir == NULL) {
+ /* BinReloc not initialized. */
+ if (default_locale_dir != NULL)
+ return g_strdup(default_locale_dir);
+ else
+ return NULL;
+ }
+
+ dir = g_build_filename(data_dir, "locale", NULL);
+ g_free(data_dir);
+ return dir;
+}
+
+
+/** Locate the application's library folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/lib"
+ * \endcode
+ *
+ * @param default_lib_dir A default path which will used as fallback.
+ * @return A string containing the library folder's path, which must be freed when
+ * no longer necessary. If BinReloc is not initialized, or if the
+ * initialization function failed, then a copy of default_lib_dir will be returned.
+ * If default_lib_dir is NULL, then NULL will be returned.
+ */
+gchar *gbr_find_lib_dir(const gchar * default_lib_dir)
+{
+ gchar *prefix, *dir;
+
+ prefix = gbr_find_prefix(NULL);
+ if (prefix == NULL) {
+ /* BinReloc not initialized. */
+ if (default_lib_dir != NULL)
+ return g_strdup(default_lib_dir);
+ else
+ return NULL;
+ }
+
+ dir = g_build_filename(prefix, LIBDIR, NULL);
+
+ g_free(prefix);
+ return dir;
+}
+
+
+/** Locate the application's libexec folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/libexec"
+ * \endcode
+ *
+ * @param default_libexec_dir A default path which will used as fallback.
+ * @return A string containing the libexec folder's path, which must be freed when
+ * no longer necessary. If BinReloc is not initialized, or if the initialization
+ * function failed, then a copy of default_libexec_dir will be returned.
+ * If default_libexec_dir is NULL, then NULL will be returned.
+ */
+gchar *gbr_find_libexec_dir(const gchar * default_libexec_dir)
+{
+ gchar *prefix, *dir;
+
+ prefix = gbr_find_prefix(NULL);
+ if (prefix == NULL) {
+ /* BinReloc not initialized. */
+ if (default_libexec_dir != NULL)
+ return g_strdup(default_libexec_dir);
+ else
+ return NULL;
+ }
+
+ dir = g_build_filename(prefix, "libexec", NULL);
+ g_free(prefix);
+ return dir;
+}
+
+
+/** Locate the application's configuration files folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/etc"
+ * \endcode
+ *
+ * @param default_etc_dir A default path which will used as fallback.
+ * @return A string containing the etc folder's path, which must be freed when
+ * no longer necessary. If BinReloc is not initialized, or if the initialization
+ * function failed, then a copy of default_etc_dir will be returned.
+ * If default_etc_dir is NULL, then NULL will be returned.
+ */
+gchar *gbr_find_etc_dir(const gchar * default_etc_dir)
+{
+ gchar *prefix, *dir;
+
+ prefix = gbr_find_prefix(NULL);
+ if (prefix == NULL) {
+ /* BinReloc not initialized. */
+ if (default_etc_dir != NULL)
+ return g_strdup(default_etc_dir);
+ else
+ return NULL;
+ }
+
+ dir = g_build_filename(prefix, "etc", NULL);
+ g_free(prefix);
+ return dir;
+}
+
+
+G_END_DECLS
+#endif /* __BINRELOC_C__ */
diff --git a/hardinfo2/cpu_util.c b/hardinfo2/cpu_util.c
new file mode 100644
index 00000000..acbe63db
--- /dev/null
+++ b/hardinfo2/cpu_util.c
@@ -0,0 +1,252 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2006 L. A. F. Pereira <l@tia.mat.br>
+ * This file by Burt P. <pburt0@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include "hardinfo.h"
+#include "cpu_util.h"
+#include "cpubits.h"
+
+#define CPU_TOPO_NULL -9877
+
+const gchar *byte_order_str() {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ return _("Little Endian");
+#else
+ return _("Big Endian");
+#endif
+}
+
+int processor_has_flag(gchar * strflags, gchar * strflag)
+{
+ gchar **flags;
+ gint ret = 0;
+ if (strflags == NULL || strflag == NULL)
+ return 0;
+ flags = g_strsplit(strflags, " ", 0);
+ ret = g_strv_contains((const gchar * const *)flags, strflag);
+ g_strfreev(flags);
+ return ret;
+}
+
+gchar* get_cpu_str(const gchar* file, gint cpuid) {
+ gchar *tmp0 = NULL;
+ gchar *tmp1 = NULL;
+ tmp0 = g_strdup_printf("/sys/devices/system/cpu/cpu%d/%s", cpuid, file);
+ g_file_get_contents(tmp0, &tmp1, NULL, NULL);
+ g_free(tmp0);
+ return tmp1;
+}
+
+gint get_cpu_int(const char* item, int cpuid, int null_val) {
+ gchar *fc = NULL;
+ int ret = null_val;
+ fc = get_cpu_str(item, cpuid);
+ if (fc) {
+ ret = atol(fc);
+ g_free(fc);
+ }
+ return ret;
+}
+
+/* cpubits is 32768 bits long
+ * core_ids are not unique among physical_ids
+ * hack up cpubits into 128 packs of 256 cores
+ * to make cores unique in cpubits */
+#define MAX_CORES_PER_PACK 256
+#define MAX_PACKS 128
+
+int cpu_procs_cores_threads_nodes(int *p, int *c, int *t, int *n)
+{
+ cpubits *threads, *cores, *packs;
+ char *tmp;
+ int i, m, pack_id, core_id;
+
+ g_file_get_contents("/sys/devices/system/cpu/present", &tmp, NULL, NULL);
+ if (tmp == NULL) {
+ *p = *c = *t = *n = -1;
+ return 0;
+ }
+
+ threads = cpubits_from_str(tmp);
+ cores = cpubits_from_str("");
+ packs = cpubits_from_str("");
+ m = cpubits_max(threads);
+ for (i = 0; i <= m; i++) {
+ pack_id = get_cpu_int("topology/physical_package_id", i, CPU_TOPO_NULL);
+ core_id = get_cpu_int("topology/core_id", i, CPU_TOPO_NULL);
+ if (pack_id < 0)
+ pack_id = 0;
+ CPUBIT_SET(packs, pack_id);
+ if (core_id >= 0) {
+ CPUBIT_SET(cores, (pack_id * MAX_CORES_PER_PACK) + core_id);
+ }
+ }
+
+ *t = cpubits_count(threads);
+ *c = cpubits_count(cores);
+ *p = cpubits_count(packs);
+ *n = 1;
+
+ g_free(tmp);
+ g_file_get_contents("/sys/devices/system/node/possible", &tmp, NULL, NULL);
+ if (tmp != NULL) {
+ cpubits *nodes = cpubits_from_str(tmp);
+ if (nodes)
+ *n = cpubits_count(nodes);
+ free(nodes);
+ }
+
+ if (!*c)
+ *c = 1;
+ if (!*p)
+ *p = 1;
+ if (!*n)
+ *n = 1;
+
+ g_free(threads);
+ g_free(cores);
+ g_free(packs);
+ g_free(tmp);
+ return 1;
+}
+
+cpufreq_data *cpufreq_new(gint id)
+{
+ cpufreq_data *cpufd;
+ cpufd = malloc(sizeof(cpufreq_data));
+ if (cpufd) {
+ memset(cpufd, 0, sizeof(cpufreq_data));
+ cpufd->id = id;
+ cpufreq_update(cpufd, 0);
+ }
+ return cpufd;
+}
+
+void cpufreq_update(cpufreq_data *cpufd, int cur_only)
+{
+ if (cpufd) {
+ cpufd->cpukhz_cur = get_cpu_int("cpufreq/scaling_cur_freq", cpufd->id, 0);
+ if (cur_only) return;
+ cpufd->scaling_driver = get_cpu_str("cpufreq/scaling_driver", cpufd->id);
+ cpufd->scaling_governor = get_cpu_str("cpufreq/scaling_governor", cpufd->id);
+ cpufd->transition_latency = get_cpu_int("cpufreq/cpuinfo_transition_latency", cpufd->id, 0);
+ cpufd->cpukhz_min = get_cpu_int("cpufreq/scaling_min_freq", cpufd->id, 0);
+ cpufd->cpukhz_max = get_cpu_int("cpufreq/scaling_max_freq", cpufd->id, 0);
+ if (cpufd->scaling_driver == NULL) cpufd->scaling_driver = g_strdup("(Unknown)");
+ if (cpufd->scaling_governor == NULL) cpufd->scaling_governor = g_strdup("(Unknown)");
+
+ /* x86 uses freqdomain_cpus, all others use affected_cpus */
+ cpufd->shared_list = get_cpu_str("cpufreq/freqdomain_cpus", cpufd->id);
+ if (cpufd->shared_list == NULL) cpufd->shared_list = get_cpu_str("cpufreq/affected_cpus", cpufd->id);
+ if (cpufd->shared_list == NULL) cpufd->shared_list = g_strdup_printf("%d", cpufd->id);
+ }
+}
+
+void cpufreq_free(cpufreq_data *cpufd)
+{
+ if (cpufd) {
+ g_free(cpufd->scaling_driver);
+ g_free(cpufd->scaling_governor);
+ }
+ g_free(cpufd);
+}
+
+cpu_topology_data *cputopo_new(gint id)
+{
+ cpu_topology_data *cputd;
+ cputd = malloc(sizeof(cpu_topology_data));
+ if (cputd) {
+ memset(cputd, 0, sizeof(cpu_topology_data));
+ cputd->id = id;
+ cputd->socket_id = get_cpu_int("topology/physical_package_id", id, CPU_TOPO_NULL);
+ cputd->core_id = get_cpu_int("topology/core_id", id, CPU_TOPO_NULL);
+ cputd->book_id = get_cpu_int("topology/book_id", id, CPU_TOPO_NULL);
+ cputd->drawer_id = get_cpu_int("topology/drawer_id", id, CPU_TOPO_NULL);
+ }
+ return cputd;
+
+}
+
+void cputopo_free(cpu_topology_data *cputd)
+{
+ g_free(cputd);
+}
+
+gchar *cpufreq_section_str(cpufreq_data *cpufd)
+{
+ if (cpufd == NULL)
+ return g_strdup("");
+
+ if (cpufd->cpukhz_min || cpufd->cpukhz_max || cpufd->cpukhz_cur) {
+ return g_strdup_printf(
+ "[%s]\n"
+ "%s=%d %s\n"
+ "%s=%d %s\n"
+ "%s=%d %s\n"
+ "%s=%d %s\n"
+ "%s=%s\n"
+ "%s=%s\n",
+ _("Frequency Scaling"),
+ _("Minimum"), cpufd->cpukhz_min, _("kHz"),
+ _("Maximum"), cpufd->cpukhz_max, _("kHz"),
+ _("Current"), cpufd->cpukhz_cur, _("kHz"),
+ _("Transition Latency"), cpufd->transition_latency, _("ns"),
+ _("Governor"), cpufd->scaling_governor,
+ _("Driver"), cpufd->scaling_driver);
+ } else {
+ return g_strdup_printf(
+ "[%s]\n"
+ "%s=%s\n",
+ _("Frequency Scaling"),
+ _("Driver"), cpufd->scaling_driver);
+ }
+}
+
+gchar *cputopo_section_str(cpu_topology_data *cputd)
+{
+ static const char na[] = N_("(Not Available)");
+ char sock_str[64] = "", core_str[64] = "";
+ char book_str[64] = "", drawer_str[64] = "";
+
+ if (cputd == NULL)
+ return g_strdup("");
+
+ if (cputd->socket_id != CPU_TOPO_NULL && cputd->socket_id != -1)
+ sprintf(sock_str, "%s=%d\n", _("Socket"), cputd->socket_id);
+ else
+ sprintf(sock_str, "%s=%s\n", _("Socket"), na);
+
+ if (cputd->core_id != CPU_TOPO_NULL)
+ sprintf(core_str, "%s=%d\n", _("Core"), cputd->core_id);
+ else
+ sprintf(core_str, "%s=%s\n", _("Core"), na);
+
+ if (cputd->book_id != CPU_TOPO_NULL)
+ sprintf(core_str, "%s=%d\n", _("Book"), cputd->book_id);
+ if (cputd->book_id != CPU_TOPO_NULL)
+ sprintf(core_str, "%s=%d\n", _("Drawer"), cputd->drawer_id);
+
+ return g_strdup_printf(
+ "[%s]\n"
+ "%s=%d\n"
+ "%s%s%s%s",
+ _("Topology"),
+ _("ID"), cputd->id,
+ sock_str, core_str, book_str, drawer_str );
+}
diff --git a/hardinfo2/dmi_util.c b/hardinfo2/dmi_util.c
new file mode 100644
index 00000000..35f9282f
--- /dev/null
+++ b/hardinfo2/dmi_util.c
@@ -0,0 +1,450 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br>
+ * This file
+ * Copyright (C) 2017 Burt P. <pburt0@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "dmi_util.h"
+
+static const char *dmi_type_strings[] = {
+ [0] = N_("BIOS Information"),
+ [1] = N_("System"),
+ [2] = N_("Base Board"),
+ [3] = N_("Chassis"),
+ [4] = N_("Processor"),
+ [5] = N_("Memory Controller"),
+ [6] = N_("Memory Module"),
+ [7] = N_("Cache"),
+ [8] = N_("Port Connector"),
+ [9] = N_("System Slots"),
+ [10] = N_("On Board Devices"),
+ [11] = N_("OEM Strings"),
+ [12] = N_("System Configuration Options"),
+ [13] = N_("BIOS Language"),
+ [14] = N_("Group Associations"),
+ [15] = N_("System Event Log"),
+ [16] = N_("Physical Memory Array"),
+ [17] = N_("Memory Device"),
+ [18] = N_("32-bit Memory Error"),
+ [19] = N_("Memory Array Mapped Address"),
+ [20] = N_("Memory Device Mapped Address"),
+ [21] = N_("Built-in Pointing Device"),
+ [22] = N_("Portable Battery"),
+ [23] = N_("System Reset"),
+ [24] = N_("Hardware Security"),
+ [25] = N_("System Power Controls"),
+ [26] = N_("Voltage Probe"),
+ [27] = N_("Cooling Device"),
+ [28] = N_("Temperature Probe"),
+ [29] = N_("Electrical Current Probe"),
+ [30] = N_("Out-of-band Remote Access"),
+ [31] = N_("Boot Integrity Services"),
+ [32] = N_("System Boot"),
+ [33] = N_("64-bit Memory Error"),
+ [34] = N_("Management Device"),
+ [35] = N_("Management Device Component"),
+ [36] = N_("Management Device Threshold Data"),
+ [37] = N_("Memory Channel"),
+ [38] = N_("IPMI Device"),
+ [39] = N_("Power Supply"),
+ [40] = N_("Additional Information"),
+ [41] = N_("Onboard Device"),
+ //127 = End of Table
+};
+
+/* frees the string and sets it NULL if it is to be ignored
+ * returns -1 if error, 0 if ok, 1 if ignored */
+static int ignore_placeholder_strings(gchar **pstr) {
+ gchar *chk, *p;
+ if (pstr == NULL || *pstr == NULL)
+ return -1;
+ chk = g_strdup(*pstr);
+
+#define DMI_IGNORE(m) if (strcasecmp(m, *pstr) == 0) { g_free(chk); g_free(*pstr); *pstr = NULL; return 1; }
+ DMI_IGNORE("To be filled by O.E.M.");
+ DMI_IGNORE("Default String");
+ DMI_IGNORE("System Product Name");
+ DMI_IGNORE("System Manufacturer");
+ DMI_IGNORE("System Version");
+ DMI_IGNORE("System Serial Number");
+ DMI_IGNORE("Rev X.0x"); /* ASUS board version nonsense */
+ DMI_IGNORE("x.x"); /* Gigabyte board version nonsense */
+ DMI_IGNORE("NA");
+ DMI_IGNORE("SKU");
+
+ /* noticed on an HP x360 with Insyde BIOS */
+ DMI_IGNORE("Type2 - Board Asset Tag");
+ DMI_IGNORE("Type1ProductConfigId");
+
+ /* Toshiba Laptop with Insyde BIOS */
+ DMI_IGNORE("Base Board Version");
+ DMI_IGNORE("No Asset Tag");
+ DMI_IGNORE("None");
+ DMI_IGNORE("Type1Family");
+ DMI_IGNORE("123456789");
+
+ /* ASUS socket 775 MB */
+ DMI_IGNORE("Asset-1234567890");
+ DMI_IGNORE("MB-1234567890");
+ DMI_IGNORE("Chassis Serial Number");
+ DMI_IGNORE("Chassis Version");
+ DMI_IGNORE("Chassis Manufacture");
+
+ /* Zotac version nonsense */
+ p = chk;
+ while (*p != 0) { *p = 'x'; p++; } /* all X */
+ DMI_IGNORE(chk);
+ p = chk;
+ while (*p != 0) { *p = '0'; p++; } /* all 0 */
+ DMI_IGNORE(chk);
+
+ /*... more, I'm sure. */
+
+ g_free(chk);
+ return 0;
+}
+
+static const char *dmi_sysfs_root(void) {
+ char *candidates[] = {
+ "/sys/devices/virtual/dmi",
+ "/sys/class/dmi",
+ NULL
+ };
+ int i = 0;
+ while (candidates[i] != NULL) {
+ if(access(candidates[i], F_OK) != -1)
+ return candidates[i];
+ i++;
+ }
+ return NULL;
+}
+
+int dmi_str_status(const char *id_str) {
+ gchar *str = dmi_get_str_abs(id_str);
+ int ret = 1;
+
+ if (!str)
+ ret = 0;
+
+ if ( ignore_placeholder_strings(&str) > 0 )
+ ret = -1;
+
+ g_free(str);
+ return ret;
+}
+
+char *dmi_get_str(const char *id_str) {
+ gchar *ret = dmi_get_str_abs(id_str);
+ /* return NULL on empty */
+ if (ret && *ret == 0) {
+ g_free(ret);
+ ret = NULL;
+ }
+ ignore_placeholder_strings(&ret);
+ return ret;
+}
+
+char *dmi_get_str_abs(const char *id_str) {
+ static struct {
+ char *id;
+ char *path;
+ } tab_dmi_sysfs[] = {
+ /* dmidecode -> sysfs */
+ { "bios-release-date", "id/bios_date" },
+ { "bios-vendor", "id/bios_vendor" },
+ { "bios-version", "id/bios_version" },
+ { "baseboard-product-name", "id/board_name" },
+ { "baseboard-manufacturer", "id/board_vendor" },
+ { "baseboard-version", "id/board_version" },
+ { "baseboard-serial-number", "id/board_serial" },
+ { "baseboard-asset-tag", "id/board_asset_tag" },
+ { "system-product-name", "id/product_name" },
+ { "system-manufacturer", "id/sys_vendor" },
+ { "system-serial-number", "id/product_serial" },
+ { "system-product-family", "id/product_family" },
+ { "system-version", "id/product_version" },
+ { "system-uuid", "product_uuid" },
+ { "system-sku", "id/product_sku" }, /*dmidecode doesn't actually support this one*/
+ { "chassis-type", "id/chassis_type" },
+ { "chassis-serial-number", "id/chassis_serial" },
+ { "chassis-manufacturer", "id/chassis_vendor" },
+ { "chassis-version", "id/chassis_version" },
+ { "chassis-asset-tag", "id/chassis_asset_tag" },
+ { NULL, NULL }
+ };
+ const gchar *dmi_root = dmi_sysfs_root();
+ gchar *ret = NULL;
+ gchar full_path[PATH_MAX];
+ gboolean spawned;
+ gchar *out, *err;
+
+ int i = 0;
+
+ /* try sysfs first */
+ if (dmi_root) {
+ while (tab_dmi_sysfs[i].id != NULL) {
+ if (strcmp(id_str, tab_dmi_sysfs[i].id) == 0) {
+ snprintf(full_path, PATH_MAX, "%s/%s", dmi_root, tab_dmi_sysfs[i].path);
+ if (g_file_get_contents(full_path, &ret, NULL, NULL) )
+ goto dmi_str_done;
+ }
+ i++;
+ }
+ }
+
+ /* try dmidecode, but may require root */
+ snprintf(full_path, PATH_MAX, "dmidecode -s %s", id_str);
+ spawned = hardinfo_spawn_command_line_sync(full_path,
+ &out, &err, &i, NULL);
+ if (spawned) {
+ if (i == 0)
+ ret = out;
+ else
+ g_free(out);
+
+ g_free(err);
+ }
+
+dmi_str_done:
+ if (ret != NULL) {
+ ret = strend(ret, '\n');
+ ret = g_strstrip(ret);
+ }
+ return ret;
+}
+
+char *dmi_chassis_type_str(int chassis_type, gboolean with_val) {
+ static const char *types[] = {
+ N_("Invalid chassis type (0)"),
+ N_("Unknown chassis type"), /* 1 is "Other", but not helpful in HardInfo */
+ N_("Unknown chassis type"),
+ N_("Desktop"),
+ N_("Low-profile Desktop"),
+ N_("Pizza Box"),
+ N_("Mini Tower"),
+ N_("Tower"),
+ N_("Portable"),
+ N_("Laptop"),
+ N_("Notebook"),
+ N_("Handheld"),
+ N_("Docking Station"),
+ N_("All-in-one"),
+ N_("Subnotebook"),
+ N_("Space-saving"),
+ N_("Lunch Box"),
+ N_("Main Server Chassis"),
+ N_("Expansion Chassis"),
+ N_("Sub Chassis"),
+ N_("Bus Expansion Chassis"),
+ N_("Peripheral Chassis"),
+ N_("RAID Chassis"),
+ N_("Rack Mount Chassis"),
+ N_("Sealed-case PC"),
+ N_("Multi-system"),
+ N_("CompactPCI"),
+ N_("AdvancedTCA"),
+ N_("Blade"),
+ N_("Blade Enclosing"),
+ N_("Tablet"),
+ N_("Convertible"),
+ N_("Detachable"),
+ N_("IoT Gateway"),
+ N_("Embedded PC"),
+ N_("Mini PC"),
+ N_("Stick PC"),
+ };
+
+ if (chassis_type <= 0) {
+ gchar *chassis = dmi_get_str("chassis-type");
+ if (chassis) {
+ chassis_type = atoi(chassis);
+ g_free(chassis);
+ } else
+ chassis_type = -1;
+ }
+
+ if (chassis_type >= 0 && chassis_type < (int)G_N_ELEMENTS(types)) {
+ if (with_val)
+ return g_strdup_printf("[%d] %s", chassis_type, _(types[chassis_type]));
+
+ return g_strdup(_(types[chassis_type]));
+ }
+ return NULL;
+}
+
+/* TODO: something better maybe */
+static char *dd_cache[128] = {};
+void dmidecode_cache_free()
+{ int i; for(i = 0; i < 128; i++) g_free(dd_cache[i]); }
+
+char *dmidecode_read(const dmi_type *type) {
+ gchar *ret = NULL;
+ gchar full_path[PATH_MAX];
+ gboolean spawned;
+ gchar *out, *err;
+
+ int i = 0;
+
+ if (type) {
+ if (dd_cache[*type])
+ return g_strdup(dd_cache[*type]);
+ snprintf(full_path, PATH_MAX, "dmidecode -t %"PRId32, *type);
+ } else {
+ if (dd_cache[127])
+ return g_strdup(dd_cache[127]);
+ snprintf(full_path, PATH_MAX, "dmidecode");
+ }
+
+ spawned = hardinfo_spawn_command_line_sync(full_path,
+ &out, &err, &i, NULL);
+ if (spawned) {
+ if (i == 0)
+ ret = out;
+ else
+ g_free(out);
+
+ g_free(err);
+ }
+
+ if (ret) {
+ if (type)
+ dd_cache[*type] = g_strdup(ret);
+ else
+ dd_cache[127] = g_strdup(ret);
+ }
+
+ return ret;
+}
+
+dmi_handle_list *dmi_handle_list_add(dmi_handle_list *hl, dmi_handle_ext new_handle_ext) {
+ if (new_handle_ext.type < G_N_ELEMENTS(dmi_type_strings) )
+ new_handle_ext.type_str = dmi_type_strings[new_handle_ext.type];
+ if (!hl) {
+ hl = malloc(sizeof(dmi_handle_list));
+ hl->count = 1;
+ hl->handles = malloc(sizeof(dmi_handle) * hl->count);
+ hl->handles_ext = malloc(sizeof(dmi_handle_ext) * hl->count);
+ } else {
+ hl->count++;
+ hl->handles = realloc(hl->handles, sizeof(dmi_handle) * hl->count);
+ hl->handles_ext = realloc(hl->handles_ext, sizeof(dmi_handle_ext) * hl->count);
+ }
+ hl->handles_ext[hl->count - 1] = new_handle_ext;
+ hl->handles[hl->count - 1] = new_handle_ext.id;
+
+ return hl;
+}
+
+dmi_handle_list *dmidecode_handles(const dmi_type *type) {
+ gchar *full = NULL, *p = NULL, *next_nl = NULL;
+ dmi_handle_list *hl = NULL;
+
+ // Handle 0x003B, DMI type 9, 17 bytes
+
+ full = dmidecode_read(type);
+ if (full) {
+ p = full;
+ while(next_nl = strchr(p, '\n')) {
+ unsigned int ch = 0, ct = 0, cb = 0;
+ strend(p, '\n');
+ if (sscanf(p, "Handle 0x%X, DMI type %u, %u bytes", &ch, &ct, &cb) > 0) {
+ if (type && !ct) ct = *type;
+ hl = dmi_handle_list_add(hl, (dmi_handle_ext){.id = ch, .type = ct, .size = cb});
+ }
+ p = next_nl + 1;
+ }
+ free(full);
+ }
+ return hl;
+}
+
+void dmi_handle_list_free(dmi_handle_list *hl) {
+ if (hl) {
+ free(hl->handles);
+ free(hl->handles_ext);
+ }
+ free(hl);
+}
+
+char *dmidecode_match(const char *name, const dmi_type *type, const dmi_handle *handle) {
+ gchar *ret = NULL, *full = NULL, *p = NULL, *next_nl = NULL;
+ unsigned int ch = 0;
+ int ln = 0;
+
+ if (!name) return NULL;
+ ln = strlen(name);
+
+ full = dmidecode_read(type);
+ if (full) {
+ p = full;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ if (!(sscanf(p, "Handle 0x%X", &ch) > 0) ) {
+ if (!handle || *handle == ch) {
+ while(*p == '\t') p++;
+ if (strncmp(p, name, ln) == 0) {
+ if (*(p + ln) == ':') {
+ p = p + ln + 1;
+ while(*p == ' ') p++;
+ ret = strdup(p);
+ break;
+ }
+ }
+ }
+ }
+ p = next_nl + 1;
+ }
+ free(full);
+ }
+
+ return ret;
+}
+
+dmi_handle_list *dmidecode_match_value(const char *name, const char *value, const dmi_type *type) {
+ dmi_handle_list *hl = NULL;
+ gchar *full = NULL, *p = NULL, *next_nl = NULL;
+ unsigned int ch = 0, ct = 0, cb = 0;
+ int ln = 0, lnv = 0;
+
+ if (!name) return NULL;
+ ln = strlen(name);
+ lnv = (value) ? strlen(value) : 0;
+
+ full = dmidecode_read(type);
+ if (full) {
+ p = full;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ if (!(sscanf(p, "Handle 0x%X, DMI type %u, %u bytes", &ch, &ct, &cb) > 0)) {
+ while(*p == '\t') p++;
+ if (strncmp(p, name, ln) == 0) {
+ if (*(p + ln) == ':') {
+ p = p + ln + 1;
+ while(*p == ' ') p++;
+ if (!value || strncmp(p, value, lnv) == 0)
+ hl = dmi_handle_list_add(hl, (dmi_handle_ext){.id = ch, .type = ct, .size = cb});
+ }
+ }
+ }
+ p = next_nl + 1;
+ }
+ free(full);
+ }
+
+ return hl;
+}
diff --git a/hardinfo2/dt_util.c b/hardinfo2/dt_util.c
new file mode 100644
index 00000000..08b0e317
--- /dev/null
+++ b/hardinfo2/dt_util.c
@@ -0,0 +1,1187 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+/*
+ * Device Tree support by Burt P. <pburt0@gmail.com>
+ * Sources:
+ * http://elinux.org/Device_Tree_Usage
+ * http://elinux.org/Device_Tree_Mysteries
+ */
+#include <unistd.h>
+#include <sys/types.h>
+#include <inttypes.h> /* for PRIu64 */
+#include <endian.h>
+#include "hardinfo.h"
+#include "dt_util.h"
+#include "appf.h"
+
+static struct {
+ char *name; int type;
+} prop_types[] = {
+ { "name", DTP_STR },
+ { "compatible", DTP_STR },
+ { "model", DTP_STR },
+ { "reg", DTP_REG },
+ { "clocks", DTP_CLOCKS },
+ { "gpios", DTP_GPIOS },
+ { "cs-gpios", DTP_GPIOS },
+ { "phandle", DTP_PH },
+ { "interrupts", DTP_INTRUPT },
+ { "interrupts-extended", DTP_INTRUPT_EX },
+ { "interrupt-parent", DTP_PH_REF },
+ { "interrupt-controller", DTP_EMPTY },
+ { "regulator-min-microvolt", DTP_UINT },
+ { "regulator-max-microvolt", DTP_UINT },
+ { "clock-frequency", DTP_UINT },
+ { "dmas", DTP_DMAS },
+ { "dma-channels", DTP_UINT },
+ { "dma-requests", DTP_UINT },
+
+ /* operating-points-v2: */
+ /* https://www.kernel.org/doc/Documentation/devicetree/bindings/opp/opp.txt */
+ { "operating-points-v2", DTP_PH_REF_OPP2 },
+ { "opp-hz", DTP_UINT64 },
+ { "opp-microvolt", DTP_UINT },
+ { "opp-microvolt-L0", DTP_UINT }, /* opp-microvolt-<named>, but this kind of */
+ { "opp-microvolt-L1", DTP_UINT }, /* wildcard matching isn't supported yet */
+ { "opp-microamp", DTP_UINT },
+ { "clock-latency-ns", DTP_UINT },
+
+ { NULL, 0 },
+};
+
+static struct {
+ char *name; uint32_t v;
+} default_values[] = {
+ { "#address-cells", 2 },
+ { "#size-cells", 1 },
+ { "#dma-cells", 1 },
+ { NULL, 0 },
+};
+
+struct _dtr_map {
+ uint32_t v; /* phandle */
+ char *label; /* alias */
+ char *path;
+ struct _dtr_map *next;
+};
+typedef struct _dtr_map dtr_map;
+
+struct _dtr {
+ dtr_map *aliases;
+ dtr_map *symbols;
+ dtr_map *phandles;
+ char *base_path;
+ char *log;
+};
+
+struct _dtr_obj {
+ char *path;
+ union {
+ void *data;
+ char *data_str;
+ dt_uint *data_int;
+ dt_uint64 *data_int64;
+ };
+ char *name;
+ uint32_t length;
+ int type;
+ char *prefix; /* if the name has a manufacturer's prefix or null */
+ char *np_name; /* the name without any prefix. points into .prefix or .name, do not free */
+ const char *alias; /* null until first dtr_obj_alias(). do not free */
+ const char *symbol; /* null until first dtr_obj_symbol(). do not free */
+ dtr *dt;
+};
+
+dtr_map *dtr_map_add(dtr_map *map, uint32_t v, const char *label, const char *path) {
+ dtr_map *it;
+ dtr_map *nmap = malloc(sizeof(dtr_map));
+ memset(nmap, 0, sizeof(dtr_map));
+ nmap->v = v;
+
+ if (label != NULL) nmap->label = strdup(label);
+ if (path != NULL) nmap->path = strdup(path);
+ if (map == NULL)
+ return nmap;
+
+ it = map;
+ while(it->next != NULL)
+ it = it->next;
+ it->next = nmap;
+
+ return nmap;
+}
+
+void dtr_map_free(dtr_map *map) {
+ dtr_map *it;
+ while(map != NULL) {
+ it = map->next;
+ free(map->label);
+ free(map->path);
+ free(map);
+ map = it;
+ }
+}
+
+/* simple sort for maps
+ * sv: 1 = sort by v, 0 = sort by label */
+void dtr_map_sort(dtr_map *map, int sv)
+{
+ int done = 0, cmp;
+ dtr_map *this, *next, *top = NULL, *next_top;
+ uint32_t tmp_v;
+ char *tmp_l, *tmp_p;
+ if (map == NULL) return;
+ do {
+ this = map;
+ next_top = NULL;
+ while(this != NULL) {
+ next = this->next;
+ if (this == top)
+ break;
+ if (next == NULL)
+ break;
+ if (sv)
+ cmp = (this->v > next->v) ? 1 : 0;
+ else
+ cmp = strcmp(this->label, next->label);
+ if (cmp > 0) {
+ tmp_v = this->v; this->v = next->v; next->v = tmp_v;
+ tmp_l = this->label; this->label = next->label; next->label = tmp_l;
+ tmp_p = this->path; this->path = next->path; next->path = tmp_p;
+ next_top = this;
+ }
+ this = next;
+ }
+ if (next_top == NULL)
+ done = 1;
+ else
+ top = next_top;
+ } while (!done);
+}
+
+const char *dtr_phandle_lookup(dtr *s, uint32_t v) {
+ dtr_map *phi = s->phandles;
+ /* 0 and 0xffffffff are invalid phandle values */
+ /* TODO: perhaps "INVALID" or something */
+ if (v == 0 || v == 0xffffffff)
+ return NULL;
+ while(phi != NULL) {
+ if (phi->v == v)
+ return phi->path;
+ phi = phi->next;
+ }
+ return NULL;
+}
+
+const char *dtr_alias_lookup(dtr *s, const char* label) {
+ dtr_map *ali = s->aliases;
+ while(ali != NULL) {
+ if (strcmp(ali->label, label) == 0)
+ return ali->path;
+ ali = ali->next;
+ }
+ return NULL;
+}
+
+const char *dtr_alias_lookup_by_path(dtr *s, const char* path) {
+ dtr_map *ali = s->aliases;
+ while(ali != NULL) {
+ if (strcmp(ali->path, path) == 0)
+ return ali->label;
+ ali = ali->next;
+ }
+ return NULL;
+}
+
+const char *dtr_symbol_lookup_by_path(dtr *s, const char* path) {
+ dtr_map *ali = s->symbols;
+ while(ali != NULL) {
+ if (strcmp(ali->path, path) == 0)
+ return ali->label;
+ ali = ali->next;
+ }
+ return NULL;
+}
+
+void _dtr_read_aliases(dtr *);
+void _dtr_read_symbols(dtr *);
+void _dtr_map_phandles(dtr *, char *np);
+int dtr_inh_find(dtr_obj *obj, char *qprop, int limit);
+#define UMIN(a,b) MIN(((uint32_t)(a)), ((uint32_t)(b)))
+
+const char *dtr_find_device_tree_root() {
+ char *candidates[] = {
+ "/proc/device-tree",
+ "/sys/firmware/devicetree/base",
+ /* others? */
+ NULL
+ };
+ int i = 0;
+ while (candidates[i] != NULL) {
+ if(access(candidates[i], F_OK) != -1)
+ return candidates[i];
+ i++;
+ }
+ return NULL;
+}
+
+dtr *dtr_new_x(const char *base_path, int fast) {
+ dtr *dt = malloc(sizeof(dtr));
+ if (dt != NULL) {
+ memset(dt, 0, sizeof(dtr));
+ dt->log = strdup("");
+
+ if (base_path == NULL)
+ base_path = DTR_ROOT;
+
+ if (base_path != NULL)
+ dt->base_path = strdup(base_path);
+ else {
+ dtr_msg(dt, "%s", "Device Tree not found.");
+ return dt;
+ }
+
+ /* build alias and phandle lists */
+ dt->aliases = NULL;
+ dt->symbols = NULL;
+ dt->phandles = NULL;
+ if (!fast) {
+ _dtr_read_aliases(dt);
+ _dtr_read_symbols(dt);
+ _dtr_map_phandles(dt, "");
+ }
+ }
+ return dt;
+}
+
+dtr *dtr_new(const char *base_path) {
+ return dtr_new_x(base_path, 0);
+}
+
+void dtr_free(dtr *s) {
+ if (s != NULL) {
+ dtr_map_free(s->aliases);
+ dtr_map_free(s->symbols);
+ dtr_map_free(s->phandles);
+ free(s->base_path);
+ free(s->log);
+ free(s);
+ }
+}
+
+int dtr_was_found(dtr *s) {
+ if (s != NULL) {
+ if (s->base_path != NULL)
+ return 1;
+ }
+ return 0;
+}
+
+void dtr_msg(dtr *s, char *fmt, ...) {
+ gchar *buf, *tmp;
+ va_list args;
+
+ va_start(args, fmt);
+ buf = g_strdup_vprintf(fmt, args);
+ va_end(args);
+
+ tmp = g_strdup_printf("%s%s", s->log, buf);
+ g_free(s->log);
+ s->log = tmp;
+}
+
+char *dtr_messages(dtr *s) {
+ if (s != NULL)
+ return strdup(s->log);
+ else
+ return NULL;
+}
+
+const char *dtr_base_path(dtr *s) {
+ if (s)
+ return s->base_path;
+ return NULL;
+}
+
+/*glib, but _dt_obj *prop uses malloc() and std types */
+dtr_obj *dtr_obj_read(dtr *s, const char *dtp) {
+ char *full_path;
+ char *slash, *coma;
+ dtr_obj *obj;
+
+ if (dtp == NULL)
+ return NULL;
+
+ obj = malloc(sizeof(dtr_obj));
+ if (obj != NULL) {
+ memset(obj, 0, sizeof(dtr_obj));
+
+ obj->dt = s;
+ if (*dtp != '/') {
+ /* doesn't start with slash, use alias */
+ obj->path = (char*)dtr_alias_lookup(s, dtp);
+ if (obj->path != NULL)
+ obj->path = strdup(obj->path);
+ else {
+ dtr_obj_free(obj);
+ return NULL;
+ }
+ } else
+ obj->path = strdup(dtp);
+
+ /* find name after last slash, or start */
+ slash = strrchr(obj->path, '/');
+ if (slash != NULL)
+ obj->name = strdup(slash + 1);
+ else
+ obj->name = strdup(obj->path);
+
+ /* find manufacturer prefix */
+ obj->prefix = strdup(obj->name);
+ coma = strchr(obj->prefix, ',');
+ if (coma != NULL) {
+ /* coma -> null; .np_name starts after */
+ *coma = 0;
+ obj->np_name = coma + 1;
+ } else {
+ obj->np_name = obj->name;
+ free(obj->prefix);
+ obj->prefix = NULL;
+ }
+
+ /* read data */
+ full_path = g_strdup_printf("%s%s", s->base_path, obj->path);
+ if ( g_file_test(full_path, G_FILE_TEST_IS_DIR) ) {
+ obj->type = DT_NODE;
+ } else {
+ if (!g_file_get_contents(full_path, (gchar**)&obj->data, (gsize*)&obj->length, NULL)) {
+ dtr_obj_free(obj);
+ g_free(full_path);
+ return NULL;
+ }
+ obj->type = dtr_guess_type(obj);
+ }
+ g_free(full_path);
+
+ return obj;
+ }
+ return NULL;
+}
+
+void dtr_obj_free(dtr_obj *s) {
+ if (s != NULL) {
+ free(s->path);
+ free(s->name);
+ free(s->prefix);
+ free(s->data);
+ free(s);
+ }
+}
+
+int dtr_obj_type(dtr_obj *s) {
+ if (s)
+ return s->type;
+ return DT_TYPE_ERR;
+}
+
+char *dtr_obj_alias(dtr_obj *s) {
+ if (s) {
+ if (s->alias == NULL)
+ s->alias = dtr_alias_lookup_by_path(s->dt, s->path);
+ return (char*)s->alias;
+ }
+ return NULL;
+}
+
+char *dtr_obj_symbol(dtr_obj *s) {
+ if (s) {
+ if (s->symbol == NULL)
+ s->symbol = dtr_symbol_lookup_by_path(s->dt, s->path);
+ return (char*)s->symbol;
+ }
+ return NULL;
+}
+
+char *dtr_obj_path(dtr_obj *s) {
+ if (s)
+ return s->path;
+ return NULL;
+}
+
+char *dtr_obj_full_path(dtr_obj *s) {
+ if (s) {
+ if (strcmp(s->path, "/") == 0)
+ return g_strdup_printf("%s", s->dt->base_path);
+ else
+ return g_strdup_printf("%s%s", s->dt->base_path, s->path);
+ }
+ return NULL;
+}
+
+dtr_obj *dtr_get_prop_obj(dtr *s, dtr_obj *node, const char *name) {
+ dtr_obj *prop;
+ char *ptmp;
+ ptmp = g_strdup_printf("%s/%s", (node == NULL) ? "" : node->path, name);
+ prop = dtr_obj_read(s, ptmp);
+ g_free(ptmp);
+ return prop;
+}
+
+char *dtr_get_prop_str(dtr *s, dtr_obj *node, const char *name) {
+ dtr_obj *prop;
+ char *ptmp;
+ char *ret = NULL;
+
+ ptmp = g_strdup_printf("%s/%s", (node == NULL) ? "" : node->path, name);
+ prop = dtr_obj_read(s, ptmp);
+ if (prop != NULL && prop->data != NULL) {
+ ret = strdup(prop->data_str);
+ dtr_obj_free(prop);
+ }
+ g_free(ptmp);
+ return ret;
+}
+
+char *dtr_get_string(const char *p, int decode) {
+ dtr *dt = dtr_new_x(NULL, 1);
+ dtr_obj *obj;
+ char *ret = NULL;
+ if (decode) {
+ obj = dtr_get_prop_obj(dt, NULL, p);
+ ret = dtr_str(obj);
+ dtr_obj_free(obj);
+ } else
+ ret = dtr_get_prop_str(dt, NULL, p);
+ dtr_free(dt);
+ return ret;
+}
+
+uint32_t dtr_get_prop_u32(dtr *s, dtr_obj *node, const char *name) {
+ dtr_obj *prop;
+ char *ptmp;
+ uint32_t ret = 0;
+
+ ptmp = g_strdup_printf("%s/%s", (node == NULL) ? "" : node->path, name);
+ prop = dtr_obj_read(s, ptmp);
+ if (prop != NULL) {
+ if (prop->data != NULL)
+ ret = be32toh(*prop->data_int);
+ dtr_obj_free(prop);
+ }
+ g_free(ptmp);
+ return ret;
+}
+
+uint64_t dtr_get_prop_u64(dtr *s, dtr_obj *node, const char *name) {
+ dtr_obj *prop;
+ char *ptmp;
+ uint64_t ret = 0;
+
+ ptmp = g_strdup_printf("%s/%s", (node == NULL) ? "" : node->path, name);
+ prop = dtr_obj_read(s, ptmp);
+ if (prop != NULL) {
+ if (prop->data != NULL)
+ ret = be64toh(*prop->data_int64);
+ dtr_obj_free(prop);
+ }
+ g_free(ptmp);
+ return ret;
+}
+
+int dtr_guess_type(dtr_obj *obj) {
+ char *tmp, *dash;
+ uint32_t i = 0, anc = 0, might_be_str = 1;
+
+ if (obj->length == 0)
+ return DTP_EMPTY;
+
+ /* special #(.*)-cells names are UINT */
+ if (*obj->name == '#') {
+ dash = strrchr(obj->name, '-');
+ if (dash != NULL) {
+ if (strcmp(dash, "-cells") == 0)
+ return DTP_UINT;
+ }
+ }
+
+ /* /aliases/ * and /__symbols__/ * are always strings */
+ if (strncmp(obj->path, "/aliases/", strlen("/aliases/") ) == 0)
+ return DTP_STR;
+ if (strncmp(obj->path, "/__symbols__/", strlen("/__symbols__/") ) == 0)
+ return DTP_STR;
+
+ /* /__overrides__/ * */
+ if (strncmp(obj->path, "/__overrides__/", strlen("/__overrides__/") ) == 0)
+ if (strcmp(obj->name, "name") != 0)
+ return DTP_OVR;
+
+ /* lookup known type */
+ while (prop_types[i].name != NULL) {
+ if (strcmp(obj->name, prop_types[i].name) == 0)
+ return prop_types[i].type;
+ i++;
+ }
+
+ /* maybe a string? */
+ for (i = 0; i < obj->length; i++) {
+ tmp = obj->data_str + i;
+ if ( isalnum(*tmp) ) anc++; /* count the alpha-nums */
+ if ( isprint(*tmp) || *tmp == 0 )
+ continue;
+ might_be_str = 0;
+ break;
+ }
+ if (might_be_str &&
+ ( anc >= obj->length - 2 /* all alpha-nums but ^/ and \0$ */
+ || anc >= 5 ) /*arbitrary*/)
+ return DTP_STR;
+
+ /* multiple of 4 bytes, try list of hex values */
+ if (!(obj->length % 4))
+ return DTP_HEX;
+
+ return DTP_UNK;
+}
+
+char *dtr_elem_phref(dtr *s, dt_uint e, int show_path, const char *extra) {
+ const char *ph_path, *al_label;
+ char *ret = NULL;
+ ph_path = dtr_phandle_lookup(s, be32toh(e));
+ if (ph_path != NULL) {
+ /* TODO: alias or symbol? */
+ al_label = dtr_symbol_lookup_by_path(s, ph_path);
+ if (al_label != NULL) {
+ if (show_path)
+ ret = g_strdup_printf("&%s (%s) %s", al_label, ph_path, extra ? extra : "");
+ else
+ ret = g_strdup_printf("&%s %s", al_label, extra ? extra : "");
+ } else {
+ if (show_path)
+ ret = g_strdup_printf("0x%x (%s) %s", be32toh(e), ph_path, extra ? extra : "");
+ }
+ }
+ if (ret == NULL)
+ ret = dtr_elem_hex(e);
+ return ret;
+}
+
+char *dtr_elem_oppv2(dtr_obj* obj) {
+ char opp_str[512] = "";
+ dtr_obj *parent = dtr_get_parent_obj(obj);
+ if (parent) {
+ dt_opp_range *opp = dtr_get_opp_range(obj->dt, parent->path);
+ if (opp) {
+ snprintf(opp_str, 511, "[%d - %d %s]", opp->khz_min, opp->khz_max, _("kHz"));
+ g_free(opp);
+ }
+ dtr_obj_free(parent);
+ }
+ return dtr_elem_phref(obj->dt, *obj->data_int, 1, opp_str);
+}
+
+char *dtr_elem_hex(dt_uint e) {
+ return g_strdup_printf("0x%x", be32toh(e) );
+}
+
+char *dtr_elem_byte(uint8_t e) {
+ return g_strdup_printf("%x", e);
+}
+
+char *dtr_elem_uint(dt_uint e) {
+ return g_strdup_printf("%u", be32toh(e) );
+}
+
+char *dtr_elem_uint64(dt_uint64 e) {
+ return g_strdup_printf("%" PRIu64, be64toh(e) );
+}
+
+char *dtr_list_byte(uint8_t *bytes, unsigned long count) {
+ char *ret, *dest;
+ char buff[4] = ""; /* max element: " 00\0" */
+ uint32_t v;
+ unsigned long i, l;
+ l = count * 4 + 1;
+ ret = malloc(l);
+ memset(ret, 0, l);
+ strcpy(ret, "[");
+ dest = ret + 1;
+ for (i = 0; i < count; i++) {
+ v = bytes[i];
+ l = sprintf(buff, "%s%02x", (i) ? " " : "", v);
+ strncpy(dest, buff, l);
+ dest += l;
+ }
+ strcpy(dest, "]");
+ return ret;
+}
+
+char *dtr_list_hex(dt_uint *list, unsigned long count) {
+ char *ret, *dest;
+ char buff[12] = ""; /* max element: " 0x00000000\0" */
+ unsigned long i, l;
+ l = count * 12 + 1;
+ dest = ret = malloc(l);
+ memset(ret, 0, l);
+ for (i = 0; i < count; i++) {
+ l = sprintf(buff, "%s0x%x", (i) ? " " : "", be32toh(list[i]));
+ strncpy(dest, buff, l);
+ dest += l;
+ }
+ return ret;
+}
+
+/*cstd, except for g_strescape()*/
+char *dtr_list_str0(const char *data, uint32_t length) {
+ char *tmp, *esc, *next_str;
+ char ret[1024] = "";
+ uint32_t l, tl;
+
+ /* treat as null-separated string list */
+ tl = 0;
+ strcpy(ret, "");
+ tmp = ret;
+ next_str = (char*)data;
+ while(next_str != NULL) {
+ l = strlen(next_str);
+ esc = g_strescape(next_str, NULL);
+ sprintf(tmp, "%s\"%s\"",
+ strlen(ret) ? ", " : "", esc);
+ free(esc);
+ tmp += strlen(tmp);
+ tl += l + 1; next_str += l + 1;
+ if (tl >= length) break;
+ }
+
+ return strdup(ret);
+}
+
+char *dtr_list_override(dtr_obj *obj) {
+ /* <phref, string "property:value"> ... */
+ char *ret = NULL;
+ char *ph, *str;
+ char *src;
+ uint32_t consumed = 0;
+ int l;
+ src = obj->data_str;
+ while (consumed + 5 <= obj->length) {
+ ph = dtr_elem_phref(obj->dt, *(dt_uint*)src, 1, NULL);
+ src += 4; consumed += 4;
+ l = strlen(src) + 1; /* consume the null */
+ str = dtr_list_str0(src, l);
+ ret = appfsp(ret, "<%s -> %s>", ph, str);
+ src += l; consumed += l;
+ free(ph);
+ free(str);
+ }
+ if (consumed < obj->length) {
+ str = dtr_list_byte((uint8_t*)src, obj->length - consumed);
+ ret = appfsp(ret, "%s", str);
+ free(str);
+ }
+ return ret;
+}
+
+uint32_t dtr_get_phref_prop(dtr *s, uint32_t phandle, char *prop) {
+ const char *ph_path;
+ char *tmp;
+ uint32_t ret;
+ ph_path = dtr_phandle_lookup(s, phandle);
+ tmp = g_strdup_printf("%s/%s", ph_path, prop);
+ ret = dtr_get_prop_u32(s, NULL, tmp);
+ free(tmp);
+ return ret;
+}
+
+char *dtr_list_phref(dtr_obj *obj, char *ext_cell_prop) {
+ /* <phref, #XXX-cells> */
+ int count = obj->length / 4;
+ int i = 0, ext_cells = 0;
+ char *ph, *ext, *ret = NULL;
+
+ while (i < count) {
+ if (ext_cell_prop == NULL)
+ ext_cells = 0;
+ else
+ ext_cells = dtr_get_phref_prop(obj->dt, be32toh(obj->data_int[i]), ext_cell_prop);
+ ph = dtr_elem_phref(obj->dt, obj->data_int[i], 0, NULL); i++;
+ if (ext_cells > count - i) ext_cells = count - i;
+ ext = dtr_list_hex((obj->data_int + i), ext_cells); i+=ext_cells;
+ ret = appfsp(ret, "<%s%s%s>",
+ ph, (ext_cells) ? " " : "", ext);
+ g_free(ph);
+ g_free(ext);
+ }
+ return ret;
+}
+
+char *dtr_list_interrupts(dtr_obj *obj) {
+ char *ext, *ret = NULL;
+ uint32_t iparent, icells;
+ int count, i = 0;
+
+ iparent = dtr_inh_find(obj, "interrupt-parent", 0);
+ if (!iparent) {
+ dtr_msg(obj->dt, "Did not find an interrupt-parent for %s", obj->path);
+ goto intr_err;
+ }
+ icells = dtr_get_phref_prop(obj->dt, iparent, "#interrupt-cells");
+ if (!icells) {
+ dtr_msg(obj->dt, "Invalid #interrupt-cells value %d for %s", icells, obj->path);
+ goto intr_err;
+ }
+
+ count = obj->length / 4;
+ while (i < count) {
+ icells = UMIN(icells, count - i);
+ ext = dtr_list_hex((obj->data_int + i), icells); i+=icells;
+ ret = appfsp(ret, "<%s>", ext);
+ }
+ return ret;
+
+intr_err:
+ return dtr_list_hex(obj->data_int, obj->length);
+
+}
+
+char *dtr_list_reg(dtr_obj *obj) {
+ char *tup_str, *ret = NULL;
+ uint32_t acells, scells, tup_len;
+ uint32_t extra, consumed; /* bytes */
+ uint32_t *next;
+
+ acells = dtr_inh_find(obj, "#address-cells", 2);
+ scells = dtr_inh_find(obj, "#size-cells", 2);
+ tup_len = acells + scells;
+ extra = obj->length % (tup_len * 4);
+ consumed = 0; /* bytes */
+
+ //printf("list reg: %s\n ... acells: %u, scells: %u, extra: %u\n", obj->path, acells, scells, extra);
+
+ if (extra) {
+ /* error: length is not a multiple of tuples */
+ dtr_msg(obj->dt, "Data length (%u) is not a multiple of (#address-cells:%u + #size-cells:%u) for %s\n",
+ obj->length, acells, scells, obj->path);
+ return dtr_list_hex(obj->data, obj->length / 4);
+ }
+
+ next = obj->data_int;
+ consumed = 0;
+ while (consumed + (tup_len * 4) <= obj->length) {
+ tup_str = dtr_list_hex(next, tup_len);
+ ret = appfsp(ret, "<%s>", tup_str);
+ free(tup_str);
+ consumed += (tup_len * 4);
+ next += tup_len;
+ }
+
+ //printf(" ... %s\n", ret);
+ return ret;
+}
+
+char* dtr_str(dtr_obj *obj) {
+ char *ret;
+ int type;
+
+ if (obj == NULL) return NULL;
+ type = obj->type;
+
+ if (type == DTP_PH_REF) {
+ if (!DTEX_PHREFS || obj->length != 4)
+ type = DTP_HEX;
+ }
+
+ switch(type) {
+ case DT_NODE:
+ ret = strdup("{node}");
+ break;
+ case DTP_EMPTY:
+ ret = strdup("{empty}");
+ break;
+ case DTP_STR:
+ ret = dtr_list_str0(obj->data_str, obj->length);
+ break;
+ case DTP_OVR:
+ ret = dtr_list_override(obj);
+ break;
+ case DTP_REG:
+ /* <#address-cells #size-cells> */
+ ret = dtr_list_reg(obj);
+ break;
+ case DTP_INTRUPT:
+ ret = dtr_list_interrupts(obj);
+ break;
+ case DTP_INTRUPT_EX:
+ /* <phref, #interrupt-cells"> */
+ ret = dtr_list_phref(obj, "#interrupt-cells");
+ break;
+ case DTP_CLOCKS:
+ /* <phref, #clock-cells"> */
+ ret = dtr_list_phref(obj, "#clock-cells");
+ break;
+ case DTP_GPIOS:
+ /* <phref, #gpio-cells"> */
+ ret = dtr_list_phref(obj, "#gpio-cells");
+ break;
+ case DTP_DMAS:
+ /* <phref, #dma-cells"> */
+ ret = dtr_list_phref(obj, "#dma-cells");
+ break;
+ case DTP_PH:
+ case DTP_HEX:
+ if (obj->length % 4)
+ ret = dtr_list_byte((uint8_t*)obj->data, obj->length);
+ else
+ ret = dtr_list_hex(obj->data, obj->length / 4);
+ break;
+ case DTP_PH_REF:
+ ret = dtr_elem_phref(obj->dt, *obj->data_int, 1, NULL);
+ break;
+ case DTP_PH_REF_OPP2:
+ ret = dtr_elem_oppv2(obj);
+ break;
+ case DTP_UINT:
+ ret = dtr_elem_uint(*obj->data_int);
+ break;
+ case DTP_UINT64:
+ ret = dtr_elem_uint64(*obj->data_int64);
+ break;
+ case DTP_UNK:
+ default:
+ if (obj->length > 64) /* maybe should use #define at the top */
+ ret = g_strdup_printf("{data} (%lu bytes)", (long unsigned int)obj->length);
+ else
+ ret = dtr_list_byte((uint8_t*)obj->data, obj->length);
+ break;
+ }
+
+ return ret;
+}
+
+dtr_obj *dtr_get_parent_obj(dtr_obj *obj) {
+ char *slash, *parent;
+ dtr_obj *ret = NULL;
+
+ if (obj == NULL)
+ return NULL;
+
+ parent = strdup(obj->path);
+ slash = strrchr(parent, '/');
+ if (slash != NULL) {
+ *slash = 0;
+ if (strlen(parent) > 0)
+ ret = dtr_obj_read(obj->dt, parent);
+ else
+ ret = dtr_obj_read(obj->dt, "/");
+ }
+ free(parent);
+ return ret;
+}
+
+/* find the value of a path-inherited property by climbing the path */
+int dtr_inh_find(dtr_obj *obj, char *qprop, int limit) {
+ dtr_obj *tobj, *pobj = NULL, *qobj;
+ uint32_t ret = 0;
+ int i, found = 0;
+
+ if (!limit) limit = 1000;
+
+ tobj = obj;
+ while (tobj != NULL) {
+ pobj = dtr_get_parent_obj(tobj);
+ if (tobj != obj)
+ dtr_obj_free(tobj);
+ if (!limit || pobj == NULL) break;
+ qobj = dtr_get_prop_obj(obj->dt, pobj, qprop);
+ if (qobj != NULL) {
+ ret = be32toh(*qobj->data_int);
+ found = 1;
+ dtr_obj_free(qobj);
+ break;
+ }
+ tobj = pobj;
+ limit--;
+ }
+ dtr_obj_free(pobj);
+
+ if (!found) {
+ i = 0;
+ while(default_values[i].name != NULL) {
+ if (strcmp(default_values[i].name, qprop) == 0) {
+ ret = default_values[i].v;
+ dtr_msg(obj->dt, "Using default value %d for %s in %s\n", ret, qprop, obj->path);
+ break;
+ }
+ i++;
+ }
+ }
+
+ return ret;
+}
+
+dt_opp_range *dtr_get_opp_range(dtr *s, const char *name) {
+ dt_opp_range *ret = NULL;
+ dtr_obj *obj = NULL, *table_obj = NULL, *row_obj = NULL;
+ uint32_t opp_ph = 0;
+ const char *opp_table_path = NULL;
+ char *tab_compat = NULL, *tab_status = NULL;
+ const gchar *fn;
+ gchar *full_path;
+ GDir *dir;
+ uint64_t khz = 0;
+ uint32_t lns = 0;
+ char *row_status = NULL;
+ uint32_t i = 0;
+
+ if (!s)
+ return NULL;
+
+ obj = dtr_obj_read(s, name);
+ if (!obj)
+ goto get_opp_finish;
+
+ opp_ph = dtr_get_prop_u32(s, obj, "operating-points-v2"); /* OPPv2 */
+ table_obj = dtr_get_prop_obj(s, obj, "operating-points"); /* OPPv1 */
+ if (!opp_ph) {
+ if (table_obj) {
+ /* only v1 */
+ ret = g_new0(dt_opp_range, 1);
+ ret->version = 1;
+ ret->clock_latency_ns = dtr_get_prop_u32(s, obj, "clock-latency");
+
+ /* pairs of (kHz,uV) */
+ for (i = 0; i < table_obj->length; i += 2) {
+ khz = table_obj->data_int[i];
+ if (khz > ret->khz_max)
+ ret->khz_max = khz;
+ if (khz < ret->khz_min || ret->khz_min == 0)
+ ret->khz_min = khz;
+ }
+
+ } else {
+ /* is clock-frequency available? */
+ khz = dtr_get_prop_u32(s, obj, "clock-frequency");
+ if (khz) {
+ ret = g_new0(dt_opp_range, 1);
+ ret->version = 0;
+ ret->khz_max = khz;
+ ret->clock_latency_ns = dtr_get_prop_u32(s, obj, "clock-latency");
+ }
+ }
+ goto get_opp_finish;
+ } else {
+ /* use v2 if both available */
+ dtr_obj_free(table_obj);
+ table_obj = NULL;
+ }
+
+ opp_table_path = dtr_phandle_lookup(s, opp_ph);
+ if (!opp_table_path)
+ goto get_opp_finish;
+
+ table_obj = dtr_obj_read(s, opp_table_path);
+ if (!table_obj)
+ goto get_opp_finish;
+
+ tab_compat = dtr_get_prop_str(s, table_obj, "compatible");
+ tab_status = dtr_get_prop_str(s, table_obj, "status");
+
+ if (!tab_compat || strcmp(tab_compat, "operating-points-v2") != 0)
+ goto get_opp_finish;
+ if (tab_status && strcmp(tab_status, "disabled") == 0)
+ goto get_opp_finish;
+
+ ret = g_new0(dt_opp_range, 1);
+ ret->version = 2;
+ ret->phandle = opp_ph;
+
+ full_path = dtr_obj_full_path(table_obj);
+ dir = g_dir_open(full_path, 0 , NULL);
+ if (dir) {
+ while((fn = g_dir_read_name(dir)) != NULL) {
+ row_obj = dtr_get_prop_obj(s, table_obj, fn);
+ if (row_obj->type == DT_NODE) {
+ row_status = dtr_get_prop_str(s, row_obj, "status");
+ if (!row_status || strcmp(row_status, "disabled") != 0) {
+ khz = dtr_get_prop_u64(s, row_obj, "opp-hz");
+ khz /= 1000; /* 64b hz -> 32b khz */
+ lns = dtr_get_prop_u32(s, row_obj, "clock-latency-ns");
+ if (khz > ret->khz_max)
+ ret->khz_max = khz;
+ if (khz < ret->khz_min || ret->khz_min == 0)
+ ret->khz_min = khz;
+ ret->clock_latency_ns = lns;
+ }
+ }
+ free(row_status); row_status = NULL;
+ dtr_obj_free(row_obj);
+ row_obj = NULL;
+ }
+ g_dir_close(dir);
+ }
+ g_free(full_path);
+
+get_opp_finish:
+ dtr_obj_free(obj);
+ dtr_obj_free(table_obj);
+ free(tab_status);
+ free(tab_compat);
+ free(row_status);
+ return ret;
+}
+
+
+void _dtr_read_aliases(dtr *s) {
+ gchar *dir_path;
+ GDir *dir;
+ const gchar *fn;
+ dtr_obj *anode, *prop;
+ dtr_map *al;
+ anode = dtr_obj_read(s, "/aliases");
+
+ dir_path = g_strdup_printf("%s/aliases", s->base_path);
+ dir = g_dir_open(dir_path, 0 , NULL);
+ if (dir) {
+ while((fn = g_dir_read_name(dir)) != NULL) {
+ prop = dtr_get_prop_obj(s, anode, fn);
+ if (prop->type == DTP_STR) {
+ if (*prop->data_str == '/') {
+ al = dtr_map_add(s->aliases, 0, prop->name, prop->data_str);
+ if (s->aliases == NULL)
+ s->aliases = al;
+ }
+ }
+ dtr_obj_free(prop);
+ }
+ g_dir_close(dir);
+ }
+ g_free(dir_path);
+ dtr_obj_free(anode);
+ dtr_map_sort(s->aliases, 0);
+}
+
+void _dtr_read_symbols(dtr *s) {
+ gchar *dir_path;
+ GDir *dir;
+ const gchar *fn;
+ dtr_obj *anode, *prop;
+ dtr_map *al;
+ anode = dtr_obj_read(s, "/__symbols__");
+
+ dir_path = g_strdup_printf("%s/__symbols__", s->base_path);
+ dir = g_dir_open(dir_path, 0 , NULL);
+ if (dir) {
+ while((fn = g_dir_read_name(dir)) != NULL) {
+ prop = dtr_get_prop_obj(s, anode, fn);
+ if (prop->type == DTP_STR) {
+ if (*prop->data_str == '/') {
+ al = dtr_map_add(s->symbols, 0, prop->name, prop->data_str);
+ if (s->symbols == NULL)
+ s->symbols = al;
+ }
+ }
+ dtr_obj_free(prop);
+ }
+ g_dir_close(dir);
+ }
+ g_free(dir_path);
+ dtr_obj_free(anode);
+ dtr_map_sort(s->symbols, 0);
+}
+
+/* TODO: rewrite */
+void _dtr_map_phandles(dtr *s, char *np) {
+ gchar *dir_path;
+ gchar *ftmp, *ntmp, *ptmp;
+ const gchar *fn;
+ GDir *dir;
+ dtr_obj *prop, *ph_prop;
+ dtr_map *ph;
+
+ if (np == NULL) np = "";
+ dir_path = g_strdup_printf("%s/%s", s->base_path, np);
+
+ prop = dtr_obj_read(s, np);
+ dir = g_dir_open(dir_path, 0 , NULL);
+ if (dir) {
+ while((fn = g_dir_read_name(dir)) != NULL) {
+ ftmp = g_strdup_printf("%s/%s", dir_path, fn);
+ if ( g_file_test(ftmp, G_FILE_TEST_IS_DIR) ) {
+ ntmp = g_strdup_printf("%s/%s", np, fn);
+ ptmp = g_strdup_printf("%s/phandle", ntmp);
+ ph_prop = dtr_obj_read(s, ptmp);
+ if (ph_prop != NULL) {
+ ph = dtr_map_add(s->phandles, be32toh(*ph_prop->data_int), NULL, ntmp);
+ if (s->phandles == NULL)
+ s->phandles = ph;
+ }
+ _dtr_map_phandles(s, ntmp);
+ g_free(ptmp);
+ g_free(ntmp);
+ dtr_obj_free(ph_prop);
+ }
+ g_free(ftmp);
+ }
+ g_dir_close(dir);
+ }
+ dtr_obj_free(prop);
+ dtr_map_sort(s->phandles, 1);
+}
+
+/*
+ * Maybe these should move to devicetree.c, but would have to expose
+ * struct internals.
+ */
+
+/* kvl: 0 = key is label, 1 = key is v */
+char *dtr_map_info_section(dtr *s, dtr_map *map, char *title, int kvl) {
+ gchar *tmp, *ret;
+ const gchar *sym;
+ ret = g_strdup_printf("[%s]\n", _(title));
+ dtr_map *it = map;
+ while(it != NULL) {
+ if (kvl) {
+ sym = dtr_symbol_lookup_by_path(s, it->path);
+ if (sym != NULL)
+ tmp = g_strdup_printf("%s0x%x (%s)=%s\n", ret,
+ it->v, sym, it->path);
+ else
+ tmp = g_strdup_printf("%s0x%x=%s\n", ret,
+ it->v, it->path);
+ } else
+ tmp = g_strdup_printf("%s%s=%s\n", ret,
+ it->label, it->path);
+ g_free(ret);
+ ret = tmp;
+ it = it->next;
+ }
+
+ return ret;
+}
+
+char *dtr_maps_info(dtr *s) {
+ gchar *ph_map, *al_map, *sy_map, *ret;
+
+ ph_map = dtr_map_info_section(s, s->phandles, _("phandle Map"), 1);
+ al_map = dtr_map_info_section(s, s->aliases, _("Alias Map"), 0);
+ sy_map = dtr_map_info_section(s, s->symbols, _("Symbol Map"), 0);
+ ret = g_strdup_printf("%s%s%s", ph_map, sy_map, al_map);
+ g_free(ph_map);
+ g_free(al_map);
+ g_free(sy_map);
+ return ret;
+}
diff --git a/hardinfo2/expr.c b/hardinfo2/expr.c
new file mode 100644
index 00000000..26284aee
--- /dev/null
+++ b/hardinfo2/expr.c
@@ -0,0 +1,250 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+/*
+ * This is only used to compute sensor values, hence the only variable supported is '@'.
+ * The '`' operator (ln(x)) is not available, nor multi-line formulas.
+ */
+
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "expr.h"
+#include "config.h"
+
+static MathToken *new_operator(gchar op)
+{
+ MathToken *t = g_new0(MathToken, 1);
+
+ t->val.op = op;
+ t->type = TOKEN_OPERATOR; /* operator */
+
+ return t;
+}
+
+static MathToken *new_variable(gchar var)
+{
+ MathToken *t = g_new0(MathToken, 1);
+
+ t->val.op = '@';
+ t->type = TOKEN_VARIABLE; /* variable */
+
+ return t;
+}
+
+static MathToken *new_value(gfloat value)
+{
+ MathToken *t = g_new0(MathToken, 1);
+
+ t->val.value = value;
+ t->type = TOKEN_VALUE; /* value */
+
+ return t;
+}
+
+static inline gint priority(char operation)
+{
+ switch (operation) {
+ case '^':
+ return 3;
+ case '*':
+ case '/':
+ return 2;
+ case '+':
+ case '-':
+ return 1;
+ case '(':
+ return 0;
+ }
+
+ return 0;
+}
+
+GSList *math_infix_to_postfix(GSList * infix)
+{
+ MathToken *stack[500];
+ gint t_sp = 0;
+
+ GSList *postfix = NULL, *p;
+ MathToken *top;
+
+ for (p = infix; p; p = p->next) {
+ MathToken *t = (MathToken *) p->data;
+
+ if (t->type == TOKEN_OPERATOR && t->val.op == '(') {
+ stack[++t_sp] = t;
+ } else if (t->type == TOKEN_OPERATOR && t->val.op == ')') {
+ for (top = stack[t_sp]; t_sp != 0 && top->val.op != '(';
+ top = stack[t_sp]) {
+ postfix = g_slist_append(postfix, stack[t_sp--]);
+ }
+ t_sp--;
+ } else if (t->type != TOKEN_OPERATOR) {
+ postfix = g_slist_append(postfix, t);
+ } else if (t_sp == 0) {
+ stack[++t_sp] = t;
+ } else {
+ while (t_sp != 0
+ && priority(t->val.op) <= priority(stack[t_sp]->val.op))
+ postfix = g_slist_append(postfix, stack[t_sp--]);
+ stack[++t_sp] = t;
+ }
+ }
+
+ while (t_sp)
+ postfix = g_slist_append(postfix, stack[t_sp--]);
+
+ return postfix;
+}
+
+static inline gfloat __result(gfloat op1, gfloat op2, gchar operation)
+{
+ switch (operation) {
+ case '^':
+ return powf(op1, op2);
+ case '+':
+ return op1 + op2;
+ case '-':
+ return op1 - op2;
+ case '/':
+ return op1 / op2;
+ case '*':
+ return op1 * op2;
+ }
+
+ return 0;
+}
+
+gfloat math_postfix_eval(GSList * postfix, gfloat at_value)
+{
+ GSList *p;
+ gfloat stack[500];
+ gint sp = 0;
+
+ memset(stack, 0, sizeof(gfloat) * 500);
+
+ for (p = postfix; p; p = p->next) {
+ MathToken *t = (MathToken *) p->data;
+
+ if (t->type == TOKEN_VARIABLE) {
+ stack[++sp] = at_value;
+ } else if (t->type == TOKEN_VALUE) {
+ stack[++sp] = t->val.value;
+ } else {
+ gfloat op1, op2;
+
+ op2 = stack[sp--];
+ op1 = stack[sp];
+
+ stack[sp] = __result(op1, op2, t->val.op);
+ }
+ }
+
+ return stack[sp];
+}
+
+GSList *math_string_to_infix(gchar * string)
+{
+ GSList *infix = NULL;
+ gchar *expr = string;
+
+ for (; *expr; expr++) {
+ if (strchr("+-/*^()", *expr)) {
+ infix = g_slist_append(infix, new_operator(*expr));
+ } else if (strchr("@", *expr)) {
+ infix = g_slist_append(infix, new_variable(*expr));
+ } else if (strchr("-.1234567890", *expr)) {
+ gchar value[32], *v = value;
+ gfloat floatval;
+
+ do {
+ *v++ = *expr++;
+ } while (*expr && strchr("-.1234567890", *expr));
+ expr--;
+ *v = '\0';
+
+ sscanf(value, "%f", &floatval);
+
+ infix = g_slist_append(infix, new_value(floatval));
+ } else if (!isspace(*expr)) {
+ g_print("Invalid token: [%c][%d]\n", *expr, *expr);
+ math_infix_free(infix, TRUE);
+ return NULL;
+ }
+ }
+
+ return infix;
+}
+
+void math_infix_free(GSList * infix, gboolean free_tokens)
+{
+ GSList *p;
+
+ if (!free_tokens)
+ for (p = infix; p; p = g_slist_delete_link(p, p));
+ else
+ for (p = infix; p; p = g_slist_delete_link(p, p)) {
+ MathToken *t = (MathToken *) p->data;
+ g_free(t);
+ }
+}
+
+GSList *math_string_to_postfix(gchar * string)
+{
+ GSList *infix;
+ GSList *postfix;
+
+ infix = math_string_to_infix(string);
+ if (!infix)
+ return NULL;
+
+ postfix = math_infix_to_postfix(infix);
+ math_infix_free(infix, FALSE);
+
+ return postfix;
+}
+
+gfloat math_string_eval(gchar * string, gfloat at_value)
+{
+ GSList *postfix;
+ gfloat val;
+
+ postfix = math_string_to_postfix(string);
+ val = math_postfix_eval(postfix, at_value);
+ math_postfix_free(postfix, TRUE);
+
+ return val;
+}
+
+#ifdef MATH_TEST
+int main(void)
+{
+ GSList *postfix;
+
+ gchar *expr = "0.9*(@+(5.2*0.923+3*(2.0)))";
+
+ postfix = math_string_to_postfix(expr);
+ g_print("%s = %f (must be 18.71964)\n", expr,
+ math_postfix_eval(postfix, 10));
+ math_postfix_free(postfix, TRUE);
+
+ return 0;
+}
+#endif
diff --git a/hardinfo2/gg_key_file_parse_string_as_value.c b/hardinfo2/gg_key_file_parse_string_as_value.c
new file mode 100644
index 00000000..6eba2af3
--- /dev/null
+++ b/hardinfo2/gg_key_file_parse_string_as_value.c
@@ -0,0 +1,112 @@
+/* From: gkeyfile.c - key file parser
+ *
+ * Copyright 2004 Red Hat, Inc.
+ * Copyright 2009-2010 Collabora Ltd.
+ * Copyright 2009 Nokia Corporation
+ *
+ * Written by Ray Strode <rstrode@redhat.com>
+ * Matthias Clasen <mclasen@redhat.com>
+ *
+ * This library 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 library 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 library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+gchar *
+gg_key_file_parse_string_as_value (const gchar *string, const gchar list_separator)
+{
+ gchar *value, *p, *q;
+ gsize length;
+ gboolean parsing_leading_space;
+
+ length = strlen (string) + 1;
+
+ /* Worst case would be that every character needs to be escaped.
+ * In other words every character turns to two characters. */
+ value = g_new (gchar, 2 * length);
+
+ p = (gchar *) string;
+ q = value;
+ parsing_leading_space = TRUE;
+ while (p < (string + length - 1))
+ {
+ gchar escaped_character[3] = { '\\', 0, 0 };
+
+ switch (*p)
+ {
+ case ' ':
+ if (parsing_leading_space)
+ {
+ escaped_character[1] = 's';
+ strcpy (q, escaped_character);
+ q += 2;
+ }
+ else
+ {
+ *q = *p;
+ q++;
+ }
+ break;
+ case '\t':
+ if (parsing_leading_space)
+ {
+ escaped_character[1] = 't';
+ strcpy (q, escaped_character);
+ q += 2;
+ }
+ else
+ {
+ *q = *p;
+ q++;
+ }
+ break;
+ case '\n':
+ escaped_character[1] = 'n';
+ strcpy (q, escaped_character);
+ q += 2;
+ break;
+ case '\r':
+ escaped_character[1] = 'r';
+ strcpy (q, escaped_character);
+ q += 2;
+ break;
+ case '\\':
+ escaped_character[1] = '\\';
+ strcpy (q, escaped_character);
+ q += 2;
+ parsing_leading_space = FALSE;
+ break;
+ default:
+ if (list_separator && *p == list_separator)
+ {
+ escaped_character[1] = list_separator;
+ strcpy (q, escaped_character);
+ q += 2;
+ parsing_leading_space = TRUE;
+ }
+ else
+ {
+ *q = *p;
+ q++;
+ parsing_leading_space = FALSE;
+ }
+ break;
+ }
+ p++;
+ }
+ *q = '\0';
+
+ return value;
+}
diff --git a/hardinfo2/gg_strescape.c b/hardinfo2/gg_strescape.c
new file mode 100644
index 00000000..93efe381
--- /dev/null
+++ b/hardinfo2/gg_strescape.c
@@ -0,0 +1,139 @@
+/* Base on: GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib.h>
+#include <string.h>
+
+guchar excmap_def[256] = {1,0};
+static void make_excmap_def() {
+ int i;
+ for(i=0; i<256; i++){
+ switch ((guchar)i)
+ {
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ case '\\':
+ case '"':
+ excmap_def[i] = 0;
+ break;
+ default:
+ if ((i < ' ') || (i >= 0177))
+ excmap_def[i] = 0;
+ else
+ excmap_def[i] = 1;
+ break;
+ }
+ }
+}
+
+gchar *
+gg_strescape (const gchar *source,
+ const gchar *exceptions,
+ const gchar *extra)
+{
+ const guchar *p;
+ gchar *dest;
+ gchar *q;
+ guchar excmap[256];
+
+ g_return_val_if_fail (source != NULL, NULL);
+
+ if (excmap_def[0]) /* [0] should be 0 or it isn't initialized */
+ make_excmap_def();
+
+ memcpy(excmap, excmap_def, 256);
+
+ p = (guchar *) source;
+ /* Each source byte needs maximally four destination chars (\777) */
+ q = dest = g_malloc (strlen (source) * 4 + 1);
+
+ if (exceptions)
+ {
+ guchar *e = (guchar *) exceptions;
+
+ while (*e)
+ {
+ excmap[*e] = 1;
+ e++;
+ }
+ }
+
+ if (extra)
+ {
+ guchar *e = (guchar *) extra;
+
+ while (*e)
+ {
+ excmap[*e] = 0;
+ e++;
+ }
+ }
+
+ while (*p)
+ {
+ if (excmap[*p])
+ *q++ = *p;
+ else
+ {
+ switch (*p)
+ {
+ case '\b':
+ *q++ = '\\';
+ *q++ = 'b';
+ break;
+ case '\f':
+ *q++ = '\\';
+ *q++ = 'f';
+ break;
+ case '\n':
+ *q++ = '\\';
+ *q++ = 'n';
+ break;
+ case '\r':
+ *q++ = '\\';
+ *q++ = 'r';
+ break;
+ case '\t':
+ *q++ = '\\';
+ *q++ = 't';
+ break;
+ case '\\':
+ *q++ = '\\';
+ *q++ = '\\';
+ break;
+ case '"':
+ *q++ = '\\';
+ *q++ = '"';
+ break;
+ default:
+ *q++ = '\\';
+ *q++ = '0' + (((*p) >> 6) & 07);
+ *q++ = '0' + (((*p) >> 3) & 07);
+ *q++ = '0' + ((*p) & 07);
+ break;
+ }
+ }
+ p++;
+ }
+ *q = 0;
+ return dest;
+}
diff --git a/hardinfo2/gpu_util.c b/hardinfo2/gpu_util.c
new file mode 100644
index 00000000..5d32f9e2
--- /dev/null
+++ b/hardinfo2/gpu_util.c
@@ -0,0 +1,477 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br>
+ * This file
+ * Copyright (C) 2018 Burt P. <pburt0@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "gpu_util.h"
+#include "nice_name.h"
+#include "cpu_util.h" /* for EMPIFNULL() */
+
+nvgpu *nvgpu_new() {
+ return g_new0(nvgpu, 1);
+}
+
+void nvgpu_free(nvgpu *s) {
+ if (s) {
+ free(s->model);
+ free(s->bios_version);
+ free(s->uuid);
+ }
+}
+
+static char *_line_value(char *line, const char *prefix) {
+ if (g_str_has_prefix(g_strstrip(line), prefix)) {
+ line += strlen(prefix) + 1;
+ return g_strstrip(line);
+ } else
+ return NULL;
+}
+
+static gboolean nv_fill_procfs_info(gpud *s) {
+ gchar *data, *p, *l, *next_nl;
+ gchar *pci_loc = pci_address_str(s->pci_dev->domain, s->pci_dev->bus, s->pci_dev->device, s->pci_dev->function);
+ gchar *nvi_file = g_strdup_printf("/proc/driver/nvidia/gpus/%s/information", pci_loc);
+
+ g_file_get_contents(nvi_file, &data, NULL, NULL);
+ g_free(pci_loc);
+ g_free(nvi_file);
+
+ if (data) {
+ s->nv_info = nvgpu_new();
+ p = data;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ g_strstrip(p);
+ if (l = _line_value(p, "Model")) {
+ s->nv_info->model = g_strdup(l);
+ goto nv_details_next;
+ }
+ if (l = _line_value(p, "GPU UUID")) {
+ s->nv_info->uuid = g_strdup(l);
+ goto nv_details_next;
+ }
+ if (l = _line_value(p, "Video BIOS")) {
+ s->nv_info->bios_version = g_strdup(l);
+ goto nv_details_next;
+ }
+
+ /* TODO: more details */
+
+ nv_details_next:
+ p = next_nl + 1;
+ }
+ g_free(data);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void intel_fill_freq(gpud *s) {
+ gchar path[256] = "";
+ gchar *min_mhz = NULL, *max_mhz = NULL;
+ if (s->sysfs_drm_path) {
+ snprintf(path, 255, "%s/%s/gt_min_freq_mhz", s->sysfs_drm_path, s->id);
+ g_file_get_contents(path, &min_mhz, NULL, NULL);
+ snprintf(path, 255, "%s/%s/gt_max_freq_mhz", s->sysfs_drm_path, s->id);
+ g_file_get_contents(path, &max_mhz, NULL, NULL);
+
+ if (min_mhz)
+ s->khz_min = atoi(min_mhz) * 1000;
+ if (max_mhz)
+ s->khz_max = atoi(max_mhz) * 1000;
+
+ g_free(min_mhz);
+ g_free(max_mhz);
+ }
+}
+
+static void amdgpu_parse_dpmclk(gchar *path, int *min, int *max) {
+ gchar *data = NULL, *p, *next_nl;
+ int sp, i, clk;
+
+ *min = -1;
+ *max = -1;
+
+ g_file_get_contents(path, &data, NULL, NULL);
+ if (data) {
+ p = data;
+
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ sp = sscanf(p, "%d: %dMhz", &i, &clk);
+
+ if (sp == 2) {
+ if (clk > 0) {
+ if (*min < 0 || clk < *min)
+ *min = clk;
+ if (clk > *max)
+ *max = clk;
+ }
+ }
+
+ p = next_nl + 1;
+ }
+ }
+ g_free(data);
+}
+
+static void amdgpu_fill_freq(gpud *s) {
+ gchar path[256] = "";
+ int clk_min = -1, clk_max = -1, mem_clk_min = -1, mem_clk_max = -1;
+
+ if (s->sysfs_drm_path) {
+ /* core */
+ snprintf(path, 255, "%s/%s/device/pp_dpm_sclk", s->sysfs_drm_path, s->id);
+ amdgpu_parse_dpmclk(path, &clk_min, &clk_max);
+
+ if (clk_max > 0)
+ s->khz_max = clk_max * 1000;
+ if (clk_min > 0)
+ s->khz_min = clk_min * 1000;
+
+ /* memory */
+ snprintf(path, 255, "%s/%s/device/pp_dpm_mclk", s->sysfs_drm_path, s->id);
+ amdgpu_parse_dpmclk(path, &mem_clk_min, &mem_clk_max);
+
+ if (mem_clk_max > 0)
+ s->mem_khz_max = mem_clk_max * 1000;
+ if (mem_clk_min > 0)
+ s->mem_khz_min = mem_clk_min * 1000;
+ }
+}
+
+gpud *gpud_new() {
+ return g_new0(gpud, 1);
+}
+
+void gpud_free(gpud *s) {
+ if (s) {
+ free(s->id);
+ free(s->nice_name);
+ free(s->vendor_str);
+ free(s->device_str);
+ free(s->location);
+ free(s->drm_dev);
+ free(s->sysfs_drm_path);
+ free(s->dt_compat);
+ g_free(s->dt_opp);
+ pcid_free(s->pci_dev);
+ nvgpu_free(s->nv_info);
+ g_free(s);
+ }
+}
+
+void gpud_list_free(gpud *s) {
+ gpud *n;
+ while(s != NULL) {
+ n = s->next;
+ gpud_free(s);
+ s = n;
+ }
+}
+
+/* returns number of items after append */
+static int gpud_list_append(gpud *l, gpud *n) {
+ int c = 0;
+ while(l != NULL) {
+ c++;
+ if (l->next == NULL) {
+ if (n != NULL) {
+ l->next = n;
+ c++;
+ }
+ break;
+ }
+ l = l->next;
+ }
+ return c;
+}
+
+int gpud_list_count(gpud *s) {
+ return gpud_list_append(s, NULL);
+}
+
+/* TODO: In the future, when there is more vendor specific information available in
+ * the gpu struct, then more precise names can be given to each gpu */
+static void make_nice_name(gpud *s) {
+
+ /* NV information available */
+ if (s->nv_info && s->nv_info->model) {
+ s->nice_name = g_strdup_printf("%s %s", "NVIDIA", s->nv_info->model);
+ return;
+ }
+
+ static const char unk_v[] = "Unknown"; /* do not... */
+ static const char unk_d[] = "Device"; /* ...translate */
+ const char *vendor_str = s->vendor_str;
+ const char *device_str = s->device_str;
+ if (!vendor_str)
+ vendor_str = unk_v;
+ if (!device_str)
+ device_str = unk_d;
+
+ /* try and a get a "short name" for the vendor */
+ vendor_str = vendor_get_shortest_name(vendor_str);
+
+ if (strstr(vendor_str, "Intel")) {
+ gchar *device_str_clean = strdup(device_str);
+ nice_name_intel_gpu_device(device_str_clean);
+ s->nice_name = g_strdup_printf("%s %s", vendor_str, device_str_clean);
+ g_free(device_str_clean);
+ } else if (strstr(vendor_str, "AMD")) {
+ /* AMD PCI strings are crazy stupid because they use the exact same
+ * chip and device id for a zillion "different products" */
+ char *full_name = strdup(device_str);
+ /* Try and shorten it to the chip code name only, at least */
+ char *b = strchr(full_name, '[');
+ if (b) *b = '\0';
+ s->nice_name = g_strdup_printf("%s %s", "AMD/ATI", g_strstrip(full_name));
+ free(full_name);
+ } else {
+ /* nothing nicer */
+ s->nice_name = g_strdup_printf("%s %s", vendor_str, device_str);
+ }
+
+}
+
+/* Look for this kind of thing:
+ * * /soc/gpu
+ * * /gpu@ff300000
+ *
+ * Usually a gpu dt node will have ./name = "gpu"
+ */
+static gchar *dt_find_gpu(dtr *dt, char *np) {
+ gchar *dir_path, *dt_path, *ret;
+ gchar *ftmp, *ntmp;
+ const gchar *fn;
+ GDir *dir;
+ dtr_obj *obj;
+
+ /* consider self */
+ obj = dtr_obj_read(dt, np);
+ dt_path = dtr_obj_path(obj);
+ ntmp = strstr(dt_path, "/gpu");
+ if (ntmp) {
+ /* was found in node name */
+ if ( strchr(ntmp+1, '/') == NULL) {
+ ftmp = ntmp + 4;
+ /* should either be NULL or @ */
+ if (*ftmp == '\0' || *ftmp == '@')
+ return g_strdup(dt_path);
+ }
+ }
+
+ /* search children ... */
+ dir_path = g_strdup_printf("%s/%s", dtr_base_path(dt), np);
+ dir = g_dir_open(dir_path, 0 , NULL);
+ if (dir) {
+ while((fn = g_dir_read_name(dir)) != NULL) {
+ ftmp = g_strdup_printf("%s/%s", dir_path, fn);
+ if ( g_file_test(ftmp, G_FILE_TEST_IS_DIR) ) {
+ if (strcmp(np, "/") == 0)
+ ntmp = g_strdup_printf("/%s", fn);
+ else
+ ntmp = g_strdup_printf("%s/%s", np, fn);
+ ret = dt_find_gpu(dt, ntmp);
+ g_free(ntmp);
+ if (ret != NULL) {
+ g_free(ftmp);
+ g_dir_close(dir);
+ return ret;
+ }
+ }
+ g_free(ftmp);
+ }
+ g_dir_close(dir);
+ }
+
+ return NULL;
+}
+
+gpud *dt_soc_gpu() {
+ static const char std_soc_gpu_drm_path[] = "/sys/devices/platform/soc/soc:gpu/drm";
+
+ /* compatible contains a list of compatible hardware, so be careful
+ * with matching order.
+ * ex: "ti,omap3-beagleboard-xm", "ti,omap3450", "ti,omap3";
+ * matches "omap3 family" first.
+ * ex: "brcm,bcm2837", "brcm,bcm2836";
+ * would match 2836 when it is a 2837.
+ */
+ const struct {
+ char *search_str;
+ char *vendor;
+ char *soc;
+ } dt_compat_searches[] = {
+ { "brcm,bcm2837-vc4", "Broadcom", "VideoCore IV" },
+ { "brcm,bcm2836-vc4", "Broadcom", "VideoCore IV" },
+ { "brcm,bcm2835-vc4", "Broadcom", "VideoCore IV" },
+ { "arm,mali-450", "ARM", "Mali 450" },
+ { "arm,mali", "ARM", "Mali family" },
+ { NULL, NULL, NULL }
+ };
+ char tmp_path[256] = "";
+ char *dt_gpu_path = NULL;
+ char *compat = NULL;
+ char *vendor = NULL, *device = NULL;
+ int i;
+
+ gpud *gpu = NULL;
+
+ dtr *dt = dtr_new(NULL);
+ if (!dtr_was_found(dt))
+ goto dt_gpu_end;
+
+ dt_gpu_path = dt_find_gpu(dt, "/");
+
+ if (dt_gpu_path == NULL)
+ goto dt_gpu_end;
+
+ snprintf(tmp_path, 255, "%s/compatible", dt_gpu_path);
+ compat = dtr_get_string(tmp_path, 1);
+
+ if (compat == NULL)
+ goto dt_gpu_end;
+
+ gpu = gpud_new();
+
+ i = 0;
+ while(dt_compat_searches[i].search_str != NULL) {
+ if (strstr(compat, dt_compat_searches[i].search_str) != NULL) {
+ vendor = dt_compat_searches[i].vendor;
+ device = dt_compat_searches[i].soc;
+ break;
+ }
+ i++;
+ }
+
+ gpu->dt_compat = compat;
+ gpu->dt_vendor = vendor;
+ gpu->dt_device = device;
+ gpu->dt_path = dt_gpu_path;
+ snprintf(tmp_path, 255, "%s/status", dt_gpu_path);
+ gpu->dt_status = dtr_get_string(tmp_path, 1);
+ snprintf(tmp_path, 255, "%s/name", dt_gpu_path);
+ gpu->dt_name = dtr_get_string(tmp_path, 1);
+ gpu->dt_opp = dtr_get_opp_range(dt, dt_gpu_path);
+ if (gpu->dt_opp) {
+ gpu->khz_max = gpu->dt_opp->khz_max;
+ gpu->khz_min = gpu->dt_opp->khz_min;
+ }
+ EMPIFNULL(gpu->dt_name);
+ EMPIFNULL(gpu->dt_status);
+
+ gpu->id = strdup("dt-soc-gpu");
+ gpu->location = strdup("SOC");
+
+ if (access(std_soc_gpu_drm_path, F_OK) != -1)
+ gpu->sysfs_drm_path = strdup(std_soc_gpu_drm_path);
+ if (vendor) gpu->vendor_str = strdup(vendor);
+ if (device) gpu->device_str = strdup(device);
+ make_nice_name(gpu);
+
+
+dt_gpu_end:
+ dtr_free(dt);
+ return gpu;
+}
+
+gpud *gpu_get_device_list() {
+ int cn = 0;
+ gpud *list = NULL;
+
+/* Can we just ask DRM someway? ... */
+ /* TODO: yes. /sys/class/drm/card* */
+
+/* Try PCI ... */
+ pcid_list pci_list = pci_get_device_list(0x300,0x3ff);
+ GSList *l = pci_list;
+
+ if (l) {
+ while(l) {
+ pcid *curr = (pcid*)l->data;
+ char *pci_loc = NULL;
+ gpud *new_gpu = gpud_new();
+ new_gpu->pci_dev = curr;
+
+ pci_loc = pci_address_str(curr->domain, curr->bus, curr->device, curr->function);
+
+ int len;
+ char drm_id[512] = "", card_id[64] = "";
+ char *drm_dev = NULL;
+ gchar *drm_path =
+ g_strdup_printf("/dev/dri/by-path/pci-%s-card", pci_loc);
+ memset(drm_id, 0, 512);
+ if ((len = readlink(drm_path, drm_id, sizeof(drm_id)-1)) != -1)
+ drm_id[len] = '\0';
+ g_free(drm_path);
+
+ if (strlen(drm_id) != 0) {
+ /* drm has the card */
+ drm_dev = strstr(drm_id, "card");
+ if (drm_dev)
+ snprintf(card_id, 64, "%s", drm_dev);
+ }
+
+ if (strlen(card_id) == 0) {
+ /* fallback to our own counter */
+ snprintf(card_id, 64, "pci-dc%d", cn);
+ cn++;
+ }
+
+ if (drm_dev)
+ new_gpu->drm_dev = strdup(drm_dev);
+
+ char *sysfs_path_candidate = g_strdup_printf("%s/%s/drm", SYSFS_PCI_ROOT, pci_loc);
+ if (access(sysfs_path_candidate, F_OK) != -1) {
+ new_gpu->sysfs_drm_path = sysfs_path_candidate;
+ } else
+ free(sysfs_path_candidate);
+ new_gpu->location = g_strdup_printf("PCI/%s", pci_loc);
+ new_gpu->id = strdup(card_id);
+ if (curr->vendor_id_str) new_gpu->vendor_str = strdup(curr->vendor_id_str);
+ if (curr->device_id_str) new_gpu->device_str = strdup(curr->device_id_str);
+ nv_fill_procfs_info(new_gpu);
+ intel_fill_freq(new_gpu);
+ amdgpu_fill_freq(new_gpu);
+ make_nice_name(new_gpu);
+ if (list == NULL)
+ list = new_gpu;
+ else
+ gpud_list_append(list, new_gpu);
+
+ free(pci_loc);
+ l=l->next;
+ }
+
+ /* don't pcid_list_free(pci_list); They will be freed by gpud_free() */
+ g_slist_free(pci_list); /* just the linking data */
+ return list;
+ }
+
+/* Try Device Tree ... */
+ list = dt_soc_gpu();
+ if (list) return list;
+
+/* Try other things ... */
+
+ return list;
+}
+
+
diff --git a/hardinfo2/hardinfo.c b/hardinfo2/hardinfo.c
new file mode 100644
index 00000000..edbad308
--- /dev/null
+++ b/hardinfo2/hardinfo.c
@@ -0,0 +1,175 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2009 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+#include <shell.h>
+
+#include <report.h>
+#include <hardinfo.h>
+#include <iconcache.h>
+#include <stock.h>
+#include <vendor.h>
+
+#include <binreloc.h>
+#include "dmi_util.h"
+
+ProgramParameters params = { 0 };
+
+int main(int argc, char **argv)
+{
+ int exit_code = 0;
+ GSList *modules;
+
+ setlocale(LC_ALL, "");
+ bindtextdomain("hardinfo2", LOCALEDIR);
+ textdomain("hardinfo2");
+
+ DEBUG("Hardinfo2 version " VERSION ". Debug version.");
+
+#if GLIB_CHECK_VERSION(2,32,0)
+#else
+ if (!g_thread_supported ()) g_thread_init (NULL);
+#endif
+
+ /* parse all command line parameters */
+ parameters_init(&argc, &argv, &params);
+
+ /* show version information and quit */
+ if (params.show_version) {
+ g_print("Hardinfo2 version " VERSION "\n");
+ g_print
+ (_(/*/ %d will be latest year of copyright*/ "Copyright (C) 2003-2023 L. A. F. Pereira. 2024-%d Hardinfo2 Project.\n\n"), HARDINFO2_COPYRIGHT_LATEST_YEAR );
+
+ g_print(_("Compile-time options:\n"
+ " Release version: %s (%s)\n"
+ " BinReloc enabled: %s\n"
+ " Data prefix: %s\n"
+ " Library prefix: %s\n"
+ " Compiled for: %s\n"),
+ RELEASE ? _("Yes") : "No (" VERSION ")", ARCH,
+ ENABLE_BINRELOC ? _("Yes") : _("No"),
+ PREFIX, LIBPREFIX, PLATFORM);
+
+ DEBUG(" Debugging is enabled.");
+
+ /* show also available modules */
+ params.list_modules = TRUE;
+ }
+
+ /* initialize the binreloc library, so we can load program data */
+ if (!binreloc_init(FALSE))
+ g_error(_("Failed to find runtime data.\n\n"
+ "\342\200\242 Is HardInfo2 correctly installed?\n"
+ "\342\200\242 See if %s and %s exists and you have read permission."),
+ PREFIX, LIBPREFIX);
+
+ /* list all module names */
+ if (params.list_modules) {
+ g_print(_("Modules:\n"
+ "%-20s %-15s %-12s\n"), _("File Name"), _("Name"), _("Version"));
+
+ for (modules = modules_load_all(); modules;
+ modules = modules->next) {
+ ShellModule *module = (ShellModule *) modules->data;
+ const ModuleAbout *ma = module_get_about(module);
+ gchar *name = g_path_get_basename(g_module_name(module->dll));
+
+ g_print("%-20s %-15s %-12s\n", name, module->name, ma->version);
+
+ g_free(name);
+ }
+
+ return 0;
+ }
+
+ if (!params.create_report && !params.run_benchmark) {
+ /* we only try to open the UI if the user didn't ask for a report. */
+ params.gui_running = ui_init(&argc, &argv);
+
+ /* as a fallback, if GTK+ initialization failed, run in report
+ generation mode. */
+ if (!params.gui_running) {
+ params.create_report = TRUE;
+ /* ... it is possible to -f html without -r */
+ if (params.report_format != REPORT_FORMAT_HTML)
+ params.markup_ok = FALSE;
+ }
+ }
+
+ if (params.use_modules) {
+ /* load only selected modules */
+ DEBUG("loading user-selected modules");
+ modules = modules_load_selected();
+ } else {
+ /* load all modules */
+ DEBUG("loading all modules");
+ modules = modules_load_all();
+ }
+
+ /* initialize vendor database */
+ vendor_init();
+
+ /* initialize moreinfo */
+ moreinfo_init();
+
+ if (params.run_benchmark) {
+ gchar *result;
+
+ result = module_call_method_param("benchmark::runBenchmark", params.run_benchmark);
+ if (!result) {
+ fprintf(stderr, _("Unknown benchmark ``%s'' or benchmark.so not loaded"), params.run_benchmark);
+ exit_code = 1;
+ } else {
+ fprintf(stderr, "\n");
+ g_print("%s\n", result);
+ g_free(result);
+ }
+ } else if (params.gui_running) {
+ /* initialize gui and start gtk+ main loop */
+ icon_cache_init();
+ stock_icons_init();
+
+ shell_init(modules);
+
+ DEBUG("entering gtk+ main loop");
+
+ gtk_main();
+ } else if (params.create_report) {
+ /* generate report */
+ gchar *report;
+
+ DEBUG("generating report");
+
+ report = report_create_from_module_list_format(modules,
+ params.
+ report_format);
+ g_print("%s", report);
+
+ g_free(report);
+ } else {
+ g_error(_("Don't know what to do. Exiting."));
+ }
+
+ moreinfo_shutdown();
+ vendor_cleanup();
+ dmidecode_cache_free();
+ free_auto_free_final();
+
+ DEBUG("finished");
+ return exit_code;
+}
diff --git a/hardinfo2/hinote_util.c b/hardinfo2/hinote_util.c
new file mode 100644
index 00000000..efc85be0
--- /dev/null
+++ b/hardinfo2/hinote_util.c
@@ -0,0 +1,27 @@
+
+#include "hardinfo.h"
+
+/* requires COMPILE_FLAGS "-std=c99" */
+
+static const char bullet_yes[] = "<big><b>\u2713</b></big>";
+static const char bullet_no[] = "<big><b>\u2022<tt> </tt></b></big>";
+static const char bullet_yes_text[] = "[X]";
+static const char bullet_no_text[] = "[ ]";
+
+gboolean note_cond_bullet(gboolean cond, gchar *note_buff, const gchar *desc_str) {
+ int l = strlen(note_buff);
+ if (params.markup_ok)
+ snprintf(note_buff + l, note_max_len - l - 1, "%s %s\n",
+ cond ? bullet_yes : bullet_no, desc_str);
+ else
+ snprintf(note_buff + l, note_max_len - l - 1, "%s %s\n",
+ cond ? bullet_yes_text : bullet_no_text, desc_str);
+ return cond;
+}
+
+gboolean note_require_tool(const gchar *tool, gchar *note_buff, const gchar *desc_str) {
+ gchar *tl = find_program((gchar*)tool);
+ gboolean found = note_cond_bullet(!!tl, note_buff, desc_str);
+ g_free(tl);
+ return found;
+}
diff --git a/hardinfo2/info.c b/hardinfo2/info.c
new file mode 100644
index 00000000..fae16497
--- /dev/null
+++ b/hardinfo2/info.c
@@ -0,0 +1,584 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2017 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "util_sysobj.h" /* for SEQ() */
+
+/* Using a slightly modified gg_key_file_parse_string_as_value()
+ * from GLib in flatten(), to escape characters and the separator.
+ * The function is not public in GLib and we don't have a GKeyFile
+ * to pass it anyway. */
+/* Now in hardinfo.h -- #include "gg_key_file_parse_string_as_value.c" */
+
+static const gchar *info_column_titles[] = {
+ "TextValue", "Value", "Progress", "Extra1", "Extra2"
+};
+
+struct Info *info_new(void)
+{
+ struct Info *info = g_new0(struct Info, 1);
+
+ info->groups = g_array_new(FALSE, FALSE, sizeof(struct InfoGroup));
+ info->view_type = SHELL_VIEW_NORMAL;
+ info->column_headers_visible = FALSE;
+ info->zebra_visible = FALSE;
+ info->normalize_percentage = TRUE;
+
+ return info;
+}
+
+void info_group_add_field(struct InfoGroup *group, struct InfoField field)
+{
+ if (!group)
+ return;
+
+ /* info_field_last() */
+ if (!field.name)
+ return;
+
+ g_array_append_val(group->fields, field);
+}
+
+void info_group_add_fieldsv(struct InfoGroup *group, va_list ap)
+{
+ while (1) {
+ struct InfoField field = va_arg(ap, struct InfoField);
+
+ /* info_field_last() */
+ if (!field.name)
+ break;
+
+ g_array_append_val(group->fields, field);
+ }
+}
+
+void info_group_add_fields(struct InfoGroup *group, ...)
+{
+ va_list ap;
+
+ va_start(ap, group);
+ info_group_add_fieldsv(group, ap);
+ va_end(ap);
+}
+
+struct InfoGroup *info_add_group(struct Info *info, const gchar *group_name, ...)
+{
+ struct InfoGroup group = {
+ .name = group_name,
+ .fields = g_array_new(FALSE, FALSE, sizeof(struct InfoField))
+ };
+ va_list ap;
+
+ va_start(ap, group_name);
+ info_group_add_fieldsv(&group, ap);
+ va_end(ap);
+
+ g_array_append_val(info->groups, group);
+
+ return &g_array_index(info->groups, struct InfoGroup, info->groups->len - 1);
+}
+
+struct InfoField info_field_printf(const gchar *name, const gchar *format, ...)
+{
+ gchar *value;
+ va_list ap;
+
+ va_start(ap, format);
+ value = g_strdup_vprintf(format, ap);
+ va_end(ap);
+
+ return (struct InfoField) {
+ .name = name,
+ .value = value,
+ .free_value_on_flatten = TRUE,
+ };
+}
+
+void info_group_strip_extra(struct InfoGroup *group)
+{
+ guint fi;
+ char *val, *oldval;
+ struct InfoField *field;
+
+ for (fi = 0; fi < group->fields->len; fi++) {
+ field = &g_array_index(group->fields, struct InfoField, fi);
+ if (field->value){
+ val = strrchr(field->value, '|');
+ if (val) {
+ oldval = (gchar*)field->value;
+ field->value = strdup(val + 1);
+ g_free(oldval);
+ }
+ }
+ }
+}
+
+void info_add_computed_group(struct Info *info, const gchar *name, const gchar *value)
+{
+ /* This is a scaffolding method: HardInfo should move away from pre-computing
+ * the strings in favor of storing InfoGroups instead; while modules are not
+ * fully converted, use this instead. */
+ struct Info *tmp_info = NULL;
+ struct InfoGroup donor = {};
+ gchar *tmp_str = NULL;
+
+ if (name)
+ tmp_str = g_strdup_printf("[%s]\n%s", name, value);
+ else
+ tmp_str = g_strdup(value);
+
+ tmp_info = info_unflatten(tmp_str);
+ if (tmp_info->groups->len != 1) {
+ fprintf(stderr,
+ "info_add_computed_group(): expected only one group in value! (actual: %d)\n",
+ tmp_info->groups->len);
+ } else {
+ donor = g_array_index(tmp_info->groups, struct InfoGroup, 0);
+ g_array_append_val(info->groups, donor);
+ }
+
+ g_free(tmp_info); // TODO: doesn't do enough
+ g_free(tmp_str);
+}
+
+void info_add_computed_group_wo_extra(struct Info *info, const gchar *name, const gchar *value)
+{
+ /* This is a scaffolding method: HardInfo should move away from pre-computing
+ * the strings in favor of storing InfoGroups instead; while modules are not
+ * fully converted, use this instead. */
+ struct InfoGroup *agroup;
+
+ info_add_computed_group(info, name, value);
+ if (info->groups->len > 0) {
+ agroup = &g_array_index(info->groups, struct InfoGroup, info->groups->len - 1);
+ info_group_strip_extra(agroup);
+ }
+}
+
+void info_set_column_title(struct Info *info, const gchar *column, const gchar *title)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS(info_column_titles); i++) {
+ if (g_str_equal(info_column_titles[i], column)) {
+ info->column_titles[i] = title;
+ return;
+ }
+ }
+}
+
+void info_set_column_headers_visible(struct Info *info, gboolean setting)
+{
+ info->column_headers_visible = setting;
+}
+
+void info_set_zebra_visible(struct Info *info, gboolean setting)
+{
+ info->zebra_visible = setting;
+}
+
+void info_set_normalize_percentage(struct Info *info, gboolean setting)
+{
+ info->normalize_percentage = setting;
+}
+
+void info_set_view_type(struct Info *info, ShellViewType setting)
+{
+ info->view_type = setting;
+}
+
+void info_set_reload_interval(struct Info *info, int setting)
+{
+ info->reload_interval = setting;
+}
+
+static int info_field_cmp_name_ascending(const void *a, const void *b)
+{
+ const struct InfoField *aa = a, *bb = b;
+ return g_strcmp0(aa->name, bb->name);
+}
+
+static int info_field_cmp_name_descending(const void *a, const void *b)
+{
+ const struct InfoField *aa = a, *bb = b;
+ return g_strcmp0(bb->name, aa->name);
+}
+
+static int info_field_cmp_value_ascending(const void *a, const void *b)
+{
+ const struct InfoField *aa = a, *bb = b;
+ return g_strcmp0(aa->value, bb->value);
+}
+
+static int info_field_cmp_value_descending(const void *a, const void *b)
+{
+ const struct InfoField *aa = a, *bb = b;
+ return g_strcmp0(bb->value, aa->value);
+}
+
+static int info_field_cmp_tag_ascending(const void *a, const void *b)
+{
+ const struct InfoField *aa = a, *bb = b;
+ return g_strcmp0(aa->tag, bb->tag);
+}
+
+static int info_field_cmp_tag_descending(const void *a, const void *b)
+{
+ const struct InfoField *aa = a, *bb = b;
+ return g_strcmp0(bb->tag, aa->tag);
+}
+
+static const GCompareFunc sort_functions[INFO_GROUP_SORT_MAX] = {
+ [INFO_GROUP_SORT_NONE] = NULL,
+ [INFO_GROUP_SORT_NAME_ASCENDING] = info_field_cmp_name_ascending,
+ [INFO_GROUP_SORT_NAME_DESCENDING] = info_field_cmp_name_descending,
+ [INFO_GROUP_SORT_VALUE_ASCENDING] = info_field_cmp_value_ascending,
+ [INFO_GROUP_SORT_VALUE_DESCENDING] = info_field_cmp_value_descending,
+ [INFO_GROUP_SORT_TAG_ASCENDING] = info_field_cmp_tag_ascending,
+ [INFO_GROUP_SORT_TAG_DESCENDING] = info_field_cmp_tag_descending,
+};
+
+static void field_free_strings(struct InfoField *field)
+{
+ if (field->free_value_on_flatten)
+ g_free((gchar *)field->value);
+ if (field->free_name_on_flatten)
+ g_free((gchar *)field->name);
+ g_free(field->tag);
+}
+
+static void free_group_fields(struct InfoGroup *group)
+{
+ if (group && group->fields) {
+ guint i;
+
+ for (i = 0; i < group->fields->len; i++) {
+ struct InfoField *field =
+ &g_array_index(group->fields, struct InfoField, i);
+ field_free_strings(field);
+ }
+
+ g_array_free(group->fields, TRUE);
+ }
+}
+
+static void flatten_group(GString *output, const struct InfoGroup *group, guint group_count)
+{
+ guint i;
+
+ if (group->name != NULL)
+ g_string_append_printf(output, "[%s#%d]\n", group->name, group_count);
+
+ if (group->sort != INFO_GROUP_SORT_NONE)
+ g_array_sort(group->fields, sort_functions[group->sort]);
+
+ if (group->fields) {
+ for (i = 0; i < group->fields->len; i++) {
+ struct InfoField *field = &g_array_index(group->fields, struct InfoField, i);
+ gchar tmp_tag[256] = ""; /* for generated tag */
+
+ gboolean do_escape = TRUE; /* refers to the value side only */
+ if (field->value && strchr(field->value, '|') ) {
+ /* turning off escaping for values that may have columns */
+ do_escape = FALSE;
+ /* TODO:/FIXME: struct InfoField should store the column values
+ * in an array instead of packing them into one value with '|'.
+ * Then each value can be escaped and joined together with '|'
+ * for flatten(). unflatten() can then split on non-escaped '|',
+ * and unescape the result values into the column value array.
+ * Another way to do this might be to check
+ * .column_headers_visible in struct Info, but that is not
+ * available here.
+ */
+ }
+
+ const gchar *tp = field->tag;
+ gboolean tagged = !!tp;
+ gboolean flagged = field->highlight || field->report_details || field->value_has_vendor;
+ if (!tp) {
+ snprintf(tmp_tag, 255, "ITEM%d-%d", group_count, i);
+ tp = tmp_tag;
+ }
+ if (!field->label_is_escaped) {
+ if (strchr(field->name, '=')
+ || strchr(field->name, '$')) {
+ // TODO: what about # ?
+ gchar *ofn = (gchar*)field->name;
+ field->name = key_label_escape(ofn);
+ g_free(ofn);
+ field->label_is_escaped = TRUE;
+ }
+ }
+ if (tagged || flagged || field->icon) {
+ g_string_append_printf(output, "$%s%s%s%s%s$",
+ field->label_is_escaped ? "@" : "",
+ field->highlight ? "*" : "",
+ field->report_details ? "!" : "",
+ field->value_has_vendor ? "^" : "",
+ tp);
+ }
+
+ if (do_escape) {
+ gchar *escaped_value = gg_key_file_parse_string_as_value(field->value, '|');
+ g_string_append_printf(output, "%s=%s\n", field->name, escaped_value);
+ g_free(escaped_value);
+ } else {
+ g_string_append_printf(output, "%s=%s\n", field->name, field->value);
+ }
+ }
+ } else if (group->computed) {
+ g_string_append_printf(output, "%s\n", group->computed);
+ }
+}
+
+static void flatten_shell_param(GString *output, const struct InfoGroup *group, guint group_count)
+{
+ guint i;
+
+ if (!group->fields)
+ return;
+
+ for (i = 0; i < group->fields->len; i++) {
+ struct InfoField *field = &g_array_index(group->fields, struct InfoField, i);
+ gchar tmp_tag[256] = ""; /* for generated tag */
+
+ const gchar *tp = field->tag;
+ gboolean tagged = !!tp;
+ if (!tp) {
+ snprintf(tmp_tag, 255, "ITEM%d-%d", group_count, i);
+ tp = tmp_tag;
+ }
+
+ if (field->update_interval) {
+ g_string_append_printf(output, "UpdateInterval$%s%s%s=%d\n",
+ tagged ? tp : "", tagged ? "$" : "", /* tag and close or nothing */
+ field->name,
+ field->update_interval);
+ }
+
+ if (field->icon) {
+ g_string_append_printf(output, "Icon$%s$=%s\n",
+ tp, field->icon);
+ }
+ }
+}
+
+static void flatten_shell_param_global(GString *output, const struct Info *info)
+{
+ guint i;
+
+ g_string_append_printf(output, "ViewType=%d\n", info->view_type);
+ g_string_append_printf(output, "ShowColumnHeaders=%s\n",
+ info->column_headers_visible ? "true" : "false");
+
+ if (info->zebra_visible)
+ g_string_append(output, "Zebra=1\n");
+
+ if (info->reload_interval)
+ g_string_append_printf(output, "ReloadInterval=%d\n", info->reload_interval);
+
+ if (!info->normalize_percentage)
+ g_string_append(output, "NormalizePercentage=false\n");
+
+ for (i = 0; i < G_N_ELEMENTS(info_column_titles); i++) {
+ if (!info->column_titles[i])
+ continue;
+
+ g_string_append_printf(output, "ColumnTitle$%s=%s\n",
+ info_column_titles[i], info->column_titles[i]);
+ }
+}
+
+gchar *info_flatten(struct Info *info)
+{
+ /* This is a scaffolding method: eventually the HardInfo shell should
+ * understand a struct Info instead of parsing these strings, which are
+ * brittle and unnecessarily complicates things. Being a temporary
+ * method, no attention is paid to improve the memory allocation
+ * strategy. */
+ GString *values;
+ GString *shell_param;
+ guint i;
+
+ values = g_string_new(NULL);
+ shell_param = g_string_new(NULL);
+
+ if (info->groups) {
+ for (i = 0; i < info->groups->len; i++) {
+ struct InfoGroup *group =
+ &g_array_index(info->groups, struct InfoGroup, i);
+
+ flatten_group(values, group, i);
+ flatten_shell_param(shell_param, group, i);
+
+ free_group_fields(group);
+ }
+
+ g_array_free(info->groups, TRUE);
+ }
+
+ flatten_shell_param_global(shell_param, info);
+ g_string_append_printf(values, "[$ShellParam$]\n%s", shell_param->str);
+
+ g_string_free(shell_param, TRUE);
+ g_free(info);
+
+ return g_string_free(values, FALSE);
+}
+
+void info_remove_group(struct Info *info, guint index)
+{
+ struct InfoGroup *grp;
+
+ if (index >= info->groups->len)
+ return;
+
+ grp = &g_array_index(info->groups, struct InfoGroup, index);
+ free_group_fields(grp);
+
+ g_array_remove_index(info->groups, index);
+}
+
+struct InfoField *info_find_field(struct Info *info, const gchar *tag, const gchar *name) {
+ struct InfoGroup *group;
+ struct InfoField *field;
+ guint gi,fi;
+ for (gi = 0; gi < info->groups->len; gi++) {
+ struct InfoGroup *group = &g_array_index(info->groups, struct InfoGroup, gi);
+ for (fi = 0; fi < group->fields->len; fi++) {
+ struct InfoField *field = &g_array_index(group->fields, struct InfoField, fi);
+ if (tag && SEQ(tag, field->tag) )
+ return field;
+ else if (name && SEQ(name, field->name) )
+ return field;
+ }
+ }
+ return NULL;
+}
+
+#define VAL_FALSE_OR_TRUE ((!g_strcmp0(value, "true") || !g_strcmp0(value, "1")) ? TRUE : FALSE)
+
+struct Info *info_unflatten(const gchar *str)
+{
+ struct Info *info = info_new();
+ GKeyFile *key_file = g_key_file_new();
+ gchar **groups;
+ gsize ngroups;
+ int g, k, spg = -1;
+
+ g_key_file_load_from_data(key_file, str, strlen(str), 0, NULL);
+ groups = g_key_file_get_groups(key_file, &ngroups);
+ for (g = 0; groups[g]; g++) {
+ gchar *group_name = groups[g];
+ gchar **keys = g_key_file_get_keys(key_file, group_name, NULL, NULL);
+
+ if (*group_name == '$') {
+ /* special group */
+ if (SEQ(group_name, "$ShellParam$") )
+ spg = g; /* handle after all groups are added */
+ else {
+ /* This special group is unknown and won't be handled, so
+ * the name will not be linked anywhere. */
+ g_free(group_name);
+ continue;
+ }
+ } else {
+ /* normal group */
+ struct InfoGroup group = {};
+ group.name = group_name;
+ group.fields = g_array_new(FALSE, FALSE, sizeof(struct InfoField));
+ group.sort = INFO_GROUP_SORT_NONE;
+
+ for (k = 0; keys[k]; k++) {
+ struct InfoField field = {};
+ gchar *flags, *tag, *name, *label, *dis;
+ key_get_components(keys[k], &flags, &tag, &name, &label, &dis, TRUE);
+ gchar *value = g_key_file_get_value(key_file, group_name, keys[k], NULL);
+
+ field.tag = tag;
+ field.name = name;
+ field.value = value;
+ if (key_label_is_escaped(flags))
+ field.label_is_escaped = TRUE;
+ if (key_wants_details(flags))
+ field.report_details = TRUE;
+ if (key_is_highlighted(flags))
+ field.highlight = TRUE;
+ if (key_value_has_vendor_string(flags))
+ field.value_has_vendor = TRUE;
+ field.free_value_on_flatten = TRUE;
+ field.free_name_on_flatten = TRUE;
+
+ g_free(flags);
+ g_free(label);
+ g_free(dis);
+ g_array_append_val(group.fields, field);
+ }
+ g_array_append_val(info->groups, group);
+ }
+ }
+
+ if (spg >= 0) {
+ gchar *group_name = groups[spg];
+ gchar **keys = g_key_file_get_keys(key_file, group_name, NULL, NULL);
+ for (k = 0; keys[k]; k++) {
+ gchar *value = g_key_file_get_value(key_file, group_name, keys[k], NULL);
+ gchar *parm = NULL;
+ if (SEQ(keys[k], "ViewType")) {
+ info_set_view_type(info, atoi(value));
+ } else if (SEQ(keys[k], "ShowColumnHeaders")) {
+ info_set_column_headers_visible(info, VAL_FALSE_OR_TRUE);
+ } else if (SEQ(keys[k], "Zebra")) {
+ info_set_zebra_visible(info, VAL_FALSE_OR_TRUE);
+ } else if (SEQ(keys[k], "ReloadInterval")) {
+ info_set_reload_interval(info, atoi(value));
+ } else if (SEQ(keys[k], "NormalizePercentage")) {
+ info_set_normalize_percentage(info, VAL_FALSE_OR_TRUE);
+ } else if (g_str_has_prefix(keys[k], "ColumnTitle$")) {
+ info_set_column_title(info, strchr(keys[k], '$') + 1, value);
+ } else if (g_str_has_prefix(keys[k], "Icon$")) {
+ const gchar *chk_name = NULL;
+ gchar *chk_tag = NULL;
+ parm = strchr(keys[k], '$');
+ if (key_is_flagged(parm))
+ chk_tag = key_mi_tag(parm);
+ struct InfoField *field = info_find_field(info, chk_tag, NULL);
+ if (field)
+ field->icon = value;
+ g_free(chk_tag);
+ } else if (g_str_has_prefix(keys[k], "UpdateInterval$")) {
+ const gchar *chk_name = NULL;
+ gchar *chk_tag = NULL;
+ parm = strchr(keys[k], '$');
+ if (key_is_flagged(parm)) {
+ chk_tag = key_mi_tag(parm);
+ chk_name = key_get_name(parm);
+ } else
+ chk_name = key_get_name(parm+1);
+ struct InfoField *field = info_find_field(info, chk_tag, chk_name);
+ if (field)
+ field->update_interval = atoi(value);
+ g_free(chk_tag);
+ }
+ }
+ g_free(group_name);
+ g_strfreev(keys);
+ }
+
+ return info;
+}
diff --git a/hardinfo2/pci_util.c b/hardinfo2/pci_util.c
new file mode 100644
index 00000000..f0c5059a
--- /dev/null
+++ b/hardinfo2/pci_util.c
@@ -0,0 +1,463 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br>
+ * This file
+ * Copyright (C) 2018 Burt P. <pburt0@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "pci_util.h"
+#include "util_ids.h"
+
+gchar *pci_ids_file = NULL;
+GTimer *pci_ids_timer = NULL;
+const gboolean nolspci = FALSE; /* true for testing */
+
+/* Two pieces of info still only from lspci:
+ * - kernel driver in use
+ * - kernel modules list
+ *
+ * TODO: could use readlink() and basename() to get kernel driver from sysfs
+ * - /sys/bus/pci/devices/<addy>/driver is a symlink
+ */
+
+const gchar *find_pci_ids_file() {
+ if (pci_ids_file) {
+ if (!strstr(pci_ids_file, ".min"))
+ return pci_ids_file;
+ if (g_timer_elapsed(pci_ids_timer, NULL) > 2.0) {
+ /* try again for the full version */
+ DEBUG("find_pci_ids_file() found only a \".min\" version, trying again...");
+ g_free(pci_ids_file);
+ pci_ids_file = NULL;
+ }
+ }
+ char *file_search_order[] = {
+ g_strdup("/usr/share/hwdata/pci.ids"),
+ g_strdup("/usr/share/misc/pci.ids"),
+ g_build_filename(g_get_user_config_dir(), "hardinfo2", "pci.ids", NULL),
+ g_build_filename(params.path_data, "pci.ids", NULL),
+ g_build_filename(g_get_user_config_dir(), "hardinfo2", "pci.ids.min", NULL),
+ g_build_filename(params.path_data, "pci.ids.min", NULL),
+ NULL
+ };
+ int n;
+ for(n = 0; file_search_order[n]; n++) {
+ if (!pci_ids_file && !access(file_search_order[n], R_OK))
+ pci_ids_file = file_search_order[n];
+ else
+ g_free(file_search_order[n]);
+ }
+ DEBUG("find_pci_ids_file() result: %s", pci_ids_file);
+ if (pci_ids_file) {
+ if (!pci_ids_timer)
+ pci_ids_timer = g_timer_new();
+ else
+ g_timer_reset(pci_ids_timer);
+ }
+ return pci_ids_file;
+}
+
+char *pci_lookup_ids_vendor_str(uint32_t id) {
+ gchar *ret = NULL;
+
+ ids_query_result result;// = {};
+ gchar *qpath;
+ memset(&result,0,sizeof(ids_query_result));
+ if (!find_pci_ids_file())
+ return FALSE;
+
+ qpath = g_strdup_printf("%04x", id);
+ scan_ids_file(pci_ids_file, qpath, &result, -1);
+ if (result.results[0]) {
+ ret = g_strdup(result.results[0]);
+ }
+ g_free(qpath);
+
+ return ret;
+}
+
+static gboolean pci_lookup_ids(pcid *d) {
+ gboolean ret = FALSE;
+ ids_query_result result;// = {};
+ gchar *qpath;
+ memset(&result,0,sizeof(ids_query_result));
+ if (!find_pci_ids_file())
+ return FALSE;
+
+ /* lookup vendor, device, sub device */
+ qpath = g_strdup_printf("%04x/%04x/%04x %04x",
+ d->vendor_id, d->device_id, d->sub_vendor_id, d->sub_device_id);
+ scan_ids_file(pci_ids_file, qpath, &result, -1);
+ if (result.results[0]) {
+ if (d->vendor_id_str) g_free(d->vendor_id_str);
+ d->vendor_id_str = g_strdup(result.results[0]);
+ ret = TRUE;
+ }
+ if (result.results[1]) {
+ if (d->device_id_str) g_free(d->device_id_str);
+ d->device_id_str = g_strdup(result.results[1]);
+ ret = TRUE;
+ }
+ if (result.results[2]) {
+ if (d->sub_device_id_str) g_free(d->sub_device_id_str);
+ d->sub_device_id_str = g_strdup(result.results[2]);
+ ret = TRUE;
+ }
+ g_free(qpath);
+
+ /* lookup sub vendor by itself */
+ qpath = g_strdup_printf("%04x", d->sub_vendor_id);
+ scan_ids_file(pci_ids_file, qpath, &result, -1);
+ if (result.results[0]) {
+ if (d->sub_vendor_id_str) g_free(d->sub_vendor_id_str);
+ d->sub_vendor_id_str = g_strdup(result.results[0]);
+ ret = TRUE;
+ };
+ g_free(qpath);
+
+ /* lookup class */
+ qpath = g_strdup_printf("C %02x/%02x", (d->class >> 8) & 0xff, (d->class & 0xff));
+ scan_ids_file(pci_ids_file, qpath, &result, -1);
+ if (result.results[0]) {
+ if (d->class_str) g_free(d->class_str);
+ d->class_str = g_strdup(result.results[0]);
+ if (result.results[1]
+ && !SEQ(result.results[0], result.results[1]) ) {
+ /* options 1: results[0] + " :: " + results[1] */
+ //d->class_str = appf(d->class_str, " :: ", "%s", result.results[1]);
+
+ /* option 2: results[1] or results[0] */
+ g_free(d->class_str);
+ d->class_str = g_strdup(result.results[1]);
+ }
+ ret = TRUE;
+ }
+ g_free(qpath);
+
+ return ret;
+}
+
+gint pcid_cmp_by_addy(gconstpointer a, gconstpointer b)
+{
+ const struct pcid *dev_a = a;
+ const struct pcid *dev_b = b;
+
+ if (!dev_a)
+ return !!dev_b;
+ if (!dev_b)
+ return !!dev_a;
+
+ return g_strcmp0(dev_a->slot_str, dev_b->slot_str);
+}
+
+void pcid_free(pcid *s) {
+ if (s) {
+ g_free(s->slot_str);
+ g_free(s->class_str);
+ g_free(s->vendor_id_str);
+ g_free(s->device_id_str);
+ g_free(s->sub_vendor_id_str);
+ g_free(s->sub_device_id_str);
+ g_free(s->driver);
+ g_free(s->driver_list);
+ g_free(s);
+ }
+}
+
+static char *lspci_line_value(char *line, const char *prefix) {
+ if (g_str_has_prefix(g_strstrip(line), prefix)) {
+ line += strlen(prefix) + 1;
+ return g_strstrip(line);
+ } else
+ return NULL;
+}
+
+/* read output line of lspci -vmmnn */
+static int lspci_line_string_and_code(char *line, char *prefix, char **str, uint32_t *code) {
+ char *l = lspci_line_value(line, prefix);
+ char *e;
+
+ if (l) {
+ e = strchr(l, 0);
+ while (e > l && *e != '[') e--;
+ sscanf(e, "[%x]", code);
+ *e = 0; /* terminate at start of code */
+ if (*str) free(*str); /* free if replacing */
+ *str = strdup(g_strstrip(l));
+ }
+ return 0;
+}
+
+static gboolean pci_fill_details(pcid *s) {
+ if (nolspci) return FALSE;
+ gboolean spawned;
+ gchar *out, *err, *p, *l, *next_nl;
+ gchar *pci_loc = pci_address_str(s->domain, s->bus, s->device, s->function);
+ gchar *lspci_cmd = g_strdup_printf("lspci -D -s %s -vvv", pci_loc);
+
+ spawned = hardinfo_spawn_command_line_sync(lspci_cmd,
+ &out, &err, NULL, NULL);
+ g_free(lspci_cmd);
+ g_free(pci_loc);
+ if (spawned) {
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ g_strstrip(p);
+ if (l = lspci_line_value(p, "Kernel driver in use")) {
+ s->driver = g_strdup(l);
+ goto pci_details_next;
+ }
+ if (l = lspci_line_value(p, "Kernel modules")) {
+ s->driver_list = g_strdup(l);
+ goto pci_details_next;
+ }
+ /* TODO: more details */
+
+ pci_details_next:
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+char *pci_address_str(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func) {
+ return g_strdup_printf("%04x:%02x:%02x.%01x", dom, bus, dev, func);
+}
+
+/* /sys/bus/pci/devices/0000:01:00.0/ */
+char *_sysfs_bus_pci(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func, const char *item) {
+ char *ret = NULL, *pci_loc, *sysfs_path;
+ pci_loc = pci_address_str(dom, bus, dev, func);
+ sysfs_path = g_strdup_printf("%s/%s/%s", SYSFS_PCI_ROOT, pci_loc, item);
+ g_file_get_contents(sysfs_path, &ret, NULL, NULL);
+ free(pci_loc);
+ free(sysfs_path);
+ return ret;
+}
+
+gboolean _sysfs_bus_pci_read_hex(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func, const char *item, uint32_t *val) {
+ char *tmp = _sysfs_bus_pci(dom, bus, dev, func, item);
+ uint32_t tval;
+ if (tmp && val) {
+ int ec = sscanf(tmp, "%x", &tval);
+ free(tmp);
+ if (ec) {
+ *val = tval;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/* https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-pci */
+static gboolean pci_get_device_sysfs(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func, pcid *s) {
+ char *tmp = NULL;
+ int ec = 0;
+ float tf;
+ s->domain = dom;
+ s->bus = bus;
+ s->device = dev;
+ s->function = func;
+ s->slot_str = s->slot_str ? s->slot_str : pci_address_str(dom, bus, dev, func);
+ if (! _sysfs_bus_pci_read_hex(dom, bus, dev, func, "class", &s->class) )
+ return FALSE;
+ s->class >>= 8; /* TODO: find out why */
+ _sysfs_bus_pci_read_hex(dom, bus, dev, func, "device", &s->device_id);
+ _sysfs_bus_pci_read_hex(dom, bus, dev, func, "vendor", &s->vendor_id);
+ _sysfs_bus_pci_read_hex(dom, bus, dev, func, "subsystem_device", &s->sub_device_id);
+ _sysfs_bus_pci_read_hex(dom, bus, dev, func, "subsystem_vendor", &s->sub_vendor_id);
+ _sysfs_bus_pci_read_hex(dom, bus, dev, func, "revision", &s->revision);
+
+ tmp = _sysfs_bus_pci(dom, bus, dev, func, "max_link_speed");
+ if (tmp) {
+ ec = sscanf(tmp, "%f GT/s", &tf);
+ if (ec) s->pcie_speed_max = tf;
+ free(tmp);
+ }
+ tmp = _sysfs_bus_pci(dom, bus, dev, func, "current_link_speed");
+ if (tmp) {
+ ec = sscanf(tmp, "%f GT/s", &tf);
+ if (ec) s->pcie_speed_curr = tf;
+ free(tmp);
+ }
+ tmp = _sysfs_bus_pci(dom, bus, dev, func, "max_link_width");
+ if (tmp) {
+ s->pcie_width_max = strtoul(tmp, NULL, 0);
+ free(tmp);
+ }
+ tmp = _sysfs_bus_pci(dom, bus, dev, func, "max_link_width");
+ if (tmp) {
+ s->pcie_width_curr = strtoul(tmp, NULL, 0);
+ free(tmp);
+ }
+ return TRUE;
+}
+
+static gboolean pci_get_device_lspci(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func, pcid *s) {
+ if (nolspci) return FALSE;
+ gboolean spawned;
+ gchar *out, *err, *p, *l, *next_nl;
+ gchar *pci_loc = pci_address_str(dom, bus, dev, func);
+ gchar *lspci_cmd = g_strdup_printf("lspci -D -s %s -vmmnn", pci_loc);
+
+ s->domain = dom;
+ s->bus = bus;
+ s->device = dev;
+ s->function = func;
+
+ spawned = hardinfo_spawn_command_line_sync(lspci_cmd,
+ &out, &err, NULL, NULL);
+ g_free(lspci_cmd);
+ if (spawned) {
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ g_strstrip(p);
+ if (l = lspci_line_value(p, "Slot")) {
+ s->slot_str = g_strdup(l);
+ if (strcmp(s->slot_str, pci_loc) != 0) {
+ printf("PCI: %s != %s\n", s->slot_str, pci_loc);
+ }
+ }
+ if (l = lspci_line_value(p, "Rev")) {
+ s->revision = strtol(l, NULL, 16);
+ }
+ lspci_line_string_and_code(p, "Class", &s->class_str, &s->class);
+ lspci_line_string_and_code(p, "Vendor", &s->vendor_id_str, &s->vendor_id);
+ lspci_line_string_and_code(p, "Device", &s->device_id_str, &s->device_id);
+ lspci_line_string_and_code(p, "SVendor", &s->sub_vendor_id_str, &s->sub_vendor_id);
+ lspci_line_string_and_code(p, "SDevice", &s->sub_device_id_str, &s->sub_device_id);
+
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ g_free(pci_loc);
+ return TRUE;
+ }
+ g_free(pci_loc);
+ return FALSE;
+}
+
+pcid *pci_get_device_str(const char *addy) {
+ uint32_t dom, bus, dev, func;
+ int ec;
+ if (addy) {
+ ec = sscanf(addy, "%x:%x:%x.%x", &dom, &bus, &dev, &func);
+ if (ec == 4) {
+ return pci_get_device(dom, bus, dev, func);
+ }
+ }
+ return NULL;
+}
+
+pcid *pci_get_device(uint32_t dom, uint32_t bus, uint32_t dev, uint32_t func) {
+ pcid *s = pcid_new();
+ gboolean ok = FALSE;
+ if (s) {
+ ok = pci_get_device_sysfs(dom, bus, dev, func, s);
+ if (ok) {
+ ok |= pci_lookup_ids(s);
+ if (!ok)
+ ok |= pci_get_device_lspci(dom, bus, dev, func, s);
+ }
+ if (!ok) {
+ pcid_free(s);
+ s = NULL;
+ }
+ }
+ return s;
+}
+
+static pcid_list pci_get_device_list_lspci(uint32_t class_min, uint32_t class_max) {
+ if (nolspci) return NULL;
+ gboolean spawned;
+ gchar *out, *err, *p, *next_nl;
+ pcid_list dl = NULL;
+ pcid *nd;
+ uint32_t dom, bus, dev, func, cls;
+ int ec;
+
+ if (class_max == 0) class_max = 0xffff;
+
+ spawned = hardinfo_spawn_command_line_sync("lspci -D -mn",
+ &out, &err, NULL, NULL);
+ if (spawned) {
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ ec = sscanf(p, "%x:%x:%x.%x \"%x\"", &dom, &bus, &dev, &func, &cls);
+ if (ec == 5) {
+ if (cls >= class_min && cls <= class_max) {
+ nd = pci_get_device(dom, bus, dev, func);
+ pci_fill_details(nd);
+ dl = g_slist_append(dl, nd);
+ }
+ }
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ }
+ return dl;
+}
+
+static pcid_list pci_get_device_list_sysfs(uint32_t class_min, uint32_t class_max) {
+ pcid_list dl = NULL;
+ pcid *nd;
+ uint32_t dom, bus, dev, func, cls;
+ int ec;
+
+ if (class_max == 0) class_max = 0xffff;
+
+ const gchar *f = NULL;
+ GDir *d = g_dir_open("/sys/bus/pci/devices", 0, NULL);
+ if (!d) return 0;
+
+ while((f = g_dir_read_name(d))) {
+ ec = sscanf(f, "%x:%x:%x.%x", &dom, &bus, &dev, &func);
+ if (ec == 4) {
+ gchar *cf = g_strdup_printf("/sys/bus/pci/devices/%s/class", f);
+ gchar *cstr = NULL;
+ if (g_file_get_contents(cf, &cstr, NULL, NULL) ) {
+ cls = strtoul(cstr, NULL, 16) >> 8;
+ if (cls >= class_min && cls <= class_max) {
+ nd = pci_get_device(dom, bus, dev, func);
+ pci_fill_details(nd);
+ dl = g_slist_append(dl, nd);
+ }
+ }
+ g_free(cstr);
+ g_free(cf);
+ }
+ }
+ g_dir_close(d);
+ return dl;
+}
+
+pcid_list pci_get_device_list(uint32_t class_min, uint32_t class_max) {
+ pcid_list dl = NULL;
+ dl = pci_get_device_list_sysfs(class_min, class_max);
+ if (!dl)
+ dl = pci_get_device_list_lspci(class_min, class_max);
+ return dl;
+}
+
diff --git a/hardinfo2/problem_marker.c b/hardinfo2/problem_marker.c
new file mode 100644
index 00000000..1e0c2aed
--- /dev/null
+++ b/hardinfo2/problem_marker.c
@@ -0,0 +1,13 @@
+
+#include "hardinfo.h"
+
+/* requires COMPILE_FLAGS "-std=c99" */
+
+const char *problem_marker() {
+ static const char as_markup[] = "<big><b>\u26A0</b></big>";
+ static const char as_text[] = "(!)";
+ if (params.markup_ok)
+ return as_markup;
+ else
+ return as_text;
+}
diff --git a/hardinfo2/socket.c b/hardinfo2/socket.c
new file mode 100644
index 00000000..b94e43c2
--- /dev/null
+++ b/hardinfo2/socket.c
@@ -0,0 +1,127 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "socket.h"
+
+Socket *sock_connect(gchar * host, gint port)
+{
+ struct sockaddr_in server;
+ Socket *s;
+ int sock;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock > 0) {
+ memset(&server, 0, sizeof(server));
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = inet_addr(host);
+ server.sin_port = htons(port);
+
+ if (connect(sock, (struct sockaddr *) (void *) &server, sizeof(server)) < 0) {
+ goto cleanup;
+ }
+
+ s = g_new0(Socket, 1);
+ s->sock = sock;
+
+ return s;
+ }
+
+cleanup:
+ close(sock);
+
+ return NULL;
+}
+
+/* From: http://www.erlenstar.demon.co.uk/unix/faq_3.html#SEC26 */
+static inline int __sock_is_ready(Socket * s, int mode)
+{
+ int rc, fd = s->sock;
+ fd_set fds;
+ struct timeval tv;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ tv.tv_sec = tv.tv_usec = 0;
+
+ if (mode == 0) {
+ /* read */
+ rc = select(fd + 1, &fds, NULL, NULL, &tv);
+ } else {
+ /* write */
+ rc = select(fd + 1, NULL, &fds, NULL, &tv);
+ }
+
+ if (rc < 0)
+ return -1;
+
+ return FD_ISSET(fd, &fds) ? 1 : 0;
+}
+
+int sock_ready_to_read(Socket * s)
+{
+ return __sock_is_ready(s, 0);
+}
+
+int sock_ready_to_write(Socket * s)
+{
+ return __sock_is_ready(s, 1);
+}
+
+int sock_read(Socket * s, gchar * buffer, gint size)
+{
+ if (size > 2 && sock_ready_to_read(s)) {
+ gint n;
+
+ n = read(s->sock, buffer, size - 1);
+ if (n > 0) {
+ buffer[n] = '\0';
+ } else {
+ return 0;
+ }
+
+ return n;
+ }
+
+ return 0;
+}
+
+int sock_write(Socket * s, gchar * str)
+{
+ while (!sock_ready_to_write(s));
+
+ return write(s->sock, str, strlen(str));
+}
+
+void sock_close(Socket * s)
+{
+ shutdown(s->sock, 2);
+ close(s->sock);
+ g_free(s);
+}
diff --git a/hardinfo2/storage_util.c b/hardinfo2/storage_util.c
new file mode 100644
index 00000000..e7006069
--- /dev/null
+++ b/hardinfo2/storage_util.c
@@ -0,0 +1,250 @@
+#include "udisks2_util.h"
+#include "storage_util.h"
+#include "util_ids.h"
+#include "hardinfo.h"
+
+gchar *sdcard_ids_file = NULL;
+gchar *oui_ids_file = NULL;
+
+// moved from udisks2_util.h
+void find_sdcard_ids_file() {
+ if (sdcard_ids_file) return;
+ char *file_search_order[] = {
+ g_build_filename(g_get_user_config_dir(), "hardinfo2", "sdcard.ids", NULL),
+ g_build_filename(params.path_data, "sdcard.ids", NULL),
+ NULL
+ };
+ int n;
+ for(n = 0; file_search_order[n]; n++) {
+ if (!sdcard_ids_file && !access(file_search_order[n], R_OK))
+ sdcard_ids_file = file_search_order[n];
+ else
+ g_free(file_search_order[n]);
+ }
+}
+
+void find_oui_ids_file() {
+ if (oui_ids_file) return;
+ char *file_search_order[] = {
+ g_build_filename(g_get_user_config_dir(), "hardinfo2", "ieee_oui.ids", NULL),
+ g_build_filename(params.path_data, "ieee_oui.ids", NULL),
+ NULL
+ };
+ int n;
+ for(n = 0; file_search_order[n]; n++) {
+ if (!oui_ids_file && !access(file_search_order[n], R_OK))
+ oui_ids_file = file_search_order[n];
+ else
+ g_free(file_search_order[n]);
+ }
+}
+
+gchar* get_oui_from_wwid(gchar* wwid){
+ gchar* ret = NULL;
+
+ if (g_str_has_prefix(wwid, "nna.")) {
+ if (strlen(wwid)*4 < 48)
+ return NULL;
+
+ switch (wwid[4]){
+ case '1':
+ case '2':
+ ret = g_strndup(wwid + 8, 6);
+ break;
+ case '5':
+ case '6':
+ ret = g_strndup(wwid + 5, 6);
+ break;
+ }
+ }
+ else if(g_str_has_prefix(wwid, "eui.")) {
+ if (strlen(wwid)*4 < 48)
+ return NULL;
+ ret = g_strndup(wwid+4, 6);
+ }
+
+ return ret;
+}
+
+gchar* get_oui_company(gchar* oui){
+ ids_query_result result = {};
+
+ if (!oui_ids_file)
+ find_oui_ids_file();
+
+ scan_ids_file(oui_ids_file, oui, &result, -1);
+ if (result.results[0])
+ return g_strdup(result.results[0]);
+
+ return NULL;
+}
+
+// moved from udisks2_util.h
+void check_sdcard_vendor(u2driveext *e) {
+ if (!e || !e->d) return;
+ if (!e->d->media) return;
+ if (! (g_str_has_prefix(e->d->media, "flash_sd")
+ || g_str_has_prefix(e->d->media, "flash_mmc") )) return;
+ if (e->d->vendor && e->d->vendor[0]) return;
+ if (!e->d->block_dev) return;
+
+ if (!sdcard_ids_file)
+ find_sdcard_ids_file();
+
+ gchar *qpath = NULL;
+ ids_query_result result = {};
+
+ gchar *oemid_path = g_strdup_printf("/sys/block/%s/device/oemid", e->d->block_dev);
+ gchar *manfid_path = g_strdup_printf("/sys/block/%s/device/manfid", e->d->block_dev);
+ gchar *oemid = NULL, *manfid = NULL;
+ g_file_get_contents(oemid_path, &oemid, NULL, NULL);
+ g_file_get_contents(manfid_path, &manfid, NULL, NULL);
+
+ unsigned int id = oemid?strtol(oemid, NULL, 16):0;
+ char c2 = id & 0xff, c1 = (id >> 8) & 0xff;
+
+ qpath = g_strdup_printf("OEMID %02x%02x", (unsigned int)c1, (unsigned int)c2);
+ scan_ids_file(sdcard_ids_file, qpath, &result, -1);
+ g_free(oemid);
+ if (result.results[0])
+ oemid = g_strdup(result.results[0]);
+ else
+ oemid = g_strdup_printf("OEM %02x%02x \"%c%c\"",
+ (unsigned int)c1, (unsigned int)c2,
+ isprint(c1) ? c1 : '.', isprint(c2) ? c2 : '.');
+ g_free(qpath);
+
+ id = manfid?strtol(manfid, NULL, 16):0;
+ qpath = g_strdup_printf("MANFID %06x", id);
+ scan_ids_file(sdcard_ids_file, qpath, &result, -1);
+ g_free(manfid);
+ if (result.results[0])
+ manfid = g_strdup(result.results[0]);
+ else
+ manfid = g_strdup_printf("MANF %06x", id);
+ g_free(qpath);
+
+ vendor_list vl = NULL;
+ const Vendor *v = NULL;
+ v = vendor_match(oemid, NULL);
+ if (v) vl = vendor_list_append(vl, v);
+ v = vendor_match(manfid, NULL);
+ if (v) vl = vendor_list_append(vl, v);
+ vl = vendor_list_remove_duplicates_deep(vl);
+ e->vendors = vendor_list_concat(e->vendors, vl);
+
+ g_free(e->d->vendor);
+ if (g_strcmp0(oemid, manfid) == 0)
+ e->d->vendor = g_strdup(oemid);
+ else
+ e->d->vendor = g_strdup_printf("%s / %s", oemid, manfid);
+
+ g_free(oemid);
+ g_free(manfid);
+ g_free(oemid_path);
+ g_free(manfid_path);
+
+ if (e->d->revision && e->d->revision[0]) return;
+
+ /* bonus: revision */
+ gchar *fwrev_path = g_strdup_printf("/sys/block/%s/device/fwrev", e->d->block_dev);
+ gchar *hwrev_path = g_strdup_printf("/sys/block/%s/device/hwrev", e->d->block_dev);
+ gchar *fwrev = NULL, *hwrev = NULL;
+ g_file_get_contents(fwrev_path, &fwrev, NULL, NULL);
+ g_file_get_contents(hwrev_path, &hwrev, NULL, NULL);
+
+ unsigned int fw = fwrev ? strtol(fwrev, NULL, 16) : 0;
+ unsigned int hw = hwrev ? strtol(hwrev, NULL, 16) : 0;
+ g_free(e->d->revision);
+ e->d->revision = g_strdup_printf("%02x.%02x", hw, fw);
+
+ g_free(fwrev);
+ g_free(hwrev);
+ g_free(fwrev_path);
+ g_free(hwrev_path);
+
+}
+
+void set_nvme_controller_info(u2driveext *e){
+ gchar *path = g_strdup_printf("/sys/block/%s/device/device", e->d->block_dev);
+ gchar *systarget = g_file_read_link(path, NULL);
+ gchar *target = systarget ? g_filename_to_utf8(systarget, -1, NULL, NULL, NULL) : NULL;
+ gchar *pci_addy = target ? g_path_get_basename(target) : NULL;
+ e->nvme_controller = pci_addy ? pci_get_device_str(pci_addy) : NULL;
+ g_free(path);
+ g_free(systarget);
+ g_free(target);
+ g_free(pci_addy);
+ if (e->nvme_controller) {
+ e->vendors = vendor_list_append(e->vendors,
+ vendor_match(e->nvme_controller->vendor_id_str, NULL));
+ e->vendors = vendor_list_append(e->vendors,
+ vendor_match(e->nvme_controller->sub_vendor_id_str, NULL));
+ }
+}
+
+GSList* get_udisks2_drives_ext(void){
+ GSList *node, *list;
+ u2driveext* extdrive;
+
+ list = get_udisks2_all_drives_info();
+
+ for (node = list; node != NULL; node = node->next) {
+ extdrive = u2drive_ext((udiskd *)node->data);
+ node->data = extdrive;
+
+ if (!extdrive->d->vendor || !extdrive->d->vendor[0]) {
+ // sometimes vendors adds their name to the model field
+ const Vendor *v = vendor_match(extdrive->d->model, NULL);
+ if (v)
+ extdrive->d->vendor = g_strdup(v->name);
+ }
+
+ check_sdcard_vendor(extdrive);
+
+ extdrive->vendors = vendor_list_append(extdrive->vendors, vendor_match(extdrive->d->vendor, NULL));
+
+ // get OUI from WWID
+ if (extdrive->d->wwid) {
+ extdrive->wwid_oui.oui = get_oui_from_wwid(extdrive->d->wwid);
+ if (extdrive->wwid_oui.oui) {
+ extdrive->wwid_oui.vendor = get_oui_company(extdrive->wwid_oui.oui);
+ }
+ if (extdrive->wwid_oui.vendor){
+ extdrive->vendors = vendor_list_append(extdrive->vendors, vendor_match(extdrive->wwid_oui.vendor, NULL));
+ }
+ }
+
+ // NVMe PCI device
+ if (strstr(extdrive->d->block_dev, "nvme")) {
+ set_nvme_controller_info(extdrive);
+ }
+
+ extdrive->vendors = gg_slist_remove_null(extdrive->vendors);
+ extdrive->vendors = vendor_list_remove_duplicates_deep(extdrive->vendors);
+
+ }
+ return list;
+}
+
+
+u2driveext* u2drive_ext(udiskd * udisks_drive_data) {
+ u2driveext* data = g_new0(u2driveext, 1);
+ data->d = udisks_drive_data;
+ return data;
+}
+
+void u2driveext_free(u2driveext *u) {
+ if (u) {
+ udiskd_free(u->d);
+ g_free(u->wwid_oui.oui);
+ g_free(u->wwid_oui.vendor);
+ pcid_free(u->nvme_controller);
+ g_free(u);
+ }
+}
+
+void storage_shutdown(){
+ g_free(sdcard_ids_file);
+ g_free(oui_ids_file);
+}
diff --git a/hardinfo2/udisks2_util.c b/hardinfo2/udisks2_util.c
new file mode 100644
index 00000000..e1567d84
--- /dev/null
+++ b/hardinfo2/udisks2_util.c
@@ -0,0 +1,641 @@
+#include <gio/gio.h>
+#include <string.h>
+#include "udisks2_util.h"
+
+#define UDISKS2_INTERFACE "org.freedesktop.UDisks2"
+#define UDISKS2_MANAGER_INTERFACE "org.freedesktop.UDisks2.Manager"
+#define UDISKS2_BLOCK_INTERFACE "org.freedesktop.UDisks2.Block"
+#define UDISKS2_LOOP_INTERFACE "org.freedesktop.UDisks2.Loop"
+#define UDISKS2_PARTITION_INTERFACE "org.freedesktop.UDisks2.Partition"
+#define UDISKS2_PART_TABLE_INTERFACE "org.freedesktop.UDisks2.PartitionTable"
+#define UDISKS2_DRIVE_INTERFACE "org.freedesktop.UDisks2.Drive"
+#define UDISKS2_DRIVE_ATA_INTERFACE "org.freedesktop.UDisks2.Drive.Ata"
+#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
+#define UDISKS2_MANAGER_OBJ_PATH "/org/freedesktop/UDisks2/Manager"
+#define UDISKS2_BLOCK_DEVICES_PATH "/org/freedesktop/UDisks2/block_devices"
+
+#define STRDUP_IF_NOT_EMPTY(S) (g_strcmp0(S, "") == 0) ? NULL: g_strdup(S);
+
+GDBusConnection* udisks2_conn = NULL;
+
+GVariant* get_dbus_property(GDBusProxy* proxy, const gchar *interface,
+ const gchar *property) {
+ GVariant *result, *v = NULL;
+ GError *error = NULL;
+
+ result = g_dbus_proxy_call_sync(proxy, "Get",
+ g_variant_new ("(ss)", interface, property),
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+
+ if (error != NULL) {
+ g_error_free (error);
+ return NULL;
+ }
+
+ g_variant_get(result, "(v)", &v);
+ g_variant_unref(result);
+ return v;
+}
+
+// this function works with udisks2 version 2.7.2 or newer
+GSList* get_block_dev_paths_from_udisks2(GDBusConnection* conn){
+ GDBusProxy *proxy;
+ GVariant *options, *v;
+ GVariantIter *iter;
+ GError *error = NULL;
+ GSList *block_paths = NULL;
+ const gchar *block_path = NULL;
+
+ proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, NULL,
+ UDISKS2_INTERFACE, UDISKS2_MANAGER_OBJ_PATH,
+ UDISKS2_MANAGER_INTERFACE, NULL, &error);
+ options = g_variant_new_parsed("@a{sv} { %s: <true> }",
+ "auth.no_user_interaction");
+ if (error != NULL){
+ g_error_free (error);
+ g_object_unref(proxy);
+ return NULL;
+ }
+
+ v = g_dbus_proxy_call_sync(proxy, "GetBlockDevices",
+ g_variant_new_tuple(&options, 1),
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+ g_object_unref(proxy);
+
+ if (error != NULL){
+ g_error_free (error);
+ return NULL;
+ }
+
+ g_variant_get(v, "(ao)", &iter);
+ while (g_variant_iter_loop (iter, "o", &block_path)){
+ block_paths = g_slist_append(block_paths, g_strdup(block_path));
+ }
+
+ g_variant_iter_free (iter);
+ g_variant_unref(v);
+ return block_paths;
+}
+
+GSList* get_block_dev_paths_from_sysfs(){
+ GSList *block_paths = NULL;
+ GDir *dir;
+ gchar *path;
+ const gchar *entry;
+
+ dir = g_dir_open("/sys/class/block", 0, NULL);
+ if (!dir)
+ return NULL;
+
+ while ((entry = g_dir_read_name(dir))) {
+ path = g_strdup_printf("%s/%s", UDISKS2_BLOCK_DEVICES_PATH, entry);
+ block_paths = g_slist_append(block_paths, path);
+ }
+
+ g_dir_close(dir);
+ return block_paths;
+}
+
+GSList* udisks2_drives_func_caller(GDBusConnection* conn,
+ gpointer (*func)(const char*, GDBusProxy*,
+ GDBusProxy*, const char*)) {
+ GDBusProxy *block_proxy, *drive_proxy;
+ GVariant *block_v, *v;
+ GSList *result_list = NULL, *block_dev_list, *node;
+ GError *error = NULL;
+ gpointer output;
+
+ gchar *block_path = NULL;
+ const gchar *block_dev, *drive_path = NULL;
+
+ if (conn == NULL)
+ return NULL;
+
+ // get block devices
+ block_dev_list = get_block_dev_paths_from_udisks2(conn);
+ if (block_dev_list == NULL)
+ block_dev_list = get_block_dev_paths_from_sysfs();
+
+ for (node = block_dev_list; node != NULL; node = node->next) {
+ block_path = (gchar *)node->data;
+ block_proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE,
+ NULL, UDISKS2_INTERFACE, block_path,
+ DBUS_PROPERTIES_INTERFACE, NULL, &error);
+ if (error){
+ g_error_free(error);
+ error = NULL;
+ continue;
+ }
+
+ // Skip partitions
+ v = get_dbus_property(block_proxy, UDISKS2_PARTITION_INTERFACE, "Size");
+ if (v){
+ g_variant_unref(v);
+ g_object_unref(block_proxy);
+ continue;
+ }
+
+ // Skip loop devices
+ v = get_dbus_property(block_proxy, UDISKS2_LOOP_INTERFACE, "BackingFile");
+ if (v){
+ g_variant_unref(v);
+ g_object_unref(block_proxy);
+ continue;
+ }
+
+ block_dev = block_path + strlen(UDISKS2_BLOCK_DEVICES_PATH) + 1;
+ drive_path = NULL;
+
+ // let's find drive proxy
+ v = get_dbus_property(block_proxy, UDISKS2_BLOCK_INTERFACE, "Drive");
+ if (v){
+ drive_path = g_variant_get_string(v, NULL);
+
+ if (g_strcmp0(drive_path, "/") != 0){
+ drive_proxy = g_dbus_proxy_new_sync(conn,
+ G_DBUS_PROXY_FLAGS_NONE, NULL,
+ UDISKS2_INTERFACE, drive_path,
+ DBUS_PROPERTIES_INTERFACE, NULL, &error);
+
+ if (error == NULL) {
+ // call requested function
+ output = func(block_dev, block_proxy, drive_proxy, drive_path);
+
+ if (output != NULL){
+ result_list = g_slist_append(result_list, output);
+ }
+ g_object_unref(drive_proxy);
+ }
+ else {
+ g_error_free(error);
+ error = NULL;
+ }
+ }
+ g_variant_unref(v);
+ }
+ g_object_unref(block_proxy);
+ }
+ g_slist_free_full(block_dev_list, g_free);
+
+ return result_list;
+}
+
+GDBusConnection* get_udisks2_connection(void) {
+ GDBusConnection *conn;
+ GDBusProxy *proxy;
+ GError *error = NULL;
+ GVariant *result = NULL;
+
+ // connection to system bus
+ conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (error != NULL) {
+ g_error_free (error);
+ return NULL;
+ }
+
+ // let's check if udisks2 is responding
+ proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, NULL,
+ UDISKS2_INTERFACE, UDISKS2_MANAGER_OBJ_PATH,
+ DBUS_PROPERTIES_INTERFACE, NULL, &error);
+ if (error != NULL) {
+ g_error_free (error);
+ g_object_unref(conn);
+ return NULL;
+ }
+
+ result = get_dbus_property(proxy, UDISKS2_MANAGER_INTERFACE, "Version");
+ g_object_unref(proxy);
+ if (error != NULL) {
+ g_error_free (error);
+ return NULL;
+ }
+
+ // OK, let's return connection to system bus
+ g_variant_unref(result);
+ return conn;
+}
+
+udiskt* udiskt_new() {
+ return g_new0(udiskt, 1);
+}
+
+udisksa* udisksa_new() {
+ return g_new0(udisksa, 1);
+}
+
+udiskp* udiskp_new() {
+ return g_new0(udiskp, 1);
+}
+
+udiskd* udiskd_new() {
+ return g_new0(udiskd, 1);
+}
+
+
+void udiskt_free(udiskt *u) {
+ if (u) {
+ g_free(u->drive);
+ g_free(u);
+ }
+}
+
+void udisksa_free(udisksa *u) {
+ if (u) {
+ g_free(u->identifier);
+ udisksa_free(u->next);
+ g_free(u);
+ }
+}
+
+void udiskp_free(udiskp *u) {
+ if (u) {
+ g_free(u->block);
+ g_free(u->type);
+ g_free(u->version);
+ g_free(u->label);
+ udiskp_free(u->next);
+ g_free(u);
+ }
+}
+
+void udiskd_free(udiskd *u) {
+ if (u) {
+ g_free(u->model);
+ g_free(u->vendor);
+ g_free(u->revision);
+ g_free(u->block_dev);
+ g_free(u->serial);
+ g_free(u->wwid);
+ g_free(u->connection_bus);
+ g_free(u->partition_table);
+ udiskp_free(u->partitions);
+ udisksa_free(u->smart_attributes);
+ g_free(u->media);
+ g_strfreev(u->media_compatibility);
+ g_free(u);
+ }
+}
+
+
+udiskp* get_udisks2_partition_info(const gchar *part_path) {
+ GVariant *v;
+ GDBusProxy *proxy=NULL;
+ GError *error = NULL;
+ udiskp* partition;
+ const gchar *str;
+
+ if (!g_str_has_prefix(part_path, UDISKS2_BLOCK_DEVICES_PATH)) {
+ return NULL;
+ }
+
+ partition = udiskp_new();
+ partition->block = g_strdup(part_path + strlen(UDISKS2_BLOCK_DEVICES_PATH) + 1);
+
+ proxy = g_dbus_proxy_new_sync(udisks2_conn, G_DBUS_PROXY_FLAGS_NONE,
+ NULL, UDISKS2_INTERFACE, part_path,
+ DBUS_PROPERTIES_INTERFACE, NULL, &error);
+
+ if ((proxy != NULL) && (error == NULL)) {
+ v = get_dbus_property(proxy, UDISKS2_BLOCK_INTERFACE, "IdLabel");
+ if (v) {
+ str = g_variant_get_string(v, NULL);
+ partition->label = STRDUP_IF_NOT_EMPTY(str);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(proxy, UDISKS2_BLOCK_INTERFACE, "IdType");
+ if (v) {
+ str = g_variant_dup_string(v, NULL);
+ partition->type = STRDUP_IF_NOT_EMPTY(str);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(proxy, UDISKS2_BLOCK_INTERFACE, "IdVersion");
+ if (v) {
+ str = g_variant_dup_string(v, NULL);
+ partition->version = STRDUP_IF_NOT_EMPTY(str);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(proxy, UDISKS2_BLOCK_INTERFACE, "Size");
+ if (v) {
+ partition->size = g_variant_get_uint64(v);
+ g_variant_unref(v);
+ }
+ }
+ else{
+ g_error_free(error);
+ }
+
+ g_object_unref(proxy);
+ return partition;
+}
+
+gpointer get_udisks2_temp(const char *blockdev, GDBusProxy *block,
+ GDBusProxy *drive, const char *drivepath){
+ GVariant *v;
+ gboolean smart_enabled = FALSE;
+ udiskt* disk_temp = NULL;
+
+ v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartEnabled");
+ if (v) {
+ smart_enabled = g_variant_get_boolean(v);
+ g_variant_unref(v);
+ }
+
+ if (!smart_enabled) {
+ return NULL;
+ }
+
+ v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartTemperature");
+ if (v) {
+ disk_temp = udiskt_new();
+ disk_temp->temperature = (gint32) (g_variant_get_double(v) - 273.15);
+ g_variant_unref(v);
+ }
+
+ if (!disk_temp) {
+ return NULL;
+ }
+
+ v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Model");
+ if (v) {
+ disk_temp->drive = g_variant_dup_string(v, NULL);
+ g_variant_unref(v);
+ }
+
+ return disk_temp;
+}
+
+gchar* get_udisks2_smart_attributes(udiskd* dsk, const char *drivepath){
+ GDBusProxy *proxy;
+ GVariant *options, *v, *v2;
+ GVariantIter *iter;
+ GError *error = NULL;
+ const char* aidenf;
+ guint8 aid;
+ gint32 avalue, aworst, athreshold, pretty_unit;
+ gint64 pretty;
+ udisksa *lastp = NULL, *p;
+
+ proxy = g_dbus_proxy_new_sync(udisks2_conn, G_DBUS_PROXY_FLAGS_NONE, NULL,
+ UDISKS2_INTERFACE, drivepath,
+ UDISKS2_DRIVE_ATA_INTERFACE, NULL, &error);
+
+ options = g_variant_new_parsed("@a{sv} { %s: <true> }",
+ "auth.no_user_interaction");
+ if (error != NULL){
+ g_error_free (error);
+ return NULL;
+ }
+
+ v = g_dbus_proxy_call_sync(proxy, "SmartGetAttributes",
+ g_variant_new_tuple(&options, 1),
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+ g_object_unref(proxy);
+
+ if (error != NULL){
+ g_error_free (error);
+ g_object_unref(proxy);
+ return NULL;
+ }
+
+ v2 = g_variant_get_child_value(v, 0);
+ iter = g_variant_iter_new(v2);
+
+ // id(y), identifier(s), flags(q), value(i), worst(i), threshold(i),
+ // pretty(x), pretty_unit(i), expansion(a{sv})
+ // pretty_unit = 0 (unknown), 1 (dimensionless), 2 (milliseconds), 3 (sectors), 4 (millikelvin).
+ while (g_variant_iter_loop (iter, "(ysqiiixia{sv})", &aid, &aidenf, NULL, &avalue,
+ &aworst, &athreshold, &pretty, &pretty_unit, NULL)){
+ p = udisksa_new();
+ p->id = aid;
+ p->identifier = g_strdup(aidenf);
+ p->value = avalue;
+ p->worst = aworst;
+ p->threshold = athreshold;
+ switch (pretty_unit){
+ case 1:
+ p->interpreted_unit = UDSK_INTPVAL_DIMENSIONLESS;
+ p->interpreted = pretty;
+ break;
+ case 2:
+ if (pretty > 1000*60*60){ // > 1h
+ p->interpreted_unit = UDSK_INTPVAL_HOURS;
+ p->interpreted = pretty / (1000*60*60);
+ }
+ else{
+ p->interpreted_unit = UDSK_INTPVAL_MILISECONDS;
+ p->interpreted = pretty;
+ }
+ break;
+ case 3:
+ p->interpreted_unit = UDSK_INTPVAL_SECTORS;
+ p->interpreted = pretty;
+ break;
+ case 4:
+ p->interpreted_unit = UDSK_INTPVAL_CELSIUS;
+ p->interpreted = (pretty - 273150) / 1000; //mK to °C
+ break;
+ default:
+ p->interpreted_unit = UDSK_INTPVAL_SKIP;
+ p->interpreted = -1;
+ break;
+ }
+ p->next = NULL;
+
+ if (lastp == NULL)
+ dsk->smart_attributes = p;
+ else
+ lastp->next = p;
+
+ lastp = p;
+ }
+ g_variant_iter_free (iter);
+ g_variant_unref(v2);
+ g_variant_unref(v);
+
+ return NULL;
+}
+
+gpointer get_udisks2_drive_info(const char *blockdev, GDBusProxy *block,
+ GDBusProxy *drive, const char *drivepath) {
+ GVariant *v;
+ GVariantIter *iter;
+ const gchar *str, *part;
+ udiskd *u = NULL;
+ udiskp **p = NULL;
+ gsize n, i;
+ u = udiskd_new();
+ u->block_dev = g_strdup(blockdev);
+
+ v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Model");
+ if (v){
+ u->model = g_variant_dup_string(v, NULL);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Vendor");
+ if (v){
+ u->vendor = g_variant_dup_string(v, NULL);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Revision");
+ if (v){
+ u->revision = g_variant_dup_string(v, NULL);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Serial");
+ if (v){
+ u->serial = g_variant_dup_string(v, NULL);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "WWN");
+ if (v){
+ str = g_variant_get_string(v, NULL);
+ if (g_str_has_prefix(str, "0x")) {
+ u->wwid = g_strdup_printf("nna.%s", str+2);
+ }
+ else if (g_str_has_prefix(str, "nna.") || g_str_has_prefix(str, "eui.")) {
+ u->wwid = g_strdup(str);
+ }
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "ConnectionBus");
+ if (v){
+ u->connection_bus = g_variant_dup_string(v, NULL);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "RotationRate");
+ if (v){
+ u->rotation_rate = g_variant_get_int32(v);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Size");
+ if (v){
+ u->size = g_variant_get_uint64(v);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Media");
+ if (v){
+ str = g_variant_get_string(v, NULL);
+ if (strcmp(str, "") != 0) {
+ u->media = g_strdup(str);
+ }
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "MediaCompatibility");
+ if (v){
+ g_variant_get(v, "as", &iter);
+ n = g_variant_iter_n_children(iter);
+ u->media_compatibility = g_malloc0_n(n + 1, sizeof(gchar*));
+
+ i = 0;
+ while (g_variant_iter_loop (iter, "s", &str) && i < n){
+ u->media_compatibility[i] = g_strdup(str);
+ i++;
+ }
+
+ g_variant_iter_free (iter);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Ejectable");
+ if (v){
+ u->ejectable = g_variant_get_boolean(v);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_INTERFACE, "Removable");
+ if (v){
+ u->removable = g_variant_get_boolean(v);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "PmSupported");
+ if (v){
+ u->pm_supported = g_variant_get_boolean(v);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "ApmSupported");
+ if (v){
+ u->apm_supported = g_variant_get_boolean(v);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "AamSupported");
+ if (v){
+ u->aam_supported = g_variant_get_boolean(v);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartSupported");
+ if (v){
+ u->smart_supported = g_variant_get_boolean(v);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartEnabled");
+ if (v){
+ u->smart_enabled = g_variant_get_boolean(v);
+ g_variant_unref(v);
+ }
+ if (u->smart_enabled){
+ v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartPowerOnSeconds");
+ if (v){
+ u->smart_poweron = g_variant_get_uint64(v);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartNumBadSectors");
+ if (v){
+ u->smart_bad_sectors = g_variant_get_int64(v);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartTemperature");
+ if (v){
+ u->smart_temperature = (gint) (g_variant_get_double(v) - 273.15);
+ g_variant_unref(v);
+ }
+ v = get_dbus_property(drive, UDISKS2_DRIVE_ATA_INTERFACE, "SmartFailing");
+ if (v){
+ u->smart_failing = g_variant_get_boolean(v);
+ g_variant_unref(v);
+ }
+ get_udisks2_smart_attributes(u, drivepath);
+ }
+
+ v = get_dbus_property(block, UDISKS2_PART_TABLE_INTERFACE, "Type");
+ if (v){
+ u->partition_table = g_variant_dup_string(v, NULL);
+ g_variant_unref(v);
+ }
+ // 'Partitions' property is available in udisks2 version 2.7.2 or newer
+ v = get_dbus_property(block, UDISKS2_PART_TABLE_INTERFACE, "Partitions");
+ if (v){
+ g_variant_get(v, "ao", &iter);
+
+ p = &(u->partitions);
+ while (g_variant_iter_loop (iter, "o", &str)) {
+ *p = get_udisks2_partition_info(str);
+ if (*p != NULL){
+ p = &((*p)->next);
+ }
+ }
+
+ g_variant_iter_free (iter);
+ g_variant_unref(v);
+ }
+
+ return u;
+}
+
+GSList* get_udisks2_temps(void){
+ return udisks2_drives_func_caller(udisks2_conn, get_udisks2_temp);
+}
+
+GSList* get_udisks2_all_drives_info(void){
+ return udisks2_drives_func_caller(udisks2_conn, get_udisks2_drive_info);
+}
+
+void udisks2_init(){
+ if (udisks2_conn == NULL){
+ //FIXME udisks2_conn = get_udisks2_connection();
+ }
+}
+
+void udisks2_shutdown(){
+ if (udisks2_conn != NULL){
+ g_object_unref(udisks2_conn);
+ udisks2_conn = NULL;
+ }
+}
diff --git a/hardinfo2/usb_util.c b/hardinfo2/usb_util.c
new file mode 100644
index 00000000..744f084f
--- /dev/null
+++ b/hardinfo2/usb_util.c
@@ -0,0 +1,557 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br>
+ * This file
+ * Copyright (C) 2017 Burt P. <pburt0@gmail.com>
+ * Copyright (C) 2019 Ondrej ÄŒerman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "usb_util.h"
+#include "util_ids.h"
+
+#define SYSFS_DIR_USB_DEVICES "/sys/bus/usb/devices"
+
+gchar *usb_ids_file = NULL;
+
+usbi *usbi_new() {
+ return g_new0(usbi, 1);
+}
+
+void usbi_free(usbi *s) {
+ if (s) {
+ g_free(s->if_label);
+ g_free(s->if_class_str);
+ g_free(s->if_subclass_str);
+ g_free(s->if_protocol_str);
+ g_free(s->driver);
+ g_free(s);
+ }
+}
+
+void usbi_list_free(usbi *s) {
+ usbi *n;
+ while(s != NULL) {
+ n = s->next;
+ usbi_free(s);
+ s = n;
+ }
+}
+
+usbd *usbd_new() {
+ return g_new0(usbd, 1);
+}
+
+void usbd_free(usbd *s) {
+ if (s) {
+ usbi_list_free(s->if_list);
+ vendor_list_free(s->vendors);
+ g_free(s->vendor);
+ g_free(s->product);
+ g_free(s->manufacturer);
+ g_free(s->device);
+ g_free(s->usb_version);
+ g_free(s->device_version);
+ g_free(s->serial);
+ g_free(s->dev_class_str);
+ g_free(s->dev_subclass_str);
+ g_free(s->dev_protocol_str);
+ g_free(s);
+ }
+}
+
+void usbd_list_free(usbd *s) {
+ usbd *n;
+ while(s != NULL) {
+ n = s->next;
+ usbd_free(s);
+ s = n;
+ }
+}
+
+/* returns number of items after append */
+static int usbd_list_append(usbd *l, usbd *n) {
+ int c = 0;
+ while(l != NULL) {
+ c++;
+ if (l->next == NULL) {
+ if (n != NULL) {
+ l->next = n;
+ c++;
+ }
+ break;
+ }
+ l = l->next;
+ }
+ return c;
+}
+
+/* returns new top of the list */
+static usbd *usbd_list_append_sorted(usbd *l, usbd *n) {
+ usbd *b = NULL;
+ usbd *t = l;
+
+ if (n == NULL)
+ return l;
+
+ while(l != NULL) {
+ if ((n->bus > l->bus) ||
+ ((n->bus == l->bus) && (n->dev >= l->dev))) {
+ if (l->next == NULL) {
+ l->next = n;
+ break;
+ }
+ b = l;
+ l = l->next;
+ }
+ else {
+ if (b == NULL) {
+ t = n;
+ n->next = l;
+ }
+ else {
+ b->next = n;
+ n->next = l;
+ }
+ break;
+ }
+ }
+
+ return t;
+}
+
+gboolean usbd_append_interface(usbd *dev, usbi *new_if){
+ usbi *curr_if;
+ if (dev->if_list == NULL){
+ dev->if_list = new_if;
+ return TRUE;
+ }
+
+ if (dev->if_list->if_number == new_if->if_number)
+ return FALSE;
+
+ curr_if = dev->if_list;
+ while(curr_if->next != NULL) {
+ curr_if = curr_if->next;
+ if (curr_if->if_number == new_if->if_number)
+ return FALSE;
+ }
+ curr_if->next = new_if;
+ return TRUE;
+}
+
+int usbd_list_count(usbd *s) {
+ return usbd_list_append(s, NULL);
+}
+
+static char *lsusb_line_value(char *line, const char *prefix) {
+ if (g_str_has_prefix(line, prefix)) {
+ line += strlen(prefix) + 1;
+ return g_strstrip(line);
+ } else
+ return NULL;
+}
+
+static gboolean usb_get_device_lsusb(int bus, int dev, usbd *s) {
+ gboolean spawned;
+ gchar *out, *err, *p, *l, *t, *next_nl;
+ gchar *lsusb_cmd = g_strdup_printf("lsusb -s %d:%d -v", bus, dev);
+ usbi *curr_if = NULL; // do not free
+
+ s->bus = bus;
+ s->dev = dev;
+
+ spawned = hardinfo_spawn_command_line_sync(lsusb_cmd,
+ &out, &err, NULL, NULL);
+ g_free(lsusb_cmd);
+ if (spawned) {
+ if (strstr(err, "information will be missing")) {
+ s->user_scan = TRUE;
+ }
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ g_strstrip(p);
+
+ // device info
+ if (l = lsusb_line_value(p, "idVendor")) {
+ s->vendor_id = strtol(l, NULL, 0);
+ if (t = strchr(l, ' '))
+ s->vendor = g_strdup(t + 1);
+ } else if (l = lsusb_line_value(p, "idProduct")) {
+ s->product_id = strtol(l, NULL, 0);
+ if (t = strchr(l, ' '))
+ s->product = g_strdup(t + 1);
+ } else if (l = lsusb_line_value(p, "MaxPower")) {
+ s->max_curr_ma = atoi(l);
+ } else if (l = lsusb_line_value(p, "bcdUSB")) {
+ s->usb_version = g_strdup(l);
+ } else if (l = lsusb_line_value(p, "bcdDevice")) {
+ s->device_version = g_strdup(l);
+ } else if (l = lsusb_line_value(p, "bDeviceClass")) {
+ s->dev_class = atoi(l);
+ if (t = strchr(l, ' '))
+ s->dev_class_str = g_strdup(t + 1);
+ } else if (l = lsusb_line_value(p, "bDeviceSubClass")) {
+ s->dev_subclass = atoi(l);
+ if (t = strchr(l, ' '))
+ s->dev_subclass_str = g_strdup(t + 1);
+ } else if (l = lsusb_line_value(p, "bDeviceProtocol")) {
+ s->dev_protocol = atoi(l);
+ if (t = strchr(l, ' '))
+ s->dev_protocol_str = g_strdup(t + 1);
+ } else if (l = lsusb_line_value(p, "iManufacturer")) {
+ if (t = strchr(l, ' '))
+ s->manufacturer = g_strdup(t + 1);
+ } else if (l = lsusb_line_value(p, "iProduct")) {
+ if (t = strchr(l, ' '))
+ s->device = g_strdup(t + 1);
+ } else if (l = lsusb_line_value(p, "iSerial")) {
+ if (t = strchr(l, ' '))
+ s->serial = g_strdup(t + 1);
+
+ // interface info
+ } else if (l = lsusb_line_value(p, "bInterfaceNumber")) {
+ curr_if = usbi_new();
+ curr_if->if_number = atoi(l);
+
+ // This may parse same interface multiple times when there
+ // are multiple Configuration descriptors per device
+ // As a workaround usbd_append_interface appends interface
+ // with same if_number only once
+ if (!usbd_append_interface(s, curr_if)){
+ usbi_free(curr_if);
+ curr_if = NULL;
+ }
+ } else if (l = lsusb_line_value(p, "bInterfaceClass")) {
+ if (curr_if != NULL){
+ curr_if->if_class = atoi(l);
+ if (t = strchr(l, ' '))
+ curr_if->if_class_str = g_strdup(t + 1);
+ }
+ } else if (l = lsusb_line_value(p, "bInterfaceSubClass")) {
+ if (curr_if != NULL){
+ curr_if->if_subclass = atoi(l);
+ if (t = strchr(l, ' '))
+ curr_if->if_subclass_str = g_strdup(t + 1);
+ }
+ } else if (l = lsusb_line_value(p, "bInterfaceProtocol")) {
+ if (curr_if != NULL){
+ curr_if->if_protocol = atoi(l);
+ if (t = strchr(l, ' '))
+ curr_if->if_protocol_str = g_strdup(t + 1);
+ }
+ } else if (l = lsusb_line_value(p, "iInterface")) {
+ if (curr_if != NULL){
+ if (t = strchr(l, ' '))
+ curr_if->if_label = g_strdup(t + 1);
+ }
+ }
+
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean usb_get_interface_sysfs(int conf, int number,
+ const char* devpath, usbi *intf){
+ gchar *ifpath, *drvpath, *tmp;
+ ids_query_result result;// = {};
+
+ memset(&result,0,sizeof(ids_query_result));
+
+ ifpath = g_strdup_printf("%s:%d.%d", devpath, conf, number);
+ if (!g_file_test(ifpath, G_FILE_TEST_EXISTS)){
+ return FALSE;
+ }
+
+ tmp = g_strdup_printf("%s/driver", ifpath);
+ drvpath = g_file_read_link(tmp, NULL);
+ g_free(tmp);
+ if (drvpath){
+ intf->driver = g_path_get_basename(drvpath);
+ g_free(drvpath);
+ }
+ else{
+ intf->driver = g_strdup(_("(None)"));
+ }
+
+ intf->if_number = number;
+ intf->if_class = h_sysfs_read_hex(ifpath, "bInterfaceClass");
+ intf->if_subclass = h_sysfs_read_hex(ifpath, "bInterfaceSubClass");
+ intf->if_protocol = h_sysfs_read_hex(ifpath, "bInterfaceProtocol");
+
+ if (intf->if_label == NULL)
+ intf->if_label = h_sysfs_read_string(ifpath, "interface");
+
+ if (intf->if_class_str == NULL && intf->if_subclass_str == NULL
+ && intf->if_protocol_str == NULL) {
+ tmp = g_strdup_printf("C %02x/%02x/%02x", intf->if_class,
+ intf->if_subclass, intf->if_protocol);
+ scan_ids_file(usb_ids_file, tmp, &result, -1);
+ if (result.results[0])
+ intf->if_class_str = g_strdup(result.results[0]);
+ if (result.results[1])
+ intf->if_subclass_str = g_strdup(result.results[1]);
+ if (result.results[2])
+ intf->if_protocol_str = g_strdup(result.results[2]);
+ g_free(tmp);
+ }
+
+ g_free(ifpath);
+ return TRUE;
+}
+
+static void find_usb_ids_file() {
+ if (usb_ids_file) return;
+ char *file_search_order[] = {
+ g_build_filename(g_get_user_config_dir(), "hardinfo2", "usb.ids", NULL),
+ g_build_filename(params.path_data, "usb.ids", NULL),
+ NULL
+ };
+ int n;
+ for(n = 0; file_search_order[n]; n++) {
+ if (!usb_ids_file && !access(file_search_order[n], R_OK))
+ usb_ids_file = file_search_order[n];
+ else
+ g_free(file_search_order[n]);
+ }
+}
+
+void usb_lookup_ids_vendor_product_str(gint vendor_id, gint product_id,
+ gchar **vendor_str, gchar **product_str) {
+ ids_query_result result;// = {};
+ gchar *qpath;
+
+ memset(&result,0,sizeof(ids_query_result));
+ if (!usb_ids_file)
+ find_usb_ids_file();
+ if (!usb_ids_file)
+ return;
+
+ qpath = g_strdup_printf("%04x/%04x", vendor_id, product_id);
+ scan_ids_file(usb_ids_file, qpath, &result, -1);
+ if (result.results[0])
+ *vendor_str = g_strdup(result.results[0]);
+ if (result.results[1])
+ *product_str = g_strdup(result.results[1]);
+ g_free(qpath);
+}
+
+static gboolean usb_get_device_sysfs(int bus, int dev, const char* sysfspath, usbd *s) {
+ usbi *intf;
+ gboolean ok;
+ int i, if_count = 0, conf = 0, ver;
+ ids_query_result result;// = {};
+ gchar *qpath;
+
+ memset(&result,0,sizeof(ids_query_result));
+ if (sysfspath == NULL)
+ return FALSE;
+
+ if (!usb_ids_file)
+ find_usb_ids_file();
+
+ s->bus = bus;
+ s->dev = dev;
+ s->dev_class = h_sysfs_read_hex(sysfspath, "bDeviceClass");
+ s->vendor_id = h_sysfs_read_hex(sysfspath, "idVendor");
+ s->product_id = h_sysfs_read_hex(sysfspath, "idProduct");
+ s->manufacturer = h_sysfs_read_string(sysfspath, "manufacturer");
+ s->device = h_sysfs_read_string(sysfspath, "product");
+ s->max_curr_ma = h_sysfs_read_int(sysfspath, "bMaxPower");
+ s->dev_class = h_sysfs_read_hex(sysfspath, "bDeviceClass");
+ s->dev_subclass = h_sysfs_read_hex(sysfspath, "bDeviceSubClass");
+ s->dev_protocol = h_sysfs_read_hex(sysfspath, "bDeviceProtocol");
+ s->speed_mbs = h_sysfs_read_int(sysfspath, "speed");
+
+ if (s->product == NULL && s->vendor == NULL) {
+ qpath = g_strdup_printf("%04x/%04x", s->vendor_id, s->product_id);
+ scan_ids_file(usb_ids_file, qpath, &result, -1);
+ if (result.results[0])
+ s->vendor = g_strdup(result.results[0]);
+ if (result.results[1])
+ s->product = g_strdup(result.results[1]);
+ g_free(qpath);
+ }
+
+ if (s->dev_class_str == NULL && s->dev_subclass_str == NULL
+ && s->dev_protocol_str == NULL) {
+ qpath = g_strdup_printf("C %02x/%02x/%02x", s->dev_class,
+ s->dev_subclass, s->dev_protocol);
+ scan_ids_file(usb_ids_file, qpath, &result, -1);
+ if (result.results[0])
+ s->dev_class_str = g_strdup(result.results[0]);
+ if (result.results[1])
+ s->dev_subclass_str = g_strdup(result.results[1]);
+ if (result.results[2])
+ s->dev_protocol_str = g_strdup(result.results[2]);
+ g_free(qpath);
+ }
+
+ conf = h_sysfs_read_hex(sysfspath, "bConfigurationValue");
+
+ if (s->usb_version == NULL)
+ s->usb_version = h_sysfs_read_string(sysfspath, "version");
+
+ if (s->serial == NULL)
+ s->serial = h_sysfs_read_string(sysfspath, "serial");
+
+ if (s->device_version == NULL) {
+ ver = h_sysfs_read_int(sysfspath, "bcdDevice");
+ if (ver > 0){
+ s->device_version = g_strdup_printf("%d.%02d", ver/100, ver%100);
+ }
+ }
+
+ if (s->if_list == NULL){ // create interfaces list
+ if_count = h_sysfs_read_int(sysfspath, "bNumInterfaces");
+ for (i = 0; i <= if_count; i++){
+ intf = usbi_new();
+ ok = usb_get_interface_sysfs(conf, i, sysfspath, intf);
+ if (ok){
+ if (!usbd_append_interface(s, intf)){
+ usbi_free(intf);
+ }
+ }
+ else{
+ usbi_free(intf);
+ }
+ }
+ }
+ else{ // improve existing list
+ intf = s->if_list;
+ while (intf){
+ usb_get_interface_sysfs(conf, intf->if_number, sysfspath, intf);
+ intf = intf->next;
+ }
+ }
+
+ return TRUE;
+}
+
+usbd *usb_get_device(int bus, int dev, const gchar* sysfspath) {
+ usbd *s = usbd_new();
+ int ok = 0;
+ if (s) {
+ /* try sysfs */
+ ok = usb_get_device_sysfs(bus, dev, sysfspath, s);
+ if (!ok) {
+ /* try lsusb */
+ ok = usb_get_device_lsusb(bus, dev, s);
+ }
+ if (!ok) {
+ usbd_free(s);
+ s = NULL;
+ }
+ }
+ return s;
+}
+
+static usbd *usb_get_device_list_lsusb() {
+ gboolean spawned;
+ gchar *out, *err, *p, *next_nl;
+ usbd *head = NULL, *nd;
+ int bus, dev, vend, prod, ec;
+
+ spawned = hardinfo_spawn_command_line_sync("lsusb",
+ &out, &err, NULL, NULL);
+ if (spawned) {
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ ec = sscanf(p, "Bus %d Device %d: ID %x:%x", &bus, &dev, &vend, &prod);
+ if (ec == 4) {
+ nd = usb_get_device(bus, dev, NULL);
+ if (head == NULL) {
+ head = nd;
+ } else {
+ usbd_list_append(head, nd);
+ }
+ }
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ }
+ return head;
+}
+
+static usbd *usb_get_device_list_sysfs() {
+ GDir *dir;
+ GRegex *regex;
+ GMatchInfo *match_info;
+ usbd *head = NULL, *nd;
+ gchar *devpath;
+ const char *entry;
+ int bus, dev;
+
+ regex = g_regex_new("^([0-9]+-[0-9]+([.][0-9]+)*)|(usb[0-9]+)$", 0, 0, NULL);
+ if (!regex){
+ return NULL;
+ }
+
+ dir = g_dir_open(SYSFS_DIR_USB_DEVICES, 0, NULL);
+ if (!dir){
+ return NULL;
+ }
+
+ while ((entry = g_dir_read_name(dir))) {
+ g_regex_match(regex, entry, 0, &match_info);
+
+ if (g_match_info_matches(match_info)) {
+ devpath = g_build_filename(SYSFS_DIR_USB_DEVICES, entry, NULL);
+ bus = h_sysfs_read_int(devpath, "busnum");
+ dev = h_sysfs_read_int(devpath, "devnum");
+
+ if (bus > 0 && dev > 0){
+ nd = usb_get_device(bus, dev, devpath);
+ if (head == NULL) {
+ head = nd;
+ } else {
+ head = usbd_list_append_sorted(head, nd);
+ }
+ }
+ g_free(devpath);
+ }
+ g_match_info_free(match_info);
+ }
+
+ g_dir_close(dir);
+ g_regex_unref(regex);
+
+ return head;
+}
+
+usbd *usb_get_device_list() {
+ usbd *lst, *l;
+
+ lst = usb_get_device_list_sysfs();
+ if (lst == NULL)
+ lst = usb_get_device_list_lsusb();
+
+ l = lst;
+ while(l) {
+ l->vendors = vendor_list_remove_duplicates_deep(vendors_match(l->vendor, l->manufacturer, NULL));
+ l = l->next;
+ }
+
+ return lst;
+}
diff --git a/hardinfo2/util.c b/hardinfo2/util.c
new file mode 100644
index 00000000..e12963c0
--- /dev/null
+++ b/hardinfo2/util.c
@@ -0,0 +1,1452 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2007 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Functions h_strdup_cprintf and h_strconcat are based on GLib version 2.4.6
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <config.h>
+
+#include <report.h>
+#include <string.h>
+#include <shell.h>
+#include <iconcache.h>
+#include <hardinfo.h>
+#include <gtk/gtk.h>
+
+#include <binreloc.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#define KiB 1024
+#define MiB 1048576
+#define GiB 1073741824
+#define TiB 1099511627776
+#define PiB 1125899906842624
+
+static GSList *modules_list = NULL;
+
+void sync_manager_clear_entries(void);
+
+gchar *find_program(gchar *program_name)
+{
+ int i;
+ char *temp;
+ static GHashTable *cache = NULL;
+ const char *path[] = { "/usr/local/bin", "/usr/local/sbin",
+ "/usr/bin", "/usr/sbin",
+ "/bin", "/sbin",
+ NULL };
+
+ /* we don't need to call stat() every time: cache the results */
+ if (!cache) {
+ cache = g_hash_table_new(g_str_hash, g_str_equal);
+ } else if ((temp = g_hash_table_lookup(cache, program_name))) {
+ return g_strdup(temp);
+ }
+
+ for (i = 0; path[i]; i++) {
+ temp = g_build_filename(path[i], program_name, NULL);
+
+ if (g_file_test(temp, G_FILE_TEST_IS_EXECUTABLE)) {
+ g_hash_table_insert(cache, program_name, g_strdup(temp));
+ return temp;
+ }
+
+ g_free(temp);
+ }
+
+ /* our search has failed; use GLib's search (which uses $PATH env var) */
+ if ((temp = g_find_program_in_path(program_name))) {
+ g_hash_table_insert(cache, program_name, g_strdup(temp));
+ return temp;
+ }
+
+ return NULL;
+}
+
+gchar *seconds_to_string(unsigned int seconds)
+{
+ unsigned int hours, minutes, days;
+ const gchar *days_fmt, *hours_fmt, *minutes_fmt, *seconds_fmt;
+ gchar *full_fmt, *ret = g_strdup("");
+
+ minutes = seconds / 60;
+ seconds %= 60;
+ hours = minutes / 60;
+ minutes %= 60;
+ days = hours / 24;
+ hours %= 24;
+
+ days_fmt = ngettext("%d day", "%d days", days);
+ hours_fmt = ngettext("%d hour", "%d hours", hours);
+ minutes_fmt = ngettext("%d minute", "%d minutes", minutes);
+ seconds_fmt = ngettext("%d second", "%d seconds", seconds);
+
+ if (days) {
+ full_fmt = g_strdup_printf("%s %s %s %s", days_fmt, hours_fmt, minutes_fmt, seconds_fmt);
+ ret = g_strdup_printf(full_fmt, days, hours, minutes, seconds);
+ } else if (hours) {
+ full_fmt = g_strdup_printf("%s %s %s", hours_fmt, minutes_fmt, seconds_fmt);
+ ret = g_strdup_printf(full_fmt, hours, minutes, seconds);
+ } else if (minutes) {
+ full_fmt = g_strdup_printf("%s %s", minutes_fmt, seconds_fmt);
+ ret = g_strdup_printf(full_fmt, minutes, seconds);
+ } else {
+ ret = g_strdup_printf(seconds_fmt, seconds);
+ }
+ g_free(full_fmt);
+ return ret;
+}
+
+gchar *size_human_readable(gfloat size)
+{
+ if (size < KiB)
+ return g_strdup_printf(_("%.1f B"), size);
+ if (size < MiB)
+ return g_strdup_printf(_("%.1f KiB"), size / KiB);
+ if (size < GiB)
+ return g_strdup_printf(_("%.1f MiB"), size / MiB);
+ if (size < TiB)
+ return g_strdup_printf(_("%.1f GiB"), size / GiB);
+ if (size < PiB)
+ return g_strdup_printf(_("%.1f TiB"), size / TiB);
+
+ return g_strdup_printf(_("%.1f PiB"), size / PiB);
+}
+
+char *strend(gchar * str, gchar chr)
+{
+ if (!str)
+ return NULL;
+
+ char *p;
+ if ((p = g_utf8_strchr(str, -1, chr)))
+ *p = 0;
+
+ return str;
+}
+
+void remove_quotes(gchar * str)
+{
+ if (!str)
+ return;
+
+ while (*str == '"')
+ *(str++) = ' ';
+
+ strend(str, '"');
+}
+
+void remove_linefeed(gchar * str)
+{
+ strend(str, '\n');
+}
+
+void widget_set_cursor(GtkWidget * widget, GdkCursorType cursor_type)
+{
+ GdkCursor *cursor;
+ GdkDisplay *display;
+ GdkWindow *gdk_window;
+
+ display = gtk_widget_get_display(widget);
+ cursor = gdk_cursor_new_for_display(display, cursor_type);
+ gdk_window = gtk_widget_get_window(widget);
+ if (cursor) {
+ gdk_window_set_cursor(gdk_window, cursor);
+ gdk_display_flush(display);
+#if GTK_CHECK_VERSION(3, 0, 0)
+ g_object_unref(cursor);
+#else
+ gdk_cursor_unref(cursor);
+#endif
+ }
+
+ while (gtk_events_pending())
+ gtk_main_iteration();
+}
+
+static gboolean __nonblock_cb(gpointer data)
+{
+ gtk_main_quit();
+ return FALSE;
+}
+
+void nonblock_sleep(guint msec)
+{
+ g_timeout_add(msec, (GSourceFunc) __nonblock_cb, NULL);
+ gtk_main();
+}
+
+static void __expand_cb(GtkWidget * widget, gpointer data)
+{
+ if (GTK_IS_EXPANDER(widget)) {
+ gtk_expander_set_expanded(GTK_EXPANDER(widget), TRUE);
+ } else if (GTK_IS_CONTAINER(widget)) {
+ gtk_container_foreach(GTK_CONTAINER(widget),
+ (GtkCallback) __expand_cb, NULL);
+ }
+}
+
+void file_chooser_open_expander(GtkWidget * chooser)
+{
+ gtk_container_foreach(GTK_CONTAINER(chooser),
+ (GtkCallback) __expand_cb, NULL);
+}
+
+void file_chooser_add_filters(GtkWidget * chooser, FileTypes * filters)
+{
+ GtkFileFilter *filter;
+ gint i;
+
+ for (i = 0; filters[i].name; i++) {
+ filter = gtk_file_filter_new();
+ gtk_file_filter_add_mime_type(filter, filters[i].mime_type);
+ gtk_file_filter_set_name(filter, filters[i].name);
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(chooser), filter);
+ }
+}
+
+gchar *file_chooser_get_extension(GtkWidget * chooser, FileTypes * filters)
+{
+ GtkFileFilter *filter;
+ const gchar *filter_name;
+ gint i;
+
+ filter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(chooser));
+ filter_name = gtk_file_filter_get_name(filter);
+ for (i = 0; filters[i].name; i++) {
+ if (g_str_equal(filter_name, filters[i].name)) {
+ return filters[i].extension;
+ }
+ }
+
+ return NULL;
+}
+
+gpointer file_types_get_data_by_name(FileTypes * filters, gchar * filename)
+{
+ gint i;
+
+ for (i = 0; filters[i].name; i++) {
+ if (g_str_has_suffix(filename, filters[i].extension)) {
+ return filters[i].data;
+ }
+ }
+
+ return NULL;
+}
+
+gchar *file_chooser_build_filename(GtkWidget * chooser, gchar * extension)
+{
+ gchar *filename =
+ gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
+ gchar *retval;
+
+ if (g_str_has_suffix(filename, extension)) {
+ return filename;
+ }
+
+ retval = g_strconcat(filename, extension, NULL);
+ g_free(filename);
+
+ return retval;
+}
+
+gboolean binreloc_init(gboolean try_hardcoded)
+{
+ GError *error = NULL;
+ gchar *tmp;
+
+ DEBUG("initializing binreloc (hardcoded = %d)", try_hardcoded);
+
+ /* If the runtime data directories we previously found, don't even try
+ to find them again. */
+ if (params.path_data && params.path_lib) {
+ DEBUG("data and lib path already found.");
+ return TRUE;
+ }
+
+ if (try_hardcoded || !gbr_init(&error)) {
+ /* We were asked to try hardcoded paths or BinReloc failed to initialize. */
+ params.path_data = g_strdup(PREFIX);
+ params.path_lib = g_strdup(LIBPREFIX);
+
+ if (error) {
+ g_error_free(error);
+ }
+
+ DEBUG("%strying hardcoded paths.",
+ try_hardcoded ? "" : "binreloc init failed. ");
+ } else {
+ /* If we were able to initialize BinReloc, build the default data
+ and library paths. */
+ DEBUG("done, trying to use binreloc paths.");
+
+ tmp = gbr_find_data_dir(PREFIX);
+ params.path_data = g_build_filename(tmp, "hardinfo2", NULL);
+ g_free(tmp);
+
+ tmp = gbr_find_lib_dir(PREFIX);
+ params.path_lib = g_build_filename(tmp, "hardinfo2", NULL);
+ g_free(tmp);
+ }
+
+ DEBUG("searching for runtime data on these locations:");
+ DEBUG(" lib: %s", params.path_lib);
+ DEBUG(" data: %s", params.path_data);
+
+ /* Try to see if the benchmark test data file isn't missing. This isn't the
+ definitive test, but it should do okay for most situations. */
+ tmp = g_build_filename(params.path_data, "benchmark.data", NULL);
+ if (!g_file_test(tmp, G_FILE_TEST_EXISTS)) {
+ DEBUG("runtime data not found");
+
+ g_free(params.path_data);
+ g_free(params.path_lib);
+ g_free(tmp);
+
+ params.path_data = params.path_lib = NULL;
+
+ if (try_hardcoded) {
+ /* We tried the hardcoded paths, but still was unable to find the
+ runtime data. Give up. */
+ DEBUG("giving up");
+ return FALSE;
+ } else {
+ /* Even though BinReloc worked OK, the runtime data was not found.
+ Try the hardcoded paths. */
+ DEBUG("trying to find elsewhere");
+ return binreloc_init(TRUE);
+ }
+ }
+ g_free(tmp);
+
+ DEBUG("runtime data found!");
+ /* We found the runtime data; hope everything is fine */
+ return TRUE;
+}
+
+static void
+log_handler(const gchar * log_domain,
+ GLogLevelFlags log_level,
+ const gchar * message, gpointer user_data)
+{
+ if (!params.gui_running) {
+ /* No GUI running: spit the message to the terminal */
+ g_print("\n\n*** %s: %s\n\n",
+ (log_level & G_LOG_FLAG_FATAL) ? _("Error") : _("Warning"),
+ message);
+ } else {
+ /* Hooray! We have a GUI running! */
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new_with_markup(NULL, GTK_DIALOG_MODAL,
+ (log_level &
+ G_LOG_FLAG_FATAL) ?
+ GTK_MESSAGE_ERROR :
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_CLOSE,
+ "<big><b>%s</b></big>\n\n%s",
+ (log_level &
+ G_LOG_FLAG_FATAL) ?
+ _("Fatal Error") :
+ _("Warning"), message);
+
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ }
+}
+
+void parameters_init(int *argc, char ***argv, ProgramParameters * param)
+{
+ static gboolean create_report = FALSE;
+ static gboolean force_all_details = FALSE;
+ static gboolean show_version = FALSE;
+ static gboolean list_modules = FALSE;
+ static gboolean autoload_deps = FALSE;
+ static gboolean skip_benchmarks = FALSE;
+ static gboolean quiet = FALSE;
+ static gchar *report_format = NULL;
+ static gchar *run_benchmark = NULL;
+ static gchar *result_format = NULL;
+ static gchar *bench_user_note = NULL;
+ static gchar **use_modules = NULL;
+ static gint max_bench_results = 50;
+
+ static GOptionEntry options[] = {
+ {
+ .long_name = "quiet",
+ .short_name = 'q',
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &quiet,
+ .description = N_("do not print status messages to standard output")},
+ {
+ .long_name = "generate-report",
+ .short_name = 'r',
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &create_report,
+ .description = N_("creates a report and prints to standard output")},
+ {
+ .long_name = "report-format",
+ .short_name = 'f',
+ .arg = G_OPTION_ARG_STRING,
+ .arg_data = &report_format,
+ .description = N_("chooses a report format ([text], html)")},
+ {
+ .long_name = "run-benchmark",
+ .short_name = 'b',
+ .arg = G_OPTION_ARG_STRING,
+ .arg_data = &run_benchmark,
+ .description = N_("run benchmark; requires benchmark.so to be loaded")},
+ {
+ .long_name = "user-note",
+ .short_name = 'u',
+ .arg = G_OPTION_ARG_STRING,
+ .arg_data = &bench_user_note,
+ .description = N_("note attached to benchmark results")},
+ {
+ .long_name = "result-format",
+ .short_name = 'g',
+ .arg = G_OPTION_ARG_STRING,
+ .arg_data = &result_format,
+ .description = N_("benchmark result format ([short], conf, shell)")},
+ {
+ .long_name = "max-results",
+ .short_name = 'n',
+ .arg = G_OPTION_ARG_INT,
+ .arg_data = &max_bench_results,
+ .description = N_("maximum number of benchmark results to include (-1 for no limit, default is 50)")},
+//#if NOT_DEFINED
+ {
+ .long_name = "list-modules",
+ .short_name = 'l',
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &list_modules,
+ .description = N_("lists modules")},
+ {
+ .long_name = "load-module",
+ .short_name = 'm',
+ .arg = G_OPTION_ARG_STRING_ARRAY,
+ .arg_data = &use_modules,
+ .description = N_("specify module to load")},
+ {
+ .long_name = "autoload-deps",
+ .short_name = 'a',
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &autoload_deps,
+ .description = N_("automatically load module dependencies")},
+//#endif
+ {
+ .long_name = "version",
+ .short_name = 'v',
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &show_version,
+ .description = N_("shows program version and quit")},
+ {
+ .long_name = "skip-benchmarks",
+ .short_name = 's',
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &skip_benchmarks,
+ .description = N_("do not run benchmarks")},
+ {
+ .long_name = "very-verbose",
+ .short_name = 'w', /* like -vv */
+ .arg = G_OPTION_ARG_NONE,
+ .arg_data = &force_all_details,
+ .description = N_("show all details")},
+ {NULL}
+ };
+ GOptionContext *ctx;
+
+ ctx = g_option_context_new(_("- System Profiler and Benchmark tool"));
+ g_option_context_set_ignore_unknown_options(ctx, FALSE);
+ g_option_context_set_help_enabled(ctx, TRUE);
+
+ g_option_context_add_main_entries(ctx, options, *(argv)[0]);
+ g_option_context_parse(ctx, argc, argv, NULL);
+
+ g_option_context_free(ctx);
+
+ if (*argc >= 2) {
+ g_print(_("Unrecognized arguments.\n"
+ "Try ``%s --help'' for more information.\n"), *(argv)[0]);
+ exit(1);
+ }
+
+ param->create_report = create_report;
+ param->report_format = REPORT_FORMAT_TEXT;
+ param->show_version = show_version;
+ param->list_modules = list_modules;
+ param->use_modules = use_modules;
+ param->run_benchmark = run_benchmark;
+ param->result_format = result_format;
+ param->max_bench_results = max_bench_results;
+ param->autoload_deps = autoload_deps;
+ param->skip_benchmarks = skip_benchmarks;
+ param->force_all_details = force_all_details;
+ param->quiet = quiet;
+ param->argv0 = *(argv)[0];
+
+ if (report_format) {
+ if (g_str_equal(report_format, "html"))
+ param->report_format = REPORT_FORMAT_HTML;
+ if (g_str_equal(report_format, "shell"))
+ param->report_format = REPORT_FORMAT_SHELL;
+ }
+
+ /* clean user note */
+ if (bench_user_note) {
+ char *p = NULL;
+ while(p = strchr(bench_user_note, ';')) { *p = ','; }
+ param->bench_user_note =
+ gg_key_file_parse_string_as_value(bench_user_note, '|');
+ }
+
+ /* html ok?
+ * gui: yes
+ * report html: yes
+ * report text: no
+ * anything else? */
+ param->markup_ok = TRUE;
+ if (param->create_report && param->report_format != REPORT_FORMAT_HTML)
+ param->markup_ok = FALSE;
+
+ // TODO: fmt_opts: FMT_OPT_ATERM, FMT_OPT_HTML, FMT_OPT_PANGO...
+ param->fmt_opts = FMT_OPT_NONE;
+
+ gchar *confdir = g_build_filename(g_get_user_config_dir(), "hardinfo2", NULL);
+ if (!g_file_test(confdir, G_FILE_TEST_EXISTS)) {
+ mkdir(confdir, 0744);
+ }
+ g_free(confdir);
+}
+
+gboolean ui_init(int *argc, char ***argv)
+{
+ DEBUG("initializing gtk+ UI");
+
+ g_set_application_name("HardInfo2");
+ g_log_set_handler(NULL,
+ G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL |
+ G_LOG_LEVEL_ERROR, log_handler, NULL);
+
+ return gtk_init_check(argc, argv);
+}
+
+/* Copyright: Jens Låås, SLU 2002 */
+gchar *strreplacechr(gchar * string, gchar * replace, gchar new_char)
+{
+ gchar *s;
+ for (s = string; *s; s++)
+ if (strchr(replace, *s))
+ *s = new_char;
+
+ return string;
+}
+
+gchar *strreplace(gchar *string, gchar *replace, gchar *replacement)
+{
+ gchar **tmp, *ret;
+
+ tmp = g_strsplit(string, replace, 0);
+ ret = g_strjoinv(replacement, tmp);
+ g_strfreev(tmp);
+
+ return ret;
+}
+
+static GHashTable *__module_methods = NULL;
+
+static void module_register_methods(ShellModule * module)
+{
+ const ShellModuleMethod *(*get_methods)(void);
+ gchar *method_name;
+
+ if (__module_methods == NULL) {
+ __module_methods = g_hash_table_new(g_str_hash, g_str_equal);
+ }
+
+ if (g_module_symbol
+ (module->dll, "hi_exported_methods", (gpointer)&get_methods)) {
+ const ShellModuleMethod *methods;
+
+ for (methods = get_methods(); methods->name; methods++) {
+ ShellModuleMethod method = *methods;
+ gchar *name = g_path_get_basename(g_module_name(module->dll));
+ gchar *simple_name = strreplace(name, "lib", "");
+
+ strend(simple_name, '.');
+
+ method_name = g_strdup_printf("%s::%s", simple_name, method.name);
+ g_hash_table_insert(__module_methods, method_name,
+ method.function);
+ g_free(name);
+ g_free(simple_name);
+ }
+ }
+
+}
+
+gchar *module_call_method(gchar * method)
+{
+ gchar *(*function) (void);
+
+ if (__module_methods == NULL) {
+ return NULL;
+ }
+
+ function = g_hash_table_lookup(__module_methods, method);
+ return function ? g_strdup(function()) : NULL;
+}
+
+/* FIXME: varargs? */
+gchar *module_call_method_param(gchar * method, gchar * parameter)
+{
+ gchar *(*function) (gchar *param);
+
+ if (__module_methods == NULL) {
+ return NULL;
+ }
+
+ function = g_hash_table_lookup(__module_methods, method);
+ return function ? g_strdup(function(parameter)) : NULL;
+}
+
+static gboolean remove_module_methods(gpointer key, gpointer value, gpointer data)
+{
+ return g_str_has_prefix(key, data);
+}
+
+static void module_unload(ShellModule * module)
+{
+ GSList *entry;
+
+ if (module->dll) {
+ gchar *name;
+
+ if (module->deinit) {
+ DEBUG("cleaning up module \"%s\"", module->name);
+ module->deinit();
+ } else {
+ DEBUG("module \"%s\" does not need cleanup", module->name);
+ }
+
+ name = g_path_get_basename(g_module_name(module->dll));
+ g_hash_table_foreach_remove(__module_methods, remove_module_methods, name);
+
+ g_module_close(module->dll);
+ g_free(name);
+ }
+
+ g_free(module->name);
+ g_object_unref(module->icon);
+
+ for (entry = module->entries; entry; entry = entry->next) {
+ ShellModuleEntry *e = (ShellModuleEntry *)entry->data;
+
+ g_source_remove_by_user_data(e);
+ g_free(e);
+ }
+
+ g_slist_free(module->entries);
+ g_free(module);
+}
+
+
+void module_unload_all(void)
+{
+ Shell *shell;
+ GSList *module, *merge_id;
+
+ shell = shell_get_main_shell();
+
+ sync_manager_clear_entries();
+ shell_clear_timeouts(shell);
+ shell_clear_tree_models(shell);
+ shell_clear_field_updates();
+ shell_set_title(shell, NULL);
+
+ for (module = shell->tree->modules; module; module = module->next) {
+ module_unload((ShellModule *)module->data);
+ }
+
+ for (merge_id = shell->merge_ids; merge_id; merge_id = merge_id->next) {
+ gtk_ui_manager_remove_ui(shell->ui_manager,
+ GPOINTER_TO_INT(merge_id->data));
+ }
+ g_slist_free(shell->tree->modules);
+ g_slist_free(shell->merge_ids);
+
+ shell->merge_ids = NULL;
+ shell->tree->modules = NULL;
+ shell->selected = NULL;
+}
+
+static ShellModule *module_load(gchar * filename)
+{
+ ShellModule *module;
+ gchar *tmp;
+
+ module = g_new0(ShellModule, 1);
+
+ if (params.gui_running) {
+ gchar *tmpicon, *dot, *simple_name;
+
+ tmpicon = g_strdup(filename);
+ dot = g_strrstr(tmpicon, "." G_MODULE_SUFFIX);
+
+ *dot = '\0';
+
+ simple_name = strreplace(tmpicon, "lib", "");
+
+ tmp = g_strdup_printf("%s.png", simple_name);
+ module->icon = icon_cache_get_pixbuf(tmp);
+
+ g_free(tmp);
+ g_free(tmpicon);
+ g_free(simple_name);
+ }
+
+ tmp = g_build_filename(params.path_lib, "modules", filename, NULL);
+ module->dll = g_module_open(tmp, G_MODULE_BIND_LAZY);
+ DEBUG("gmodule resource for ``%s'' is %p (%s)", tmp, module->dll, g_module_error());
+ g_free(tmp);
+
+ if (module->dll) {
+ void (*init) (void);
+ ModuleEntry *(*get_module_entries) (void);
+ gint(*weight_func) (void);
+ gchar *(*name_func) (void);
+ ModuleEntry *entries;
+ gint i = 0;
+
+ if (!g_module_symbol(module->dll, "hi_module_get_entries", (gpointer) & get_module_entries) ||
+ !g_module_symbol(module->dll, "hi_module_get_name", (gpointer) & name_func)) {
+ DEBUG("cannot find needed symbols; is ``%s'' a real module?", filename);
+ goto failed;
+ }
+
+ if (g_module_symbol(module->dll, "hi_module_init", (gpointer) & init)) {
+ DEBUG("initializing module ``%s''", filename);
+ init();
+ }
+
+ g_module_symbol(module->dll, "hi_module_get_weight",
+ (gpointer) & weight_func);
+
+ module->weight = weight_func ? weight_func() : 0;
+ module->name = name_func();
+
+ g_module_symbol(module->dll, "hi_module_get_about",
+ (gpointer) & (module->aboutfunc));
+ g_module_symbol(module->dll, "hi_module_deinit",
+ (gpointer) & (module->deinit));
+ g_module_symbol(module->dll, "hi_module_get_summary",
+ (gpointer) & (module->summaryfunc));
+
+ entries = get_module_entries();
+ while (entries[i].name) {
+ if (*entries[i].name == '#') { i++; continue; } /* skip */
+
+ ShellModuleEntry *entry = g_new0(ShellModuleEntry, 1);
+
+ if (params.gui_running) {
+ entry->icon = icon_cache_get_pixbuf(entries[i].icon);
+ }
+ entry->icon_file = entries[i].icon;
+
+ g_module_symbol(module->dll, "hi_more_info",
+ (gpointer) & (entry->morefunc));
+ g_module_symbol(module->dll, "hi_get_field",
+ (gpointer) & (entry->fieldfunc));
+ g_module_symbol(module->dll, "hi_note_func",
+ (gpointer) & (entry->notefunc));
+
+ entry->name = _(entries[i].name); //gettext unname N_() in computer.c line 67 etc...
+ entry->scan_func = entries[i].scan_callback;
+ entry->func = entries[i].callback;
+ entry->number = i;
+ entry->flags = entries[i].flags;
+
+ module->entries = g_slist_append(module->entries, entry);
+
+ i++;
+ }
+
+ DEBUG("registering methods for module ``%s''", filename);
+ module_register_methods(module);
+ } else {
+ DEBUG("cannot g_module_open(``%s''). permission problem?", filename);
+ failed:
+ DEBUG("loading module %s failed: %s", filename, g_module_error());
+
+ g_free(module->name);
+ g_free(module);
+ module = NULL;
+ }
+
+ return module;
+}
+
+static gboolean module_in_module_list(gchar * module, gchar ** module_list)
+{
+ int i = 0;
+
+ if (!module_list)
+ return TRUE;
+
+ for (; module_list[i]; i++) {
+ if (g_str_equal(module_list[i], module))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gint module_cmp(gconstpointer m1, gconstpointer m2)
+{
+ ShellModule *a = (ShellModule *) m1;
+ ShellModule *b = (ShellModule *) m2;
+
+ return a->weight - b->weight;
+}
+
+#if 0
+static void module_entry_free(gpointer data, gpointer user_data)
+{
+ ShellModuleEntry *entry = (ShellModuleEntry *) data;
+
+ if (entry) {
+ g_free(entry->name);
+ g_object_unref(entry->icon);
+
+ g_free(entry);
+ }
+}
+#endif
+
+const ModuleAbout *module_get_about(ShellModule * module)
+{
+ if (module->aboutfunc) {
+ return module->aboutfunc();
+ }
+
+ return NULL;
+}
+
+static GSList *modules_check_deps(GSList * modules)
+{
+ GSList *mm;
+ ShellModule *module;
+
+ for (mm = modules; mm; mm = mm->next) {
+ gchar **(*get_deps) (void);
+ gchar **deps;
+ gint i;
+
+ module = (ShellModule *) mm->data;
+
+ if (g_module_symbol(module->dll, "hi_module_get_dependencies",
+ (gpointer) & get_deps)) {
+ for (i = 0, deps = get_deps(); deps[i]; i++) {
+ GSList *l;
+ ShellModule *m;
+ gboolean found = FALSE;
+
+ for (l = modules; l && !found; l = l->next) {
+ m = (ShellModule *) l->data;
+ gchar *name = g_path_get_basename(g_module_name(m->dll));
+
+ found = g_str_equal(name, deps[i]);
+ g_free(name);
+ }
+
+ if (!found) {
+ if (params.autoload_deps) {
+ ShellModule *mod = module_load(deps[i]);
+
+ if (mod)
+ modules = g_slist_append(modules, mod);
+ modules = modules_check_deps(modules); /* re-check dependencies */
+
+ break;
+ }
+
+ if (params.gui_running) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new(NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ _("Module \"%s\" depends on module \"%s\", load it?"),
+ module->name,
+ deps[i]);
+ gtk_dialog_add_buttons(GTK_DIALOG(dialog),
+ "_No",
+ GTK_RESPONSE_REJECT,
+ "_Open",
+ GTK_RESPONSE_ACCEPT, NULL);
+
+ if (gtk_dialog_run(GTK_DIALOG(dialog)) ==
+ GTK_RESPONSE_ACCEPT) {
+ ShellModule *mod = module_load(deps[i]);
+
+ if (mod)
+ modules = g_slist_prepend(modules, mod);
+ modules = modules_check_deps(modules); /* re-check dependencies */
+ } else {
+ g_error("HardInfo2 cannot run without loading the additional module.");
+ exit(1);
+ }
+
+ gtk_widget_destroy(dialog);
+ } else {
+ g_error(_("Module \"%s\" depends on module \"%s\"."),
+ module->name, deps[i]);
+ }
+ }
+ }
+ }
+ }
+
+ return modules;
+}
+
+
+GSList *modules_get_list()
+{
+ return modules_list;
+}
+
+static GSList *modules_load(gchar ** module_list)
+{
+ GDir *dir;
+ GSList *modules = NULL;
+ ShellModule *module;
+ gchar *filename;
+
+ filename = g_build_filename(params.path_lib, "modules", NULL);
+ dir = g_dir_open(filename, 0, NULL);
+ g_free(filename);
+
+ if (dir) {
+ GList *filenames = NULL;
+ while ((filename = (gchar *)g_dir_read_name(dir))) {
+ if (g_strrstr(filename, "." G_MODULE_SUFFIX) &&
+ module_in_module_list(filename, module_list)) {
+ if (g_strrstr(filename, "devices." G_MODULE_SUFFIX)) {
+ filenames = g_list_prepend(filenames, filename);
+ }
+ else {
+ filenames = g_list_append(filenames, filename);
+ }
+ }
+ }
+ GList* item = NULL;
+ while (item = g_list_first(filenames)) {
+ if (module = module_load((gchar *)item->data)) {
+ modules = g_slist_prepend(modules, module);
+ }
+ filenames = g_list_delete_link(filenames, item);
+ }
+#if GLIB_CHECK_VERSION(2,44,0)
+ //FIXME change this to not use g_steal_pointer
+ g_list_free_full (g_steal_pointer (&filenames), g_object_unref);
+#endif
+ g_dir_close(dir);
+ }
+
+ modules = modules_check_deps(modules);
+
+ if (g_slist_length(modules) == 0) {
+ if (params.use_modules == NULL) {
+ g_error
+ (_("No module could be loaded. Check permissions on \"%s\" and try again."),
+ params.path_lib);
+ } else {
+ g_error
+ (_("No module could be loaded. Please use hardinfo2 -l to list all avai"
+ "lable modules and try again with a valid module list."));
+
+ }
+ }
+
+ modules_list = g_slist_sort(modules, module_cmp);
+ return modules_list;
+}
+
+GSList *modules_load_selected(void)
+{
+ return modules_load(params.use_modules);
+}
+
+GSList *modules_load_all(void)
+{
+ return modules_load(NULL);
+}
+
+gint tree_view_get_visible_height(GtkTreeView * tv)
+{
+ GtkTreePath *path;
+ GdkRectangle rect;
+ GtkTreeIter iter;
+ GtkTreeModel *model = gtk_tree_view_get_model(tv);
+ gint nrows = 1;
+
+ path = gtk_tree_path_new_first();
+ gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, &rect);
+
+ /* FIXME: isn't there any easier way to tell the number of rows? */
+ gtk_tree_model_get_iter_first(model, &iter);
+ do {
+ nrows++;
+ } while (gtk_tree_model_iter_next(model, &iter));
+
+ gtk_tree_path_free(path);
+
+ return nrows * rect.height;
+}
+
+void module_entry_scan_all_except(ModuleEntry * entries, gint except_entry)
+{
+ ModuleEntry entry;
+ gint i = 0;
+ void (*scan_callback) (gboolean reload);
+ gchar *text;
+
+ shell_view_set_enabled(FALSE);
+
+ for (entry = entries[0]; entry.name; entry = entries[++i]) {
+ if (i == except_entry)
+ continue;
+
+ text = g_strdup_printf(_("Scanning: %s..."), _(entry.name));
+ shell_status_update(text);
+ g_free(text);
+
+ if ((scan_callback = entry.scan_callback)) {
+ scan_callback(FALSE);
+ }
+ }
+
+ shell_view_set_enabled(TRUE);
+ shell_status_update(_("Done."));
+}
+
+void module_entry_scan_all(ModuleEntry * entries)
+{
+ module_entry_scan_all_except(entries, -1);
+}
+
+void module_entry_reload(ShellModuleEntry * module_entry)
+{
+
+ if (module_entry->scan_func) {
+ module_entry->scan_func(TRUE);
+ }
+}
+
+void module_entry_scan(ShellModuleEntry * module_entry)
+{
+ if (module_entry->scan_func) {
+ module_entry->scan_func(FALSE);
+ }
+}
+
+gchar *module_entry_get_field(ShellModuleEntry * module_entry, gchar * field)
+{
+ if (module_entry->fieldfunc) {
+ return module_entry->fieldfunc(field);
+ }
+
+ return NULL;
+}
+
+gchar *module_entry_function(ShellModuleEntry * module_entry)
+{
+ if (module_entry->func) {
+ return module_entry->func();
+ }
+
+ return NULL;
+}
+
+gchar *module_entry_get_moreinfo(ShellModuleEntry * module_entry, gchar * field)
+{
+ if (module_entry->morefunc) {
+ return module_entry->morefunc(field);
+ }
+
+ return NULL;
+}
+
+const gchar *module_entry_get_note(ShellModuleEntry * module_entry)
+{
+ if (module_entry->notefunc) {
+ return module_entry->notefunc(module_entry->number);
+ }
+
+ return NULL;
+}
+
+gchar *h_strdup_cprintf(const gchar * format, gchar * source, ...)
+{
+ gchar *buffer, *retn;
+ va_list args;
+
+ va_start(args, source);
+ buffer = g_strdup_vprintf(format, args);
+ va_end(args);
+
+ if (source) {
+ retn = g_strconcat(source, buffer, NULL);
+ g_free(buffer);
+ g_free(source);
+ } else {
+ retn = buffer;
+ }
+
+ return retn;
+}
+
+gchar *h_strconcat(gchar * string1, ...)
+{
+ gsize l;
+ va_list args;
+ gchar *s;
+ gchar *concat;
+ gchar *ptr;
+
+ if (!string1)
+ return NULL;
+
+ l = 1 + strlen(string1);
+ va_start(args, string1);
+ s = va_arg(args, gchar *);
+ while (s) {
+ l += strlen(s);
+ s = va_arg(args, gchar *);
+ }
+ va_end(args);
+
+ concat = g_new(gchar, l);
+ ptr = concat;
+
+ ptr = g_stpcpy(ptr, string1);
+ va_start(args, string1);
+ s = va_arg(args, gchar *);
+ while (s) {
+ ptr = g_stpcpy(ptr, s);
+ s = va_arg(args, gchar *);
+ }
+ va_end(args);
+
+ g_free(string1);
+
+ return concat;
+}
+
+static gboolean h_hash_table_remove_all_true(gpointer key, gpointer data, gpointer user_data)
+{
+ return TRUE;
+}
+
+void
+h_hash_table_remove_all(GHashTable *hash_table)
+{
+ g_hash_table_foreach_remove(hash_table,
+ h_hash_table_remove_all_true,
+ NULL);
+}
+
+gfloat
+h_sysfs_read_float(const gchar *endpoint, const gchar *entry)
+{
+ gchar *tmp, *buffer;
+ gfloat return_value = 0.0f;
+
+ tmp = g_build_filename(endpoint, entry, NULL);
+ if (g_file_get_contents(tmp, &buffer, NULL, NULL))
+ return_value = atof(buffer);
+
+ g_free(tmp);
+ g_free(buffer);
+
+ return return_value;
+}
+
+gint
+h_sysfs_read_int(const gchar *endpoint, const gchar *entry)
+{
+ gchar *tmp, *buffer;
+ gint return_value = 0;
+
+ tmp = g_build_filename(endpoint, entry, NULL);
+ if (g_file_get_contents(tmp, &buffer, NULL, NULL))
+ return_value = atoi(buffer);
+
+ g_free(tmp);
+ g_free(buffer);
+
+ return return_value;
+}
+
+gint
+h_sysfs_read_hex(const gchar *endpoint, const gchar *entry)
+{
+ gchar *tmp, *buffer;
+ gint return_value = 0;
+
+ tmp = g_build_filename(endpoint, entry, NULL);
+ if (g_file_get_contents(tmp, &buffer, NULL, NULL))
+ return_value = (gint) strtoll(buffer, NULL, 16);
+
+ g_free(tmp);
+ g_free(buffer);
+
+ return return_value;
+}
+
+gchar *
+h_sysfs_read_string(const gchar *endpoint, const gchar *entry)
+{
+ gchar *tmp, *return_value;
+
+ tmp = g_build_filename(endpoint, entry, NULL);
+ if (!g_file_get_contents(tmp, &return_value, NULL, NULL)) {
+ g_free(return_value);
+
+ return_value = NULL;
+ } else {
+ return_value = g_strstrip(return_value);
+ }
+
+ g_free(tmp);
+
+ return return_value;
+}
+
+static GHashTable *_moreinfo = NULL;
+
+void
+moreinfo_init(void)
+{
+ if (G_UNLIKELY(_moreinfo)) {
+ DEBUG("moreinfo already initialized");
+ return;
+ }
+ DEBUG("initializing moreinfo");
+ _moreinfo = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+}
+
+void
+moreinfo_shutdown(void)
+{
+ if (G_UNLIKELY(!_moreinfo)) {
+ DEBUG("moreinfo not initialized");
+ return;
+ }
+ DEBUG("shutting down moreinfo");
+ g_hash_table_destroy(_moreinfo);
+ _moreinfo = NULL;
+}
+
+void
+moreinfo_add_with_prefix(gchar *prefix, gchar *key, gchar *value)
+{
+ if (G_UNLIKELY(!_moreinfo)) {
+ DEBUG("moreinfo not initialized");
+ return;
+ }
+
+ if (prefix) {
+ gchar *hashkey = g_strconcat(prefix, ":", key, NULL);
+ g_hash_table_insert(_moreinfo, hashkey, value);
+ return;
+ }
+
+ g_hash_table_insert(_moreinfo, g_strdup(key), value);
+}
+
+void
+moreinfo_add(gchar *key, gchar *value)
+{
+ moreinfo_add_with_prefix(NULL, key, value);
+}
+
+static gboolean
+_moreinfo_del_cb(gpointer key, gpointer value, gpointer data)
+{
+ return g_str_has_prefix(key, data);
+}
+
+void
+moreinfo_del_with_prefix(gchar *prefix)
+{
+ if (G_UNLIKELY(!_moreinfo)) {
+ DEBUG("moreinfo not initialized");
+ return;
+ }
+
+ g_hash_table_foreach_remove(_moreinfo, _moreinfo_del_cb, prefix);
+}
+
+void
+moreinfo_clear(void)
+{
+ if (G_UNLIKELY(!_moreinfo)) {
+ DEBUG("moreinfo not initialized");
+ return;
+ }
+ h_hash_table_remove_all(_moreinfo);
+}
+
+gchar *
+moreinfo_lookup_with_prefix(gchar *prefix, gchar *key)
+{
+ if (G_UNLIKELY(!_moreinfo)) {
+ DEBUG("moreinfo not initialized");
+ return 0;
+ }
+
+ if (prefix) {
+ gchar *lookup_key = g_strconcat(prefix, ":", key, NULL);
+ gchar *result = g_hash_table_lookup(_moreinfo, lookup_key);
+ g_free(lookup_key);
+ return result;
+ }
+
+ return g_hash_table_lookup(_moreinfo, key);
+}
+
+gchar *
+moreinfo_lookup(gchar *key)
+{
+ return moreinfo_lookup_with_prefix(NULL, key);
+}
+
+#if !GLIB_CHECK_VERSION(2,44,0)
+gboolean g_strv_contains(const gchar * const * strv, const gchar *str) {
+ /* g_strv_contains() requires glib>2.44
+ * fallback for older versions */
+ gint i = 0;
+ while(strv[i] != NULL) {
+ if (g_strcmp0(strv[i], str) == 0)
+ return 1;
+ i++;
+ }
+ return 0;
+}
+#endif
+
+gchar *hardinfo_clean_grpname(const gchar *v, int replacing) {
+ gchar *clean, *p;
+
+ p = clean = g_strdup(v);
+ while (*p != 0) {
+ switch(*p) {
+ case '[':
+ *p = '('; break;
+ case ']':
+ *p = ')'; break;
+ default:
+ break;
+ }
+ p++;
+ }
+ if (replacing)
+ g_free((gpointer)v);
+ return clean;
+}
+
+/* Hardinfo labels that have # are truncated and/or hidden.
+ * Labels can't have $ because that is the delimiter in
+ * moreinfo. */
+gchar *hardinfo_clean_label(const gchar *v, int replacing) {
+ gchar *clean, *p;
+
+ p = clean = g_strdup(v);
+ while (*p != 0) {
+ switch(*p) {
+ case '#': case '$':
+ *p = '_';
+ break;
+ default:
+ break;
+ }
+ p++;
+ }
+ if (replacing)
+ g_free((gpointer)v);
+ return clean;
+}
+
+/* hardinfo uses the values as {ht,x}ml, apparently */
+gchar *hardinfo_clean_value(const gchar *v, int replacing) {
+ gchar *clean, *tmp;
+ gchar **vl;
+ if (v == NULL) return NULL;
+
+ vl = g_strsplit(v, "&", -1);
+ if (g_strv_length(vl) > 1)
+ clean = g_strjoinv("&amp;", vl);
+ else
+ clean = g_strdup(v);
+ g_strfreev(vl);
+
+ vl = g_strsplit(clean, "<", -1);
+ if (g_strv_length(vl) > 1) {
+ tmp = g_strjoinv("&lt;", vl);
+ g_free(clean);
+ clean = tmp;
+ }
+ g_strfreev(vl);
+
+ vl = g_strsplit(clean, ">", -1);
+ if (g_strv_length(vl) > 1) {
+ tmp = g_strjoinv("&gt;", vl);
+ g_free(clean);
+ clean = tmp;
+ }
+ g_strfreev(vl);
+
+ if (replacing)
+ g_free((gpointer)v);
+ return clean;
+}
+
+gboolean hardinfo_spawn_command_line_sync(const gchar *command_line,
+ gchar **standard_output,
+ gchar **standard_error,
+ gint *exit_status,
+ GError **error)
+{
+ shell_status_pulse();
+ return g_spawn_command_line_sync(command_line, standard_output,
+ standard_error, exit_status, error);
+}
diff --git a/hardinfo2/vendor.c b/hardinfo2/vendor.c
new file mode 100644
index 00000000..9230c696
--- /dev/null
+++ b/hardinfo2/vendor.c
@@ -0,0 +1,655 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2009 L. A. F. Pereira <l@tia.mat.br>
+ *
+ * List of vendors based on GtkSysInfo (c) Pissens Sebastien.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include "vendor.h"
+#include "syncmanager.h"
+#include "config.h"
+#include "hardinfo.h"
+#include "strstr_word.h"
+#include "util_sysobj.h" /* for appfsp() and SEQ() */
+
+/* { match_string, match_rule, name, url } */
+static Vendor vendors_builtin[] = {
+ /* BIOS manufacturers */
+ { "American Megatrends", 0, "American Megatrends", "www.ami.com"},
+ { "Award", 0, "Award Software International", "www.award-bios.com"},
+ { "Phoenix", 0, "Phoenix Technologies", "www.phoenix.com"},
+ /* x86 vendor strings */
+ { "AMDisbetter!", 0, "Advanced Micro Devices", "www.amd.com" },
+ { "AuthenticAMD", 0, "Advanced Micro Devices", "www.amd.com" },
+ { "CentaurHauls", 0, "VIA (formerly Centaur Technology)", "www.via.tw" },
+ { "CyrixInstead", 0, "Cyrix", "" },
+ { "GenuineIntel", 0, "Intel", "www.intel.com" },
+ { "TransmetaCPU", 0, "Transmeta", "" },
+ { "GenuineTMx86", 0, "Transmeta", "" },
+ { "Geode by NSC", 0, "National Semiconductor", "" },
+ { "NexGenDriven", 0, "NexGen", "" },
+ { "RiseRiseRise", 0, "Rise Technology", "" },
+ { "SiS SiS SiS", 0, "Silicon Integrated Systems", "" },
+ { "UMC UMC UMC", 0, "United Microelectronics Corporation", "" },
+ { "VIA VIA VIA", 0, "VIA", "www.via.tw" },
+ { "Vortex86 SoC", 0, "DMP Electronics", "" },
+ /* x86 VM vendor strings */
+ { "KVMKVMKVM", 0, "KVM", "" },
+ { "Microsoft Hv", 0, "Microsoft Hyper-V", "www.microsoft.com" },
+ { "lrpepyh vr", 0, "Parallels", "" },
+ { "VMwareVMware", 0, "VMware", "" },
+ { "XenVMMXenVMM", 0, "Xen HVM", "" },
+};
+
+#define ven_msg(msg, ...) fprintf (stderr, "[%s] " msg "\n", __FUNCTION__, ##__VA_ARGS__) /**/
+#define ven_file_err(msg, ...) { \
+ ven_msg(msg, ##__VA_ARGS__); \
+ fflush(stderr); \
+ if (vendor_die_on_error) exit(-1); }
+
+static vendor_list vendors = NULL;
+vendor_list get_vendors_list() { return vendors; }
+gboolean vendor_die_on_error = FALSE;
+
+/* sort the vendor list by length of match_string,
+ * LONGEST first */
+int vendor_sort (const Vendor *ap, const Vendor *bp) {
+ int la = 0, lb = 0;
+ if (ap) la = ap->weight;
+ if (bp) lb = bp->weight;
+ return lb-la;
+}
+
+static int read_from_vendor_conf(const char *path) {
+ GKeyFile *vendors_file;
+ gchar *tmp;
+ gint num_vendors, i, count = 0; /* num_vendors is file-reported, count is actual */
+
+ DEBUG("using vendor.conf format loader for %s", path);
+
+ vendors_file = g_key_file_new();
+ if (g_key_file_load_from_file(vendors_file, path, 0, NULL)) {
+ num_vendors = g_key_file_get_integer(vendors_file, "vendors", "number", NULL);
+
+ for (i = num_vendors - 1; i >= 0; i--) {
+ Vendor *v = g_new0(Vendor, 1);
+
+ tmp = g_strdup_printf("vendor%d", i);
+
+ v->match_string = g_key_file_get_string(vendors_file, tmp, "match_string", NULL);
+ if (v->match_string == NULL) {
+ /* try old name */
+ v->match_string = g_key_file_get_string(vendors_file, tmp, "id", NULL);
+ }
+ if (v->match_string) {
+ v->match_rule = g_key_file_get_integer(vendors_file, tmp, "match_case", NULL);
+ v->name = g_key_file_get_string(vendors_file, tmp, "name", NULL);
+ v->name_short = g_key_file_get_string(vendors_file, tmp, "name_short", NULL);
+ v->url = g_key_file_get_string(vendors_file, tmp, "url", NULL);
+ v->url_support = g_key_file_get_string(vendors_file, tmp, "url_support", NULL);
+
+ vendors = g_slist_prepend(vendors, v);
+ count++;
+ } else {
+ /* don't add if match_string is null */
+ g_free(v);
+ }
+
+ g_free(tmp);
+ }
+ g_key_file_free(vendors_file);
+ DEBUG("... found %d match strings", count);
+ return count;
+ }
+ g_key_file_free(vendors_file);
+ return 0;
+}
+
+static int read_from_vendor_ids(const char *path) {
+#define VEN_BUFF_SIZE 128
+#define VEN_FFWD() while(isspace((unsigned char)*p)) p++;
+#define VEN_CHK(TOK) (strncmp(p, TOK, tl = strlen(TOK)) == 0 && (ok = 1))
+ char buff[VEN_BUFF_SIZE] = "";
+
+ char vars[7][VEN_BUFF_SIZE];
+ char *name = vars[0];
+ char *name_short = vars[1];
+ char *url = vars[2];
+ char *url_support = vars[3];
+ char *wikipedia = vars[4];
+ char *note = vars[5];
+ char *ansi_color = vars[6];
+ int name_rule_count = -1;
+
+ int count = 0;
+ FILE *fd;
+ char *p, *b;
+ int tl, line = -1, ok = 0;
+
+ DEBUG("using vendor.ids format loader for %s", path);
+
+ fd = fopen(path, "r");
+ if (!fd) return 0;
+
+ while (fgets(buff, VEN_BUFF_SIZE, fd)) {
+ ok = 0;
+ line++;
+
+ b = strchr(buff, '\n');
+ if (b)
+ *b = 0;
+ else
+ ven_file_err("%s:%d: line longer than VEN_BUFF_SIZE (%lu)", path, line, (unsigned long)VEN_BUFF_SIZE);
+
+ b = strchr(buff, '#');
+ if (b) *b = 0; /* line ends at comment */
+
+ p = buff;
+ VEN_FFWD();
+ if (VEN_CHK("name ")) {
+ if (name_rule_count == 0)
+ ven_file_err("%s:%d: name \"%s\" had no match rules", path, line, name);
+ strncpy(name, p + tl, VEN_BUFF_SIZE - 1);
+ strcpy(name_short, "");
+ strcpy(url, "");
+ strcpy(url_support, "");
+ strcpy(wikipedia, "");
+ strcpy(note, "");
+ strcpy(ansi_color, "");
+ name_rule_count = 0;
+ }
+ if (VEN_CHK("name_short "))
+ strncpy(name_short, p + tl, VEN_BUFF_SIZE - 1);
+ if (VEN_CHK("url "))
+ strncpy(url, p + tl, VEN_BUFF_SIZE - 1);
+ if (VEN_CHK("url_support "))
+ strncpy(url_support, p + tl, VEN_BUFF_SIZE - 1);
+ if (VEN_CHK("wikipedia "))
+ strncpy(wikipedia, p + tl, VEN_BUFF_SIZE - 1);
+ if (VEN_CHK("note "))
+ strncpy(note, p + tl, VEN_BUFF_SIZE - 1);
+ if (VEN_CHK("ansi_color "))
+ strncpy(ansi_color, p + tl, VEN_BUFF_SIZE - 1);
+
+#define dup_if_not_empty(s) (strlen(s) ? g_strdup(s) : NULL)
+
+ int mrule = -1;
+ if (VEN_CHK("match_string "))
+ mrule = VENDOR_MATCH_RULE_WORD_IGNORE_CASE;
+ else if (VEN_CHK("match_string_case "))
+ mrule = VENDOR_MATCH_RULE_WORD_MATCH_CASE;
+ else if (VEN_CHK("match_string_exact "))
+ mrule = VENDOR_MATCH_RULE_EXACT;
+ else if (VEN_CHK("match_string_prefix "))
+ mrule = VENDOR_MATCH_RULE_WORD_PREFIX_IGNORE_CASE;
+ else if (VEN_CHK("match_string_prefix_case "))
+ mrule = VENDOR_MATCH_RULE_WORD_PREFIX_MATCH_CASE;
+ else if (VEN_CHK("match_string_suffix "))
+ mrule = VENDOR_MATCH_RULE_WORD_PREFIX_IGNORE_CASE;
+ else if (VEN_CHK("match_string_suffix_case "))
+ mrule = VENDOR_MATCH_RULE_WORD_PREFIX_MATCH_CASE;
+ else if (VEN_CHK("match_string_num_prefix "))
+ mrule = VENDOR_MATCH_RULE_NUM_PREFIX_IGNORE_CASE;
+ else if (VEN_CHK("match_string_num_prefix_case "))
+ mrule = VENDOR_MATCH_RULE_NUM_PREFIX_MATCH_CASE;
+
+ if (mrule >= 0) {
+ Vendor *v = g_new0(Vendor, 1);
+ v->file_line = line;
+ v->match_string = g_strdup(p+tl);
+ v->ms_length = strlen(v->match_string);
+ v->match_rule = mrule;
+ v->name = g_strdup(name);
+ v->name_short = dup_if_not_empty(name_short);
+ v->url = dup_if_not_empty(url);
+ v->url_support = dup_if_not_empty(url_support);
+ v->wikipedia = dup_if_not_empty(wikipedia);
+ v->note = dup_if_not_empty(note);
+ v->ansi_color = dup_if_not_empty(ansi_color);
+
+ v->weight = v->ms_length;
+ /* NUM_PREFIX rules consider +1 characters */
+ if (v->match_rule == VENDOR_MATCH_RULE_NUM_PREFIX_MATCH_CASE
+ || v->match_rule == VENDOR_MATCH_RULE_NUM_PREFIX_IGNORE_CASE)
+ v->weight++;
+
+ v->has_parens = g_utf8_strchr(v->match_string, -1, '(') ? TRUE : FALSE;
+
+ vendors = g_slist_prepend(vendors, v);
+ name_rule_count++;
+ count++;
+ }
+
+ g_strstrip(buff);
+ if (!ok && *buff != 0)
+ ven_file_err("unrecognised item at %s:%d, %s", path, line, buff);
+ }
+
+ fclose(fd);
+
+ DEBUG("... found %d match strings", count);
+
+ return count;
+}
+
+void vendor_init(void)
+{
+ gchar *path;
+ static SyncEntry se = {
+ .name = N_("Update vendor list"),
+ .file_name = "vendor.ids",
+ };
+
+ /* already initialized */
+ if (vendors) return;
+
+ DEBUG("initializing vendor list");
+ sync_manager_add_entry(&se);
+
+ char *file_search_order[] = {
+ /* new format */
+ g_build_filename(g_get_user_config_dir(), "hardinfo2", "vendor.ids", NULL),
+ g_build_filename(params.path_data, "vendor.ids", NULL),
+ /* old format */
+ g_build_filename(g_get_user_config_dir(), "hardinfo2", "vendor.conf", NULL),
+ g_build_filename(g_get_home_dir(), ".hardinfo2", "vendor.conf", NULL), /* old place */
+ g_build_filename(params.path_data, "vendor.conf", NULL),
+ NULL
+ };
+
+ int n = 0;
+ while (file_search_order[n]) {
+ DEBUG("searching for vendor data at %s ...", file_search_order[n]);
+ if (!access(file_search_order[n], R_OK)) {
+ path = file_search_order[n];
+ DEBUG("... file exists.");
+ break;
+ }
+ n++;
+ }
+
+ int fail = 1;
+
+ /* new format */
+ if (path && strstr(path, "vendor.ids")) {
+ fail = !read_from_vendor_ids(path);
+ }
+
+ /* old format */
+ if (path && strstr(path, "vendor.conf")) {
+ fail = !read_from_vendor_conf(path);
+ }
+
+ if (fail) {
+ gint i;
+
+ DEBUG("vendor data not found, using internal database");
+
+ for (i = G_N_ELEMENTS(vendors_builtin) - 1; i >= 0; i--) {
+ vendors = g_slist_prepend(vendors, (gpointer) &vendors_builtin[i]);
+ }
+ }
+
+ /* sort the vendor list by length of match string so that short strings are
+ * less likely to incorrectly match.
+ * example: ST matches ASUSTeK but SEAGATE is not ASUS */
+ vendors = g_slist_sort(vendors, (GCompareFunc)vendor_sort);
+
+ /* free search location strings */
+ n = 0;
+ while (file_search_order[n]) {
+ free(file_search_order[n]);
+ n++;
+ }
+}
+
+void vendor_cleanup() {
+ DEBUG("cleanup vendor list");
+ //FIXME CRASH g_slist_free_full(vendors, (GDestroyNotify)vendor_free);
+}
+
+void vendor_free(Vendor *v) {
+ if (v) {
+ g_free(v->name);
+ g_free(v->name_short);
+ g_free(v->url);
+ g_free(v->url_support);
+ g_free(v->ansi_color);
+ g_free(v->match_string);
+ g_free(v);
+ }
+}
+
+const Vendor *vendor_match(const gchar *id_str, ...) {
+ Vendor *ret = NULL;
+ va_list ap, ap2;
+ gchar *tmp = NULL, *p = NULL;
+ int tl = 0, c = 0;
+
+ if (id_str) {
+ c++;
+ tl += strlen(id_str);
+ tmp = appfsp(tmp, "%s", id_str);
+
+ va_start(ap, id_str);
+ p = va_arg(ap, gchar*);
+ while(p) {
+ c++;
+ tl += strlen(p);
+ tmp = appfsp(tmp, "%s", p);
+ p = va_arg(ap, gchar*);
+ }
+ va_end(ap);
+ }
+ if (!c || tl == 0)
+ return NULL;
+
+ vendor_list vl = vendors_match_core(tmp, 1);
+ if (vl) {
+ ret = (Vendor*)vl->data;
+ vendor_list_free(vl);
+ }
+ return ret;
+}
+
+static const gchar *vendor_get_name_ex(const gchar * id_str, short shortest) {
+ const Vendor *v = vendor_match(id_str, NULL);
+
+ if (v) {
+ if (shortest) {
+ int sl = strlen(id_str);
+ int nl = (v->name) ? strlen(v->name) : 0;
+ int snl = (v->name_short) ? strlen(v->name_short) : 0;
+ if (!nl) nl = 9999;
+ if (!snl) snl = 9999;
+ /* if id_str is shortest, then return as if
+ * not found (see below).
+ * if all equal then prefer
+ * name_short > name > id_str. */
+ if (nl < snl)
+ return (sl < nl) ? id_str : v->name;
+ else
+ return (sl < snl) ? id_str : v->name_short;
+ } else
+ return v->name;
+ }
+
+ return id_str; /* Preserve an old behavior, but what about const? */
+}
+
+const gchar *vendor_get_name(const gchar * id_str) {
+ return vendor_get_name_ex(id_str, 0);
+}
+
+const gchar *vendor_get_shortest_name(const gchar * id_str) {
+ return vendor_get_name_ex(id_str, 1);
+}
+
+const gchar *vendor_get_url(const gchar * id_str) {
+ const Vendor *v = vendor_match(id_str, NULL);
+
+ if (v)
+ return v->url;
+
+ return NULL;
+}
+
+gchar *vendor_get_link(const gchar *id_str)
+{
+ const Vendor *v = vendor_match(id_str, NULL);
+
+ if (!v) {
+ return g_strdup(id_str);
+ }
+
+ return vendor_get_link_from_vendor(v);
+}
+
+gchar *vendor_get_link_from_vendor(const Vendor *v)
+{
+#if GTK_CHECK_VERSION(2, 18, 0)
+ gboolean gtk_label_link_ok = TRUE;
+#else
+ gboolean gtk_label_link_ok = FALSE;
+#endif
+ gboolean link_ok = params.markup_ok && gtk_label_link_ok;
+ /* If using a real link, and wikipedia is available,
+ * target that instead of url. There's usually much more
+ * information there, plus easily click through to company url. */
+ gboolean link_wikipedia = TRUE;
+
+ if (!v)
+ return g_strdup(_("Unknown"));
+
+ gchar *url = NULL;
+
+ if (link_ok && link_wikipedia && v->wikipedia
+ || v->wikipedia && !v->url)
+ url = g_strdup_printf("http://wikipedia.com/wiki/%s", v->wikipedia);
+ else if (v->url)
+ url = g_strdup(v->url);
+
+ if (!url)
+ return g_strdup(v->name);
+
+ auto_free(url);
+
+ if (link_ok) {
+ const gchar *prefix;
+
+ if (!strncmp(url, "http://", sizeof("http://") - 1) ||
+ !strncmp(url, "https://", sizeof("https://") - 1)) {
+ prefix = "";
+ } else {
+ prefix = "http://";
+ }
+
+ return g_strdup_printf("<a href=\"%s%s\">%s</a>", prefix, url, v->name);
+ }
+
+ return g_strdup_printf("%s (%s)", v->name, url);
+}
+
+vendor_list vendor_list_concat_va(int count, vendor_list vl, ...) {
+ vendor_list ret = vl, p = NULL;
+ va_list ap;
+ va_start(ap, vl);
+ if (count > 0) {
+ count--; /* includes vl */
+ while (count) {
+ p = va_arg(ap, vendor_list);
+ ret = g_slist_concat(ret, p);
+ count--;
+ }
+ } else {
+ p = va_arg(ap, vendor_list);
+ while (p) {
+ ret = g_slist_concat(ret, p);
+ p = va_arg(ap, vendor_list);
+ }
+ }
+ va_end(ap);
+ return ret;
+}
+
+int vendor_cmp_deep(const Vendor *a, const Vendor *b) {
+ int r;
+ if (a && !b) return 1;
+ if (!a && b) return -1;
+ if (!a && !b) return 0;
+ r = g_strcmp0(a->name, b->name);
+ if (!!r) return r;
+ r = g_strcmp0(a->name_short, b->name_short);
+ if (!!r) return r;
+ r = g_strcmp0(a->ansi_color, b->ansi_color);
+ if (!!r) return r;
+ r = g_strcmp0(a->url, b->url);
+ if (!!r) return r;
+ r = g_strcmp0(a->url_support, b->url_support);
+ if (!!r) return r;
+ r = g_strcmp0(a->wikipedia, b->wikipedia);
+ if (!!r) return r;
+ return 0;
+}
+
+vendor_list vendor_list_remove_duplicates_deep(vendor_list vl) {
+ /* vendor_list is GSList* */
+ GSList *tvl = vl;
+ GSList *evl = NULL;
+ while(tvl) {
+ const Vendor *tv = tvl->data;
+ evl = tvl->next;
+ while(evl) {
+ const Vendor *ev = evl->data;
+ if (vendor_cmp_deep(ev, tv) == 0) {
+ GSList *next = evl->next;
+ vl = g_slist_delete_link(vl, evl);
+ evl = next;
+ } else
+ evl = evl->next;
+ }
+ tvl = tvl->next;
+ }
+ return vl;
+}
+
+vendor_list vendors_match(const gchar *id_str, ...) {
+ va_list ap, ap2;
+ gchar *tmp = NULL, *p = NULL;
+ int tl = 0, c = 0;
+
+ if (id_str) {
+ c++;
+ tl += strlen(id_str);
+ tmp = appfsp(tmp, "%s", id_str);
+
+ va_start(ap, id_str);
+ p = va_arg(ap, gchar*);
+ while(p) {
+ c++;
+ tl += strlen(p);
+ tmp = appfsp(tmp, "%s", p);
+ p = va_arg(ap, gchar*);
+ }
+ va_end(ap);
+ }
+ if (!c || tl == 0)
+ return NULL;
+
+ return vendors_match_core(tmp, -1);
+}
+
+vendor_list vendors_match_core(const gchar *str, int limit) {
+ gchar *p = NULL;
+ GSList *vlp;
+ int found = 0;
+ vendor_list ret = NULL;
+
+ /* pass [array_index]: function
+ * 1st [3]: only check match strings that have () in them
+ * 2nd [2]: ignore text in (), like (formerly ...) or (nee ...),
+ * but unfortunately also (now ...)
+ * 3rd [1]: (passes[0]): full text */
+ gchar *passes[3] = { g_strdup(str), g_strdup(str), g_strdup(str) };
+ int pass = 1; p = passes[1];
+ while((p = strchr(p, '('))) {
+ pass = 3; p++;
+ while(*p && *p != ')') { *p = ' '; p++; }
+ }
+
+ for (; pass > 0; pass--) {
+ for (vlp = vendors; vlp; vlp = vlp->next) {
+ Vendor *v = (Vendor *)vlp->data;
+ char *m = NULL;
+
+ if (!v) continue;
+ if (!v->match_string) continue;
+
+ if (v->has_parens)
+ if (pass != 3) continue;
+
+ //ven_msg("pass:%d <<%s>> EXAMINE: \"%s\"", pass, v->match_string, passes[pass-1]);
+ int epass;
+
+#define standard_match_work_inner(fn) { \
+ /* clear so it doesn't match again */ \
+ for(epass = pass; epass > 0; epass--) \
+ { char *s = passes[epass-1] + (m - passes[pass-1]); \
+ char *e = s + v->ms_length; \
+ for(; s < e; s++) *s = ' '; \
+ g_strstrip(passes[epass-1]); } \
+ /* add to return list */ \
+ ret = vendor_list_append(ret, v); \
+ found++; \
+ if (*passes[0] == 0) \
+ goto vendors_match_core_finish; \
+ if (limit > 0 && found >= limit) \
+ goto vendors_match_core_finish; }
+#define standard_match_work(fn) \
+ if ((m = fn(passes[pass-1], v->match_string))) \
+ standard_match_work_inner();
+
+ switch(v->match_rule) {
+ case VENDOR_MATCH_RULE_EXACT:
+ if (SEQ(passes[pass-1], v->match_string) ) {
+ /* add to return list */
+ ret = vendor_list_append(ret, v);
+ found++;
+ goto vendors_match_core_finish; /* no way for any other match to happen */
+ }
+ break;
+ case VENDOR_MATCH_RULE_WORD_IGNORE_CASE:
+ standard_match_work(strcasestr_word);
+ break;
+ case VENDOR_MATCH_RULE_WORD_MATCH_CASE:
+ standard_match_work(strstr_word);
+ break;
+ case VENDOR_MATCH_RULE_WORD_PREFIX_MATCH_CASE:
+ standard_match_work(strstr_word_prefix);
+ break;
+ case VENDOR_MATCH_RULE_WORD_PREFIX_IGNORE_CASE:
+ standard_match_work(strcasestr_word_prefix);
+ break;
+ case VENDOR_MATCH_RULE_WORD_SUFFIX_MATCH_CASE:
+ standard_match_work(strstr_word_suffix);
+ break;
+ case VENDOR_MATCH_RULE_WORD_SUFFIX_IGNORE_CASE:
+ standard_match_work(strcasestr_word_suffix);
+ break;
+ case VENDOR_MATCH_RULE_NUM_PREFIX_IGNORE_CASE:
+ if ((m = strstr_word_prefix(passes[pass-1], v->match_string)))
+ if (isdigit(m[v->ms_length]))
+ standard_match_work_inner();
+ break;
+ case VENDOR_MATCH_RULE_NUM_PREFIX_MATCH_CASE:
+ if ((m = strcasestr_word_prefix(passes[pass-1], v->match_string)))
+ if (isdigit(m[v->ms_length]))
+ standard_match_work_inner();
+ break;
+ }
+ }
+ }
+
+vendors_match_core_finish:
+
+ g_free(passes[0]);
+ g_free(passes[1]);
+ g_free(passes[2]);
+ return ret;
+}
diff --git a/hardinfo2/x_util.c b/hardinfo2/x_util.c
new file mode 100644
index 00000000..2a7febb4
--- /dev/null
+++ b/hardinfo2/x_util.c
@@ -0,0 +1,342 @@
+/*
+ * HardInfo - Displays System Information
+ * Copyright (C) 2003-2017 L. A. F. Pereira <l@tia.mat.br>
+ * This file
+ * Copyright (C) 2018 Burt P. <pburt0@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "hardinfo.h"
+#include "x_util.h"
+#include <X11/Xlib.h>
+
+/* wayland stuff lives here for now */
+
+wl_info *get_walyand_info() {
+ wl_info *s = malloc(sizeof(wl_info));
+ memset(s, 0, sizeof(wl_info));
+ s->xdg_session_type = g_strdup( getenv("XDG_SESSION_TYPE") );
+ if (s->xdg_session_type == NULL)
+ s->xdg_session_type = strdup("x11");
+ s->display_name = g_strdup( getenv("WAYLAND_DISPLAY") );
+ return s;
+}
+
+void wl_free(wl_info *s) {
+ if (s) {
+ free(s->xdg_session_type);
+ free(s->display_name);
+ free(s);
+ }
+}
+
+/* get x information from xrandr, xdpyinfo, and glxinfo */
+
+static char *simple_line_value(char *line, const char *prefix) {
+ if (g_str_has_prefix(g_strstrip(line), prefix)) {
+ line += strlen(prefix) + 1;
+ return g_strstrip(line);
+ } else
+ return NULL;
+}
+
+gboolean fill_glx_info(glx_info *glx) {
+ gboolean spawned;
+ gchar *out, *err, *p, *l, *next_nl;
+ gchar *glx_cmd = g_strdup("glxinfo");
+
+#define GLX_MATCH_LINE(prefix_str, struct_member) \
+ if (l = simple_line_value(p, prefix_str)) { glx->struct_member = g_strdup(l); goto glx_next_line; }
+
+ spawned = hardinfo_spawn_command_line_sync(glx_cmd,
+ &out, &err, NULL, NULL);
+ g_free(glx_cmd);
+ if (spawned) {
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ g_strstrip(p);
+ GLX_MATCH_LINE("GLX version", glx_version);
+ GLX_MATCH_LINE("OpenGL vendor string", ogl_vendor);
+ GLX_MATCH_LINE("OpenGL renderer string", ogl_renderer);
+ GLX_MATCH_LINE("OpenGL renderer string", ogl_renderer);
+ GLX_MATCH_LINE("OpenGL core profile version string", ogl_core_version);
+ GLX_MATCH_LINE("OpenGL core profile shading language version string", ogl_core_sl_version);
+ GLX_MATCH_LINE("OpenGL version string", ogl_version);
+ GLX_MATCH_LINE("OpenGL shading language version string", ogl_sl_version);
+ GLX_MATCH_LINE("OpenGL ES profile version string", ogles_version);
+ GLX_MATCH_LINE("OpenGL ES profile shading language version string", ogles_sl_version);
+
+ if (l = simple_line_value(p, "direct rendering")) {
+ if (strstr(p, "Yes"))
+ glx->direct_rendering = 1;
+ goto glx_next_line;
+ }
+
+ glx_next_line:
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+glx_info *glx_create() {
+ glx_info *s = malloc(sizeof(glx_info));
+ memset(s, 0, sizeof(glx_info));
+ return s;
+}
+
+void glx_free(glx_info *s) {
+ if (s) {
+ free(s->glx_version);
+ free(s->ogl_vendor);
+ free(s->ogl_renderer);
+ free(s->ogl_core_version);
+ free(s->ogl_core_sl_version);
+ free(s->ogl_version);
+ free(s->ogl_sl_version);
+ free(s->ogles_version);
+ free(s->ogles_sl_version);
+ free(s);
+ }
+}
+
+gboolean fill_xinfo(xinfo *xi) {
+ gboolean spawned;
+ gchar *out, *err, *p, *l, *next_nl;
+ gchar *xi_cmd = g_strdup("xdpyinfo");
+
+#define XI_MATCH_LINE(prefix_str, struct_member) \
+ if (l = simple_line_value(p, prefix_str)) { xi->struct_member = g_strdup(l); goto xi_next_line; }
+
+ spawned = hardinfo_spawn_command_line_sync(xi_cmd,
+ &out, &err, NULL, NULL);
+ g_free(xi_cmd);
+ if (spawned) {
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ g_strstrip(p);
+ XI_MATCH_LINE("name of display", display_name);
+ XI_MATCH_LINE("vendor string", vendor);
+ XI_MATCH_LINE("vendor release number", release_number);
+ XI_MATCH_LINE("XFree86 version", version);
+ XI_MATCH_LINE("X.Org version", version);
+
+ xi_next_line:
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+gboolean fill_xrr_info(xrr_info *xrr) {
+ gboolean spawned;
+ gchar *out, *err, *p, *next_nl;
+ gchar *xrr_cmd = g_strdup("xrandr --prop");
+ int ec;
+
+ x_screen ts;
+ x_output to;
+ char output_id[128];
+ char status[128];
+ char alist[512];
+
+ memset(&ts, 0, sizeof(x_screen));
+ memset(&to, 0, sizeof(x_output));
+ memset(output_id, 0, sizeof(output_id));
+ memset(status, 0, sizeof(status));
+ memset(alist, 0, sizeof(alist));
+
+ spawned = hardinfo_spawn_command_line_sync(xrr_cmd,
+ &out, &err, NULL, NULL);
+ g_free(xrr_cmd);
+ if (spawned) {
+ p = out;
+ while(next_nl = strchr(p, '\n')) {
+ strend(p, '\n');
+ g_strstrip(p);
+
+ ec = sscanf(p, "Screen %d: minimum %d x %d, current %d x %d, maximum %d x %d",
+ &ts.number,
+ &ts.min_px_width, &ts.min_px_height,
+ &ts.px_width, &ts.px_height,
+ &ts.max_px_width, &ts.max_px_height);
+ if (ec == 7) {
+ /* new screen */
+ xrr->screen_count++;
+ if (xrr->screens == NULL)
+ xrr->screens = malloc(xrr->screen_count * sizeof(x_screen));
+ else
+ xrr->screens = realloc(xrr->screens, xrr->screen_count * sizeof(x_screen));
+ memcpy(&xrr->screens[xrr->screen_count-1], &ts, sizeof(x_screen));
+ memset(&ts, 0, sizeof(x_screen)); /* clear the temp */
+ goto xrr_next_line;
+ }
+
+ /* looking for:
+ * <output_id> (connected|disconnected|unknown connection) (primary|?) <%dx%d+%d+%d> (attribute_list) mm x mm
+ */
+ ec = sscanf(p, "%127s %127[^(](%511[^)]", output_id, status, alist);
+ if (ec == 3) {
+ int is_output = 0, found_rect = 0, n = 0;
+ gchar **ot = g_strsplit(status, " ", 0);
+
+ /* find rect */
+ while (ot[n] != NULL) {
+ ec = sscanf(ot[n], "%dx%d+%d+%d", &to.px_width, &to.px_height, &to.px_offset_x, &to.px_offset_y);
+ if (ec == 4) {
+ found_rect = 1;
+ break;
+ }
+ n++;
+ }
+ if (found_rect)
+ to.screen = xrr->screen_count - 1;
+ else
+ to.screen = -1;
+
+ if (strcmp("disconnected", ot[0]) == 0) {
+ is_output = 1;
+ to.connected = 0;
+ } else
+ if (strcmp("unknown", ot[0]) == 0 && strcmp("connection", ot[1]) == 0) {
+ is_output = 1;
+ to.connected = -1;
+ } else
+ if (strcmp("connected", ot[0]) == 0) {
+ is_output = 1;
+ to.connected = 1;
+ }
+ g_strfreev(ot);
+ if (is_output) {
+ strncpy(to.name, output_id, 63);
+ xrr->output_count++;
+ if (xrr->outputs == NULL)
+ xrr->outputs = malloc(xrr->output_count * sizeof(x_output));
+ else
+ xrr->outputs = realloc(xrr->outputs, xrr->output_count * sizeof(x_output));
+ memcpy(&xrr->outputs[xrr->output_count-1], &to, sizeof(x_output));
+ memset(&to, 0, sizeof(x_output)); /* clear the temp */
+ goto xrr_next_line;
+ }
+ }
+
+ xrr_next_line:
+ p = next_nl + 1;
+ }
+ g_free(out);
+ g_free(err);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+gboolean fill_basic_xlib(xinfo *xi) {
+ Display *display;
+ int s, w, h, rn;
+
+ display = XOpenDisplay(NULL);
+ if (display) {
+ if (!xi->display_name)
+ xi->display_name = XDisplayName(NULL);
+
+ if (!xi->vendor)
+ xi->vendor = XServerVendor(display);
+
+ if (!xi->release_number) {
+ rn = XVendorRelease(display);
+ xi->release_number = g_strdup_printf("%d", rn);
+ }
+
+ if (!xi->xrr->screen_count) {
+ s = DefaultScreen(display);
+ w = XDisplayWidth(display, s);
+ h = XDisplayHeight(display, s);
+
+ /* create at least one screen */
+ x_screen ts;
+ memset(&ts, 0, sizeof(x_screen));
+ ts.number = s;
+ ts.px_width = w;
+ ts.px_height = h;
+
+ xi->xrr->screen_count++;
+ if (xi->xrr->screens == NULL)
+ xi->xrr->screens = malloc(xi->xrr->screen_count * sizeof(x_screen));
+ else
+ xi->xrr->screens = realloc(xi->xrr->screens, xi->xrr->screen_count * sizeof(x_screen));
+ memcpy(&xi->xrr->screens[xi->xrr->screen_count-1], &ts, sizeof(x_screen));
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+xrr_info *xrr_create() {
+ xrr_info *xrr = malloc(sizeof(xrr_info));
+ memset(xrr, 0, sizeof(xrr_info));
+ return xrr;
+}
+
+void xrr_free(xrr_info *xrr) {
+ if (xrr) {
+ free(xrr->screens);
+ free(xrr->outputs);
+ free(xrr->providers);
+ free(xrr->monitors);
+ free(xrr);
+ }
+}
+
+xinfo *xinfo_get_info() {
+ int fail = 0;
+ xinfo *xi = malloc(sizeof(xinfo));
+ memset(xi, 0, sizeof(xinfo));
+ xi->glx = glx_create();
+ xi->xrr = xrr_create();
+
+ if ( !fill_xinfo(xi) )
+ fail++;
+ if ( !fill_xrr_info(xi->xrr) )
+ fail++;
+ if ( !fill_glx_info(xi->glx) )
+ fail++;
+
+ if (fail) {
+ /* as fallback, try xlib directly */
+ if ( !fill_basic_xlib(xi) )
+ xi->nox = 1;
+ }
+ return xi;
+}
+
+void xinfo_free(xinfo *xi) {
+ if (xi) {
+ free(xi->display_name);
+ free(xi->vendor);
+ free(xi->version);
+ free(xi->release_number);
+ xrr_free(xi->xrr);
+ glx_free(xi->glx);
+ free(xi);
+ }
+}