// meta4ra -- Metalink 4.0 utilities // Copyright (C) 2021-2024 Sergey Matveev // // 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 // the Free Software Foundation, version 3 of the License. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . package internal import ( "bytes" "errors" "io" "os/exec" "strings" ) type Hasher struct { Names []string Cmds []*exec.Cmd Ins []io.WriteCloser Outs []io.ReadCloser } func NewHasher(hashes string) (*Hasher, error) { h := Hasher{} for _, hc := range strings.Split(hashes, ",") { cols := strings.SplitN(hc, ":", 2) name, cmdline := cols[0], cols[1] if cmdline == BuiltinCmd { newHash, exists := BuiltinHashes[name] if !exists { return nil, errors.New("no builtin hash: " + name) } b := &BuiltinHasher{h: newHash()} h.Names = append(h.Names, name) h.Ins = append(h.Ins, b) h.Outs = append(h.Outs, b) h.Cmds = append(h.Cmds, nil) } else { cmd := exec.Command("/bin/sh", "-e", "-c", cmdline) in, err := cmd.StdinPipe() if err != nil { return &h, err } out, err := cmd.StdoutPipe() if err != nil { return &h, err } h.Names = append(h.Names, name) h.Ins = append(h.Ins, in) h.Outs = append(h.Outs, out) h.Cmds = append(h.Cmds, cmd) } } return &h, nil } func (h *Hasher) Start() (err error) { for _, cmd := range h.Cmds { if cmd == nil { continue } err = cmd.Start() if err != nil { return } } return nil } func (h *Hasher) Stop() { for _, cmd := range h.Cmds { if cmd == nil || cmd.Process == nil { continue } cmd.Process.Kill() cmd.Process.Release() } } func (h *Hasher) Write(p []byte) (n int, rerr error) { errs := make(chan error) for _, in := range h.Ins { go func(in io.WriteCloser) { _, err := io.Copy(in, bytes.NewReader(p)) errs <- err }(in) } for i := 0; i < len(h.Names); i++ { if err := <-errs; err != nil { rerr = err } } n = len(p) return } func (h *Hasher) Sums() (sums []Hash, err error) { sums = make([]Hash, 0, len(h.Names)) for i, name := range h.Names { if err = h.Ins[i].Close(); err != nil { return } var out []byte out, err = io.ReadAll(h.Outs[i]) if err != nil { return } if cmd := h.Cmds[i]; cmd != nil { if err = h.Cmds[i].Wait(); err != nil { return } } cols := strings.Fields(strings.TrimSuffix(string(out), "\n")) sums = append(sums, Hash{Type: name, Hash: cols[0]}) } return }