@ -15,13 +15,7 @@
# include "spawn.h"
/* Generally useful function -- what luaL_checkudata() should do */
extern void * checkuserdata ( lua_State * L , int idx , const char * tname )
{
void * ud ;
luaL_argcheck ( L , ud = luaL_checkudata ( L , idx , tname ) , idx , tname ) ;
return ud ;
}
# define absindex(L,i) ((i)>0?(i):lua_gettop(L)+(i)+1)
/* return HANDLE from a FILE */
extern HANDLE get_handle ( FILE * f )
@ -144,9 +138,19 @@ static int ex_currentdir(lua_State *L)
}
static FILE * check_file ( lua_State * L , int idx )
FILE * check_file ( lua_State * L , int idx , const char * argname )
{
FILE * * pf = checkuserdata ( L , idx , LUA_FILEHANDLE ) ;
FILE * * pf ;
if ( idx > 0 ) pf = luaL_checkudata ( L , idx , LUA_FILEHANDLE ) ;
else {
idx = absindex ( idx ) ;
pf = lua_touserdata ( L , idx ) ;
luaL_getmetatable ( L , LUA_FILEHANDLE ) ;
if ( ! pf | | ! lua_getmetatable ( L , idx - 1 ) | | ! lua_rawequal ( L , - 1 , - 2 ) )
luaL_error ( L , " %s option: expected %s, got %s " ,
argname , LUA_FILEHANDLE , luaL_typename ( L , idx - 2 ) ) ;
lua_pop ( L , 2 ) ;
}
if ( ! * pf ) return luaL_error ( L , " attempt to use a closed file " ) , NULL ;
return * pf ;
}
@ -187,7 +191,7 @@ static int ex_dirent(lua_State *L)
size = get_size ( name ) ;
} break ;
case LUA_TUSERDATA : {
FILE * f = check_file ( L , 1 );
FILE * f = check_file ( L , 1 , NULL );
BY_HANDLE_FILE_INFORMATION info ;
if ( ! GetFileInformationByHandle ( get_handle ( f ) , & info ) )
return push_error ( L ) ;
@ -235,9 +239,9 @@ static int diriter_setpathname(lua_State *L, int index)
lua_pushliteral ( L , LUA_DIRSEP ) ;
lua_concat ( L , 2 ) ;
}
lua_pushvalue ( L , index ) ; /* ... pathname diriter */
lua_insert ( L , - 2 ) ; /* ... diriter pathname */
lua_settable ( L , LUA_REGISTRYINDEX ) ; /* ... */
lua_pushvalue ( L , index ) ; /* ... pathname diriter */
lua_insert ( L , - 2 ) ; /* ... diriter pathname */
lua_settable ( L , LUA_REGISTRYINDEX ) ; /* ... */
return 0 ;
}
@ -263,37 +267,37 @@ static int ex_dir(lua_State *L)
switch ( lua_type ( L , 1 ) ) {
default : return luaL_argerror ( L , 1 , " expected pathname " ) ;
case LUA_TSTRING :
lua_pushvalue ( L , 1 ) ; /* pathname ... pathname */
lua_pushliteral ( L , " \\ * " ) ; /* pathname ... pathname "\\*" */
lua_concat ( L , 2 ) ; /* pathname ... pattern */
lua_pushvalue ( L , 1 ) ; /* pathname ... pathname */
lua_pushliteral ( L , " \\ * " ) ; /* pathname ... pathname "\\*" */
lua_concat ( L , 2 ) ; /* pathname ... pattern */
pathname = lua_tostring ( L , - 1 ) ;
lua_pushcfunction ( L , ex_dir ) ; /* pathname ... pat iter */
pi = lua_newuserdata ( L , sizeof * pi ) ; /* pathname ... pat iter state */
lua_pushcfunction ( L , ex_dir ) ; /* pathname ... pat iter */
pi = lua_newuserdata ( L , sizeof * pi ) ; /* pathname ... pat iter state */
pi - > hf = FindFirstFile ( pathname , & pi - > fd ) ;
if ( pi - > hf = = INVALID_HANDLE_VALUE )
return push_error ( L ) ;
luaL_getmetatable ( L , DIR_HANDLE ) ; /* pathname ... pat iter state M */
lua_setmetatable ( L , - 2 ) ; /* pathname ... pat iter state */
lua_pushvalue ( L , 1 ) ; /* pathname ... pat iter state pathname */
diriter_setpathname ( L , - 2 ) ; /* pathname ... pat iter state */
luaL_getmetatable ( L , DIR_HANDLE ) ; /* pathname ... pat iter state M */
lua_setmetatable ( L , - 2 ) ; /* pathname ... pat iter state */
lua_pushvalue ( L , 1 ) ; /* pathname ... pat iter state pathname */
diriter_setpathname ( L , - 2 ) ; /* pathname ... pat iter state */
return 2 ;
case LUA_TUSERDATA :
pi = checkuser data( L , 1 , DIR_HANDLE ) ;
pi = luaL_ checkudata( L , 1 , DIR_HANDLE ) ;
if ( pi - > hf = = INVALID_HANDLE_VALUE )
return 0 ;
lua_newtable ( L ) ; /* diriter ... entry */
diriter_getpathname ( L , 1 ) ; /* diriter ... entry dirpath */
lua_pushstring ( L , pi - > fd . cFileName ) ; /* diriter ... entry dirpath name */
lua_pushliteral ( L , " name " ) ; /* diriter ... entry dirpath name "name" */
lua_pushvalue ( L , - 2 ) ; /* diriter ... entry dirpath name "name" name */
lua_settable ( L , - 5 ) ; /* diriter ... entry dirpath name */
lua_concat ( L , 2 ) ; /* diriter ... entry fullpath */
lua_newtable ( L ) ; /* diriter ... entry */
diriter_getpathname ( L , 1 ) ; /* diriter ... entry dirpath */
lua_pushstring ( L , pi - > fd . cFileName ) ; /* diriter ... entry dirpath name */
lua_pushliteral ( L , " name " ) ; /* diriter ... entry dirpath name "name" */
lua_pushvalue ( L , - 2 ) ; /* diriter ... entry dirpath name "name" name */
lua_settable ( L , - 5 ) ; /* diriter ... entry dirpath name */
lua_concat ( L , 2 ) ; /* diriter ... entry fullpath */
if ( ! FindNextFile ( pi - > hf , & pi - > fd ) ) {
FindClose ( pi - > hf ) ;
pi - > hf = INVALID_HANDLE_VALUE ;
}
lua_replace ( L , 1 ) ; /* fullpath ... entry */
lua_replace ( L , 2 ) ; /* fullpath entry ... */
lua_replace ( L , 1 ) ; /* fullpath ... entry */
lua_replace ( L , 2 ) ; /* fullpath entry ... */
return ex_dirent ( L ) ;
}
/*NOTREACHED*/
@ -304,8 +308,8 @@ static int file_lock(lua_State *L, FILE *f, const char *mode, long offset, long
{
HANDLE h = get_handle ( f ) ;
DWORD flags ;
LARGE_INTEGER len = { 0 };
OVERLAPPED ov = { 0 } ;
LARGE_INTEGER len = { 0 ,0 };
OVERLAPPED ov = { . hEvent = INVALID_HANDLE_VALUE } ;
BOOL ret ;
if ( length ) len . LowPart = length ;
else len . LowPart = GetFileSize ( h , & len . HighPart ) ;
@ -327,7 +331,7 @@ static int file_lock(lua_State *L, FILE *f, const char *mode, long offset, long
/* file mode [offset [length]] -- true/nil error */
static int ex_lock ( lua_State * L )
{
FILE * f = check_file ( L , 1 );
FILE * f = check_file ( L , 1 , NULL );
const char * mode = luaL_checkstring ( L , 2 ) ;
long offset = luaL_optnumber ( L , 3 , 0 ) ;
long length = luaL_optnumber ( L , 4 , 0 ) ;
@ -362,86 +366,87 @@ static int ex_pipe(lua_State *L)
FILE * i , * o , * * pf ;
if ( ! make_pipe ( & i , & o ) )
return push_error ( L ) ;
luaL_getmetatable ( L , LUA_FILEHANDLE ) ;
* ( pf = lua_newuserdata ( L , sizeof * pf ) ) = i ;
lua_pushvalue ( L , - 2 ) ;
lua_setmetatable ( L , - 2 ) ;
* ( pf = lua_newuserdata ( L , sizeof * pf ) ) = o ;
lua_pushvalue ( L , - 3 ) ;
lua_setmetatable ( L , - 2 ) ;
luaL_getmetatable ( L , LUA_FILEHANDLE ) ; /* M */
* ( pf = lua_newuserdata ( L , sizeof * pf ) ) = i ; /* M i */
lua_pushvalue ( L , - 2 ) ; /* M i M */
lua_setmetatable ( L , - 2 ) ; /* M i */
* ( pf = lua_newuserdata ( L , sizeof * pf ) ) = o ; /* M i o */
lua_pushvalue ( L , - 3 ) ; /* M i o M */
lua_setmetatable ( L , - 2 ) ; /* M i o */
return 2 ;
}
static void get_redirect ( lua_State * L , int idx , const char * stdname , struct spawn_params * p )
{
lua_getfield ( L , idx , stdname ) ;
if ( ! lua_isnil ( L , - 1 ) )
spawn_param_redirect ( p , stdname , check_file ( L , - 1 , stdname ) ) ;
lua_pop ( L , 1 ) ;
}
/* filename [args-opts] -- true/nil error */
/* args-opts -- true/nil error */
static int ex_spawn ( lua_State * L )
{
struct spawn_params params = { L } ;
struct process * proc ;
struct spawn_params * params = spawn_param_init ( L ) ;
if ( lua_type ( L , 1 ) = = LUA_TTABLE ) {
lua_getfield ( L , 1 , " command " ) ; /* opts ... cmd */
lua_getfield ( L , 1 , " command " ) ; /* opts ... cmd */
if ( ! lua_isnil ( L , - 1 ) ) {
/* convert {command=command,arg1,...} to command {arg1,...} */
lua_insert ( L , 1 ) ; /* cmd opts ... */
lua_insert ( L , 1 ) ; /* cmd opts ... */
}
else {
/* convert {arg0,arg1,...} to arg0 {arg1,...} */
size_t i , n = lua_objlen ( L , 1 ) ;
lua_rawgeti ( L , 1 , 1 ) ; /* opts ... nil cmd */
if ( lua_isnil ( L , - 1 ) )
return luaL_error ( L , " no command specified " ) ;
/* XXX check LUA_TSTRING */
lua_insert ( L , 1 ) ; /* cmd opts ... nil */
lua_rawgeti ( L , 1 , 1 ) ; /* opts ... nil cmd */
lua_insert ( L , 1 ) ; /* cmd opts ... nil */
for ( i = 2 ; i < = n ; i + + ) {
lua_rawgeti ( L , 2 , i ) ; /* cmd opts ... nil argi */
lua_rawseti ( L , 2 , i - 1 ) ; /* cmd opts ... nil */
lua_rawgeti ( L , 2 , i ) ; /* cmd opts ... nil argi */
lua_rawseti ( L , 2 , i - 1 ) ; /* cmd opts ... nil */
}
lua_rawseti ( L , 2 , n ) ; /* cmd opts ... */
lua_rawseti ( L , 2 , n ) ; /* cmd opts ... */
}
}
/* get filename to execute */
spawn_param_filename ( & params ) ;
if ( lua_type ( L , 1 ) ! = LUA_TSTRING )
return luaL_error ( L , " command option: expected string, got %s " , luaL_typename ( L , 1 ) ) ;
spawn_param_filename ( params , lua_tostring ( L , 1 ) ) ;
/* get arguments, environment, and redirections */
switch ( lua_type ( L , 2 ) ) {
default : return luaL_argerror ( L , 2 , " expected options table " ) ;
case LUA_TNONE :
spawn_param_defaults ( & params ) ; /* cmd opts ... */
break ;
case LUA_TNONE : break ;
case LUA_TTABLE :
lua_getfield ( L , 2 , " args " ) ; /* cmd opts ... argtab */
lua_getfield ( L , 2 , " args " ) ; /* cmd opts ... argtab */
switch ( lua_type ( L , - 1 ) ) {
default : return luaL_error ( L , " args option must be an array " ) ;
case LUA_TNIL :
lua_pop ( L , 1 ) ; /* cmd opts ... */
lua_pushvalue ( L , 2 ) ; /* cmd opts ... opts */
lua_pop ( L , 1 ) ; /* cmd opts ... */
lua_pushvalue ( L , 2 ) ; /* cmd opts ... opts */
if ( 0 ) /*FALLTHRU*/
case LUA_TTABLE :
if ( lua_objlen ( L , 2 ) > 0 )
return luaL_error ( L , " cannot specify both the args option and array values " ) ;
spawn_param_args ( & params ) ; /* cmd opts ... */
spawn_param_args ( params ) ; /* cmd opts ... */
break ;
}
lua_getfield ( L , 2 , " env " ) ; /* cmd opts ... envtab */
lua_getfield ( L , 2 , " env " ) ; /* cmd opts ... envtab */
switch ( lua_type ( L , - 1 ) ) {
default : return luaL_error ( L , " env option must be a table " ) ;
case LUA_TNIL :
case LUA_TTABLE :
spawn_param_env ( & params ) ; /* cmd opts ... */
spawn_param_env ( params ) ; /* cmd opts ... */
break ;
}
spawn_param_redirects ( & params ) ; /* cmd opts ... */
get_redirect ( L , 2 , " stdin " , params ) ; /* cmd opts ... */
get_redirect ( L , 2 , " stdout " , params ) ; /* cmd opts ... */
get_redirect ( L , 2 , " stderr " , params ) ; /* cmd opts ... */
break ;
}
proc = lua_newuserdata ( L , sizeof * proc ) ; /* cmd opts ... proc */
luaL_getmetatable ( L , PROCESS_HANDLE ) ; /* cmd opts ... proc M */
lua_setmetatable ( L , - 2 ) ; /* cmd opts ... proc */
if ( ! spawn_param_execute ( & params , proc ) )
return push_error ( L ) ;
return 1 ; /* ... proc */
return spawn_param_execute ( params ) ; /* proc/nil error */
}
@ -474,47 +479,69 @@ static const luaL_reg ex_oslib[] = {
} ;
static const luaL_reg ex_diriter_methods [ ] = {
{ " __gc " , diriter_close } ,
/* {"__tostring", diriter_tostring}, */
{ 0 , 0 }
} ;
static const luaL_reg ex_process_methods [ ] = {
{ " wait " , process_wait } ,
{ " __tostring " , process_tostring } ,
{ " wait " , process_wait } ,
{ 0 , 0 }
} ;
/* copy the fields given in 'l' from one table to another; insert missing fields */
static void copy_fields ( lua_State * L , const luaL_reg * l , int from , int to )
{
from = absindex ( L , from ) ;
to = absindex ( L , to ) ;
for ( ; l - > name ; l + + ) {
lua_getfield ( L , from , l - > name ) ;
if ( lua_isnil ( L , - 1 ) ) {
lua_pop ( L , 1 ) ;
lua_pushcfunction ( L , l - > func ) ;
}
lua_setfield ( L , to , l - > name ) ;
}
}
int luaopen_ex ( lua_State * L )
{
/* extend the io table */
lua_getglobal ( L , " io " ) ;
if ( lua_isnil ( L , - 1 ) ) return luaL_error ( L , " io not loaded " ) ;
luaL_openlib ( L , 0 , ex_iolib , 0 ) ;
/* Make all functions available via ex. namespace */
luaL_register ( L , " ex " , ex_iolib ) ; /* . ex */
luaL_register ( L , 0 , ex_oslib ) ;
luaL_register ( L , 0 , ex_iofile_methods ) ;
luaL_register ( L , 0 , ex_process_methods + 1 ) ; /* XXX don't insert __tostring */
lua_replace ( L , 1 ) ; /* ex . */
/* extend the os table */
lua_getglobal ( L , " os " ) ;
lua_getglobal ( L , " os " ) ; /* ex . os */
if ( lua_isnil ( L , - 1 ) ) return luaL_error ( L , " os not loaded " ) ;
luaL_openlib ( L , " os " , ex_oslib , 0 ) ;
copy_fields ( L , ex_oslib , 1 , - 1 ) ; /* ex . os */
/* extend the io table */
lua_getglobal ( L , " io " ) ; /* ex . io */
if ( lua_isnil ( L , - 1 ) ) return luaL_error ( L , " io not loaded " ) ;
copy_fields ( L , ex_iolib , 1 , - 1 ) ; /* ex . io */
lua_getfield ( L , 1 , " pipe " ) ; /* ex . io ex_pipe */
lua_getfield ( L , - 2 , " stderr " ) ; /* ex . io ex_pipe io_stderr */
lua_getfenv ( L , - 1 ) ; /* ex . io ex_pipe io_stderr E */
lua_setfenv ( L , - 3 ) ; /* ex . io ex_pipe io_stderr */
/* extend the io.file metatable */
luaL_getmetatable ( L , LUA_FILEHANDLE ) ;
luaL_getmetatable ( L , LUA_FILEHANDLE ) ; /* ex . F */
if ( lua_isnil ( L , - 1 ) ) return luaL_error ( L , " can't find FILE* metatable " ) ;
luaL_openlib ( L , 0 , ex_iofile_methods , 0 ) ;
copy_fields( L , ex_iofile_methods , 1 , - 1 ) ; /* ex . F */
/* diriter metatable */
luaL_newmetatable ( L , DIR_HANDLE ) ;
luaL_openlib ( L , 0 , ex_diriter_methods , 0 ) ;
luaL_newmetatable ( L , DIR_HANDLE ) ; /* ex . D */
luaL_ register ( L , 0 , ex_diriter_methods ); /* ex . D */
/* proc metatable */
luaL_newmetatable ( L , PROCESS_HANDLE ) ; /* proc */
luaL_openlib ( L , 0 , ex_process_methods , 0 ) ; /* proc */
lua_pushliteral ( L , " __index " ) ; /* proc __index */
lua_pushvalue ( L , - 2 ) ; /* proc __index proc */
lua_settable ( L , - 3 ) ; /* proc */
/* Make all functions available via ex. namespace */
luaL_openlib ( L , " ex " , ex_iolib , 0 ) ;
luaL_openlib ( L , 0 , ex_oslib , 0 ) ;
luaL_openlib ( L , 0 , ex_iofile_methods , 0 ) ;
luaL_openlib ( L , 0 , ex_process_methods , 0 ) ;
luaL_newmetatable ( L , PROCESS_HANDLE ) ; /* ex . P */
copy_fields ( L , ex_process_methods , 1 , - 1 ) ; /* ex . P */
lua_pushliteral ( L , " __index " ) ; /* ex . P __index */
lua_pushvalue ( L , - 2 ) ; /* ex . P __index P */
lua_settable ( L , - 3 ) ; /* ex . P */
lua_settop ( L , 1 ) ; /* ex */
return 1 ;
}