1 /*
2 * Copyright (c) 2002-2003, Darren Hiebert
3 *
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
6 *
7 * This module contains a lose assortment of shared functions.
8 */
9
10 /*
11 * INCLUDE FILES
12 */
13 #include "general.h" /* must always come first */
14
15 #ifdef HAVE_STDLIB_H
16 # include <stdlib.h> /* to declare malloc (), realloc () */
17 #endif
18 #include <ctype.h>
19 #include <string.h>
20 #include <stdio.h> /* to declare tempnam(), and SEEK_SET (hopefully) */
21
22 #ifdef HAVE_FCNTL_H
23 # include <fcntl.h> /* to declare O_RDWR, O_CREAT, O_EXCL */
24 #endif
25 #ifdef HAVE_UNISTD_H
26 # include <unistd.h> /* to declare mkstemp () */
27 #endif
28
29 #ifdef HAVE_LIMITS_H
30 # include <limits.h> /* to declare MB_LEN_MAX */
31 #endif
32 #ifndef MB_LEN_MAX
33 # define MB_LEN_MAX 6
34 #endif
35
36 /* To declare "struct stat" and stat ().
37 */
38 #if defined (HAVE_SYS_TYPES_H)
39 # include <sys/types.h>
40 #else
41 # if defined (HAVE_TYPES_H)
42 # include <types.h>
43 # endif
44 #endif
45 #ifdef HAVE_SYS_STAT_H
46 # include <sys/stat.h>
47 #else
48 # ifdef HAVE_STAT_H
49 # include <stat.h>
50 # endif
51 #endif
52
53 #ifdef HAVE_DIRECT_H
54 # include <direct.h> /* to _getcwd */
55 #endif
56 #ifdef HAVE_DIR_H
57 # include <dir.h> /* to declare findfirst() and findnext() */
58 #endif
59 #ifdef HAVE_IO_H
60 # include <io.h> /* to declare open() */
61 #endif
62 #include "debug.h"
63 #include "routines.h"
64 #ifdef HAVE_ICONV
65 # include "mbcs.h"
66 #endif
67 #ifdef HAVE_ERRNO_H
68 # include <errno.h>
69 #endif
70
71 #include "options.h"
72
73 /*
74 * MACROS
75 */
76 #ifndef TMPDIR
77 # define TMPDIR "/tmp"
78 #endif
79
80 /* File type tests.
81 */
82 #ifndef S_ISREG
83 # if defined (S_IFREG)
84 # define S_ISREG(mode) ((mode) & S_IFREG)
85 # endif
86 #endif
87
88 #ifndef S_ISLNK
89 # ifdef S_IFLNK
90 # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
91 # else
92 # define S_ISLNK(mode) false /* assume no soft links */
93 # endif
94 #endif
95
96 #ifndef S_ISDIR
97 # ifdef S_IFDIR
98 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
99 # else
100 # define S_ISDIR(mode) false /* assume no soft links */
101 # endif
102 #endif
103
104 #ifndef S_IFMT
105 # define S_IFMT 0
106 #endif
107
108 #ifndef S_IXUSR
109 # define S_IXUSR 0
110 #endif
111 #ifndef S_IXGRP
112 # define S_IXGRP 0
113 #endif
114 #ifndef S_IXOTH
115 # define S_IXOTH 0
116 #endif
117
118 #ifndef S_IRUSR
119 # define S_IRUSR 0400
120 #endif
121 #ifndef S_IWUSR
122 # define S_IWUSR 0200
123 #endif
124
125 #ifndef S_ISUID
126 # define S_ISUID 0
127 #endif
128
129 #ifndef S_ISGID
130 # define S_ISGID 0
131 #endif
132
133 /* Hack for ridiculous practice of Microsoft Visual C++.
134 */
135 #if defined (WIN32)
136 # if defined (_MSC_VER)
137 # define stat _stat
138 # define getcwd _getcwd
139 # define currentdrive() (_getdrive() + 'A' - 1)
140 # define PATH_MAX _MAX_PATH
141 # else
142 # define currentdrive() 'C'
143 # endif
144 #endif
145
146 #ifndef PATH_MAX
147 # define PATH_MAX 256
148 #endif
149
150 /*
151 * Miscellaneous macros
152 */
153
154
155 /*
156 * DATA DEFINITIONS
157 */
158 #if defined (MSDOS_STYLE_PATH)
159 const char *const PathDelimiters = ":/\\";
160 #endif
161
162 char *CurrentDirectory;
163
164 static const char *ExecutableProgram;
165 static const char *ExecutableName;
166
167 /*
168 * FUNCTION PROTOTYPES
169 */
170 #ifdef NEED_PROTO_STAT
171 extern int stat (const char *, struct stat *);
172 #endif
173 #ifdef NEED_PROTO_LSTAT
174 extern int lstat (const char *, struct stat *);
175 #endif
176 #if defined (WIN32)
177 # define lstat(fn,buf) stat(fn,buf)
178 #endif
179
180 /*
181 * FUNCTION DEFINITIONS
182 */
183
freeRoutineResources(void)184 extern void freeRoutineResources (void)
185 {
186 if (CurrentDirectory != NULL)
187 eFree (CurrentDirectory);
188 }
189
setExecutableName(const char * const path)190 extern void setExecutableName (const char *const path)
191 {
192 ExecutableProgram = path;
193 ExecutableName = baseFilename (path);
194 }
195
getExecutableName(void)196 extern const char *getExecutableName (void)
197 {
198 return ExecutableName;
199 }
200
getExecutablePath(void)201 extern const char *getExecutablePath (void)
202 {
203 return ExecutableProgram;
204 }
205
206 /*
207 * Memory allocation functions
208 */
209
eMalloc(const size_t size)210 extern void *eMalloc (const size_t size)
211 {
212 void *buffer = malloc (size);
213
214 if (buffer == NULL)
215 error (FATAL, "out of memory");
216
217 return buffer;
218 }
219
eCalloc(const size_t count,const size_t size)220 extern void *eCalloc (const size_t count, const size_t size)
221 {
222 void *buffer = calloc (count, size);
223
224 if (buffer == NULL)
225 error (FATAL, "out of memory");
226
227 return buffer;
228 }
229
eRealloc(void * const ptr,const size_t size)230 extern void *eRealloc (void *const ptr, const size_t size)
231 {
232 void *buffer;
233 if (ptr == NULL)
234 buffer = eMalloc (size);
235 else
236 {
237 buffer = realloc (ptr, size);
238 if (buffer == NULL)
239 error (FATAL, "out of memory");
240 }
241 return buffer;
242 }
243
eFree(void * const ptr)244 extern void eFree (void *const ptr)
245 {
246 Assert (ptr != NULL);
247 free (ptr);
248 }
249
250 /*
251 * String manipulation functions
252 */
253
254 /*
255 * Compare two strings, ignoring case.
256 * Return 0 for match, < 0 for smaller, > 0 for bigger
257 * Make sure case is folded to uppercase in comparison (like for 'sort -f')
258 * This makes a difference when one of the chars lies between upper and lower
259 * ie. one of the chars [ \ ] ^ _ ` for ascii. (The '_' in particular !)
260 */
struppercmp(const char * s1,const char * s2)261 extern int struppercmp (const char *s1, const char *s2)
262 {
263 int result;
264 do
265 {
266 result = toupper ((int) *s1) - toupper ((int) *s2);
267 } while (result == 0 && *s1++ != '\0' && *s2++ != '\0');
268 return result;
269 }
270
strnuppercmp(const char * s1,const char * s2,size_t n)271 extern int strnuppercmp (const char *s1, const char *s2, size_t n)
272 {
273 int result;
274 do
275 {
276 result = toupper ((int) *s1) - toupper ((int) *s2);
277 } while (result == 0 && --n > 0 && *s1++ != '\0' && *s2++ != '\0');
278 return result;
279 }
280
281 #ifndef HAVE_STRSTR
strstr(const char * str,const char * substr)282 extern char* strstr (const char *str, const char *substr)
283 {
284 const size_t length = strlen (substr);
285 const char *p;
286
287 for (p = str ; *p != '\0' ; ++p)
288 if (strncmp (p, substr, length) == 0)
289 return (char*) p;
290 return NULL;
291 }
292 #endif
293
strrstr(const char * str,const char * substr)294 extern char* strrstr (const char *str, const char *substr)
295 {
296 const size_t length = strlen (substr);
297 const char *p;
298
299 for (p = str + strlen(str) - length ; p >= str ; --p)
300 if (strncmp (p, substr, length) == 0)
301 return (char*) p;
302 return NULL;
303 }
304
eStrdup(const char * str)305 extern char* eStrdup (const char* str)
306 {
307 char* result = xMalloc (strlen (str) + 1, char);
308 strcpy (result, str);
309 return result;
310 }
311
eStrndup(const char * str,size_t len)312 extern char* eStrndup (const char* str, size_t len)
313 {
314 char* result = xMalloc (len + 1, char);
315 memset(result, 0, len + 1);
316 strncpy (result, str, len);
317 return result;
318 }
319
toLowerString(char * str)320 extern void toLowerString (char* str)
321 {
322 while (*str != '\0')
323 {
324 *str = tolower ((int) *str);
325 ++str;
326 }
327 }
328
toUpperString(char * str)329 extern void toUpperString (char* str)
330 {
331 while (*str != '\0')
332 {
333 *str = toupper ((int) *str);
334 ++str;
335 }
336 }
337
338 /* Newly allocated string containing lower case conversion of a string.
339 */
newLowerString(const char * str)340 extern char* newLowerString (const char* str)
341 {
342 char* const result = xMalloc (strlen (str) + 1, char);
343 int i = 0;
344 do
345 result [i] = tolower ((int) str [i]);
346 while (str [i++] != '\0');
347 return result;
348 }
349
350 /* Newly allocated string containing upper case conversion of a string.
351 */
newUpperString(const char * str)352 extern char* newUpperString (const char* str)
353 {
354 char* const result = xMalloc (strlen (str) + 1, char);
355 int i = 0;
356 do
357 result [i] = toupper ((int) str [i]);
358 while (str [i++] != '\0');
359 return result;
360 }
361
362 /* Safe wrapper for strtoul
363 *
364 * The conversion result is placed in value and must only be used if the
365 * function returned true.
366 */
strToULong(const char * const str,int base,unsigned long * value)367 extern bool strToULong(const char *const str, int base, unsigned long *value)
368 {
369 char *endptr;
370
371 errno = 0;
372 *value = strtoul (str, &endptr, base);
373 return *endptr == '\0' && str != endptr && errno == 0;
374 }
375
376 /* Safe wrapper for strtol/atol
377 *
378 * The conversion result is placed in value and must only be used if the
379 * function returned true.
380 */
strToLong(const char * const str,int base,long * value)381 extern bool strToLong(const char *const str, int base, long *value)
382 {
383 char *endptr;
384
385 errno = 0;
386 *value = strtol (str, &endptr, base);
387 return *endptr == '\0' && str != endptr && errno == 0;
388 }
389
strToUInt(const char * const str,int base,unsigned int * value)390 extern bool strToUInt(const char *const str, int base, unsigned int *value)
391 {
392 unsigned long ulong_value;
393
394 if(!strToULong(str, base, &ulong_value) || ulong_value > UINT_MAX)
395 return false;
396
397 *value = (unsigned int) ulong_value;
398 return true;
399 }
400
strToInt(const char * const str,int base,int * value)401 extern bool strToInt(const char *const str, int base, int *value)
402 {
403 long long_value;
404
405 if(!strToLong(str, base, &long_value) || long_value > INT_MAX || long_value < INT_MIN)
406 return false;
407
408 *value = (int) long_value;
409 return true;
410 }
411
412 /*
413 * File system functions
414 */
415
setCurrentDirectory(void)416 extern void setCurrentDirectory (void)
417 {
418 char* buf;
419 if (CurrentDirectory == NULL)
420 CurrentDirectory = xMalloc ((size_t) (PATH_MAX + 1), char);
421 buf = getcwd (CurrentDirectory, PATH_MAX);
422 if (buf == NULL)
423 perror ("");
424 if (CurrentDirectory [strlen (CurrentDirectory) - (size_t) 1] !=
425 PATH_SEPARATOR)
426 {
427 sprintf (CurrentDirectory + strlen (CurrentDirectory), "%c",
428 OUTPUT_PATH_SEPARATOR);
429 }
430 }
431
432 /* For caching of stat() calls */
eStat(const char * const fileName)433 extern fileStatus *eStat (const char *const fileName)
434 {
435 struct stat status;
436 static fileStatus file;
437 if (file.name == NULL || strcmp (fileName, file.name) != 0)
438 {
439 eStatFree (&file);
440 file.name = eStrdup (fileName);
441 if (lstat (file.name, &status) != 0)
442 file.exists = false;
443 else
444 {
445 file.isSymbolicLink = (bool) S_ISLNK (status.st_mode);
446 if (file.isSymbolicLink && stat (file.name, &status) != 0)
447 file.exists = false;
448 else
449 {
450 file.exists = true;
451 file.isDirectory = (bool) S_ISDIR (status.st_mode);
452 file.isNormalFile = (bool) (S_ISREG (status.st_mode));
453 file.isExecutable = (bool) ((status.st_mode &
454 (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
455 file.isSetuid = (bool) ((status.st_mode & S_ISUID) != 0);
456 file.isSetgid = (bool) ((status.st_mode & S_ISGID) != 0);
457 file.size = status.st_size;
458 }
459 }
460 }
461 return &file;
462 }
463
eStatFree(fileStatus * status)464 extern void eStatFree (fileStatus *status)
465 {
466 if (status->name != NULL)
467 {
468 eFree (status->name);
469 status->name = NULL;
470 }
471 }
472
doesFileExist(const char * const fileName)473 extern bool doesFileExist (const char *const fileName)
474 {
475 fileStatus *status = eStat (fileName);
476 return status->exists;
477 }
478
doesExecutableExist(const char * const fileName)479 extern bool doesExecutableExist (const char *const fileName)
480 {
481 fileStatus *status = eStat (fileName);
482 return status->exists && status->isExecutable;
483 }
484
isRecursiveLink(const char * const dirName)485 extern bool isRecursiveLink (const char* const dirName)
486 {
487 bool result = false;
488 fileStatus *status = eStat (dirName);
489 if (status->isSymbolicLink)
490 {
491 char* const path = absoluteFilename (dirName);
492 while (path [strlen (path) - 1] == PATH_SEPARATOR)
493 path [strlen (path) - 1] = '\0';
494 while (! result && strlen (path) > (size_t) 1)
495 {
496 char *const separator = strrchr (path, PATH_SEPARATOR);
497 if (separator == NULL)
498 break;
499 else if (separator == path) /* backed up to root directory */
500 *(separator + 1) = '\0';
501 else
502 *separator = '\0';
503 result = isSameFile (path, dirName);
504 }
505 eFree (path);
506 }
507 return result;
508 }
509
510 /*
511 * Pathname manipulation (O/S dependent!!!)
512 */
513
isPathSeparator(const int c)514 static bool isPathSeparator (const int c)
515 {
516 bool result;
517 #if defined (MSDOS_STYLE_PATH)
518 result = (bool) (strchr (PathDelimiters, c) != NULL);
519 #else
520 result = (bool) (c == PATH_SEPARATOR);
521 #endif
522 return result;
523 }
524
525 #if ! defined (HAVE_STAT_ST_INO)
526
canonicalizePath(char * const path CTAGS_ATTR_UNUSED)527 static void canonicalizePath (char *const path CTAGS_ATTR_UNUSED)
528 {
529 # if defined (MSDOS_STYLE_PATH)
530 char *p;
531 for (p = path ; *p != '\0' ; ++p)
532 if (isPathSeparator (*p) && *p != ':')
533 *p = PATH_SEPARATOR;
534 # endif
535 }
536
537 #endif
538
isSameFile(const char * const name1,const char * const name2)539 extern bool isSameFile (const char *const name1, const char *const name2)
540 {
541 bool result = false;
542 #if defined (HAVE_STAT_ST_INO)
543 struct stat stat1, stat2;
544
545 if (stat (name1, &stat1) == 0 && stat (name2, &stat2) == 0)
546 result = (bool) (stat1.st_ino == stat2.st_ino);
547 #else
548 {
549 char *const n1 = absoluteFilename (name1);
550 char *const n2 = absoluteFilename (name2);
551 canonicalizePath (n1);
552 canonicalizePath (n2);
553 # if defined (CASE_INSENSITIVE_FILENAMES)
554 result = (bool) (strcasecmp (n1, n2) == 0);
555 # else
556 result = (bool) (strcmp (n1, n2) == 0);
557 # endif
558 free (n1);
559 free (n2);
560 }
561 #endif
562 return result;
563 }
564
baseFilename(const char * const filePath)565 extern const char *baseFilename (const char *const filePath)
566 {
567 #if defined (MSDOS_STYLE_PATH)
568 const char *tail = NULL;
569 unsigned int i;
570
571 /* Find whichever of the path delimiters is last.
572 */
573 for (i = 0 ; i < strlen (PathDelimiters) ; ++i)
574 {
575 # ifdef HAVE_MBLEN
576 const char *p;
577 int ml;
578
579 /* Some DBCS has letter contains 0x5C in trailing byte.
580 * So skip to the trailing byte. */
581 for (p = filePath ; *p != '\0' ; ++p)
582 {
583 ml = mblen(p, MB_LEN_MAX);
584 if (ml > 1)
585 p += ml - 1;
586 else if (*p == PathDelimiters [i] && p > tail)
587 tail = p;
588 }
589 # else
590 const char *sep = strrchr (filePath, PathDelimiters [i]);
591
592 if (sep > tail)
593 tail = sep;
594 # endif
595 }
596 #else
597 const char *tail = strrchr (filePath, PATH_SEPARATOR);
598 #endif
599 if (tail == NULL)
600 tail = filePath;
601 else
602 ++tail; /* step past last delimiter */
603
604 return tail;
605 }
606
fileExtension(const char * const fileName)607 extern const char *fileExtension (const char *const fileName)
608 {
609 const char *extension;
610 const char *pDelimiter = NULL;
611 const char *const base = baseFilename (fileName);
612
613 pDelimiter = strrchr (base, '.');
614
615 if (pDelimiter == NULL)
616 extension = "";
617 else
618 extension = pDelimiter + 1; /* skip to first char of extension */
619
620 return extension;
621 }
622
baseFilenameSansExtensionNew(const char * const fileName,const char * const templateExt)623 extern char* baseFilenameSansExtensionNew (const char *const fileName,
624 const char *const templateExt)
625 {
626 const char *pDelimiter = NULL;
627 const char *const base = baseFilename (fileName);
628 char* shorten_base;
629
630 pDelimiter = strrchr (base, templateExt[0]);
631
632 if (pDelimiter && (strcmp (pDelimiter, templateExt) == 0))
633 {
634 shorten_base = eStrndup (base, pDelimiter - base);
635 return shorten_base;
636 }
637 else
638 return NULL;
639 }
640
isAbsolutePath(const char * const path)641 extern bool isAbsolutePath (const char *const path)
642 {
643 bool result = false;
644 #if defined (MSDOS_STYLE_PATH)
645 if (isPathSeparator (path [0]))
646 result = true;
647 else if (isalpha (path [0]) && path [1] == ':')
648 {
649 if (isPathSeparator (path [2]))
650 result = true;
651 else
652 /* We don't support non-absolute file names with a drive
653 * letter, like `d:NAME' (it's too much hassle).
654 */
655 error (FATAL,
656 "%s: relative file names with drive letters not supported",
657 path);
658 }
659 #else
660 result = isPathSeparator (path [0]);
661 #endif
662 return result;
663 }
664
combinePathAndFile(const char * const path,const char * const file)665 extern char *combinePathAndFile (
666 const char *const path, const char *const file)
667 {
668 vString *const filePath = vStringNew ();
669 const int lastChar = path [strlen (path) - 1];
670 bool terminated = isPathSeparator (lastChar);
671
672 vStringCopyS (filePath, path);
673 if (! terminated)
674 vStringPut (filePath, OUTPUT_PATH_SEPARATOR);
675 vStringCatS (filePath, file);
676
677 return vStringDeleteUnwrap (filePath);
678 }
679
680 /* Return a newly-allocated string whose contents concatenate those of
681 * s1, s2, s3.
682 * Routine adapted from Gnu etags.
683 */
concat(const char * s1,const char * s2,const char * s3)684 static char* concat (const char *s1, const char *s2, const char *s3)
685 {
686 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
687 char *result = xMalloc (len1 + len2 + len3 + 1, char);
688
689 strcpy (result, s1);
690 strcpy (result + len1, s2);
691 strcpy (result + len1 + len2, s3);
692 result [len1 + len2 + len3] = '\0';
693
694 return result;
695 }
696
697 /* Return a newly allocated string containing the absolute file name of FILE
698 * given CWD (which should end with a slash).
699 * Routine adapted from Gnu etags.
700 */
absoluteFilename(const char * file)701 extern char* absoluteFilename (const char *file)
702 {
703 char *slashp, *cp;
704 char *res = NULL;
705 if (isAbsolutePath (file))
706 {
707 #ifdef MSDOS_STYLE_PATH
708 if (file [1] == ':')
709 res = eStrdup (file);
710 else
711 {
712 char drive [3];
713 sprintf (drive, "%c:", currentdrive ());
714 res = concat (drive, file, "");
715 }
716 #else
717 res = eStrdup (file);
718 #endif
719 }
720 else
721 res = concat (CurrentDirectory, file, "");
722
723 /* Delete the "/dirname/.." and "/." substrings. */
724 slashp = strchr (res, PATH_SEPARATOR);
725 while (slashp != NULL && slashp [0] != '\0')
726 {
727 if (slashp[1] == '.')
728 {
729 if (slashp [2] == '.' &&
730 (slashp [3] == PATH_SEPARATOR || slashp [3] == '\0'))
731 {
732 cp = slashp;
733 do
734 cp--;
735 while (cp >= res && ! isAbsolutePath (cp));
736 if (cp < res)
737 cp = slashp;/* the absolute name begins with "/.." */
738 #ifdef MSDOS_STYLE_PATH
739 /* Under MSDOS and NT we get `d:/NAME' as absolute file name,
740 * so the luser could say `d:/../NAME'. We silently treat this
741 * as `d:/NAME'.
742 */
743 else if (cp [0] != PATH_SEPARATOR)
744 cp = slashp;
745 #endif
746 memmove (cp, slashp + 3, strlen (slashp + 3) + 1);
747 slashp = cp;
748 continue;
749 }
750 else if (slashp [2] == PATH_SEPARATOR || slashp [2] == '\0')
751 {
752 memmove (slashp, slashp + 2, strlen (slashp + 2) + 1);
753 continue;
754 }
755 }
756 slashp = strchr (slashp + 1, PATH_SEPARATOR);
757 }
758
759 if (res [0] == '\0')
760 return eStrdup ("/");
761 else
762 {
763 #ifdef MSDOS_STYLE_PATH
764 /* Canonicalize drive letter case. */
765 if (res [1] == ':' && islower (res [0]))
766 res [0] = toupper (res [0]);
767 #endif
768
769 return res;
770 }
771 }
772
773 /* Return a newly allocated string containing the absolute file name of dir
774 * where `file' resides given `CurrentDirectory'.
775 * Routine adapted from Gnu etags.
776 */
absoluteDirname(char * file)777 extern char* absoluteDirname (char *file)
778 {
779 char *slashp, *res;
780 char save;
781 slashp = strrchr (file, PATH_SEPARATOR);
782 if (slashp == NULL)
783 res = eStrdup (CurrentDirectory);
784 else
785 {
786 save = slashp [1];
787 slashp [1] = '\0';
788 res = absoluteFilename (file);
789 slashp [1] = save;
790 }
791 return res;
792 }
793
794 /* Return a newly allocated string containing the file name of FILE relative
795 * to the absolute directory DIR (which should end with a slash).
796 * Routine adapted from Gnu etags.
797 */
relativeFilename(const char * file,const char * dir)798 extern char* relativeFilename (const char *file, const char *dir)
799 {
800 const char *fp, *dp;
801 char *absdir, *res;
802 int i;
803
804 /* Find the common root of file and dir (with a trailing slash). */
805 absdir = absoluteFilename (file);
806 fp = absdir;
807 dp = dir;
808 while (*fp++ == *dp++)
809 continue;
810 fp--;
811 dp--; /* back to the first differing char */
812 do
813 { /* look at the equal chars until path sep */
814 if (fp == absdir)
815 return absdir; /* first char differs, give up */
816 fp--;
817 dp--;
818 } while (*fp != PATH_SEPARATOR);
819
820 /* Build a sequence of "../" strings for the resulting relative file name.
821 */
822 i = 0;
823 while ((dp = strchr (dp + 1, PATH_SEPARATOR)) != NULL)
824 i += 1;
825 res = xMalloc (3 * i + strlen (fp + 1) + 1, char);
826 res [0] = '\0';
827 while (i-- > 0)
828 strcat (res, "../");
829
830 /* Add the file name relative to the common root of file and dir. */
831 strcat (res, fp + 1);
832 free (absdir);
833
834 return res;
835 }
836
tempFile(const char * const mode,char ** const pName)837 extern MIO *tempFile (const char *const mode, char **const pName)
838 {
839 char *name;
840 FILE *fp;
841 MIO *mio;
842 int fd;
843 #if defined(HAVE_MKSTEMP)
844 const char *const pattern = "tags.XXXXXX";
845 const char *tmpdir = NULL;
846 fileStatus *file = eStat (ExecutableProgram);
847 # ifdef WIN32
848 tmpdir = getenv ("TMP");
849 # else
850 if (! file->isSetuid)
851 tmpdir = getenv ("TMPDIR");
852 # endif
853 if (tmpdir == NULL)
854 tmpdir = TMPDIR;
855 name = xMalloc (strlen (tmpdir) + 1 + strlen (pattern) + 1, char);
856 sprintf (name, "%s%c%s", tmpdir, OUTPUT_PATH_SEPARATOR, pattern);
857 fd = mkstemp (name);
858 eStatFree (file);
859 #elif defined(HAVE_TEMPNAM)
860 const char *tmpdir = NULL;
861 # ifdef WIN32
862 tmpdir = getenv ("TMP");
863 # endif
864 if (tmpdir == NULL)
865 tmpdir = TMPDIR;
866 name = tempnam (tmpdir, "tags");
867 if (name == NULL)
868 error (FATAL | PERROR, "cannot allocate temporary file name");
869 fd = open (name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
870 #else
871 name = xMalloc (L_tmpnam, char);
872 if (tmpnam (name) != name)
873 error (FATAL | PERROR, "cannot assign temporary file name");
874 fd = open (name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
875 #endif
876 if (fd == -1)
877 error (FATAL | PERROR, "cannot open temporary file");
878 fp = fdopen (fd, mode);
879 if (fp == NULL)
880 error (FATAL | PERROR, "cannot open temporary file");
881 mio = mio_new_fp (fp, fclose);
882 DebugStatement (
883 debugPrintf (DEBUG_STATUS, "opened temporary file %s\n", name); )
884 Assert (*pName == NULL);
885 *pName = name;
886 return mio;
887 }
888