#!/usr/bin/perl # Don't use the -w switch. Use the warnings pragma. That way you can disable it where needed. use strict; use warnings; use CGI; use CGI::Carp qw(fatalsToBrowser); my $query = new CGI; use constant DATA_FILE => 'testfile.txt'; use constant DELIMITER => '|'; # I cleaned up all the stuff at the front and jump right into the script. my ( $input, $errors ) = Process_Input($query); if (@$errors) { Display_Error_Page($errors); } else { Store_Data( $input->{name}, $input->{name1}, $input->{comments}, Format_Date_Time(), ); my $data = Load_Data(); Display_Main_Page($data); } exit; # not strictly necessary, but the exit communicates that # we are at the end of application logic rather nicely. # This replaces part of the big if/elsif/block. # We build up a list of bad fields in @errors. # Also, accepted field data from the query are stored in a hash. sub Process_Input { my $q = shift; my %input; my @errors; foreach my $field (qw(name name1 comments)) { my $value = $query->param($field); if ( defined $value and length $value ) { $input{$field} = $value; } else { $input{$field} = undef; push @errors, $field; } } return \%input, \@errors; } # A very simple routine to reduce some repeated code. # It could be extended by adding named arguments with # sensible defaults: # * content_type => content type to use. # * title => title of page # * heading => heading to put at top of page # * footer => stuff to display at bottom of page # * body => a string or array ref of stuff to put between # the heading and the footer. # Or you could move up to a proper templating system. # sub Print_Page { print "Content-type:text/html\n\n"; print join "\n", @_; return; } # Renamed from Writing_to_file for these reasons: # * Data store may not always be a file. # * The name was grammatically weird (I don't know the correct term, sorry). # Write_To_File would have been better, but see the first reason. # # Fixed open() statement. # Used 3 argument open # Used lexical filehandle. # Reformatted the 'or die' clause. # Used low precedence 'or'. # Fixed error message and included $! to get real error. # # Consider making the file path the first argument of this routine. Gets rid # of a global--it's a constant, but still it's a global. sub Store_Data { my @input_to_file = @_; open( my $data, '>>', DATA_FILE ) or die "Error opening file: $!"; # This is the source of your problem. # The serialization you are using does not escape '\n', and so entries # with line breaks are treated at separate one field entries. # # One solution would be to fix your serialization routine to escape # '\n' and '|' and also make sure that whatever you use to mark your # escapes is also escaped. For example, if you use '\', to escape '|' so # that '|' becomes '\|', and use '\n' for new lines, the you'll also need # to escape '\' as '\\'. # # The other option would be to use a library to serialize your data. # Text::CSV, a db file, YAML, or DBD::SQLite would all be good options. print $data join( DELIMITER, @input_to_file ), "\n"; close($data); } # Fixed the case of the routine name to be consistent. # Changed name to Display_Main_Page, because it may be going to anything, not # just a browser. # # This routine requires a reference to a two dimension array for its argument. # sub Display_Main_Page { my $data = shift; Print_Page( "