Compare commits
No commits in common. "master" and "0.2.1" have entirely different histories.
.github/workflows
.gitignoreMakefileREADMEREADME-ru.mdREADME.mdactions.goargs.gogo.modgo.summain.gooutput
reader.gorun_test.shstat_unix.gostat_windows.gotest/data
tests
common.sh
expected_ini
test_add_keyval.sh.initest_add_keyval_existing.sh.initest_add_section.sh.initest_del_key.sh.initest_del_key_multiple.sh.initest_del_keyval.sh.initest_del_section.sh.initest_get_key.sh.initest_get_key_multiple.sh.initest_get_keyval.sh.initest_get_section.sh.initest_get_section_list.sh.initest_set_keyval.sh.ini
in_ini
test_add_keyval.sh.initest_add_keyval_existing.sh.initest_add_section.sh.initest_del_key.sh.initest_del_key_multiple.sh.initest_del_keyval.sh.initest_del_section.sh.initest_get_key.sh.initest_get_key_multiple.sh.initest_get_keyval.sh.initest_get_section.sh.initest_get_section_list.sh.initest_set_keyval.sh.ini
test_add_keyval.shtest_add_keyval_existing.shtest_add_section.shtest_del_key.shtest_del_key_multiple.shtest_del_keyval.shtest_del_section.shtest_get_key.shtest_get_key_multiple.shtest_get_keyval.shtest_get_section.shtest_get_section_list.shtest_set_keyval.shtypes
writer.go
118
.github/workflows/binary-release.yml
vendored
118
.github/workflows/binary-release.yml
vendored
@ -1,118 +0,0 @@
|
|||||||
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: multini ${{ 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
23
.github/workflows/build.yml
vendored
@ -1,23 +0,0 @@
|
|||||||
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)
|
|
||||||
|
|
32
.github/workflows/codeql-analysis.yml
vendored
32
.github/workflows/codeql-analysis.yml
vendored
@ -1,32 +0,0 @@
|
|||||||
name: "CodeQL"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
language: [ 'go' ]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v1
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v1
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v1
|
|
23
.github/workflows/tests.yml
vendored
23
.github/workflows/tests.yml
vendored
@ -1,23 +0,0 @@
|
|||||||
name: tests
|
|
||||||
|
|
||||||
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) && make test
|
|
||||||
|
|
23
.gitignore
vendored
23
.gitignore
vendored
@ -1,3 +1,20 @@
|
|||||||
/bin/
|
# Binaries for programs and plugins
|
||||||
/test/data/out_ini/
|
bin/*
|
||||||
/cmd/multini/multini
|
multini
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Custom binary test
|
||||||
|
tests/out_ini/*
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
36
Makefile
36
Makefile
@ -1,30 +1,21 @@
|
|||||||
NAME = multini
|
NAME=multini
|
||||||
VERSION := $(shell git describe)
|
VERSION=0.2.1
|
||||||
GOCMD = go
|
GOCMD=go
|
||||||
LDFLAGS := "$(LDFLAGS) -s -w -X 'main.Version=$(VERSION)'"
|
LDFLAGS:="$(LDFLAGS) -X 'main.Version=$(VERSION)'"
|
||||||
GOBUILD = $(GOCMD) build -ldflags=$(LDFLAGS)
|
GOBUILD=$(GOCMD) build -ldflags=$(LDFLAGS)
|
||||||
SRCMAIN = ./cmd/$(NAME)
|
SRCMAIN=.
|
||||||
SRCDOC = ./doc
|
BINDIR=bin
|
||||||
SRCTEST = ./test
|
BIN=$(BINDIR)/$(NAME)
|
||||||
BINDIR = bin
|
README=README
|
||||||
BIN = $(BINDIR)/$(NAME)
|
LICENSE=LICENSE
|
||||||
README = $(SRCDOC)/README
|
TEST=./run_test.sh
|
||||||
LICENSE = LICENSE
|
PREFIX=/usr
|
||||||
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)
|
||||||
|
|
||||||
@ -57,7 +48,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 doc
|
install: check-build
|
||||||
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)/
|
||||||
@ -80,4 +71,3 @@ clean:
|
|||||||
|
|
||||||
test: check-build
|
test: check-build
|
||||||
$(TEST) $(BIN)
|
$(TEST) $(BIN)
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
A utility for manipulating ini files with duplicate keys
|
A utility for manipulating ini files with duplicate keys
|
||||||
|
|
||||||
Usage: multini [OPTION]... [ACTION] config_file [section] [key] [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.
|
111
README-ru.md
111
README-ru.md
@ -1,111 +0,0 @@
|
|||||||
# Multini
|
|
||||||
|
|
||||||
[](https://github.com/GenZmeY/multini/actions?query=workflow%3Abuild)
|
|
||||||
[](https://github.com/GenZmeY/multini/actions?query=workflow%3Atests)
|
|
||||||
[](https://github.com/GenZmeY/multini/security/code-scanning)
|
|
||||||
[](https://golang.org)
|
|
||||||
[](LICENSE)
|
|
||||||
[](https://github.com/GenZmeY/multini/releases)
|
|
||||||
|
|
||||||
[English](README.md), [Русский](README-ru.md)
|
|
||||||
|
|
||||||
*Утилита командной строки для манипулирования ini файлами с дублирующимися именами ключей.*
|
|
||||||
|
|
||||||
Скомпилированная версия multini доступна на [странице релизов](https://github.com/GenZmeY/multini/releases).
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
# Описание
|
|
||||||
Некоторые программы используют формат ini файлов допускающий повторяющиеся имена ключей.
|
|
||||||
Например игры основаные на [unreal engine](https://en.wikipedia.org/wiki/Unreal_Engine).
|
|
||||||
Это может выглядеть так (часть конфигурации Killing Floor 2):
|
|
||||||
```
|
|
||||||
[OnlineSubsystemSteamworks.KFWorkshopSteamworks]
|
|
||||||
ServerSubscribedWorkshopItems=2267561023
|
|
||||||
ServerSubscribedWorkshopItems=2085786712
|
|
||||||
ServerSubscribedWorkshopItems=2222630586
|
|
||||||
ServerSubscribedWorkshopItems=2146677560
|
|
||||||
```
|
|
||||||
Большинство реализаций поддерживают только одно свойство с заданным именем в секции. Если их несколько, будет обрабатываться только первый (или последний) ключ, чего в данном случае недостаточно. multini решает эту проблему.
|
|
||||||
|
|
||||||
**примечание:**
|
|
||||||
- multini чувствителен к регистру;
|
|
||||||
- кавычки вокруг значения не обрабатываются (multini считает их частью значения);
|
|
||||||
- многострочные значения не поддерживаются.
|
|
||||||
(но все это может измениться в будущем)
|
|
||||||
|
|
||||||
# Сборка и установка (вручную)
|
|
||||||
1. Установите [golang](https://golang.org), [git](https://git-scm.com/), [make](https://www.gnu.org/software/make/);
|
|
||||||
2. Клонируйте этот репозиторий: `git clone https://github.com/GenZmeY/multini`
|
|
||||||
3. Перейдите в каталог с исходниками: `cd multini`
|
|
||||||
4. Выполните сборку: `make`
|
|
||||||
5. Выполните установку: `make install`
|
|
||||||
|
|
||||||
# Использование
|
|
||||||
```
|
|
||||||
Использование: multini [ПАРАМЕТРЫ]... ДЕЙСТВИЕ ini_file [секция] [ключ] [значение]
|
|
||||||
Действия:
|
|
||||||
-g, --get Получить значения для заданной комбинации параметров.
|
|
||||||
-s, --set Установить значения для заданной комбинации параметров.
|
|
||||||
-a, --add Добавить значения для заданной комбинации параметров.
|
|
||||||
-d, --del Удалить указанную комбинацию параметров.
|
|
||||||
-c, --chk Показать ошибки парсинга указанного файла.
|
|
||||||
|
|
||||||
Параметры:
|
|
||||||
-e, --existing Для --set и --del завершить программу с ошибкой, если элемент остутствует.
|
|
||||||
-r, --reverse Для --add добавлять элемент в начало секции
|
|
||||||
-i, --inplace Перезаписывать исходный файл.
|
|
||||||
Это не атомарно, но требует меньше разрешений
|
|
||||||
чем способ по умолчанию с заменой файла.
|
|
||||||
-o, --output ФАЙЛ Записать результат в ФАЙЛ. '-' означает стандартный вывод
|
|
||||||
-u, --unix Использовать LF в конце строки
|
|
||||||
-w, --windows Использовать CRLF в конце строки
|
|
||||||
-q, --quiet Подавить весь вывод
|
|
||||||
-h, --help Отобразить страницу помощи
|
|
||||||
--version Отобразить версию
|
|
||||||
```
|
|
||||||
|
|
||||||
# Примеры
|
|
||||||
**вывести глобальное значение вне секции:**
|
|
||||||
`multini --get ini_file '' key`
|
|
||||||
|
|
||||||
**вывести секцию:**
|
|
||||||
`multini --get ini_file section`
|
|
||||||
|
|
||||||
**вывести список секций:**
|
|
||||||
`multini --get ini_file`
|
|
||||||
|
|
||||||
**вывести значение:**
|
|
||||||
`multini --get ini_file section key`
|
|
||||||
- если ключей несколько, отобразится список всех значений этих ключей
|
|
||||||
|
|
||||||
**создать/обновить ключ (в единственном экземпляре):**
|
|
||||||
`multini --set ini_file section key value`
|
|
||||||
- если ключа нет, он будет добавлен
|
|
||||||
- если ключ существует, значение будет обновлено
|
|
||||||
- если ключ существует и имеет несколько значений, будет установлен ключ с указанным значением, остальные значения будут удалены
|
|
||||||
|
|
||||||
**добавить ключ с указанным значением:**
|
|
||||||
`multini --add ini_file section key value`
|
|
||||||
- если ключа нет, он будет добавлен
|
|
||||||
- если ключ существует и не имеет указанного значения, будет добавлено новое значение
|
|
||||||
- если указанное значение повторяет существующее, никаких изменений не будет
|
|
||||||
|
|
||||||
**удалить все ключи с указанным именем:**
|
|
||||||
`multini --del ini_file section key`
|
|
||||||
|
|
||||||
**удалить ключ с указанным именем и значением:**
|
|
||||||
`multini --del ini_file section key value`
|
|
||||||
|
|
||||||
**удалить секцию:**
|
|
||||||
`multini --del ini_file section`
|
|
||||||
|
|
||||||
**короткие версии параметров можно комбинировать:**
|
|
||||||
`multini -gq ini_file section key value`
|
|
||||||
- проверить наличие ключа с заданным значением, используя код возврата
|
|
||||||
|
|
||||||
# Лицензия
|
|
||||||
Copyright © 2020 GenZmeY
|
|
||||||
|
|
||||||
[MIT License](LICENSE).
|
|
||||||
|
|
111
README.md
111
README.md
@ -1,111 +0,0 @@
|
|||||||
# Multini
|
|
||||||
|
|
||||||
[](https://github.com/GenZmeY/multini/actions?query=workflow%3Abuild)
|
|
||||||
[](https://github.com/GenZmeY/multini/actions?query=workflow%3Atests)
|
|
||||||
[](https://github.com/GenZmeY/multini/security/code-scanning)
|
|
||||||
[](https://golang.org)
|
|
||||||
[](LICENSE)
|
|
||||||
[](https://github.com/GenZmeY/multini/releases)
|
|
||||||
|
|
||||||
[English](README.md), [Русский](README-ru.md)
|
|
||||||
|
|
||||||
*Command line utility for manipulating ini files with duplicate key names.*
|
|
||||||
|
|
||||||
A compiled version of multini is available on the [release page](https://github.com/GenZmeY/multini/releases).
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
# Description
|
|
||||||
Some programs use ini file format with duplicate key names.
|
|
||||||
For example, these are games based on the [unreal engine](https://en.wikipedia.org/wiki/Unreal_Engine).
|
|
||||||
It might look like this (part of the Killing Floor 2 config):
|
|
||||||
```
|
|
||||||
[OnlineSubsystemSteamworks.KFWorkshopSteamworks]
|
|
||||||
ServerSubscribedWorkshopItems=2267561023
|
|
||||||
ServerSubscribedWorkshopItems=2085786712
|
|
||||||
ServerSubscribedWorkshopItems=2222630586
|
|
||||||
ServerSubscribedWorkshopItems=2146677560
|
|
||||||
```
|
|
||||||
Most implementations only support having one property with a given name in a section. If there are several of them, only the first (or last) key will be processed, which is not enough in this case. multini solves this problem.
|
|
||||||
|
|
||||||
**note:**
|
|
||||||
- multini is case sensitive;
|
|
||||||
- quotes around the value are not processed (they are part of the value for multini);
|
|
||||||
- multi-line values are not supported.
|
|
||||||
(but this may change in the future)
|
|
||||||
|
|
||||||
# Build & Install (Manual)
|
|
||||||
1. Install [golang](https://golang.org), [git](https://git-scm.com/), [make](https://www.gnu.org/software/make/);
|
|
||||||
2. Clone this repo: `git clone https://github.com/GenZmeY/multini`
|
|
||||||
3. Go to the source directory: `cd multini`
|
|
||||||
4. Build: `make`
|
|
||||||
5. Install: `make install`
|
|
||||||
|
|
||||||
# Usage
|
|
||||||
```
|
|
||||||
Usage: multini [OPTION]... ACTION ini_file [section] [key] [value]
|
|
||||||
Actions:
|
|
||||||
-g, --get Get 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.
|
|
||||||
-d, --del Delete the given combination of parameters.
|
|
||||||
-c, --chk Display parsing errors for the specified file.
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-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.
|
|
||||||
This is not atomic but has less restrictions
|
|
||||||
than the default replacement method.
|
|
||||||
-o, --output FILE Write output to FILE instead. '-' means stdout
|
|
||||||
-u, --unix Use LF 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
|
|
||||||
--version Write version to stdout
|
|
||||||
```
|
|
||||||
|
|
||||||
# Examples
|
|
||||||
**output a global value not in a section:**
|
|
||||||
`multini --get ini_file '' key`
|
|
||||||
|
|
||||||
**output section:**
|
|
||||||
`multini --get ini_file section`
|
|
||||||
|
|
||||||
**output list of existing sections:**
|
|
||||||
`multini --get ini_file`
|
|
||||||
|
|
||||||
**output value:**
|
|
||||||
`multini --get ini_file section key`
|
|
||||||
- if there are several keys, a list of all values of this keys will be displayed
|
|
||||||
|
|
||||||
**create/update a single key:**
|
|
||||||
`multini --set ini_file section key value`
|
|
||||||
- if there is no key, it will be added
|
|
||||||
- if the key exists, the value will be updated
|
|
||||||
- if the key exists and has several values, the key with the specified value will be set, the rest of the values will be deleted
|
|
||||||
|
|
||||||
**add a key with specified value:**
|
|
||||||
`multini --add ini_file section key value`
|
|
||||||
- if there is no key, it will be added
|
|
||||||
- if the key exists and does not have the specified value, the new value will be added
|
|
||||||
- if the specified value repeats the existing one, no changes will be made
|
|
||||||
|
|
||||||
**delete all keys with specified name:**
|
|
||||||
`multini --del ini_file section key`
|
|
||||||
|
|
||||||
**delete a key with specified name and value:**
|
|
||||||
`multini --del ini_file section key value`
|
|
||||||
|
|
||||||
**delete a section:**
|
|
||||||
`multini --del ini_file section`
|
|
||||||
|
|
||||||
**short options can be combined:**
|
|
||||||
`multini -gq ini_file section key value`
|
|
||||||
- check the existence of a key with a given value using the return code
|
|
||||||
|
|
||||||
# License
|
|
||||||
Copyright © 2020 GenZmeY
|
|
||||||
|
|
||||||
The content of this repository is licensed under [MIT License](LICENSE).
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"multini/internal/output"
|
"multini/output"
|
||||||
"multini/internal/types"
|
"multini/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func chk() int {
|
func chk() int {
|
@ -4,8 +4,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"multini/internal/output"
|
"multini/output"
|
||||||
"multini/internal/types"
|
"multini/types"
|
||||||
|
|
||||||
"github.com/juju/gnuflag"
|
"github.com/juju/gnuflag"
|
||||||
)
|
)
|
||||||
@ -40,7 +40,7 @@ var (
|
|||||||
func printHelp() {
|
func printHelp() {
|
||||||
output.Println("A utility for manipulating ini files with duplicate keys")
|
output.Println("A utility for manipulating ini files with duplicate keys")
|
||||||
output.Println("")
|
output.Println("")
|
||||||
output.Println("Usage: multini [OPTION]... [ACTION] config_file [section] [key] [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.")
|
||||||
@ -55,6 +55,7 @@ func printHelp() {
|
|||||||
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.")
|
||||||
output.Println(" -o, --output FILE Write output to FILE instead. '-' means stdout")
|
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(" -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(" -q, --quiet Suppress all normal output")
|
||||||
@ -99,7 +100,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseArgs() error {
|
func parseArgs() error {
|
||||||
gnuflag.Parse(false)
|
gnuflag.Parse(true)
|
||||||
|
|
||||||
// info
|
// info
|
||||||
switch {
|
switch {
|
5
go.mod
5
go.mod
@ -1,5 +0,0 @@
|
|||||||
module multini
|
|
||||||
|
|
||||||
go 1.13
|
|
||||||
|
|
||||||
require github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d
|
|
2
go.sum
2
go.sum
@ -1,2 +0,0 @@
|
|||||||
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=
|
|
@ -3,8 +3,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"multini/internal/output"
|
"multini/output"
|
||||||
"multini/internal/types"
|
"multini/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
@ -7,103 +7,49 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"multini/internal/output"
|
"multini/output"
|
||||||
"multini/internal/types"
|
"multini/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Ng - Named Group
|
// Ng - Named Group
|
||||||
NgPrefix string = `prefix`
|
NgPrefix string = `prefix`
|
||||||
NgPostifx string = `postfix`
|
NgPostifx string = `postfix`
|
||||||
NgSection string = `section`
|
NgSection string = `section`
|
||||||
NgKey string = `key`
|
NgKey string = `key`
|
||||||
NgKeyPostfix string = `key_postfix`
|
NgKeyPostfix string = `key_postfix`
|
||||||
NgValue string = `value`
|
NgValue string = `value`
|
||||||
NgValuePrefix string = `value_prefix`
|
NgValuePrefix string = `value_prefix`
|
||||||
NgValuePostfix string = `value_postfix`
|
NgValuePostfix string = `value_postfix`
|
||||||
NgComment string = `comment`
|
NgComment string = `comment`
|
||||||
NgData string = `data`
|
NgCommentPrefix string = `comment_prefix`
|
||||||
|
|
||||||
RxEmpty string = `^(?P<` + NgPrefix + `>\s+)?$`
|
RxBodyPrefix string = `(?P<` + NgPrefix + `>\s+)?`
|
||||||
RxSection string = `^(?P<` + NgPrefix + `>\s+)?\[(?P<` + NgSection + `>[^\]]+)\](?P<` + NgPostifx + `>\s+)?$`
|
RxSectionName string = `\[(?P<` + NgSection + `>.+)\]`
|
||||||
RxKey string = `^(?P<` + NgPrefix + `>\s+)?(?P<` + NgKey + `>.*[^\s]+)(?P<` + NgKeyPostfix + `>\s+)?$`
|
RxKey string = `(?P<` + NgKey + `>(?:[^;#=]+[^\s=;#]|[^;#=]))?`
|
||||||
RxValue string = `^(?P<` + NgValuePrefix + `>\s+)?(?P<` + NgValue + `>.*[^\s])(?P<` + NgValuePostfix + `>\s+)?$`
|
RxKeyPostfix string = `(?P<` + NgKeyPostfix + `>\s+)?`
|
||||||
|
RxValuePrefix string = `(?P<` + NgValuePrefix + `>\s+)?`
|
||||||
RxEmptyCompile *regexp.Regexp = regexp.MustCompile(RxEmpty)
|
RxValue string = `(?P<` + NgValue + `>(?:[^;#]+[^\s;#]|[^;#]))?`
|
||||||
RxSectionCompile *regexp.Regexp = regexp.MustCompile(RxSection)
|
RxValuePostfix string = `(?P<` + NgValuePostfix + `>\s+)?`
|
||||||
RxKeyCompile *regexp.Regexp = regexp.MustCompile(RxKey)
|
RxKeyVal string = RxKey + RxKeyPostfix + `=` + RxValuePrefix + RxValue + RxValuePostfix
|
||||||
RxValueCompile *regexp.Regexp = regexp.MustCompile(RxValue)
|
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 parse(str string) map[string]string {
|
func rxParse(rx *regexp.Regexp, str string) map[string]string {
|
||||||
var result map[string]string = make(map[string]string)
|
match := rx.FindStringSubmatch(str)
|
||||||
var data string
|
result := make(map[string]string)
|
||||||
|
for i, name := range rx.SubexpNames() {
|
||||||
data, result[NgComment] = getDataComment(str)
|
|
||||||
|
|
||||||
if data != "" {
|
|
||||||
findNamedGroups(data, RxEmptyCompile, &result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if result[NgPrefix] != "" {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
findNamedGroups(data, RxSectionCompile, &result)
|
|
||||||
|
|
||||||
if result[NgSection] == "" && data != "" {
|
|
||||||
keyPart, valPart := getKeyValue(data)
|
|
||||||
findNamedGroups(keyPart, RxKeyCompile, &result)
|
|
||||||
findNamedGroups(valPart, RxValueCompile, &result)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func findNamedGroups(str string, Rx *regexp.Regexp, result *map[string]string) {
|
|
||||||
match := Rx.FindStringSubmatch(str)
|
|
||||||
for i, name := range Rx.SubexpNames() {
|
|
||||||
if i != 0 && name != "" && i <= len(match) {
|
if i != 0 && name != "" && i <= len(match) {
|
||||||
(*result)[name] = match[i]
|
result[name] = match[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return result
|
||||||
|
|
||||||
func getDataComment(str string) (string, string) {
|
|
||||||
var indexes []int
|
|
||||||
var commentIndex int = -1
|
|
||||||
|
|
||||||
indexes = append(indexes, strings.Index(str, "//"))
|
|
||||||
indexes = append(indexes, strings.Index(str, "#"))
|
|
||||||
indexes = append(indexes, strings.Index(str, ";"))
|
|
||||||
|
|
||||||
for _, index := range indexes {
|
|
||||||
if commentIndex == -1 {
|
|
||||||
if index != -1 {
|
|
||||||
commentIndex = index
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if index != -1 {
|
|
||||||
if commentIndex > index {
|
|
||||||
commentIndex = index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if commentIndex == -1 {
|
|
||||||
return str, ""
|
|
||||||
} else {
|
|
||||||
return str[:commentIndex], str[commentIndex:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getKeyValue(data string) (string, string) {
|
|
||||||
index := strings.Index(data, "=")
|
|
||||||
if index != -1 {
|
|
||||||
return data[:index], data[index+1:]
|
|
||||||
}
|
|
||||||
return "", ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func debugMap(el map[string]string) string {
|
func debugMap(el map[string]string) string {
|
||||||
@ -115,14 +61,14 @@ func debugMap(el map[string]string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func appendLine(ini *types.Ini, line string) error {
|
func appendLine(ini *types.Ini, line string) error {
|
||||||
// elements := rxParse(line)
|
elements := rxParse(RxCompiled, line)
|
||||||
elements := parse(line)
|
|
||||||
switch {
|
switch {
|
||||||
case elements[NgSection] != "":
|
case elements[NgSection] != "":
|
||||||
var newSection types.Section
|
var newSection types.Section
|
||||||
newSection.Name = elements[NgSection]
|
newSection.Name = elements[NgSection]
|
||||||
newSection.Prefix = elements[NgPrefix]
|
newSection.Prefix = elements[NgPrefix]
|
||||||
newSection.Postfix = elements[NgPostifx]
|
newSection.Postfix = elements[NgPostifx]
|
||||||
|
newSection.Comment.Prefix = elements[NgCommentPrefix]
|
||||||
newSection.Comment.Value = elements[NgComment]
|
newSection.Comment.Value = elements[NgComment]
|
||||||
if newSection.Line() == line {
|
if newSection.Line() == line {
|
||||||
ini.Sections = append(ini.Sections, &newSection)
|
ini.Sections = append(ini.Sections, &newSection)
|
||||||
@ -141,6 +87,7 @@ func appendLine(ini *types.Ini, line string) error {
|
|||||||
newKeyValue.PrefixValue = elements[NgValuePrefix]
|
newKeyValue.PrefixValue = elements[NgValuePrefix]
|
||||||
newKeyValue.PostfixValue = elements[NgValuePostfix]
|
newKeyValue.PostfixValue = elements[NgValuePostfix]
|
||||||
newKeyValue.Comment.Value = elements[NgComment]
|
newKeyValue.Comment.Value = elements[NgComment]
|
||||||
|
newKeyValue.Comment.Prefix = elements[NgCommentPrefix]
|
||||||
if newKeyValue.Line() == line {
|
if newKeyValue.Line() == line {
|
||||||
ini.Sections[len(ini.Sections)-1].(*types.Section).Lines = append(ini.Sections[len(ini.Sections)-1].(*types.Section).Lines, &newKeyValue)
|
ini.Sections[len(ini.Sections)-1].(*types.Section).Lines = append(ini.Sections[len(ini.Sections)-1].(*types.Section).Lines, &newKeyValue)
|
||||||
return nil
|
return nil
|
||||||
@ -151,8 +98,8 @@ func appendLine(ini *types.Ini, line string) error {
|
|||||||
}
|
}
|
||||||
case elements[NgComment] != "":
|
case elements[NgComment] != "":
|
||||||
var newComment types.Comment
|
var newComment types.Comment
|
||||||
|
newComment.Prefix = elements[NgPrefix] + elements[NgCommentPrefix]
|
||||||
newComment.Value = elements[NgComment]
|
newComment.Value = elements[NgComment]
|
||||||
newComment.Prefix = elements[NgPrefix]
|
|
||||||
if newComment.Line() == line {
|
if newComment.Line() == line {
|
||||||
ini.Sections[len(ini.Sections)-1].(*types.Section).Lines = append(ini.Sections[len(ini.Sections)-1].(*types.Section).Lines, &newComment)
|
ini.Sections[len(ini.Sections)-1].(*types.Section).Lines = append(ini.Sections[len(ini.Sections)-1].(*types.Section).Lines, &newComment)
|
||||||
return nil
|
return nil
|
@ -1,11 +1,10 @@
|
|||||||
#!/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/data"
|
TestDir="$ScriptDir/tests"
|
||||||
Multini=$(readlink -e "$1")
|
Multini=$(readlink -e "$1")
|
||||||
|
|
||||||
if [[ -z "$Multini" ]]; then
|
if [[ -z "$Multini" ]]; then
|
@ -1,20 +0,0 @@
|
|||||||
# comment
|
|
||||||
; comment again
|
|
||||||
// comment with indent
|
|
||||||
DefKey1 = Some Value1
|
|
||||||
DefKey2 = Some Value2 And Tabs! # With Comment
|
|
||||||
DefKey3=NoSpaces!
|
|
||||||
|
|
||||||
[SimpleSection] # Comment For Section
|
|
||||||
Key1 = 1
|
|
||||||
Key2 = 2
|
|
||||||
Key3 = 3
|
|
||||||
|
|
||||||
[MultipleKeySection] // C style comment
|
|
||||||
Key = 1
|
|
||||||
Key = 2
|
|
||||||
Key = 3
|
|
||||||
|
|
||||||
[SectionWithIndent]
|
|
||||||
Key=Value
|
|
||||||
[SectionWithoutNewLineBefore]
|
|
@ -1 +0,0 @@
|
|||||||
skip/skip
|
|
@ -1,19 +0,0 @@
|
|||||||
# comment
|
|
||||||
; comment again
|
|
||||||
// comment with indent
|
|
||||||
DefKey1 = Some Value1
|
|
||||||
DefKey2 = Some Value2 And Tabs! # With Comment
|
|
||||||
DefKey3=NoSpaces!
|
|
||||||
|
|
||||||
[SimpleSection] # Comment For Section
|
|
||||||
Key1 = 1
|
|
||||||
Key2 = 2
|
|
||||||
|
|
||||||
[MultipleKeySection] // C style comment
|
|
||||||
Key = 1
|
|
||||||
Key = 2
|
|
||||||
Key = 3
|
|
||||||
|
|
||||||
[SectionWithIndent]
|
|
||||||
Key=Value
|
|
||||||
[SectionWithoutNewLineBefore]
|
|
@ -1,19 +0,0 @@
|
|||||||
# comment
|
|
||||||
; comment again
|
|
||||||
|
|
||||||
DefKey1 = Some Value1
|
|
||||||
DefKey2 = Some Value2 And Tabs! # With Comment
|
|
||||||
DefKey3=NoSpaces!
|
|
||||||
|
|
||||||
[Slashes/Test] // Comment For Section
|
|
||||||
./Dir1/File = skip/skip // comment
|
|
||||||
Key2 = 2
|
|
||||||
|
|
||||||
[MultipleKeySection] // C style comment
|
|
||||||
Key = 1
|
|
||||||
Key = 2
|
|
||||||
Key = 3
|
|
||||||
|
|
||||||
[SectionWithIndent]
|
|
||||||
Key=Value
|
|
||||||
[SectionWithoutNewLineBefore]
|
|
@ -1,7 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
source "common.sh"
|
|
||||||
|
|
||||||
$Multini --get "$InIni" 'Slashes/Test' './Dir1/File' > "$OutIni"
|
|
||||||
|
|
||||||
compare
|
|
20
tests/expected_ini/test_add_keyval.sh.ini
Normal file
20
tests/expected_ini/test_add_keyval.sh.ini
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# comment
|
||||||
|
; comment again
|
||||||
|
|
||||||
|
DefKey1 = Some Value1
|
||||||
|
DefKey2 = Some Value2 And Tabs! # With Comment
|
||||||
|
DefKey3=NoSpaces!
|
||||||
|
|
||||||
|
[SimpleSection] # Comment For Section
|
||||||
|
Key1 = 1
|
||||||
|
Key2 = 2
|
||||||
|
Key3 = 3
|
||||||
|
|
||||||
|
[MultipleKeySection]
|
||||||
|
Key = 1
|
||||||
|
Key = 2
|
||||||
|
Key = 3
|
||||||
|
|
||||||
|
[SectionWithIndent]
|
||||||
|
Key=Value
|
||||||
|
[SectionWithoutNewLineBefore]
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection] // C style comment
|
[MultipleKeySection]
|
||||||
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] // C style comment
|
[MultipleKeySection]
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
||||||
@ -17,5 +17,4 @@ 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] // C style comment
|
[MultipleKeySection]
|
||||||
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] // C style comment
|
[MultipleKeySection]
|
||||||
|
|
||||||
[SectionWithIndent]
|
[SectionWithIndent]
|
||||||
Key=Value
|
Key=Value
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection] // C style comment
|
[MultipleKeySection]
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 3
|
Key = 3
|
||||||
|
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection] // C style comment
|
[MultipleKeySection]
|
||||||
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] // C style comment
|
[MultipleKeySection]
|
||||||
Key = onlyone
|
Key = onlyone
|
||||||
|
|
||||||
[SectionWithIndent]
|
[SectionWithIndent]
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection] // C style comment
|
[MultipleKeySection]
|
||||||
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] // C style comment
|
[MultipleKeySection]
|
||||||
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] // C style comment
|
[MultipleKeySection]
|
||||||
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] // C style comment
|
[MultipleKeySection]
|
||||||
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] // C style comment
|
[MultipleKeySection]
|
||||||
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] // C style comment
|
[MultipleKeySection]
|
||||||
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] // C style comment
|
[MultipleKeySection]
|
||||||
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] // C style comment
|
[MultipleKeySection]
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
19
tests/in_ini/test_get_keyval.sh.ini
Normal file
19
tests/in_ini/test_get_keyval.sh.ini
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# comment
|
||||||
|
; comment again
|
||||||
|
|
||||||
|
DefKey1 = Some Value1
|
||||||
|
DefKey2 = Some Value2 And Tabs! # With Comment
|
||||||
|
DefKey3=NoSpaces!
|
||||||
|
|
||||||
|
[SimpleSection] # Comment For Section
|
||||||
|
Key1 = 1
|
||||||
|
Key2 = 2
|
||||||
|
|
||||||
|
[MultipleKeySection]
|
||||||
|
Key = 1
|
||||||
|
Key = 2
|
||||||
|
Key = 3
|
||||||
|
|
||||||
|
[SectionWithIndent]
|
||||||
|
Key=Value
|
||||||
|
[SectionWithoutNewLineBefore]
|
@ -9,7 +9,7 @@ DefKey3=NoSpaces!
|
|||||||
Key1 = 1
|
Key1 = 1
|
||||||
Key2 = 2
|
Key2 = 2
|
||||||
|
|
||||||
[MultipleKeySection] // C style comment
|
[MultipleKeySection]
|
||||||
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] // C style comment
|
[MultipleKeySection]
|
||||||
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] // C style comment
|
[MultipleKeySection]
|
||||||
Key = 1
|
Key = 1
|
||||||
Key = 2
|
Key = 2
|
||||||
Key = 3
|
Key = 3
|
@ -89,14 +89,6 @@ 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()
|
@ -7,7 +7,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"multini/internal/types"
|
"multini/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Source: https://gist.github.com/var23rav/23ae5d0d4d830aff886c3c970b8f6c6b
|
// Source: https://gist.github.com/var23rav/23ae5d0d4d830aff886c3c970b8f6c6b
|
||||||
@ -114,7 +114,7 @@ func iniWriteInplace(filename string, ini *types.Ini) error {
|
|||||||
}
|
}
|
||||||
mode = info.Mode()
|
mode = info.Mode()
|
||||||
}
|
}
|
||||||
targetFile, err := os.OpenFile(realfilename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
|
targetFile, err := os.OpenFile(realfilename, os.O_WRONLY|os.O_CREATE, mode)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
datawriter := bufio.NewWriter(targetFile)
|
datawriter := bufio.NewWriter(targetFile)
|
||||||
_, err = datawriter.WriteString(ini.Full())
|
_, err = datawriter.WriteString(ini.Full())
|
Loading…
x
Reference in New Issue
Block a user