basic tag path traversal
authorIan Beckwith <ianb@erislabs.net>
Tue, 21 Sep 2010 03:43:28 +0000 (04:43 +0100)
committerIan Beckwith <ianb@erislabs.net>
Tue, 21 Sep 2010 03:43:28 +0000 (04:43 +0100)
lib/ID3FS/DB.pm
lib/ID3FS/Fuse.pm
lib/ID3FS/Path.pm
lib/ID3FS/PathElement/Album.pm [new file with mode: 0644]
lib/ID3FS/PathElement/Artist.pm [new file with mode: 0644]
lib/ID3FS/PathElement/Boolean.pm [new file with mode: 0644]
lib/ID3FS/PathElement/File.pm [new file with mode: 0644]
lib/ID3FS/PathElement/Tag.pm [new file with mode: 0644]
lib/ID3FS/PathElement/Tagval.pm [new file with mode: 0644]

index b8a2c8c..fe660d5 100644 (file)
@@ -83,12 +83,60 @@ sub cmd_sth
 
 sub tags
 {
-    my($self, $path)=@_;
-    my $sql="SELECT DISTINCT name FROM tags;";
-    my $tags=$self->cmd_rows($sql);
+    my($self, @constraints)=@_;
+    if(!@constraints) # /
+    {
+       my $sql="SELECT DISTINCT name FROM tags;";
+       my $tags=$self->cmd_rows($sql);
+       return(map { $_->[0]; } @$tags);
+    }
+    my @file_ids=();
+    my @tag_ids=();
+    my $main_sql_start=("SELECT DISTINCT tags.name FROM files\n" .
+                       "INNER JOIN files_x_tags ON files.id=files_x_tags.files_id\n" .
+                       "INNER JOIN tags ON tags.id=files_x_tags.tags_id\n" .
+                       "WHERE files.id in (" .
+                       ("\tSELECT DISTINCT files.id FROM files\n" .
+                        "\tINNER JOIN files_x_tags ON files.id=files_x_tags.files_id\n" .
+                        "\tINNER JOIN tags ON tags.id=files_x_tags.tags_id\n" .
+                        "\tWHERE tags.id in ("));
+    my $main_sql_mid=")\n) AND tags.id NOT IN (";
+    my $main_sql_end=")\n";
+    while(my $constraint=shift @constraints)
+    {
+       print "CONSTRAINT: $constraint->{name}\n";
+       my $cid=$constraint->{id};
+       push(@tag_ids, $cid);
+    }
+    my $sql = ($main_sql_start . join(", ", @tag_ids) .
+              $main_sql_mid   . join(", ", @tag_ids) .
+              $main_sql_end);
+    print "SQL: $sql\n";
+    my $result=$self->cmd_rows($sql);
+    my @tagnames=map { $_->[0]; } @$result;
+    print "SUBNAMES: ", join(', ', @tagnames), "\n";
+    return(@tagnames);
+}
+
+sub tag_values
+{
+    my($self, $tag)=@_;
+    my $sql=("SELECT DISTINCT tagvals.name FROM tags\n" .
+            "INNER JOIN tags_x_tagvals ON tags.id=tags_x_tagvals.tags_id\n" .
+            "INNER JOIN tagvals ON tagvals.id=tags_x_tagvals.tagvals_id\n" .
+            "WHERE tags.name=?");
+    my $tags=$self->cmd_rows($sql, $tag);
     return(map { $_->[0]; } @$tags);
 }
 
+sub tag_id
+{
+    my($self, $tag)=@_;
+    my $sql='SELECT id FROM tags WHERE name=?';
+    my ($id)=$self->cmd_onerow($sql, $tag);
+    return($id);
+}
+
 sub add
 {
     my($self,$path)=@_;
index 68a1b52..de81df9 100644 (file)
@@ -4,7 +4,7 @@ use strict;
 use warnings;
 use ID3FS::Path;
 use Fuse;
-use POSIX qw(EINVAL EROFS EOPNOTSUPP S_IRUSR S_IRGRP S_IROTH S_IXUSR S_IXGRP S_IXOTH);
+use POSIX qw(EINVAL EROFS ENOENT EOPNOTSUPP S_IRUSR S_IRGRP S_IROTH S_IXUSR S_IXGRP S_IXOTH);
 use vars qw($TYPE_DIR $TYPE_SYMLINK);
 ($TYPE_DIR, $TYPE_SYMLINK)=(0040, 0120);
 
@@ -63,15 +63,15 @@ sub run
 sub getattr
 {
     my($self, $filename)=@_;
-    print "GETATTR: $filename\n";
+    print "**GETATTR: $filename\n";
+    my $path=ID3FS::Path->new($self->{db}, $filename);
+    return(-ENOENT()) unless($path->isvalid());
     my($dev,$ino,$nlink)=(0,0,1);
     my $uid=$<;
     my $gid=(split(/ /, $( ))[0];
     my($rdev,$size)=(0,1);
     my($atime,$mtime,$ctime)=(0,0,0);
     my($blksize,$blocks)=(512,1);
-
-    my $path=ID3FS::Path->new($self->{db}, $filename);
     my $mode=$self->mode( $path->isdir() ? $TYPE_DIR : $TYPE_SYMLINK );
     return($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
           $atime,$mtime,$ctime,$blksize,$blocks);
@@ -80,7 +80,7 @@ sub getattr
 sub readlink
 {
     my($self,$filename)=@_;
-    print "READLINK: $filename\n";
+    print "**READLINK: $filename\n";
     my $path=ID3FS::Path->new($self->{db}, $filename);
     return(-EINVAL()) if($path->isdir());
     return $path->dest();
@@ -89,8 +89,9 @@ sub readlink
 sub getdir
 {
     my($self, $filename)=@_;
-    print "GETDIR: $filename\n";
+    print "**GETDIR: $filename\n";
     my $path=ID3FS::Path->new($self->{db}, $filename);
+    return(-ENOENT()) unless($path->isvalid());
     if($path->isdir())
     {
        return(".", "..", $path->dirents(), 0);
index b2202c2..7ceb45b 100644 (file)
@@ -2,6 +2,15 @@ package ID3FS::Path;
 
 use strict;
 use warnings;
+use ID3FS::PathElement::Artist;
+use ID3FS::PathElement::Album;
+use ID3FS::PathElement::Boolean;
+use ID3FS::PathElement::File;
+use ID3FS::PathElement::Tag;
+use ID3FS::PathElement::Tagval;
+
+our ($STATE_INVALID, $STATE_ROOT, $STATE_WANTMORE, $STATE_TAG, $STATE_TAGVAL,
+     $STATE_BOOL, $STATE_ARTIST, $STATE_ALBUM, $STATE_FILE)=(0..8);
 
 sub new
 {
@@ -12,15 +21,26 @@ sub new
 
     $self->{db}=shift;
     $self->{path}=shift;
-
+    $self->parse();
+    print "STATE: ", $self->state(), "\n";
     return $self;
 }
 
 sub isdir
 {
     my($self)=@_;
-    return 1 if($self->{path} eq "/");
-    return 0;
+    if(($self->state() eq $STATE_FILE) ||
+       ($self->state() eq $STATE_INVALID))
+    {
+       return 0;
+    }
+    return 1;
+}
+
+sub isvalid
+{
+    my($self)=@_;
+    return($self->state() != $STATE_INVALID);
 }
 
 sub dest
@@ -32,8 +52,133 @@ sub dest
 sub dirents
 {
     my($self)=@_;
-    return $self->{db}->tags();
+    return $self->{db}->tags(@{$self->{elements}});
+#    elsif($self->state() == $STATE_WANTMORE)
+#    {
+#      return(qw(AND OR ALL));
+#    }
 }
 
+sub parse
+{
+    my($self)=@_;
+    @{$self->{components}}=split(/\//, $self->{path});
+    shift @{$self->{components}}; # drop empty field before leading /
+    print "PATH: $self->{path}\n";
+    print "COMPONENTS: ", join(' | ', @{$self->{components}}), "\n";
+    $self->state($STATE_ROOT);
+    return if($self->{path} eq "/");
+    my @parts=@{$self->{components}};
+    my($tag, $tagval);
+    $self->{elements}=[];
+    while(my $name=shift @parts)
+    {
+       print "NAME: $name\n";
+       if   ($self->state() == $STATE_INVALID)
+       {
+           print "SM: INVALID: $name\n";
+           return;
+       }
+       elsif($self->state() == $STATE_ROOT)
+       {
+           print "SM: ROOT: $name\n";
+           $tag=ID3FS::PathElement::Tag->new($self->{db}, $name);
+           if($tag)
+           {
+               push(@{$self->{elements}}, $tag);
+               $self->state($STATE_WANTMORE);
+           }
+           else
+           {
+               $self->state($STATE_INVALID);
+           }
+       }
+       elsif($self->state() == $STATE_WANTMORE)
+       {
+           print "SM: WANTMORE: $name\n";
+           $tag=ID3FS::PathElement::Tag->new($self->{db}, $name);
+           if($tag)
+           {
+               push(@{$self->{elements}}, $tag);
+#              $self->state($STATE_TAG);
+           }
+           else
+           {
+               $self->state($STATE_INVALID);
+           }
+#          if(($name eq "AND") || ($name eq "OR"))
+#          {
+#              $self->state($STATE_BOOL);
+#              push(@{$self->{elements}}, ID3FS::PathElement::Boolean->new($name));
+#          }
+#          elsif($name eq "ALL")
+#          {
+#              $self->state($STATE_ARTIST);
+#          }
+#          else
+#          {
+#              $self->state($STATE_INVALID);
+#          }
+       }
+       elsif($self->state() == $STATE_TAG)
+       {
+           print "SM: TAG: $name\n";
+           $self->state($STATE_WANTMORE);
+           next;
+           my @valid_tagvals=$self->{db}->tag_values($tag);
+           print "TAGVALUES: $name: ", join(', ', @valid_tagvals), "\n";
+           if(@valid_tagvals)
+           {
+               if(grep { $name eq $_; } @valid_tagvals)
+               {
+                   print "TAGVAL VALID\n";
+                   $self->state($STATE_TAGVAL);
+                   push(@{$self->{elements}}, ID3FS::PathElement::Tagval($name));
+               }
+               else
+               {
+                   print "ERROR: unknown tagval: $tagval\n";
+                   $self->state($STATE_INVALID);
+               }
+           }
+           else
+           {
+               $self->state($STATE_INVALID);
+           }
+       }
+       elsif($self->state() == $STATE_TAGVAL)
+       {
+           print "SM: TAGVAL: $name\n";
+       }
+       elsif($self->state() == $STATE_BOOL)
+       {
+           print "SM: BOOL: $name\n";
+       }
+       elsif($self->state() == $STATE_ARTIST)
+       {
+           print "SM: ARTIST: $name\n";
+       }
+       elsif($self->state() == $STATE_ALBUM)
+       {
+           print "SM: ALBUM: $name\n";
+       }
+       elsif($self->state() == $STATE_FILE)
+       {
+           print "SM: FILE: $name\n";
+       }
+       else
+       {
+           print "SM: ERROR: UNKNOWN STATE: $self->{state}\n";
+           $self->state($STATE_INVALID);
+       }
+    }
+}
+
+sub state
+{
+    my($self, $newstate)=@_;
+    $self->{state}=$newstate if(defined($newstate));
+    return $self->{state};
+}
 
 1;
diff --git a/lib/ID3FS/PathElement/Album.pm b/lib/ID3FS/PathElement/Album.pm
new file mode 100644 (file)
index 0000000..1a4b7d5
--- /dev/null
@@ -0,0 +1,18 @@
+package ID3FS::PathElement::Album;
+
+use strict;
+use warnings;
+
+sub new
+{
+    my $proto=shift;
+    my $class=ref($proto) || $proto;
+    my $self={};
+    bless($self,$class);
+
+    $self->{name}=shift;
+
+    return $self;
+}
+
+1;
diff --git a/lib/ID3FS/PathElement/Artist.pm b/lib/ID3FS/PathElement/Artist.pm
new file mode 100644 (file)
index 0000000..9a6c669
--- /dev/null
@@ -0,0 +1,18 @@
+package ID3FS::PathElement::Artist;
+
+use strict;
+use warnings;
+
+sub new
+{
+    my $proto=shift;
+    my $class=ref($proto) || $proto;
+    my $self={};
+    bless($self,$class);
+
+    $self->{name}=shift;
+
+    return $self;
+}
+
+1;
diff --git a/lib/ID3FS/PathElement/Boolean.pm b/lib/ID3FS/PathElement/Boolean.pm
new file mode 100644 (file)
index 0000000..8f2f35f
--- /dev/null
@@ -0,0 +1,18 @@
+package ID3FS::PathElement::Boolean;
+
+use strict;
+use warnings;
+
+sub new
+{
+    my $proto=shift;
+    my $class=ref($proto) || $proto;
+    my $self={};
+    bless($self,$class);
+
+    $self->{name}=shift;
+
+    return $self;
+}
+
+1;
diff --git a/lib/ID3FS/PathElement/File.pm b/lib/ID3FS/PathElement/File.pm
new file mode 100644 (file)
index 0000000..2ec2f3b
--- /dev/null
@@ -0,0 +1,18 @@
+package ID3FS::PathElement::File;
+
+use strict;
+use warnings;
+
+sub new
+{
+    my $proto=shift;
+    my $class=ref($proto) || $proto;
+    my $self={};
+    bless($self,$class);
+
+    $self->{name}=shift;
+
+    return $self;
+}
+
+1;
diff --git a/lib/ID3FS/PathElement/Tag.pm b/lib/ID3FS/PathElement/Tag.pm
new file mode 100644 (file)
index 0000000..7113b43
--- /dev/null
@@ -0,0 +1,21 @@
+package ID3FS::PathElement::Tag;
+
+use strict;
+use warnings;
+
+sub new
+{
+    my $proto=shift;
+    my $class=ref($proto) || $proto;
+    my $self={};
+    bless($self,$class);
+
+    $self->{db}=shift;
+    $self->{name}=shift;
+    $self->{id}=($self->{db}->tag_id($self->{name}));
+    return(undef) unless(defined($self->{id}));
+    print "TAG ID: $self->{id}\n";
+    return($self);
+}
+
+1;
diff --git a/lib/ID3FS/PathElement/Tagval.pm b/lib/ID3FS/PathElement/Tagval.pm
new file mode 100644 (file)
index 0000000..43c2f49
--- /dev/null
@@ -0,0 +1,18 @@
+package ID3FS::PathElement::Tagval;
+
+use strict;
+use warnings;
+
+sub new
+{
+    my $proto=shift;
+    my $class=ref($proto) || $proto;
+    my $self={};
+    bless($self,$class);
+
+    $self->{name}=shift;
+
+    return $self;
+}
+
+1;