├── LICENSE ├── README.md ├── api.go ├── api_darwin.go ├── api_linux.go ├── api_other.go ├── api_windows.go ├── doc.go ├── go.mod ├── go.sum ├── lfs.go └── util.go /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gopher-lfs [![GoDoc](https://godoc.org/layeh.com/gopher-lfs?status.svg)](https://godoc.org/layeh.com/gopher-lfs) 2 | 3 | Package lfs partially implements the [luafilesystem module](http://keplerproject.github.io/luafilesystem/) for [gopher-lua](https://github.com/yuin/gopher-lua). 4 | 5 | ## License 6 | 7 | Public domain. 8 | -------------------------------------------------------------------------------- /api.go: -------------------------------------------------------------------------------- 1 | package lfs 2 | 3 | import ( 4 | "os" 5 | "time" 6 | 7 | "github.com/yuin/gopher-lua" 8 | ) 9 | 10 | var api = map[string]lua.LGFunction{ 11 | "attributes": apiAttributes, 12 | "chdir": apiChdir, 13 | "lock_dir": apiLockdir, 14 | "currentdir": apiCurrentdir, 15 | "dir": apiDir, 16 | "lock": apiLock, 17 | "link": apiLink, 18 | "mkdir": apiMkdir, 19 | "rmdir": apiRmdir, 20 | "setmode": apiSetmode, 21 | "symlinkattributes": apiSymlinkattributes, 22 | "touch": apiTouch, 23 | "unlock": apiUnlock, 24 | } 25 | 26 | func apiAttributes(L *lua.LState) int { 27 | return attributes(L, os.Stat) 28 | } 29 | 30 | func apiChdir(L *lua.LState) int { 31 | dir := L.CheckString(1) 32 | 33 | err := os.Chdir(dir) 34 | if err != nil { 35 | L.Push(lua.LNil) 36 | L.Push(lua.LString(err.Error())) 37 | return 2 38 | } 39 | L.Push(lua.LTrue) 40 | return 1 41 | } 42 | 43 | func apiLockdir(L *lua.LState) int { 44 | L.RaiseError("unimplemented function") 45 | return 0 46 | } 47 | 48 | func apiCurrentdir(L *lua.LState) int { 49 | dir, err := os.Getwd() 50 | if err != nil { 51 | L.Push(lua.LNil) 52 | L.Push(lua.LString(err.Error())) 53 | return 2 54 | } 55 | L.Push(lua.LString(dir)) 56 | return 1 57 | } 58 | 59 | func apiDir(L *lua.LState) int { 60 | path := L.CheckString(1) 61 | 62 | f, err := os.Open(path) 63 | if err != nil { 64 | L.RaiseError("%s", err) 65 | return 0 66 | } 67 | stat, err := f.Stat() 68 | if err != nil { 69 | L.RaiseError("%s", err) 70 | return 0 71 | } 72 | if !stat.IsDir() { 73 | L.RaiseError("not a directory") 74 | return 0 75 | } 76 | L.Push(L.NewFunction(dirItr)) 77 | ud := L.NewUserData() 78 | ud.Value = f 79 | L.Push(ud) 80 | return 2 81 | } 82 | 83 | func apiLock(L *lua.LState) int { 84 | L.RaiseError("unimplemented function") 85 | return 0 86 | } 87 | 88 | func apiLink(L *lua.LState) int { 89 | old := L.CheckString(1) 90 | new := L.CheckString(2) 91 | symlink := L.OptBool(3, false) 92 | 93 | var err error 94 | if symlink { 95 | err = os.Symlink(old, new) 96 | } else { 97 | err = os.Link(old, new) 98 | } 99 | if err != nil { 100 | L.Push(lua.LNil) 101 | L.Push(lua.LString(err.Error())) 102 | return 2 103 | } 104 | L.Push(lua.LTrue) 105 | return 1 106 | } 107 | 108 | func apiMkdir(L *lua.LState) int { 109 | dir := L.CheckString(1) 110 | 111 | err := os.Mkdir(dir, 0755) 112 | if err != nil { 113 | L.Push(lua.LNil) 114 | L.Push(lua.LString(err.Error())) 115 | return 2 116 | } 117 | L.Push(lua.LTrue) 118 | return 1 119 | } 120 | 121 | func apiRmdir(L *lua.LState) int { 122 | dir := L.CheckString(1) 123 | 124 | stat, err := os.Stat(dir) 125 | if err != nil { 126 | L.Push(lua.LNil) 127 | L.Push(lua.LString(err.Error())) 128 | return 2 129 | } 130 | if !stat.IsDir() { 131 | L.Push(lua.LNil) 132 | L.Push(lua.LString("not a directory")) 133 | return 2 134 | } 135 | err = os.Remove(dir) 136 | if err != nil { 137 | L.Push(lua.LNil) 138 | L.Push(lua.LString(err.Error())) 139 | return 2 140 | } 141 | L.Push(lua.LTrue) 142 | return 1 143 | } 144 | 145 | func apiSetmode(L *lua.LState) int { 146 | L.RaiseError("unimplemented function") 147 | return 0 148 | } 149 | 150 | func apiSymlinkattributes(L *lua.LState) int { 151 | return attributes(L, os.Lstat) 152 | } 153 | 154 | func apiTouch(L *lua.LState) int { 155 | filepath := L.CheckString(1) 156 | atime := L.OptInt64(2, time.Now().Unix()) 157 | mtime := L.OptInt64(3, atime) 158 | 159 | err := os.Chtimes(filepath, time.Unix(atime, 0), time.Unix(mtime, 0)) 160 | if err != nil { 161 | L.Push(lua.LNil) 162 | L.Push(lua.LString(err.Error())) 163 | return 2 164 | } 165 | L.Push(lua.LTrue) 166 | return 1 167 | } 168 | 169 | func apiUnlock(L *lua.LState) int { 170 | L.RaiseError("unimplemented function") 171 | return 0 172 | } 173 | -------------------------------------------------------------------------------- /api_darwin.go: -------------------------------------------------------------------------------- 1 | // +build darwin 2 | 3 | package lfs 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | 9 | "github.com/yuin/gopher-lua" 10 | ) 11 | 12 | func attributesFill(tbl *lua.LTable, stat os.FileInfo) error { 13 | sys := stat.Sys().(*syscall.Stat_t) 14 | tbl.RawSetString("dev", lua.LNumber(sys.Dev)) 15 | tbl.RawSetString("ino", lua.LNumber(sys.Ino)) 16 | { 17 | var mode string 18 | switch sys.Mode & syscall.S_IFMT { 19 | case syscall.S_IFREG: 20 | mode = "file" 21 | case syscall.S_IFDIR: 22 | mode = "directory" 23 | case syscall.S_IFLNK: 24 | mode = "link" 25 | case syscall.S_IFSOCK: 26 | mode = "socket" 27 | case syscall.S_IFIFO: 28 | mode = "named pipe" 29 | case syscall.S_IFCHR: 30 | mode = "char device" 31 | case syscall.S_IFBLK: 32 | mode = "block device" 33 | default: 34 | mode = "other" 35 | } 36 | tbl.RawSetString("mode", lua.LString(mode)) 37 | } 38 | tbl.RawSetString("nlink", lua.LNumber(sys.Nlink)) 39 | tbl.RawSetString("uid", lua.LNumber(sys.Uid)) 40 | tbl.RawSetString("gid", lua.LNumber(sys.Gid)) 41 | tbl.RawSetString("rdev", lua.LNumber(sys.Rdev)) 42 | tbl.RawSetString("access", lua.LNumber(sys.Atimespec.Sec)) 43 | tbl.RawSetString("modification", lua.LNumber(sys.Mtimespec.Sec)) 44 | tbl.RawSetString("change", lua.LNumber(sys.Ctimespec.Sec)) 45 | tbl.RawSetString("size", lua.LNumber(sys.Size)) 46 | tbl.RawSetString("blocks", lua.LNumber(sys.Blocks)) 47 | tbl.RawSetString("blksize", lua.LNumber(sys.Blksize)) 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /api_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package lfs 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | 9 | "github.com/yuin/gopher-lua" 10 | ) 11 | 12 | func attributesFill(tbl *lua.LTable, stat os.FileInfo) error { 13 | sys := stat.Sys().(*syscall.Stat_t) 14 | tbl.RawSetString("dev", lua.LNumber(sys.Dev)) 15 | tbl.RawSetString("ino", lua.LNumber(sys.Ino)) 16 | { 17 | var mode string 18 | switch sys.Mode & syscall.S_IFMT { 19 | case syscall.S_IFREG: 20 | mode = "file" 21 | case syscall.S_IFDIR: 22 | mode = "directory" 23 | case syscall.S_IFLNK: 24 | mode = "link" 25 | case syscall.S_IFSOCK: 26 | mode = "socket" 27 | case syscall.S_IFIFO: 28 | mode = "named pipe" 29 | case syscall.S_IFCHR: 30 | mode = "char device" 31 | case syscall.S_IFBLK: 32 | mode = "block device" 33 | default: 34 | mode = "other" 35 | } 36 | tbl.RawSetString("mode", lua.LString(mode)) 37 | } 38 | tbl.RawSetString("nlink", lua.LNumber(sys.Nlink)) 39 | tbl.RawSetString("uid", lua.LNumber(sys.Uid)) 40 | tbl.RawSetString("gid", lua.LNumber(sys.Gid)) 41 | tbl.RawSetString("rdev", lua.LNumber(sys.Rdev)) 42 | tbl.RawSetString("access", lua.LNumber(sys.Atim.Sec)) 43 | tbl.RawSetString("modification", lua.LNumber(sys.Mtim.Sec)) 44 | tbl.RawSetString("change", lua.LNumber(sys.Ctim.Sec)) 45 | tbl.RawSetString("size", lua.LNumber(sys.Size)) 46 | tbl.RawSetString("blocks", lua.LNumber(sys.Blocks)) 47 | tbl.RawSetString("blksize", lua.LNumber(sys.Blksize)) 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /api_other.go: -------------------------------------------------------------------------------- 1 | // +build !darwin,!linux,!windows 2 | 3 | package lfs 4 | 5 | import ( 6 | "errors" 7 | "os" 8 | "runtime" 9 | 10 | "github.com/yuin/gopher-lua" 11 | ) 12 | 13 | func attributesFill(tbl *lua.LTable, stat os.FileInfo) error { 14 | return errors.New("unsupported operating system " + runtime.GOOS) 15 | } 16 | -------------------------------------------------------------------------------- /api_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package lfs 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | "time" 9 | 10 | "github.com/yuin/gopher-lua" 11 | ) 12 | 13 | func attributesFill(tbl *lua.LTable, stat os.FileInfo) error { 14 | sys := stat.Sys().(*syscall.Win32FileAttributeData) 15 | tbl.RawSetString("dev", lua.LNumber(0)) 16 | tbl.RawSetString("ino", lua.LNumber(0)) 17 | 18 | if stat.IsDir() { 19 | tbl.RawSetString("mode", lua.LString("directory")) 20 | } else { 21 | tbl.RawSetString("mode", lua.LString("file")) 22 | } 23 | 24 | tbl.RawSetString("nlink", lua.LNumber(0)) 25 | tbl.RawSetString("uid", lua.LNumber(0)) 26 | tbl.RawSetString("gid", lua.LNumber(0)) 27 | tbl.RawSetString("rdev", lua.LNumber(0)) 28 | 29 | tbl.RawSetString("access", lua.LNumber(time.Unix(0, sys.LastAccessTime.Nanoseconds()/1e9).Second())) 30 | tbl.RawSetString("modification", lua.LNumber(time.Unix(0, sys.CreationTime.Nanoseconds()/1e9).Second())) 31 | tbl.RawSetString("change", lua.LNumber(time.Unix(0, sys.LastWriteTime.Nanoseconds()/1e9).Second())) 32 | tbl.RawSetString("size", lua.LNumber(stat.Size())) 33 | 34 | tbl.RawSetString("blocks", lua.LNumber(0)) 35 | tbl.RawSetString("blksize", lua.LNumber(0)) 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Package lfs partially implements the luafilesystem module for gopher-lua. 2 | // 3 | // Documentation 4 | // 5 | // See http://keplerproject.github.io/luafilesystem/manual.html 6 | // 7 | // Example 8 | // 9 | // Below is an example usage of the library: 10 | // L := lua.NewState() 11 | // lfs.Preload(L) 12 | package lfs // import "layeh.com/gopher-lfs" 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module layeh.com/gopher-lfs 2 | 3 | go 1.12 4 | 5 | require github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 2 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 3 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 4 | github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036 h1:1b6PAtenNyhsmo/NKXVe34h7JEZKva1YB/ne7K7mqKM= 5 | github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= 6 | golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 7 | -------------------------------------------------------------------------------- /lfs.go: -------------------------------------------------------------------------------- 1 | package lfs 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | // Preload adds lfs to the given Lua state's package.preload table. After it 8 | // has been preloaded, it can be loaded using require: 9 | // 10 | // local lfs = require("lfs") 11 | func Preload(L *lua.LState) { 12 | L.PreloadModule("lfs", load) 13 | } 14 | 15 | func load(L *lua.LState) int { 16 | t := L.NewTable() 17 | L.SetFuncs(t, api) 18 | L.Push(t) 19 | return 1 20 | } 21 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package lfs 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/yuin/gopher-lua" 8 | ) 9 | 10 | func attributes(L *lua.LState, statFunc func(string) (os.FileInfo, error)) int { 11 | fp := L.CheckString(1) 12 | 13 | stat, err := statFunc(fp) 14 | if err != nil { 15 | L.Push(lua.LNil) 16 | L.Push(lua.LString(err.Error())) 17 | return 2 18 | } 19 | table := L.NewTable() 20 | if err := attributesFill(table, stat); err != nil { 21 | L.Push(lua.LNil) 22 | L.Push(lua.LString(err.Error())) 23 | return 2 24 | } 25 | if table.RawGetString("mode").String() == "link" { 26 | if path, err := filepath.EvalSymlinks(fp); err == nil { 27 | table.RawSetString("target", lua.LString(path)) 28 | } 29 | } 30 | if L.GetTop() > 1 { 31 | requestName := L.CheckString(2) 32 | L.Push(table.RawGetString(requestName)) 33 | return 1 34 | } 35 | L.Push(table) 36 | return 1 37 | } 38 | 39 | func dirItr(L *lua.LState) int { 40 | ud := L.CheckUserData(1) 41 | 42 | f, ok := ud.Value.(*os.File) 43 | if !ok { 44 | return 0 45 | } 46 | names, err := f.Readdirnames(1) 47 | if err != nil { 48 | return 0 49 | } 50 | L.Push(lua.LString(names[0])) 51 | return 1 52 | } 53 | --------------------------------------------------------------------------------