Beispiel #1
0
static FILE * open_command_file( int const slot )
{
    string * const command_file = cmdtab[ slot ].command_file;

    /* If the temporary command file name has not already been prepared for this
     * slot number, prepare a new one containing a '##' place holder that will
     * be changed later and needs to be located at a fixed distance from the
     * end.
     */
    if ( !command_file->value )
    {
        DWORD const procID = GetCurrentProcessId();
        string const * const tmpdir = path_tmpdir();
        string_new( command_file );
        string_reserve( command_file, tmpdir->size + 64 );
        command_file->size = sprintf( command_file->value,
            "%s\\jam%d-%02d-##.bat", tmpdir->value, procID, slot );
    }

    /* For some reason opening a command file can fail intermittently. But doing
     * some retries works. Most likely this is due to a previously existing file
     * of the same name that happens to still be opened by an active virus
     * scanner. Originally pointed out and fixed by Bronek Kozicki.
     *
     * We first try to open several differently named files to avoid having to
     * wait idly if not absolutely necessary. Our temporary command file names
     * contain a fixed position place holder we use for generating different
     * file names.
     */
    {
        char * const index1 = command_file->value + command_file->size - 6;
        char * const index2 = index1 + 1;
        int waits_remaining;
        assert( command_file->value < index1 );
        assert( index2 + 1 < command_file->value + command_file->size );
        assert( index2[ 1 ] == '.' );
        for ( waits_remaining = 3; ; --waits_remaining )
        {
            int index;
            for ( index = 0; index != 20; ++index )
            {
                FILE * f;
                *index1 = '0' + index / 10;
                *index2 = '0' + index % 10;
                f = fopen( command_file->value, "w" );
                if ( f ) return f;
            }
            if ( !waits_remaining ) break;
            Sleep( 250 );
        }
    }

    return 0;
}
Beispiel #2
0
LIST * var_get( struct module_t * module, OBJECT * symbol )
{
    LIST * result = L0;
#ifdef OPT_AT_FILES
    /* Some "fixed" variables... */
    if ( object_equal( symbol, constant_TMPDIR ) )
    {
        list_free( saved_var );
        result = saved_var = list_new( object_new( path_tmpdir()->value ) );
    }
    else if ( object_equal( symbol, constant_TMPNAME ) )
    {
        list_free( saved_var );
        result = saved_var = list_new( path_tmpnam() );
    }
    else if ( object_equal( symbol, constant_TMPFILE ) )
    {
        list_free( saved_var );
        result = saved_var = list_new( path_tmpfile() );
    }
    else if ( object_equal( symbol, constant_STDOUT ) )
    {
        list_free( saved_var );
        result = saved_var = list_new( object_copy( constant_STDOUT ) );
    }
    else if ( object_equal( symbol, constant_STDERR ) )
    {
        list_free( saved_var );
        result = saved_var = list_new( object_copy( constant_STDERR ) );
    }
    else
#endif
    {
        VARIABLE * v;
        int n;

        if ( ( n = module_get_fixed_var( module, symbol ) ) != -1 )
        {
            if ( DEBUG_VARGET )
                var_dump( symbol, module->fixed_variables[ n ], "get" );
            result = module->fixed_variables[ n ];
        }
        else if ( module->variables && ( v = (VARIABLE *)hash_find(
            module->variables, symbol ) ) )
        {
            if ( DEBUG_VARGET )
                var_dump( v->symbol, v->value, "get" );
            result = v->value;
        }
    }
    return result;
}
Beispiel #3
0
const char * path_tmpfile(void)
{
    const char * result = 0;

    string file_path;
    string_copy(&file_path,path_tmpdir());
    string_push_back(&file_path,PATH_DELIM);
    string_append(&file_path,path_tmpnam());
    result = newstr(file_path.value);
    string_free(&file_path);

    return result;
}
Beispiel #4
0
OBJECT * path_tmpfile(void)
{
    OBJECT * result = 0;
    OBJECT * tmpnam;

    string file_path;
    string_copy(&file_path,path_tmpdir());
    string_push_back(&file_path,PATH_DELIM);
    tmpnam = path_tmpnam();
    string_append(&file_path,object_str(tmpnam));
    object_free(tmpnam);
    result = object_new(file_path.value);
    string_free(&file_path);

    return result;
}
Beispiel #5
0
LIST * var_get( OBJECT * symbol )
{
    LIST * result = 0;
#ifdef OPT_AT_FILES
    /* Some "fixed" variables... */
    if ( strcmp( "TMPDIR", object_str( symbol ) ) == 0 )
    {
        list_free( saved_var );
        result = saved_var = list_new( L0, object_new( path_tmpdir() ) );
    }
    else if ( strcmp( "TMPNAME", object_str( symbol ) ) == 0 )
    {
        list_free( saved_var );
        result = saved_var = list_new( L0, path_tmpnam() );
    }
    else if ( strcmp( "TMPFILE", object_str( symbol ) ) == 0 )
    {
        list_free( saved_var );
        result = saved_var = list_new( L0, path_tmpfile() );
    }
    else if ( strcmp( "STDOUT", object_str( symbol ) ) == 0 )
    {
        list_free( saved_var );
        result = saved_var = list_new( L0, object_new( "STDOUT" ) );
    }
    else if ( strcmp( "STDERR", object_str( symbol ) ) == 0 )
    {
        list_free( saved_var );
        result = saved_var = list_new( L0, object_new( "STDERR" ) );
    }
    else
#endif
    {
        VARIABLE var;
        VARIABLE * v = &var;

        v->symbol = symbol;

        if ( varhash && hashcheck( varhash, (HASHDATA * *)&v ) )
        {
            if ( DEBUG_VARGET )
                var_dump( v->symbol, v->value, "get" );
            result = v->value;
        }
    }
    return result;
}
Beispiel #6
0
LIST *
var_get( char *symbol )
{
    LIST * result = 0;
    #ifdef OPT_AT_FILES
    /* Some "fixed" variables... */
    if ( strcmp( "TMPDIR", symbol ) == 0 )
    {
        result = list_new( L0, newstr( (char*)path_tmpdir() ) );
    }
    else if ( strcmp( "TMPNAME", symbol ) == 0 )
    {
        result = list_new( L0, newstr( (char*)path_tmpnam() ) );
    }
    else if ( strcmp( "TMPFILE", symbol ) == 0 )
    {
        result = list_new( L0, newstr( (char*)path_tmpfile() ) );
    }
    else if ( strcmp( "STDOUT", symbol ) == 0 )
    {
        result = list_new( L0, newstr( "STDOUT" ) );
    }
    else if ( strcmp( "STDERR", symbol ) == 0 )
    {
        result = list_new( L0, newstr( "STDERR" ) );
    }
    else
    #endif
    {
        VARIABLE var, *v = &var;
    
        v->symbol = symbol;
    
        if( varhash && hashcheck( varhash, (HASHDATA **)&v ) )
        {
            if( DEBUG_VARGET )
                var_dump( v->symbol, v->value, "get" );
            result = v->value;
        }
    }
    return result;
}
Beispiel #7
0
void exec_cmd
(
    char * command,
    void (* func)( void * closure, int status, timing_info *, char * invoked_command, char * command_output ),
    void * closure,
    LIST * shell,
    char * action,
    char * target
)
{
    int      slot;
    int      raw_cmd = 0 ;
    char   * argv_static[ MAXARGC + 1 ];  /* +1 for NULL */
    char * * argv = argv_static;
    char   * p;
    char   * command_orig = command;

    /* Check to see if we need to hack around the line-length limitation. Look
     * for a JAMSHELL setting of "%", indicating that the command should be
     * invoked directly.
     */
    if ( shell && !strcmp( shell->string, "%" ) && !list_next( shell ) )
    {
        raw_cmd = 1;
        shell = 0;
    }

    /* Find a slot in the running commands table for this one. */
    for ( slot = 0; slot < MAXJOBS; ++slot )
        if ( !cmdtab[ slot ].pi.hProcess )
            break;
    if ( slot == MAXJOBS )
    {
        printf( "no slots for child!\n" );
        exit( EXITBAD );
    }

    /* Compute the name of a temp batch file, for possible use. */
    if ( !cmdtab[ slot ].tempfile_bat )
    {
        char const * tempdir = path_tmpdir();
        DWORD procID = GetCurrentProcessId();

        /* SVA - allocate 64 bytes extra just to be safe. */
        cmdtab[ slot ].tempfile_bat = BJAM_MALLOC_ATOMIC( strlen( tempdir ) + 64 );

        sprintf( cmdtab[ slot ].tempfile_bat, "%s\\jam%d-%02d.bat",
            tempdir, procID, slot );
    }

    /* Trim leading, -ending- white space */
    while ( *( command + 1 ) && isspace( *command ) )
        ++command;

    /* Write to .BAT file unless the line would be too long and it meets the
     * other spawnability criteria.
     */
    if ( raw_cmd && ( can_spawn( command ) >= MAXLINE ) )
    {
        if ( DEBUG_EXECCMD )
            printf("Executing raw command directly\n");
    }
    else
    {
        FILE * f = 0;
        int tries = 0;
        raw_cmd = 0;

        /* Write command to bat file. For some reason this open can fail
         * intermitently. But doing some retries works. Most likely this is due
         * to a previously existing file of the same name that happens to be
         * opened by an active virus scanner. Pointed out and fixed by Bronek
         * Kozicki.
         */
        for ( ; !f && ( tries < 4 ); ++tries )
        {
            f = fopen( cmdtab[ slot ].tempfile_bat, "w" );
            if ( !f && ( tries < 4 ) ) Sleep( 250 );
        }
        if ( !f )
        {
            printf( "failed to write command file!\n" );
            exit( EXITBAD );
        }
        fputs( command, f );
        fclose( f );

        command = cmdtab[ slot ].tempfile_bat;

        if ( DEBUG_EXECCMD )
        {
            if ( shell )
                printf( "using user-specified shell: %s", shell->string );
            else
                printf( "Executing through .bat file\n" );
        }
    }

    /* Formulate argv; If shell was defined, be prepared for % and ! subs.
     * Otherwise, use stock cmd.exe.
     */
    if ( shell )
    {
        int i;
        char jobno[ 4 ];
        int gotpercent = 0;

        sprintf( jobno, "%d", slot + 1 );

        for ( i = 0; shell && ( i < MAXARGC ); ++i, shell = list_next( shell ) )
        {
            switch ( shell->string[ 0 ] )
            {
                case '%': argv[ i ] = command; ++gotpercent; break;
                case '!': argv[ i ] = jobno; break;
                default : argv[ i ] = shell->string;
            }
            if ( DEBUG_EXECCMD )
                printf( "argv[%d] = '%s'\n", i, argv[ i ] );
        }

        if ( !gotpercent )
            argv[ i++ ] = command;

        argv[ i ] = 0;
    }
    else if ( raw_cmd )
    {
        argv = string_to_args( command );
    }
    else
    {
        argv[ 0 ] = "cmd.exe";
        argv[ 1 ] = "/Q/C";  /* anything more is non-portable */
        argv[ 2 ] = command;
        argv[ 3 ] = 0;
    }

    /* Catch interrupts whenever commands are running. */
    if ( !cmdsrunning++ )
        istat = signal( SIGINT, onintr );

    /* Start the command. */
    {
        SECURITY_ATTRIBUTES sa
            = { sizeof( SECURITY_ATTRIBUTES ), 0, 0 };
        SECURITY_DESCRIPTOR sd;
        STARTUPINFO si
            = { sizeof( STARTUPINFO ), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        string cmd;

        /* Init the security data. */
        InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION );
        SetSecurityDescriptorDacl( &sd, TRUE, NULL, FALSE );
        sa.lpSecurityDescriptor = &sd;
        sa.bInheritHandle = TRUE;

        /* Create the stdout, which is also the merged out + err, pipe. */
        if ( !CreatePipe( &cmdtab[ slot ].pipe_out[ 0 ],
            &cmdtab[ slot ].pipe_out[ 1 ], &sa, 0 ) )
        {
            perror( "CreatePipe" );
            exit( EXITBAD );
        }

        /* Create the stdout, which is also the merged out+err, pipe. */
        if ( globs.pipe_action == 2 )
        {
            if ( !CreatePipe( &cmdtab[ slot ].pipe_err[ 0 ],
                &cmdtab[ slot ].pipe_err[ 1 ], &sa, 0 ) )
            {
                perror( "CreatePipe" );
                exit( EXITBAD );
            }
        }

        /* Set handle inheritance off for the pipe ends the parent reads from. */
        SetHandleInformation( cmdtab[ slot ].pipe_out[ 0 ], HANDLE_FLAG_INHERIT, 0 );
        if ( globs.pipe_action == 2 )
            SetHandleInformation( cmdtab[ slot ].pipe_err[ 0 ], HANDLE_FLAG_INHERIT, 0 );

        /* Hide the child window, if any. */
        si.dwFlags |= STARTF_USESHOWWINDOW;
        si.wShowWindow = SW_HIDE;

        /* Set the child outputs to the pipes. */
        si.dwFlags |= STARTF_USESTDHANDLES;
        si.hStdOutput = cmdtab[ slot ].pipe_out[ 1 ];
        if ( globs.pipe_action == 2 )
        {
            /* Pipe stderr to the action error output. */
            si.hStdError = cmdtab[ slot ].pipe_err[ 1 ];
        }
        else if ( globs.pipe_action == 1 )
        {
            /* Pipe stderr to the console error output. */
            si.hStdError = GetStdHandle( STD_ERROR_HANDLE );
        }
        else
        {
            /* Pipe stderr to the action merged output. */
            si.hStdError = cmdtab[ slot ].pipe_out[ 1 ];
        }

        /* Let the child inherit stdin, as some commands assume it's available. */
        si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);

        /* Save the operation for exec_wait() to find. */
        cmdtab[ slot ].func = func;
        cmdtab[ slot ].closure = closure;
        if ( action && target )
        {
            string_copy( &cmdtab[ slot ].action, action );
            string_copy( &cmdtab[ slot ].target, target );
        }
        else
        {
            string_free( &cmdtab[ slot ].action );
            string_new ( &cmdtab[ slot ].action );
            string_free( &cmdtab[ slot ].target );
            string_new ( &cmdtab[ slot ].target );
        }
        string_copy( &cmdtab[ slot ].command, command_orig );

        /* Put together the command we run. */
        {
            char * * argp = argv;
            string_new( &cmd );
            string_copy( &cmd, *(argp++) );
            while ( *argp )
            {
                string_push_back( &cmd, ' ' );
                string_append( &cmd, *(argp++) );
            }
        }

        /* Create output buffers. */
        string_new( &cmdtab[ slot ].buffer_out );
        string_new( &cmdtab[ slot ].buffer_err );

        /* Run the command by creating a sub-process for it. */
        if (
            ! CreateProcess(
                NULL                    ,  /* application name               */
                cmd.value               ,  /* command line                   */
                NULL                    ,  /* process attributes             */
                NULL                    ,  /* thread attributes              */
                TRUE                    ,  /* inherit handles                */
                CREATE_NEW_PROCESS_GROUP,  /* create flags                   */
                NULL                    ,  /* env vars, null inherits env    */
                NULL                    ,  /* current dir, null is our       */
                                           /* current dir                    */
                &si                     ,  /* startup info                   */
                &cmdtab[ slot ].pi         /* child process info, if created */
                )
            )
        {
            perror( "CreateProcess" );
            exit( EXITBAD );
        }

        /* Clean up temporary stuff. */
        string_free( &cmd );
    }

    /* Wait until we are under the limit of concurrent commands. Do not trust
     * globs.jobs alone.
     */
    while ( ( cmdsrunning >= MAXJOBS ) || ( cmdsrunning >= globs.jobs ) )
        if ( !exec_wait() )
            break;

    if ( argv != argv_static )
        free_argv( argv );
}
Beispiel #8
0
void
execcmd( 
	char *string,
	void (*func)( void *closure, int status, timing_info* ),
	void *closure,
	LIST *shell )
{
    int pid;
    int slot;
    int raw_cmd = 0 ;
    char *argv_static[ MAXARGC + 1 ];	/* +1 for NULL */
    char **argv = argv_static;
    char *p;

    /* Check to see if we need to hack around the line-length limitation. */
    /* Look for a JAMSHELL setting of "%", indicating that the command
     * should be invoked directly */
    if ( shell && !strcmp(shell->string,"%") && !list_next(shell) )
    {
        raw_cmd = 1;
        shell = 0;
    }

    if ( !is_win95_defined )
        set_is_win95();
          
    /* Find a slot in the running commands table for this one. */
    if ( is_win95 )
    {
        /* only synchronous spans are supported on Windows 95/98 */
        slot = 0;
    }
    else
    {
        for( slot = 0; slot < MAXJOBS; slot++ )
            if( !cmdtab[ slot ].pid )
                break;
    }
    if( slot == MAXJOBS )
    {
        printf( "no slots for child!\n" );
        exit( EXITBAD );
    }
  
    if( !cmdtab[ slot ].tempfile )
    {
        const char *tempdir = path_tmpdir();
        DWORD procID = GetCurrentProcessId();
  
        /* SVA - allocate 64 other just to be safe */
        cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 64 );
        if ( DEBUG_PROFILE )
            profile_memory( strlen( tempdir ) + 64 );
  
        sprintf( cmdtab[ slot ].tempfile, "%s\\jam%d-%02d.bat", 
                 tempdir, procID, slot );		
    }

    /* Trim leading, ending white space */

    while( isspace( *string ) )
        ++string;

    /* Write to .BAT file unless the line would be too long and it
     * meets the other spawnability criteria.
     */
    if( raw_cmd && can_spawn( string ) >= MAXLINE )
    {
        if( DEBUG_EXECCMD )
            printf("Executing raw command directly\n");        
    }
    else
    {
        FILE *f = 0;
        int tries = 0;
        raw_cmd = 0;
        
        /* Write command to bat file. For some reason this open can
           fails intermitently. But doing some retries works. Most likely
           this is due to a previously existing file of the same name that
           happens to be opened by an active virus scanner. Pointed out,
           and fix by Bronek Kozicki. */
        for (; !f && tries < 4; ++tries)
        {
            f = fopen( cmdtab[ slot ].tempfile, "w" );
            if ( !f && tries < 4 ) Sleep( 250 );
        }
        if (!f)
        {
            printf( "failed to write command file!\n" );
            exit( EXITBAD );
        }
        fputs( string, f );
        fclose( f );

        string = cmdtab[ slot ].tempfile;
        
        if( DEBUG_EXECCMD )
        {
            if (shell)
                printf("using user-specified shell: %s", shell->string);
            else
                printf("Executing through .bat file\n");
        }
    }

    /* Forumulate argv */
    /* If shell was defined, be prepared for % and ! subs. */
    /* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */

    if( shell )
    {
        int i;
        char jobno[4];
        int gotpercent = 0;

        sprintf( jobno, "%d", slot + 1 );

        for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) )
        {
            switch( shell->string[0] )
            {
            case '%':	argv[i] = string; gotpercent++; break;
            case '!':	argv[i] = jobno; break;
            default:	argv[i] = shell->string;
            }
            if( DEBUG_EXECCMD )
                printf( "argv[%d] = '%s'\n", i, argv[i] );
        }

        if( !gotpercent )
            argv[i++] = string;

        argv[i] = 0;
    }
    else if (raw_cmd)
    {
        argv = string_to_args(string);
    }
    else
    {
        /* don't worry, this is ignored on Win95/98, see later.. */
        argv[0] = "cmd.exe";
        argv[1] = "/Q/C";		/* anything more is non-portable */
        argv[2] = string;
        argv[3] = 0;
    }

    /* Catch interrupts whenever commands are running. */

    if( !cmdsrunning++ )
        istat = signal( SIGINT, onintr );

    /* Start the command */

    /* on Win95, we only do a synchronous call */
    if ( is_win95 )
    {
        static const char* hard_coded[] =
            {
                "del", "erase", "copy", "mkdir", "rmdir", "cls", "dir",
                "ren", "rename", "move", 0
            };
          
        const char**  keyword;
        int           len, spawn = 1;
        int           result;
        timing_info time = {0,0};
          
        for ( keyword = hard_coded; keyword[0]; keyword++ )
        {
            len = strlen( keyword[0] );
            if ( strnicmp( string, keyword[0], len ) == 0 &&
                 !isalnum(string[len]) )
            {
                /* this is one of the hard coded symbols, use 'system' to run */
                /* them.. except for "del"/"erase"                            */
                if ( keyword - hard_coded < 2 )
                    result = process_del( string );
                else
                    result = system( string );

                spawn  = 0;
                break;
            }
        }
          
        if (spawn)
        {
            char**  args;
            
            /* convert the string into an array of arguments */
            /* we need to take care of double quotes !!      */
            args = string_to_args( string );
            if ( args )
            {
#if 0
                char** arg;
                fprintf( stderr, "%s: ", args[0] );
                arg = args+1;
                while ( arg[0] )
                {
                    fprintf( stderr, " {%s}", arg[0] );
                    arg++;
                }
                fprintf( stderr, "\n" );
#endif              
                result = spawnvp( P_WAIT, args[0], args );
                record_times(result, &time);
                free_argv( args );
            }
            else
                result = 1;
        }
        func( closure, result ? EXEC_CMD_FAIL : EXEC_CMD_OK, &time );
        return;
    }

    if( DEBUG_EXECCMD )
    {
        char **argp = argv;

        printf("Executing command");
        while(*argp != 0)
        {
            printf(" [%s]", *argp);
            argp++;
        }
        printf("\n");
    }

    /* the rest is for Windows NT only */
    /* spawn doesn't like quotes around the command name */
    if ( argv[0][0] == '"')
    {
        int l = strlen(argv[0]);

        /* Clobber any closing quote, shortening the string by one
         * element */
        if (argv[0][l-1] == '"')
            argv[0][l-1] = '\0';
        
        /* Move everything *including* the original terminating zero
         * back one place in memory, covering up the opening quote */
        memmove(argv[0],argv[0]+1,l);
    }
    if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 )
    {
        perror( "spawn" );
        exit( EXITBAD );
    }
    /* Save the operation for execwait() to find. */

    cmdtab[ slot ].pid = pid;
    cmdtab[ slot ].func = func;
    cmdtab[ slot ].closure = closure;

    /* Wait until we're under the limit of concurrent commands. */
    /* Don't trust globs.jobs alone.                            */

    while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs )
        if( !execwait() )
            break;
    
    if (argv != argv_static)
    {
        free_argv(argv);
    }
}
Beispiel #9
0
LIST * var_get( struct module_t * module, OBJECT * symbol )
{
    LIST * result = L0;
#ifdef OPT_AT_FILES
    /* Some "fixed" variables... */
    if ( object_equal( symbol, constant_TMPDIR ) )
    {
        list_free( saved_var );
        result = saved_var = list_new( object_new( path_tmpdir()->value ) );
    }
    else if ( object_equal( symbol, constant_TMPNAME ) )
    {
        list_free( saved_var );
        result = saved_var = list_new( path_tmpnam() );
    }
    else if ( object_equal( symbol, constant_TMPFILE ) )
    {
        list_free( saved_var );
        result = saved_var = list_new( path_tmpfile() );
    }
    else if ( object_equal( symbol, constant_STDOUT ) )
    {
        list_free( saved_var );
        result = saved_var = list_new( object_copy( constant_STDOUT ) );
    }
    else if ( object_equal( symbol, constant_STDERR ) )
    {
        list_free( saved_var );
        result = saved_var = list_new( object_copy( constant_STDERR ) );
    }
    else
#endif
    {
        VARIABLE * v;
        int n;

        if ( ( n = module_get_fixed_var( module, symbol ) ) != -1 )
        {
            if ( DEBUG_VARGET )
                var_dump( symbol, module->fixed_variables[ n ], "get" );
            result = module->fixed_variables[ n ];
        }
        else if ( module->variables && ( v = (VARIABLE *)hash_find(
            module->variables, symbol ) ) )
        {
            if ( DEBUG_VARGET )
                var_dump( v->symbol, v->value, "get" );
            result = v->value;
        }

#ifdef OS_VMS
        else if ( ( module->name && object_equal( module->name, constant_ENVIRON ) )
                  || root_module() == module )
        {
            /* On VMS, when a variable from root or ENVIRON module is not found,
             * explicitly request it from the process.
             * By design, process variables (and logicals) are not made available
             * to C main(), and thus will not get loaded in bulk to root/ENVRON.
             * So we get around it by getting any such variable on first request.
             */
            const char * val = getenv( object_str( symbol ) );

            if ( val )
            {
                struct module_t * environ_module = module;
                char * environ[ 2 ] = { 0 }; /* NULL-terminated */
                string buf[ 1 ];

                if ( root_module() == module )
                {
                    environ_module = bindmodule( constant_ENVIRON );
                }

                string_copy( buf, object_str( symbol ) );
                string_append( buf, "=" );
                string_append( buf, val );

                environ[ 0 ] = buf->value;

                /* Load variable to global module, with splitting, for backward
                 * compatibility. Then to .ENVIRON, without splitting.
                 */
                var_defines( root_module(), environ, 1 );
                var_defines( environ_module, environ, 0 );
                string_free( buf );

                if ( module->variables && ( v = (VARIABLE *)hash_find(
                    module->variables, symbol ) ) )
                {
                    if ( DEBUG_VARGET )
                        var_dump( v->symbol, v->value, "get" );
                    result = v->value;
                }
            }
        }
#endif
    }
    return result;
}