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>
42 # include <readline/readline.h>
43 # include <readline/history.h>
52 #include "PyrKernel.h"
53 #include "PyrPrimitive.h"
56 #include "VMGlobals.h"
57 #include "SC_DirUtils.h" // for gIdeName
58 #include "SC_LibraryConfig.h"
62 static FILE* gPostDest
= stdout
;
64 static const int ticks_per_second
= 50; // every 20 milliseconds
66 SC_TerminalClient::SC_TerminalClient(const char* name
)
67 : SC_LanguageClient(name
),
68 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::onScheduleChanged()
369 pthread_cond_signal( &mCond
);
373 void SC_TerminalClient::onInput()
377 pthread_cond_signal( &mCond
);
381 void SC_TerminalClient::onQuit( int exitCode
)
384 postfl("main: quit request %i\n", exitCode
);
386 pthread_cond_signal( &mCond
);
390 void SC_TerminalClient::onRecompileLibrary()
394 pthread_cond_signal( &mCond
);
398 extern void ElapsedTimeToTimespec(double elapsed
, struct timespec
*spec
);
400 void SC_TerminalClient::commandLoop()
402 bool haveNext
= false;
403 struct timespec nextAbsTime
;
407 while( shouldBeRunning() )
410 while ( mInput
|| mSched
|| mRecompile
) {
413 bool recompile
= mRecompile
;
421 // clear input signal, as we've processed anything signalled so far.
432 haveNext
= tickLocked( &secs
);
433 // clear scheduler signal, as we've processed all items scheduled up to this time.
434 // and will enter the wait according to schedule.
442 //postfl("tick -> next time = %f\n", haveNext ? secs : -1);
443 ElapsedTimeToTimespec( secs
, &nextAbsTime
);
456 if( !shouldBeRunning() ) {
459 else if( haveNext
) {
460 int result
= pthread_cond_timedwait( &mCond
, &mSignalMutex
, &nextAbsTime
);
461 if( result
== ETIMEDOUT
) mSched
= true;
464 pthread_cond_wait( &mCond
, &mSignalMutex
);
471 void SC_TerminalClient::daemonLoop()
478 static void sc_rl_cleanlf(void)
480 rl_reset_line_state();
485 static void sc_rl_signalhandler(int sig
)
487 // ensure ctrl-C clears line rather than quitting (ctrl-D will quit nicely)
488 rl_replace_line("", 0);
492 static int sc_rl_mainstop(int i1
, int i2
)
494 SC_TerminalClient::instance()->stopMain();
495 sc_rl_cleanlf(); // We also push a newline so that there's some UI feedback
499 int SC_TerminalClient::readlineRecompile(int i1
, int i2
)
501 static_cast<SC_TerminalClient
*>(SC_LanguageClient::instance())->onRecompileLibrary();
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
);
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::readlineFunc( void *arg
)
535 SC_TerminalClient
*client
= static_cast<SC_TerminalClient
*>(arg
);
538 rl_readline_name
= "sclang";
539 rl_basic_word_break_characters
= " \t\n\"\\'`@><=;|&{}().";
540 //rl_attempted_completion_function = sc_rl_completion;
541 rl_bind_key(CTRL('t'), &sc_rl_mainstop
);
542 rl_bind_key(CTRL('x'), &readlineRecompile
);
543 rl_callback_handler_install( "sc3> ", &readlineCmdLine
);
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 FD_SET(STDIN_FD
, &fds
);
559 FD_SET(client
->mInputCtlPipe
[0], &fds
);
561 if( select(FD_SETSIZE
, &fds
, NULL
, NULL
, NULL
) < 0 ) {
562 if( errno
== EINTR
) continue;
563 postfl("readline: select() error:\n%s\n", strerror(errno
));
568 if( FD_ISSET(client
->mInputCtlPipe
[0], &fds
) ) {
569 postfl("readline: quit requested\n");
573 if( FD_ISSET(STDIN_FD
, &fds
) ) {
574 rl_callback_read_char();
578 postfl("readline: stopped.\n");
583 // Completion from sclang dictionary TODO
584 char ** sc_rl_completion (const char *text, int start, int end);
585 char ** sc_rl_completion (const char *text, int start, int end){
586 char **matches = (char **)NULL;
587 printf("sc_rl_completion(%s, %i, %i)\n", text, start, end);
595 void *SC_TerminalClient::pipeFunc( void *arg
)
597 SC_TerminalClient
*client
= static_cast<SC_TerminalClient
*>(arg
);
600 const size_t toRead
= 256;
602 SC_StringBuffer stack
;
607 bool shouldRead
= true;
609 FD_SET(STDIN_FD
, &fds
);
610 FD_SET(client
->mInputCtlPipe
[0], &fds
);
612 if( select(FD_SETSIZE
, &fds
, NULL
, NULL
, NULL
) < 0 ) {
613 if( errno
== EINTR
) continue;
614 postfl("pipe-in: select() error: %s\n", strerror(errno
));
619 if( FD_ISSET(client
->mInputCtlPipe
[0], &fds
) ) {
620 postfl("pipe-in: quit requested\n");
624 if( FD_ISSET(STDIN_FD
, &fds
) ) {
627 bytes
= read( STDIN_FD
, buf
, toRead
);
630 client
->pushCmdLine( stack
, buf
, bytes
);
632 else if( bytes
== 0 ) {
633 postfl("pipe-in: EOF. Will quit.\n");
639 if( errno
== EAGAIN
) {
640 break; // no more to read this time;
642 else if( errno
!= EINTR
){
643 postfl("pipe-in: read() error: %s\n", strerror(errno
));
653 postfl("pipe-in: stopped.\n");
660 void *SC_TerminalClient::pipeFunc( void *arg
)
662 SC_TerminalClient
*client
= static_cast<SC_TerminalClient
*>(arg
);
664 SC_StringBuffer stack
;
665 HANDLE hStdIn
= GetStdHandle(STD_INPUT_HANDLE
);
666 HANDLE hnds
[] = { client
->mQuitInputEvent
, hStdIn
};
668 bool shouldRun
= true;
670 DWORD result
= WaitForMultipleObjects( 2, hnds
, false, INFINITE
);
672 if( result
== WAIT_FAILED
) {
673 postfl("pipe-in: wait error.\n");
678 int hIndex
= result
- WAIT_OBJECT_0
;
681 postfl("pipe-in: quit requested.\n");
687 if (!PeekNamedPipe(hStdIn
, NULL
, 0, NULL
, &nAvail
, NULL
)) {
688 DWORD err
= GetLastError();
689 if( err
== ERROR_BROKEN_PIPE
) {
690 postfl("pipe-in: Pipe has been ended. Quitting.\n");
694 postfl("pipe-in: Error trying to peek stdin (%Li). Quitting.\n", err
);
703 DWORD nRead
= sc_min(256, nAvail
);
704 if (!ReadFile(hStdIn
, buf
, nRead
, &nRead
, NULL
)) {
705 postfl("pipe-in: Error trying to read stdin (%Li). Quitting.\n", GetLastError());
710 else if (nRead
> 0) {
711 client
->pushCmdLine( stack
, buf
, nRead
);
719 postfl("pipe-in: stopped.\n");
726 void SC_TerminalClient::pushCmdLine( SC_StringBuffer
&buf
, const char *newData
, size_t size
)
735 case kRecompileLibrary
:
736 case kInterpretCmdLine
:
737 case kInterpretPrintCmdLine
:
738 mInputBuf
.append( buf
.getData(), buf
.getSize() );
749 if(signal
) onInput();
757 void SC_TerminalClient::initInput()
762 if( pipe( mInputCtlPipe
) == -1 ) {
763 postfl("Error creating pipe for input thread control:\n%s\n", strerror(errno
));
769 if (strcmp(gIdeName
, "none") == 0) {
770 // Other clients (emacs, vim, ...) won't want to interact through rl
777 if( fcntl( STDIN_FD
, F_SETFL
, O_NONBLOCK
) == -1 ) {
778 postfl("Error setting up non-blocking pipe reading:\n%s\n", strerror(errno
));
784 mQuitInputEvent
= CreateEvent( NULL
, false, false, NULL
);
785 if( mQuitInputEvent
== NULL
) {
786 postfl("Error creating event for input thread control.\n");
789 postfl("Created input thread control event.\n");
795 void SC_TerminalClient::startInput()
797 mInputShouldBeRunning
= true;
800 pthread_create( &mInputThread
, NULL
, &SC_TerminalClient::readlineFunc
, this );
803 pthread_create( &mInputThread
, NULL
, &SC_TerminalClient::pipeFunc
, this );
806 void SC_TerminalClient::endInput()
808 // wake up the input thread in case it is waiting
809 // for input to be processed
811 mInputShouldBeRunning
= false;
812 pthread_cond_signal( &mInputCond
);
816 postfl("main: sending quit command to input thread.\n");
818 ssize_t bytes
= write( mInputCtlPipe
[1], &c
, 1 );
819 if( bytes
< 1 ) { postfl("WARNING: could not send quit command to input thread.\n"); }
822 postfl("main: signalling input thread quit event\n");
823 SetEvent( mQuitInputEvent
);
826 postfl("main: stopped, waiting for input thread to join...\n");
828 pthread_join( mInputThread
, NULL
);
830 postfl("main: input thread joined.\n");
833 void SC_TerminalClient::cleanupInput()
836 if( mUseReadline
) rl_callback_handler_remove();
840 int SC_TerminalClient::prArgv(struct VMGlobals
* g
, int)
842 int argc
= ((SC_TerminalClient
*)SC_TerminalClient::instance())->options().mArgc
;
843 char** argv
= ((SC_TerminalClient
*)SC_TerminalClient::instance())->options().mArgv
;
845 PyrSlot
* argvSlot
= g
->sp
;
847 PyrObject
* argvObj
= newPyrArray(g
->gc
, argc
* sizeof(PyrObject
), 0, true);
848 SetObject(argvSlot
, argvObj
);
850 for (int i
=0; i
< argc
; i
++) {
851 PyrString
* str
= newPyrString(g
->gc
, argv
[i
], 0, true);
852 SetObject(argvObj
->slots
+i
, str
);
854 g
->gc
->GCWrite(argvObj
, (PyrObject
*)str
);
860 int SC_TerminalClient::prExit(struct VMGlobals
* g
, int)
864 int err
= slotIntVal(g
->sp
, &code
);
867 ((SC_TerminalClient
*)SC_LanguageClient::instance())->onQuit( code
);
872 int SC_TerminalClient::prScheduleChanged( struct VMGlobals
*g
, int numArgsPushed
)
874 static_cast<SC_TerminalClient
*>(instance())->onScheduleChanged();
878 int SC_TerminalClient::prRecompile(struct VMGlobals
*, int)
880 static_cast<SC_TerminalClient
*>(instance())->onRecompileLibrary();