files.c   [plain text]


/*
 * Copyright (c) 2010 Apple Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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.
 *
 * Portions of this software have been released under the following terms:
 *
 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
 *
 * To anyone who acknowledges that this file is provided "AS IS"
 * without any express or implied warranty:
 * permission to use, copy, modify, and distribute this file for any
 * purpose is hereby granted without fee, provided that the above
 * copyright notices and this notice appears in all source code copies,
 * and that none of the names of Open Software Foundation, Inc., Hewlett-
 * Packard Company or Digital Equipment Corporation be used
 * in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  Neither Open Software
 * Foundation, Inc., Hewlett-Packard Company nor Digital
 * Equipment Corporation makes any representations about the suitability
 * of this software for any purpose.
 *
 * Copyright (c) 2007, Novell, Inc. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Novell Inc. nor the names of its contributors
 *     may be used to endorse or promote products derived from this
 *     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 HOLDERS 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.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

/*
**
**  NAME:
**
**      files.c
**
**  FACILITY:
**
**      Interface Definition Language (IDL) Compiler
**
**  ABSTRACT:
**
**  IDL file manipulation routines.
**
**  VERSION: DCE 1.0
**
*/

#include <sys/types.h>
#include <sys/stat.h>

#include <nidl.h>
#include <files.h>
#include <unistd.h>
#include "message.h"

/*
**  Default filespec; only good for one call to FILE_parse.
*/
char const *FILE_def_filespec = NULL;

/*
**  F I L E _ o p e n
**
**  Opens an existing file for read access.
*/

boolean FILE_open               /* Returns TRUE on success */
(
    char        *filespec,      /* [in] Filespec */
    FILE        **fid           /*[out] File handle; ==NULL on FALSE status */
)
{
    if ((*fid = fopen(filespec, "r")) == NULL)
    {
        idl_error_list_t errvec[2];
        errvec[0].msg_id = NIDL_OPENREAD;
        errvec[0].arg[0] = filespec;
        errvec[1].msg_id = NIDL_SYSERRMSG;
        errvec[1].arg[0] = strerror(errno);
        error_list(2, errvec, TRUE);
    }

    return TRUE;
}

/*
**  F I L E _ c r e a t e
**
**  Creates and opens a new file for write access.
*/

boolean FILE_create             /* Returns TRUE on success */
(
    char        *filespec,      /* [in] Filespec */
    FILE        **fid           /*[out] File handle; ==NULL on FALSE status */
)
{
#define MODE_WRITE "w"

    if ((*fid = fopen(filespec, MODE_WRITE)) == NULL)
    {
        idl_error_list_t errvec[2];
        errvec[0].msg_id = NIDL_OPENWRITE;
        errvec[0].arg[0] = filespec;
        errvec[1].msg_id = NIDL_SYSERRMSG;
        errvec[1].arg[0] = strerror(errno);
        error_list(2, errvec, TRUE);
    }

    return TRUE;
}

/*
**  F I L E _ l o o k u p
**
**  Looks for the specified file first in the working directory,
**  and then in the list of specified directories.
**
**  Returns:    TRUE if file was found, FALSE otherwise
*/

boolean FILE_lookup             /* Returns TRUE on success */
(
    char const  *filespec,      /* [in] Filespec */
    char const  * const *idir_list,    /* [in] Array of directories to search */
                                /*      NULL => just use filespec */
    struct stat *stat_buf,      /*[out] Stat buffer - see stat.h */
    char        *lookup_spec,   /*[out] Filespec of found file (on success) */
	size_t lookup_spec_len		/* [in] len of lookup_spec */
)
{
#ifdef HASDIRTREE
    int     i;

    /*
     * First try the filespec by itself.
     */
    if (stat(filespec, stat_buf) != -1)
    {
        strlcpy(lookup_spec, filespec, lookup_spec_len);
        return TRUE;
    }

    /*
     * Fail if idir_list is null.
     */
    if (idir_list == NULL)
        return FALSE;

    /*
     * Lookup other pathnames using the directories in the idir_list.
     */
    for (i = 0; idir_list[i]; i++)
    {
        if (FILE_form_filespec(filespec, idir_list[i], (char *)NULL,
                               (char *)NULL, lookup_spec, lookup_spec_len)
            &&  stat(lookup_spec, stat_buf) != -1)
            return TRUE;
    }

    /*
     * On Unix-like filesystems, make another pass over the idir_list if the
     * search filespec has a directory name, prepending each idir to the search
     * filespec.  For example, importing "y/z.idl" will match "/x/y/z.idl" if
     * -I/x is on the command line.
     */
    if (*filespec != BRANCHCHAR && FILE_has_dir_info(filespec))
    {
        for (i = 0; idir_list[i]; i++)
        {
            sprintf(lookup_spec, "%s%c%s", idir_list[i], BRANCHCHAR, filespec);
            if (stat(lookup_spec, stat_buf) != -1)
                return TRUE;
        }
    }
#else
    error(NIDL_FNUNIXONLY, __FILE__, __LINE__);
#endif

    return FALSE;
}

/*
**  F I L E _ f o r m _ f i l e s p e c
**
**  Forms a file specification from the specified components.
*/

boolean FILE_form_filespec      /* Returns TRUE on success */
(                               /* For all [in] args, NULL => none */
    char const  *in_filespec,   /* [in] Filespec (full or partial) */
    char const  *dirspec,       /* [in] Directory; used if in_filespec */
                                /*      doesn't have directory field */
    char const  *type,          /* [in] Filetype; used if in_filespec */
                                /*      doesn't have filetype field */
    char const  *rel_filespec,  /* [in] Related filespec; fields are used to */
                                /*      fill in missing components after */
                                /*      applying in_filespec, dir, type */
    char        *out_filespec,   /*[out] Full filespec formed */
	size_t		out_filespec_len /* [in] len of out_filespec */
)
{
    char const *dir = NULL;        /* Directory specified */
    char       in_dir[PATH_MAX];   /* Directory part of in_filespec */
    char       in_name[PATH_MAX];  /* Filename part of in_filespec */
    char       in_type[PATH_MAX];  /* Filetype part of in_filespec */
    char       rel_dir[PATH_MAX];  /* Directory part of rel_filespec */
    char       rel_name[PATH_MAX]; /* Filename part of rel_filespec */
    char       rel_type[PATH_MAX]; /* Filetype part of rel_filespec */
    char const *res_dir;           /* Resultant directory */
    char const *res_name;          /* Resultant filename */
    char const *res_type;          /* Resultant filetype */

    in_dir[0]   = '\0';
    in_name[0]  = '\0';
    in_type[0]  = '\0';
    rel_dir[0]  = '\0';
    rel_name[0] = '\0';
    rel_type[0] = '\0';
    res_dir     = "";
    res_name    = "";
    res_type    = "";

    /* Parse in_filespec into its components. */
    if (in_filespec != NULL && in_filespec[0] != '\0')
    {
        /*
         * Setup the related or file type global FILE_def_filespec such that
         * any file lookup is handled appropriately in FILE_parse.
         */
        if (rel_filespec)
            FILE_def_filespec = rel_filespec;
        else if (type)
            FILE_def_filespec = type;

        if (!FILE_parse(in_filespec, in_dir, sizeof (in_dir), in_name, sizeof(in_name), in_type, sizeof(in_type)))
            return FALSE;
    }

    if (dir == NULL)
	dir = dirspec;

    /* Parse rel_filespec into its components. */
    if (rel_filespec != NULL && rel_filespec[0] != '\0')
        if (!FILE_parse(rel_filespec, rel_dir, sizeof(rel_dir), rel_name, sizeof(rel_name), rel_type, sizeof(rel_type)))
            return FALSE;

    /* Apply first valid of in_dir, dir, or rel_dir. */
    if (in_dir[0] != '\0')
        res_dir = in_dir;
    else if (dir != NULL && dir[0] != '\0')
        res_dir = dir;
    else if (rel_dir[0] != '\0')
        res_dir = rel_dir;

    /* Apply first valid of in_name, rel_name. */
    if (in_name[0] != '\0')
        res_name = in_name;
    else if (rel_name[0] != '\0')
        res_name = rel_name;

    /* Apply first valid of in_type, type, rel_type.  Note that rel_type is
     * only applied if in_filespec is null.
     */
    if (in_type[0] != '\0')
        res_type = in_type;
    else if (type != NULL && type[0] != '\0')
        res_type = type;
    else if (rel_type[0] != '\0')
        res_type = rel_type;

#ifdef HASDIRTREE

    /* Concatenate the result. */

    out_filespec[0] = '\0';

    if (res_dir[0] != '\0')
    {
        strlcat(out_filespec, res_dir, out_filespec_len);
        strlcat(out_filespec, BRANCHSTRING, out_filespec_len);
    }

    if (res_name[0] != '\0')
        strlcat(out_filespec, res_name, out_filespec_len);

    if (res_type[0] != '\0')
        strlcat(out_filespec, res_type, out_filespec_len); /* The '.' is part of the filetype */

    return TRUE;

#else
    error(NIDL_FNUNIXONLY, __FILE__, __LINE__);
#endif
}

/*
**  F I L E _ p a r s e
**
**  Parses a specified pathanme into individual components.
*/

boolean FILE_parse              /* Returns TRUE on success */
(
    char const  *filespec,      /* [in] Filespec */
    char        *dir,           /*[i,o] Directory portion; NULL =>don't want */
    size_t	dir_len,		/*[i] len of dir */
    char        *name,          /*[i,o] Filename portion;  NULL =>don't want */
    size_t	name_len ATTRIBUTE_UNUSED,		/*[i] len of name */
    char        *type,          /*[i,o] File type (ext);   NULL =>don't want */
    size_t	type_len		/*[i] len of type */
)
{
#if defined(HASDIRTREE)
    FILE_k_t    filekind;       /* File kind */
    char const  *pn;
    int         pn_len,
                leaf_len;
    int         i,
                j;
    int         leaf_start,
                ext_start;
    int         dir_end,
                leaf_end;
    boolean     slash_seen,
                dot_seen;

    /* Init return values. */
    if (dir)
        dir[0] = '\0';
    if (name)
        name[0] = '\0';
    if (type)
        type[0] = '\0';

    /*
     * If the filespec has BRANCHCHAR do special case check to see if pathname
     * is a directory to prevent directory /foo/bar from being interpreted as
     * directory /foo file bar.
     */
    if (strchr(filespec, BRANCHCHAR)
        &&  FILE_kind(filespec, &filekind)
        &&  filekind == file_dir)
    {
        strlcpy(dir, filespec, dir_len);
        return TRUE;
    }

    /*
     *  Scan backwards looking for a BRANCHCHAR -
     *  If not found, then no directory was specified.
     */
    pn = filespec;
    pn_len = strlen(pn);
    slash_seen = FALSE;
    dir_end = -1;
    leaf_start = 0;
    dot_seen = FALSE;

    /*
     * For temporary VMS support, until full file capabilities are in place,
     * look for the defined BRANCHCHAR or a colon, which is indicative of a
     * device name or logical name.  Device and directory information is
     * collectively returned as the dir argument.
     */
    for (i = pn_len - 1; i >= 0; i--)
        if (pn[i] == BRANCHCHAR
#if BRANCHAR == '\\'
            || pn[i] == '/'
#endif
           )
        {
            /*
             * On VMS, the BRANCHCHAR is considered part of the directory.
             */
            leaf_start = i + 1;
            dir_end = i > 0 ? i : 1;
            slash_seen = TRUE;
            break;
        }

    if (dir)
    {
        if (slash_seen)
        {
            strncpy(dir, pn, dir_end);
            dir[dir_end] = '\0';
        }
        else
            dir[0] = '\0';
    }

    /*
     *  Start scanning from the BRANCHCHAR for a '.' to find the leafname.
     */
    ext_start = pn_len;
    leaf_end = pn_len;

    for (j = pn_len; j > leaf_start; --j)
        if (pn[j] == '.')
        {
            leaf_end = j - 1;
            ext_start = j;      /* Extension includes the '.' */
            dot_seen = TRUE;
            break;
        }

    if (leaf_end >= dir_end + 1)
    {
        leaf_len = dot_seen ? leaf_end - leaf_start + 1 : leaf_end - leaf_start;
        if (name)
        {
            strncpy(name, &pn[leaf_start], leaf_len);
            name[leaf_len] = '\0';
        }

        if (!dot_seen)
        {
            if (type)
                type[0] = '\0';
            return TRUE;
        }
        else
        {
        if (type)
            strlcpy(type, &pn[ext_start], type_len);
        }
    }

    return TRUE;

#else
    error(NIDL_FNUNIXONLY, __FILE__, __LINE__);
    return FALSE;
#endif
}


/*
**  F I L E _ h a s _ d i r _ i n f o
**
**  Returns:    TRUE if filespec includes directory information.
*/

boolean FILE_has_dir_info
(
    char const  *filespec       /* [in] Filespec */
)
{
    char    dir[PATH_MAX];      /* Directory part of filespec */

    if (!FILE_parse(filespec, dir, sizeof(dir), (char *)NULL, 0, (char *)NULL, 0))
        return FALSE;

    return (dir[0] != '\0');
}

/*
**  F I L E _ i s _ c w d
**
**  Returns:    TRUE if filespec is equivalent to the current working directory.
*/

boolean FILE_is_cwd
(
    char        *filespec       /* [in] Filespec */
)
{
    char    *cwd;       /* Current working directory */
    char    *twd;       /* Temp working directory = filespec argument */
    boolean result;     /* Function result */

    /* Null filespec => current working directory. */
    if (filespec[0] == '\0')
        return TRUE;

    /* Get current working directory. */
    cwd = getcwd((char *)NULL, PATH_MAX);
    if (cwd == NULL)
        return FALSE;

    /* chdir to the passed directory filespec. */
    if (chdir(filespec) != 0)
    {
        /* Can chdir; probably a bogus directory. */
        free(cwd);
        return FALSE;
    }

    /*
     * Again get current working directory - this gets us the passed
     * directory filespec in a "normallized form".
     */
    twd = getcwd((char *)NULL, PATH_MAX);
    if (twd == NULL)
    {
        free(cwd);
        return FALSE;
    }

    if (strcmp(cwd, twd) == 0)
        result = TRUE;
    else
    {
        /* Not current working directory; be sure to chdir back to original! */
        result = FALSE;
        chdir(cwd);
    }

    /* Free storage malloc'ed by getcwd(). */
    free(cwd);
    free(twd);

    return result;
}

/*
**  F I L E _ k i n d
**
**  Returns whether a pathname is a directory, a file, or something else.
*/

boolean FILE_kind               /* Returns TRUE on success */
(
    char const  *filespec,      /* [in] Filespec */
    FILE_k_t    *filekind       /*[out] File kind (on success) */
)
{
    struct stat fileinfo;

    if (stat(filespec, &fileinfo) == -1)
        return FALSE;

    switch (fileinfo.st_mode & S_IFMT)
    {
    case S_IFDIR:
        *filekind = file_dir;
        break;

    case S_IFREG:
        *filekind = file_file;
        break;

    default:
        *filekind = file_special;
    }

    return TRUE;
}

/*
**  F I L E _ c o n t a i n s _ e v _ r e f
**
**  Scans a pathname to see if it contains an environment variable reference.
*/

boolean FILE_contains_ev_ref    /* Returns TRUE if filespec contains an */
                                /* environment variable reference */
(
    STRTAB_str_t    fs_id       /* [in] Filespec stringtable ID */
)
{
    char const  *pn;
    unsigned int         i;

    STRTAB_str_to_string(fs_id, &pn);

    for (i = 0; i < strlen(pn) - 1; i++)
        if (pn[i] == '$' && pn[i + 1] == '(')
            return TRUE;

    return FALSE;
}

/*
**  F I L E _ e x e c u t e _ c m d
**
**  This routine executes the specified command string with
**  the specified parameters.  All error output goes to the
**  default output/error device.
*/

int FILE_execute_cmd
(
    char    *cmd_string,        /* command to execute */
    char    *p1,                /* parameter1 */
    char    *p2,                /* parameter2 */
    long    msg_id              /* Optional msg_id to output */
)
{
    char    *cmd;       /* Command derived from inputs */
    int     status;
	size_t	cmd_len = 0;

    /* Alloc space and create command string */
	cmd_len = strlen(cmd_string) + strlen(p1) + strlen(p2) + 3;
    cmd = NEW_VEC (char, cmd_len);
    cmd[0] = '\0';
    strlcat(cmd, cmd_string, cmd_len);
    strlcat(cmd, " ", cmd_len);
    strlcat(cmd, p1, cmd_len);
    strlcat(cmd, " ", cmd_len);
    strlcat(cmd, p2, cmd_len);

    /* Output a message, if msg_id specified is non-zero */
    if (msg_id != 0)
        message_print(msg_id, (char*)cmd);

    /* Execute the command, errors to default output device */
    status = system(cmd);

    /* Free the command string */
    FREE(cmd);

    return status;
}

/*
**  F I L E _ d e l e t e
**
**  This routine deletes the file specified by the filename
**  string specified.
*/

void FILE_delete
(
    char    *filename
)
{
    unlink (filename);
}
/* preserve coding style vim: set tw=78 sw=4 : */