Skip to content

Commit a4ed629

Browse files
committed
Added chgrp command.
1 parent f310f1c commit a4ed629

File tree

4 files changed

+170
-0
lines changed

4 files changed

+170
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. The format
55
## [0.29.00] - 2021-12-15
66
### Added
77
- uuidgen command.
8+
- chgrp command.
89
- dirname command (with integration tests.)
910
### Changed
1011
- mimixbox

docs/introduction/en/CommandAppletList.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
| basename| Print basename (PATH without "/") from file path |
66
| cat | Concatenate files and print on the standard output|
77
| cawsay | Print message with cow's ASCII art|
8+
| chgrp | Change the group of each FILE to GROUP|
89
| chroot | Run command or interactive shell with special root directory|
910
| cp | Copy file(s) otr Directory(s) |
1011
| dirname | Print only directory path |

internal/applets/applet.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"sort"
2424
"strconv"
2525

26+
"github.com/nao1215/mimixbox/internal/applets/fileutils/chgrp"
2627
"github.com/nao1215/mimixbox/internal/applets/fileutils/cp"
2728
"github.com/nao1215/mimixbox/internal/applets/fileutils/ln"
2829
"github.com/nao1215/mimixbox/internal/applets/fileutils/mkdir"
@@ -86,6 +87,7 @@ func init() {
8687
"basename": {basename.Run, "Print basename (PATH without\"/\") from file path"},
8788
"cat": {cat.Run, "Concatenate files and print on the standard output"},
8889
"cowsay": {cowsay.Run, "Print message with cow's ASCII art"},
90+
"chgrp": {chgrp.Run, "Change the group of each FILE to GROUP"},
8991
"chroot": {chroot.Run, "Run command or interactive shell with special root directory"},
9092
"cp": {cp.Run, "Copy file(s) otr Directory(s)"},
9193
"dirname": {dirname.Run, "Print only directory path"},
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
//
2+
// mimixbox/internal/applets/fileutils/chgrp/chgrp.go
3+
//
4+
// Copyright 2021 Naohiro CHIKAMATSU
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
package chgrp
18+
19+
import (
20+
"fmt"
21+
"os"
22+
"os/user"
23+
"path/filepath"
24+
"strconv"
25+
"syscall"
26+
27+
mb "github.com/nao1215/mimixbox/internal/lib"
28+
29+
"github.com/jessevdk/go-flags"
30+
)
31+
32+
const cmdName string = "chgrp"
33+
34+
const version = "1.0.0"
35+
36+
var osExit = os.Exit
37+
38+
// Exit code
39+
const (
40+
ExitSuccess int = iota // 0
41+
ExitFailuer
42+
)
43+
44+
type groupInfo struct {
45+
group string
46+
files []string
47+
}
48+
49+
type options struct {
50+
Recursive bool `short:"R" long:"recursive" description:"Change file group IDs recursively"`
51+
Version bool `short:"v" long:"version" description:"Show chgrp command version"`
52+
}
53+
54+
func Run() (int, error) {
55+
var opts options
56+
var args []string
57+
var err error
58+
59+
if args, err = parseArgs(&opts); err != nil {
60+
return ExitFailuer, nil
61+
}
62+
63+
groupInfo := groupInfo{args[0], args[1:]}
64+
return chgrp(groupInfo, opts)
65+
}
66+
67+
func chgrp(gInfo groupInfo, opts options) (int, error) {
68+
gid, err := lookupGid(gInfo.group)
69+
if err != nil {
70+
return ExitFailuer, err
71+
}
72+
73+
status := ExitSuccess
74+
for _, path := range gInfo.files {
75+
path = os.ExpandEnv(path)
76+
if opts.Recursive {
77+
if err := changeGroupRecursive(path, gid); err != nil {
78+
status = ExitFailuer
79+
fmt.Fprintln(os.Stderr, cmdName+": "+err.Error())
80+
continue
81+
}
82+
} else {
83+
if err := changeGroup(path, gid); err != nil {
84+
status = ExitFailuer
85+
fmt.Fprintln(os.Stderr, cmdName+": "+err.Error())
86+
continue
87+
}
88+
}
89+
90+
}
91+
return status, nil
92+
}
93+
94+
func changeGroupRecursive(path string, gid int) error {
95+
err := filepath.Walk(path, func(p string, info os.FileInfo, err error) error {
96+
if err != nil {
97+
return err
98+
}
99+
if err := changeGroup(p, gid); err != nil {
100+
return err
101+
}
102+
return nil
103+
})
104+
return err
105+
}
106+
107+
func changeGroup(path string, gid int) error {
108+
var st syscall.Stat_t
109+
if err := syscall.Stat(path, &st); err != nil {
110+
return err
111+
}
112+
return os.Chown(path, int(st.Uid), gid)
113+
}
114+
115+
func lookupGid(gidFromUserInput string) (int, error) {
116+
group, err := user.LookupGroupId(gidFromUserInput)
117+
if err != nil {
118+
group, err = user.LookupGroup(gidFromUserInput)
119+
if err != nil {
120+
return 0, err
121+
}
122+
}
123+
124+
gid, err := strconv.Atoi(group.Gid)
125+
if err != nil {
126+
return 0, err
127+
}
128+
129+
return gid, nil
130+
}
131+
132+
func parseArgs(opts *options) ([]string, error) {
133+
p := initParser(opts)
134+
135+
args, err := p.Parse()
136+
if err != nil {
137+
return nil, err
138+
}
139+
140+
if opts.Version {
141+
mb.ShowVersion(cmdName, version)
142+
osExit(ExitSuccess)
143+
}
144+
145+
if !isValidArgNr(args) {
146+
if len(args) == 0 {
147+
fmt.Fprintln(os.Stderr, cmdName+": no operand")
148+
} else if len(args) == 1 {
149+
fmt.Fprintln(os.Stderr, cmdName+": no operand after "+args[0])
150+
}
151+
osExit(ExitFailuer)
152+
}
153+
return args, nil
154+
}
155+
156+
func initParser(opts *options) *flags.Parser {
157+
parser := flags.NewParser(opts, flags.Default)
158+
parser.Name = cmdName
159+
parser.Usage = "[OPTIONS] GROUP FILES"
160+
161+
return parser
162+
}
163+
164+
func isValidArgNr(args []string) bool {
165+
return len(args) >= 2
166+
}

0 commit comments

Comments
 (0)