My Computer s First Word Was LinuxSome Examples

My Computer's First Word Was "Linux!" ”Some Examples

Check Your Internet Mail at Login

Many Internet mail clients offer the option to periodically check whether there is any unread mail in your inbox. Sometimes there is a small applet or tray icon that changes color or shape to indicate new mail. But why stop there? Keeping with the spirit of the Unix philosophy, this example shows how to set up your machine to talk to you when new mail arrives and also to notify you of mail when you first log in.

The example is written in the Swiss Army chainsaw of languages, Perl. Without further ado, I'll present the code and then explain the various pieces. Of course, if you're already comfortable with Perl, you can certainly skip over the explanations .

 1 #!/usr/bin/perl 
 2 
 3 # Daemonize myself 
 4 exit if (fork() != 0); 
 5 close STDIN; 
 6 close STDOUT; 
 7 close STDERR; 
 8 
 9 use Socket; 
 10 
 11 $POP_SERVER = "pop.myserver.com"; 
 12 $POP_PORT = 110; 
 13 $POP_USER = "pcurtis"; 
 14 $POP_PASSWORD = "notmyrealpassword!"; 
 15 
 16 socket(S, PF_INET, SOCK_STREAM, getprotobyname("tcp")) 
 17  die "Can't create socket: $!"; 
 18 connect(S, sockaddr_in($POP_PORT, inet_aton($POP_SERVER))) 
 19  die "Can't connect: $!"; 
 20 
 21 select S; $ = 1; $line = <S>; 
 22 print "USER $POP_USER\n"; $line = <S>; 
 23 print "PASS $POP_PASSWORD\n"; $line = <S>; 
 24 if ($line =~ /^\+OK/&& $line =~ /has (\d+) mail messages/i) { 
 25 $count = (0 +); 
 26 $word = ($count  "no") . " message" 
 27 . (($count != 1) ? "s" : ""); 
 28 &say("You have $word."); 
 29 &say("Bedder cheg it now.") if ($count == 1); 
 30 } 
 31 print S "QUIT\n"; 
 32 close(S); 
 33 exit; 
 34 
 35 sub say { 
 36 my ($string) = @_; 
 37 $string =~ s/'/\'/g; 
 38 open(FH, " festival"); 
 39 print FH "(voice_kal_diphone)\n(SayText '$string')\n"; 
 40 close FH; 
 41 sleep(2); 
 42 } 
 43 

Save this program to whatever directory with whatever name you prefer, and make sure to set the executable bit in the file permissions. Then, add a call to this command in your .profile file. Some mail clients also allow you to run an arbitrary command when new mail is detected . You can place a call to this command there as well. For example, to configure kmail, the KDE 2 mail client, select Settings > Configuration from the menu, select the Miscellaneous category, and modify the box labeled "New mail notification."

The foregoing Perl script doesn't rely on external utilities; it actually implements a very small portion of the POP3 protocol, just enough to retrieve the number of messages on the server. The script will have to be adjusted if your mail server cannot handle a POP3 protocol or if the message formats returned by the POP server are slightly different.

Lines 3 “7 cause the process to fork and close all its handles, putting it in the background. This will keep a misbehaving script from locking up your mail client or blocking your login scripts from running. You do want to be able to log in, right?

Line 9 defines the library required to use socket routines. It is provided as part of the standard Perl distribution. Lines 11 “14 set up the appropriate parameters for your mail account.

Lines 16 “19 are where the action starts; the first two commands create a new socket and try to connect to the given port on the POP server. Once a connection is established, line 21 tells Perl to print to the socket and then reads the first "hello" line sent by the mail server and throws it away. Lines 22 and 23 send the username and password, reading the response lines each time. For simplicity, the script isn't written with error checking at each step.

Line 24 is where the first response line is examined. My POP server returns this line after the username and password are provided (and are correct!):

 +OK User pcurtis has 2 mail messages 

The if statement checks that the lines starts with +OK (part of the POP protocol) and then matches the text surrounding the number of interest. If your POP server returns a slightly different string, you may have to adjust that second regular expression.

If the response has the expected form, lines 25 “29 build an appropriate text string to send to Festival. Since Festival provides a sophisticated semantic layer, it's okay to put just the number into the string; it will be spelled out. Then the say function is called to speak the given text. And line 29 causes the script to tell you that you "better check it." Notice the intentional misspelling, which actually makes the generated speech sound cleaner on my system.

In case we haven't yet changed the init.scm script, we provide a separate subroutine for saying a phrase using a nondefault voice. We could have used the --tts option to Festival, but a separate subroutine is more flexible. The sleep command isn't strictly necessary, but helps if the audio device is not completely synchronous; it gives the audio time to be output before the subroutine returns.

It's easy to think of a number of ways to enhance this program; some of them could be enlightening exercises:

·                 More robust error checking.

·                 Implementing more of the protocol to retrieve message sizes, headers, From: fields, and so on.

·                 Providing command line options for different behaviors. For example, a ”just-new option, which would not log into the POP server, just verbally inform you of new mail. This could be the command run from a mail monitor. You could cause the mail client (or some other application) to be automatically launched.

·                 Just running the script from cron makes a simple but effective mail monitor. It would have to be modified to wait until you are logged in, perhaps by looking at the output of the who command.

The Lazy Man's File Browser

The next example would stand a good chance of winning a Most Gratuitously Useless award, if such an award existed. It uses Perl to glue together an IRMan infrared remote control, Festival, and XMMS to make the ELM jukebox. ELM stands for "extremely lazy man's" jukebox; the "lazy man" would be me. Even though XMMS already has a plug-in for the IRMan, it is only set up to use the standard "CD player" commands: play, pause, next track, and so on. If you want to add songs not on the playlist, you still have to select them from a dialog box. I want to be able to walk through my music collection using the remote control, with vocal feedback about my current location. Then, once I locate a song, I want to send it to XMMS to play. I chose to use Festival over rsynth because of its superior ability to handle the complex semantics of typical filenames: things like slashes , numbers , and abbreviations.

In case you're wondering, IRMan is a very cool and inexpensive piece of hardware that reads remote control codes from almost any universal remote ( Figure 21-1 ). It plugs in to a serial port and has a simple, openly implemented communication protocol. To be specific, it dumps a six-byte code to the serial port every time it receives a key press. You can use it to remote-control -enable almost any application with only a little programming. Even better, some applications have built-in support for IRMan. Go out and buy one right now !

Figure 21-1. IRMan remote control receiver.

When you first run this program, you start with a current directory and a current position within that directory. You navigate by changing the current position; if the currently selected item is a subdirectory, you can descend into it. Every time you change the current position, the program tells you the name of the file. If you change the current directory, you will hear the directory name and the number of files it contains.

The six file-navigation commands are: Forward, Back, Forward half, Back half, Up, and Down. Forward and Back move to the next or previous file in the current directory, respectively. Forward half moves from the current position halfway to the end of the directory, and Back half moves from the current position halfway toward the beginning of the directory listing. Up moves to the parent directory, and Down descends into a subdirectory. The other two commands, Play and Stop, will tell XMMS to start or stop playing the currently selected song, respectively.

As an example, say I start in the root of my music collection. I would use Forward and Forward half to navigate to the artist I wanted to hear and then use Down to descend and hear the number of files in that subdirectory. Then I would use Forward and Forward half again to pick an album or song name. Finally, when I picked the song I wanted to hear, I would use Play.

The following script is easily adapted to become a general-purpose file browser, with added features such as following symbolic links, launching various applications de pending on file types, and so on. Navigation through long directory listings could also use some refinement.

 1 #!/usr/bin/perl 
 2 
 3 use Socket; 
 4 
 5 use constant FORWARD_1 => 1; 
 6 use constant FORWARD_HALF => 2; 
 7 use constant BACK_1 => 3; 
 8 use constant BACK_HALF => 4; 
 9 use constant UP => 5; 
 10 use constant DOWN => 6; 
 11 use constant PLAY => 7; 
 12 use constant STOP => 8; 
 13 use constant QUIT => 100; 
 14 
 15 my %KEYS = ( 
 16 "660000000000" => FORWARD_1, 
 17 "460000000000" => FORWARD_HALF, 
 18 "620000000000" => BACK_1, 
 19 "420000000000" => BACK_HALF, 
 20 "750000000000" => UP, 
 21 "640000000000" => DOWN, 
 22 "710000000000" => QUIT, 
 23 "700000000000" => PLAY, 
 24 "730000000000" => STOP 
 25); 
 26 
 27 my $QUIET = 0; 
 28 my $FEST_PID = &forkOff("festival", "--server"); 
 29 sleep(2); 
 30 
 31 my ($dir, @files, $index); 
 32 &gotoDirectory("/home/mp3"); 
 33 while (1) { 
 34 if (!$QUIET && $index >= 0 && $index < @files) { 
 35 my $tmp = $files[$index]; 
 36 $tmp =~ s/\.mp3$//; 
 37 &say($tmp); 
 38 } 
 39 $cmd = $KEYS{&readKeypress}; 
 40 next if !$cmd; 
 41 if ($cmd == QUIT) { 
 42 kill 15, $FEST_PID; 
 43 wait; 
 44 exit; 
 45 } 
 46 elsif ($cmd == FORWARD_1) { 
 47 $index = ($index + 1) % @files; 
 48 } 
 49 elsif ($cmd == BACK_1) { 
 50 $index = ($index - 1) % @files; 
 51 } 
 52 elsif ($cmd == FORWARD_HALF) { 
 53 $index = int(($index + @files) / 2); 
 54 } 
 55 elsif ($cmd == BACK_HALF) { 
 56 $index = int($index / 2); 
 57 } 
 58 elsif ($cmd == DOWN) { 
 59 if (!-d "$dir/$files[$index]") { 
 60 &say($files[$index] . " is not a directory."); 
 61 $QUIET = 1; 
 62 } 
 63 else { 
 64 &gotoDirectory("$dir/$files[$index]"); 
 65 } 
 66 } 
 67 elsif ($cmd == UP) { 
 68 $dir =~ s/\/[^\/]*$//; 
 69 $dir = "/" unless $dir; 
 70 &gotoDirectory($dir); 
 71 } 
 72 elsif ($cmd == PLAY) { 
 73 my $tmp = "$dir/$files[$index]"; 
 74 if (!-f $tmp  $tmp !~ /\.(mp3wavogg)$/) { 
 75 &say($files[$index] . " is not a playable file."); 
 76 $QUIET = 1; 
 77 } 
 78 else { 
 79 &forkOff("xmms", "-p", $tmp); 
 80 $QUIET = 1; 
 81 } 
 82 } 
 83 elsif ($cmd == STOP) { 
 84 &forkOff("xmms", "-s"); 
 85 $QUIET = 1; 
 86 } 
 87 } 
 88 
 89 sub readKeypress { 
 90 my ($k, @k); 
 91 open(FH, "/dev/ttyS1"); 
 92 read(FH, $k, 6); 
 93 close FH; 
 94 @k = unpack("C6", $k); 
 95 $k[5] = $k[5]; 
 96 return join("", map {sprintf("%02x", $_)} @k); 
 97 } 
 98 
 99 sub gotoDirectory { 
 100 my ($d) = @_; 
 101 my @list; 
 102 $dir = $d; 
 103 opendir(DH, $dir); 
 104 while (defined (my $file = readdir DH)) { 
 105 next if ($file =~ /^\./); 
 106 push @list, $file; 
 107 } 
 108 closedir DH; 
 109 @files = sort @list; 
 110 $index = -1; 
 111 (my $tmp = $d) =~ s/^\/usr\/local\/music\/Old By Name\/?//; 
 112 &say("$tmp. " . scalar @files . " files."); 
 113 } 
 114 
 115 sub say { 
  116 my ($string) = @_;  
  117 $cmd = "(Parameter.set 'Audio_Method 'linux16audio)\n (voice_kal_diphone)\n(SayText \"$string\")\n";  
  118 socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp'))  die "socket: $!";  
  119 connect(SOCK, sockaddr_in(1314, inet_aton("localhost")))  die "connect: $!";  
  120 print SOCK $cmd;  
  121 close SOCK;  
  122 }  
  123  
  124 sub forkOff {  
  125 my ($cmd, @args) = @_;  
  126 my $pid = fork();  
  127 die "Error in fork()" if ($pid < 0);  
  128 unless ($pid) {  
  129 close(0); close(1); close(2);  
  130 exec ($cmd, @args);  
  131 }  
  132 return $pid;  
  133 }  

Because this script has to manage many processes, it uses a separate subroutine, forkOff(), to start a child process. It starts out by running Festival in server mode, so it can simply open a socket to port 1314 and send Scheme commands. This is done in the say() subroutine. Then it sets the current directory and position and goes into a continuous loop where it reads the remote control key presses via readKeypress() and updates the directory and position appropriately. If the key press is Play or Stop, the script will launch XMMS to handle that file. Admittedly, this program is pretty roughly done, but it shows how you can take many different tools and put them together to do something totally different.

In order to use this program effectively, you will have to enter the appropriate IRMan codes in the %KEYS hash. You can use the configuration dialog for the XMMS IRMan plug-in to find the various codes. Simply bring up the dialog and press any of the action buttons . Another dialog will pop up telling you to enter a code or press a button on the remote. When you press a button, a 12-digit hexadecimal code will appear in the text box. These are the codes you need to insert into the script.

I'm also getting away with a little simplification in the getKeypress() subroutine: In order to communicate with the serial port, you must set the appropriate baud rate for IRMan. This is done through the tcsetattr() system call, which isn't part of the Perl core language. You will have to obtain the Device::SerialPort module from CPAN and use it to set the baud rate and serial port attributes required to use that particular piece of hardware.

 



Multitool Linux. Practical Uses for Open Source Software
Multitool Linux: Practical Uses for Open Source Software
ISBN: 0201734206
EAN: 2147483647
Year: 2002
Pages: 257

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net