adtools/clib2

View on GitHub
library/unistd_execvp.c

Summary

Maintainability
Test Coverage
/*
 * $Id: unistd_execvp.c,v 1.7 2006-09-25 14:51:15 obarthel Exp $
 *
 * :ts=4
 *
 * Portable ISO 'C' (1994) runtime library for the Amiga computer
 * Copyright (c) 2002-2015 by Olaf Barthel <obarthel (at) gmx.net>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Neither the name of Olaf Barthel nor the names of contributors
 *     may be used to endorse or promote products derived from this
 *     software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _UNISTD_HEADERS_H
#include "unistd_headers.h"
#endif /* _UNISTD_HEADERS_H */

/****************************************************************************/

/* The following is not part of the ISO 'C' (1994) standard. */

/****************************************************************************/

/* A local version of the BSD strsep() function. */
static char *
local_strsep(char ** string_ptr, const char * delimiter)
{
    char * result = NULL;
    char * string;

    assert( string_ptr != NULL && delimiter != NULL );

    string = (*string_ptr);
    if(string != NULL)
    {
        char * token = string;
        char c;

        while(TRUE)
        {
            c = (*string++);

            /* Reached the end of the string? */
            if(c == '\0')
            {
                /* No further string data available. */
                (*string_ptr) = NULL;

                result = token;
                break;
            }

            /* Is that character a delimiter? */
            if(strchr(delimiter,c) != NULL)
            {
                /* NUL-terminate the string, overwriting
                   the delimiter character */
                string[-1] = '\0';

                /* Scanning can resume with the next following
                   character. */
                (*string_ptr) = string;

                result = token;
                break;
            }
        }
    }

    return(result);
}

/****************************************************************************/

int
execvp(const char *command,char * const argv[])
{
    char * command_buffer = NULL;
    size_t command_name_len,i;
    char * path_copy = NULL;
    int result = -1;
    BOOL found_path_separators;

    /* Do not allow null command */
    if(command == NULL || (*command) == '\0')
    {
        __set_errno(ENOENT);
        goto out;
    }

    command_name_len = strlen(command);

    /* Check if there are any path separator characters in the
       command name. */
    found_path_separators = FALSE;

    for(i = 0 ; i < command_name_len ; i++)
    {
        if(command[i] == '/' || command[i] == ':')
        {
            found_path_separators = TRUE;
            break;
        }
    }

    /* If it's an absolute or relative path name, it's easy. */
    if(found_path_separators)
    {
        result = execve(command, argv, environ);
    }
    else
    {
        size_t command_buffer_size = 0;
        const char * path_delimiter;
        char * path;
        const char * search_prefix;
        size_t search_prefix_len;
        size_t complete_path_len;
        int error;

        /* We first look up the PATH environment variable because
           we will be making a copy of it. This avoids trouble
           lateron when we will be calling getenv() again. */
        path = getenv("PATH");
        if(path == NULL)
            path = (char *)__default_path;

        path_copy = strdup(path);
        if(path_copy == NULL)
        {
            __set_errno(ENOMEM);
            goto out;
        }

        path = path_copy;

        path_delimiter = getenv("PATH_SEPARATOR");
        if(path_delimiter == NULL)
            path_delimiter = __default_path_delimiter;

        while((search_prefix = local_strsep(&path,path_delimiter)) != NULL)
        {
            if((*search_prefix) == '\0')
                search_prefix = ".";

            search_prefix_len = strlen(search_prefix);

            complete_path_len = search_prefix_len + 1 + command_name_len;
            if(complete_path_len + 1 > command_buffer_size)
            {
                char * new_command_buffer;

                /* Allocate a little more memory than we
                   really need. */
                new_command_buffer = malloc(complete_path_len + 10);
                if(new_command_buffer == NULL)
                {
                    __set_errno(ENOMEM);
                    goto out;
                }

                if(command_buffer != NULL)
                    free(command_buffer);

                command_buffer        = new_command_buffer;
                command_buffer_size    = complete_path_len + 10;
            }

            /* Combine the search prefix with the command name. */
            memcpy(command_buffer,search_prefix,search_prefix_len);
            command_buffer[search_prefix_len] = '/';
            memcpy(&command_buffer[search_prefix_len+1],command,command_name_len);
            command_buffer[complete_path_len] = '\0';

            /* Now try to run that command. */
            result = execve(command_buffer, argv, environ);

            /* Did it work? And if it didn't work, did it fail because
               the command to be run could not be executed? */
            error = __get_errno();

            if(result == 0 ||
               (error != EACCES &&
                error != EISDIR &&
                error != ENOENT &&
                error != ENOEXEC &&
                error != EPERM))
            {
                break;
            }

            /* Just in case somebody wants to quit... */
            __check_abort();
        }
    }

 out:

    if(path_copy != NULL)
        free(path_copy);

    if(command_buffer != NULL)
        free(command_buffer);

    return(result);
}