1
0
Fork 0
mirror of https://github.com/vbatts/go-mtree.git synced 2024-12-22 05:46:30 +00:00

*: switch everything to govis

Now that we have govis, move everything to using govis.{Vis,Unvis} and
then remove the cvis build tags (because that code no longer exists).

Signed-off-by: Aleksa Sarai <asarai@suse.de>
This commit is contained in:
Aleksa Sarai 2017-02-16 03:10:30 +11:00
parent 91d7ec8c89
commit c5ec1c9f3a
No known key found for this signature in database
GPG key ID: 9E18AA267DDB8DB4
21 changed files with 26 additions and 1252 deletions

View file

@ -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

View file

@ -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
}
}

View file

@ -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 <sys/cdefs.h>
#include <sys/types.h>
#include <ctype.h>
#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);
}

View file

@ -1,22 +0,0 @@
package cvis
// #include "vis.h"
// #include <stdlib.h>
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
}

View file

@ -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 <sys/cdefs.h>
#include <sys/types.h>
#include <limits.h>
#include <ctype.h>
#include <stdio.h>
#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);
}

View file

@ -1,28 +0,0 @@
package cvis
// #include "vis.h"
// #include <stdlib.h>
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

View file

@ -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 <sys/types.h>
/*
* 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 <sys/cdefs.h>
__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_ */

View file

@ -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
}

View file

@ -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
}

View file

@ -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

View file

@ -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
}

8
tar.go
View file

@ -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
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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 */
)

View file

@ -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)
}
}
}

89
vis.go
View file

@ -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)
}

View file

@ -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))
}

107
vis_go.go
View file

@ -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
}

View file

@ -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
}
}
}

View file

@ -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
}