Rework Archive

Package Archive:
- complete rework of package
- split package between compression (stream compression only) and
  archive (catalogue/dictionary/tape archive type)
- rework compression to be stream only working without any temporary
  file or any like
- rework archive like TAR, ZIP... to expose stream instead of writing
  directly file/path
- rework archive like TAR, ZIP... to allow adding file from stream only
  and allowing to parsing local path to add it into archive
- rework detection of compression / archive
- rework extractAll function to not using useless local temporary file
- adding test can be used as example and perform testing of most of
  package code
- using const custom type for compression and archive allow them to be
  parsed, marshalled or unmarshalled from text or json and more
- apply following change into other module that use it
This commit is contained in:
nabbar
2024-04-02 08:53:19 +02:00
parent cce6ba5348
commit 71c18b96a5
50 changed files with 3874 additions and 2198 deletions

View File

@@ -1,226 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive
import (
"fmt"
"io"
"os"
"path/filepath"
libbz2 "github.com/nabbar/golib/archive/bz2"
libgzp "github.com/nabbar/golib/archive/gzip"
libtar "github.com/nabbar/golib/archive/tar"
libzip "github.com/nabbar/golib/archive/zip"
liberr "github.com/nabbar/golib/errors"
libfpg "github.com/nabbar/golib/file/progress"
)
type ArchiveType uint8
const (
permDir = os.FileMode(0775)
permFile = os.FileMode(0664)
)
const (
TypeTar = iota + 1
TypeTarGzip
TypeGzip
TypeZip
)
func ExtractFile(src, dst libfpg.Progress, fileNameContain, fileNameRegex string) liberr.Error {
var (
e error
tmp libfpg.Progress
err liberr.Error
)
defer func() {
if tmp != nil {
_ = tmp.CloseDelete()
}
}()
if tmp, e = libfpg.Temp(""); e != nil {
return ErrorFileOpen.Error(e)
} else {
dst.SetRegisterProgress(tmp)
}
if _, e = src.Seek(0, io.SeekStart); e != nil {
return ErrorFileSeek.Error(e)
// #nosec
}
if err = libbz2.GetFile(src, tmp); err == nil {
//logger.DebugLevel.Log("try another archive...")
return ExtractFile(tmp, dst, fileNameContain, fileNameRegex)
} else if err.IsCode(libbz2.ErrorIOCopy) {
return err
}
if err = libgzp.GetFile(src, tmp); err == nil {
//logger.DebugLevel.Log("try another archive...")
return ExtractFile(tmp, dst, fileNameContain, fileNameRegex)
} else if !err.IsCode(libgzp.ErrorGZReader) {
return err
}
if err = libtar.GetFile(src, tmp, fileNameContain, fileNameRegex); err == nil {
//logger.DebugLevel.Log("try another archive...")
return ExtractFile(tmp, dst, fileNameContain, fileNameRegex)
} else if !err.IsCode(libtar.ErrorTarNext) {
return err
}
if err = libzip.GetFile(src, tmp, fileNameContain, fileNameRegex); err == nil {
//logger.DebugLevel.Log("try another archive...")
return ExtractFile(tmp, dst, fileNameContain, fileNameRegex)
} else if !err.IsCode(libzip.ErrorZipOpen) {
return err
}
if _, e = dst.ReadFrom(src); e != nil {
//logger.ErrorLevel.LogErrorCtx(logger.DebugLevel, "reopening file", err)
return ErrorIOCopy.Error(e)
}
return nil
}
func ExtractAll(src libfpg.Progress, originalName, outputPath string, defaultDirPerm os.FileMode) liberr.Error {
var (
e error
i os.FileInfo
tmp libfpg.Progress
dst libfpg.Progress
err liberr.Error
)
defer func() {
if src != nil {
_ = src.Close()
}
if dst != nil {
_ = dst.Close()
}
if tmp != nil {
_ = tmp.CloseDelete()
}
}()
if tmp, e = libfpg.Temp(""); e != nil {
return ErrorFileOpen.Error(e)
} else {
src.SetRegisterProgress(tmp)
}
if err = libbz2.GetFile(src, tmp); err == nil {
if inf, er := tmp.Stat(); er == nil {
tmp.Reset(inf.Size())
}
return ExtractAll(tmp, originalName, outputPath, defaultDirPerm)
} else if !err.IsCode(libbz2.ErrorIOCopy) {
return err
}
if err = libgzp.GetFile(src, tmp); err == nil {
if inf, er := tmp.Stat(); er == nil {
tmp.Reset(inf.Size())
}
return ExtractAll(tmp, originalName, outputPath, defaultDirPerm)
} else if !err.IsCode(libgzp.ErrorGZReader) {
return err
}
if tmp != nil {
_ = tmp.CloseDelete()
}
if i, e = os.Stat(outputPath); e != nil && os.IsNotExist(e) {
//nolint #nosec
/* #nosec */
if e = os.MkdirAll(outputPath, permDir); e != nil {
return ErrorDirCreate.Error(e)
}
} else if e != nil {
return ErrorDirStat.Error(e)
} else if !i.IsDir() {
return ErrorDirNotDir.Error(nil)
}
if err = libtar.GetAll(src, outputPath, defaultDirPerm); err == nil {
return nil
} else if !err.IsCode(libtar.ErrorTarNext) {
return err
}
if err = libzip.GetAll(src, outputPath, defaultDirPerm); err == nil {
return nil
} else if !err.IsCode(libzip.ErrorZipOpen) {
return err
}
if dst, e = libfpg.New(filepath.Join(outputPath, originalName), os.O_RDWR|os.O_CREATE|os.O_TRUNC, permFile); e != nil {
return ErrorFileOpen.Error(e)
} else {
src.SetRegisterProgress(dst)
}
if _, e = src.Seek(0, io.SeekStart); e != nil {
return ErrorFileSeek.Error(e)
} else if _, e = dst.ReadFrom(src); e != nil {
return ErrorIOCopy.Error(e)
}
return nil
}
func CreateArchive(archiveType ArchiveType, archive libfpg.Progress, stripPath string, comment string, pathContent ...string) (created bool, err liberr.Error) {
if len(pathContent) < 1 {
//nolint #goerr113
return false, ErrorParamEmpty.Error(fmt.Errorf("pathContent is empty"))
}
switch archiveType {
case TypeGzip:
return libgzp.Create(archive, stripPath, comment, pathContent...)
case TypeTar:
return libtar.Create(archive, stripPath, comment, pathContent...)
case TypeTarGzip:
return libtar.CreateGzip(archive, stripPath, comment, pathContent...)
case TypeZip:
return libzip.Create(archive, stripPath, comment, pathContent...)
//@TODO: add zip mode
}
return false, nil
}

View File

@@ -23,77 +23,53 @@
*
*/
package gzip
package archive
import (
gz "compress/gzip"
"io"
"github.com/nabbar/golib/errors"
libfpg "github.com/nabbar/golib/file/progress"
"bytes"
"encoding/json"
"strings"
)
func GetFile(src io.ReadSeeker, dst io.WriteSeeker) errors.Error {
if _, e := src.Seek(0, io.SeekStart); e != nil {
return ErrorFileSeek.Error(e)
} else if _, e = dst.Seek(0, io.SeekStart); e != nil {
return ErrorFileSeek.Error(e)
func (a Algorithm) MarshalText() ([]byte, error) {
return []byte(a.String()), nil
}
func (a *Algorithm) UnmarshalText(b []byte) error {
*a = None
s := strings.Trim(string(b), "\"")
s = strings.Trim(s, "'")
s = strings.TrimSpace(s)
switch {
case strings.EqualFold(s, Tar.String()):
*a = Tar
case strings.EqualFold(s, Zip.String()):
*a = Zip
default:
*a = None
}
r, e := gz.NewReader(src)
if e != nil {
return ErrorGZReader.Error(e)
return nil
}
func (a Algorithm) MarshalJSON() ([]byte, error) {
if a.IsNone() {
return []byte("null"), nil
}
return append(append([]byte{'"'}, []byte(a.String())...), '"'), nil
}
defer func() {
_ = r.Close()
}()
func (a *Algorithm) UnmarshalJSON(b []byte) error {
var s string
// #nosec
_, e = io.Copy(dst, r)
if e != nil {
return ErrorIOCopy.Error(e)
} else if _, e = dst.Seek(0, io.SeekStart); e != nil {
return ErrorFileSeek.Error(e)
} else {
if n := []byte("null"); bytes.Equal(b, n) {
*a = None
return nil
} else if err := json.Unmarshal(b, &s); err != nil {
return err
} else {
return a.UnmarshalText([]byte(s))
}
}
func GetGunZipSize(src io.ReadSeeker) int64 {
if _, e := src.Seek(0, io.SeekStart); e != nil {
return 0
}
r, e := gz.NewReader(src)
if e != nil {
return 0
}
defer func() {
_ = r.Close()
}()
if s, k := src.(libfpg.Progress); k {
s.RegisterFctReset(func(size, current int64) {
})
s.RegisterFctIncrement(func(size int64) {
})
s.RegisterFctEOF(func() {
})
}
var n int64
// #nosec
n, e = io.Copy(io.Discard, r)
if e != nil {
return 0
}
return n
}

View File

@@ -0,0 +1,94 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive
import (
"bufio"
"io"
arctps "github.com/nabbar/golib/archive/archive/types"
)
func Parse(s string) Algorithm {
var alg = None
if e := alg.UnmarshalText([]byte(s)); e != nil {
return None
} else {
return alg
}
}
// Detect will try to detect the algorithm of the input and returns the corresponding reader.
// This function will read the first 6 bytes of the input and try to detect the algorithm.
// If algorithm detection trigger a supported algorithm, the function will try to convert
// the input to appropriate reader for the archive.
// In any case, if en error occurs, the function will return an error.
// Otherwise, the function will return the algorithm, the reader and a nil error.
//
// If the input is a zip archive and the input is not a io.ReaderAt compatible, the function will return.
// If the input is a tar archive and the input is a strict io.ReadCloser, with no seek or read at compatible,
// the reader result could be use only for one time.
//
// This difference are based on how the algorithm work:
// - zip: will use dictionary / catalog to store position and metadata of each embedded file
// - tar: (TAR = Tape ARchive) will store each file continuously beginning with his metadata and following with his content.
//
// As that, a strict reader could be use only for tar archive and the reader result could be use only for one time.
// In this case, the best use if calling the walk function of the reader.
func Detect(r io.ReadCloser) (Algorithm, arctps.Reader, io.ReadCloser, error) {
var (
err error
buf []byte
bfr = &rdr{
r: r,
b: bufio.NewReader(r),
}
)
if buf, err = bfr.Peek(265); err != nil {
return None, nil, nil, err
}
switch {
case Tar.DetectHeader(buf): // tar
if t, e := Tar.Reader(bfr); e != nil {
return None, nil, nil, e
} else {
return Tar, t, bfr, nil
}
case Zip.DetectHeader(buf): // zip
bfr.b = nil // do not use buffer (using ReaderAt)
if z, e := Zip.Reader(bfr); e != nil {
return None, nil, nil, e
} else {
return Zip, z, r, nil
}
default:
return None, nil, bfr, nil
}
}

61
archive/archive/io.go Normal file
View File

@@ -0,0 +1,61 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive
import (
"errors"
"io"
arctar "github.com/nabbar/golib/archive/archive/tar"
arctps "github.com/nabbar/golib/archive/archive/types"
arczip "github.com/nabbar/golib/archive/archive/zip"
)
var (
ErrInvalidAlgorithm = errors.New("invalid algorithm")
)
func (a Algorithm) Reader(r io.ReadCloser) (arctps.Reader, error) {
switch a {
case Tar:
return arctar.NewReader(r)
case Zip:
return arczip.NewReader(r)
default:
return nil, ErrInvalidAlgorithm
}
}
func (a Algorithm) Writer(w io.WriteCloser) (arctps.Writer, error) {
switch a {
case Tar:
return arctar.NewWriter(w)
case Zip:
return arczip.NewWriter(w)
default:
return nil, ErrInvalidAlgorithm
}
}

View File

@@ -1,39 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package archive
import (
liberr "github.com/nabbar/golib/errors"
)
const (
MinPkgArchive = liberr.MinPkgArchive
MinPkgArchiveBZ2 = MinPkgArchive + 20
MinPkgArchiveGZip = MinPkgArchiveBZ2 + 20
MinPkgArchiveTar = MinPkgArchiveGZip + 20
MinPkgArchiveZip = MinPkgArchiveTar + 20
)

View File

@@ -1,115 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive
import (
"os"
"path/filepath"
"regexp"
"strings"
)
const (
pathSeparatorOs = string(os.PathSeparator)
pathSeparatorCommon = "/"
pathParentCommon = ".."
)
type File struct {
Name string
Path string
}
func (a File) Matching(containt string) bool {
if len(containt) < 1 {
return false
}
return strings.Contains(strings.ToLower(a.Name), strings.ToLower(containt))
}
func (a File) Regex(regex string) bool {
if len(regex) < 1 {
return false
}
b, _ := regexp.MatchString(regex, a.Name)
return b
}
func (a File) MatchingFullPath(containt string) bool {
if len(containt) < 1 {
return false
}
return strings.Contains(strings.ToLower(a.GetKeyMap()), strings.ToLower(containt))
}
func (a File) RegexFullPath(regex string) bool {
if len(regex) < 1 {
return false
}
b, _ := regexp.MatchString(regex, a.GetKeyMap())
return b
}
func (a File) GetKeyMap() string {
return filepath.Join(a.Path, a.Name)
}
func (a File) GetDestFileOnly(baseDestination string) string {
return filepath.Join(baseDestination, a.Name)
}
func (a File) GetDestWithPath(baseDestination string) string {
return filepath.Join(baseDestination, a.Path, a.Name)
}
func NewFile(name, path string) File {
return File{
Name: name,
Path: path,
}
}
func NewFileFullPath(fullpath string) File {
return NewFile(filepath.Base(fullpath), filepath.Dir(fullpath))
}
func CleanPath(p string) string {
for {
if strings.HasPrefix(p, pathParentCommon) {
p = strings.TrimPrefix(p, pathParentCommon)
} else if strings.HasPrefix(p, pathSeparatorCommon+pathParentCommon) {
p = strings.TrimPrefix(p, pathSeparatorCommon+pathParentCommon)
} else if strings.HasPrefix(p, pathSeparatorOs+pathParentCommon) {
p = strings.TrimPrefix(p, pathSeparatorOs+pathParentCommon)
} else {
return filepath.Clean(p)
}
}
}

165
archive/archive/reader.go Normal file
View File

@@ -0,0 +1,165 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive
import (
"bufio"
"io"
"io/fs"
)
// struct to allow compatibility with io.ReadSeeker for all type of archive
type rdr struct {
r io.ReadCloser
b *bufio.Reader
}
func (o *rdr) Close() error {
return o.r.Close()
}
func (o *rdr) Read(p []byte) (n int, err error) {
if o.b != nil {
return o.b.Read(p)
}
return o.r.Read(p)
}
func (o *rdr) Peek(n int) ([]byte, error) {
if o.b != nil {
return o.b.Peek(n)
} else {
b := bufio.NewReader(o.r)
return b.Peek(n)
}
}
func (o *rdr) ReadAt(p []byte, off int64) (n int, err error) {
if r, k := o.r.(io.ReaderAt); k {
return r.ReadAt(p, off)
}
if r, k := o.r.(io.ReadSeeker); k {
if _, err = r.Seek(off, io.SeekStart); err != nil {
return 0, err
}
if o.b != nil {
o.b.Reset(r)
return o.b.Read(p)
} else {
return o.r.Read(p)
}
}
return 0, fs.ErrInvalid
}
func (o *rdr) Size() int64 {
var s int64
if r, k := o.r.(io.Seeker); k {
if c, e := r.Seek(0, io.SeekCurrent); e != nil {
return 0
} else if _, e = r.Seek(c, io.SeekStart); e != nil {
return 0
} else if s, e = r.Seek(0, io.SeekEnd); e != nil {
return 0
} else if _, e = r.Seek(c, io.SeekStart); e != nil {
return 0
} else {
return s
}
}
if r, k := o.r.(io.ReaderAt); k {
if _, e := r.ReadAt(nil, 0); e != nil {
return 0
} else if s, e = io.Copy(io.Discard, o.r); e != nil {
return 0
} else if _, e = r.ReadAt(nil, s); e != nil {
return 0
} else if o.b != nil {
o.b.Reset(o.r)
return s
} else {
return s
}
}
return 0
}
func (o *rdr) Seek(offset int64, whence int) (int64, error) {
if r, k := o.r.(io.Seeker); k {
if n, e := r.Seek(offset, whence); e != nil {
return n, e
} else if o.b != nil {
o.b.Reset(o.r)
return n, nil
} else {
return n, nil
}
}
if whence != io.SeekStart {
return 0, fs.ErrInvalid
}
if _, e := o.ReadAt(make([]byte, 0), 0); e != nil {
return 0, e
} else if o.b != nil {
o.b.Reset(o.r)
return 0, nil
} else {
return 0, nil
}
}
func (o *rdr) Reset() bool {
if r, k := o.r.(io.Seeker); k {
_, e := r.Seek(0, io.SeekStart)
if e == nil {
if o.b != nil {
o.b.Reset(o.r)
}
return true
}
}
if r, k := o.r.(io.ReaderAt); k {
_, e := r.ReadAt(nil, 0)
if e == nil {
if o.b != nil {
o.b.Reset(o.r)
}
return true
}
}
return false
}

View File

@@ -23,32 +23,25 @@
*
*/
package bz2
package tar
import (
"compress/bzip2"
"archive/tar"
"io"
"github.com/nabbar/golib/errors"
arctps "github.com/nabbar/golib/archive/archive/types"
)
func GetFile(src io.ReadSeeker, dst io.WriteSeeker) errors.Error {
if _, e := src.Seek(0, io.SeekStart); e != nil {
return ErrorFileSeek.Error(e)
} else if _, e = dst.Seek(0, io.SeekStart); e != nil {
return ErrorFileSeek.Error(e)
}
r := bzip2.NewReader(src)
// #nosec
_, e := io.Copy(dst, r)
if e != nil {
return ErrorIOCopy.Error(e)
} else if _, e = dst.Seek(0, io.SeekStart); e != nil {
return ErrorFileSeek.Error(e)
} else {
return nil
}
func NewReader(r io.ReadCloser) (arctps.Reader, error) {
return &rdr{
r: r,
z: tar.NewReader(r),
}, nil
}
func NewWriter(w io.WriteCloser) (arctps.Writer, error) {
return &wrt{
w: w,
z: tar.NewWriter(w),
}, nil
}

View File

@@ -0,0 +1,170 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package tar
import (
"archive/tar"
"io"
"io/fs"
arctps "github.com/nabbar/golib/archive/archive/types"
)
type reset interface {
Reset() bool
}
type rdr struct {
r io.ReadCloser
z *tar.Reader
}
func (o *rdr) Reset() bool {
if r, k := o.r.(reset); k {
return r.Reset()
}
return false
}
func (o *rdr) Close() error {
return o.r.Close()
}
func (o *rdr) List() ([]string, error) {
var (
e error
h *tar.Header
l = make([]string, 0)
)
if o.Reset() {
o.z = tar.NewReader(o.r)
}
for e == nil {
h, e = o.z.Next()
if h != nil {
l = append(l, h.Name)
_, _ = io.Copy(io.Discard, o.z)
}
}
return l, nil
}
func (o *rdr) Info(s string) (fs.FileInfo, error) {
var (
e error
h *tar.Header
)
if o.Reset() {
o.z = tar.NewReader(o.r)
}
for e == nil {
h, e = o.z.Next()
if h != nil && h.Name == s {
return h.FileInfo(), nil
} else if h != nil {
_, _ = io.Copy(io.Discard, o.z)
}
}
return nil, fs.ErrNotExist
}
func (o *rdr) Get(s string) (io.ReadCloser, error) {
var (
e error
h *tar.Header
)
if o.Reset() {
o.z = tar.NewReader(o.r)
}
for e == nil {
h, e = o.z.Next()
if h != nil && h.Name == s {
return io.NopCloser(o.z), nil
} else if h != nil {
_, _ = io.Copy(io.Discard, o.z)
}
}
return nil, fs.ErrNotExist
}
func (o *rdr) Has(s string) bool {
var (
e error
h *tar.Header
)
if o.Reset() {
o.z = tar.NewReader(o.r)
}
for e == nil {
h, e = o.z.Next()
if h != nil && h.Name == s {
return true
} else if h != nil {
_, _ = io.Copy(io.Discard, o.z)
}
}
return false
}
func (o *rdr) Walk(fct arctps.FuncExtract) {
var (
e error
h *tar.Header
)
if o.Reset() {
o.z = tar.NewReader(o.r)
}
for e == nil {
h, e = o.z.Next()
if h == nil || e != nil {
continue
}
if !fct(h.FileInfo(), io.NopCloser(o.z), h.Name, h.Linkname) {
return
}
// prevent file cursor not at EOF of current file
_, _ = io.Copy(io.Discard, o.z)
}
}

View File

@@ -0,0 +1,157 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package tar
import (
"archive/tar"
"io"
"io/fs"
"os"
"path/filepath"
arctps "github.com/nabbar/golib/archive/archive/types"
)
type wrt struct {
w io.WriteCloser
z *tar.Writer
}
func (o *wrt) Close() error {
if e := o.z.Flush(); e != nil {
return e
} else if e = o.z.Close(); e != nil {
return e
} else if e = o.w.Close(); e != nil {
return e
}
return nil
}
// Add adds a file to the tar archive.
//
// It takes in the file information, the file reader, and the target path if the new file is a link.
// It returns an error if any operation fails.
func (o *wrt) Add(i fs.FileInfo, r io.ReadCloser, forcePath, target string) error {
var (
e error
h *tar.Header
)
defer func() {
if r != nil {
_ = r.Close()
}
}()
if h, e = tar.FileInfoHeader(i, target); e != nil {
return e
} else if len(target) > 0 {
h.Linkname = target
}
if len(forcePath) > 0 {
h.Name = forcePath
}
if e = o.z.WriteHeader(h); e != nil {
return e
}
if r != nil {
if _, e = io.Copy(o.z, r); e != nil {
return e
}
}
return nil
}
func (o *wrt) FromPath(source string, filter string, fct arctps.ReplaceName) error {
if i, e := os.Stat(source); e == nil && !i.IsDir() {
return o.addFiltering(source, filter, fct, i)
}
return filepath.Walk(source, func(path string, info fs.FileInfo, e error) error {
if e != nil {
return e
}
return o.addFiltering(path, filter, fct, info)
})
}
func (o *wrt) addFiltering(source string, filter string, fct arctps.ReplaceName, info fs.FileInfo) error {
var (
ok bool
err error
hdf *os.File
target string
)
if len(filter) < 1 {
filter = "*"
}
if fct == nil {
fct = func(source string) string {
return source
}
}
if ok, err = filepath.Match(filter, source); err != nil {
return err
} else if !ok {
return nil
}
if info == nil {
return fs.ErrInvalid
} else if info.IsDir() {
return nil
} else if info.Mode()&os.ModeSymlink != 0 { // SymLink
if target, err = os.Readlink(source); err != nil {
return err
}
} else if info.Mode()&os.ModeDevice != 0 { // HardLink
if target, err = os.Readlink(source); err != nil {
return err
}
} else if info.Mode().IsRegular() {
if hdf, err = os.Open(source); err != nil {
return err
} else {
defer func() {
_ = hdf.Close()
}()
}
} else {
return fs.ErrInvalid
}
return o.Add(info, hdf, fct(source), target)
}

80
archive/archive/types.go Normal file
View File

@@ -0,0 +1,80 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive
import "bytes"
type Algorithm uint8
const (
None Algorithm = iota
Tar
Zip
)
func (a Algorithm) IsNone() bool {
return a == None
}
func (a Algorithm) String() string {
switch a {
case Tar:
return "tar"
case Zip:
return "zip"
default:
return "none"
}
}
func (a Algorithm) Extension() string {
switch a {
case Tar:
return ".tar"
case Zip:
return ".zip"
default:
return ""
}
}
func (a Algorithm) DetectHeader(h []byte) bool {
if len(h) < 263 {
return false
}
switch a {
case Tar:
exp := append([]byte("ustar"), 0x00)
val := h[257:263]
return bytes.Equal(val, exp)
case Zip:
exp := []byte{0x50, 0x4b, 0x03, 0x04}
return bytes.Equal(h[0:4], exp)
default:
return false
}
}

View File

@@ -0,0 +1,77 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package types
import (
"io"
"io/fs"
)
type FuncExtract func(fs.FileInfo, io.ReadCloser, string, string) bool
type Reader interface {
io.Closer
// List try to return a slice of all path into the archive or an error.
List() ([]string, error)
// Info returns the file information as fs.FileInfo for the given path or an error.
//
// Parameters:
// - string: the path of the embedded file into the archive to get information for.
//
// Returns:
// - fs.FileInfo: the file information for the given path.
// - error: an error if the file information could not be retrieved.
Info(string) (fs.FileInfo, error)
// Get retrieves an io.ReadCloser or an error based on the provided file path.
//
// Parameters:
// - string: the path of the embedded file into the archive to get information for.
//
// Returns:
// - io.ReadCloser: the read/close stream for the given path.
// - error: an error if the file information could not be retrieved.
Get(string) (io.ReadCloser, error)
// Has will check if the archive contains the given path.
//
// Parameters:
// - string: the path of the embedded file into the archive to get information for.
//
// Returns:
// - bool: true if the archive contains the given path.
Has(string) bool
// Walk applies the given function to each element in the archive.
//
// Parameters:
// - FuncExtract: the function will be call on each item in the archive.
// The function can return false to stop or true to continue the walk.
// The function will accept the following parameters:
// - fs.FileInfo: the file information for the given path.
// - io.ReadCloser: the read/close stream for the given path.
// - string: the path of the embedded file into the archive.
// - string: the link target of the embedded file if it is a link or a symlink.
Walk(FuncExtract)
}

View File

@@ -0,0 +1,56 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package types
import (
"io"
"io/fs"
)
type ReplaceName func(string) string
type Writer interface {
io.Closer
// Add will add the given file into the archive.
//
// Parameter(s):
// - fs.FileInfo: the file information for the given path (permission, size, etc...).
// - io.ReadCloser: the read/close stream to read the data and store it into the archive.
// - string: use to force a different pathname for the embedded file into the archive.
// - string: use to specify the target if the embedded file is a link.
// Return type: error
Add(fs.FileInfo, io.ReadCloser, string, string) error
// FromPath will parse recursively the given path and add it into the archive
//
// Parameter(s):
// - string: the source path to parse recursively and add into the archive.
// - string: a filtering string to accept only certain files (empty do disable filtering).
// - ReplaceName: a function to replace the name of the embedded file, if needed.
// Returns error if triggered
FromPath(string, string, ReplaceName) error
}

View File

@@ -0,0 +1,75 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package zip
import (
"archive/zip"
"io"
"io/fs"
arctps "github.com/nabbar/golib/archive/archive/types"
)
type readerSize interface {
Size() int64
}
type readerSeek interface {
io.Seeker
}
type readerAt interface {
io.ReadCloser
io.ReaderAt
}
func NewReader(r io.ReadCloser) (arctps.Reader, error) {
if s, k := r.(readerSize); !k {
return nil, fs.ErrInvalid
} else if ra, ok := r.(readerAt); !ok {
return nil, fs.ErrInvalid
} else if rs, o := r.(io.Seeker); !o {
return nil, fs.ErrInvalid
} else if siz := s.Size(); siz <= 0 {
return nil, fs.ErrInvalid
} else if _, e := rs.Seek(0, io.SeekStart); e != nil {
return nil, e
} else if z, err := zip.NewReader(ra, siz); err != nil {
return nil, err
} else {
return &rdr{
r: r,
z: z,
}, nil
}
}
func NewWriter(w io.WriteCloser) (arctps.Writer, error) {
return &wrt{
w: w,
z: zip.NewWriter(w),
}, nil
}

View File

@@ -0,0 +1,92 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package zip
import (
"archive/zip"
"io"
"io/fs"
arctps "github.com/nabbar/golib/archive/archive/types"
)
type rdr struct {
r io.ReadCloser
z *zip.Reader
}
func (o *rdr) Close() error {
return o.r.Close()
}
func (o *rdr) List() ([]string, error) {
var res = make([]string, 0, len(o.z.File))
for _, f := range o.z.File {
res = append(res, f.Name)
}
return res, nil
}
func (o *rdr) Info(s string) (fs.FileInfo, error) {
for _, f := range o.z.File {
if f.Name == s {
return f.FileInfo(), nil
}
}
return nil, fs.ErrNotExist
}
func (o *rdr) Get(s string) (io.ReadCloser, error) {
for _, f := range o.z.File {
if f.Name == s {
return f.Open()
}
}
return nil, fs.ErrNotExist
}
func (o *rdr) Has(s string) bool {
for _, f := range o.z.File {
if f.Name == s {
return true
}
}
return false
}
func (o *rdr) Walk(fct arctps.FuncExtract) {
for _, f := range o.z.File {
r, _ := f.Open()
if !fct(f.FileInfo(), r, f.Name, "") {
return
}
}
}

View File

@@ -0,0 +1,141 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package zip
import (
"archive/zip"
"io"
"io/fs"
"os"
"path/filepath"
arctps "github.com/nabbar/golib/archive/archive/types"
)
type wrt struct {
w io.WriteCloser
z *zip.Writer
}
func (o *wrt) Close() error {
if e := o.z.Flush(); e != nil {
return e
} else if e = o.z.Close(); e != nil {
return e
} else if e = o.w.Close(); e != nil {
return e
}
return nil
}
func (o *wrt) Add(i fs.FileInfo, r io.ReadCloser, forcePath, notUse string) error {
var (
e error
h *zip.FileHeader
w io.Writer
)
if r == nil {
return nil
}
defer func() {
if r != nil {
_ = r.Close()
}
}()
if h, e = zip.FileInfoHeader(i); e != nil {
return e
} else if len(forcePath) > 0 {
h.Name = forcePath
}
if w, e = o.z.CreateHeader(h); e != nil {
return e
} else if _, e = io.Copy(w, r); e != nil {
return e
}
return nil
}
func (o *wrt) FromPath(source string, filter string, fct arctps.ReplaceName) error {
if i, e := os.Stat(source); e == nil && !i.IsDir() {
return o.addFiltering(source, filter, fct, i)
}
return filepath.Walk(source, func(path string, info fs.FileInfo, e error) error {
if e != nil {
return e
}
return o.addFiltering(path, filter, fct, info)
})
}
func (o *wrt) addFiltering(source string, filter string, fct arctps.ReplaceName, info fs.FileInfo) error {
var (
ok bool
err error
hdf *os.File
)
if len(filter) < 1 {
filter = "*"
}
if fct == nil {
fct = func(source string) string {
return source
}
}
if ok, err = filepath.Match(filter, source); err != nil {
return err
} else if !ok {
return nil
}
if info == nil {
return fs.ErrInvalid
} else if info.IsDir() {
return nil
} else if info.Mode().IsRegular() {
if hdf, err = os.Open(source); err != nil {
return err
} else {
defer func() {
_ = hdf.Close()
}()
}
} else {
return fs.ErrInvalid
}
return o.Add(info, hdf, fct(source), "")
}

View File

@@ -0,0 +1,118 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive_test
import (
"bufio"
"io"
"os"
libarc "github.com/nabbar/golib/archive"
arccmp "github.com/nabbar/golib/archive/compress"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("archive/compress/bzip", func() {
Context("Write/Read a bzip compressed file", func() {
It("Create a bzip compressed file must succeed", func() {
var (
hdf *os.File
buf *bufio.Writer
wrt io.WriteCloser
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
arc[arccmp.Bzip2.String()] = "lorem_ipsum" + arccmp.Bzip2.Extension()
hdf, err = os.Create(arc[arccmp.Bzip2.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
wrt, err = arccmp.Bzip2.Writer(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(wrt).ToNot(BeNil())
buf = bufio.NewWriter(wrt)
_, err = buf.WriteString(loremIpsum)
Expect(err).ToNot(HaveOccurred())
err = buf.Flush()
Expect(err).ToNot(HaveOccurred())
err = hdf.Sync()
Expect(err).ToNot(HaveOccurred())
err = wrt.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).ToNot(HaveOccurred())
})
It("Detect and Extract a bzip compressed file must succeed", func() {
var (
hdf *os.File
alg arccmp.Algorithm
rdr io.ReadCloser
buf *bufio.Reader
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
hdf, err = os.Open(arc[arccmp.Bzip2.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
alg, rdr, err = libarc.DetectCompression(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(rdr).ToNot(BeNil())
Expect(alg).To(Equal(arccmp.Bzip2))
buf = bufio.NewReader(rdr)
_, err = io.Copy(io.Discard, buf)
Expect(err).ToNot(HaveOccurred())
err = buf.UnreadByte()
Expect(err).To(HaveOccurred())
err = rdr.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).ToNot(HaveOccurred())
})
})
})

View File

@@ -0,0 +1,101 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive_test
import (
"strings"
libarc "github.com/nabbar/golib/archive"
arcarc "github.com/nabbar/golib/archive/archive"
arccmp "github.com/nabbar/golib/archive/compress"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func testingCompress(alg arccmp.Algorithm, str, ext string) {
var (
tmp arccmp.Algorithm
jsn = []byte("\"" + str + "\"")
res []byte
)
Expect(libarc.ParseCompression(str)).To(Equal(alg))
Expect(strings.ToLower(alg.String())).To(Equal(str))
Expect(strings.ToLower(alg.Extension())).To(Equal(ext))
Expect(alg.IsNone()).To(BeFalse())
err = tmp.UnmarshalJSON(jsn)
Expect(err).ToNot(HaveOccurred())
Expect(tmp).To(Equal(alg))
res, err = tmp.MarshalJSON()
Expect(err).ToNot(HaveOccurred())
Expect(res).To(Equal(jsn))
}
func testingArchive(alg arcarc.Algorithm, str, ext string) {
var (
tmp arcarc.Algorithm
jsn = []byte("\"" + str + "\"")
res []byte
)
Expect(libarc.ParseArchive(str)).To(Equal(alg))
Expect(strings.ToLower(alg.String())).To(Equal(str))
Expect(strings.ToLower(alg.Extension())).To(Equal(ext))
Expect(alg.IsNone()).To(BeFalse())
err = tmp.UnmarshalJSON(jsn)
Expect(err).ToNot(HaveOccurred())
Expect(tmp).To(Equal(alg))
res, err = tmp.MarshalJSON()
Expect(err).ToNot(HaveOccurred())
Expect(res).To(Equal(jsn))
}
var _ = Describe("archive/archive/algorithm", func() {
Context("Using algorithm const", func() {
It("gzip must succeed", func() {
testingCompress(arccmp.Gzip, "gzip", ".gz")
})
It("bzip2 must succeed", func() {
testingCompress(arccmp.Bzip2, "bzip2", ".bz2")
})
It("lz4 must succeed", func() {
testingCompress(arccmp.LZ4, "lz4", ".lz4")
})
It("xz must succeed", func() {
testingCompress(arccmp.XZ, "xz", ".xz")
})
It("tar must succeed", func() {
testingArchive(arcarc.Tar, "tar", ".tar")
})
It("zip must succeed", func() {
testingArchive(arcarc.Zip, "zip", ".zip")
})
})
})

View File

@@ -0,0 +1,118 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive_test
import (
"bufio"
"io"
"os"
libarc "github.com/nabbar/golib/archive"
arccmp "github.com/nabbar/golib/archive/compress"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("archive/compress/gzip", func() {
Context("Write/Read a gzip compressed file", func() {
It("Create a gzip compressed file must succeed", func() {
var (
hdf *os.File
buf *bufio.Writer
wrt io.WriteCloser
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
arc[arccmp.Gzip.String()] = "lorem_ipsum" + arccmp.Gzip.Extension()
hdf, err = os.Create(arc[arccmp.Gzip.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
wrt, err = arccmp.Gzip.Writer(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(wrt).ToNot(BeNil())
buf = bufio.NewWriter(wrt)
_, err = buf.WriteString(loremIpsum)
Expect(err).ToNot(HaveOccurred())
err = buf.Flush()
Expect(err).ToNot(HaveOccurred())
err = hdf.Sync()
Expect(err).ToNot(HaveOccurred())
err = wrt.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).ToNot(HaveOccurred())
})
It("Detect and Extract a gzip compressed file must succeed", func() {
var (
hdf *os.File
alg arccmp.Algorithm
rdr io.ReadCloser
buf *bufio.Reader
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
hdf, err = os.Open(arc[arccmp.Gzip.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
alg, rdr, err = libarc.DetectCompression(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(rdr).ToNot(BeNil())
Expect(alg).To(Equal(arccmp.Gzip))
buf = bufio.NewReader(rdr)
_, err = io.Copy(io.Discard, buf)
Expect(err).ToNot(HaveOccurred())
err = buf.UnreadByte()
Expect(err).To(HaveOccurred())
err = rdr.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).ToNot(HaveOccurred())
})
})
})

118
archive/archive_lz4_test.go Normal file
View File

@@ -0,0 +1,118 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive_test
import (
"bufio"
"io"
"os"
libarc "github.com/nabbar/golib/archive"
arccmp "github.com/nabbar/golib/archive/compress"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("archive/compress/lz4", func() {
Context("Write/Read a lz4 compressed file", func() {
It("Create a lz4 compressed file must succeed", func() {
var (
hdf *os.File
buf *bufio.Writer
wrt io.WriteCloser
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
arc[arccmp.LZ4.String()] = "lorem_ipsum" + arccmp.LZ4.Extension()
hdf, err = os.Create(arc[arccmp.LZ4.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
wrt, err = arccmp.LZ4.Writer(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(wrt).ToNot(BeNil())
buf = bufio.NewWriter(wrt)
_, err = buf.WriteString(loremIpsum)
Expect(err).ToNot(HaveOccurred())
err = buf.Flush()
Expect(err).ToNot(HaveOccurred())
err = hdf.Sync()
Expect(err).ToNot(HaveOccurred())
err = wrt.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).ToNot(HaveOccurred())
})
It("Detect and Extract a lz4 compressed file must succeed", func() {
var (
hdf *os.File
alg arccmp.Algorithm
rdr io.ReadCloser
buf *bufio.Reader
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
hdf, err = os.Open(arc[arccmp.LZ4.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
alg, rdr, err = libarc.DetectCompression(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(rdr).ToNot(BeNil())
Expect(alg).To(Equal(arccmp.LZ4))
buf = bufio.NewReader(rdr)
_, err = io.Copy(io.Discard, buf)
Expect(err).ToNot(HaveOccurred())
err = buf.UnreadByte()
Expect(err).To(HaveOccurred())
err = rdr.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).ToNot(HaveOccurred())
})
})
})

View File

@@ -0,0 +1,121 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive_test
import (
"crypto/rand"
"math/big"
"os"
"path/filepath"
"reflect"
"strconv"
"strings"
"testing"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
/*
Using https://onsi.github.io/ginkgo/
Running with $> ginkgo -cover .
*/
type EmptyStruct struct{}
var (
err error
lst = make(map[string]string, 0)
arc = make(map[string]string, 0)
dst string
)
func rnd(min, max int) int {
diff := big.NewInt(int64(max - min))
n, _ := rand.Int(rand.Reader, diff)
return min + int(n.Int64())
}
// TestGolibEncodingAESHelper tests the Golib AES Encoding Helper function.
func TestGolibArchiveHelper(t *testing.T) {
time.Sleep(500 * time.Millisecond) // Adding delay for better testing synchronization
RegisterFailHandler(Fail) // Registering fail handler for better test failure reporting
RunSpecs(t, "Archive Helper Suite") // Running the test suite for Encoding AES Helper
}
var _ = BeforeSuite(func() {
for i := 1; i <= 5; i++ {
p := filepath.Join(strings.Replace(reflect.TypeOf(EmptyStruct{}).PkgPath(), "_test", "", -1), "lorem_ipsum_"+strconv.Itoa(i)+".txt")
lst[filepath.Base(p)] = p
}
dst = filepath.Join(strings.Replace(reflect.TypeOf(EmptyStruct{}).PkgPath(), "_test", "", -1), "extract_all_dir")
var (
w *os.File
n int
m = len(loremIpsum)
)
for f := range lst {
w, err = os.Create(filepath.Base(f))
Expect(err).ToNot(HaveOccurred())
Expect(w).ToNot(BeNil())
l := rnd(10, m)
p := rnd(0, m-l)
n, err = w.Write([]byte(loremIpsum[p : p+l]))
Expect(err).ToNot(HaveOccurred())
Expect(n).To(BeEquivalentTo(l))
err = w.Sync()
Expect(err).ToNot(HaveOccurred())
err = w.Close()
Expect(err).ToNot(HaveOccurred())
}
})
var _ = AfterSuite(func() {
for b := range lst {
_ = os.Remove(b)
}
for _, b := range arc {
_ = os.Remove(b)
}
for strings.Contains(dst, string(filepath.Separator)) && len(dst) > 3 {
_ = os.RemoveAll(dst)
dst = filepath.Dir(dst)
}
if len(dst) > 3 {
_ = os.RemoveAll(dst)
}
})

208
archive/archive_tar_test.go Normal file
View File

@@ -0,0 +1,208 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive_test
import (
"io"
"io/fs"
"os"
"path/filepath"
libarc "github.com/nabbar/golib/archive"
arcarc "github.com/nabbar/golib/archive/archive"
arctps "github.com/nabbar/golib/archive/archive/types"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("archive/archive/tar", func() {
Context("Write/Read a tar archive file", func() {
It("Create a tar archive must succeed", func() {
var (
hdf *os.File
wrt arctps.Writer
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
arc[arcarc.Tar.String()] = "lorem_ipsum" + arcarc.Tar.Extension()
hdf, err = os.Create(arc[arcarc.Tar.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
wrt, err = arcarc.Tar.Writer(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(wrt).ToNot(BeNil())
for f, p := range lst {
var (
i fs.FileInfo
h *os.File
)
i, err = os.Stat(f)
Expect(err).ToNot(HaveOccurred())
Expect(i).ToNot(BeNil())
h, err = os.Open(f)
Expect(err).ToNot(HaveOccurred())
Expect(h).ToNot(BeNil())
err = wrt.Add(i, h, p, "")
Expect(err).ToNot(HaveOccurred())
err = h.Close()
Expect(err).To(HaveOccurred())
}
err = hdf.Sync()
Expect(err).ToNot(HaveOccurred())
err = wrt.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).To(HaveOccurred())
})
It("Detect and Extract a tar archive must succeed", func() {
var (
hdf *os.File
alg arcarc.Algorithm
rdr arctps.Reader
fnd []string
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
hdf, err = os.Open(arc[arcarc.Tar.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
alg, rdr, _, err = libarc.DetectArchive(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(rdr).ToNot(BeNil())
Expect(alg).To(Equal(arcarc.Tar))
fnd, err = rdr.List()
Expect(err).ToNot(HaveOccurred())
Expect(fnd).ToNot(BeNil())
for _, g := range fnd {
f, ok := lst[filepath.Base(g)]
Expect(ok).To(BeTrue())
Expect(g).To(Equal(f))
}
for _, f := range lst {
var (
i fs.FileInfo
r io.ReadCloser
n int64
)
Expect(rdr.Has(f)).To(BeTrue())
i, err = rdr.Info(f)
Expect(err).ToNot(HaveOccurred())
Expect(i).ToNot(BeNil())
r, err = rdr.Get(f)
Expect(err).ToNot(HaveOccurred())
Expect(r).ToNot(BeNil())
n, err = io.Copy(io.Discard, r)
Expect(err).ToNot(HaveOccurred())
Expect(n).To(BeEquivalentTo(i.Size()))
err = r.Close()
Expect(err).ToNot(HaveOccurred())
}
err = rdr.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).To(HaveOccurred())
})
It("Detect and Extract a tar archive with walk must succeed", func() {
var (
hdf *os.File
alg arcarc.Algorithm
rdr arctps.Reader
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
hdf, err = os.Open(arc[arcarc.Tar.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
alg, rdr, _, err = arcarc.Detect(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(rdr).ToNot(BeNil())
Expect(alg).To(Equal(arcarc.Tar))
rdr.Walk(func(i fs.FileInfo, r io.ReadCloser, f, t string) bool {
g, ok := lst[filepath.Base(f)]
Expect(ok).To(BeTrue())
Expect(f).To(Equal(g))
Expect(i).ToNot(BeNil())
Expect(r).ToNot(BeNil())
var n int64
n, err = io.Copy(io.Discard, r)
Expect(err).ToNot(HaveOccurred())
Expect(n).To(BeEquivalentTo(i.Size()))
err = r.Close()
Expect(err).ToNot(HaveOccurred())
return true
})
err = rdr.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).To(HaveOccurred())
})
})
})

123
archive/archive_tgz_test.go Normal file
View File

@@ -0,0 +1,123 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive_test
import (
"io"
"io/fs"
"os"
libarc "github.com/nabbar/golib/archive"
arcarc "github.com/nabbar/golib/archive/archive"
arctps "github.com/nabbar/golib/archive/archive/types"
arccmp "github.com/nabbar/golib/archive/compress"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("archive/tar+gzip with extract all", func() {
Context("Create a tar+gzip archive file and extract it with extract all", func() {
It("Create a tar archive must succeed", func() {
var (
hdf *os.File
gzp io.WriteCloser
wrt arctps.Writer
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
arc[arcarc.Tar.String()+arccmp.Gzip.String()] = "lorem_ipsum" + arcarc.Tar.Extension() + arccmp.Gzip.Extension()
hdf, err = os.Create(arc[arcarc.Tar.String()+arccmp.Gzip.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
gzp, err = arccmp.Gzip.Writer(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(gzp).ToNot(BeNil())
wrt, err = arcarc.Tar.Writer(gzp)
Expect(err).ToNot(HaveOccurred())
Expect(wrt).ToNot(BeNil())
for f, p := range lst {
var (
i fs.FileInfo
h *os.File
)
i, err = os.Stat(f)
Expect(err).ToNot(HaveOccurred())
Expect(i).ToNot(BeNil())
h, err = os.Open(f)
Expect(err).ToNot(HaveOccurred())
Expect(h).ToNot(BeNil())
err = wrt.Add(i, h, p, "")
Expect(err).ToNot(HaveOccurred())
err = h.Close()
Expect(err).To(HaveOccurred())
}
err = hdf.Sync()
Expect(err).ToNot(HaveOccurred())
err = wrt.Close()
Expect(err).ToNot(HaveOccurred())
err = gzp.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).ToNot(HaveOccurred())
})
It("Detect all a tar+gzip archive must succeed", func() {
var hdf *os.File
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
hdf, err = os.Open(arc[arcarc.Tar.String()+arccmp.Gzip.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
err = libarc.ExtractAll(hdf, arc[arcarc.Tar.String()+arccmp.Gzip.String()], dst)
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).ToNot(HaveOccurred())
})
})
})

118
archive/archive_xz_test.go Normal file
View File

@@ -0,0 +1,118 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive_test
import (
"bufio"
"io"
"os"
libarc "github.com/nabbar/golib/archive"
arccmp "github.com/nabbar/golib/archive/compress"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("archive/compress/xz", func() {
Context("Write/Read a xz compressed file", func() {
It("Create a xz compressed file must succeed", func() {
var (
hdf *os.File
buf *bufio.Writer
wrt io.WriteCloser
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
arc[arccmp.XZ.String()] = "lorem_ipsum" + arccmp.XZ.Extension()
hdf, err = os.Create(arc[arccmp.XZ.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
wrt, err = arccmp.XZ.Writer(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(wrt).ToNot(BeNil())
buf = bufio.NewWriter(wrt)
_, err = buf.WriteString(loremIpsum)
Expect(err).ToNot(HaveOccurred())
err = buf.Flush()
Expect(err).ToNot(HaveOccurred())
err = hdf.Sync()
Expect(err).ToNot(HaveOccurred())
err = wrt.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).ToNot(HaveOccurred())
})
It("Detect and Extract a xz compressed file must succeed", func() {
var (
hdf *os.File
alg arccmp.Algorithm
rdr io.ReadCloser
buf *bufio.Reader
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
hdf, err = os.Open(arc[arccmp.XZ.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
alg, rdr, err = libarc.DetectCompression(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(rdr).ToNot(BeNil())
Expect(alg).To(Equal(arccmp.XZ))
buf = bufio.NewReader(rdr)
_, err = io.Copy(io.Discard, buf)
Expect(err).ToNot(HaveOccurred())
err = buf.UnreadByte()
Expect(err).To(HaveOccurred())
err = rdr.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).ToNot(HaveOccurred())
})
})
})

212
archive/archive_zip_test.go Normal file
View File

@@ -0,0 +1,212 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive_test
import (
"io"
"io/fs"
"os"
"path/filepath"
libarc "github.com/nabbar/golib/archive"
arcarc "github.com/nabbar/golib/archive/archive"
arctps "github.com/nabbar/golib/archive/archive/types"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("archive/archive/zip", func() {
Context("Write/Read a zip archive file", func() {
It("Create a zip archive must succeed", func() {
var (
hdf *os.File
wrt arctps.Writer
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
arc[arcarc.Zip.String()] = "lorem_ipsum" + arcarc.Zip.Extension()
hdf, err = os.Create(arc[arcarc.Zip.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
wrt, err = arcarc.Zip.Writer(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(wrt).ToNot(BeNil())
for f, p := range lst {
var (
i fs.FileInfo
h *os.File
)
i, err = os.Stat(f)
Expect(err).ToNot(HaveOccurred())
Expect(i).ToNot(BeNil())
h, err = os.Open(f)
Expect(err).ToNot(HaveOccurred())
Expect(h).ToNot(BeNil())
err = wrt.Add(i, h, p, "")
Expect(err).ToNot(HaveOccurred())
err = h.Close()
Expect(err).To(HaveOccurred())
}
err = hdf.Sync()
Expect(err).ToNot(HaveOccurred())
err = wrt.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).To(HaveOccurred())
})
It("Detect and Extract a zip archive must succeed", func() {
var (
hdf *os.File
alg arcarc.Algorithm
rdr arctps.Reader
fnd []string
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
i, e := os.Stat(arc[arcarc.Zip.String()])
Expect(e).ToNot(HaveOccurred())
Expect(i).ToNot(BeNil())
hdf, err = os.Open(arc[arcarc.Zip.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
alg, rdr, _, err = libarc.DetectArchive(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(rdr).ToNot(BeNil())
Expect(alg).To(Equal(arcarc.Zip))
fnd, err = rdr.List()
Expect(err).ToNot(HaveOccurred())
Expect(fnd).ToNot(BeNil())
for _, g := range fnd {
f, ok := lst[filepath.Base(g)]
Expect(ok).To(BeTrue())
Expect(g).To(Equal(f))
}
for _, f := range lst {
var (
i fs.FileInfo
r io.ReadCloser
n int64
)
Expect(rdr.Has(f)).To(BeTrue())
i, err = rdr.Info(f)
Expect(err).ToNot(HaveOccurred())
Expect(i).ToNot(BeNil())
r, err = rdr.Get(f)
Expect(err).ToNot(HaveOccurred())
Expect(r).ToNot(BeNil())
n, err = io.Copy(io.Discard, r)
Expect(err).ToNot(HaveOccurred())
Expect(n).To(BeEquivalentTo(i.Size()))
err = r.Close()
Expect(err).ToNot(HaveOccurred())
}
err = rdr.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).To(HaveOccurred())
})
It("Detect and Extract a zip archive with walk must succeed", func() {
var (
hdf *os.File
alg arcarc.Algorithm
rdr arctps.Reader
)
defer func() {
if hdf != nil {
_ = hdf.Close()
}
}()
hdf, err = os.Open(arc[arcarc.Zip.String()])
Expect(err).ToNot(HaveOccurred())
Expect(hdf).ToNot(BeNil())
alg, rdr, _, err = arcarc.Detect(hdf)
Expect(err).ToNot(HaveOccurred())
Expect(rdr).ToNot(BeNil())
Expect(alg).To(Equal(arcarc.Zip))
rdr.Walk(func(i fs.FileInfo, r io.ReadCloser, f, t string) bool {
g, ok := lst[filepath.Base(f)]
Expect(ok).To(BeTrue())
Expect(f).To(Equal(g))
Expect(i).ToNot(BeNil())
Expect(r).ToNot(BeNil())
var n int64
n, err = io.Copy(io.Discard, r)
Expect(err).ToNot(HaveOccurred())
Expect(n).To(BeEquivalentTo(i.Size()))
err = r.Close()
Expect(err).ToNot(HaveOccurred())
return true
})
err = rdr.Close()
Expect(err).ToNot(HaveOccurred())
err = hdf.Close()
Expect(err).To(HaveOccurred())
})
})
})

View File

@@ -1,64 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package bz2
import (
"fmt"
arcmod "github.com/nabbar/golib/archive/archive"
liberr "github.com/nabbar/golib/errors"
)
const pkgName = "golib/archive/bz2"
const (
ErrorParamEmpty liberr.CodeError = iota + arcmod.MinPkgArchiveBZ2
ErrorFileSeek
ErrorIOCopy
)
func init() {
if liberr.ExistInMapMessage(ErrorParamEmpty) {
panic(fmt.Errorf("error code collision %s", pkgName))
}
liberr.RegisterIdFctMessage(ErrorParamEmpty, getMessage)
}
func getMessage(code liberr.CodeError) (message string) {
switch code {
case liberr.UnknownError:
return liberr.NullMessage
case ErrorParamEmpty:
return "given parameters is empty"
case ErrorFileSeek:
return "cannot seek into file"
case ErrorIOCopy:
return "io copy occurs error"
}
return liberr.NullMessage
}

View File

@@ -0,0 +1,79 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package compress
import (
"bytes"
"encoding/json"
"strings"
)
func (a Algorithm) MarshalText() ([]byte, error) {
return []byte(a.String()), nil
}
func (a *Algorithm) UnmarshalText(b []byte) error {
*a = None
s := strings.Trim(string(b), "\"")
s = strings.Trim(s, "'")
s = strings.TrimSpace(s)
switch {
case strings.EqualFold(s, Gzip.String()):
*a = Gzip
case strings.EqualFold(s, Bzip2.String()):
*a = Bzip2
case strings.EqualFold(s, LZ4.String()):
*a = LZ4
case strings.EqualFold(s, XZ.String()):
*a = XZ
default:
*a = None
}
return nil
}
func (a Algorithm) MarshalJSON() ([]byte, error) {
if a.IsNone() {
return []byte("null"), nil
}
return append(append([]byte{'"'}, []byte(a.String())...), '"'), nil
}
func (a *Algorithm) UnmarshalJSON(b []byte) error {
var s string
if n := []byte("null"); bytes.Equal(b, n) {
*a = None
return nil
} else if err := json.Unmarshal(b, &s); err != nil {
return err
} else {
return a.UnmarshalText([]byte(s))
}
}

View File

@@ -0,0 +1,76 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package compress
import (
"bufio"
"io"
)
func Parse(s string) Algorithm {
var alg = None
if e := alg.UnmarshalText([]byte(s)); e != nil {
return None
} else {
return alg
}
}
func Detect(r io.Reader) (Algorithm, io.ReadCloser, error) {
var (
err error
alg Algorithm
bfr = bufio.NewReader(r)
buf []byte
res io.ReadCloser
)
if buf, err = bfr.Peek(6); err != nil {
return None, nil, err
}
// Vérifier le type de compression
switch {
case Gzip.DetectHeader(buf): // gzip
alg = Gzip
case Bzip2.DetectHeader(buf): // bzip2
alg = Bzip2
case LZ4.DetectHeader(buf): // lz4
alg = LZ4
case XZ.DetectHeader(buf): // xz
alg = XZ
default:
alg = None
}
if err != nil {
return None, nil, err
} else if res, err = alg.Reader(bfr); err != nil {
return None, nil, err
} else {
return alg, res, err
}
}

67
archive/compress/io.go Normal file
View File

@@ -0,0 +1,67 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package compress
import (
"compress/bzip2"
"compress/gzip"
"io"
bz2 "github.com/dsnet/compress/bzip2"
"github.com/pierrec/lz4/v4"
"github.com/ulikunitz/xz"
)
func (a Algorithm) Reader(r io.Reader) (io.ReadCloser, error) {
switch a {
case Bzip2:
return io.NopCloser(bzip2.NewReader(r)), nil
case Gzip:
return gzip.NewReader(r)
case LZ4:
return io.NopCloser(lz4.NewReader(r)), nil
case XZ:
c, e := xz.NewReader(r)
return io.NopCloser(c), e
default:
return io.NopCloser(r), nil
}
}
func (a Algorithm) Writer(w io.WriteCloser) (io.WriteCloser, error) {
switch a {
case Bzip2:
return bz2.NewWriter(w, nil)
case Gzip:
return gzip.NewWriter(w), nil
case LZ4:
return lz4.NewWriter(w), nil
case XZ:
return xz.NewWriter(w)
default:
return w, nil
}
}

96
archive/compress/types.go Normal file
View File

@@ -0,0 +1,96 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package compress
import "bytes"
type Algorithm uint8
const (
None Algorithm = iota
Bzip2
Gzip
LZ4
XZ
)
func (a Algorithm) IsNone() bool {
return a == None
}
func (a Algorithm) String() string {
switch a {
case Gzip:
return "gzip"
case Bzip2:
return "bzip2"
case LZ4:
return "lz4"
case XZ:
return "xz"
default:
return "none"
}
}
func (a Algorithm) Extension() string {
switch a {
case Gzip:
return ".gz"
case Bzip2:
return ".bz2"
case LZ4:
return ".lz4"
case XZ:
return ".xz"
default:
return ""
}
}
func (a Algorithm) DetectHeader(h []byte) bool {
if len(h) < 6 {
return false
}
switch a {
case Gzip:
exp := []byte{31, 139}
return bytes.Equal(h[0:2], exp)
case Bzip2:
exp := []byte{'B', 'Z', 'h'}
return bytes.Equal(h[0:3], exp) && h[3] >= '0' && h[3] <= '9'
case LZ4:
exp := []byte{0x04, 0x22, 0x4D, 0x18}
return bytes.Equal(h[0:4], exp)
case XZ:
exp := []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}
alt := []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
return bytes.Equal(h[0:6], exp) || bytes.Equal(h[0:6], alt)
default:
return false
}
}

View File

@@ -1,79 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package archive
import (
"fmt"
arcmod "github.com/nabbar/golib/archive/archive"
liberr "github.com/nabbar/golib/errors"
)
const pkgName = "golib/archive"
const (
ErrorParamEmpty liberr.CodeError = iota + arcmod.MinPkgArchive
ErrorFileSeek
ErrorFileOpen
ErrorFileClose
ErrorDirCreate
ErrorDirStat
ErrorDirNotDir
ErrorIOCopy
)
func init() {
if liberr.ExistInMapMessage(ErrorParamEmpty) {
panic(fmt.Errorf("error code collision %s", pkgName))
}
liberr.RegisterIdFctMessage(ErrorParamEmpty, getMessage)
}
func getMessage(code liberr.CodeError) (message string) {
switch code {
case liberr.UnknownError:
return liberr.NullMessage
case ErrorParamEmpty:
return "given parameters is empty"
case ErrorFileSeek:
return "cannot seek into file"
case ErrorFileOpen:
return "cannot open file"
case ErrorFileClose:
return "closing file occurs error"
case ErrorDirCreate:
return "make directory occurs error"
case ErrorDirStat:
return "checking directory occurs error"
case ErrorDirNotDir:
return "directory given is not a directory"
case ErrorIOCopy:
return "error occurs when io copy"
}
return liberr.NullMessage
}

185
archive/extract.go Normal file
View File

@@ -0,0 +1,185 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive
import (
"io"
"io/fs"
"os"
"path/filepath"
"strings"
arcarc "github.com/nabbar/golib/archive/archive"
arctps "github.com/nabbar/golib/archive/archive/types"
arccmp "github.com/nabbar/golib/archive/compress"
)
func ExtractAll(r io.ReadCloser, archiveName, destination string) error {
var (
e error
n string
a arccmp.Algorithm
o io.ReadCloser
)
if r == nil {
return fs.ErrInvalid
}
for e == nil {
a, o, e = DetectCompression(r)
if a.IsNone() {
break
}
n = strings.TrimSuffix(filepath.Base(archiveName), a.Extension())
return ExtractAll(o, n, destination)
}
var (
b arcarc.Algorithm
z arctps.Reader
)
if b, z, r, e = DetectArchive(o); e != nil {
return e
} else if b.IsNone() {
return writeFile(archiveName, destination, r, nil)
} else if z == nil {
return fs.ErrInvalid
} else {
var err error
z.Walk(func(info fs.FileInfo, closer io.ReadCloser, dst, target string) bool {
defer func() {
if closer != nil {
_ = closer.Close()
}
}()
if info.IsDir() {
if e = createPath(filepath.Join(destination, cleanPath(dst)), info.Mode()); e != nil {
err = e
return false
}
} else if info.Mode()&os.ModeSymlink != 0 {
if e = writeSymLink(true, dst, target, destination); e != nil {
err = e
return false
}
} else if info.Mode()&os.ModeDevice != 0 {
if e = writeSymLink(false, dst, target, destination); e != nil {
err = e
return false
}
} else if info.Mode().IsRegular() {
if e = writeFile(dst, destination, closer, info); e != nil {
err = e
return false
}
}
// prevent file cursor not at EOF of current file for TAPE Archive
_, _ = io.Copy(io.Discard, closer)
return true
})
return err
}
}
func cleanPath(path string) string {
for strings.Contains(path, ".."+string(filepath.Separator)) {
path = filepath.Clean(path)
}
return path
}
func createPath(dest string, info os.FileMode) error {
if i, err := os.Stat(dest); err == nil {
if i.IsDir() {
return nil
} else {
return os.ErrInvalid
}
} else if os.IsNotExist(err) {
if err = createPath(filepath.Dir(dest), info); err != nil {
return err
} else if info != 0 {
return os.Mkdir(dest, info)
} else {
return os.Mkdir(dest, 0755)
}
} else {
return err
}
}
func writeFile(name, dest string, r io.ReadCloser, i fs.FileInfo) error {
var (
dst = filepath.Join(dest, cleanPath(name))
hdf *os.File
err error
)
defer func() {
if hdf != nil {
_ = hdf.Sync()
_ = hdf.Close()
}
}()
if err = createPath(filepath.Dir(dst), 0); err != nil {
return err
} else if hdf, err = os.Create(dst); err != nil {
return err
} else if _, err = io.Copy(hdf, r); err != nil {
return err
} else if i != nil {
if err = os.Chmod(dst, i.Mode()); err != nil {
return err
}
}
return nil
}
func writeSymLink(isSymLink bool, name, target, dest string) error {
var (
dst = filepath.Join(dest, cleanPath(name))
err error
)
if err = createPath(filepath.Dir(dst), 0); err != nil {
return err
} else if isSymLink {
return os.Symlink(target, dst)
} else {
return os.Link(target, dst)
}
}

View File

@@ -1,76 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package gzip
import (
"fmt"
arcmod "github.com/nabbar/golib/archive/archive"
liberr "github.com/nabbar/golib/errors"
)
const pkgName = "golib/archive/gzip"
const (
ErrorParamEmpty liberr.CodeError = iota + arcmod.MinPkgArchiveGZip
ErrorParamMismatching
ErrorGZCreate
ErrorGZReader
ErrorFileSeek
ErrorIOCopy
ErrorFileOpen
)
func init() {
if liberr.ExistInMapMessage(ErrorParamEmpty) {
panic(fmt.Errorf("error code collision %s", pkgName))
}
liberr.RegisterIdFctMessage(ErrorParamEmpty, getMessage)
}
func getMessage(code liberr.CodeError) (message string) {
switch code {
case liberr.UnknownError:
return liberr.NullMessage
case ErrorParamEmpty:
return "given parameters is empty"
case ErrorParamMismatching:
return "given parameters is not matching the awaiting scope"
case ErrorGZCreate:
return "cannot create the GZip archive"
case ErrorGZReader:
return "cannot create new reader GZip"
case ErrorFileSeek:
return "cannot seek into file"
case ErrorIOCopy:
return "io copy occurs error"
case ErrorFileOpen:
return "cannot open file content"
}
return liberr.NullMessage
}

View File

@@ -1,91 +0,0 @@
/***********************************************************************************************************************
*
* MIT License
*
* Copyright (c) 2021 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
**********************************************************************************************************************/
package gzip
import (
"compress/gzip"
"fmt"
"io"
"os"
liberr "github.com/nabbar/golib/errors"
)
func Create(archive io.WriteSeeker, stripPath string, comment string, content ...string) (bool, liberr.Error) {
var (
w *gzip.Writer
f *os.File
err error
)
if len(content) != 1 {
//nolint #goerr113
return false, ErrorParamMismatching.Error(fmt.Errorf("content path must be limited to strictly one contents"))
}
if _, err = archive.Seek(0, io.SeekStart); err != nil {
return false, ErrorFileSeek.Error(err)
}
if _, err = os.Stat(content[0]); err != nil {
return false, ErrorParamEmpty.Error(err)
}
w = gzip.NewWriter(archive)
defer func() {
if w != nil {
_ = w.Close()
}
}()
if f, err = os.Open(content[0]); err != nil {
return false, ErrorFileOpen.Error(err)
}
defer func() {
if f != nil {
_ = f.Close()
}
}()
if _, err = io.Copy(w, f); err != nil {
return false, ErrorIOCopy.Error(err)
}
if err = w.Close(); err != nil {
return false, ErrorGZCreate.Error(err)
}
if _, err = archive.Seek(0, io.SeekStart); err != nil {
return false, ErrorFileSeek.Error(err)
}
return true, nil
}

View File

@@ -1,54 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package gzipreader
import (
"bytes"
"compress/gzip"
"io"
)
type GZipReader interface {
io.Reader
LenCompressed() int64
LenUnCompressed() int64
Rate() float64
}
// GzipReader is used to GZIP a io.reader on fly.
// The given io.reader is not a gzip reader but the result is a GZipped reader
func GzipReader(r io.Reader) GZipReader {
b := bytes.NewBuffer(make([]byte, 0, 32*1024))
return &gzr{
r: r,
b: b,
z: gzip.NewWriter(b),
}
}

View File

@@ -1,97 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package gzipreader
import (
"bytes"
"compress/gzip"
"errors"
"io"
)
type gzr struct {
r io.Reader
b *bytes.Buffer
z *gzip.Writer
nc int64
nu int64
}
func (o *gzr) Read(p []byte) (n int, err error) {
var (
s []byte
er error
nr int
ew error
nw int
)
if i := cap(p); i > o.b.Cap() || i < 1 {
s = make([]byte, 0, o.b.Cap())
} else {
s = make([]byte, 0, i)
}
nr, er = o.r.Read(s)
o.nu += int64(nr)
if er != nil && !errors.Is(er, io.EOF) {
return 0, err
} else if nr > 0 {
nw, ew = o.z.Write(s)
}
if ew != nil {
return 0, ew
} else if nw != nr {
return 0, errors.New("invalid write buffer")
} else if er != nil && errors.Is(er, io.EOF) {
if ew = o.z.Close(); ew != nil {
return 0, ew
}
}
copy(p, o.b.Bytes())
o.b.Reset()
o.nc += int64(len(p))
return len(p), er
}
func (o *gzr) LenCompressed() int64 {
return o.nc
}
func (o *gzr) LenUnCompressed() int64 {
return o.nu
}
func (o *gzr) Rate() float64 {
return 1 - float64(o.nc/o.nu)
}

50
archive/interface.go Normal file
View File

@@ -0,0 +1,50 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive
import (
"io"
arcarc "github.com/nabbar/golib/archive/archive"
arctps "github.com/nabbar/golib/archive/archive/types"
arccmp "github.com/nabbar/golib/archive/compress"
)
func ParseCompression(s string) arccmp.Algorithm {
return arccmp.Parse(s)
}
func DetectCompression(r io.Reader) (arccmp.Algorithm, io.ReadCloser, error) {
return arccmp.Detect(r)
}
func ParseArchive(s string) arcarc.Algorithm {
return arcarc.Parse(s)
}
func DetectArchive(r io.ReadCloser) (arcarc.Algorithm, arctps.Reader, io.ReadCloser, error) {
return arcarc.Detect(r)
}

790
archive/lorem_ipsum_test.go Normal file
View File

@@ -0,0 +1,790 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive_test
const loremIpsum = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. In nec consectetur
leo, non faucibus lectus. Aenean sed felis et ex porttitor viverra quis in
felis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per
inceptos himenaeos. Mauris gravida lorem nisl, non pretium metus tristique sed.
In pretium mauris at tellus pharetra accumsan. Vestibulum eget tortor mauris.
Maecenas non pharetra turpis. Nunc lobortis consequat velit id maximus. Nunc eu
metus sem.
Donec egestas sem non nisl iaculis, et auctor augue condimentum. Etiam pulvinar
ligula ante, vitae hendrerit leo gravida sit amet. Integer dictum metus vel leo
consequat, ac ultrices tortor tincidunt. Donec eros arcu, ornare vitae lorem
ac, lacinia convallis urna. Mauris blandit at justo et hendrerit. Nam commodo
augue arcu, mollis lacinia nisi facilisis at. Nulla iaculis quam nisl, et
placerat nisl tincidunt vel. Nam bibendum nulla non luctus efficitur. Nunc quis
tellus sapien. Vivamus laoreet porta fermentum. Ut posuere id risus sed
scelerisque. Praesent leo nibh, hendrerit a risus a, pulvinar sollicitudin
velit. Maecenas sed pharetra massa. Aliquam sed suscipit nisi. Nullam auctor
tortor at faucibus ultrices. Mauris dictum eros ac fermentum facilisis.
Pellentesque varius pretium fringilla. Aenean nisi risus, tincidunt non quam
ac, mattis venenatis mauris. Ut at facilisis est. Phasellus laoreet nibh at
magna accumsan, sit amet ullamcorper magna laoreet. Duis sit amet tortor
gravida sem convallis tristique. Praesent pharetra metus vitae scelerisque
pretium. Phasellus a suscipit ex, finibus pellentesque elit. Proin ac tincidunt
ipsum, vel dignissim quam. In vulputate turpis eget nibh bibendum blandit vitae
sit amet eros. Proin eget tortor est. Curabitur sollicitudin elit mi, in
efficitur velit eleifend ut. Orci varius natoque penatibus et magnis dis
parturient montes, nascetur ridiculus mus. Curabitur tincidunt dui ligula, id
vehicula turpis lacinia sed. Mauris egestas ullamcorper est sit amet iaculis.
Sed nisl purus, commodo at vulputate sed, maximus pretium ipsum.
Donec fermentum nunc ac aliquam congue. Donec eu risus nec lorem pulvinar
malesuada non id enim. Vestibulum sed tellus vitae sapien tincidunt molestie.
Aenean vitae ante enim. Morbi malesuada volutpat laoreet. Suspendisse vehicula,
leo nec mattis maximus, justo turpis viverra lorem, accumsan malesuada massa
libero at tortor. Ut sed cursus quam, in rhoncus tortor. Vestibulum non
scelerisque felis. Quisque tempor dolor id lorem rhoncus fringilla nec vitae
ligula. Donec ac urna dignissim, tincidunt ex vel, vulputate felis.
Suspendisse sagittis, tellus sit amet fringilla sollicitudin, metus ligula
faucibus nisl, tristique faucibus nisl mi vel eros. Vestibulum ante ipsum
primis in faucibus orci luctus et ultrices posuere cubilia curae; Suspendisse
quis metus laoreet, viverra tellus vitae, pretium velit. Suspendisse potenti.
Quisque vulputate eget dolor id fermentum. Proin vel consectetur orci. Class
aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos
himenaeos. Nam at leo in enim sodales egestas tincidunt volutpat ex. Duis vel
ornare sapien. Integer feugiat est semper tortor maximus, non rhoncus sapien
dapibus. Phasellus tristique fermentum lorem, nec mattis ipsum volutpat eu.
Curabitur tempus erat viverra pulvinar scelerisque. Proin blandit turpis sed
enim imperdiet, volutpat pretium diam tincidunt. Mauris feugiat rutrum lacus,
eu ullamcorper ipsum auctor consectetur. Nulla placerat dolor in dolor tempor,
non dignissim velit auctor. Praesent vehicula dui nunc, eu dictum sapien
venenatis id. Sed et finibus turpis. Maecenas sollicitudin eros ac neque ornare
congue. Praesent neque risus, malesuada sed mauris sed, fringilla interdum
justo. Vestibulum porttitor quam sit amet nulla consequat, non posuere risus
porta. Nulla tristique feugiat gravida. Aliquam erat volutpat. Fusce venenatis
gravida ligula in pretium. Sed cursus sem non augue dignissim, at facilisis
nunc dignissim. Curabitur scelerisque enim in ligula posuere, in malesuada elit
commodo.
Vivamus magna dolor, efficitur et condimentum at, maximus eget massa. Nullam
sit amet vestibulum est, et pellentesque sem. Nunc porta sem sit amet suscipit
lobortis. Cras consequat ullamcorper velit, at dapibus erat cursus ut. Sed
eleifend elit et libero eleifend rhoncus vitae quis velit. Nulla cursus diam
vitae suscipit blandit. Vivamus condimentum, felis ut consectetur mattis, risus
eros blandit nisl, sed dapibus orci quam quis sapien. Lorem ipsum dolor sit
amet, consectetur adipiscing elit. Donec quis felis a eros consectetur
sollicitudin. Fusce mattis orci et convallis luctus. Nullam aliquet viverra
dapibus. Curabitur sit amet dolor rutrum, pretium metus a, ultricies ante.
Proin faucibus congue turpis, sit amet dignissim orci vestibulum eu. Cras
consectetur augue a nisi congue, a posuere justo bibendum. Etiam erat erat,
semper id enim sit amet, interdum facilisis magna. Suspendisse a augue quis sem
lobortis pretium eget ut lorem. Vivamus lobortis dui id ex bibendum, at laoreet
mi auctor. Nunc vestibulum tortor interdum nisl rhoncus mattis. Nullam at
volutpat risus. Fusce vel pellentesque odio, id convallis elit. Aliquam sodales
iaculis sapien, et gravida nunc fringilla et. In sed commodo diam, in accumsan
velit. Nulla condimentum massa vitae erat consequat iaculis. Nulla facilisi.
Mauris sapien risus, tempus molestie viverra ut, consequat non tellus. Aliquam
purus dolor, tristique sagittis orci ac, luctus efficitur libero. Integer
maximus magna magna, id condimentum tellus molestie eget. Integer posuere lacus
vel venenatis cursus. Ut auctor enim vitae felis elementum, vel blandit lorem
fermentum. Orci varius natoque penatibus et magnis dis parturient montes,
nascetur ridiculus mus. Pellentesque habitant morbi tristique senectus et netus
et malesuada fames ac turpis egestas. Morbi convallis mattis odio a pulvinar.
Vestibulum quis lacus posuere, faucibus est a, suscipit velit. Phasellus
tincidunt neque enim, at tempor neque auctor sed. In sit amet nisl quis diam
eleifend dapibus a vel nisl. Orci varius natoque penatibus et magnis dis
parturient montes, nascetur ridiculus mus. Sed at dui vehicula dolor tincidunt
auctor vitae sit amet turpis. Maecenas nisi sapien, porta sed volutpat non,
imperdiet eu magna.
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere
cubilia curae; Curabitur vitae tempus nibh, in vulputate sem. Quisque a
tincidunt mi, at porttitor dolor. Maecenas rhoncus sapien lectus, ut porta eros
porta at. Mauris finibus purus sit amet leo mattis accumsan. Vivamus molestie
dapibus iaculis. Donec sagittis mattis nunc nec dapibus. Nulla cursus non mi et
malesuada. Fusce tempor nulla eget sagittis ultrices. Proin vel ipsum ac erat
tempor semper consectetur ut sapien. Ut aliquet viverra magna, id mollis ligula
interdum et. Morbi porta dapibus metus ac varius.
Nullam blandit quis nulla vitae volutpat. Etiam leo ante, mattis non congue
sed, vulputate a augue. Cras eget tempor enim, semper mattis eros. Sed sed
varius mi. Donec porta neque purus, vitae accumsan ante consequat vitae.
Suspendisse luctus, sem quis convallis luctus, dolor sapien ultricies tellus,
sagittis egestas ipsum odio eget augue. Nulla ut est a dui pulvinar ultrices ac
nec augue. Vestibulum ultrices molestie libero, et aliquam erat sodales nec.
Maecenas elementum finibus dolor, eu porta eros vulputate quis.
In sit amet pharetra lorem. Nulla dictum lectus ac odio mollis dapibus. Sed
euismod bibendum orci vel posuere. Curabitur malesuada lobortis nunc id
condimentum. Maecenas iaculis mi sit amet nunc cursus vehicula. Nam tristique
vel purus vehicula auctor. Cras at ipsum nisi.
Aenean sit amet sem euismod, molestie purus condimentum, rutrum felis. Quisque
nec porttitor nisl, id molestie massa. Donec venenatis blandit hendrerit. Nulla
facilisi. Maecenas ultricies urna et ipsum sagittis, eget placerat est tempor.
Sed commodo nisl eget lorem vulputate, quis malesuada dui facilisis. Integer ut
placerat ligula. Pellentesque blandit mi et vestibulum blandit.
Fusce non erat turpis. Phasellus lobortis molestie nisi, nec luctus dolor
posuere fringilla. Proin viverra ipsum a iaculis convallis. Morbi ac elit erat.
Ut a lacus felis. Fusce volutpat, orci sed dapibus varius, lectus enim lobortis
elit, ac elementum ipsum purus ac ligula. Duis posuere arcu et urna commodo
posuere. Proin placerat nibh non pharetra tincidunt. Nam convallis ex enim, vel
lacinia eros placerat at. Quisque laoreet risus non risus pellentesque, quis
ultrices lectus posuere. Aliquam ut risus at ex dignissim porttitor. Vivamus
non magna quis tellus venenatis viverra id nec orci.
Ut augue libero, luctus eget consequat facilisis, venenatis id nunc. Nam
molestie ante a faucibus egestas. Integer fringilla laoreet fringilla. Quisque
euismod scelerisque libero ac finibus. Sed porttitor sollicitudin mi at tempor.
Nam at convallis tellus. Curabitur ornare elit a quam consectetur, sit amet
pharetra elit sodales. In convallis tincidunt congue. Morbi vehicula aliquam
sem. Cras urna sem, mollis vel viverra id, vestibulum sit amet est. Donec
condimentum lacus ut mauris viverra auctor. Aenean turpis dui, condimentum sed
varius ac, finibus vitae ex. Vivamus vehicula vehicula nibh consequat faucibus.
Vivamus at lectus orci. Duis et sapien at metus laoreet rutrum sit amet
fringilla nisi.
Duis feugiat lorem vel suscipit consectetur. Vivamus molestie diam urna, at
egestas ante volutpat non. Fusce vitae justo a leo iaculis suscipit vitae id
urna. Aliquam sit amet diam eu neque sollicitudin semper a elementum nisi.
Vivamus aliquam diam eget nunc scelerisque placerat. Vestibulum pharetra at
felis ut consectetur. Integer auctor mauris libero, id auctor nulla cursus
consequat. Vivamus porta erat nec metus placerat, sit amet ultrices orci
commodo. Integer tempus blandit iaculis. Donec sagittis, mi ultricies eleifend
egestas, nisi odio posuere lacus, eu ultrices erat turpis vel est. Nullam
scelerisque lectus et iaculis sodales. In quis diam fringilla sem eleifend
hendrerit quis ut augue.
Cras non pulvinar urna. In id urna justo. Duis pellentesque enim in nunc
hendrerit, vel suscipit libero venenatis. Phasellus egestas convallis neque,
sit amet pretium nunc lacinia eu. Maecenas placerat justo vitae sem ultricies,
ut luctus magna blandit. Donec in ligula vel arcu aliquet tristique. Nam
imperdiet augue eget aliquam commodo. Pellentesque accumsan auctor sapien, in
feugiat turpis. Pellentesque at purus at risus venenatis tempor. Ut vestibulum
enim mi, eget blandit odio faucibus eu. Phasellus egestas, velit non egestas
bibendum, mi leo ultrices massa, sed maximus erat turpis eget est. Suspendisse
lacus lacus, finibus sed dolor ac, sagittis viverra metus. Cras velit nisi,
malesuada eget metus nec, aliquet auctor mauris. Phasellus lobortis turpis
neque, eu dictum nulla posuere vel.
Sed fermentum imperdiet tellus ac vulputate. Donec vel purus ullamcorper mi
varius pulvinar. Proin feugiat vehicula massa in dignissim. Quisque posuere
porttitor placerat. Aliquam erat volutpat. Vivamus sollicitudin vulputate
metus, nec eleifend nulla. Morbi vitae cursus arcu. Aenean vel rutrum purus.
Pellentesque sollicitudin libero a neque mattis, vitae convallis leo dictum.
Aenean ex turpis, placerat at ultrices sit amet, consectetur id dui.
Praesent id tortor sagittis, sagittis nisi quis, scelerisque magna. Nulla non
tortor sed dui rutrum pharetra. Aenean aliquam scelerisque metus in
sollicitudin. Proin ac tincidunt nisl. Etiam tempus sollicitudin eleifend.
Pellentesque vehicula nibh at sem bibendum, facilisis pellentesque diam
volutpat. Integer ullamcorper mattis ligula eu semper. Ut dignissim laoreet
magna, vitae ornare dui pretium vitae. Nullam consectetur, mi sit amet
facilisis consequat, arcu risus tristique metus, sed hendrerit ligula erat ut
mauris. Maecenas mi ex, porttitor vitae tellus at, commodo pulvinar sem. Donec
ac imperdiet sapien, quis feugiat elit.
Vivamus porta, massa a vulputate viverra, odio quam laoreet nunc, nec vehicula
ligula sapien vel ante. Nullam ut ante arcu. In non molestie turpis. Etiam nec
efficitur tellus. Proin placerat diam vitae pellentesque congue. Fusce aliquam
augue eu lectus pellentesque, et condimentum tortor volutpat. Donec semper
sodales felis ut elementum. Curabitur suscipit lacus sit amet risus posuere, et
porta quam pretium. Praesent aliquam luctus vulputate. In tincidunt, elit sed
congue hendrerit, augue mi laoreet lacus, eu imperdiet est quam eget elit.
Aliquam porta sit amet tortor in semper. Nullam nec nisl eu lectus sagittis
lacinia vel a nisi.
Ut suscipit, ligula nec blandit varius, neque mi tempus odio, ut bibendum massa
enim nec libero. Nulla fermentum tristique nisi at tempor. Sed nec pharetra
felis, et tempor quam. Mauris lobortis arcu sed posuere pretium. Aenean eu
lacinia ex, aliquet dignissim dui. Integer lobortis et risus ut porta. Donec
dapibus viverra quam, et vulputate metus consequat ut. Suspendisse accumsan et
orci vitae ornare. Curabitur volutpat orci quis lacus pharetra, ut condimentum
eros pellentesque. Vivamus scelerisque mauris eget luctus vestibulum. Fusce
placerat, tellus in tincidunt tempor, est dolor ultrices urna, id euismod neque
sapien nec metus. Praesent lacinia eget sapien sed accumsan. Maecenas tempor
nunc ac sapien aliquet, eu sagittis metus ornare. Praesent ut urna tellus.
Donec elementum sem diam. Sed pretium consectetur massa eget gravida.
Donec ut est ut velit tincidunt pretium eget ut massa. Ut tempor eget nunc eget
egestas. In ac risus elementum, ornare metus vel, laoreet nisl. Ut auctor,
risus facilisis pellentesque ornare, lacus ex scelerisque ex, a feugiat arcu mi
vitae tortor. Ut venenatis finibus lorem sit amet pretium. Aliquam vel
scelerisque sem. Vestibulum iaculis sed erat vel cursus. In volutpat sodales
dignissim.
Donec nibh magna, scelerisque vel ante non, semper aliquam nisi. Ut ornare
pellentesque sapien aliquam tristique. Donec convallis tempus magna, eu blandit
felis finibus quis. Donec laoreet odio ut nisi aliquam venenatis. Nam
consectetur a lorem bibendum accumsan. Vivamus leo neque, sollicitudin vel
sodales quis, ultricies nec felis. Aenean luctus mauris nec imperdiet
dignissim.
Mauris posuere nibh dui, ut luctus felis consequat et. Aenean odio nibh, tempor
in sapien et, suscipit pulvinar ex. Vivamus molestie augue felis, non venenatis
ante congue eu. Aliquam commodo feugiat augue at venenatis. Nulla sapien odio,
posuere vel aliquet et, volutpat sed nunc. Aliquam eu pharetra sem. Nulla
facilisi. Duis interdum leo lectus, quis finibus nisi euismod quis.
Fusce et neque vel tellus porta vulputate. Duis non ex turpis. Class aptent
taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
Sed eget risus at dolor consectetur suscipit sed sed sem. Sed pharetra lectus
ante, sit amet malesuada felis euismod vitae. Vivamus condimentum dapibus erat,
at varius urna condimentum sit amet. Morbi condimentum sapien est. Nulla
malesuada erat nec semper scelerisque. Ut porta, risus eu elementum viverra,
dui nulla vehicula purus, eu placerat magna sapien ut quam. Aliquam suscipit
massa eget lacus ornare feugiat. Praesent sagittis aliquam malesuada. Aliquam
quam turpis, efficitur non elit vitae, laoreet tristique erat. Proin at magna
sollicitudin nulla aliquam ultrices ac id dui. Vestibulum facilisis sed leo
vitae tempor. Donec id lorem pharetra, tempor mauris ut, convallis magna. Etiam
feugiat est eget varius fringilla.
Cras tristique erat at justo maximus tempor. Pellentesque fringilla felis at
magna luctus, vel posuere purus venenatis. Ut tristique ligula vitae metus
euismod euismod. Ut et tortor eu dui malesuada dictum. Cras laoreet rutrum ante
quis tincidunt. Aenean diam magna, tristique a erat a, efficitur posuere nisi.
Vestibulum dapibus, tortor vitae malesuada vulputate, eros augue ullamcorper
lacus, a facilisis turpis turpis vel dui. Class aptent taciti sociosqu ad
litora torquent per conubia nostra, per inceptos himenaeos. Donec ultrices quam
turpis, id tempor nulla accumsan in. Suspendisse scelerisque volutpat lectus, a
placerat lorem sodales in. Suspendisse porttitor felis augue, a suscipit justo
sollicitudin eu. Interdum et malesuada fames ac ante ipsum primis in faucibus.
Mauris scelerisque mi in risus mattis, posuere facilisis lacus rutrum. Integer
a nisi sed ante molestie placerat. Ut eros dui, luctus vitae libero ultricies,
sodales eleifend urna. Suspendisse potenti.
Phasellus tempus convallis posuere. In hac habitasse platea dictumst. Integer
sit amet mi posuere, porta eros a, semper odio. Nunc mi lectus, pulvinar mollis
enim non, posuere vestibulum enim. Donec luctus arcu sed felis imperdiet, non
condimentum tellus viverra. Ut vestibulum ligula imperdiet, blandit lacus id,
molestie sapien. Integer ultricies tortor a lacus egestas, a fermentum arcu
aliquam. Vivamus quis diam eleifend, euismod nulla quis, consequat elit. Mauris
sodales justo quis risus vehicula accumsan.
Aliquam fermentum dolor in sem bibendum bibendum. Sed sit amet augue malesuada,
imperdiet augue eu, finibus risus. In vitae urna eros. Maecenas ex urna,
lacinia sagittis tellus in, aliquam feugiat risus. Ut faucibus, nulla a
pellentesque rhoncus, libero massa sodales mauris, vel dignissim ex est eu
justo. Mauris libero nisl, varius vitae molestie sit amet, porta vitae justo.
Pellentesque vel volutpat ante. Morbi ac elementum diam. Vestibulum ultricies
fringilla laoreet. Nulla efficitur eros et quam rhoncus, in gravida lorem
auctor. Integer quis mi elementum, dapibus nibh sed, tempus risus.
Fusce malesuada neque non nibh laoreet, id posuere quam mattis. Nulla volutpat
mauris tortor, ac pellentesque ligula fermentum ut. Curabitur eu sem vel risus
consequat dictum quis a elit. Praesent ultricies feugiat pretium. Nullam in
congue metus, sed interdum est. Integer malesuada magna vitae lacus iaculis
tincidunt. Ut volutpat euismod mi, eget placerat sapien sagittis sed. In
vehicula neque id leo mollis, dapibus consequat justo venenatis. Donec aliquam
augue sed finibus efficitur. Etiam molestie ligula bibendum felis volutpat
malesuada. Sed molestie tempor nulla, at sollicitudin leo mattis vel. Sed a
nunc porta, auctor est malesuada, suscipit velit. Phasellus euismod urna
turpis, ut imperdiet ligula malesuada vel. Nullam iaculis vitae quam quis
dignissim. Nam sit amet odio varius, pellentesque ex vel, finibus urna.
In lacinia semper fringilla. Proin ut turpis quis nunc ultricies suscipit in
eget odio. Curabitur sodales et tortor et finibus. Suspendisse potenti.
Phasellus augue arcu, pellentesque vel hendrerit quis, interdum at neque. Cras
ornare, ante et ullamcorper commodo, dolor enim consectetur dolor, vel
hendrerit orci arcu at nisl. Vestibulum tempus urna et purus aliquet
scelerisque. Donec et mollis enim. Donec ipsum augue, condimentum et rhoncus
sed, convallis sed velit. Maecenas faucibus, urna nec dictum tristique, augue
diam pharetra nisi, vel imperdiet mi turpis vitae mauris. Fusce consequat massa
eu libero efficitur, id imperdiet enim congue. Nullam volutpat scelerisque
nulla, in tincidunt tellus finibus in.
Nam neque magna, venenatis ac dignissim vel, pretium in purus. Curabitur
vestibulum feugiat dolor, vitae euismod justo tempor et. Nulla id lectus eget
quam congue scelerisque. Ut eu aliquam lorem. Morbi non fringilla massa, vel
faucibus quam. Sed posuere vehicula massa, ac laoreet magna gravida sed.
Curabitur vitae volutpat metus, eu porta odio. Morbi id arcu ac est viverra
blandit eget id dolor. Donec egestas ante vitae mattis placerat. Nulla iaculis
erat in pharetra facilisis. Praesent efficitur eros ut lacus ornare aliquam a
eu erat. Aenean placerat blandit mauris non congue.
Nulla fringilla id felis eu blandit. Integer suscipit sed nibh ut fringilla.
Quisque lacinia porttitor erat vel fringilla. Phasellus varius, dui eu interdum
mattis, lectus ipsum varius arcu, nec dignissim mi nisl ac mauris. Ut lacinia
magna libero, fermentum ultrices nibh pharetra sit amet. Morbi tempus sed purus
fringilla ultrices. In malesuada placerat dolor, dapibus viverra erat convallis
placerat. Ut ultrices porta urna, a scelerisque dolor molestie in. Vivamus at
odio quam. Integer suscipit id sem eu sodales. Nullam quis rhoncus nisl.
Maecenas eleifend venenatis purus facilisis facilisis. In molestie orci eu
volutpat pretium. Curabitur quis est erat. Lorem ipsum dolor sit amet,
consectetur adipiscing elit. Donec at dapibus nunc. Ut semper enim sagittis
tellus imperdiet luctus. Morbi feugiat pellentesque scelerisque. Sed ac ex
blandit, iaculis diam a, placerat ipsum. Quisque vitae lacinia ante, molestie
consequat metus. Ut vitae fermentum erat. Proin vitae vehicula tellus. Maecenas
aliquet malesuada lobortis. Maecenas eu mattis est. Etiam in quam eu lectus
consequat varius.
Vestibulum nec leo nibh. Nulla cursus mauris eget rutrum sagittis. Morbi vel
varius ligula. Donec ut vestibulum dolor. Integer maximus hendrerit erat, in
varius dolor molestie non. Fusce non pellentesque ex, quis mollis metus. Nunc
tempor, sapien ac tempus porttitor, erat neque fringilla nibh, vitae egestas
massa arcu sed arcu. Morbi sollicitudin nisi nulla, eu pharetra massa finibus
eget. Aliquam erat volutpat. Vivamus rutrum, nisl eu finibus vestibulum, sem
tellus viverra ipsum, ullamcorper fermentum ligula lectus et mi. Aliquam ligula
tortor, convallis sit amet ex ut, tempus tempor dui.
Nulla tempus, quam ac faucibus gravida, magna est interdum augue, ac fermentum
odio arcu ut arcu. Quisque vel libero a sem posuere convallis. Quisque iaculis
convallis imperdiet. Nulla a nisi quis ante tempus ultricies. Donec eu ante
gravida, finibus lectus ac, viverra lacus. Nullam posuere blandit leo, eget
euismod tellus egestas eu. Nunc quis mauris tortor. Donec molestie euismod leo,
ut facilisis risus laoreet quis. Mauris metus nisi, semper non diam id,
ullamcorper egestas libero. Aenean blandit mauris quam. Fusce vitae varius leo.
Donec efficitur elit vel blandit dictum. Aenean molestie commodo enim. Fusce
tempor luctus luctus.
Donec sit amet nibh sit amet arcu dignissim vulputate. Pellentesque ornare enim
arcu, ac ullamcorper est hendrerit in. Duis sit amet ipsum bibendum, congue
mauris in, tincidunt metus. Integer in lorem at nisi consectetur convallis id
quis diam. Sed orci purus, euismod ut sem ut, sollicitudin maximus lacus. Sed
pellentesque convallis arcu quis tincidunt. Donec mattis purus sodales risus
mattis aliquet.
Nulla facilisi. Donec neque lectus, rhoncus at sagittis ut, bibendum non augue.
Praesent posuere elit massa, eu porta justo aliquam non. Morbi sagittis lorem
non lacus congue lobortis. Aliquam sodales augue nec leo ullamcorper feugiat.
Quisque neque risus, porta viverra interdum id, tincidunt ut justo. Morbi
dictum dui id magna faucibus, vel ultricies magna lobortis. Quisque lacinia leo
quam, nec convallis ex egestas et. Integer vitae velit ex. Etiam ut vestibulum
ipsum, in placerat mi. Vestibulum id blandit risus. Pellentesque eu mi
pharetra, consectetur arcu imperdiet, viverra justo. Curabitur tempor, magna
vitae laoreet maximus, metus eros maximus libero, quis scelerisque dolor tortor
ac mauris. Maecenas placerat nisl tellus. Donec accumsan, eros eu efficitur
convallis, turpis augue lacinia velit, in blandit libero diam a nulla. Quisque
vitae felis eros.
Donec gravida pretium vulputate. Duis vestibulum pharetra ipsum non volutpat.
Suspendisse volutpat, velit ut sodales porta, enim nulla fringilla magna, ac
faucibus augue risus convallis ante. Duis pretium a eros eget tincidunt. In
ligula risus, suscipit eu risus eget, fermentum pulvinar lectus. In aliquet
quam nec aliquet accumsan. Mauris imperdiet, elit et tincidunt bibendum, ligula
erat sollicitudin odio, vel semper augue diam ut magna. Donec vehicula, orci ac
scelerisque eleifend, orci velit fermentum urna, id sagittis felis velit ac
ipsum. Maecenas felis lacus, faucibus id tempor non, ultrices placerat elit.
Nullam ante lorem, volutpat nec sagittis quis, luctus eget purus. Ut nec orci
sem.
Curabitur condimentum condimentum metus eu elementum. Nunc pharetra nisi
gravida ex consequat posuere. Nullam fermentum mauris in ex molestie fermentum.
Sed neque neque, sodales ut turpis eu, porttitor tincidunt urna. Curabitur
vehicula nibh ac mollis semper. Phasellus maximus nulla ut magna molestie
rutrum eget et orci. Phasellus aliquet commodo nibh, ac pretium velit
pellentesque et. Vestibulum ante ipsum primis in faucibus orci luctus et
ultrices posuere cubilia curae; Sed eu mi fermentum, gravida tellus quis,
hendrerit quam. Integer vel justo fermentum, dictum nibh eget, tincidunt dolor.
Nunc nec tortor vel neque lacinia vulputate. In vitae ullamcorper neque, ut
sollicitudin arcu. Nam sit amet egestas ligula. Pellentesque habitant morbi
tristique senectus et netus et malesuada fames ac turpis egestas. Praesent
scelerisque turpis at dolor placerat fringilla.
Praesent dignissim ligula et dignissim elementum. Fusce vel auctor enim. Etiam
non lectus non erat ultricies porta sed rhoncus est. Morbi at posuere ligula.
Morbi egestas ante quis porta pulvinar. Sed sit amet est felis. Proin ante
eros, aliquam eu erat a, blandit tincidunt ante. Suspendisse sed lectus
efficitur, molestie odio sit amet, convallis eros. Orci varius natoque
penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin id
ligula accumsan, consectetur tortor ac, facilisis justo. Nulla facilisi.
Quisque sed quam fermentum, blandit magna at, consequat sapien. Interdum et
malesuada fames ac ante ipsum primis in faucibus.
Sed vehicula vitae dui sed porttitor. Sed condimentum tortor justo, eget
facilisis dolor euismod a. Proin commodo justo sed ultrices malesuada. Nulla
eget risus aliquet, dignissim tortor vitae, rutrum erat. Quisque eleifend et
metus non sagittis. Maecenas magna sapien, congue at tincidunt nec, eleifend
sit amet sapien. Morbi vitae elementum mi. Duis at mauris nisi. Cras urna arcu,
auctor in fringilla lobortis, porta vel elit. Donec sed velit eu felis varius
rhoncus. Orci varius natoque penatibus et magnis dis parturient montes,
nascetur ridiculus mus. Ut eget nisi nisl. Nullam vitae diam in diam ultrices
tincidunt. Sed lacinia elit eu augue lacinia dictum. Aenean eu nisi volutpat,
interdum velit vitae, ullamcorper urna.
Suspendisse ac est pulvinar, viverra nulla quis, varius sem. Praesent sit amet
lacinia eros. Etiam consequat, quam a rutrum accumsan, lacus orci dictum justo,
ut condimentum sem mi lobortis sem. Donec tempor lacus id laoreet ultricies.
Duis quis lobortis lectus. Quisque rhoncus, ante ut aliquet sagittis, ligula
metus molestie tellus, eget pretium risus risus ac augue. Ut nulla urna,
gravida vel consequat at, condimentum ac sapien. Proin hendrerit purus mi, ut
tristique diam suscipit a. Quisque nunc nisi, dignissim vel volutpat id,
accumsan eu tortor. Sed nisl enim, faucibus ut dictum vel, eleifend ac massa.
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere
cubilia curae; Quisque venenatis quis nisl nec viverra. Vivamus a neque sit
amet nisi viverra imperdiet. Curabitur congue sollicitudin erat, quis interdum
urna molestie eget. Sed sit amet erat at lorem mollis malesuada. Pellentesque
imperdiet pulvinar velit, in aliquet ipsum suscipit sit amet. Phasellus mollis
ligula dolor, hendrerit gravida massa convallis vel. Nulla enim eros, dignissim
id euismod sed, fringilla id justo. Ut a tristique lectus, quis viverra turpis.
Proin at urna vel purus volutpat tristique a eu nisl. Etiam vitae libero
fringilla, tristique lorem ac, luctus odio. Nam pharetra lorem vel porttitor
porta. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere
cubilia curae; Sed blandit erat tincidunt, facilisis diam in, bibendum libero.
Sed semper dui dapibus commodo bibendum.
Maecenas eleifend sapien magna, eget lobortis elit aliquet eu. Etiam laoreet
sapien id leo tempus convallis. Donec molestie, elit non tincidunt eleifend,
sem metus tincidunt erat, sed bibendum ipsum enim placerat arcu. Vestibulum
fringilla dictum rhoncus. Mauris feugiat at lectus a lacinia. Donec et
tincidunt lorem, in condimentum velit. Vivamus porttitor mi in nunc aliquam,
vitae consequat mi pharetra. Vivamus laoreet eget felis sit amet vestibulum.
Donec rutrum volutpat mi, quis commodo ante tempus eu. Praesent sit amet nibh a
ligula hendrerit vehicula. Sed pellentesque a nulla ut fringilla. Phasellus id
tortor metus. Vestibulum sit amet blandit odio, non gravida dui. Donec auctor
feugiat viverra. Mauris vel blandit lorem.
Suspendisse viverra gravida nisl, quis tempor nisl consequat eu. Aliquam
finibus eros lobortis lectus varius sagittis quis non velit. Integer tincidunt
elit felis, a auctor arcu cursus placerat. In a nunc elit. Suspendisse
tristique turpis est, quis iaculis nisl interdum vel. Quisque elit nulla,
lacinia nec est vel, porttitor ullamcorper lorem. Suspendisse at sem ornare,
ultrices mauris vitae, iaculis quam. Suspendisse vitae tellus dictum,
consectetur dui vitae, porttitor ipsum. Pellentesque quis tellus dolor.
Pellentesque in tincidunt odio. Proin a molestie ipsum, eget viverra purus.
Nulla facilisi. Pellentesque lacinia sollicitudin lacus eget imperdiet. In
volutpat tortor mi. Aliquam tincidunt placerat risus, sit amet sagittis nisi
mattis dictum.
Mauris sed est laoreet augue finibus feugiat. Aliquam vehicula molestie enim,
in blandit enim scelerisque sit amet. Vestibulum consequat massa ex, nec
lacinia nisl ultricies vel. Aliquam erat volutpat. Proin rhoncus euismod
feugiat. Sed tincidunt augue odio, ac auctor justo consequat ut. Nulla
fermentum metus sollicitudin leo rutrum, non pulvinar turpis elementum. Quisque
lorem turpis, accumsan nec dapibus ut, aliquam ut lorem.
Morbi a imperdiet quam. Aliquam euismod tincidunt lorem ac gravida. Lorem ipsum
dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet,
consectetur adipiscing elit. Aliquam eu sagittis orci. Nulla ac varius eros, id
elementum ante. Etiam aliquam sem sed metus eleifend maximus. Phasellus eu
gravida risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices
posuere cubilia curae; Nullam velit mauris, tempor ut malesuada sollicitudin,
gravida eget magna. Phasellus aliquam mollis risus ac rutrum. Donec vulputate
ligula sed purus vestibulum mattis quis at orci. Morbi vel consectetur magna.
Pellentesque eget ligula sed magna vehicula blandit sed at ex. Nullam aliquet
vel turpis vel varius. Vestibulum sit amet varius neque. Phasellus sem enim,
varius ut consectetur sed, pulvinar et velit. Cras a porta mi, ut feugiat elit.
Sed commodo libero eu enim consectetur, nec maximus quam consequat. Vestibulum
in ante facilisis, iaculis eros sit amet, finibus magna. Sed ullamcorper
eleifend dui eu congue. Phasellus vel tellus a lectus aliquet dapibus. Ut
congue, magna vel fringilla mattis, leo dolor convallis metus, a ullamcorper
ipsum neque sed dolor. Ut quis egestas dui. Vestibulum nec egestas dolor.
Curabitur dictum orci augue. Sed tortor risus, porta vitae sodales ac, aliquam
eget ex. Proin accumsan non lacus eget placerat. Orci varius natoque penatibus
et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque malesuada
ornare sem id tincidunt. Vivamus ac vestibulum dui. Phasellus vel laoreet dui.
Aliquam vulputate est vel neque egestas, at fermentum lectus consectetur.
Integer mollis turpis nunc, condimentum placerat justo sodales sed.
Nunc ullamcorper malesuada elit sed convallis. Nulla malesuada sapien facilisis
orci vulputate, feugiat gravida felis commodo. Suspendisse lacinia tempus
elementum. Suspendisse aliquet id ex vel commodo. Praesent iaculis neque congue
ullamcorper consequat. Etiam ut tellus vel libero ullamcorper commodo. Ut
dignissim enim ac laoreet gravida. Duis vitae dictum ex. Morbi mattis tellus et
laoreet congue.
Nam elementum at magna tincidunt ornare. Quisque rutrum interdum eros eget
feugiat. Donec lacinia ornare ultricies. Etiam sollicitudin blandit sapien
placerat laoreet. Morbi sed urna in libero egestas faucibus. In id sem eu sem
tempus euismod. Sed orci nulla, finibus vel lorem vel, luctus feugiat nibh.
Integer at pellentesque est. Aliquam dapibus, enim in convallis tempor, orci
ante posuere odio, id rutrum mauris lorem id lectus. Sed suscipit, neque quis
aliquam gravida, mi velit porttitor tortor, id luctus velit mauris ut turpis.
Nullam quis egestas lorem. Nam et varius mauris, dictum varius elit. Nullam nec
orci non lorem posuere sodales nec eu odio. Phasellus nec consectetur lectus.
Nulla tempor imperdiet neque, quis auctor felis malesuada euismod. Nullam
imperdiet ante non dignissim sodales. Quisque sed egestas ante. Phasellus vitae
lorem nisl. Quisque malesuada elementum sem. Quisque ligula velit, aliquam sed
hendrerit a, hendrerit in lectus. Nulla non volutpat est, vitae condimentum
risus. Integer accumsan rutrum lacus, ac tempus mauris tincidunt vitae.
Suspendisse gravida in ex vel rutrum. Curabitur nec sagittis nulla. Quisque
tincidunt malesuada augue vitae condimentum. Suspendisse eu leo ac dolor
posuere ultricies quis ac metus. Sed porta, dui quis fringilla facilisis, ante
velit posuere turpis, nec gravida purus lectus eget odio. Proin id bibendum
justo. Proin sapien est, varius tempus libero a, consequat iaculis ipsum.
Curabitur ut nisi quis lectus vehicula pretium ut at tellus. Quisque consequat
eget enim sed eleifend. In at interdum metus. Vestibulum ante ipsum primis in
faucibus orci luctus et ultrices posuere cubilia curae; Integer porta nibh a
dictum tempus. Mauris interdum libero quam, id venenatis nibh tempus in. Nulla
ornare blandit dui, nec facilisis eros tempor quis. Curabitur euismod lobortis
condimentum. Nam convallis odio vitae dui porttitor, in hendrerit eros
bibendum. Proin convallis, sapien a convallis vulputate, felis nulla iaculis
purus, in mollis neque nulla non odio. Nullam non tellus varius, blandit ante
sed, venenatis enim. Vivamus aliquam risus quis risus aliquet, id vestibulum
felis eleifend. Praesent a felis a odio bibendum tincidunt. Sed odio nisl,
laoreet nec nunc in, suscipit accumsan tellus. Nulla vitae lorem eu orci
bibendum mattis id quis nisi. Mauris a purus libero.
Curabitur non sapien efficitur, egestas orci at, imperdiet neque. Vestibulum
condimentum mauris tellus, eget mollis sapien vulputate quis. Ut ac lectus
enim. Cras ut convallis nisl. Proin sed porta elit. Nam posuere tellus nec
massa scelerisque, at facilisis nibh vulputate. Mauris dictum neque iaculis
quam elementum, posuere lacinia libero egestas. Nulla sapien risus, vestibulum
fermentum tincidunt non, placerat in orci. Sed eu ipsum nisi. Aliquam eget
felis felis. Donec non nisi vel nunc luctus vehicula quis at tellus. Mauris
tincidunt nulla sit amet lorem sodales imperdiet. Suspendisse ac enim a enim
tincidunt fermentum quis ornare lacus. Phasellus ac nunc vitae libero fermentum
ultricies. Praesent suscipit ligula metus, et lobortis mauris varius vel.
Maecenas lectus tortor, dignissim malesuada blandit in, iaculis ac tellus.
Nulla vel sollicitudin lectus, eget pellentesque massa. Nam ac bibendum ligula.
Phasellus molestie nec dui vel accumsan. Nam sit amet tempor diam. In vulputate
aliquam nisl et aliquam. Donec feugiat fringilla turpis sit amet gravida.
Pellentesque consectetur erat quis neque gravida gravida. Fusce blandit vitae
tellus quis malesuada. Mauris volutpat interdum ligula nec dapibus. Class
aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos
himenaeos.
Vivamus in odio purus. Nam blandit dolor ut dolor mollis dapibus. Quisque
posuere ullamcorper tortor ac pharetra. Sed eget sagittis purus. Donec ut augue
ac nisi accumsan tempus. Aliquam at neque nec lacus aliquam tempor. Mauris et
purus quis odio bibendum porta sed eget arcu. Curabitur mauris nisl, semper id
facilisis et, convallis nec diam. Orci varius natoque penatibus et magnis dis
parturient montes, nascetur ridiculus mus. Aliquam erat volutpat. Vestibulum
accumsan lobortis orci, accumsan bibendum dui facilisis nec. Donec at nisi non
nulla facilisis dictum. Phasellus sit amet nisi ornare, posuere nisi ac,
egestas nulla. Nam gravida vel quam quis pharetra.
Nulla pharetra dignissim mauris, ut finibus nunc volutpat eget. Donec mattis
lobortis porta. Sed consequat magna turpis, dapibus pretium odio euismod nec.
Nunc ultricies volutpat imperdiet. In posuere nisi et purus ullamcorper
sagittis. Sed ligula turpis, lobortis nec vulputate id, luctus in lorem.
Curabitur consectetur vel mauris vitae malesuada. Phasellus vestibulum dapibus
urna, in rutrum neque porttitor ut. Pellentesque sodales pellentesque aliquet.
Maecenas dapibus dignissim metus, quis dignissim magna.
Quisque id ante commodo, porttitor nibh venenatis, pharetra purus. Duis viverra
at libero non gravida. Aenean dictum sit amet sem at malesuada. Integer vitae
quam a ante dictum condimentum nec nec felis. Donec et suscipit massa, et
vulputate ex. Integer eget libero nec felis facilisis cursus nec sit amet nisi.
Nullam facilisis, quam sit amet dictum luctus, erat mauris accumsan risus, ac
commodo enim eros ut augue. Curabitur at dolor tortor. Suspendisse tincidunt
malesuada volutpat.
Pellentesque non purus in turpis accumsan euismod sit amet in lacus. Vestibulum
ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;
Morbi consectetur volutpat ultricies. Donec lacus mauris, euismod sed nunc in,
ultricies vehicula dui. Praesent a enim quis tortor fermentum mollis at non
lorem. Suspendisse potenti. Sed non commodo enim. Vivamus a odio urna. Donec ac
orci est. Sed tincidunt metus nulla, at placerat lectus pharetra et.
Suspendisse ac ex nibh. Praesent id bibendum eros, sit amet semper tortor.
Aliquam a dolor at eros gravida consequat quis tincidunt libero. Morbi a nisl
id augue viverra consectetur ac scelerisque dui. Morbi nec tellus quam. In
cursus augue nec mattis pellentesque. Aliquam erat volutpat. Phasellus vehicula
lorem consectetur leo molestie, fringilla ornare tortor interdum. Duis quis
sodales magna, at mollis risus. Proin venenatis purus sed urna pretium, vel
eleifend arcu condimentum. Maecenas maximus placerat purus, in molestie turpis
consectetur in. Aliquam a arcu gravida, condimentum elit vel, vulputate dolor.
Duis vel justo ac velit bibendum ullamcorper. Suspendisse vestibulum mollis
libero eu fringilla. Vestibulum vel velit pretium, congue nisi id, viverra
sapien.
Cras consectetur tellus quis tellus posuere sodales. Pellentesque habitant
morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aenean
at congue mauris. Duis eget nulla vitae neque tempor fermentum a vitae enim.
Aliquam volutpat massa et lorem porttitor, eget sagittis libero tristique.
Fusce vel leo id elit fringilla mattis. Curabitur vitae ligula urna. Mauris
varius rutrum varius. Phasellus facilisis ipsum sed dui condimentum, at varius
justo elementum. Maecenas vehicula justo a cursus mattis. Nullam id enim nisl.
Pellentesque dapibus erat nunc, vitae tempor sapien blandit et. Donec pretium
dui in risus vehicula dapibus. Cras feugiat facilisis placerat. Curabitur quis
odio sed mi maximus auctor ac ac elit.
Aenean sit amet neque ligula. Aliquam et risus ac arcu egestas dictum. Quisque
varius efficitur enim, aliquet vehicula sem euismod ac. Vestibulum ac efficitur
erat. Ut elementum, purus non tristique molestie, eros dui cursus est, eu
ultricies tortor lorem id quam. Etiam volutpat euismod velit, ut posuere dolor
tempor quis. Vivamus venenatis neque sed ante sodales pharetra. Nam nec lectus
urna. Etiam ultricies venenatis metus vitae hendrerit. Integer massa nunc,
auctor ut luctus ac, dignissim vel mauris. Nullam sollicitudin laoreet justo
vel pretium. Nam blandit mauris a mauris porttitor, sed cursus risus posuere.
Nam consequat venenatis consectetur. Duis non porta tortor. Nullam bibendum
lorem in fringilla eleifend.
Ut sed urna ipsum. Pellentesque vestibulum, augue quis vulputate rutrum, nulla
ipsum imperdiet diam, viverra volutpat ligula leo eu mi. Phasellus accumsan
risus in augue tincidunt laoreet. Proin bibendum nunc ut vulputate porttitor.
Vestibulum laoreet malesuada pharetra. Sed in gravida augue. Nunc hendrerit id
urna in iaculis. Morbi fringilla hendrerit orci, vitae gravida felis ultrices
quis. Integer id commodo eros. Maecenas eu augue nec purus tristique dapibus.
Aenean metus diam, vehicula id mauris sit amet, iaculis sollicitudin dui.
Suspendisse neque dui, pharetra eu accumsan a, interdum et metus. Proin et
lacus eget purus sagittis tempor at eu dolor. Nullam eget auctor velit. Sed
vitae venenatis nulla, ut dignissim magna. Proin euismod et arcu eu suscipit.
Curabitur sit amet congue lorem. Nullam tincidunt nisl sit amet odio dignissim,
nec vulputate leo lobortis. Pellentesque habitant morbi tristique senectus et
netus et malesuada fames ac turpis egestas. Ut congue ipsum sem, et rhoncus
neque interdum nec. Curabitur in eros ullamcorper, facilisis leo auctor,
vestibulum nisl. Curabitur eu odio eros. Fusce fermentum ornare augue non
luctus. Nunc eget purus in eros euismod fermentum. Praesent interdum neque sed
mi gravida, vel condimentum est efficitur. Ut a nulla non lectus pellentesque
pretium.
Mauris dictum odio consequat iaculis dapibus. Fusce porttitor, nulla non semper
ultricies, lectus mauris lacinia quam, id dapibus metus metus eu neque.
Maecenas commodo, velit sed hendrerit imperdiet, ex turpis tincidunt nisi,
vitae accumsan ipsum lorem id dui. Pellentesque nec ipsum neque. Ut sed quam
auctor, fermentum lorem tincidunt, aliquam ligula. Ut vulputate nibh id ligula
aliquam aliquet. Donec egestas libero a augue convallis commodo. Sed commodo
ligula aliquet dictum aliquam. Integer pulvinar urna ac sapien scelerisque
mattis. Curabitur maximus ultricies nisi, sed tristique lacus tincidunt vitae.
Fusce tempus magna egestas interdum molestie.
Quisque nec molestie libero. Aliquam erat volutpat. Praesent sagittis tortor
ante, vel laoreet dui fermentum in. Aliquam a leo ut arcu egestas posuere. Nam
sit amet ex pulvinar, dapibus mi nec, malesuada velit. Phasellus dignissim
sapien nulla, sit amet aliquam turpis iaculis quis. Nulla vel metus id mauris
eleifend aliquam. Phasellus et risus purus. Vivamus lobortis lorem non libero
semper, et lacinia diam accumsan.
Ut eu molestie ante. Nunc a varius neque, non sollicitudin nisi. Nullam et
fringilla libero. Fusce turpis lacus, aliquam non dui ac, laoreet condimentum
magna. Integer scelerisque risus non lectus porttitor cursus. Duis porta cursus
turpis, vitae laoreet justo mattis a. Donec ac orci odio. Aliquam pulvinar
dictum ex. Vivamus ut ipsum nec sapien venenatis pulvinar. Nullam pulvinar
porttitor libero, et luctus quam tincidunt sed. Aenean id efficitur dolor.
Curabitur nisi dolor, mollis ac elit sed, sodales viverra ante. Nulla eget
malesuada sapien. Aliquam eu erat ac lorem laoreet sagittis.
Nam commodo leo libero, eu faucibus eros maximus in. Nunc efficitur suscipit mi
quis rutrum. Sed non ligula odio. Maecenas tempus erat urna, nec mattis sapien
ullamcorper eget. Ut massa mauris, cursus quis nulla et, rutrum pharetra arcu.
Morbi erat urna, dapibus ac odio in, tincidunt aliquam odio. Proin quis justo
et nisi cursus maximus. Maecenas eu ornare dui. Sed nec vulputate justo. Etiam
dapibus sed metus vitae consequat. In at pharetra arcu. Duis id massa mauris.
Integer accumsan orci a congue laoreet. Morbi auctor pulvinar pellentesque.
Praesent sollicitudin enim risus, non tristique sapien hendrerit quis. Donec
eget tempus est, sed ultrices ex. Maecenas ut turpis placerat, faucibus magna
a, dapibus nisl. Donec vitae est eu leo interdum placerat nec sodales nulla. In
lobortis egestas enim, ac pretium enim blandit in. Nullam efficitur justo id
magna accumsan egestas. Proin non neque nec libero dapibus tincidunt. Duis eget
tristique libero. Nam in lobortis ligula. Donec fermentum metus in enim
pharetra malesuada.
Nunc interdum, orci ac luctus viverra, turpis nunc tempus purus, dignissim
tempus turpis mauris ac diam. Pellentesque ut urna enim. In dui sapien, euismod
et sagittis et, posuere vitae quam. Sed sit amet nulla turpis. Nullam non
aliquam enim, eget tempor nulla. Aliquam tincidunt hendrerit tellus sit amet
porttitor. Integer erat lorem, ultrices quis semper eget, rutrum id nisl. Cras
porttitor felis vitae lorem tristique semper. Aenean maximus lectus libero, ac
fermentum tortor consequat ac. Integer cursus augue nec tellus molestie, vitae
ornare massa ultrices. Duis luctus dignissim porttitor. Sed eu lobortis neque.
Aenean dapibus laoreet congue. Integer vitae viverra sem.
Mauris velit lectus, blandit at auctor non, porttitor dapibus velit. Phasellus
egestas volutpat lectus, in sodales lacus cursus vitae. Fusce lectus sapien,
blandit in elit sit amet, rhoncus dignissim turpis. Donec ut ex dictum, auctor
ex varius, maximus ligula. Fusce mattis pharetra eros, sit amet tincidunt ante
sollicitudin sit amet. Duis eros metus, congue id massa sit amet, lobortis
malesuada lectus. Pellentesque sagittis urna et imperdiet feugiat. Sed at nisl
vitae lectus laoreet malesuada ut a leo. Sed rhoncus ex sit amet erat luctus
bibendum. Quisque bibendum purus ultricies ligula tincidunt lobortis.
Vestibulum orci erat, euismod sed felis non, sagittis ultrices purus. Donec
faucibus dolor est, vitae ornare nulla congue ac. Duis ac ultrices massa, id
facilisis turpis. Curabitur luctus lorem sed justo imperdiet aliquam. Quisque a
ultricies quam. Nam scelerisque eu felis id dictum.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec molestie tempus
justo eget vulputate. Praesent fermentum metus ex, ac convallis ipsum egestas
et. Phasellus vel placerat metus. Nulla facilisi. Mauris pretium risus in
sodales convallis. Vestibulum rutrum rutrum nibh, nec gravida nisl gravida eu.
Sed imperdiet at urna a tincidunt. Curabitur sed diam aliquet, consectetur
metus ac, placerat tellus. Quisque vel dignissim nisi. Donec vitae elit risus.
Phasellus ullamcorper, libero sit amet tincidunt placerat, neque arcu tincidunt
orci, eu pulvinar orci dui in diam. Integer lacinia vestibulum feugiat. Sed a
luctus enim. Aenean eu augue est. Vivamus molestie, arcu eu bibendum
vestibulum, est lorem volutpat leo, vel euismod leo nibh nec ipsum. Integer
magna leo, tempus eu consectetur vitae, tristique ultrices lectus. Curabitur
elit turpis, convallis et eros a, condimentum condimentum metus. Lorem ipsum
dolor sit amet, consectetur adipiscing elit. Maecenas vulputate lacus at mattis
facilisis.
Ut diam mi, facilisis vel augue nec, luctus consectetur augue. Nulla non
pellentesque nulla. Integer vel porta augue. Nullam tincidunt sit amet nisi id
mollis. Nulla fringilla nunc in metus vulputate, sit amet gravida nisi cursus.
Mauris posuere elementum metus, ut molestie felis consectetur a. Nullam vitae
euismod velit, sit amet ultricies elit. Ut auctor orci felis, tempor feugiat
felis elementum facilisis. Pellentesque vehicula non lectus in dapibus. Nam sit
amet tincidunt felis. Pellentesque ac dui eu ipsum viverra placerat non a odio.
Sed sit amet vestibulum nisi, et sodales mauris. Nam vestibulum feugiat
interdum.
Maecenas feugiat a elit sit amet lacinia. Fusce tristique felis vel elit
pharetra, et vestibulum arcu pellentesque. Proin commodo egestas risus, eget
tincidunt turpis pharetra eget. Maecenas tincidunt ipsum ultrices erat
vestibulum suscipit. Etiam aliquet risus at ante maximus suscipit. Ut non
faucibus diam, a dapibus est. Proin egestas, leo et malesuada tincidunt, quam
erat commodo ex, ut convallis tellus ex ac massa. Aenean maximus laoreet magna.
In ut nibh placerat nisl aliquam fermentum et et quam. Aenean varius libero sit
amet aliquet fermentum. Proin et velit eget nulla dignissim tristique.
Vivamus malesuada, mauris in tincidunt congue, sem sapien efficitur mi, in
elementum enim est a diam. Nulla in tincidunt diam, molestie mollis risus. In
pharetra iaculis erat, ac semper diam condimentum vitae. Morbi ante neque,
sollicitudin eget mauris sit amet, eleifend mattis mi. Curabitur blandit nunc
sapien, at rhoncus sem ultricies non. Quisque a tristique magna. Praesent
eleifend, lorem nec blandit commodo, mi nisi tincidunt metus, cursus aliquam
eros lectus vel dui. Mauris blandit congue tortor et consectetur. Etiam vitae
sem commodo, auctor est elementum, tempus urna.
Vivamus gravida luctus scelerisque. Praesent non scelerisque erat. Pellentesque
fermentum tellus sapien, et viverra odio sollicitudin ac. Etiam accumsan odio
metus, et dictum nulla pharetra sed. Ut eu vulputate quam. Orci varius natoque
penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi at
molestie justo. Ut mattis porta tempor. Nunc commodo est ac felis tempor, quis
lobortis mauris commodo. Nullam id accumsan nulla. Nullam in nulla mauris. Sed
euismod tortor eget tempus venenatis. Suspendisse consequat pellentesque enim.
Nunc nulla ligula, volutpat ut consequat eget, dignissim vitae mi. Etiam sit
amet justo nec justo lacinia malesuada eget at diam. Nunc consequat libero
eleifend, consequat ex ac, tincidunt turpis. Morbi faucibus quam et ante
efficitur, dignissim ullamcorper orci finibus. Phasellus vel convallis orci.
Donec ultricies, odio eu tristique accumsan, turpis risus malesuada metus, in
mattis elit ligula nec metus. Sed ac eros velit. Pellentesque rutrum sagittis
finibus. Nullam cursus interdum est, sit amet maximus est posuere eu. Integer
tempor justo non tellus volutpat, at vulputate urna malesuada. Vestibulum eget
eros quis erat sollicitudin finibus. Aenean in sem eget ipsum sodales
hendrerit. Praesent sit amet egestas lorem, vel bibendum nulla. Nullam ac
malesuada lacus.
Nullam massa lacus, lobortis vel vulputate in, venenatis id arcu. Sed eu libero
elit. Suspendisse metus metus, lobortis quis congue in, imperdiet vel est.
Phasellus elit leo, rutrum eget augue vel, vestibulum feugiat mi. Nullam nisl
arcu, interdum vitae lorem nec, accumsan viverra leo. Proin massa magna,
volutpat vitae dapibus et, porta in lectus. Aliquam venenatis enim ex, ac
consectetur ligula porttitor sed. Curabitur vel justo eros. Aliquam at id.
`

View File

@@ -1,103 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package tar
import (
"fmt"
arcmod "github.com/nabbar/golib/archive/archive"
liberr "github.com/nabbar/golib/errors"
)
const pkgName = "golib/archive/tar"
const (
ErrorParamEmpty liberr.CodeError = iota + arcmod.MinPkgArchiveTar
ErrorTarNext
ErrorFileOpen
ErrorFileSeek
ErrorFileClose
ErrorIOCopy
ErrorDirCreate
ErrorLinkCreate
ErrorSymLinkCreate
ErrorDestinationStat
ErrorDestinationIsDir
ErrorDestinationIsNotDir
ErrorDestinationRemove
ErrorTarCreate
ErrorGzipCreate
ErrorTarCreateAddFile
)
func init() {
if liberr.ExistInMapMessage(ErrorParamEmpty) {
panic(fmt.Errorf("error code collision %s", pkgName))
}
liberr.RegisterIdFctMessage(ErrorParamEmpty, getMessage)
}
func getMessage(code liberr.CodeError) (message string) {
switch code {
case liberr.UnknownError:
return liberr.NullMessage
case ErrorParamEmpty:
return "given parameters is empty"
case ErrorTarNext:
return "cannot get next tar file"
case ErrorFileSeek:
return "cannot seek into file"
case ErrorFileOpen:
return "cannot open file"
case ErrorFileClose:
return "closing file occurs error"
case ErrorIOCopy:
return "io copy occurs error"
case ErrorDirCreate:
return "make directory occurs error"
case ErrorLinkCreate:
return "creation of symlink occurs error"
case ErrorSymLinkCreate:
return "creation of symlink occurs error"
case ErrorDestinationStat:
return "cannot stat destination"
case ErrorDestinationIsDir:
return "cannot create destination not directory over an existing directory"
case ErrorDestinationIsNotDir:
return "cannot create destination directory over an existing non directory"
case ErrorDestinationRemove:
return "cannot remove destination "
case ErrorTarCreate:
return "cannot create Tar archive"
case ErrorGzipCreate:
return "cannot create Gzip compression for the Tar archive"
case ErrorTarCreateAddFile:
return "cannot add file content to tar archive"
}
return liberr.NullMessage
}

View File

@@ -1,237 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package tar
import (
"archive/tar"
"io"
"os"
"path/filepath"
"runtime"
"strings"
libarc "github.com/nabbar/golib/archive/archive"
liberr "github.com/nabbar/golib/errors"
libfpg "github.com/nabbar/golib/file/progress"
)
func GetFile(src, dst libfpg.Progress, filenameContain, filenameRegex string) liberr.Error {
if _, e := src.Seek(0, io.SeekStart); e != nil {
return ErrorFileSeek.Error(e)
} else if _, e = dst.Seek(0, io.SeekStart); e != nil {
return ErrorFileSeek.Error(e)
}
r := tar.NewReader(src)
for {
h, e := r.Next()
if e != nil && e == io.EOF {
return nil
} else if e != nil {
return ErrorTarNext.Error(e)
}
if h.FileInfo().Mode()&os.ModeType == os.ModeType {
continue
}
f := libarc.NewFileFullPath(h.Name)
//nolint #nosec
/* #nosec */
if f.MatchingFullPath(filenameContain) || f.RegexFullPath(filenameRegex) {
if _, e = dst.ReadFrom(r); e != nil {
return ErrorIOCopy.Error(e)
} else if _, e = dst.Seek(0, io.SeekStart); e != nil {
return ErrorFileSeek.Error(e)
} else {
return nil
}
}
}
}
func GetAll(src io.ReadSeeker, outputFolder string, defaultDirPerm os.FileMode) liberr.Error {
if _, e := src.Seek(0, io.SeekStart); e != nil {
return ErrorFileSeek.Error(e)
}
r := tar.NewReader(src)
for {
h, e := r.Next()
if e != nil && e == io.EOF {
return nil
} else if e != nil {
return ErrorTarNext.Error(e)
}
dst := filepath.Join(outputFolder, libarc.CleanPath(strings.Replace(h.Name, ".."+string(filepath.Separator), "", -1)))
//nolint #nosec
/* #nosec */
if err := writeContent(r, h, dst, defaultDirPerm); err != nil {
return err
}
}
}
func writeContent(r io.Reader, h *tar.Header, out string, defaultDirPerm os.FileMode) (err liberr.Error) {
var (
e error
inf = h.FileInfo()
dst libfpg.Progress
)
if e := dirIsExistOrCreate(filepath.Dir(out), defaultDirPerm); e != nil {
return e
}
defer func() {
if dst != nil {
if e := dst.Close(); e != nil {
err = ErrorFileClose.Error(e)
err.Add(err)
}
}
}()
if h.Typeflag&tar.TypeDir == tar.TypeDir {
err = dirIsExistOrCreate(out, h.FileInfo().Mode())
return
} else if err = notDirExistCannotClean(out, h.Typeflag, h.Linkname); err != nil {
return
} else if h.Typeflag&tar.TypeLink == tar.TypeLink {
return createLink(out, libarc.CleanPath(h.Linkname), false)
} else if h.Typeflag&tar.TypeSymlink == tar.TypeSymlink {
return createLink(out, libarc.CleanPath(h.Linkname), true)
}
if dst, e = libfpg.New(out, os.O_RDWR|os.O_CREATE|os.O_TRUNC, inf.Mode()); e != nil {
return ErrorFileOpen.Error(e)
} else if _, e = io.Copy(dst, r); e != nil {
return ErrorIOCopy.Error(e)
} else if e = dst.Close(); e != nil {
return ErrorFileClose.Error(e)
}
return nil
}
func dirIsExistOrCreate(dirname string, dirPerm os.FileMode) liberr.Error {
if i, e := os.Stat(dirname); e != nil && os.IsNotExist(e) {
if e = os.MkdirAll(dirname, dirPerm); e != nil {
return ErrorDirCreate.Error(e)
}
} else if e != nil {
return ErrorDestinationStat.Error(e)
} else if !i.IsDir() {
return ErrorDestinationIsNotDir.Error(nil)
}
return nil
}
func notDirExistCannotClean(filename string, flag byte, targetLink string) liberr.Error {
if strings.EqualFold(runtime.GOOS, "windows") {
if flag&tar.TypeLink == tar.TypeLink {
return nil
} else if flag&tar.TypeSymlink == tar.TypeSymlink {
return nil
}
}
if _, e := os.Stat(filename); e != nil && os.IsNotExist(e) {
return nil
} else if e != nil {
return ErrorDestinationStat.Error(e)
} else if flag&tar.TypeLink == tar.TypeLink || flag&tar.TypeSymlink == tar.TypeSymlink {
if hasFSLink(filename) && compareLinkTarget(filename, targetLink) {
return nil
}
}
if e := os.Remove(filename); e != nil {
err := ErrorDestinationRemove.Error(e)
return err
}
return nil
}
func hasFSLink(path string) bool {
link, _ := filepath.EvalSymlinks(path)
if link != "" {
return true
}
return false
}
func createLink(link, target string, sym bool) liberr.Error {
if strings.EqualFold(runtime.GOOS, "windows") {
return nil
}
if _, e := os.Stat(link); e != nil && !os.IsNotExist(e) {
return ErrorDestinationStat.Error(e)
} else if e == nil {
return nil
} else if compareLinkTarget(link, target) {
return nil
}
if sym {
err := os.Symlink(libarc.CleanPath(target), libarc.CleanPath(link))
if err != nil {
return ErrorLinkCreate.Error(err)
}
} else {
err := os.Link(libarc.CleanPath(target), libarc.CleanPath(link))
if err != nil {
return ErrorLinkCreate.Error(err)
}
}
return nil
}
func compareLinkTarget(link, target string) bool {
var l string
l, _ = filepath.EvalSymlinks(link)
if l == "" {
return false
}
return strings.EqualFold(libarc.CleanPath(l), libarc.CleanPath(target))
}

View File

@@ -1,171 +0,0 @@
/***********************************************************************************************************************
*
* MIT License
*
* Copyright (c) 2021 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
**********************************************************************************************************************/
package tar
import (
"archive/tar"
"compress/gzip"
"fmt"
"io"
"os"
"path/filepath"
"strings"
liberr "github.com/nabbar/golib/errors"
)
func Create(archive io.WriteSeeker, stripPath string, comment string, content ...string) (bool, liberr.Error) {
if _, err := archive.Seek(0, io.SeekStart); err != nil {
return false, ErrorFileSeek.Error(err)
}
if ok, err := createTar(archive, stripPath, content...); err != nil || !ok {
return ok, err
}
if _, err := archive.Seek(0, io.SeekStart); err != nil {
return false, ErrorFileSeek.Error(err)
}
return true, nil
}
func CreateGzip(archive io.WriteSeeker, stripPath string, comment string, content ...string) (bool, liberr.Error) {
if _, err := archive.Seek(0, io.SeekStart); err != nil {
return false, ErrorFileSeek.Error(err)
}
z := gzip.NewWriter(archive)
if ok, err := createTar(z, stripPath, content...); err != nil || !ok {
return ok, err
}
if err := z.Close(); err != nil {
return false, ErrorGzipCreate.Error(err)
}
if _, err := archive.Seek(0, io.SeekStart); err != nil {
return false, ErrorFileSeek.Error(err)
}
return true, nil
}
func createTar(w io.Writer, stripPath string, content ...string) (bool, liberr.Error) {
var (
t *tar.Writer
n int64
err error
lEr = ErrorTarCreateAddFile.Error(nil)
)
stripPath = strings.TrimLeft(stripPath, "/")
t = tar.NewWriter(w)
for i := 0; i < len(content); i++ {
if content[i] == "" {
continue
}
err = filepath.Walk(content[i], func(file string, inf os.FileInfo, err error) error {
var (
e error
h *tar.Header
f *os.File
)
// generate tar header
h, e = tar.FileInfoHeader(inf, file)
if e != nil {
return e
}
// must provide real name
// (see https://golang.org/src/archive/tar/common.go?#L626)
h.Name = filepath.ToSlash(file)
if stripPath != "" {
h.Name = filepath.Clean(strings.Replace(strings.TrimLeft(h.Name, "/"), stripPath, "", 1))
}
h.Name = strings.TrimLeft(h.Name, "/")
if h.Name == "" || h.Name == "." {
return nil
}
// write header
if e = t.WriteHeader(h); e != nil {
return e
}
// if not a dir, write file content
if !inf.IsDir() {
//nolint #gosec
/* #nosec */
f, e = os.Open(file)
if e != nil {
return e
}
if _, e = io.Copy(t, f); e != nil {
return e
}
}
n++
return nil
})
if err != nil {
lEr.Add(err)
continue
}
}
if n < 1 {
if lEr.HasParent() {
return false, lEr
}
//nolint #goerr113
return false, ErrorTarCreate.Error(fmt.Errorf("no file to add in archive"))
} else if !lEr.HasParent() {
lEr = nil
}
if err = t.Close(); err != nil {
return false, ErrorTarCreate.Error(err)
}
return true, lEr
}

44
archive/writecloser.go Normal file
View File

@@ -0,0 +1,44 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package archive
import "io"
func NopWriteCloser(w io.Writer) io.WriteCloser {
return &wrp{w: w}
}
type wrp struct {
w io.Writer
}
func (o *wrp) Write(p []byte) (n int, err error) {
return o.w.Write(p)
}
func (o *wrp) Close() error {
return nil
}

View File

@@ -1,106 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package zip
import (
"fmt"
arcmod "github.com/nabbar/golib/archive/archive"
liberr "github.com/nabbar/golib/errors"
)
const pkgName = "golib/archive/zip"
const (
ErrorParamEmpty liberr.CodeError = iota + arcmod.MinPkgArchiveZip
ErrorFileOpen
ErrorFileClose
ErrorFileSeek
ErrorFileStat
ErrorIOCopy
ErrorZipOpen
ErrorZipCreate
ErrorZipComment
ErrorZipAddFile
ErrorZipFileOpen
ErrorZipFileClose
ErrorDirCreate
ErrorDestinationStat
ErrorDestinationIsDir
ErrorDestinationIsNotDir
ErrorDestinationRemove
)
func init() {
if liberr.ExistInMapMessage(ErrorParamEmpty) {
panic(fmt.Errorf("error code collision %s", pkgName))
}
liberr.RegisterIdFctMessage(ErrorParamEmpty, getMessage)
}
func getMessage(code liberr.CodeError) (message string) {
switch code {
case liberr.UnknownError:
return liberr.NullMessage
case ErrorParamEmpty:
return "given parameters is empty"
case ErrorFileOpen:
return "cannot open zipped file"
case ErrorFileClose:
return "closing file occurs error"
case ErrorFileStat:
return "getting file stat occurs error"
case ErrorFileSeek:
return "cannot seek into file"
case ErrorIOCopy:
return "io copy occurs error"
case ErrorZipOpen:
return "cannot open zip file"
case ErrorZipCreate:
return "cannot create zip file"
case ErrorZipComment:
return "cannot set comment to zip file"
case ErrorZipAddFile:
return "cannot add file to zip file"
case ErrorZipFileOpen:
return "cannot open file into zip file"
case ErrorZipFileClose:
return "cannot flose file into zip file"
case ErrorDirCreate:
return "make directory occurs error"
case ErrorDestinationStat:
return "cannot stat destination"
case ErrorDestinationIsDir:
return "cannot create destination not directory over an existing directory"
case ErrorDestinationIsNotDir:
return "cannot create destination directory over an existing non directory"
case ErrorDestinationRemove:
return "cannot remove destination "
}
return liberr.NullMessage
}

View File

@@ -1,216 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package zip
import (
"archive/zip"
"io"
"os"
"path/filepath"
"strings"
arcmod "github.com/nabbar/golib/archive/archive"
liberr "github.com/nabbar/golib/errors"
libfpg "github.com/nabbar/golib/file/progress"
)
func GetFile(src, dst libfpg.Progress, filenameContain, filenameRegex string) liberr.Error {
var (
arc *zip.Reader
inf os.FileInfo
err error
)
if _, err = src.Seek(0, io.SeekStart); err != nil {
return ErrorFileSeek.Error(err)
} else if _, err = dst.Seek(0, io.SeekStart); err != nil {
return ErrorFileSeek.Error(err)
} else if inf, err = src.Stat(); err != nil {
return ErrorFileStat.Error(err)
} else if arc, err = zip.NewReader(src, inf.Size()); err != nil {
return ErrorZipOpen.Error(err)
}
for _, f := range arc.File {
if f.Mode()&os.ModeType == os.ModeType {
continue
}
z := arcmod.NewFileFullPath(f.Name)
if z.MatchingFullPath(filenameContain) || z.RegexFullPath(filenameRegex) {
if f == nil {
continue
}
var (
r io.ReadCloser
e error
)
if r, e = f.Open(); e != nil {
//logger.ErrorLevel.LogErrorCtx(logger.DebugLevel, "open zipped file reader", err)
return ErrorZipFileOpen.Error(e)
}
defer func() {
_ = r.Close()
}()
//nolint #nosec
/* #nosec */
if _, e = dst.ReadFrom(r); e != nil {
//logger.ErrorLevel.LogErrorCtx(logger.DebugLevel, "copy buffer from archive reader", err)
return ErrorIOCopy.Error(e)
}
if _, e = dst.Seek(0, io.SeekStart); e != nil {
//logger.ErrorLevel.LogErrorCtx(logger.DebugLevel, "seeking temp file", err)
return ErrorFileSeek.Error(e)
}
return nil
}
}
return nil
}
func GetAll(src libfpg.Progress, outputFolder string, defaultDirPerm os.FileMode) liberr.Error {
var (
r *zip.Reader
i os.FileInfo
e error
)
if _, e = src.Seek(0, io.SeekStart); e != nil {
return ErrorFileSeek.Error(e)
} else if i, e = src.Stat(); e != nil {
return ErrorFileStat.Error(e)
} else if r, e = zip.NewReader(src, i.Size()); e != nil {
return ErrorZipOpen.Error(e)
}
for _, f := range r.File {
if f == nil {
continue
}
dst := filepath.Join(outputFolder, arcmod.CleanPath(strings.Replace(f.Name, ".."+string(filepath.Separator), "..", -1)))
// #nosec
if err := writeContent(f, dst, defaultDirPerm); err != nil {
return err
}
}
return nil
}
func writeContent(f *zip.File, out string, defaultDirPerm os.FileMode) (err liberr.Error) {
var (
dst libfpg.Progress
inf = f.FileInfo()
r io.ReadCloser
e error
)
if err = dirIsExistOrCreate(filepath.Dir(out), defaultDirPerm); err != nil {
return
}
defer func() {
if dst != nil {
if e = dst.Close(); e != nil {
err = ErrorFileClose.Error(e)
err.Add(err)
}
}
if r != nil {
if e = r.Close(); e != nil {
err = ErrorZipFileClose.Error(err)
}
}
}()
if inf.IsDir() {
err = dirIsExistOrCreate(out, inf.Mode())
return
} else if inf.Mode()&os.ModeSymlink == os.ModeSymlink {
return nil
} else if err = notDirExistCannotClean(out); err != nil {
return
}
if dst, e = libfpg.New(out, os.O_RDWR|os.O_CREATE|os.O_TRUNC, inf.Mode()); e != nil {
return ErrorFileOpen.Error(e)
} else {
}
if r, e = f.Open(); e != nil {
return ErrorZipFileOpen.Error(e)
}
// #nosec
_, e = io.Copy(dst, r)
if e != nil {
return ErrorIOCopy.Error(e)
} else if e = dst.Close(); e != nil {
return ErrorFileClose.Error(e)
}
return nil
}
func dirIsExistOrCreate(dirname string, dirPerm os.FileMode) liberr.Error {
if i, e := os.Stat(filepath.Dir(dirname)); e != nil && os.IsNotExist(e) {
if e = os.MkdirAll(filepath.Dir(dirname), dirPerm); e != nil {
return ErrorDirCreate.Error(e)
}
} else if e != nil {
return ErrorDestinationStat.Error(e)
} else if !i.IsDir() {
return ErrorDestinationIsNotDir.Error(nil)
}
return nil
}
func notDirExistCannotClean(filename string) liberr.Error {
if i, e := os.Stat(filename); e != nil && !os.IsNotExist(e) {
return ErrorDestinationStat.Error(e)
} else if e == nil && i.IsDir() {
return ErrorDestinationIsDir.Error(nil)
} else if e == nil {
if e = os.Remove(filename); e != nil {
return ErrorDestinationRemove.Error(e)
}
}
return nil
}

View File

@@ -1,161 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package zip
import (
"archive/zip"
"compress/flate"
"fmt"
"io"
"os"
"path/filepath"
"strings"
liberr "github.com/nabbar/golib/errors"
)
func Create(archive io.WriteSeeker, stripPath string, comment string, content ...string) (bool, liberr.Error) {
if _, err := archive.Seek(0, io.SeekStart); err != nil {
return false, ErrorFileSeek.Error(err)
}
var (
z = zip.NewWriter(archive)
)
if z == nil {
return false, ErrorZipOpen.Error(nil)
} else {
z.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
return flate.NewWriter(out, flate.BestCompression)
})
}
if comment != "" {
if e := z.SetComment(comment); e != nil {
return false, ErrorZipComment.Error(e)
}
}
if ok, err := addFileToZip(z, stripPath, content...); err != nil || !ok {
return ok, err
}
if _, err := archive.Seek(0, io.SeekStart); err != nil {
return false, ErrorFileSeek.Error(err)
}
return true, nil
}
func addFileToZip(z *zip.Writer, stripPath string, content ...string) (bool, liberr.Error) {
var (
n int64
err error
w io.Writer
lEr = ErrorZipAddFile.Error(nil)
)
stripPath = strings.TrimLeft(stripPath, "/")
for i := 0; i < len(content); i++ {
if content[i] == "" {
continue
}
err = filepath.Walk(content[i], func(file string, inf os.FileInfo, err error) error {
var (
e error
h *zip.FileHeader
f *os.File
)
// generate tar header
h, e = zip.FileInfoHeader(inf)
if e != nil {
return e
}
// must provide real name
// (see https://golang.org/src/archive/tar/common.go?#L626)
h.Name = filepath.ToSlash(file)
if stripPath != "" {
h.Name = filepath.Clean(strings.Replace(strings.TrimLeft(h.Name, "/"), stripPath, "", 1))
}
h.Name = strings.TrimLeft(h.Name, "/")
if h.Name == "" || h.Name == "." {
return nil
}
// write header
if w, e = z.CreateHeader(h); e != nil {
return e
} else if !inf.IsDir() {
// if not a dir, write file content
//nolint #gosec
/* #nosec */
f, e = os.Open(file)
if e != nil {
return e
}
if _, e = io.Copy(w, f); e != nil {
return e
}
}
n++
return nil
})
if err != nil {
lEr.Add(err)
continue
}
}
if n < 1 {
if lEr.HasParent() {
return false, lEr
}
return false, ErrorZipCreate.Error(fmt.Errorf("no file to add in archive"))
} else if !lEr.HasParent() {
lEr = nil
}
if err = z.Close(); err != nil {
return false, ErrorZipCreate.Error(err)
}
return true, lEr
}

6
go.mod
View File

@@ -14,6 +14,7 @@ require (
github.com/aws/smithy-go v1.20.1
github.com/bits-and-blooms/bitset v1.13.0
github.com/c-bata/go-prompt v0.2.6
github.com/dsnet/compress v0.0.1
github.com/fatih/color v1.16.0
github.com/fsnotify/fsnotify v1.7.0
github.com/fxamacker/cbor/v2 v2.6.0
@@ -35,9 +36,10 @@ require (
github.com/nats-io/nats-server/v2 v2.10.12
github.com/nats-io/nats.go v1.34.0
github.com/nutsdb/nutsdb v0.14.3
github.com/onsi/ginkgo/v2 v2.17.0
github.com/onsi/ginkgo/v2 v2.17.1
github.com/onsi/gomega v1.32.0
github.com/pelletier/go-toml v1.9.5
github.com/pierrec/lz4/v4 v4.1.21
github.com/prometheus/client_golang v1.19.0
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/sirupsen/logrus v1.9.3
@@ -45,6 +47,7 @@ require (
github.com/spf13/jwalterweatherman v1.1.0
github.com/spf13/viper v1.18.2
github.com/ugorji/go/codec v1.2.12
github.com/ulikunitz/xz v0.5.11
github.com/vbauerster/mpb/v8 v8.7.2
github.com/xanzy/go-gitlab v0.101.0
github.com/xhit/go-simple-mail v2.2.2+incompatible
@@ -174,7 +177,6 @@ require (
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/paulmach/orb v0.11.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.0 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/term v1.2.0-beta.2 // indirect
github.com/prometheus/client_model v0.5.0 // indirect

View File

@@ -33,11 +33,13 @@ package nutsdb
import (
"io"
"os"
"path/filepath"
"strings"
libfpg "github.com/nabbar/golib/file/progress"
arcarc "github.com/nabbar/golib/archive/archive"
arctps "github.com/nabbar/golib/archive/archive/types"
"github.com/nabbar/golib/archive"
libarc "github.com/nabbar/golib/archive"
arccmp "github.com/nabbar/golib/archive/compress"
liberr "github.com/nabbar/golib/errors"
"github.com/nutsdb/nutsdb"
)
@@ -86,30 +88,27 @@ func (s *snap) Prepare(opt Options, db *nutsdb.DB) liberr.Error {
func (s *snap) Save(opt Options, writer io.Writer) liberr.Error {
var (
e error
tmp libfpg.Progress
err liberr.Error
g io.WriteCloser
t arctps.Writer
f = func(str string) string {
return strings.TrimPrefix(str, s.path)
}
)
defer func() {
if tmp != nil {
_ = tmp.CloseDelete()
if t != nil {
_ = t.Close()
}
if g != nil {
_ = g.Close()
}
}()
if tmp, e = libfpg.Unique(opt.GetTempDir(), opt.NewTempFilePattern("tar")); e != nil {
return ErrorFileTemp.Error(e)
}
if _, err = archive.CreateArchive(archive.TypeTarGzip, tmp, s.path, s.path); err != nil {
return ErrorFolderArchive.Error(err)
}
if _, e = tmp.Seek(0, io.SeekStart); e != nil {
if g, e = arccmp.Gzip.Writer(libarc.NopWriteCloser(writer)); e != nil {
return ErrorDatabaseSnapshot.Error(e)
}
if _, e = tmp.WriteTo(writer); e != nil {
} else if t, e = arcarc.Tar.Writer(g); e != nil {
return ErrorDatabaseSnapshot.Error(e)
} else if e = t.FromPath(s.path, "", f); e != nil {
return ErrorDatabaseSnapshot.Error(e)
}
@@ -118,38 +117,21 @@ func (s *snap) Save(opt Options, writer io.Writer) liberr.Error {
func (s *snap) Load(opt Options, reader io.Reader) liberr.Error {
var (
a string
o string
e error
tmp libfpg.Progress
err liberr.Error
d string
)
defer func() {
if tmp != nil {
_ = tmp.CloseDelete()
}
}()
if tmp, e = libfpg.Unique(opt.GetTempDir(), opt.NewTempFilePattern("tar.gz")); e != nil {
return ErrorFileTemp.Error(e)
}
if _, e = tmp.ReadFrom(reader); e != nil {
if d, e = opt.NewTempFolder(); e != nil {
return ErrorDatabaseSnapshot.Error(e)
} else if e = libarc.ExtractAll(io.NopCloser(reader), "unknown", d); e != nil {
return ErrorDatabaseSnapshot.Error(e)
} else {
s.path = d
}
if o, err = opt.NewTempFolder(); err != nil {
return err
}
if err = archive.ExtractAll(tmp, filepath.Base(a), o, opt.Permission()); err != nil {
return ErrorFolderExtract.Error(err)
}
s.path = o
return nil
}

View File

@@ -1,99 +0,0 @@
//+build examples
//go:build examples
// +build examples
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package main
import (
"fmt"
"io/ioutil"
"os"
"path"
"github.com/nabbar/golib/archive"
"github.com/nabbar/golib/errors"
"github.com/nabbar/golib/ioutils"
"github.com/nabbar/golib/logger"
)
// git archive --format=tar --output=git.tar HEAD && gzip git.tar
const fileName = "./git.tar.gz"
func main() {
var (
src ioutils.FileProgress
tmp ioutils.FileProgress
out string
err error
)
defer func() {
if src != nil {
_ = src.Close()
}
if tmp != nil {
_ = tmp.Close()
}
}()
logger.SetLevel(logger.DebugLevel)
logger.EnableColor()
logger.FileTrace(true)
errors.SetModeReturnError(errors.ErrorReturnCodeErrorTraceFull)
if src, err = ioutils.NewFileProgressPathOpen(fileName); err != nil {
panic(err)
}
if tmp, err = ioutils.NewFileProgressTemp(); err != nil {
panic(err)
} else {
out = tmp.FilePath()
_ = tmp.Close()
}
if err = archive.ExtractAll(src, path.Base(src.FilePath()), out, 0775); err != nil {
panic(err)
}
if list, e := ioutil.ReadDir(out); e != nil {
panic(e)
} else {
for _, f := range list {
var (
isDir bool
isLink bool
isFile bool
)
isDir = f.IsDir()
isLink = f.Mode()&os.ModeSymlink == os.ModeSymlink
isFile = !isLink && !isDir
println(fmt.Sprintf("Item '%s' is Dir '%v', is Link '%v', is File '%v'", f.Name(), isDir, isLink, isFile))
}
}
}

View File

@@ -1,100 +0,0 @@
//go:build examples
// +build examples
/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package main
import (
"io"
"io/ioutil"
"github.com/nabbar/golib/errors"
"github.com/nabbar/golib/archive"
"github.com/nabbar/golib/ioutils"
)
// git archive --format=tar --output=git.tar HEAD
// const fileName = "./git.tar"
const fileName = "./vendor.zip"
// const contain = "version/license_mit.go"
const contain = "vendor/github.com/gin-gonic/gin/internal/json/json.go"
// const regex = ""
const regex = "vendor\\.tar(\\.(?:gz|bz))?"
func main() {
var (
src ioutils.FileProgress
tmp ioutils.FileProgress
rio ioutils.FileProgress
err errors.Error
)
defer func() {
if src != nil {
_ = src.Close()
}
if tmp != nil {
_ = tmp.Close()
}
if rio != nil {
_ = rio.Close()
}
}()
if src, err = ioutils.NewFileProgressPathOpen(fileName); err != nil {
panic(err)
}
if tmp, err = ioutils.NewFileProgressTemp(); err != nil {
panic(err)
}
if rio, err = ioutils.NewFileProgressTemp(); err != nil {
panic(err)
}
if _, e := tmp.ReadFrom(src); e != nil {
panic(e)
}
if err = archive.ExtractFile(tmp, rio, contain, regex); err != nil {
panic(err)
}
if _, e := rio.Seek(0, io.SeekStart); e != nil {
panic(e)
}
if b, e := ioutil.ReadAll(rio); e != nil {
panic(e)
} else {
println(string(b))
}
}

View File

@@ -1,35 +0,0 @@
#!/usr/bin/env bash
[ ! -e vendor.tar.gz ] || rm -v vendor.tar.gz
[ ! -e vendor.tar.bz2 ] || rm -v vendor.tar.bz2
[ ! -e vendor.zip ] || rm -v vendor.zip
[ ! -e ph-000.tar ] || rm -v ph-00*.tar
[ ! -e test-archive ] || rm -v test-archive
tar cf ph-000.tar vendor/ && \
cp -v ph-000.tar ph-001.tar && \
cp -v ph-000.tar ph-002.tar && \
cp -v ph-000.tar ph-003.tar && \
cp -v ph-000.tar ph-004.tar && \
cp -v ph-000.tar ph-005.tar && \
cp -v ph-000.tar ph-006.tar && \
cp -v ph-000.tar ph-007.tar && \
cp -v ph-000.tar ph-008.tar && \
cp -v ph-000.tar ph-009.tar && \
tar czf vendor.tar.gz vendor/ && \
tar cjf vendor.tar.bz2 ph-00*.tar vendor.tar.gz && \
zip vendor.zip ph-00*.tar vendor.tar.bz2 && \
rm -vf ph-00*.tar vendor.tar.* && \
ls -ahl vendor.zip && \
go build -a -v -o test-archive ./test/test-archive/ && \
echo -e "\n\n\t>>Try to print contents of file github.com/gin-gonic/gin/internal/json/json.go" && \
./test-archive
echo -e "\n\n\t>>from archive zip >> bz2 >> tar >> gz >> tar :" && \
ls -ahl vendor.zip && \
echo -e "\n\n"
[ ! -e vendor.tar.gz ] || rm -v vendor.tar.gz
[ ! -e vendor.tar.bz2 ] || rm -v vendor.tar.bz2
[ ! -e vendor.zip ] || rm -v vendor.zip
[ ! -e ph-000.tar ] || rm -v ph-00*.tar
[ ! -e test-archive ] || rm -v test-archive