2 * Copyright (c) 2002-2003, Darren Hiebert
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
7 * This module contains a lose assortment of shared functions.
13 #include "general.h" /* must always come first */
16 #include <glib/gstdio.h>
21 # include <stdlib.h> /* to declare malloc (), realloc () */
25 #include <stdio.h> /* to declare tempnam(), and SEEK_SET (hopefully) */
28 # include <fcntl.h> /* to declare O_RDWR, O_CREAT, O_EXCL */
31 # include <unistd.h> /* to declare mkstemp () */
35 # include <limits.h> /* to declare MB_LEN_MAX */
41 /* To declare "struct stat" and stat ().
43 #if defined (HAVE_SYS_TYPES_H)
44 # include <sys/types.h>
46 # if defined (HAVE_TYPES_H)
50 #ifdef HAVE_SYS_STAT_H
51 # include <sys/stat.h>
59 # include <direct.h> /* to _getcwd */
62 # include <dir.h> /* to declare findfirst() and findnext() */
65 # include <io.h> /* to declare open() */
82 # define TMPDIR "/tmp"
88 # if defined (S_IFREG)
89 # define S_ISREG(mode) ((mode) & S_IFREG)
95 # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
97 # define S_ISLNK(mode) FALSE /* assume no soft links */
103 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
105 # define S_ISDIR(mode) FALSE /* assume no soft links */
124 # define S_IRUSR 0400
127 # define S_IWUSR 0200
138 /* Hack for ridiculous practice of Microsoft Visual C++.
141 # if defined (_MSC_VER)
143 # define getcwd _getcwd
144 # define PATH_MAX _MAX_PATH
149 # define PATH_MAX 256
152 #define selected(var,feature) (((int)(var) & (int)(feature)) == (int)feature)
155 * Miscellaneous macros
162 #if defined (MSDOS_STYLE_PATH)
163 const char *const PathDelimiters
= ":/\\";
166 char *CurrentDirectory
;
168 static const char *ExecutableProgram
= NULL
;
169 static const char *ExecutableName
= "geany";
172 * FUNCTION PROTOTYPES
174 #ifdef NEED_PROTO_STAT
175 extern int stat (const char *, struct stat
*);
177 #ifdef NEED_PROTO_LSTAT
178 extern int lstat (const char *, struct stat
*);
183 * FUNCTION DEFINITIONS
187 extern void setExecutableName (const char *const path
)
189 ExecutableProgram
= path
;
190 ExecutableName
= baseFilename (path
);
193 extern const char *getExecutableName (void)
195 return ExecutableName
;
199 extern void *eMalloc (const size_t size
)
201 void *buffer
= g_malloc (size
);
204 error (FATAL
, "out of memory");
209 extern void *eCalloc (const size_t count
, const size_t size
)
211 void *buffer
= calloc (count
, size
);
214 error (FATAL
, "out of memory");
219 extern void *eRealloc (void *const ptr
, const size_t size
)
223 buffer
= eMalloc (size
);
226 buffer
= g_realloc (ptr
, size
);
228 error (FATAL
, "out of memory");
233 extern void eFree (void *const ptr
)
240 extern char* strstr (const char *str
, const char *substr
)
242 const size_t length
= strlen (substr
);
243 const char *match
= NULL
;
246 for (p
= str
; *p
!= '\0' && match
== NULL
; ++p
)
247 if (strncmp (p
, substr
, length
) == 0)
249 return (char*) match
;
253 extern char* eStrdup (const char* str
)
255 char* result
= xMalloc (strlen (str
) + 1, char);
256 strcpy (result
, str
);
260 extern void toLowerString (char* str
)
264 *str
= tolower ((int) *str
);
269 extern void toUpperString (char* str
)
273 *str
= toupper ((int) *str
);
278 /* Newly allocated string containing lower case conversion of a string.
280 extern char* newLowerString (const char* str
)
282 char* const result
= xMalloc (strlen (str
) + 1, char);
285 result
[i
] = tolower ((int) str
[i
]);
286 while (str
[i
++] != '\0');
290 /* Newly allocated string containing upper case conversion of a string.
292 extern char* newUpperString (const char* str
)
294 char* const result
= xMalloc (strlen (str
) + 1, char);
297 result
[i
] = toupper ((int) str
[i
]);
298 while (str
[i
++] != '\0');
304 static void setCurrentDirectory (void)
306 char* const cwd
= getcwd (NULL
, PATH_MAX
);
307 CurrentDirectory
= xMalloc (strlen (cwd
) + 2, char);
308 if (cwd
[strlen (cwd
) - (size_t) 1] == PATH_SEPARATOR
)
309 strcpy (CurrentDirectory
, cwd
);
311 sprintf (CurrentDirectory
, "%s%c", cwd
, OUTPUT_PATH_SEPARATOR
);
317 extern boolean
doesFileExist (const char *const fileName
)
321 return (boolean
) (g_stat (fileName
, &fileStatus
) == 0);
324 extern boolean
isRecursiveLink (const char* const dirName
)
326 boolean result
= FALSE
;
327 char* const path
= absoluteFilename (dirName
);
328 while (path
[strlen (path
) - 1] == PATH_SEPARATOR
)
329 path
[strlen (path
) - 1] = '\0';
330 while (! result
&& strlen (path
) > (size_t) 1)
332 char *const separator
= strrchr (path
, PATH_SEPARATOR
);
333 if (separator
== NULL
)
335 else if (separator
== path
) /* backed up to root directory */
336 *(separator
+ 1) = '\0';
339 result
= isSameFile (path
, dirName
);
345 extern boolean
isSameFile (const char *const name1
, const char *const name2
)
347 boolean result
= FALSE
;
348 #ifdef HAVE_STAT_ST_INO
349 GStatBuf stat1
, stat2
;
351 if (g_stat (name1
, &stat1
) == 0 && g_stat (name2
, &stat2
) == 0)
352 result
= (boolean
) (stat1
.st_ino
== stat2
.st_ino
);
358 * Pathname manipulation (O/S dependent!!!)
361 extern const char *baseFilename (const char *const filePath
)
363 #if defined (MSDOS_STYLE_PATH)
364 const char *tail
= NULL
;
367 /* Find whichever of the path delimiters is last.
369 for (i
= 0 ; i
< strlen (PathDelimiters
) ; ++i
)
371 const char *sep
= strrchr (filePath
, PathDelimiters
[i
]);
377 const char *tail
= strrchr (filePath
, PATH_SEPARATOR
);
382 ++tail
; /* step past last delimiter */
388 * File extension and language mapping
390 extern const char *fileExtension (const char *const fileName
)
392 const char *extension
;
393 const char *pDelimiter
= NULL
;
395 pDelimiter
= strrchr (fileName
, '.');
397 if (pDelimiter
== NULL
)
400 extension
= pDelimiter
+ 1; /* skip to first char of extension */
405 extern boolean
isAbsolutePath (const char *const path
)
407 boolean result
= FALSE
;
408 #if defined (MSDOS_STYLE_PATH)
409 if (strchr (PathDelimiters
, path
[0]) != NULL
)
411 else if (isalpha (path
[0]) && path
[1] == ':')
413 if (strchr (PathDelimiters
, path
[2]) != NULL
)
416 /* We don't support non-absolute file names with a drive
417 * letter, like `d:NAME' (it's too much hassle).
420 "%s: relative file names with drive letters not supported",
424 result
= (boolean
) (path
[0] == PATH_SEPARATOR
);
429 extern vString
*combinePathAndFile (const char *const path
,
430 const char *const file
)
432 vString
*const filePath
= vStringNew ();
433 const int lastChar
= path
[strlen (path
) - 1];
434 # ifdef MSDOS_STYLE_PATH
435 boolean terminated
= (boolean
) (strchr (PathDelimiters
, lastChar
) != NULL
);
437 boolean terminated
= (boolean
) (lastChar
== PATH_SEPARATOR
);
440 vStringCopyS (filePath
, path
);
443 vStringPut (filePath
, OUTPUT_PATH_SEPARATOR
);
444 vStringTerminate (filePath
);
446 vStringCatS (filePath
, file
);
451 /* Return a newly-allocated string whose contents concatenate those of
453 * Routine adapted from Gnu etags.
455 static char* concat (const char *s1
, const char *s2
, const char *s3
)
457 int len1
= strlen (s1
), len2
= strlen (s2
), len3
= strlen (s3
);
458 char *result
= xMalloc (len1
+ len2
+ len3
+ 1, char);
461 strcpy (result
+ len1
, s2
);
462 strcpy (result
+ len1
+ len2
, s3
);
463 result
[len1
+ len2
+ len3
] = '\0';
468 /* Return a newly allocated string containing the absolute file name of FILE
469 * given CWD (which should end with a slash).
470 * Routine adapted from Gnu etags.
472 extern char* absoluteFilename (const char *file
)
477 if (isAbsolutePath (file
))
478 res
= eStrdup (file
);
480 res
= concat (CurrentDirectory
, file
, "");
482 /* Delete the "/dirname/.." and "/." substrings. */
483 slashp
= strchr (res
, '/');
484 while (slashp
!= NULL
&& slashp
[0] != '\0')
486 if (slashp
[1] == '.')
488 if (slashp
[2] == '.' && (slashp
[3] == '/' || slashp
[3] == '\0'))
493 while (cp
>= res
&& ! isAbsolutePath (cp
));
495 cp
= slashp
;/* the absolute name begins with "/.." */
496 #ifdef MSDOS_STYLE_PATH
497 /* Under MSDOS and NT we get `d:/NAME' as absolute file name,
498 * so the luser could say `d:/../NAME'. We silently treat this
501 else if (cp
[0] != '/')
504 memmove (cp
, slashp
+ 3, strlen (slashp
+ 3) + 1);
508 else if (slashp
[2] == '/' || slashp
[2] == '\0')
510 memmove (slashp
, slashp
+ 2, strlen (slashp
+ 2) + 1);
514 slashp
= strchr (slashp
+ 1, '/');
518 return eStrdup ("/");
521 #ifdef MSDOS_STYLE_PATH
522 /* Canonicalize drive letter case. */
523 if (res
[1] == ':' && islower (res
[0]))
524 res
[0] = toupper (res
[0]);
531 /* Return a newly allocated string containing the absolute file name of dir
532 * where FILE resides given CWD (which should end with a slash).
533 * Routine adapted from Gnu etags.
535 extern char* absoluteDirname (char *file
)
539 #ifdef MSDOS_STYLE_PATH
541 for (p
= file
; *p
!= '\0' ; p
++)
545 slashp
= strrchr (file
, '/');
547 res
= eStrdup (CurrentDirectory
);
552 res
= absoluteFilename (file
);
558 /* Return a newly allocated string containing the file name of FILE relative
559 * to the absolute directory DIR (which should end with a slash).
560 * Routine adapted from Gnu etags.
562 extern char* relativeFilename (const char *file
, const char *dir
)
568 /* Find the common root of file and dir (with a trailing slash). */
569 absdir
= absoluteFilename (file
);
572 while (*fp
++ == *dp
++)
575 dp
--; /* back to the first differing char */
577 { /* look at the equal chars until '/' */
579 return absdir
; /* first char differs, give up */
582 } while (*fp
!= '/');
584 /* Build a sequence of "../" strings for the resulting relative file name.
587 while ((dp
= strchr (dp
+ 1, '/')) != NULL
)
589 res
= xMalloc (3 * i
+ strlen (fp
+ 1) + 1, char);
594 /* Add the file name relative to the common root of file and dir. */
595 strcat (res
, fp
+ 1);
601 extern long unsigned int getFileSize (const char *const name
)
604 unsigned long size
= 0;
606 if (g_stat (name
, &fileStatus
) == 0)
607 size
= fileStatus
.st_size
;
613 static boolean
isSymbolicLink (const char *const name
)
619 boolean result
= FALSE
;
621 if (g_lstat (name
, &fileStatus
) == 0)
622 result
= (boolean
) (S_ISLNK (fileStatus
.st_mode
));
628 static boolean
isNormalFile (const char *const name
)
631 boolean result
= FALSE
;
633 if (g_stat (name
, &fileStatus
) == 0)
634 result
= (boolean
) (S_ISREG (fileStatus
.st_mode
));
640 extern boolean
isExecutable (const char *const name
)
643 boolean result
= FALSE
;
645 if (g_stat (name
, &fileStatus
) == 0)
646 result
= (boolean
) ((fileStatus
.st_mode
& (S_IXUSR
|S_IXGRP
|S_IXOTH
)) != 0);
653 static boolean
isSetUID (const char *const name
)
659 boolean result
= FALSE
;
661 if (g_stat (name
, &fileStatus
) == 0)
662 result
= (boolean
) ((fileStatus
.st_mode
& S_ISUID
) != 0);
671 static boolean
isDirectory (const char *const name
)
673 boolean result
= FALSE
;
676 if (g_stat (name
, &fileStatus
) == 0)
677 result
= (boolean
) S_ISDIR (fileStatus
.st_mode
);
682 /*#ifndef HAVE_FGETPOS*/
684 extern int fgetpos ( stream, pos )
690 *pos = ftell (stream);
697 extern int fsetpos ( stream, pos )
701 return fseek (stream, *pos, SEEK_SET);
706 extern FILE *tempFile (const char *const mode
, char **const pName
)
712 const char *const template = "tags.XXXXXX";
713 const char *tmpdir
= NULL
;
714 if (! isSetUID (ExecutableProgram
))
715 tmpdir
= getenv ("TMPDIR");
718 name
= xMalloc (strlen (tmpdir
) + 1 + strlen (template) + 1, char);
719 sprintf (name
, "%s%c%s", tmpdir
, OUTPUT_PATH_SEPARATOR
, template);
722 name
= xMalloc (L_tmpnam
, char);
723 if (tmpnam (name
) != name
)
724 error (FATAL
| PERROR
, "cannot assign temporary file name");
725 fd
= open (name
, O_RDWR
| O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
);
728 error (FATAL
| PERROR
, "cannot open temporary file");
729 fp
= fdopen (fd
, mode
);
731 error (FATAL
| PERROR
, "cannot open temporary file");
733 debugPrintf (DEBUG_STATUS
, "opened temporary file %s\n", name
); )
734 Assert (*pName
== NULL
);
739 extern void error (const errorSelection selection
,
740 const char *const format
, ...)
744 va_start (ap
, format
);
745 fprintf (errout
, "%s: %s", getExecutableName (),
746 selected (selection
, WARNING
) ? "Warning: " : "");
747 vfprintf (errout
, format
, ap
);
748 if (selected (selection
, PERROR
))
749 fprintf (errout
, " : %s", g_strerror (errno
));
750 fputs ("\n", errout
);
752 if (selected (selection
, FATAL
))
757 extern int stricmp (const char *s1
, const char *s2
)
762 result
= toupper ((int) *s1
) - toupper ((int) *s2
);
763 } while (result
== 0 && *s1
++ != '\0' && *s2
++ != '\0');
768 #ifndef HAVE_STRNICMP
769 extern int strnicmp (const char *s1
, const char *s2
, size_t n
)
774 result
= toupper ((int) *s1
) - toupper ((int) *s2
);
775 } while (result
== 0 && --n
> 0 && *s1
++ != '\0' && *s2
++ != '\0');
781 /* vi:set tabstop=4 shiftwidth=4: */