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