Making "inline" behave like an attribute. Fixes #1
[arduino-ctags.git] / routines.c
blob83bcdccda5fd6aac4c85c3a998ec2fa9c751e670
1 /*
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.
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"
66 * MACROS
68 #ifndef TMPDIR
69 # define TMPDIR "/tmp"
70 #endif
72 /* File type tests.
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
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
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
98 #ifndef S_IFMT
99 # define S_IFMT 0
100 #endif
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
112 #ifndef S_IRUSR
113 # define S_IRUSR 0400
114 #endif
115 #ifndef S_IWUSR
116 # define S_IWUSR 0200
117 #endif
119 #ifndef S_ISUID
120 # define S_ISUID 0
121 #endif
123 /* Hack for rediculous practice of Microsoft Visual C++.
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
141 #ifndef PATH_MAX
142 # define PATH_MAX 256
143 #endif
146 * Miscellaneous macros
148 #define selected(var,feature) (((int)(var) & (int)(feature)) == (int)feature)
151 * DATA DEFINITIONS
153 #if defined (MSDOS_STYLE_PATH)
154 const char *const PathDelimiters = ":/\\";
155 #elif defined (VMS)
156 const char *const PathDelimiters = ":]>";
157 #endif
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 *);
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
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);
191 #ifdef VAXC
193 /* remove filetype from executable name */
194 char *p = strrchr (ExecutableName, '.');
195 if (p != NULL)
196 *p = '\0';
198 #endif
201 extern const char *getExecutableName (void)
203 return ExecutableName;
206 extern const char *getExecutablePath (void)
208 return ExecutableProgram;
211 extern void error (
212 const errorSelection selection, const char *const format, ...)
214 va_list ap;
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);
233 * Memory allocation functions
236 extern void *eMalloc (const size_t size)
238 void *buffer = malloc (size);
240 if (buffer == NULL)
241 error (FATAL, "out of memory");
243 return buffer;
246 extern void *eCalloc (const size_t count, const size_t size)
248 void *buffer = calloc (count, size);
250 if (buffer == NULL)
251 error (FATAL, "out of memory");
253 return buffer;
256 extern void *eRealloc (void *const ptr, const size_t size)
258 void *buffer;
259 if (ptr == NULL)
260 buffer = eMalloc (size);
261 else
263 buffer = realloc (ptr, size);
264 if (buffer == NULL)
265 error (FATAL, "out of memory");
267 return buffer;
270 extern void eFree (void *const ptr)
272 Assert (ptr != NULL);
273 free (ptr);
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)
289 int result;
292 result = toupper ((int) *s1) - toupper ((int) *s2);
293 } while (result == 0 && *s1++ != '\0' && *s2++ != '\0');
294 return result;
297 extern int strnuppercmp (const char *s1, const char *s2, size_t n)
299 int result;
302 result = toupper ((int) *s1) - toupper ((int) *s2);
303 } while (result == 0 && --n > 0 && *s1++ != '\0' && *s2++ != '\0');
304 return result;
307 #ifndef HAVE_STRSTR
308 extern char* strstr (const char *str, const char *substr)
310 const size_t length = strlen (substr);
311 const char *match = NULL;
312 const char *p;
314 for (p = str ; *p != '\0' && match == NULL ; ++p)
315 if (strncmp (p, substr, length) == 0)
316 match = p;
317 return (char*) match;
319 #endif
321 extern char* eStrdup (const char* str)
323 char* result = xMalloc (strlen (str) + 1, char);
324 strcpy (result, str);
325 return result;
328 extern void toLowerString (char* str)
330 while (*str != '\0')
332 *str = tolower ((int) *str);
333 ++str;
337 extern void toUpperString (char* str)
339 while (*str != '\0')
341 *str = toupper ((int) *str);
342 ++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);
351 int i = 0;
353 result [i] = tolower ((int) str [i]);
354 while (str [i++] != '\0');
355 return result;
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);
363 int i = 0;
365 result [i] = toupper ((int) str [i]);
366 while (str [i++] != '\0');
367 return result;
371 * File system functions
374 extern void setCurrentDirectory (void)
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)
391 sprintf (CurrentDirectory + strlen (CurrentDirectory), "%c",
392 OUTPUT_PATH_SEPARATOR);
396 #ifdef AMIGA
397 static boolean isAmigaDirectory (const char *const name)
399 boolean result = FALSE;
400 struct FileInfoBlock *const fib = xMalloc (1, struct FileInfoBlock);
401 if (fib != NULL)
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);
409 UnLock (flock);
411 eFree (fib);
413 return result;
415 #endif
417 /* For caching of stat() calls */
418 extern fileStatus *eStat (const char *const fileName)
420 struct stat status;
421 static fileStatus file;
422 if (file.name == NULL || strcmp (fileName, file.name) != 0)
424 eStatFree (&file);
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 void eStatFree (fileStatus *status)
454 if (status->name != NULL)
456 eFree (status->name);
457 status->name = NULL;
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)
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);
487 eFree (path);
489 return result;
492 #ifndef HAVE_FGETPOS
494 extern int fgetpos (FILE *stream, fpos_t *pos)
496 int result = 0;
498 *pos = ftell (stream);
499 if (*pos == -1L)
500 result = -1;
502 return result;
505 extern int fsetpos (FILE *stream, fpos_t const *pos)
507 return fseek (stream, *pos, SEEK_SET);
510 #endif
513 * Pathname manipulation (O/S dependent!!!)
516 static boolean isPathSeparator (const int c)
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;
527 #if ! defined (HAVE_STAT_ST_INO)
529 static void canonicalizePath (char *const path __unused__)
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
539 #endif
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);
549 #else
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);
563 #endif
564 return result;
567 extern const char *baseFilename (const char *const filePath)
569 #if defined (MSDOS_STYLE_PATH) || defined (VMS)
570 const char *tail = NULL;
571 unsigned int i;
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]);
579 if (sep > tail)
580 tail = sep;
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
591 /* remove version number from filename */
592 char *p = strrchr ((char *) tail, ';');
593 if (p != NULL)
594 *p = '\0';
596 #endif
598 return 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);
606 #ifdef QDOS
607 pDelimiter = strrchr (base, '_');
608 #endif
609 if (pDelimiter == NULL)
610 pDelimiter = strrchr (base, '.');
612 if (pDelimiter == NULL)
613 extension = "";
614 else
615 extension = pDelimiter + 1; /* skip to first char of extension */
617 return extension;
620 extern boolean isAbsolutePath (const char *const path)
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] == ':')
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).
634 error (FATAL,
635 "%s: relative file names with drive letters not supported",
636 path);
638 #elif defined (VMS)
639 result = (boolean) (strchr (path, ':') != NULL);
640 #else
641 result = isPathSeparator (path [0]);
642 #endif
643 return result;
646 extern vString *combinePathAndFile (
647 const char *const path, const char *const file)
649 vString *const filePath = vStringNew ();
650 #ifdef VMS
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);
660 else
661 vStringNCatS (filePath, file, versionId - file);
662 vStringCopyToLower (filePath, filePath);
664 else
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, ']');
674 else
675 vStringPut (filePath, '>');
676 vStringTerminate (filePath);
678 #else
679 const int lastChar = path [strlen (path) - 1];
680 boolean terminated = isPathSeparator (lastChar);
682 vStringCopyS (filePath, path);
683 if (! terminated)
685 vStringPut (filePath, OUTPUT_PATH_SEPARATOR);
686 vStringTerminate (filePath);
688 vStringCatS (filePath, file);
689 #endif
691 return filePath;
694 /* Return a newly-allocated string whose contents concatenate those of
695 * s1, s2, s3.
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);
703 strcpy (result, s1);
704 strcpy (result + len1, s2);
705 strcpy (result + len1 + len2, s3);
706 result [len1 + len2 + len3] = '\0';
708 return result;
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)
717 char *slashp, *cp;
718 char *res = NULL;
719 if (isAbsolutePath (file))
721 #ifdef MSDOS_STYLE_PATH
722 if (file [1] == ':')
723 res = eStrdup (file);
724 else
726 char drive [3];
727 sprintf (drive, "%c:", currentdrive ());
728 res = concat (drive, file, "");
730 #else
731 res = eStrdup (file);
732 #endif
734 else
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'))
746 cp = slashp;
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'.
757 else if (cp [0] != PATH_SEPARATOR)
758 cp = slashp;
759 #endif
760 strcpy (cp, slashp + 3);
761 slashp = cp;
762 continue;
764 else if (slashp [2] == PATH_SEPARATOR || slashp [2] == '\0')
766 strcpy (slashp, slashp + 2);
767 continue;
770 slashp = strchr (slashp + 1, PATH_SEPARATOR);
773 if (res [0] == '\0')
774 return eStrdup ("/");
775 else
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
783 return res;
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)
793 char *slashp, *res;
794 char save;
795 slashp = strrchr (file, PATH_SEPARATOR);
796 if (slashp == NULL)
797 res = eStrdup (CurrentDirectory);
798 else
800 save = slashp [1];
801 slashp [1] = '\0';
802 res = absoluteFilename (file);
803 slashp [1] = save;
805 return res;
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)
814 const char *fp, *dp;
815 char *absdir, *res;
816 int i;
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 */
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);
834 /* Build a sequence of "../" strings for the resulting relative file name.
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, "../");
844 /* Add the file name relative to the common root of file and dir. */
845 strcat (res, fp + 1);
846 free (absdir);
848 return res;
851 extern FILE *tempFile (const char *const mode, char **const pName)
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;
891 /* vi:set tabstop=4 shiftwidth=4: */