Skip to content

Commit ffaddf3

Browse files
committed
Merge branch 'master' into hw09_struct_validator
2 parents 61f97f7 + 4ad7aa6 commit ffaddf3

File tree

11 files changed

+280
-10
lines changed

11 files changed

+280
-10
lines changed

hw08_envdir_tool/.sync

Whitespace-only changes.

hw08_envdir_tool/env_reader.go

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
package main
22

3+
import (
4+
"bufio"
5+
"bytes"
6+
"errors"
7+
"fmt"
8+
"log"
9+
"os"
10+
"strings"
11+
"sync"
12+
)
13+
14+
var (
15+
ErrInvalidDir = errors.New("invalid directory")
16+
ErrEmptyDir = errors.New("empty directory")
17+
)
18+
319
type Environment map[string]EnvValue
420

521
// EnvValue helps to distinguish between empty files and files with the first empty line.
@@ -8,9 +24,84 @@ type EnvValue struct {
824
NeedRemove bool
925
}
1026

27+
var mu sync.Mutex
28+
1129
// ReadDir reads a specified directory and returns map of env variables.
1230
// Variables represented as files where filename is name of variable, file first line is a value.
1331
func ReadDir(dir string) (Environment, error) {
14-
// Place your code here
15-
return nil, nil
32+
osDir, err := os.ReadDir(dir)
33+
if err != nil {
34+
return nil, ErrInvalidDir
35+
}
36+
37+
if len(osDir) == 0 {
38+
return nil, ErrEmptyDir
39+
}
40+
41+
wg := sync.WaitGroup{}
42+
osEnv := make(Environment)
43+
44+
for _, file := range osDir {
45+
wg.Add(1)
46+
go processFile(dir, file, osEnv, &wg)
47+
}
48+
49+
wg.Wait()
50+
return osEnv, nil
51+
}
52+
53+
func processFile(dir string, file os.DirEntry, osEnv Environment, wg *sync.WaitGroup) {
54+
defer wg.Done()
55+
var envValue EnvValue
56+
57+
osFile, err := os.Open(dir + "/" + file.Name())
58+
if err != nil {
59+
fmt.Println("Couldn't open file", err)
60+
return
61+
}
62+
defer closeFile(osFile)
63+
64+
scanner := bufio.NewScanner(osFile)
65+
66+
if file.Name() == "UNSET" {
67+
envValue.NeedRemove = true
68+
envValue.Value = ""
69+
mu.Lock()
70+
osEnv[file.Name()] = envValue
71+
mu.Unlock()
72+
return
73+
}
74+
75+
for scanner.Scan() {
76+
processLine(scanner, &envValue)
77+
mu.Lock()
78+
osEnv[file.Name()] = envValue
79+
mu.Unlock()
80+
break
81+
}
82+
83+
if err := scanner.Err(); err != nil {
84+
log.Println("Scanner error:", err)
85+
}
86+
}
87+
88+
func closeFile(osFile *os.File) {
89+
err := osFile.Close()
90+
if err != nil {
91+
fmt.Println("Couldn't close file")
92+
}
93+
}
94+
95+
func processLine(scanner *bufio.Scanner, envValue *EnvValue) {
96+
nullByte := make([]byte, 1)
97+
nullByte[0] = 0
98+
text := string(bytes.ReplaceAll([]byte(scanner.Text()), nullByte, []byte("\n")))
99+
100+
if len(strings.TrimSpace(scanner.Text())) > 0 {
101+
envValue.NeedRemove = false
102+
} else {
103+
envValue.NeedRemove = true
104+
}
105+
106+
envValue.Value = text
16107
}
Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,36 @@
11
package main
22

3-
import "testing"
3+
import (
4+
"errors"
5+
"os"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
)
410

511
func TestReadDir(t *testing.T) {
6-
// Place your code here
12+
t.Run("wrong dir", func(t *testing.T) {
13+
dir := "./test/env"
14+
_, err := ReadDir(dir)
15+
require.Truef(t, errors.Is(err, ErrInvalidDir), "actual err - %v", err)
16+
})
17+
18+
t.Run("no empty values", func(t *testing.T) {
19+
dir := "./testdata/env2"
20+
env, err := ReadDir(dir)
21+
require.NoError(t, err)
22+
for key, val := range env {
23+
require.Truef(t, val.Value != "", "error empty value: %v", key)
24+
}
25+
})
26+
27+
t.Run("empty directory", func(t *testing.T) {
28+
dir := "./testdata/empty"
29+
30+
_ = os.Mkdir(dir, 0o755)
31+
_, err := ReadDir(dir)
32+
require.Truef(t, errors.Is(err, ErrEmptyDir), "actual err - %v", err)
33+
34+
_ = os.Remove(dir)
35+
})
736
}

hw08_envdir_tool/executor.go

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,78 @@
11
package main
22

3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
"os/exec"
8+
"strings"
9+
)
10+
311
// RunCmd runs a command + arguments (cmd) with environment variables from env.
412
func RunCmd(cmd []string, env Environment) (returnCode int) {
5-
// Place your code here.
6-
return
13+
var err error
14+
if len(cmd) == 0 {
15+
fmt.Println("Error: no command provided")
16+
return 1
17+
}
18+
19+
safeCmd, err := sanitizeInput(cmd)
20+
if err != nil {
21+
fmt.Println("Error:", err)
22+
return 1
23+
}
24+
25+
command := exec.Command(safeCmd[0], safeCmd[1:]...) //nolint:gosec
26+
command.Env = os.Environ()
27+
28+
for name, value := range env {
29+
if value.NeedRemove {
30+
command.Env = removeEnv(command.Env, name)
31+
} else {
32+
cleanValue := strings.ReplaceAll(value.Value, "\x00", "")
33+
if strings.Trim(name, " ") == "" {
34+
command.Env = append(command.Env, cleanValue)
35+
} else {
36+
command.Env = append(command.Env, name+"="+cleanValue)
37+
}
38+
}
39+
}
40+
41+
command.Stdin = os.Stdin
42+
command.Stdout = os.Stdout
43+
command.Stderr = os.Stderr
44+
45+
err = command.Start()
46+
if err != nil {
47+
fmt.Println("Error starting command:", err)
48+
return 1
49+
}
50+
51+
err = command.Wait()
52+
if err != nil {
53+
fmt.Println("Error waiting for command:", err)
54+
return 1
55+
}
56+
57+
return command.ProcessState.ExitCode()
58+
}
59+
60+
func sanitizeInput(input []string) ([]string, error) {
61+
for _, arg := range input {
62+
if strings.ContainsAny(arg, `;&|<>`) {
63+
return nil, errors.New("invalid input")
64+
}
65+
}
66+
return input, nil
67+
}
68+
69+
func removeEnv(env []string, name string) []string {
70+
result := make([]string, 0, len(env))
71+
prefix := name + "="
72+
for _, e := range env {
73+
if len(e) > len(prefix) && e[:len(prefix)] != prefix {
74+
result = append(result, e)
75+
}
76+
}
77+
return result
778
}

hw08_envdir_tool/executor_test.go

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,45 @@
11
package main
22

3-
import "testing"
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
48

59
func TestRunCmd(t *testing.T) {
6-
// Place your code here
10+
t.Run("no command provided", func(t *testing.T) {
11+
cmd := []string{}
12+
env := Environment{}
13+
returnCode := RunCmd(cmd, env)
14+
require.Equal(t, 1, returnCode)
15+
})
16+
17+
t.Run("command with environment variables", func(t *testing.T) {
18+
cmd := []string{"env"}
19+
env := Environment{
20+
"FOO": {Value: "foo", NeedRemove: false},
21+
"BAR": {Value: "bar", NeedRemove: false},
22+
"UNSET": {Value: "", NeedRemove: true},
23+
}
24+
returnCode := RunCmd(cmd, env)
25+
require.Equal(t, 0, returnCode)
26+
})
27+
28+
t.Run("command with empty environment variable", func(t *testing.T) {
29+
cmd := []string{"env"}
30+
env := Environment{
31+
"EMPTY": {Value: "", NeedRemove: false},
32+
}
33+
returnCode := RunCmd(cmd, env)
34+
require.Equal(t, 0, returnCode)
35+
})
36+
37+
t.Run("command with environment variable to remove", func(t *testing.T) {
38+
cmd := []string{"env"}
39+
env := Environment{
40+
"REMOVE_ME": {Value: "", NeedRemove: true},
41+
}
42+
returnCode := RunCmd(cmd, env)
43+
require.Equal(t, 0, returnCode)
44+
})
745
}

hw08_envdir_tool/go.mod

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1-
module github.com/fixme_my_friend/hw08_envdir_tool
1+
module github.com/DEMAxx/demin/hw08_envdir_tool
22

33
go 1.22
4+
5+
require github.com/stretchr/testify v1.10.0
6+
7+
require (
8+
github.com/davecgh/go-spew v1.1.1 // indirect
9+
github.com/pmezard/go-difflib v1.0.0 // indirect
10+
gopkg.in/yaml.v3 v3.0.1 // indirect
11+
)

hw08_envdir_tool/go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
6+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
7+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
8+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
9+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
10+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

hw08_envdir_tool/main.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
package main
22

3+
import (
4+
"fmt"
5+
"os"
6+
)
7+
38
func main() {
4-
// Place your code here.
9+
if len(os.Args) < 3 {
10+
fmt.Printf("Usage: %s /path/to/env/dir command [args...]\n", os.Args[0])
11+
os.Exit(1)
12+
}
13+
14+
envDir := os.Args[1]
15+
command := os.Args[2:]
16+
17+
env, err := ReadDir(envDir)
18+
if err != nil {
19+
fmt.Printf("Error reading environment directory: %s\n", err)
20+
os.Exit(1)
21+
}
22+
23+
code := RunCmd(command, env)
24+
os.Exit(code)
525
}

hw08_envdir_tool/testdata/env2/BAR

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bar
2+
PLEASE IGNORE SECOND LINE

hw08_envdir_tool/testdata/env2/FOO

20 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)