2 * $Id: routines.c 536 2007-06-02 06:09:00Z elliotth $
4 * Copyright (c) 2002-2003, Darren Hiebert
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
9 * This module contains a lose assortment of shared functions.
15 #include "general.h" /* must always come first */
18 # include <stdlib.h> /* to declare malloc (), realloc () */
24 #include <stdio.h> /* to declare tempnam(), and SEEK_SET (hopefully) */
27 # include <fcntl.h> /* to declar O_RDWR, O_CREAT, O_EXCL */
30 # include <unistd.h> /* to declare mkstemp () */
33 /* To declare "struct stat" and stat ().
35 #if defined (HAVE_SYS_TYPES_H)
36 # include <sys/types.h>
38 # if defined (HAVE_TYPES_H)
42 #ifdef HAVE_SYS_STAT_H
43 # include <sys/stat.h>
51 # include <dos.h> /* to declare MAXPATH */
54 # include <direct.h> /* to _getcwd */
57 # include <dir.h> /* to declare findfirst() and findnext() */
60 # include <io.h> /* to declare open() */
69 # define TMPDIR "/tmp"
75 # if defined (S_IFREG) && ! defined (AMIGA)
76 # define S_ISREG(mode) ((mode) & S_IFREG)
78 # define S_ISREG(mode) TRUE /* assume regular file */
84 # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
86 # define S_ISLNK(mode) FALSE /* assume no soft links */
92 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
94 # define S_ISDIR(mode) FALSE /* assume no soft links */
113 # define S_IRUSR 0400
116 # define S_IWUSR 0200
123 /* Hack for rediculous practice of Microsoft Visual C++.
126 # if defined (_MSC_VER)
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')
137 # define currentdrive() 'C'
142 # define PATH_MAX 256
146 * Miscellaneous macros
148 #define selected(var,feature) (((int)(var) & (int)(feature)) == (int)feature)
153 #if defined (MSDOS_STYLE_PATH)
154 const char *const PathDelimiters
= ":/\\";
156 const char *const PathDelimiters
= ":]>";
159 char *CurrentDirectory
;
161 static const char *ExecutableProgram
;
162 static const char *ExecutableName
;
165 * FUNCTION PROTOTYPES
167 #ifdef NEED_PROTO_STAT
168 extern int stat (const char *, struct stat
*);
170 #ifdef NEED_PROTO_LSTAT
171 extern int lstat (const char *, struct stat
*);
173 #if defined (MSDOS) || defined (WIN32) || defined (VMS) || defined (__EMX__) || defined (AMIGA)
174 # define lstat(fn,buf) stat(fn,buf)
178 * FUNCTION DEFINITIONS
181 extern void freeRoutineResources (void)
183 if (CurrentDirectory
!= NULL
)
184 eFree (CurrentDirectory
);
187 extern void setExecutableName (const char *const path
)
189 ExecutableProgram
= path
;
190 ExecutableName
= baseFilename (path
);
193 /* remove filetype from executable name */
194 char *p
= strrchr (ExecutableName
, '.');
201 extern const char *getExecutableName (void)
203 return ExecutableName
;
206 extern const char *getExecutablePath (void)
208 return ExecutableProgram
;
212 const errorSelection selection
, const char *const format
, ...)
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
))
222 fprintf (errout
, " : %s", strerror (errno
));
226 fputs ("\n", errout
);
228 if (selected (selection
, FATAL
))
233 * Memory allocation functions
236 extern void *eMalloc (const size_t size
)
238 void *buffer
= malloc (size
);
241 error (FATAL
, "out of memory");
246 extern void *eCalloc (const size_t count
, const size_t size
)
248 void *buffer
= calloc (count
, size
);
251 error (FATAL
, "out of memory");
256 extern void *eRealloc (void *const ptr
, const size_t size
)
260 buffer
= eMalloc (size
);
263 buffer
= realloc (ptr
, size
);
265 error (FATAL
, "out of memory");
270 extern void eFree (void *const ptr
)
272 Assert (ptr
!= NULL
);
277 * String manipulation functions
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 !)
287 extern int struppercmp (const char *s1
, const char *s2
)
292 result
= toupper ((int) *s1
) - toupper ((int) *s2
);
293 } while (result
== 0 && *s1
++ != '\0' && *s2
++ != '\0');
297 extern int strnuppercmp (const char *s1
, const char *s2
, size_t n
)
302 result
= toupper ((int) *s1
) - toupper ((int) *s2
);
303 } while (result
== 0 && --n
> 0 && *s1
++ != '\0' && *s2
++ != '\0');
308 extern char* strstr (const char *str
, const char *substr
)
310 const size_t length
= strlen (substr
);
311 const char *match
= NULL
;
314 for (p
= str
; *p
!= '\0' && match
== NULL
; ++p
)
315 if (strncmp (p
, substr
, length
) == 0)
317 return (char*) match
;
321 extern char* eStrdup (const char* str
)
323 char* result
= xMalloc (strlen (str
) + 1, char);
324 strcpy (result
, str
);
328 extern void toLowerString (char* str
)
332 *str
= tolower ((int) *str
);
337 extern void toUpperString (char* str
)
341 *str
= toupper ((int) *str
);
346 /* Newly allocated string containing lower case conversion of a string.
348 extern char* newLowerString (const char* str
)
350 char* const result
= xMalloc (strlen (str
) + 1, char);
353 result
[i
] = tolower ((int) str
[i
]);
354 while (str
[i
++] != '\0');
358 /* Newly allocated string containing upper case conversion of a string.
360 extern char* newUpperString (const char* str
)
362 char* const result
= xMalloc (strlen (str
) + 1, char);
365 result
[i
] = toupper ((int) str
[i
]);
366 while (str
[i
++] != '\0');
371 * File system functions
374 extern void setCurrentDirectory (void)
379 if (CurrentDirectory
== NULL
)
380 CurrentDirectory
= xMalloc ((size_t) (PATH_MAX
+ 1), char);
382 strcpy (CurrentDirectory
, ".");
384 buf
= getcwd (CurrentDirectory
, PATH_MAX
);
388 if (CurrentDirectory
[strlen (CurrentDirectory
) - (size_t) 1] !=
391 sprintf (CurrentDirectory
+ strlen (CurrentDirectory
), "%c",
392 OUTPUT_PATH_SEPARATOR
);
397 static boolean
isAmigaDirectory (const char *const name
)
399 boolean result
= FALSE
;
400 struct FileInfoBlock
*const fib
= xMalloc (1, struct FileInfoBlock
);
403 const BPTR flock
= Lock ((UBYTE
*) name
, (long) ACCESS_READ
);
405 if (flock
!= (BPTR
) NULL
)
407 if (Examine (flock
, fib
))
408 result
= ((fib
->fib_DirEntryType
>= 0) ? TRUE
: FALSE
);
417 /* For caching of stat() calls */
418 extern fileStatus
*eStat (const char *const fileName
)
421 static fileStatus file
;
422 if (file
.name
== NULL
|| strcmp (fileName
, file
.name
) != 0)
425 file
.name
= eStrdup (fileName
);
426 if (lstat (file
.name
, &status
) != 0)
430 file
.isSymbolicLink
= (boolean
) S_ISLNK (status
.st_mode
);
431 if (file
.isSymbolicLink
&& stat (file
.name
, &status
) != 0)
437 file
.isDirectory
= isAmigaDirectory (file
.name
);
439 file
.isDirectory
= (boolean
) S_ISDIR (status
.st_mode
);
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
;
452 extern void eStatFree (fileStatus
*status
)
454 if (status
->name
!= NULL
)
456 eFree (status
->name
);
461 extern boolean
doesFileExist (const char *const fileName
)
463 fileStatus
*status
= eStat (fileName
);
464 return status
->exists
;
467 extern boolean
isRecursiveLink (const char* const dirName
)
469 boolean result
= FALSE
;
470 fileStatus
*status
= eStat (dirName
);
471 if (status
->isSymbolicLink
)
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)
478 char *const separator
= strrchr (path
, PATH_SEPARATOR
);
479 if (separator
== NULL
)
481 else if (separator
== path
) /* backed up to root directory */
482 *(separator
+ 1) = '\0';
485 result
= isSameFile (path
, dirName
);
494 extern int fgetpos (FILE *stream
, fpos_t *pos
)
498 *pos
= ftell (stream
);
505 extern int fsetpos (FILE *stream
, fpos_t const *pos
)
507 return fseek (stream
, *pos
, SEEK_SET
);
513 * Pathname manipulation (O/S dependent!!!)
516 static boolean
isPathSeparator (const int c
)
519 #if defined (MSDOS_STYLE_PATH) || defined (VMS)
520 result
= (boolean
) (strchr (PathDelimiters
, c
) != NULL
);
522 result
= (boolean
) (c
== PATH_SEPARATOR
);
527 #if ! defined (HAVE_STAT_ST_INO)
529 static void canonicalizePath (char *const path __unused__
)
531 #if defined (MSDOS_STYLE_PATH)
533 for (p
= path
; *p
!= '\0' ; ++p
)
534 if (isPathSeparator (*p
) && *p
!= ':')
541 extern boolean
isSameFile (const char *const name1
, const char *const name2
)
543 boolean result
= FALSE
;
544 #if defined (HAVE_STAT_ST_INO)
545 struct stat stat1
, stat2
;
547 if (stat (name1
, &stat1
) == 0 && stat (name2
, &stat2
) == 0)
548 result
= (boolean
) (stat1
.st_ino
== stat2
.st_ino
);
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);
558 result
= (boolean
) (strcmp (n1
, n2
) == 0);
567 extern const char *baseFilename (const char *const filePath
)
569 #if defined (MSDOS_STYLE_PATH) || defined (VMS)
570 const char *tail
= NULL
;
573 /* Find whichever of the path delimiters is last.
575 for (i
= 0 ; i
< strlen (PathDelimiters
) ; ++i
)
577 const char *sep
= strrchr (filePath
, PathDelimiters
[i
]);
583 const char *tail
= strrchr (filePath
, PATH_SEPARATOR
);
588 ++tail
; /* step past last delimiter */
591 /* remove version number from filename */
592 char *p
= strrchr ((char *) tail
, ';');
601 extern const char *fileExtension (const char *const fileName
)
603 const char *extension
;
604 const char *pDelimiter
= NULL
;
605 const char *const base
= baseFilename (fileName
);
607 pDelimiter
= strrchr (base
, '_');
609 if (pDelimiter
== NULL
)
610 pDelimiter
= strrchr (base
, '.');
612 if (pDelimiter
== NULL
)
615 extension
= pDelimiter
+ 1; /* skip to first char of extension */
620 extern boolean
isAbsolutePath (const char *const path
)
622 boolean result
= FALSE
;
623 #if defined (MSDOS_STYLE_PATH)
624 if (isPathSeparator (path
[0]))
626 else if (isalpha (path
[0]) && path
[1] == ':')
628 if (isPathSeparator (path
[2]))
631 /* We don't support non-absolute file names with a drive
632 * letter, like `d:NAME' (it's too much hassle).
635 "%s: relative file names with drive letters not supported",
639 result
= (boolean
) (strchr (path
, ':') != NULL
);
641 result
= isPathSeparator (path
[0]);
646 extern vString
*combinePathAndFile (
647 const char *const path
, const char *const file
)
649 vString
*const filePath
= vStringNew ();
651 const char *const directoryId
= strstr (file
, ".DIR;1");
653 if (directoryId
== NULL
)
655 const char *const versionId
= strchr (file
, ';');
657 vStringCopyS (filePath
, path
);
658 if (versionId
== NULL
)
659 vStringCatS (filePath
, file
);
661 vStringNCatS (filePath
, file
, versionId
- file
);
662 vStringCopyToLower (filePath
, filePath
);
666 /* File really is a directory; append it to the path.
667 * Gotcha: doesn't work with logical names.
669 vStringNCopyS (filePath
, path
, strlen (path
) - 1);
670 vStringPut (filePath
, '.');
671 vStringNCatS (filePath
, file
, directoryId
- file
);
672 if (strchr (path
, '[') != NULL
)
673 vStringPut (filePath
, ']');
675 vStringPut (filePath
, '>');
676 vStringTerminate (filePath
);
679 const int lastChar
= path
[strlen (path
) - 1];
680 boolean terminated
= isPathSeparator (lastChar
);
682 vStringCopyS (filePath
, path
);
685 vStringPut (filePath
, OUTPUT_PATH_SEPARATOR
);
686 vStringTerminate (filePath
);
688 vStringCatS (filePath
, file
);
694 /* Return a newly-allocated string whose contents concatenate those of
696 * Routine adapted from Gnu etags.
698 static char* concat (const char *s1
, const char *s2
, const char *s3
)
700 int len1
= strlen (s1
), len2
= strlen (s2
), len3
= strlen (s3
);
701 char *result
= xMalloc (len1
+ len2
+ len3
+ 1, char);
704 strcpy (result
+ len1
, s2
);
705 strcpy (result
+ len1
+ len2
, s3
);
706 result
[len1
+ len2
+ len3
] = '\0';
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.
715 extern char* absoluteFilename (const char *file
)
719 if (isAbsolutePath (file
))
721 #ifdef MSDOS_STYLE_PATH
723 res
= eStrdup (file
);
727 sprintf (drive
, "%c:", currentdrive ());
728 res
= concat (drive
, file
, "");
731 res
= eStrdup (file
);
735 res
= concat (CurrentDirectory
, file
, "");
737 /* Delete the "/dirname/.." and "/." substrings. */
738 slashp
= strchr (res
, PATH_SEPARATOR
);
739 while (slashp
!= NULL
&& slashp
[0] != '\0')
741 if (slashp
[1] == '.')
743 if (slashp
[2] == '.' &&
744 (slashp
[3] == PATH_SEPARATOR
|| slashp
[3] == '\0'))
749 while (cp
>= res
&& ! isAbsolutePath (cp
));
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
757 else if (cp
[0] != PATH_SEPARATOR
)
760 strcpy (cp
, slashp
+ 3);
764 else if (slashp
[2] == PATH_SEPARATOR
|| slashp
[2] == '\0')
766 strcpy (slashp
, slashp
+ 2);
770 slashp
= strchr (slashp
+ 1, PATH_SEPARATOR
);
774 return eStrdup ("/");
777 #ifdef MSDOS_STYLE_PATH
778 /* Canonicalize drive letter case. */
779 if (res
[1] == ':' && islower (res
[0]))
780 res
[0] = toupper (res
[0]);
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.
791 extern char* absoluteDirname (char *file
)
795 slashp
= strrchr (file
, PATH_SEPARATOR
);
797 res
= eStrdup (CurrentDirectory
);
802 res
= absoluteFilename (file
);
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.
812 extern char* relativeFilename (const char *file
, const char *dir
)
818 /* Find the common root of file and dir (with a trailing slash). */
819 absdir
= absoluteFilename (file
);
822 while (*fp
++ == *dp
++)
825 dp
--; /* back to the first differing char */
827 { /* look at the equal chars until path sep */
829 return absdir
; /* first char differs, give up */
832 } while (*fp
!= PATH_SEPARATOR
);
834 /* Build a sequence of "../" strings for the resulting relative file name.
837 while ((dp
= strchr (dp
+ 1, PATH_SEPARATOR
)) != NULL
)
839 res
= xMalloc (3 * i
+ strlen (fp
+ 1) + 1, char);
844 /* Add the file name relative to the common root of file and dir. */
845 strcat (res
, fp
+ 1);
851 extern FILE *tempFile (const char *const mode
, char **const pName
)
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");
864 name
= xMalloc (strlen (tmpdir
) + 1 + strlen (pattern
) + 1, char);
865 sprintf (name
, "%s%c%s", tmpdir
, OUTPUT_PATH_SEPARATOR
, pattern
);
868 #elif defined(HAVE_TEMPNAM)
869 name
= tempnam (TMPDIR
, "tags");
871 error (FATAL
| PERROR
, "cannot allocate temporary file name");
872 fd
= open (name
, O_RDWR
| O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
);
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
);
880 error (FATAL
| PERROR
, "cannot open temporary file");
881 fp
= fdopen (fd
, mode
);
883 error (FATAL
| PERROR
, "cannot open temporary file");
885 debugPrintf (DEBUG_STATUS
, "opened temporary file %s\n", name
); )
886 Assert (*pName
== NULL
);
891 /* vi:set tabstop=4 shiftwidth=4: */