Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / lang / LangSource / SC_TerminalClient.cpp
blob17198a8a5f030c0a48868e4ede5e562f046ece42
1 /*
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"
28 #include <errno.h>
29 #include <fcntl.h>
31 #ifdef SC_WIN32
32 # define __GNU_LIBRARY__
33 # include "getopt.h"
34 # include "SC_Win32Utils.h"
35 # include <io.h>
36 # include <windows.h>
37 #else
38 # include <sys/param.h>
39 # include <sys/poll.h>
40 # include <unistd.h>
41 #endif
43 #ifdef HAVE_READLINE
44 # include <readline/readline.h>
45 # include <readline/history.h>
46 # include <signal.h>
47 #endif
50 #include <string.h>
51 #include <time.h>
53 #include "GC.h"
54 #include "PyrKernel.h"
55 #include "PyrPrimitive.h"
56 #include "PyrLexer.h"
57 #include "PyrSlot.h"
58 #include "VMGlobals.h"
59 #include "SC_DirUtils.h" // for gIdeName
60 #include "SC_LibraryConfig.h"
62 #define STDIN_FD 0
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),
71 mReturnCode(0),
72 mUseReadline(false),
73 mSignals(0)
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);
97 fflush(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()
108 fflush(gPostDest);
111 void SC_TerminalClient::printUsage()
113 Options opt;
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());
123 fprintf(stdout,
124 "Options:\n"
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",
135 memGrowBuf,
136 memSpaceBuf,
137 opt.mPort,
138 gIdeName
142 bool SC_TerminalClient::parseOptions(int& argc, char**& argv, Options& opt)
144 const char* optstr = ":d:Dg:hl:m:rsu:i:";
145 int c;
147 // inhibit error reporting
148 opterr = 0;
150 while ((c = getopt(argc, argv, optstr)) != -1) {
151 switch (c) {
152 case 'd':
153 opt.mRuntimeDir = optarg;
154 break;
155 case 'D':
156 opt.mDaemon = true;
157 break;
158 case 'g':
159 if (!parseMemArg(optarg, &opt.mMemGrow)) {
160 optopt = c;
161 goto optArgInvalid;
163 break;
164 case 'h':
165 goto help;
166 case 'l':
167 opt.mLibraryConfigFile = optarg;
168 break;
169 case 'm':
170 if (!parseMemArg(optarg, &opt.mMemSpace)) {
171 optopt = c;
172 goto optArgInvalid;
174 break;
175 case 'r':
176 opt.mCallRun = true;
177 break;
178 case 's':
179 opt.mCallStop = true;
180 break;
181 case 'u':
182 if (!parsePortArg(optarg, &opt.mPort)) {
183 optopt = c;
184 goto optArgInvalid;
186 break;
187 case '?':
188 goto optInvalid;
189 break;
190 case ':':
191 goto optArgExpected;
192 break;
193 case 'i':
194 gIdeName = optarg;
195 break;
196 default:
197 ::post("%s: unknown error (getopt)\n", getName());
198 quit(255);
199 return false;
203 argv += optind;
204 argc -= optind;
206 return true;
208 help:
209 printUsage();
210 quit(0);
211 return false;
213 optInvalid:
214 ::post("%s: invalid option -%c\n", getName(), optopt);
215 quit(1);
216 return false;
218 optArgExpected:
219 ::post("%s: missing argument for option -%c\n", getName(), optopt);
220 quit(1);
221 return false;
223 optArgInvalid:
224 ::post("%s: invalid argument for option -%c -- %s\n", getName(), optopt, optarg);
225 quit(1);
226 return false;
229 int SC_TerminalClient::run(int argc, char** argv)
231 Options& opt = mOptions;
233 if (!parseOptions(argc, argv, opt)) {
234 return mReturnCode;
237 // finish argv processing
238 const char* codeFile = 0;
240 if (argc > 0) {
241 codeFile = argv[0];
242 opt.mDaemon = true;
243 argv++; argc--;
246 opt.mArgc = argc;
247 opt.mArgv = argv;
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);
254 else
255 SC_LanguageConfig::readLibraryConfigYAML(opt.mLibraryConfigFile);
256 } else
257 SC_LanguageConfig::readDefaultLibraryConfig();
259 // initialize runtime
260 initRuntime(opt);
262 // startup library
263 mShouldBeRunning = true;
264 compileLibrary();
266 // enter main loop
267 if (codeFile) executeFile(codeFile);
268 if (opt.mCallRun) runMain();
270 if (opt.mDaemon) {
271 daemonLoop();
273 else {
274 initInput();
275 if( shouldBeRunning() ) startInput();
276 if( shouldBeRunning() ) commandLoop();
277 endInput();
278 cleanupInput();
281 if (opt.mCallStop) stopMain();
283 // shutdown library
284 shutdownLibrary();
285 flush();
287 shutdownRuntime();
289 return mReturnCode;
292 void SC_TerminalClient::quit(int code)
294 mReturnCode = code;
295 mShouldBeRunning = false;
298 void SC_TerminalClient::interpretCmdLine(PyrSymbol* method, SC_StringBuffer& cmdLine)
300 setCmdLine(cmdLine);
301 cmdLine.reset();
302 runLibrary(method);
303 flush();
306 void SC_TerminalClient::interpretCmdLine(PyrSymbol* method, const char* cmdLine)
308 setCmdLine(cmdLine);
309 runLibrary(method);
310 flush();
314 void SC_TerminalClient::interpretCmdLine(PyrSymbol* method, const char *cmdLine, size_t size)
316 setCmdLine(cmdLine, size);
317 runLibrary(method);
318 flush();
321 // WARNING: Call with input locked!
322 void SC_TerminalClient::interpretInput()
324 char *data = mInputBuf.getData();
325 int c = mInputBuf.getSize();
326 int i = 0;
327 while( i < c ) {
328 switch (data[i]) {
329 case kInterpretCmdLine:
330 interpretCmdLine(s_interpretCmdLine, data, i);
331 break;
332 case kInterpretPrintCmdLine:
333 interpretCmdLine(s_interpretPrintCmdLine, data, i);
334 break;
336 case kRecompileLibrary:
337 recompileLibrary();
338 break;
340 default:
341 ++i;
342 continue;
345 data += i+1;
346 c -= i+1;
347 i = 0;
349 mInputBuf.reset();
350 if( mUseReadline ) pthread_cond_signal( &mInputCond );
353 void SC_TerminalClient::onLibraryStartup()
355 SC_LanguageClient::onLibraryStartup();
356 int base, index = 0;
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 )
367 lockSignal();
368 mSignals |= sig;
369 pthread_cond_signal( &mCond );
370 unlockSignal();
373 void SC_TerminalClient::onQuit( int exitCode )
375 lockSignal();
376 postfl("main: quit request %i\n", exitCode);
377 quit( exitCode );
378 pthread_cond_signal( &mCond );
379 unlockSignal();
382 extern void ElapsedTimeToTimespec(double elapsed, struct timespec *spec);
384 void SC_TerminalClient::commandLoop()
386 bool haveNext = false;
387 struct timespec nextAbsTime;
389 lockSignal();
391 while( shouldBeRunning() )
394 while ( mSignals ) {
395 int sig = mSignals;
397 unlockSignal();
399 if (sig & sig_input) {
400 //postfl("input\n");
401 lockInput();
402 interpretInput();
403 // clear input signal, as we've processed anything signalled so far.
404 lockSignal();
405 mSignals &= ~sig_input;
406 unlockSignal();
407 unlockInput();
410 if (sig & sig_sched) {
411 //postfl("tick\n");
412 double secs;
413 lock();
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.
417 lockSignal();
418 mSignals &= ~sig_sched;
419 unlockSignal();
420 unlock();
422 flush();
424 //postfl("tick -> next time = %f\n", haveNext ? secs : -1);
425 ElapsedTimeToTimespec( secs, &nextAbsTime );
428 if (sig & sig_stop) {
429 stopMain();
430 lockSignal();
431 mSignals &= ~sig_stop;
432 unlockSignal();
435 if (sig & sig_recompile) {
436 recompileLibrary();
437 lockSignal();
438 mSignals &= ~sig_recompile;
439 unlockSignal();
442 lockSignal();
445 if( !shouldBeRunning() ) {
446 break;
448 else if( haveNext ) {
449 int result = pthread_cond_timedwait( &mCond, &mSignalMutex, &nextAbsTime );
450 if( result == ETIMEDOUT ) mSignals |= sig_sched;
452 else {
453 pthread_cond_wait( &mCond, &mSignalMutex );
457 unlockSignal();
460 void SC_TerminalClient::daemonLoop()
462 commandLoop();
465 #ifdef HAVE_READLINE
467 static void sc_rl_cleanlf(void)
469 rl_reset_line_state();
470 rl_crlf();
471 rl_redisplay();
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);
478 sc_rl_cleanlf();
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
486 return 0;
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);
495 return matches;
499 int SC_TerminalClient::readlineRecompile(int i1, int i2)
501 static_cast<SC_TerminalClient*>(SC_LanguageClient::instance())->sendSignal(sig_recompile);
502 sc_rl_cleanlf();
503 return 0;
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");
512 client->onQuit(0);
513 return;
516 if(*cmdLine!=0){
517 // If line wasn't empty, store it so that uparrow retrieves it
518 add_history(cmdLine);
519 int len = strlen(cmdLine);
521 client->lockInput();
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()
535 // Setup readline
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
544 #ifndef _WIN32
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 );
553 #endif
556 #ifndef _WIN32
558 void *SC_TerminalClient::readlineFunc( void *arg )
560 readlineInit();
562 SC_TerminalClient *client = static_cast<SC_TerminalClient*>(arg);
564 fd_set fds;
565 FD_ZERO(&fds);
567 while(true) {
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));
574 client->onQuit(1);
575 break;
578 if( FD_ISSET(client->mInputCtlPipe[0], &fds) ) {
579 postfl("readline: quit requested\n");
580 break;
583 if( FD_ISSET(STDIN_FD, &fds) ) {
584 rl_callback_read_char();
588 postfl("readline: stopped.\n");
590 return NULL;
593 #else
595 void *SC_TerminalClient::readlineFunc( void *arg )
597 readlineInit();
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;
605 while (shouldRun) {
606 DWORD result = WaitForMultipleObjects( 2, hnds, false, INFINITE );
608 if( result == WAIT_FAILED ) {
609 postfl("readline: wait error.\n");
610 client->onQuit(1);
611 break;
614 int hIndex = result - WAIT_OBJECT_0;
616 if( hIndex == 0 ) {
617 postfl("readline: quit requested.\n");
618 break;
621 if( hIndex == 1 ) {
622 rl_callback_read_char();
626 postfl("readline: stopped.\n");
628 return NULL;
630 #endif // !_WIN32
632 #endif // HAVE_READLINE
634 #ifndef _WIN32
636 void *SC_TerminalClient::pipeFunc( void *arg )
638 SC_TerminalClient *client = static_cast<SC_TerminalClient*>(arg);
640 ssize_t bytes;
641 const size_t toRead = 256;
642 char buf[toRead];
643 SC_StringBuffer stack;
645 fd_set fds;
646 FD_ZERO(&fds);
648 bool shouldRead = true;
649 while(shouldRead) {
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));
656 client->onQuit(1);
657 break;
660 if( FD_ISSET(client->mInputCtlPipe[0], &fds) ) {
661 postfl("pipe-in: quit requested\n");
662 break;
665 if( FD_ISSET(STDIN_FD, &fds) ) {
667 while(true) {
668 bytes = read( STDIN_FD, buf, toRead );
670 if( bytes > 0 ) {
671 client->pushCmdLine( stack, buf, bytes );
673 else if( bytes == 0 ) {
674 postfl("pipe-in: EOF. Will quit.\n");
675 client->onQuit(0);
676 shouldRead = false;
677 break;
679 else {
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));
685 client->onQuit(1);
686 shouldRead = false;
687 break;
694 postfl("pipe-in: stopped.\n");
696 return NULL;
699 #else
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);
706 char buf[256];
707 while(1) {
708 DWORD n;
709 BOOL ok = ReadFile( hStdIn, &buf, 256, &n, NULL );
710 if(ok) {
711 client->pushCmdLine( stack, buf, n );
713 else {
714 postfl("pipe-in: ERROR (ReadFile): %i\n", GetLastError());
715 client->onQuit(1);
716 break;
719 return NULL;
722 #endif
724 void SC_TerminalClient::pushCmdLine( SC_StringBuffer &buf, const char *newData, size_t size)
726 lockInput();
728 bool signal = false;
730 while (size--) {
731 char c = *newData++;
732 switch (c) {
733 case kRecompileLibrary:
734 case kInterpretCmdLine:
735 case kInterpretPrintCmdLine:
736 mInputBuf.append( buf.getData(), buf.getSize() );
737 mInputBuf.append(c);
738 signal = true;
739 buf.reset();
740 break;
742 default:
743 buf.append(c);
747 if(signal) sendSignal(sig_input);
749 unlockInput();
755 void SC_TerminalClient::initInput()
758 #ifndef _WIN32
759 if( pipe( mInputCtlPipe ) == -1 ) {
760 postfl("Error creating pipe for input thread control:\n%s\n", strerror(errno));
761 quit(1);
763 #else
764 mQuitInputEvent = CreateEvent( NULL, false, false, NULL );
765 if( mQuitInputEvent == NULL ) {
766 postfl("Error creating event for input thread control.\n");
767 quit(1);
769 #endif
771 #ifdef HAVE_READLINE
772 if (strcmp(gIdeName, "none") == 0) {
773 // Other clients (emacs, vim, ...) won't want to interact through rl
774 mUseReadline = true;
775 return;
777 #endif
779 #ifndef _WIN32
780 if( fcntl( STDIN_FD, F_SETFL, O_NONBLOCK ) == -1 ) {
781 postfl("Error setting up non-blocking pipe reading:\n%s\n", strerror(errno));
782 quit(1);
784 #endif // !_WIN32
788 void SC_TerminalClient::startInput()
790 mInputShouldBeRunning = true;
791 #ifdef HAVE_READLINE
792 if( mUseReadline )
793 pthread_create( &mInputThread, NULL, &SC_TerminalClient::readlineFunc, this );
794 else
795 #endif
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.
804 #ifdef _WIN32
805 if (mUseReadline) {
806 #endif
807 // wake up the input thread in case it is waiting
808 // for input to be processed
809 lockInput();
810 mInputShouldBeRunning = false;
811 pthread_cond_signal( &mInputCond );
812 unlockInput();
814 #ifndef _WIN32
815 char c = 'q';
816 ssize_t bytes = write( mInputCtlPipe[1], &c, 1 );
817 if( bytes < 1 )
818 postfl("WARNING: could not send quit command to input thread.\n");
819 #else
820 SetEvent( mQuitInputEvent );
821 #endif
823 postfl("main: waiting for input thread to join...\n");
824 pthread_join( mInputThread, NULL );
826 #ifdef _WIN32
827 } // if (mUseReadline)
828 #endif
829 postfl("main: quitting...\n");
832 void SC_TerminalClient::cleanupInput()
834 #ifdef HAVE_READLINE
835 if( mUseReadline ) rl_callback_handler_remove();
836 #endif
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);
852 argvObj->size++;
853 g->gc->GCWrite(argvObj, (PyrObject*)str);
856 return errNone;
859 int SC_TerminalClient::prExit(struct VMGlobals* g, int)
861 int code;
863 int err = slotIntVal(g->sp, &code);
864 if (err) return err;
866 ((SC_TerminalClient*)SC_LanguageClient::instance())->onQuit( code );
868 return errNone;
871 int SC_TerminalClient::prScheduleChanged( struct VMGlobals *g, int numArgsPushed)
873 static_cast<SC_TerminalClient*>(instance())->sendSignal(sig_sched);
874 return errNone;
877 int SC_TerminalClient::prRecompile(struct VMGlobals *, int)
879 static_cast<SC_TerminalClient*>(instance())->sendSignal(sig_recompile);
880 return errNone;
882 // EOF