Tweak print/hplip version 3.16.11_2
[dports.git] / Tools / scripts / addport
1 #!/usr/bin/env perl
2 #
3 # addport - perl script that adds new ports to the
4 # FreeBSD Ports Collection.  Replaces easy-import.
5 #
6 # Copyright (c) 2000 Will Andrews and Michael Haro
7 # All rights reserved.
8 #
9 # Redistribution and use in source and binary forms, with or without
10 # modification, are permitted provided that the following conditions
11 # are met:
12 # 1. Redistributions of source code must retain the above copyright
13 #    notice, this list of conditions and the following disclaimer.
14 # 2. Redistributions in binary form must reproduce the above copyright
15 #    notice, this list of conditions and the following disclaimer in the
16 #    documentation and/or other materials provided with the distribution.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 # SUCH DAMAGE.
29 #
30 # Original shell script & idea: Will Andrews <will@FreeBSD.org>
31 # Original conversion to Perl : Michael Haro <mharo@FreeBSD.org>
32 # Maintainer alumni           : Renato Botelho <garga@FreeBSD.org>
33 #
34 # Id: addport,v 1.2 2000/04/02 06:21:13 will Exp (original shell script)
35 # Id: addport,v 1.5 2000/04/22 22:19:43 mharo Exp (perl conversion)
36 # $FreeBSD$
37 #
38 # MAINTAINER=   crees@FreeBSD.org
39 #
40
41 # Smartmatch operator (~~) used
42 require 5.10.1;
43
44 use Cwd "abs_path";
45 use Getopt::Std;
46 use Sys::Hostname;
47 use locale;
48 use strict;
49
50 # Subroutine prototypes
51 sub warnx($);
52 sub err($$);
53 sub errx($$);
54 sub prompt($);
55 sub query($);
56 sub usage();
57 sub contains($@);
58 sub lsports();
59 sub lastcomment();
60
61 my %opts;
62
63 getopts('ac:d:fh:il:L:mns:tu:y', \%opts);
64
65 my $autofill_l = $opts{'l'};
66 my $autofill_L = $opts{'L'};
67 my $autofill = ($autofill_l ? $autofill_l : $autofill_L);
68 my $c = $opts{'c'} if ($opts{'c'} ne "");
69 my $distdir = $opts{'s'} if ($opts{'s'} ne "");
70 my $dir = $opts{'d'};
71 my $h = "repo.FreeBSD.org";
72 $h = $opts{'h'} if ($opts{'h'} ne "");
73 my $u = $ENV{USER};
74 $u = $opts{'u'} if ($opts{'u'} ne "");
75 my $more_testing = $opts{'t'};
76 my $interactive = $opts{'i'};
77 my $nomkdir = $opts{'m'};
78 my $addlchk = $opts{'a'};
79 my $nofetch = $opts{'f'};
80 my $checkexist = $opts{'y'};
81 my $currentdir = abs_path(".");
82 my %l10nprefix = (
83         'chinese'       => 'zh-',
84         'french'        => 'fr-',
85         'german'        => 'de-',
86         'hebrew'        => 'iw-',
87         'hungarian'     => 'hu-',
88         'japanese'      => 'ja-',
89         'korean'        => 'ko-',
90         'portuguese'    => 'pt-',
91         'russian'       => 'ru-',
92         'ukrainian'     => 'uk-',
93         'vietnamese'    => 'vi-',
94 );
95
96 my $tmpdir;
97 my $repo;
98 my $portsdir = $ENV{PORTSDIR} ? $ENV{PORTSDIR} : '/usr/ports';
99 my $repo = $ENV{ADDPSVNROOT}? $ENV{ADDPSVNROOT} : "svn+ssh://$u\@$h/ports/head";
100 my $make = "make";
101 my $portlint = `which portlint`; chomp $portlint;
102 my $plint_args = "-N -a -c -t";
103 my $perl = "perl";
104 my $cp = "cp";
105 my $mv = "mv";
106 my $rm = "rm";
107 chomp(my $svnlite = `which svnlite`);
108 my $svn = ($svnlite ? $svnlite : "svn");
109 my $keyword = '\$FreeBSD\\\$';
110 # vars required for commitfile
111 my $descr; my $portversion; my $pkgcomment;
112 my $tmp; my $pkgcommentlen; my $comment; my $maintainer = "";
113 my $maintaineraddr; my $tmp2; my $offset; my $commitfile = "";
114 my $moved = "";
115 $tmp = $tmp2 = $offset = 0;
116
117 # Check the editor.
118 my $edit = "/usr/bin/vi";
119 $edit = $ENV{EDITOR} if ($ENV{EDITOR} ne "");
120
121 # Check svn version
122 my $svnversion = `$svn --version --quiet 2>/dev/null`;
123 chomp $svnversion;
124 if ($svnversion eq "") {
125         errx(1, "Subversion binary not found in \$PATH, aborting.");
126 }
127 my @svnversion = split(/\./, $svnversion);
128 if ($svnversion[0] == 1 && $svnversion[1] < 7) {
129         errx(1, "minimum Subversion version of 1.7 not met, aborting.");
130 }
131
132 # stuff that always happens when we start
133 BEGIN {
134         $tmpdir=`mktemp -d -t ap`;
135         chomp $tmpdir;
136         if ($tmpdir eq "") {
137                 errx(1,"making random tmpdir didn't work, aborting.");
138         }
139 }
140
141 # stuff that always happens when we exit
142 END {
143         # only remove $tmpdir if it points to something in /tmp
144         # this is a silly little security thing
145         if (defined($rm) && defined($tmpdir)) {
146                 system("$rm -rf $tmpdir") if ($tmpdir =~ m,/tmp/,);
147         }
148 }
149
150 # setup the list of commands to run on the new port(s).
151 my @commands;
152 my $passenv = "";
153 if ($addlchk && -f $portlint) {
154         $passenv = "DISTDIR=\"$distdir\"" if -d $distdir;
155         $passenv = $passenv . " PORTSDIR=\"$portsdir\"" if !$nomkdir;
156         push(@commands, "$make $passenv clean check-categories");
157         push(@commands, "$portlint $plint_args");
158         push(@commands, "$make $passenv FETCH_BEFORE_ARGS='-A' checksum") if !$nofetch;
159         if ($more_testing) {
160                 push(@commands, "$make $passenv distclean");
161                 push(@commands, "$make $passenv build");
162                 push(@commands, "$make $passenv distclean");
163         }
164         if (!$nomkdir) {
165                 chdir $tmpdir;
166                 print "Checking out Mk directory to ensure portlint correctness.\n";
167                 system("$svn co $repo/Mk Mk") && errx(1, "Could not checkout Mk directory");
168                 system("$svn co $repo/Templates Templates") && errx(1, "Could not checkout Templates directory");
169                 chdir $currentdir;
170         }
171 }
172
173 if ($dir eq "") {
174         warnx("Need to specify a directory with -d argument!");
175         usage();
176         exit 1;
177 }
178
179 # check the port doesn't exist already
180
181 if ($checkexist) {
182         my $found = 0;
183         print ">> Fetching INDEX to scan for duplicates...\n";
184         my $indexfile = "$tmpdir/" . `$make -C $portsdir -V INDEXFILE`;
185         system("$make -C /usr/ports INDEXDIR=$tmpdir fetchindex") && errx(1, "Could not fetch INDEX file.");
186         my @namepart;
187         foreach (split(/\,/, $dir)) {
188                 s/^.*\///;
189                 foreach (split(/[-_]/)) {
190                         next if length () <=2 or /^rubygem$/;
191                         push(@namepart, $_);
192                 }
193         }
194         open(INDEXFILE, "< $indexfile") or errx(1, "$indexfile unreadable.");
195         while (my $line = <INDEXFILE>) {
196                 $line =~ m,^[^|]*\|/usr/ports/[^/]*/([^|]*)\|,;
197                 $line = $1;
198                 foreach my $dpart (@namepart) {
199                         if ($line =~ /^[^ ]*\b$dpart\b/i) {
200                                 $line =~ s/\s+/ /g;
201                                 print "$dpart matches $line\n";
202                                 $found = 1;
203                         }
204                 }
205         }
206         if ($found) {
207                 prompt ("Possible duplicates found -- still OK?")
208                         and errx(1, "Investigating duplicates");
209         }
210 }
211
212 # make sure we're in the right place.
213 chdir $currentdir;
214 my @dirs = split(/\,/, $dir);
215 foreach my $i (@dirs) { $i = abs_path($i); }
216 my $portname; my $wrapat;
217 foreach my $thisdir (@dirs) {
218         # make double sure where we are..
219         chdir $thisdir;
220         # do some dir sanity checking first
221         errx(1, "Please specify valid directories to import new ports from.") if $thisdir eq "";
222         errx(1, "$thisdir is either not a directory or does not exist.") if (! -d $thisdir);
223
224         print "Working with port directory $thisdir.\n";
225
226         $portname = `basename $thisdir`;        # avoid problems with dirs containing `/' in cvs
227         chomp $portname;
228         warnx("Port directory contains upper-case character! Please try using an all lower-case name to make everybody's life a bit easier.") if ($portname =~ /[A-Z]/);
229         if ($interactive) {
230                 if (prompt("Port directory name will be $portname in SVN Repo.  OK? ")) {
231                         do {
232                                 $portname = query("Preferred name for port directory? ");
233                         } while (prompt("Is the new name $portname OK? "));
234                 }
235         }
236
237         chdir $thisdir or err(1, "$thisdir");
238
239         # now run the tests on this baby.
240         for (@commands) {
241                 system("$_") && errx(1, "'$_' had problems. aborting.");
242         }
243
244         # Get the category name and make it suitable for use with svn
245         my @categories = split(/ /, `$make -VCATEGORIES`);
246         my $category = $categories[0];
247         if ($interactive) {
248                 if (prompt("Port $portname will be put in category $category.  OK? " )) {
249                         do {
250                                 $category = query("Preferred category for $portname? ");
251                         } while (prompt("Is the new category $category OK? "));
252                 }
253         }
254         chomp $category;
255
256         # Do commitfile checking but only if the user did not request automatic filling.
257         if (!$autofill) {
258                 if (-f $c) {
259                         system("$mv $c $tmpdir/commitfile") && errx(1, "Oops, can't move commitfile!");
260                         print "\nRemember, you asked to use a commit file to read for the commit log.\n";
261                         print "This means you'll get a message saying the log message was unchanged or\n";
262                         print "not specified.  Just tell it to continue and it will be committed.\n\n";
263                         $commitfile = "--file $tmpdir/commitfile";
264                 }
265         } else {
266                 ## Set up the autofill file.
267                 # Read COMMENT for part of the commit message.
268                 if ($autofill_l) {
269                         chomp($pkgcomment = `$make $passenv -V COMMENT`);
270                         # Change the first character to lowercase to make it fit with the
271                         # rest of the commit message, only if the second is not upper case.
272                         $pkgcomment =~ s/(^.)(?![A-Z])/\l$1/;
273                         $pkgcomment .= ".";
274                         $pkgcomment .= "\n\n" if ($autofill != -1);
275                 } else {
276                         $pkgcomment = `cat pkg-descr`;
277                         $pkgcomment .= "\n" if ($autofill != -1);
278                 }
279                 chomp($maintaineraddr = `$make $passenv -V MAINTAINER`);
280                 chomp($portversion = `$make $passenv -V PORTVERSION`);
281                 # Read Makefile to find necessary variables.
282                 open(MAKEFILE, "Makefile") or die("Can't open Makefile for reading: $!");
283                 while(<MAKEFILE>) {
284                         chomp;
285                         die ("Old style Makefile headers detected") if (/^# (?:[Nn]ew )?[Pp]orts collection [Mm]akefile/);
286                         ($maintainer) = (m/^# Created by:\s+(\w.*)$/) if (/^# Created by/);
287                 }
288                 close(MAKEFILE);
289                 $maintainer = $maintaineraddr unless ($maintainer);
290                 # Write out the data to the comment file.
291                 open(AUTOFILL, "> $tmpdir/commitfile") or die("Can't open $tmpdir/commitfile for writing: $!");
292                 if ($autofill_l) {
293                         # pretty print; wrap @ 72 characters
294                         $tmp = "Add $portname $portversion, $pkgcomment";
295                         $wrapat = 72;
296                         while($wrapat > 1) {
297                                 $tmp2 = $tmp;
298                                 $tmp =~ s/(.{$wrapat}([^ ]+)?) /$1\n/g;
299                                 last unless $tmp =~ /^[^\n]{73}/;
300                                 $wrapat--;
301                                 $tmp = $tmp2;
302                         }
303                 } else {
304                         $tmp = $pkgcomment;
305                 }
306                 print AUTOFILL $tmp;
307                 print AUTOFILL "PR:             $autofill\n" if ($autofill != -1);
308                 print AUTOFILL "Submitted by:   $maintainer" if ($maintainer && $autofill != -1);
309                 close(AUTOFILL);
310                 print "Okay, a commit log message was automatically generated for you.\n";
311                 print "Now you will have a chance to edit it to make sure it's OK to use.\n";
312                 print "Here's the contents of the file:\n--start--\n";
313                 open(AUTOFILL, "$tmpdir/commitfile") or die("Can't open $tmpdir/commitfile for reading: $!");
314                 print while (<AUTOFILL>);
315                 close(AUTOFILL);
316                 $tmp = prompt("\n--end--\nDo you wish to edit the file before continuing? ");
317                 system("$edit $tmpdir/commitfile") if ($tmp == 0);
318                 print "\nRemember, you asked to use a commit file to read for the commit log.\n";
319                 print "This means you'll get a message saying the log message was unchanged or\n";
320                 print "not specified.  Just tell it to continue and it will be committed.\n\n";
321                 $commitfile = "--file $tmpdir/commitfile";
322         }
323
324         print "We're ready to commit.\n";
325         print "Source directory: $thisdir\n";
326         print "Target SVN Repo directory: ports/$category/$portname\n";
327         prompt("Adding port $portname to $category OK? ") && errx(1, "user abort requested");
328         
329         chdir $tmpdir or err(1, "$tmpdir");
330
331         # let's get our hands dirty.
332         if (! -d "ports") {
333                 system("$svn co --depth empty $repo ports") && errx(1, "can't get ports root, aborting.");
334                 chdir "ports" or err(1,"ports");
335                 system("$svn up --depth files $category") && errx(1, "can't get temporary category directory, aborting.");
336         }
337         chdir $category or err(1,"$category");
338
339         # check for previous existence of port -- on the way use filthy
340         # home-made XML parser.
341         # Until svn revs are in the database, we'll use dates.
342         print "Checking for previous versions of $category/$portname... \n";
343         my $previous_incarnation = "bogus";
344         my $oldportlist = `/usr/bin/fetch -qo - http://people.FreeBSD.org/~crees/removed_ports/index.xml`;
345         if ($oldportlist !=~ /^fetch:[^:]+: Not Found/) {
346                 foreach (split("\n", $oldportlist)) {
347                         if (/^ +\<port\>$category\/$portname(?:\<removed_revision\>r([0-9]*)\<\/removed_revision\>)?\<removed_date\>([^<]*)/) {
348                                 print "Found one!\n";
349                                 if ($1 == "") {
350                                         $previous_incarnation = $2;
351                                         $previous_incarnation =~ s,/,-,g;
352                                         print "This port was last alive on $previous_incarnation.\n";
353                                         $previous_incarnation = "\{$previous_incarnation\}";
354                                 } else {
355                                         $previous_incarnation = $1 - 1;
356                                         print "The last living revision of this port was r$previous_incarnation.\n";
357                                 }
358                                 last;
359                         }
360                 }
361         } else {
362                 print "Could not fetch list-- perhaps the site is down.";
363         }
364         if ($previous_incarnation ne "bogus") {
365                 print "Fetching older version... ";
366                 system("$svn cp -q '$repo/$category/$portname\@$previous_incarnation' .");
367                 print "[DONE]\n";
368                 print "Removing irrelevant files and directories... ";
369                 my @oldfiles = split("\0", `cd $portname && find . -type f -print0`);
370                 my @olddirs = split("\0", `cd $portname && find . -type d -print0 | sort -r`);
371                 my @newfiles = split("\0", `cd $thisdir && find . -type f -print0`);
372                 my @newdirs = split("\0", `cd $thisdir && find . -type d -print0| sort -r`);
373
374                 foreach my $file (@oldfiles) {
375                         system("cd $portname && $svn rm $file")
376                                 if !($file ~~ @newfiles)
377                 }
378
379                 foreach my $directory (@olddirs) {
380                         system("cd $portname && $svn rm $directory")
381                                 if !($directory ~~ @newdirs);
382                 }
383
384                 print "[DONE]\n";
385
386                 # Remove cvs2svn props if present
387                 print "Removing cvs2svn props...\n";
388                 system("$svn propdel -qR cvs2svn:cvs-rev $portname");
389
390                 $moved = "MOVED";
391                 print "Removing port's entry from $moved...\n";
392                 system("cd .. && $svn up -q $moved && sed -i '' -e '\\,^$category/$portname\|\|,d' $moved");
393
394                 # Add note to log about readdition
395                 system("echo '(Readdition of $category/$portname which was removed on $previous_incarnation)\n' > $tmpdir/commitfile.tmp && cat $tmpdir/commitfile >> $tmpdir/commitfile.tmp && mv $tmpdir/commitfile.tmp $tmpdir/commitfile") unless ($commitfile eq "");
396
397         } else {
398                 print "[none found]\n";
399         }
400         system("$cp -PRp $thisdir .");
401         system("$svn add --force --depth empty `find $portname -type d | grep -v '^$portname/work'`") && errx(1, "svn add for dirs failed, aborting.");
402
403         system("$svn add --force `find $portname -type f | grep -v '^$portname/work'`") && errx(1, "svn add for files failed, aborting.");
404
405         # Find keywords in old files and strip
406         print "Stripping any keywords...\n";
407         system("sed -i '' -e 's,\\\$Free" . "BSD:[^\$]*\\\$,\$Free" . "BSD\$,' \$(find $portname -type f)");
408
409         # find files with keywords in and propset
410
411         print "Setting correct properties on files...\n";
412
413         my @portfiles = split("\0", `find $portname -type f -print0`);
414         my $portfiles = join(" ", @portfiles);
415         my @keywordfiles = split("\n", `grep -l $keyword $portfiles`);
416         foreach (@portfiles) {
417                 if ($_ ~~ @keywordfiles) {
418                         system("$svn -q propset svn:keywords FreeBSD=%H $_");
419                         system("$svn -q propdel fbsd:nokeywords $_");
420                 } else {
421                         system("$svn -q propset fbsd:nokeywords on $_");
422                         system("$svn -q propdel svn:keywords $_");
423                 }
424         }
425
426         # strip svn:executable if added-- not allowed
427
428         system("cd $portname && $svn -qR propdel svn:executable");
429
430         # figure out where the port name belongs in category Makefile
431         my ($spaces, @ports) = &lsports;
432         errx(1, "Error: $portname already exists in $category\'s Makefile") if (&contains($portname, @ports));
433         my $port = "";
434         foreach my $tmp (sort(@ports)) {
435                 if ($tmp gt $portname) {
436                         $port = $tmp;
437                         last;
438                 }
439         }
440
441         # now let's insert it
442         my $cmd;
443         if (scalar @ports == 0) {
444                 # there were no previous SUBDIR += lines, so we're going to
445                 # put ourselves after the last comment (we can't be after a
446                 # .include <bsd.port.subdir.mk> for example).
447                 my $lastcommentnum = &lastcomment;
448                 $cmd = "$lastcommentnum\n+\ni\n";
449         } else {
450                 if ($port eq "") {
451                         # there were previous SUBDIR lines, but none was greater than we are,
452                         # means, we're the last port, so, add ourselves after the last port
453                         $port = $ports[$#ports];
454                         $cmd = "/^" . $spaces . "SUBDIR += $port/\na\n";
455                 } else {
456                         # OK, append ourselves in the right place, so things *stay* sorted.
457                         $cmd = "/^" . $spaces . "SUBDIR += $port/\ni\n";
458                 }
459         }
460         print "Inserting new port into $category/Makefile...\n";
461         open(ED, "|ed Makefile") || die "Cannot start ed to actually insert module\n";
462         print ED "$cmd" . $spaces . "SUBDIR += $portname\n.\nw\nq\n";
463                 close(ED);
464
465         # commit the actual port.
466         chdir "$tmpdir/ports" or err(1, "$tmpdir/ports");
467         if ($opts{'n'}) {
468                 print "Faking commit....\n";
469         } else {
470                 system("$svn ci $commitfile $moved $category/Makefile $category/$portname") && errx(1, "svn commit failed, aborting.");
471         }
472 }
473
474 print <<EOF;
475 You're done! The new port $portname has been completely imported in
476 the tree. Don't forget to add the creator's name and email address to
477 the Contributors' List if they are not already there. To do this, edit
478 doc/head/en_US.ISO8859-1/articles/contributors/contrib.additional.xml.
479 EOF
480
481 sub warnx($) {
482         my ($msg) = @_;
483         print STDERR $0 . ": " . $msg . "\n";
484 }
485
486 sub err($$) {
487         my ($ex, $msg) = @_;
488
489         warnx("WARNING: err called incorrectly") if (($ex !~ m/^\d+/) || ($msg eq ""));
490         print STDERR $0 . ": " . $msg . ": $!\n";
491         exit $ex;
492 }
493
494 sub errx($$) {
495         my ($ex,$msg) = @_;
496
497         warnx("WARNING: errx called incorrectly") if (($ex !~ m/^\d+/) || ($msg eq ""));
498         print STDERR $0 . ": " . $msg . "\n";
499         exit $ex;
500 }
501
502 sub prompt($) {
503         my ($msg) = @_;
504         my $reply = query($msg);
505         return 0 if ($reply =~ m/^[Yy]/);
506         return 1 if ($reply =~ m/^[Nn]/);
507 }
508
509 sub query($) {
510         my ($msg) = @_;
511
512         print "$msg";
513         my $reply = <>;
514         chomp $reply;
515         return $reply;
516 }
517
518 sub usage() {
519 #addport,v \$Revision: 1.21 $
520 print <<EOF;
521 authors: <will\@FreeBSD.org>, <mharo\@FreeBSD.org>
522
523 SYNOPSIS
524         $0 [-c commitfile] [-h host] [-l PR number | -L PR number]
525            [-s distdir] [-u user] [-afimnty] -d directory
526
527         Where "directory" contains the comma-delimited list
528         of root directories of new ports that you wish to
529         add to the Ports Collection.  The name of this directory
530         *WILL* matter in regards to the repository!
531
532 OPTIONS
533         -a              Perform checks on the port to make sure
534                         there are no problems.  Recommended.
535         -c file         Use file in place of normal log message.
536         -f              Do not fetch the distfile.
537         -h host         Use a svnhost besides repo.FreeBSD.org.
538         -i              Interactive mode; allow more control over
539                         where things are placed.
540         -l PR#          Attempts to autogenerate a commit message by
541                         reading the Makefile file.
542                         The PR number must be passed to -l.  If there is
543                         no PR (i.e., self-created or submitted in
544                         private email), use PR# -1.
545         -L PR#          Like -l but it'll generate commit message based
546                         on pkg-descr
547         -m              Do not checkout ports/Mk (needed for support
548                         of portlinting etc).
549         -n              Do not actually commit anything.
550         -s distdir      Use a different directory besides the default,
551                         for downloading distfiles.
552         -t              Do more port testing.  Requires -a.
553         -u user         Use a different username (default: $u).
554         -y              Check for similarly named ports.
555
556 ENVIRONMENT VARIABLES
557         $0 supports the following environment variables:
558
559         ADDPSVNROOT     - Location of SVN repository.
560         USER            - Username of user invoking $0.
561
562 EXAMPLES
563         % addport -n -d greatgame,helpfuldev,shoot
564                 Will show what happens but not actually commit ports
565         named "greatgame", "helpfuldev", and "shoot".
566
567         % addport
568                 Displays this message.  :-)
569
570 EOF
571 }
572
573 sub contains($@) {
574     # look if the first parameter is contained in the list following it
575     my ($item, @list) = @_;
576
577     foreach my $i (@list) {
578         return 1 if $i eq $item;
579     }
580     return 0;
581 }
582
583 sub lsports() {
584     my @rv = ();
585     my $spaces;
586
587     open(F, "Makefile") || die "can't open Makefile: $!";
588     while(<F>) {
589         chomp;
590         chomp;
591         next if $_ !~ m/SUBDIR/;
592
593         if ( !defined($spaces) ) {
594                 m/^([\s\t]+)[^\s\t]/;
595                 $spaces = $1;
596         }
597
598         s/^[ \t]+SUBDIR[ \t]+\+?=[\ \t]+//;
599         push(@rv, $_);
600     }
601     close(F);
602
603     return ($spaces, @rv);
604 }
605
606 # this finds the last comment in the Makefile
607 sub lastcomment() {
608         my $num = 0;
609         my $diff = 0;
610
611         open(F, "Makefile");
612         while(<F>) {
613                 chomp;
614                 if ($_ =~ m/^#/) {
615                         $num += $diff;
616                         $num++;
617                         $diff = 0;
618                 } else {
619                         $diff += 1;
620                 }
621                 next;
622         }
623         return $num;
624 }
625