#!/usr/bin/perl -wT # Copyright 2010 Emma Rath. All rights reserved. # Soon this program will be released under an open source software license such as GNU General Public License or # Creative Commons license for Free Software Foundation's GNU General Public License at creativecommons.org # perl -T HBPLUSchains.pl -infile HBPLUS_output.hb2 # This perl program runs as a as a cgi script on a web server that displays web pages. # This perl program also runs as a batch program on the command line. # This program reads in an HBPLUS output file (*.hb2) # and writes out only those hydrogen bonds that are between 2 atoms of 2 different chains. # This program also writes out the PyMOL commands that will select those different-chain hydrogen bond atoms. # An example of HBPLUS output that is input to this program : # HBPLUS Hydrogen Bond Calculator v 3.2 Aug 23 14:17:47 EST 2010 # (c) I McDonald, D Naylor, D Jones and J Thornton 1993 All Rights Reserved. # Citing HBPLUS in publications that use these results is condition of use. # <- Brookhaven Code "SDH_HUMAN.pdb" <- PDB file # <---DONOR---> <-ACCEPTOR--> atom ^ # c i cat <-CA-CA-> ^ H-A-AA ^ H- # h n atom resd res DA || num DHA H-A angle D-A-AA Bond # n s type num typ dist DA aas dist angle dist angle num # A0052-SER N A0497-ASP OD2 2.84 MS 445 7.00 106.8 2.38 152.7 151.9 1 # A0055-TYR N A0052-SER O 2.70 MM 3 5.57 158.2 1.75 127.3 131.3 2 # A0500-ARG NH2 A0055-TYR OH 2.70 SS 445 11.79 103.9 2.28 113.4 109.0 3 # A0058-VAL N A0244-ILE O 2.87 MM 186 6.32 151.0 1.96 149.6 159.1 4 # A0246-ARG N A0058-VAL O 2.87 MM 188 6.24 150.5 1.96 158.2 168.1 5 # A0246-ARG NE A0059-ASP OD1 2.66 SS 187 5.10 101.4 2.28 96.4 114.9 6 # A0060-HIS N A0246-ARG O 3.10 MM 186 6.16 165.6 2.12 138.3 142.1 7 # A0248-ARG N A0060-HIS O 3.14 MM 188 6.32 167.3 2.16 145.9 143.2 8 # A0060-HIS NE2 A0245-HIS ND1 2.84 SS 185 7.14 154.1 1.90 120.7 118.7 9 # A0245-HIS ND1 A0060-HIS NE2 2.84 SS 185 7.14 138.8 2.01 133.9 115.3 10 # A0062-PHE N A0248-ARG O 2.97 MM 186 6.24 160.4 2.01 163.6 168.9 11 # A0063-ASP N A0086-ASN O 2.87 MM 23 5.10 139.1 2.03 142.7 155.2 12 # A0250-LYS N A0063-ASP O 2.93 MM 187 5.39 132.9 2.16 124.9 135.1 13 # A0251-ASN N A0063-ASP O 3.29 MM 188 6.71 148.1 2.40 144.7 153.7 14 # A0086-ASN N A0063-ASP OD2 3.19 MS 23 5.10 166.4 2.21 104.9 104.7 15 # A0064-ALA N A0086-ASN O 3.30 MM 22 6.56 153.1 2.37 143.0 145.7 16 # A0088-ALA N A0064-ALA O 2.96 MM 24 6.32 154.7 2.02 148.0 155.7 17 # A0065-VAL N A0251-ASN O 2.95 MM 186 6.40 145.3 2.08 144.6 155.3 18 # B0170-TYR N A0154-GLU OE1 2.77 MS -1 7.07 171.1 1.78 111.2 109.9 155 # A0162-ARG NH2 A0154-GLU OE2 2.96 SS 8 11.09 164.3 1.98 112.2 113.3 156 # B0223-ARG NH1 A0154-GLU OE2 2.99 SS -1 10.05 165.2 2.01 128.9 124.0 157 # A0155-ASN ND2 B0168-GLN O 3.28 SM -1 7.87 139.3 2.45 128.1 137.2 158 # B0223-ARG NH2 A0157-GLY O 3.04 SM -1 7.75 117.2 2.45 120.7 115.5 159 # A0160-PHE N A0158-MET O 2.90 MM 2 5.74 121.4 2.25 115.6 98.5 160 # B0223-ARG NH1 A0158-MET O 3.02 SM -1 10.49 176.6 2.02 121.2 121.8 161 # B0177-ARG NE A0159-PRO O 3.06 SM -1 7.42 124.5 2.38 100.1 103.4 162 # B0177-ARG NH2 A0159-PRO O 2.94 SM -1 7.42 128.6 2.21 157.7 143.3 163 # B0177-ARG NE A0160-PHE O 3.04 SM -1 8.19 137.3 2.22 101.1 113.6 164 # B0046-ARG NE B0061-TYR OH 3.32 SS 15 11.96 167.6 2.34 152.3 150.0 625 # B0046-ARG NH1 B0138-ASP OD1 2.66 SS 92 4.36 132.3 1.89 104.2 97.6 626 # B0046-ARG NH2 B0084-ASP OD2 3.23 SS 38 11.83 155.6 2.29 144.7 139.9 627 # B0046-ARG NH2 B0086-THR OG1 2.70 SS 40 9.75 92.2 2.47 116.2 132.7 628 use warnings; use strict; use diagnostics; use Getopt::Long; use Math::Complex; use Math::Trig; use CGI; use Data::Dumper; # $Data::Dumper::Purity = 1; # print "var x = " . Dumper( $x ) . "\n"; # debug # my $wait_for_key_press = ; # debug my $q = new CGI; my $global; my $input; my $output; my $debug = ''; #=================================================================================================== #================================= MAIN ================================== #=================================================================================================== init(); get_program_mode(); if ($global->{'program_mode'} eq 'cgi-display-form') { display_html_form(); } elsif ($global->{'program_mode'} eq 'cgi-output') { get_html_input(); if ($global->{'got_error'} == 0) { build_and_output_results_for_html_output(); } else { display_html_form(); } } else { # $global->{'program_mode'} eq 'command-line' get_command_line_input(); if ($global->{'got_error'} == 0) { my $untainted_infile = untaint( $input->{'infile'} ); if ($untainted_infile eq '') { $untainted_infile = "HBPLUSchains"; } my $output_file = $untainted_infile . ".txt"; open( OUTFILE, ">$output_file") or die "Cannot open $output_file for writing\n"; build_and_output_results_for_command_line(); close OUTFILE; } else { output_command_line_help(); } } #=================================================================================================== # this is the main logic subroutine of this program for command-line mode. # given an input file, process the input and produce the output. #=================================================================================================== sub build_and_output_results_for_command_line { build_the_output(); print "HBPLUS lines of the different-chain hydrogen bond atoms only :\n"; print "\n"; my @output_lines = @{$output->{'output_lines'}}; foreach my $line (@output_lines) { print OUTFILE "$line\n"; } print "\n"; print "PyMOL commands to select the different-chain hydrogen bond atoms :\n"; print "\n"; my @chain_string = @{$output->{'chain_string'}}; foreach my $chain (@chain_string) { print OUTFILE "$chain\n"; } print "\n"; } #=================================================================================================== # this is the main logic subroutine of this program for cgi-output mode. # given an input file, process the input and produce the output. #=================================================================================================== sub build_and_output_results_for_html_output { build_the_output(); if ($input->{'whereto'} eq 'S') { # write output to the screen print $q->header(); print #$q->start_html(-title=>$text_title), "\n", '' . "\n", "\n"; print "HBPLUS lines of the different-chain hydrogen bond atoms only :
\n"; print "
\n"; print "\n"; my @output_lines = @{$output->{'output_lines'}}; foreach my $line (@output_lines) { $line =~ s/ / /g; $line = "$line
\n"; print $line; } print "
\n"; print "
\n"; print "PyMOL commands to select the different-chain hydrogen bond atoms :
\n"; print "
\n"; my @chain_string = @{$output->{'chain_string'}}; foreach my $chain (@chain_string) { print "$chain

\n"; } print "
\n"; print "\n"; } else { # $input->{'whereto'} eq 'F' # write output to a file to be downloaded print $q->header("Content-type: text/plain"); my @output_lines = @{$output->{'output_lines'}}; print "HBPLUS lines of the different-chain hydrogen bond atoms only :\n"; print "\n"; foreach my $line (@output_lines) { print "$line\n"; } print "\n"; print "PyMOL commands to select the different-chain hydrogen bond atoms :\n"; print "\n"; my @chain_string = @{$output->{'chain_string'}}; foreach my $chain (@chain_string) { print "$chain\n"; } print "\n"; } if ($debug ne '') { print "$debug\n"; } } #=================================================================================================== # move the input STRIDE file contents to output, reformatting it to PDB format #=================================================================================================== sub build_the_output { my @input_lines = @{$input->{'input_lines'}}; my @output_lines; $output->{'output_lines'} = \@output_lines; my $in_data_lines = 0; my $heading_line = 'n s type num typ dist DA aas dist angle dist angle num'; my @chain; my @chain_string; $output->{'chain_string'} = \@chain_string; for (my $i = 0; $i < @input_lines; $i++) { my $line = $input_lines[$i]; my $trimmed_line = trim($line); if ($in_data_lines == 0) { if ($line eq $heading_line) { $in_data_lines = 1; } } else { # $in_data_lines == 1 my $chain1 = substr( $line, 0, 1 ); my $resi1 = sprintf("%1d", substr( $line, 2, 4 )); my $name1 = trim(substr( $line, 10, 3 )); my $chain2 = substr( $line, 14, 1 ); my $resi2 = sprintf("%1d", substr( $line, 15, 4 )); my $name2 = trim(substr( $line, 24, 3 )); if ($chain1 ne $chain2) { push( @output_lines, $line ); my $chain1_string = "(chain $chain1 and resi $resi1 and name $name1)"; my $chain2_string = "(chain $chain2 and resi $resi2 and name $name2)"; my $found_chain1 = -1; my $found_chain2 = -1; for (my $j = 0; $j < @chain; $j++ ) { if ($chain[$j] eq $chain1) { $found_chain1 = $j; $chain1_string = $chain_string[$j] . " or " . $chain1_string; } if ($chain[$j] eq $chain2) { $found_chain2 = $j; $chain2_string = $chain_string[$j] . " or " . $chain2_string; } } if ($found_chain1 == -1) { push( @chain, $chain1 ); $found_chain1 = $#chain; } if ($found_chain2 == -1) { push( @chain, $chain2 ); $found_chain2 = $#chain; } $chain_string[$found_chain1] = $chain1_string; $chain_string[$found_chain2] = $chain2_string; } } } } #=================================================================================================== # this program is being called to display the HTML input form #=================================================================================================== sub display_html_form { html_header(); my $request_uri = $ENV{'REQUEST_URI'}; my @bits = split(/\//, $request_uri); my $pgm_name = $bits[$#bits]; print "

\n"; print "
\n", " \n"; if ($global->{'got_error'} == 1) { print " \n", " \n", " \n"; } print " \n", " \n", " \n"; print " \n", " \n", " \n"; print "
\n", "

" . $global->{'err_msg'} . "
Didn't process because of input error(s).

\n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", "This program reads in an HBPLUS output file (*.hb2)
\n", "and writes out only those hydrogen bonds that are between 2 atoms of 2 different chains.
\n", "This program also writes out the PyMOL commands that will select those different-chain hydrogen bond atoms.\n", "
\n", "Please provide an HBPLUS file. See format here.
\n", "
\n", "Upload the HBPLUS file from your computer.
Please note that this HBPLUS file is not validated, so make sure that it is a valid STRIDE file.
\n", "
\n", "
\n", "\n", "\n", "
or
\n", "
\n", "\n", "\n", "\n", "\n", "
Enter the HBPLUS file contents here.
Please note that these HBPLUS file contents are not validated, so make sure that they are valid HBPLUS file contents.
(Provide either a file or enter the contents here, not both.
If both provided, then contents here will be processed instead of uploaded file.)

\n", "
\n", "Display results on screen
\n", "Write results to output file

\n", "
\n", "\n", "
\n", "
\n", "


", ""; print "Here is an example of HBPLUS output that is input to this program :
\n", "
\n", "\n", "HBPLUS Hydrogen Bond Calculator v 3.2            Aug 23 14:17:47 EST 2010
", "(c) I McDonald, D Naylor, D Jones and J Thornton 1993 All Rights Reserved.
", "Citing HBPLUS in publications that use these results is condition of use.
", "     <- Brookhaven Code \"SDH_HUMAN.pdb\" <- PDB file
", "<---DONOR---> <-ACCEPTOR-->    atom                        ^               
", "c    i                          cat <-CA-CA->   ^        H-A-AA   ^      H- 
", "h    n   atom  resd res      DA  || num        DHA   H-A  angle D-A-AA Bond
", "n    s   type  num  typ     dist DA aas  dist angle  dist       angle   num
", "A0052-SER N   A0497-ASP OD2 2.84 MS 445  7.00 106.8  2.38 152.7 151.9     1
", "A0055-TYR N   A0052-SER O   2.70 MM   3  5.57 158.2  1.75 127.3 131.3     2
", "A0500-ARG NH2 A0055-TYR OH  2.70 SS 445 11.79 103.9  2.28 113.4 109.0     3
", "A0058-VAL N   A0244-ILE O   2.87 MM 186  6.32 151.0  1.96 149.6 159.1     4
", "A0246-ARG N   A0058-VAL O   2.87 MM 188  6.24 150.5  1.96 158.2 168.1     5
", "A0246-ARG NE  A0059-ASP OD1 2.66 SS 187  5.10 101.4  2.28  96.4 114.9     6
", "A0060-HIS N   A0246-ARG O   3.10 MM 186  6.16 165.6  2.12 138.3 142.1     7
", "A0248-ARG N   A0060-HIS O   3.14 MM 188  6.32 167.3  2.16 145.9 143.2     8
", "A0060-HIS NE2 A0245-HIS ND1 2.84 SS 185  7.14 154.1  1.90 120.7 118.7     9
", "A0245-HIS ND1 A0060-HIS NE2 2.84 SS 185  7.14 138.8  2.01 133.9 115.3    10
", "A0062-PHE N   A0248-ARG O   2.97 MM 186  6.24 160.4  2.01 163.6 168.9    11
", "A0063-ASP N   A0086-ASN O   2.87 MM  23  5.10 139.1  2.03 142.7 155.2    12
", "A0250-LYS N   A0063-ASP O   2.93 MM 187  5.39 132.9  2.16 124.9 135.1    13
", "A0251-ASN N   A0063-ASP O   3.29 MM 188  6.71 148.1  2.40 144.7 153.7    14
", "A0086-ASN N   A0063-ASP OD2 3.19 MS  23  5.10 166.4  2.21 104.9 104.7    15
", "A0064-ALA N   A0086-ASN O   3.30 MM  22  6.56 153.1  2.37 143.0 145.7    16
", "A0088-ALA N   A0064-ALA O   2.96 MM  24  6.32 154.7  2.02 148.0 155.7    17
", "A0065-VAL N   A0251-ASN O   2.95 MM 186  6.40 145.3  2.08 144.6 155.3    18
", "B0170-TYR N   A0154-GLU OE1 2.77 MS  -1  7.07 171.1  1.78 111.2 109.9   155
", "A0162-ARG NH2 A0154-GLU OE2 2.96 SS   8 11.09 164.3  1.98 112.2 113.3   156
", "B0223-ARG NH1 A0154-GLU OE2 2.99 SS  -1 10.05 165.2  2.01 128.9 124.0   157
", "A0155-ASN ND2 B0168-GLN O   3.28 SM  -1  7.87 139.3  2.45 128.1 137.2   158
", "B0223-ARG NH2 A0157-GLY O   3.04 SM  -1  7.75 117.2  2.45 120.7 115.5   159
", "A0160-PHE N   A0158-MET O   2.90 MM   2  5.74 121.4  2.25 115.6  98.5   160
", "B0223-ARG NH1 A0158-MET O   3.02 SM  -1 10.49 176.6  2.02 121.2 121.8   161
", "B0177-ARG NE  A0159-PRO O   3.06 SM  -1  7.42 124.5  2.38 100.1 103.4   162
", "B0177-ARG NH2 A0159-PRO O   2.94 SM  -1  7.42 128.6  2.21 157.7 143.3   163
", "B0177-ARG NE  A0160-PHE O   3.04 SM  -1  8.19 137.3  2.22 101.1 113.6   164
", "B0046-ARG NE  B0061-TYR OH  3.32 SS  15 11.96 167.6  2.34 152.3 150.0   625
", "B0046-ARG NH1 B0138-ASP OD1 2.66 SS  92  4.36 132.3  1.89 104.2  97.6   626
", "B0046-ARG NH2 B0084-ASP OD2 3.23 SS  38 11.83 155.6  2.29 144.7 139.9   627
", "B0046-ARG NH2 B0086-THR OG1 2.70 SS  40  9.75  92.2  2.47 116.2 132.7   628
", "
\n", "
\n"; print "
\n", "
\n"; html_footer(); } #=================================================================================================== # in command line mode - get the command line options, read the input file. #=================================================================================================== sub get_command_line_input { my $input_file; my $input_h; my $input_help; GetOptions( "infile=s" => \$input_file, "h" => \$input_h, "help" => \$input_help ); if (!defined $input_file) { die "Usage $0 -infile INPUTFILE\n"; } if (defined $input_file) { $input->{'infile'} = $input_file; } if (defined $input_h) { $input->{'h'} = $input_h; } if (defined $input_help) { $input->{'help'} = $input_help; } if ((defined $input_h) || (defined $input_help)) { $global->{'got_error'} = 1; } open INFILE, $input->{'infile'} or die $!; my @input_lines = ; my @new_input_lines; foreach my $line (@input_lines) { chomp($line); push(@new_input_lines, $line); } $input->{'input_lines'} = \@new_input_lines; } #=================================================================================================== # get the input from the html form, so can process the input #=================================================================================================== sub get_html_input { my $params = $q->Vars; if (defined($params->{'whereto'})) { if ($params->{'whereto'} ne '') { my $input_whereto = trim( $params->{'whereto'} ); $input->{'whereto'} = uc($input_whereto); if (($input->{'whereto'} ne 'S') and ($input->{'whereto'} ne 'F')) { $input->{'whereto'} = 'S'; } } } my @input_lines; $input->{'input_lines'} = \@input_lines; my $user_provided_file_contents = 0; if (defined($params->{'file_contents'})) { my $file_contents = trim( $params->{'file_contents'} ); if ($file_contents ne '') { $user_provided_file_contents = 1; my @file_lines = split(/\n|\r/, $file_contents); foreach my $file_line (@file_lines) { $file_line = trim($file_line); if ($file_line ne '') { push( @input_lines, $file_line ); } } } } if ( (defined($params->{'upload_file'})) && ($user_provided_file_contents == 0)) { my $upload_file = $q->param('upload_file'); my $upload_filehandle = $q->upload('upload_file'); if ($upload_file ne '') { if ($upload_filehandle eq '') { $global->{'got_error'} = 1; $global->{'err_msg'} .= "The upload didn't work. Probably your file is too big - bigger than 5MB.
"; } else { # read the uploaded file my @uploaded_lines; my $user_data = ''; while ( <$upload_filehandle> ) { my $input_line = $_; chomp($input_line); push( @input_lines, $input_line ); } } } } if ($global->{'got_error'} == 0) { my $num_input_lines = $#{$input->{'input_lines'}}; if ($num_input_lines < 1) { $global->{'got_error'} = 1; $global->{'err_msg'} .= "No input lines were given. Please enter your STRIDE file contents or upload a STRIDE file.
"; } } } #=================================================================================================== # figure out whether this program is being called to display the HTML input form, # or whether the form has been filled and now the results need to be displayed, # or whether this program is being run in batch mode from the command line. #=================================================================================================== sub get_program_mode { $global->{'program_mode'} = 'command-line'; if (defined($ENV{'REQUEST_METHOD'})) { if ($ENV{'REQUEST_METHOD'} eq 'POST') { $global->{'program_mode'} = 'cgi-output'; } else { $global->{'program_mode'} = 'cgi-display-form'; } } else { $global->{'program_mode'} = 'command-line'; } } #=================================================================================================== # html code for the graphics part of the header of the html page #=================================================================================================== sub html_code_for_graphics_header { my $title = shift; my $html_code = ''; $html_code .= "
\n" . "\n" . #" style='background-image: url(\"bluegold.gif\");\n" . #" background-attachment: fixed;\n" . #" background-position: top right;\n" . #" background-repeat: no-repeat;'>\n" . " \n" . " \n" . " \n" . "
\n" . " \n" . " \n" . " \n" . " \n" . #" \n" . #" \n" . " \n" . " \n" . "
\n" . " $title\n" . " \n" . #" $title\n" . #" \n" . #" \n" . #" \n" . " \n" . "
\n" . "
\n" . "
\n" . "
\n
"; return $html_code; } #=================================================================================================== # html code for the header of the html page #=================================================================================================== sub html_header { my $title = 'EXTRACT HBPLUS HYDROGEN BONDS FROM DIFFERENT CHAINS'; print $q->header(); print #$q->start_html(-title=>$text_title), "$title\n", '' . "\n", "\n"; my $output_line = html_code_for_graphics_header($title); print $output_line; } #=================================================================================================== # html code for the footer of the html page #=================================================================================================== sub html_footer { print $debug . "
\n"; print "

\n"; print "

\n"; print "SOURCE CODE : HBPLUSchains_pl.txt
\n"; print "

\n"; print "COPYRIGHT :
\n"; print "Copyright © 2010 Emma Rath. All rights reserved.
\n"; print "Soon this program will be released under an open source software license such as GNU General Public License or
\n"; print "Creative Commons license for Free Software Foundation's GNU General Public License at creativecommons.org
\n"; print "

\n"; print "
\n"; print $q->end_html(); } #=================================================================================================== # initialise global and program variables before starting processing. #=================================================================================================== sub init { my %global_hash; $global = \%global_hash; $global->{'got_error'} = 0; $global->{'err_msg'} = ''; my %input_hash; $input = \%input_hash; $input->{'infile'} = ''; $input->{'whereto'} = ''; $input->{'h'} = ''; $input->{'help'} = ''; my @input_lines; $input->{'input_lines'} = \@input_lines; } #=================================================================================================== sub is_valid_chain { my $chain = shift; my $is_ok = 0; if (length($chain) <= 1) { $is_ok = 1; } return $is_ok; } #=================================================================================================== sub is_valid_decimal { my $input_integer = shift; my $return = 1; if ($input_integer !~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/) { $return = 0; } else { if ($input_integer < 1) { $return = 0; } } return $return; } #=================================================================================================== sub is_valid_integer { my $input_integer = shift; my $return = 1; if ($input_integer !~ /^\d+$/) { $return = 0; } else { if ($input_integer < 1) { $return = 0; } } return $return; } #=================================================================================================== sub is_valid_add_number { my $input_integer = shift; my $return = 1; if ($input_integer !~ /^-?\d+$/) { $return = 0; } return $return; } #=================================================================================================== # in command line mode, output some text help #=================================================================================================== sub output_command_line_help { my $command_line_help_text = ''; # perl -T HBPLUSchains.pl -infile HBPLUS_output.hb2 # This perl program runs as a as a cgi script on a web server that displays web pages. # This perl program also runs as a batch program on the command line. # This program reads in an HBPLUS output file (*.hb2) # and writes out only those hydrogen bonds that are between 2 atoms of 2 different chains. # This program also writes out the PyMOL commands that will select those different-chain hydrogen bond atoms. $command_line_help_text .= "Here is an example of calling this program in command line mode :\n"; $command_line_help_text .= "\n"; $command_line_help_text .= " perl -T HBPLUSchains.pl -infile HBPLUS_output.hb2\n"; $command_line_help_text .= "\n"; $command_line_help_text .= "This program reads in an HBPLUS output file (*.hb2)\n"; $command_line_help_text .= "and writes out only those hydrogen bonds that are between 2 atoms of 2 different chains.\n"; $command_line_help_text .= "This program also writes out the PyMOL commands that will select those different-chain hydrogen bond atoms.\n"; $command_line_help_text .= "\n"; print $command_line_help_text; } #=================================================================================================== # a perl utility subroutine - remove leading and trailing blanks from a string #=================================================================================================== sub trim { my $string = shift; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } #=================================================================================================== # untaint file name #=================================================================================================== sub untaint { my $path = shift; # user_error("path cannot contain metacharacters") if $path=~/[\n|<>&!;\'\"]/; # extract only legal alphanumerics from the filename # $path =~ m!(/[a-zA-Z/0-9._~\-]+)!; # $path =~ /([\w"-"".""_"]+)/; $path =~ /([a-zA-Z0-9._~\-]+)/; if (defined($1)) { $path = $1; } else { $path = ''; } # user_error("path cannot contain relative directories") # if $path=~m!\.\.!; return $path; }