Tue, 28 Aug 2012 18:29:30 +0200
Update from Drupal 6.x to 7.x and introduce several new HTML5 themes. Because
many themes from Drupal 6.x have since been abandoned, left unmaintained, or
not ported to Drupal 7.x, this package has changed in size and utility.
michael@516 | 1 | #!/usr/bin/perl -w |
michael@516 | 2 | # |
michael@516 | 3 | # Tool to send git commit notifications |
michael@516 | 4 | # |
michael@516 | 5 | # Copyright 2005 Alexandre Julliard |
michael@516 | 6 | # |
michael@516 | 7 | # This program is free software; you can redistribute it and/or |
michael@516 | 8 | # modify it under the terms of the GNU General Public License as |
michael@516 | 9 | # published by the Free Software Foundation; either version 2 of |
michael@516 | 10 | # the License, or (at your option) any later version. |
michael@516 | 11 | # |
michael@516 | 12 | # |
michael@516 | 13 | # This script is meant to be called from .git/hooks/post-receive. |
michael@516 | 14 | # |
michael@516 | 15 | # Usage: git-notify [options] [--] old-sha1 new-sha1 refname |
michael@516 | 16 | # |
michael@516 | 17 | # -c name Send CIA notifications under specified project name |
michael@516 | 18 | # -m addr Send mail notifications to specified address |
michael@516 | 19 | # -n max Set max number of individual mails to send |
michael@516 | 20 | # -r name Set the git repository name |
michael@516 | 21 | # -s bytes Set the maximum diff size in bytes (-1 for no limit) |
michael@516 | 22 | # -u url Set the URL to the gitweb browser |
michael@516 | 23 | # -i branch If at least one -i is given, report only for specified branches |
michael@516 | 24 | # -x branch Exclude changes to the specified branch from reports |
michael@516 | 25 | # -X Exclude merge commits |
michael@516 | 26 | # |
michael@516 | 27 | |
michael@516 | 28 | use strict; |
michael@516 | 29 | use open ':utf8'; |
michael@516 | 30 | use Encode 'encode'; |
michael@516 | 31 | use Cwd 'realpath'; |
michael@516 | 32 | |
michael@516 | 33 | binmode STDIN, ':utf8'; |
michael@516 | 34 | binmode STDOUT, ':utf8'; |
michael@516 | 35 | |
michael@516 | 36 | sub git_config($); |
michael@516 | 37 | sub get_repos_name(); |
michael@516 | 38 | |
michael@516 | 39 | # some parameters you may want to change |
michael@516 | 40 | |
michael@516 | 41 | # CIA notification address |
michael@516 | 42 | my $cia_address = "cia\@cia.navi.cx"; |
michael@516 | 43 | |
michael@516 | 44 | # debug mode |
michael@516 | 45 | my $debug = 0; |
michael@516 | 46 | |
michael@516 | 47 | # configuration parameters |
michael@516 | 48 | |
michael@516 | 49 | # base URL of the gitweb repository browser (can be set with the -u option) |
michael@516 | 50 | my $gitweb_url = git_config( "notify.baseurl" ); |
michael@516 | 51 | |
michael@516 | 52 | # default repository name (can be changed with the -r option) |
michael@516 | 53 | my $repos_name = git_config( "notify.repository" ) || get_repos_name(); |
michael@516 | 54 | |
michael@516 | 55 | # max size of diffs in bytes (can be changed with the -s option) |
michael@516 | 56 | my $max_diff_size = git_config( "notify.maxdiff" ) || 10000; |
michael@516 | 57 | |
michael@516 | 58 | # address for mail notices (can be set with -m option) |
michael@516 | 59 | my $commitlist_address = git_config( "notify.mail" ); |
michael@516 | 60 | |
michael@516 | 61 | # project name for CIA notices (can be set with -c option) |
michael@516 | 62 | my $cia_project_name = git_config( "notify.cia" ); |
michael@516 | 63 | |
michael@516 | 64 | # max number of individual notices before falling back to a single global notice (can be set with -n option) |
michael@516 | 65 | my $max_individual_notices = git_config( "notify.maxnotices" ) || 100; |
michael@516 | 66 | |
michael@516 | 67 | # branches to include |
michael@516 | 68 | my @include_list = split /\s+/, git_config( "notify.include" ) || ""; |
michael@516 | 69 | |
michael@516 | 70 | # branches to exclude |
michael@516 | 71 | my @exclude_list = split /\s+/, git_config( "notify.exclude" ) || ""; |
michael@516 | 72 | |
michael@516 | 73 | # set this to something that takes "-s" |
michael@516 | 74 | my $mailer = git_config( "notify.mailer" ) || "/usr/bin/mail"; |
michael@516 | 75 | |
michael@516 | 76 | # Extra options to git rev-list |
michael@516 | 77 | my @revlist_options; |
michael@516 | 78 | |
michael@516 | 79 | sub usage() |
michael@516 | 80 | { |
michael@516 | 81 | print "Usage: $0 [options] [--] old-sha1 new-sha1 refname\n"; |
michael@516 | 82 | print " -c name Send CIA notifications under specified project name\n"; |
michael@516 | 83 | print " -m addr Send mail notifications to specified address\n"; |
michael@516 | 84 | print " -n max Set max number of individual mails to send\n"; |
michael@516 | 85 | print " -r name Set the git repository name\n"; |
michael@516 | 86 | print " -s bytes Set the maximum diff size in bytes (-1 for no limit)\n"; |
michael@516 | 87 | print " -u url Set the URL to the gitweb browser\n"; |
michael@516 | 88 | print " -i branch If at least one -i is given, report only for specified branches\n"; |
michael@516 | 89 | print " -x branch Exclude changes to the specified branch from reports\n"; |
michael@516 | 90 | print " -X Exclude merge commits\n"; |
michael@516 | 91 | exit 1; |
michael@516 | 92 | } |
michael@516 | 93 | |
michael@516 | 94 | sub xml_escape($) |
michael@516 | 95 | { |
michael@516 | 96 | my $str = shift; |
michael@516 | 97 | $str =~ s/&/&/g; |
michael@516 | 98 | $str =~ s/</</g; |
michael@516 | 99 | $str =~ s/>/>/g; |
michael@516 | 100 | my @chars = unpack "U*", $str; |
michael@516 | 101 | $str = join "", map { ($_ > 127) ? sprintf "&#%u;", $_ : chr($_); } @chars; |
michael@516 | 102 | return $str; |
michael@516 | 103 | } |
michael@516 | 104 | |
michael@516 | 105 | # format an integer date + timezone as string |
michael@516 | 106 | # algorithm taken from git's date.c |
michael@516 | 107 | sub format_date($$) |
michael@516 | 108 | { |
michael@516 | 109 | my ($time,$tz) = @_; |
michael@516 | 110 | |
michael@516 | 111 | if ($tz < 0) |
michael@516 | 112 | { |
michael@516 | 113 | my $minutes = (-$tz / 100) * 60 + (-$tz % 100); |
michael@516 | 114 | $time -= $minutes * 60; |
michael@516 | 115 | } |
michael@516 | 116 | else |
michael@516 | 117 | { |
michael@516 | 118 | my $minutes = ($tz / 100) * 60 + ($tz % 100); |
michael@516 | 119 | $time += $minutes * 60; |
michael@516 | 120 | } |
michael@516 | 121 | return gmtime($time) . sprintf " %+05d", $tz; |
michael@516 | 122 | } |
michael@516 | 123 | |
michael@516 | 124 | # fetch a parameter from the git config file |
michael@516 | 125 | sub git_config($) |
michael@516 | 126 | { |
michael@516 | 127 | my ($param) = @_; |
michael@516 | 128 | |
michael@516 | 129 | open CONFIG, "-|" or exec "git", "config", $param; |
michael@516 | 130 | my $ret = <CONFIG>; |
michael@516 | 131 | chomp $ret if $ret; |
michael@516 | 132 | close CONFIG or $ret = undef; |
michael@516 | 133 | return $ret; |
michael@516 | 134 | } |
michael@516 | 135 | |
michael@516 | 136 | # parse command line options |
michael@516 | 137 | sub parse_options() |
michael@516 | 138 | { |
michael@516 | 139 | while (@ARGV && $ARGV[0] =~ /^-/) |
michael@516 | 140 | { |
michael@516 | 141 | my $arg = shift @ARGV; |
michael@516 | 142 | |
michael@516 | 143 | if ($arg eq '--') { last; } |
michael@516 | 144 | elsif ($arg eq '-c') { $cia_project_name = shift @ARGV; } |
michael@516 | 145 | elsif ($arg eq '-m') { $commitlist_address = shift @ARGV; } |
michael@516 | 146 | elsif ($arg eq '-n') { $max_individual_notices = shift @ARGV; } |
michael@516 | 147 | elsif ($arg eq '-r') { $repos_name = shift @ARGV; } |
michael@516 | 148 | elsif ($arg eq '-s') { $max_diff_size = shift @ARGV; } |
michael@516 | 149 | elsif ($arg eq '-u') { $gitweb_url = shift @ARGV; } |
michael@516 | 150 | elsif ($arg eq '-i') { push @include_list, shift @ARGV; } |
michael@516 | 151 | elsif ($arg eq '-x') { push @exclude_list, shift @ARGV; } |
michael@516 | 152 | elsif ($arg eq '-X') { push @revlist_options, "--no-merges"; } |
michael@516 | 153 | elsif ($arg eq '-d') { $debug++; } |
michael@516 | 154 | else { usage(); } |
michael@516 | 155 | } |
michael@516 | 156 | if (@ARGV && $#ARGV != 2) { usage(); } |
michael@516 | 157 | @exclude_list = map { "^$_"; } @exclude_list; |
michael@516 | 158 | } |
michael@516 | 159 | |
michael@516 | 160 | # send an email notification |
michael@516 | 161 | sub mail_notification($$$@) |
michael@516 | 162 | { |
michael@516 | 163 | my ($name, $subject, $content_type, @text) = @_; |
michael@516 | 164 | $subject = encode("MIME-Q",$subject); |
michael@516 | 165 | if ($debug) |
michael@516 | 166 | { |
michael@516 | 167 | print "---------------------\n"; |
michael@516 | 168 | print "To: $name\n"; |
michael@516 | 169 | print "Subject: $subject\n"; |
michael@516 | 170 | print "Content-Type: $content_type\n"; |
michael@516 | 171 | print "\n", join("\n", @text), "\n"; |
michael@516 | 172 | } |
michael@516 | 173 | else |
michael@516 | 174 | { |
michael@516 | 175 | my $pid = open MAIL, "|-"; |
michael@516 | 176 | return unless defined $pid; |
michael@516 | 177 | if (!$pid) |
michael@516 | 178 | { |
michael@516 | 179 | exec $mailer, "-s", $subject, "-a", "Content-Type: $content_type", $name or die "Cannot exec $mailer"; |
michael@516 | 180 | } |
michael@516 | 181 | print MAIL join("\n", @text), "\n"; |
michael@516 | 182 | close MAIL; |
michael@516 | 183 | } |
michael@516 | 184 | } |
michael@516 | 185 | |
michael@516 | 186 | # get the default repository name |
michael@516 | 187 | sub get_repos_name() |
michael@516 | 188 | { |
michael@516 | 189 | my $dir = `git rev-parse --git-dir`; |
michael@516 | 190 | chomp $dir; |
michael@516 | 191 | my $repos = realpath($dir); |
michael@516 | 192 | $repos =~ s/(.*?)((\.git\/)?\.git)$/$1/; |
michael@516 | 193 | $repos =~ s/(.*)\/([^\/]+)\/?$/$2/; |
michael@516 | 194 | return $repos; |
michael@516 | 195 | } |
michael@516 | 196 | |
michael@516 | 197 | # extract the information from a commit or tag object and return a hash containing the various fields |
michael@516 | 198 | sub get_object_info($) |
michael@516 | 199 | { |
michael@516 | 200 | my $obj = shift; |
michael@516 | 201 | my %info = (); |
michael@516 | 202 | my @log = (); |
michael@516 | 203 | my $do_log = 0; |
michael@516 | 204 | |
michael@516 | 205 | open TYPE, "-|" or exec "git", "cat-file", "-t", $obj or die "cannot run git-cat-file"; |
michael@516 | 206 | my $type = <TYPE>; |
michael@516 | 207 | chomp $type; |
michael@516 | 208 | close TYPE; |
michael@516 | 209 | |
michael@516 | 210 | open OBJ, "-|" or exec "git", "cat-file", $type, $obj or die "cannot run git-cat-file"; |
michael@516 | 211 | while (<OBJ>) |
michael@516 | 212 | { |
michael@516 | 213 | chomp; |
michael@516 | 214 | if ($do_log) |
michael@516 | 215 | { |
michael@516 | 216 | last if /^-----BEGIN PGP SIGNATURE-----/; |
michael@516 | 217 | push @log, $_; |
michael@516 | 218 | } |
michael@516 | 219 | elsif (/^(author|committer|tagger) ((.*)(<.*>)) (\d+) ([+-]\d+)$/) |
michael@516 | 220 | { |
michael@516 | 221 | $info{$1} = $2; |
michael@516 | 222 | $info{$1 . "_name"} = $3; |
michael@516 | 223 | $info{$1 . "_email"} = $4; |
michael@516 | 224 | $info{$1 . "_date"} = $5; |
michael@516 | 225 | $info{$1 . "_tz"} = $6; |
michael@516 | 226 | } |
michael@516 | 227 | elsif (/^tag (.*)$/) |
michael@516 | 228 | { |
michael@516 | 229 | $info{"tag"} = $1; |
michael@516 | 230 | } |
michael@516 | 231 | elsif (/^$/) { $do_log = 1; } |
michael@516 | 232 | } |
michael@516 | 233 | close OBJ; |
michael@516 | 234 | |
michael@516 | 235 | $info{"type"} = $type; |
michael@516 | 236 | $info{"log"} = \@log; |
michael@516 | 237 | return %info; |
michael@516 | 238 | } |
michael@516 | 239 | |
michael@516 | 240 | # send a commit notice to a mailing list |
michael@516 | 241 | sub send_commit_notice($$) |
michael@516 | 242 | { |
michael@516 | 243 | my ($ref,$obj) = @_; |
michael@516 | 244 | my %info = get_object_info($obj); |
michael@516 | 245 | my @notice = (); |
michael@516 | 246 | my $subject; |
michael@516 | 247 | |
michael@516 | 248 | if ($info{"type"} eq "tag") |
michael@516 | 249 | { |
michael@516 | 250 | push @notice, |
michael@516 | 251 | "Module: $repos_name", |
michael@516 | 252 | "Branch: $ref", |
michael@516 | 253 | "Tag: $obj", |
michael@516 | 254 | $gitweb_url ? "URL: $gitweb_url/?a=tag;h=$obj\n" : "", |
michael@516 | 255 | "Tagger: " . $info{"tagger"}, |
michael@516 | 256 | "Date: " . format_date($info{"tagger_date"},$info{"tagger_tz"}), |
michael@516 | 257 | "", |
michael@516 | 258 | join "\n", @{$info{"log"}}; |
michael@516 | 259 | $subject = "Tag " . $info{"tag"} . " : " . $info{"tagger_name"} . ": " . ${$info{"log"}}[0]; |
michael@516 | 260 | } |
michael@516 | 261 | else |
michael@516 | 262 | { |
michael@516 | 263 | push @notice, |
michael@516 | 264 | "Module: $repos_name", |
michael@516 | 265 | "Branch: $ref", |
michael@516 | 266 | "Commit: $obj", |
michael@516 | 267 | $gitweb_url ? "URL: $gitweb_url/?a=commit;h=$obj\n" : "", |
michael@516 | 268 | "Author: " . $info{"author"}, |
michael@516 | 269 | "Date: " . format_date($info{"author_date"},$info{"author_tz"}), |
michael@516 | 270 | "", |
michael@516 | 271 | join "\n", @{$info{"log"}}, |
michael@516 | 272 | "", |
michael@516 | 273 | "---", |
michael@516 | 274 | ""; |
michael@516 | 275 | |
michael@516 | 276 | open STAT, "-|" or exec "git", "diff-tree", "--stat", "-M", "--no-commit-id", $obj or die "cannot exec git-diff-tree"; |
michael@516 | 277 | push @notice, join("", <STAT>); |
michael@516 | 278 | close STAT; |
michael@516 | 279 | |
michael@516 | 280 | open DIFF, "-|" or exec "git", "diff-tree", "-p", "-M", "--no-commit-id", $obj or die "cannot exec git-diff-tree"; |
michael@516 | 281 | my $diff = join( "", <DIFF> ); |
michael@516 | 282 | close DIFF; |
michael@516 | 283 | |
michael@516 | 284 | if (($max_diff_size == -1) || (length($diff) < $max_diff_size)) |
michael@516 | 285 | { |
michael@516 | 286 | push @notice, $diff; |
michael@516 | 287 | } |
michael@516 | 288 | else |
michael@516 | 289 | { |
michael@516 | 290 | push @notice, "Diff: $gitweb_url/?a=commitdiff;h=$obj" if $gitweb_url; |
michael@516 | 291 | } |
michael@516 | 292 | |
michael@516 | 293 | $subject = $info{"author_name"} . ": " . ${$info{"log"}}[0]; |
michael@516 | 294 | } |
michael@516 | 295 | |
michael@516 | 296 | mail_notification($commitlist_address, $subject, "text/plain; charset=UTF-8", @notice); |
michael@516 | 297 | } |
michael@516 | 298 | |
michael@516 | 299 | # send a commit notice to the CIA server |
michael@516 | 300 | sub send_cia_notice($$) |
michael@516 | 301 | { |
michael@516 | 302 | my ($ref,$commit) = @_; |
michael@516 | 303 | my %info = get_object_info($commit); |
michael@516 | 304 | my @cia_text = (); |
michael@516 | 305 | |
michael@516 | 306 | return if $info{"type"} ne "commit"; |
michael@516 | 307 | |
michael@516 | 308 | push @cia_text, |
michael@516 | 309 | "<message>", |
michael@516 | 310 | " <generator>", |
michael@516 | 311 | " <name>git-notify script for CIA</name>", |
michael@516 | 312 | " </generator>", |
michael@516 | 313 | " <source>", |
michael@516 | 314 | " <project>" . xml_escape($cia_project_name) . "</project>", |
michael@516 | 315 | " <module>" . xml_escape($repos_name) . "</module>", |
michael@516 | 316 | " <branch>" . xml_escape($ref). "</branch>", |
michael@516 | 317 | " </source>", |
michael@516 | 318 | " <body>", |
michael@516 | 319 | " <commit>", |
michael@516 | 320 | " <revision>" . substr($commit,0,10) . "</revision>", |
michael@516 | 321 | " <author>" . xml_escape($info{"author"}) . "</author>", |
michael@516 | 322 | " <log>" . xml_escape(join "\n", @{$info{"log"}}) . "</log>", |
michael@516 | 323 | " <files>"; |
michael@516 | 324 | |
michael@516 | 325 | open COMMIT, "-|" or exec "git", "diff-tree", "--name-status", "-r", "-M", $commit or die "cannot run git-diff-tree"; |
michael@516 | 326 | while (<COMMIT>) |
michael@516 | 327 | { |
michael@516 | 328 | chomp; |
michael@516 | 329 | if (/^([AMD])\t(.*)$/) |
michael@516 | 330 | { |
michael@516 | 331 | my ($action, $file) = ($1, $2); |
michael@516 | 332 | my %actions = ( "A" => "add", "M" => "modify", "D" => "remove" ); |
michael@516 | 333 | next unless defined $actions{$action}; |
michael@516 | 334 | push @cia_text, " <file action=\"$actions{$action}\">" . xml_escape($file) . "</file>"; |
michael@516 | 335 | } |
michael@516 | 336 | elsif (/^R\d+\t(.*)\t(.*)$/) |
michael@516 | 337 | { |
michael@516 | 338 | my ($old, $new) = ($1, $2); |
michael@516 | 339 | push @cia_text, " <file action=\"rename\" to=\"" . xml_escape($new) . "\">" . xml_escape($old) . "</file>"; |
michael@516 | 340 | } |
michael@516 | 341 | } |
michael@516 | 342 | close COMMIT; |
michael@516 | 343 | |
michael@516 | 344 | push @cia_text, |
michael@516 | 345 | " </files>", |
michael@516 | 346 | $gitweb_url ? " <url>" . xml_escape("$gitweb_url/?a=commit;h=$commit") . "</url>" : "", |
michael@516 | 347 | " </commit>", |
michael@516 | 348 | " </body>", |
michael@516 | 349 | " <timestamp>" . $info{"author_date"} . "</timestamp>", |
michael@516 | 350 | "</message>"; |
michael@516 | 351 | |
michael@516 | 352 | mail_notification($cia_address, "DeliverXML", "text/xml", @cia_text); |
michael@516 | 353 | } |
michael@516 | 354 | |
michael@516 | 355 | # send a global commit notice when there are too many commits for individual mails |
michael@516 | 356 | sub send_global_notice($$$) |
michael@516 | 357 | { |
michael@516 | 358 | my ($ref, $old_sha1, $new_sha1) = @_; |
michael@516 | 359 | my @notice = (); |
michael@516 | 360 | |
michael@516 | 361 | push @revlist_options, "--pretty"; |
michael@516 | 362 | open LIST, "-|" or exec "git", "rev-list", @revlist_options, "^$old_sha1", "$new_sha1", @exclude_list or die "cannot exec git-rev-list"; |
michael@516 | 363 | while (<LIST>) |
michael@516 | 364 | { |
michael@516 | 365 | chomp; |
michael@516 | 366 | s/^commit /URL: $gitweb_url\/?a=commit;h=/ if $gitweb_url; |
michael@516 | 367 | push @notice, $_; |
michael@516 | 368 | } |
michael@516 | 369 | close LIST; |
michael@516 | 370 | |
michael@516 | 371 | mail_notification($commitlist_address, "New commits on branch $ref", "text/plain; charset=UTF-8", @notice); |
michael@516 | 372 | } |
michael@516 | 373 | |
michael@516 | 374 | # send all the notices |
michael@516 | 375 | sub send_all_notices($$$) |
michael@516 | 376 | { |
michael@516 | 377 | my ($old_sha1, $new_sha1, $ref) = @_; |
michael@516 | 378 | |
michael@516 | 379 | $ref =~ s/^refs\/heads\///; |
michael@516 | 380 | |
michael@516 | 381 | return if (@include_list && !grep {$_ eq $ref} @include_list); |
michael@516 | 382 | |
michael@516 | 383 | if ($old_sha1 eq '0' x 40) # new ref |
michael@516 | 384 | { |
michael@516 | 385 | send_commit_notice( $ref, $new_sha1 ) if $commitlist_address; |
michael@516 | 386 | return; |
michael@516 | 387 | } |
michael@516 | 388 | |
michael@516 | 389 | my @commits = (); |
michael@516 | 390 | |
michael@516 | 391 | open LIST, "-|" or exec "git", "rev-list", @revlist_options, "^$old_sha1", "$new_sha1", @exclude_list or die "cannot exec git-rev-list"; |
michael@516 | 392 | while (<LIST>) |
michael@516 | 393 | { |
michael@516 | 394 | chomp; |
michael@516 | 395 | die "invalid commit $_" unless /^[0-9a-f]{40}$/; |
michael@516 | 396 | unshift @commits, $_; |
michael@516 | 397 | } |
michael@516 | 398 | close LIST; |
michael@516 | 399 | |
michael@516 | 400 | if (@commits > $max_individual_notices) |
michael@516 | 401 | { |
michael@516 | 402 | send_global_notice( $ref, $old_sha1, $new_sha1 ) if $commitlist_address; |
michael@516 | 403 | return; |
michael@516 | 404 | } |
michael@516 | 405 | |
michael@516 | 406 | foreach my $commit (@commits) |
michael@516 | 407 | { |
michael@516 | 408 | send_commit_notice( $ref, $commit ) if $commitlist_address; |
michael@516 | 409 | send_cia_notice( $ref, $commit ) if $cia_project_name; |
michael@516 | 410 | } |
michael@516 | 411 | } |
michael@516 | 412 | |
michael@516 | 413 | parse_options(); |
michael@516 | 414 | |
michael@516 | 415 | # append repository path to URL |
michael@516 | 416 | $gitweb_url .= "/$repos_name.git" if $gitweb_url; |
michael@516 | 417 | |
michael@516 | 418 | if (@ARGV) |
michael@516 | 419 | { |
michael@516 | 420 | send_all_notices( $ARGV[0], $ARGV[1], $ARGV[2] ); |
michael@516 | 421 | } |
michael@516 | 422 | else # read them from stdin |
michael@516 | 423 | { |
michael@516 | 424 | while (<>) |
michael@516 | 425 | { |
michael@516 | 426 | chomp; |
michael@516 | 427 | if (/^([0-9a-f]{40}) ([0-9a-f]{40}) (.*)$/) { send_all_notices( $1, $2, $3 ); } |
michael@516 | 428 | } |
michael@516 | 429 | } |
michael@516 | 430 | |
michael@516 | 431 | exit 0; |