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; +} |