Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / mvdan.cc / xurls / v2@v2.2.0 / cmd / xurls / main.go
1 // Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
2 // See LICENSE for licensing information
3
4 package main
5
6 import (
7         "bufio"
8         "bytes"
9         "errors"
10         "flag"
11         "fmt"
12         "io"
13         "io/ioutil"
14         "net/http"
15         "net/url"
16         "os"
17         "regexp"
18         "strings"
19         "time"
20
21         "mvdan.cc/xurls/v2"
22 )
23
24 var (
25         matching = flag.String("m", "", "")
26         relaxed  = flag.Bool("r", false, "")
27         fix      = flag.Bool("fix", false, "")
28 )
29
30 func init() {
31         flag.Usage = func() {
32                 p := func(format string, a ...interface{}) {
33                         fmt.Fprintf(os.Stderr, format, a...)
34                 }
35                 p("Usage: xurls [-h] [files]\n\n")
36                 p("If no files are given, it reads from standard input.\n\n")
37                 p("   -m <regexp>   only match urls whose scheme matches a regexp\n")
38                 p("                    example: 'https?://|mailto:'\n")
39                 p("   -r            also match urls without a scheme (relaxed)\n")
40                 p("   -fix          overwrite urls that redirect\n")
41         }
42 }
43
44 func scanPath(re *regexp.Regexp, path string) error {
45         f := os.Stdin
46         if path != "-" {
47                 var err error
48                 f, err = os.Open(path)
49                 if err != nil {
50                         return err
51                 }
52                 defer f.Close()
53         }
54         bufr := bufio.NewReader(f)
55         var fixedBuf bytes.Buffer
56         anyFixed := false
57         var broken []string
58         for {
59                 line, err := bufr.ReadBytes('\n')
60                 offset := 0
61                 for _, pair := range re.FindAllIndex(line, -1) {
62                         // The indexes are based on the original line.
63                         pair[0] += offset
64                         pair[1] += offset
65                         match := line[pair[0]:pair[1]]
66                         if !*fix {
67                                 fmt.Printf("%s\n", match)
68                                 continue
69                         }
70                         u, err := url.Parse(string(match))
71                         if err != nil {
72                                 continue
73                         }
74                         fixed := u.String()
75                         switch u.Scheme {
76                         case "http", "https":
77                                 // See if the URL redirects somewhere.
78                                 client := &http.Client{
79                                         Timeout: 10 * time.Second,
80                                         CheckRedirect: func(req *http.Request, via []*http.Request) error {
81                                                 if len(via) >= 10 {
82                                                         return errors.New("stopped after 10 redirects")
83                                                 }
84                                                 // Keep the fragment around.
85                                                 req.URL.Fragment = u.Fragment
86                                                 fixed = req.URL.String()
87                                                 return nil
88                                         },
89                                 }
90                                 resp, err := client.Get(fixed)
91                                 if err != nil {
92                                         continue
93                                 }
94                                 if resp.StatusCode >= 400 {
95                                         broken = append(broken, string(match))
96                                 }
97                                 resp.Body.Close()
98                         }
99                         if fixed != string(match) {
100                                 // Replace the url, and update the offset.
101                                 newLine := line[:pair[0]]
102                                 newLine = append(newLine, fixed...)
103                                 newLine = append(newLine, line[pair[1]:]...)
104                                 offset += len(newLine) - len(line)
105                                 line = newLine
106                                 anyFixed = true
107                         }
108                 }
109                 if *fix {
110                         if path == "-" {
111                                 os.Stdout.Write(line)
112                         } else {
113                                 fixedBuf.Write(line)
114                         }
115                 }
116                 if err == io.EOF {
117                         break
118                 } else if err != nil {
119                         return err
120                 }
121         }
122         if anyFixed && path != "-" {
123                 f.Close()
124                 // Overwrite the file, if we weren't reading stdin. Report its
125                 // path too.
126                 fmt.Println(path)
127                 if err := ioutil.WriteFile(path, fixedBuf.Bytes(), 0666); err != nil {
128                         return err
129                 }
130         }
131         if len(broken) > 0 {
132                 return fmt.Errorf("found %d broken urls in %q:\n%s", len(broken),
133                         path, strings.Join(broken, "\n"))
134         }
135         return nil
136 }
137
138 func main() { os.Exit(main1()) }
139
140 func main1() int {
141         flag.Parse()
142         if *relaxed && *matching != "" {
143                 fmt.Fprintln(os.Stderr, "-r and -m at the same time don't make much sense")
144                 return 1
145         }
146         var re *regexp.Regexp
147         if *relaxed {
148                 re = xurls.Relaxed()
149         } else if *matching != "" {
150                 var err error
151                 if re, err = xurls.StrictMatchingScheme(*matching); err != nil {
152                         fmt.Fprintln(os.Stderr, err)
153                         return 1
154                 }
155         } else {
156                 re = xurls.Strict()
157         }
158         args := flag.Args()
159         if len(args) == 0 {
160                 args = []string{"-"}
161         }
162         for _, path := range args {
163                 if err := scanPath(re, path); err != nil {
164                         fmt.Fprintln(os.Stderr, err)
165                         return 1
166                 }
167         }
168         return 0
169 }