l-os.lua /size: 20 Kb    last modification: 2021-10-28 13:50
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
l-os
'
]
=
{
2
version
=
1
.
001
,
3
comment
=
"
companion to luat-lib.mkiv
"
,
4
author
=
"
Hans Hagen, PRAGMA-ADE, Hasselt NL
"
,
5
copyright
=
"
PRAGMA ADE / ConTeXt Development Team
"
,
6
license
=
"
see context related readme files
"
7
}
8 9
-- This file deals with some operating system issues. Please don't bother me
10
-- with the pros and cons of operating systems as they all have their flaws
11
-- and benefits. Bashing one of them won't help solving problems and fixing
12
-- bugs faster and is a waste of time and energy.
13
--
14
-- path separators: / or \ ... we can use / everywhere
15
-- suffixes : dll so exe <none> ... no big deal
16
-- quotes : we can use "" in most cases
17
-- expansion : unless "" are used * might give side effects
18
-- piping/threads : somewhat different for each os
19
-- locations : specific user file locations and settings can change over time
20
--
21
-- os.type : windows | unix (new, we already guessed os.platform)
22
-- os.name : windows | msdos | linux | macosx | solaris | .. | generic (new)
23
-- os.platform : extended os.name with architecture
24 25
-- os.sleep() => socket.sleep()
26
-- math.randomseed(tonumber(string.sub(string.reverse(tostring(math.floor(socket.gettime()*10000))),1,6)))
27 28
local
os
=
os
29
local
date
,
time
,
difftime
=
os
.
date
,
os
.
time
,
os
.
difftime
30
local
find
,
format
,
gsub
,
upper
,
gmatch
=
string
.
find
,
string
.
format
,
string
.
gsub
,
string
.
upper
,
string
.
gmatch
31
local
concat
=
table
.
concat
32
local
random
,
ceil
,
randomseed
,
modf
=
math
.
random
,
math
.
ceil
,
math
.
randomseed
,
math
.
modf
33
local
type
,
setmetatable
,
tonumber
,
tostring
=
type
,
setmetatable
,
tonumber
,
tostring
34 35
-- This check needs to happen real early on. Todo: we can pick it up from the commandline
36
-- if we pass --binpath= (which is useful anyway)
37 38
do
39 40
local
selfdir
=
os
.
selfdir
41 42
if
selfdir
=
=
"
"
then
43
selfdir
=
nil
44
end
45 46
if
not
selfdir
then
47 48
-- We need a fallback plan so let's see what we get.
49 50
if
arg
then
51
-- passed by mtx-context ... saves network access
52
for
i
=
1
,
#
arg
do
53
local
a
=
arg
[
i
]
54
if
find
(
a
,
"
^%-%-[c:]*texmfbinpath=
"
)
then
55
selfdir
=
gsub
(
a
,
"
^.-=
"
,
"
"
)
56
break
57
end
58
end
59
end
60 61
if
not
selfdir
then
62
selfdir
=
os
.
selfbin
or
"
luatex
"
63
if
find
(
selfdir
,
"
[/\\]
"
)
then
64
selfdir
=
gsub
(
selfdir
,
"
[/\\][^/\\]*$
"
,
"
"
)
65
elseif
os
.
getenv
then
66
local
path
=
os
.
getenv
(
"
PATH
"
)
67
local
name
=
gsub
(
selfdir
,
"
^.*[/\\][^/\\]
"
,
"
"
)
68
local
patt
=
"
[^:]+
"
69
if
os
.
type
=
=
"
windows
"
then
70
patt
=
"
[^;]+
"
71
name
=
name
.
.
"
.exe
"
72
end
73
local
isfile
74
if
lfs
then
75
-- we're okay as lfs is assumed present
76
local
attributes
=
lfs
.
attributes
77
isfile
=
function
(
name
)
78
local
a
=
attributes
(
name
,
"
mode
"
)
79
return
a
=
=
"
file
"
or
a
=
=
"
link
"
or
nil
80
end
81
else
82
-- we're not okay and much will not work as we miss lfs
83
local
open
=
io
.
open
84
isfile
=
function
(
name
)
85
local
f
=
open
(
name
)
86
if
f
then
87
f
:
close
(
)
88
return
true
89
end
90
end
91
end
92
for
p
in
gmatch
(
path
,
patt
)
do
93
-- possible speedup: there must be tex in 'p'
94
if
isfile
(
p
.
.
"
/
"
.
.
name
)
then
95
selfdir
=
p
96
break
97
end
98
end
99
end
100
end
101 102
-- let's hope we're okay now
103 104
os
.
selfdir
=
selfdir
or
"
.
"
105 106
end
107 108
-- print(os.selfdir) os.exit()
109 110
end
111 112
-- The following code permits traversing the environment table, at least in luatex. Internally all
113
-- environment names are uppercase.
114 115
-- The randomseed in Lua is not that random, although this depends on the operating system as well
116
-- as the binary (Luatex is normally okay). But to be sure we set the seed anyway. It will be better
117
-- in Lua 5.4 (according to the announcements.)
118 119
math
.
initialseed
=
tonumber
(
string
.
sub
(
string
.
reverse
(
tostring
(
ceil
(
socket
and
socket
.
gettime
(
)
*
10000
or
time
(
)
)
)
)
,
1
,
6
)
)
120 121
randomseed
(
math
.
initialseed
)
122 123
if
not
os
.
__getenv__
then
124 125
os
.
__getenv__
=
os
.
getenv
126
os
.
__setenv__
=
os
.
setenv
127 128
if
os
.
env
then
129 130
local
osgetenv
=
os
.
getenv
131
local
ossetenv
=
os
.
setenv
132
local
osenv
=
os
.
env
local
_
=
osenv
.
PATH
-- initialize the table
133 134
function
os
.
setenv
(
k
,
v
)
135
if
v
=
=
nil
then
136
v
=
"
"
137
end
138
local
K
=
upper
(
k
)
139
osenv
[
K
]
=
v
140
if
type
(
v
)
=
=
"
table
"
then
141
v
=
concat
(
v
,
"
;
"
)
-- path
142
end
143
ossetenv
(
K
,
v
)
144
end
145 146
function
os
.
getenv
(
k
)
147
local
K
=
upper
(
k
)
148
local
v
=
osenv
[
K
]
or
osenv
[
k
]
or
osgetenv
(
K
)
or
osgetenv
(
k
)
149
if
v
=
=
"
"
then
150
return
nil
151
else
152
return
v
153
end
154
end
155 156
else
157 158
local
ossetenv
=
os
.
setenv
159
local
osgetenv
=
os
.
getenv
160
local
osenv
=
{
}
161 162
function
os
.
setenv
(
k
,
v
)
163
if
v
=
=
nil
then
164
v
=
"
"
165
end
166
local
K
=
upper
(
k
)
167
osenv
[
K
]
=
v
168
end
169 170
function
os
.
getenv
(
k
)
171
local
K
=
upper
(
k
)
172
local
v
=
osenv
[
K
]
or
osgetenv
(
K
)
or
osgetenv
(
k
)
173
if
v
=
=
"
"
then
174
return
nil
175
else
176
return
v
177
end
178
end
179 180
local
function
__index
(
t
,
k
)
181
return
os
.
getenv
(
k
)
182
end
183
local
function
__newindex
(
t
,
k
,
v
)
184
os
.
setenv
(
k
,
v
)
185
end
186 187
os
.
env
=
{
}
188 189
setmetatable
(
os
.
env
,
{
__index
=
__index
,
__newindex
=
__newindex
}
)
190 191
end
192 193
end
194 195
-- end of environment hack
196 197
if
not
io
.
fileseparator
then
198 199
if
find
(
os
.
getenv
(
"
PATH
"
)
,
"
;
"
,
1
,
true
)
then
200
io
.
fileseparator
,
io
.
pathseparator
,
os
.
type
=
"
\\
"
,
"
;
"
,
os
.
type
or
"
windows
"
201
else
202
io
.
fileseparator
,
io
.
pathseparator
,
os
.
type
=
"
/
"
,
"
:
"
,
os
.
type
or
"
unix
"
203
end
204 205
end
206 207
os
.
type
=
os
.
type
or
(
io
.
pathseparator
=
=
"
;
"
and
"
windows
"
)
or
"
unix
"
208
os
.
name
=
os
.
name
or
(
os
.
type
=
=
"
windows
"
and
"
mswin
"
)
or
"
linux
"
209 210
if
os
.
type
=
=
"
windows
"
then
211
os
.
libsuffix
,
os
.
binsuffix
,
os
.
binsuffixes
=
'
dll
'
,
'
exe
'
,
{
'
exe
'
,
'
cmd
'
,
'
bat
'
}
212
else
213
os
.
libsuffix
,
os
.
binsuffix
,
os
.
binsuffixes
=
'
so
'
,
'
'
,
{
'
'
}
214
end
215 216
do
217 218
local
execute
=
os
.
execute
219
local
iopopen
=
io
.
popen
220
local
ostype
=
os
.
type
221 222
local
function
resultof
(
command
)
223
-- already has flush, b is new and we need it to pipe xz output
224
local
handle
=
iopopen
(
command
,
ostype
=
=
"
windows
"
and
"
rb
"
or
"
r
"
)
225
if
handle
then
226
local
result
=
handle
:
read
(
"
*all
"
)
or
"
"
227
handle
:
close
(
)
228
return
result
229
else
230
return
"
"
231
end
232
end
233 234
os
.
resultof
=
resultof
235 236
function
os
.
pipeto
(
command
)
237
return
iopopen
(
command
,
"
w
"
)
-- already has flush
238
end
239 240
local
launchers
=
{
241
windows
=
"
start %s
"
,
242
macosx
=
"
open %s
"
,
243
unix
=
"
xdg-open %s &> /dev/null &
"
,
244
}
245 246
function
os
.
launch
(
str
)
247
local
command
=
format
(
launchers
[
os
.
name
]
or
launchers
.
unix
,
str
)
248
-- todo: pcall
249
-- print(command)
250
execute
(
command
)
251
end
252 253
end
254 255
do
256 257
local
gettimeofday
=
os
.
gettimeofday
or
os
.
clock
258
os
.
gettimeofday
=
gettimeofday
259 260
local
startuptime
=
gettimeofday
(
)
261 262
function
os
.
runtime
(
)
263
return
gettimeofday
(
)
-
startuptime
264
end
265 266
-- print(os.gettimeofday()-os.time())
267
-- os.sleep(1.234)
268
-- print (">>",os.runtime())
269
-- print(os.date("%H:%M:%S",os.gettimeofday()))
270
-- print(os.date("%H:%M:%S",os.time()))
271 272
end
273 274
-- We can use HOSTTYPE on some platforms (but not consistently on e.g. Linux).
275
--
276
-- os.bits = 32 | 64
277
--
278
-- os.uname() : return {
279
-- machine = "x86_64",
280
-- nodename = "MYLAPTOP",
281
-- release = "build 9200",
282
-- sysname = "Windows",
283
-- version = "6.02",
284
-- }
285 286
do
287 288
local
name
=
os
.
name
or
"
linux
"
289
local
platform
=
os
.
getenv
(
"
MTX_PLATFORM
"
)
or
"
"
290
local
architecture
=
os
.
uname
and
os
.
uname
(
)
.
machine
-- lmtx
291
local
bits
=
os
.
getenv
(
"
MTX_BITS
"
)
or
find
(
platform
,
"
64
"
)
and
64
or
32
292 293
if
platform
~
=
"
"
then
294 295
-- we're okay already
296 297
elseif
os
.
type
=
=
"
windows
"
then
298 299
-- PROCESSOR_ARCHITECTURE : binary platform
300
-- PROCESSOR_ARCHITEW6432 : OS platform
301 302
architecture
=
string
.
lower
(
architecture
or
os
.
getenv
(
"
PROCESSOR_ARCHITECTURE
"
)
or
"
"
)
303
if
architecture
=
=
"
x86_64
"
then
304
bits
,
platform
=
64
,
"
win64
"
305
elseif
find
(
architecture
,
"
amd64
"
)
then
306
bits
,
platform
=
64
,
"
win64
"
307
elseif
find
(
architecture
,
"
arm64
"
)
then
308
bits
,
platform
=
64
,
"
windows-arm64
"
309
elseif
find
(
architecture
,
"
arm32
"
)
then
310
bits
,
platform
=
32
,
"
windows-arm32
"
311
else
312
bits
,
platform
=
32
,
"
mswin
"
313
end
314 315
elseif
name
=
=
"
linux
"
then
316 317
-- There is no way to detect if musl is used because there is no __MUSL__
318
-- and it looks like there never will be. Folks don't care about cases where
319
-- one ships multipe binaries (as with TeX distibutions) and want to select
320
-- the right one. So probably it expects users to compile locally in which
321
-- case we don't care to much as they can then sort it out.
322 323
architecture
=
architecture
or
os
.
getenv
(
"
HOSTTYPE
"
)
or
resultof
(
"
uname -m
"
)
or
"
"
324
local
musl
=
find
(
os
.
selfdir
or
"
"
,
"
linuxmusl
"
)
325
if
find
(
architecture
,
"
x86_64
"
)
then
326
bits
,
platform
=
64
,
musl
and
"
linuxmusl
"
or
"
linux-64
"
327
elseif
find
(
architecture
,
"
ppc
"
)
then
328
bits
,
platform
=
32
,
"
linux-ppc
"
-- this will be dropped
329
else
330
bits
,
platform
=
32
,
musl
and
"
linuxmusl
"
or
"
linux
"
331
end
332 333
elseif
name
=
=
"
macosx
"
then
334 335
-- Identifying the architecture of OSX is quite a mess and this is the best
336
-- we can come up with. For some reason $HOSTTYPE is a kind of pseudo
337
-- environment variable, not known to the current environment. And yes,
338
-- uname cannot be trusted either, so there is a change that you end up with
339
-- a 32 bit run on a 64 bit system. Also, some proper 64 bit intel macs are
340
-- too cheap (low-end) and therefore not permitted to run the 64 bit kernel.
341 342
architecture
=
architecture
or
resultof
(
"
echo $HOSTTYPE
"
)
or
"
"
343
if
architecture
=
=
"
"
then
344
bits
,
platform
=
64
,
"
osx-intel
"
345
elseif
find
(
architecture
,
"
i386
"
)
then
346
bits
,
platform
=
64
,
"
osx-intel
"
347
elseif
find
(
architecture
,
"
x86_64
"
)
then
348
bits
,
platform
=
64
,
"
osx-64
"
349
elseif
find
(
architecture
,
"
arm64
"
)
then
350
bits
,
platform
=
64
,
"
osx-arm
"
351
else
352
bits
,
platform
=
32
,
"
osx-ppc
"
353
end
354 355
elseif
name
=
=
"
sunos
"
then
356 357
architecture
=
architecture
or
resultof
(
"
uname -m
"
)
or
"
"
358
if
find
(
architecture
,
"
sparc
"
)
then
359
bits
,
platform
=
32
,
"
solaris-sparc
"
360
else
-- if architecture == 'i86pc'
361
bits
,
platform
=
32
,
"
solaris-intel
"
362
end
363 364
elseif
name
=
=
"
freebsd
"
then
365 366
architecture
=
architecture
or
os
.
getenv
(
"
MACHTYPE
"
)
or
resultof
(
"
uname -m
"
)
or
"
"
367
if
find
(
architecture
,
"
amd64
"
)
or
find
(
architecture
,
"
AMD64
"
)
then
368
bits
,
platform
=
64
,
"
freebsd-amd64
"
369
else
370
bits
,
platform
=
32
,
"
freebsd
"
371
end
372 373
elseif
name
=
=
"
kfreebsd
"
then
374 375
architecture
=
architecture
or
os
.
getenv
(
"
HOSTTYPE
"
)
or
resultof
(
"
uname -m
"
)
or
"
"
376
if
architecture
=
=
"
x86_64
"
then
377
bits
,
platform
=
64
,
"
kfreebsd-amd64
"
378
else
379
bits
,
platform
=
32
,
"
kfreebsd-i386
"
380
end
381 382
else
383 384
architecture
=
architecture
or
resultof
(
"
uname -m
"
)
or
"
"
385 386
if
find
(
architecture
,
"
aarch64
"
)
then
387
bits
,
platform
=
"
linux-aarch64
"
388
elseif
find
(
architecture
,
"
armv7l
"
)
then
389
-- linux-armel
390
bits
,
platform
=
32
,
"
linux-armhf
"
391
elseif
find
(
architecture
,
"
mips64
"
)
or
find
(
architecture
,
"
mips64el
"
)
then
392
bits
,
platform
=
64
,
"
linux-mipsel
"
393
elseif
find
(
architecture
,
"
mipsel
"
)
or
find
(
architecture
,
"
mips
"
)
then
394
bits
,
platform
=
32
,
"
linux-mipsel
"
395
else
396
bits
,
platform
=
64
,
"
linux-64
"
-- was 32, "linux"
397
end
398 399
end
400 401
os
.
setenv
(
"
MTX_PLATFORM
"
,
platform
)
402
os
.
setenv
(
"
MTX_BITS
"
,
bits
)
403 404
os
.
platform
=
platform
405
os
.
bits
=
bits
406
os
.
newline
=
name
=
=
"
windows
"
and
"
\013\010
"
or
"
\010
"
-- crlf or lf
407 408
end
409 410
-- beware, we set the randomseed
411 412
-- From wikipedia: Version 4 UUIDs use a scheme relying only on random numbers. This
413
-- algorithm sets the version number as well as two reserved bits. All other bits
414
-- are set using a random or pseudorandom data source. Version 4 UUIDs have the form
415
-- xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx with hexadecimal digits x and hexadecimal
416
-- digits 8, 9, A, or B for y. e.g. f47ac10b-58cc-4372-a567-0e02b2c3d479. As we don't
417
-- call this function too often there is not so much risk on repetition.
418 419
do
420 421
local
t
=
{
8
,
9
,
"
a
"
,
"
b
"
}
422 423
function
os
.
uuid
(
)
424
return
format
(
"
%04x%04x-4%03x-%s%03x-%04x-%04x%04x%04x
"
,
425
random
(
0xFFFF
)
,
random
(
0xFFFF
)
,
426
random
(
0x0FFF
)
,
427
t
[
ceil
(
random
(
4
)
)
]
or
8
,
random
(
0x0FFF
)
,
428
random
(
0xFFFF
)
,
429
random
(
0xFFFF
)
,
random
(
0xFFFF
)
,
random
(
0xFFFF
)
430
)
431
end
432 433
end
434 435
do
436 437
-- this is fragile because it depends on time and so we only check once during
438
-- a run (the computer doesn't move zones) .. Michal Vlasák made a better one
439 440
-- local d
441
--
442
-- function os.timezone()
443
-- d = d or ((tonumber(date("%H")) or 0) - (tonumber(date("!%H")) or 0))
444
-- if d > 0 then
445
-- return format("+%02i:00",d)
446
-- else
447
-- return format("-%02i:00",-d)
448
-- end
449
-- end
450 451
local
hour
,
min
452 453
function
os
.
timezone
(
difference
)
454
if
not
hour
then
455
-- somehow looks too complex:
456
local
current
=
time
(
)
457
local
utcdate
=
date
(
"
!*t
"
,
current
)
458
local
localdate
=
date
(
"
*t
"
,
current
)
459
localdate
.
isdst
=
false
460
local
timediff
=
difftime
(
time
(
localdate
)
,
time
(
utcdate
)
)
461
hour
,
min
=
modf
(
timediff
/
3600
)
462
min
=
min
*
60
463
end
464
if
difference
then
465
return
hour
,
min
466
else
467
return
format
(
"
%+03d:%02d
"
,
hour
,
min
)
-- %+ means: always show sign
468
end
469
end
470 471
-- localtime with timezone: 2021-10-22 10:22:54+02:00
472 473
local
timeformat
=
format
(
"
%%s%s
"
,
os
.
timezone
(
)
)
474
local
dateformat
=
"
%Y-%m-%d %H:%M:%S
"
475
local
lasttime
=
nil
476
local
lastdate
=
nil
477 478
function
os
.
fulltime
(
t
,
default
)
479
t
=
t
and
tonumber
(
t
)
or
0
480
if
t
>
0
then
481
-- valid time
482
elseif
default
then
483
return
default
484
else
485
t
=
time
(
)
486
end
487
if
t
~
=
lasttime
then
488
lasttime
=
t
489
lastdate
=
format
(
timeformat
,
date
(
dateformat
)
)
490
end
491
return
lastdate
492
end
493 494
-- localtime without timezone: 2021-10-22 10:22:54
495 496
local
dateformat
=
"
%Y-%m-%d %H:%M:%S
"
497
local
lasttime
=
nil
498
local
lastdate
=
nil
499 500
function
os
.
localtime
(
t
,
default
)
501
t
=
t
and
tonumber
(
t
)
or
0
502
if
t
>
0
then
503
-- valid time
504
elseif
default
then
505
return
default
506
else
507
t
=
time
(
)
508
end
509
if
t
~
=
lasttime
then
510
lasttime
=
t
511
lastdate
=
date
(
dateformat
,
t
)
512
end
513
return
lastdate
514
end
515 516
function
os
.
converttime
(
t
,
default
)
517
local
t
=
tonumber
(
t
)
518
if
t
and
t
>
0
then
519
return
date
(
dateformat
,
t
)
520
else
521
return
default
or
"
-
"
522
end
523
end
524 525
-- table with values
526 527
function
os
.
today
(
)
528
return
date
(
"
!*t
"
)
529
end
530 531
-- utc time without timezone: 2021-10-22 08:22:54
532 533
function
os
.
now
(
)
534
return
date
(
"
!%Y-%m-%d %H:%M:%S
"
)
535
end
536 537
end
538 539
do
540 541
local
cache
=
{
}
542 543
local
function
which
(
filename
)
544
local
fullname
=
cache
[
filename
]
545
if
fullname
=
=
nil
then
546
local
suffix
=
file
.
suffix
(
filename
)
547
local
suffixes
=
suffix
=
=
"
"
and
os
.
binsuffixes
or
{
suffix
}
548
for
directory
in
gmatch
(
os
.
getenv
(
"
PATH
"
)
,
"
[^
"
.
.
io
.
pathseparator
.
.
"
]+
"
)
do
549
local
df
=
file
.
join
(
directory
,
filename
)
550
for
i
=
1
,
#
suffixes
do
551
local
dfs
=
file
.
addsuffix
(
df
,
suffixes
[
i
]
)
552
if
io
.
exists
(
dfs
)
then
553
fullname
=
dfs
554
break
555
end
556
end
557
end
558
if
not
fullname
then
559
fullname
=
false
560
end
561
cache
[
filename
]
=
fullname
562
end
563
return
fullname
564
end
565 566
os
.
which
=
which
567
os
.
where
=
which
568 569
-- print(os.which("inkscape.exe"))
570
-- print(os.which("inkscape"))
571
-- print(os.which("gs.exe"))
572
-- print(os.which("ps2pdf"))
573 574
end
575 576
if
not
os
.
sleep
then
577 578
local
socket
=
socket
579 580
function
os
.
sleep
(
n
)
581
if
not
socket
then
582
-- so we delay ... if os.sleep is really needed then one should also
583
-- be sure that socket can be found
584
socket
=
require
(
"
socket
"
)
585
end
586
socket
.
sleep
(
n
)
587
end
588 589
end
590 591
-- These are moved from core-con.lua (as I needed them elsewhere).
592 593
do
594 595
local
function
isleapyear
(
year
)
-- timed for bram's cs practicum
596
-- return (year % 400 == 0) or (year % 100 ~= 0 and year % 4 == 0) -- 3:4:1600:1900 = 9.9 : 8.2 : 5.0 : 6.8 (29.9)
597
return
(
year
%
4
=
=
0
)
and
(
year
%
100
~
=
0
or
year
%
400
=
=
0
)
-- 3:4:1600:1900 = 5.1 : 6.5 : 8.1 : 10.2 (29.9)
598
-- return (year % 4 == 0) and (year % 400 == 0 or year % 100 ~= 0) -- 3:4:1600:1900 = 5.2 : 8.5 : 6.8 : 10.1 (30.6)
599
end
600 601
os
.
isleapyear
=
isleapyear
602 603
-- nicer:
604
--
605
-- local days = {
606
-- [false] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
607
-- [true] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
608
-- }
609
--
610
-- local function nofdays(year,month)
611
-- return days[isleapyear(year)][month]
612
-- return month == 2 and isleapyear(year) and 29 or days[month]
613
-- end
614
--
615
-- more efficient:
616 617
local
days
=
{
31
,
28
,
31
,
30
,
31
,
30
,
31
,
31
,
30
,
31
,
30
,
31
}
618 619
local
function
nofdays
(
year
,
month
,
day
)
620
if
not
month
then
621
return
isleapyear
(
year
)
and
365
or
364
622
elseif
not
day
then
623
return
month
=
=
2
and
isleapyear
(
year
)
and
29
or
days
[
month
]
624
else
625
for
i
=
1
,
month
-1
do
626
day
=
day
+
days
[
i
]
627
end
628
if
month
>
2
and
isleapyear
(
year
)
then
629
day
=
day
+
1
630
end
631
return
day
632
end
633
end
634 635
os
.
nofdays
=
nofdays
636 637
function
os
.
weekday
(
day
,
month
,
year
)
638
return
date
(
"
%w
"
,
time
{
year
=
year
,
month
=
month
,
day
=
day
}
)
+
1
639
end
640 641
function
os
.
validdate
(
year
,
month
,
day
)
642
-- we assume that all three values are set
643
-- year is always ok, even if lua has a 1970 time limit
644
if
month
<
1
then
645
month
=
1
646
elseif
month
>
12
then
647
month
=
12
648
end
649
if
day
<
1
then
650
day
=
1
651
else
652
local
max
=
nofdays
(
year
,
month
)
653
if
day
>
max
then
654
day
=
max
655
end
656
end
657
return
year
,
month
,
day
658
end
659 660
function
os
.
date
(
fmt
,
...
)
661
if
not
fmt
then
662
-- otherwise differences between unix, mingw and msvc
663
fmt
=
"
%Y-%m-%d %H:%M
"
664
end
665
return
date
(
fmt
,
...
)
666
end
667 668
end
669 670
do
671 672
local
osexit
=
os
.
exit
673
local
exitcode
=
nil
674 675
function
os
.
setexitcode
(
code
)
676
exitcode
=
code
677
end
678 679
function
os
.
exit
(
c
)
680
if
exitcode
~
=
nil
then
681
return
osexit
(
exitcode
)
682
end
683
if
c
~
=
nil
then
684
return
osexit
(
c
)
685
end
686
return
osexit
(
)
687
end
688 689
end
690