2020-11-26 04:18:52 +00:00
|
|
|
/*
|
|
|
|
Pak: Wrapper designed for package managers to unify software management commands between distros
|
|
|
|
Copyright (C) 2020 Arsen Musayelyan
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2021-05-05 21:00:44 +00:00
|
|
|
"github.com/alessio/shellescape"
|
2021-01-04 10:33:29 +00:00
|
|
|
"github.com/rs/zerolog"
|
|
|
|
"github.com/rs/zerolog/log"
|
2021-01-04 03:41:26 +00:00
|
|
|
flag "github.com/spf13/pflag"
|
2020-11-26 04:18:52 +00:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"os/user"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2021-01-04 10:33:29 +00:00
|
|
|
var Log = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
2020-11-26 04:18:52 +00:00
|
|
|
|
2021-01-04 10:33:29 +00:00
|
|
|
func main() {
|
2020-11-26 04:18:52 +00:00
|
|
|
|
2021-01-04 10:33:29 +00:00
|
|
|
// Create help flag
|
2021-05-06 17:50:27 +00:00
|
|
|
helpFlagGiven := flag.BoolP("help", "h", false, "Show help screen")
|
2021-01-04 10:33:29 +00:00
|
|
|
// Create package manager override flag
|
2021-05-06 17:50:27 +00:00
|
|
|
packageManagerOverride := flag.StringP("package-manager", "p", os.Getenv("PAK_MGR_OVERRIDE"), "Override package manager wrapped by pak")
|
2020-11-30 22:25:31 +00:00
|
|
|
// Parse arguments for flags
|
|
|
|
flag.Parse()
|
|
|
|
|
2021-01-04 10:33:29 +00:00
|
|
|
// Check which user is running command
|
|
|
|
currentUser, err := user.Current()
|
|
|
|
if err != nil {
|
|
|
|
Log.Fatal().Err(err).Msg("Error getting current user")
|
|
|
|
}
|
|
|
|
// If running as root
|
|
|
|
if strings.Contains(currentUser.Username, "root") {
|
|
|
|
// Print warning message
|
|
|
|
Log.Warn().Msg("Running as root may cause extraneous root invocation")
|
2020-11-26 04:18:52 +00:00
|
|
|
}
|
|
|
|
|
2021-01-04 03:41:26 +00:00
|
|
|
// Get arguments without flags
|
|
|
|
args := flag.Args()
|
2020-11-30 22:25:31 +00:00
|
|
|
|
2020-11-28 02:26:42 +00:00
|
|
|
// Define variables for config file location, and override state boolean
|
|
|
|
var isOverridden bool
|
2021-01-04 03:41:26 +00:00
|
|
|
|
|
|
|
// Read /etc/pak.toml into new Config{}
|
|
|
|
config := NewConfig("/etc/pak.toml")
|
|
|
|
|
2020-11-28 02:26:42 +00:00
|
|
|
// If override is set
|
2021-05-06 17:50:27 +00:00
|
|
|
if *packageManagerOverride != "" {
|
2021-01-04 03:41:26 +00:00
|
|
|
// Set active package manager to override
|
2021-05-06 17:50:27 +00:00
|
|
|
config.ActiveManager = *packageManagerOverride
|
2020-11-28 02:26:42 +00:00
|
|
|
// Set override state to true
|
|
|
|
isOverridden = true
|
|
|
|
} else {
|
|
|
|
// Set override state to false
|
|
|
|
isOverridden = false
|
|
|
|
}
|
|
|
|
|
2020-11-26 04:18:52 +00:00
|
|
|
// Parse list of commands in config line 2 and set to variable as array
|
2021-01-04 03:41:26 +00:00
|
|
|
commands := config.Managers[config.ActiveManager].Commands
|
2020-11-26 04:18:52 +00:00
|
|
|
//fmt.Println(commands) //DEBUG
|
|
|
|
|
|
|
|
// Set the root option in config line 3 to a variable
|
2021-01-04 03:41:26 +00:00
|
|
|
useRoot := config.Managers[config.ActiveManager].UseRoot
|
2020-11-26 04:18:52 +00:00
|
|
|
//fmt.Println(useRoot) //DEBUG
|
|
|
|
|
|
|
|
// Set command to use to invoke root at config line 4 to a variable
|
2021-01-04 03:41:26 +00:00
|
|
|
rootCommand := config.RootCommand
|
2020-11-26 04:18:52 +00:00
|
|
|
//fmt.Println(rootCommand) //DEBUG
|
|
|
|
|
|
|
|
// Parse list of shortcuts in config and line 5 set to variable as an array
|
2021-01-04 03:41:26 +00:00
|
|
|
shortcuts := config.Managers[config.ActiveManager].Shortcuts
|
2020-11-26 04:18:52 +00:00
|
|
|
//fmt.Println(shortcuts) //DEBUG
|
|
|
|
|
|
|
|
// Create similar to slice to put all matched commands into
|
2021-01-04 10:33:29 +00:00
|
|
|
similarTo := []string{}
|
2020-11-26 04:18:52 +00:00
|
|
|
|
|
|
|
// Displays help message if no arguments provided or -h/--help is passed
|
2021-05-06 17:50:27 +00:00
|
|
|
if len(args) == 0 || *helpFlagGiven || Contains(args, "help") {
|
|
|
|
printHelpMessage(config.ActiveManager, rootCommand, commands, shortcuts, useRoot, isOverridden)
|
2020-11-26 04:18:52 +00:00
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create distance slice to store JaroWinkler distance values
|
2021-01-04 03:41:26 +00:00
|
|
|
distance := map[string]float64{}
|
2020-11-26 04:18:52 +00:00
|
|
|
// Appends JaroWinkler distance between each available command and the first argument to an array
|
2021-01-04 03:41:26 +00:00
|
|
|
for command := range commands {
|
|
|
|
distance[command] = JaroWinkler(command, args[0], 1, 0)
|
2020-11-26 04:18:52 +00:00
|
|
|
}
|
|
|
|
|
2020-11-29 00:38:45 +00:00
|
|
|
// Deals with shortcuts
|
2021-01-04 03:41:26 +00:00
|
|
|
for shortcut, mapping := range shortcuts {
|
2020-11-29 00:38:45 +00:00
|
|
|
// If the first argument is a shortcut and similarTo does not already contain its mapping, append it
|
2021-01-04 03:41:26 +00:00
|
|
|
if args[0] == shortcut && !Contains(similarTo, mapping) {
|
|
|
|
similarTo = append(similarTo, mapping)
|
2020-11-29 00:38:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-26 04:18:52 +00:00
|
|
|
// Compares each distance to the max of the distance slice and appends the closest command to similarTo
|
2021-01-04 03:41:26 +00:00
|
|
|
for command, cmdDist := range distance {
|
2020-11-26 04:18:52 +00:00
|
|
|
// If current element is the closest to the first argument
|
2021-01-04 03:41:26 +00:00
|
|
|
if cmdDist == Max(GetValuesDist(distance)) {
|
2020-11-26 04:18:52 +00:00
|
|
|
// Append command at same index as distance to similarTo
|
2021-01-04 03:41:26 +00:00
|
|
|
similarTo = append(similarTo, commands[command])
|
2020-11-26 04:18:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If similarTo is still empty, log it fatally as something is wrong with the config or the code
|
2021-01-04 03:41:26 +00:00
|
|
|
if len(similarTo) == 0 {
|
2021-01-04 10:33:29 +00:00
|
|
|
Log.Fatal().Msg("This command does not match any known commands or shortcuts")
|
2021-01-04 03:41:26 +00:00
|
|
|
}
|
2021-05-06 17:50:27 +00:00
|
|
|
var overriddenStr string
|
|
|
|
if isOverridden {
|
|
|
|
overriddenStr = "(overridden)"
|
|
|
|
} else {
|
|
|
|
overriddenStr = ""
|
2021-01-04 03:41:26 +00:00
|
|
|
}
|
2020-11-26 04:18:52 +00:00
|
|
|
// Print text showing command being run and package manager being used
|
2021-05-06 17:50:27 +00:00
|
|
|
fmt.Println("Running:", strings.Title(GetKey(commands, similarTo[0])), "using", strings.Title(config.ActiveManager), overriddenStr)
|
2020-11-26 04:18:52 +00:00
|
|
|
// Run package manager with the proper arguments passed if more than one argument exists
|
|
|
|
var cmdArr []string
|
|
|
|
// If root is to be used, append it to cmdArr
|
2021-01-04 03:41:26 +00:00
|
|
|
if useRoot {
|
|
|
|
cmdArr = append(cmdArr, rootCommand)
|
|
|
|
}
|
2021-01-09 09:14:40 +00:00
|
|
|
// If command to be run has a prefix of "cmd:"
|
|
|
|
if strings.HasPrefix(similarTo[0], "cmd:") {
|
|
|
|
// Append the command to the slice without the prefix
|
|
|
|
cmdArr = append(cmdArr, strings.TrimPrefix(similarTo[0], "cmd:"))
|
|
|
|
} else {
|
|
|
|
// Otherwise, append all commands and arguments for the package manager to slice
|
|
|
|
cmdArr = append(cmdArr, config.ActiveManager, similarTo[0])
|
|
|
|
}
|
2020-11-26 04:18:52 +00:00
|
|
|
// If greater than 2 arguments, append them to cmdArr
|
2021-01-04 03:41:26 +00:00
|
|
|
if len(args) >= 2 {
|
2021-05-05 21:06:18 +00:00
|
|
|
cmdArr = append(cmdArr, shellescape.QuoteCommand(args[1:]))
|
2021-01-04 03:41:26 +00:00
|
|
|
}
|
2020-11-26 04:18:52 +00:00
|
|
|
// Create space separated string from cmdArr
|
2021-05-05 21:06:18 +00:00
|
|
|
cmdStr := strings.Join(cmdArr, " ")
|
2020-11-26 04:18:52 +00:00
|
|
|
// Instantiate exec.Command object with command sh, flag -c, and cmdStr
|
|
|
|
command := exec.Command("sh", "-c", cmdStr)
|
|
|
|
// Set standard outputs for command
|
|
|
|
command.Stdout = os.Stdout
|
|
|
|
command.Stdin = os.Stdin
|
|
|
|
command.Stderr = os.Stderr
|
|
|
|
// Run command
|
|
|
|
err = command.Run()
|
|
|
|
// If command returned an error, log fatally with explanation
|
|
|
|
if err != nil {
|
2021-01-04 10:33:29 +00:00
|
|
|
Log.Fatal().Err(err).Msg("Error received from child process")
|
2020-11-26 04:18:52 +00:00
|
|
|
}
|
2021-01-04 03:41:26 +00:00
|
|
|
}
|