use strict; use warnings; use Scalar::Util qw( reftype ); #use IO::Prompt; # half-assed prompt routine to stand in for the nonportable IO::Prompt sub prompt { print join "\n", @_; print "\n"; my $in = <>; $in = "\e" if $in =~ /ESC/; chomp $in; return 0 if uc($in) eq 'N'; return $in; } # return to 'real' code. # Field definitions to reduce repeated code. # This could also be stored as an AoA # ( [ id => 'Product ID' ], # [ comments => 'Comments' ], # ); my @fields = ( { name => 'id', description => 'Product ID', }, { name => 'comments', description => 'Comments', }, # And so forth. In the order you want to process them. ); # It looks like your code was in a sub rountine call. # So I enclosed my code in a subroutine as well. Get_Data_For_DB( \@fields ); # the main sub sub Get_Data_For_DB { my $fields = shift; # Get field specification my @database = read_from_db(); my $database_type = 'foo'; # give this variable a value. while ( collect_user_data( \@database, $fields ) ) { last unless continue_data_entry(); } # finalize changes and return result. return write_to_db($database_type, @database); } # Attempt to get data from user. # Return TRUE if we have data or a SKIP condition # Return FALSE on an ABORT condition. sub collect_user_data { my $db = shift; my $fields = shift; my $entry = get_data_entry( $fields ); # this should probably be replaced with # a dispatch table if any more conditions get added. if ( reftype $entry eq 'ARRAY' ) { push @$db, $entry; return 1; } elsif ( $entry eq 'SKIP' ) { return 1; } elsif ( $entry eq 'ABORT' ) { return; } die "Unexpected result: $entry"; } # Get one entry worth of data. # If entry succeeds, return an array ref containing data. # Else return a status string- # 'ABORT' to abort data entry # 'SKIP' to store no new data in db. sub get_data_entry { my $fields = shift; # Initial entry my @values; # Ordered list. foreach my $field ( @$fields ) { my $value = prompt "Enter the $field->{description}: ", '-escape'; return 'ABORT' if $value eq "\e"; # what happens if user types "foo\e" # Store data for return. push @values, $value; } # Confirmation - print field/value pairs print "Confirm the following data:\n"; print map { " $fields->[$_]{description}: $values[$_]\n" } 0..$#$fields; print "\n"; my $yn = prompt "Is this information correct? y/n: ", '-yn'; return $yn ? \@values : 'SKIP'; } # Prompt user for continuing entry. # Return TRUE for Y # Return FALSE for N or ESC sub continue_data_entry { my $continue = prompt "Add another entry? y/n: ", '-yn', '-escape'; $continue = 0 if $continue eq "\e"; return $continue; } # Dummy routine for test puproses. sub read_from_db { return; } # Dummy routine for test puproses. sub write_to_db { my $type = shift; use Data::Dumper; print Dumper \@_; }