814 lines
17 KiB
C
814 lines
17 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these libraries and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
/*
|
|
* $XConsortium: util_file.c /main/4 1995/11/06 18:54:05 rswiston $
|
|
*
|
|
* @(#)util_file.c 1.24 19 Apr 1995 cde_app_builder/src/libAButil
|
|
*
|
|
* RESTRICTED CONFIDENTIAL INFORMATION:
|
|
*
|
|
* The information in this document is subject to special
|
|
* restrictions in a confidential disclosure agreement between
|
|
* HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
|
|
* document outside HP, IBM, Sun, USL, SCO, or Univel without
|
|
* Sun's specific written approval. This document and all copies
|
|
* and derivative works thereof must be returned or destroyed at
|
|
* Sun's request.
|
|
*
|
|
* Copyright 1993 Sun Microsystems, Inc. All rights reserved.
|
|
*
|
|
*/
|
|
|
|
|
|
/*
|
|
* File: util_file.c
|
|
*/
|
|
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <ab_private/abio.h>
|
|
#include "utilP.h"
|
|
|
|
/*************************************************************************
|
|
** **
|
|
** Private Function Declarations **
|
|
** **
|
|
**************************************************************************/
|
|
|
|
/*************************************************************************
|
|
** **
|
|
** Data **
|
|
** **
|
|
**************************************************************************/
|
|
#define AB_EXT_LENGTH 3
|
|
|
|
/*************************************************************************
|
|
** **
|
|
** Function Definitions **
|
|
** **
|
|
**************************************************************************/
|
|
|
|
/*
|
|
* removes all buffering from a file. This is useful for debugging
|
|
* to avoid losing data, and to keep stdout and stderr messages from
|
|
* getting jumbled (ordered is basically guaranteed if both streams
|
|
* are unbuffered).
|
|
*
|
|
* This *is* a performance hit, so don't use this except when debugging.
|
|
* Note that stderr is always unbuffered.
|
|
*/
|
|
int
|
|
util_unbuffer_file(FILE *fp)
|
|
{
|
|
int iReturn= 0;
|
|
int iRC= 0; /* return code */
|
|
int fileMode= 0;
|
|
|
|
setbuf(fp, NULL);
|
|
/* this is too much - actually waits for sync to disk, and runs
|
|
* RIDICULOUSLY SLOW.
|
|
*/
|
|
/*
|
|
if ((iRC= fileMode= fcntl(fileno(fp), F_GETFL)) >= 0)
|
|
{
|
|
fileMode |= O_DSYNC;
|
|
iRC= fcntl(fileno(fp), F_SETFL, fileMode);
|
|
}
|
|
*/
|
|
iReturn= iRC;
|
|
|
|
return iReturn;
|
|
}
|
|
|
|
|
|
BOOL
|
|
util_file_exists(STRING fileName)
|
|
{
|
|
struct stat fileInfo;
|
|
return (stat(fileName, &fileInfo) == 0);
|
|
}
|
|
|
|
|
|
/* Returns the file size, or ERR_ value (<0) on error
|
|
*
|
|
*/
|
|
long
|
|
util_file_size(STRING fileName)
|
|
{
|
|
struct stat fileInfo;
|
|
if (stat(fileName, &fileInfo) != 0)
|
|
{
|
|
return ERR_FILE_NOT_FOUND;
|
|
}
|
|
return (long)fileInfo.st_size;
|
|
}
|
|
|
|
|
|
/*
|
|
* Extension should not contain it's leading "."
|
|
*
|
|
* if extension is NULL or the empty string, returns TRUE if the name
|
|
* has no extension
|
|
*/
|
|
BOOL
|
|
util_file_name_has_extension(STRING fileName, STRING extension)
|
|
{
|
|
BOOL hasExtension= FALSE;
|
|
|
|
if (extension == NULL)
|
|
{
|
|
hasExtension = ( (fileName == NULL) || (strlen(fileName) == 0) );
|
|
}
|
|
else
|
|
{
|
|
if (fileName == NULL)
|
|
{
|
|
hasExtension= FALSE;
|
|
}
|
|
else
|
|
{
|
|
char *dotPtr= strrchr(fileName, '.');
|
|
if (dotPtr == NULL)
|
|
{
|
|
hasExtension= FALSE;
|
|
}
|
|
else
|
|
{
|
|
hasExtension= util_streq(dotPtr+1, extension);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hasExtension;
|
|
}
|
|
|
|
|
|
BOOL
|
|
util_file_name_has_ab_extension(STRING fileName)
|
|
{
|
|
BOOL hasExtension= FALSE;
|
|
char *dotPtr= strrchr(fileName, '.');
|
|
|
|
if (dotPtr != NULL)
|
|
{
|
|
hasExtension= (strncmp(dotPtr, ".bi", 3) == 0);
|
|
}
|
|
return hasExtension;
|
|
}
|
|
|
|
|
|
STRING
|
|
util_get_file_name_from_path(
|
|
STRING path,
|
|
STRING fileNameBuf,
|
|
int fileNameBufSize
|
|
)
|
|
{
|
|
char *slashPtr= strrchr(path, '/');
|
|
if (slashPtr == NULL)
|
|
{
|
|
strncpy(fileNameBuf, path, fileNameBufSize);
|
|
}
|
|
else
|
|
{
|
|
strncpy(fileNameBuf, slashPtr+1, fileNameBufSize);
|
|
}
|
|
fileNameBuf[fileNameBufSize-1]= 0;
|
|
return fileNameBuf;
|
|
}
|
|
|
|
|
|
/*
|
|
* If path is not absolute, directory is "."
|
|
*
|
|
* The directory name returned does NOT have a trailing '/'. (The
|
|
* only exception being the directory "/" .
|
|
*/
|
|
STRING
|
|
util_get_dir_name_from_path(
|
|
STRING path,
|
|
STRING dirNameBuf,
|
|
int dirNameBufSize
|
|
)
|
|
{
|
|
char *slashPtr= strrchr(path, '/');
|
|
assert(dirNameBufSize > 1);
|
|
if (slashPtr == NULL)
|
|
{
|
|
/* no directory name */
|
|
strcpy(dirNameBuf, ".");
|
|
}
|
|
else if (slashPtr == path)
|
|
{
|
|
/* file in / */
|
|
strcpy(dirNameBuf, "/");
|
|
}
|
|
else
|
|
{
|
|
/* directory and file name */
|
|
int copyCount=
|
|
util_min(dirNameBufSize, (((int)(slashPtr - path))+1));
|
|
strncpy(dirNameBuf, path, copyCount);
|
|
dirNameBuf[dirNameBufSize-1]= 0;
|
|
while ((copyCount > 1) && (dirNameBuf[copyCount-1] == '/'))
|
|
{
|
|
--copyCount;
|
|
}
|
|
dirNameBuf[copyCount]= 0;
|
|
}
|
|
|
|
return dirNameBuf;
|
|
}
|
|
|
|
|
|
/*
|
|
* Truncates the open file to length bytes.
|
|
*
|
|
* ftruncate() is not defined in POSIX, so the header files don't define
|
|
* it when _POSIX_SOURCE is not defined. We're going to use it, anyway,
|
|
* and this prototype is identical in the header files for all the
|
|
* platforms (Sun/HP/IBM).
|
|
*/
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
extern int ftruncate(int filedes, off_t length);
|
|
#ifdef __cplusplus
|
|
} // extern "C"
|
|
#endif
|
|
int
|
|
util_fdtruncate(int filedes, off_t length)
|
|
{
|
|
return ftruncate(filedes, length);
|
|
}
|
|
|
|
|
|
BOOL
|
|
util_directory_exists(STRING dir_name)
|
|
{
|
|
BOOL exists = FALSE;
|
|
struct stat dir_info;
|
|
|
|
if (stat(dir_name, &dir_info) != 0)
|
|
{
|
|
goto epilogue;
|
|
}
|
|
if (S_ISDIR(dir_info.st_mode))
|
|
{
|
|
exists = TRUE;
|
|
}
|
|
|
|
epilogue:
|
|
return exists;
|
|
}
|
|
|
|
|
|
BOOL
|
|
util_paths_are_same_file(STRING path1, STRING path2)
|
|
{
|
|
BOOL same_file = FALSE;
|
|
struct stat file_info1;
|
|
struct stat file_info2;
|
|
|
|
if (stat(path1, &file_info1) != 0)
|
|
{
|
|
goto epilogue;
|
|
}
|
|
if (stat(path2, &file_info2) != 0)
|
|
{
|
|
goto epilogue;
|
|
}
|
|
|
|
same_file = ( (file_info1.st_dev == file_info2.st_dev)
|
|
&& (file_info1.st_ino == file_info2.st_ino) );
|
|
|
|
epilogue:
|
|
return same_file;
|
|
}
|
|
|
|
|
|
/*
|
|
* Converts the path to a relative path from from_dir.
|
|
*
|
|
* for path and from_dir, NULL, "", or "." = current directory
|
|
*
|
|
* Returns "." if path and from_dir reference the same directory
|
|
*/
|
|
int
|
|
util_cvt_path_to_relative(
|
|
STRING path_in,
|
|
STRING from_dir,
|
|
char *buf,
|
|
int buf_size
|
|
)
|
|
{
|
|
#define path_is_dot(arg_path) \
|
|
(util_strempty(arg_path) || util_streq(arg_path, "."))
|
|
|
|
int return_value = 0;
|
|
BOOL found_relative= FALSE;
|
|
STRING path = NULL;
|
|
BOOL more_path = TRUE;
|
|
char cwd[MAXPATHLEN]= "";
|
|
STRING from= NULL;
|
|
char *rightmost_slash= NULL;
|
|
char *last_rightmost_slash= NULL;
|
|
|
|
if ( (path_is_dot(from_dir) && path_is_dot(path_in))
|
|
|| (util_streq(from_dir, path_in))
|
|
)
|
|
{
|
|
/* the strings are equivalent! */
|
|
strcpy(buf, ".");
|
|
return return_value;
|
|
}
|
|
|
|
/*
|
|
* Determine "from" dir.
|
|
*/
|
|
if (path_is_dot(from_dir))
|
|
{
|
|
from = ".";
|
|
}
|
|
else
|
|
{
|
|
from = from_dir;
|
|
}
|
|
|
|
/*
|
|
* Determine the directory we are trying to convert.
|
|
*/
|
|
if (path_is_dot(path_in))
|
|
{
|
|
if (getcwd(cwd, MAXPATHLEN) == NULL)
|
|
{
|
|
return_value = ERR;
|
|
goto epilogue;
|
|
}
|
|
path = cwd;
|
|
}
|
|
else
|
|
{
|
|
path = path_in;
|
|
}
|
|
|
|
if (!util_directory_exists(from))
|
|
{
|
|
return_value = ERR;
|
|
goto epilogue;
|
|
}
|
|
|
|
rightmost_slash = NULL;
|
|
last_rightmost_slash = NULL;
|
|
more_path = TRUE;
|
|
while ((!found_relative) && more_path)
|
|
{
|
|
if (util_paths_are_same_file(from, path))
|
|
{
|
|
found_relative = TRUE;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Get the name of the next dir up
|
|
*/
|
|
rightmost_slash= strrchr(path, '/');
|
|
if (last_rightmost_slash != NULL)
|
|
{
|
|
*last_rightmost_slash = '/';
|
|
}
|
|
last_rightmost_slash= rightmost_slash;
|
|
|
|
/*
|
|
* Put in a NULL, so that util_paths_are_same_file is happy
|
|
*/
|
|
if (rightmost_slash == NULL)
|
|
{
|
|
more_path = FALSE;
|
|
}
|
|
else
|
|
{
|
|
(*rightmost_slash) = 0;
|
|
}
|
|
}
|
|
|
|
if (rightmost_slash != NULL)
|
|
{
|
|
*rightmost_slash = '/';
|
|
}
|
|
|
|
if (found_relative)
|
|
{
|
|
if (rightmost_slash == NULL)
|
|
{
|
|
/* they are the same damn file (directory)! */
|
|
strncpy(buf, ".", buf_size);
|
|
buf[buf_size-1]= 0;
|
|
}
|
|
else
|
|
{
|
|
while ((*rightmost_slash == '/') && (*rightmost_slash != 0))
|
|
{
|
|
++rightmost_slash;
|
|
}
|
|
strncpy(buf, rightmost_slash, buf_size);
|
|
buf[buf_size-1]= 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strncpy(buf, path, buf_size);
|
|
buf[buf_size-1]= 0;
|
|
}
|
|
|
|
epilogue:
|
|
return return_value;
|
|
#undef path_is_dot
|
|
}
|
|
|
|
/* This routine will create a directory hierarchy in the
|
|
* current working directory if the hierarchy does not
|
|
* already exist. If mkdir fails, errno will be set and
|
|
* a negative value will be returned.
|
|
*/
|
|
int
|
|
util_mkdir_hier(
|
|
STRING path
|
|
)
|
|
{
|
|
STRING slash_ptr = NULL;
|
|
int ret = 0;
|
|
|
|
/* As an example: path -> "x/y/z", so slash_ptr points
|
|
* to the same string.
|
|
*/
|
|
slash_ptr = path;
|
|
while (slash_ptr != NULL)
|
|
{
|
|
/* 1) x/y/z 2) x/y/z 3) x/y/z
|
|
* ^ ^ ^
|
|
* | | |
|
|
* slash_ptr slash_ptr slash_ptr == NULL
|
|
*/
|
|
slash_ptr = strchr(slash_ptr, '/');
|
|
|
|
/* 1) x NULL y/z 2) x/y NULL z
|
|
* ^ ^
|
|
* | |
|
|
* slash_ptr slash_ptr
|
|
*/
|
|
if (slash_ptr != NULL)
|
|
{
|
|
*slash_ptr = 0;
|
|
}
|
|
|
|
/* Make the directory named:
|
|
* 1) "x" 2) "x/y" 3) "x/y/z"
|
|
* in the cwd
|
|
*/
|
|
ret = mkdir(path, 0777);
|
|
|
|
/* If there was an error return -1 */
|
|
if ( ret != 0 && errno != EEXIST )
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if ( slash_ptr != NULL )
|
|
{
|
|
/* 1) x/y/z 2) x/y/z 3) slash_ptr == NULL
|
|
* ^ ^
|
|
* | |
|
|
* slash_ptr slash_ptr
|
|
*/
|
|
*slash_ptr = '/';
|
|
|
|
/* If there are multiple slashes, skip them.
|
|
* 1) x/y/z 2) x/y/z
|
|
* ^ ^
|
|
* | |
|
|
* slash_ptr slash_ptr
|
|
*/
|
|
while (*slash_ptr == '/')
|
|
{
|
|
++slash_ptr;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
BOOL
|
|
util_path_is_absolute(
|
|
STRING dir
|
|
)
|
|
{
|
|
BOOL val = FALSE;
|
|
|
|
if ( dir[0] == '/' )
|
|
val = TRUE;
|
|
|
|
return val;
|
|
}
|
|
|
|
/* This routine is passed in an absolute path name (from the file
|
|
* chooser) and derives the module or project name for the ABObj
|
|
* struct. The obj name is passed back in the objname parameter.
|
|
* This routine assumes that objname already has allocated space.
|
|
*/
|
|
int
|
|
util_derive_name_from_path(
|
|
char *fullpath,
|
|
char *objname
|
|
)
|
|
{
|
|
char *filename, *name;
|
|
int len = 0;
|
|
|
|
if ( util_file_name_has_ab_extension(fullpath) )
|
|
{
|
|
/*
|
|
* Check return value of strrchr before adding 1 to it
|
|
*/
|
|
if (filename = strrchr(fullpath, '/'))
|
|
name = (STRING)strdup(filename + 1);
|
|
else
|
|
name = (STRING)strdup(fullpath);
|
|
|
|
len = strlen(name) - (AB_EXT_LENGTH + 1);
|
|
strncpy(objname, name, len);
|
|
objname[len] = '\0';
|
|
free(name);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Check return value of strrchr before adding 1 to it
|
|
*/
|
|
if (filename = strrchr(fullpath, '/'))
|
|
strcpy(objname, filename + 1);
|
|
else
|
|
strcpy(objname, fullpath);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* This routine is passed in a name (from the project or module
|
|
* name dialog). It checks the name for the ".bil" or ".bip"
|
|
* extension and strips it off if the name has it. The project
|
|
* or module name is passed back in the new_name parameter. This
|
|
* routine assumes that new_name already has allocated space.
|
|
*/
|
|
int
|
|
util_check_name(
|
|
STRING name,
|
|
STRING new_name
|
|
)
|
|
{
|
|
int len = 0;
|
|
|
|
if ( util_file_name_has_ab_extension(name) )
|
|
{
|
|
len = strlen(name) - (AB_EXT_LENGTH + 1);
|
|
strncpy(new_name, name, len);
|
|
new_name[len] = '\0';
|
|
}
|
|
else
|
|
{
|
|
strcpy(new_name, name);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
BOOL
|
|
util_file_is_regular_file(STRING filename)
|
|
{
|
|
BOOL IsRegFile = FALSE;
|
|
struct stat file_info;
|
|
|
|
if (stat(filename, &file_info) != 0)
|
|
{
|
|
goto epilogue;
|
|
}
|
|
if (S_ISREG(file_info.st_mode))
|
|
{
|
|
IsRegFile = TRUE;
|
|
}
|
|
|
|
epilogue:
|
|
return IsRegFile;
|
|
}
|
|
|
|
BOOL
|
|
util_file_is_directory(STRING filename)
|
|
{
|
|
BOOL IsDir = FALSE;
|
|
struct stat file_info;
|
|
|
|
if (stat(filename, &file_info) != 0)
|
|
{
|
|
goto epilogue;
|
|
}
|
|
if (S_ISDIR(file_info.st_mode))
|
|
{
|
|
IsDir = TRUE;
|
|
}
|
|
|
|
epilogue:
|
|
return IsDir;
|
|
}
|
|
|
|
|
|
/*
|
|
* from fopen() man page: legal types are:
|
|
*
|
|
* r, rb, w, wb, a, ab,
|
|
* r+, r+b, rb+, w+, w+b, wb+, a+, a+b, ab+
|
|
*
|
|
* The 'b' option is ignored.
|
|
*
|
|
*/
|
|
FILE *
|
|
util_fopen_locked(const char *filename, const char *accessType)
|
|
{
|
|
FILE *file = NULL;
|
|
char char1 = accessType[0];
|
|
BOOL charPlus =
|
|
( (accessType[1] != 0)
|
|
&& ((accessType[1] == '+') || (accessType[2] == '+')));
|
|
BOOL truncateFile = FALSE;
|
|
int lockType = -1;
|
|
|
|
/*
|
|
* Open the file
|
|
* If a truncated open, open the existing file, first. That way, we
|
|
* can get a write lock before actually doing the truncate.
|
|
*/
|
|
switch (accessType[0])
|
|
{
|
|
case 'r':
|
|
file = fopen(filename, accessType);
|
|
if (charPlus)
|
|
{
|
|
lockType = F_WRLCK;
|
|
}
|
|
else
|
|
{
|
|
lockType = F_RDLCK;
|
|
}
|
|
break;
|
|
|
|
case 'w':
|
|
errno = 0;
|
|
file = fopen(filename, "r+"); /* use existing, first! */
|
|
if ((file == NULL) && (errno == ENOENT))
|
|
{
|
|
file = fopen(filename, accessType);
|
|
}
|
|
lockType = F_WRLCK;
|
|
truncateFile = TRUE;
|
|
break;
|
|
|
|
case 'a':
|
|
file = fopen(filename, accessType);
|
|
lockType = F_WRLCK;
|
|
break;
|
|
|
|
default:
|
|
errno = 0; /* file is NULL */
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Get the appropriate lock on the file.
|
|
* Truncate the file, if necessary.
|
|
*/
|
|
if ((file != NULL) && (lockType != -1))
|
|
{
|
|
if ( (util_flock(file, TRUE, lockType, 0, 0) >= 0)
|
|
&& truncateFile)
|
|
{
|
|
util_ftruncate(file, 0, accessType);
|
|
}
|
|
}
|
|
|
|
return file;
|
|
}
|
|
|
|
|
|
int
|
|
util_flock(FILE *file, BOOL wait, int lockType, off_t offset, off_t length)
|
|
{
|
|
struct flock lock;
|
|
int fcntlParam = (wait? F_SETLKW:F_SETLK);
|
|
|
|
lock.l_type = lockType;
|
|
lock.l_whence = SEEK_SET;
|
|
lock.l_start = offset;
|
|
lock.l_len = length;
|
|
lock.l_pid = (pid_t)-1;
|
|
|
|
#ifdef DEBUG
|
|
if (debugging() && wait)
|
|
{
|
|
if (fcntl(fileno(file), F_SETLK, (void*)&lock) == 0)
|
|
{
|
|
/* got the lock */
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
/* didn't get the lock - we're going to block */
|
|
util_dprintf(1, "Waiting for lock [%s]...\n",
|
|
(fcntlParam == F_RDLCK? "READ":
|
|
(fcntlParam == F_WRLCK? "WRITE":
|
|
"BAD TYPE"))
|
|
);
|
|
}
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
if (fcntl(fileno(file), fcntlParam, (void*)&lock) != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
util_funlock(FILE *file, off_t offset, off_t length)
|
|
{
|
|
struct flock lock;
|
|
|
|
lock.l_type = F_UNLCK;
|
|
lock.l_whence = SEEK_SET;
|
|
lock.l_start = offset;
|
|
lock.l_len = length;
|
|
lock.l_pid = (pid_t)-1;
|
|
|
|
if (fcntl(fileno(file), F_SETLKW, (void*)&lock) != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
util_ftruncate(FILE *file, off_t length, const char *accessType)
|
|
{
|
|
int fd = fileno(file);
|
|
off_t offset = ftell(file);
|
|
|
|
util_fdtruncate(fd, length);
|
|
|
|
/*
|
|
* Associate the stream with the file descriptor fildes.
|
|
*/
|
|
fdopen(fd, accessType);
|
|
|
|
/*
|
|
* Perform a seek on the stream, just to make sure it's in sync.
|
|
*/
|
|
if (offset > length)
|
|
{
|
|
fseek(file, 0, SEEK_END);
|
|
}
|
|
else
|
|
{
|
|
fseek(file, offset, SEEK_SET);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|