(file) Return to RcsLite.pm CVS log (file) (dir) Up to [RizwankCVS] / geekymedia_web / twiki / lib / TWiki / Store

   1 rizwank 1.1 # Module of TWiki Collaboration Platform, http://TWiki.org/
   2             #
   3             # Copyright (C) 2002 John Talintyre, john.talintyre@btinternet.com
   4             #
   5             # For licensing info read license.txt file in the TWiki root.
   6             # This program is free software; you can redistribute it and/or
   7             # modify it under the terms of the GNU General Public License
   8             # as published by the Free Software Foundation; either version 2
   9             # of the License, or (at your option) any later version.
  10             #
  11             # This program is distributed in the hope that it will be useful,
  12             # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14             # GNU General Public License for more details, published at 
  15             # http://www.gnu.org/copyleft/gpl.html
  16             #
  17             #
  18             # Functions used by both Rcs and RcsFile - they both inherit from this Class
  19             #
  20             # Simple interface to RCS.  Doesn't support:
  21             #    branches
  22 rizwank 1.1 #    locking
  23             #
  24             # This modules doesn't know anything about the content of the topic e.g. it doesn't know
  25             # about the meta data.
  26             #
  27             # FIXME:
  28             #  - need to tidy up dealing with \n for differences
  29             #  - still have difficulty on line ending at end of sequences, consequence of doing a line based diff
  30             #  - most serious is when having multiple line ends on one seq but not other - this needs fixing
  31             #  - tidyup us of 1. for revisions
  32             #  - cleaner dealing with errors/warnings
  33             
  34             =begin twiki
  35             
  36             ---+ TWiki::Store::RcsLite Module
  37             
  38             This module implements rcs (without calling it)
  39             
  40             =cut
  41             
  42             package TWiki::Store::RcsLite;
  43 rizwank 1.1 
  44             use TWiki::Store::RcsFile;
  45             @ISA = qw(TWiki::Store::RcsFile);
  46             
  47             use strict;
  48             #use Algorithm::Diff;# qw(diff sdiff);
  49             use Algorithm::Diff;
  50             use FileHandle;
  51             use TWiki;
  52             
  53             TWiki::writeDebug("Diff version $Algorithm::Diff::VERSION\n");
  54             
  55             my $DIFF_DEBUG = 0;
  56             my $DIFFEND_DEBUG = 0;
  57             
  58             # ======================
  59             =pod
  60             
  61             ---++ sub new (  $proto, $web, $topic, $attachment, %settings  )
  62             
  63             Not yet documented.
  64 rizwank 1.1 
  65             =cut to implementation
  66             
  67             sub new
  68             {
  69                my( $proto, $web, $topic, $attachment, %settings ) = @_;
  70                my $class = ref($proto) || $proto;
  71                my $self = TWiki::Store::RcsFile->new( $web, $topic, $attachment, %settings );
  72                bless( $self, $class );
  73                $self->_init();
  74                $self->{head} = 0;
  75                return $self;
  76             }
  77             
  78             # ======================
  79             =pod
  80             
  81             ---++ sub _trace ()
  82             
  83             Not yet documented.
  84             
  85 rizwank 1.1 =cut to implementation
  86             
  87             sub _trace
  88             {
  89             #   my( $text ) = @_;
  90             #   TWiki::writeDebug( "RcsLite $text" );
  91             }
  92             
  93             
  94             # Process an RCS file
  95             
  96             # File format information:
  97             #
  98             #rcstext    ::=  admin {delta}* desc {deltatext}*
  99             #admin      ::=  head {num};
 100             #                { branch   {num}; }
 101             #                access {id}*;
 102             #                symbols {sym : num}*;
 103             #                locks {id : num}*;  {strict  ;}
 104             #                { comment  {string}; }
 105             #                { expand   {string}; }
 106 rizwank 1.1 #                { newphrase }*
 107             #delta      ::=  num
 108             #                date num;
 109             #                author id;
 110             #                state {id};
 111             #                branches {num}*;
 112             #                next {num};
 113             #                { newphrase }*
 114             #desc       ::=  desc string
 115             #deltatext  ::=  num
 116             #                log string
 117             #                { newphrase }*
 118             #                text string
 119             #num        ::=  {digit | .}+
 120             #digit      ::=  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
 121             #id         ::=  {num} idchar {idchar | num }*
 122             #sym        ::=  {digit}* idchar {idchar | digit }*
 123             #idchar     ::=  any visible graphic character except special
 124             #special    ::=  $ | , | . | : | ; | @
 125             #string     ::=  @{any character, with @ doubled}*@
 126             #newphrase  ::=  id word* ;
 127 rizwank 1.1 #word       ::=  id | num | string | :
 128             #
 129             # Identifiers are case sensitive. Keywords are in lower case only. The sets of keywords and 
 130             # identifiers can overlap. In most environments RCS uses the ISO 8859/1 encoding: 
 131             # visible graphic characters are codes 041-176 and 240-377, and white space characters are 
 132             # codes 010-015 and 040. 
 133             #
 134             # Dates, which appear after the date keyword, are of the form Y.mm.dd.hh.mm.ss, 
 135             # where Y is the year, mm the month (01-12), dd the day (01-31), hh the hour (00-23), 
 136             # mm the minute (00-59), and ss the second (00-60). Y contains just the last two digits of 
 137             # the year for years from 1900 through 1999, and all the digits of years thereafter. 
 138             # Dates use the Gregorian calendar; times use UTC. 
 139             #
 140             # The newphrase productions in the grammar are reserved for future extensions to the format 
 141             # of RCS files. No newphrase will begin with any keyword already in use. 
 142             
 143             # ======================
 144             =pod
 145             
 146             ---++ sub _readTo (  $file, $char  )
 147             
 148 rizwank 1.1 Not yet documented.
 149             
 150             =cut to implementation
 151             
 152             sub _readTo
 153             {
 154                 my( $file, $char ) = @_;
 155                 my $buf = "";
 156                 my $ch;
 157                 my $space = 0;
 158                 my $string = "";
 159                 my $state = "";
 160                 while( read( $file, $ch, 1 ) ) {
 161                    if( $ch eq "@" ) {
 162                       if( $state eq "@" ) {
 163                          $state = "e";
 164                          next;
 165                       } elsif( $state eq "e" ) {
 166                          $state = "@";
 167                          $string .= "@";
 168                          next;
 169 rizwank 1.1           } else {
 170                          $state = "@";
 171                          next;
 172                       }
 173                    } else {
 174                       if( $state eq "e" ) {
 175                          $state = "";
 176                          if( $char eq "@" ) {
 177                             last;
 178                          }
 179                          # End of string
 180                       } elsif ( $state eq "@" ) {
 181                          $string .= $ch;
 182                          next;
 183                       }
 184                    }
 185                    
 186                    if( $ch =~ /\s/ ) {
 187                       if( length( $buf ) == 0 ) {
 188                           next;
 189                       } elsif( $space ) {
 190 rizwank 1.1               next;
 191                       } else {
 192                           $space = 1;
 193                           $ch = " ";
 194                       }
 195                    } else {
 196                       $space = 0;
 197                    }
 198                    $buf .= $ch;
 199                    if( $ch eq $char ) {
 200                        last;
 201                    }
 202                 }
 203                 return( $buf, $string );
 204             }
 205             
 206             # ======================
 207             # Called by routines that must make sure RCS file has been read in
 208             =pod
 209             
 210             ---++ sub _ensureProcessed (  $self  )
 211 rizwank 1.1 
 212             Not yet documented.
 213             
 214             =cut to implementation
 215             
 216             sub _ensureProcessed
 217             {
 218                 my( $self ) = @_;
 219                 if( ! $self->{where} ) {
 220                     $self->_process();
 221                 }
 222             }
 223             
 224             # ======================
 225             # Read in the whole RCS file
 226             =pod
 227             
 228             ---++ sub _process (  $self  )
 229             
 230             Not yet documented.
 231             
 232 rizwank 1.1 =cut to implementation
 233             
 234             sub _process
 235             {
 236                 my( $self ) = @_;
 237                 my $rcsFile = $self->rcsFile();
 238                 if( ! -e $rcsFile ) {
 239                     $self->{where} = "nofile";
 240                     return;
 241                 }
 242                 my $fh = new FileHandle;
 243                 if( ! $fh->open( $rcsFile ) ) {
 244                     $self->_warn( "Couldn't open file $rcsFile" );
 245                     $self->{where} = "nofile";
 246                     return;
 247                 }
 248                 my $where = "admin.head";
 249                 binmode( $fh );
 250                 my $lastWhere = "";
 251                 my $going = 1;
 252                 my $term = ";";
 253 rizwank 1.1     my $string = "";
 254                 my $num = "";
 255                 my $headNum = "";
 256                 my @date = ();
 257                 my @author = ();
 258                 my @log = ();
 259                 my @text = ();
 260                 my $dnum = "";
 261                 while( $going ) {
 262                    ($_, $string) = _readTo( $fh, $term );
 263                    last if( ! $_ );
 264                   
 265                    my $lastWhere = $where;
 266                    #print "\"$where -- $_\"\n";
 267                    if( $where eq "admin.head" ) {
 268                       if( /^head\s+([0-9]+)\.([0-9]+);$/o ) {
 269                          die( "Only support start of version being 1" ) if( $1 ne "1" );
 270                          $headNum = $2;
 271                          $where = "admin.access"; # Don't support branch
 272                       } else {
 273                          last;
 274 rizwank 1.1           }
 275                    } elsif( $where eq "admin.access" ) {
 276                       if( /^access\s*(.*);$/o ) {
 277                          $where = "admin.symbols";
 278                          $self->{access} = $1;
 279                       } else {
 280                          last;
 281                       }
 282                    } elsif( $where eq "admin.symbols" ) {
 283                       if( /^symbols(.*);$/o ) {
 284                          $where = "admin.locks";
 285                          $self->{symbols} = $1;
 286                       } else {
 287                          last;
 288                       }
 289                    } elsif( $where eq "admin.locks" ) {
 290                       if( /^locks.*;$/o ) {
 291                          $where = "admin.postLocks";
 292                       } else {
 293                          last;
 294                       }
 295 rizwank 1.1        } elsif( $where eq "admin.postLocks" ) {
 296                       if( /^strict\s*;/o ) {
 297                          $where = "admin.postStrict";
 298                       }
 299                    } elsif( $where eq "admin.postStrict" &&
 300                             /^comment\s.*$/o ) {
 301                          $where = "admin.postComment";
 302                          $self->{comment} = $string;
 303                    } elsif( ( $where eq "admin.postStrict" || $where eq "admin.postComment" )  &&
 304                             /^expand\s/o ) {
 305                          $where = "admin.postExpand";
 306                          $self->{expand} = $string;         
 307                    } elsif( $where eq "admin.postStrict" || $where eq "admin.postComment" || 
 308                             $where eq "admin.postExpand" || $where eq "delta.date") {
 309                       if( /^([0-9]+)\.([0-9]+)\s+date\s+(\d\d(\d\d)?(\.\d\d){5}?);$/o ) {
 310                          $where = "delta.author";
 311                          $num = $2;
 312                          $date[$num] = TWiki::Store::RcsFile::_rcsDateTimeToEpoch ($3 );
 313                       }
 314                    } elsif( $where eq "delta.author" ) {
 315                       if( /^author\s+(.*);$/o ) {
 316 rizwank 1.1              $author[$num] = $1;
 317                          if( $num == 1 ) {
 318                             $where = "desc";
 319                             $term = "@";
 320                          } else {
 321                             $where = "delta.date";
 322                          }
 323                       }
 324                    } elsif( $where eq "desc" ) {
 325                       if( /desc\s*$/o ) {
 326                          $self->{"description"} = $string;
 327                          $where = "deltatext.log";
 328                       }
 329                    } elsif( $where eq "deltatext.log" ) {
 330                       if( /\d+\.(\d+)\s+log\s+$/o ) {
 331                          $dnum = $1;
 332                          $log[$dnum] = $string;
 333                          $where = "deltatext.text";
 334                       }
 335                    } elsif( $where eq "deltatext.text" ) {
 336                       if( /text\s*$/o ) {
 337 rizwank 1.1              $where = "deltatext.log";
 338                          $text[$dnum] = $string;
 339                          if( $dnum == 1 ) {
 340                             $where = "done";
 341                             last;
 342                          }
 343                       }
 344                    }
 345                 }
 346                 
 347                 $self->{"head"} = $headNum;
 348                 $self->{"author"} = \@author;
 349                 $self->{"date"} = \@date;   #TODO: i hitnk i need to make this into epochSecs
 350                 $self->{"log"} = \@log;
 351                 $self->{"delta"} = \@text;
 352                 $self->{"status"} = $dnum;
 353                 $self->{where} = $where;
 354                 
 355                 close( $fh );
 356             }
 357             
 358 rizwank 1.1 # ======================
 359             =pod
 360             
 361             ---++ sub _formatString (  $str  )
 362             
 363             Not yet documented.
 364             
 365             =cut to implementation
 366             
 367             sub _formatString
 368             {
 369                 my( $str ) = @_;
 370                 $str =~ s/@/@@/go;
 371                 return "\@$str\@";
 372             }
 373             
 374             # ======================
 375             # Write content of the RCS file
 376             =pod
 377             
 378             ---++ sub _write (  $self, $file  )
 379 rizwank 1.1 
 380             Not yet documented.
 381             
 382             =cut to implementation
 383             
 384             sub _write
 385             {
 386                 my( $self, $file ) = @_;
 387                 
 388                 # admin
 389                 print $file "head\t1." . $self->numRevisions() . ";\n";
 390                 print $file "access" . $self->access() . ";\n";
 391                 print $file "symbols" . $self->{symbols} . ";\n";
 392                 print $file "locks; strict;\n";
 393                 printf $file "comment\t%s;\n", ( _formatString( $self->comment() ) );
 394                 printf $file "expand\t@%s@;\n", ( $self->{expand} ) if ( $self->{expand} );
 395                 
 396                 print $file "\n";
 397                 
 398                 # delta
 399                 for( my $i=$self->numRevisions(); $i>0; $i--) {
 400 rizwank 1.1        printf $file "\n1.%d\ndate\t%s;\tauthor %s;\tstate Exp;\nbranches;\n", 
 401                           ($i, TWiki::Store::RcsFile::_epochToRcsDateTime( ${$self->{date}}[$i] ), $self->author($i) );
 402                    if( $i == 1 ) {
 403                        print $file "next\t;\n";
 404                    } else {
 405                        printf $file "next\t1.%d;\n", ($i - 1);
 406                    }
 407                 }
 408                 
 409                 printf $file "\n\ndesc\n%s\n\n", ( _formatString( $self->description() ) );
 410                 
 411                 for( my $i=$self->numRevisions(); $i>0; $i--) {
 412                    printf $file "\n1.$i\nlog\n%s\ntext\n%s\n",
 413                           ( _formatString( $self->log($i) ), _formatString( $self->delta($i) ) );
 414                 }
 415             }
 416             
 417             # ======================
 418             =pod
 419             
 420             ---++ sub _binaryChange (  $self  )
 421 rizwank 1.1 
 422             Not yet documented.
 423             
 424             =cut to implementation
 425             
 426             sub _binaryChange
 427             {
 428                my( $self ) = @_;
 429                # Nothing to be done but note for re-writing
 430                $self->{expand} = "b" if( $self->{binary} );
 431                # FIXME: unless we have to not do diffs for binary files
 432             }
 433             
 434             # ======================
 435             =pod
 436             
 437             ---++ sub numRevisions (  $self  )
 438             
 439             Not yet documented.
 440             
 441             =cut to implementation
 442 rizwank 1.1 
 443             sub numRevisions
 444             {
 445                 my( $self ) = @_;
 446                 $self->_ensureProcessed();
 447                 return $self->{"head"};
 448             }
 449             
 450             # ======================
 451             =pod
 452             
 453             ---++ sub access (  $self  )
 454             
 455             Not yet documented.
 456             
 457             =cut to implementation
 458             
 459             sub access
 460             {
 461                 my( $self ) = @_;
 462                 $self->_ensureProcessed();
 463 rizwank 1.1     return $self->{access};
 464             }
 465             
 466             # ======================
 467             =pod
 468             
 469             ---++ sub comment (  $self  )
 470             
 471             Not yet documented.
 472             
 473             =cut to implementation
 474             
 475             sub comment
 476             {
 477                 my( $self ) = @_;
 478                 $self->_ensureProcessed();
 479                 return $self->{"comment"};
 480             }
 481             
 482             # ======================
 483             =pod
 484 rizwank 1.1 
 485             ---++ sub date (  $self, $version  )
 486             
 487             Not yet documented.
 488             | $date | in epoch seconds |
 489             
 490             =cut to implementation
 491             
 492             sub date
 493             {
 494                 my( $self, $version ) = @_;
 495                 $self->_ensureProcessed();
 496                 my $date = ${$self->{"date"}}[$version];
 497                 if( $date ) {
 498             #        $date = TWiki::Store::RcsFile::_rcsDateTimeToEpoch( $date );
 499                 } else {
 500                     $date = 0;#MMMM, should this be 0, or now()?
 501                 }
 502                 return $date;
 503             }
 504             
 505 rizwank 1.1 # ======================
 506             =pod
 507             
 508             ---++ sub description (  $self  )
 509             
 510             Not yet documented.
 511             
 512             =cut to implementation
 513             
 514             sub description
 515             {
 516                 my( $self ) = @_;
 517                 $self->_ensureProcessed();
 518                 return $self->{"description"};
 519             }
 520             
 521             # ======================
 522             =pod
 523             
 524             ---++ sub author (  $self, $version  )
 525             
 526 rizwank 1.1 Not yet documented.
 527             
 528             =cut to implementation
 529             
 530             sub author
 531             {
 532                 my( $self, $version ) = @_;
 533                 $self->_ensureProcessed();
 534                 return ${$self->{"author"}}[$version];
 535             }
 536             
 537             # ======================
 538             =pod
 539             
 540             ---++ sub log (  $self, $version  )
 541             
 542             Not yet documented.
 543             
 544             =cut to implementation
 545             
 546             sub log
 547 rizwank 1.1 {
 548                 my( $self, $version ) = @_;
 549                 $self->_ensureProcessed();
 550                 return ${$self->{"log"}}[$version];
 551             }
 552             
 553             # ======================
 554             =pod
 555             
 556             ---++ sub delta (  $self, $version  )
 557             
 558             Not yet documented.
 559             
 560             =cut to implementation
 561             
 562             sub delta
 563             {
 564                 my( $self, $version ) = @_;
 565                 $self->_ensureProcessed();
 566                 return ${$self->{"delta"}}[$version];
 567             }
 568 rizwank 1.1 
 569             # ======================
 570             =pod
 571             
 572             ---++ sub addRevision (  $self, $text, $log, $author, $date  )
 573             
 574             Not yet documented.
 575             | $date | in epoch seconds |
 576             
 577             =cut to implementation
 578             
 579             sub addRevision
 580             {
 581                 my( $self, $text, $log, $author, $date ) = @_;
 582                 _trace( "::addRevision date=\"$date\"" );
 583                 $self->_ensureProcessed();
 584                 
 585                 $self->_save( $self->file(), \$text );
 586                 $text = $self->_readFile( $self->{file} ) if( $self->{attachment} );
 587                 my $head = $self->numRevisions();
 588                 if( $head ) {
 589 rizwank 1.1         my $delta = _diffText( \$text, \$self->delta($head), "", 0 );
 590                     ${$self->{"delta"}}[$head] = $delta;
 591                 }   
 592                 $head++;
 593                 ${$self->{"delta"}}[$head] = $text;
 594                 $self->{"head"} = $head;
 595                 ${$self->{"log"}}[$head] = $log;
 596                 ${$self->{"author"}}[$head] = $author;
 597                 if( $date ) {
 598              #       $date =~ s/[ \/\:]/\./go;
 599                 } else {
 600                     $date = time();
 601                 }
 602             #    $date = TWiki::Store::RcsFile::_epochToRcsDateTime( $date );
 603             
 604             
 605                 _trace("::addRevision date now=\"$date\"" );
 606                 ${$self->{"date"}}[$head] = $date;
 607             
 608                 return $self->_writeMe();
 609             }
 610 rizwank 1.1 
 611             # ======================
 612             =pod
 613             
 614             ---++ sub _writeMe (  $self  )
 615             
 616             Not yet documented.
 617             
 618             =cut to implementation
 619             
 620             sub _writeMe
 621             {
 622                 my( $self ) = @_;
 623                 my $dataError = "";
 624                 my $out = new FileHandle;
 625                 
 626                 chmod( 0644, $self->rcsFile()  ); # FIXME move permission to config or similar
 627                 if( ! $out->open( "> " . $self->rcsFile() ) ) {
 628                    $dataError = "Problem opening " . $self->rcsFile() . " for writing";
 629                 } else {
 630                    binmode( $out );
 631 rizwank 1.1        $self->_write( $out );
 632                    close( $out );
 633                 }
 634                 chmod( 0444, $self->rcsFile()  ); # FIXME as above
 635                 return $dataError;    
 636             }
 637             
 638             # ======================
 639             =pod
 640             
 641             ---++ sub replaceRevision (  $self, $text, $comment, $user, $date  )
 642             
 643             Not yet documented.
 644             # Replace the top revision
 645             # Return non empty string with error message if there is a problem
 646             | $date | is on epoch seconds |
 647             
 648             =cut to implementation
 649             
 650             sub replaceRevision
 651             {
 652 rizwank 1.1     my( $self, $text, $comment, $user, $date ) = @_;
 653                 _trace( "::replaceRevision date=\"$date\"" );
 654                 $self->_ensureProcessed();
 655                 $self->_delLastRevision();
 656                 $self->addRevision( $text, $comment, $user, $date );    
 657             }
 658             
 659             # ======================
 660             # Delete the last revision - do nothing if there is only one revision
 661             =pod
 662             
 663             ---++ sub deleteRevision (  $self  )
 664             
 665             Not yet documented.
 666             
 667             =cut to implementation
 668             
 669             sub deleteRevision
 670             {
 671                 my( $self ) = @_;
 672                 $self->_ensureProcessed();
 673 rizwank 1.1     return "" if( $self->numRevisions() <= 1 );
 674                 $self->_delLastRevision();
 675                 return $self->_writeMe();
 676             }
 677             
 678             # ======================
 679             =pod
 680             
 681             ---++ sub _delLastRevision (  $self  )
 682             
 683             Not yet documented.
 684             
 685             =cut to implementation
 686             
 687             sub _delLastRevision
 688             {
 689                 my( $self ) = @_;
 690                 my $numRevisions = $self->numRevisions();
 691                 if( $numRevisions > 1 ) {
 692                     # Need to recover text for last revision
 693                     my $lastText = $self->getRevision( $numRevisions - 1 );
 694 rizwank 1.1         $numRevisions--;
 695                     $self->{"delta"}->[$numRevisions] = $lastText;
 696                 } else {
 697                     $numRevisions--;
 698                 }
 699                 $self->{head} = $numRevisions;
 700             }
 701             
 702             # ======================
 703             =pod
 704             
 705             ---++ sub revisionDiff (  $self, $rev1, $rev2, $contextLines  )
 706             
 707             Not yet documented.
 708             | TODO: | so why does this read the rcs file, re-create each of the 2 revisions and then diff them? isn't the delta in the rcs file good enough? (until you want context?) |
 709             =cut to implementation
 710             
 711             sub revisionDiff
 712             {
 713                 my( $self, $rev1, $rev2, $contextLines ) = @_;
 714                 $self->_ensureProcessed();
 715 rizwank 1.1     my $text1 = $self->getRevision( $rev1 );
 716                 my $text2 = $self->getRevision( $rev2 );
 717             	
 718                 my @lNew = _mySplit( \$text1 );
 719                 my @lOld = _mySplit( \$text2 );
 720             	my $diff = Algorithm::Diff::sdiff( \@lNew, \@lOld );
 721             
 722             	#the Diff::sdiff algol seems to work better with \n, and the rendering currently needs no \n's
 723             	my @list;
 724             	foreach my $ele ( @$diff ) {
 725             		@$ele[1] =~ s/\n//go;
 726             		@$ele[2] =~ s/\n//go;
 727             		push @list, $ele;
 728             	}
 729             	return ("", \@list);	
 730             }
 731             
 732             
 733             =pod
 734             
 735             ---+++ setTopicRevisionTag( $web, $topic, $rev, $tag ) ==> $success
 736 rizwank 1.1 
 737             | Description: | sets a names tag on the specified revision |
 738             | Parameter: =$web= | webname |
 739             | Parameter: =$topic= | topic name |
 740             | Parameter: =$rev= | the revision we are taging |
 741             | Parameter: =$tag= | the string to tag with |
 742             | Return: =$success= |  |
 743             | TODO: | we _need_ an error mechanism! |
 744             | Since: | TWiki:: (20 April 2004) |
 745             
 746             =cut
 747             
 748             sub setTopicRevisionTag
 749             {
 750             #	my ( $self, $web, $topic, $rev, $tag ) = @_;
 751             
 752             	TWiki::writeDebug("setTopicRevisionTag - not implemented in RCSLite");
 753             #TODO: implement me :)
 754             	
 755             	return "";
 756             }
 757 rizwank 1.1 
 758             
 759             # ======================
 760             =pod
 761             
 762             ---++ sub getRevision (  $self, $version  )
 763             
 764             Not yet documented.
 765             
 766             =cut to implementation
 767             
 768             sub getRevision
 769             {
 770                 my( $self, $version ) = @_;
 771                 $self->_ensureProcessed();
 772                 my $head = $self->numRevisions();
 773                 if( $version == $head ) {
 774                     return $self->delta( $version );
 775                 } else {
 776                     my $headText = $self->delta( $head );
 777                     my @text = _mySplit( \$headText, 1 );
 778 rizwank 1.1         return $self->_patchN( \@text, $head-1, $version );
 779                 }
 780             }
 781             
 782             # ======================
 783             # If revision file is missing, information based on actual file is returned.
 784             # Date is in epoch based seconds
 785             =pod
 786             
 787             ---++ sub getRevisionInfo (  $self, $version  )
 788             
 789             Not yet documented.
 790             
 791             =cut to implementation
 792             
 793             sub getRevisionInfo
 794             {
 795                 my( $self, $version ) = @_;
 796                 $self->_ensureProcessed();
 797                 $version = $self->numRevisions() if( ! $version );
 798             
 799 rizwank 1.1 	#TODO: need to add a where $revision is not number, find out what rev number the tag refers to
 800             
 801                 my @result;
 802                 
 803                 if( $self->{where} && $self->{where} ne "nofile" ) {
 804                     @result = ( "", $version, $self->date( $version ), $self->author( $version ), $self->comment( $version ) );
 805                 } else {
 806                     @result = $self->_getRevisionInfoDefault();
 807                 }
 808             
 809                 return @result;
 810             }
 811             
 812             
 813             # ======================
 814             # Apply delta (patch) to text.  Note that RCS stores reverse deltas, the is text for revision x
 815             # is patched to produce text for revision x-1.
 816             # It is fiddly dealing with differences in number of line breaks after the end of the
 817             # text.
 818             =pod
 819             
 820 rizwank 1.1 ---++ sub _patch (  $text, $delta  )
 821             
 822             Not yet documented.
 823             
 824             =cut to implementation
 825             
 826             sub _patch
 827             {
 828                # Both params are references to arrays
 829                my( $text, $delta ) = @_;
 830                my $adj = 0;
 831                my $pos = 0;
 832                my $last = "";
 833                my $d;
 834                my $extra = "";
 835                my $max = $#$delta;
 836                while( $pos <= $max ) {
 837                    $d = $delta->[$pos];
 838                    if( $d =~ /^([ad])(\d+)\s(\d+)\n(\n*)/ ) {
 839                       $last = $1;
 840                       $extra = $4;
 841 rizwank 1.1           my $offset = $2;
 842                       my $length = $3;
 843                       if( $last eq "d" ) {
 844                          my $start = $offset + $adj - 1;
 845                          my @removed = splice( @$text, $start, $length );
 846                          $adj -= $length;
 847                          $pos++;
 848                       } elsif( $last eq "a" ) {
 849                          my @toAdd = @$delta[$pos+1..$pos+$length];
 850                          if( $extra ) {
 851                              if( @toAdd ) {
 852                                  $toAdd[$#toAdd] .= $extra;
 853                              } else {
 854                                  @toAdd = ( $extra );
 855                              }
 856                          }
 857                          splice( @$text, $offset + $adj, 0, @toAdd );
 858                          $adj += $length;
 859                          $pos += $length + 1;
 860                       }
 861                    } else {
 862 rizwank 1.1           warn( "wrong! - should be \"[ad]<num> <num>\" and was: \"" . $d . "\"\n\n" ); #FIXME remove die
 863                       return;
 864                    }
 865                }
 866             }
 867             
 868             
 869             # ======================
 870             =pod
 871             
 872             ---++ sub _patchN (  $self, $text, $version, $target  )
 873             
 874             Not yet documented.
 875             
 876             =cut to implementation
 877             
 878             sub _patchN
 879             {
 880                 my( $self, $text, $version, $target ) = @_;
 881             
 882                 my $deltaText= $self->delta( $version );
 883 rizwank 1.1     my @delta = _mySplit( \$deltaText );
 884                 _patch( $text, \@delta );
 885                 if( $version <= $target ) {
 886                     return join( "", @$text );
 887                 } else {
 888                     return $self->_patchN( $text, $version-1, $target );
 889                 }
 890             }
 891             
 892             # ======================
 893             # Split and make sure we have trailing carriage returns
 894             =pod
 895             
 896             ---++ sub _mySplit (  $text, $addEntries  )
 897             
 898             Not yet documented.
 899             
 900             =cut to implementation
 901             
 902             sub _mySplit
 903             {
 904 rizwank 1.1     my( $text, $addEntries ) = @_;
 905             
 906                 my $ending = "";
 907                 if( $$text =~ /(\n+)$/o ) {
 908                     $ending = $1;
 909                 }
 910             
 911                 my @list = split( /\n/o, $$text );
 912                 for( my $i = 0; $i<$#list; $i++ ) {
 913                 	    $list[$i] .= "\n";
 914                 }
 915             	
 916                 if( $ending ) {
 917                     if( $addEntries ) {
 918                         my $len = length($ending);
 919                         if( @list ) {
 920                            $len--;
 921                            $list[$#list] .= "\n";
 922                         }
 923                         for( my $i=0; $i<$len; $i++ ) {
 924                             push @list, ("\n");
 925 rizwank 1.1            }
 926                     } else {
 927                         if( @list ) {
 928                             $list[$#list] .= $ending;
 929                         } else {
 930                             @list = ( $ending );
 931                         }
 932                     }
 933                 }
 934                 # TODO: deal with Mac style line ending??
 935             
 936                 return @list; # FIXME would it be more efficient to return a reference?
 937             }
 938             
 939             # ======================
 940             # Way of dealing with trailing \ns feels clumsy
 941             =pod
 942             
 943             ---++ sub _diffText (  $new, $old, $type, $contextLines  )
 944             
 945             Not yet documented.
 946 rizwank 1.1 
 947             =cut to implementation
 948             
 949             sub _diffText
 950             {
 951                 my( $new, $old, $type, $contextLines ) = @_;
 952                 
 953                 my @lNew = _mySplit( $new );
 954                 my @lOld = _mySplit( $old );
 955                 return _diff( \@lNew, \@lOld, $type, $contextLines );
 956             }
 957             
 958             # ======================
 959             =pod
 960             
 961             ---++ sub _lastNoEmptyItem (  $items  )
 962             
 963             Not yet documented.
 964             
 965             =cut to implementation
 966             
 967 rizwank 1.1 sub _lastNoEmptyItem
 968             {
 969                my( $items ) = @_;
 970                my $pos = $#$items;
 971                my $count = 0;
 972                my $item;
 973                while( $pos >= 0 ) {
 974                   $item = $items->[$pos];
 975                   last if( $item );
 976                   $count++;
 977                   $pos--;
 978                }
 979                return( $pos, $count );
 980             }
 981             
 982             # ======================
 983             # Deal with trailing carriage returns - Algorithm doesn't give output that RCS format is too happy with
 984             =pod
 985             
 986             ---++ sub _diffEnd (  $new, $old, $type  )
 987             
 988 rizwank 1.1 Not yet documented.
 989             
 990             =cut to implementation
 991             
 992             sub _diffEnd
 993             {
 994                my( $new, $old, $type ) = @_;
 995                return if( $type ); # FIXME
 996                
 997                my( $posNew, $countNew ) = _lastNoEmptyItem( $new );
 998                my( $posOld, $countOld ) = _lastNoEmptyItem( $old );
 999             
1000                return "" if( $countNew == $countOld );
1001                
1002                if( $DIFFEND_DEBUG ) {
1003                  print( "countOld, countNew, posOld, posNew, lastOld, lastNew, lenOld: " .
1004                         "$countOld, $countNew, $posOld, $posNew, " . $#$old . ", " . $#$new . 
1005                         "," . @$old . "\n" );
1006                }
1007                
1008                $posNew++;
1009 rizwank 1.1    my $toDel = ( $countNew < 2 ) ? 1 : $countNew;
1010                my $startA = @$new - ( ( $countNew > 0 ) ? 1 : 0 );
1011                my $toAdd = ( $countOld < 2 ) ? 1 : $countOld;
1012                my $theEnd = "d$posNew $toDel\na$startA $toAdd\n";
1013                for( my $i=$posOld; $i<@${old}; $i++ ) {
1014                    $theEnd .= $old->[$i] ? $old->[$i] : "\n";
1015                }
1016                
1017                for( my $i=0; $i<$countNew; $i++ ) {pop @$new;}
1018                pop @$new;
1019                for( my $i=0; $i<$countOld; $i++ ) {pop @$old;}
1020                pop @$old;
1021                
1022                print "--$theEnd--\n"  if( $DIFFEND_DEBUG );
1023                   
1024                return $theEnd;
1025             }
1026             
1027             # ======================
1028             # no type means diff for putting in rcs file, diff means normal diff output
1029             =pod
1030 rizwank 1.1 
1031             ---++ sub _diff (  $new, $old, $type, $contextLines  )
1032             
1033             Not yet documented.
1034             
1035             TODO need to add functionality to use $contextLines
1036             
1037             =cut to implementation
1038             
1039             sub _diff
1040             {
1041                 my( $new, $old, $type, $contextLines ) = @_;
1042                 # Work out diffs to change new to old, params are refs to lists
1043                 my $diffs = Algorithm::Diff::diff( $new, $old );
1044             	
1045                 my $adj = 0;
1046                 my @patch = ();
1047                 my @del = ();
1048                 my @ins = ();
1049                 my $out = "";
1050                 my $start = 0;
1051 rizwank 1.1     my $start1;
1052                 my $chunkSign = "";
1053                 my $count = 0;
1054                 my $numChunks = @$diffs;
1055                 my $last = 0;
1056                 my $lengthNew = @$new - 1;
1057                 foreach my $chunk ( @$diffs ) {
1058                    $count++;
1059                    print "[\n" if( $DIFF_DEBUG );
1060                    $chunkSign = "";
1061                    my @lines = ();
1062                    foreach my $line ( @$chunk ) {
1063                        my( $sign, $pos, $what ) = @$line;
1064                        print "$sign $pos \"$what\"\n" if( $DIFF_DEBUG );
1065                        if( $chunkSign ne $sign && $chunkSign ne "") {
1066                            if( $chunkSign eq "-" && $type eq "diff" ) {
1067                               # Might be change of lines
1068                               my $chunkLength = @$chunk;
1069                               my $linesSoFar = @lines;
1070                               if( $chunkLength == 2 * $linesSoFar ) {
1071                                  $chunkSign = "c";
1072 rizwank 1.1                      $start1 = $pos;
1073                               }
1074                            }
1075                            $adj += _addChunk( $chunkSign, \$out, \@lines, $start, $adj, $type, $start1, $last ) if( $chunkSign ne "c" );
1076                        }
1077                        if( ! @lines ) {
1078                            $start = $pos;
1079                        }
1080                        $chunkSign = $sign if( $chunkSign ne "c" );
1081                        push @lines, ( $what );
1082                    }
1083             
1084                    $last = 1 if( $count == $numChunks );
1085                    if( $last && $chunkSign eq "+" ) {
1086                        my $endings = 0;
1087                        for( my $i=$#$old; $i>=0; $i-- ) {
1088                            if( $old->[$i] ) {
1089                                last;
1090                            } else {
1091                                $endings++;
1092                            }
1093 rizwank 1.1            }
1094                        my $has = 0;
1095                        for( my $i=$#lines; $i>=0; $i-- ) {
1096                            if( $lines[$i] ) {
1097                                last;
1098                            } else {
1099                                $has++;
1100                            }
1101                        }
1102                        for( my $i=0; $i<$endings-$has; $i++ ) {
1103                            push @lines, ("");
1104                        }
1105                    }
1106                    $adj += _addChunk( $chunkSign, \$out, \@lines, $start, $adj, $type, $start1, $last, $lengthNew );
1107                    print "]\n" if( $DIFF_DEBUG );
1108                 }
1109                 # Make sure we have the correct number of carriage returns at the end
1110                 
1111                 print "pre end: \"$out\"\n" if( $DIFFEND_DEBUG );
1112                 return $out; # . $theEnd;
1113             }
1114 rizwank 1.1 
1115             
1116             # ======================
1117             =pod
1118             
1119             ---++ sub _range (  $start, $end  )
1120             
1121             Not yet documented.
1122             
1123             =cut to implementation
1124             
1125             sub _range
1126             {
1127                my( $start, $end ) = @_;
1128                if( $start == $end ) {
1129                   return "$start";
1130                } else {
1131                   return "$start,$end";
1132                }
1133             }
1134             
1135 rizwank 1.1 # ======================
1136             =pod
1137             
1138             ---++ sub _addChunk (  $chunkSign, $out, $lines, $start, $adj, $type, $start1, $last, $newLines  )
1139             
1140             Not yet documented.
1141             
1142             =cut to implementation
1143             
1144             sub _addChunk
1145             {
1146                my( $chunkSign, $out, $lines, $start, $adj, $type, $start1, $last, $newLines ) = @_;
1147                my $nLines = @$lines;
1148                if( $lines->[$#$lines] =~ /(\n+)$/o ) {
1149                   $nLines += ( ( length( $1 ) == 0 ) ? 0 : length( $1 ) -1 );
1150                }
1151                if( $nLines > 0 ) {
1152                    print "addChunk chunkSign=$chunkSign start=$start adj=$adj type=$type start1=$start1 " .
1153                          "last=$last newLines=$newLines nLines=$nLines\n" if( $DIFF_DEBUG );
1154                    $$out .= "\n" if( $$out && $$out !~ /\n$/o );
1155                    if( $chunkSign eq "c" ) {
1156 rizwank 1.1           $$out .= _range( $start+1, $start+$nLines/2 );
1157                       $$out .= "c";
1158                       $$out .= _range( $start1+1, $start1+$nLines/2 );
1159                       $$out .= "\n";
1160                       $$out .= "< " . join( "< ", @$lines[0..$nLines/2-1] );
1161                       $$out .= "\n" if( $lines->[$nLines/2-1] !~ /\n$/o );
1162                       $$out .= "---\n";
1163                       $$out .= "> " . join( "> ", @$lines[$nLines/2..$nLines-1] );
1164                       $nLines = 0;
1165                    } elsif( $chunkSign eq "+" ) {
1166                       if( $type eq "diff" ) {
1167                           $$out .= $start-$adj . "a";
1168                           $$out .= _range( $start+1, $start+$nLines ) . "\n";
1169                           $$out .= "> " . join( "> ", @$lines );
1170                       } else {
1171                           $$out .= "a";
1172                           $$out .= $start-$adj;
1173                           $$out .= " $nLines\n";
1174                           $$out .= join( "", @$lines );
1175                       }
1176                    } else {
1177 rizwank 1.1           print "Start nLines newLines: $start $nLines $newLines\n" if( $DIFF_DEBUG );
1178                       if( $type eq "diff" ) {
1179                           $$out .= _range( $start+1, $start+$nLines );
1180                           $$out .= "d";
1181                           $$out .= $start + $adj . "\n";
1182                           $$out .= "< " . join( "< ", @$lines );
1183                       } else {
1184                           $$out .= "d";
1185                           $$out .= $start+1;
1186                           $$out .= " $nLines";
1187                           $$out .= "\n" if( $last );
1188                       }
1189                       $nLines *= -1;
1190                    }
1191                    @$lines = ();
1192                }
1193                return $nLines;
1194             }
1195             
1196             
1197             
1198 rizwank 1.1 # ======================
1199             =pod
1200             
1201             ---++ sub validTo (  $self  )
1202             
1203             Not yet documented.
1204             
1205             =cut to implementation
1206             
1207             sub validTo
1208             {
1209                 my( $self ) = @_;
1210                 $self->_ensureProcessed();
1211                 return $self->{"status"};
1212             }
1213             
1214             1;

Rizwan Kassim
Powered by
ViewCVS 0.9.2