github.com/qiniu/gopher-lua@v0.2017.11/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  	closed bool
    36  }
    37  
    38  type lFileType int
    39  
    40  const (
    41  	lFileFile lFileType = iota
    42  	lFileProcess
    43  )
    44  
    45  const fileDefOutIndex = 1
    46  const fileDefInIndex = 2
    47  const fileDefaultWriteBuffer = 4096
    48  const fileDefaultReadBuffer = 4096
    49  
    50  func checkFile(L *LState) *lFile {
    51  	ud := L.CheckUserData(1)
    52  	if file, ok := ud.Value.(*lFile); ok {
    53  		return file
    54  	}
    55  	L.ArgError(1, "file expected")
    56  	return nil
    57  }
    58  
    59  func errorIfFileIsClosed(L *LState, file *lFile) {
    60  	if file.closed {
    61  		L.ArgError(1, "file is closed")
    62  	}
    63  }
    64  
    65  func newFile(L *LState, file *os.File, path string, flag int, perm os.FileMode, writable, readable bool) (*LUserData, error) {
    66  	ud := L.NewUserData()
    67  	var err error
    68  	if file == nil {
    69  		file, err = os.OpenFile(path, flag, perm)
    70  		if err != nil {
    71  			return nil, err
    72  		}
    73  	}
    74  	lfile := &lFile{fp: file, pp: nil, writer: nil, reader: nil, closed: false}
    75  	ud.Value = lfile
    76  	if writable {
    77  		lfile.writer = file
    78  	}
    79  	if readable {
    80  		lfile.reader = bufio.NewReaderSize(file, fileDefaultReadBuffer)
    81  	}
    82  	L.SetMetatable(ud, L.GetTypeMetatable(lFileClass))
    83  	return ud, nil
    84  }
    85  
    86  func newProcess(L *LState, cmd string, writable, readable bool) (*LUserData, error) {
    87  	ud := L.NewUserData()
    88  	c, args := popenArgs(cmd)
    89  	pp := exec.Command(c, args...)
    90  	lfile := &lFile{fp: nil, pp: pp, writer: nil, reader: nil, closed: false}
    91  	ud.Value = lfile
    92  
    93  	var err error
    94  	if writable {
    95  		lfile.writer, err = pp.StdinPipe()
    96  	}
    97  	if readable {
    98  		var reader io.Reader
    99  		reader, err = pp.StdoutPipe()
   100  		lfile.reader = bufio.NewReaderSize(reader, 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  		err = file.pp.Wait()
   282  		var exitStatus int // Initialised to zero value = 0
   283  		if err != nil {
   284  			if e2, ok := err.(*exec.ExitError); ok {
   285  				if s, ok := e2.Sys().(syscall.WaitStatus); ok {
   286  					exitStatus = s.ExitStatus()
   287  				} else {
   288  					err = errors.New("Unimplemented for system where exec.ExitError.Sys() is not syscall.WaitStatus.")
   289  				}
   290  			}
   291  		} else {
   292  			exitStatus = 0
   293  		}
   294  		L.Push(LNumber(exitStatus))
   295  		return 1
   296  	}
   297  
   298  errreturn:
   299  	L.RaiseError(err.Error())
   300  	return 0
   301  }
   302  
   303  func fileFlushAux(L *LState, file *lFile) int {
   304  	if n := fileIsWritable(L, file); n != 0 {
   305  		return n
   306  	}
   307  	errorIfFileIsClosed(L, file)
   308  
   309  	if bwriter, ok := file.writer.(*bufio.Writer); ok {
   310  		if err := bwriter.Flush(); err != nil {
   311  			L.Push(LNil)
   312  			L.Push(LString(err.Error()))
   313  			return 2
   314  		}
   315  	}
   316  	L.Push(LTrue)
   317  	return 1
   318  }
   319  
   320  func fileReadAux(L *LState, file *lFile, idx int) int {
   321  	if n := fileIsReadable(L, file); n != 0 {
   322  		return n
   323  	}
   324  	errorIfFileIsClosed(L, file)
   325  	if L.GetTop() == idx-1 {
   326  		L.Push(LString("*l"))
   327  	}
   328  	var err error
   329  	top := L.GetTop()
   330  	for i := idx; i <= top; i++ {
   331  		switch lv := L.Get(i).(type) {
   332  		case LNumber:
   333  			size := int64(lv)
   334  			if size == 0 {
   335  				_, err = file.reader.ReadByte()
   336  				if err == io.EOF {
   337  					L.Push(LNil)
   338  					goto normalreturn
   339  				}
   340  				file.reader.UnreadByte()
   341  			}
   342  			var buf []byte
   343  			var iseof bool
   344  			buf, err, iseof = readBufioSize(file.reader, size)
   345  			if iseof {
   346  				L.Push(LNil)
   347  				goto normalreturn
   348  			}
   349  			if err != nil {
   350  				goto errreturn
   351  			}
   352  			L.Push(LString(string(buf)))
   353  		case LString:
   354  			options := L.CheckString(i)
   355  			if len(options) > 0 && options[0] != '*' {
   356  				L.ArgError(2, "invalid options:"+options)
   357  			}
   358  			for _, opt := range options[1:] {
   359  				switch opt {
   360  				case 'n':
   361  					var v LNumber
   362  					_, err = fmt.Fscanf(file.reader, LNumberScanFormat, &v)
   363  					if err == io.EOF {
   364  						L.Push(LNil)
   365  						goto normalreturn
   366  					}
   367  					if err != nil {
   368  						goto errreturn
   369  					}
   370  					L.Push(v)
   371  				case 'a':
   372  					var buf []byte
   373  					buf, err = ioutil.ReadAll(file.reader)
   374  					if err == io.EOF {
   375  						L.Push(LString(""))
   376  						goto normalreturn
   377  					}
   378  					if err != nil {
   379  						goto errreturn
   380  					}
   381  					L.Push(LString(string(buf)))
   382  				case 'l':
   383  					var buf []byte
   384  					var iseof bool
   385  					buf, err, iseof = readBufioLine(file.reader)
   386  					if iseof {
   387  						L.Push(LNil)
   388  						goto normalreturn
   389  					}
   390  					if err != nil {
   391  						goto errreturn
   392  					}
   393  					L.Push(LString(string(buf)))
   394  				default:
   395  					L.ArgError(2, "invalid options:"+string(opt))
   396  				}
   397  			}
   398  		}
   399  	}
   400  normalreturn:
   401  	return L.GetTop() - top
   402  
   403  errreturn:
   404  	L.RaiseError(err.Error())
   405  	//L.Push(LNil)
   406  	//L.Push(LString(err.Error()))
   407  	return 2
   408  }
   409  
   410  var fileSeekOptions = []string{"set", "cur", "end"}
   411  
   412  func fileSeek(L *LState) int {
   413  	file := checkFile(L)
   414  	if file.Type() != lFileFile {
   415  		L.Push(LNil)
   416  		L.Push(LString("can not seek a process."))
   417  		return 2
   418  	}
   419  
   420  	top := L.GetTop()
   421  	if top == 1 {
   422  		L.Push(LString("cur"))
   423  		L.Push(LNumber(0))
   424  	} else if top == 2 {
   425  		L.Push(LNumber(0))
   426  	}
   427  
   428  	var pos int64
   429  	var err error
   430  
   431  	err = file.AbandonReadBuffer()
   432  	if err != nil {
   433  		goto errreturn
   434  	}
   435  
   436  	pos, err = file.fp.Seek(L.CheckInt64(3), L.CheckOption(2, fileSeekOptions))
   437  	if err != nil {
   438  		goto errreturn
   439  	}
   440  
   441  	L.Push(LNumber(pos))
   442  	return 1
   443  
   444  errreturn:
   445  	L.Push(LNil)
   446  	L.Push(LString(err.Error()))
   447  	return 2
   448  }
   449  
   450  func fileWrite(L *LState) int {
   451  	return fileWriteAux(L, checkFile(L), 2)
   452  }
   453  
   454  func fileClose(L *LState) int {
   455  	return fileCloseAux(L, checkFile(L))
   456  }
   457  
   458  func fileFlush(L *LState) int {
   459  	return fileFlushAux(L, checkFile(L))
   460  }
   461  
   462  func fileLinesIter(L *LState) int {
   463  	var file *lFile
   464  	if ud, ok := L.Get(1).(*LUserData); ok {
   465  		file = ud.Value.(*lFile)
   466  	} else {
   467  		file = L.Get(UpvalueIndex(2)).(*LUserData).Value.(*lFile)
   468  	}
   469  	buf, _, err := file.reader.ReadLine()
   470  	if err != nil {
   471  		if err == io.EOF {
   472  			L.Push(LNil)
   473  			return 1
   474  		}
   475  		L.RaiseError(err.Error())
   476  	}
   477  	L.Push(LString(string(buf)))
   478  	return 1
   479  }
   480  
   481  func fileLines(L *LState) int {
   482  	file := checkFile(L)
   483  	ud := L.CheckUserData(1)
   484  	if n := fileIsReadable(L, file); n != 0 {
   485  		return 0
   486  	}
   487  	L.Push(L.NewClosure(fileLinesIter, L.Get(UpvalueIndex(1)), ud))
   488  	return 1
   489  }
   490  
   491  func fileRead(L *LState) int {
   492  	return fileReadAux(L, checkFile(L), 2)
   493  }
   494  
   495  var filebufOptions = []string{"no", "full"}
   496  
   497  func fileSetVBuf(L *LState) int {
   498  	var err error
   499  	var writer io.Writer
   500  	file := checkFile(L)
   501  	if n := fileIsWritable(L, file); n != 0 {
   502  		return n
   503  	}
   504  	switch filebufOptions[L.CheckOption(2, filebufOptions)] {
   505  	case "no":
   506  		switch file.Type() {
   507  		case lFileFile:
   508  			file.writer = file.fp
   509  		case lFileProcess:
   510  			file.writer, err = file.pp.StdinPipe()
   511  			if err != nil {
   512  				goto errreturn
   513  			}
   514  		}
   515  	case "full", "line": // TODO line buffer not supported
   516  		bufsize := L.OptInt(3, fileDefaultWriteBuffer)
   517  		switch file.Type() {
   518  		case lFileFile:
   519  			file.writer = bufio.NewWriterSize(file.fp, bufsize)
   520  		case lFileProcess:
   521  			writer, err = file.pp.StdinPipe()
   522  			if err != nil {
   523  				goto errreturn
   524  			}
   525  			file.writer = bufio.NewWriterSize(writer, bufsize)
   526  		}
   527  	}
   528  	L.Push(LTrue)
   529  	return 1
   530  errreturn:
   531  	L.Push(LNil)
   532  	L.Push(LString(err.Error()))
   533  	return 2
   534  }
   535  
   536  func ioInput(L *LState) int {
   537  	if L.GetTop() == 0 {
   538  		L.Push(fileDefIn(L))
   539  		return 1
   540  	}
   541  	switch lv := L.Get(1).(type) {
   542  	case LString:
   543  		file, err := newFile(L, nil, string(lv), os.O_RDONLY, 0600, false, true)
   544  		if err != nil {
   545  			L.RaiseError(err.Error())
   546  		}
   547  		L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefInIndex, file)
   548  		L.Push(file)
   549  		return 1
   550  	case *LUserData:
   551  		if _, ok := lv.Value.(*lFile); ok {
   552  			L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefInIndex, lv)
   553  			L.Push(lv)
   554  			return 1
   555  		}
   556  
   557  	}
   558  	L.ArgError(1, "string or file expedted, but got "+L.Get(1).Type().String())
   559  	return 0
   560  }
   561  
   562  func ioClose(L *LState) int {
   563  	if L.GetTop() == 0 {
   564  		return fileCloseAux(L, fileDefOut(L).Value.(*lFile))
   565  	}
   566  	return fileClose(L)
   567  }
   568  
   569  func ioFlush(L *LState) int {
   570  	return fileFlushAux(L, fileDefOut(L).Value.(*lFile))
   571  }
   572  
   573  func ioLinesIter(L *LState) int {
   574  	var file *lFile
   575  	toclose := false
   576  	if ud, ok := L.Get(1).(*LUserData); ok {
   577  		file = ud.Value.(*lFile)
   578  	} else {
   579  		file = L.Get(UpvalueIndex(2)).(*LUserData).Value.(*lFile)
   580  		toclose = true
   581  	}
   582  	buf, _, err := file.reader.ReadLine()
   583  	if err != nil {
   584  		if err == io.EOF {
   585  			if toclose {
   586  				fileCloseAux(L, file)
   587  			}
   588  			L.Push(LNil)
   589  			return 1
   590  		}
   591  		L.RaiseError(err.Error())
   592  	}
   593  	L.Push(LString(string(buf)))
   594  	return 1
   595  }
   596  
   597  func ioLines(L *LState) int {
   598  	if L.GetTop() == 0 {
   599  		L.Push(L.Get(UpvalueIndex(2)))
   600  		L.Push(fileDefIn(L))
   601  		return 2
   602  	}
   603  
   604  	path := L.CheckString(1)
   605  	ud, err := newFile(L, nil, path, os.O_RDONLY, os.FileMode(0600), false, true)
   606  	if err != nil {
   607  		return 0
   608  	}
   609  	L.Push(L.NewClosure(ioLinesIter, L.Get(UpvalueIndex(1)), ud))
   610  	return 1
   611  }
   612  
   613  var ioOpenOpions = []string{"r", "rb", "w", "wb", "a", "ab", "r+", "rb+", "w+", "wb+", "a+", "ab+"}
   614  
   615  func ioOpenFile(L *LState) int {
   616  	path := L.CheckString(1)
   617  	if L.GetTop() == 1 {
   618  		L.Push(LString("r"))
   619  	}
   620  	mode := os.O_RDONLY
   621  	perm := 0600
   622  	writable := true
   623  	readable := true
   624  	switch ioOpenOpions[L.CheckOption(2, ioOpenOpions)] {
   625  	case "r", "rb":
   626  		mode = os.O_RDONLY
   627  		writable = false
   628  	case "w", "wb":
   629  		mode = os.O_WRONLY | os.O_CREATE
   630  		readable = false
   631  	case "a", "ab":
   632  		mode = os.O_WRONLY | os.O_APPEND | os.O_CREATE
   633  	case "r+", "rb+":
   634  		mode = os.O_RDWR
   635  	case "w+", "wb+":
   636  		mode = os.O_RDWR | os.O_TRUNC | os.O_CREATE
   637  	case "a+", "ab+":
   638  		mode = os.O_APPEND | os.O_RDWR | os.O_CREATE
   639  	}
   640  	file, err := newFile(L, nil, path, mode, os.FileMode(perm), writable, readable)
   641  	if err != nil {
   642  		L.Push(LNil)
   643  		L.Push(LString(err.Error()))
   644  		L.Push(LNumber(1)) // C-Lua compatibility: Original Lua pushes errno to the stack
   645  		return 3
   646  	}
   647  	L.Push(file)
   648  	return 1
   649  
   650  }
   651  
   652  var ioPopenOptions = []string{"r", "w"}
   653  
   654  func ioPopen(L *LState) int {
   655  	cmd := L.CheckString(1)
   656  	if L.GetTop() == 1 {
   657  		L.Push(LString("r"))
   658  	}
   659  	var file *LUserData
   660  	var err error
   661  
   662  	switch ioPopenOptions[L.CheckOption(2, ioPopenOptions)] {
   663  	case "r":
   664  		file, err = newProcess(L, cmd, false, true)
   665  	case "w":
   666  		file, err = newProcess(L, cmd, true, false)
   667  	}
   668  	if err != nil {
   669  		L.Push(LNil)
   670  		L.Push(LString(err.Error()))
   671  		return 2
   672  	}
   673  	L.Push(file)
   674  	return 1
   675  }
   676  
   677  func ioRead(L *LState) int {
   678  	return fileReadAux(L, fileDefIn(L).Value.(*lFile), 1)
   679  }
   680  
   681  func ioType(L *LState) int {
   682  	ud, udok := L.Get(1).(*LUserData)
   683  	if !udok {
   684  		L.Push(LNil)
   685  		return 1
   686  	}
   687  	file, ok := ud.Value.(*lFile)
   688  	if !ok {
   689  		L.Push(LNil)
   690  		return 1
   691  	}
   692  	if file.closed {
   693  		L.Push(LString("closed file"))
   694  		return 1
   695  	}
   696  	L.Push(LString("file"))
   697  	return 1
   698  }
   699  
   700  func ioTmpFile(L *LState) int {
   701  	file, err := ioutil.TempFile("", "")
   702  	if err != nil {
   703  		L.Push(LNil)
   704  		L.Push(LString(err.Error()))
   705  		return 2
   706  	}
   707  	L.G.tempFiles = append(L.G.tempFiles, file)
   708  	ud, _ := newFile(L, file, "", 0, os.FileMode(0), true, true)
   709  	L.Push(ud)
   710  	return 1
   711  }
   712  
   713  func ioOutput(L *LState) int {
   714  	if L.GetTop() == 0 {
   715  		L.Push(fileDefOut(L))
   716  		return 1
   717  	}
   718  	switch lv := L.Get(1).(type) {
   719  	case LString:
   720  		file, err := newFile(L, nil, string(lv), os.O_WRONLY|os.O_CREATE, 0600, true, false)
   721  		if err != nil {
   722  			L.RaiseError(err.Error())
   723  		}
   724  		L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefOutIndex, file)
   725  		L.Push(file)
   726  		return 1
   727  	case *LUserData:
   728  		if _, ok := lv.Value.(*lFile); ok {
   729  			L.Get(UpvalueIndex(1)).(*LTable).RawSetInt(fileDefOutIndex, lv)
   730  			L.Push(lv)
   731  			return 1
   732  		}
   733  
   734  	}
   735  	L.ArgError(1, "string or file expedted, but got "+L.Get(1).Type().String())
   736  	return 0
   737  }
   738  
   739  func ioWrite(L *LState) int {
   740  	return fileWriteAux(L, fileDefOut(L).Value.(*lFile), 1)
   741  }
   742  
   743  //