writeToOutput writes file content to a local path and returns the final destination. Symlink output paths are refused. A directory target receives the file under its remote basename using the local operating system's path rules, and existing regular files are only replaced when clobber is true.
(file *repoFile, output string, clobber bool)
| 260 | // its remote basename using the local operating system's path rules, and existing |
| 261 | // regular files are only replaced when clobber is true. |
| 262 | func writeToOutput(file *repoFile, output string, clobber bool) (string, error) { |
| 263 | dest := output |
| 264 | outputDir := "" |
| 265 | |
| 266 | // A trailing separator signals the user intends dest to be a directory even if it does not exist yet. |
| 267 | asDir := strings.HasSuffix(dest, "/") || strings.HasSuffix(dest, string(os.PathSeparator)) |
| 268 | |
| 269 | if lr, err := lstatF(trimTrailingPathSeparators(dest)); err == nil { |
| 270 | if lr.isSymlink { |
| 271 | return "", fmt.Errorf("output path is a symlink") |
| 272 | } |
| 273 | if lr.isDir { |
| 274 | asDir = true |
| 275 | } |
| 276 | } else if !os.IsNotExist(err) { |
| 277 | return "", err |
| 278 | } |
| 279 | |
| 280 | name := dest |
| 281 | if asDir { |
| 282 | name = filepath.Base(file.Name) |
| 283 | if name == "." || !filepath.IsLocal(name) { |
| 284 | return "", fmt.Errorf("invalid output filename %q", file.Name) |
| 285 | } |
| 286 | outputDir = dest |
| 287 | dest = filepath.Join(dest, name) |
| 288 | } |
| 289 | |
| 290 | if lr, err := lstatF(dest); err == nil { |
| 291 | if lr.isSymlink { |
| 292 | return "", fmt.Errorf("output path is a symlink") |
| 293 | } |
| 294 | if !clobber { |
| 295 | return "", fmt.Errorf("output path already exists: %q (use --clobber to overwrite)", dest) |
| 296 | } |
| 297 | } else if !os.IsNotExist(err) { |
| 298 | return "", err |
| 299 | } |
| 300 | |
| 301 | if err := writeOutputFile(outputDir, name, file.Content, 0o644, clobber); err != nil { |
| 302 | if os.IsExist(err) && !clobber { |
| 303 | return "", fmt.Errorf("output path already exists: %q (use --clobber to overwrite)", dest) |
| 304 | } |
| 305 | return "", err |
| 306 | } |
| 307 | |
| 308 | return dest, nil |
| 309 | } |
| 310 | |
| 311 | func trimTrailingPathSeparators(path string) string { |
| 312 | end := len(path) |