~ubuntu-branches/ubuntu/dapper/xscreensaver/dapper

« back to all changes in this revision

Viewing changes to driver/xscreensaver-text

  • Committer: Bazaar Package Importer
  • Author(s): Oliver Grawert
  • Date: 2005-10-11 21:00:42 UTC
  • mfrom: (2.1.1 sarge)
  • Revision ID: james.westby@ubuntu.com-20051011210042-u7q6zslgevdxspr3
Tags: 4.21-4ubuntu17
updated pt_BR again, fixed to UTF-8 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/perl -w
 
2
# Copyright � 2005 Jamie Zawinski <jwz@jwz.org>
 
3
#
 
4
# Permission to use, copy, modify, distribute, and sell this software and its
 
5
# documentation for any purpose is hereby granted without fee, provided that
 
6
# the above copyright notice appear in all copies and that both that
 
7
# copyright notice and this permission notice appear in supporting
 
8
# documentation.  No representations are made about the suitability of this
 
9
# software for any purpose.  It is provided "as is" without express or 
 
10
# implied warranty.
 
11
#
 
12
# This program writes some text to stdout, based on preferences in the
 
13
# .xscreensaver file.  It may load a file, a URL, run a program, or just
 
14
# print the date.
 
15
#
 
16
# Created: 19-Mar-2005.
 
17
 
 
18
require 5;
 
19
use diagnostics;
 
20
use strict;
 
21
use Socket;
 
22
use POSIX qw(strftime);
 
23
use Text::Wrap qw(wrap);
 
24
use bytes;
 
25
 
 
26
my $progname = $0; $progname =~ s@.*/@@g;
 
27
my $version = q{ $Revision: 1.3 $ }; $version =~ s/^[^0-9]+([0-9.]+).*$/$1/;
 
28
 
 
29
my $verbose = 0;
 
30
my $http_proxy = undef;
 
31
 
 
32
my $config_file = $ENV{HOME} . "/.xscreensaver";
 
33
my $text_mode     = 'date';
 
34
my $text_literal  = '';
 
35
my $text_file     = '';
 
36
my $text_program  = '';
 
37
my $text_url      = '';
 
38
 
 
39
my $wrap_columns  = undef;
 
40
 
 
41
 
 
42
# Maps HTML character entities to the corresponding Latin1 characters.
 
43
#
 
44
my %entity_table = (
 
45
   "quot"   => '"', "amp"    => '&', "lt"     => '<', "gt"     => '>',
 
46
   "nbsp"   => ' ', "iexcl"  => '�', "cent"   => '�', "pound"  => '�',
 
47
   "curren" => '�', "yen"    => '�', "brvbar" => '�', "sect"   => '�',
 
48
   "uml"    => '�', "copy"   => '�', "ordf"   => '�', "laquo"  => '�',
 
49
   "not"    => '�', "shy"    => '�', "reg"    => '�', "macr"   => '�',
 
50
   "deg"    => '�', "plusmn" => '�', "sup2"   => '�', "sup3"   => '�',
 
51
   "acute"  => '�', "micro"  => '�', "para"   => '�', "middot" => '�',
 
52
   "cedil"  => '�', "sup1"   => '�', "ordm"   => '�', "raquo"  => '�',
 
53
   "frac14" => '�', "frac12" => '�', "frac34" => '�', "iquest" => '�',
 
54
   "Agrave" => '�', "Aacute" => '�', "Acirc"  => '�', "Atilde" => '�',
 
55
   "Auml"   => '�', "Aring"  => '�', "AElig"  => '�', "Ccedil" => '�',
 
56
   "Egrave" => '�', "Eacute" => '�', "Ecirc"  => '�', "Euml"   => '�',
 
57
   "Igrave" => '�', "Iacute" => '�', "Icirc"  => '�', "Iuml"   => '�',
 
58
   "ETH"    => '�', "Ntilde" => '�', "Ograve" => '�', "Oacute" => '�',
 
59
   "Ocirc"  => '�', "Otilde" => '�', "Ouml"   => '�', "times"  => '�',
 
60
   "Oslash" => '�', "Ugrave" => '�', "Uacute" => '�', "Ucirc"  => '�',
 
61
   "Uuml"   => '�', "Yacute" => '�', "THORN"  => '�', "szlig"  => '�',
 
62
   "agrave" => '�', "aacute" => '�', "acirc"  => '�', "atilde" => '�',
 
63
   "auml"   => '�', "aring"  => '�', "aelig"  => '�', "ccedil" => '�',
 
64
   "egrave" => '�', "eacute" => '�', "ecirc"  => '�', "euml"   => '�',
 
65
   "igrave" => '�', "iacute" => '�', "icirc"  => '�', "iuml"   => '�',
 
66
   "eth"    => '�', "ntilde" => '�', "ograve" => '�', "oacute" => '�',
 
67
   "ocirc"  => '�', "otilde" => '�', "ouml"   => '�', "divide" => '�',
 
68
   "oslash" => '�', "ugrave" => '�', "uacute" => '�', "ucirc"  => '�',
 
69
   "uuml"   => '�', "yacute" => '�', "thorn"  => '�', "yuml"   => '�',
 
70
   "apos"   => '\''
 
71
);
 
72
 
 
73
# Maps certain UTF8 characters (2 or 3 bytes) to the corresponding
 
74
# Latin1 characters.
 
75
#
 
76
my %unicode_latin1_table = (
 
77
   "\xC2\xA1" => '�', "\xC2\xA2" => '�', "\xC2\xA3" => '�', "\xC2\xA4" => '�',
 
78
   "\xC2\xA5" => '�', "\xC2\xA6" => '�', "\xC2\xA7" => '�', "\xC2\xA8" => '�',
 
79
   "\xC2\xA9" => '�', "\xC2\xAA" => '�', "\xC2\xAB" => '�', "\xC2\xAC" => '�',
 
80
   "\xC2\xAD" => '�', "\xC2\xAE" => '�', "\xC2\xAF" => '�', "\xC2\xB0" => '�',
 
81
   "\xC2\xB1" => '�', "\xC2\xB2" => '�', "\xC2\xB3" => '�', "\xC2\xB4" => '�',
 
82
   "\xC2\xB5" => '�', "\xC2\xB6" => '�', "\xC2\xB7" => '�', "\xC2\xB8" => '�',
 
83
   "\xC2\xB9" => '�', "\xC2\xBA" => '�', "\xC2\xBB" => '�', "\xC2\xBC" => '�',
 
84
   "\xC2\xBD" => '�', "\xC2\xBE" => '�', "\xC2\xBF" => '�', "\xC3\x80" => '�',
 
85
   "\xC3\x81" => '�', "\xC3\x82" => '�', "\xC3\x83" => '�', "\xC3\x84" => '�',
 
86
   "\xC3\x85" => '�', "\xC3\x86" => '�', "\xC3\x87" => '�', "\xC3\x88" => '�',
 
87
   "\xC3\x89" => '�', "\xC3\x8A" => '�', "\xC3\x8B" => '�', "\xC3\x8C" => '�',
 
88
   "\xC3\x8D" => '�', "\xC3\x8E" => '�', "\xC3\x8F" => '�', "\xC3\x90" => '�',
 
89
   "\xC3\x91" => '�', "\xC3\x92" => '�', "\xC3\x93" => '�', "\xC3\x94" => '�',
 
90
   "\xC3\x95" => '�', "\xC3\x96" => '�', "\xC3\x97" => '�', "\xC3\x98" => '�',
 
91
   "\xC3\x99" => '�', "\xC3\x9A" => '�', "\xC3\x9B" => '�', "\xC3\x9C" => '�',
 
92
   "\xC3\x9D" => '�', "\xC3\x9E" => '�', "\xC3\x9F" => '�', "\xC3\xA0" => '�',
 
93
   "\xC3\xA1" => '�', "\xC3\xA2" => '�', "\xC3\xA3" => '�', "\xC3\xA4" => '�',
 
94
   "\xC3\xA5" => '�', "\xC3\xA6" => '�', "\xC3\xA7" => '�', "\xC3\xA8" => '�',
 
95
   "\xC3\xA9" => '�', "\xC3\xAA" => '�', "\xC3\xAB" => '�', "\xC3\xAC" => '�',
 
96
   "\xC3\xAD" => '�', "\xC3\xAE" => '�', "\xC3\xAF" => '�', "\xC3\xB0" => '�',
 
97
   "\xC3\xB1" => '�', "\xC3\xB2" => '�', "\xC3\xB3" => '�', "\xC3\xB4" => '�',
 
98
   "\xC3\xB5" => '�', "\xC3\xB6" => '�', "\xC3\xB7" => '�', "\xC3\xB8" => '�',
 
99
   "\xC3\xB9" => '�', "\xC3\xBA" => '�', "\xC3\xBB" => '�', "\xC3\xBC" => '�',
 
100
   "\xC3\xBD" => '�', "\xC3\xBE" => '�', "\xC3\xBF" => '�',
 
101
 
 
102
   "\xE2\x80\x93" => '--',  "\xE2\x80\x94" => '--',
 
103
   "\xE2\x80\x98" => '`',   "\xE2\x80\x99" => '\'',
 
104
   "\xE2\x80\x9C" => "``",  "\xE2\x80\x9D" => "''",
 
105
   "\xE2\x80\xA6" => '...',
 
106
);
 
107
 
 
108
 
 
109
# Convert any HTML entities to Latin1 characters.
 
110
#
 
111
sub de_entify {
 
112
  my ($text) = @_;
 
113
  $text =~ s/(&(\#)?([[:alpha:]\d]+);?)/
 
114
    {
 
115
     my $c;
 
116
     if ($2) {
 
117
       $c = chr($3);  # the &#number is always decimal, right?
 
118
     } else {
 
119
       $c = $entity_table{$3};
 
120
     }
 
121
#    print STDERR "$progname: warning: unknown HTML character entity \"$1\"\n"
 
122
#     unless $c;
 
123
     ($c ? $c : "[$3]");
 
124
    }
 
125
   /gexi;
 
126
  return $text;
 
127
}
 
128
 
 
129
 
 
130
# Convert any Unicode characters to Latin1 if possible.
 
131
# Unconvertable bytes are left alone.
 
132
#
 
133
sub de_unicoddle {
 
134
  my ($text) = @_;
 
135
  foreach my $key (keys (%unicode_latin1_table)) {
 
136
    my $val = $unicode_latin1_table{$key};
 
137
    $text =~ s/$key/$val/gs;
 
138
  }
 
139
  return $text;
 
140
}
 
141
 
 
142
 
 
143
# Reads the prefs we use from ~/.xscreensaver
 
144
#
 
145
sub get_prefs {
 
146
 
 
147
  my $got_any_p = 0;
 
148
  local *IN;
 
149
 
 
150
  if (open (IN, "<$config_file")) {
 
151
    print STDERR "$progname: reading $config_file\n" if ($verbose > 1);
 
152
    my $body = '';
 
153
    while (<IN>) { $body .= $_; }
 
154
    close IN;
 
155
    $got_any_p = get_prefs_1 ($body);
 
156
 
 
157
  } elsif ($verbose > 1) {
 
158
    print STDERR "$progname: $config_file: $!\n";
 
159
  }
 
160
 
 
161
  if (! $got_any_p) {
 
162
    # We weren't able to read settings from the .xscreensaver file.
 
163
    # Fall back to any settings in the X resource database
 
164
    # (/usr/X11R6/lib/X11/app-defaults/XScreenSaver)
 
165
    #
 
166
    print STDERR "$progname: reading X resources\n" if ($verbose > 1);
 
167
    my $body = `appres XScreenSaver xscreensaver -1`;
 
168
    $got_any_p = get_prefs_1 ($body);
 
169
  }
 
170
 
 
171
  if ($verbose > 1) {
 
172
    printf STDERR "$progname: mode:    $text_mode\n";
 
173
    printf STDERR "$progname: literal: $text_literal\n";
 
174
    printf STDERR "$progname: file:    $text_file\n";
 
175
    printf STDERR "$progname: program: $text_program\n";
 
176
    printf STDERR "$progname: url:     $text_url\n";
 
177
  }
 
178
 
 
179
  $text_mode =~ tr/A-Z/a-z/;
 
180
  $text_literal =~ s@\\n@\n@gs;
 
181
}
 
182
 
 
183
 
 
184
sub get_prefs_1 {
 
185
  my ($body) = @_;
 
186
 
 
187
  my $got_any_p = 0;
 
188
  $body =~ s@\\\n@@gs;
 
189
 
 
190
  if ($body =~ m/^[.*]*textMode:[ \t]*([^\s]+)\s*$/im) {
 
191
    $text_mode = $1;
 
192
    $got_any_p = 1;
 
193
  }
 
194
  if ($body =~ m/^[.*]*textLiteral:[ \t]*(.*?)[ \t]*$/im) {
 
195
    $text_literal = $1;
 
196
  }
 
197
  if ($body =~ m/^[.*]*textFile:[ \t]*(.*?)[ \t]*$/im) {
 
198
    $text_file = $1;
 
199
  }
 
200
  if ($body =~ m/^[.*]*textProgram:[ \t]*(.*?)[ \t]*$/im) {
 
201
    $text_program = $1;
 
202
  }
 
203
  if ($body =~ m/^[.*]*textURL:[ \t]*(.*?)[ \t]*$/im) {
 
204
    $text_url = $1;
 
205
  }
 
206
 
 
207
  return $got_any_p;
 
208
}
 
209
 
 
210
 
 
211
# like system() but checks errors.
 
212
#
 
213
sub safe_system {
 
214
  my (@cmd) = @_;
 
215
 
 
216
  print STDERR "$progname: executing " . join(' ', @cmd) . "\n"
 
217
    if ($verbose > 3);
 
218
 
 
219
  system @cmd;
 
220
  my $exit_value  = $? >> 8;
 
221
  my $signal_num  = $? & 127;
 
222
  my $dumped_core = $? & 128;
 
223
  error ("$cmd[0]: core dumped!") if ($dumped_core);
 
224
  error ("$cmd[0]: signal $signal_num!") if ($signal_num);
 
225
  error ("$cmd[0]: exited with $exit_value!") if ($exit_value);
 
226
}
 
227
 
 
228
 
 
229
sub which {
 
230
  my ($cmd) = @_;
 
231
 
 
232
  if ($cmd =~ m@^\./|^/@) {
 
233
    error ("cannot execute $cmd") unless (-x $cmd);
 
234
    return $cmd;
 
235
  }
 
236
 
 
237
 foreach my $dir (split (/:/, $ENV{PATH})) {
 
238
    my $cmd2 = "$dir/$cmd";
 
239
    print STDERR "$progname:   checking $cmd2\n" if ($verbose > 3);
 
240
    return $cmd2 if (-x "$cmd2");
 
241
  }
 
242
  error ("$cmd not found on \$PATH");
 
243
}
 
244
 
 
245
 
 
246
sub output {
 
247
 
 
248
  # Do some basic sanity checking (null text, null file names, etc.)
 
249
  #
 
250
  if (($text_mode eq 'literal' && $text_literal =~ m/^\s*$/i) ||
 
251
      ($text_mode eq 'file'    && $text_file    =~ m/^\s*$/i) ||
 
252
      ($text_mode eq 'program' && $text_program =~ m/^\s*$/i) ||
 
253
      ($text_mode eq 'url'     && $text_url     =~ m/^\s*$/i)) {
 
254
    print STDERR "$progname: falling back to 'date'\n" if ($verbose);
 
255
    $text_mode = 'date';
 
256
  }
 
257
 
 
258
  if ($text_mode eq 'literal') {
 
259
    $text_literal = strftime ($text_literal, localtime);
 
260
    print STDOUT $text_literal;
 
261
    print STDOUT "\n" unless ($text_literal =~ m/\n$/s);
 
262
 
 
263
  } elsif ($text_mode eq 'file') {
 
264
 
 
265
    local *IN;
 
266
    if (open (IN, "<$text_file")) {
 
267
      print STDERR "$progname: reading $text_file\n" if ($verbose);
 
268
 
 
269
      if ($wrap_columns && $wrap_columns > 0) {
 
270
        # read it, then reformat it.
 
271
        my $body = '';
 
272
        while (<IN>) { $body .= $_; }
 
273
        reformat_text ($body);
 
274
      } else {
 
275
        # stream it
 
276
        while (<IN>) { print $_; }
 
277
      }
 
278
      close IN;
 
279
    } else {
 
280
      error ("$text_file: $!");
 
281
    }
 
282
 
 
283
  } elsif ($text_mode eq 'program') {
 
284
 
 
285
    $text_program = which ($text_program);
 
286
    print STDERR "$progname: running $text_program\n" if ($verbose);
 
287
 
 
288
    if ($wrap_columns && $wrap_columns > 0) {
 
289
      # read it, then reformat it.
 
290
      my $body = `( $text_program ) 2>&1`;
 
291
      reformat_text ($body);
 
292
    } else {
 
293
      # stream it
 
294
      safe_system ("$text_program");
 
295
    }
 
296
 
 
297
  } elsif ($text_mode eq 'url') {
 
298
 
 
299
    get_url_text ($text_url);
 
300
 
 
301
  } else { # $text_mode eq 'date'
 
302
 
 
303
    safe_system ("uname", "-n");
 
304
    if (-f "/etc/redhat-release") { system ("cat", "/etc/redhat-release"); }
 
305
    safe_system ("uname", "-sr");
 
306
    print "\n";
 
307
    safe_system ("date", "+%c");
 
308
    print "\n";
 
309
    my $ut = `uptime`;
 
310
    $ut =~ s/^[ \d:]*//;
 
311
    $ut =~ s/,\s*(load)/\n$1/;
 
312
    print "$ut\n";
 
313
  }
 
314
 
 
315
}
 
316
 
 
317
 
 
318
# Loads the given URL, returns: $http, $head, $body.
 
319
#
 
320
sub get_url_1 {
 
321
  my ($url, $referer) = @_;
 
322
  
 
323
  if (! ($url =~ m@^http://@i)) {
 
324
    error ("not an HTTP URL: $url");
 
325
  }
 
326
 
 
327
  my ($url_proto, $dummy, $serverstring, $path) = split(/\//, $url, 4);
 
328
  $path = "" unless $path;
 
329
 
 
330
  my ($them,$port) = split(/:/, $serverstring);
 
331
  $port = 80 unless $port;
 
332
 
 
333
  my $them2 = $them;
 
334
  my $port2 = $port;
 
335
  if ($http_proxy) {
 
336
    $serverstring = $http_proxy if $http_proxy;
 
337
    $serverstring =~ s@^[a-z]+://@@;
 
338
    ($them2,$port2) = split(/:/, $serverstring);
 
339
    $port2 = 80 unless $port2;
 
340
  }
 
341
 
 
342
  my ($remote, $iaddr, $paddr, $proto, $line);
 
343
  $remote = $them2;
 
344
  if ($port2 =~ /\D/) { $port2 = getservbyname($port2, 'tcp') }
 
345
  if (!$port2) {
 
346
    error ("unrecognised port in $url");
 
347
  }
 
348
 
 
349
  $iaddr = inet_aton($remote);
 
350
  return ("error", "host not found", "$remote") unless ($iaddr);
 
351
 
 
352
  $paddr   = sockaddr_in($port2, $iaddr);
 
353
 
 
354
 
 
355
  my $head = "";
 
356
  my $body = "";
 
357
 
 
358
  $proto   = getprotobyname('tcp');
 
359
  if (!socket(S, PF_INET, SOCK_STREAM, $proto)) {
 
360
    error ("socket: $!");
 
361
  }
 
362
  if (!connect(S, $paddr)) {
 
363
    return ("error", "connect($serverstring)", "$!");
 
364
  }
 
365
 
 
366
  select(S); $| = 1; select(STDOUT);
 
367
 
 
368
  my $user_agent = "$progname/$version";
 
369
 
 
370
  my $hdrs = ("GET " . ($http_proxy ? $url : "/$path") . " HTTP/1.0\r\n" .
 
371
              "Host: $them\r\n" .
 
372
              "User-Agent: $user_agent\r\n");
 
373
  if ($referer) {
 
374
    $hdrs .= "Referer: $referer\r\n";
 
375
  }
 
376
  $hdrs .= "\r\n";
 
377
 
 
378
  if ($verbose > 3) {
 
379
    foreach (split('\r?\n', $hdrs)) {
 
380
      print STDERR "  ==> $_\n";
 
381
    }
 
382
  }
 
383
  print S $hdrs;
 
384
  my $http = <S> || "";
 
385
 
 
386
  $_  = $http;
 
387
  s/[\r\n]+$//s;
 
388
  print STDERR "  <== $_\n" if ($verbose > 3);
 
389
 
 
390
  while (<S>) {
 
391
    $head .= $_;
 
392
    s/[\r\n]+$//s;
 
393
    last if m@^$@;
 
394
    print STDERR "  <== $_\n" if ($verbose > 3);
 
395
  }
 
396
 
 
397
  print STDERR "  <== \n" if ($verbose > 4);
 
398
  my $lines = 0;
 
399
  while (<S>) {
 
400
    s/\r\n/\n/gs;
 
401
    print STDERR "  <== $_" if ($verbose > 4);
 
402
    $body .= $_;
 
403
    $lines++;
 
404
  }
 
405
 
 
406
  print STDERR "  <== [ body ]: $lines lines, " . length($body) . " bytes\n"
 
407
    if ($verbose == 4);
 
408
 
 
409
  close S;
 
410
 
 
411
  if (!$http) {
 
412
    error ("null response: $url");
 
413
  }
 
414
 
 
415
  return ( $http, $head, $body );
 
416
}
 
417
 
 
418
 
 
419
# Loads the given URL, processes redirects, returns (content-type, body).
 
420
#
 
421
sub get_url {
 
422
  my ($url, $referer) = @_;
 
423
 
 
424
  print STDERR "$progname: loading $url\n" if ($verbose > 2);
 
425
 
 
426
  my $orig_url = $url;
 
427
  my $loop_count = 0;
 
428
  my $max_loop_count = 10;
 
429
 
 
430
  do {
 
431
    my ( $http, $head, $body ) = get_url_1 ($url, $referer);
 
432
 
 
433
    if ( $http eq "error" ) {
 
434
      return ("error", "$head: $body");
 
435
    }
 
436
 
 
437
    $http =~ s/[\r\n]+$//s;
 
438
 
 
439
    if ( $http =~ m@^HTTP/[0-9.]+ 30[123]@ ) {
 
440
      $_ = $head;
 
441
 
 
442
      my ( $location ) = m@^location:[ \t]*(.*)$@im;
 
443
      if ( $location ) {
 
444
        $location =~ s/[\r\n]$//;
 
445
 
 
446
        print STDERR "$progname: redirect from $url to $location\n"
 
447
          if ($verbose > 3);
 
448
 
 
449
        $referer = $url;
 
450
        $url = $location;
 
451
 
 
452
        if ($url =~ m@^/@) {
 
453
          $referer =~ m@^(http://[^/]+)@i;
 
454
          $url = $1 . $url;
 
455
        } elsif (! ($url =~ m@^[a-z]+:@i)) {
 
456
          $_ = $referer;
 
457
          s@[^/]+$@@g if m@^http://[^/]+/@i;
 
458
          $_ .= "/" if m@^http://[^/]+$@i;
 
459
          $url = $_ . $url;
 
460
        }
 
461
 
 
462
      } else {
 
463
        error ("no Location with \"$http\"");
 
464
      }
 
465
 
 
466
      if ($loop_count++ > $max_loop_count) {
 
467
        error ("too many redirects ($max_loop_count) from $orig_url");
 
468
      }
 
469
 
 
470
    } elsif ( $http =~ m@^HTTP/[0-9.]+ ([4-9][0-9][0-9].*)$@ ) {
 
471
      error ("failed: $1 ($url)");
 
472
 
 
473
    } else {
 
474
      my $ct = 'text/plain';
 
475
      $ct = $1 if ($head =~ m/^content-type:\s*([^\s]+)/mi);
 
476
      return ($ct, $body);
 
477
    }
 
478
  } while (1);
 
479
}
 
480
 
 
481
 
 
482
# Make an educated guess as to what's in this document.
 
483
# We don't necessarily take the Content-Type header at face value.
 
484
# Returns 'html', 'rss', or 'text';
 
485
#
 
486
sub guess_content_type {
 
487
  my ($ct, $body) = @_;
 
488
 
 
489
  $body =~ s/^(.{512}).*/$1/s;  # only look in first half K of file
 
490
 
 
491
  if ($ct =~ m@^text/.*html@i)          { return 'html'; }
 
492
  if ($ct =~ m@\b(atom|rss|xml)\b@i)    { return 'rss';  }
 
493
 
 
494
  if ($body =~ m@^\s*<\?xml@is)         { return 'rss';  }
 
495
  if ($body =~ m@^\s*<!DOCTYPE RSS@is)  { return 'rss';  }
 
496
  if ($body =~ m@^\s*<!DOCTYPE HTML@is) { return 'html'; }
 
497
 
 
498
  if ($body =~ m@<(BASE|HTML|HEAD|BODY|SCRIPT|STYLE|TABLE|A\s+HREF)\b@i) {
 
499
    return 'html';
 
500
  }
 
501
 
 
502
  if ($body =~ m@<(RSS|CHANNEL|GENERATOR|DESCRIPTION|CONTENT|FEED|ENTRY)\b@i) {
 
503
    return 'rss';
 
504
  }
 
505
 
 
506
  return 'text';
 
507
}
 
508
 
 
509
sub reformat_html {
 
510
  my ($body, $rss_p) = @_;
 
511
  $_ = $body;
 
512
 
 
513
  if (! $rss_p) {
 
514
    # In HTML, unfold lines (this breaks PRE.  Sue me.)
 
515
    # In RSS, assume \n means literal line break.
 
516
    s@[\r\n]@ @gsi;
 
517
  }
 
518
 
 
519
  s@<!--.*?-->@@gsi;                             # lose comments
 
520
  s@<(STYLE|SCRIPT)\b[^<>]*>.*?</\1\s*>@@gsi;    # lose css and js
 
521
 
 
522
  s@</?(BR|TR|TD|LI|DIV)\b[^<>]*>@\n@gsi; # line break at BR, TD, DIV, etc
 
523
  s@</?(P|UL|OL|BLOCKQUOTE)\b[^<>]*>@\n\n@gsi; # two line breaks
 
524
 
 
525
  s@<lj\s+user=\"?([^<>\"]+)\"?[^<>]*>?@$1@gsi;  # handle <LJ USER=>
 
526
  s@</?[BI]>@*@gsi;                              # bold, italic => asterisks
 
527
 
 
528
 
 
529
  s@<[^<>]*>?@@gs;                # lose all other HTML tags
 
530
  $_ = de_entify ($_);            # convert HTML entities
 
531
 
 
532
  # elide any remaining non-Latin1 binary data...
 
533
  s/([\177-\377]+(\s*[\177-\377]+)[^a-z\d]*)/�...� /g;
 
534
  #s/([\177-\377]+(\s*[\177-\377]+)[^a-z\d]*)/�$1� /g;
 
535
 
 
536
  $_ .= "\n";
 
537
 
 
538
  s/[ \t]+$//gm;                  # lose whitespace at end of line
 
539
  s@\n\n\n+@\n\n@gs;              # compress blank lines
 
540
 
 
541
  if (!defined($wrap_columns) || $wrap_columns > 0) {
 
542
    $Text::Wrap::columns = ($wrap_columns || 72);
 
543
    $_ = wrap ("", "  ", $_);     # wrap the lines as a paragraph
 
544
    s/[ \t]+$//gm;                # lose whitespace at end of line again
 
545
  }
 
546
 
 
547
  print STDOUT $_;
 
548
}
 
549
 
 
550
 
 
551
sub reformat_rss {
 
552
  my ($body) = @_;
 
553
 
 
554
  $body =~ s/(<(ITEM|ENTRY)\b)/\001\001$1/gsi;
 
555
  my @items = split (/\001\001/, $body);
 
556
  shift @items;
 
557
 
 
558
  # Let's skip forward in the stream by a random amount, so that if
 
559
  # two copies of ljlatest are running at the same time (e.g., on a
 
560
  # multi-headed machine), they get different text.  (Put the items
 
561
  # that we take off the front back on the back.)
 
562
  #
 
563
  if ($#items > 10) {
 
564
    my $n = int (rand ($#items - 5));
 
565
    while ($n-- > 0) {
 
566
      push @items, (shift @items);
 
567
    }
 
568
  }
 
569
 
 
570
  my $i = 0;
 
571
  foreach (@items) {
 
572
 
 
573
    my ($title, $body1, $body2);
 
574
    
 
575
    $title = $2 if (m@<(TITLE            [^<>\s]*)[^<>]*>\s*(.*?)\s*</\1>@xsi);
 
576
    $body1 = $3 if (m@<((DESCRIPTION)    [^<>\s]*)[^<>]*>\s*(.*?)\s*</\1>@xsi);
 
577
    $body2 = $3 if (m@<((CONTENT)        [^<>\s]*)[^<>]*>\s*(.*?)\s*</\1>@xsi);
 
578
 
 
579
    # If there are both <description> and <content> or <content:encoded>,
 
580
    # use whichever one contains more text.
 
581
    #
 
582
    if ($body1 && $body2 && length($body2) >= length($body1)) {
 
583
      $body1 = $body2;
 
584
    }
 
585
 
 
586
    next unless defined ($body1);
 
587
    $title = rss_field_to_html ($title);
 
588
    $body1 = rss_field_to_html ($body1);
 
589
 
 
590
    reformat_html ("$title<P>$body1", 1);
 
591
    print "\n";
 
592
  }
 
593
}
 
594
 
 
595
 
 
596
sub rss_field_to_html {
 
597
  my ($body) = @_;
 
598
 
 
599
  # Assume that if <![CDATA[...]]> is present, everything inside that.
 
600
  #
 
601
  if ($body =~ m/^\s*<!\[CDATA\[(.*?)\]\s*\]/is) {
 
602
    $body = $1;
 
603
  } else {
 
604
    $body = de_entify ($body);      # convert entities to get HTML from XML
 
605
  }
 
606
 
 
607
  $body = de_unicoddle ($body);     # convert UTF8 to Latin1
 
608
  return $body;
 
609
}
 
610
 
 
611
 
 
612
sub reformat_text {
 
613
  my ($body) = @_;
 
614
 
 
615
  # only re-wrap if --cols was specified.  Otherwise, dump it as is.
 
616
  #
 
617
  if ($wrap_columns && $wrap_columns > 0) {
 
618
    print STDERR "$progname: wrapping at $wrap_columns...\n" if ($verbose > 2);
 
619
    $Text::Wrap::columns = $wrap_columns;
 
620
    $body = wrap ("", "", $body);
 
621
    $body =~ s/[ \t]+$//gm;
 
622
  }
 
623
 
 
624
  print STDOUT $body;
 
625
}
 
626
 
 
627
 
 
628
sub get_url_text {
 
629
  my ($url) = @_;
 
630
 
 
631
  # historical suckage: the environment variable name is lower case.
 
632
  $http_proxy = $ENV{http_proxy} || $ENV{HTTP_PROXY};
 
633
 
 
634
  if ($http_proxy && $http_proxy =~ m@^http://([^/]*)/?$@ ) {
 
635
    # historical suckage: allow "http://host:port" as well as "host:port".
 
636
    $http_proxy = $1;
 
637
  }
 
638
 
 
639
  my ($ct, $body) = get_url ($url);
 
640
  if ($ct eq "error") {
 
641
    $text_mode = 'file';
 
642
    output ();
 
643
    exit;
 
644
  }
 
645
 
 
646
  $ct = guess_content_type ($ct, $body);
 
647
  if ($ct eq 'html') {
 
648
    print STDERR "$progname: converting HTML...\n" if ($verbose > 2);
 
649
    reformat_html ($body, 0);
 
650
  } elsif ($ct eq 'rss')  {
 
651
    print STDERR "$progname: converting RSS...\n" if ($verbose > 2);
 
652
    reformat_rss ($body);
 
653
  } else {
 
654
    print STDERR "$progname: plain text...\n" if ($verbose > 2);
 
655
    reformat_text ($body);
 
656
  }
 
657
}
 
658
 
 
659
 
 
660
 
 
661
sub error {
 
662
  my ($err) = @_;
 
663
  print STDERR "$progname: $err\n";
 
664
  exit 1;
 
665
}
 
666
 
 
667
sub usage {
 
668
  print STDERR "usage: $progname [ --options ... ]\n" .
 
669
   ("\n" .
 
670
    "       Prints out some text for use by various screensavers,\n" .
 
671
    "       according to the options in the ~/.xscreensaver file.\n" .
 
672
    "       This may dump the contents of a file, run a program,\n" .
 
673
    "       or load a URL.\n".
 
674
    "\n" .
 
675
    "   Options:\n" .
 
676
    "\n" .
 
677
    "       --date           Print the host name and current time.\n" .
 
678
    "\n" .
 
679
    "       --text STRING    Print out the given text.  It may contain %\n" .
 
680
    "                        escape sequences as per strftime(2).\n" .
 
681
    "\n" .
 
682
    "       --file PATH      Print the contents of the given file.\n" .
 
683
    "                        If --cols is specified, re-wrap the lines;\n" .
 
684
    "                        otherwise, print them as-is.\n" .
 
685
    "\n" .
 
686
    "       --program CMD    Run the given program and print its output.\n" .
 
687
    "                        If --cols is specified, re-wrap the output.\n" .
 
688
    "\n" .
 
689
    "       --url HTTP-URL   Download and print the contents of the HTTP\n" .
 
690
    "                        document.  If it contains HTML, RSS, or Atom,\n" .
 
691
    "                        it will be converted to plain-text.\n" .
 
692
    "\n" .
 
693
    "       --cols N         Wrap lines at this column.  Default 72.\n" .
 
694
    "\n");
 
695
  exit 1;
 
696
}
 
697
 
 
698
sub main {
 
699
 
 
700
  my $load_p = 1;
 
701
 
 
702
  while ($#ARGV >= 0) {
 
703
    $_ = shift @ARGV;
 
704
    if ($_ eq "--verbose") { $verbose++; }
 
705
    elsif (m/^-v+$/) { $verbose += length($_)-1; }
 
706
    elsif (m/^--?date$/)    { $text_mode = 'date';
 
707
                              $load_p = 0; }
 
708
    elsif (m/^--?text$/)    { $text_mode = 'literal';
 
709
                              $text_literal = shift @ARGV;
 
710
                              $load_p = 0; }
 
711
    elsif (m/^--?file$/)    { $text_mode = 'file';
 
712
                              $text_file = shift @ARGV;
 
713
                              $load_p = 0; }
 
714
    elsif (m/^--?program$/) { $text_mode = 'program';
 
715
                              $text_program = shift @ARGV;
 
716
                              $load_p = 0; }
 
717
    elsif (m/^--?url$/)     { $text_mode = 'url';
 
718
                              $text_url = shift @ARGV;
 
719
                              $load_p = 0; }
 
720
    elsif (m/^--?col(umn)?s?$/) { $wrap_columns = 0 + shift @ARGV; }
 
721
    elsif (m/^-./) { usage; }
 
722
    else { usage; }
 
723
  }
 
724
 
 
725
  get_prefs() if ($load_p);
 
726
  output();
 
727
}
 
728
 
 
729
main;
 
730
exit 0;