source: gutenbach/debian/lib/sipbmp3-filter @ d414b9c

debianmacno-cupsnodebathenaweb
Last change on this file since d414b9c was d414b9c, checked in by presbrey <presbrey>, 17 years ago

Removed ogg vorbis streaming buffer; too large

  • Property mode set to 100755
File size: 18.3 KB
Line 
1#!/usr/athena/bin/perl
2# Play the data on STDIN as an audio file
3#
4# $Id: sipbmp3-filter,v 1.13 2007-04-20 08:49:58 presbrey Exp $
5# $Source: /tmp/tmp.UFBNno9997/RCS/sipbmp3-filter,v $
6#
7# TODO
8# ----
9# Make this structured code. It's a mess.
10# Repeat what we just played for EXT files too
11# Support HTTP Auth on ogg streams
12# License, cleanup and package
13#
14# Jered Floyd <jered@mit.edu> takes very little credit for this code
15
16use Getopt::Std;
17
18# Get the MP3Info module from this directory, because I suck.
19unshift(@INC, "/usr/local/bin");
20require MP3::Info;
21import MP3::Info;
22&use_winamp_genres();
23
24print STDERR "STDERR FROM SPOOL FILTER\n";
25
26# set real uid to be effective uid
27$< = $>;
28
29# Attach necessary lockers
30system("/bin/athena/attach -h -n -q infoagents sipb outland ' .
31    'consult 2>&1 > /dev/null");
32
33# Select the correct output device and set the volume
34system("/usr/local/bin/audio_setdevice -out headphones 100 ' .
35    '2>&1 </dev/null > /dev/null");
36
37# The command line we get from lpd is (no spaces between options and args):
38#  -C lpr -C class
39#  -A LPRng internal identifier
40#  -H originating host
41#  -J lpr -J jobname (default: list of files)
42#  -L lpr -U username
43#  -P logname
44#  -Q queuename (lpr -Q)
45#  -Z random user-specified options
46#  -a printcap af (accounting file name)
47#  -d printcap sd entry (spool dir)
48#  -e print job data file name (currently being processed)
49#  -h print job originiating host (same as -H)
50#  -j job number in spool queue
51#  -k print job control file name
52#  -l printcap pl (page length)
53#  -n user name (same as -L)
54#  -s printcap sf (status file)
55#  -w printcap pw (page width)
56#  -x printcap px (page x dimension)
57#  -y printcap py (page y dimension)
58# accounting file name
59
60# All the filter_options from lpd
61getopt('ACFHJLPQRZacdefhijklnprswxy', \%opts);
62
63# Status messages at start of playback
64open(ZEPHYR, '|/usr/athena/bin/zwrite -d -n -c sipb-auto -i ' .
65  'sipbmp3@xcb -s "SIPB LPR music spooler"');
66print(ZEPHYR "User $opts{'n'} on host $opts{'H'} is playing:\n");
67
68# So, the file we're currently processing is "-d/-e".
69# Do some magic to make sure it's an MP3, and get the important bits
70# to zephyr out.
71open(DATAFILE, "$opts{'d'}/$opts{'e'}");
72sysread(DATAFILE, $magic, 3);
73close(DATAFILE);
74
75# MPEG header is beshort &0xffe0
76# meditate upon the header:
77($magic0, $magic1, @magic2) = unpack("C3", $magic);
78if ((($magic0 & 0xff) == 0xff) &&
79    (($magic1 & 0xe0) == 0xe0)) {
80  # MPEG audio file, we like it
81  # Fall through
82} elsif ($magic eq "ID3") {
83  # ID3 v2 does sketchy things with "garbage" beginning with ID3 at file start.
84  # MPEG audio file with ID3 tag, we like it
85  # Fall through
86} elsif ($magic eq "EXT") {
87  # This is an external stream reference (a jered-special)
88  &play_external_reference(\%opts, $magic);
89  exit 0;
90} elsif ($magic eq "htt") {
91  # This is an external stream reference (a jered-special)
92  &play_external_reference(\%opts, $magic);
93  exit 0;
94} elsif ($magic eq "Ogg") {
95  # Ogg-Vorbis audio file
96  &play_ogg_audio(\%opts);
97  exit 0;
98} else {
99    # add more cases later, whine for now
100    printf(ZEPHYR "I don't think this is an MPEG audio file... %02x %02x\n",
101           $magic0, $magic1);
102    print ZEPHYR "I'm going to try playing it as one anyway.\n";
103}
104# Default
105&play_mpeg_audio(\%opts);
106
107
108# Play an external stream reference
109sub play_external_reference {
110    # Retrieve those command line opts.
111    my %opts = %{shift(@_)};
112    my $magic = shift(@_);
113
114    # External references are *not* playlists; they only support
115    # a single URL in order to be fair.  A reference is structured as
116    # follows:
117    #
118    # EXT <format>\n
119    # <URI>[ <user:pass>]\n
120    # Descriptive text of the file that will be\n
121    # played; this may span multiple lines until the\n
122    # EOF
123    #
124    # Where <URI> is a valid URI for the stream, and <format> is a
125    # recognized format name (currently 'MP3' or 'OGG' or 'SHOUTCAST').
126    # <user:pass> is an optional user and password pair for HTTP Basic Auth.
127    my $format, $uri, $userpass;
128
129    if ("$magic" eq "EXT") {
130      if (<STDIN> =~ /^EXT (.*)$/) {
131        # Found the header
132        $format = $1;
133      } else {
134        print ZEPHYR "Couldn't read EXT header\n";
135        close(ZEPHYR);
136      }
137 
138      if (<STDIN> =~ /^(\S*)\s*(.*)$/) {
139        # Found the URI (and optionally user:pass)
140        $uri = $1;
141        if ($2) {
142        $userpass = $2;
143        }
144      } else {
145        print ZEPHYR "Couldn't read URI for external reference\n";
146        close(ZEPHYR);
147      }
148  } elsif ("$magic" eq "htt") {
149      if (<STDIN> =~ /^(\S*)\s*(.*)$/) {
150          $uri=$1;
151          my $pid = open(W3M, "-|");
152          unless (defined $pid) {
153              print ZEPHYR "Couldn't fork: $!\n";
154              close(ZEPHYR);
155              die;
156          }
157
158          if ($pid) { #parent
159              $contenttype="unknown";
160              while (<W3M>) {
161                  if ($_ =~ /^Content-Type:\s(\S*)/) {
162                      $contenttype=$1;
163                  }
164              }
165              if ($contenttype eq "audio/mpeg") { $format="MP3" }
166              elsif ($contenttype eq "application/x-ogg") { $format="OGG" }
167              elsif ($contenttype eq "application/ogg") { $format="OGG" }
168              elsif ($contenttype eq "audio/x-scpls") { $format="SHOUTCAST" }
169              else {
170                  print ZEPHYR
171                      "Unknown Content-Type $contenttype for URI $uri\n";
172                  close(ZEPHYR);
173              }
174              close(W3M) || print ZEPHYR "w3m exited $?\n";
175          }
176          else { # child
177              exec("/mit/sipb/bin/w3m", "-dump_head", $uri)
178                  || die "Couldn't exec";
179          }
180      } else {
181        print ZEPHYR "Couldn't read URI for external reference\n";
182        close(ZEPHYR);
183    }
184  } else {
185      print ZEPHYR "Unknown syntax in play_external_reference(): $magic\n";
186      close(ZEPHYR);
187  }
188
189    # Echo the rest to the zephyr
190    while (<STDIN>) {
191      print ZEPHYR $_;
192    }
193    print ZEPHYR "\n";
194
195    # Play the thing the right way
196    if (($format eq "MP3") ||
197        ($format eq "mp3")) {
198      print ZEPHYR "MP3 audio stream...\n";
199      close(ZEPHYR);
200      &play_mpeg_stream($uri, $userpass);
201    }
202    elsif ($format eq "SHOUTCAST") {
203        print ZEPHYR "Shoutcast playlist...\n";
204        #Don't close ZEPHYR yet, will print the name of the stream if available
205        $uri = &get_shoutcast($uri);
206        close(ZEPHYR);
207        &play_mpeg_stream($uri, undef);
208    }   
209    elsif (($format eq "OGG") ||
210             ($format eq "ogg")) {
211      print ZEPHYR "OggVorbis stream...\n";
212      close(ZEPHYR);
213      &play_ogg_stream($uri, $userpass);
214    } else {
215      print ZEPHYR "Unrecognized stream format: $format\n";
216      close(ZEPHYR);
217    }
218}
219
220# Process a Shoutcast playlist
221# get_shoutcast(URI)
222sub get_shoutcast {
223  my $uri = shift(@_);
224 
225  # grab the shoutcast .pls file
226  my $pid = open(W3M, "-|");
227  unless (defined $pid) {
228      print ZEPHYR "Couldn't fork: $!\n";
229      close(ZEPHYR);
230      die;
231  }
232  if (!$pid) { # child
233      exec("/mit/sipb/bin/w3m","-dump_source",$uri) || die "Couldn't fork";
234  }
235  while (<W3M>) {
236      if (/^File\d+=(\S+)/) {
237          push(@uris, $1);
238      }
239      if (/^Title\d+=(.+)$/) {
240          push(@titles, $1);
241      }
242  }
243  close(W3M);
244 
245  # choose a random server
246  $server = int(rand scalar(@uris));
247  # print the name of the stream if available
248  print ZEPHYR "$titles[$server]\n";
249  return $uris[$server];
250}
251
252# Play an MPEG audio stream
253# play_mpeg_stream(URI, userpass)
254sub play_mpeg_stream {
255  my $uri = shift(@_);
256  my $userpass = shift(@_);
257
258  # Add the user:pass argument if is was given to us
259  my $up = '';
260  if ($userpass) {
261    $up = '-u ' . $userpass;
262  }
263
264  system("ps ax | grep ogg123 | awk '{print $2}' | xargs kill -9");
265  system("ps ax | grep mpg123 | awk '{print $2}' | xargs kill -9");
266  system("chmod a+rw /dev/audio");
267
268  # Prepare to write status:
269  open(ZEPHYR, '|/usr/athena/bin/zwrite -d -n -c sipb-auto -i ' .
270       'sipbmp3@xcb -s "SIPB LPR music spooler"');
271     
272  # fork for mpg123
273  my $pid = open(MP3STATUS, "-|");
274  unless (defined $pid) {
275      print ZEPHYR "Couldn't fork: $!\n";
276      close(ZEPHYR);
277      return;
278  }
279 
280  if ($pid) { #parent
281      # Check if there were any errors
282      if ($_ = <MP3STATUS>) {
283          print ZEPHYR "Playback completed with the following errors:\n";
284          print ZEPHYR $_;
285          while (<MP3STATUS>) {
286              print ZEPHYR $_;
287          }
288      } else {
289          print ZEPHYR "Playback completed successfully.\n";
290      }
291      close(MP3STATUS) || print ZEPHYR "mpg123 exited $?\n";
292     
293      close(ZEPHYR);
294  }
295  else { # child
296      # redirect STDERR to STDOUT
297      open STDERR, '>&STDOUT';
298
299      # disabling the buffer seems to improve streaming performances
300      # (files don't get cut in the middle) - perhaps mpg123 fails to
301      # wait for OS TCP buffer to be filled. -- jtwang 22 Oct 2003
302      #exec("/mit/infoagents/bin/mpg123", "-b16384", "-q", $up, $uri ||
303      #   die "Couldn't exec";
304      exec("/mit/infoagents/bin/mpg123", "-q", $up, $uri) ||
305          die "Couldn't exec";
306  }
307}
308
309# ID3 comments often have useless crap because tools like iTunes were
310# written by drooling idiots
311sub filter_comment {
312  my $comment = shift(@_);
313
314  if ($comment =~ /^engiTunes_CDDB/) {
315    return undef;
316  }
317  return $comment;
318}
319
320
321# Play an MPEG audio file
322sub play_mpeg_audio {
323    # Retrieve those command line opts.
324    my %opts = %{shift(@_)};
325
326    my %MPEGModes = ( 0 => "stereo",
327                      1 => "joint-stereo",
328                      2 => "dual-channel",
329                      3 => "single-channel");
330
331    # If it's an MP3 file, try to extract useful data
332    my $tag = get_mp3tag("$opts{'d'}/$opts{'e'}");
333    if (!$tag) {
334        print ZEPHYR "No ID3 tag found\n";
335        my @fnamearray = split /,/, $opts{'J'};
336        foreach $fname (@fnamearray) {
337            print ZEPHYR "Filename: $fname\n";
338        }
339#           print ZEPHYR "Filename: $opts{'J'}\n\n";
340    } else {
341      print ZEPHYR "Title          : $tag->{TITLE}\n";
342      print ZEPHYR "Artist         : $tag->{ARTIST}\n";
343      print ZEPHYR "Album          : $tag->{ALBUM}\n";
344      if ($tag->{TRACKNUM} =~ /(.*)\/.*/) {
345          $tag->{TRACKNUM} = $1;
346      }
347      if ($tag->{TRACKNUM}) {
348          print ZEPHYR "Track #        : $tag->{TRACKNUM}\n";
349      }
350      if ($tag->{YEAR}) {
351        print ZEPHYR "Year           : $tag->{YEAR}\n";
352      }
353      if ($tag->{GENRE}) {
354        # iTunes (?) does something weird with genres, leaving them
355        # as the string "(##)" where ## is the genre type. Let's deal
356        # with this.
357        if ($tag->{GENRE} =~ /^\((\d*)\)$/) {
358          $tag->{GENRE} = $MP3::Info::mp3_genres[$1];
359        }
360        if ($tag->{GENRE} =~ /^(\d*)$/) {
361          $tag->{GENRE} = $MP3::Info::mp3_genres[$1];
362        }
363        print ZEPHYR "Genre          : $tag->{GENRE}\n";
364      }
365      if (ref $tag->{COMMENT} eq 'ARRAY') {
366        foreach $index (0 .. $#{$tag->{COMMENT}}) {
367          if ($comment = filter_comment(@{$tag->{COMMENT}}[$index])) {
368            print ZEPHYR "Comment        : $comment\n";
369          }
370        }
371      } elsif ($tag->{COMMENT}) {
372        if ($comment = filter_comment($tag->{COMMENT})) {
373          print ZEPHYR "Comment        : $comment\n";
374        }
375      }
376
377      # Maybe get some extended ID3v2 info?
378      my $v2tag = get_mp3tag("$opts{'d'}/$opts{'e'}", 2, 1);
379      %lessinfo = %$v2tag;
380      foreach $dtag (keys %MP3::Info::v2_to_v1_names) {
381        delete $lessinfo{$dtag};
382      }
383      delete $lessinfo{'GENRE'};
384      # Delete annoying useless crap
385      my @bad_tags = ('GEO', 'GEOB', # General encapsulated object
386                      'PIC', 'APIC', # Attached picture.
387                      );
388      foreach $dtag (@bad_tags) {
389          delete $lessinfo{$dtag};
390      }
391      while (($key,$val) = each %lessinfo) {
392        printf ZEPHYR "%-15s: %s\n", $MP3::Info::v2_tag_names{$key}, $val;
393      }
394      print ZEPHYR "\n";
395    }
396
397    my $info = get_mp3info("$opts{'d'}/$opts{'e'}");
398    if (!$info) {
399        print ZEPHYR "No MPEG header found\n";
400    } else {
401        print ZEPHYR "MPEG $info->{VERSION} layer $info->{LAYER}, ";
402        if ($info->{VBR}) {
403            print ZEPHYR "VBR ";
404        }
405        print ZEPHYR "$info->{BITRATE} kbit/s, $info->{FREQUENCY} kHz ";
406        print ZEPHYR $MPEGModes{$info->{STEREO}};
407        print ZEPHYR "\n\n";
408        printf ZEPHYR "Track length: %d:%02ds\n", $info->{MM}, $info->{SS};
409    }
410    close(ZEPHYR);
411
412    # Play the file
413    # mpg123 is a crock.  If you don't give it -q, it needs to be on a pty
414    # or it SEGVs. Really.
415
416    system("chmod a+rw /dev/audio");
417    system("ps ax | grep ogg123 | awk '{print $2}' | xargs kill -9");
418    system("ps ax | grep mpg123 | awk '{print $2}' | xargs kill -9");
419    system("/mit/infoagents/bin/mpg123 -b 16384 -q - >/tmp/mpg123.out 2>&1");
420
421    # Done. Status:
422    open(ZEPHYR, '|/usr/athena/bin/zwrite -d -n -c sipb-auto -i ' .
423         'sipbmp3@xcb -s "SIPB LPR music spooler"');
424
425    # Check if there were any errors
426    open(MP3STATUS, "/tmp/mpg123.out");
427    if ($_ = <MP3STATUS>) {
428        print ZEPHYR "Playback completed with the following errors:\n";
429        print ZEPHYR $_;
430        while (<MP3STATUS>) {
431            print ZEPHYR $_;
432        }
433    } else {
434        print ZEPHYR "Playback completed successfully.\n";
435        # Repeat tag data for those playing at home
436        if (!$tag) {
437          print ZEPHYR "No ID3 tag found\n";
438          print ZEPHYR "Filename: $opts{'J'}\n\n";
439        } else {
440          print ZEPHYR "Title          : $tag->{TITLE}\n";
441          print ZEPHYR "Artist         : $tag->{ARTIST}\n";
442          print ZEPHYR "Album          : $tag->{ALBUM}\n";
443          if ($tag->{TRACKNUM} =~ /(.*)\/.*/) {
444              $tag->{TRACKNUM} = $1;
445          }
446          if ($tag->{TRACKNUM}) {
447              print ZEPHYR "Track #        : $tag->{TRACKNUM}\n";
448          }
449          if ($tag->{YEAR}) {
450            print ZEPHYR "Year           : $tag->{YEAR}\n";
451          }
452          if ($tag->{GENRE}) {
453              print ZEPHYR "Genre          : $tag->{GENRE}\n";
454          }
455          if (ref $tag->{COMMENT} eq 'ARRAY') {
456            foreach $index (0 .. $#{$tag->{COMMENT}}) {
457              if ($comment = filter_comment(@{$tag->{COMMENT}}[$index])) {
458                print ZEPHYR "Comment        : $comment\n";
459              }
460            }
461          } elsif ($tag->{COMMENT}) {
462            if ($comment = filter_comment($tag->{COMMENT})) {
463              print ZEPHYR "Comment        : $comment\n";
464            }
465          }
466
467          # others
468          while (($key,$val) = each %lessinfo) {
469            printf ZEPHYR "%-15s: %s\n", $MP3::Info::v2_tag_names{$key}, $val;
470          }
471        }
472    }
473    close(MP3STATUS);
474    unlink(MP3STATUS);
475
476    close(ZEPHYR);
477}
478
479# Play an OggVorbis audio stream (doesn't support auth!)
480# play_ogg_stream(URI)
481#  Mostly copied from play_ogg_audio
482sub play_ogg_stream {
483  my $uri = shift(@_);
484
485  system("chmod a+rw /dev/audio");
486  system("ps -aef | grep ogg123 | awk '{print $2}' | xargs kill -9");
487  system("ps -aef | grep mpg123 | awk '{print $2}' | xargs kill -9");
488
489  # Prepare to write status:
490  open(ZEPHYR, '|/usr/athena/bin/zwrite -d -n -c sipb-auto -i sipbmp3@xcb -s "SIPB LPR music spooler"');
491  system("/mit/sipb/bin/ogg123 -dau -q -f - $uri 2> /tmp/ogg123.out | audioplay");
492
493  # Check if there were any errors
494  open(OGGSTATUS, "/tmp/ogg123.out");
495  if ($_ = <OGGSTATUS>) {
496    print ZEPHYR "Playback completed with the following errors:\n";
497    print ZEPHYR $_;
498    while (<OGGSTATUS>) {
499      print ZEPHYR $_;
500    }
501  } else {
502    print ZEPHYR "Playback completed successfully.\n";
503    # Repeat tag data for those playing at home
504    if (!$ogginfo{'title'}) {
505      print ZEPHYR "No ogginfo data found\n";
506      print ZEPHYR "Filename: $opts{'J'}\n\n";
507    } else {
508      print ZEPHYR "Title          : $ogginfo{'title'}\n";
509      print ZEPHYR "Artist         : $ogginfo{'artist'}\n";
510      print ZEPHYR "Album          : $ogginfo{'album'}\n";
511      print ZEPHYR "Track #        : $ogginfo{'tracknumber'}\n";
512
513      # others
514      while (($key,$val) = each %lessinfo) {
515        printf ZEPHYR "%-15s: %s\n", $key, $val;
516      }
517    }
518  }
519
520  close(OGGSTATUS);
521  unlink(OGGSTATUS);
522
523  close(ZEPHYR);
524}
525
526# Play an OggVorbis audio file
527sub play_ogg_audio {
528  # Retrieve those command line opts.
529  my %opts = %{shift(@_)};
530
531  # Get ogginfo stuff
532  open(OGGINFO, "/mit/sipb/bin/ogginfo $opts{'d'}/$opts{'e'}|");
533  while (<OGGINFO>) {
534    if (/(.*)=(.*)/) {
535      $ogginfo{lc("$1")} = $2;
536    }
537  }
538  close(OGGINFO);
539
540  # If there's no title, print the filename
541  if (!$ogginfo{'title'}) {
542    print ZEPHYR "No ogginfo data found\n";
543    print ZEPHYR "Filename: $opts{'J'}\n";
544  } else {
545    print ZEPHYR "Title          : $ogginfo{'title'}\n";
546    print ZEPHYR "Artist         : $ogginfo{'artist'}\n";
547    print ZEPHYR "Album          : $ogginfo{'album'}\n";
548    print ZEPHYR "Track #        : $ogginfo{'tracknumber'}\n";
549
550    # others
551    %lessinfo = %ogginfo;
552    delete $lessinfo{'filename'};
553    delete $lessinfo{'title'};
554    delete $lessinfo{'artist'};
555    delete $lessinfo{'album'};
556    delete $lessinfo{'tracknumber'};
557    delete $lessinfo{'header_integrity'};
558    delete $lessinfo{'stream_integrity'};
559    delete $lessinfo{'file_truncated'};
560    delete $lessinfo{'version'};
561    delete $lessinfo{'channels'};
562    delete $lessinfo{'bitrate_upper'};
563    delete $lessinfo{'bitrate_nominal'};
564    delete $lessinfo{'bitrate_lower'};
565    delete $lessinfo{'bitrate_average'};
566    delete $lessinfo{'length'};
567    delete $lessinfo{'playtime'};
568    delete $lessinfo{'kbps'};
569    delete $lessinfo{'time'};
570    delete $lessinfo{'rg_radio'};
571    delete $lessinfo{'rg_audiophile'};
572    delete $lessinfo{'rg_peak'};
573    delete $lessinfo{'replaygain_album_peak'};
574    delete $lessinfo{'replaygain_track_peak'};
575    delete $lessinfo{'replaygain_album_gain'};
576    delete $lessinfo{'replaygain_track_gain'};
577
578    while (($key,$val) = each %lessinfo) {
579      printf ZEPHYR "%-15s: %s\n", $key, $val;
580    }
581  }
582
583  printf ZEPHYR "\nOgg Vorbis, average %.1f kbit/s, %d channels\n\n",
584    $ogginfo{'bitrate_average'}/1024, $ogginfo{'channels'};
585  print ZEPHYR "Track length: $ogginfo{'playtime'}s\n";
586  close(ZEPHYR);
587
588  # Play the file
589
590  system("chmod a+rw /dev/audio");
591  system("ps -aef | grep ogg123 | awk '{print $2}' | xargs kill -9");
592  system("ps -aef | grep mpg123 | awk '{print $2}' | xargs kill -9");
593  system("/mit/sipb/bin/ogg123 -b 40000 -dau -q -f - - 2> /tmp/ogg123.out | audioplay");
594
595  # Done. Status:
596  open(ZEPHYR, '|/usr/athena/bin/zwrite -d -n -c sipb-auto -i sipbmp3@xcb -s "SIPB LPR music spooler"');
597
598  # Check if there were any errors
599  open(OGGSTATUS, "/tmp/ogg123.out");
600  if ($_ = <OGGSTATUS>) {
601    print ZEPHYR "Playback completed with the following errors:\n";
602    print ZEPHYR $_;
603    while (<OGGSTATUS>) {
604      print ZEPHYR $_;
605    }
606  } else {
607    print ZEPHYR "Playback completed successfully.\n";
608    # Repeat tag data for those playing at home
609    if (!$ogginfo{'title'}) {
610      print ZEPHYR "No ogginfo data found\n";
611      print ZEPHYR "Filename: $opts{'J'}\n\n";
612    } else {
613      print ZEPHYR "Title          : $ogginfo{'title'}\n";
614      print ZEPHYR "Artist         : $ogginfo{'artist'}\n";
615      print ZEPHYR "Album          : $ogginfo{'album'}\n";
616      print ZEPHYR "Track #        : $ogginfo{'tracknumber'}\n";
617
618      # others
619      while (($key,$val) = each %lessinfo) {
620        printf ZEPHYR "%-15s: %s\n", $key, $val;
621      }
622    }
623  }
624
625  close(OGGSTATUS);
626  unlink(OGGSTATUS);
627
628  close(ZEPHYR);
629}
Note: See TracBrowser for help on using the repository browser.