/* * 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 #include #include #include #include #include #include #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; }