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(0x02, &sc_rl_mainstop
);
542 rl_bind_key(CTRL('x'), &readlineRecompile
);
543 // TODO 0x02 is ctrl-B;
544 // ctrl-. would be nicer but keycode not working here (plain "." is 46 (0x2e))
545 rl_callback_handler_install( "sc3> ", &readlineCmdLine
);
547 // Set our handler for SIGINT that will clear the line instead of terminating.
548 // NOTE: We prevent readline from setting its own signal handlers,
549 // to not override ours.
550 rl_catch_signals
= 0;
551 struct sigaction sact
;
552 memset( &sact
, 0, sizeof(struct sigaction
) );
553 sact
.sa_handler
= &sc_rl_signalhandler
;
554 sigaction( SIGINT
, &sact
, 0 );
560 FD_SET(STDIN_FD
, &fds
);
561 FD_SET(client
->mInputCtlPipe
[0], &fds
);
563 if( select(FD_SETSIZE
, &fds
, NULL
, NULL
, NULL
) < 0 ) {
564 if( errno
== EINTR
) continue;
565 postfl("readline: select() error:\n%s\n", strerror(errno
));
570 if( FD_ISSET(client
->mInputCtlPipe
[0], &fds
) ) {
571 postfl("readline: quit requested\n");
575 if( FD_ISSET(STDIN_FD
, &fds
) ) {
576 rl_callback_read_char();
580 postfl("readline: stopped.\n");
585 // Completion from sclang dictionary TODO
586 char ** sc_rl_completion (const char *text, int start, int end);
587 char ** sc_rl_completion (const char *text, int start, int end){
588 char **matches = (char **)NULL;
589 printf("sc_rl_completion(%s, %i, %i)\n", text, start, end);
597 void *SC_TerminalClient::pipeFunc( void *arg
)
599 SC_TerminalClient
*client
= static_cast<SC_TerminalClient
*>(arg
);
602 const size_t toRead
= 256;
604 SC_StringBuffer stack
;
609 bool shouldRead
= true;
611 FD_SET(STDIN_FD
, &fds
);
612 FD_SET(client
->mInputCtlPipe
[0], &fds
);
614 if( select(FD_SETSIZE
, &fds
, NULL
, NULL
, NULL
) < 0 ) {
615 if( errno
== EINTR
) continue;
616 postfl("pipe-in: select() error: %s\n", strerror(errno
));
621 if( FD_ISSET(client
->mInputCtlPipe
[0], &fds
) ) {
622 postfl("pipe-in: quit requested\n");
626 if( FD_ISSET(STDIN_FD
, &fds
) ) {
629 bytes
= read( STDIN_FD
, buf
, toRead
);
632 client
->pushCmdLine( stack
, buf
, bytes
);
634 else if( bytes
== 0 ) {
635 postfl("pipe-in: EOF. Will quit.\n");
641 if( errno
== EAGAIN
) {
642 break; // no more to read this time;
644 else if( errno
!= EINTR
){
645 postfl("pipe-in: read() error: %s\n", strerror(errno
));
655 postfl("pipe-in: stopped.\n");
662 void *SC_TerminalClient::pipeFunc( void *arg
)
664 SC_TerminalClient
*client
= static_cast<SC_TerminalClient
*>(arg
);
666 SC_StringBuffer stack
;
667 HANDLE hStdIn
= GetStdHandle(STD_INPUT_HANDLE
);
668 HANDLE hnds
[] = { client
->mQuitInputEvent
, hStdIn
};
670 bool shouldRun
= true;
672 DWORD result
= WaitForMultipleObjects( 2, hnds
, false, INFINITE
);
674 if( result
== WAIT_FAILED
) {
675 postfl("pipe-in: wait error.\n");
680 int hIndex
= result
- WAIT_OBJECT_0
;
683 postfl("pipe-in: quit requested.\n");
689 if (!PeekNamedPipe(hStdIn
, NULL
, 0, NULL
, &nAvail
, NULL
)) {
690 DWORD err
= GetLastError();
691 if( err
== ERROR_BROKEN_PIPE
) {
692 postfl("pipe-in: Pipe has been ended. Quitting.\n");
696 postfl("pipe-in: Error trying to peek stdin (%Li). Quitting.\n", err
);
705 DWORD nRead
= sc_min(256, nAvail
);
706 if (!ReadFile(hStdIn
, buf
, nRead
, &nRead
, NULL
)) {
707 postfl("pipe-in: Error trying to read stdin (%Li). Quitting.\n", GetLastError());
712 else if (nRead
> 0) {
713 client
->pushCmdLine( stack
, buf
, nRead
);
721 postfl("pipe-in: stopped.\n");
728 void SC_TerminalClient::pushCmdLine( SC_StringBuffer
&buf
, const char *newData
, size_t size
)
737 case kRecompileLibrary
:
738 case kInterpretCmdLine
:
739 case kInterpretPrintCmdLine
:
740 mInputBuf
.append( buf
.getData(), buf
.getSize() );
751 if(signal
) onInput();
759 void SC_TerminalClient::initInput()
764 if( pipe( mInputCtlPipe
) == -1 ) {
765 postfl("Error creating pipe for input thread control:\n%s\n", strerror(errno
));
771 if (strcmp(gIdeName
, "none") == 0) {
772 // Other clients (emacs, vim, ...) won't want to interact through rl
779 if( fcntl( STDIN_FD
, F_SETFL
, O_NONBLOCK
) == -1 ) {
780 postfl("Error setting up non-blocking pipe reading:\n%s\n", strerror(errno
));
786 mQuitInputEvent
= CreateEvent( NULL
, false, false, NULL
);
787 if( mQuitInputEvent
== NULL
) {
788 postfl("Error creating event for input thread control.\n");
791 postfl("Created input thread control event.\n");
797 void SC_TerminalClient::startInput()
799 mInputShouldBeRunning
= true;
802 pthread_create( &mInputThread
, NULL
, &SC_TerminalClient::readlineFunc
, this );
805 pthread_create( &mInputThread
, NULL
, &SC_TerminalClient::pipeFunc
, this );
808 void SC_TerminalClient::endInput()
810 // wake up the input thread in case it is waiting
811 // for input to be processed
813 mInputShouldBeRunning
= false;
814 pthread_cond_signal( &mInputCond
);
818 postfl("main: sending quit command to input thread.\n");
820 ssize_t bytes
= write( mInputCtlPipe
[1], &c
, 1 );
821 if( bytes
< 1 ) { postfl("WARNING: could not send quit command to input thread.\n"); }
824 postfl("main: signalling input thread quit event\n");
825 SetEvent( mQuitInputEvent
);
828 postfl("main: stopped, waiting for input thread to join...\n");
830 pthread_join( mInputThread
, NULL
);
832 postfl("main: input thread joined.\n");
835 void SC_TerminalClient::cleanupInput()
838 if( mUseReadline
) rl_callback_handler_remove();
842 int SC_TerminalClient::prArgv(struct VMGlobals
* g
, int)
844 int argc
= ((SC_TerminalClient
*)SC_TerminalClient::instance())->options().mArgc
;
845 char** argv
= ((SC_TerminalClient
*)SC_TerminalClient::instance())->options().mArgv
;
847 PyrSlot
* argvSlot
= g
->sp
;
849 PyrObject
* argvObj
= newPyrArray(g
->gc
, argc
* sizeof(PyrObject
), 0, true);
850 SetObject(argvSlot
, argvObj
);
852 for (int i
=0; i
< argc
; i
++) {
853 PyrString
* str
= newPyrString(g
->gc
, argv
[i
], 0, true);
854 SetObject(argvObj
->slots
+i
, str
);
856 g
->gc
->GCWrite(argvObj
, (PyrObject
*)str
);
862 int SC_TerminalClient::prExit(struct VMGlobals
* g
, int)
866 int err
= slotIntVal(g
->sp
, &code
);
869 ((SC_TerminalClient
*)SC_LanguageClient::instance())->onQuit( code
);
874 int SC_TerminalClient::prScheduleChanged( struct VMGlobals
*g
, int numArgsPushed
)
876 static_cast<SC_TerminalClient
*>(instance())->onScheduleChanged();
880 int SC_TerminalClient::prRecompile(struct VMGlobals
*, int)
882 static_cast<SC_TerminalClient
*>(instance())->onRecompileLibrary();