diff --git a/Makefile.am b/Makefile.am index 52961492..8d141efd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,7 +18,7 @@ distclean-local: clean-local: rm -rf $(builddir)/go.mod $(builddir)/go.sum - go clean -C $(srcdir) -x -modfile $(abs_builddir)/go.mod -cache + go clean -C $(srcdir) -x -modfile $(abs_srcdir)/go.mod -cache ###################################################################### if ENABLE_HAWKGO @@ -35,8 +35,8 @@ hawkgo.check: CC=$(CC) \ CGO_CFLAGS="-I$(abs_srcdir)/lib -I$(abs_builddir)/lib $(CFLAGS) $(CGO_CFLAGS_EXTRA) $(CGO_CFLAGS_ADD)" \ CGO_LDFLAGS="-L$(abs_builddir)/lib -L$(abs_builddir)/lib/.libs -lhawk -ldl $(LIBM) $(CGO_LDFLAGS_EXTRA) $(CGO_LDFLAGS_ADD)" \ - go test -C $(srcdir) -ldflags "-X 'main.BINDIR=$(bindir)' -X 'main.SBINDIR=$(sbindir)' -X 'main.LIBDIR=$(libdir)' -X 'main.SYSCONFDIR=$(sysconfdir)'" -x -modfile $(abs_builddir)/go.mod - go clean -C $(srcdir) -x -modfile $(abs_builddir)/go.mod + go test -C $(srcdir) -ldflags "-X 'main.BINDIR=$(bindir)' -X 'main.SBINDIR=$(sbindir)' -X 'main.LIBDIR=$(libdir)' -X 'main.SYSCONFDIR=$(sysconfdir)'" -x -modfile $(abs_srcdir)/go.mod + go clean -C $(srcdir) -x -modfile $(abs_srcdir)/go.mod endif ###################################################################### diff --git a/Makefile.in b/Makefile.in index 2ebabb97..a7a32a26 100644 --- a/Makefile.in +++ b/Makefile.in @@ -877,14 +877,14 @@ distclean-local: clean-local: rm -rf $(builddir)/go.mod $(builddir)/go.sum - go clean -C $(srcdir) -x -modfile $(abs_builddir)/go.mod -cache + go clean -C $(srcdir) -x -modfile $(abs_srcdir)/go.mod -cache @ENABLE_HAWKGO_TRUE@hawkgo.check: @ENABLE_HAWKGO_TRUE@ CC=$(CC) \ @ENABLE_HAWKGO_TRUE@ CGO_CFLAGS="-I$(abs_srcdir)/lib -I$(abs_builddir)/lib $(CFLAGS) $(CGO_CFLAGS_EXTRA) $(CGO_CFLAGS_ADD)" \ @ENABLE_HAWKGO_TRUE@ CGO_LDFLAGS="-L$(abs_builddir)/lib -L$(abs_builddir)/lib/.libs -lhawk -ldl $(LIBM) $(CGO_LDFLAGS_EXTRA) $(CGO_LDFLAGS_ADD)" \ -@ENABLE_HAWKGO_TRUE@ go test -C $(srcdir) -ldflags "-X 'main.BINDIR=$(bindir)' -X 'main.SBINDIR=$(sbindir)' -X 'main.LIBDIR=$(libdir)' -X 'main.SYSCONFDIR=$(sysconfdir)'" -x -modfile $(abs_builddir)/go.mod -@ENABLE_HAWKGO_TRUE@ go clean -C $(srcdir) -x -modfile $(abs_builddir)/go.mod +@ENABLE_HAWKGO_TRUE@ go test -C $(srcdir) -ldflags "-X 'main.BINDIR=$(bindir)' -X 'main.SBINDIR=$(sbindir)' -X 'main.LIBDIR=$(libdir)' -X 'main.SYSCONFDIR=$(sysconfdir)'" -x -modfile $(abs_srcdir)/go.mod +@ENABLE_HAWKGO_TRUE@ go clean -C $(srcdir) -x -modfile $(abs_srcdir)/go.mod ###################################################################### diff --git a/bin/Makefile.am b/bin/Makefile.am index 25bc36cb..f2c84a66 100644 --- a/bin/Makefile.am +++ b/bin/Makefile.am @@ -38,8 +38,6 @@ hawk_LDFLAGS = $(LDFLAGS_COMMON) ## hawk_LDADD = $(LIBADD_COMMON) $(LIBM) - - if ENABLE_HAWKGO ## the attempt to compose a proper procedure using a regular compiler failed. @@ -52,9 +50,9 @@ if ENABLE_HAWKGO bin_PROGRAMS += hawkgo hawkgo_SOURCES = \ - ../go.mod \ ../hawk.go \ ../hawk-inst.go \ + getopt.go \ hawkgo.go ##hawkgo_DEPENDENCIES = @@ -71,16 +69,16 @@ CGO_LDFLAGS_ADD="" endif hawkgo$(EXEEXT): ../lib/libhawk.la $(hawkgo_OBJECTS) - cp -pf $(top_srcdir)/go.mod $(builddir)/go.mod >/dev/null 2>&1 || true - chmod u+w $(builddir)/go.mod ## with `make distcheck`, the echo's redirection to the file fails without this permission change + ##cp -pf $(top_srcdir)/go.mod $(builddir)/go.mod >/dev/null 2>&1 || true + ##chmod u+w $(builddir)/go.mod ## with `make distcheck`, the echo's redirection to the file fails without this permission change [ -f $(srcdir)/go.sum ] && cp -pf $(srcdir)/go.sum $(builddir)/go.sum >/dev/null 2>&1 || true ## --------------------------------------------------------------- CC=$(CC) \ CGO_CFLAGS="-I$(abs_top_srcdir)/lib -I$(abs_top_builddir)/lib $(CFLAGS) $(CGO_CFLAGS_EXTRA) $(CGO_CFLAGS_ADD)" \ CGO_LDFLAGS="-L$(abs_top_builddir)/lib -L$(abs_top_builddir)/lib/.libs -lhawk -ldl $(LIBM) $(CGO_LDFLAGS_EXTRA) $(CGO_LDFLAGS_ADD)" \ - go build -C $(srcdir) -ldflags "-X 'main.BINDIR=$(bindir)' -X 'main.SBINDIR=$(sbindir)' -X 'main.LIBDIR=$(libdir)' -X 'main.SYSCONFDIR=$(sysconfdir)'" -x -o $(abs_builddir)/$@ -modfile $(abs_builddir)/go.mod $(abs_srcdir)/hawkgo.go + go build -C $(srcdir) -ldflags "-X 'main.BINDIR=$(bindir)' -X 'main.SBINDIR=$(sbindir)' -X 'main.LIBDIR=$(libdir)' -X 'main.SYSCONFDIR=$(sysconfdir)'" -x -o $(abs_builddir)/$@ -modfile $(abs_top_srcdir)/go.mod $(abs_srcdir)/hawkgo.go $(abs_srcdir)/getopt.go ## --------------------------------------------------------------- - go clean -C $(srcdir) -x -modfile $(abs_builddir)/go.mod + go clean -C $(srcdir) -x -modfile $(abs_top_srcdir)/go.mod ## the go to o recipe is fake to deceive make .go.o: diff --git a/bin/Makefile.in b/bin/Makefile.in index 91eba7d2..8259f091 100644 --- a/bin/Makefile.in +++ b/bin/Makefile.in @@ -125,11 +125,11 @@ am__v_lt_1 = hawk_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(hawk_CFLAGS) $(CFLAGS) \ $(hawk_LDFLAGS) $(LDFLAGS) -o $@ -am__hawkgo_SOURCES_DIST = ../go.mod ../hawk.go ../hawk-inst.go \ +am__hawkgo_SOURCES_DIST = ../hawk.go ../hawk-inst.go getopt.go \ hawkgo.go am__dirstamp = $(am__leading_dot)dirstamp -@ENABLE_HAWKGO_TRUE@am_hawkgo_OBJECTS = ../go.$(OBJEXT) \ -@ENABLE_HAWKGO_TRUE@ ../hawk.$(OBJEXT) ../hawk-inst.$(OBJEXT) \ +@ENABLE_HAWKGO_TRUE@am_hawkgo_OBJECTS = ../hawk.$(OBJEXT) \ +@ENABLE_HAWKGO_TRUE@ ../hawk-inst.$(OBJEXT) getopt.$(OBJEXT) \ @ENABLE_HAWKGO_TRUE@ hawkgo.$(OBJEXT) hawkgo_OBJECTS = $(am_hawkgo_OBJECTS) hawkgo_LDADD = $(LDADD) @@ -372,9 +372,9 @@ hawk_CFLAGS = $(CFLAGS_COMMON) hawk_LDFLAGS = $(LDFLAGS_COMMON) hawk_LDADD = $(LIBADD_COMMON) $(LIBM) @ENABLE_HAWKGO_TRUE@hawkgo_SOURCES = \ -@ENABLE_HAWKGO_TRUE@ ../go.mod \ @ENABLE_HAWKGO_TRUE@ ../hawk.go \ @ENABLE_HAWKGO_TRUE@ ../hawk-inst.go \ +@ENABLE_HAWKGO_TRUE@ getopt.go \ @ENABLE_HAWKGO_TRUE@ hawkgo.go @ENABLE_HAWKGO_TRUE@hawkgo_LINK = @@ -469,7 +469,6 @@ hawk$(EXEEXT): $(hawk_OBJECTS) $(hawk_DEPENDENCIES) $(EXTRA_hawk_DEPENDENCIES) ../$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) ../$(DEPDIR) @: >>../$(DEPDIR)/$(am__dirstamp) -../go.$(OBJEXT): ../$(am__dirstamp) ../$(DEPDIR)/$(am__dirstamp) ../hawk.$(OBJEXT): ../$(am__dirstamp) ../$(DEPDIR)/$(am__dirstamp) ../hawk-inst.$(OBJEXT): ../$(am__dirstamp) \ ../$(DEPDIR)/$(am__dirstamp) @@ -802,14 +801,12 @@ uninstall-am: uninstall-binPROGRAMS @ENABLE_HAWKGO_TRUE@hawkgo$(EXEEXT): ../lib/libhawk.la $(hawkgo_OBJECTS) -@ENABLE_HAWKGO_TRUE@ cp -pf $(top_srcdir)/go.mod $(builddir)/go.mod >/dev/null 2>&1 || true -@ENABLE_HAWKGO_TRUE@ chmod u+w $(builddir)/go.mod ## with `make distcheck`, the echo's redirection to the file fails without this permission change @ENABLE_HAWKGO_TRUE@ [ -f $(srcdir)/go.sum ] && cp -pf $(srcdir)/go.sum $(builddir)/go.sum >/dev/null 2>&1 || true @ENABLE_HAWKGO_TRUE@ CC=$(CC) \ @ENABLE_HAWKGO_TRUE@ CGO_CFLAGS="-I$(abs_top_srcdir)/lib -I$(abs_top_builddir)/lib $(CFLAGS) $(CGO_CFLAGS_EXTRA) $(CGO_CFLAGS_ADD)" \ @ENABLE_HAWKGO_TRUE@ CGO_LDFLAGS="-L$(abs_top_builddir)/lib -L$(abs_top_builddir)/lib/.libs -lhawk -ldl $(LIBM) $(CGO_LDFLAGS_EXTRA) $(CGO_LDFLAGS_ADD)" \ -@ENABLE_HAWKGO_TRUE@ go build -C $(srcdir) -ldflags "-X 'main.BINDIR=$(bindir)' -X 'main.SBINDIR=$(sbindir)' -X 'main.LIBDIR=$(libdir)' -X 'main.SYSCONFDIR=$(sysconfdir)'" -x -o $(abs_builddir)/$@ -modfile $(abs_builddir)/go.mod $(abs_srcdir)/hawkgo.go -@ENABLE_HAWKGO_TRUE@ go clean -C $(srcdir) -x -modfile $(abs_builddir)/go.mod +@ENABLE_HAWKGO_TRUE@ go build -C $(srcdir) -ldflags "-X 'main.BINDIR=$(bindir)' -X 'main.SBINDIR=$(sbindir)' -X 'main.LIBDIR=$(libdir)' -X 'main.SYSCONFDIR=$(sysconfdir)'" -x -o $(abs_builddir)/$@ -modfile $(abs_top_srcdir)/go.mod $(abs_srcdir)/hawkgo.go $(abs_srcdir)/getopt.go +@ENABLE_HAWKGO_TRUE@ go clean -C $(srcdir) -x -modfile $(abs_top_srcdir)/go.mod @ENABLE_HAWKGO_TRUE@.go.o: @ENABLE_HAWKGO_TRUE@ echo $< > $@ diff --git a/bin/getopt.go b/bin/getopt.go new file mode 100644 index 00000000..49dc775b --- /dev/null +++ b/bin/getopt.go @@ -0,0 +1,424 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package getopt parses command lines using getopt(3) syntax. +// It is a replacement for flag.Parse but still expects flags themselves +// to be defined in package flag. +// +// Flags defined with one-letter names are available as short flags +// (invoked using one dash, as in -x) and all flags are available as +// long flags (invoked using two dashes, as in --x or --xylophone). +// +// To use, define flags as usual with package flag. Then introduce +// any aliases by calling getopt.Alias: +// +// getopt.Alias("n", "dry-run") +// getopt.Alias("v", "verbose") +// +// Or call getopt.Aliases to define a list of aliases: +// +// getopt.Aliases( +// "n", "dry-run", +// "v", "verbose", +// ) +// +// One name in each pair must already be defined in package flag +// (so either "n" or "dry-run", and also either "v" or "verbose"). +// +// Then parse the command-line: +// +// getopt.Parse() +// +// If it encounters an error, Parse calls flag.Usage and then exits the program. +// +// When writing a custom flag.Usage function, call getopt.PrintDefaults +// instead of flag.PrintDefaults to get a usage message that includes the +// names of aliases in flag descriptions. +// +// At initialization time, this package installs a new flag.Usage that is the +// same as the default flag.Usage except that it calls getopt.PrintDefaults +// instead of flag.PrintDefaults. +// +// This package also defines a FlagSet wrapping the standard flag.FlagSet. +// +// Caveat +// +// In general Go flag parsing is preferred for new programs, because +// it is not as pedantic about the number of dashes used to invoke +// a flag (you can write -verbose or --verbose and the program +// does not care). This package is meant to be used in situations +// where, for legacy reasons, it is important to use exactly getopt(3) +// syntax, such as when rewriting in Go an existing tool that already +// uses getopt(3). +package main // import "rsc.io/getopt" + +import ( + "flag" + "fmt" + "io" + "os" + "reflect" + "strings" + "unicode/utf8" +) + +func init() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + PrintDefaults() // ours not package flag's + } + + CommandLine.FlagSet = flag.CommandLine + CommandLine.name = os.Args[0] + CommandLine.errorHandling = flag.ExitOnError + CommandLine.outw = os.Stderr + CommandLine.Usage = func() { flag.Usage() } +} + +var CommandLine FlagSet + +// A FlagSet is a set of defined flags. +// It wraps and provides the same interface as flag.FlagSet +// but parses command line arguments using getopt syntax. +// +// Note that "go doc" shows only the methods customized +// by package getopt; FlagSet also provides all the methods +// of the embedded flag.FlagSet, like Bool, Int, NArg, and so on. +type FlagSet struct { + *flag.FlagSet + + alias map[string]string + unalias map[string]string + name string + errorHandling flag.ErrorHandling + outw io.Writer +} + +func (f *FlagSet) out() io.Writer { + if f.outw == nil { + return os.Stderr + } + return f.outw +} + +// SetOutput sets the destination for usage and error messages. +// If output is nil, os.Stderr is used. +func (f *FlagSet) SetOutput(output io.Writer) { + f.FlagSet.SetOutput(output) + f.outw = output +} + +// NewFlagSet returns a new, empty flag set with the specified name and error +// handling property. +func NewFlagSet(name string, errorHandling flag.ErrorHandling) *FlagSet { + f := new(FlagSet) + f.Init(name, errorHandling) + return f +} + +// Init sets the name and error handling proprety for a flag set. +func (f *FlagSet) Init(name string, errorHandling flag.ErrorHandling) { + if f.FlagSet == nil { + f.FlagSet = new(flag.FlagSet) + } + f.FlagSet.Init(name, errorHandling) + f.name = name + f.errorHandling = errorHandling + f.FlagSet.Usage = f.defaultUsage +} + +func (f *FlagSet) init() { + if f.alias == nil { + f.alias = make(map[string]string) + f.unalias = make(map[string]string) + } +} + +// Lookup returns the Flag structure of the named flag, +// returning nil if none exists. +// If name is a defined alias for a defined flag, +// Lookup returns the original flag; in this case +// the Name field in the result will differ from the +// name passed to Lookup. +func (f *FlagSet) Lookup(name string) *flag.Flag { + if x, ok := f.alias[name]; ok { + name = x + } + return f.FlagSet.Lookup(name) +} + +// Alias introduces an alias for an existing flag name. +// The short name must be a single letter, and the long name must be multiple letters. +// Exactly one name must be defined as a flag already: the undefined name is introduced +// as an alias for the defined name. +// Alias panics if both names are already defined or if both are undefined. +// +// For example, if a flag named "v" is already defined using package flag, +// then it is available as -v (or --v). Calling Alias("v", "verbose") makes the same +// flag also available as --verbose. +func Alias(short, long string) { + CommandLine.Alias(short, long) +} + +// Alias introduces an alias for an existing flag name. +// The short name must be a single letter, and the long name must be multiple letters. +// Exactly one name must be defined as a flag already: the undefined name is introduced +// as an alias for the defined name. +// Alias panics if both names are already defined or if both are undefined. +// +// For example, if a flag named "v" is already defined using package flag, +// then it is available as -v (or --v). Calling Alias("v", "verbose") makes the same +// flag also available as --verbose. +func (f *FlagSet) Alias(short, long string) { + f.init() + if short == "" || long == "" { + panic("Alias: invalid empty flag name") + } + if utf8.RuneCountInString(short) != 1 { + panic("Alias: invalid short flag name -" + short) + } + if utf8.RuneCountInString(long) == 1 { + panic("Alias: invalid long flag name --" + long) + } + + f1 := f.Lookup(short) + f2 := f.Lookup(long) + if f1 == nil && f2 == nil { + panic("Alias: neither -" + short + " nor -" + long + " is a defined flag") + } + if f1 != nil && f2 != nil { + panic("Alias: both -" + short + " and -" + long + " are defined flags") + } + + if f1 != nil { + f.alias[long] = short + f.unalias[short] = long + } else { + f.alias[short] = long + f.unalias[long] = short + } +} + +// Aliases introduces zero or more aliases. The argument list must consist of an +// even number of strings making up a sequence of short, long pairs to be passed +// to Alias. +func Aliases(list ...string) { + CommandLine.Aliases(list...) +} + +// Aliases introduces zero or more aliases. The argument list must consist of an +// even number of strings making up a sequence of short, long pairs to be passed +// to Alias. +func (f *FlagSet) Aliases(list ...string) { + if len(list)%2 != 0 { + panic("getopt: Aliases not invoked with pairs") + } + for i := 0; i < len(list); i += 2 { + f.Alias(list[i], list[i+1]) + } +} + +type boolFlag interface { + IsBoolFlag() bool +} + +func (f *FlagSet) failf(format string, args ...interface{}) error { + err := fmt.Errorf(format, args...) + fmt.Fprintln(f.out(), err) + f.Usage() + return err +} + +// defaultUsage is the default function to print a usage message. +func (f *FlagSet) defaultUsage() { + if f.name == "" { + fmt.Fprintf(f.out(), "Usage:\n") + } else { + fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) + } + f.PrintDefaults() +} + +// Parse parses the command-line flags from os.Args[1:]. +func Parse() { + CommandLine.Parse(os.Args[1:]) +} + +// Parse parses flag definitions from the argument list, +// which should not include the command name. +// Parse must be called after all flags and aliases in the FlagSet are defined +// and before flags are accessed by the program. +// The return value will be flag.ErrHelp if -h or --help were used but not defined. +func (f *FlagSet) Parse(args []string) error { + for len(args) > 0 { + arg := args[0] + if len(arg) < 2 || arg[0] != '-' { + break + } + args = args[1:] + if arg[:2] == "--" { + // Process single long option. + if arg == "--" { + break + } + name := arg[2:] + value := "" + haveValue := false + if i := strings.Index(name, "="); i >= 0 { + name, value = name[:i], name[i+1:] + haveValue = true + } + fg := f.Lookup(name) + if fg == nil { + if name == "h" || name == "help" { + // TODO ErrHelp + } + return f.failf("flag provided but not defined: --%s", name) + } + if b, ok := fg.Value.(boolFlag); ok && b.IsBoolFlag() { + if haveValue { + if err := fg.Value.Set(value); err != nil { + return f.failf("invalid boolean value %q for --%s: %v", value, name, err) + } + } else { + if err := fg.Value.Set("true"); err != nil { + return f.failf("invalid boolean flag %s: %v", name, err) + } + } + continue + } + if !haveValue { + if len(args) == 0 { + return f.failf("missing argument for --%s", name) + } + value, args = args[0], args[1:] + } + if err := fg.Value.Set(value); err != nil { + return f.failf("invalid value %q for flag --%s: %v", value, name, err) + } + continue + } + + // Process one or more short options. + for arg = arg[1:]; arg != ""; { + r, size := utf8.DecodeRuneInString(arg) + if r == utf8.RuneError && size == 1 { + return f.failf("invalid UTF8 in command-line flags") + } + name := arg[:size] + arg = arg[size:] + fg := f.Lookup(name) + if fg == nil { + if name == "h" { + // TODO ErrHelp + } + return f.failf("flag provided but not defined: -%s", name) + } + if b, ok := fg.Value.(boolFlag); ok && b.IsBoolFlag() { + if err := fg.Value.Set("true"); err != nil { + return f.failf("invalid boolean flag %s: %v", name, err) + } + continue + } + if arg == "" { + if len(args) == 0 { + return f.failf("missing argument for -%s", name) + } + arg, args = args[0], args[1:] + } + if err := fg.Value.Set(arg); err != nil { + return f.failf("invalid value %q for flag -%s: %v", arg, name, err) + } + break // consumed arg + } + } + + // Arrange for flag.NArg, flag.Args, etc to work properly. + f.FlagSet.Parse(append([]string{"--"}, args...)) + return nil +} + +// PrintDefaults is like flag.PrintDefaults but includes information +// about short/long alias pairs and prints the correct syntax for +// long flags. +func PrintDefaults() { + CommandLine.PrintDefaults() +} + +// PrintDefaults is like flag.PrintDefaults but includes information +// about short/long alias pairs and prints the correct syntax for +// long flags. +func (f *FlagSet) PrintDefaults() { + f.FlagSet.VisitAll(func(fg *flag.Flag) { + name := fg.Name + short, long := "", "" + other := f.unalias[name] + if utf8.RuneCountInString(name) > 1 { + long, short = name, other + } else { + short, long = name, other + } + var s string + if short != "" { + s = fmt.Sprintf(" -%s", short) // Two spaces before -; see next two comments. + if long != "" { + s += ", --" + long + } + } else { + s = fmt.Sprintf(" --%s", long) // Two spaces before -; see next two comments. + } + name, usage := flag.UnquoteUsage(fg) + if len(name) > 0 { + s += " " + name + } + + // Boolean flags of one ASCII letter are so common we + // treat them specially, putting their usage on the same line. + if len(s) <= 4 { // space, space, '-', 'x'. + s += "\t" + } else { + // Four spaces before the tab triggers good alignment + // for both 4- and 8-space tab stops. + s += "\n \t" + } + s += usage + if !isZeroValue(fg, fg.DefValue) { + if strings.HasSuffix(reflect.TypeOf(fg.Value).String(), "stringValue") { + // put quotes on the value + s += fmt.Sprintf(" (default %q)", fg.DefValue) + } else { + s += fmt.Sprintf(" (default %v)", fg.DefValue) + } + } + fmt.Fprint(f.out(), s, "\n") + }) +} + +// isZeroValue guesses whether the string represents the zero +// value for a flag. It is not accurate but in practice works OK. +func isZeroValue(f *flag.Flag, value string) bool { + // Build a zero value of the flag's Value type, and see if the + // result of calling its String method equals the value passed in. + // This works unless the Value type is itself an interface type. + typ := reflect.TypeOf(f.Value) + var z reflect.Value + if typ.Kind() == reflect.Ptr { + z = reflect.New(typ.Elem()) + } else { + z = reflect.Zero(typ) + } + if value == z.Interface().(flag.Value).String() { + return true + } + + switch value { + case "false": + return true + case "": + return true + case "0": + return true + } + return false +} diff --git a/bin/hawkgo.go b/bin/hawkgo.go index d68332a7..d9488f18 100644 --- a/bin/hawkgo.go +++ b/bin/hawkgo.go @@ -5,31 +5,34 @@ import "flag" import "fmt" import "io" import "os" +import "path/filepath" import "runtime" import "runtime/debug" -//import "sync" -import "time" +//import "time" type Config struct { assign string call string fs string - srcstr string - srcfile string + show_extra_info bool - files []string + srcstr string + srcfiles []string + datfiles []string } func exit_with_error(msg string, err error) { - fmt.Printf("ERROR: %s - %s\n", msg, err.Error()) + fmt.Fprintf(os.Stderr, "ERROR: %s - %s\n", msg, err.Error()) os.Exit(1) } func parse_args_to_config(cfg *Config) bool { - var flgs *flag.FlagSet + //var flgs *flag.FlagSet + var flgs *FlagSet var err error - flgs = flag.NewFlagSet("", flag.ContinueOnError) + //flgs = flag.NewFlagSet("", flag.ContinueOnError) + flgs = NewFlagSet("", flag.ContinueOnError) flgs.Func("assign", "set a global variable with a value", func(v string) error { cfg.assign = v return nil @@ -42,16 +45,33 @@ func parse_args_to_config(cfg *Config) bool { cfg.fs = v return nil }) + flgs.Func("file", "set the source file", func(v string) error { + cfg.srcfiles = append(cfg.srcfiles, v) + return nil + }) + flgs.BoolVar(&cfg.show_extra_info, "show-extra-info", false, "show extra information") + + flgs.Alias("c", "call") + flgs.Alias("D", "show-extra-info") + flgs.Alias("F", "field-separator") + flgs.Alias("f", "file") + flgs.Alias("v", "assign") flgs.SetOutput(io.Discard) // prevent usage output + //err = flgs.Parse(os.Args[1:]) err = flgs.Parse(os.Args[1:]) if err != nil { fmt.Fprintf(os.Stderr, "ERROR: %s\n", err.Error()) goto wrong_usage } - if flgs.NArg() == 0 { goto wrong_usage} - cfg.files = flgs.Args() + //if flgs.NArg() == 0 { goto wrong_usage} + cfg.datfiles = flgs.Args() + if len(cfg.srcfiles) <= 0 { + if len(cfg.datfiles) == 0 { goto wrong_usage } + cfg.srcstr = cfg.datfiles[0] + cfg.datfiles = cfg.datfiles[1:] + } return true @@ -88,34 +108,46 @@ func main() { if parse_args_to_config(&cfg) == false { os.Exit(99) } fmt.Printf("config [%+v]\n", cfg) - h, err = make_hawk(`function main(s) { -print enbase64(s, "hello", 1.289); -print debase64(s); -return x -}`) + h, err = hawk.New() if err != nil { - fmt.Printf("ERROR: failed to make hawk - %s\n", err.Error()) + fmt.Fprintf(os.Stderr, "ERROR: failed to make hawk - %s\n", err.Error()) return } - rtx, err = h.NewRtx("test3") + if (len(cfg.srcfiles) > 0) { + err = h.ParseFiles(cfg.srcfiles) + } else { + err = h.ParseText(cfg.srcstr) + } if err != nil { - fmt.Printf("ERROR: failed to make rtx - %s\n", err.Error()) + fmt.Fprintf(os.Stderr, "ERROR: failed to make hawk - %s\n", err.Error()) + h.Close() + return + } + + rtx, err = h.NewRtx(filepath.Base(os.Args[0]), cfg.datfiles) + if err != nil { + fmt.Fprintf(os.Stderr, "ERROR: failed to make rtx - %s\n", err.Error()) } else { var v *hawk.Val - v, err = rtx.Call("main", hawk.Must(rtx.NewValFromStr("this is a test3 string"))) - if err != nil { - fmt.Printf("ERROR: failed to make rtx - %s\n", err.Error()) + if cfg.call != "" { + v, err = rtx.Call(cfg.call/*, cfg.datfiles*/) // TODO: pass arguments. } else { - fmt.Printf("V=>[%v]\n", v.String()) + //v, err = rtx.Loop() + v, err = rtx.Exec(cfg.datfiles) + } + if err != nil { + fmt.Fprintf(os.Stderr, "ERROR: failed to run rtx - %s\n", err.Error()) + } else if cfg.show_extra_info { + fmt.Printf("[RETURN] - [%v]\n", v.String()) + // TODO: print global variables and values } } h.Close() - fmt.Printf ("END OF TEST3\n") runtime.GC() runtime.Gosched() - time.Sleep(1000 * time.Millisecond) // give finalizer time to print +// time.Sleep(1000 * time.Millisecond) // give finalizer time to print } diff --git a/hawk.go b/hawk.go index 0aeb5005..7775435e 100644 --- a/hawk.go +++ b/hawk.go @@ -18,7 +18,6 @@ static void init_parsestd_for_text_in(hawk_parsestd_t* in, hawk_bch_t* ptr, hawk in[0].type = HAWK_PARSESTD_BCS; in[0].u.bcs.ptr = ptr; in[0].u.bcs.len = len; - in[1].type = HAWK_PARSESTD_NULL; } static void init_parsestd_for_file_in(hawk_parsestd_t* in, hawk_bch_t* path) @@ -26,7 +25,6 @@ static void init_parsestd_for_file_in(hawk_parsestd_t* in, hawk_bch_t* path) in[0].type = HAWK_PARSESTD_FILEB; in[0].u.fileb.path = path; in[0].u.fileb.cmgr = HAWK_NULL; - in[1].type = HAWK_PARSESTD_NULL; } static hawk_ooch_t* valtostr_out_cpldup(hawk_rtx_valtostr_out_t* out, hawk_oow_t* len) @@ -309,7 +307,10 @@ func (hawk *Hawk) SetLogMask(log_mask BitMask) { func (hawk *Hawk) AddGlobal(name string) error { var x C.int - x = C.hawk_addgblwithbcstr(hawk.c, C.CString(name)) + var cname *C.hawk_bch_t + cname = C.CString(name) + x = C.hawk_addgblwithbcstr(hawk.c, cname) + C.free(unsafe.Pointer(cname)) if x <= -1 { return hawk.make_errinfo() } return nil } @@ -346,29 +347,65 @@ func hawk_go_fnc_handler(rtx_xtn *C.rtx_xtn_t, name *C.hawk_bch_t, namelen C.haw func (hawk *Hawk) AddFunc(name string, min_args uint, max_args uint, spec string, fn Fnc) error { var fnc *C.hawk_fnc_t + var cname *C.hawk_bch_t + var cspec *C.hawk_bch_t - fnc = C.add_fnc_with_bcstr(hawk.c, C.CString(name), C.hawk_oow_t(min_args), C.hawk_oow_t(max_args), C.CString(spec)); + cname = C.CString(name) + cspec = C.CString(spec) + fnc = C.add_fnc_with_bcstr(hawk.c, cname, C.hawk_oow_t(min_args), C.hawk_oow_t(max_args), cspec); + C.free(unsafe.Pointer(cspec)) + C.free(unsafe.Pointer(cname)) if fnc == nil { return hawk.make_errinfo() } hawk.fnctab[name] = fn; return nil } -func (hawk *Hawk) ParseFile(text string) error { +func (hawk *Hawk) ParseFile(file string) error { var x C.int var in [2]C.hawk_parsestd_t - C.init_parsestd_for_file_in(&in[0], C.CString(text)) + var cfile *C.hawk_bch_t + + cfile = C.CString(file) + C.init_parsestd_for_file_in(&in[0], cfile) x = C.hawk_parsestd(hawk.c, &in[0], nil) + C.free(unsafe.Pointer(cfile)) if x <= -1 { return hawk.make_errinfo() } return nil } -func (hawk *Hawk) ParseText(text string) error { +func (hawk *Hawk) ParseFiles(files []string) error { + var x C.int + var in []C.hawk_parsestd_t + var idx int + var count int + var cfiles []*C.hawk_bch_t + + count = len(files) + in = make([]C.hawk_parsestd_t, count + 1) + for idx = 0; idx < count; idx++ { + cfiles[idx] = C.CString(files[idx]) + C.init_parsestd_for_file_in(&in[idx], cfiles[idx]) + } + in[idx]._type = C.HAWK_PARSESTD_NULL + x = C.hawk_parsestd(hawk.c, &in[0], nil) + for idx = 0; idx < count; idx++ { + C.free(unsafe.Pointer(cfiles[idx])) + } + if x <= -1 { return hawk.make_errinfo() } + return nil +} + +func (hawk *Hawk) ParseText(file string) error { var x C.int var in [2]C.hawk_parsestd_t + var cfile *C.hawk_bch_t - C.init_parsestd_for_text_in(&in[0], C.CString(text), C.hawk_oow_t(len(text))) + cfile = C.CString(file) + C.init_parsestd_for_text_in(&in[0], cfile, C.hawk_oow_t(len(file))) + in[1]._type = C.HAWK_PARSESTD_NULL x = C.hawk_parsestd(hawk.c, &in[0], nil) + C.free(unsafe.Pointer(cfile)) if x <= -1 { return hawk.make_errinfo() } return nil } @@ -440,12 +477,32 @@ func deregister_rtx_instance(rtx *Rtx) { } } -func (hawk *Hawk) NewRtx(id string) (*Rtx, error) { +func (hawk *Hawk) NewRtx(id string, in []string) (*Rtx, error) { var rtx *C.hawk_rtx_t var g *Rtx var xtn *C.rtx_xtn_t + var cid *C.hawk_bch_t + var cin []*C.hawk_bch_t + var idx int + var in_count int + + in_count = len(in) + cin = make([]*C.hawk_bch_t, in_count + 1) + for idx = 0; idx < in_count; idx++ { + cin[idx] = C.CString(in[idx]) + } + cin[idx] = (*C.hawk_bch_t)(nil) + cid = C.CString(id) + if in_count > 0 { + rtx = C.hawk_rtx_openstdwithbcstr(hawk.c, C.hawk_oow_t(unsafe.Sizeof(*xtn)), cid, &cin[0], nil, nil) + } else { + rtx = C.hawk_rtx_openstdwithbcstr(hawk.c, C.hawk_oow_t(unsafe.Sizeof(*xtn)), cid, nil, nil, nil) + } + for idx = 0; idx < in_count; idx++ { + C.free(unsafe.Pointer(cin[idx])) + } + C.free(unsafe.Pointer(cid)) - rtx = C.hawk_rtx_openstdwithbcstr(hawk.c, C.hawk_oow_t(unsafe.Sizeof(*xtn)), C.CString(id), nil, nil, nil) if rtx == nil { return nil, hawk.make_errinfo() } g = &Rtx{c: rtx} @@ -490,6 +547,38 @@ func (rtx *Rtx) make_errinfo() *Err { return &err } +func (rtx *Rtx) Exec(args []string) (*Val, error) { + var val *C.hawk_val_t + var cargs []*C.hawk_bch_t + var idx int + var count int + + count = len(args) + cargs = make([]*C.hawk_bch_t, count) + for idx = 0; idx < count; idx++ { + cargs[idx] = C.CString(args[idx]) + } + + if count > 0 { + val = C.hawk_rtx_execwithbcstrarr(rtx.c, &cargs[0], C.hawk_oow_t(count)) + } else { + val = C.hawk_rtx_execwithbcstrarr(rtx.c, (**C.hawk_bch_t)(nil), C.hawk_oow_t(count)) + } + + for idx = 0; idx < count; idx++ { + C.free(unsafe.Pointer(cargs[idx])) + } + if val == nil { return nil, rtx.make_errinfo() } + return &Val{rtx: rtx, c: val}, nil +} + +func (rtx *Rtx) Loop() (*Val, error) { + var val *C.hawk_val_t + val = C.hawk_rtx_loop(rtx.c) + if val == nil { return nil, rtx.make_errinfo() } + return &Val{rtx: rtx, c: val}, nil +} + func (rtx *Rtx) Call(name string, args ...*Val) (*Val, error) { var fun *C.hawk_fun_t var val *C.hawk_val_t @@ -675,7 +764,12 @@ func (rtx *Rtx) NewValFromFlt(v float64) (*Val, error) { func (rtx *Rtx) NewValFromStr(v string) (*Val, error) { return rtx.make_val(func() *C.hawk_val_t { - return C.hawk_rtx_makestrvalwithbchars(rtx.c, C.CString(v), C.hawk_oow_t(len(v))) + var vv *C.hawk_val_t + var cv *C.hawk_bch_t + cv = C.CString(v) + vv = C.hawk_rtx_makestrvalwithbchars(rtx.c, cv, C.hawk_oow_t(len(v))) + C.free(unsafe.Pointer(cv)) + return vv }) } diff --git a/hawk_test.go b/hawk_test.go index 73e76752..5e793bd9 100644 --- a/hawk_test.go +++ b/hawk_test.go @@ -79,7 +79,7 @@ func run_hawk(h *hawk.Hawk, id int, t *testing.T, wg *sync.WaitGroup) { defer wg.Done() - rtx, err = h.NewRtx(fmt.Sprintf("%d", id)) + rtx, err = h.NewRtx(fmt.Sprintf("%d", id), nil) if err != nil { t.Errorf("failed to create rtx id[%d] - %s", id, err.Error()) return @@ -181,7 +181,7 @@ return x; return } - rtx, err = h.NewRtx("test2") + rtx, err = h.NewRtx("test2", nil) if err != nil { t.Errorf("failed to create rtx - %s", err.Error()) } else { @@ -310,7 +310,7 @@ return x return } - rtx, err = h.NewRtx("test3") + rtx, err = h.NewRtx("test3", nil) if err != nil { t.Errorf("failed to create rtx - %s", err.Error()) } else {