Update a configuration file: Difference between revisions

Content deleted Content added
→‎{{header|TXR}}: [minor] Stylistic consistency.
add Go version
Line 348: Line 348:
# How many bananas we have
# How many bananas we have
NUMBEROFBANANAS 48</lang>
NUMBEROFBANANAS 48</lang>
=={{header|Go}}==
<lang go>package main

import (
"bufio"
"fmt"
"io"
"log"
"os"
"strings"
"unicode"
)

// line represents a single line in the configuration file.
type line struct {
kind lineKind
option string
value string
disabled bool
}

// lineKind represents the different kinds of configuration line.
type lineKind int

const (
_ lineKind = iota
ignore
parseError
comment
blank
value
)

func (l line) String() string {
switch l.kind {
case ignore, parseError, comment, blank:
return l.value
case value:
s := l.option
if l.disabled {
s = "; " + s
}
if l.value != "" {
s += " " + l.value
}
return s
}
panic("unexpected line kind")
}

func removeDross(s string) string {
return strings.Map(func(r rune) rune {
if r < 32 || r > 0x7f || unicode.IsControl(r) {
return -1
}
return r
}, s)
}

func parseLine(s string) line {
if s == "" {
return line{kind: blank}
}
if s[0] == '#' {
return line{kind: comment, value: s}
}
s = removeDross(s)
fields := strings.Fields(s)
if len(fields) == 0 {
return line{kind: blank}
}
// Strip leading semicolons (but record that we found them)
semi := false
for len(fields[0]) > 0 && fields[0][0] == ';' {
semi = true
fields[0] = fields[0][1:]
}
// Lose the first field if it was all semicolons
if fields[0] == "" {
fields = fields[1:]
}
switch len(fields) {
case 0:
// This can only happen if the line starts
// with a semicolon but has no other information
return line{kind: ignore}
case 1:
return line{
kind: value,
option: strings.ToUpper(fields[0]),
disabled: semi,
}
case 2:
return line{
kind: value,
option: strings.ToUpper(fields[0]),
value: fields[1],
disabled: semi,
}
}
return line{kind: parseError, value: s}
}

// Config represents a "standard" configuration file.
type Config struct {
options map[string]int // index of each option in lines.
lines []line
}

// index returns the index of the given option in
// c.lines, or -1 if not found.
func (c *Config) index(option string) int {
if i, ok := c.options[option]; ok {
return i
}
return -1
}

// addLine adds a line to the config, ignoring
// duplicate options and "ignore" lines.
func (c *Config) addLine(l line) {
switch l.kind {
case ignore:
return
case value:
if c.index(l.option) >= 0 {
return
}
c.options[l.option] = len(c.lines)
c.lines = append(c.lines, l)
default:
c.lines = append(c.lines, l)
}
}

// ReadConfig reads a configuration file from path and returns it.
func ReadConfig(path string) (*Config, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
r := bufio.NewReader(f)
c := &Config{options: make(map[string]int)}
for {
s, err := r.ReadString('\n')
if s != "" {
if err == nil {
// strip newline unless we encountered an error without finding one.
s = s[:len(s)-1]
}
c.addLine(parseLine(s))
}
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
}
return c, nil
}

// Set sets an option to a value, adding the option if necessary. If
// the option was previously disabled, it will be enabled.
func (c *Config) Set(option string, val string) {
if i := c.index(option); i >= 0 {
line := &c.lines[i]
line.disabled = false
line.value = val
return
}
c.addLine(line{
kind: value,
option: option,
value: val,
})
}

// Enable sets the enabled status of an option. It is
// ignored if the option does not already exist.
func (c *Config) Enable(option string, enabled bool) {
if i := c.index(option); i >= 0 {
c.lines[i].disabled = !enabled
}
}

// Write writes the configuration file to the writer.
func (c *Config) Write(w io.Writer) {
for _, line := range c.lines {
fmt.Println(line)
}
}

func main() {
c, err := ReadConfig("/tmp/cfg")
if err != nil {
log.Fatalln(err)
}
c.Enable("NEEDSPEELING", false)
c.Set("SEEDSREMOVED", "")
c.Set("NUMBEROFBANANAS", "1024")
c.Set("NUMBEROFSTRAWBERRIES", "62000")
c.Write(os.Stdout)
}</lang>


=={{header|PicoLisp}}==
=={{header|PicoLisp}}==