Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
287547b69e | |||
f4934225b6 | |||
69be8312f8 | |||
f083dcd3d8 | |||
44e955a047 | |||
c76140ffe1 | |||
a89e63f19f | |||
0ea49ff1ad | |||
325c6c25d7 | |||
805813201e | |||
702cd21256 |
118
.github/workflows/binary-release.yml
vendored
Normal file
118
.github/workflows/binary-release.yml
vendored
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
name: binary release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '[0-9]+.[0-9]+.[0-9]+'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Get the version
|
||||||
|
id: get_version
|
||||||
|
run: echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3)
|
||||||
|
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: '1.13.0'
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: make -j $(nproc) compile VERSION=${{ steps.get_version.outputs.VERSION }}
|
||||||
|
|
||||||
|
- name: create release
|
||||||
|
id: create_release
|
||||||
|
uses: actions/create-release@latest
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ steps.get_version.outputs.VERSION }}
|
||||||
|
release_name: Release ${{ steps.get_version.outputs.VERSION }}
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
|
- name: darwin-386
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./bin/${{ github.event.repository.name }}-darwin-386
|
||||||
|
asset_name: ${{ github.event.repository.name }}-darwin-386
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: darwin-amd64
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./bin/${{ github.event.repository.name }}-darwin-amd64
|
||||||
|
asset_name: ${{ github.event.repository.name }}-darwin-amd64
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: freebsd-386
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./bin/${{ github.event.repository.name }}-freebsd-386
|
||||||
|
asset_name: ${{ github.event.repository.name }}-freebsd-386
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: freebsd-amd64
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./bin/${{ github.event.repository.name }}-freebsd-amd64
|
||||||
|
asset_name: ${{ github.event.repository.name }}-freebsd-amd64
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: linux-386
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./bin/${{ github.event.repository.name }}-linux-386
|
||||||
|
asset_name: ${{ github.event.repository.name }}-linux-386
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: linux-amd64
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./bin/${{ github.event.repository.name }}-linux-amd64
|
||||||
|
asset_name: ${{ github.event.repository.name }}-linux-amd64
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: windows-386
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./bin/${{ github.event.repository.name }}-windows-386.exe
|
||||||
|
asset_name: ${{ github.event.repository.name }}-windows-386.exe
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: windows-amd64
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./bin/${{ github.event.repository.name }}-windows-amd64.exe
|
||||||
|
asset_name: ${{ github.event.repository.name }}-windows-amd64.exe
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
23
.github/workflows/build.yml
vendored
Normal file
23
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
name: build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: '1.13.0'
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: make -j $(nproc)
|
||||||
|
|
36
Makefile
36
Makefile
@ -1,21 +1,30 @@
|
|||||||
NAME=multini
|
NAME = multini
|
||||||
VERSION=0.1
|
VERSION = dev_$(shell date +%F_%T)
|
||||||
GOCMD=go
|
GOCMD = go
|
||||||
LDFLAGS:="$(LDFLAGS) -X 'main.Version=$(VERSION)'"
|
LDFLAGS := "$(LDFLAGS) -X 'main.Version=$(VERSION)'"
|
||||||
GOBUILD=$(GOCMD) build -ldflags=$(LDFLAGS)
|
GOBUILD = $(GOCMD) build -ldflags=$(LDFLAGS)
|
||||||
SRCMAIN=.
|
SRCMAIN = ./cmd/$(NAME)
|
||||||
BINDIR=bin
|
SRCDOC = ./doc
|
||||||
BIN=$(BINDIR)/$(NAME)
|
SRCTEST = ./test
|
||||||
README=README
|
BINDIR = bin
|
||||||
LICENSE=LICENSE
|
BIN = $(BINDIR)/$(NAME)
|
||||||
TEST=./run_test.sh
|
README = $(SRCDOC)/README
|
||||||
PREFIX=/usr
|
LICENSE = LICENSE
|
||||||
|
TEST = $(SRCTEST)/run_test.sh
|
||||||
|
PREFIX = /usr
|
||||||
|
|
||||||
|
.PHONY: all prep doc build check-build freebsd-386 darwin-386 linux-386 windows-386 freebsd-amd64 darwin-amd64 linux-amd64 windows-amd64 compile install check-install uninstall clean test
|
||||||
|
|
||||||
all: build
|
all: build
|
||||||
|
|
||||||
prep: clean
|
prep: clean
|
||||||
|
go mod init; go mod tidy
|
||||||
mkdir $(BINDIR)
|
mkdir $(BINDIR)
|
||||||
|
|
||||||
|
doc: check-build
|
||||||
|
test -d $(SRCDOC) || mkdir $(SRCDOC)
|
||||||
|
$(BIN) --help > $(README)
|
||||||
|
|
||||||
build: prep
|
build: prep
|
||||||
$(GOBUILD) -o $(BIN) $(SRCMAIN)
|
$(GOBUILD) -o $(BIN) $(SRCMAIN)
|
||||||
|
|
||||||
@ -48,7 +57,7 @@ windows-amd64: prep
|
|||||||
|
|
||||||
compile: freebsd-386 darwin-386 linux-386 windows-386 freebsd-amd64 darwin-amd64 linux-amd64 windows-amd64
|
compile: freebsd-386 darwin-386 linux-386 windows-386 freebsd-amd64 darwin-amd64 linux-amd64 windows-amd64
|
||||||
|
|
||||||
install: check-build
|
install: check-build doc
|
||||||
install -m 755 -d $(PREFIX)/bin/
|
install -m 755 -d $(PREFIX)/bin/
|
||||||
install -m 755 $(BIN) $(PREFIX)/bin/
|
install -m 755 $(BIN) $(PREFIX)/bin/
|
||||||
install -m 755 -d $(PREFIX)/share/licenses/$(NAME)/
|
install -m 755 -d $(PREFIX)/share/licenses/$(NAME)/
|
||||||
@ -71,3 +80,4 @@ clean:
|
|||||||
|
|
||||||
test: check-build
|
test: check-build
|
||||||
$(TEST) $(BIN)
|
$(TEST) $(BIN)
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"multini/output"
|
"multini/internal/output"
|
||||||
"multini/types"
|
"multini/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func chk() int {
|
func chk() int {
|
||||||
@ -22,7 +22,7 @@ func chk() int {
|
|||||||
|
|
||||||
func add(ini *types.Ini) error {
|
func add(ini *types.Ini) error {
|
||||||
if ArgKeyIsSet {
|
if ArgKeyIsSet {
|
||||||
return ini.AddKey(ArgSection, ArgKey, ArgValue)
|
return ini.AddKey(ArgSection, ArgKey, ArgValue, ArgReverse)
|
||||||
} else {
|
} else {
|
||||||
ini.AddSection(ArgSection)
|
ini.AddSection(ArgSection)
|
||||||
return nil
|
return nil
|
@ -4,8 +4,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"multini/output"
|
"multini/internal/output"
|
||||||
"multini/types"
|
"multini/internal/types"
|
||||||
|
|
||||||
"github.com/juju/gnuflag"
|
"github.com/juju/gnuflag"
|
||||||
)
|
)
|
||||||
@ -23,6 +23,8 @@ var (
|
|||||||
ArgUnix bool
|
ArgUnix bool
|
||||||
ArgHelp bool
|
ArgHelp bool
|
||||||
ArgExisting bool
|
ArgExisting bool
|
||||||
|
ArgReverse bool
|
||||||
|
ArgQuiet bool
|
||||||
ArgOutput string
|
ArgOutput string
|
||||||
|
|
||||||
ArgFile string
|
ArgFile string
|
||||||
@ -40,14 +42,15 @@ func printHelp() {
|
|||||||
output.Println("")
|
output.Println("")
|
||||||
output.Println("Usage: multini [OPTION]... [ACTION] config_file [section] [param] [value]")
|
output.Println("Usage: multini [OPTION]... [ACTION] config_file [section] [param] [value]")
|
||||||
output.Println("Actions:")
|
output.Println("Actions:")
|
||||||
output.Println(" -g, --get get values for a given combination of parameters.")
|
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(" -s, --set Set values for a given combination of parameters.")
|
||||||
output.Println(" -a, --add add 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(" -d, --del Delete the given combination of parameters.")
|
||||||
output.Println(" -c, --chk display parsing errors for the specified file.")
|
output.Println(" -c, --chk Display parsing errors for the specified file.")
|
||||||
output.Println("")
|
output.Println("")
|
||||||
output.Println("Options:")
|
output.Println("Options:")
|
||||||
output.Println(" -e, --existing For --set and --del, fail if item is missing.")
|
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(" -i, --inplace Lock and write files in place.")
|
||||||
output.Println(" This is not atomic but has less restrictions")
|
output.Println(" This is not atomic but has less restrictions")
|
||||||
output.Println(" than the default replacement method.")
|
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(" -v, --verbose Indicate on stderr if changes were made")
|
||||||
output.Println(" -u, --unix Use LF as end of line")
|
output.Println(" -u, --unix Use LF as end of line")
|
||||||
output.Println(" -w, --windows Use CRLF 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(" -h, --help Write this help to stdout")
|
||||||
output.Println(" --version Write version to stdout")
|
output.Println(" --version Write version to stdout")
|
||||||
}
|
}
|
||||||
@ -74,14 +78,18 @@ func init() {
|
|||||||
gnuflag.BoolVar(&ArgDel, "d", false, "")
|
gnuflag.BoolVar(&ArgDel, "d", false, "")
|
||||||
gnuflag.BoolVar(&ArgChk, "chk", false, "")
|
gnuflag.BoolVar(&ArgChk, "chk", false, "")
|
||||||
gnuflag.BoolVar(&ArgChk, "c", false, "")
|
gnuflag.BoolVar(&ArgChk, "c", false, "")
|
||||||
gnuflag.BoolVar(&ArgDel, "inplace", false, "")
|
gnuflag.BoolVar(&ArgInplace, "inplace", false, "")
|
||||||
gnuflag.BoolVar(&ArgDel, "i", false, "")
|
gnuflag.BoolVar(&ArgInplace, "i", false, "")
|
||||||
gnuflag.BoolVar(&ArgUnix, "unix", false, "")
|
gnuflag.BoolVar(&ArgUnix, "unix", false, "")
|
||||||
gnuflag.BoolVar(&ArgUnix, "u", false, "")
|
gnuflag.BoolVar(&ArgUnix, "u", false, "")
|
||||||
gnuflag.BoolVar(&ArgWindows, "windows", false, "")
|
gnuflag.BoolVar(&ArgWindows, "windows", false, "")
|
||||||
gnuflag.BoolVar(&ArgWindows, "w", false, "")
|
gnuflag.BoolVar(&ArgWindows, "w", false, "")
|
||||||
|
gnuflag.BoolVar(&ArgReverse, "reverse", false, "")
|
||||||
|
gnuflag.BoolVar(&ArgReverse, "r", false, "")
|
||||||
gnuflag.BoolVar(&ArgExisting, "existing", false, "")
|
gnuflag.BoolVar(&ArgExisting, "existing", false, "")
|
||||||
gnuflag.BoolVar(&ArgExisting, "e", false, "")
|
gnuflag.BoolVar(&ArgExisting, "e", false, "")
|
||||||
|
gnuflag.BoolVar(&ArgQuiet, "quiet", false, "")
|
||||||
|
gnuflag.BoolVar(&ArgQuiet, "q", false, "")
|
||||||
gnuflag.BoolVar(&ArgVerbose, "verbose", false, "")
|
gnuflag.BoolVar(&ArgVerbose, "verbose", false, "")
|
||||||
gnuflag.BoolVar(&ArgVerbose, "v", false, "")
|
gnuflag.BoolVar(&ArgVerbose, "v", false, "")
|
||||||
gnuflag.StringVar(&ArgOutput, "output", "", "")
|
gnuflag.StringVar(&ArgOutput, "output", "", "")
|
||||||
@ -92,7 +100,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseArgs() error {
|
func parseArgs() error {
|
||||||
gnuflag.Parse(true)
|
gnuflag.Parse(false)
|
||||||
|
|
||||||
// info
|
// info
|
||||||
switch {
|
switch {
|
||||||
@ -117,6 +125,7 @@ func parseArgs() error {
|
|||||||
// Output settings
|
// Output settings
|
||||||
output.SetEndOfLineNative()
|
output.SetEndOfLineNative()
|
||||||
output.SetVerbose(ArgVerbose)
|
output.SetVerbose(ArgVerbose)
|
||||||
|
output.SetQuiet(ArgQuiet)
|
||||||
|
|
||||||
// Positional Args
|
// Positional Args
|
||||||
for i := 0; i < 4 && i < gnuflag.NArg(); i++ {
|
for i := 0; i < 4 && i < gnuflag.NArg(); i++ {
|
@ -1,11 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"multini/output"
|
"multini/internal/output"
|
||||||
"multini/types"
|
"multini/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -37,7 +36,7 @@ func main() {
|
|||||||
|
|
||||||
ini, err = iniRead(ArgFile)
|
ini, err = iniRead(ArgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
output.Errorln(err)
|
||||||
os.Exit(EXIT_FILE_READ_ERR)
|
os.Exit(EXIT_FILE_READ_ERR)
|
||||||
}
|
}
|
||||||
|
|
@ -7,8 +7,8 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"multini/output"
|
"multini/internal/output"
|
||||||
"multini/types"
|
"multini/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -26,15 +26,15 @@ var (
|
|||||||
|
|
||||||
RxBodyPrefix string = `(?P<` + NgPrefix + `>\s+)?`
|
RxBodyPrefix string = `(?P<` + NgPrefix + `>\s+)?`
|
||||||
RxSectionName string = `\[(?P<` + NgSection + `>.+)\]`
|
RxSectionName string = `\[(?P<` + NgSection + `>.+)\]`
|
||||||
RxKey string = `(?P<` + NgKey + `>(?:[^;#=]+[^\s=;#]|[^;#=]))?`
|
RxKey string = `(?P<` + NgKey + `>(?:[^;#/=]+[^\s=;#/]|[^;#/=]))?`
|
||||||
RxKeyPostfix string = `(?P<` + NgKeyPostfix + `>\s+)?`
|
RxKeyPostfix string = `(?P<` + NgKeyPostfix + `>\s+)?`
|
||||||
RxValuePrefix string = `(?P<` + NgValuePrefix + `>\s+)?`
|
RxValuePrefix string = `(?P<` + NgValuePrefix + `>\s+)?`
|
||||||
RxValue string = `(?P<` + NgValue + `>(?:[^;#]+[^\s;#]|[^;#]))?`
|
RxValue string = `(?P<` + NgValue + `>(?:[^;#/]+[^\s;#/]|[^;#/]))?`
|
||||||
RxValuePostfix string = `(?P<` + NgValuePostfix + `>\s+)?`
|
RxValuePostfix string = `(?P<` + NgValuePostfix + `>\s+)?`
|
||||||
RxKeyVal string = RxKey + RxKeyPostfix + `=` + RxValuePrefix + RxValue + RxValuePostfix
|
RxKeyVal string = RxKey + RxKeyPostfix + `=` + RxValuePrefix + RxValue + RxValuePostfix
|
||||||
RxBody string = `(?:` + RxSectionName + `|` + RxKeyVal + `)?`
|
RxBody string = `(?:` + RxSectionName + `|` + RxKeyVal + `)?`
|
||||||
RxBodyPostfix string = `(?P<` + NgPostifx + `>\s+)?`
|
RxBodyPostfix string = `(?P<` + NgPostifx + `>\s+)?`
|
||||||
RxCommentPrefix string = `(?P<` + NgCommentPrefix + `>[#;]\s*)`
|
RxCommentPrefix string = `(?P<` + NgCommentPrefix + `>([#;]|//)\s*)`
|
||||||
RxCommentText string = `(?P<` + NgComment + `>.+)?`
|
RxCommentText string = `(?P<` + NgComment + `>.+)?`
|
||||||
RxComment string = `(?:` + RxCommentPrefix + RxCommentText + `)?`
|
RxComment string = `(?:` + RxCommentPrefix + RxCommentText + `)?`
|
||||||
Rx string = RxBodyPrefix + RxBody + RxBodyPostfix + RxComment
|
Rx string = RxBodyPrefix + RxBody + RxBodyPostfix + RxComment
|
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/internal/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
|
||||||
|
}
|
@ -2,21 +2,21 @@ A utility for manipulating ini files with duplicate keys
|
|||||||
|
|
||||||
Usage: multini [OPTION]... [ACTION] config_file [section] [param] [value]
|
Usage: multini [OPTION]... [ACTION] config_file [section] [param] [value]
|
||||||
Actions:
|
Actions:
|
||||||
-g, --get get values for a given combination of parameters.
|
-g, --get Get values for a given combination of parameters.
|
||||||
-s, --set set values for a given combination of parameters.
|
-s, --set Set values for a given combination of parameters.
|
||||||
-a, --add add values for a given combination of parameters.
|
-a, --add Add values for a given combination of parameters.
|
||||||
-d, --del delete the given combination of parameters.
|
-d, --del Delete the given combination of parameters.
|
||||||
-c, --chk display parsing errors for the specified file.
|
-c, --chk Display parsing errors for the specified file.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-e, --existing For --set and --del, fail if item is missing.
|
-e, --existing For --set and --del, fail if item is missing.
|
||||||
|
-r, --reverse For --add, adds an item to the top of the section
|
||||||
-i, --inplace Lock and write files in place.
|
-i, --inplace Lock and write files in place.
|
||||||
This is not atomic but has less restrictions
|
This is not atomic but has less restrictions
|
||||||
than the default replacement method.
|
than the default replacement method.
|
||||||
-o, --output FILE Write output to FILE instead. '-' means stdout
|
-o, --output FILE Write output to FILE instead. '-' means stdout
|
||||||
-u, --unix Use LF as end of line
|
-u, --unix Use LF as end of line
|
||||||
-w, --windows Use CRLF as end of line
|
-w, --windows Use CRLF as end of line
|
||||||
|
-q, --quiet Suppress all normal output
|
||||||
-h, --help Write this help to stdout
|
-h, --help Write this help to stdout
|
||||||
--version Write version to stdout
|
--version Write version to stdout
|
||||||
|
|
||||||
2020 (c) GenZmeY
|
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module multini
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d h1:c93kUJDtVAXFEhsCh5jSxyOJmFHuzcihnslQiX8Urwo=
|
||||||
|
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
@ -16,6 +16,14 @@ var (
|
|||||||
verbose *log.Logger = devNull
|
verbose *log.Logger = devNull
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func SetQuiet(enabled bool) {
|
||||||
|
if enabled {
|
||||||
|
stdout = devNull
|
||||||
|
stderr = devNull
|
||||||
|
verbose = devNull
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func SetVerbose(enabled bool) {
|
func SetVerbose(enabled bool) {
|
||||||
if enabled {
|
if enabled {
|
||||||
verbose = stderr
|
verbose = stderr
|
@ -89,6 +89,14 @@ func (obj *Ini) GetKeyVal(section, key, value string) error {
|
|||||||
func (obj *Ini) AddSection(section string) *Section {
|
func (obj *Ini) AddSection(section string) *Section {
|
||||||
sect, err := obj.FindSection(section)
|
sect, err := obj.FindSection(section)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
sectSize := len(obj.Sections)
|
||||||
|
if sectSize > 1 {
|
||||||
|
prevSect := obj.Sections[sectSize-1].(*Section)
|
||||||
|
lineSize := len(prevSect.Lines)
|
||||||
|
if lineSize == 0 || lineSize > 0 && prevSect.Lines[lineSize-1].Type() != TEmptyLine {
|
||||||
|
obj.Sections[sectSize-1].(*Section).Lines = append(obj.Sections[sectSize-1].(*Section).Lines, &EmptyLine{})
|
||||||
|
}
|
||||||
|
}
|
||||||
var newSection Section
|
var newSection Section
|
||||||
newSection.Name = section
|
newSection.Name = section
|
||||||
newSection.Prefix = obj.Sections[len(obj.Sections)-1].Indent()
|
newSection.Prefix = obj.Sections[len(obj.Sections)-1].Indent()
|
||||||
@ -102,7 +110,7 @@ func (obj *Ini) SetSection(section string) *Section {
|
|||||||
return obj.AddSection(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)
|
sect, err := obj.FindSection(section)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if createIfNotExist() {
|
if createIfNotExist() {
|
||||||
@ -111,7 +119,7 @@ func (obj *Ini) AddKey(section, key, value string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sect.AddKey(key, value)
|
sect.AddKey(key, value, reverse)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -111,39 +111,54 @@ func (obj *Section) GetKeyVal(name, value string) error {
|
|||||||
return errors.New("Parameter:Value not found: " + name + ":" + value)
|
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 newKeyValue KeyValue
|
||||||
var replaceIndex int = -1
|
var replaceIndex int = -1
|
||||||
newKeyValue.Key = name
|
newKeyValue.Key = name
|
||||||
newKeyValue.Value = value
|
newKeyValue.Value = value
|
||||||
// replace first emptyline
|
if reverse {
|
||||||
for i := len(obj.Lines) - 1; i >= 0; i-- {
|
// for right indent and tabs
|
||||||
if obj.Lines[i].Type() == TEmptyLine {
|
for i := 0; i < len(obj.Lines); i++ {
|
||||||
replaceIndex = i
|
if obj.Lines[i].Type() == TKeyValue {
|
||||||
} else {
|
template := obj.Lines[i].(*KeyValue)
|
||||||
break
|
newKeyValue.PrefixKey = template.PrefixKey
|
||||||
|
newKeyValue.PostfixKey = template.PostfixKey
|
||||||
|
newKeyValue.PrefixValue = template.PrefixValue
|
||||||
|
newKeyValue.PostfixValue = template.PostfixValue
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
obj.Lines = append([]Element{&newKeyValue}, obj.Lines...)
|
||||||
// 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 {
|
} else {
|
||||||
obj.Lines = append(obj.Lines, obj.Lines[replaceIndex])
|
// replace first emptyline
|
||||||
obj.Lines[replaceIndex] = &newKeyValue
|
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
|
gotIt := false
|
||||||
for i, keyVal := range obj.Lines {
|
for i, keyVal := range obj.Lines {
|
||||||
if keyVal.Type() == TKeyValue &&
|
if keyVal.Type() == TKeyValue &&
|
||||||
@ -157,7 +172,7 @@ func (obj *Section) AddKey(name, value string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !gotIt {
|
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 !gotIt {
|
||||||
if createIfNotExist() {
|
if createIfNotExist() {
|
||||||
obj.appendKey(name, value)
|
obj.appendKey(name, value, false)
|
||||||
} else {
|
} else {
|
||||||
return errors.New("Parameter not found: " + name)
|
return errors.New("Parameter not found: " + name)
|
||||||
}
|
}
|
@ -10,7 +10,7 @@ DefKey3=NoSpaces!
|
|||||||
Key2 = 2
|
Key2 = 2
|
||||||
Key3 = 3
|
Key3 = 3
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
||||||
@ -17,4 +17,5 @@ DefKey3=NoSpaces!
|
|||||||
[SectionWithIndent]
|
[SectionWithIndent]
|
||||||
Key=Value
|
Key=Value
|
||||||
[SectionWithoutNewLineBefore]
|
[SectionWithoutNewLineBefore]
|
||||||
|
|
||||||
[NewSection]
|
[NewSection]
|
@ -8,7 +8,7 @@ DefKey3=NoSpaces!
|
|||||||
[SimpleSection] # Comment For Section
|
[SimpleSection] # Comment For Section
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
|
|
||||||
[SectionWithIndent]
|
[SectionWithIndent]
|
||||||
Key=Value
|
Key=Value
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 3
|
Key = 3
|
||||||
|
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = onlyone
|
Key = onlyone
|
||||||
|
|
||||||
[SectionWithIndent]
|
[SectionWithIndent]
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection]
|
[MultipleKeySection] // C style comment
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -1,10 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
DEF='\e[0m'; BLD='\e[1m'; RED='\e[31m'; GRN='\e[32m'; WHT='\e[97m'
|
DEF='\e[0m'; BLD='\e[1m'; RED='\e[31m'; GRN='\e[32m'; WHT='\e[97m'
|
||||||
|
|
||||||
ScriptFullname=$(readlink -e "$0")
|
ScriptFullname=$(readlink -e "$0")
|
||||||
ScriptName=$(echo "$ScriptFullname" | awk -F '/' '{print $NF;}')
|
ScriptName=$(echo "$ScriptFullname" | awk -F '/' '{print $NF;}')
|
||||||
ScriptDir=$(dirname "$ScriptFullname")
|
ScriptDir=$(dirname "$ScriptFullname")
|
||||||
TestDir="$ScriptDir/tests"
|
TestDir="$ScriptDir/data"
|
||||||
Multini=$(readlink -e "$1")
|
Multini=$(readlink -e "$1")
|
||||||
|
|
||||||
if [[ -z "$Multini" ]]; then
|
if [[ -z "$Multini" ]]; then
|
46
writer.go
46
writer.go
@ -1,46 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"multini/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func replaceOriginal(oldFile, newFile string) error {
|
|
||||||
err := os.Remove(oldFile)
|
|
||||||
if err == nil {
|
|
||||||
err = os.Rename(newFile, oldFile)
|
|
||||||
}
|
|
||||||
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 {
|
|
||||||
targetFile, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0644)
|
|
||||||
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