pldb (list): Allow multiple ranges.
[dvddb] / pldb
diff --git a/pldb b/pldb
index 2a16fe5..7f59373 100755 (executable)
--- a/pldb
+++ b/pldb
@@ -44,7 +44,7 @@ sub db_connect (;$) {
              RaiseError => 1,
              ReadOnly => 1,
              %$opts);
-  $DB = DBI->connect("dbi:Pg:host=roadstar", "", "", \%opts);
+  $DB = DBI->connect("dbi:Pg:host=roadstar;dbname=mdw", "", "", \%opts);
 }
 
 sub must_exist ($$@) {
@@ -104,7 +104,7 @@ defcmd "lists", sub {
   while (my @r = $st->fetchrow_array) { my ($name) = @r; print "$name\n"; }
 };
 
-defcmd "edit GROUP LIST[=[+|-]POS]|-LIST ...", sub {
+defcmd "edit GROUP LIST[[+|-]=POS]|-LIST ...", sub {
   @_ >= 2 or bail_usage;
   my ($group, @ops) = @_;
 
@@ -123,12 +123,13 @@ defcmd "edit GROUP LIST[=[+|-]POS]|-LIST ...", sub {
   my $st_del = $DB->prepare
     ("DELETE FROM playlist_position WHERE group_name = ? AND list_name = ?");
 
-  for my $op (@ops) {
+  OP: for my $op (@ops) {
     if ($op =~ /^ - (.++) $/x) {
       my ($list) = ($1);
       must_list $list; must_member $group, $list;
       $st_del->execute($group, $list);
-    } elsif ($op =~ /^ ([^=]++) (?: = ([-+])?+ (\d++))?+ $/x) {
+    } elsif ($op =~ /^ ((?: [^-+=]++ | [-+][^=])++)
+                      (?: ([-+])?+ = ([-+]?+ \d++))?+ $/x) {
       my ($list, $rel, $pos) = ($1, $2, $3);
 
       $st_chk->execute($list);
@@ -142,8 +143,10 @@ defcmd "edit GROUP LIST[=[+|-]POS]|-LIST ...", sub {
        @r or die "`$list' is not a member of `$group'";
        my ($cur) = @r; $st_getpos->finish;
 
-       if ($rel eq "+") { $pos = $cur + $pos; }
-       elsif ($rel eq "-") { $pos = $cur - $pos; }
+       my $end;
+       if ($rel eq "+") { $pos = $cur + $pos; $end = $n; }
+       elsif ($rel eq "-") { $pos = $cur - $pos; $end = 0; }
+       if ($pos == $end) { $st_del->execute($group, $list); next OP; }
       }
       0 <= $pos && $pos < $n
        or die "`$list' position $pos out of range 0 .. $n";
@@ -257,7 +260,7 @@ defcmd "next [-pu] [-o N] [-n N] GROUP [LIST]", sub {
   defined $prevlist and print "$prevlist\n", map "\t$_\n", @out;
 };
 
-defcmd "list LIST", sub {
+defcmd "list [-g [MARK=]GROUP,...] [-r [LO][-[HI]],...] LIST", sub {
   local @ARGV = @_;
   my $bogusp = 0;
   my %opt;
@@ -306,12 +309,42 @@ defcmd "list LIST", sub {
      WHERE e.list_name = ?";
   my @arg = ($list);
 
+  my @r;
   if (defined $opt{"r"}) {
-    $opt{"r"} =~ /^ (\d+)? (?: - (\d+)?)? $/x
-      or die "invalid range expression `$opt{'r'}'";
-    my ($lo, $hi) = ($1, $2);
-    if (defined $lo) { $q .= " AND ? <= e.entry"; push @arg, $lo; }
-    if (defined $hi) { $q .= " AND e.entry < ?"; push @arg, $hi; }
+    for my $r (split /,/, $opt{"r"}) {
+      $r =~ /^ (\d+)? (?: - (\d+)?)? $/x
+       or die "invalid range expression `$r'";
+      my ($lo, $hi) = ($1 // 0, $2 // $emax + 1);
+      $hi <= $emax or $hi = $emax + 1;
+      push @r, [$lo, $hi];
+    }
+  }
+
+  if (@r) {
+    my ($curr_lo, $curr_hi) = (undef, undef);
+    my @newr;
+    RANGE: for my $r (sort { $a->[0] <=> $b->[0] ||
+                            $a->[1] <=> $b->[1] } @r) {
+      my ($lo, $hi) = @$r;
+      if (defined $curr_hi) {
+       if ($lo <= $curr_hi) { $curr_hi = $hi; next RANGE; }
+       push @newr, [$curr_lo, $curr_hi];
+      }
+      ($curr_lo, $curr_hi) = ($lo, $hi);
+    }
+    push @newr, [$curr_lo, $curr_hi];
+    my @q;
+    for my $r (@newr) {
+      my ($lo, $hi) = @$r;
+      my @qr;
+      if ($lo > 0) { push @qr, "? <= e.entry"; push @arg, $lo; }
+      if ($hi <= $emax ) { push @qr, "e.entry < ?"; push @arg, $hi; }
+      if (!@qr) { }
+      elsif (@qr == 1) { push @q, @qr; }
+      else { push @q, "(" . join(" AND ", @qr) . ")"; }
+    }
+    if (@q == 1) { $q .= " AND $q[0]"; }
+    else { $q .= " AND (" . join(" OR ", @q) . ")"; }
   }
 
   $q .= " ORDER BY e.entry ASC";