#! /usr/bin/env perl
my
$globalL
;
my
$progname
=
'lua'
;
main();
sub
LUA_QL {
return
"'"
.
shift
() .
"'"
};
sub
lua_stdin_is_tty { 1; }
{
my
$term
;
INIT {
$term
= Term::ReadLine->new(
'lua'
); }
sub
readline
{
my
(
$L
,
$b
,
$prompt
) =
@_
;
my
$line
=
$term
->
readline
(
$prompt
);
if
(
defined
$line
)
{
$$b
=
$line
;
return
1;
}
return
0;
}
sub
saveline {
my
(
$L
,
$idx
) =
@_
;
if
(
$L
->strlen(
$idx
) > 0 )
{
$term
->addhistory(
$L
->tostring(
$idx
) );
}
}
sub
freeline { }
sub
assert {}
}
sub
lstop {
my
(
$L
,
$ar
) =
@_
;
$L
->sethook(
undef
, 0, 0);
$L
->error(
"interrupted!"
);
}
sub
laction {
$SIG
{
'INT'
} =
'DEFAULT'
;
$globalL
->sethook( lstop, Lua::API::MASKCALL | Lua::API::MASKRET | Lua::API::MASKCOUNT, 1);
}
sub
print_usage {
print
STDERR
"usage: $0 [options] [script [args]].\n"
,
"Available options are:\n"
.
" -e stat execute string "
. LUA_QL(
"stat"
) .
"\n"
.
" -l name require library "
. LUA_QL(
"name"
) .
"\n"
.
" -i enter interactive mode after executing "
. LUA_QL(
"script"
) .
"\n"
.
" -v show version information\n"
.
" -- stop handling options\n"
.
" - execute stdin and stop handling options\n"
;
}
sub
l_message {
my
(
$pname
,
$msg
) =
@_
;
print
STDERR
"$pname: "
if
defined
$pname
;
print
STDERR
"$msg\n"
;
}
sub
report {
my
(
$L
,
$status
) =
@_
;
if
(
$status
&& !
$L
->isnil(-1))
{
my
$msg
=
$L
->tostring(-1);
$msg
=
"(error object is not a string)"
if
!
defined
$msg
;
l_message(
$progname
,
$msg
);
$L
->
pop
( 1);
}
return
$status
;
}
sub
traceback {
my
(
$L
) =
@_
;
return
1
if
(!
$L
->isstring(1));
$L
->getfield( Lua::API::GLOBALSINDEX,
"debug"
);
if
(!
$L
->istable(-1))
{
$L
->
pop
( 1);
return
1;
}
$L
->getfield(-1,
"traceback"
);
if
(!
$L
->isfunction( -1))
{
$L
->
pop
(2);
return
1;
}
$L
->pushvalue(1);
$L
->pushinteger(2);
$L
->call( 2, 1);
return
1;
}
sub
docall {
my
(
$L
,
$narg
,
$clear
) =
@_
;
my
$base
=
$L
->gettop() -
$narg
;
$L
->pushcfunction( \
&traceback
);
$L
->insert(
$base
);
$SIG
{
'INT'
} = \
&laction
;
my
$status
=
$L
->pcall(
$narg
, (
$clear
? 0 : Lua::API::MULTRET),
$base
);
$SIG
{
'INT'
} =
'DEFAULT'
;
$L
->remove(
$base
);
$L
->gc( Lua::API::GCCOLLECT, 0)
if
$status
!= 0;
return
$status
;
}
sub
print_version {
l_message(
undef
, Lua::API::RELEASE .
" "
. Lua::API::COPYRIGHT);
}
sub
getargs {
my
(
$L
,
$argv
,
$n
) =
@_
;
my
$narg
=
@$argv
- (
$n
+ 1);
$L
->checkstack(
$narg
+ 3,
"too many arguments to script"
);
$L
->pushstring(
$argv
->[
$_
])
for
(
$n
+1 ..
@$argv
-1 );
$L
->createtable(
$narg
,
$n
+1);
for
my
$i
( 0..
@$argv
-1)
{
$L
->pushstring(
$argv
->[
$i
]);
$L
->rawseti( -2,
$i
-
$n
);
}
return
$narg
;
}
sub
dofile {
my
(
$L
,
$name
) =
@_
;
my
$status
=
$L
->loadfile(
$name
) || docall(
$L
, 0, 1);
return
report(
$L
,
$status
);
}
sub
dostring {
my
(
$L
,
$s
,
$name
) =
@_
;
my
$status
=
$L
->loadbuffer(
$s
,
length
(
$s
),
$name
) || docall(
$L
, 0, 1);
return
report(
$L
,
$status
);
}
sub
dolibrary {
my
(
$L
,
$name
) =
@_
;
$L
->getglobal(
"require"
);
$L
->pushstring(
$name
);
return
report(
$L
, docall(
$L
, 1, 1));
}
sub
get_prompt {
my
(
$L
,
$firstline
) =
@_
;
$L
->getfield( Lua::API::GLOBALSINDEX,
$firstline
?
"_PROMPT"
:
"_PROMPT2"
);
my
$p
=
$L
->tostring( -1);
$p
= (
$firstline
?
'> '
:
'>> '
)
if
!
defined
$p
;
$L
->
pop
( 1);
return
$p
;
}
sub
incomplete {
my
(
$L
,
$status
) =
@_
;
if
(
$status
== Lua::API::ERRSYNTAX)
{
my
$lmsg
;
my
$msg
=
$L
->tolstring( -1, \
$lmsg
);
my
$eof
= LUA_QL(
"<eof>"
);
if
(
$msg
=~ /
$eof
$/ )
{
$L
->
pop
( 1);
return
1;
}
}
return
0;
}
sub
pushline {
my
(
$L
,
$firstline
) =
@_
;
my
$b
;
my
$prmt
= get_prompt(
$L
,
$firstline
);
return
0
if
$L
->
readline
( \
$b
,
$prmt
) == 0;
chomp
$b
;
my
$l
=
length
(
$b
);
if
(
$firstline
&&
substr
(
$b
, 0, 1 ) eq
'='
)
{
$L
->pushfstring(
"return %s"
,
$b
+1);
}
else
{
$L
->pushstring(
$b
);
}
$L
->freeline(
$b
);
return
1;
}
sub
loadline {
my
(
$L
) =
@_
;
$L
->settop( 0);
return
-1
if
! pushline(
$L
, 1 );
my
$status
;
for
(;;)
{
$status
=
$L
->loadbuffer(
$L
->tostring( 1),
$L
->strlen( 1),
"=stdin"
);
last
if
!incomplete(
$L
,
$status
);
return
-1
if
!pushline(
$L
, 0);
$L
->pushliteral(
"\n"
);
$L
->insert( -2);
$L
->concat( 3);
}
$L
->saveline( 1);
$L
->remove( 1);
return
$status
;
}
sub
dotty {
my
(
$L
) =
@_
;
my
$oldprogname
=
$progname
;
$progname
=
undef
;
my
$status
;
while
((
$status
= loadline(
$L
)) != -1)
{
$status
= docall(
$L
, 0, 0)
if
$status
== 0;
report(
$L
,
$status
);
if
(
$status
== 0 &&
$L
->gettop > 0)
{
$L
->getglobal(
"print"
);
$L
->insert( 1);
if
(
$L
->pcall(
$L
->gettop-1, 0, 0) != 0)
{
l_message(
$progname
,
$L
->pushfstring(
"error calling "
. LUA_QL(
"print"
) .
" (%s)"
,
$L
->tostring( -1)));
}
}
}
$L
->settop(0);
print
STDOUT
"\n"
;
STDOUT->flush;
$progname
=
$oldprogname
;
return
;
}
sub
handle_script {
my
(
$L
,
$argv
,
$n
) =
@_
;
my
$narg
= getargs(
$L
,
$argv
,
$n
);
$L
->setglobal(
"arg"
);
my
$fname
=
$argv
->[
$n
];
if
(
$fname
eq
'-'
&&
$argv
->[
$n
-1] ne
"--"
)
{
$fname
=
undef
;
}
my
$status
=
$L
->loadfile(
$fname
);
$L
->insert( -(
$narg
+1));
if
(
$status
== 0 )
{
$ status = docall(
$L
,
$narg
, 0);
}
else
{
$L
->
pop
(
$narg
);
}
return
report(
$L
,
$status
);
}
sub
collectargs {
my
(
$argv
,
$pi
,
$pv
,
$pe
) =
@_
;
for
(
my
$i
= 0 ;
$i
<
@$argv
;
$i
++ )
{
if
(
substr
(
$argv
->[
$i
], 0, 1 ) ne
'-'
)
{
return
$i
; }
my
$opt
=
substr
(
$argv
->[
$i
], 1, 1 );
if
(
$opt
eq
'-'
)
{
return
-1
if
length
(
$argv
->[
$i
] ) != 2;
return
(
@$argv
>
$i
+1 ?
$i
+1 : 0);
}
elsif
( !
defined
$opt
)
{
return
$i
;
}
elsif
(
$opt
eq
'i'
)
{
return
-1
if
length
(
$argv
->[
$i
] ) != 2;
$$pi
= 1;
$$pv
= 1;
}
elsif
(
$opt
eq
'v'
)
{
return
-1
if
length
(
$argv
->[
$i
] ) != 2;
$$pv
= 1;
}
elsif
(
$opt
eq
'e'
||
$opt
eq
'l'
)
{
$$pe
= 1
if
$opt
eq
'e'
;
if
(
length
(
$argv
->[
$i
]) == 2)
{
$i
++;
return
-1
if
@$argv
==
$i
;
}
}
else
{
return
-1;
}
}
return
0;
}
sub
runargs {
my
(
$L
,
$argv
,
$n
) =
@_
;
for
(
my
$i
= 0;
$i
<
$n
;
$i
++)
{
next
if
( !
defined
$argv
->[
$i
] );
$L
->assert(
$argv
->[
$i
] =~ /^-/ );
if
(
$argv
->[
$i
] =~ /^-e/ )
{
my
$chunk
=
substr
(
$argv
->[
$i
], 2 );
$chunk
=
$argv
->[++
$i
]
if
length
(
$chunk
) == 0;
$L
->assert(
length
(
$chunk
) );
if
(dostring(
$L
,
$chunk
,
"=(command line)"
) != 0)
{
return
1; }
}
elsif
(
$argv
->[
$i
] =~ /^-l/ )
{
my
$filename
=
substr
(
$argv
->[
$i
], 2 );
$filename
=
$argv
->[++
$i
]
if
length
(
$filename
) == 0;
$L
->assert(
length
(
$filename
) );
if
(dolibrary(
$L
,
$filename
))
{
return
1; }
}
}
return
0;
}
sub
handle_luainit {
my
(
$L
) =
@_
;
if
( !
defined
$ENV
{LUA_INIT} )
{
return
0;
}
elsif
(
$ENV
{LUA_INIT} =~ /^@/ )
{
return
dofile(
$L
,
substr
(
$ENV
{LUA_INIT}, 1) );
}
else
{
return
dostring(
$L
,
$ENV
{LUA_INIT},
"="
.
'LUA_INIT'
);
}
}
sub
pmain {
my
(
$L
) =
@_
;
my
$s
=
$L
->touserdata(1);
my
$argv
=
$s
->{argv};
my
(
$has_i
,
$has_v
,
$has_e
);
$globalL
=
$L
;
$progname
= $0;
if
(
@$argv
&&
length
(
$argv
->[0]))
{
$L
->gc( Lua::API::GCSTOP, 0);
}
$L
->openlibs;
$L
->gc( Lua::API::GCRESTART, 0);
$s
->{status} = handle_luainit(
$L
);
return
0
if
$s
->{status} != 0;
my
$script
= collectargs(
$argv
, \
$has_i
, \
$has_v
, \
$has_e
);
if
(
$script
< 0)
{
print_usage();
$s
->{status} = 1;
return
0;
}
print_version()
if
$has_v
;
$s
->{status} = runargs(
$L
,
$argv
, (
$script
> 0) ?
$script
:
scalar
@$argv
);
return
0
if
$s
->{status} != 0;
if
(
$script
)
{
$s
->{status} = handle_script(
$L
,
$argv
,
$script
);
}
return
0
if
$s
->{status} != 0;
if
(
$has_i
)
{
dotty(
$L
);
}
elsif
(
$script
== 0 && !
$has_e
&& !
$has_v
)
{
if
( lua_stdin_is_tty() )
{
print_version();
dotty(
$L
);
}
else
{
dofile(
$L
);
}
}
return
0;
}
sub
main {
my
$L
= Lua::API::State->
open
();
if
(!
defined
$L
) {
l_message($0,
"cannot create state: not enough memory"
);
return
1;
}
my
%s
= (
argc
=>
scalar
@ARGV
,
argv
=> \
@ARGV
);
my
$status
=
$L
->cpcall( \
&pmain
, \
%s
);
report(
$L
,
$status
);
return
(
$status
||
$s
{status}) ? 1 : 0 ;
}