757 lines
18 KiB
C
757 lines
18 KiB
C
/*
|
|
* $Id: routines.c 536 2007-06-02 06:09:00Z elliotth $
|
|
*
|
|
* Copyright (c) 2002-2003, Darren Hiebert
|
|
*
|
|
* This source code is released for free distribution under the terms of the
|
|
* GNU General Public License.
|
|
*
|
|
* This module contains a lose assortment of shared functions.
|
|
*/
|
|
#include "third_party/ctags/general.h"
|
|
/* must always come first */
|
|
#include "libc/calls/struct/stat.h"
|
|
#include "libc/errno.h"
|
|
#include "libc/fmt/fmt.h"
|
|
#include "libc/log/log.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/stdio/temp.h"
|
|
#include "third_party/ctags/debug.h"
|
|
#include "third_party/ctags/routines.h"
|
|
|
|
/*
|
|
* MACROS
|
|
*/
|
|
#ifndef TMPDIR
|
|
#define TMPDIR "/tmp"
|
|
#endif
|
|
|
|
/* File type tests.
|
|
*/
|
|
#ifndef S_ISREG
|
|
#if defined(S_IFREG) && !defined(AMIGA)
|
|
#define S_ISREG(mode) ((mode)&S_IFREG)
|
|
#else
|
|
#define S_ISREG(mode) TRUE /* assume regular file */
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef S_ISLNK
|
|
#ifdef S_IFLNK
|
|
#define S_ISLNK(mode) (((mode)&S_IFMT) == S_IFLNK)
|
|
#else
|
|
#define S_ISLNK(mode) FALSE /* assume no soft links */
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef S_ISDIR
|
|
#ifdef S_IFDIR
|
|
#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR)
|
|
#else
|
|
#define S_ISDIR(mode) FALSE /* assume no soft links */
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef S_IFMT
|
|
#define S_IFMT 0
|
|
#endif
|
|
|
|
#ifndef S_IXUSR
|
|
#define S_IXUSR 0
|
|
#endif
|
|
#ifndef S_IXGRP
|
|
#define S_IXGRP 0
|
|
#endif
|
|
#ifndef S_IXOTH
|
|
#define S_IXOTH 0
|
|
#endif
|
|
|
|
#ifndef S_IRUSR
|
|
#define S_IRUSR 0400
|
|
#endif
|
|
#ifndef S_IWUSR
|
|
#define S_IWUSR 0200
|
|
#endif
|
|
|
|
#ifndef S_ISUID
|
|
#define S_ISUID 0
|
|
#endif
|
|
|
|
/* Hack for rediculous practice of Microsoft Visual C++.
|
|
*/
|
|
#if defined(WIN32)
|
|
#if defined(_MSC_VER)
|
|
#define stat _stat
|
|
#define getcwd _getcwd
|
|
#define currentdrive() (_getdrive() + 'A' - 1)
|
|
#define PATH_MAX _MAX_PATH
|
|
#elif defined(__BORLANDC__)
|
|
#define PATH_MAX MAXPATH
|
|
#define currentdrive() (getdisk() + 'A')
|
|
#elif defined(DJGPP)
|
|
#define currentdrive() (getdisk() + 'A')
|
|
#else
|
|
#define currentdrive() 'C'
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef PATH_MAX
|
|
#define PATH_MAX 256
|
|
#endif
|
|
|
|
/*
|
|
* Miscellaneous macros
|
|
*/
|
|
#define selected(var, feature) (((int)(var) & (int)(feature)) == (int)feature)
|
|
|
|
/*
|
|
* DATA DEFINITIONS
|
|
*/
|
|
#if defined(MSDOS_STYLE_PATH)
|
|
const char *const PathDelimiters = ":/\\";
|
|
#elif defined(VMS)
|
|
const char *const PathDelimiters = ":]>";
|
|
#endif
|
|
|
|
char *CurrentDirectory;
|
|
|
|
static const char *ExecutableProgram;
|
|
static const char *ExecutableName;
|
|
|
|
/*
|
|
* FUNCTION PROTOTYPES
|
|
*/
|
|
#ifdef NEED_PROTO_STAT
|
|
extern int stat(const char *, struct stat *);
|
|
#endif
|
|
#ifdef NEED_PROTO_LSTAT
|
|
extern int lstat(const char *, struct stat *);
|
|
#endif
|
|
#if defined(MSDOS) || defined(WIN32) || defined(VMS) || defined(__EMX__) || \
|
|
defined(AMIGA)
|
|
#define lstat(fn, buf) stat(fn, buf)
|
|
#endif
|
|
|
|
/*
|
|
* FUNCTION DEFINITIONS
|
|
*/
|
|
|
|
extern void freeRoutineResources(void) {
|
|
if (CurrentDirectory != NULL) eFree(CurrentDirectory);
|
|
}
|
|
|
|
extern void setExecutableName(const char *const path) {
|
|
ExecutableProgram = path;
|
|
ExecutableName = baseFilename(path);
|
|
#ifdef VAXC
|
|
{
|
|
/* remove filetype from executable name */
|
|
char *p = strrchr(ExecutableName, '.');
|
|
if (p != NULL) *p = '\0';
|
|
}
|
|
#endif
|
|
}
|
|
|
|
extern const char *getExecutableName(void) {
|
|
return ExecutableName;
|
|
}
|
|
|
|
extern const char *getExecutablePath(void) {
|
|
return ExecutableProgram;
|
|
}
|
|
|
|
extern void error(const errorSelection selection, const char *const format,
|
|
...) {
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
fprintf(errout, "%s: %s", getExecutableName(),
|
|
selected(selection, WARNING) ? "Warning: " : "");
|
|
vfprintf(errout, format, ap);
|
|
if (selected(selection, PERROR))
|
|
#ifdef HAVE_STRERROR
|
|
fprintf(errout, " : %s", strerror(errno));
|
|
#else
|
|
perror(" ");
|
|
#endif
|
|
fputs("\n", errout);
|
|
va_end(ap);
|
|
if (selected(selection, FATAL)) exit(1);
|
|
}
|
|
|
|
/*
|
|
* Memory allocation functions
|
|
*/
|
|
|
|
extern void *eMalloc(const size_t size) {
|
|
void *buffer = malloc(size);
|
|
|
|
if (buffer == NULL) error(FATAL, "out of memory");
|
|
|
|
return buffer;
|
|
}
|
|
|
|
extern void *eCalloc(const size_t count, const size_t size) {
|
|
void *buffer = calloc(count, size);
|
|
|
|
if (buffer == NULL) error(FATAL, "out of memory");
|
|
|
|
return buffer;
|
|
}
|
|
|
|
extern void *eRealloc(void *const ptr, const size_t size) {
|
|
void *buffer;
|
|
if (ptr == NULL)
|
|
buffer = eMalloc(size);
|
|
else {
|
|
buffer = realloc(ptr, size);
|
|
if (buffer == NULL) error(FATAL, "out of memory");
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
extern void eFree(void *const ptr) {
|
|
Assert(ptr != NULL);
|
|
free(ptr);
|
|
}
|
|
|
|
/*
|
|
* String manipulation functions
|
|
*/
|
|
|
|
/*
|
|
* Compare two strings, ignoring case.
|
|
* Return 0 for match, < 0 for smaller, > 0 for bigger
|
|
* Make sure case is folded to uppercase in comparison (like for 'sort -f')
|
|
* This makes a difference when one of the chars lies between upper and lower
|
|
* ie. one of the chars [ \ ] ^ _ ` for ascii. (The '_' in particular !)
|
|
*/
|
|
extern int struppercmp(const char *s1, const char *s2) {
|
|
int result;
|
|
do {
|
|
result = toupper((int)*s1) - toupper((int)*s2);
|
|
} while (result == 0 && *s1++ != '\0' && *s2++ != '\0');
|
|
return result;
|
|
}
|
|
|
|
extern int strnuppercmp(const char *s1, const char *s2, size_t n) {
|
|
int result;
|
|
do {
|
|
result = toupper((int)*s1) - toupper((int)*s2);
|
|
} while (result == 0 && --n > 0 && *s1++ != '\0' && *s2++ != '\0');
|
|
return result;
|
|
}
|
|
|
|
#ifndef HAVE_STRSTR
|
|
extern char *strstr(const char *str, const char *substr) {
|
|
const size_t length = strlen(substr);
|
|
const char *match = NULL;
|
|
const char *p;
|
|
|
|
for (p = str; *p != '\0' && match == NULL; ++p)
|
|
if (strncmp(p, substr, length) == 0) match = p;
|
|
return (char *)match;
|
|
}
|
|
#endif
|
|
|
|
extern char *eStrdup(const char *str) {
|
|
char *result = xMalloc(strlen(str) + 1, char);
|
|
strcpy(result, str);
|
|
return result;
|
|
}
|
|
|
|
extern void toLowerString(char *str) {
|
|
while (*str != '\0') {
|
|
*str = tolower((int)*str);
|
|
++str;
|
|
}
|
|
}
|
|
|
|
extern void toUpperString(char *str) {
|
|
while (*str != '\0') {
|
|
*str = toupper((int)*str);
|
|
++str;
|
|
}
|
|
}
|
|
|
|
/* Newly allocated string containing lower case conversion of a string.
|
|
*/
|
|
extern char *newLowerString(const char *str) {
|
|
char *const result = xMalloc(strlen(str) + 1, char);
|
|
int i = 0;
|
|
do
|
|
result[i] = tolower((int)str[i]);
|
|
while (str[i++] != '\0');
|
|
return result;
|
|
}
|
|
|
|
/* Newly allocated string containing upper case conversion of a string.
|
|
*/
|
|
extern char *newUpperString(const char *str) {
|
|
char *const result = xMalloc(strlen(str) + 1, char);
|
|
int i = 0;
|
|
do
|
|
result[i] = toupper((int)str[i]);
|
|
while (str[i++] != '\0');
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* File system functions
|
|
*/
|
|
|
|
extern void setCurrentDirectory(void) {
|
|
#ifndef AMIGA
|
|
char *buf;
|
|
#endif
|
|
if (CurrentDirectory == NULL)
|
|
CurrentDirectory = xMalloc((size_t)(PATH_MAX + 1), char);
|
|
#ifdef AMIGA
|
|
strcpy(CurrentDirectory, ".");
|
|
#else
|
|
buf = getcwd(CurrentDirectory, PATH_MAX);
|
|
if (buf == NULL) perror("");
|
|
#endif
|
|
if (CurrentDirectory[strlen(CurrentDirectory) - (size_t)1] !=
|
|
PATH_SEPARATOR) {
|
|
sprintf(CurrentDirectory + strlen(CurrentDirectory), "%c",
|
|
OUTPUT_PATH_SEPARATOR);
|
|
}
|
|
}
|
|
|
|
#ifdef AMIGA
|
|
static boolean isAmigaDirectory(const char *const name) {
|
|
boolean result = FALSE;
|
|
struct FileInfoBlock *const fib = xMalloc(1, struct FileInfoBlock);
|
|
if (fib != NULL) {
|
|
const BPTR flock = Lock((UBYTE *)name, (long)ACCESS_READ);
|
|
|
|
if (flock != (BPTR)NULL) {
|
|
if (Examine(flock, fib))
|
|
result = ((fib->fib_DirEntryType >= 0) ? TRUE : FALSE);
|
|
UnLock(flock);
|
|
}
|
|
eFree(fib);
|
|
}
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
/* For caching of stat() calls */
|
|
extern fileStatus *eStat(const char *const fileName) {
|
|
struct stat status;
|
|
static fileStatus file;
|
|
if (file.name == NULL || strcmp(fileName, file.name) != 0) {
|
|
eStatFree(&file);
|
|
file.name = eStrdup(fileName);
|
|
if (lstat(file.name, &status) != 0)
|
|
file.exists = FALSE;
|
|
else {
|
|
file.isSymbolicLink = (boolean)S_ISLNK(status.st_mode);
|
|
if (file.isSymbolicLink && stat(file.name, &status) != 0)
|
|
file.exists = FALSE;
|
|
else {
|
|
file.exists = TRUE;
|
|
#ifdef AMIGA
|
|
file.isDirectory = isAmigaDirectory(file.name);
|
|
#else
|
|
file.isDirectory = (boolean)S_ISDIR(status.st_mode);
|
|
#endif
|
|
file.isNormalFile = (boolean)(S_ISREG(status.st_mode));
|
|
file.isExecutable =
|
|
(boolean)((status.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
|
|
file.isSetuid = (boolean)((status.st_mode & S_ISUID) != 0);
|
|
file.size = status.st_size;
|
|
}
|
|
}
|
|
}
|
|
return &file;
|
|
}
|
|
|
|
extern void eStatFree(fileStatus *status) {
|
|
if (status->name != NULL) {
|
|
eFree(status->name);
|
|
status->name = NULL;
|
|
}
|
|
}
|
|
|
|
extern boolean doesFileExist(const char *const fileName) {
|
|
fileStatus *status = eStat(fileName);
|
|
return status->exists;
|
|
}
|
|
|
|
extern boolean isRecursiveLink(const char *const dirName) {
|
|
boolean result = FALSE;
|
|
fileStatus *status = eStat(dirName);
|
|
if (status->isSymbolicLink) {
|
|
char *const path = absoluteFilename(dirName);
|
|
while (path[strlen(path) - 1] == PATH_SEPARATOR)
|
|
path[strlen(path) - 1] = '\0';
|
|
while (!result && strlen(path) > (size_t)1) {
|
|
char *const separator = strrchr(path, PATH_SEPARATOR);
|
|
if (separator == NULL)
|
|
break;
|
|
else if (separator == path) /* backed up to root directory */
|
|
*(separator + 1) = '\0';
|
|
else
|
|
*separator = '\0';
|
|
result = isSameFile(path, dirName);
|
|
}
|
|
eFree(path);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#ifndef HAVE_FGETPOS
|
|
|
|
extern int fgetpos(FILE *stream, fpos_t *pos) {
|
|
int result = 0;
|
|
|
|
*pos = ftell(stream);
|
|
if (*pos == -1L) result = -1;
|
|
|
|
return result;
|
|
}
|
|
|
|
extern int fsetpos(FILE *stream, fpos_t const *pos) {
|
|
return fseek(stream, *pos, SEEK_SET);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Pathname manipulation (O/S dependent!!!)
|
|
*/
|
|
|
|
static boolean isPathSeparator(const int c) {
|
|
boolean result;
|
|
#if defined(MSDOS_STYLE_PATH) || defined(VMS)
|
|
result = (boolean)(strchr(PathDelimiters, c) != NULL);
|
|
#else
|
|
result = (boolean)(c == PATH_SEPARATOR);
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
#if !defined(HAVE_STAT_ST_INO)
|
|
|
|
static void canonicalizePath(char *const path __unused__) {
|
|
#if defined(MSDOS_STYLE_PATH)
|
|
char *p;
|
|
for (p = path; *p != '\0'; ++p)
|
|
if (isPathSeparator(*p) && *p != ':') *p = PATH_SEPARATOR;
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
extern boolean isSameFile(const char *const name1, const char *const name2) {
|
|
boolean result = FALSE;
|
|
#if defined(HAVE_STAT_ST_INO)
|
|
struct stat stat1, stat2;
|
|
|
|
if (stat(name1, &stat1) == 0 && stat(name2, &stat2) == 0)
|
|
result = (boolean)(stat1.st_ino == stat2.st_ino);
|
|
#else
|
|
{
|
|
char *const n1 = absoluteFilename(name1);
|
|
char *const n2 = absoluteFilename(name2);
|
|
canonicalizePath(n1);
|
|
canonicalizePath(n2);
|
|
#if defined(CASE_INSENSITIVE_FILENAMES)
|
|
result = (boolean)(strcasecmp(n1, n2) == 0);
|
|
#else
|
|
result = (boolean)(strcmp(n1, n2) == 0);
|
|
#endif
|
|
free(n1);
|
|
free(n2);
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
extern const char *baseFilename(const char *const filePath) {
|
|
#if defined(MSDOS_STYLE_PATH) || defined(VMS)
|
|
const char *tail = NULL;
|
|
unsigned int i;
|
|
|
|
/* Find whichever of the path delimiters is last.
|
|
*/
|
|
for (i = 0; i < strlen(PathDelimiters); ++i) {
|
|
const char *sep = strrchr(filePath, PathDelimiters[i]);
|
|
|
|
if (sep > tail) tail = sep;
|
|
}
|
|
#else
|
|
const char *tail = strrchr(filePath, PATH_SEPARATOR);
|
|
#endif
|
|
if (tail == NULL)
|
|
tail = filePath;
|
|
else
|
|
++tail; /* step past last delimiter */
|
|
#ifdef VAXC
|
|
{
|
|
/* remove version number from filename */
|
|
char *p = strrchr((char *)tail, ';');
|
|
if (p != NULL) *p = '\0';
|
|
}
|
|
#endif
|
|
|
|
return tail;
|
|
}
|
|
|
|
extern const char *fileExtension(const char *const fileName) {
|
|
const char *extension;
|
|
const char *pDelimiter = NULL;
|
|
const char *const base = baseFilename(fileName);
|
|
#ifdef QDOS
|
|
pDelimiter = strrchr(base, '_');
|
|
#endif
|
|
if (pDelimiter == NULL) pDelimiter = strrchr(base, '.');
|
|
|
|
if (pDelimiter == NULL)
|
|
extension = "";
|
|
else
|
|
extension = pDelimiter + 1; /* skip to first char of extension */
|
|
|
|
return extension;
|
|
}
|
|
|
|
extern boolean isAbsolutePath(const char *const path) {
|
|
boolean result = FALSE;
|
|
#if defined(MSDOS_STYLE_PATH)
|
|
if (isPathSeparator(path[0]))
|
|
result = TRUE;
|
|
else if (isalpha(path[0]) && path[1] == ':') {
|
|
if (isPathSeparator(path[2]))
|
|
result = TRUE;
|
|
else
|
|
/* We don't support non-absolute file names with a drive
|
|
* letter, like `d:NAME' (it's too much hassle).
|
|
*/
|
|
error(FATAL, "%s: relative file names with drive letters not supported",
|
|
path);
|
|
}
|
|
#elif defined(VMS)
|
|
result = (boolean)(strchr(path, ':') != NULL);
|
|
#else
|
|
result = isPathSeparator(path[0]);
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
extern vString *combinePathAndFile(const char *const path,
|
|
const char *const file) {
|
|
vString *const filePath = vStringNew();
|
|
#ifdef VMS
|
|
const char *const directoryId = strstr(file, ".DIR;1");
|
|
|
|
if (directoryId == NULL) {
|
|
const char *const versionId = strchr(file, ';');
|
|
|
|
vStringCopyS(filePath, path);
|
|
if (versionId == NULL)
|
|
vStringCatS(filePath, file);
|
|
else
|
|
vStringNCatS(filePath, file, versionId - file);
|
|
vStringCopyToLower(filePath, filePath);
|
|
} else {
|
|
/* File really is a directory; append it to the path.
|
|
* Gotcha: doesn't work with logical names.
|
|
*/
|
|
vStringNCopyS(filePath, path, strlen(path) - 1);
|
|
vStringPut(filePath, '.');
|
|
vStringNCatS(filePath, file, directoryId - file);
|
|
if (strchr(path, '[') != NULL)
|
|
vStringPut(filePath, ']');
|
|
else
|
|
vStringPut(filePath, '>');
|
|
vStringTerminate(filePath);
|
|
}
|
|
#else
|
|
const int lastChar = path[strlen(path) - 1];
|
|
boolean terminated = isPathSeparator(lastChar);
|
|
|
|
vStringCopyS(filePath, path);
|
|
if (!terminated) {
|
|
vStringPut(filePath, OUTPUT_PATH_SEPARATOR);
|
|
vStringTerminate(filePath);
|
|
}
|
|
vStringCatS(filePath, file);
|
|
#endif
|
|
|
|
return filePath;
|
|
}
|
|
|
|
/* Return a newly-allocated string whose contents concatenate those of
|
|
* s1, s2, s3.
|
|
* Routine adapted from Gnu etags.
|
|
*/
|
|
static char *concat(const char *s1, const char *s2, const char *s3) {
|
|
int len1 = strlen(s1), len2 = strlen(s2), len3 = strlen(s3);
|
|
char *result = xMalloc(len1 + len2 + len3 + 1, char);
|
|
|
|
strcpy(result, s1);
|
|
strcpy(result + len1, s2);
|
|
strcpy(result + len1 + len2, s3);
|
|
result[len1 + len2 + len3] = '\0';
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Return a newly allocated string containing the absolute file name of FILE
|
|
* given CWD (which should end with a slash).
|
|
* Routine adapted from Gnu etags.
|
|
*/
|
|
extern char *absoluteFilename(const char *file) {
|
|
char *slashp, *cp;
|
|
char *res = NULL;
|
|
if (isAbsolutePath(file)) {
|
|
#ifdef MSDOS_STYLE_PATH
|
|
if (file[1] == ':')
|
|
res = eStrdup(file);
|
|
else {
|
|
char drive[3];
|
|
sprintf(drive, "%c:", currentdrive());
|
|
res = concat(drive, file, "");
|
|
}
|
|
#else
|
|
res = eStrdup(file);
|
|
#endif
|
|
} else
|
|
res = concat(CurrentDirectory, file, "");
|
|
|
|
/* Delete the "/dirname/.." and "/." substrings. */
|
|
slashp = strchr(res, PATH_SEPARATOR);
|
|
while (slashp != NULL && slashp[0] != '\0') {
|
|
if (slashp[1] == '.') {
|
|
if (slashp[2] == '.' &&
|
|
(slashp[3] == PATH_SEPARATOR || slashp[3] == '\0')) {
|
|
cp = slashp;
|
|
do
|
|
cp--;
|
|
while (cp >= res && !isAbsolutePath(cp));
|
|
if (cp < res) cp = slashp; /* the absolute name begins with "/.." */
|
|
#ifdef MSDOS_STYLE_PATH
|
|
/* Under MSDOS and NT we get `d:/NAME' as absolute file name,
|
|
* so the luser could say `d:/../NAME'. We silently treat this
|
|
* as `d:/NAME'.
|
|
*/
|
|
else if (cp[0] != PATH_SEPARATOR)
|
|
cp = slashp;
|
|
#endif
|
|
memmove(cp, slashp + 3, strlen(slashp + 3) + 1);
|
|
slashp = cp;
|
|
continue;
|
|
} else if (slashp[2] == PATH_SEPARATOR || slashp[2] == '\0') {
|
|
memmove(slashp, slashp + 2, strlen(slashp + 2) + 1);
|
|
continue;
|
|
}
|
|
}
|
|
slashp = strchr(slashp + 1, PATH_SEPARATOR);
|
|
}
|
|
|
|
if (res[0] == '\0')
|
|
return eStrdup("/");
|
|
else {
|
|
#ifdef MSDOS_STYLE_PATH
|
|
/* Canonicalize drive letter case. */
|
|
if (res[1] == ':' && islower(res[0])) res[0] = toupper(res[0]);
|
|
#endif
|
|
|
|
return res;
|
|
}
|
|
}
|
|
|
|
/* Return a newly allocated string containing the absolute file name of dir
|
|
* where `file' resides given `CurrentDirectory'.
|
|
* Routine adapted from Gnu etags.
|
|
*/
|
|
extern char *absoluteDirname(char *file) {
|
|
char *slashp, *res;
|
|
char save;
|
|
slashp = strrchr(file, PATH_SEPARATOR);
|
|
if (slashp == NULL)
|
|
res = eStrdup(CurrentDirectory);
|
|
else {
|
|
save = slashp[1];
|
|
slashp[1] = '\0';
|
|
res = absoluteFilename(file);
|
|
slashp[1] = save;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* Return a newly allocated string containing the file name of FILE relative
|
|
* to the absolute directory DIR (which should end with a slash).
|
|
* Routine adapted from Gnu etags.
|
|
*/
|
|
extern char *relativeFilename(const char *file, const char *dir) {
|
|
const char *fp, *dp;
|
|
char *absdir, *res;
|
|
int i;
|
|
|
|
/* Find the common root of file and dir (with a trailing slash). */
|
|
absdir = absoluteFilename(file);
|
|
fp = absdir;
|
|
dp = dir;
|
|
while (*fp++ == *dp++) continue;
|
|
fp--;
|
|
dp--; /* back to the first differing char */
|
|
do { /* look at the equal chars until path sep */
|
|
if (fp == absdir) return absdir; /* first char differs, give up */
|
|
fp--;
|
|
dp--;
|
|
} while (*fp != PATH_SEPARATOR);
|
|
|
|
/* Build a sequence of "../" strings for the resulting relative file name.
|
|
*/
|
|
i = 0;
|
|
while ((dp = strchr(dp + 1, PATH_SEPARATOR)) != NULL) i += 1;
|
|
res = xMalloc(3 * i + strlen(fp + 1) + 1, char);
|
|
res[0] = '\0';
|
|
while (i-- > 0) strcat(res, "../");
|
|
|
|
/* Add the file name relative to the common root of file and dir. */
|
|
strcat(res, fp + 1);
|
|
free(absdir);
|
|
|
|
return res;
|
|
}
|
|
|
|
extern FILE *tempFile(const char *const mode, char **const pName) {
|
|
char *name;
|
|
FILE *fp;
|
|
int fd;
|
|
#if defined(HAVE_MKSTEMP)
|
|
const char *const pattern = "tags.XXXXXX";
|
|
const char *tmpdir = NULL;
|
|
fileStatus *file = eStat(ExecutableProgram);
|
|
if (!file->isSetuid) tmpdir = getenv("TMPDIR");
|
|
if (tmpdir == NULL) tmpdir = TMPDIR;
|
|
name = xMalloc(strlen(tmpdir) + 1 + strlen(pattern) + 1, char);
|
|
sprintf(name, "%s%c%s", tmpdir, OUTPUT_PATH_SEPARATOR, pattern);
|
|
fd = mkstemp(name);
|
|
eStatFree(file);
|
|
#elif defined(HAVE_TEMPNAM)
|
|
name = tempnam(TMPDIR, "tags");
|
|
if (name == NULL)
|
|
error(FATAL | PERROR, "cannot allocate temporary file name");
|
|
fd = open(name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
|
|
#else
|
|
name = xMalloc(L_tmpnam, char);
|
|
if (tmpnam(name) != name)
|
|
error(FATAL | PERROR, "cannot assign temporary file name");
|
|
fd = open(name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
|
|
#endif
|
|
if (fd == -1) error(FATAL | PERROR, "cannot open temporary file");
|
|
fp = fdopen(fd, mode);
|
|
if (fp == NULL) error(FATAL | PERROR, "cannot open temporary file");
|
|
DebugStatement(debugPrintf(DEBUG_STATUS, "opened temporary file %s\n", name);)
|
|
Assert(*pName == NULL);
|
|
*pName = name;
|
|
return fp;
|
|
}
|
|
|
|
/* vi:set tabstop=4 shiftwidth=4: */
|