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