release: 0.2

- fix inplace arg
- follow symlinks by default
- reverse flag
- quiet flag
- try chown/chmod on unix
This commit is contained in:
GenZmeY 2020-04-27 14:35:10 +03:00
parent 294a89ba74
commit 702cd21256
10 changed files with 144 additions and 43 deletions

View File

@ -1,5 +1,5 @@
NAME=multini
VERSION=0.1
VERSION=0.2
GOCMD=go
LDFLAGS:="$(LDFLAGS) -X 'main.Version=$(VERSION)'"
GOBUILD=$(GOCMD) build -ldflags=$(LDFLAGS)

View File

@ -22,7 +22,7 @@ func chk() int {
func add(ini *types.Ini) error {
if ArgKeyIsSet {
return ini.AddKey(ArgSection, ArgKey, ArgValue)
return ini.AddKey(ArgSection, ArgKey, ArgValue, ArgReverse)
} else {
ini.AddSection(ArgSection)
return nil

23
args.go
View File

@ -23,6 +23,8 @@ var (
ArgUnix bool
ArgHelp bool
ArgExisting bool
ArgReverse bool
ArgQuiet bool
ArgOutput string
ArgFile string
@ -40,14 +42,15 @@ func printHelp() {
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(" -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.")
@ -55,6 +58,7 @@ func printHelp() {
// 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")
}
@ -74,14 +78,18 @@ func init() {
gnuflag.BoolVar(&ArgDel, "d", false, "")
gnuflag.BoolVar(&ArgChk, "chk", false, "")
gnuflag.BoolVar(&ArgChk, "c", false, "")
gnuflag.BoolVar(&ArgDel, "inplace", false, "")
gnuflag.BoolVar(&ArgDel, "i", 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", "", "")
@ -117,6 +125,7 @@ func parseArgs() error {
// Output settings
output.SetEndOfLineNative()
output.SetVerbose(ArgVerbose)
output.SetQuiet(ArgQuiet)
// Positional Args
for i := 0; i < 4 && i < gnuflag.NArg(); i++ {

View File

@ -1,7 +1,6 @@
package main
import (
"fmt"
"os"
"multini/output"
@ -37,7 +36,7 @@ func main() {
ini, err = iniRead(ArgFile)
if err != nil {
fmt.Println(err)
output.Errorln(err)
os.Exit(EXIT_FILE_READ_ERR)
}

View File

@ -16,6 +16,14 @@ var (
verbose *log.Logger = devNull
)
func SetQuiet(enabled bool) {
if enabled {
stdout = devNull
stderr = devNull
verbose = devNull
}
}
func SetVerbose(enabled bool) {
if enabled {
verbose = stderr

17
stat_unix.go Normal file
View 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
stat_windows.go Normal file
View File

@ -0,0 +1,11 @@
// +build windows
package main
import (
"os"
)
func GetUidGid(info os.FileInfo) (int, int) {
return -1, -1
}

View File

@ -102,7 +102,7 @@ func (obj *Ini) SetSection(section string) *Section {
return obj.AddSection(section)
}
func (obj *Ini) AddKey(section, key, value string) error {
func (obj *Ini) AddKey(section, key, value string, reverse bool) error {
sect, err := obj.FindSection(section)
if err != nil {
if createIfNotExist() {
@ -111,7 +111,7 @@ func (obj *Ini) AddKey(section, key, value string) error {
return err
}
}
sect.AddKey(key, value)
sect.AddKey(key, value, reverse)
return nil
}

View File

@ -111,39 +111,54 @@ func (obj *Section) GetKeyVal(name, value string) error {
return errors.New("Parameter:Value not found: " + name + ":" + value)
}
func (obj *Section) appendKey(name, value string) {
func (obj *Section) appendKey(name, value string, reverse bool) {
var newKeyValue KeyValue
var replaceIndex int = -1
newKeyValue.Key = name
newKeyValue.Value = value
// replace first emptyline
for i := len(obj.Lines) - 1; i >= 0; i-- {
if obj.Lines[i].Type() == TEmptyLine {
replaceIndex = i
} else {
break
if reverse {
// for right indent and tabs
for i := 0; i < len(obj.Lines); i++ {
if obj.Lines[i].Type() == TKeyValue {
template := obj.Lines[i].(*KeyValue)
newKeyValue.PrefixKey = template.PrefixKey
newKeyValue.PostfixKey = template.PostfixKey
newKeyValue.PrefixValue = template.PrefixValue
newKeyValue.PostfixValue = template.PostfixValue
break
}
}
}
// for right indent and tabs
for i := len(obj.Lines) - 1; i >= 0; i-- {
if obj.Lines[i].Type() == TKeyValue {
template := obj.Lines[i].(*KeyValue)
newKeyValue.PrefixKey = template.PrefixKey
newKeyValue.PostfixKey = template.PostfixKey
newKeyValue.PrefixValue = template.PrefixValue
newKeyValue.PostfixValue = template.PostfixValue
break
}
}
if replaceIndex == -1 {
obj.Lines = append(obj.Lines, &newKeyValue)
obj.Lines = append([]Element{&newKeyValue}, obj.Lines...)
} else {
obj.Lines = append(obj.Lines, obj.Lines[replaceIndex])
obj.Lines[replaceIndex] = &newKeyValue
// replace first emptyline
for i := len(obj.Lines) - 1; i >= 0; i-- {
if obj.Lines[i].Type() == TEmptyLine {
replaceIndex = i
} else {
break
}
}
// for right indent and tabs
for i := len(obj.Lines) - 1; i >= 0; i-- {
if obj.Lines[i].Type() == TKeyValue {
template := obj.Lines[i].(*KeyValue)
newKeyValue.PrefixKey = template.PrefixKey
newKeyValue.PostfixKey = template.PostfixKey
newKeyValue.PrefixValue = template.PrefixValue
newKeyValue.PostfixValue = template.PostfixValue
break
}
}
if replaceIndex == -1 {
obj.Lines = append(obj.Lines, &newKeyValue)
} else {
obj.Lines = append(obj.Lines, obj.Lines[replaceIndex])
obj.Lines[replaceIndex] = &newKeyValue
}
}
}
func (obj *Section) AddKey(name, value string) {
func (obj *Section) AddKey(name, value string, reverse bool) {
gotIt := false
for i, keyVal := range obj.Lines {
if keyVal.Type() == TKeyValue &&
@ -157,7 +172,7 @@ func (obj *Section) AddKey(name, value string) {
}
}
if !gotIt {
obj.appendKey(name, value)
obj.appendKey(name, value, reverse)
}
}
@ -176,7 +191,7 @@ func (obj *Section) SetKey(name, value string) error {
}
if !gotIt {
if createIfNotExist() {
obj.appendKey(name, value)
obj.appendKey(name, value, false)
} else {
return errors.New("Parameter not found: " + name)
}

View File

@ -4,15 +4,44 @@ import (
"bufio"
"io/ioutil"
"os"
"path/filepath"
"multini/types"
)
func replaceOriginal(oldFile, newFile string) error {
err := os.Remove(oldFile)
if err == nil {
err = os.Rename(newFile, oldFile)
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)
err = os.Remove(realOldFile)
if err != nil {
return err
}
err = os.Rename(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
}
@ -33,7 +62,20 @@ func iniWrite(filename string, ini *types.Ini) error {
}
func iniWriteInplace(filename string, ini *types.Ini) error {
targetFile, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0644)
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, mode)
if err == nil {
datawriter := bufio.NewWriter(targetFile)
_, err = datawriter.WriteString(ini.Full())