]> Sergey Matveev's repositories - meta4ra.git/blobdiff - cmd/meta4ra/check.go
Place positional parameters first
[meta4ra.git] / cmd / meta4ra / check.go
index 3b3e0a916087635136fd4ff6e9db74145aa63e2b..4cdeb9a120e1f885dace9db81bddb922552870be 100644 (file)
@@ -1,5 +1,5 @@
 // meta4ra -- Metalink 4.0 utilities
-// Copyright (C) 2021-2024 Sergey Matveev <stargrave@stargrave.org>
+// Copyright (C) 2021-2025 Sergey Matveev <stargrave@stargrave.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -23,20 +23,24 @@ import (
        "io"
        "io/fs"
        "log"
+       "net/http"
        "os"
        "path"
        "strings"
+       "time"
 
        meta4ra "go.stargrave.org/meta4ra/internal"
 )
 
 func runCheck() {
        pipe := flag.Bool("pipe", false, "Verify file data from stdin, copy to stdout")
+       progress := flag.Bool("progress", false, "Show progress of piping/downloading")
        allHashes := flag.Bool("all-hashes", false, "Check all hashes, not the first common one")
        hashes := flag.String("hashes", meta4ra.HashesDefault,
                "hash-name:commandline[,...]")
        extractSig := flag.Bool("extract-sig", false, "Extract signature files")
        metaPath := flag.String("meta4", "file.meta4", "Metalink file")
+       dl := flag.Int("dl", -1, "URL index to download, instead of reading from stdin")
        flag.Usage = func() {
                fmt.Fprintf(flag.CommandLine.Output(),
                        "Usage: %s [options] [FILE ...]\n", os.Args[0])
@@ -46,6 +50,10 @@ If no FILEs are specified, then all <file>s from metalink are searched and
 verified if they exist. Otherwise only specified FILEs are checked. If you
 want to skip any <file> verification (for example only to validate the format
 and -extract-sig, then you can just specify an empty ("") FILE.
+
+If -dl (> 0) is specified, then it automatically implies -pipe, but
+downloads data by specified URLs index, instead of reading from stdin.
+That can be used as a downloading utility.
 `)
        }
        flag.Parse()
@@ -73,6 +81,9 @@ and -extract-sig, then you can just specify an empty ("") FILE.
        for _, fn := range flag.Args() {
                toCheck[path.Base(fn)] = fn
        }
+       if *dl != -1 {
+               *pipe = true
+       }
        if *pipe && len(toCheck) != 1 {
                log.Fatalln("exactly single FILE must be specified when using -pipe")
        }
@@ -96,7 +107,7 @@ and -extract-sig, then you can just specify an empty ("") FILE.
                        if err = os.WriteFile(
                                fn,
                                []byte(strings.TrimPrefix(sig.Signature, "\n")),
-                               fs.FileMode(0666),
+                               fs.FileMode(0o666),
                        ); err != nil {
                                log.Println("Error:", f.Name, "can not save signature:", err)
                                bad = true
@@ -155,19 +166,39 @@ and -extract-sig, then you can just specify an empty ("") FILE.
                continue
        HashFound:
 
-               fd := os.Stdin
+               var src io.ReadCloser
+               src = os.Stdin
                if !*pipe {
-                       fd, err = os.Open(fullPath)
+                       src, err = os.Open(fullPath)
+                       if err != nil {
+                               log.Println("Error:", f.Name, err)
+                               bad = true
+                               continue
+                       }
+               }
+               if *dl != -1 {
+                       var resp *http.Response
+                       resp, err = http.Get(f.URLs[*dl].URL)
                        if err != nil {
                                log.Println("Error:", f.Name, err)
                                bad = true
                                continue
                        }
+                       log.Println("HTTP response:")
+                       for k := range resp.Header {
+                               log.Printf("\t%+q: %+q\n", k, resp.Header.Get(k))
+                       }
+                       if resp.StatusCode != http.StatusOK {
+                               log.Println("Bad status code:", f.Name, resp.Status)
+                               bad = true
+                               continue
+                       }
+                       src = resp.Body
                }
                err = hasher.Start()
                if err != nil {
                        if !*pipe {
-                               fd.Close()
+                               src.Close()
                        }
                        hasher.Stop()
                        log.Println("Error:", f.Name, err)
@@ -180,9 +211,14 @@ and -extract-sig, then you can just specify an empty ("") FILE.
                } else {
                        w = hasher
                }
-               _, err = io.Copy(w, bufio.NewReaderSize(fd, meta4ra.BufLen))
-               if !*pipe {
-                       fd.Close()
+               if *progress {
+                       bar := Progress{w: w, now: time.Now(), total: f.Size}
+                       bar.next = bar.now.Add(ProgressPeriod)
+                       w = &bar
+               }
+               _, err = io.Copy(w, bufio.NewReaderSize(src, meta4ra.BufLen))
+               if !*pipe || *dl != -1 {
+                       src.Close()
                }
                if err != nil {
                        hasher.Stop()