]> asedeno.scripts.mit.edu Git - git.git/blobdiff - gitweb/gitweb.perl
gitweb: Use single implementation of export_ok check.
[git.git] / gitweb / gitweb.perl
index c5254afa7fabbc38c2abf188570cd5ab463b4c37..68bdf62657f748839b771ba4d58e8cbc943c0f93 100755 (executable)
@@ -534,23 +534,87 @@ sub evaluate_path_info {
        return if $input_params{'action'};
        $path_info =~ s,^\Q$project\E/*,,;
 
-       my ($refname, $pathname) = split(/:/, $path_info, 2);
+       # next, check if we have an action
+       my $action = $path_info;
+       $action =~ s,/.*$,,;
+       if (exists $actions{$action}) {
+               $path_info =~ s,^$action/*,,;
+               $input_params{'action'} = $action;
+       }
+
+       # list of actions that want hash_base instead of hash, but can have no
+       # pathname (f) parameter
+       my @wants_base = (
+               'tree',
+               'history',
+       );
+
+       # we want to catch
+       # [$hash_parent_base[:$file_parent]..]$hash_parent[:$file_name]
+       my ($parentrefname, $parentpathname, $refname, $pathname) =
+               ($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?(.+?)(?::(.+))?$/);
+
+       # first, analyze the 'current' part
        if (defined $pathname) {
-               # we got "project.git/branch:filename" or "project.git/branch:dir/"
-               # we could use git_get_type(branch:pathname), but it needs $git_dir
+               # we got "branch:filename" or "branch:dir/"
+               # we could use git_get_type(branch:pathname), but:
+               # - it needs $git_dir
+               # - it does a git() call
+               # - the convention of terminating directories with a slash
+               #   makes it superfluous
+               # - embedding the action in the PATH_INFO would make it even
+               #   more superfluous
                $pathname =~ s,^/+,,;
                if (!$pathname || substr($pathname, -1) eq "/") {
-                       $input_params{'action'} = "tree";
+                       $input_params{'action'} ||= "tree";
                        $pathname =~ s,/$,,;
                } else {
-                       $input_params{'action'} = "blob_plain";
+                       # the default action depends on whether we had parent info
+                       # or not
+                       if ($parentrefname) {
+                               $input_params{'action'} ||= "blobdiff_plain";
+                       } else {
+                               $input_params{'action'} ||= "blob_plain";
+                       }
                }
                $input_params{'hash_base'} ||= $refname;
                $input_params{'file_name'} ||= $pathname;
        } elsif (defined $refname) {
-               # we got "project.git/branch"
-               $input_params{'action'} = "shortlog";
-               $input_params{'hash'} ||= $refname;
+               # we got "branch". In this case we have to choose if we have to
+               # set hash or hash_base.
+               #
+               # Most of the actions without a pathname only want hash to be
+               # set, except for the ones specified in @wants_base that want
+               # hash_base instead. It should also be noted that hand-crafted
+               # links having 'history' as an action and no pathname or hash
+               # set will fail, but that happens regardless of PATH_INFO.
+               $input_params{'action'} ||= "shortlog";
+               if (grep { $_ eq $input_params{'action'} } @wants_base) {
+                       $input_params{'hash_base'} ||= $refname;
+               } else {
+                       $input_params{'hash'} ||= $refname;
+               }
+       }
+
+       # next, handle the 'parent' part, if present
+       if (defined $parentrefname) {
+               # a missing pathspec defaults to the 'current' filename, allowing e.g.
+               # someproject/blobdiff/oldrev..newrev:/filename
+               if ($parentpathname) {
+                       $parentpathname =~ s,^/+,,;
+                       $parentpathname =~ s,/$,,;
+                       $input_params{'file_parent'} ||= $parentpathname;
+               } else {
+                       $input_params{'file_parent'} ||= $input_params{'file_name'};
+               }
+               # we assume that hash_parent_base is wanted if a path was specified,
+               # or if the action wants hash_base instead of hash
+               if (defined $input_params{'file_parent'} ||
+                       grep { $_ eq $input_params{'action'} } @wants_base) {
+                       $input_params{'hash_parent_base'} ||= $parentrefname;
+               } else {
+                       $input_params{'hash_parent'} ||= $parentrefname;
+               }
        }
 }
 evaluate_path_info();
@@ -699,14 +763,61 @@ sub href (%) {
 
        my ($use_pathinfo) = gitweb_check_feature('pathinfo');
        if ($use_pathinfo) {
-               # use PATH_INFO for project name
+               # try to put as many parameters as possible in PATH_INFO:
+               #   - project name
+               #   - action
+               #   - hash_parent or hash_parent_base:/file_parent
+               #   - hash or hash_base:/filename
+
+               # When the script is the root DirectoryIndex for the domain,
+               # $href here would be something like http://gitweb.example.com/
+               # Thus, we strip any trailing / from $href, to spare us double
+               # slashes in the final URL
+               $href =~ s,/$,,;
+
+               # Then add the project name, if present
                $href .= "/".esc_url($params{'project'}) if defined $params{'project'};
                delete $params{'project'};
 
-               # Summary just uses the project path URL
-               if (defined $params{'action'} && $params{'action'} eq 'summary') {
+               # Summary just uses the project path URL, any other action is
+               # added to the URL
+               if (defined $params{'action'}) {
+                       $href .= "/".esc_url($params{'action'}) unless $params{'action'} eq 'summary';
                        delete $params{'action'};
                }
+
+               # Next, we put hash_parent_base:/file_parent..hash_base:/file_name,
+               # stripping nonexistent or useless pieces
+               $href .= "/" if ($params{'hash_base'} || $params{'hash_parent_base'}
+                       || $params{'hash_parent'} || $params{'hash'});
+               if (defined $params{'hash_base'}) {
+                       if (defined $params{'hash_parent_base'}) {
+                               $href .= esc_url($params{'hash_parent_base'});
+                               # skip the file_parent if it's the same as the file_name
+                               delete $params{'file_parent'} if $params{'file_parent'} eq $params{'file_name'};
+                               if (defined $params{'file_parent'} && $params{'file_parent'} !~ /\.\./) {
+                                       $href .= ":/".esc_url($params{'file_parent'});
+                                       delete $params{'file_parent'};
+                               }
+                               $href .= "..";
+                               delete $params{'hash_parent'};
+                               delete $params{'hash_parent_base'};
+                       } elsif (defined $params{'hash_parent'}) {
+                               $href .= esc_url($params{'hash_parent'}). "..";
+                               delete $params{'hash_parent'};
+                       }
+
+                       $href .= esc_url($params{'hash_base'});
+                       if (defined $params{'file_name'} && $params{'file_name'} !~ /\.\./) {
+                               $href .= ":/".esc_url($params{'file_name'});
+                               delete $params{'file_name'};
+                       }
+                       delete $params{'hash'};
+                       delete $params{'hash_base'};
+               } elsif (defined $params{'hash'}) {
+                       $href .= esc_url($params{'hash'});
+                       delete $params{'hash'};
+               }
        }
 
        # now encode the parameters explicitly
@@ -742,8 +853,7 @@ sub validate_project {
        my $input = shift || return undef;
        if (!validate_pathname($input) ||
                !(-d "$projectroot/$input") ||
-               !check_head_link("$projectroot/$input") ||
-               ($export_ok && !(-e "$projectroot/$input/$export_ok")) ||
+               !check_export_ok("$projectroot/$input") ||
                ($strict_export && !project_in_list($input))) {
                return undef;
        } else {
@@ -1846,7 +1956,10 @@ sub git_get_project_ctags {
        my $ctags = {};
 
        $git_dir = "$projectroot/$path";
-       foreach (<$git_dir/ctags/*>) {
+       unless (opendir D, "$git_dir/ctags") {
+               return $ctags;
+       }
+       foreach (grep { -f $_ } map { "$git_dir/ctags/$_" } readdir(D)) {
                open CT, $_ or next;
                my $val = <CT>;
                chomp $val;
@@ -1854,6 +1967,7 @@ sub git_get_project_ctags {
                my $ctag = $_; $ctag =~ s#.*/##;
                $ctags->{$ctag} = $val;
        }
+       closedir D;
        $ctags;
 }