iolib.go 16 KB

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