Files
2021-10-08 17:25:17 +09:00

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;
}