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