github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/stdlib/io/read.go (about) 1 package io 2 3 import ( 4 "io" 5 "io/ioutil" 6 7 "github.com/hirochachacha/plua/internal/file" 8 "github.com/hirochachacha/plua/object" 9 "github.com/hirochachacha/plua/object/fnutil" 10 ) 11 12 func _read(th object.Thread, args []object.Value, f file.File, off int, doClose bool, raiseError bool) ([]object.Value, *object.RuntimeError) { 13 if f.IsClosed() { 14 return nil, object.NewRuntimeError("file is already closed") 15 } 16 17 ap := fnutil.NewArgParser(th, args) 18 19 if len(args) == off { 20 line, err := readStrippedLine(f) 21 if err != nil { 22 if doClose { 23 f.Close() 24 } 25 if err != io.EOF { 26 if raiseError { 27 return nil, object.NewRuntimeError(err.Error()) 28 } 29 return fileResult(th, err) 30 } 31 return []object.Value{nil}, nil 32 } 33 return []object.Value{line}, nil 34 } 35 36 rets := make([]object.Value, 0, len(args)-off) 37 38 for i := range args[off:] { 39 if i64, err := ap.ToGoInt64(i + off); err == nil { 40 s, err := readCount(f, i64) 41 if err != nil { 42 if err == io.EOF { 43 if len(rets) == 0 { 44 if doClose { 45 f.Close() 46 } 47 rets = append(rets, nil) 48 } 49 return rets, nil 50 } 51 if doClose { 52 f.Close() 53 } 54 if raiseError { 55 return nil, object.NewRuntimeError(err.Error()) 56 } 57 return fileResult(th, err) 58 } 59 60 rets = append(rets, s) 61 62 continue 63 } 64 65 fmt, err := ap.ToGoString(i + off) 66 if err != nil { 67 return nil, ap.TypeError(i+off, "string or integer") 68 } 69 70 if len(fmt) > 0 && fmt[0] == '*' { 71 fmt = fmt[1:] 72 } 73 74 var val object.Value 75 var e error 76 77 switch fmt { 78 case "n": 79 val, e = readNumber(f) 80 case "a", "all": 81 val, e = readAll(f) 82 case "l": 83 val, e = readStrippedLine(f) 84 case "L": 85 val, e = readLine(f) 86 default: 87 return nil, ap.ArgError(i+off, "invalid format") 88 } 89 90 if e != nil { 91 if e == io.EOF { 92 if len(rets) == 0 { 93 if doClose { 94 f.Close() 95 } 96 rets = append(rets, nil) 97 } 98 return rets, nil 99 } 100 if doClose { 101 f.Close() 102 } 103 if raiseError { 104 return nil, object.NewRuntimeError(e.Error()) 105 } 106 return fileResult(th, e) 107 } 108 109 rets = append(rets, val) 110 } 111 112 return rets, nil 113 } 114 115 func readNumber(f file.File) (val object.Value, err error) { 116 return newScanner(f).scanNumber() 117 } 118 119 func readAll(f file.File) (val object.Value, err error) { 120 bs, err := ioutil.ReadAll(f) 121 if err != nil { 122 return nil, err 123 } 124 125 return object.String(bs), nil 126 } 127 128 func readStrippedLine(f file.File) (s object.Value, err error) { 129 line, err := f.ReadBytes('\n') 130 if err != nil { 131 if err == io.EOF { 132 if len(line) == 0 { 133 return nil, io.EOF 134 } 135 return object.String(line), nil 136 } 137 return nil, err 138 } 139 140 if len(line) == 0 { 141 return nil, io.EOF 142 } 143 144 return object.String(line[:len(line)-1]), nil 145 } 146 147 func readLine(f file.File) (s object.Value, err error) { 148 line, err := f.ReadBytes('\n') 149 if err != nil { 150 if err == io.EOF { 151 if len(line) == 0 { 152 return nil, io.EOF 153 } 154 return object.String(line), nil 155 } 156 return nil, err 157 } 158 159 if len(line) == 0 { 160 return nil, io.EOF 161 } 162 163 return object.String(line), nil 164 } 165 166 func readCount(f file.File, i int64) (s object.Value, err error) { 167 if i == 0 { 168 _, err := f.ReadByte() 169 if err != nil { 170 return nil, err 171 } 172 173 f.UnreadByte() 174 175 return object.String(""), nil 176 } 177 178 bs := make([]byte, i) 179 180 var n int 181 for { 182 var m int 183 m, err = f.Read(bs[n:]) 184 n += m 185 if err != nil { 186 break 187 } 188 if i == int64(n) { 189 break 190 } 191 } 192 193 return object.String(bs[:n]), err 194 }