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