📄 ch19.htm
字号:
END<BR>
print "Current time: $time\n";<BR>
}<BR>
<BR>
sub print_stack {<BR>
<BR>
$printed_lines = 0;<BR>
<BR>
#<BR>
# referring to the posts/ directory, ls -1t *.posts will force
a 1 column<BR>
# output, organized most recent file to least recent, of all .post
entries<BR>
#<BR>
chop(@posts = `ls -1t posts/*.post`);<BR>
foreach ( @posts ) {<BR>
if ( $printed_lines < $maxlines
) {<BR>
open(POST,$_);
<BR>
while (
$this_line = <POST> ) {<BR>
print
$this_line;<BR>
$printed_lines++;
<BR>
}<BR>
close(POST);
<BR>
} else {<BR>
system("rm
$_"); # clear old entries from the stack<BR>
}<BR>
}<BR>
}<BR>
<BR>
sub update_stack {<BR>
<BR>
$post_name = $input{'id'} . ".$$.post";
<BR>
$id_snippet = substr($input{'id'},-2,2);<BR>
open(POST,"> posts/$post_name");
<BR>
<BR>
# allows users to control line breaks without access to HTML by
simply<BR>
# hitting ENTER in the comments textarea<BR>
$input{'comments'} =~ s/\cM\n/<BR>\n/g;
<BR>
print POST <<END;<BR>
<HR><BR>
<!-- $input{'handle'} --><BR>
<!-- ID#: $input{'id'} --><BR>
<TABLE WIDTH=100%><TR><TD ALIGN=LEFT VALIGN=CENTER><B>$input{'handle'}</B>
<BR>
<FONT SIZE=-2>($id_snippet)</FONT></TD><TD
ALIGN=RIGHT VALIGN=CENTER><I>$time</I><BR>
Â</TD></TR></TABLE><BR>
$input{'comments'}<BR>
END<BR>
close(POST);<BR>
}<BR>
<BR>
sub print_footer {<BR>
<BR>
print <<END;<BR>
<HR><BR>
<P><FONT SIZE=-1><B>This chat room is maintained
by<BR>
<A HREF="mailto:$admin_email">$admin_name</A></B></FONT>
<BR>
</BODY><BR>
</HTML><BR>
END<BR>
<BR>
}</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
With this small amount of Perl code, we have a working World Wide
Web chat room at our disposal. I'll take some time now to discuss
how it works and tell you how to make it work on your system.
<P>
All form data is passed to chat.cgi using the <TT><FONT FACE="Courier">POST</FONT></TT>
method. The name-value pairs are decoded and placed in a Perl
associative array named <TT><FONT FACE="Courier">%input</FONT></TT>.
If the URL is being accessed directly, then <TT><FONT FACE="Courier">%input</FONT></TT>
won't exist because no <TT><FONT FACE="Courier">POST</FONT></TT>
information will come to chat.cgi. Thus, I have chat.cgi check
for the existence of fields within <TT><FONT FACE="Courier">%input</FONT></TT>
to decide whether or not the user is accessing the chat room for
the first time.
<P>
If the access is direct, chat.cgi creates an <TT><FONT FACE="Courier">ID#</FONT></TT>
rather than simply perpetuating the existing one. Also, a message
is inserted into the form handle field. After the form is submitted
for the first time, whatever is in that field upon submission
becomes the value of that field upon reload. This doesn't prevent
a user from modifying his name after he's started chatting, though.
An <TT><FONT FACE="Courier">ID#</FONT></TT> cannot be changed.
<TT><FONT FACE="Courier">ID#</FONT></TT> will be used for more
than just decoration in amendments to the code later in this chapter.
<P>
Previous postings are recorded as files in the "posts"
subdirectory beneath the directory that holds chat.cgi. Each file
is "free-floating HTML" in the sense that they contain
HTML markup but aren't by themselves full HTML files. This is
permissible because they are meant to be output as part of a greater
stream of data that all together is a full and valid HTML file.
<P>
The stack of previous posts is limited in a sense by the number
of newlines compared against the variable <TT><FONT FACE="Courier">$maxlines</FONT></TT>.
Each time a line within a .post file is output to the Web, a counter
is incremented. Once the counter is greater than <TT><FONT FACE="Courier">$maxlines</FONT></TT>,
all subsequent .post files are deleted rather than output; .post
files are output in a last-in, first-output way. This makes sense
in the context of how an HTML page is displayed. I can accomplish
this quite simply in my code by using the Perl command
<BLOCKQUOTE>
<TT><FONT FACE="Courier">chop(@posts = `ls -1t posts/*.post`);</FONT></TT>
</BLOCKQUOTE>
<P>
The logic in this line is nested. I'll start on the inside and
work out.
<P>
<TT><FONT FACE="Courier">ls</FONT></TT> is the UNIX command similar
to <TT><FONT FACE="Courier">dir</FONT></TT> in DOS. It provides
a listing of the files within a directory. <TT><FONT FACE="Courier">ls
-1t posts/*.post</FONT></TT> provides a one-column listing of
all files in the posts/ directory ending with .post, arranged
in order of newest first to oldest last.
<P>
By placing this command in backticks (<TT><FONT FACE="Courier">``</FONT></TT>),
Perl is instructed to shell to UNIX, perform the command within
the backticks, and use the standard output of that command as
a return value. I then place the standard output into the array
<TT><FONT FACE="Courier">@posts</FONT></TT>. Each line of standard
output will occupy one element in the <TT><FONT FACE="Courier">@posts</FONT></TT>
array.
<P>
Finally, <TT><FONT FACE="Courier">chop()</FONT></TT> removes the
newline character from each element in the <TT><FONT FACE="Courier">@posts</FONT></TT>
array. Now, I have an array with the filenames I need in the order
I need. Note that, by default, Perl keeps newline characters attached
to the ends of the lines it reads (from both files and <TT><FONT FACE="Courier">stdin</FONT></TT>).
Most other systems don't do this.
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Tip</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
Perl is the UNIX uber-toolbox. Not only is it a powerful and highly usable tool in its own right, but it has facilities that allow it easy access to all of the rest of UNIX-backticks (<TT><FONT FACE="Courier">``</FONT></TT>) is a perfect example of this. I
almost go out of my way to find situations where I can put the combination of UNIX and Perl to use.
</BLOCKQUOTE>
</TD></TR>
</TABLE></CENTER>
<P>
<P>
I have made the "executive decision" to disallow HTML
in chat.cgi. I accomplish this in a combination of ways. A flag,
called <TT><FONT FACE="Courier">$html</FONT></TT>, is provided.
If it equals zero, then HTML is "turned off." This allows
an <TT><FONT FACE="Courier">if</FONT></TT> statement to apply
the following code:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$input{$_} =~ s/<([^>]|\n)*>//g;</FONT></TT>
</BLOCKQUOTE>
<P>
This line demonstrates the great power and utter confusion of
the Perl <TT><FONT FACE="Courier">s///</FONT></TT> command. "<TT><FONT FACE="Courier">s</FONT></TT>"
stands for "substitute." Whatever is between the first
and second slashes is what is to be looked for. Whatever is between
the second and third slashes is what that is to be replaced with.
Both clauses are expressed in terms of Perl's regular expression
syntax. Conceptually, this isn't a difficult topic. However, regular
expressions often "bloat" so much that they become scary-so
scary that a voluntary PG-13 rating would be appreciated. The
string of characters between the first two slashes in the preceding
line of code means: "match on everything between < and
>, or between < and a newline character, or between the
beginning of a line and a >." I think.
<P>
We all just have to get used to it, I guess. Regardless, the net
effect (no pun intended) of this incantation is to remove all
HTML tags.
<P>
Another option for disallowing HTML is somewhat simpler:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$input{$_} =~ s/\</\&lt\;/g;<BR>
$input{$_} =~ s/\>/\&gt\;/g;</FONT></TT>
</BLOCKQUOTE>
<P>
This simply replaces <TT><FONT FACE="Courier">></FONT></TT>
with <TT><FONT FACE="Courier">&gt;</FONT></TT> and <TT><FONT FACE="Courier"><</FONT></TT>
with <TT><FONT FACE="Courier">&lt;</FONT></TT>, the harmless
HTML codes for these characters. People can still try to use HTML
with these lines in place, but their unsuccessful attempts will
be visible for all to see.
<H2><A NAME="GettingchatcgitoWorkforYou"><FONT SIZE=5 COLOR=#FF0000>Getting
chat.cgi to Work for You</FONT></A></H2>
<P>
It's nice to be able to use someone else's CGI code where appropriate,
but sometimes setting up that code to work on your system can
be as hard as writing your own CGI program. To help you out, I'll
go through and talk about what's needed to make the chat.cgi code
work for you.
<P>
There are three things to take into account when installing this
code on your system:
<UL>
<LI><FONT COLOR=#000000>The Perl code itself</FONT>
<LI><FONT COLOR=#000000>Your configuration of UNIX</FONT>
<LI><FONT COLOR=#000000>Your server configuration</FONT>
</UL>
<P>
Near the top of the Perl code, there is a section discussing "Configuration
Variables." This section is mostly straightforward, and parts
of it are even documented within the code. You should change the
variables starting with <TT><FONT FACE="Courier">$admin</FONT></TT>
to your name and e-mail address. <TT><FONT FACE="Courier">$baseurl</FONT></TT>
is the portion of the URL that references your chat.cgi installation
up to but not including the final <TT><FONT FACE="Courier">/</FONT></TT>
character. <TT><FONT FACE="Courier">$progname</FONT></TT> is whatever
you call your installation of chat.cgi. As a bit of a trick, a
<TT><FONT FACE="Courier">#name</FONT></TT> can be added after
the name of the program in <TT><FONT FACE="Courier">$progname</FONT></TT>
to reference a <TT><FONT FACE="Courier"><A NAME=name></FONT></TT>
tag that is added elsewhere in the program.
<P>
Perl programs can often reference UNIX system calls and programs.
Different UNIX systems provide different command paths and directory
structures, so any Perl CGI program you acquire might have to
be modified to reference these UNIX commands correctly on your
local system. Within the Perl CGI program, be on the lookout for
<TT><FONT FACE="Courier">system ("...")</FONT></TT>
statements and backticks <TT><FONT FACE="Courier">`...`</FONT></TT>.
<P>
If you do end up having to change the Perl code to correctly reference
UNIX commands on your local system, you might have a difficult
time finding exactly where these commands are kept. Here is a
list of the four things I try before going to the sysadmin to
find a command I need on a UNIX system:
<UL>
<LI><TT><FONT FACE="Courier">which COMMAND</FONT></TT> will tell
me which instance of <TT><FONT FACE="Courier">COMMAND</FONT></TT>
comes first on my command path.
<LI><TT><FONT FACE="Courier">whereis COMMAND</FONT></TT> will
report all instances of <TT><FONT FACE="Courier">COMMAND</FONT></TT>
on my command path.
<LI><FONT COLOR=#000000>If </FONT><TT><FONT FACE="Courier">COMMAND</FONT></TT>
isn't on my command path at all, <TT><FONT FACE="Courier">man
COMMAND</FONT></TT> will bring up the manual page <TT><FONT FACE="Courier">COMMAND</FONT></TT>.
There are often clues in the man page that you can use to find
the location of <TT><FONT FACE="Courier">COMMAND</FONT></TT>.
<LI><FONT COLOR=#000000>Failing these three methods, you can use
the UNIX </FONT><TT><FONT FACE="Courier">find</FONT></TT> command.
It's slow, and its usage is famously cryptic, but it does work.
You can learn its mysteries through <TT><FONT FACE="Courier">man
find</FONT></TT>.
</UL>
<P>
With regards to UNIX directly and not the commands available through
it, chat.cgi requires that a "posts" subdirectory be
made within the directory that contains chat.cgi. The directory
containing chat.cgi and the "posts" must be writable
by the Web server. The Web server must also be able to run chat.cgi;
both chat.cgi and the directory that contains it must have adequate
execute permission to do this. For a discussion on file and directory
permissions, type <TT><FONT FACE="Courier">man chmod</FONT></TT>
at your UNIX shell.
<P>
Even after you have done an appropriate <TT><FONT FACE="Courier">chmod</FONT></TT>
of the files and directories involved, your CGI programs won't
execute unless the Web server is prepared to accept them. To ensure
this, chat.cgi must either be located in the /cgi-bin/ directory
of your system, or the .cgi Magic MIME type must be enabled in
your server's srm.conf file. If you are a sysadmin, these are
both easy tasks to accomplish. If you require assistance from
your sysadmin to do either of these things, tell them that I said
it was all right. Trust me, this will work.
<H2><A NAME="ChatRoomSystemsandEntryPages"><FONT SIZE=5 COLOR=#FF0000>Chat
Room Systems and Entry Pages</A></FONT></H2>
<P>
Listing 19.1, chat.cgi, is a multistate CGI-no .html file is needed
as a "springboard" to activate it. In general, I don't
feel it necessary to provide an .html bridge for CGI programs.
Instead, I make my CGIs "aware" of the context in which
they are being invoked. However, in the realm of chat room programming,
there are two particular schools of thought on the matter. Neither
is right or wrong; they simply reflect different philosophies
and methods of organization.
<P>
The method I have modeled chat.cgi after is the multistate system.
My reasons for this are that it allows users to see what has happened
in the discussion stack before actually becoming part of the conversation,
and that any chat room CGI will ultimately need the capability
to use itself as both source and destination of form information-that
is, without going through any gross and unnecessary contortions
just to "prove a point" or something.
<P>
The other school entails the creation of a static .html page that
is a "gateway" into the CGI. There are a couple of reasons
that would motivate this sort of setup:
<UL>
<LI><FONT COLOR=#000000>The chat room is "member's only,"
and people were forced to access it through the "front door"
where a username and password must be supplied.</FONT>
<LI><FONT COLOR=#000000>The chat room is actually a system of
chat rooms. The .html entry page would contain a form that would
ask for both a handle and a choice of room within the chat system
(for example, the Aloha Deck, the Acapulco Lounge or, umm... Ten-Forward).</FONT>
</UL>
<P>
If the programmer wants to have several different rooms in a chat
room system, they might still rely on the same CGI program. On
the entry page, the form would have a selection of rooms available
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -