Fix build with core.autocrlf=true
[gitweb.git] / perl / Git.pm
index 9026a7bb980a984086a62536f46ec3837588f277..bfce1f795dfa5fea05f4f96637a1ae2333038735 100644 (file)
@@ -59,7 +59,7 @@ =head1 SYNOPSIS
                 command_bidi_pipe command_close_bidi_pipe
                 version exec_path html_path hash_object git_cmd_try
                 remote_refs prompt
-                get_tz_offset
+                get_tz_offset get_record
                 credential credential_read credential_write
                 temp_acquire temp_is_locked temp_release temp_reset temp_path);
 
@@ -188,7 +188,8 @@ sub repository {
                };
 
                if ($dir) {
-                       $dir =~ m#^/# or $dir = $opts{Directory} . '/' . $dir;
+                       _verify_require();
+                       File::Spec->file_name_is_absolute($dir) or $dir = $opts{Directory} . '/' . $dir;
                        $opts{Repository} = abs_path($dir);
 
                        # If --git-dir went ok, this shouldn't die either.
@@ -392,7 +393,7 @@ sub command_close_pipe {
 Execute the given C<COMMAND> in the same way as command_output_pipe()
 does but return both an input pipe filehandle and an output pipe filehandle.
 
-The function will return return C<($pid, $pipe_in, $pipe_out, $ctx)>.
+The function will return C<($pid, $pipe_in, $pipe_out, $ctx)>.
 See C<command_close_bidi_pipe()> for details.
 
 =cut
@@ -537,6 +538,20 @@ sub get_tz_offset {
        return sprintf("%s%02d%02d", $sign, (gmtime(abs($t - $gm)))[2,1]);
 }
 
+=item get_record ( FILEHANDLE, INPUT_RECORD_SEPARATOR )
+
+Read one record from FILEHANDLE delimited by INPUT_RECORD_SEPARATOR,
+removing any trailing INPUT_RECORD_SEPARATOR.
+
+=cut
+
+sub get_record {
+       my ($fh, $rs) = @_;
+       local $/ = $rs;
+       my $rec = <$fh>;
+       chomp $rec if defined $rs;
+       $rec;
+}
 
 =item prompt ( PROMPT , ISPASSWORD  )
 
@@ -864,6 +879,76 @@ sub ident_person {
        return "$ident[0] <$ident[1]>";
 }
 
+=item parse_mailboxes
+
+Return an array of mailboxes extracted from a string.
+
+=cut
+
+# Very close to Mail::Address's parser, but we still have minor
+# differences in some cases (see t9000 for examples).
+sub parse_mailboxes {
+       my $re_comment = qr/\((?:[^)]*)\)/;
+       my $re_quote = qr/"(?:[^\"\\]|\\.)*"/;
+       my $re_word = qr/(?:[^]["\s()<>:;@\\,.]|\\.)+/;
+
+       # divide the string in tokens of the above form
+       my $re_token = qr/(?:$re_quote|$re_word|$re_comment|\S)/;
+       my @tokens = map { $_ =~ /\s*($re_token)\s*/g } @_;
+       my $end_of_addr_seen = 0;
+
+       # add a delimiter to simplify treatment for the last mailbox
+       push @tokens, ",";
+
+       my (@addr_list, @phrase, @address, @comment, @buffer) = ();
+       foreach my $token (@tokens) {
+               if ($token =~ /^[,;]$/) {
+                       # if buffer still contains undeterminated strings
+                       # append it at the end of @address or @phrase
+                       if ($end_of_addr_seen) {
+                               push @phrase, @buffer;
+                       } else {
+                               push @address, @buffer;
+                       }
+
+                       my $str_phrase = join ' ', @phrase;
+                       my $str_address = join '', @address;
+                       my $str_comment = join ' ', @comment;
+
+                       # quote are necessary if phrase contains
+                       # special characters
+                       if ($str_phrase =~ /[][()<>:;@\\,.\000-\037\177]/) {
+                               $str_phrase =~ s/(^|[^\\])"/$1/g;
+                               $str_phrase = qq["$str_phrase"];
+                       }
+
+                       # add "<>" around the address if necessary
+                       if ($str_address ne "" && $str_phrase ne "") {
+                               $str_address = qq[<$str_address>];
+                       }
+
+                       my $str_mailbox = "$str_phrase $str_address $str_comment";
+                       $str_mailbox =~ s/^\s*|\s*$//g;
+                       push @addr_list, $str_mailbox if ($str_mailbox);
+
+                       @phrase = @address = @comment = @buffer = ();
+                       $end_of_addr_seen = 0;
+               } elsif ($token =~ /^\(/) {
+                       push @comment, $token;
+               } elsif ($token eq "<") {
+                       push @phrase, (splice @address), (splice @buffer);
+               } elsif ($token eq ">") {
+                       $end_of_addr_seen = 1;
+                       push @address, (splice @buffer);
+               } elsif ($token eq "@" && !$end_of_addr_seen) {
+                       push @address, (splice @buffer), "@";
+               } else {
+                       push @buffer, $token;
+               }
+       }
+
+       return @addr_list;
+}
 
 =item hash_object ( TYPE, FILENAME )
 
@@ -1353,6 +1438,44 @@ sub END {
 
 } # %TEMP_* Lexical Context
 
+=item prefix_lines ( PREFIX, STRING [, STRING... ])
+
+Prefixes lines in C<STRING> with C<PREFIX>.
+
+=cut
+
+sub prefix_lines {
+       my $prefix = shift;
+       my $string = join("\n", @_);
+       $string =~ s/^/$prefix/mg;
+       return $string;
+}
+
+=item get_comment_line_char ( )
+
+Gets the core.commentchar configuration value.
+The value falls-back to '#' if core.commentchar is set to 'auto'.
+
+=cut
+
+sub get_comment_line_char {
+       my $comment_line_char = config("core.commentchar") || '#';
+       $comment_line_char = '#' if ($comment_line_char eq 'auto');
+       $comment_line_char = '#' if (length($comment_line_char) != 1);
+       return $comment_line_char;
+}
+
+=item comment_lines ( STRING [, STRING... ])
+
+Comments lines following core.commentchar configuration.
+
+=cut
+
+sub comment_lines {
+       my $comment_line_char = get_comment_line_char;
+       return prefix_lines("$comment_line_char ", @_);
+}
+
 =back
 
 =head1 ERROR HANDLING