#!/usr/bin/perl #--------------- # # FreeCheck - a free check printing application released # under the GNU General Public Licene. # # Copyright (C) 2000 Eric Sandeen (eric_sandeen@bigfoot.com) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # #--------------- $version = "0.2"; $ConfigFile = "freecheck.cfg"; use Getopt::Long; # This tells us how to format the strings from the cfg file # so we can print it as a PostScript definition # The key read will replace "value" for each of these # Strings are enclosed in parentheses (String) (Foobar) # Fonts are preceded by a "/" /FontName /Arial # Digits are fine as they are Digit 123 # Booleans are fine too Bool true # But to be safe, do digits and bools as subroutines: # Subroutines are in {} {subr} {3 mul} %Formats = qw( # Globals MICRFontName /value MICRFontSize {value} TransitSymbol (value) OnUsSymbol (value) AmountSymbol (value) DashSymbol (value) MICRVerTweak {value} MICRHorTweak {value} # Account CheckNumber {value} PrintCheckBody {value} PrintMICRLine {value} NumPages {value} Name1 (value) Name2 (value) Address1 (value) Address2 (value) CityStateZip (value) PhoneNumber (value) BankName (value) BankAddr1 (value) BankAddr2 (value) BankCityStateZip (value) RoutingNumber (value) AccountNumber (value) Fraction (value) PrintVOID {value} # Styles StandardFontName /value StandardFontSize {value} CheckNumDigits {value} CheckNumFont /value CheckNumSize {value} MemoLineHeight {value} SignatureLineHeight {value} BankInfoHeight {value} AmountLineHeight {value} PayeeLineHeight {value} DateLineHeight {value} # Check Blank Types CheckHeight {value} CheckWidth {value} CheckHorOffset {value} CheckVerOffset {value} ChecksPerPage {value} LeftMargin {value} RightMargin {value} TopMargin {value} ); # Parse command line options and deal with them: GetOptions ("account:s", # Account definition file "checknum:i", # Check number optional (overrides acct file) "pages:i", # Number of pages to print "checkstyle:s", # Check style (defaults to "normal_style.ps" "checktype:s", # Check blank definition "nomicr", # Prevents MICR line from printing (body only) "nobody", # Prevents body from printing (MICR line only) "showaccounts", # Show available accounts "showstyles", # Show available check styles "showblanks", # Show available check blanks "test", # Don't increment check n, and print VOID "cgi:s", # Accept big string from CGI script (or similar) "help") or Show_Usage(); if ($opt_help) { Show_Usage(); } # Some defaults... if (!$opt_cgi) { $opt_account = "sample"; $opt_checktype = "MVG3001"; $opt_checkstyle = "normal"; } # Pull the config file into a string... $config_file = read_file($ConfigFile); # See what sections are available Get_Sections(); # If we're missing the [Global] section, or if a requested section # cannot be found, die. if (!$global_found) { die ("No [Global] section found in config file\n"); } if ($accounts !~ /${opt_account}/i) { die ("Account $opt_account not found in config file\n"); } if ($checkblanks !~ /$opt_checktype/i) { die ("Check type $opt_checktype not found in config file\n"); } if ($checkstyles !~ /$opt_checkstyle/i) { die ("Style $opt_checkstyle not found in config file\n"); } # Show list of available sections, if requested if ($opt_showaccounts || $opt_showstyles || $opt_showblanks) { print "\nFreeCheck v$version\n"; if ($opt_showaccounts) { print "Accounts:\n"; foreach (split(/\s+/,$accounts)) { print "\t$_\n"; } } if ($opt_showstyles) { print "Check Styles:\n"; foreach (split(/\s+/,$checkstyles)) { print "\t$_\n"; } } if ($opt_showblanks) { print "Check Types:\n"; foreach (split(/\s+/,$checkblanks)) { print "\t$_\n"; } } die("\n"); } # Go through the config and fill up a hash with PostScript defines... Parse_Config($config_file); # Overwrite anything we got from the config file with what was on the # Command Line (if anything...) if ($opt_checknum) { $Definitions{"CheckNumber"} = $opt_checknum; } if ($opt_pages) { $Definitions{"NumPages"} = $opt_pages; } if ($opt_nomicr) { $Definitions{"PrintMICRLine"} = "false"; } if ($opt_nobody) { $Definitions{"PrintCheckBody"} = "false"; } # This probably isn't in the config file (although it might be...) # so cover both possibilites (true/false) if ($opt_test) { $Definitions{"PrintVOID"} = "true"; } else { $Definitions{"PrintVOID"} = "false"; } # the --cgi option lets us pass in name value pairs in a string. # This will overwrite anything we got from the config file, or # from the other command line options (--cgi has the last word) # Parse as follows: # name is the first word, everything following it is the value # each line contains one name/value pair. while ( $opt_cgi =~ /(^\w+)\s+?(.*$)/mcg ) { $Definitions{$1} = $2; } # If there's a dash in the account string, replace it with the dash symbol $Definitions{"AccountNumber"} =~ s/\-/$Definitions{"DashSymbol"}/g; # Print PostScript # Initial stuff: print "%!\n"; print "/inch {72 mul} def\n"; # Go through $Definitions and print them out PostScript-Like Print_Defs(); # Then print the main body Print_Body(); # Update the config file with the new check number, if it's not just a test if (!$opt_test) { $next_check_number = $Definitions{"CheckNumber"} + ($Definitions{"NumPages"} * $Definitions{"ChecksPerPage"}); $config_file = Replace_Val($config_file, "Account", $opt_account, "CheckNumber", $next_check_number); write_file ("freecheck.cfg", $config_file); } ############### # Subroutines # ############### # read_file and write_file shamelessly stolen from the File::Slurp module # Short enough, and I didn't want to require a non-standard module sub read_file { my ($file) = @_; local(*F); my $r; my (@r); open(F, "<$file") || die "open $file: $!"; @r = ; close(F); return @r if wantarray; return join("",@r); } sub write_file { my ($f, @data) = @_; local(*F); open(F, ">$f") || die "open >$f: $!"; (print F @data) || die "write $f: $!"; close(F) || die "close $f: $!"; return 1; } # Wow, this is ugly! Anybody have a better suggestion? sub Parse_Config { local ($config_file) = ($_[0]); # Find each section we're looking for... while ($config_file =~ /^\[\s*( Global | Account\s+${opt_account} | Style\s+${opt_checkstyle} | CheckBlank\s+${opt_checktype} )\s*\]/xmgci) { # and get the lines under it one by one while ($config_file =~ /(^.+$)/mgc) { $line = $+; # If this line is a comment, skip it if ($line =~ /^#/) { next; } # If the line we just found is a new section..."[...]" if ($line =~ /^\[.+\]/) { # and it is another section we're looking for # Grab the next line, and keep going if ($line =~ /\[\s*( Global | Account\s+${opt_account} | Style\s+${opt_checkstyle} | CheckBlank\s+${opt_checktype} )\s*]/xi) { # Grab the next line, and keep going next; } else { # Not a section we need, so break out # of the loop last; } } ($key, $val) = split (/\s*=\s*/,$line); # Need to strip trailing whitespace... $val =~ s/\s*$//; $Definitions{$key} = $val; } # line-by-line while } # section match conditional } sub Replace_Val { local ($string, $section, $name, $key, $value) = ($_[0], $_[1], $_[2], $_[3], $_[4]); # We want to get "[section name] ... key = value" and replace it # with the new value. # s - "." matches ANYTHING including newline # m - ^ and $ match after and before any newline # in this case, ".+?" means the minimum number of i.e. end # when we find the first instance of $key after [section name] $string =~ s/(^\[\s*$section\s+$name\s*\].+?^${key}\s*=\s*).*?$/$+$value/smi; $string; } # Given a section type, list all the section names of that type sub Get_Sections { local $section; while ($config_file =~ /^\[\s*( Global | Account.+ | Style.+ | CheckBlank.+ )\s*\]/xmgci) { $section = $+; if ( $section =~/CheckBlank\s+(.+)/i ) { $checkblanks = "$+ $checkblanks"; } elsif ( $section =~/Style\s+(.+)/i ) { $checkstyles = "$+ $styles"; } elsif ( $section =~/Account\s+(.+)/i ) { $accounts = "$+ $accounts"; } elsif ( $section =~/Global/i ) { $global_found = "true"; } } } sub Show_Usage { print "\nFreeCheck v.$version - a Free Check printing Utility\n\n"; print "Usage: freecheck :\n"; print "\n"; print "options:\n"; print " --account account to use (default \"$opt_account\")\n"; print " --checknum starting check number (overrides cfg)\n"; print " --pages number of pages to print (overrides cfg)\n"; print " --checkstyle check style to use (default \"$opt_checkstyle\")\n"; print " --checktype blank check type to use (default \"$opt_checktype\")\n"; print " --nomicr do not print MICR line\n"; print " --nobody do not print check body\n"; print " --showaccounts show all configured accounts\n"; print " --showstyles show all configured check styles\n"; print " --showblanks show all configured check blanks\n"; print " --help print this message\n"; print " --test print but don't increment check number\n"; print " and print VOID on the check\n"; print " --cgi accept string from CGI script (see docs)\n"; print "\nconfig file \"freecheck.cfg\" must be in the same directory,\n"; print "as the freecheck executable (this will change in the future...)\n"; die "\n"; } sub Print_Defs { # Go through each def in the hash table, and print according to the # formatting hash while ( ($key, $val) = each (%Definitions) ) { print "/$key\t"; $_ = $Formats{$key}; s/value/$val/; print; print " def\n"; } } sub Print_Body { # This is the main body of the postscript file, that acts on all of the # definitions we got from the config file. print <