github.com/erikdubbelboer/gopher-lua@v0.0.0-20160512044044-e68f0dc85040/iolib.go (about)

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