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