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"
33 # include "SC_Win32Utils.h"
36 # include <sys/param.h>
37 # include <sys/poll.h>
45 #include "PyrKernel.h"
46 #include "PyrPrimitive.h"
48 #include "VMGlobals.h"
49 #include "SC_DirUtils.h" // for gIdeName
51 static FILE* gPostDest
= stdout
;
53 SC_TerminalClient::SC_TerminalClient(const char* name
)
54 : SC_LanguageClient(name
),
55 mShouldBeRunning(false),
60 void SC_TerminalClient::postText(const char* str
, size_t len
)
62 fwrite(str
, sizeof(char), len
, gPostDest
);
65 void SC_TerminalClient::postFlush(const char* str
, size_t len
)
67 fwrite(str
, sizeof(char), len
, gPostDest
);
71 void SC_TerminalClient::postError(const char* str
, size_t len
)
73 fprintf(gPostDest
, "ERROR: ");
74 fwrite(str
, sizeof(char), len
, gPostDest
);
77 void SC_TerminalClient::flush()
82 void SC_TerminalClient::printUsage()
86 const size_t bufSize
= 128;
87 char memGrowBuf
[bufSize
];
88 char memSpaceBuf
[bufSize
];
90 snprintMemArg(memGrowBuf
, bufSize
, opt
.mMemGrow
);
91 snprintMemArg(memSpaceBuf
, bufSize
, opt
.mMemSpace
);
93 fprintf(stdout
, "Usage:\n %s [options] [file..] [-]\n\n", getName());
96 " -d <path> Set runtime directory\n"
97 " -D Enter daemon mode (no input)\n"
98 " -g <memory-growth>[km] Set heap growth (default %s)\n"
99 " -h Display this message and exit\n"
100 " -l <path> Set library configuration file\n"
101 " -m <memory-space>[km] Set initial heap size (default %s)\n"
102 " -r Call Main.run on startup\n"
103 " -s Call Main.stop on shutdown\n"
104 " -u <network-port-number> Set UDP listening port (default %d)\n"
105 " -i <ide-name> Specify IDE name (for enabling IDE-specific class code, default \"%s\")\n",
113 bool SC_TerminalClient::parseOptions(int& argc
, char**& argv
, Options
& opt
)
115 const char* optstr
= ":d:Dg:hl:m:rsu:i:";
118 // inhibit error reporting
121 while ((c
= getopt(argc
, argv
, optstr
)) != -1) {
124 opt
.mRuntimeDir
= optarg
;
130 if (!parseMemArg(optarg
, &opt
.mMemGrow
)) {
138 opt
.mLibraryConfigFile
= optarg
;
141 if (!parseMemArg(optarg
, &opt
.mMemSpace
)) {
150 opt
.mCallStop
= true;
153 if (!parsePortArg(optarg
, &opt
.mPort
)) {
168 ::post("%s: unknown error (getopt)\n", getName());
185 ::post("%s: invalid option -%c\n", getName(), optopt
);
190 ::post("%s: missing argument for option -%c\n", getName(), optopt
);
195 ::post("%s: invalid argument for option -%c -- %s\n", getName(), optopt
, optarg
);
200 int SC_TerminalClient::run(int argc
, char** argv
)
202 Options
& opt
= mOptions
;
204 if (!parseOptions(argc
, argv
, opt
)) {
208 // finish argv processing
209 const char* codeFile
= 0;
220 // read library configuration file
222 if (opt
.mLibraryConfigFile
) {
223 readLibraryConfig(opt
.mLibraryConfigFile
, opt
.mLibraryConfigFile
);
225 readDefaultLibraryConfig();
228 // initialize runtime
232 mShouldBeRunning
= true;
236 if (codeFile
) executeFile(codeFile
);
237 if (opt
.mCallRun
) runMain();
239 if (opt
.mDaemon
) daemonLoop();
242 if (opt
.mCallStop
) stopMain();
251 void SC_TerminalClient::quit(int code
)
254 mShouldBeRunning
= false;
257 bool SC_TerminalClient::readCmdLine(int fd
, SC_StringBuffer
& cmdLine
)
259 const int bufSize
= 256;
262 int n
= read(fd
, buf
, bufSize
);
268 if (c
== kInterpretCmdLine
) {
269 interpretCmdLine(s_interpretCmdLine
, cmdLine
);
270 } else if (c
== kInterpretPrintCmdLine
) {
271 interpretCmdLine(s_interpretPrintCmdLine
, cmdLine
);
281 } else if (errno
!= EAGAIN
) {
289 void SC_TerminalClient::interpretCmdLine(PyrSymbol
* method
, SC_StringBuffer
& cmdLine
)
297 void SC_TerminalClient::commandLoop()
301 struct pollfd pfds
[1] = { fd
, POLLIN
, 0 };
302 SC_StringBuffer cmdLine
;
304 if (fcntl(fd
, F_SETFL
, O_NONBLOCK
) == -1) {
310 while (shouldBeRunning()) {
312 int nfds
= poll(pfds
, POLLIN
, 50);
314 while (readCmdLine(fd
, cmdLine
));
316 if(pfds
[0].revents
== POLLNVAL
){
317 // we reach here when reading directly from CLI, but not if being piped data! (osx 10.4.11 and 10.5.7 at least)
321 } else if (nfds
== -1) {
333 # define nanosleep(tv, tz) win32_nanosleep(tv, tz)
336 void SC_TerminalClient::daemonLoop()
338 struct timespec tv
= { 0, 500000 };
340 while (shouldBeRunning()) {
341 tick(); // also flushes post buffer
342 if (nanosleep(&tv
, 0) == -1) {
350 int SC_TerminalClient::prArgv(struct VMGlobals
* g
, int)
352 int argc
= ((SC_TerminalClient
*)SC_TerminalClient::instance())->options().mArgc
;
353 char** argv
= ((SC_TerminalClient
*)SC_TerminalClient::instance())->options().mArgv
;
355 PyrSlot
* argvSlot
= g
->sp
;
357 PyrObject
* argvObj
= newPyrArray(g
->gc
, argc
* sizeof(PyrObject
), 0, true);
358 SetObject(argvSlot
, argvObj
);
360 for (int i
=0; i
< argc
; i
++) {
361 PyrString
* str
= newPyrString(g
->gc
, argv
[i
], 0, true);
362 SetObject(argvObj
->slots
+i
, str
);
364 g
->gc
->GCWrite(argvObj
, (PyrObject
*)str
);
370 int SC_TerminalClient::prExit(struct VMGlobals
* g
, int)
374 int err
= slotIntVal(g
->sp
, &code
);
377 ((SC_TerminalClient
*)SC_LanguageClient::instance())->quit(code
);
382 void SC_TerminalClient::onLibraryStartup()
385 base
= nextPrimitiveIndex();
386 definePrimitive(base
, index
++, "_Argv", &SC_TerminalClient::prArgv
, 1, 0);
387 definePrimitive(base
, index
++, "_Exit", &SC_TerminalClient::prExit
, 1, 0);