Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling

appending to html at beginning

by Limbomusic (Acolyte)
on Feb 03, 2017 at 07:48 UTC ( [id://1180934] : perlquestion . print w/replies, xml ) Need Help??

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

Hello :-) I am totally new to perl. I'm trying to get a madlibs story working. (I know there is a template called weblibs for this but couldnt get that to work)

Anyway I have got it working but all new stories are appended/added AFTER one another - I want the last one to be listed FIRST in the html - file instead of last. Is this possible?

I have an input.html with forms that send the nouns/adjectives to that generates the story and a link to the resulting stories.html file where the stories are listed (the "wrong" way)

here is my .pl file:
#!/usr/bin/perl local ($buffer, @pairs, $pair, $name, $value, %FORM); # Read in text $ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/; if ($ENV{'REQUEST_METHOD'} eq "GET") { $buffer = $ENV{'QUERY_STRING'}; } # Split information into name/value pairs @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%(..)/pack("C", hex($1))/eg; $FORM{$name} = $value; } $navn = $FORM{navn}; $navn2 = $FORM{navn2}; $adj1 = $FORM{adj1}; $bann1 = $FORM{bann1}; open(my $fh, '>>', 'stories.html'); print "Content-type:text/html\r\n\r\n"; print $fh "<HR color=#000000 SIZE=4>\n<h3>$navn something. $bann1!!! s +houted $navn! A $adj1 $navn2 something $navn!! something $navn said $ +bann1! Hihi.</h3>\n"; print "<a href='stories.html'>READ STORY</a>"; close $fh;

Replies are listed 'Best First'.
Re: appending to html at beginning
by Discipulus (Canon) on Feb 03, 2017 at 08:10 UTC
    Hello Limbomusic and welcome to the monastery and to the wonderful world of Perl!

    I know nothing about what you are telling (madlibs) nor I understand the web related part (it seems a very crude type of web interaction) but if you are trying to prepend some text to an already existing file.. no there is no an automatic way to do it, as far as i know.

    You need a two steps work. Between different possibilities you can work with a temporary file: you open a tempfile, you write $new_lines to it then you open the existing original file for read and write it's lines to the temp one. Finally you swap files.

    Or (if your existing file is not huge) you can avoid the tempfile overwriting the original (a security copy can be handy anyway; if something go bad you can loose everything!). Something like:

    use strict; use warnings; my @new_lines = some_function; # something that retrieve new lines my $file_path = 'file_path.ext'; open my $orig_file, '<', $file_path or die "unable to open '$file_path +' for read!"; my @orig_lines = <$orig_file>; close $orig_file; #reopen it wiping the content open my $new_file, '>', $file_path or die "unable to open '$file_path' + for write!"; #print first new get lines and then old ones print $new_file $_ for @new_lines,@orig_lines;


    PS this it is discussed in Perl FAQs: append to the beginning of a file.

    Also at SO brian_d_foy tells us a bit more detailed answer: prepend to a file.

    See also rename to swap files and sysopen and it's constant if you want more granularity on opening only yet existing file.



    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

      Hi Discipulus,

      you open a tempfile, you write $new_lines to it then you open the existing original file for read and write it's lines to the temp one. Finally you swap files.

      I was considering posting this, but went with the Tie::File example instead. But since I happened to be working with code that uses the rename method just the other day, I had the basics handy, here it is:

      #!/usr/bin/env perl use warnings; use strict; use File::Temp qw/tempfile/; use File::Basename qw/fileparse/; my $filename = "/tmp/test.html"; my @to_insert = ( '<p>Hello,', 'World! It is '.gmtime(time).' UTC</p>' ); open my $ifh, '<', $filename or die "open $filename: $!"; my ($basename,$path) = fileparse($filename); my ($tfh,$tfn) = tempfile( $basename.'_XXXXXXXXXX', SUFFIX=>'.tmp', DIR=>$path ); #warn "Debug: writing to $tfn\n"; # change the lines of the input file however you wish here my $found; while (<$ifh>) { print $tfh $_; if (/<!--\s*INSERT\s+HERE\s*-->/i) { $found=1; print $tfh "$_\n" for @to_insert; } } close $ifh; close $tfh; chmod(0666&~umask,$tfn) or warn "Couldn't chmod $tfn: $!"; if (!rename($tfn, $filename)) { my $e=$!; unlink($tfn); die "Renaming $tfn to $filename failed: $e"; } #warn "Debug: Renamed $tfn to $filename\n"; die "Marker not found" unless $found;

      For those who don't know, the advantage is that rename is an atomic operation on many filesystems, meaning that on such filesystems, the filename (in this case /tmp/test.txt) will always exist and always point to either the old version or the new version of the file, and never point to an "in-between" version where the file is still being written.

      Update: Forgot that unlink can set $!, so I preserved its value. Update 2: The code now modifies the file in the same way my other example does. Update 3, 2017-02-07: I was just reminded of the issue that files created by File::Temp have 0600 permissions, so I added the chmod to the code above.

      Update 2017-09-20: I provided an updated version of the above code using my new module File::Replace here: [RFC] File::Replace

      -- Hauke D

Re: appending to html at beginning
by haukex (Archbishop) on Feb 03, 2017 at 08:13 UTC

    Hi Limbomusic,

    It appears you have found some very old example code - the code you are using to get the form parameters is outdated, and your script isn't doing use warnings; use strict; at the top (see Use strict and warnings).

    If you are just learning Perl / CGI scripting, there are several modern web frameworks nowadays, whose use I'd strongly recommend. For example, a good starting point is Mojolicious::Guides::Tutorial. The "old" CGI module, which is the way a script like the one you've shown would handle form parameters, is no longer recommended for new developments.

    Anyway, moving on to your question: Your open is using a mode of '>>', which is "append mode", that's why anything you write to the filehandle $fh is being inserted at the end of the file. If you wanted to insert lines at the top of a file, there are several ways to do so. However, simply inserting lines at the top of the file will break the HTML, so instead you'd need to insert the lines at the correct place instead. One way to do this would be to insert a marker comment at the proper place in the file, like <!-- INSERT HERE -->, and then one relatively easy way to insert lines in the middle of a file is Tie::File (see also the documentation of splice).

    #!/usr/bin/env perl use warnings; use strict; my $filename = 'test.html'; my @to_insert = ( '<p>Hello,', 'World! It is '.gmtime(time).' UTC</p>' ); use Tie::File; tie my @lines, 'Tie::File', $filename or die "tie $filename: $!"; my $found; for (my $i=0; defined($lines[$i]); $i++) { if ($lines[$i]=~/<!--\s*INSERT\s+HERE\s*-->/i) { $found=1; splice @lines, $i+1, 0, @to_insert; last; } } die "Marker not found" unless $found; untie @lines;

    Update: Code now inserts current time to make the order of updates to the file more clear.

    Note that there are more "modern" ways to accomplish this instead of editing an HTML file. For example, you could store new "stories" in a file or a database on the server, and then when someone wants to see the list of stories, build the HTML page to display them dynamically. This has the advantages that you could allow for the user to select a range of stories, easily change the display format, etc. Even more advanced and "modern" techniques would be to provide the "stories" in JSON format, and then have JavaScript in the browser dynamically retrieve and display them. However, if you are just getting started, then the above is a good first step before going into these advanced techniques.

    Hope this helps,
    -- Hauke D

      Thanx for lightning fast replies :-) Sadly I am not "mentally equipped" yet - to fully understand how to implement your suggestions, and yes, old code - If I just put
      use warnings;
      use strict;
      I get 500 internal server error - because I guess the code isnt totally "right" ?
      I do have tho - a working guestbook (which I found at Matt Wrights website) - and there the entries gets sorted last first - he has some code:
      # Open Link File to Output open (GUEST,">$guestbookreal") || die "Can't Open $guestbookreal: $!\n +"; for ($i=0;$i<=$SIZE;$i++) { $_=$LINES[$i]; if (/<!--begin-->/) { if ($entry_order eq '1') { print GUEST "<!--begin-->\n"; } if ($line_breaks == 1) { $FORM{'comments'} =~ s/\cM\n/<br>\n/g; } print GUEST "<b>$FORM{'comments'}</b><br>\n";
      which I guess has something to do with it - but dont know how to implement it.
      I,ll keep investigating and gonna fiddle with your suggestions, but for now, I think I,m just gonna find some html which sends the user to the bottom of the page.
      Thank you SO much for your input guys.
      Kind regards

      P.S - "Madlibs" I guess is the english word - in norwegian - we call it an "adjective story".

        I do have tho - a working guestbook (which I found at Matt Wrights website)

        Do be very, very careful with that. There has been plenty of discussion about that collection of scripts over the years and almost all of the recent comments have been uncomplimentary. The scripts are very old and at least some are very insecure. See the summary at the NMS project for some background and how to obtain better versions of those scripts.

        The monks here can help you with your perl questions but if you are going to try to patch some of Matt's old code often the better strategy would be to start again from scratch with the benefit of a modern approach and security as a primary principle.

        Addendum: For the benefit of non-English speakers, there is a word in English which means "appending to the beginning" and it is prepending. Appending is reserved for adding to the end only.

        Hi Limbomusic,

        I get 500 internal server error - because I guess the code isnt totally "right" ?

        Yes, as described in Use strict and warnings, one of the first things that will happen is that you get errors because you need to declare your variables at or before their first use, for example my $navn = $FORM{navn};. The best way to debug this is by running the script from the command line and looking at the errors you get there. If you're just looking for compilation errors like undeclared variables, you can also run perl -c

        Once you've got the script working locally, if you have trouble on the server, see CGI Help Guide and Troubleshooting Perl CGI scripts.

        a working guestbook

        Please be very careful with code you find like this - what you've shown has a security hole! The problem is print GUEST "<b>$FORM{'comments'}</b><br>\n"; - this allows the visitor to insert any arbitrary HTML code into the page, including JavaScript, which has the potential to seriously mess with the user. This is known as a Cross-site scripting (XSS) attack, for a tutorial see for example this.

        Sorry, but based on the code I have to recommend removing that script from your site, or at least temporarily disabling it. Note: As I was writing this, hippo posted a reply pointing you to a better alternative.

        Hope this helps,
        -- Hauke D

        I get 500 internal server error - because I guess the code isnt totally "right" ?

        yes you cannot put use strict; use warnings; bare statements without modifying your code. You need to declare all variables you use using my like in my $var = "foo";

        Blindly develop on webserver is, in my experience, a frustrting operation: everything you get wrong you got a 500 error.

        if you want to learn about Modern Perl Web you can start with and it's module Dancer2 it is pretty easy to use.


        There are no rules, there are no thumbs..
        Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: appending to html at beginning (use CGI)
by Anonymous Monk on Feb 03, 2017 at 11:35 UTC

    This is what you're supposed to do

    use CGI (); my $q = CGI->new; my $navn = $q->param('navn'); my $navn2 = $q->param('navn2'); my $adj1 = $q->param('adj1'); my $bann1 = $q->param('bann1'); ... print $q->header, $readystory;

      Hi Anonymous,

      That's how I would have done it too, years ago* - now, CGI's own documentation says: is no longer considered good practice for developing web applications, including quick prototyping and small web scripts. There are far better, cleaner, quicker, easier, safer, more scalable, more extensible, more modern alternatives available at this point in time. These will be documented with CGI::Alternatives.

      * Update: Okay, I admit I have sinned more recently than that. But only because (I thought) I knew what I was doing :-) And I still wouldn't recommend CGI to newcomers, who are more likely to misuse the module.

      -- Hauke D

        Did you look at the op? Op wrote and ran a cgi program. But is hand parsing...reinventing the module-- cgi module is best for doing that; every time you quote that when if doesnt apply is trolling