diff options
Diffstat (limited to 'deps/sysobj_early')
24 files changed, 3974 insertions, 0 deletions
| diff --git a/deps/sysobj_early/data/sort_vendors.pl b/deps/sysobj_early/data/sort_vendors.pl new file mode 100644 index 00000000..6ed69e58 --- /dev/null +++ b/deps/sysobj_early/data/sort_vendors.pl @@ -0,0 +1,43 @@ +#!/usr/bin/perl + +# Usage: +#   sort_vendors.pl <some_path/vendor.ids> +# Output goes to stdout. + +use utf8; +use strict; +#use File::Slurp::Unicode qw(read_file); +sub read_file { +    my $file = shift; +    return do { +        local $/ = undef; +        open my $fh, "<", $file || die "Could not open $file: $!"; +        <$fh>; +    }; +} + +my $file = shift; +die "Invalid file: $file" unless (-f $file && -r $file); +my $raw = read_file($file); +$raw =~ s/^\n*|\n*$//g; + +my $c = 0; +my @coms = (); +my %vendors = (); +SEC: foreach my $s (split(/\n{2,}/, $raw)) { +    my @lines = split(/\n/, $s); +    foreach my $l (@lines) { +        if ($l =~ /^name\s+(.*)/) { +            $vendors{"$1_$c"} = $s; +            $c++; +            next SEC; +        } +    } +    push @coms, $s; +} +foreach (@coms) { +    print "$_\n\n"; +} +foreach (sort { lc $a cmp lc $b } keys %vendors) { +    print "$vendors{$_}\n\n"; +} diff --git a/deps/sysobj_early/gui/uri_handler.c b/deps/sysobj_early/gui/uri_handler.c new file mode 100644 index 00000000..de9f64a7 --- /dev/null +++ b/deps/sysobj_early/gui/uri_handler.c @@ -0,0 +1,53 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#include <stdio.h> +#include "uri_handler.h" + +//compatibility +#ifndef G_SPAWN_DEFAULT +#define G_SPAWN_DEFAULT 0 +#endif + +static uri_handler uri_func = NULL; + +void uri_set_function(uri_handler f) { +    uri_func = f; +} + +gboolean uri_open(const gchar *uri) { +    gboolean ret = FALSE; +    if (uri_func) +        ret = uri_func(uri); +    if (ret) return TRUE; + +    return uri_open_default(uri); +} + +gboolean uri_open_default(const gchar *uri) { +    gchar *argv[] = { "/usr/bin/xdg-open", (gchar*)uri, NULL }; +    GError *err = NULL; +    g_spawn_async(NULL, argv, NULL, G_SPAWN_DEFAULT, NULL, NULL, NULL, &err ); +    if (err) { +        fprintf(stderr, "Error opening URI %s: %s\n", uri, err->message); +        g_error_free(err); +    } +    return TRUE; +} diff --git a/deps/sysobj_early/gui/uri_handler.h b/deps/sysobj_early/gui/uri_handler.h new file mode 100644 index 00000000..8d7892c5 --- /dev/null +++ b/deps/sysobj_early/gui/uri_handler.h @@ -0,0 +1,32 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#ifndef URI_HANDLER_H +#define URI_HANDLER_H + +#include <glib.h> + +typedef gboolean (*uri_handler)(const gchar *uri); + +void uri_set_function(uri_handler f); +gboolean uri_open(const gchar *uri); +gboolean uri_open_default(const gchar *uri); /* uses xdg-open */ + +#endif diff --git a/deps/sysobj_early/include/appf.h b/deps/sysobj_early/include/appf.h new file mode 100644 index 00000000..b99f9373 --- /dev/null +++ b/deps/sysobj_early/include/appf.h @@ -0,0 +1,40 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#ifndef _APPF_H_ +#define _APPF_H_ + +/* Appends a formatted element to a string, adding an optional + * separator string if the string is not empty. + * The string is created if str is null. + * ex: ret = appf(ret, "; ", "%s = %d", name, value); */ +char *appf(char *str, const char *sep, const char *fmt, ...) +    __attribute__ ((format (printf, 3, 4))); + +/* Same as above except that str is untouched. + * ex: ret = appf(keeper, "; ", "%s = %d", name, value); */ +char *appfdup(const char *str, const char *sep, const char *fmt, ...) +    __attribute__ ((format (printf, 3, 4))); + +/* for convenience */ +#define appfsp(str, fmt, ...) appf(str, " ", fmt, __VA_ARGS__) +#define appfnl(str, fmt, ...) appf(str, "\n", fmt, __VA_ARGS__) + +#endif diff --git a/deps/sysobj_early/include/auto_free.h b/deps/sysobj_early/include/auto_free.h new file mode 100644 index 00000000..bddaa321 --- /dev/null +++ b/deps/sysobj_early/include/auto_free.h @@ -0,0 +1,67 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#ifndef _AUTO_FREE_H_ +#define _AUTO_FREE_H_ + +#include <glib.h> + +/* DEBUG_AUTO_FREE messages level: + * 0 - none + * 1 - some + * 2 - much + */ +#ifndef DEBUG_AUTO_FREE +#define DEBUG_AUTO_FREE 0 +#endif +/* the period between free_auto_free()s in the main loop */ +#define AF_SECONDS 11 +/* the minimum time between auto_free(p) and free(p) */ +#define AF_DELAY_SECONDS 10 + +#define AF_USE_SYSOBJ 0 + +#if (DEBUG_AUTO_FREE > 0) +#define auto_free(p) auto_free_ex_(p, (GDestroyNotify)g_free, __FILE__, __LINE__, __FUNCTION__) +#define auto_free_ex(p, f) auto_free_ex_(p, f, __FILE__, __LINE__, __FUNCTION__) +#define auto_free_on_exit(p) auto_free_on_exit_ex_(p, (GDestroyNotify)g_free, __FILE__, __LINE__, __FUNCTION__) +#define auto_free_on_exit_ex(p, f) auto_free_on_exit_ex_(p, f, __FILE__, __LINE__, __FUNCTION__) +#else +#define auto_free(p) auto_free_ex_(p, (GDestroyNotify)g_free, NULL, 0, NULL) +#define auto_free_ex(p, f) auto_free_ex_(p, f, NULL, 0, NULL) +#define auto_free_on_exit(p) auto_free_on_exit_ex_(p, (GDestroyNotify)g_free, NULL, 0, NULL) +#define auto_free_on_exit_ex(p, f) auto_free_on_exit_ex_(p, f, NULL, 0, NULL) +#endif +gpointer auto_free_ex_(gpointer p, GDestroyNotify f, const char *file, int line, const char *func); +gpointer auto_free_on_exit_ex_(gpointer p, GDestroyNotify f, const char *file, int line, const char *func); + +/* free all the auto_free marked items in the + * current thread with age > AF_DELAY_SECONDS */ +void free_auto_free(); + +/* call at thread termination: + * free all the auto_free marked items in the + * current thread regardless of age */ +void free_auto_free_thread_final(); + +/* call at program termination */ +void free_auto_free_final(); + +#endif diff --git a/deps/sysobj_early/include/cpubits.h b/deps/sysobj_early/include/cpubits.h new file mode 100644 index 00000000..2d8fe8a6 --- /dev/null +++ b/deps/sysobj_early/include/cpubits.h @@ -0,0 +1,39 @@ +/* + * rpiz - https://github.com/bp0/rpiz + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#ifndef _CPUBITS_H_ +#define _CPUBITS_H_ + +#include <stdint.h> + +typedef uint32_t cpubits; +uint32_t cpubits_count(cpubits *b); +int cpubits_min(cpubits *b); +int cpubits_max(cpubits *b); +int cpubits_next(cpubits *b, int start, int end); +cpubits *cpubits_from_str(char *str); +char *cpubits_to_str(cpubits *bits, char *str, int max_len); + +#define CPUBITS_SIZE 4096 /* bytes, multiple of sizeof(uint32_t) */ +#define CPUBIT_SET(BITS, BIT) ((BITS)[(BIT)/32] |= (1 << (BIT)%32)) +#define CPUBIT_GET(BITS, BIT) (((BITS)[(BIT)/32] & (1 << (BIT)%32)) >> (BIT)%32) +#define CPUBITS_CLEAR(BITS) memset((BITS), 0, CPUBITS_SIZE) + +#endif diff --git a/deps/sysobj_early/include/format_early.h b/deps/sysobj_early/include/format_early.h new file mode 100644 index 00000000..e5524e26 --- /dev/null +++ b/deps/sysobj_early/include/format_early.h @@ -0,0 +1,47 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#ifndef _FORMAT_EARLY_H_ +#define _FORMAT_EARLY_H_ + +#include <glib.h> +#include <strings.h> +#include "appf.h" +#include "util_sysobj.h" +#include "vendor.h" + +enum { +    FMT_OPT_NONE   = 0, +    FMT_OPT_ATERM  = 1<<16,  /* ANSI color terminal */ +    FMT_OPT_PANGO  = 1<<17,  /* pango markup for gtk */ +    FMT_OPT_HTML   = 1<<18,  /* html */ +}; + +gchar *safe_ansi_color(gchar *ansi_color, gboolean free_in); /* verify the ansi color */ +const gchar *color_lookup(int ansi_color); /* ansi_color to html color */ +/* wrap the input str with color based on fmt_opts (none,term,html,pango) */ +gchar *format_with_ansi_color(const gchar *str, const gchar *ansi_color, int fmt_opts); + +void tag_vendor(gchar **str, guint offset, const gchar *vendor_str, const char *ansi_color, int fmt_opts); +gchar *vendor_match_tag(const gchar *vendor_str, int fmt_opts); + +gchar *vendor_list_ribbon(const vendor_list vl_in, int fmt_opts); + +#endif diff --git a/deps/sysobj_early/include/gg_slist.h b/deps/sysobj_early/include/gg_slist.h new file mode 100644 index 00000000..255465ee --- /dev/null +++ b/deps/sysobj_early/include/gg_slist.h @@ -0,0 +1,30 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#ifndef _GG_SLIST_H_ +#define _GG_SLIST_H_ + +#include <glib.h> + +GSList *gg_slist_remove_null(GSList *sl); +GSList *gg_slist_remove_duplicates(GSList *sl); /* by pointer */ +GSList *gg_slist_remove_duplicates_custom(GSList *sl, GCompareFunc func); + +#endif diff --git a/deps/sysobj_early/include/nice_name.h b/deps/sysobj_early/include/nice_name.h new file mode 100644 index 00000000..80031c91 --- /dev/null +++ b/deps/sysobj_early/include/nice_name.h @@ -0,0 +1,32 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#ifndef _NICE_NAME_H_ +#define _NICE_NAME_H_ + +/* cleaned in-place */ +void nice_name_x86_cpuid_model_string(char *cpuid_model_string); + +/* Intel Graphics may have very long names, + * like "Intel Corporation Seventh Generation Something Core Something Something Integrated Graphics Processor Revision Ninety-four" + * cleaned in-place */ +void nice_name_intel_gpu_device(char *pci_ids_device_string); + +#endif diff --git a/deps/sysobj_early/include/strstr_word.h b/deps/sysobj_early/include/strstr_word.h new file mode 100644 index 00000000..f17e78ff --- /dev/null +++ b/deps/sysobj_early/include/strstr_word.h @@ -0,0 +1,35 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#ifndef __STRSTR_WORD_H__ +#define __STRSTR_WORD_H__ + +/* versions of strstr() and strcasestr() where the match must be preceded and + * succeded by a non-alpha-numeric character. */ +char *strstr_word(const char *haystack, const char *needle); +char *strcasestr_word(const char *haystack, const char *needle); + +/* word boundary at start only (prefix), or end only (suffix) */ +char *strstr_word_prefix(const char *haystack, const char *needle); +char *strcasestr_word_prefix(const char *haystack, const char *needle); +char *strstr_word_suffix(const char *haystack, const char *needle); +char *strcasestr_word_suffix(const char *haystack, const char *needle); + +#endif diff --git a/deps/sysobj_early/include/util_edid.h b/deps/sysobj_early/include/util_edid.h new file mode 100644 index 00000000..5bb4b932 --- /dev/null +++ b/deps/sysobj_early/include/util_edid.h @@ -0,0 +1,268 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#ifndef __UTIL_EDID_H__ +#define __UTIL_EDID_H__ + +#define _GNU_SOURCE +#include <stdint.h>  /* for *int*_t types */ +#include <glib.h> + +typedef struct _edid edid; + +typedef struct { +    edid *e; +    uint32_t offset; +} edid_addy; + +/* OUI is stored in EDID as 24-bit little-endian, + * but lookup file from IEEE expects big-endian. + * .oui_str is .oui rendered into big-endian for + * easy lookup. */ +typedef struct { +    union { +        char pnp[7]; /* only needs 3+null */ +        char oui_str[7]; /* needs 6+null */ +    }; +    uint32_t oui; +    uint8_t type; /* enum VEN_TYPE_* */ +} edid_ven; + +typedef struct { +    char *str; +    uint16_t len; +    uint8_t is_product_name; +    uint8_t is_serial; +} DisplayIDString; + +typedef struct { +    uint8_t version; +    uint8_t extension_length; +    uint8_t primary_use_case; +    uint8_t extension_count; +    uint16_t blocks; +    uint8_t checksum_ok; +} DisplayIDMeta; + +typedef struct { +    edid_addy addy; +    union { +        uint8_t tag; +        uint8_t type; +    }; +    uint8_t revision; +    uint8_t len; +    uint8_t bounds_ok; + +    /* for vendor specific block */ +    edid_ven ven; +} DisplayIDBlock; + +typedef struct { +    edid_addy addy; +    uint8_t interface; +    uint8_t supports_hdcp; +    uint8_t exists; +} DIExtData; + +/* order by rising priority */ +enum { +    OUTSRC_INVALID = -1, +    OUTSRC_EDID    =  0, +    OUTSRC_ETB, +    OUTSRC_STD, +    OUTSRC_DTD, +    OUTSRC_CEA_DTD, +    OUTSRC_SVD, + +    OUTSRC_DID_TYPE_I, +    OUTSRC_DID_TYPE_VI, +    OUTSRC_DID_TYPE_VII, +}; + +typedef struct { +    float horiz_cm, vert_cm; +    float diag_cm, diag_in; +    int horiz_blanking, vert_blanking; +    int horiz_pixels, vert_lines, vert_pixels; +    float vert_freq_hz; +    uint8_t is_interlaced; +    uint8_t is_preferred; +    int stereo_mode; +    uint64_t pixel_clock_khz; +    int src; /* enum OUTSRC_* */ +    uint64_t pixels; /* h*v: easier to compare */ +    char class_inch[12]; +} edid_output; + +struct edid_std { +    uint8_t *ptr; +    edid_output out; +}; + +struct edid_dtd { +    edid_addy addy; +    uint8_t cea_ext; /* in a CEA block vs one of the regular EDID descriptors */ +    edid_output out; +    uint8_t bounds_ok; +}; + +struct edid_svd { +    uint8_t v; +    uint8_t is_native; +    edid_output out; +}; + +struct edid_sad { +    uint8_t v[3]; +    uint8_t format, channels, freq_bits; +    int depth_bits; /* format 1 */ +    int max_kbps;   /* formats 2-8 */ +}; + +struct edid_cea_block { +    edid_addy addy; +    int type, len; +    uint8_t bounds_ok; + +    /* for vendor specific block */ +    edid_ven ven; +}; + +struct edid_descriptor { +    edid_addy addy; +    uint8_t type; +    char text[14]; +}; + +struct edid_manf_date { +    uint8_t week; +    uint8_t is_model_year; /* ignore week */ +    uint16_t year; +    int std; /* enum STD_* */ +}; + +enum { +    VEN_TYPE_INVALID = 0, +    VEN_TYPE_PNP, +    VEN_TYPE_OUI, +}; + +enum { +    STD_EDID         = 0, +    STD_EEDID        = 1, +    STD_EIACEA861    = 2, +    STD_DISPLAYID    = 3, +    STD_DISPLAYID20  = 4, +}; + +typedef struct _edid { +    union { +        void* data; +        uint8_t* u8; +        uint16_t* u16; +    }; +    unsigned int len; + +    /* enum STD_* */ +    int std; + +    uint8_t ver_major, ver_minor; +    uint8_t checksum_ok; /* first 128-byte block only */ +    uint8_t ext_blocks, ext_blocks_ok, ext_blocks_fail; +    uint8_t *ext_ok; + +    int etb_count; +    edid_output etbs[24]; + +    int std_count; +    struct edid_std stds[8]; + +    int dtd_count; +    struct edid_dtd *dtds; + +    int cea_block_count; +    struct edid_cea_block *cea_blocks; + +    int svd_count; +    struct edid_svd *svds; + +    int sad_count; +    struct edid_sad *sads; + +    edid_ven ven; +    struct edid_descriptor d[4]; +    /* point into d[].text */ +    char *name; +    char *serial; +    char *ut1; +    char *ut2; + +    uint8_t a_or_d; /* 0 = analog, 1 = digital */ +    uint8_t interface; /* digital interface */ +    uint8_t bpc;       /* digital bpc */ +    uint16_t product; +    uint32_t n_serial; +    struct edid_manf_date dom; +    edid_output img; +    edid_output img_svd; +    edid_output img_max; +    uint32_t speaker_alloc_bits; + +    DIExtData di; + +    DisplayIDMeta did; +    int did_block_count; +    DisplayIDBlock *did_blocks; +    int did_string_count; +    DisplayIDString *did_strings; + +    int didt_count; +    edid_output *didts; + +    GString *msg_log; +} edid; +edid *edid_new(const char *data, unsigned int len); +edid *edid_new_from_hex(const char *hex_string); +edid *edid_new_from_file(const char *path); +void edid_free(edid *e); +char *edid_dump_hex(edid *e, int tabs, int breaks); + +const char *edid_standard(int std); +const char *edid_output_src(int src); +const char *edid_interface(int type); +const char *edid_di_interface(int type); +const char *edid_descriptor_type(int type); +const char *edid_ext_block_type(int type); +const char *edid_cea_block_type(int type); +const char *edid_cea_audio_type(int type); + +char *edid_output_describe(edid_output *out); +char *edid_base_descriptor_describe(struct edid_descriptor *d); +char *edid_dtd_describe(struct edid_dtd *dtd, int dump_bytes); +char *edid_cea_block_describe(struct edid_cea_block *blk); +char *edid_cea_audio_describe(struct edid_sad *sad); +char *edid_cea_speaker_allocation_describe(int bitfield, int short_version); +const char *edid_did_block_type(int type); +char *edid_did_block_describe(DisplayIDBlock *blk); + +char *edid_dump2(edid *e); + +#endif diff --git a/deps/sysobj_early/include/util_ids.h b/deps/sysobj_early/include/util_ids.h new file mode 100644 index 00000000..c5dccfe7 --- /dev/null +++ b/deps/sysobj_early/include/util_ids.h @@ -0,0 +1,76 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#ifndef _UTIL_IDS_H_ +#define _UTIL_IDS_H_ + +#include <glib.h> + +#define IDS_LOOKUP_BUFF_SIZE 220 +#define IDS_LOOKUP_MAX_DEPTH 4 + +/* may be static, all results[] are NULL or point into _strs */ +typedef struct { +    gchar *results[IDS_LOOKUP_MAX_DEPTH+1]; /* last always NULL */ +    gchar _strs[IDS_LOOKUP_BUFF_SIZE*IDS_LOOKUP_MAX_DEPTH]; +} ids_query_result; +#define ids_query_result_new() g_new0(ids_query_result, 1) +#define ids_query_result_free(s) g_free(s); +void ids_query_result_cpy(ids_query_result *dest, ids_query_result *src); + +/* Given a qpath "/X/Y/Z", find names as: + * X <name> ->result[0] + * \tY <name> ->result[1] + * \t\tZ <name> ->result[2] + * + * Works with: + * - pci.ids "<vendor>/<device>/<subvendor> <subdevice>" or "C <class>/<subclass>/<prog-if>" + *     ... need to query "<subvendor>" separately + * - arm.ids "<implementer>/<part>" + * - sdio.ids "<vendor>/<device>", "C <class>" + * - sdcard.ids "OEMID <code>", "MANFID <code>" + * - usb.ids "<vendor>/<device>", "C <class>" etc. + * - edid.ids "<3letter_vendor>" + */ +long scan_ids_file(const gchar *file, const gchar *qpath, ids_query_result *result, long start_offset); + +typedef struct { +    gchar *qpath; +    ids_query_result result; +} ids_query; + +ids_query *ids_query_new(const gchar *qpath); +void ids_query_free(ids_query *s); +typedef GSList* ids_query_list; + +/* query_list is a GSList of ids_query* */ +long scan_ids_file_list(const gchar *file, ids_query_list query_list, long start_offset); +/* after scan_ids_file_list(), count hits */ +int query_list_count_found(ids_query_list query_list); + +/* returns GSList of ids_query* */ +typedef gchar* (*split_loc_function)(const char *line); +ids_query_list ids_file_all_get_all(const gchar *file, split_loc_function split_loc_func); + +/* debugging */ +void ids_trace_start(); +void ids_trace_stop(); + +#endif diff --git a/deps/sysobj_early/include/util_sysobj.h b/deps/sysobj_early/include/util_sysobj.h new file mode 100644 index 00000000..1bff3d5a --- /dev/null +++ b/deps/sysobj_early/include/util_sysobj.h @@ -0,0 +1,53 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#ifndef _UTIL_SYSOBJ_H_ +#define _UTIL_SYSOBJ_H_ + +#include <glib.h> +#include "appf.h" + +/* string eq */ +#define SEQ(s1, s2) (g_strcmp0((s1), (s2)) == 0) + +/* handy for static halp */ +#define BULLET "\u2022" +#define REFLINK(URI) "<a href=\"" URI "\">" URI "</a>" +#define REFLINKT(TEXT, URI) "<a href=\"" URI "\">" TEXT "</a>" + +gboolean util_have_root(); +void util_null_trailing_slash(gchar *str); /* in-place */ +void util_compress_space(gchar *str); /* in-place, multiple whitespace replaced by one space */ +void util_strstrip_double_quotes_dumb(gchar *str); /* in-place, strips any double-quotes from the start and end of str */ +gchar *util_build_fn(const gchar *base, const gchar *name); /* returns "<base>[/<name>]" */ +gchar *util_canonicalize_path(const gchar *path); /* resolve . and .., but not symlinks */ +gchar *util_normalize_path(const gchar *path, const gchar *relto); /* resolve . and .., and symlinks */ +gsize util_count_lines(const gchar *str); /* doesn't count empty last line */ +gchar *util_escape_markup(gchar *v, gboolean replacing); +int util_get_did(gchar *str, const gchar *lbl); /* ("cpu6", "cpu") -> 6, returns -1 if error */ +int util_maybe_num(gchar *str); /* returns the guessed base, 0 for not num */ +gchar *util_find_line_value(gchar *data, gchar *key, gchar delim); +gchar *util_strchomp_float(gchar* str_float); /* in-place, must use , or . for decimal sep */ +gchar *util_safe_name(const gchar *name, gboolean lower_case); /* make a string into a name nice and safe for file name */ + +/* to quiet -Wunused-parameter nagging.  */ +#define PARAM_NOT_UNUSED(p) (void)p + +#endif diff --git a/deps/sysobj_early/src/appf.c b/deps/sysobj_early/src/appf.c new file mode 100644 index 00000000..8e6991e3 --- /dev/null +++ b/deps/sysobj_early/src/appf.c @@ -0,0 +1,63 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#define _GNU_SOURCE /* for vasprintf() */ +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include "appf.h" + +char *appf(char *str, const char *sep, const char *fmt, ...) { +    char *buf = NULL; +    int inlen, seplen, len; +    va_list args; +    va_start(args, fmt); +    len = vasprintf(&buf, fmt, args); +    va_end(args); +    if (len < 0) return str; +    if (!str) return buf; +    inlen = strlen(str); +    seplen = (inlen && sep) ? strlen(sep) : 0; +    str = realloc(str, inlen + seplen + len + 1); +    if (seplen) strcpy(str + inlen, sep); +    strcpy(str + inlen + seplen, buf); +    free(buf); +    return str; +} + +char *appfdup(const char *str, const char *sep, const char *fmt, ...) { +    char *buf = NULL, *ret = NULL; +    int inlen, seplen, len; +    va_list args; +    va_start(args, fmt); +    len = vasprintf(&buf, fmt, args); +    va_end(args); +    if (len < 0) return NULL; +    if (!str) return buf; +    inlen = strlen(str); +    seplen = (inlen && sep) ? strlen(sep) : 0; +    ret = malloc(inlen + seplen + len + 1); +    strcpy(ret, str); +    if (seplen) strcpy(ret + inlen, sep); +    strcpy(ret + inlen + seplen, buf); +    free(buf); +    return ret; +} diff --git a/deps/sysobj_early/src/auto_free.c b/deps/sysobj_early/src/auto_free.c new file mode 100644 index 00000000..9a10e27a --- /dev/null +++ b/deps/sysobj_early/src/auto_free.c @@ -0,0 +1,219 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#include "auto_free.h" +#if (AF_USE_SYSOBJ) +#include "sysobj.h" +#else +#include <stdio.h> +#define sysobj_elapsed() af_elapsed() +#define sysobj_stats af_stats +static struct { +    double auto_free_next; +    unsigned long long +        auto_freed, +        auto_free_len; +} af_stats; +#endif + +//Compatibility +#ifndef G_SOURCE_REMOVE +  #define G_SOURCE_REMOVE FALSE +#endif + +#ifndef G_SOURCE_CONTINUE +  #define G_SOURCE_CONTINUE TRUE +#endif + + +static GMutex *free_lock = NULL; +static GSList *free_list = NULL; +static gboolean free_final = FALSE; +static GTimer *auto_free_timer = NULL; +static guint free_event_source = 0; +#define af_elapsed() (auto_free_timer ? g_timer_elapsed(auto_free_timer, NULL) : 0) + +#define auto_free_msg(msg, ...)  fprintf (stderr, "[%s] " msg "\n", __FUNCTION__, ##__VA_ARGS__) /**/ + +typedef struct { +    gpointer ptr; +    GThread *thread; +    GDestroyNotify f_free; +    double stamp; + +    const char *file; +    int  line; +    const char *func; +} auto_free_item; + +gboolean free_auto_free_sf(gpointer trash) { +    (void)trash; +    if (free_final) { +        free_event_source = 0; +        return G_SOURCE_REMOVE; +    } +    free_auto_free(); +    sysobj_stats.auto_free_next = sysobj_elapsed() + AF_SECONDS; +    return G_SOURCE_CONTINUE; +} + +gpointer auto_free_ex_(gpointer p, GDestroyNotify f, const char *file, int line, const char *func) { +    if (!p) return p; + +    /* an auto_free() after free_auto_free_final()? +     * Changed mind, I guess, just go with it. */ +    if (free_final) +        free_final = FALSE; + +    if (!auto_free_timer) { +        auto_free_timer = g_timer_new(); +        g_timer_start(auto_free_timer); +    } + +    if (!free_event_source) { +        /* if there is a main loop, then this will call +         * free_auto_free() in idle time every AF_SECONDS seconds. +         * If there is no main loop, then free_auto_free() +         * will be called at sysobj_cleanup() and when exiting +         * threads, as in sysobj_foreach(). */ +        free_event_source = g_timeout_add_seconds(AF_SECONDS, (GSourceFunc)free_auto_free_sf, NULL); +        sysobj_stats.auto_free_next = sysobj_elapsed() + AF_SECONDS; +    } + +    auto_free_item *z = g_new0(auto_free_item, 1); +    z->ptr = p; +    z->f_free = f; +    z->thread = g_thread_self(); +    z->file = file; +    z->line = line; +    z->func = func; +    z->stamp = af_elapsed(); +#if GLIB_CHECK_VERSION(2,32,0) +    if(free_lock==NULL) {free_lock=g_new(GMutex,1);g_mutex_init(free_lock);} +#else +    if(free_lock==NULL) free_lock=g_mutex_new(); +#endif +    g_mutex_lock(free_lock); +    free_list = g_slist_prepend(free_list, z); +    sysobj_stats.auto_free_len++; +    g_mutex_unlock(free_lock); +    return p; +} + +gpointer auto_free_on_exit_ex_(gpointer p, GDestroyNotify f, const char *file, int line, const char *func) { +    if (!p) return p; + +    auto_free_item *z = g_new0(auto_free_item, 1); +    z->ptr = p; +    z->f_free = f; +    z->thread = g_thread_self(); +    z->file = file; +    z->line = line; +    z->func = func; +    z->stamp = -1.0; +#if GLIB_CHECK_VERSION(2,32,0) +    if(free_lock==NULL) g_mutex_init(free_lock); +#else +    if(free_lock==NULL) free_lock=g_mutex_new(); +#endif +    g_mutex_lock(free_lock); +    free_list = g_slist_prepend(free_list, z); +    sysobj_stats.auto_free_len++; +    g_mutex_unlock(free_lock); +    return p; +} + +static struct { GDestroyNotify fptr; char *name; } +    free_function_tab[] = { +    { (GDestroyNotify) g_free,             "g_free" }, +#if (AF_USE_SYSOBJ) +    { (GDestroyNotify) sysobj_free,        "sysobj_free" }, +    { (GDestroyNotify) class_free,         "class_free" }, +    { (GDestroyNotify) sysobj_filter_free, "sysobj_filter_free" }, +    { (GDestroyNotify) sysobj_virt_free,   "sysobj_virt_free" }, +#endif +    { NULL, "(null)" }, +}; + +static void free_auto_free_ex(gboolean thread_final) { +    GThread *this_thread = g_thread_self(); +    GSList *l = NULL, *n = NULL; +    long long unsigned fc = 0; +    double now = af_elapsed(); + +    if (!free_list) return; + +#if GLIB_CHECK_VERSION(2,32,0) +    if(free_lock==NULL) {free_lock=g_new(GMutex,1);g_mutex_init(free_lock);} +#else +    if(free_lock==NULL) free_lock=g_mutex_new(); +#endif +    g_mutex_lock(free_lock); +    if (DEBUG_AUTO_FREE) +        auto_free_msg("%llu total items in queue, but will free from thread %p only... ", sysobj_stats.auto_free_len, this_thread); +    for(l = free_list; l; l = n) { +        auto_free_item *z = (auto_free_item*)l->data; +        n = l->next; +        if (!free_final && z->stamp < 0) continue; +        double age = now - z->stamp; +        if (free_final || (z->thread == this_thread && (thread_final || age > AF_DELAY_SECONDS) ) ) { +            if (DEBUG_AUTO_FREE == 2) { +                char fptr[128] = "", *fname; +                for(int i = 0; i < (int)G_N_ELEMENTS(free_function_tab); i++) +                    if (z->f_free == free_function_tab[i].fptr) +                        fname = free_function_tab[i].name; +                if (!fname) { +                    snprintf(fname, 127, "%p", z->f_free); +                    fname = fptr; +                } +                if (z->file || z->func) +                    auto_free_msg("free: %s(%p) age:%lfs from %s:%d %s()", fname, z->ptr, age, z->file, z->line, z->func); +                else +                    auto_free_msg("free: %s(%p) age:%lfs", fname, z->ptr, age); +            } + +            z->f_free(z->ptr); +            g_free(z); +            free_list = g_slist_delete_link(free_list, l); +            fc++; +        } +    } +    if (DEBUG_AUTO_FREE) +        auto_free_msg("... freed %llu (from thread %p)", fc, this_thread); +    sysobj_stats.auto_freed += fc; +    sysobj_stats.auto_free_len -= fc; +    g_mutex_unlock(free_lock); +} + +void free_auto_free_thread_final() { +    free_auto_free_ex(TRUE); +} + +void free_auto_free_final() { +    free_final = TRUE; +    free_auto_free_ex(TRUE); +    if (auto_free_timer) +        g_timer_destroy(auto_free_timer); +    auto_free_timer = NULL; +} + +void free_auto_free() { +    free_auto_free_ex(FALSE); +} diff --git a/deps/sysobj_early/src/cpubits.c b/deps/sysobj_early/src/cpubits.c new file mode 100644 index 00000000..fe8ba207 --- /dev/null +++ b/deps/sysobj_early/src/cpubits.c @@ -0,0 +1,132 @@ +/* + * rpiz - https://github.com/bp0/rpiz + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#define _GNU_SOURCE +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "cpubits.h" + +uint32_t cpubits_count(cpubits *b) { +    static const uint32_t max = CPUBITS_SIZE * 8; +    uint32_t count = 0, i = 0; +    while (i < max) { +        count += CPUBIT_GET(b, i); +        i++; +    } +    return count; +} + +int cpubits_min(cpubits *b) { +    int i = 0; +    while (i < CPUBITS_SIZE * 8) { +        if (CPUBIT_GET(b, i)) +            return i; +        i++; +    } +    return -1; +} + +int cpubits_max(cpubits *b) { +    int i = CPUBITS_SIZE * 8 - 1; +    while (i >= 0) { +        if (CPUBIT_GET(b, i)) +            return i; +        i--; +    } +    return i; +} + +int cpubits_next(cpubits *b, int start, int end) { +    start++; /* not including the start bit */ +    if (start >= 0) { +        int i = start; +        if (end == -1) +            end = CPUBITS_SIZE * 8; +        while (i < end) { +            if (CPUBIT_GET(b, i)) +                return i; +            i++; +        } +    } +    return -1; +} + +cpubits *cpubits_from_str(char *str) { +    char *v, *nv, *hy; +    int r0, r1; +    cpubits *newbits = malloc(CPUBITS_SIZE); +    if (newbits) { +        memset(newbits, 0, CPUBITS_SIZE); +        if (str != NULL) { +            v = (char*)str; +            while ( *v != 0 ) { +                nv = strchr(v, ',');                /* strchrnul() */ +                if (nv == NULL) nv = strchr(v, 0);  /* equivalent  */ +                hy = strchr(v, '-'); +                if (hy && hy < nv) { +                    r0 = strtol(v, NULL, 0); +                    r1 = strtol(hy + 1, NULL, 0); +                } else { +                    r0 = r1 = strtol(v, NULL, 0); +                } +                for (; r0 <= r1; r0++) { +                    CPUBIT_SET(newbits, r0); +                } +                v = (*nv == ',') ? nv + 1 : nv; +            } +        } +    } +    return newbits; +} + +char *cpubits_to_str(cpubits *bits, char *str, int max_len) { +    static const uint32_t max = CPUBITS_SIZE * 8; +    uint32_t i = 1, seq_start = 0, seq_last = 0, seq = 0, l = 0; +    char buffer[65536] = ""; +    if (CPUBIT_GET(bits, 0)) { +        seq = 1; +        strcpy(buffer, "0"); +    } +    while (i < max) { +        if (CPUBIT_GET(bits, i) ) { +            seq_last = i; +            if (!seq) { +                seq = 1; +                seq_start = i; +                l = strlen(buffer); +                sprintf(buffer + l, "%s%d", l ? "," : "", i); +            } +        } else { +            if (seq && seq_last != seq_start) { +                l = strlen(buffer); +                sprintf(buffer + l, "-%d", seq_last); +            } +            seq = 0; +        } +        i++; +    } +    if (str == NULL) +        return strdup(buffer); +    else { +        strncpy(str, buffer, max_len); +        return str; +    } +} diff --git a/deps/sysobj_early/src/format_early.c b/deps/sysobj_early/src/format_early.c new file mode 100644 index 00000000..0220cd8d --- /dev/null +++ b/deps/sysobj_early/src/format_early.c @@ -0,0 +1,160 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#include "format_early.h" +#include <string.h> +#include <stdlib.h> + +#define ANSI_COLOR_RESET   "\x1b[0m" + +const gchar *color_lookup(int ansi_color) { +    static struct { int ansi; const gchar *html; } tab[] = { +        { 30,  "#010101" }, { 31,  "#de382b" }, { 32,  "#39b54a" }, { 33,  "#ffc706" }, +        { 34,  "#006fb8" }, { 35,  "#762671" }, { 36,  "#2cb5e9" }, { 37,  "#cccccc" }, +        { 90,  "#808080" }, { 91,  "#ff0000" }, { 92,  "#00ff00" }, { 93,  "#ffff00" }, +        { 94,  "#0000ff" }, { 95,  "#ff00ff" }, { 96,  "#00ffff" }, { 97,  "#ffffff" }, +        { 40,  "#010101" }, { 41,  "#de382b" }, { 42,  "#39b54a" }, { 43,  "#ffc706" }, +        { 44,  "#006fb8" }, { 45,  "#762671" }, { 46,  "#2cb5e9" }, { 47,  "#cccccc" }, +        { 100, "#808080" }, { 101, "#ff0000" }, { 102, "#00ff00" }, { 103, "#ffff00" }, +        { 104, "#0000ff" }, { 105, "#ff00ff" }, { 106, "#00ffff" }, { 107, "#ffffff" }, +    }; +    for (int i = 0; i<(int)G_N_ELEMENTS(tab); i++) +        if (tab[i].ansi == ansi_color) +            return tab[i].html; +    return NULL; +} + +gchar *safe_ansi_color(gchar *ansi_color, gboolean free_in) { +    if (!ansi_color) return NULL; +    gchar *ret = NULL; +    gchar **codes = g_strsplit(ansi_color, ";", -1); +    if (free_in) +        g_free(ansi_color); +    int len = g_strv_length(codes); +    for(int i = 0; i < len; i++) { +        int c = atoi(codes[i]); +        if (c == 0 || c == 1 +            || ( c >= 30 && c <= 37) +            || ( c >= 40 && c <= 47) +            || ( c >= 90 && c <= 97) +            || ( c >= 100 && c <= 107) ) { +                ret = appf(ret, ";", "%s", codes[i]); +        } +    } +    g_strfreev(codes); +    return ret; +} + +gchar *format_with_ansi_color(const gchar *str, const gchar *ansi_color, int fmt_opts) { +    gchar *ret = NULL; + +    gchar *safe_color = g_strdup(ansi_color); +    util_strstrip_double_quotes_dumb(safe_color); + +    if (fmt_opts & FMT_OPT_ATERM) { +        safe_color = safe_ansi_color(safe_color, TRUE); +        ret = g_strdup_printf("\x1b[%sm%s" ANSI_COLOR_RESET, safe_color, str); +        goto format_with_ansi_color_end; +    } + +    if (fmt_opts & FMT_OPT_PANGO || fmt_opts & FMT_OPT_HTML) { +        int fgc = 37, bgc = 40; +        gchar **codes = g_strsplit(safe_color, ";", -1); +        int len = g_strv_length(codes); +        for(int i = 0; i < len; i++) { +            int c = atoi(codes[i]); +            if ( (c >= 30 && c <= 37) +               || ( c >= 90 && c <= 97 ) ) { +                fgc = c; +            } +            if ( (c >= 40 && c <= 47) +               || ( c >= 100 && c <= 107) ) { +                bgc = c; +            } +        } +        g_strfreev(codes); +        const gchar *html_color_fg = color_lookup(fgc); +        const gchar *html_color_bg = color_lookup(bgc); +        if (fmt_opts & FMT_OPT_PANGO) +            ret = g_strdup_printf("<span background=\"%s\" color=\"%s\"><b> %s </b></span>", html_color_bg, html_color_fg, str); +        else if (fmt_opts & FMT_OPT_HTML) +            ret = g_strdup_printf("<span style=\"background-color: %s; color: %s;\"><b> %s </b></span>", html_color_bg, html_color_fg, str); +    } + +format_with_ansi_color_end: +    g_free(safe_color); +    if (!ret) +        ret = g_strdup(str); +    return ret; +} + +void tag_vendor(gchar **str, guint offset, const gchar *vendor_str, const char *ansi_color, int fmt_opts) { +    if (!str || !*str) return; +    if (!vendor_str || !ansi_color) return; +    gchar *work = *str, *new = NULL; +    if (g_str_has_prefix(work + offset, vendor_str) +        || strncasecmp(work + offset, vendor_str, strlen(vendor_str)) == 0) { +        gchar *cvs = format_with_ansi_color(vendor_str, ansi_color, fmt_opts); +        *(work+offset) = 0; +        new = g_strdup_printf("%s%s%s", work, cvs, work + offset + strlen(vendor_str) ); +        g_free(work); +        *str = new; +        g_free(cvs); +    } +} + +gchar *vendor_match_tag(const gchar *vendor_str, int fmt_opts) { +    const Vendor *v = vendor_match(vendor_str, NULL); +    if (v) { +        gchar *ven_tag = v->name_short ? g_strdup(v->name_short) : g_strdup(v->name); +        tag_vendor(&ven_tag, 0, ven_tag, v->ansi_color, fmt_opts); +        return ven_tag; +    } +    return NULL; +} + +gchar *vendor_list_ribbon(const vendor_list vl_in, int fmt_opts) { +    gchar *ret = NULL; +    vendor_list vl = g_slist_copy(vl_in); /* shallow is fine */ +    vl = vendor_list_remove_duplicates(vl); +    if (vl) { +        GSList *l = vl, *n = l ? l->next : NULL; +        /* replace each vendor with the vendor tag */ +        for(; l; l = n) { +            n = l->next; +            const Vendor *v = l->data; +            if (!v) { +                vl = g_slist_delete_link(vl, l); +                continue; +            } +            gchar *ven_tag = v->name_short ? g_strdup(v->name_short) : g_strdup(v->name); +            if(ven_tag) { +                tag_vendor(&ven_tag, 0, ven_tag, v->ansi_color, fmt_opts); +                l->data = ven_tag; +            } +        } +        /* vl is now a regular GSList of formatted vendor tag strings */ +        vl = gg_slist_remove_duplicates_custom(vl, (GCompareFunc)g_strcmp0); +        for(l = vl; l; l = l->next) +            ret = appfsp(ret, "%s", (gchar*)l->data); +    } +    g_slist_free_full(vl, g_free); +    return ret; +} diff --git a/deps/sysobj_early/src/gg_slist.c b/deps/sysobj_early/src/gg_slist.c new file mode 100644 index 00000000..595dbc46 --- /dev/null +++ b/deps/sysobj_early/src/gg_slist.c @@ -0,0 +1,49 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#include <glib.h> + +GSList *gg_slist_remove_duplicates(GSList *sl) { +    for (GSList *l = sl; l; l = l->next) { +        GSList *d = NULL; +        while(d = g_slist_find(l->next, l->data) ) +            sl = g_slist_delete_link(sl, d); +    } +    return sl; +} + +GSList *gg_slist_remove_duplicates_custom(GSList *sl, GCompareFunc func) { +    for (GSList *l = sl; l; l = l->next) { +        GSList *d = NULL; +        while(d = g_slist_find_custom(l->next, l->data, func) ) +            sl = g_slist_delete_link(sl, d); +    } +    return sl; +} + +GSList *gg_slist_remove_null(GSList *sl) { +    GSList *n = sl ? sl->next : NULL; +    for (GSList *l = sl; l; l = n) { +        n = l->next; +        if (l->data == NULL) +            sl = g_slist_delete_link(sl, l); +    } +    return sl; +} diff --git a/deps/sysobj_early/src/nice_name.c b/deps/sysobj_early/src/nice_name.c new file mode 100644 index 00000000..e5e15a46 --- /dev/null +++ b/deps/sysobj_early/src/nice_name.c @@ -0,0 +1,203 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#define _GNU_SOURCE +#include <string.h> +#include <ctype.h> + +#include "nice_name.h" +#include "util_sysobj.h" + +/* export */ +/* replaces the extra chars with spaces, then when done with a series of + * str_shorten()s, use util_compress_space() to squeeze. */ +gboolean str_shorten(gchar *str, const gchar *find, const gchar *replace) { +    if (!str || !find || !replace) return FALSE; +    long unsigned lf = strlen(find); +    long unsigned lr = strlen(replace); +    gchar *p = strstr(str, find); +    if (p) { +        if (lr > lf) lr = lf; +        gchar *buff = g_strnfill(lf, ' '); +        strncpy(buff, replace, lr); +        strncpy(p, buff, lf); +        g_free(buff); +        return TRUE; +    } +    return FALSE; +} + +gboolean str_shorten_anycase(gchar *str, const gchar *find, const gchar *replace) { +    if (!str || !find || !replace) return FALSE; +    long unsigned lf = strlen(find); +    long unsigned lr = strlen(replace); +    gchar *p = strcasestr(str, find); +    if (p) { +        if (lr > lf) lr = lf; +        gchar *buff = g_strnfill(lf, ' '); +        strncpy(buff, replace, lr); +        strncpy(p, buff, lf); +        g_free(buff); +        return TRUE; +    } +    return FALSE; +} + +void nice_name_x86_cpuid_model_string(char *cpuid_model_string) { +    static gboolean move_vendor_to_front = TRUE; +    static gboolean remove_long_core_count = TRUE; +    static gboolean remove_amd_compute_cores = TRUE; +    static gboolean remove_amd_xn_ncore_redundancy = TRUE; +    static gboolean remove_processor_cpu_apu_etc = TRUE; +    static gboolean remove_mhz_ghz = TRUE; +    static gboolean remove_radeon = TRUE; + +    if (!cpuid_model_string) return; +    g_strstrip(cpuid_model_string); + +    while(str_shorten(cpuid_model_string, "Genuine Intel", "Intel")) {}; +    while(str_shorten_anycase(cpuid_model_string, "(R)", "")) {}; +    while(str_shorten_anycase(cpuid_model_string, "(TM)", "")) {}; +    while(str_shorten_anycase(cpuid_model_string, "(C)", "")) {}; +    while(str_shorten(cpuid_model_string, "@", "")) {}; + +    if (move_vendor_to_front) { +        /* vendor not at the beginning, try to move there. +         * ex: Mobile AMD Sempron(tm) Processor 3600+ +         * ex: Dual Core AMD Opteron(tm) Processor 165 */ +        char *intel = strstr(cpuid_model_string, "Intel "); +        char *amd = strstr(cpuid_model_string, "AMD "); +        if (amd || intel) { +            if (amd && !intel) { +                if (amd != cpuid_model_string) { +                    int l = amd - cpuid_model_string; +                    memmove(cpuid_model_string+4, cpuid_model_string, l); +                    memcpy(cpuid_model_string, "AMD ", 4); +                } +            } else if (intel && !amd) { +                int l = intel - cpuid_model_string; +                memmove(cpuid_model_string+6, cpuid_model_string, l); +                memcpy(cpuid_model_string, "Intel ", 6); +            } +        } +    } + +    if (g_str_has_prefix(cpuid_model_string, "AMD")) { +        while(str_shorten(cpuid_model_string, "Mobile Technology", "Mobile")) {}; + +        if (remove_radeon) { +            char *radeon = strcasestr(cpuid_model_string, "with Radeon"); +            if (!radeon) +                radeon = strcasestr(cpuid_model_string, "Radeon"); +            if (radeon) *radeon = 0; +        } + +        if (remove_amd_compute_cores) { +            if (strcasestr(cpuid_model_string, " COMPUTE CORES ")) { +                /* ex: AMD FX-9800P RADEON R7, 12 COMPUTE CORES 4C+8G */ +                char *comma = strchr(cpuid_model_string, ','); +                if (comma) *comma = 0; +            } +        } + +        if (remove_amd_xn_ncore_redundancy) { +            /* remove Xn n-core redundancy */ +            if (strstr(cpuid_model_string, "X2")) { +                str_shorten(cpuid_model_string, "Dual Core", ""); +                str_shorten(cpuid_model_string, "Dual-Core", ""); +            } +            if (strstr(cpuid_model_string, "X3")) +                str_shorten(cpuid_model_string, "Triple-Core", ""); +            if (strstr(cpuid_model_string, "X4")) +                str_shorten(cpuid_model_string, "Quad-Core", ""); +        } +    } + +    if (g_str_has_prefix(cpuid_model_string, "Cyrix")) { +        /* ex: Cyrix MediaGXtm MMXtm Enhanced */ +        while(str_shorten(cpuid_model_string, "tm ", "")) {}; +    } + +    if (remove_processor_cpu_apu_etc) { +        while(str_shorten(cpuid_model_string, " CPU", "")) {}; +        while(str_shorten(cpuid_model_string, " APU", "")) {}; +        while(str_shorten_anycase(cpuid_model_string, " Integrated Processor", "")) {}; +        while(str_shorten_anycase(cpuid_model_string, " Processor", "")) {}; +    } else { +        while(str_shorten(cpuid_model_string, " processor", " Processor")) {}; +    } + +    if (remove_mhz_ghz) { +        /* 1400MHz, 1.6+ GHz, etc */ +        char *u = NULL; +        while((u = strcasestr(cpuid_model_string, "GHz")) +            || (u = strcasestr(cpuid_model_string, "MHz")) ) { +            if (u[3] == '+') u[3] = ' '; +            strncpy(u, " ", 3); +            while(isspace(*u)) {u--;} +            while (isdigit(*u) || *u == '.' || *u == '+') +                { *u = ' '; u--;} +        } +    } + +    if (remove_long_core_count) { +        /* note: "Intel Core 2 Duo" and "Intel Core 2 Quad" +         * are the marketing names, don't remove those. */ +        static char *count_strings[] = { +            "Dual", "Triple", "Quad", "Six", +            "Eight", "Octal", "Twelve", "Sixteen", +            "8", "16", "24", "32", "48", "56", "64", +        }; +        char buffer[] = "Enough room for the longest... -Core"; +        char *dash = strchr(buffer, '-'); +        char *m = NULL; + +        unsigned int i = 0; +        for(; i < G_N_ELEMENTS(count_strings); i++) { +            int l = strlen(count_strings[i]); +            m = dash-l; +            memcpy(m, count_strings[i], l); +            *dash = '-'; +            while(str_shorten_anycase(cpuid_model_string, m, "")) {}; +            *dash = ' '; +            while(str_shorten_anycase(cpuid_model_string, m, "")) {}; +        } +    } + +    /* finalize */ +    util_compress_space(cpuid_model_string); +    g_strstrip(cpuid_model_string); +} + +/* Intel Graphics may have very long names, + * like "Intel Corporation Seventh Generation Something Core Something Something Integrated Graphics Processor Revision Ninety-four" */ +void nice_name_intel_gpu_device(char *pci_ids_device_string) { +    while(str_shorten_anycase(pci_ids_device_string, "(R)", "")) {}; /* Intel(R) -> Intel */ +    str_shorten(pci_ids_device_string, "Graphics Controller", "Graphics"); +    str_shorten(pci_ids_device_string, "Graphics Device", "Graphics"); +    str_shorten(pci_ids_device_string, "Generation", "Gen"); +    str_shorten(pci_ids_device_string, "Core Processor", "Core"); +    str_shorten(pci_ids_device_string, "Atom Processor", "Atom"); +    str_shorten(pci_ids_device_string, "Xeon Processor", "Xeon"); +    str_shorten(pci_ids_device_string, "Celeron Processor", "Celeron"); +    str_shorten(pci_ids_device_string, "Pentium Processor", "Pentium"); +    util_compress_space(pci_ids_device_string); +    g_strstrip(pci_ids_device_string); +} diff --git a/deps/sysobj_early/src/strstr_word.c b/deps/sysobj_early/src/strstr_word.c new file mode 100644 index 00000000..0b51e4ac --- /dev/null +++ b/deps/sysobj_early/src/strstr_word.c @@ -0,0 +1,80 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +/* versions of strstr() and strcasestr() where the match must be preceded and + * succeded by a non-alpha-numeric character. */ + +#define _GNU_SOURCE +#include <string.h> +#include <ctype.h> + +static char *_strstr(const char *haystack, const char *needle, int anycase) { +    return anycase +        ? strcasestr(haystack, needle) +        : strstr(haystack, needle); +} + +static char *_strstr_word(const char *haystack, const char *needle, +                          int anycase, int prefix_ok, int suffix_ok) { + +    if (!haystack || !needle) +        return NULL; + +    char *c; +    const char *p = haystack; +    size_t l = strlen(needle); +    while((c = _strstr(p, needle, anycase))) { +        const char *before = (c == haystack) ? NULL : c-1; +        const char *after = c + l; +        int ok = 1, wbs = 1, wbe = 1; +        if (isalnum(*after)) wbe = 0; +        if (before && isalnum(*before)) wbs = 0; +        if (!wbe && !prefix_ok) ok = 0; +        if (!wbs && !suffix_ok) ok = 0; +        if (!(wbs || wbe)) ok = 0; +        if (ok) return c; +        p++; +    } +    return NULL; +} + +char *strstr_word(const char *haystack, const char *needle) { +    return _strstr_word(haystack, needle, 0, 0, 0); +} + +char *strcasestr_word(const char *haystack, const char *needle) { +    return _strstr_word(haystack, needle, 1, 0, 0); +} + +char *strstr_word_prefix(const char *haystack, const char *needle) { +    return _strstr_word(haystack, needle, 0, 1, 0); +} + +char *strcasestr_word_prefix(const char *haystack, const char *needle) { +    return _strstr_word(haystack, needle, 1, 1, 0); +} + +char *strstr_word_suffix(const char *haystack, const char *needle) { +    return _strstr_word(haystack, needle, 0, 0, 1); +} + +char *strcasestr_word_suffix(const char *haystack, const char *needle) { +    return _strstr_word(haystack, needle, 1, 0, 1); +} diff --git a/deps/sysobj_early/src/util_edid.c b/deps/sysobj_early/src/util_edid.c new file mode 100644 index 00000000..06c5534e --- /dev/null +++ b/deps/sysobj_early/src/util_edid.c @@ -0,0 +1,1470 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#define _GNU_SOURCE +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#include <endian.h> +#include <stdio.h> +#include "gettext.h" +#include "util_edid.h" +#include "util_sysobj.h" + +#include "util_edid_svd_table.c" + +// TODO: find a better fix, I've seen a few EDID strings with bogus chars +#if !GLIB_CHECK_VERSION(2,52,0) +__attribute__ ((weak)) +gchar *g_utf8_make_valid(const gchar *s, const gssize l) { +    if (l < 0) +        return g_strdup(s); +    else +        return g_strndup(s, (gsize)l); +} +#endif + +#define NOMASK (~0U) +#define BFMASK(LSB, MASK) (MASK << LSB) + +#define DPTR(ADDY) (uint8_t*)(&((ADDY).e->u8[(ADDY).offset])) +#define OFMT "@%03d" /* for addy.offset */ + +#define EDID_MSG_STDERR 0 +#define edid_msg(e, msg, ...) {\ +    if (EDID_MSG_STDERR) fprintf (stderr, ">[%s;L%d] " msg "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__); \ +    g_string_append_printf(e->msg_log, "[%s;L%d] " msg "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__); } + +static int str_make_printable(char *str) { +    int rc = 0; +    char *p; +    for(p = str; *p; p++) { +        if (!isprint(*p)) { +            *p = '.'; +            rc++; +        } +    } +    return rc; +} + +static inline +uint32_t bf_value(uint32_t value, uint32_t mask) { +    uint32_t result = value & mask; +    if (result) +        while(!(mask & 1)) { +            result >>= 1; +            mask >>= 1; +        } +    return result; +} + +static inline +uint8_t bounds_check(edid *e, uint32_t offset) { +    if (!e) return 0; +    if (offset > e->len) return 0; +    return 1; +} + +static inline +char *rstr(edid *e, uint32_t offset, uint32_t len) { +    if (!bounds_check(e, offset+len)) return NULL; +    char *raw = malloc(len+1), *ret = NULL; +    strncpy(raw, (char*)&e->u8[offset], len); +    raw[len] = 0; +    ret = g_utf8_make_valid(raw, len); +    g_free(raw); +    return ret; +} + +static inline +char *rstr_strip(edid *e, uint32_t offset, uint32_t len) { +    if (!bounds_check(e, offset+len)) return NULL; +    char *raw = malloc(len+1), *ret = NULL; +    strncpy(raw, (char*)&e->u8[offset], len); +    raw[len] = 0; +    ret = g_strstrip(g_utf8_make_valid(raw, len)); +    g_free(raw); +    return ret; +} + +static inline +uint32_t r8(edid *e, uint32_t offset, uint32_t mask) { +    if (!bounds_check(e, offset)) return 0; +    return bf_value(e->u8[offset], mask); +} + +static inline +uint32_t r16le(edid *e, uint32_t offset, uint32_t mask) { +    if (!bounds_check(e, offset+1)) return 0; +    uint32_t v = (e->u8[offset+1] << 8) + e->u8[offset]; +    return bf_value(v, mask); +} + +static inline +uint32_t r16be(edid *e, uint32_t offset, uint32_t mask) { +    if (!bounds_check(e, offset+1)) return 0; +    uint32_t v = (e->u8[offset] << 8) + e->u8[offset+1]; +    return bf_value(v, mask); +} + +static inline +uint32_t r24le(edid *e, uint32_t offset, uint32_t mask) { +    if (!bounds_check(e, offset+2)) return 0; +    uint32_t v = (e->u8[offset+2] << 16) + (e->u8[offset+1] << 8) + e->u8[offset]; +    return bf_value(v, mask); +} + +static inline +uint32_t r24be(edid *e, uint32_t offset, uint32_t mask) { +    if (!bounds_check(e, offset+2)) return 0; +    uint32_t v = (e->u8[offset] << 16) + (e->u8[offset+1] << 8) + e->u8[offset+2]; +    return bf_value(v, mask); +} + +static inline +uint32_t r32le(edid *e, uint32_t offset, uint32_t mask) { +    if (!bounds_check(e, offset+3)) return 0; +    uint32_t v = (e->u8[offset+3] << 24) + (e->u8[offset+2] << 16) +        + (e->u8[offset+1] << 8) + e->u8[offset]; +    return bf_value(v, mask); +} + +static inline +uint32_t r32be(edid *e, uint32_t offset, uint32_t mask) { +    if (!bounds_check(e, offset+3)) return 0; +    uint32_t v = (e->u8[offset] << 24) + (e->u8[offset+1] << 16) +        + (e->u8[offset+2] << 8) + e->u8[offset+3]; +    return bf_value(v, mask); +} + +static inline +int rpnpcpy(edid_ven *dest, edid *e, uint32_t offset) { +    uint32_t pnp = r16be(e, offset, NOMASK); +    edid_ven ret = {.type = VEN_TYPE_INVALID}; +    if (pnp) { +        ret.type = VEN_TYPE_PNP; +        ret.pnp[2] = 64 + (pnp & 0x1f); +        ret.pnp[1] = 64 + ((pnp >> 5) & 0x1f); +        ret.pnp[0] = 64 + ((pnp >> 10) & 0x1f); +        *dest = ret; +        return 1; +    } +    return 0; +} + +static inline +int rouicpy(edid_ven *dest, edid *e, uint32_t offset) { +    edid_ven ret = {.type = VEN_TYPE_OUI}; +    ret.oui = r24le(e, offset, NOMASK); +    sprintf(ret.oui_str, "%02x%02x%02x", +        (ret.oui >> 16) & 0xff, +        (ret.oui >> 8) & 0xff, +            ret.oui & 0xff ); +    if (ret.oui) { +        *dest = ret; +        return 1; +    } +    return 0; +} + +static int _block_check_n(const void *bytes, int len) { +    if (!bytes) return 0; +    uint8_t sum = 0; +    uint8_t *data = (uint8_t*)bytes; +    int i; +    for(i=0; i<len; i++) +        sum += data[i]; +    return sum == 0 ? 1 : 0; +} + +static int block_check_n(edid *e, uint32_t offset, int len) { +    if (!bounds_check(e, offset+len)) return 0; +    return _block_check_n(e->u8, len); +} + +static int block_check(edid *e, uint32_t offset) { +    if (!bounds_check(e, offset+128)) return 0; +    return _block_check_n(e->u8, 128); +} + +static char *hex_bytes(uint8_t *bytes, int count) { +    char *buffer = malloc(count*3+1), *p = buffer; +    memset(buffer, 0, count*3+1); +    int i; +    for(i = 0; i < count; i++) { +        sprintf(p, "%02x ", (unsigned int)bytes[i]); +        p += 3; +    } +    return buffer; +} + +#define OUTPUT_CPY_SIZE(DEST, SRC)  \ +    (DEST).horiz_cm = (SRC).horiz_cm; \ +    (DEST).vert_cm = (SRC).vert_cm; \ +    (DEST).diag_cm = (SRC).diag_cm; \ +    (DEST).diag_in = (SRC).diag_in; \ +    edid_output_fill(&(DEST)); + +static void edid_output_fill(edid_output *out) { +    out->diag_cm = +        sqrt( (out->horiz_cm * out->horiz_cm) +         + (out->vert_cm * out->vert_cm) ); +    out->diag_in = out->diag_cm / 2.54; + +    if (out->is_interlaced) { +        if (out->vert_lines) +            out->vert_pixels = out->vert_lines * 2; +        else +            out->vert_lines = out->vert_pixels / 2; +    } else { +        if (out->vert_lines) +            out->vert_pixels = out->vert_lines; +        else +            out->vert_lines = out->vert_pixels; +    } + +    if (!out->vert_freq_hz && out->pixel_clock_khz) { +        uint64_t h = out->horiz_pixels + out->horiz_blanking; +        uint64_t v = out->vert_lines + out->vert_blanking; +        if (h && v) { +            uint64_t work = out->pixel_clock_khz * 1000; +            work /= (h*v); +            out->vert_freq_hz = work; +        } +    } + +    out->pixels = out->horiz_pixels; +    out->pixels *= out->vert_pixels; + +    if (out->diag_in) { +        static const char *inlbl = "\u2033"; /* double prime */ +        sprintf(out->class_inch, "%0.1f%s", out->diag_in, inlbl); +        util_strchomp_float(out->class_inch); +    } +} + +static void cea_block_decode(struct edid_cea_block *blk) { +    if (!blk) return; +    if (!blk->bounds_ok) +        blk->bounds_ok = +        bounds_check(blk->addy.e, blk->addy.offset + 1 + blk->len); +    if (!blk->bounds_ok) return; + +    edid *e = blk->addy.e; +    static uint32_t h = 1; /* header size */ +    uint32_t a = blk->addy.offset; /* start of block, includes header */ +    uint8_t *ptr = DPTR(blk->addy); +    int i; +    switch(blk->type) { +        case 0x1: /* SADS */ +            for(i = h; i <= blk->len; i+=3) { +                struct edid_sad *sad = &e->sads[e->sad_count]; +                sad->v[0] = ptr[i]; +                sad->v[1] = ptr[i+1]; +                sad->v[2] = ptr[i+2]; +                sad->format = bf_value(sad->v[0], 0x78); +                sad->channels = 1 + bf_value(sad->v[0], 0x07); +                sad->freq_bits = sad->v[1]; +                if (sad->format == 1) { +                    sad->depth_bits = sad->v[2]; +                } else if (sad->format >= 2 +                        && sad->format <= 8) { +                    sad->max_kbps = 8 * sad->v[2]; +                } +                e->sad_count++; +            } +            break; +        case 0x4: /* Speaker allocation */ +            e->speaker_alloc_bits = ptr[h]; +            break; +        case 0x2: /* SVDs */ +            for(i = h; i <= blk->len; i++) +                e->svds[e->svd_count++].v = ptr[i]; +            break; +        case 0x3: /* Vendor-specific */ +            rouicpy(&blk->ven, e, a+h); +            // TODO: +            break; +        default: +            break; +    } +} + +static void did_block_decode(DisplayIDBlock *blk) { +    if (!blk) return; + +    //printf("did_block_decode: %s\n", hex_bytes(DPTR(blk->addy), blk->len+3)); + +    if (!blk->bounds_ok) +        blk->bounds_ok = +        bounds_check(blk->addy.e, blk->addy.offset + 3 + blk->len); +    if (!blk->bounds_ok) return; + +    edid *e = blk->addy.e; +    static uint32_t h = 3; /* header size */ +    uint32_t a = blk->addy.offset; /* start of block, includes header */ +    uint8_t *u8 = DPTR(blk->addy); +    int b = h; +    edid_ven ven;// = {}; +    edid_output out;// = {}; +    memset(&ven,0,sizeof(edid_ven)); +    memset(&out,0,sizeof(edid_output)); +    if (blk) { +        switch(blk->type) { +            case 0:     /* Product ID (1.x) */ +                /* UNTESTED */ +                if (rpnpcpy(&ven, e, a+h) ) +                    e->ven = ven; +                if (u8[12] || u8[13]) { +                    e->dom.week = u8[12]; +                    e->dom.year = u8[13] + 2000; +                    e->dom.is_model_year = (e->dom.week == 255); +                    e->dom.std = STD_DISPLAYID; +                } +                e->did_strings[e->did_string_count].is_product_name = 1; +                e->did_strings[e->did_string_count].len = blk->len; +                e->did_strings[e->did_string_count].str = rstr_strip(e, a+h+12, u8[b+11]); +                e->name = e->did_strings[e->did_string_count].str; +                e->did_string_count++; +                break; +            case 0x20:  /* Product ID */ +                /* UNTESTED */ +                if (rouicpy(&ven, e, a+h) ) +                    e->ven = ven; +                if (u8[12] || u8[13]) { +                    e->dom.week = u8[12]; +                    e->dom.year = u8[13] + 2000; +                    e->dom.is_model_year = (e->dom.week == 255); +                    e->dom.std = STD_DISPLAYID20; +                } +                e->did_strings[e->did_string_count].is_product_name = 1; +                e->did_strings[e->did_string_count].len = blk->len; +                e->did_strings[e->did_string_count].str = rstr_strip(e, a+h+12, u8[b+11]); +                e->name = e->did_strings[e->did_string_count].str; +                e->did_string_count++; +                break; +            case 0x0a: /* Serial Number (ASCII String) */ +                e->did_strings[e->did_string_count].is_serial = 1; +                e->did_strings[e->did_string_count].len = blk->len; +                e->did_strings[e->did_string_count].str = rstr_strip(e, a+h, blk->len); +                e->serial = e->did_strings[e->did_string_count].str; +                e->did_string_count++; +                break; +            case 0x0b: /* General Purpose ASCII String */ +                e->did_strings[e->did_string_count].len = blk->len; +                e->did_strings[e->did_string_count].str = rstr(e, a+h, blk->len); +                e->did_string_count++; +                break; +            case 0x03: /* Type I Detailed timings */ +                out.pixel_clock_khz = 10 * r24le(e, a+h, NOMASK); +                out.horiz_pixels    = 1 + (u8[b+5] << 8) + u8[b+4]; +                out.horiz_blanking  = (u8[b+7] << 8) + u8[b+6]; +                out.vert_lines      = 1 + (u8[b+13] << 8) + u8[b+12]; +                out.vert_blanking   = (u8[b+15] << 8) + u8[b+14]; +                out.is_interlaced   = bf_value(u8[b+3], BFMASK(4, 0x1)); +                out.stereo_mode     = bf_value(u8[b+3], BFMASK(5, 0x3)); +                out.is_preferred    = bf_value(u8[b+3], BFMASK(7, 0x1)); +                out.src = OUTSRC_DID_TYPE_I; +                edid_output_fill(&out); +                e->didts[e->didt_count++] = out; +                break; +            case 0x13: /* Type VI Detailed timings (super 0x03) */ +                /* UNTESTED */ +                out.pixel_clock_khz = (u8[b+2] << 16) & 0xa0; +                out.pixel_clock_khz += u8[b+1] << 8; +                out.pixel_clock_khz += u8[b]; +                out.horiz_pixels = ((u8[b+5] << 8) + u8[b+3]) & 0x7fff; +                out.vert_lines = ((u8[b+6] << 8) + u8[b+5]) & 0x7fff; +                // TODO: blanking... +                out.is_interlaced = (u8[b+13] >> 7) & 0x1; +                out.stereo_mode = (u8[b+13] >> 5) & 0x2; +                out.src = OUTSRC_DID_TYPE_VI; +                edid_output_fill(&out); +                e->didts[e->didt_count++] = out; +                break; +            case 0x22: /* Type VII Detailed timings (super 0x13) */ +                /* UNTESTED */ +                out.pixel_clock_khz = u8[b+2] << 16; +                out.pixel_clock_khz += u8[b+1] << 8; +                out.pixel_clock_khz += u8[b]; +                out.horiz_pixels = (u8[b+5] << 8) + u8[b+4]; +                out.horiz_blanking = (u8[b+7] << 8) + u8[b+6]; +                out.vert_lines = (u8[b+13] << 8) + u8[b+12]; +                out.vert_blanking = (u8[b+15] << 8) + u8[b+14]; +                out.is_interlaced = (u8[b+3] >> 4) & 0x1; +                out.stereo_mode = (u8[b+3] >> 5) & 0x2; +                out.is_preferred = (u8[b+3] >> 7) & 0x1; +                out.src = OUTSRC_DID_TYPE_VII; +                edid_output_fill(&out); +                e->didts[e->didt_count++] = out; +                break; +            case 0x7e: /* vendor specific data */ +            case 0x7f: /* vendor specific data */ +                rouicpy(&blk->ven, e, a+h); +                // TODO: +                break; +            case 0x81: /* CTA DisplayID, ... Embedded CEA Blocks */ +                while(b < blk->len) { +                    int db_type = (u8[b] & 0xe0) >> 5; +                    int db_size =  u8[b] & 0x1f; +                    e->cea_blocks[e->cea_block_count].addy.e = blk->addy.e; +                    e->cea_blocks[e->cea_block_count].addy.offset = blk->addy.offset + b; +                    e->cea_blocks[e->cea_block_count].type = db_type; +                    e->cea_blocks[e->cea_block_count].len = db_size; +                    cea_block_decode(&e->cea_blocks[e->cea_block_count]); +                    e->cea_block_count++; +                    b += db_size + 1; +                } +                break; +            default: +                break; +        } +    } +} + +static edid_output edid_output_from_svd(uint8_t index) { +    int i; +    if (index >= 128 && index <= 192) index &= 0x7f; /* "native" flag for 0-64 */ +    for(i = 0; i < (int)G_N_ELEMENTS(cea_standard_timings); i++) { +        if (cea_standard_timings[i].index == index) { +            edid_output out;// = {}; +            memset(&out,0,sizeof(edid_output)); +            out.horiz_pixels = cea_standard_timings[i].horiz_active; +            out.vert_lines = cea_standard_timings[i].vert_active; +            if (strchr(cea_standard_timings[i].short_name, 'i')) +                out.is_interlaced = 1; +            out.pixel_clock_khz = cea_standard_timings[i].pixel_clock_mhz * 1000; +            out.vert_freq_hz = cea_standard_timings[i].vert_freq_hz; +            out.src = OUTSRC_SVD; +            edid_output_fill(&out); +            return out; +        } +    } +    return (edid_output){.src = OUTSRC_INVALID}; +} + +edid *edid_new(const char *data, unsigned int len) { +    if (len < 128) return NULL; + +    int i; +    edid *e = malloc(sizeof(edid)); +    memset(e, 0, sizeof(edid)); +    e->data = malloc(len); +    memcpy(e->data, data, len); +    e->len = len; +    e->ver_major = e->u8[18]; +    e->ver_minor = e->u8[19]; + +    e->msg_log = g_string_new(NULL); + +#define RESERVE_COUNT 300 +    e->dtds = malloc(sizeof(struct edid_dtd) * RESERVE_COUNT); +    e->cea_blocks = malloc(sizeof(struct edid_cea_block) * RESERVE_COUNT); +    e->svds = malloc(sizeof(struct edid_svd) * RESERVE_COUNT); +    e->sads = malloc(sizeof(struct edid_sad) * RESERVE_COUNT); +    e->did_blocks = malloc(sizeof(DisplayIDBlock) * RESERVE_COUNT); +    e->didts = malloc(sizeof(edid_output) * RESERVE_COUNT); +    e->did_strings = malloc(sizeof(edid_output) * RESERVE_COUNT); +    memset(e->dtds, 0, sizeof(struct edid_dtd) * RESERVE_COUNT); +    memset(e->cea_blocks, 0, sizeof(struct edid_cea_block) * RESERVE_COUNT); +    memset(e->svds, 0, sizeof(struct edid_svd) * RESERVE_COUNT); +    memset(e->sads, 0, sizeof(struct edid_sad) * RESERVE_COUNT); +    memset(e->did_blocks, 0, sizeof(DisplayIDBlock) * RESERVE_COUNT); +    memset(e->didts, 0, sizeof(edid_output) * RESERVE_COUNT); +    memset(e->did_strings, 0, sizeof(edid_output) * RESERVE_COUNT); + +    /* base product information */ +    rpnpcpy(&e->ven, e, 8);             /* bytes 8-9 */ +    e->product = r16le(e, 10, NOMASK);  /* bytes 10-11 */ +    e->n_serial = r32le(e, 12, NOMASK); /* bytes 12-15 */ +    e->dom.week = e->u8[16];            /* byte 16 */ +    e->dom.year = e->u8[17] + 1990;     /* byte 17 */ +    e->dom.is_model_year = (e->dom.week == 255); +    e->dom.std = STD_EDID; + +    e->a_or_d = (e->u8[20] & 0x80) ? 1 : 0; +    if (e->a_or_d == 1) { +        /* digital */ +        switch((e->u8[20] >> 4) & 0x7) { +            case 0x1: e->bpc = 6; break; +            case 0x2: e->bpc = 8; break; +            case 0x3: e->bpc = 10; break; +            case 0x4: e->bpc = 12; break; +            case 0x5: e->bpc = 14; break; +            case 0x6: e->bpc = 16; break; +        } +        e->interface = e->u8[20] & 0xf; +    } + +    if (e->u8[21] && e->u8[22]) { +        e->img.horiz_cm = e->u8[21]; +        e->img.vert_cm = e->u8[22]; +        edid_output_fill(&e->img); +        e->img_max = e->img; +    } + +    /* established timing bitmap */ +#define ETB_CHECK(BYT, BIT, HP, VP, RF, IL)      \ +    if (e->u8[BYT] & (1<<BIT)) {                 \ +        e->etbs[e->etb_count] = e->img;          \ +        e->etbs[e->etb_count].horiz_pixels = HP; \ +        e->etbs[e->etb_count].vert_pixels = VP;  \ +        e->etbs[e->etb_count].vert_freq_hz = RF; \ +        e->etbs[e->etb_count].is_interlaced = IL;\ +        e->etbs[e->etb_count].src = OUTSRC_ETB;  \ +        edid_output_fill(&e->etbs[e->etb_count]);\ +        e->etb_count++; }; +    ETB_CHECK(35, 7, 720, 400, 70, 0); //(VGA) +    ETB_CHECK(35, 6, 720, 400, 88, 0); //(XGA) +    ETB_CHECK(35, 5, 640, 480, 60, 0); //(VGA) +    ETB_CHECK(35, 4, 640, 480, 67, 0); //(Apple Macintosh II) +    ETB_CHECK(35, 3, 640, 480, 72, 0); +    ETB_CHECK(35, 2, 640, 480, 75, 0); +    ETB_CHECK(35, 1, 800, 600, 56, 0); +    ETB_CHECK(35, 0, 800, 600, 60, 0); +    ETB_CHECK(36, 7, 800, 600, 72, 0); +    ETB_CHECK(36, 6, 800, 600, 75, 0); +    ETB_CHECK(36, 5, 832, 624, 75, 0); //(Apple Macintosh II) +    ETB_CHECK(36, 4, 1024, 768, 87, 1); //(1024×768i) +    ETB_CHECK(36, 3, 1024, 768, 60, 0); +    ETB_CHECK(36, 2, 1024, 768, 70, 0); +    ETB_CHECK(36, 1, 1024, 768, 75, 0); +    ETB_CHECK(36, 0, 1280, 1024, 75, 0); +    ETB_CHECK(37, 7, 1152, 870, 75, 0); //(Apple Macintosh II) + +    /* standard timings */ +    for(i = 38; i < 53; i+=2) { +        /* 0101 is unused */ +        if (e->u8[i] == 0x01 && e->u8[i+1] == 0x01) +            continue; +        /* 00.. is invalid/"reserved" */ +        if (e->u8[i] == 0x00) +            continue; +        double xres = (e->u8[i] + 31) * 8; +        double yres = 0; +        int iar = (e->u8[i+1] >> 6) & 0x3; +        int vf = (e->u8[i+1] & 0x3f) + 60; +        switch(iar) { +            case 0: /* 16:10 (v<1.3 1:1) */ +                if (e->ver_major == 1 && e->ver_minor < 3) +                    yres = xres; +                else +                    yres = xres*10/16; +                break; +            case 0x1: /* 4:3 */ +                yres = xres*4/3; +                break; +            case 0x2: /* 5:4 */ +                yres = xres*4/5; +                break; +            case 0x3: /* 16:9 */ +                yres = xres*9/16; +                break; +        } +        e->stds[e->std_count].ptr = &e->u8[i]; +        e->stds[e->std_count].out = e->img; /* inherit */ +        e->stds[e->std_count].out.horiz_pixels = xres; +        e->stds[e->std_count].out.vert_pixels = yres; +        e->stds[e->std_count].out.vert_freq_hz = vf; +        e->stds[e->std_count].out.src = OUTSRC_STD; +        edid_output_fill(&e->stds[e->std_count].out); +        e->std_count++; +    } + +    uint16_t dh, dl; +#define CHECK_DESCRIPTOR(INDEX, OFFSET)       \ +    e->d[INDEX].addy.e = e;                   \ +    e->d[INDEX].addy.offset = OFFSET;         \ +    if (e->u8[OFFSET] == 0) {                 \ +        dh = be16toh(e->u16[OFFSET/2]);       \ +        dl = be16toh(e->u16[OFFSET/2+1]);     \ +        e->d[INDEX].type = (dh << 16) + dl;   \ +        switch(e->d[INDEX].type) {            \ +            case 0xfc: case 0xff: case 0xfe:  \ +                strncpy(e->d[INDEX].text, (char*)e->u8+OFFSET+5, 13);  \ +        }                                     \ +    } else e->dtds[e->dtd_count++].addy = e->d[INDEX].addy; + +    CHECK_DESCRIPTOR(0, 54); +    CHECK_DESCRIPTOR(1, 72); +    CHECK_DESCRIPTOR(2, 90); +    CHECK_DESCRIPTOR(3, 108); + +    e->checksum_ok = block_check(e, 0); /* first 128-byte block only */ +    if (len > 128) { +        /* check extension blocks */ +        int blocks = len / 128; +        blocks--; +        e->ext_blocks = blocks; +        e->ext_ok = malloc(sizeof(uint8_t) * blocks); +        for(; blocks; blocks--) { +            uint32_t offset = blocks * 128; +            uint8_t *u8 = e->u8 + offset; +            int r = block_check(e, offset); +            e->ext_ok[blocks-1] = r; +            if (r) e->ext_blocks_ok++; +            else e->ext_blocks_fail++; + +            if (u8[0] == 0x40) { +                /* DI-EXT */ +                e->di.exists = 1; +                e->di.addy.e = e; +                e->di.addy.offset = offset; + +                e->di.interface = r8(e, offset + 2, NOMASK); +                e->di.supports_hdcp = r8(e, offset + 3, 0x8); +            } + +            if (u8[0] == 0x70) { +                /* DisplayID */ +                e->did.version = u8[1]; +                if (e->did.version >= 0x20) +                    e->std = MAX(e->std, STD_DISPLAYID20); +                else e->std = MAX(e->std, STD_DISPLAYID); +                e->did.extension_length = u8[2]; +                e->did.primary_use_case = u8[3]; +                e->did.extension_count  = u8[4]; +                e->did.checksum_ok = block_check_n(e, offset, e->did.extension_length + 5); +                int db_end = u8[2] + 5; +                int b = 5; +                while(b < db_end) { +                    if (r24le(e, offset + b, NOMASK) == 0) break; +                    int db_type = u8[b]; +                    int db_revision = u8[b+1] & 0x7; +                    int db_size = u8[b+2]; +                    e->did_blocks[e->did_block_count].addy.e = e; +                    e->did_blocks[e->did_block_count].addy.offset = offset + b; +                    e->did_blocks[e->did_block_count].type = db_type; +                    e->did_blocks[e->did_block_count].revision = db_revision; +                    e->did_blocks[e->did_block_count].len = db_size; +                    did_block_decode(&e->did_blocks[e->did_block_count]); +                    e->did_block_count++; +                    e->did.blocks++; +                    b += db_size + 3; +                } +                if (b > db_end) +                    edid_msg(e, "DID block overrun [in ext " OFMT "], expect to end at +%d, but last ends at +%d" , offset, db_end-1, b-1); +                //printf("DID: v:%02x el:%d uc:%d ec:%d, blocks:%d ok:%d\n", +                //    e->did.version, e->did.extension_length, +                //    e->did.primary_use_case, e->did.extension_count, +                //    e->did.blocks, e->checksum_ok); +            } + +            if (u8[0] == 0x02) { +                e->std = MAX(e->std, STD_EIACEA861); +                /* CEA extension */ +                int db_end = u8[2]; +                if (db_end) { +                    int b = 4; +                    while(b < db_end) { +                        int db_type = (u8[b] & 0xe0) >> 5; +                        int db_size =  u8[b] & 0x1f; +                        e->cea_blocks[e->cea_block_count].addy.e = e; +                        e->cea_blocks[e->cea_block_count].addy.offset = offset + b; +                        e->cea_blocks[e->cea_block_count].type = db_type; +                        e->cea_blocks[e->cea_block_count].len = db_size; +                        cea_block_decode(&e->cea_blocks[e->cea_block_count]); +                        e->cea_block_count++; +                        b += db_size + 1; +                    } +                    if (b > db_end) { +                        b = db_end; +                        edid_msg(e, "CEA block overrun [in ext " OFMT "], expect to end at +%d, but last ends at +%d" , offset, db_end-1, b-1); +                    } +                    /* DTDs */ +                    while(b < 127) { +                        if (u8[b]) { +                            e->dtds[e->dtd_count].addy.e = e; +                            e->dtds[e->dtd_count].addy.offset = offset + b; +                            e->dtds[e->dtd_count].cea_ext = 1; +                            e->dtd_count++; +                        } +                        b += 18; +                    } +                } +            } +        } +    } +    if (e->ext_blocks_ok) { +        e->std = MAX(e->std, STD_EEDID); +    } + +    /* strings */ +    for(i = 0; i < 4; i++) { +        g_strstrip(e->d[i].text); +        str_make_printable(e->d[i].text); +        switch(e->d[i].type) { +            case 0xfc: +                e->name = e->d[i].text; +                break; +            case 0xff: +                e->serial = e->d[i].text; +                break; +            case 0xfe: +                if (e->ut1) +                    e->ut2 = e->d[i].text; +                else +                    e->ut1 = e->d[i].text; +                break; +        } +    } + +    /* quirks */ +    if (!e->name) { +        if (SEQ(e->ut1, "LG Display") && e->ut2) +            /* LG may use "uspecified text" for name and model */ +            e->name = e->ut2; +        else if (SEQ(e->ut1, "AUO") && e->ut2) +            /* Same with AUO */ +            e->name = e->ut2; +        else { +            if (e->ut1) e->name = e->ut1; +            if (e->ut2 && !e->serial) e->serial = e->ut2; +        } +    } +    if (!e->interface && e->di.interface) { +        if (e->di.interface >= 1 +            && e->di.interface <= 5) +            e->interface = 1; /* DVI */ +    } + +    /* largest in ETB */ +    for (i = 0; i < e->etb_count; i++) { +        if (e->etbs[i].pixels > e->img.pixels) +            e->img = e->etbs[i]; +        if (e->etbs[i].pixels > e->img_max.pixels) +            e->img_max = e->etbs[i]; +    } + +    /* largest in STDs */ +    for (i = 0; i < e->std_count; i++) { +        if (e->stds[i].out.pixels > e->img.pixels) +            e->img = e->stds[i].out; +        if (e->stds[i].out.pixels > e->img_max.pixels) +            e->img_max = e->stds[i].out; +    } + +    /* dtds */ +    for(i = 0; i < e->dtd_count; i++) { +        edid_addy a = e->dtds[i].addy; +        if (!e->dtds[i].bounds_ok) +            e->dtds[i].bounds_ok = bounds_check(a.e, a.offset + 18); +        if (!e->dtds[i].bounds_ok) { +            printf("bounds fail\n"); +            exit(0); +        } +        uint8_t *u8 = DPTR(a); +        edid_output *out = &e->dtds[i].out; +        if (e->dtds[i].cea_ext) out->src = OUTSRC_CEA_DTD; +        else out->src = OUTSRC_DTD; +        out->pixel_clock_khz = 10 * r16le(a.e, a.offset, NOMASK); +        out->horiz_pixels = +            ((u8[4] & 0xf0) << 4) + u8[2]; +        out->vert_lines = +            ((u8[7] & 0xf0) << 4) + u8[5]; + +        out->horiz_blanking = +            ((u8[4] & 0x0f) << 8) + u8[3]; +        out->vert_blanking = +            ((u8[7] & 0x0f) << 8) + u8[6]; + +        out->horiz_cm = +            ((u8[14] & 0xf0) << 4) + u8[12]; +        out->horiz_cm /= 10; +        out->vert_cm = +            ((u8[14] & 0x0f) << 8) + u8[13]; +        out->vert_cm /= 10; +        out->is_interlaced = (u8[17] & 0x80) >> 7; +        out->stereo_mode = (u8[17] & 0x60) >> 4; +        out->stereo_mode += u8[17] & 0x01; +        edid_output_fill(out); +    } + +    if (e->dtd_count) { +        /* first DTD is "preferred" */ +        e->img_max = e->dtds[0].out; +    } + +    /* svds */ +    for(i = 0; i < e->svd_count; i++) { +        e->svds[i].out = edid_output_from_svd(e->svds[i].v); +        if (e->svds[i].out.src == OUTSRC_INVALID) +            continue; + +        if (e->svds[i].v >= 128 && +            e->svds[i].v <= 192) { +            e->svds[i].is_native = 1; + +            edid_output tmp = e->img_max; +            /* native res is max real res, right? */ +            e->img_max = e->svds[i].out; +            e->img_max.is_preferred = 1; +            OUTPUT_CPY_SIZE(e->img_max, tmp); +        } +        if (e->svds[i].out.pixels > e->img_svd.pixels +            || e->svds[i].is_native) { +            e->img_svd = e->svds[i].out; +            if (e->svds[i].is_native) +                e->img_svd.is_preferred = 1; +            OUTPUT_CPY_SIZE(e->img_svd, e->img_max); +        } +    } +    /* remove invalid SVDs */ +    int d = 0; +    for(i = 0; i < e->svd_count; i++) { +        if (d != i) +            e->svds[d].out = e->svds[i].out; +        if (e->svds[i].out.src != OUTSRC_INVALID) +            d++; +    } +    e->svd_count -= (i-d); + +    /* didts */ +    for(i = 0; i < e->didt_count; i++) { +        int pref = e->didts[i].is_preferred; +        int max_pref = e->img_max.is_preferred; +        int bigger = (e->didts[i].pixels > e->img_max.pixels); +        int better = (e->didts[i].src > e->img_max.src); +        if ((bigger && !max_pref) +            || (pref && !max_pref) +            || (better)) { +            edid_output tmp = e->img_max; +            e->img_max = e->didts[i]; +            OUTPUT_CPY_SIZE(e->img_max, tmp); +        } +    } + +    if (!e->speaker_alloc_bits && e->sad_count) { +        /* make an assumption */ +        if (e->sads[0].channels == 2) +            e->speaker_alloc_bits = 0x1; +    } + +    /* squeeze lists */ +#define SQUEEZE(C, L) \ +    if (!e->C) { free(e->L); e->L = NULL; } \ +    else { e->L = realloc(e->L, sizeof(e->L[0]) * (e->C)); } + +    SQUEEZE(dtd_count, dtds); +    SQUEEZE(cea_block_count, cea_blocks); +    SQUEEZE(svd_count, svds); +    SQUEEZE(sad_count, sads); +    SQUEEZE(did_block_count, did_blocks); +    SQUEEZE(didt_count, didts); +    SQUEEZE(did_string_count, did_strings); +    return e; +} + +void edid_free(edid *e) { +    int i; +    if (e) { +        g_free(e->ext_ok); +        g_free(e->cea_blocks); +        g_free(e->dtds); +        g_free(e->svds); +        g_free(e->sads); +        g_free(e->did_blocks); +        g_free(e->didts); +        for(i = 0; i < e->did_string_count; i++) +            g_free(e->did_strings[i].str); +        g_free(e->did_strings); +        g_free(e->data); +        g_string_free(e->msg_log, TRUE); +        g_free(e); +    } +} + +edid *edid_new_from_hex(const char *hex_string) { +    int blen = strlen(hex_string) / 2; +    uint8_t *buffer = malloc(blen), *n = buffer; +    memset(buffer, 0, blen); +    int len = 0; + +    const char *p = hex_string; +    char byte[3] = ".."; + +    while(p && *p) { +        if (isxdigit(p[0]) && isxdigit(p[1])) { +            byte[0] = p[0]; +            byte[1] = p[1]; +            *n = strtol(byte, NULL, 16); +            n++; +            len++; +            p += 2; +        } else +            p++; +    } + +    edid *e = edid_new((char*)buffer, len); +    free(buffer); +    return e; +} + +edid *edid_new_from_file(const char *path) { +    char *bin = NULL; +    gsize len = 0; +    if (g_file_get_contents(path, &bin, &len, NULL) ) { +        edid *ret = edid_new(bin, len); +        g_free(bin); +        return ret; +    } +    return NULL; +} + +char *edid_dump_hex(edid *e, int tabs, int breaks) { +    if (!e) return NULL; +    int lines = 1 + (e->len / 16); +    int blen = lines * 35 + 1; +    unsigned int pc = 0; +    char *ret = malloc(blen); +    memset(ret, 0, blen); +    uint8_t *u8 = e->u8; +    char *p = ret; +    for(; lines; lines--) { +        int i, d = MIN(16, (e->len - pc)); +        if (!d) break; +        for(i = 0; i < tabs; i++) +            sprintf(p++, "\t"); +        for(i = d; i; i--) { +            sprintf(p, "%02x", (unsigned int)*u8); +            p+=2; +            u8++; +            pc++; +            if (pc == e->len) { +                if (breaks) sprintf(p++, "\n"); +                goto edid_dump_hex_done; +            } +        } +        if (breaks) sprintf(p++, "\n"); +    } +edid_dump_hex_done: +    return ret; +} + +const char *edid_standard(int std) { +    switch(std) { +        case STD_EDID: return N_("VESA EDID"); +        case STD_EEDID: return N_("VESA E-EDID"); +        case STD_EIACEA861: return N_("EIA/CEA-861"); +        case STD_DISPLAYID: return N_("VESA DisplayID"); +        case STD_DISPLAYID20: return N_("VESA DisplayID 2.0"); +    }; +    return N_("unknown"); +} + +const char *edid_output_src(int src) { +    switch(src) { +        case OUTSRC_EDID: return N_("VESA EDID"); +        case OUTSRC_ETB: return N_("VESA EDID ETB"); +        case OUTSRC_STD: return N_("VESA EDID STD"); +        case OUTSRC_DTD: return N_("VESA EDID DTD"); +        case OUTSRC_CEA_DTD: return N_("EIA/CEA-861 DTD"); +        case OUTSRC_SVD: return N_("EIA/CEA-861 SVD"); +        case OUTSRC_DID_TYPE_I: return N_("DisplayID Type I"); +        case OUTSRC_DID_TYPE_VI: return N_("DisplayID Type VI"); +        case OUTSRC_DID_TYPE_VII: return N_("DisplayID Type VII"); +    }; +    return N_("unknown"); +} + +const char *edid_interface(int type) { +    switch(type) { +        case 0: return N_("undefined"); +        case 0x1: return N_("DVI"); +        case 0x2: return N_("HDMIa"); +        case 0x3: return N_("HDMIb"); +        case 0x4: return N_("MDDI"); +        case 0x5: return N_("DisplayPort"); +    }; +    return N_("unknown"); +} + +const char *edid_di_interface(int type) { +    switch(type) { +        case 0: return N_("Analog"); +        case 0x1: return N_("DVI"); +        case 0x2: return N_("DVI - Single Link"); +        case 0x3: return N_("DVI - Dual Link (Hi-Resolution)"); +        case 0x4: return N_("DVI - Dual Link (Hi-Color)"); +        case 0x5: return N_("DVI for Consumer Electronics"); +        case 0x6: return N_("PnD"); +        case 0x7: return N_("DFP"); +        case 0x8: return N_("OpenLDI - Single Link"); +        case 0x9: return N_("OpenLDI - Dual Link"); +        case 0xa: return N_("OpenLDI for Consumer Electronics"); +    }; +    return N_("unknown"); +} + +const char *edid_cea_audio_type(int type) { +    switch(type) { +        case 0: case 15: return N_("reserved"); +        case 1: return N_("LPCM"); +        case 2: return N_("AC-3"); +        case 3: return N_("MPEG1 (layers 1 and 2)"); +        case 4: return N_("MPEG1 layer 3"); +        case 5: return N_("MPEG2"); +        case 6: return N_("AAC"); +        case 7: return N_("DTS"); +        case 8: return N_("ATRAC"); +        case 9: return N_("DSD"); +        case 10: return N_("DD+"); +        case 11: return N_("DTS-HD"); +        case 12: return N_("MLP/Dolby TrueHD"); +        case 13: return N_("DST Audio"); +        case 14: return N_("WMA Pro"); +    } +    return N_("unknown type"); +} + +const char *edid_cea_block_type(int type) { +    switch(type) { +        case 0x01: +            return N_("audio"); +        case 0x02: +            return N_("video"); +        case 0x03: +            return N_("vendor specific"); +        case 0x04: +            return N_("speaker allocation"); +    } +    return N_("unknown type"); +} + +const char *edid_did_block_type(int type) { +    switch(type) { +        /* 1.x */ +        case 0x00: return N_("Product Identification (1.x)"); +        case 0x01: return N_("Display Parameters (1.x)"); +        case 0x02: return N_("Color Characteristics (1.x)"); +        case 0x03: return N_("Type I Timing - Detailed (1.x)"); +        case 0x04: return N_("Type II Timing - Detailed (1.x)"); +        case 0x05: return N_("Type III Timing - Short (1.x)"); +        case 0x06: return N_("Type IV Timing - DMT ID Code (1.x)"); +        case 0x07: return N_("VESA Timing Standard (1.x)"); +        case 0x08: return N_("CEA Timing Standard (1.x)"); +        case 0x09: return N_("Video Timing Range (1.x)"); +        case 0x0A: return N_("Product Serial Number (1.x)"); +        case 0x0B: return N_("General Purpose ASCII String (1.x)"); +        case 0x0C: return N_("Display Device Data (1.x)"); +        case 0x0D: return N_("Interface Power Sequencing (1.x)"); +        case 0x0E: return N_("Transfer Characteristics (1.x)"); +        case 0x0F: return N_("Display Interface Data (1.x)"); +        case 0x10: return N_("Stereo Display Interface (1.x)"); +        case 0x11: return N_("Type V Timing - Short (1.x)"); +        case 0x12: return N_("Tiled Display Topology (1.x)"); +        case 0x13: return N_("Type VI Timing - Detailed (1.x)"); +        case 0x7F: return N_("Vendor specific (1.x)"); +        /* 2.x */ +        case 0x20: return N_("Product Identification"); +        case 0x21: return N_("Display Parameters"); +        case 0x22: return N_("Type VII - Detailed Timing"); +        case 0x23: return N_("Type VIII - Enumerated Timing Code"); +        case 0x24: return N_("Type IX - Formula-based Timing"); +        case 0x25: return N_("Dynamic Video Timing Range Limits"); +        case 0x26: return N_("Display Interface Features"); +        case 0x27: return N_("Stereo Display Interface"); +        case 0x28: return N_("Tiled Display Topology"); +        case 0x29: return N_("ContainerID"); +        case 0x7E: return N_("Vendor specific"); +        case 0x81: return N_("CTA DisplayID"); +    } +    return N_("unknown type"); +} + +const char *edid_ext_block_type(int type) { +    switch(type) { +        case 0x00: +            return N_("timing extension"); +        case 0x02: +            return N_("EIA/CEA-861 extension (CEA-EXT)"); +        case 0x10: +            return N_("Video Timing Block extension (VTB-EXT)"); +        case 0x20: +            return N_("EDID 2.0 extension"); +        case 0x40: +            return N_("Display Information extension (DI-EXT)"); +        case 0x50: +            return N_("Localized String extension (LS-EXT)"); +        case 0x60: +            return N_("Digital Packet Video Link extension (DPVL-EXT)"); +        case 0x70: +            return N_("DisplayID"); +        case 0xa7: +        case 0xaf: +        case 0xbf: +            return N_("display transfer characteristics data block"); +        case 0xf0: +            return N_("extension block map"); +        case 0xff: +            return N_("manufacturer-defined extension/display device data block"); +    } +    return N_("unknown block type"); +} + +const char *edid_descriptor_type(int type) { +    switch(type) { +        case 0xff: +            return N_("display serial number"); +        case 0xfe: +            return N_("unspecified text"); +        case 0xfd: +            return N_("display range limits"); +        case 0xfc: +            return N_("display name"); +        case 0xfb: +            return N_("additional white point"); +        case 0xfa: +            return N_("additional standard timing identifiers"); +        case 0xf9: +            return N_("Display Color Management"); +        case 0xf8: +            return N_("CVT 3-byte timing codes"); +        case 0xf7: +            return N_("additional standard timing"); +        case 0x10: +            return N_("dummy"); +    } +    if (type && type < 0x0f) +        return N_("manufacturer reserved descriptor"); +    return N_("detailed timing descriptor"); +} + +char *edid_cea_audio_describe(struct edid_sad *sad) { +    if (!sad) return NULL; + +    if (!sad->format) +        return  g_strdup_printf("format:([%x] %s)", +            sad->format, _(edid_cea_audio_type(sad->format)) ); + +    gchar *ret = NULL; +    gchar *tmp[3] = {NULL,NULL,NULL}; +#define appfreq(b, f) if (sad->freq_bits & (1 << b)) tmp[0] = appf(tmp[0], ", ", "%d", f); +#define appdepth(b, d) if (sad->depth_bits & (1 << b)) tmp[1] = appf(tmp[1], ", ", "%d%s", d, _("-bit")); +    appfreq(0, 32); +    appfreq(1, 44); +    appfreq(2, 48); +    appfreq(3, 88); +    appfreq(4, 96); +    appfreq(5, 176); +    appfreq(6, 192); + +    if (sad->format == 1) { +        appdepth(0, 16); +        appdepth(1, 20); +        appdepth(2, 24); +        tmp[2] = g_strdup_printf("depths: %s", tmp[1]); +    } else if (sad->format >= 2 +                && sad->format <= 8 ) { +        tmp[2] = g_strdup_printf("max_bitrate: %d %s", sad->max_kbps, _("kbps")); +    } else +        tmp[2] = g_strdup(""); + +    ret = g_strdup_printf("format:([%x] %s) channels:%d rates:%s %s %s", +        sad->format, _(edid_cea_audio_type(sad->format)), +        sad->channels, tmp[0], _("kHz"), +        tmp[2]); +    g_free(tmp[0]); +    g_free(tmp[1]); +    g_free(tmp[2]); +    return ret; +} + +char *edid_cea_speaker_allocation_describe(int bitfield, int short_version) { +    gchar *spk_list = NULL; +#define appspk(b, sv, fv) if (bitfield & (1 << b)) \ +    spk_list = appf(spk_list, short_version ? ", " : "\n", "%s", short_version ? sv : fv); + +    appspk(0, "FL+FR", _("Front left and right")); +    appspk(1, "LFE", _("Low-frequency effects")); +    appspk(2, "FC", _("Front center")); +    appspk(3, "RL+RR", _("Rear left and right")); +    appspk(4, "RC", _("Rear center")); +    appspk(5, "???", _("")); +    appspk(6, "???", _("")); + +    return spk_list; +} + +char *edid_cea_block_describe(struct edid_cea_block *blk) { +    gchar *ret = NULL; +    if (blk) { +        char *hb = hex_bytes(DPTR(blk->addy), blk->len+1); +        switch(blk->type) { +            case 0x1: /* SAD list */ +                ret = g_strdup_printf("([%x] %s) sads:%d", +                    blk->type, _(edid_cea_block_type(blk->type)), +                    blk->len/3); +                break; +            case 0x2: /* SVD list */ +                ret = g_strdup_printf("([%x] %s) svds:%d", +                    blk->type, _(edid_cea_block_type(blk->type)), +                    blk->len); +                break; +            case 0x4: /* speaker allocation */ +                ret = g_strdup_printf("([%x] %s) len:%d", +                    blk->type, _(edid_cea_block_type(blk->type)), +                    blk->len); +                break; +            case 0x3: /* vendor specific */ +                ret = g_strdup_printf("([%x] %s) len:%d (OUI:%s) -- %s", +                    blk->type, _(edid_cea_block_type(blk->type)), +                    blk->len, blk->ven.oui_str, +                    hb); +                break; +            default: +                ret = g_strdup_printf("([%x] %s) len:%d -- %s", +                    blk->type, _(edid_cea_block_type(blk->type)), +                    blk->len, +                    hb); +                break; +        } +        free(hb); +    } +    return ret; +} + +char *edid_base_descriptor_describe(struct edid_descriptor *d) { +    gchar *ret = NULL; +    if (d) { +        char *hb = hex_bytes(DPTR(d->addy), 18); +        char *txt = NULL; +        switch(d->type) { +            case 0: /* DTD */ +                txt = "{...}"; /* displayed elsewhere */ +                break; +            case 0x10: /* dummy */ +                txt = ""; +                break; +            default: +                txt = (*d->text) ? d->text : hb; +                break; +        }; +        ret = g_strdup_printf("([%02x] %s) %s", +            d->type, _(edid_descriptor_type(d->type)), +            txt); +        free(hb); +    } +    return ret; +} + +char *edid_did_block_describe(DisplayIDBlock *blk) { +    if (!blk) return NULL; + +    gchar *ret = NULL; +    edid *e = blk->addy.e; +    uint32_t a = blk->addy.offset + 3; +    char *str = NULL; + +    //printf("edid_did_block_describe: ((%d)) t:%02x a{%p, %d}...\n", blk->addy.e->did.extension_count, blk->type, blk->addy.e, blk->addy.offset); +    char *hb = hex_bytes(DPTR(blk->addy), blk->len+3); +    switch(blk->type) { +        case 0x0a: /* Product Serial ASCII string */ +            str = rstr_strip(e, a, blk->len); +            ret = g_strdup_printf("([%02x:r%02x] %s) len:%d \"%s\"", +                blk->type, blk->revision, _(edid_did_block_type(blk->type)), +                blk->len, +                str); +            break; +        case 0x0b: /* ASCII string */ +            str = rstr(e, a, blk->len); +            ret = g_strdup_printf("([%02x:r%02x] %s) len:%d \"%s\"", +                blk->type, blk->revision, _(edid_did_block_type(blk->type)), +                blk->len, +                str); +            break; +        default: +            ret = g_strdup_printf("([%02x:r%02x] %s) len:%d -- %s", +                blk->type, blk->revision, _(edid_did_block_type(blk->type)), +                blk->len, +                hb); +            break; +    } +    free(hb); + +    return ret; +} + +char *edid_output_describe(edid_output *out) { +    gchar *ret = NULL; +    if (out) { +        ret = g_strdup_printf("%dx%d@%.0f%s", +            out->horiz_pixels, out->vert_pixels, out->vert_freq_hz, _("Hz") ); +        if (out->diag_cm) +            ret = appfsp(ret, "%0.1fx%0.1f%s (%0.1f\")", +                out->horiz_cm, out->vert_cm, _("cm"), out->diag_in ); +        ret = appfsp(ret, "%s", out->is_interlaced ? "interlaced" : "progressive"); +        ret = appfsp(ret, "%s", out->stereo_mode ? "stereo" : "normal"); +    } +    return ret; +} + +char *edid_dtd_describe(struct edid_dtd *dtd, int dump_bytes) { +    gchar *ret = NULL; +    if (dtd) { +        edid_output *out = &dtd->out; +        char *hb = hex_bytes(DPTR(dtd->addy), 18); +        ret = g_strdup_printf("%dx%d@%.0f%s, %0.1fx%0.1f%s (%0.1f\") %s %s (%s)%s%s", +            out->horiz_pixels, out->vert_lines, out->vert_freq_hz, _("Hz"), +            out->horiz_cm, out->vert_cm, _("cm"), out->diag_in, +            out->is_interlaced ? "interlaced" : "progressive", +            out->stereo_mode ? "stereo" : "normal", +            _(edid_output_src(out->src)), +            dump_bytes ? " -- " : "", +            dump_bytes ? hb : ""); +        free(hb); +    } +    return ret; +} + +char *edid_manf_date_describe(struct edid_manf_date dom) { +    if (!dom.year) return g_strdup("unspecified"); +    if (dom.is_model_year) +        return g_strdup_printf(_("model year %d"), dom.year); +    if (dom.week && dom.week <= 53) +        return g_strdup_printf(_("week %d of %d"), dom.week, dom.year); +    return g_strdup_printf("%d", dom.year); +} + +char *edid_dump2(edid *e) { +    char *ret = NULL; +    int i; +    if (!e) return NULL; + +    ret = appfnl(ret, "edid version: %d.%d (%d bytes)", e->ver_major, e->ver_minor, e->len); +    if (e->std) +        ret = appfnl(ret, "extended to: %s", _(edid_standard(e->std)) ); + +    ret = appfnl(ret, "mfg: %s, model: [%04x-%08x] %u-%u", e->ven.pnp, e->product, e->n_serial, e->product, e->n_serial); +    char *dom_desc = edid_manf_date_describe(e->dom); +    ret = appfnl(ret, "date: %s", dom_desc); +    g_free(dom_desc); + +    if (e->name) +        ret = appfnl(ret, "product: %s", e->name); +    if (e->serial) +        ret = appfnl(ret, "serial: %s", e->serial); + +    ret = appfnl(ret, "type: %s", e->a_or_d ? "digital" : "analog"); +    if (e->bpc) +        ret = appfnl(ret, "bits per color channel: %d", e->bpc); +    if (e->interface) +        ret = appfnl(ret, "interface: %s", _(edid_interface(e->interface))); +    if (e->di.exists) { +        ret = appfnl(ret, "interface_ext: %s", _(edid_di_interface(e->di.interface))); +        ret = appfnl(ret, "hdcp: %s", e->di.supports_hdcp ? "supported" : "no"); +    } + +    char *desc = edid_output_describe(&e->img); +    char *desc_svd = edid_output_describe(&e->img_svd); +    char *desc_max = edid_output_describe(&e->img_max); +    ret = appfnl(ret, "base(%s): %s", _(edid_output_src(e->img.src)), desc); +    if (e->svd_count) +        ret = appfnl(ret, "svd(%s): %s", _(edid_output_src(e->img_svd.src)), desc_svd); +    ret = appfnl(ret, "max(%s): %s", _(edid_output_src(e->img_max.src)), desc_max); +    g_free(desc); +    g_free(desc_svd); +    g_free(desc_max); + +    if (e->speaker_alloc_bits) { +        char *desc = edid_cea_speaker_allocation_describe(e->speaker_alloc_bits, 1); +        ret = appfnl(ret, "speakers: %s", desc); +        g_free(desc); +    } + +    for(i = 0; i < e->etb_count; i++) { +        char *desc = edid_output_describe(&e->etbs[i]); +        ret = appfnl(ret, "etb[%d]: %s", i, desc); +        g_free(desc); +    } + +    for(i = 0; i < e->std_count; i++) { +        char *desc = edid_output_describe(&e->stds[i].out); +        ret = appfnl(ret, "std[%d]: %s", i, desc); +        g_free(desc); +    } + +    ret = appfnl(ret, "checksum %s", e->checksum_ok ? "ok" : "failed!"); +    if (e->ext_blocks_ok || e->ext_blocks_fail) +        ret = appf(ret, "", ", extension blocks: %d of %d ok", e->ext_blocks_ok, e->ext_blocks_ok + e->ext_blocks_fail); + +    for(i = 0; i < 4; i++) { +        char *desc = edid_base_descriptor_describe(&e->d[i]); +        ret = appfnl(ret, "descriptor[%d] %s", i, desc); +        g_free(desc); +    } + +    for(i = 0; i < e->ext_blocks; i++) { +        int type = e->u8[(i+1)*128]; +        int version = e->u8[(i+1)*128 + 1]; +        ret = appfnl(ret, "ext[%d] ([%02x:v%02x] %s) %s", i, +            type, version, _(edid_ext_block_type(type)), +            e->ext_ok[i] ? "ok" : "fail" +        ); +    } + +    for(i = 0; i < e->dtd_count; i++) { +        char *desc = edid_dtd_describe(&e->dtds[i], 0); +        ret = appfnl(ret, "dtd[%d] %s", i, desc); +        free(desc); +    } + +    for(i = 0; i < e->cea_block_count; i++) { +        char *desc = edid_cea_block_describe(&e->cea_blocks[i]); +        ret = appfnl(ret, "cea_block[%d] %s", i, desc); +        free(desc); +    } + +    for(i = 0; i < e->svd_count; i++) { +        char *desc = edid_output_describe(&e->svds[i].out); +        ret = appfnl(ret, "svd[%d] [%02x] %s", i, e->svds[i].v, desc); +        free(desc); +    } + +    for(i = 0; i < e->sad_count; i++) { +        char *desc = edid_cea_audio_describe(&e->sads[i]); +        ret = appfnl(ret, "sad[%d] [%02x%02x%02x] %s", i, +            e->sads[i].v[0], e->sads[i].v[1], e->sads[i].v[2], +            desc); +        free(desc); +    } + +    for(i = 0; i < e->did_block_count; i++) { +        char *desc = edid_did_block_describe(&e->did_blocks[i]); +        ret = appfnl(ret, "did_block[%d] %s", i, desc); +        free(desc); +    } + +    for(i = 0; i < e->didt_count; i++) { +        char *desc = edid_output_describe(&e->didts[i]); +        ret = appfnl(ret, "did_timing[%d]: %s", i, desc); +        g_free(desc); +    } + +    for(i = 0; i < e->did_string_count; i++) { +        ret = appfnl(ret, "did_string[%d]: %s", i, e->did_strings[i].str); +    } + +    ret = appfnl(ret, "parse messages:\n%s---", e->msg_log->str); + +    return ret; +} + diff --git a/deps/sysobj_early/src/util_edid_svd_table.c b/deps/sysobj_early/src/util_edid_svd_table.c new file mode 100644 index 00000000..4833c77d --- /dev/null +++ b/deps/sysobj_early/src/util_edid_svd_table.c @@ -0,0 +1,164 @@ + +struct { +    int index; +    const char *short_name; +    const char *disp_ratio, *pixel_ratio; +    float pixel_clock_mhz, vert_freq_hz, horiz_freq_khz; +    float horiz_active, vert_active, horiz_total, vert_total; +    float field_rate_hz; +} cea_standard_timings[] = { +{ 1, "DMT0659", "4:3", "1:1", 25.175, 59.94, 31.469, 640, 480, 800, 525, 60 }, +{ 2, "480p", "4:3", "8:9", 27.0, 59.94, 31.469, 720, 480, 858, 525, 60 }, +{ 3, "480pH", "16:9", "32:37", 27.0, 59.94, 31.469, 720, 480, 858, 525, 60 }, +{ 4, "720p", "16:9", "1:1", 74.25, 60, 45.0, 1280, 720, 1650, 750, 60 }, +{ 5, "1080i", "16:9", "1:1", 74.25, 60, 33.75, 1920, 540, 2200, 562.5, 60 }, +{ 6, "480i", "4:3", "8:9", 27.0, 59.94, 15.734, 1440, 240, 1716, 262.5, 60 }, +{ 7, "480iH", "16:9", "32:37", 27.0, 59.94, 15.734, 1440, 240, 1716, 262.5, 60 }, +{ 8, "240p", "4:3", "4:9", 27.0, 59.826, 15.734, 1440, 240, 1716, 262.5, 60 }, +{ 9, "240pH", "16:9", "16:27", 27.0, 59.826, 15.734, 1440, 240, 1716, 262.5, 60 }, +{ 10, "480i4x", "4:3", "2:9-20:9", 54.0, 59.94, 15.734, 2880, 240, 3432, 262.5, 60 }, +{ 11, "480i4xH", "16:9", "8:27-80:27", 54.0, 59.94, 15.734, 2880, 240, 3432, 262.5, 60 }, +{ 12, "240p4x", "4:3", "1:9-10:9", 54.0, 60, 15.734, 2880, 240, 3432, 262.5, 60 }, +{ 13, "240p4xH", "16:9", "4:27-40:37", 54.0, 60, 15.734, 2880, 240, 3432, 262.5, 60 }, +{ 14, "480p2x", "4:3", "4:9 or 8:9", 54.0, 59.94, 31.469, 1440, 480, 1716, 525, 60 }, +{ 15, "480p2xH", "16:9", "16:27 or 32:37", 54.0, 59.94, 31.469, 1440, 480, 1716, 525, 60 }, +{ 16, "1080p", "16:9", "1:1", 148.5, 60, 67.5, 1920, 1080, 2200, 1125, 60 }, +{ 17, "576p", "4:3", "16:15", 27.0, 50, 31.25, 720, 576, 864, 625, 50 }, +{ 18, "576pH", "16:9", "64:45", 27.0, 50, 31.25, 720, 576, 864, 625, 50 }, +{ 19, "720p50", "16:9", "1:1", 74.25, 50, 37.5, 1280, 720, 1980, 750, 50 }, +{ 20, "1080i25", "16:9", "1:1", 74.25, 50, 28.125, 1920, 540, 2640, 562.5, 50 }, +{ 21, "576i", "4:3", "16:15", 27.0, 50, 15.625, 1440, 288, 1728, 312.5, 50 }, +{ 22, "576iH", "16:9", "64:45", 27.0, 50, 15.625, 1440, 288, 1728, 312.5, 50 }, +{ 23, "288p", "4:3", "8:15", 27.0, 50, 15.625, 1440, 288, 1728, 313, 50 }, +{ 24, "288pH", "16:9", "32:45", 27.0, 50, 15.625, 1440, 288, 1728, 313, 50 }, +{ 25, "576i4x", "4:3", "2:15-20:15", 54.0, 50, 15.625, 2880, 288, 3456, 312.5, 50 }, +{ 26, "576i4xH", "16:9", "16:45-160:45", 54.0, 50, 15.625, 2880, 288, 3456, 312.5, 50 }, +{ 27, "288p4x", "4:3", "1:15-10:15", 54.0, 50, 15.625, 2880, 288, 3456, 313, 50 }, +{ 28, "288p4xH", "16:9", "8:45-80:45", 54.0, 50, 15.625, 2880, 288, 3456, 313, 50 }, +{ 29, "576p2x", "4:3", "8:15 or 16:15", 54.0, 50, 31.25, 1440, 576, 1728, 625, 50 }, +{ 30, "576p2xH", "16:9", "32:45 or 64:45", 54.0, 50, 31.25, 1440, 576, 1728, 625, 50 }, +{ 31, "1080p50", "16:9", "1:1", 148.5, 50, 56.25, 1920, 1080, 2640, 1125, 50 }, +{ 32, "1080p24", "16:9", "1:1", 74.25, 23.98, 27.0, 1920, 1080, 2750, 1125, 0 }, +{ 33, "1080p25", "16:9", "1:1", 74.25, 25, 28.125, 1920, 1080, 2640, 1125, 0 }, +{ 34, "1080p30", "16:9", "1:1", 74.25, 29.97, 33.75, 1920, 1080, 2500, 1125, 0 }, +{ 35, "480p4x", "4:3", "2:9, 4:9 or 8:9", 108.0, 59.94, 31.469, 2880, 240, 3432, 262.5, 60 }, +{ 36, "480p4xH", "16:9", "8:27, 16:27 or 32:27", 108.0, 59.94, 31.469, 2880, 240, 3432, 262.5, 60 }, +{ 37, "576p4x", "4:3", "4:15, 8:15, or 16:15", 108.0, 50, 31.25, 2880, 576, 3456, 625, 50 }, +{ 38, "576p4xH", "16:9", "16:45, 32:45 or 64:45", 108.0, 50, 31.25, 2880, 576, 3456, 625, 50 }, +{ 39, "1080i25", "16:9", "1:1", 72.0, 50, 31.25, 1920, 540, 2304, 625, 50 }, +{ 40, "1080i50", "16:9", "1:1", 148.5, 100, 56.25, 1920, 540, 2640, 562.5, 100 }, +{ 41, "720p100", "16:9", "1:1", 148.5, 100, 45.0, 1280, 720, 1980, 750, 100 }, +{ 42, "576p100", "4:3", "16:15", 54.0, 100, 62.5, 720, 576, 864, 625, 100 }, +{ 43, "576p100H", "16:9", "64:45", 54.0, 100, 62.5, 720, 576, 864, 625, 100 }, +{ 44, "576i50", "4:3", "16:15", 54.0, 100, 31.25, 1440, 576, 1728, 625, 100 }, +{ 45, "576i50H", "16:9", "64:45", 54.0, 100, 31.25, 1440, 576, 1728, 625, 100 }, +{ 46, "1080i60", "16:9", "1:1", 148.5, 119.88, 67.5, 1920, 540, 2200, 562.5, 120 }, +{ 47, "720p120", "16:9", "1:1", 148.5, 119.88, 90.0, 1280, 720, 1650, 750, 120 }, +{ 48, "480p119", "4:3", "8:9", 54.0, 119.88, 62.937, 720, 576, 858, 525, 120 }, +{ 49, "480p119H", "16:9", "32:37", 54.0, 119.88, 62.937, 720, 576, 858, 525, 120 }, +{ 50, "480i59", "4:3", "16:15", 54.0, 119.88, 31.469, 1440, 576, 1716, 525, 120 }, +{ 51, "480i59H", "16:9", "64:45", 54.0, 119.88, 31.469, 1440, 576, 1716, 525, 120 }, +{ 52, "576p200", "4:3", "16:15", 108.0, 200, 125.0, 720, 576, 864, 625, 200 }, +{ 53, "576p200H", "16:9", "64:45", 108.0, 200, 125.0, 720, 576, 864, 625, 200 }, +{ 54, "576i100", "4:3", "16:15", 108.0, 200, 62.5, 1440, 288, 1728, 312.5, 200 }, +{ 55, "576i100H", "16:9", "64:45", 108.0, 200, 62.5, 1440, 288, 1728, 312.5, 200 }, +{ 56, "480p239", "4:3", "8:9", 108.0, 239.76, 125.874, 720, 480, 858, 525, 240 }, +{ 57, "480p239H", "16:9", "32:37", 108.0, 239.76, 125.874, 720, 480, 858, 525, 240 }, +{ 58, "480i119", "4:3", "8:9", 108.0, 239.76, 62.937, 1440, 240, 1716, 262.5, 240 }, +{ 59, "480i119H", "16:9", "32:37", 108.0, 239.76, 62.937, 1440, 240, 1716, 262.5, 240 }, +{ 60, "720p24", "16:9", "1:1", 59.4, 23.98, 18.0, 1280, 720, 3300, 750, 0 }, +{ 61, "720p25", "16:9", "1:1", 74.25, 25, 18.75, 1280, 720, 3960, 750, 0 }, +{ 62, "720p30", "16:9", "1:1", 74.25, 29.97, 22.5, 1280, 720, 3300, 750, 0 }, +{ 63, "1080p120", "16:9", "1:1", 297.0, 119.88, 135.0, 1920, 1080, 2200, 1125, 120 }, +{ 64, "1080p100", "16:9", "1:1", 297.0, 100, 112.5, 1920, 1080, 2640, 1125, 100 }, +{ 65, "720p24", "64:27", "4:3", 59.4, 23.98, 18.0, 1280, 720, 3300, 750, 0 }, +{ 66, "720p25", "64:27", "4:3", 74.25, 25, 18.75, 1280, 720, 3960, 750, 0 }, +{ 67, "720p30", "64:27", "4:3", 74.25, 29.97, 22.5, 1280, 720, 3300, 750, 0 }, +{ 68, "720p50", "64:27", "4:3", 74.25, 50, 37.5, 1280, 720, 1980, 750, 50 }, +{ 69, "720p", "64:27", "4:3", 74.25, 60, 45.0, 1650, 750, 1650, 750, 60 }, +{ 70, "720p100", "64:27", "4:3", 148.5, 100, 75.0, 1280, 720, 1980, 750, 100 }, +{ 71, "720p120", "64:27", "4:3", 148.5, 119.88, 90.0, 1280, 720, 1650, 750, 120 }, +{ 72, "1080p24", "64:27", "4:3", 74.25, 23.98, 27.0, 1920, 1080, 2750, 1125, 0 }, +{ 73, "1080p25", "64:27", "4:3", 74.25, 25, 28.125, 1920, 1080, 2640, 1125, 0 }, +{ 74, "1080p30", "64:27", "4:3", 74.25, 29.97, 33.75, 1920, 1080, 2500, 1125, 0 }, +{ 75, "1080p50", "64:27", "4:3", 148.5, 50, 56.25, 1920, 1080, 2640, 1125, 50 }, +{ 76, "1080p", "64:27", "4:3", 148.5, 60, 67.5, 1920, 1080, 2200, 1125, 60 }, +{ 77, "1080p100", "64:27", "4:3", 297.0, 100, 112.5, 1920, 1080, 2640, 1125, 100 }, +{ 78, "1080p120", "64:27", "4:3", 297.0, 119.88, 135.0, 1920, 1080, 2200, 1125, 120 }, +{ 79, "720p2x24", "64:27", "64:63", 59.4, 23.98, 18.0, 1680, 720, 3300, 750, 0 }, +{ 80, "720p2x25", "64:27", "64:63", 59.4, 25, 18.75, 1680, 720, 3168, 750, 0 }, +{ 81, "720p2x30", "64:27", "64:63", 59.4, 29.97, 22.5, 1680, 720, 2640, 750, 0 }, +{ 82, "720p2x50", "64:27", "64:63", 82.5, 50, 37.5, 1680, 720, 2200, 750, 50 }, +{ 83, "720p2x", "64:27", "64:63", 99.0, 60, 45.0, 1680, 720, 2200, 750, 60 }, +{ 84, "720p2x100", "64:27", "64:63", 165.0, 100, 82.5, 1680, 720, 2000, 825, 100 }, +{ 85, "720p2x120", "64:27", "64:63", 198.0, 119.88, 99.0, 1680, 720, 2000, 825, 120 }, +{ 86, "1080p2x24", "64:27", "1:1", 99.0, 23.98, 26.4, 2560, 1080, 3750, 1100, 0 }, +{ 87, "1080p2x25", "64:27", "1:1", 90.0, 25, 28.125, 2560, 1080, 3200, 1125, 0 }, +{ 88, "1080p2x30", "64:27", "1:1", 118.8, 29.97, 33.75, 2560, 1080, 3520, 1125, 0 }, +{ 89, "1080p2x50", "64:27", "1:1", 185.625, 50, 56.25, 2560, 1080, 3000, 1125, 50 }, +{ 90, "1080p2x", "64:27", "1:1", 198.0, 60, 66.0, 2560, 1080, 3000, 1100, 60 }, +{ 91, "1080p2x100", "64:27", "1:1", 371.25, 100, 125.0, 2560, 1080, 2970, 1250, 100 }, +{ 92, "1080p2x120", "64:27", "1:1", 495.0, 119.88, 150.0, 2560, 1080, 3300, 1250, 120 }, +{ 93, "2160p24", "16:9", "1:1", 297.0, 23.98, 54.0, 3840, 2160, 5500, 2250, 0 }, +{ 94, "2160p25", "16:9", "1:1", 297.0, 25, 56.25, 3840, 2160, 5280, 2250, 0 }, +{ 95, "2160p30", "16:9", "1:1", 297.0, 29.97, 67.5, 3840, 2160, 4400, 2250, 0 }, +{ 96, "2160p50", "16:9", "1:1", 594.0, 50, 112.5, 3840, 2160, 5280, 2250, 50 }, +{ 97, "2160p60", "16:9", "1:1", 594.0, 60, 135.0, 3840, 2160, 4400, 2250, 60 }, +{ 98, "2160p24", "256:135", "1:1", 297.0, 23.98, 67.5, 4096, 2160, 5500, 2250, 0 }, +{ 99, "2160p25", "256:135", "1:1", 297.0, 25, 112.5, 4096, 2160, 5280, 2250, 0 }, +{ 100, "2160p30", "256:135", "1:1", 297.0, 29.97, 135.0, 4096, 2160, 4400, 2250, 0 }, +{ 101, "2160p50", "256:135", "1:1", 594.0, 50, 112.5, 4096, 2160, 5280, 2250, 50 }, +{ 102, "2160p", "256:135", "1:1", 594.0, 60, 135.0, 4096, 2160, 4400, 2250, 60 }, +{ 103, "2160p24", "64:27", "4:3", 297.0, 23.98, 67.5, 3840, 2160, 5500, 2250, 0 }, +{ 104, "2160p25", "64:27", "4:3", 297.0, 25, 112.5, 3840, 2160, 5280, 2250, 0 }, +{ 105, "2160p30", "64:27", "4:3", 297.0, 29.97, 135.0, 3840, 2160, 4400, 2250, 0 }, +{ 106, "2160p50", "64:27", "4:3", 594.0, 50, 112.5, 3840, 2160, 5280, 2250, 50 }, +{ 107, "2160p", "64:27", "4:3", 594.0, 60, 135.0, 3840, 2160, 4400, 2250, 60 }, +{ 108, "720p48", "16:9", "1:1", 90.0, 47.96, 36.0, 1280, 720, 2500, 750, 0 }, +{ 109, "720p48", "64:27", "4:3", 90.0, 47.96, 36.0, 1280, 720, 2500, 750, 0 }, +{ 110, "720p2x48", "64:27", "64:63", 99.0, 47.96, 36.0, 1680, 720, 2750, 825, 0 }, +{ 111, "1080p48", "16:9", "1:1", 148.5, 47.96, 54.0, 1920, 1080, 2750, 1125, 0 }, +{ 112, "1080p48", "64:27", "4:3", 148.5, 47.96, 54.0, 1920, 1080, 2750, 1125, 0 }, +{ 113, "1080p2x48", "64:27", "1:1", 198.0, 47.96, 52.8, 2560, 1080, 3750, 1100, 0 }, +{ 114, "2160p48", "16:9", "1:1", 594.0, 47.96, 108.0, 3840, 2160, 5500, 2250, 0 }, +{ 115, "2160p48", "256:135", "1:1", 594.0, 47.96, 108.0, 4096, 2160, 5500, 2250, 0 }, +{ 116, "2160p48", "64:27", "4:3", 594.0, 47.96, 108.0, 3840, 2160, 5500, 2250, 0 }, +{ 117, "2160p100", "16:9", "1:1", 1188.0, 100, 225.0, 3840, 2160, 5280, 2250, 100 }, +{ 118, "2160p120", "16:9", "1:1", 1188.0, 119.88, 270.0, 3840, 2160, 4400, 2250, 120 }, +{ 119, "2160p100", "64:27", "4:3", 1188.0, 100, 225.0, 3840, 2160, 5280, 2250, 100 }, +{ 120, "2160p120", "64:27", "4:3", 1188.0, 119.88, 270.0, 3840, 2160, 4400, 2250, 120 }, +{ 121, "2160p2x24", "64:27", "1:1", 396.0, 23.98, 52.8, 5120, 2160, 7500, 2200, 0 }, +{ 122, "2160p2x25", "64:27", "1:1", 396.0, 25, 55.0, 5120, 2160, 7200, 2200, 0 }, +{ 123, "2160p2x30", "64:27", "1:1", 396.0, 29.97, 66.0, 5120, 2160, 6000, 2200, 0 }, +{ 124, "2160p2x48", "64:27", "1:1", 742.5, 47.96, 118.8, 5120, 2160, 6250, 2450, 0 }, +{ 125, "2160p2x50", "64:27", "1:1", 742.5, 50, 112.5, 5120, 2160, 6600, 2250, 50 }, +{ 126, "2160p2x", "64:27", "1:1", 742.5, 60, 135.0, 5120, 2160, 5500, 2250, 60 }, +{ 127, "2160p2x100", "64:27", "1:1", 1485.0, 100, 225.0, 5120, 2160, 6600, 2250, 100 }, +{ 193, "2160p2x120", "64:27", "1:1", 1485.0, 119.88, 270.0, 5120, 2160, 5500, 2250, 120 }, +{ 194, "4320p24", "16:9", "1:1", 1188.0, 23.98, 108.0, 7680, 4320, 11000, 4500, 0 }, +{ 195, "4320p25", "16:9", "1:1", 1188.0, 25, 110.0, 7680, 4320, 10800, 4400, 0 }, +{ 196, "4320p30", "16:9", "1:1", 1188.0, 29.97, 132.0, 7680, 4320, 9000, 4400, 0 }, +{ 197, "4320p48", "16:9", "1:1", 2376.0, 47.96, 216.0, 7680, 4320, 11000, 4500, 0 }, +{ 198, "4320p50", "16:9", "1:1", 2376.0, 50, 220.0, 7680, 4320, 10800, 4400, 50 }, +{ 199, "4320p", "16:9", "1:1", 2376.0, 60, 264.0, 7680, 4320, 9000, 4400, 60 }, +{ 200, "4320p100", "16:9", "1:1", 4752.0, 100, 450.0, 7680, 4320, 10560, 4500, 100 }, +{ 201, "4320p120", "16:9", "1:1", 4752.0, 119.88, 540.0, 7680, 4320, 8800, 4500, 120 }, +{ 202, "4320p24", "64:27", "4:3", 1188.0, 23.98, 108.0, 7680, 4320, 11000, 4500, 0 }, +{ 203, "4320p25", "64:27", "4:3", 1188.0, 25, 110.0, 7680, 4320, 10800, 4400, 0 }, +{ 204, "4320p30", "64:27", "4:3", 1188.0, 29.97, 132.0, 7680, 4320, 9000, 4400, 0 }, +{ 205, "4320p48", "64:27", "4:3", 2376.0, 47.96, 216.0, 7680, 4320, 11000, 4500, 0 }, +{ 206, "4320p50", "64:27", "4:3", 2376.0, 50, 220.0, 7680, 4320, 10800, 4400, 50 }, +{ 207, "4320p", "64:27", "4:3", 2376.0, 60, 264.0, 7680, 4320, 9000, 4400, 60 }, +{ 208, "4320p100", "64:27", "4:3", 4752.0, 100, 450.0, 7680, 4320, 10560, 4500, 100 }, +{ 209, "4320p120", "64:27", "4:3", 4752.0, 119.88, 540.0, 7680, 4320, 8800, 4500, 120 }, +{ 210, "4320p2x24", "64:27", "1:1", 1485.0, 23.98, 118.8, 10240, 4320, 12500, 4950, 0 }, +{ 211, "4320p2x25", "64:27", "1:1", 1485.0, 25, 110.0, 10240, 4320, 13500, 4400, 0 }, +{ 212, "4320p2x30", "64:27", "1:1", 1485.0, 29.97, 135.0, 10240, 4320, 11000, 4500, 0 }, +{ 213, "4320p2x48", "64:27", "1:1", 2970.0, 47.96, 237.6, 10240, 4320, 12500, 4950, 0 }, +{ 214, "4320p2x50", "64:27", "1:1", 2970.0, 50, 220.0, 10240, 4320, 13500, 4400, 50 }, +{ 215, "4320p2x", "64:27", "1:1", 2970.0, 60, 270.0, 10240, 4320, 11000, 4400, 60 }, +{ 216, "4320p2x100", "64:27", "1:1", 5940.0, 100, 450.0, 10240, 4320, 13200, 4500, 100 }, +{ 217, "4320p2x120", "64:27", "1:1", 5940.0, 119.88, 540.0, 10240, 4320, 11000, 4500, 120 }, +{ 218, "2160p100", "256:135", "1:1", 1188.0, 100, 225.0, 4096, 2160, 5280, 2250, 100 }, +{ 219, "2160p120", "256:135", "1:1", 1188.0, 119.88, 270.0, 4096, 2160, 4400, 2250, 120 }, +}; diff --git a/deps/sysobj_early/src/util_ids.c b/deps/sysobj_early/src/util_ids.c new file mode 100644 index 00000000..197b1ed3 --- /dev/null +++ b/deps/sysobj_early/src/util_ids.c @@ -0,0 +1,316 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#include "util_ids.h" +#include <glib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#define ids_msg(msg, ...)  fprintf (stderr, "[%s] " msg "\n", __FUNCTION__, ##__VA_ARGS__) /**/ +static int ids_tracing = 0; +void ids_trace_start() { ids_tracing = 1; } +void ids_trace_stop() { ids_tracing = 0; } + +ids_query *ids_query_new(const gchar *qpath) { +    ids_query *s = g_new0(ids_query, 1); +    s->qpath = qpath ? g_strdup(qpath) : NULL; +    return s; +} + +void ids_query_free(ids_query *s) { +    if (s) g_free(s->qpath); +    g_free(s); +} + +void ids_query_result_cpy(ids_query_result *dest, ids_query_result *src) { +    if (!dest || !src) return; +    memcpy(dest, src, sizeof(ids_query_result)); +    for(int i = 0; dest->results[i]; i++) +        dest->results[i] = dest->_strs + (src->results[i] - src->_strs); +} + +/* c001 < C 01 */ +//TODO: compare more than the first char +static int ids_cmp(const char *s1, const char *s2) { +    int cmp = (s2 ? 1 : 0) - (s1 ? 1 : 0); +    if (cmp == 0 && isalpha(*s1) && isalpha(*s2)) +        cmp = (islower(*s2) ? 1 : 0) - (islower(*s1) ? 1 : 0); +    if (cmp == 0) +        return g_strcmp0(s1, s2); +    else +        return cmp; +} + +static void ids_query_result_set_str(ids_query_result *ret, int tabs, gchar *p) { +    if (!p) { +        ret->results[tabs] = p; +    } else { +        if (tabs == 0) { +            ret->results[tabs] = ret->_strs; +            strncpy(ret->results[tabs], p, IDS_LOOKUP_BUFF_SIZE-1); +        } else { +            ret->results[tabs] = ret->results[tabs-1] + strlen(ret->results[tabs-1]) + 1; +            strncpy(ret->results[tabs], p, IDS_LOOKUP_BUFF_SIZE-1); +        } +    } +    /* all following strings become invalid */ +    while(tabs < IDS_LOOKUP_MAX_DEPTH) +        ret->results[++tabs] = NULL; +} + +/* Given a qpath "/X/Y/Z", find names as: + * X <name> ->result[0] + * \tY <name> ->result[1] + * \t\tZ <name> ->result[2] + * + * Works with: + * - pci.ids "<vendor>/<device>/<subvendor> <subdevice>" or "C <class>/<subclass>/<prog-if>" + * - arm.ids "<implementer>/<part>" + * - sdio.ids "<vendor>/<device>", "C <class>" + * - usb.ids "<vendor>/<device>", "C <class>" etc + * - edid.ids "<3letter_vendor>" + */ +long scan_ids_file(const gchar *file, const gchar *qpath, ids_query_result *result, long start_offset) { +    gchar **qparts = NULL; +    gchar buff[IDS_LOOKUP_BUFF_SIZE] = ""; +    ids_query_result ret;// = {}; +    gchar *p = NULL; + +    FILE *fd; +    int tabs; +    int qdepth; +    int qpartlen[IDS_LOOKUP_MAX_DEPTH]; +    long last_root_fpos = -1, fpos, line = -1; + +    memset(&ret,0,sizeof(ids_query_result)); + +    if (!qpath) +        return -1; + +    fd = fopen(file, "r"); +    if (!fd) { +        ids_msg("file could not be read: %s", file); +        return -1; +    } + +    qparts = g_strsplit(qpath, "/", -1); +    qdepth = g_strv_length(qparts); +    if (qdepth > IDS_LOOKUP_MAX_DEPTH) { +        ids_msg("qdepth (%d) > ids_max_depth (%d) for %s", qdepth, IDS_LOOKUP_MAX_DEPTH, qpath); +        qdepth = IDS_LOOKUP_MAX_DEPTH; +    } +    for(int i = 0; i < qdepth; i++) +        qpartlen[i] = strlen(qparts[i]); + +    if (start_offset > 0) +        fseek(fd, start_offset, SEEK_SET); + +    for (fpos = ftell(fd); fgets(buff, IDS_LOOKUP_BUFF_SIZE, fd); fpos = ftell(fd)) { +        p = strchr(buff, '\n'); +        if (!p) +            ids_msg("line longer than IDS_LOOKUP_BUFF_SIZE (%d), file: %s, offset: %ld", IDS_LOOKUP_BUFF_SIZE, file, fpos); +        line++; + +        /* line ends at comment */ +        p = strchr(buff, '#'); +        if (p) *p = 0; +        /* trim trailing white space */ +        if (!p) p = buff + strlen(buff); +        p--; +        while(p > buff && isspace((unsigned char)*p)) p--; +        *(p+1) = 0; +        p = buff; + +        if (buff[0] == 0)    continue; /* empty line */ +        if (buff[0] == '\n') continue; /* empty line */ + +        /* scan for fields */ +        tabs = 0; +        while(*p == '\t') { tabs++; p++; } + +        if (tabs >= qdepth) continue; /* too deep */ +        if (tabs != 0 && !ret.results[tabs-1]) +            continue; /* not looking at this depth, yet */ +        if (ret.results[tabs]) +            goto ids_lookup_done; /* answered at this level */ + +        if (ids_tracing) ids_msg("[%s] looking at (%d) %s...", file, tabs, p); + +        if (g_str_has_prefix(p, qparts[tabs]) +            && isspace(*(p + qpartlen[tabs])) ) { +            /* found */ +            p += qpartlen[tabs]; +            while(isspace((unsigned char)*p)) p++; /* ffwd */ + +            if (tabs == 0) last_root_fpos = fpos; +            ids_query_result_set_str(&ret, tabs, p); + +            if (ids_tracing) { +                int i = 0; +                for(; i < IDS_LOOKUP_MAX_DEPTH; i++) { +                    if (!qparts[i]) break; +                    ids_msg(" ...[%d]: %s\t--> %s", i, qparts[i], ret.results[i]); +                } +            } +            continue; +        } + +        if (ids_cmp(p, qparts[tabs]) == 1) { +            if (ids_tracing) +                ids_msg("will not be found qparts[tabs] = %s, p = %s", qparts[tabs], p); +            goto ids_lookup_done; /* will not be found */ +        } + +    } /* for each line */ + +ids_lookup_done: +    if (ids_tracing) +        ids_msg("bailed at line %ld...", line); +    fclose(fd); + +    if (result) { +        ids_query_result_cpy(result, &ret); +        return last_root_fpos; +    } +    return last_root_fpos; +} + +static gint _ids_query_list_cmp(const ids_query *ql1, const ids_query *ql2) { +    return g_strcmp0(ql1->qpath, ql2->qpath); +} + +long scan_ids_file_list(const gchar *file, ids_query_list query_list, long start_offset) { +    GSList *tmp = g_slist_copy(query_list); +    tmp = g_slist_sort(tmp, (GCompareFunc)_ids_query_list_cmp); + +    long offset = start_offset; +    for (GSList *l = query_list; l; l = l->next) { +        ids_query *q = l->data; +        offset = scan_ids_file(file, q->qpath, &(q->result), offset); +        if (offset == -1) +            break; +    } +    g_slist_free(tmp); +    return offset; +} + +int query_list_count_found(ids_query_list query_list) { +    long count = 0; +    for (GSList *l = query_list; l; l = l->next) { +        ids_query *q = l->data; +        if (q->result.results[0]) count++; +    } +    return count; +} + +static gchar *split_loc_default(const char *line) { +    return g_utf8_strchr(line, -1, ' '); +} + +GSList *ids_file_all_get_all(const gchar *file, split_loc_function split_loc_func) { +    GSList *ret = NULL; +    gchar buff[IDS_LOOKUP_BUFF_SIZE] = ""; +    gchar *p = NULL, *name = NULL; + +    FILE *fd; +    int tabs = 0, tabs_last = 0; +    long fpos, line = -1; + +    fd = fopen(file, "r"); +    if (!fd) { +        ids_msg("file could not be read: %s", file); +        return ret; +    } + +    ids_query_result *working = g_new0(ids_query_result, 1); +    gchar **qparts = g_new0(gchar*, IDS_LOOKUP_MAX_DEPTH + 1); +    for(tabs = IDS_LOOKUP_MAX_DEPTH-1; tabs>=0; tabs--) +        qparts[tabs] = g_malloc0(IDS_LOOKUP_BUFF_SIZE); +    tabs = 0; + +    if (!split_loc_func) split_loc_func = split_loc_default; + +    for (fpos = ftell(fd); fgets(buff, IDS_LOOKUP_BUFF_SIZE, fd); fpos = ftell(fd)) { +        p = strchr(buff, '\n'); +        if (!p) +            ids_msg("line longer than IDS_LOOKUP_BUFF_SIZE (%d), file: %s, offset: %ld", IDS_LOOKUP_BUFF_SIZE, file, fpos); +        line++; + +        /* line ends at comment */ +        p = strchr(buff, '#'); +        if (p) *p = 0; +        /* trim trailing white space */ +        if (!p) p = buff + strlen(buff); +        p--; +        while(p > buff && isspace((unsigned char)*p)) p--; +        *(p+1) = 0; +        p = buff; + +        if (buff[0] == 0)    continue; /* empty line */ +        if (buff[0] == '\n') continue; /* empty line */ + +        /* scan for fields */ +        tabs = 0; +        while(*p == '\t') { tabs++; p++; } + +        if (tabs >= IDS_LOOKUP_MAX_DEPTH) continue; /* too deep */ +        if (tabs > tabs_last + 1) { +            /* jump too big, there's a qpath part that is "" */ +            ids_msg("jump too big from depth %d to %d, file: %s, offset: %ld", tabs_last, tabs, file, fpos); +            continue; +        } + +        name = split_loc_func(p); +        if (!name) { +            ids_msg("expected name/value split not found, file: %s, offset: %ld", file, fpos); +            continue; +        } +        *name = 0; name++; /* split ptr is the first char of split string */ +        g_strstrip(p); +        g_strstrip(name); + +        // now  p = id, name = name +        // ids_msg("p: %s -- name: %s", p, name); + +        strncpy(qparts[tabs], p, IDS_LOOKUP_BUFF_SIZE-1); +        ids_query_result_set_str(working, tabs, name); +        if (tabs < tabs_last) +            for(;tabs_last > tabs; tabs_last--) { +                qparts[tabs_last][0] = 0; +                working->results[tabs_last] = NULL; +        } + +        ids_query *found = ids_query_new(NULL); +        ids_query_result_cpy(&found->result, working); +        found->qpath = g_strjoinv("/", qparts); +        p = found->qpath + strlen(found->qpath) - 1; +        while(*p == '/') { *p = 0; p--; } +        ret = g_slist_append(ret, found); + +        tabs_last = tabs; +    } /* for each line */ + +    fclose(fd); +    g_strfreev(qparts); +    ids_query_result_free(working); + +    return ret; +} diff --git a/deps/sysobj_early/src/util_sysobj.c b/deps/sysobj_early/src/util_sysobj.c new file mode 100644 index 00000000..94f71944 --- /dev/null +++ b/deps/sysobj_early/src/util_sysobj.c @@ -0,0 +1,303 @@ +/* + * sysobj - https://github.com/bp0/verbose-spork + * 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; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU 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 Street, Fifth Floor, Boston, MA  02110-1301, USA. + * + */ + +#define _GNU_SOURCE +#include <string.h> +#include <stdio.h> +#include <stdlib.h>  /* for realpath() */ +#include <unistd.h>  /* for getuid() */ +#include <ctype.h>   /* for isxdigit(), etc. */ + +#include "util_sysobj.h" + +gchar *util_build_fn(const gchar *base, const gchar *name) { +    gchar *ret = NULL; +    gboolean slash = TRUE; + +    if (!base) return NULL; +    if (base[strlen(base)-1] == '/') +        slash = FALSE; + +    if (name) { +        if (*name == '/') slash = FALSE; +        ret = g_strdup_printf("%s%s%s", base, slash ? "/" : "", name); +    } else +        ret = g_strdup(base); + +    util_null_trailing_slash(ret); +    return ret; +} + +gboolean util_have_root() { +    return (getuid() == 0) ? TRUE : FALSE; +} + +void util_compress_space(gchar *str) { +    gchar *p = str, *t = str; +    if (str && *str) { +        int n = 0; +        while(*p) { +            if (isspace(*p) ) { +                if (!n) +                    *t++ = ' '; +                n++; +            } else { +                n = 0; +                if (t != p) +                    *t = *p; +                t++; +            } +            p++; +        } +    } +    if (t != p) +        *t = 0; +} + +void util_null_trailing_slash(gchar *str) { +    if (str && *str) { +        if (str[strlen(str)-1] == '/' ) +            str[strlen(str)-1] = 0; +    } +} + +gsize util_count_lines(const gchar *str) { +    gchar **lines = NULL; +    gsize count = 0; + +    if (str) { +        lines = g_strsplit(str, "\n", 0); +        count = g_strv_length(lines); +        if (count && *lines[count-1] == 0) { +            /* if the last line is empty, don't count it */ +            count--; +        } +        g_strfreev(lines); +    } + +    return count; +} + +int util_get_did(gchar *str, const gchar *lbl) { +    int id = -2; +    gchar tmpfmt[128] = ""; +    gchar tmpchk[128] = ""; +    sprintf(tmpfmt, "%s%s", lbl, "%d"); +    if ( sscanf(str, tmpfmt, &id)==1 ) { +        sprintf(tmpchk, tmpfmt, id); +        if ( SEQ(str, tmpchk) ) +            return id; +    } +    return -1; +} + +gchar *util_escape_markup(gchar *v, gboolean 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("&", vl); +    else +        clean = g_strdup(v); +    g_strfreev(vl); + +    vl = g_strsplit(clean, "<", -1); +    if (g_strv_length(vl) > 1) { +        tmp = g_strjoinv("<", vl); +        g_free(clean); +        clean = tmp; +    } +    g_strfreev(vl); + +    vl = g_strsplit(clean, ">", -1); +    if (g_strv_length(vl) > 1) { +        tmp = g_strjoinv(">", vl); +        g_free(clean); +        clean = tmp; +    } +    g_strfreev(vl); + +    if (replacing) +        g_free((gpointer)v); +    return clean; +} + +void util_strstrip_double_quotes_dumb(gchar *str) { +    if (!str) return; +    g_strstrip(str); +    gchar *front = str, *back = str + strlen(str) - 1; +    if (back <= front) return; +    while(*front == '"') { *front = 'X'; front++; } +    while(*back == '"') { *back = 0; back--; } +    int nl = strlen(front); +    memmove(str, front, nl); +    str[nl] = 0; +} + +/* "194.110 MHz" -> "194.11 MHz" + * "5,0 golden rings" -> "5 golden rings" */ +gchar *util_strchomp_float(gchar* str_float) { +    if (!str_float) return NULL; +    char *dot = strchr(str_float, '.'); +    char *comma = strchr(str_float, ','); +    char *p = NULL, *dec = NULL, *src = NULL, *target = NULL; +    if (!dot && !comma) return str_float; +    if (dot > comma) +        dec = dot; +    else +        dec = comma; +    p = dec + 1; +    while(isdigit(*p)) p++; +    target = src = p; +    p--; +    while(*p == '0') { target = p; p--; }; +    if (target == dec + 1) +        target = dec; +    if (target != src) +        memmove(target, src, strlen(src)+1); +    return str_float; +} + +/* resolve . and .., but not symlinks */ +gchar *util_normalize_path(const gchar *path, const gchar *relto) { +    gchar *resolved = NULL; +#if GLIB_CHECK_VERSION(2, 58, 0) +    resolved = g_canonicalize_filename(path, relto); +#else +    /* burt's hack version */ +    gchar *frt = relto ? g_strdup(relto) : NULL; +    util_null_trailing_slash(frt); +    gchar *fpath = frt +        ? g_strdup_printf("%s%s%s", frt, (*path == '/') ? "" : "/", path) +        : g_strdup(path); +    g_free(frt); + +    /* note: **parts will own all the part strings throughout */ +    gchar **parts = g_strsplit(fpath, "/", -1); +    gsize i, pn = g_strv_length(parts); +    GList *lparts = NULL, *l = NULL, *n = NULL, *p = NULL; +    for (i = 0; i < pn; i++) +        lparts = g_list_append(lparts, parts[i]); + +    i = 0; +    gchar *part = NULL; +    l = lparts; +    while(l) { +        n = l->next; p = l->prev; +        part = l->data; + +        if (SEQ(part, ".") ) +            lparts = g_list_delete_link(lparts, l); + +        if (SEQ(part, "..") ) { +            if (p) +                lparts = g_list_delete_link(lparts, p); +            lparts = g_list_delete_link(lparts, l); +        } + +        l = n; +    } + +    resolved = g_strdup(""); +    l = lparts; +    while(l) { +        resolved = g_strdup_printf("%s%s/", resolved, (gchar*)l->data ); +        l = l->next; +    } +    g_list_free(lparts); +    util_null_trailing_slash(resolved); +    g_free(fpath); + +    g_strfreev(parts); +#endif + +    return resolved; +} + +/* resolve . and .. and symlinks */ +gchar *util_canonicalize_path(const gchar *path) { +    char *resolved = realpath(path, NULL); +    gchar *ret = g_strdup(resolved); /* free with g_free() instead of free() */ +    free(resolved); +    return ret; +} + +int util_maybe_num(gchar *str) { +    int r = 10, i = 0, l = (str) ? strlen(str) : 0; +    if (!l || l > 32) return 0; +    gchar *chk = g_strdup(str); +    g_strstrip(chk); +    l = strlen(chk); +    if (l > 2 && !strncmp(chk, "0x", 2)) { +        i = 2; r = 16; +    } +    for (; i < l; i++) { +        if (isxdigit(chk[i]))  { +            if (!isdigit(chk[i])) +                r = 16; +        } else { +            r = 0; +            break; +        } +    } +    g_free(chk); +    return r; +} + +gchar *util_safe_name(const gchar *name, gboolean lower_case) { +    if (!name) return NULL; +    const gchar *p = name; +    gchar *buff = g_new0(gchar, strlen(name) + 1); +    gchar *t = buff; +    while(*p) { +        gboolean ok = g_ascii_isalnum(*p); +        if (*p == '.' || *p == '-') ok = TRUE; +        if (*p == '/') ok = FALSE; +        *t = ok ? *p : '_'; +        t++; +        p = g_utf8_next_char(p); +    } +    gchar *ret = lower_case ? g_ascii_strdown(buff, -1) : g_strdup(buff); +    g_free(buff); +    return ret; +} + +gchar *util_find_line_value(gchar *data, gchar *key, gchar delim) { +    gchar *ret = NULL; +    gchar **lines = g_strsplit(data, "\n", -1); +    gsize line_count = g_strv_length(lines); +    gsize i = 0; +    for (i = 0; i < line_count; i++) { +        gchar *line = lines[i]; +        gchar *value = g_utf8_strchr(line, -1, delim); +        if (!value) continue; +        *value = 0; +        value = g_strstrip(value+1); +        gchar *lkey = g_strstrip(line); + +        if (SEQ(lkey, key) ) { +            ret = g_strdup(value); +        } +    } +    g_strfreev(lines); +    return ret; +} | 
