supernova: fix for small audio vector sizes
[supercollider.git] / lang / LangSource / SC_TerminalClient.cpp
blob1b687d379c2cb858a074a7b131962f5df00b7aeb
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 # ifdef HAVE_READLINE
42 # include <readline/readline.h>
43 # include <readline/history.h>
44 # include <signal.h>
45 # endif
46 #endif
48 #include <string.h>
49 #include <time.h>
51 #include "GC.h"
52 #include "PyrKernel.h"
53 #include "PyrPrimitive.h"
54 #include "PyrLexer.h"
55 #include "PyrSlot.h"
56 #include "VMGlobals.h"
57 #include "SC_DirUtils.h" // for gIdeName
58 #include "SC_LibraryConfig.h"
60 #define STDIN_FD 0
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),
69 mReturnCode(0),
70 mUseReadline(false),
71 mInput(false),
72 mSched(true),
73 mRecompile(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);
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::onScheduleChanged()
367 lockSignal();
368 mSched = true;
369 pthread_cond_signal( &mCond );
370 unlockSignal();
373 void SC_TerminalClient::onInput()
375 lockSignal();
376 mInput = true;
377 pthread_cond_signal( &mCond );
378 unlockSignal();
381 void SC_TerminalClient::onQuit( int exitCode )
383 lockSignal();
384 postfl("main: quit request %i\n", exitCode);
385 quit( exitCode );
386 pthread_cond_signal( &mCond );
387 unlockSignal();
390 void SC_TerminalClient::onRecompileLibrary()
392 lockSignal();
393 mRecompile = true;
394 pthread_cond_signal( &mCond );
395 unlockSignal();
398 extern void ElapsedTimeToTimespec(double elapsed, struct timespec *spec);
400 void SC_TerminalClient::commandLoop()
402 bool haveNext = false;
403 struct timespec nextAbsTime;
405 lockSignal();
407 while( shouldBeRunning() )
410 while ( mInput || mSched || mRecompile ) {
411 bool input = mInput;
412 bool sched = mSched;
413 bool recompile = mRecompile;
415 unlockSignal();
417 if (input) {
418 //postfl("input\n");
419 lockInput();
420 interpretInput();
421 // clear input signal, as we've processed anything signalled so far.
422 lockSignal();
423 mInput = false;
424 unlockSignal();
425 unlockInput();
428 if (sched) {
429 //postfl("tick\n");
430 double secs;
431 lock();
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.
435 lockSignal();
436 mSched = false;
437 unlockSignal();
438 unlock();
440 flush();
442 //postfl("tick -> next time = %f\n", haveNext ? secs : -1);
443 ElapsedTimeToTimespec( secs, &nextAbsTime );
446 if (recompile) {
447 recompileLibrary();
448 lockSignal();
449 mRecompile = false;
450 unlockSignal();
453 lockSignal();
456 if( !shouldBeRunning() ) {
457 break;
459 else if( haveNext ) {
460 int result = pthread_cond_timedwait( &mCond, &mSignalMutex, &nextAbsTime );
461 if( result == ETIMEDOUT ) mSched = true;
463 else {
464 pthread_cond_wait( &mCond, &mSignalMutex );
468 unlockSignal();
471 void SC_TerminalClient::daemonLoop()
473 commandLoop();
476 #ifdef HAVE_READLINE
478 static void sc_rl_cleanlf(void)
480 rl_reset_line_state();
481 rl_crlf();
482 rl_redisplay();
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);
489 sc_rl_cleanlf();
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
496 return 0;
499 int SC_TerminalClient::readlineRecompile(int i1, int i2)
501 static_cast<SC_TerminalClient*>(SC_LanguageClient::instance())->onRecompileLibrary();
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->onInput();
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);
537 // Setup readline
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 );
556 fd_set fds;
557 FD_ZERO(&fds);
559 while(true) {
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));
566 client->onQuit(1);
567 break;
570 if( FD_ISSET(client->mInputCtlPipe[0], &fds) ) {
571 postfl("readline: quit requested\n");
572 break;
575 if( FD_ISSET(STDIN_FD, &fds) ) {
576 rl_callback_read_char();
580 postfl("readline: stopped.\n");
582 return NULL;
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);
590 return matches;
593 #endif
595 #ifndef _WIN32
597 void *SC_TerminalClient::pipeFunc( void *arg )
599 SC_TerminalClient *client = static_cast<SC_TerminalClient*>(arg);
601 ssize_t bytes;
602 const size_t toRead = 256;
603 char buf[toRead];
604 SC_StringBuffer stack;
606 fd_set fds;
607 FD_ZERO(&fds);
609 bool shouldRead = true;
610 while(shouldRead) {
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));
617 client->onQuit(1);
618 break;
621 if( FD_ISSET(client->mInputCtlPipe[0], &fds) ) {
622 postfl("pipe-in: quit requested\n");
623 break;
626 if( FD_ISSET(STDIN_FD, &fds) ) {
628 while(true) {
629 bytes = read( STDIN_FD, buf, toRead );
631 if( bytes > 0 ) {
632 client->pushCmdLine( stack, buf, bytes );
634 else if( bytes == 0 ) {
635 postfl("pipe-in: EOF. Will quit.\n");
636 client->onQuit(0);
637 shouldRead = false;
638 break;
640 else {
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));
646 client->onQuit(1);
647 shouldRead = false;
648 break;
655 postfl("pipe-in: stopped.\n");
657 return NULL;
660 #else
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;
671 while (shouldRun) {
672 DWORD result = WaitForMultipleObjects( 2, hnds, false, INFINITE );
674 if( result == WAIT_FAILED ) {
675 postfl("pipe-in: wait error.\n");
676 client->onQuit(1);
677 break;
680 int hIndex = result - WAIT_OBJECT_0;
682 if( hIndex == 0 ) {
683 postfl("pipe-in: quit requested.\n");
684 break;
687 if( hIndex == 1 ) {
688 DWORD nAvail;
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");
693 client->onQuit(0);
695 else {
696 postfl("pipe-in: Error trying to peek stdin (%Li). Quitting.\n", err);
697 client->onQuit(1);
699 break;
702 while (nAvail > 0)
704 char buf[256];
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());
708 client->onQuit(1);
709 shouldRun = false;
710 break;
712 else if (nRead > 0) {
713 client->pushCmdLine( stack, buf, nRead );
716 nAvail -= nRead;
721 postfl("pipe-in: stopped.\n");
723 return NULL;
726 #endif
728 void SC_TerminalClient::pushCmdLine( SC_StringBuffer &buf, const char *newData, size_t size)
730 lockInput();
732 bool signal = false;
734 while (size--) {
735 char c = *newData++;
736 switch (c) {
737 case kRecompileLibrary:
738 case kInterpretCmdLine:
739 case kInterpretPrintCmdLine:
740 mInputBuf.append( buf.getData(), buf.getSize() );
741 mInputBuf.append(c);
742 signal = true;
743 buf.reset();
744 break;
746 default:
747 buf.append(c);
751 if(signal) onInput();
753 unlockInput();
759 void SC_TerminalClient::initInput()
762 #ifndef _WIN32
764 if( pipe( mInputCtlPipe ) == -1 ) {
765 postfl("Error creating pipe for input thread control:\n%s\n", strerror(errno));
766 quit(1);
769 #ifdef HAVE_READLINE
771 if (strcmp(gIdeName, "none") == 0) {
772 // Other clients (emacs, vim, ...) won't want to interact through rl
773 mUseReadline = true;
774 return;
777 #endif
779 if( fcntl( STDIN_FD, F_SETFL, O_NONBLOCK ) == -1 ) {
780 postfl("Error setting up non-blocking pipe reading:\n%s\n", strerror(errno));
781 quit(1);
784 #else // !_WIN32
786 mQuitInputEvent = CreateEvent( NULL, false, false, NULL );
787 if( mQuitInputEvent == NULL ) {
788 postfl("Error creating event for input thread control.\n");
789 quit(1);
791 postfl("Created input thread control event.\n");
793 #endif // !_WIN32
797 void SC_TerminalClient::startInput()
799 mInputShouldBeRunning = true;
800 #ifdef HAVE_READLINE
801 if( mUseReadline )
802 pthread_create( &mInputThread, NULL, &SC_TerminalClient::readlineFunc, this );
803 else
804 #endif
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
812 lockInput();
813 mInputShouldBeRunning = false;
814 pthread_cond_signal( &mInputCond );
815 unlockInput();
817 #ifndef _WIN32
818 postfl("main: sending quit command to input thread.\n");
819 char c = 'q';
820 ssize_t bytes = write( mInputCtlPipe[1], &c, 1 );
821 if( bytes < 1 ) { postfl("WARNING: could not send quit command to input thread.\n"); }
823 #else
824 postfl("main: signalling input thread quit event\n");
825 SetEvent( mQuitInputEvent );
826 #endif
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()
837 #ifdef HAVE_READLINE
838 if( mUseReadline ) rl_callback_handler_remove();
839 #endif
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);
855 argvObj->size++;
856 g->gc->GCWrite(argvObj, (PyrObject*)str);
859 return errNone;
862 int SC_TerminalClient::prExit(struct VMGlobals* g, int)
864 int code;
866 int err = slotIntVal(g->sp, &code);
867 if (err) return err;
869 ((SC_TerminalClient*)SC_LanguageClient::instance())->onQuit( code );
871 return errNone;
874 int SC_TerminalClient::prScheduleChanged( struct VMGlobals *g, int numArgsPushed)
876 static_cast<SC_TerminalClient*>(instance())->onScheduleChanged();
877 return errNone;
880 int SC_TerminalClient::prRecompile(struct VMGlobals *, int)
882 static_cast<SC_TerminalClient*>(instance())->onRecompileLibrary();
883 return errNone;
885 // EOF