1 /*
2     Commandline interpreter interface.
3     Copyright (c) 2003-2006 stefan kersten.
4     Copyright (c) 2013 tim blechmann.
5 
6     ====================================================================
7 
8     SuperCollider real time audio synthesis system
9     Copyright (c) 2002 James McCartney. All rights reserved.
10     http://www.audiosynth.com
11 
12     This program is free software; you can redistribute it and/or modify
13     it under the terms of the GNU General Public License as published by
14     the Free Software Foundation; either version 2 of the License, or
15     (at your option) any later version.
16 
17     This program is distributed in the hope that it will be useful,
18     but WITHOUT ANY WARRANTY; without even the implied warranty of
19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20     GNU General Public License for more details.
21 
22     You should have received a copy of the GNU General Public License
23     along with this program; if not, write to the Free Software
24     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
25 */
26 
27 #include "SC_TerminalClient.h"
28 #ifdef SC_QT
29 #    include "../../QtCollider/LanguageClient.h"
30 #endif
31 
32 #include <boost/bind.hpp>
33 
34 #ifdef _WIN32
35 #    define __GNU_LIBRARY__
36 #    include "getopt.h"
37 #    include "SC_Win32Utils.h"
38 #    include <io.h>
39 #    include <windows.h>
40 #    include <ioapiset.h>
41 #    include <iostream> // for cerr
42 #endif
43 
44 #ifdef __APPLE__
45 #    include "../../common/SC_Apple.hpp"
46 #endif
47 
48 #ifdef HAVE_READLINE
49 #    include <readline/readline.h>
50 #    include <readline/history.h>
51 #    include <signal.h>
52 #endif
53 
54 #include "GC.h"
55 #include "PyrKernel.h"
56 #include "PyrPrimitive.h"
57 #include "PyrLexer.h"
58 #include "PyrSlot.h"
59 #include "VMGlobals.h"
60 #include "SC_Filesystem.hpp"
61 #include "SC_LanguageConfig.hpp"
62 #include "SC_Version.hpp"
63 
64 #include <boost/filesystem/operations.hpp>
65 
66 static FILE* gPostDest = stdout;
67 
68 #ifdef _WIN32
69 static UINT gOldCodePage; // for remembering the old codepage when we switch to UTF-8
70 #endif
71 
SC_TerminalClient(const char * name)72 SC_TerminalClient::SC_TerminalClient(const char* name):
73     SC_LanguageClient(name),
74     mReturnCode(0),
75     mUseReadline(false),
76     mWork(mIoService),
77     mTimer(mIoService),
78 #ifndef _WIN32
79     mStdIn(mInputService, STDIN_FILENO)
80 #else
81     mStdIn(mInputService, GetStdHandle(STD_INPUT_HANDLE))
82 #endif
83 {
84 }
85 
~SC_TerminalClient()86 SC_TerminalClient::~SC_TerminalClient() {}
87 
postText(const char * str,size_t len)88 void SC_TerminalClient::postText(const char* str, size_t len) { fwrite(str, sizeof(char), len, gPostDest); }
89 
postFlush(const char * str,size_t len)90 void SC_TerminalClient::postFlush(const char* str, size_t len) {
91     fwrite(str, sizeof(char), len, gPostDest);
92     fflush(gPostDest);
93 }
94 
postError(const char * str,size_t len)95 void SC_TerminalClient::postError(const char* str, size_t len) {
96     fprintf(gPostDest, "ERROR: ");
97     fwrite(str, sizeof(char), len, gPostDest);
98 }
99 
flush()100 void SC_TerminalClient::flush() { fflush(gPostDest); }
101 
printUsage()102 void SC_TerminalClient::printUsage() {
103     Options opt;
104 
105     const size_t bufSize = 128;
106     char memGrowBuf[bufSize];
107     char memSpaceBuf[bufSize];
108 
109     snprintMemArg(memGrowBuf, bufSize, opt.mMemGrow);
110     snprintMemArg(memSpaceBuf, bufSize, opt.mMemSpace);
111 
112     fprintf(stdout, "Usage:\n   %s [options] [file..] [-]\n\n", getName());
113     fprintf(
114         stdout,
115         "Options:\n"
116         "   -v                             Print supercollider version and exit\n"
117         "   -d <path>                      Set runtime directory\n"
118         "   -D                             Enter daemon mode (no input)\n"
119         "   -g <memory-growth>[km]         Set heap growth (default %s)\n"
120         "   -h                             Display this message and exit\n"
121         "   -l <path>                      Set library configuration file\n"
122         "   -m <memory-space>[km]          Set initial heap size (default %s)\n"
123         "   -r                             Call Main.run on startup\n"
124         "   -s                             Call Main.stop on shutdown\n"
125         "   -u <network-port-number>       Set UDP listening port (default %d)\n"
126         "   -i <ide-name>                  Specify IDE name (for enabling IDE-specific class code, default \"%s\")\n"
127         "   -a                             Standalone mode (exclude SCClassLibrary and user and system Extensions "
128         "folders from search path)\n",
129         memGrowBuf, memSpaceBuf, opt.mPort, SC_Filesystem::instance().getIdeName().c_str());
130 }
131 
parseOptions(int & argc,char ** & argv,Options & opt)132 bool SC_TerminalClient::parseOptions(int& argc, char**& argv, Options& opt) {
133     const char* optstr = ":d:Dg:hl:m:rsu:i:av";
134     int c;
135 
136     // inhibit error reporting
137     opterr = 0;
138 
139     while ((c = getopt(argc, argv, optstr)) != -1) {
140         switch (c) {
141         case 'd':
142             opt.mRuntimeDir = optarg;
143             break;
144         case 'D':
145             opt.mDaemon = true;
146             break;
147         case 'g':
148             if (!parseMemArg(optarg, &opt.mMemGrow)) {
149                 optopt = c;
150                 goto optArgInvalid;
151             }
152             break;
153         case 'h':
154             goto help;
155         case 'l':
156             opt.mLibraryConfigFile = optarg;
157             break;
158         case 'm':
159             if (!parseMemArg(optarg, &opt.mMemSpace)) {
160                 optopt = c;
161                 goto optArgInvalid;
162             }
163             break;
164         case 'r':
165             opt.mCallRun = true;
166             break;
167         case 'v':
168             fprintf(stdout, "sclang %s (%s)\n", SC_VersionString().c_str(), SC_BuildString().c_str());
169             quit(0);
170             return false;
171             break;
172         case 's':
173             opt.mCallStop = true;
174             break;
175         case 'u':
176             if (!parsePortArg(optarg, &opt.mPort)) {
177                 optopt = c;
178                 goto optArgInvalid;
179             }
180             break;
181         case '?':
182             goto optInvalid;
183             break;
184         case ':':
185             goto optArgExpected;
186             break;
187         case 'i':
188             SC_Filesystem::instance().setIdeName(optarg);
189             break;
190         case 'a':
191             opt.mStandalone = true;
192             break;
193         default:
194             ::post("%s: unknown error (getopt)\n", getName());
195             quit(255);
196             return false;
197         }
198     }
199 
200     argv += optind;
201     argc -= optind;
202 
203     return true;
204 
205 help:
206     printUsage();
207     quit(0);
208     return false;
209 
210 optInvalid:
211     ::post("%s: invalid option -%c\n", getName(), optopt);
212     quit(1);
213     return false;
214 
215 optArgExpected:
216     ::post("%s: missing argument for option -%c\n", getName(), optopt);
217     quit(1);
218     return false;
219 
220 optArgInvalid:
221     ::post("%s: invalid argument for option -%c -- %s\n", getName(), optopt, optarg);
222     quit(1);
223     return false;
224 }
225 
run(int argc,char ** argv)226 int SC_TerminalClient::run(int argc, char** argv) {
227     Options& opt = mOptions;
228 
229     if (!parseOptions(argc, argv, opt)) {
230         return mReturnCode;
231     }
232 
233     // finish argv processing
234     const char* codeFile = nullptr;
235 
236     if (argc > 0) {
237         codeFile = argv[0];
238         opt.mDaemon = true;
239         argv++;
240         argc--;
241     }
242 
243     opt.mArgc = argc;
244     opt.mArgv = argv;
245 
246     // read library configuration file
247     if (opt.mLibraryConfigFile)
248         SC_LanguageConfig::setConfigPath(opt.mLibraryConfigFile);
249     SC_LanguageConfig::readLibraryConfig(opt.mStandalone);
250 
251     // initialize runtime
252     initRuntime(opt);
253 
254     // Create config directory so that it can be used by Quarks, etc. See #2919.
255     if (!opt.mStandalone && !opt.mLibraryConfigFile)
256         boost::filesystem::create_directories(
257             SC_Filesystem::instance().getDirectory(SC_Filesystem::DirName::UserConfig));
258 
259     // startup library
260     compileLibrary(opt.mStandalone);
261 
262     // enter main loop
263     if (codeFile)
264         executeFile(codeFile);
265     if (opt.mCallRun)
266         runMain();
267 
268     if (opt.mDaemon) {
269         daemonLoop();
270     } else {
271         initInput();
272         startInput();
273         commandLoop();
274         endInput();
275         cleanupInput();
276     }
277 
278     if (opt.mCallStop)
279         stopMain();
280 
281     // shutdown library
282     shutdownLibrary();
283     flush();
284 
285     shutdownRuntime();
286 
287     return mReturnCode;
288 }
289 
recompileLibrary()290 void SC_TerminalClient::recompileLibrary() { SC_LanguageClient::recompileLibrary(mOptions.mStandalone); }
291 
quit(int code)292 void SC_TerminalClient::quit(int code) { mReturnCode = code; }
293 
resolveMethodSymbol(bool silent)294 static PyrSymbol* resolveMethodSymbol(bool silent) {
295     if (silent)
296         return s_interpretCmdLine;
297     else
298         return s_interpretPrintCmdLine;
299 }
300 
interpretCmdLine(const char * cmdLine,bool silent)301 void SC_TerminalClient::interpretCmdLine(const char* cmdLine, bool silent) {
302     setCmdLine(cmdLine);
303     runLibrary(resolveMethodSymbol(silent));
304     flush();
305 }
306 
307 
interpretCmdLine(const char * cmdLine,size_t size,bool silent)308 void SC_TerminalClient::interpretCmdLine(const char* cmdLine, size_t size, bool silent) {
309     setCmdLine(cmdLine, size);
310     runLibrary(resolveMethodSymbol(silent));
311     flush();
312 }
313 
314 // Note: called only if the input thread does not perform an asynchronous read operation
interpretInput()315 void SC_TerminalClient::interpretInput() {
316     char* data = mInputBuf.getData();
317     int c = mInputBuf.getSize();
318     int i = 0;
319     while (i < c) {
320         switch (data[i]) {
321         case kInterpretCmdLine:
322             interpretCmdLine(data, i, true);
323             break;
324         case kInterpretPrintCmdLine:
325             interpretCmdLine(data, i, false);
326             break;
327 
328         case kRecompileLibrary:
329             recompileLibrary();
330             break;
331 
332         default:
333             ++i;
334             continue;
335         }
336 
337         data += i + 1;
338         c -= i + 1;
339         i = 0;
340     }
341     mInputBuf.reset();
342 
343     if (mUseReadline)
344         mReadlineSem.post();
345     else
346         startInputRead();
347 }
348 
onLibraryStartup()349 void SC_TerminalClient::onLibraryStartup() {
350     SC_LanguageClient::onLibraryStartup();
351     int base, index = 0;
352     base = nextPrimitiveIndex();
353     definePrimitive(base, index++, "_Argv", &SC_TerminalClient::prArgv, 1, 0);
354     definePrimitive(base, index++, "_Exit", &SC_TerminalClient::prExit, 1, 0);
355     definePrimitive(base, index++, "_AppClock_SchedNotify", &SC_TerminalClient::prScheduleChanged, 1, 0);
356     definePrimitive(base, index++, "_Recompile", &SC_TerminalClient::prRecompile, 1, 0);
357 }
358 
sendSignal(Signal sig)359 void SC_TerminalClient::sendSignal(Signal sig) {
360     switch (sig) {
361     case sig_input:
362         mIoService.post(boost::bind(&SC_TerminalClient::interpretInput, this));
363         break;
364 
365     case sig_recompile:
366         mIoService.post(boost::bind(&SC_TerminalClient::recompileLibrary, this));
367         break;
368 
369     case sig_sched:
370         mIoService.post(boost::bind(&SC_TerminalClient::tick, this, boost::system::error_code()));
371         break;
372 
373     case sig_stop:
374         mIoService.post(boost::bind(&SC_TerminalClient::stopMain, this));
375         break;
376     }
377 }
378 
onQuit(int exitCode)379 void SC_TerminalClient::onQuit(int exitCode) {
380     postfl("main: quit request %i\n", exitCode);
381     quit(exitCode);
382     stop();
383 }
384 
385 extern void ElapsedTimeToChrono(double elapsed, std::chrono::system_clock::time_point& out_time_point);
386 
387 /**
388  * \brief Repeatedly calls the main AppClock loop.
389  *
390  * This method does the following:
391  *
392  * - Wraps \c tickLocked (locking \c gLangMutex before and after), which in
393  *   turn calls \c runLibrary.
394  * - Schedules to call itself again using a \c boost::asio::basic_waitable_timer
395  *   (\c mTimer).
396  *
397  * Since it calls itself, a single call to \c tick will cause the method to
398  * keep calling itself autonomously.
399  *
400  * If \c tick is called again externally while a timer is running, any
401  * previously scheduled \c tick call is canceled. This forces a premature \c
402  * tick, which will schedule itself again, and so on.
403  *
404  * The timed calls to \c tick are used for events scheduled on the AppClock.
405  * The interruption feature is used when sclang receives unanticipated events
406  * such as inbound OSC messages.
407  */
tick(const boost::system::error_code & error)408 void SC_TerminalClient::tick(const boost::system::error_code& error) {
409     /*
410     Even if the timeout is canceled, this callback will always fire (see docs
411     for boost::asio::basic_waitable_timer).
412 
413     Distinguishing between a canceled and successful callback is done by
414     inspecting the error. If it turns out this method was called because of a
415     cancelled timer, we need to bail out and let the tick call that
416     interrupted us take over.
417 
418     If we aren't the result of a canceled timer call, we're either the result
419     of a scheduled timer expiry, or we *are* the interruption and we need to
420     cancel any scheduled tick calls. Calling mTimer.cancel() if the error is
421     a success error code handles both cases.
422 
423     Previously, instead of this if/else block, this was just a call to
424     mTimer.cancel(). This was causing this method to rapidly call itself
425     excessively, hogging the CPU. See discussion at #2144.
426     */
427     if (error == boost::system::errc::success) {
428         mTimer.cancel();
429     } else {
430         return;
431     }
432 
433     double secs;
434     lock();
435     bool haveNext = tickLocked(&secs);
436     unlock();
437 
438     flush();
439 
440     std::chrono::system_clock::time_point nextAbsTime;
441     ElapsedTimeToChrono(secs, nextAbsTime);
442 
443     if (haveNext) {
444         mTimer.expires_at(nextAbsTime);
445         mTimer.async_wait(boost::bind(&SC_TerminalClient::tick, this, _1));
446     }
447 }
448 
commandLoop()449 void SC_TerminalClient::commandLoop() { mIoService.run(); }
450 
daemonLoop()451 void SC_TerminalClient::daemonLoop() { commandLoop(); }
452 
453 #ifdef HAVE_READLINE
454 
sc_rl_cleanlf(void)455 static void sc_rl_cleanlf(void) {
456     rl_reset_line_state();
457     rl_crlf();
458     rl_redisplay();
459 }
460 
sc_rl_signalhandler(int sig)461 static void sc_rl_signalhandler(int sig) {
462     // ensure ctrl-C clears line rather than quitting (ctrl-D will quit nicely)
463     rl_replace_line("", 0);
464     sc_rl_cleanlf();
465 }
466 
sc_rl_mainstop(int i1,int i2)467 static int sc_rl_mainstop(int i1, int i2) {
468     static_cast<SC_TerminalClient*>(SC_LanguageClient::instance())->sendSignal(SC_TerminalClient::sig_stop);
469     sc_rl_cleanlf(); // We also push a newline so that there's some UI feedback
470     return 0;
471 }
472 
473 /*
474 // Completion from sclang dictionary TODO
475 char ** sc_rl_completion (const char *text, int start, int end);
476 char ** sc_rl_completion (const char *text, int start, int end){
477     char **matches = (char **)NULL;
478     printf("sc_rl_completion(%s, %i, %i)\n", text, start, end);
479     return matches;
480 }
481 */
482 
readlineRecompile(int i1,int i2)483 int SC_TerminalClient::readlineRecompile(int i1, int i2) {
484     static_cast<SC_TerminalClient*>(SC_LanguageClient::instance())->sendSignal(sig_recompile);
485     sc_rl_cleanlf();
486     return 0;
487 }
488 
readlineCmdLine(char * cmdLine)489 void SC_TerminalClient::readlineCmdLine(char* cmdLine) {
490     SC_TerminalClient* client = static_cast<SC_TerminalClient*>(instance());
491 
492     if (cmdLine == nullptr) {
493         postfl("\nExiting sclang (ctrl-D)\n");
494         client->onQuit(0);
495         return;
496     }
497 
498     if (*cmdLine != 0) {
499         // If line wasn't empty, store it so that uparrow retrieves it
500         add_history(cmdLine);
501         int len = strlen(cmdLine);
502 
503         client->mInputBuf.append(cmdLine, len);
504         client->mInputBuf.append(kInterpretPrintCmdLine);
505         client->sendSignal(sig_input);
506         client->mReadlineSem.wait();
507     }
508 }
509 
readlineInit()510 void SC_TerminalClient::readlineInit() {
511     // Setup readline
512     rl_readline_name = "sclang";
513     rl_basic_word_break_characters = " \t\n\"\\'`@><=;|&{}().";
514     // rl_attempted_completion_function = sc_rl_completion;
515     rl_bind_key(CTRL('t'), &sc_rl_mainstop);
516     rl_bind_key(CTRL('x'), &readlineRecompile);
517     rl_callback_handler_install("sc3> ", &readlineCmdLine);
518 
519     // FIXME: Implement the code below on Windows
520 #    ifndef _WIN32
521     // Set our handler for SIGINT that will clear the line instead of terminating.
522     // NOTE: We prevent readline from setting its own signal handlers,
523     // to not override ours.
524     rl_catch_signals = 0;
525     struct sigaction sact;
526     memset(&sact, 0, sizeof(struct sigaction));
527     sact.sa_handler = &sc_rl_signalhandler;
528     sigaction(SIGINT, &sact, nullptr);
529 #    endif
530 }
531 
532 #endif // HAVE_READLINE
533 
startInputRead()534 void SC_TerminalClient::startInputRead() {
535 #ifndef _WIN32
536     if (mUseReadline)
537         mStdIn.async_read_some(boost::asio::null_buffers(), boost::bind(&SC_TerminalClient::onInputRead, this, _1, _2));
538     else
539         mStdIn.async_read_some(boost::asio::buffer(inputBuffer),
540                                boost::bind(&SC_TerminalClient::onInputRead, this, _1, _2));
541 #else
542     mStdIn.async_wait([&](const boost::system::error_code& error) {
543         if (error)
544             onInputRead(error, 0);
545         else {
546             DWORD bytes_transferred;
547 
548             ::ReadFile(GetStdHandle(STD_INPUT_HANDLE), inputBuffer.data(), inputBuffer.size(), &bytes_transferred,
549                        nullptr);
550 
551             onInputRead(error, bytes_transferred);
552         }
553     });
554 #endif
555 }
556 
onInputRead(const boost::system::error_code & error,std::size_t bytes_transferred)557 void SC_TerminalClient::onInputRead(const boost::system::error_code& error, std::size_t bytes_transferred) {
558     if (error == boost::asio::error::operation_aborted) {
559         postfl("SCLang Input: Quit requested\n");
560         return;
561     }
562 
563     if (error == boost::asio::error::eof) {
564         postfl("SCLang Input: EOF. Will quit.\n");
565         onQuit(0);
566         return;
567     }
568 
569     if (error) {
570         postfl("SCLang Input: %s.\n", error.message().c_str());
571         onQuit(1);
572         return;
573     }
574 
575     if (!error) {
576 #if HAVE_READLINE
577         if (mUseReadline) {
578             rl_callback_read_char();
579             startInputRead();
580             return;
581         }
582 #endif
583         pushCmdLine(inputBuffer.data(), bytes_transferred);
584     }
585 }
586 
inputThreadFn()587 void SC_TerminalClient::inputThreadFn() {
588 #if HAVE_READLINE
589     if (mUseReadline)
590         readlineInit();
591 #endif
592 
593 #ifdef _WIN32
594     // make sure there's nothing on stdin before we launch the service
595     // this fixes #4214
596     DWORD bytesRead = 0;
597     auto success = ReadFile(GetStdHandle(STD_INPUT_HANDLE), inputBuffer.data(), inputBuffer.size(), &bytesRead, NULL);
598 
599     if (success) {
600         pushCmdLine(inputBuffer.data(), bytesRead);
601     }
602 #endif
603 
604     startInputRead();
605 
606     boost::asio::io_service::work work(mInputService);
607     mInputService.run();
608 }
609 
610 
pushCmdLine(const char * newData,size_t size)611 void SC_TerminalClient::pushCmdLine(const char* newData, size_t size) {
612     bool signal = false;
613     while (size--) {
614         char c = *newData++;
615         switch (c) {
616         case kRecompileLibrary:
617         case kInterpretCmdLine:
618         case kInterpretPrintCmdLine:
619             mInputBuf.append(mInputThrdBuf.getData(), mInputThrdBuf.getSize());
620             mInputBuf.append(c);
621             signal = true;
622             mInputThrdBuf.reset();
623             break;
624 
625         default:
626             mInputThrdBuf.append(c);
627         }
628     }
629 
630     if (signal)
631         sendSignal(sig_input);
632     else
633         startInputRead();
634 }
635 
636 
initInput()637 void SC_TerminalClient::initInput() {
638 #ifdef HAVE_READLINE
639     if (!SC_Filesystem::instance().usingIde()) {
640         // Other clients (emacs, vim, ...) won't want to interact through rl
641         mUseReadline = true;
642         return;
643     }
644 #endif
645 }
646 
647 
startInput()648 void SC_TerminalClient::startInput() {
649     SC_Thread thread(std::bind(&SC_TerminalClient::inputThreadFn, this));
650     mInputThread = std::move(thread);
651 }
652 
endInput()653 void SC_TerminalClient::endInput() {
654     mInputService.stop();
655     mStdIn.cancel();
656 #ifdef _WIN32
657     // Note this breaks Windows XP compatibility, since this function is only defined in Vista and later
658     ::CancelIoEx(GetStdHandle(STD_INPUT_HANDLE), nullptr);
659 #endif
660     postfl("main: waiting for input thread to join...\n");
661     mInputThread.join();
662     postfl("main: quitting...\n");
663 }
664 
cleanupInput()665 void SC_TerminalClient::cleanupInput() {
666 #ifdef HAVE_READLINE
667     if (mUseReadline)
668         rl_callback_handler_remove();
669 #endif
670 }
671 
prArgv(struct VMGlobals * g,int)672 int SC_TerminalClient::prArgv(struct VMGlobals* g, int) {
673     int argc = ((SC_TerminalClient*)SC_TerminalClient::instance())->options().mArgc;
674     char** argv = ((SC_TerminalClient*)SC_TerminalClient::instance())->options().mArgv;
675 
676     PyrSlot* argvSlot = g->sp;
677 
678     PyrObject* argvObj = newPyrArray(g->gc, argc * sizeof(PyrObject), 0, true);
679     SetObject(argvSlot, argvObj); // this is okay here as we don't use the receiver
680 
681     for (int i = 0; i < argc; i++) {
682         PyrString* str = newPyrString(g->gc, argv[i], 0, true);
683         SetObject(argvObj->slots + i, str);
684         argvObj->size++;
685         g->gc->GCWriteNew(argvObj, (PyrObject*)str); // we know str is white so we can use GCWriteNew
686     }
687 
688     return errNone;
689 }
690 
prExit(struct VMGlobals * g,int)691 int SC_TerminalClient::prExit(struct VMGlobals* g, int) {
692     int code;
693 
694     int err = slotIntVal(g->sp, &code);
695     if (err)
696         return err;
697 
698     ((SC_TerminalClient*)SC_LanguageClient::instance())->onQuit(code);
699 
700     return errNone;
701 }
702 
prScheduleChanged(struct VMGlobals * g,int numArgsPushed)703 int SC_TerminalClient::prScheduleChanged(struct VMGlobals* g, int numArgsPushed) {
704     static_cast<SC_TerminalClient*>(instance())->sendSignal(sig_sched);
705     return errNone;
706 }
707 
prRecompile(struct VMGlobals *,int)708 int SC_TerminalClient::prRecompile(struct VMGlobals*, int) {
709     static_cast<SC_TerminalClient*>(instance())->sendSignal(sig_recompile);
710     return errNone;
711 }
712 
createLanguageClient(const char * name)713 SCLANG_DLLEXPORT SC_LanguageClient* createLanguageClient(const char* name) {
714     if (SC_LanguageClient::instance())
715         return nullptr;
716 
717 #ifdef __APPLE__
718     SC::Apple::disableAppNap();
719 #endif
720 
721 #ifdef _WIN32
722     // set codepage to UTF-8
723     gOldCodePage = GetConsoleOutputCP();
724     if (!SetConsoleOutputCP(65001))
725         std::cerr << "WARNING: could not set codepage to UTF-8" << std::endl;
726 #endif
727 
728 #ifdef SC_QT
729     return new QtCollider::LangClient(name);
730 #else
731     return new SC_TerminalClient(name);
732 #endif
733 }
734 
destroyLanguageClient(class SC_LanguageClient * languageClient)735 SCLANG_DLLEXPORT void destroyLanguageClient(class SC_LanguageClient* languageClient) {
736 #ifdef _WIN32
737     // reset codepage from UTF-8
738     SetConsoleOutputCP(gOldCodePage);
739 #endif
740     delete languageClient;
741 }
742