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