-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcopy.go
More file actions
200 lines (169 loc) · 5.04 KB
/
Copy pathcopy.go
File metadata and controls
200 lines (169 loc) · 5.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// copy.go
package main
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"unicode"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/storage"
log "github.com/schollz/logger"
)
// sanitizeName strips invisible/non-graphic/non-print runes (ZWSP U+200B,
// ZWNJ U+200C, ZWJ U+200D, BOM U+FEFF, control characters) from a filename.
// These characters commonly appear in names produced by iOS Voice Memos /
// Telegram exports and break croc's ValidFileName validation. All other
// characters (CJK, Cyrillic, emoji, spaces, '.', '-', '_') are preserved.
func sanitizeFileName(s string) string {
var b strings.Builder
b.Grow(len(s))
for _, r := range s {
if unicode.IsGraphic(r) && unicode.IsPrint(r) {
b.WriteRune(r)
}
}
return b.String()
}
func Rename(src, dst string) error {
if noRename {
return fmt.Errorf("no rename")
}
if _, err := os.Stat(src); err != nil {
return err
}
// Check that dst is not a subdirectory of src
srcAbs, err := filepath.Abs(src)
if err != nil {
return err
}
dstAbs, err := filepath.Abs(dst)
if err != nil {
return err
}
if strings.HasPrefix(dstAbs, srcAbs+string(filepath.Separator)) {
return errors.New("destination cannot be inside source directory")
}
// Try standard rename first
if err := os.Rename(src, dst); err == nil {
return nil
} else {
return err
}
}
// Тип функции для копирования файла
type CopyFile func(srcURI fyne.URI, dstPath string) error
func copyFiles(srcURI fyne.URI, dstDir string, copyFile CopyFile) error {
// Для отслеживания циклов
var visited sync.Map
deep := 0
var walk func(current fyne.URI, currentRelPath string) error
// Определяем walk внутри copyFiles, чтобы она имела доступ к visited, dstDir, и copyFile
walk = func(current fyne.URI, currentRelPath string) error {
currentStr := current.String()
// Проверяем - Load безопасен для конкурентного доступа
if _, loaded := visited.Load(currentStr); loaded {
return fmt.Errorf("walk visited %s", currentStr)
}
var finalRelPath string
if deep == 0 {
finalRelPath = currentRelPath
} else {
finalRelPath = filepath.Join(currentRelPath, uriBase(current))
}
var dstPath string
if finalRelPath == "" {
dstPath = dstDir
} else {
dstPath = filepath.Join(dstDir, finalRelPath)
}
// log.Debugf("walk:\ncurrent\t%s\ndstDir\t%s\nrelPath\t%s\ndstPath\t%s\ndeep\t%v", current, dstDir, currentRelPath, dstPath, deep)
deep++
defer func() { deep-- }()
if IsDirectory(current) {
// Сохраняем - Store безопасен для конкурентного доступа
visited.Store(currentStr, true)
if isAndroid && deep > 1 {
return fmt.Errorf("walk deep %d", deep)
}
children, err := List(current)
if err != nil {
return fmt.Errorf("walk list %s: %w", current, err)
}
count := len(children)
if count == 0 {
return fmt.Errorf("count == 0")
}
log.Debugf("walk list %s: %d", current, count)
// Вычисляем relPath для дочерних элементов относительно dstDir
relPathForChildDir, errRel := filepath.Rel(dstDir, dstPath)
if errRel != nil {
return fmt.Errorf("walk rel %s: %w", dstPath, errRel)
}
for _, child := range children {
if child.String() == current.String() {
log.Debugf("walk skipping %s", child)
continue
}
if err := walk(child, relPathForChildDir); err != nil {
log.Errorf("walk %s walk %s: %v", current, child, err)
// return err
// Продолжаем обработку других детей
}
}
return nil
}
// Это файл
dir := filepath.Dir(dstPath)
if err := os.MkdirAll(dir, 0700); err != nil {
return fmt.Errorf("walk MkdirAll: %w", err)
}
if err := copyFile(current, dstPath); err != nil {
return fmt.Errorf("walk copyFile %s %s: %w", current, dstPath, err)
} else {
log.Debugf("walk copyFile %s %s", current, dstPath)
}
return nil
}
// Проверяем srcURI сначала, чтобы определить, файл это или каталог, до запуска рекурсии.
if IsDirectory(srcURI) {
if err := os.MkdirAll(dstDir, 0700); err != nil {
return fmt.Errorf("mkDirAll: %w", err)
}
return walk(srcURI, "")
}
log.Debugf("copyFile %s %s", srcURI, dstDir)
return copyFile(srcURI, dstDir)
}
// func canList(u fyne.URI) bool {
// ok, err := storage.CanList(u)
// if err != nil {
// log.Errorf("CanList error: %v", err)
// return false
// }
// if !ok {
// return false
// }
// log.Debug("CanList")
// items, err := storage.List(u)
// if err != nil {
// log.Errorf("List error: %v", err)
// return false
// }
// log.Debugf("List %d", len(items))
// return true
// }
func storageChild(uri fyne.URI) (isDir bool, childCount int, err error) {
if uri == nil {
return false, 0, fmt.Errorf("uri is nil")
}
isDir, err = storage.CanList(uri)
if err != nil || !isDir {
return
}
children, err := storage.List(uri)
childCount = len(children)
return
}