3 # Filter this script to pod2man to get a man page:
4 # pod2man -c "Fvwm Utility" fvwm-menu-headlines | nroff -man | less -e
8 use vars qw($siteInfo @smonths @lmonths %smonthHash %lmonthHash);
9 use vars qw($entityMap $errorMenuContent);
12 use POSIX qw(strftime);
15 my $version = "@VERSION@";
19 'name' => "FreshMeat",
20 'host' => "freshmeat.net",
21 'path' => "/backend/recentnews.txt",
22 'func' => \&processFreshMeat,
23 'flds' => 'headline, date, url',
27 'host' => "slashdot.org",
28 'path' => "/slashdot.xml",
29 'func' => \&processSlashdot,
30 'flds' => 'title, url, time, author, department, topic, comments, section, image',
33 'name' => "LinuxToday",
34 'host' => "linuxtoday.com",
35 'path' => "/lthead.txt",
36 'func' => \&processLinuxToday,
37 'flds' => 'headline, url, date',
41 'host' => "segfault.org",
42 'path' => "/stories.txt",
43 'func' => \&processSegfault,
44 'flds' => 'headline, url, date, author_name, author_email, type',
48 'host' => "www.appwatch.com",
49 'path' => "/appwatch.rdf",
50 'func' => \&processPoorRdf,
51 'flds' => 'title, link, description',
54 'name' => "LinuxApps",
55 'host' => "www.linuxapps.com",
56 'path' => "/backend/linux_basic.txt",
57 'func' => \&processLinuxApps,
58 'flds' => 'headline, date, url',
61 'name' => "JustLinux",
62 'host' => "www.justlinux.com",
63 'path' => "/backend/discussion.rdf",
64 'func' => \&processPoorRdf,
65 'flds' => 'title, link',
68 'name' => "DaemonNews",
69 'host' => "daily.daemonnews.org",
70 'path' => "/ddn.rdf.php3",
71 'func' => \&processPoorRdf,
72 'flds' => 'title, link',
75 'name' => "GNOME-News",
76 'host' => "news.gnome.org",
78 'func' => \&process_GNOME_KDE_News,
79 'flds' => 'title, link',
83 'host' => "news.kde.org",
85 'func' => \&process_GNOME_KDE_News,
86 'flds' => 'title, link',
90 'host' => "freekde.org",
91 'path' => "/freekdeorg.rdf",
92 'func' => \&processFreeKDE,
93 'flds' => 'title, link',
96 'name' => "RootPrompt",
97 'host' => "rootprompt.org",
99 'func' => \&processRootPrompt,
100 'flds' => 'title, link, description',
103 'name' => "NewsForge",
104 'host' => "www.newsforge.com",
105 'path' => "/newsforge.xml",
106 'func' => \&processSlashdot,
107 'flds' => 'title, url, time, author, topic, comments, section, image, description',
110 'name' => "Kuro5hin",
111 'host' => "www.kuro5hin.org",
112 'path' => "/backend.rdf",
113 'func' => \&processKuro5hin,
114 'flds' => 'title, link, description',
118 'host' => "bbspot.com",
119 'path' => "/bbspot.rdf",
120 'func' => \&processPoorRdf,
121 'flds' => 'title, link',
125 'host' => "linuxfr.org",
126 'path' => "/short.php3",
127 'func' => \&processLinuxFr,
128 'flds' => 'headline, url, author_name, author_email, type',
131 'name' => "ThinkGeek",
132 'host' => "www.thinkgeek.com",
133 'path' => "/thinkgeek.rdf",
134 'func' => \&processPoorRdf,
135 'flds' => 'title, link',
139 # Site specific parsers may use these constants to convert month to unix time.
140 local @smonths = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
141 local @lmonths = qw(January February March April May June July August September October November December);
142 local (%smonthHash, %lmonthHash) = ();
143 foreach (0 .. 11) { $smonthHash{$smonths[$_]} = $_; $lmonthHash{$lmonths[$_]} = $_; }
145 my $home = $ENV{'HOME'} || '/tmp';
146 my $fvwmUserDir = $ENV{'FVWM_USERDIR'} || "$home/.fvwm";
147 $fvwmUserDir = $home unless -d $fvwmUserDir;
148 my $workHome = "$fvwmUserDir/.fvwm-menu-headlines";
150 require "$workHome/extension.pl" if -r "$workHome/extension.pl";
153 my $defaultSite = 'freshmeat';
157 my $itemF = '%h\t(%[%Y-%m-%d %H:%M])';
158 my $execF = q(netscape -remote 'openURL(%u, new-window)' || netscape '%u');
167 my $frontpage = undef;
169 my @time = localtime();
170 my $menuFile = undef;
171 my $fakeFile = undef;
172 my $endl = "\r\n"; # this is preferable for http sockets to "\n"
175 "help" => \&showHelp,
176 "version" => \&showVersion,
180 "title=s" => \$title,
183 "command=s" => \$commF,
184 "icon-title=s" => \$iconT,
185 "icon-item=s" => \$iconI,
186 "icon-home=s" => \$iconH,
187 "wm-icons" => \$wmIcons,
188 "proxy=s" => \$proxy,
189 "frontpage:s" => \$frontpage,
190 "file:s" => \$menuFile,
191 "fake:s" => \$fakeFile,
193 wrongUsage() if @ARGV;
197 my $_info = $siteInfo->{lc($info)};
198 die "Unsupported site '$info'; try --info.\n" unless $_info;
200 "Site Name:\n\t$_info->{'name'}\n",
201 "Home Page:\n\thttp://$_info->{'host'}/\n",
202 "Headlines:\n\thttp://$_info->{'host'}$_info->{'path'}\n",
203 "Headline fields:\n\t$_info->{'flds'}\n";
205 print "All supported sites:\n\t", join(", ", getAllSiteNames()),
206 "\n\nSpecify a site name after --info to get a site headlines info.\n";
211 $site ||= $defaultSite; $site = lc($site);
212 die "Unsupported site '$site'; try --info.\n" unless exists $siteInfo->{$site};
213 #$name ||= "MenuHeadlines$siteInfo->{$site}->{'name'}";
215 $title ||= "$siteInfo->{$site}->{'name'} Headlines";
217 my $siteName = $siteInfo->{$site}->{'name'};
218 my $siteHost = $siteInfo->{$site}->{'host'};
219 my $sitePath = $siteInfo->{$site}->{'path'};
220 my $siteFunc = $siteInfo->{$site}->{'func'};
222 $commF ||= "Exec $execF";
224 $title =~ s/\\t/\t/g;
225 $itemF =~ s/\\t/\t/g;
226 $commF =~ s/\\t/\t/g;
230 $iconI ||= "menu/information.xpm";
231 $iconH ||= "menu/home.xpm";
234 my $iconTStr = $iconT? "%$iconT%": "";
235 my $iconIStr = $iconI? "%$iconI%": "";
236 my $iconHStr = $iconH? "%$iconH%": "";
238 if (defined $proxy && $proxy =~ /^(.+):(\d+)$/) {
244 # 1) no --file option or value '-' specified (STDOUT is used)
245 # 2) no or empty menu file in --file specified (the default name is used)
246 # 3) non-empty menu file specified (use it)
247 $menuFile = undef if defined $menuFile && $menuFile eq '-';
249 $menuFile =~ s:^~(/|$):$home$1:;
250 $menuFile =~ m:^(.+)/[^/]+$:; $workHome = $1 || ".";
251 } elsif (defined $menuFile) {
252 $menuFile = "$workHome/$site.menu";
257 $content .= qq(DestroyMenu $name\n);
258 $content .= qq(AddToMenu $name "$iconTStr$title" Title\n);
259 local $errorMenuContent = $content . "+ `<msg>` DestroyMenu $name\n";
261 if (defined $frontpage && $frontpage !~ /^b/) {
262 my $cmd = expandAllWidthSpecifiers($commF, {'u' => "http://$siteHost/"});
263 $content .= qq(+ "$iconHStr$siteName Frontpage" $cmd\n);
264 $content .= qq(+ "" Nop\n);
267 unless (defined $fakeFile) {
268 # network connection portion is pretty much stolen from 'man perlipc'
269 my $host = $proxy || $siteHost;
270 my $iaddr = inet_aton($host) || dieNet("Can't resolve host $host");
271 my $paddr = sockaddr_in($port, $iaddr);
272 my $proto = getprotobyname('tcp');
273 socket(SOCK, PF_INET, SOCK_STREAM, $proto) &&
274 connect(SOCK, $paddr) || dieNet("Can't connect host $host");
275 select(SOCK); $| = 1; select(STDOUT);
278 my $httpHeaders = "$endl" .
279 "Host: $siteHost$endl" .
280 "Connection: close$endl" .
281 "User-Agent: fvwm-menu-headlines/$version$endl$endl";
282 if (defined $proxy) {
283 print SOCK "GET http://$siteHost$sitePath HTTP/1.1$httpHeaders";
285 print SOCK "GET $sitePath HTTP/1.1$httpHeaders";
288 # skip http response headers
289 while (<SOCK> !~ /^\r?\n$/s) {}
292 $fakeFile =~ s:^~(/|$):$home$1:;
294 $fakeFile = "$workHome/$site.in";
296 open(SOCK, "<$fakeFile") || dieSys("Can't open $fakeFile");
299 my $entries = &$siteFunc;
301 close(SOCK) || dieNet("Error closing socket");
303 foreach (@$entries) {
304 my $text = expandAllWidthSpecifiers($itemF, $_);
305 my $comm = expandAllWidthSpecifiers($commF, $_);
307 $content .= qq(+ "$iconIStr$text" $comm\n);
310 if (defined $frontpage && $frontpage =~ /^b/) {
311 my $cmd = expandAllWidthSpecifiers($commF, {'u' => "http://$siteHost/"});
312 $content .= qq(+ "" Nop\n);
313 $content .= qq(+ "$iconHStr$siteName Frontpage" $cmd\n);
316 if (defined $menuFile) {
317 unless (-d $workHome) {
318 mkdir($workHome, 0775) || dieSys("Can't create $workHome");
320 open(MENU_FILE, ">$menuFile") || dieSys("Can't open $menuFile");
321 print MENU_FILE $content;
322 close(MENU_FILE) || dieSys("Can't close $menuFile");
329 # ---------------------------------------------------------------------------
331 # make unix time from year (2001 or 101), mon (0..11), day, hour, min, sec
332 sub makeTime { # ($$$$$$$)
333 my ($year, $mon, $day, $hour, $min, $sec, $hourD) = @_;
334 $year = 1973 unless $year && $year > 0; # it's my year :-)
335 $mon = 0 unless $mon && $mon > 0 && $mon <= 11;
336 $day = 1 unless $day && $day > 0 && $day <= 31;
337 $hour = 0 unless $hour && $hour >= 0 && $hour < 24;
338 $min = 0 unless $min && $min >= 0 && $min < 60;
339 $sec = 0 unless $sec && $sec >= 0 && $sec < 60;
341 return timegm($sec, $min, $hour, $day, $mon, $year) - $hourD * 60 * 60;
344 sub setEntryAliasesAndTime ($$$$) {
351 while (($alias, $orig) = each %$aliases) {
352 $entry->{$alias} = !$orig? "":
353 ref($orig) eq 'CODE'? &{$orig}($entry): $entry->{$orig};
354 $entry->{$alias} = "" unless defined $entry->{$alias};
357 $entry->{'_'} = makeTime(&{$timeSub}($entry->{'d'}), $hOffset);
369 sub processXml ($$$$) {
370 my $entryTag = shift;
375 my $doc = join("", <SOCK>);
376 print STDERR $doc if $ENV{"DEBUG_DUMP_RESPONSE"};
379 foreach ($doc =~ m!<$entryTag\b[^>]*>(.*?)</$entryTag>!sg) {
380 # replace ' with single quote and " with double quote
381 s/&(?:(\w+)|#(\d+));/ $1? $entityMap->{$1} || "{$1}": chr($2) /ge;
385 foreach (m!(<.*?>.*?</.*?>)!sg) {
386 m!<(.*?)>\s*(.*?)\s*</(.*?)>!s;
387 # ignore incorect fields or throw error?
388 next unless $1 && $2 && $3;
393 setEntryAliasesAndTime($entry, $aliases, $timeSub, $hOffset);
394 push @entries, $entry;
399 sub processText ($$$$) {
411 # ### It waits 15 seconds until returning last undef :-(
413 # local $SIG{ALRM} = sub { die "\n"; };
414 # alarm(1); $line = <SOCK>; alarm(0);
417 last ENTRY unless defined $line;
418 next if $_ eq '_ignore_';
419 print STDERR $line if $ENV{"DEBUG_DUMP_RESPONSE"};
423 # $line =~ s/<.*?>//g;
424 # $line =~ s/&\w{1,5}?;/ /g;
425 $entry->{$_} = $line;
428 setEntryAliasesAndTime($entry, $aliases, $timeSub, $hOffset);
429 push @entries, $entry;
434 sub processSlashdot () {
437 { 'h' => 'title', 'u' => 'url', 'd' => 'time' },
439 $_[0] =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/;
440 ($1, ($2 || 0) - 1, $3, $4, $5, $6);
445 sub processFreshMeat () {
447 [ qw( headline date url ) ],
448 { 'h' => 'headline', 'u' => 'url', 'd' => 'date' },
450 $_[0] =~ /^(?:\w+, )?(\w+) (\d+)\w* (\d+),? (\d+):(\d+)/;
451 ($3, $lmonthHash{$1}, $2, $4, $5, 0);
452 }, -5 + (abs((localtime())[4] - 5.5) < 3),
456 sub processLinuxToday () {
458 last if /linuxtoday.com/; # skip the text note
459 last if /&&/ and <SOCK> x 3; # if the note was changed
462 [ qw( _ignore_ headline url date ) ],
463 { 'h' => 'headline', 'u' => 'url', 'd' => 'date' },
465 $_[0] =~ /(\w+) (\d+), (\d+), (\d+):(\d+):(\d+)/;
466 ($3, $smonthHash{$1}, $2, $4, $5, $6);
471 sub processSegfault () {
473 last if /^%%/; # skip the text note
476 [ qw( headline url date author_name author_email type _ignore_ ) ],
477 { 'h' => 'headline', 'u' => 'url', 'd' => 'date' },
479 $_[0] =~ /(\d+) (\w+) (\d+):(\d+):(\d+) (\d+)/;
480 ($6, $smonthHash{$2}, $1, $3, $4, $5);
481 }, -8 + (abs((localtime())[4] - 5.5) < 3),
485 sub processPoorRdf () {
488 { 'h' => 'title', 'u' => 'link', 'd' => undef },
490 # this site's rdf does not supply the time, how weird...
491 (gmtime())[5,4,3,2,1,0];
496 sub processLinuxApps () {
498 [ qw( headline date url ) ],
499 { 'h' => 'headline', 'u' => 'url', 'd' => 'date' },
501 $_[0] =~ /(\w+) (\d+) (\d+):(\d+):(\d+) \w+ (\d+)/;
502 ($6, $smonthHash{$1}, $2, $3, $4, $5);
507 sub process_GNOME_KDE_News () {
508 my $linkToTime = sub ($) { $_[0]->{'link'} =~ m|/(\d+)/?$|; $1; };
511 { 'h' => 'title', 'u' => 'link', 'd' => $linkToTime },
513 (gmtime($_[0]))[5,4,3,2,1,0];
518 sub processFreeKDE () {
519 my $linkToDate = sub ($) {
520 $_[0]->{'link'} =~ m|/(\d\d/\d\d/\d\d)/|; $1? "20$1": '';
524 { 'h' => 'title', 'u' => 'link', 'd' => $linkToDate },
526 $_[0] =~ m|(\d+)/(\d+)/(\d+)|;
527 ($1, ($2 || 0) - 1, $3);
532 sub processRootPrompt () {
533 my $descToDate = sub ($) {
534 $_[0]->{'description'} =~ /^(\d+ \w{3} \d{4}):/; $1;
538 { 'h' => 'title', 'u' => 'link', 'd' => $descToDate },
540 $_[0] =~ /(\d+) (\w+) (\d+)/;
541 ($3, $smonthHash{$2}, $1);
546 sub processKuro5hin () {
547 my $linkToDate = sub ($) {
548 $_[0]->{'link'} =~ m|sid=(\d\d\d\d/\d{1,2}/\d{1,2})/|; $1;
552 { 'h' => 'title', 'u' => 'link', 'd' => $linkToDate },
554 $_[0] =~ m|(\d+)/(\d+)/(\d+)|;
555 ($1, ($2 || 0) - 1, $3);
560 sub processLinuxFr () {
561 my $linkToDate = sub ($) {
562 $_[0]->{'url'} =~ m|/(\d\d\d\d/\d\d/\d\d)/|; $1;
564 my $hackForUrl = sub ($) {
565 # hack for netscape -remote openURL
566 my $u = $_[0]->{'url'};
567 $u =~ s|,|\%2c|g; $u;
570 last if /^%%/; # skip the text note
573 [ qw( headline url author_name author_email type _ignore_ ) ],
574 { 'h' => 'headline', 'u' => $hackForUrl, 'd' => $linkToDate },
576 $_[0] =~ m|(\d+)/(\d+)/(\d+)|;
577 ($1, ($2 || 0) - 1, $3);
581 # ---------------------------------------------------------------------------
585 $msg = "$0: $msg: [$!]\n";
588 # # be quiet in non interactive shells?
589 # if ($ENV{'SHLVL'} || 0) == 1 || defined($ENV{'PS1'})
598 $errorMenuContent =~ s/<msg>/$msg; check network connection/;
599 print $errorMenuContent;
603 # like strftime, but gets unix time, instead of sec/min/hour/day/mon/year.
604 sub formatTime ($$) {
605 my ($fmt, $time) = @_;
607 strftime($fmt, localtime($time));
610 # Substitutes all %N1*N2x in $name by properly stripped and justified $values.
611 # $name example: %[%d %b %y %H:%M], %*-7(some text), %-32*30h, %{url}.
612 # $values is a hash of named values to substitute.
613 sub expandAllWidthSpecifiers ($$) {
614 my ($name, $values) = @_;
615 $name =~ s/%(-?\d+)?(\*(-?)(\d+))?(\w|{\w+}|\(.*?\)|\[.*?\])/
616 my $tag = substr($5, 0, 1);
617 my $arg = length($5) == 1? $5: substr($5, 1, -1);
620 $tag eq '['? formatTime($arg, $values->{'_'}):
622 $value = "(%$5 is not defined)" unless defined $value;
623 $value = !$2 || $4 <= 3 || $4 > length($value)? $value: $3?
624 "..." . substr($value, -$4 + 3, $4 - 3):
625 substr($value, 0, $4 - 3) . "...";
626 $1? sprintf("%$1s", $value): $value;
631 sub getAllSiteNames () {
632 return sort map { $siteInfo->{$_}->{'name'} } keys %$siteInfo;
636 $site ||= $defaultSite;
637 #$name ||= "MenuHeadlines$siteInfo->{$site}->{'name'}";
639 $title ||= "$siteInfo->{$site}->{'name'} Headlines";
641 print "A perl script which builds headlines menu for fvwm.\n";
642 print "Supported sites: ", join(', ', getAllSiteNames()), "\n\n";
643 print "Usage: $0 [OPTIONS]\n";
645 print "\t--help show this help and exit\n";
646 print "\t--version show the version and exit\n";
647 print "\t--info=[NAME] information about a site\n";
648 print "\t--site=NAME headlines site, default is $site\n";
649 print "\t--name=NAME menu name, default is '$name'\n";
650 print "\t--title=NAME menu title, default is '$title'\n";
651 print "\t--item=FORMAT menu item format, default is '$itemF'\n";
652 print "\t--exec=FORMAT exec command, default is {$execF}\n";
653 print "\t--command=FORMAT fvwm command, default is no\n";
654 print "\t--icon-title=XPM menu title icon, default is no\n";
655 print "\t--icon-item=XPM menu item icon, default is no\n";
656 print "\t--icon-home=XPM menu home icon, default is no\n";
657 print "\t--wm-icons define icon names to use with wm-icons\n";
658 print "\t--frontpage[=V] show frontpage item; values: top, bottom\n";
659 print "\t--proxy=host[:port] specify proxy host and port (80)\n";
660 print "\t--file[=FILE] menu file, default is $workHome/$site.menu\n";
661 print "\t--fake[=FILE] don't connect, read input from file\n";
662 print "Short options are ok if not ambiguous: -h, -t.\n";
672 print STDERR "Try '$0 --help' for more information.\n";
678 # ---------------------------------------------------------------------------
682 fvwm-menu-headlines - builds headlines menu definition for FVWM
686 B<fvwm-menu-headlines>
688 [ B<--version>|B<-v> ]
690 [ B<--site>|B<-s> site ]
691 [ B<--name>|B<-n> name ]
692 [ B<--title>|B<-t> title ]
694 [ B<--exec>|B<-e> exec-command ]
695 [ B<--command>|B<-e> fvwm-command ]
696 [ B<--icon-title> icon ]
697 [ B<--icon-item> icon ]
698 [ B<--icon-home> icon ]
700 [ B<--frontpage> [where] ]
701 [ B<--proxy>|B<-p> host:port ]
707 This configurable perl script builds an fvwm menu definition for headlines
708 of popular news web sites: FreshMeat, Slashdot, LinuxToday, Segfault, AppWatch,
709 LinuxApps, JustLinux, DaemonNews, GNOME-News, KDE-News, FreeKDE, RootPrompt,
710 LinuxFr, ThinkGeek and more.
712 It is possible to specify a customized menu item format, change a command
713 (usually launching a browser) and add menu icons (there is a support for
714 the wm-icons package).
718 B<--help> - show the help and exit
720 B<--version> - show the version and exit
722 B<--info> [site] - if site name is given print the site specific info,
723 otherwise print all site names
725 B<--site> site - defile a web site, headlines of which to show, this option
726 also can be used together with --help to get new defaults.
727 Default site: freshmeat.
729 B<--name>, B<--title>, B<--icon> - define menu name, menu title and menu icon
730 accordingly given in the following argument. Default is name
731 "MenuHeadlinesFreshmeat", title "Freshmeat Headlines" and no mini-icon
732 (equivalent to an empty icon argument).
734 B<--item>, B<--exec> - define menu item or exec format in the following
735 argument (what is shown and what is executed when the item is chosen),
736 default is '%h\t(%[%Y-%m-%d %H:%M])'.
737 TAB can be specified as '\t', but in .fvwm2rc you should specify a double
738 backslash or a real TAB.
740 Format specifiers for a headline entry:
743 %d - date in native format
744 %[strftime-argument-string] - date, see strftime(3)
745 the date is represented accourding to the local time
746 %{name} - site-specific-named-value
747 %(text) - arbitrary text
749 These specifiers can receive an optional integer size, positive for right
750 adjusted string or negative for left adjusted, example: %8x; and optional
751 *num or *-num, which means to leave only the first or last (if minus) num of
752 chars, the num must be greater than 3, since the striped part is replaced
753 with "...", example: %*30x. Both can be combined: %-10*-20x, this instructs to
754 get only the 20 last characters, but if the length is less then 10 - to fill
755 with up to 10 spaces on the right.
757 B<--command> like B<--exec> above, but enables to specify any fvwm command,
758 for example, "Function FuncFvwmShowURL '%u'" not only Exec.
760 In fact, --exec="mozilla '%u'" is equivalent
761 to --command="Exec mozilla '%u'"
763 B<--icon-title>, B<--icon-item>, B<--icon-home> - define menu icon for
764 title, regular item and home item respectively given in the following argument.
765 Default is no menu icons (equivalent to an empty icon argument).
767 B<--wm-icons> - define icon names suitable for use with wm-icons package.
768 Currently this is equivalent to: --icon-title '' --icon-item
769 menu/information.xpm --icon-home menu/home.xpm.
771 B<--frontpage> [where] - show site fronpage item in the menu too. Optional
772 value can be used to specify where this item will be placed in the menu -
773 'top' or 't', 'bottom' or 'b'.
775 B<--proxy> host[:port] - define a proxy to use.
776 Example: --proxy proxy.inter.net:3128
778 B<--file> [file] - write the menu output to specified file. If no filename is
779 given with this option (or empty filename), the default filename
780 WORK_HOME/SITE.menu is used. Without this option or with '-'
781 filename, the menu output is written to standard output.
783 B<--fake> [file] - don't connect to the host using HTTP protocol, instead,
784 read from WORK_HOME/SITE.in file. The following reads input from
785 segfault.in (downloaded http://segfault.org/stories.txt) and saves output
786 to segfault.menu (both files are in WORK_HOME):
787 fvwm-menu-headlines --site segfault --fake --file
789 WORK_HOME of this script is ~/.fvwm/.fvwm-menu-headlines.
790 It is created if needed.
792 Option parameters can be specified both using '=' and in the next argument.
793 Short options are ok if not ambiguous: C<-h>, C<-t>; but be careful with
794 short options, what is now unambiguous, can become ambiguous in the next
799 1. One of the ways to use this script is to define a crontab
800 entry to run the script every hour or so for every monitored site:
802 0,30 * * * * fvwm-menu-headlines --file --site freshmeat
803 1,31 * * * * fvwm-menu-headlines --file --site linuxtoday
804 2,32 * * * * fvwm-menu-headlines --file --site slashdot
806 Then add these lines to your fvwm configuration file:
808 DestroyFunc FuncFvwmMenuHeadlines
809 AddToFunc FuncFvwmMenuHeadlines
810 + I Read "$HOME/.fvwm/.fvwm-menu-headlines/$0.menu"
812 DestroyMenu MenuHeadlines
813 AddToMenu MenuHeadlines "Headlines" Title
814 + MissingSubmenuFunction FuncFvwmMenuHeadlines
815 + "FreshMeat" Popup freshmeat
816 + "LinuxToday" Popup linuxtoday
817 + "Slashdot" Popup slashdot
819 2. Another way to use this script (only if you have fast network/proxy) is to
820 run it every time you want to open your Headlines submenus.
821 (Note, the submenu that is once created is not reloaded, use "Reset all".)
823 In this case your fvwm configuration lines could be:
825 DestroyFunc FuncFvwmMenuHeadlines
826 AddToFunc FuncFvwmMenuHeadlines
827 + I PipeRead "fvwm-menu-headlines --site $0"
829 DestroyMenu MenuHeadlines
830 AddToMenu MenuHeadlines "Headlines" Title
831 + MissingSubmenuFunction FuncFvwmMenuHeadlines
832 + "FreshMeat" Popup freshmeat
833 + "Slashdot" Popup slashdot
834 + "LinuxToday" Popup linuxtoday
835 + "Segfault" Popup segfault
836 + "AppWatch" Popup appwatch
837 + "GNOME News" Popup gnome-news
838 + "KDE News" Popup kde-news
840 + "Reset all" FuncResetHeadlines
842 DestroyFunc FuncResetHeadlines
843 AddToFunc FuncResetHeadlines
844 + I DestroyMenu freshmeat
845 + I DestroyMenu linuxtoday
846 + I DestroyMenu slashdot
847 + I DestroyMenu segfault
848 + I DestroyMenu appwatch
849 + I DestroyMenu gnome-news
850 + I DestroyMenu kde-news
852 And finally, add "Popup MenuHeadlines" somewhere.
854 3. Here is a usual usage. Use FvwmConsole or FvwmCommand to run fvwm commands
855 from a shell script. Every time you want headlines from some site, execute
856 (give any additional options if you want):
858 PipeRead "fvwm-menu-headlines --site segfault --name MenuHeadlinesSegfault"
859 # this will take several seconds, you may use: BusyCursor Read true
860 Popup MenuHeadlinesSegfault
862 =head1 HOW TO ADD SITE HEADLINES
864 It is possible to add user defined site headlines without touching the script
865 itself. Put your perl extensions to the file WORK_HOME/extension.pl.
866 For each site add something similar to:
868 $siteInfo->{'myslashdot'} = {
869 'name' => "MySlashdot",
870 'host' => "myslashdot.org",
871 'path' => "/myslashdot.xml",
872 'func' => \&processMySlashdot,
873 # the following string is only used in --info
874 'flds' => 'time, title, department, topic, author, url',
877 sub processMySlashdot () {
880 # mandatory 'h', 'u' and 'd' aliases or undef
881 { 'h' => 'title', 'u' => 'url', 'd' => 'time' },
882 sub ($) { # convert 'd' string to (y, m, d, H, M, S)
883 $_[0] =~ /(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)/;
884 ($1, ($2 || 0) - 1, $3, $4, $5, $6);
885 }, +0, # timezone offset; already in UTC
893 This script is inspired by WMHeadlines v1.3 by:
895 Jeff Meininger <jeffm@boxybutgood.com>
896 (http://rive.boxybutgood.com/WMHeadlines/).
898 Reimplemented for FVWM and heavily enhanced by:
900 Mikhael Goikhman <migo@homemail.com>, 16 Dec 1999.
904 The script is distributed by the same terms as fvwm itself.
905 See GNU General Public License for details.
909 Report bugs to fvwm-bug@fvwm.org.
913 # ===========================================================================