ActivePerl FAQ - General programming

# General programming

- NAME
- DESCRIPTION
  - How do I change the Win32 Registry?
  - How do I read from/write to a named pipe?
  - How do I write socket scripts?
  - What's all this I hear about not being able to use a socket as a
  - How do I write a sockets server in ActivePerl?
  - How do I send or receive files by FTP?
  - How do I send or receive files by HTTP?
  - How do I manage user accounts with ActivePerl?
  - How do I read from and write to serial ports?
  - Why doesn't the -d operator work?
  - Reading from and writing to files mysteriously fails. What's wrong?
  - When I try to open a file, I get a "bad argument" error.
  - Why do I get an error using Perl's here-doc syntax (<<), that
- AUTHOR AND COPYRIGHT

## NAME

ActivePerl-faq8 - General programming

## DESCRIPTION

General programming questions about ActivePerl

## How do I change the Win32 Registry?

There are several Win32 Registry functions provided with ActivePerl. Check the win32mod document provided with ActivePerl.

If you don't understand how the Registry works, remember that a Registry key is like a directory, and a Registry value is like a file. There are several top level keys, and these are kind of like drives.

If you really don't fully understand the Registry, it's probably in your best interest not to mess around with it.

## How do I read from/write to a named pipe?

Named pipes are a interprocess communcations mechanism, mainly used with Microsoft operating systems (like Win32 platforms). A named pipe can be addressed just like a file.

The name of a named pipe is a UNC (Universal Naming Convention) name, and looks like \\servername\pipe\pipename. `servername` is the name of the server you're connecting to, or `.` for the current computer. `pipe` is a constant, and `pipename` is the name of the pipe, such as sql for Microsoft SQL Server.

You can use `open(),` `close(),` `read(),` and `print()` on a named pipe just like a file. However, you can't use `sysread()` or `syswrite()` on one, because they aren't really files.

There's a program called Win32Pipe on the CPAN archive that can be used to create a named pipe.

If you're starting from scratch, and you have a TCP/IP infrastructure, consider using sockets rather than named pipes for your IPC mechanism.

## How do I write socket scripts?

There are several examples of socket scripts that are distributed with ActivePerl. They're in the eg subdirectory of your perl directory.

See How do I write a sockets server in Perl for Win32? for information about sockets servers.

## What's all this I hear about not being able to use a socket as a filehandle?

Early versions of Perl for Win32 didn't allow you to read or write to a socket as if it were a filehandle. The current versions fully support this, and you shouldn't worry about it too much. If the version that you're using doesn't work well, get the latest build from ActiveState (see Where is the ActivePerl interpreter available?).

You don't have to specify `USE_SOCKETS_AS_FILEHANDLES` when building Perl for Win32 to get sockets to work like filehandles. It doesn't hurt, but it's not necessary.

## How do I write a sockets server in Perl for Win32?

There's an example of a socket server, TCP-SERVER, in the eg directory of your perl directory. In general, information on socket programming for UNIX is applicable to ActivePerl. See especially the perlipc page of the documentation.

If you need to develop a server that can service multiple clients at once, take a look at the IO::Select module. This module allows you to write servers that can manage open connections from multiple clients. Individual requests on a connection are queued up, so if your server can provide quick responses, this approach may work well for you. Here's an example, adapted from Erik Olson's Programming with Perl Modules (one of the volumes in O'Reilly's Win32 Perl Resource Kit):

        use IO::Socket;
        use IO::Select;

        # Create a socket to listen on.
        #
        my $listener =
          IO::Socket::INET->new( LocalPort => 8008, Listen => 5, Reuse => 1 );

        die "Can't create socket for listening: $!" unless $listener;
        print "Listening for connections on port 8008\n";

        my $readable = IO::Select->new;     # Create a new IO::Select object
        $readable->add($listener);          # Add the listener to it

        while(1) {

            # Get a list of sockets that are ready to talk to us.
            #
            my ($ready) = IO::Select->select($readable, undef, undef, undef);
            foreach my $s (@$ready) {

                # Is it a new connection?
                #
                if($s == $listener) {

                    # Accept the connection and add it to our readable list.
                    #
                    my $new_sock = $listener->accept;
                    $readable->add($new_sock) if $new_sock;

                    print $new_sock "Welcome!\r\n";

                } else {  # It's an established connection

                    my $buf = <$s>;   # Try to read a line

                    # Was there anyone on the other end?
                    #
                    if( defined $buf ) {

                        # If they said goodbye, close the socket. If not,
                        # echo what they said to us.
                        #
                        if ($buf =~ /goodbye/i) {
                            print $s "See you later!\n";
                            $readable->remove($s);
                            $s->close;
                        } else {
                            print $s "You said: $buf\n";
                        }

                    } else { # The client disconnected.

                        $readable->remove($s);
                        $s->close;
                        print STDERR "Client Connection closed\n";

                    }
                }
            }
        }

For more information, see the IO::Socket and IO::Select documentation. It is also possible to write a multithreaded server using ActivePerl, if threads are enabled in the version of Perl you are using. However, threading is still somewhat experimental in Perl 5.005, so use this feature with caution.

## How do I send or receive files by FTP?

See the Net::FTP module. Net::FTP is part of the libnet bundle, which is available from CPAN, and can be installed using the Perl Package Manager (PPM).

Aldo Calpini has developed a ActivePerl extension to do FTP and HTTP using the WININET library. It's in alpha testing and is available on his web page at http:// dada.perl.it/

## How do I send or receive files by HTTP?

The libwww-perl bundle (LWP) is a collection of modules for WWW access in Perl. LWP is available from CPAN in source form, or you can install it using the Perl Package Manager (PPM). LWP may also be included with future binary releases of Perl.

Aldo Calpini has developed a ActivePerl extension to do FTP and HTTP using the WININET library. It's in alpha testing and is available on his web page at http://dada.perl.it/

## How do I manage user accounts with ActivePerl?

There's an extension called Win32::NetAdmin distributed with ActivePerl. It has a pretty low-level interface, but it is very possible to manage users and groups with this module.

## How do I read from and write to serial ports?

Serial ports can be opened just like files in ActivePerl. To open COM1, just do this:

        open( PORT, "+>COM1" ) or die "Can't open COM1: $!";

You should be able to read from and write to the file handle using the standard I/O functions (read() and `print()),` but not the system functions (sysread() and `syswrite()).`

It has been noted (but not tested) that modems that use the Hayes command set require a carriage return (\r) rather than a line feed (\n) at the end of the command.

## Why doesn't the -d operator work?

It does, in fact, work. However, people tend to use it incorrectly and get bad results. To check for all the subdirectories in a directory, try code like this:

        $path = shift;
        $path = "." unless $path;

        opendir( DIR, $path )
            or die "Can't open $path: $!";

        while ( $entry = readdir( DIR ) ) {
            $type = ( -d "$path\\$entry" ) ? "dir" : "file"; # $path is crucial!
            print "$type\t$entry\n";
        }

        closedir( DIR );

It's a common mistake to leave out the `$path` from the `-d` check. If you do this, perl thinks you're talking about files in the current directory. Since the dirs don't `-e` in your current directory, they definitely don't `-d`. Exceptions are . and .., which exist in every directory.

## Reading from and writing to files mysteriously fails. What's wrong?

On Win32 platforms, there's a big difference between text files and binary files. For text files, the \r\n characters are translated into \n when read from disk, and the ^Z character is read as an end-of-file marker. For binary files, no such translation is used.

Although this works great for text files, it really messes things up when you're trying to read and write binary files. If the read or write does not abort prematurely because a ^Z was found in the file, you will almost definitely get incorrect bytes in the file due to \n -> \r\n translation.

The problem is that ActivePerl, and the C runtime library it uses, open file in text mode by default. For each file handle you use in Perl for binary data, you need to specify that the file handle is in binary mode. Fortunately, there's a function, binmode, that does just that. See the perlfunc documentation file for details.

This script copies one binary file to another. Note its use of binmode to set the mode of the file handle.

        open( INFILE, "<$infile" );
        open( OUTFILE, ">$outfile" );

        binmode( INFILE ); binmode( OUTFILE ); # crucial for binary files!

        while ( read( INFILE, $buffer, 1024 ) ) {
            print OUTFILE $buffer;
        }

        close( INFILE );
        close( OUTFILE );

## When I try to open a file, I get a "bad argument" error.

Win32 platforms use the '\' character as a delimiter for paths in a file name (C:\like\this). However, Perl uses the '\' character as an escape code, to symbolize a special character like the line feed character (\n) or the tab character (\t).

So, if you try and open a file like this:

        open( MYFILE, "C:\temp\newfile.txt" );

you'll get an error. One solution is to replace each '\' with a double-'\', to show that you really mean to use that character, not an escape:

       open( MYFILE, "C:\\temp\\newfile.txt" );

Another solution is to use non-interpolating single quote strings, which lets Perl know not to use any special characters:

       open( MYFILE, 'C:\temp\newfile.txt' );

Finally, you can also use the / character to separate directory components in pathnames. You must avoid using this in calls to external programs, because some programs tend to treat / as the option-prefix instead of directory separator. However, ActivePerl (and in fact, all of the Win32 API) understands that / is a directory separator, so using / allows you to more easily port scripts between UNIX and Win32:

       open( MYFILE, '/temp/newfile.txt' );

See the perlop documentation page for more information on the differences between single quotes (') and double quotes (``).

## Why do I get an error using Perl's here-doc syntax (<<), that says "Can't find string terminator anywhere before EOF"?

This is a weird error that occurs when your string terminator is on the last line of your script. With a script like:

        print <<"END";
        The snake is old, and his skin is cold.
        END

perl is looking for the word END on a line by itself, followed by a line-feed character (\n). If the END is the last line of your script, you have to remember to hit <Enter> after the word END, so that Perl can recognize it as the string terminator.

Most UNIX text editors will do this automatically. Most Windows text editors won't. Thus the problem.

Note that this can also cause a problem with Perl formats, since these are terminated with a single . on a line by itself. However, it's much more rare, since programmers often specify the format for output at the top rather than at the bottom of a file.

## AUTHOR AND COPYRIGHT

This FAQ was originally assembled and maintained by Evangelo Prodromou. It has been revised and updated by Brian Jepson of O'Reilly & Associates, David Grove, David Dmytryshyn and David Sparks of ActiveState.

This FAQ is in the public domain. If you use it, however, please ensure that you give credit to the original authors.

ActivePerl FAQ - General programming