1
- // Copyright 2018 The Go Authors. All rights reserved.
2
1
// Use of this source code is governed by a BSD-style
3
2
// license that can be found in the LICENSE file.
4
3
@@ -34,14 +33,15 @@ package txtar
34
33
import (
35
34
"bytes"
36
35
"fmt"
37
- "io/ioutil "
36
+ "os "
38
37
"strings"
39
38
)
40
39
41
40
// An Archive is a collection of files.
42
41
type Archive struct {
43
42
Comment []byte
44
43
Files []File
44
+ UseCRLF bool
45
45
}
46
46
47
47
// A File is a single file in an archive.
@@ -55,18 +55,22 @@ type File struct {
55
55
// a.Comment and all a.File[i].Data contain no file marker lines,
56
56
// and all a.File[i].Name is non-empty.
57
57
func Format (a * Archive ) []byte {
58
+ lineSeparator := lf
59
+ if a .UseCRLF {
60
+ lineSeparator = crlf
61
+ }
58
62
var buf bytes.Buffer
59
- buf .Write (fixNL (a .Comment ))
63
+ buf .Write (fixNL (a .Comment , a . UseCRLF ))
60
64
for _ , f := range a .Files {
61
- fmt .Fprintf (& buf , "-- %s --\n " , f .Name )
62
- buf .Write (fixNL (f .Data ))
65
+ fmt .Fprintf (& buf , "-- %s --%s " , f .Name , lineSeparator )
66
+ buf .Write (fixNL (f .Data , a . UseCRLF ))
63
67
}
64
68
return buf .Bytes ()
65
69
}
66
70
67
71
// ParseFile parses the named file as an archive.
68
72
func ParseFile (file string ) (* Archive , error ) {
69
- data , err := ioutil .ReadFile (file )
73
+ data , err := os .ReadFile (file )
70
74
if err != nil {
71
75
return nil , err
72
76
}
@@ -77,17 +81,23 @@ func ParseFile(file string) (*Archive, error) {
77
81
// The returned Archive holds slices of data.
78
82
func Parse (data []byte ) * Archive {
79
83
a := new (Archive )
84
+ i := bytes .IndexByte (data , '\n' )
85
+ if i > 0 && data [i - 1 ] == '\r' {
86
+ a .UseCRLF = true
87
+ }
80
88
var name string
81
- a .Comment , name , data = findFileMarker (data )
89
+ a .Comment , name , data = findFileMarker (data , a . UseCRLF )
82
90
for name != "" {
83
91
f := File {name , nil }
84
- f .Data , name , data = findFileMarker (data )
92
+ f .Data , name , data = findFileMarker (data , a . UseCRLF )
85
93
a .Files = append (a .Files , f )
86
94
}
87
95
return a
88
96
}
89
97
90
98
var (
99
+ crlf = []byte ("\r \n " )
100
+ lf = []byte ("\n " )
91
101
newlineMarker = []byte ("\n -- " )
92
102
marker = []byte ("-- " )
93
103
markerEnd = []byte (" --" )
@@ -97,15 +107,15 @@ var (
97
107
// extracts the file name, and returns the data before the marker,
98
108
// the file name, and the data after the marker.
99
109
// If there is no next marker, findFileMarker returns before = fixNL(data), name = "", after = nil.
100
- func findFileMarker (data []byte ) (before []byte , name string , after []byte ) {
110
+ func findFileMarker (data []byte , useCRLF bool ) (before []byte , name string , after []byte ) {
101
111
var i int
102
112
for {
103
- if name , after = isMarker (data [i :]); name != "" {
113
+ if name , after = isMarker (data [i :], useCRLF ); name != "" {
104
114
return data [:i ], name , after
105
115
}
106
116
j := bytes .Index (data [i :], newlineMarker )
107
117
if j < 0 {
108
- return fixNL (data ), "" , nil
118
+ return fixNL (data , useCRLF ), "" , nil
109
119
}
110
120
i += j + 1 // positioned at start of new possible marker
111
121
}
@@ -114,27 +124,35 @@ func findFileMarker(data []byte) (before []byte, name string, after []byte) {
114
124
// isMarker checks whether data begins with a file marker line.
115
125
// If so, it returns the name from the line and the data after the line.
116
126
// Otherwise it returns name == "" with an unspecified after.
117
- func isMarker (data []byte ) (name string , after []byte ) {
127
+ func isMarker (data []byte , useCRLF bool ) (name string , after []byte ) {
118
128
if ! bytes .HasPrefix (data , marker ) {
119
129
return "" , nil
120
130
}
121
131
if i := bytes .IndexByte (data , '\n' ); i >= 0 {
122
- data , after = data [:i ], data [i + 1 :]
132
+ if useCRLF && len (data ) > 0 && data [i - 1 ] == '\r' {
133
+ data , after = data [:i - 1 ], data [i + 1 :]
134
+ } else {
135
+ data , after = data [:i ], data [i + 1 :]
136
+ }
123
137
}
124
138
if ! (bytes .HasSuffix (data , markerEnd ) && len (data ) >= len (marker )+ len (markerEnd )) {
125
139
return "" , nil
126
140
}
127
141
return strings .TrimSpace (string (data [len (marker ) : len (data )- len (markerEnd )])), after
128
142
}
129
143
130
- // If data is empty or ends in \n, fixNL returns data.
131
- // Otherwise fixNL returns a new slice consisting of data with a final \n added.
132
- func fixNL (data []byte ) []byte {
133
- if len (data ) == 0 || data [len (data )- 1 ] == '\n' {
144
+ // If data is empty or ends in lineSeparator, fixNL returns data.
145
+ // Otherwise fixNL returns a new slice consisting of data with a final lineSeparator added.
146
+ func fixNL (data []byte , useCRLF bool ) []byte {
147
+ lineSeparator := lf
148
+ if useCRLF {
149
+ lineSeparator = crlf
150
+ }
151
+ if len (data ) == 0 || (len (data ) >= len (lineSeparator ) && bytes .Equal (data [len (data )- len (lineSeparator ):], lineSeparator )) {
134
152
return data
135
153
}
136
- d := make ([]byte , len (data )+ 1 )
154
+ d := make ([]byte , len (data )+ len ( lineSeparator ) )
137
155
copy (d , data )
138
- d [ len (data )] = '\n'
156
+ d = append ( d [: len (d ) - len ( lineSeparator )], lineSeparator ... )
139
157
return d
140
158
}
0 commit comments