#!/usr/bin/perl # # perfbrowse.perl -- CGI browser for PERFORCE # # $Id: //guest/matthew_rees/perfbrowse/perfbrowse.pl#3 $ # Updated by Jeff Marshall at Paragon Software, Inc. # (jam@paragon-software.com) # # Revised by Matthew Rees (matthew@marc.com) 6/14/1999 # - Add FORMS capability # - Add a nice way to browse the repository # - Many other miscellaneous changes # - Tested with Perforce server version 99.1 # Set these according to your system: $ENV{P4PORT} = "1666"; $ENV{P4CLIENT} = "perfbrowse"; # When using authentication on the web server, REMOTE_USER is set and could # be used here. But it wouldn't work for any users that use a password! #$ENV{P4USER} = $ENV{REMOTE_USER}; $ENV{P4USER} = "p4admin"; # The PATH environment variable needs to be able to pick up the p4 command $ENV{PATH} .= ":/usr/local/bin"; # Set TOPPAGE to something other than this script if you want to use a custom # static top-level page (link to this is included at the bottom of every page) # Suggestion: run 'perfbrowse.pl > index.html' and edit to create your top page #$TOPPAGE = "$myname"; $TOPPAGE = "/index.html"; # These icons are used for the depot browser # (They may be left undefined, but it looks much better you find icons!) $UPDIR_ICON = "/icons/hand.up.gif"; $DIR_ICON = "/icons/folder.gif"; # The name of this script $myname = $ENV{SCRIPT_NAME} || "cgi/perfbrowse.pl"; $BLUE = qq(); $RED = qq(); $END = qq(); $ADD = $BLUE; $ADDEND = $END; $DEL = "$RED"; $DELEND = "$END"; $MAXCONTEXT = 30; $NCONTEXT = 10; $PRINTEDHEADER = 0; &ReadParse(\%INPUT); ################################ # # No arguments. # # Put up a basic index page # ################################ if (! defined $INPUT{'cmd'}) { print &header("Perforce Web Interface"); print "

Perforce Web Interface

\n
\n"; print "

Browse the depot: Select a starting point

\n\n", qq(
\n", qq(), "\n", "
Or enter a directory to start:\n
", qq(), "\n", qq(
), "\n", "
", "Show deleted files/directories\n
\n
\n\n"; print "

Query Changes: Select a path to limit the query\n

\n", "\n\n", qq(
\n", qq(), "\n", "
\n
Or enter your own path specification:\n
), "\n", qq(), "\n
", qq(), "\n", "Limit results to last ", qq(), "\nchanges\n", "
\n
\n\n"; print "
\n

Other Queries:

\n", "
  • Clients :\n", "See what clients have been defined by any user.\n", "
  • Users :\n", "See information about the users known to the Perforce server.\n", "
  • Branches :\n", "See what branches have been created in the depot.\n", "
  • Labels :\n", "See information about labels in the depot.\n", "
  • Jobs :\n", "See information about jobs.\n", "
  • \n"; print "\n


    \n"; print "
    Perforce Home Page", "
    \n"; print "\n\n"; exit 0; } ################################ # # Browse the repository ('browse') # "spec=path_spec" - the file/directory specification # "showdeleted=b" - flag to also list deleted files/directories # ################################ elsif ( $INPUT{'cmd'} eq "browse" ) { local ($spec) = $INPUT{'spec'}; $spec = "//*" if (!$spec || $spec =~ m!^/+$!); local ($sdel) = "showdeleted=1" if( $INPUT{'showdeleted'} ); print &header("Listing of $spec"); print "

    Listing of $spec

    \n"; print &formtop("browse"), "\n", "
    ", "Show deleted files/directories\n"; local ($head, $tail) = ($spec =~ m!^(.*)/([^/]*)$!); local ($getdirs, $getfiles) = (1,1); local ($wildcards) = ($spec =~ m![*]|\.\.\.!); local ($showpaths) = ($head =~ m![*]|\.\.\.!); local ($nfound) = 0; if (! $tail) { $getfiles = 0; $spec = $head; } elsif (! $wildcards) { &p4open('P4', "dirs ".($sdel ? "-D " : "")."\"$spec\" 2> /dev/null|"); if() { $spec .= "/*"; } else { $getdirs = 0; } close P4; } print "

    \n"; print "\n"; #### Print the Directories #### if( $getdirs ) { if ($tail && !$wildcards) { if($UPDIR_ICON) { print ""; } else { print ""; } print "\n"; } &p4open('P4', "dirs ".($sdel ? "-D " : "")."\"$spec\" 2> /dev/null|"); local( $dirsymbol ) = ($DIR_ICON ? "" : "(dir) "); while () { local( $d ) = ($showpaths ? $_ : m!^.*/([^/]+)$!); print ""; print "", "\n"; $nfound ++; } close P4; print "
    (dir) ", &url( "browse", "spec=$head", "$sdel", ".. (UP)" ), "
    $dirsymbol   ", &url("browse", "spec=$_", "$sdel", $d), "

    \n"; } #### Print the Files ### # Sample: //depot/main/p4/Jamrules#71 - edit change 42 (text) if( $getfiles ) { print "\n"; &p4open( 'P4', "files \"$spec\" 2> /dev/null|" ); while () { if ( local($filepath,$rev,$action,$change,$type) = m!^(\S.+)\#(\d+) - (\S+) \S+ (\S+) \((\w+)\)$! ) { next if ( !$sdel && $action eq "delete" ); local($file) = ($showpaths ? $filepath : $filepath =~ m!^.*/(\S.+)!); print "\n"; $nfound ++; } } close P4; print "
  • "; if ($action eq "delete") { print "$DEL"; } print &url( "filelog", "file=$filepath", "$file" ); if ($action eq "delete") { print "$DELEND"; } print "
  • ", "(rev ", &url("print", "file=$filepath", "rev=$rev", "#$rev"), ", $action \@ change ", &url( "describe", "change=$change", "#$change" ), " ) <$type>
    \n"; } print "No such file or directory
    \n" if (!$nfound); $spec =~ s!(.*)/\*$!$1/\.\.\.! if ($getdirs); @OTHER_FOOTERS = (" | ", &url ("changes", "spec=$spec", "limit=1", "Show changes")); } ################################ # # p4 users # ################################ elsif ( $INPUT{'cmd'} eq "users" ) { &p4open( 'P4', "users|" ); print &header("Perforce Users"); print "

    Perforce Users

    \n
    ", "This browser allows you to view information about ", " Perforce users.\n", "
    "; print "", "\n"; # Sample: # jam (Jeffrey A. Marshall) accessed 1998/07/03 while( ) { if (local( $user, $email, $fullname, $accessed ) = /(\S+) <(\S+)> \((.*)\) accessed (\S+)/) { print "", "\n"; } } print "
    UserEmailFull Name", "Last Accessed
    ", &url ( "user", "name=$user", "$user"), "", &mailto ( "$email" ), "", "$fullname", "", "$accessed", "
    \n"; close P4; } ################################ # # p4 user # "name=username" - the user to list *required* # ################################ elsif ( $INPUT{'cmd'} eq "user" ) { local( $user, $email, $update, $access, $fullname, $jobview ); $user = $INPUT{'name'}; &p4open ('P4', "user -o $user|"); while () { next if (/^User:/); next if (/^Email:/ && (( $email ) = /^Email:\s*(.*)$/ )); next if (/^Update:/ && (( $update ) = /^Update:\s*(.*)$/ )); next if (/^Access:/ && (( $access ) = /^Access:\s*(.*)$/ )); next if (/^FullName:/ && (( $fullname ) = /^FullName:\s*(.*)$/ )); next if (/^JobView:/ && (( $jobview ) = /^JobView:\s*(.*)$/ )); last if (/^Reviews:/); } print &header("Perforce User Information"); print "

    Perforce User Information for $user

    \n
    ", "This browser allows you to view information about ", " a given Perforce user.\n
    ", "

    Full Name

            $fullname
    \n", "

    Email

            ", &mailto ("$email"), "
    \n"; print "

    Last Update

            $update
    \n" if $update; print "

    Last Access

            $access
    \n"; print "

    JobView

                $jobview
    \n" if $jobview; print "

    Reviews

    \n
    ";
    
        while () { print; }
        print "
    \n"; close P4; @OTHER_FOOTERS = (" | ", , &url ("opened", "user=$user", "Files Opened by $user")); } ################################ # # p4 clients # ################################ elsif ( $INPUT{'cmd'} eq "clients" ) { &p4open( 'P4', "clients|" ); print &header("Perforce Clients"); print "

    Perforce Clients

    \n
    ", "This browser allows you to view information about ", " Perforce clients.\n", "
    "; print "", "\n"; # Sample: # Client oak.template 1998/06/25 root /tmp 'OAK client template ' while( ) { if (local( $client, $date, $root, $descrip ) = /Client (\S+) (\S+) root (\S+) '(.*)'/) { print "", "\n" } } print "
    ClientDateRoot Directory", "Description
    ", &url ( "client", "name=$client", "$client"), "$date", "$root", "$descrip", "
    \n"; close P4; } ################################ # # p4 client # "name=clientname" - the client to list *required* # ################################ elsif ( $INPUT{'cmd'} eq "client" ) { local( $client, $date, $update, $access, $owner, $root, $opts ); $client = $INPUT{'name'}; &p4open ('P4', "client -o $client|"); while () { next if (/^Client:/); next if (/^Date:/ && (( $date ) = /^Date:\s*(.*)$/ )); # Pre-99.1 next if (/^Update:/ && (( $update ) = /^Update:\s*(.*)$/ )); next if (/^Access:/ && (( $access ) = /^Access:\s*(.*)$/ )); next if (/^Owner:/ && (( $owner ) = /^Owner:\s*(\S+)$/ )); last if (/^Description:/); } print &header("Perforce Client Information"); print "

    Perforce Client Information for $client

    \n
    ", "This browser allows you to view information about ", " a given Perforce client.\n
    "; unless ($date || $update) { print "

    Client $client doesn't exist

    "; } else { if($date) { print "

    Date

            $date
    \n"; } else { print "

    Update

          $update
    \n", "

    Access

          $access
    \n"; } print "

    User

            ",
    	      &url ("user", "name=$owner", "$owner"), "
    \n", "

    Description

    \n";
    
    	while ()
    	{
    	   next if (/^$/);
    	   last if (/^Root:/ && (( $root ) = /^Root:\s*(.*)$/ ));
    	   print;
    	}
    	while ()
    	{
    	   next if (/^Options:/ && (( $opts ) = /^Options:\s*(.*)$/ ));
    	   last if (/^View:/);
            }
    	print "

    Root Directory

            $root
    \n", "

    Options

            $opts
    \n", "

    View

    ";
    	while ()
    	{
    	    last if (/^$/);
    	    print;
    	}
    	print "
    "; } close P4; @OTHER_FOOTERS = (" | ", &url ("files", "spec=\@$client", "Files in $client"), " | ", &url ("opened", "client=$client", "Files Opened in $client")); } ################################ # # p4 jobs # ################################ elsif ( $INPUT{'cmd'} eq "jobs" ) { &p4open( 'P4', "jobs|" ); print &header("Perforce Jobs"); print "

    Perforce Jobs

    \n
    ", "This browser allows you to view information about ", " Perforce Jobs.\n", "
    "; print "", "\n"; # Sample: # job000011 on 1998/07/03 by jam *open* 'Another test. ' while( ) { if (local( $name, $date, $user, $status, $descrip ) = /^(\S+) on (\S+) by (\S+) \*(\S+)\* '(.*) '$/) { print "", "\n"; } } print "
    Job NameDateUser", "StatusDescription
    ", &url ( "job", "job=$name", "$name"), "$date", "", &url ( "user", "name=$user", "$user"), "$status", "$descrip", "
    \n"; close P4; } ################################ # # List branches or labels ('p4 branches' or 'p4 labels') # ################################ elsif ( $INPUT{'cmd'} eq "branches" || $INPUT{'cmd'} eq "labels" ) { local ($type, $viewer, $plural); local ($cmd) = $INPUT{'cmd'}; if ($cmd eq "branches") { $type = "Branch"; $plural = "Branches"; $viewer = "branch"; } else { $type = "Label"; $plural = "Labels"; $viewer = "label"; } &p4open( 'P4', "$cmd|" ); print &header("Perforce ${plural}"); print "

    Perforce ${plural}

    \n
    ", "This browser allows you to view information about ", " Perforce ${plural}.\n", "
    ", "", "\n"; # Sample: # Branch test 1998/07/03 'Created by jam. ' # Label example-label.template 1998/06/26 'Label tempalte for the example ' while( ) { if (local( $name, $date, $descrip ) = /^$type (\S+) (\S+) '(.*) '$/) { print "", "\n"; } } print "
    $type NameDateDescription
    ", &url ( "$viewer", "name=$name", "$name"), "$date", "$descrip", "
    \n"; close P4; } ############################### # # p4 branch # "name=branchname" - the branch name to describe *required* # ############################### elsif ( $INPUT{'cmd'} eq "branch" ) { local( $name, $date, $update, $access, $owner ); $name = $INPUT{'name'}; &p4open ('P4', "branch -o $name|"); while () { next if (/^Date/ && (( $date ) = /^Date:\s*(.*)$/ )); # Pre-99.1 next if (/^Update/ && (( $update ) = /^Update:\s*(.*)$/ )); next if (/^Access/ && (( $access ) = /^Access:\s*(.*)$/ )); next if (/^Owner/ && (( $owner ) = /^Owner:\s*(.*)$/ )); last if (/^Description:/); } print &header("Perforce Branch Information"); print "

    Perforce Branch Information for $name

    \n
    ", "This browser allows you to view information about ", " a given Perforce branch.\n
    "; if($owner) { if($date) { print "

    Date

            $date
    \n" } else { print "

    Update

          $update
    \n", "

    Access

          $access
    \n"; } print "

    User

            ",
    	&url ("user", "name=$owner", "$owner"), "
    \n"; print "

    Description

    \n";
    	while () {
    	    next if (/^$/);
    	    last if (/^View:/);
    	    print;
    	}
    	print "
    \n"; print "

    View

    \n";
    	local (@speclist, $spec);
    	while () {
    	    next if (/^$/);
    	    push( @speclist, /^\s*\S+\s+(\S+)$/ );
    	    print;
    	}
    	$spec = join('+', @speclist);
    	print "
    \n"; @OTHER_FOOTERS = (" | ", &url ("files", "spec=$spec", "Files in this Branch")); } else { print "

    Branch $name doesn't exist

    \n"; } close P4; } ############################### # # p4 label # "name=labelname" - the label name to describe *required* # ############################### elsif ( $INPUT{'cmd'} eq "label" ) { local( $name, $date, $update, $owner, $opts ); $name = $INPUT{'name'}; &p4open ('P4', "label -o $name|"); while () { next if (/^Date/ && (( $date ) = /^Date:\s*(.*)$/ )); # Pre-99.1 next if (/^Update/ && (( $update ) = /^Update:\s*(.*)$/ )); next if (/^Owner/ && (( $owner ) = /^Owner:\s*(.*)$/ )); last if (/^Description:/); } print &header("Perforce Label Information"); print "

    Perforce Label Information for $name

    \n
    ", "This browser allows you to view information about ", " a given Perforce label.\n
    "; if($owner) { if($date) { print "

    Date

            $date
    \n" } else { print "

    Last Update

          $update
    \n"; } print "

    User

            ",
    	&url ("user", "name=$owner", "$owner"), "
    \n"; print "

    Description

    \n";
    	while () {
    	    next if (/^$/);
    	    last if (/^\S+/);
    	    print;
    	}
    	print "
    \n"; if( /^Options/ && (( $opts ) = /^Options:\s*(.*)$/ )) { print "

    Options

         $opts
    \n"; } while () { last if (/^View/); } print "

    View

    \n";
    	while () {
    	    next if (/^$/);
    	    print;
    	}
    	print "
    \n"; @OTHER_FOOTERS = (" | ", &url ("files", "spec=\@$name", "Files in this Label")); } else { print "

    Label $name doesn't exist

    \n"; } close P4; } ################################ # # p4 changes # "spec=path_spec" - the file/path specification *required* # "limit=b" - flag to limit the number of changes listed # "max=n" - max # of changes to list (ignored unless 'limit' set) # ################################ elsif( $INPUT{'cmd'} eq "changes" ) { local ($max) = $INPUT{'max'} || 20; print &header("Changes for $INPUT{'spec'}"); print "

    Changes for $INPUT{'spec'}

    \n", "This form displays the changes for the files you've selected.\n", "Click on the change number to see details of a change. Changes\n", "are listed in reverse chronological order, so you see what's\n", "most recent first.
    \n"; print &formtop("changes"); print "
    \n", "
    ", "Limit results to last "; print "changes\n"; print "

    \n"; &bail("Invalid number entered") unless $max > 0; $max = ( $INPUT{'limit'} ? "-m " . $max : "" ); &p4open( 'P4', "changes -l $max $INPUT{'spec'} |" ); while () { s/&/&/g; s/\"/"/g; s//>/g; if( local( $change, $on, $user, $client ) = /^Change (\d+) on (\S+) by (\S+)@(\S+)$/ ) { print "
    ", &url( "describe", "change=$change", "Change $change" ), " on $on by ", &url ("user", "name=$user", "$user"), " on client ", &url ("client", "name=$client", "$client"), "
    \n"; } else { chop; print "$_
    \n"; } } print "
    \n"; close P4; } ################################ # # p4 describe # "change=c" - the change number to list # ################################ elsif( $INPUT{'cmd'} eq "describe" ) { &p4open( 'P4', "describe -s $INPUT{'change'}|" ); $_ = ; ( local($chn, $user, $client, $date, $time) = /^Change (\d+) by (\S*)@(\S*) on (\S*) (\S*)$/ ) || &bail( $_ ); print &header("Change $chn"); print "

    Change $chn

    \n", "This form displays the details of a change. For each of the\n", "files affected, you can click on:\n", "
      \n", "
    • Filename -- to see the complete file history\n", "
    • Revision Number -- to see the file text\n", "
    • Action -- to see the deltas (diffs)\n", "
    ", "
    \n",
    	"Author        ", &url("user", "name=$user", 
    						"$user"), "\n",
    	"Client        ", &url ("client", "name=$client", 
    						 "$client"), "\n",
    	"Date          $time $date\n",
    	"

    \n", "

    Description

    \n", "
    \n";
    
        while() {
    	next if /^\s*$/;
    	last if /^Jobs fixed/;
    	last if /^Affected files/;
    	print $_;
        }
    
        print
    	"
    ", "
    \n"; # display jobs if( /^Jobs fixed/ ) { print "

    Jobs Fixed

    \n", "
      \n"; while ( ) { local( $job, $time, $user, $client ); while( ( $job, $time, $user, $client ) = /(\S*) fixed on (\S*) by (\S*)@(\S*)/ ) { print "
    • ", &url( "job", "job=$job", $job ), "

      \n";
      
      		while() {
      		    last if /^\S/;
      		    print $_;
      		}
      	    }
      
      	    print "
      \n"; last if /^Affected files/; } print "", "
      \n"; } print "

      Files

      \n", "
        \n", "", "\n"; # Sample: # ... //depot/main/p4/Jamrules#71 edit while() { if( local( $file, $rev, $act ) = /^\.\.\. (\S*)#(\d*) (\S*)$/ ) { print "", "\n"; } } print "
        FileRevAction
        ", &url( "filelog", "file=$file", "$file" ), "", &url( "print", "file=$file", "rev=$rev", "$rev" ), "", &url( "diff", "file=$file", "rev=$rev", "mode=$act", "$act" ), "
      \n"; close P4; } ################################ # # p4 filelog # "file=f" - the file to display # ################################ elsif ($INPUT{'cmd'} eq "filelog") { local( $name ) = $INPUT{'file'}; &p4open( 'P4', "filelog $name|" ); $name = ; chop $name; print &header("Filelog $name"); print "

      Filelog $name

      \n", "This form shows the history of an individual file across\n", "changes. You can click on the following:\n", "
        \n", "
      • Revision Number -- to see the file text\n", "
      • Action -- to see the deltas (diffs)\n", "
      • Change -- to see the complete change description, including\n", "other files.\n", "
      ", "
      \n"; print "", "", "\n"; # Sample: # ... #78 change 1477 edit on 04/18/1996 by user@client (text) 'Fix mkdir' while( ) { if (local( $rev, $change, $act, $date, $user, $client, $desc ) = /^\.\.\. \#(\d+) \S+ (\d+) (\S+) on (\S+) by (\S*)@(\S*) \(\S+\) '(.*)'/) { if ($act eq 'branch') { $_ = ; my ($fromname,$fromrev) = /^.*branch from (\S+?)\#(\d+).*/; print "", "\n"; } elsif ($act eq 'delete') { print "", "\n"; } else { print "", "\n"; } } } print "
      RevActionDateUserclientChangeDesc
      ", &url( "print", "file=$name", "rev=$rev", "$rev" ), "", &url( "filelog", "file=$fromname", "rev=$fromrev", $act ), "$date", "", &url ("user", "name=$user", "$user"), "", &url ("client", "name=$client", "$client"), "", &url( "describe", "change=$change", "$change" ), "$desc", "
      ", &url( "print", "file=$name", "rev=$rev", "$rev" ), "$DEL$act$DELEND", "$date", "", &url ("user", "name=$user", "$user"), "", &url ("client", "name=$client", "$client"), "", &url( "describe", "change=$change", "$change" ), "$desc", "
      ", &url( "print", "file=$name", "rev=$rev", "$rev" ), "", &url( "diff", "file=$name", "rev=$rev", "mode=$act", $act ), "$date", "", &url ("user", "name=$user", "$user"), "", &url ("client", "name=$client", "$client"), "", &url( "describe", "change=$change", "$change" ), "$desc", "
      \n"; close P4; # Check if file is opened by any client &p4open( 'P4', "opened -a $name 2> /dev/null |" ) if $name; if( $name && ($_ = ) ) { print "

      \n\n", "\n", "", "", "\n"; # Samples: # //foo/file.java#2 - edit default change (text) by user@client # //foo/file.java#2 - delete change 154 (text) by user@client *locked* do { if (local( $rev, $act, $change, $list, $user, $client, $stat ) = /^\S+\#(\d+) - (\w+) (\w+) (\w+) \(\w+\) by (\S+)@(\S+)\s?(.*)$/) { print "", ($stat ? "" : ""), "\n"; } } while (); print "
      ", "
      This file is currently opened by:
      UserClientOpened
      for...
      Revision
      opened
      Pending
      changelist
      Status
      ", &url("user", "name=$user", $user), "", &url("client", "name=$client", $client ), "$act$rev", ("$change" eq "default" ? "default" : &url("describe","change=$list",$list)), "$stat
      \n"; } close P4 if $name; } ################################ # # p4 files # "spec=file_spec" - the spec (path) for the file(s) *required* # ################################ elsif ($INPUT{'cmd'} eq "files") { &p4open( 'P4', "files $INPUT{'spec'}|" ); print &header("Files for $INPUT{'spec'}"); print "

      Files for $INPUT{'spec'}

      \n", "This form displays files in the depot for a given revision.\n", "For each of the files, you can click on:\n", "
        \n", "
      • Filename -- to see the complete file history\n", "
      • Revision Number -- to see the file text\n", "
      • Action -- to see the deltas (diffs)\n", "
      • Change -- to see the complete change description, including\n", "other files.\n", "
      ", "
      \n"; print "

      Files

      \n", "
        \n", "", "\n"; # Sample: # //example/find/TypeExpr.java#1 - add change 5 (ktext) while() { if( local( $file, $rev, $act, $change, $type ) = /^(\S+)#(\d*) - (\S+) change (\d*) \((\S+)\)$/ ) { print "", "\n"; } } print "
        FileRevActionChange
        ", &url( "filelog", "file=$file", "$file" ), "", &url( "print", "file=$file", "rev=$rev", "$rev" ), "", &url( "diff", "file=$file", "rev=$rev", "mode=$act", "$act" ), "", &url( "describe", "change=$change", "$change" ), "", "$type", "
      \n"; close P4; } ################################ # # p4 opened # "spec=spec" - a file specification (path) to use # "client=clientname" - a client to be queried # "user=username" - a user to be queried # ################################ elsif ($INPUT{'cmd'} eq "opened") { local( $openedcmd ) = ( $INPUT{'client'} ? "-c $INPUT{'client'} opened" : "opened -a" ); &p4open( 'P4', "$openedcmd $INPUT{'spec'} |" ); local( $title ) = "Opened files for"; $title .= " $INPUT{'user'}" if $INPUT{'user'}; $title .= " $INPUT{'client'}" if $INPUT{'client'}; $title .= " $INPUT{'spec'}" if $INPUT{'spec'}; print &header("$title"); print "

      $title

      \n", "For each of the files, you can click on:\n", "
        \n", "
      • Filename -- to see the complete file history\n", "
      • Revision Number -- to see the file text\n", "
      • Change List (if not default) -- to see a change description\n", "
      • User -- to see the a user description\n", "
      • Client -- to see the a client description\n", "
      ", "
      \n"; print "

      Files

      \n", "
        \n", "", "", "\n"; # Samples: # //foo/file.java#2 - edit default change (text) by user@client # //foo/file.java#2 - delete change 154 (text) by user@client *locked* while() { if (local($file, $rev, $act, $change, $list, $type, $user, $client, $stat) = /^(\S+)#(\d+) - (\w+) (\w+) (\w+) \((\w+)\) by (\S+)@(\S+)\s?(.*)$/) { next if ( $INPUT{'user'} && $INPUT{'user'} ne $user ); print "", "", "", "", "", "", "", "", ($stat ? "" : "" ), "\n"; } } print "
        RevActionChange ListTypeUserClient
        ", &url( "filelog", "file=$file", "$file" ), "", &url( "print", "file=$file", "rev=$rev", "$rev" ), "$act", ("$change" eq "default" ? "default" : &url("describe", "change=$list", $list)), "$type", &url( "user", "name=$user", "$user" ), "", &url( "client", "name=$client", "$client" ), "$stat
      \n"; close P4; } ################################ # # p4 print # "file=filespec" - the file to print *required* # "rev=n" - the file revision to print # ################################ elsif ($INPUT{'cmd'} eq "print") { local($name, $rev) = ($INPUT{'file'}, $INPUT{'rev'}); $rev = "head" unless $rev; &p4open( 'P4', "print $name#$rev|" ); # Get header line # //depot/main/jam/Jamfile#39 - edit change 1749 (text) $_ = ; local( $name, $rev, $type ) = m!^(\S+)\#(\d+) - \S+ \S+ \S+ \((\w+)\)!; local( $ext ) = $name =~ m!^.*\.([^/.]+)$!; local( $ctype ); if( $type =~ /binary/ && ($ext) ) { if( $ext =~ /^gif$/i || $ext =~ /^jpg$/i ) { $ctype = "image/$ext"; } else { $ctype = "application/$ext"; } } elsif ($ext =~ /^html?$/i) { $ctype = "text/html"; } if($ctype) { print "Content-type: $ctype \n\n" if ($ENV{'GATEWAY_INTERFACE'}); while() { print; } close P4; exit; } print &header("File $name#$rev"); print "

      File $name#$rev

      \n", "This form shows you the raw contents of a file, as long as \n", "it isn't binary.", "
      \n"; if( $type =~ /binary/ ) { print "

      $type

      \n"; } else { print "
      \n";
      
      	while(  ) {
      	    s/&/&/g;
      	    s/\"/"/g;
      	    s//>/g;
      	    print $_;
      	}
      
      	print "
      \n"; } close P4; } ################################ # # p4 diff # "file=filename" - the name of the file to diff *required* # "rev=n" - the revision to diff (with previous version) *req'd* # "mode=mode" - needed if this rev is 'add', 'delete', or 'branch' # ################################ elsif ($INPUT{'cmd'} eq "diff") { local( $name,$rev,$mode ) = ($INPUT{'file'},$INPUT{'rev'},$INPUT{'mode'}); local( $nchunk ) = 0; print &header("$name#$rev - $mode"); print "

      $name#$rev - $mode

      \n", "This form shows you the deltas (diffs) that lead from the\n", "previous to the current revision.\n", "
      \n"; if ($mode ne 'add' && $mode ne 'delete' && $mode ne 'branch') { local($f1) = "$name#" . ($rev - 1); local($f2) = "$name#" . ($rev); &p4open('P4', "diff2 $f1 $f2|"); $_ = ; while () { local( $dels, $adds ); local( $la, $lb, $op, $ra, $rb ) = /(\d+),?(\d*)([acd])(\d+),?(\d*)/; next unless $ra; if( !$lb ) { $lb = $op ne 'a' ? $la : $la - 1; } if( !$rb ) { $rb = $op ne 'd' ? $ra : $ra - 1; } $start[ $nchunk ] = $op ne 'd' ? $ra : $ra + 1; $dels[ $nchunk ] = $dels = $lb - $la + 1; $adds[ $nchunk ] = $adds = $rb - $ra + 1; $lines[ $nchunk ] = (); # deletes while( $dels-- ) { $_ = ; s/^. //; if (/[&<>]/) { s/&/\&/g; s//\>/g; } $lines[ $nchunk ] .= $_; } # separator if ($op eq 'c') { $_ = ; } # adds while( $adds-- ) { $_ = ; } $nchunk++; } close P4; } # Now walk through the diff chunks, reading the current rev and # displaying it as necessary. print "
      ",
          	"$ADD added lines $ADDEND\n",
      	"$DEL deleted lines $DELEND\n",
      	"

      \n";
      
          local( $curlin ) = 1;
      
          &p4open('P4', "print -q $name#$rev|");
      
          for( $n = 0; $n < $nchunk; $n++ )
          {
      	# print up to this chunk.
      
      	&catchup( 'P4', $start[ $n ] - $curlin );
      
      	# display deleted lines -- we saved these from the diff
      
      	if( $dels[ $n ] )
      	{
      		print "$DEL";
      		print $lines[ $n ];
      		print "$DELEND";
      	}
      
      	# display added lines -- these are in the file stream.
      
      	if( $adds[ $n ] )
      	{
      		print "$ADD";
      		&display( 'P4', $adds[ $n ] );
      		print "$ADDEND";
      	}
      
      	$curlin = $start[ $n ] + $adds[ $n ];
          }
      
          &catchup( 'P4', 999999999 );
      
          print "
      \n"; close P4; } ################################ # # p4 job # "job=n" - the job to describe *required* # ################################ elsif ($INPUT{'cmd'} eq "job") { local( $user, $job, $status, $time, $date ); &p4open( 'P4', "job -o $INPUT{'job'}|" ); while( ) { next if ( /^Job/ && ( ( $job ) = /^Job:\s(\S*)/ ) ); next if ( /^User/ && ( ( $user ) = /^User:\s*(\S*)/ ) ); next if ( /^Status/ && ( ( $status ) = /^Status:\s*(\S*)/ ) ); next if ( /^Date/ && ( ( $date, $time ) = /^Date:\s*(\S*) (\S*)/ ) ); last if ( /^Description/ ); } print &header("Job $job"); print "

      Job $job

      \n", "This form displays the details of a job. You can click on a\n", "change number to see its description.\n", "", "
      \n",
              "User          ", &url("user", "name=$user", "$user"),
      	"\n",
      	"Status        $status\n",
      	"Date          $time $date\n",
      	"

      \n", "

      Description

      \n", "
      \n";
      
          while() {
      	print $_;
          }
      
          print
      	"
      ", "
      \n"; close P4; # display fixes &p4open( 'P4', "fixes -j $INPUT{'job'}|" ); $count = 0; while( ) { print "

      Fixes

      \n", "
        \n", "", "\n" if( !$count++ ); # jobx fixed by change N on 1997/04/25 by user@host if( local( $change, $date, $user, $client ) = /^\S* fixed by change (\S*) on (\S*) by (\S*)@(\S*)/ ) { print "", "\n"; } } print "
        ChangeDateUser\@Client
        ", &url( "describe", "change=$change", "$change" ), "", $date, "", &url ("user", "name=$user", "$user"), "\@", &url ("client", "name=$client", "$client"), "
      \n" if( $count ); close P4; } ################################ # # None of the above. # ################################ else { &bail( "Invalid invocation $INPUT{'cmd'}" ); } # Trailer @HTMLFOOTER = ( "

      *  *  *
      \n", "Top | \n", &url ("clients", "Clients"), " | \n", &url ("users", "Users"), " | \n", &url ("branches", "Branches"), " | \n", &url ("labels", "Labels"), " | \n", &url ("jobs", "Jobs"), "\n", @OTHER_FOOTERS, "\n", "\n"); print @HTMLFOOTER; ################################################################## ################################################################## # # Subroutines. # ################################################################## ################################################################## sub header { if($PRINTEDHEADER) { return; } $PRINTEDHEADER = 1; local ($string) = "Content-type: text/html\n\n" if ($ENV{'GATEWAY_INTERFACE'}); $string .= "\n" . "@_\n" . "\n"; return $string; } sub url { local( @options ) = @_[0 .. $#_-1]; local ($i) = 0; for(; $i<=$#options; $i++) { splice(@options,$i,1) if !$options[$i]; } local( $url ) = "$myname?cmd=" . join('&',@options); return qq($_[$#_]); } sub formtop { local( $action ) = $_[0]; return "

      \n" . "\n"; } sub mailto { return qq(@_) ; } sub bail { print &header("Script Error"); print @_, "\n\n"; die @_; } sub p4open { local( $handle, @command ) = @_; open( $handle, "p4 @command" ) || &bail( "p4 @command failed" ); } # Support for processing diff chunks. # # skip: skip lines in source file # display: display lines in source file, handling funny chars # catchup: display & skip as necessary # sub skip { local( $handle, $to ) = @_; while( $to > 0 && ( $_ = <$handle> ) ) { $to--; } return $to; } sub display { local( $handle, $to ) = @_; while( $to-- > 0 && ( $_ = <$handle> ) ) { if (/[&<>]/) { s/&/\&/g; s//\>/g; } print $_; } } sub catchup { local( $handle, $to ) = @_; if( $to > $MAXCONTEXT ) { local( $skipped ) = $to - $NCONTEXT * 2; &display( $handle, $NCONTEXT ); $skipped -= &skip( $handle, $skipped ); print "
      ", "$skipped lines skipped", "

      \n" if( $skipped ); &display( $handle, $NCONTEXT ); } else { &display; } } sub ReadParse { local (*hash) = @_ if @_; # Reference to hash table local ($i, $key, $val, $in, @list); # Read in text if ($ENV{ 'REQUEST_METHOD'} eq "GET" ) { $in = $ENV{'QUERY_STRING'}; } elsif ( $ENV{'REQUEST_METHOD'} eq "POST" ) { read(STDIN,$in,$ENV{'CONTENT_LENGTH'}); } else { $in = $ARGV[0]; } # Convert %XX from hex numbers to alphanumeric $in =~ s/%(\w\w)/pack("c",hex($1))/ge; @list = split(/[&;]/,$in); &TranslateOldStyle( \@list ); foreach $i (0 .. $#list) { # Convert plus's to spaces $list[$i] =~ s/\+/ /g; # Split into key and value (splits on first '=') ($key, $val) = split(/=/,$list[$i],2); # Associate key and value $hash{$key} .= "\0" if defined($hash{$key}); # \0 is the multiple separator $hash{$key} .= $val; } return scalar(@list); } sub TranslateOldStyle { local (*argv) = @_; # Reference to argument list # If we have the old style, all args are concatenated into the first # arg separated by spaces. The first character is '@' unless there is # only a file spec for 'changes' (In which case I assume the spec begins # with '//'). return unless scalar(@argv) == 1; $argv[0] = "\@changes+" . $argv[0] if ( $argv[0] =~ m!^//\w+! ); return unless $argv[0] =~ /^\@/; $argv[0] =~ s/^\@//; @argv = split(/\+/,$argv[0]); local( $cmd ) = $argv[0]; if( $cmd eq "changes" ) { $argv[1] = "spec=" . $argv[1]; push( @argv, "limit=1" ); } elsif( $cmd eq "describe" ) { $argv[1] = "change=" . $argv[1]; } elsif( $cmd eq "job" ) { $argv[1] = "job=" . $argv[1]; } elsif( $cmd eq "diff" ) { $argv[1] = "file=" . $argv[1]; $argv[2] = "rev=" . $argv[2]; $argv[3] = "mode=" . $argv[3]; } elsif( $cmd eq "print" ) { $argv[1] = "file=" . $argv[1]; $argv[2] = "rev=" . $argv[2]; } elsif( $cmd eq "filelog" ) { $argv[1] = "file=" . $argv[1]; } elsif( $cmd eq "files" ) { splice( @argv, 1, $#argv, ( "spec=" . join('+', @argv[1..$#argv]) )); } elsif( $cmd eq "opened" ) { local($mode) = splice( @argv, 1, 1 ); $argv[1] = ( $mode eq "user" ? "user=" : "client=" ) . $argv[1]; } elsif( $cmd eq "user" || $cmd eq "client" || $cmd eq "branch" || $cmd eq "label" ) { $argv[1] = "name=" . $argv[1]; } $argv[0] = "cmd=" . $cmd; }