1#! /usr/bin/env perl
2
3# colorsvn
4#
5# based on colorgcc
6#
7# Requires the ANSIColor module from CPAN.
8#
9# Usage:
10#
11# In a directory that occurs in your PATH _before_ the directory
12# where svn lives, create a softlink to colorsvn:
13#
14#    svn -> colorsvn
15#
16# That's it. When "svn" is invoked, colorsvn is run instead.
17#
18# The default settings can be overridden with ~/.colorcvsrc.
19# See the colorcvsrc-sample for more information.
20#
21# Note:
22#
23# colorsvn will only emit color codes if:
24#
25#    (1) tts STDOUT is a tty.
26#    (2) the value of $TERM is not listed in the "nocolor" option.
27#    (3) the svn command is not a commit or import (as the text editor
28#    opened by svn will often be hampered by colorsvn).
29#
30# If colorsvn colorizes the output, svn's STDERR will be
31# combined with STDOUT. Otherwise, colorsvn just passes the output from
32# svn through without modification.
33#
34# Copyright 2002 Neil Stevens <neil@qualityassistant.com>
35#
36# Copyright 1999 Jamie Moyers <jmoyers@geeks.com>
37#
38# This program is free software; you can redistribute it and/or modify
39# it under the terms of the GNU General Public License as published by
40# the Free Software Foundation; version 2 of the License as published
41# by the Free Software Foundation.
42#
43# This program is distributed in the hope that it will be useful,
44# but WITHOUT ANY WARRANTY; without even the implied warranty of
45# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
46# GNU General Public License for more details.
47#
48# You should have received a copy of the GNU General Public License
49# along with this program; if not, write to the Free Software
50# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
51
52use Term::ANSIColor;
53use IPC::Open3;
54use File::Spec::Functions qw/catfile file_name_is_absolute path/;
55
56sub initDefaults
57{
58	$svnCmd = "svn";
59
60	$nocolor{"dumb"} = "true";
61
62	$colors{"P"} = color("reset");
63	$colors{"U"} = color("reset");
64	$colors{" "} = color("reset");
65	$colors{"C"} = color("bold red");
66	$colors{"M"} = color("bold yellow");
67	$colors{'G'} = color("bold yellow");
68	$colors{"A"} = color("cyan");
69	$colors{"R"} = color("cyan");
70	$colors{"D"} = color("red");
71	$colors{"I"} = color("bold");
72	$colors{"?"} = color("bold");
73	$colors{"!"} = color("bold");
74	$colors{"~"} = color("bold red");
75	$colors{"server"} = color("bold green");
76	$colors{"warning"} = color("bold cyan");
77
78	# Applies when only the properties changed
79	$propcolors{"C"} = color("bold red");
80	$propcolors{"M"} = color("yellow");
81}
82
83sub loadPreferences
84{
85# Usage: loadPreferences("filename");
86
87	my($filename) = @_;
88
89	open(PREFS, "<$filename") || return;
90
91	while(<PREFS>)
92	{
93		next if (m/^\#.*/);          # It's a comment.
94		next if (!m/(.*):\s*(.*)/);  # It's not of the form "foo: bar".
95
96		$option = $1;
97		$value = $2;
98
99		if ($option =~ /svn/)
100		{
101			$svnCmd = $value;
102		}
103		elsif ($option eq "nocolor")
104		{
105			# The nocolor option lists terminal types, separated by
106			# spaces, not to do color on.
107			foreach $termtype (split(/\s+/, $value))
108			{
109				$nocolor{$termtype} = "true";
110			}
111		}
112		elsif ($option =~ /prop (.)/)
113		{
114		        # Property color
115		        $propcolors{$1} = color($value);
116		}
117		else
118		{
119			$colors{$option} = color($value);
120		}
121	}
122	close(PREFS);
123}
124
125#
126# Main program
127#
128
129# Set up default values for colors and svn path.
130initDefaults();
131
132# Read the configuration file, if there is one.
133$configFile = $ENV{"HOME"} . "/.colorcvsrc";
134if (-f $configFile)
135{
136	loadPreferences($configFile);
137}
138
139# Get the terminal type.
140$terminal = $ENV{"TERM"} || "dumb";
141
142$commit = 0;
143foreach (@ARGV)
144{
145	if(/^ci$/ || /^commit$/ || /^import$/ || /^prop/ || /^p[delsg]$/)
146	{
147		$commit = 1;
148		break;
149	}
150	elsif (! /^-/)
151	{
152		break;
153	}
154}
155
156# If it's in the list of terminal types not to color, or if
157# we're writing to something that's not a tty, don't do color.
158if (! -t STDOUT || $commit == 1 || $nocolor{$terminal})
159{
160	exec $svnCmd, @ARGV;
161	die("Couldn't exec");
162}
163
164sub svn_not_found() {
165	die ("$svnCmd not found, add svn=/full/path/to/svn to ~/.colorcvsrc");
166}
167
168# Check if we have SVN binary accessible. Of course, there could
169# be a race, but we don't care - all we want is to print out
170# nice error if executable SVN binary could not be found.
171if (file_name_is_absolute($svnCmd)) {
172	svn_not_found unless -f $svnCmd and -x $svnCmd;
173} else {
174	my $found = 0;
175	foreach (path()) {
176		my $path = catfile($_, $svnCmd);
177		if (-f $path and -x $path) {
178			$found = 1;
179			last;
180		}
181	}
182	svn_not_found unless $found;
183}
184
185# Keep the pid of the svn process so we can get its return
186# code and use that as our return code.
187$svn_pid = open3('<&STDIN', \*SVNOUT, \*SVNOUT, $svnCmd, @ARGV);
188$svnName = $svnCmd;
189$svnName =~ s,.*/(.*)$,\1,;
190
191# Colorize the output from the svn program.
192while(<SVNOUT>)
193{
194	chomp;
195	if (m/^ (.).+/) # Property changed only
196	{
197	        print($propcolors{$1}, $_, color("reset"));
198	}
199	elsif (m/^(.).+/) # S filename
200	{
201		print($colors{$1}, $_, color("reset"));
202	}
203	elsif (m/warning:/) # warning
204	{
205		print($colors{"warning"}, $_, color("reset"));
206	}
207	elsif (m/^$svnName[^:]*: / || m/^svn server: /) # server message
208	{
209		print($colors{"server"}, $_, color("reset"));
210	}
211	else # Anything else
212	{
213		# Print normally.
214		print(color("reset"), $_);
215	}
216	print "\n";
217}
218
219# Get the return code of the svn program and exit with that.
220waitpid($svn_pid, 0);
221exit ($? >> 8);
222