lagrenouille has asked for the wisdom of the Perl Monks concerning the following question:

G'day again

I finally got round to testing out the CGI script and so on you kindly suggested before. I now have a different problem with the script, whereas before it would die with a server error... now it simply REPLACES the contents of the file it is supposed to be appended to.

The idea is as follows:
/protected/addnews.html - Adds a news item, has a submission form for 'nickname', 'headline' and 'news_text'
/index.html - The main page (no frames site) which is modified by the following script
/cgi-bin/news.pl - see below for source

The idea is to sandwich the news in amongst the already created html document in index.html but at the moment it is simply replaced and not formatted as I would like it (in fact not at all!)
Here is the 'updated' index.html

From: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testingFrom: David</TD> <TD><B>test</B></TD> <TD>testing

and here is the script i am using to update it:

#!/usr/bin/perl -w # news.pl # Attempt #3 at a news script. # Use the 'strict' switch to ensure logical operation of the code. #use strict; # Make use of the CGI module. use CGI qw(:standard); use CGI::Carp 'fatalsToBrowser'; # Initialise variables for the data previously inputted. my $nickname = param("nickname"); # Nickname my $headline = param("headline"); # Headline my $news_text = param("news_text"); # News Item Text # Create a variable to store the location of the display page, "index. +html". my $old_news = "../html/index.html"; # Read the current data or die with an error message. open (NEWS, "<$old_news") || die "$!"; # Read the data from '$old_news' into an array called '@news'. my @news = <NEWS>; # Close the '$old_news' file after use. close (NEWS); # Re-open the '$old_news' file for the input of the new data. open (NEWS, ">$old_news") || die "$!"; # Write the data back into the '$old_news' file with the new data incl +uded where appropriate. # Loop through each line of the '$old_news' file and write it back. #my $line = ""; foreach $line (@news) { # Add the 'nickname' of the poster. print NEWS "From: $nickname</TD>\n<TD>"; # Add the 'headline' of the post. print NEWS "<B>$headline</B></TD>\n<TD>"; # Add the 'news_text' underneath the 'headline'. print NEWS "$news_text"; } # Close the '$old_news' file after use. close (NEWS); # Provide the poster with a link to their news item. print header(), start_html, h2("Thank you, $nickname"), h4("Go here to view your news item: index.html"), end_html; # EOF news.pl

Note that I had to comment out 'strict' becaus I couldn;t work out how to initialise the variable $line (I made it my $line = ""; and it wouldn't work) and I've removed the check for the tag to try adn get some output.

How can I get the script to write out correctly my data into the table I created?
How do I correct the 'race' problem?
(BTW only 1 person will use the script but for completeness and learning I'd like to know how to fix this problem)

Thanks in advance La Grenouille

Replies are listed 'Best First'.
Re: News script again
by Zaxo (Archbishop) on Oct 03, 2001 at 08:52 UTC

    Setting aside detailed review, the line

    open (NEWS, ">$old_news") || die "$!";
    should be
    open (NEWS, ">>$old_news") || die "$!";
    to append to the file. It is likely that you also need a write lock on the file to prevent a data losing race.

    After Compline,
    Zaxo

Re: News script again
by MZSanford (Curate) on Oct 03, 2001 at 14:47 UTC
    Just to add to Zaxo's above comment, flock() eq 'good' ...

    People new to CGI programming often miss the fact that the http server will start multiple copies of your process, and any data files read/written could be done so at the same time. While reads are ok, all processes should be using flock() so they do not read partial data, or write at the same time as another process.

    Moral of the story : Pleas use flock() for any CGI process which reads/writes data files.
    "They shall not overcome. Whoever told them that the truth shall set them free was obviously and grossly unfamiliar with federal law."
        -- John Ashcroft
      G'day

      I'd be more than happy to use flock()...

      If I knew what it was
      Its syntax and
      when to use it and why?

      Thanks again
      La Grenouille

        flock allows you to lock a file, so other process that check for locks will wait until the file is unlocked to use it. This way 2 processes will not try to write a file at once, which usually results in one of the writes being lost.

        See perldoc -f flock, search for flock on this site and look at KM's RE: File Locking for more information.

        Ok, here some help to get you started :

        1. flock() is a function used to lock files.
        2. See the basic info here
        3. When to use it. flock() is used whenever two (or more) processes may try to access the same file at the same time. It gives you the abiality to have one process write at a time, and all reads will wait until the wrtie is complete.

        There are some limitations to flock().
        1. If there is a process which does not check for a lock, it will not stop it from writting. This means, that for flock() to work correctly, all processes which read/write the data file need to impliment flock().
        2. flock() is unimplimented on some OS's.

        "They shall not overcome. Whoever told them that the truth shall set them free was obviously and grossly unfamiliar with federal law."
            -- John Ashcroft

        The name flock is a contraction of "file lock". The syntax for it is available via perldoc -f flock, or here.

        Why? Because if two programs, or two instances of the same program (as you may get from a web server) attempt to rewrite the same file at the same time you lose changes.

        For example:

        • prog 1 reads file
        • prog 2 reads file
        • prog 1 writes file
        • prog 2 writes file, losing prog 1's changes

        Worse:

        • prog 1 reads file
        • prog 1 starts rewriting file, and gets halfway
        • prog 2 reads file (but only reads the first half)
        • prog 1 finishes writing
        • prog 2 rewrites file
        Now half the file is simply missing!

        By calling flock() before starting to read the file, the problem will be avoided, as prog 2 will simply stall until prog 1 releases the lock.