Beefy Boxes and Bandwidth Generously Provided by pair Networks Frank
Don't ask to ask, just ask
 
PerlMonks  

RedWolf MonkChatter

by strredwolf (Chaplain)
on Feb 28, 2001 at 03:28 UTC ( #61227=sourcecode: print w/ replies, xml ) Need Help??

Category: Chatterbox Clients
Author/Contact Info strredwolf
Description: A chatterbox client in Perl/TK. Doesn't need XML or the PerlMonks module. Fairly complete now.

This has a newer XML pharser (it's not XML::Simple). It also works in Windows. (Hey! ActiveState doesn't have Tk pre-bundled!) Also, Level and XP stats, Jumpscroll control, and clearing of the private messages once received (which will automagically sweep previous items you've seen as a side benifit). Also a raw client view and processing of [jumptags] and some HTML(CODE and A tags).

To use: Run as is, and log in via the top line like the Java Chatterbox client (which it's style borrows from). Once logged in, hit the "Return" key to send in a line of chatter on the bottom entryline. There's several options on the ">>" menu worth perusing; "Regenerate" will redo the chatter log.

#!/usr/bin/perl

### Modules
use Tk;
use IO::Socket;

### Win32 junk
BEGIN {
  my($win32)=1 if($^O eq 'MSWin32');
  
  if($win32) {
    eval 'use Win32::Shell';
    die "$@\n" if($@);
  }
}

### Configurables
$maxlines=500;
$blanknode=14532;
$delaysec=10;
# $proxy='';

### Code starts here
# HTTP request (w/o LWP's complexity)
sub httpreq {
  my($method,$url,$headers,$content)=@_;
  my($site,$dir,$port,$remote,$req,$result,$code,@top,$bot,$sock);
  my($e)="\r\n";
  
#  if(defined $proxy) {
#    print "Through proxy....";
#    $proxy=~ m#^http://([^/]+)(/.*)?$#i;
#  } else {
    $url=~ m#^http://([^/]+)(/.*)?$#i;
#  }
  $site=$1;$dir=$2;
  $dir="/" unless($dir);
  
  if($site =~ /^([^:]+):(\d+)$/)
    {    
      $remote=$1; $port=$2;
    } else {
      $remote=$site; $port=80;
    }
  
#  if(defined $proxy) {
#    $req="\U$method\E $url HTTP\\1.0$e";
#  } else {    
    $req="\U$method\E $dir HTTP\\1.0$e";
#  }

  foreach $j (@$headers)
    {
      $req .= "$j$e";
    }
  
  if($method =~ /POST/i)
    {
      my($l)=length $content;
      $req .= "Content-Length: $l$e$e";
      $req .= $content;
    } else {
      $req .= $e;
    }
  
  $sock=IO::Socket::INET->new(
                  Proto => "tcp",
                  PeerAddr => $remote,
                  PeerPort => $port)
    or return ''; #Nothing
  $sock->autoflush(1);
  
  print $sock $req;
  $code=<$sock>;
  my($i)=0;
  while(<$sock>)
    {
      tr/\r\n//d;
      last if(/^$/);
      $top[$i++]=$_;
      $main->update;
    }
  $bot='';
  while(<$sock>) { $bot .= $_; $main->update; }
  
  ($code,$bot,@top);
}

### XML minipharser
# Splits a message up along XML tags.  Returns the message in an array
+.
sub splitxml {

  my($d,$fl,@j)=@_;
  my(@tags,$i,$last);
  
  while($d)
    {
      "" =~ /(.?)/;
      if($d =~ s/^(<[^>]+>)//s) {
    $tags[$i++]=$1;
      } elsif( $fl && $d =~ s/^(\[[^\]]+\])//s ) {
    $tags[$i++]=$1;
      }    else {
           $d=~s/^([^<]+)//s unless($fl);
    $d=~s/^([^<\[]+)//s if($fl);
    if(length $1) {
      $tags[$i++]=$1;
    } else {
      $d.=">" if($d=~/^\</);
      $d.="]" if($d=~/^\[/);
    }
    
      }
    }
  (@tags);
}

# Takes a tag and splits it up into options.
# Returns a tag name and an hash.
sub splittag {
  my($tag)=@_;
  my($i,$j,$k,%opts,@t,$ti);
  
  $tag=~ s/^<//; $tag=~ s/>$//;
  
  $tag=~ s/^(\S+)\s+//; $ti=$1;
  
  while($tag) {
    $tag =~ s/^([^=]+)(=[\"\']([^\"\']+)[\"\']\s?)?//;
    $opts{$1}=($2 ? $3 : 1);
  }
  ($ti, %opts);
}

# Pharses a line and translantes all those &amp; thingies...
sub xlateamp {
  my($l,@j)=@_;
  
  $l=~ s/&lt;/</g;
  $l=~ s/&gt;/>/g;
  $l=~ s/&amp;/&/g;
  $l=~ s/&#(\d+);/chr($1)/eg;
  
  return($l);
}

###
# URL launcher
sub do_url {
  my($tag)=@_;
  my($i,$h);
  
  if($tag =~ /^url(\d+)/) {
    $i=$1;
    $h=$url[$i];
    
    $h=~ s#^id:\/\/(\d+)$#http:\/\/perlmonks.org\/index.pl?node_id=$1#
+;
    if($h !~ /^http:\/\// ) {
      $h =~ s/([^\w ])/sprintf("%%%02X",ord($1))/eg;
      $h =~ s/ /+/g;
      $h="http://perlmonks.org/index.pl?node=$h";
    }
    
    if($win32) {
      Win32::Shell::Execute("open", $h, undef,undef, "SW_SHOWNORMAL");
    } else {
      system("netscape -remote 'openURL($h)'");
    }
  }
}

# statnode
sub statnode {
  my($node)=@_;
  my($i,$j,$k,$l,$fl);
  my($cd,$doc,@head)=&httpreq("GET",
                  "http://perlmonks.org/index.pl?node=node+query+xml+g
+enerator&nodes=$node",
                  ["Cookie: $cookie"], "");
  
  my(@lines)=splitxml($doc);
  $fl=0;
  for($i=0;$i<@lines;$i++) {
    $l=$lines[$i];
    $l=~ tr/\n\r//d;
    $l=~ s/^\s+//;
    $fl++ if($l=~/^<node/i);
    $fl=0 if($l=~/^<\/node/i);
    $k=$l if($fl);
  }
  
  $k="id://$node" unless($k);
  return($k);
}

# LinePharzer
sub do_add {
  my($msg)=@_;
  
  if($raw)
    {
      $main_index_list->insert("end","\n$msg");
    } else {
      my($i,$j,$k,$l,%tags,$op);
      my(@lines)=splitxml($msg,1);
      
      $main_index_list->insert("end","\n");
      $op='';
      for($i=0;$i<scalar @lines;$i++)
    {
      $l=$lines[$i];
      
      $tags{'code'}++, next if($l=~/^<\s*code/i && !$tags{'code'});
      $tags{'code'}=$op='', next if($l=~/^<\/code/i);
      
      unless($tags{'code'})
        {
          if($l=~/^\[([^\]]+)\]/) {
        $j=$1; $k=$1;
        
        if($j=~/^id:\/\/(\d+)$/) {
          $k=statnode($1);
        }
        
        $j=$1, $k=$2 if($j=~/^([^\|]+)\|(.+)$/);
        
        $url[$uc]=$j; $l=$k; $op="url$uc"; $uc++;
        
        $main_index_list->insert("end",$l,$op);
        $main_index_list->tagConfigure($op,-foreground=>"blue",
                           -data=>$j,
                           -underline=>1);
        # merlyn code...
        $main_index_list->tagBind($op, "<1>", do { my $thing = $op; su
+b { do_url($thing) } } );
        $op='';
        next;
          } 
          if($l=~/^<a/i) {
        my($t,%opt)=splittag($l);            
        $url[$uc]=$opt{'href'}; $tags{'a'}=$op="url$uc"; $uc++;
        $main_index_list->insert("end",'',$op);
        $main_index_list->tagConfigure($op,-foreground=>"blue",
                           -data=>$j,
                           -underline=>1);
        # merlyn code...
        $main_index_list->tagBind($op, "<1>", do { my $thing = $op; su
+b { do_url($thing) } } );
        next;
          }
          if($l=~/^<\/a/i) {
        $tags{'a'}=$op='';
        next;
          }
          $l=xlateamp($l);
        }
      
      $main_index_list->insert("end",$l,$op) if($op);
      $main_index_list->insert("end",$l) unless($op);
    }
    }
}

# Do an update of the tags.
sub do_update {
  return if($uplock);
  $uplock=1;
  $main_bot_stat->configure(-text=>"Updating...");
  my($c,$d,@h)=&httpreq("GET",
            "http://perlmonks.org/index.pl?node=chatterbox+xml+ticker"
+,
            "","");
  my($i,$l,$t,$au,$bt,$m,$fl,$uid,$ti,%opts);

  if($c =~ /200 OK/ )
    {
      my(@lines)=splitxml($d);
      $fl=0;
      for($i=0;$i<@lines;$i++)
    {
      $l=$lines[$i];
      $l=~ tr/\n\r//d;
      $l=~ s/^\s+//;
      if($l=~ /^<message/)
        {
          $au=$t=$uid='';
          ($ti,%opts)=splittag($l);
          $au=$opts{'author'};
          $t=$opts{'time'};
          $uid=$opts{'user_id'};
          $fl=1; $t=~ s/^....//;
          $t=~ /^....(..)(..)/;$t+=0;
          $m="$1:$2: [id://$uid|$au]";
          next;
        } 
        
      if( $l =~ /^<\/message>/ )
        {
          $t+=.01 while($msg{$t} && $msg{$t} ne $m);
          $msg{$t}=$m;
          $bt=$t; $fl=0;
          next;
        }
      
      if( $fl==1 )
        {
          $l=xlateamp($l);
          if( $l=~ /^\/me(.+)$/) { $m.=$1; } else { $m.=": $l"; }
          $fl++;
        } elsif ($fl>1) {
          $m .= xlateamp($l);
        }
    }
      
      if($prv) {
    if($justin) {
      $justin=0;
      $j=(int $lasttime)+.01;
      foreach $z (sort keys %pmsg)
        {
          $msg{$j}=$pmsg{$z};
          $bt=$j; $j+=.01;
        }
    }
    foreach $j (sort keys %pmsg) {
      $i=$j; $i+=.01 while($msg{$i} && $msg{$i} ne $pmsg{$j});
      $msg{$i}=$pmsg{$j};
      $bt=$i if($bt < $i);
    }
    
    $prv=0; %pmsg=();
      }
      
      $bt=$bt+0;
      if($bt > $lasttime)
    {
      my($f)=0;
      my(@ti)=sort keys %msg;
      $lasttime=$ti[0]-1 unless($lasttime);
      foreach $i (@ti)
        {
          
          $f++ if($i > $lasttime);
          do_add($msg{$i}) if($f);
        }
      $lasttime=$ti[-1]+0;
      $main_index_list->see("end") if($f && $jump);
    }
    }
  $main_bot_stat->configure(-text=>" ");
  $uplock=0;
}

# We need a cookie for this routine.  It's the XP and Private stuff.
sub do_userprv {
  return 1 unless($in);
  
  # Get the privates...
  my($c,$ml,@h)=&httpreq("GET","http://perlmonks.org/index.pl?node=pri
+vate+message+xml+ticker",
             ["Cookie: $cookie"],"");
  my(@lines)=splitxml($ml);
  my($i,$fl,$l,$au,$t,$m,@mid,$mi,$z,$ft,$uid);
  
  for($i=0;$i<@lines;$i++)
    {
      $l=$lines[$i];
      $l=~ tr/\n\r//d;
      $l=~ s/^\s+//;
      if($l=~ /^<message/)
    {
      $au=$t=$uid='';
      ($ti,%opts)=splittag($l);
      $au=$opts{'author'};
      $t=$opts{'time'};
      $uid=$opts{'user_id'};
      $z=$opts{'message_id'};
      $mid[$mi++]="deletemsg_$z=yup";
      $fl=1; $t=~ s/^....//;
      $t=~ /^....(..)(..)/;$t+=0;
      $m="$1:$2: [id://$uid|$au]";
      next;
    } 
      if($l=~ /^<message message_id=(.+) author=(.+) time=(.+)>/)
    {
      $z=$1;
      $au=$2; $t=$3;
      $au=~ tr/\"\'//d; $t=~ tr/\"\'//d; 
      $z=~ tr/\"\'//d; $t=~ s/^....//;
      $mid[$mi++]="deletemsg_$z=yup";
      $fl=1;     
      $t=~ /^....(..)(..)/; $t+=0;
      $m="$1:$2: $au";
      next;
    } 
    
      if( $l =~ /^<\/message>/ )
    {
      $pmsg{$t}=$m; $prv++;
      $bt=$t; $fl=0; $ft=$t unless($ft);
      next;
    }
      
      if( $fl==1 )
    {
      $l=~ s/&lt;/</g;
      $l=~ s/&gt;/>/g;
      $l=~ s/&amp;/&/g;
      if( $l=~ /^\/me(.+)$/) { 
        $m.=" (privately) ";
      } else { 
        $m.="> "; 
      }
      $m .= $l;
      $fl++;
    } elsif ($fl>1) {
      $l=~ s/&lt;/</g;
      $l=~ s/&gt;/>/g;
      $l=~ s/&amp;/&/g;
      $m .= $l;
    }
    }

    # Now clear 'em...
  my($req)=join "&",@mid;
  $req.="&message=&message_send=talk&op=message";
  ($c,$ml,@h)=&httpreq("POST","http://perlmonks.org/index.pl",
               ["Cookie: $cookie"], $req)
    if($oktoclear && $mi);
  
  # Get XP.
  
  ($c,$ml,@h)=&httpreq("GET","http://perlmonks.org/index.pl?node=XP+xm
+l+ticker",["Cookie: $cookie"], "");
  @lines=splitxml($ml);
  for($i=0;$i<@lines;$i++)
    {
      $l=$lines[$i];
      $l=~ tr/\n\r//d;
      $l=~ s/^\s+//;
      if($l =~ /^<XP level=(.+) xp=(.+) xp2nextlevel=(.+) votesleft=(.
++)>/)
    {
      my($lv,$xp)=($1, $2);
      $lv=~ tr/\'\"//d; $xp=~ tr/\'\"//d;
      $main_bot_xp->configure(-text=>"Lev: $lv XP: $xp");
    }
    }
  
}

# Do Login/out
sub do_loginout
{
  my($i,$a,$c,@b);
  unless($in)
    {
      $user=$main_btns_user->get;
      $pass=$main_btns_pass->get;
      
      # Log us in...
      ($a,$c,@b)=&httpreq("POST","http://perlmonks.org/index.pl","",
              "op=login&user=$user&passwd=$pass&expires=%2b10y&login=L
+ogin&node_id=$blanknode");
      if($a =~ /200 OK/)
    {
      foreach $i (@b)
        {
          $cookie=$1 if($i =~/Set\-Cookie: (userpass=[^;]+);/ );
        }
      
      if($cookie)
        {
          $main_bot_stat->configure(-text=>"Logged in.");
          $in++; $justin++;
          $main_btns_open->configure(-text=>"Logout");
          $main_btns_user->configure(-state=>"disabled");
          $main_btns_pass->configure(-state=>"disabled");
          $main_art_text->configure(-state=>"normal");
          do_userprv;
        }
    }
    } else {
      ($a,$c,@b)= &httpreq("GET",
               "http://perlmonks.org/index.pl?op=logout&node_id=$blank
+node",
               ["Cookie: $cookie"], "");
      if($a =~ /200 OK/) {
    $cookie=''; $in=0;
    $main_bot_stat->configure(-text=>"Logged out.");
    $main_btns_open->configure(-text=>"Login");
    $main_btns_user->configure(-state=>"normal");
    $main_btns_pass->configure(-state=>"normal");
    $main_art_text->configure(-state=>"disabled");
      }
    }
}

# Do some smackdowns.
sub do_chatter {
  my($a,$b,@c,$z);
  my($line)=$main_art_text->get;
  $main_art_text->delete(0,end);
  
  $line =~ s/([^\w ])/sprintf("%%%02X",ord($1))/eg;
  $line =~ s/ /+/g;
  
  ($a,$c,@b)=&httpreq("POST","http://perlmonks.org/index.pl",
              ["Cookie: $cookie"], 
              "op=message&message=$line&node_id=$blanknode");

  do_update;
}

# Menu handler
sub do_menu {
  $menued=0 if($main_opts->state ne "normal");
  
  unless($menued) {
    $main_opts->post($main_btns_opts->rootx,
             $main_btns_opts->rooty+$main_btns_opts->height);
  } else {
    $main_opts->unpost;
  }
  $menued=1-$menued;
}

sub do_regen {
  my($i);
  $main_index_list->delete("1.0", "end");
  
  my(@ti)=sort keys %msg;
  foreach $i (@ti)
    {
      do_add($msg{$i});
    }
  $lasttime=$ti[-1]+0;
  $main_index_list->see("end");
}

$user=$pass=$line=$cookie=$lasttime='';
$in=$prv=$oktoclear=$justin=$menued=$uc=$uplock=0;
$jump=$raw=1;
@url=();

$main=MainWindow->new(-height=>320, -width=>240);

$main->geometry("240x320");

$main_btns = $main->Frame;
$main_btns_user = $main_btns->Entry(-width=> 10);
$main_btns_user->insert(0,"(username)");
$main_btns_pass = $main_btns->Entry(-show=> "*", -width=> 10);
$main_btns_open = $main_btns->Button(-text=> "Login",-padx=>1,-pady=>1
+, 
                     -command=>sub{do_loginout});
$main_btns_opts=$main_btns->Button(-text=>">>",-padx=>1,-pady=>1,
                   -command=>sub{do_menu });
$main_btns_opts->pack(-side=>"right");
$main_btns_open->pack(-side=>"right");
$main_btns_user->pack(-anchor=>"w", -side=>"left", -fill=>"x", -expand
+=>1);
$main_btns_pass->pack(-anchor=>"w", -side=>"left", -fill=>"x", -expand
+=>1);
$main_btns->pack(-anchor=>"w",-side=>"top",-fill=>"x",-expand=>1);

$main_opts=$main->Menu(-tearoff=>1,-title=>"Options");
$main_opts->add("command", -label=>"Regenerate", -command=>sub{do_rege
+n});
$main_opts->add("separator");
$main_opts->add("checkbutton", -variable=>\$jump, -label=>"Jump on scr
+oll");
$main_opts->add("checkbutton", -variable=>\$oktoclear,
        -label=>"Autoclear private messages");
$main_opts->add("checkbutton", -variable=>\$raw,
        -label=>"Don't do fancy formatting");

#$main_opts=$main->Frame;
#$main_opts_jump=$main_opts->Checkbutton(-variable=>\$jump, -text=>"Ju
+mpscroll");
#$main_opts_jump->pack(-anchor=>"w", -side=>"left");
#$main_opts_clear=$main_opts->Checkbutton(-variable=>\$oktoclear, -tex
+t=>"AutoClear PrvMsg");
#$main_opts_clear->pack(-anchor=>"w",-side=>"left");
#$main_opts->pack(-anchor=>"e",-side=>"top",-fill=>"x",-expand=>1);

$main_bot=$main->Frame;
$main_bot_stat=$main_bot->Label(-text=>"RedWolf MonkChatter");
$main_bot_stat->pack(-anchor=>"w",-side=>"left");
$main_bot_xp=$main_bot->Label(-text=>"Lev: 0 XP: 0");
$main_bot_xp->pack(-anchor=>"e",-side=>"right");
$main_bot->pack(-anchor=>"s",-side=>"bottom",-fill=>"x",-expand=>1);

$main_index = $main->Frame;
$main_index_list = $main_index->Text(-wrap=>"word");
$main_index_list->configure(-font=>"fixed") unless($win32);
$main_index_scroll = $main_index->Scrollbar(-width=> 10,
                        -command=> ["yview", $main_index_list]);
$main_index_list->configure(-yscrollcommand=>['set', $main_index_scrol
+l]);
$main_index_scroll->pack(-side=>"right",-fill=>"y");
$main_index_list->pack(-fill=>"both", -expand=>1);

$main_art = $main->Frame;
$main_art_text = $main_art->Entry(-state=>"disable");
$main_art_text->bind("<Return>",sub{do_chatter;});
$main_art_text->pack(-fill=>'x', -expand=>1);
$main_art->pack(-fill=>"x", -expand=>1, -anchor=>"w", -side=>"bottom")
+;

$main_index->pack(-fill=>"both", -anchor=>"w",-side=>"bottom");

do_update;
$main->repeat($delaysec*1000,sub{do_update});
$main->repeat(60*1000,sub{do_userprv});

MainLoop;


Comment on RedWolf MonkChatter
Download Code
Re: RedWolf MonkChatter
by OeufMayo (Curate) on Feb 28, 2001 at 04:30 UTC

    Maybe I shouldn't have ++ it this node, but I must admit that it's quite fast and simple...

    Obviously the speed of the script is mainly due to the fact that strredwolf has done his own XML (pseudo)parser. I talked with him on the CB and he didn't want to have the overhead of loading the regular parsing module. I agree that the XML output for the chatterbox is really simple, but I would not advise anyone to do so, especially in the Real World ™, just to gain on the overhead of the XML parsing with tools like XML::Parser, XML::Twig, XML::Simple or XML::PYX.

    Anyway, nice client strredwolf!

    Update: A XML::Parser Tutorial is now available, taking the CB ticker as a parsing example.

    <kbd>--
    my $OeufMayo = new PerlMonger::Paris({http => 'paris.mongueurs.net'});</kbd>
      Thanks. Yeah, it's rather simple code tuned to PM's chatterbox XML. I've installed XML, and it's no eazy feat (aka can't be done via CPAN::shell). So I've coded my own.

      There is other problems, though. It's tuned to PM's XML. I'm looking into a new engine... but it's a nice clone of the java client.

      --
      $Stalag99{"URL"}="http://stalag99.keenspace.com";

Re: RedWolf MonkChatter
by dws (Chancellor) on Feb 28, 2001 at 04:47 UTC
    Taking the extra steps towards use strict; are worthwhile. You're pretty close.

Re: RedWolf MonkChatter
by ZZamboni (Curate) on Feb 28, 2001 at 06:13 UTC
    I assume you already know about my PerlMonks modules, so I will skip the obvious plug (which I just did, anyway :-)

    What actually surprised me is that I was convinced that I had used XML::Simple for parsing the chatbox XML (I wrote it a long time ago), but I just looked and I also did manual parsing. The chatbox XML is so simple that I didn't feel the need for it. I may have to update that some time. I did use XML::Simple for the NewestNodes module, though, since the output is much more complex. And XML::Simple is very easy to use. Interestingly, I don't remember the XML modules being problematic to install.

    Another thing that surprised me is how similar the code is. Comparing your do_update() with my PerlMonks::Chat::getallines() shows a very similar logical structure, even though the code itself is different. Amazing!

    --ZZamboni

      I did it through CPAN, which became a mess. You had to install Bundle::XML, which included XML::Simple et all...

      OUI! But I'm going to change that. There's another engine which I've put in. Should be going in soon.

      --
      $Stalag99{"URL"}="http://stalag99.keenspace.com";

Re: RedWolf MonkChatter
by mirod (Canon) on Feb 28, 2001 at 14:26 UTC

    You should really use an XML parser (what else would you expect from me ;--). On top of the reasons given in On XML Parsing, if the format of the CB changes even slightly you will have to update your code (think about an extra attribute for each message). If you used an XML module you would have a much better chance to survive those modifications. Plus learning how to use one of the XML modules is a Good Thing (tm).

    2 more points: I don't find XML::Simple difficult to install. I would not use the xml bundle. Either get XML::Parser 2.27 which comes with expat or get XML::Parser 2.30 and expat from http://sourceforge.net/projects/expat/ and compile it, install XML::Parser get XML::Simple, install it et voila!

    Then I am not sure I would want to use XML::Simple for the CB. The problem is that it will create an unordered list of messages, that you then have to order on the time field. I would probably use XML::PYX to get the messages in the right order. OK, I'm lying I would use XML::Twig, but maybe starting with PYX would be easier.

    Update: epoptai below is right, XML::Simple actually stores the messages in the proper order. I would have sworn I saw them messed up the other day. I would still try another method, if only to get a break from the ubiquitous XML::Simple ;--)

    Here is how XML::PYX outputs each message in the CB:

    (message Aauthor strredwolf Atime 20010228082806 -\n -OK, I'm convinced, I will use XML::PYX )message

    Easy to process isn't it?

      Does XML::PYX and XML::Twig need XML::Parser(and thus expat)? I'm trying to get away from having folks need to compile that (which kinda breaks compiling though CPAN::shell).

      But then, XML::PYX seems to use a similar engine that I'm using now.

      --
      $Stalag99{"URL"}="http://stalag99.keenspace.com";

        All of the XML processing modules on CPAN are based on expat, including XML::Parser, XML::PYX, XML::Simple, XML::Twig and many more.

        I fail to see why it is such a problem. Yes it means you cannot install directly using CPAN. But it installs very easily under Unix and once again XML::Parser comes with Activestate's Windows port. I know CPAN is extremelly convenient but it is not the only valid method to install modules.

        If you want to do any serious XML processing you really need a proper parser. Using regexps is false laziness. It is akin to not using strict: good for a quick hack but a no-no for anything serious.

        Incidentally there are good reasons why expat is not bundled with XML::Parser anymore. It is used by other applications, Apache for example and bundling it lead to incompatibilities between the different versions and problems building them.

      I am not sure I would want to use XML::Simple for the CB. The problem is that it will create an unordered list of messages, that you then have to order on the time field.

      No such problem mirod. I'm using XML::Simple (v2.27) to parse the CB and can assure you that the messages come out of the parser in the correct order.

Back to Code Catacombs

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: sourcecode [id://61227]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (4)
As of 2014-04-19 21:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (483 votes), past polls