Init.
This commit is contained in:
commit
07ab12b271
4 changed files with 277 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/tests
|
18
main.go
Normal file
18
main.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"git.lattuga.net/blallo/ruspa/sizer"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var path string
|
||||
var debug bool
|
||||
flag.StringVar(&path, "path", ".", "The base path.")
|
||||
flag.BoolVar(&debug, "debug", false, "Toggle debug messages.")
|
||||
flag.Parse()
|
||||
sizer.Debug = debug
|
||||
rootINode := sizer.NewINode(sizer.FileTypes["dir"], 0, path)
|
||||
rootINode.Sizer()
|
||||
}
|
61
sizer/logger.go
Normal file
61
sizer/logger.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package sizer
|
||||
|
||||
import "log"
|
||||
|
||||
// Debug is a global bool to toggle debugging messages.
|
||||
var Debug bool
|
||||
|
||||
type console log.Logger
|
||||
|
||||
func (l *console) SetFlags(i int) {
|
||||
log.SetFlags(i)
|
||||
}
|
||||
|
||||
func (l *console) Print(v ...interface{}) {
|
||||
log.Print(v...)
|
||||
}
|
||||
|
||||
func (l *console) Printf(format string, v ...interface{}) {
|
||||
log.Printf(format, v...)
|
||||
}
|
||||
|
||||
func (l *console) Println(v ...interface{}) {
|
||||
log.Println(v...)
|
||||
}
|
||||
|
||||
func (l *console) Fatal(v ...interface{}) {
|
||||
log.Fatal(v...)
|
||||
}
|
||||
|
||||
func (l *console) Fatalf(format string, v ...interface{}) {
|
||||
log.Fatalf(format, v...)
|
||||
}
|
||||
|
||||
func (l *console) Fatalln(v ...interface{}) {
|
||||
log.Fatalln(v...)
|
||||
}
|
||||
|
||||
func (l *console) Debug(msg ...interface{}) {
|
||||
if Debug {
|
||||
l.Print(msg...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *console) Debugf(format string, msg ...interface{}) {
|
||||
if Debug {
|
||||
l.Printf(format, msg...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *console) Debugln(msg ...interface{}) {
|
||||
if Debug {
|
||||
l.Println(msg...)
|
||||
}
|
||||
}
|
||||
|
||||
// Console is the logger object
|
||||
var Console console
|
||||
|
||||
func init() {
|
||||
Console.SetFlags(0)
|
||||
}
|
197
sizer/main.go
Normal file
197
sizer/main.go
Normal file
|
@ -0,0 +1,197 @@
|
|||
package sizer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// FileType is a const representing a file
|
||||
FileType = iota
|
||||
// DirType is a const representing a directory
|
||||
DirType = iota
|
||||
// SymLinkType is a const representing a symbolic link
|
||||
SymLinkType = iota
|
||||
// HardLinkType is a const representing a hard link
|
||||
HardLinkType = iota
|
||||
// CharDevType is a const representing a char device
|
||||
CharDevType = iota
|
||||
// NamedPipeType is a const representing a named pipe
|
||||
NamedPipeType = iota
|
||||
// SocketType is a const representing a socket, both unix and network
|
||||
SocketType = iota
|
||||
)
|
||||
|
||||
// FileTypes is a map containing the above defined types
|
||||
var FileTypes = map[string]int{
|
||||
"file": FileType,
|
||||
"dir": DirType,
|
||||
"slnk": SymLinkType,
|
||||
"hlnk": HardLinkType,
|
||||
"char": CharDevType,
|
||||
"pipe": NamedPipeType,
|
||||
"sock": SocketType,
|
||||
}
|
||||
|
||||
// INode is an entry in the filesystem. It can be:
|
||||
// - a file
|
||||
// - a directory
|
||||
// - a symbolic link
|
||||
// - a hard link
|
||||
// - a char device/named pipe/socket
|
||||
// It takes a Kind (as above described), a Size and a Path
|
||||
type INode struct {
|
||||
Kind int
|
||||
Size int64
|
||||
Path string
|
||||
Children *INodeList
|
||||
}
|
||||
|
||||
// INodeList is a simple *INode slice with a Mutex to allow safe
|
||||
// concurrent write.
|
||||
type INodeList struct {
|
||||
Elements []*INode
|
||||
safe sync.Mutex
|
||||
}
|
||||
|
||||
type statusReport struct {
|
||||
path string
|
||||
err error
|
||||
}
|
||||
|
||||
// Append appends safely an inode to the INodeList.
|
||||
func (il *INodeList) Append(node *INode) {
|
||||
il.safe.Lock()
|
||||
il.Elements = append(il.Elements, node)
|
||||
il.safe.Unlock()
|
||||
}
|
||||
|
||||
// INodeChan is a channel global to the sizer package that is used
|
||||
// to funnel the retrieved sizez and infos on the inodes.
|
||||
var INodeChan chan *INode
|
||||
|
||||
// ErrNotIdenfiedType is the error returned when the a classifier does not
|
||||
// recognize the INode type as one present in FileTypes.
|
||||
var ErrNotIdenfiedType = errors.New("inode not identified")
|
||||
|
||||
// NewINode returns a new *INode with kind, size and path
|
||||
func NewINode(kind int, size int64, path string) *INode {
|
||||
var children INodeList
|
||||
abspath, _ := filepath.Abs(path)
|
||||
var newINode = INode{
|
||||
Kind: kind,
|
||||
Size: size,
|
||||
Path: abspath,
|
||||
Children: &children,
|
||||
}
|
||||
return &newINode
|
||||
}
|
||||
|
||||
// Name returns the name of the file at the INode.
|
||||
func (i *INode) Name() string {
|
||||
_, name := filepath.Split(i.Path)
|
||||
return name
|
||||
}
|
||||
|
||||
// IdentifyType classifies a file-like object in one of the FileTypes.
|
||||
func IdentifyType(path string) (int, error) {
|
||||
return -1, ErrNotIdenfiedType
|
||||
}
|
||||
|
||||
func ls(path string) ([]string, error) {
|
||||
var content []string
|
||||
f, err := os.Open(path)
|
||||
defer f.Close()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
files, err := f.Readdir(-1)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
for _, fi := range files {
|
||||
content = append(content, filepath.Join(path, fi.Name()))
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func runThrough(i *INode, ch chan int64, report chan statusReport, content []string) {
|
||||
Console.Debugln("Entering:", i.Path)
|
||||
for _, filepath := range content {
|
||||
f, err := os.Lstat(filepath)
|
||||
if err != nil {
|
||||
report <- statusReport{path: i.Path, err: err}
|
||||
}
|
||||
switch mode := f.Mode(); {
|
||||
case mode.IsDir():
|
||||
Console.Debugln(filepath, "is a directory")
|
||||
dirINode := NewINode(FileTypes["dir"], f.Size(), filepath)
|
||||
i.Children.Append(dirINode)
|
||||
ch <- f.Size()
|
||||
go dirINode.walkDir(report)
|
||||
case mode.IsRegular():
|
||||
Console.Debugln(filepath, "is a regular file")
|
||||
fileINode := NewINode(FileTypes["file"], f.Size(), filepath)
|
||||
i.Children.Append(fileINode)
|
||||
ch <- f.Size()
|
||||
default:
|
||||
Console.Debugln(filepath, "is NOT a regular file")
|
||||
var otherINode *INode
|
||||
inodeType, err := IdentifyType(filepath)
|
||||
if err != nil {
|
||||
if err != ErrNotIdenfiedType {
|
||||
report <- statusReport{path: filepath, err: err}
|
||||
return // Is this necessary?
|
||||
}
|
||||
otherINode = NewINode(-1, 0, filepath)
|
||||
} else {
|
||||
otherINode = NewINode(inodeType, 0, filepath)
|
||||
}
|
||||
i.Children.Append(otherINode)
|
||||
}
|
||||
}
|
||||
report <- statusReport{path: i.Path, err: nil}
|
||||
}
|
||||
|
||||
func (i *INode) walkDir(reportUp chan statusReport) {
|
||||
ch := make(chan int64)
|
||||
report := make(chan statusReport)
|
||||
content, err := ls(i.Path)
|
||||
if err != nil {
|
||||
Console.Fatal(err)
|
||||
}
|
||||
go runThrough(i, ch, report, content)
|
||||
for {
|
||||
select {
|
||||
case size := <-ch:
|
||||
i.Size += size
|
||||
case status := <-report:
|
||||
if status.err != nil {
|
||||
reportUp <- status
|
||||
}
|
||||
if status.path == i.Path {
|
||||
reportUp <- status
|
||||
close(reportUp)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sizer is the main entrypoint for the computation of the size of a directory.
|
||||
func (i *INode) Sizer() {
|
||||
if i.Kind == FileTypes["file"] {
|
||||
Console.Println("This is not a directory!")
|
||||
return
|
||||
}
|
||||
report := make(chan statusReport)
|
||||
i.walkDir(report)
|
||||
for r := range report {
|
||||
if r.err != nil {
|
||||
Console.Fatalf("%s: %s", r.path, r.err)
|
||||
}
|
||||
Console.Printf("%s: %d", i.Path, i.Size)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue