// ListBookmarks prints the list of bookmarks with their content func ListBookmarks(env *environments.Environment) { bookmarksFileInfos, err := env.GetBookmarksFileInfos() if err != nil { utils.Logger.Panic(err) } maxLength := 1 for _, fileInfo := range bookmarksFileInfos { name := fileInfo.Name() if len(name) > maxLength { maxLength = len(name) } } template := "%-" + strconv.Itoa(maxLength) + "s %s\n" utils.Logger.WithFields(logrus.Fields{ "template": template, }).Info("Calculated template to print") for _, fileInfo := range bookmarksFileInfos { fileName := fileInfo.Name() content, err := ioutil.ReadFile(env.GetBookmarkFileName(fileName)) if err != nil { utils.Logger.WithFields(logrus.Fields{ "filename": fileName, "error": err, }).Warn("Cannot read a content of the file so skip") continue } fmt.Printf(template, fileName, string(content)) } }
func processHistories(env *environments.Environment) (resultChan chan bool, consumeChan chan *HistoryEntry) { resultChan = make(chan bool, 1) consumeChan = make(chan *HistoryEntry, historyEventsCapacity) go func() { entries := make(map[string]bool) files, err := env.GetTracesFileInfos() if err != nil { utils.Logger.WithFields(logrus.Fields{ "error": err, }).Warn("Error on traces directory listing") resultChan <- true return } for _, file := range files { entries[file.Name()] = true } utils.Logger.WithField("filenames", entries).Info("Parsed filenames") for entry := range consumeChan { if _, found := entries[entry.GetTraceName()]; found { entry.hasHistory = true } } resultChan <- true }() return }
// ListTrace implements l command (list trace). func ListTrace(argument string, env *environments.Environment) { number, err := strconv.Atoi(argument) if err != nil || number < 0 { utils.Logger.Panicf("Cannot convert argument to a command number: %s", argument) } commands, err := historyentries.GetCommands(historyentries.GetCommandsPrecise, nil, env, number) if err != nil { utils.Logger.Panic(err) } command := commands.Result().(historyentries.HistoryEntry) hashFilename := command.GetTraceName() filename := env.GetTraceFileName(hashFilename) if _, err := os.Stat(filename); os.IsNotExist(err) { utils.Logger.Panicf("Output for %s is not exist", argument) } file := utils.Open(filename) defer file.Close() ungzippedFile, err := gzip.NewReader(file) if err != nil { utils.Logger.Panic(err) } defer ungzippedFile.Close() scanner := bufio.NewScanner(ungzippedFile) for scanner.Scan() { os.Stdout.WriteString(scanner.Text()) os.Stdout.WriteString("\n") } if err := scanner.Err(); err != nil { utils.Logger.Panic(err) } }
// ExecuteBookmark executes command by its bookmark name. func ExecuteBookmark(name string, interactive bool, pseudoTTY bool, env *environments.Environment) { content, err := ioutil.ReadFile(env.GetBookmarkFileName(name)) if err != nil { utils.Logger.Panic("Unknown bookmark") } execute(string(content), env.Shell, interactive, pseudoTTY) }
// ToString converts history entry to the string representation according to the environment setting. func (he HistoryEntry) ToString(env *environments.Environment) string { timestamp := "" if formattedTimestamp := env.FormatTimeStamp(he.timestamp); formattedTimestamp != "" { timestamp = " (" + formattedTimestamp + ")" } history := markHasNoHistory if he.hasHistory { history = markHasHistory } return fmt.Sprintf("!%-5d%s %c %s", he.number, timestamp, history, he.command) }
// GetCommands returns a keeper for the commands based on given mode and regular expression. // varargs is the auxiliary list of numbers which makes sense in the context of GetCommandsMode setting // only. func GetCommands(mode GetCommandsMode, filter *utils.Regexp, env *environments.Environment, varargs ...int) (commands Keeper, err error) { keeper := getKeeper(mode, varargs...) resultChan, consumeChan := processHistories(env) parser := getParser(env) histFileName, err := env.GetHistFileName() if err != nil { return } file := utils.Open(histFileName) defer file.Close() scanner := bufio.NewScanner(file) commands, err = parser(keeper, scanner, filter, consumeChan) if err == nil { <-resultChan return commands, nil } return }
// Tee implements t (trace, tee) command. func Tee(input string, interactive bool, pseudoTTY bool, env *environments.Environment) { output, err := ioutil.TempFile(env.TmpDir, "ah") if err != nil { utils.Logger.Panic("Cannot create temporary file") } bufferedOutput := bufio.NewWriter(output) gzippedWrapper := utils.NewSynchronizedWriter(gzip.NewWriter(bufferedOutput)) combinedStdout := io.MultiWriter(os.Stdout, gzippedWrapper) combinedStderr := io.MultiWriter(os.Stderr, gzippedWrapper) var commandError *exec.ExitError defer func() { // defer here because command may cause a panic but we do not want to lose any output gzippedWrapper.Close() bufferedOutput.Flush() output.Close() if hash, err := getPreciseHash(input, env); err == nil { err = os.Rename(output.Name(), env.GetTraceFileName(hash)) if err != nil { utils.Logger.Errorf("Cannot save trace: %v. Get it here: %s", err, output.Name()) } else { os.Remove(output.Name()) } } else { utils.Logger.Errorf("Error occured on fetching command number: %v", err) } if commandError != nil { os.Exit(utils.GetStatusCode(commandError)) } }() commandError = utils.Exec(input, string(env.Shell), interactive, pseudoTTY, os.Stdin, combinedStdout, combinedStderr) }
// Bookmark implements "b" (bookmark) command. func Bookmark(commandNumber int, bookmarkAs string, env *environments.Environment) { if commandNumber < 0 { utils.Logger.Panic("Command number should be >= 0") } commandsKeeper, err := historyentries.GetCommands(historyentries.GetCommandsAll, nil, env) if err != nil { utils.Logger.Panic(err) } commands := commandsKeeper.Result().([]historyentries.HistoryEntry) if len(commands) <= commandNumber { utils.Logger.Panic("Command number does not exist") } command := commands[commandNumber-1] filename := env.GetBookmarkFileName(bookmarkAs) file, err := os.Create(filename) if err != nil { utils.Logger.Panicf("Cannot create bookmark %s: %v", filename, err) } defer file.Close() file.WriteString(command.GetCommand()) }
// RemoveBookmarks removes the list of bookmarks from the storage. func RemoveBookmarks(bookmarks []string, env *environments.Environment) { for _, bookmark := range bookmarks { utils.RemoveWithLogging(env.GetBookmarkFileName(bookmark)) } }
func main() { defer func() { if exc := recover(); exc != nil { utils.Logger.Fatal(exc) } }() arguments, err := docopt.Parse(docoptOptions, nil, true, version, false) if err != nil { panic(err) } if arguments["--debug"].(bool) { utils.EnableLogging() } else { utils.DisableLogging() } utils.Logger.WithField("arguments", arguments).Info("Parsed arguments") defaultEnv := environments.MakeDefaultEnvironment() cmdLineEnv := new(environments.Environment) configEnv, err := defaultEnv.ReadFromConfig() if err != nil { utils.Logger.WithField("error", err).Warn("Cannot read config file") } argShell := arguments["--shell"] if argShell != nil { cmdLineEnv.Shell = argShell.(string) } argHistFile := arguments["--histfile"] if argHistFile != nil { cmdLineEnv.HistFile = argHistFile.(string) } argHistTimeFormat := arguments["--histtimeformat"] if argHistTimeFormat != nil { cmdLineEnv.HistTimeFormat = argHistTimeFormat.(string) } argAppDir := arguments["--appdir"] if argAppDir != nil { cmdLineEnv.AppDir = argAppDir.(string) } argTmpDir := arguments["--tmpdir"] if argTmpDir != nil { cmdLineEnv.TmpDir = argTmpDir.(string) } utils.Logger.WithFields(logrus.Fields{ "default": defaultEnv, "config": configEnv, "cmdLineEnv": cmdLineEnv, }).Debug("Environments") env := environments.MergeEnvironments(defaultEnv, configEnv, cmdLineEnv) utils.Logger.WithField("result env", env).Debug("Ready to start") utils.Logger.WithFields(logrus.Fields{ "error": os.MkdirAll(env.TracesDir, 0777), }).Info("Create traces dir") utils.Logger.WithFields(logrus.Fields{ "error": os.MkdirAll(env.BookmarksDir, 0777), }).Info("Create bookmarks dir") utils.Logger.WithFields(logrus.Fields{ "error": os.MkdirAll(env.TmpDir, 0777), }).Info("Create create temporary dir") var exec executor switch { case arguments["t"].(bool): utils.Logger.Info("Execute command 'tee'") exec = executeTee case arguments["s"].(bool): utils.Logger.Info("Execute command 'show'") exec = executeShow case arguments["l"].(bool): utils.Logger.Info("Execute command 'listTrace'") exec = executeListTrace case arguments["b"].(bool): utils.Logger.Info("Execute command 'listTrace'") exec = executeListTrace case arguments["e"].(bool): utils.Logger.Info("Execute command 'execute'") exec = executeExec case arguments["lb"].(bool): utils.Logger.Info("Execute command 'listBookmarks'") exec = executeListBookmarks case arguments["rb"].(bool): utils.Logger.Info("Execute command 'removeBookmarks'") exec = executeRemoveBookmarks case arguments["gt"].(bool) || arguments["gb"].(bool): utils.Logger.Info("Execute command 'gc'") exec = executeGC case arguments["al"].(bool): utils.Logger.Info("Execute command 'al'") exec = executeAl case arguments["ad"].(bool): utils.Logger.Info("Execute command 'ad'") exec = executeAd case arguments["ar"].(bool): utils.Logger.Info("Execute command 'ar'") exec = executeAr case arguments["at"].(bool): utils.Logger.Info("Execute command 'at'") exec = executeAt default: utils.Logger.Panic("Unknown command. Please be more precise") return } exec(arguments, env) }
// GetFormattedTime returns formatted time stamp of the history entry. func (he HistoryEntry) GetFormattedTime(env *environments.Environment) string { return env.FormatTimeStamp(he.timestamp) }