2 ** Splint - annotation-assisted static program checker
3 ** Copyright (C) 1994-2001 University of Virginia,
4 ** Massachusetts Institute of Technology
6 ** This program is free software; you can redistribute it and/or modify it
7 ** under the terms of the GNU General Public License as published by the
8 ** Free Software Foundation; either version 2 of the License, or (at your
9 ** option) any later version.
11 ** This program is distributed in the hope that it will be useful, but
12 ** WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ** General Public License for more details.
16 ** The GNU General Public License is available from http://www.gnu.org/ or
17 ** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
18 ** MA 02111-1307, USA.
20 ** For information on lclint: lclint-request@cs.virginia.edu
21 ** To report a bug: lclint-bug@cs.virginia.edu
22 ** For more information: http://www.splint.org
27 ** Provide a system-independent interface to system-dependent
32 * Modified by Herbert 04/19/97:
33 * - added conditional 'OS2' to conditional 'MSDOS'
34 * - added include of new header portab.h.
35 * - changed '/' to macro.
36 * - added DOS / OS/2 specific stuff in osd_getPath.
38 * - added OS/2 specific includes before osd_getPid()
39 * - handle files like in WIN32 for OS/2 in osd_fileExists()
44 # include <sys/types.h>
45 # include <sys/stat.h>
46 /* Fix suggested by Lars Rasmussen */
49 /* POSIX platforms should defined getpid in unistd.h */
50 # if defined (WIN32) || (defined(OS2) && defined(__IBMC__))
58 # include "lclintMacros.nf"
65 extern int stat (const char *, /*@out@*/ struct stat *);
68 static bool osd_executableFileExists (char *);
70 static bool nextdir (char **p_current_dir, /*@out@*/ char **p_dir,
71 /*@out@*/ size_t *p_len);
73 extern cstring LSLRootName (cstring filespec)
76 char *result, *startName, *tail;
79 llassert (cstring_isDefined (filespec));
80 tail = strrchr (filespec, CONNECTCHAR);
81 startName = (tail == NULL ? filespec : &tail[1]);
82 tail = strrchr (startName, '.');
83 nameLength = (tail == NULL ? strlen (startName)
84 : size_fromInt (tail - startName));
85 result = dmalloc (nameLength + 1);
86 strncpy (result, startName, nameLength);
87 result[(int) nameLength] = '\0';
89 /*@noaccess cstring@*/
92 extern /*@observer@*/ cstring
93 osd_getEnvironment (cstring env, /*@returned@*/ cstring def)
96 char *ret = osd_getEnvironmentVariable (env);
106 /*@noaccess cstring@*/
112 ** FUNCTIONAL DESCRIPTION:
114 ** This function attempts to locate a file in a search list. On VMS, it
115 ** just concatinates the path and file names, and then lets RMS do the
116 ** searching. On Ultrix, it searches for the file on the path.
118 ** FORMAL PARAMETERS:
120 ** path: search path where to look for the file.
121 ** file: name of file to search for.
122 ** returnPath: if a file is found, this is where the concatenated
123 ** directory and file name are returned.
127 ** OSD_FILEFOUND: the file was found on the search list.
128 ** OSD_FILENOTFOUND the file was not found.
129 ** OSD_PATHTOOLONG the concatenated directory and file name are too
138 ** Requires that parameters, path and file, are valid C strings.
144 extern /*@observer@*/ cstring osd_getHomeDir ()
146 /* Would something different be better for windows? */
147 return (osd_getEnvironmentVariable (cstring_makeLiteralTemp ("HOME")));
150 filestatus osd_findOnLarchPath (cstring file, cstring *returnPath)
152 return (osd_getPath (context_getLarchPath (), file, returnPath));
156 osd_getPath (cstring path, cstring file, cstring *returnPath)
161 char aPath[MAXPATHLEN];
162 filestatus rVal = OSD_FILENOTFOUND; /* assume file not found. */
167 llassert (cstring_isDefined (file));
171 # if defined(OS2) || defined(MSDOS) || defined(WIN32)
172 /* under OS/2 and MSDOS the includePath may be empty, if so, search
173 * the current directory. */
175 (*file == CONNECTCHAR || (file[0] != '\0' && file[1] == ':'))
177 (*file == CONNECTCHAR)
181 /* No path specified. Look for it in the current directory. */
183 strcpy (&aPath[0], file);
185 if (osd_fileExists (&aPath[0]))
187 rVal = OSD_FILEFOUND;
188 *returnPath = dmalloc (strlen (&aPath[0]) + 1);
189 strcpy (*returnPath, &aPath[0]);
194 /* Path specified. Loop through directories in path looking for the */
195 /* first occurrence of the file. */
197 while (nextdir (&fullPath, &dirPtr, &dirLen) &&
198 rVal == OSD_FILENOTFOUND)
200 if ((dirLen + strlen (file) + 2) <= MAXPATHLEN)
202 /* Cat directory and filename, and see if file exists. */
203 strncpy (&aPath[0], dirPtr, dirLen);
204 strcpy (&aPath[0] + dirLen, ""); /* Null terminate aPath. */
205 strcat (&aPath[0], CONNECTSTR);
206 strcat (&aPath[0], file);
208 if (osd_fileExists (&aPath[0]))
210 rVal = OSD_FILEFOUND;
211 *returnPath = (char *) dmalloc (strlen (&aPath[0]) + 1);
212 strcpy (*returnPath, &aPath[0]);
217 rVal = OSD_PATHTOOLONG;
223 /*@noaccess cstring@*/
227 osd_getExePath (cstring path, cstring file, cstring *returnPath)
232 char aPath[MAXPATHLEN];
233 filestatus rVal = OSD_FILENOTFOUND; /* assume file not found. */
236 fullPath = osd_getEnvironmentVariable (path);
238 if (fullPath == NULL)
240 /* No path specified. Look for it in the current directory. */
241 llassert (cstring_isDefined (file));
242 strcpy (&aPath[0], file);
244 if (osd_fileExists (&aPath[0]))
246 rVal = OSD_FILEFOUND;
247 *returnPath = dmalloc (strlen (&aPath[0]) + 1);
248 strcpy (*returnPath, &aPath[0]);
254 ** Path specified. Loop through directories in path looking
255 ** for the first occurrence of the file.
258 while (nextdir (&fullPath, &dirPtr, &dirLen) &&
259 rVal == OSD_FILENOTFOUND)
261 llassert (cstring_isDefined (file));
263 if ((dirLen + cstring_length (file) + 2) <= MAXPATHLEN)
265 /* Cat directory and filename, and see if file exists. */
266 strncpy (&aPath[0], dirPtr, dirLen);
267 strcpy (&aPath[0] + dirLen, ""); /* Null terminate aPath. */
268 strcat (&aPath[0], CONNECTSTR);
269 strcat (&aPath[0], file);
271 if (osd_executableFileExists (&aPath[0]))
273 rVal = OSD_FILEFOUND;
274 *returnPath = dmalloc (strlen (&aPath[0]) + 1);
275 strcpy (*returnPath, &aPath[0]);
280 rVal = OSD_PATHTOOLONG;
286 /*@noaccess cstring@*/
290 osd_fileExists (cstring filespec)
294 return (stat (cstring_toCharsSafe (filespec), &buf) == 0);
296 # if defined (WIN32) || defined (OS2)
297 FILE *test = fileTable_openFile (context_fileTable (), filespec, "r");
301 (void) fileTable_closeFile (context_fileTable (),test);
315 osd_executableFileExists (/*@unused@*/ char *filespec)
319 # if defined(__IBMC__) && defined(OS2)
320 # define S_IFMT (unsigned short)0xFFFF
322 if (stat (filespec, &buf) == 0)
324 /* mask by file type */
325 /*@-unrecog@*/ /* S_IFMT is not defined */
326 if ((buf.st_mode & S_IFMT) != S_IFDIR /*@=unrecog@*/) /* not a directory */
328 /* as long as it is an executable file */
329 # if defined(__IBMC__) && defined(OS2)
330 int com_or_exe_pos = strlen( filespec) - 4;
331 return stricmp( &filespec[com_or_exe_pos], ".exe") == 0
332 || stricmp( &filespec[com_or_exe_pos], ".com") == 0
333 || stricmp( &filespec[com_or_exe_pos], ".bat") == 0
334 || stricmp( &filespec[com_or_exe_pos], ".cmd") == 0;
336 return (((buf.st_mode & S_IXUSR)
337 # if !defined(MSDOS) && !defined(OS2)
338 | (buf.st_mode & S_IXGRP) |
339 (buf.st_mode & S_IXOTH)
353 ** FUNCTIONAL DESCRIPTION:
355 ** Find the next directory from a directory path.
357 ** FORMAL PARAMETERS:
359 ** char ** current_dir :
360 ** Points to the current position in the path string. The first time
361 ** you call this routine, this should point to the first character of
362 ** the path. On return, this will be updated to point to the
363 ** terminating \0 or : of the first directory found. You can then pass
364 ** it unchanged for subsequent calls; this routine will correctly skip
368 ** On exit, this will point to the first character of the directory
369 ** that was found. This will be a pointer directly into the client's
372 ** unsigned int * len :
373 ** On exit, this will contain the length of the directory that was
374 ** found, not counting any terminating \0 or :. If no directory was
375 ** found, this will be 0.
378 ** TRUE if we found another directory.
383 ** We return a pointer and length, rather than a string, because of a)
384 ** historical reasons; and b) this avoids having to allocate storage.
392 nextdir (d_char *current_dir, d_char *dir, size_t *len)
396 if (**current_dir == '\0')
402 *dir = (**current_dir == PATH_SEPARATOR ? *current_dir + 1 : *current_dir);
404 /* Find next ':' or end of string */
405 for (tchar = *dir; *tchar != '\0' && *tchar != PATH_SEPARATOR; tchar++)
410 *current_dir = tchar;
411 *len = size_fromInt (tchar - *dir);
415 /*@observer@*/ /*@null@*/ cstring osd_getEnvironmentVariable (cstring var)
417 /* evans - 2001-08-26 fixed OS instead of OS2 bug, reported by Alexander Mai */
418 # if defined(UNIX) || defined(OS2) || defined(MSDOS) || defined(WIN32)
419 char *val = getenv (cstring_toCharsSafe (var));
423 return cstring_undefined;
427 return cstring_makeLiteralTemp (val);
430 return cstring_undefined;
437 extern /*@external@*/ int _flushall (void) /*@modifies fileSystem@*/ ;
441 extern /*@external@*/ int system (const char *) /*@modifies fileSystem@*/ ;
443 int osd_system (cstring cmd)
446 /* system ("printenv"); */
451 res = system (cstring_toCharsSafe (cmd));
457 /* This should be defined by unistd.h */
459 extern /*@external@*/ int unlink (const char *) /*@modifies fileSystem@*/ ;
463 int osd_unlink (cstring fname)
467 res = unlink (cstring_toCharsSafe (fname));
471 llcontbug (message ("Cannot remove temporary file: %s (%s)",
473 cstring_fromChars (strerror (errno))));
479 # if defined (WIN32) || defined (OS2) && defined (__IBMC__)
486 # if defined (WIN32) || defined (OS2) && defined (__IBMC__)
487 int pid = _getpid ();
489 pid_t pid = getpid ();
495 cstring osd_fixDefine (cstring x)
498 llassert (cstring_isDefined (x));
500 if (strchr (x, '\'') != NULL) {
502 ** If there is a single quote, check for <ident>='<string>' and
503 ** produce <ident>=<string>
506 char *eqs = strchr (x, '=');
509 if (eqs[1] == '\'') {
510 char *endqu = strrchr (x, '\'');
513 if (*(endqu - 1) != '\\') {
514 if (*(endqu + 1) == '\0') {
519 def = cstring_fromChars (eqs + 2);
521 res = cstring_concat (cstring_fromChars (x), def);
532 return cstring_copy (x);
533 /*@noaccess cstring@*/
536 bool osd_fileIsReadable (cstring f)
538 FILE *fl = fileTable_openFile (context_fileTable (), f, "r");
542 check (fileTable_closeFile (context_fileTable (), fl));
551 bool osd_isConnectChar (char c)
553 if (c == CONNECTCHAR)
558 # ifdef HASALTCONNECTCHAR
559 if (c == ALTCONNECTCHAR)
569 ** Returns true if c2 starts with the same path as c1
571 ** This is called by context_isSystemDir to determine if a
572 ** directory is on the system path.
574 ** In unix, this is just a string comparison. For Win32 and OS2, we need a more
575 ** complex comparison.
578 bool osd_equalCanonicalPrefix (cstring dirpath, cstring prefixpath)
580 llassert (cstring_isDefined (prefixpath));
582 if (cstring_isEmpty (dirpath))
584 return (cstring_isEmpty (prefixpath));
587 # if defined (WIN32) || defined (OS2)
589 /*@access cstring@*/ /* Moved this from cstring - should abstract it... */
592 ** If one has a drive specification, but the other doesn't, skip it.
595 if (strchr (dirpath, ':') == NULL
596 && strchr (prefixpath, ':') != NULL)
598 prefixpath = strchr (prefixpath, ':') + 1;
602 if (strchr (prefixpath, ':') == NULL
603 && strchr (dirpath, ':') != NULL)
605 dirpath = strchr (dirpath, ':') + 1;
610 int len = size_toInt (strlen (prefixpath));
614 for (i = 0, slen = 0; i < len; i++, slen++)
616 /* Allow any number of connect characters in any combination:
617 * c:/usr//src\/foo == c:\\usr/src\/foo
618 * After this we'll be at the last of such a sequence */
620 if (osd_isConnectChar (dirpath[slen]) && osd_isConnectChar (prefixpath[i]))
622 /* Skip one or more connect chars */
624 for (; osd_isConnectChar (dirpath[slen+1]); ++slen)
629 for (; osd_isConnectChar (prefixpath[i+1]); ++i)
634 /* Windows, MSDOS and OS/2 use case-insensitive path specs! */
635 else if (toupper (dirpath[slen]) != toupper (prefixpath[i]))
643 /*@noaccess cstring@*/
647 return (cstring_equalPrefix (dirpath, prefixpath));
653 ** This code provided by Herbert Martin Dietze, to canonicalize path names.
656 char *osd_getcwd (/*@returned@*/ char *str, size_t size)
658 return getcwd (str, size);
661 /*@null@*/ /*@observer@*/ char *
662 osd_dirNext (char *str)
664 char *p1 = strchr (str, '/');
665 char *p2 = strchr (str, '\\');
682 else /* both not null */
684 return (p1 < p2 ? p1 : p2) + 1;
689 osd_dirShift (char *str, size_t num) /*@modifies str@*/
693 assert (num <= strlen (str));
695 for (i = 0; str[i] != '\0'; i++)
697 str[i] = str[i + num];
702 osd_dirDotdot (char *str)
704 return str[0] == '.' && str[1] == '.' && osd_isConnectChar (str[2]);
708 osd_dirNormalize (char *str)
712 for (; osd_isConnectChar (str[0]); str++)
716 for (; str != NULL && osd_dirDotdot (str); str = osd_dirNext (str))
720 for (pos1 = pos2 = str;
722 pos2 = pos1, pos1 = osd_dirNext (pos1))
724 /* remove redundant `./' entry */
725 while (pos1[0] == '.' && osd_isConnectChar (pos1[1]))
727 osd_dirShift (pos1, 2);
730 /* remove redundant `foo/../' entry */
731 if (osd_dirDotdot (pos1) && pos2 < pos1)
733 osd_dirShift (pos2, pos1 - pos2 + 1);
734 osd_dirNormalize (str);
740 osd_dirAbsolute (char *str)
743 size_t size = PATH_MAX * sizeof (*ret);
745 if (osd_isConnectChar (str[0]))
747 ret = dmalloc ((strlen (str) + 1) * sizeof (*ret));
752 ret = dmalloc (size);
754 ret = osd_getcwd (ret, size);
755 ret = realloc (ret, (strlen (str) + strlen (ret) + 2) * sizeof (*ret));
762 strcat (ret, CONNECTSTR);
766 osd_dirNormalize (ret);
775 ** This code is adapted from:
777 ** 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
780 ** Protoize program - Original version by Ron Guilmette (rfg@segfault.us.com).
781 ** Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
783 ** This file is part of GNU CC.
785 ** GNU CC is free software; you can redistribute it and/or modify
786 ** it under the terms of the GNU General Public License as published by
787 ** the Free Software Foundation; either version 2, or (at your option)
788 ** any later version.
790 ** GNU CC is distributed in the hope that it will be useful,
791 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
792 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
793 ** GNU General Public License for more details.
795 ** You should have received a copy of the GNU General Public License
796 ** along with GNU CC; see the file COPYING. If not, write to
797 ** the Free Software Foundation, 59 Temple Place - Suite 330,
798 ** Boston, MA 02111-1307, USA.
802 ** Return the absolutized filename for the given relative
803 ** filename. Note that if that filename is already absolute, it may
804 ** still be returned in a modified form because this routine also
805 ** eliminates redundant slashes and single dots and eliminates double
806 ** dots to get a shortest possible filename from the given input
807 ** filename. The absolutization of relative filenames is made by
808 ** assuming that the given filename is to be taken as relative to
809 ** the first argument (cwd) or to the current directory if cwd is
813 /* A pointer to the current directory filename (used by abspath). */
814 static /*@only@*/ cstring osd_cwd = cstring_undefined;
816 static void osd_setWorkingDirectory (void)
819 char *buf = dmalloc (sizeof (*buf) * MAXPATHLEN);
820 char *cwd = getcwd (buf, MAXPATHLEN);
822 llassert (cstring_isUndefined (osd_cwd));
826 lldiagmsg (message ("Cannot get working directory: %s\n",
827 lldecodeerror (errno)));
831 osd_cwd = cstring_fromCharsNew (cwd);
836 ; /* Don't know how to do this for non-POSIX platforms */
840 void osd_initMod (void)
842 osd_setWorkingDirectory ();
845 cstring osd_absolutePath (cstring cwd, cstring filename)
848 /* Setup the current working directory as needed. */
849 cstring cwd2 = cstring_isDefined (cwd) ? cwd : osd_cwd;
851 char *endp, *outp, *inp;
854 llassert (cstring_isDefined (cwd2));
855 llassert (cstring_isDefined (filename));
857 abs_buffer = (char *) dmalloc (size_fromInt (cstring_length (cwd2) + cstring_length (filename) + 2));
861 ** Copy the filename (possibly preceded by the current working
862 ** directory name) into the absolutization buffer.
868 if (filename[0] != '/')
872 while ((*endp++ = *src_p++) != '\0')
877 *(endp-1) = '/'; /* overwrite null */
882 while ((*endp++ = *src_p++) != '\0')
888 /* Now make a copy of abs_buffer into abs_buffer, shortening the
889 filename (by taking out slashes and dots) as we go. */
891 outp = inp = abs_buffer;
892 *outp++ = *inp++; /* copy first slash */
895 *outp++ = *inp++; /* copy second slash */
903 else if (inp[0] == '/' && outp[-1] == '/')
908 else if (inp[0] == '.' && outp[-1] == '/')
914 else if (inp[1] == '/')
919 else if ((inp[1] == '.')
920 && (inp[2] == '\0' || inp[2] == '/'))
922 inp += (inp[2] == '/') ? 3 : 2;
925 while (outp >= abs_buffer && *outp != '/')
930 if (outp < abs_buffer)
932 /* Catch cases like /.. where we try to backup to a
933 point above the absolute root of the logical file
936 llfatalbug (message ("Invalid file name: %s", filename));
955 /* On exit, make sure that there is a trailing null, and make sure that
956 the last character of the returned string is *not* a slash. */
962 /*@noaccess cstring@*/
963 return cstring_fromChars (abs_buffer);
965 return cstring_copy (filename);
970 ** Given a filename (and possibly a directory name from which the filename
971 ** is relative) return a string which is the shortest possible
972 ** equivalent for the corresponding full (absolutized) filename. The
973 ** shortest possible equivalent may be constructed by converting the
974 ** absolutized filename to be a relative filename (i.e. relative to
975 ** the actual current working directory). However if a relative filename
976 ** is longer, then the full absolute filename is returned.
978 ** KNOWN BUG: subpart of the original filename is actually a symbolic link.
981 cstring osd_outputPath (cstring filename)
986 cstring cwd_p = osd_cwd;
988 int unmatched_slash_count = 0;
989 size_t filename_len = size_fromInt (cstring_length (filename));
993 rel_buf_p = rel_buffer = (char *) dmalloc (filename_len);
995 llassert (cwd_p != NULL);
996 llassert (path_p != NULL);
998 while ((*cwd_p != '\0') && (*cwd_p == *path_p))
1004 if ((*cwd_p == '\0') && (*path_p == '\0' || *path_p == '/')) /* whole pwd matched */
1006 if (*path_p == '\0') /* input *is* the current path! */
1007 return cstring_makeLiteral (".");
1010 /*@i324 ! lclint didn't report an errors for: return ++path_p; */
1011 return cstring_fromCharsNew (++path_p);
1016 if (*path_p != '\0')
1020 while (*cwd_p != '/') /* backup to last slash */
1027 unmatched_slash_count++;
1030 /* Find out how many directory levels in cwd were *not* matched. */
1031 while (*cwd_p != '\0')
1033 if (*cwd_p++ == '/')
1034 unmatched_slash_count++;
1037 /* Now we know how long the "short name" will be.
1038 Reject it if longer than the input. */
1039 if (unmatched_slash_count * 3 + strlen (path_p) >= filename_len)
1041 return cstring_copy (filename);
1046 ** I'm trusting the code on this one...don't see how this is guaranteed though.
1049 assertSet (rel_buffer);
1051 /* For each of them, put a `../' at the beginning of the short name. */
1052 while (unmatched_slash_count-- > 0)
1054 /* Give up if the result gets to be longer
1055 than the absolute path name. */
1056 if (rel_buffer + filename_len <= rel_buf_p + 3)
1059 return cstring_copy (filename);
1067 /* Then tack on the unmatched part of the desired file's name. */
1070 if (rel_buffer + filename_len <= rel_buf_p)
1072 cstring_free (rel_buffer);
1073 return cstring_copy (filename);
1076 while ((*rel_buf_p++ = *path_p++) != '\0') ;
1079 if (*(rel_buf_p-1) == '/')
1080 *--rel_buf_p = '\0';
1084 /*@noaccess cstring@*/
1086 return cstring_copy (filename);