emacs-epc-0.1.1/0000755000175000017500000000000012177363221013204 5ustar dogslegdogslegemacs-epc-0.1.1/demo/0000755000175000017500000000000012177363221014130 5ustar dogslegdogslegemacs-epc-0.1.1/demo/dbi-server.pl0000644000175000017500000000117312177363221016531 0ustar dogslegdogsleg#!/usr/bin/perl use RPC::EPC::Service; use Data::Dumper; use DBI; sub db_connect { my $dbh; my $methods = { 'connect' => sub { my ($args) = @_; my $dbname = $args; $dbh = DBI->connect("dbi:SQLite:dbname=$dbname","",""); return $dbh->get_info(18); }, 'query' => sub { my ($args) = @_; my $sql = $args; my $sth = $dbh->prepare($sql); $sth->execute(); my $rs = $sth->fetchall_arrayref; unshift @$rs, $sth->{NAME}; return $rs; }, }; my $server = RPC::EPC::Service->new(8888, $methods); $server->start; } db_connect; emacs-epc-0.1.1/demo/echo-client.pl0000644000175000017500000000027112177363221016657 0ustar dogslegdogsleguse RPC::EPC::Service; $client = RPC::EPC::Service->new(8888,{}); $client->client_start; $ret = $client->call_method('echo', 'hello epc!'); print $ret->recv . "\n"; $client->stop; emacs-epc-0.1.1/demo/dbi-client.el0000644000175000017500000000165612177363221016474 0ustar dogslegdogsleg(require 'epc) (defun edbc-query () (interactive) (lexical-let* ( (dbname (read-string "db file: ")) (sql (read-string "SQL : ")) (epc (epc:start-epc "perl" '("dbi-server.pl")))) (message "DB %s / %s [%S]" dbname sql epc) (deferred:$ (epc:call-deferred epc 'connect dbname) (deferred:nextc it (lambda (x) (message "Return : %S" x) (epc:call-deferred epc 'query sql))) (deferred:nextc it (lambda (records) (let ((buf (get-buffer-create "*EDBC Result*"))) (with-current-buffer buf (erase-buffer) (loop for line in records do (loop for col in line do (insert (format "%s | " col))) (insert "\n"))) (pop-to-buffer buf)) (epc:stop-epc epc))) (deferred:watch it (lambda (x) (epc:stop-epc epc)))))) emacs-epc-0.1.1/demo/echo-client.el0000644000175000017500000000106512177363221016646 0ustar dogslegdogsleg(require 'epc) (setq epc (epc:start-epc "perl" '("echo-server.pl"))) (setq epc2 (epc:start-epc "perl" '("echo-server.pl"))) (deferred:$ (epc:call-deferred epc 'echo 10) (deferred:nextc it (lambda (x) (message "Return : %S" x)))) (deferred:$ (epc:call-deferred epc 'add '(10 40)) (deferred:nextc it (lambda (x) (message "Return : %S" x)))) (message "%S" (epc:call-sync epc 'echo '(10 40))) (message "%S" (epc:call-sync epc2 'add '(10 40))) (message "%S" (epc:sync epc (epc:query-methods-deferred epc))) (epc:stop-epc epc) (epc:stop-epc epc2) emacs-epc-0.1.1/demo/echo-server.pl0000644000175000017500000000066312177363221016714 0ustar dogslegdogsleg#!/usr/bin/perl use RPC::EPC::Service; sub echo_test { my $methods = { 'echo' => [sub { my $args = shift; return $args; },"args","just echo back arguments."], 'add' => sub { my $args_ref = shift; my ($a,$b) = @$args_ref; return $a + $b; } }; my $server = RPC::EPC::Service->new(0, $methods); $server->start; } echo_test(); emacs-epc-0.1.1/epcs.el0000644000175000017500000001317612177363221014470 0ustar dogslegdogsleg;;; epcs.el --- EPC Server ;; Copyright (C) 2011,2012,2013 Masashi Sakurai ;; Author: Masashi Sakurai ;; Keywords: lisp ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; ;;; Code: (require 'epc) (defvar epcs:client-processes nil "[internal] A list of ([process object] . [`epc:manager' instance]). When the server process accepts the client connection, the `epc:manager' instance is created and stored in this variable `epcs:client-processes'. This variable is used for the management purpose.") ;; epcs:server ;; name : process name (string) ex: "EPC Server 1" ;; process : server process object ;; port : port number ;; connect-function : initialize function for `epc:manager' instances (defstruct epcs:server name process port connect-function) (defvar epcs:server-processes nil "[internal] A list of ([process object] . [`epcs:server' instance]). This variable is used for the management purpose.") (defun epcs:server-start (connect-function &optional port) "Start TCP Server and return the main process object." (lexical-let* ((connect-function connect-function) (name (format "EPC Server %s" (epc:uid))) (buf (epc:make-procbuf (format "*%s*" name))) (main-process (make-network-process :name name :buffer buf :family 'ipv4 :server t :nowait t :host "127.0.0.1" :service (or port t) :sentinel (lambda (process message) (epcs:sentinel process message connect-function))))) (unless port ;; notify port number to the parent process via STDOUT. (message "%s\n" (process-contact main-process :service))) (push (cons main-process (make-epcs:server :name name :process main-process :port (process-contact main-process :service) :connect-function connect-function)) epcs:server-processes) main-process)) (defun epcs:server-stop (process) "Stop the TCP server process." (cond ((and process (assq process epcs:server-processes)) (epc:log "EPCS: Shutdown Server: %S" process) (let ((buf (process-buffer process))) (delete-process process) (kill-buffer buf)) (setq epcs:server-processes (assq-delete-all process epcs:server-processes))) (t (error "Not found in the server process list. [%S]" process)))) (defun epcs:get-manager-by-process (proc) "[internal] Return the epc:manager instance for the PROC." (loop for (pp . mngr) in epcs:client-processes if (eql pp proc) do (return mngr) finally return nil)) (defun epcs:kill-all-processes () "Kill all child processes for debug purpose." (interactive) (loop for (proc . mngr) in epcs:client-processes do (ignore-errors (delete-process proc) (kill-buffer (process-buffer proc))))) (defun epcs:accept (process) "[internal] Initialize the process and return epc:manager object." (epc:log "EPCS: >> Connection accept: %S" process) (lexical-let* ((connection-id (epc:uid)) (connection-name (format "epc con %s" connection-id)) (channel (cc:signal-channel connection-name)) (connection (make-epc:connection :name connection-name :process process :buffer (process-buffer process) :channel channel))) (epc:log "EPCS: >> Connection establish") (set-process-coding-system process 'binary 'binary) (set-process-filter process (lambda (p m) (epc:process-filter connection p m))) (set-process-sentinel process (lambda (p e) (epc:process-sentinel connection p e))) (make-epc:manager :server-process process :port t :connection connection))) (defun epcs:sentinel (process message connect-function) "[internal] Process sentinel handler for the server process." (epc:log "EPCS: SENTINEL: %S %S" process message) (let ((mngr (epcs:get-manager-by-process process))) (cond ;; new connection ((and (string-match "open" message) (null mngr)) (condition-case err (let ((mngr (epcs:accept process))) (push (cons process mngr) epcs:client-processes) (epc:init-epc-layer mngr) (when connect-function (funcall connect-function mngr)) mngr) ('error (epc:log "EPCS: Protocol error: %S" err) (epc:log "EPCS: ABORT %S" process) (delete-process process)))) ;; ignore ((null mngr) nil ) ;; disconnect (t (let ((pair (assq process epcs:client-processes)) d) (when pair (epc:log "EPCS: DISCONNECT %S" process) (epc:stop-epc (cdr pair)) (setq epcs:client-processes (assq-delete-all process epcs:client-processes)) )) nil)))) ;; Management GUI ;; todo... (provide 'epcs) ;;; epcs.el ends here emacs-epc-0.1.1/RPC-EPC/0000755000175000017500000000000012177363221014235 5ustar dogslegdogslegemacs-epc-0.1.1/RPC-EPC/Changes0000644000175000017500000000027412177363221015533 0ustar dogslegdogslegRevision history for RPC-EPC 0.0.3 2011/12/26 Corrected perlcritic test. 0.0.2 2011/12/26 Corrected the document and Build.PL. 0.0.1 2011/12/24 Initial release. emacs-epc-0.1.1/RPC-EPC/lib/0000755000175000017500000000000012177363221015003 5ustar dogslegdogslegemacs-epc-0.1.1/RPC-EPC/lib/RPC/0000755000175000017500000000000012177363221015427 5ustar dogslegdogslegemacs-epc-0.1.1/RPC-EPC/lib/RPC/EPC/0000755000175000017500000000000012177363221016036 5ustar dogslegdogslegemacs-epc-0.1.1/RPC-EPC/lib/RPC/EPC/Service.pm0000644000175000017500000003553512177363221020007 0ustar dogslegdogslegpackage RPC::EPC::Service; use warnings; use strict; use utf8; use Carp; use version; our $VERSION = qv('0.0.7'); use base 'Exporter'; our @EXPORT = qw( to_sexp ); use Encode; use AnyEvent; use AnyEvent::Socket; use AnyEvent::Handle; use Data::SExpression; use Data::SExpression::Symbol; use B; use Data::Dumper; # for debug ################################################## # sexp encoding # (This code is based on Mojolicious's JSON library.) # Translate an argument object into S-expression text. sub to_sexp { my $arg = shift; if (ref $arg eq 'HASH') { return _to_sexp_hash($arg); } elsif (ref $arg eq 'ARRAY') { return _to_sexp_list($arg); } my $flags = B::svref_2object(\$arg)->FLAGS; if ($flags & (B::SVp_IOK | B::SVp_NOK) && !($flags & B::SVp_POK)) { return _to_sexp_num($arg); } else { return _to_sexp_string($arg); } } # for elisp's prin1 escape my %ESCAPE = ( '"' => '"', '\\' => '\\', ); my %REVERSE; for my $key (keys %ESCAPE) { $REVERSE{$ESCAPE{$key}} = "\\$key" } sub _to_sexp_string { my $string = shift; return "nil" unless $string; # Escape $string =~ s/([\\\"])/$REVERSE{$1}/gs; $string = Encode::encode_utf8($string) if Encode::is_utf8($string); # Stringify return "\"$string\""; } sub _to_sexp_num { return shift; } sub _to_sexp_list { my $list = shift; my @out = map {to_sexp $_} @$list; return "(" . join(" ", @out) . ")"; } sub _to_sexp_hash { my $hash = shift; my $out = []; foreach my $key ( keys %$hash ) { push @$out, "(".to_sexp($key)." . ".to_sexp($hash->{$key}).")"; } return "(" . join(" ", @$out) . ")"; } ################################################## # nil to undef our $NIL = Data::SExpression::Symbol->new("nil"); sub _nil_to_undef { my $arg = shift; if (ref $arg eq 'HASH') { return _nil_to_undef_hash($arg); } elsif (ref $arg eq 'ARRAY') { return _nil_to_undef_list($arg); } return if ($NIL eq $arg); return $arg; } sub _nil_to_undef_list { my $list = shift; for (my $i = 0; $i < @$list; $i++) { $list->[$i] = _nil_to_undef($list->[$i]); } return $list; } sub _nil_to_undef_hash { my $hash = shift; foreach my $key ( keys %$hash ) { $hash->{$key} = _nil_to_undef($hash->{$key}); } return $hash; } sub _correct_method_spec { my $a = shift; if (ref($a) eq "CODE") { return {'sub' => $a, 'arg_spec' => undef, 'docstring' => undef}; } elsif (ref($a) eq "ARRAY") { my ($sub,$arg,$doc) = @$a; return {'sub' => $sub, 'arg_spec' => $arg, 'docstring' => $doc}; } elsif (ref($a) eq "HASH") { return $a; } else { return $a; } } sub _correct_method_specs { my $methods = shift; my $ret = {}; foreach my $key ( keys %$methods ) { $ret->{$key} = _correct_method_spec( $methods->{$key} ); } return $ret; } ################################################## # Protocol Stacks sub new { my ($class, $port, $methods) = @_; my $cv = AnyEvent->condvar; return bless { 'port' => $port, 'count' => 0, 'methods' => _correct_method_specs($methods), 'sessions' => {}, 'wait' => $cv }, $class; } sub _register_event_loop { my ($self,$fh) = @_; my $ds = Data::SExpression->new({use_symbol_class=>1,fold_alists=>1}); my $hdl; $hdl = new AnyEvent::Handle (fh => $fh, on_error => sub { my ($hdl, $fatal, $msg) = @_; AE::log warn => "got error $msg\n"; $hdl->destroy; $self->{wait}->send; }); $self->{handle} = $hdl; my $reading_buffer = ""; my @reader; @reader = (line => sub { my ($hdl, $incoming) = @_; # print STDERR "INCOMING:$incoming"; $reading_buffer .= $incoming . "\n"; my $len = substr($reading_buffer, 0, 6); if (!$len =~ /[0-9a-f]{6}/i) { # print STDERR "Wrong length code: $reading_buffer\nAbort\n"; AE::log warn => "Wrong length code: $reading_buffer\n"; $hdl->destroy; $self->{wait}->send; return; } $len = hex($len); my $body = substr($reading_buffer, 6); if ($len > length $body) { # try next lines # print STDERR "lencode:$len / current:" . (length $body) . "\n"; $hdl->push_read(@reader); return; } # print STDERR "Here len:$len / current:" . (length $body) . "\n"; if ($len < length $body) { $reading_buffer = substr($body, $len+1); $body = substr($body, 0, $len); } else { $reading_buffer = ""; } my ($text, $sexp); eval { ($sexp, $text) = $ds->read(Encode::decode_utf8($body)); }; # print STDERR "SEXP:".Dumper $sexp; if ($sexp->[0]) { if ($sexp->[0]->name eq "quit") { $hdl->destroy; $self->{wait}->send; return; } my $handler = $self->{handlers}->{shift(@$sexp)->name}; if ($handler) { $handler->($sexp); } } else { # print STDERR 'NULL:'.Dumper $sexp; } $hdl->push_read(@reader); }); $hdl->push_read(@reader); } sub _handle_connection { my ($self,$fh,$host,$port) = @_; my $handlers = { 'call' => sub { $self->_call(@_); }, 'return' => sub { $self->_return(@_); }, 'return-error' => sub { $self->_return_error(@_); }, 'epc-error' => sub { $self->_epc_error(@_); }, 'methods' => sub { $self->_query_methods(@_); }, }; $self->{handlers} = $handlers; $self->_register_event_loop($fh); } sub _uid { my $self = shift; return $self->{count}++; } sub _send_message { my ($self, $message) = @_; my $hdl = $self->{handle}; my $len = length($message) + 1; # print STDERR ">>> [$message]\n"; $hdl->push_write(sprintf("%06x%s\n", $len, $message)); } sub _call { my ($self, $sexp) = @_; # print STDERR "CALL:" . Dumper $sexp; my $id = shift(@$sexp); my $name = shift(@$sexp); my $task = $self->{methods}->{$name}->{sub}; if ($task) { my $args = _nil_to_undef($sexp->[0]); eval { my $ret = $task->($args); if ((ref $ret) eq "AnyEvent::CondVar") { $ret = $ret->recv; } $self->_send_message("(return $id ".to_sexp($ret).")"); }; if ($@) { $self->_send_message("(return-error $id ".to_sexp($@).")"); } } else { $self->_send_message("(epc-error $id \"Not found the method: $name\")"); } } sub _return { my ($self, $sexp) = @_; # print STDERR "RET:" . Dumper $sexp; my $id = shift(@$sexp); my $result = _nil_to_undef($sexp); my $cv = $self->{sessions}->{$id}; if ($cv) { delete $self->{sessions}->{$id}; $cv->send($result->[0]); } else { print STDERR "Not found ID : $id\n"; } } sub _return_error { my ($self, $sexp) = @_; # print STDERR "ERR-RET:" . Dumper $sexp; my $id = shift(@$sexp); my $result = $sexp->[0]; my $cv = $self->{sessions}->{$id}; if ($cv) { delete $self->{sessions}->{$id}; $cv->croak(['ERROR',$result]); } else { print STDERR "Not found ID : $id\n"; } } sub _epc_error { my ($self, $sexp) = @_; # print STDERR "EPCERR-RET:" . Dumper $sexp; my $id = shift(@$sexp); my $result = $sexp->[0]; my $cv = $self->{sessions}->{$id}; if ($cv) { delete $self->{sessions}->{$id}; $cv->croak(['EPC_ERROR',$result]); } else { print STDERR "Not found ID : $id\n"; } } sub _query_methods { my ($self, $sexp) = @_; # print STDERR "METHODS:" . Dumper $sexp; my $id = shift(@$sexp); eval { my @ret = (); my $hash = $self->{methods}; # print STDERR "HASH:" . Dumper $hash; foreach my $key ( keys %$hash ) { my $method = $hash->{$key}; push @ret, [$key, $method->{'arg_spec'}, $method->{'docstring'}]; } $self->_send_message("(return $id ".to_sexp(\@ret).")"); }; if ($@) { $self->_send_message("(return-error $id ".to_sexp($@).")"); } } sub call_method { my $self = shift; my $name = shift; my $args = shift; my $cv = AnyEvent->condvar; my $id = $self->_uid; $self->{sessions}->{$id} = $cv; $self->_send_message("(call $id $name ".(to_sexp $args).")"); return $cv; } sub define_method { my $self = shift; my $name = shift; $self->{methods}->{$name} = _correct_method_spec(\@_); } sub query_methods { my $self = shift; my $cv = AnyEvent->condvar; my $id = $self->_uid; $self->{sessions}->{$id} = $cv; $self->_send_message("(methods $id)"); return $cv; } sub start { my $self = shift; my $server = tcp_server undef, $self->{port}, sub { $self->_handle_connection(@_); }, sub { my ($fh, $thishost, $thisport) = @_; binmode( STDOUT, ":unix" ); # immediate flush print "$thisport\n"; # epc protocol }; $self->{server} = $server; $self->{wait}->recv; } sub client_start { my $self = shift; my $host = "127.0.0.1"; my $cv = AnyEvent->condvar; my $client = tcp_connect $host, $self->{port}, sub { my ($fh) = @_; if ($fh) { $self->_handle_connection($fh, $host, $self->{port}); $cv->send; } else { $cv->croak(['Could not connect server.']); } }; $self->{client} = $client; $cv->recv; } sub stop { my $self = shift; $self->{handle}->push_shutdown; } 1; # Magic true value required at end of module __END__ =head1 NAME RPC::EPC::Service - An Asynchronous Remote Procedure Stack. =head1 VERSION This document describes RPC::EPC::Service version 0.0.4 =head1 SYNOPSIS =head2 Server code use RPC::EPC::Service; my $server = RPC::EPC::Service->new(8888, { 'add' => sub { my $args_ref = shift; my ($a,$b) = @$args_ref; return $a + $b; }); $server->start; =head2 Client code use RPC::EPC::Service; my $client = RPC::EPC::Service->new($port,{}); $client->client_start; my $ret = $client->call_method('add', [1,2]); $ret->recv == 3; $client->stop; =head1 DESCRIPTION This module enables to connect the other process with the S-expression protocol, like the Swank protocol of the SLIME. SLIME : http://common-lisp.net/project/slime/ The primary objective is for users to make some Emacs extensions with the Perl and CPAN. =head2 Protocol The encoding format is the S-expression. The TCP socket is employed by the communication. Because the RPC session is written in the async manner, the programs can call the procedures asynchronously. =head2 Object Serialization This module can translate following types: =over 4 =item * undef =item * Number =item * String =item * Array =item * Hash =item * Complex of Array and Hash. =back =head1 INTERFACE =head2 Server and Client Commons =head3 C $service = RPC::EPC::Service->new($port, $handlers); Create a server object. If port number is 0 or undef, the number is decided by the OS. The C<$handlers> object is a hash of method names and sub blocks, which methods are called by the peer process. Methods can be also defined by C after the initialization. =head3 define_method $service->define_method($method_name, sub { .... }); Define a method which is called by the peer process. The following form defines a method with documents for the method argument and specifications. $service->define_method($method_name, sub { .... }, "arg1 arg2", "document for this method"); The documents are referred by the peer process for users to inspect the methods. =head3 call_method $ret = $service->call_method($method_name, $args); print $ret->recv; Call the peer's method. The arguments should be packed in one object, such as Array and Hash. This method returns immediately, not waiting for the result, and value C<$ret> is C object. To obtain the result, the program calls the C method, because the peer's task is executed concurrently and the result is sent asynchronously. The C method may raise the error. The error has two types, the peer's program (Application Error) and the RPC stack (RPC Error). The Application Error is a normal error which is caused by peer's program, such as 'division by zero', 'file not found' and so on. The programmers are responsible to this type errors, recovering error handling or just fixing bugs. The RPC Error is a communication error which is caused by RPC stack, such as 'connection closed', 'method not found', 'serialization error' and so on. This type errors are caused by environment problems, bugs of peer's program, our side one or the RPC stack. Here is a sample robust code: $ret = $service->call_method($method_name, $args); eval { print $ret->recv; # normal result }; if ($@) { # Error handling if ($@->[0] eq "ERROR") { # Application Error print $@->[1]; # error message } elsif ($@->[0] eq "EPC-ERROR") { # RPC Error print $@->[1]; # error message } } =head3 query_methods $service->query_methods(); Define a method which is called by the peer process. =head2 Server side =head3 start $service->start; Initialize the connection port and wait for the client connection. This method starts the event loop and blocks the control. =head2 Client side =head3 client_start $service->client_start; Establish the connection to the server. If connection failed, it will die. =head3 stop $service->stop; Shutdown the connection. =head2 Utilities =head3 to_sexp Translate a Perl object into S-expression string. In normal use, serializing and unserializing are applied automatically. =head1 AUTHOR Masashi Sakurai C<< >> =head1 LICENSE AND COPYRIGHT Copyright (c) 2011, 2012 Masashi Sakurai C<< >>. All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENSE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. emacs-epc-0.1.1/RPC-EPC/Makefile.PL0000644000175000017500000000025112177363221016205 0ustar dogslegdogsleguse lib qw(lib); use Module::Build::Compat; Module::Build::Compat->run_build_pl(args => \@ARGV); Module::Build::Compat->write_makefile(build_class => 'Module::Build'); emacs-epc-0.1.1/RPC-EPC/MANIFEST.SKIP0000644000175000017500000000215312177363221016134 0ustar dogslegdogsleg #!start included /usr/lib64/perl5/5.12.4/ExtUtils/MANIFEST.SKIP # Avoid version control files. \bRCS\b \bCVS\b \bSCCS\b ,v$ \B\.svn\b \B\.git\b \B\.gitignore\b \b_darcs\b \B\.cvsignore$ # Avoid VMS specific MakeMaker generated files \bDescrip.MMS$ \bDESCRIP.MMS$ \bdescrip.mms$ # Avoid Makemaker generated and utility files. \bMANIFEST\.bak \bMakefile$ \bblib/ \bMakeMaker-\d \bpm_to_blib\.ts$ \bpm_to_blib$ \bblibdirs\.ts$ # 6.18 through 6.25 generated this # Avoid Module::Build generated and utility files. \bBuild$ \b_build/ \bBuild.bat$ \bBuild.COM$ \bBUILD.COM$ \bbuild.com$ # Avoid temp and backup files. ~$ \.old$ \#$ \b\.# \.bak$ \.tmp$ \.# \.rej$ # Avoid OS-specific files/dirs # Mac OSX metadata \B\.DS_Store # Mac OSX SMB mount metadata files \B\._ # Avoid Devel::Cover files. \bcover_db\b #!end included /usr/lib64/perl5/5.12.4/ExtUtils/MANIFEST.SKIP # Avoid configuration metadata file ^MYMETA\. # Avoid Module::Build generated and utility files. \bBuild$ \bBuild.bat$ \b_build \bBuild.COM$ \bBUILD.COM$ \bbuild.com$ ^MANIFEST\.SKIP # Avoid archives of this distribution \bRPC-EPC-Service-[\d\.\_]+ emacs-epc-0.1.1/RPC-EPC/MANIFEST0000644000175000017500000000044212177363221015366 0ustar dogslegdogslegBuild.PL Changes ignore.txt lib/RPC/EPC/Service.pm Makefile.PL MANIFEST This list of files README t/00.load.t t/01.primitives.t t/02.process.t t/03.echo.t t/04.add.t t/05.errors.t t/06.query.t t/07.encode.t t/_process.pl t/_add.pl t/_echo.pl t/_errors.pl t/_methods.pl META.yml META.json emacs-epc-0.1.1/RPC-EPC/README0000644000175000017500000000104412177363221015114 0ustar dogslegdogslegRPC-EPC version 0.0.2 An asynchronous remote procedure stack for Emacs. INSTALLATION To install this module, run the following commands: perl Makefile.PL make make test make install Alternatively, to install with Module::Build, you can use the following commands: perl Build.PL ./Build ./Build test ./Build install DEPENDENCIES AnyEvent Data::SExpression COPYRIGHT AND LICENCE Copyright (C) 2011, Masashi Sakurai This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. emacs-epc-0.1.1/RPC-EPC/Build.PL0000644000175000017500000000077712177363221015544 0ustar dogslegdogsleguse strict; use warnings; use Module::Build; my $builder = Module::Build->new( module_name => 'RPC::EPC::Service', license => 'perl', dist_author => 'Masashi Sakurai ', dist_version_from => 'lib/RPC/EPC/Service.pm', requires => { 'AnyEvent' => 6.1, 'Data::SExpression' => 0.41, 'Test::More' => 0, 'version' => 0, }, add_to_cleanup => [ 'RPC-EPC-*' ], ); $builder->create_build_script(); emacs-epc-0.1.1/RPC-EPC/ignore.txt0000644000175000017500000000016512177363221016263 0ustar dogslegdogslegblib* Makefile Makefile.old Build Build.bat _build* pm_to_blib* *.tar.gz .lwpcookies cover_db pod2htm*.tmp RPC-EPC-* emacs-epc-0.1.1/RPC-EPC/t/0000755000175000017500000000000012177363221014500 5ustar dogslegdogslegemacs-epc-0.1.1/RPC-EPC/t/01.primitives.t0000644000175000017500000000205112177363221017275 0ustar dogslegdogsleguse Test::More tests => 18; use RPC::EPC::Service; ################################################## # Service $h2 = {}; $ss = RPC::EPC::Service->new(9876,$h2); is($ss->{port},9876); is($ss->{count},0); ok($ss->{methods} => $h2); ok($ss->{sessions} => {}); ok(ref $ss->{wait}, 'AnyEvent::CondVar'); ################################################## # to_sexp interface is(to_sexp("abcd"),"\"abcd\""); is(to_sexp("ab'cd"),"\"ab'cd\""); is(to_sexp("a\nbcd"),"\"a\nbcd\""); is(to_sexp(undef),"nil"); is(to_sexp(123), "123"); # array my @a = (2,4,6); is(to_sexp(\@a), "(2 4 6)"); is(to_sexp([1,2,3]), "(1 2 3)"); is(to_sexp([1,"2",3]), "(1 \"2\" 3)"); # hash my %a = ('a'=>'b','c'=>'d'); is(to_sexp(\%a), "((\"c\" . \"d\") (\"a\" . \"b\"))"); is(to_sexp({'a'=>'b','c'=>'d'}), "((\"c\" . \"d\") (\"a\" . \"b\"))"); is(to_sexp({'a'=>1,'b'=>2}), "((\"a\" . 1) (\"b\" . 2))"); # complex is(to_sexp([1,[2,3.3,45],10]), "(1 (2 3.3 45) 10)"); is(to_sexp({'a'=>[1,2,3], 'c'=>{name => 'OK'}}), "((\"c\" . ((\"name\" . \"OK\"))) (\"a\" . (1 2 3)))"); emacs-epc-0.1.1/RPC-EPC/t/07.encode.t0000644000175000017500000000070212177363221016346 0ustar dogslegdogsleguse Test::More tests => 3; use IPC::Open2; use Encode; use RPC::EPC::Service; use Data::Dumper; $pid = open2(*PROC_OUT, undef, "perl ./t/_echo.pl"); $port = ; eval { $client = RPC::EPC::Service->new($port,{}); $client->client_start; foreach my $text ('abcd', '日本語', 'efgh') { my $ret = $client->call_method('echo', [Encode.decode_utf8($text)])->recv; is($ret->[0], Encode.decode_utf8($text)); } }; kill 1, $pid; emacs-epc-0.1.1/RPC-EPC/t/_echo.pl0000644000175000017500000000043112177363221016110 0ustar dogslegdogsleg#!/usr/bin/perl use RPC::EPC::Service; use Data::Dumper; sub echo_test { my $methods = { 'echo' => sub { my $args = shift; return $args; } }; my $server = RPC::EPC::Service->new(8888, $methods); $server->start; } echo_test(); emacs-epc-0.1.1/RPC-EPC/t/_methods.pl0000644000175000017500000000067112177363221016643 0ustar dogslegdogsleg#!/usr/bin/perl use RPC::EPC::Service; use Data::Dumper; sub methods_test { my $methods = { 'method1' => {'sub' => sub { return 1; }, 'arg_spec' => "args"}, 'test2' => [sub { return 2; }, 'a b c','docstring here...'] }; my $server = RPC::EPC::Service->new(8888, $methods); $server->define_method("defined_method", sub {}, "A","B"); $server->start; } methods_test(); emacs-epc-0.1.1/RPC-EPC/t/pod.t0000644000175000017500000000021412177363221015444 0ustar dogslegdogsleg#!perl -T use Test::More; eval "use Test::Pod 1.14"; plan skip_all => "Test::Pod 1.14 required for testing POD" if $@; all_pod_files_ok(); emacs-epc-0.1.1/RPC-EPC/t/06.query.t0000644000175000017500000000100312177363221016250 0ustar dogslegdogsleguse Test::More tests => 1; use IPC::Open2; use RPC::EPC::Service; $pid = open2(*PROC_OUT, undef, "perl ./t/_methods.pl"); $port = ; eval { $client = RPC::EPC::Service->new($port,{}); $client->client_start; $ret = $client->query_methods(); is(to_sexp($ret->recv), to_sexp([['defined_method',"A","B"], ['method1','args',undef], ['test2','a b c','docstring here...'], ])); }; kill 1, $pid; emacs-epc-0.1.1/RPC-EPC/t/_process.pl0000644000175000017500000000014512177363221016652 0ustar dogslegdogsleguse RPC::EPC::Service; my $s=RPC::EPC::Service->new(8888,{}); $s->{wait}->send; $s->start; sleep 1; emacs-epc-0.1.1/RPC-EPC/t/04.add.t0000644000175000017500000000074512177363221015645 0ustar dogslegdogsleguse Test::More tests => 3; use IPC::Open2; use RPC::EPC::Service; $pid = open2(*PROC_OUT, undef, "perl ./t/_add.pl"); $port = ; eval { $client = RPC::EPC::Service->new($port,{}); $client->client_start; $ret = $client->call_method('add', [1,2]); is($ret->recv, 3); $ret = $client->call_method('add', [123,-122]); is($ret->recv, 1); # ignore additional arguments $ret = $client->call_method('add', [123,-122], 123); is($ret->recv, 1); }; kill 1, $pid; emacs-epc-0.1.1/RPC-EPC/t/00.load.t0000644000175000017500000000020712177363221016021 0ustar dogslegdogsleguse Test::More tests => 1; BEGIN { use_ok( 'RPC::EPC::Service' ); } diag( "Testing RPC::EPC::Service $RPC::EPC::Service::VERSION" ); emacs-epc-0.1.1/RPC-EPC/t/pod-coverage.t0000644000175000017500000000025412177363221017241 0ustar dogslegdogsleg#!perl -T use Test::More; eval "use Test::Pod::Coverage 1.04"; plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage" if $@; all_pod_coverage_ok(); emacs-epc-0.1.1/RPC-EPC/t/03.echo.t0000644000175000017500000000173212177363221016027 0ustar dogslegdogsleguse Test::More tests => 8; use IPC::Open2; use RPC::EPC::Service; $pid = open2(*PROC_OUT, undef, "perl ./t/_echo.pl"); $port = ; eval { $client = RPC::EPC::Service->new($port,{}); $client->client_start; $ret = $client->call_method('echo', "hello epc!"); is($ret->recv, 'hello epc!'); $ret = $client->call_method('echo', 123); is($ret->recv, 123); $ret = $client->call_method('echo', undef); is($ret->recv, undef); $ret = $client->call_method('echo', [1,2,3]); is(to_sexp($ret->recv), to_sexp(['1','2','3'])); $ret = $client->call_method('echo', {a=>'b'}); is(to_sexp($ret->recv), to_sexp({a=>'b'})); $ret = $client->call_method('echo', [10,20,[1,2,3]]); is(to_sexp($ret->recv), to_sexp(['10','20',['1','2','3']])); $ret = $client->call_method('echo', {a=>[1,2,3]}); is(to_sexp($ret->recv), to_sexp([['a','1','2','3']])); $ret = $client->call_method('echo', "abcd\nefgh\nOK"); is($ret->recv, "abcd\nefgh\nOK"); }; kill 1, $pid; emacs-epc-0.1.1/RPC-EPC/t/05.errors.t0000644000175000017500000000137512177363221016432 0ustar dogslegdogsleguse Test::More tests => 6; use IPC::Open2; use RPC::EPC::Service; use Data::Dumper; $pid = open2(*PROC_OUT, undef, "perl ./t/_errors.pl"); $port = ; eval { $client = RPC::EPC::Service->new($port,{}); $client->client_start; eval { $ret = $client->call_method('num_error'); $ret->recv; }; # print STDERR Dumper $@; is('ERROR' => $@->[0]); like($@->[1],qr/Illegal division by zero/); eval { $ret = $client->call_method('die_error'); $ret->recv; }; # print STDERR Dumper $@; is('ERROR' => $@->[0]); like($@->[1],qr/die!/,); eval { $ret = $client->call_method('other_method'); $ret->recv; }; # print STDERR Dumper $@; is('EPC_ERROR' => $@->[0]); like($@->[1],qr/other_method/); }; kill 1, $pid; emacs-epc-0.1.1/RPC-EPC/t/_add.pl0000644000175000017500000000050512177363221015724 0ustar dogslegdogsleg#!/usr/bin/perl use RPC::EPC::Service; use Data::Dumper; sub add_test { my $methods = { 'add' => sub { my $args_ref = shift; my ($a,$b) = @$args_ref; return $a + $b; } }; my $server = RPC::EPC::Service->new(8888, $methods); $server->start; } add_test(); emacs-epc-0.1.1/RPC-EPC/t/02.process.t0000644000175000017500000000033712177363221016566 0ustar dogslegdogsleguse Test::More tests => 1; use IPC::Open2; use RPC::EPC::Service; # start server $pid = open2(*PROC_OUT, undef, "perl ./t/_process.pl"); $val = ; is("8888\n", $val); close PROC_OUT; kill 1, $pid; sleep 0.5; emacs-epc-0.1.1/RPC-EPC/t/perlcritic.t0000644000175000017500000000026112177363221017024 0ustar dogslegdogsleg#!perl -T use Test::More; eval "use Test::Perl::Critic"; plan skip_all => "Test::Perl::Critic required for testing PBP compliance" if $@; Test::Perl::Critic::all_critic_ok(); emacs-epc-0.1.1/RPC-EPC/t/_errors.pl0000644000175000017500000000050012177363221016503 0ustar dogslegdogsleg#!/usr/bin/perl use RPC::EPC::Service; use Data::Dumper; sub errors_test { my $methods = { 'num_error' => sub { 1/0; }, 'die_error' => sub { die "die!"; }, }; my $server = RPC::EPC::Service->new(8888, $methods); $server->start; } errors_test(); emacs-epc-0.1.1/epc.el0000644000175000017500000010014212177363221014273 0ustar dogslegdogsleg;;; epc.el --- A RPC stack for the Emacs Lisp ;; Copyright (C) 2011, 2012, 2013 Masashi Sakurai ;; Author: SAKURAI Masashi ;; Version: 0.1.1 ;; Keywords: lisp, rpc ;; Package-Requires: ((concurrent "0.3.1") (ctable "0.1.2")) ;; URL: https://github.com/kiwanami/emacs-epc ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; This program is an asynchronous RPC stack for Emacs. Using this ;; RPC stack, the Emacs can communicate with the peer process. ;; Because the protocol is S-expression encoding and consists of ;; asynchronous communications, the RPC response is fairly good. ;; ;; Current implementations for the EPC are followings: ;; - epcs.el : Emacs Lisp implementation ;; - RPC::EPC::Service : Perl implementation ;;; Code: (eval-when-compile (require 'cl)) (require 'concurrent) (require 'ctable) ;;================================================== ;; Utility (defvar epc:debug-out nil) (defvar epc:debug-buffer "*epc log*") ;;(setq epc:debug-out t) ;;(setq epc:debug-out nil) (defun epc:log-init () (when (get-buffer epc:debug-buffer) (kill-buffer epc:debug-buffer))) (defun epc:log (&rest args) (when epc:debug-out (with-current-buffer (get-buffer-create epc:debug-buffer) (buffer-disable-undo) (goto-char (point-max)) (insert (apply 'format args) "\n")))) (defun epc:make-procbuf (name) "[internal] Make a process buffer." (let ((buf (get-buffer-create name))) (with-current-buffer buf (set (make-local-variable 'kill-buffer-query-functions) nil) (erase-buffer) (buffer-disable-undo)) buf)) (defun epc:document-function (function docstring) "Document FUNCTION with DOCSTRING. Use this for `defstruct' accessor etc." (put function 'function-documentation docstring)) (put 'epc:document-function 'lisp-indent-function 'defun) (put 'epc:document-function 'doc-string-elt 2) ;;================================================== ;; Low Level Interface (defvar epc:uid 1) (defun epc:uid () (incf epc:uid)) (defvar epc:accept-process-timeout 150 "Asynchronous timeout time. (msec)") (defvar epc:accept-process-timeout-count 100 " Startup function waits (`epc:accept-process-timeout' * `epc:accept-process-timeout-count') msec for the external process getting ready.") (defstruct epc:connection "Set of information for network connection and event handling. name : Connection name. This name is used for process and buffer names. process : Connection process object. buffer : Working buffer for the incoming data. channel : Event channels for incoming messages." name process buffer channel) (epc:document-function 'epc:connection-name "[internal] Connection name. This name is used for process and buffer names. \(fn EPC:CONNECTION)") (epc:document-function 'epc:connection-process "[internal] Connection process object. \(fn EPC:CONNECTION)") (epc:document-function 'epc:connection-buffer "[internal] Working buffer for the incoming data. \(fn EPC:CONNECTION)") (epc:document-function 'epc:connection-channel "[internal] Event channels for incoming messages. \(fn EPC:CONNECTION)") (defun epc:connect (host port) "[internal] Connect the server, initialize the process and return epc:connection object." (epc:log ">> Connection start: %s:%s" host port) (lexical-let* ((connection-id (epc:uid)) (connection-name (format "epc con %s" connection-id)) (connection-buf (epc:make-procbuf (format "*%s*" connection-name))) (connection-process (open-network-stream connection-name connection-buf host port)) (channel (cc:signal-channel connection-name)) (connection (make-epc:connection :name connection-name :process connection-process :buffer connection-buf :channel channel))) (epc:log ">> Connection establish") (set-process-coding-system connection-process 'binary 'binary) (set-process-filter connection-process (lambda (p m) (epc:process-filter connection p m))) (set-process-sentinel connection-process (lambda (p e) (epc:process-sentinel connection p e))) (set-process-query-on-exit-flag connection-process nil) connection)) (defun epc:connection-reset (connection) "[internal] Reset the connection for restarting the process." (cc:signal-disconnect-all (epc:connection-channel connection)) connection) (defun epc:process-sentinel (connection process msg) (epc:log "!! Process Sentinel [%s] : %S : %S" (epc:connection-name connection) process msg) (epc:disconnect connection)) (defun epc:net-send (connection sexp) (let* ((msg (encode-coding-string (concat (epc:prin1-to-string sexp) "\n") 'utf-8-unix)) (string (concat (epc:net-encode-length (length msg)) msg)) (proc (epc:connection-process connection))) (epc:log ">> SEND : [%S]" string) (process-send-string proc string))) (defun epc:disconnect (connection) (lexical-let ((process (epc:connection-process connection)) (buf (epc:connection-buffer connection)) (name (epc:connection-name connection))) (epc:log "!! Disconnect [%s]" name) (when process (set-process-sentinel process nil) (delete-process process) (when (get-buffer buf) (kill-buffer buf))) (epc:log "!! Disconnected finished [%s]" name))) (defun epc:process-filter (connection process message) (epc:log "INCOMING: [%s] [%S]" (epc:connection-name connection) message) (with-current-buffer (epc:connection-buffer connection) (goto-char (point-max)) (insert message) (epc:process-available-input connection process))) (defun epc:process-available-input (connection process) "Process all complete messages that have arrived from Lisp." (with-current-buffer (process-buffer process) (while (epc:net-have-input-p) (let ((event (epc:net-read-or-lose process)) (ok nil)) (epc:log "<< RECV [%S]" event) (unwind-protect (condition-case err (progn (apply 'cc:signal-send (cons (epc:connection-channel connection) event)) (setq ok t)) ('error (epc:log "MsgError: %S / <= %S" err event))) (unless ok (epc:run-when-idle 'epc:process-available-input connection process))))))) (defun epc:net-have-input-p () "Return true if a complete message is available." (goto-char (point-min)) (and (>= (buffer-size) 6) (>= (- (buffer-size) 6) (epc:net-decode-length)))) (defun epc:run-when-idle (function &rest args) "Call FUNCTION as soon as Emacs is idle." (apply #'run-at-time (if (featurep 'xemacs) itimer-short-interval 0) nil function args)) (defun epc:net-read-or-lose (process) (condition-case error (epc:net-read) (error (debug 'error error) (error "net-read error: %S" error)))) (defun epc:net-read () "Read a message from the network buffer." (goto-char (point-min)) (let* ((length (epc:net-decode-length)) (start (+ 6 (point))) (end (+ start length)) content) (assert (plusp length)) (prog1 (save-restriction (narrow-to-region start end) (read (decode-coding-string (buffer-string) 'utf-8-unix))) (delete-region (point-min) end)))) (defun epc:net-decode-length () "Read a 24-bit hex-encoded integer from buffer." (string-to-number (buffer-substring-no-properties (point) (+ (point) 6)) 16)) (defun epc:net-encode-length (n) "Encode an integer into a 24-bit hex string." (format "%06x" n)) (defun epc:prin1-to-string (sexp) "Like `prin1-to-string' but don't octal-escape non-ascii characters. This is more compatible with the CL reader." (with-temp-buffer (let (print-escape-nonascii print-escape-newlines print-length print-level) (prin1 sexp (current-buffer)) (buffer-string)))) ;;================================================== ;; High Level Interface (defstruct epc:manager "Root object that holds all information related to an EPC activity. `epc:start-epc' returns this object. title : instance name for displaying on the `epc:controller' UI server-process : process object for the peer commands : a list of (prog . args) port : port number connection : epc:connection instance methods : alist of method (name . function) sessions : alist of session (id . deferred) exit-hook : functions for after shutdown EPC connection" title server-process commands port connection methods sessions exit-hooks) (epc:document-function 'epc:manager-title "Instance name (string) for displaying on the `epc:controller' UI You can modify this slot using `setf' to change the title column in the `epc:controller' table UI. \(fn EPC:MANAGER)") (epc:document-function 'epc:manager-server-process "Process object for the peer. This is *not* network process but the external program started by `epc:start-epc'. For network process, see `epc:connection-process'. \(fn EPC:MANAGER)") (epc:document-function 'epc:manager-commands "[internal] a list of (prog . args) \(fn EPC:MANAGER)") (epc:document-function 'epc:manager-port "Port number (integer). \(fn EPC:MANAGER)") (epc:document-function 'epc:manager-connection "[internal] epc:connection instance \(fn EPC:MANAGER)") (epc:document-function 'epc:manager-methods "[internal] alist of method (name . function) \(fn EPC:MANAGER)") (epc:document-function 'epc:manager-sessions "[internal] alist of session (id . deferred) \(fn EPC:MANAGER)") (epc:document-function 'epc:manager-exit-hooks "Hooks called after shutdown EPC connection. Use `epc:manager-add-exit-hook' to add hook. \(fn EPC:MANAGER)") (defstruct epc:method "Object to hold serving method information. name : method name (symbol) ex: 'test task : method function (function with one argument) arg-specs : arg-specs (one string) ex: \"(A B C D)\" docstring : docstring (one string) ex: \"A test function. Return sum of A,B,C and D\" " name task docstring arg-specs) (epc:document-function 'epc:method-name "[internal] method name (symbol) ex: 'test \(fn EPC:METHOD)") (epc:document-function 'epc:method-task "[internal] method function (function with one argument) \(fn EPC:METHOD)") (epc:document-function 'epc:method-arg-specs "[internal] arg-specs (one string) ex: \"(A B C D)\" \(fn EPC:METHOD)") (epc:document-function 'epc:method-docstring "[internal] docstring (one string) ex: \"A test function. Return sum of A,B,C and D\" \(fn EPC:METHOD)") (defvar epc:live-connections nil "[internal] A list of `epc:manager' objects those currently connect to the epc peer. This variable is for debug purpose.") (defun epc:live-connections-add (mngr) "[internal] Add the EPC manager object." (push mngr epc:live-connections)) (defun epc:live-connections-delete (mngr) "[internal] Remove the EPC manager object." (setq epc:live-connections (delete mngr epc:live-connections))) (defun epc:start-epc (server-prog server-args) "Start the epc server program and return an epc:manager object. Start server program SERVER-PROG with command line arguments SERVER-ARGS. The server program must print out the port it is using at the first line of its stdout. If the server prints out non-numeric value in the first line or does not print out the port number in three seconds, it is regarded as start-up failure." (let ((mngr (epc:start-server server-prog server-args))) (epc:init-epc-layer mngr) mngr)) (defun epc:server-process-name (uid) (format "epc:server:%s" uid)) (defun epc:server-buffer-name (uid) (format " *%s*" (epc:server-process-name uid))) (defun epc:start-server (server-prog server-args) "[internal] Start a peer server and return an epc:manager instance which is set up partially." (let* ((uid (epc:uid)) (process-name (epc:server-process-name uid)) (process-buffer (get-buffer-create (epc:server-buffer-name uid))) (process (apply 'start-process process-name process-buffer server-prog server-args)) (cont 1) port) (while cont (accept-process-output process 0 epc:accept-process-timeout t) (let ((port-str (with-current-buffer process-buffer (buffer-string)))) (cond ((string-match "^[0-9]+$" port-str) (setq port (string-to-number port-str) cont nil)) ((< 0 (length port-str)) (error "Server may raise an error. \ Use \"M-x epc:pop-to-last-server-process-buffer RET\" \ to see full traceback:\n%s" port-str)) ((not (eq 'run (process-status process))) (setq cont nil)) (t (incf cont) (when (< epc:accept-process-timeout-count cont) ; timeout 15 seconds (error "Timeout server response.")))))) (set-process-query-on-exit-flag process nil) (make-epc:manager :server-process process :commands (cons server-prog server-args) :title (mapconcat 'identity (cons server-prog server-args) " ") :port port :connection (epc:connect "localhost" port)))) (defun epc:stop-epc (mngr) "Disconnect the connection for the server." (let* ((proc (epc:manager-server-process mngr)) (buf (and proc (process-buffer proc)))) (epc:disconnect (epc:manager-connection mngr)) (when proc (accept-process-output proc 0 epc:accept-process-timeout t)) (when (and proc (equal 'run (process-status proc))) (kill-process proc)) (when buf (kill-buffer buf)) (condition-case err (epc:manager-fire-exit-hook mngr) (error (epc:log "Error on exit-hooks : %S / " err mngr))) (epc:live-connections-delete mngr))) (defun epc:start-epc-debug (port) "[internal] Return an epc:manager instance which is set up partially." (epc:init-epc-layer (make-epc:manager :server-process nil :commands (cons "[DEBUG]" nil) :port port :connection (epc:connect "localhost" port)))) (defun epc:args (args) "[internal] If ARGS is an atom, return it. If list, return the cadr of it." (cond ((atom args) args) (t (cadr args)))) (defun epc:init-epc-layer (mngr) "[internal] Connect to the server program and return an epc:connection instance." (lexical-let* ((mngr mngr) (conn (epc:manager-connection mngr)) (channel (epc:connection-channel conn))) ;; dispatch incoming messages with the lexical scope (loop for (method . body) in `((call . (lambda (args) (epc:log "SIG CALL: %S" args) (apply 'epc:handler-called-method ,mngr (epc:args args)))) (return . (lambda (args) (epc:log "SIG RET: %S" args) (apply 'epc:handler-return ,mngr (epc:args args)))) (return-error . (lambda (args) (epc:log "SIG RET-ERROR: %S" args) (apply 'epc:handler-return-error ,mngr (epc:args args)))) (epc-error . (lambda (args) (epc:log "SIG EPC-ERROR: %S" args) (apply 'epc:handler-epc-error ,mngr (epc:args args)))) (methods . (lambda (args) (epc:log "SIG METHODS: %S" args) (epc:handler-methods ,mngr (caadr args)))) ) do (cc:signal-connect channel method body)) (epc:live-connections-add mngr) mngr)) (defun epc:manager-add-exit-hook (mngr hook-function) "Register the HOOK-FUNCTION which is called after the EPC connection closed by the EPC controller UI. HOOK-FUNCTION is a function with no argument." (let* ((hooks (epc:manager-exit-hooks mngr))) (setf (epc:manager-exit-hooks mngr) (cons hook-function hooks)) mngr)) (defun epc:manager-fire-exit-hook (mngr) "[internal] Call exit-hooks functions of MNGR. After calling hooks, this functions clears the hook slot so as not to call doubly." (let* ((hooks (epc:manager-exit-hooks mngr))) (run-hooks hooks) (setf (epc:manager-exit-hooks mngr) nil) mngr)) (defun epc:manager-status-server-process (mngr) "[internal] Return the status of the process object for the peer process. If the process is nil, return nil." (and mngr (epc:manager-server-process mngr) (process-status (epc:manager-server-process mngr)))) (defun epc:manager-status-connection-process (mngr) "[internal] Return the status of the process object for the connection process." (and (epc:manager-connection mngr) (process-status (epc:connection-process (epc:manager-connection mngr))))) (defun epc:manager-restart-process (mngr) "[internal] Restart the process and reconnect." (cond ((null (epc:manager-server-process mngr)) (error "Cannot restart this EPC process!")) (t (epc:stop-epc mngr) (let* ((cmds (epc:manager-commands mngr)) (new-mngr (epc:start-server (car cmds) (cdr cmds)))) (setf (epc:manager-server-process mngr) (epc:manager-server-process new-mngr)) (setf (epc:manager-port mngr) (epc:manager-port new-mngr)) (setf (epc:manager-connection mngr) (epc:manager-connection new-mngr)) (setf (epc:manager-methods mngr) (epc:manager-methods new-mngr)) (setf (epc:manager-sessions mngr) (epc:manager-sessions new-mngr)) (epc:connection-reset (epc:manager-connection mngr)) (epc:init-epc-layer mngr) (epc:live-connections-delete new-mngr) (epc:live-connections-add mngr) mngr)))) (defun epc:manager-send (mngr method &rest messages) "[internal] low-level message sending." (let* ((conn (epc:manager-connection mngr))) (epc:net-send conn (cons method messages)))) (defun epc:manager-get-method (mngr method-name) "[internal] Return a method object. If not found, return nil." (loop for i in (epc:manager-methods mngr) if (eq method-name (epc:method-name i)) do (return i))) (defun epc:handler-methods (mngr uid) "[internal] Return a list of information for registered methods." (let ((info (loop for i in (epc:manager-methods mngr) collect (list (epc:method-name i) (or (epc:method-arg-specs i) "") (or (epc:method-docstring i) ""))))) (epc:manager-send mngr 'return uid info))) (defun epc:handler-called-method (mngr uid name args) "[internal] low-level message handler for peer's calling." (lexical-let ((mngr mngr) (uid uid)) (let* ((methods (epc:manager-methods mngr)) (method (epc:manager-get-method mngr name))) (cond ((null method) (epc:log "ERR: No such method : %s" name) (epc:manager-send mngr 'epc-error uid (format "EPC-ERROR: No such method : %s" name))) (t (condition-case err (let* ((f (epc:method-task method)) (ret (apply f args))) (cond ((deferred-p ret) (deferred:nextc ret (lambda (xx) (epc:manager-send mngr 'return uid xx)))) (t (epc:manager-send mngr 'return uid ret)))) (error (epc:log "ERROR : %S" err) (epc:manager-send mngr 'return-error uid err)))))))) (defun epc:manager-remove-session (mngr uid) "[internal] Remove a session from the epc manager object." (loop with ret = nil for pair in (epc:manager-sessions mngr) unless (eq uid (car pair)) do (push pair ret) finally do (setf (epc:manager-sessions mngr) ret))) (defun epc:handler-return (mngr uid args) "[internal] low-level message handler for normal returns." (let ((pair (assq uid (epc:manager-sessions mngr)))) (cond (pair (epc:log "RET: id:%s [%S]" uid args) (epc:manager-remove-session mngr uid) (deferred:callback (cdr pair) args)) (t ; error (epc:log "RET: NOT FOUND: id:%s [%S]" uid args))))) (defun epc:handler-return-error (mngr uid args) "[internal] low-level message handler for application errors." (let ((pair (assq uid (epc:manager-sessions mngr)))) (cond (pair (epc:log "RET-ERR: id:%s [%S]" uid args) (epc:manager-remove-session mngr uid) (deferred:errorback (cdr pair) (format "%S" args))) (t ; error (epc:log "RET-ERR: NOT FOUND: id:%s [%S]" uid args))))) (defun epc:handler-epc-error (mngr uid args) "[internal] low-level message handler for epc errors." (let ((pair (assq uid (epc:manager-sessions mngr)))) (cond (pair (epc:log "RET-EPC-ERR: id:%s [%S]" uid args) (epc:manager-remove-session mngr uid) (deferred:errorback (cdr pair) (list 'epc-error args))) (t ; error (epc:log "RET-EPC-ERR: NOT FOUND: id:%s [%S]" uid args))))) (defun epc:call-deferred (mngr method-name args) "Call peer's method with args asynchronously. Return a deferred object which is called with the result." (let ((uid (epc:uid)) (sessions (epc:manager-sessions mngr)) (d (deferred:new))) (push (cons uid d) sessions) (setf (epc:manager-sessions mngr) sessions) (epc:manager-send mngr 'call uid method-name args) d)) (defun epc:define-method (mngr method-name task &optional arg-specs docstring) "Define a method and return a deferred object which is called by the peer." (let* ((method (make-epc:method :name method-name :task task :arg-specs arg-specs :docstring docstring)) (methods (cons method (epc:manager-methods mngr)))) (setf (epc:manager-methods mngr) methods) method)) (defun epc:query-methods-deferred (mngr) "Return a list of information for the peer's methods. The list is consisted of lists of strings: (name arg-specs docstring)." (let ((uid (epc:uid)) (sessions (epc:manager-sessions mngr)) (d (deferred:new))) (push (cons uid d) sessions) (setf (epc:manager-sessions mngr) sessions) (epc:manager-send mngr 'methods uid) d)) (defun epc:sync (mngr d) "Wrap deferred methods with synchronous waiting, and return the result. If an exception is occurred, this function throws the error." (lexical-let ((result 'epc:nothing)) (deferred:$ d (deferred:nextc it (lambda (x) (setq result x))) (deferred:error it (lambda (er) (setq result (cons 'error er))))) (while (eq result 'epc:nothing) (save-current-buffer (accept-process-output (epc:connection-process (epc:manager-connection mngr)) 0 epc:accept-process-timeout t))) (if (and (consp result) (eq 'error (car result))) (error (cdr result)) result))) (defun epc:call-sync (mngr method-name args) "Call peer's method with args synchronously and return the result. If an exception is occurred, this function throws the error." (epc:sync mngr (epc:call-deferred mngr method-name args))) (defun epc:live-p (mngr) "Return non-nil when MNGR is an EPC manager object with a live connection." (let ((proc (ignore-errors (epc:connection-process (epc:manager-connection mngr))))) (and (processp proc) ;; Same as `process-live-p' in Emacs >= 24: (memq (process-status proc) '(run open listen connect stop))))) ;;================================================== ;; Troubleshooting / Debugging support (defun epc:pop-to-last-server-process-buffer () "Open the buffer for most recently started server program process. This is useful when you want to check why the server program failed to start (e.g., to see its traceback / error message)." (interactive) (let ((buffer (get-buffer (epc:server-buffer-name epc:uid)))) (if buffer (pop-to-buffer buffer) (error "No buffer for the last server process. \ Probably the EPC connection exits correctly or you didn't start it yet.")))) ;;================================================== ;; Management Interface (defun epc:controller () "Display the management interface for EPC processes and connections. Process list. Session status, statistics and uptime. Peer's method list. Display process buffer. Kill sessions and connections. Restart process." (interactive) (let* ((buf-name "*EPC Controller*") (buf (get-buffer buf-name))) (unless (buffer-live-p buf) (setq buf (get-buffer-create buf-name))) (epc:controller-update-buffer buf) (pop-to-buffer buf))) (defun epc:controller-update-buffer (buf) "[internal] Update buffer for the current epc processes." (let* ((data (loop for mngr in epc:live-connections collect (list (epc:manager-server-process mngr) (epc:manager-status-server-process mngr) (epc:manager-status-connection-process mngr) (epc:manager-title mngr) (epc:manager-commands mngr) (epc:manager-port mngr) (length (epc:manager-methods mngr)) (length (epc:manager-sessions mngr)) mngr))) (param (copy-ctbl:param ctbl:default-rendering-param)) (cp (ctbl:create-table-component-buffer :buffer buf :width nil :model (make-ctbl:model :column-model (list (make-ctbl:cmodel :title "" :align 'left) (make-ctbl:cmodel :title "" :align 'center) (make-ctbl:cmodel :title "" :align 'center) (make-ctbl:cmodel :title " Title " :align 'left :max-width 30) (make-ctbl:cmodel :title " Command " :align 'left :max-width 30) (make-ctbl:cmodel :title " Port " :align 'right) (make-ctbl:cmodel :title " Methods " :align 'right) (make-ctbl:cmodel :title " Live sessions " :align 'right)) :data data) :custom-map epc:controller-keymap :param param))) (pop-to-buffer (ctbl:cp-get-buffer cp)))) (eval-when-compile ; introduce anaphoric variable `cp' and `mngr'. (defmacro epc:controller-with-cp (&rest body) `(let ((cp (ctbl:cp-get-component))) (when cp (let ((mngr (car (last (ctbl:cp-get-selected-data-row cp))))) ,@body))))) (defun epc:controller-update-command () (interactive) (epc:controller-with-cp (epc:controller-update-buffer (current-buffer)))) (defun epc:controller-connection-restart-command () (interactive) (epc:controller-with-cp (let* ((proc (epc:manager-server-process mngr)) (msg (format "Restart the EPC process [%s] ? " proc))) (when (and proc (y-or-n-p msg)) (epc:manager-restart-process mngr) (epc:controller-update-buffer (current-buffer)))))) (defun epc:controller-connection-kill-command () (interactive) (epc:controller-with-cp (let* ((proc (epc:manager-server-process mngr)) (msg (format "Kill the EPC process [%s] ? " proc))) (when (and proc (y-or-n-p msg)) (epc:stop-epc mngr) (epc:controller-update-buffer (current-buffer)))))) (defun epc:controller-connection-buffer-command () (interactive) (epc:controller-with-cp (switch-to-buffer (epc:connection-buffer (epc:manager-connection mngr))))) (defun epc:controller-methods-show-command () (interactive) (epc:controller-with-cp (epc:controller-methods mngr))) (defun epc:controller-methods (mngr) "Display a list of methods for the MNGR process." (let* ((buf-name "*EPC Controller/Methods*") (buf (get-buffer buf-name))) (unless (buffer-live-p buf) (setq buf (get-buffer-create buf-name)) (with-current-buffer buf (setq buffer-read-only t))) (lexical-let ((buf buf) (mngr mngr)) (deferred:$ (epc:query-methods-deferred mngr) (deferred:nextc it (lambda (methods) (epc:controller-methods-update-buffer buf mngr methods) (pop-to-buffer buf))))))) (defface epc:face-title '((((class color) (background light)) :foreground "Slategray4" :background "Gray90" :weight bold) (((class color) (background dark)) :foreground "maroon2" :weight bold)) "Face for titles" :group 'epc) (defun epc:controller-methods-update-buffer (buf mngr methods) "[internal] Update methods list buffer for the epc process." (with-current-buffer buf (let* ((data (loop for m in methods collect (list (car m) (or (nth 1 m) "") (or (nth 2 m) "")))) (param (copy-ctbl:param ctbl:default-rendering-param)) cp buffer-read-only) (erase-buffer) (insert (propertize (format "EPC Process : %s\n" (mapconcat 'identity (epc:manager-commands mngr) " ")) 'face 'epc:face-title) "\n") (setq cp (ctbl:create-table-component-region :model (make-ctbl:model :column-model (list (make-ctbl:cmodel :title "Method Name" :align 'left) (make-ctbl:cmodel :title "Arguments" :align 'left) (make-ctbl:cmodel :title "Document" :align 'left)) :data data) :keymap epc:controller-methods-keymap :param param)) (set (make-local-variable 'epc:mngr) mngr) (ctbl:cp-set-selected-cell cp '(0 . 0)) (ctbl:cp-get-buffer cp)))) (defun epc:controller-methods-eval-command () (interactive) (let ((cp (ctbl:cp-get-component))) (when cp (let* ((method-name (car (ctbl:cp-get-selected-data-row cp))) (args (eval-minibuffer (format "Arguments for calling [%s] : " method-name)))) (deferred:$ (epc:call-deferred epc:mngr method-name args) (deferred:nextc it (lambda (ret) (message "Result : %S" ret))) (deferred:error it (lambda (err) (message "Error : %S" ret)))))))) (defun epc:define-keymap (keymap-list &optional prefix) "[internal] Keymap utility." (let ((map (make-sparse-keymap))) (mapc (lambda (i) (define-key map (if (stringp (car i)) (read-kbd-macro (if prefix (replace-regexp-in-string "prefix" prefix (car i)) (car i))) (car i)) (cdr i))) keymap-list) map)) (defun epc:add-keymap (keymap keymap-list &optional prefix) (loop with nkeymap = (copy-keymap keymap) for i in keymap-list do (define-key nkeymap (if (stringp (car i)) (read-kbd-macro (if prefix (replace-regexp-in-string "prefix" prefix (car i)) (car i))) (car i)) (cdr i)) finally return nkeymap)) (defvar epc:controller-keymap (epc:define-keymap '( ("g" . epc:controller-update-command) ("R" . epc:controller-connection-restart-command) ("D" . epc:controller-connection-kill-command) ("K" . epc:controller-connection-kill-command) ("m" . epc:controller-methods-show-command) ("C-m" . epc:controller-methods-show-command) ("B" . epc:controller-connection-buffer-command) )) "Keymap for the controller buffer.") (defvar epc:controller-methods-keymap (epc:add-keymap ctbl:table-mode-map '( ("q" . bury-buffer) ("e" . epc:controller-methods-eval-command) )) "Keymap for the controller methods list buffer.") (provide 'epc) ;;; epc.el ends here emacs-epc-0.1.1/img/0000755000175000017500000000000012177363221013760 5ustar dogslegdogslegemacs-epc-0.1.1/img/mm-conns.png0000644000175000017500000002400512177363221016216 0ustar dogslegdogslegPNG  IHDRDŷsRGB pHYs+tIMEf;iTXtCommentCreated with GIMPd.e IDATxy\'-rDAQ*(oPPKUTDzԪP*PQE%WHv!1 d2̳<1L&ajj:m4HbOs ۟S炂:8Ub7ۛf +**.]ԥ;( B KMѣGGDD t?:/ ywy16Okhh8;;AIOO1cPRR)**7>}"/^֩SºTǏU@SBDɡP(all,B9xҧHLL$х W^}1~iӦ˜3fTVV3rɾi/>3--NFF&::ܹso޼p,X 77777{̘1x2mU,k`U@ z;?755)**agϞUQQijjhOYпG0̆;SSSyɓ4Q݋,## 77 .*))[x` fkk5ϙ3G^^^QQikk+**Κ5rPmmmiiicǎ!Q!󒐐 111 ŋ ((HO/cB#ߐ{+oޱcNOJJ:deecbb02dɽ{TիWy<ޚ5k._6k,uuW^xJ蘚zCau9rD__?%%sEEE&&&'''߻w+O$x?1bSsss/ţyzgqq1Bڵk%a۷o]zB yׯ}񥮮8 ` ̛Hq ::ZEE%88xƍ'|B$xiii3f̘4iyعsF\\\~~Ν;!o"Bn{cmmqFcǎyyy]vڵDD6斑pBI^͛ o߾UTT;rH~5kɻToIKKwttōEE'N`dț{5o2OWq=՝>}===P}l{_˛⼷7>;VO/c]#ߐ%yҥqqq]>/z޽7nbrSSS===̀ccc MLL-sΕիW+V`X,K[[>!bŊ#GnܸW)))<oʔ) >bK!)nCBh̘1? fry dāCbo;511H^ڰaѣ?35*4n|xAA={Əg+Dr+WoLOOqㆫ۷o?233###Н;w'??(--KyuuuEE|tSr}"#WZ/ZjG~ LUUUe$G#DAA'%Mx7obKPOYYٝ;w,^XAA!;;‚H1<{/_TUUx&T1M544̚5ÂvyZPk///=D}YMM ͛7--- rssÄ7o֦R***|۶mT*`,\\~]##~ޢCޯ~h4lz>l6oEa#.pƌ]6.)o*S__ORhR@zl6ۻwyx`@ 7y3 ܼ9???99")=AOAO/@nXo+H)Z<.gg'.P( zn=A ?|G޻wgÇ_K]˔fMK7=!˸ZGG!rR4i99_#wPf҂*))yꕅBHVVvjT) rikiCgClW__eeeB moojOs r 99GL&;ϯwߓ.=?=~;@t}.PUUUNN")=AOAO/@n44>  os<IIOgk^`;2t 'R>Bs_B`8I1P z~zBr> q;::7dϞ=t:N D}z[Ϳ;4+W'NUWWpxHҥK׭[^zĉmmmbO~^={ mW<^m^fN.&9waC-,o iyg`b[nmll>7nݻw1y|(**~.:tTT}z[>ٳgRPPuֳgFFFz{{S(>n)//"9 ygO"σl6N߹s466jiiijjz{{744`xj*|WWW:c\xy6b^.g}}}555WW/^OÉD'\.dΡۇh?-¤Ã$:^PPo߾[nQTl[lYMMMxx%KJJJBǏonn>zhvvS>dO)>F`` O333Ϝ9ǦPĥK {՞lW$.e^~r׮]{y#;;0eee:ٳǏRСC߯YRR`0v5''O~w***~izz:=Zo{IJ&O0hY'^濉9t埔9,R0dvY_+ mb!v:3qUoDְpw ^3j2~[NjMH:/Zc#BMZ0{֤q㖺8Ϟ>D?vX--yyyt:>}zjjjfffgYgg;Lnnn~ԩ+Wjjjwa``0dȐ?z//) -,,-[vʕ Bzzz򦦦&&&U-^͛7a\\ܲe˼RRRFQTTteo133ꫯ0700PTT=qƍ9rdkkk]'  2 fddijjjhh8::/Aѱ~z qedd+bXhhhnn.$֋G|BQhMTT!+%򷞜`--}|ԙ蘟Juײw6ܥbputlBUm|iiәVuޓ\r:6ȨnvM~wJn_!DD;r#܉?_OI]0{AKL yKĈDԟOd"?՞B%gQEW_}5wiӦᒚ>p&Lrb3?OKK[x fϞS$m?4閖ØL3sX!Ϛ펯B!8y_g ;|TCM->G*!dma_U5&,E間t)2Im254+,kgc̯+}B1 cMMQ͢?MfkظO.=K?l0Ç3J˶mBo߾U?=uԕ+W;gϞ544z;Bq߾}pȑGGǃO:555ܹ_WWvرsN3dȐ|ڵk)Qgg-[ONNnŊuuu/ D]xq̘1l6aX666 8p`NNN!KKv#|7111222!++++^oى!&**B͙3]vPQQQQQQVVh=4Bnnĉ㝄x%%.Fzn~"_hBݱcG\\IWO_A'jO~988iIIIB&yرP.{5ʕ+g޹s*y6l8w\[[yqKYYYee7o̜9ں'7ͯr@hh \.yEſGc(kij`$ҳLxcgM/Yxq{G)S7~~AaO552Ē]PfB<+{d施(G%&yWP=\;ji v*to\I&}B%3[jUDDČ3ĎbaOT\n`````d㏱櫨(LsNrrݻSSScWWAȄ,]t׮]rrrswpB'!uU!/iyۊE[4UFF -H=ʯ?_?g6]wҊML|V^P$IK̛ͫW TCMUCM@_]Uٵ3w(6r,TDB:?HIIa?~z҅ {^44LMMMMM'OsƍKJcǎ3&::={lѢE\OO/;;;...&&믿 . h4Zu_C'T*U*Omm)vAȼ9⬬gJp"Kdgkjj`0Μ9&bW=7OİHH_gΜihhذaGNNNNNNJJJŖأ#^qɹ|R[[uuuuuu###553f+, Wa7***Ν m?7ꪪLFÇw?B_z~h6Bh䈴;D<.NN"d+ݒbBlw?=rs^t!骽mMMgҭ?J=iHS7Zl`]CS#Ba(%xjt:>I:c``߼ySXX(xɓ'uuucǎUSSc2eeez|^R/11ӧ"P(\.?P<==/^x…ӧk݈`0ׯ_ 9pBHKKB{f466 Q?100믿xǚ5k,,,ttt~B?^3=DWW|?**I]]]R?"џ]A;_*zܹիWA%{MODg]YR~zii NǖƌԔ;|=Z)J bIDAT߿-;v??~d<g뼼>ʤ3YNpy^~}a MwN<[i>Ee=`hqm[Flmk59vpuDEeĨGPocS? v-oB)r4j:'\O=ztBBݻalСۿ¾}F`ӿ!(([Xj y!Z<|ѣG~ݺ5556ÇaבMMM---yyy ?;88u%Kmv HHHhZZZw544D)))޽[AA˷nWVVTWWx<,6l#<جY!GP̒{OS0Ə_\\|Q;;j+++KSnٲ$Asd>/^$ D 3Yfڵ믿5?D"9 I',///8o>0aB呑CYYY ڵ x]zkeee00¸%oiy3@ 7y3@ y3@ 7y3 fۓjmIENDB`emacs-epc-0.1.1/img/epc-stack.png0000644000175000017500000003040012177363221016335 0ustar dogslegdogslegPNG  IHDR(LK0IDATx Ž{awلD 8`!Ey_ѧ$.!M!$9Q ԰(*a(_V=5}{0=3]]}~߮,5%ek{-k{P}a-|)({ rۜ5P4ڶNh\{=QG \{)6_I!P>HD-zJ8Da=^5%A`G(, `PBLԠ!A 0(AAaP0(! `P0(aP0(! ! !A 0(AAaP0(! `PB z.kfvAA*Kl:~yv0(jWEʠmATs iNvAOwz| }nv"Gͱԩ/4?vz~k:#tA&bsew<[!͢4M׳k*r];qPy:oPǤ5lPn԰=gإo9Nխeh?W ?3 R"C3{'zWյ_%ʭ11 3~{IXEY +oۺs+'ƛN~M) `P0(Ń{8vc+G?nm^ɩYݳ3osRNlZj$dZ_O?8攮my-Gkgw5mR/VɛLK^%Ϣ^4g:9>5u"0(4%nvQQyWe =zM?g]Z.tZ; =inyhS~4z ?ɫ;5;];zOwcPD6dle,FMKZ>Я s-ǠH>9z~'?I+ `P0(i  {U Z1!2O:.ٴ>BRϮ:ΊS2(E: AdP ӱcIP/ToB`~y ^|:xtɦ;whLNz b$('BR1(*iOPAɠy({.< B`vqgmy%<$jM;ev:z\u#5'_"&VgoI:bP0(,} 5 ܷf.ާB`v6u K6-M5n?8;M't_3nټkb A8~ Tw,5 R;"+ p̢ "5ҁ]&ng߻w.G&~++mkqȘ&A{o-NY^S>8aƠ`P0(PQgXyA 0( aP0(A 0(0(! !A BAAaP0(aP0(! ! 0(! !A6(Ż;(ɽ]p5( l^NԠ,5(kz^\O)e=ϯ1rܤjܗ) _zƕ).pG(wqhPE [~xoZh[6B(G[^D1&䞦dfĜǹ-Ooi|B(G޷rmT棁õ's6޿lG($SmJ8֚Q#{ٽ) ;B+潍A BzBzp @^mJUTP.*m.K1m&šjt~ J(5JA:Min5>jt/`PB8TטJSclJ ^Y6LK#JR̀z޾iR{#$JG6rY<2}ǵ!*hP䍜>ۏr~ d~eRuNSBi^eE/DPL@ uC,wX&&Jue^Jq{!A̧Qehq{=[π֧ PO˓tmѠH?|g{!AI[uAT}p'qL7,,Si~6;W-+3avܐbׅUqIj;Z1g#J$svkҼ_ 0(i!}BfwX?Ӵ!TګHяca stϕSb}mlwX&`P_5Y:{ BAAaP `PP ")(}Q͚vf- 2c'Nϰ5jb׭W>G/{-wKq-0(YbG^Zaw<):ooAA G_X>pgٷ3|;펝1/aS{Rvg7r=0(`$AɐA9oP>4יף{*3ֻm۵z`P2*o%t+ȡ1]+,+ б,l췬  J&K-,kаQq'}"*/odL욢J|wV|k R6% `PбfP^{ sIgYwY󖭜yҤƠT7)c]˲̪8 !tƋߠlA˅VDz a}VIj,b:c=/idеz[Eɻ:q ҇VbкMJCc#NH銈5(Qν >S'h.ŠLd>%OyeyZk;_ߺA ddnk ̡FZ.CO582x= cİ#II2oV;>iG* %:u:vX!8GS%hxJ EaP0((M<;-[Ͻ DԪu['O|cP:G12J`=?h+J ^(%|򏆘>(e:Z!K3OV}*~%73=AAUOnO>("yGd !@?h ݾ_AI7&bN.K?LO ߼(y/Pq-1F-ST}V/N7Ġ`P14(u<'ge~;A)u/gGywH e1*4RQpYmAweDJS1(G'*\\;ӠԍaP AAȠHwr9{y{qVgQVQA|| VK=k}eР*v5yS%|{LbR1(=hڽҨɄANڝ<0(dcvN]n{cL!25:4,v@"o[67dРo&Dt ʹck隝FMINa%:VAI%=mO+="5o覚&Fߔ fϔk0(!N@4`PBcG6ng%0(! @0Jr@7?jrZ0(! ! !A A 0(0(! !A BAAaP0(aP0(! @l~x]Rxk;By 垖{refe;By u BJ8Y߾b:%)a=J8fPC䞦dy2 JtbBݼӈAAaP2tJB-=:=ZiR'tP9SeOsȡی~׀3AjX?Qii<37 J_GS#c Dn B $H,ɲrHOfJq%J43D?Z`cEP1;nͰotlEjdkht&#j(ݠIi4v'AJt Bqf$+(ӫq5lz4(T}*diaJ+=fm3l}tb~yZJf1Ҧe67S0(ͺ&쟺6pc}6`PBjGq+)){e׫WϮ]ݭ[jR=t38i۴iȂƌcT(jժ|]Q-0(Z'ѣ1bP Aq[x3zj =z8i|A % t  8Ci뾊ktLw4䯬H*vӽgӠ<2gy;nذ]~}wҚWQnܸݻwQ'aZl]F iӦ5km۶+WVHt$:sʍ/ٞw:u&Mؗ\rI31uK_U\<5(2( %g])}`YI'Dkkc.dǩE h)PNg :uWZhP]SOuGncL2NYt]tQ4-rLO1 nvf*&%KTʗ_3_kq JcccP2}mԠܤ4g|wOڏ2+1И70ȗ|勾 +#~%OĠ`XW[KV+1h)"۸ͪ=oykEd6_"ۓ֖W5 :oVb8շ8Gc䵉b`䷵79:w|wjź*׶6YRgZ} ʥ^~OT(7Ιׯ)S̓>AyuۯJۍz~J4`'wߝ4͈#4_̙LU:& Rk"޽3Oַe?SNMĉy͛7ϸ%1DyjPd4nɰB)B ]D?þKZM"ξ殆p'_V@7 >ʱQG/_cruZ*W_ 4Qנd:F="6 ވQ_da@0 Fi* >q7>D{]2q[Qq 8(g9/&nryl|}VAD8~H-!Eqi}m_lҽq Ro?6 kV[:~<ӻ´H(_[x<ƾy{| &Z߼NX8B+um/MعĹ޺OKYLs+g [}U[3 Oy:͏ݨtJ &nM*MQ?˷/5t ;QٲeKgzԩN tTkP2p]6 4lVJ|Vq;u$N(l_յo*w_E qy7_-5 dJ_aκAn71+ݼ&i̙wx|X*fɫ4a4>U^sW\ACFik*q0P<.:}}5njkg_~n\-),G#V8'eiF< ORó5fGYAɔA ;wVګ!۔ |_~FWA k1("y'FC3[to, 4hs(..4j98P7n|rG;G Y/ٳ)AYOSڵ {SƍcP ) q@Ԡ":^8D( \y EҌUڡ(>WRi'mg>T:IigyM)mT*,wJt,WnR}czJo**n%ƻqIcJ˔LRڪt9afmI~ne\A `P4PjA+1KpYtR]|6#6Fh*CJNSg_#y^TGK5XKmkdJ͔8y`PA !wԀ 3- ݡtUt]RSOP*Ԃb m\Zt0Xqoض^9cAə€u龺q-򃺆ď!:z*K&O!c^>N:E! j>öa %g J ifRo] ]7d_) VZ4 -MJiS6KQn`A딥ϰmQaPA{"Gx˥^} [-C:^|OϡcdPY;bP]{2F?ŋXo,k+d(,7oa3KӐIv12( $X6 ɀAkiO7d\S_<{G.9b>|[:(KTjXjcם^ڠjN}, [^o/!0( `P0( `PWvڅAwRPPPI5kִ[ha2^rq yt!޽{9sؽz4ibׯ_ݻ_ꫯron~͛ۍ7o޷o_c믷4hA8 WXqs:k͘AٴiݥKihF/ؓmC駟V{maPB)sbm6K.q 45(sK/zFc~>sjOԩ,9sfFQ>}t^xaСCN- /`~β &`P۠Wϴ]{`d|(osrh]J[#35ۡ,mWxN&߱c[fi&-[Æ 6lh7j>|]ZZZi֭s֓vў>}|Q֥^j'=zԩ o,VdܹӮQݲeHлY޷o_wo*5)ŽF~H[o9Zn]a~_'O~y)5k6àƪ8l䫺2yRׇ&Y& kSZYR@2TV I J*OL^ȋI&mժU Ҷm M[nmj)Ls)8O.ӦMm5kV4ɘ?~욅yg;-Z\LԪxDkH|0qdzz/FoܸqW^Yn\s JZȇVF>2^# &C/̿Ic]̪8|a}Vbbce}l}wdYmwe~H~'Y&3U>ix>`%r36,i3J|(h!_l~J XiP$ ;7Ay[L`P"Ç7n?|gt4_qIL:ՙ/=)[$@e,sꪫy_|{n駟.Gr7WVdH%_Ҥk{ I۩S'?C9'~dzο]vAziI_Zτz:xh^gZ0I>^Y ;t0#h h:`vr~:yj*dS'mA<0|}ѠD97C~s=i ~+O1۹S:|2O}wy[n`ܴitT]Z%J?/^줗>y '8˥HQ,YԢ\~v=mJJ6wo2_'d3X:q*~̧1PԵa6x置 ".3y4/ވ%X}8i JQ|Jߣ-VE97E!M%"wkMyR a%I-?Iy?餓t…)"N$c=6$"[tieit?c MټysJ2G|F*_ v*Gs 4¼˼Sݔה:$iX`)(cn*[!&qɪAq_k8uٙo$݇;F? d)ouWDf},ח .tС &l4~oezz~7kAIii!֧jѤP/Y;R3 AAH>%P/qUePaPԚLwo^3m&=v{:U?ޙ'%S4۔”)S;LJn͙woM6μ'|s9Ҍ't4) .^z3O8(Aq J'G493￿|;Q1a~B΄A73Α{%zDz~ ^yʓ*5b\d, &iJn'#Sji& Ly5ح="#fzS&FC),5eAŝ>A;U"}Ǭ;Z:X=#H96maRkϲ>:H*;o!59 >l?fˠ6RC0(AC^Ֆήm;K_I͛U֋. G;4ȓtĔGN 1bi߿\yeXlKő#Gb1*Tj/X VDj( ([\%TK {el9~1?Ț7D61DdF_#}5;%dyGu]w/k z|"djNi;|7OTgɆE+.lSM5 P2kكu] /c 5U]fa2!,c4SEޥ2F5cBl]TQ3F2222FgBH˃v#&yv y3"md!U&o ,Sk&+0crQ=ldRe1d @H&Rrܤuh o,(9}#XN UF[}eklYlO+?8>_)i-{~܉dPƲ2 c Ș7p&g9j  INP}lY;q_N֨DC5em5_2l>F!cB2v>!q$0Nz%ar o oroBi^B֗-1d #>c:<'7J6&xd~Q&SlSf2I^svdWM\cRˤ?M[oߍ2F1d pbP~0Z>Nњ(ˏt׮%#SM~({>DZزp>QP<$cLg1@2AՖ1j(kl˝0tr6OyjCNʏ6 NB]FĚ3ck\o$?#cd -cs405 kM?"H:}$0E)dmbd՗XMY:=?51@2AՖNZD5cQ>d祱M Ci <(n)2A12SoMGOey`۔jaN>c/ Ӿa?z?5y1x[d 1 cPe*'NG;É5mxJqL8&uH$٦Tk,hl7iS26m'6G}+b\ :tȭ c@ƠzglS+6*^kئT{;l4%dLrbS g%1b2ى[0bl׮]IC2F1d d #2@Rd d e )@22P2 c@@C1 cT!eA1(&c\PBJ+eq6{^lyϏM}w5ׂold c cTt0(̼&|k#c@#֌MJ<8092~+6rw5^}?nߖond cg KT´FLbyz HQD_XXyÑ1 cTKW s,o"%{]x#c@2&aA YYGCωU cXa P9( dK8 c@udV2qtF7GnKֻ9x\OnKT MM6d!c2e1@@ƢHXyɘ_T2筑} W˞`Jٕ\v]&;,g%>ӍyUNI}3L ZJ2U o:ۊ!cU #]g*a!c*b*aVɜ2h 3]͜,fL ,NIiY7E< Y>1mkː1Ree>E&M<`|GLrA5ɵ5grE۠ۢ.0>A1&}d]fI<޺o1edI7Og]=M6i`ׇMvLEzL޲681Hu1d R׊54St%eI䋄q{߹>)g #URZ \7iѐZki˂}Ӑeg[ -R1:d݇CZ㲐}Jzڟ 2V4{"ʱBƐ1(Bƴ6Mܘ )_y+^l֔5B*)c=m٣Nmvz`rlEuo[wmil[+Olٓvzo1[}c<m-۝溆ز&Z!6o{iڟ&';S'JFyOD9V2Xt`sf/;L[dٰ/E_w}}kf{*)cԂlYJ_UlMȺ`ۼekʚٲ: Ak8ɐuƝ2_u9ׄlW:4_3ݖ|`ëSZ4{"ʱBƐ12MuDGHM;N%2vy+^k1RUep c!ztW5'Ym;-$v9?&خL5+ tk4_ xO1r1d *iE5 !NU NUֹ2іa!5c[}ek5Y%ݶ(e{&;i$d]Bj /?L|kS,S4{"ʱBƐ12w"My+jrM.ub]C1R%e}h_!hmuPx>]cvzI~;0RUǡ84%tkY;_fRӧ1r1d *(z1jH5cw|V\d#BYH`#W RBo{Ye]_ i!J縖s›}g|`(( CIGƾn{]dR)H<{grLϘ0ω{KM޳dv:ykX V־Q蘦8DYmNg:Z`c;ρ kYɘ+\wK'Չ ᡃz+c=X!cTRӑw]tdL?zN;H0:F~ikWv$3Hخn)' rܜ3M$i+"S7ϾkƷÓ_d  1IqǜIGƾa:嫶0~gҨ{#O)'ijgDٻq2Sro[ dîj}&usFʪdn- ֐iECVR x @ƪ%.ib2o4%oӎe>r@WȴL,j2+T1%0d N2H8 c*o  4wr7WV/$ whwXS2E¸@ƪE0·2M8`$Sc;ɼe~3ٺj;6U*cHXUt$!2F# 4aX{9,?id:G*D¸@ƪ}+a1j5=:N&sK$c]%JXC/^OSNyrd,;Q {X%>\^(AHxo4̞wlmپfoL7]/% ur)/Ze;w,q>˳UN;xN?tiаdƜ%i_߮\xaS[2^9Inʝǫ,,׍R{ L^fMDveLGUL$c |rd s|H9XBsˢZJ.bvټߞ*L1Rel173+ckvטm87%~4Sr)V%=*vSj2ͿSNyl1ޓnX۸e?z"~ԩ|sWK:uZJCJ]`xӝ>#W_}nLɋ\|(]L.Jw_^xrg#FO-5n zWdV"{ǯҵb'MFλqk+}oȂ|2d0:d2xknw7@fdy:s[ 2ԬY3.8X0n=sES Ҧ޴Si "?tyXx|ƛV˛˽XU3aw7@Ֆ2Qʴ[Ioٶ[|ᴚ%^yiAJ08+>翽+'qڟ'l+rkt3,}iY~~~e&ϑlVX_;*;2;svFn%im\3B[,'Wܐ1R%d,])+22}LYkiUVF2h(ϕj?RMՠASs=/>eWlr.XUl=*z~MOJy I;FHՒRW2Ve,2twn?~X:m֧Q\%(Қ-mRuUfLQos[;ܲG}\m'O>Lʘ9$2F%2ZB"cɤLύs mw7Y2J*cQiӋ^_(?Fn_6oG˭Z1Re/e*f;vdɘ_̹p[i_&asM:B%; >MGe,sTƢZ=ڱdI}RUVr2XU&L'xw&=#> Jd CH1tH(Q eW.w=jVd 1d KL~dF&m#>G]#)a[ۦM!g2Lf4ɳVhvNeub/Oےl C}Ev3Ydm23NaT!eLhX۸e?z"?ݎt^ײHGD#$>r׸/ɴϕl?#v:込OݻSKmF!-p+:hοnaF;ڱkρŶY;6irNzO;ǭN|`q>22VdLRz۝XSd1y,L6mbɃvzǯX OVґ®ɛ&PH=VϲFd1qiW}m},>wπtTyћG \Ck<_kJkx^˭]i u֬Q_ؼ=ays l>a\) cUVkkdy;s#Ml@7AE멝, yVe6*myEhˋVsf$cOZ2ĉc KVT2Vdܐ^R3uߐ1B[wNebYJBF'sTƢMGe,sTƢ#~9g,Z5$q~ujy+g'v+ oA1@4sxz)RV͘_z9&frA1@ dlU5gL: _&FRt 1 c ci ojw ӥ契=g~GϾfENV&כ\mrId c cX9lX7yfd|2k?o܍5jQäkx1mxy& 1 c ciMnykdw 6WT![6v䍘,sukf!3 _?nT70#vSkbȤ>2F1d 1dc{v+nح!˟2WVqkT ֖:2ߺRr S ‰kjkb"8u1 c c%Lkt@-3RnJ[SbbL7 6't*2KM9:jb6qsZH5 #c cVƼ1YQL,zRs2LlӌYnKt~L[ ք5vIWFۺ8BCP&o/&|mG͓1d 1j)c^ J֒Z..f:ʙZFNes1/YU0*^gZgIMMNs><&ϻ] !{ot4\dl>6oH\N c cRR扙5Өn|RRt_NQC{ܝsq~%#[&HL*KfEɎc)2@1^ץZ9yn$UL̛] KɮqrpT9g22d,M듟!$Qz ֊iEHWi22F /;{#E>.cJ߁]Qg,esɺCb{7N#;#c cA2@YanW:hr]&_7av$~ J6M11m}tMGMu;gH22F4!u{}Ѥ&wY^/G֞T@T& SGwt&j6OԾb׌=&ʡmSa1d 1d cwֱdX!&_pbd7bHcDt?ukô6M11m{xVL(I@@2 ތL+j.2ijTʮube-#yLj)M&L%lAn1m5bZ+V22d,5}B5d:ǹVʚ:2l./H< rրi>b uGMڰc=;k[+V"22d,z'V)S1kb\#E* *_Z 5a:jJSL&&1d d #XdNRd|bVY}#9ϦIu`Wt05Q%zM22d,#)K c^|iT%0d d #X!dg1d 1d cA1@1d d d CƐ1 c cd d d d c cAƐ1222F1d 1d cACC#22F1d c c222.@2d d # c c c cH Nd d #2FJQN%' rr|:22d #$bvGd^9_rh"9}9Õ!c cAƐ1R2S%OlXd`xn(Lq,.d22F1dTĞͤȖ.,~K6)[WkǺRSB6@@2DL"6eGLʌm"slWSB6qp<22d #gDv 4dJYwWv7@}&[V<%d96&&d%=w52!cAƐ1Dt cdDYCM FjTV?=tN?L.ą̼#d2 cCH;"kOd"ǜʮQCbdu5B?+)Y2)!ą̼R&tI;Lf2o2#d se Xvf<C!cA@[XDW&[ 7p77Wd:֎m s%;ov̼+P3YdrdɃ .1ɳ$γde ʲڜd&}L.*d]oa&;V!I?ldR(Lg1@ƒ科&׆ü" ~εl$>&Bsː41 mxIǟ3Yjݼ'LduXl'twb>b α{tL&L?dȁ_7 X2mdoˬqd#8sPX c/&C7_K[rAƢ,K(cw/MuX:{)ʣ3~!cA-^VL{nλ U)&8MMi&_8$U1hɍNK&hv?t4gE@}]3ns:UYoNG_VuF24e_{=V֊75-&\ [,rpشkc;YdWeeژemxi_!0y?(Km6iid T*,Җ鯠5엿+uIdlgPhj'l:*co{:˂=ee[Fgs(\+ʖ YWlGe_!a.I%vȫCMXezJPxU댺_ cyZǵvI-k1?X4Eϥ2 YgYXZIi%u *}ӛC9#2FҔ#;UY߱>`ZۥkDkM֋R/Lg2e2k'(w;X/"j۲#/(-vDYVw9fE y!˦s2=X u1@RSvl_r h i-& L:lkґse/HxiO:/L}]RD | 2F1dd c{7N}K"X_0:$Vvt#D<%> >'n6Q:N&sGSߑC\;dlr8'`yW3W95`DY5U[2:骐e9VN),<:2Xz\F-PCE&D5JS^nː('ŴyI/)eZN$>7}#2F2mG΅4BRO[+.&`v{t kҧZޕ ]d^j VL(X=ԍ˓}X[ztPt$mSX[ή1;$vDYvsπW}vzD 5~樐e9VN),<:2Xf?G;9iʄ({, yyȞO y('`K?%(|"ujs`qŐ㉌d #ؾMd1nP< ?92V vl}"'{c5d\qh=ӿʐуޗ):ʜI]e쾒=peLWMouG1|8D9=*l]D(~/Ș65 Lx4dtS %+Ψ29ۻw`p=ubuǑo):NlpV\GIGl(h#ֵSJS2ewfySt4> <:#2F2'ˎcean-9?M6!33V{T}cN! ˆ}w(W*B90+2wK2rdȏܱz;B;16(z!cH!cU{Dd0DL39(Tp~Q"Kۺ7z1GQbst0(;Ku!cH!cUfG~ 1D`{%V֊,Qgo(j1d |d,E2#22V d֩wM7#d߉3=6СBnj K-swdX]1E2#UQ)CƐ1@ƲVT/$'m)wfq /lqok+9_=.#[& '~*gyCdΣ%ӋJBH}2T>)CƐ1@Ʋvh{1mbz^O9<";[!yscz-E=}2̝~⡲}wۧ#)"cTL5RIHdϐ1d ,1|cMng9k8%dQL_ٽجQ ^kŎ_]v|Ryf!hV22e25Sܼln"F/R0J";6m'vwh_1<]5 P}# TL2F)#1@ƲV ˱l|OdǠPF!#"3#~(cX_Fɞ жr|L);( J2F1~*z~DƐ1@ƲVƎ.~^$O"zlkl6n1~h;y=e弁E-c(ȉ=2Pf$ C*!ceMJI_cl#?U92NY:2j{_ic>)d쾲f#cezёJ1d Ϗ2XՊ[/O-[Gd͌߻_SGwt$N"bZ+~Pu݁@1*a2T c cY#bگx;" Y򔑰Vrtwewe~nD^Ko꬙5LEL_[+Mu8{@ d*a2T2X؉EX^[ٳ{1CEK!UOU SQ1YELZ1;sq,!cH!ceɂˉ?CSڴ|[ۥ S!SZ|Vv&Z#Mkܑ1 C CfZ5ZiMc rKքyuxU´6L%L1d d @Ɛ1@ƲRƴްYEKisECLWI۾fL0OŠ?22F1d$1RR!SR)eZs9b c cAƐ1R\TT`*dZCECNIX22F1d'c: ֎liLcMӕ0d d #2F m_JR F30d d #2FGJ%+U20d d #2F*022F1d c c222!cA1@12:tЦ}m@ƪkz ld d Fėy|"(c!Ķ1d 1R2@U'KvN !cRT2Vd,9w722FJ$cQ%d:X:D2Hҕ0.>,cyw c c$-T¸@ƪkdzN !cH2vtl=%S ^|$ rʋ#cىJػ /S(GƐ1@ƪ2F9XU`ڵ)ʑ1d j)c;G\,?SU{.\QN92F1do1mڵkW@1N1)fCHhN 32>XU+])*!cq O2VՆ*2)Mӕ2>XUsRƻC#%t.DD cUUҕ22)5r!'2Uxw c ce,Ʌ|""cɤw722FLCƊJuR22Vhu' ea25EƐ1 ~JLy|.1)תkd #2e#. cHK[Έ 2F1@Ɛ1dEU{ 2R 2d 1d\#z>6grE`mV>NY_2vI]|Ne]VvR&;M&ZdcɃ!+$:vo1edI7ãv>>R|͓';^2m;jzirm9i}O/@˶LNrQ`2 s Ic&$9')$:&DQ{:a_CSdI@y-]T=QY&#2F497ϯ|ɏ@Ƃϥӭ|(31ɱa#y=–09dJKƂYP^L0gA{|kω빥 ḏ^|od #Ș6<<),u7&犲8I1ܚ`{,{{t Yp9ɘw|k5?Q2es5g'&gڟs-[c~sel^cQOb'jϹr{¶bd;NN {MX#cCH51j222!c cAƐ1 c cd d d 㜈d d # c c c c@@2d 1d 1d 1 c8R&Ѽ5jԐK-Cݻ}ҸqcU\|O˪U1@Ɛ1@Ɛ1@2`>"p^s"c!c!cPe,yO<)7n'nUW]lҤInY͚5׿]V<(#GK.}nCCCtՋ=nkVl3fȝw 222X5c6m׌]wu.rlʕ 2A3ֳgZKeǏGC#2V2Vvm9sM62lذ"tcǎ!c!cd +>ca4k̝wٲe c c@Kƾ2e x@XXm#AZKI͗oC+I O@ʘa mN֯_/r36m}Lo@yX0zY%cv!cPf2RVMC\ K3xRI@*kG?J&Mŋ1( +m4GƐ%c>m/.~Wd,lT7St"[ #,^yK/7T`ؙgQ|hr箖uJ˫spOu>mVa|i_w3۾"SYviF vJ~dIkn2E_oe<22EC9>77tԪU isI=[=wS^ݺEʾ"q5*RqiK$cQ C"eߦIRNm7ukyhƯJcgY<ȕ~wr+{Rv箐 ^u>{~y=ݲ;[vKs׭e~e1d,R~1~w>C<{|h6/xDkv%,!UA n{X~iɘh)6+V.ʚ>})~1d 1d,MZ?eͤE濼٥_bԚ.>(?^Ť/t /[8b[ִIe,v!cX|6Sص\run?|lߞ _׿Uݲk9m5S&5j >cm:۪۬$[w67zΕ19d d KL`JoǛ n-~&=D0c7+;lcBS;y"WV2e1d,eT$ի#5k֐]2ZovFOln՟4M_uezm2^~\ser~zZ2=nWݕ7+ܼeLqe22Ϙzoy̫6kqZRvMՌZ¦Ie1d,ezwsvxPBnXDBLMe:_iW+o]5폖pı4dLnkp]pgYd d Ko4ŷ~1^#j O>]? 2GsX؏A|>omsۅ!c)N*c}b-Sr;[uf%Vۧ/H~Sn312nd d +:>vyMd%nٔCdkT02Ca6N_K/? e1d,iTrtC탵}͘"Zh1L)>ݲpu1Y:_˚_NpTt-+G˴W&2+.IL1YJd +:dٱsT(f?ח-ZH랳16n}/wpM _.RiY>>ߋHȗonᎊx2"(+QK4r;_w7PkĎR k3l<qd +OzYDt:FTd d+o=v_ RPVΗhZzRG^xx}t9 m޹!73§}Ӑ1d"yTb?D2qIٸq'?q˯*d 1d A>Y*' rB*T^t>2#&:|"G/hA5.jHzo,>4j(}z_~gX[9o[S۬CkTۍ ce͞={N:<@+g};dnyz1@Ɛ1K7$$2E?vϫm۶u{!MyM6kƮ:d 1d 1 cQȱ#Gt'NP3CC#2[wynMGfr}<ծ][=\iӦ 6C#2w}7im>X cT"#2Vj*d3U6!c@Ʋk׺-M6-Xaak|T& BƐ1 c[o/>/|T.(Sʋ&vˮ@1d cXвeK"eʔ)O4}\C2ɘ=WXnyI7j$_Uc#__5<[?Ku}lbq-N:|wz{v.}j[خKwY*=}ivaSwi̓!cJ(cd j˘֌S) [fMw*g&Mֲy~6.?<󆎗Zjy\sP|&/}.y1d c!cP2(Ay_zYٷpk ٪+_ҲgD|[ۏKdrz#9x[ Zͣ5jZ6sH9dtE_<2F1@Ɛ1]Kʐ]C]O/]zں*22d 1d *\ҙWkK.ʏyewK˴*sh?0Ӧ< Hw4y1d c!cPd,X~MՌZhxe]|[fղm8Lw4YF cACRy-~b>c?zݲS;wx;}+6 O>j?ֲp"<2F1@Ɛ12M36N_o1)?{H<:7Ϸ} ) Kgd # cTjZuvr֙>[z0fG+v5-ck~|w~\yen?4mz(֤52d 1d *L2d 1d 1d 1 c cC2a@ c c c2d #22F1@Ɛ1@Ɛ1@Ɛ1@22d CƐ1@Ɛ1@Ɛ1@22d # c c c@@2d CƐ1@Ɛ1@Ɛ1@Ɛ1d رRBƐ12@)ɘJerd\oX,݀!cAC4dLJ%|9>" ˾dw2 C22%b"D6~ 9dǜnv]&sW2d 1d 2b"5]M>ӇȆieӌ5g+e{BȐ1d c!c5ǤI/#dE6$'?"&Ufɺ)iMB!cd 4eȎA6E0kK.k&FȐ1d c!c4<Z4&boj2̗bb_ىyߒ壻˒SeD C2-R]yㅌ2d87\bAa 0D%rrޓ"5Rdh16ce*e;!MBtdWYd:}ȨC*LNaSӗZˎq3U4gG?' rܜ3KNsG ,29lR`22l7+oc2B"|d w,39ar^v{m >ǣ&+BQozG4CƐ"4ʹ!x. Ic&$y95&MF4/ ;9;윬h4:f2I^xt;jT+*b3"! 1}scѱ>e\QkFd~$oxsEC#cXEKUm:&uM09ӤI@βiw#^mfƉEByv{ٿy2Em*O#9:XWjCuWM\cthjOe_\\oZl2y~.T~d cs꙼i ^{ޭ&evIz} [Gp_zx~#c2=F=7e~a,m ؃&Ln1/shYOY3;9\n y]o] cGmt(MiDWfl|6 LYylgjLT޴flr|m7/2Y0|#/@Ɛ %.acǎڬY̙#|{ߚ9sz\ݻw.]gC!?279d+o wՊS:dͪ>-GgI{ L1Pf) GL׌!cXE5&O|wݟk7u[)"*esg%Ces?GtdW_ CXHrl1Y`e+j/#!_3(q[9>c2]ƹ!sba~ǘ\(ARek$y4٩y"^<5*G*'Wlz)1;lΝG6l43ecu.F>"w٥L5Xf]d{)"c%crrr{VTl}D6l.p_k}+sڞlȶ Vڱт_]Er^4K\R~D=.sb}.V"c2}NKܐ91l7eHymb$9;:w&篭42t0uoǚbˌ|5I6&[~eZ6/䓿'=i֊ ]v' yCq3\$r3"2VEvm-ɖjMɜYe~gGVw "k`kjT3;`kDQ<({.r}rU)@Ɛ9ƹ!sbpyb"6r13`K?N9ONm3=r<+m6_xIdÿcVLkTŽ9^`_ȔO_@z#VlD^2 8*>6Q>{[ɍ :F T_TkY2#5=\'6BWUo"aKz{/+X{m7\I9 wp:c2m)4B]۵d[xc߬{uZ ؉&G2];W ;Eqnp%Uo!bXSZ1{Kt!(+c{82/Ǒ2w&WquMako$$77Wl:1?[,Qd ˜/RRƶ/qel׌߈,f@Ǝ<&r2{z^yU#s~MQez+c2vwqŲrl4rXJy%*_2yLȬގTseL_xjd> Ks;-xDb*ax(˰/RN- yZP ;[?vpHknymS([LGtʣӢ:*h!cɒ쮦X:}(EHi/G*r]FzHA22_R?"{E>]MGԩnEŝިu1ZMfʆ)247o^52-B#c3z:2?Ǖ/\K/=ڣG}hDd,[d, _HIŽ"=X2#c"{cm*~Ȣeϴdһ*>wT/\!cɗ'Am؂dq;5E<|weo!zd Cb0Eء%r ?70"3EϷc+mpG~l\)%ޔJckwTVJz2US1mLG?߅x}B2 Uƪ{טӂ*enJN,WYqd#dSrե1gss},Gw犱SdYnE-ܡkŘQMjuqΝ;޳gϽ2<: Y[h5cY3AN,|\dcEv}d>NdSuo#K&@#sBo%"WXJ02K+9ڃVX7}\xW8@eloB9VC^#E֕],9OUP5QN(ao1d,e"O1ߦ7\orɹ&gE{ Mc2^Xh%DS"U]E>yWds?[K#{iDY'jr쑱1 xPMQ)j}:+cLΌ 22 =uXoi"Hew/eɄnĒ*a:"mZ^v؆)ZCƲV8 7un(W^ylRz&VAUSF;1ome%@w<['\ ;8Y7u+Z:9HfF&:1=_vZ1){fo1d,hj07쐭;?kҿjm5ź)wb@CDme%@9{׮IŽrx.~+v%Om͝2sK٫ico1d,d,Q;Tϖ&C?؜۴6\ ӃxZk^rW_"OD.I뾽p馛;AfYrz/W]}\xEgc %eWoR[4 }t_]Mإd 1d/-cտ.2.9u\䖤i^*d*^,+*u%LG6ͬOʗtg-%豓kGfd⤙QZU\iFh2̝w}2# kOq;3O.2;Ҙ1d eLGvw1)1]#^*d*^+10 ʌH2J$rGoP9ꎊ釿!j1=]RZQ\ȦOȿg$j>v?^.sttDpʴ:2 =n?M{_qߍ4E5E.2ɒ1+,veuʗH22:e0W\=gkFֺ|hbŢi"X2S''׺]=mv֭kvTtO}ij!c2utLKGtʢF5az>eFi޼y2c1?qRt YR!K1{~ wv;QmdlˎCqyphK2)2@cɘVS ɘN5T":叶Fz9$ K7VMfbHrR~fb<9 ^'r)grOGVZm9#o?[-4M176NkkS݆NS˄)ߍyt/YC1dLKz %V 72}{j]?X0c$c,X0#pZ^9i Fhm?gu)dj c,c'F+mGDTS mٵ5`-[ŕ .-mCUk]^ X\{reclr4y'dLfJ]_#ݑs9Gm:oCGޟ/|K-Hƨ!c@Ҩ~.GTT 3 &) c"~xKDd,=C5Ed 1d c2PMd #XrHX5E"22F$X") Onnn'2_CƠKT5EQ2F5E81{^!c@~9UK_!cFZUS$TSH &ҁ2dC!cXX TSHWt !cF#e2)"c D1d  CƲ)RMjI2dsNʐ1d,kdj"@rd,A 1d C!cXpv*oS24pi=2H=>`- :NN{*ek~!cQzꅌN{ɘf֭XPsiOR"2eQ#{z&y/VXKƺu놌N{|/1iGLtt BƐ15#]2‘c\ӵbZzx% I6d F'N6q?_oO>CƲ3w+{#":Bknp:Je㍚nEy9^ͼi=5Ut$L%l@qAGMrI2X&-M?"cΰ-Z!]CdX)ӑ/X96يZ[ʲk|ވОy*` xjl[[$ȧ]Cd CRo`>#XFcxˀE-q~㍖]cSv}9߰dړۮٸl|bl]](MUŲwSȡBGLre,DL?2a2&,_ǟ>OF;j&GL:}>6^bWW{MFm]'{} cXr` }3)__ orm`?Чgp!c8B>1;'fZ"9Ԋe; )nsAF߹a䷇Nlir` tLȑ9>ŕlWmdɣvNF~lr[=r|p:(*M^nKúix>z Lw!c)@_D};1c%?[֦G{DZ/F[~2Fԓ1;YK&neEM;?r&ړۮQrk,9sU&Gw1#a'99ɶ70FCM~h{dX?o sWE }~8?y Cƒ 9?~}P"ZW g"Oϐ>CƠY0!Yk)5;G)wC{rC9Q=7ϕ0)2"ж֎ض7䃻EE|7smn&˜w=@Ɛ;"v=kEоű#;#㸏A"OOX#cKBn'阒 4ۉ:A~39OLkV5p6!cXJTlqNk~,\["w Rz1H52F1d cIaAtaLN maxQX ch@Ɛs&6Y~,\[CEAg_'3OGƐ1@Ɛ앱&Ot Fv7v]G:>7c{ ΥCƐϢǢڷhNPG?/F}^cd #XdL!Z KԐ }xHBUʹUY#}pcOt\pQ}t=  cXxεǢb[`MStkD}z}zE*222cYE-;k4CU(־dkxH3OGƐ1@Ɛ1@Ɛt1 c c c2d #22F1@Ɛ1@Ɛ1@Ɛ1@22d Cl1@Ɛ1@Ɛ1@22d # c c c@@2d À1@Ɛ1@Ɛ1@Ɛ1d #2F1d 1d c!c!c!cd d #2!c!c!cd d #2F1@Ɛ1@Ɛ1@22d #2!c!c!c!c2F1d c cACCC#22F1d CCC#22F1d c!c!cd d #2F1d CCCxE c cC22d 1d 1d 1d 1 c cCƐ1d 1d 1d 1 c cC222A1@1!c@Ɛ1d 1d 1d @@2d d # c c c c@@2!c c c c@@2d 1d 1d 1 c cC2222 c cC22d 1d 1d 1d C22d 1d 1d 1d 1 c cC222A1@1!cd d d C1@1!c@@ d 1d 1d 1d C22d 1d 1d 1d 1 c cC222A1@1!cd d d c cAƐ1 c c222AƐ1 c cd d d d c cAƐ1222F1d 1d cACC#22F1d c c222/ cC2BkR22Aƚ622AƐ1@@2d,>k3&!A6Yb;& e|> $d!&L\dh2dhTxC#2F2\n59c&#a.wrٶ4BF!\="U&œ8TyC#2F2\ƆYx¤I{ hYm MQ{M|mm۵ ?wd d0#cmmۦ8p2j c c c$d,T8yΩZaDc &wjnrm^pЎiO]&TDCmm ʏ c c c$d.'|uaDp2z+˵Q$?#S]f=TfΉ|@2Ԗx( ! J2!c:r5"Xd@۵&."pxUM7x0_lCm2xxS2d cA!ڠ2=o'rtDXP`TG0kN!<}ȩI{Q-Ωuam#Ϛ1d cA!2x2"c,2h#\ m_WD9 9[w&k9BKכ22FX3Ш+ag'6LSDb|{8^%DX@ Mש5c:Upﴮ{@cQ SRX\DƐ1@Ɛ1d Qe/a#cX2\eQȘV4ZQ+V9# wӫ)Z6-}?r3Mӿb̷w=*e_GR_Uʐ1d 1d &c$ C cʌMѬ[`P[X(؋VѴk[fF۟9C CJ22@e> CƐg Jsω>7‘1 cd(K>W(2F1d 1 e9p&2l3A8\^{^A1@2sF9ᕉd d \nr6 BI0s+#222 c@d c!cA1@# c2F1d 1@22!c!cd d CC1 c2F1@Ɛ1d c c2Is:r Jy@ ]d`~}'&ݢ) 262tп`L(FëRYު9ZdK..JS2hաUo(} %}y='*ԕbӦ*y.yE4-z8ZW)8OG^C7vD|R1 Ml 5i}x$W~աUPσ>*b"<-tEXtSoftwareby.blooddy.crypto.image.PNG24EncoderIENDB`emacs-epc-0.1.1/img/mm-methods.png0000644000175000017500000015243712177363221016554 0ustar dogslegdogslegPNG  IHDR?B* sRGB pHYs+tIMEɨiTXtCommentCreated with GIMPd.e IDATxy\S@B!\QVڪ-*qz˭JEkJUP. .(. ( 0p?N{~r})Lfyy3g (?H!/TaT0 Fxbq]](_*G0"]v&1ƝG7<0qۤYGϧ4o)RW;hlv'qx-z;M٬.=}ʭWV\R`Yjo)ϥog }Ty~+‚g_, X?qG:`>卻+_I$m\do u44 q23+*--R臒G.W*ijMDu4{~zДV)ޫIRo9X>>%nr#; 45,VDnN$W4mns^w[[,?BL{ofW HN뺆Jq}cɋ2!Lbڭ*M;3*D啯;X1 2X65\J*蹼R$moq9`0D@~R鹺nAvjwj[TV )Du4Ujb8z3T7DQ}ʭdP"lh=khrTݩ\U[~E_45xB测gv'JlgI<-EnzCUVD=B`m`mI!B3s9H`a21[1/d<!d;0QZfuBJɔ]CU^JY!$9~!M_l?E`ʤIoKk"hkKw4픫YU[l[Jjj!RZ5C,KM0VTR鹺ujg/*%?owSoM;덦K{uֹ@ A#W}CӕwD@3þR!dea{-bbB#/M9/jکZ z]bmJ|@ћ7rւR33'6p Թ`6B~/bM*m :ERi;|xuB^.QXq?'xQ M;g)`EikibVXq'G>nY]SrhiH|~(jU_\J\ӷ8_˱ƃoTzx|TvֳDVhen1ġ=#+G6?ax1&AlHڊJ*y]OS~ֻ겿DQ}[bqUty\2Wߗ<BU BrYlr鎼)G.TJ=6uBHI1R/'uQ^ukq=@OGOGڄa {+^Ո͍Ǎr'com6mύ;KʫTF:* B(`:[MXV.x>_V5UU+sc?ot+iMu foۂVIO,5ہ~]IvRq#r$;!ahf{9**PG뿩g*OU\=3*x̾S`enkw;E`WTZ%S&g$p?D"* z@@77@@* 7PDoۏpooo(`_~& oܸVQ6.п Pr>z+++;ydDggaXss3Xy{afc0 u-[K1 1blx (8av]en统g?r] 0 SSSꫯb1o:::)yfffff&U;ܼyӠ x?HJJ~O::yϣ7@ J333o޼9n82z211;vP(D[ð,/***$44ۧmmmׯ700`cƌyeaa6svܸq;w쐘\'OB}ٜ9s|~rrr@@ann.\pԩÇO6M6z[nٳO8agg.]8Bnxћ;%1 #g͚plmm###MLLL޽{/޽Kܝs8-- r<ɸ'ћӏ=zر'|"z۳gOll#Gnݺehhܼf͚UV9;;QڶmCCC˗>|NLLTUU AEFFܹ3;;{ٲei… O:UTT$aܹsDKKyO>'>>J.M0a„ 999dzjjjDDD\\ܕ+Wweb=;~wSS1cƬ_+V??,, !D3믻vڷoÇO8 Y\]]SRRhoEE;|ŋGE_ bW=%66vљF|rxx_|㸩cLjl ***)))H77E ɓ'8~UPSS˗/ Fbb"G$q8CI$o>"=55ON"b+//XbQ;dKHH`0$..⨨(+++TtEՋM[Ç455رH_vvqq1q[nqyӦMDbtt 9޿!TUUՃq'tbfSܹ*))!SM6e*Z[[;>Zݧl'zG}fE"QiiiHHϟ?/0# ͕J>>>DmmmGGǼXhW^/֒)"hŊzzz3f̐J~333MMM qϥgr{ɓ'MLLLIydaaAj*А!C\\\4hA544ДjeerJ@444Q_PFFƄ TUUYލX,B諯g0D&JBdpۍ1 kkk#R*׮]+ipppeeell͛7\O9uԸq㈁9nܸUVUVVwȦNamm9r]zPi/;KKܸ#GDFFӧS_~7"z[d_bXzzz<R 2Nvvv۷okkksrrݍ1 +..&`rʽ{=e˖yzzi o,jjjt &.z6.iӦm۶Y@3g~Tp׮]>oe퍄X{#͛7kjjJ҇y\]]bbb+**|||+O2%,,,&&FCCᄈ6m_ttS||Ç/rÆ l0 366>sСC###9\~s)MMDA>}zr8^WW@xVVVL&!4x`⇷o&_ٻNlذy!}5?ۆ ¶l!2ѣ7n:|2TˡC>@>B5JII!_8455.\H.xxx={o? m̙b޼yĶ6onooommcPDD=6Ɲyݻ6lG}`0:=fĉ~˗w޽o>zzP8qĩS=@b1![]] }WO>uttk``2_L,WUUݽ{ԩSd#LLLB .~AjllD=}T$#8^"sx<555V C:C:_P~AFsSYY9fT] xj?S l6l] 7J9 xqWQ++{ KLڅx AW RFBƍ`e_ˏ%譬}Ҹ_CQKkk7N#sp [^lɆZ`l+#^xqjRXX[KKKgϞ}'&&&˗/2?=ׯ_hnnYB ]GoÞ>i\ɋ/+%IբKJ?}rٽؒ_L߻n Ϟ='S 믿~۱cRuҊv 9}tJJJHHH>p/e*s.זVFw2K;wOť[_xۺ-[1SY1S]bfi3j(_#qsÿҴw2sJN󣉛`υ"Hl9t9gTLfI[曦jkk[mPW>כ~yN\;GOg=)x_5kɔ>hmذRGGg=BEDDhhhlڴKpߺu 355ݷoy1wP;;4'666'O?ON:uf"7oެcaaѡꦦ&D\rq<::dovҥR}5>@Y7tt :EdqAqAӈ_miɟMdƦ&Оbqsg 6ލ#"[{ko 6g[4!4v-6?B#=|[̚S5Mg'N?aynٲ믿9s&rbٜW>z?p9UUI&֮ZÇ˗/wrrz s8˝uܻw>={6CR$ 쏧~7˗!;?q䀏_۟|#}rGBٺEfֽ1ýB dl6@w$gLUOO&765Mx zС!կ?:ӏ<%&2ޑ#=\[zymmmL_J+Wp˗]]]epqq] UUU "ruv&ਫ藖jkKCV;:]۟!jbM{'߹Ĥ.&D:B9XY D8X;XRm--G["zE+"u 2cbTڦK!!!'OLII0D\\V(U%g'N^\Z:{TUTT{Ʊ 454-T{7Bd aD"U+WUVݹfֽ((]tiJJʿĪ,qDDT"++G׿_˱{c#iw򪈀b2ooE^bPWW rrroa#~ ]qu rߎ;9Í :g/*zY>nifJpB ÝlvVVbqyCPՋxtq152q\iGGJJJrrY:|`0,Yo>bdW'YYYķ_ nnnGuuWrϟ刈;wvPPPP]]=dȐhjj[ȠA4)==-)^### Þ??ܹse:$9@s] +,93]F͙?ߟ6qN^>ۍzےͻhjp}~xe˖,Zx[_|ӧOB4 T֫5jԨ2Ql'N8'#>ho>A<@Gg(_UU'e_JI:vyn?tHܰzU糷uDOg:?JW<_([QO@C1{|xI<^ʱ83ecwyLrJwV;{+W\l#BBBAAA|>ҥKF۵k1}޽ L4`W ^^^AAA...'N ׯ_cǎÇzܹ?c3gμw`^u.**iU~z駪*@pU?x ---%%B75>bVnnnnuu_=wԼ3~Lt 7s?-! ˵KGRi-aÆ}+WV·dp8/PwUgbvKC>χn__C~8EKRir7v)i0Go88Nx] 7* 7n(`_~(9mذ0 lf'OoKqgfw&Dm)ՇȵKzz7rO11r޼yӧO񉏏'ZbܔFsgs9` @;fl \o*&&&&%%a6w\К5kWZ:4Pgݺuϟ߱cnjj:f̘@_u׮]{m۶544$%%9::I{쉍=rȭ[ } TMQ=ȥgz޾}{yy3gF'пJ?6P7B*..⨨(+++TtEĂJcc#N6Z۹P:tP( ^Rv@BBH$H77O8;;oڴH除uuuҩ'Onooqիĥ㦦ǎ#4447z=(Bz&LJ80̳ggrJ8~-44Ν;d"{xxX]cݙYJMMߨ1zaΟ۷HOMM%AEEgUU1]eddL0AUUU6177W*z;k[p'4hjㆆPmm Ν벊|bCN/kH$Zbތ3Ri[[M9 ϐ!C\\\z&WHHHkkʕ+eoQɋ0 !B"4$$حells|???%DSKKKi WOk>s9~xFFưa:z_~;ռruu%ǻQii)\,+xjdvV%x zQyu򣓓S/އr)l6yvAWVVƚܼyS\Okkd]]]ȑ#w3,--sss9I܁W.X2QOO&WToM%Wo~LxxxRRΧO>}ڵӦ8ީ466c BUgPW_9sѣG111ڛݵk:'"n߾M|qww)GCC< `0|d {@+e˖yzzO]]]+"&))ȨǢqM6m۶F^*x<^aa T==sQQQii܋Տ5//+WS{\r޽DF?;c=eϯrqq^ccc Êl ~uօ +kŘ]Æ 7oB(++kժUSL שּׂMzEcccEE! UU>}zo^fMAUBȥXa3g)WW׼ uuurIb3bBBq]񬬬Z|򉊊J|||G>}b777ߺu|]4,Yd͚RÇTP3B(;;[(խZ*00͍?vq f~~~ϟ'|6l n;6B޽{999b8<<חXǨyEB'󋎎vrr?|rˏ;tƦjX6r|>܁XSS1cƌJ"߿CBgϞ1byzzv7 a݉'8tҰ04)HMM-J.e۝Jed{{{5zhFˍ7u;SJ?/?z!٥ׯg2\.wԩRٷyL&SGGqF_Q=oٲޞfkkk/Y?˕gX,ؽ{S WUw1Em@Lŋ`f;/Q?T~\ՕfO4)33ܹcggEn:zy*'O$>YZZكzq?i3:OZм|2IeeD",))!~ȝIIInnnQgzO-=[TIM{kkk[[۷Ϛ5 !tƍY`V{|򖕕 zI"txA[[ l1c׭[7{'NKgrGEEhii;V(Ċ1ח-n_|EìY8mZZZE]f Φj?gϞ?]v-MTiBys433KNNR^* Əptooor`hh}U=/Ңr w|]q+zPqqw)ƍ۹sgw\J.Rّ&zc,,,fΜIf.,,oBCCR2o4'e߿bX,*7qŢE,,,N:uڵ===Hڵk ޽{-&yEkk봴4P_XZZ+,K%K믿1crss7տ۪0;;;$O_8sNs "]n{ZZZtuu7oLB=||mgIu"@ r%%%֭ 4RQ^^^zƍ"ڵk7oq:??ժUA"7?;J^*^/{ԻwM hii͚5͛SNuttoqqq:::|ʕ+ӧGDDtw] N:555pDTj~PwӯEoIM6lиp8ԩS 8T o4r)*\?KcG*LUƍ?qF$su^^'M$k~piMMP($C BPի/_2D"H$p8"7mmboݺqSScǎ ***)))4yӦM666do&&.jIȮFÇX|,_Y֮];d'r˧jv@4yoCCٳgibvvv4y y[dd[wTvA+ɓqz*9 oqqq<O,GEEYYYIҥK.ZK}{=(jw@0aTJL#L&j݄j|uqыzq/ܹsL$OXb.Nh/͸;*ODGFo8WTTyzzVUU)UB3qnnnࠣ+J}||lڎyyyG''' ^UD"QiiiHHqwظ4U2$991!!ŅjРA!ЀՔܹsdy޽{رD M{ϟOvǏ'hʧs;{O*\]]򍌌JKK!##c„ E"ъ+f̘!JhQ(?4vW^OHHHkkʕ+eogS !aB!iHi߶z`w???B"Bd^(:?(4_uqыzPq1gΜǏgdd 6W_jkkoB?4rE}}biii8>\,+ύӞJtv20H]]W%O.GдNNNE9r׮]MԼ~:Ivˍ/lː!C~7NjgϞefs%ҙɔ;T+++cccMLLn޼)@. 姑WQmXZZ9r%22r4z-\(N7`~@l\X6.Óo?v>~k׮%6~ wK(T;݉E*Μ9ѣCIzhTNN;U~===WXXh)M?ÇE"QMMMRR} 8<~xyyy 8̟?رcG366@~~~UUy9Y^]yNILL\liB8N]]]+$vAqM6m۶FoC^TTTZZ*w1COSa\~ާq7T+##ʕ+AAAMMMʕ+KDl~\=[x۶ IDATr?BU1aD?ܺu… :=Y{3772eJXXXLLw}gmm=m4,Yd͚RÇ/^466VTT8^PP`2uuu D񬬬L1ɓ'G+22=5k~ǟ~˒_ѫ!t޽Xz4jÆ 7oB(++kժU9sfСJpuuˋQWW/W?T i'wh^ӧY,qss[_.=(jwPvvP([jU``x׏B*4|W^Cߎ k@??$_~ ~G\J.UH|>/::)>>òu˗/?vXRR7R6hZ w#RSS;l И1cFee%Ĉ577^ؘd# q_xqѣG.H߿갑SWN'x. R >d2/^LJ^\.'w&''۳XѣG72dwqnܸx8Ą%JJ?TRٝ!ٝ3L.;uT-Q]n'Nq|ҥaaa]AQoٲޞfkkk/YJT~4bq@@ݻeZ Zf BxtK@5%Pʎ곸ՕfO4)33ם;w}i&uuo/w;`hh~Goܲe(Jqq1a#F SS]JJJ%I+.].{h U~ KiŋyyyLm .s\DTn:OOA'WC_~p$ݻw/BhݺuEDDdeeaQQQJOOx\.ɓ'1 ́޽K7nΝ_f3vQ?vܹܿs555̒ɟDEEhii;V(Ǐp8ZZZAAAD 0 cnjjH$^ @_n8GEEYXX͜9\XX7߄JRT_~m򋮮srrRSS޽xuuu~~U8ڵk7oOuz?X a .N:`0qݢE,,,N:uڵ===HDc*"\nxxxIIɺu|~CC?uZZP(/,--|~``իWoܸCSSSSTTw^EV~bB KY,m槩wƍǏٸq#9'1//o&M"Z~_PsN#"":222͍脄A&7?UFS/RBCC ܹC&{xxX/_d0D6Hp:M;v?Dᆆ55g8njjz12]EE%%%˗ٵʭ㲣F(fee!:$ BS+Hx<޾}T)nnnD'~A_譋;!!!VVV+W$UD+Vpvvӛ1cT*mkk~h̙3Æ իZcnnT*!>jkk;::np8##RHTZZ|q{{B&LPUUN|>!dnnࠣC [PP ~h``b1t3/_PNEfiiyl޼>pppfffll|g0۷o{?~ Od zUw.***DbccO>A1n@U/C3gwzs_Ϟ=1bLIIA=@_/N%wݝJ|EDijE5553f̨#Tv|>djkk/^TͫW666f2:::~~~dQ^^^\.;[;OPH|E\ʶ*?UŮl6{ҤIۏy;;'OVh/_/S X,&ߕ[]] W0J]R):;;ϛ7GUr[[Ʉ ]>ଅ~O@;o ~/?z 7n(`_~(9 Doiii5776l0 06 h(++;yR5iCzqqɓuttx<^XXXߛ8>Ë p(..0lĈds`hhK#/Ёݽ{L7n޹G5q !4(h^P)UXx^ezx)^%SzGv[)ת=QZx1,(6p\0u2AԠ̞}{}A DǓ񧴶B 222\\\lmm,XP(Eq\[[ۈvXZZ:99 ʺG C|9;wH$:455ɩ&55{kӦMnnngΜ)++ sttTTx;y򤃃Å jjjd2YUU /&Nxp8g)H$xR)11 .$ʲrkBCCQ'zyyUUU]~f_t.~nnΝ;_~bŊd^'%%ŋ9=s BHE"sjP?~>>>^,?E2n|(--U(֭swwGz Wgfft}uqp0 377G 9$IM:uɒ%&zh4|>رcR&Z=|bpJrqzaOirrGgg~V0aBVV yFy결88,K NT[o޽{|Z1m4NuM6 H$7oޤ)/۶m7f=;’ЍtbTٕ+WH s Euu5a BP `tvV__߀6ޘVNjЕZN.켼`?mڴ8jٺCN40Jm6Xj*NjQYfQ{/O2́{yy9mk׮-((zw'Os=0>*9::0r#ɔ OOOޞ.>]tvsii)IsQզ#n& }G],"p5pwwMOO}7Z$0E~Xr<++맟~b8deeeaa1(R)ڿ}Z+V0a5ގ*[VVܻ֭woʕn._:;Gsݹs>z8+JtYXX~ kƍ貣wށE$L&۵k׾}(yףϵmmm$I}'OFe E```ee˗#""zzzeXqqqGE rϢ􅣣#ollt95]tv޽{7nܘ9S!A8p`֬Yyyy999(uٲe111ֻwya={ׯ {?.((|2~nmm%I0777-gʕfffyyyh"FVK`d8;w.))IKKcNqG(VVV\pA RRR񩫫 mmm lP_lٲ%==&88Xݾ};220TaXuu5iìB`tva:.666??x&'l3Jrɒ%r{=22zժU==/rwjRRx˗/G'Hlll|V\yEJ^P(\]]1 #9@Ͷۼy3yJJJfΜp͛wQ&$$̝;wh:D"$322}$nP_ Paaa?nر0tY L/z{{o.l}HHTQQQ`` #:L&/]||윛7o »w/\VSJ}qpp0իW͛3N#G|駿H$b)>3 Nb^n _}jl1}z$xax? |ax~r<XPTL z߰8cl8kzӧSyư׭տcE"Q__ߠpRtR{{{>3l|fJKKq}7Tz޽G>{R_}՞={Syư׭2np;w.".=&wlijjjiih4C:JWTT;02! GGG]yL/WK.M>rZ%ry``ŋx<~N8uT@@HEwwwNND"1f$ɴ4.A&##v b؜744,ZFDD`xrrruu5V+**fϞy<^hhݻw_'N.͔Ù9sIvX,?~?2eJII s'&&~GN Alݺ}C`?pڵAM88q""" YpEjIIIΖGg:}a==cii4(랞F3W KM իWSw)Ht:)ɓ'.\PSS#ɪPÇ=<effv+.~llP(Z4__ߡzR,KѼ֛Ah &deeKL<7spWW?!!NT[o޽{|Z1m4NuM61C( Ho޼IR^mFYo>dXEEE(Jr t3=0T+j ò BߍC4 ?vlmm hkk3)miӦšiU T*Ussstt4M%߿OCŋpFb۶mbqժU:Nվcr'Mfrss^3ğ2e B_AD+ P]o۷#}1Q믿"ΝsNff&7q'Hd2ٮ]ۇ*K!P(,++3/.9(b m$>Ӏɓ'ٚWr8ףϵmmmƶ5E```ee˗#""zzzjϸG" Zݸq訩ywﳌϔ=RD q޽7n)0ٳg9@ ~:ux˖-666:ۑoݭ$I644`fnnN_*EGGGEEaV]]V0 񩫫 mmm fn@ 8wo]RRFݢ+]xyJ ʅ {?.((|\,//o$.~ǚZ-J{ ]+/c!@! +++CBB.\wÆ )))huٲe111ֻwտA}a=`6!"$${Ⱦ IDATfџ/..={ BRRx˗/G'VHݾ}@ `!!!(<44tP !> xAP;nġ̜9̛7mCC>^vɥ߶Zrŋm+ar|$`vvv7o:]'$$̝;wD$Ifdd$Inݺ5&&pjaj:,,СCЭ;v`ΜGFFX[[ZѣGӿt30`_hP&X;DTXZZ.YD.S͛BݻƶWԷRjkk?~ o0&ry ȑ#~/"c⨨> `zA yVecR2dž^^^h/t{53L^k'LlzSu:#0+9Ʌ4^K!cBQ^^R15@.h`a8K_^$ǚ?xLR\t=Ϗy~8?ƅ^xF3{W_}gϞWyS*8ϝ; ŔWqtj*￯wZ2! L2xb__ǃ'%rҥӧSIr\. 52دt:]EEũSƼvwwH$N^`za7bTxJJ 㖖NNN⧥!Gn544,ZFDD@Vlii9Y`͚5\.wƌedd.X@P0䛘xrrruu5ءO8QUUE,\lQ|b?c)SB. ðF3& Kןw)Ht:k@/zijjSAAAMMMjj*W{{{=psT*}||,--,Y"˱BCCP(HLJJrrrb<o (00Adloo^jգGݎI?ah ]xooͶ aΗ:Q9v/gHTaaaߝnر0tm:D" l͛Ne\IEP&闇.>]t͛Bݻq}‹ /Vo>~888`L ft:rK(>ZfCgaKK?`z/z7VNso$I0@.h֛B(// a 0qFd)ʥK<8}x~KIIqqKKKŰ~zH7~HR{{{^_zlRaE|W{1I{=zuxޔJ%s7)vB^F3~H5«*e***N:0GAyҥӧSIr\. 5xb__a?4(srr$ ^`zaF3"... ,P(%&&8\]]?ohhXh˵h4͜9bLG&%%9;;[ZZΟ?fND֭[8?~8//?\v-A%%%aaa'N^V֬Yrg̘QZZKdZZr''N8QUUE,\lfyb͔)SJJJ JH!C|$faazj*rccΝ;%Nqq B/09=~8p8Ç=<dXEEERRqQXo|>_VgddL6MmݺuӦM(~dd$&ILNN|OVH$7oޤ)o۶mR1(/:+(]]]ϟg't!| h|cPL&o$In`|Yo^^x֛1OOOpiooRѮ2@000p}*++/^lnn>Vpp0| Þ&Mjnnf' a.>] ja燳sii)IsQհ8kFgeeS'lEYf1j1 1(y^[[z{{%%%|Riqq92+SEc>#owwC?1?I_)㛎sݹs'33K@/Vza4_uttzX[[S&vgg砿²gb j ƍ貣waHں~93'Hd2ٮ]bXYYy刈"wQd2Ay=K}ڼaL@ q\Thwލ7 YSkأۖ-[mllu:۷###-֠ +++TaXuuu||-lٲLkkݻw{xx|oݭ$I644`fnno={O>qƎ;F|gϞp8P(  pႃ 6i^(?ԨjTo ]]y̗ ̚5+///''G,666??x٠^xéa2L?wfۇMMME]\\]EEE< j'; icccmmjժGAU($Isp8W\ZDO֭[cbbʙfy<щv$IZv!ݩJ"aA+`vvv7o:C?1(G:yCt*JKK%Krԛ7o »wVhS /F/S ZR[[`x1)nqx⨨>l6VezaX%R?yn^Ƌ^ `w\}jGW_}gHSSSKKFTBi ]| ^I[&A1c=]8\tis\.o.^u:]EEũSF(srr$ t'@/^0}0[bbG}t)PH֭[I0L&%%9;;[ZZΟ?Y l޼vʔ)%%%(b|>ㅆ޽{аh".kkk14;vq֭=qDDDDUUpƒfP^8'''WWWOimmeȎ$ɴ4>LǏmllaXFF   Ӡ{zz4͠07/rfaazj*rccΝ;%Nq L]/09MHH8qWUUlKHܴiۙ3gU*3-pH|rLvZ H,++ʒ׮] G[ZZzjyyyff& _bņ H2 bvoܸ.;::jjjyg,ʽ{H,**O&OIPY;.((hii+I```ee˗#""Z,+..ѣ_2ˋvvv>>uuuAAAVVVt={ׯS1 kjjjT* 0l˖-666:ۑ rd'o0_ BBB80k֬ײٳg ÞZ;wr푑666֫Vz  B`߳g̙3---lNII̙39μy;LoaQQQ`` #I9%I믿0oy$V:;ڱcal谷ϡ$%%999l|rt N%fm޼T۷ol6>$$Jʠ IEP&闇.>]Jrɒ%rW7o wޅ^0^xpZM}+cR0EӱX[BXgFjl6:@/ ^^=/}5 x `'r 8{#IR/r@/@ Eyy9H [o3ӧO09X|)))88nii^~H$a|RtR{{{>zJ(9tmy^x}:n(--qU޾={tð۷orxa6z|w7@?\.a]]]AxzzWWWOOOOOO{{ZNyyyՍŋ\.q( [oaؓ'OՔ)S$ILy+֯_O >>>5iҤPFڵk *++ݾ}ɓ';::j6v`Bߍ?j5Î@ggR$̙VMGܣ<ѣ,k׮/ZG h-9++ߟ D;mll~*p֬YcXg8j.66`\F<<_LF >/cRNj-??BBBmmmc[˗/GDD b=,6f=H7>5n;n5 ]qR޽{7nܘgj~ǧ.333445((J ;w.))IKKӏ-BaoJdCCannn[lIOO toߎd( ]:Fuٲe111ֻw;_tttTTa83(9lgvC { ϟ6mʕ+&gϞp8qweHǨ~nF>/^N ֮]cǎ#G|ǚZ-J/ž2<<<$$… h=bÆ )))hYҍFƎt㼱zߨ ̚5+///''G.666??x&'aO-0 XRR2sL3o< vٳg̙vvv[ly J!44tP $۷ 6moo¼M.H$O?aFm0D/2lHkkUV=zhܝ-*** xAP;Cbڙ6o<>s+ar|ؚ&%%999l|rtBtr7دrssE":$u֘am˳|0 `vvv7o c_S$juXXءCO-[;v0 9eփtQyN;~*JKK%Krj|yP({o*gNS~JMssӧOKR*7 ~qcR? ΎQV2bx t̆fyFyQSSikkvvttzyyڍ?{9ݻvڹs'tl =fotm$3̞=_<<a„ӧOd31^?&<|իd2IA^&`l30z>krO Eyy9H [o\׋D^+W~';cT*.]joocbb^LSqY:}![&/^xaT}u:]EEũSLFAǡ\ti*\._1=ˠOɑH$ ̽Ǐ666SL)))A቉}ѩSB!A[nEъH^^އ~vZ JJJ&NX[[aXEEٳ|> {.C?~W*3f$$$;v ֭[2##v ðQo͚5k\3JKK[===fZmRRaаh".kkk gbb"SZ[[ڟrvvv~'NNNTӟFILKKCY' 0F#Hx<̙3+**E?4ѵ͛mmm/ @'NDDDTUUQ! ._VgddL6MmݺuӦMRXXb[1ccc) [oQԃjzp8Tɓ'S233r(ҧӲ"(22FB  BP>dXEEE(Jr r oOn;CPNXk.~ӧ;&''{xxtvv H$ZtIW^:E?FH$ZxNC#F]j%͛7@Km(Y ;dܦ/^4ScOF;e2Fdkko@@@[[IYoßZA?\IYܨ5xDL6JJڶmX,vtt\jNj _XAA/a8c;=yAcC!!!$e0z]o۷22ѣGYYY...׮]_r6[o$IRGp~?k,-h</++kݺu(EyR0 ERT6G؞PN }ݿk wwo;---))/ml=~n~ Jȑ^WX@61v6h?0cFB/&Y8wܝ;w233M[}}}[[ۨU ꪢǏ"hdX֯_o>ss@Y,1)((qё766TƦ젷7n̛7$&&2̨r 8 ǓH$dC,߱tݻlyt"00rѢEyyyfUX,V\\ܡCW$qlAa3j7|JðAܻwoZZZ^^޲eLJ[o?cMMZJvP~V$0 V?@ 8wo]RR6]vǎG/G.ӧOQ.[lIOO toߎEkۃebbb233w0cEGGGEEaV]]ܞ>>>uuuAAAVVVt ֋hS`aa!z/ӦMcZrY^^ް.Ξ=pAooשː|GG^~cð[n)p___~eTx !!!.\@!6lHIIAz8fMg?Ѝct㼱z֨ ̚5+///''G.666??x&'aO-f6oLENHH;wmtW("}IܺukLL I%%%3gp8C [h ]?ydm}ԙS$k þ${{{o.l}HH7X_n I?aԆGB#]^jգGYTTvҵ'"55vqqA;Ջ7od޹s0\>l{&%%999l|rtBtlPƖv޳g̙3---lBu9gS$juXXءCO-[;v0 9e;C1cm:a38ӍtSTXZZ.YD.S͛7BݻwmS9sJiG?sBB8MlRSR ð_~eV:Kh0||`95ݻW\\d++;w_U=zh=zTQQFwttzyyڍ20elɖmp_3f߿͚5O0#4fj$ZeƊ{WzTc01@.h?dP(A*ozLYoJrҥ|>?&&9N>=b@ `޽S ǐ1ɓQ!-ZR0}/׋D't)--q֯_R72Gꫯ>1uT-dUUUSSӶm@^/Rܹs_)oH755h4't_N8ug&^ȉ'b6^.]>}:J.[/x'EwwwNND"1~G վ... ,P(%&&8\]]M-굶 -rT:F"x3gVTTS,5kp3fRJ[pq^^އ~vZ JJJ&NX[[ l޼vʔ)%%% rG裏N:% غu3zFlj'""" 1!ߴ̃޺uO?՟ ,))/Trӂi{{B0,;;[P( v(ZWWٕ+WeZZ \BK $yUjDDfww77\>V322Mnݺi&:u6}y'$$)Jt7ܻw 7 .Q<'j%͛7@Km~X"MRql=NGrrGggP}Dy결`#tv>1._F;e2 ;7 m9%-zzzzzz۫Th gHrCo8arNxyzx byʔ)h }uZ A!Hr5k-A/kTVV }'OvttZN.켼Fottti+&MĬG섑'tmhhP ;KKKI3gZ6q+Zʢ^0>cǎ988DDD۷oǣEr<**JӅ/YD&%&&&$$`g^5kTUU}7o===T<"f;99۷l0&[4 .?]Z6 >66{vB[[H${L Z`lv)((n6??1"FS/}{$IBBo%L&6=&&&C]]]_N__lfو0 lb۠y毖e˖aoF?|ðCΜ'$i`#]@\>zh4MMM*7`` 8㸽=>pMmҮ]btVqFggg@4m~f 8 Lv޽B X8Z-aaaTD"f;w?~ܦȃ?~l4zO?l9]W<1dA  3x\|7ޠ&jmmmmm ߨf||[&χ}믿2G8p@"n۶l6L٭AILLXrejj*:܍aXooo^y;v쨬T*AAAS jl6NNN~~~s>CW*|EP |}}х}t6 W744$o iݵ022bgged й`j+?y򤸸ãY+[V;w?///++ H&}O:K֭[322MсpxD8~Ρ,Е{}4dKܹ#4 OU x[xt!I> Y|9u_Ÿ\RB8rȉ'0  8wuu eee;"44TT^z5..ntttRZXXFl͛7AJvZ xDH$jll*\7Z-vʕI,((سgOyyHlnݺR L&Ncǎ988DDD۷o3G(^t魷ު˛vcֿTUUq8P866v ;$o*++^JȈN#I0//_O$)ʘj˥wA<==7mڔ$y<ѣG}||lقrtvv(N΀=N"${zzz%K|555T}FEEMjwF+v)I!::ԩSw-E0 s:00m˕梇xxxX 0]< q644ABBa=tjTlkkDwޝi`V#"",H<'Ljsij<ׯ;p%,`6^qd29sfzeM{!WKnFoΜco$IZ>h4MSSvq ظ7z{ ( 8A%HN< B\vҵZƍARRҴ$m6Ǐï @?0j8Yf;G<~h4N~r2+ 8s R._ImmmE_555Z[&̐7|3009[wwrPdrwwwAw.oo {xx8::{Ldz;::gt:e$x>Jonn^n@ QQQwe7CӧN3n?3h>ϋD" g [o dF趇{ѕKtj=o߾}ǎAGGG/]TVk_2RZ#6okooR֯_W_Mfu=8KH$EEE;wtppXbE}},>a9998ۻM*ztth4NzC~ӞÇKRl<Ǐ ~zSS\.G{xbccctt^G-Y?KJJD|gVӿkF'x{{ŬՕ&H1L p>\77JJ=%***88ެgPRRR]]R E{{/Dy`ׇ]t_{{7l˗]?)^tm?]?Zeee·NMM |֭[ҵz`'b{KK͛`IlsӜWSSޢyN/^b bsju=8K|>_&=x 33 }4{ 9UhQQp8-ZF5ʥ$Ivvv뱱hmjiFo)))"hbb2~cX^rQ:>22| ><>>CCCFoNNNZ-ZfMAACY^ m?]?Zeee`0\l6'''ݻw/]]?7n|)Iׯ_/wf2R{[[HR|jưߞibjŋ$IVVVZ4MGGaFtʝ6tj'i#iΜ*>`ѢEjl6NNN~~~)7|r ӥD  O>_Աe:u^?pD"quuݶml6L iĉ+WRGիW{yyQdѵ;C?+VsV+1???;;;t LۗADD  /cǎJR4uizzzIIo? ˖-흫OH$0ٙagh\x744$o iif`eS˧ 8>%St<LɓbSV4Z.++;w^^^VV֟K,ao dX^_.sRs}gھK&}O:K֭[322M*?f{YqSʥKܹ#W1ͱ7H85ð7o*jڵ3-U x[xtRC 8kZʕ+I}YHH˩9?>/J ő#GN8x<(JC?)ܙ (<T*^G),+55ؘLStuu.ytSJAA={-G2dĄ :::<==7mڔ$y<ѣG}||l21|xrG:;;ryTTN + ȓ'O^ٳ?'Pxҥz>//SV!UUUUG(ݸq:jM֬YSUU駟޼yСCj?+//G'm5mω]fe(]%JeLLLddduuݻwq|F'anRTA&EDD^3=t$Ivwwcp.r):\RQQQWWn:klP>Ou@||۶mۓ'O KKOO l6922\KtsU TV`oo⃮_jyw~GW_N7Seeel>yftC~XX vfqBXX";C?!Iðikn{]tOϭsYYX,F7oذ$䤤9tKWt.MsJ`>u] h9o4?b1Al6i߾}`cq?**jҸEѠEBtӶ6Htݙ Y)j?""hfOO,@\0S/}{$IBBo%L&6mq 0 u$K. [о/`^@7t0Su68JSSk611q…i/z{, ?9`~X8x}_ӏ|?vq5MSSvq ظ=|… ڵK,j7ntvvIII 9srrpq|oW\I=ǭk8HYCCccc qw?~|^t<~h4ߵk^駟'd+,,K*Wjq R$ 5?**ݻɓ?xff&3:0ɔnoo>τ>uttCVS^^};vQ__tRZM=;hooFȰ6htth4Nzx]{!I2//Mbyp.??h4>x̙73[ff|yHDDrr2:j2>|X*f[lfyN[[[[ZZsJKKH$]ϘL&$uhWWW^0Ǐ ~zSS\.GVSVV|TP7lݺ5;;.$r8P슊80lѢE5jB*++-/I%%%...*JPǧA|'ޖ󟎏kW^222.]~ 6}ey3>>O>222Z-zf͚\.xm2R{[[HR|jgX,f^x?״qpjZhgΜA rFN 962z̩^?pD"quuݶml6L3=Vfsxx8zىaࠃQ*|Es=+V@N:y ___tA,+11qbbbʕl^ۛ O>_OM_իk/:sرRTM]^RR2888m<)q @/\ez{{V,Kww``"ݽ$ɷ~`0NsOsB||'O=<_**#G8quh@mgΜټyԫAgToVs*ʫWQ;X JMM-,,D#6x:885Lϰ ]BqV]re S^^XBҥKoV}}}^^h͚5UUU~͛7:dN\===7mڔ$y<ѣG}||l"(((111!!ð48УGFFFt:IyyyQg>bd;ڋVUUq8P866v ;v!""l6߾};>>ð~j37CW.iD"RY]]bt999s\.`v-Je0d2YDD:Er Cӕjccc[[[&޽;1p@=+EVGDD?b;mÆ 6fK^0Dp[&ff\YLLL[҇nMl4xN?z^?96gNI| . .@[i4&h[Կq61z۵kX,L| j6XVaZvƍ ))ip>W\ۃ?~l4-ǏïiZ¨DBjGj@F^O?577[NZB .@\x%Cjjj|>fo59IkkkkkkhhFVlnnn>|HHȜgJRN l?.L쭻rqqq(d2eeeۿh۷ر Kjt rrrp<33쎎N?ȩ۸v*e_}դlVkLd^^>ť3WTTsN+VSptt|4 sQ86 MZxG}De9|T*5Щ .z\`A\~I.{zyy]x1::Uח9;;>|855U(~7[nΦKGݻwPwuuI$gL&g7INNǫAo|v/^dXh9X泳^璒jJP(۩|L&{Aff&A$I~>>> O>F+p8 -Z^tussTTسH|cccі tqs:-%%E$MLLX&o,\.L  +W{ҥS+Th,?Qb2R{[[HF|WJ^lIbQxxxx?I˗/]FׯF0Th4YF@pP(,$It>u KMsTT~-LTf9<<urr0lŊ:&ɐfgǎJ2((hik'&&V\S .lٲ^^ۛHABӧ+]C/ٙ.?]ww$~m qmƅiFol6{jUL& ,28J/L&cX_|[n}ר氽jcn߾1FFFж̽{>C~5WE5r>{ҥ;wXPPgϞr[ iAAA uttyzznڴ)))I.xGlٲb?zhddDӑ$ݍa}tvv(Nd}ZJeLLLddduuݻwütrΨ8P(qu;:anRTA&EDDccǎ988DDD۷o+0c\ "##O::zҥj0$)...ŵS)ׯꫯ&e3LYYY/C..ũrrrpwssTh3VExzyy-^>2>|X*f[lfyNkF'x{{{ǛL&S@@ۙ?~ ׯ755rj/?aX___WWWZZD"zd21g'|L&{Aff&A$I666DEEK.kooq;|2]2ggÇ of֭$ITWWT*B sjjj[4ŋY,T,yNuhWWW^85q{ 9UhQQp8-ZF ussTTسyNI|cccі<ӌޖ/_^QQ^]v ݾ}X,>yP(SRRD"Ĥ⭎ۼIgި|/^?Lԕ+WX,hD7'''VY.YY@ 0 +W4{%I2;;ghhv`Sqd2IRww6*>88~cX(^r ]\S3' FсaXiiFh4;]~rF@ 8s JW(7$u:]```HH58әS^ۛHABӧ+Zzi^С'''J>Xhџ9Lȼ= M@@zr-[ۋu۶mfd2lW򢮁HKKcȿb t,[0,11qbbbʕ0;[cǎJR4uizzzII zVfsxx8zgyc S\Dyzz:;;tm0BCC>@om0lf`EzDWWWh4HlM= c?y򤸸ã%KϙVΝ;/L&/N>=u[fddPwm9[Կyθ_=NhpK.ݹsG. incoCKwڵjժ;w1DSx1;zYIBBB/_1i~KRBqȑ'NlAhhRzj\\X),+55Q7o*jڵtO*Nb0' 8kZʕ+>XPPgϞrǑ6;v!""l6߾};>>ðv/]UUPSSÐ_&%&&&$$`сܭYO?yC,QQQ:.<<|ɒ%t_nݺR L&@'BKz<58>UUUq8P866v ` D"Rvqq\{tsӦMIIIr=zg˖- wqj8ѣG###:$n üZ.A'O\zuyyٳg攔u\c3ߵ066. lsdd$ʶdɒcǎOu%8NHHȵk0Н &dt5"] Al''}QX__j*;س/322¦j2XLd~~ HLNNNJJ"I2++͍f͛7;=$I SNY޵$Ӂxm۶'OXdjCL QQQ--B +WƢ⡸&޽;1p@=+EVGDD?2]$IBBr$fyo/+L&͞E0JyCW `>ÙS`㘎  RS FoFoFo0z0z0z_7 P{_* IENDB`emacs-epc-0.1.1/img/epc-internal.png0000644000175000017500000010117212177363221017051 0ustar dogslegdogslegPNG  IHDRg'IDATx }W]ٔEKT x^}ՃGx c8K ˁdg`aa`/VUo==ݟ|k> $ACٽ{w*DH 8!B!#1B!BqB@į]"N"8!DDqBqDqBq@ APR\ڰa>Soj<5Y9< ?p=8!q.W&#=]\+=r\qB\(B!57^½ $oٲnzzQR͊GS9_m%kr^sp/{Dq Dn*k-x ½'$E\x| Z=8!+.!qs)7^8½'qB(J 8!'!D=8!.ʰa$P%={@@ A a=8!8C!@ 8!'!D=8!x,r 5aլY3դISO\<7pP1B5lP qBSn](m7ux4X;KtNֿ=M:=3i'NjJk-m}uTĝ.7Mz|yDq7 sr?BXVPS}-; ۇ`ͱjj?āW]0Sɍ[1)x⊸tfȧRm.6K/ڷoۈT6ډ1w!!\aUGĝ.ٳwB=8ĻeXL'ݶm[=o_v!0H'xEfF"[=P~%7n~!C]p_E](nTіe[oU?dս".NZl7o ֮];]D \:d^ԍ1e=8ĻeXL'4YQټ Nt:cd+#{/+fʕ*==]Ӡǥ?+?3JD<%CFI) YE-b:1Tuk[ t>>8H'J|Zj#1 ^Z% v2MJqHVw˰N>'5dY} }[ t>>8H'8=v&C7"#gʔ)>P{G D-b:1YPv}6or:cdx{@Tq&stDGĿϟ?_SXHc&0;[tݻmiwz~rDP"E\$qE2X{2,IN:1;=FNAn|~rDw}=4݈8ĿeXL'mMydczGZĝ#' |˴[n%"{q#qs0U <  a1~ȑ#'뗎Ҟ|IGˮ;=FNaѢE2&xEr? G,*,<  5!{G ";x`Zvjܹ֬s ?_U sҩ]we;6nxN:jݺu+))ѥ/cƌ"k;mp!@q½qD x&K;Yxzg.=%Ș{|wmW꿯]Km?ľ08!{G "|`rG^ͥҁ{6ڵk'˴Rl;-;$½qD DRX$'ϫ;zޓR`mצMeu@8!meGbaH87D xd_~޴i'|BK;gyƯ|Jq^^Ǐےo߾6;e;ӧ˗/KAv08iT½qDGēZEeѣGǍ{L5jH=#?8qjذy>}tMZSܲ/ PiiiAüe}8 8"NqD<"wڥk)ɏڵSV|Nj I† ԗ_~ eJ<#* i<E!Hቈ w_8IX"oh^z/B/b -")))ꭷt2L}[ i~3p4'ܫ<!!K.4L3/?p4:D'8"gAtCsڗhJC_dxHp "D܅"^rH;6zXsca\Jt gUTTk+⁆МЗ&G&>D"NqDV$]"΍Kb=zХ"xa*74}79橉Aq#Q| p0fkCEjz!մiS|rYׯڱc 4_![}.{h(#0qDGD#1xCW}fۼLYT铎nd;wsh}.{m oqo< "#\߀#8" "`"oRxi'ζ|nĈjرjZ%|?t %txD \߀#8" "u!OB6H_d8dx@SI5h@sso}aqs}"#xx!Ox_d.NYePKm/_9&}t߂P!@ "#xxyX$DJ.#/Ҧz…K/d\޽ڵkur{ksh}.{N-1qo{*3I\DQ4ĉUÆ .#/G%"}׺Gqnuܜ@@ } tLCDqDGy"< "N@q#0s!{qAqD08!{G ""ND +T$n#/C'{D I'))tN*8 ^=8"'crJÉE.rkr^sp/{DGēZK- /{{qDO/ @!a:D= : w Oe/L!nWcnDq}"#8"_ĥZ1!Սz: 8"#Jq'\8ۥ6_O-nDS㜕WcnΈ8!8 8A]/S+WD XgnЈ8!8 8A]+g!ppyM'D'+E|/-*''Gs2GS9_54"N"8"N0DBr !$^mQiET.89'x8 In҈8!8 8A]-Rxz:7iDDqD ┈˗ U 7vD 8"#8!5$"?~uN_gssG "#8"'s 9q_n;"p xxPD<9wGqDO̙3G5j(&`Hă 8"#∸ 2ոwOr?@qD<ٻwoݻwgW*@T'nEܩ#8In/-=Vq-fDƹGK2%(`lO~މ FAU/_:/RU+++u ]zݻ~[]v-5'1}Qk֬ž={԰a:u^*,̳~z=OfT:u|fͲ̗"NjK DĭW JℶoOvV"#'NP&LM4QO=p*G6ljum0P=|^pA9R5nXMΜ9΋/~ZM6W^QudffѣGydcƌQ^|AC"+W#5>״0ogeekܾ^0j*DٿT/8 8"9mjT2KXwAGpҤI6m?jOj/^nv4نP?R+#999UV^mV={a_~kv:,7ؼ?\ƥar=ܹS]rE:tbhZ~¾6O?7j(㡱 k#A2mɒ%L]|Ym۶MOJK+:=G)G*' -F{p:t".߿OEīk>)\fpB^z40{=i3g 5Pm)ukz:~x?PORp:nO%%%7"'dey1C-}؏e2rʪ%ģzz;v^.U}ݻ~}vϴ[}m?۷Qe˖3f={x %r.A)qD$W ,t­".g~WJz>Gs L8pMp~-ōiz*bC~_4Wn&9$k(֭W ټys\>X*"*6d\"D\ks-kAs/4 V>DrdصDqP;"L6g 7x5,}?HSH _&U̍(f?̽ ~ח "nӖ1Eq@qD|:t[hr-!e˖^+f[-D<!授G:$Fd\+\:n}=]:p~%uco"TH qN>/ *Ĉ4Q:uh޼QNJMą]B.߈8 8"gtf{o7 %߁"ݸ<zHqtFrȐ!CJ!NP#)NѢE=;T9rD=zd'TJe,x@%?P!F:}P$R"*\8qKlUlNqWD=P ?釮w"$*Eܟq7D~իW"|FɏxD'1qi{l{iN1vSeŹJ]9D$;8 /|'|O2)S|ӖBX8AILEJG;eKOOwTHwVGq@0R >|ճgO݇;v, $"gϞ_٫t*㫊SGq"N"8"NqiS6| N*+N "8!8 8ݻS;6KN#މۡCեggsD]*=_t﫮]:uuy۵k4h˷]vjժU}'caÆ /ٗSRRm֬3fx0w,hݲlYGڵU~Ԏ;,1;9s  "EʪWpf'.d3g2ի/B+Vm܌3TzKͷ/gz!YΝw[[du5ݻ{-S'ND4"N"8"N/K;lۿ?]9ʊ]ͽꬲsW^կ }.nHD=Z#X›7oV^Xxӧ|2"M:EUIؑD'!1Y[콧e(#o-=#8"NvjZy.]DrիgzS6W<^T]߳ge'OV:tP9998779GKa#q.y"#DTU}{do8qpЋ7mڤ222RP%кH˗mz-㣏>R< "M:yZ*+& 9rcKr^!#x"~t|Nܒݸ짯N콣#⡉t&EEE [ħO HUKN|E~]w޽ڵkuuw{{tc>@DB:1@$ DGCqݸ1+**ޱY"kpq".ۣG]-=+\`Ѫqj*--Hj 6 Z.{h7M=È8$G"1|չ"N^čvNDht& 7o{~ۃKlyUgcfHx>$(٧ϪN!6ry*\8qd|]j.Q񪎟#@GID$|w">gd}sDDA"~GQvW'n 0D.b #PE*׮]S))){N2E/g7{lu=;w={OܹSuѺyG}e_6os>Dg̘Qe|͜9Syވx' 5q]@cq"NNM㍧ƃ:􍪬8[cۯJ/I4Vo^뿥vC 1b;vڼy*//-FIt۶m=vmիo\))6"?w.sĿwS .ԡCj7qtxQĿo#^s7?Dq@qD<|_fJQ wKg)uR.]xIېd\\["uj{,Nd\ \^=F oOucyoS8 tg?ވ8"8 8"κޫ0%޽{;޽;kz.b7^3z뭷ԦMTFFF@Iԩĉ:$*\-3::vh)y_iݺ?u#6>)K 1Ԟ#Hx#8 #x$E\:E\-mIYԂ tiӦor{;d]{gYɖ-Z^~eKhjҥ#9#Ǐmonih]H 饗,''Fgz\jx'vDq@q#aDGX4$J>LK'k|׺'qm \+޸qc5zh-F}L }j)w.sӳG}T\tOzTtGyDz'N6lX-oaSK RUQ97D9wqDG6R|2]{8"r $B9qDGēTĥzudҶwFA&!"ȹ#8"% ;cM͕*==]wX*ͥ97sEsGqD'| \%1ζTQzFqD<E<1 N@*5wXE< d"uL"7s,@ ""-Սkr^#qbLH81WOzFqD;n!SEc5?y"N8DG "J/)c|qM7! D@9Ν;kT_xUkRĥc6G3Re}Ν8 Gq gǎ{USz4iy_rsNX5:ujٲj޼4hZvm$33S7N5mT;>d=tU'8"#$^zFqD<E4s< tE|IB3#3"#xHʕ+M9oAyvRJE9gG%gDGPBDkq ̤_mp,o5qD '[T[_M9W伐C/r!G"g1cxɆ#OX7WI %ٖ*8 o_7G*M|ߪ/q[qlG9wqDG]u"b4! 8"\"~2n0WK'$Ԙn!-X{K = D ∸EĹHKq@!<~".qGdK'=1wp]pRiwZcgz[DI(d{j$BYyH4"""j*-R*.yO?ԧN4"M6UǏ{>EZؾxCqVZy߶m[uYωPgvK{*XiZ'>gWe5SbQGgm#1""x\Q-ťdCt^TZZ.){3g=R,e.\RRR<ѣdUVV._mۦ|!mײ€nI)y󴈋Zĥ=S{'Kde흸e!∸=rZoD@H Q_馛TAA&kn7!zӦMi;v㎰oׯt?eԌ3TϞ=nƐDXlCu9)m5rE-uBeLp{lʕ+^LȾ1wVx"N\#9#8"8 q(k֬[[ZTTdP&ÝkW^KI?Zվf9܊ sN%YD܈:錄J=l_׮]ݽ{wok׮Y;qℚ0acA[YCrD@D<E|E'z6l4yw>E~uyyyҥzWu񌌌DXYĥú`k>>Jd;w.:2+ ݸEW^yMK3 #Ҏcǎ^HudYNt _'/"TqDD8qyЖͥu^^=yYZEEaxΝnݪ|^wY>p}AyΜ9&q)=W5=PXu Owv{DAkaMڍ#nqCZ:F :tky:j)p)i . iYNt 2u!mCx0!GqDq@LĿ O|?~ݺuO~.UJ|^?ĉugmjƏFi_tI Ⱦ}YjүJH b-)=C<&XDMvG[w]G^ُCq/fCAYib2rB(r2Oe7v"8  "qĝ\3".b-nExF~UY"n&ntxfDJU]y"/C'ۉ#"8"qinM:[s)q9@,mڴס-6mү۵kgƌD Ԃ Ç,'D_t'ۀ#"8"Nxw&=S6RՉq7xQQSKZX;w`ܸqiӦ !CtPdH`tqDq@'m%(՛:Aq@qD' GkV}1^YUps=VXE"1q nH387Nq5^=6ᛎ"9LUJ2#8"8"d"nWJ{sq"9^YDD@đq#{]CCq=}2Mk 8 8"Γm8ÙE6+pe< .DD@đ$V y7ڍ;+{lUeE j[n+X Xu80bږH}"" 8"գ{'nkDIU}9FN2ҿ <"~%ս{w[h$222eee8"8"#∸WUuNܜ["JAȱ)[8_5y-_\M:կd_.((P=jڴ^FrNժU+qFˏ?'xB]p_uUթSG |m]vAFvکUV]W8./GGqDU:>K.O6c@~nzՅ5y=j!?>#URRUǍ{=sNխ[7>UE\g̘aY̙3u鱈mt^z/BXbjٲeu#<%v=VXE<o瘸o瘀@ĉDh7nM@q|n9DW'r SڃgRĥD!y*"^;w)I:XoJ/|@U++++m##8"NwNՉ[컽=RIf jړ٫W,kV׮]?oNz<NK{u}.]-?m7ϋ##8"Wqs_쳯eG#eP^6mT^^ߒZ)}%N Y[nm)7":l_4Dܾz뭷ԦMtjN8"8"#∸ٳWzOt}铑7rHKgiFGbW^+y…K/?^-YDm߾F|ڴiO>m3ۃˈA4{M7:k޼JmqqzGU۶m|qӥ>}Rc)u6L?crL^;ى' $Uѣ.vp]ÚrO ѧ|1o߱h18gDqD԰U yDq3vlq\:AːfF@hzZGr>! "qx[jxF7kmcr cƌäu#8p$8&Jq"ք+ⱂk xD\Rݘ# "@#GqJ %#9@qDG "#8AġFyo<"a 8"#GqD "~ 7tߪ/7^ }pjܸjѢ01͗78"'Wd \{$b"sK]MPAIx g/Ok7xzI貵pߪ!oFU~[U|n=w@OYʹeq#^"^QQLGDG "X q)/5=mߗ};8"'RΉLGܑ纷~"~4aAI͉xgo? .*a/b5-֭[eK\_omJ>Eu/TmUF Ոm W?|JLVg~:N -~_t[yyfgM)"NqD\s\Rp2DܑGqC"O'QX.̴ͪ_^^տ~Rj{y_WAUQ>Tg>BSO>ywkuj{fXZlz?e!{u NQӞ&O#GąC;pGqC"땋~_?KK:'kճB;{n)!?wj*Y/R_'}\udwo?-"xO!4^>wt+"D78'sD'8";a77կԯ_ԫWW0Lk:]*أCd]R]˿2oQ>kg#GM>G#狜7r#xGġDnHKY4ii|fM.iu_;/cl.piCKͥO8"NqD @Pyui̙5j#P".8}mX]wZ޷3jy]dY|[TE*9UwnTvqD 8"#)n߾XOV!xcѽKit3W/m>l})44nqD 8"#D*_:[ #x8J"R:-ۇQ7np{k6Lȃ^sLDsDXKUsi=Պ::%"o.ś/eTG "^" .[cMVOc6UvZ@ZOu]ڵkEWGY|N$٘gǎ[nQÆ w 5aլY&ꩧR^zg_1}Ϟ=jСAU-ԋ/\)3_~/ɑ?3x:$|"NqDGēR_y/͛W^O:#{^ӥ;z-G<"n_1]߾ٳgJ,5_3 8"#8"#8 AE\"8"NqDx7m6եK-"֭+M6կE#)ԭ[ׯK{PE"nq?<&/6f#8Aq@jD:GԍNg߾}==#-R:oR}\>ٵkW‰q.,S"8"#8"\۴irڵMv7|Stgmi1c6`=m^ҝ&/}]=ܘt&=' EU B#8"'-&'wO6g7xï|={VюeHlZZTY^ލ2]7"C? -; "5q>{8߯n[9&>q#8""^TTe\+Ν._P>]z.դUVyի1hƍҥ!Ct{vs-Zk۶gKZGW&#熜#r9c?r.!8AqDD<*"WlKuD'8"#wI@q#8"C*ƄstD'xD[p"m8G񀷈'DD<9D 8""DBFq@G "#GqDqD 8Aq@G "#\H8"#8"N"D@q@q4I)j,D 8 AaA,͢V\} r猜?r!'7p#)..V0`j֬jذ{G}^tkV-ZP#GT;vpgΜQz-ܢ6mz-URRR(3gjԨQ҉ ݛq#=GԂ? P}< GăCT׮]K.9^HnΝ;[lٳg]/\VxÖ"NqDFJH4r^![5=(ݻ{[ŋ*??_׫WOo]fNN?~~oذa{?VST~ͪO>S".qmcA "".[cMVOcGMޣ%*+\/͓'ry#q.y[ē7ŋ?Օ+5K,w2{kKwW괴4^֭-333ѣUƍU&MԘ1cTvv٣mԩzڴizT7r 5a]U_SO\5'ⱿGYEfևD@`'BEHI;Yj#H~zK)Y-SJ=@woҥC۪U+/m۶ {VV*"ڵkjʔ)~3Z"+?>ؗӣG]zL"^^,nq#5Bh<(#/R,KU,B߿_1B'ʑ#GD˼vzԲeti̜9S+I) 4yQF .OuK't'Nϛ7ϲ^{ͳ#˴ Ƹjz3A-A """.Պ nqD_֭߯ yv9/H_~YWwԶf˼~~#mnݺy'΃m!R=ߞ^zzI!uDDqB(GCvmwuD\J{ر$ܞ/R'?Q}˔r#Fqґ[ɏ F4h9~sp#C7ߜ"];D ""'h񠋈#(FUrm%2tɧT;wū,˺L7xCeϤ)n)aE\ڃz4Of5o"## xDD?^F޽K鶬W:k{gu]YΌ3< iÇWV;sK 8"'-&'Of_nSgأ~X:to֭[u" }ۥJQD\>ue)EqD .ڃ#\H8"#x]7Z|eڲeqD'=Ji8"΅#8""3K/pfD\zOԩڴiC,CIo$R+<\#8"8""8@ UC꧿N䚒dDE}G ˮ#6"##∸vm* ԕ+Wԁ԰at;qbepDDj̙3G5lEFqDqDw={~X5o\իWO?P+V+ɋ (8"H".P#8"xPOtq|}ؑGDwDܝ"n7DGPZ-"NHDYZj :T5h@hB⋪kBYT+7|̃ꗟ 5sNGc@xjjgŋZjyͷi&ϴ;viwq"WģsޞMsn֏D@qD<s7αP#qZ|EEE^.؊P^|PFqDDGI"%–qDFD|Ru݈HLkܸqUTT Q/ 8""G{ƍe˖L}cx[chwH}GݻuQ֭ ́-к|_~n]:ww:ZVKz޹@GUy|)Jx% +ĂPQ(E >VVV5@%R"* AR0HTĄÝ;3$d&90;snqsseLwҭ[7StRѣGO]EԩSx^ '8"8"_K :eqx/fȻۺ;w^8%%Qzj:uj!8"^;1bcxVZZ깟^{… }*Wر#"틸!GGCNo>TEٳ>}HQQؔGĽSbOn]֦M),,t?~\zq!)MԦ"靖xe˴W\O<ܶG{hw |l߼E<,sA_q#8""T)>l."5x]V]s=wLoGEE"GKIŇY98Aq@:V<[uLj=f8"#8"ް GqDeΜ9fGœɁcxq7usD9VI&!8"#8AY@+M_'7s."#]v5ur]xƍ\N]E\{5KcƌiwS7puVs2RxӴiG*xdnq#8"{~BI111f$u1)* !nVj̾tz GNqD 8 8 8AqD 8"#G Aq##Gq#8"""^yM 8AqDo Vn;Im?V[v7-֯|ΦZEی~-!8AqD"|Gϗc'Ijm7~ oZa_ZZPh[6cmK8"N 'bx)ܗg9mWMզ"9mKA " x)ACSSgDdzm(^38AqDoT K\DꌶmGMݖq'@{g=R BیH{ GqDq@qDDq@q#8"8 GDqDGqD@q7WěNNo.8EA " 8 #8"8 #GqDq@ ""8 8"#8" GqDq@qDD@G " WěNNo.8EA " 8 #8"8 #GqDq@ ""8 8"#8" GqDq@qDD@G " xCPR|ّkH~<h;ڌmG8 8AqD#d}b Fۑ'DqD ^"~B _o:9E/8@BvTm)⼿{q#8"(vg|9y;qF*?χ'1Nhc%mW8 8"#8"t*B#p QGGqD@q?؇R8"8"#8"W/:><DGqDG' GGC5-Z$ #GqD'8"8AM+@#!P'7s&jj"NqDGΦMUVfzLLL8Q>ph]dԨQҮ];2d[qHΎ"D@qDڨk***dժU2xj:++Ku&[n2ٰa2D'8"#∸|[ߒj{ ̙3%%%Q|rDD'8"#∸,X@MO={ܹsE{RXX(Gq@q#8"IeeL6 7##gϞEq@q#8"6ǎ+VH߾}޽{e8"8"NqDG& gϖ_Q##Y 񦯓Sě﹏d1b~isۺYu"~Qٳl߾\-3"#8o28hтf"V(<0"8"#"NC-+;gDGq@` mۤSNr7{ʷo.C54h1vs{hن _~ҲeKiժlڴ:_OM62j(Ojj\{Ҿ}{0asc9rmVj_!8"#C̙3\u:uj!8AqD={Q=EEEbS^>?V"n k׮qqq>)nyzBGǥgϞ Gqh"Dy}x]Dͱkqu;wq*QQQzoV.祺2]GmcC."D@!D|5$UUUq զn޽r5xn'OVMYǎMwGq7W9'Ec9sd͚52=zhGn۷AzMo8=9@ZmnSbbUVɤI@y}@xD@ġىxNNOWTT‡.7nṯ֕m]zLWA3fL>tus]\Wkw}W:SW]5].9礗S+evڅG('ȡʖ~e4B mV;6mG " "_/[,111mn[Ӟr­"n˗Kݥ]v[;˖-3bA{{lIYBBٗӻrKZkH& m;چqD 8"a"1 )%xP||2 AA"D@GqﯷlIvvYlJFۆm+fm G "  #8Ԁ}Hj]>DG " @"^y|'D'8"CY5#N>/9~Wh1&.OGqA?W9';Ӓr>4IFۏՖ]jZN#D 8"BɩÉ+^_8AqD" P هGڞx]!Gă,UUU0qD@gJDꌶmGq#L돠ו#^H88"N""̾"" ʾ-q>qDD}T{[oUΞ=*|Azݻlus_E<2^7qܒSŻAqDqS(VTTH~<­}i)..6[6۞y!{fk~wq3T_wˉ'_4D@CBǍgo޼vs% s}jnSq }PΝ}7>>1رc~w5k=o}ŋ{'uعw>=vUֵLo5;9y\v}Lvvߑ+R})z{j[724oIb8>Xl;vg2{mf?;*:u2`f{n]bxʮZSX|͎XUM˗ "8"h"Yz˾}L[ v~Am=.6{lO}kZگ.¦\sp ի=Y`g6/R,Ybn6ZlYPdnchD Lr"~yj97=D\ǺYuC̙^`vÇ Yvfk{x۱{_|Pc.Cyl\\)k#͢w1, 6"9w˽k.i=Cj8vyyY(mȑF[je.e6|Cgwu M6{GkϹ.ֽ{w1c~2]lJnns=kr-f8=f.T7O"8@oZq<7qq#8 a(Ηw_\D< 獳A 4P@81ϖa7_0oFq@”O$xrQƗaol$>tq'=8{#8@$?5+WbEۘ'Nk}?cW~bm$"v #?hT˪Ǜ1q4pc;"?-ۇ1f'ᬒNqPG{K_~V^S>qiѢ!܏spBqG_@C22e:u幒_{ZKJJd„ Ү];;v8ů/%/?V9`Zm!@C2ҹsg3ccc%?? uqZJcXA#}Sײ_sm"xD\3 8 amo/++3ex[ WTTxTʃy D<2Ehe!8 a~U;4>le˖ϭ۷V"w^Oٮ]LٰaÂr D|7UriÇ7e/1N:G-a>8!8@ɋqr7Lq}C2ql^"wuN:\zL/iv q,\G;%=zҠO|pBq&q/9ݧ\{{t@]ѣr]w:tiӦq{:))HG… >,111:"^q=ɓz D<DZ#.G|٦z =7J0n?/z =u[sk'8"8!g!6mieoo׮Mn0[/UT?7۝jz'z Es|<9P| GqBq110ۥ_UWEll{-5-'lX*:ܟ{9! # > 6@ 7YG0!v* M|׎yvTu|}΅E!0v{sw"N"8^|!ӛsxEOʆ?5 )?Ukek=Sn5?(78ر獮஋mIYW75yaυX%*z۾Xeh}w"N""=mIı-E\Yls0}|ě|zo3kEox, *Z]u{Wq֡:}萾fS:*uK/tNtx}+o@DD',UUUX%U ℐ Ќ$l6MDeggcV6cmKA A3{woTZZbUhڏ%D@yQch1@]ѶBqzzm(^38!8@{#P+hۉ "N"8!8 DqBq@ A/ 5w}m-tEXtSoftwareby.blooddy.crypto.image.PNG24EncoderIENDB`emacs-epc-0.1.1/test-epc.el0000644000175000017500000002350112177363221015253 0ustar dogslegdogsleg;;; test-epc.el --- test code for epc ;; Copyright (C) 2011 SAKURAI Masashi ;; Author: SAKURAI Masashi ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; Run tests: ;; $ emacs -L . -L $HOME/.emacs.d/elisp -batch -l deferred -l concurrent -l epc -l epcs -l test-epc -f cc:test-all ;;; Code: (require 'epc) (require 'epcs) (require 'cl) (require 'pp) (defmacro epc:with-self-server-client (connect-function &rest body) `(lexical-let* ((server-process (epcs:server-start ,connect-function t)) (client-mngr (epc:start-epc-debug (process-contact server-process :service))) (dfinish (lambda (x) (epc:stop-epc client-mngr) (epcs:server-stop server-process)))) ,@body )) (defun epc:test-echo () (epc:with-self-server-client (lambda (mngr) (epc:define-method mngr 'echo (lambda (x) x))) (deferred:$ (epc:call-deferred client-mngr 'echo '("echo test")) (deferred:nextc it (lambda (x) (if (equal "echo test" x) t (format "Return : [%s]" x)))) (deferred:watch it dfinish)))) (defun epc:test-echo-list () (epc:with-self-server-client (lambda (mngr) (epc:define-method mngr 'echo (lambda (x) x))) (deferred:$ (epc:call-deferred client-mngr 'echo '((1 2 "echo test"))) (deferred:nextc it (lambda (x) (if (equal '(1 2 "echo test") x) t (format "Return : [%s]" x)))) (deferred:watch it dfinish)))) (defun epc:test-add () (epc:with-self-server-client (lambda (mngr) (epc:define-method mngr 'add '+)) (deferred:$ (epc:call-deferred client-mngr 'add '(1 2 3)) (deferred:nextc it (lambda (x) (if (equal 6 x) t (format "Return : [%s]" x)))) (deferred:watch it dfinish)))) (defun epc:test-deferred () (epc:with-self-server-client (lambda (mngr) (epc:define-method mngr 'deferred (lambda (x) (deferred:next (lambda () "OK"))))) (deferred:$ (epc:call-deferred client-mngr 'deferred '("OK?")) (deferred:nextc it (lambda (x) (if (equal "OK" x) t (format "Return : [%s]" x)))) (deferred:watch it dfinish)))) ;; (cc:debug (epc:test-deferred) "test-deferred %S" x) (defun epc:test-large-data () (lexical-let ((len (* 65536 2))) (epc:with-self-server-client (lambda (mngr) (epc:define-method mngr 'large-echo (lambda (x) x))) (deferred:$ (epc:call-deferred client-mngr 'large-echo (make-string len ?x)) (deferred:nextc it (lambda (x) (if (= len (length x)) t (format "Return : [%s]" (length x))))) (deferred:watch it dfinish))))) ;; (cc:debug (epc:test-large-data) "test-large-data %S" x) (defun epc:test-multibytes () (lexical-let ((str "日本語能力!!ソハンカク")) (epc:with-self-server-client (lambda (mngr) (epc:define-method mngr 'echo (lambda (x) (cond ((equal x str) str) (t (error "Different content!")))))) (deferred:$ (epc:call-deferred client-mngr 'echo (list str)) (deferred:nextc it (lambda (x) (if (equal x str) t (format "Return : [%s]" x)))) (deferred:watch it dfinish))))) ;; (cc:debug (epc:test-multibytes) "test-multibytes %S" x) (defun epc:test-ping-pong () (epc:with-self-server-client (lambda (mngr) (lexical-let ((mngr mngr)) (epc:define-method mngr 'ping (lambda (x) (epc:call-deferred mngr 'pong (list (1+ x))))))) (epc:define-method client-mngr 'pong (lambda (x) (cond ((< 3 x) x) (t (epc:call-deferred client-mngr 'ping (list (1+ x))))))) (deferred:$ (epc:call-deferred client-mngr 'ping (list 1)) (deferred:nextc it (lambda (x) (if (equal 4 x) t (format "Return : [%s]" x)))) (deferred:watch it dfinish)))) ;; (cc:debug (epc:test-ping-pong) "test-ping-pong %S" x) (defun epc:test-app-error () (epc:with-self-server-client (lambda (mngr) (epc:define-method mngr 'error-calc (lambda (x) (/ 1 0)))) (deferred:$ (epc:call-deferred client-mngr 'error-calc (list 0)) (deferred:nextc it (lambda (x) nil)) (deferred:error it (lambda (x) (if (string-match "arith-error" x) t (format "Return : [%S]" x)))) (deferred:watch it dfinish)))) ;; (cc:debug (epc:test-app-error) "app-error %S" x) (defun epc:test-epc-error () (epc:with-self-server-client (lambda (mngr) ) ; nothing (deferred:$ (epc:call-deferred client-mngr 'some-method 0) (deferred:nextc it (lambda (x) nil)) (deferred:error it (lambda (x) (if (string-match "^EPC-ERROR:" x) t (format "Return : [%S]" x)))) (deferred:watch it dfinish)))) ;; (cc:debug (epc:test-epc-error) "epc-error %S" x) (defun epc:test-epc-methods () (epc:with-self-server-client (lambda (mngr) (epc:define-method mngr 'echo (lambda (xs) xs) "XS" "Return XS") (epc:define-method mngr 'add (lambda (xs) (apply '+ xs)) "XS.." "Sum XS")) (deferred:$ (epc:query-methods-deferred client-mngr) (deferred:nextc it (lambda (x) (if (equal x '((add "XS.." "Sum XS") (echo "XS" "Return XS"))) t (format "Return : [%s]" x)))) (deferred:watch it dfinish)))) (defun epc:test-epc-server-counts () (lexical-let (server-count1 server-count2 client-count1 client-count2) (epc:with-self-server-client (lambda (mngr) (epc:define-method mngr 'echo (lambda (x) x))) (deferred:$ (epc:call-deferred client-mngr 'echo (list 0)) (deferred:nextc it (deferred:lambda (x) (cond ;; * test 1 ;; waiting for finishing other threads ((< 1 (length epcs:server-processes)) (deferred:nextc (deferred:wait 30) self)) (t nil)))) (deferred:nextc it (lambda (x) (setq server-count1 (length epcs:server-processes) client-count1 (length epcs:client-processes)))) (deferred:watch it dfinish) (deferred:nextc it (lambda (x) (setq server-count2 (length epcs:server-processes) client-count2 (length epcs:client-processes)) ;; * test 2 ;; comparing connection numbers (if (and (= 0 server-count2) (= 1 server-count1) (= 0 client-count2) (= 1 client-count1)) t (format "server %s %s / client %s %s" server-count1 server-count2 client-count1 client-count2)))))))) ;;================================================== ;; Async Test Framework (based on deferred.el) ;; * template ;; ;; (defun cc:test-template () ;; (lexical-let* ;; ((dtest (deferred:new)) (x nil)) ;; ;; (deferred:callback dtest ;; (if x t (format "Fail %s" x))) ;; ;; dtest)) ;; (cc:debug (cc:test-fib-gen) "Fib10 : %s" x) (defvar cc:test-functions '( epc:test-echo epc:test-echo-list epc:test-add epc:test-deferred epc:test-ping-pong epc:test-epc-error epc:test-app-error ;;epc:test-large-data epc:test-multibytes epc:test-epc-server-counts epc:test-epc-methods )) (defmacro cc:debug (d msg &rest args) `(deferred:nextc ,d (lambda (x) (funcall 'message ,msg ,@args) x))) (defvar cc:test-finished-flag nil) (defun cc:test-all (&optional on-fail-function) (interactive) (lexical-let ((start-time (float-time))) (deferred:$ (deferred:parallel (loop for i in cc:test-functions collect (cons i (deferred:timeout 3000 "timeout" (funcall i))))) (deferred:nextc it (lambda (results) (pop-to-buffer (with-current-buffer (get-buffer-create "*cc:test*") (erase-buffer) (loop for i in results for name = (car i) for result = (cdr i) with fails = 0 do (insert (format "%s : %s\n" name (if (eq t result) "OK" (format "FAIL > %s" result)))) (unless (eq t result) (incf fails)) finally (goto-char (point-min)) (insert (format "Test Finished : %3f sec (%s)\nTests Fails: %s / %s\n" (- (float-time) start-time) (format-time-string "%Y/%m/%d %H:%M:%S" (current-time)) fails (length results))) (when (and (< 0 fails) (get-buffer epc:debug-buffer)) (insert "==================\n" (with-current-buffer (get-buffer epc:debug-buffer) (buffer-string)) "==================\n"))) (message (buffer-string)) (current-buffer))) (setq cc:test-finished-flag t)))) (while (null cc:test-finished-flag) (sleep-for 0 10) (sit-for 0 10)))) ;; (progn (eval-current-buffer) (cc:test-all)) (provide 'test-epc) ;;; test-epc.el ends here emacs-epc-0.1.1/readme.md0000644000175000017500000003573412177363221014777 0ustar dogslegdogsleg# EPC : The Emacs RPC This program is an asynchronous RPC stack for Emacs. Using this RPC stack, the Emacs can communicate with the peer process. Because the protocol is S-expression encoding and consists of asynchronous communications, the RPC response is fairly good. Current implementations for the EPC are followings: - epcs.el : Emacs Lisp implementation - RPC::EPC::Service : Perl implementation - One can get this module by CPAN or PPM. - python-epc : Python implementation - http://python-epc.readthedocs.org/en/latest/ The current status is beta. This library needs more applications to confirm stability of the API and robustness of the implementation. ## Applications Projects using EPC: - [Emacs DBI](https://github.com/kiwanami/emacs-edbi): Database GUI and API for Emacs - [Emacs Jedi](https://github.com/tkf/emacs-jedi): Python auto-completion for Emacs ## Sample Code Here is a client code. ```lisp (require 'epc) (setq epc (epc:start-epc "perl" '("echo-server.pl"))) (deferred:$ (epc:call-deferred epc 'echo '(10)) (deferred:nextc it (lambda (x) (message "Return : %S" x)))) (deferred:$ (epc:call-deferred epc 'add '(10 40)) (deferred:nextc it (lambda (x) (message "Return : %S" x)))) ;; calling synchronously (message "%S" (epc:call-sync epc 'echo '(10 40))) ;; Request peer's methods (message "%S" (epc:sync epc (epc:query-methods-deferred epc))) (epc:stop-epc epc) ``` Here is a server code in perl. ```perl #!/usr/bin/perl use RPC::EPC::Service; sub echo_test { my $methods = { 'echo' => [sub { my $args = shift; return $args; },"args","just echo back arguments."], 'add' => sub { my $args_ref = shift; my ($a,$b) = @$args_ref; return $a + $b; } }; my $server = RPC::EPC::Service->new(0, $methods); $server->start; } echo_test(); ``` Here is the equivalent server code in emacs lisp. ```lisp (require 'epcs) (let ((connect-function (lambda (mngr) (epc:define-method mngr 'echo (lambda (&rest x) x) "args" "just echo back arguments.") (epc:define-method mngr 'add '+ "args" "add argument numbers.") )) server-process) (setq server-process (epcs:server-start connect-function)) (sleep-for 10) (epcs:server-stop server-process)) ``` The elisp server code should be started with some arguments (batch starting and indicating load pathes) like the following code: ```lisp (setq epc (epc:start-epc "emacs" '("-L" ".." "-L" "~/.emacs.d/elisp" "-batch" "-l" "deferred" "-l" "concurrent" "-l" "epc" "-l" "epcs" "-l" "echo-server.el"))) ``` # Installation ## Package installation If you use package.el with Marmalade (http://marmalade-repo.org/), you just select the package 'epc' and install it. ## Manual installation This program depends on following programs: - deferred.el, concurrent.el / https://github.com/kiwanami/emacs-deferred - ctable.el / https://github.com/kiwanami/emacs-ctable Place those programs and this one (epc.el) in your load path and add following code. ```lisp (require 'epc) ``` # API Document This section describes the overview of the EPC and how to use API. ### API Overview The EPC uses a peer-to-peer-architecture. After the connection is established, both peers can define remote methods and call the methods at the other side. Let we define the words *server* and *client*. *Server* is a process which opens a TCP port and waiting for the connection. *Client* is a process which connects to the *server*. In most cases, a *client* process starts a *server* process. Then, the *server* process provides some services to the *client* process. This diagram shows the API usage and the relation of processes. ![API Overview](img/Overview.png) ### Object Serialization All values which are transferred as arguments and return values between processes, are encoded into the S-expression text format. Simple list structure and some primitive types can be transferred. Complicated objects, such as buffer objects, can not be serialized. The EPC stack doesn't provide transparent remote object service, that is ORB. The EPC stack can translate following types: - nil - symbol - number - string - list - complex object of list and alist. The elisp function `prin1` is employed for the serialization from objects to string. The peer EPC stack decodes the S-expression text and reconstructs appropriate objects in the particular language environment. One may want to translate an `alist` as a collection object of key-value pairs transparently, so called 'Hash'. However, because we can not distinguish between alist and nested list, it is responsible for the programmer to exchange the alist objects and the hash objects. ### EPC Manager Object (epc:manager) The struct `epc:manager` defines all information for an EPC activity, such as the connection status, remote methods and sessions. Many API functions needs the instance object as an argument. One, however, doesn't have to learn the internal slots and detailed implementations. An instance of the struct `epc:manager` is created by calling the initialization function `epc:start-epc`. One can stop the EPC connection with calling the termination function `epc:stop-epc`. ### Start EPC (epc:start-epc) * epc:start-epc (server-prog server-args) * Start the epc server program, establish the connection and return an `epc:manager` object. * Argument * server-prog: a path string for the server program * server-args: a list of command line arguments * Return * This function blocks the evaluation and returns an `epc:manager` object. * Error * If the server prints out non-numeric value in the first line or does not print out the port number in three seconds, it is regarded as start-up failure. The established EPC session is registered to the global variable for the connection management interface. (See the Management Interface section.) ### Stop EPC (epc:stop-epc) * epc:stop-epc (mngr) * Disconnect the connection and kill the server process. * If the `epc:manager` object has exit hooks, this function executes those clean-up hooks. * Argument * an `epc:manager` object ### Define Remote Method (epc:define-method) * epc:define-method (mngr method-name task &optional arg-specs docstring) * Define a remote method * Argument * mngr: `epc:manager` object * method-name: the method name * task: function symbol or lambda * arg-specs: argument signature for the remote method [optional] * docstring: short description for the remote method [optional] * Return * an `epc:method` object The documents are referred by the peer process for users to inspect the methods. ### Call Remote Method (epc:call-deferred, epc:call-sync) * epc:call-deferred (mngr method-name args) * Call the remote method asynchronously. * Argument * mngr: `epc:manager` object * method-name: the method name to call * args: a list of the arguments * Return * Deferred object * See the next section for the error handling * epc:call-sync (mngr method-name args) * Call the remote method synchronously. * Argument * mngr: `epc:manager` object * method-name: the method name to call * args: a list of the arguments * Return * a result from the remote method ### Error Handling The remote method calling may raise the error. The error has two types, the peer's program (`application-error`) and the EPC stack (`epc-error`). The `application-error` is a normal error which is caused by peer's program, such as 'division by zero', 'file not found' and so on. The programmers are responsible to this type errors, recovering error handling or just fixing bugs. The `epc-error` is a communication error which is caused by EPC stack, such as 'connection closed', 'method not found', 'serialization error' and so on. This type errors are caused by environment problems, bugs of peer's program, our side one or the EPC stack. Here is a sample robust code: ```lisp (deferred:$ (epc:call-deferred mngr "a-method" '(1 2)) (deferred:next it (lambda (x) ;; Normal return ;; x: result value )) (deferred:error it (lambda (err) (cond ((stringp err) ;; application error ;; err: error message ) ((eq 'epc-error (car err)) ;; epc error ;; err: (cadr err) -> error information ))))) ``` In the case of synchronous calling, a signal will be thrown immediately. ### Utilities * epc:live-p (mngr) * If the EPC stack for `mngr` is eastablished, this function returns `t`. * epc:query-methods-deferred (mngr) * Return a list of `epc:method` objects for the peer process. ### Define Server Following functions require the 'epcs' package. * epcs:server-start (connect-function &optional port) * Start EPC manager stack and initialize the manager with connect-function. * Argument * connect-function: a function symbol or lambda with one argument `mngr`, in which function the manager should define some remote methods. * port: TCP port number. (default: determined by the OS) * Return * process object Here is a sample code for the EPC server: ```lisp (require 'epcs) (let ((connect-function (lambda (mngr) (epc:define-method mngr 'echo (lambda (x) x) "args" "just echo back arguments.") (epc:define-method mngr 'add '+ "args" "add argument numbers.") )) server-process) (setq server-process (epcs:server-start connect-function)) ;; do something or wait for clients (epcs:server-stop server-process)) ``` * epcs:server-stop (process) * Stop EPC manager stack. * Argument * process: process object ### Debug Because the EPC stack is designed to work asynchronously, sometimes one can not use the debugger for the own programs. Then, logging is useful to analyze the troubles. The EPC has some debug functions for analyzing low level communication. * epc:debug-out * If this variable is non-nil, the EPC stack records events and communications into the debug buffer. * epc:debug-buffer * debug buffer name (default: '*epc log*') * epc:log (&rest args) * logging debug messages ## Management Interface The EPC has a management interface for the EPC connections. Users can check the current connection status, inspect method specs and terminate the connection. ### Current Connections Executing `M-x epc:controller`, one can display the list of current established connections. ![Current Connections](img/mm-conns.png) This table shows following information: | Column | Note | |--------|------| | Process | Process name | | Proc | Process status (`process-status` for the process) | | Conn | Connection status (`process-status` for the TCP connection) | | Title | Connection title which is defined by the EPC user program. | | Command | Process command and arguments. | | Port | TCP port which is opened by the remote process. | | Methods | Number of methods which are defined at the Emacs side. | | Live sessions | Number of sessions which are waiting for a return value.| One can use following key-bind: | Key | Command | Note | |-----|---------|------| | g | `epc:controller-update-command` | Refresh the table. | | R | `epc:controller-connection-restart-command` | Restart the selected connection. | | D,K | `epc:controller-connection-kill-command` | Kill the selected process and connection. | | m,RET | `epc:controller-methods-show-command` | Display a method list of the remote process. (See the next sub-section for details.) | | B | `epc:controller-connection-buffer-command` | Display the connection buffer. | ### Remote Method List Displaying a method list, one can inspect the methods which are defined by the remote process. ![Remote Method List](img/mm-methods.png) This table shows following information: | Column | Note | |--------|------| | Method Name | Method name to call.| | Arguments | [optional] Argument names.| | Document | [optional] Method spec document.| Here, 'Arguments' and 'Document' may be blank, because those are not essential slots. | Key | Command | Note | |-----|---------|------| | e | `epc:controller-methods-eval-command` | Evaluate the selected remote method with some arguments. | | q | `bury-buffer`| Bury this buffer. | # Implementation This section describes the EPC architecture and the wire-protocol so as to implement the peer stacks. ## Protocol Details The EPC protocol is based on the SWANK protocol. - [Understanding SLIME (Using Emacs and Lisp Cooperatively)](http://bc.tech.coop/blog/081209.html) ### Message Envelope - PAYLOAD-LENGTH : 24-bit hex-encoded integer - PAYLOAD-CONTENT : S-expression, text, utf-8 - (MESSAGE-TYPE . MESSAGE-BODY-LIST) - MESSAGE-TYPE : `call` | `return` | `return-error` | `epc-error` | `methods` - MESSAGE-BODY-LIST : (Dependent on message types.) ### Message [call] This message represents initiating method calling. - MESSAGE-BODY-LIST : `(UID METHOD-NAME ARGS)` - UID : The session ID, which is an unique ID generated by the caller side. - METHOD-NAME : A symbol for method name. - ARGS : A list of method arguments. ### Message [return] This message represents the normal finish of the method calling. - MESSAGE-BODY-LIST : `(UID RETURN-VALUE)` - UID : The session ID to return. - RETURN-VALUE : A return object. ### Message [return-error] This message represents the application error, which is due to the application. - MESSAGE-BODY-LIST : `(UID ERROR-MESSAGE)` - UID : The session ID to return. - ERROR-MESSAGE : An error message. ### Message [epc-error] This message represents the EPC error, which is due to the EPC stack. - MESSAGE-BODY-LIST : `(UID ERROR-MESSAGE)` - UID : The session ID to return. - ERROR-MESSAGE : An error message. ### Message [methods] This message represents the method query. - MESSAGE-BODY-LIST : `(UID)` - UID : The session ID, which is an unique ID generated by the caller side. The response message is returned by the `return` message. ## EPC Internal The EPC is developed on `deferred.el` and `concurrent.el`. The library `deferred.el` provides primitive an asynchronous framework and `concurrent.el` does concurrent programing components. - [deferred.el document](https://github.com/kiwanami/emacs-deferred/blob/master/README.markdown) - [concurrent.el document](https://github.com/kiwanami/emacs-deferred/blob/master/README-concurrent.markdown) The EPC user should learn asynchronous programing on `deferred.el`. The components of `concurrent.el` are just internally used at `epc.el`. ![EPC Stack](img/epc-stack.png) Here is a diagram for the `epc.el` architecture, which diagram may be helpful for code reading of `epc.el`. ![Internal Architecture](img/epc-internal.png) ## Other Stacks - perl EPC document http://search.cpan.org/~kiwanami/RPC-EPC-Service-v0.0.7/lib/RPC/EPC/Service.pm - python EPC document http://python-epc.readthedocs.org/en/latest/ # License EPC is licensed under GPL v3. # Acknowledgment I received generous support from @tkf. Thanks! ---- (C) 2012, 2013 SAKURAI Masashi. m.sakurai at kiwanami.net