|
|
Subject:
Port perl script from command line to the web
Category: Computers > Internet Asked by: bcomeara-ga List Price: $15.00 |
Posted:
31 Jul 2006 23:05 PDT
Expires: 30 Aug 2006 23:05 PDT Question ID: 751348 |
I am trying to convert a Perl program that interacts with the user via the command line to a cgi script. The program is set up with numerous subroutines; it was created to interact with a MySQL database for a project on insects. For example, if I were to need the ID number of the person who collected a particular specimen, the main code would call $collectorid=&gethumanname('collector') which would then link to the following subroutine which simply returns the ID number, first adding the name to the database if it doesn't already exist: sub gethumanname { $humantype=$_[0]; $sth = $db_handle->prepare("SELECT * FROM humanname"); $sth->execute(); print "\n"; while (@data = $sth->fetchrow_array()) { $id = $data[0]; $firstname = $data[1]; $middlename = $data[2]; $lastname = $data[3]; print "$id: $firstname $lastname\n"; } print "Type the $humantype ID number, or type N for new $humantype.\nYour choice: "; $collectorchoice=<STDIN>; chomp $collectorchoice; if ($collectorchoice=~m/(\d)+/) { $collectorid=$collectorchoice; } else { #need to add new collector print "$humantype first name: "; $firstname=<STDIN>; chomp $firstname; print "$humantype middle name: "; $middlename=<STDIN>; chomp $middlename; print "$humantype last name: "; $lastname=<STDIN>; chomp $lastname; print "$humantype email: "; $email=<STDIN>; chomp $email; $insert_handle = $db_handle->prepare_cached('INSERT INTO humanname (firstname,middlename,lastname,email) VALUES (?,?,?,?)'); $insert_handle->execute($firstname, $middlename, $lastname, $email); sleep(2); $sth = $db_handle->prepare("SELECT * FROM humanname WHERE firstname=\'$firstname\' AND lastname=\'$lastname\'"); $sth->execute(); while (@data = $sth->fetchrow_array()) { $collectorid = $data[0]; } } return "$collectorid"; } My problem is how to replace <STDIN> with a cgi script. It'd be easiest if I could just replace the <STDIN> lines with a new subroutine. For example, rather than using print "enter email: "; $email=<STDIN>; chomp $email; I could just use $email=&getwebinput("enter email: "); I thus tried adding this subroutine : sub getwebinput { $stufftoprint=$_[0]; $output=param('inputfield'); if ($output!~m/[a-z0-9]/i) { print header; print start_html("Ant DB"); print '<form method="post" action="onlineinteract.cgi">'; print "$stufftoprint "; print '<input class="post" style="width: 200px;" name="inputfield" size="100" maxlength="100" type="text">'; print '<input type="hidden" name="modechoice" value="$modechoice">'; print '<input value="Enter" type="submit"></form>'; print end_html; } else { param(-name=>'inputfield',-value=>''); return $output; } } But this will just display the form and continue running through the program, not stop and wait for input as in the original <STDIN> line. Is there a good way to force the program to stop and wait for the cgi subroutine to return adequate input before continuing [call a separate cgi script, etc]? If not, what's the best way to go from here? Thank you for your help. |
|
There is no answer at this time. |
|
Subject:
Re: Port perl script from command line to the web
From: sycophant-ga on 01 Aug 2006 00:03 PDT |
Hi, Unfortunately my Perl is a bit rusty by now (I've done all my web coding in PHP since 1998) but basically a CGI and command line program operate in quite differnt ways. The CGI process isn't interactive like a command-line program. It will get called each time the page is requested... The general idea is: if (defined($FORM_VARIABLE)) { return data. } else { return form. } When the page is called without any form variable we assume that it is being loaded for the first time and return the form. If the page is called and the form data is passed, then we take that data and process it, then return the result. I'd be happy to write this code in PHP for you, but I am no longer familiar enough with Perl to adapt your existing script. Regards, Sycophant |
Subject:
Re: Port perl script from command line to the web
From: bcomeara-ga on 01 Aug 2006 07:29 PDT |
Thank you, Sycophant. It just seems that in the age of Ajax, there should be some solution. For example, there's a tutorial on using javascript and php to pull info from a database without needing to load a page at http://www.webpasties.com/xmlHttpRequest/ . The only difference from my need (and it may be a significant difference) is that in the tutorial example, the web page calls the program on the server (PHP & MySQL) when it needs info, whereas in my application the program on the server needs to call a web page when it needs info. Perl can pull info from external programs (using backticks) -- is there any way to have this external program be one to interact with the user on the web? |
Subject:
Re: Port perl script from command line to the web
From: bozo99-ga on 01 Aug 2006 15:30 PDT |
Normally in the CGI world the user fills in a form and hits a submit button (possibly one of several that do different things). Then all the data on the form is sent to the webserver and then to the CGI script in an organised manner so that the CGI can know what values to read and what to do with them. They arrive as environment variables in a GET method and as STDIN in a POST method. So supposing the form of your queries can be expressed in one form per query-type all you need to do is arrange the CGI to take user input from the GET and/or POST values given to it by the webserver (that it got from the browser). This means no direct use of STDIN for reading individual fieldsa as in your code fragment. You probably want to read and store all input right at the beginning before making your database calls. sycophant-ga has described the "comboform" type of CGI that acts as both the blank form and the processing script. The backticks in perl are not really relevant or necessary, and depending on what's involved could lead to security problems. xmlhttprequest would allow your form to be rewritten during its completion rather than waiting for the submit button at the end of the entire form. This may or may not be useful for your application (that might depend on how stylish you want it to look). |
Subject:
Re: Port perl script from command line to the web
From: bcomeara-ga on 01 Aug 2006 22:11 PDT |
Thank you for your comments, bozo99. I could rewrite the 2600 line program to work as a traditional cgi, displaying a form, then processing input, then displaying a new form, etc. This is certainly possible, and I could just sit down and do this over a few days, but I already have a working program that takes text input -- I just want to throw it on the web so I can use it more easily. All I need is a way for the program to display one string to a user and return one string from the user before moving on. So far, this doesn't seem possible. Any other suggestions? |
Subject:
Re: Port perl script from command line to the web
From: bozo99-ga on 02 Aug 2006 16:36 PDT |
I'm unsure whether for your situation it would be a net saving in effort, but one option is to arrange a new program to mediate between the web and the existing program. Each prompt from your existing program causes a CGI program to display a page prompting the user, and what the user supplies gets back to the DB program. Issues like knowing when each message is finished will be involved - it may simplify things to modify the program slightly to signal this unmistakably unless it is obvious (like if they are all one line each). I've done this (for a very simple program) using a pair of named pipes when my aim was to interface a CGI in a chroot jail with a program running outside the chroot. |
Subject:
Re: Port perl script from command line to the web
From: bcomeara-ga on 04 Aug 2006 15:58 PDT |
Thanks, bozo99. That's exactly what I'd like to do (thus my comment about backticks and system calls). Do you have any example code, or suggestions on how to set it up (use javascript, etc.)? Thanks again. |
Subject:
Re: Port perl script from command line to the web
From: bozo99-ga on 08 Aug 2006 01:05 PDT |
I'm going to give a bare-bones example with no session management such as cookies and locks that would allow 2 people to use this program at the same time. First a simple interactive program for calculating BMI, as an example session and the program in perl (watch out for whether this display breaks long lines). Heights may be given in m (e.g. 1.68), cm (e.g. 168), feet (e.g 5.5) or inches (e.g. 66) What is your height ? 6 In what units ? ft Weights may be given in kg (e.g. 70) or lb (e.g. 154) What is your weight ? 90 In what units ? kg BMI= 26.9 #!/usr/bin/perl -w # BMI calculator # b=m/(h^2) (kg and m) print "Heights may be given in m (e.g. 1.68), cm (e.g. 168), feet (e.g 5.5) or inches (e.g. 66)\n"; print "What is your height ?\n"; $h_num=<>; chomp($h_num); print "In what units ?\n"; $h_u=<>; chomp($h_u); $h_u=lc $h_u; print "Weights may be given in kg (e.g. 70) or lb (e.g. 154)\n"; print "What is your weight ?\n"; $w_num=<>; chomp($w_num); print "In what units ?\n"; $w_u=<>; chomp($w_u); $w_u=lc $w_u; if ("cm" eq $h_u) { $h_num /= 100; $h_u="m"; } if ("ft" eq $h_u) { $h_num *= 0.3048; $h_u="m"; } if ("in" eq $h_u) { $h_num *= 0.0254; $h_u="m"; } if ("m" ne $h_u) { printf("Sorry, don't understand height units %s.\n", $h_u); exit(1); } if ("lb" eq $w_u) { $h_num *= 0.45359 ; $w_u="kg"; } if ("kg" ne $w_u) { printf("Sorry, don't understand weight units %s.\n", $w_u); exit(1); } $b=$w_num / ($h_num * $h_num); printf("BMI=%5.1f\n" , $b); exit(0); Next that wants to speak to a CGI program so we have the same program extended to write to two named pipes called q and a. These pipes are made with mknod and look like this (in a directory named for "google answers inter process communication"). ls -l /var/www/gaipc/* prw-rw-rw- 1 root daemon 0 Aug 8 08:31 /var/www/gaipc/a prw-rw-rw- 1 root daemon 0 Aug 8 08:31 /var/www/gaipc/q #!/usr/bin/perl -w # BMI calculator (talks to a CGI through named pipes) # b=m/(h^2) (kg and m) open(Q, ">/var/www/gaipc/q") or die("open q ipc"); print Q "Heights may be given in m (e.g. 1.68), cm (e.g. 168), feet (e.g 5.5) or inches (e.g. 66)\n"; print Q "What is your height ?\n"; close(Q); open(A, "</var/www/gaipc/a") or die("open a ipc"); $h_num=<A>; chomp($h_num); close(A); open(Q, ">/var/www/gaipc/q") or die("open q ipc"); print Q "In what units ?\n"; close(Q); open(A, "</var/www/gaipc/a") or die("open a ipc"); $h_u=<A>; chomp($h_u); $h_u=lc $h_u; close(A); open(Q, ">/var/www/gaipc/q") or die("open q ipc"); print Q "Weights may be given in kg (e.g. 70) or lb (e.g. 154)\n"; print Q "What is your weight ?\n"; close(Q); open(A, "</var/www/gaipc/a") or die("open a ipc"); $w_num=<A>; chomp($w_num); close(A); open(Q, ">/var/www/gaipc/q") or die("open q ipc"); print Q "In what units ?\n"; close(Q); open(A, "</var/www/gaipc/a") or die("open a ipc"); $w_u=<A>; chomp($w_u); close(A); $w_u=lc $w_u; if ("cm" eq $h_u) { $h_num /= 100; $h_u="m"; } if ("ft" eq $h_u) { $h_num *= 0.3048; $h_u="m"; } if ("in" eq $h_u) { $h_num *= 0.0254; $h_u="m"; } if ("m" ne $h_u) { printf("Sorry, don't understand height units %s.\n", $h_u); exit(1); } if ("lb" eq $w_u) { $h_num *= 0.45359 ; $w_u="kg"; } if ("kg" ne $w_u) { printf("Sorry, don't understand weight units %s.\n", $w_u); exit(1); } $b=$w_num / ($h_num * $h_num); open(Q, ">/var/www/gaipc/q") or die("open q ipc"); printf(Q "BMI=%5.1f\n" , $b); close(Q); exit(0); Yoou will notice this version of the program repeatedly opens and closes the named pipes to show when each message is over. The prompts (sent to q) end in a '?' iff an answer is required immediately after. Then we need the CGI to act on the other side of those pipes. #!/usr/bin/perl -w sub show_question { open(Q, "</var/www/gaipc/q"); while(<Q>) { chomp(); printf("%s<br>\n", $_); if (/\?$/) { # got a final ? so prompt for input printf("<FORM action=%c%c method=%cPOST%c>",34,34,34,34); printf("<INPUT type=%ctext%c name=%cinput%c>\n", 34,34, 34,34); printf("<INPUT type=%csubmit%c value=%cSend%c>",34,34,34,34); print "</FORM>"; }; } close(Q); } ######################## sub show_answer { $answer=shift; open(A, ">/var/www/gaipc/a"); printf(A "%s\n", $answer); close(A); } ######################## print "Content-type: html\n"; # cookie management would go here if we had it print "\n<html><body>\n"; if ("POST" eq $ENV{"REQUEST_METHOD"}) { # pass data to pipe while (<>) { chomp(); if (/^input=(\S+)$/) { show_answer($1); } } } show_question(); print "</body></html>\n"; exit(0); Now the order of events is that you start the local bmicalc.plx program and it writes the first question into q and wants to read an answer from a. This will mean it blocks waiting for someone to visit the web page (for the first time in a GET method). (It would be a mistake for someone to call it initially with a POST when it isn't already in a conversation. That mistake is not currently handled.) The exchange of questions and answers takes place until if it all works a calculated BMI is displayed. The calculation is all done in the interactive program and the CGI only converts data from web to local and back. The webserver may be running in a chroot jail, so you might want to remove the text "/var/www" from the pipe names in the CGI. Pathnames on your system may be differnet anyway. The local-side program doesn't need to run as the same user as the webserver, but does need access to the pipes. With a little more work and fancy formatting in the questions other things than text could be used (such as radio buttons for selection of units). In a production system I would want to use Perl's tainting in both parts of the system to check for valid data. |
Subject:
Re: Port perl script from command line to the web
From: bcomeara-ga on 22 Aug 2006 10:44 PDT |
Thank you, bozo99. I checked with my webhost, and only root is allowed to run mknod, so I cannot use this solution. |
If you feel that you have found inappropriate content, please let us know by emailing us at answers-support@google.com with the question ID listed above. Thank you. |
Search Google Answers for |
Google Home - Answers FAQ - Terms of Service - Privacy Policy |