#!/usr/bin/perl use warnings; use strict; # constants for prices our $GB_PRICE = 3.95; our $BRUSCHETTA_PRICE = 4.95; our $HUMMUS_PRICE = 5.95; our $SALAD_PRICE = 1.00; our $SPAGHETTI_PRICE = 8.95; our $LASAGNA_PRICE = 9.95; our $PIZZA_PRICE = 10.95; our $SOFT_DRINK_PRICE = 2.00; our $BEER_PRICE = 3.00; our $LIQUOR_PRICE = 3.50; our $WINE_PRICE = 4.00; our $GST = 0.07; our $PST = 0.08; our $LST = 0.10; # the first argument of this function is a filename # the second argument is a reference to a hash of variables # all variables in that hash will be written out to a file # the format of the file is as follows # SCALARS: s:name:value # ARRAYS : a:name:value1:value2:value3 # HASHES : h:name:key1:value1:key2:value2 # newlines are the seperators! there is no terminating character sub write_hash { my $filename = shift @_; my $vars = shift @_; open(FILE, ">$filename"); foreach my $key (keys %{$vars}) { # this one is just for convienence my $i = \( $vars->{$key} ); # this is a switch statement # make a reference to $vars->{$key} and pass it to ref for (ref $i) { # if it is scalar, ref will return SCALAR... if (/SCALAR/) { print FILE "s:$key:" . $vars->{$key} . "\n"; } # ...otherwise it returns an empty string # which means it is a hash or an array else { my $j = $vars->{$key}; my $type = ref $j; # if it is an array, print out each element if ($type eq "ARRAY") { print FILE "a:$key"; for my $element ( @{$j} ) { print FILE ":$element"; } print FILE "\n"; } # likewise for a hash, print out the key/value pairs elsif ($type eq "HASH") { print FILE "h:$key"; for my $k ( keys %{$j} ) { print FILE ":$k:" . $j->{$k}; } print FILE "\n"; } } } } close(FILE); return; } # the first argument of this function is a filename # function returns a hash # see the above function for the format of the file sub read_hash { my $filename = shift @_; my %vars; # these are used when extracting data from the fields my ($type, $name, $value, $key); open(FILE, "$filename"); my @data = ; close(FILE); # iterate through the array foreach my $line (@data) { # retrieve the type $type = substr($line, 0, 1); for ($type) { # if it's a scalar... if (/s/) { (undef,$name,$value) = split(/:/, $line, 3); chomp $value; $vars{$name} = $value; } # if it's an array... if (/a/) { # stores the elements of the array, temporarily my ($values, @elements); (undef,$name,$values) = split(/:/, $line, 3); chomp $values; @elements = split(/:/, $values); # now we've got the values split up # time to write things to the hash $vars{$name} = (\@elements); } # if it's a hash... if (/h/) { # $k and $v are for key and value my ($k, $v, $pairs, %hash); (undef,$name,$pairs) = split(/:/, $line, 3); chomp $pairs; # extract all the pairs and put them in the hash while (defined $pairs) { ($k,$v,$pairs) = split(/:/, $pairs, 3); $hash{$k} = $v; } # now shove a hash ref into our big hash! $vars{$name} = \%hash; } } } return %vars; } sub write_start { # get the error message, if there is one # undefined if there is no errmsg my $errmsg = shift(@_); print "Content-Type: text/html"; print "\n\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print 'Restaurant Bill Calculator' . "\n"; print '' . "\n"; print '' . "\n"; print '
' . "\n"; if (defined $errmsg) { print '
' . $errmsg . '
' . "\n"; print '
' . "\n"; } print '' . "\n"; print '' . "\n"; print ''; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '
Number of Patrons: ' . "\n"; print '' . "\n"; print '
' . "\n"; print '' . "\n"; print '
' . "\n"; print '' . "\n"; print '
' . "\n"; print '' . "\n"; print '' . "\n"; return; } sub write_appetizer { # get the session id... my $session_id = shift @_; # this will have an error message if one was passed, and will be # undefined otherwise my $errmsg = shift @_; # ...and write the page print "Content-Type: text/html"; print "\n\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print 'Restaurant Bill Calculator - Appetizers' . "\n"; print '' . "\n"; print '' . "\n"; print '
' . "\n"; if (defined $errmsg) { print '
' . $errmsg . '
' . "\n"; } print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' ."\n"; print '' . "\n"; print '' . "\n"; print '
Appetizers: ' . "\n"; print '' . "\n"; print '
Quantity: '; print '' . "\n"; print '
Add more?
'; print ''; print '
' . "\n"; print '' . "\n"; print '' . "\n"; print '
' . "\n"; print '' . "\n"; print '' . "\n"; return; } sub write_entree { # retrieve the session id... my $session_id = shift @_; # and the error message my $errmsg = shift @_; # ...and write the page print "Content-Type: text/html"; print "\n\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print 'Restaurant Bill Calculator - Entrees' . "\n"; print '' . "\n"; print '' . "\n"; print '
' . "\n"; if (defined $errmsg) { print '
' . $errmsg . '
' . "\n"; } print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print ''."\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' ."\n"; print '' . "\n"; print '' . "\n"; print '
Entrees: ' . "\n"; print '' . "\n"; print '
Add a Salad?
Quantity: '; print '' . "\n"; print '
Add more?
'; print ''; print '
' . "\n"; print '' . "\n"; print '' . "\n"; print '
' . "\n"; print '' . "\n"; print '' . "\n"; return; } sub write_drinks { # get the session id... my $session_id = shift @_; # this will have an error message if one was passed, and will be # undefined otherwise my $errmsg = shift @_; # ...and write the page print "Content-Type: text/html"; print "\n\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print 'Restaurant Bill Calculator - Drinks' . "\n"; print '' . "\n"; print '' . "\n"; print '
' . "\n"; if (defined $errmsg) { print '
' . $errmsg . '
' . "\n"; } print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' ."\n"; print '' . "\n"; print '' . "\n"; print '
Drinks: ' . "\n"; print '' . "\n"; print '
Quantity: '; print '' . "\n"; print '
Add more?
'; print ''; print '
' . "\n"; print '' . "\n"; print '' . "\n"; print '
' . "\n"; print '' . "\n"; print '' . "\n"; return; } sub write_subtotal { # get the session id... my $session_id = shift @_; # and the variables we'll need my $vars = shift @_; my $appetizers = $vars->{'appetizers'}; my $entrees = $vars->{'entrees'}; my $drinks = $vars->{'drinks'}; # ...and write the page print "Content-Type: text/html"; print "\n\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print 'Restaurant Bill Calculator - Subtotal' . "\n"; print '' . "\n"; print '' . "\n"; print '
' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; # now for the fun part print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '
ItemTotal #Price PerTotal Charge
Garlic Bread' . $appetizers->{'garlic bread'} . '$'; printf "%.2lf", $GB_PRICE; print '$'; printf "%.2lf", $appetizers->{'garlic bread'} * $GB_PRICE; print '
Bruschetta' . $appetizers->{'bruschetta'} . '$'; printf "%.2lf", $BRUSCHETTA_PRICE; print '$'; printf "%.2lf", $appetizers->{'bruschetta'} * $BRUSCHETTA_PRICE; print '
Humous & Tabouleh' . $appetizers->{'hummus'} . '$'; printf "%.2lf", $HUMMUS_PRICE; print '$'; printf "%.2lf", $appetizers->{'hummus'} * $HUMMUS_PRICE; print '
Spaghetti' . $entrees->{'spaghetti'} . '$'; printf "%.2lf", $SPAGHETTI_PRICE; print '$'; printf "%.2lf", $entrees->{'spaghetti'} * $SPAGHETTI_PRICE; print '
Lasagna' . $entrees->{'lasagna'} . '$'; printf "%.2lf", $LASAGNA_PRICE; print '$'; printf "%.2lf", $entrees->{'lasagna'} * $LASAGNA_PRICE; print '
Pizza' . $entrees->{'pizza'} . '$'; printf "%.2lf", $PIZZA_PRICE; print '$'; printf "%.2lf", $entrees->{'pizza'} * $PIZZA_PRICE; print '
Salad' . $entrees->{'salad'} . '$'; printf "%.2lf", $SALAD_PRICE; print '$'; printf "%.2lf", $entrees->{'salad'} * $SALAD_PRICE; print '
Soft Drink' . $drinks->{'soft drink'} . '$'; printf "%.2lf", $SOFT_DRINK_PRICE; print '$'; printf "%.2lf", $drinks->{'soft drink'} * $SOFT_DRINK_PRICE; print '
Beer' . $drinks->{'beer'} . '$'; printf "%.2lf", $BEER_PRICE; print '$'; printf "%.2lf", $drinks->{'beer'} * $BEER_PRICE; print '
Liquor' . $drinks->{'liquor'} . '$'; printf "%.2lf", $LIQUOR_PRICE; print '$'; printf "%.2lf", $drinks->{'liquor'} * $LIQUOR_PRICE; print '
Wine' . $drinks->{'wine'} . '$'; printf "%.2lf", $WINE_PRICE; print '$'; printf "%.2lf", $drinks->{'wine'} * $WINE_PRICE; print '
Sub-total$' . "\n"; printf "%.2lf", $vars->{'subtotal'}; print '
GST (7%)$' . "\n"; printf "%.2lf", $vars->{'subtotal'} * $GST; print '
PST (8%)$' . "\n"; printf "%.2lf", $vars->{'food_subtotal'} * $PST; print '
Liquor Tax (10%)$' . "\n"; printf "%.2lf", $vars->{'liquor_subtotal'} * $LST; print '

TOTAL BILL$' . "\n"; printf "%.2lf", $vars->{'total'}; print '
' . "\n"; print '
' . "\n"; print '' . "\n"; print 'Please select a Gratuity: ' . "\n"; print '' . "\n"; print ''; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; } sub write_total { # get the data my $vars = shift @_; # get the amount of the tip my $tip_amt = ('.' . $vars->{'tip'}) * $vars->{'subtotal'}; # write the page print "Content-Type: text/html"; print "\n\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print '' . "\n"; print 'Restaurant Bill Calculator - Grand Total' . "\n"; print '' . "\n"; print '' . "\n"; print '

Grand Total:

' . "\n"; print '

The final total of the bill is $'; printf "%.2lf", $vars->{'total'}; print '

'; print 'With a gratuity of ' . $vars->{'tip'} . '%'; print ' of the sub-total ($'; printf "%.2lf", $tip_amt; print ')'; print ' the grand total is: $'; printf "%.2lf", $vars->{'total'} + $tip_amt; print '.'; print '

'; print 'There are ' . $vars->{'patrons'} . ' patrons. Each person owes $'; printf "%.2lf", ($vars->{'total'} + $tip_amt) / $vars->{'patrons'}; print '.'; print '

'; print ''; print ''; } # returns the number of patrons, and the errorcode sub process_patron { # put the query string into something we can work with my $qs = $_[0]; my $err = 0; if (! $qs->{'patrons'} || length $qs->{'patrons'} > 2) { $err = 1; } return $qs->{'patrons'}, $err; } sub process_appetizer { # again with the query string my $qs = shift @_; # more contains 'on' if it was checked my $more = ($qs->{'more'}) ? 1 : 0; # fill %ret with the return values my %ret = ( appetizer => $qs->{'appetizer'}, qty => $qs->{'qty'}, more => $more, ); return %ret; } sub process_entree { # get the QS my $qs = shift @_; # contains 'on' if it was checked my $more = ($qs->{'more'}) ? 1 : 0; my $salad = ($qs->{'salad'}) ? 1 : 0; # fill ret with the appropriate values my %ret = ( entree => $qs->{'entree'}, qty => $qs->{'qty'}, more => $more, salad => $salad ); return %ret; } sub process_drinks { # get the query string my $qs = shift @_; # more contains 'on' if it was checked my $more = ($qs->{'more'}) ? 1 : 0; # fill %ret with the return values my %ret = ( drink => $qs->{'drink'}, qty => $qs->{'qty'}, more => $more ); return %ret; } sub main { my %qs= split(/[;&=]/,$ENV{QUERY_STRING}); foreach (values(%qs)) { s/(%[0-9a-f]{2}|\+)/$1 eq '+'?' ': chr(hex(substr($1,1)))/gei; } # initialize a few variables my $page = $qs{'file'}; my $err = 0; my $filename = ""; my $errmsg = ""; # if its the patron page, generate a new session id # otherwise, get the session id my $session_id; my %vars; if (! defined $qs{'session_id'} ) { $session_id = int(rand(100)) . time(); } else { $session_id = $qs{'session_id'}; $filename = ".meal-" . $session_id; %vars = read_hash($filename); } # this is the big important variable. it holds all the information # from the forms. # this is a switch statement for ($page) { if (/patron/) { ($vars{'patrons'}, $err) = process_patron(\%qs); if (! $err) { write_appetizer($session_id); } else { $errmsg = "Number of patrons must be between 1 and 99"; # needs error message write_start($errmsg); } } elsif (/appetizer/) { # validate the appetizer page my %ret = process_appetizer(\%qs); my $amt = 0; # if this isn't the first time through the appetizers page # get the total number of appetizers ordered already... if (defined $vars{'appetizers'}) { foreach my $key (keys %{ $vars{'appetizers'} }) { $amt += $vars{'appetizers'}->{$key}; } } # ...and add the number of appetizers ordered on this iteration $amt += $ret{'qty'}; # if there are more appetizers than people, give an error... if ($amt > $vars{'patrons'}) { $errmsg = "Total quantity of appetizers must be between" . " 0 and " . $vars{'patrons'}; write_appetizer($session_id, $errmsg); } # ...otherwise, save the data and display the appropriate page else { # these are just for syntactical simplicity my $key = $ret{'appetizer'}; my $qty = $ret{'qty'}; # if the appetizers reference isn't there... if (! defined $vars{'appetizers'}) { # ...create it... $vars{'appetizers'} = {}; # ...and initialize it $vars{'appetizers'}->{'garlic bread'} = 0; $vars{'appetizers'}->{'bruschetta'} = 0; $vars{'appetizers'}->{'hummus'} = 0; } # save the appetizer data $vars{'appetizers'}->{$key} += $qty; # if they want to add more appetizers... if ($ret{'more'} == 1) { # ...write out the appetizer page write_appetizer($session_id); } # otherwise... else { # write out the entree page write_entree($session_id); } } } elsif (/entree/) { # validate the entree data my %ret = process_entree(\%qs); my $amt = 0; # if this isn't the first time through the entree page # total up the amount of entrees ordered so far... if (defined $vars{'entrees'}) { foreach my $key (keys %{ $vars{'entrees'} }) { $amt += $vars{'entrees'}->{$key}; } } # take off the salads..we don't count them $amt -= $vars{'entrees'}->{'salad'}; # ...and add the amount ordered in this iteration $amt += $ret{'qty'}; print STDERR "amt: " . $amt . "\n"; # if the amount of entrees is not the same as number of patrons # and they are not going to add more... if ($amt != $vars{'patrons'} && $ret{'more'} == 0) { # give 'em a nice error message and send 'em back. $errmsg = "Total number of entrees must be " . $vars{'patrons'}; write_entree($session_id, $errmsg); } else { # for simplicity my $key = $ret{'entree'}; my $qty = $ret{'qty'}; # if the entree hash in not defined, if (! defined $vars{'entrees'}) { # declare it... $vars{'entrees'} = {}; # and initialize it $vars{'entrees'}->{'salad'} = 0; $vars{'entrees'}->{'spaghetti'} = 0; $vars{'entrees'}->{'lasagna'} = 0; $vars{'entrees'}->{'pizza'} = 0; } # save the data $vars{'entrees'}->{$key} += $qty; $vars{'entrees'}->{'salad'} += $qty * $ret{'salad'}; # if they want to add more, write out the entree page if ($ret{'more'} == 1) { write_entree($session_id); } # otherwise, write out the drinks page else { write_drinks($session_id); } } } elsif (/drink/) { # validate the data my %ret = process_drinks(\%qs); my $amt = 0; # if this isn't the first time through the drinks page # get the total number of drinks ordered already.... if (defined $vars{'drinks'}) { foreach my $key (keys %{ $vars{'drinks'} }) { $amt += $vars{'drinks'}->{$key}; } } # ...and add the number of drinks ordered on this iteration $amt += $ret{'qty'}; # drinks must be between number of patrons and number of patrons*3 # otherwise, give an error if (($amt < $vars{'patrons'} || $amt > ($vars{'patrons'} * 3)) && $ret{'more'} == 0 ) { $errmsg = "Total quantity of drinks must be between " . $vars{'patrons'} . " and " . $vars{'patrons'} * 3; write_drinks($session_id, $errmsg); } # otherwise, save the data and display the appropriate page else { # just for convienence my $key = $ret{'drink'}; my $qty = $ret{'qty'}; # if the drinks reference isn't there... if (! defined $vars{'drinks'}) { # ...create it... $vars{'drinks'} = {}; # ...and initialize it... $vars{'drinks'}->{'soft drink'} = 0; $vars{'drinks'}->{'beer'} = 0; $vars{'drinks'}->{'liquor'} = 0; $vars{'drinks'}->{'wine'} = 0; } # save the drink data $vars{'drinks'}->{$key} += $qty; # we're nearing the end, so make up the total $vars{'subtotal'} = $vars{'appetizers'}->{'garlic bread'} * $GB_PRICE + $vars{'appetizers'}->{'bruschetta'} * $BRUSCHETTA_PRICE + $vars{'appetizers'}->{'hummus'} * $HUMMUS_PRICE + $vars{'entrees'}->{'spaghetti'} * $SPAGHETTI_PRICE + $vars{'entrees'}->{'lasagna'} * $LASAGNA_PRICE + $vars{'entrees'}->{'pizza'} * $PIZZA_PRICE + $vars{'entrees'}->{'salad'} * $SALAD_PRICE + $vars{'drinks'}->{'soft drink'} * $SOFT_DRINK_PRICE + $vars{'drinks'}->{'beer'} * $BEER_PRICE + $vars{'drinks'}->{'liquor'} * $LIQUOR_PRICE + $vars{'drinks'}->{'wine'} * $WINE_PRICE; $vars{'food_subtotal'} = $vars{'subtotal'} - $vars{'drinks'}->{'beer'} * $BEER_PRICE - $vars{'drinks'}->{'liquor'} * $LIQUOR_PRICE - $vars{'drinks'}->{'wine'} * $WINE_PRICE; $vars{'liquor_subtotal'} = $vars{'drinks'}->{'beer'} * $BEER_PRICE + $vars{'drinks'}->{'liquor'} * $LIQUOR_PRICE + $vars{'drinks'}->{'wine'} * $WINE_PRICE; $vars{'total'} = $vars{'subtotal'} + $vars{'subtotal'} * $GST + $vars{'food_subtotal'} * $PST + $vars{'liquor_subtotal'} * $LST; # if they want to add more... if ($ret{'more'} == 1) { write_drinks($session_id); } # otherwise.. else { write_subtotal($session_id, \%vars); } } } elsif (/subtotal/) { $vars{'tip'} = $qs{'tip'}; write_total(\%vars); } } # write the vars out to the file, whee. $filename = ".meal-" . $session_id; write_hash($filename, \%vars); } main();