<commit_before>package main

import (
	"encoding/json"
	"io/ioutil"
	"log/syslog"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"time"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
	"github.com/hanwen/go-fuse/fuse/pathfs"

	"github.com/rfjakob/gocryptfs/internal/configfile"
	"github.com/rfjakob/gocryptfs/internal/cryptocore"
	"github.com/rfjakob/gocryptfs/internal/ctlsock"
	"github.com/rfjakob/gocryptfs/internal/exitcodes"
	"github.com/rfjakob/gocryptfs/internal/fusefrontend"
	"github.com/rfjakob/gocryptfs/internal/fusefrontend_reverse"
	"github.com/rfjakob/gocryptfs/internal/readpassword"
	"github.com/rfjakob/gocryptfs/internal/tlog"
)

// doMount mounts an encrypted directory.
// Called from main.
func doMount(args *argContainer) int {
	// Check mountpoint
	var err error
	args.mountpoint, err = filepath.Abs(flagSet.Arg(1))
	if err != nil {
		tlog.Fatal.Printf("Invalid mountpoint: %v", err)
		os.Exit(ErrExitMountPoint)
	}
	// We cannot mount "/home/user/.cipher" at "/home/user" because the mount
	// will hide ".cipher" also for us.
	if args.cipherdir == args.mountpoint || strings.HasPrefix(args.cipherdir, args.mountpoint+"/") {
		tlog.Fatal.Printf("Mountpoint %q would shadow cipherdir %q, this is not supported",
			args.mountpoint, args.cipherdir)
		os.Exit(ErrExitMountPoint)
	}
	if args.nonempty {
		err = checkDir(args.mountpoint)
	} else {
		err = checkDirEmpty(args.mountpoint)
	}
	if err != nil {
		tlog.Fatal.Printf("Invalid mountpoint: %v", err)
		os.Exit(ErrExitMountPoint)
	}
	// Open control socket early so we can error out before asking the user
	// for the password
	if args.ctlsock != "" {
		// We must use an absolute path because we cd to / when daemonizing.
		// This messes up the delete-on-close logic in the unix socket object.
		args.ctlsock, _ = filepath.Abs(args.ctlsock)
		var sock net.Listener
		sock, err = net.Listen("unix", args.ctlsock)
		if err != nil {
			tlog.Fatal.Printf("ctlsock: %v", err)
			os.Exit(ErrExitMount)
		}
		args._ctlsockFd = sock
		// Close also deletes the socket file
		defer func() {
			err = sock.Close()
			if err != nil {
				tlog.Warn.Print(err)
			}
		}()
	}
	// Get master key (may prompt for the password)
	var masterkey []byte
	var confFile *configfile.ConfFile
	if args.masterkey != "" {
		// "-masterkey"
		masterkey = parseMasterKey(args.masterkey)
	} else if args.zerokey {
		// "-zerokey"
		tlog.Info.Printf("Using all-zero dummy master key.")
		tlog.Info.Printf(tlog.ColorYellow +
			"ZEROKEY MODE PROVIDES NO SECURITY AT ALL AND SHOULD ONLY BE USED FOR TESTING." +
			tlog.ColorReset)
		masterkey = make([]byte, cryptocore.KeyLen)
	} else {
		// Load master key from config file
		// Prompts the user for the password
		masterkey, confFile, err = loadConfig(args)
		if err != nil {
			if args._ctlsockFd != nil {
				// Close the socket file (which also deletes it)
				args._ctlsockFd.Close()
			}
			exitcodes.Exit(err)
		}
		readpassword.CheckTrailingGarbage()
		printMasterKey(masterkey)
	}
	// We cannot use JSON for pretty-printing as the fields are unexported
	tlog.Debug.Printf("cli args: %#v", args)
	// Initialize FUSE server
	srv := initFuseFrontend(masterkey, args, confFile)
	tlog.Info.Println(tlog.ColorGreen + "Filesystem mounted and ready." + tlog.ColorReset)
	var paniclog *os.File
	// We have been forked into the background, as evidenced by the set
	// "notifypid".
	if args.notifypid > 0 {
		// Chdir to the root directory so we don't block unmounting the CWD
		os.Chdir("/")
		// Switch to syslog
		if !args.nosyslog {
			paniclog, err = ioutil.TempFile("", "gocryptfs_paniclog.")
			if err != nil {
				tlog.Fatal.Printf("Failed to create gocryptfs_paniclog: %v", err)
				os.Exit(ErrExitMount)
			}
			// Switch all of our logs and the generic logger to syslog
			tlog.Info.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_INFO)
			tlog.Debug.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_DEBUG)
			tlog.Warn.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_WARNING)
			tlog.SwitchLoggerToSyslog(syslog.LOG_USER | syslog.LOG_WARNING)
			// Daemons should close all fds (and we don't want to get killed by
			// SIGPIPE if any of those get closed on the other end)
			os.Stdin.Close()
			// Redirect stdout and stderr to /tmp/gocryptfs_paniclog.NNNNNN
			// instead of closing them so users have a chance to get the
			// backtrace on a panic.
			// https://github.com/golang/go/issues/325#issuecomment-66049178
			syscall.Dup2(int(paniclog.Fd()), 1)
			syscall.Dup2(int(paniclog.Fd()), 2)
			// No need for the extra FD anymore, we have it saved in Stderr
			paniclog.Close()
		}
		// Disconnect from the controlling terminal by creating a new session.
		// This prevents us from getting SIGINT when the user presses Ctrl-C
		// to exit a running script that has called gocryptfs.
		_, err = syscall.Setsid()
		if err != nil {
			tlog.Warn.Printf("Setsid: %v", err)
		}
		// Send SIGUSR1 to our parent
		sendUsr1(args.notifypid)
	}
	// Wait for SIGINT in the background and unmount ourselves if we get it.
	// This prevents a dangling "Transport endpoint is not connected"
	// mountpoint if the user hits CTRL-C.
	handleSigint(srv, args.mountpoint)
	// Jump into server loop. Returns when it gets an umount request from the kernel.
	srv.Serve()
	// Delete empty paniclogs
	if paniclog != nil {
		// The paniclog FD is saved in Stderr
		fi, err := os.Stderr.Stat()
		if err != nil {
			tlog.Warn.Printf("paniclog fstat error: %v", err)
		} else if fi.Size() > 0 {
			tlog.Warn.Printf("paniclog at %q is not empty (size %d). Not deleting it.",
				paniclog.Name(), fi.Size())
		} else {
			syscall.Unlink(paniclog.Name())
		}
	}
	return 0
}

// initFuseFrontend - initialize gocryptfs/fusefrontend
// Calls os.Exit on errors
func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfFile) *fuse.Server {
	// Reconciliate CLI and config file arguments into a fusefrontend.Args struct
	// that is passed to the filesystem implementation
	cryptoBackend := cryptocore.BackendGoGCM
	if args.openssl {
		cryptoBackend = cryptocore.BackendOpenSSL
	}
	if args.aessiv {
		cryptoBackend = cryptocore.BackendAESSIV
	}
	frontendArgs := fusefrontend.Args{
		Cipherdir:      args.cipherdir,
		Masterkey:      key,
		PlaintextNames: args.plaintextnames,
		LongNames:      args.longnames,
		CryptoBackend:  cryptoBackend,
		ConfigCustom:   args._configCustom,
		Raw64:          args.raw64,
		NoPrealloc:     args.noprealloc,
		HKDF:           args.hkdf,
		SerializeReads: args.serialize_reads,
		ForceDecode:    args.forcedecode,
	}
	// confFile is nil when "-zerokey" or "-masterkey" was used
	if confFile != nil {
		// Settings from the config file override command line args
		frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames)
		frontendArgs.Raw64 = confFile.IsFeatureFlagSet(configfile.FlagRaw64)
		frontendArgs.HKDF = confFile.IsFeatureFlagSet(configfile.FlagHKDF)
		if confFile.IsFeatureFlagSet(configfile.FlagAESSIV) {
			frontendArgs.CryptoBackend = cryptocore.BackendAESSIV
		} else if args.reverse {
			tlog.Fatal.Printf("AES-SIV is required by reverse mode, but not enabled in the config file")
			os.Exit(ErrExitUsage)
		}
	}
	// If allow_other is set and we run as root, try to give newly created files to
	// the right user.
	if args.allow_other && os.Getuid() == 0 {
		frontendArgs.PreserveOwner = true
	}
	jsonBytes, _ := json.MarshalIndent(frontendArgs, "", "\t")
	tlog.Debug.Printf("frontendArgs: %s", string(jsonBytes))
	var finalFs pathfs.FileSystem
	var ctlSockBackend ctlsock.Interface
	if args.reverse {
		fs := fusefrontend_reverse.NewFS(frontendArgs)
		finalFs = fs
		ctlSockBackend = fs
	} else {
		fs := fusefrontend.NewFS(frontendArgs)
		finalFs = fs
		ctlSockBackend = fs
	}
	// We have opened the socket early so that we cannot fail here after
	// asking the user for the password
	if args._ctlsockFd != nil {
		go ctlsock.Serve(args._ctlsockFd, ctlSockBackend)
	}
	pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true}
	pathFs := pathfs.NewPathNodeFs(finalFs, pathFsOpts)
	fuseOpts := &nodefs.Options{
		// These options are to be compatible with libfuse defaults,
		// making benchmarking easier.
		NegativeTimeout: time.Second,
		AttrTimeout:     time.Second,
		EntryTimeout:    time.Second,
	}
	conn := nodefs.NewFileSystemConnector(pathFs.Root(), fuseOpts)
	var mOpts fuse.MountOptions
	mOpts.AllowOther = false
	if args.allow_other {
		tlog.Info.Printf(tlog.ColorYellow + "The option \"-allow_other\" is set. Make sure the file " +
			"permissions protect your data from unwanted access." + tlog.ColorReset)
		mOpts.AllowOther = true
		// Make the kernel check the file permissions for us
		mOpts.Options = append(mOpts.Options, "default_permissions")
	}
	if args.forcedecode {
		tlog.Info.Printf(tlog.ColorYellow + "THE OPTION \"-forcedecode\" IS ACTIVE. GOCRYPTFS WILL RETURN CORRUPT DATA!" +
			tlog.ColorReset)
	}
	if args.nonempty {
		mOpts.Options = append(mOpts.Options, "nonempty")
	}
	// Set values shown in "df -T" and friends
	// First column, "Filesystem"
	fsname := args.cipherdir
	if args.fsname != "" {
		fsname = args.fsname
	}
	mOpts.Options = append(mOpts.Options, "fsname="+fsname)
	// Second column, "Type", will be shown as "fuse." + Name
	mOpts.Name = "gocryptfs"
	if args.reverse {
		mOpts.Name += "-reverse"
	}
	// The kernel enforces read-only operation, we just have to pass "ro".
	// Reverse mounts are always read-only.
	if args.ro || args.reverse {
		mOpts.Options = append(mOpts.Options, "ro")
	}
	// Add additional mount options (if any) after the stock ones, so the user has
	// a chance to override them.
	if args.ko != "" {
		parts := strings.Split(args.ko, ",")
		tlog.Debug.Printf("Adding -ko mount options: %v", parts)
		mOpts.Options = append(mOpts.Options, parts...)
	}
	srv, err := fuse.NewServer(conn.RawFS(), args.mountpoint, &mOpts)
	if err != nil {
		tlog.Fatal.Printf("Mount failed: %v", err)
		os.Exit(ErrExitMount)
	}
	srv.SetDebug(args.fusedebug)

	// All FUSE file and directory create calls carry explicit permission
	// information. We need an unrestricted umask to create the files and
	// directories with the requested permissions.
	syscall.Umask(0000)

	return srv
}

func handleSigint(srv *fuse.Server, mountpoint string) {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt)
	signal.Notify(ch, syscall.SIGTERM)
	go func() {
		<-ch
		err := srv.Unmount()
		if err != nil {
			tlog.Warn.Print(err)
			if runtime.GOOS == "linux" {
				// MacOSX does not support lazy unmount
				tlog.Info.Printf("Trying lazy unmount")
				cmd := exec.Command("fusermount", "-u", "-z", mountpoint)
				cmd.Stdout = os.Stdout
				cmd.Stderr = os.Stderr
				cmd.Run()
			}
		}
		os.Exit(1)
	}()
}
<commit_msg>main: increase open file limit to 4096<commit_after>package main

import (
	"encoding/json"
	"io/ioutil"
	"log/syslog"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"time"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
	"github.com/hanwen/go-fuse/fuse/pathfs"

	"github.com/rfjakob/gocryptfs/internal/configfile"
	"github.com/rfjakob/gocryptfs/internal/cryptocore"
	"github.com/rfjakob/gocryptfs/internal/ctlsock"
	"github.com/rfjakob/gocryptfs/internal/exitcodes"
	"github.com/rfjakob/gocryptfs/internal/fusefrontend"
	"github.com/rfjakob/gocryptfs/internal/fusefrontend_reverse"
	"github.com/rfjakob/gocryptfs/internal/readpassword"
	"github.com/rfjakob/gocryptfs/internal/tlog"
)

// doMount mounts an encrypted directory.
// Called from main.
func doMount(args *argContainer) int {
	// Check mountpoint
	var err error
	args.mountpoint, err = filepath.Abs(flagSet.Arg(1))
	if err != nil {
		tlog.Fatal.Printf("Invalid mountpoint: %v", err)
		os.Exit(ErrExitMountPoint)
	}
	// We cannot mount "/home/user/.cipher" at "/home/user" because the mount
	// will hide ".cipher" also for us.
	if args.cipherdir == args.mountpoint || strings.HasPrefix(args.cipherdir, args.mountpoint+"/") {
		tlog.Fatal.Printf("Mountpoint %q would shadow cipherdir %q, this is not supported",
			args.mountpoint, args.cipherdir)
		os.Exit(ErrExitMountPoint)
	}
	if args.nonempty {
		err = checkDir(args.mountpoint)
	} else {
		err = checkDirEmpty(args.mountpoint)
	}
	if err != nil {
		tlog.Fatal.Printf("Invalid mountpoint: %v", err)
		os.Exit(ErrExitMountPoint)
	}
	// Open control socket early so we can error out before asking the user
	// for the password
	if args.ctlsock != "" {
		// We must use an absolute path because we cd to / when daemonizing.
		// This messes up the delete-on-close logic in the unix socket object.
		args.ctlsock, _ = filepath.Abs(args.ctlsock)
		var sock net.Listener
		sock, err = net.Listen("unix", args.ctlsock)
		if err != nil {
			tlog.Fatal.Printf("ctlsock: %v", err)
			os.Exit(ErrExitMount)
		}
		args._ctlsockFd = sock
		// Close also deletes the socket file
		defer func() {
			err = sock.Close()
			if err != nil {
				tlog.Warn.Print(err)
			}
		}()
	}
	// Get master key (may prompt for the password)
	var masterkey []byte
	var confFile *configfile.ConfFile
	if args.masterkey != "" {
		// "-masterkey"
		masterkey = parseMasterKey(args.masterkey)
	} else if args.zerokey {
		// "-zerokey"
		tlog.Info.Printf("Using all-zero dummy master key.")
		tlog.Info.Printf(tlog.ColorYellow +
			"ZEROKEY MODE PROVIDES NO SECURITY AT ALL AND SHOULD ONLY BE USED FOR TESTING." +
			tlog.ColorReset)
		masterkey = make([]byte, cryptocore.KeyLen)
	} else {
		// Load master key from config file
		// Prompts the user for the password
		masterkey, confFile, err = loadConfig(args)
		if err != nil {
			if args._ctlsockFd != nil {
				// Close the socket file (which also deletes it)
				args._ctlsockFd.Close()
			}
			exitcodes.Exit(err)
		}
		readpassword.CheckTrailingGarbage()
		printMasterKey(masterkey)
	}
	// We cannot use JSON for pretty-printing as the fields are unexported
	tlog.Debug.Printf("cli args: %#v", args)
	// Initialize FUSE server
	srv := initFuseFrontend(masterkey, args, confFile)
	tlog.Info.Println(tlog.ColorGreen + "Filesystem mounted and ready." + tlog.ColorReset)
	var paniclog *os.File
	// We have been forked into the background, as evidenced by the set
	// "notifypid".
	if args.notifypid > 0 {
		// Chdir to the root directory so we don't block unmounting the CWD
		os.Chdir("/")
		// Switch to syslog
		if !args.nosyslog {
			paniclog, err = ioutil.TempFile("", "gocryptfs_paniclog.")
			if err != nil {
				tlog.Fatal.Printf("Failed to create gocryptfs_paniclog: %v", err)
				os.Exit(ErrExitMount)
			}
			// Switch all of our logs and the generic logger to syslog
			tlog.Info.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_INFO)
			tlog.Debug.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_DEBUG)
			tlog.Warn.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_WARNING)
			tlog.SwitchLoggerToSyslog(syslog.LOG_USER | syslog.LOG_WARNING)
			// Daemons should close all fds (and we don't want to get killed by
			// SIGPIPE if any of those get closed on the other end)
			os.Stdin.Close()
			// Redirect stdout and stderr to /tmp/gocryptfs_paniclog.NNNNNN
			// instead of closing them so users have a chance to get the
			// backtrace on a panic.
			// https://github.com/golang/go/issues/325#issuecomment-66049178
			syscall.Dup2(int(paniclog.Fd()), 1)
			syscall.Dup2(int(paniclog.Fd()), 2)
			// No need for the extra FD anymore, we have it saved in Stderr
			paniclog.Close()
		}
		// Disconnect from the controlling terminal by creating a new session.
		// This prevents us from getting SIGINT when the user presses Ctrl-C
		// to exit a running script that has called gocryptfs.
		_, err = syscall.Setsid()
		if err != nil {
			tlog.Warn.Printf("Setsid: %v", err)
		}
		// Send SIGUSR1 to our parent
		sendUsr1(args.notifypid)
	}
	// Increase the number of open files limit up to the hard limit. It's not
	// dramatic if that fails, so do it after we have switched to syslog and not
	// bother the user with warning.
	var lim syscall.Rlimit
	syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
	lim.Cur = lim.Max
	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &lim)
	if err != nil {
		tlog.Warn.Printf("Setting RLIMIT_NOFILE failed: %v", err)
	} else if lim.Cur < 4096 {
		tlog.Warn.Printf("Low hard limit for RLIMIT_NOFILE: %d", lim.Cur)
	}
	// Wait for SIGINT in the background and unmount ourselves if we get it.
	// This prevents a dangling "Transport endpoint is not connected"
	// mountpoint if the user hits CTRL-C.
	handleSigint(srv, args.mountpoint)
	// Jump into server loop. Returns when it gets an umount request from the kernel.
	srv.Serve()
	// Delete empty paniclogs
	if paniclog != nil {
		// The paniclog FD is saved in Stderr
		fi, err := os.Stderr.Stat()
		if err != nil {
			tlog.Warn.Printf("paniclog fstat error: %v", err)
		} else if fi.Size() > 0 {
			tlog.Warn.Printf("paniclog at %q is not empty (size %d). Not deleting it.",
				paniclog.Name(), fi.Size())
		} else {
			syscall.Unlink(paniclog.Name())
		}
	}
	return 0
}

// initFuseFrontend - initialize gocryptfs/fusefrontend
// Calls os.Exit on errors
func initFuseFrontend(key []byte, args *argContainer, confFile *configfile.ConfFile) *fuse.Server {
	// Reconciliate CLI and config file arguments into a fusefrontend.Args struct
	// that is passed to the filesystem implementation
	cryptoBackend := cryptocore.BackendGoGCM
	if args.openssl {
		cryptoBackend = cryptocore.BackendOpenSSL
	}
	if args.aessiv {
		cryptoBackend = cryptocore.BackendAESSIV
	}
	frontendArgs := fusefrontend.Args{
		Cipherdir:      args.cipherdir,
		Masterkey:      key,
		PlaintextNames: args.plaintextnames,
		LongNames:      args.longnames,
		CryptoBackend:  cryptoBackend,
		ConfigCustom:   args._configCustom,
		Raw64:          args.raw64,
		NoPrealloc:     args.noprealloc,
		HKDF:           args.hkdf,
		SerializeReads: args.serialize_reads,
		ForceDecode:    args.forcedecode,
	}
	// confFile is nil when "-zerokey" or "-masterkey" was used
	if confFile != nil {
		// Settings from the config file override command line args
		frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames)
		frontendArgs.Raw64 = confFile.IsFeatureFlagSet(configfile.FlagRaw64)
		frontendArgs.HKDF = confFile.IsFeatureFlagSet(configfile.FlagHKDF)
		if confFile.IsFeatureFlagSet(configfile.FlagAESSIV) {
			frontendArgs.CryptoBackend = cryptocore.BackendAESSIV
		} else if args.reverse {
			tlog.Fatal.Printf("AES-SIV is required by reverse mode, but not enabled in the config file")
			os.Exit(ErrExitUsage)
		}
	}
	// If allow_other is set and we run as root, try to give newly created files to
	// the right user.
	if args.allow_other && os.Getuid() == 0 {
		frontendArgs.PreserveOwner = true
	}
	jsonBytes, _ := json.MarshalIndent(frontendArgs, "", "\t")
	tlog.Debug.Printf("frontendArgs: %s", string(jsonBytes))
	var finalFs pathfs.FileSystem
	var ctlSockBackend ctlsock.Interface
	if args.reverse {
		fs := fusefrontend_reverse.NewFS(frontendArgs)
		finalFs = fs
		ctlSockBackend = fs
	} else {
		fs := fusefrontend.NewFS(frontendArgs)
		finalFs = fs
		ctlSockBackend = fs
	}
	// We have opened the socket early so that we cannot fail here after
	// asking the user for the password
	if args._ctlsockFd != nil {
		go ctlsock.Serve(args._ctlsockFd, ctlSockBackend)
	}
	pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true}
	pathFs := pathfs.NewPathNodeFs(finalFs, pathFsOpts)
	fuseOpts := &nodefs.Options{
		// These options are to be compatible with libfuse defaults,
		// making benchmarking easier.
		NegativeTimeout: time.Second,
		AttrTimeout:     time.Second,
		EntryTimeout:    time.Second,
	}
	conn := nodefs.NewFileSystemConnector(pathFs.Root(), fuseOpts)
	var mOpts fuse.MountOptions
	mOpts.AllowOther = false
	if args.allow_other {
		tlog.Info.Printf(tlog.ColorYellow + "The option \"-allow_other\" is set. Make sure the file " +
			"permissions protect your data from unwanted access." + tlog.ColorReset)
		mOpts.AllowOther = true
		// Make the kernel check the file permissions for us
		mOpts.Options = append(mOpts.Options, "default_permissions")
	}
	if args.forcedecode {
		tlog.Info.Printf(tlog.ColorYellow + "THE OPTION \"-forcedecode\" IS ACTIVE. GOCRYPTFS WILL RETURN CORRUPT DATA!" +
			tlog.ColorReset)
	}
	if args.nonempty {
		mOpts.Options = append(mOpts.Options, "nonempty")
	}
	// Set values shown in "df -T" and friends
	// First column, "Filesystem"
	fsname := args.cipherdir
	if args.fsname != "" {
		fsname = args.fsname
	}
	mOpts.Options = append(mOpts.Options, "fsname="+fsname)
	// Second column, "Type", will be shown as "fuse." + Name
	mOpts.Name = "gocryptfs"
	if args.reverse {
		mOpts.Name += "-reverse"
	}
	// The kernel enforces read-only operation, we just have to pass "ro".
	// Reverse mounts are always read-only.
	if args.ro || args.reverse {
		mOpts.Options = append(mOpts.Options, "ro")
	}
	// Add additional mount options (if any) after the stock ones, so the user has
	// a chance to override them.
	if args.ko != "" {
		parts := strings.Split(args.ko, ",")
		tlog.Debug.Printf("Adding -ko mount options: %v", parts)
		mOpts.Options = append(mOpts.Options, parts...)
	}
	srv, err := fuse.NewServer(conn.RawFS(), args.mountpoint, &mOpts)
	if err != nil {
		tlog.Fatal.Printf("Mount failed: %v", err)
		os.Exit(ErrExitMount)
	}
	srv.SetDebug(args.fusedebug)

	// All FUSE file and directory create calls carry explicit permission
	// information. We need an unrestricted umask to create the files and
	// directories with the requested permissions.
	syscall.Umask(0000)

	return srv
}

func handleSigint(srv *fuse.Server, mountpoint string) {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt)
	signal.Notify(ch, syscall.SIGTERM)
	go func() {
		<-ch
		err := srv.Unmount()
		if err != nil {
			tlog.Warn.Print(err)
			if runtime.GOOS == "linux" {
				// MacOSX does not support lazy unmount
				tlog.Info.Printf("Trying lazy unmount")
				cmd := exec.Command("fusermount", "-u", "-z", mountpoint)
				cmd.Stdout = os.Stdout
				cmd.Stderr = os.Stderr
				cmd.Run()
			}
		}
		os.Exit(1)
	}()
}
<|endoftext|>


<commit_before>// Copyright 2013 Richard Lehane. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package mscfb implements a reader for Microsoft's Compound File Binary File Format (http://msdn.microsoft.com/en-us/library/dd942138.aspx).
//
// The Compound File Binary File Format is also known as the Object Linking and Embedding (OLE) or Component Object Model (COM) format and was used by many
// early MS software such as MS Office.
//
// Example:
//   file, _ := os.Open("test/test.doc")
//   defer file.Close()
//   doc, err := mscfb.New(file)
//   if err != nil {
//     log.Fatal(err)
//   }
//   for entry, err := doc.Next(); err == nil; entry, err = doc.Next() {
//     buf := make([]byte, 512)
//     i, _ := entry.Read(buf)
//     if i > 0 {
//       fmt.Println(buf[:i])
//     }
//     fmt.Println(entry.Name)
//   }
package mscfb

import (
	"encoding/binary"
	"errors"
	"io"
	"time"
)

var (
	ErrFormat = errors.New("mscfb: not a valid compound file")
	ErrRead   = errors.New("mscfb: error reading compound file")
	ErrBadDir = errors.New("mscfb: error traversing directory structure")
	ErrSeek   = errors.New("mscfb: error calculating offset")
)

var sectorSize uint32

func setSectorSize(ss uint16) {
	sectorSize = uint32(1 << ss)
}

const (
	signature            uint64 = 0xE11AB1A1E011CFD0
	miniStreamSectorSize uint32 = 64
	miniStreamCutoffSize uint64 = 4096
	dirEntrySize         uint32 = 128 //128 bytes
)

const (
	maxRegSect     uint32 = 0xFFFFFFFA // Maximum regular sector number
	difatSect      uint32 = 0xFFFFFFFC //Specifies a DIFAT sector in the FAT
	fatSect        uint32 = 0xFFFFFFFD // Specifies a FAT sector in the FAT
	endOfChain     uint32 = 0xFFFFFFFE // End of linked chain of sectors
	freeSect       uint32 = 0xFFFFFFFF // Speficies unallocated sector in the FAT, Mini FAT or DIFAT
	maxRegStreamID uint32 = 0xFFFFFFFA // maximum regular stream ID
	noStream       uint32 = 0xFFFFFFFF // empty pointer
)

func (r *Reader) readAt(offset int64, length int) ([]byte, error) {
	if r.slicer {
		b, err := r.ra.(slicer).Slice(int(offset), length)
		if err != nil {
			return nil, ErrRead
		}
		return b, nil
	}
	if length > len(r.buf) {
		return nil, ErrRead
	}
	if _, err := r.ra.ReadAt(r.buf[:length], offset); err != nil {
		return nil, ErrRead
	}
	return r.buf[:length], nil
}

func (r *Reader) fileOffset(sn uint32, mini bool) int64 {
	if mini {
		num := sectorSize / 64
		sec := sn / num
		dif := sn % num
		return int64((r.header.miniStreamLocs[sec]+1)*sectorSize + dif*64)
	}
	return int64((sn + 1) * sectorSize)
}

// check the FAT sector for the next sector in a chain
func (r *Reader) findNext(sn uint32, mini bool) (uint32, error) {
	entries := sectorSize / 4
	index := int(sn / entries) // find position in DIFAT or minifat array
	var sect uint32
	if mini {
		if index < 0 || index >= len(r.header.miniFatLocs) {
			return 0, ErrSeek
		}
		sect = r.header.miniFatLocs[index]
	} else {
		if index < 0 || index >= len(r.header.difats) {
			return 0, ErrSeek
		}
		sect = r.header.difats[index]
	}
	fatIndex := sn % entries // find position within FAT or MiniFAT sector
	offset := r.fileOffset(sect, false)
	offset += int64(fatIndex * 4)
	buf, err := r.readAt(offset, 4)
	if err != nil {
		return 0, err
	}
	return binary.LittleEndian.Uint32(buf), nil
}

// Reader provides sequential access to the contents of a MS compound file (MSCFB)
type Reader struct {
	slicer  bool
	buf     []byte
	header  *header
	File    []*File // File is a slice of directory entries. Not necessarily in correct order. Use Next() for order.
	entry   int
	indexes []int
	ra      io.ReaderAt
}

// New returns a MSCFB reader
func New(ra io.ReaderAt) (*Reader, error) {
	r := &Reader{ra: ra}
	if _, ok := ra.(slicer); ok {
		r.slicer = true
	} else {
		r.buf = make([]byte, lenHeader)
	}
	if err := r.setHeader(); err != nil {
		return nil, err
	}
	// resize the buffer to 4096 if sector size isn't 512
	if !r.slicer && int(sectorSize) > len(r.buf) {
		r.buf = make([]byte, sectorSize)
	}
	if err := r.setDifats(); err != nil {
		return nil, err
	}
	if err := r.setDirEntries(); err != nil {
		return nil, err
	}
	if err := r.setMiniStream(); err != nil {
		return nil, err
	}
	if err := r.traverse(); err != nil {
		return nil, err
	}
	return r, nil
}

// ID returns the CLSID (class ID) field from the root directory entry
func (r *Reader) ID() string {
	return r.File[0].ID()
}

// Created returns the created field from the root directory entry
func (r *Reader) Created() time.Time {
	return r.File[0].Created()
}

// Modified returns the last modified field from the root directory entry
func (r *Reader) Modified() time.Time {
	return r.File[0].Modified()
}

// Next iterates to the next directory entry.
// This isn't necessarily an adjacent *File within the File slice, but is based on the Left Sibling, Right Sibling and Child information in directory entries.
func (r *Reader) Next() (*File, error) {
	r.entry++
	if r.entry >= len(r.File) {
		return nil, io.EOF
	}
	return r.File[r.indexes[r.entry]], nil
}

// Read the current directory entry
func (r *Reader) Read(b []byte) (n int, err error) {
	if r.entry >= len(r.File) {
		return 0, io.EOF
	}
	return r.File[r.indexes[r.entry]].Read(b)
}

// Slicer interface avoids a copy by getting a byte slice directly from the underlying reader
type slicer interface {
	Slice(offset int, length int) ([]byte, error)
}
<commit_msg>int64<commit_after>// Copyright 2013 Richard Lehane. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package mscfb implements a reader for Microsoft's Compound File Binary File Format (http://msdn.microsoft.com/en-us/library/dd942138.aspx).
//
// The Compound File Binary File Format is also known as the Object Linking and Embedding (OLE) or Component Object Model (COM) format and was used by many
// early MS software such as MS Office.
//
// Example:
//   file, _ := os.Open("test/test.doc")
//   defer file.Close()
//   doc, err := mscfb.New(file)
//   if err != nil {
//     log.Fatal(err)
//   }
//   for entry, err := doc.Next(); err == nil; entry, err = doc.Next() {
//     buf := make([]byte, 512)
//     i, _ := entry.Read(buf)
//     if i > 0 {
//       fmt.Println(buf[:i])
//     }
//     fmt.Println(entry.Name)
//   }
package mscfb

import (
	"encoding/binary"
	"errors"
	"io"
	"time"
)

var (
	ErrFormat = errors.New("mscfb: not a valid compound file")
	ErrRead   = errors.New("mscfb: error reading compound file")
	ErrBadDir = errors.New("mscfb: error traversing directory structure")
	ErrSeek   = errors.New("mscfb: error calculating offset")
)

var sectorSize uint32

func setSectorSize(ss uint16) {
	sectorSize = uint32(1 << ss)
}

const (
	signature            uint64 = 0xE11AB1A1E011CFD0
	miniStreamSectorSize uint32 = 64
	miniStreamCutoffSize uint64 = 4096
	dirEntrySize         uint32 = 128 //128 bytes
)

const (
	maxRegSect     uint32 = 0xFFFFFFFA // Maximum regular sector number
	difatSect      uint32 = 0xFFFFFFFC //Specifies a DIFAT sector in the FAT
	fatSect        uint32 = 0xFFFFFFFD // Specifies a FAT sector in the FAT
	endOfChain     uint32 = 0xFFFFFFFE // End of linked chain of sectors
	freeSect       uint32 = 0xFFFFFFFF // Speficies unallocated sector in the FAT, Mini FAT or DIFAT
	maxRegStreamID uint32 = 0xFFFFFFFA // maximum regular stream ID
	noStream       uint32 = 0xFFFFFFFF // empty pointer
)

func (r *Reader) readAt(offset int64, length int) ([]byte, error) {
	if r.slicer {
		b, err := r.ra.(slicer).Slice(offset, length)
		if err != nil {
			return nil, ErrRead
		}
		return b, nil
	}
	if length > len(r.buf) {
		return nil, ErrRead
	}
	if _, err := r.ra.ReadAt(r.buf[:length], offset); err != nil {
		return nil, ErrRead
	}
	return r.buf[:length], nil
}

func (r *Reader) fileOffset(sn uint32, mini bool) int64 {
	if mini {
		num := sectorSize / 64
		sec := sn / num
		dif := sn % num
		return int64((r.header.miniStreamLocs[sec]+1)*sectorSize + dif*64)
	}
	return int64((sn + 1) * sectorSize)
}

// check the FAT sector for the next sector in a chain
func (r *Reader) findNext(sn uint32, mini bool) (uint32, error) {
	entries := sectorSize / 4
	index := int(sn / entries) // find position in DIFAT or minifat array
	var sect uint32
	if mini {
		if index < 0 || index >= len(r.header.miniFatLocs) {
			return 0, ErrSeek
		}
		sect = r.header.miniFatLocs[index]
	} else {
		if index < 0 || index >= len(r.header.difats) {
			return 0, ErrSeek
		}
		sect = r.header.difats[index]
	}
	fatIndex := sn % entries // find position within FAT or MiniFAT sector
	offset := r.fileOffset(sect, false)
	offset += int64(fatIndex * 4)
	buf, err := r.readAt(offset, 4)
	if err != nil {
		return 0, err
	}
	return binary.LittleEndian.Uint32(buf), nil
}

// Reader provides sequential access to the contents of a MS compound file (MSCFB)
type Reader struct {
	slicer  bool
	buf     []byte
	header  *header
	File    []*File // File is a slice of directory entries. Not necessarily in correct order. Use Next() for order.
	entry   int
	indexes []int
	ra      io.ReaderAt
}

// New returns a MSCFB reader
func New(ra io.ReaderAt) (*Reader, error) {
	r := &Reader{ra: ra}
	if _, ok := ra.(slicer); ok {
		r.slicer = true
	} else {
		r.buf = make([]byte, lenHeader)
	}
	if err := r.setHeader(); err != nil {
		return nil, err
	}
	// resize the buffer to 4096 if sector size isn't 512
	if !r.slicer && int(sectorSize) > len(r.buf) {
		r.buf = make([]byte, sectorSize)
	}
	if err := r.setDifats(); err != nil {
		return nil, err
	}
	if err := r.setDirEntries(); err != nil {
		return nil, err
	}
	if err := r.setMiniStream(); err != nil {
		return nil, err
	}
	if err := r.traverse(); err != nil {
		return nil, err
	}
	return r, nil
}

// ID returns the CLSID (class ID) field from the root directory entry
func (r *Reader) ID() string {
	return r.File[0].ID()
}

// Created returns the created field from the root directory entry
func (r *Reader) Created() time.Time {
	return r.File[0].Created()
}

// Modified returns the last modified field from the root directory entry
func (r *Reader) Modified() time.Time {
	return r.File[0].Modified()
}

// Next iterates to the next directory entry.
// This isn't necessarily an adjacent *File within the File slice, but is based on the Left Sibling, Right Sibling and Child information in directory entries.
func (r *Reader) Next() (*File, error) {
	r.entry++
	if r.entry >= len(r.File) {
		return nil, io.EOF
	}
	return r.File[r.indexes[r.entry]], nil
}

// Read the current directory entry
func (r *Reader) Read(b []byte) (n int, err error) {
	if r.entry >= len(r.File) {
		return 0, io.EOF
	}
	return r.File[r.indexes[r.entry]].Read(b)
}

// Slicer interface avoids a copy by getting a byte slice directly from the underlying reader
type slicer interface {
	Slice(offset int64, length int) ([]byte, error)
}
<|endoftext|>


<commit_before>package msgid

import (
	"bytes"
	"encoding/base64"
	"encoding/binary"
	"strconv"
	"sync"
	"time"
)

// Reserve a byte out of the counter half of the Id.
// Maybe for subnodes or go routines if useful.
var max int64 = 1<<23 - 1

func read_int64(data []byte) (ret int64) {
	buf := bytes.NewBuffer(data)
	binary.Read(buf, binary.BigEndian, &ret)
	return
}

type MsgId2 [16]byte

type MsgId struct {
	Millis int64
	Id     int64
}

// Get the SpawnId from the MsgId instance.
func (m MsgId) SpawnerId() int32 {
	return int32(m.Id >> 32)
}

// Get the sequence (counter) from the MsgId instance.  This will overlap
// after enough instances or on a restart.  The overall instance will be unique
// when combined with Millis.  This will rarely be useful by itself.
func (m MsgId) SequenceVal() int32 {
	return int32(m.Id)
}

// Get the milliseconds of the MsgId converted to a time.Time struct.
// The time has millisecond resolution per the name.
func (m MsgId) Time() time.Time {
	return time.Unix(0, m.Millis*1e6)

}

// Encode the MsgId instance as a base64 encoded string.
func (m MsgId) EncodeB64() string {
	b := make([]byte, 16)
	binary.BigEndian.PutUint64(b[0:8], uint64(m.Millis))
	binary.BigEndian.PutUint64(b[8:16], uint64(m.Id))
	ret := base64.StdEncoding.EncodeToString(b)
	return ret
}

type InvalidDataLengthError int64

func (e InvalidDataLengthError) Error() string {
	return "The token to be decoded must be 16 bytes after decoding.  The length was " + strconv.FormatInt(int64(e), 10)
}

// Decode a base64 encoded string into a MsgId instance.
func DecodeB64(input string) (*MsgId, error) {
	data, err := base64.StdEncoding.DecodeString(input)
	if err != nil {
		return nil, err
	}
	if len(data) != 16 {
		return nil, InvalidDataLengthError(len(data))
	}
	ret := new(MsgId)
	ret.Millis = read_int64(data[0:8])
	ret.Id = read_int64(data[8:])
	return ret, nil
}

type counter struct {
	curVal int64
	prev   int64
	sync.Mutex
}

func (ctr *counter) next(millis int64) (int64, int64) {
	ctr.Lock()
	defer ctr.Unlock()
	if millis > ctr.prev {
		ctr.prev = millis
		ctr.curVal = 1
	} else {
		ctr.curVal++
	}
	return ctr.prev, ctr.curVal
}

// MsgIdGenerator struct holding the spawnerId and internal counter.
type MsgIdGenerator struct {
	spawnerId int64
	ctr       *counter
}

// SpawnerId associated with this instance.
func (gen *MsgIdGenerator) SpawnerId() int32 {
	return int32(gen.spawnerId)
}

// Create a new MsgIdGenerator.
func New(spawnerId int32) *MsgIdGenerator {
	gen := &MsgIdGenerator{spawnerId: int64(spawnerId)}
	gen.ctr = new(counter)
	return gen
}

// Get the next MsgId.
func (gen *MsgIdGenerator) Next() MsgId {
	millis, id := gen.ctr.next(time.Now().UnixNano() / 1e6)
	return MsgId{millis, gen.spawnerId<<32 | id}
}

// Convenience method to get the next MsgId as a Base64 encoded string.
func (gen *MsgIdGenerator) NextToken() string {
	msgId := gen.Next()
	return msgId.EncodeB64()
}
<commit_msg>Add default db column names<commit_after>package msgid

import (
	"bytes"
	"encoding/base64"
	"encoding/binary"
	"strconv"
	"sync"
	"time"
)

// Reserve a byte out of the counter half of the Id.
// Maybe for subnodes or go routines if useful.
var max int64 = 1<<23 - 1

func read_int64(data []byte) (ret int64) {
	buf := bytes.NewBuffer(data)
	binary.Read(buf, binary.BigEndian, &ret)
	return
}

type MsgId2 [16]byte

type MsgId struct {
	Millis int64 `db:"millis"`
	Id     int64 `db:"seq"`
}

// Get the SpawnId from the MsgId instance.
func (m MsgId) SpawnerId() int32 {
	return int32(m.Id >> 32)
}

// Get the sequence (counter) from the MsgId instance.  This will overlap
// after enough instances or on a restart.  The overall instance will be unique
// when combined with Millis.  This will rarely be useful by itself.
func (m MsgId) SequenceVal() int32 {
	return int32(m.Id)
}

// Get the milliseconds of the MsgId converted to a time.Time struct.
// The time has millisecond resolution per the name.
func (m MsgId) Time() time.Time {
	return time.Unix(0, m.Millis*1e6)

}

// Encode the MsgId instance as a base64 encoded string.
func (m MsgId) EncodeB64() string {
	b := make([]byte, 16)
	binary.BigEndian.PutUint64(b[0:8], uint64(m.Millis))
	binary.BigEndian.PutUint64(b[8:16], uint64(m.Id))
	ret := base64.StdEncoding.EncodeToString(b)
	return ret
}

type InvalidDataLengthError int64

func (e InvalidDataLengthError) Error() string {
	return "The token to be decoded must be 16 bytes after decoding.  The length was " + strconv.FormatInt(int64(e), 10)
}

// Decode a base64 encoded string into a MsgId instance.
func DecodeB64(input string) (*MsgId, error) {
	data, err := base64.StdEncoding.DecodeString(input)
	if err != nil {
		return nil, err
	}
	if len(data) != 16 {
		return nil, InvalidDataLengthError(len(data))
	}
	ret := new(MsgId)
	ret.Millis = read_int64(data[0:8])
	ret.Id = read_int64(data[8:])
	return ret, nil
}

type counter struct {
	curVal int64
	prev   int64
	sync.Mutex
}

func (ctr *counter) next(millis int64) (int64, int64) {
	ctr.Lock()
	defer ctr.Unlock()
	if millis > ctr.prev {
		ctr.prev = millis
		ctr.curVal = 1
	} else {
		ctr.curVal++
	}
	return ctr.prev, ctr.curVal
}

// MsgIdGenerator struct holding the spawnerId and internal counter.
type MsgIdGenerator struct {
	spawnerId int64
	ctr       *counter
}

// SpawnerId associated with this instance.
func (gen *MsgIdGenerator) SpawnerId() int32 {
	return int32(gen.spawnerId)
}

// Create a new MsgIdGenerator.
func New(spawnerId int32) *MsgIdGenerator {
	gen := &MsgIdGenerator{spawnerId: int64(spawnerId)}
	gen.ctr = new(counter)
	return gen
}

// Get the next MsgId.
func (gen *MsgIdGenerator) Next() MsgId {
	millis, id := gen.ctr.next(time.Now().UnixNano() / 1e6)
	return MsgId{millis, gen.spawnerId<<32 | id}
}

// Convenience method to get the next MsgId as a Base64 encoded string.
func (gen *MsgIdGenerator) NextToken() string {
	msgId := gen.Next()
	return msgId.EncodeB64()
}
<|endoftext|>


<commit_before>package ntemplate

import (
	"os"
	"path/filepath"
	"strings"

	"github.com/webx-top/echo"
)

type PathAliases map[string][]string

func (p PathAliases) Add(alias, absPath string) PathAliases {
	var err error
	absPath, err = filepath.Abs(absPath)
	if err != nil {
		panic(err)
	}
	if _, ok := p[alias]; !ok {
		p[alias] = []string{}
	}
	if !strings.HasSuffix(absPath, echo.FilePathSeparator) {
		absPath += echo.FilePathSeparator
	}
	p[alias] = append(p[alias], absPath)
	return p
}

func (p PathAliases) ParsePrefix(withAliasPrefixPath string) string {
	rpath, _ := p.ParsePrefixOk(withAliasPrefixPath)
	return rpath
}

func (p PathAliases) ParsePrefixOk(withAliasPrefixPath string) (string, bool) {
	if len(withAliasPrefixPath) < 3 {
		return withAliasPrefixPath, false
	}
	if withAliasPrefixPath[0] == '/' || withAliasPrefixPath[0] == '.' {
		fi, err := os.Stat(withAliasPrefixPath)
		if err == nil && !fi.IsDir() {
			return withAliasPrefixPath, false
		}
		withAliasPrefixPath = withAliasPrefixPath[1:]
	}
	parts := strings.SplitN(withAliasPrefixPath, `/`, 2)
	if len(parts) != 2 {
		return withAliasPrefixPath, false
	}
	alias := parts[0]
	if opaths, ok := p[alias]; ok {
		if len(opaths) == 1 {
			return filepath.Join(opaths[0], withAliasPrefixPath), true
		}
		for _, opath := range opaths {
			_tmpl := filepath.Join(opath, withAliasPrefixPath)
			fi, err := os.Stat(_tmpl)
			if err == nil && !fi.IsDir() {
				return _tmpl, true
			}
		}
	}
	return withAliasPrefixPath, false
}

func (p PathAliases) RestorePrefix(fullpath string) string {
	rpath, _ := p.RestorePrefixOk(fullpath)
	return rpath
}

func (p PathAliases) RestorePrefixOk(fullpath string) (string, bool) {
	for _, absPaths := range p {
		for _, absPath := range absPaths {
			if strings.HasPrefix(fullpath, absPath) {
				return filepath.ToSlash(fullpath[len(absPath):]), true
			}
		}
	}
	return fullpath, false
}

func (p PathAliases) Parse(withAliasTagPath string) string {
	rpath, _ := p.ParseOk(withAliasTagPath)
	return rpath
}

func (p PathAliases) ParseOk(withAliasTagPath string) (string, bool) {
	if len(withAliasTagPath) < 3 || withAliasTagPath[0] != '[' {
		return withAliasTagPath, false
	}
	withAliasTagPath = withAliasTagPath[1:]
	parts := strings.SplitN(withAliasTagPath, `]`, 2)
	if len(parts) != 2 {
		return withAliasTagPath, false
	}
	alias := parts[0]
	rpath := parts[1]
	if opaths, ok := p[alias]; ok {
		if len(opaths) == 1 {
			return filepath.Join(opaths[0], rpath), true
		}
		for _, opath := range opaths {
			_tmpl := filepath.Join(opath, rpath)
			fi, err := os.Stat(_tmpl)
			if err == nil && !fi.IsDir() {
				return _tmpl, true
			}
		}
	}
	return rpath, false
}

func (p PathAliases) Restore(fullpath string) string {
	rpath, _ := p.RestoreOk(fullpath)
	return rpath
}

func (p PathAliases) RestoreOk(fullpath string) (string, bool) {
	for alias, absPaths := range p {
		for _, absPath := range absPaths {
			if strings.HasPrefix(fullpath, absPath) {
				return `[` + alias + `]` + filepath.ToSlash(fullpath[len(absPath):]), true
			}
		}
	}
	return fullpath, false
}
<commit_msg>update<commit_after>package ntemplate

import (
	"os"
	"path/filepath"
	"strings"

	"github.com/webx-top/echo"
)

type PathAliases map[string][]string

func (p PathAliases) AddAllSubdir(absPath string) error {
	fp, err := os.Open(absPath)
	if err != nil {
		return err
	}
	defer fp.Close()
	dirs, err := fp.ReadDir(-1)
	if err != nil {
		return err
	}
	for _, dir := range dirs {
		if strings.HasPrefix(dir.Name(), `.`) {
			continue
		}
		p.Add(dir.Name(), absPath)
	}
	return nil
}

func (p PathAliases) Add(alias, absPath string) PathAliases {
	var err error
	absPath, err = filepath.Abs(absPath)
	if err != nil {
		panic(err)
	}
	if _, ok := p[alias]; !ok {
		p[alias] = []string{}
	}
	if !strings.HasSuffix(absPath, echo.FilePathSeparator) {
		absPath += echo.FilePathSeparator
	}
	p[alias] = append(p[alias], absPath)
	return p
}

func (p PathAliases) ParsePrefix(withAliasPrefixPath string) string {
	rpath, _ := p.ParsePrefixOk(withAliasPrefixPath)
	return rpath
}

func (p PathAliases) ParsePrefixOk(withAliasPrefixPath string) (string, bool) {
	if len(withAliasPrefixPath) < 3 {
		return withAliasPrefixPath, false
	}
	if withAliasPrefixPath[0] == '/' || withAliasPrefixPath[0] == '.' {
		fi, err := os.Stat(withAliasPrefixPath)
		if err == nil && !fi.IsDir() {
			return withAliasPrefixPath, false
		}
		withAliasPrefixPath = withAliasPrefixPath[1:]
	}
	parts := strings.SplitN(withAliasPrefixPath, `/`, 2)
	if len(parts) != 2 {
		return withAliasPrefixPath, false
	}
	alias := parts[0]
	if opaths, ok := p[alias]; ok {
		if len(opaths) == 1 {
			return filepath.Join(opaths[0], withAliasPrefixPath), true
		}
		for _, opath := range opaths {
			_tmpl := filepath.Join(opath, withAliasPrefixPath)
			fi, err := os.Stat(_tmpl)
			if err == nil && !fi.IsDir() {
				return _tmpl, true
			}
		}
	}
	return withAliasPrefixPath, false
}

func (p PathAliases) RestorePrefix(fullpath string) string {
	rpath, _ := p.RestorePrefixOk(fullpath)
	return rpath
}

func (p PathAliases) RestorePrefixOk(fullpath string) (string, bool) {
	for _, absPaths := range p {
		for _, absPath := range absPaths {
			if strings.HasPrefix(fullpath, absPath) {
				return filepath.ToSlash(fullpath[len(absPath):]), true
			}
		}
	}
	return fullpath, false
}

func (p PathAliases) Parse(withAliasTagPath string) string {
	rpath, _ := p.ParseOk(withAliasTagPath)
	return rpath
}

func (p PathAliases) ParseOk(withAliasTagPath string) (string, bool) {
	if len(withAliasTagPath) < 3 || withAliasTagPath[0] != '[' {
		return withAliasTagPath, false
	}
	withAliasTagPath = withAliasTagPath[1:]
	parts := strings.SplitN(withAliasTagPath, `]`, 2)
	if len(parts) != 2 {
		return withAliasTagPath, false
	}
	alias := parts[0]
	rpath := parts[1]
	if opaths, ok := p[alias]; ok {
		if len(opaths) == 1 {
			return filepath.Join(opaths[0], rpath), true
		}
		for _, opath := range opaths {
			_tmpl := filepath.Join(opath, rpath)
			fi, err := os.Stat(_tmpl)
			if err == nil && !fi.IsDir() {
				return _tmpl, true
			}
		}
	}
	return rpath, false
}

func (p PathAliases) Restore(fullpath string) string {
	rpath, _ := p.RestoreOk(fullpath)
	return rpath
}

func (p PathAliases) RestoreOk(fullpath string) (string, bool) {
	for alias, absPaths := range p {
		for _, absPath := range absPaths {
			if strings.HasPrefix(fullpath, absPath) {
				return `[` + alias + `]` + filepath.ToSlash(fullpath[len(absPath):]), true
			}
		}
	}
	return fullpath, false
}
<|endoftext|>


<commit_before>//Package ngorm is a Go Object relation mapper that focus on performance,
//maintainability, modularity,	battle testing, extensibility , safety and
//developer frindliness.
//
// To achieve all of the goals, the project is divided into many components. The
// components are desined in a functional style API, whereby objects are
// explicitly passed around as arguments to functions that operate on them.
//
// This tries to avoid defining methods on structs. This comes at a cost of
// limiting chaining, this cost is intentional. I intend to work really hard on
// improving performance and thus avoiding spaghetti is not an option.
//
// Installation
//
// You can install  with go get
//   go get -u github.com/gernest/ngorm
//
//
// The package is divided into two phases, Query building and Query execution
// phase.
//
// Query Building
//
// The subpackage engine exposes a structure named Engine. This structure has
// everything necessary to build a query. Most of the functions defined in this
// package subpackages operate on this struct by accepting it as the first
// argument.
//
// Having this as a separate layer helps fine tuning the generated querries and
// also it make easy to test and very that the ORM is doing the right thing. So,
// the generated query can be easily optimised without adding a lot of overhead.
//
// Query execution
//
// This is s the phase where the generated sql query is executed. This phase is as generic as
// possible in a way that you can easily implement adoptes for non SQL database
// and still reap all the benefits of this package.
//
// Table of Ccntents
//
// The following are links to packages under this project.
//
// WARNING: You will never be touching most of these  packages. They are the
// building block of the high level API.
//   [engine] https://godoc.org/github.com/gernest/ngorm/engine
// This is what drives the whole project, helps with query building and provides
// conveinet structure to help with query execution.
//
//   [scope] https://godoc.org/github.com/gernest/ngorm/scope
// Functions to help with model manipulations.
//
//   [search] https://godoc.org/github.com/gernest/ngorm/search
// Functions to help with search  querries building.
//
//   [hooks] https://godoc.org/github.com/gernest/ngorm/hooks
// Callbacks executed by ngorm. You can easily overide and provide custom ones
// to suit your needs.
//
//   [logger] https://godoc.org/github.com/gernest/ngorm/logger
// The logger used by ngorm for logging. It is an interface, and a reference
// implementation is provided.
//
//   [dialects] https://godoc.org/github.com/gernest/ngorm/dialects
// Adopts to different SQL databases supported by ngorm. For now ngorm support
// ql .
package ngorm

import (
	"bytes"
	"context"
	"database/sql"
	"fmt"

	"github.com/gernest/ngorm/dialects"
	"github.com/gernest/ngorm/dialects/ql"
	"github.com/gernest/ngorm/engine"
	"github.com/gernest/ngorm/hooks"
	"github.com/gernest/ngorm/logger"
	"github.com/gernest/ngorm/model"
	"github.com/gernest/ngorm/scope"
	"github.com/uber-go/zap"
)

//Opener is an interface that is used to open up connection to SQL databases.
type Opener interface {
	Open(dialect string, args ...interface{}) (model.SQLCommon, dialects.Dialect, error)
}

// DB provide an API for interacting with SQL databases using Go data structures.
type DB struct {
	db            model.SQLCommon
	dialect       dialects.Dialect
	connStr       string
	ctx           context.Context
	cancel        func()
	singularTable bool
	structMap     *model.SafeStructsMap
	hooks         *hooks.Book
	log           *logger.Zapper
}

//Open opens a database connection and returns *DB instance., dialect is the
//name of the driver that you want to use. The underlying connections are
//handled by database/sql package. Arguments that are accepted by database/sql
//Open function are valid here.
//
// Not all databases are supported. There is still an ongoing efforts to add
// more databases but for now the following are the databases  supported by this
// library,
//
//   * ql https://github.com/cznic/ql
//
// The drivers for the libraries must be imported inside your application in the
// same package as you invoke this function.
//
// Example
//
//   import _ "github.com/cznic/ql/driver"  // imports ql driver
func Open(dialect string, args ...interface{}) (*DB, error) {
	return OpenWithOpener(&DefaultOpener{}, dialect, args...)
}

// OpenWithOpener uses the opener to initialize the dialects and establish
// database connection. In fact Open does not do anything by itself, it just
// calls this function with the default Opener.
//
// Please see Open function for details. The only difference is here you need to
// pass an Opener. See the Opener interface for details about what the Opener is
// and what it is used for.
func OpenWithOpener(opener Opener, dialect string, args ...interface{}) (*DB, error) {
	db, dia, err := opener.Open(dialect, args...)
	if err != nil {
		return nil, err
	}
	dia.SetDB(db)
	o := zap.New(
		zap.NewTextEncoder(zap.TextNoTime()), // drop timestamps in tests
	)
	ctx, cancel := context.WithCancel(context.Background())
	return &DB{
		db:        db,
		dialect:   dia,
		structMap: model.NewStructsMap(),
		ctx:       ctx,
		hooks:     hooks.DefaultBook(),
		cancel:    cancel,
		log:       logger.New(o),
	}, nil
}

// NewEngine returns an initialized engine ready to kick some ass.
func (db *DB) NewEngine() *engine.Engine {
	return &engine.Engine{
		Search:        &model.Search{},
		Scope:         &model.Scope{},
		StructMap:     db.structMap,
		SingularTable: db.singularTable,
		Ctx:           db.ctx,
		Dialect:       db.dialect,
		SQLDB:         db.db,
		Log:           db.log,
	}
}

//CreateTable creates new database tables that maps to the models.
func (db *DB) CreateTable(models ...interface{}) (sql.Result, error) {
	query, err := db.CreateTableSQL(models...)
	if err != nil {
		return nil, err
	}
	return db.ExecTx(query.Q, query.Args...)
}

//ExecTx wraps the query execution in a Transaction. This ensure all operations
//are Rolled back in case the execution fials.
func (db *DB) ExecTx(query string, args ...interface{}) (sql.Result, error) {
	tx, err := db.db.Begin()
	if err != nil {
		return nil, err
	}
	r, err := tx.Exec(query, args...)
	if err != nil {
		_ = tx.Rollback()
		return nil, err
	}
	err = tx.Commit()
	if err != nil {
		return nil, err
	}
	return r, nil
}

//CreateTableSQL return the sql query for creating tables for all the given
//models. The queries are wrapped in a TRANSACTION block.
func (db *DB) CreateTableSQL(models ...interface{}) (*model.Expr, error) {
	var buf bytes.Buffer
	_, _ = buf.WriteString("BEGIN TRANSACTION; \n")
	for _, m := range models {
		e := db.NewEngine()

		// Firste we generate the SQL
		err := scope.CreateTable(e, m)
		if err != nil {
			return nil, err
		}
		_, _ = buf.WriteString("\t" + e.Scope.SQL + ";\n")
		if e.Scope.MultiExpr {
			for _, expr := range e.Scope.Exprs {
				_, _ = buf.WriteString("\t" + expr.Q + ";\n")
			}
		}
	}
	_, _ = buf.WriteString("COMMIT;")
	return &model.Expr{Q: buf.String()}, nil
}

//DefaultOpener implements Opener interface.
type DefaultOpener struct {
}

//Open opens up database connection using the database/sql packge.
func (d *DefaultOpener) Open(dialect string, args ...interface{}) (model.SQLCommon, dialects.Dialect, error) {
	var source string
	var dia dialects.Dialect
	var common model.SQLCommon
	var err error

	switch value := args[0].(type) {
	case string:
		var driver = dialect
		if len(args) == 1 {
			source = value
		} else if len(args) >= 2 {
			driver = value
			source = args[1].(string)
		}
		common, err = sql.Open(driver, source)
		if err != nil {
			return nil, nil, err
		}
	case model.SQLCommon:
		common = value
	default:
		return nil, nil, fmt.Errorf("unknown argument %v", value)
	}
	switch dialect {
	case "ql":
		dia = ql.File()
	case "ql-mem":
		dia = ql.Memory()
	default:
		return nil, nil, fmt.Errorf("unsupported dialect %s", dialect)
	}
	return common, dia, nil
}
<commit_msg>Fix misspell<commit_after>//Package ngorm is a Go Object relation mapper that focus on performance,
//maintainability, modularity,	battle testing, extensibility , safety and
//developer frindliness.
//
// To achieve all of the goals, the project is divided into many components. The
// components are desined in a functional style API, whereby objects are
// explicitly passed around as arguments to functions that operate on them.
//
// This tries to avoid defining methods on structs. This comes at a cost of
// limiting chaining, this cost is intentional. I intend to work really hard on
// improving performance and thus avoiding spaghetti is not an option.
//
// Installation
//
// You can install  with go get
//   go get -u github.com/gernest/ngorm
//
//
// The package is divided into two phases, Query building and Query execution
// phase.
//
// Query Building
//
// The subpackage engine exposes a structure named Engine. This structure has
// everything necessary to build a query. Most of the functions defined in this
// package subpackages operate on this struct by accepting it as the first
// argument.
//
// Having this as a separate layer helps fine tuning the generated querries and
// also it make easy to test and very that the ORM is doing the right thing. So,
// the generated query can be easily optimised without adding a lot of overhead.
//
// Query execution
//
// This is s the phase where the generated sql query is executed. This phase is as generic as
// possible in a way that you can easily implement adoptes for non SQL database
// and still reap all the benefits of this package.
//
// Table of Ccntents
//
// The following are links to packages under this project.
//
// WARNING: You will never be touching most of these  packages. They are the
// building block of the high level API.
//   [engine] https://godoc.org/github.com/gernest/ngorm/engine
// This is what drives the whole project, helps with query building and provides
// conveinet structure to help with query execution.
//
//   [scope] https://godoc.org/github.com/gernest/ngorm/scope
// Functions to help with model manipulations.
//
//   [search] https://godoc.org/github.com/gernest/ngorm/search
// Functions to help with search  querries building.
//
//   [hooks] https://godoc.org/github.com/gernest/ngorm/hooks
// Callbacks executed by ngorm. You can easily overide and provide custom ones
// to suit your needs.
//
//   [logger] https://godoc.org/github.com/gernest/ngorm/logger
// The logger used by ngorm for logging. It is an interface, and a reference
// implementation is provided.
//
//   [dialects] https://godoc.org/github.com/gernest/ngorm/dialects
// Adopts to different SQL databases supported by ngorm. For now ngorm support
// ql .
package ngorm

import (
	"bytes"
	"context"
	"database/sql"
	"fmt"

	"github.com/gernest/ngorm/dialects"
	"github.com/gernest/ngorm/dialects/ql"
	"github.com/gernest/ngorm/engine"
	"github.com/gernest/ngorm/hooks"
	"github.com/gernest/ngorm/logger"
	"github.com/gernest/ngorm/model"
	"github.com/gernest/ngorm/scope"
	"github.com/uber-go/zap"
)

//Opener is an interface that is used to open up connection to SQL databases.
type Opener interface {
	Open(dialect string, args ...interface{}) (model.SQLCommon, dialects.Dialect, error)
}

// DB provide an API for interacting with SQL databases using Go data structures.
type DB struct {
	db            model.SQLCommon
	dialect       dialects.Dialect
	connStr       string
	ctx           context.Context
	cancel        func()
	singularTable bool
	structMap     *model.SafeStructsMap
	hooks         *hooks.Book
	log           *logger.Zapper
}

//Open opens a database connection and returns *DB instance., dialect is the
//name of the driver that you want to use. The underlying connections are
//handled by database/sql package. Arguments that are accepted by database/sql
//Open function are valid here.
//
// Not all databases are supported. There is still an ongoing efforts to add
// more databases but for now the following are the databases  supported by this
// library,
//
//   * ql https://github.com/cznic/ql
//
// The drivers for the libraries must be imported inside your application in the
// same package as you invoke this function.
//
// Example
//
//   import _ "github.com/cznic/ql/driver"  // imports ql driver
func Open(dialect string, args ...interface{}) (*DB, error) {
	return OpenWithOpener(&DefaultOpener{}, dialect, args...)
}

// OpenWithOpener uses the opener to initialize the dialects and establish
// database connection. In fact Open does not do anything by itself, it just
// calls this function with the default Opener.
//
// Please see Open function for details. The only difference is here you need to
// pass an Opener. See the Opener interface for details about what the Opener is
// and what it is used for.
func OpenWithOpener(opener Opener, dialect string, args ...interface{}) (*DB, error) {
	db, dia, err := opener.Open(dialect, args...)
	if err != nil {
		return nil, err
	}
	dia.SetDB(db)
	o := zap.New(
		zap.NewTextEncoder(zap.TextNoTime()), // drop timestamps in tests
	)
	ctx, cancel := context.WithCancel(context.Background())
	return &DB{
		db:        db,
		dialect:   dia,
		structMap: model.NewStructsMap(),
		ctx:       ctx,
		hooks:     hooks.DefaultBook(),
		cancel:    cancel,
		log:       logger.New(o),
	}, nil
}

// NewEngine returns an initialized engine ready to kick some ass.
func (db *DB) NewEngine() *engine.Engine {
	return &engine.Engine{
		Search:        &model.Search{},
		Scope:         &model.Scope{},
		StructMap:     db.structMap,
		SingularTable: db.singularTable,
		Ctx:           db.ctx,
		Dialect:       db.dialect,
		SQLDB:         db.db,
		Log:           db.log,
	}
}

//CreateTable creates new database tables that maps to the models.
func (db *DB) CreateTable(models ...interface{}) (sql.Result, error) {
	query, err := db.CreateTableSQL(models...)
	if err != nil {
		return nil, err
	}
	return db.ExecTx(query.Q, query.Args...)
}

//ExecTx wraps the query execution in a Transaction. This ensure all operations
//are Rolled back in case the execution fials.
func (db *DB) ExecTx(query string, args ...interface{}) (sql.Result, error) {
	tx, err := db.db.Begin()
	if err != nil {
		return nil, err
	}
	r, err := tx.Exec(query, args...)
	if err != nil {
		_ = tx.Rollback()
		return nil, err
	}
	err = tx.Commit()
	if err != nil {
		return nil, err
	}
	return r, nil
}

//CreateTableSQL return the sql query for creating tables for all the given
//models. The queries are wrapped in a TRANSACTION block.
func (db *DB) CreateTableSQL(models ...interface{}) (*model.Expr, error) {
	var buf bytes.Buffer
	_, _ = buf.WriteString("BEGIN TRANSACTION; \n")
	for _, m := range models {
		e := db.NewEngine()

		// Firste we generate the SQL
		err := scope.CreateTable(e, m)
		if err != nil {
			return nil, err
		}
		_, _ = buf.WriteString("\t" + e.Scope.SQL + ";\n")
		if e.Scope.MultiExpr {
			for _, expr := range e.Scope.Exprs {
				_, _ = buf.WriteString("\t" + expr.Q + ";\n")
			}
		}
	}
	_, _ = buf.WriteString("COMMIT;")
	return &model.Expr{Q: buf.String()}, nil
}

//DefaultOpener implements Opener interface.
type DefaultOpener struct {
}

//Open opens up database connection using the database/sql package.
func (d *DefaultOpener) Open(dialect string, args ...interface{}) (model.SQLCommon, dialects.Dialect, error) {
	var source string
	var dia dialects.Dialect
	var common model.SQLCommon
	var err error

	switch value := args[0].(type) {
	case string:
		var driver = dialect
		if len(args) == 1 {
			source = value
		} else if len(args) >= 2 {
			driver = value
			source = args[1].(string)
		}
		common, err = sql.Open(driver, source)
		if err != nil {
			return nil, nil, err
		}
	case model.SQLCommon:
		common = value
	default:
		return nil, nil, fmt.Errorf("unknown argument %v", value)
	}
	switch dialect {
	case "ql":
		dia = ql.File()
	case "ql-mem":
		dia = ql.Memory()
	default:
		return nil, nil, fmt.Errorf("unsupported dialect %s", dialect)
	}
	return common, dia, nil
}
<|endoftext|>


<commit_before>// This file is part of *xliffer*
//
// Copyright (C) 2015, Travelping GmbH <copyright@travelping.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strings"

	"github.com/tealeg/xlsx"
)

// converts a single master.xlsx into many .xliff files, ready
// to be shipped to translators.
//
// structure of the master.xlsx:
//
// name of sheet: PROJECT_NAME
//
// col0 - keys
// col1 - master-translation
// col2 - COUNTRY-LANG (iso 2 letter code)
//
// example master.xlsx:
//
// keys | note-column        | DE-de  | DE-en | EN-en | EN-x
// x    | greeting           | hallo  | hello | hello | hello
// y    | saying goodbye     | tschüß | bye   | bye   | bye
//
//
// from-xlsx creates translation .xliff files in the given directory:
//
// master.xlsx ->
//                 DE-de.xliff
//                 DE-en.xliff
//                 EN-en.xliff
//                 EN-x.xliff
//

const (
	XLSX_KEY_COLUMN    = 0
	XLSX_NOTE_COLUMN   = 1
	XLSX_SOURCE_COLUMN = 2
	XLSX_TARGET_COLUMN = 3

	XLSX_MAX_SHEETNAME = 30 // xlsx-limit
)

type xlsxConverter struct {
	fileName     string
	skipRows     int
	sheetNumber  int
	keyColumn    int
	noteColumn   int
	sourceColumn int
	targetColumn int
	sourceLang   string
	targetLang   string

	destDir  string
	exporter xlsxExporter
}

type xlsxTransUnit struct {
	Id         string
	Source     string
	SourceLang string
	Target     string
	TargetLang string
	Note       string
}

type xlsxExporter interface {
	Open(folder, base string) error
	Write(units []xlsxTransUnit) error
	Close() error
	Filename() string
}

func init() {
	registeredConverters["from-xlsx"] = new(xlsxConverter)
}

func (x *xlsxConverter) Description() string {
	return "Converts an Excel sheet to XLIFF, JSON"
}

func (x *xlsxConverter) ParseArgs(base string, args []string) error {

	fs := flag.NewFlagSet(base+" from-xlsx", flag.ExitOnError)
	destType := "xliff"
	pretty := false

	fs.StringVar(&destType, "to", "json", "output format (xliff, json)")
	fs.BoolVar(&pretty, "pretty", pretty, "pretty print the output files")
	fs.StringVar(&x.fileName, "in", "", "infile")
	fs.IntVar(&x.skipRows, "skip-rows", 0, "number of rows to skip")
	fs.IntVar(&x.sheetNumber, "sheet", 1, "number of the sheet containing the translations")
	fs.IntVar(&x.keyColumn, "key-column", 0, "column holding the key / msgid")
	fs.IntVar(&x.sourceColumn, "source-col", -1, "column holding the source for the translation")
	fs.IntVar(&x.noteColumn, "note-col", -1, "column holding notes (0 - not used)")
	fs.IntVar(&x.targetColumn, "target-col", -1, "column holding the target translation")
	fs.StringVar(&x.sourceLang, "source-lang", "en", "source language")
	fs.StringVar(&x.targetLang, "target-lang", "en", "target language")
	fs.StringVar(&x.destDir, "dir", "", "output directory")

	err := fs.Parse(args)
	if err != nil {
		return err
	}

	if x.destDir == "" {
		x.destDir, _ = os.Getwd()
	}
	if err = os.MkdirAll(x.destDir, 0777); err != nil {
		return err
	}

	switch destType {
	case "json":
		x.exporter = &xlsxJsonExporter{pretty: pretty}
	case "xliff":
		x.exporter = &xlsxXliffExporter{pretty: pretty}
	default:
		return fmt.Errorf("unsupported 'type': %q", destType)
	}

	return nil
}

func (conv *xlsxConverter) Prepare() error {
	return nil
}

func (conv *xlsxConverter) Convert(w io.Writer) error {

	var xlFile, err = xlsx.OpenFile(conv.fileName)
	if err != nil {
		return err
	}

	var sheet *xlsx.Sheet

	for s := range xlFile.Sheets {
		if s == (conv.sheetNumber - 1) {
			sheet = xlFile.Sheets[s]
			break
		}
	}

	if sheet == nil {
		return fmt.Errorf("did not find sheet %d in %s\n",
			conv.sheetNumber, conv.fileName)
	}

	keyRow, keyCol := conv.detectBounds(sheet)
	bodyRow := keyRow + 1

	srcCol := conv.sourceColumn
	if srcCol == -1 {
		// key | note | src | target-1 | target-2
		srcCol = keyCol + XLSX_SOURCE_COLUMN // TODO: detect "source" if not set
	}

	// the target column defines the column where the translated languages
	// start. if not defined, it defaults to the source column. why?
	// because that way the "initial" language also gets an export, either
	// to .xliff (which can then be handed to the translator team) or to
	// to .json and pretend it's already translated.
	targetCol := conv.targetColumn
	if targetCol == -1 {
		targetCol = srcCol // TODO: detect "target" automatically
	}

	head := sheet.Rows[keyRow].Cells

	//for x := range head[keyCol:] {
	//	fmt.Println(x, x+keyCol, head[x].String(), len(head), srcCol)
	//}

	srcLang := langFromCCLang(head[srcCol].String())
	rows := sheet.Rows

	for x := targetCol; x < len(rows[keyRow].Cells); x++ {

		cc_lang := head[x].String()
		lang := langFromCCLang(cc_lang)
		units := make([]xlsxTransUnit, 0, len(rows)-keyRow)

		for y := bodyRow; y < len(rows); y++ {
			row := rows[y]
			if (keyCol >= len(row.Cells)) || row.Cells[keyCol].String() == "" {
				continue
			}
			if (srcCol >= len(row.Cells)) || row.Cells[srcCol].String() == "" {
				continue
			}
			if (x >= len(row.Cells)) || row.Cells[x].String() == "" {
				continue
			}
			unit := conv.rowToTransUnit(y, keyCol, srcCol, x, srcLang, lang, rows)
			units = append(units, unit)
		}
		name := sheet.Name + "-" + cc_lang
		conv.exportUnits(conv.destDir, name, conv.exporter, units)
	}

	return nil
}

// extracts a row of the following form
//   key | comment | note | source | target-1 | target-2 | ...
// into a xlsxTransUnit
func (conv *xlsxConverter) rowToTransUnit(row, keyCol, sourceCol, targetCol int, sourceLang, targetLang string, rows []*xlsx.Row) xlsxTransUnit {
	unit := xlsxTransUnit{
		Id:         rows[row].Cells[keyCol].String(),
		Source:     rows[row].Cells[sourceCol].String(),
		SourceLang: sourceLang,
		Target:     rows[row].Cells[targetCol].String(),
		TargetLang: targetLang,
	}
	return unit
}

func (conv *xlsxConverter) exportUnits(dir, name string, x xlsxExporter, entries []xlsxTransUnit) {
	if err := x.Open(dir, name); err != nil {
		log.Printf("err: opening export file %q: %s", x.Filename(), err)
		return
	}
	defer x.Close()

	if err := x.Write(entries); err != nil {
		log.Printf("err: write entries to %q: %s", x.Filename(), err)
		return
	}

	log.Printf("written %d entries to %q: ok.", len(entries), x.Filename())
}

func langFromCCLang(cc_lang string) string {
	parts := strings.Split(cc_lang, "-")
	if len(parts) == 2 {
		return parts[1]
	}
	return ""
}

// finds the key-column after x.skipRows, where the content starts
// aka "the header".
func (x *xlsxConverter) detectBounds(sheet *xlsx.Sheet) (int, int) {

	var cell *xlsx.Cell
	i, j := 0, 0

	for i = range sheet.Rows {
		if i < (x.skipRows) {
			continue
		}

		for j, cell = range sheet.Rows[i].Cells {
			if key := cell.String(); key != "" {
				return i, j
			}
		}
	}

	return -1, -1
}
<commit_msg>rename function<commit_after>// This file is part of *xliffer*
//
// Copyright (C) 2015, Travelping GmbH <copyright@travelping.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strings"

	"github.com/tealeg/xlsx"
)

// converts a single master.xlsx into many .xliff files, ready
// to be shipped to translators.
//
// structure of the master.xlsx:
//
// name of sheet: PROJECT_NAME
//
// col0 - keys
// col1 - master-translation
// col2 - COUNTRY-LANG (iso 2 letter code)
//
// example master.xlsx:
//
// keys | note-column        | DE-de  | DE-en | EN-en | EN-x
// x    | greeting           | hallo  | hello | hello | hello
// y    | saying goodbye     | tschüß | bye   | bye   | bye
//
//
// from-xlsx creates translation .xliff files in the given directory:
//
// master.xlsx ->
//                 DE-de.xliff
//                 DE-en.xliff
//                 EN-en.xliff
//                 EN-x.xliff
//

const (
	XLSX_KEY_COLUMN    = 0
	XLSX_NOTE_COLUMN   = 1
	XLSX_SOURCE_COLUMN = 2
	XLSX_TARGET_COLUMN = 3

	XLSX_MAX_SHEETNAME = 30 // xlsx-limit
)

type xlsxConverter struct {
	fileName     string
	skipRows     int
	sheetNumber  int
	keyColumn    int
	noteColumn   int
	sourceColumn int
	targetColumn int
	sourceLang   string
	targetLang   string

	destDir  string
	exporter xlsxExporter
}

type xlsxTransUnit struct {
	Id         string
	Source     string
	SourceLang string
	Target     string
	TargetLang string
	Note       string
}

type xlsxExporter interface {
	Open(folder, base string) error
	Write(units []xlsxTransUnit) error
	Close() error
	Filename() string
}

func init() {
	registeredConverters["from-xlsx"] = new(xlsxConverter)
}

func (x *xlsxConverter) Description() string {
	return "Converts an Excel sheet to XLIFF, JSON"
}

func (x *xlsxConverter) ParseArgs(base string, args []string) error {

	fs := flag.NewFlagSet(base+" from-xlsx", flag.ExitOnError)
	destType := "xliff"
	pretty := false

	fs.StringVar(&destType, "to", "json", "output format (xliff, json)")
	fs.BoolVar(&pretty, "pretty", pretty, "pretty print the output files")
	fs.StringVar(&x.fileName, "in", "", "infile")
	fs.IntVar(&x.skipRows, "skip-rows", 0, "number of rows to skip")
	fs.IntVar(&x.sheetNumber, "sheet", 1, "number of the sheet containing the translations")
	fs.IntVar(&x.keyColumn, "key-column", 0, "column holding the key / msgid")
	fs.IntVar(&x.sourceColumn, "source-col", -1, "column holding the source for the translation")
	fs.IntVar(&x.noteColumn, "note-col", -1, "column holding notes (0 - not used)")
	fs.IntVar(&x.targetColumn, "target-col", -1, "column holding the target translation")
	fs.StringVar(&x.sourceLang, "source-lang", "en", "source language")
	fs.StringVar(&x.targetLang, "target-lang", "en", "target language")
	fs.StringVar(&x.destDir, "dir", "", "output directory")

	err := fs.Parse(args)
	if err != nil {
		return err
	}

	if x.destDir == "" {
		x.destDir, _ = os.Getwd()
	}
	if err = os.MkdirAll(x.destDir, 0777); err != nil {
		return err
	}

	switch destType {
	case "json":
		x.exporter = &xlsxJsonExporter{pretty: pretty}
	case "xliff":
		x.exporter = &xlsxXliffExporter{pretty: pretty}
	default:
		return fmt.Errorf("unsupported 'type': %q", destType)
	}

	return nil
}

func (conv *xlsxConverter) Prepare() error {
	return nil
}

func (conv *xlsxConverter) Convert(w io.Writer) error {

	var xlFile, err = xlsx.OpenFile(conv.fileName)
	if err != nil {
		return err
	}

	var sheet *xlsx.Sheet

	for s := range xlFile.Sheets {
		if s == (conv.sheetNumber - 1) {
			sheet = xlFile.Sheets[s]
			break
		}
	}

	if sheet == nil {
		return fmt.Errorf("did not find sheet %d in %s\n",
			conv.sheetNumber, conv.fileName)
	}

	keyRow, keyCol := conv.detectBounds(sheet)
	bodyRow := keyRow + 1

	srcCol := conv.sourceColumn
	if srcCol == -1 {
		// key | note | src | target-1 | target-2
		srcCol = keyCol + XLSX_SOURCE_COLUMN // TODO: detect "source" if not set
	}

	// the target column defines the column where the translated languages
	// start. if not defined, it defaults to the source column. why?
	// because that way the "initial" language also gets an export, either
	// to .xliff (which can then be handed to the translator team) or to
	// to .json and pretend it's already translated.
	targetCol := conv.targetColumn
	if targetCol == -1 {
		targetCol = srcCol // TODO: detect "target" automatically
	}

	head := sheet.Rows[keyRow].Cells

	//for x := range head[keyCol:] {
	//	fmt.Println(x, x+keyCol, head[x].String(), len(head), srcCol)
	//}

	srcLang := conv.langFromBCP47(head[srcCol].String())
	rows := sheet.Rows

	for x := targetCol; x < len(rows[keyRow].Cells); x++ {

		bcp47 := head[x].String()
		lang := conv.langFromBCP47(bcp47)
		units := make([]xlsxTransUnit, 0, len(rows)-keyRow)

		for y := bodyRow; y < len(rows); y++ {
			row := rows[y]
			if (keyCol >= len(row.Cells)) || row.Cells[keyCol].String() == "" {
				continue
			}
			if (srcCol >= len(row.Cells)) || row.Cells[srcCol].String() == "" {
				continue
			}
			if (x >= len(row.Cells)) || row.Cells[x].String() == "" {
				continue
			}
			unit := conv.rowToTransUnit(y, keyCol, srcCol, x, srcLang, lang, rows)
			units = append(units, unit)
		}
		name := sheet.Name + "-" + bcp47
		conv.exportUnits(conv.destDir, name, conv.exporter, units)
	}

	return nil
}

// extracts a row of the following form
//   key | comment | note | source | target-1 | target-2 | ...
// into a xlsxTransUnit
func (conv *xlsxConverter) rowToTransUnit(row, keyCol, sourceCol, targetCol int, sourceLang, targetLang string, rows []*xlsx.Row) xlsxTransUnit {
	unit := xlsxTransUnit{
		Id:         rows[row].Cells[keyCol].String(),
		Source:     rows[row].Cells[sourceCol].String(),
		SourceLang: sourceLang,
		Target:     rows[row].Cells[targetCol].String(),
		TargetLang: targetLang,
	}
	return unit
}

func (conv *xlsxConverter) exportUnits(dir, name string, x xlsxExporter, entries []xlsxTransUnit) {
	if err := x.Open(dir, name); err != nil {
		log.Printf("err: opening export file %q: %s", x.Filename(), err)
		return
	}
	defer x.Close()

	if err := x.Write(entries); err != nil {
		log.Printf("err: write entries to %q: %s", x.Filename(), err)
		return
	}

	log.Printf("written %d entries to %q: ok.", len(entries), x.Filename())
}

// finds the key-column after x.skipRows, where the content starts
// aka "the header".
func (x *xlsxConverter) detectBounds(sheet *xlsx.Sheet) (int, int) {

	var cell *xlsx.Cell
	i, j := 0, 0

	for i = range sheet.Rows {
		if i < (x.skipRows) {
			continue
		}

		for j, cell = range sheet.Rows[i].Cells {
			if key := cell.String(); key != "" {
				return i, j
			}
		}
	}

	return -1, -1
}

// a very very simplified way of extracting the language part
// from a BCP47 formed "tag".
//
// "en-US" -> "en"
// "en-US strange comment" -> "en"
// "well i dont know" -> "well i dont know"
func (x *xlsxConverter) langFromBCP47(bcp47 string) string {
	parts := strings.Split(bcp47, "-")
	return strings.ToLower(parts[0])
}
<|endoftext|>


<commit_before>package signals

import (
	"math/rand"
	"encoding/gob"
)

func init() {
	gob.Register(Noise{})
}

type Noise struct {
	generator rand.Rand
}

func NewNoise() Noise {
	return Noise{*rand.New(rand.NewSource(rand.Int63()))}  // give each noise, very probably, a different generator source
}

// Noise is a deterministic random level, white.
// deterministic becasue has the same value at the same time for the same Noise, but random otherwise.
func (s Noise) Level(t interval) (l level) {
	rand.Seed(int64(t))  // default generator set to the same seed for the same time
	s.generator.Seed(int64(rand.Int63()))   // Noise sets its generator's seed to a random number from default generator, which is the same at a given t, so the same random numbers generated from it, for the same t, but different for different Noises.
	l+=level(s.generator.Int63())
	l-=level(s.generator.Int63())
	return
}

<commit_msg>noise comment<commit_after>package signals

import (
	"math/rand"
	"encoding/gob"
)

func init() {
	gob.Register(Noise{})
}

// Noise is a deterministic random level, white.
// deterministic becasue has the same value at the same time for the same Noise, but random otherwise.
type Noise struct {
	generator rand.Rand
}

func NewNoise() Noise {
	return Noise{*rand.New(rand.NewSource(rand.Int63()))}  // give each noise, very probably, a different generator source
}

func (s Noise) Level(t interval) (l level) {
	rand.Seed(int64(t))  // default generator set to the same seed for the same time
	s.generator.Seed(int64(rand.Int63()))   // Noise sets its generator's seed to a random number from default generator, which is the same at a given t, so the same random numbers generated from it, for the same t, but different for different Noises.
	l+=level(s.generator.Int63())
	l-=level(s.generator.Int63())
	return
}

<|endoftext|>


<commit_before>package xsrf

import (
	"fmt"
	"html/template"
	"net/http"
	"time"

	"github.com/go-xweb/uuid"
	"github.com/lunny/tango"
)

const (
	XSRF_TAG string = "_xsrf"
)

func XsrfName() string {
	return XSRF_TAG
}

type Xsrfer interface {
	CheckXsrf() bool
}

type NoCheck struct {
}

func (NoCheck) CheckXsrf() bool {
	return false
}

type XsrfChecker interface {
	SetXsrfValue(string)
	SetXsrfFormHtml(template.HTML)
}

type Checker struct {
	XsrfValue string
	XsrfFormHtml template.HTML
}

func (Checker) CheckXsrf() bool {
	return true
}

func (c *Checker) SetXsrfValue(v string) {
	c.XsrfValue = v
}

func (c *Checker) SetXsrfFormHtml(t template.HTML) {
	c.XsrfFormHtml = t
}

type Xsrf struct {
	timeout time.Duration
}

func New(timeout time.Duration) *Xsrf {
	return &Xsrf{
		timeout: timeout,
	}
}

// NewCookie is a helper method that returns a new http.Cookie object.
// Duration is specified in seconds. If the duration is zero, the cookie is permanent.
// This can be used in conjunction with ctx.SetCookie.
func newCookie(name string, value string, age int64) *http.Cookie {
	var utctime time.Time
	if age == 0 {
		// 2^31 - 1 seconds (roughly 2038)
		utctime = time.Unix(2147483647, 0)
	} else {
		utctime = time.Unix(time.Now().Unix()+age, 0)
	}
	return &http.Cookie{Name: name, Value: value, Expires: utctime}
}

func (xsrf *Xsrf) Handle(ctx *tango.Context) {
	var action interface{}
	if action = ctx.Action(); action == nil {
		ctx.Next()
		return
	}

	// if action implements check xsrf option and ask not check then return
	if checker, ok := action.(Xsrfer); ok && !checker.CheckXsrf() {
		ctx.Next()
		return
	}

	if ctx.Req().Method == "GET" {
		var val string = ""
		cookie, err := ctx.Req().Cookie(XSRF_TAG)
		if err != nil {
			val = uuid.NewRandom().String()
			cookie = newCookie(XSRF_TAG, val, int64(xsrf.timeout))
			ctx.SetHeader("Set-Cookie", cookie.String())
		} else {
			val = cookie.Value
		}

		if c, ok := action.(XsrfChecker); ok {
			c.SetXsrfValue(val)
			c.SetXsrfFormHtml(template.HTML(fmt.Sprintf(`<input type="hidden" name="%v" value="%v" />`,
				XSRF_TAG, val)))
		}
	} else if ctx.Req().Method == "POST" {
		res, err := ctx.Req().Cookie(XSRF_TAG)
		formVals := ctx.Req().Form[XSRF_TAG]
		var formVal string
		if len(formVals) > 0 {
			formVal = formVals[0]
		}
		if err != nil || res.Value == "" || res.Value != formVal {
			ctx.WriteHeader(http.StatusInternalServerError)
			ctx.Write([]byte("xsrf token error."))
			return
		}
	}

	ctx.Next()
}
<commit_msg>bug fixed<commit_after>package xsrf

import (
	"fmt"
	"html/template"
	"net/http"
	"time"

	"github.com/go-xweb/uuid"
	"github.com/lunny/tango"
)

const (
	XSRF_TAG string = "_xsrf"
)

func XsrfName() string {
	return XSRF_TAG
}

type Xsrfer interface {
	CheckXsrf() bool
}

type NoCheck struct {
}

func (NoCheck) CheckXsrf() bool {
	return false
}

type XsrfChecker interface {
	SetXsrfValue(string)
	SetXsrfFormHtml(template.HTML)
}

type Checker struct {
	XsrfValue string
	XsrfFormHtml template.HTML
}

func (Checker) CheckXsrf() bool {
	return true
}

func (c *Checker) SetXsrfValue(v string) {
	c.XsrfValue = v
}

func (c *Checker) SetXsrfFormHtml(t template.HTML) {
	c.XsrfFormHtml = t
}

type Xsrf struct {
	timeout time.Duration
}

func New(timeout time.Duration) *Xsrf {
	return &Xsrf{
		timeout: timeout,
	}
}

// NewCookie is a helper method that returns a new http.Cookie object.
// Duration is specified in seconds. If the duration is zero, the cookie is permanent.
// This can be used in conjunction with ctx.SetCookie.
func newCookie(name string, value string, age int64) *http.Cookie {
	var utctime time.Time
	if age == 0 {
		// 2^31 - 1 seconds (roughly 2038)
		utctime = time.Unix(2147483647, 0)
	} else {
		utctime = time.Unix(time.Now().Unix()+age, 0)
	}
	return &http.Cookie{Name: name, Value: value, Expires: utctime}
}

func (xsrf *Xsrf) Handle(ctx *tango.Context) {
	var action interface{}
	if action = ctx.Action(); action == nil {
		ctx.Next()
		return
	}

	// if action implements check xsrf option and ask not check then return
	if checker, ok := action.(Xsrfer); ok && !checker.CheckXsrf() {
		ctx.Next()
		return
	}

	if ctx.Req().Method == "GET" {
		var val string = ""
		cookie, err := ctx.Req().Cookie(XSRF_TAG)
		if err != nil {
			val = uuid.NewRandom().String()
			cookie = newCookie(XSRF_TAG, val, int64(xsrf.timeout))
			ctx.Header().Set("Set-Cookie", cookie.String())
		} else {
			val = cookie.Value
		}

		if c, ok := action.(XsrfChecker); ok {
			c.SetXsrfValue(val)
			c.SetXsrfFormHtml(template.HTML(fmt.Sprintf(`<input type="hidden" name="%v" value="%v" />`,
				XSRF_TAG, val)))
		}
	} else if ctx.Req().Method == "POST" {
		res, err := ctx.Req().Cookie(XSRF_TAG)
		formVals := ctx.Req().Form[XSRF_TAG]
		var formVal string
		if len(formVals) > 0 {
			formVal = formVals[0]
		}
		if err != nil || res.Value == "" || res.Value != formVal {
			ctx.WriteHeader(http.StatusInternalServerError)
			ctx.Write([]byte("xsrf token error."))
			return
		}
	}

	ctx.Next()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
	"bytes"

	"github.com/JustinBeckwith/go-yelp/yelp"
	"github.com/guregu/null"
	"github.com/line/line-bot-sdk-go/linebot"
)

var o *yelp.AuthOptions

type UrlShortener struct {
	ShortUrl    string
	OriginalUrl string
}

func yelp_init() {
	rand.Seed(time.Now().UnixNano())

	// check environment variables
	o = &yelp.AuthOptions{
		ConsumerKey:       os.Getenv("CONSUMER_KEY"),
		ConsumerSecret:    os.Getenv("CONSUMER_SECRET"),
		AccessToken:       os.Getenv("ACCESS_TOKEN"),
		AccessTokenSecret: os.Getenv("ACCESS_TOKEN_SECRET"),
	}

	if o.ConsumerKey == "" || o.ConsumerSecret == "" || o.AccessToken == "" || o.AccessTokenSecret == "" {
		log.Fatal("Wrong environment setting about yelp-api-keys")
	}
}

func yelp_parse(bot *linebot.Client, token string, loc *linebot.LocationMessage, food string) {
	var err error

	// create a new yelp client with the auth keys
	client := yelp.New(o, nil)

	if loc == nil {
		// make a simple query for food and location
		results, err := client.DoSimpleSearch(food, "台北市大安區")
		if err == nil {
			log.Println("loc nil")
			yelp_parse_result(bot, token, results)
		}
	} else {
		// Build an advanced set of search criteria that include
		// general options, and coordinate options.
		s := yelp.SearchOptions{
			GeneralOptions: &yelp.GeneralOptions{
				Term: food,
			},
			Location: loc.Address,
			CoordinateOptions: &yelp.CoordinateOptions{
				Latitude:  null.FloatFrom(loc.Latitude),
				Longitude: null.FloatFrom(loc.Longitude),
			},
		}

		// Perform the search using the search options
		results, err := client.DoSearch(s)
		if err == nil {
			log.Println(fmt.Sprintf("loc:(%f, %f)", loc.Latitude, loc.Longitude))
			yelp_parse_result(bot, token, results)
		}
	}
	if err != nil {
		log.Println(err)
		_, err = bot.ReplyMessage(token, linebot.NewTextMessage("查無資料！")).Do()
	}
}

func yelp_parse_result(bot *linebot.Client, token string, results yelp.SearchResult) {
	var err error
//	var msgs []linebot.Message
	var outmsg bytes.Buffer

	for i := 0; i < 3; i++ {
		//i := 0
		//if results.Total >= 20 {
		//	i = rand.Intn(20)
		//} else if results.Total >= 10 {
		//	i = rand.Intn(10)
		//} else if results.Total > j {
		//	i = j
		//} else if results.Total <= j && results.Total != 0 {
		if ( results.Total <= i) {
			_, err = bot.ReplyMessage(token, linebot.NewTextMessage("已無更多資料！")).Do()
			break
		}
		urlOrig := UrlShortener{}
		urlOrig.short(results.Businesses[i].MobileURL)
		address := strings.Join(results.Businesses[i].Location.DisplayAddress, ",")
		//var largeImageURL = strings.Replace(results.Businesses[i].ImageURL, "ms.jpg", "l.jpg", 1)

		outmsg.WriteString("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n地址："+address+"\n更多資訊："+urlOrig.ShortUrl+"\n")

//		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL), linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl), linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude)) ).Do()
//		msgs = append(msgs, linebot.NewImageMessage(largeImageURL, largeImageURL))
//		msgs = append(msgs, linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl))
//		msgs = append(msgs, linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude)))
//		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL)).Do()
//		_, err = bot.ReplyMessage(token, linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl)).Do()
//		_, err = bot.ReplyMessage(token, linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude))).Do()
	}

	if outmsg.String() != "" {
		_, err = bot.ReplyMessage(token, linebot.NewTextMessage(outmsg.String())).Do()
	}
	if err != nil {
		log.Println(err)
	}
}

func getResponseData(urlOrig string) string {
	response, err := http.Get(urlOrig)
	if err != nil {
		log.Println(err)
	}
	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	return string(contents)
}

func isGdShortener(urlOrig string) (string, string) {
	escapedUrl := url.QueryEscape(urlOrig)
	isGdUrl := fmt.Sprintf("http://is.gd/create.php?url=%s&format=simple", escapedUrl)
	return getResponseData(isGdUrl), urlOrig
}

func (u *UrlShortener) short(urlOrig string) *UrlShortener {
	shortUrl, originalUrl := isGdShortener(urlOrig)
	u.ShortUrl = shortUrl
	u.OriginalUrl = originalUrl
	return u
}
<commit_msg>test location<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
	"bytes"

	"github.com/JustinBeckwith/go-yelp/yelp"
	"github.com/guregu/null"
	"github.com/line/line-bot-sdk-go/linebot"
)

var o *yelp.AuthOptions

type UrlShortener struct {
	ShortUrl    string
	OriginalUrl string
}

func yelp_init() {
	rand.Seed(time.Now().UnixNano())

	// check environment variables
	o = &yelp.AuthOptions{
		ConsumerKey:       os.Getenv("CONSUMER_KEY"),
		ConsumerSecret:    os.Getenv("CONSUMER_SECRET"),
		AccessToken:       os.Getenv("ACCESS_TOKEN"),
		AccessTokenSecret: os.Getenv("ACCESS_TOKEN_SECRET"),
	}

	if o.ConsumerKey == "" || o.ConsumerSecret == "" || o.AccessToken == "" || o.AccessTokenSecret == "" {
		log.Fatal("Wrong environment setting about yelp-api-keys")
	}
}

func yelp_parse(bot *linebot.Client, token string, loc *linebot.LocationMessage, food string) {
	var err error

	// create a new yelp client with the auth keys
	client := yelp.New(o, nil)

	if loc == nil {
		// make a simple query for food and location
		results, err := client.DoSimpleSearch(food, "台北市大安區")
		if err == nil {
			log.Println("loc nil")
			yelp_parse_result(bot, token, results)
		}
	} else {
		// Build an advanced set of search criteria that include
		// general options, and coordinate options.
		s := yelp.SearchOptions{
			GeneralOptions: &yelp.GeneralOptions{
				Term: food,
			},
			LocationOptions: &yelp.CoordinateOptions{
				Location: loc.Address,
			},
			CoordinateOptions: &yelp.CoordinateOptions{
				Latitude:  null.FloatFrom(loc.Latitude),
				Longitude: null.FloatFrom(loc.Longitude),
			},
			LocaleOptions: &yelp.CoordinateOptions{
				lang: "zh",
			},
		}

		// Perform the search using the search options
		results, err := client.DoSearch(s)
		if err == nil {
			log.Println(fmt.Sprintf("loc:(%f, %f)", loc.Latitude, loc.Longitude))
			yelp_parse_result(bot, token, results)
		}
	}
	if err != nil {
		log.Println(err)
		_, err = bot.ReplyMessage(token, linebot.NewTextMessage("查無資料！")).Do()
	}
}

func yelp_parse_result(bot *linebot.Client, token string, results yelp.SearchResult) {
	var err error
//	var msgs []linebot.Message
	var outmsg bytes.Buffer

	for i := 0; i < 3; i++ {
		//i := 0
		//if results.Total >= 20 {
		//	i = rand.Intn(20)
		//} else if results.Total >= 10 {
		//	i = rand.Intn(10)
		//} else if results.Total > j {
		//	i = j
		//} else if results.Total <= j && results.Total != 0 {
		if ( results.Total <= i) {
			_, err = bot.ReplyMessage(token, linebot.NewTextMessage("已無更多資料！")).Do()
			break
		}
		urlOrig := UrlShortener{}
		urlOrig.short(results.Businesses[i].MobileURL)
		address := strings.Join(results.Businesses[i].Location.DisplayAddress, ",")
		//var largeImageURL = strings.Replace(results.Businesses[i].ImageURL, "ms.jpg", "l.jpg", 1)

		outmsg.WriteString("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n地址："+address+"\n更多資訊："+urlOrig.ShortUrl+"\n")

//		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL), linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl), linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude)) ).Do()
//		msgs = append(msgs, linebot.NewImageMessage(largeImageURL, largeImageURL))
//		msgs = append(msgs, linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl))
//		msgs = append(msgs, linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude)))
//		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL)).Do()
//		_, err = bot.ReplyMessage(token, linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl)).Do()
//		_, err = bot.ReplyMessage(token, linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude))).Do()
	}

	if outmsg.String() != "" {
		_, err = bot.ReplyMessage(token, linebot.NewTextMessage(outmsg.String())).Do()
	}
	if err != nil {
		log.Println(err)
	}
}

func getResponseData(urlOrig string) string {
	response, err := http.Get(urlOrig)
	if err != nil {
		log.Println(err)
	}
	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	return string(contents)
}

func isGdShortener(urlOrig string) (string, string) {
	escapedUrl := url.QueryEscape(urlOrig)
	isGdUrl := fmt.Sprintf("http://is.gd/create.php?url=%s&format=simple", escapedUrl)
	return getResponseData(isGdUrl), urlOrig
}

func (u *UrlShortener) short(urlOrig string) *UrlShortener {
	shortUrl, originalUrl := isGdShortener(urlOrig)
	u.ShortUrl = shortUrl
	u.OriginalUrl = originalUrl
	return u
}
<|endoftext|>