2 Commandline interpreter interface.
3 Copyright (c) 2003-2006 stefan kersten.
5 ====================================================================
7 SuperCollider real time audio synthesis system
8 Copyright (c) 2002 James McCartney. All rights reserved.
9 http://www.audiosynth.com
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 #include "SC_TerminalClient.h"
32 # define __GNU_LIBRARY__
34 # include "SC_Win32Utils.h"
38 # include <sys/param.h>
39 # include <sys/poll.h>
44 # include <readline/readline.h>
45 # include <readline/history.h>
54 #include "PyrKernel.h"
55 #include "PyrPrimitive.h"
58 #include "VMGlobals.h"
59 #include "SC_DirUtils.h" // for gIdeName
60 #include "SC_LibraryConfig.h"
64 static FILE* gPostDest
= stdout
;
66 static const int ticks_per_second
= 50; // every 20 milliseconds
68 SC_TerminalClient::SC_TerminalClient(const char* name
)
69 : SC_LanguageClient(name
),
70 mShouldBeRunning(false),
75 pthread_cond_init (&mCond
, NULL
);
76 pthread_mutex_init(&mSignalMutex
, NULL
);
77 pthread_mutex_init(&mInputMutex
, NULL
);
78 pthread_cond_init(&mInputCond
, NULL
);
81 SC_TerminalClient::~SC_TerminalClient()
83 pthread_cond_destroy (&mCond
);
84 pthread_mutex_destroy(&mSignalMutex
);
85 pthread_mutex_destroy(&mInputMutex
);
86 pthread_cond_destroy(&mInputCond
);
89 void SC_TerminalClient::postText(const char* str
, size_t len
)
91 fwrite(str
, sizeof(char), len
, gPostDest
);
94 void SC_TerminalClient::postFlush(const char* str
, size_t len
)
96 fwrite(str
, sizeof(char), len
, gPostDest
);
100 void SC_TerminalClient::postError(const char* str
, size_t len
)
102 fprintf(gPostDest
, "ERROR: ");
103 fwrite(str
, sizeof(char), len
, gPostDest
);
106 void SC_TerminalClient::flush()
111 void SC_TerminalClient::printUsage()
115 const size_t bufSize
= 128;
116 char memGrowBuf
[bufSize
];
117 char memSpaceBuf
[bufSize
];
119 snprintMemArg(memGrowBuf
, bufSize
, opt
.mMemGrow
);
120 snprintMemArg(memSpaceBuf
, bufSize
, opt
.mMemSpace
);
122 fprintf(stdout
, "Usage:\n %s [options] [file..] [-]\n\n", getName());
125 " -d <path> Set runtime directory\n"
126 " -D Enter daemon mode (no input)\n"
127 " -g <memory-growth>[km] Set heap growth (default %s)\n"
128 " -h Display this message and exit\n"
129 " -l <path> Set library configuration file\n"
130 " -m <memory-space>[km] Set initial heap size (default %s)\n"
131 " -r Call Main.run on startup\n"
132 " -s Call Main.stop on shutdown\n"
133 " -u <network-port-number> Set UDP listening port (default %d)\n"
134 " -i <ide-name> Specify IDE name (for enabling IDE-specific class code, default \"%s\")\n",
142 bool SC_TerminalClient::parseOptions(int& argc
, char**& argv
, Options
& opt
)
144 const char* optstr
= ":d:Dg:hl:m:rsu:i:";
147 // inhibit error reporting
150 while ((c
= getopt(argc
, argv
, optstr
)) != -1) {
153 opt
.mRuntimeDir
= optarg
;
159 if (!parseMemArg(optarg
, &opt
.mMemGrow
)) {
167 opt
.mLibraryConfigFile
= optarg
;
170 if (!parseMemArg(optarg
, &opt
.mMemSpace
)) {
179 opt
.mCallStop
= true;
182 if (!parsePortArg(optarg
, &opt
.mPort
)) {
197 ::post("%s: unknown error (getopt)\n", getName());
214 ::post("%s: invalid option -%c\n", getName(), optopt
);
219 ::post("%s: missing argument for option -%c\n", getName(), optopt
);
224 ::post("%s: invalid argument for option -%c -- %s\n", getName(), optopt
, optarg
);
229 int SC_TerminalClient::run(int argc
, char** argv
)
231 Options
& opt
= mOptions
;
233 if (!parseOptions(argc
, argv
, opt
)) {
237 // finish argv processing
238 const char* codeFile
= 0;
249 // read library configuration file
250 if (opt
.mLibraryConfigFile
) {
251 int argLength
= strlen(opt
.mLibraryConfigFile
);
252 if (strcmp(opt
.mLibraryConfigFile
+ argLength
- 5, ".yaml"))
253 SC_LanguageConfig::readLibraryConfig(opt
.mLibraryConfigFile
);
255 SC_LanguageConfig::readLibraryConfigYAML(opt
.mLibraryConfigFile
);
257 SC_LanguageConfig::readDefaultLibraryConfig();
259 // initialize runtime
263 mShouldBeRunning
= true;
267 if (codeFile
) executeFile(codeFile
);
268 if (opt
.mCallRun
) runMain();
275 if( shouldBeRunning() ) startInput();
276 if( shouldBeRunning() ) commandLoop();
281 if (opt
.mCallStop
) stopMain();
292 void SC_TerminalClient::quit(int code
)
295 mShouldBeRunning
= false;
298 void SC_TerminalClient::interpretCmdLine(PyrSymbol
* method
, SC_StringBuffer
& cmdLine
)
306 void SC_TerminalClient::interpretCmdLine(PyrSymbol
* method
, const char* cmdLine
)
314 void SC_TerminalClient::interpretCmdLine(PyrSymbol
* method
, const char *cmdLine
, size_t size
)
316 setCmdLine(cmdLine
, size
);
321 // WARNING: Call with input locked!
322 void SC_TerminalClient::interpretInput()
324 char *data
= mInputBuf
.getData();
325 int c
= mInputBuf
.getSize();
329 case kInterpretCmdLine
:
330 interpretCmdLine(s_interpretCmdLine
, data
, i
);
332 case kInterpretPrintCmdLine
:
333 interpretCmdLine(s_interpretPrintCmdLine
, data
, i
);
336 case kRecompileLibrary
:
350 if( mUseReadline
) pthread_cond_signal( &mInputCond
);
353 void SC_TerminalClient::onLibraryStartup()
355 SC_LanguageClient::onLibraryStartup();
357 base
= nextPrimitiveIndex();
358 definePrimitive(base
, index
++, "_Argv", &SC_TerminalClient::prArgv
, 1, 0);
359 definePrimitive(base
, index
++, "_Exit", &SC_TerminalClient::prExit
, 1, 0);
360 definePrimitive(base
, index
++, "_AppClock_SchedNotify",
361 &SC_TerminalClient::prScheduleChanged
, 1, 0);
362 definePrimitive(base
, index
++, "_Recompile", &SC_TerminalClient::prRecompile
, 1, 0);
365 void SC_TerminalClient::sendSignal( Signal sig
)
369 pthread_cond_signal( &mCond
);
373 void SC_TerminalClient::onQuit( int exitCode
)
376 postfl("main: quit request %i\n", exitCode
);
378 pthread_cond_signal( &mCond
);
382 extern void ElapsedTimeToTimespec(double elapsed
, struct timespec
*spec
);
384 void SC_TerminalClient::commandLoop()
386 bool haveNext
= false;
387 struct timespec nextAbsTime
;
391 while( shouldBeRunning() )
399 if (sig
& sig_input
) {
403 // clear input signal, as we've processed anything signalled so far.
405 mSignals
&= ~sig_input
;
410 if (sig
& sig_sched
) {
414 haveNext
= tickLocked( &secs
);
415 // clear scheduler signal, as we've processed all items scheduled up to this time.
416 // and will enter the wait according to schedule.
418 mSignals
&= ~sig_sched
;
424 //postfl("tick -> next time = %f\n", haveNext ? secs : -1);
425 ElapsedTimeToTimespec( secs
, &nextAbsTime
);
428 if (sig
& sig_stop
) {
431 mSignals
&= ~sig_stop
;
435 if (sig
& sig_recompile
) {
438 mSignals
&= ~sig_recompile
;
445 if( !shouldBeRunning() ) {
448 else if( haveNext
) {
449 int result
= pthread_cond_timedwait( &mCond
, &mSignalMutex
, &nextAbsTime
);
450 if( result
== ETIMEDOUT
) mSignals
|= sig_sched
;
453 pthread_cond_wait( &mCond
, &mSignalMutex
);
460 void SC_TerminalClient::daemonLoop()
467 static void sc_rl_cleanlf(void)
469 rl_reset_line_state();
474 static void sc_rl_signalhandler(int sig
)
476 // ensure ctrl-C clears line rather than quitting (ctrl-D will quit nicely)
477 rl_replace_line("", 0);
481 static int sc_rl_mainstop(int i1
, int i2
)
483 static_cast<SC_TerminalClient
*>(SC_LanguageClient::instance())
484 ->sendSignal( SC_TerminalClient::sig_stop
);
485 sc_rl_cleanlf(); // We also push a newline so that there's some UI feedback
490 // Completion from sclang dictionary TODO
491 char ** sc_rl_completion (const char *text, int start, int end);
492 char ** sc_rl_completion (const char *text, int start, int end){
493 char **matches = (char **)NULL;
494 printf("sc_rl_completion(%s, %i, %i)\n", text, start, end);
499 int SC_TerminalClient::readlineRecompile(int i1
, int i2
)
501 static_cast<SC_TerminalClient
*>(SC_LanguageClient::instance())->sendSignal(sig_recompile
);
506 void SC_TerminalClient::readlineCmdLine( char *cmdLine
)
508 SC_TerminalClient
*client
= static_cast<SC_TerminalClient
*>(instance());
510 if( cmdLine
== NULL
) {
511 postfl("\nExiting sclang (ctrl-D)\n");
517 // If line wasn't empty, store it so that uparrow retrieves it
518 add_history(cmdLine
);
519 int len
= strlen(cmdLine
);
522 client
->mInputBuf
.append(cmdLine
, len
);
523 client
->mInputBuf
.append(kInterpretPrintCmdLine
);
524 client
->sendSignal(sig_input
);
525 // Wait for input to be processed,
526 // so that its output is displayed before readline prompt.
527 if (client
->mInputShouldBeRunning
)
528 pthread_cond_wait( &client
->mInputCond
, &client
->mInputMutex
);
529 client
->unlockInput();
533 void SC_TerminalClient::readlineInit()
536 rl_readline_name
= "sclang";
537 rl_basic_word_break_characters
= " \t\n\"\\'`@><=;|&{}().";
538 //rl_attempted_completion_function = sc_rl_completion;
539 rl_bind_key(CTRL('t'), &sc_rl_mainstop
);
540 rl_bind_key(CTRL('x'), &readlineRecompile
);
541 rl_callback_handler_install( "sc3> ", &readlineCmdLine
);
543 // FIXME: Implement the code below on Windows
545 // Set our handler for SIGINT that will clear the line instead of terminating.
546 // NOTE: We prevent readline from setting its own signal handlers,
547 // to not override ours.
548 rl_catch_signals
= 0;
549 struct sigaction sact
;
550 memset( &sact
, 0, sizeof(struct sigaction
) );
551 sact
.sa_handler
= &sc_rl_signalhandler
;
552 sigaction( SIGINT
, &sact
, 0 );
558 void *SC_TerminalClient::readlineFunc( void *arg
)
562 SC_TerminalClient
*client
= static_cast<SC_TerminalClient
*>(arg
);
568 FD_SET(STDIN_FD
, &fds
);
569 FD_SET(client
->mInputCtlPipe
[0], &fds
);
571 if( select(FD_SETSIZE
, &fds
, NULL
, NULL
, NULL
) < 0 ) {
572 if( errno
== EINTR
) continue;
573 postfl("readline: select() error:\n%s\n", strerror(errno
));
578 if( FD_ISSET(client
->mInputCtlPipe
[0], &fds
) ) {
579 postfl("readline: quit requested\n");
583 if( FD_ISSET(STDIN_FD
, &fds
) ) {
584 rl_callback_read_char();
588 postfl("readline: stopped.\n");
595 void *SC_TerminalClient::readlineFunc( void *arg
)
599 SC_TerminalClient
*client
= static_cast<SC_TerminalClient
*>(arg
);
601 HANDLE hStdIn
= GetStdHandle(STD_INPUT_HANDLE
);
602 HANDLE hnds
[] = { client
->mQuitInputEvent
, hStdIn
};
604 bool shouldRun
= true;
606 DWORD result
= WaitForMultipleObjects( 2, hnds
, false, INFINITE
);
608 if( result
== WAIT_FAILED
) {
609 postfl("readline: wait error.\n");
614 int hIndex
= result
- WAIT_OBJECT_0
;
617 postfl("readline: quit requested.\n");
622 rl_callback_read_char();
626 postfl("readline: stopped.\n");
632 #endif // HAVE_READLINE
636 void *SC_TerminalClient::pipeFunc( void *arg
)
638 SC_TerminalClient
*client
= static_cast<SC_TerminalClient
*>(arg
);
641 const size_t toRead
= 256;
643 SC_StringBuffer stack
;
648 bool shouldRead
= true;
650 FD_SET(STDIN_FD
, &fds
);
651 FD_SET(client
->mInputCtlPipe
[0], &fds
);
653 if( select(FD_SETSIZE
, &fds
, NULL
, NULL
, NULL
) < 0 ) {
654 if( errno
== EINTR
) continue;
655 postfl("pipe-in: select() error: %s\n", strerror(errno
));
660 if( FD_ISSET(client
->mInputCtlPipe
[0], &fds
) ) {
661 postfl("pipe-in: quit requested\n");
665 if( FD_ISSET(STDIN_FD
, &fds
) ) {
668 bytes
= read( STDIN_FD
, buf
, toRead
);
671 client
->pushCmdLine( stack
, buf
, bytes
);
673 else if( bytes
== 0 ) {
674 postfl("pipe-in: EOF. Will quit.\n");
680 if( errno
== EAGAIN
) {
681 break; // no more to read this time;
683 else if( errno
!= EINTR
){
684 postfl("pipe-in: read() error: %s\n", strerror(errno
));
694 postfl("pipe-in: stopped.\n");
701 void *SC_TerminalClient::pipeFunc( void *arg
)
703 SC_TerminalClient
*client
= static_cast<SC_TerminalClient
*>(arg
);
704 SC_StringBuffer stack
;
705 HANDLE hStdIn
= GetStdHandle(STD_INPUT_HANDLE
);
709 BOOL ok
= ReadFile( hStdIn
, &buf
, 256, &n
, NULL
);
711 client
->pushCmdLine( stack
, buf
, n
);
714 postfl("pipe-in: ERROR (ReadFile): %i\n", GetLastError());
724 void SC_TerminalClient::pushCmdLine( SC_StringBuffer
&buf
, const char *newData
, size_t size
)
733 case kRecompileLibrary
:
734 case kInterpretCmdLine
:
735 case kInterpretPrintCmdLine
:
736 mInputBuf
.append( buf
.getData(), buf
.getSize() );
747 if(signal
) sendSignal(sig_input
);
755 void SC_TerminalClient::initInput()
759 if( pipe( mInputCtlPipe
) == -1 ) {
760 postfl("Error creating pipe for input thread control:\n%s\n", strerror(errno
));
764 mQuitInputEvent
= CreateEvent( NULL
, false, false, NULL
);
765 if( mQuitInputEvent
== NULL
) {
766 postfl("Error creating event for input thread control.\n");
772 if (strcmp(gIdeName
, "none") == 0) {
773 // Other clients (emacs, vim, ...) won't want to interact through rl
780 if( fcntl( STDIN_FD
, F_SETFL
, O_NONBLOCK
) == -1 ) {
781 postfl("Error setting up non-blocking pipe reading:\n%s\n", strerror(errno
));
788 void SC_TerminalClient::startInput()
790 mInputShouldBeRunning
= true;
793 pthread_create( &mInputThread
, NULL
, &SC_TerminalClient::readlineFunc
, this );
796 pthread_create( &mInputThread
, NULL
, &SC_TerminalClient::pipeFunc
, this );
799 void SC_TerminalClient::endInput()
801 // NOTE: On Windows, there is no way to safely interrupt
802 // the pipe-reading thread. So just quit and let it die.
807 // wake up the input thread in case it is waiting
808 // for input to be processed
810 mInputShouldBeRunning
= false;
811 pthread_cond_signal( &mInputCond
);
816 ssize_t bytes
= write( mInputCtlPipe
[1], &c
, 1 );
818 postfl("WARNING: could not send quit command to input thread.\n");
820 SetEvent( mQuitInputEvent
);
823 postfl("main: waiting for input thread to join...\n");
824 pthread_join( mInputThread
, NULL
);
827 } // if (mUseReadline)
829 postfl("main: quitting...\n");
832 void SC_TerminalClient::cleanupInput()
835 if( mUseReadline
) rl_callback_handler_remove();
839 int SC_TerminalClient::prArgv(struct VMGlobals
* g
, int)
841 int argc
= ((SC_TerminalClient
*)SC_TerminalClient::instance())->options().mArgc
;
842 char** argv
= ((SC_TerminalClient
*)SC_TerminalClient::instance())->options().mArgv
;
844 PyrSlot
* argvSlot
= g
->sp
;
846 PyrObject
* argvObj
= newPyrArray(g
->gc
, argc
* sizeof(PyrObject
), 0, true);
847 SetObject(argvSlot
, argvObj
);
849 for (int i
=0; i
< argc
; i
++) {
850 PyrString
* str
= newPyrString(g
->gc
, argv
[i
], 0, true);
851 SetObject(argvObj
->slots
+i
, str
);
853 g
->gc
->GCWrite(argvObj
, (PyrObject
*)str
);
859 int SC_TerminalClient::prExit(struct VMGlobals
* g
, int)
863 int err
= slotIntVal(g
->sp
, &code
);
866 ((SC_TerminalClient
*)SC_LanguageClient::instance())->onQuit( code
);
871 int SC_TerminalClient::prScheduleChanged( struct VMGlobals
*g
, int numArgsPushed
)
873 static_cast<SC_TerminalClient
*>(instance())->sendSignal(sig_sched
);
877 int SC_TerminalClient::prRecompile(struct VMGlobals
*, int)
879 static_cast<SC_TerminalClient
*>(instance())->sendSignal(sig_recompile
);