1package lua
2
3import (
4	"io/ioutil"
5	"os"
6	"strings"
7	"time"
8)
9
10var startedAt time.Time
11
12func init() {
13	startedAt = time.Now()
14}
15
16func getIntField(L *LState, tb *LTable, key string, v int) int {
17	ret := tb.RawGetString(key)
18	if ln, ok := ret.(LNumber); ok {
19		return int(ln)
20	}
21	return v
22}
23
24func getBoolField(L *LState, tb *LTable, key string, v bool) bool {
25	ret := tb.RawGetString(key)
26	if lb, ok := ret.(LBool); ok {
27		return bool(lb)
28	}
29	return v
30}
31
32func OpenOs(L *LState) int {
33	osmod := L.RegisterModule(OsLibName, osFuncs)
34	L.Push(osmod)
35	return 1
36}
37
38var osFuncs = map[string]LGFunction{
39	"clock":     osClock,
40	"difftime":  osDiffTime,
41	"execute":   osExecute,
42	"exit":      osExit,
43	"date":      osDate,
44	"getenv":    osGetEnv,
45	"remove":    osRemove,
46	"rename":    osRename,
47	"setenv":    osSetEnv,
48	"setlocale": osSetLocale,
49	"time":      osTime,
50	"tmpname":   osTmpname,
51}
52
53func osClock(L *LState) int {
54	L.Push(LNumber(float64(time.Now().Sub(startedAt)) / float64(time.Second)))
55	return 1
56}
57
58func osDiffTime(L *LState) int {
59	L.Push(LNumber(L.CheckInt64(1) - L.CheckInt64(2)))
60	return 1
61}
62
63func osExecute(L *LState) int {
64	var procAttr os.ProcAttr
65	procAttr.Files = []*os.File{os.Stdin, os.Stdout, os.Stderr}
66	cmd, args := popenArgs(L.CheckString(1))
67	args = append([]string{cmd}, args...)
68	process, err := os.StartProcess(cmd, args, &procAttr)
69	if err != nil {
70		L.Push(LNumber(1))
71		return 1
72	}
73
74	ps, err := process.Wait()
75	if err != nil || !ps.Success() {
76		L.Push(LNumber(1))
77		return 1
78	}
79	L.Push(LNumber(0))
80	return 1
81}
82
83func osExit(L *LState) int {
84	L.Close()
85	os.Exit(L.OptInt(1, 0))
86	return 1
87}
88
89func osDate(L *LState) int {
90	t := time.Now()
91	cfmt := "%c"
92	if L.GetTop() >= 1 {
93		cfmt = L.CheckString(1)
94		if strings.HasPrefix(cfmt, "!") {
95			t = time.Now().UTC()
96			cfmt = strings.TrimLeft(cfmt, "!")
97		}
98		if L.GetTop() >= 2 {
99			t = time.Unix(L.CheckInt64(2), 0)
100		}
101		if strings.HasPrefix(cfmt, "*t") {
102			ret := L.NewTable()
103			ret.RawSetString("year", LNumber(t.Year()))
104			ret.RawSetString("month", LNumber(t.Month()))
105			ret.RawSetString("day", LNumber(t.Day()))
106			ret.RawSetString("hour", LNumber(t.Hour()))
107			ret.RawSetString("min", LNumber(t.Minute()))
108			ret.RawSetString("sec", LNumber(t.Second()))
109			ret.RawSetString("wday", LNumber(t.Weekday()+1))
110			// TODO yday & dst
111			ret.RawSetString("yday", LNumber(0))
112			ret.RawSetString("isdst", LFalse)
113			L.Push(ret)
114			return 1
115		}
116	}
117	L.Push(LString(strftime(t, cfmt)))
118	return 1
119}
120
121func osGetEnv(L *LState) int {
122	v := os.Getenv(L.CheckString(1))
123	if len(v) == 0 {
124		L.Push(LNil)
125	} else {
126		L.Push(LString(v))
127	}
128	return 1
129}
130
131func osRemove(L *LState) int {
132	err := os.Remove(L.CheckString(1))
133	if err != nil {
134		L.Push(LNil)
135		L.Push(LString(err.Error()))
136		return 2
137	} else {
138		L.Push(LTrue)
139		return 1
140	}
141}
142
143func osRename(L *LState) int {
144	err := os.Rename(L.CheckString(1), L.CheckString(2))
145	if err != nil {
146		L.Push(LNil)
147		L.Push(LString(err.Error()))
148		return 2
149	} else {
150		L.Push(LTrue)
151		return 1
152	}
153}
154
155func osSetLocale(L *LState) int {
156	// setlocale is not supported
157	L.Push(LFalse)
158	return 1
159}
160
161func osSetEnv(L *LState) int {
162	err := os.Setenv(L.CheckString(1), L.CheckString(2))
163	if err != nil {
164		L.Push(LNil)
165		L.Push(LString(err.Error()))
166		return 2
167	} else {
168		L.Push(LTrue)
169		return 1
170	}
171}
172
173func osTime(L *LState) int {
174	if L.GetTop() == 0 {
175		L.Push(LNumber(time.Now().Unix()))
176	} else {
177		tbl := L.CheckTable(1)
178		sec := getIntField(L, tbl, "sec", 0)
179		min := getIntField(L, tbl, "min", 0)
180		hour := getIntField(L, tbl, "hour", 12)
181		day := getIntField(L, tbl, "day", -1)
182		month := getIntField(L, tbl, "month", -1)
183		year := getIntField(L, tbl, "year", -1)
184		isdst := getBoolField(L, tbl, "isdst", false)
185		t := time.Date(year, time.Month(month), day, hour, min, sec, 0, time.Local)
186		// TODO dst
187		if false {
188			print(isdst)
189		}
190		L.Push(LNumber(t.Unix()))
191	}
192	return 1
193}
194
195func osTmpname(L *LState) int {
196	file, err := ioutil.TempFile("", "")
197	if err != nil {
198		L.RaiseError("unable to generate a unique filename")
199	}
200	file.Close()
201	os.Remove(file.Name()) // ignore errors
202	L.Push(LString(file.Name()))
203	return 1
204}
205
206//
207