--
-- KERNEL.LUA               Copyright (c) 2002-04, Asko Kauppi
--
-- This is the backbone of the LuaX system, loading in modules and governing their usage.
-- This script is automatically loaded by 'luax' at startup (if found at the current
-- directory, or in the same dir with the executable).
--
-- To-do:
--      - Testing with PocketPC
--      - ...
--
if not (lib and lib.static) then
    error "This script requires LuaX."
end
local m= lib.static   -- our namespace
local _

ASSUME= assert

-- Stdout capture for luax_embed:
--
if rawget(_G,"STDOUT_CAPTURE") then  -- "" if enabled
    --local old_write= io.write
    io.write= function(...)
                local str= arg[1]
                for i=2,table.getn(arg) do
                    str= (str or "")..'\t'..arg[i]
                end
                STDOUT_CAPTURE= STDOUT_CAPTURE..str
                --old_write( unpack(arg) )
              end

    --local old_print= print
    print= function(...)
            io.write( unpack(arg) )
            STDOUT_CAPTURE= STDOUT_CAPTURE..'\n'
            --old_print( unpack(arg) )
           end
end

-- Make sure debug stuff really gets on screen:
--
function _PRINT(...) print( unpack(arg) ); io.flush(); end
function _WRITE(...) io.write( unpack(arg) ); io.flush(); end

function _WARN(str) _PRINT( "\t\nWARNING: "..str ) end

--
-- License info etc. (these apply to the LuaX system itself, each
-- module has its own similar fields):
--
ASSUME( m._info )  -- overriding the static sys module's fields

m._info= {
    MODULE= "LuaX",
    AUTHOR= "asko.kauppi@sci.fi",
    LICENSE= "LGPL",
    PLATFORM= "Win32, Linux, NetBSD/FreeBSD, OS X",
    RELEASE= 20041017,  -- release date
    DEPENDENCIES= nil,
    NOTES= nil }

local _lookup= { }
    -- These are just samples, actual mapping happens later on:
    --
    -- ["lib.sdl"]= { "sdl_module" },
    -- ["lib.sys"]= { "sys_module", "sys_script" },
    -- ["lib.tools"]= { "tools.lua", "tools_script" }

local _mappath= { }
    -- Full pathname search paths for 'USES'.
    
-----
-- The following static entries are depended upon:
--
ASSUME( lib.static.gluahost )
ASSUME( lib.static.chdir )

local Loc_FileExists= ASSUME( lib.static.fileinfo )

local function Loc_DirExists( name )
    --
    local tbl= lib.static.fileinfo(name)
    
    -- 'tbl.type' contains "d" if its a directory
    --
    return tbl and string.find( ASSUME(tbl.type), "d")
end

-- We present the OSes as tables, so we can place additional info
-- (s.a. version numbers, processor platform..) within there later.
--
WIN32= os.getenv("WINDIR") and { msys= os.getenv("OSTYPE")=="msys" } or false   -- Win95..XP (not CE)

WINCE= false
local f= lib.static.wince_GetSystemInfo
if f then
    local family, level, rev= f()
    ASSUME( family=="x86" or family=="arm" )
    WINCE= { [family]=level }  -- :)
end

LINUX= false
BSD= false
DARWIN= false
QNX= false

WIN32_MSYS= WIN32 and WIN32.msys

local function dump_string(str)    -- debugging!
    --
    for n=1,string.len(str) do
        local c= string.sub(str,n,n)
        print( c, string.byte(c) )
    end
end

if not (WIN32 or WINCE) then
    --
    -- Any LuaX compilation will have 'io.popen()' enabled:
    --
    local f= io.popen "uname"
    uname= f:read()  -- first line
    f:close()

    if uname=="Linux" then
        LINUX= { x86=true }
    elseif string.find( uname, "BSD" ) then   -- "NetBSD"/"FreeBSD"
        BSD= { x86=true }
    elseif uname=="Darwin" then
        DARWIN= { ppc=true }
    elseif uname=="QNX" then
        local cpu= ASSUME( os.getenv("PROCESSOR") )
        QNX= { x86= (cpu=="x86") }
    else
        error( "Unknown OS: '"..uname.."'" )
    end
end

if WIN32 and WIN32.msys then
    -- Win32/MSYS won't print nicely without this:
    --
    -- Note: even with this, some printout (s.a. using 'io.stderr:write'
    --       explicitly will flush differently). Fix it if you can!
    --
    local _print= print
    local _write= io.write
    function print(...) _print( unpack(arg) ); io.flush() end
    function io.write(...) _write( unpack(arg) ); io.flush() end
end

local DLL_SUFFIX= (WIN32 and ".dll") or
                  (DARWIN and ".dylib") or ".so"

--
-- Let's make it easier for others to know where we're installed.
--
-- Note: we cannot use 'arg[0]' (which would normally be the name of
--       this script) since we're loaded with "-l" as a library.
--
-- If started as an exe, we have 'lib.static.exepath' set up for us.
-- If started as a dll, the host must have set 'LUAX_ROOT' already.
--
if not rawget(_G,"LUAX_ROOT") then   -- running as exe
    --
    local path= ASSUME(lib.static.exepath)

    if not Loc_FileExists( path.."kernel.lua" ) then    -- same dir
        _,_,path= string.find( path, "(.+[/\\]).+[/\\]" )   -- parent

        if not path then    -- must be "out_xxx/luax" (development phase)
            path= lib.static.chdir()    -- current dir
        end
    end
    rawset( _G, "LUAX_ROOT", path )     -- global, for all to see
end

ASSUME( Loc_FileExists(LUAX_ROOT.."kernel.lua"),
        "LUAX_ROOT must lead to 'kernel.lua' (not found!)" )


--==( Lua 5.0 modifications )==--

-- These fixes should make it to the Lua core some day..? --

-----
-- Modify 'dofile()' so that it takes parameters and can also 
-- pass on (as 'arg[0]') the name of the file itself.
--
-- Also, must be able to RETURN several values at once..
--
local old_dofile= ASSUME(dofile)

function dofile( fn, ... )
    --
    local safe= rawget(_G,"arg") -- protect the 'arg' the caller sees
    local ret, ret2
       
    arg[0]= fn
    rawset( _G, "arg", arg )    -- set the global 'arg' table
    ret, ret2= old_dofile(fn)

    rawset( _G, "arg", safe )   -- original 'arg' back    
    return ret, ret2
end

-----
-- Debug dump - should be part of any Lua programmer's toolkit..
--
-- Note: This is only for our own need, see 'Scripts/dump.lua' for the
--       real version.
--
-- Note2: Crafted such that it works even in disabled sandboxes (setfenv)
--
local _write= assert(io.write)
local _print= assert(print)
local _flush= assert(io.flush)
local _pairs= assert(pairs)

local function _dump( tbl, lev )
    --
    lev= lev or 0

    if not tbl then
        _print "(nil)"
    else
        for key,val in _pairs(tbl) do   -- BUG!!: This don't work in a sandbox
            --
            _write(string.rep("  ", lev))
            _write(key, " : ")
    
            if type(val)=='table' then
                _print "table"
                if lev >= 0 then  -- recurse
                    _dump(val,lev+1)
                end
            elseif type(val)=='string' then
                _print( "\t'"..val.."'" )
            else
                _print( "",val )
            end
        end
    end
    
    _flush()  -- make sure dump always gets to screen
end
dump= _dump


--==( Local functions )==--

-----
-- str= Loc_FullPath( fn_str )
--
-- Returns the full pathname of 'fn'.
--
local function Loc_FullPath( fn )
    --
    local c1= string.sub(fn,1,1)    -- first char
    local c2= string.sub(fn,2,2)    -- 2nd char

    -- Absolute pathnames can be:
    --
    -- '/some/path..."      (Linux, Unix etc.)
    -- 'C:\some\path..."    (Win32)
    -- '\some\path.."       (Win32, using current drive)
    -- '\\some\path.."      (Win32)
    --
    if (c1=='/') or (c2==':') or (c1..c2=='\\\\') then
        --
        return fn   -- Already an absolute path
    end

    if WINCE then    -- WinCE only has absolute paths (no drive names)
        if c1=='\\' then
            return fn
        else
            return LUAX_ROOT..fn  -- think LuaX root is always current
        end
    else
        local cwd= lib.static.chdir()

        if c1=='\\' then  -- Absolute path on current drive
            return string.sub( cwd, 1, 3 ) .. fn
        else
            return cwd..fn
        end
    end
end

-----
-- Return a table (s.a. "lib.tools.misc") based on a variable name.
-- If such a table (and/or its parents) don't exist, create them.
--
local function Loc_EnsureTableExists( key )  -- str
    --
    local tbl, parent

    -- Easy way if doing a global (first level) table:
    --
    if not string.find( key, "%." ) then

        -- Note: Use 'rawset' so we won't get caught by access checking.
        --
        tbl= rawget( _G, key )

        if type(tbl)~='table' then
            tbl= {}
            rawset( _G, key, tbl )
        end
    else
        -- Make sure parents exist first (possibly NOT the optimal way?!?)
        --
        _,_,parent= string.find( key, "(.+)%." )

        if parent and parent~="lib" then
            Loc_EnsureTableExists( parent )
        end

        -- Now also we can be born.
        --
        local cmd= key.."= (type("..key..")=='table') ".."and "..
                   key.." or { }; return "..key
    
        -- What was wrong with Lua 4.0's "dostring()".. nothing?!?
        --
        tbl= assert( loadstring(cmd) ) ()
    end
    
    return tbl
end

-----
-- [suffix_str]= Loc_Suffix( filename_str )
--
-- Returns: The filename suffix (if any), in lowercase (e.g. ".dll").
--
local function Loc_Suffix( fn_str )
    --
    local i,_,sfx= string.find( fn_str, ".+(%.%w+)$" );

    if i then
        return string.lower(sfx)
    else
        return nil
    end
end

-----
-- str= Loc_AddTerminatingSlash( fname_str )
--
-- Returns: A string guaranteed to have '/' (or '\') at its end
--
local function Loc_AddTerminatingSlash( str )
    --
    local c= string.sub( str,-1 )
    
    if (c=='/') or (c=='\\') then
        return str  -- already ends with a slash
    else
        return str..'/'
    end
end

-----
-- [found_fn_str | found_table]= Loc_FindModule( filename_str, suffix_str )
--
-- Tries to look for a module in given libraries (_mappath)
-- and returns the filename of the found module (or 'nil' for none found).
--
local function Loc_FindModule2( fname_str, ext )
    --
--print( "trying:", fname_str )

    if Loc_FileExists( fname_str ) then
        return fname_str
    end
    
    if Loc_FileExists( fname_str..ext ) then
        return fname_str..ext
    end
    
    return nil  -- No such file!
end

--
local function Loc_FindModule( filename_str )
    --
    local ret= nil

    -- Try without path first:
    --
    ret= Loc_FindModule2( filename_str, DLL_SUFFIX )
    
    if (not ret) and _mappath then
        --
        for i,v in ipairs(_mappath) do
            --
            ret= Loc_FindModule2( v..filename_str, DLL_SUFFIX )
            if ret then break; end
        end
    end
    
    return ret
end

--
local function Loc_PrintMap( key )
    --
    local fn_list= _lookup[key]
    
    if not fn_list then
        _PRINT( "No mapping for '"..key.."'." )
        return
    end

    local str
    for _,fn in fn_list do
        str= str and (str.."\n\t\t\t"..fn) or fn
    end

    local spaces= 16- string.len(key)
    
    _PRINT( "  '"..key.."' ".. string.rep(' ',spaces).. "-> "..str )
end


--==( Public functions )==--

-----
-- This helps us ship unmodified luaSocket files:
--
local _require= ASSUME( require )     -- original one

function require( str )
    --
    local lookup= { ltn12=  "lib.ltn12",
                    mime=   "lib.mime",
                    socket= "lib.socket",
                    ftp=    "lib.socket.ftp",
                    http=   "lib.socket.http",
                    url=    "lib.socket.url",
                    tp=     "lib.socket.tp",
                  }

    if lookup[str] then
        return USES( lookup[str], "quiet" )
    else
        return _require( str )
    end
end


-----
-- [int]= Gluahost( filename )
--
-- This function is called by wrapper scripts that try to initialize the
-- gluax module they need. Without us, they would reach the gluahost code
-- itself (so the wrappers can also be used bare-boned).
--
-- Note: Gluahost() normally returns a number (0 for success, <0 for error).
--       We return 'nil' if the binary module cannot be found: this allows
--       the wrapper to step back gracefully, without generating error
--       messages for non-compiled modules.
--
function Gluahost( fname, namespace_tbl )
    --
    ASSUME( type(namespace_tbl)=="table" )

    local fn= Loc_FindModule(fname) or fname

    if not Loc_FileExists(fn) then
        return nil
        --
    elseif true then
        -- Set C code globals table to the namespace, causing any global
        -- access to be wrapped in namespace (for luaSocket 2.0, mainly).
        --
        local OLD_G= getfenv(0)
        local m= namespace_tbl

        -- Set metatables so that real globals ('string.*' etc.) are seen through
        -- (but changes will be made in 'm').
        --
        local mt= getmetatable(m)
        if mt then
            ASSUME( not mt.__index )
            mt.__index= _G
        else
            setmetatable(m, { __index=_G } )
        end

        setfenv( 0, m )    -- "global global" (the thing C code uses)
            --
            local ret= lib.static.gluahost( fn, m )
            
        setfenv( 0, OLD_G )

        if not mt then
            setmetatable(m,nil)
        else
            mt.__index= nil
        end        

        return ret
    else
        -- The old way..
        return lib.static.gluahost( fn, namespace_tbl )
    end
end

-----
-- 'MAP_PATH( path_str/tbl [, ...] )'
-- 'MAP_PATH()'                         <-- prints existing map paths
--
-- Add the given directory (directories) into the search list of modules.
--
function MAP_PATH( ... )
    --
    if not arg[1] then  -- print existing map paths
        for _,v in ipairs(_mappath) do  -- in order
            print( '\t'..v )
        end
    end

    for _,v in ipairs(arg) do
        --
        if type(v)=="table" then
            MAP_PATH( unpack(v) ) -- expand table parameters
            --
        elseif type(v)=="string" then
            if v~="" then
                v= Loc_AddTerminatingSlash(v)
            end
            table.insert( _mappath, Loc_FullPath(v) )
            --
        elseif not v then   -- hole in the table, just skip.
            --
        else
            ASSUME( false, "Unknown param for 'MAP_PATH()': "..v )
        end
    end
end

-----
-- 'MAP( key_str, filename_str [,filename2 [,...]] )'
--
-- Called by a configuration file that maps library id's to actual files
-- implementing them.
--
-- Note: A single key may be mapped to several modules, so that they all
--       together form the necessary behaviour. The mapped files are remembered
--       in order.
--
-- Note: No initialization happens at this stage, not even the checking that
--       the file exists. That is done on demand at first 'USES()' needing
--       the library.
--
-- Note: The _full_ pathname of the file is stored, for a purpose. This allows
--       the applications to change directories and the mappings won't be
--       affected (even if they are relative).
--
function MAP( key, ... )
    --
    if not arg[1] then
        --
        if key then
            Loc_PrintMap( key )
        else
            for k in _lookup do
                Loc_PrintMap(k)
            end
        end
        return
    end

    -- 'arg' given, do a mapping
    --
    _lookup[key]= _lookup[key] or {}

    -- Append filenames to the lookup:
    --
    for _,fn in ipairs(arg) do  -- go in order
        table.insert( _lookup[key], fn )
    end
end


-----
-- tbl= USES( key_str [,"quiet"] [,"global"] [,"try"] [,release_num] [,args_tbl] )
--
-- Purpose: Makes sure that a certain module (or rather, its API) is available.
--          Used by application scripts (or other modules) to tell their
--          dependencies.
--
-- Params:  The 'key' is a key into the library lookup table (above), which
--          allows the implementation behind a certain API to be freely exchanged
--          (as long as the API behaviour remains untouched).
--
--          "quiet" flag prevents any informational printouts.
--          "global" flag copies the contents to the global namespace.
--          "try" does not care if the module's not found - just returns 'nil'.
--
-- Returns: A reference to the created module table (also placed under global
--          'key' name) or 'nil' if the module couldn't be loaded.
--
function USES( key, ... )
    --
    local cmd, map_tbl, file, target_tbl, err, tmp, skip_infocheck
    local args, release= nil
    local quiet= false
    local global= false
    local try= false
    local globalize_func= nil    -- void= func( namespace_tbl )

    if type(key) ~= "string" then
        error( "USES(): Place hyphens around the module name." )
    end

    for i,v in ipairs(arg) do   -- skip ".n" field
        --
        if type(v)=="number" then
            release= v
        elseif type(v)=="table" then
            args= v
        elseif v=="quiet" then
            quiet= true
        elseif v=="global" then
            global= true
        elseif v=="try" then
            try= true
        else
            error( "Unknown param: "..v )
        end
    end
    
    if not quiet then
        _WRITE( "USES '"..key.."'" ..(global and " (global)" or "") ..": " )
    end

    -----
    -- Make sure the namespace exists before passing it to the modules.
    --
    if string.find( key, "%." ) then
        target_tbl= Loc_EnsureTableExists( key )    -- "lib.*"
    else
        -- "global", "debug", "libext" ..
        --
        -- Without this, we'd collide at least with 'debug' namespace and possibly
        -- others in the future as well?
        --
        target_tbl= Loc_EnsureTableExists( "lib.others."..key )  -- pushes them inside the 'lib' realm
    end

    ASSUME( target_tbl )

    if target_tbl._name and (not global) then
        --
        if not quiet then _PRINT( "already loaded." ); end
        return target_tbl
    end

    target_tbl._name= key
    -----
    -- Resolve mapping ("do we know this guy..?!?")
    --
    map_tbl= _lookup[key]

    if not map_tbl then
        error( "No module mapping for key '".. key .."'!" )
    end

    -----
    -- Find the files and link to them..
    --
    for i,v in ipairs(map_tbl) do
        --
        file= Loc_FindModule(v)
    
        if not file then
            if not quiet then _PRINT "not found!"; end
            return nil
        end

        if type(file)=="table" then
            --
            -- It's not a file but a library alias.
            --
            -- _Copy_ the contents, don't assign! 
            -- (we want to allow for cascading loading, right..)
            --
            --for k,v in file do
            --    target_tbl[k]= v
            --end
            ASSUME( false )
        else
            ASSUME( type(file)=="string" )

            -- Print actually used filenames:
            --
            if not quiet then _PRINT( file ); end
            
            -- Do the actual loading (into 'target_tbl')
            --
            if Loc_Suffix(v) == ".lua" then
                local ret
                --
                -- Lua scripts:
                --
                -- The script is expected to return a 'namespace table', which 
                -- contains its functions and other data.
                --
                ret, globalize_func= dofile(file, unpack(args or {}))

                if not ret then
                    if try then return nil; end
                    error( "Error loading '"..file.."'." )
                end

                if globalize_func then
                    ASSUME( type(globalize_func) == "function" )
                end

                if type(ret)=="function" then
                    ASSUME( false )     -- Not implemented yet!
                    --
                elseif type(ret)=="table" then
                    -- Copy contents of received table to those already in target
                    -- (allows several modules to be loaded into same namespace)
                    --
                    for k,v in ret do
                        target_tbl[k]= v
                    end
                    
                    -- What should we do if the module uses metatables..?
                    --
                    local mt= getmetatable(ret)
                    
                    if mt then
                        if getmetatable(target_tbl) then
                            error( "Not ready to merge metatables, yet.." )
                            -- ..but could be in the future (fix this if it hurts!)
                        else
                            setmetatable( target_tbl, mt )  -- okay, we'll use it
                        end
                    end
                    
                elseif type(ret)=="boolean" then
                    -- okay, it didn't provide any namespace
                    --
                    skip_infocheck= true
                else
                    error( "Unknown return value (not table or function) "..
                           "from '"..file.."'." )
                end
            else
                -- Binary modules:  (currently, only gluax supported)
                --
                if try and (not Loc_FileExists(file)) then
                    return nil  -- avoid giving error messages
                end
                
                local old_arg= arg
                arg= args or {}
                arg[0]= file

                    local err= lib.static.gluahost( arg[0], target_tbl )
                    
                arg= old_arg

                if err<0 then
                    error( "Error '"..err.."' loading '"..file.."'." )
                end
                
                -- Are there baked-in wrappers in the module? (1..N)
                --
                if target_tbl._wrap then
                    --
                    local wraps= target_tbl._wrap
                    target_tbl._wrap= nil   -- no longer required
                    
                    ASSUME( type(wraps)=="table" )

                    local low_api= target_tbl    -- C level API
                    local public_api= nil

                    target_tbl= nil
                    
                    for kk,vv in wraps do
                        --
                        -- HACK: 'loadstring()' block does not get parameters
                        -- through the normal way (at least, Lua 5.0.1).
                        --
                        --local tbl= assert( loadstring(vv)( low_api ) )
                        --
                        local old_arg= rawget( _G, "arg" )
                        rawset( _G, "arg", { low_api } )
                        local tbl= assert( loadstring(vv) )()
                        rawset( _G, "arg", old_arg )
                        
                        if not public_api then  -- 1st wrapper
                            public_api= tbl
                        else    -- 2..N
                            for kkk,vvv in tbl do
                                public_api[kkk]= vvv
                            end
                        end
                        
                        local mt= getmetatable(tbl)
                        if mt then
                            local mt2= getmetatable(public_api)
                            if not mt2 then
                                setmetatable(public_api,mt)
                            else
                                for kkk,vvv in mt do
                                    mt2[kkk]= vvv   -- append methods
                                end
                            end
                        end
                    end
                    target_tbl= ASSUME( public_api )
                end
            end
        end
    end

    -- Check for compliance with the 'info' fields (nasty, aren't we!)
    --
    if type(target_tbl._info) ~= "table" then
        if not skip_infocheck then
            _WARN( "Module '"..key.."': missing info fields!" )
        end
    else
        -- 1= must-have
        -- 2= optional
        --
        local tags= { MODULE=1, AUTHOR=1, LICENSE=1,
                      PLATFORM=2, COPYRIGHT=2, DEPENDENCIES=2, NOTES=2,
                      CREDITS=2, LINKS=2, RELEASE=2, }
        
        for k,_ in target_tbl._info do
            if not tags[k] then
                _WARN( "Unknown info field: '"..k.."'." )
            else
                tags[k]= nil  -- we've had this!
            end
        end
        
        -- Check that we got all must-haves:
        --
        for k,v in tags do
            if v==1 then
                _WARN( "Must-have info field '"..k.."' is missing!" )
            end
        end
    end

    -- Check that the caller gets a fresh module:
    --
    if release then
        local tmp= target_tbl._info.RELEASE 
        
        if tmp and (tmp>=release) then
            -- all is okay :)
        else
            error( "Module "..key.." must be updated!"..
                   (tmp and (" ("..tmp.." < "..release..")" ) or "") )
        end
    end
            
    -- All done, module loaded and available at 'lib.*'
    --
    if global then
        --
        if globalize_func then
            globalize_func( target_tbl )
        else
            for k,v in target_tbl do
                rawset(_G,k,v)  -- tolerant to access checking
            end
        end
    end
        
    return target_tbl
end


-----
-- Prepare for arg override:
--
-- When 'kernel.lua' is loaded, 'arg' is nil. It gets set up later,
-- and 'gluahost.c' gladly intercepts that call for us. :)
--
-- The reason? Just because we care.. and we modify the "flag=value"
-- syntax params for tha apps, placing them in "arg.flag" etc.
-- for easy access.
--
-- Note: This introduces an incompatibility with the way Lua 5.0 
--       parses cmdline args. If that matters, just rename this
--       func and things will be back to normal.
--
function _SetArgs(tbl)  -- called by 'gluahost.c'

    -----    
    -- tbl[-5].. are the exe filename etc. that's not of interest
    -- to us (let alone the application scripts!)
    --
    -- tbl[0] is the name of the application script
    --
    -- tbl[1..N] are the params to the app
    --
    arg= { [0]= tbl[0] }

    for i,v in ipairs(tbl) do   -- 1..N
        --
        local _,_, flag,val= string.find( v, "(.+)=(.+)" )

        if flag then
            arg[flag]= val
        else
            table.insert( arg, v )
        end
    end

    -- Once this is called we can detach ourselves
    --
    _SetArgs= nil
end


-----
-- Debugging features (write out to console):
--
function _MyHook( type )    -- 'type'= "call"/"return"/"line"/"count"
    --
    local info= debug.getinfo( 2, "n" )
    local name

    name= (info and info.name) or "(no name)"

    print( "Debug: " ..type.. " '" ..name.. "'" )
end
--debug.sethook( _MyHook, "cl" )


--==( Main level )==--

function help( issue )
    --
    if lib.others.globals then
        lib.others.globals.off()   -- interactive mode
    end
    
    print(
[[  
Short help:
  USES 'lib.sys'              to load a module
  MAP('my.module', filename)  to add a module
  MAP()                       to see the current (default) mappings
  dump(lib.sys)               to see the contents of a module
  help()                      to reprint this message
]].."\n"..
--Global namecheck is on - can be switched off by 'globals.off()'.
--
      ( (WIN32 and (not WIN32_MSYS)) and "Ctrl-Z" or "Ctrl-D") .." to exit." )
    --
    -- Note: For MSYS, neither Ctrl-Z or Ctrl-D works.. :)
end


-- Give a welcome message:
--
print( [[  
=============================
== Welcome to LuaX!
==
== LuaX is released under the LGPL Open Source license, see 'license.txt'.
== Individual modules may use other licenses.
==
== Copyright (c) 2003-04, asko.kauppi@sci.fi
=============================]] )

-- Autoload 'mappings.lua' if we can find it.
--
local fn= LUAX_ROOT.."mappings.lua"

ASSUME( Loc_FileExists(fn), "Mappings file '"..fn.."' not found!" )

ASSUME( dofile(fn) )


--
return true
