diff --git a/Makefile b/Makefile index 72c21f6..8967673 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,10 @@ BUILDPATH := github.com/vbatts/go-mtree/cmd/gomtree CWD := $(shell pwd) SOURCE_FILES := $(shell find . -type f -name "*.go") CLEAN_FILES := *~ -TAGS := cvis +TAGS := ARCHES := linux,386 linux,amd64 linux,arm linux,arm64 openbsd,amd64 windows,amd64 darwin,amd64 -default: build validation +default: build validation .PHONY: validation validation: .test .lint .vet .cli.test diff --git a/cvis/cvis_test.go b/cvis/cvis_test.go deleted file mode 100644 index 75e3884..0000000 --- a/cvis/cvis_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// +build cgo,!govis - -package cvis - -import "testing" - -// The resulting string of Vis output could potentially be four times longer than -// the original. Vis must handle this possibility. -func TestVisLength(t *testing.T) { - testString := "All work and no play makes Jack a dull boy\n" - for i := 0; i < 20; i++ { - Vis(testString, DefaultVisFlags) - testString = testString + testString - } -} diff --git a/cvis/unvis.c b/cvis/unvis.c deleted file mode 100644 index ab16298..0000000 --- a/cvis/unvis.c +++ /dev/null @@ -1,293 +0,0 @@ -/*- - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)unvis.c 8.1 (Berkeley) 6/4/93"; -#endif /* LIBC_SCCS and not lint */ - -#include - -#include -#include -#include "vis.h" - -/* - * decode driven by state machine - */ -#define S_GROUND 0 /* haven't seen escape char */ -#define S_START 1 /* start decoding special sequence */ -#define S_META 2 /* metachar started (M) */ -#define S_META1 3 /* metachar more, regular char (-) */ -#define S_CTRL 4 /* control char started (^) */ -#define S_OCTAL2 5 /* octal digit 2 */ -#define S_OCTAL3 6 /* octal digit 3 */ -#define S_HEX2 7 /* hex digit 2 */ - -#define S_HTTP 0x080 /* %HEXHEX escape */ - -#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') -#define ishex(c) ((((u_char)(c)) >= '0' && ((u_char)(c)) <= '9') || (((u_char)(c)) >= 'a' && ((u_char)(c)) <= 'f')) - -/* - * unvis - decode characters previously encoded by vis - */ -int -unvis(char *cp, int c, int *astate, int flag) -{ - - if (flag & UNVIS_END) { - if (*astate == S_OCTAL2 || *astate == S_OCTAL3) { - *astate = S_GROUND; - return (UNVIS_VALID); - } - return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD); - } - - switch (*astate & ~S_HTTP) { - - case S_GROUND: - *cp = 0; - if (c == '\\') { - *astate = S_START; - return (0); - } - if (flag & VIS_HTTPSTYLE && c == '%') { - *astate = S_START | S_HTTP; - return (0); - } - *cp = c; - return (UNVIS_VALID); - - case S_START: - if (*astate & S_HTTP) { - if (ishex(tolower(c))) { - *cp = isdigit(c) ? (c - '0') : (tolower(c) - 'a'); - *astate = S_HEX2; - return (0); - } - } - switch(c) { - case '\\': - *cp = c; - *astate = S_GROUND; - return (UNVIS_VALID); - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - *cp = (c - '0'); - *astate = S_OCTAL2; - return (0); - case 'M': - *cp = 0200; - *astate = S_META; - return (0); - case '^': - *astate = S_CTRL; - return (0); - case 'n': - *cp = '\n'; - *astate = S_GROUND; - return (UNVIS_VALID); - case 'r': - *cp = '\r'; - *astate = S_GROUND; - return (UNVIS_VALID); - case 'b': - *cp = '\b'; - *astate = S_GROUND; - return (UNVIS_VALID); - case 'a': - *cp = '\007'; - *astate = S_GROUND; - return (UNVIS_VALID); - case 'v': - *cp = '\v'; - *astate = S_GROUND; - return (UNVIS_VALID); - case 't': - *cp = '\t'; - *astate = S_GROUND; - return (UNVIS_VALID); - case 'f': - *cp = '\f'; - *astate = S_GROUND; - return (UNVIS_VALID); - case 's': - *cp = ' '; - *astate = S_GROUND; - return (UNVIS_VALID); - case 'E': - *cp = '\033'; - *astate = S_GROUND; - return (UNVIS_VALID); - case '\n': - /* - * hidden newline - */ - *astate = S_GROUND; - return (UNVIS_NOCHAR); - case '$': - /* - * hidden marker - */ - *astate = S_GROUND; - return (UNVIS_NOCHAR); - } - *astate = S_GROUND; - return (UNVIS_SYNBAD); - - case S_META: - if (c == '-') - *astate = S_META1; - else if (c == '^') - *astate = S_CTRL; - else { - *astate = S_GROUND; - return (UNVIS_SYNBAD); - } - return (0); - - case S_META1: - *astate = S_GROUND; - *cp |= c; - return (UNVIS_VALID); - - case S_CTRL: - if (c == '?') - *cp |= 0177; - else - *cp |= c & 037; - *astate = S_GROUND; - return (UNVIS_VALID); - - case S_OCTAL2: /* second possible octal digit */ - if (isoctal(c)) { - /* - * yes - and maybe a third - */ - *cp = (*cp << 3) + (c - '0'); - *astate = S_OCTAL3; - return (0); - } - /* - * no - done with current sequence, push back passed char - */ - *astate = S_GROUND; - return (UNVIS_VALIDPUSH); - - case S_OCTAL3: /* third possible octal digit */ - *astate = S_GROUND; - if (isoctal(c)) { - *cp = (*cp << 3) + (c - '0'); - return (UNVIS_VALID); - } - /* - * we were done, push back passed char - */ - return (UNVIS_VALIDPUSH); - - case S_HEX2: /* second mandatory hex digit */ - if (ishex(tolower(c))) { - *cp = (isdigit(c) ? (*cp << 4) + (c - '0') : (*cp << 4) + (tolower(c) - 'a' + 10)); - } - *astate = S_GROUND; - return (UNVIS_VALID); - - default: - /* - * decoder in unknown state - (probably uninitialized) - */ - *astate = S_GROUND; - return (UNVIS_SYNBAD); - } -} - -/* - * strunvis - decode src into dst - * - * Number of chars decoded into dst is returned, -1 on error. - * Dst is null terminated. - */ - -int -strunvis(char *dst, const char *src) -{ - char c; - char *start = dst; - int state = 0; - - while ( (c = *src++) ) { - again: - switch (unvis(dst, c, &state, 0)) { - case UNVIS_VALID: - dst++; - break; - case UNVIS_VALIDPUSH: - dst++; - goto again; - case 0: - case UNVIS_NOCHAR: - break; - default: - return (-1); - } - } - if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID) - dst++; - *dst = '\0'; - return (dst - start); -} - -int -strunvisx(char *dst, const char *src, int flag) -{ - char c; - char *start = dst; - int state = 0; - - while ( (c = *src++) ) { - again: - switch (unvis(dst, c, &state, flag)) { - case UNVIS_VALID: - dst++; - break; - case UNVIS_VALIDPUSH: - dst++; - goto again; - case 0: - case UNVIS_NOCHAR: - break; - default: - return (-1); - } - } - if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID) - dst++; - *dst = '\0'; - return (dst - start); -} diff --git a/cvis/unvis.go b/cvis/unvis.go deleted file mode 100644 index 398a53b..0000000 --- a/cvis/unvis.go +++ /dev/null @@ -1,22 +0,0 @@ -package cvis - -// #include "vis.h" -// #include -import "C" -import ( - "fmt" - "unsafe" -) - -// Unvis decodes the Vis() string encoding -func Unvis(src string) (string, error) { - cDst, cSrc := C.CString(string(make([]byte, len(src)+1))), C.CString(src) - defer C.free(unsafe.Pointer(cDst)) - defer C.free(unsafe.Pointer(cSrc)) - ret := C.strunvis(cDst, cSrc) - // TODO(vbatts) this needs to be confirmed against UnvisError - if ret == -1 { - return "", fmt.Errorf("failed to decode: %q", src) - } - return C.GoString(cDst), nil -} diff --git a/cvis/vis.c b/cvis/vis.c deleted file mode 100644 index c3390b4..0000000 --- a/cvis/vis.c +++ /dev/null @@ -1,202 +0,0 @@ -/*- - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)vis.c 8.1 (Berkeley) 7/19/93"; -#endif /* LIBC_SCCS and not lint */ - - -#include - -#include -#include -#include -#include -#include "vis.h" - -#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') - -/* - * vis - visually encode characters - */ -char * -vis(dst, c, flag, nextc) - char *dst; - int c, nextc; - int flag; -{ - c = (unsigned char)c; - - if (flag & VIS_HTTPSTYLE) { - /* Described in RFC 1808 */ - if (!(isalnum(c) /* alpha-numeric */ - /* safe */ - || c == '$' || c == '-' || c == '_' || c == '.' || c == '+' - /* extra */ - || c == '!' || c == '*' || c == '\'' || c == '(' - || c == ')' || c == ',')) { - *dst++ = '%'; - snprintf(dst, 4, (c < 16 ? "0%X" : "%X"), c); - dst += 2; - goto done; - } - } - - if ((flag & VIS_GLOB) && - (c == '*' || c == '?' || c == '[' || c == '#')) - ; - else if (isgraph(c) || - ((flag & VIS_SP) == 0 && c == ' ') || - ((flag & VIS_TAB) == 0 && c == '\t') || - ((flag & VIS_NL) == 0 && c == '\n') || - ((flag & VIS_SAFE) && (c == '\b' || c == '\007' || c == '\r'))) { - *dst++ = c; - if (c == '\\' && (flag & VIS_NOSLASH) == 0) - *dst++ = '\\'; - *dst = '\0'; - return (dst); - } - - if (flag & VIS_CSTYLE) { - switch(c) { - case '\n': - *dst++ = '\\'; - *dst++ = 'n'; - goto done; - case '\r': - *dst++ = '\\'; - *dst++ = 'r'; - goto done; - case '\b': - *dst++ = '\\'; - *dst++ = 'b'; - goto done; - case '\a': - *dst++ = '\\'; - *dst++ = 'a'; - goto done; - case '\v': - *dst++ = '\\'; - *dst++ = 'v'; - goto done; - case '\t': - *dst++ = '\\'; - *dst++ = 't'; - goto done; - case '\f': - *dst++ = '\\'; - *dst++ = 'f'; - goto done; - case ' ': - *dst++ = '\\'; - *dst++ = 's'; - goto done; - case '\0': - *dst++ = '\\'; - *dst++ = '0'; - if (isoctal(nextc)) { - *dst++ = '0'; - *dst++ = '0'; - } - goto done; - } - } - if (((c & 0177) == ' ') || isgraph(c) || (flag & VIS_OCTAL)) { - *dst++ = '\\'; - *dst++ = ((u_char)c >> 6 & 07) + '0'; - *dst++ = ((u_char)c >> 3 & 07) + '0'; - *dst++ = ((u_char)c & 07) + '0'; - goto done; - } - if ((flag & VIS_NOSLASH) == 0) - *dst++ = '\\'; - if (c & 0200) { - c &= 0177; - *dst++ = 'M'; - } - if (iscntrl(c)) { - *dst++ = '^'; - if (c == 0177) - *dst++ = '?'; - else - *dst++ = c + '@'; - } else { - *dst++ = '-'; - *dst++ = c; - } -done: - *dst = '\0'; - return (dst); -} - -/* - * strvis, strvisx - visually encode characters from src into dst - * - * Dst must be 4 times the size of src to account for possible - * expansion. The length of dst, not including the trailing NUL, - * is returned. - * - * Strvisx encodes exactly len bytes from src into dst. - * This is useful for encoding a block of data. - */ -int -strvis(dst, src, flag) - char *dst; - const char *src; - int flag; -{ - char c; - char *start; - - for (start = dst; (c = *src); ) - dst = vis(dst, c, flag, *++src); - *dst = '\0'; - return (dst - start); -} - -int -strvisx(dst, src, len, flag) - char *dst; - const char *src; - size_t len; - int flag; -{ - int c; - char *start; - - for (start = dst; len > 1; len--) { - c = *src; - dst = vis(dst, c, flag, *++src); - } - if (len) - dst = vis(dst, *src, flag, '\0'); - *dst = '\0'; - - return (dst - start); -} diff --git a/cvis/vis.go b/cvis/vis.go deleted file mode 100644 index c10073d..0000000 --- a/cvis/vis.go +++ /dev/null @@ -1,28 +0,0 @@ -package cvis - -// #include "vis.h" -// #include -import "C" -import ( - "fmt" - "math" - "unsafe" -) - -// Vis is a wrapper around the C implementation -func Vis(src string, flags int) (string, error) { - // dst needs to be 4 times the length of str, must check appropriate size - if uint32(len(src)*4+1) >= math.MaxUint32/4 { - return "", fmt.Errorf("failed to encode: %q", src) - } - dst := string(make([]byte, 4*len(src)+1)) - cDst, cSrc := C.CString(dst), C.CString(src) - defer C.free(unsafe.Pointer(cDst)) - defer C.free(unsafe.Pointer(cSrc)) - C.strvis(cDst, cSrc, C.int(flags)) - - return C.GoString(cDst), nil -} - -// DefaultVisFlags are the common flags used in mtree string encoding -var DefaultVisFlags = C.VIS_WHITE | C.VIS_OCTAL | C.VIS_GLOB diff --git a/cvis/vis.h b/cvis/vis.h deleted file mode 100644 index 0798008..0000000 --- a/cvis/vis.h +++ /dev/null @@ -1,90 +0,0 @@ -/*- - * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)vis.h 8.1 (Berkeley) 6/2/93 - * $FreeBSD$ - */ - -#ifndef _VIS_H_ -#define _VIS_H_ - -#include - -/* - * to select alternate encoding format - */ -#define VIS_OCTAL 0x01 /* use octal \ddd format */ -#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */ - -/* - * to alter set of characters encoded (default is to encode all - * non-graphic except space, tab, and newline). - */ -#define VIS_SP 0x04 /* also encode space */ -#define VIS_TAB 0x08 /* also encode tab */ -#define VIS_NL 0x10 /* also encode newline */ -#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) -#define VIS_SAFE 0x20 /* only encode "unsafe" characters */ - -/* - * other - */ -#define VIS_NOSLASH 0x40 /* inhibit printing '\' */ -#define VIS_HTTPSTYLE 0x80 /* http-style escape % HEX HEX */ -#define VIS_GLOB 0x100 /* encode glob(3) magics */ - -/* - * unvis return codes - */ -#define UNVIS_VALID 1 /* character valid */ -#define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ -#define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ -#define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ -#define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ - -/* - * unvis flags - */ -#define UNVIS_END 1 /* no more characters */ - -#include - -__BEGIN_DECLS -char *vis(char *, int, int, int); -int strvis(char *, const char *, int); -int strvisx(char *, const char *, size_t, int); -int strunvis(char *, const char *); -int strunvisx(char *, const char *, int); -int unvis(char *, int, int *, int); -__END_DECLS - -#endif /* !_VIS_H_ */ diff --git a/entry.go b/entry.go index 558d1cd..adf2592 100644 --- a/entry.go +++ b/entry.go @@ -4,6 +4,8 @@ import ( "fmt" "path/filepath" "strings" + + "github.com/vbatts/go-mtree/pkg/govis" ) type byPos []Entry @@ -47,7 +49,7 @@ func (e Entry) Descend(filename string) *Entry { func (e Entry) Find(filepath string) *Entry { resultnode := &e for _, path := range strings.Split(filepath, "/") { - encoded, err := Vis(path, DefaultVisFlags) + encoded, err := govis.Vis(path, DefaultVisFlags) if err != nil { return nil } @@ -68,7 +70,7 @@ func (e Entry) Ascend() *Entry { // Path provides the full path of the file, despite RelativeType or FullType. It // will be in Unvis'd form. func (e Entry) Path() (string, error) { - decodedName, err := Unvis(e.Name) + decodedName, err := govis.Unvis(e.Name, DefaultVisFlags) if err != nil { return "", err } diff --git a/keywordfunc.go b/keywordfunc.go index c9a0f77..e88fa2e 100644 --- a/keywordfunc.go +++ b/keywordfunc.go @@ -11,6 +11,7 @@ import ( "io" "os" + "github.com/vbatts/go-mtree/pkg/govis" "golang.org/x/crypto/ripemd160" ) @@ -119,7 +120,7 @@ var ( linkKeywordFunc = func(path string, info os.FileInfo, r io.Reader) (KeyVal, error) { if sys, ok := info.Sys().(*tar.Header); ok { if sys.Linkname != "" { - linkname, err := Vis(sys.Linkname, DefaultVisFlags) + linkname, err := govis.Vis(sys.Linkname, DefaultVisFlags) if err != nil { return emptyKV, err } @@ -133,7 +134,7 @@ var ( if err != nil { return emptyKV, err } - linkname, err := Vis(str, DefaultVisFlags) + linkname, err := govis.Vis(str, DefaultVisFlags) if err != nil { return emptyKV, err } diff --git a/keywords.go b/keywords.go index b139d6d..ed121b5 100644 --- a/keywords.go +++ b/keywords.go @@ -3,8 +3,14 @@ package mtree import ( "fmt" "strings" + + "github.com/vbatts/go-mtree/pkg/govis" ) +// DefaultVisFlags is the set of Vis flags used when encoding filenames and +// other similar entries. +const DefaultVisFlags govis.VisFlag = govis.VisWhite | govis.VisOctal | govis.VisGlob + // Keyword is the string name of a keyword, with some convenience functions for // determining whether it is a default or bsd standard keyword. type Keyword string diff --git a/keywords_linux.go b/keywords_linux.go index a14108e..bab7a8d 100644 --- a/keywords_linux.go +++ b/keywords_linux.go @@ -12,6 +12,7 @@ import ( "strings" "syscall" + "github.com/vbatts/go-mtree/pkg/govis" "github.com/vbatts/go-mtree/xattr" ) @@ -62,7 +63,7 @@ var ( } klist := []KeyVal{} for k, v := range hdr.Xattrs { - encKey, err := Vis(k, DefaultVisFlags) + encKey, err := govis.Vis(k, DefaultVisFlags) if err != nil { return emptyKV, err } @@ -84,7 +85,7 @@ var ( if err != nil { return emptyKV, err } - encKey, err := Vis(xlist[i], DefaultVisFlags) + encKey, err := govis.Vis(xlist[i], DefaultVisFlags) if err != nil { return emptyKV, err } diff --git a/tar.go b/tar.go index 800a484..7f7318e 100644 --- a/tar.go +++ b/tar.go @@ -9,6 +9,8 @@ import ( "os" "path/filepath" "strings" + + "github.com/vbatts/go-mtree/pkg/govis" ) // Streamer creates a file hierarchy out of a tar stream @@ -128,7 +130,7 @@ hdrloop: return } // Alright, it's either file or directory - encodedName, err := Vis(filepath.Base(hdr.Name), DefaultVisFlags) + encodedName, err := govis.Vis(filepath.Base(hdr.Name), DefaultVisFlags) if err != nil { tmpFile.Close() os.Remove(tmpFile.Name()) @@ -148,7 +150,7 @@ hdrloop: log.Println(err) break } - linkname, err := Unvis(KeyVal(kv).Value()) + linkname, err := govis.Unvis(KeyVal(kv).Value(), DefaultVisFlags) if err != nil { log.Println(err) break @@ -248,7 +250,7 @@ func populateTree(root, e *Entry, hdr *tar.Header) error { dirNames := strings.Split(wd, "/") parent := root for _, name := range dirNames[:] { - encoded, err := Vis(name, DefaultVisFlags) + encoded, err := govis.Vis(name, DefaultVisFlags) if err != nil { return err } diff --git a/unvis.go b/unvis.go deleted file mode 100644 index 7c63666..0000000 --- a/unvis.go +++ /dev/null @@ -1,7 +0,0 @@ -package mtree - -// Unvis is a wrapper for the C implementation of unvis, which decodes a string -// that potentially has characters that are encoded with Vis -func Unvis(src string) (string, error) { - return unvis(src) -} diff --git a/unvis_c.go b/unvis_c.go deleted file mode 100644 index ac421f1..0000000 --- a/unvis_c.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build cgo,cvis - -package mtree - -import ( - "github.com/vbatts/go-mtree/cvis" -) - -func unvis(src string) (string, error) { - return cvis.Unvis(src) -} diff --git a/unvis_go.go b/unvis_go.go deleted file mode 100644 index 0f2a848..0000000 --- a/unvis_go.go +++ /dev/null @@ -1,233 +0,0 @@ -// +build !cvis - -package mtree - -import "unicode" - -func unvis(src string) (string, error) { - dst := []rune{} - var s state - for i, r := range src { - again: - err := unvisRune(&dst, r, &s, 0) - switch err { - case unvisValid: - break - case unvisValidPush: - goto again - case unvisNone: - fallthrough - case unvisNochar: - break - default: - return "", err - } - if i == len(src)-1 { - unvisRune(&dst, r, &s, unvisEnd) - } - } - - str := "" - for _, ch := range dst { - str += string(ch) - } - return str, nil -} - -func unvisRune(dst *[]rune, r rune, s *state, flags VisFlag) error { - if (flags & unvisEnd) != 0 { - if *s == stateOctal2 || *s == stateOctal3 { - *s = stateGround - return unvisValid - } - if *s == stateGround { - return unvisNochar - } - return unvisErrSynbad - } - - switch *s & ^stateHTTP { - case stateGround: - if r == '\\' { - *s = stateStart - return unvisNone - } - if flags&VisHttpstyle != 0 && r == '%' { - *s = stateStart | stateHTTP - return unvisNone - } - *dst = append(*dst, r) - return unvisValid - case stateStart: - if *s&stateHTTP != 0 && ishex(unicode.ToLower(r)) { - if unicode.IsNumber(r) { - *dst = append(*dst, r-'0') - } else { - *dst = append(*dst, unicode.ToLower(r)-'a') - } - *s = stateHex2 - return unvisNone - } - switch r { - case '\\': - *s = stateGround - *dst = append(*dst, r) - return unvisValid - case '0': - fallthrough - case '1': - fallthrough - case '2': - fallthrough - case '3': - fallthrough - case '4': - fallthrough - case '5': - fallthrough - case '6': - fallthrough - case '7': - *s = stateOctal2 - *dst = append(*dst, r-'0') - return unvisNone - case 'M': - *s = stateMeta - *dst = append(*dst, rune(0200)) - return unvisNone - case '^': - *s = stateCtrl - return unvisNone - case 'n': - *s = stateGround - *dst = append(*dst, '\n') - return unvisValid - case 'r': - *s = stateGround - *dst = append(*dst, '\r') - return unvisValid - case 'b': - *s = stateGround - *dst = append(*dst, '\b') - return unvisValid - case 'a': - *s = stateGround - *dst = append(*dst, '\007') - return unvisValid - case 'v': - *s = stateGround - *dst = append(*dst, '\v') - return unvisValid - case 't': - *s = stateGround - *dst = append(*dst, '\t') - return unvisValid - case 'f': - *s = stateGround - *dst = append(*dst, '\f') - return unvisValid - case 's': - *s = stateGround - *dst = append(*dst, ' ') - return unvisValid - case 'E': - *s = stateGround - *dst = append(*dst, '\033') - return unvisValid - case '\n': - // hidden newline - *s = stateGround - return unvisNochar - case '$': - // hidden marker - *s = stateGround - return unvisNochar - } - *s = stateGround - return unvisErrSynbad - case stateMeta: - if r == '-' { - *s = stateMeta1 - } else if r == '^' { - *s = stateCtrl - } else { - *s = stateGround - return unvisErrSynbad - } - return unvisNone - case stateMeta1: - *s = stateGround - dp := *dst - dp[len(dp)-1] |= r - return unvisValid - case stateCtrl: - dp := *dst - if r == '?' { - dp[len(dp)-1] |= rune(0177) - } else { - dp[len(dp)-1] |= r & 037 - } - *s = stateGround - return unvisValid - case stateOctal2: - if isoctal(r) { - dp := *dst - if len(dp) > 0 { - last := dp[len(dp)-1] - dp[len(dp)-1] = (last << 3) + (r - '0') - } else { - dp = append(dp, (0<<3)+(r-'0')) - } - *s = stateOctal3 - return unvisNone - } - *s = stateGround - return unvisValidPush - case stateOctal3: - *s = stateGround - if isoctal(r) { - dp := *dst - if len(dp) > 0 { - last := dp[len(dp)-1] - dp[len(dp)-1] = (last << 3) + (r - '0') - } else { - dp = append(dp, (0<<3)+(r-'0')) - } - return unvisValid - } - return unvisValidPush - case stateHex2: - if ishex(unicode.ToLower(r)) { - last := rune(0) - dp := *dst - if len(dp) > 0 { - last = dp[len(dp)-1] - } - if unicode.IsNumber(r) { - dp = append(dp, (last<<4)+(r-'0')) - } else { - dp = append(dp, (last<<4)+(unicode.ToLower(r)-'a'+10)) - } - } - *s = stateGround - return unvisValid - } - - *s = stateGround - return unvisErrSynbad -} - -type state int - -const ( - stateGround state = iota /* haven't seen escape char */ - stateStart /* start decoding special sequence */ - stateMeta /* metachar started (M) */ - stateMeta1 /* metachar more, regular char (-) */ - stateCtrl /* control char started (^) */ - stateOctal2 /* octal digit 2 */ - stateOctal3 /* octal digit 3 */ - stateHex2 /* hex digit 2 */ - - stateHTTP state = 0x080 /* %HEXHEX escape */ -) diff --git a/unvis_go_test.go b/unvis_go_test.go deleted file mode 100644 index 0289330..0000000 --- a/unvis_go_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package mtree - -import "testing" - -type runeCheck func(rune) bool - -func TestUnvisHelpers(t *testing.T) { - testset := []struct { - R rune - Check runeCheck - Expect bool - }{ - {'a', ishex, true}, - {'A', ishex, true}, - {'z', ishex, false}, - {'Z', ishex, false}, - {'G', ishex, false}, - {'1', ishex, true}, - {'0', ishex, true}, - {'9', ishex, true}, - {'0', isoctal, true}, - {'3', isoctal, true}, - {'7', isoctal, true}, - {'9', isoctal, false}, - {'a', isoctal, false}, - {'z', isoctal, false}, - {'3', isalnum, true}, - {'a', isalnum, true}, - {';', isalnum, false}, - {'!', isalnum, false}, - {' ', isalnum, false}, - {'3', isgraph, true}, - {'a', isgraph, true}, - {';', isgraph, true}, - {'!', isgraph, true}, - {' ', isgraph, false}, - } - - for i, ts := range testset { - got := ts.Check(ts.R) - if got != ts.Expect { - t.Errorf("%d: %q expected: %t; got %t", i, string(ts.R), ts.Expect, got) - } - } -} - -func TestUnvisUnicode(t *testing.T) { - // Ensure that unicode strings are not messed up by Unvis. - for _, test := range []string{ - "", - "this.is.a.normal_string", - "AC_Raíz_Certicámara_S.A..pem", - "NetLock_Arany_=Class_Gold=_Főtanúsítvány.pem", - "TÜBİTAK_UEKAE_Kök_Sertifika_Hizmet_Sağlayıcısı_-_Sürüm_3.pem", - } { - got, err := Unvis(test) - if err != nil { - t.Errorf("unexpected error doing unvis(%q): %s", test, err) - continue - } - if got != test { - t.Errorf("expected %q to be unchanged, got %q", test, got) - } - } -} - -func TestVisUnvis(t *testing.T) { - // Round-trip testing. - for _, test := range []string{ - "", - "this.is.a.normal_string", - "AC_Raíz_Certicámara_S.A..pem", - "NetLock_Arany_=Class_Gold=_Főtanúsítvány.pem", - "TÜBİTAK_UEKAE_Kök_Sertifika_Hizmet_Sağlayıcısı_-_Sürüm_3.pem", - "hello world [ this string needs=enco ding! ]", - "even \n more encoding necessary\a\a ", - "\024 <-- some more weird characters --> 你好,世界", - } { - enc, err := Vis(test, DefaultVisFlags) - if err != nil { - t.Errorf("unexpected error doing vis(%q): %s", test, err) - continue - } - dec, err := Unvis(enc) - if err != nil { - t.Errorf("unexpected error doing unvis(%q): %s", enc, err) - continue - } - if dec != test { - t.Errorf("roundtrip failed: unvis(vis(%q) = %q) = %q", test, enc, dec) - } - } -} diff --git a/vis.go b/vis.go deleted file mode 100644 index 440283c..0000000 --- a/vis.go +++ /dev/null @@ -1,89 +0,0 @@ -package mtree - -import "unicode" - -// Vis is a wrapper of the C implementation of the function vis, which encodes -// a character with a particular format/style. -// For most use-cases use DefaultVisFlags. -func Vis(src string, flags VisFlag) (string, error) { - return vis(src, flags) -} - -// DefaultVisFlags are the typical flags used for encoding strings in mtree -// manifests. -var DefaultVisFlags = VisWhite | VisOctal | VisGlob - -// VisFlag sets the extent of charactures to be encoded -type VisFlag int - -// flags for encoding -const ( - // to select alternate encoding format - VisOctal VisFlag = 0x01 // use octal \ddd format - VisCstyle VisFlag = 0x02 // use \[nrft0..] where appropriate - - // to alter set of characters encoded (default is to encode all non-graphic - // except space, tab, and newline). - VisSp VisFlag = 0x04 // also encode space - VisTab VisFlag = 0x08 // also encode tab - VisNl VisFlag = 0x10 // also encode newline - VisWhite VisFlag = (VisSp | VisTab | VisNl) - VisSafe VisFlag = 0x20 // only encode "unsafe" characters - - // other - VisNoSlash VisFlag = 0x40 // inhibit printing '\' - VisHttpstyle VisFlag = 0x80 // http-style escape % HEX HEX - VisGlob VisFlag = 0x100 // encode glob(3) magics - -) - -// errors used in the tokenized decoding strings -const ( - // unvis return codes - unvisValid unvisErr = 1 // character valid - unvisValidPush unvisErr = 2 // character valid, push back passed char - unvisNochar unvisErr = 3 // valid sequence, no character produced - unvisErrSynbad unvisErr = -1 // unrecognized escape sequence - unvisErrUnrecoverable unvisErr = -2 // decoder in unknown state (unrecoverable) - unvisNone unvisErr = 0 - - // unvisEnd means there are no more characters - unvisEnd VisFlag = 1 // no more characters -) - -// unvisErr are the return conditions for Unvis -type unvisErr int - -func (ue unvisErr) Error() string { - switch ue { - case unvisValid: - return "character valid" - case unvisValidPush: - return "character valid, push back passed char" - case unvisNochar: - return "valid sequence, no character produced" - case unvisErrSynbad: - return "unrecognized escape sequence" - case unvisErrUnrecoverable: - return "decoder in unknown state (unrecoverable)" - } - return "Unknown Error" -} - -func ishex(r rune) bool { - lr := unicode.ToLower(r) - return (lr >= '0' && lr <= '9') || (lr >= 'a' && lr <= 'f') -} - -func isoctal(r rune) bool { - return r <= '7' && r >= '0' -} - -// the ctype isgraph is "any printable character except space" -func isgraph(r rune) bool { - return unicode.IsPrint(r) && !unicode.IsSpace(r) -} - -func isalnum(r rune) bool { - return unicode.IsNumber(r) || unicode.IsLetter(r) -} diff --git a/vis_c.go b/vis_c.go deleted file mode 100644 index 5024356..0000000 --- a/vis_c.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build cgo,cvis - -package mtree - -import ( - "github.com/vbatts/go-mtree/cvis" -) - -func vis(src string, flags VisFlag) (string, error) { - return cvis.Vis(src, int(flags)) -} diff --git a/vis_go.go b/vis_go.go deleted file mode 100644 index a914693..0000000 --- a/vis_go.go +++ /dev/null @@ -1,107 +0,0 @@ -// +build !cvis - -package mtree - -import ( - "fmt" - "unicode" -) - -func vis(src string, flags VisFlag) (string, error) { - var ret string - for _, r := range src { - vStr, err := visRune(r, flags) - if err != nil { - return "", err - } - ret = ret + vStr - } - return ret, nil -} - -func visRune(r rune, flags VisFlag) (string, error) { - if flags&VisHttpstyle != 0 { - // Described in RFC 1808 - if !isalnum(r) || - /* safe */ - r == '$' || r == '-' || r == '_' || r == '.' || r == '+' || - /* extra */ - r == '!' || r == '*' || r == '\'' || r == '(' || - r == ')' || r == ',' { - if r < 16 { - return fmt.Sprintf("%%0%X", r), nil - } - return fmt.Sprintf("%%%X", r), nil - } - } - - if (flags&VisGlob) != 0 && (r == '*' || r == '?' || r == '[' || r == '#') { - // ... ? - } else if isgraph(r) || - ((flags&VisSp) == 0 && r == ' ') || - ((flags&VisTab) == 0 && r == '\t') || - ((flags&VisNl) == 0 && r == '\n') || - ((flags&VisSafe) != 0 && (r == '\b' || r == '\007' || r == '\r')) { - if r == '\\' && (flags&VisNoSlash) == 0 { - return fmt.Sprintf("%s\\", string(r)), nil - } - return string(r), nil - } - - if (flags & VisCstyle) != 0 { - switch r { - case '\n': - return "\\n", nil - case '\r': - return "\\r", nil - case '\b': - return "\\b", nil - case '\a': - return "\\a", nil - case '\v': - return "\\v", nil - case '\t': - return "\\t", nil - case '\f': - return "\\f", nil - case ' ': - return "\\s", nil - case rune(0x0): - return "\\0", nil - /* - if isoctal(nextr) { - dst = append(dst, '0') - dst = append(dst, '0') - } - */ - } - } - if ((r & 0177) == ' ') || isgraph(r) || (flags&VisOctal) != 0 { - dst := make([]rune, 4) - dst[0] = '\\' - dst[1] = (r >> 6 & 07) + '0' - dst[2] = (r >> 3 & 07) + '0' - dst[3] = (r & 07) + '0' - return string(dst), nil - } - var dst []rune - if (flags & VisNoSlash) == 0 { - dst = append(dst, '\\') - } - if (r & 0200) != 0 { - r &= 0177 - dst = append(dst, 'M') - } - if unicode.IsControl(r) { - dst = append(dst, '^') - if r == 0177 { - dst = append(dst, '?') - } else { - dst = append(dst, r+'@') - } - } else { - dst = append(dst, '-') - dst = append(dst, r) - } - return string(dst), nil -} diff --git a/vis_test.go b/vis_test.go deleted file mode 100644 index 03aba51..0000000 --- a/vis_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package mtree - -import "testing" - -func TestVisBasic(t *testing.T) { - testset := []struct { - Src, Dest string - }{ - {"[", "\\133"}, - {" ", "\\040"}, - {" ", "\\011"}, - {"dir with space", "dir\\040with\\040space"}, - {"consec spaces", "consec\\040\\040\\040spaces"}, - {"trailingsymbol[", "trailingsymbol\\133"}, - {" [ leadingsymbols", "\\040\\133\\040leadingsymbols"}, - {"no_need_for_encoding", "no_need_for_encoding"}, - } - - for i := range testset { - got, err := Vis(testset[i].Src, DefaultVisFlags) - if err != nil { - t.Errorf("working with %q: %s", testset[i].Src, err) - } - if got != testset[i].Dest { - t.Errorf("%q: expected %#v; got %#v", testset[i].Src, testset[i].Dest, got) - continue - } - - got, err = Unvis(got) - if err != nil { - t.Errorf("working with %q: %s: %q", testset[i].Src, err, got) - continue - } - if got != testset[i].Src { - t.Errorf("%q: expected %#v; got %#v", testset[i].Dest, testset[i].Src, got) - continue - } - } -} diff --git a/walk.go b/walk.go index b1dfe87..3af4110 100644 --- a/walk.go +++ b/walk.go @@ -9,6 +9,8 @@ import ( "sort" "strings" "time" + + "github.com/vbatts/go-mtree/pkg/govis" ) // ExcludeFunc is the type of function called on each path walked to determine @@ -165,7 +167,7 @@ func Walk(root string, excludes []ExcludeFunc, keywords []Keyword, fsEval FsEval } } } - encodedEntryName, err := Vis(entryPathName, DefaultVisFlags) + encodedEntryName, err := govis.Vis(entryPathName, DefaultVisFlags) if err != nil { return err }