/* 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 . */ package main import ( "fmt" flag "github.com/spf13/pflag" "log" "os" "os/exec" "os/user" "strings" ) func main() { // Check which currentUser is running command currentUser, err := user.Current() if err != nil { log.Fatal(err) } // Create help flags var helpFlagGiven bool flag.BoolVarP(&helpFlagGiven, "help", "h", false, "Show help screen") // Check to make sure root is not being used unless -r/--root specified var rootCheckBypass bool // Create --root and -r flags for root check bypass flag.BoolVarP(&rootCheckBypass, "root", "r", false, "Bypass root check") var packageManagerOverride string flag.StringVarP(&packageManagerOverride, "package-manager", "p", os.Getenv("PAK_MGR_OVERRIDE"), "Override package manager wrapped by pak") // Parse arguments for flags flag.Parse() // If flag not given if !rootCheckBypass { // If current user is root if strings.Contains(currentUser.Username, "root") { // Print warning message and exit fmt.Println("Do not run as root, this program will invoke root for you if selected in config.") fmt.Println("If you would like to bypass this, run this command with -r or --root.") os.Exit(1) } } // Get arguments without flags args := flag.Args() // Define variables for config file location, and override state boolean var isOverridden bool // Read /etc/pak.toml into new Config{} config := NewConfig("/etc/pak.toml") // If override is set if packageManagerOverride != "" { // Set active package manager to override config.ActiveManager = packageManagerOverride // Set override state to true isOverridden = true } else { // Set override state to false isOverridden = false } // Parse list of commands in config line 2 and set to variable as array commands := config.Managers[config.ActiveManager].Commands //fmt.Println(commands) //DEBUG // Set the root option in config line 3 to a variable useRoot := config.Managers[config.ActiveManager].UseRoot //fmt.Println(useRoot) //DEBUG // Set command to use to invoke root at config line 4 to a variable rootCommand := config.RootCommand //fmt.Println(rootCommand) //DEBUG // Parse list of shortcuts in config and line 5 set to variable as an array shortcuts := config.Managers[config.ActiveManager].Shortcuts //fmt.Println(shortcuts) //DEBUG // Create similar to slice to put all matched commands into var similarTo []string // Displays help message if no arguments provided or -h/--help is passed if len(args) == 0 || helpFlagGiven || Contains(args, "help") { printHelpMessage(config.ActiveManager, useRoot, rootCommand, commands, shortcuts, isOverridden) os.Exit(0) } // Create distance slice to store JaroWinkler distance values distance := map[string]float64{} // Appends JaroWinkler distance between each available command and the first argument to an array for command := range commands { distance[command] = JaroWinkler(command, args[0], 1, 0) } // Deals with shortcuts for shortcut, mapping := range shortcuts { // If the first argument is a shortcut and similarTo does not already contain its mapping, append it if args[0] == shortcut && !Contains(similarTo, mapping) { similarTo = append(similarTo, mapping) } } // Compares each distance to the max of the distance slice and appends the closest command to similarTo for command, cmdDist := range distance { // If current element is the closest to the first argument if cmdDist == Max(GetValuesDist(distance)) { // Append command at same index as distance to similarTo similarTo = append(similarTo, commands[command]) } } // If similarTo is still empty, log it fatally as something is wrong with the config or the code if len(similarTo) == 0 { log.Fatalln("This command does not match any known commands or shortcuts") } // Anonymous function to decide whether to print (overridden) printOverridden := func() string { if isOverridden { return "(overridden)" } else { return "" } } // Print text showing command being run and package manager being used fmt.Println("Running:", strings.Title(GetKey(commands, similarTo[0])), "using", strings.Title(config.ActiveManager), printOverridden()) // 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 if useRoot { cmdArr = append(cmdArr, rootCommand) } // Create slice with all commands and arguments for the package manager cmdArr = append(cmdArr, config.ActiveManager, similarTo[0]) // If greater than 2 arguments, append them to cmdArr if len(args) >= 2 { cmdArr = append(cmdArr, strings.Join(args[1:], " ")) } // Create space separated string from cmdArr cmdStr := strings.Join(cmdArr, " ") // 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 { fmt.Println("Error received from child process") log.Fatal(err) } }