]> andersk Git - splint.git/blob - src/osd.c
b429d3f30b37c0785df7c81c5dec6a9c0730ef46
[splint.git] / src / osd.c
1 /*
2 ** Splint - annotation-assisted static program checker
3 ** Copyright (C) 1994-2003 University of Virginia,
4 **         Massachusetts Institute of Technology
5 **
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.
10 ** 
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.
15 ** 
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.
19 **
20 ** For information on splint: info@splint.org
21 ** To report a bug: splint-bug@splint.org
22 ** For more information: http://www.splint.org
23 */
24 /*
25 ** osd.c
26 **
27 ** Provide a system-independent interface to system-dependent
28 ** file operations.
29 */
30
31 /*
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.
37  * Herbert 06/12/2000:
38  * - added OS/2 specific includes before osd_getPid()
39  * - handle files like in WIN32 for OS/2 in osd_fileExists()
40  * Herbert 02/17/2002:
41  * - added OS/2 support to absolute file names
42  */
43
44 /*@-allmacros*/
45 /*@ignore@*/
46 # include <sys/types.h>
47 # include <sys/stat.h>
48 /* Fix suggested by Lars Rasmussen */
49 # include <errno.h>
50
51 /* POSIX platforms should defined getpid in unistd.h */
52 # if defined (WIN32) || (defined(OS2) && defined(__IBMC__))
53 # include <process.h>
54 # include <direct.h>
55 # define getcwd _getcwd
56 # else
57 # include <unistd.h>
58 # endif
59
60 /*@end@*/
61 /*@=allmacros*/
62 # include "splintMacros.nf"
63 # include "basic.h"
64 # include "osd.h"
65
66 /* from stat.h */
67 /*@ignore@*/
68 extern int stat (const char *, /*@out@*/ struct stat *);
69 /*@end@*/
70
71 static bool osd_executableFileExists (char *);
72
73 static bool nextdir (char **p_current_dir, /*@out@*/ char **p_dir, 
74                      /*@out@*/ size_t *p_len);
75
76 extern cstring LSLRootName (cstring filespec)
77 {
78   /*@access cstring@*/
79   char *result, *startName, *tail;
80   size_t nameLength;
81
82   llassert (cstring_isDefined (filespec));
83   tail = strrchr (filespec, CONNECTCHAR);
84   startName = (tail == NULL ? filespec : &tail[1]);
85   tail = strrchr (startName, '.');
86   nameLength = (tail == NULL ? strlen (startName) 
87                 : size_fromInt (tail - startName));
88   result = dmalloc (nameLength + 1);
89   strncpy (result, startName, nameLength);
90   result[(int) nameLength] = '\0';
91   return result;
92   /*@noaccess cstring@*/
93 }
94
95 extern /*@observer@*/ cstring
96 osd_getEnvironment (cstring env, /*@returned@*/ cstring def)
97 {
98   /*@access cstring@*/
99   char *ret = osd_getEnvironmentVariable (env);
100
101   if (ret == NULL)
102     {
103       return def;
104     }
105   else
106     {
107       return ret;
108     }
109   /*@noaccess cstring@*/
110 }
111
112
113 /*
114 **++
115 **  FUNCTIONAL DESCRIPTION:
116 **
117 **      This function attempts to locate a file in a search list.  On VMS, it
118 **      just concatinates the path and file names, and then lets RMS do the
119 **      searching.  On Ultrix, it searches for the file on the path.
120 **
121 **  FORMAL PARAMETERS:
122 **
123 **      path:       search path where to look for the file.
124 **      file:       name of file to search for.
125 **      returnPath: if a file is found, this is where the concatenated
126 **                  directory and file name are returned.
127 **
128 **  RETURN VALUE:
129 **
130 **      OSD_FILEFOUND:      the file was found on the search list.
131 **      OSD_FILENOTFOUND    the file was not found.
132 **      OSD_PATHTOOLONG     the concatenated directory and file name are too
133 **                          long.
134 **
135 **  SIDE EFFECTS:
136 **
137 **      None
138 **
139 **  PRECONDITIONS:
140 **
141 **      Requires that parameters, path and file, are valid C strings.
142 **
143 **
144 **--
145 */
146
147 extern /*@observer@*/ cstring osd_getHomeDir ()
148 {
149   /* Would something different be better for windows? */
150   return (osd_getEnvironmentVariable (cstring_makeLiteralTemp ("HOME")));
151 }
152
153 filestatus osd_findOnLarchPath (cstring file, cstring *returnPath)
154 {
155   return (osd_getPath (context_getLarchPath (), file, returnPath));
156 }
157
158 extern filestatus
159 osd_getPath (cstring path, cstring file, cstring *returnPath)
160 {
161   char *fullPath;
162   char *dirPtr;
163   size_t dirLen;
164   char aPath[MAXPATHLEN];
165   filestatus rVal = OSD_FILENOTFOUND;   /* assume file not found. */
166   
167   /*@access cstring@*/
168   
169   fullPath = path;
170   llassert (cstring_isDefined (file));
171   
172   /* 2002-01-01: make sure returnPath gets defined even when there are errors.
173   **             (fixed splint checking detected this)
174   */
175
176   *returnPath = cstring_undefined;
177
178   if (fullPath == NULL 
179       || 
180 # if defined(OS2) || defined(MSDOS) || defined(WIN32)
181       /* under OS/2 and MSDOS the includePath may be empty, if so, search 
182        * the current directory. */
183       *fullPath == '\0' || 
184       (*file == CONNECTCHAR || (file[0] != '\0' && file[1] == ':'))
185 # else
186     (*file == CONNECTCHAR)
187 # endif
188       )
189     {
190       /* No path specified. Look for it in the current directory.           */
191       
192       strcpy (&aPath[0], file);
193       
194       if (osd_fileExists (&aPath[0]))
195         {
196           rVal = OSD_FILEFOUND;
197           *returnPath = dmalloc (strlen (&aPath[0]) + 1);
198           strcpy (*returnPath, &aPath[0]);
199         }
200     }
201   else
202     {
203       /* Path specified. Loop through directories in path looking for the */
204       /* first occurrence of the file.                              */
205       
206       while (nextdir (&fullPath, &dirPtr, &dirLen) &&
207              rVal == OSD_FILENOTFOUND)
208         {
209           if ((dirLen + strlen (file) + 2) <= MAXPATHLEN)
210             {
211               /* Cat directory and filename, and see if file exists.  */
212               strncpy (&aPath[0], dirPtr, dirLen);
213               strcpy (&aPath[0] + dirLen, "");  /* Null terminate aPath. */
214               strcat (&aPath[0], CONNECTSTR);
215               strcat (&aPath[0], file);
216               
217               if (osd_fileExists (&aPath[0]))
218                 {
219                   rVal = OSD_FILEFOUND;
220                   *returnPath = (char *) dmalloc (strlen (&aPath[0]) + 1);
221                   strcpy (*returnPath, &aPath[0]);
222                 }
223             }
224           else
225             {
226               rVal = OSD_PATHTOOLONG;
227             }
228         }       
229     }
230
231   return rVal;
232   /*@noaccess cstring@*/
233 }
234
235 extern filestatus
236 osd_getExePath (cstring path, cstring file, cstring *returnPath)
237 {
238   char *fullPath;
239   char *dirPtr;
240   size_t dirLen;
241   char aPath[MAXPATHLEN];
242   filestatus rVal = OSD_FILENOTFOUND;   /* assume file not found. */
243   /*@access cstring@*/ 
244
245   *returnPath = cstring_undefined;  
246   fullPath = osd_getEnvironmentVariable (path);
247   
248   if (fullPath == NULL)
249     {
250       /* No path specified. Look for it in the current directory. */
251       llassert (cstring_isDefined (file));
252       strcpy (&aPath[0], file);
253       
254       if (osd_fileExists (&aPath[0]))
255         {
256           rVal = OSD_FILEFOUND;
257           *returnPath = dmalloc (strlen (&aPath[0]) + 1);
258           strcpy (*returnPath, &aPath[0]);
259         }
260     }
261   else
262     {
263      /* 
264      ** Path specified. Loop through directories in path looking
265      ** for the first occurrence of the file.                               
266      */
267
268       while (nextdir (&fullPath, &dirPtr, &dirLen) &&
269              rVal == OSD_FILENOTFOUND)
270         {
271           llassert (cstring_isDefined (file));
272
273           if ((dirLen + cstring_length (file) + 2) <= MAXPATHLEN)
274             {
275               /* Cat directory and filename, and see if file exists.  */
276               strncpy (&aPath[0], dirPtr, dirLen);
277               strcpy (&aPath[0] + dirLen, ""); /* Null terminate aPath. */
278               strcat (&aPath[0], CONNECTSTR);
279               strcat (&aPath[0], file);
280
281               if (osd_executableFileExists (&aPath[0]))
282                 {
283                   rVal = OSD_FILEFOUND;
284                   *returnPath = dmalloc (strlen (&aPath[0]) + 1);
285                   strcpy (*returnPath, &aPath[0]);
286                 }
287             }
288           else
289             {
290               rVal = OSD_PATHTOOLONG;
291             }
292         }
293     }
294
295   return rVal;
296   /*@noaccess cstring@*/
297 }
298
299 bool
300 osd_fileExists (cstring filespec)
301 {
302 # ifdef UNIX
303   struct stat buf;
304   return (stat (cstring_toCharsSafe (filespec), &buf) == 0);
305 # else
306 # if defined (WIN32) || defined (OS2)
307   FILE *test = fileTable_openReadFile (context_fileTable (), filespec);
308   
309   if (test != NULL) 
310     {
311       (void) fileTable_closeFile (context_fileTable (),test);
312       return TRUE;
313     } 
314   else
315     { 
316       return FALSE;
317     }
318 # else 
319   return FALSE;
320 # endif
321 # endif
322 }
323
324 # if defined(__IBMC__) && defined(OS2)
325 # define S_IFMT (unsigned short)0xFFFF
326 # endif
327
328 /*
329 ** Works form Win32 at least...
330 */
331
332 # ifndef S_IXUSR
333 /*@-macrounrecog@*/
334 # define S_IXUSR _S_IEXEC
335 /*@=macrounrecog@*/
336 # endif
337
338 bool
339 osd_executableFileExists (/*@unused@*/ char *filespec)
340 {
341 # ifdef UNIX
342   struct stat buf;
343   if (stat (filespec, &buf) == 0)
344     { 
345       /* mask by file type */
346       /*@-unrecog@*/ /* S_IFMT is not defined */
347       if ((buf.st_mode & S_IFMT) != S_IFDIR /*@=unrecog@*/) /* not a directory */
348         {
349           /* as long as it is an executable file */
350 # if defined(__IBMC__) && defined(OS2)
351           int com_or_exe_pos = strlen( filespec) - 4;
352           return stricmp( &filespec[com_or_exe_pos], ".exe") == 0
353             || stricmp( &filespec[com_or_exe_pos], ".com") == 0
354             || stricmp( &filespec[com_or_exe_pos], ".bat") == 0
355             || stricmp( &filespec[com_or_exe_pos], ".cmd") == 0;
356 # else
357           return (((buf.st_mode & S_IXUSR)
358 # if defined (S_IXGRP) && defined (S_IXOTH)
359                    | (buf.st_mode & S_IXGRP) |
360                    (buf.st_mode & S_IXOTH)
361 # endif
362                    ) != 0);
363 # endif
364         }
365     }
366 # endif
367   return (FALSE);
368
369 }
370
371 /*
372 **++
373 **  FUNCTIONAL DESCRIPTION:
374 **
375 **      Find the next directory from a directory path.
376 **
377 **  FORMAL PARAMETERS:
378 **
379 **      char ** current_dir :
380 **          Points to the current position in the path string.  The first time
381 **          you call this routine, this should point to the first character of
382 **          the path.  On return, this will be updated to point to the
383 **          terminating \0 or : of the first directory found.  You can then pass
384 **          it unchanged for subsequent calls; this routine will correctly skip
385 **          over the :.
386 **
387 **      char ** dir :
388 **          On exit, this will point to the first character of the directory
389 **          that was found.  This will be a pointer directly into the client's
390 **          path string.
391 **
392 **      unsigned int * len :
393 **          On exit, this will contain the length of the directory that was
394 **          found, not counting any terminating \0 or :.  If no directory was
395 **          found, this will be 0.
396 **
397 **  RETURN VALUE:
398 **      TRUE if we found another directory.
399 **      FALSE otherwise.
400 **
401 **  DESIGN:
402 **
403 **      We return a pointer and length, rather than a string, because of a)
404 **      historical reasons; and b) this avoids having to allocate storage.
405 **
406 **
407 **
408 **--
409 */
410
411 static bool
412 nextdir (d_char *current_dir, d_char *dir, size_t *len)
413 {
414   char *tchar;
415
416   if (**current_dir == '\0')
417     {
418       *len = 0;
419       *dir = NULL;
420       return FALSE;
421     }
422
423   *dir = (**current_dir == PATH_SEPARATOR ? *current_dir + 1 : *current_dir);
424   
425   /* Find next ':' or end of string */
426   for (tchar = *dir; *tchar != '\0' && *tchar != PATH_SEPARATOR; tchar++)
427     {
428       ;
429     }
430   
431   *current_dir = tchar;
432   *len = size_fromInt (tchar - *dir);
433   return TRUE;
434 }
435
436 /*@observer@*/ /*@null@*/ cstring osd_getEnvironmentVariable (cstring var)
437 {
438   /* evans - 2001-08-26 fixed OS instead of OS2 bug, reported by Alexander Mai */
439 # if defined(UNIX) || defined(OS2) || defined(MSDOS) || defined(WIN32)
440   char *val = getenv (cstring_toCharsSafe (var));
441
442   if (val == NULL) 
443     {
444       return cstring_undefined;      
445     } 
446   else 
447     {
448       return cstring_makeLiteralTemp (val);
449     }
450 # else
451   return cstring_undefined;
452 # endif
453 }
454
455 # ifdef WIN32
456 extern /*@external@*/ int _flushall (void) /*@modifies fileSystem@*/ ;
457 # endif
458
459 # ifndef system
460 extern /*@external@*/ int system (const char *) /*@modifies fileSystem@*/ ;
461 # endif
462 int osd_system (cstring cmd)
463 {
464   int res;
465     /* system ("printenv"); */
466 # ifdef WIN32
467   (void) _flushall (); 
468 # endif
469
470   res = system (cstring_toCharsSafe (cmd));
471   return res;
472 }
473
474 # ifndef unlink
475 /* This should be defined by unistd.h */
476 /*@-redecl@*/
477 extern /*@external@*/ int unlink (const char *) /*@modifies fileSystem@*/ ;
478 /*@=redecl@*/
479 # endif
480
481 static bool s_tempError = FALSE;
482
483 void osd_setTempError (void)
484 {
485   s_tempError = TRUE;
486 }
487
488 int osd_unlink (cstring fname)
489 {
490   int res;
491
492   res = unlink (cstring_toCharsSafe (fname));
493
494   if (res != 0)
495     {
496       if (!s_tempError)
497         {
498           llcontbug (message ("Cannot remove temporary file: %s (%s)",
499                               fname,
500                               cstring_fromChars (strerror (errno))));
501         }
502     }
503   
504   return res;
505 }
506
507 # if defined (WIN32) || defined (OS2) && defined (__IBMC__)
508 int
509 # else
510 int /* pid_t */
511 # endif
512 osd_getPid ()
513 {
514 # if defined (WIN32) || defined (OS2) && defined (__IBMC__)
515   int pid = _getpid ();
516 # else
517   pid_t pid = getpid ();
518 # endif
519
520   return (int) pid;
521 }
522   
523 cstring osd_fixDefine (cstring x)
524 {
525   /*@access cstring@*/
526   llassert (cstring_isDefined (x));
527 # ifdef UNIX
528   if (strchr (x, '\'') != NULL) {
529     /*
530     ** If there is a single quote, check for <ident>='<string>' and 
531     ** produce <ident>=<string>
532     */
533
534     char *eqs = strchr (x, '=');
535
536     if (eqs != NULL) {
537       if (eqs[1] == '\'') {
538         char *endqu = strrchr (x, '\'');
539
540         if (endqu != NULL) {
541           if (*(endqu - 1) != '\\') {
542             if (*(endqu + 1) == '\0') {
543               cstring res;
544               cstring def;
545
546               *endqu = '\0';
547               def = cstring_fromChars (eqs + 2);
548               eqs[1] = '\0';
549               res = cstring_concat (cstring_fromChars (x), def);
550                       return res;
551             }
552           }
553         }
554       }
555     }
556   } 
557
558 # endif
559
560   return cstring_copy (x);
561   /*@noaccess cstring@*/
562 }
563
564 bool osd_fileIsReadable (cstring f)
565 {
566   FILE *fl = fileTable_openReadFile (context_fileTable (), f);
567
568   if (fl != NULL)
569     {
570       check (fileTable_closeFile (context_fileTable (), fl));
571       return (TRUE);
572     }
573   else
574     {
575       return (FALSE);
576     }
577 }
578
579 bool osd_isConnectChar (char c)
580 {
581   if (c == CONNECTCHAR) 
582     {
583       return TRUE;
584     }
585
586 # ifdef HASALTCONNECTCHAR
587   if (c == ALTCONNECTCHAR)
588     {
589       return TRUE;
590     }
591 # endif
592
593   return FALSE;
594 }
595
596 /*
597 ** Returns true if c2 starts with the same path as c1
598 **
599 ** This is called by context_isSystemDir to determine if a
600 ** directory is on the system path.
601 **
602 ** In unix, this is just a string comparison.  For Win32 and OS2, we need a more
603 ** complex comparison.
604 */
605
606 bool osd_equalCanonicalPrefix (cstring dirpath, cstring prefixpath)
607 {
608   llassert (cstring_isDefined (prefixpath));
609
610   if (cstring_isEmpty (dirpath)) 
611     {
612       return (cstring_isEmpty (prefixpath));
613     }
614
615 # if defined (WIN32) || defined (OS2)
616
617   /*@access cstring@*/ /* Moved this from cstring - should abstract it... */
618
619   /*
620   ** If one has a drive specification, but the other doesn't, skip it.
621   */
622   
623   if (strchr (dirpath, ':') == NULL
624       && strchr (prefixpath, ':') != NULL)
625     {
626       prefixpath = strchr (prefixpath, ':') + 1;
627     }
628   else 
629     {
630       if (strchr (prefixpath, ':') == NULL
631           && strchr (dirpath, ':') != NULL)
632         {
633           dirpath = strchr (dirpath, ':') + 1;
634         }
635     }
636
637   {
638     int len = size_toInt (strlen (prefixpath));
639     int i = 0;
640     int slen = 0;
641
642     for (i = 0, slen = 0; i < len; i++, slen++)
643       {
644         /* Allow any number of connect characters in any combination:
645          * c:/usr//src\/foo == c:\\usr/src\/foo 
646          * After this we'll be at the last of such a sequence */
647
648         if (osd_isConnectChar (dirpath[slen]) && osd_isConnectChar (prefixpath[i]))
649           {
650             /* Skip one or more connect chars */
651
652             for (; osd_isConnectChar (dirpath[slen+1]); ++slen)
653               {
654                 ; 
655               }
656             
657             for (; osd_isConnectChar (prefixpath[i+1]); ++i)
658               {
659                 ;
660               }
661           }
662         /* Windows, MSDOS and OS/2 use case-insensitive path specs! */
663         else if (toupper (dirpath[slen]) != toupper (prefixpath[i]))
664           {
665             return FALSE;
666           }
667
668       }
669   }
670
671   /*@noaccess cstring@*/ 
672
673   return TRUE;
674 # else
675   return (cstring_equalPrefix (dirpath, prefixpath));
676 # endif
677 }
678
679 # if 0
680 /*
681 ** This code provided by Herbert Martin Dietze, to canonicalize path names.
682 */
683
684 char *osd_getcwd (/*@returned@*/ char *str, size_t size)
685
686   return getcwd (str, size);
687 }
688
689 /*@null@*/ /*@observer@*/ char *
690 osd_dirNext (char *str)
691 {
692   char *p1 = strchr (str, '/');
693   char *p2 = strchr (str, '\\');
694
695   if (p1 == NULL)
696     {
697       if (p2 != NULL)
698         {
699           return p2 + 1;
700         }
701       else
702         {
703           return NULL;
704         }
705     }
706   else if (p2 == NULL)
707     {
708       return p1 + 1;
709     }
710   else /* both not null */
711     {
712       return (p1 < p2 ? p1 : p2) + 1;
713     }
714 }
715
716 static void 
717 osd_dirShift (char *str, size_t num) /*@modifies str@*/
718 {
719   int i;
720   
721   assert (num <= strlen (str));
722   
723   for (i = 0; str[i] != '\0'; i++)
724     {
725       str[i] = str[i + num];
726     }
727 }
728
729 bool
730 osd_dirDotdot (char *str)
731 {
732   return str[0] == '.' && str[1] == '.' && osd_isConnectChar (str[2]);
733 }
734
735 void
736 osd_dirNormalize (char *str)
737 {
738   char *pos1, *pos2;
739
740   for (; osd_isConnectChar (str[0]); str++)
741     {
742     }
743
744   for (; str != NULL && osd_dirDotdot (str); str = osd_dirNext (str))
745     {
746     }
747
748   for (pos1 = pos2 = str; 
749        pos1 != NULL; 
750        pos2 = pos1, pos1 = osd_dirNext (pos1))
751     {
752       /* remove redundant `./' entry */
753       while (pos1[0] == '.' && osd_isConnectChar (pos1[1]))
754         {
755           osd_dirShift (pos1, 2);
756         }
757
758       /* remove redundant `foo/../' entry */
759       if (osd_dirDotdot (pos1) && pos2 < pos1)
760         {
761           osd_dirShift (pos2, pos1 - pos2 + 1);
762           osd_dirNormalize (str);
763         }
764     }
765 }
766
767 /*@null@*/ char *
768 osd_dirAbsolute (char *str)
769 {
770   char *ret = NULL;
771   size_t size = PATH_MAX * sizeof (*ret);
772   
773   DPRINTF (("Absolute for: %s", str));
774
775 # if defined (WIN32) || defined (OS2) || defined (MSDOS)
776   if (strlen (str) > 1 && str[1] == ':')
777     {
778       /*
779       ** Its a drive letter
780       */
781       
782       ret = dmalloc ((strlen (str) + 1) * sizeof (*ret));
783       strcpy (ret, str);
784     }
785   else
786 # endif
787   if (osd_isConnectChar (str[0]))
788     {
789       ret = dmalloc ((strlen (str) + 1) * sizeof (*ret));
790       strcpy (ret, str);
791     }
792   else
793     {
794       ret = dmalloc (size);
795       
796       ret = osd_getcwd (ret, size);
797       ret = realloc (ret, (strlen (str) + strlen (ret) + 2) * sizeof (*ret));
798
799       if (ret == NULL)
800         {
801           return NULL;
802         }
803
804       strcat (ret, CONNECTSTR);
805       strcat (ret, str);
806     }
807   
808   osd_dirNormalize (ret);
809   return ret;
810 }
811
812 # endif
813
814 /*
815 ** absolute paths
816 **
817 ** This code is adapted from:
818 **
819 ** 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
820 **
821 **
822 **  Protoize program - Original version by Ron Guilmette (rfg@segfault.us.com).
823 **   Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
824 **
825 ** This file is part of GNU CC.
826 **
827 ** GNU CC is free software; you can redistribute it and/or modify
828 ** it under the terms of the GNU General Public License as published by
829 ** the Free Software Foundation; either version 2, or (at your option)
830 ** any later version.
831 **
832 ** GNU CC is distributed in the hope that it will be useful,
833 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
834 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
835 ** GNU General Public License for more details.
836 ** 
837 ** You should have received a copy of the GNU General Public License
838 ** along with GNU CC; see the file COPYING.  If not, write to
839 ** the Free Software Foundation, 59 Temple Place - Suite 330,
840 ** Boston, MA 02111-1307, USA.  
841 */
842
843 /* 
844 ** Return the absolutized filename for the given relative
845 ** filename.  Note that if that filename is already absolute, it may
846 ** still be returned in a modified form because this routine also
847 ** eliminates redundant slashes and single dots and eliminates double
848 ** dots to get a shortest possible filename from the given input
849 ** filename.  The absolutization of relative filenames is made by
850 ** assuming that the given filename is to be taken as relative to
851 ** the first argument (cwd) or to the current directory if cwd is
852 ** NULL.  
853 */
854
855 /* A pointer to the current directory filename (used by abspath).  */
856 static /*@only@*/ cstring osd_cwd = cstring_undefined;
857
858 static void osd_setWorkingDirectory (void)
859 {
860 # if defined (UNIX) || defined (OS2)
861   char *buf = dmalloc (sizeof (*buf) * MAXPATHLEN);
862   char *cwd = getcwd (buf, MAXPATHLEN);
863
864   llassert (cstring_isUndefined (osd_cwd));
865
866   if (cwd == NULL)
867     {
868       lldiagmsg (message ("Cannot get working directory: %s\n",
869                           lldecodeerror (errno)));
870       osd_cwd = cstring_makeLiteral ("<missing directory>");
871     }
872   else
873     {
874       osd_cwd = cstring_fromCharsNew (cwd);
875     }
876
877   sfree (buf);
878 # else
879   ; /* Don't know how to do this for non-POSIX platforms */
880 # endif
881 }
882
883 void osd_initMod (void)
884 {
885   osd_setWorkingDirectory ();
886 }
887
888 void osd_destroyMod (void)
889 {
890   cstring_free (osd_cwd);
891   osd_cwd = cstring_undefined;
892 }
893
894 cstring osd_absolutePath (cstring cwd, cstring filename)
895 {
896 # if defined (UNIX) || defined (OS2)
897   /* Setup the current working directory as needed.  */
898   cstring cwd2 = cstring_isDefined (cwd) ? cwd : osd_cwd;
899   char *abs_buffer;
900   char *endp, *outp, *inp;
901
902   /*@access cstring@*/
903   llassert (cstring_isDefined (cwd2));
904   llassert (cstring_isDefined (filename));
905
906   abs_buffer = (char *) dmalloc (cstring_length (cwd2) + cstring_length (filename) + 2);
907   endp = abs_buffer;
908   
909   /*
910   ** Copy the  filename (possibly preceded by the current working
911   ** directory name) into the absolutization buffer.  
912   */
913   
914   {
915     const char *src_p;
916
917     if (!osd_isConnectChar (filename[0])
918 # ifdef OS2
919         && !(isalpha (filename[0]) && filename[1] == ':')
920 # endif
921         )
922       {
923         src_p = cwd2;
924
925         while ((*endp++ = *src_p++) != '\0') 
926           {
927             continue;
928           }
929
930         *(endp-1) = CONNECTCHAR;                        /* overwrite null */
931       }
932
933     src_p = filename;
934
935     while ((*endp++ = *src_p++) != '\0')
936       {
937         continue;
938       }
939   }
940   
941   /* Now make a copy of abs_buffer into abs_buffer, shortening the
942      filename (by taking out slashes and dots) as we go.  */
943   
944   outp = inp = abs_buffer;
945   *outp++ = *inp++;             /* copy first slash */
946 #ifdef apollo
947   if (inp[0] == '/')
948     *outp++ = *inp++;           /* copy second slash */
949 #endif
950   for (;;)
951     {
952       if (inp[0] == '\0')
953         {
954           break;
955         }
956       else if (osd_isConnectChar (inp[0]) && osd_isConnectChar (outp[-1]))
957         {
958           inp++;
959           continue;
960         }
961       else if (inp[0] == '.' && osd_isConnectChar (outp[-1]))
962         {
963           if (inp[1] == '\0')
964             {
965               break;
966             }
967           else if (osd_isConnectChar (inp[1]))
968             {
969               inp += 2;
970               continue;
971             }
972           else if ((inp[1] == '.') 
973                    && (inp[2] == '\0' || osd_isConnectChar (inp[2])))
974             {
975               inp += (osd_isConnectChar (inp[2])) ? 3 : 2;
976               outp -= 2;
977         
978               while (outp >= abs_buffer && !osd_isConnectChar (*outp))
979                 {
980                   outp--;
981                 }
982
983               if (outp < abs_buffer)
984                 {
985                   /* Catch cases like /.. where we try to backup to a
986                      point above the absolute root of the logical file
987                      system.  */
988                   
989                   llfatalbug (message ("Invalid file name: %s", filename));
990                 }
991
992               *++outp = '\0';
993               continue;
994             }
995           else
996             {
997               ;
998             }
999         }
1000       else
1001         {
1002           ;
1003         }
1004
1005       *outp++ = *inp++;
1006     }
1007   
1008   /* On exit, make sure that there is a trailing null, and make sure that
1009      the last character of the returned string is *not* a slash.  */
1010   
1011   *outp = '\0';
1012   if (osd_isConnectChar (outp[-1]))
1013     *--outp  = '\0';
1014   
1015   /*@noaccess cstring@*/
1016   return cstring_fromChars (abs_buffer);
1017 # else
1018   DPRINTF (("Here: %s", filename));
1019   return cstring_copy (filename);
1020 # endif
1021 }
1022
1023 /* 
1024 ** Given a filename (and possibly a directory name from which the filename
1025 ** is relative) return a string which is the shortest possible
1026 ** equivalent for the corresponding full (absolutized) filename.  The
1027 ** shortest possible equivalent may be constructed by converting the
1028 ** absolutized filename to be a relative filename (i.e. relative to
1029 ** the actual current working directory).  However if a relative filename
1030 ** is longer, then the full absolute filename is returned.
1031 **
1032 ** KNOWN BUG:   subpart of the original filename is actually a symbolic link.  
1033 **
1034 ** this is really horrible code...surely someone has written a less buggy version of this!
1035 */
1036
1037 cstring osd_outputPath (cstring filename)
1038 {
1039 # if defined (UNIX) || defined (OS2)
1040   char *rel_buffer;
1041   char *rel_buf_p;
1042   cstring cwd_p = osd_cwd;
1043   char *path_p;
1044   int unmatched_slash_count = 0;
1045   size_t filename_len = cstring_length (filename);
1046   
1047   llassertretval (filename_len > 0, filename);
1048
1049   /*@access cstring@*/
1050   path_p = filename;
1051   rel_buffer = (char *) dmalloc (filename_len);
1052   rel_buf_p = rel_buffer;
1053   *rel_buf_p = '\0';
1054
1055   if (cwd_p == NULL) 
1056     {
1057       /* Need to prevent recursive assertion failures */
1058       return cstring_copy (filename);
1059     }
1060
1061   llassert (cwd_p != NULL);
1062   llassert (path_p != NULL);
1063
1064   while ((*cwd_p != '\0') && (*cwd_p == *path_p))
1065     {
1066       cwd_p++;
1067       path_p++;
1068     }
1069   
1070   if ((*cwd_p == '\0') && (*path_p == '\0' || osd_isConnectChar (*path_p)))  /* whole pwd matched */
1071     {
1072       if (*path_p == '\0')             /* input *is* the current path! */
1073         {
1074           cstring_free (rel_buffer);
1075           return cstring_makeLiteral (".");
1076         }
1077       else
1078         {
1079           cstring_free (rel_buffer);
1080           return cstring_fromCharsNew (path_p + 1);
1081         }
1082     }
1083   else
1084     {
1085
1086       /* drl   2002-10/14 I had to put this code back*/
1087       /* 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
1088        */
1089       
1090       /* evans 2002-02-05 This is horrible code, which I've removed.  I couldn't find any
1091       ** test cases that need it, so I hope I'm not breaking anything.
1092       */
1093       /*#if 0*/
1094
1095       if (*path_p != '\0')
1096         {
1097           --cwd_p;
1098           --path_p;
1099
1100           while (cwd_p >= osd_cwd && !osd_isConnectChar (*cwd_p)) /* backup to last slash */
1101             {
1102               --cwd_p;
1103               --path_p;
1104             }
1105           cwd_p++;
1106           path_p++;
1107           unmatched_slash_count++;
1108         }
1109
1110       /* Find out how many directory levels in cwd were *not* matched.  */
1111       while (*cwd_p != '\0')
1112         {
1113           if (osd_isConnectChar (*cwd_p++))
1114             unmatched_slash_count++;
1115         }
1116       
1117       /* Now we know how long the "short name" will be.
1118          Reject it if longer than the input.  */
1119       if (unmatched_slash_count * 3 + strlen (path_p) >= filename_len)
1120         {
1121           cstring_free (rel_buffer);
1122           /* fprintf (stderr, "Returning filename: %s [%p]\n", filename); */
1123           return cstring_copy (filename);
1124         }
1125
1126       /*drl 10-14-2002 end previously removed code */
1127       /*#endif*/
1128       /* For each of them, put a `../' at the beginning of the short name.  */
1129       while (unmatched_slash_count-- > 0)
1130         {
1131           /* Give up if the result gets to be longer
1132              than the absolute path name.  */
1133           char * temp_rel_buf_p;
1134
1135           /*drl This comment is necessary because for some reason Splint
1136             does not realize that the pasts where rel_buf_p is released
1137             do not reach here*/
1138           /*@-usereleased@*/
1139           temp_rel_buf_p = rel_buf_p;
1140           /*@-usereleased@*/
1141           
1142           if (rel_buffer + filename_len <= temp_rel_buf_p + 3)
1143             {
1144               sfree (rel_buffer);
1145               return cstring_copy (filename);
1146             }
1147
1148           *rel_buf_p++ = '.';
1149           *rel_buf_p++ = '.';
1150           *rel_buf_p++ = CONNECTCHAR;
1151         }
1152       
1153       /* Then tack on the unmatched part of the desired file's name.  */
1154
1155       do
1156         {
1157           if (rel_buffer + filename_len <= rel_buf_p)
1158             {
1159               cstring_free (rel_buffer);
1160               return cstring_copy (filename);
1161             }
1162         } /*@-usereleased@*/
1163       while ((*rel_buf_p++ = *path_p++) != '\0') ;
1164
1165       
1166       /*@=usereleased@*/ /* Splint limitation: shouldn't need these */
1167       --rel_buf_p;
1168
1169       if (osd_isConnectChar (*(rel_buf_p-1)))
1170         *--rel_buf_p = '\0';
1171
1172       /* fprintf (stderr, "Returning buffer: %s [%p]\n", rel_buffer, rel_buffer); */
1173       return rel_buffer;
1174     }
1175   /*@noaccess cstring@*/
1176 # else
1177   return cstring_copy (filename);
1178 # endif
1179 }
1180
1181 cstring osd_getCurrentDirectory ()
1182 {
1183 # if defined(MSDOS) || defined(OS2)
1184   return cstring_makeLiteralTemp ("");
1185 # else
1186   return cstring_makeLiteralTemp ("./");
1187 # endif
1188 }
1189
1190
1191
This page took 0.83723 seconds and 3 git commands to generate.