/* * Copyright (C) 2012-2015 Robin Haberkorn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include "sciteco.h" #include "cmdline.h" #include "interface.h" #include "ioview.h" #include "parser.h" #include "goto.h" #include "qregisters.h" #include "ring.h" #include "undo.h" #include "error.h" /* * Define this to pause the program at the beginning * of main() (Windows only). * This is a useful hack on Windows, where gdbserver * sometimes refuses to start SciTECO but attaches * to a running process just fine. */ //#define DEBUG_PAUSE namespace SciTECO { #define INI_FILE ".teco_ini" /* * defining the global objects here ensures * a ctor/dtor order without depending on the * GCC init_priority() attribute */ InterfaceCurrent interface; IOView QRegisters::view; /* * Scintilla will be initialized after these * ctors (in main()), but dtors are guaranteed * to be executed before Scintilla's * destruction */ QRegisterTable QRegisters::globals; Ring ring; namespace Flags { tecoInt ed = ED_AUTOEOL; } static gchar *eval_macro = NULL; static gchar *mung_file = NULL; static gboolean mung_profile = TRUE; sig_atomic_t sigint_occurred = FALSE; extern "C" { static gpointer g_malloc_exception(gsize n_bytes); static gpointer g_calloc_exception(gsize n_blocks, gsize n_block_bytes); static gpointer g_realloc_exception(gpointer mem, gsize n_bytes); static void sigint_handler(int signal); } const gchar * get_eol_seq(gint eol_mode) { switch (eol_mode) { case SC_EOL_CRLF: return "\r\n"; case SC_EOL_CR: return "\r"; case SC_EOL_LF: default: return "\n"; } } #ifdef G_OS_WIN32 /* * Keep program self-contained under Windows * Look for config files (profile and session), * as well as standard library macros in the * program's directory. */ static inline gchar * get_default_config_path(const gchar *program) { return g_path_get_dirname(program); } #elif defined(G_OS_UNIX) static inline gchar * get_default_config_path(const gchar *program) { return g_strdup(g_getenv("HOME") ? : g_get_home_dir()); } #else static inline gchar * get_default_config_path(const gchar *program) { return g_strdup(g_get_user_config_dir()); } #endif static inline void process_options(int &argc, char **&argv) { static const GOptionEntry option_entries[] = { {"eval", 'e', 0, G_OPTION_ARG_STRING, &eval_macro, "Evaluate macro", "macro"}, {"mung", 'm', 0, G_OPTION_ARG_FILENAME, &mung_file, "Mung file instead of " "$SCITECOCONFIG" G_DIR_SEPARATOR_S INI_FILE, "file"}, {"no-profile", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &mung_profile, "Do not mung " "$SCITECOCONFIG" G_DIR_SEPARATOR_S INI_FILE " even if it exists"}, {NULL} }; GError *gerror = NULL; GOptionContext *options; GOptionGroup *interface_group = interface.get_options(); options = g_option_context_new("[--] [ARGUMENT...]"); g_option_context_set_summary( options, PACKAGE_STRING " -- Scintilla-based Text Editor and COrrector" ); g_option_context_set_description( options, "Bug reports should go to <" PACKAGE_BUGREPORT "> or " "<" PACKAGE_URL_DEV ">." ); g_option_context_add_main_entries(options, option_entries, NULL); if (interface_group) g_option_context_add_group(options, interface_group); if (!g_option_context_parse(options, &argc, &argv, &gerror)) { g_fprintf(stderr, "Option parsing failed: %s\n", gerror->message); g_error_free(gerror); exit(EXIT_FAILURE); } g_option_context_free(options); if (mung_file) { if (!g_file_test(mung_file, G_FILE_TEST_IS_REGULAR)) { g_fprintf(stderr, "Cannot mung \"%s\". File does not exist!\n", mung_file); exit(EXIT_FAILURE); } } /* remaining arguments, are arguments to the interface */ } static inline void initialize_environment(const gchar *program) { gchar *default_configpath, *abs_path; gchar **env; /* * Initialize $SCITECOCONFIG and $SCITECOPATH */ default_configpath = get_default_config_path(program); g_setenv("SCITECOCONFIG", default_configpath, FALSE); #ifdef G_OS_WIN32 gchar *default_scitecopath; default_scitecopath = g_build_filename(default_configpath, "lib", NIL); g_setenv("SCITECOPATH", default_scitecopath, FALSE); g_free(default_scitecopath); #else g_setenv("SCITECOPATH", SCITECOLIBDIR, FALSE); #endif g_free(default_configpath); /* * $SCITECOCONFIG and $SCITECOPATH may still be relative. * They are canonicalized, so macros can use them even * if the current working directory changes. */ abs_path = get_absolute_path(g_getenv("SCITECOCONFIG")); g_setenv("SCITECOCONFIG", abs_path, TRUE); g_free(abs_path); abs_path = get_absolute_path(g_getenv("SCITECOPATH")); g_setenv("SCITECOPATH", abs_path, TRUE); g_free(abs_path); env = g_listenv(); for (gchar **key = env; *key; key++) { gchar name[1 + strlen(*key) + 1]; QRegister *reg; name[0] = '$'; strcpy(name + 1, *key); reg = QRegisters::globals.insert(name); reg->set_string(g_getenv(*key)); } g_strfreev(env); } /* * Callbacks */ class g_bad_alloc : public std::bad_alloc { public: const char * what() const throw() { return "glib allocation"; } }; static gpointer g_malloc_exception(gsize n_bytes) { gpointer p = malloc(n_bytes); if (!p) throw g_bad_alloc(); return p; } static gpointer g_calloc_exception(gsize n_blocks, gsize n_block_bytes) { gpointer p = calloc(n_blocks, n_block_bytes); if (!p) throw g_bad_alloc(); return p; } static gpointer g_realloc_exception(gpointer mem, gsize n_bytes) { gpointer p = realloc(mem, n_bytes); if (!p) throw g_bad_alloc(); return p; } static void sigint_handler(int signal) { sigint_occurred = TRUE; } } /* namespace SciTECO */ /* * main() must be defined in the root * namespace, so we import the "SciTECO" * namespace. We have no more declarations * to make in the "SciTECO" namespace. */ using namespace SciTECO; int main(int argc, char **argv) { static GotoTable cmdline_goto_table; static QRegisterTable local_qregs; static GMemVTable vtable = { g_malloc_exception, /* malloc */ g_realloc_exception, /* realloc */ free, /* free */ g_calloc_exception, /* calloc */ malloc, /* try_malloc */ realloc /* try_realloc */ }; #ifdef DEBUG_PAUSE /* Windows debugging hack (see above) */ system("pause"); #endif signal(SIGINT, sigint_handler); g_mem_set_vtable(&vtable); process_options(argc, argv); interface.main(argc, argv); /* remaining arguments are arguments to the munged file */ /* * QRegister view must be initialized only now * (e.g. after Curses/GTK initialization). */ QRegisters::view.initialize(); /* search string and status register */ QRegisters::globals.insert("_"); /* replacement string register */ QRegisters::globals.insert("-"); /* current buffer name and number ("*") */ QRegisters::globals.insert(new QRegisterBufferInfo()); /* environment defaults and registers */ initialize_environment(argv[0]); QRegisters::locals = &local_qregs; ring.edit((const gchar *)NULL); /* add remaining arguments to unnamed buffer */ for (int i = 1; i < argc; i++) { /* * FIXME: arguments may contain line-feeds. * Once SciTECO is 8-byte clear, we can add the * command-line params null-terminated. */ interface.ssm(SCI_APPENDTEXT, strlen(argv[i]), (sptr_t)argv[i]); interface.ssm(SCI_APPENDTEXT, 1, (sptr_t)"\n"); } /* * Execute macro or mung file */ try { if (eval_macro) { try { Execute::macro(eval_macro, false); } catch (Error &error) { error.add_frame(new Error::ToplevelFrame()); throw; /* forward */ } QRegisters::hook(QRegisters::HOOK_QUIT); exit(EXIT_SUCCESS); } if (!mung_file && mung_profile) mung_file = g_build_filename(g_getenv("SCITECOCONFIG"), INI_FILE, NIL); if (mung_file && g_file_test(mung_file, G_FILE_TEST_IS_REGULAR)) { Execute::file(mung_file, false); /* FIXME: make quit immediate in batch/macro mode (non-UNDO)? */ if (quit_requested) { QRegisters::hook(QRegisters::HOOK_QUIT); exit(EXIT_SUCCESS); } } g_free(mung_file); } catch (Error &error) { error.display_full(); exit(EXIT_FAILURE); } catch (...) { exit(EXIT_FAILURE); } /* * If munged file didn't quit, switch into interactive mode */ /* commandline replacement string register */ QRegisters::globals.insert(CTL_KEY_ESC_STR); Goto::table = &cmdline_goto_table; undo.enabled = true; ring.set_scintilla_undo(true); QRegisters::view.set_scintilla_undo(true); interface.event_loop(); /* * Ordinary application termination: * Interface is shut down, so we are * in non-interactive mode again. */ undo.enabled = false; undo.clear(); /* also empties all Scintilla undo buffers */ ring.set_scintilla_undo(false); QRegisters::view.set_scintilla_undo(false); try { QRegisters::hook(QRegisters::HOOK_QUIT); } catch (Error &error) { error.display_full(); exit(EXIT_FAILURE); } return 0; }