1 /*
2     Abstract interpreter interface.
3     Copyright (c) 2003 2004 stefan kersten.
4 
5     ====================================================================
6 
7     SuperCollider real time audio synthesis system
8     Copyright (c) 2002 James McCartney. All rights reserved.
9     http://www.audiosynth.com
10 
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.
15 
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.
20 
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
24 */
25 
26 #include "SC_LanguageClient.h"
27 #include "SC_Lock.h"
28 #include <cstring>
29 #include <string>
30 #include <cerrno>
31 
32 #ifdef _WIN32
33 #    include <stdio.h>
34 #    include <direct.h>
35 #    define snprintf _snprintf
36 #    ifndef PATH_MAX
37 #        define PATH_MAX _MAX_PATH
38 #    endif
39 #else
40 #    include <unistd.h>
41 #endif
42 
43 #include "PyrObject.h"
44 #include "PyrKernel.h"
45 #include "PyrPrimitive.h"
46 #include "PyrSched.h"
47 #include "GC.h"
48 #include "VMGlobals.h"
49 #include "SCBase.h"
50 #include "SC_StringBuffer.h"
51 
52 void closeAllGUIScreens();
53 void initGUI();
54 void initGUIPrimitives();
55 
56 extern PyrString* newPyrStringN(class PyrGC* gc, long length, long flags, long collect);
57 
58 // =====================================================================
59 // SC_LanguageClient
60 // =====================================================================
61 
62 SC_LanguageClient* gInstance = nullptr;
63 SC_Lock gInstanceMutex;
64 
65 class HiddenLanguageClient {
66 public:
HiddenLanguageClient()67     HiddenLanguageClient(): mPostFile(nullptr), mScratch(0), mRunning(false) {}
68 
69     std::string mName;
70     FILE* mPostFile;
71     SC_StringBuffer mScratch;
72     bool mRunning;
73 };
74 
SC_LanguageClient(const char * name)75 SC_LanguageClient::SC_LanguageClient(const char* name) {
76     mHiddenClient = new HiddenLanguageClient;
77 
78     lockInstance();
79 
80     if (gInstance) {
81         unlockInstance();
82         fprintf(stderr, "SC_LanguageClient already running\n");
83         abort();
84     }
85 
86     mHiddenClient->mName = name;
87     gInstance = this;
88 
89     unlockInstance();
90 }
91 
~SC_LanguageClient()92 SC_LanguageClient::~SC_LanguageClient() {
93     lockInstance();
94     gInstance = nullptr;
95     delete mHiddenClient;
96     unlockInstance();
97 }
98 
initRuntime(const Options & opt)99 void SC_LanguageClient::initRuntime(const Options& opt) {
100     // start virtual machine
101     if (!mHiddenClient->mRunning) {
102         mHiddenClient->mRunning = true;
103         if (opt.mRuntimeDir) {
104             int err = chdir(opt.mRuntimeDir);
105             if (err)
106                 error("Cannot change to runtime directory: %s", strerror(errno));
107         }
108         pyr_init_mem_pools(opt.mMemSpace, opt.mMemGrow);
109         init_OSC(opt.mPort);
110         schedInit();
111         onInitRuntime();
112     }
113 }
114 
shutdownRuntime()115 void SC_LanguageClient::shutdownRuntime() {
116     schedCleanup();
117     cleanup_OSC();
118 }
119 
compileLibrary(bool standalone)120 void SC_LanguageClient::compileLibrary(bool standalone) { ::compileLibrary(standalone); }
121 
122 extern void shutdownLibrary();
shutdownLibrary()123 void SC_LanguageClient::shutdownLibrary() {
124     ::shutdownLibrary();
125     flush();
126 }
127 
recompileLibrary(bool standalone)128 void SC_LanguageClient::recompileLibrary(bool standalone) { compileLibrary(standalone); }
129 
setCmdLine(const char * buf,size_t size)130 void SC_LanguageClient::setCmdLine(const char* buf, size_t size) {
131     if (isLibraryCompiled()) {
132         lock();
133         if (isLibraryCompiled()) {
134             VMGlobals* g = gMainVMGlobals;
135 
136             PyrString* strobj = newPyrStringN(g->gc, size, 0, true);
137             memcpy(strobj->s, buf, size);
138 
139             SetObject(&slotRawInterpreter(&g->process->interpreter)->cmdLine, strobj);
140             g->gc->GCWriteNew(slotRawObject(&g->process->interpreter),
141                               strobj); // we know strobj is white so we can use GCWriteNew
142         }
143         unlock();
144     }
145 }
146 
setCmdLine(const char * str)147 void SC_LanguageClient::setCmdLine(const char* str) { setCmdLine(str, strlen(str)); }
148 
setCmdLinef(const char * fmt,...)149 void SC_LanguageClient::setCmdLinef(const char* fmt, ...) {
150     SC_StringBuffer& scratch = mHiddenClient->mScratch;
151     va_list ap;
152     va_start(ap, fmt);
153     scratch.reset();
154     scratch.vappendf(fmt, ap);
155     va_end(ap);
156     setCmdLine(scratch.getData());
157 }
158 
runLibrary(PyrSymbol * symbol)159 void SC_LanguageClient::runLibrary(PyrSymbol* symbol) {
160     lock();
161     ::runLibrary(symbol);
162     unlock();
163 }
164 
runLibrary(const char * methodName)165 void SC_LanguageClient::runLibrary(const char* methodName) {
166     lock();
167     ::runLibrary(getsym(methodName));
168     unlock();
169 }
170 
executeFile(const char * fileName)171 void SC_LanguageClient::executeFile(const char* fileName) {
172     std::string escaped_file_name(fileName);
173     int i = 0;
174     while (i < escaped_file_name.size()) {
175         if (escaped_file_name[i] == '\\')
176             escaped_file_name.insert(++i, 1, '\\');
177         ++i;
178     }
179 
180     setCmdLinef("thisProcess.interpreter.executeFile(\"%s\")", escaped_file_name.c_str());
181     runLibrary(s_interpretCmdLine);
182 }
183 
snprintMemArg(char * dst,size_t size,int arg)184 void SC_LanguageClient::snprintMemArg(char* dst, size_t size, int arg) {
185     int rem = arg;
186     int mod = 0;
187     const char* modstr = "";
188 
189     while (((rem % 1024) == 0) && (mod < 4)) {
190         rem /= 1024;
191         mod++;
192     }
193 
194     switch (mod) {
195     case 0:
196         modstr = "";
197         break;
198     case 1:
199         modstr = "k";
200         break;
201     case 2:
202         modstr = "m";
203         break;
204     case 3:
205         modstr = "g";
206         break;
207     default:
208         rem = arg;
209         modstr = "";
210         break;
211     }
212 
213     snprintf(dst, size, "%d%s", rem, modstr);
214 }
215 
parseMemArg(const char * arg,int * res)216 bool SC_LanguageClient::parseMemArg(const char* arg, int* res) {
217     long value, factor = 1;
218     char* endPtr = nullptr;
219 
220     if (*arg == '\0')
221         return false;
222 
223     value = strtol(arg, &endPtr, 0);
224 
225     char spec = *endPtr++;
226     if (spec != '\0') {
227         if (*endPtr != '\0')
228             // trailing characters
229             return false;
230 
231         switch (spec) {
232         case 'k':
233             factor = 1024;
234             break;
235         case 'm':
236             factor = 1024 * 1024;
237             break;
238         default:
239             // invalid mem spec
240             return false;
241         }
242     }
243 
244     *res = value * factor;
245 
246     return true;
247 }
248 
parsePortArg(const char * arg,int * res)249 bool SC_LanguageClient::parsePortArg(const char* arg, int* res) {
250     long value;
251     char* endPtr;
252 
253     if (*arg == '\0')
254         return false;
255 
256     value = strtol(arg, &endPtr, 0);
257 
258     if ((*endPtr != '\0') || (value < 0) || (value > 65535))
259         return false;
260 
261     *res = value;
262 
263     return true;
264 }
265 
tick()266 void SC_LanguageClient::tick() {
267     if (trylock()) {
268         if (isLibraryCompiled()) {
269             ::runLibrary(s_tick);
270         }
271         unlock();
272     }
273     flush();
274 }
275 
tickLocked(double * nextTime)276 bool SC_LanguageClient::tickLocked(double* nextTime) {
277     if (isLibraryCompiled()) {
278         ::runLibrary(s_tick);
279     }
280 
281     return slotDoubleVal(&gMainVMGlobals->result, nextTime) == errNone;
282 }
283 
onInitRuntime()284 void SC_LanguageClient::onInitRuntime() {}
285 
onLibraryStartup()286 void SC_LanguageClient::onLibraryStartup() {}
287 
onLibraryShutdown()288 void SC_LanguageClient::onLibraryShutdown() {}
289 
onInterpStartup()290 void SC_LanguageClient::onInterpStartup() {}
291 
292 // runLibrary methods
interpretCmdLine()293 void SC_LanguageClient::interpretCmdLine() { runLibrary(s_interpretCmdLine); }
interpretPrintCmdLine()294 void SC_LanguageClient::interpretPrintCmdLine() { runLibrary(s_interpretPrintCmdLine); }
runMain()295 void SC_LanguageClient::runMain() { runLibrary(s_run); }
stopMain()296 void SC_LanguageClient::stopMain() { runLibrary(s_stop); }
297 
298 // locks
lock()299 void SC_LanguageClient::lock() { gLangMutex.lock(); }
trylock()300 bool SC_LanguageClient::trylock() { return gLangMutex.try_lock(); }
unlock()301 void SC_LanguageClient::unlock() { gLangMutex.unlock(); }
302 
instance()303 SC_LanguageClient* SC_LanguageClient::instance() { return gInstance; }
304 
lockInstance()305 void SC_LanguageClient::lockInstance() { gInstanceMutex.lock(); }
unlockInstance()306 void SC_LanguageClient::unlockInstance() { gInstanceMutex.unlock(); }
307 
308 extern bool compiledOK;
309 
getName() const310 const char* SC_LanguageClient::getName() const { return mHiddenClient->mName.c_str(); }
311 
getPostFile()312 FILE* SC_LanguageClient::getPostFile() { return mHiddenClient->mPostFile; }
setPostFile(FILE * file)313 void SC_LanguageClient::setPostFile(FILE* file) { mHiddenClient->mPostFile = file; }
314 
isLibraryCompiled()315 bool SC_LanguageClient::isLibraryCompiled() { return compiledOK; }
316 
run(int argc,char ** argv)317 int SC_LanguageClient::run(int argc, char** argv) {
318     throw std::runtime_error("SC_LanguageClient::run only supported on terminal client");
319 }
320 
321 // =====================================================================
322 // library functions
323 // =====================================================================
324 
setPostFile(FILE * file)325 void setPostFile(FILE* file) { SC_LanguageClient::instance()->setPostFile(file); }
326 
vpost(const char * fmt,va_list ap)327 int vpost(const char* fmt, va_list ap) {
328     char buf[512];
329     int n = vsnprintf(buf, sizeof(buf), fmt, ap);
330     if (n > 0) {
331         SC_LanguageClient* client = SC_LanguageClient::lockedInstance();
332         if (client)
333             client->postText(buf, sc_min(n, sizeof(buf) - 1));
334         SC_LanguageClient::unlockInstance();
335     }
336     return 0;
337 }
338 
post(const char * fmt,...)339 void post(const char* fmt, ...) {
340     va_list ap;
341     va_start(ap, fmt);
342     vpost(fmt, ap);
343     va_end(ap);
344 }
345 
postfl(const char * fmt,...)346 void postfl(const char* fmt, ...) {
347     char buf[512];
348     va_list ap;
349     va_start(ap, fmt);
350     int n = vsnprintf(buf, sizeof(buf), fmt, ap);
351     if (n > 0) {
352         SC_LanguageClient* client = SC_LanguageClient::lockedInstance();
353         if (client)
354             client->postFlush(buf, sc_min(n, sizeof(buf) - 1));
355         SC_LanguageClient::unlockInstance();
356     }
357     va_end(ap);
358 }
359 
postText(const char * str,long len)360 void postText(const char* str, long len) {
361     SC_LanguageClient* client = SC_LanguageClient::lockedInstance();
362     if (client)
363         client->postFlush(str, len);
364     SC_LanguageClient::unlockInstance();
365 }
366 
postChar(char c)367 void postChar(char c) {
368     SC_LanguageClient* client = SC_LanguageClient::lockedInstance();
369     if (client)
370         client->postFlush(&c, sizeof(char));
371     SC_LanguageClient::unlockInstance();
372 }
373 
error(const char * fmt,...)374 void error(const char* fmt, ...) {
375     char buf[512];
376     va_list ap;
377     va_start(ap, fmt);
378     int n = vsnprintf(buf, sizeof(buf), fmt, ap);
379     if (n > 0) {
380         SC_LanguageClient* client = SC_LanguageClient::lockedInstance();
381         if (client)
382             client->postError(buf, sc_min(n, sizeof(buf) - 1));
383         SC_LanguageClient::unlockInstance();
384     }
385     va_end(ap);
386 }
387 
flushPostBuf(void)388 void flushPostBuf(void) { SC_LanguageClient::instance()->flush(); }
389 
closeAllGUIScreens()390 void closeAllGUIScreens() { SC_LanguageClient::instance()->onLibraryShutdown(); }
391 
initGUI()392 void initGUI() { SC_LanguageClient::instance()->onInterpStartup(); }
393 
initGUIPrimitives()394 void initGUIPrimitives() { SC_LanguageClient::instance()->onLibraryStartup(); }
395 
396 long scMIDIout(int port, int len, int statushi, int chan, int data1, int data2);
scMIDIout(int port,int len,int statushi,int chan,int data1,int data2)397 long scMIDIout(int port, int len, int statushi, int chan, int data1, int data2) { return 0; }
398 // EOF
399