QL.túra

Kulturális smörgåsbord Esterházytól Wass Albertig, a gumicsizmától az iPad-ig, a Teletabiktól Sexpírig, a makrofágtól a mikrokontrollerig, miazmás...

Címkék

2000 (20) 2001 (6) 2002 (16) 2003 (164) 2004 (61) 2005 (40) 2006 (31) 2007 (28) 2008 (33) 2009 (175) 2010 (188) 2011 (201) 2012 (86) 2013 (40) 2014 (36) 2015 (26) 2016 (10) adáshiba (91) android (1) animáció (93) cygwin (3) film (410) gezarol (13) hájtek (159) hangoskönyv (32) ipad (17) klip (12) könyv (191) linux (29) színház (169) vers (17) windows (37) zene (111) Címkefelhő

+jegyzések

Most ...

... múlok .osan

bmi_tiny.png


... hallgatom
Szabó Magda: Régimódi történet
https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSqTmZEqlCRFgojyt52Q2n_qiMTyVlt-zJu-DpbNKVY6OQbBh4u


... olvasom
Alexander Steele (szerk.): Kezdő írók kézikönyve


... (Kik ezek? Kik ezek az embek? Kik ezek?)
profile for TrueY on Stack Exchange, a network of free, community-driven Q&A sites
free counters

[Hájtek] Palm m515 adatbázisok konverziója #2 (Pilot-DB)

2012.02.10. 11:00 | TrueY | Szólj hozzá!

Címkék: linux 2011 hájtek

Az első cikkben leírtam, hogyan tudjuk olvashatóvá konvertálni a belső adatbázisokat. Most a Pilot-DB adatbázisok konvertálását fogom összefoglalni.

Nekem a 1.2.0m verzió van feltéve. A most elérhető legfrissebb az 1.1.3. Vajon mi történet a későbbi verziókkal? Azok miért tűntek el a valahol, az internet sűrű bugyrában? Van windows-os verzió. A pdb2csv program a Pilot-DB PDB file-jait csv formátumban írja ki (típus: DBOS, DB00). Pluszban egy másik leíró file-t is létrehoz, amit a fejlesztők szerint ifo kiterjesztéssel illik létrehozni (ifo file formátum leírása). Próbálkozzunk a konverzióval.

wine pdb2csv.exe Backup/file.PDB

Elvileg a csv2pdb-vel létre is hozhatunk ilyen file-okat. A gond az, hogy a belsőleg létrehozottakat nem lehet így konvertálni. Hibás rekordformátumra hivatkozik (file.PDB: record is corrupt).

Letöltöttem hát a konvertáló program forrását. A g++ 4.0-ás változatához egy kis foltozás szükséges (ezt innen tölthetjük le). Ez után ki-tar, "./configure", make, "make install" (ehhez megfelelő jogok kellenek). A program futtatásához a lefordított könyvtárak elérését is be kell állítani. Annak a path-szát adjuk meg:

LD_LIBRARY_PATH=/usr/local/lib/palm-db-tools/flatfile pdb2csv ...

De ez is ugyanazt a hibaüzenetet dobja a Palm-on létrehozott adatbázisokra. Valami jobb megoldást kell keresni hát... A CPAN-on találtam egy perl csomagot. Feltettem hát:

cpan
install Palm::PDB

Ezzel szépen meg lehet nyitni a file-okat, de sajna a teljes header-t és az adat rekordokat nem fordítja le, hanem úgy hagyja, rusnyán, binárisan. A fordítást tehát nekem kellett megírni. Ebben nagy segítségemre volt a a pdb2csv forrása. Íme hát. Némi kis kiegészítéssel körítve...

A szokásos fejléc, plusz a PDB -hez kapcsolatos fejlécek

#!/usr/bin/perl
#
# Open PDB files
#
# ...

use strict;
use warnings;
use POSIX;

use Palm::PDB;
use Palm::Raw;
Az egyes mezőtípusok konverzióját segítő hash:
sub dump_pdb($); # Dump Pilot-DB

Palm::PDB::RegisterPDBHandlers("Palm::Raw", "");

# Filed types: String(0), Note(5), Boolean(1), Integer(2), Float(8),
#   Calculated(9), Date(3), Time(4), List(6), Link(7) = Z*?, Linked(10) = nn?
my %fld_type = (
    0 => ["StrZ", -1, "Z*"],     # ASCIZ
    1 => ["Bool", 1, "C"],       # Boolean
    2 => ["Int",   4, "N"],      # N big-endian, V little-endian,
    3 => ["Date",  4, "nCC"],    # YYYY, M, D
    4 => ["Time",  2, "CC"],     # HH:MM
    5 => ["Note",  -2, "Z*nZ*"], # ASCIZ. some num???, ASCIZ
    6 => ["List",  1, "C"],      # C unsigned byte
    7 => ["Link",  -1, "Z*"],    # ??? Not tested
    8 => ["Float", 8, "d>"],     # big-endian double
    9 => ["Calc", 9, "C9"],      # How to decode?
    10 => ["Linked", 4, "nn"],   # ??? Not tested
);
A parancssor feldolgozása és az eredmény xml formátumban való kiírása következik. Használata: pdb2xml.pl [--header|--test] file.PDB [file1.PDB ...]. A --header csak a fejlécet írja ki, --test ellenőrzi, hogy Pilot DB-ről van-e szó (aláírás DBOS és DB00) és annak megfelelően kilép a programból.

for (@ARGV) {
    if ($_ =~ /^--(.*)(?:=(.*))?$/) {
        my ($arg, $val) = ($1, $2);
    if ($arg eq "header") { $fHdr = 1;
    } elsif ($arg eq "test") { $fHdr = 2;
    } else { die "Bad arg ($arg)";
    }
    } else {
    if (-r $_) { push @files, $_;
    } else { die "File cannot read";
    }
    }
}

if ($#files == -1) { die "No file given"; }
for (@files) { dump_pdb($_); }

exit;
Pár segédfüggvény. Az első konvertál, a második rekurzívan kiír egy hash referenciát, aminek az értéke lehet hash, array, vagy egy string érték.

sub enc($) {
    my $x = shift;
    $x =~ s{&}{&}; 
    $x =~ s{<}{&lt;}; 
    $x =~ s{>}{&gt;}; 

    return $x;
}

sub show(%;$);

sub show(%;$) {
    my ($h, $n) = @_;

    if (!defined $n) { $n = 0; }
    my $sp = " " x ($n * 2);
    for my $k (keys %$h) {
    my $v = $h->{$k};
    if (!defined $v) { print $sp, "<$k/>\n";
    } elsif (ref $v eq "") {
        print $sp, ($v ne "" ? "<$k>".enc($v)."</$k>" : "<$k/>"), "\n";
    } elsif (ref $v eq "HASH") {
        print $sp, "<$k>\n";
        show($v, $n + 1);
        print $sp, "</$k>\n";
    } elsif (ref $v eq "ARRAY") {
        my $r = ref $v->[0];
        if ($r eq "") {
        for my $l (@$v) {
            print $sp, ($l ne "" ? "<$k>".enc($l)."</$k>" : "<$k/>"),
                "\n";
        }
        } elsif ($r eq "HASH") {
        print $sp, "<$k>\n";
        for my $l (@$v) { show $l, $n + 1; }
        print $sp, "</$k>\n";
        } elsif ($r eq "ARRAY") { die "Bad ARRAY of ARRAY";
        } else { die "Bad ref ($r) of ARRAY";
        }
    } else {
        die "Bad reference (", ref($v), ")";
    }
    }
} # End of show
És végül a konvertáló függvény. Először a fejléc információt gyűjti ki. A definiált "view"-kkal nem foglalkoztam. A "calc" mezőket is csak felsorolom és az adattartalmat nem konvertáltam. Aki ezeket is értelmezni szeretné, írja meg és küldje el! Előre is kösz!
sub dump_pdb($) {
    my $file = shift;
    my $pdb = new Palm::PDB();
    $pdb->Load($file);

    if ($pdb->{creator} ne "DBOS" || $pdb->{type} ne "DB00") {
        if ($fHdr == 2) { exit 1; }
        die "$file is not a Pilot-DB file";
    }
    if ($fHdr == 2) { exit; }

    my ($name, $major, $minor) =
        ($pdb->{"name"}, $pdb->{"version"}, $pdb->{"modnum"});
    # Sometimes the name contains more bytes
    if ($name =~ /\000/) { $name = unpack "Z*", $name; }

    #
    # Read Header
    #

    my $hdr = $pdb->{appinfo};

    # 2, 64 can occure multiple times
    my %hdr_type = (0 => "Field name", 1 => "Field type", 2 => "Field data",
        64 => "List View Definitions", 65 => "List View Options",
    128 => "LFind Options", 254 => "About", 1024 => "???");
    my %hdr;

    my $hdr1 = $hdr;
    my ($wmode, $fld_no) = unpack "n2", $hdr1;
   
    # $wmode == 32768 (read only)
    # First byte: other settings: backup, copy-prevention
    if ($wmode != 0 && $wmode != 32768) { die "Bad mode ($wmode)"; }

    #
    # Collect header chunks
    #

    $hdr1 = substr($hdr1,4);
    while (length($hdr1)) {
        my ($t, $l) = unpack "n2", $hdr1;
        $hdr1 = substr($hdr1, 4);
        if (length($hdr1) < $l) { die "Length problem"; }
        if (!exists $hdr_type{$t}) { die "Bad header chunk type ($t)"; }

        my $chunk = substr($hdr1, 0, $l);

        if ($t == 2 || $t == 64) { push @{$hdr{$t}}, $chunk;
        } else {
            if (exists $hdr{$t}) { die "Multiple definitions ($t)"; }
            $hdr{$t} = $chunk;
        }

        $hdr1 = substr($hdr1, $l);
    }

    #
    # Process header chunks
    #

    # Get column titles

    my @fld_names = unpack("Z*" x $fld_no, $hdr{0});
    if ($#fld_names != $fld_no - 1) { die "Field count not match"; }

    # Get header bytes

    my $hdr_bytes = $fld_no * 2;
    if (length($hdr{1}) != $hdr_bytes) { die "Field type size not match"; }

    # Read field types
    # Header record (contains column headers, enum values, views)

    my @fld_types = unpack "n" x $fld_no, $hdr{1};
    my @fld_type_names = map {
        if (!exists $fld_type{$_}) { die "Not defined field type ($_)"; }
        $fld_type{$_}[0]
    } @fld_types;

    # Read List values

    my @list;
    my @enum;
    my @calc;
    for my $x (@{$hdr{2}}) {
        my $seq = unpack "n", $x;
        $x = substr($x, 2);
        my $fld_type_name = $fld_type{$fld_types[$seq]}[0];

        if ($fld_type_name eq "List") {
            my ($pcs, $w) = unpack "nn", $x;
            $x = substr($x, 4);
            $list[$seq] = [ unpack("Z*" x $pcs, $x) ];
            push @enum, {list =>
                [{field_name => $fld_names[$seq]}, {db => $pcs},
                {name => [@{$list[$seq]}]}, {seq => $seq}, {_what => $w}]
            };
            if (scalar(@{$list[$seq]}) != $pcs) {
                die "Bad number of list elements ($seq)";
            }
        } elsif ($fld_type_name eq "Calc") {
            push @calc, {calc => [{seq => $seq}, {length => length($x)}]};
        } else { die "Bad field type for DATA ($fld_type_name)";
        }
    }

    #
    # Create header tree
    #

    my @wfld;
    for (my $i = 0; $i <= $#fld_names; ++$i) {
         push @wfld, {field => [{name => $fld_names[$i]},
             {type_name => $fld_type_names[$i]}, {seq => $i}]};
    }

    my $hhdr = [ {name => $name}, {version => $major}, {modnum => $minor},
        {field_no => $fld_no},
        {field_names => join("|", @fld_names)},
        {field_type => join("|", @fld_type_names)},
        {fields => [@wfld]},
        {lists => [@enum]},
        {mode => $wmode}
    ];
    if ($#calc > -1) { push @$hhdr, {calculations => [@calc]}; }

    #
    # Read records
    #

    # {records} - reference to one record (hash)
    # {id}, {category} (0), {attributes} (hash dirty), {offset}
    # {data} -> raw record
    my $res = $pdb->{records};
    my $hbody = [{record_total_no => scalar(@$res)}];
    if ($fHdr) { exit 0; }

    my $n = 0;
    my $ni = 0;
    my $N = scalar @$res;
    my @records;
    for my $e (@$res) {
        ++$n;
        # Dirty => 1, dirty => 1, if deleted: Delete => 1, expunged => 1
        if (exists $e->{attributes}{Delete}) {
            warn "---- Deleted $n --------\n";
            next;
        }
        my $record = [{record_no => ++$ni}, {record_ord => $n}, {fields => []}];

        for my $k (keys %$e) {
            next if $k eq "data";
            push @$record, {$k => $e->{$k}};
        }

        my $dat = $e->{data};
        if (length($dat) == 0) { die "Zero length"; }

        # get data positions
        my @pos = unpack "n" x $fld_no, $dat;
        $dat = substr($dat, $hdr_bytes);
        my $empty = 0;
        my @fields;

        for (my $i = 0; $i <= $#fld_types; ++$i) {
            my $type = $fld_types[$i];
            if (!exists $fld_type{$type}) { die "$type not defined"; }
            my $tnam = $fld_type{$type}[0];
            my $size = $fld_type{$type}[1];
            my $utyp = $fld_type{$type}[2];
            my @val = unpack $utyp, $dat;

            if ($size == -1) { $size = length($val[0]) + 1; # String
            } elsif ($size == -2) {
                if (length($val[0]) == 0) {
                    $size = 3;
                    pop @val; # Empty Note
                    ++$empty;
                } else { $size = length($val[0]) + length($val[2]) + 4 ; # Note
                }
           
            }
            $dat = substr($dat, $size);

            my $info = "";
            if ($tnam eq "List") {
                if (!defined $list[$i]) { die "List ($n/$i) not defined"; }
                my $val = $val[0];
                $info = ($val == 255 ? "N/A" : $list[$i][$val]);
                if (!defined $info) {
                    die "Not defined list item ($n/$i)";
                }
                $val[0] = $info;
                $info = "";
            }

            for (@val) { s/\x95/*/g; }

            push @fields, {field => [{name => $fld_names[$i]}, {type => $tnam},
                {value => [@val]}, {info => $info}]};
        }
        $record->[2] = {fields => [@fields]};
        push @records, {record => $record};

        # Dirty hack. Some byte remains if a Note is empty
        if (length($dat) != $empty) {
            print "  dt (", length($dat), "): $dat\n";
            warn "Some data remainig at record#$n, length: ", length($dat);
        }
    }

    push @$hbody, {records => [@records]};

    print '<?xml version="1.0" encoding="ISO-8859-2"?>', "\n";
    #print '<?xml-stylesheet type="text/css" href="x.css"?>', "\n";
    show {pilot_db => [{header => $hhdr}, {body => $hbody}]};
} # End of dump_db
Az xml-t pedig mondjuk xslt-vel át tudjuk konvertálni tetszőleges más formátumra is.

Mentegessünk minden nap!

A bejegyzés trackback címe:

https://qltura.blog.hu/api/trackback/id/tr723343649

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.
süti beállítások módosítása