137 lines
3.4 KiB
Go
137 lines
3.4 KiB
Go
package archiver
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"zipprine/internal/models"
|
|
|
|
"github.com/nwaples/rardecode"
|
|
)
|
|
|
|
// extractRar extracts a RAR archive
|
|
func extractRar(config *models.ExtractConfig) error {
|
|
file, err := os.Open(config.ArchivePath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open RAR file: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
reader, err := rardecode.NewReader(file, "")
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create RAR reader: %w", err)
|
|
}
|
|
|
|
if err := os.MkdirAll(config.DestPath, 0755); err != nil {
|
|
return fmt.Errorf("failed to create destination directory: %w", err)
|
|
}
|
|
|
|
for {
|
|
header, err := reader.Next()
|
|
if err != nil {
|
|
if err.Error() == "EOF" {
|
|
break
|
|
}
|
|
return fmt.Errorf("failed to read RAR entry: %w", err)
|
|
}
|
|
|
|
if header.IsDir {
|
|
targetPath := filepath.Join(config.DestPath, header.Name)
|
|
if err := os.MkdirAll(targetPath, 0755); err != nil {
|
|
return fmt.Errorf("failed to create directory: %w", err)
|
|
}
|
|
continue
|
|
}
|
|
|
|
targetPath := filepath.Join(config.DestPath, header.Name)
|
|
|
|
if _, err := os.Stat(targetPath); err == nil && !config.OverwriteAll {
|
|
fmt.Printf("Skipping existing file: %s\n", header.Name)
|
|
continue
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
|
|
return fmt.Errorf("failed to create parent directory: %w", err)
|
|
}
|
|
|
|
outFile, err := os.Create(targetPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create file %s: %w", header.Name, err)
|
|
}
|
|
|
|
if _, err := outFile.ReadFrom(reader); err != nil {
|
|
outFile.Close()
|
|
return fmt.Errorf("failed to write file %s: %w", header.Name, err)
|
|
}
|
|
outFile.Close()
|
|
|
|
// Set permissions if requested
|
|
if config.PreservePerms {
|
|
if err := os.Chmod(targetPath, header.Mode()); err != nil {
|
|
fmt.Printf("Warning: failed to set permissions for %s: %v\n", header.Name, err)
|
|
}
|
|
}
|
|
|
|
fmt.Printf("Extracted: %s\n", header.Name)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// analyzeRar analyzes a RAR archive and returns information about it
|
|
func analyzeRar(path string) (*models.ArchiveInfo, error) {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to open RAR file: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
fileStat, _ := file.Stat()
|
|
|
|
reader, err := rardecode.NewReader(file, "")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create RAR reader: %w", err)
|
|
}
|
|
|
|
info := &models.ArchiveInfo{
|
|
Type: models.RAR,
|
|
CompressedSize: fileStat.Size(),
|
|
Files: []models.FileInfo{},
|
|
}
|
|
|
|
for {
|
|
header, err := reader.Next()
|
|
if err != nil {
|
|
if err.Error() == "EOF" {
|
|
break
|
|
}
|
|
return nil, fmt.Errorf("failed to read RAR entry: %w", err)
|
|
}
|
|
|
|
if !header.IsDir {
|
|
info.FileCount++
|
|
info.TotalSize += header.UnPackedSize
|
|
info.Files = append(info.Files, models.FileInfo{
|
|
Name: header.Name,
|
|
Size: header.UnPackedSize,
|
|
IsDir: header.IsDir,
|
|
ModTime: header.ModificationTime.Format("2006-01-02 15:04:05"),
|
|
})
|
|
}
|
|
}
|
|
|
|
if info.TotalSize > 0 {
|
|
info.CompressionRatio = float64(info.CompressedSize) / float64(info.TotalSize)
|
|
}
|
|
|
|
return info, nil
|
|
}
|
|
|
|
// Note: RAR compression is proprietary and requires a license.
|
|
// This implementation only supports extraction using the rardecode library.
|
|
// For compression, users should use WinRAR or other licensed tools.
|
|
func createRar(config *models.CompressConfig) error {
|
|
return fmt.Errorf("RAR compression is not supported due to proprietary format. Please use ZIP, TAR, or TAR.GZ for compression")
|
|
}
|