Skip to content

Instantly share code, notes, and snippets.

@joevt
Last active March 10, 2024 08:39
Show Gist options
  • Save joevt/0c75b42171b3fb1a5248b4e2bee8e4d0 to your computer and use it in GitHub Desktop.
Save joevt/0c75b42171b3fb1a5248b4e2bee8e4d0 to your computer and use it in GitHub Desktop.

Revisions

  1. joevt revised this gist Nov 18, 2021. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions ioreg.pl
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    #!/bin/perl

    # by joevt Dec 30, 2020
    # by joevt Nov 18, 2021

    use 5.010;
    use strict;
    @@ -307,11 +307,11 @@ sub parseioreg {
    my $thedispnodename = "";

    while ( /$regex(?&node)/g ) {
    if ($nodename =~ /^disp0@.*/) {
    $thedispnodename = "disp0";
    if ($nodename =~ /^(disp\d+)@.*/) {
    $thedispnodename = "$1";
    }
    elsif ($nodename =~ /^dispext\d+@.*/) {
    $thedispnodename = "dispext0";
    elsif ($nodename =~ /^(dispext\d+)@.*/) {
    $thedispnodename = "$1";
    }
    my $thedict = parsenode();

  2. joevt revised this gist Dec 31, 2020. 1 changed file with 139 additions and 108 deletions.
    247 changes: 139 additions & 108 deletions ioreg.pl
    Original file line number Diff line number Diff line change
    @@ -1,80 +1,69 @@
    #!/bin/perl

    # by joevt Dec 7, 2020
    # by joevt Dec 30, 2020

    use 5.010;
    use strict;
    #use warnings;
    use Data::Dumper qw(Dumper);

    use JSON::PP;


    sub myinc
    {
    my $val = $_[0];
    $_[0] = $val > 1 ? 3 : 2;
    return $val > 1 ? 2 : 1;
    }

    my $parseall = 1;

    my $nodenamendx = 1;
    my $propertiesndx = 1;
    my $propertyndx = 1;
    my $valuendx = 1;
    my $stringndx = 1;
    my $fieldndx = 1;
    my $fieldsndx = 1;
    my $itemsndx = 1;
    my $itemndx = 1;

    my @nodename = ("s","s");
    my @properties = ("s","s");
    my @property = ("s","s");
    my @value = ("s","s");
    my @string = ("s","s");
    my @field = ("s","s");
    my @fields = ("s","s");
    my @items = ("s","s");
    my @item = ("s","s");


    #my $regex = qr

    my $regex = qr /(?x) # ignore white space in regular expressions (means all spaces, tabs, and newlines need to be escaped)
    my $nodeindent = 0;
    my $nodename = "";
    my $thepath = "";
    my $nodeid = "";
    my $nodeflags = "";
    my $classname = "";

    my $propertyndx = 0;
    my $stringndx = 0;
    my $fieldndx = 0;
    my $fieldsndx = 0;
    my $itemsndx = 0;
    my $itemndx = 0;

    my @property = [];
    my @value = [];
    my @string = [];
    my @field = [];
    my @fields = [];
    my @items = [];
    my @item = [];

    my $regex = qr !(?x) # ignore white space in regular expressions (means all spaces, tabs, and newlines need to be escaped)
    (?m) # ^ matches line instead of start of string
    #(?i) # ^ case insensitive
    #(?p) # Preserve the string matched such that ${^PREMATCH}, ${^MATCH}, and ${^POSTMATCH} are available for use after matching.
    (?{
    $nodenamendx = 1;
    $propertiesndx = 1;
    $propertyndx = 1;
    $valuendx = 1;
    $stringndx = 1;
    $fieldndx = 1;
    $fieldsndx = 1;
    $itemsndx = 1;
    $itemndx = 1;
    $stringndx = 0;
    $fieldndx = 0;
    $fieldsndx = 0;
    $itemsndx = 0;
    $itemndx = 0;
    })
    (?(DEFINE)(?'indent'^[|\ ]*+))
    (?(DEFINE)(?'nodehead'(?&indent)\+\-o\ (?P<nodename>.*?)(?{$nodename[myinc($nodenamendx)]=$+{nodename}})\ \ <class\ (?P<classname>[^,]+).*\n))
    (?(DEFINE)(?'nodeproperties'((?&indent)\{\n(?P<properties>(?&property)++)(?{$properties[myinc($propertiesndx)]=$+{properties}})(?&indent)\}\n|(?{$properties[myinc($propertiesndx)]=""}))))
    (?(DEFINE)(?'property'(?&indent)(?&string)(?{$property[myinc($propertyndx)]=$string[$stringndx-1]})\ =\ (?P<value>(?&item))(?{$value[myinc($valuendx)]=$+{value}}).*\n))
    (?(DEFINE)(?'nodehead'(?P<nodeindent>(?&indent))\+\-o\ (?P<nodename>.*?)\ \ <class\ (?P<classname>[^,]+),\ id\ (?P<nodeid>[^,]+),\ (?P<nodeflags>[^>]+)>\n(?{$nodename=$+{nodename}; $nodeindent=(length $+{nodeindent})/2; $classname=$+{classname}; $nodeid=$+{nodeid}; $nodeflags=$+{nodeflags}})))
    (?(DEFINE)(?'nodeproperties'((?&indent)\{\n(?{$propertyndx=0;})(?&property)++(?&indent)\}\n|)))
    (?(DEFINE)(?'property'(?&indent)(?&string)(?{$property[$propertyndx]=$string[$stringndx-1];})\ =\ (?P<value>(?&item))(?{$value[$propertyndx++]=$+{value};}).*\n))
    (?(DEFINE)(?'node'(?&nodehead)(?&nodeproperties)))
    (?(DEFINE)(?'string'"(?P<stringx>[^"]*+)"(?{$string[myinc($stringndx)]=$+{stringx}})))
    (?(DEFINE)(?'string'"(?P<stringx>[^"]*+)"(?{$string[$stringndx++]=$+{stringx}})))
    (?(DEFINE)(?'number'\d++))
    (?(DEFINE)(?'boolean'(?:No|Yes)))
    (?(DEFINE)(?'data'<[0-9a-f]*+>))
    (?(DEFINE)(?'datahex'(?:\n(?&indent)[0-9A-F]++:(?:\ [0-9A-F]{2})++.*)++))
    (?(DEFINE)(?'field'(?&string)(?{$field[myinc($fieldndx)]=$string[$stringndx-1]})=(?&item)))
    (?(DEFINE)(?'dictionary'\{(?P<fields>(?:((?&field),)*+(?&field))|)(?{$fields[myinc($fieldsndx)]=$+{fields}})\}))
    (?(DEFINE)(?'array'\((?P<items>(?:((?&item),)*+(?&item))|)(?{$items[myinc($itemsndx)]=$+{items}})\)))
    (?(DEFINE)(?'field'(?&string)(?{$field[$fieldndx++]=$string[$stringndx-1]})=(?&item)))
    (?(DEFINE)(?'dictionary'\{(?P<fields>(?:((?&field),)*+(?&field))|)(?{$fields[$fieldsndx++]=$+{fields}})\}))
    (?(DEFINE)(?'array'\((?P<items>(?:((?&item),)*+(?&item))|)(?{$items[$itemsndx++]=$+{items}})\)))
    (?(DEFINE)(?'dataarray'\<((?&item),)*+(?&item)\>))
    (?(DEFINE)(?'item'(?P<itemx>(?:(?&dictionary)|(?&array)|(?&data)|(?&dataarray)|(?&datahex)|(?&string)|(?&number)|(?&boolean)))(?{$item[myinc($itemndx)]=$+{itemx}})))
    /;
    (?(DEFINE)(?'item'(?P<itemx>(?:(?&dictionary)|(?&array)|(?&data)|(?&dataarray)|(?&datahex)|(?&string)|(?&number)|(?&boolean)))(?{$item[$itemndx++]=$+{itemx}})))
    !;


    sub printvalue {
    @@ -83,7 +72,7 @@ sub printvalue {

    if ( $lvalue =~ /^$regex(?&array)/ ) {
    my $litems = $items[$itemsndx-1];
    printf("(");
    printf("[");

    #print "«[" . $litems . "]»\n";

    @@ -103,7 +92,7 @@ sub printvalue {
    if ($needcomma) {
    printf ("\n%*s", $depth * 2, "");
    }
    printf(")");
    printf("]");
    }

    elsif ( $lvalue =~ /^$regex(?&dictionary)/ ) {
    @@ -117,7 +106,7 @@ sub printvalue {

    #print "«“" . $field[1] . " = " . $item[$itemndx-1] . "”»\n";

    my $lfield = $field[1];
    my $lfield = $field[0];
    my $litem = $item[$itemndx-1];
    if ($needcomma) {
    printf (",");
    @@ -144,17 +133,17 @@ sub printvalue {

    sub printnode {
    my $depth = $_[0];
    my $properties = $_[1];
    printf ("%*s\"%s\" : {", $depth * 2, "", $nodename[1]);
    printf ("%*s\"%s\" : {", $depth * 2, "", $nodename);
    $depth += 1;
    my $needcomma = 0;
    while ( $properties =~ /$regex(?&property)/g ) {

    for (my $ndx = 0; $ndx < $propertyndx; $ndx++) {
    if ($needcomma) {
    printf (",");
    }
    printf ("\n");
    printf ("%*s\"%s\" : ", $depth * 2, "", $property[1]);
    printvalue($depth, $value[1]);
    printf ("%*s\"%s\" : ", $depth * 2, "", $property[$ndx]);
    printvalue($depth, $value[$ndx]);
    $needcomma = 1;
    }
    if ($needcomma) {
    @@ -171,16 +160,19 @@ sub printioreg {
    }
    printf ("\n");

    #printf ("%*s\"%s\" : {}", $depth * 2, "", $nodename[1]);
    printnode(0, $properties[1]);
    #printf ("%*s\"%s\" : {}", $depth * 2, "", $nodename);
    printnode(0);

    $needcomma = 1;
    }
    }

    my %thenodes = ();
    my $thenode = "";

    my %root = (
    _1_indent => -1,
    _9_children => []
    );
    my $prevnode = \%root;
    my %dispnodes = ();

    sub parsevalue {
    my $depth = $_[0];
    @@ -201,9 +193,12 @@ sub parsevalue {

    elsif ( $lvalue =~ /^$regex(?&dictionary)/ ) {
    my $lfields = $fields[$fieldsndx-1];

    #print "«{" . $lfields . "}»\n";

    my %thedict = ();
    while ( $lfields =~ /$regex(?&field)(,?)/g ) {
    my $lfield = $field[1];
    my $lfield = $field[0];

    my $newpath = $path . '/' . $lfield;

    @@ -272,40 +267,65 @@ sub parsevalue {
    }

    sub parsenode {
    my $depth = $_[0];
    my $path = $_[1];
    my $properties = $_[2];

    $depth += 1;
    my $needcomma = 0;
    $thepath =~ s|^((/[^/]*){$nodeindent}).*|$1/$nodename|;

    my %thedict = (
    _1_indent => $nodeindent,
    _2_name => $nodename,
    _3_path => $thepath,
    _4_class => $classname,
    _5_id => $nodeid,
    _6_flags => $nodeflags,
    _7_properties => {},
    _9_children => []
    );

    my %thedict = ();
    my $parent = $prevnode;
    while ($parent->{"_1_indent"} >= $nodeindent) {
    $parent = $parent->{"_8_parent"};
    }
    $thedict{"_8_parent"} = $parent;

    while ( $properties =~ /$regex(?&property)/g ) {
    if ( $parseall || ($property[1] =~ /TimingElements|DPTimingModeId/) ) {
    $thedict{$property[1]} = parsevalue($depth, $path . "/" . $property[1], $value[1]);
    for (my $ndx = 0; $ndx < $propertyndx; $ndx++) {
    if ( $parseall || ($property[$ndx] =~ /TimingElements|DPTimingModeId/) ) {
    #print "value:" . $value[$ndx] . "\n";
    $thedict{"_7_properties"}{$property[$ndx]} = parsevalue($nodeindent, $thepath . "/" . $property[$ndx], $value[$ndx]);
    }
    }


    push(@{$parent->{"_9_children"}}, \%thedict);
    $prevnode = \%thedict;

    #print "node:\n";
    #say Dumper \%thedict;
    #print "\n";

    return \%thedict;
    }

    sub parseioreg {
    while ( $_ =~ /$regex(?&node)/g ) {
    if ($nodename[1] =~ /^disp0@.*/) {
    $thenode = "disp0";
    my $thedispnodename = "";

    while ( /$regex(?&node)/g ) {
    if ($nodename =~ /^disp0@.*/) {
    $thedispnodename = "disp0";
    }
    elsif ($nodename[1] =~ /^dispext\d+@.*/) {
    $thenode = "dispext0";
    elsif ($nodename =~ /^dispext\d+@.*/) {
    $thedispnodename = "dispext0";
    }
    if ( $nodename[1] eq "AppleCLCD2" ) {
    $thenodes{$thenode} = parsenode(0, $thenode, $properties[1]);
    my $thedict = parsenode();

    if ( $nodename eq "AppleCLCD2" ) {
    $dispnodes{$thedispnodename} = $thedict;
    }
    }
    }

    sub dumpstruct {
    my $indenting = -1;
    my $path = "";
    my %donenodes = ();

    my $inner; $inner = sub {
    my $ref = $_[0];
    my $key = $_[1];
    @@ -316,31 +336,43 @@ sub dumpstruct {
    print '"',$key,'"',' : ';
    }
    if (ref $ref eq 'ARRAY') {
    print "[";
    my $needcomma = 0;
    for my $k(@{$ref}) {
    if ($needcomma) {
    print ",";
    if (exists $donenodes{$ref}) {
    print "@" . $donenodes{$ref};
    }
    else {
    $donenodes{$ref} = $path;
    print "[";
    my $needcomma = 0;
    for my $k(@{$ref}) {
    if ($needcomma) {
    print ",";
    }
    print "\n";
    $inner->($k);
    $needcomma = 1;
    }
    print "\n";
    $inner->($k);
    $needcomma = 1;
    #$inner->($_) for @{$ref};
    print "\n",' ' x ($indenting * 4),"]";
    }
    #$inner->($_) for @{$ref};
    print "\n",' ' x ($indenting * 4),"]";
    }
    elsif (ref $ref eq 'HASH'){
    print "{";
    my $needcomma = 0;
    for my $k(sort keys %{$ref}){
    if ($needcomma) {
    print ",";
    if (exists $donenodes{$ref}) {
    print "%" . $donenodes{$ref};
    }
    else {
    $donenodes{$ref} = $path;
    print "{";
    my $needcomma = 0;
    for my $k(sort keys %{$ref}){
    if ($needcomma) {
    print ",";
    }
    print "\n";
    $inner->($ref->{$k},$k);
    $needcomma = 1;
    }
    print "\n";
    $inner->($ref->{$k},$k);
    $needcomma = 1;
    print "\n",' ' x ($indenting * 4),"}";
    }
    print "\n",' ' x ($indenting * 4),"}";
    }

    elsif ( JSON::PP::is_bool($ref) ) {
    @@ -360,13 +392,13 @@ sub dumpstruct {
    }

    sub dumpresolutions {
    for my $j(sort keys %thenodes){
    for my $j(sort keys %dispnodes){
    print "\n$j:\n";
    for my $k(@{$thenodes{$j}{"TimingElements"}}) {
    for my $k(@{$dispnodes{$j}{"_7_properties"}{"TimingElements"}}) {
    my %l = %$k;
    printf(
    "%s%dx%d%s@%.3fHz %.3fkHz %.2fMHz h(%d %d %d %s) v(%d %d %d %s) %s%s%s%s%s\n",
    ((exists $thenodes{$j}{"DPTimingModeId"}) && $l{"ID"} eq $thenodes{$j}{"DPTimingModeId"}) ? " -> " : " ",
    ((exists $dispnodes{$j}{"_7_properties"}{"DPTimingModeId"}) && $l{"ID"} eq $dispnodes{$j}{"_7_properties"}{"DPTimingModeId"}) ? " -> " : " ",
    $l{"HorizontalAttributes"}{"Active"},
    $l{"VerticalAttributes"}{"Active"},
    $l{"IsInterlaced"} ? "i" : "",
    @@ -396,16 +428,15 @@ sub dumpresolutions {
    }

    my $utf8_encoded_json_text = "";


    while (<>) {
    #printioreg();

    parseioreg();

    #dumpstruct(\%thenodes); # outputs JSON with indents
    #dumpstruct($root{"_9_children"}); # outputs JSON with indents

    #$utf8_encoded_json_text = encode_json( \%thenodes ); # outputs JSON with no white space
    #$utf8_encoded_json_text = encode_json( \%root ); # outputs JSON with no white space
    #print $utf8_encoded_json_text . "\n";

    dumpresolutions();
  3. joevt created this gist Dec 8, 2020.
    412 changes: 412 additions & 0 deletions ioreg.pl
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,412 @@
    #!/bin/perl

    # by joevt Dec 7, 2020

    use strict;
    #use warnings;

    use JSON::PP;


    sub myinc
    {
    my $val = $_[0];
    $_[0] = $val > 1 ? 3 : 2;
    return $val > 1 ? 2 : 1;
    }

    my $parseall = 1;

    my $nodenamendx = 1;
    my $propertiesndx = 1;
    my $propertyndx = 1;
    my $valuendx = 1;
    my $stringndx = 1;
    my $fieldndx = 1;
    my $fieldsndx = 1;
    my $itemsndx = 1;
    my $itemndx = 1;

    my @nodename = ("s","s");
    my @properties = ("s","s");
    my @property = ("s","s");
    my @value = ("s","s");
    my @string = ("s","s");
    my @field = ("s","s");
    my @fields = ("s","s");
    my @items = ("s","s");
    my @item = ("s","s");


    #my $regex = qr

    my $regex = qr /(?x) # ignore white space in regular expressions (means all spaces, tabs, and newlines need to be escaped)
    (?m) # ^ matches line instead of start of string
    #(?i) # ^ case insensitive
    #(?p) # Preserve the string matched such that ${^PREMATCH}, ${^MATCH}, and ${^POSTMATCH} are available for use after matching.
    (?{
    $nodenamendx = 1;
    $propertiesndx = 1;
    $propertyndx = 1;
    $valuendx = 1;
    $stringndx = 1;
    $fieldndx = 1;
    $fieldsndx = 1;
    $itemsndx = 1;
    $itemndx = 1;
    })
    (?(DEFINE)(?'indent'^[|\ ]*+))
    (?(DEFINE)(?'nodehead'(?&indent)\+\-o\ (?P<nodename>.*?)(?{$nodename[myinc($nodenamendx)]=$+{nodename}})\ \ <class\ (?P<classname>[^,]+).*\n))
    (?(DEFINE)(?'nodeproperties'((?&indent)\{\n(?P<properties>(?&property)++)(?{$properties[myinc($propertiesndx)]=$+{properties}})(?&indent)\}\n|(?{$properties[myinc($propertiesndx)]=""}))))
    (?(DEFINE)(?'property'(?&indent)(?&string)(?{$property[myinc($propertyndx)]=$string[$stringndx-1]})\ =\ (?P<value>(?&item))(?{$value[myinc($valuendx)]=$+{value}}).*\n))
    (?(DEFINE)(?'node'(?&nodehead)(?&nodeproperties)))
    (?(DEFINE)(?'string'"(?P<stringx>[^"]*+)"(?{$string[myinc($stringndx)]=$+{stringx}})))
    (?(DEFINE)(?'number'\d++))
    (?(DEFINE)(?'boolean'(?:No|Yes)))
    (?(DEFINE)(?'data'<[0-9a-f]*+>))
    (?(DEFINE)(?'datahex'(?:\n(?&indent)[0-9A-F]++:(?:\ [0-9A-F]{2})++.*)++))
    (?(DEFINE)(?'field'(?&string)(?{$field[myinc($fieldndx)]=$string[$stringndx-1]})=(?&item)))
    (?(DEFINE)(?'dictionary'\{(?P<fields>(?:((?&field),)*+(?&field))|)(?{$fields[myinc($fieldsndx)]=$+{fields}})\}))
    (?(DEFINE)(?'array'\((?P<items>(?:((?&item),)*+(?&item))|)(?{$items[myinc($itemsndx)]=$+{items}})\)))
    (?(DEFINE)(?'dataarray'\<((?&item),)*+(?&item)\>))
    (?(DEFINE)(?'item'(?P<itemx>(?:(?&dictionary)|(?&array)|(?&data)|(?&dataarray)|(?&datahex)|(?&string)|(?&number)|(?&boolean)))(?{$item[myinc($itemndx)]=$+{itemx}})))
    /;


    sub printvalue {
    my $depth = $_[0];
    my $lvalue = $_[1];

    if ( $lvalue =~ /^$regex(?&array)/ ) {
    my $litems = $items[$itemsndx-1];
    printf("(");

    #print "«[" . $litems . "]»\n";

    my $needcomma = 0;
    while ( $litems =~ /$regex(?&item),?/g ) {

    #print "«“" . $item[$itemndx-1] . "”»\n";

    my $litem = $item[$itemndx-1];
    if ($needcomma) {
    printf (",");
    }
    printf ("\n%*s", $depth * 2 + 2, "");
    printvalue($depth + 1, $litem);
    $needcomma = 1;
    }
    if ($needcomma) {
    printf ("\n%*s", $depth * 2, "");
    }
    printf(")");
    }

    elsif ( $lvalue =~ /^$regex(?&dictionary)/ ) {
    my $lfields = $fields[$fieldsndx-1];
    printf("{");

    #print "«{" . $lfields . "}»\n";

    my $needcomma = 0;
    while ( $lfields =~ /$regex(?&field)(,?)/g ) {

    #print "«“" . $field[1] . " = " . $item[$itemndx-1] . "”»\n";

    my $lfield = $field[1];
    my $litem = $item[$itemndx-1];
    if ($needcomma) {
    printf (",");
    }
    printf("\n%*s\"%s\" : ", $depth * 2 + 2, "", $lfield);
    printvalue($depth + 1, $litem);
    $needcomma = 1;
    }
    if ($needcomma) {
    printf ("\n%*s", $depth * 2, "");
    }
    printf("}");
    }

    elsif ( $lvalue =~ /^$regex(?&datahex)/ ) {
    $lvalue =~ s/$regex(?&indent)(?P<achar>.)/' ' x ($depth * 2 + 2) . $+{achar}/ge;
    print $lvalue;
    }

    else {
    print $lvalue;
    }
    }

    sub printnode {
    my $depth = $_[0];
    my $properties = $_[1];
    printf ("%*s\"%s\" : {", $depth * 2, "", $nodename[1]);
    $depth += 1;
    my $needcomma = 0;
    while ( $properties =~ /$regex(?&property)/g ) {
    if ($needcomma) {
    printf (",");
    }
    printf ("\n");
    printf ("%*s\"%s\" : ", $depth * 2, "", $property[1]);
    printvalue($depth, $value[1]);
    $needcomma = 1;
    }
    if ($needcomma) {
    printf "\n";
    }
    print "}";
    }

    sub printioreg {
    my $needcomma = 0;
    while ( $_ =~ /$regex(?&node)/g ) {
    if ($needcomma) {
    printf (",");
    }
    printf ("\n");

    #printf ("%*s\"%s\" : {}", $depth * 2, "", $nodename[1]);
    printnode(0, $properties[1]);

    $needcomma = 1;
    }
    }

    my %thenodes = ();
    my $thenode = "";


    sub parsevalue {
    my $depth = $_[0];
    my $path = $_[1];
    my $lvalue = $_[2];

    if ( $lvalue =~ /^$regex(?&array)/ ) {
    my $litems = $items[$itemsndx-1];
    my @thearray = ();
    my $arrayndx = 0;
    while ( $litems =~ /$regex(?&item),?/g ) {
    my $litem = $item[$itemndx-1];
    $thearray[$arrayndx] = parsevalue($depth + 1, $path . '[' . $arrayndx . ']', $litem);
    $arrayndx++;
    }
    return \@thearray;
    }

    elsif ( $lvalue =~ /^$regex(?&dictionary)/ ) {
    my $lfields = $fields[$fieldsndx-1];
    my %thedict = ();
    while ( $lfields =~ /$regex(?&field)(,?)/g ) {
    my $lfield = $field[1];

    my $newpath = $path . '/' . $lfield;

    if ($parseall || ($newpath =~
    m"
    TimingElements\[\d+\](/
    (
    (
    ColorModes(\[\d+\]/
    ID
    )?
    )|
    (
    HorizontalAttributes(/
    (
    \w+
    )
    )?
    )|
    (
    VerticalAttributes(/
    (
    \w+
    )
    )?
    )|
    ID|
    IsInterlaced|
    IsOverscanned|
    IsPreferred|
    IsPromoted|
    IsSplit|
    IsVirtual
    )
    )?
    $"x))
    {
    my $litem = $item[$itemndx-1];
    $thedict{$lfield} = parsevalue($depth + 1, $newpath, $litem);
    }
    }
    return \%thedict;
    }

    elsif ( $lvalue =~ /^$regex(?&datahex)/ ) {
    $lvalue =~ s/^[ |]+[0-9A-F]+:((?: [0-9A-F]{2}){1,32}) .*/$1/gm;
    $lvalue =~ s/[\n ]//gm;
    return "<" . $lvalue . ">";
    }

    elsif ( $lvalue =~ /^$regex(?&number)/ ) {
    return $lvalue + 0;
    }

    elsif ( $lvalue =~ /^$regex(?&string)/ ) {
    return $string[$stringndx-1];
    }

    elsif ( $lvalue =~ /^$regex(?&boolean)/ ) {
    return ( $lvalue eq "Yes" ? JSON::PP::true : JSON::PP::false );
    }

    else {
    return $lvalue;
    }
    }

    sub parsenode {
    my $depth = $_[0];
    my $path = $_[1];
    my $properties = $_[2];

    $depth += 1;
    my $needcomma = 0;

    my %thedict = ();

    while ( $properties =~ /$regex(?&property)/g ) {
    if ( $parseall || ($property[1] =~ /TimingElements|DPTimingModeId/) ) {
    $thedict{$property[1]} = parsevalue($depth, $path . "/" . $property[1], $value[1]);
    }
    }

    return \%thedict;
    }

    sub parseioreg {
    while ( $_ =~ /$regex(?&node)/g ) {
    if ($nodename[1] =~ /^disp0@.*/) {
    $thenode = "disp0";
    }
    elsif ($nodename[1] =~ /^dispext\d+@.*/) {
    $thenode = "dispext0";
    }
    if ( $nodename[1] eq "AppleCLCD2" ) {
    $thenodes{$thenode} = parsenode(0, $thenode, $properties[1]);
    }
    }
    }

    sub dumpstruct {
    my $indenting = -1;
    my $inner; $inner = sub {
    my $ref = $_[0];
    my $key = $_[1];
    $indenting++;

    print ' ' x ($indenting * 4);
    if ($key) {
    print '"',$key,'"',' : ';
    }
    if (ref $ref eq 'ARRAY') {
    print "[";
    my $needcomma = 0;
    for my $k(@{$ref}) {
    if ($needcomma) {
    print ",";
    }
    print "\n";
    $inner->($k);
    $needcomma = 1;
    }
    #$inner->($_) for @{$ref};
    print "\n",' ' x ($indenting * 4),"]";
    }
    elsif (ref $ref eq 'HASH'){
    print "{";
    my $needcomma = 0;
    for my $k(sort keys %{$ref}){
    if ($needcomma) {
    print ",";
    }
    print "\n";
    $inner->($ref->{$k},$k);
    $needcomma = 1;
    }
    print "\n",' ' x ($indenting * 4),"}";
    }

    elsif ( JSON::PP::is_bool($ref) ) {
    print ($ref ? "true" : "false");
    }

    elsif (($ref ^ $ref) ne '0') {
    print '"',$ref,'"';
    }
    else {
    print $ref;
    }
    $indenting--;
    };
    $inner->($_) for @_;
    print "\n";
    }

    sub dumpresolutions {
    for my $j(sort keys %thenodes){
    print "\n$j:\n";
    for my $k(@{$thenodes{$j}{"TimingElements"}}) {
    my %l = %$k;
    printf(
    "%s%dx%d%s@%.3fHz %.3fkHz %.2fMHz h(%d %d %d %s) v(%d %d %d %s) %s%s%s%s%s\n",
    ((exists $thenodes{$j}{"DPTimingModeId"}) && $l{"ID"} eq $thenodes{$j}{"DPTimingModeId"}) ? " -> " : " ",
    $l{"HorizontalAttributes"}{"Active"},
    $l{"VerticalAttributes"}{"Active"},
    $l{"IsInterlaced"} ? "i" : "",

    $l{"VerticalAttributes"}{"PreciseSyncRate"} / 65536.0,
    $l{"HorizontalAttributes"}{"PreciseSyncRate"} / 65536.0,
    $l{"HorizontalAttributes"}{"PreciseSyncRate"} * $l{"HorizontalAttributes"}{"Total"} / 65536000.0,

    $l{"HorizontalAttributes"}{"FrontPorch"},
    $l{"HorizontalAttributes"}{"SyncWidth"},
    $l{"HorizontalAttributes"}{"BackPorch"},
    $l{"HorizontalAttributes"}{"SyncPolarity"} ? "+" : "-",

    $l{"VerticalAttributes"}{"FrontPorch"},
    $l{"VerticalAttributes"}{"SyncWidth"},
    $l{"VerticalAttributes"}{"BackPorch"},
    $l{"VerticalAttributes"}{"SyncPolarity"} ? "+" : "-",

    $l{"IsOverscanned"} ? " (overscan)" : "",
    $l{"IsPreferred"} ? " (preferred)" : "",
    $l{"IsPromoted"} ? " (promoted)" : "",
    $l{"IsSplit"} ? " (tiled)" : "",
    $l{"IsVirtual"} ? " (virtual)" : ""
    );
    }
    }
    }

    my $utf8_encoded_json_text = "";


    while (<>) {
    #printioreg();

    parseioreg();

    #dumpstruct(\%thenodes); # outputs JSON with indents

    #$utf8_encoded_json_text = encode_json( \%thenodes ); # outputs JSON with no white space
    #print $utf8_encoded_json_text . "\n";

    dumpresolutions();
    }