/
wget.go
161 lines (134 loc) · 3.18 KB
/
wget.go
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
// Copyright 2012-2021 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Wget reads one file from a url and writes to stdout.
//
// Synopsis:
//
// wget URL
//
// Description:
//
// Returns a non-zero code on failure.
//
// Notes:
//
// There are a few differences with GNU wget:
// - Upon error, the return value is always 1.
// - The protocol (http/https) is mandatory.
//
// Example:
//
// wget -O google.txt http://google.com/
package main
import (
"context"
"errors"
"flag"
"fmt"
"log"
"net/url"
"os"
"path"
"strings"
"github.com/u-root/u-root/pkg/curl"
"github.com/u-root/uio/uio"
)
var errEmptyURL = errors.New("empty url")
type cmd struct {
url string
outputPath string
}
// flags parses wget flags
// wget is old school, and allows flags after the URL.
// This code does not process the -- flag specified in the
// man page, as the command itself does not seem to either.
func flags(args ...string) (string, string, error) {
// -- takes priority over everything else.
// flag package does not allow - as a flag.
// except, in spite of the docs, wget on linux seems
// to ignore --. Great. It's a terrible idea anyway,
// unless you really like creating files that start
// with -
// If at some point one wishes to add -- support,
// the slices package is a good place to start.
if len(args) == 0 {
return "", "", errEmptyURL
}
f := flag.NewFlagSet(args[0], flag.ContinueOnError)
var outPath = f.String("O", "", "output file")
if err := f.Parse(args[1:]); err != nil {
return "", "", err
}
if len(f.Args()) == 0 {
return "", "", errEmptyURL
}
URL := f.Args()[0]
// Now, it is allowed to have switches after the URL,
// handle following flags
if err := f.Parse(f.Args()[1:]); err != nil {
return "", "", err
}
return *outPath, URL, nil
}
func command(args ...string) (*cmd, error) {
outPath, URL, err := flags(args...)
if err != nil {
return nil, err
}
return &cmd{
outputPath: outPath,
url: URL,
}, nil
}
func (c *cmd) run() error {
log.SetPrefix("wget: ")
if c.url == "" {
return errEmptyURL
}
parsedURL, err := url.Parse(c.url)
if err != nil {
return err
}
if c.outputPath == "" {
c.outputPath = defaultOutputPath(parsedURL.Path)
}
schemes := curl.Schemes{
"tftp": curl.DefaultTFTPClient,
"http": curl.DefaultHTTPClient,
// curl.DefaultSchemes doesn't support HTTPS by default.
"https": curl.DefaultHTTPClient,
"file": &curl.LocalFileClient{},
}
reader, err := schemes.FetchWithoutCache(context.Background(), parsedURL)
if err != nil {
return fmt.Errorf("failed to download %v: %w", c.url, err)
}
if err := uio.ReadIntoFile(reader, c.outputPath); err != nil {
return err
}
return nil
}
func usage() {
log.Printf("Usage: %s [ARGS] URL\n", os.Args[0])
flag.PrintDefaults()
os.Exit(2)
}
func defaultOutputPath(urlPath string) string {
if urlPath == "" || strings.HasSuffix(urlPath, "/") {
return "index.html"
}
return path.Base(urlPath)
}
func main() {
c, err := command(os.Args...)
if err == nil {
err = c.run()
}
if err != nil {
if errors.Is(err, errEmptyURL) {
usage()
}
log.Fatal(err)
}
}