]> Sergey Matveev's repositories - meta4ra.git/blobdiff - cmd/meta4ra/check.go
*-hashes-detect, *-hash, *-check -stdin/-all-hashes, optional builtins
[meta4ra.git] / cmd / meta4ra / check.go
similarity index 59%
rename from cmd/meta4-check/main.go
rename to cmd/meta4ra/check.go
index b807a26993c8b945b477ab63c72ea3ca8b9461ae..c030f8b8417ec376d2008ab8aed377efa43ac15e 100644 (file)
@@ -13,7 +13,6 @@
 // You should have received a copy of the GNU General Public License
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-// Metalink 4.0 checker
 package main
 
 import (
@@ -28,12 +27,14 @@ import (
        "path"
        "strings"
 
-       "go.stargrave.org/meta4ra"
+       meta4ra "go.stargrave.org/meta4ra/internal"
 )
 
-func main() {
-       hashes := flag.String("hashes",
-               strings.Join(meta4ra.HashesDefault, ","), "hash-name:command-s")
+func runCheck() {
+       stdin := flag.Bool("stdin", false, "Compare data of single file taken from stdin")
+       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")
        flag.Usage = func() {
@@ -44,11 +45,10 @@ func main() {
 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 empty ("") FILE.
+and -extract-sig, then you can just specify an empty ("") FILE.
 `)
        }
        flag.Parse()
-       log.SetFlags(log.Lshortfile)
 
        data, err := os.ReadFile(*metaPath)
        if err != nil {
@@ -64,6 +64,9 @@ and -extract-sig, then you can just specify empty ("") FILE.
        for _, fn := range flag.Args() {
                toCheck[path.Base(fn)] = fn
        }
+       if *stdin && len(toCheck) != 1 {
+               log.Fatalln("exactly single FILE must be specified when using -stdin")
+       }
 
        bad := false
        for _, f := range meta.Files {
@@ -92,28 +95,39 @@ and -extract-sig, then you can just specify empty ("") FILE.
                }
 
                fullPath := toCheck[f.Name]
+               delete(toCheck, f.Name)
                if !(len(toCheck) == 0 || fullPath != "") {
                        continue
                }
                if fullPath == "" {
                        fullPath = f.Name
                }
-               s, err := os.Stat(fullPath)
-               if err != nil {
-                       fmt.Println(err)
-                       bad = true
-                       continue
+               if !*stdin {
+                       s, err := os.Stat(fullPath)
+                       if err != nil {
+                               fmt.Println(err)
+                               bad = true
+                               continue
+                       }
+                       if uint64(s.Size()) != f.Size {
+                               fmt.Println("size mismatch",
+                                       f.Name, "our:", s.Size(), "their:", f.Size)
+                               bad = true
+                               continue
+                       }
                }
-               if uint64(s.Size()) != f.Size {
-                       fmt.Println("size mismatch",
-                               f.Name, "our:", s.Size(), "their:", f.Size)
+
+               hasher, err := meta4ra.NewHasher(*hashes)
+               if err != nil {
+                       fmt.Println(f.Name, err)
                        bad = true
                        continue
                }
-
-               hasher := meta4ra.NewHasher(*hashes)
                var hashTheir string
                var hashName string
+               if *allHashes {
+                       goto HashFound
+               }
                for i, name := range hasher.Names {
                        for _, h := range f.Hashes {
                                if h.Type == name {
@@ -130,35 +144,80 @@ and -extract-sig, then you can just specify empty ("") FILE.
                fmt.Println("no common hashes found for:", f.Name)
                bad = true
                continue
-
        HashFound:
-               fd, err := os.Open(fullPath)
+
+               fd := os.Stdin
+               if !*stdin {
+                       fd, err = os.Open(fullPath)
+                       if err != nil {
+                               fmt.Println("Error:", f.Name, err)
+                               bad = true
+                               continue
+                       }
+               }
+               err = hasher.Start()
                if err != nil {
+                       if !*stdin {
+                               fd.Close()
+                       }
+                       hasher.Stop()
                        fmt.Println("Error:", f.Name, err)
                        bad = true
                        continue
                }
-               hasher.Start()
-               _, err = io.Copy(hasher, bufio.NewReaderSize(fd, 1<<20))
-               fd.Close()
-               sums := hasher.Sums()
+               _, err = io.Copy(hasher, bufio.NewReaderSize(fd, meta4ra.BufLen))
+               if !*stdin {
+                       fd.Close()
+               }
                if err != nil {
+                       hasher.Stop()
                        fmt.Println("Error:", f.Name, err)
                        bad = true
                        continue
                }
-               hashOur := sums[0].Hash
-               if hashOur == hashTheir {
-                       fmt.Println(f.Name, hashName, "good")
-               } else {
-                       fmt.Println(
-                               "hash mismatch:", f.Name, hashName,
-                               "our:", hashOur,
-                               "their:", hashTheir,
-                       )
+               sums, err := hasher.Sums()
+               if err != nil {
+                       hasher.Stop()
+                       fmt.Println("Error:", f.Name, err)
                        bad = true
                        continue
                }
+               if *allHashes {
+                       hashesOur := make(map[string]string, len(sums))
+                       for _, h := range sums {
+                               hashesOur[h.Type] = h.Hash
+                       }
+                       for _, h := range f.Hashes {
+                               hashOur := hashesOur[h.Type]
+                               if h.Hash == hashOur {
+                                       fmt.Println(f.Name, h.Type, "good")
+                               } else {
+                                       fmt.Println(
+                                               "hash mismatch:", f.Name, h.Type,
+                                               "our:", hashOur,
+                                               "their:", h.Hash,
+                                       )
+                                       bad = true
+                               }
+                       }
+               } else {
+                       hashOur := sums[0].Hash
+                       if hashOur == hashTheir {
+                               fmt.Println(f.Name, hashName, "good")
+                       } else {
+                               fmt.Println(
+                                       "hash mismatch:", f.Name, hashName,
+                                       "our:", hashOur,
+                                       "their:", hashTheir,
+                               )
+                               bad = true
+                               continue
+                       }
+               }
+       }
+       if len(toCheck) != 0 {
+               fmt.Println("not all FILEs met")
+               bad = true
        }
        if bad {
                os.Exit(1)