X-Git-Url: http://andersk.mit.edu/gitweb/splint.git/blobdiff_plain/1d239d69c6db918ef0691cc89740335df558c7c6..35b9a1d988c06908076e3a6eeae55b1147879607:/src/osd.c diff --git a/src/osd.c b/src/osd.c index 59961d2..3716f30 100644 --- a/src/osd.c +++ b/src/osd.c @@ -1,6 +1,6 @@ /* -** LCLint - annotation-assisted static program checker -** Copyright (C) 1994-2001 University of Virginia, +** Splint - annotation-assisted static program checker +** Copyright (C) 1994-2003 University of Virginia, ** Massachusetts Institute of Technology ** ** This program is free software; you can redistribute it and/or modify it @@ -17,9 +17,9 @@ ** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, ** MA 02111-1307, USA. ** -** For information on lclint: lclint-request@cs.virginia.edu -** To report a bug: lclint-bug@cs.virginia.edu -** For more information: http://lclint.cs.virginia.edu +** For information on splint: info@splint.org +** To report a bug: splint-bug@splint.org +** For more information: http://www.splint.org */ /* ** osd.c @@ -37,6 +37,8 @@ * Herbert 06/12/2000: * - added OS/2 specific includes before osd_getPid() * - handle files like in WIN32 for OS/2 in osd_fileExists() + * Herbert 02/17/2002: + * - added OS/2 support to absolute file names */ /*@-allmacros*/ @@ -49,13 +51,15 @@ /* POSIX platforms should defined getpid in unistd.h */ # if defined (WIN32) || (defined(OS2) && defined(__IBMC__)) # include +# include +# define getcwd _getcwd # else # include # endif /*@end@*/ /*@=allmacros*/ -# include "lclintMacros.nf" +# include "splintMacros.nf" # include "basic.h" # include "osd.h" # include "portab.h" @@ -166,6 +170,12 @@ osd_getPath (cstring path, cstring file, cstring *returnPath) fullPath = path; llassert (cstring_isDefined (file)); + /* 2002-01-01: make sure returnPath gets defined even when there are errors. + ** (fixed splint checking detected this) + */ + + *returnPath = cstring_undefined; + if (fullPath == NULL || # if defined(OS2) || defined(MSDOS) || defined(WIN32) @@ -218,7 +228,7 @@ osd_getPath (cstring path, cstring file, cstring *returnPath) } } } - + return rVal; /*@noaccess cstring@*/ } @@ -232,7 +242,8 @@ osd_getExePath (cstring path, cstring file, cstring *returnPath) char aPath[MAXPATHLEN]; filestatus rVal = OSD_FILENOTFOUND; /* assume file not found. */ /*@access cstring@*/ - + + *returnPath = cstring_undefined; fullPath = osd_getEnvironmentVariable (path); if (fullPath == NULL) @@ -294,11 +305,11 @@ osd_fileExists (cstring filespec) return (stat (cstring_toCharsSafe (filespec), &buf) == 0); # else # if defined (WIN32) || defined (OS2) - FILE *test = fopen (cstring_toCharsSafe (filespec), "r"); + FILE *test = fileTable_openReadFile (context_fileTable (), filespec); if (test != NULL) { - (void) fclose (test); + (void) fileTable_closeFile (context_fileTable (),test); return TRUE; } else @@ -311,14 +322,25 @@ osd_fileExists (cstring filespec) # endif } +# if defined(__IBMC__) && defined(OS2) +# define S_IFMT (unsigned short)0xFFFF +# endif + +/* +** Works form Win32 at least... +*/ + +# ifndef S_IXUSR +/*@-macrounrecog@*/ +# define S_IXUSR _S_IEXEC +/*@=macrounrecog@*/ +# endif + bool osd_executableFileExists (/*@unused@*/ char *filespec) { # ifdef UNIX - struct stat buf; -# if defined(__IBMC__) && defined(OS2) -# define S_IFMT (unsigned short)0xFFFF -# endif + struct stat buf; if (stat (filespec, &buf) == 0) { /* mask by file type */ @@ -327,14 +349,14 @@ osd_executableFileExists (/*@unused@*/ char *filespec) { /* as long as it is an executable file */ # if defined(__IBMC__) && defined(OS2) - int com_or_exe_pos = strlen( filespec) - 4; - return stricmp( &filespec[com_or_exe_pos], ".exe") == 0 - || stricmp( &filespec[com_or_exe_pos], ".com") == 0 - || stricmp( &filespec[com_or_exe_pos], ".bat") == 0 - || stricmp( &filespec[com_or_exe_pos], ".cmd") == 0; + int com_or_exe_pos = strlen( filespec) - 4; + return stricmp( &filespec[com_or_exe_pos], ".exe") == 0 + || stricmp( &filespec[com_or_exe_pos], ".com") == 0 + || stricmp( &filespec[com_or_exe_pos], ".bat") == 0 + || stricmp( &filespec[com_or_exe_pos], ".cmd") == 0; # else return (((buf.st_mode & S_IXUSR) -# if !defined(MSDOS) && !defined(OS2) +# if defined (S_IXGRP) && defined (S_IXOTH) | (buf.st_mode & S_IXGRP) | (buf.st_mode & S_IXOTH) # endif @@ -342,7 +364,6 @@ osd_executableFileExists (/*@unused@*/ char *filespec) # endif } } - # endif return (FALSE); @@ -396,6 +417,7 @@ nextdir (d_char *current_dir, d_char *dir, size_t *len) if (**current_dir == '\0') { *len = 0; + *dir = NULL; return FALSE; } @@ -431,8 +453,6 @@ nextdir (d_char *current_dir, d_char *dir, size_t *len) # endif } -# ifndef NOLCL - # ifdef WIN32 extern /*@external@*/ int _flushall (void) /*@modifies fileSystem@*/ ; # endif @@ -451,7 +471,6 @@ int osd_system (cstring cmd) res = system (cstring_toCharsSafe (cmd)); return res; } -# endif # ifndef unlink /* This should be defined by unistd.h */ @@ -460,6 +479,13 @@ extern /*@external@*/ int unlink (const char *) /*@modifies fileSystem@*/ ; /*@=redecl@*/ # endif +static s_tempError = FALSE; + +void osd_setTempError (void) +{ + s_tempError = TRUE; +} + int osd_unlink (cstring fname) { int res; @@ -468,9 +494,12 @@ int osd_unlink (cstring fname) if (res != 0) { - llcontbug (message ("Cannot remove temporary file: %s (%s)", - fname, - cstring_fromChars (strerror (errno)))); + if (!s_tempError) + { + llcontbug (message ("Cannot remove temporary file: %s (%s)", + fname, + cstring_fromChars (strerror (errno)))); + } } return res; @@ -535,11 +564,11 @@ cstring osd_fixDefine (cstring x) bool osd_fileIsReadable (cstring f) { - FILE *fl = fopen (cstring_toCharsSafe (f), "r"); + FILE *fl = fileTable_openReadFile (context_fileTable (), f); - if (fl != (FILE *) 0) + if (fl != NULL) { - check (fclose (fl) == 0); + check (fileTable_closeFile (context_fileTable (), fl)); return (TRUE); } else @@ -565,3 +594,592 @@ bool osd_isConnectChar (char c) return FALSE; } +/* +** Returns true if c2 starts with the same path as c1 +** +** This is called by context_isSystemDir to determine if a +** directory is on the system path. +** +** In unix, this is just a string comparison. For Win32 and OS2, we need a more +** complex comparison. +*/ + +bool osd_equalCanonicalPrefix (cstring dirpath, cstring prefixpath) +{ + llassert (cstring_isDefined (prefixpath)); + + if (cstring_isEmpty (dirpath)) + { + return (cstring_isEmpty (prefixpath)); + } + +# if defined (WIN32) || defined (OS2) + + /*@access cstring@*/ /* Moved this from cstring - should abstract it... */ + + /* + ** If one has a drive specification, but the other doesn't, skip it. + */ + + if (strchr (dirpath, ':') == NULL + && strchr (prefixpath, ':') != NULL) + { + prefixpath = strchr (prefixpath, ':') + 1; + } + else + { + if (strchr (prefixpath, ':') == NULL + && strchr (dirpath, ':') != NULL) + { + dirpath = strchr (dirpath, ':') + 1; + } + } + + { + int len = size_toInt (strlen (prefixpath)); + int i = 0; + int slen = 0; + + for (i = 0, slen = 0; i < len; i++, slen++) + { + /* Allow any number of connect characters in any combination: + * c:/usr//src\/foo == c:\\usr/src\/foo + * After this we'll be at the last of such a sequence */ + + if (osd_isConnectChar (dirpath[slen]) && osd_isConnectChar (prefixpath[i])) + { + /* Skip one or more connect chars */ + + for (; osd_isConnectChar (dirpath[slen+1]); ++slen) + { + ; + } + + for (; osd_isConnectChar (prefixpath[i+1]); ++i) + { + ; + } + } + /* Windows, MSDOS and OS/2 use case-insensitive path specs! */ + else if (toupper (dirpath[slen]) != toupper (prefixpath[i])) + { + return FALSE; + } + + } + } + + /*@noaccess cstring@*/ + + return TRUE; +# else + return (cstring_equalPrefix (dirpath, prefixpath)); +# endif +} + +# if 0 +/* +** This code provided by Herbert Martin Dietze, to canonicalize path names. +*/ + +char *osd_getcwd (/*@returned@*/ char *str, size_t size) +{ + return getcwd (str, size); +} + +/*@null@*/ /*@observer@*/ char * +osd_dirNext (char *str) +{ + char *p1 = strchr (str, '/'); + char *p2 = strchr (str, '\\'); + + if (p1 == NULL) + { + if (p2 != NULL) + { + return p2 + 1; + } + else + { + return NULL; + } + } + else if (p2 == NULL) + { + return p1 + 1; + } + else /* both not null */ + { + return (p1 < p2 ? p1 : p2) + 1; + } +} + +static void +osd_dirShift (char *str, size_t num) /*@modifies str@*/ +{ + int i; + + assert (num <= strlen (str)); + + for (i = 0; str[i] != '\0'; i++) + { + str[i] = str[i + num]; + } +} + +bool +osd_dirDotdot (char *str) +{ + return str[0] == '.' && str[1] == '.' && osd_isConnectChar (str[2]); +} + +void +osd_dirNormalize (char *str) +{ + char *pos1, *pos2; + + for (; osd_isConnectChar (str[0]); str++) + { + } + + for (; str != NULL && osd_dirDotdot (str); str = osd_dirNext (str)) + { + } + + for (pos1 = pos2 = str; + pos1 != NULL; + pos2 = pos1, pos1 = osd_dirNext (pos1)) + { + /* remove redundant `./' entry */ + while (pos1[0] == '.' && osd_isConnectChar (pos1[1])) + { + osd_dirShift (pos1, 2); + } + + /* remove redundant `foo/../' entry */ + if (osd_dirDotdot (pos1) && pos2 < pos1) + { + osd_dirShift (pos2, pos1 - pos2 + 1); + osd_dirNormalize (str); + } + } +} + +/*@null@*/ char * +osd_dirAbsolute (char *str) +{ + char *ret = NULL; + size_t size = PATH_MAX * sizeof (*ret); + + DPRINTF (("Absolute for: %s", str)); + +# if defined (WIN32) || defined (OS2) || defined (MSDOS) + if (strlen (str) > 1 && str[1] == ':') + { + /* + ** Its a drive letter + */ + + ret = dmalloc ((strlen (str) + 1) * sizeof (*ret)); + strcpy (ret, str); + } + else +# endif + if (osd_isConnectChar (str[0])) + { + ret = dmalloc ((strlen (str) + 1) * sizeof (*ret)); + strcpy (ret, str); + } + else + { + ret = dmalloc (size); + + ret = osd_getcwd (ret, size); + ret = realloc (ret, (strlen (str) + strlen (ret) + 2) * sizeof (*ret)); + + if (ret == NULL) + { + return NULL; + } + + strcat (ret, CONNECTSTR); + strcat (ret, str); + } + + osd_dirNormalize (ret); + return ret; +} + +# endif + +/* +** absolute paths +** +** This code is adapted from: +** +** http://src.openresources.com/debian/src/devel/HTML/S/altgcc_2.7.2.2.orig%20altgcc-2.7.2.2.orig%20protoize.c.html#1297 +** +** +** Protoize program - Original version by Ron Guilmette (rfg@segfault.us.com). +** Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. +** +** This file is part of GNU CC. +** +** GNU CC is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2, or (at your option) +** any later version. +** +** GNU CC is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with GNU CC; see the file COPYING. If not, write to +** the Free Software Foundation, 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +*/ + +/* +** Return the absolutized filename for the given relative +** filename. Note that if that filename is already absolute, it may +** still be returned in a modified form because this routine also +** eliminates redundant slashes and single dots and eliminates double +** dots to get a shortest possible filename from the given input +** filename. The absolutization of relative filenames is made by +** assuming that the given filename is to be taken as relative to +** the first argument (cwd) or to the current directory if cwd is +** NULL. +*/ + +/* A pointer to the current directory filename (used by abspath). */ +static /*@only@*/ cstring osd_cwd = cstring_undefined; + +static void osd_setWorkingDirectory (void) +{ +# if defined (UNIX) || defined (OS2) + char *buf = dmalloc (sizeof (*buf) * MAXPATHLEN); + char *cwd = getcwd (buf, MAXPATHLEN); + + llassert (cstring_isUndefined (osd_cwd)); + + if (cwd == NULL) + { + lldiagmsg (message ("Cannot get working directory: %s\n", + lldecodeerror (errno))); + osd_cwd = cstring_makeLiteral (""); + } + else + { + osd_cwd = cstring_fromCharsNew (cwd); + } + + sfree (buf); +# else + ; /* Don't know how to do this for non-POSIX platforms */ +# endif +} + +void osd_initMod (void) +{ + osd_setWorkingDirectory (); +} + +void osd_destroyMod (void) +{ + cstring_free (osd_cwd); + osd_cwd = cstring_undefined; +} + +cstring osd_absolutePath (cstring cwd, cstring filename) +{ +# if defined (UNIX) || defined (OS2) + /* Setup the current working directory as needed. */ + cstring cwd2 = cstring_isDefined (cwd) ? cwd : osd_cwd; + char *abs_buffer; + char *endp, *outp, *inp; + + /*@access cstring@*/ + llassert (cstring_isDefined (cwd2)); + llassert (cstring_isDefined (filename)); + + abs_buffer = (char *) dmalloc (cstring_length (cwd2) + cstring_length (filename) + 2); + endp = abs_buffer; + + /* + ** Copy the filename (possibly preceded by the current working + ** directory name) into the absolutization buffer. + */ + + { + const char *src_p; + + if (!osd_isConnectChar (filename[0]) +# ifdef OS2 + && !(isalpha (filename[0]) && filename[1] == ':') +# endif + ) + { + src_p = cwd2; + + while ((*endp++ = *src_p++) != '\0') + { + continue; + } + + *(endp-1) = CONNECTCHAR; /* overwrite null */ + } + + src_p = filename; + + while ((*endp++ = *src_p++) != '\0') + { + continue; + } + } + + /* Now make a copy of abs_buffer into abs_buffer, shortening the + filename (by taking out slashes and dots) as we go. */ + + outp = inp = abs_buffer; + *outp++ = *inp++; /* copy first slash */ +#ifdef apollo + if (inp[0] == '/') + *outp++ = *inp++; /* copy second slash */ +#endif + for (;;) + { + if (inp[0] == '\0') + { + break; + } + else if (osd_isConnectChar (inp[0]) && osd_isConnectChar (outp[-1])) + { + inp++; + continue; + } + else if (inp[0] == '.' && osd_isConnectChar (outp[-1])) + { + if (inp[1] == '\0') + { + break; + } + else if (osd_isConnectChar (inp[1])) + { + inp += 2; + continue; + } + else if ((inp[1] == '.') + && (inp[2] == '\0' || osd_isConnectChar (inp[2]))) + { + inp += (osd_isConnectChar (inp[2])) ? 3 : 2; + outp -= 2; + + while (outp >= abs_buffer && !osd_isConnectChar (*outp)) + { + outp--; + } + + if (outp < abs_buffer) + { + /* Catch cases like /.. where we try to backup to a + point above the absolute root of the logical file + system. */ + + llfatalbug (message ("Invalid file name: %s", filename)); + } + + *++outp = '\0'; + continue; + } + else + { + ; + } + } + else + { + ; + } + + *outp++ = *inp++; + } + + /* On exit, make sure that there is a trailing null, and make sure that + the last character of the returned string is *not* a slash. */ + + *outp = '\0'; + if (osd_isConnectChar (outp[-1])) + *--outp = '\0'; + + /*@noaccess cstring@*/ + return cstring_fromChars (abs_buffer); +# else + DPRINTF (("Here: %s", filename)); + return cstring_copy (filename); +# endif +} + +/* +** Given a filename (and possibly a directory name from which the filename +** is relative) return a string which is the shortest possible +** equivalent for the corresponding full (absolutized) filename. The +** shortest possible equivalent may be constructed by converting the +** absolutized filename to be a relative filename (i.e. relative to +** the actual current working directory). However if a relative filename +** is longer, then the full absolute filename is returned. +** +** KNOWN BUG: subpart of the original filename is actually a symbolic link. +** +** this is really horrible code...surely someone has written a less buggy version of this! +*/ + +cstring osd_outputPath (cstring filename) +{ + /*@i2534 fix this junky code once and for all! */ +# if defined (UNIX) || defined (OS2) + char *rel_buffer; + char *rel_buf_p; + cstring cwd_p = osd_cwd; + char *path_p; + int unmatched_slash_count = 0; + size_t filename_len = cstring_length (filename); + + llassertretval (filename_len > 0, filename); + + /*@access cstring@*/ + path_p = filename; + rel_buffer = (char *) dmalloc (filename_len); + rel_buf_p = rel_buffer; + *rel_buf_p = '\0'; + + if (cwd_p == NULL) + { + /* Need to prevent recursive assertion failures */ + return cstring_copy (filename); + } + + llassert (cwd_p != NULL); + llassert (path_p != NULL); + + while ((*cwd_p != '\0') && (*cwd_p == *path_p)) + { + cwd_p++; + path_p++; + } + + if ((*cwd_p == '\0') && (*path_p == '\0' || osd_isConnectChar (*path_p))) /* whole pwd matched */ + { + if (*path_p == '\0') /* input *is* the current path! */ + { + cstring_free (rel_buffer); + return cstring_makeLiteral ("."); + } + else + { + /*@i324 ! splint didn't report an errors for: return ++path_p; */ + cstring_free (rel_buffer); + return cstring_fromCharsNew (path_p + 1); + } + } + else + { + + /* drl 2002-10/14 I had to put this code back*/ + /* the case that needs it is when splint is given an absolute path name of a file outside of the current directory and the subdirectories below the current directory. e.g. cd /home/; splint /tmp/prog.c + */ + + /* evans 2002-02-05 This is horrible code, which I've removed. I couldn't find any + ** test cases that need it, so I hope I'm not breaking anything. + */ + /*#if 0*/ + + if (*path_p != '\0') + { + --cwd_p; + --path_p; + + while (cwd_p >= osd_cwd && !osd_isConnectChar (*cwd_p)) /* backup to last slash */ + { + --cwd_p; + --path_p; + } + cwd_p++; + path_p++; + unmatched_slash_count++; + } + + /* Find out how many directory levels in cwd were *not* matched. */ + while (*cwd_p != '\0') + { + if (osd_isConnectChar (*cwd_p++)) + unmatched_slash_count++; + } + + /* Now we know how long the "short name" will be. + Reject it if longer than the input. */ + if (unmatched_slash_count * 3 + strlen (path_p) >= filename_len) + { + cstring_free (rel_buffer); + /* fprintf (stderr, "Returning filename: %s [%p]\n", filename); */ + return cstring_copy (filename); + } + + /*drl 10-14-2002 end previously removed code */ + /*#endif*/ + /* For each of them, put a `../' at the beginning of the short name. */ + while (unmatched_slash_count-- > 0) + { + /* Give up if the result gets to be longer + than the absolute path name. */ + /*@i423@*/ if (rel_buffer + filename_len <= rel_buf_p + 3) + { + sfree (rel_buffer); + return cstring_copy (filename); + } + + *rel_buf_p++ = '.'; + *rel_buf_p++ = '.'; + *rel_buf_p++ = CONNECTCHAR; + } + + /* Then tack on the unmatched part of the desired file's name. */ + + do + { + if (rel_buffer + filename_len <= rel_buf_p) + { + cstring_free (rel_buffer); + return cstring_copy (filename); + } + } /*@-usereleased@*/ + while ((*rel_buf_p++ = *path_p++) != '\0') ; + + + /*@=usereleased@*/ /*@i523! shouldn't need these */ + --rel_buf_p; + + if (osd_isConnectChar (*(rel_buf_p-1))) + *--rel_buf_p = '\0'; + + /* fprintf (stderr, "Returning buffer: %s [%p]\n", rel_buffer, rel_buffer); */ + return rel_buffer; + } + /*@noaccess cstring@*/ +# else + return cstring_copy (filename); +# endif +} + +cstring osd_getCurrentDirectory () +{ +# if defined(MSDOS) || defined(OS2) + return cstring_makeLiteralTemp (""); +# else + return cstring_makeLiteralTemp ("./"); +# endif +} + + +