iolib.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  1. package lua
  2. import (
  3. "bufio"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "os"
  8. "os/exec"
  9. "syscall"
  10. )
  11. var ioFuncs = map[string]LGFunction{
  12. "close": ioClose,
  13. "flush": ioFlush,
  14. "lines": ioLines,
  15. "input": ioInput,
  16. "output": ioOutput,
  17. "open": ioOpenFile,
  18. "popen": ioPopen,
  19. "read": ioRead,
  20. "type": ioType,
  21. "tmpfile": ioTmpFile,
  22. "write": ioWrite,
  23. }
  24. const lFileClass = "FILE*"
  25. type lFile struct {
  26. fp *os.File
  27. pp *exec.Cmd
  28. writer io.Writer
  29. reader *bufio.Reader
  30. stdout io.ReadCloser
  31. closed bool
  32. }
  33. type lFileType int
  34. const (
  35. lFileFile lFileType = iota
  36. lFileProcess
  37. )
  38. const fileDefOutIndex = 1
  39. const fileDefInIndex = 2
  40. const fileDefaultWriteBuffer = 4096
  41. const fileDefaultReadBuffer = 4096
  42. func checkFile(L *LState) *lFile {
  43. ud := L.CheckUserData(1)
  44. if file, ok := ud.Value.(*lFile); ok {
  45. return file
  46. }
  47. L.ArgError(1, "file expected")
  48. return nil
  49. }
  50. func errorIfFileIsClosed(L *LState, file *lFile) {
  51. if file.closed {
  52. L.ArgError(1, "file is closed")
  53. }
  54. }
  55. func newFile(L *LState, file *os.File, path string, flag int, perm os.FileMode, writable, readable bool) (*LUserData, error) {
  56. ud := L.NewUserData()
  57. var err error
  58. if file == nil {
  59. file, err = os.OpenFile(path, flag, perm)
  60. if err != nil {
  61. return nil, err
  62. }
  63. }
  64. lfile := &lFile{fp: file, pp: nil, writer: nil, reader: nil, stdout: nil, closed: false}
  65. ud.Value = lfile
  66. if writable {
  67. lfile.writer = file
  68. }
  69. if readable {
  70. lfile.reader = bufio.NewReaderSize(file, fileDefaultReadBuffer)
  71. }
  72. L.SetMetatable(ud, L.GetTypeMetatable(lFileClass))
  73. return ud, nil
  74. }
  75. func newProcess(L *LState, cmd string, writable, readable bool) (*LUserData, error) {
  76. ud := L.NewUserData()
  77. c, args := popenArgs(cmd)
  78. pp := exec.Command(c, args...)
  79. lfile := &lFile{fp: nil, pp: pp, writer: nil, reader: nil, stdout: nil, closed: false}
  80. ud.Value = lfile
  81. var err error
  82. if writable {
  83. lfile.writer, err = pp.StdinPipe()
  84. }
  85. if readable {
  86. lfile.stdout, err = pp.StdoutPipe()
  87. lfile.reader = bufio.NewReaderSize(lfile.stdout, fileDefaultReadBuffer)
  88. }
  89. if err != nil {
  90. return nil, err
  91. }
  92. err = pp.Start()
  93. if err != nil {
  94. return nil, err
  95. }
  96. L.SetMetatable(ud, L.GetTypeMetatable(lFileClass))
  97. return ud, nil
  98. }
  99. func (file *lFile) Type() lFileType {
  100. if file.fp == nil {
  101. return lFileProcess
  102. }
  103. return lFileFile
  104. }
  105. func (file *lFile) Name() string {
  106. switch file.Type() {
  107. case lFileFile:
  108. return fmt.Sprintf("file %s", file.fp.Name())
  109. case lFileProcess:
  110. return fmt.Sprintf("process %s", file.pp.Path)
  111. }
  112. return ""
  113. }
  114. func (file *lFile) AbandonReadBuffer() error {
  115. if file.Type() == lFileFile && file.reader != nil {
  116. _, err := file.fp.Seek(-int64(file.reader.Buffered()), 1)
  117. if err != nil {
  118. return err
  119. }
  120. file.reader = bufio.NewReaderSize(file.fp, fileDefaultReadBuffer)
  121. }
  122. return nil
  123. }
  124. func fileDefOut(L *LState) *LUserData {
  125. return L.Get(UpvalueIndex(1)).(*LTable).RawGetInt(fileDefOutIndex).(*LUserData)
  126. }
  127. func fileDefIn(L *LState) *LUserData {
  128. return L.Get(UpvalueIndex(1)).(*LTable).RawGetInt(fileDefInIndex).(*LUserData)
  129. }
  130. func fileIsWritable(L *LState, file *lFile) int {
  131. if file.writer == nil {
  132. L.Push(LNil)
  133. L.Push(LString(fmt.Sprintf("%s is opened for only reading.", file.Name())))
  134. L.Push(LNumber(1)) // C-Lua compatibility: Original Lua pushes errno to the stack
  135. return 3
  136. }
  137. return 0
  138. }
  139. func fileIsReadable(L *LState, file *lFile) int {
  140. if file.reader == nil {
  141. L.Push(LNil)
  142. L.Push(LString(fmt.Sprintf("%s is opened for only writing.", file.Name())))
  143. L.Push(LNumber(1)) // C-Lua compatibility: Original Lua pushes errno to the stack
  144. return 3
  145. }
  146. return 0
  147. }
  148. var stdFiles = []struct {
  149. name string
  150. file *os.File
  151. writable bool
  152. readable bool
  153. }{
  154. {"stdout", os.Stdout, true, false},
  155. {"stdin", os.Stdin, false, true},
  156. {"stderr", os.Stderr, true, false},
  157. }
  158. func OpenIo(L *LState) int {
  159. mod := L.RegisterModule(IoLibName, map[string]LGFunction{}).(*LTable)
  160. mt := L.NewTypeMetatable(lFileClass)
  161. mt.RawSetString("__index", mt)
  162. L.SetFuncs(mt, fileMethods)
  163. mt.RawSetString("lines", L.NewClosure(fileLines, L.NewFunction(fileLinesIter)))
  164. for _, finfo := range stdFiles {
  165. file, _ := newFile(L, finfo.file, "", 0, os.FileMode(0), finfo.writable, finfo.readable)
  166. mod.RawSetString(finfo.name, file)
  167. }
  168. uv := L.CreateTable(2, 0)
  169. uv.RawSetInt(fileDefOutIndex, mod.RawGetString("stdout"))
  170. uv.RawSetInt(fileDefInIndex, mod.RawGetString("stdin"))
  171. for name, fn := range ioFuncs {
  172. mod.RawSetString(name, L.NewClosure(fn, uv))
  173. }
  174. mod.RawSetString("lines", L.NewClosure(ioLines, uv, L.NewClosure(ioLinesIter, uv)))
  175. // Modifications are being made in-place rather than returned?
  176. L.Push(mod)
  177. return 1
  178. }
  179. var fileMethods = map[string]LGFunction{
  180. "__tostring": fileToString,
  181. "write": fileWrite,
  182. "close": fileClose,
  183. "flush": fileFlush,
  184. "lines": fileLines,
  185. "read": fileRead,
  186. "seek": fileSeek,
  187. "setvbuf": fileSetVBuf,
  188. }
  189. func fileToString(L *LState) int {
  190. file := checkFile(L)
  191. if file.Type() == lFileFile {
  192. if file.closed {
  193. L.Push(LString("file (closed)"))
  194. } else {
  195. L.Push(LString("file"))
  196. }
  197. } else {
  198. if file.closed {
  199. L.Push(LString("process (closed)"))
  200. } else {
  201. L.Push(LString("process"))
  202. }
  203. }
  204. return 1
  205. }
  206. func fileWriteAux(L *LState, file *lFile, idx int) int {
  207. if n := fileIsWritable(L, file); n != 0 {
  208. return n
  209. }
  210. errorIfFileIsClosed(L, file)
  211. top := L.GetTop()
  212. out := file.writer
  213. var err error
  214. for i := idx; i <= top; i++ {
  215. L.CheckTypes(i, LTNumber, LTString)
  216. s := LVAsString(L.Get(i))
  217. if _, err = out.Write(unsafeFastStringToReadOnlyBytes(s)); err != nil {
  218. goto errreturn
  219. }
  220. }
  221. file.AbandonReadBuffer()
  222. L.Push(LTrue)
  223. return 1
  224. errreturn:
  225. file.AbandonReadBuffer()
  226. L.Push(LNil)
  227. L.Push(LString(err.Error()))
  228. L.Push(LNumber(1)) // C-Lua compatibility: Original Lua pushes errno to the stack
  229. return 3
  230. }
  231. func fileCloseAux(L *LState, file *lFile) int {
  232. file.closed = true
  233. var err error
  234. if file.writer != nil {
  235. if bwriter, ok := file.writer.(*bufio.Writer); ok {
  236. if err = bwriter.Flush(); err != nil {
  237. goto errreturn
  238. }
  239. }
  240. }
  241. file.AbandonReadBuffer()
  242. switch file.Type() {
  243. case lFileFile:
  244. if err = file.fp.Close(); err != nil {
  245. goto errreturn
  246. }
  247. L.Push(LTrue)
  248. return 1
  249. case lFileProcess:
  250. if file.stdout != nil {
  251. file.stdout.Close() // ignore errors
  252. }
  253. err = file.pp.Wait()
  254. var exitStatus int // Initialised to zero value = 0
  255. if err != nil {
  256. if e2, ok := err.(*exec.ExitError); ok {
  257. if s, ok := e2.Sys().(syscall.WaitStatus); ok {
  258. exitStatus = s.ExitStatus()
  259. } else {
  260. err = errors.New("Unimplemented for system where exec.ExitError.Sys() is not syscall.WaitStatus.")
  261. }
  262. }
  263. } else {
  264. exitStatus = 0
  265. }
  266. L.Push(LNumber(exitStatus))
  267. return 1
  268. }
  269. errreturn:
  270. L.RaiseError(err.Error())
  271. return 0
  272. }
  273. func fileFlushAux(L *LState, file *lFile) int {
  274. if n := fileIsWritable(L, file); n != 0 {
  275. return n
  276. }
  277. errorIfFileIsClosed(L, file)
  278. if bwriter, ok := file.writer.(*bufio.Writer); ok {
  279. if err := bwriter.Flush(); err != nil {
  280. L.Push(LNil)
  281. L.Push(LString(err.Error()))
  282. return 2
  283. }
  284. }
  285. L.Push(LTrue)
  286. return 1
  287. }
  288. func fileReadAux(L *LState, file *lFile, idx int) int {
  289. if n := fileIsReadable(L, file); n != 0 {
  290. return n
  291. }
  292. errorIfFileIsClosed(L, file)
  293. if L.GetTop() == idx-1 {
  294. L.Push(LString("*l"))
  295. }
  296. var err error
  297. top := L.GetTop()
  298. for i := idx; i <= top; i++ {
  299. switch lv := L.Get(i).(type) {
  300. case LNumber:
  301. size := int64(lv)
  302. if size == 0 {
  303. _, err = file.reader.ReadByte()
  304. if err == io.EOF {
  305. L.Push(LNil)
  306. goto normalreturn
  307. }
  308. file.reader.UnreadByte()
  309. }
  310. var buf []byte
  311. var iseof bool
  312. buf, err, iseof = readBufioSize(file.reader, size)
  313. if iseof {
  314. L.Push(LNil)
  315. goto normalreturn
  316. }
  317. if err != nil {
  318. goto errreturn
  319. }
  320. L.Push(LString(string(buf)))
  321. case LString:
  322. options := L.CheckString(i)
  323. if len(options) > 0 && options[0] != '*' {
  324. L.ArgError(2, "invalid options:"+options)
  325. }
  326. for _, opt := range options[1:] {
  327. switch opt {
  328. case 'n':
  329. var v LNumber
  330. _, err = fmt.Fscanf(file.reader, LNumberScanFormat, &v)
  331. if err == io.EOF {
  332. L.Push(LNil)
  333. goto normalreturn
  334. }
  335. if err != nil {
  336. goto errreturn
  337. }
  338. L.Push(v)
  339. case 'a':
  340. var buf []byte
  341. buf, err = io.ReadAll(file.reader)
  342. if err == io.EOF {
  343. L.Push(emptyLString)
  344. goto normalreturn
  345. }
  346. if err != nil {
  347. goto errreturn
  348. }
  349. L.Push(LString(string(buf)))
  350. case 'l':
  351. var buf []byte
  352. var iseof bool
  353. buf, err, iseof = readBufioLine(file.reader)
  354. if iseof {
  355. L.Push(LNil)
  356. goto normalreturn
  357. }
  358. if err != nil {
  359. goto errreturn
  360. }
  361. L.Push(LString(string(buf)))
  362. default:
  363. L.ArgError(2, "invalid options:"+string(opt))
  364. }
  365. }
  366. }
  367. }
  368. normalreturn:
  369. return L.GetTop() - top
  370. errreturn:
  371. L.Push(LNil)
  372. L.Push(LString(err.Error()))
  373. L.Push(LNumber(1)) // C-Lua compatibility: Original Lua pushes errno to the stack
  374. return 3
  375. }
  376. var fileSeekOptions = []string{"set", "cur", "end"}
  377. func fileSeek(L *LState) int {
  378. file := checkFile(L)
  379. if file.Type() != lFileFile {
  380. L.Push(LNil)
  381. L.Push(LString("can not seek a process."))
  382. return 2
  383. }
  384. top := L.GetTop()
  385. if top == 1 {
  386. L.Push(LString("cur"))
  387. L.Push(LNumber(0))
  388. } else if top == 2 {
  389. L.Push(LNumber(0))
  390. }
  391. var pos int64
  392. var err error
  393. err = file.AbandonReadBuffer()
  394. if err != nil {
  395. goto errreturn
  396. }
  397. pos, err = file.fp.Seek(L.CheckInt64(3), L.CheckOption(2, fileSeekOptions))
  398. if err != nil {
  399. goto errreturn
  400. }
  401. L.Push(LNumber(pos))
  402. return 1
  403. errreturn:
  404. L.Push(LNil)
  405. L.Push(LString(err.Error()))
  406. return 2
  407. }
  408. func fileWrite(L *LState) int {
  409. return fileWriteAux(L, checkFile(L), 2)
  410. }
  411. func fileClose(L *LState) int {
  412. return fileCloseAux(L, checkFile(L))
  413. }
  414. func fileFlush(L *LState) int {
  415. return fileFlushAux(L, checkFile(L))
  416. }
  417. func fileLinesIter(L *LState) int {
  418. var file *lFile
  419. if ud, ok := L.Get(1).(*LUserData); ok {
  420. file = ud.Value.(*lFile)
  421. } else {
  422. file = L.Get(UpvalueIndex(2)).(*LUserData).Value.(*lFile)
  423. }
  424. buf, _, err := file.reader.ReadLine()
  425. if err != nil {
  426. if err == io.EOF {
  427. L.Push(LNil)
  428. return 1
  429. }
  430. L.RaiseError(err.Error())
  431. }
  432. L.Push(LString(string(buf)))
  433. return 1
  434. }
  435. func fileLines(L *LState) int {
  436. file := checkFile(L)
  437. ud := L.CheckUserData(1)
  438. if n := fileIsReadable(L, file); n != 0 {
  439. return 0
  440. }
  441. L.Push(L.NewClosure(fileLinesIter, L.Get(UpvalueIndex(1)), ud))
  442. return 1
  443. }
  444. func fileRead(L *LState) int {
  445. return fileReadAux(L, checkFile(L), 2)
  446. }
  447. var filebufOptions = []string{"no", "full"}
  448. func fileSetVBuf(L *LState) int {
  449. var err error
  450. var writer io.Writer
  451. file := checkFile(L)
  452. if n := fileIsWritable(L, file); n != 0 {
  453. return n
  454. }
  455. switch filebufOptions[L.CheckOption(2, filebufOptions)] {
  456. case "no":
  457. switch file.Type() {
  458. case lFileFile:
  459. file.writer = file.fp
  460. case lFileProcess:
  461. file.writer, err = file.pp.StdinPipe()
  462. if err != nil {
  463. goto errreturn
  464. }
  465. }
  466. case "full", "line": // TODO line buffer not supported
  467. bufsize := L.OptInt(3, fileDefaultWriteBuffer)
  468. switch file.Type() {
  469. case lFileFile:
  470. file.writer = bufio.NewWriterSize(file.fp, bufsize)
  471. case lFileProcess:
  472. writer, err = file.pp.StdinPipe()
  473. if err != nil {
  474. goto errreturn
  475. }
  476. file.writer = bufio.NewWriterSize(writer, bufsize)
  477. }
  478. }
  479. L.Push(LTrue)
  480. return 1
  481. errreturn:
  482. L.Push(LNil)
  483. L.Push(LString(err.Error()))
  484. return 2
  485. }
  486. func ioInput(L *LState) int {
  487. if L.GetTop() == 0 {
  488. L.Push(fileDefIn(L))
  489. return 1
  490. }
  491. switch lv := L.Get(1).(type) {
  492. case LString:
  493. file, err := newFile(L, nil, string(lv), os.O_RDONLY, 0600, false, true)
  494. if err != nil {
  495. L.RaiseError(err.Error())
  496. }
  497. L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefInIndex, file)
  498. L.Push(file)
  499. return 1
  500. case *LUserData:
  501. if _, ok := lv.Value.(*lFile); ok {
  502. L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefInIndex, lv)
  503. L.Push(lv)
  504. return 1
  505. }
  506. }
  507. L.ArgError(1, "string or file expedted, but got "+L.Get(1).Type().String())
  508. return 0
  509. }
  510. func ioClose(L *LState) int {
  511. if L.GetTop() == 0 {
  512. return fileCloseAux(L, fileDefOut(L).Value.(*lFile))
  513. }
  514. return fileClose(L)
  515. }
  516. func ioFlush(L *LState) int {
  517. return fileFlushAux(L, fileDefOut(L).Value.(*lFile))
  518. }
  519. func ioLinesIter(L *LState) int {
  520. var file *lFile
  521. toclose := false
  522. if ud, ok := L.Get(1).(*LUserData); ok {
  523. file = ud.Value.(*lFile)
  524. } else {
  525. file = L.Get(UpvalueIndex(2)).(*LUserData).Value.(*lFile)
  526. toclose = true
  527. }
  528. buf, _, err := file.reader.ReadLine()
  529. if err != nil {
  530. if err == io.EOF {
  531. if toclose {
  532. fileCloseAux(L, file)
  533. }
  534. L.Push(LNil)
  535. return 1
  536. }
  537. L.RaiseError(err.Error())
  538. }
  539. L.Push(LString(string(buf)))
  540. return 1
  541. }
  542. func ioLines(L *LState) int {
  543. if L.GetTop() == 0 {
  544. L.Push(L.Get(UpvalueIndex(2)))
  545. L.Push(fileDefIn(L))
  546. return 2
  547. }
  548. path := L.CheckString(1)
  549. ud, err := newFile(L, nil, path, os.O_RDONLY, os.FileMode(0600), false, true)
  550. if err != nil {
  551. return 0
  552. }
  553. L.Push(L.NewClosure(ioLinesIter, L.Get(UpvalueIndex(1)), ud))
  554. return 1
  555. }
  556. var ioOpenOpions = []string{"r", "rb", "w", "wb", "a", "ab", "r+", "rb+", "w+", "wb+", "a+", "ab+"}
  557. func ioOpenFile(L *LState) int {
  558. path := L.CheckString(1)
  559. if L.GetTop() == 1 {
  560. L.Push(LString("r"))
  561. }
  562. mode := os.O_RDONLY
  563. perm := 0600
  564. writable := true
  565. readable := true
  566. switch ioOpenOpions[L.CheckOption(2, ioOpenOpions)] {
  567. case "r", "rb":
  568. mode = os.O_RDONLY
  569. writable = false
  570. case "w", "wb":
  571. mode = os.O_WRONLY | os.O_TRUNC | os.O_CREATE
  572. readable = false
  573. case "a", "ab":
  574. mode = os.O_WRONLY | os.O_APPEND | os.O_CREATE
  575. case "r+", "rb+":
  576. mode = os.O_RDWR
  577. case "w+", "wb+":
  578. mode = os.O_RDWR | os.O_TRUNC | os.O_CREATE
  579. case "a+", "ab+":
  580. mode = os.O_APPEND | os.O_RDWR | os.O_CREATE
  581. }
  582. file, err := newFile(L, nil, path, mode, os.FileMode(perm), writable, readable)
  583. if err != nil {
  584. L.Push(LNil)
  585. L.Push(LString(err.Error()))
  586. L.Push(LNumber(1)) // C-Lua compatibility: Original Lua pushes errno to the stack
  587. return 3
  588. }
  589. L.Push(file)
  590. return 1
  591. }
  592. var ioPopenOptions = []string{"r", "w"}
  593. func ioPopen(L *LState) int {
  594. cmd := L.CheckString(1)
  595. if L.GetTop() == 1 {
  596. L.Push(LString("r"))
  597. } else if L.GetTop() > 1 && (L.Get(2)).Type() == LTNil {
  598. L.SetTop(1)
  599. L.Push(LString("r"))
  600. }
  601. var file *LUserData
  602. var err error
  603. switch ioPopenOptions[L.CheckOption(2, ioPopenOptions)] {
  604. case "r":
  605. file, err = newProcess(L, cmd, false, true)
  606. case "w":
  607. file, err = newProcess(L, cmd, true, false)
  608. }
  609. if err != nil {
  610. L.Push(LNil)
  611. L.Push(LString(err.Error()))
  612. return 2
  613. }
  614. L.Push(file)
  615. return 1
  616. }
  617. func ioRead(L *LState) int {
  618. return fileReadAux(L, fileDefIn(L).Value.(*lFile), 1)
  619. }
  620. func ioType(L *LState) int {
  621. ud, udok := L.Get(1).(*LUserData)
  622. if !udok {
  623. L.Push(LNil)
  624. return 1
  625. }
  626. file, ok := ud.Value.(*lFile)
  627. if !ok {
  628. L.Push(LNil)
  629. return 1
  630. }
  631. if file.closed {
  632. L.Push(LString("closed file"))
  633. return 1
  634. }
  635. L.Push(LString("file"))
  636. return 1
  637. }
  638. func ioTmpFile(L *LState) int {
  639. file, err := os.CreateTemp("", "")
  640. if err != nil {
  641. L.Push(LNil)
  642. L.Push(LString(err.Error()))
  643. return 2
  644. }
  645. L.G.tempFiles = append(L.G.tempFiles, file)
  646. ud, _ := newFile(L, file, "", 0, os.FileMode(0), true, true)
  647. L.Push(ud)
  648. return 1
  649. }
  650. func ioOutput(L *LState) int {
  651. if L.GetTop() == 0 {
  652. L.Push(fileDefOut(L))
  653. return 1
  654. }
  655. switch lv := L.Get(1).(type) {
  656. case LString:
  657. file, err := newFile(L, nil, string(lv), os.O_WRONLY|os.O_CREATE, 0600, true, false)
  658. if err != nil {
  659. L.RaiseError(err.Error())
  660. }
  661. L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefOutIndex, file)
  662. L.Push(file)
  663. return 1
  664. case *LUserData:
  665. if _, ok := lv.Value.(*lFile); ok {
  666. L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefOutIndex, lv)
  667. L.Push(lv)
  668. return 1
  669. }
  670. }
  671. L.ArgError(1, "string or file expedted, but got "+L.Get(1).Type().String())
  672. return 0
  673. }
  674. func ioWrite(L *LState) int {
  675. return fileWriteAux(L, fileDefOut(L).Value.(*lFile), 1)
  676. }
  677. //