// blassic.cpp // Revision 24-apr-2009 #include "blassic.h" #include "keyword.h" #include "program.h" #include "runner.h" #include "cursor.h" #include "graphics.h" #include "charset.h" #include "sysvar.h" #include "trace.h" #include "util.h" #include "error.h" #include #include #include #include //#include #include #include #include #include #include #ifdef BLASSIC_USE_WINDOWS #include #else #include #include #include #include #endif #if defined __BORLANDC__ && defined _Windows #include #else #define USEUNIT(a) #endif USEUNIT("codeline.cpp"); USEUNIT("error.cpp"); USEUNIT("keyword.cpp"); USEUNIT("program.cpp"); USEUNIT("runner.cpp"); USEUNIT("token.cpp"); USEUNIT("var.cpp"); USEUNIT("dim.cpp"); USEUNIT("file.cpp"); USEUNIT("cursor.cpp"); USEUNIT("graphics.cpp"); USEUNIT("sysvar.cpp"); USEUNIT("version.cpp"); USEUNIT("trace.cpp"); USEUNIT("socket.cpp"); USEUNIT("runnerline.cpp"); USEUNIT("function.cpp"); USEUNIT("key.cpp"); USEUNIT("edit.cpp"); USEUNIT("directory.cpp"); USEUNIT("using.cpp"); USEUNIT("regexp.cpp"); USEUNIT("dynamic.cpp"); USEUNIT("mbf.cpp"); USEUNIT("memory.cpp"); USEUNIT("fileconsole.cpp"); USEUNIT("filepopen.cpp"); USEUNIT("fileprinter.cpp"); USEUNIT("filewindow.cpp"); USEUNIT("runnerline_instructions.cpp"); USEUNIT("showerror.cpp"); USEUNIT("runnerline_impl.cpp"); USEUNIT("runnerline_print.cpp"); USEUNIT("filesocket.cpp"); USEUNIT("charset_spectrum.cpp"); USEUNIT("charset_default.cpp"); USEUNIT("charset_cpc.cpp"); USEUNIT("element.cpp"); //--------------------------------------------------------------------------- bool fInterrupted= false; const std::string strPrompt ("Ok"); namespace sysvar= blassic::sysvar; //************************************************ // Local functions and classes //************************************************ namespace { void set_title (const std::string & str, Runner & runner) { // C++ Builder don't like this. //std::string title ( str.empty () ? std::string ("Blassic") : // (str == "-") ? std::string ("Blassic (stdin)") : str); std::string title; if (str.empty () ) title= "Blassic"; else if (str == "-") title= "Blassic (stdin)"; else title= str; graphics::set_default_title (title); runner.set_title (title); } // Workaround to problem in cygwin: #ifndef SIGBREAK const int SIGBREAK= 21; #endif void handle_sigint (int) { fInterrupted= true; #ifdef BLASSIC_USE_WINDOWS signal (SIGINT, handle_sigint); signal (SIGBREAK, handle_sigint); #endif } #ifndef BLASSIC_USE_WINDOWS void handle_SIGSEGV (int, siginfo_t * info, void *) { fprintf (stderr, "Segmentation fault with si_addr %p" #ifdef HAVE_SIGINFO_T_SI_PTR " si_ptr %p" #endif " and si_code %i\n", info->si_addr, #ifdef HAVE_SIGINFO_T_SI_PTR info->si_ptr, #endif info->si_code); abort (); } #endif void init_signal_handlers () { TRACEFUNC (tr, "init_signal_handlers"); #ifdef BLASSIC_USE_WINDOWS signal (SIGINT, handle_sigint); signal (SIGBREAK, handle_sigint); #else struct sigaction act; act.sa_handler= handle_sigint; act.sa_flags= 0; sigaction (SIGINT, & act, 0); signal (SIGPIPE, SIG_IGN); #ifndef NDEBUG signal (SIGUSR1, TraceFunc::show); #endif // This helps debugging machine code. #ifndef NDEBUG act.sa_handler= NULL; act.sa_sigaction= & handle_SIGSEGV; act.sa_flags= SA_SIGINFO; sigaction (SIGSEGV, & act, 0); #endif #endif } class Initializer { public: Initializer (const char * progname, bool detached) : detached_text (detached), detached_graphics (false) { TRACEFUNC (tr, "Initializer::Initializer"); if (! detached_text) cursor::initconsole (); graphics::initialize (progname); } ~Initializer () { TRACEFUNC (tr, "Initializer::~Initializer"); if (! detached_graphics) graphics::uninitialize (); if (! detached_text) cursor::quitconsole (); } void detachgraphics () { detached_graphics= true; } void detachtext () { detached_text= true; } private: bool detached_text; bool detached_graphics; }; std::vector args; void setprogramargs (char * * argv, size_t n) { TRACEFUNC (tr, "setprogramargs"); sysvar::set16 (sysvar::NumArgs, short (n) ); args.clear (); std::copy (argv, argv + n, std::back_inserter (args) ); } void blassic_runprogram (const std::string & name, Runner & runner) { set_title (name, runner); runner.run (); } } // namespace // These are globals. void setprogramargs (const std::vector & nargs) { TRACEFUNC (tr, "setprogramargs"); sysvar::set16 (sysvar::NumArgs, short (nargs.size () ) ); args= nargs; } std::string getprogramarg (size_t n) { //if (n >= num_args) if (n >= args.size () ) return std::string (); //return std::string (args [n]); return args [n]; } namespace { class ProtectCerrBuf { public: ProtectCerrBuf () : buf (std::cerr.rdbuf () ) { } ~ProtectCerrBuf () { std::cerr.rdbuf (buf); } private: std::streambuf * buf; }; //************************************************ // Options processing. //************************************************ class Options { public: Options (int argc, char * * argv); bool must_run () const; // Public, to read it directly instead of provide accesor. bool detach; bool tron; bool tronline; bool errout; std::string exec_command; std::string print_args; std::string progname; std::string mode; private: bool norun; const int argc; char * * const argv; typedef bool (Options::*handle_option_t) (int & n); typedef std::map mapoption_t; static mapoption_t mapoption; static bool initmapoption (); static bool mapoption_inited; static const mapoption_t::const_iterator no_option; static const char IncompatibleExecPrint []; handle_option_t gethandle (const char * str); bool handle_option_exec (int & n); bool handle_option_print (int & n); bool handle_option_mode (int & n); bool handle_option_detach (int & n); bool handle_option_auto (int & n); bool handle_option_exclude (int & n); bool handle_option_debug (int & n); bool handle_option_info (int & n); bool handle_option_cpc (int & n); bool handle_option_spectrum (int & n); bool handle_option_msx (int & n); bool handle_option_gwbasic (int & n); bool handle_option_appleII (int & n); bool handle_option_allflags (int & n); bool handle_option_rotate (int & n); bool handle_option_lfcr (int & n); bool handle_option_norun (int & n); bool handle_option_tron (int & n); bool handle_option_tronline (int & n); bool handle_option_errout (int & n); bool handle_option_comblank (int & n); bool handle_option_double_dash (int & n); bool handle_option_dash (int & n); bool handle_option_default (int & n); }; Options::Options (int argc, char * * argv) : detach (false), tron (false), tronline (false), errout (false), norun (false), argc (argc), argv (argv) { TRACEFUNC (tr, "Options::Options"); int n= 1; for ( ; n < argc; ++n) { if ( (this->*gethandle (argv [n]) ) (n) ) break; } if (n >= argc) return; progname= argv [n]; ++n; if (n < argc) { int narg= argc - n; setprogramargs (argv + n, narg); } } bool Options::must_run () const { return ! norun && ! progname.empty (); } Options::mapoption_t Options::mapoption; bool Options::initmapoption () { mapoption ["-e"]= & Options::handle_option_exec; mapoption ["-p"]= & Options::handle_option_print; mapoption ["-m"]= & Options::handle_option_mode; mapoption ["-d"]= & Options::handle_option_detach; mapoption ["-a"]= & Options::handle_option_auto; mapoption ["-x"]= & Options::handle_option_exclude; mapoption ["--debug"]= & Options::handle_option_debug; mapoption ["--info"]= & Options::handle_option_info; mapoption ["--cpc"]= & Options::handle_option_cpc; mapoption ["--spectrum"]= & Options::handle_option_spectrum; mapoption ["--msx"]= & Options::handle_option_msx; mapoption ["--gwbasic"]= & Options::handle_option_gwbasic; mapoption ["--appleII"]= & Options::handle_option_appleII; mapoption ["--allflags"]= & Options::handle_option_allflags; mapoption ["--rotate"]= & Options::handle_option_rotate; mapoption ["--lfcr"]= & Options::handle_option_lfcr; mapoption ["--norun"]= & Options::handle_option_norun; mapoption ["--tron"]= & Options::handle_option_tron; mapoption ["--tronline"]= & Options::handle_option_tronline; mapoption ["--errout"]= & Options::handle_option_errout; mapoption ["--comblank"]= & Options::handle_option_comblank; mapoption ["--"]= & Options::handle_option_double_dash; mapoption ["-"]= & Options::handle_option_dash; return true; } bool Options::mapoption_inited= Options::initmapoption (); const Options::mapoption_t::const_iterator Options::no_option= mapoption.end (); const char Options::IncompatibleExecPrint []= "Options -e and -p are incompatibles"; Options::handle_option_t Options::gethandle (const char * str) { mapoption_t::const_iterator it (mapoption.find (str) ); if (it != no_option) return it->second; else { if (str [0] == '-') throw "Invalid option"; else return & Options::handle_option_default; } } bool Options::handle_option_exec (int & n) { if (++n == argc) throw "Option -e needs argument"; if (! exec_command.empty () ) throw "Option -e can only be used once"; if (! print_args.empty () ) throw IncompatibleExecPrint; exec_command= argv [n]; return false; } bool Options::handle_option_print (int & n) { static const char PrintNeedsArgument []= "Option -p needs at least one argument"; if (++n == argc) throw PrintNeedsArgument; if (! exec_command.empty () ) throw IncompatibleExecPrint; bool empty= true; while (n < argc) { if (strcmp (argv [n], "--") == 0) break; if (! print_args.empty () ) print_args+= ": "; print_args+= "PRINT "; print_args+= argv [n]; ++n; empty= false; } if (empty) throw PrintNeedsArgument; return false; } bool Options::handle_option_mode (int & n) { if (++n == argc) throw "Option -m needs argument"; if (!mode.empty () ) throw "Option -m can only be used once"; mode= argv [n]; if (mode.empty () ) throw "Invalid empty string mode"; return false; } bool Options::handle_option_detach (int & /*n*/) { detach= true; return false; } bool Options::handle_option_auto (int & n) { if (++n == argc) throw "Option -a needs argument"; std::istringstream iss (argv [n]); BlLineNumber ini; iss >> ini; char c= char (iss.get () ); if (! iss.eof () ) { if (c != ',') throw "Bad parameter"; BlLineNumber inc; iss >> inc; if (! iss) throw "Bad parameter"; iss >> c; if (! iss.eof () ) throw "Bad parameter"; sysvar::set32 (sysvar::AutoInc, inc); } sysvar::set32 (sysvar::AutoInit, ini); return false; } bool Options::handle_option_exclude (int & n) { if (++n == argc) throw "Option -x needs argument"; excludekeyword (argv [n] ); return false; } bool Options::handle_option_debug (int & n) { if (++n == argc) throw "Option --debug needs argument"; sysvar::set32 (sysvar::DebugLevel, atoi (argv [n] ) ); return false; } bool Options::handle_option_info (int & /* n */) { sysvar::setFlags1 (sysvar::ShowDebugInfo); return false; } bool Options::handle_option_cpc (int & /* n */) { TRACEFUNC (tr, "handle_option_cpc"); sysvar::setFlags1 (sysvar::LocateStyle | sysvar::ThenOmitted | sysvar::SpaceBefore | sysvar::SpaceStr_s); sysvar::set16 (sysvar::Zone, 13); charset::default_charset= & charset::cpc_data; return false; } bool Options::handle_option_spectrum (int & /* n */) { TRACEFUNC (tr, "handle_option_spectrum"); sysvar::set (sysvar::TypeOfVal, 1); sysvar::set (sysvar::TypeOfNextCheck, 1); sysvar::set (sysvar::TypeOfDimCheck, 1); sysvar::setFlags1 (sysvar::TabStyle | sysvar::RelaxedGoto); sysvar::setFlags2 (sysvar::SeparatedGoto | sysvar::TruePositive | sysvar::BoolMode); sysvar::set16 (sysvar::Zone, 16); charset::default_charset= & charset::spectrum_data; return false; } bool Options::handle_option_msx (int & /* n */) { TRACEFUNC (tr, "handle_option_msx"); sysvar::setFlags1 (sysvar::ThenOmitted | sysvar::SpaceBefore | sysvar::SpaceStr_s); sysvar::set16 (sysvar::Zone, 14); charset::default_charset= & charset::msx_data; return false; } bool Options::handle_option_gwbasic (int & /* n */) { TRACEFUNC (tr, "handle_option_gwbasic"); sysvar::setFlags1 (sysvar::ThenOmitted | sysvar::SpaceBefore | sysvar::SpaceStr_s); sysvar::set16 (sysvar::Zone, 14); return false; } bool Options::handle_option_appleII (int & /* n */) { TRACEFUNC (tr, "handle_option_appleII"); // Pending of determine Flags 1 values. //sysvar::setFlags1 (sysvar::LocateStyle | sysvar::ThenOmitted | // sysvar::SpaceBefore | sysvar::SpaceStr_s); sysvar::setFlags2 (sysvar::TruePositive | sysvar::BoolMode); // Pending of stablish Zone value. sysvar::set16 (sysvar::Zone, 13); return false; } bool Options::handle_option_allflags (int & /* n */) { sysvar::setFlags1 (sysvar::Flags1Full); sysvar::setFlags2 (sysvar::Flags2Full); return false; } bool Options::handle_option_rotate (int & /* n */) { sysvar::set (sysvar::GraphRotate, 1); return false; } bool Options::handle_option_lfcr (int & /* n */) { sysvar::setFlags1 (sysvar::ConvertLFCR); return false; } bool Options::handle_option_norun (int & /* n */) { norun= true; return false; } bool Options::handle_option_tron (int & /* n */) { tron= true; return false; } bool Options::handle_option_tronline (int & /* n */) { tron= true; tronline= true; return false; } bool Options::handle_option_errout (int & /* n */) { errout= true; std::cerr.rdbuf (std::cout.rdbuf () ); return false; } bool Options::handle_option_comblank (int & /* n */) { sysvar::setFlags2 (sysvar::BlankComment); return false; } bool Options::handle_option_double_dash (int & n) { ++n; return true; } bool Options::handle_option_dash (int & /* n */) { return true; } bool Options::handle_option_default (int & /*n*/) { return true; } class BlBuf : public std::streambuf { public: BlBuf (GlobalRunner & globalrunner) : globalrunner (globalrunner) { } private: void sendchar (blassic::file::BlFile & f, char c) { if (c == '\n') f.endline (); else f << c; } int overflow (int ch) { sync (); blassic::file::BlFile & f= globalrunner.getfile (DefaultChannel); sendchar (f, static_cast (static_cast (ch) ) ); return 0; } int sync () { blassic::file::BlFile & f= globalrunner.getfile (DefaultChannel); std::streamsize n= pptr () - pbase (); for (std::streamsize i= 0; i < n; ++i) sendchar (f, * (pbase () + i) ); pbump (-n); gbump (egptr () - gptr () ); return 0; } GlobalRunner & globalrunner; }; int blassic_main (int argc, char * * argv) { using std::cerr; using std::endl; TRACEFUNC (tr, "blassic_main"); init_signal_handlers (); sysvar::init (); Options options (argc, argv); //Program program; std::auto_ptr pprogram (newProgram () ); Program & program= * pprogram.get (); // Load program before detaching, or error messages are lost. // Can be to run it or for use with options print or execute. // Seems not very useful with print, but does not hurt and // somebody can find an application. if (! options.progname.empty () ) { if (options.progname == "-") program.load (std::cin); else program.load (options.progname); } // Detach is done before initializing console and graphics. if (options.detach) { #ifdef BLASSIC_USE_WINDOWS FreeConsole (); #else switch (fork () ) { case pid_t (-1): throw "fork failed"; case pid_t (0): // Child. Detach and continue. { bool showdebug= showdebuginfo (); int newstd= open ("/dev/null", O_RDWR); if (newstd == -1) { close (STDIN_FILENO); close (STDOUT_FILENO); if (! showdebug) close (STDERR_FILENO); } else { dup2 (newstd, STDIN_FILENO); dup2 (newstd, STDOUT_FILENO); if (! showdebug) dup2 (newstd, STDERR_FILENO); close (newstd); } } break; default: // Parent: exit inmediately. return 0; } #endif } // Iniittialize console if not detached and graphics. Initializer initializer (argv [0], options.detach); // Execute mode options. bool spectrummode= false; if (! options.mode.empty () ) { if (options.mode.size () > 0 && isdigit (options.mode [0] ) ) { int mode= atoi (options.mode.c_str () ); std::string::size_type x= options.mode.find ('x'); if (x != std::string::npos) { int mode2= atoi (options.mode.c_str () + x + 1); graphics::setmode (mode, mode2, false); } else if (mode != 0) graphics::setmode (mode); } else { graphics::setmode (options.mode); if (options.mode == "spectrum") spectrummode= true; } } // Initialize runners. GlobalRunner globalrunner (program); Runner runner (globalrunner); if (spectrummode) runner.spectrumwindows (); // Save state of cerr now, after possible redirection to cout // but before redirection to program output, to keep it // usable in main. ProtectCerrBuf protectcerrbuf; if (options.errout) { static BlBuf buf (globalrunner); std::cerr.rdbuf (& buf); } // Tron options. if (options.tron) globalrunner.tron (options.tronline, 0); // Execution options. static const char EXECUTING []= "Executing line: "; if (! options.exec_command.empty () ) { if (showdebuginfo () ) cerr << EXECUTING << options.exec_command << endl; CodeLine codeline; codeline.scan (options.exec_command); if (codeline.number () != LineDirectCommand) { program.insert (codeline); runner.run (); } else runner.runline (codeline); return 0; } if (! options.print_args.empty () ) { if (showdebuginfo () ) cerr << EXECUTING << options.print_args << endl; CodeLine codeline; codeline.scan (options.print_args); runner.runline (codeline); return 0; } //if (! options.progname.empty () ) if (options.must_run () ) { blassic_runprogram (options.progname, runner); return 0; } // And if nothing of these... set_title (options.progname, runner); runner.interactive (); return 0; } } // namespace //************************************************ // class Exit //************************************************ Exit::Exit (int ncode) : exitcode (ncode) { } int Exit::code () const { return exitcode; } //************************************************ // main //************************************************ int main (int argc, char * * argv) { using std::cerr; using std::endl; // This can make some speed gain. //std::ios::sync_with_stdio (false); std::cout.sync_with_stdio (false); TRACEFUNC (tr, "main"); int r; try { r= blassic_main (argc, argv); #ifndef NDEBUG std::ostringstream oss; oss << "Returning " << r << " without exception."; TRMESSAGE (tr, oss.str () ); #endif } catch (BlErrNo ben) { cerr << ErrStr (ben) << endl; TRMESSAGE (tr, ErrStr (ben) ); r= 127; } catch (BlError & be) { cerr << be << endl; TRMESSAGE (tr, util::to_string (be) ); r= 127; } catch (BlBreakInPos & bbip) { cerr << bbip << endl; TRMESSAGE (tr, util::to_string (bbip) ); r= 127; } catch (std::exception & e) { cerr << e.what () << endl; TRMESSAGE (tr, e.what () ); r= 127; } catch (Exit & e) { r= e.code (); TRMESSAGE (tr, "Exit " + util::to_string (r) ); } catch (const char * str) { cerr << str << endl; TRMESSAGE (tr, str); r= 127; } catch (...) { cerr << "Unexpected error." << endl; TRMESSAGE (tr, "Unexpected error."); r= 127; } return r; } // End of blassic.cpp