summaryrefslogtreecommitdiff
path: root/routines.c
diff options
context:
space:
mode:
authorPatrick McCarty <patrick.mccarty@linux.intel.com>2013-04-12 10:40:25 -0700
committerPatrick McCarty <patrick.mccarty@linux.intel.com>2013-04-12 10:40:25 -0700
commit793eb3eb123664017100dfb1a5407f0da639a339 (patch)
tree2a6511443d115418007151f95f3a0fa8f5300974 /routines.c
downloadctags-793eb3eb123664017100dfb1a5407f0da639a339.tar.gz
ctags-793eb3eb123664017100dfb1a5407f0da639a339.tar.bz2
ctags-793eb3eb123664017100dfb1a5407f0da639a339.zip
Imported Upstream version 5.8upstream/5.8upstream
Diffstat (limited to 'routines.c')
-rw-r--r--routines.c891
1 files changed, 891 insertions, 0 deletions
diff --git a/routines.c b/routines.c
new file mode 100644
index 0000000..83bcdcc
--- /dev/null
+++ b/routines.c
@@ -0,0 +1,891 @@
+/*
+* $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 FILES
+*/
+#include "general.h" /* must always come first */
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h> /* to declare malloc (), realloc () */
+#endif
+#include <ctype.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdio.h> /* to declare tempnam(), and SEEK_SET (hopefully) */
+
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h> /* to declar O_RDWR, O_CREAT, O_EXCL */
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h> /* to declare mkstemp () */
+#endif
+
+/* To declare "struct stat" and stat ().
+ */
+#if defined (HAVE_SYS_TYPES_H)
+# include <sys/types.h>
+#else
+# if defined (HAVE_TYPES_H)
+# include <types.h>
+# endif
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#else
+# ifdef HAVE_STAT_H
+# include <stat.h>
+# endif
+#endif
+
+#ifdef HAVE_DOS_H
+# include <dos.h> /* to declare MAXPATH */
+#endif
+#ifdef HAVE_DIRECT_H
+# include <direct.h> /* to _getcwd */
+#endif
+#ifdef HAVE_DIR_H
+# include <dir.h> /* to declare findfirst() and findnext() */
+#endif
+#ifdef HAVE_IO_H
+# include <io.h> /* to declare open() */
+#endif
+#include "debug.h"
+#include "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
+ strcpy (cp, slashp + 3);
+ slashp = cp;
+ continue;
+ }
+ else if (slashp [2] == PATH_SEPARATOR || slashp [2] == '\0')
+ {
+ strcpy (slashp, slashp + 2);
+ 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: */