diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2016-11-16 16:06:12 +0100 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2016-11-18 07:05:52 +0100 |
commit | cb5e08b40d7444817c7eb6c1e4e8bf5208c2823c (patch) | |
tree | b0eb53e43e896415acf7362551a71828b6f9eaa7 | |
parent | e7867fb0d9979c550e6e3d7597ece73b680c4af6 (diff) | |
download | sciteco-cb5e08b40d7444817c7eb6c1e4e8bf5208c2823c.tar.gz |
improved command line option handling
* it turns out that option-like arguments could not be reliably passed to
SciTECO scripts for two reasons:
a) "--" arguments are not removed from argv by GOption if it detects
and following option-like argument.
"--" would thus be passed as a script argument which will disable
option parsing in scripts that interpret "--".
b) A script run via the Hash-Bang line "#!...sciteco -m" would
require an explicit "--" to turn of GOption parsing.
However it is __impossible__ to insert after the script file name
on UNIX.
* Therefore, SciTECO now removes leading "--" arguments left over by GOption.
* If possible (Glib >= 2.44), option parsing is performed in strict POSIX
mode which inhibits parsing after the first non-option argument.
This reduces the number of cases where an explicit "--" is required.
* --mung no longer takes an argument. Instead, the first non-option argument
is expected to be the script file name.
This looks weird at first but is more consistent with how other interpeters
work. Once we revise argument passing to scripts, the script name can also
be passed to the script which is more consistent with it being the first
non-option argument.
Also, with strict POSIX parsing, this fixed Hash-Bang lines since
the script file name constructed by the kernel will automatically switch
off option parsing, passing all option-like script arguments uninterpreted
to the script.
* Since we're supporting Glib < 2.44, the Hash-Bang lines are still broken
for certain builds.
Therefore, a wrapper script is installed to libexecdir (it never has to be
executed by users and Hash-Bang lines need absolute paths anyway) which
transparently inserts "--" into the SciTECO command line and should be used
as the interpreter in portable SciTECO scripts.
The wrapper script is generated and points to the exact SciTECO binary
installed. This is important when doing parallel installs of Curses and Gtk
binaries since each one will get its own working wrapper script.
The wrapper-script workaround can be removed once we depend on Glib >= 2.44
(some day...).
* The default /usr/bin/env Hash-Bang lines are no longer used in the
scripts since they are broken anyway (UNIX incl. Linux cannot pass
multiple arguments to the interpreter!).
Scripts that get installed will get a fixed-up Hash-Bang line referring
to the installed SciTECO binary anyway.
* Interface::main() has been renamed to Interface::init() and is optional
now. The Interface::main() method was introduced because of the misconception
that interfaces will find their options in the argv array and have to do
their own parsing.
This is wrong, since their option group already cares about parsing.
Therefore, gtk_init() does not have to called explicitly, too.
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | bootstrap.am | 1 | ||||
-rwxr-xr-x | doc/grosciteco.tes | 2 | ||||
-rwxr-xr-x | doc/htbl.tes | 2 | ||||
-rw-r--r-- | doc/sciteco.1.in | 42 | ||||
-rw-r--r-- | src/Makefile.am | 21 | ||||
-rw-r--r-- | src/interface-curses/interface-curses.cpp | 10 | ||||
-rw-r--r-- | src/interface-curses/interface-curses.h | 4 | ||||
-rw-r--r-- | src/interface-gtk/interface-gtk.cpp | 10 | ||||
-rw-r--r-- | src/interface-gtk/interface-gtk.h | 4 | ||||
-rw-r--r-- | src/interface.h | 7 | ||||
-rw-r--r-- | src/main.cpp | 92 | ||||
-rwxr-xr-x | src/symbols-extract.tes | 2 |
13 files changed, 149 insertions, 49 deletions
@@ -31,6 +31,7 @@ testsuite.dir # Binaries /src/sciteco /src/sciteco-minimal +/src/sciteco-wrapper # Generated source files /src/interface-gtk/gtk-info-popup.[ch] diff --git a/bootstrap.am b/bootstrap.am index 7ff35d3..bc6f721 100644 --- a/bootstrap.am +++ b/bootstrap.am @@ -25,6 +25,7 @@ SUBST_MACRO = eb$<\e \ <fs@PACKAGE_URL^Q@\e@PACKAGE_URL@\e;>j \ <fs@PACKAGE_URL_DEV^Q@\e@PACKAGE_URL_DEV@\e;>j \ <fs@bindir^Q@\e$(bindir)\e;>j \ + <fs@libexecdir^Q@\e$(libexecdir)\e;>j \ <fs@pkgdatadir^Q@\e$(pkgdatadir)\e;>j \ <fs@scitecolibdir^Q@\e$(scitecolibdir)\e;>j \ <fs@TECO_INTEGER^Q@\e@TECO_INTEGER@\e;>j \ diff --git a/doc/grosciteco.tes b/doc/grosciteco.tes index d84c6ab..7be01fb 100755 --- a/doc/grosciteco.tes +++ b/doc/grosciteco.tes @@ -1,4 +1,4 @@ -#!/usr/bin/env sciteco -m +#!/usr/local/bin/sciteco -m !* grosciteco.tes <output-woman> <output-tec> <input> *! !* Process command-line options *! diff --git a/doc/htbl.tes b/doc/htbl.tes index 4118e55..fced7e7 100755 --- a/doc/htbl.tes +++ b/doc/htbl.tes @@ -1,4 +1,4 @@ -#!/usr/bin/env sciteco -m +#!/usr/local/bin/sciteco -m !* htbl.tes <input> <output> *! !* Troff tbl "drop-in" replacement *! diff --git a/doc/sciteco.1.in b/doc/sciteco.1.in index e6c4d8a..ed3892f 100644 --- a/doc/sciteco.1.in +++ b/doc/sciteco.1.in @@ -17,10 +17,11 @@ Scintilla-based \fBT\fPext \fBE\fPditor and \fBCO\fPrrector .SY @PACKAGE@ .OP "-h|--help" .OP "-e|--eval" macro -.OP "-m|--mung" file +.OP "-m|--mung" .OP "--no-profile" .RI [ "UI option .\|.\|." ] .OP "--" +.RI [ script ] .RI [ "argument .\|.\|." ] .YS . @@ -40,26 +41,46 @@ natively supports Microsoft Windows NT\*(Tm. .LP .SCITECO_TOPIC mung When executed, \*(ST mungs (executes) the TECO macro stored in the file -specified by the +specified in the \fIscript\fP argument if .B "--mung" -option or the macro specified via +is given or the macro specified via .B "--eval" respectively. Munged files and macros are executed in non-interactive (\fIbatch\fP) mode, allowing the user to write stand-alone TECO scripts. Only when munging files as opposed to other means of executing macros, -the first line is ignored if it begins with \(lq#!\(rq. +the first line is ignored if it begins with a \(lq#\(rq (hash sign). Therefore under UNIX-like operating systems, TECO macro files may be invoked as scripts by using a Hash-Bang line like .\" FIXME: We'd like to include #! as a topic, but ! character are currently .\" not allowed since they are not escaped correctly. .SCITECO_TOPIC scripting .RS -.SCITECO_TT .EX +.SCITECO_TT #!@bindir@/sciteco -m +.SCITECO_TT_END .EE +.RE +. +.LP +Note that UNIX Hash-Bang lines will only pass a \fBsingle\fP argument to the +interpreter before the script's file name, so all required \*(ST options must +be mangled into a single argument with their single-letter names. +Passing option-like arguments (beginning with a dash) to scripts may cause +problems because \*(ST might try to interpret these options. +Beginning with Glib 2.44, \*(ST thus stops parsing at the first non-option +argument (which will always be the munged file name in a script invocation). +For binaries linked against older versions of Glib, \*(ST works around this +issue by providing a wrapper script that can be used in place of the main +executable. +A portable Hash-Bang line should thus look like: +.RS +.EX +.SCITECO_TT +#!@libexecdir@/sciteco-wrapper -m .SCITECO_TT_END +.EE .RE . .LP @@ -69,6 +90,8 @@ All command line arguments after the \*(ST options are passed as .I arguments to the munged macro by placing each argument on its own line in the buffer. +The \fIscript\fP file name expected when \(lq--mung\(rq is given +is currently \fBnot\fP considered a macro argument. In any case the current buffer position (called .IR dot ) is left at the beginning of the buffer. @@ -137,6 +160,8 @@ option is absent, \*(ST will mung On UNIX/Linux, the default profile is at .I ~/.teco_ini (see \fBENVIRONMENT\fP). +It will consequently not expect a \fIscript\fP file name as +the first non-option argument. The profile will usually set up various Scintilla and \*(ST options, configure syntax highlighting, define commonly used macros and open files specified as arguments to \*(ST. @@ -160,9 +185,12 @@ Similar to munging but always exits afterwards. If the option is specified, the .B \-\-mung option has no effect. -.IP "\fB-m\fR, \fB--mung\fR \fIfile" +.IP "\fB-m\fR, \fB--mung\fR" .SCITECO_TOPIC "-m" "--mung" -Mung \fIfile\fP. +Mung \fIscript\fP. +The script file name is expected as the first non-option +argument, so it does not necessarily have to follow the +\fB--mung\fP option. Default is .IR $SCITECOCONFIG/.teco_ini . .IP "\fB--no-profile\fP" diff --git a/src/Makefile.am b/src/Makefile.am index 314faa4..72e9157 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -76,10 +76,27 @@ CLEANFILES = $(BUILT_SOURCES) \ symbols-scintilla.cpp : @SCINTILLA_PATH@/include/Scintilla.h \ symbols-extract.tes - $(SCITECO_MINIMAL) -m @srcdir@/symbols-extract.tes \ + $(SCITECO_MINIMAL) -m -- @srcdir@/symbols-extract.tes \ $< $@ "SCI_" scintilla symbols-scilexer.cpp : @SCINTILLA_PATH@/include/SciLexer.h \ symbols-extract.tes - $(SCITECO_MINIMAL) -m @srcdir@/symbols-extract.tes \ + $(SCITECO_MINIMAL) -m -- @srcdir@/symbols-extract.tes \ $< $@ "SCLEX_,SCE_" scilexer + +# This installs a wrapper script to libexecdir to be used as +# the SciTECO interpreter in Hash-Bang lines. +# It makes sure that option parsing is disabled for all +# script arguments which is necessary for builds against Glib < 2.44. +# NOTE: When we raise the Glib requirement to 2.44, the sciteco-wrapper +# workaround can be removed completely. +libexec_SCRIPTS = sciteco-wrapper +CLEANFILES += $(libexec_SCRIPTS) + +.PHONY: sciteco-wrapper +sciteco-wrapper: + echo '#!/bin/sh' >$@ + echo 'OPT=$$1;' \ + 'shift;' \ + "exec $(bindir)/`echo sciteco | @SED@ '$(transform)'`$(EXEEXT)" \ + '"$$OPT" -- $$@' >>$@ diff --git a/src/interface-curses/interface-curses.cpp b/src/interface-curses/interface-curses.cpp index d592b89..5e5b8d8 100644 --- a/src/interface-curses/interface-curses.cpp +++ b/src/interface-curses/interface-curses.cpp @@ -312,7 +312,7 @@ InterfaceCurses::InterfaceCurses() : stdout_orig(-1), stderr_orig(-1), } void -InterfaceCurses::main_impl(int &argc, char **&argv) +InterfaceCurses::init(void) { /* * We must register this handler to handle @@ -518,12 +518,16 @@ InterfaceCurses::init_screen(void) * This sets the program name to "SciTECO" * which may then also be used as the X11 class name * for overwriting X11 resources in .Xdefaults + * * FIXME: We could support passing in resource * overrides via the SciTECO command line. * But unfortunately, Xinitscr() is called too - * late to modify argc/argv for command-line parsing. + * late to modify argc/argv for command-line parsing + * (and GOption needs to know about the additional + * possible arguments since they are not passed through + * transparently). * Therefore this could only be supported by - * adding a special option like --resource. + * adding a special option like --resource KEY=VAL. */ Xinitscr(1, (char **)argv); } diff --git a/src/interface-curses/interface-curses.h b/src/interface-curses/interface-curses.h index d036d37..a0198cc 100644 --- a/src/interface-curses/interface-curses.h +++ b/src/interface-curses/interface-curses.h @@ -121,8 +121,8 @@ public: InterfaceCurses(); ~InterfaceCurses(); - /* implementation of Interface::main() */ - void main_impl(int &argc, char **&argv); + /* override of Interface::init() */ + void init(void); /* override of Interface::init_color() */ void init_color(guint color, guint32 rgb); diff --git a/src/interface-gtk/interface-gtk.cpp b/src/interface-gtk/interface-gtk.cpp index 4ef0b38..920f01b 100644 --- a/src/interface-gtk/interface-gtk.cpp +++ b/src/interface-gtk/interface-gtk.cpp @@ -183,7 +183,12 @@ InterfaceGtk::get_options(void) {NULL} }; - GOptionGroup *group = gtk_get_option_group(TRUE); + /* + * Parsing the option context with the Gtk option group + * will automatically initialize Gtk, so gtk_init() + * does not have to be called. + */ + GOptionGroup *group = gtk_get_option_group(FALSE); g_option_group_add_entries(group, entries); @@ -191,7 +196,7 @@ InterfaceGtk::get_options(void) } void -InterfaceGtk::main_impl(int &argc, char **&argv) +InterfaceGtk::init(void) { static const Cmdline empty_cmdline; @@ -208,7 +213,6 @@ InterfaceGtk::main_impl(int &argc, char **&argv) g_thread_init(NULL); #endif gdk_threads_init(); - gtk_init(&argc, &argv); /* * Register clipboard registers. diff --git a/src/interface-gtk/interface-gtk.h b/src/interface-gtk/interface-gtk.h index 0145ff4..a19d253 100644 --- a/src/interface-gtk/interface-gtk.h +++ b/src/interface-gtk/interface-gtk.h @@ -113,8 +113,8 @@ public: /* overrides Interface::get_options() */ GOptionGroup *get_options(void); - /* implementation of Interface::main() */ - void main_impl(int &argc, char **&argv); + /* override of Interface::init() */ + void init(void); /* implementation of Interface::vmsg() */ void vmsg_impl(MessageType type, const gchar *fmt, va_list ap); diff --git a/src/interface.h b/src/interface.h index 98254ac..e841680 100644 --- a/src/interface.h +++ b/src/interface.h @@ -194,11 +194,8 @@ public: return NULL; } - inline void - main(int &argc, char **&argv) - { - impl().main_impl(argc, argv); - } + /* default implementation */ + inline void init(void) {} /* makes sense only on Curses */ inline void init_color(guint color, guint32 rgb) {} diff --git a/src/main.cpp b/src/main.cpp index d14e2e5..e9be929 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -75,7 +75,7 @@ namespace Flags { } static gchar *eval_macro = NULL; -static gchar *mung_file = NULL; +static gboolean mung_file = FALSE; static gboolean mung_profile = TRUE; sig_atomic_t sigint_occurred = FALSE; @@ -165,29 +165,31 @@ get_default_config_path(const gchar *program) #endif -static inline void +static inline gchar * 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"}, + {"mung", 'm', 0, G_OPTION_ARG_NONE, &mung_file, + "Mung script file (first non-option argument) instead of " + "$SCITECOCONFIG" G_DIR_SEPARATOR_S INI_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"}, + "$SCITECOCONFIG" G_DIR_SEPARATOR_S INI_FILE " " + "even if it exists"}, {NULL} }; + gchar *mung_filename = NULL; + GError *gerror = NULL; GOptionContext *options; GOptionGroup *interface_group = interface.get_options(); - options = g_option_context_new("[--] [ARGUMENT...]"); + options = g_option_context_new("[--] [SCRIPT] [ARGUMENT...]"); g_option_context_set_summary( options, @@ -203,6 +205,21 @@ process_options(int &argc, char **&argv) if (interface_group) g_option_context_add_group(options, interface_group); +#if GLIB_CHECK_VERSION(2,44,0) + /* + * If possible we parse in POSIX mode, which means that + * the first non-option argument terminates option parsing. + * SciTECO considers all non-option arguments to be script + * arguments and it makes little sense to mix script arguments + * with SciTECO options, so this lets the user avoid "--" + * in many situations. + * It is also strictly required to make hash-bang lines like + * #!/usr/bin/sciteco -m + * work (see sciteco(1)). + */ + g_option_context_set_strict_posix(options, TRUE); +#endif + if (!g_option_context_parse(options, &argc, &argv, &gerror)) { g_fprintf(stderr, "Option parsing failed: %s\n", gerror->message); @@ -212,15 +229,41 @@ process_options(int &argc, char **&argv) g_option_context_free(options); + /* + * GOption will NOT remove "--" if followed by an + * option-argument, which may interfer with scripts + * doing their own option handling and interpreting "--". + * + * NOTE: This is still true if we're parsing in GNU-mode + * and "--" is not the first non-option argument as in + * sciteco foo -- -C bar. + */ + if (argc >= 2 && !strcmp(argv[1], "--")) { + argv[1] = argv[0]; + argv++; + argc--; + } + if (mung_file) { - if (!g_file_test(mung_file, G_FILE_TEST_IS_REGULAR)) { + if (argc < 2) { + g_fprintf(stderr, "Script to mung expected!\n"); + exit(EXIT_FAILURE); + } + + if (!g_file_test(argv[1], G_FILE_TEST_IS_REGULAR)) { g_fprintf(stderr, "Cannot mung \"%s\". File does not exist!\n", - mung_file); + argv[1]); exit(EXIT_FAILURE); } + + mung_filename = g_strdup(argv[1]); + + argv[1] = argv[0]; + argv++; + argc--; } - /* remaining arguments, are arguments to the interface */ + return mung_filename; } static inline void @@ -369,6 +412,8 @@ main(int argc, char **argv) realloc /* try_realloc */ }; + gchar *mung_filename; + #ifdef DEBUG_PAUSE /* Windows debugging hack (see above) */ system("pause"); @@ -379,9 +424,12 @@ main(int argc, char **argv) g_mem_set_vtable(&vtable); - process_options(argc, argv); - interface.main(argc, argv); - /* remaining arguments are arguments to the munged file */ + mung_filename = process_options(argc, argv); + /* + * All remaining arguments in argv are arguments + * to the macro or munged file. + */ + interface.init(); /* * QRegister view must be initialized only now @@ -405,7 +453,7 @@ main(int argc, char **argv) ring.edit((const gchar *)NULL); /* add remaining arguments to unnamed buffer */ - for (int i = 1; i < argc; i++) { + for (gint i = 1; i < argc; i++) { /* * FIXME: arguments may contain line-feeds. * Once SciTECO is 8-byte clear, we can add the @@ -435,15 +483,15 @@ main(int argc, char **argv) exit(EXIT_SUCCESS); } - if (!mung_file && mung_profile) + if (!mung_filename && mung_profile) /* NOTE: Still safe to use g_getenv() */ - mung_file = g_build_filename(g_getenv("SCITECOCONFIG"), - INI_FILE, NIL); + mung_filename = g_build_filename(g_getenv("SCITECOCONFIG"), + INI_FILE, NIL); - if (mung_file && - g_file_test(mung_file, G_FILE_TEST_IS_REGULAR)) { + if (mung_filename && + g_file_test(mung_filename, G_FILE_TEST_IS_REGULAR)) { try { - Execute::file(mung_file, false); + Execute::file(mung_filename, false); } catch (Quit) { /* * ^C invoked, quit hook should still @@ -456,7 +504,6 @@ main(int argc, char **argv) exit(EXIT_SUCCESS); } } - g_free(mung_file); } catch (Error &error) { error.display_full(); exit(EXIT_FAILURE); @@ -495,5 +542,6 @@ main(int argc, char **argv) exit(EXIT_FAILURE); } + g_free(mung_filename); return 0; } diff --git a/src/symbols-extract.tes b/src/symbols-extract.tes index 2a6664c..e81331a 100755 --- a/src/symbols-extract.tes +++ b/src/symbols-extract.tes @@ -1,4 +1,4 @@ -#!./sciteco-minimal -m +#!/usr/local/bin/sciteco -m ! ./symbols-extract.tes <input file> <output file> <prefix pattern list> <array name> ! EMQ[$SCITECOPATH]/string.tes |