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