Google Answers Logo
View Question
 
Q: Port perl script from command line to the web ( No Answer,   8 Comments )
Question  
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.
Answer  
There is no answer at this time.

Comments  
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.

Important Disclaimer: Answers and comments provided on Google Answers are general information, and are not intended to substitute for informed professional medical, psychiatric, psychological, tax, legal, investment, accounting, or other professional advice. Google does not endorse, and expressly disclaims liability for any product, manufacturer, distributor, service or service provider mentioned or any opinion expressed in answers or comments. Please read carefully the Google Answers Terms of Service.

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 Answers  


Google Home - Answers FAQ - Terms of Service - Privacy Policy