1 // Package dos2unix provides functions to convert between dos and unix line 2 // termination styles 3 package dos2unix // import "vimagination.zapto.org/dos2unix" 4 5 import ( 6 "io" 7 "slices" 8 ) 9 10 type byteReader struct { 11 io.Reader 12 buf [1]byte 13 } 14 15 func (b *byteReader) ReadByte() (byte, error) { 16 _, err := io.ReadFull(b.Reader, b.buf[:]) 17 return b.buf[0], err 18 } 19 20 type dos2unix struct { 21 r io.Reader 22 b bool 23 char [1]byte 24 } 25 26 func (d *dos2unix) Read(b []byte) (int, error) { 27 if len(b) == 0 { 28 return 0, nil 29 } 30 31 var n int 32 33 s := b 34 35 if d.b { 36 b[0] = d.char[0] 37 s = b[1:] 38 n = 1 39 40 if d.b = len(s) == 0 && b[0] == '\r'; d.b { 41 _, err := d.r.Read(d.char[:]) 42 if d.char[0] == '\n' { 43 b[0] = '\n' 44 d.b = false 45 } 46 47 return n, err 48 } 49 } 50 51 m, err := d.r.Read(s) 52 n += m 53 54 lastIsCR := false 55 56 for i := 0; i < n; i++ { 57 c := b[i] 58 if lastIsCR && c == '\n' { 59 b = slices.Delete(b, i-1, i) 60 n-- 61 i-- 62 } 63 64 lastIsCR = c == '\r' 65 } 66 67 if lastIsCR && err == nil { 68 n-- 69 d.char[0] = '\r' 70 } 71 72 d.b = lastIsCR 73 74 return n, err 75 } 76 77 // DOS2Unix wraps a byte reader with a reader that replaces all instances of 78 // \r\n with \n 79 func DOS2Unix(r io.Reader) io.Reader { 80 return &dos2unix{r: r} 81 } 82 83 type unix2dos struct { 84 r io.ByteReader 85 lf bool 86 } 87 88 func (u *unix2dos) Read(b []byte) (int, error) { 89 var n int 90 for len(b) > 0 { 91 if u.lf { 92 b[0] = '\n' 93 u.lf = false 94 b = b[1:] 95 n++ 96 continue 97 } 98 c, err := u.r.ReadByte() 99 if err != nil { 100 return n, err 101 } 102 if c == '\n' { 103 u.lf = true 104 c = '\r' 105 } 106 b[0] = c 107 b = b[1:] 108 n++ 109 } 110 return n, nil 111 } 112 113 // Unix2DOS wraps a byte reader with a reader that replaces all instances of \n 114 // with \r\n. 115 // 116 // When reading from a non-buffered input, it is recommended to wrap the Reader 117 // with a bufio.Reader. 118 func Unix2DOS(r io.Reader) io.Reader { 119 br, ok := r.(io.ByteReader) 120 if !ok { 121 br = &byteReader{Reader: r} 122 } 123 return &unix2dos{r: br} 124 } 125