#!/usr/bin/perl # Filename: former # Author: David Ljung Madison # See License: http://MarginalHacks.com/License # Version: 1.00 # Description: Creates self-validating form CGI code # See ePerl: http://MarginalHacks.com/Hacks/ePerl use strict; ################################################## # Setup the variables ################################################## my $PROGNAME = $0; $PROGNAME =~ s|.*/||; ################################################## # Usage ################################################## sub usage { my $msg; foreach $msg (@_) { print "ERROR: $msg\n"; } print "\n"; print "Usage:\t$PROGNAME [-d] \n"; print "\tGenerates form perl code from a .fd (form description)\n"; print "\t-d\tSet debug mode\n"; print "\n"; exit -1; } sub parse_args { my $file; while ($#ARGV>=0) { my $arg=shift(@ARGV); if ($arg =~ /^-h$/) { usage(); } if ($arg =~ /^-d$/) { $MAIN::DEBUG=1; next; } if ($arg =~ /^-/) { usage("Unknown option: $arg"); } usage("Too many files specified [$arg and $file]") if (defined($file)); $file=$arg; } usage("No file defined") if (!defined($file)); $file; } sub pfatal { die("[$PROGNAME, line $.] @_\n"); } sub fatal { die("[$PROGNAME] @_\n"); } ################################################## # Read form descriptor ################################################## # We start/end forms with:
..
# # And fill it with fields: # # name;Prompt;type type_opts;[err msg];checking expression # # And selects/radio buttons: # # # Example: #
# form_number;1;hidden # first_name;First Name:;input size='25' maxsize='100';1; # email;Email:;input size='25' maxsize='100';/\S\@\S+\.\S+/;email must be of form login@somewhere.com # cost;Cost:;input size='5' maxsize='10';/^\d+$/ && $_;cost must be a number # # # red;Red;$query->{first_name} =~ /mike/;Only people named Mike like the color red # green;Green # blue;Of course, Blue is my favorite color # # next;Next page;submit #
# # "checking expression" of 1 means the field needs any string entry # ################################################## sub read_fd { my ($fd) = @_; open(FILE,$fd) || usage("Couldn't open file: $fd"); my %forms; my ($form,$select,$radio,$srprompt); while() { s/#.*//; # Ignore comments s/^\s+//; s/\s+$//; next if /^$/; # and whitespace # Start a form? unless ($form) { pfatal("Expected
") unless (/^$/); $form = $1; pfatal("Saw form [$form] twice") if ($forms{$form}); next; } # Start or stop something? if (/^<(\/)?(\S+)(\s+(\S.*))?>$/) { my ($stop,$what,$name) = ($1 ? 1 : 0, $2, $4); if ($stop) { if ($what eq "select") { pfatal("Saw when not in ") if ($select); fatal("Never saw ") if ($radio); undef $form; } next; } $srprompt = $name; ($name,$srprompt) = ($1,$2) if $name =~ /(.+);(.+)/; pfatal("No name for <$what>") unless $name; pfatal("Didn't finish when entering when entering ") if ($what eq "radio" && $select); $select = $name if ($what eq "select"); $radio = $name if ($what eq "radio"); next; } if ($select || $radio) { my @a = split(/;/,$_,4); my %f; ($f{name},$f{string},$f{check},$f{err}) = @a; $f{string} =~ s/([\$])/\\$1/g; $f{type} = $select ? "select" : "radio"; $f{type_opt} = $select || $radio; $f{prompt} = $srprompt; push(@{$forms{$form}}, \%f); next; } my @a = split(/;/,$_,5); pfatal("Couldn't parse line:\n $_") if $#a < 2; my %f; ($f{name},$f{prompt},$f{type_str},$f{check},$f{err}) = @a; ($f{type},$f{type_opt}) = split(/\s+/,$f{type_str},2); pfatal("Unknown field type [$f{type}]") unless (grep($f{type} eq $_, ("input","hidden","submit"))); # Add to the forms array push(@{$forms{$form}}, \%f); } close(FILE); fatal("Never saw ") if ($select); fatal("Never saw ") if ($radio); fatal("Never saw ") if ($form); \%forms; } sub display_forms { my ($forms) = @_; foreach my $form ( keys %$forms ) { print "FORM: $form\n"; foreach my $field ( @{$forms->{$form}} ) { print " $field->{type}: \"$field->{prompt}\"\t\t[$field->{name}]\n"; } } } ################################################## # Write the CGI/HTML ################################################## sub do_print { my ($str) = @_; $str =~ s/"/\\"/g; $str =~ s/\n/\\n/g; print " print \"$str\";\n"; } sub write_utils { my ($forms) = @_; print <<'END_UTILS'; # Unquote form data sub unhtml { my ($str) = @_; $str =~ s/%([0-9a-f]{2})/chr(hex($1))/eig; $str =~ s/\+/ /g; $str; } # Make strings safe for form values sub html_safe { my ($str) = @_; $str =~ s/"/"/g; $str; } sub parse_query { my $QUERY_STRING; if ($ENV{REQUEST_METHOD} eq "POST") { read(STDIN,$QUERY_STRING,$ENV{CONTENT_LENGTH}); } else { $QUERY_STRING = $ENV{QUERY_STRING}; } chomp($QUERY_STRING); # $QUERY_STRING is of the form: "variable=value&var2=val2&.." my @querys=split(/[\&\?]/,$QUERY_STRING); my (%query,$var,$val); foreach my $str (@querys) { $var=$str if (!(($var,$val) = ($str =~ /([^=]*)=(.*)/))); $val=unhtml($val); $query{$var}=$val; } \%query; } END_UTILS # Write checkers for each form foreach my $form ( keys %$forms ) { print "sub check_form_$form {\n"; print " my (\$query) = \@_;\n"; print " my \@bad;\n"; my $last_select; foreach my $field ( @{$forms->{$form}} ) { my $chk = $field->{check}; next unless $chk || $chk eq "0"; $chk = "/\\S/" if ($chk == 1); my $name = $field->{name}; my $err = $field->{err}; $err =~ s/\@/\\@/g; $err =~ s/\$/\\\$/g; if ($field->{type} eq "select" || $field->{type} eq "radio") { $name = $field->{type_opt}; if ($last_select ne $name) { print " push(\@bad,[$name,\"Must select one of the $field->{prompt} options\"])\n"; print " unless (\$query->{$name});\n"; $last_select = $name; } print " \$_ = \$query->{$name}; push(\@bad,[$name,\"$err\"])\n"; print " unless (\$_ ne $field->{name} || $chk);\n"; } else { print " \$_ = \$query->{$name}; push(\@bad,[$name,\"$err\"])\n"; print " unless ($chk);\n"; } } # Print errors? print " return unless \@bad;\n"; do_print "

Error: fields are missing or invalid (marked in red)

\n"; print " my \@bad_fields;\n"; print " foreach ( \@bad ) {\n"; print " push(\@bad_fields,\$_->[0]);\n"; print " print \"
  • \$_->[1]\\n\" if (\$_->[1]);\n"; print " }\n"; print " \@bad_fields;\n"; print "}\n\n"; } } sub write_forms { my ($forms) = @_; foreach my $form ( keys %$forms ) { my ($select,$radio); print "sub form_$form {\n"; print " my (\$query,\@bad_fields) = \@_;\n"; print " my \%did_fields;\n"; do_print "
    \n"; #do_print " \n"; do_print "
    \n"; foreach my $field ( @{$forms->{$form}} ) { if ($select && $field->{type} ne "select") { do_print " \n"; undef $select; } undef $radio if ($radio && $field->{type} ne "radio"); if ($field->{type} eq "input") { print " \$did_fields{$field->{name}} = 1;\n"; do_print " \n"; print " if (grep($field->{name} eq \$_, \@bad_fields)) {\n"; do_print " \n"; print " } else {\n"; do_print " \n"; print " }\n"; do_print " \n"; do_print " \n"; } elsif ($field->{type} eq "submit" || $field->{type} eq "hidden") { print " \$did_fields{$field->{name}} = 1;\n"; do_print " \n"; do_print " \n"; do_print " \n"; } elsif ($field->{type} eq "select") { unless ($select) { print " \$did_fields{$field->{type_opt}} = 1;\n"; do_print " \n"; $select = $field->{type_opt}; print " if (grep($select eq \$_, \@bad_fields)) {\n"; do_print " \n"; print " } else {\n"; do_print " \n"; print " }\n"; do_print " \n"; $radio = $field->{type_opt}; print " if (grep($radio eq \$_, \@bad_fields)) {\n"; do_print " \n"; print " } else {\n"; do_print " \n"; print " }\n"; do_print "
    $field->{prompt}$field->{prompt}<$field->{type} name='$field->{name}' value="; print " print '\"'.html_safe(\$query->{$field->{name}}).'\"';\n"; do_print " $field->{type_opt}>
    {type} name='$field->{name}' value='$field->{prompt}' $field->{type_opt}>
    $field->{prompt}$field->{prompt}\n"; do_print "
    $field->{prompt}$field->{prompt}\n"; } do_print " {$radio} eq \"$field->{name}\");\n"; do_print "> $field->{string}\n"; } else { fatal("write_forms() Unknown field type?? [$field->{type}]"); } } do_print "
    \n"; # Add hidden any fields that were in the query that weren't in the form print " foreach my \$k ( keys \%\$query ) {\n"; print " next if (\$did_fields{\$k});\n"; do_print " {\$k}).'\"';\n"; do_print ">\n"; print " }\n"; do_print "
    \n"; print "}\n\n"; } } ################################################## # Main code ################################################## sub main { my $fd = parse_args(); my $forms = read_fd($fd); #display_forms($forms); write_utils($forms); write_forms($forms); print "\n1;\n"; } main();