change project structure
This commit is contained in:
68
cmd/multini/actions.go
Normal file
68
cmd/multini/actions.go
Normal file
@ -0,0 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"multini/output"
|
||||
"multini/types"
|
||||
)
|
||||
|
||||
func chk() int {
|
||||
var ok bool
|
||||
var err error
|
||||
ok, err = iniCheck(ArgFile)
|
||||
if err != nil {
|
||||
output.Errorln(err)
|
||||
return EXIT_FILE_READ_ERR
|
||||
}
|
||||
if ok {
|
||||
return EXIT_SUCCESS
|
||||
} else {
|
||||
return EXIT_BAD_SYNTAX_ERR
|
||||
}
|
||||
}
|
||||
|
||||
func add(ini *types.Ini) error {
|
||||
if ArgKeyIsSet {
|
||||
return ini.AddKey(ArgSection, ArgKey, ArgValue, ArgReverse)
|
||||
} else {
|
||||
ini.AddSection(ArgSection)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func set(ini *types.Ini) error {
|
||||
if ArgKeyIsSet {
|
||||
return ini.SetKey(ArgSection, ArgKey, ArgValue)
|
||||
} else {
|
||||
ini.SetSection(ArgSection)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func get(ini *types.Ini) error {
|
||||
var err error = nil
|
||||
var res string
|
||||
if ArgValueIsSet {
|
||||
return ini.GetKeyVal(ArgSection, ArgKey, ArgValue)
|
||||
} else if ArgKeyIsSet {
|
||||
res, err = ini.GetKey(ArgSection, ArgKey)
|
||||
} else if ArgSectionIsSet {
|
||||
res, err = ini.GetSection(ArgSection)
|
||||
} else {
|
||||
res = ini.Get()
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
output.Println(res)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func del(ini *types.Ini) error {
|
||||
if ArgValueIsSet {
|
||||
return ini.DelKeyVal(ArgSection, ArgKey, ArgValue)
|
||||
} else if ArgKeyIsSet {
|
||||
return ini.DelKey(ArgSection, ArgKey)
|
||||
} else {
|
||||
return ini.DelSection(ArgSection)
|
||||
}
|
||||
}
|
186
cmd/multini/args.go
Normal file
186
cmd/multini/args.go
Normal file
@ -0,0 +1,186 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"multini/output"
|
||||
"multini/types"
|
||||
|
||||
"github.com/juju/gnuflag"
|
||||
)
|
||||
|
||||
var (
|
||||
ArgInplace bool
|
||||
ArgGet bool
|
||||
ArgSet bool
|
||||
ArgAdd bool
|
||||
ArgDel bool
|
||||
ArgChk bool
|
||||
ArgVerbose bool
|
||||
ArgVersion bool
|
||||
ArgWindows bool
|
||||
ArgUnix bool
|
||||
ArgHelp bool
|
||||
ArgExisting bool
|
||||
ArgReverse bool
|
||||
ArgQuiet bool
|
||||
ArgOutput string
|
||||
|
||||
ArgFile string
|
||||
ArgFileIsSet bool = false
|
||||
ArgSection string
|
||||
ArgSectionIsSet bool = false
|
||||
ArgKey string
|
||||
ArgKeyIsSet bool = false
|
||||
ArgValue string
|
||||
ArgValueIsSet bool = false
|
||||
)
|
||||
|
||||
func printHelp() {
|
||||
output.Println("A utility for manipulating ini files with duplicate keys")
|
||||
output.Println("")
|
||||
output.Println("Usage: multini [OPTION]... [ACTION] config_file [section] [param] [value]")
|
||||
output.Println("Actions:")
|
||||
output.Println(" -g, --get Get values for a given combination of parameters.")
|
||||
output.Println(" -s, --set Set values for a given combination of parameters.")
|
||||
output.Println(" -a, --add Add values for a given combination of parameters.")
|
||||
output.Println(" -d, --del Delete the given combination of parameters.")
|
||||
output.Println(" -c, --chk Display parsing errors for the specified file.")
|
||||
output.Println("")
|
||||
output.Println("Options:")
|
||||
output.Println(" -e, --existing For --set and --del, fail if item is missing.")
|
||||
output.Println(" -r, --reverse For --add, adds an item to the top of the section")
|
||||
output.Println(" -i, --inplace Lock and write files in place.")
|
||||
output.Println(" This is not atomic but has less restrictions")
|
||||
output.Println(" than the default replacement method.")
|
||||
output.Println(" -o, --output FILE Write output to FILE instead. '-' means stdout")
|
||||
// output.Println(" -v, --verbose Indicate on stderr if changes were made")
|
||||
output.Println(" -u, --unix Use LF as end of line")
|
||||
output.Println(" -w, --windows Use CRLF as end of line")
|
||||
output.Println(" -q, --quiet Suppress all normal output")
|
||||
output.Println(" -h, --help Write this help to stdout")
|
||||
output.Println(" --version Write version to stdout")
|
||||
}
|
||||
|
||||
func printVersion() {
|
||||
output.Println("multini ", Version)
|
||||
}
|
||||
|
||||
func init() {
|
||||
gnuflag.BoolVar(&ArgGet, "get", false, "")
|
||||
gnuflag.BoolVar(&ArgGet, "g", false, "")
|
||||
gnuflag.BoolVar(&ArgAdd, "add", false, "")
|
||||
gnuflag.BoolVar(&ArgAdd, "a", false, "")
|
||||
gnuflag.BoolVar(&ArgSet, "set", false, "")
|
||||
gnuflag.BoolVar(&ArgSet, "s", false, "")
|
||||
gnuflag.BoolVar(&ArgDel, "del", false, "")
|
||||
gnuflag.BoolVar(&ArgDel, "d", false, "")
|
||||
gnuflag.BoolVar(&ArgChk, "chk", false, "")
|
||||
gnuflag.BoolVar(&ArgChk, "c", false, "")
|
||||
gnuflag.BoolVar(&ArgInplace, "inplace", false, "")
|
||||
gnuflag.BoolVar(&ArgInplace, "i", false, "")
|
||||
gnuflag.BoolVar(&ArgUnix, "unix", false, "")
|
||||
gnuflag.BoolVar(&ArgUnix, "u", false, "")
|
||||
gnuflag.BoolVar(&ArgWindows, "windows", false, "")
|
||||
gnuflag.BoolVar(&ArgWindows, "w", false, "")
|
||||
gnuflag.BoolVar(&ArgReverse, "reverse", false, "")
|
||||
gnuflag.BoolVar(&ArgReverse, "r", false, "")
|
||||
gnuflag.BoolVar(&ArgExisting, "existing", false, "")
|
||||
gnuflag.BoolVar(&ArgExisting, "e", false, "")
|
||||
gnuflag.BoolVar(&ArgQuiet, "quiet", false, "")
|
||||
gnuflag.BoolVar(&ArgQuiet, "q", false, "")
|
||||
gnuflag.BoolVar(&ArgVerbose, "verbose", false, "")
|
||||
gnuflag.BoolVar(&ArgVerbose, "v", false, "")
|
||||
gnuflag.StringVar(&ArgOutput, "output", "", "")
|
||||
gnuflag.StringVar(&ArgOutput, "o", "", "")
|
||||
gnuflag.BoolVar(&ArgVersion, "version", false, "")
|
||||
gnuflag.BoolVar(&ArgHelp, "help", false, "")
|
||||
gnuflag.BoolVar(&ArgHelp, "h", false, "")
|
||||
}
|
||||
|
||||
func parseArgs() error {
|
||||
gnuflag.Parse(false)
|
||||
|
||||
// info
|
||||
switch {
|
||||
case ArgHelp:
|
||||
printHelp()
|
||||
os.Exit(EXIT_SUCCESS)
|
||||
case ArgVersion:
|
||||
printVersion()
|
||||
os.Exit(EXIT_SUCCESS)
|
||||
}
|
||||
|
||||
// File EOF
|
||||
switch {
|
||||
case ArgWindows:
|
||||
types.SetEndOfLineWindows()
|
||||
case ArgUnix:
|
||||
types.SetEndOfLineUnix()
|
||||
default:
|
||||
types.SetEndOfLineNative()
|
||||
}
|
||||
|
||||
// Output settings
|
||||
output.SetEndOfLineNative()
|
||||
output.SetVerbose(ArgVerbose)
|
||||
output.SetQuiet(ArgQuiet)
|
||||
|
||||
// Positional Args
|
||||
for i := 0; i < 4 && i < gnuflag.NArg(); i++ {
|
||||
switch i {
|
||||
case 0:
|
||||
ArgFile = gnuflag.Arg(0)
|
||||
ArgFileIsSet = true
|
||||
case 1:
|
||||
ArgSection = gnuflag.Arg(1)
|
||||
ArgSectionIsSet = true
|
||||
case 2:
|
||||
ArgKey = gnuflag.Arg(2)
|
||||
ArgKeyIsSet = true
|
||||
case 3:
|
||||
ArgValue = gnuflag.Arg(3)
|
||||
ArgValueIsSet = true
|
||||
}
|
||||
}
|
||||
|
||||
if !ArgFileIsSet {
|
||||
return errors.New("Config_file not specified")
|
||||
}
|
||||
|
||||
// Mode
|
||||
actionCounter := 0
|
||||
if ArgChk {
|
||||
actionCounter++
|
||||
}
|
||||
if ArgDel {
|
||||
actionCounter++
|
||||
if !ArgSectionIsSet {
|
||||
return errors.New("Section not specified")
|
||||
}
|
||||
}
|
||||
if ArgGet {
|
||||
actionCounter++
|
||||
}
|
||||
if ArgSet {
|
||||
actionCounter++
|
||||
if !ArgSectionIsSet {
|
||||
return errors.New("Section not specified")
|
||||
}
|
||||
}
|
||||
if ArgAdd {
|
||||
actionCounter++
|
||||
if !ArgSectionIsSet {
|
||||
return errors.New("Section not specified")
|
||||
}
|
||||
}
|
||||
switch actionCounter {
|
||||
case 0:
|
||||
return errors.New("Action not set")
|
||||
case 1:
|
||||
return nil
|
||||
default:
|
||||
return errors.New("Only one action can be used at the same time")
|
||||
}
|
||||
}
|
79
cmd/multini/main.go
Normal file
79
cmd/multini/main.go
Normal file
@ -0,0 +1,79 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"multini/output"
|
||||
"multini/types"
|
||||
)
|
||||
|
||||
const (
|
||||
EXIT_SUCCESS int = 0
|
||||
EXIT_ARG_ERR int = 1
|
||||
EXIT_FILE_READ_ERR int = 2
|
||||
EXIT_BAD_SYNTAX_ERR int = 3
|
||||
EXIT_ELEMENT_NOT_FOUND int = 4
|
||||
EXIT_FILE_WRITE_ERR int = 5
|
||||
EXIT_ACTION_ERR int = 6
|
||||
)
|
||||
|
||||
var (
|
||||
Version string = "development"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
var ini types.Ini
|
||||
|
||||
if err = parseArgs(); err != nil {
|
||||
output.Errorln(err)
|
||||
os.Exit(EXIT_ARG_ERR)
|
||||
}
|
||||
|
||||
if ArgChk {
|
||||
os.Exit(chk())
|
||||
}
|
||||
|
||||
ini, err = iniRead(ArgFile)
|
||||
if err != nil {
|
||||
output.Errorln(err)
|
||||
os.Exit(EXIT_FILE_READ_ERR)
|
||||
}
|
||||
|
||||
switch {
|
||||
case ArgGet:
|
||||
err = get(&ini)
|
||||
case ArgAdd:
|
||||
err = add(&ini)
|
||||
case ArgDel:
|
||||
err = del(&ini)
|
||||
case ArgSet:
|
||||
err = set(&ini)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
output.Errorln(err)
|
||||
os.Exit(EXIT_ACTION_ERR)
|
||||
}
|
||||
|
||||
if ArgOutput == "-" {
|
||||
output.Println(ini.Full())
|
||||
os.Exit(EXIT_SUCCESS)
|
||||
} else if ArgOutput != "" {
|
||||
ArgFile = ArgOutput
|
||||
}
|
||||
|
||||
if ArgAdd || ArgSet || ArgDel {
|
||||
if ArgInplace || ArgOutput != "" {
|
||||
err = iniWriteInplace(ArgFile, &ini)
|
||||
} else {
|
||||
err = iniWrite(ArgFile, &ini)
|
||||
}
|
||||
if err != nil {
|
||||
output.Errorln(err)
|
||||
os.Exit(EXIT_FILE_WRITE_ERR)
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(EXIT_SUCCESS)
|
||||
}
|
182
cmd/multini/reader.go
Normal file
182
cmd/multini/reader.go
Normal file
@ -0,0 +1,182 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"multini/output"
|
||||
"multini/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// Ng - Named Group
|
||||
NgPrefix string = `prefix`
|
||||
NgPostifx string = `postfix`
|
||||
NgSection string = `section`
|
||||
NgKey string = `key`
|
||||
NgKeyPostfix string = `key_postfix`
|
||||
NgValue string = `value`
|
||||
NgValuePrefix string = `value_prefix`
|
||||
NgValuePostfix string = `value_postfix`
|
||||
NgComment string = `comment`
|
||||
NgCommentPrefix string = `comment_prefix`
|
||||
|
||||
RxBodyPrefix string = `(?P<` + NgPrefix + `>\s+)?`
|
||||
RxSectionName string = `\[(?P<` + NgSection + `>.+)\]`
|
||||
RxKey string = `(?P<` + NgKey + `>(?:[^;#=]+[^\s=;#]|[^;#=]))?`
|
||||
RxKeyPostfix string = `(?P<` + NgKeyPostfix + `>\s+)?`
|
||||
RxValuePrefix string = `(?P<` + NgValuePrefix + `>\s+)?`
|
||||
RxValue string = `(?P<` + NgValue + `>(?:[^;#]+[^\s;#]|[^;#]))?`
|
||||
RxValuePostfix string = `(?P<` + NgValuePostfix + `>\s+)?`
|
||||
RxKeyVal string = RxKey + RxKeyPostfix + `=` + RxValuePrefix + RxValue + RxValuePostfix
|
||||
RxBody string = `(?:` + RxSectionName + `|` + RxKeyVal + `)?`
|
||||
RxBodyPostfix string = `(?P<` + NgPostifx + `>\s+)?`
|
||||
RxCommentPrefix string = `(?P<` + NgCommentPrefix + `>[#;]\s*)`
|
||||
RxCommentText string = `(?P<` + NgComment + `>.+)?`
|
||||
RxComment string = `(?:` + RxCommentPrefix + RxCommentText + `)?`
|
||||
Rx string = RxBodyPrefix + RxBody + RxBodyPostfix + RxComment
|
||||
RxCompiled *regexp.Regexp = regexp.MustCompile(Rx)
|
||||
)
|
||||
|
||||
func rxParse(rx *regexp.Regexp, str string) map[string]string {
|
||||
match := rx.FindStringSubmatch(str)
|
||||
result := make(map[string]string)
|
||||
for i, name := range rx.SubexpNames() {
|
||||
if i != 0 && name != "" && i <= len(match) {
|
||||
result[name] = match[i]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func debugMap(el map[string]string) string {
|
||||
var dbgMap strings.Builder
|
||||
for key, val := range el {
|
||||
dbgMap.WriteString(" " + key + ": \"" + val + "\"" + output.EOL())
|
||||
}
|
||||
return dbgMap.String()
|
||||
}
|
||||
|
||||
func appendLine(ini *types.Ini, line string) error {
|
||||
elements := rxParse(RxCompiled, line)
|
||||
switch {
|
||||
case elements[NgSection] != "":
|
||||
var newSection types.Section
|
||||
newSection.Name = elements[NgSection]
|
||||
newSection.Prefix = elements[NgPrefix]
|
||||
newSection.Postfix = elements[NgPostifx]
|
||||
newSection.Comment.Prefix = elements[NgCommentPrefix]
|
||||
newSection.Comment.Value = elements[NgComment]
|
||||
if newSection.Line() == line {
|
||||
ini.Sections = append(ini.Sections, &newSection)
|
||||
return nil
|
||||
} else {
|
||||
output.Verboseln("Got:", newSection.Line())
|
||||
var newTrash types.Trash = types.Trash{Value: line}
|
||||
ini.Sections[len(ini.Sections)-1].(*types.Section).Lines = append(ini.Sections[len(ini.Sections)-1].(*types.Section).Lines, &newTrash)
|
||||
}
|
||||
case elements[NgKey] != "":
|
||||
var newKeyValue types.KeyValue
|
||||
newKeyValue.Key = elements[NgKey]
|
||||
newKeyValue.PostfixKey = elements[NgKeyPostfix]
|
||||
newKeyValue.PrefixKey = elements[NgPrefix]
|
||||
newKeyValue.Value = elements[NgValue]
|
||||
newKeyValue.PrefixValue = elements[NgValuePrefix]
|
||||
newKeyValue.PostfixValue = elements[NgValuePostfix]
|
||||
newKeyValue.Comment.Value = elements[NgComment]
|
||||
newKeyValue.Comment.Prefix = elements[NgCommentPrefix]
|
||||
if newKeyValue.Line() == line {
|
||||
ini.Sections[len(ini.Sections)-1].(*types.Section).Lines = append(ini.Sections[len(ini.Sections)-1].(*types.Section).Lines, &newKeyValue)
|
||||
return nil
|
||||
} else {
|
||||
output.Verboseln("Got:", newKeyValue.Line())
|
||||
var newTrash types.Trash = types.Trash{Value: line}
|
||||
ini.Sections[len(ini.Sections)-1].(*types.Section).Lines = append(ini.Sections[len(ini.Sections)-1].(*types.Section).Lines, &newTrash)
|
||||
}
|
||||
case elements[NgComment] != "":
|
||||
var newComment types.Comment
|
||||
newComment.Prefix = elements[NgPrefix] + elements[NgCommentPrefix]
|
||||
newComment.Value = elements[NgComment]
|
||||
if newComment.Line() == line {
|
||||
ini.Sections[len(ini.Sections)-1].(*types.Section).Lines = append(ini.Sections[len(ini.Sections)-1].(*types.Section).Lines, &newComment)
|
||||
return nil
|
||||
} else {
|
||||
output.Verboseln("Got:", newComment.Line())
|
||||
var newTrash types.Trash = types.Trash{Value: line}
|
||||
ini.Sections[len(ini.Sections)-1].(*types.Section).Lines = append(ini.Sections[len(ini.Sections)-1].(*types.Section).Lines, &newTrash)
|
||||
}
|
||||
case elements[NgPrefix] != "" || line == "":
|
||||
var newEmptyLine types.EmptyLine
|
||||
newEmptyLine.Prefix = elements[NgPrefix]
|
||||
if newEmptyLine.Line() == line {
|
||||
ini.Sections[len(ini.Sections)-1].(*types.Section).Lines = append(ini.Sections[len(ini.Sections)-1].(*types.Section).Lines, &newEmptyLine)
|
||||
return nil
|
||||
} else {
|
||||
output.Verboseln("Got:", newEmptyLine.Line())
|
||||
var newTrash types.Trash = types.Trash{Value: line}
|
||||
ini.Sections[len(ini.Sections)-1].(*types.Section).Lines = append(ini.Sections[len(ini.Sections)-1].(*types.Section).Lines, &newTrash)
|
||||
}
|
||||
default:
|
||||
var newTrash types.Trash = types.Trash{Value: line}
|
||||
ini.Sections[len(ini.Sections)-1].(*types.Section).Lines = append(ini.Sections[len(ini.Sections)-1].(*types.Section).Lines, &newTrash)
|
||||
}
|
||||
return errors.New(debugMap(elements))
|
||||
}
|
||||
|
||||
func iniRead(filename string) (types.Ini, error) {
|
||||
var (
|
||||
err error
|
||||
iniFile *os.File
|
||||
ini types.Ini
|
||||
)
|
||||
iniFile, err = os.Open(filename)
|
||||
|
||||
if err != nil {
|
||||
return ini, err
|
||||
}
|
||||
|
||||
fileScanner := bufio.NewScanner(iniFile)
|
||||
fileScanner.Split(bufio.ScanLines)
|
||||
ini.Init()
|
||||
|
||||
for i := 1; fileScanner.Scan(); i++ {
|
||||
appendLine(&ini, fileScanner.Text())
|
||||
}
|
||||
|
||||
iniFile.Close()
|
||||
return ini, nil
|
||||
}
|
||||
|
||||
func iniCheck(filename string) (bool, error) {
|
||||
var (
|
||||
err error
|
||||
iniFile *os.File
|
||||
ini types.Ini
|
||||
ok bool = true
|
||||
)
|
||||
iniFile, err = os.Open(filename)
|
||||
|
||||
if err != nil {
|
||||
return ok, err
|
||||
}
|
||||
|
||||
fileScanner := bufio.NewScanner(iniFile)
|
||||
fileScanner.Split(bufio.ScanLines)
|
||||
ini.Init()
|
||||
|
||||
for i := 1; fileScanner.Scan(); i++ {
|
||||
line := fileScanner.Text()
|
||||
err = appendLine(&ini, line)
|
||||
if err != nil {
|
||||
output.Errorln(i, ":", line)
|
||||
output.Verboseln(err)
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
|
||||
iniFile.Close()
|
||||
return ok, nil
|
||||
}
|
17
cmd/multini/stat_unix.go
Normal file
17
cmd/multini/stat_unix.go
Normal file
@ -0,0 +1,17 @@
|
||||
// +build !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func GetUidGid(info os.FileInfo) (int, int) {
|
||||
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||
if ok {
|
||||
return int(stat.Uid), int(stat.Gid)
|
||||
} else {
|
||||
return -1, -1
|
||||
}
|
||||
}
|
11
cmd/multini/stat_windows.go
Normal file
11
cmd/multini/stat_windows.go
Normal file
@ -0,0 +1,11 @@
|
||||
// +build windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func GetUidGid(info os.FileInfo) (int, int) {
|
||||
return -1, -1
|
||||
}
|
127
cmd/multini/writer.go
Normal file
127
cmd/multini/writer.go
Normal file
@ -0,0 +1,127 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"multini/types"
|
||||
)
|
||||
|
||||
// Source: https://gist.github.com/var23rav/23ae5d0d4d830aff886c3c970b8f6c6b
|
||||
/*
|
||||
GoLang: os.Rename() give error "invalid cross-device link" for Docker container with Volumes.
|
||||
MoveFile(source, destination) will work moving file between folders
|
||||
*/
|
||||
func tryMoveFile(sourcePath, destPath string) error {
|
||||
inputFile, err := os.Open(sourcePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outputFile, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
inputFile.Close()
|
||||
return err
|
||||
}
|
||||
defer outputFile.Close()
|
||||
_, err = io.Copy(outputFile, inputFile)
|
||||
inputFile.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// The copy was successful, so now delete the original file
|
||||
err = os.Remove(sourcePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func tryRemoveRenameFile(sourcePath, destPath string) bool {
|
||||
err := os.Remove(destPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
err = os.Rename(sourcePath, destPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func replaceOriginal(oldFile, newFile string) error {
|
||||
realOldFile, err := filepath.EvalSymlinks(oldFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
infoOldFile, err := os.Stat(realOldFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mode := infoOldFile.Mode()
|
||||
|
||||
var uid, gid int = GetUidGid(infoOldFile)
|
||||
|
||||
if !tryRemoveRenameFile(newFile, realOldFile) {
|
||||
err = tryMoveFile(newFile, realOldFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = os.Chmod(realOldFile, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// try to restore original uid/gid
|
||||
// don't worry if we can't
|
||||
os.Chown(realOldFile, uid, gid)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func iniWrite(filename string, ini *types.Ini) error {
|
||||
tmpFile, err := ioutil.TempFile(os.TempDir(), "multini")
|
||||
if err == nil {
|
||||
datawriter := bufio.NewWriter(tmpFile)
|
||||
_, err = datawriter.WriteString(ini.Full())
|
||||
if err == nil {
|
||||
err = datawriter.Flush()
|
||||
tmpFile.Close()
|
||||
if err == nil {
|
||||
err = replaceOriginal(filename, tmpFile.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func iniWriteInplace(filename string, ini *types.Ini) error {
|
||||
realfilename, err := filepath.EvalSymlinks(filename)
|
||||
mode := os.FileMode(int(0644))
|
||||
if os.IsNotExist(err) {
|
||||
realfilename = filename
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else {
|
||||
info, err := os.Stat(realfilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mode = info.Mode()
|
||||
}
|
||||
targetFile, err := os.OpenFile(realfilename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
|
||||
if err == nil {
|
||||
datawriter := bufio.NewWriter(targetFile)
|
||||
_, err = datawriter.WriteString(ini.Full())
|
||||
if err == nil {
|
||||
err = datawriter.Flush()
|
||||
targetFile.Close()
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
Reference in New Issue
Block a user