2026-05-02 00:05:08 +03:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/zemenawi/zutils/pkg/colors"
|
|
|
|
|
"github.com/zemenawi/zutils/pkg/formatter"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func FileInfoCommand(args []string) error {
|
|
|
|
|
if len(args) < 1 {
|
|
|
|
|
return fmt.Errorf("please specify a file path")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filePath := args[0]
|
|
|
|
|
|
|
|
|
|
file, err := os.Open(filePath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error opening file: %w", err)
|
|
|
|
|
}
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
|
|
fileInfo, err := file.Stat()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error getting file info: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if fileInfo.IsDir() {
|
|
|
|
|
return fmt.Errorf("'%s' is a directory, use 'z info dir <path>' instead", filePath)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Count content
|
|
|
|
|
lines, words, chars := countFileContent(file)
|
|
|
|
|
|
|
|
|
|
// Reset file pointer
|
|
|
|
|
file.Seek(0, 0)
|
|
|
|
|
|
|
|
|
|
// Get file info
|
|
|
|
|
fileType := getFileType(filePath)
|
|
|
|
|
tokens := int(float64(words) / 0.75)
|
|
|
|
|
size := formatter.FormatSize(fileInfo.Size())
|
feat: add permission interpreter, junks, ports, usages, network commands
New Commands:
- z network: Show IP addresses, public IP, DNS servers, active connections
- z ports: List listening ports with process/PID info
- z usages: All processes sorted by resource usage with filtering
- z junks: Find and clean junk files (caches, temporary files)
Enhancements:
- Permission interpreter in z info file - shows human-readable permissions
e.g. -rw-rw-r-- → "Owner can read, write; Group can read, write; Other can read"
- z usages now supports filtering: z usages chrome -m -n=50
- Summary section shows total CPU/memory when filtering by name
- Added help entries for all new commands
2026-05-02 01:53:36 +03:00
|
|
|
perms := FormatPermissions(fileInfo.Mode())
|
2026-05-02 00:05:08 +03:00
|
|
|
modTime := fileInfo.ModTime().Format(time.RFC1123)
|
|
|
|
|
|
|
|
|
|
// Print output
|
|
|
|
|
PrintBoxHeader("FILE INFORMATION", colors.Cyan)
|
|
|
|
|
|
|
|
|
|
fmt.Printf("%s%s📁 Path:%s %s\n", colors.Blue, colors.Bold, colors.Reset, filePath)
|
|
|
|
|
fmt.Printf("%s%s📄 Name:%s %s\n", colors.Blue, colors.Bold, colors.Reset, filepath.Base(filePath))
|
|
|
|
|
fmt.Printf("%s%s📏 Size:%s %s\n", colors.Blue, colors.Bold, colors.Reset, size)
|
|
|
|
|
fmt.Printf("%s%s🔖 Type:%s %s\n", colors.Blue, colors.Bold, colors.Reset, fileType)
|
|
|
|
|
fmt.Println()
|
|
|
|
|
|
|
|
|
|
PrintSectionHeader("CONTENT STATISTICS")
|
|
|
|
|
fmt.Printf("%s%s📊 Lines:%s %d\n", colors.Green, colors.Bold, colors.Reset, lines)
|
|
|
|
|
fmt.Printf("%s%s📝 Words:%s %d\n", colors.Green, colors.Bold, colors.Reset, words)
|
|
|
|
|
fmt.Printf("%s%s🔢 Characters:%s %d\n", colors.Green, colors.Bold, colors.Reset, chars)
|
|
|
|
|
fmt.Printf("%s%s🤖 Tokens (est):%s %d\n", colors.Green, colors.Bold, colors.Reset, tokens)
|
|
|
|
|
fmt.Println()
|
|
|
|
|
|
|
|
|
|
PrintSectionHeader("FILE DETAILS")
|
|
|
|
|
fmt.Printf("%s%s🔒 Permissions:%s %s\n", colors.Yellow, colors.Bold, colors.Reset, perms)
|
feat: add permission interpreter, junks, ports, usages, network commands
New Commands:
- z network: Show IP addresses, public IP, DNS servers, active connections
- z ports: List listening ports with process/PID info
- z usages: All processes sorted by resource usage with filtering
- z junks: Find and clean junk files (caches, temporary files)
Enhancements:
- Permission interpreter in z info file - shows human-readable permissions
e.g. -rw-rw-r-- → "Owner can read, write; Group can read, write; Other can read"
- z usages now supports filtering: z usages chrome -m -n=50
- Summary section shows total CPU/memory when filtering by name
- Added help entries for all new commands
2026-05-02 01:53:36 +03:00
|
|
|
fmt.Printf("%s └─ %s%s\n", colors.Gray, colors.Reset, GetPermissionHuman(fileInfo.Mode()))
|
2026-05-02 00:05:08 +03:00
|
|
|
fmt.Printf("%s%s📅 Modified:%s %s\n", colors.Yellow, colors.Bold, colors.Reset, modTime)
|
|
|
|
|
fmt.Printf("%s%s📂 Extension:%s %s\n", colors.Yellow, colors.Bold, colors.Reset, getFileExtension(filePath))
|
|
|
|
|
fmt.Println()
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func countFileContent(file *os.File) (lines, words, chars int) {
|
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
|
|
|
lines = 0
|
|
|
|
|
words = 0
|
|
|
|
|
chars = 0
|
|
|
|
|
|
|
|
|
|
for scanner.Scan() {
|
|
|
|
|
lines++
|
|
|
|
|
line := scanner.Text()
|
|
|
|
|
chars += len(line)
|
|
|
|
|
words += len(strings.Fields(line))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return lines, words, chars
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getFileType(path string) string {
|
|
|
|
|
ext := strings.ToLower(filepath.Ext(path))
|
|
|
|
|
switch ext {
|
|
|
|
|
case ".go":
|
|
|
|
|
return "Go Source"
|
|
|
|
|
case ".js":
|
|
|
|
|
return "JavaScript"
|
|
|
|
|
case ".ts":
|
|
|
|
|
return "TypeScript"
|
|
|
|
|
case ".py":
|
|
|
|
|
return "Python Source"
|
|
|
|
|
case ".java":
|
|
|
|
|
return "Java Source"
|
|
|
|
|
case ".c":
|
|
|
|
|
return "C Source"
|
|
|
|
|
case ".cpp":
|
|
|
|
|
return "C++ Source"
|
|
|
|
|
case ".rs":
|
|
|
|
|
return "Rust Source"
|
|
|
|
|
case ".json":
|
|
|
|
|
return "JSON Data"
|
|
|
|
|
case ".yaml", ".yml":
|
|
|
|
|
return "YAML Data"
|
|
|
|
|
case ".xml":
|
|
|
|
|
return "XML Data"
|
|
|
|
|
case ".html":
|
|
|
|
|
return "HTML Document"
|
|
|
|
|
case ".css":
|
|
|
|
|
return "CSS Stylesheet"
|
|
|
|
|
case ".md":
|
|
|
|
|
return "Markdown"
|
|
|
|
|
case ".txt":
|
|
|
|
|
return "Text File"
|
|
|
|
|
case ".pdf":
|
|
|
|
|
return "PDF Document"
|
|
|
|
|
case ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".svg":
|
|
|
|
|
return "Image File"
|
|
|
|
|
case ".mp3", ".wav", ".ogg":
|
|
|
|
|
return "Audio File"
|
|
|
|
|
case ".mp4", ".avi", ".mkv":
|
|
|
|
|
return "Video File"
|
|
|
|
|
case ".zip", ".tar", ".gz", ".7z", ".rar":
|
|
|
|
|
return "Archive File"
|
|
|
|
|
case ".exe", ".bin":
|
|
|
|
|
return "Binary File"
|
|
|
|
|
default:
|
|
|
|
|
return "Unknown Type"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getFileExtension(path string) string {
|
|
|
|
|
ext := filepath.Ext(path)
|
|
|
|
|
if ext == "" {
|
|
|
|
|
return "none"
|
|
|
|
|
}
|
|
|
|
|
return ext
|
|
|
|
|
}
|
feat: add permission interpreter, junks, ports, usages, network commands
New Commands:
- z network: Show IP addresses, public IP, DNS servers, active connections
- z ports: List listening ports with process/PID info
- z usages: All processes sorted by resource usage with filtering
- z junks: Find and clean junk files (caches, temporary files)
Enhancements:
- Permission interpreter in z info file - shows human-readable permissions
e.g. -rw-rw-r-- → "Owner can read, write; Group can read, write; Other can read"
- z usages now supports filtering: z usages chrome -m -n=50
- Summary section shows total CPU/memory when filtering by name
- Added help entries for all new commands
2026-05-02 01:53:36 +03:00
|
|
|
|
|
|
|
|
type Permission struct {
|
|
|
|
|
Symbol string
|
|
|
|
|
Human string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func InterpretPermissions(mode os.FileMode) []Permission {
|
|
|
|
|
var perms []Permission
|
|
|
|
|
|
|
|
|
|
isDir := mode.IsDir()
|
|
|
|
|
isLink := mode&os.ModeSymlink != 0
|
|
|
|
|
|
|
|
|
|
bits := []uint32{
|
|
|
|
|
uint32(mode >> 6) & 0x7,
|
|
|
|
|
uint32(mode >> 3) & 0x7,
|
|
|
|
|
uint32(mode) & 0x7,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
who := []string{"Owner", "Group", "Other"}
|
|
|
|
|
readable := []string{"read", "write", "execute"}
|
|
|
|
|
|
|
|
|
|
for i, whoStr := range who {
|
|
|
|
|
for j := 0; j < 3; j++ {
|
|
|
|
|
bitVal := (bits[i] >> uint(2-j)) & 1
|
|
|
|
|
symbol := "-"
|
|
|
|
|
human := "no " + readable[j]
|
|
|
|
|
if bitVal == 1 {
|
|
|
|
|
if j == 2 {
|
|
|
|
|
if isDir {
|
|
|
|
|
symbol = "x"
|
|
|
|
|
human = whoStr + " can enter directory"
|
|
|
|
|
} else if isLink {
|
|
|
|
|
symbol = "x"
|
|
|
|
|
human = whoStr + " can follow link"
|
|
|
|
|
} else {
|
|
|
|
|
symbol = "x"
|
|
|
|
|
human = whoStr + " can execute"
|
|
|
|
|
}
|
|
|
|
|
} else if j == 1 {
|
|
|
|
|
symbol = "w"
|
|
|
|
|
human = whoStr + " can write"
|
|
|
|
|
} else {
|
|
|
|
|
symbol = "r"
|
|
|
|
|
human = whoStr + " can read"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
perms = append(perms, Permission{Symbol: symbol, Human: human})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if isDir {
|
|
|
|
|
perms = append([]Permission{{Symbol: "d", Human: "Directory"}}, perms...)
|
|
|
|
|
} else if isLink {
|
|
|
|
|
perms = append([]Permission{{Symbol: "l", Human: "Symbolic Link"}}, perms...)
|
|
|
|
|
} else {
|
|
|
|
|
perms = append([]Permission{{Symbol: "-", Human: "Regular File"}}, perms...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return perms
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func FormatPermissions(mode os.FileMode) string {
|
|
|
|
|
perms := InterpretPermissions(mode)
|
|
|
|
|
var result string
|
|
|
|
|
for _, p := range perms {
|
|
|
|
|
result += p.Symbol
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetPermissionHuman(mode os.FileMode) string {
|
|
|
|
|
perms := InterpretPermissions(mode)
|
|
|
|
|
var parts []string
|
|
|
|
|
var currentWho string
|
|
|
|
|
|
|
|
|
|
for i, p := range perms {
|
|
|
|
|
if i == 0 {
|
|
|
|
|
currentWho = p.Human
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
whoIdx := (i - 1) / 3
|
|
|
|
|
who := []string{"Owner", "Group", "Other"}[whoIdx]
|
|
|
|
|
if p.Symbol != "-" {
|
|
|
|
|
parts = append(parts, who+" "+p.Human[len(who)+1:])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(parts) == 0 {
|
|
|
|
|
return "No permissions"
|
|
|
|
|
}
|
|
|
|
|
return currentWho + ", " + strings.Join(parts, ", ")
|
|
|
|
|
}
|