Index: src/emacs_mode/DefCommand.cc =================================================================== --- src/emacs_mode/DefCommand.cc (revision 168) +++ src/emacs_mode/DefCommand.cc (working copy) @@ -24,25 +24,75 @@ #include "../Quad_FX.hh" +static void log_error( Error &error, ostream &out ) +{ + UCS_string l1 = error.get_error_line_1(); + UCS_string l2 = error.get_error_line_2(); + UCS_string l3 = error.get_error_line_3(); + out << l1 << endl << l2 << endl << l3; +} + void DefCommand::run_command( NetworkConnection &conn, const std::vector &args ) { vector content = conn.load_block(); - Shape shape( content.size() ); - Value_P function_list_value( new Value( shape, LOC ) ); - for( vector::const_iterator i = content.begin() ; i != content.end() ; i++ ) { - UCS_string s = ucs_string_from_string( *i ); - Shape row_shape( s.size() ); - Value_P row_cell( new Value( row_shape, LOC ) ); - for( int i2 = 0 ; i2 < s.size() ; i2++ ) { - new (row_cell->next_ravel()) CharCell( s[i2] ); + try { + stringstream out; + + Shape shape( content.size() ); + Value_P function_list_value( new Value( shape, LOC ) ); + for( vector::const_iterator i = content.begin() ; i != content.end() ; i++ ) { + new (function_list_value->next_ravel()) PointerCell( make_string_cell( *i, LOC ) ); } - new (function_list_value->next_ravel()) PointerCell( row_cell ); + function_list_value->check_value( LOC ); + + Quad_FX quad_fx; + + if( args.size() > 1 ) { + Shape tag_shape( 2 ); + Value_P tag( new Value( tag_shape, LOC ) ); + new (tag->next_ravel()) IntCell( 0 ); + new (tag->next_ravel()) PointerCell( make_string_cell( args[1], LOC ) ); + function_list_value->check_value( LOC ); + Token result = quad_fx.eval_AB( tag, function_list_value ); + out << "function defined\n" << result.canonical( PST_CS_NONE ).to_string(); + } + else { + Token result = quad_fx.eval_B( function_list_value ); + if( result.is_apl_val() ) { + Value_P value = result.get_apl_val(); + if( value->is_int_skalar( 0 ) ) { + out << "error\n" + << "parse error\n" + << "Error parsing expression\n" + << value->get_ravel( 0 ).get_int_value(); + } + else if( value->is_char_string() ) { + out << "function defined\n" + << value->get_UCS_ravel(); + } + else { + out << "error\n" + << "illegal result type"; + } + } + else { + out << "error\n" + << "unknown error"; + } + } + out << "\n" + << END_TAG << "\n"; + conn.write_string_to_fd( out.str() ); } - function_list_value->check_value( LOC ); + catch( Error &error ) { + stringstream out; + out << "error\n"; - Quad_FX quad_fx; - Token result = quad_fx.eval_B( function_list_value ); - conn.write_string_to_fd( result.canonical( PST_CS_NONE ).to_string() ); - conn.write_string_to_fd( "\n" END_TAG "\n" ); + log_error( error, out ); + + out << "\n" + << END_TAG << "\n"; + conn.write_string_to_fd( out.str() ); + } } Index: src/emacs_mode/FnTagCommand.cc =================================================================== --- src/emacs_mode/FnTagCommand.cc (revision 0) +++ src/emacs_mode/FnTagCommand.cc (working copy) @@ -0,0 +1,63 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#include "NetworkConnection.hh" +#include "FnTagCommand.hh" +#include "emacs.hh" + +#include "../UserFunction.hh" + +void FnTagCommand::run_command( NetworkConnection &conn, const std::vector &args ) +{ + std::string name = args[1]; + + std::stringstream out; + + UCS_string ucs_name = ucs_string_from_string( name ); + NamedObject *obj = Workspace::lookup_existing_name( ucs_name ); + if( obj == NULL ) { + out << "undefined\n"; + } + else if( !obj->is_user_defined() ) { + out << "system function\n"; + } + else { + const Function *function = obj->get_function(); + if( function == NULL ) { + out << "symbol is not a function\n"; + } + else if( function->get_exec_properties()[0] != 0 ) { + out << "function is not executable\n"; + } + else { + const UserFunction *ufun = function->get_ufun1(); + if( ufun == NULL ) { + out << "not a user function"; + } + else { + UTF8_string creator = ufun->get_creator(); + out << "tag\n" << creator.c_str() << "\n"; + } + } + } + out << END_TAG << "\n"; + + conn.write_string_to_fd( out.str() ); +} Index: src/emacs_mode/FnTagCommand.hh =================================================================== --- src/emacs_mode/FnTagCommand.hh (revision 0) +++ src/emacs_mode/FnTagCommand.hh (working copy) @@ -0,0 +1,32 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#ifndef FN_TAG_COMMAND_HH +#define FN_TAG_COMMAND_HH + +#include "NetworkCommand.hh" + +class FnTagCommand : public NetworkCommand { +public: + FnTagCommand( std::string name_in ) : NetworkCommand( name_in ) {}; + virtual void run_command( NetworkConnection &conn, const std::vector &args ); +}; + +#endif Index: src/emacs_mode/FollowCommand.cc =================================================================== --- src/emacs_mode/FollowCommand.cc (revision 0) +++ src/emacs_mode/FollowCommand.cc (working copy) @@ -0,0 +1,139 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#include "emacs.hh" +#include "NetworkConnection.hh" +#include "FollowCommand.hh" + +#include "TraceData.hh" +#include "LockWrapper.hh" +#include "../Symbol.hh" + +#include +#include +#include +#include +#include + +typedef map SymbolTraceMap; + +SymbolTraceMap trace_data; +pthread_mutex_t trace_data_lock = PTHREAD_MUTEX_INITIALIZER; + +void symbol_assignment( const Symbol &symbol, Symbol_Event ev ) +{ + LockWrapper lock_wrapper( &trace_data_lock ); + + SymbolTraceMap::iterator it = trace_data.find( &symbol ); + if( it != trace_data.end() ) { + TraceData *data = it->second; + data->send_update( ev ); + } +} + +static bool parse_boolean( string arg ) +{ + if( arg == "on" ) { + return true; + } + else if( arg == "off" ) { + return false; + } + else { + throw ConnectionError( "unexpected argument to trace" ); + } +} + +static TraceData *find_trace_data( Symbol *symbol ) +{ + TraceData *data; + SymbolTraceMap::iterator it = trace_data.find( symbol ); + if( it == trace_data.end() ) { + data = new TraceData( symbol ); + trace_data[symbol] = data; + } + else { + data = it->second; + } + + return data; +} + +static void enable_trace( NetworkConnection &conn, Symbol *symbol, int cr_level = -1 ) +{ + LockWrapper lock_wrapper( &trace_data_lock ); + + TraceData *data = find_trace_data( symbol ); + data->add_listener( &conn, cr_level ); + + stringstream out; + out << "enabled" << endl; + Value_P v = symbol->get_value(); + TraceData::display_value_for_trace( out, v, cr_level ); + conn.send_reply( out.str() ); +} + +static void disable_trace( NetworkConnection &conn, Symbol *symbol ) +{ + LockWrapper lock_wrapper( &trace_data_lock ); + + TraceData *data = find_trace_data( symbol ); + data->remove_listener( &conn ); + + conn.send_reply( "disabled" ); +} + +void FollowCommand::run_command( NetworkConnection &conn, const std::vector &args ) +{ + int num_args = args.size(); + if( num_args < 3 || num_args > 4 ) { + throw ConnectionError( "Wrong number of arguments to trace" ); + } + + SymbolTable &symbol_table = const_cast( Workspace::get_symbol_table() ); + Symbol *symbol = symbol_table.lookup_existing_symbol( ucs_string_from_string( args[1] ) ); + if( symbol == NULL ) { + conn.send_reply( "undefined" ); + return; + } + if( symbol->get_nc() != NC_VARIABLE ) { + conn.send_reply( "wrong type" ); + return; + } + + bool enable = parse_boolean( args[2] ); + if( enable ) { + int cr_level = -1; + if( num_args > 3 ) { + string cr_arg = args[3]; + if( cr_arg != "off" ) { + long v = strtol( cr_arg.c_str(), NULL, 10 ); + if( v == LONG_MAX && errno == ERANGE ) { + throw ConnectionError( "Illegal CR level argument to follow command" ); + } + cr_level = v; + } + } + enable_trace( conn, symbol, cr_level ); + } + else { + disable_trace( conn, symbol ); + } +} Index: src/emacs_mode/FollowCommand.hh =================================================================== --- src/emacs_mode/FollowCommand.hh (revision 0) +++ src/emacs_mode/FollowCommand.hh (working copy) @@ -0,0 +1,38 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#ifndef FOLLOW_COMMAND_HH +#define FOLLOW_COMMAND_HH + +#include "NetworkCommand.hh" +#include "TraceData.hh" +#include "../Symbol.hh" + +class FollowCommand : public NetworkCommand { +public: + FollowCommand( std::string name_in ) : NetworkCommand( name_in ) {}; + virtual void run_command( NetworkConnection &conn, const std::vector &args ); +}; + +typedef map SymbolTraceMap; + +void symbol_assignment( const Symbol &symbol, Symbol_Event ev ); + +#endif Index: src/emacs_mode/GetVarCommand.cc =================================================================== --- src/emacs_mode/GetVarCommand.cc (revision 168) +++ src/emacs_mode/GetVarCommand.cc (working copy) @@ -36,11 +36,13 @@ static void send_reply( NetworkConnection &conn, std::string message ) { - conn.write_string_to_fd( message ); - conn.write_string_to_fd( "\n" END_TAG "\n" ); + stringstream out; + out << message << "\n" + << END_TAG << "\n"; + conn.write_string_to_fd( out.str() ); } -static void escape_char( stringstream &out, Unicode unicode ) +static void escape_char( ostream &out, Unicode unicode ) { if( unicode == '\\' ) { out << "\\\\"; @@ -54,7 +56,7 @@ } } -void skalar_value_to_el( stringstream &out, Value_P value ) +void skalar_value_to_el( ostream &out, Value_P value ) { Cell &cell = value->get_ravel( 0 ); if( cell.is_integer_cell() ) { @@ -74,9 +76,9 @@ } } -void apl_value_to_el( stringstream &out, Value_P value ); +static void apl_value_to_el( ostream &out, Value_P value ); -void output_onelevel( stringstream &out, Value_P value, int level, int start, int end ) +static void output_onelevel( ostream &out, Value_P value, int level, int start, int end ) { const Shape &shape = value->get_shape(); int size = shape.get_shape_item( level ); @@ -97,7 +99,7 @@ out << ")\n"; } -void apl_value_to_el( stringstream &out, Value_P value ) +static void apl_value_to_el( ostream &out, Value_P value ) { const Shape &shape = value->get_shape(); if( value->is_empty() ) { @@ -164,12 +166,12 @@ Value_P value = symbol->get_value(); try { stringstream out; + out.precision( 20 ); out << "content\n"; apl_value_to_el( out, value ); - conn.write_string_to_fd( out.str() ); + conn.send_reply( out.str() ); } catch( InvalidSymbolContent &exception ) { - conn.write_string_to_fd( exception.get_message() ); + conn.send_reply( exception.get_message() ); } - conn.write_string_to_fd( "\n" END_TAG "\n" ); } Index: src/emacs_mode/Listener.cc =================================================================== --- src/emacs_mode/Listener.cc (revision 0) +++ src/emacs_mode/Listener.cc (working copy) @@ -0,0 +1,37 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#include "Listener.hh" +#include "TcpListener.hh" +#include "UnixSocketListener.hh" + +Listener *Listener::create_listener( int port ) +{ + Listener *ret; + + if( port >= 0 ) { + ret = new TcpListener( port ); + } + else { + ret = new UnixSocketListener(); + } + + return ret; +} Index: src/emacs_mode/Listener.hh =================================================================== --- src/emacs_mode/Listener.hh (revision 0) +++ src/emacs_mode/Listener.hh (working copy) @@ -0,0 +1,57 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#ifndef LISTENER_HH +#define LISTENER_HH + +#include + +#include "network.hh" + +typedef void *ThreadFunction( void * ); + +class Listener { +public: + Listener() { register_listener( this ); } + virtual ~Listener() { unregister_listener( this ); } + virtual std::string start( void ) = 0; + virtual void wait_for_connection( void ) = 0; + virtual void close_connection( void ) = 0; + static Listener *create_listener( int port ); + virtual void set_thread( pthread_t thread_id_in ) { thread_id = thread_id_in; } + virtual pthread_t get_thread( void ) { return thread_id; } + +protected: + pthread_t thread_id; +}; + +class ListenerWrapper { +public: + ListenerWrapper( Listener *listener_in ) : listener( listener_in ) { } + + virtual ~ListenerWrapper() { + listener->close_connection(); + } + +private: + Listener *listener; +}; + +#endif Index: src/emacs_mode/LockWrapper.cc =================================================================== --- src/emacs_mode/LockWrapper.cc (revision 0) +++ src/emacs_mode/LockWrapper.cc (working copy) @@ -0,0 +1,31 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#include "LockWrapper.hh" + +LockWrapper::LockWrapper( pthread_mutex_t *lock_in ) : lock( lock_in ) +{ + pthread_mutex_lock( lock ); +} + +LockWrapper::~LockWrapper() +{ + pthread_mutex_unlock( lock ); +} Index: src/emacs_mode/LockWrapper.hh =================================================================== --- src/emacs_mode/LockWrapper.hh (revision 0) +++ src/emacs_mode/LockWrapper.hh (working copy) @@ -0,0 +1,35 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#ifndef LOCK_WRAPPER_HH +#define LOCK_WRAPPER_HH + +#include + +class LockWrapper { +public: + LockWrapper( pthread_mutex_t *lock_in ); + virtual ~LockWrapper(); + +private: + pthread_mutex_t *lock; +}; + +#endif Index: src/emacs_mode/Makefile.am =================================================================== --- src/emacs_mode/Makefile.am (revision 168) +++ src/emacs_mode/Makefile.am (working copy) @@ -5,13 +5,21 @@ DefCommand.cc DefCommand.hh \ emacs.cc emacs.hh \ FnCommand.cc FnCommand.hh \ + FnTagCommand.cc FnTagCommand.hh \ + FollowCommand.cc FollowCommand.hh \ GetVarCommand.cc GetVarCommand.hh \ + Listener.cc Listener.hh \ + LockWrapper.cc LockWrapper.hh \ network.cc network.hh \ NetworkCommand.hh \ NetworkConnection.cc NetworkConnection.hh \ - RunCommand.cc RunCommand.hh \ SicCommand.cc SicCommand.hh \ SiCommand.cc SiCommand.hh \ + SystemFnCommand.cc SystemFnCommand.hh \ + SystemVariableCommand.cc SystemVariableCommand.hh \ + TcpListener.cc TcpListener.hh \ + TraceData.cc TraceData.hh \ + UnixSocketListener.cc UnixSocketListener.hh \ util.cc util.hh \ - VariablesCommand.cc VariablesCommand.hh - + VariablesCommand.cc VariablesCommand.hh \ + VersionCommand.cc VersionCommand.hh Index: src/emacs_mode/Makefile.in =================================================================== --- src/emacs_mode/Makefile.in (revision 168) +++ src/emacs_mode/Makefile.in (working copy) @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.6 from Makefile.am. +# Makefile.in generated by automake 1.13.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software -# Foundation, Inc. +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -16,23 +15,51 @@ @SET_MAKE@ VPATH = @srcdir@ -am__make_dryrun = \ - { \ - am__dry=no; \ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ - echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ - | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ - *) \ - for am__flg in $$MAKEFLAGS; do \ - case $$am__flg in \ - *=*|--*) ;; \ - *n*) am__dry=yes; break;; \ - esac; \ - done;; \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ - test $$am__dry = yes; \ - } + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -52,7 +79,8 @@ build_triplet = @build@ host_triplet = @host@ subdir = src/emacs_mode -DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in AUTHORS +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp AUTHORS ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \ @@ -99,31 +127,68 @@ LTLIBRARIES = $(pkglib_LTLIBRARIES) libemacs_la_LIBADD = am_libemacs_la_OBJECTS = DefCommand.lo emacs.lo FnCommand.lo \ - GetVarCommand.lo network.lo NetworkConnection.lo RunCommand.lo \ - SicCommand.lo SiCommand.lo util.lo VariablesCommand.lo + FnTagCommand.lo FollowCommand.lo GetVarCommand.lo Listener.lo \ + LockWrapper.lo network.lo NetworkConnection.lo SicCommand.lo \ + SiCommand.lo SystemFnCommand.lo SystemVariableCommand.lo \ + TcpListener.lo TraceData.lo UnixSocketListener.lo util.lo \ + VariablesCommand.lo VersionCommand.lo libemacs_la_OBJECTS = $(am_libemacs_la_OBJECTS) +AM_V_lt = $(address@hidden@) +am__v_lt_ = $(address@hidden@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(address@hidden@) +am__v_P_ = $(address@hidden@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(address@hidden@) +am__v_GEN_ = $(address@hidden@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(address@hidden@) +am__v_at_ = $(address@hidden@) +am__v_at_0 = @ +am__v_at_1 = DEFAULT_INCLUDES = address@hidden@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ - $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(address@hidden@) +am__v_CXX_ = $(address@hidden@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = CXXLD = $(CXX) -CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \ - $(LDFLAGS) -o $@ +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(address@hidden@) +am__v_CXXLD_ = $(address@hidden@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ - $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(address@hidden@) +am__v_CC_ = $(address@hidden@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = CCLD = $(CC) -LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ - $(LDFLAGS) -o $@ +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(address@hidden@) +am__v_CCLD_ = $(address@hidden@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = SOURCES = $(libemacs_la_SOURCES) DIST_SOURCES = $(libemacs_la_SOURCES) am__can_run_installinfo = \ @@ -131,11 +196,29 @@ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASSERT_LEVEL_WANTED = @ASSERT_LEVEL_WANTED@ AUTOCONF = @AUTOCONF@ @@ -178,6 +261,7 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBICONV = @LIBICONV@ @@ -287,15 +371,24 @@ DefCommand.cc DefCommand.hh \ emacs.cc emacs.hh \ FnCommand.cc FnCommand.hh \ + FnTagCommand.cc FnTagCommand.hh \ + FollowCommand.cc FollowCommand.hh \ GetVarCommand.cc GetVarCommand.hh \ + Listener.cc Listener.hh \ + LockWrapper.cc LockWrapper.hh \ network.cc network.hh \ NetworkCommand.hh \ NetworkConnection.cc NetworkConnection.hh \ - RunCommand.cc RunCommand.hh \ SicCommand.cc SicCommand.hh \ SiCommand.cc SiCommand.hh \ + SystemFnCommand.cc SystemFnCommand.hh \ + SystemVariableCommand.cc SystemVariableCommand.hh \ + TcpListener.cc TcpListener.hh \ + TraceData.cc TraceData.hh \ + UnixSocketListener.cc UnixSocketListener.hh \ util.cc util.hh \ - VariablesCommand.cc VariablesCommand.hh + VariablesCommand.cc VariablesCommand.hh \ + VersionCommand.cc VersionCommand.hh all: all-am @@ -331,6 +424,7 @@ $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): + install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ @@ -357,14 +451,17 @@ clean-pkglibLTLIBRARIES: -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) - @list='$(pkglib_LTLIBRARIES)'; for p in $$list; do \ - dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ - test "$$dir" != "$$p" || dir=.; \ - echo "rm -f \"$${dir}/so_locations\""; \ - rm -f "$${dir}/so_locations"; \ - done + @list='$(pkglib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + libemacs.la: $(libemacs_la_OBJECTS) $(libemacs_la_DEPENDENCIES) $(EXTRA_libemacs_la_DEPENDENCIES) - $(CXXLINK) -rpath $(pkglibdir) $(libemacs_la_OBJECTS) $(libemacs_la_LIBADD) $(LIBS) + $(AM_V_CXXLD)$(CXXLINK) -rpath $(pkglibdir) $(libemacs_la_OBJECTS) $(libemacs_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -374,36 +471,45 @@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ address@hidden@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ @AMDEP_TRUE@@am__include@ @address@hidden/$(DEPDIR)/address@hidden@ .cc.o: address@hidden@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< address@hidden@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po address@hidden@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ address@hidden@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< address@hidden@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po address@hidden@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ address@hidden@ $(CXXCOMPILE) -c -o $@ $< address@hidden@ $(address@hidden@)$(CXXCOMPILE) -c -o $@ $< .cc.obj: address@hidden@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` address@hidden@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po address@hidden@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ address@hidden@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` address@hidden@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po address@hidden@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ address@hidden@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` address@hidden@ $(address@hidden@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cc.lo: address@hidden@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< address@hidden@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo address@hidden@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ address@hidden@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< address@hidden@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo address@hidden@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ address@hidden@ $(LTCXXCOMPILE) -c -o $@ $< address@hidden@ $(address@hidden@)$(LTCXXCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo @@ -411,26 +517,15 @@ clean-libtool: -rm -rf .libs _libs -ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ - mkid -fID $$unique -tags: TAGS +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags -TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ + $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ @@ -442,15 +537,11 @@ $$unique; \ fi; \ fi -ctags: CTAGS -CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique @@ -459,7 +550,22 @@ here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags @@ -603,19 +709,20 @@ .MAKE: install-am install-strip -.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ - clean-libtool clean-pkglibLTLIBRARIES ctags distclean \ - distclean-compile distclean-generic distclean-libtool \ - distclean-tags distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am install-man \ - install-pdf install-pdf-am install-pkglibLTLIBRARIES \ - install-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-compile \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags uninstall uninstall-am uninstall-pkglibLTLIBRARIES +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-pkglibLTLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-pkglibLTLIBRARIES install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-pkglibLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. Index: src/emacs_mode/NetworkConnection.cc =================================================================== --- src/emacs_mode/NetworkConnection.cc (revision 168) +++ src/emacs_mode/NetworkConnection.cc (working copy) @@ -21,6 +21,7 @@ #include "emacs.hh" #include "util.hh" #include "NetworkConnection.hh" +#include "LockWrapper.hh" #include "../UserFunction.hh" #include "../Quad_FX.hh" #include "SiCommand.hh" @@ -29,7 +30,11 @@ #include "DefCommand.hh" #include "GetVarCommand.hh" #include "VariablesCommand.hh" -#include "RunCommand.hh" +#include "FnTagCommand.hh" +#include "VersionCommand.hh" +#include "FollowCommand.hh" +#include "SystemFnCommand.hh" +#include "SystemVariableCommand.hh" #include #include @@ -42,7 +47,6 @@ #include #include - static void add_command( std::map &commands, NetworkCommand *command ) { commands.insert( std::pair( command->get_name(), command ) ); @@ -51,12 +55,19 @@ NetworkConnection::NetworkConnection( int socket_in ) : socket_fd(socket_in), buffer_pos(0), buffer_length(0) { + pthread_mutex_init( &connection_lock, NULL ); + add_command( commands, new SiCommand( "si" ) ); add_command( commands, new SicCommand( "sic" ) ); add_command( commands, new FnCommand( "fn" ) ); add_command( commands, new DefCommand( "def" ) ); add_command( commands, new GetVarCommand( "getvar" ) ); add_command( commands, new VariablesCommand( "variables" ) ); + add_command( commands, new FnTagCommand( "functiontag" ) ); + add_command( commands, new VersionCommand( "proto" ) ); + add_command( commands, new FollowCommand( "trace" ) ); + add_command( commands, new SystemFnCommand( "systemcommands" ) ); + add_command( commands, new SystemVariableCommand( "systemvariables" ) ); } NetworkConnection::~NetworkConnection() @@ -107,6 +118,8 @@ void NetworkConnection::write_string_to_fd( const std::string &s ) { + LockWrapper lock_wrapper( &connection_lock ); + const char *buf = s.c_str(); int n = strlen( buf ); int pos = 0; @@ -134,7 +147,7 @@ int NetworkConnection::process_command( const std::string &command ) { - LockWrapper lock; + ActiveWrapper lock; std::vector elements = split( command, ':' ); if( elements.size() > 0 ) { std::string operation = elements[0]; @@ -165,3 +178,20 @@ end = process_command( command ); } } + +void NetworkConnection::send_reply( const std::string &str ) +{ + std::stringstream out; + out << str << "\n" + << END_TAG << "\n"; + write_string_to_fd( out.str() ); +} + +void NetworkConnection::send_notification( const std::string &str ) +{ + std::stringstream out; + out << NOTIFICATION_START_TAG << "\n" + << str << "\n" + << NOTIFICATION_END_TAG << "\n"; + write_string_to_fd( out.str() ); +} Index: src/emacs_mode/NetworkConnection.hh =================================================================== --- src/emacs_mode/NetworkConnection.hh (revision 168) +++ src/emacs_mode/NetworkConnection.hh (working copy) @@ -21,6 +21,7 @@ #ifndef NETWORK_CONNECTION_HH #define NETWORK_CONNECTION_HH +#include #include #include #include @@ -35,6 +36,8 @@ std::string read_line_from_fd( void ); void write_string_to_fd( const std::string &s ); std::vector load_block( void ); + void send_reply( const std::string &message ); + void send_notification( const std::string &message ); private: int socket_fd; @@ -42,6 +45,7 @@ int buffer_pos; int buffer_length; std::map commands; + pthread_mutex_t connection_lock; int process_command( const std::string &command ); void show_si( void ); Index: src/emacs_mode/RunCommand.cc =================================================================== --- src/emacs_mode/RunCommand.cc (revision 168) +++ src/emacs_mode/RunCommand.cc (working copy) @@ -1,50 +0,0 @@ -/* - This file is part of GNU APL, a free implementation of the - ISO/IEC Standard 13751, "Programming Language APL, Extended" - - Copyright (C) 2014 Elias Mårtenson - - 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 . -*/ - -#include "RunCommand.hh" -#include "emacs.hh" -#include "NetworkConnection.hh" - -void RunCommand::run_command( NetworkConnection &conn, const std::vector &args ) -{ - stringstream out; - while( 1 ) { - std::string line = conn.read_line_from_fd(); - if( line == END_TAG ) { - break; - } - out << line << "\n"; - } - - Token result = Bif_F1_EXECUTE::execute_statement( ucs_string_from_string( out.str() ) ); - TokenTag tag = result.get_tag(); - - stringstream result_stream; - if( tag == TOK_ERROR ) { - result_stream << "error:" << result.get_int_val(); - } - else { - result_stream << "result:NOT-IMPL"; - } - - result_stream << "\n" << END_TAG << "\n"; - - conn.write_string_to_fd( result_stream.str() ); -} Index: src/emacs_mode/RunCommand.hh =================================================================== --- src/emacs_mode/RunCommand.hh (revision 168) +++ src/emacs_mode/RunCommand.hh (working copy) @@ -1,32 +0,0 @@ -/* - This file is part of GNU APL, a free implementation of the - ISO/IEC Standard 13751, "Programming Language APL, Extended" - - Copyright (C) 2014 Elias Mårtenson - - 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 . -*/ - -#ifndef RUN_COMMAND_HH -#define RUN_COMMAND_HH - -#include "NetworkCommand.hh" - -class RunCommand : public NetworkCommand { -public: - RunCommand( std::string name_in ) : NetworkCommand( name_in ) {}; - virtual void run_command( NetworkConnection &conn, const std::vector &args ); -}; - -#endif Index: src/emacs_mode/SystemFnCommand.cc =================================================================== --- src/emacs_mode/SystemFnCommand.cc (revision 0) +++ src/emacs_mode/SystemFnCommand.cc (working copy) @@ -0,0 +1,34 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#include "NetworkConnection.hh" +#include "SystemFnCommand.hh" +#include "emacs.hh" + +void SystemFnCommand::run_command( NetworkConnection &conn, const std::vector &args ) +{ + stringstream out; + +#define cmd_def(NAME, CMD, ARG) out << NAME << "\n"; +#include "../Command.def" + + out << END_TAG << "\n"; + conn.write_string_to_fd( out.str() ); +} Index: src/emacs_mode/SystemFnCommand.hh =================================================================== --- src/emacs_mode/SystemFnCommand.hh (revision 0) +++ src/emacs_mode/SystemFnCommand.hh (working copy) @@ -0,0 +1,32 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#ifndef SYSTEM_FN_COMMAND_HH +#define SYSTEM_FN_COMMAND_HH + +#include "NetworkCommand.hh" + +class SystemFnCommand : public NetworkCommand { +public: + SystemFnCommand( std::string name_in ) : NetworkCommand( name_in ) {}; + virtual void run_command( NetworkConnection &conn, const std::vector &args ); +}; + +#endif Index: src/emacs_mode/SystemVariableCommand.cc =================================================================== --- src/emacs_mode/SystemVariableCommand.cc (revision 0) +++ src/emacs_mode/SystemVariableCommand.cc (working copy) @@ -0,0 +1,36 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#include "NetworkConnection.hh" +#include "SystemVariableCommand.hh" +#include "emacs.hh" + +void SystemVariableCommand::run_command( NetworkConnection &conn, const std::vector &args ) +{ + stringstream out; + +#define ro_sv_def(VAR) out << id_name( ID_ ## VAR ) << "\n"; +#define rw_sv_def(VAR) out << id_name( ID_ ## VAR ) << "\n"; +#define sf_def(VAR) out << id_name( ID_ ## VAR ) << "\n"; +#include "../SystemVariable.def" + + out << END_TAG << "\n"; + conn.write_string_to_fd( out.str() ); +} Index: src/emacs_mode/SystemVariableCommand.hh =================================================================== --- src/emacs_mode/SystemVariableCommand.hh (revision 0) +++ src/emacs_mode/SystemVariableCommand.hh (working copy) @@ -0,0 +1,32 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#ifndef SYSTEM_VARIABLE_COMMAND_HH +#define SYSTEM_VARIABLE_COMMAND_HH + +#include "NetworkCommand.hh" + +class SystemVariableCommand : public NetworkCommand { +public: + SystemVariableCommand( std::string name_in ) : NetworkCommand( name_in ) {}; + virtual void run_command( NetworkConnection &conn, const std::vector &args ); +}; + +#endif Index: src/emacs_mode/TcpListener.cc =================================================================== --- src/emacs_mode/TcpListener.cc (revision 0) +++ src/emacs_mode/TcpListener.cc (working copy) @@ -0,0 +1,142 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#include "emacs.hh" +#include "network.hh" +#include "NetworkConnection.hh" +#include "TcpListener.hh" + +#include +#include +#include +#include +#include +#include +#include + +std::string TcpListener::start( void ) +{ + struct addrinfo *addr; + int ret; + + stringstream serv_name; + serv_name << port; + + struct addrinfo hints = { 0 }; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + hints.ai_flags = 0; + hints.ai_addrlen = 0; + hints.ai_addr = NULL; + hints.ai_canonname = NULL; + hints.ai_next = NULL; + + ret = getaddrinfo( "127.0.0.1", serv_name.str().c_str(), &hints, &addr ); + if( ret != 0 ) { + stringstream errmsg; + errmsg << "Error looking up listener host: " << gai_strerror( ret ); + Workspace::more_error() = UCS_string( errmsg.str().c_str() ); + DOMAIN_ERROR; + } + + AddrWrapper addrWrapper( addr ); + + server_socket = socket( AF_INET, SOCK_STREAM, 0 ); + if( server_socket == -1 ) { + stringstream errmsg; + errmsg << "Error creating socket: " << strerror( errno ); + Workspace::more_error() = UCS_string( errmsg.str().c_str() ); + DOMAIN_ERROR; + } + + int v = 1; + if( setsockopt( server_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&v, sizeof( v ) ) == -1 ) { + stringstream errmsg; + errmsg << "Error setting SO_REUSEADDR parameter: " << strerror( errno ); + close( server_socket ); + Workspace::more_error() = UCS_string( errmsg.str().c_str() ); + DOMAIN_ERROR; + } + + if( bind( server_socket, addr->ai_addr, addr->ai_addrlen ) == -1 ) { + stringstream errmsg; + errmsg << "Unable to bind to port " << port << ": " << strerror( errno ); + close( server_socket ); + Workspace::more_error() = UCS_string( errmsg.str().c_str() ); + DOMAIN_ERROR; + } + + if( listen( server_socket, 2 ) == -1 ) { + stringstream errmsg; + errmsg << "Error calling accept: " << strerror( errno ) << endl; + close( server_socket ); + Workspace::more_error() = UCS_string( errmsg.str().c_str() ); + DOMAIN_ERROR; + } + + struct sockaddr_in listen_address; + socklen_t listen_address_len = sizeof( listen_address ); + if( getsockname( server_socket, (struct sockaddr *)&listen_address, &listen_address_len ) == -1 ) { + stringstream errmsg; + errmsg << "Error getting port number of socket: " << strerror( errno ) << endl; + close( server_socket ); + Workspace::more_error() = UCS_string( errmsg.str().c_str() ); + DOMAIN_ERROR; + } + + stringstream info_stream; + info_stream << "mode:tcp addr:" << ntohs( listen_address.sin_port ); + string conninfo = info_stream.str(); + return conninfo; +} + +void TcpListener::wait_for_connection( void ) +{ + while( true ) { + struct sockaddr addr; + socklen_t length; + int socket = accept( server_socket, &addr, &length ); + if( socket == -1 ) { + if( !closing ) { + CERR << "Error accepting network connection: " << strerror( errno ) << endl; + } + break; + } + else { + NetworkConnection *conn = new NetworkConnection( socket ); + pthread_t thread_id; + int ret = pthread_create( &thread_id, NULL, connection_loop, conn ); + if( ret != 0 ) { + CERR << "Error creating thread" << endl; + delete conn; + } + } + } +} + +void TcpListener::close_connection( void ) +{ + closing = true; + + if( server_socket != 0 ) { + close( server_socket ); + } +} Index: src/emacs_mode/TcpListener.hh =================================================================== --- src/emacs_mode/TcpListener.hh (revision 0) +++ src/emacs_mode/TcpListener.hh (working copy) @@ -0,0 +1,40 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#ifndef TCP_LISTENER_HH +#define TCP_LISTENER_HH + +#include "Listener.hh" + +class TcpListener : public Listener { +public: + TcpListener( int port_in ) : port( port_in ), closing( false ) {}; + virtual ~TcpListener() {}; + virtual std::string start( void ); + virtual void wait_for_connection( void ); + virtual void close_connection( void ); + +private: + int port; + int server_socket; + bool closing; +}; + +#endif Index: src/emacs_mode/TraceData.cc =================================================================== --- src/emacs_mode/TraceData.cc (revision 0) +++ src/emacs_mode/TraceData.cc (working copy) @@ -0,0 +1,87 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#include "TraceData.hh" +#include "LockWrapper.hh" +#include "FollowCommand.hh" +#include "../Workspace.hh" + +TraceData::TraceData( Symbol *symbol_in ) : symbol( symbol_in ) +{ +} + +void TraceData::add_listener( NetworkConnection *connection, int cr_level ) +{ + Assert( active_listeners.find( connection ) == active_listeners.end() ); + + if( active_listeners.empty() ) { + symbol->set_monitor_callback( symbol_assignment ); + } + + active_listeners.insert( pair( connection, cr_level ) ); +} + +void TraceData::remove_listener( NetworkConnection *connection ) +{ + int n = active_listeners.erase( connection ); + Assert( n == 1 ); + + if( active_listeners.empty() ) { + symbol->set_monitor_callback( NULL ); + } +} + +void TraceData::display_value_for_trace( ostream &out, const Value_P &value, int cr_level ) +{ + if( cr_level < 0 ) { + value->print( out ); + } + else { + if( cr_level < 1 || cr_level > 8 ) { + throw new ConnectionError( "Illegal CR level" ); + } + Value_P cr_formatted = Quad_CR::do_CR( cr_level, *value ); + out << *cr_formatted; + } +} + +void TraceData::send_update( Symbol_Event ev ) +{ + const Value_P v = symbol->get_value(); + + for( map::iterator it = active_listeners.begin() + ; it != active_listeners.end() + ; it++ ) { + NetworkConnection *conn = it->first; + + stringstream out; + if( ev == SEV_ERASED ) { + out << "sev_erased" << endl << symbol->get_name() << endl; + } + else { + out << "symbol_update" << endl << symbol->get_name() << endl; + int cr_level = it->second.get_cr_level(); + display_value_for_trace( out, v, cr_level ); + } + + string str = out.str(); + conn->send_notification( str ); + } +} Index: src/emacs_mode/TraceData.hh =================================================================== --- src/emacs_mode/TraceData.hh (revision 0) +++ src/emacs_mode/TraceData.hh (working copy) @@ -0,0 +1,55 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#ifndef TRACE_DATA_HH +#define TRACE_DATA_HH + +#include "emacs.hh" +#include "NetworkConnection.hh" +#include "pthread.h" +#include "../Symbol.hh" + +#include +#include + +class TraceDataEntry { +public: + TraceDataEntry( int cr_level_in ) : cr_level( cr_level_in ) {} + int get_cr_level( void ) { return cr_level; } + +private: + int cr_level; +}; + +class TraceData { +public: + TraceData( Symbol *symbol_in ); + virtual ~TraceData() {}; + void add_listener( NetworkConnection *connection, int cr_level = -1 ); + void remove_listener( NetworkConnection *connection ); + void send_update( Symbol_Event ev ); + static void display_value_for_trace( ostream &out, const Value_P &value, int cr_level ); + +private: + Symbol *symbol; + map active_listeners; +}; + +#endif Index: src/emacs_mode/UnixSocketListener.cc =================================================================== --- src/emacs_mode/UnixSocketListener.cc (revision 0) +++ src/emacs_mode/UnixSocketListener.cc (working copy) @@ -0,0 +1,179 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#include "emacs.hh" +#include "network.hh" +#include "UnixSocketListener.hh" +#include "NetworkConnection.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +UnixSocketListener::~UnixSocketListener() +{ +} + +std::string UnixSocketListener::start( void ) +{ + server_socket = socket( AF_UNIX, SOCK_STREAM, 0 ); + if( server_socket == -1 ) { + stringstream errmsg; + errmsg << "Error creating unix domain socket: " << strerror( errno ) << endl; + Workspace::more_error() = UCS_string( errmsg.str().c_str() ); + DOMAIN_ERROR; + } + + stringstream name; + name << "/tmp/gnu_apl_conn_" << getpid(); + filename = name.str(); + + unlink( filename.c_str() ); + + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy( addr.sun_path, filename.c_str(), sizeof( addr.sun_path ) ); + if( bind( server_socket, (struct sockaddr *)&addr, sizeof( addr ) ) == -1 ) { + stringstream errmsg; + errmsg << "Error binding unix domain socket: " << strerror( errno ) << endl; + close( server_socket ); + Workspace::more_error() = UCS_string( errmsg.str().c_str() ); + DOMAIN_ERROR; + } + + initialised = true; + + if( chmod( filename.c_str(), 0600 ) == -1 ) { + stringstream errmsg; + errmsg << "Error setting permissions: " << strerror( errno ) << endl; + close( server_socket ); + Workspace::more_error() = UCS_string( errmsg.str().c_str() ); + DOMAIN_ERROR; + } + + if( listen( server_socket, 2 ) == -1 ) { + stringstream errmsg; + errmsg << "Error starting listener on unix domain socket: " << strerror( errno ) << endl; + close( server_socket ); + Workspace::more_error() = UCS_string( errmsg.str().c_str() ); + DOMAIN_ERROR; + } + + stringstream info_stream; + info_stream << "mode:unix addr:" << name.str(); + string conninfo = info_stream.str(); + return conninfo; +} + +void UnixSocketListener::wait_for_connection( void ) +{ + int pipe_fd[2]; + if( pipe( pipe_fd ) == -1 ) { + CERR << "Error creating pipe" << endl; + return; + } + + notification_fd = pipe_fd[1]; + + while( true ) { + struct pollfd fds[2]; + fds[0].fd = server_socket; + fds[0].events = POLLIN | POLLPRI; + fds[1].fd = pipe_fd[0]; + fds[1].events = POLLIN | POLLPRI; + + int ret = poll( fds, 2, -1 ); + if( ret == -1 ) { + CERR << "Error while waiting for connection: " << strerror( errno ) << endl; + break; + } + if( ret == 0 ) { + CERR << "Timed out while waiting for incoming connection" << endl; + break; + } + + if( fds[1].revents & (POLLIN | POLLPRI) ) { + CERR << "Connection interrupted (expected)" << endl; + break; + } + + if( fds[0].revents & POLLERR ) { + CERR << "Error on file handle" << endl; + break; + } + if( fds[0].revents & POLLHUP ) { + CERR << "Connection was closed" << endl; + break; + } + + if( fds[0].revents & (POLLIN | POLLPRI) ) { + struct sockaddr addr; + socklen_t length; + int socket = accept( server_socket, &addr, &length ); + if( socket == -1 ) { + if( !closing ) { + CERR << "Error accepting network connection: " << strerror( errno ) << endl; + } + break; + } + else { + NetworkConnection *conn = new NetworkConnection( socket ); + pthread_t thread_id; + int ret = pthread_create( &thread_id, NULL, connection_loop, conn ); + if( ret != 0 ) { + CERR << "Error creating thread" << endl; + delete conn; + } + } + } + else { + CERR << "Unexpected result from poll on socket" << endl; + } + } +} + +void UnixSocketListener::close_connection( void ) +{ + bool was_closing = closing; + closing = true; + if( initialised && !was_closing ) { + if( server_socket != 0 ) { + int v = 1; + if( write( notification_fd, &v, sizeof( v ) ) == -1 ) { + CERR << "Error writing message to notification file" << endl; + } + close( server_socket ); + } + + void *result; + pthread_join( thread_id, &result ); + + if( unlink( filename.c_str() ) == -1 ) { + CERR << "Error removing socket file name: " << filename << ": " << strerror( errno ) << endl; + } + } +} Index: src/emacs_mode/UnixSocketListener.hh =================================================================== --- src/emacs_mode/UnixSocketListener.hh (revision 0) +++ src/emacs_mode/UnixSocketListener.hh (working copy) @@ -0,0 +1,42 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#ifndef UNIX_SOCKET_LISTENER_HH +#define UNIX_SOCKET_LISTENER_HH + +#include "Listener.hh" + +class UnixSocketListener : public Listener { +public: + UnixSocketListener() : server_socket( 0 ), initialised( false ), closing( false ) {}; + virtual ~UnixSocketListener(); + virtual std::string start( void ); + virtual void wait_for_connection( void ); + virtual void close_connection( void ); + +private: + int server_socket; + std::string filename; + bool initialised; + bool closing; + int notification_fd; +}; + +#endif Index: src/emacs_mode/VariablesCommand.cc =================================================================== --- src/emacs_mode/VariablesCommand.cc (revision 168) +++ src/emacs_mode/VariablesCommand.cc (working copy) @@ -22,28 +22,29 @@ #include "VariablesCommand.hh" #include "emacs.hh" +enum TypeSpec { + ALL, + VARIABLE, + FUNCTION +}; + void VariablesCommand::run_command( NetworkConnection &conn, const std::vector &args ) { stringstream out; - bool all_types = false; - NameClass cls; - if( args.size() < 2 ) { - all_types = true; - } - else { + TypeSpec cls = ALL; + if( args.size() >= 2 ) { string typespec = args[1]; if( typespec == "variable" ) { - cls = NC_VARIABLE; + cls = VARIABLE; } else if( typespec == "function" ) { - cls = NC_FUNCTION; + cls = FUNCTION; } else { CERR << "Illegal variable type: " << typespec << endl; throw DisconnectedError( "Illegal variable type" ); } - all_types = false; } int num_symbols = Workspace::symbols_allocated(); @@ -51,11 +52,15 @@ Workspace::get_all_symbols( symbols, num_symbols ); for( int i = 0 ; i < num_symbols ; i++ ) { Symbol *symbol = symbols[i]; - if( !symbol->is_erased() && (all_types || symbol->top_of_stack()->name_class == cls) ) { - out << symbol->get_name() << "\n"; + if( !symbol->is_erased() ) { + NameClass symbol_nc = symbol->top_of_stack()->name_class; + if( (cls == ALL && (symbol_nc == NC_VARIABLE || symbol_nc == NC_FUNCTION || symbol_nc == NC_OPERATOR)) + || (cls == VARIABLE && symbol_nc == NC_VARIABLE) + || (cls == FUNCTION && (symbol_nc == NC_FUNCTION || symbol_nc == NC_OPERATOR)) ) { + out << symbol->get_name() << "\n"; + } } } - conn.write_string_to_fd( out.str() ); - conn.write_string_to_fd( END_TAG "\n" ); + conn.send_reply( out.str() ); } Index: src/emacs_mode/VersionCommand.cc =================================================================== --- src/emacs_mode/VersionCommand.cc (revision 0) +++ src/emacs_mode/VersionCommand.cc (working copy) @@ -0,0 +1,31 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#include "NetworkConnection.hh" +#include "VersionCommand.hh" +#include "emacs.hh" + +void VersionCommand::run_command( NetworkConnection &conn, const std::vector &args ) +{ + stringstream out; + out << PROTOCOL_VERSION << "\n" + << END_TAG << "\n"; + conn.write_string_to_fd( out.str() ); +} Index: src/emacs_mode/VersionCommand.hh =================================================================== --- src/emacs_mode/VersionCommand.hh (revision 0) +++ src/emacs_mode/VersionCommand.hh (working copy) @@ -0,0 +1,32 @@ +/* + This file is part of GNU APL, a free implementation of the + ISO/IEC Standard 13751, "Programming Language APL, Extended" + + Copyright (C) 2014 Elias Mårtenson + + 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 . +*/ + +#ifndef VERSION_COMMAND_HH +#define VERSION_COMMAND_HH + +#include "NetworkCommand.hh" + +class VersionCommand : public NetworkCommand { +public: + VersionCommand( std::string name_in ) : NetworkCommand( name_in ) {}; + virtual void run_command( NetworkConnection &conn, const std::vector &args ); +}; + +#endif Index: src/emacs_mode/emacs.cc =================================================================== --- src/emacs_mode/emacs.cc (revision 168) +++ src/emacs_mode/emacs.cc (working copy) @@ -36,7 +36,6 @@ void set_active( bool v ) { - return; pthread_mutex_lock( &apl_main_lock ); if( !apl_active && !v ) { std::cerr << "Unlocking while the lock is unlocked" << std::endl; @@ -100,7 +99,7 @@ { int port; if( B->is_empty() ) { - port = 7293; + port = 0; } else { port = B->get_ravel( 0 ).get_near_int( qct ); @@ -122,13 +121,19 @@ return Token(TOK_APL_VALUE1, Value::Str0_P); } +void close_fun( Cause cause ) +{ + close_listeners(); +} + void *get_function_mux( const char *function_name ) { - if (!strcmp(function_name, "get_signature")) return (void *)&get_signature; - if (!strcmp(function_name, "eval_B")) return (void *)&eval_B; - if (!strcmp(function_name, "eval_AB")) return (void *)&eval_AB; - if (!strcmp(function_name, "eval_XB")) return (void *)&eval_XB; - if (!strcmp(function_name, "eval_AXB")) return (void *)&eval_AXB; + if( strcmp( function_name, "get_signature" ) == 0 ) return (void *)&get_signature; + if( strcmp( function_name, "eval_B" ) == 0 ) return (void *)&eval_B; + if( strcmp( function_name, "eval_AB" ) == 0 ) return (void *)&eval_AB; + if( strcmp( function_name, "eval_XB" ) == 0 ) return (void *)&eval_XB; + if( strcmp( function_name, "eval_AXB" ) == 0 ) return (void *)&eval_AXB; + if( strcmp( function_name, "close_fun" ) == 0 ) return (void *)&close_fun; return 0; } @@ -139,3 +144,15 @@ UTF8_string utf( (const UTF8 *)buf, length ); return UCS_string( utf ); } + +Value_P make_string_cell( const std::string &string, const char *loc ) +{ + UCS_string s = ucs_string_from_string( string ); + Shape shape( s.size() ); + Value_P cell( new Value( shape, loc ) ); + for( int i = 0 ; i < s.size() ; i++ ) { + new (cell->next_ravel()) CharCell( s[i] ); + } + cell->check_value( loc ); + return cell; +} Index: src/emacs_mode/emacs.hh =================================================================== --- src/emacs_mode/emacs.hh (revision 168) +++ src/emacs_mode/emacs.hh (working copy) @@ -21,7 +21,7 @@ #ifndef EMACS_HH #define EMACS_HH -// #pragma GCC diagnostic push +#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunknown-pragmas" #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wunused-variable" @@ -36,15 +36,20 @@ void set_active( bool v ); +#define PROTOCOL_VERSION "1.3" + #define END_TAG "APL_NATIVE_END_TAG" +#define NOTIFICATION_START_TAG "APL_NATIVE_NOTIFICATION_START" +#define NOTIFICATION_END_TAG "APL_NATIVE_NOTIFICATION_END" -class LockWrapper +class ActiveWrapper { public: - LockWrapper() { set_active( true ); }; - virtual ~LockWrapper() { set_active( false ); }; + ActiveWrapper() { set_active( true ); }; + virtual ~ActiveWrapper() { set_active( false ); }; }; const UCS_string ucs_string_from_string( const std::string &string ); +Value_P make_string_cell( const std::string &string, const char *loc ); #endif Index: src/emacs_mode/network.cc =================================================================== --- src/emacs_mode/network.cc (revision 168) +++ src/emacs_mode/network.cc (working copy) @@ -20,29 +20,16 @@ #include "emacs.hh" #include "NetworkConnection.hh" +#include "Listener.hh" #include #include -#include -#include -#include -#include -class AddrWrapper { -public: - AddrWrapper(struct addrinfo *addr_in) : addr(addr_in) {} - virtual ~AddrWrapper() { freeaddrinfo( addr ); } +static std::vector registered_listeners; +static pthread_mutex_t registered_listeners_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t registered_listeners_cond = PTHREAD_COND_INITIALIZER; -private: - struct addrinfo *addr; -}; - -struct ListenerLoopData { - ListenerLoopData( int server_socket_in ) : server_socket( server_socket_in ) {} - int server_socket; -}; - -static void *connection_loop( void *arg ) +void *connection_loop( void *arg ) { std::auto_ptr connection( (NetworkConnection *)arg ); try { @@ -62,28 +49,10 @@ static void *listener_loop( void *arg ) { - ListenerLoopData *data = (ListenerLoopData *)arg; - int server_socket = data->server_socket; - delete data; + Listener *listener( (Listener *)arg ); - while( 1 ) { - struct sockaddr addr; - socklen_t length; - int socket = accept( server_socket, &addr, &length ); - if( socket == -1 ) { - CERR << "Error accepting network connection: " << strerror( errno ) << endl; - break; - } - else { - NetworkConnection *conn = new NetworkConnection( socket ); - pthread_t thread_id; - int ret = pthread_create( &thread_id, NULL, connection_loop, conn ); - if( ret != 0 ) { - CERR << "Error creating thread" << endl; - delete conn; - } - } - } + ListenerWrapper listener_wrapper( listener ); + listener->wait_for_connection(); return NULL; } @@ -91,69 +60,73 @@ Token start_listener( int port ) { pthread_t thread_id; - int server_socket; - struct addrinfo *addr; - int ret; - stringstream serv_name; - serv_name << port; + auto_ptr listener( Listener::create_listener( port ) ); - struct addrinfo hints; - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - hints.ai_flags = 0; - hints.ai_addrlen = 0; - hints.ai_addr = NULL; - hints.ai_canonname = NULL; - hints.ai_next = NULL; - - ret = getaddrinfo( "127.0.0.1", serv_name.str().c_str(), &hints, &addr ); - if( ret != 0 ) { - stringstream errmsg; - errmsg << "Error looking up listener host: " << gai_strerror( ret ); - Workspace::more_error() = UCS_string( errmsg.str().c_str() ); + string conninfo = listener->start(); + + int res = pthread_create( &thread_id, NULL, listener_loop, listener.get() ); + if( res != 0 ) { + Workspace::more_error() = UCS_string( "Unable to start network connection thread" ); DOMAIN_ERROR; } - AddrWrapper addrWrapper( addr ); + listener->set_thread( thread_id ); + listener.release(); - server_socket = socket( AF_INET, SOCK_STREAM, 0 ); - if( server_socket == -1 ) { - stringstream errmsg; - errmsg << "Error creating socket: " << strerror( errno ); - Workspace::more_error() = UCS_string( errmsg.str().c_str() ); - DOMAIN_ERROR; + COUT << "Network listener started. Connection information: " << conninfo << endl; + + return Token(TOK_APL_VALUE1, Value::Str0_P); +} + +void register_listener( Listener *listener ) +{ + pthread_mutex_lock( ®istered_listeners_lock ); + registered_listeners.push_back( listener ); + pthread_cond_broadcast( ®istered_listeners_cond ); + pthread_mutex_unlock( ®istered_listeners_lock ); +} + +void unregister_listener( Listener *listener ) +{ + pthread_mutex_lock( ®istered_listeners_lock ); + bool found = false; + for( vector::iterator i = registered_listeners.begin() ; i != registered_listeners.end() ; i++ ) { + if( *i == listener ) { + registered_listeners.erase( i ); + found = true; + break; + } } - int v = 1; - setsockopt( server_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&v, sizeof( v ) ); + Assert( found ); - if( bind( server_socket, addr->ai_addr, addr->ai_addrlen ) == -1 ) { - stringstream errmsg; - errmsg << "Unable to bind to port " << port << ": " << strerror( errno ); - Workspace::more_error() = UCS_string( errmsg.str().c_str() ); - DOMAIN_ERROR; + pthread_mutex_unlock( ®istered_listeners_lock ); + + pthread_cond_broadcast( ®istered_listeners_cond ); +// listener->close_connection(); +} + +void close_listeners( void ) +{ + vector to_be_closed; + pthread_mutex_lock( ®istered_listeners_lock ); + for( vector::iterator i = registered_listeners.begin() ; i != registered_listeners.end() ; i++ ) { + to_be_closed.push_back( *i ); } +// registered_listeners.clear(); + pthread_mutex_unlock( ®istered_listeners_lock ); - if( listen( server_socket, 2 ) == -1 ) { - close( server_socket ); - stringstream errmsg; - errmsg << "Error calling accept: " << strerror( errno ) << endl; - Workspace::more_error() = UCS_string( errmsg.str().c_str() ); - DOMAIN_ERROR; + for( vector::iterator i = to_be_closed.begin() ; i != to_be_closed.end() ; i ++ ) { + (*i)->close_connection(); +// delete *i; } - ListenerLoopData *listener_loop_data = new ListenerLoopData( server_socket ); - int res = pthread_create( &thread_id, NULL, listener_loop, listener_loop_data ); - if( res != 0 ) { - delete listener_loop_data; - close( server_socket ); - Workspace::more_error() = UCS_string( "Unable to start network connection thread" ); - DOMAIN_ERROR; +#if 0 + pthread_mutex_lock( ®istered_listeners_lock ); + while( registered_listeners.size() > 0 ) { + pthread_cond_wait( ®istered_listeners_cond, ®istered_listeners_lock ); } - - COUT << "Network listener started. Connection information: mode:tcp addr:" << port << endl; - - return Token(TOK_APL_VALUE1, Value::Str0_P); + pthread_mutex_unlock( ®istered_listeners_lock ); +#endif } Index: src/emacs_mode/network.hh =================================================================== --- src/emacs_mode/network.hh (revision 168) +++ src/emacs_mode/network.hh (working copy) @@ -21,6 +21,30 @@ #ifndef NETWORK_HH #define NETWORK_HH +#include "emacs.hh" + +#include +#include +#include +#include +#include + +class Listener; + +class AddrWrapper { +public: + AddrWrapper(struct addrinfo *addr_in) : addr(addr_in) {} + virtual ~AddrWrapper() { freeaddrinfo( addr ); } + +private: + struct addrinfo *addr; +}; + + Token start_listener( int port ); +void *connection_loop( void *arg ); +void register_listener( Listener *listener ); +void unregister_listener( Listener *listener ); +void close_listeners( void ); #endif