Updated Spanish translation
[anjuta-git-plugin.git] / tagmanager / routines.c
blobaea16307b1e5d6172540bb601e5c55adb5d5802b
1 /*
2 * $Id$
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.
13 * INCLUDE FILES
15 #include "general.h" /* must always come first */
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) */
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
33 /* To declare "struct stat" and stat ().
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
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"
65 #ifdef TRAP_MEMORY_CALLS
66 # include "safe_malloc.h"
67 #endif
70 * MACROS
72 #ifndef TMPDIR
73 # define TMPDIR "/tmp"
74 #endif
76 /* File type tests.
78 #ifndef S_ISREG
79 # if defined (S_IFREG) && ! defined (AMIGA)
80 # define S_ISREG(mode) ((mode) & S_IFREG)
81 # else
82 # define S_ISREG(mode) TRUE /* assume regular file */
83 # endif
84 #endif
86 #ifndef S_ISLNK
87 # ifdef S_IFLNK
88 # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
89 # else
90 # define S_ISLNK(mode) FALSE /* assume no soft links */
91 # endif
92 #endif
94 #ifndef S_ISDIR
95 # ifdef S_IFDIR
96 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
97 # else
98 # define S_ISDIR(mode) FALSE /* assume no soft links */
99 # endif
100 #endif
102 #ifndef S_IFMT
103 # define S_IFMT 0
104 #endif
106 #ifndef S_IXUSR
107 # define S_IXUSR 0
108 #endif
109 #ifndef S_IXGRP
110 # define S_IXGRP 0
111 #endif
112 #ifndef S_IXOTH
113 # define S_IXOTH 0
114 #endif
116 #ifndef S_IRUSR
117 # define S_IRUSR 0400
118 #endif
119 #ifndef S_IWUSR
120 # define S_IWUSR 0200
121 #endif
123 #ifndef S_ISUID
124 # define S_ISUID 0
125 #endif
127 /* Hack for rediculous practice of Microsoft Visual C++.
129 #if defined (WIN32)
130 # if defined (_MSC_VER)
131 # define stat _stat
132 # define getcwd _getcwd
133 # define currentdrive() (_getdrive() + 'A' - 1)
134 # define PATH_MAX _MAX_PATH
135 # elif defined (__BORLANDC__)
136 # define PATH_MAX MAXPATH
137 # define currentdrive() (getdisk() + 'A')
138 # elif defined (DJGPP)
139 # define currentdrive() (getdisk() + 'A')
140 # else
141 # define currentdrive() 'C'
142 # endif
143 #endif
145 #ifndef PATH_MAX
146 # define PATH_MAX 256
147 #endif
150 * Miscellaneous macros
152 #define selected(var,feature) (((int)(var) & (int)(feature)) == (int)feature)
155 * DATA DEFINITIONS
157 #if defined (MSDOS_STYLE_PATH)
158 const char *const PathDelimiters = ":/\\";
159 #elif defined (VMS)
160 const char *const PathDelimiters = ":]>";
161 #endif
163 char *CurrentDirectory;
165 static const char *ExecutableProgram;
166 static const char *ExecutableName;
169 * FUNCTION PROTOTYPES
171 #ifdef NEED_PROTO_STAT
172 extern int stat (const char *, struct stat *);
173 #endif
174 #ifdef NEED_PROTO_LSTAT
175 extern int lstat (const char *, struct stat *);
176 #endif
177 #if defined (MSDOS) || defined (WIN32) || defined (VMS) || defined (__EMX__) || defined (AMIGA)
178 # define lstat(fn,buf) stat(fn,buf)
179 #endif
182 * FUNCTION DEFINITIONS
185 extern void freeRoutineResources (void)
187 if (CurrentDirectory != NULL)
188 eFree (CurrentDirectory);
191 extern void setExecutableName (const char *const path)
193 ExecutableProgram = path;
194 ExecutableName = baseFilename (path);
195 #ifdef VAXC
197 /* remove filetype from executable name */
198 char *p = strrchr (ExecutableName, '.');
199 if (p != NULL)
200 *p = '\0';
202 #endif
205 extern const char *getExecutableName (void)
207 return ExecutableName;
210 extern void error (
211 const errorSelection selection, const char *const format, ...)
213 va_list ap;
215 va_start (ap, format);
216 fprintf (errout, "%s: %s", getExecutableName (),
217 selected (selection, WARNING) ? "Warning: " : "");
218 vfprintf (errout, format, ap);
219 if (selected (selection, PERROR))
220 #ifdef HAVE_STRERROR
221 fprintf (errout, " : %s", strerror (errno));
222 #else
223 perror (" ");
224 #endif
225 fputs ("\n", errout);
226 va_end (ap);
227 if (selected (selection, FATAL))
228 exit (1);
232 * Memory allocation functions
235 extern void *eMalloc (const size_t size)
237 void *buffer = malloc (size);
239 if (buffer == NULL)
240 error (FATAL, "out of memory");
242 return buffer;
245 extern void *eCalloc (const size_t count, const size_t size)
247 void *buffer = calloc (count, size);
249 if (buffer == NULL)
250 error (FATAL, "out of memory");
252 return buffer;
255 extern void *eRealloc (void *const ptr, const size_t size)
257 void *buffer;
258 if (ptr == NULL)
259 buffer = eMalloc (size);
260 else
262 buffer = realloc (ptr, size);
263 if (buffer == NULL)
264 error (FATAL, "out of memory");
266 return buffer;
269 extern void eFree (void *const ptr)
271 Assert (ptr != NULL);
272 free (ptr);
276 * String manipulation functions
280 * Compare two strings, ignoring case.
281 * Return 0 for match, < 0 for smaller, > 0 for bigger
282 * Make sure case is folded to uppercase in comparison (like for 'sort -f')
283 * This makes a difference when one of the chars lies between upper and lower
284 * ie. one of the chars [ \ ] ^ _ ` for ascii. (The '_' in particular !)
286 extern int struppercmp (const char *s1, const char *s2)
288 int result;
291 result = toupper ((int) *s1) - toupper ((int) *s2);
292 } while (result == 0 && *s1++ != '\0' && *s2++ != '\0');
293 return result;
296 extern int strnuppercmp (const char *s1, const char *s2, size_t n)
298 int result;
301 result = toupper ((int) *s1) - toupper ((int) *s2);
302 } while (result == 0 && --n > 0 && *s1++ != '\0' && *s2++ != '\0');
303 return result;
306 #ifndef HAVE_STRSTR
307 extern char* strstr (const char *str, const char *substr)
309 const size_t length = strlen (substr);
310 const char *match = NULL;
311 const char *p;
313 for (p = str ; *p != '\0' && match == NULL ; ++p)
314 if (strncmp (p, substr, length) == 0)
315 match = p;
316 return (char*) match;
318 #endif
320 extern char* eStrdup (const char* str)
322 char* result = xMalloc (strlen (str) + 1, char);
323 strcpy (result, str);
324 return result;
327 extern void toLowerString (char* str)
329 while (*str != '\0')
331 *str = tolower ((int) *str);
332 ++str;
336 extern void toUpperString (char* str)
338 while (*str != '\0')
340 *str = toupper ((int) *str);
341 ++str;
345 /* Newly allocated string containing lower case conversion of a string.
347 extern char* newLowerString (const char* str)
349 char* const result = xMalloc (strlen (str) + 1, char);
350 int i = 0;
352 result [i] = tolower ((int) str [i]);
353 while (str [i++] != '\0');
354 return result;
357 /* Newly allocated string containing upper case conversion of a string.
359 extern char* newUpperString (const char* str)
361 char* const result = xMalloc (strlen (str) + 1, char);
362 int i = 0;
364 result [i] = toupper ((int) str [i]);
365 while (str [i++] != '\0');
366 return result;
370 * File system functions
373 extern void setCurrentDirectory (void)
375 #ifndef AMIGA
376 char* buf;
377 #endif
378 if (CurrentDirectory == NULL)
379 CurrentDirectory = xMalloc ((size_t) (PATH_MAX + 1), char);
380 #ifdef AMIGA
381 strcpy (CurrentDirectory, ".");
382 #else
383 buf = getcwd (CurrentDirectory, PATH_MAX);
384 if (buf == NULL)
385 perror ("");
386 #endif
387 if (CurrentDirectory [strlen (CurrentDirectory) - (size_t) 1] !=
388 PATH_SEPARATOR)
390 sprintf (CurrentDirectory + strlen (CurrentDirectory), "%c",
391 OUTPUT_PATH_SEPARATOR);
395 #ifdef AMIGA
396 static boolean isAmigaDirectory (const char *const name)
398 boolean result = FALSE;
399 struct FileInfoBlock *const fib = xMalloc (1, struct FileInfoBlock);
400 if (fib != NULL)
402 const BPTR flock = Lock ((UBYTE *) name, (long) ACCESS_READ);
404 if (flock != (BPTR) NULL)
406 if (Examine (flock, fib))
407 result = ((fib->fib_DirEntryType >= 0) ? TRUE : FALSE);
408 UnLock (flock);
410 eFree (fib);
412 return result;
414 #endif
416 /* For caching of stat() calls */
417 extern fileStatus *eStat (const char *const fileName)
419 struct stat status;
420 static fileStatus file;
421 if (file.name == NULL || strcmp (fileName, file.name) != 0)
423 if (file.name != NULL)
424 eFree (file.name);
425 file.name = eStrdup (fileName);
426 if (lstat (file.name, &status) != 0)
427 file.exists = FALSE;
428 else
430 file.isSymbolicLink = (boolean) S_ISLNK (status.st_mode);
431 if (file.isSymbolicLink && stat (file.name, &status) != 0)
432 file.exists = FALSE;
433 else
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;
449 return &file;
452 extern boolean doesFileExist (const char *const fileName)
454 fileStatus *status = eStat (fileName);
455 return status->exists;
458 extern boolean isRecursiveLink (const char* const dirName)
460 boolean result = FALSE;
461 fileStatus *status = eStat (dirName);
462 if (status->isSymbolicLink)
464 char* const path = absoluteFilename (dirName);
465 while (path [strlen (path) - 1] == PATH_SEPARATOR)
466 path [strlen (path) - 1] = '\0';
467 while (! result && strlen (path) > (size_t) 1)
469 char *const separator = strrchr (path, PATH_SEPARATOR);
470 if (separator == NULL)
471 break;
472 else if (separator == path) /* backed up to root directory */
473 *(separator + 1) = '\0';
474 else
475 *separator = '\0';
476 result = isSameFile (path, dirName);
478 eFree (path);
480 return result;
483 #ifndef HAVE_FGETPOS
485 extern int fgetpos (FILE *stream, fpos_t *pos)
487 int result = 0;
489 *pos = ftell (stream);
490 if (*pos == -1L)
491 result = -1;
493 return result;
496 extern int fsetpos (FILE *stream, fpos_t const *pos)
498 return fseek (stream, *pos, SEEK_SET);
501 #endif
504 * Pathname manipulation (O/S dependent!!!)
507 static boolean isPathSeparator (const int c)
509 boolean result;
510 #if defined (MSDOS_STYLE_PATH) || defined (VMS)
511 result = (boolean) (strchr (PathDelimiters, c) != NULL);
512 #else
513 result = (boolean) (c == PATH_SEPARATOR);
514 #endif
515 return result;
518 #if ! defined (HAVE_STAT_ST_INO)
520 static void canonicalizePath (char *const path __unused__)
522 #if defined (MSDOS_STYLE_PATH)
523 char *p;
524 for (p = path ; *p != '\0' ; ++p)
525 if (isPathSeparator (*p) && *p != ':')
526 *p = PATH_SEPARATOR;
527 #endif
530 #endif
532 extern boolean isSameFile (const char *const name1, const char *const name2)
534 boolean result = FALSE;
535 #if defined (HAVE_STAT_ST_INO)
536 struct stat stat1, stat2;
538 if (stat (name1, &stat1) == 0 && stat (name2, &stat2) == 0)
539 result = (boolean) (stat1.st_ino == stat2.st_ino);
540 #else
542 char *const n1 = absoluteFilename (name1);
543 char *const n2 = absoluteFilename (name2);
544 canonicalizePath (n1);
545 canonicalizePath (n2);
546 # if defined (CASE_INSENSITIVE_FILENAMES)
547 result = (boolean) (strcasecmp (n1, n2) == 0);
548 #else
549 result = (boolean) (strcmp (n1, n2) == 0);
550 #endif
551 free (n1);
552 free (n2);
554 #endif
555 return result;
558 extern const char *baseFilename (const char *const filePath)
560 #if defined (MSDOS_STYLE_PATH) || defined (VMS)
561 const char *tail = NULL;
562 unsigned int i;
564 /* Find whichever of the path delimiters is last.
566 for (i = 0 ; i < strlen (PathDelimiters) ; ++i)
568 const char *sep = strrchr (filePath, PathDelimiters [i]);
570 if (sep > tail)
571 tail = sep;
573 #else
574 const char *tail = strrchr (filePath, PATH_SEPARATOR);
575 #endif
576 if (tail == NULL)
577 tail = filePath;
578 else
579 ++tail; /* step past last delimiter */
580 #ifdef VAXC
582 /* remove version number from filename */
583 char *p = strrchr ((char *) tail, ';');
584 if (p != NULL)
585 *p = '\0';
587 #endif
589 return tail;
592 extern const char *fileExtension (const char *const fileName)
594 const char *extension;
595 const char *pDelimiter = NULL;
596 const char *const base = baseFilename (fileName);
597 #ifdef QDOS
598 pDelimiter = strrchr (base, '_');
599 #endif
600 if (pDelimiter == NULL)
601 pDelimiter = strrchr (base, '.');
603 if (pDelimiter == NULL)
604 extension = "";
605 else
606 extension = pDelimiter + 1; /* skip to first char of extension */
608 return extension;
611 extern boolean isAbsolutePath (const char *const path)
613 boolean result = FALSE;
614 #if defined (MSDOS_STYLE_PATH)
615 if (isPathSeparator (path [0]))
616 result = TRUE;
617 else if (isalpha (path [0]) && path [1] == ':')
619 if (isPathSeparator (path [2]))
620 result = TRUE;
621 else
622 /* We don't support non-absolute file names with a drive
623 * letter, like `d:NAME' (it's too much hassle).
625 error (FATAL,
626 "%s: relative file names with drive letters not supported",
627 path);
629 #elif defined (VMS)
630 result = (boolean) (strchr (path, ':') != NULL);
631 #else
632 result = isPathSeparator (path [0]);
633 #endif
634 return result;
637 extern vString *combinePathAndFile (
638 const char *const path, const char *const file)
640 vString *const filePath = vStringNew ();
641 #ifdef VMS
642 const char *const directoryId = strstr (file, ".DIR;1");
644 if (directoryId == NULL)
646 const char *const versionId = strchr (file, ';');
648 vStringCopyS (filePath, path);
649 if (versionId == NULL)
650 vStringCatS (filePath, file);
651 else
652 vStringNCatS (filePath, file, versionId - file);
653 vStringCopyToLower (filePath, filePath);
655 else
657 /* File really is a directory; append it to the path.
658 * Gotcha: doesn't work with logical names.
660 vStringNCopyS (filePath, path, strlen (path) - 1);
661 vStringPut (filePath, '.');
662 vStringNCatS (filePath, file, directoryId - file);
663 if (strchr (path, '[') != NULL)
664 vStringPut (filePath, ']');
665 else
666 vStringPut (filePath, '>');
667 vStringTerminate (filePath);
669 #else
670 const int lastChar = path [strlen (path) - 1];
671 boolean terminated = isPathSeparator (lastChar);
673 vStringCopyS (filePath, path);
674 if (! terminated)
676 vStringPut (filePath, OUTPUT_PATH_SEPARATOR);
677 vStringTerminate (filePath);
679 vStringCatS (filePath, file);
680 #endif
682 return filePath;
685 /* Return a newly-allocated string whose contents concatenate those of
686 * s1, s2, s3.
687 * Routine adapted from Gnu etags.
689 static char* concat (const char *s1, const char *s2, const char *s3)
691 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
692 char *result = xMalloc (len1 + len2 + len3 + 1, char);
694 strcpy (result, s1);
695 strcpy (result + len1, s2);
696 strcpy (result + len1 + len2, s3);
697 result [len1 + len2 + len3] = '\0';
699 return result;
702 /* Return a newly allocated string containing the absolute file name of FILE
703 * given CWD (which should end with a slash).
704 * Routine adapted from Gnu etags.
706 extern char* absoluteFilename (const char *file)
708 char *slashp, *cp;
709 char *res = NULL;
710 if (isAbsolutePath (file))
712 #ifdef MSDOS_STYLE_PATH
713 if (file [1] == ':')
714 res = eStrdup (file);
715 else
717 char drive [3];
718 sprintf (drive, "%c:", currentdrive ());
719 res = concat (drive, file, "");
721 #else
722 res = eStrdup (file);
723 #endif
725 else
726 res = concat (CurrentDirectory, file, "");
728 /* Delete the "/dirname/.." and "/." substrings. */
729 slashp = strchr (res, PATH_SEPARATOR);
730 while (slashp != NULL && slashp [0] != '\0')
732 if (slashp[1] == '.')
734 if (slashp [2] == '.' &&
735 (slashp [3] == PATH_SEPARATOR || slashp [3] == '\0'))
737 cp = slashp;
739 cp--;
740 while (cp >= res && ! isAbsolutePath (cp));
741 if (cp < res)
742 cp = slashp;/* the absolute name begins with "/.." */
743 #ifdef MSDOS_STYLE_PATH
744 /* Under MSDOS and NT we get `d:/NAME' as absolute file name,
745 * so the luser could say `d:/../NAME'. We silently treat this
746 * as `d:/NAME'.
748 else if (cp [0] != PATH_SEPARATOR)
749 cp = slashp;
750 #endif
751 strcpy (cp, slashp + 3);
752 slashp = cp;
753 continue;
755 else if (slashp [2] == PATH_SEPARATOR || slashp [2] == '\0')
757 strcpy (slashp, slashp + 2);
758 continue;
761 slashp = strchr (slashp + 1, PATH_SEPARATOR);
764 if (res [0] == '\0')
765 return eStrdup ("/");
766 else
768 #ifdef MSDOS_STYLE_PATH
769 /* Canonicalize drive letter case. */
770 if (res [1] == ':' && islower (res [0]))
771 res [0] = toupper (res [0]);
772 #endif
774 return res;
778 /* Return a newly allocated string containing the absolute file name of dir
779 * where `file' resides given `CurrentDirectory'.
780 * Routine adapted from Gnu etags.
782 extern char* absoluteDirname (char *file)
784 char *slashp, *res;
785 char save;
786 slashp = strrchr (file, PATH_SEPARATOR);
787 if (slashp == NULL)
788 res = eStrdup (CurrentDirectory);
789 else
791 save = slashp [1];
792 slashp [1] = '\0';
793 res = absoluteFilename (file);
794 slashp [1] = save;
796 return res;
799 /* Return a newly allocated string containing the file name of FILE relative
800 * to the absolute directory DIR (which should end with a slash).
801 * Routine adapted from Gnu etags.
803 extern char* relativeFilename (const char *file, const char *dir)
805 const char *fp, *dp;
806 char *absdir, *res;
807 int i;
809 /* Find the common root of file and dir (with a trailing slash). */
810 absdir = absoluteFilename (file);
811 fp = absdir;
812 dp = dir;
813 while (*fp++ == *dp++)
814 continue;
815 fp--;
816 dp--; /* back to the first differing char */
818 { /* look at the equal chars until path sep */
819 if (fp == absdir)
820 return absdir; /* first char differs, give up */
821 fp--;
822 dp--;
823 } while (*fp != PATH_SEPARATOR);
825 /* Build a sequence of "../" strings for the resulting relative file name.
827 i = 0;
828 while ((dp = strchr (dp + 1, PATH_SEPARATOR)) != NULL)
829 i += 1;
830 res = xMalloc (3 * i + strlen (fp + 1) + 1, char);
831 res [0] = '\0';
832 while (i-- > 0)
833 strcat (res, "../");
835 /* Add the file name relative to the common root of file and dir. */
836 strcat (res, fp + 1);
837 free (absdir);
839 return res;
842 extern FILE *tempFile (const char *const mode, char **const pName)
844 char *name;
845 FILE *fp;
846 int fd;
847 #if defined(HAVE_MKSTEMP)
848 const char *const pattern = "tags.XXXXXX";
849 const char *tmpdir = NULL;
850 fileStatus *file = eStat (ExecutableProgram);
851 if (! file->isSetuid)
852 tmpdir = getenv ("TMPDIR");
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 #elif defined(HAVE_TEMPNAM)
859 name = tempnam (TMPDIR, "tags");
860 if (name == NULL)
861 error (FATAL | PERROR, "cannot allocate temporary file name");
862 fd = open (name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
863 #else
864 name = xMalloc (L_tmpnam, char);
865 if (tmpnam (name) != name)
866 error (FATAL | PERROR, "cannot assign temporary file name");
867 fd = open (name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
868 #endif
869 if (fd == -1)
870 error (FATAL | PERROR, "cannot open temporary file");
871 fp = fdopen (fd, mode);
872 if (fp == NULL)
873 error (FATAL | PERROR, "cannot open temporary file");
874 DebugStatement (
875 debugPrintf (DEBUG_STATUS, "opened temporary file %s\n", name); )
876 Assert (*pName == NULL);
877 *pName = name;
878 return fp;
881 /* vi:set tabstop=4 shiftwidth=4: */