Loading ...
Sorry, an error occurred while loading the content.

A better way (long) [was:Reading from and writing to a file on a Linux machine]

Expand Messages
  • Charles K. Clarkson
    ... Be careful what you wish for! ... I edited Don s script for clarity. Mainly, I lined up some of the comments and added some spacing. I did change the order
    Message 1 of 1 , Jan 1, 2002
    • 0 Attachment
      "Don Smith" wished:

      : Oh well, the script works so maybe someone new to
      : perl can benefit from it and perhaps someone more
      : experienced can show us a better way.

      Be careful what you wish for!

      : The script :
      : 1) takes a user input from an HTML file ($direcn)
      : 2) Opens "listing.dat" which lists all the directory names
      : 3) Checks to see if the directory name is in the list.
      : 4) If it is, it prints the "Already in use" error message
      : and makes $test = 1.
      : 5) If it isn't found, it closes the file.
      : 6) The file Listing.dat is reopenned to be written to.
      : 7) The directory name is added to the bottom of the list.
      : 8) The file is then closed.
      : 9) The directory is then created.
      : 10) "OK" and the directory are printed to the browser.

      I edited Don's script for clarity. Mainly, I lined up
      some of the comments and added some spacing. I did change
      the order of some statements to illustrate a point or two.
      If you read this in a monospaced font, like 'Courier New',
      it may look less cluttered. The unedited version is at:
      <http://groups.yahoo.com/group/perl-beginner/message/7991>

      : #!/usr/bin/perl -w
      :
      : use lib '/CGI.pm';
      : use CGI::Carp 'fatalsToBrowser';
      :
      : use CGI qw/:standard/;
      :
      : $CGI::POST_MAX=1024 * 10; # max 10K posts - security
      : $CGI::DISABLE_UPLOADS = 1; # no uploads - security
      :

      perl modules come in 2 flavors. Object oriented and
      function oriented. The push seems to be toward objects,
      but functions are usually faster. In order to use the
      function oriented interface, we have to import the
      functions. The standard functions can be imported with
      ':standard'.

      How many functions are in :standard? 140.

      Actually, it's 139. Tr and TR are the same sub.
      Curiously enough we don't use any of them in this script.
      It's like writing a 2000 line script and only using 20
      lines to process the request.

      : my $foo = new CGI;

      In order to use the object oriented interface to
      CGI.pm, we need only create a new object, as above. The
      entire CGI.pm file is still read into memory, but no
      subroutines are imported into our namespace.

      It is generally considered a 'good' habit to either
      use the object or the function oriented interface, but
      never both.

      : # Reads directory name from input form.
      : my $direcn = $foo->param('direcn');

      Here we get the param passed to this script using
      the $foo object. As a function we might have used:

      my $direcn = param('direcn');

      Either way, we should make certain something was
      passed:

      die 'Incorrect Parameters' unless param('direcn');
      or:
      die 'Incorrect Parameters' unless $foo->param('direcn');

      CGI::Carp 'fatalsToBrowser' will handle this.

      : use strict; # Use only the variables in the
      : # script - security

      I usually place this as the second line of *every*
      script.

      : print "Content-type: text/html\n\n"; # Required - It could cause
      : # a "500 server error" if
      : # left out on the linux
      : # machine.

      It's required by HTTP. The standard specifies an
      Entity-header and an Entity-body. If you view the
      source of a browser window (connected to a site), you
      see the Entity-body. The head was received by the
      browser and used to help render the page.

      You can read the spec at:
      <ftp://ftp.isi.edu/in-notes/rfc2616.txt>
      Check out chapter 7 (it's really suspenseful.)

      A blank line is used to separate the header from the
      rest. That's why Content-type ends with: \n\n.

      :standard imported a function to handle this:

      print header;

      And we could use the $foo object:

      print $foo->header;

      Version 2.752 of CGI.pm prints:

      Content-Type: text/html; charset=ISO-8859-1


      : # Change Path - linux format shown. Use full path on Windows.
      : my $path = "/hdd/3/web/test/public_html";

      I get real picky about double quotes. It's more a
      matter of style. I just hate to use double quotes on
      a string that doesn't need interpolating:

      my $path = '/hdd/3/web/test/public_html';

      or, using a quote-like operator:

      my $path = q|/hdd/3/web/test/public_html|;

      : my $dir = "$path/$direcn"; # Do not change this

      I think I would have waited till later to do this.

      : my $filename = "$path/listing.dat";
      : my ($test);
      :
      : open (FILEHANDLE, "<$filename") || die "Cannot open $filename.";
      : while (<FILEHANDLE>) {
      :
      : # goto next line unless we get a match
      : chomp $direcn;

      This shouldn't be necessary. $direcn was received via a
      form and shouldn't have a newline character in it.

      : next unless /\b$direcn\b/i;

      The o option on the match will speed things up here.
      Basically it tells perl $direcn is the same each time
      /\b$direcn\b/ is proccessed. See perlre for more details.

      next unless /\b$direcn\b/io;

      : print "Content-type: text/html\n\n";

      We only need this once.

      : if ($direcn ne ""){

      Since $direcn isn't changing, we only need to check
      this once. Inside the while block, we are checking it
      on each pass. Imagine a listing.dat file with 20,000
      entries.

      : print "<BR><BR><BR><BR><Center><H1>Sorry,
      : the directory name \"" .
      : $direcn . "\" is in use.</H1><BR>
      : <H2>Please click your browser's back
      : button and enter another directory
      : name.</H2></Center>" ;

      Remember those 140 functions that were imported?
      Three of them are above. <center> is not part of
      the :standard import.

      Assuming we used: use CGI qw/:standard center/;
      or: use CGI qw/param br h1 h2 center header/;

      print
      br, br, br, br,
      center(
      h1(qq|Sorry, the directory name "$direcn" is in use.),
      br,
      h2( q|Please click your browser's back button|,
      q|and enter another directory name.| )
      );

      : $test = "1";

      Use numbers as numbers and strings as strings. Let perl
      sort'em out:

      $test = 1;

      : }
      : }
      :
      : close FILEHANDLE;
      : if ($test ne "1"){
      : open (FILEHANDLE2, ">>$filename")|| die "Cannot open $filename.";
      :
      : print FILEHANDLE2 "$direcn\n";
      : close FILEHANDLE2;
      : }
      :
      : mkdir ($dir,0777); #The 0777 is the chmod. It can be
      : changed later.
      :
      : print "OK <BR><BR>";
      : print $dir;


      Before we continue, Let's take another look at our code
      using the function oriented style. I changed some variable
      names (to protect the innocent):

      use CGI qw/:standard center/;
      use CGI::Carp 'fatalsToBrowser'; # send errors to the browser
      $CGI::POST_MAX = 1024 * 10; # max 10K posts - security
      $CGI::DISABLE_UPLOADS = 1; # no uploads - security

      # this really needs its own subroutine and nice message
      die q|Incorrect Parameters| unless param('direcn');

      # Reads directory name from input form.
      my $dir_name = param('direcn');

      # Change Path - linux format shown.
      # Use full path on Windows.
      my $path = q|/hdd/3/web/test/public_html|;
      my $file_name = qq|$path/listing.dat|;

      my $test;
      open FH, $file_name or die qq|Cannot open $filename: $!|;
      while (<FH>) {
      # goto next line unless we get a match
      next unless /\b$dir_name\b/io;
      print
      br, br, br, br,
      center(
      h1(qq|Sorry, the directory name "$dir_name" is in use.|),
      br,
      h2( q|Please click your browser's back button|,
      q|and enter another directory name.| )
      );
      $test = 1;
      }
      close FH;

      Personally, I don't like having all that stuff in the
      while. This is a style issue, not a programming issue. That's
      one problem with reviewing code. Some items are not wrong or
      less efficient. They're just not the reviewer's style. I'd
      rather separate the print from the while and close that file
      ASAP.
      I prefer a subroutine (or function):

      sub valid_directory {
      # Reads directory name from input form.
      my ($dir_name, $file_name) = @_;

      open FH, $file_name or die qq|Cannot open $file_name: $!|;
      while (<FH>) {
      # return 'undef' if we get a match
      return if /\b$dir_name\b/io;
      }
      close FH;

      # directory name is valid - return it.
      return $dir_name;
      }

      We can use this fuction as:

      my $dir_name = valid_directory( param('direcn'), $file_name );

      If $dir_name is taken it will be defined undef.
      Otherwise it will be defined by the directory name.
      Also, the sub stops checking as soon as it finds a
      match. The previous inline routine did not.

      Let's take another look:

      use CGI qw/:standard center/;
      use CGI::Carp 'fatalsToBrowser'; # send errors to the browser
      $CGI::POST_MAX = 1024 * 10; # max 10K posts - security
      $CGI::DISABLE_UPLOADS = 1; # no uploads - security

      # Change Path - linux format shown.
      # Use full path on Windows.
      my $path = q|/hdd/3/web/test/public_html|;
      my $file_name = qq|$path/listing.dat|;

      die q|Incorrect Parameters| unless param('direcn');

      my $dir_name = valid_directory( param('direcn') );

      if ( defined $dir_name ) {

      open FH, qq|>>$filename| or
      die qq|Cannot open $filename: $!|;
      print FH qq|$dir_name\n|;
      close FH;
      mkdir ("$path/$dir_name", 0777);
      print
      header, start_html(q|Invalid Directory|),
      q|OK|, br, br, $dir,
      end_html;


      } else {
      $dir_name = param('direcn');
      print
      header, start_html(q|Invalid Directory|),
      br, br, br, br, center(
      h1( qq|Sorry, the directory name "$dir_name" is in use.| ),
      br, h2( q|Please click your browser's back button|,
      q|and enter another directory name.| ) ),
      end_html;
      }

      sub valid_directory {
      # Reads directory name from input form.
      my ($dir_name, $file_name) = @_;

      open FH, $file_name or die qq|Cannot open $file_name: $!|;
      while (<FH>) {
      # return 'undef' if we get a match
      return if /\b$dir_name\b/io;
      }
      close FH;

      # directory name is valid - return it.
      return $dir_name;
      }

      __END__



      HTH,
      Charles K. Clarkson
      Clarkson Energy Homes, Inc.
      254 968-8328


      All generalizations are bad.
      - R. H. Grenier
    Your message has been successfully submitted and would be delivered to recipients shortly.