Before the wide spread availability of Perl, I would script ftp transfers with .netrc, ksh scripts and other clumsy ways. None of those methods are fun, flexible or easy. On the other hand, Perl's Net::FTP module is all of that.
With Net::FTP, you have total control. You know when there are errors, timeouts, whatever. It's not at all difficult: anyone with basic scripting skills can understand and use this.
I'm going to present two programs here. One is very simple; you can probably understand it even if you know no Perl at all. It just logs into my ftp site, gets a listing, and displays it. The other is a fairly complicated program that goes out to a list of hosts and gets files with a date equal to or newer than what you specify. Even with the extra complexity, you should be able to follow it, and perhaps modify it for your own needs.
Here's the first:
#!/usr/bin/perl
use Net::FTP;
my $host="aplawrence.com";
my $directory="pub";
$ftp=Net::FTP->new($host,Timeout=>240) or $newerr=1;
push @ERRORS, "Can't ftp to $host: $!\n" if $newerr;
myerr() if $newerr;
print "Connected\n";
$ftp->login("ftp","apl\@") or $newerr=1;
print "Getting file list";
push @ERRORS, "Can't login to $host: $!\n" if $newerr;
$ftp->quit if $newerr;
myerr() if $newerr;
print "Logged in\n";
$ftp->cwd($directory) or $newerr=1;
push @ERRORS, "Can't cd $!\n" if $newerr;
myerr() if $newerr;
$ftp->quit if $newerr;
@files=$ftp->dir or $newerr=1;
push @ERRORS, "Can't get file list $!\n" if $newerr;
myerr() if $newerr;
print "Got file list\n";
foreach(@files) {
print "$_\n";
}
$ftp->quit;
sub myerr {
print "Error: \n";
print @ERRORS;
exit 0;
}
Pretty simple, right? Net::FTP makes it all so easy, so let's do something that would absolutely drive me batty without it.
#!/usr/bin/perl
use Net::FTP;
$date=shift @ARGV;
@months=qw(null Jan Feb Mar Apr My Jun Jul Aug Sep Oct Nov Dec);
@hosts=qw(pcunix.org pcunix.com xyz.com);
@dirs=qw(pub pub pub);
@logins=qw(ftp anonymous fred);
@passwords=qw(tony\@ apl\@ fxdfed);
$x=0;
foreach(@months) {
$months{$_}=$x++;
}
# we need this hash later
if (not $date) {
$now=time();
$now -= (24 * 3600 );
# yesterday
($nowsec,$nowmin,$nowhr,$nowday,$nowmon,$nowyr,$nowdow,$nowdoy,$nowdst)=localtime($now);
$nowyr+=1900;$nowmon++;
$date=sprintf("%.2d/%.2d/%.4d",$nowmon,$nowday,$nowyr);
print "Using $date\n";
}
$now=time();
($nowsec,$nowmin,$nowhr,$nowday,$nowmon,$nowyr,$nowdow,$nowdoy,$nowdst)=localtime($now);
$nowyr+=1900;
# need $nowyr later
($month,$day,$year)=split /\//,$date;
#
# broken next century - blame me then
#
$year+=2000 if $year < 100;
$x=0;
foreach (@hosts) {
$newerr=0;
$ftp=Net::FTP->new($_,Timeout=>240) or $newerr=1;
push @ERRORS, "Can't ftp to $_: $!\n" if $newerr;
next if $newerr;
print "Connected $_\n";
$ftp->login($logins[$x],$passwords[$x]) or $newerr=1;
push @ERRORS, "Can't login to $_: $!\n" if $newerr;
$ftp->quit if $newerr;
next if $newerr;
print "Logged in $_\n";
$ftp->cwd($dirs[$x]) or $newerr=1;
push @ERRORS, "Can't cd $dirs[$x] on $_ $!\n" if $newerr;
$ftp->quit if $newerr;
next if $newerr;
print "Getting file list $_\n";
@files=$ftp->dir or $newerr=1;
push @ERRORS, "Can't get file list on $_ $!\n" if $newerr;
$ftp->quit if $newerr;
next if $newerr;
print "Got list $_\n";
print "Looking for $date $time\n";
foreach(@files) {
$_=substr($_,41);
s/ */ /g;
s/^ *//g;
chomp;
@stuff=split / /;
# if it's today, the year slot will have time instead
# so make it this year
$stuff[2]=$nowyr if /:/;
$ftp->quit if ($stuff[2] < $year);
next if ($stuff[2] < $year);
$ftp->quit if ($months{$stuff[0]} < $month and $stuff[2] == $year);
next if ($months{$stuff[0]} < $month and $stuff[2] == $year);
$ftp->quit if ($stuff[0] < $day and $stuff[2] == $year and $months{$stuff[0]} == $month);
next if ($stuff[1] < $day and $stuff[2] == $year and $months{$stuff[0]} == $month);
print "Getting $_\n";
$ftp->get($stuff[3],$stuff[3]) or $newerr=1;
push @ERRORS, "Couldn't get $stuff[3] $!\n" if $newerr;
}
$ftp->quit;
}
print @ERRORS;
exit 0;
Enter your email address for automatic notification of new posts here
(be sure to whitelist 'feedburner.com' if you use spam filtering)
| Views for this page | ||||
|---|---|---|---|---|
| Today | This Week | This Month | This Year | Overall |
| 9 | 9 | 1,167 | 13,621 | 111,412 |
Have you tried Searching this site?
Unix/Linux/Mac OS X support by phone, email or on-site: Support Rates
This is a Unix/Linux resource website. It contains technical articles about Unix, Linux and general computing related subjects, opinion, news, help files, how-to's, tutorials and more. We appreciate comments and article submissions.

PerlNetFtp :
Article:
http://www.aplawrence.com/Unixart/perlnetftp.html
if you wanna get a simple list of files in a ftp dir, you should replace dir with ls (i.e. @files=$ftp->ls or print "Cannot get file listing\n).
--
Sure. but that produces a different list. In this example, we want to compare dates.
--TonyLawrence
--
How did you know what I needed? Thank you for being perversely helpful. I hope this won't encourage my already significantly deep lazy streak. . .
Sam
Coolz shiznit manz, wellz dunz
I tihnk I may join yur clan of wiki tips for editing
thank you very mutch... they wont gimme anythin else to do here aight
Sat Mar 12 15:45:47 2005: Subject: Timok
I was searching for information about net::ftp library for perl. Your article was very helpful. Now i can automate many task. Many tx
http://www.achsms.pl
Wed Mar 30 12:47:44 2005: Subject: A couple of glitches Jack
As listed on the page the script does not increment $x which means that it does not cycle through the remote directories properly. Adding an $x++; after the final $ftp->quit; fixes that.
The extra $ftp->quit s in the section where the script is checking the age of the file mean that the connection is dropped when the first old file is met and not reestablished before it attempts to download any files of the right age. Removing them seems to do no harm so I imagine that they are a hangover from some less general purpose script.
After fixing those I found this extremely valuable. Thank you Mr. Lawrence.
Wed Mar 30 13:33:45 2005: Subject: TonyLawrence
Sorry about that.. yes, this was taken from a more specific script, so yes, I must have left stuff in I shouldn't have :-)
Fri Apr 29 20:05:48 2005: Subject: date ftp gets anonymous
is the second example ftp supposed to get/download the files to the current directory where the perl script is located?
Fri Apr 29 21:12:24 2005: Subject: TonyLawrence
is the second example ftp supposed to get/download the files to the current directory where the perl script is located?
Wherever you are stting when you run it. If the script is /xyz/script.pl, you could
cd /tmp
/xyz/script.pl
and the files would be in /tmp
Thu May 12 20:11:15 2005: Subject: anonymous
Thank you, Tony. This was very helpful. - Rick Brandfass
Thu May 12 21:55:12 2005: Subject: TonyLawrence
I'm sure there's more than one Rick Brandfass in the world.. but I assume this is the same Rick that I see in person once a year or so?
Fri May 13 12:55:40 2005: Subject: anonymous
Yup, hope to see you soon. - Rick
Fri May 13 13:58:55 2005: Subject: anonymous
I tried your first script, but unfortunally it failed when it tried to get the file list.
Here is the error messages:
Connected
Getting file listLogged in
Can't locate object method "new" via package "Net::FTP::A" at /usr/share/lib/perl5/irix-n32/5.00405/IO/Socket.pm line 253.
Attempt to free unreferenced scalar during global destruction.
What do I miss here?
Roger
Fri May 13 14:35:12 2005: Subject: TonyLawrence
I don't know: did you cut and paste or type it yourself?
Mon May 16 06:38:58 2005: Subject: anonymous
By cut and paste...
Roger
Mon May 16 10:19:11 2005: Subject: TonyLawrence
Well, I just cut and pasted it and it works..
If you didn't have Net::Ftp you'd get "Can't locate Net/FTP.pm in @INC" immediately, so maybe you have it but it is somewhow corrupt?
Tue May 17 19:01:24 2005: Subject: Got an error using your script. Gabriel
(your comments go here)
I tried your script but I was not able to download any files. Instead I get the following error message: "Bad file descriptor". I tried using a manual FTP and I was able to download the file with no problems. What do you suggest?
Tue May 17 19:29:05 2005: Subject: TonyLawrence
I suggest you review the code. You have done something wrong.
Installation and light training Boston and New England
Tue May 17 19:37:22 2005: Subject: TonyLawrence
Just so you know I'm not trying to be difficult, I just pasted the first example of the above code into a Linux machine:
"t.pl" 38L, 808C written
[root@kerio root]# chmod 755 t.pl
[root@kerio root]# ./t.pl
Connected
Getting file listLogged in
Got file list
-rw-r--r-- 1 pcunix ftpadmin 3353 Jul 11 2004 404.pl
-rw-r--r-- 1 pcunix ftpadmin 7146 Mar 3 2001 Driver.c
-rw-r--r-- 1 pcunix ftpadmin 36821 Dec 23 2002 Linuxsamples.zip
-rw-r--r-- 1 pcunix ftpadmin 40016 Jan 20 2003 MacOSXsamples.zip
-rw-r--r-- 1 pcunix ftpadmin 6640 Mar 16 2002 README.ssh
-rwxr-xr-x 1 pcunix ftpadmin 1474560 Jul 28 2004 RESCO218.RWI
-rw-r--r-- 1 pcunix ftpadmin 37550 Dec 23 2002 SCOsamples.zip
etc.
The code works. If it doesn't work for you, you need to figure out why.
Wed May 25 09:25:33 2005: Subject: Bad file descriptor Jack
I think hte Bad file descriptor problem is due to the legacy $ftp->quit() calls which leave you referrring to a closed ftp connection. Make sense of those and you'll be on your way.
Wed May 25 10:57:04 2005: Subject: TonyLawrence
But it should be exiting after an ftp->quit
Tue Jun 14 18:10:43 2005: Subject: Bad fd anonymous
did you figure out how to get rid of that Bad File Descript error. I can run a standard get and put inside the perl script but the way it is fails with that error message as stated before.
Tue Jun 14 21:43:46 2005: Subject: TonyLawrence
I dunno. I must just not be seeing something here.. works for me, but not for you..
Wed Jul 13 23:05:49 2005: Subject: Bad File Descriptor anonymous
OK. I had the same error and I solved forcing passive mode this way:
$ftp=Net::FTP->new($host, Debug => 1, Passive => 1);
Note that Debug is ON, it really helps to see what is going on.
Something else, maybe the file you trying to send is already there, I guess this could be the reason for this message too. Try deleting the file(s) manually and run your code again with passive mode ON.
Enjoy!!! revac71
Thu Jul 21 15:47:15 2005: Subject: anonymous
Tony - this is very useful, thanks for sharing. I need to do opposite, upload newer files, not download. My ftp and perl skills are minimal. Appreciate any help. -Debster
Thu Jul 21 18:06:10 2005: Subject: TonyLawrence
It's just "put" instead of "get".
Mon Aug 1 22:15:52 2005: Subject: anonymous
Not that minimal :) Need to get the local list and upload any newer files.
Mon Aug 1 22:24:30 2005: Subject: TonyLawrence
Well, that's similar to what the second example does.
If you can't grok it, I or dozens of other folks at the Consultants page could write a script to spec; probably wouldn't cost much.
Fri Sep 9 15:43:30 2005: Subject: $ftp->cwd RobG
This doesn't seem to work very well. When I sign on, I am in (say) /home/user1, but I need to get files from /u1/scratch. I can cwd to u1, but only see subdirectories belonging to root. Any idea what's going on?
Fri Sep 9 16:14:18 2005: Subject: TonyLawrence
I don't understand your question. You "signed on"? To what? What are you supposed to be seeing?
Perl isn't doing anything different than you'd do by hand, so if you aren't getting what you expect, you did something wrong or ignored an error return you need to pay attention to.
Sat Sep 10 15:40:52 2005: Subject: Problem after Problem... BigDumbDinosaur
Hey, everyone who had "trouble" with Tony's script! I copied and pasted it onto my rusty old SCO box and guess what? After removing the extra $ftp->quit it worked for me. So, please stop complaining that it *doesn't* work, 'cuz it does.
One bad thing, tho. Now I have to scrap the ancient ksh script I wrote oh so many years ago to "automate" FTP sessions. Now what am I going to do with all my new-found spare time? <Grin>
Mon Sep 12 17:08:16 2005: Subject: $ftp->cwd RobG
Sorry, my comment was a bit terse.
I have a perl script using Net::FTP to sign onto a remote server, change to a specific directory on that server, and look for certain files. Now, if I do this "by hand" (ftp in UNIX), I have no problem doing a cwd, but $ftp->cwd only works for some directories. And even if it works, it does not "see" everything in that directory. So, there seem to be some restrictions on what $ftp->cwd can do.
Installation and light training Boston and New England
Reliable and experienced, punctual and professional.
Mon Sep 12 17:12:54 2005: Subject: TonyLawrence
There's something wrong in your script. If you can do it by hand, Net::FTP can do it.
Mon Sep 12 17:21:40 2005: Subject: $ftp->cwd RobG
In the following (with Debug on), I first look for files rg* in remote directory /home/rgrig/ftptmp - success. Then, look for files rg* in remote directory /u3/tsqadmin/scratch (same system) - failure ("No such file or directory")
rgrig@sc2{250} ftptst3 "/home/rgrig/ftptmp" "rg*"
Net::FTP>>> Net::FTP(2.74)
Net::FTP>>> Exporter(5.562)
Net::FTP>>> Net::Cmd(2.25)
Net::FTP>>> IO::Socket::INET(1.25)
Net::FTP>>> IO::Socket(1.26)
Net::FTP>>> IO::Handle(1.21)
Net::FTP=GLOB(0xd1acc)<<< 220 sc2 FTP server ready.
Net::FTP=GLOB(0xd1acc)>>> user rgrig
Net::FTP=GLOB(0xd1acc)<<< 331 Password required for rgrig.
Net::FTP=GLOB(0xd1acc)>>> PASS ....
Net::FTP=GLOB(0xd1acc)<<< 230 User rgrig logged in.
Net::FTP=GLOB(0xd1acc)>>> CWD /home/rgrig/ftptmp
Net::FTP=GLOB(0xd1acc)<<< 250 CWD command successful.
Net::FTP=GLOB(0xd1acc)>>> PORT 127,0,0,1,177,182
Net::FTP=GLOB(0xd1acc)<<< 200 PORT command successful.
Net::FTP=GLOB(0xd1acc)>>> NLST rg*
Net::FTP=GLOB(0xd1acc)<<< 150 Opening ASCII mode data connection for file list.
Net::FTP=GLOB(0xd1acc)<<< 226 Transfer complete.
rgtst1
rgtst2
Net::FTP=GLOB(0xd1acc)>>> QUIT
Net::FTP=GLOB(0xd1acc)<<< 221-You have transferred 0 bytes in 0 files.
Net::FTP=GLOB(0xd1acc)<<< 221-Total traffic for this session was 363 bytes in 1 transfers.
Net::FTP=GLOB(0xd1acc)<<< 221-Thank you for using the FTP service on sc2.
Net::FTP=GLOB(0xd1acc)<<< 221 Goodbye.
rgrig@yowie{83} ls /home/rgrig/ftptmp
abc rgtst1 rgtst2
rgrig@sc2{251} ftptst3 "/u3/tsqadmin/scratch" "rg*"
Net::FTP>>> Net::FTP(2.74)
Net::FTP>>> Exporter(5.562)
Net::FTP>>> Net::Cmd(2.25)
Net::FTP>>> IO::Socket::INET(1.25)
Net::FTP>>> IO::Socket(1.26)
Net::FTP>>> IO::Handle(1.21)
Net::FTP=GLOB(0xd1acc)<<< 220 sc2 FTP server ready.
Net::FTP=GLOB(0xd1acc)>>> user rgrig
Net::FTP=GLOB(0xd1acc)<<< 331 Password required for rgrig.
Net::FTP=GLOB(0xd1acc)>>> PASS ....
Net::FTP=GLOB(0xd1acc)<<< 230 User rgrig logged in.
Net::FTP=GLOB(0xd1acc)>>> CWD /u3/tsqadmin/scratch
Net::FTP=GLOB(0xd1acc)<<< 550 /u3/tsqadmin/scratch: No such file or directory.
rgrig@yowie{86} ls /u3/tsqadmin/scratch/rg*
/u3/tsqadmin/scratch/rgtst1 /u3/tsqadmin/scratch/rgtst2
Sun Dec 18 04:33:31 2005: Subject: Thanks! anonymous
The simple script is exactly what I was looking for.
Thanks for doing this.
Don
Wed Jan 11 16:06:19 2006: Subject: error while do an $ftp->ls jrg
when doing an "@files=$ftp->ls, $newerror=1", if there aren't any files, this returns with $newerror equal to 1, but if there is at least one file, then it works fine. Has anybody seen this before? Is there a workaround?
Wed Jan 11 18:39:59 2006: Subject: TonyLawrence
Workaround for what? What do you want to do if there are no files?
Thu Jan 12 13:46:39 2006: Subject: re: error while do an $ftp->ls jrg
I think it should return with $newerr=0 and @files="", since there really weren't any problems doing the ls, it's just that there are no files.
Wed Jan 25 08:00:14 2006: Subject: Works for me!!!! anonymous
Exactly what I was looking for... You Rock!!! Worked like a charm
Wed Jan 25 08:53:28 2006: Subject: ftp->size anonymous
I am trying to print the size of the file (using examle #1) like so:
foreach (@files) {
$size = ftp->size($file); #Get file size then later convert to KB's
print "$_ is $size (bytes) in size.";
I am getting an error that I perhaps forgot to load the module. Can you help?
Wed Jan 25 11:21:14 2006: Subject: TonyLawrence
We can't help when you are vague and coy. You got an error. Say what the error is, don't tell us what you think it means.
Getting free help demands more from YOU. If you want to pay for help, you can be confusing, vague, and incomplete. When you want free help, you have to be accurate, concise and intelligent. See http://aplawrence.com/newtosco.html#newsg for advice on what you need to do to get free advice.
Wed Jan 25 17:51:53 2006: Subject: YOU MISSED MY QUESTION anonymous
Didn't mean to sound "coy", I just wanted to know if the following is the correct syntax in order to get the size of the file:
$size = ftp->size($_);
No if you don't want answer this which ...was... my original question, then thanks for what you have given me so far and I'll research further.
Wed Jan 25 18:07:32 2006: Subject: TonyLawrence
I call b.s.
You explicitly did NOT ask that question. Go back and read what you wrote. I don't mean to sound nasty, but you didn't even really ask a question at all, and certainly didn't ask this question.
Your syntax is incorrect if it's as you wrote it here. Look closely at the examples and note that you need $ftp->size
Wed Jan 25 18:43:10 2006: Subject: OK UNCLE!! anonymous
OK, first, I appreciate your help!!! I didn't mean to sonund coy... I don't want to come across as vague... (It was 5AM and I had been searching *a long time* and finding a *lot* of bad examples on other sites.)
But thanks. I'll try it
NEXT PROJECT: Learn how to properly ask a question after hours of frustration
Wed Jan 25 18:54:15 2006: Subject: TonyLawrence
Good luck - hope you have it now.
Fri Jan 27 19:01:57 2006: Subject: Tom
Like others here, I found this very helpful, so thanks!
One problem I had was using $ftp->ls on a directory with a really large number of files (5k plus). It returns with an error "Arguments too long". I'm assuming I'm pushing up against the size of the buffer or something. Do you have any suggestions as to how to deal with this? For example, is there a way to grab the directory listing in pieces?
Thank you for any suggestions.
Fri Jan 27 19:27:33 2006: Subject: TonyLawrence
That's probably coming from the server - it's screwing up.
Fri Jan 27 20:57:09 2006: Subject: Tom
Turns out you're right. The server is running SCO Unix. When I did a test on another client's server with lots of files in it (running Redhat Fedora) it worked fine. So now I've got to figure out why the SCO box is doing that and either figure out a fix or a workaround.
Thanks.
Fri Jan 27 22:21:05 2006: Subject: TonyLawrence
Increase MAXEXECARGS (kernel tunable)
Tue Jan 31 21:58:57 2006: Subject: $ftp->message ?? anonymous
I see that information gets printed out if debug is on in a previous message, but the POD for Net::FTP says to use $ftp->message. When I do this, I get Net::FTP=GLOB(0x1f6c0e8)->message but there is no associated message...???
How can I get this to print the actual messages? $! doesn't give me what I would expect either, a bad password gives me Bad file descriptor
Tue Jan 31 22:13:56 2006: Subject: TonyLawrence
Tue Jan 31 22:17:56 2006: Subject: RE: $ftp->message anonymous
But I don't want to die, I just want to log the message. I tried to treat it like an array ref, but that barfed...
Tue Jan 31 22:26:36 2006: Subject: TonyLawrence
Then use "warn" or store it:
Tue Jan 31 22:28:00 2006: Subject: RE: $ftp->message anonymous
Got it, Thanks so much for your help!
Mon Nov 13 10:45:15 2006: Subject: anonymous
Hi can you help me turning off the user interactive prompt for FTP using this script......Thanks
Mon Nov 13 11:21:13 2006: Subject: TonyLawrence
No idea what you mean - explain more fully or see
http://aplawrence.com/rates.html
Fri Jan 5 23:06:49 2007: Subject: Clumsy code anonymous
Your second Net::FTP example has some very clumsy Perl constructs that could be much simplified. Let me know if you'd like to see a much neater, easier, simpler version.
Fri Jan 5 23:12:17 2007: Subject: TonyLawrence
That depends.
I try to write so that people who don't know much about Perl have a chance of following along. I admit that sometimes you can write great Perl that is also easy for the neophyte to understand, and if that's what you had in mind, yeah, I'm all for it.
On the other hand, there's nothing wrong with posting a higher level example in the comments so folks can learn from that too..
Thu Apr 19 14:55:00 2007: Subject: anonymous
I have a perl ftp script which has an statement like
$ftp->get($file1) || warn "can't get $file1";
It has to download hundreds of files from a ftp server. When running, it usually works ok, but suddently I get the following error:
Timeout at /usr/local/lib/perl5/site_perl/Net/FTP.pm line 503
I get the error at different files each time, so it's nothing to do with the files on the ftp site. I would like to retry with the file when this happens, but the problem is that control doesn't return to the script as it exits with a -1, my warn statement isn't reached, and the script fails.
Do you know how to get the module not to abort and kill the script too? Or can this not be avoided?
Thu Apr 19 17:37:17 2007: Subject: tonylawrence
Are you trapping ALL errors or just the get?
You can set a longer timeout too..
Thu Apr 19 18:06:43 2007: Subject: anonymous
I'm trapping all errors, but it is always 'get' that fails, and always on a different file.
Files are pretty small, so with a timeout=10 is normally ok. But anyway, I've tried with 1200 for example, and suddently on a file it keeps thinking and thinking until it reaches timeout (without a timeout it would probably go thinking infinitely...), and then kills the program with the message: Timeout at FTP.pm line 503.
I think it's a problem with the ftp server, not with the perl module, but I was wondering if I could wait for the timeout to run out and then just warn that the file could not be downloaded, but without killing the program with the Timeout message.
Thu Apr 19 18:09:58 2007: Subject: tonylawrence
Beyond my knowledge; suggest trying at a Perl newsgroup.
Fri Apr 20 07:43:04 2007: Subject: anonymous
Ok. Many thanks for your answer anyway!
Wed May 30 23:14:41 2007: Subject: months array Mac
In the @months array the abbreviation for May is incorrect (at least on my installation) it is listed as My when it should be May. Here I am testing in May, and it failed. Once I corrected this all worked fine. Thanks for this.
Mon Jul 16 15:37:51 2007: Subject: Thank you tony anonymous
This was really helpful
Thu May 1 17:04:19 2008: Subject: question anonymous
Why would you try and do $ftp->quit and things like that AFTER calling myerr() which has an exit statement?
Thu May 1 17:09:17 2008: Subject: TonyLawrence
Shouldn't be that way :-)
Add your comments